sympy-0.7.4.1/0000755000175000017500000000000012253362407013244 5ustar georgeskgeorgesksympy-0.7.4.1/examples/0000755000175000017500000000000012253362407015062 5ustar georgeskgeorgesksympy-0.7.4.1/examples/intermediate/0000755000175000017500000000000012253362407017534 5ustar georgeskgeorgesksympy-0.7.4.1/examples/intermediate/limit_examples_advanced.ipynb0000644000175000017500000007024212253362407025445 0ustar georgeskgeorgesk{ "metadata": { "name": "limit_examples_advanced" }, "name": "limit_examples_advanced", "nbformat": 2, "worksheets": [ { "cells": [ { "cell_type": "code", "collapsed": true, "input": "from sympy import *", "language": "python", "outputs": [], "prompt_number": 1 }, { "cell_type": "code", "collapsed": true, "input": "x = Symbol(\"x\")", "language": "python", "outputs": [], "prompt_number": 2 }, { "cell_type": "code", "collapsed": false, "input": "limit(exp(x)*exp(x**2)*(erf(x+1/exp(x))-erf(x)), x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 3, "text": "2/sqrt(pi)" } ], "prompt_number": 3 }, { "cell_type": "markdown", "source": "The examples here show the limit computation on exp-log expressions (from Gruntz' thesis pp. 122 to 123)" }, { "cell_type": "markdown", "source": "Eqn 8.1" }, { "cell_type": "code", "collapsed": false, "input": "exp(x)*(exp(1/x-exp(-x))-exp(1/x))", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 4, "text": "(-exp(1/x) + exp(-exp(-x) + 1/x))*exp(x)" } ], "prompt_number": 4 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 5, "text": "-1" } ], "prompt_number": 5 }, { "cell_type": "markdown", "source": "Eqn 8.2" }, { "cell_type": "code", "collapsed": false, "input": "exp(x)*(exp(1/x+exp(-x)+exp(-x**2)) - exp(1/x-exp(-exp(x))))", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 6, "text": "(-exp(-exp(-exp(x)) + 1/x) + exp(exp(-x**2) + exp(-x) + 1/x))*exp(x)" } ], "prompt_number": 6 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 7, "text": "1" } ], "prompt_number": 7 }, { "cell_type": "markdown", "source": "Eqn 8.3" }, { "cell_type": "code", "collapsed": false, "input": "exp(exp(x-exp(-x))/(1-1/x)) - exp(exp(x))", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 8, "text": "exp(exp(x - exp(-x))/(1 - 1/x)) - exp(exp(x))" } ], "prompt_number": 8 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 9, "text": "oo" } ], "prompt_number": 9 }, { "cell_type": "markdown", "source": "Eqn 8.4" }, { "cell_type": "code", "collapsed": false, "input": "exp(exp(exp(x)/(1-1/x))) - exp(exp(exp(x)/(1-1/x-log(x)**(-log(x)))))", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 10, "text": "exp(exp(exp(x)/(1 - 1/x))) - exp(exp(exp(x)/(1 - log(x)**(-log(x)) - 1/x)))" } ], "prompt_number": 10 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 11, "text": "-oo" } ], "prompt_number": 11 }, { "cell_type": "markdown", "source": "Eqn 8.5" }, { "cell_type": "code", "collapsed": false, "input": "exp(exp(exp(x+exp(-x)))) / exp(exp(exp(x)))", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 12, "text": "exp(-exp(exp(x)))*exp(exp(exp(x + exp(-x))))" } ], "prompt_number": 12 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 13, "text": "oo" } ], "prompt_number": 13 }, { "cell_type": "markdown", "source": "Eqn 8.6" }, { "cell_type": "code", "collapsed": false, "input": "exp(exp(exp(x))) / exp(exp(exp(x-exp(-exp(x)))))", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 14, "text": "exp(exp(exp(x)))*exp(-exp(exp(x - exp(-exp(x)))))" } ], "prompt_number": 14 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 15, "text": "oo" } ], "prompt_number": 15 }, { "cell_type": "markdown", "source": "Eqn 8.7" }, { "cell_type": "code", "collapsed": false, "input": "exp(exp(exp(x))) / exp(exp(exp(x-exp(-exp(exp(x))))))", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 16, "text": "exp(exp(exp(x)))*exp(-exp(exp(x - exp(-exp(exp(x))))))" } ], "prompt_number": 16 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 17, "text": "1" } ], "prompt_number": 17 }, { "cell_type": "markdown", "source": "Eqn 8.8" }, { "cell_type": "code", "collapsed": false, "input": "exp(exp(x)) / exp(exp(x-exp(-exp(exp(x)))))", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 18, "text": "exp(exp(x))*exp(-exp(x - exp(-exp(exp(x)))))" } ], "prompt_number": 18 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 19, "text": "1" } ], "prompt_number": 19 }, { "cell_type": "markdown", "source": "Eqn 8.9" }, { "cell_type": "code", "collapsed": false, "input": "log(x)**2 * exp(sqrt(log(x))*(log(log(x)))**2 * exp(sqrt(log(log(x))) * (log(log(log(x))))**3)) / sqrt(x)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 20, "text": "exp(exp(sqrt(log(log(x)))*log(log(log(x)))**3)*sqrt(log(x))*log(log(x))**2)*log(x)**2/sqrt(x)" } ], "prompt_number": 20 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 21, "text": "0" } ], "prompt_number": 21 }, { "cell_type": "markdown", "source": "Eqn 8.10" }, { "cell_type": "code", "collapsed": false, "input": "(x*log(x)*(log(x*exp(x)-x**2))**2) / (log(log(x**2+2*exp(exp(3*x**3*log(x))))))", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 22, "text": "x*log(x)*log(-x**2 + x*exp(x))**2/log(log(x**2 + 2*exp(exp(3*x**3*log(x)))))" } ], "prompt_number": 22 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 23, "text": "1/3" } ], "prompt_number": 23 }, { "cell_type": "markdown", "source": "Eqn 8.11" }, { "cell_type": "code", "collapsed": false, "input": "(exp(x*exp(-x)/(exp(-x)+exp(-2*x**2/(x+1)))) - exp(x))/x", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 24, "text": "(-exp(x) + exp(x*exp(-x)/(exp(-2*x**2/(x + 1)) + exp(-x))))/x" } ], "prompt_number": 24 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 25, "text": "-exp(2)" } ], "prompt_number": 25 }, { "cell_type": "markdown", "source": "Eqn 8.12" }, { "cell_type": "code", "collapsed": false, "input": "(3**x + 5**x)**(1/x)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 26, "text": "(3**x + 5**x)**(1/x)" } ], "prompt_number": 26 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 27, "text": "5" } ], "prompt_number": 27 }, { "cell_type": "markdown", "source": "Eqn 8.13" }, { "cell_type": "code", "collapsed": false, "input": "x/log(x**(log(x**(log(2)/log(x)))))", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 28, "text": "x/log(x**log(x**(log(2)/log(x))))" } ], "prompt_number": 28 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 29, "text": "oo" } ], "prompt_number": 29 }, { "cell_type": "markdown", "source": "Eqn 8.14" }, { "cell_type": "code", "collapsed": false, "input": "exp(exp(2*log(x**5+x)*log(log(x)))) / exp(exp(10*log(x)*log(log(x))))", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 30, "text": "exp(-exp(10*log(x)*log(log(x))))*exp(exp(2*log(x**5 + x)*log(log(x))))" } ], "prompt_number": 30 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 31, "text": "oo" } ], "prompt_number": 31 }, { "cell_type": "markdown", "source": "Eqn 8.15" }, { "cell_type": "code", "collapsed": false, "input": "4*exp(exp(S(5)/2*x**(-S(5)/7)+ S(21)/8*x**(S(6)/11)+2*x**(-8)+S(54)/17*x**(S(49)/45) ))**8 / (9*log(log(-log(S(4)/3*x**(-S(5)/14))))**(S(7)/6))", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 32, "text": "4*exp(8*exp(54*x**(49/45)/17 + 21*x**(6/11)/8 + 2/x**8 + 5/(2*x**(5/7))))/(9*log(log(-log(4/(3*x**(5/14)))))**(7/6))" } ], "prompt_number": 32 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 33, "text": "oo" } ], "prompt_number": 33 }, { "cell_type": "markdown", "source": "Eqn 8.16" }, { "cell_type": "code", "collapsed": false, "input": "(exp(4*x*exp(-x)/(1/exp(x)+1/exp(2*x**2/(x+1)))) - exp(x)) / exp(x)**4", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 34, "text": "(-exp(x) + exp(4*x*exp(-x)/(exp(-2*x**2/(x + 1)) + exp(-x))))*exp(-4*x)" } ], "prompt_number": 34 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 35, "text": "1" } ], "prompt_number": 35 }, { "cell_type": "markdown", "source": "Eqn 8.17" }, { "cell_type": "code", "collapsed": false, "input": "exp(x*exp(-x)/(exp(-x)+exp(-2*x**2/(x+1))))/exp(x)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 36, "text": "exp(-x)*exp(x*exp(-x)/(exp(-2*x**2/(x + 1)) + exp(-x)))" } ], "prompt_number": 36 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 37, "text": "1" } ], "prompt_number": 37 }, { "cell_type": "markdown", "source": "Eqn 8.18" }, { "cell_type": "code", "collapsed": false, "input": "(exp(exp(-x/(1+exp(-x))))*exp(-x/(1+exp(-x/(1+exp(-x)))))*exp(exp(-x+exp(-x/(1+exp(-x)))))) / (exp(-x/(1+exp(-x))))**2 - exp(x) + x", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 38, "text": "x - exp(x) + exp(2*x/(1 + exp(-x)))*exp(-x/(1 + exp(-x/(1 + exp(-x)))))*exp(exp(-x/(1 + exp(-x))))*exp(exp(-x + exp(-x/(1 + exp(-x)))))" } ], "prompt_number": 38 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 39, "text": "2" } ], "prompt_number": 39 }, { "cell_type": "markdown", "source": "Eqn 8.19" }, { "cell_type": "code", "collapsed": false, "input": "log(x)*(log(log(x)+log(log(x))) - log(log(x))) / (log(log(x)+log(log(log(x)))))", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 40, "text": "(log(log(x) + log(log(x))) - log(log(x)))*log(x)/log(log(x) + log(log(log(x))))" } ], "prompt_number": 40 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 41, "text": "1" } ], "prompt_number": 41 }, { "cell_type": "markdown", "source": "Eqn 8.20" }, { "cell_type": "code", "collapsed": false, "input": "exp((log(log(x+exp(log(x)*log(log(x)))))) / (log(log(log(exp(x)+x+log(x))))))", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 42, "text": "exp(log(log(x + exp(log(x)*log(log(x)))))/log(log(log(x + exp(x) + log(x)))))" } ], "prompt_number": 42 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 43, "text": "E" } ], "prompt_number": 43 }, { "cell_type": "markdown", "source": "The following examples show limit computation on special functions (from Gruntz' thesis p. 126)" }, { "cell_type": "markdown", "source": "Eqn 8.21" }, { "cell_type": "code", "collapsed": false, "input": "exp(x)*(sin(1/x+exp(-x))-sin(1/x+exp(-x**2)))", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 44, "text": "(sin(exp(-x) + 1/x) - sin(exp(-x**2) + 1/x))*exp(x)" } ], "prompt_number": 44 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 45, "text": "1" } ], "prompt_number": 45 }, { "cell_type": "markdown", "source": "Eqn 8.22" }, { "cell_type": "code", "collapsed": false, "input": "exp(exp(x)) * (exp(sin(1/x+exp(-exp(x)))) - exp(sin(1/x)))", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 46, "text": "(-exp(sin(1/x)) + exp(sin(exp(-exp(x)) + 1/x)))*exp(exp(x))" } ], "prompt_number": 46 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 47, "text": "1" } ], "prompt_number": 47 }, { "cell_type": "markdown", "source": "Eqn 8.23" }, { "cell_type": "code", "collapsed": false, "input": "(erf(x-exp(-exp(x))) - erf(x)) * exp(exp(x)) * exp(x**2)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 48, "text": "(-erf(x) + erf(x - exp(-exp(x))))*exp(x**2)*exp(exp(x))" } ], "prompt_number": 48 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 49, "text": "-2/sqrt(pi)" } ], "prompt_number": 49 }, { "cell_type": "markdown", "source": "Eqn 8.24" }, { "cell_type": "code", "collapsed": false, "input": "(Ei(x-exp(-exp(x))) - Ei(x)) *exp(-x)*exp(exp(x))*x", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 50, "text": "x*(-Ei(x) + Ei(x - exp(-exp(x))))*exp(-x)*exp(exp(x))" } ], "prompt_number": 50 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 51, "text": "-1" } ], "prompt_number": 51 }, { "cell_type": "markdown", "source": "Eqn 8.25" }, { "cell_type": "code", "collapsed": false, "input": "exp((log(2)+1)*x) * (zeta(x+exp(-x)) - zeta(x))", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 52, "text": "(-zeta(x) + zeta(x + exp(-x)))*exp(x*(log(2) + 1))" } ], "prompt_number": 52 }, { "cell_type": "code", "collapsed": false, "input": "#limit(_, x, oo)", "language": "python", "outputs": [], "prompt_number": 53 }, { "cell_type": "markdown", "source": "Eqn 8.26" }, { "cell_type": "code", "collapsed": false, "input": "exp(x)*(gamma(x+exp(-x)) - gamma(x))", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 54, "text": "(-gamma(x) + gamma(x + exp(-x)))*exp(x)" } ], "prompt_number": 54 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 55, "text": "oo" } ], "prompt_number": 55 }, { "cell_type": "markdown", "source": "Eqn 8.27" }, { "cell_type": "code", "collapsed": false, "input": "exp(gamma(x-exp(-x))*exp(1/x)) - exp(gamma(x))", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 56, "text": "exp(exp(1/x)*gamma(x - exp(-x))) - exp(gamma(x))" } ], "prompt_number": 56 }, { "cell_type": "code", "collapsed": false, "input": "#limit(_, x, oo)", "language": "python", "outputs": [], "prompt_number": 57 }, { "cell_type": "markdown", "source": "Eqn 8.28" }, { "cell_type": "code", "collapsed": false, "input": "(gamma(x+1/gamma(x)) - gamma(x)) / log(x)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 58, "text": "(-gamma(x) + gamma(x + 1/gamma(x)))/log(x)" } ], "prompt_number": 58 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 59, "text": "1" } ], "prompt_number": 59 }, { "cell_type": "markdown", "source": "Eqn 8.29" }, { "cell_type": "code", "collapsed": false, "input": "x * (gamma(x-1/gamma(x)) - gamma(x) + log(x))", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 60, "text": "x*(log(x) - gamma(x) + gamma(x - 1/gamma(x)))" } ], "prompt_number": 60 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 61, "text": "1/2" } ], "prompt_number": 61 }, { "cell_type": "markdown", "source": "Eqn 8.30" }, { "cell_type": "code", "collapsed": false, "input": "((gamma(x+1/gamma(x)) - gamma(x))/log(x) - cos(1/x))*x*log(x)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 62, "text": "x*((-gamma(x) + gamma(x + 1/gamma(x)))/log(x) - cos(1/x))*log(x)" } ], "prompt_number": 62 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 63, "text": "-1/2" } ], "prompt_number": 63 }, { "cell_type": "markdown", "source": "Eqn 8.31" }, { "cell_type": "code", "collapsed": false, "input": "gamma(x+1)/sqrt(2*pi) - exp(-x)*(x**(x+S(1)/2) + x**(x-S(1)/2)/12)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 64, "text": "-(x**(x - 1/2)/12 + x**(x + 1/2))*exp(-x) + sqrt(2)*gamma(x + 1)/(2*sqrt(pi))" } ], "prompt_number": 64 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 65, "text": "oo" } ], "prompt_number": 65 }, { "cell_type": "markdown", "source": "Eqn 8.32" }, { "cell_type": "code", "collapsed": false, "input": "log(gamma(gamma(x)))/exp(x)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 66, "text": "exp(-x)*log(gamma(gamma(x)))" } ], "prompt_number": 66 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 67, "text": "oo" } ], "prompt_number": 67 }, { "cell_type": "markdown", "source": "Eqn 8.33" }, { "cell_type": "code", "collapsed": false, "input": "exp(exp(digamma(digamma(x))))/x", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 68, "text": "exp(exp(polygamma(0, polygamma(0, x))))/x" } ], "prompt_number": 68 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 69, "text": "exp(-1/2)" } ], "prompt_number": 69 }, { "cell_type": "markdown", "source": "Eqn 8.34" }, { "cell_type": "code", "collapsed": false, "input": "exp(exp(digamma(log(x))))/x", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 70, "text": "exp(exp(polygamma(0, log(x))))/x" } ], "prompt_number": 70 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 71, "text": "exp(-1/2)" } ], "prompt_number": 71 }, { "cell_type": "markdown", "source": "Eqn 8.35" }, { "cell_type": "code", "collapsed": false, "input": "exp(exp(exp(digamma(digamma(digamma(x))))))/x", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 72, "text": "exp(exp(exp(polygamma(0, polygamma(0, polygamma(0, x))))))/x" } ], "prompt_number": 72 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 73, "text": "0" } ], "prompt_number": 73 }, { "cell_type": "markdown", "source": "Eqn 8.36" }, { "cell_type": "code", "collapsed": false, "input": "besselj(2,x)*exp(x*(2*log(2+sqrt(3))-sqrt(3)))*sqrt(x)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 74, "text": "sqrt(x)*exp(x*(-sqrt(3) + 2*log(sqrt(3) + 2)))*besselj(2, x)" } ], "prompt_number": 74 }, { "cell_type": "code", "collapsed": false, "input": "#limit(_, x, oo)", "language": "python", "outputs": [], "prompt_number": 75 }, { "cell_type": "markdown", "source": "Eqn 8.37" }, { "cell_type": "code", "collapsed": false, "input": "Max(x, exp(x))/log(Min(exp(-x), exp(-exp(x))))", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 76, "text": "Max(x, exp(x))/log(Min(exp(-x), exp(-exp(x))))" } ], "prompt_number": 76 }, { "cell_type": "code", "collapsed": false, "input": "#limit(_, x, oo)", "language": "python", "outputs": [], "prompt_number": 77 }, { "cell_type": "markdown", "source": "Some other examples" }, { "cell_type": "code", "collapsed": false, "input": "digamma(digamma(digamma(x)))", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 78, "text": "polygamma(0, polygamma(0, polygamma(0, x)))" } ], "prompt_number": 78 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 79, "text": "oo" } ], "prompt_number": 79 }, { "cell_type": "code", "collapsed": false, "input": "loggamma(loggamma(x))", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 80, "text": "loggamma(loggamma(x))" } ], "prompt_number": 80 }, { "cell_type": "code", "collapsed": false, "input": "limit(_, x, oo)", "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 81, "text": "oo" } ], "prompt_number": 81 } ] } ] }sympy-0.7.4.1/examples/intermediate/schwarzschild.ipynb0000644000175000017500000005414412253362407023457 0ustar georgeskgeorgesk{ "metadata": { "name": "schwarzschild" }, "nbformat": 3, "worksheets": [ { "cells": [ { "cell_type": "markdown", "source": [ "# Schwarzschild Solution to the Einstein's equation" ] }, { "cell_type": "markdown", "source": [ "We will rederive the equations from *General Theory of Relativity*, chapter 18, by Dirac. They describe a spherically symetric solution to the Einstein's equation in vacuum.", "", "We will not try to solve the resulting equations.", "", "This verion of the notebook uses funtions that explicitly calculate the Christoffel symbols, hence the work is coordinate-system-dependent." ] }, { "cell_type": "markdown", "source": [ "Import the necessary modules." ] }, { "cell_type": "code", "input": [ "from sympy.diffgeom import *", "TP = TensorProduct" ], "language": "python", "outputs": [], "prompt_number": 1 }, { "cell_type": "markdown", "source": [ "Define a 4D manifold with a patch and a coordinate chart on which we will work." ] }, { "cell_type": "code", "input": [ "m = Manifold('Schwarzschild', 4)", "p = Patch('origin', m)", "cs = CoordSystem('spherical', p, ['t', 'r', 'theta', 'phi'])" ], "language": "python", "outputs": [], "prompt_number": 2 }, { "cell_type": "code", "input": [ "m, p, cs" ], "language": "python", "outputs": [ { "latex": [ "$$\\begin{pmatrix}\\mathbb{Schwarzschild}, & \\mathrm{origin}_{\\mathbb{Schwarzschild}}, & \\mathrm{spherical}^{\\mathrm{origin}}_{\\mathbb{Schwarzschild}}\\end{pmatrix}$$" ], "output_type": "pyout", "prompt_number": 3, "text": [ "(Manifold(Schwarzschild, 4),", " Patch(origin, Manifold(Schwarzschild, 4)),", " CoordSystem(spherical, Patch(origin, Manifold(Schwarzschild, 4))))" ] } ], "prompt_number": 3 }, { "cell_type": "markdown", "source": [ "Prepare the variables containing the scalar fields and the 1-form fields." ] }, { "cell_type": "code", "input": [ "t, r, theta, phi = cs.coord_functions()", "t, r, theta, phi" ], "language": "python", "outputs": [ { "latex": [ "$$\\begin{pmatrix}\\boldsymbol{\\mathrm{t}}, & \\boldsymbol{\\mathrm{r}}, & \\boldsymbol{\\mathrm{\\theta}}, & \\boldsymbol{\\mathrm{\\phi}}\\end{pmatrix}$$" ], "output_type": "pyout", "prompt_number": 4, "text": [ "(t, r, \u03b8, \u03c6)" ] } ], "prompt_number": 4 }, { "cell_type": "code", "input": [ "dt, dr, dtheta, dphi = cs.base_oneforms()", "dt, dr, dtheta, dphi" ], "language": "python", "outputs": [ { "latex": [ "$$\\begin{pmatrix}\\mathbb{d}t, & \\mathbb{d}r, & \\mathbb{d}\\theta, & \\mathbb{d}\\phi\\end{pmatrix}$$" ], "output_type": "pyout", "prompt_number": 5, "text": [ "(\u2146 t, \u2146 r, \u2146 \u03b8, \u2146 \u03c6)" ] } ], "prompt_number": 5 }, { "cell_type": "markdown", "source": [ "The most general spherically-symmetric metric has the following form." ] }, { "cell_type": "code", "input": [ "metric = exp(2*f(r))*TP(dt, dt) - exp(2*g(r))*TP(dr, dr) - r**2*TP(dtheta, dtheta) - r**2*sin(theta)**2*TP(dphi, dphi)", "metric" ], "language": "python", "outputs": [ { "latex": [ "$$e^{2 \\operatorname{f}{\\left (\\boldsymbol{\\mathrm{r}} \\right )}} \\mathbb{d}t\\otimes\\mathbb{d}t - e^{2 \\operatorname{g}{\\left (\\boldsymbol{\\mathrm{r}} \\right )}} \\mathbb{d}r\\otimes\\mathbb{d}r - \\left(\\boldsymbol{\\mathrm{r}}\\right)^{2} \\sin^{2}{\\left (\\boldsymbol{\\mathrm{\\theta}} \\right )} \\mathbb{d}\\phi\\otimes\\mathbb{d}\\phi - \\left(\\boldsymbol{\\mathrm{r}}\\right)^{2} \\mathbb{d}\\theta\\otimes\\mathbb{d}\\theta$$" ], "output_type": "pyout", "prompt_number": 6, "text": [ "", " 2\u22c5f(r) 2\u22c5g(r) 2 2 ", "\u212f \u22c5TensorProduct(dt, dt) - \u212f \u22c5TensorProduct(dr, dr) - r \u22c5sin (\u03b8)\u22c5Ten", "", " 2 ", "sorProduct(dphi, dphi) - r \u22c5TensorProduct(dtheta, dtheta)" ] } ], "prompt_number": 6 }, { "cell_type": "markdown", "source": [ "The matrix $M$ representing the two-form as a bilinear map $V,U\\to V^tMU$ over the column vectors $V$ and $U$ in the canonical basis of the choosen coordinate system is:" ] }, { "cell_type": "code", "input": [ "twoform_to_matrix(metric)" ], "language": "python", "outputs": [ { "latex": [ "$$\\left[\\begin{smallmatrix}e^{2 \\operatorname{f}{\\left (\\boldsymbol{\\mathrm{r}} \\right )}} & 0 & 0 & 0\\\\0 & - e^{2 \\operatorname{g}{\\left (\\boldsymbol{\\mathrm{r}} \\right )}} & 0 & 0\\\\0 & 0 & - \\left(\\boldsymbol{\\mathrm{r}}\\right)^{2} & 0\\\\0 & 0 & 0 & - \\left(\\boldsymbol{\\mathrm{r}}\\right)^{2} \\sin^{2}{\\left (\\boldsymbol{\\mathrm{\\theta}} \\right )}\\end{smallmatrix}\\right]$$" ], "output_type": "pyout", "prompt_number": 7, "text": [ "", "\u23a1 2\u22c5f(r) \u23a4", "\u23a2\u212f 0 0 0 \u23a5", "\u23a2 \u23a5", "\u23a2 2\u22c5g(r) \u23a5", "\u23a2 0 -\u212f 0 0 \u23a5", "\u23a2 \u23a5", "\u23a2 2 \u23a5", "\u23a2 0 0 -r 0 \u23a5", "\u23a2 \u23a5", "\u23a2 2 2 \u23a5", "\u23a3 0 0 0 -r \u22c5sin (\u03b8)\u23a6" ] } ], "prompt_number": 7 }, { "cell_type": "markdown", "source": [ "Now we will calculate the components in the same basis of the Ricci tensor." ] }, { "cell_type": "code", "input": [ "ricci = metric_to_Ricci_components(metric)" ], "language": "python", "outputs": [], "prompt_number": 8 }, { "cell_type": "code", "input": [ "# TODO: Fix the issue requiring this workaround (simplify unhappy about the fields)", "diffgeom_simplify = lambda a: a.subs(zip(cs.coord_functions(), cs._dummies)).simplify().subs(zip(cs._dummies, cs.coord_functions()))", "ricci = [[diffgeom_simplify(ricci[i][j])", " for j in range(4)] for i in range(4)]", "" ], "language": "python", "outputs": [], "prompt_number": 9 }, { "cell_type": "markdown", "source": [ "The diagonal components give the equations we are interested in." ] }, { "cell_type": "code", "input": [ "ricci[0][0]" ], "language": "python", "outputs": [ { "latex": [ "$$\\left(\\boldsymbol{\\mathrm{r}}\\right)^{-1} \\left(\\left(\\left. \\frac{\\partial}{\\partial \\xi_{1}} \\operatorname{f}{\\left (\\xi_{1} \\right )} \\right|_{\\substack{ \\xi_{1}=\\boldsymbol{\\mathrm{r}} }}\\right)^{2} \\boldsymbol{\\mathrm{r}} - \\left. \\frac{\\partial}{\\partial \\xi_{1}} \\operatorname{f}{\\left (\\xi_{1} \\right )} \\right|_{\\substack{ \\xi_{1}=\\boldsymbol{\\mathrm{r}} }} \\left. \\frac{\\partial}{\\partial \\xi_{1}} \\operatorname{g}{\\left (\\xi_{1} \\right )} \\right|_{\\substack{ \\xi_{1}=\\boldsymbol{\\mathrm{r}} }} \\boldsymbol{\\mathrm{r}} + 2 \\left. \\frac{\\partial}{\\partial \\xi_{1}} \\operatorname{f}{\\left (\\xi_{1} \\right )} \\right|_{\\substack{ \\xi_{1}=\\boldsymbol{\\mathrm{r}} }} + \\left. \\frac{\\partial^{2}}{\\partial^{2} \\xi_{1}} \\operatorname{f}{\\left (\\xi_{1} \\right )} \\right|_{\\substack{ \\xi_{1}=\\boldsymbol{\\mathrm{r}} }} \\boldsymbol{\\mathrm{r}}\\right) e^{2 \\operatorname{f}{\\left (\\boldsymbol{\\mathrm{r}} \\right )} - 2 \\operatorname{g}{\\left (\\boldsymbol{\\mathrm{r}} \\right )}}$$" ], "output_type": "pyout", "prompt_number": 10, "text": [ "", " \u239b 2 ", " -1 \u239c\u239b d \u239e\u2502 \u239b d \u239e\u2502 \u239b d \u239e\u2502 \u239b d ", "r \u22c5\u239c\u239c\u2500\u2500\u2500(f(\u03be\u2081))\u239f\u2502 \u22c5r - \u239c\u2500\u2500\u2500(f(\u03be\u2081))\u239f\u2502 \u22c5\u239c\u2500\u2500\u2500(g(\u03be\u2081))\u239f\u2502 \u22c5r + 2\u22c5\u239c\u2500\u2500\u2500(f(\u03be", " \u239c\u239dd\u03be\u2081 \u23a0\u2502\u03be\u2081=r \u239dd\u03be\u2081 \u23a0\u2502\u03be\u2081=r \u239dd\u03be\u2081 \u23a0\u2502\u03be\u2081=r \u239dd\u03be\u2081 ", " \u239d ", "", " \u239b 2 \u239e\u2502 \u239e ", " \u239e\u2502 \u239c d \u239f\u2502 \u239f 2\u22c5f(r) - 2\u22c5g(r)", "\u2081))\u239f\u2502 + \u239c\u2500\u2500\u2500\u2500(f(\u03be\u2081))\u239f\u2502 \u22c5r\u239f\u22c5\u212f ", " \u23a0\u2502\u03be\u2081=r \u239c 2 \u239f\u2502 \u239f ", " \u239dd\u03be\u2081 \u23a0\u2502\u03be\u2081=r \u23a0 " ] } ], "prompt_number": 10 }, { "cell_type": "code", "input": [ "ricci[1][1]" ], "language": "python", "outputs": [ { "latex": [ "$$- \\left(\\boldsymbol{\\mathrm{r}}\\right)^{-1} \\left(\\left(\\left(\\left. \\frac{\\partial}{\\partial \\xi_{1}} \\operatorname{f}{\\left (\\xi_{1} \\right )} \\right|_{\\substack{ \\xi_{1}=\\boldsymbol{\\mathrm{r}} }}\\right)^{2} - \\left. \\frac{\\partial}{\\partial \\xi_{1}} \\operatorname{f}{\\left (\\xi_{1} \\right )} \\right|_{\\substack{ \\xi_{1}=\\boldsymbol{\\mathrm{r}} }} \\left. \\frac{\\partial}{\\partial \\xi_{1}} \\operatorname{g}{\\left (\\xi_{1} \\right )} \\right|_{\\substack{ \\xi_{1}=\\boldsymbol{\\mathrm{r}} }} + \\left. \\frac{\\partial^{2}}{\\partial^{2} \\xi_{1}} \\operatorname{f}{\\left (\\xi_{1} \\right )} \\right|_{\\substack{ \\xi_{1}=\\boldsymbol{\\mathrm{r}} }}\\right) \\boldsymbol{\\mathrm{r}} - 2 \\left. \\frac{\\partial}{\\partial \\xi_{1}} \\operatorname{g}{\\left (\\xi_{1} \\right )} \\right|_{\\substack{ \\xi_{1}=\\boldsymbol{\\mathrm{r}} }}\\right)$$" ], "output_type": "pyout", "prompt_number": 11, "text": [ "", " \u239b\u239b 2 \u239b 2 ", " -1 \u239c\u239c\u239b d \u239e\u2502 \u239b d \u239e\u2502 \u239b d \u239e\u2502 \u239c d ", "-r \u22c5\u239c\u239c\u239c\u2500\u2500\u2500(f(\u03be\u2081))\u239f\u2502 - \u239c\u2500\u2500\u2500(f(\u03be\u2081))\u239f\u2502 \u22c5\u239c\u2500\u2500\u2500(g(\u03be\u2081))\u239f\u2502 + \u239c\u2500\u2500\u2500\u2500(f(\u03be\u2081))", " \u239c\u239c\u239dd\u03be\u2081 \u23a0\u2502\u03be\u2081=r \u239dd\u03be\u2081 \u23a0\u2502\u03be\u2081=r \u239dd\u03be\u2081 \u23a0\u2502\u03be\u2081=r \u239c 2 ", " \u239d\u239d \u239dd\u03be\u2081 ", "", "\u239e\u2502 \u239e \u239e", "\u239f\u2502 \u239f \u239b d \u239e\u2502 \u239f", "\u239f\u2502 \u239f\u22c5r - 2\u22c5\u239c\u2500\u2500\u2500(g(\u03be\u2081))\u239f\u2502 \u239f", "\u239f\u2502 \u239f \u239dd\u03be\u2081 \u23a0\u2502\u03be\u2081=r\u239f", "\u23a0\u2502\u03be\u2081=r\u23a0 \u23a0" ] } ], "prompt_number": 11 }, { "cell_type": "code", "input": [ "ricci[2][2]" ], "language": "python", "outputs": [ { "latex": [ "$$\\left(- \\left. \\frac{\\partial}{\\partial \\xi_{1}} \\operatorname{f}{\\left (\\xi_{1} \\right )} \\right|_{\\substack{ \\xi_{1}=\\boldsymbol{\\mathrm{r}} }} \\boldsymbol{\\mathrm{r}} + \\left. \\frac{\\partial}{\\partial \\xi_{1}} \\operatorname{g}{\\left (\\xi_{1} \\right )} \\right|_{\\substack{ \\xi_{1}=\\boldsymbol{\\mathrm{r}} }} \\boldsymbol{\\mathrm{r}} -1 + e^{2 \\operatorname{g}{\\left (\\boldsymbol{\\mathrm{r}} \\right )}}\\right) e^{- 2 \\operatorname{g}{\\left (\\boldsymbol{\\mathrm{r}} \\right )}}$$" ], "output_type": "pyout", "prompt_number": 12, "text": [ "", "\u239b \u239b d \u239e\u2502 \u239b d \u239e\u2502 2\u22c5g(r)\u239e -2\u22c5g(r)", "\u239c- \u239c\u2500\u2500\u2500(f(\u03be\u2081))\u239f\u2502 \u22c5r + \u239c\u2500\u2500\u2500(g(\u03be\u2081))\u239f\u2502 \u22c5r - 1 + \u212f \u239f\u22c5\u212f ", "\u239d \u239dd\u03be\u2081 \u23a0\u2502\u03be\u2081=r \u239dd\u03be\u2081 \u23a0\u2502\u03be\u2081=r \u23a0 " ] } ], "prompt_number": 12 }, { "cell_type": "code", "input": [ "ricci[3][3]" ], "language": "python", "outputs": [ { "latex": [ "$$\\sin^{2}{\\left (\\boldsymbol{\\mathrm{\\theta}} \\right )} \\left(- \\left. \\frac{\\partial}{\\partial \\xi_{1}} \\operatorname{f}{\\left (\\xi_{1} \\right )} \\right|_{\\substack{ \\xi_{1}=\\boldsymbol{\\mathrm{r}} }} \\boldsymbol{\\mathrm{r}} + \\left. \\frac{\\partial}{\\partial \\xi_{1}} \\operatorname{g}{\\left (\\xi_{1} \\right )} \\right|_{\\substack{ \\xi_{1}=\\boldsymbol{\\mathrm{r}} }} \\boldsymbol{\\mathrm{r}} -1 + e^{2 \\operatorname{g}{\\left (\\boldsymbol{\\mathrm{r}} \\right )}}\\right) e^{- 2 \\operatorname{g}{\\left (\\boldsymbol{\\mathrm{r}} \\right )}}$$" ], "output_type": "pyout", "prompt_number": 13, "text": [ "", " 2 \u239b \u239b d \u239e\u2502 \u239b d \u239e\u2502 2\u22c5g(r)\u239e -2\u22c5g(r)", "sin (\u03b8)\u22c5\u239c- \u239c\u2500\u2500\u2500(f(\u03be\u2081))\u239f\u2502 \u22c5r + \u239c\u2500\u2500\u2500(g(\u03be\u2081))\u239f\u2502 \u22c5r - 1 + \u212f \u239f\u22c5\u212f ", " \u239d \u239dd\u03be\u2081 \u23a0\u2502\u03be\u2081=r \u239dd\u03be\u2081 \u23a0\u2502\u03be\u2081=r \u23a0 " ] } ], "prompt_number": 13 }, { "cell_type": "markdown", "source": [ "The off-diagonal components are zero." ] }, { "cell_type": "code", "input": [ "all(ricci[i][j]==0 for i in range(4) for j in range(4) if i!=j)" ], "language": "python", "outputs": [ { "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAACsAAAASCAYAAADCKCelAAAABHNCSVQICAgIfAhkiAAAAjJJREFU\nSInt1Vtoz2EYB/DPThiNslIa0syFHNpKc8qiqU2Klhs3oiV3SOLKhQuUQ9wop5Q55UJJEW7YyI0i\nFE3LULhgKdzMqbl437/99vM/JG0lvjfv7/d9n8P3fXre5+UvQlHieyYe4hMe4yPK0YAvuI1vkatD\nBSrxfgj1/sQuHMaIBDcdfTiRsp2Md0Mjqx+lie9aLMf3BNcQ1xspvxe4M3iysqM4rnVoN1Ao/WJv\npfgi9AyerPxYhvFZ+Nd4noUfiVWDqug3USP0a1uWvZU4hEuC8DXYg7OojzZTos1lvx5sI67lyFuL\nYzHeAZwSLnNetEaxrSl+OPbG7y5cwUJhMryMAotxBGXYhEepGPdwOkvOtcJEqkpw27CikNiTUWxN\nim9CC4ahFzsjPxEPsAhLhOrDVVxI+FcII3BdKu48fMWCBFcvFGNcIbHdQs+mMRdjhMvXh1lZbKri\nYaqES9uS2GuOflNTPtfxFvuwHwexHqMLCZ0QA57PY7NDmAhFeWw244PQOhnsxpuUXZlQ7aP5RBXn\n4HONrCQWo0M4VC404SY+p2Jn4lbHtRIlePYnYjty7JcL7dCeLzgm4WnKb7bwdMOWuPYIz3vykcpg\nmtjf2cQWo1F485/kEDFf6MlCYrswNvG/PQp6jhnojHymBZoNbKtGbMUZiY0SXMQooRqZGXtX6Lk2\nnEsEWY0NmCN/G1QLI6wz5jqOpcLE6BYq2xttS4XZWoVXQjHux9z5cvzHv4UfsudwW40c0GcAAAAA\nSUVORK5CYII=\n", "prompt_number": 14, "text": [ "True" ] } ], "prompt_number": 14 }, { "cell_type": "markdown", "source": [ "For completeness we can also check out the Christoffel symbol of 2nd kind. We will print only the non-zero components, and only one of the symmetric components (symmetric in the last two indices)." ] }, { "cell_type": "code", "input": [ "ch_2nd = metric_to_Christoffel_2nd(metric)", "filt = [((i,j,k), diffgeom_simplify(ch_2nd[i][j][k]))", " for i in range(4) for j in range(4) for k in range(j,4)", " if ch_2nd[i][j][k]!=0]" ], "language": "python", "outputs": [], "prompt_number": 15 }, { "cell_type": "code", "input": [ "filt[0:3]" ], "language": "python", "outputs": [ { "latex": [ "$$\\begin{bmatrix}\\begin{pmatrix}\\begin{pmatrix}0, & 0, & 1\\end{pmatrix}, & \\left. \\frac{\\partial}{\\partial \\xi_{1}} \\operatorname{f}{\\left (\\xi_{1} \\right )} \\right|_{\\substack{ \\xi_{1}=\\boldsymbol{\\mathrm{r}} }}\\end{pmatrix}, & \\begin{pmatrix}\\begin{pmatrix}1, & 0, & 0\\end{pmatrix}, & \\left. \\frac{\\partial}{\\partial \\xi_{1}} \\operatorname{f}{\\left (\\xi_{1} \\right )} \\right|_{\\substack{ \\xi_{1}=\\boldsymbol{\\mathrm{r}} }} e^{2 \\operatorname{f}{\\left (\\boldsymbol{\\mathrm{r}} \\right )} - 2 \\operatorname{g}{\\left (\\boldsymbol{\\mathrm{r}} \\right )}}\\end{pmatrix}, & \\begin{pmatrix}\\begin{pmatrix}1, & 1, & 1\\end{pmatrix}, & \\left. \\frac{\\partial}{\\partial \\xi_{1}} \\operatorname{g}{\\left (\\xi_{1} \\right )} \\right|_{\\substack{ \\xi_{1}=\\boldsymbol{\\mathrm{r}} }}\\end{pmatrix}\\end{bmatrix}$$" ], "output_type": "pyout", "prompt_number": 16, "text": [ "[((0, 0, 1), ", "\u239b d \u239e\u2502 ", "\u239c\u2500\u2500\u2500(f(\u03be\u2081))\u239f\u2502 ", "\u239dd\u03be\u2081 \u23a0\u2502\u03be\u2081=r),", " ((1, 0, 0),", " ", "\u239b d \u239e\u2502 2\u22c5f(r) - 2\u22c5g(r)", "\u239c\u2500\u2500\u2500(f(\u03be\u2081))\u239f\u2502 \u22c5\u212f ", "\u239dd\u03be\u2081 \u23a0\u2502\u03be\u2081=r ),", " ((1, 1, 1), ", "\u239b d \u239e\u2502 ", "\u239c\u2500\u2500\u2500(g(\u03be\u2081))\u239f\u2502 ", "\u239dd\u03be\u2081 \u23a0\u2502\u03be\u2081=r)]" ] } ], "prompt_number": 16 }, { "cell_type": "code", "input": [ "filt[3:6]" ], "language": "python", "outputs": [ { "latex": [ "$$\\begin{bmatrix}\\begin{pmatrix}\\begin{pmatrix}1, & 2, & 2\\end{pmatrix}, & - \\boldsymbol{\\mathrm{r}} e^{- 2 \\operatorname{g}{\\left (\\boldsymbol{\\mathrm{r}} \\right )}}\\end{pmatrix}, & \\begin{pmatrix}\\begin{pmatrix}1, & 3, & 3\\end{pmatrix}, & - \\sin^{2}{\\left (\\boldsymbol{\\mathrm{\\theta}} \\right )} \\boldsymbol{\\mathrm{r}} e^{- 2 \\operatorname{g}{\\left (\\boldsymbol{\\mathrm{r}} \\right )}}\\end{pmatrix}, & \\begin{pmatrix}\\begin{pmatrix}2, & 1, & 2\\end{pmatrix}, & \\left(\\boldsymbol{\\mathrm{r}}\\right)^{-1}\\end{pmatrix}\\end{bmatrix}$$" ], "output_type": "pyout", "prompt_number": 17, "text": [ "[((1, 2, 2), ", " -2\u22c5g(r)", "-r\u22c5\u212f ),", " ((1, 3, 3), ", " 2 -2\u22c5g(r)", "-sin (\u03b8)\u22c5r\u22c5\u212f ),", " ((2, 1, 2), ", " -1", "r )]" ] } ], "prompt_number": 17 }, { "cell_type": "code", "input": [ "filt[6:9]" ], "language": "python", "outputs": [ { "latex": [ "$$\\begin{bmatrix}\\begin{pmatrix}\\begin{pmatrix}2, & 3, & 3\\end{pmatrix}, & - \\cos{\\left (\\boldsymbol{\\mathrm{\\theta}} \\right )} \\sin{\\left (\\boldsymbol{\\mathrm{\\theta}} \\right )}\\end{pmatrix}, & \\begin{pmatrix}\\begin{pmatrix}3, & 1, & 3\\end{pmatrix}, & \\left(\\boldsymbol{\\mathrm{r}}\\right)^{-1}\\end{pmatrix}, & \\begin{pmatrix}\\begin{pmatrix}3, & 2, & 3\\end{pmatrix}, & \\frac{1}{\\tan{\\left (\\boldsymbol{\\mathrm{\\theta}} \\right )}}\\end{pmatrix}\\end{bmatrix}$$" ], "output_type": "pyout", "prompt_number": 18, "text": [ "[((2, 3, 3), -cos(\u03b8)\u22c5sin(\u03b8)),", " ((3, 1, 3), ", " -1", "r ),", " ((3, 2, 3), ", " -1 ", "tan (\u03b8))]" ] } ], "prompt_number": 18 }, { "cell_type": "markdown", "source": [ "We can also confirm that the Christoffel symbol is symmetric." ] }, { "cell_type": "code", "input": [ "all([ch_2nd[k][i][j] == ch_2nd[k][j][i] for k in range(4) for i in range(4) for j in range(4)])" ], "language": "python", "outputs": [ { "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAACsAAAASCAYAAADCKCelAAAABHNCSVQICAgIfAhkiAAAAjJJREFU\nSInt1Vtoz2EYB/DPThiNslIa0syFHNpKc8qiqU2Klhs3oiV3SOLKhQuUQ9wop5Q55UJJEW7YyI0i\nFE3LULhgKdzMqbl437/99vM/JG0lvjfv7/d9n8P3fXre5+UvQlHieyYe4hMe4yPK0YAvuI1vkatD\nBSrxfgj1/sQuHMaIBDcdfTiRsp2Md0Mjqx+lie9aLMf3BNcQ1xspvxe4M3iysqM4rnVoN1Ao/WJv\npfgi9AyerPxYhvFZ+Nd4noUfiVWDqug3USP0a1uWvZU4hEuC8DXYg7OojzZTos1lvx5sI67lyFuL\nYzHeAZwSLnNetEaxrSl+OPbG7y5cwUJhMryMAotxBGXYhEepGPdwOkvOtcJEqkpw27CikNiTUWxN\nim9CC4ahFzsjPxEPsAhLhOrDVVxI+FcII3BdKu48fMWCBFcvFGNcIbHdQs+mMRdjhMvXh1lZbKri\nYaqES9uS2GuOflNTPtfxFvuwHwexHqMLCZ0QA57PY7NDmAhFeWw244PQOhnsxpuUXZlQ7aP5RBXn\n4HONrCQWo0M4VC404SY+p2Jn4lbHtRIlePYnYjty7JcL7dCeLzgm4WnKb7bwdMOWuPYIz3vykcpg\nmtjf2cQWo1F485/kEDFf6MlCYrswNvG/PQp6jhnojHymBZoNbKtGbMUZiY0SXMQooRqZGXtX6Lk2\nnEsEWY0NmCN/G1QLI6wz5jqOpcLE6BYq2xttS4XZWoVXQjHux9z5cvzHv4UfsudwW40c0GcAAAAA\nSUVORK5CYII=\n", "prompt_number": 19, "text": [ "True" ] } ], "prompt_number": 19 } ] } ] }sympy-0.7.4.1/examples/intermediate/partial_differential_eqs.py0000755000175000017500000000370112253362407025132 0ustar georgeskgeorgesk#!/usr/bin/env python """Partial Differential Equations example Demonstrates various ways to solve partial differential equations """ from sympy import symbols, Eq, Function, pde_separate, pprint, sin, cos, latex from sympy import Derivative as D def main(): r, phi, theta = symbols("r,phi,theta") Xi = Function('Xi') R, Phi, Theta, u = map(Function, ['R', 'Phi', 'Theta', 'u']) C1, C2 = symbols('C1,C2') pprint("Separation of variables in Laplace equation in spherical coordinates") pprint("Laplace equation in spherical coordinates:") eq = Eq(D(Xi(r, phi, theta), r, 2) + 2/r * D(Xi(r, phi, theta), r) + 1/(r**2 * sin(phi)**2) * D(Xi(r, phi, theta), theta, 2) + cos(phi)/(r**2 * sin(phi)) * D(Xi(r, phi, theta), phi) + 1/r**2 * D(Xi(r, phi, theta), phi, 2)) pprint(eq) pprint("We can either separate this equation in regards with variable r:") res_r = pde_separate(eq, Xi(r, phi, theta), [R(r), u(phi, theta)]) pprint(res_r) pprint("Or separate it in regards of theta:") res_theta = pde_separate(eq, Xi(r, phi, theta), [Theta(theta), u(r, phi)]) pprint(res_theta) res_phi = pde_separate(eq, Xi(r, phi, theta), [Phi(phi), u(r, theta)]) pprint("But we cannot separate it in regards of variable phi: ") pprint("Result: %s" % res_phi) pprint("\n\nSo let's make theta dependent part equal with -C1:") eq_theta = Eq(res_theta[0], -C1) pprint(eq_theta) pprint("\nThis also means that second part is also equal to -C1:") eq_left = Eq(res_theta[1], -C1) pprint(eq_left) pprint("\nLets try to separate phi again :)") res_theta = pde_separate(eq_left, u(r, phi), [Phi(phi), R(r)]) pprint("\nThis time it is successful:") pprint(res_theta) pprint("\n\nSo our final equations with separated variables are:") pprint(eq_theta) pprint(Eq(res_theta[0], C2)) pprint(Eq(res_theta[1], C2)) if __name__ == "__main__": main() sympy-0.7.4.1/examples/intermediate/infinite_1d_box.py0000755000175000017500000000553712253362407023164 0ustar georgeskgeorgesk#!/usr/bin/env python """ Applying perturbation theory to calculate the ground state energy of the infinite 1D box of width ``a`` with a perturbation which is linear in ``x``, up to second order in perturbation """ from sympy.core import pi from sympy import Integral, var, S from sympy.functions import sin, sqrt def X_n(n, a, x): """ Returns the wavefunction X_{n} for an infinite 1D box ``n`` the "principal" quantum number. Corresponds to the number of nodes in the wavefunction. n >= 0 ``a`` width of the well. a > 0 ``x`` x coordinate. """ n, a, x = map(S, [n, a, x]) C = sqrt(2 / a) return C * sin(pi * n * x / a) def E_n(n, a, mass): """ Returns the Energy psi_{n} for a 1d potential hole with infinity borders ``n`` the "principal" quantum number. Corresponds to the number of nodes in the wavefunction. n >= 0 ``a`` width of the well. a > 0 ``mass`` mass. """ return ((n * pi / a)**2) / mass def energy_corrections(perturbation, n, a=10, mass=0.5): """ Calculating first two order corrections due to perturbation theory and returns tuple where zero element is unperturbated energy, and two second is corrections ``n`` the "nodal" quantum number. Corresponds to the number of nodes in the wavefunction. n >= 0 ``a`` width of the well. a > 0 ``mass`` mass. """ x, _a = var("x _a") Vnm = lambda n, m, a: Integral(X_n(n, a, x) * X_n(m, a, x) * perturbation.subs({_a: a}), (x, 0, a)).n() # As we know from theory for V0*r/a we will just V(n, n-1) and V(n, n+1) # wouldn't equals zero return (E_n(n, a, mass).evalf(), Vnm(n, n, a).evalf(), (Vnm(n, n - 1, a)**2/(E_n(n, a, mass) - E_n(n - 1, a, mass)) + Vnm(n, n + 1, a)**2/(E_n(n, a, mass) - E_n(n + 1, a, mass))).evalf()) def main(): print() print("Applying perturbation theory to calculate the ground state energy") print("of the infinite 1D box of width ``a`` with a perturbation") print("which is linear in ``x``, up to second order in perturbation.") print() x, _a = var("x _a") perturbation = .1 * x / _a E1 = energy_corrections(perturbation, 1) print("Energy for first term (n=1):") print("E_1^{(0)} = ", E1[0]) print("E_1^{(1)} = ", E1[1]) print("E_1^{(2)} = ", E1[2]) print() E2 = energy_corrections(perturbation, 2) print("Energy for second term (n=2):") print("E_2^{(0)} = ", E2[0]) print("E_2^{(1)} = ", E2[1]) print("E_2^{(2)} = ", E2[2]) print() E3 = energy_corrections(perturbation, 3) print("Energy for third term (n=3):") print("E_3^{(0)} = ", E3[0]) print("E_3^{(1)} = ", E3[1]) print("E_3^{(2)} = ", E3[2]) print() if __name__ == "__main__": main() sympy-0.7.4.1/examples/intermediate/vandermonde.py0000755000175000017500000001116312253362407022415 0ustar georgeskgeorgesk#!/usr/bin/env python """Vandermonde matrix example Demonstrates matrix computations using the Vandermonde matrix. * http://en.wikipedia.org/wiki/Vandermonde_matrix """ from sympy import Matrix, pprint, Rational, sqrt, symbols, Symbol, zeros from sympy.core.compatibility import xrange def symbol_gen(sym_str): """Symbol generator Generates sym_str_n where n is the number of times the generator has been called. """ n = 0 while True: yield Symbol("%s_%d" % (sym_str, n)) n += 1 def comb_w_rep(n, k): """Combinations with repetition Returns the list of k combinations with repetition from n objects. """ if k == 0: return [[]] combs = [[i] for i in range(n)] for i in range(k - 1): curr = [] for p in combs: for m in range(p[-1], n): curr.append(p + [m]) combs = curr return combs def vandermonde(order, dim=1, syms='a b c d'): """Comptutes a Vandermonde matrix of given order and dimension. Define syms to give beginning strings for temporary variables. Returns the Matrix, the temporary variables, and the terms for the polynomials. """ syms = syms.split() n = len(syms) if n < dim: new_syms = [] for i in range(dim - n): j, rem = divmod(i, n) new_syms.append(syms[rem] + str(j)) syms.extend(new_syms) terms = [] for i in range(order + 1): terms.extend(comb_w_rep(dim, i)) rank = len(terms) V = zeros(rank) generators = [symbol_gen(syms[i]) for i in range(dim)] all_syms = [] for i in range(rank): row_syms = [next(g) for g in generators] all_syms.append(row_syms) for j, term in enumerate(terms): v_entry = 1 for k in term: v_entry *= row_syms[k] V[i*rank + j] = v_entry return V, all_syms, terms def gen_poly(points, order, syms): """Generates a polynomial using a Vandermonde system""" num_pts = len(points) if num_pts == 0: raise ValueError("Must provide points") dim = len(points[0]) - 1 if dim > len(syms): raise ValueError("Must provide at lease %d symbols for the polynomial" % dim) V, tmp_syms, terms = vandermonde(order, dim) if num_pts < V.shape[0]: raise ValueError( "Must provide %d points for order %d, dimension " "%d polynomial, given %d points" % (V.shape[0], order, dim, num_pts)) elif num_pts > V.shape[0]: print("gen_poly given %d points but only requires %d, "\ "continuing using the first %d points" % \ (num_pts, V.shape[0], V.shape[0])) num_pts = V.shape[0] subs_dict = {} for j in range(dim): for i in range(num_pts): subs_dict[tmp_syms[i][j]] = points[i][j] V_pts = V.subs(subs_dict) V_inv = V_pts.inv() coeffs = V_inv.multiply(Matrix([points[i][-1] for i in xrange(num_pts)])) f = 0 for j, term in enumerate(terms): t = 1 for k in term: t *= syms[k] f += coeffs[j]*t return f def main(): order = 2 V, tmp_syms, _ = vandermonde(order) print("Vandermonde matrix of order 2 in 1 dimension") pprint(V) print('-'*79) print("Computing the determinate and comparing to \sum_{0 out SUCCESSFUL: - beginner.basic [...] NO FAILED EXAMPLES $ When examples fail: $ ./all.py -w > out Traceback (most recent call last): File "./all.py", line 111, in run_examples [...] SUCCESSFUL: - beginner.basic [...] FAILED: - intermediate.mplot2D [...] $ Obviously, we want to achieve the first result. """ import imp import optparse import os import sys import traceback # add local sympy to the module path this_file = os.path.abspath(__file__) sympy_dir = os.path.join(os.path.dirname(this_file), "..") sympy_dir = os.path.normpath(sympy_dir) sys.path.insert(0, sympy_dir) import sympy TERMINAL_EXAMPLES = [ "beginner.basic", "beginner.differentiation", "beginner.expansion", "beginner.functions", "beginner.limits_examples", "beginner.precision", "beginner.print_pretty", "beginner.series", "beginner.substitution", "intermediate.coupled_cluster", "intermediate.differential_equations", "intermediate.infinite_1d_box", "intermediate.partial_differential_eqs", "intermediate.trees", "intermediate.vandermonde", "advanced.curvilinear_coordinates", "advanced.dense_coding_example", "advanced.fem", "advanced.gibbs_phenomenon", "advanced.grover_example", "advanced.hydrogen", "advanced.pidigits", "advanced.qft", "advanced.relativity", "galgebra.eval_check", "galgebra.exp_check", "galgebra.manifold_check", "galgebra.mv_setup_options", "galgebra.prob_not_solenoidal", "galgebra.simple_check", "galgebra.terminal_check", ] WINDOWED_EXAMPLES = [ "beginner.plotting_nice_plot", "intermediate.mplot2d", "intermediate.mplot3d", "intermediate.print_gtk", "advanced.autowrap_integrators", "advanced.autowrap_ufuncify", "advanced.pyglet_plotting", "galgebra.latex_check", "galgebra.manifold_check_latex", "galgebra.matrix_latex", "galgebra.physics_check_latex", "galgebra.print_check_latex", "galgebra.products_latex", "galgebra.simple_check_latex", "galgebra.spherical_latex", ] EXAMPLE_DIR = os.path.dirname(__file__) def __import__(name, globals=None, locals=None, fromlist=None): """An alternative to the import function so that we can import modules defined as strings. This code was taken from: http://docs.python.org/lib/examples-imp.html """ # Fast path: see if the module has already been imported. try: return sys.modules[name] except KeyError: pass # If any of the following calls raises an exception, # there's a problem we can't handle -- let the caller handle it. module_name = name.split('.')[-1] module_path = os.path.join(EXAMPLE_DIR, *name.split('.')[:-1]) fp, pathname, description = imp.find_module(module_name, [module_path]) try: return imp.load_module(module_name, fp, pathname, description) finally: # Since we may exit via an exception, close fp explicitly. if fp: fp.close() def load_example_module(example): """Loads modules based upon the given package name""" mod = __import__(example) return mod def run_examples(windowed=False, quiet=False, summary=True): """Run all examples in the list of modules. Returns a boolean value indicating whether all the examples were successful. """ successes = [] failures = [] examples = TERMINAL_EXAMPLES if windowed: examples += WINDOWED_EXAMPLES if quiet: from sympy.utilities.runtests import PyTestReporter reporter = PyTestReporter() reporter.write("Testing Examples\n") reporter.write("-" * reporter.terminal_width) else: reporter = None for example in examples: if run_example(example, reporter=reporter): successes.append(example) else: failures.append(example) if summary: show_summary(successes, failures, reporter=reporter) return len(failures) == 0 def run_example(example, reporter=None): """Run a specific example. Returns a boolean value indicating whether the example was successful. """ if reporter: reporter.write(example) else: print("=" * 79) print("Running: ", example) try: mod = load_example_module(example) if reporter: suppress_output(mod.main) reporter.write("[PASS]", "Green", align="right") else: mod.main() return True except: if reporter: reporter.write("[FAIL]", "Red", align="right") traceback.print_exc() return False class DummyFile(object): def write(self, x): pass def suppress_output(fn): """Suppresses the output of fn on sys.stdout.""" save_stdout = sys.stdout try: sys.stdout = DummyFile() fn() finally: sys.stdout = save_stdout def show_summary(successes, failures, reporter=None): """Shows a summary detailing which examples were successful and which failed.""" if reporter: reporter.write("-" * reporter.terminal_width) if failures: reporter.write("FAILED:\n", "Red") for example in failures: reporter.write(" %s\n" % example) else: reporter.write("ALL EXAMPLES PASSED\n", "Green") else: if successes: print("SUCCESSFUL: ", file=sys.stderr) for example in successes: print(" -", example, file=sys.stderr) else: print("NO SUCCESSFUL EXAMPLES", file=sys.stderr) if failures: print("FAILED: ", file=sys.stderr) for example in failures: print(" -", example, file=sys.stderr) else: print("NO FAILED EXAMPLES", file=sys.stderr) def main(*args, **kws): """Main script runner""" parser = optparse.OptionParser() parser.add_option('-w', '--windowed', action="store_true", dest="windowed", help="also run examples requiring windowed environment") parser.add_option('-q', '--quiet', action="store_true", dest="quiet", help="runs examples in 'quiet mode' suppressing example output and \ showing simple status messages.") parser.add_option('--no-summary', action="store_true", dest="no_summary", help="hides the summary at the end of testing the examples") (options, _) = parser.parse_args() return 0 if run_examples(windowed=options.windowed, quiet=options.quiet, summary=not options.no_summary) else 1 if __name__ == "__main__": sys.exit(main(*sys.argv[1:])) sympy-0.7.4.1/examples/notebooks/0000755000175000017500000000000012253362407017065 5ustar georgeskgeorgesksympy-0.7.4.1/examples/notebooks/trace.ipynb0000644000175000017500000001522412253362407021232 0ustar georgeskgeorgesk{ "metadata": { "name": "trace" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "code", "collapsed": true, "input": [ "from sympy import symbols\n", "from sympy.core.trace import Tr\n", "from sympy.matrices.matrices import Matrix\n", "from IPython.core.display import display_pretty\n", "from sympy.printing.latex import *\n", "\n", "%load_ext sympyprinting" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 2 }, { "cell_type": "markdown", "metadata": {}, "source": [ "###Basic Examples" ] }, { "cell_type": "code", "collapsed": true, "input": [ "a, b, c, d = symbols('a b c d'); \n", "A, B = symbols('A B', commutative=False)\n", "t = Tr(A*B)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 3 }, { "cell_type": "code", "collapsed": false, "input": [ "t" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\mbox{Tr}\\left(A B\\right)$$" ], "output_type": "pyout", "prompt_number": 4, "text": [ "Tr(A\u22c5B)" ] } ], "prompt_number": 4 }, { "cell_type": "code", "collapsed": false, "input": [ "latex(t)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 5, "text": [ "\\mbox{Tr}\\left(A B\\right)" ] } ], "prompt_number": 5 }, { "cell_type": "code", "collapsed": false, "input": [ "display_pretty(t)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "display_data", "text": [ "Tr(\u03c1((\u27581,1\u27e9, 0.5),(\u27581,-1\u27e9, 0.5)))" ] } ], "prompt_number": 14 }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Using Matrices" ] }, { "cell_type": "code", "collapsed": true, "input": [ "t = Tr ( Matrix([ [2,3], [3,4] ]))" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 15 }, { "cell_type": "code", "collapsed": false, "input": [ "t" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$6$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAAwAAAASCAYAAABvqT8MAAAABHNCSVQICAgIfAhkiAAAAO5JREFU\nKJHN0r1KQ0EQhuHnhAgBhaiIFpLOxs5O8CIsFG/A1spCL0CwsUtnaat4C7aWNooiCAEJKBb+oMGg\nSCzOHlyWlWDnV+3M7vvN7O7wRxWZXAs7+MIL3rCHXs5gGh0shXgS19isDtQSYB9tnIa4jgZec+5r\n+MD4sHtUOsTlsEP1aL2AeyxiGbNoYgs3KTiqfJVzbET5FTxhLgVmMEAfY1G+FqoepcBIAC4ybZ8p\n/6KoHOATd6F8ql5oeSIG4ARTGaCBLh7TjVW8V05BBZ5xkDECx9j1M2PruIpN0uFrYhvzofcH5ajc\n/lbhH+gb6f4rZTpaz0QAAAAASUVORK5CYII=\n", "prompt_number": 16, "text": [ "6" ] } ], "prompt_number": 16 }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Example using modules in physics.quantum" ] }, { "cell_type": "code", "collapsed": true, "input": [ "from sympy.physics.quantum.density import Density\n", "from sympy.physics.quantum.spin import (\n", " Jx, Jy, Jz, Jplus, Jminus, J2,\n", " JxBra, JyBra, JzBra,\n", " JxKet, JyKet, JzKet,\n", ")" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 7 }, { "cell_type": "code", "collapsed": false, "input": [ "d = Density([JzKet(1,1),0.5],[JzKet(1,-1),0.5]); d" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\rho\\left(\\begin{pmatrix}{\\left|1,1\\right\\rangle }, & 0.5\\end{pmatrix},\\begin{pmatrix}{\\left|1,-1\\right\\rangle }, & 0.5\\end{pmatrix}\\right)$$" ], "output_type": "pyout", "prompt_number": 8, "text": [ "\u03c1((\u27581,1\u27e9, 0.5),(\u27581,-1\u27e9, 0.5))" ] } ], "prompt_number": 8 }, { "cell_type": "code", "collapsed": true, "input": [ "t = Tr(d)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 9 }, { "cell_type": "code", "collapsed": false, "input": [ "t" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\mbox{Tr}\\left(\\rho\\left(\\begin{pmatrix}{\\left|1,1\\right\\rangle }, & 0.5\\end{pmatrix},\\begin{pmatrix}{\\left|1,-1\\right\\rangle }, & 0.5\\end{pmatrix}\\right)\\right)$$" ], "output_type": "pyout", "prompt_number": 10, "text": [ "Tr(\u03c1((\u27581,1\u27e9, 0.5),(\u27581,-1\u27e9, 0.5)))" ] } ], "prompt_number": 10 }, { "cell_type": "code", "collapsed": false, "input": [ "latex(t)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 11, "text": [ "\n", "\\mbox{Tr}\\left(\\rho\\left(\\begin{pmatrix}{\\left|1,1\\right\\rangle }, & 0.5\\end{p\n", "matrix},\\begin{pmatrix}{\\left|1,-1\\right\\rangle }, & 0.5\\end{pmatrix}\\right)\\r\n", "ight)" ] } ], "prompt_number": 11 }, { "cell_type": "code", "collapsed": false, "input": [ "t.doit()" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$1.0$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAABsAAAASCAYAAACq26WdAAAABHNCSVQICAgIfAhkiAAAASNJREFU\nOI3t1EErBGEcx/HPimLXgUiU5eKinGxyc8KLkLeDK+WqpChcpJQjBxc33JALsgfFwYrEOsws0za7\nBpOT3+U/z/eZZ77PMzPPwx8mU4P3Yg8DCZ/TjDlkUQzbqziuNyiHSZyinFAEy1iLtMdxja5aAwax\nhVkcfEPWjxdMVPErrCSdaVLZPB4Fry6aJdyioQIa/D4FXOKpip+jA0NpyrrxEMNLYe1JW1aK4RXW\nlqbsGW8xvCmsH31pyE5q8FxYb9KUHaM9hreGtZi2LB/DR3Av+FN/JcujJbw+EqxsNNLfiDEsCr5p\n3WwLNnXccVPAK3bDdgab2PA5+SnBduisJejCPs5CURl3OMR05L4+XGAmwrKCk2QdC9jB8Fcr+s+P\n8g572TfbrLZhHwAAAABJRU5ErkJggg==\n", "prompt_number": 12, "text": [ "1.00000000000000" ] } ], "prompt_number": 12 }, { "cell_type": "code", "collapsed": true, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 12 } ], "metadata": {} } ] }sympy-0.7.4.1/examples/notebooks/fresnel_integrals.ipynb0000644000175000017500000064155512253362407023656 0ustar georgeskgeorgesk{ "metadata": { "name": "fresnel_integrals" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "code", "collapsed": false, "input": "%pylab inline", "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": "\nWelcome to pylab, a matplotlib-based Python environment [backend: module://IPython.zmq.pylab.backend_inline].\nFor more information, type 'help(pylab)'.\n" } ], "prompt_number": 1 }, { "cell_type": "code", "collapsed": true, "input": "from sympy import Symbol, fresnels, fresnelc, oo, I, re, im, series, Rational, sin, cos, exp, plot\nfrom sympy.plotting import plot, plot_parametric\nfrom matplotlib.pyplot import figsize", "language": "python", "metadata": {}, "outputs": [], "prompt_number": 2 }, { "cell_type": "markdown", "metadata": {}, "source": "Plot of the two Fresnel integrals $S(x)$ and $C(x)$" }, { "cell_type": "code", "collapsed": false, "input": "x = Symbol(\"x\")", "language": "python", "metadata": {}, "outputs": [], "prompt_number": 3 }, { "cell_type": "code", "collapsed": false, "input": "plot(fresnels(x), fresnelc(x), (x, 0, 8))", "language": "python", "metadata": {}, "outputs": [ { "output_type": "display_data", "png": "iVBORw0KGgoAAAANSUhEUgAAAWYAAAD8CAYAAABErA6HAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJztnXl0HNWd778t4d2WWwveN7UW77IkL2wmlpFkCAkEEGPD\n4yWTTcCbmSQnDA5hMjPRSU4OBE1mkkA2y+GFCZlHhJSEhASI1WwxBtuy2rZkbZZLtmXjVa2WsY1s\nyer3x09XVV1dVV3VXS2VpN/nHA5WV3XVreru7/3V9/7u77qCwWAQDMMwjGNIGO4GMAzDMKGwMDMM\nwzgMFmaGYRiHwcLMMAzjMK4b7gYoqa6uhtvthiRJKC0ttbydYRhmNOCYiLmurg4AUFhYCADw+Xwh\n230+HzweDwoLC+HxeMK2MwzDjBYcI8yVlZVITk4GAHg8HtTU1ITt88QTTwAAJElCXl7ekLaPYRhm\nqHCMMAcCAaSkpAz+3dnZGbI9Ly8P6enpSElJCdmPYRhmtOEoj9lorksgEEBmZiYqKipQWlqK/Px8\npKenD253uVz49re/Pfh3QUEBCgoK4tlchmGYuOAYYXa73fD7/QCArq4upKamhmyvqKjAI488gqSk\nJLjdblRVVWHr1q0h+5SVlQ1VcxmGYeKGY6yMLVu2QJIkAEB7ezuKi4sBUKQsSEpKAkADhG63e+gb\nyTAMMwS4nFQro6KiAh6PJyQdbs2aNaitrQUAlJeXw+PxwO/3h6XLuVwuQyuEYRhmpOAoYY4FFmaG\nYUYLjrEyGIZhGMIxg3+jja4u4NVXgYQE4K67gAF7nGEYJiJsZcQBSQK+/nVg4UKgtxc4dAh4+WVg\n5szhbhnDMCMBFmabuXIF+NzngE98AvjHf6TXvvtd4PBh4IUXAJdreNvHMIzzYY/ZZp5/Hrh6FfiH\nf5Bfe/JJ4MAB4A9/GL52MQwzcuCI2UYuXAAyMoCdO4HFi0O3/eUvwFNPAe++y1EzwzDGcMRsI7/6\nFfDZz4aLMgDccQdw+jTw/vtD3iyGYUYYLMw2EQwCr78OlJRob09IAL7yFWD79qFtF8MwIw8WZpt4\n/32gvR24+Wb9ff7u72ifnp6haxfDMCMPFmab+N3vyMYw8o9nzwauXQO83qFrF8MwIw8WZhu4dg34\n9a8pIo7E5s3ABx/Ev00Mw4xcWJht4IMPaPJIVlbkfQsLScRHRy4MwzDxgIXZBv72N+DBB83tu2QJ\n5TkPVDhlGIYJg4XZBl56Cdiwwdy+LhdQVARoLGnIMAwDgIU5Zs6eBY4eBdatM/+eoiIeAGQYRh8W\n5hjxeilavs5Cnb6NG4FLl9hnZhhGGxbmGKmpoQjYCvPnA/X1wJEj8WkTwzAjGxbmGAgGgR07rAsz\nANx4I6fNMQyjDQtzDEgSRb9Lllh/LwszwzB6OEqYq6ur4fV6UVFREbatrq4OCQkJyMzMRGZmJh59\n9NFhaGEo774LLFgQXbW4SMLc3w/4/dG3jWGYkYtjlpaqq6sDABQWFkKSJPh8PuTl5Q1u7+rqQn9/\nPwDA5/MhOTl5WNqpZNcu4JZbontvXh7Q1ARcvgxMnhy67ehRWgHl8mUgJ4cK7U+cGHNzGYYZITgm\nYq6srBwUW4/HgxpVom9hYeHgv2tra7Fo0aKhbJ4mu3YZFy0yYtIk4L77qIC+kmvXgH/9V4qo//Qn\n4OOPgfLy2NvKMMzIwTHCHAgEkJKSMvh3Z2en5n5erxebN28eqmbp4vcDx49TRBstkycDAw8Kg1RV\nAd3dwDe+AYwfD/zbvwF//COdi2GYsYFjrAwAplYg2bFjR0j0rKSsrGzw3wUFBSgoKLCpZeF88AFN\nKrGSv6wmNzdUmPv7gbIy4Kc/lX3rmTOBW28FfvIT4Pvfj6nJDMOMEBwjzG63G/6B0a6uri6kpqZq\n7lenDjEVKIU53tTXk2DGQm4urREo2LkTyM4G1P3Jo48CX/0q0NcXW0fAMMzIwDFWxpYtWyANVPZp\nb29HcXExALI4BJKDKv+8+SawZk1sx1i5EmhsJMEFyMa45ZbwLI/sbLJO3nortvMxDDMycIwwiwwM\nr9cLt9uN3NxcAECRYvaGy+VCRkbGsLRPSTAI1NYCq1fHdpypU4F584DmZhr0e+kl4P77tfd96CHy\nmhmGGf3wKtlR0N5ONsaJE7Efa8sW4K67aHXt738f+MMftPdraKBC/I2NvMo2w4x2HBMxjyT27o3d\nxhCsXw90dACvvQYsXaq/3/LlwJUrXF+DYcYCLMxRUFtrnzDPnw+89x7V3Ni0SX8/l4v85zfftOe8\nDMM4FxbmKLBTmJcuBQ4dojzlm24y3veOO4CDB+05L8MwzoWF2SL9/cC4cUB+vj3H83iAkyfp/5Gm\nXa9eDfzlL/acl2EY58LCbJFjxyjCnTHDnuONGwckJ5vL8MjOplmBH35oz7kZhnEmLMwWOXgQWLXK\n3mMGg+aKFCUk0Crbe/fae36GYZwFC7NFDhyIrT6GmmvXKAoeKJwXkawswOez7/wMwzgPFmaLHDxo\nrzA3NZGVcfSouf3z88MLHzEMM7pgYbaI3VbG7t0ktk1N5vZnYWaY0Q8LswUuXaLZftnZ9h3z2DHg\nzjuBtja5ZoYRixZRO86csa8NDMM4CxZmCzQ00Pp+dlZ4e+MNWs1k5kxzdobLRfuzz8wwoxcWZgvY\nbWP09ZHY5+TQRBOzdsZtt/HUbIYZzbAwW8DujIzWVmDuXGDaNGvCPG8e8P779rVDyaVLwH/9F/Cj\nHwFXr8bnHAzDGMPCbAG7MzL276di+YA1YV6+nKrMxYNvfxs4fJgWAnjuuficg2EYY1iYTRIMOkeY\nly4FWlooB9pOmpqAd98F/uM/gO98hwor8SAjwww9LMwmOX6cFk+9/nr7jnnsmOxZL10KXLhAHUAk\npk6ldrS329cWAPj1r4G776brnDOH2vTSS6H7BIOU4me2E2EYxjoszCZpaJCjW7t49105Ak9NBc6e\nBc6dM/deu+2MYBD47/8G7r1Xfu2OO4Df/S50v+efB55+Gnj88fj53Awz1mFhNklDg3Ehe6t0ddFA\n27x58muZmZTPbIZly6iYkl3U19MqKsuWya994hPAxYvUVtHml18GnnkG+OIXgWefte/8DMPIsDCb\npLnZXmFuaqKcaOUyUVaEeflye4X5jTcoele2Z+JEIC2NInsA+P3v6R5kZQH33AOcP8/1oRkmHjhK\nmKurq+H1elFRUaG5va6uDtXV1brb40lzM7B4sX3Ha2wMjU4B68J85Yp97XnrLaCgIPz1TZsoTVDs\nc8cd9O/ERNr/r3+1rw0MwxCOEea6gQIQhYWFAACfxtS2p59+GiUlJQgEAprb40UwSFkQS5bYd8xY\nhTkzE3j9dXODhZHo76di/evXh2+74Qbg1VfJxjhzBtiwQd52yy3hg4MMw8SOY4S5srISycnJAACP\nx4OampqQ7VVVVVi7di0AYOvWrcjLyxuytp07R4/4aWn2HbOpKTZhTk4GJkygAcNYaWkhL3nmzPBt\n+fnkr9fU0D1Q1o2++Waanu73x94GhmFkHCPMgUAAKSkpg393dnaGbK+trUVnZyd8Ph/Ky8uHtG3C\nxlD6r7HS2BjuWVsRZoAG6+yYmr17N0XGWkyeTKur/P734VbHuHE0a/G992I7/y9/CTz0ELBrV2zH\nYZjRgmOEGQCCEZ7L09LSBiPl6urqsO1lZWWD/7399tu2tctuG+PiRYrCFy0KfT01lWwFsxGoVSHX\no6EBuPVW/e1ZWcCePdpWR1ERbYuWAwcoJa+wECgvD50GfvYs8PbbwOXL0R+fYUYiNtZJiw232w3/\ngCJ1dXUhNTU1ZHtqairS09MH9927dy9KSkpC9ikrK4tL25qb7RVmEYEnJoa+7nLJYrtuXeTjZGTY\nI8wffAB86lP621eupBxnrQVoV6yg2hrR8tvfAsXFlH63cydlh9x1F9DbS7nS3d10ju99L/pzMMxI\nwzER85YtWyBJEgCgvb0dxcXFAMjiAID7779/cHsgEMA6M8plE0ORkSGwOgAYq5XR3x+5al5SElka\nU6aEb1uzhqrkRTMIee0a2SD33Ud/r19PmR8A8Kc/0RqHL74I7NtH9TsYZqzgGGEWFoXX64Xb7Ubu\nwDS7oqIiAEB6ejrcbjeqq6vh9/txn/g1DwFDkZEhsCrMsUbM7e2A2w0o7P0wrlyh/7TEd+ZMqpJ3\n/Lj1c9fWUrbHggX09+23U252fz/wf/8vpepNmwbceCMJdTQEg/ZkrjDMUOIYYQaA0tJSFBYWorS0\ndPC12trakO0lJSV46qmnhqxNV67QqiUej33H1Br4E1gRWzsG/w4dIgE0or6eMkBOntTevmpVdBNN\n3n6bxFgwdy7ZO3V1dM5PfpJev/VW4G9/s37806eBzZuBrVtDveuPPza3WgzDDBeOEmYn0tZGg3Tj\nxtl3TK1UOYEVYZ4xgzqOAbcnKhoajKNlgEQ3K0t/pmG0wrxrFzCQATnIzJnA//t/lC89kD2J9eup\n0l13t7Xj//jH5I9fvAi88gq99uGHlAHyxS9aPx7DDBUszBGwe+CvpwdYuJCiXS2sCLPLFXvUbNRJ\nAGQr1NfTwJ+eMK9ZYz2fOhikqPWmm0Jf37iRls1S1hCZMIGq3u3ebf74PT00YPnlL1Pk/Ze/0OvP\nP0/pfzNmAP/zP9bazDBDBQtzBOwe+JMk8mP1IvCZMyk9zGw0F6vPbGSrANTWW28lMdOrZpeRAbz5\nprXznjxJHrNSgAHqJPbtI7FXsnixtWp6onLfnDlk1dTWAp2dlPnxwANURc9KRmUwSMWbvvAFOg5A\ng5cHDpA1wjB2wsIcAbsH/lpbyRbQQ6TMmY2CY4mY+/sjX19TE6WurVih78suXmx+lW9BXR0tKque\ntLN8OQneihWhry9dKhdTMsPu3ZSGBwCTJgGzZwMvvEDZJRkZFKnv3w90dJg73q5dJO5pacD27fRa\nRQX510M45MGMEViYI2C3lXH4sLEwA+SvmlkxG4gtYu7oAKZPp//0EBkkGRnAa69p7zN5MkXVA9mM\npmhv157UEgiQDXHpUujrubkkpFaOr0wBvP12av+cOfR3QgL5z2bF/rXXqAzqF75A/75yhXKuy8sp\najZ77a++CpSVyeMCgQAvOsCEw8JsQDBov5Vx+DCQnW28z8SJFMmaIRZhNkrbE5w4QVHsrFkklhcu\naO933XXm2wxQBDowXyiEAwdoBqTaz87MBMaPNzfQ2dcHVFaS/SJYtow+y40b5deKisw9bQSD1K7b\nb6fjJCUBv/kNvb5qFUX3b7wR+TjnzgE/+Ql1Gtu30xPLN74BfO1rNLhphq4u+5cUY5wHC7MBp0+T\nSEbKWrCCmYjZyoy+WKwMSQq3DNTs3UvtjTTQmJVlbRLIoUPanYIkkTesXjYrIYEE0YzP3NJCuc/K\nJ4HVq4FTp0Kj6EWLzKXhHTlCIpqZSX8vW0aDiSKjZPVqc+VPX3+d7vfjj1OWyPvvk9D+n/9DMyAj\n0dAAbNkC/Nu/0d+1tcA//zN1OMzogoXZALttDMCcMFvxmOfNo9oa0dSTOHhQFhs9WlvlCN8oOs/O\npn3N0NtL16f1JLJnD51Hkb4+yLJl5oT54EGaNKMmMTF00HXxYnOidvAgZdIIPzwriyJ+Mfn0ppuA\njz6KPJGlupp89RUrKIulupreu2kT3btIA76/+Q3wmc9Qx9PURFPhu7qAX/wi8jX8+MfAV79KFsy5\nczSB59SpyO9jhgcWZgPstjEuX6YR/fnzjfezEjEnJFDkZ8XfFbS1GXcS3d3U5tmz6W8jYbYSMbe1\nUYcyaVL4tsZGqvOsJZhmVxKvryf/WP2a2x36/gUL6PO4eNH4eHV1oSVRly2j94nMkdmzyeIxWhw3\nGKTFdz/xCRL49espQ2TdOprqPmsWPZ3o0ddH2Sqf+QwFCy++SOf84Q8pkhbLf2nR0EBPBhcukMf9\n3HMUoT/3nPF1791L0fyhQ5R58sILke8/z7S0BxZmA+zOyGhrI19VXbxIzbx59MM3m4YVrc98+LBx\nxCy2i0jRroi5pYVqOasJBumHX1RE3rZyth5gPmI+eVJbmGfNIpESJCZShxKp3cqnBoD89N7e0Kh8\n1izKv9ZDkmjgUaQHrllDxxU+eGYmCbUe9fX0nZg3j9779tvk/Sclkd1m9F6vl2yXO++ktMZXXqEI\n+uBBivT12L6dIuwXXqCc75deAv7zP/WFV3jmX/oS3Z/nn6eMlf5+/XOcOSO34coV9s8FLMwGxGPg\nL5KNAZBgLFxoHIEpiaaY0ccf0+O0qFOhhVqQjIR5/nxaA9CMpdLcTBM81Jw8ScIzaxaJmDozZelS\nc9HYe++RaCnp6CB/WR3xLVkS2c5Q34ejR2kgUjlF/ROfoIhYj/p6YOpU+e/ERBIi8dRw443G7di/\nX546f9NNFMWLoKGwMLTDUbNrF00QWr+eRDo5ma7n2jX9TJfTpymH/ZlnaODzlVcoNfDYMf2OrLaW\n7vN115GQ//739FnodRrd3ZTl8uijZKvcey/wne/oX0dTE3USPT3UBqMJR/399i69NtSwMBtgd8Rs\nVpgB6wOAViNmSSLxv86g8Ku6vUbCnJhI9UTMtEMtdIK2NsrIAMieUR9r0SKKFI1+cFevUrStzvjY\nu5dS7tTiF0mYg8Hw+9DQQNaGMgtl3jzjBQP27Qv9LvX305OIiBCXLzcW9uZm+rwA6rimTJE7t8xM\n/ckyfX00BrF6NXV2V67I92bWLP1r372b9k9LI5uksZHSG8VnoMXOnTRwm5tLVsmKFdSZ6O3/17/S\nd2bqVOAHP6COd/9+/fvw7LPAr39NU/afeAJ48kl9n/z73wfuv586igceoOhdi0uXqBM5dYoCIaPa\n4oEAfR+uXYt/ZM/CrMPlyxQ1qIvZx4IVYbYSBUcTMZtpi1pA586lH7k6x1iQk2PO625p0X4SUXre\nWtd03XUU4RudQ5Ioeh8/PvxabrstfEJLJGH+8EOyC5KS5NfOnQu3biJlt1y4QIIlaG6mqebiqSA9\nnaJqvQHAt96S7ZnLl0kse3ro79xcfZ+8tZUie1F3pLdXvhaja9+7V7ZdsrNp0HTcOIrsB5bnDKOx\nkSyTdevo37m5ZFkZnWPdOrqunTtJ+LOztYW8v59E+YtfpEHQ66+njkO1Ah0AehLct48ClmeeIavn\n97/X/t7+5jfAtm0k+v/yL/RUoPVb2rOHhP7FF4FvfQv4X/+L7qXWNZWV0dPjT38afY46C7MOhw9T\nb24UUVol0qw/JfGOmCMN/AHh4p2QQPdETxjnzTOXy3z5sra3rTyf3jVFGmTUisa7u0m4cnIo2lWu\nEBMpM0PreO++S2KivNaMDODPf9af/fjWW6FR/KFD9B7hmSckAPfco20T9PeTny3sGdGxCStl7lw6\n3vnz4e89eDB0yv2ECXLnZCTMLS3y+65elS2knBztFXb6+0nQ8vPJMjpxgu7bypU0IUdLFHfvpv2X\nLSOxX7uW/q1Vk+X4cUp/vP122nfpUtpXy8J57z0S7ZtvlisYzp6tHQ2/+y7wla/Q59PTQ523ltj/\n+c/UiVRX0/d/0iTtJ6TnnqPr+s53gKoq8vKjgYVZB7ttDCB+EfPChfQjVQ+WRWqL0cCf1iM8QH/r\ndQJmOoiPPqJ7KzI91G0yipjF+Y0G61pbw6PxlhYSiYSE8Fzs7Gw6r96jqVqYxaIAN90U2o5Jk8ga\n0HoMF6KlLFw1YQKts6gU92vXtK/tww/lrBKAorBp02ShcblIpLSis5Mn5fUce3ro0V5E6UadknJ8\n5fJluncACeKrr4Z3QEeP0jWmptL+06fT+SZMoA5JPWh74QIJ3JIlsn01axZ1PlqTbRoa6KkkNZXs\nmGnT6G8tYa6royAhP58+27Vr6X3vvBO63+XLJPh33km/nRkzKMo/cCD8mC0tVJVQkshGysmhlX+U\nnDtHn//f/z2d6/OfpwFhowFWPViYdbA7h/nCBfpv7lxz+1uZODJ+PB3XyKNUE6mTOH+efoTqlcGN\nfGYzbZYkirq1FrZVdhZ6x1q1ynj2nxBh9XGFyKg7j6lT6bFYr9C/+innxAn6oeXkhD8dLF6s/cRw\n6hQJybRp9PfFizQIt3x56P5691Zt/TQ1kSgp37thg/4juLAkWlrI5hFi7PGQcAtLRHDtGrVD3McJ\nE+jYvb0kSnPmhJ+ruVmurS1EWJznhhvCO5zGRhLi666TSwNIEt2T1NRwm0AIczBIloqop6IlzDt3\nUr749Ok09pGUROJcXx9+zI8+ok61t1eeyamOrM+dI4EVTxBTp9K51ddUW0uf4Y03Uke1fj19d6yU\nEhCwMOsQj6pyd98tRx6RWLSIxMJsYSCrKXNXrxpHzEeO0D5qATU6j5koXx05CkRUKdrk8dBgjDqS\nnT3beDRey3pQirWW4Bs90quP19ZGx0hPJ5FWDkTm5WnbPG1tofda/L14cagto/fEoRbmixcpCyQp\nSbYYpk8PFx4gtANuaaEc8fZ2EqJx4+g61Oc8dow6qylT6HN5/XUSTBFpFxSEd0DKgduWFtr/ww/p\n7/nzw+t1Hz4se+6trdT+5mY6Z3t7+H0UwtzZSR3JiRP0G+nsDPflJ00iC6W5mZ4yRHvUn3tTE1kX\n4p729tLTpySF2kL19TTYm5BAv8eeHjr+H/8Ymgp44ICcny8EPymJ/G6rsDDrEI8cZivpOxMmkAiZ\nXbLJivXR00ODFEapcpKkLaCRUubOnDG+Tj1hPnmSfpwiqpw8mX7o6lVTIkXl06eHdzjKc8YqzKLz\nGD+efsTKY4llttSohVlE4VlZoR2P3meofgoQOcz79sklSIUlo0RtR7W2UrSbkyOLrNa1K7/7J06Q\nuKWlyW0TIqrkwAF5lZ/mZhIoMWFmxYrwNMqGBtlzb2gItahuuSX8OyaE+fBhOvaJEySUak/63Dmy\nGGbOpPdMmyb7+W536NPBgQPyxKHz56lDcrnC78nRo9QZBYNyCuusWfQdVQr44cN0TS0t1LE1NVHO\nudm0VyUszBqIcph2R8xWl6eK1wDgsWMkokYDm3rtNRLm666j4xp9EY8c0T6ulrWidS7hp2uNiF+6\nRP6kusZztMLc20v3StmRiIgZCLcu9DoNtTCfOUMDSfPm0eOvGBjT+ww7O+W6IsGg3Abl+bS89zNn\nKCNBeNPiHiclye/TunaljdfaStG58rPQ6gTUkXlenrx/ejpZN+r9xT3p7w+1ddLSQv3yvj55MFJ0\nasIeUNsZra1UqMrlkuuunDpF383Tp0M/n4sXKfL1+6kNYvZkZmbovfT5KKvlww+pbfv20fHnzAnd\nTwxK1tfL1mJ6enS1TBwlzNXV1fB6vaioqNDc/sQTTwCA7na7OHmSelqjcphW0YsUjYhXypyZtujt\nM38+pSOpfUlBpIhW77iSFL5St9axxo+nH4SWny5J9ENQ20XRCvPRo3SuCRNCjyUERZ0yZ1aY9++n\nwlhiOr0yCtOq4Ldnj5zDfO4c3YPk5NDzZWTQNmUEru7sxN/K9+kJswhK2trIXlB2GkuWhM9KVXrx\nR45QRoQkUUeSkRFuSymzgl5/nXxoke2RmRkq/G1tJHRTpsgWSFsbHVstzG1t8qSdxkY6t5iRqc7o\neestuv9iDKKxkZ721J+reGpqbaXv/+HDdG5lxxwM0meyeDF9Dh4PCfTixcYzQvVwjDDXDSRHFhYW\nAgB8GldTUVGBrKwsZFhVOIvEIyMj2ojZSsF8sxGzmbbo7SNmJeqlzEXqIPQskrY2EiYzx9K7L1qi\nf+ECiYh4ZNUq+qSXnaDlVxtFzOnpJOZqX1wtzMp2ejzytWgtknDtGtlZ4rFfeX7lfZg8mToQ4esC\n4cL88cfhkbbWgKXy+y86ImXEvGhRaB3rjz6i7XPmkEAdOUKR6rRpFK0KW+rECdpf7JORQe+9cIEm\nwIixgyVLQr3bhgY5h7u1lf49aRI9EaiFWXlvRUaGuNZ16+T7Iwb7PB5ZmEWOfFZWqIUoSvW2tVFE\nPGkSXdeaNbKVcfo0tWP6dLJwxOc4a1Z063I6RpgrKyuRPJAF7/F4UKORTFhRUYHDhw/jNuHYxwm7\nB/6A6CJmK2KrN1imhZ44KjFqb6TMDL1tvb3yoI3W+dQdgd6x9F7XarM4rhjE1Cr6NGeOPGlDiVqY\ng8HQe6cWtcmTKRJWiqPSehAoO72MjNC2qK/txAl6fBZRoDJiV3dQ6r9Pn5Zzn0Wa4pw54cLc3Bw6\n1V0dMQthFu+ZN4+emsRYgiTRvUtICM1AUV6L8jtz6hRlNghLJSODRFE8iS1cGFpGVfjLgCySIqrW\nipgzMsj+EMuitbeT0KemyqlwYmB9woRQz19YLOKYV65QexYtovbl5pI4t7VRep3w0ZXfvSNHqBNo\na5PL5VotMuYYYQ4EAkhRFD7uFKMaCvx+P7xeL8rLy+PaFrtT5Xp7yR4Rj6NmsWJPTJ5MP2D1YJkW\nej6voKeHIgG1V6tsVzQpc8eP04CmelaeaJNaVK1GzGoBBCgVa8MG4/eLAR915KgW5tOn6T6LmXNa\nhZvUxz57ln78YubdlSsU6YkKg+r91fdW/eSiFzED4ZN/fD55gLe9naJudV3t5GSyCERnEgiQnSLS\nOsX5lB2/GEsQA4jKzqqtTe4MlJ+f8rqUTxDi8xGzOtvb6f+nT8vCr0yVE08B2dl07NmzSWDFYsDi\ne3TsGEWr119Pn9epU6ECqY6ss7PlHP3MTNmGOXZMXqNz/34S4wUL5N+QuD5xvGvX6L4oO4RFi6zP\nzHWMMANAMEKFmtLSUhQWFqKzsxNe9WgCgLKyssH/3ray0qYKu60MI0EyQnzwZssomo2wI1kZ4seh\nVwUv2pQ5vShc+WirJCODOhv19VuxMhobw8uLmvWZ9TIyBOIxVTkTTn1stY3R3h468KoWU/X9U1+T\nlYhZKZhqn10Ij/raRQaIyxX6hDBpEn0Wwo7QEiXxb9Ghqwcnhb+rtFgaG2UPPyeH9h83jo4hhF8I\ns7BFpk+Xxc7lImtDRLiiLepZpHpCCsgRs4jCp0+XrRLl5yfeI+6fOK7y+3vyJEXm119Pxzl1yni2\nrB6OEWY9Aw34AAAgAElEQVS32w3/wDe8q6sLqSIpcoCKigpUV1cDAFJTUyFpXKlSmAsKCqJuSzxy\nmK36ywA9Dore3gxmImzxYzNqT6TtRsKcnk5RhpaloifMfj/9wNQrxbjdNNJ97lzo61aEWS2Meu/X\nE2alR6uOyF0u+p4YDQCqz6+2kbTEVXlv1U83ymucO5csBOGXKwVA3dkpP9Np08hKEN8rtTCLoERp\nOQC0uK04vvJcymMrr9dMxKxs46xZoYOZR47Q09uxY7Kwi45SKbLCzujupv1nztSe3q/M6BHnVUbh\nys5DnF+0VdxPj0c+d0oKBS/nz8v3QJJoYonyvFbGigSOEeYtW7YMim17ezuKB5Y4Dgy45h6PB0VF\nRQDI5lgr1vWxmYsXqbc2yvG1SjT+ssDuAcAzZ0Ifx7WI5EEbCfOkSaEDPerjagm+2gdWonX96ogP\n0Pev9YRZ3X61MF++TB2C8nugdazs7FALZOnS0IwFo4E/ILwj07Iy9NL11H658l6pOzv1edWZGeIa\nlEGJ+j0JCeHCpd5PbbVoecx6wqwUcvEZNzfLeePKjlJpSwhhbm8HPv1pum5lOt7KlfTUOn68PDdA\nnPfMGdlqUpYbENcn9vP76fpTUkLPrd6vrY1+XwAJ9MmTIzxizsvLAwB4vV643W7kDkwLEmJcWFiI\nmpoaVFdXIy0tbXC73Rw5QgMlkYrZWyHaiBmwNgC4dGnkSSxm2hLJg164kDxJvdocViJa0Sa9jkAZ\nGQnUER9AP7ZZs0JT2wBtMdV6slBnZgiBUX4PtNqvHgCcPz90HUGtiFl5bydNokhUdGTz5lEEJsRd\neU6xoowye0V5r5UCoP4M1edVC7NWxKxuu/I9S5fKM+6U59KzWsQ97+8PTZXT21/8u74+fOBPva8Q\n5tZW+TupjJjnzJHzosX7xHdOecyFC8nb7umRf3fiHrS3A5/6lH47e3vl76r4vKZMocktmZnUAVjB\nMcIMyB5yaWnp4Gu1isXfSkpKUFJSgscffzxubdD6IcdKLBGz1WJGkax1M22JFDGrPUA1em3WO7dR\nm/RGtNXir3UMsZSXehBz0SIadFJOd8/MpOsRE1f0UuXU3w2rVkZPT/j4xYUL8nsSE0OjYLXoZWSE\nPllkZsqpXTNmUJsDgfD74feHCnN+vtyxKTsl5cC3uu3KqHfhQlpMtq+P7mV6upyBIt6TlkZPAn4/\niVRyMnVAotO7coXaIJ5KlEGIEDllRoYyYp45kz7fCxdosLGhIfTcegWxRM1w8R1XHlNZVlYZCQvv\nWXw3Zs6kc3Z3036HDtF9E9G48glAWChWFioGHCbMTiAewjxUEbP4IhgNFtoRMSvPpYVWm/UG+MT5\njIRZS+QjDZIB+hNOJkygqFOZqzpxIgm4EEQtYdY6h9rKSE2lqNDvl/1L5ffpnXfCbTL1vRR/+/0k\noKKQlHrwEaDrE9Ggy0VipYwIARLPAwdCy47OmCHXr1iwgKJ0IejKaFbP/sjIoI7s2DGqNzFhAnWC\nSvvE5aLCRuKeZmVRepmYjXj0KN1zsUCuxyPbOuJc6lQ50TaXS566nZJCT1EHD9L7enupsxDfYeXv\nYvFi2m/iRLLzlBGzaKPoONraqI3p6RRJi1xql4vuVVsbta2jQ15JXkxqEec9dowE32pGFguzCruF\n2UiQzGAlYk5Kosjk9Gn9fSK1RdQDiFWY1W0+e5Z+DFqzKY06Ai0rQ+scWqJl9FlGGgBUC7PfT4Kh\nrrYnfshiQoQyHU1ka4hx7P5+urfq+6+ekXbDDRRJtrWRAIgIWSsdUD09WkzNVn7OwuaZOFHeTzm7\nTqx9+M47FPWJLBY9KyMYpH2uv55qEgv7QOszSEiQO67MTJpEouUvA6GZHxkZcr2LFSvo3ktS6PGF\nsAKyQC5eTGI6d66cBZWSQhNYzp+n4zY2ygN06gFeZS6zuMZJk2gatvLJSzkTcM8eel8wSBaU+N5k\nZVE0HQxqr9hjBAuzCruFubOTvvgij9UqViJmIHKVuUgRs5ggoFyfzup5tDoTI3vEaJtexGzGyogk\nzEYDgFqpcmobAaD7lJoaGn2Lx2VxfvGeDz+U84aVqO9lSgrlzKpn7mkJn/q9SqEW7W9vD1/8Vv10\ntWQJlcsUA39qWwKgKHf9ernjz8oioRXn0RscFVaPmLghrunkSaprrWTDBmr/1Kn0WZw7RxFrRwfd\nZ+W9U9oxK1aQ4IoOR9kOl4tys0Wt7uZmOUDQipgPH6ZOobaW6oQAdB7lfjfcQO1fsoQ++zVr6O/u\nbrk2idtNNU7EepNWYGFWYbcwxxItA/RlDAa1V43QQl1nQI2ZVDkz7TUTMSstFb370NND0bSYcKFm\n7lzyXNUrYJgVZr1rycsLv6dGwmz0vVAPAC5bRp+BVg7zjTeGv19LXFtbQwfJ9K5n0SISADHoKyLm\nP/9Z3rexMXRFb4C+VytXyhMzliwhu0P4y+LeqFMYxQxCca6WFrmNkYQ5Kys06t2/X36aEEyfLtdz\nXruWvhciG0Rd5Gr+fHkad3o6ZVTNmaO97223yffz0iW6zr4+Cpq0fHSXi4758cf0pLN7t1xICpAn\nw1x/PUX5ouLeXXeFnjcpiaJmqyshsTAr+Phj6qH1RCIaYvGXgfCZWpEwEszLl+kHZ1Ss34y/HOk8\nbjf5hsr841OnKMJRE2kyS0JC6OCYQHlP9OwiIzGdNSu8rrOIpjo7yadUruRtJPJaA4AtLfSacqCv\nqSlcIAFq44kTsh0iojZ15DdhQngbxo2j7634LLKy6DyXL5NIAdoTplwuOp/oiJYsoWOIiFmSKAtB\n/YSwdKnsaYsKcuLYR4+GihdAg2RiNmpmJn0nhGd86VL4Wol5eXIGw8WLcmQrSaFLZIlji+/YxImU\nppaQQPdSPQ8hLY0i4HHjyOKYOpXu75UroVH40qVy/YtJk+QC/rfeGvrZLVtGxxPXISrgqeu9bNwY\n3bp/LMwK2tspArEzVS7WiBmIbgBQi/Z2ejQzKtZ/7py2gKpJTzcu5K/uTPbvp+hCjZn7o1VoZ8YM\n+lF1d4dPlRYYCbPW2oEiYhbRslKUtGwEgXoAULS3pSX0PXozSqdOJfuoo4P+nj+f2tfRIe//0Udk\nNWjl1y9dKkeZixfTU8jixXL7Ozq0PU6lyC5eTNGzOF9Dg/bvYO1aOcpOT6cOV0zBbmsLP092NnUo\nvb307+5u+o0Fg8Abb4SLbVaWXCBp3DjZF/f5wu/d0qXkcX/8MXXeQiDPnJHbJFi9mu5/MEjtuXaN\nrnFgusQg8+fTto4OeRXz/fvDr2vJErKlGhvpe33pEh1f/dvJyaHvmboOSyRYmBU4LSNDYGUA0GhN\nvrY2eQRcj337QiNFPbQyG5So26y3lJUZ60QtfAD9aNato/dribtIxdIbDRfpccoZimlp1Gnt2xfe\n1kgRs1qYW1vDa3ob1fheskQW18REitr37JHFSEz80BJLpTCLwbM1a+Tte/eGCyBA90/4xdnZJC7i\n+9/WRiKsRtw3gJ68Jk8mMbtyhSJI9XmmTKFORxRKGj+exPPkSRJ2dYSZm0tZE729JGZi9e+LF8Pb\nM2kSvebzkcimpNB5zp8PF8i8PPruHzhAA5xiHcWcnND9XC5g0ybqNMaNo46kri7cI05MpGv9+c/p\nGHv30m9B7ZmvX08BkdbqMkawMCsw+vFFy1BHzCJpXitlzkxbrC4YayYzQ29hV8CcMKutAkFKComd\n1nUJi0TP25s0iX5syqL+opjRnj3hEVJnp/59UbcvOZlSuc6fDxXi8eP1a7AsWxa6YOmiRfKjNEDb\ntMQVoOtQvnfhQjnn9vx5eckkNZmZcgH7q1fpXok622fPap8vJ4c80/5+edbc0aMkjHffLc96U5Ke\nToLo88kLs+7bR+1WWyVutyy2R4/S8draqAPRmlN2xx1kSQUC5JlXV1MEqx5sT0qie/rsszTA2NBA\n5xDZGUo2bABeeIF86dmzgV27gIF5biHcdx+17dFH6XO6+Wa5vKzyev7pn4BHHgl/vxEWLenRTVub\n/pc/WuyKmH/1K3P7JifTj+Xs2fAvyZEjxjVAjARUr11tbRRhqMnIkH/0nZ10bHWqGUCCNlCCW5fs\nbIpM1AjrYfz48AjJzNNPaio9yiv3E4Ngd9whv3buHAmD8GzVLFxI9/vyZVmYAgESOWGvXL4MvPYa\n8Nvfah9jzZrQ8pULFoSKqSRpDxwC5NP+8pfy35cuyaliBw9qCyBAUeT58xTBNjXRd6e+Xq5RrJVJ\nkJZG19XQQMdOT5ftBL2UsPx8iijPniVhP3CAIumByb5hbNpEArtuHVkS3/2uXINZa9/HHqPf7d/9\nHfCznwGK+WkhPPgg8NxzwNatwJtvak/2Achbd7vpvvb10f3UspBuvZWyM8aPN/4Of/rT+tv04IhZ\ngd1WRqSMA7NYGfwD9CPZSE8E585R1KQeibd6HrFNtFmIvZY4mCkYJawC9VOAEGafL3xA08xnqbQA\nlMc8fjxUZA4doohWq/0APdaKFDlBZmbovT50iKIuPSspOzt0KvdHH8nr+QHkeeoFDUuW0Cy8zk6K\nZBMS5Nzi+noSOC2Sk0nwfD4S5tWr6d8HD1IEqDUmAACf/CS1dd8+ijjr6kh4lfaJksJCin5raoAv\nfYnu+e7d4f6uoKSE9rnvPuB//2+6v5/9rPa+a9YAP/gB8K//CmzZAmzfDjz0kPa+69cDL71E9+sf\n/oEEXYvERBqLSUkhW085MUeN1YqRZuGIWYHdwnz0qHHGgVnmzCG/7dKl8BxYLYRg3nJL6OuRrAwr\n0bI4j3IlCyUZGXKOqd5xe3rI6zP64gMUpSUmUseh9L+XLAF+9COKRtWRT1dXuH+oZtkyivaUZGXJ\nA1WCxsbwwSQ1ovMQ57x4MXSJqOZm4+vMySFRFFH35csknMIDP3ZM/3oSE2n0f+dOelzPzqb7eu0a\nCfPdd+ufd9UqugdtbbTfW2+Rv3rDDfrvuece4FvfIn/4sceAL3+Zngz0hHbWLODv/56i88JC2rev\nT/9+ZGQAv/ud3Ik9/7x+W4BQ79nqRA6nwsI8wNWr0RWzN8IOfxmgCEgUqBHTQo3QyjgQ01SNxCEa\nYdaLmGfOpAiuq0v/uG1tJCSRBiQB+tG3toYK8+LF9EitNbPq3XeBf/kX42OuWEGeo5KJE0lUlRke\nkqQ9EKZErHEnmDIldF3EnTvDU8PU5731VvK3N2ygiHnePHrsd7lIrI0GZT/1KRp86+igKPLECeCP\nf6To36gC7l13kbjOnEniefw41Vv50Y/033PTTcC//zv5r7NnU5Q6bpy2vyy4917533oRvBIz34nR\nDAvzAGLevp2PJnb4ywIxAGhGmDMzgT/9KfQ1veprSqwKs3JVC/VTQUKCnBd8+LBcmUuJlZXIp06l\n6E85WDN1KkXT2dmhohAMhlYl02P5cnr/1avy5y5WTO7pkVO19uwxjjoBum8vviifXwyQnT1Lgnrs\nGPDww8bHKCqiiNXtpkyG3Fzy6RMT9W0CwSc/CXzlK9QBf+c7dL4f/5hqVRiVeM3JAZ5+mvz2yZOB\nsjJ6MotkZynFfvZs430Z67AwD2CUpxrLMe3K8rCSMqcVyZrNyFBGNpEQa9x1dGiv46fMk9XyR60I\n88qV2ilHs2eHL3R55gxFuOpULDWirkh9PfmrAHU0aWl0L1auJNH2+fQHqgTr1wOf+xztf+wY2S53\n3UVe7C23kGBGslY2bQK+9z2KkNeupQHIrVtJmL/3PeP3LlgA/PM/U6S9Zg1dT16eufENpehPmGDc\neTNDAwvzAPHKYVavNxctGRmho/ZGKOsgiAErMx2P1YgZII+ypUVbmG+8kR6pX30V2LYtfLvebEC9\n8+zbF/56amqoZQCQkPb26g/WKbnlFuCDD2RhPnKEHtWbm+WBsbvuogjWiNRUilrffpuevj7/eep0\nXnuN7Jy1ayM/nq9aRVHyvn3AT35CHctXv0p+rJny46KuA0DXbtfTGjP0cFbGAE6rw6zGSi5zSgqJ\nonJK9JkzxoVUrKbKKdt16JD2tgUL6FE8OVn7cfr9982vrZiTA/zhD6HL2gPkY4sptIL6evOCv2GD\nfF+vXKF/b9xImQYAibbZFMo776RR/7feIgvh3nup06iuBu6/39wxnnwSqKyUo/0774xsozCjDxbm\nAeJR7lOsTGwHVqwMl4tmLClX5KitNa6RoVzo0grLl4ennAnEIpnq+gkA+dJNTZF9YEFKCo3oq2cA\nJiaSJaEU7F27InuygvXrqWPp66PshDlzKO/U5yNbQgzGmWHzZmrn2rV03KlTgf/4D+CHPzTfHpfL\n3pIAzMiEhXkAu2f9ifKZkR6BzSIWktRbzkmN0t8FIvu50UTLAAmzMt9WyYIFNPCl5a22tVEmgJX7\nM3Mm1S0QnDhB7Q4EZJvn2jUSU3WqoB7z55NP/frrwI4dFC1nZFDbv/QlEkmzx5o4kYT4scdkG2XW\nLHsX9mXGBo4S5urqani9XlRUVBjuV15ebut5+/powMZOT87OjAyA/MmlS6mdZlAK89WrNEBnZw6z\nYOVKEjStVbFdLjnLQU1jo/UZUatW0UQGwd/+Rjm1GzcCf/0rvbZ/P12n0dOBmgcfpLS5tjbZcvjW\nt2jwTAy+McxQ4hhhrhsw9QoH5jb6fD7N/WpqarBjxw5bz93RQZGdcoWHWLHTXxbMnh1qTxihFGZJ\nosjQKBUwWmF2uymS1aplAZAoay0Q+/775oolKdm4kWwKMQOwvp4mQnzmMzTFFqDBNq0p4kbcfjtl\nPYi0MYCeUB57zFx6IsPYjWOEubKyEskDlUc8Hg9qamo093OZGWq3iFOryqkx8nPVKIXZTFpad7f5\ngTg1eXnkyarp6SH/98MPw7ft3Rt50oaaxYtJOOvq5HXsNm6k//r6KCPiwAGKoq3gcpEHbHdHyjDR\n4hhhDgQCSFFktXdqGJc+n28worYTp+cwC9QVyIxYtIiu6cIFEuZIU1XffTf66awbNoRWaRPU19OA\n4s6doXUu+vrImjE7ICYQi3tWVdGU3Rkz6B6PH08VvL73PeokOMplRjqOEWYACBot7wzAb3Z9JYvE\nI2Lu74+PMOulpqlJTCRRPnQocsR89SoJa7TCvGwZDZ6pqa+n6HXt2tAp4gcOUJ6xelkhM3zuc+Sz\n//d/U40Gwd13UzpdpGnYDDMScMwEE7fbPSi8XV1dSFX9as1Ey2VlZYP/LigoQIFRkQAFbW3hi1XG\nSk0NVb2yk6VLyWMWFcQikZNDlcJaWvSrcwHkDy9cGP2Mr3XrKApWTmMG6B4UF1MO9RtvyML/zjuR\nZ8Hp4XYDFRXU6ainApsp8MQwIwHHCPOWLVtQW1uLwsJCtLe3o3igVFUgEIDb7YYkSZAkCZ2dnfD7\n/fD5fMhTzZNVCrMV7I6Yu7up3kCkKcFWmT6dJmscP649007NqlWyMBtFzK2tofWHrSLSAvfupUI8\nAFkXH3xAtXTT0sh+EFRXUxGcaJkyhUWYGd04xsoQIuv1euF2u5E7MAe1aGDpgJKSEpSUlMDlcqG7\nu9u2QcD+fvMrQ5tFeNZxGKe05DPn5ND03uuvN+4kamvDV3ywypo1FCELxIobHg+tBNHcTFOVGxsp\n73njxtjOxzCjGVcwkrE7QnC5XBE9ai1OnCAPVNQOtoPKSlqporravmMKvv51ytF9/PHI+54/TxbF\nypUUverxqU/Rqg9WsxmU7N5N9SEOHSKb5Uc/ohoR4iGmvJwi8/5+mi79j/8Y/bkYZrTjmIh5uHB6\njQw1a9aYr5mRlha+RJEWygLv0bJuHU0/r60l8X399VB75J/+ie7J4sWhg3YMw4TjGI95uOjoiF2U\n1LS1Ga8AEQtLltBECLNcuaK9Vprg7Fma0mzGszbC5aJaEc8+S/czNTX0HkyaBHzzm7Gdg2HGCmM+\nYm5qsr/QdzyicMGKFRSRq0tdGmHkddfWUu6vmSyPSHz2s3LmSFlZfDx2hhkLjPmI+ciR2LxVLeIp\nzBMm0NTphobIEzTEytRGg4VNTeaL9EQiMZHziBnGDsZ8xHzkiL1Tpy9dosI98+bZd0w1elOg1Rw/\nTlbNoUPa9SoAKv5jtnYxwzBDw5gXZrtT5SSJBsHssAb0yM01J8x1dSS6YpFPNX19VEzI7sk1DMPE\nxpgW5q4uKlcZzdRgPeJpYwjWrKGVnCPh85Ewr1hBAqymro5WOklLs7+NDMNEz5gWZlEBzs5BqqEQ\n5rw8ypHWsycEHR0kvMXFwCuvhG/fsYMq1jEM4yzGtDDHI994KIR5yhTKBzayM/r7SYzz86n62+TJ\nVLNCyRtv0EKjDMM4izEvzHbXTB4KYQZIUA8c0N/e2koLoM6eTZkcCxfSatWC9nYqlylqWzAM4xzG\ntDDbPfAHDJ0wL18O/PnP+tv37Akd1HvwQeCXv5SXeXrpJfKex42LbzsZhrHOmBZmuyPmnh7g9Gma\nBh1vNmyg4vZ9fdrba2pCV3e+7TYqmfnii1TP+I9/BL72tfi3k2EY64xpYbY7Ym5vJ8vguiGYtjNz\nJvnHysVJBcEgCfNAYT4ANMD5/e9TgaWvfAX4whcorY9hGOcxZmf+Xb1KFeXsjG6HysYQ3HAD2Rk3\n3RT6+qFDVFND/TSwciXwP/9Dk2DsnobOMIx9jNmI+ehRmp1nZ3Q71MJ8331kZ6irnVZXU5F8rTRA\nMSDIMIxzGbPCHI+Bv3iW+9RizRpagXrfPvm1YBB46y2q9MYwzMhkzArzSE6VE7hcwGOPAdu2ya/t\n3EmZF/EqO8owTPwZ08I8UlPllDz4INkyBw7Q9PLt26kQfTxrdTAME1/G7OCfJNlX7hIAentpCnSs\nBeetkpxMQvzYY5QRMm0a8NBDQ9sGhmHsZcwKs90R87FjwJw5NMtuqNm8mSL106dpkdPExKFvA8Mw\n9uEoYa6urobb7YYkSSgtLQ3bXlVVheTkZLz88sv4+c9/HvV5gkG5gJFdDIeNoYRrKjPM6MExTmRd\nXR0AoLCwEADgU1Xo8Xq98Hq9KCwshCRJ2L9/f9TnOnOGivokJUXfXjXDLcwMw4weHCPMlZWVSE5O\nBgB4PB7U1NSEbC8sLMTPfvYzAIDf70dubm7U5xotA38Mw4xOHCPMgUAAKSkpg393dnaG7dPd3Y3y\n8nI8+eSTMZ3LbhsDYGFmGMY+HOUxB9VT2FRMnz4dW7duxaZNm5Cfn490VbGHsrKywX8XFBSgoKBA\n8zgcMTMM42QcI8xutxt+vx8A0NXVhVTVek91dXVwuVzIy8tDfn4+qqqqsHXr1pB9lMJshCRRtTW7\nuHaNcontjsIZhhmbOMbK2LJlCyRJAgC0t7ejuLgYAFkcAA3+CeEOBALIiCHktXvWX0cHcP31wKRJ\n9h2TYZixi2OEOS8vDwAJsNvtHhzcKxqoXfnwww9DkiRUVFQgOTkZ9913X9TnstvKYBuDYRg7cQUj\nGbsjBJfLFdGjBqjkZVoa/d+uacs//zkVEqqosOd4DMOMbRwTMQ8VkkQF4u2sJcERM8MwdjImhdnu\nQbqhLvfJMMzoZswJczxE9MwZIDvb3mMyDDN2YWGOkb4+wOcDsrLsOybDMGObMSfMdlsZR45QVTlO\nlWMYxi7GnDDbHTE3NdHCpwzDMHYxpoT52jXg+HF7i9k3NwNLl9p3PIZhmDElzCdOUA6znbZDUxML\nM8Mw9jKmhDkeC7CyMDMMYzdjSpglyV5/ORgkK4M9ZoZh7GRMCfOZM/aK6MmTZIsoykgzDMPEzJgS\n5oMHgfnz7TseD/wxDBMPxpQw253DzP4ywzDxYMwJs905zCzMDMPYzZgR5kAAuHqV0uXsgieXMAwT\nD8aMMAsbw+Wy75jsMTMMEw/GnDDbRSAAXLwIzJtn3zEZhmEAFuaoETaGnRE4wzAMMMaEmQf+GIYZ\nCVw33A1QUl1dDbfbDUmSUFpaGra9YmBRvSNHjuDpp5+2dGxJAu65x5ZmAqCVsdets+94DMMwAsdE\nzHV1dQCAwsJCAIDP5wvZ7vV6UVRUhNLSUkiSBK/Xa+n4dtfJ2LWL1g5kGIaxG8cIc2VlJZKTkwEA\nHo8HNTU1IdslSRp8zePxQJIk08fu66PKcgsX2tfeAweAnBz7jscwDCNwjJURCASQoig60dnZGbJd\naW3U1dXhgQceMH3sjg5g1ixgwoTY2wlQzY0rVzgjg2GY+OAYYQaAYDAYcZ+6ujqsXr0aubm5YdvK\nysoG/11QUICCggIA9mdk1NcDq1ZxRgbDMPHBMcLsdrvh9/sBAF1dXUhNTdXcz+v14qmnntLcphRm\nJXYL88GDbGMwDBM/HOMxb9myZdA3bm9vR3FxMQCyOATbtm3D1q1bAcDS4J/dA38szAzDxBPHCHNe\nXh4AEly32z1oVRQVFQEAampq8M1vfhOZmZlISUmBy4KPYHcOMw/8MQwTT1xBM8buCMDlcul61GvW\nAD/9qT15x729wPTpwLlzwJQpsR+PYRhGjWMi5nhip8fc2krF9lmUGYaJF6NemLu6KI9ZZyzRMuwv\nMwwTb0a9MNtd7pOFmWGYeDMmhJkH/hiGGUmMCWG2y18OBoFTpwCNuS0MwzC2wcJsgRMnSJgXLLDn\neAzDMFqwMFtgzx5KueOp2AzDxJNRL8xHjtjnMe/ezTWYGYaJP6NamHt7gZMn7bMe9uwBbrjBnmMx\nDMPoMaqFuaMDmD0bGD8+9mNduwbs20ezCBmGYeLJqBZmO/3lxkZgzhxgoJY/wzBM3GBhNokY+GMY\nhok3o16Y7Rr4Y3+ZYZihYlQLs511mDliZhhmqBjVwjxunD0R8+XLVFVu1arYj8UwDBOJUS3Mr70G\npKfHfpy6OmD5cvsWc2UYhjFi1ApzVxfQ3w8oFt6OGvaXGYYZShwlzNXV1fB6vaioqNDd54knnjB1\nLDHwZ8f06ePHgZtvjv04DMMwZnCMMNfV1QEACgsLAQA+ny9sn23btqG6utrU8exKlQsGgd/+Frjp\nphfamREAAAfESURBVNiPxTAMYwbHCHNlZSWSB2ZveDwe1NTUhO3z8MMPw2NSbe3KyGhtpZmDixbF\nfiyGYRgzOEaYA4EAUhSGcGdnZ0zHsytifvttoKAg9uMwDMOYxTHCDEB3letosEuY33mHhZlhmKHF\nMcLsdrvh9/sBAF1dXUiNcfVUO4Q5GKSIecOG2I7DMAxjheuGuwGCLVu2oLa2FoWFhWhvb0dxcTEA\nsjjcbrepY5SVlQGgNLmOjgIsWFAQU5taW4G1a+3JhWYYhjGLYyLmvLw8AIDX64Xb7UbuwMJ6RUVF\ng/tUVVWhtrYW27dv1zxGWVkZysrK8PnPl2Hu3IKYy32+/jowYwavWMIwzNDiCtpp7A4jLpdr0KPe\nsQN46ingzTdjO+YddwAPPwzcd58NDWQYhjGJYyJmO7Gjqtzly8B77wEDadUMwzBDxqgV5lgH/t55\nB8jPB6ZPt6dNDMMwZmFh1uG118jKYBiGGWpYmDUIBoH2duDTn7avTQzDMGZhYdagthZoaQFWrLCv\nTQzDMGYZdcLs98de7vOll4AHHuA0OYZhhgfHTDCxCxEtRyuq/f1AZSXlMDMMwwwHoy5ijtXG2LUL\ncLtpxRKGYZjhYFQKcyw5zL/9LdkYDMMww8WotDLy86N7b28v8PLLwM6d9raJYRjGCqMyYo7WyvjT\nn2ilksxMe9vEMAxjBRZmBT/4AfDQQ/a2h2EYxiqjSph7e4GTJ4EFC6y/9/33gVOngHvvtb9dDMMw\nVhhVwnz8ODB7NqIq9/mDHwBf/zqQmGh/uxiGYawwqgb/orUxJImKFv3qV7Y3iWEYxjKjKmKOVph/\n+EPgy18Gpk61v00MwzBWGfMR8+HDwAcfAK+8Ep82MQzDWGXURcxWJpcEg8Cjj9KEktmz49cuhmEY\nK4wqYT5yxFrE/OtfA4EA8NWvxq9NDMMwVhlVa/4lJQUhSUBqauT9T56kySR/+EP0MwUZhmHigaM8\n5urqarjdbkiShNLSUsvbAXPlPi9cAO66C3jsMRZlhmGcR1RWxttvv21zM4C6ujoAQOHA6qc+n8/S\ndsBcuc+eHuCee4AbbwS+9rVYW21MPO5TrHCbzOPEdnGbzDHS2+QYYa6srERycjIAwOPxoKamxtJ2\nel3/+MEg8Ne/UqTs8QDPPhv/Qvgj/csxVDixTYAz28VtMsdIb5NjrIxAIIAUhQ/R2dlpaTugLczB\nIOD1At/+Nq1u8u//DmzezDP8GIZxLo4RZgCINA4ZafuiRUBnJwmw3w8cOwY89xxw5gwJ8gMPsCAz\nDON8osrKcPFieAzDMJYxK7dRRczxyLDz+Xyora1FaWkpysvLUVxcjNzcXAQCAbjdbt3tDMMwow1b\nJpg88cQTMR8jLy8PAOD1euF2uwdFt6ioyHC7k7HjvjDDS3l5+XA3gRmLBGPkF7/4RTAjIyPWw8RE\nVVVVsKamJrht27ZhbYcSJ9wXNdu2bQtu27Yt+MQTTwx3UwZ5+eWXgzU1NcFHHnlkuJsSxo4dO4LF\nxcXD3YxBvvGNbwSDwaCjvuf79u0LVlVVOaZN+/btC7pcrmBGRkYwIyPDMd8rqxoVc8T88MMPwxPL\nstQxYia/eTgY7vuixuv1oqioCKWlpZAkCV6vd7ibBK/XC6/Xi8LCQkiShP379w93k0Jw2lhKRUUF\nsrKykBHLasM28/TTT6OkpASBQMARv72uri709/ejra0NL7/8Mr75zW8Od5Pg8/ng8XhQWFgIj8dj\n6j6N+FoZZvKbGUCSpMF74/F4IEnSMLeIOtOf/exnAAC/3+8oe8rn8w129k6hoqIChw8fxm233Tbc\nTQEAVFVVYe3atQCArVu3DtqNw4nyM6utrcWiRYuGrzEKhK0pSZKp+zTihdlMfjMDlJaWDk5jr6ur\nG/xBDTfd3d0oLy/Hk08+OdxNCcHv9w93E8Lw+/3wer2O8b1ra2vR2dkJn8/nmDYJvF4vNm/ePNzN\nAEDjY+np6UhJSQnRKiMiZmVUVFSEvZaSkoKSkhLrLYwTwdFRh2lIqKurw+rVqx0TnU6fPh1bt27F\npk2bkJ+fj/T09OFukiOjZQCDHeuOHTsGLaDhJi0tDXl5eaipqUF1dbVjdGHHjh2OuD8ABY+ZmZmo\nqKhAaWmpqe95RGHWKxbkFNxu92B009XVhVQzpeXGMF6vF0899dRwNwMAdRIulwt5eXnIz89HVVUV\ntm7dOtzNgiRJkCQJnZ2d8Pv98Pl8w/6YXlFRMRgQpaamQpKkYRee1NTUQYFxu93Yu3evY4RZjD05\ngYqKCjzyyCNISkqC2+029T2P2cqoqqpCbW0ttm/fHuuhomLLli2Dfml7ezuKi4uHpR1qhvu+aLFt\n27bBL4RTBv9EpxoIBBwzqFVSUoKSkhK4XC50d3c7YhDQ4/EMpo52dnY6woq6//77B397gUAA69at\nG+YWEU4YP1GTlJQEgDxwt9sdcf9RUY+5oqJicEDL6RH+cFFTU4PNmzcjJSUFfr8fVVVVwz6I1N3d\njcrKSgD0Y3JKJO9UqqurAVAA8vjjjw9zawgRydfW1jrm82tvb8czzzwzOLDsBMrLy+HxeOD3+01p\n1KgQZoZhmNHEiM/KYBiGGW2wMDMMwzgMFmaGYRiHwcLMMAzjMFiYGYZhHAYLM8MwjMNgYWYYhnEY\n/x9TP3s0txWEHwAAAABJRU5ErkJggg==\n", "text": "" }, { "output_type": "pyout", "prompt_number": 4, "text": "" } ], "prompt_number": 4 }, { "cell_type": "markdown", "metadata": {}, "source": "The Cornu spiral defined as the parametric curve $u(t),v(t) := C(t), S(t)$" }, { "cell_type": "code", "collapsed": false, "input": "figsize(8,8)\nplot_parametric(fresnelc(x), fresnels(x))", "language": "python", "metadata": {}, "outputs": [ { "output_type": "display_data", "png": "iVBORw0KGgoAAAANSUhEUgAAAd0AAAHTCAYAAABiN8IeAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3XlYlOX+BvB7EFwQdQDN3RTNFtPE9deiYaCZtpmWthzb\nJDtlq5l22mzVNLOyrKTUbDFRykxzg1zaXBBMTT2mg2aKG5ui7Mzvj/uMA4qAMDPvvHB/rosLhhne\neQaXe77ParHb7XaIiIiI2/kY3QAREZHqQqErIiLiIQpdERERD1HoioiIeIiv0Q0Qqc5iYmJgtVph\ns9kQGRl53veLiLmo0hUxSEJCAgAgPDwcAJCYmFjs/sTERISEhCA8PBwhISFn3S8i5qPQFTFIdHQ0\nAgMDAQAhISGIjY096zFjx44FANhsNoSGhnq0fSLiegpdEYOkp6cjKCjo9O2UlJRi94eGhqJNmzYI\nCgoq9jgRMS+N6YoYqLS9adLT09GuXTtERUUhMjISXbp0QZs2bYo9xmKx4OWXXz59OywsDGFhYe5q\nrohUkkJXxCBWqxWpqakAgLS0NAQHBxe7PyoqCiNHjkT9+vVhtVqxYMECjBkz5qzrjB8/3hPNFREX\nUPeyiEGGDh0Km80GAEhKSkLfvn0BsMJ1qF+/PgBOtrJarZ5vpIi4lEV7L4sYJyoqCiEhIcWWBHXr\n1g3x8fEAgMmTJyMkJASpqaklLhmyWCyldlGLiHdR6IqYmEJXxFzUvSwiIuIhCl0REREPUeiKiIh4\niEJXRETEQxS6IiIiHqLQFRER8RCFroiIiIcodEVERDxEoSsiIuIhCl0REREPUeiKiIh4iEJXRETE\nQxS6IiIiHqLQFRER8RCFroiIiIcodEVERDxEoSsiIuIhCl0REREPUeiKiIh4iK/RDRAREfey24ED\nB4C9e/n54EEgMxP4+29+Dgjg9wDAYgH8/YG6dYH69YEWLYDAQH5u0QIICeH9UjEWu91uN7oRIlIx\nFosF+icsReXnA1u3AuvWAUlJwNq1wPbtQJ06QGgog7RZM6BVK4ZtQAC/Z7EwnO12ICsLOHkSOH4c\nyMhgWP/zD5CbC6xfDzRpAnTsCFx+OdCjB9CzJ9C0qdGv3BwUuiImptAVAPjvf4Hly4EVK4DsbFat\n//d/wLXXAm3bApdeCgQHu+a58vMZ5jt2AAkJwIYN/NrPD+jbF+jfH+jTh2EuZ1PoipiYQrf62r4d\n+OYbYMECoF07oHFjoF8/Bl7Dhp5ti90ObNnC4F++nEHcqxdw/fXAHXeoCi5KoStiYgrd6iUjA/jy\nS2DWLMDXl8E2ZAjQvTvg40XTYjMz2a09bx6waBHQtSswYgRwyy3s5q7OFLoiJqbQrR7++gt4/33g\nq68YskOGAOHhQI0aRresbFlZwJIlwMyZrIAfegh44AFW59WRQlfExBS6Vdv27cDrr3OM9pprgEce\n4SQos9q3D4iOBiZNAnr3BsaM4dhzdaLQFTExhW7V9M8/wGuvAT/+CIwaxbCtV8/oVrlOZiYr3++/\nZ7f4m2+yi7w68KJRABGR6i0ri5XtFVcAjRoBf/4JjB1btQIX4Mzmxx8Hli1jV/mgQcBttwE7dxrd\nMvdT6IqIeIFly7i0Z/NmID6e4Vu/vtGtci8/P2DkSI5ZX301cMMNwJNPAunpRrfMfRS6IiIGSksD\nhg8HHnuMXa4LFgBt2hjdKs+qUwcYPZoTrU6d4mznr7/mUqSqRmO6IiamMV1zi4sDnnuOk4kmTODW\ni8JK/8EHue3kxx8DLVsa3SLXUeiKmJhC15wKCoA33mCgzJkDREQY3SLvk5sLvPUW8Msv7Am4+26j\nW+QaCl0RE1Pomk96OvDss8CuXcDcudqtqSyJicBdd3F28wcfmH+cW2O6IiIesmcPcOWVQO3awMqV\nCtzyCA1ld3Pt2sCdd3JGt5kpdEVEPCA+nmtuH3uMu0v5+RndIvOoWxeYMQO4/XYgLAz49lujW1Rx\n6l4WMTF1L5vDL79wHWpUFPcflorbuBEYPBh44gng6ad5JKGZKHRFTEyh6/1+/RUYOpQHFYSFGd2a\nquHQIWDYMC6tmjHDXL0GCl0RE1PoereEBJ4v+8UXPOZOXOfkSb6ZKSwE5s83z3IrjemKiLjB9u3A\nwIHAJ58ocN2hbl1g4UKgbVtWvRkZRreofFTpipiYKl3vdOAAz4+9+27gnnuMbk3VZrdzctqGDcDy\n5UBgoNEtKp1CV8TEFLreJycHuPZa4Oabgf/8x+jWVA92O7eRPHSIY7wBAUa36NwUuiImptD1Pg89\nBKSmcpzRbDNrzcxuZ+9CcjKPDPTWyVUa0xURcZEZMzhbedYsBa6nWSzcVtNi4Rsfb30vqkpXxMRU\n6XqPbduAMWOA994D2rc3rh25ucCWLdyMY9s2YP9+Hhq/bRvQsSOwahXQrh2/36oVv5eZyUMXmjbl\nDlAdO3IHKDM6eRK47jqgb18ej+htFLoiJqbQ9Q75+Qythx9mF6en7dnDmbwbNwJLlgA9enANa4cO\nwMUXA82bAxdcwH2LHWGanc0ZvykpwN9/82P9ep7nW7s2H3v99Rybvugiz7+myjh6lOfzPvkk8Mgj\nRremOIWuiIkpdL3DhAmsIJcv91y38okTwLx57MpOSwOuuQYYNIh7O1utlbv28ePATz8By5YxyC0W\n4L77OBvb22cHO9hs7GZ+/nmgTx+jW+Ok0BUxMYWu8bZt405TmzYBF17o/uc7ehSYOpXP5+/Pyrp/\nf6BGDfc8X0EBz/398UceQ3j33Zwp3Lq1e57PlVauBO69l78rbzlcQqErYqCYmBhYrVbYbDZERkae\ndX9CQgKSkpKQmppa4v0KXWPZ7dwV6frreei6O506xfNlf/yRx9w9+6zngy85mcfrffMNg/7FF4Em\nTTzbhvM1fjywejUQGwv4+hrdGs1eFjFMQkICACA8PBwAkJiYeNZjJk6ciMGDByM9Pb3E+8VY8+cD\nf/3Frld3WrQIuPRSnsG7YAEwfboxlWbTpsAbb3Dst1Yt4P77WXXn53u+LeX14otAzZrASy8Z3RJS\n6IoYJDo6GoH/GyALCQlBbGxssfsXLFiA7t27AwDGjBmD0NBQj7dRzi0nh92tU6e6r2s3NZVbHL77\nLvdvnjvXM13YZWnYEHjnHc7UXrKEbzp27DC6VSWrUQP46itg61aOUxtNoStikPT0dAQFBZ2+nZKS\nUuz++Ph4pKSkIDExEZMnT/Z086QMM2dyvNNdJwdt3MjxyCZNGGy9e1fuenY7K9L8fNetYW3fnuOm\nvXvz48svvXN9bKNGwMiRQGQklxQZyQt6uEWqr7LGYxs2bIjQ0FDExsYiJiYGgwcPPusx48ePP/11\nWFgYwnR+nNvl5ABvvgnExLjn+nPn8rzYGTOAW289v5/NzmZg//Ybl//88w+X/6SlcX9iiwXo1YsV\nYFoa1+x27MhZzz17nv9pPY7NKHr3Bu64g+OnH37I7mdvcuON/L2+9BIwZYpx7VDoihjEarUiNTUV\nAJCWlobg4OBi9wcHB6NNmzanH7tx48YyQ1c84+uvgYgIrod1Jbudy4+WLeMSpA4dyvdzx4/zDcBv\nv3EZUVgY19becANwySVAixZAcLAzCHNz2XW9fz/Hif/4A5g0iet9L7wQGD4cuOmm81sedMklfP4n\nnuDSpblzgQYNzvtX4FbvvQdcfjnfHPTsaUwb1L0sYpChQ4fCZrMBAJKSktC3b18A7HYGgCFDhpy+\nPz09HT1c/T+8VIjdzhm8t9/u+uu++CJ3k5o3r3yBa7Nxdm6HDsDixQzZffs48WrKFIZnjx5As2bF\nK8+aNdlt3b07lwBNmsSg37wZePRR7mbVpg3w3HPAn3+W/zUEBABRUUBICIP/8OHz/S24V8OGHB9/\n8EH2VhhBoStiEMfEqLi4OFitVnTu3BkAEBERAQBo06YNrFYrYmJikJqaittuu82wtorTunVAejqX\nzLjShAmsOqdNK3tN6eHDHKMcNYphvWEDK93bbqvc5hV167JKff99YPduXmvgQM5STkoq3zV8fPga\n7rgDuOUW4NixirfHHYYO5e5hH39szPNrna6IiWmdrufdfTfQtSvw9NOuu+YXX7DLeubM0gPXbudj\nnnsO+Pe/2ZVbZC7eaYWFXMq0fTsr56ws3vb35zhucDCr0SZNgM6dOabr71/yc2Zmcob2mjUctx03\njpVyWex2PjYujl3l9eqV73fhCbt3M3h37OAkK09S6IqYmELXs9LTWb19913JYVcR69dzyU1MDHDZ\nZed+3JEjrDitVoZZx47F78/P50zi+fOBX34B8vKAAQNYrbZty9CrU4dhmJ7OannHDm54kZ3Nx3Tr\nxkq3pCDav5+VdWEhlwuVZz9mux144QUG/8KF7ltaVRGPP85JYO+959nnVeiKmJhC17O++IKbU3z/\nvWuul5rKU32mTePBAufyxx/sTu7TB3j11eJnxZ44AXzyCavJlBQuM7r+elay5ZWZyVnHa9Zw2U//\n/qzkzwx2u53dsh9+yE0ybrml7Gvn5QH9+nHi0sSJ5W+Tux09yg1H1q/nGw5PUeiKmJhC17NuuQUY\nMgT4178qfy27neOel18OvPzyuR/300/c/vGBBzge6ZCfzyVFS5ZwSdCzzzLAAQbKqlVAQgJPElqz\nhlsgNm3KqrNTJ6BlS3Yzh4byKLwLLuDPpqYCn33GNxedOnGJTcuWxdu0cSMr4ldeKd/2l8eOsUp/\n+GGOEXuLN97gphnffOO551ToipiYQtdzjh9n+Pz9t2uWwixcyHHcOXPOfXbt2rUM+ejo4ptw7NzJ\nMd169XhmbKdOHLddsIABu2kT23rddTzar2VLdofXrMnlQikpwIED7F7++Wfg11+5jjUsDBg8mOO7\nmZmsoGfN4ozmhx8ufoLSvn08s3bUKHbVlmXNGuDOO1m1e3oc9VxOneKM73ffdb5hcTeFroiJKXQ9\nZ9EiHjbgilmvWVkcv/3sMwZjSRIS2M377bc8ts/hm29Y+T75JJcE5eYCn37Kqu3GG1lJXn01u03/\n+IPheOwYN8nYu5fB2bMng7hlS47jdu/OUFy2DPj9d4bjo48yfLdvZ0Wblwd8/nnxCVEHDrCNDz0E\nPPZY2a977FiOH3t6HLU0U6ZwidTcuZ55PoWuiIkpdD1n1ChuHDFmTOWvNXEiQzU6uuT7Dx5kd+zI\nkVwGBLA7esoUVqXjxwNXXMEqddIkbkf55pusxqdPZ4DeeCM3rKhVi0Frt3NtqsXCMeGaNVnxLlvG\npUp3380JXb6+fGMRF8cJU/368edef51j2cuWcd2vw969PF7wiSe4oUZpsrJYlU+b5volVxWVkcHx\n78REoFUr9z+fQlfExBS6ntOxIyvTyu5RkpLCzSx+/50bUJypoICVY+vW3CzD4aWXGLJz53L89dVX\n2Y384INc+vP88wzTvn25VnbhQi4Pat/eWZ1mZfH6J06wi7pWLc5wvvRSzmJes4bB89xznK08ciTD\n+MUXGcbTpzP416wpPs67bh3fjHz2GZ+vND/+CDz1FMdSy7P0yBNGj+ZnT2wPqdAVMTGFrmekpDAg\nU1Mrfybr5MlcMztjRsn3f/wxu5SXLWN4AtysYs0a3levHsPw5Elea9o0VqUjRrBCXruWY6b167NL\nefdudg03b8625+Vx2U+TJtyhyc+P39u1i9VxvXrAihUc6xw2jN3MhYVcH1y/Psd533mHFXfDhs52\nT5vG8enffis+u7okw4cDV13FcWJv8PffHNO12dy/daVCV8TEFLqesXw5w+bbbyt3nawsdlH//DMn\nOJ0pKYnjqxs2OJf8xMVxrevcuQzKJ59khfr44wzfZs245OX334HGjYFDh7gG94ILOJb73/9yrW6t\nWqwsGzRgiFosXLdbUMCq8/bb2b7NmzkTeu5cVr2vv84x2FWrWKU2aAC89hpnMMfEOAPWbufkrjZt\nOHZbmvXr+Xx//eU9ByPceSfHtx1Vr7toG0gRkTJs2eKa8b5Fi7jUpqTABVhBjhvnDNwjR7g86Y03\n+PyObR9HjmS38tVXM2S3b2ew7t/PQM3L47KhkyfZTd2oEbd49PXlRKasLHYxOzbGaNyYP3vkCCdt\nffEFJ3o1aADccw+r3YgIji/n5LAru1kzdnE7WCzslo6K4jhvaXr2ZKXrrlOaKuLJJ7nntbvfwyp0\nRUTKsGXL2RtFVERUFBAeXvJ9CQms/B591Pm9J57grODrrgM++ohLXJ59lt8fOJDjthdeyIo2JYXd\nyceOMXDT0zmmm5bGWcaHD/MxBw+yGzUjg9dLTuZOUceP8+dtNq7fPXyYpw716sUx2Mce43jt2LHs\n9n7tNU4++u03Z3sdJxS98ELZv4sHH+QkMG/pqOnRg7+D33937/ModEVEynD0KGcCV8bhw6w0zzXD\n94svOI5apw5vr1rFEHjiCYbbnDlcuvPyy6wUf/qJ3aEbNrByTU5m0GZk8BqnTrGKPXnS+fnECT7W\n15e3c3K4Hjcriz/r48Ofz8hgF3BAALueL7qIleCECayI589n9XzvvdyXOT/f+Tqeeorhvn176b+P\n8HA+97p1lfu9uorFwjcMc+a493kUuiIiZdi6tfLdy0uXOvc/PlNyMjB7NnedAlj9zZjBZUO1anHC\n0b//zbBzVKeBgZwAlJrK8MzI4LraU6f4JiEz0xmqjoDNyuLH8eO8ffIku6MzM/nzhw7x67Q0Bu3R\no6yCCwv52C++AJ55hkuW0tO5cUf9+tw60qFePXYdlzUT2MeHQb5oUeV+r650zz38HWdnu+85FLoi\nIqVwjI+WddxeWbZtO/dexQsXMmAdS3t+/pn/8d92GzfDaN6c3dvvv891rhkZ3Cs5OZkTpo4f54So\n9HR+nZfHn8/NZWAWFrIaLSjgB8D7T51iCJ88yZ/z9WWQZ2ezovXx4TW2buWWld9+y7C/9VZu0GGx\ncG3vokXFq92HH2a3c1pa6b+TAQM4K9rRJqO1agV06cLZ2+6i0BURKcXhw5ywVJmlQoWF3PLxqqtK\nvn/WrOJ7Ei9c6Ly9cCHwyCNcBztmjLPb+ZJLOHP4+HF+pKczIHNyGICFhc7rWSysWH19i2/lCPCx\nOTn82ZMnWS0nJ/Nxycn8CAlh+wcM4GEHjzzCpUlHjnDM12LhDG+HRo04IeuHH0r/vTiOF3T3OOr5\ncGy76S4KXRGRUqSlsdKtjORkdsOWtBlGcjKfo3dv3s7IAP78k//5//wzt3H09+eRftu2cXnQkSP8\n2lGlOtbe5uQUD9sGDdgtvX49Q/nkSa7Hffdddh87Ariw0Bm62dkM6OxsVrqOjTSsVnaNb9vG30dY\nmHOt8S23nD0WOnAgt40sy9ChXPPrLQYM4Bppd1XfCl0RkVJkZnJCUWXs2MHJT2dWmQAr144dnetd\nV69mF67VynHeO+/k0poePbhBxuHDPLygaVOGrqMruWjg+vpyHe+xY9xFqnt3voaaNYF27Tg5a9cu\nrrutX9/ZFrud4XvqlLPLuV49Xq9+fZ7XGxEBfPUV19n+8APDadAgBlVKivNa/fo5J3aV5ppreD6x\nt2jZkt3569e75/oKXRGRUmRnl+/A9tLs2MF1siVJSOA5uQ5r17IbuqCAE5tuuokbUZw6BXTtyiBz\ndCWfOMGPnBzn0htfX3aPvvde2V3i/ftziVDRvZSLdlEfO8bQzMzkcqb0dHYJz5rFtcb5+TxUoV49\njvPGxjqv4+/P8eeyuo67deOYcVZW6Y/zpAED+IbEHRS6IiKlyM9nF3BlZGby3NySHDrEjSgcVq4E\nrryS3bh793KNbZMmzuCuW5fdvunprGx9fIqvdX3zTVae5RUczGVH/v687Zh4lZPD5/LzY+DXqsVx\n5Lg4hv/q1exidozlXn011zMX1aoV3zCUxt+fIXfmzxpp4ECeU+wOCl0RkVL4+lZ+fK+0Sjczk7tG\nAQz4rCx2N2/YwHHehARWl6mpDNqsLI4B16zJx+flOUO3TZuKnYLUvDkPVHDIy3POdk5LY+DWqMHn\niY3loQrr1zN09+3jz3TsyC7rokJDy9dN61gP7C3+7//Yw3HwoOuvrdAVESlFjRrFl8NURK1arCjP\nZLdz/W7z5rx94AC/rl2b46OXX87u2YAAXiM1laEbEMDqEyg+certtyvextGjnW8MHGO7ubnO3aoc\na4EvuoiVt6NCd0yCuuwyvpaiVfell/K1lOXii7lHtLfw9eXrdMcEL4WuiEgpAgI4qagyjh1jeJ0p\nO5vdq45gSklxBvDWrZxMdeKEc8mPY7LViRP8bLc7Q65mTeDmmyveRl9frlF1sFgY6DVq8PWfPMlZ\n0zVqsEs7NpbbPu7cyXBu0ICToo4ccV6jZUtg8eKyt3q85BLnmwhv0bOneyZTKXRFREoRHMzQrIxT\np0qeuZydDXTu7Lzt2DkKYEXboIGz0q5Zk2Hn6+tc4lM0zAIDK3/sYNGzgh2BnpfHdmRnsyu5Vi3n\nZCtfX/6MY0nV3r3Ff1f+/gzysmYwBwZ6V/cywNe1YYPrr6vQFREpRcOGlQ/diy4quYva1xeIj3fe\nLix0VorBwc5TgRzjtgUFDOEjR3hf0Uq3soELsNvYoaCAz+tYllSjBpcbWa18A9GmDdt7wQXOmceN\nG59dsR48WHZPQcOGDHJv0r07x9MrO7RwJoWuiEgp/P05kanoGtTzlZXFMdAz1anD8HJM1KpXj6EG\nsKLMymIVabE4d43Ky+PPOSpnx2dXhNaZm4D4+rLCTk3l7UOH+PuoWZPt8PEpPqbcuPHZFb3dXnZw\n1avnnklLlWG1Ai1acKMSV1LoioiUwmLhmOvu3RW/huNw+TP5+rLr1hF2wcHOytVi4f7Hl13GsdwT\nJ1j1Zmc7g7lowKWlObumK6roxCE/P4aqnx8rUbudbc3L4/M62pya6twzevv2sw+lv+CCksezi/L1\ndX1F6Qrh4Vy65UoKXRGRMrRvf/ZymPP9+XMF4qlTPGQA4CSqlSv5vQ4dOIbasiVDLjub4XbkiLM7\n18fHGbz5+dwfuaIcG10AvKbFwqB1TKgKCHB2tVssDOCcHM64btiQP1dQcPYs7Z07naF8Lj4+rjmv\n2NVateKxiq6k0BURKUNlQ7dZs3MvP+nRw1lN+fpyYtW2bQyh/ft5OzWV1W3jxgywEyfYzXvmAQYv\nvFDxQ+HffZdrhgFn6Pr58Xr16nE5kb8/lw9lZXE7yN272dVcowa/d8EFbKNDdjbHRotuNVkSx57Q\n3qZdu8r1cJREoSsiUoauXSs3ttepE7uXSwrELl2ApCTn7YgIYPNmfn/3bmcIN2/OEGzZkmGQn198\nGRHAcdGnnz7/9h0+XHxzDMdYrp8fA9duZ9gGBfH5//yTY9Hx8cB11/FnduzgRKyiXcn79jm7o0tz\n/DgrZ2/Tti2wZ49rr6nQFREpQ9euPGyg6KSh8+GYFfzXX2ffFxrKwHLo0gXYtImTpZo1Y5dvQAAn\ncyUlOWczN2jAcPTzKz5z+b33gKlTy9+2tDRWo44ua0eFW7Mmx7IdG2Ts2MGKNDQUmDuXYbt9O/dO\nBrjkp1Gj4tfeubPsKhfgkqKuXcvfZk9p25Z7U1e096AkCl0RkTI0a8bwqMyuSV278kShM4WGcj3o\n3r283bcvT905dgx44AEGXP/+fExKCjekcMwSPnWKwVizpjN47XZWuwMGsIIszU8/sWrev5+3a9Tg\ntRyBC7BbOTDQ2a5Wrdjd3rYtsG4dt4IEOBZddHMNgL8vx/2lSUriGwlvU68e3/BUdu/tohS6IiLl\ncOONZW/eX5p+/Uo+cadGDQbk/Pm8Xa8ecMMNnBQ1cCAnJ3XrxsC9/35WXo4uW8e4rmOmsU+R/9GX\nLmXl2asXz73dvJlV66FDwCefcIvJfv2cy4EcP+/ry8D18+MbjZMnnScJ3X8/K/5hw3jwQW4uK/Dc\nXGDFCr45KGr37uKHOZyLzcYQ90bduzvfELmCQldEpBx69gS+/bbiPz9wICtFx2SlooYPZ4g5ujEf\neQT48EMGX69eDOD77mMXbtu2rLyDghiO+fnOvZn9/IqPn+bmAr/8AowcySq0bl2Oyf773xyXdawP\ntlicFW6tWs5KNyeHlfWAAXzD0LcvK++77gK++QYYMYI/HxvLNwpFjwjMyuJjrr667N9NZia3gvRG\nNWu6dg2xQldEpBwGDGB3bEXPfQ0KYoVY0jmtV17J4IyL4+2ePRmSX34JjBoFzJvHQwEcS4lSUljF\nhoRwAwd/f2dXaECAs/ItyrFJRX6+M9wd1W3t2vwICOAsaR8ftrdFCwbv/v3AkCHA++8Dr77KCnbZ\nMoYvACxaxBORilqxArj2Wuea4tL8+GP5KmIjBAbydCdXUeiKiJRDUBCDcMWKil8jIgKIiTn7+xYL\ncMstrGgdgfjEEzws3m4HPvqI1erYsay2r72WAexYP2u3syJzdDc7do2qUcN5QIFjGZDjo0YNVrX1\n6jl/NiCAXcoXXMCfOXCAs6cDAhjAdeuyi3nMGOD11znZa8cOVvB33ln8Nc2dy+q+LGlpHDP15tBN\nS3Pd9RS6IiLlNHw4q86KuvVWrtfdvPns++68kxOTHIen/9//AVddxaU8N93EE4SmTWPwzp/Pcd68\nPG5GERzs3C3KsbY2IIDfCwgoHqp16zqrYkdAN2rEAHV0NwcHA//8A9x2GydDjRjBruLnnmPoBwcD\n//oXHztpEvDss7yOQ3o6J4INHlz27yQ+nuPlZe1aZRSrVaErImKIQYPYFVrRAxD8/Bii77xz9n2+\nvjwP97PPuCwIYMBu3Qr88AMwfjzHaG024NFH2b07YADvv/BCBmzr1gxPgEHqqGQbNmTFVq8eK1lH\nd3TTpnzeGjX4+Jo12WW9Zw/fBPz2G7u3p01jgDZqBEyfDjzzDF/L+vXOMeOi5s3jIQ9nLiEqyZIl\n7DL3VupeFhExSGAgK84vvqj4Ne65h2O3Jc2EvuEGhuCLL/K21QpMmAA89BCD8N13gYULGQJPPcVl\nOg88wG0ka9dmhduiBSdbHTjAIA4Kco7xOmYmBway4k1LY1DXqsUwbtSIwd63LyvyZ58FZs4ErriC\nIfzoo+y6auQNAAAgAElEQVQG79aNY71PP832BQQ4X0N2Nr8XGVn278Ju5xuKG2+s+O/T3dS9LCJi\noMhIICqq4htl+Ps7q90zN12wWFhJ7tzJGcEAl6xMn85JS5mZHD9dvpyzj197jV3NF13E8eYGDTg7\nuW1bjsU2acLgbtKEO1o1bsxgrV+fQdysGW83aQJs2cJD6C0WLhN68UXg+ec5qes//wGefJKPHz2a\n7Ro7ls9zZhfy9OmcsXzmmt2S7NjByrxTp4r9Lj3B39+1hzEodEVEzsM11zDAli2r+DUefJDBOnfu\n2fc1asQK8q23nDtYDRrELt2wMG7Z+N137GaePp1dv02bcv1saCg3rygo4ESrGjU4/tqoEbuYg4IY\nzE2bcja0vz/3PO7UiUH52298rquu4vM/8wzw8MNcwpSVBXzwAUM5JgZYvZq7XxVdonT0KN8sPP98\n+X4PX3zBtb1lbRNpJMexiq7igmOPRUSqD4uFE4tee43dwRUJDF9fVsv33cftFIseHg8Affqwy3jQ\nIHYhN20K3Hsvu4SHDQPeeIOBPXs2q9A+fRiA33/Pg9f9/bmEx8eHXdHZ2ZwhXLs290yuU4f3tWzJ\nCVPffsvu6tatOSu5RQter1YthmLPnsDEiayOf/6Z63xXrGDXa1EvvABcemn5ZiLn5bH9Je3S5U1c\n/YbAYre7cldJEfEki8UC/RP2vIICTv55/33u6lRRr7/OsdpPPy159u6kSQy32bMZhADHgl95hdXp\n2LEMr7ff5jaRl13GZToWCyc5rVjBsV3HGO/hw3xMYCDHbnv14ilH+/ZxB6usLL4RuP12Ll+aMwcY\nOpSVucXCSVMvvcRZzH37Fm/rd99xstevvxYf4z2X774DpkzhNb3ZokV8g/TDD665nkJXxMQUusZZ\nuJChuX59xZe75OfzoPQbbgDGjSv5MVOnMgBnznSeOZuRwYDbsIHdv0OHcrw3JoYBceQIw7dtW4a1\n43AEgG8Yjh7lBhe7dnHJzuWXc7JWRARvT57MmczPPOMcm128mM81c+bZbzT27mUX9IsvcqOP8rju\nOk7MKs+yIiMtXszff2XOKi5KoStiYgpd49jtrBTvv5+VYEUdPsxx4pdf5szmkixYwKU777/PKtTR\n5blxI6vhbdvYjnvu4WSngwe5beOePQxmx9F5vr6sQoOCuMzokksYkj4+DJcFCziJ6vbbOXGrRg2G\n9JQpfO7vvuPErqIyMxnY3buzi7o81q3jzOi4uOJHE3qj6GhOVnPsjV1ZCl0RE1PoGmvTJm5csX17\n+bY7PJc//2SoPvPMuXdx2rqVQRgWxolKRceBExM5qWrpUlarl1zCU406dOB48JnjkpmZrHITE9m9\nu2gRu4uHDOGSqJo1+bidOzlbu0ULBm/RvZUBTjB6/HHO5J4xo3zjn3Y7q/u77nLu3ezNPv2Ub2A+\n+8w111PoipiYQtd448ZxL+SoqMpdZ8MGjtU+/DCDvCRZWez6/fVXhusjj3BylUNODmcxb9oErF3L\nz82acUZzbi53l0pL4zrbwEBWv//3f9xko2igHjsGvPkmx3QnTGAlf+Zezrm53KErN5ebYZS3Yl2+\nnIc5fPtt8XOAvdWUKRwPL2lDk4pQ6IoYKCYmBlarFTabDZGl7CYwefJkjBkz5qzvK3SNd/w4JzV9\n9BHHZitj40aO1d56a+mbS/z3vzyeLzqaj7v/fgZrSU6e5PF9ubkMzgYNGLglVaW7djEQv/iC1336\naa7tPdPx46yKQ0LY7eyojMuSk8Pf1ZQp3r0hRlEvv8zf1fjxrrme1umKGCQhIQEAEB4eDgBITEws\n8XGxsbFYuXKlx9ol56d+fU4u+s9/Kr49pEP37pw4NX8+N6PIzS35cRdfzMorLo4B2KULu2ujothV\nXXTjDsdxfm3bcrlQUJAzcAsKuA/09OnccapXLwby1q1cp1tS4O7ZwzW9PXpw3W55AxfgsqPrrjNP\n4ALO8XBXMUFxL1I1RUdHo9//poGGhIQgNjYWoaGhZz3O4s07BwgABskNN3Ai05Illdu8v317dtc+\n8ACD8M03GbIlufhiVo2vv87lQb/9xk07YmMZjB078k2Bjw/X7losHM/dv5/juRYLx2svvpjrgOfM\nKb2bODqa63cfeohj0OfzV3PzZuDjjzk72kxq1jx7HXVlKHRFDJKeno6goKDTt1NSUs56TGJiIsLD\nw/HWW295smlSAa++ynHWV17h15URGMjlPx99xNN8bruNXb3nqirr1OGeyLfcwtspKZzRfPAglwcl\nJ3OHKsd5u1ddBdx9NydanbnBRUkOHuT2j4cPM3S7dTu/13PiBDf1ePdd7uZlJgkJ3HzEVRS6IgYq\nazw2NTW1zGuMLzLYFBYWhrCwsEq2SirC15cV6ogR/Dx0aOWu5+PDdaz9+3OGcHw8g2vw4LIrzOBg\nnrlbWSdPcsz2s8/43J99VvwIv/Kw2zk57JprKv87McL+/eceL68Iha6IQaxW6+lQTUtLQ3BwcLH7\nHVVuWca7aoaHVFrjxuzq7duXXZKuCL62bbmGduVKbv/49tvchnHoUG7r6A5Hj3IJ0G+/sTJeupSH\nKlTEhx9yK8pPP3VtGz3Bbud2nC1buu6amkglYpChQ4fCZrMBAJKSktD3f/vqpf/v8E6bzYaYmBjM\nmDEDqamp55xoJd7liiu4L/Kbb7pu/NJi4S5Qq1ez+/r339lN+/DD3Ls4L6/yz3HyJMN9yBDu27xn\nDyc+Oc7GrYjoaF5j+nTnOb9mkprqPJPYVVTpihgkNDQU8fHxiIuLg9VqRefOnQEAERERiI+Px+D/\n7Y8XFRWFjIwMTagykfBwTlgaOJBBduYuThVlsQDXX8+P//yHO0iNGcOAvP12hmPnztxfuWnTs9fW\nOhQUsILbtYsB/t//cgJY//4M908/rdxmHwDfDIwaxQr9wgsrdy2j7N3L34kraZ2uiIlpna53++EH\nLv2ZPZvLcdwlOdm5KcaGDTyn1seHE6WOHuXpQcnJrDZtNm6EcegQx1kvvJATq3r3dt3SmDVrgCee\n4PInV05C8rQ5czgb3FX7LgMKXRFTU+h6v7g4Lv2ZNs2zE4lOneJs4+PHuSmFxcKu0sBA4IIL+LU7\nLF/OGdfffMOlVGY2ZgzXNT/3nOuuqe5lERE3Cg9nF+vNN3M7wSefPHe3ryv5+3MzDE+KiuK64YUL\nWT2b3bZt3GrTlVTpipiYKl3zOHiQ466Bgey2LLJE2/Ryc7ld4tKlnDzVvr3RLXKNFi14IETr1q67\npmYvi4h4QLNmnH18ySWselesMLpFrmGzcferHTs4eaqqBG5aGrvmXblGF1Doioh4jJ8f19m+9BI3\n0Rg3jmtYzaiwkIcujBrF7S+/+658u1uZRUIC3xy5eihAoSsi4mH9+gFbtjC4Lr0UmDWLy3jMYts2\nzkqeNQuYNIkzlavaira1a127KYaDQldExABWKwPrhx/40aMH8OOP3AXJW/3zD8/W7dOHZ+n++itw\n+eVGt8o91q7lMipX00QqERPTRKqqwW5n9+zEiTyK75FHgEGDvOeQ96QknhA0cybP2X322cpvnuHN\ncnK4f/WBA6491g9Q6IqYmkK3aikoYNU7ZQrQsCFP87n3Xs6iNaItcXHsQl65khXuE09wQlhV9+uv\nPGRi0ybXX1uhK2JiCt2qa9Mmrnu12bgk5/bbgZtucv1s2qLy84F163geb1QUt5J89FE+d0CA+57X\n20yYABw5wh21XM1LOi9ERKSorl35kZ3N5UWrVrHytVqBW2/lWGpoKCdiVbQb+tQpHi6/fj2wbx/X\nD194IXeUWrmSezhXR0lJzrOJXU2VroiJqdKtXgoLOet5wwaGcEICJzddeSV3oLrwQnb/+vuzMq1f\nH8jKYqVst/Ns2NRUYPduHnbQujXXovbsyclRV1/N6rY6O36c3fkHDrj2dCEHha6IiSl0JTOTAbpv\nH08OSknh2t/MTFbFx44BNWs6u6UvuIBLYdq3Z+j6+RnafK8zbx4r/iVL3HN9ha6IiSl0RVzrrrtY\n9UdGuuf6Cl0RE1PoirhObi7QuDG3tGzSxD3Poc0xREREwL2xL73UfYELKHRFREQAAD/9xJnb7qTu\nZRETU/eyiGucOsVZy1u3As2bu+95VOmKiEi1t3Ah9792Z+ACCl0RERHMng3cd5/7n0fdyyImpu5l\nkcrbvx/o3JkbjdSp497nUqUrIiLV2tdfA4895v7ABVTpipiaKl2RysnJ4c5csbFAhw7ufz5VuiIi\nUm3Nmwd07OiZwAUUuiIiUk3Z7cC77wJPPum551ToiohItfTLL8DJk0D//p57ToWuiIhUS++9B/zn\nP4CPB5NQE6lETEwTqUQqJj4euPVWni1cu7bnnleVroiIVDuvvQaMHevZwAVU6YqYmipdkfOXmAgM\nHAjs2eOZtblFqdIVEZFq5ZNPgJde8nzgAqp0RUxNla7I+Vm1CnjwQR5UX6uW559fla6IiFQLhYXA\nM88AEyYYE7iAQldERKqJr78G/PyAO+4wrg3qXhYxMXUvi5RPVhZw8cXA3LnA1Vcb1w6FroiJKXRF\nyufll4ETJ4B33jG2HQpdERNT6IqUbedOoFcvYPNmoHlzY9uiMV0REamy7HZg5EguETI6cAGFroiI\nVGGzZwOnTgGPPGJ0S0jdyyImpu5lkXM7fBjo1AlYtgwIDTW6NaTQFTExha5Iyex24Pbbga5dgeee\nM7o1Tr5GN0BERMTVvvySE6i+/NLolhSnSlfExFTpipzt779Z4a5cCXTubHRritNEKhERqTIKC4H7\n7gNGj/a+wAVU6YqYmipdkeJeeYWV7owZQI0aRrfmbBrTFRGRKuHHHxm28fHeGbiAKl0RU1OlK0J7\n9gBXXQV8+62xeyuXRWO6IiJiaidPAoMGcdcpbw5cQJWuiKFiYmJgtVphs9kQGRl51v1RUVEAgD17\n9mDixIln3a9KV6o7ux245x7A15e7T1ksRreodKp0RQySkJAAAAgPDwcAJCYmFrs/Li4OERERiIyM\nhM1mQ1xcnMfbKOLtpk0Dtm8HPv7Y+wMXUOiKGCY6OhqBgYEAgJCQEMTGxha732aznf5eSEgIbDab\nx9so4s3WrgXeeIPjuHXqGN2a8tHsZRGDpKenIygo6PTtlJSUYvcX7W5OSEjAsGHDSrzO+PHjT38d\nFhaGsLAwl7ZTxBvt2gU8/zx3nGrTxujWlJ9CV8RA5RmPTUhIQNeuXdH5HCv9i4auSHVw8CBw/fXA\niy8Cffsa3Zrzo+5lEYNYrVakpqYCANLS0hAcHFzi4+Li4jBhwgRPNk3Ea6WlMXAffhh44AGjW3P+\nFLoiBhk6dOjpcdqkpCT0/d9b9vT09NOPmTFjBsaMGQMAmkgl1d6pU8BNN7G6ffZZo1tTMQpdEYOE\n/u+Az7i4OFit1tPdxxEREQCA2NhYjBs3Du3atUNQUBAsZpiaKeImeXnA0KEcv337bXPMVC6J1umK\nmJjW6Up1YLezK/nwYeD77wE/P6NbVHGqdEVExGsVFgKjRgG5ucD8+eYOXECzl0VExEvl5fGYvgMH\ngEWLgLp1jW5R5Sl0RUTE62RnA3fcwUp36VLzbH5RFnUvi4iIVzlxAhgwAPD3N9duU+Wh0BUREa+R\nmgpERADt2gFffQXUrGl0i1xLoSsiIl4hORm49lqgd2/gk0+89yD6ylDoioiI4fbuBXr1AoYNAyZN\nMu863LJoIpWIiBhq2zbuNDV6NJcHVWUKXRERMcwPP3Dji08+AW67zejWuJ9CV0REPM5uByZMAKZP\nBxYvBnr2NLpFnqHQFRERjzp1itWtzQasXw80b250izxHE6lERMRj9u/nhCk/P2DNmuoVuIBCV0RE\nPOTHH4EbbwTuvhuYM6dqbXpRXupeFhERt8rLA55/Hpg7lxte9O5tdIuMo9AVERG32bePa28DA4GE\nBKBRI6NbZCx1L4uIiMvZ7dw3uUcPYPBgzlCu7oELqNIVEREXS04GHn0U2LGD47hduxrdIu+hSldE\nRFzCbgdmzgSuuALo0AFITFTgnkmVroiIVJrNBjz0EJCeDqxcyeCVs6nSFRGRCisoAKZO5djt9dcD\n69YpcEujSldERCpk2zZgxAigdm3g99+Biy4yukXeT5WuiIicl9xc4JVXgD59uJ3jTz8pcMtLla6I\niJSL3Q4sXQrMng1kZXGiVIsWRrfKXBS6IiJSpvh44NlnuRzorbd4/m1VPWjendS9LCIi55SUBNx1\nF3DzzdxZautWfq3ArRiFroiInCUlBXj6aaBbN+CSS4Bdu7gkyFf9o5Wi0BURkdOysoBJkxi02dnA\n9u3ASy8BAQFGt6xq0HsWERHB8ePAjBnAihUM2F9+AS6+2OhWVT2qdEVEqrGDB4GxY4E2bTgb+a23\neFCBAtc9FLoiItXQ9u1cY3v55UBODrBpE8+6DQ01umVVm7qXRUSqCbud3caTJgEbNwKPPQbs3g0E\nBRndsupDoSsiUsUVFADff8+wTU0FnnkGmD+f2zeKZyl0RUSqqGPH2GX88ceA1cqx25tvBmrUMLpl\n1ZdCV0SkCsnPB5YtA2bNAuLiuHPU7Nk8BUgbWhjPYrfb7UY3QkQqxmKxQP+EBQB27mTQzpkDtG7N\nSVJ33AE0aGB0y6QoVboiIiaVkQHMm8ew3bcP+Ne/eOLPpZca3TI5F1W6IiamSrf6ycxk9/FvvwEz\nZwIREcD99/MAeW3R6P30RyQi4uWOHAF++AFYuBBYswa46ipgyBAu92nY0OjWyflQpStiYqp0qy6b\njSH73Xc82ef664FbbwVuuIEzkcWcFLoiJqbQrToKC4HNmxm0Cxeyur35ZgZteDhQq5bRLRRXUOiK\nmJhC17zsdmDHDk58+ukndhv36gVcdBEwaBDQs6fW01ZFCl0RE1Pomofdzi5jR8iuWgX4+wN9+gDX\nXcfPzZoZ3UpxN4WuiIkpdL3X8ePAli3sMrbZgJgYblzhCNg+fXiyj1QvCl0RE1PoGs9uB/7+m+H6\nxx/Oz4cO8QSfzp3Zbdy9O9C+vXaFqu4UuiImptD1rJwc4M8/i4frH3+wm/iKK/jRuTM/X3SRxmTl\nbApdERNT6LrP0aPFw3XzZmDPHqBtW2ewOj43amR0a8UsKn2I/erVq13QDO+l12du3v76YmJiEBcX\nh6ioqArdX5W5+8/ObucpPJs3A4sX8ySeiROBgQOB5s1Zqb7+OrB/P8dhP/+cx+Jt3Qp88QWPx4uI\nqHjgevvfzcrS6yuZQrcMen3m5s2vLyEhAQAQHh4OAEhMTDyv+6u6yvzZFRYChw8DmzZxzesHHwDj\nxgH33AOEhQHt2rFLuH17YPhwYPp0ICEBqFsXeOghbrGYlgasXg289x63WezSxbXnz3rz301X0Osr\nmbaBFDFIdHQ0+vXrBwAICQlBbGwsQkNDy31/dVJQAGRlFf9ISwMOHAD++efsj4MHebpOixbFP/r1\nc37dvDlDVsSTFLoiBklPT0dQUNDp2ykpKed1v1EKC4HsbAaf43N5Ps73sUePAlOn8nZ+PlCnjvOj\ndm0gJISh6QjR0FDn182aubYqFXGVSk+ksmj+u4iIVEMVic9KV7qaOSlmZrez6zInB8jNdX6c7+2K\n/MzOneMQENAXdeqE4+jRBcjKSkLdumNO33/8+DjY7X2Rnx8Oi2UBfHx4f82a3Ie3Zk3AZrOgUyf7\n6dtFP878Xkm369fn78FRPRatJs/1UasW4FPp2SAi1ZO6l8XrFRYCycnA3r08qHvfPp4pmpHhmvDz\n8SlfSJU3yGrWZJiV9Zh9+4Ziz5543H57OL7+Ogm9evXFFVcA2dnpaNTIip07h2LLlng8/HA43nkn\nCX379kXnzsV/NxYLl7OIiDkodKuxmJgYWK1W2Gw2REZGnvNxkydPxpgxY9zWjrw8Tn7Zt694sDpu\n//MPEBQEXHghP1q3Bpo25Ud5w/Bcj/HzM3IDg1BERcUjIyMOl15qxaBBTNRu3SIQHx+PRo1CsXNn\nPFavjoPVakXnMxNXTMNb/q2J8So9plvWX6by/mXzVmW1PyEhAUlJSUhNTTXV63O0e/DgwYiKikK3\nbt1KnBkbGxuLSZMmYcWKFRV+rqwsbpNXNEiLBuuhQ0CTJgzTosHq+LpVq4pNiinrz86x9nXPnj2Y\nOHFihV+fp5T0eopujlHV/62Z7c/LwZP/1oxSVf+fdHBlzlVqZKasdYSJiYkICQlBeHg4QkJCTLfO\nsDzrJCdOnIjBgwcjPT3dVK8vOjoagYGBAJzLUUpyvhPlkpOBH34Axo8HbroJGDAACAzkhgNvvQWs\nW8cKs29fbjywdi1w8iRDee1abjrw+uvAiBF8TPv2FQvcsv7s4uLiEBERgcjISNhsNsTFxZ3/k3hQ\nVV/TW9X+vIpy1781b1GV/58EXJ9zlQrd8vxlGjt2LADAZrOZbo1hWa9vwYIF6N69OwBgzJgxpnp9\n5VmOkpiYePovWkmSk7mTzyuvMGCbNeMG7x98wLHS++4DPvwQOHUK2L0biIsDPvsMeOkl4N57gWuv\nZUXr5+f611fWn53NZjv9vZCQENhsNtc3woXKej3l/Y/dW1W1P6+iXPFvzZtV5f8nAdfnXKXGdMv6\nyxQaGoo2bdogKCjIlNvYlfX64uPjAfAfTGxsrOnGYsoaWUhNTT399aFD3N0nPp6fN23iWsquXflx\n333AtGnsDvaGN+xl/dkV7QJKSEjAsGHDPNa2ijDrmt7yqmp/Xmc6n39rZlPV/590dc65dclQeno6\n2rVrh6ioKERGRqJLly5oY7IDJMv6x9KwYUOEhoYiNjYWMTExGDx4sIdaVraS/gIEBQVh8ODBsFqt\np/+hp6WlITg4+PRjDh0CoqMTkZ4ejvffZ7fvZZcB3boxYIcP59Z4rVt7R8CeS3mmKyQkJKBr166m\nmKRU1usx+/K9qvbn5VDavzXA3FWug5n/nywPV+ZcmaFb0f+4HT87cuRI1K9fH1arFQsWLPC6dzmV\neX3BwcGnf7lWqxUbN270qr9MpQ3oDx06FPHx8QgPD8eWLUmoUaMvRo0Cvv02HdnZVrRoYUPbtjY0\naZKC9u1TMXt2Irp08a5uocr82TnExcVhwoQJbm2nK5T1esr7er1VVfvzKqrov7WkJC79AviftWPy\njc1mQ0pKClJTU5GYmGiqLliz/z9ZFlfnXJmhW97/uEv6ywQA9f+3+j48PNwrx2Eq8/qGDBmCBQsW\nnP5ejx49PNLmyiosBPLzQ7FoUTzefjsO+/dbMXBgZwwYAKxZE4EtW+JhsfAfRVRUFH76KQM+Pt5X\n0lb27+aMGTNO/+OIi4vz6mrjXK+nvPd7u6r251VUaGgo4uPjERdXfOlXRASXhjkCKCoqChkZGaab\nUFVV/590cHXOVXrJUFRU1OmJDY7/BLt163a6H3/y5MkICQkx7VTxsl5fVFQUgoKCEB8f79XvwI8f\nB1asAJYsAZYudc4oHjgQuOYa90xmMlppf3axsbG44447EBQUhNTUVCxYsADXXXedwS0uXUmvp+iS\noZLuN5Oq9udVnVSV/yfPxZU5p0Psqyi7Hdi1iyG7eDGwcSNw9dXOoA0JMbqF4go6xF7EXBS6VUhO\nDrBmDYN2yRLOLnaEbHi4jjGrihS6Iuai0DW5EydYyc6bB6xaxXWyjqDt1Mm7ZxdL5Sl0RcxFoWtC\nBQXATz8Bc+Zw96devYBhw4D+/QGTTVqVSlLoipiLQtdE/vyTQfvll9zsf/hwhu0FFxjdMjGKQlfE\nXHTKkJfLygLmzmXX8apVwD33cBZyhw5Gt0xERM6XKl0vlZQEfPQRMGsW0LMnMGoUDwAw7hg68Uaq\ndEXMpVIHHohrFRayir35ZqB7d95et44Tpfr3V+CKiJidupe9QEYG8PnnPJGndm3gsceAb74B/P2N\nbpmIiLiSQtdAR48C777LmcitWgGffsrdobTMR0SkalL3sgGSk4HRo4GLLwZSUoCvv+Y62169FLgi\nIlWZQteD9u0DHn2UM48LCoAtW4CPPwZMdtqhiIhUkELXAw4cAF54AejSBahXD9ixg93KLVoY3TIR\nEfEkha4bHT/OsO3UCfDxAf76C5g4EWjc2OiWiYiIERS6bpCXxzW27dsDf/8NJCYCr74KBAUZ3TIR\nETGSZi+7kN0OLFoEjB0LNG/Oc2tDQ41ulYiIeAuFrots2ACMGcPZyO+8A9xwg2Yii4hIcdoGspLS\n04Fx44B//gFuvRW47z7AV29lxEO0DaSIuWhMt4LsdmDBAufBA19+CYwYocAVEZFzU0RUwP79XG+7\nezc3tbjmGqNbJCIiZqBK9zwUFADvvcfJUd27c1ayAldERMpLlW45bd8O3H8/UKcO8Ouv3MJRRETk\nfKjSLYPdDrz/PnDttTzTdtUqBa6IiFSMKt1SHD7M2cipqcDvvwPt2hndIhERMTNVuuewZAnQuTPQ\ntSvwyy8KXBERqTxVumfIyuImFz/8wJnJvXsb3SIREakqVOkW8c8/QHg4kJ8P/PGHAldERFxLofs/\n69YBPXsCt9zCwwqsVqNbJCIiVY26lwHMmQOMHg3MnAncdJPRrRERkaqqWoduQQHw3HNATAywerVz\nS0cRERF3qLahm5EB3HUXcOoUTwgKDja6RSIiUtVVyzHd3buBK68ELrwQWLFCgSsiIp5R7UI3Lg64\n+mrgsceA6dMBPz+jWyQiItVFtepenjsXeOop4JtvgD59jG6NiIhUN9UmdD/4AHj7bWDtWqB9e6Nb\nIyIi1VG1CN133mHorl4NtG5tdGtERKS6qvKhO2ECMGsWsGYN0LKl0a0REZHqrMqGrt0OvPoqx29X\nrwaaNTO6RSIiUt1VydC124EXXgAWLWLgNm5sdItERESqYOja7TwlKC6OB843bGh0i0RERKhKha7d\nDrz1FqvbuDggKMjoFomIiDhVqdCdOhX48ktOmlLgilnExMTAarXCZrMhMjLyrPujoqIAAHv27MHE\niTnWBzMAACAASURBVBM93TwRcaEqsyNVdDRDd+lSbeso5pGQkAAACA8PBwAkJiYWuz8uLg4RERGI\njIyEzWZDXFycx9soIq5TJUL355+BUaOAxYu1LEjMJTo6GoGBgQCAkJAQxMbGFrvfZrOd/l5ISAhs\nNpvH2ygirmP67uUdO4AhQ4CvvgKuuMLo1oicn/T0dAQVGQtJSUkpdn/R7uaEhAQMGzbsrGuMHz/+\n9NdhYWEICwtzeTtFxDVMHbrJycCAAcCkSUDfvka3RqRi7HZ7mY9JSEhA165d0blz57PuKxq6IuLd\nTBu6mZnAjTcCDz4I3Huv0a0ROTfHRKiigoKCMHjwYFitVqSmpgIA0tLSEHyOCQlxcXGYMGGCW9sp\nIu5nsZfnbbaXsduBkSMBf39OnrJYjG6RSMUkJiYiPj4ekZGRmDx5Mvr27YvOnTsjPT0dVqsVADBj\nxgw89NBDABi+jklXAGCxWMpVKYuIdzDlRKr33gM2b+aaXAWumFloaCgAhqnVaj3dfRwREQEAiI2N\nxbhx49CuXTsEBQXBor/wIqZmukp3/Xrgppv4uU0bo1sjYixVuiLmYqpKNzUVGDoU+OQTBa6IiJiP\naSpdux249VYgJITjuCKiSlfEbEwze3nqVODQIWD+fKNbIiIiUjGmqHTXrQNuuYXjuK1bG90aEe+h\nSlfEXLx+TDctDXj4YWDGDAWuiIiYm9dXupGRQJ06wPvvG90SEe+jSlfEXLx6THfVKmD5cmDbNqNb\nIiIiUnle272clcUqd/p0oH59o1sjIiJSeV7bvTxuHLB3L/DNN0a3RMR7qXtZxFy8sns5IQGYORPY\nutXoloiIiLiO13Uv5+cDI0bwuL7GjY1ujYiIiOt4XehGRQEdO+q4PhERqXq8akz3+HGgfXtg2TKg\nhLO6ReQMGtMVMRevqnQnTgT691fgiohI1eQ1le7ffwOhocAffwAtWhjdGhFzUKUrYi5eE7r/+he3\neXztNaNbImIeCl0Rc/GK0N2yBbjjDmDjRqBePaNbI2IeCl0Rc/GKMd3x43mogQJXRESqMsMr3e3b\ngT59gKQkwN/fyJaImI8qXRFzMbzSnTABeOIJBa6IiFR9hla6NhvQowewZw/QoIFRrRAxL1W6IuZi\naKU7aRIwcqQCV0REqgfDDjw4fBjYsIG7T4mIiFQHhlW6UVFA9+7ABRcY1QIRERHPMmRMt6AAaNMG\n+P577kIlIhWjMV0RczGk0l26FGjaVIErIiLViyGh+9FHwL//bcQzi4iIGMfj3cv79gFXXQX89ZfW\n5opUlrqXRczF45XuV18BgwYpcEVEpPrxeOjOmwcMHerpZxURETGeR0N3xw4gJQW4+mpPPquIiIh3\n8GjozpvHI/x8DN/xWURExPM8Fn92O/DNN+paFhGR6stjofvnn0CzZjzgQEREpDry2N7LixcDHToA\nFounntEccnJ4ytKePVxOdfQocOwYewYOHWJXvI8P0KQJH9u0KbfObNMGuOgifq5Rw+hXISIi5eGx\ndbp9+gCjRwM33uiJZ/NeaWnAypXApk1AXBwDNT0duOwy9gQ0agQ0bMhg9fMDCgv5AQCpqXzs3r08\nFrFuXV6rUyegf39+DgsDrFYjX6F4ktbpipiLR0I3M5OV2qFDQECAu5/N+9hs3Pry66+BrVuB3r2B\nm24COnYEOneu3Jrl9HRg82YgIQFYvhz47TcgPBzo1QsYPBho3dplL0O8kEJXxFw8ErqLFwNTpgCr\nVrn7mbxHTg4wfz4r0aVLgeHDgeuvZxjWru2+583OBlavBmJi2F2dmws89BBw223ufV4xhkJXxFw8\nErqPPQY0bw6MG+fuZzJeRgbwySfAu+8yYIcNY5e6n5/n25KbCyxaxGMUCwqAa68FRo0CAgM93xZx\nD4WuiLl4ZPby/v1ARIQnnsk4WVms5i+6CDhyhF298+Zxy0sjAhcAatYEhgxhW6ZNA5KSgEsuAV55\nhW8ORETEs9weuidOsIv1iivc/UzGsNt5LvBll7E7d80a4O23OV7rTS69FJg5E9i4ke1s3x6YPZsV\nsIiIeIbbu5fXrGG38u+/u/NZjHH4MPDww5xd/Nhj5qrmt27lbPK0NHaHd+lidIukItS9LGIubq90\nN24Eund397N43vffA/36sYKMjjZX4AKsxJcvBx59FHj8ceCll4C8PKNbJSJStbm90h06lBOJ/vUv\ndz6L5+TnA6+/DsyaBcydy7OBze7QIeC++zjO+9VXQEiI0S2S8lKlK2Iubq90N2yoOls/pqcDAwcC\n//0vkJhYNQIX4BrqH3/kYRS33QasWGF0i0REqia3hm5KCtCyJWf0mt3Bg1zv2r498MUXQFCQ0S1y\nLR8f4KmngPffB+69F/jsM6NbVH3ExMQgLi4OUVFRpT5u8uTJHmqRiLiLW0N31y5u1mD2o/ySkrjm\ntksXLr3xdcOO1SdP8ve1fj03EVmxAli7FoiPd25y4Qm9e3PS25QpwLPPOregFPdISEgAAISHhwMA\nEhMTS3xcbGwsVq5c6bF2iYh7uPXAg927gbZt3fkM7rd/P/Dgg8DTT3PSkSucPMntGlev5kQzX18e\neFBQAPTsyXHjtDT2EmzezAMQQkLYc9CuHSdtdesGdO3qnsMOWrcGfvmF20g+9xwwYYL53zh5q+jo\naPTr1w8AEBISgtjYWISGhp71OItOChGpEtwaunv2mDt0jx3jDOXIyMoH7qlTwJIlnPX8/ffA7bcD\nrVqxS/fyy4EWLUo/gSkvj29i/vgD2LkTeOABHnjQoQNwzz3cbcqVARwUBPzwA3DDDXzt06frhCh3\nSE9PR1CRsYqUlJSzHpOYmIjw8HC89dZbnmyaiLiB20P3f71mppOdzbC54w5WuRX1zz/ABx9wHHjA\nAH588MH5nwTk58flSZdeytvjxwN//w0sWMD1ts2bswIeMcJ1h0oEBPCNQr9+3MXq5ZcVvO5Q1uzj\n1NRUD7VERNzN7d3LDz3kzmdwD7sdeOQRjmeOH1+xaxw9Crz2Gmc6d+gA/Pqr60/8adWKbwiefpqz\nxN9+mxXpgw8CTzzhmgMO6tfngQ0REUCDBqzM5fyUNEEqKCgIgwcPhtVqPR2qaWlpCA4OLvY4R5Vb\nmvFF/pKGhYUhLCys0m0WEfdwa+gmJ5tzzefnn3Ms97vvzr+yKyzkzN9Jk7g++csveUauu/XowU06\ndu3iOOzFF7Oivummyl87MJC/iyuv5JiyK65ZnURGRp7zvqFDhyI+Ph7h4eFISkpC3759AbDb2Wq1\nwmazwWazISUlBampqUhMTDxrzHd8Rd8ZiojHuS108/LYtdqkibuewT127ACeeYYTic63m/bgQW4y\n0bgxQ+ryy8v+mfR0YMsWHkx/7Bi7jOvV49cNG/L317Ahq+VLLil75nT79jzWb80ablH500/ACy8A\nZxRQ561VK76me+5hO6rCMjBvEBoaivj4eMTFxcFqtaJz584AgIiICMTHx2Pw4MEAWC1nZGRoQpWI\nybltR6pDh3jIweHD7ri6e+Tnc+es667j5KnzERfHrtfBg4Hnnz93OBYUsKt54UIuRTp6lNXxVVcx\nXGvXZmV56hQ/cnJ4QP2+fZz13KwZx5n79Su7uzorC3jxRXbzjxsH/N//nd9rKslHH3Gv5t9/B+rU\nqfz1pHK0I5WIubgtdLdt4xaQf/7pjqu7x9SpwOLFQGzs+XUrR0Ux3L75BjjXcFpqKvD118Cnn3KW\n8dChQJ8+QGho+df9pqdz7e6aNVzH27Ahg37gwNJnLi9aBLzzDmc8Dx9e/tdVErsdGDmS1fxrr1Xu\nWlJ5Cl0Rc3Fb97Kje9QsDh5kkH388fkF7uuvsxL9+eeSu1xPnOAEp59+4hrczz7j+tozFRSwVyAt\njdVtzZqc4dy4sfM8XqsVuPlmfuTkcOnRxImcGT1sGLdwLKntN9/Mto0YwbW+lZkMZbFwvLpjR75p\nuO66il9LRKS6cVulu2QJsGwZd3AygxEjOO55PkshJ01i2M6YATRtevb9S5eym3rIEM4mbtPGed+x\nYzxneM0adjc3bMhu5hMneH9hIbuSN25k2Pbvzw0xwsKACy90XsduZ2U+cya7kydN4rhuSQ4c4BKu\nRx/lUYSV8eOPfDOxeDHg71+5a0nFqdIVMRe3VboZGayqzGDHDo6Zvv12+X/mq684sSgm5uzAPXUK\nePNNYN48din37s3vFxYyaOfPZ1j16sVKccQITpQqaYw0N5djsvHxPGRhzhyG66hRHD+uVQvo25fX\nmTmTM4ynTuXY9JlVb/PmrLj79OHPVWY514ABbMtbb3ENr4iIlM1tle7s2dzmcPZsd1zdtYYP5xKb\n558v3+PXr+dyoDVrgMsuK37fwYPAmDEMvI8+4kxku52V/8cfc/z21ltZ/fr58VqbNjH4d+5kd3Je\nHkMxJYVBefXV7M7t3p3XWrYM+PBDhvjw4exadowL79zJiVYDB3KNca1aZ7c/KYkTsd55p3LLf/7+\nm2PSCQnFq2/xHFW6IubittCdMYPV2YwZ7ri66+zbB9x/P/Dtt+XbJerYMeDOO1lp3nJL8fv++ou7\nQ115JTB2LPcrPngQ+Pe/OQb72GPcVvHXX1kB//wzZypfdRXDu0kThqdjn2PHsqukJIb2sWMM2UGD\nOC4cH88qs25dHk7QpQt/7uRJPi4wkFVvvXpnv45169jNPHfuubujy+ONN9irMWlSxa8hFafQFTEX\nt4XutGncjemDD9xxddf5z3/YHfzuu+V7/L33cvx1ypTi309KYqjecotzuVFsLNe1jh7NMd116zjT\nOSuLlWbbttxLeft2jv+mp3NZT40aDFvHNppt2nDNb5cuvMbChezSfvJJVr9ff81NOPr14/csFk7M\neuQRhv306SWPu0ZFcWw2Oto5Wet8nTjB17FmjXOLSvEcha6Iubi10k1O5n693iovj2OhUVHc8KEs\n33/Pym7t2uJbLKamcly2Tx/nBKUZMzhGPHMmrz16NLtjIyMZiFOnMhzDwthNbLEwdFNSeDsw0Dm5\nKiCA9339NbuDb7+ds5zfeovbM77yCm8/+ihPJpo2jSFaWMjNOnJzGcpnLk2y29lN3r17xbe7BDh+\n/eefHOcWz1LoipiL2w5sy8sDjhxx19VdY/lyBk95Ajc7G5g1i0t0igZuYSGr5bZtnYE7dSrHspcv\nZ9Dddx+3T3zqKW4s8fnnrGAvuYTjuX/8wco2I4Nh6ePDEN2/n99fvhzYupUTp0JCOIFp5ky2p149\nXj89nRXrgQPA448zaH18uETJbmcbz2Sx8A3HL7+wV6KiRo1ie/fsqfg1RESqA7eFrr8/u2292c8/\ns7u4PGbN4ucz16V++iknQU2YwNszZ7LLdsECBuWoURxfTUvjxKWrrmJX9K5dDEqAO2EdOcIx28OH\n+fWhQxwPTk5myJ88ycd//jlDtHdvTpZq3pwTqZ56il28MTG8zpgxfEPg58fu5R07eFTfmZo14zjz\n6NEV+x0CPBTh8sv5PCIicm5u616eN4+Tk+bNc8fVKy8/nzOFt2xhcJUmK4sV5vLlQKdOzu/v3s1J\nTd9/z/t/+YXd6R9/zMrx/fcZZh9/zK7ivDyGap067Ea2WBiu9erxvsxMBurhwzzP1s+P9504wdC9\n/HK2u2FDdlX36cMx3g4duMPVU0+xy7lXL+Cuu7gZx9ixbOsvv/D+pUvP3rQkN5fLjl57zbm86Xwl\nJfENyZ9/at2uJ6l7WcRcqm2lu3Ejg+b/2zvzsCjr9Y0/A7JvI4gbLkCG5jELNXH9qQe3Nk+Fu2md\nzLLjMTuV1jmeLpf2OJ5yKTWUtKyM5KRmZYrWUTulgihu5QJuiJBsoggMML8/7t7eGWaQEWaF+3Nd\nczHLO+/7nenKe57n+zz3U5fgiiBqfeABY8EVgZ/xpEkQ3KIiPH72WaSJly5FUdO8eXCDunQJQpuX\nB1E9ehT39XoIaE4OUthXr6IaWafD4wsXcExgIHyaT59GJNuyJUS/Sxe8Z+1aiPysWTj38uWI5Hfs\nwFoHDECU/dJLpp/P0xMWkQsX1v/7jIhABfbGjfU/ByGENHaarOimpIi0a2fZsUuWwErRkB9+QHSn\nWCq+/jpEuX9/RLdTpkD4YmIQjSpRbVWVyJUrKJQqLoZYV1ZCOEtK8PfaNdyKixGF5uTguywuRpRc\nWAihPn8eKejyctySkmBL+fLLiJJnzsTar17FGhcsQO+0OT/sCROQ8k5NrfdXKg8/jIItQggh5rGZ\n6AYEIEXqrGRnIz1bF0eP4nMMH278/DvvIKr08kIh1Nq1qGyeOxcR37Fj+A4OH8b78/IgqsXFEN/C\nQmORrajA/bIy3EpL8beiQq1sVo69elV9rrQUkXBQEKqos7Mh/vPni4wYgTUoVpxaLSJac3uvnp7o\n9V2+vP7f6f33I12v7FUTQggxxmaiGxLSsKjJluj1SBkrZhI3YutW7JkaTvE5dQrCN2YMHi9ahOjy\n9GlEwMOH46+XFyLa3FxEpbm5ENLqaoivIrDl5Yh2q6txq6pSb+XlqviWlkKoy8ogbO7uWMe5c3C2\n6toVhVSjRmH03q5diMDj4xEti8Co47PPzFcajxmDfXglMr5Z/P0xznHbtvq9nxBCGjs2E92wMKQ+\nq6ttdYX6c+mSyJ13mh9SUJOkJPSyGrJ5M8TFxwcuVNnZ2NtdtQqD4199FY5ReXnYh83KgoD6+aEo\n6/p1fC86nen3Y25En16PYysq8FcR3ooKRLe+vqhwfvttFDOtWIH95FWrkEKfOVONdv39kRL/5BPT\n67RqhcKwr7+27Hs0x333wVeaEEKIKTYTXW9vpDYvX7bVFerPiRMQrbooKUGfbb9+xs+vXo3CKhGI\nco8eiBy//BLHh4RgOEFJCfZv/fxw7LVrqlgaim3Llqg6zslBxHvhAvZep09HpKygRL7l5Wr6ubwc\n1ygvh1HG/v2Iolu1QvS7axeGH2zZguuKwDXr/ffN/yDq3dt8a5Gl3HsvrllVVf9zEEJIY8VmoiuC\nKCs725ZXqB8XL8IfuS7S0xEZGpphnD0r0qEDxEmvhwBPnAjDilmz4JHcpQuEVimSUkb2KZGqIkjN\nmiECvXQJ+6mtW+P5sDCRQYOwv5qfj/1jJQLW63EepfiqrAyRdEUFPldJCaLsDz7AOT/+GMYcPXui\neEwErUfdu5tP/99zD1Lh9c1QdOiA/eGff67f+wkhpDFjU9ENC0PU5mycPasOFbgR6ekQLEP27IGg\nurkhtRwRgVTzoUMQ29JSCI5Gg2OUSmSNxjid7OWFfde//tX84HkFjQZifuyYcf9rZaW67xsQgPNp\nNDC7KC7G5J+YGIzyu3YN6XTDtHHnzub3Xjt0gOg2xKFKGcZACCHEGJuKbq9eagGPM1FWZll/7oUL\nphaRBw6oUfLevTjPyZNIWZ88iWi1shJp9aIiRH2KQOp06nk++wzfj6VERaHvVkE5Z2UlUsvHj6vC\nvno1ImXlR8Pu3ahk3rQJkbIIepRri0ZjYvDZ6stddyHNTQghxBibim5oKETK2bh40fyc2ZocP246\nJ7a0VJ2ms28fIsi0NOyTbt8OE4uAAKSO/f2RAtbpII4K/fqZjgW0hB494LOsUFmppq87dEDh1oUL\nEPN27SC6/fohou7cGVH5xYt47513ojLbXBq5Z0+0OtUXii4hhJjHpqLbrZvIkSO2vEL90OkQgdZF\n8+YoSDJkzx41Stbp0E505Agqoc+cgQjq9bhdv64eZ4hSSVwfDEcQKkJeXY09XXd33Pz98fzp0xDX\n/fsRBd9yCyJyEazX1xfVzzUxPK4+dO+OPmS6ExJCiDF2EV1n+8e3ZUvL/IHT0lQBUwgNVb2Lc3Nx\nrjNn8LyHh1qtrfgoV1YaR7q+vpb1B9dGUJBIeLj6uLoa6fLLlyGsHh5oZSorQwV0x45qEZafH/az\nFbp2xf5tTcLDG1Z9HBSkmoAQQghRsanotmgBAXC2YqqCAohCXXToYDqD1sNDFeyMDKSSmzfH52zT\nBilepS1IRC2iUggNbfj6u3VT7ytmGkq63NMTzxUUQPw8PNS94KAg0/8Wubmm52/RAoVhDWHkSGOB\nJ4QQYmPRFYE7krOlmP380FpTF4o9Y83nlMhd8W5WzC+uXEHFsmLd6OkJMTTcP7aGNaahqYdGA5H1\n9VV7j5Xn2rbFepRisKAg41R3VJTp5xPBjwittmFrLClxvh9bhBDiaGwuuq1bYx/UmQgNRf9rXSjj\n9gw5c0Z9rk0bFFaFhUGIjx2D0Or1iJBLStRIVIl2reFLnJen3q+uRvq4shL9xEphlF4P8dRo1JS3\np6dxuvzKFfOi6+XV8D7bzp0t+2FDCCFNCZuL7oABzie6bdtaFoX17Gkqzr17qynZykrcDwtD207X\nrji3nx8iT6VXt7paFd2LFxu+x52Wpt53c0MKuaoK4ltRoaaby8pwXSVqrVncVFhomj4XwTGW9DHf\nCL2ee7qEEFITm4tunz4QifJyW1/Jctq3t8yeMjDQ1FGrTRtVsFu0gImEVity8CDSt/7+EEEvL0Se\n3t5q9CuC7+H77+u/9itX1OtrNBBaT09cp1kzRN7Xr2Ov+dAhHO/jg+PPnYNFpYKnp+lAexFE+PUd\nZq/g749UPCGEEBWbi25AAFKNzuRQ1KULeljrIizMtM+4RQs10uzdG6Lcuzf6Wjt2VIfP5+erUWSz\nZsaDDGbNqv/alfm9Iqro6nS4X1WFauRmzfC9DxqElLfS4lRRIXLrrer7c3JMW6JEEAEfP17/NYpA\n3BWxJ4QQAmwuuiIwwd++3R5XsoywMAhQXenPnj1NHbX69lXf17OnOiLQywsid/as2i6Un48I1M3N\nWHQPH67f3NqDBzG3V0E5pxJd+/tD+AsKIMR9+8Ila8gQrCczEz+ARBBxp6WZmn+IYM94wICbX58h\nJSWcq0sIITWxi+jGxjZsco210WggRHW1xdx5J0bzGRZT9ekDC8fr10Vuvx2R47lzKM4qL8f+bYsW\nSGFHRCDqVIqpDIV3xgyMCLSUkyeR8lX6Z93dIaQeHriGnx8eR0XB+vHbb+FG9dVXiMSPHYOYKpHt\n8eNwxTJnEpKZab7A6maoqjK/X0wIIU0Zu4hu//6o+nWmiUNdu9a9t+rjg0jV8DitFj8iUlIQwXbq\nBE/jMWPgeRwejmKqw4chiG5uiECDg3E+RYj0eojezJmmjlWG6PUiK1eiN1epBlZMMHx9cXNzQ1R9\n5gxSw507o2DLzw97unfcAcvHuDj1vMrgBnOcOaNGxPXFx8c6PcmEENKYsIvoNmsGswRnGm4eGyuy\nY0fdx40fb5oaHzUK03tERB57TGTJEny+wEBE0KdOIdUcHAxRDAhQBbhmqnnZMgj5lCm4Tk4Oout9\n+0Rmz4ZwTZ+uRp6K4Lq747w+Pijg6tABn6m6Gn3Rzz0nsnGjyBNP4Jrp6RhyoPDddyLDh5v/zPv2\nGRtw1Idz55zPiYwQQhyNXURXBEJ1M+lUWxMTY1lby8iRSO0aVl8/9BAi3ZwcpKD/+EeR9etFxo5F\nRBkTA7E9dQoiaLjn6u0NwTRsySktFfnoI4hg27awloyJEfnXv4xblgxtHgMCkBr29VUrlouKMMg+\nJwdzcdesEZkwQeToUUS2Q4ao17t2Des2x08/IY3eEAoKrGMEQgghjQm7ie7IkehrtcR+0R54e2P/\n8z//ufFxHTtCLA2jdK1WZPBgRKkiIo8/jvOMHo20cps2iDaHDkWkWlwMUdRokNJV5vF6ehpHvbWh\n9OJ6e0NkmzXDe5VINywMFdlBQbjuiBEi778P4W3fXmTxYpGpU9XU9tateK+59G9WFlLvNecI3yx6\nvbFzFiGEEDuKblAQ/qFfv95eV6ybceMsmxs7YYLIW28Zp0vnzBFJTETP7MCBSO8uWSLywQci8fEi\nTz+NCDk8HMVW7dqhwErp5Q0KQsWz0mPr5QVR9PAwvimC6+EBsfb3h9NUYCCKlfz9cY3iYojlsWNI\nM2/cKPLPf2J/9uxZpKgVEhMRrZvjm28QaRv6RdeHtDRE7YQQQlTsJroi2P9MTLTnFW/MyJGo8q3L\n8nDUKAjdtm3qc+3bo992zhw8fuklRMNFRbi/fDn2g7/5BlaYoaEQWq0WQurlhR8hWq0a8SoVv4q5\nhuJs5ecHkfXyUlPVvr6oRA4OhvDffTfG/q1di8roOXMQAb/zDqqYlag2PR2f17CoypD0dHzehlBd\njc9srh2JEEKaMnYV3REjUMHsLAMQPD2RGl6x4sbHubvjuL//3Xjo+9NPwwhj0yaIzLx5IpMnY6+0\nf3+RL74QWbAAbTsRERBbHx8IeEgIRFcRXyV69feHyHp74/mWLfF8SAiOCQiA4FdUIHo+eVLkwQdF\nXnsNrUxLl6II6tFH8SNgyxYUZCksWQJBNtcqlJODvuORIxv2vWZlYR5vUFDDzkMIIY0Nu4quu7vI\nI484V7T75JMwnTA3zN2QuDgMrP/gA/U5X1+RZ5/F/un58xCrf/wDe6mzZiHS27ZN5O23cY2oKIhd\ncLAqsGFhEFgRCHfLlrgFBKB4y91dnRbk46NWP3fvjgrhxx/H+RMSUHWcmyvy6qsoEHvzTXzXyijC\n3bsRFU+ebP4zfvYZ0s61tRJZSkYG2pQIIYQYY1fRFRH5859F1q1ruPmCtWjfHnuu//73jY/TaGDB\n+OGHxi5VAwZAZKdMQR/tk0/i/qhRIs8/j4h34UIUMul0EPeoKDW1HBCA/WBlv1eng0iGhECEtVo8\nDg1VC6Y0Ggh3TAwqwtesgdHH1q34HN7eiG7vvlv1UNbp8Ny0aebtGXU6iPdf/tLw7/TQIfwoIIQQ\nYozdRbdTJ5hCfP65va9cO3PnIsVsODLPHD16oCL5pZdUZygRCFW/ftjDLS0VefFFtA/NnAmryNWr\nRd54A2L39NM4xsMDBVChoUgxu7lBXMPDIcRFRRDC6mr8MAgNRaTq7w8x/e9/IfIrViBC3b8fui7H\n8wAAD6ZJREFU4tumDcReBOtQWLBAJDISJh7mSErC6z17NuCL/I28PJFevRp+HkIIaWxo9Hr7Wxhs\n3Yp9xUOHGl4lay3mzoWb03vv3fi4ykoI78CBIi+/rD5fVYVI8uxZpKADA2F9uXw5hOy55yCKX36J\nqHbYMHV/u7AQhhjh4Yhw9XqkjgsKILLt2yM6DglBCjk1FT28rVohgh44UOT115G6fuopiPqaNWra\nOiUFPxSSk81XFOt0iNZffBGVzw2huhrp8YMHsedMbItGoxEH/C9MCKknDhFdvV4kOhrFP/fcY++r\nm6ewEHu2W7Ygor0RublwehozRuThh9Xnq6og3j//DFHs1Al2jHPmwKDixRchtuvWQXzLy+GL3LEj\nfnzo9WoVswii37IyiOaJEzDPGDEC+6UJCdivffdd7MPm5YlMnAj7xrffRvQsgvdNmCCyaBF6i83x\n3nvoM96+veE/gjIysP998mTDzkMsg6JLiGvhENEVQb/ue++J7NrliKubZ/16RIz795uv7jXk2DFU\nKScmGv9w0OshiJ9/DkGeMgVCtnMn9oN370Y0OnYsouYff4Qw7tkDn+Tr19U93BYtEOF27Yr0dUYG\nrldUhB8tzz+Pvd1t25DKfvRRkRdeUN2usrNxrVGjUHBljvx8FFa99hrctRpKfDyKypYsafi5SN1Q\ndAlxLRwmupWViCwTE1Fs5Azo9RDJLl0QsdZFWhrGFq5YIfLAA8avHTyICmatFoL2hz/g+dRUtdo5\nKAjX6tQJxVTNmyOdXF0NAb54Ee03GRmIHMvKIJATJ+K82dloU9q7F61ChpHs2bNIad91F4S4NiZN\nQpq6rkIySxk0CJH9vfda53zkxlB0CXEtHCa6Ioj8Vq1CUZCz7O3++ivSyytWWCYcGRkoDJs9G1Gl\n4efQ6WAVmZiI1PDkyWo0WVkJAT50CMKal4f92ogIpJh9fSGsYWEit90GL2RlPzYrCynqTz/Ftf/x\nDxRfKaSnYw932DAIf21s2oR96V271LaihvDrr/iMX3zBAfb2gqJLiGvhUNGtqsL+5GuvNdwFyZr8\n8AOMMBITLfMgPncO6eK2bfEerdb49eJijOfbtAnFUn37QhCjoiz/sXHxIvZc9+zBvNv+/WHtaFgY\npdfDkSopCanmsWNrP9+JE2h3+uorRMPWYNkypMs//tg65yN1Q9ElxLVwqOiK4B/92bMRMTrT0POV\nK1F89L//YW+1LsrLUSi1bx/+3n+/6TFVVRip9/33iHKzsyHqkZEit9yC/VkPD4jnlStIKRcUQNQr\nK9H72r8/WoZqRpKXLmFft6wM7UlKOtscV66gf/epp9BXbC369kWE7SzFcU0Bii4hroXDRVevRwvO\n5MmIzpyJhQtRVLVuneWWhjt3IgIdNAip3drm0iptQYcOiRw/DtEsL8d+rjKD19MTRVSdO2Pv181M\nV3VZGfaIk5LQOjRvntoqZI7ycqTN77oLGQZrpfWPHYOIp6TghwOxnOTkZNFqtZKZmSnTpk0zef3A\ngQOSlZUlBQUFJq9TdAlxLRwuuiKIcocNw1i8li0dvRoVvR7RY1oaBhfUTBvXRlkZWnni45E2nzYN\nZhHW3LcuLITpxhdfoJJ5xgzs/d4InQ6OYGVlMNSwZKygpcyciUKwhQutd86mgCKocXFxkpCQIL16\n9ZLo6GijY8aOHStJSUkSHx8vQ4cONXqdokuIa2F3RypzdO+OSFdxUnIWNBpUBd99NyqDDe0fb4S3\nNyqHT5xAhPr88xgjuGhRw/pXr15FOn7SJOzXHjyIfdRly+oW3LIyzPv18MCeqzUFt7AQn8tMkEbq\nICkpSZo3by4iIpGRkZKSkmL0+oYNG+Su3zbdZ8+ebSLIhBDXwml2UefPxz7kzp3of3UWNBrsUzZr\nJnLffXCbstRXODAQAxGeeQYFUFu3igwZgr3brl2Rgg4Ph3NTaCgGDbi7w5e6sBAmHJmZENfMTLx/\n3Djs644ebXlW4PJltA15e2Ovuq4e5Jtl8WJUWbdvb93zNgWKiookODj498f5+flGr6empoqISHp6\nuqSkpMhsw5FRvzF//vzf7w8ePFgG1+aCQghxOE4juv7+SMkuXQqXJn9/R69IRaNBW05UFGwSFy9G\nr6yluLmhcOn//k/klVdETp1ClW9mJlLXBw5g+k+3biI//YS+2S5dIMI+PtjTnTEDlck3OwHo0CH0\nEI8bB9MOc/vCDaGoCJH23r3WPW9Toq70cIsWLSQ6OlpSUlIkOTlZ4moMQzYUXUKIc+M0oiuCSHLD\nBnVcnrMxejSEd/ZspHmXLcM+5s3g5oZzREXZZo0K1dX4AbNlCwqmJkywzXWWLsV/t1tusc35GwMJ\nCQkmzwUHB0tcXJxotVop+G2uZGFhoYSEhBgdFxISIhERESIiotVqZf/+/SaiSwhxHZxKdEVgHxgd\nLbJxo6nLkzPQvTuKl154AcPjp09HFOks5h4i2F995hmkqNeuxehCW5CfD1vLZctsc/7GgrmKZIVx\n48ZJamqqxMbGSlZWlgwbNkxEkHbWarUyevRo2bBhw+/P9e7d2y5rJoTYBqcopDIkMFDko48gZhcu\nOHo15vH1RYT36quoUI6LQ7rY0RQXi7z1Fvpl//QnCKKtBFcEVpmdO9s+am/MKIVRO3bsEK1WK3f+\nZlk2dOhQERGJiIgQrVYrycnJUlBQIA899JDD1koIaThO0TJkjiVLMDRgxw7rF/5YE50OdpYLFiDN\nOmECnJ7sGfnm5qr7qpGRKPwKC7PtNVNTYQBy/LjlrVTE+rBliBDXwmlFt7oa6eV27eqecesMlJXB\nRGPxYojQ+PFIO1viZlUf9HrYVW7Zgork8eNF/vY3+0SdVVX4YfHEE+j7JY6DokuIa+G0oiuCdGlM\nDAqXpk519Goso6oKbU+JiUg5R0Vh7u7AgUjFNiQCLilBNLt5M9ysTp5EGn7CBNuJuzni4zFUYd06\n61dDk5uDokuIa+HUoiuCgfAPPIB2l4EDHb2am+PqVThZbd+Oz3H8OHyJW7dGT3KbNriv1aI1SOnR\nLS3FxJ68PNgr/vwzjDYOHoSA33orUrvdutm/gCs9XWTECNhjduxo32sTUyi6hLgWTi+6IvDznTQJ\nf2+/3dGrqT/Z2bC8TE9HZXF6OjyX3dwwReiOO0SOHMHA+vPnURDl6YkIOToaldNeXo5bf2kp7Czn\nzsV/D+J4KLqEuBYuIboiIuvXw05x927MnCX2Ra/H9KT8fGQdnKlFqilD0SXEtXC6Pt3aGD8e/+AP\nHw5LxVatHL2ipsXSpSJff41RhxRcQgipHy4T6SrMny/yyy9oKQoNdfRqmgbffouxiz/+CK9o4jww\n0iXEtXC52tN582A5eDNTf0j9OXJE5JFH0DNNwSWEkIbhMullBY0GQwN8fTFAICWFVbS24sQJVCq/\n+y76cgkhhDQMl0svG7J4MUT3lVdQ+Uusx+nTyCYsWCDy2GOOXg2pDaaXCXEtXC69bMisWWhdGToU\nRT7EOmRlYYTh3LkUXEIIsSYuLboiqGrevBmOVStXorWF1J+MDJFBg1CwNn26o1dDCCGNC5dOLxuS\nmSny+ONoJXr/fZGAAEevyPXYuRM/YpYtExk71tGrIZbA9DIhroXLR7oKkZEYLB8QANekjAxHr8h1\n0OtFli8XWbRIJCmJgksIIbai0US6hqxbh4k7b72Fdhea8tdOaSnSyAcPiiQn23b+LrE+jHQJcS0a\npRw9/DDG3q1cKTJkCFpfiClHj4oMG4ZI98cfKbiEEGJrGqXoimCk3g8/iDz4IAYIvPmmSGWlo1fl\nHFRWirz+OgqmnnpK5MMPRfz8HL0qQghp/DTK9HJNsrIwcN3XV+S552Cq0VQ5fBiD54ODMbiAxiKu\nDdPLhLgWTUJ0RZBCTU7GpKLbbxd54w3MtG0qXLmC6HbzZux3T53KwQWNAYouIa5Fo00v10SjERk9\nGgPhhwzBbepUkXPnHL0y26LTiaxdi5m8OTki27ejtYqCSwgh9qfJiK6Ct7fIs8+iuKpVK7hZPfKI\nyKFDjl6ZddHpRFavhth+/bXIl1+KrFkj0rato1dGCCFNlyaTXq6NggKYaSxdKnLbbRDkESNE3N0d\nvbL68euvIqtWiXzzjYiHB5ylBg509KqIrWB6mRDXosmLrkJFhcj69TCHOHxYZOJE+Dp36+boldWN\nXi+ybx8MLjZtEnnoIZEZM0R69HD0yoitoegS4lpQdGug18PN6uOPRT79FFW+EyeKjBkD1ytnQa8X\nOXAAPxKSkpBGjo3FgIKQEEevjtgLii4hrgVF9wZUV4vs3i3yySciaWkiRUUQtthYFGKFhtp3PUVF\nIt99h3GGZ8+K/PILLBvHjhXp3p3FUU0Rii4hrgVF10L0epEjR0R27MBt1y6RkSNFPD3RgtStG/62\na2cd8SsqgmPUiRMw+di7V6RlS+zTDh2Kfedu3Si0TR2KLiGuBUW3nuh0SENnZGAP+PBhiLJGA3Fs\n00akdWvcwsMRNXt6qrfycvTOGt4uX8a0pDNnRCIicK4+fZA67tMH0ayHh6M/OXEmKLqEuBYUXSuT\nnw/RvHRJJDcXf/V6kfPnUayl04l4eeF+YKDxrVUrRMrh4RBuRrGkLii6hLgWFF1CXBiKLiGuRZMz\nxyCEEEIcBUWXEEIIsRMUXUIIIcROUHQJIYQQO0HRJYQQQuwERZcQQgixExRdQgghxE5QdAkhhBA7\nQdElhBBC7ARFlxBCCLETFF1CCCHETlB0CSGEEDtB0SWEEELsBEWXEEIIsRMUXUIIIcROUHQJIYQQ\nO0HRJYQQQuwERZcQQgixE80cvQBCmjrJycmi1WolMzNTpk2bdtOvE0JcB0a6hDiQAwcOiIhIbGys\niIikp6cbvZ6eni6RkZESGxsrkZGRJq8TQlwLii4hDiQpKUmaN28uIiKRkZGSkpJicswLL7wgIiKZ\nmZkSHR1t1/URQqwL08uEOJCioiIJDg7+/XF+fr7R69HR0RIRESHBwcGSkJBg9hzz58///f7gwYNl\n8ODBtlgqIcQKUHQJcTB6vb7W14qKiqRTp06SkJAg06ZNkx49ekhERIRF7yWEOB8UXUJsjLkINTg4\nWOLi4kSr1UpBQYGIiBQWFkpISIjJe5988kkJDAwUrVYrGzZskNmzZ9tl3YQQ60PRJcTG3KjieNy4\ncZKamiqxsbGSlZUlw4YNExFEuFqtVkREAgMDRQTFVpmZmbZfMCHEZmj0zE8R4lASEhIkMjLSqCWo\nV69ekpqaKiIi8fHxEhkZKQUFBWwZIsTFoegSQgghdoItQ4QQQoidoOgSQgghdoKiSwghhNgJii4h\nhBBiJyi6hBBCiJ2g6BJCCCF24v8BBiDuvbcyGUYAAAAASUVORK5CYII=\n", "text": "" }, { "output_type": "pyout", "prompt_number": 5, "text": "" } ], "prompt_number": 5 }, { "cell_type": "markdown", "metadata": {}, "source": "Compute and plot the leading order behaviour around $x=0$" }, { "cell_type": "code", "collapsed": false, "input": "ltc = series(fresnelc(x), x, n=2).removeO()\nlts = series(fresnels(x), x, n=4).removeO()", "language": "python", "metadata": {}, "outputs": [], "prompt_number": 6 }, { "cell_type": "code", "collapsed": false, "input": "lts, ltc", "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 7, "text": "(pi*x**3/6, x)" } ], "prompt_number": 7 }, { "cell_type": "code", "collapsed": false, "input": "figsize(4,4)\nplot(fresnels(x), lts, (x, 0, 1))\nplot(fresnelc(x), ltc, (x, 0, 1))", "language": "python", "metadata": {}, "outputs": [ { "output_type": "display_data", "png": "iVBORw0KGgoAAAANSUhEUgAAAQQAAAD7CAYAAACMu+pyAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAG8tJREFUeJzt3Xt41NWdx/F3vLTenUyoXXerLYPgFtttIoKX7qMjk3hb\nq5Ygcdv1stpg++hWa5uCrq6h3pBIRV1EM3gpaGVDRq23IsyorVQUwowVuXhhRhHtimYycVUUIdk/\nvmQkJMwkmctvJvm8nofHzJxfZr7EyZdzzu97zinp7OzsREQE2M3pAESkcCghiEiSEoKIJCkhiEjS\nHukuCAQCuFwuotEotbW1PdrD4TCxWIx4PN5ru4gUj5Q9hHA4DIDP5wMgEon0uGb69OlUV1eTSCR6\nbReR4pEyITQ1NVFaWgqAx+MhGAx2a29ubmbs2LEA1NXVUVFRkaMwRSQfUiaERCKB2+1OPm5tbe3W\n3tLSQmtrK5FIhIaGhtxEKCJ5k3YOIV3d0rBhw6ioqCAYDBIIBKiurk62lZSUcO211yYfe71evF7v\nwKMVkZxKmRBcLhfxeByAtrY2ysrKurWXlZUxfPjw5LUrVqzolhAA6uvrsxiuiORSyiFDTU0N0WgU\ngFgsRlVVFWBDCYCJEycm2xOJBOPGjctlrCKSYykTQtckYSgUwuVyUV5eDkBlZSUAw4cPx+VyEQgE\niMfjTJgwIcfhikguleRycVNJSUnaOQgRKRyqVBSRJCUEEUlSQhCRJCUEEUlSQhCRJCUEEUlSQhCR\nJCUEEUlSQhCRJCUEEUlSQhCRJCUEEUlSQhCRJCUEEUlSQhCRJCUEEUlSQhCRJCUEEUlSQhCRJCUE\nEUlSQhCRJCUEEUlSQhCRJCUEEUlSQhCRJCUEEUlSQhAZZF57beDfq4QgMsjcc8/AvzdtQggEAoRC\nIfx+f6/tU6ZMAdhlu4jkzxdfwLx5A//+lAkhHA4D4PP5AIhEIj2u8fv9jBw5khEjRgw8ChHJiiee\ngFGjBv79KRNCU1MTpaWlAHg8HoLBYI9r/H4/b7zxBuPHjx94FCKSFffcAxddNPDvT5kQEokEbrc7\n+bi1tbXHNfF4nFAoRENDw8CjEJGMbdwIL7wAEycO/DX2SHdBZ2dnyvba2loAlixZQigUSg4vutTX\n1ye/9nq9eL3e/kcpImndfz9MmgT77jvw10iZEFwuF/F4HIC2tjbKysq6tfv9ftxuN9XV1ZSVlRGN\nRlMmBBHJjY4OuPdeaGrK7HVSDhlqamqIRqMAxGIxqqqqABtKgM0rVFZWAjacGDt2bGbRiMiAPPss\n7L8/jBmT2eukTAgVFRUAhEIhXC4X5eXlAMkk4PP5CAaDBAIBhg0blmwXkfyaNw8uuwxKSjJ7nZLO\ndJMEmbx4SUnaOQgRycymTXD44RCLgcuV2WupUlGkyN1/P/zwh5knA1APQaSodXTAyJHw0EMwblzm\nr6cegkgRC4XggAMgW/P5SggiRezuu+HiizOfTOyiIYNIkfrb32D0aHj7beslZIN6CCJF6t574eyz\ns5cMQD0EkaK0bRuMGAGBQObFSDtSD0GkCP3xj3D00dlNBqCEIFKU7rwTTjkl+6+rIYNIkVm/Ho45\nBjZsgL33zu5rq4cgUmTmzIELLsh+MgD1EESKyqefwqGHwvLl4PFk//XVQxApIgsW2GRiLpIBKCGI\nFI3OTpg9Gy65JHfvoYQgUiReegkSidzcXeiihCBSJGbPhp/9DHbL4W+tJhVFisCmTVBeDq++Cjts\nhJ516iGIFIHGRjjjjNwmA1APQaTgbdkCw4fDokXw3e/m9r3UQxApcAsXwre/nftkAEoIIgWtsxNm\nzYLLL+/796xePfD3U0IQKWDLltmtxtNO69v1nZ2Z1SkoIYgUsFmz4Oc/7/utxgULoL194O+nSUWR\nArVhA1RUwFtv2alM6Xz0kc01LFwIxx03sPdUD0GkQM2eDeef37dkADBtGpx88sCTAfTh9GcRyb9P\nPrE9E5cv79v1q1fbn3nzMntf9RBECtD8+fDP/2z1B+l0TSSecQYcdFBm76segkiB6eiA226Du+7q\n2/UPPWTzBxdfnPl7p+0hBAIBQqEQfr8/5XUNDQ2ZRyMiLFoE3/kOHH98+mvb26GuzvZY3H33zN87\nZUIIh8OAHfsOEIlEer0uGAyyZMmSzKMREW65Bc46q2+nMdXXw6mn2h6L2ZAyITQ1NVFaWgqAx+Mh\nGAz2el1Jts6REhniVq6EN9+ESZPSX/vKK/Dgg3DTTdl7/5QJIZFI4N5heVVra2uPayKRSLIHISKZ\nueUWuOwy2HPP1Nd1dNhE4m9+A1/7WvbeP+0cQrrCong8nrVgRIayt96CxYuhtjb9tfPnwz/8Q9+u\n7Y+UdxlcLlfyF76trY2ysrJu7X3pHdTX1ye/9nq9eL3egUUqMsjddhtcdFH6sxoTCZg6FR57LDsT\niTtKmRBqampoaWnB5/MRi8WoqqraHlACl8tFNBolGo3S2tpKPB4nEolQUVHR7TV2TAgi0ru2Nvjd\n72xeIJ2rr4Yzz4SxY7MfR8ohQ9cvdygUwuVyUV5eDkBlZSUA1dXVVFdXU1JSQnt7uyYXRQbI74fT\nT4dvfCP1deEwNDfDjTfmJg4tbhJx2GefwahR8NRTVn+wKx0dtk5h8mS48MLcxKLSZRGH3X8/fO97\nqZMBwD332DLoCy7IXSzqIYg4aOtW6x3Mnw/f//6ur/vwQ6istOSxfeSeE+ohiDioqcnmDVIlA7C7\nCl5vbpMBaHGTiGM6O2H6dLj55tTXvfAC/PGPsHZt7mNSD0HEIU8+aXUEqY5m27rVTmuaOTN9fUI2\nKCGIOKCz09YgTJ2aehHTHXdYaXJNTX7i0pBBxAFLl8L770N19a6vefdduOEGGzLkq8RHPQQRB9xx\nB/z617BHin+SZ860BUyjRuUvLvUQRPJs5UpoaUm9/+GiRfDoo5kdujIQ6iGI5Nl118EvfgF77dV7\n++bN1jP47/+GvffOb2wqTBLJo5dftlOY1q/f9S/7NdfAunV2vkK+KSGI5NHEibYe4Yorem9ft852\nW/7rX22/g3xTQhDJk1dftfLj9eth3317tnd22l6K48fbrklO0ByCSJ5cf731DHpLBmD7I8bjmR3W\nmin1EETyYO1aOOEEiEZhv/16tre2whFHwOOP52bjk75SQhDJg3/7Nxg9Gq66qvf2n/wE9tkHbr89\nv3HtTHUIIjm2bh28/rod3tqb55+3uoM1a/IbV280hyCSY/X1MGECHHhgz7bPP7eahNtuy8/ipXQ0\nZBDJoVdegZNOssNXeps7uP56q0b8/e/zt14hFSUEkRw66yybTPzFL3q2vf661SSsXAnf/Gb+Y+uN\nEoJIjrS0WEJ4442eVYmdneDzwQ9+0HuycIrmEERy5Jpr7K5CbyXKv/udndz8H/+R/7hSUQ9BJAf+\n8hf48Y/htdfgq1/t3rZpE3z3u7Yt2pFHOhPfrighiOTA+PFWe9Db+Qk//jH84z9aD6LQqA5BJMue\neQbeeQfOO69n26JFtgNSY2P+4+oLJQSRLOrstLqDm2/uuRvSxx/DT39qyWBX6xmcpklFkSx6+GH4\nv/+zuws7u+YaOP54q0soVOohiGTJ1q12V+H22+3ItR0tXw4PPWRLoAuZeggiWXLvvXYK0849gC1b\nbBhx++0wbJgjofVZ2h5CIBDA5XIRjUapra3t0d7c3ExpaSkLFy7krrvuykmQIoXu009h2jTbGHXn\nEuSbb7Yew9lnOxNbf6TsIYTDYQB8Ph8AkUikW3soFCIUCuHz+YhGo7z88ss5ClOksN12m53PuPNe\nBmvWWM9gzpzCWKuQTsqE0NTURGlpKQAej4dgMNit3efzMWfOHADi8TjluT6JUqQAffihnaFwww3d\nn9+2DS66yHZZPuQQZ2Lrr5RDhkQigdvtTj5ubW3tcU17ezuNjY1ceeWV2Y9OpAhcf72dvzhyZPfn\n586Fr3wFJk92Jq6BSDuHkK7S8MADD6Suro6TTjqJI488kuHDh3drr6+vT37t9Xrxer0DClSkEL3x\nBjzwQM/NTdavtzsOL77Y845DIUuZEFwuF/F4HIC2tjbKysq6tYfDYUpKSqioqODII4+kubmZurq6\nbtfsmBBEBpspU6CuDg466MvnOjpsqHDVVT17DYUuZe6qqakhGo0CEIvFqKqqAmwoATap2JUwEokE\nI0aMyGWsIgXlT3+CcLjnlulz5thOSJdf7kxcmUiZECoqKgD7xXe5XMlJw8rKSgAmT55MNBrF7/dT\nWlrKhAkTchyuSGHo6IBf/hKmT+9+JFssBtdeC/fdB7vv7lx8A6XVjiIDMH++bZq6bNmXtxM7O+H0\n0608ecoUZ+MbKJUui/RTVxHSvHndawu6VjD+8pfOxJUN6iGI9FN9vS1vvueeL5+LxWDcOJtXGD3a\nsdAypoQg0g9vv227HEUicOih9lxHh53ZeOqpdsehmBXRHVIR5/3qV3ZXoSsZgN1V+OyzXZ/oXEw0\nhyDSR88+CytW2NxBlzfftA1TH3igOO8q7Ew9BJE+2LrVegYzZ365i/K2bXDBBfCv/wqjRjkaXtYo\nIYj0wd13214GO5bazJwJe+7ZszCpmGnIIJLGBx/Ak0/CrFlf3mZctQoaGuwwlmJaq5DOIPqriOTG\nlCm2bfo//ZM93rIFLr0UZswonCPYskU9BJEU/vIXWLy4+2rG//ov+Pu/t/mDwUYJQWQXtm61fQ5m\nzvzyqPY//9nuMrz8cnHsgNRfGjKI7MIdd8DXvw6TJtnj9nY7fMXv777ceTBRpaJIL959F773PRsy\nHH64PXfeeXbAyvZdAwclDRlEenHddVaV2JUMFi6El16y/Q8GMyUEkZ089pidz/jKK/b4nXfgt7+1\nJc+FegRbtighiOzgo4/gkkvsl3+vvawa8dxz4cwzbTXjYKc5BJEdXHqpLVSaO9ce33ADhEKwZMng\nWKuQjnoIItstW2aHta5ebY9ffNEOWVm5cmgkA9BtRxHAqg9ra608ubTUbjH+6Edw1112XuNQoSGD\nCHZXYflym1AE2wbt008tIQwlGjLIkLd6NSxdavMGJSV2ivPTT9veB0ONEoIMaVu32pqE2lo7f3HN\nGlvM9NxzsM8+TkeXf5pDkCFtxgybM6ittSHCpEl21sIRRzgdmTM0hyBD1quvwokn2l2EQw+1Q1k/\n+cS2QxuMC5f6QkMGGZK++MKGCjfeaMngwQehtRXuv3/oJgNQQpAhatYsKCuDn/wE1q61cxiDQdh/\nf6cjc5aGDDLkhMNWivzCC+B2W0nyFVfYic1DnRKCDCmbN8OYMXD11bZb8nnnWRXiffcN7aFCl7RD\nhkAggMvlIhqNUltb26Pd7/cDsH79eqZPn579CEWyaMoU2+fgRz+yo9heftmWNSsZmJS3HcPbF3/7\nfD4AIpFIt/ZQKERlZSW1tbVEo1FCoVCOwhTJ3OLF8OijcOedVpV4443Q3Dw06w12JWVCaGpqorS0\nFACPx0MwGOzWHo1Gk895PB6i0WiOwhTJzIcf2hzBffdZMdLZZ8Mtt3y5AYqYlEOGRCKB2+1OPm5t\nbe3WvuMQIhwOc84552Q5PJHMdXZa4dFPfwonnAAnn2xDhh/+0OnICk/aOYS+TAqGw2HGjBlDeXl5\nj7b6+vrk116vF6/X268ARTI1Z46d2rxgAfznf9rBKtdf73RUhSllQnC5XMTjcQDa2tooKyvr9bpQ\nKMRNN93Ua9uOCUEk31atgmuvtc1SFy2C//kfO21pqOxv0F8p5xBqamqS8wKxWIyqqirAhhJdGhsb\nqaurA9CkohSUTz+FmhqbK/jsM7j4YnjkETujUXqXMiFUVFQA9ovucrmSQ4LKykoAgsEgU6dO5bDD\nDsPtdlOiezdSQK67Do47Dk47zQqRbr0Vtn+kZRdUmCSD0vz5Nk+wbBlUV8Mxx8AuRrWyAyUEGXTW\nrLG7Cc88Yzsevf02/OEPmjfoCyUEGVQ++QTGjoW6OlvReOuttlnqgQc6HVlx0AYpMmh0dsLUqXD0\n0XZMu99vPQMlg77T8mcZNGbPtt5AYyOccorVHYwa5XRUxUVDBhkUli61ycMnn7RVjFOnajnzQCgh\nSNF77z2bN5gzB2bOtCHDjBlOR1WclBCkqH3+ue2FeNhhtgVaPG7boO2m2bEB0RyCFK3OTluw9PHH\nVon44ot2q1HJYOD0o5OiNWsWRCLg9doahccf194GmdKQQYrS00/Dv/+7bXJy5ZXw/PM2bJDMaMgg\nRWfdOjj3XDuq/d57bcGSkkF2qIcgReWDD+DYY+FnP7NVjHffDWec4XRUg4cSghSNzZvB54OjjrJT\nmuvr7bAVyR4lBCkKHR1wzjmwbZstXrrwQluvINmluwxSFGbMgI0b4atfhR/8QMkgV5QQpODdfrvt\nlrzbbrDffnDzzU5HNHhpyCAFbcEC6w2MHAkHHwzz5mlfg1xSQpCCFQzaduljx8Kee8LChfZfyR0N\nGaQgrVgBv/2tHbt20EHWU1AyyD0lBCk4q1bZxOHmzfb4zjthr72cjWmo0JBBCsrrr8OJJ8KIEdYj\n0PqE/FJCkILx9ttw/PEwerQta37iCSWDfNOQQQrCxo1w+eVQVmabo6pn4AwtbhLHbdxoS5j32ss2\nR21uhr33djqqoUk9BHFUVzLYc0/bEPWRR5QMnKSEII7ZuNHmDPbYA6qqbJOTr3zF6aiGNiUEcUQ0\nCmefDVu2wPjxdqCK6gyclzYhBAIBQqEQfr9/l9dMmTIlq0HJ4LZ6NXz/+7B+vW2VPnu2ypELRcqE\nEA6HAfD5fABEIpEe1zQ2NhIIBHIQmgxGLS127uI++8DVV8O0aaBDwwtHyoTQ1NREaWkpAB6Ph2Aw\n2OOayZMn4/F4chOdDCp/+pPNFXzxhZ3E/POfOx2R7CzlbcdEIoHb7U4+bm1tzXlAMjj94Q/WIwDb\n7eiEE5yNR3qXtg5BlYaSqdmz4aqrbJiwdCkccYTTEcmupEwILpeLeDwOQFtbG2VlZf1+g/r6+uTX\nXq8Xr9fb79eQ4tTRAb/+tR2++q1v2dbpBx/sdFSSSsqEUFNTQ0tLCz6fj1gsRlVVFWBDCZfL1ac3\n2DEhyNCxebOdqvToo7Yx6u9/r4KjYpByUrGiogKAUCiEy+WivLwcgMrKyuQ1zc3NtLS0MHfu3ByG\nKcXkf//XDlxtbraJw4cfVjIoFlrtKFn1179aj2DzZnjwQTjrLKcjkv5QpaJkzUMPwamnWsXhSy8p\nGRQjJQTJ2LZtcMkldtbiqFF21Np3vuN0VDIQSgiSkQ8+gJNOggcegCuusOPYDzzQ6ahkoLQfggzY\n88/D6adbD+GRR6wKUYqbegjSbx0dcN11Nnk4YoQtUlIyGBzUQ5B+ef99mzhctQp+9Ss7kn03/bMy\naCghSJ8FAnDeeXa+4vPPwzHHOB2RZJtyu6T18cc2V1BTA6ecAu+8o2QwWKmHICk98wxUV9uS5UAA\nzjzT6Ygkl9RDkF598gmccYZNFp54Irz7rpLBUKAegvSwcKEVGe22m/UKVHE4dKiHIEnvvQfHHgvn\nnGNnK27apGQw1CghCNu2wdSpcOihlgSWLbN1CTpgdehRQhjiHn4Yhg2DWbNg+nR4800YN87pqMQp\nWv48RL3yig0NWlttS/R582C//ZyOSpymHsIQ89578C//AuXlVmD05z9bL0HJQEAJYchIJGDCBDjk\nEIjFYNEiiETg8MOdjkwKiRLCIJdI2NDga1+D5cth/nxYs8aWLIvsTHUIg9SmTbbJ6dNP2/bnd99t\ntQU6JUlSUQ9hkFm71lYjHnwwhMNw5522icmFFyoZSHpKCIPEE0/YROERR9gx6489Bm+9Beef73Rk\nUkyUEIrY5s1QVwelpbbO4O/+znY9XrXK7iSI9JfmEIrQc8/ZOYlvvGFJ4fzz4cYbYf/9nY5Mip16\nCEXivfdg8mT45jdh/HhbjTh7Nnz0Edxxh5KBZId6CAWsvR1mzLANTNeuha9/3SYHp0zRzsaSG0oI\nBeb99+HWW+Hxxy0JHHCAFRQ98oiKiCT3lBAKwNKlMHcuLF4Mf/sbuN126/DBB+3OgUi+aHGTAzZs\nAL8fnnrKqgY//9xOPDrtNLjsMpsnEHGCEkIerF1rJcPPPguvvQZtbdYLOPZY27j0nHPsPEQRp6W9\nyxAIBAiFQvj9/gG1F5vnnnsuo+/ftMn+9a+uhtGjbZOR0aPtObcbfvMbW1/Q2mrFROeem1kyyDRe\nJxRbzMUWLww85pQJIRwOA+Dz+QCIRCL9ai9Gff1BbtkCS5bAlVdaEdDIkbac+OCDbfehdevguONg\nwQIbEnzwATz5JFx6aXbvEAylD6tTii1eGHjMKScVm5qaOGn7sjiPx0MwGKSioqLP7cWuvd1WCK5c\naV39116zcuB4HHbf3X7R3W749rfh5JNtd+JTToF993U6cpGBSZkQEokEbrc7+bi1tbVf7YWoo8Nq\n/aNR+7Nhg/2Sv/++Pb9hAzQ02D6Dn38Oe+xhRT+HHALf+IYdUHL00eD1Wl2AyGCS9rZjuknBdO0l\nRbnEblryq61bbRKwrc22HXvqKQfD2oVp06alv6jAFFvMxRYvQH19fb+/J2VCcLlcxONxANra2igr\nK+tXu+4wiBSXlJOKNTU1RKNRAGKxGFXbz/xOJBIp20WkOKVMCF0ThKFQCJfLRfn2srnKysqU7ZIb\nfb3F29DQkKeIpFBMmTJll239KQ3IWmFSIBDA5XIRjUapra3td7sT0sXU9QNcv34906dPz3d43YTD\nYWKxGNXV1fj9fo466qhe7+gEg0FmzJjB4sWLHYiyp3Q/466/VzweL5rPRaF9lhsbG5kxYwZvvvlm\nj7a+fm66ZGX5czHWK6SLKRQKUVlZSW1tLdFolFAolPcYd9TU1ERpaSnw5S3e3hTSJG5f/r9Pnz6d\n6upqEolEUXwuIpEIHo8Hn8+Hx+MpiJgnT56Mx+Ppta2vn5suWUkI6d60v0HlQ7qYotFo8jmPx5Oc\nK3FKX27xRiKR5Ae5EKT7GTc3NzN27FgA6urqCqKGpS+f1a7ueTQaLYiYU+lvaUBWEkIx1iuki6m2\ntjbZHQyHw8kPrpPSje667vgUinQ/45aWFlpbW4lEIgUz75Eu5oqKCoYPH47b7e52XSHrz6xA1nZM\nyrRewQl9iSkcDjNmzBjHJ0zT3eIttN5Bl3Q/42HDhiX/lQ0EAvkIKa1UMScSCQ477DD8fj+1tbXE\nYrE8RtZ/6T43O8tKQsi0XsEJfY0pFApx00035TO0XqW7BRyNRgkEAjQ2NhKPxwtibJvuZ1xWVsbw\n4cOT165YsSLvMe4sXcx+v5+LL76Y6upqFi5cSHNzsxNhpjXQ0oCsJIRirFdIFzPY7G1dXR2A45OK\n6W4BV1dXU11dTUlJCe3t7QUxuZjuZzxx4sRkeyKRYFwBHDvdl8/FAQccANjEo8vlyn+QO2lubqal\npYW5c+cmnxtoaUDWbjv6/f7k5FvX2Puoo46ipaVll+1OSxVzMBhk0qRJuN1u4vE4zc3NjB8/3uGI\ni09fPhdut5uWlpaC6IlB+pgbGhrweDwFdas0W3K6QYqIFBdtwy4iSUoIIpKkhCAiSUoIIpKkhCAi\nSUoIIpL0/72sxsqe951gAAAAAElFTkSuQmCC\n", "text": "" }, { "output_type": "display_data", "png": "iVBORw0KGgoAAAANSUhEUgAAAQQAAAD7CAYAAACMu+pyAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAGJtJREFUeJzt3XtwXHX9xvF3KHeGzummLSIIZHsBSmmTNi0gFJduwkUG\nuQQbEEGQSasiiCOxiDIEfkBrowzjOArd6ijMqBOzIJdBLrsWKCCXsAtiSUvbXS6thZ5msxUGkDrm\n98eXXTZtupvL7p5zdp/XjGPTsw0fwjlPP9/LOadmYGBgABERYC+nCxAR91AgiEiWAkFEsoYVCEuX\nLt3jsXA4TDQaJRQKFa0oEXFGwUBYuXIl4XB4yGOxWAyAYDAIQDweL2JpIlJuBQNh8eLF+P3+IY91\ndXUxYcIEAPx+P5FIpLjViUhZjWkOIZ1O4/P5sl/39fWNuSARcc6YJxW1jUGkcuw9lj9sWRapVAqA\n/v5+amtrBx2vqanhpptuyn4dCAQIBAJj+UeKSAmNKhDS6TSWZdHa2kpPTw/BYJBkMklzc/Nun+3o\n6BhrjSIyArYNkyaN7s8WHDJ0d3fT09PDqlWrsr/X1NQEQENDAwDRaBTLsqivrx9dFSJSFLYNny76\njUpNKe9lqKmp0RyDSJlkwuArX4Fbbx3d99BORZEKkBsG//d/o/8+6hBEPG77drjiCpgzBzo6oKZm\n9N9LHYKIh23fDgsXwqxZYw8DUCCIeFYmDM45x8wZjDUMQIEg4kmlCAPQHIKI59g2XH451NcXNwxA\nHYKIp2RWExobix8GoEAQ8YzcpcViTCAORYEg4gG77jMoRRiA5hBEXM+24eqrYerU0oYBqEMQcbVM\nZzBlSunDABQIIq61670JpQ4DUCCIuFK55gx2pTkEEZexbXNvQmMj3HRT+cIA1CGIuEqmM6ivL38Y\ngAJBxDWcGibk0pBBxAVsGxYtgpNPdi4MQB2CiOMynUEw6GwYgAJBxFG5w4Qf/9jZMAAFgohj3DBn\nsCvNIYg4wLbhkkvglFPgxhvdEQagDkGk7DKdwfz57goDUCCIlJUbhwm5FAgiZbJtGzQ1uTcMYIzv\ndhSR4bFtEwaXXQY/+IE7wwDUIYiUXO4wwc1hAAoEkZJy+5zBrjRkECmRTBhceilcd537wwDUIYiU\nRG5n4JUwAAWCSNF5bZiQS4EgUkReDgPQ1mWRorFt+O53Ydo0b4YBqEMQKYpMZ+DlMAAFgsiYeX2Y\nkEuBIDIGlRQGoEAQGbVKCwPQpKLIqJTz9WrlVHCnYjgcxrIsEokEbW1tIz4uUmkyncE551RWGECB\nIUMsFgMgGAwCEI/HBx2Px+P4/X6CwSB+v3+34yKVxonXq5VT3kDo6upiwoQJAPj9fiKRyG6fWbp0\nKQCJRIKGhoYSlCjiDpU4Z7CrvIGQTqfx+XzZr/v6+gYdb2hooK6uDp/PN+hzIpXGtuG88yo7DGAY\ncwj5JgXT6TRTp04lFArR1tbGnDlzqKurG/SZjo6O7K8DgQCBQGDUxYo4Yft20xm0tsINN1RuGECB\nQLAsi1QqBUB/fz+1tbWDjodCIZYsWcL48eOxLIvu7m7a29sHfSY3EES8Zvt2WLjQTCBWehhAgSFD\na2sriUQCgGQySXNzM2A6g4zx48cDZuLRsqxS1SlSdrlhUIkTiEMpuA8hFArh9/sHLSs2NjbS09MD\nQGdnJ36/n1Qqtduyo/YhiFdt3w4XXAALFlRPGIA2JonsJtMZLFrkjterDcfHH0N/v/n/Tz6Bo48e\n3ffRI9REcuQOE9wSBgMD8N570NsLGzfC+vXwzjvw9ttwyCHw+OPw3//CqafCpk2w777mM6OhDkHk\nU26ZM7BteOEFeO45ePllWLsWdu6E6dPh2GPB74cjj4QjjoDDDoNJk+DAA4tTrwJBBGfDYMcOWL3a\n/E2fTMLf/w6nnQazZ8PcueZ/n/98eWpRIEjVs2245hrzN2+5wiCRgL/8BR55BF55BebMgdNPNy9z\nOf54GDeu9DUMRYEgVS13O/Itt8BeJXwgwNtvwx//CD09sGaN6UbOPdd0JgceWLp/7kgoEKRqlePe\nhPffhwcfhLvvhtdfh5YWuPhis5zpVBeQjwJBqlIpw2BgwHQBK1dCdzeceaYJgTPPNCsAbqZlR6k6\ntg1XXWXa9VtuKV4YfPIJdHXBL39plgFbWkxXcOihxfn+5aAOQapKKTqDVAruvRd++lOYMQOuvRa+\n/OXSzkeUijoEqRrFDoPNm+HOO+G3vzW3Rj/2mFkh8DIFglSFYobB5s2wbJnZDThrFrz6KnzhC8Wr\n1UkebGpERsa24cor4fzzxxYGW7ea/QqzZ8NBB8Ef/gB33FE5YQAKBKlwmc5g1izo6BhdGKTT8KMf\nwcyZMHmyuadgxQrz60qjIYNUrLEOEz75xKwY3HMPNDaaHYWV1A0MRYEgFWksYTAwAA8/DN//Phxz\njBkazJhRulrdRIEgFce2obl5dGHw2mvwve+ZJcO77jL3FlQTzSFIRcl0BpdcMrIwSKfNJqVg0Gwo\nevTR6gsDUCBIBckdJlx33fDCYGDgsyHBtm1mZ+FVV8HeVdo7V+m/tlSa0cwZbNhgguOtt+C+++DE\nE0tfp9upQxDPs21z89BXvzq8MNi5E5Yvh5NOgrPPNjciKQwMdQjiabmdwU9+UjgMYjGzSWnyZBME\nRx1VljI9Qzc3iWeNZJjwn//AzTebx5N985vw9a+74wGqbqNAEE/K3MI8fXrhMHj5ZfjGN2DaNLOU\neMgh5avTazSHIJ6T6QwKhcHOneZeg7POMluP77tPYVCI5hDEU4Y7TFi/3gwLJk6EeNw8rlwKU4cg\nnmHbcPnl5hVrewqDgQFYtQpOPtnMFTzyiMJgJNQhiCfkdgY33TR0GNi2CYFx48xLTqZPL3+dXqcO\nQVxvOMOESATq682Ow64uhcFoaZVBXM224VvfMhf6UA9E3bkTOjvhV7+C3/2uOu8/KCYNGcS1dn2J\nyq5h8NZbcNFF4POZicNJk5yps5JoyCCuVGiY8PDDMH++uTPxoYcUBsWiDkFcZ9s20/oPFQY7d8IN\nN8DTT8MDD+gehGJTIIir2DaccYbZQ9DePjgMtmwxQ4SDDzbLibW1ztVZqTRkENfIDBPOPnv3MFi9\nGk45xbwO7eGHFQalog5BXGFPcwYDA/Czn8HPf27enHzaac7WWekKBkI4HMayLBKJBG1tbbsdj8Vi\nJJNJUqnUkMdFCtlTGLz/PlxxhXmN+osvwhFHOFtnNcg7ZIjFYgAEg0EA4vH4bp9Zvnw5LS0tpNPp\nIY+L5GPb8J3v7B4GGzaYCcPp080EosKgPPIGQldXFxMmTADA7/cTiUQGHe/u7mbevHkAtLe309DQ\nUKIypRJlOoOjjx4cBo88Yu5FuOYauP122H9/Z+usJnkDIZ1O4/P5sl/39fUNOt7T00NfXx/xeJzO\nzs7SVCgVaahhwsCAeWdiWxvcfz8sWeJ0ldWn4BxCoa3HEydOpKGhgUgkQjgcpqWlZdDxjo6O7K8D\ngQCBQGBUhUrlGCoMPvzQvA8hmTTzBbpD0Rl5A8GyLFKpFAD9/f3U7rLWU1tbS11dXfazL730Ut5A\nEMmEwaWXfvao9M2b4dxz4bjjzJKihgjOyTtkaG1tJZFIAJBMJmlubgbMUALgwgsvzB5Pp9PMnz+/\nlLWKxw313oTnn4cTToDWVvj97xUGTssbCJlJwmg0imVZ1NfXA9D06S1ldXV1WJZFOBwmlUpxwQUX\nlLhc8aqhhgn33mu+vvtu+OEP9dBTN9Dtz1Jytg1XXw1Tp5owGBiAG2+EtWvhttvMUEHcQTsVpaQy\nncE555gw+Ogj8wTkrVvNSoLuUnQX3csgJZM7TLj1Vnj3XfjSl8w8QTSqMHAjBYKUxLZtg+cM/vEP\nOP10s5pwzz2w335OVyhD0RyCFJ1tw/nnm/ch3HADPPYYXHYZ/PrX5oEm4l7qEKSoMsOEQMCEwcqV\n5tHp99+vMPACTSpK0eTOGXR0wPXXmyB45hmzwiDup0CQorBtc+/BeeeZ16ZdcgkccIB5uaoeZuId\nGjLImGU6gxkzzP0IZ5xh9hrcdZfCwGsUCDImucOEK680jzmbNw/+9CdtQ/YirTLIqNk2fPvbcOyx\nZlXhxhtNd3DNNU5XJqOlDkFGJdMZHHMMLFhgHn565ZUKA6/TpKKMWO4wYfp0cyvzffeZ4YJ4mwJB\nRsS2zWPSL77YvGW5u9s8In3GDKcrk2LQHIIMW+6NSh9+CE88AY8+Cocf7nRlUizqEGRYcl+ikkiY\npxytWQOfPoNXKoQCQQrKhMGZZ8L27eYW5scfNxuPpLJolUHysm1zl+L555u5AjDzBgqDyqRAkD3K\ndAannGI2Gp1+urlZaW/1lRVLk4oypEwYfPGL5lkGixbBtdc6XZWUmgJBdmPbsHgx+Hzmseh33GFu\nVpLKpyGDDJLpDA48EB58EH7zG4VBNdFoULJytyM/+aTZfbhggdNVSTlpyCCAeQZiU5N5y3IsBn/9\nK8ye7XRVUm7qEATb/iwMXnsNnnoKpk1zuipxgjqEKpcZJhx8MPT1QSSircjVTJOKVSwTBvvua962\nvGaNwqDaqUOoUpm7Fj/6yNyP8PDDMH6801WJ09QhVCHbhoUL4YMPTEfw6KMKAzEUCFXGtuG008zt\nywsXwgMPmD0HIqAhQ1WxbfMClX//26wqrFplHnIikqEOoUrYNrS1mZWE884zOxAVBrIrdQhVwLbN\nW5c/+cTcpHTbbVBT43RV4kbqECqcbcOpp8LWraZDuP12hYHsmXYqVjDbNs8yePdd80p2PSJdClEg\nVKhMZ/Cvf8Gdd5p3JogUUnDIEA6HiUajhEKhvJ/r7OwsWlEyNrZtHmzywQdw990KAxm+vIEQi8UA\nCAaDAMTj8SE/F4lEeOKJJ4pcmoyGbcNJJ5nO4Be/gK99zemKxEvyBkJXVxcTPn3Ott/vJxKJDPm5\nGs1SuYJtw4knmgnE7m7zYFSRkcgbCOl0Gp/Pl/26r69vt8/E4/FsByHOsW044QSzgvDQQ3DWWU5X\nJF5UcFKx0D6CVCpVtGJkdGzbvILdtuGxx/SORRm9vIFgWVb2gu/v76e2tnbQ8eF0Bx0dHdlfBwIB\nAoHA6CqVIdk2NDaaF6isXg3z5ztdkXhZ3kBobW2lp6eHYDBIMpmkubkZMEMJy7JIJBIkEgn6+vpI\npVLE43EaGhoGfY/cQJDism2YOxdSKXj2Waivd7oi8bq8cwiZizsajWJZFvWfnnFNTU0AtLS00NLS\nQk1NDTt27NDkYhllnmewzz7wwgsKAykO3cvgQbZtAuCDD+DFF+Hoo52uSCqFdip6jG2bpyF/+KF5\nOvKUKU5XJJVEgeAhmScdffQRvPoqHHmk0xVJpVEgeIRtw/HHm6cbvfaaHoYqpaE5BA/Yts2Ewf/+\nZ168euihTlcklUodgstt2wYzZ5odiGvXwuTJTlcklUyB4GLbtpknHe21F/zznzBxotMVSaXTE5Nc\nats2mDEDPv4YXn9dYSDloTkEF3rvPRMGBxxgOgPLcroiqRYKBJd57z1zP8LAgAkDvUBFyklDBhd5\n912zmpCZQFQYSLkpEFxi61Y49tjP9hkcfLDTFUk1UiC4wNatcNxx5qWra9cqDMQ5CgSHbdliOoOJ\nE00YHHSQ0xVJNVMgOGjLFrOaMGWK2YF4wAFOVyTVTqsMDtmyxdy16POZMNh/f6crElGH4IjNm80w\nYfJkM4GoMBC3UCCU2TvvmKXFww6DV16B/fZzuiKRzygQyuidd2DOHDjiCPM8g333dboikcEUCGXy\n9ttmAvFzn4OeHoWBuJMCoQzeesvsMzjySPPYs332cboikaEpEErszTdNGBx1lJkzUBiIm2nZsYTe\nfNO8hfnQQ82j0vfW0yfE5dQhlMibb5onHU2apDAQ71AglEAyaYYJU6eaOQOFgXiFhgxFlkya5xkc\nfrhZTRg3zumKRIZPHUIRJRJmn4HCQLxKgVAkiYTZgVhXBy+9pDAQb9KQoQgSCZg3zywtvviiwkC8\nSx3CGCUSZjXhqKPUGYj3KRDGYONGM2cwe7YJg7300xSP0yk8Sps2waxZ5v6EZ59VGEhl0Gk8Chs3\nmgnE446DZ55RGEjl0KTiCG3YYPYZTJsGzz+vMJDKotN5BDZsgLlzFQZSuXRKD9Mbb5g5g5kzFQZS\nuXRaD8Mbb0B9vVlN0JyBVDKd2gWsX2+CYN48eO45hYFUtoL34YXDYSzLIpFI0NbWttvxUCgEwKZN\nm1i+fHnxK3RQpjM48USIRhUGUvnynuKxWAyAYDAIQDweH3Q8Go3S1NREW1sbiUSCaDRaojLLL9MZ\n1NcrDKR65D3Nu7q6mDBhAgB+v59IJDLoeCKRyP6e3+8nkUiUqMzyWr/e7EA84QRtOpLqknfIkE6n\n8fl82a/7+voGHc8dQsRiMS666KIil1d+69ebrmDePPjb3xQGUl0KziEMZ2NRLBZj7ty51NfX73as\no6Mj++tAIEAgEBhRgeWUCYOGBnjySYWBVJ+8gWBZFqlUCoD+/n5qa2uH/Fw0GmXZsmVDHssNBDdb\nt848ELWhQUuLUr3ynvatra3ZeYFkMklzczNghhIZK1eupL29HcCzk4rr1pkgmDUL1qxRGEj1ynvq\nNzQ0AOZCtywrOyRoamoCIBKJcP311zN16lR8Ph81NTUlLrf4MmEwZ46ZM9DzDKSaVfXNTblhoM5A\npIp3Kq5bZ25UWrBAYSCSUZUdgjoDkaFV3aXQ26swENmTqroc1q0zDzc55RSFgchQqmbIkBkmzJ0L\nTz+tMBAZSlVcFgoDkeGp+Eujt9cMExobFQYihVT05bFunZk8nD8fnnpKYSBSSMXOIWiYIDJyFRkI\n69aZZxkcf7zCQGQkKu5SWbfOPMugvl5hIDJSFXW5ZIYJs2fD6tUKA5GRqpghQ2+veRjqzJnadCQy\nWhVx2fT2mtWExkaFgchYeL5DyISBVhNExs7TgZAZJmg1QaQ4PHsJ9fZqaVGk2Dx5GWWGCVpaFCku\nzw0ZNEwQKR1PXU6ZzkBhIFIanukQenvhpJPMPgOFgUhpeOKy6u0125Fnz1YYiJSS6y+t3AlEbUcW\nKS1XX17adCRSXq6dQ+jthZNPNq9X01uYRcrDlZdZpjOYOVNhIFJOrrvUcocJeiW7SHm56nJ7/XXN\nGYg4yTWXXGZpMRBQGIg4xRWTilpNEHEHxy89DRNE3MPRy6+31wSBhgki7uDYkEHDBBH3ceQy1DBB\nxJ3K3iH09prXsc+aBdGowkDETQpejuFwmGg0SigUGtXxXJlhwrHHujcMnnzySadLGBGv1Qveq9lr\n9cLoa857ScZiMQCCwSAA8Xh8RMdzeWXOwGv/8b1WL3ivZq/VCyUKhK6uLiZMmACA3+8nEomM6HhG\nby8sXGg2Hrk5DESqXd5LM51O4/P5sl/39fWN6Dh81hlMmaJ7E0Tcbu9CHyg051jo+IwZNQA8+yyM\nGzeCyhx08803O13CiHitXvBezV6rF6Cjo2PEfyZvIFiWRSqVAqC/v5/a2toRHXfiVfAiMnp5G/jW\n1lYSiQQAyWSS5uZmwAwV8h0XEW/KGwgNDQ0ARKNRLMuivr4egKamprzHpTSGu8Tb2dlZporELZYu\nXbrHYyPZGlC0jUnhcBjLskgkErS1tY34uBMK1ZT5AW7atInly5eXu7xBYrEYyWSSlpYWQqEQjY2N\n2UDOFYlEWLFiBY8//rgDVe6u0M848++VSqU8c1647VxeuXIlK1asYOPGjbsdG+55k1GUOf9i7lco\nl0I1RaNRmpqaaGtrI5FIEI1Gy15jruEu8dbU1JSzrLyG8999+fLltLS0kE6nPXFexONx/H4/wWAQ\nv9/vipoXL16M3+8f8thwz5uMogRCsfYrlFOhmhKJRPb3/H5/dq7EKcNZ4o3H49kT2Q0K/Yy7u7uZ\nN28eAO3t7Xn/5iqX4ZyrmfY8kUi4ouZ8hnPe5CpKIBRjv0K5Faqpra0t2w7GYrHsieukQqO7zIqP\nWxT6Gff09NDX10c8HnfNvEehmhsaGqirq8Pn8w36nJuNZFagaNuExrpfwQnDqSkWizF37lzHJ0wL\nLfG6rTvIKPQznjhxYvZv2XA4XI6SCspXczqdZurUqYRCIdra2kgmk2WsbOQKnTe7KkogjHW/ghOG\nW1M0GmXZsmXlLG1IhZaAE4kE4XCYlStXkkqlXDG2LfQzrq2tpa6uLvvZl156qew17qpQzaFQiCVL\nltDS0sKf//xnuru7nSizoNFuDShKIHhxv0KhmsHM3ra3twM4PqlYaAm4paWFlpYWampq2LFjhysm\nFwv9jC+88MLs8XQ6zfz5850pNMdwzovx48cDZuLRsqzyF7mL7u5uenp6WLVqVfb3Rrs1oGjLjqFQ\nKDv5lhl7NzY20tPTs8fjTstXcyQSYdGiRfh8PlKpFN3d3SxcuNDhir1nOOeFz+ejp6fHFZ0YFK65\ns7MTv9/vqqXSYinpA1JExFt076GIZCkQRCRLgSAiWQoEEclSIIhIlgJBRLL+H6yk3Q+5wb9JAAAA\nAElFTkSuQmCC\n", "text": "" }, { "output_type": "pyout", "prompt_number": 8, "text": "" } ], "prompt_number": 8 }, { "cell_type": "markdown", "metadata": {}, "source": "Compute and plot the asymptotic series expansion at $x=\\infty$" }, { "cell_type": "code", "collapsed": false, "input": "# Series expansion at infinity is not implemented yet\n#ats = series(fresnels(x), x, oo)\n#atc = series(fresnelc(x), x, oo)", "language": "python", "metadata": {}, "outputs": [], "prompt_number": 9 }, { "cell_type": "code", "collapsed": false, "input": "# However we can use the well known values\nats = Rational(1,2) - cos(pi*x**2/2)/(pi*x)\natc = Rational(1,2) + sin(pi*x**2/2)/(pi*x)", "language": "python", "metadata": {}, "outputs": [], "prompt_number": 10 }, { "cell_type": "code", "collapsed": false, "input": "figsize(4,4)\nplot(fresnels(x), ats, (x, 6, 8))\nplot(fresnelc(x), atc, (x, 6, 8))", "language": "python", "metadata": {}, "outputs": [ { "output_type": "display_data", "png": "iVBORw0KGgoAAAANSUhEUgAAAPoAAAD0CAYAAACy5jtNAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJztnXtQXNd9x7+LQDyEYFneT8EKJEdybLAsPxInwQLFSZM0\nqVGrGWfazKRlnHQy6WRiVW7TadVpMnWsyUwe00zHO/2jkz5lbV5t4tRiHSdOY6fCEFt+SAIWxPu1\nyy4gQCC4/eNw2GW5e/fec37ngs39zGjEPu+5e+/vnN/7uDRN0+Dg4PCuJm27B+Dg4KAeR9AdHHYB\njqA7OOwCHEF3cNgFOIK+w/H7/QgEAvD5fLqvnz17FgA2vd7V1QW/35/0Mw67D0fQdzBdXV0AgJaW\nFgBAd3f3lvf4fD40NDTg4MGDG8899dRTaGtrQyQS0f2Mw+7DEfQdzIULF1BQUAAA8Hq96Ojo2PIe\nn8+Hnp4enDhxAgBw8eJFHD9+HABw5swZNDU12Tdghx2LI+g7mEgkAo/Hs/E4FApteU84HEYgEMD5\n8+cBAJcvX0YoFEJ3d/fGcw4O6ds9AAdjUuUztbe3AwAuXbqEQCAAl8uFoqIiNDU1oaOjA36/H21t\nbRvvd7lc+Ju/+ZuNx83NzWhublYydoedgyPoOxi3241wOAwAmJmZQWFh4abXfT4fPB4P2traUFhY\niGAwiMLCQtTV1W18/vLly5sEHQDOnTtny/gddg6O6r6DOX36NILBIACgv78fJ0+eBMBUeoDZ7a2t\nrQCYWn/8+HGcOnVq4zORSAT33XffNozcYafhCPoOhjvSAoEA3G43GhsbAWBDuFtaWjbU86KiIjQ2\nNqKurg5utxt+vx/hcBiPPvroto3fYefgcopadhculyul3e/w7sNZ0R0cdgGOoDs47AIcQXdw2AU4\ngu7gsAtwBN3BYRfgCLqDwy7AEXQHh12AI+gODrsAR9AdHHYBjqA7OOwCHEF3cNgFbJugaxrwne8A\nP/nJdo3AwWH3sG316P/wD8Bf/iWwZw/wm98Ahw9v10gcHN79bFv12l13Ae99L3DtGnDHHcC//Mt2\njGL34VSv7U62RdBv3QJycoDLl4Ef/Qj43veA9V4JDopxBH13si02+n/9F5CRAdxzD/CHfwiEQsDS\n0naMxMFhd7Btgl5Rwf6urwcWFoBLl7ZjJDH+7M+A970PGB3d3nE4OKhgWwT97beBY8dij0tLgf/5\nn+0YCaO7G/jnfwZGRoAvfGH7xuHgoIptEfQbN4D77489vu8+oKdnO0bC+Na3gMJC4IknmN/AweHd\nhu2CvrYGTE8D6xuLAADq6oArV+weSYznnwc+8hHgj/+Yqe7Xr2/fWBwcVGC7oA8OMmG/++7Ycw88\nAKy3L7cdTWMTz2OPsUhAaSnwH/+xPWNZWgJOngSefHJ7ju/w7sV2QX/pJSAriyXKcJqbgbQ0Fnaz\nm85ONp73vY89PnwY+PWv7R8HAPzFX7DxfPObzG/g4ECF7YLe3Q1UVm5+rriYCfnrr9s9Gqa279kD\nuFzs8V13Af399o8DAL7/feCzn2WTzdNPb88YHN6d2C7o164B6xuEboIn0NjNb3/LQnychx7anhDb\nwkLM63/iBFvZt4OlJWBgYHuO7aAO2wV9YIA53xLxeLbHIffGG4DXG3v88MPA/DywvGzvOH74QyA7\nm/02p06xTEG7E9iWloCGBpbI9Oab9h7bQS22C/rUlH4BS03N9qwkMzPsxuYUFQF79wKvvGLvOF54\nAaiqYn8/+CCQnw90ddk7hvPngZUVJuxxG646vAuwXdBnZ5kdnEhNzfaozOEwi+PHU1hovxnx6qsx\nEyItja3mzz9v7xj8fhYB+cQnmEljN5oG3L5t/3F3A7YK+toac7rFZ8VxvF5gbMzO0TC7eGUFOH58\n8/NuN8ves5PJyc1JRIcOMcel3+9HIBCAz+fT/dzZs2cBQPf18+fPWxpDTw/Q1gb80R8BfX3AzZuW\nPi7F6ipw9Chw4IC9x90t2Cro4+Ps/9rara+95z3A3Jydo2EOr8xMIDd38/MlJcDVq/aOZXoaeP/7\nY49raoCudd29paUFANCtE3Pz+XxoaGjAwYMHNz3f0dGBSxYKCKamgMVF4FOfYsfetw/47/8WOBFB\nvvtdpl2lpwN/+7f2HXe3YKugX7nCLmSazlHvusv+OHp3NxP0RA4etFe7iESY8+/BB2PP3XUXMD5+\nAQXrIQqv14uOjo4tn/X5fOjp6cGJ+FRDsHJUKzz3HLB/P/sHMPPlhResnYcM//qvLInq5El7jwuw\na233ImM3tgr6W2+xMJoehw8zG21mxr7x9PayTLhEjh5lq5tdvPwy+12ysmLPHTsGLC5G4PF4Np4L\nhUJbPhsOhxEIBDap6d3d3RtagFl+9avNYc+77rJXq7l6FXj0URZxsNM/8NprTJs8duzd7R+wVdB7\nepj9q0dGBlvt33jDvvFcv64f0z96lKmydvHqq1vNhwcfZD6N5WXjGFt7eztaWloQCoUQCAQAMOG3\nyltvbY6G1NfbV2gUjbJ/p06xFT0jw77w3t/9Xcxs/N737DnmdmC7jV5Tk/z1zEyWUGMXY2OxkFY8\njY1sdl9bs2ccV69unXAKCoA9e9zo7GRCOzMzg8LCwk3v8fl88Pv9AIDCwkIEg0Gh1Rxgoc077og9\nfuAB+9TZn/2MhRMLC1mWYnY2MyXs4KWXgI9/nJ37hQv2HHNhAfj0p+31gdjaHHJyksWpk5Gby7y9\ndjE1BTzyyNbnS0rY/319LKasmvFx/SSijIzTeOWVTvzpn7agv78fJ0+eBABEIhG43W54vV7ce++9\nAJha39raimAwiGAwiFAohHA4jO7ubjQ1NW363nPnzm383dzcjObmZiwuAvFve+gh5v1eXd1cl6CC\nX/1qs0ZTXs4ahqpmcZHdA5/5DPMP2bWiP/446378/PPAxIS+z4oaWwV9eBi4887kr7vdrLrNLhYW\n9JN3XC7mlHrtNXsEfWwM+MAHtj7v8TRhfLwTgUAAbrcbjY2NAIDW1lZ0dnaipaVlY0UvKipCU1PT\nhlD7fD5Eo1Fdp1y8oHNmZjaH93g9wrVrwJEjcueXitdf3zzRHTnC7hXV/PSnTHuoqgJ+7/eAr36V\n+Yks+jEt89xzwJe+xIqXnnsO+NjH1B4PsFnQZ2dZnDQZRUX2Js3MzyefeHJy7LNRx8b0Q47l5cC+\nfe1oacEmdbwzLhG+ra1N9zvb29vR3t5u6vjDw2xVSZz0srPZyqpa0EdGgA9/OPb46FHg5z9Xe0yA\nHYM7YxsbmYC/9hr7WxUTE2xS/eIXWUu1H//YHkG31UZfWDBeISsq7HOC3brFbPBkN3FOjn2daW/e\n1B9Haak9K1tnJ1PPE1XI0lJ7nKMTE0y4OceOsUVBNa+9FvMZuVxAXh7w4otqj/mDHwBlZcwHc+gQ\ni7jYga2CvrJivFFDdbV94bW+vtjF1aOoyB4h0zQWQ9dLCz5wgCXSqObKlVj8PJ79++2Z7BYWNpsN\nDz7InKGqw11TU5tX76Ii9X0AXnwx9lt/6EP2lUTbJuiLi2wFjS8JTcTrZc4fO7h6lYVxklFezlYa\n1fDEHD2Tpr7enomvr08/n6CqSv1kFwqxCTe+41BREQu1vvaa2mMPDbFNRDher/rcgaGhWMr1Rz7C\n5MKO+L1tgt7Tw1TD7Ozk7/F67Wsp1dubPHkHYCpdJKJ+HK+/zjzOeg6gw4ftyRYcGGChrUQaGtT/\nBl1d7NwTJ92MDLWJM5rGhCw+G/HIEfUa1NWrTGUH2OSuafYkCNkm6L29xisowGKZKyv2jOfGjeRq\nO8CcY3Zkx12/nnzyO3qU1YirrksPhzfX5HNqa9X7TK5cYXn1iZSVqc2pGBpisfv43IGjR9UvNC7X\n5snF67WnJNo2Qe/v35ziqQdPXrEjUWN4mDW7SEZdnT1qczDI6t/1qK5m/+tkvpIyMbG1vRfANIqF\nBbXHDgaZUCeSm6s26tHVxc4t3gHZ1KTWCbi4yK7lAw/Enltbs6fvgG2CPjSk7/CJJyOD/fC9verH\nMz2tf4NxGhrs6TJjNOHs2cP+qS6ZnZ+PqZPxHD3KNCyVGkVfn75mVV2ttrDojTe2ph2/971M8KJR\nNce8fJmda/xxq6rsud9tE/TRUaYqpSIjwx5P7+SksaDX18c84ioZH2fNMZORlaU+LXh5meV7J1Jd\nzSbeyUl1x56e1k+LrqpS6wwdGIhtC8bZu5eZUapWWD2P/h132OMLsk3Qo1H9vPJEsrLsaSmVajzZ\n2cyeUp2SOz9vnP+vOi14ZYV5ffUE3eVigq6ywGRsLGaixHPwoFo1urdXv6ApO5sV+KhAr6ahro5p\nu6qxdUVPpboD7Ma248QXFvSz0eLJyFCfHTcxoR/a4qhe2Xp7mXmQ7NpkZan9DRYW9EOuDQ1sElTF\n5KT+BJudrU6VHh7eurjceac9HXVsXdGNVGVOfn6sE41Kbt/WLySJx47suPl547TgjAy1sexr14yL\nVnJz1f4Gc3P62ZJHjqiNL4dC+r+7x6Ou3mJycqug33038wuojqzYJujz8/qe3UQ8HvWJKprG4tN6\nDqh4cnLUJ4wsLTE1NRklJWpDXH19xrkNpaXqrgdvBqmX/uv1qnWMLS7q/+5lZeqcgENDW2Wgqool\nid24oeaYHFsz44xsUU5xsfpwEnd+lJcbvy8vT22RDb/RjSacsjJ1NzvAbjAjkyo3V90KNzXFfAB6\nC0B6OvunKlMtWX1BVZU656Oe9uJyMSeg6kYbtgn68nJqVRlgN7bqOHpfH7vBUtVZl5WpdQjxCUfP\nGcWpqlI7hokJ4x4BpaXqJt6332Y3erKy0PR0NfYybyqiJ+g1Neom1mTRjexs9ZEVWwR9dZVdTL3s\nq0RU39gAE/RkSSrx5OWxEkpV9PamnnBqatQmrYyNGYc9KyrUJQ719hprEzk5aiIwPEtTL35/8KAa\n59jaWnIzpaREvQPaFkEPhZiwG60cnMpK9amnw8P6oZVESkrUqs3BYOq04IMH1cbyw2Fjr391tToN\na2DAeMLdv1+NAFy/nlyLUFVfMDzMhF1PBlRNaPHYIugDA+ZUZYCFvFQnqfBGC6moqFBrRgwOGjvC\nAGbTra6q88pGIsa+CpXXY3jYuN6goECNYywYTK5J3HEHE0jqKspr15gpojfBlJSod0DbIuj9/alX\nLo4dparj48Y3GKeqSq12MTy8NQ0zkZISdnOochDdvGnsI/B6WWRABeHw1uy0eIqK1EQcBgeT113w\n+4I62tLbm/yYVVVqNUfAJkEfGkq9cnH4hVe5kk5NJW87HU9trdoy0VDIWG0GYtlpqmLZa2vGTtL6\nevYeFR1xR0eNJ7rSUjXVZCMjxtc/PZ2p95QMDCQ/14oK9eWxtgj66Kh+KaIee/awm1tlk8iZGf36\n60S8XrVJG6Oj5ibArCx1gj4/b5whyB11KsKMMzPGE11lpZqVLho11iRU/N7Dw8knFzv2m7NF0MfH\nzaW/ctLT1SYQRKOxls5GcJVWlfpudsLZu1eN9391NXXXH5eLTb4qwlyzs8bZkgcOqEmDHR01Nt32\n7aO//8bHk1/rgwfVNxixRdCnp815uTl796rNSFtbM57R48ehspouGk2tugPsxlPhlOKrdCp/RV6e\nmol3cdHYP1BXp8Zfk+p3Ly6m94nMzSVPGDt0iGmOKtNgbRH0ubnUWWjxZGerzUhLVYueiCrtYn7e\n3O+yf78ar2wwaC4SkpamxpS6dcs4W9LrVdNxaH7euHIxJ4deg4pGk5cj88xAlRmhtq3oZrzcnH37\n1IYbbt0yVzILqM13X1gwl//vdqtx1gwM6O8mm0hurpqJN1Vh0YED7D3U4b2FBeMJprCQ/veemko+\nqbtc7DqoLEe2RdDn582pqJz9+9V6IVdWzOXdA0x1V6Vd3LplXLnGKShQ4302E8cHmEOOWpVdXmaq\nqpGg83AU9eq6smKcpVlaSt8MYmnJ2ExxudQmzdgi6JpmTXV3u9WpMTz5xIyAAUy7UBXDXl01l/9f\nXKzG+5wqvMVRkSHIM95STTTp6bQCoGnsn5Ggl5fTh3eXl42jG1lZatNgbRH0VNlXiXg86vKruaZg\n1jmYm6smaYN3dzWjWZSVqfE+R6PmTAcVK3owyIQ4FXv30voHQiHmjDWKulRX09YXaBqb1I3Kkfft\nU1tXYYugr6wYqy2JFBWpS5gZHGTOJbMb6eXnq1Ob09LM2cgVFWoKW0ZHzTnjiovpC41u3DB37llZ\ntKZTf3/q63/gAG2CEF+0jMzXvDy1finlgq5p7EczaxMDbLZV1UZocNB8Oi7AtAsVzfu4oJuhqkpN\nnDUSMW55zSkro0/oMJstuW8fbcehVIU0ABN0yomVl0UbTS5ut9oGI8oFna/MVpxx5eXqklRGRsyt\nJJzCQjVls8PD5kplAXbjqQgzzc2ZqyisqKC/HmNj5pKoqEOLg4Opr39tbawpCAVGvfs5RUXv8PDa\n0BCbyczYY5zKSnWZQmNj5vPuAXXaxfi4OT+B3+/H0FAAa2s+3YSKs2fPAmD7oXN8Ph98Ph+efPJJ\nw+++edNchmBVFX0yx9SUufbf+fm0AmAmHZu/TmUzj4ykPlfVJdHKBX1w0JqQA0zNV7U108SEOU8z\np7RUjXYxNpbaT9C13mD8k59ke6O/8MLWxuA+nw8NDQ04uO7pCQQCaG1tRXt7O4LBIAKBQNLvX1w0\n5yQ9cID+N5ieNldYRO2YnZgwl9NB2XZ8eDi1DJSXq+16q1zQR0fNq6gc7gxRUTFlNXmnokKNdjE9\nnXrCuXDhAgoKCtbzzb344Q87trzH5/Ohp6cHJ06cAAAEg0F0dLD3eb1eBA3yd2/dMpcKXF0d8xxT\nEYmYMxuoQ4vT0+Y0CZeLLlHKzLlWV6stz7ZF0FPtuZYIdxCpCLGFw9by7quq1DReCIdTTziRSASe\n9R8jLQ0YHt6qw4bDYQQCAZw/fx4A0N7ejvb2dgBMIzjO9+jVwWw0hE9IlLby/LzxDjWc0lLalS4c\nNueAzMyk8/aPjqb2C6huoWZRqbbO5KTx9sR68E4cw8PmqrusEI3qN+hLRk1NTLsw6yU3QyRiTnXV\n1o3jjAz9kCMX6kuXLiEQCKClhan5XV1dOHbsGBobG7d85ty5cwDYOY2MNANoNhwDr2AbHDSnAZjB\nrKBThxaXl83VOeTk0Hn7Z2ZSLy7UIb1ElK/oU1PWbGJORoaaYpLZWXM3GIerXNSz7exs6pXF7XYj\nvB7Ez8ycwdra5lnP5/PB7/cDAAoLCzep6YFAAH//93+v+73nzp3Dl798DsA5fPKTzabGm55Om7m1\ntGTOP0AdWpyaMqfRUSZKzc6mXrBqa5nqrkp9Vy7o4bC5lSuR3Fw1pZk3b1qrXOPaCPXuMfPzqe22\n06dPbwhvWlo/8vNPAmAqPcBs8NbWVgBAKBTaUNOfeeYZnDlzBgCSOuNu3Ih1rzEDpSoLsJXVjHZA\n7ZhdWDB3/fPy6Oot5uZSRze4p19V0oxyQZ+ZERP0PXvUFJMsL1tLx+WVRdQVbGZCW01NTQCYsObk\nuKFpTA3nwt3S0oKOjg74/X4UFRWhsbERHR0dePLJJ1FfXw+PxwNXEte+1WgIpSoLsBi1mQrC2lra\nLYvMOiALCuh8RAsL5vJI9uxRVxKt3EafnbVmE3NyctQUkywtmS9R5aSl0ech37plbmXhNrjX27Jx\n43V2dm683tbWtun9ra2tG+q+EcPD1hKHKIt7rGRLcpU3HKbx1ywvm98ajOqaLy2Zm1wyMph59OCD\nNMeNR/mKbtbpkoiqUtXbt63l3QNMIKjNCLOqK6eggDbMND5uLRpCeT14SrEZweXmBVVMe3XVXOVi\ncTFdvcXKirnFhdo8ike5oK+sWLOJOSqKSbgKaCXvHlCjXZhVXTmFhbSFPhMT1qIhbjedKjs4aLwV\nUyJUjlkeJjWzopeW0nn7b99OvUU3wDI2VW3waMuKbiXPneN20xeT8BXJSqNKgKmtlNoFV13N1sQD\nzJ6nLCyZmrL2O1AW95jJFIuHykcyMsLsYDMJXFT5/bwnvhm/UG6uut4HtqzoZmbQRKhXMMBaxVg8\neXm0+dZcYKw4KalTccNhcxliHMrS4ZERa9mS+fk0jkAr4cHKShpvP1+hzdRXUHr6E1Eu6Kur1m1i\ngNlI1Lm/VlcSTn4+rXYhMuFQF/rMzJjLEOOUltJpFOPj1gqLMjJowk4jI+ZLlGtqaKrXBgfNT2qU\n5lEiSgWdt7AVWdFVFJOYSUXUg9oRNjRkfcKprKTdTMJsiSqnrIxuayar/gEqx9j4uPnrf+AATX7/\n8LC55h4Am3hVVbApFXSubllNgQWYTUNdTGJ1JeEUFdFqFyKFPtRFD6ur1pyklKWqoZC1bMnMTJoV\nfWLC/PWnSpQaGzM/uZSVqavaVCroQ0OxLZasQmUjxSOSdw8wRxhlvvXEhPVCH+7QpBpHOGwt7FlZ\nSXfsUMhaBWFBAU0K8tSU+a3BAJoEFiuTS2GhuuYTSgV9ZETMJgbYCka975mZ0lA9qM0Iq6orwGxL\nl4su3/zWLWvREF7cQ0EkYq2CkMoxa1WT4AksMli558rL1e31p1TQragtiXC7nrJE1KqnOX4slOMQ\nnXD27KETdLNJHBzK4p65OWuOwKIiGkdgJGLt+lP0qwuFzIcxy8vVNAEFbLDRraqoHK7uUCYQRKPW\nVhJORQXtTDszIzbhUG62aDUawhNcKCYaq9mSJSU0GlU0ai2kmZEhf/9ZudbU91k8SgXdqk2USHY2\nbZL/6qpY8k5VFW3HG9FCH6pUXB4NsZrzn55Ok7hitsiDU1ZGo1HNzVnLl6dIlIpGzWsv1dXqatKV\nCvrsrNgNzdE02oops7XIifCbgyr0YVV15VRW0sTzeT61Vc9/RgaNRnHrlrUKQirH7M2b1jQJivx+\nK2FM/j4VveOUCvrkpNyKvncvreq+sGCu62kimZlMbaUai2ihz8oKjaANDpqP7caTlUUz8ZqtIONU\nVtKEFhcXrWkSFIlSCwvmrzWPUKnYmkmpoFu1iRLJzqYtxLfqaY5nzx46+1h0wtm/n6bQx0qGWDxU\nxT1W/QOVlUy7k13VzZYGc9xueS1ucdHaMVX1YVAq6KIqKmffPtrdK1ZWxHueUTrCzLZRSoSq0Ec0\nGpKbK6/K8k0urQg6D0XKTjK3b1vTJCjy+62WI1OZR4koFfSbN+WaBeTm0iYQrK5ad0BxKGvSRScc\nt5smvCWSsAMw/4as/ch/Q6t5BGlp8iqt1etPEdazmgKelaWmnZRSQbdqEyWSn0+X5C+Tdw/Q1qSv\nrYmNg6qCTDQaQlFGKeofkF3pRCINZWXyYb3lZWv9D7Ky1JSqKhV0qzZRIpTFJNwEEElUAehq0nmh\nhJVadA5VKq6VJI54KFJRrew5F4+sY5Z/1orJUl4u1+KbXysr/hhV23QrFXSrjRgT8XjoaqCHh61t\nl5wIVU06nyxEJhyqCjLRhJ3CQnnVfXxczBEou9LxugsrVFbKTWx830Erk0VurpptupUKuoxNDNCl\nPgJyefcAnSNsaEh8laCq6FteFvP6U2gUohWEOTlyK93oqPXrX1UlF9bjHW2sQN37gKNM0FdW5Gxi\ngLaYZGxMTGXkUFVQyUw4VVU0KZKTk2Kqe2mpvEYh4x+QMZ1EIg1cGxXNyrPaSQeg733AUSboXP0Q\nuaE4lDXpop5mDlVNukgtOoenSMrWhVtJ4oiH4npYrSDjyK50VspFOXxiEE0SstLoguPxvMMy40ZG\nmIoq48ygLNsTrUXnUDVnFFVdgZhwyt4Ii4tiqjtFlxtR/4CsoItef5mKwclJ69e6uFhNBZsyQRft\nzxYPVd8uQHwl4ZSXi4WFEhG5+BzuTBwclBuDaIYghUYh6h+QdcyGw+J7AIpmqk1PWzdTVLRQAxQK\nuqxNDNClPgLiteicsjKamL5oaItDUUEm2pmXF13IaDai/gFZx6xMabBoWE/kWpeW0rdQAxQK+sSE\neNMJDl/5KAopolE5QadyhM3MWGujlIjMCsNZXRUTdL6dtUziys2b1ppScmRVWtG6C5l6C6uNLgA1\nLdQAhYI+NSVnE3P27KGpgZbNu+dqq2y9sNl90ZNBUUEmEw2R7XIjmi1ZVia30pnZulgPmbCeSKMT\n2ZBeMoQE/cUXX0z5HtF2SYmkp9Mk+d+8aU7Qk50b/6xsiM3Mvujx+P1+BAIB+Hw+AGyFiU8cOXv2\nLABsvK73GT1EJ2FZjcJqLTqnosJcmCvZ9RPVJGQqBufnrU8ufAJOpj2akT09lAl6OCynonKoiknM\nloYmOzeutsoWVpjZF53T1dUFgG2PDADd3d1b4sk+nw8NDQ04ePBg0s8kIpMhKFt0IVrQU1FhLoKT\n7PqJRhry8sQF3WqjCyBm7iabTG0VdDPI2sQcqmqepSW5AhuAxhG2uGj+4l+4cAEF67qf1+tFR0fH\nllRcn8+Hnp4enDhxIulnEhFJQeUkahRWEd25p7qaaQOippNopEGmJn1pSWxyoTJX41Em6FZV1GRQ\n7cu9siKXdw8wdVfWjLBSix6JROCJ+xFDodCWeHI4HEYgEMD58+eTfiYRGUEvLhaPPoj2qgNifg3R\nWLrLZf8egKJmSlYWfU26S9OsR0Vdonqfg4ODNAIiK7aia5qW8l9RkYZz51K/L9W/hx7S8PDDct+x\ntqYB0DA5Kfc9VVUavvhFue9wuTRcuWLuvWfPnkVHRwc0TcOzzz6Lp59+Gp//vIYDB9jrzzzzDC5e\nvAhN0/D000/jmWee0f1M/HcCQE2N+PgffphdE5HP/u//atizR/zYGRkafvpT659bXWXXf37e+me/\n/nUNBQVi4wU0DAxY/1xJiYavfMXoe62jTHVfWpKrRedQJPlz1UvWlJAtrNA0a22UTp8+jWAwCADo\n7+/HyZMnUVICzM8z/dXr9aK1tRUAU9GPHz+u+xm98xBFprhHtoJw714xjz8Pj4kU04iWBvMIgYjq\nTt1CDTAh6F1dXfD7/UlDNclCObK16ByKmvThYWaj6aWwpjq/+PCVjAcWiH3WbDSiqakJABAIBOB2\nu9HY2Lhd38b0AAAduElEQVSeoceEu6WlBR0dHfD7/SgqKkJjY+PGZ/7xH/8Rf/7nf45Tp06hvr4e\nn/vc5za+lx/fTBguERmbVaTII57MzFgOQVdXF9LS0lBfX7/l/Dj82n372z7hmouKCrEEFtGW2gAT\n9HA49fWxcv1Snv5TTz2FtrY2RCKRLaEao1DO7dvijRjjodgn3agu2Oj8gM3hK9nCCl6LbsXF0d7e\njpaWFrS3twNgv2l6eufG621tbWhra8MTTzyx6TMNDQ3QNA29vb149tln8eSTT2687nabC8PpUVQk\nnqEmW0EYn7wyMzODtbU13fPj8GuXmXlQqjRYJIHFynbJieTnA4OD3fB6vWhpaYHX67Uke3oYCvrF\nixdx/PhxAMCZM2c2VgtOslAOzyCTaTrBodiOZ3RU39Oc6vyAzeErj0cuYYai0MdsBRm/AQCgs7MT\ntbW1G489HnNhOD1krodsn//4dl5G58fh1664+IRw3UVFhVi9hWhLbYAJ+vx8TCMJBoOmZS8ZhoLe\n2dmJUCiE7u7ujfBNPMlCOVwYZDrAcihqoJOtJKnOD9gcvpJtpURR6GN1V9NAIIA/+IM/2PRcYaG5\nMJweMtdDtoJQL0tN7/w4/Np9//vnhTWJ3FymgVltIyazwWhhIXD7dhPq6urg8Xg2XSeO1euXUnUv\nKiramE38fv+W1/W8gKOj7MeRiddyRG2keIyaDqQ6P646h0IhzM8HpAorZDad5PDJ06xmcenSJeQn\nZC7xhB0RD67MRoCiFWSc/Pytjlm98+PEX7u0tIDQMbmpZTUjUqTRBYf5QSKor6+Hz+dDe3s7+vv7\nt7zPyvUzFPTCwkLU1dUBANxuNy5fvrzpdbfbjfD6FDszM4PC9btwdJSmdhtgHmrZJP9QSF9lTHV+\nPp9vQ/gLCwuxthaUaqUk2/wCsH7jcVsuntLS5NcuFdxmFYnyiO5my9Hz+OudH7D52q2tFSI9PSh8\nXJF6C5mirpISIBLx4fHHH0dbWxueffZZXLx4cdN7rF4/Q0E/derURqgmEongvvvu2/gb0A//AGK9\nspJRVcVuKpkS0WQqY6rzSwxfHTt2XGpXT1nVlZOebk7Q+bklUl6e/NqlgodMRSY82QrCRI+/3vnp\nXbtoNITCwuPCx83MtB7Wk+k7UFbG7ve89fBIS0sL3OupgalkLxmGgl5XVwe32w2/349QKIRHH30U\nADZ+QL3wDyAfRomHqz8y+e7J8u5TnV9i+OqDH2yUmnCoCn3MVpC5XK6NYpd4KiuTX7tUcNNDpFRW\ndHNJTmIXWr3z07t2t28XobLS3PnpIVIaHImIX2sWlj6D8+fPb4R+edQllewlRVPAl7+saRUVdN+3\nZ4+mvfyy+OfvukvTPvUp+XFMTLCUl9VVsc/ff7+mPfKI/Dg8Hk372tesf25tjaVrTU7KHX/PHk37\n5S+tf87t1rSnnxY/7re/rWn791v/3B13aNpjj4kft7ZW0z73OWufaWrStN/9XbHjXbmiaS6X2GeT\noSQzTrSlbzJka9JF6oL14PalaKYeVaGP6PZQ3L6VHYNoTbpokQfHbE16IlZKg/UQyYiUMVOqq2NZ\nlFQoEfRwWK4vWiIiNlI8ok0HEsnIkGulJHvDcURTcfm4ZR2lon3UZHazBcS70FopDdZDZA9AmXuO\nm5lU+w4CigRdpFeWEbI16RS16ByZfdJXV2nGIdr1hKrGWbQmXXbnnspKsQiMbN2FyC49shuMpqXJ\nd/vd9H10XxVjdlYujJKIbJK/7GaP8cjskz47SyPobrfYbE+17bNIHzWZWnQO/+1EMvPs3gNQ1kyh\nbj6hRNBlwyiJyO6TLqsyxiPTnPHWLZpxiKbiUgn6/v3WrwfXyGTyCHjI1qoZt7AgN8EUF1tvNS3b\n6ER299hElAi66JY/yZDdJ11WZYxHppWSaJvlRERTcSlacgEsbGT1esgUecSTk2MtS407tazsUZ5I\naan1Qh7RllmcggLaUlUlgi5rnyQi07dLpi5YD5n9q9fWxPZFT0R0V1OKbZ8BsR4BsrXoHE2zZjrx\nCUmmxbbVVtPcTJGZ1F2ud8CKvrxMZxMDbAUTrRrjq5ho3nEi+/eLaRcUNxynvFwsM41K0EVs1tFR\nmiSqvXutmU4ipcGJVFZaC+vx8cmYKbLmaiJKBF10y59kyGzHwzd7pGpzJ7pP+uCg3IaT8ZSXi8WT\nqfbdFrkesrXonJwc64IuazJYrUkfHJQ/pmyTk0TIBV3T6GxRjszGczJ1wXp4PGJmBEUtOkc0nkyx\nvzsgVpNOlUQVX5NuBso9AM3+5hT3nGhkJRnkgs5vAEobXbRvF8Bmf6oCG0DcEUZZ6GO1Jp0j25KL\nI1KTTrVzj1WPP0VpMJ+gzDphR0flr7Vsk5NEyAWd2iYG5Daeo1IZOaKb/VGOg4curWoWVPtul5db\nvx6ytegcq+28KEqDXS5mdpmNa8vUonOKiuRbqMVDLug8jELZ+l1m47lwmCbtlCOqXUxM0Gw6CYg3\nQ5CppY9HZGdZqp17PB5rgk5VGpyRYV7QZVtmAfT7pJML+tgYnS3K4TXpIsI+MUHXBAMQd4RR3XAc\nke2hZGrp4xGpSZ+boykssmo6UZUGW2k1LVOLzpExV/VQIuhUtegcvhKKJHzI7keeSFWVmBlBdcNx\nRHqcU+27zVdmK/kEVAU95eXW/BNUdRdW9kmfmZEPo4pW6iVDiY1OaRNzRPfljkZpYtcc3hXUquoq\nuy96IllZ1hMqZPd253Cb1UrRBVUSVXGxtbDTdpQGU9xz1dVyXZUSIRf06WnaWnSOaE061YXmiDrC\nqMdhNRWX0rEDWLNZAbrCospKax5/qjoHK97+2Vl57YWbq1STM7mgU9gneojWpIvsUW1EVhZb0aya\nEbJtlBKxWpM+OEjrILV6PZaXaXIramqsrXQzMzQTrJX8fop7jpsbMluAxUMu6NQ2MUe0Jv32bdqY\nPiBmRlAX+ljNnKLKNedYsVkBusKi2lq2ypntvrK4SLcHoFlvP8UxuXl044bc93DIBZ3aJuaI1qTP\nzdHm3QNMbbVqRlDWxAPWU3GHh+nCewDTKMyaDrw9tEw1F4evzmYnuZUVmuNa2XOOqhw5I0PML6UH\nuaDLFtwnQ7R90vIyXYkqR0S7EFVdk22kl9jjPH6nGb3PjI3Rhhmt2Kx8QqCYaKyudLdv0wi6lZr0\n27dp7rnMTLleifGQCzp1GIkjUgMNyNcF6yFSky5ywxltpBefOdXR0YFLly5tvEdvcz7KhB3AWi42\nryCjIj3dnMefaxJ2lwZTmSnZ2XSlqkpy3altYkCsJn15Wb59kR5Wa9K5TWlV0I020ou/8VxxXjZN\n03Q355uaonWSWqlJHx6mLSzKzDTn8edCQjHBmc3v59lsFFrtvn3iTU4Seceo7iI16RR1wXpYbc7I\nTQ6rgma0kR7PnOru7t60s+g999yjuzlfKETbsLOoyLzNSlFBFk92trlS1Rs36MwVs3sADg0x84LC\n8ZmXt4O97svL9KoyIFYDTVEXnGwsVrQLmeYHWhL3Ms+cCifMOJGI/uZ8FNla8ZSUmL8e1ElU+/aZ\n85FQahI1NeZSsCnq3zluN11NOnFWuhqbGBBL8h8aolUZOfn51ryhoqEto430WCru5tUcYJsLPv74\n48jLy4Pb7cbFixdx5swZzM4Chw+z95w7d27j/c3NzWhubrY8NivXg3pDD7Mr3fAw3XF5TfrKivE9\nNTxMp714PEBvL813kQo6t2Eom05wRJL8qdoXJWJFbeXjELn4p0+fRmdnJ1paWjZtpBeJRFBd7Yam\nBXHxYhDhcAjhcHjD8Ra/OR/fiC8+1zxe0EWx0l6Jqhad43ab8/hTmgzZ2WyyHhszbjRJue9geTlw\n9SrNd5Gq7jxTSoVwidSkU3uaOWVl1mq7RZsfJNtIr7W1dT2e3IaWlja4XC5Eo1G4XC6cOaO/Od/C\nAm0cv7ra/PWgqkXnmHUETk7SOiA1LbW3f2qKpkoPYN9D1QmWdEVXZRMDYrt0hEJqknfKy62ZETKq\nKxfUeBW9s7MTQKwmvb29feN9AHDmzJkt37O0RNfbHrDW5SYapY3EFBcDv/1t6vdNTdFOMLm5qU02\nyjJtq00pjSBf0VUUtACxjeesCDtFpw89rF4AatWVY6UmfWWFNszI03nNREKoC3pKS805Aqny3Dlm\nKvamp+kml5oauq5ApII+PEybGBEPn0CsqDLhMO3WUByzHlgOterKsVKTvroqt4lBIjyKYCZDLT2d\nVpswq1FFIrT1Bfv2pf69w2G6ycXrpStVJRXL8XE1NjEnLc2atzsapW0jxeHahVkbNRxWY0KY3R5K\nVeJQRgYwMJD6fSMjtAJnVqOam2NhQCry8lKH9WS3YoqntpZdN6uNOPUgFXRq50ciVmugqS80h5+j\nWe2Coj5ZD7fbXOaUKifp3r3mrsetW7STzIED5jSqhQXa5K2CgtS/98QE3bXeu5dpTeupEFKQCjql\nfaKH1Y3nbt5Uk45rtcMK5SaP8WRmmkscGRhQ4zvJyTFXdLG8zFYnKmprmaCnEvalJdoJpqgodX6/\n7IaOiaSn70BBV2UTc6zuZLq0pCamD1grIZyZUZf/byaePDhI16kkntzc1BMN75Li9dIdly8mZtRo\nSr9ESUnqsN7SEu2klplpzjxKBamgz87SxRD1sFIDDahLxwWslRAuLakZh8djroJseFhdRWEq84VP\nRJSmi8vFwrjruUBJWV2lFbry8tQtuVZWgLo6umOa1ZpSQSroqmxizv791gR9dZWmRFEPK3uArayo\nGYfZnPuRETVJTCUlqZtfBIO0e99xMjKMPf78d6FOEjIKd62sMA2GUtDz861psckgFXRq50ciVton\nqfI0czwec/nWKlRXTlmZuaaPExNqfCdm9vDu76etXIs/tlGoKxhkx6UM99bUGKdhc8ckpT8kP5+m\nJp1U0KmzrxIpLjbf7ID/6CpaTwPmmwJwDUSF78JsPHlqSs3xS0tTJ8wMDam5BqmaTwSD9FqE12vs\n6+jro0/QKiqiSYMlFXSVNjHAbiyz5aEDA/Q7xsTj8ZjTLgYG1KiuAPutzTjZZmZo49icqqrUGsXY\nmJrcilQq7cAA/QRz4ADT0JJl5XEzhZKKCvEtw+MhHRa18yOR8nLzJz00pMYu5VRWmhvLwIAa1RVg\ntqCZMVDnmnPMxLPHx9U4AisqjLW7oSH6nI6MDDZhJysdHRykD2NWVNA0nyATdG67qApnAWwFMZv7\nOzysVtALC81dAFWqK8BUSTMZevPzaq7LwYOpJ5poVI2fJC/PeEUfHVWXdtzXp//ayAj9pFZbu8NW\ndN62R6W6XFNjvphkbExNIQmnqsrcBVClugKxmypV+GVxUY1J1dDATAejVZ2y+UM8lZXGZtzEBG1B\nCycrK3kCy/g4vS+kvp5ms0UyQee2qEqsJPlPTKhRGTm1teYuQChE31c+nvR0c11IVJhU/Pc1ShyK\nRNQ4aKurjZt/hMNqzJWSkuQT6/w8/bkePhzrZisDmWgODqqzRTlc/TSjvk9Pq83SO3jQXFGLqi43\nnMxM4xRJTWMren29muOnpwM9Pclfn51Vo03U1RlHHCIRNaHerKzk8fvpafrJpbSUTagi25HFQybo\nIyNqar/jycw0Xxo5N6c2pm/WPp6dVZtElJNjvKLyyIAqrcJIlQWYMKrIIWhoMP7t5+fVTDDFxclT\nb6em6P0RLhdb2GRbSpEJumqbmGM2yX96Wo2NxuF2Z6p898lJtYJeXGycLdjToy68B7DfwWjiXV5W\no03U17Nc/2Tm0759aiaYmprkvoGbN9UcMzt7Bwn6xISamutEzLTzAdR5muMxYx/PzakdR3a28e/R\n16fWpMrLS16qevs2c9YdOkR/3Oxspp4n+/1HR4E77qA/bmVl8ol1ZUXNMffvT+7pNwuZoKu2iTmp\nVhCOKk9zPFlZqSuLFhbU5dsDbEU3ypwaGlJbOlxQkDzMxa+Tqh4Fe/cCb7659fmFBWZWNTTQHzOZ\nF3x5mTnNeEttSowcgGYhE/SZGbUqKic723yzA8riAj3y81NfgNu3meNOFeXlxhl6g4NqnYGlpclL\nZa9fV9NXn7Nvn/6K/vbbzFxRocm85z362YBXr7LjqTBfS0vlS1XJBD0aVev84ng8qat5eChCpYAB\nbNIxyrdeW2MzvQrVlVNZaZxvHgqpvS5GmVt9feqShQBmKur5a65dU+cvuvNOdn8l5nNcuaIuvFxb\nK78HG9nQbt5UbxMDTFVNlZEWDjM1SvXE4/EYF7Zw76xKTefAAeMw040balV3rze5c2p0VG2RU1WV\nvgBcv67OL8GdsG+/vfn5a9fUmSiHDontJBwPmaAvLtJ280hGeXnqk75+na22qhN4UtnH166xbEFV\nHm+A3QRGYSbVK/qhQ8nzGnp71WZKFhToa1Q3bqhtgOJ2A6+/vvm5vj51x7z7bvnsODJRWFlRryoD\nbBZPtR0S1X5VqUiVh6xadQWAo0eZPyJZFVs0qtYZeORIrOFCIqOj6rUZPe3uxg01zTg5e/cCb721\n+bmREXUL3bFj7BrLbOZAIuirq+xGs0PQa2tTZ8b196vbSCKesjLjFV1VU8Z4eK5AMl/B8rLa68IT\nh/QcgpOT6hp/ACyUpWe2RCJq4tmcqqqtvoGhIXWmq9vNtNP1DXqEIBH00VGmnqq0BTn19an7XA8O\nqs1zjx+L0aSjOrQFsN89I0M/zAQwQVAR2+WkpzPz5MoV/ddUhJs4R4/q+weGh9WakdXVW3MXZmfZ\neFSRlwf85jfinycR9N5e9bYox0zF1Oio2qw4zuHDxpNOKGSPgzLZ9rpc3bvzTrXHz87eqsoCbNVT\nEcvmNDbGIhvxLC6qPefa2q3hrrk5YH1PTCUUFprbby4ZJILe12dPsgwQc3gYJfmrTjvlHDpkvJPG\n4KB8tqDf70cgEIDP50v6nsxM4D//8/zG466uLvj9fnztaz64XGp3zwFYnDfZRLO+AawS8vKY1hAv\nALyI55571B23qWmzJjc/z871/vvVHfPOO+Wy40gEXUV/rmSkpTFVNTG8Ec/MjNqwDmffPuPqrVBI\nzhHW1dUFILaTKt//PJHMzA5cv35p4/FTTz2FtrY29PREsHev/mcoKShgkY54JieZ0Kn222RmbrZd\nua9CpSbx0EPMZOBa5csvM61GpT/mrrtYFEcUEkEfGrInz52TlWXsWY9G1ae/cpKprQCz22Ru9AsX\nLqBgXVXyer3o6OjQfV9RkWtDq7h48SKOHz8OAKivP4O8PIX65DqVlVuzFbu7mValahttTnn55t//\nlVdiLZ9UUVfHvp+H2F56Sb2p+MgjcuFiMmecyrhlInl5xoK+tGRPBABgoZZklUXLy3LOqEgkAk/c\nHRTSyTXt7u7Gffe1bMTSL1++jFAohO7ubvzkJ+dt0WwaGrYmrrz6Kt1OoEZUVABvvBF7fPmy+kXH\n5WKT2AsvsMe//a365KwHHzS/qaceJII+Pa22i0oiHo9x6unCglpPc+JY9GyntTVmt733vXLfr6Vo\nLRIOh3H0aGy2d7lcKCoqQlNTE6JRwOXyyw3ABE1NW/MJ3n5bbSybc/jwZsfYtWtq1XZOcTHwi1+w\nv3t6gHvvVXu8tDQ5M5Akb2lmxh7vMqe0NHkxCa9cOnLEvrHoTTq8tZbMBOh2uxFeD1DPzMygMEFt\n6u7uRktLC/LyYvHkwsJC1K1X80SjbuzffxlA26bPnTt3buPv5uZmNDc3iw8SwPvex8yUtbXYhDM8\nrDa0xnn/+4Ef/Sj2uL+fjUc1H/wg8Pzz7O+hIaC1Vf0xH3tM/LMkgj47q7bNcyJVVcCvfqX/2ptv\nsptNdbeb+LHohT26u5mjTsZWPH36NDo7O9HS0oL+/n6cPHkSAFPp3W43gsEggsEgJidDAML4xS+6\ncerUKVy8eBEAMD8fwT333Lfle+MFnYIDB5gt/tZbsbBWTw/Q1mb8OQoeeQT4zGeYuZaVxRzDX/6y\n+uM++ijwb//GHHG3bgGf+IT6Y8qcF4nqrrInmR719ck3DnjrLbVlmYkcOaI/lrfekvdbNK0HZgOB\nANxuNxrXY1Wt68tHW1sb2trakJ7uAhDFm2+6UFdXB7fbDb/fj+XlMD772UflBmECl4tNrC+9FHsu\nGlUbbuKUljKb/LnnWCx7cRH42MfUH/fhh9n/jz3GTBfV/RIBydoNjYA9ezTt7bcpvskcFy5oWmam\n/mt/9VeaVlu7/WN57DFNO3zYvnGUlWna174WexyNahqgaTdvbn4f0SXfwr33atqf/An7e2lJ09LS\nNG1sTMmhtuD1atqnP61pzzzDfge7+M53NO3QIU0LBOw7pijSqvvMDIsn2uXlBlhMMVmCP69cs4vj\nx5nqpmmb1fSBAXv9Fh4PMxc4v/41+x1UJ8twysuB//s/9ncgwLQquxy0H/84W9FnZ+1zwgLAF77A\n/r0TkFbd33iD2WcqO4kkws0EvVrk4WF7GmBwDhxgApUY7hsfV9twIpGGhs1JK6+8oma/tWQ8/HDM\nQfqzn9k7yX3pS6y46MUXgfZ2+477TkJa0N98094VFIjtCPPqq1tfU125lIjLxVbzxIKD2VmmedjF\nPfdsboJhR2w3nt//ffbbR6PMVr/7bvuOXVsL/NM/AX/913Ke6Xcz0oJ+/bo9lWKJ7NunXzE1Omrv\nSgowp1tiCWEoBDzwgH1jaG1lpaI87N7XJx/Dt0JVFcsY+/a3mTZz+rR9xwaYF/yJJ+w95jsJaUFX\nXRKYjIoK/Rxz1YUUeni9m5NmhoaYwNm5oj/wAItjv/Yae9zfD3zoQ/YdH2ChrvPnmRlnR2jNwTzS\nzriBAXvTXzmlpVsLKZaWWHjlvq2hY6UcOQL88pexxy+8wEI+qvO840lLYx1K/X52PZaXgU99yr7j\nA8A3vsHO+6MfVd/Gy8Ea0oIeiTDPs93U1wOXLm1+7tVXWTzTjgYY8Rw/Dnzve7HHL71kb5EPp7YW\n+MEP2Mq+f789O+fEk5kJfPWr9h7TwRzS8+74uL0hDc7dd29tEsnLBe3mkUeYusrTUPv77TcfAJY5\n1dsL/Pu/A5/8pP3Hd9i5SAv60pLazhrJeOghJljxNR9vvGFvKi6nqoqN5cc/Zo9fe82erLBETpwA\nfud3mFbz9a/bf3yHnYtL08R3Xo5GmYp686Z9iRmctTVmA1+7FvOyP/QQs0/jixzs4t57mfPtW99i\nUYgbN7bHSZkKl8uVsiLO4d2H1Ire2cni2XYLOcCcPTU1LAuLc/Xq9qjMAKuieuEF4LvfZbXKO1HI\nHXYvUoJ++bL9Dp94cnKAn/889nhuzp5yQT2eeILFsb/xDXuKKhwcrCAl6H192+OI49x/f6zpQHc3\n62hiRy2yHtXVzOP8gQ8A3/zm9ozBwSEZUoL+6qv2tFVOxkc/GqsFv3CBpXzaGbtO5ItfZHHs7cgU\ndHAwQkrQh4bUttVNxaOPMqF6+WXm8d7OsTg47GSkEmZmZ2MF+NtBRgYLp33lKywV97vf3b6xODjs\nZITDa/39rAZ9acme7hrJeP55VrF0992bPfAO+jjhtd2J8Iru97Mun9sp5ADw4Q+zunS7NpBwcHgn\nIizoly7Z12k1FU4BhYODMcIicvky8JGPUA7FwcFBFUKC/txzTFX+/Oeph+Pg4KACIWdcczNT2x0v\n9zsPxxm3OxES9Nu3WY67wzsPR9B3J0KquyPkDg7vLBx/tYPDLsARdAeHXYAj6A4OuwBH0B0cdgGO\noO9w/H4/AoEAfD6f4fvOnz9v6jmH3Ykj6DuYrq4uAEBLSwsAoDt+F8U4Ojo6cCmh97Xecw67F0fQ\ndzAXLlxAQUEBAMDr9aKjo0P3fS6dih695xx2L46g72AikQg8cS18QqHQlvd0d3dvrPhGzznsbhxB\n3+GkymILh8OmnnPY3TiCvoNxu90bQjszM4PChE3unNXcwSxOMusO5vTp0+js7ERLSwv6+/tx8uRJ\nAEyld7vdCAaDCAaDCIVCCIfD6O7u1n2uKWErnXPnzm383dzcjObmZhvPymE7cAR9B9PU1ITOzk4E\nAgG43W40ru9O0drais7OTrSt703s8/kQjUbhcrl0n4vHKWjZnUhtyeTg4PDOwLHRHRx2AY6gOzjs\nAhxBd3DYBTiC7uCwC3AE3cFhF+AIuoPDLsARdAeHXYAj6A4Ou4D/B7LmloiwqoteAAAAAElFTkSu\nQmCC\n", "text": "" }, { "output_type": "display_data", "png": "iVBORw0KGgoAAAANSUhEUgAAAPoAAAD0CAYAAACy5jtNAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJztnWlsHOd9/7/LWzyHXN6kxFs+ZFukbSlO4iSMSSVw0iKF\naVipAyQpUCIu0CZvrMhtioYBAkSNkrxpi3+rRdFcSFNJm6NO4SDiJnIS23FEk44j25IpLineh3a5\nPMV7/i8ePdzl7M71HLPrcD6AIHK4u8/Mzvye53c/HlVVVbi4uPxJk5bsE3BxcZGPK+guLvsAV9Bd\nXPYBrqC7uOwDXEFPcfx+PwKBAHw+X8K/nz59GgD2/L2/vx9+v1/3PS77D1fQU5j+/n4AQEdHBwBg\nYGAg7jU+nw8tLS1oamraPXbmzBl0dXUhEokkfI/L/sMV9BTm/PnzKC4uBgA0Njait7c37jU+nw+D\ng4N47LHHAAAXL17EsWPHAACnTp1CW1ubcyfskrK4gp7CRCIRlJSU7P4eCoXiXhMOhxEIBHD27FkA\nwJUrVxAKhTAwMLB7zMUlI9kn4GKMWT5Td3c3AODSpUsIBALweDwoLS1FW1sbent74ff70dXVtft6\nj8eDL3/5y7u/t7e3o729Xcq5u6QOrqCnMIqiIBwOAwDm5+fh9Xr3/N3n86GkpARdXV3wer0IBoPw\ner1oaGjYff+VK1f2CDoA9PT0OHL+LqmDq7qnMCdPnkQwGAQADA8P48SJEwCISg8Qu72zsxMAUeuP\nHTuGJ598cvc9kUgEx48fT8KZu6QarqCnMNSRFggEoCgKWltbAWBXuDs6OnbV89LSUrS2tqKhoQGK\nosDv9yMcDuOJJ55I2vm7pA4et6hlf+HxeEztfpc/PdwV3cVlH+AKuovLPsAVdBeXfYAr6C4u+wBX\n0F1c9gGuoLu47ANcQXdx2Qe4gu7isg9wBd3FZR/gCrqLyz5AiqBPTgKPPAI8+6yMT3dxcbGLlFz3\nxx8H3n4bmJ0Ffv974L77RI/gwoqb674/Eb6iqyoR7jNngMOHgW99S/QILi4udhEu6L/6FbCwADz1\nFPCBD5DfXVxckotwQb9wAWhpAdLSgM98Brh5E9jZET2Ki4uLHYQL+ssvA83N5OeHHgIyM4GXXhI9\nij3OnQMOHQK+8Y3knoeLS7IQLuiTkwDtNejxAEVFwAsviB7FOqoKfOlLQGUl8NWvAtvbyTsXF5dk\nIVTQVRUIh4GPfjR67K67gD/8QeQo9njpJWB5GXjxRSAjA/jxj5N3Li4uyUKooL/1FhH2I0eix44c\nAd55R+Qo9vjud4HycuDAAaCsDPjBD5J3Li4uyUKooL/4IpCXR1R2ytGjwJ2mpUnhN78B7mxcgsce\nA958M3nncvUq8JGPAM8/n7xzcNmfCBX0118Hamv3Hnv0UWB+nqz0yeDWragpceIEMDycnPMAgE99\nCvjjH4HPfz5534fL/kSooF+7RtTjWO69lzjAJidFjmSNjQ0i6I8/Tn4/cQLY3CTHnGZ+nmQL/vzn\nwNIS8ItfOH8OLvsXoYI+Pw/cfffeY+npQHY2Cbs5zUsvkfGplpGXBxQXAwn2KpTOd74DKAoxZYqK\niO8gWWxuJm9sl+QgVNBHR8kKrqWlhTjqnObll4GDB/cey8tLTlz/hReAujry84kTwOCg8+dAqagA\nRkaSN76L8wgV9I0NINEuvapKHFFO89prQEHB3mMHDxIV2mmGhoD3vY/8/MEPAm+84fw5/OQn5P/6\neuCLX3R+fJfkIUzQ19bIvzu7Bu2hpgYYGxM1knWCQeDOfoO73HVXclaz2dloItHHPkYiEwsLzp6D\nz0f+/9Snkp+t6OIswgT96tVoJpyWxkZgakrUSNZZWADuv3/vsYceAm7fdvY8lpbIv44O8ruiEAfl\niy86ex5DQ+T/z36WOEeddkr+5Cck1HnlirPjuggU9DfeAHJyEv/trrucX70AIBSK1zAeeACYmXH2\nPH79ayA3FygsjB6rqgJefdX8vX6/H4FAAD66HGs4ffo0ACT8+9mzZ3d/3t6OJi55vWSy8futXwMv\nqgr87d8CExPA3/2dc+O6EIQJ+vXriVdzgDSe2NoSNZI1VJWsog8+uPf4gw+Sh97JVf13vyOCHktV\nlbmDsr+/HwDZNRUABgYG4l7j8/nQ0tKCpqamPcd7e3tx6dIl3XOoqnK2hPjyZaLV/fSnxBG5tubc\n2C4CBX18PD5ZhnLffcDKirPlqtQnUF+/93h+PqmoSyAz0rh6lQhWLHV1ZHI04vz58yguLgZA9kLv\nTRAX9Pl8GBwcxGOPPbbnuCc2PRHAL39JwpyUe+5xNjX5e98j13zsGJmEL1xwbuwLF0hF5fe+59yY\nqYYwQR8Z0V/RKyrI/6OjokYzZ2CArGBpCa4wN9fZQptwOL6d1gMPmKcGRyIRlJSU7P4eCoUSfHYY\ngUBgj5o+MDCwqwVQ+vuJU5Ry/DgwPW39GngZGIimIpeXAz/7mXNjf+lLxH/0pS85N2aqIUzQQyH9\nFd3jIfFrJ/PMr17V9xnk5Tm7mr3zzl4hA4iTMIHcxmHW3627uxsdHR0IhUIIBAIAiPBrCQZJay/K\nI4+QSIBT3LhBwooAcYiaaTOiuHmT/KMZiU7lL6gqcTo67fjVQ6iga0NZsaSnO5skMjICVFcn/ltZ\nmbM57wsL8Sv6sWPEV2BUH68oyq7Qzs/Pw+v17vm7z+eD/45Hzev1IhgMJlzNAeJhj81xeM97yNhO\nOEm3t4mPhqYiHz8ejQDI5vvfJ7kUTU1AVpZz1YunTpEiqk9/2pnxzBAm6Csr5MvUo7DQWeEKBvd6\nuWM5dAiYm3PuXLa24p2ClZXEZxEM6r/v5MmTCN55wfDwME6cOAGAqPQAsds7OzsBELX+4YcfRjAY\nhN/vx7lz5xAOh3cdeHNz0XPo6enBmTM9yMvrwX/8x2VxF6rD66+TZKrGRvJ7RwewuupMYc+LL0az\nNQ8fBu4oPdL5n/8B/vIvyXipsKoLE/SNDRJG06O42NmkmaWlaMqplupq54ps5udJbrm2BsDjISuM\nka+g7c4SHAgEoCgKWu/ECqlwd3R0oLe3F36/H6WlpWhra0NXVxe6urrg8XiwsLAAj8eDzU1yDtRG\n7unpQU9PD7KyerCz0y76kuP4zW+IuUQ5coRMck5M/LOzRIMASGbizZvyxxwZISHcb36T/O6k41EX\nVQAbG6oKqOrSkv5rOjpU9b3vFTGaNaqqVPXZZxP/7cwZVVUUZ87j8mVVTU9P/DevV1W/8hX55/Da\na9FziL3lzc2q+ulPyx//c59T1fvv33ssN1dVv/td+WNnZ6vq+fPk5//9X1XNz5c/5te+pqpFReTn\n1lZV/eQn5Y9phpAVnc7M+fn6r6mpcVZdXlqKqopaDh8mqqMTvPUW6W6TiJISZ1a1gQGiUWm5915n\nmoJcvUqSdGIpLJSf77++TqIuH/4w+b29nZiYy8tyx33xRVLIBZDoSjILmChCBP36dRKbNqKhwdkk\nibW1vV7mWO65x7lSTaNEovJyZ1TJa9cS35+iImcKfCYn4/03lZXyPe+vvUaEvbSU/F5QQJzCr7wi\nd9zBQSLgAPD+9ye3lRpFiKAPDemvWpT6ehJPdgJVJZ7e2N51sdCV3onVbHJS3/tfU+NMOm4wGH3Y\nY2luduaeLC/H1xy0tMjX8K5cidckFIXsJCST2VkS1QCI43FpKfl7GwgR9Js348tBtTQ3O5cGOzlJ\nhJ0m6mjJyiLOMCdm2tlZ/fNoaHAmvDU2lniyuftuZ0yY+fl4QW9qkj/JvfFG/HNZUyP3vtPUa2ou\nNDYSbSoZpdGxCBH08XEyUxrR0kI8806EVK5fJ62dNVmge8jOdkbQJyYSr6YAedid0CrW1xNHRO6/\nX745tbVF/mmLi5qb5SfsBIPxjUe8XpK8I4vBQWIe0E1MaHQl2WXBQgR9cTE+l1tLeTn53wmH3OCg\nflYcRVGccYRFIvEPG4VOfrIZG0t8f5qbycQr0zl1/Tp50GMyeQGQBCLZ8eXJyfjv/vBhuVrU734X\n1RgpjY3J7T4MCFzRE3l1Y/F4nMuOGx7eG7dNRE6OM3H91VX9RCLqFJRtv62uJnZMZmYSr7RMtfLq\n1cT1BvffT657fV3e2KFQ/HU3NZHnVRZvvBGv3R44QByiyUSIoC8sxOdyJyIryzgTTBRjY+amRGWl\nM46o9fWoGqeFajmybdWNjcS9/ABiwsj0fg8Nxa/mQLTgSGYvwY2N+NTj++6Tq0WNjMTXfNTXy51c\nrCBE0JeX9QtaYsnNdaaCbXo63tuqpaBA/pdPc9nvuSfx36mWI9NXsLhIJlg9rSIvT67NahSROXhQ\nnkqrqiRmfvTo3uOtrcRkkOUrGh6OTuCUI0eS38dfiKDv7Ognp8SSn++Mury0pB/SolRUEG+wTOhE\nYmTW5OTIFbR33iHmgV6eQ1GR3Fi+kTNyZUXetc/NkedS24+gooJoErL6Bi4tRZNlKI2Nye+6K0TQ\nV1aMK9coiuJMDfTsbPysqqWqiqx2Mrl2zdz7n5cn9yG4cYOs6HqUlsrN+5+ZIWZSIoqK5Gl4V6+S\n717rH6BalCxNIhyOX/SOHJHri7ACt6BvbZGZ04qge73WarB5WV42X9EPHiQTlExu3DBPJCoslKvl\njIwYn0NlpVwfQSikH3XweuVNMm+/rR95URR5TuGFhfhErXvvJTLiVNp1IrgFnXZ31SsJjaWiwpnO\no6ur+pVrFCdSckdH9dNfKV6vXC1nbMw4mampyVjj4GV9XX8RqKyUF0sfG9PX6nJy5DiFd3bIwqd1\nAGZnE9MpmSE2bkEfGjJXTymVlcSGkc3GRrxtpqWpSX6m3tSUvn1KqamRG9ednDSOQMjOt19Y0Pff\n1NTIi3wMD+trMrI0ibExYhZo9x8EiIwkY7ciCregj4wY24Cx1NTIV5dpnrteSItSX09eK9N2mpqK\n7/6qRbY5MztrHIGoq5ObuLKxEe+cotTXy9OqjCZZWX6Jt94iPoFEi56iOBNa1oNb0CcmrKntALHV\nZDsl6Aqhl19OyckhN0SmIywUMg/zVVfL9f6Hw/rOMICstrLiyuvrZDLVm3QbGuSlAIdC+s9AZaWc\n73xwUL9UOz8/uZ53bkEfH9/bRtiIujr5gn7jhv6sqiUjQ25oKxIxn3AOHpS7opolMzU3E9tSRpz3\nxg1yH/Sej+Zmef6BSEQ/t6O2Vs4EY+T49HqTs1sRhVvQ5+asr+gNDfIeKsrwsHEDjFhycuTOsla8\n//X1cie/lRXSI08PGuOX4RAMBo37FDQ1EdVdhq9keVn/uuvq5JiQs7P62lN1tfxwrhHcgn7rlnm6\nKYXaTDJTT0dHiUPE6vnIjCGvruqHliiNjXKdgrdvG4c+aVxZRlfWYNC4uIguEDIyFNfW9J2ADQ1y\nJtexMf1FpqLC2T76WrgFfX7e3A7dHSxNblYSQGLCVieejAziY5DF+rq595+ql7JirEY2MiU7W46j\nyOjBp2RkyJlkVFW/w9Dhw3I6DBn5BWprnYk46cEt6Ldvm5eoxpKZKVfQp6as+wzKyuSGtra2jFtg\nA+T78HjkCBpVi83Skw8ckJOhNjlpbtZlZ4t/HtbWiCDraTJUpRddnhuJ6MtCff27PGEmEkkcN9Qj\nO1tuMYkdUyIvT15WGLUBrVT1ZWbKqY0fGYk2PjCioECOZjMzY67t5eaKj+MHg8bXnZ5O/ia6as/I\nL9DU5EzvAT0cX9Hz8uTaxeFw4rLIRHi98sI7VoUMIJOfjBX15k2iGpshswbBLDVaxiQzNGSu1Xk8\n4ifXtTX96z18mDiik9U7jlvQNzbMPcux5OfLdUosLlrXMMrL5dlNIyPWhAwggiYrU8vKRCPLIzw8\nbJ4wVFwsXqsaGzPvSpyTI16TUFV9U41GN5IVYhNS1GLmWY6lqEhuO6nlZfPYNaWyUp7dNDZm3VeQ\nnS1H0MfHzYtqAGJHy4iELC8bJ+sAZFIWPdlOTJhPMPn5Yk3IrS2y6Bk5PtPTk5cdxyXoVBUxitNq\nKS+X65RYXbVuSlRVyUvBtGKfUmQ87ADRnKzkFHi9cgT99m1zH4WiiC90mpoyv+6yMrGpx9T0Mmph\nlpWVvOw4LkGnX5RZv7hYFEVuWeTamrVuNwDRRGQ5SKamEvdKS8SBA3LMGavJTBUVchJI1tfNtb3y\ncvFmw9ycuUM2N1esGm2WHASQ++zk/oOxcAn62Fg0Nm6V0lK5hS2bm9Y1jIYGeckqt25Zz9ArKZET\n5rt1y9okXF0tR7PZ2jLPI5BhPoVC5g7ZkhKx+e6jo+b+kPx85zb31MIt6FYdThSZqjs1Jaw0wQCi\nKr6MXPNw2LwWnVJaKqfl8vy8eZksQDQg0ZrN5iZxTplNutXV4r//SMT8usvLxUZcxsbM/SHl5fLb\nl+nBJegTE9ZLVCmVlfLsYvolWrWN6Y2REUOORKyH+crL5Wg5ViMQhw6J12zoymXmFDt0SHyW2uKi\nuROwslLs5GrFL3DgwLt0RZ+asubVjaWmRp5dPDJi35TIyJATw15ctD7hGE1+fr8fgUAAPp8v4d9P\nnz4NAHv+7vP54PP5MDv7nCXH5MGDZPUVKXAjI8TLbFadVl8vfpJZWTF3yFZXi9UsZ2asdRN6V67o\ns7PmGyVoqa2VZxePjpqvIFoyM+UI+sqKeYNKSk1N4iKL/v5+AEBHRwcAYGBgIO41Pp8PLS0taLoT\nwA0EAujs7ER3dzc2NoJYXAyYjk/voUiHoBWbFYgKpEihW1szNxkOHRJb2GLlfpeVJa+CjUvQb90y\n31xRS20tWT1kZAhNTNivb5alTt2+ba4+UvQmv/Pnz6P4jjetsbERvb29ca/x+XwYHBzEY489BgAI\nBoO7r1PVRmxvmwduaQWbyNCPFVUWiDYAERnT3tw07xlYVydWg5meNo9wVFXJ35tdD+7wmtW8cgpV\nb2TEba20btJy4ICcBoXr69bj+QcPJq7Tj0QiKIkx9EMJAr/hcBiBQABnz54FAHR3d6O7uxsAsLPT\nj/b2Y5bOITNTbOhnasq6/0bkJENbiZk5ZGkrse1tMeMuLpqv6DIcj1ax6TPfy8KCvWQZgMzeaWlE\ntbPiEbZDKGT/M/Pz5WTqbW5aj+fTc15cjLfzVJMuHVSoL126hEAgsKvmv/pqP4CH8NGPtsa9p6en\nZ/fn9vZ2tLe3IydHrGYzO2td2xMZdqKedDOho6vv7Ky9Wg09bt82H9OJVmp6cAn64iKbsNI68Acf\n5Bk9npkZ++G+oiLxzRnpSmF1EqST382bwAMPRI8rioLwHdVnfn4eXo13z+fzoaSkBF1dXfB6vQgG\ng7uC/uMfB5CW9rWEjR9iBZ0iOoEkFLIeXkxLExf5oAUtVkw4qkmIEPTVVfMswLo6+Z2H9eBS3VdW\n7JWoUrKy5CT3z89bf7goxcXiK9hoOqud7yYjI151PnnyJIJ3kqOHh4dx4sQJAESlB4jd3tnZCYCo\n9ceOETX93LlzePzxU0hLI845KxQXi/UIRyLWMyZFlgvb6TAk0hG7vm6tbZiqJkfYuQTdTl55LDk5\ncgR9YcFeOi4gJyuNXpvZHu2xZGXFq69tbW0AiLAqioLWVqKGU+Hu6OhAb28v/H4/SktL0drait7e\nXjz33HM4ebIZW1sl8Fj0Th44IDY1eWHBeh6BSPNpctK6oBcViYs0WCnuotGNZFSwcanuVmaxROTl\nyXGALS1Zj11TysrEe0JHR+2bEDk5iR86aoNTlRwA+vr6dn/u6ura8/rOzk6Ew2F885vAV78K3HHG\nm1JUJHZFX1qyHl4sKhLnnJ2ZsZ7bkZYmxjegqtayAIGouWCn4lMEXCt6ZqZ1h1Ms+flytmZiMSUq\nKsSn5E5MmBc4aMnNFbuiTk/bS2YqKREb411dtR5eVBRx5pOd3A5RCw6dpKyYjU7tKKyFWdC3tohg\nWWmVpKWwUE6G0O3b1mvRKTJKVScn7antAPFQi5z87BTVAOJLZdfWrGt7IicZO9ddWCjGETs6an0v\nAZGORzswCzq1a63aYbGInMFjsRO7pshIyZ2dtR/PF938IRSyl8wkutjITnhRZKefSMS6Q1ZRxCw4\ndoq7vF65jVf0YBZ0erJ2U2ABcrEyUgHtPFyUgwfFJU1Q5ubsraaA+MnPbgRCdLHR9rZ5dhqlokJc\nIokdb78oR+zkpPXkoLS05PR3Zxb0iQlrRQuJkFWTvr1t38lx6JD4kEcoZH33Goroyc+O1xsgarYo\nzYaWqFqddEWaT3aKiUpLxXzndvwhBQVyNzDRg0vQ7ZaoUmQ4wGjGkV3VnWokokNLdlODRXv/l5ft\nJTOJLDYaHycrl9XnQ2Q9vB2HbGWlmHFnZqybakVFcvcS0INZ0GdmrDc/1FJRId4BRh0cLJOP6A0M\n7K6mgPjJz24E4tAhcfvi0c5DVhFpPtlxyJaXi5lc7RR3FRe/CwXdbi06paZGfLMBugk9Czs7YvO8\nl5fth/kqK8XmQdupngOiE5MIP8H4uL3wIjWfRAj7+rr1666qEvOdh8PWTTVZ3YTM4HLGsTjiADk1\n6Syxa4rodsurq/YFXXQ7p40Ne2ZMbLERL9PT9rQ9GooUkTG2sWHdNyDqObSTkSm7Z6IezIJuZxbT\nQmdwkTXpU1P2Y9cU0V1Y19bsraaAeO//5qZ9x2SifHsWWLS9jAwxGyqoqvXrpuYKL3YyMmWYrVZg\nFnSWAhIKDT2JTBCZnWU3JXJzxcY27a6mQLSdk0hNx26oMVG+PQss2l56Ov+KvrVFBNdueTCvKm3H\nVBNlLtiFWdAXF+0XkOwZWJCaSGFpa0UpKBBbqrq1ZT9jkE5SIrz/Kyvkgbeaa07Ry7e3i91kHYCo\n+ryCTicpOzFtj4dfi1ldte4ArK5+l1WvLS/bLyCJJTNTbPsgloeLIrKgg8aQWYp9RG22OD4ebQ9l\nB1H59izaXk4O/9gsDlkR5kp6ur1uQqITtKzALOisteiUggKxdvH8vP3YNUVkyIN6rVnPRYTqTJOZ\n7JKfL0azWVy0H17My+M3n6am7FcNZmbyf+cLC9YndjohOG2nMwu63fCNFo9HbF2unbCKlpIScbnW\n09Pk2lgcg6K8/6wRCFFNOJaW7At6fj5/xtjUlP3cDhHmip2NRjMyyPPhdGELs6BvbPAJumgHmN1q\nrVhEZqWxrqaAGPUVsB/eohQUiDFh7LS6pogwn2Zm7E+whYV8TmHqWLNTNZmMCjZmQWcpIIlFlJpI\n4TElRBZV2Clw0JKXJyYSwRqBEFUuylIuXFTEr02wePuzsvhq0un9suMfysx8lwg6jYHzCLromnSW\n2DWlupqtOCcRrKspIK4hB6t24/WK0WxYwosizKdQyP5185YH07x+O8+PiAiDXZgEnT4MPO2aRZdl\nstSiU6qrxdnoLLXoFBGrGsAegSgvF6fZ2I06iMgYs1OLTuE1GVj8IbL2EjCCSdCpHcmaiQYQx4/I\njiZbW2whLUBsyOPWLfZ4vqjJLxJhy1oUVZNuZUskLSImGRZBVxS+59DORhUUEREGuzAJOo1X8qi7\nopP7WWrRKdXV4jYZ5EkNFuX9Z6meA8TUpFNhtWuji5hkWLz9paV83zmLA1B0gpYVmASdx+FEEakm\n0s9htdHpjRIR12dZVSiiCh6Wl9kFnVezsZudFjs270S7smLfnCwr4ysPnptjaxsmo5WaEUyCzhKv\n1CKydRF9uFir1wCioYgo6GBJFqGI6tvGGoEQUeQxPm4/aQUgKcO8qaGrq/bDerwRFxbHZzJq0pkE\nnaeAhCKyddH4OHvsmiIq5MGTGiyqJn1tzf4DD0Sz+XhWG9ZkHVrUwzPRsCRNVVXxPYcs6b5er1j/\nlBWYBJ2nFp0iQk2kTE7yreaAuG2iWGrRKaIacrBGIGiYiKcGgVXbo34NnlDX5qZ9hyyvJsHSNiwZ\nNelMgs5TQEIR2bpIhCkhaksilmQRigj1FSDCyhpqzMjgE3QebS8tjc98YqkarK3lW3BYTLXy8ndJ\nrjuPw4lCZ0ERtooIU0JUyGNjgz3MJ6ohx+3b9sNbFN6adB5tj2eSoYuG3cgLNXFY7XQWB6DotmFW\nYBJ03lp0gKw6ImqBATGmhKiQB088n14Dz4TD2g2Xkp3NF30Ih9lrDrKy2Mem985uaDMrizyHrJOb\nlX3RtYiIMNiF0Rl3masWnUL3SeeF5+Gi0FTIy5cvM3/Gzg5/arBWffX7/QgEAvD5fAlff/r0aQDY\n/TsRFD9++1v99xhx4ADfRMPTeYinkoxui8Ry/3giLk63DWN9PhlX9MtctegUUa2LeB4uCg158Ag6\nLQjhbchBJ7/+/n4A0Z1UBwYG4l7v8/nQ0tKCpqYmAEBvbz/S0ozfYwRvvj3L1tUUntTQyUmycLDc\nP56Iy+Ym2zZgrAlajgr61ha7wykWXjWRsrjI3uiBUlLCn6k3PU1WFZ7U4NjJ7/z58yi+IzWNjY3o\n7e2Ne73P58Pg4CAeu7M/8vPPn0d6uvF7jODdeJAnvMhT0ciSikrJzmZ3xG5t2dfgRLYNswqToG9v\ns9uAseTmiknuZ9kXXYuImnRaycRDbE16JBJBSYxLN5RACsLhMAKBAM6ePQsAmJuLICvL+D1G8O4k\nwlMuzFNJxrOhCGsfgK0tsjKz7Cicni62lZoZHlW1H+DyiKrpdHFxsQ2DyLKt6MePq5iZUaGqfP+O\nHVPx+OP8n6MoKs6e5fuM739fxYEDfJ/xz/9MzoXnM44cUXHyJPn59OnT6O3thaqquHDhAr7+9a/v\nee25c+dw8eJFqKqKr3/96zh37hyOHj2Nigr999AHRe/fM8+oqKtjP/+cHBU//CHbe0+eVHHvvWzv\nffpp8t2xvLe1VcUnPmH/fa+9piItjW3MggIV//IvbO9lgUnQX32VLcVSi6gN53hq0SkistJExPNj\n1deTJ08iGAwCAIaHh3HixAkARKUHiA3e2dkJgKjox44dQ2npSWRkxL/HKrxFHizZaRSeikaWphMU\n1pr0iQm2vH7A+Zp0U0Hv7++H3+/XDdWYhX+MKC4W07qIxfNJodf38su+hCEPbfjKCJZ90bXE1qS3\ntbUBAAIuckcLAAAd1klEQVSBABRFQWtrKwDsCndHRwd6e3vh9/tRWlqK1tZWqGobcnKAf//3f8cX\nv/hFPPnkk2hubsYzzzwTN1aie8eTtaXe2T+NNbxoZ5Lp7+9HWloampub0dzcjIGBZ+IcslbvnaKw\nPYc8VZxW/FNmsmVH9kwF/cyZM+jq6kIkEokL1VgJ/xghqnURTy06vT4gAlUdiFvVteErI0TE87U1\n6d3d3ejo6EB3d/fusb6+vt2fu7q60NXVhWeffRYAeWDvuqsbLS0tUFUVN27cwIULF/Dcc8/tGUfv\n3vEUG9EJgrVc2E5F4/z8PHZ2dnavr6DgubhUVKv3jrXIhKUWnWKWoDUwMIDGxkZ0dHSgsbGRW/YM\nBf3ixYs4duwYAODUqVO7KwzFSvjHiLIy/uT+7W2ykrCsIrHX9w//cApAW5z3VRu+MiIS4Q/z8U5+\nNGuRPgAAmRjq6+v3vE7v3vGYMLydh+xUkmmvb3OzPqGgW7l3rEUmc3PsppqVjrtUIwkGg2hra9vz\n3diVPUNB7+vrQygUwsDAwG74JhYr4R8jRGw4Rx8uli9ce32JMqS04SsjeJJFKLwNObTVc4FAAE89\n9VTc6/Tu3cGD7Ln2tFyYNSjDUtRDry9RLbrVe8dak87TYlxRjP1TbW1taGhoQElJye59ik0usyt7\npqp7aWnp7kru9/vj/s7qBQT4a4EBIpg8sevY60tL88dlSFHVORQKIRAIGH4Wa2eXWKqq+Ca/1dW9\nRRaXLl1CkU7aYKJ7R9VulnPg2boaIFqZ3YpGen2JUlGt3jvWIpP5efa2YV6vsV8gEomgubkZPp8P\n3d3dGB4ejluE7MieoYh4vV40NDQAABRFwZUrV/b8XVEUhO+4iOfn5+G1mbUiwtPN83Bpr8/jubKn\nJt3n8+1Obl6vd9cDrgfLxgVaeCc/bfMFastp0bt3PBsP8rYYo5qIHdOFXp+2xbSde8daHsxTxen1\nGpsLPp8Pn/vc59DV1YULFy7g4sWLe1Z0u7JnKOhPPvnk7hcUiURw/Pjx3Z8B/fCPVegMzgNP6qP2\n+g4cOI7paePwlRFra/ypwbz10bEPvNHDbXTvWLO2eMOLdhtfxF4frUVnuXcHD7IJOk/bMCvmQuEd\ndaGjowOKomBqil32DAW9oaEBiqLA7/cjFArhiSeeABAN7+iFf6xChYLHJp2dZXf+aK/P630Cc3PG\n4SsjePZ/o1CnIodFtJuS6fF4dD3ORveOdeNBEeXCdmrSY69vZ4fU4LPcO3rP7GpSy8vsexuY+adO\nnTqFs2fP7oa2u7u7MTPDIXtqkvF4VHVwkP39f/M3qlpXJ+ZcHnhAVZ94gv396emq+tvf8p3Dzo6q\nAqoaCsl5r5Vbriiq+o1v2B//L/5CVY8etf++WHJzVfXb37b3nsVFct07O2xj0u9tdNTe+7xeVf3K\nV9jGfOEFVc3MtPee7m62sVRVVTlLMPjh7b566xZ/WysKz64dvMkiFI+HvaUSTbTh9fyzttVi3Tgi\nlpwc+737RkejjUxYoHvJ2zVXeDQ4FnOBp6ow6YLOuz/1/Lw4Qedpw0szukSU77K2VBob43vgKayb\nPYoIL7JMMqwtpmPJyLD/HPLsKFxba79tGE/jzKQLOm9NOksXTj14dkoRsU0VJTOTrSOtiAceYN+r\nnMc5FTu23ZVraoq/CzDLxocstegUlq63POniSRd03j3BFxf5a9EpPDXpdJsqEbCor4CYHXQAdhOG\np9U1haV33/Q0/wRr9zmkqzFr6jWLicbTCz7pgp6Xx2d7rKyIE3SeXTsmJvjbWVFyc9n6tvHkXsfC\nWmzE0+qawrLRpIiqQbtNUBYWiKDyaDB2eybypIsnXdB5u6+ydOHUg6cN7/S0uA0pWPu28WzZHAur\nCbO2xl8uzDLJ8OxgS7FrroyPk1WdR4uz2zORJwyddEHnLeIQkaRC4S3o4F1VKKwtlUQ88AB7kQdP\nLXrs2HYnGZ4dbClFRfa+87Exfn+I3V51PL3gky7oBQV8XUd5NkzQwtOGV0SyCIV1n3QRDzzAZsKI\nCi+yNL4QtaGInYiLCH9IcbE9E40nNTrpgs67JziP51MLzz7p4bDYMB+LjSzigQfI92A3REeFk9eM\nYtllV8SGInafw+lp/m3AsrLs+QV4TMOkC3p5ObuTgeQzsXs+tfC04RXRW57CqjqLCG8BxM62O/lS\nPwtv4w2Woh6eFtMUu70RRPhDrNSkU3j3KUy6oPN4uql6K0rAAPZMPZHxfNZ90kU88ADJGbe7ekxM\nEC80b7vr2lr7GpWIqkG7JgNPLTqluNi6icZj3gIpIOhVVexOBlqLLrL7NOuuHaKEDGCf/HZ2xDgm\naVGMHYEbH+dPWgHYKhrX1vjj93YjLiJMtZIS6yYa3XKKlaQLOs9WwbSjiUhY90nn2bhAC6v3PxIR\nI+jUyWRnwuMpF46Frsx21Oj1dX6HrN2NDyMRfr+AnQQtno6zQAoIOvV0s9gfoh6uWA4cYEvJFRnP\nZ538REYg0tPJKmIVEUkrQFRDszM2y77oWmpr7X3nCwv8/pCKCuvmwswMX0Ql6YLOMoNTRKQ+amHd\nJ11Eb3kKVV/tqrBbW+Ick3ZNmNlZceFFu5VkIsJ6NOJiVdhFmGp2IgyTk3wmatIFnQoqi7osahWJ\nhXWjv81N/lWFQlVCO7F0mnutafbKTE6Ovawtng0UtBQWWh+bCgqvyVJYSCYYq/d+dZV/TDvmAq+X\nP+mCTmuBWTzdIjyfWlgLOnh6y2uhnVTtrKj0ARUVgbBbLsrTKDERVid+WprL6whMSyOTpdXncH2d\nX9Dt1KQvLvKZhkkXdIA8VCyebpG16BS7GVJANO4ryj4GSDKGHTuVemVFRSDsmjAiatEpubnW/SQ3\nb4rT6uwUmayv82twhw5FMwrNmJ7m80elhKBnZLCp7iI2TNDCsmsHPXfeTKlY0tPtfSeiatEpdvdJ\nv32bv18epaDA+iQjcuvh7Gzrgr69zS/oVBu1kh03P8/n/EsJQbczg8ciIvVRC0tWmshadIrdmvSJ\nCbERCLsmjMhJ184kMzkpziGbk2PtOaT+kLo6/jHT04GREfPX8fZdSAlBz89n21lSZJIKxU7IgzIx\nIXY1B4jqPDtrfSO9H/3o7K4Ky7PxJaWkxJ4Js7oqbkW3U9QzPS3O22/1OaRVbiImtsxMayba8jKf\nTyAlBN2umkjZ3hZXokphydSbmhKrNgPkoXvnHWsb6fX29uLatUvIzTXfnM8qdk0YEUkrsWNbnWRE\n7GBLsdoH4OZNcf6Q7GxrEQZe0yglBF1R2DzdkYi4JBWK3QwpQGwtOqWoCLh+3dpGeh6PBxsbZMJU\nVTVucz4WqqrsJZBsboqLOtjJGLt1S5zJYLUmXUQtOiU315qJtrbG5xNICUEvLWXrvrqxIS52TWGp\nSZ+dFR/mKy4GVlfNN9IbGBhAR0cHtraIoD/44INxm/OxUF5u/Z5Qz7EoQbdjPolIRaVY7QI8Pi7O\nVLNqLvAmQ6WEoLM2ZRSR+qilpsZehhQgtrc8xeslE5lqkhtM99/a3KTVUPGb87FQW2vdhKH3TpR2\nVV1tPWNsYYF9txQtVVXWtLnpaXEaXFGRtQjD9jaf80+wZclGRYV9T7dIz2cssTXpVieRcFh8mK+s\nDNjeNt5Ij67mAJmYSkujm/MVFhZCURRcvHgRp06d2vO+np6e3Z/b29vR3t4eN/6hQ9ZNGOqpFvXw\n19Zar0lfXhY3wZSUWBO6mRlxDsDiYnOzlU6kPP6olBD06mr7DjCqYon2ugPRTD2rgr60BDQ3iz0H\nclNPIhjsQ0dHx56N9CKRCBRFQTAYRDAYRCgUwtZWGDs7xPEWuzlfoo0WYwVdj7q6qEpuFjq8eVOs\nM9KO+STS219VZc1kEKnBlZSYx+6DQeKd5ylTTQlBtzODU3i34THCbhvehQXxE051NbC93QagL24j\nvc7OTvT19aGrqwsAWcW3txfg9Xp2N+drbGxEOBxGd3c30/hUQ5mbMxek8XGxMXy6i8nmpnlqq4gM\ntdhxrSw4KyviCpjKy81r0m/e5H/OU0LQDx2yX5Yp0vOpxWrIg7KyIm5VodBOK1RQqYoOAH19fXte\n293djc9/vht3drWOU9VZoBsMjI6aX9vUlNgqQlq8MTFhXqQj0ttv1VyZngZ0Nqm1TVWVudkqwvmX\nEs44Gn+10xRQdCZYLHYLOm7fFpvnDtif/FRVvGPSajKHyKQVSkaGecYYNS1E+Wnq6631ZuMtMIml\nttb8uZ+Y4Pd/pISg09CUnTRYUbuSJMLuTinr6+JWFQqtr7Zi0qgqOQfRjkmrud8ik1YomZnmlWTU\nSSVKjfZ6iRZjlry1vCxOg6urM7/H09P8329KCDotVbVTrSXS86nF7u4xW1vihSwry3r5rugHnmI1\nmSMUEtugEyArmFnBys2bZEIQVWdA7WAzTYI3eSWWhgZzx2MoxB9CTAlBB6zN4LHIqEWn2NkphbbB\nEi3oAHnwbt40fx11TIpozhhLfr41E0Zk0golL898krGzMFglMxMwSz0QuZcAnZyN0o1nZ/lr/VNG\n0O12NFlcFNeMUYudfdLpwyhj0snKsjb5jY7KcUwWFlrL/RbRP01LQYF5xtjNm3KKicw0ifV1cc64\njAwySRtNLvPz/M96ygi63aaMInuUabFT0DEyIr5ElaIo1uqtx8bkOCYVxZpmIzJphVJWZp5IMjkp\nvsYgJ8d4ciXZimI1uMxMY3NhcZHfLEsZQc/Pt+cAW1wUl/qoxU5N+tiYPBMiI8OaliPjgQes1yDs\n7IiPOhQVma/os7PiNYmiIuMFZ2SErMAiNYmCAuMJXURUJ2UE3aqaSFlaEl+iSqmpsa4tjI/z706i\nR2GhNRtZRngLIN+DlQy1SES8oFdUmE8yk5NitomOpaTEeIIJBuVEGIz8DSsrxGnHQ8oIupWc31hW\nV8V7mSlVVdYdgxMT4h82SnGxdRtZtOoMEPXZSmXVxoZ4Z2R1tXmhkwhvtJbSUuPvnLFGyJCSEuMJ\nfXOT3yeQMoJup9kAQBwihw7JOZf6euspubyN9Y0oLbVmI8tY2QDy/ZptDUWTVhobxY5dW2uedx4O\ni5/gKiuNn8PRUfHfdUGBvom2tUVMI95aipQRdLulqpubckJaAHlore5eKbLxgZaKCmt7c4XDciIQ\njY3mEx4VCtFmVH29ed65CCeVlpoaY0fs5KT4ib2sTF+LoHnuvKZZygi6nVJVuouJqBCHFqoOWnEO\nhsNyKugA8hBbmfwWF+X4K1pazLfLGhrir6xKBJ1sjVhZERfPpphpMTMz4nMGqqr0zdbBQTGh05QR\n9Koq67nu1G6UtZLSgo4EFZ5xyLKPAZJWa2VX1eVl8c4wIDqBGdmPQ0NynJEHDxJBN5ro1tbEa3UN\nDcaaxK1b4rWn2lp9zW14WEyqd8oIup1N7oLB6G4mssjKstaGd2lJfOUaxUoeNEAmA9G59kA0NfnG\nDf3XiNxAIZbMTDL+0JD+a0Q4qbQ0NxubbaJ2rI2lsVE/ujE+LmZiSRlBb2y0Lug0x1kmOTnWUixX\nV8VXjVGamqx9J6oqbs81LdnZxprN+Li8PIL8fH1B39oi180bdtJSVUU+V89sy84W/123tOibrSMj\nYpKhUkbQqapmpcPH2Ji8yjVKXp61ZBWZ3v+aGrKqGTmHVJWosLL8FQcOGOfbT06KL2ihpKfrC3ow\nSL4b0ZMM1WKuXUv895ER8X6Be+4h2kmiSX16WowPKGUEPSfHPOeXMjUlL/2VYpYhRdnakreaUieM\nkepMw2+yzIeCAuNS1VBIXuJSRYX+ZCvKSZWInBzy+YlYXwfuukvseIWF5NlPpDktLoqZWFJG0AGi\njhvZZJTpaXmxa4rXax7DpnnPotXHWAoKjAX9xg3iDJOVnacoxhPe7Ky8qEN2tr6fZHBQjm8A0DcZ\ntrfJv3vuET9mZibw9tvxx6emxIQQU0rQs7OtlWXOzcnzuFPMUiGBaNWYTO0iLc1YyxkaktdpBzBP\nRY1E5Pkoysv1Pf43b4pvsU1RlMT+GXpMdHiNjplIi1hcFJOMlFKCbqVEECAPnix1kVJTYx7Xv3FD\nrucfIJqL0eQ3OipvZQOISWDUhGNpSV7iUlWVfiLJ2Jj4ghZKTU3ica9elTepFhQkntC3t4F77+X/\n/JQS9MJCaw6wUEieukiprjZX3YNB+U7B4mLj72R2Vp6gAUTYjDSbtTXx6a+UQ4f0tYnpaXn9CEpL\nE9c6vPOOvEm1uDjeRt/eJs7p++/n//yUEnSz5H7K2pp4z6eWhgbzCMDIiHynoFlhiRWfBg+NjcZJ\nK9vbwOHDcsZuaNBPGFpelhftqKtLnKk2MiLP6VlbGz+hDw8TjVFEQlZKCXppqbVebQsL8m4ypbnZ\nPFllYkK+r6Cy0riaamZGXmYeQDzMehmLy8tyClood9+tP9lOTcnzDRw+nLjt8/CwvIm9sTH+Pg8M\niDMVUkrQKyqs7Yu9vi7v4aIcPhzNqddjakq+CXHwoLEzLBSSV64LEPuQepu1XLtGnIWy7NYHHiDj\nJppwl5ZIookMjhxJvKJPTsqbXO69N35Se/NNcQ7HlBL0ujrziiVaFilLXaTQldqoLv3WLbmrKUBi\n9Eaqs2zthrZATuQQvHZNrse/oICoromSV0Q5qRJx//1kgtdqMjK23qIcPRov6CMj4kK3KSXoNTXm\nzSeoai/LVoolO1s/QwqQG1qiNDcbV4+trMjLigOimWKJYrw3bsg3XbKzgT/+ce+x1VWyyt93n5wx\n8/PJdWvHnZ6Wt8A88AC5ptjn/+23xYXyUkrQm5rMV3SaESUrQSSWrCxjZ5fM0BKFqnRGwi5rlaHk\n5iae8IaG5MWyKQUF8WO/8Qa5/zLDisXFxEaOZXUVePBBOeNlZRHT9eWXo8cmJ8Vl4aWUoB8+TB5o\no4f6xg25NziWsjLjGLZMRxSlooKsqInSUDc3ycMna2WLPYdEGWp2tpZmpbk5vr/71avyNYncXDKh\nUBYXyXP5wAPyxtzeBl59Nfr76irQ1ibms1NK0CsriW1klHI5NCS+l7ceOTnGWWnLy+LznrX86Ed+\nAAGcPeuL+9s77xAVs6gIOHv27O7x/v5++P1++Hzx72GhvDyxr2J0VE4dfCy5ucBbb+09dvWq/Mm+\nsXHvvX/1VfLcycybaGqKmkjb28T/8sEPivnslBJ0j4eo5UZ28diYvGopLWVl+ruFrKyQGV6W5xcg\nAgsAeXkdmJoCBjS65JtvEpWvt7cXly5d2j1+5swZdHV1IRKJxL2HhdLSxBNeKCTXPwCQz9fGlwcH\n5WdGHjq0V4v5/e/lleNS6uvJJAYAr79OFj1RGmNKCTpAZk29yiGAqLCyUh+11NXpe7zffltuaAkA\nzp8/j+LiYlRVAVtbjejt7d3z9+vXqeMomod78eJFHDt2DADZPrlNgO53992JS2Vv35arygLk87VJ\nM+Pj8k2mtra96divvy4/wvLBD0aTo/7v/0jYVFSKdcoJen6+sbo8Oyt/NqckylaiXL0qP/01Eomg\npKQE+fnkoQtpsolGRoDi4oE9e6dfuXIFoVAIAwMDe9R5Hu6+O96coju4ynJOUR5+OD4SMzcn32T6\n0IeiWhtAzBTZvpA/+zMy3uoq8MorYp2sKSfoDQ3GabBLS/Kz4ih33aWfmTU4KF+VAwBVVdHUlDhp\nZmgIOHBgb0K+x+NBaWnp7kru9/u5z+HBB+OjIcPDpBZfZokuADz0EBkn1oRaXASOH5c77tGj5H/q\nH7h+Xf6kdugQ8T384Afk+RJlnwMpKOiZmcati1ZX5Ye0KEeO6Av6xIR8zUJRFITDYRw8CMzOzsOr\nScMLBgfQ2tqx55jX60XDHelTFAVXrlyJ+9yenp7df5cvXzY9D5qYEqvKvvoqcZTJrt7LyCDm3K9+\nRX5fXSUr7Yc+JHfc9HQS2vvxj4ljLCMD+PjH5Y4JkMXlX/+VJGP99V+L+1xJPTrYqakB/vAH/b/f\nukVUSSc4ciTa3krbtH9oSL6v4OTJk+jr68P993dgbW0YJ06cAEBUekVRsLQUxPZ2EOfOhRAOhzEw\nMIAnn3wSFy9e3H3d8QRLX09Pj63zSE8nE/BLLwEnT5JjfX3ym39Q6upIfPnpp4HLl4km5YRD9pFH\ngN/9DvjZz8jkIlt1B4BvfhPo7AROnBC7oKXcil5fr1/Yoqoke0hE2Z4VcnKIsy3RxBOJyPW4A9hV\nvzc3A1hfV3D0aCsAoLOzEwCwstKFp5/ugsfjwcLCAjweDxoaGqAoCvx+P8LhMJ544gkh51JcDMQq\nB1evyuk8m4imJqI6A0AgIL++gPLhD5N7//3vy7/XlLY24oc6f17wB6spxn/+p6rm5ib+2/Q0SafZ\n3nbufPLzVfX//b/44yUlqvrVrzpzDpub5LrHx6PHNjZU1eNR1bk5e5/Fesvb21X18cejvx85oqp/\n9VdMH2WbL39ZVRWF/Pzoo3vPQyZLS6paVqaqRUWq+m//5syYski5Ff3ee/Uz4/74R+fSXymKQuLV\nWjY3ndMsaLuqV16JHuvvJ+q0rK2jtbS07E1cmZwkHnEnePrpqG3+hz8Q1dYJ8vOB//5v4O//Hnjm\nGWfGlEXKCTqNmyZq43T9utySzETU1sZnhe3sEO+/Uw86QAQ6VnV+6SX54b1Y2tujJcQbG+RnJ5xT\nAEmN9nqBU6eIB/4zn3FmXADo6ABOn3Z2cZFByp1+bi75UmmGUCxvvinfy6ulqSk+3/2tt8h5ODnp\nVFSQpA3Ka685U8FH+fjHSYhvZgZ44QXioHMq+gEAn/408N3vEm+7Uzb6nxIpJ+gA8ebGFhRQhoac\nfbgBkrSgXdFffZXEO52cdI4e3VvYEgxGY71OUFREtIrvfAf40Y/kZ8Rp+drXgN5e4gF3sU9KCnpB\nQXwhA0DiuE6uIgBJzNB2OHn9decnnA98YG8L4mvXxFU2WeU97wF++EMSWhOZzGEFj4eEu9LTnR33\nT4WUFPTy8mg4JZa5OfmdZbS8//3EHo/NeX/zTeey8yh//ufkPMJhYqeurJCUSSf5p38iWtXcHPDc\nc86O7cJHSgp6otxqgGRIOamuAkRlzcgAXnwxemxoSH6utRZFIQk6P/wh8ItfkGNOJHDEcvw48Pzz\nJInEqXoDFzGkpKA3N8fvlLGzQ1T3Rx5x/nzKykhGFiUcBt73PufPo6WFhHu+/W2SA+60YxIgKrvs\nyjEX8aSkoD/8cPzG8G++Sewz2f3cE9HcTOqRAXJey8vOhZZi+cIXiLf9F78AnnrK+fFd3r2kpKC/\n732kkCA2lv7SS0SNTsYq9oEPRENsP/0pcRYmI8TzyU8CH/sY0Wq+8AXnx3d595KSgl5SQmLpsery\nK6/I2dzOCk8+ScyG9XXg5z93rqhGi8cDXLxIzuHdnsDh4iwp+7iUlAC//nX09+vXnXc+UdraSBba\nf/0XcOkS8OijyTkPFxdWUlbQjxzZmzQzPi6/8N+Ij3wE+Md/JKmfbmjJ5d1Gygr60aPRlM/tbRJu\nczpuHIvPB7z3vSRDS3bvMBcX0XhU1aiLevL4zW+AT3yCNJr45S/Jz8vLyXHG/Snh8XiQorfcRSIp\n12GG8uijpIrt+eeJp7u21hVyFxdWUlbQPR6SifWtb5G8989+Ntln5OLy7iVlVXeAeN0ff5w0XQgG\nnem6+qeOq7rvT1Ja0AHSVvjAAeerxf5UcQV9f5Lygu4iFlfQ9ycpG15zcXERhyvoLi77AFfQXVz2\nAa6gu7jsA1xBd3HZB7iC7uKyD3AF3cVlH+AKuovLPsAV9BTH7/cjEAjA5/MZvu7s2bOWjrnsT1xB\nT2H6+/sBAB0dHQCAgYGBhK/r7e3FpUuXTI+57F9cQU9hzp8/j+I7jfIaGxvR29ub8HWeBPW7iY65\n7F9cQU9hIpEISkpKdn8PhUJxrxkYGNhd8Y2OuexvXEFPccwKUMLhsKVjLvsbV9BTGEVRdoV2fn4e\nXk0zeXc1d7FKynaYcQFOnjyJvr4+dHR0YHh4GCdOnABAVHpFURAMBhEMBhEKhRAOhzEwMJDwWJtm\n29Wenp7dn9vb29He3u7gVbkkA1fQU5i2tjb09fUhEAhAURS0trYCADo7O9HX14euri4AgM/nw8LC\nAjweT8Jjsbi16PsTt/GEi8s+wLXRXVz2Aa6gu7jsA1xBd3HZB7iC7uKyD3AF3cVlH+AKuovLPsAV\ndBeXfYAr6C4u+4D/D7yUXoFdkz+AAAAAAElFTkSuQmCC\n", "text": "" }, { "output_type": "pyout", "prompt_number": 11, "text": "" } ], "prompt_number": 11 }, { "cell_type": "raw", "metadata": {}, "source": "Another nice example of a parametric plot" }, { "cell_type": "code", "collapsed": false, "input": "alpha = Symbol(\"alpha\")\nr = 3.0\ncirc = r*exp(1.0j*alpha)", "language": "python", "metadata": {}, "outputs": [], "prompt_number": 12 }, { "cell_type": "code", "collapsed": false, "input": "figsize(8,8)\nplot_parametric(re(fresnelc(circ)), im(fresnelc(circ)), (alpha, 0, 2*pi))", "language": "python", "metadata": {}, "outputs": [ { "output_type": "display_data", "png": "iVBORw0KGgoAAAANSUhEUgAAAekAAAHTCAYAAAADL+BcAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzsnXd4U9Ubx78tiLKklL0ECgiCKGXIEqmUguwpCILKVFFc\niIyfCKgIgsgWoSigiOy9hJYyyh4FZI+yZNNSKHQ37++Pr7EttDRN7k1ukvN5nvsU0uSckzT3vOfd\nHiIiUCgUCoVCYTg8Hb0AhUKhUCgU6aOEtEKhUCgUBkUJaYVCoVAoDIoS0gqFQqFQGJTsjl6AQuEO\nLF26FF5eXggPD0efPn10eUyhULgeSkgrFDoTFhYGHx8f+Pr6/vd/M/7+/ggPD0dYWBjMiRbWPGYe\nW6FQuBbK3K1Q2IFBgwYBAMLDw+Hr64uFCxcif/78AAAfHx8EBQVh0aJF8PLysuoxhULhmighrVDo\njK+vL8qWLQtvb294e3sDAKKiov77NwBERETY9JhCoXBNlLlbodCZqKgolC9fHoGBgejTpw+qV68O\nANCzjpCHhweGDx/+3//9/Pzg5+en23wKhUIflJBWKHQmMDAQ7777Lp5++ml4eXlhyZIl8PLyQmRk\nJAAK8QIFCgBAlh+7c+fOf489zIgRI3R7TwqFwj4oc7dCYQeefvppAAz28vLyQufOnREeHg6AfuqA\ngACrHjt//jwCAgIc8I4UCoU9UJq0QqEzAwcOxLhx4+Dj44PIyMj/Uqb279+P4OBgeHl5oVq1ajY/\nplAoXA8P1WBDoXA9PDw8dPV5KxQK+6DM3QqFQqFQGBQlpBUKhUKhMChKSCsUCoVCYVCUkFYoFAqF\nwqAoIa1QKBQKhUFRQlqhUCgUCoOihLRCoVAoFAZFCWmFQqFQKAyKEtIKhUKhUBgUJaQVCoVCoTAo\nqna3QmEAkpOBmBjg/n1eMTFAQgJ/l7q6p/nfnp7AE08AefIAuXOnXNmypR1XBIiN5VgJCUBiIq/0\n/m0e98knH395eOj/eSgUCqKEtEKhA/HxwNWrwD//PHrlzg0cOpQikO/fB+LiUgRtnjyAjw9w507K\neKkFY3IykC8fcP58ymvj4yloPT15AUD27IDJBHh5AQ8eUIA/8URagZsrF38+8QTw7LPAyZMcK72r\nShXg8GGgSJGMr8KFU/7t5aUEukJhK6rBhkKRRUwm4NIlIDz8UQF85Qp/3rkDFCsGlCz56FWsGJA3\nL4Wx+cqZM0W4RkYCZ84AFy8CFy6k/XnxIoVt6dJAmTIcq0ABwNsbyJ+f4z71FNC6tQdCQgSenhzv\n2jXg+vX0r2LFKPQrVKCgrlAh5SpcOK2gvX8fuHEj4+vmzZR/x8WlFdrVqvG9VqzIq3x5HhAUCkXG\nKCGtUDyGuDjg2DFqvubr8GEKHE/PtMK3RImUfxcu/Kjp+WFEKHQPHQLCwlJ+mkxA0aIUwmZhXLp0\nyuXllfm6Le2CJQJERPDQcfo0Dwepr8TEFIHt60sNv3p1oGzZzLXk2Ni0QvvOHeDoUWrrp07xvZco\nkSK0n38eqFwZePFFavgKhUIJaYXiPyIi0grjQ4eAs2cpoKpVS7lefJHaa1ZITKRwSi2MDx2iBu3r\ny3HNP318UrRqa9GqVaVZqz9zhlr39u3AwYPUqKtVo8CuXp1rr1gx84NJahITaY04dYrXjRtASAhw\n4gS17Bo1gJo1+fPFF/lZKRTuhhLSCrfk7l1g925gx44UgXz3LoVBaoFcuTLNx1nBZAKOHAH27QP2\n7OHYx48DzzyTVhhXq0YzsB7o3U/61i0eNA4eTPmZLx9N7v7+vKpXz5rQNhMfD/z9N3DgAK/9+3nA\nqVCBAtvPjz8rV1Y+b4Xro4S0wi2IiwN27gSCg3kdOwa88go3e7PQLFPGuk1fhOOFhPDauhUoWBBo\n147m6WrVgBdeYFCYvdBbSKfHnTvUtIOC+BlfuwY0bJgitCtVsl6oxsWlCO5z54AlSxgB/+qrKVeF\nCkpoK1wPJaQVLklyMjd0s1Des4fRyf7+QOPGQN26WdeQU3PuHLBpE7B5M7BlCwOiGjWisPDzo6/V\nkThCSD/MtWv8fIKDKbiTk1MEtr8/ffe2cOFCysEoJIQWjNRC2xK/uUJhdJSQVrgMZ88C69dTKGzd\nSkFpFggNG9Icay3JycCuXcCqVbyKFKHmbRYIpUtr9jY0wQhCOjUi/PuYD00hIfTrmw9Nfn6MULdl\n/HPn0grtMmV4GGvTBqhfnylpCoWzoYS0wqk5exZYtAhYuDAljcjfn1pt0aK2jX3/PrBxI4Xy2rUU\n+q1b86pe3fbgLj0xmpB+GJOJUfJmLXvnTqBWLbogXn+d/mZbxz9yBFi5ElixgmlxLVoAbdsCAQH2\ndT0oFLaghLTC6Th/Hli8mIL5yhWgY0egc2dqS7YKzgcPgKVLgdWrgb/+AurUoVBu1cp42vLjMLqQ\nfpiEBAbyLVtGf7OXF9CpEwX2c8/ZPv7FizxsrVjBgD4/Pwrsli2ZLqdQGBUlpBVOweXL1JgXLaKQ\nbt+em3jDhtZFEKdGhFHes2dTSNSvD/ToQTOsLSZyR+JsQjo1JhNdC4sX8/L2prDWSmDfuQOsW0eB\nffQoi7m8/Ta/U3nz2j6+QqElSkgrDMvVq9ykFy1iCk7bttSYGzXSxr94+TLw22/AnDksi9mjB9Ct\nGzdtZ8eZhXRqTCaawhcvZqpXbCzQuzfwxhvaHKDi4ujK+O03xjG0bAl0784Dmq2HP4VCC5SQVhiK\n6GhqOLNm0afYujUFc+PGQI4cto8fG0s/5ezZNHt26kTh/NJLrhUJ7CpCOjXJyYyonzWLfuy2bYFe\nvYCXX9bmb3frFl0ov/1GH3bXrsBbbzF9TqFwFEpIKwzBmTPA1KnAvHkUyq+9BjRtqk1tZxEK5Nmz\nqZXXqEHB3Lat61axckUhnZqbN4Hffwd++YXCu3dvClStisOcPMnx581jHn3TptSw8+TRZnyFwlKU\nkFY4DJOJwVlTpjCnuXdv4P33bc+fNXP9OjfaOXMYmPTOO9zIS5XSZnwj4+pC2owI/dezZjGeoFEj\n4MMPmRanhXZtMtEMPnUq07q6dwf69WMJVIXCHighrbA79+4Bc+dSOOfJA3z0EX2MthQXSc2RI8D3\n39OnXbYstWatTKLOgrsI6dTcu0dz9dy5jNIfPJiR/1r5li9dAmbM4IGgWjUeBpo3V75rhb4oIa2w\nG6dPp5i0AwIonOvV0054hoYCo0czwOiTT4B333Xe6GxbcUchbUaEwWCjR7NpxxdfMHpbq7aYcXEM\nZJsyBbh9m5p1z562FWNRKDJCCWmFrphN2pMnswlDnz7Ae+9pZ9I2mZhOM2YMzdtffEGTtlZaubPi\nzkI6Ndu3U1gfPgx8+ikPblqmWe3dy4PnkSO01gwa5B7uFIX9UEJaoQv37wO//soNLG9eas2dO2sn\nPBMTadr8/numYw0eDHTooEo/mlFCOi2HDvG7EhTEQ+JHHwGFCmk3/rVrwIQJNIV37AgMGUJXi0Jh\nKwYubKhwRmJjgR9/ZD/g06cZtLV/P82NWgjomBhg2jTg2WcZ2fvDD9TQO3dWAlqRMdWqAX/+ySCz\nmzcZ+PXRR/Qza0GxYsDYsfzOFy7MPtg9ejBrQaGwBSWkFZqQkAD8/DNrZ2/fznzWqVO18znfuweM\nGgX4+FAb+vNPRts2bepeAWEK2yhfnsFfR4/y0Ojry6j/48e1Gb9gQeDbb1lTvkwZfv+7dQNOnNBm\nfIX7oYS0wiaSkxlNW6kSi5AsX86ralVtxk9KovB/9llqQJs3c/w6dbQZX+GeFC9OzffsWQruV18F\nunSh71oL8ucHhg9nZ67KlVkrvFMn+q4ViqygfNIKqzCZ2Ijiq6/o2/v2W3Yw0pKNG4HPPuP4P/5I\nrUdhGconnTViYph1MGwYS4N+8w0FuVbcv8/D5vjxrEHerx8PtgpFZihNWpElRIA1a1i1a+xYYNIk\nFnvQUkCfPMm2gv36Ufhv3qwEtEJfcuUC+valT7lgQVqCRoxgvrUW5MkDfP45EB4OlCsHNGgA9O/P\nFC6F4nEoIa2wmOBg+tiGDKEpb+9eoEkT7XzCEREM5mnQgJWjjh1j6U7lc1bYi3z5GAV+4AAFtjlA\nMTlZm/Fz5gQ+/jjFR/3cc9Su4+O1GV/heihztyJTzp/nxpItG/1qnTvb3rc5NQkJwE8/Ad99R1Pg\niBHapse4I8rcrQ1791IDjopiJkGTJtqOf/IkMHAgA9fGjmW7THUoVaRGCWlFhsTFAePG0aT92WfA\ngAHaVW0CUkznn3/OnNLx44EqVbQb351RQlo7RNg57ddfeVCdPFn7giVBQby/nn6a8Re1amk7vsJ5\nUeZuRbr89Rf9cgcP0vQ3dKi2AvrIEZYGHTQImDgR2LBBCWiFMfHwoNtl8WLmW/v68uCqlQkcYCvW\ngweZDtamDRt5XL2q3fgK50UJaUUaLl1i5a4PPuBGtHw5ULq0duPfuweMHEkB3a4dU16aNdNufIVC\nL558krEYoaG8L2rXpmDVimzZ2B/71Cng+eeBF18Epk9nJoXCfVFCWgGAfuExY4Dq1dnk/uhRdvjR\nki1buPFcu0Zf3AcfAE88oe0cCoXeVKrEQjoffsgD5oABTLHSirx5aWHauhX44w8GUh47pt34CudC\nCWkFgoMpmENDGSgzfLi2DSpiY9nc4M03WYXs559Z7EGhcFY8PGiaPnqUWQlVqgCrV2s7R+XKwLZt\nbBjj5wd8+SXjRBTuhRLSbsydO2zp2Ls3I0tXr2bZTS3Zv5/a+dWr9EO3aKHt+AqFIylUiPXpZ8+m\nRv3ZZxTaWuHpyc5dhw/TDP7CC9TiFe6DEtJuyl9/8YY3magNtG6tbepHYiI18hYt+HPhQqBAAe3G\nVyiMRKNGPIRmy8bgMq0FafHiDFz74Qc2q+nTB4iM1HYOhTFRKVhuxoMHzMtcs4anf39/7ec4dowm\nuiJF2LpPy/KKRkCEPsioKODu3ZSfd+8C0dE073t6Pnply/bo/3PmBLy9eRUoQDdAzpy2r1GrFKyE\nBL6/h6/kZB7EPDwefZ8PP5b6/088AeTOTQ20cGH6X10tL3jjRnbAevttBklqHXcRHc10xV9/pRbf\nqJG24yuMhRLSbsTOnRSe9eszctvLS9vxk5PZU/f771mYpHdv59qAk5KAf/5h6cbz51N+xsXx32aB\nHB1NQZovHz/DfPnS/jtHDgpykynlSk5O+39zxO69e9SI7tzhz4gICm+z4Pb2pmaWLRtzycuUSbny\n5Mn4vWQmpOPi+F4vX370SkqideXOHQpiL6+0V/78QNGi/CxMpkffa3qPmf+fLx9w4QKbpdy6xUNA\n4cIpQtv8s3BhHu6KF2fVrxIltC2gozc3b1JQ374NzJ/PUqBas2EDo8G7dmX5XC1TJBXGQQlpNyA+\nnlW8Zs9mSke7dtrPER7OQBoPD57ujdrwPjaWJRnPnk0rjMPDKbQKF6ZfvmxZ/vTxAUqWTCuEn35a\nv97VIlxjZGTa6+xZCrfUV65cFNZVqrC4xgsv8CpfHnjiCQrp27dZzer4cb7v48cpfP/+m4KvZEm+\n9uGrUCEK41y59D1oxcZSWJuFduqfcXE0IZ8+zQNS+fJshfrss2mvAgWMeRgUAaZMYbOOH39ky0qt\n13n7Nk3f4eGMBH/+eW3HVzgeJaRdnCNHWBihbFn20S1SRNvxRYCZMxl5OmQIA9GMovHExDDg5sCB\nlOvsWfrfExNThLBZIJcurZ82cvDgQZw/fx6RkZHo06cPAGDp0qXw8vJCeHh4lh9bsmQpPDy8cOBA\nOHx9++DoUVpKjhyhUE9K8sBTT/HW9vFh8J6vLyOGn3uOgtgofydLiI4Gzpzhdfp02itXLgrwWrVS\nrrJljSO4jxwB3niDn/9PP/GgpyUiNH0PGsQuXv37O9ffVvF4dNIHFI5GhEJ5+HCan99+W/tNKyKC\nwnnvXuZ0Vq6s7fhZITYWCAtLK5DPnaNAqlGD/ac/+IBV1BxhFhwzZgwWLVqEcePGISws7D9TtL+/\nP8LDw7P82P37gLe3Pw4fDsfmzWE4dcoXXl5M1alRgxv2yJHAxYvUmnftYqR9/fq86tWjFmoUQZYZ\nefPyoFG9etrHRah1HzkC7NsHLFjAKOu4uLRCu1YtmugdwQsv8LMfMICC+o8/gLp1tRvfw4Nm74YN\nqa2vXUtrlqvFgrgrSpN2QaKjgZ49acpdtEj7tCoAOHSIzQA6dwa+/tr+RUkSE7kpb97MPO+wMArk\nF16gkKpRg6Y/I/jplixZgvPnz2PgwIH/PTZ48GA0adIEjRo1QnBwMA4ePIiIiAgEBATA39//kcdq\n1/bHlCnB2LDhIE6ejEBcXADq1vVHwYLByJHjIEaPHpjGSvKwTzopicJ6xw5q3Dt2MIiwfn3g1VfZ\nOOK555xHaGfG1av8fqS+GjSgj79xYwZMOkJor1jBlKr+/Wl5ypZN2/GTkuifnj6dglpV83N+lCbt\nYpw4QeHZoAHw++/aFiUx8+efbCk5dSqFtD0wmagtBQdTMIeG8vDRqBGj1Rs0oLZlRPbv3w8ACAsL\nQ1BQEAYOHIioqCh4e3v/95yIiIg0jyUlAYcPR+DUqShs3OiN06cZfPT00xGoVy8KQ4d6o1Ytfh6b\nNkVk6sbInp1anK8vK2UB9MHv3s3mDhMnMritaVNejRs7d8GZ4sVZA7tNG/5fhFXuNm8GlizhZ1Cy\nJIV148bUQu3x/Wnbllp99+60QM2YARQrpt342bMz/qRpUxYP6tSJQluvGAqF/qg/nQuxZAnQrx/L\ne/bsqf34SUnA4MGsWxwUxBKfehITw3zu5cvpi4yM5Kbaowcwdy5QsKC+82tJwYIF4evri6CgICxd\nuhQAHom+Tkpifu2ECdS4ChSg/7JPHwbl7d4NbNrEqGpLNt0RI0b8928/Pz/4+fml+X3JkkDHjrxE\n6N/96y9qYL16MSCtSxfWWX/uOdvev6Px8OB7eO45uj2Sklh3OyiIQV1vvMGD3iuv8JBbsaJ+aylR\ngn/HCRNY/3vlSh6etKRuXWDPHv79XnuNB2vV/tU5UULaBTALz6VLmZbxsN9OC27fptacPTtNh6mU\nQE2JimIO97Jl1BJr1mQ0+jffaNvow54UKFAAZf8Nd/fy8sK+ffvg5eWFyMhI3LkDrFwZhc2bC+DM\nGaBy5Uj06gW8/HIU7t4tgIgIoFKlSOTODURFRaHAvxVhIv+tZHHnzp3/HnuY1EI6Mzw8KJgqVqSV\nJD6e1oqNGymk8+dnr+9OnVi72tnJnh146SVeQ4fyQLhzJw9HjRoxkr99e17VqmnvBsiWjS1ay5Sh\nq2HmTO2zLgoV4n4wbBjvoyVLVAtMZ0TFADo5N27QXHfsWEoJTq05eJA3ea1awLp12gvoa9dYz7tp\nU+CZZ1hZqXVrppUEB9M06awCGgA6duyI8PBwABS01aq9hHz5OmPkyHCULg0EB4ejU6cAbNjQGf36\nhePDD4G7d8MREBCAzp07//fa8PBHHzt//jwCAgI0X/OTT9Jq8f337Iz2888ployqVXloOnlS82kd\nRq5cvI+mTmWu+C+/8KDy+uuMFP/sMx5atGxPCdCKsX49D0ZjxtCioSXZswOjR9Od0bw5EBio7fgK\n/VGBY07Mzp3Ubnv14mlZ6yAUAJg3j80xpk2jFqUVDx5Q81+4kJq5uXXla689vkiHsxIYGIjoaG/M\nn78fFy6MRo0aQNmygWjZ0gfXrqWkVgUGBsLHxydNupWlj6VGq4pjD2My8Xu3eDE1s9q1aSJ+6y39\nrCuORISFXZYt41WsGN0877yjrQvgyhX6zytXpiDVI+Dx5ElaBurV42FEj3gVhQ6IwukwmUQmTxYp\nXFhk7Vp95khOFvnmG5Fy5USOHNFmTJNJJDRUpFcvES8vkebNRRYvFomN1WZ8I3L3rsjPP4vUrClS\nqpTIV1+JnD+v/7z2uLWTk0W2bhXp1o1/z27dRLZt49/ZVTl2TOSLL0SKFROpXVtk+nSRO3e0GfvB\nA5GOHUXq1RO5cUObMR8mOlrk9df5fbx4UZ85FNqiNGknIz6eqRshIdRE9UivSkhgXnVEBPNObdWQ\nrlwBfvuNAUkeHgz86t7ddfM4RZjiNGsWg4L8/VkiNSBAH2tHeuilSWdERASzCWbM4N+4b1/X1a4B\nxoFs3Mjv9MaNTHXq0YN/a1v+xiYTo7N//x1YtYquBa0xV0KbPJmWrBo1tJ9DoR1KSDsR9+4xhaNk\nSW6GWjRieJj794EOHTj2ggXWm8RMJgatrFrFXO2OHRlxXru26+TiPkxEBKPOZ87ke+zdm4eRwoXt\nvxZ7C2kzIvTdzpjBAMBWrZhxUKeOa//d//yTArtoUeae9+1rW9e3+fNZve/XX4GWLTVbahqWL+c6\n58xRLWQNjSPVeIXlXL8u4usr8v77IklJ+sxx65bISy+J9Owpkpho3Rj374v89JNIxYoi1aqJzJtH\nM54rc+GCyEcfieTPLzJggMiOHY43+Rrh1r59W2TCBJFWrUTq1BFZvpwmclfm4EGRHj1o/u/TR+To\nUevH2r1bpHhxkfHj9fs+7dolUrQoXTIKY+L4O1mRKefOiZQvLzJ8uH4366VLIpUqiQwaZN0cly7x\ntQULirRtK7Jli+MFld4cPizy5psi3t4iAweKXLni6BWlYAQhbSYpibEHNWrwO/brryLx8Y5elb5c\nvy4yciQFYOPGImvWWHdAuXhR5I03RD79VL/76cwZ7i+DB7v+IcoZMc6drEiXQ4d4mp42Tb85jh8X\neeYZkR9+yPpr9+zhJpI/v8jHH4ucPav9+oyEySQSEiLSrBk34NGjtQsc0hIjCWkzJpNIUJBIQIBI\nyZLUEO/dc/Sq9CUuTuS332gFq1CBVqb797M2RmQkg9R699bPinbzJq0dXbtyzQrjoHzSBmbbNvpy\np07VNv0pNXv2MPVj7FgG+ljKjh0McMmVi00devbUvruP0dixA/jf/5gi1ro1Py+jprE4yidtKQcO\n8Dtnzr3+6CN+l1wVs69+4UKmrn3+OfD++0Du3Ja9Pjqa92nRoox70KNWfmwsS4lGRtJf7cxlYV0K\nBx8SFBmwfLlIoULUPPRiwwaap1evtvw1oaE035UtK/LLLyIJCfqtzygcOEDNuXRpmmqt9dfbE2e5\ntU+cYEpQsWLUMt3h+3TkCFOtihQRGTvWcs06JkakRQuR1q31S1tMSqJFrHJlxlooHI9z3MluRmAg\nTan79+s3x59/Ms96+3bLnr9jB82UZcqIzJrlHpvpiRPcTIsVE5k61bnMgM4ipM3s3y/SpAnz8ufP\ndw/f6JEjPKAUKSLy/ffMYc6M+Hi+xt8/62bzrDBhgkj9+iJ//63fHArLcK472cUxmUS++46C8NQp\n/eaZNUukRAnLipSkFs6Bga4f8CMiEhVFbSIggJunM0anO5uQNrN5M/2vL74osnGjo1djH/7+W6RT\nJx6af/gh8+9bUhIjyOvV0zceYv58KgthYfrNocgc5ZM2CCYT6wNv3sz8Yr0KfQQGAj/9lHkhlAsX\n2KQ+IYH+17ffBnLk0GdNRkGERSQGD2ad49GjnbdzkNF90o9DhEVgJk+m/3/SJNbPdnWOHWMBnCVL\nWMe7SxfAM4PuCiYT86j37GE9fVtysh/HkiXsGrZunSp64jAcfEhQCLXTrl1FXn5Z35PxggWMFD99\nOuPnxMQw1cvbW+Trr/l/dyAsjOa9mjUZse7suMKtHRcnMmpUynfRlcvHpiY0lN/D2rWZx5wRJpPI\nt9+KVK+u775hjo/ZvVu/ORQZ4/x3spMTHS3StCkLPugpENeupTnt8OH0f28yiSxZwuCo1193n7q+\nkZEiH3zAz2bGDP1SXOyNKwhpMxcuMPe+fHmR9esdvRr7kJwsMncu3VJdu7IOQXqYTCL9+9P0bYlP\n21rWrKGgDg3Vbw5F+rjOneyExMQwUvPDD/WNGN66lVHcGZ3Kjx1jIEqVKiLBwfqtw0gkJzM6vUgR\nkffeY3UsV8KVhLSZtWsZWNa+vfscIqOjRYYNozVh+PD0g8WSk1klsFEjfQ/6GzZQUG/dqt8cikdR\n/aQdRFIS8MYbzA2dNIl9X/Vg/37mWi9YwPrJqbl7l20oGzZkDuahQ2x47+ocOcL6yjNnsr709On6\n+fQU2tG8OdtGvvAC4OvLJhFJSY5elb7kyQN8/TUQFgacOgVUqsT2sSZTynM8PfldLlyY/a8TEvRZ\nS9Om3Ec6dmSfd4V9UELaAZhMbL6QkMDCBBkFh9jK8eMszh8YyIIRqeefPZs3/P37fF7//vodFIxC\nUhIwalRKD+6dO4GaNR29KkVWeOopYPhw9iDftQt4+WXgzBlHr0p/nnmGTTwWLmRA3TvvAH//nfL7\nbNnYaS57dhYk0evw0qgRg8n692eQq8IOOFqVdzdMJpHPPhOpW1ffPMfwcJZe/P33tI/v2cMmGnXq\niOzbp9/8RuPcOfrtGjUSuXzZ0avRH3e4tZOT2Ve9YEHGE7h6rXgzyclMoyxYkP3JU6dFxsUx33zA\nAH0/j5AQzr93r35zKIjr38kG47vvRJ5/XiQiQr85rlwR8fFhAQ4zkZFsAlGsmMicOe5RLEKEG9Xs\n2dxQxo93n/ftDkLazLFjrI3dsiUbW7gL//zDgNMqVdJmJNy/zwPpN9/oO//KlYzpOHFC33ncHfe5\nkw3AjBksp6lnt6Tbt3nTjhqV8timTSKlSokMGSJy965+cxuN27dFOnQQqVo146h2V8WdhLQItcmh\nQ1l8Y8UKR6/GfphMrB5YpAi1Z3MhlGvX2DRn4UJ95589m/NkFH2usB33upMdyOLF1GLPnNFvjnv3\nRGrVEvniC968Dx4wPaNUKfep3mTmr7+YvvLZZ+6TX5sadxPSZkJDaUXq1cv1O2yl5uZNkS5dGP0e\nEsLHDh1iNLbeef8//MAWpLdu6TuPu6ICx+zApk1Av36s2lO+vD5zJCezE9Wrr7Ja0f79QPXqQEQE\ncPgwEBCgz7xGIzYW+PhjBobNnQuMH2/cTlUK7alfn1kKAFCtGrttuQOFCgHz5wMTJgDdugEDBwLl\nygG//AI3WhV9AAAgAElEQVS0awdcuqTf3AMGMDukRQsGoiq0RQlpndm7l9GWS5dy09CLr74Cbt1i\n5OvIkYzq/vpr4I8/3Kfl3MWLjNy+eZMHk9QR7Qr3IW9eltecOBFo1ozZDe5Cq1YsL2oyAbVqAaVL\ns9xwq1Zsd6kXo0cDVasC7dsD8fH6zeOOKCGtIydOsO71r78CDRroN8/ixRTG333HnOe9e5lXqVcP\naiOyfTvzwBs1okbh7e3oFSkcTatW/F5MmAD07es+wiNfPlqQhg7lQTVPHgrsN9+kxU0PPDyAn3/m\nXN276zePO6KEtE5cvMjk/3HjqNXqxeHDbB7fpQtNTr16AWvX6tegw4gEBrLAwty5bDrg4eHoFSmM\nQsWKbEIREcED7JUrjl6R/ejeHQgNZUOdu3eBqCg2j9GL7Nl5QL59m1Y8J+3vYjiUkNaBmzeBJk3o\nq+neXb95IiIomH186HPauRN47z33EVKJicCHHwI//sjNqEkTR69IYUTy5mUBjjZtqFFu3+7oFdmP\nihWB3bvps758mcVQZs3Sb76nngKWLQOWLwemTdNvHndCtarUmAcPWJqvRg3gm2/0mycpiUEyZ86w\nvOiECcCTT+o3n9GIiODnnDMnT+/58jl6RcbCmVtV6smGDWy7Onw4LVDucqAF6BZ77z1WOly5Ut8S\nwOHhQL16LGHauLF+87gDSkhriAgDl/Lnp39Gzw2gWTPWzw0M5KbjThw9Sq2oY0f64bNlc/SKjIcS\n0hlz7hzwxRdAqVK0wuhVlteIhIezBvr587QovPSSfnNt2cL9MDQUqFBBv3lcHSWkNWTMGJp6tm3T\nL+0nIYE32bZtrJ378sv6zGNUVq4E+vTh5tqtm6NXY1yUkH48UVE86BUtylgGd0rTS0hgnMyWLYxf\n0TM9c+ZMWvl27QK8vPSbx5VRQloj1q9n04w9e4CSJfWZ459/qEGfPs0brG5dfeYxKlOmsAvPhAn6\nagCugBLSmRMXB7z1FmNIVqxwPyHSrh0P+lOm8HPQi/79gbNn2XFOWb2yjhLSGnDmDDXaZcvoJ9aD\n0FDeSJGRTOlq316feYyICPPAFy8GNm5kRyDF41FC2jJMJrZrDQnhQbtECUevyH7ExjK3+e5dFlsa\nMUIfF11SEpWLF15gapgia7iRN0YfoqOBtm2ZcqCXgP79dwplHx/g88/dS0CbTIzgXreOPjQloBVa\n4unJoifduzPQ6fhxR6/IfuTMScXCZKIbqXt3fXLJs2cHFi0CVq+mgqHIGkpI24DJxKCt+vWBd9/V\nfnwRnm6/+or9Y+PjgSFDtJ/HqCQm0u987Bg1nUKFHL0ihSvi4cEymqNGMeJ5715Hr8h+vPACMGwY\nkCMHNevGjZnnrDX58wOrVrHy4r592o/vyighbQPffQdcv06fjtbEx9O8vX49MHs2r99+cx+fTkwM\nLRT37/MzePppR69I4ep060arVcuWrDngLnz0EYVolSq0JtStSxee1lSqxP4Cb7xBE7vCMpSQtpJN\nm5hzuXSp9vnJERGMuIyN5RwffwyMHQuULavtPEYlJoZBLRUr8vPNmdPRK1K4CwEBFNRt27qPoPb0\nBObMYSR269ZMT2vQgBkkWtOhA/3TvXurimSWooS0FVy7Ri131CigWDFtxz5zhifZunXpxxkzhr7o\nd97Rdh6jEhfHDbJQIZZUfeIJR69I4W40bep+grpYMWDGDFoTOnXi++/YkcVItOaHHxjt/fPP2o/t\niqjo7ixiMvEmrleP3aa0JCyMgWGdOtHHvW0bTUOHD7uHPzY+nhp0vnzcJLJnd/SKnBcV3W07Gzbw\nML5ypfukO773Hqsm/v47Y0FatKCCMHy4tpHfp08zlmfTJn27A7oCSpPOIuPGUdsbNkzbcXftAl57\nDfjgAwroe/cYlBYY6B4COiGBJ/fcuZWAVhiD115jHEibNrw/3YHx4xnYNX8+fdS7dzOzol8/BnJq\nxbPPApMmUSHRs4WmK6A06SywZw/b3+3fr20q0ObN1Jh/+40bAwD06EFftzuYhJKSeDi5dYsNAJSJ\n23aUJq0dGzYwSHTCBNbkd3UOHuQ+tHcvUKYMY0Q++IBV2hYuZCS4VvTpw/HnzXOvOupZQWnSFnL3\nLtC1K4WmlgJ67VoK6MWLUwT0smUsXvLDD9rNY1REGBh35QqriSkBrTAar73GgietWtGX6upUr86U\nNHNf6Fy56K8GaO3SMpd60iS681T+dMYoTdoCRNgwPV8+YPp07cZdsoQn1FWrgNq1+dj16/TRrFgB\n1Kmj3VxG5ccfeYPu2KE6WWmJ0qS1Z8YMurt27ACKFHH0avTFZGKke6NGwP/+x8cSE6moxMQw60Kr\neufHj1NRWbSIaVqKtChN2gKWLQNOnqRA0Yq5c5mfuHFjioAWYVP2vn3dQ0AvW8bPdN06JaAVxufd\ndxn93Ly56/tRPT25R+3Ywa5zAK1cf/7JmgVt2jBFVAsqV+Zn26MHNXdFWpQmnQk3b7K+7bp12vmj\nfvoJGD2akY2pT47LlzNifO9ebf0+RmTPHhaN2LDBPfx89kZp0vogwgjo8HC6qlz9Pp06lYfp4OAU\nn3FSEiO+r1+nFTBXLtvnMZkAf3/uCQMG2D6eK6GEdCZ07cquVmPHajPetGnsBjNtGvOfzcTG8kT5\nyy/6NmM3AuHhTL+YOZN+PqMhQpNeZCRw5w6v9P4dGclKTRcupBRmMP/MmZOBf3nyMGI9Tx4+N18+\ntkcsVoxXkSL6+OH1EtIifO83bqS9EhPZpS0ujt/l1D9LlgQuX+bn8eSTFGzmf+fJw02+SBFehQun\n/DtfPmMGEyUlAa+/zgjlMWOMuUatSEriIfp//2MktpnkZKBXL+DiRdbkzpPH9rnCw9ndbudOfrYK\nooT0Y1i/ns0d/v5bm9PinDmsw71tG6MmU/P115xn8WLb5zEykZHMMe/fn/54R2EycYM5cwY4dy7t\n5enJPM78+Xl5e2f8M3V7Q/Nm7eHB4JqYGJY1ffCAPxMTGSB37VrKdesWxwoI4IZYsWLaK29e696f\nLUI6KYmfw8mTDJQyX5GRKfeCWZCar7Jl+b5z5qSvMvXPHDmYYhcfz8v874QEfiYRERT0N2+mCP2b\nNyngq1blGJUq8apYkT/LlHFsml5MDKtyde3q+prftm008584wQOnGZOJrrnTp2lVsPa7mprJk+mb\n3rrVfUogZ4YS0hlw/z7w/PPMU9aiKfqSJfRBb978aHDExYs8rR44AJQubftcRiU+noVgatSwb8u6\nuDhGkB46xJ+HDwNHjgB+ftxsy5VLuXx8eNmrt3ByMgX1P/9wszt5Ejh1iteZM1yjpyc1jFq1eBUo\nkPm4lgpp82cTFsbPJyyMPshXX+Xvy5fnVaECP5+SJbULGMqM2FiaVC9c4Odx8mTK53P9Ov9OFSvy\n+1SjBj8jb2/7rA0ALl1i7Mjs2fxeuzJvvsmD0ahRaR83mYD33+ff448/bNeoTSagYUNGkX/8sW1j\nuQpKSGfAp59Sc5g71/ax1q1jUMTGjcCLLz76+44d2Y3mq69sn8uoiLB6U0wMrQWeOoYsRkbSZBYa\nyuvQIVaMKlmSkfMvvsjP254bujWYtf0DB1hgYu9e/rtQIfrvfH15gCxX7lGTa0ZCOjqan822bbwO\nH6abpXJljmf+fIze0CQ2loeYkyf59929m/ULihal4Kxdm1e1avpq3KGhrEcdGsqDjKty5Qq/F7t3\n89CWGpOJCkh4OKuz2eq+MZdG3rOH3213RwnpdNi3j77So0eBggVtG2vLFvpyVq1KP2I7OJgJ/ceO\nuXYjiSlTaE1Yv14b10FqHjwAgoL4d1u5koKtdm3g5Zd51amT1kznzCQnU5MMC+OhLyiIm2JAANsM\nNmpEIW4W0iYTi1OsXk0T9ooV1DobNgReeYWfjRb+RCOQnMx0nj17eP3zDyuFvfoq0KQJLz02/Zkz\n2ZN6927jH25sYexYHuzWrHn0d4mJjPguVgyYNct2P/2PP3LP3LxZ3wO9M6CE9EMkJgI1a7ITzJtv\n2jbW3r2sfbtwYfrBYImJPJ1+9x2L+bsqW7cyD3L/fqBECW3GvHCBfrA1a5gm8tJLtEjUrKm/9mQL\n48aNw8CBAwEAS5cuhZeXF8LDw9GnTx+rHjt3Lhwvv9wHQUHMFjhwgGbJXbs80LmzYNs2Co5WrXi9\n9JL9zNVG4MYNHmQ2buSVKxeFdcuWFN5aHRjff5/a5ooVritUEhIYIzB+PD+/h7l/n59pixbAiBG2\nzZWcTJ9/t24sSerOuOjXyXrGj+dpsGtX28Y5eZJF6WfPzjhae9o0mmDbtLFtLiNz6xZvtDlzbBPQ\nItSUhwzhRvHSSxT6vXtTYwoKYmpMzZrGFdBBQUHYtGkTAODgwYMAAH9/fwBAWFiYVY8xSC0M777L\nXr1161KbBHiA8fZmDfi+fak5u5OABhjU9uabdFtdvUohWr480x2LF+d9vmIFffO2MGkS6+1PnqzN\nuo1Ijhx8f598kv7nlScPD87z5lGbtoVs2VjkaPhwHsjdGSWkU3H2LEtx/vyzbeaaW7d4muzUKf0T\nJ8AT/qhR/NK7agqHyUQB0a2b9YE1J04AX35JM+Vnn1EAz5rFQJXZs+kPdBYTo0eqP/TChQuRP39+\nAICPjw+CgoKwaNEieP0bsWbJYyJAbKwP+vULQokSFBTNm9M3CLDW8uTJTH967z26AGbMYIlbd8TD\ngwe8AQP4HTp9mmb/KVN4MO/WjSZWa8pe5sjBQh9jx9LF5ao0bZqiTadH4cJ0aQ0bRoFtC5UqcZwv\nv7RtHGdHCel/EWHVm6FDH02PygqxsdSMu3RhsFhGDBlCAebKZfDGj6dA+PrrrL3uxg2+tnp1BkjF\nxrIM4bZtwDffUNg4m0kxLCzsP20YAO7evQvvVJFrERERiIqKsuixGzeisGaNN5o1M/fcjsD+/RQO\nvXqlBMRlz04rzk8/AX/9Ra1k0yZmELz1Ft0E7uzsKlyY93xwMC1f9erRF1q0KO/PrH4+xYrx4Ni9\nO3D7tn7rdjQ//shmI5cupf/7ChVonejRgy4/W3jvPY6xfr1t4zgzBjUM2p85cyhQPvrI+jFMJlbi\nKV368YJpzx5W2jp50vq5jM6uXbRK7NtnWbSnCLB9OwVKSAgPOuPGMQXJFfIlIyMjH3ksq+Eg4eHc\nrE6eZKDYyJGM1g4KSv9gOSKVY9DPzw/Nm/uheXNaeubNYyzEjRtsptChg3HdBPagSBH6Pvv1Y/76\nggV0HzzxBF0F3bsznz0zmjZl/EXPngxidEUrWdmyrB/x+efMaU6P2rVprm7Thvf1wxHhlpIjBw8E\nn3zCA7urV3hLDyfTR/Thxg1g0CDmRNuyUX35Jf2js2dnrOmZTCzkMXq085hps8qdO7QkBAZm3jEs\nOppNS154gVpN/fo0Q86cyZvSFQT0w1o0AHh5ef0nuKOiolCgQIF0H8uXzwsbNkSiYUNg0KAoFCtW\nAH37euHTTyNRuzYQFXUHBTJInB4xYsR/l5+f33+PFyrEFMPVq6ldT5tG7WfyZAb/uDvFivHzOXmS\nZTF37aJgevttHrAzO1uNGkX/97Rp9lmvIxg0iAfw7dszfk7LljxIvvYai9NYS4sWdHdNmWL9GM6M\nEtJgU4t33mGeqLX8+itPlStWPD44Z8kSFmHo3t36uYyMCLWIdu2A1q0zft65c6w4Vro0NcFJkxjw\n1L+/6zXbCA8Px9KlSzFz5kxERkYiLCwMnTt3Rvi/zuPw8HAEBASkeezs2XCIBGDZss6YMSMc778P\nfPFFOMaMCUDPninPO3/+PAKsrLbj6cmI723bqDlu305h9OWX9Pm7Ox4etOT8+Sdzd6tWpQCuW5f3\ncUbNIMz+6ZEjmYfuiuTMyZKogwY9/tDSty+D81q2ZKqktUyYwPlu3LB+DGfF7YX07t08EQ4fbv0Y\nwcH0Ya1dSy0lI0wm4Ntv6Q90Np+qpcydy/c5Zkz6vz9xgtG2detSYzlyhP7mRo1c0zQIAB06dECH\nDh3g4eGBu3fvwsPDA77/ngiDg4Ph5eWFatWqwdfXF8nJwNChwZg40QtLl1bDxIm+GDoUKFQoGAUK\npDzv4dfaSu3aLDKzezctIZUrM2jnyhWbh3YJChWieXf5cqZnjh/Pamc//cQCPQ9ToQJ9t6NGadt/\n2Uh07MjCQZs3P/55I0eyemPfvtZ3uapYkZaMoUOte71TI26MySTy6qsigYHWj3HqlEjFiiJbtmT+\n3GXLRGrU4LyuyNWrIgULivz996O/O3pU5I03RAoXFvnuO5G7d+2/PiNjMomsWydSs6bIa6+J/PWX\nbd8TW2/tmzdFBg4U8fYWGTRIJDLSpuFcktBQkTZtRJ59VmTUKJHo6LS/N5lEWrcW+eorx6zPHsyd\nK9KwYebPS0gQadVKZNgw6+eKihIpWlRk3z7rx3BG3FpIb9zIGywx0brXx8SIVK0qMmNG5s81mUR8\nfUVWrLBuLmegUyeRIUPSPnbkiMjrr4sUKSLy/fePbmQKkUOHRBo35ndx5UptDnFanb8vXxbp3ZuH\nrx9/FImN1WRYl+L4cR5AixQRmTAh7Wf0zz8ZH1xdgcREER8fkW3bMn/utWsiJUrwMGotv/wiUreu\n6yo66eG2QtpkotaycKH1Y/Tty5vTki/M6tUiL7zgul+utWtFypXjwUWEgqd9e25c48aJ3L/v2PUZ\nkStXRHr2pHVhyhRqG1qhtZHsxAmRDz/k33jNGk2HdhkOH6bmXKKEyPTpIvHxfHzGDJHatUWSkhy7\nPr0IDBQJCLDsudu2cU+4eNG6uZKTuW///rt1r3dG3FZIL1lCzTY52brXz58vUqGCZWZbk0nkpZdE\nFi2ybi6jc/++SOnSIps2iZw7J9K/v0ixYtS8Hjxw9OqMR3KyyM8/09UycKDInTvaz6GXJ2v9epHy\n5Wm6PHdOlymcnj17KLTKlqUwSUigSXjCBEevTB/i40WeeUZk1y7Lnj9uHPfDuDjr5tu5k4cedzn4\nu6WQTkwUqVSJG441nDpFE1ZYmGXP37BBpHJl6w8ERmfAAJHOnem7LFBA5Ntv3ecGyirnzlE4v/QS\n/fR6oWe4SVwcfbAlS4qMH++6GqKtbNki8vbbItWri/z5J++N8HBHr0offvpJpHlzy55rMom0bUvL\njLW8/jrdZ+6AWwrpX38VeeUV60zPsbEiL75Ic5YlmEwi9eqJ/PFH1udyBvbtE3n6aZps336bJlzF\noyQni0yezMPduHHWx0FYij1iQs+f54Gjfn2Rs2d1n84pMZl475csSctdgwau6fKKjaWZf/9+y55/\n5w5dJ3/+ad18x4+LFCrkHgGobiek4+Jomtmxw7rXv/ceA6QsvdGCgxkQ5IraRmioSN68vNncLeIy\nK5w+LfLyyxRmp07ZZ057JW4kJ9OMW7CgyMyZrimAtCA6WmTwYJHs2UXefNN6U6+RmTiRGrKlhIXx\ne3P8uHXzde8uMnKkda91JtxOSE+cKNKypXWv/fNP+uOycnrz8xOZM8e6+YzKvXv0O+fN69pmfFtJ\nShL54QeaOSdNsu9Bzd7ZlceO0azbsiWjeBXps3ixyJNPijRtallEtDPx4AFTpA4ftvw1s2aJPPec\ndVkfZ8/y3oqIyPprnQkXLamRPtHRLMc5alTWX3vmDKthLVpkeTnP7dtZhN7WtpdGYs0aFia4cYN1\njZcudd3CLLZw5Qq7oG3axFKSH33kGiVOM6JyZZbPrFaN17Jljl6RMenYMaVz2xtvsOpedLSjV6UN\nuXKxU11W9teePVlI5733st7spVw5oH179ghwaRx9SrAnI0eKdO2a9dfFxopUqyYybVrWXvf++7YV\nSjESN27QzF++PE34n34q8s03jl6VMdm0idHt337rOCuDI2/tnTv5PRkyREX3p8fFiywSc+QIU/Ce\necb6IFajER1NX3FWTNgPHjA91dI4n9SYP8sbN7L+WmfBbYT0rVs0jVgT4PL++4wmzIq/LTyc/hZX\nKP6wYQNTrL76innQ+/aJFC+uNuCHSU4WGTGCAnrzZseuxdHn7+hoHoh9fRlgpkjLsGEpCsOmTSJl\nyoj06OEaptuJE5nxkRVOn2alPWsyHvr3p9LgqriNkB4wgMI2qyxcyMCoqKisve5//xP5+OOsz2ck\n4uNFPv+ckakhISmPN23KlAtFCvfuMXf4nXdYHtXROFpIi/BQO2ECi1ds2uTo1RiL6GgedM25xdHR\nPOA984zI1q2OXZut3Lgh4uWV9VKyM2aI1KqV9cyHq1f5ublqZonj72Q7cPkyTSJZ3TzPnaM2bGla\ngZnERGpTeubB6s2ZM6zs06oVrRBmtmxhkQZzNSWFyIULLA/bp49xPhcjCGkzISHUFK0xZ7oyc+Y8\nmimydi2Dr4YO1bYCnb3p2jXrxVtMJpbHHTMm6/N98onIF19k/XXOgHHuZB356qus+0+Tk5nTaM3G\nsmIFc6OdlXnzeDiZPDntBmIyMY3ot98ctzajERrKA9nEicZKPzKSkBahybtyZW6mKhuAJCXxM3nY\nH339ukizZix446z556GhrMiY1b/1+fPce44dy/rrvL1dM2/aWHeyDty9K5I/P7XprDB9ukidOtal\nzbRoITJ7dtZf52iio0XeeotdvdKrprZ2LTcVV8z5tobly0UaNbKtYYBeGE1Ii9D82bChSIcOKTXe\n3Z0FC7jPPHzAM5mYtte0KdO2nA2TidYla9wc06fzgJJVs3fnzixF7GoY707WmClTRDp2zNprLl+2\n7jRnfm3+/M5XFvPAAZ58e/ZMf+3JyYxwX7bM/mszIr/+auy2eUYU0iIs4tGlCy1Nt287ejWOJymJ\nJYo3bkz/9/v3M2hz4ED9q9RpzfTpbLKTVZKTefgdOzZrr9u3T6RUKed2E6SHMe9kjUhOplaYlUAM\nk4masLWVbEaOtC5AzVGYTKwUVbAgm4ZkxMKFDOowkknXUfzwAwNVTp509EoyxqhCWoT35ahRIs8/\n79qpM5Yybx7dSBndW7dusWGHn59zfV737llnxRRhdkyBAuy+lhX8/FyvBLNx72QN2LiRJpesCJb5\n87l5WBMAlJTEzfvgway/1hEkJLDdpp/f4zsaJSSwtKm7R+iaTBTQlSqJXLrk6NU8HiMLaRF+lsOG\nsdqUEaLhHUlSEu+v4ODHP+d//6OmaGm3KSPwwQciX39t3WunTcu6y3HNGqb9uZIyYew72UZataKW\naCm3bjFdZM8e6+Zbt06kRg3rXmtvoqJEmjRhgMq9e49/7h9/MFrTlb741jBsGA99zqDNGF1Im/n6\nawqof/5x9Eocy9y59NdnxsqVvGedJXjz0CFmg1gTLJicTAVi/PisvaZyZZGgoKzPZ1Sc4062gnPn\naC7JSsGNN98U+ewz6+ds1465fkbn4kVaC95/P3M/l8nEmsyrVtlnbUbl66958zuDgBZxHiEtwpSb\n2rWtM4u6ComJrMewZUvmzz16lH7qkSOd4+Bctapl7ys9zGmwWWlM88svLIziKrhs1eXp04EePVhP\n1hLWrmXt4a+/tm6+69eBkBCgSxfrXm8vDhwA6tblZzNtGpA9++Ofv2UL8OAB0KKFXZZnSCZOBP74\nAwgOBgoXdvRqXI9Bg4DXXwdeew2IjHT0ahxD9uzA0KHA779n/twqVbhXrVrF2tcJCfqvzxa6dQPm\nzbPutT4+wMiRwHffWV7b+803gcOHgaNHrZvTcDj6lKAHDx5Qi36cn/Xh57dpY5vPdfRokV69rH+9\nPVixgqfSrERot2iRNZeBqzF3Li0JzmaOdbZb22RiVcC6dZ0vM0Ir4uOZMWBp3ev79+nS8/dnf2aj\nYs54sbZEclISM0sWLLD8NRMnMiLeFXBJTfqPP4B69XgKs4Tx44EcOYDGja2bz2QCZs0C+vSx7vX2\nYNIkoF8/YN06oF07y15z4gSwfz/Qvbu+azMqW7cCn39O7aZECUevxrXx8ADGjQOefZadooyuHepB\njhxA7960AlpC7tzA8uXsQPbyy8DFi/quz1pKlgR8fdlBzxqyZeP+NXAgEBNj2Ws6dwYCA4F796yb\n00i4nJAWAaZOZVtJS7hyhebM77+3fs6dO4FnngFeesn6MfQiKYmfxcyZwI4dQK1alr/2zz+BTz4B\nnnpKv/UZlVOn2Gpy/nxuggr98fDgYfeJJ4Avv8x660JX4N13qWTcv2/Z87NlAyZPpoLw4YfA8eP6\nrs9aune3zJSfEa+8Qjfd2LGWPb9oUcDfn3uYs+NyQnr7diA+nn8gSxgyhDdG2bLWz7lgARAQwE3G\nSCQlAb16AXfuUECXKWP5a6OjgSlTgLff1m15huX2bfrgR42y3rqisI7s2bmxbtlCP6S7UbIk0LAh\nBXVW+Phjao+NG9MfazTatwe2bQMiIqwfY9w47kmWWgz69qVy4uy4nJCeMoUnSk8L3tnevUBQEAW1\ntYgwgKNtW+vH0IPERKBrV+DWLWonXl5Ze/2CBYCfH1CsmC7LMyxxcfxbvv46TY8K+5M7N7BiBfDz\nz8CyZY5ejf3p149BnVm1JHTrRq26aVO6qYzE008DzZszQNdannmGVsEvvrDs+Y0bMxDRaJ9FVnEp\nIf3PP4zAtUT7E6Epd9QoIG9e6+cMC6M5uFIl68fQGrOAfvCAm5w15uqZM43tY9cDEUbLFi/O74XC\ncRQvTkH98cfA3387ejX2xd+fPvkdO7L+2o4d6Ytt3ty61+uJv79tQhqggN69m1p5Znh6cg9zdm3a\npYT0zz8z/N4SobtwIbUmW825K1YAbdoYx9SdmMg0sNhY6wX0oUPAjRs8kbsTEybQVTJ3rmWWGIW+\n1KgB/PADTaV37zp6NfbDwwN4/33gp5+se32rVvT/tmtHt4FRaN4c2LSJe5S15MpFs/dHHwHJyZk/\nv0cPYPFiuu+cFZfZiuLjgSVLaOrOjNhY5mZOnGj7ZrxyJYW0EUhMBN54g5/F0qXAk09aN05gIH3Z\n2UQqz9oAACAASURBVLJpuz4js307g1ImTQJy5nT0ahRmOndmvEfv3u4VSPb228CFC3RXWUPTpsCi\nRdwPQkI0XZrVFC3KjJudO20b5/XXgXz5gF9+yfy5xYoBjRoxCt5ZcRkhvWkTUKQIULFi5s8dP56R\n2K+8Ytuc588D164x6tDRJCRwQ0tM5GHFWgEdG0t/dM+e2q7PyNy+TffAr78ycEdhLH78EQgPZ7yJ\nu+DlxWDWJUusH8PPj4f1Tp1Y/MQItGhhu8nbw4OH6ZUrLYuCf+stxuU4Ky4jpBct4gkrM65f5xfX\n0lD+x7FqFdCypeM1zsREpjgkJ9smoAHeQAEBQKlS2q3PyIjQ79mlC81xCuPx1FM0WW7YQFeMu/DG\nG7anENWvD/z2G4MhjxzRZl22YGvwmJlq1RiMNnVq5s997TXg2DHg8mXb53UELiGk4+KA1auBDh0y\nf+733zPFwZaUKzNGMHWL0H+VOzc3shw5bBtv2TLg1Ve1WZszMHMmc+VVoJix8fGh0Orene4cd6BJ\nEwqXf/6xbZxmzRj13awZcO6cNmuzllq1aMK/cMH2sYYPp5Uls4IlTz5J//zChbbP6QhcQkhv3Ai8\n+GLm6ULXrjEoaNAg2+eMjGQd7IAA28eyhVGjGGE+ebLtAjo+Hli/3vEHD3sRHs6iGdOns4CGwth0\n7w5UqAB89ZWjV2IfnnySGvCiRbaP1bkzP7eAAODqVdvHsxZPTx4W1q2zfaxKleh7nzw58+d26eK8\nhU1cQkgvWkS/S2aMG0f/hBa5v+vWUeO0tIGHHsybR1/LmjVAnjy2j7d5M4v3Fy1q+1hGx2QC3nmH\nOfLPPefo1SgswcMDmDGD5luj+Fj1Rkvh8u67LPAREGBbURFb0cIvbearr+ifjop6/PP8/Hg4OX1a\nm3ntidML6dhYCqn27R//vOvXgTlztNGiAWDPHqB1a23GsoaQEGDAAH7ZtSo4smxZ5p+jqzBzJk2o\nH3/s6JUoskKhQhTUvXu7R31vPz/g0iXg7Fltxhs8mClan3xCN6EjaNKE2RSW1uF+HBUqMC5o4sTH\nPy9bNipyzqhNO72Q3rABqF49c+1v7Fiay7QQaCKMgG7SxPaxrOHYMfrnFiyg5qsFyckMhLO0+YYz\nc/06MGwYC/Y7OuhPkXVatWJMyfjxjl6J/mTPzoBYLUzeZkaPZsngnj0dk9bm5cWGG1qlhg0bxgCy\nzNqcmgPxnC2Vz+mFtCWmbq216NOnGajliHSda9doLvrhB20DvHbsYJUnLQLqjM7nn3OD0uqAo7Av\nHh5Mxxo/nmmQrk7HjtqZhwF+fr/+yiCyb7/Vbtys0KKFNn5pgBaxdu0YRPY46tRh3I0Ra5s/DqcW\n0jExDHTKLKp73Dhq0cWLazPv9u1sDWdv7t+naad3b+3bR7qLqXvrVv793CX4yFUpWxb47DNWnnI2\nzSir1KsHHD0K3Lyp3Zg5c7Ja4qxZ2mrplmL2S2v1t/vf/1hx8nG+dg8PatO25J47AqcW0uvXM6S/\nUKGMn3P9OjB7tnZaNACEhgINGmg3niUkJfEL5uvLL6SWiLAij6ubupOT6YOePJmWEIVz8/nnjIDe\nsMHRK9GXHDlYNeuvv7Qdt1gxppF+8AGwb5+2Y2dG5cpAuXLaBXKVKUOLQ2badIcOrJPhTGR39AJs\nwRJT96RJ2mrRADWxgQO1G88Svv2Wp9/p07WvE37wIDc7Vzf//vYbo+C1CPiLiWEK1+XLzPu8fTvt\nz4IF2ZP6YXx82PikQIGUq3Rpfj8rVuT/jcaDBwxcunyZEbLXrvHngweMqo2N5VWqFE2oOXLw+5Qj\nBzfixEQepAsV4nstUQIoXx7w9rbtu5wjBzs/DRrE+BBXji9o1oyHEa0taNWqsQxwu3ZsXGEvF56H\nB+OIdu60rEqkJQwZwiqSgwax0El6VK9O3/X5887j2vMQcU5j0YMH3NjOneOGmB537zKXbs8etjnT\ngqtXgapVuRHbqwnDihWMxjx4kBub1kycSFP6l19qP7ZRiIkBnn2Wpq46dSx/XXIyhe3Bg8xHv3eP\nFpyICN7kPj5A/vz8DhYqlPLT2zv970dCAjeJiIiUy8ODB79Tp5ivXbEirzp1qHHUqJH1euIeHh7I\n6q0twvvpwAH67Y4eZZDitWs82Ny7x3uuWDH+9PZmCmKuXFxfzpz8vOLj+T7j4xlBfOMG7xfzPRMa\nCpw5w/ddoUJKS9RatbiJZiWtUYRWrd69mVLnqly6xM/mxg19DiNTptDltXGj/WoGTJxITdraRiLp\n0bkz3QOPy9p45x2Whe7XT7t59cRphfTataywNWdOxs+ZMIE9o7UMu1+8mB1mVq3SbszHcf48ULs2\nK6rVrq3PHI0a0TLQrJk+4xuBqVOBEyfYp/dxJCTQ9Ld1KzsIibA6kq8vN8kaNShYSpXSfrMU4SZ8\n6hSvS5do4jx+nAfDtm0pyBo0yLxwjSVCWoTCcuvWtO+3dWsK4SpVgOef50Eku8Y2NxEeUM6c4Xd8\n507eq0eP8vNt0IA18Zs0ebw7C2DOdM+ePEi5cnOUKlUY8KXHPmAyMWq+ShVtSiZbwo4dVD60NLXv\n3s06/GfOZHx/LlzIPXzNGu3m1ROnFdL9+tFk9tln6f8+OZma07x52jbA+OgjmoQsbTxuCwkJDFDr\n0gX49FP95ihQgKZMLy995nA0Dx5Q0ISEUDN9mCtXeMOuXk2B/OSTLB3bsCE/f0eboB88YOP6PXvo\nTzt1ig3tW7XilZ51JSMhLcL614sX8ypYkCZpPz9e5co5tu1qfDxrTIeFMfo3JIRWhWbNWIP5pZfS\n33xff53r/+ADuy/ZbowaRXeNXrn9t2/zIPrTTwxQ1ZuYGB7AIiNt6zfwMHXqMB+8bdv0fx8ZSR/2\nzZvWtfK1O+KkVKwocvBgxr9fuVKkVi0Rk0nbeX19RXbu1HbMjPj4Y5HWrbV/D6nZvVvkxRf1G98I\njB8v0rFj2sdu3hSZOFGkWzeR/PlFunYV+fNPkYgIx6wxK1y/LjJ7tsh774nkyyfy+usia9eKJCam\nPOfhW/vkSZHhw0XKlRPx8REZNEhk3z59v1taEB8vsnmzyMCBIlWrijRpwve9a1fate/ZI/LMM3y+\nq7J8uUizZvrOERoqUriwyKVL+s5j5oUXRPbu1XbMBQtEXnnl8c+pV0/kr7+0nVcvnFJIX7ki4u0t\nkpyc8XMaNRKZN0/bee/eFcmd2z4bwbJlIqVL6y80xo0T+eADfedwJDExIsWKiRw6xL/bsmU8+OTL\nJ9K9u0hQkEhCgqNXaT2RkSI//yxSp45I0aIi330ncvEihXRCgsjixbwXihQRGTVK5MAB4wvmxxEe\nLvLttyIVKog8+yz/ffEif9e4scgvvzh2fXpy/bqIl9fj9z0tGDOGQswe90WvXiLTpmk7ZmKiSKlS\n/K5nxDffiHzyibbz6oVTpmCFhNAUmVHg1t9/0/9oSevKrLBrF1Czpu2NLDLj/HnW2V24UJ9AsdQ4\nIp3MnsyZw9rcs2YxqnjSJJrBLl9mtLe/v3M318ifn9+VXbt4X8THs9kMwGI3U6YAffrQvz10KM2Z\njjRn20rZskxBPHWKzXL++YfxAo0a8Xv83Xd0dbkiRYrQ9XLypL7zDBwI5Mtnn0DSWrW0T//Knh34\n8MPHlwpt3pwBoM6A0wrpx1XbmjyZ7Ru1FqbHjtEvpifx8UwrGzpUv0AxMyYThbQjCrPYg9BQxg6E\nhVFA79nD4KgePYC8eR29Ou0pUYIHDnOQ14kTfJ8VKuh/sLQ3Hh70PU6fzoyLDz6gr/3qVeDttx3b\nQEJP6tdnwJWeeHryADt/vraVztKjZk19crT79GGcybVr6f++WjWmD4aHaz+31jitkG7UKP3fRUQw\nzebdd7Wfd/duRvXqybhxjKy1R+OHU6eYT1iihP5z2QsR5pP6+bGCmrc3ta3Bgxk85ookJbHaUoUK\nDIbZvp2P//MPrQatWwNvvklt2hV58kkWqVi2jD2GQ0IYNDpkSMabtLNSrx4j4fWmYEFmxUyfrm2l\ns4epWpWWw/v3tR03f34G3GaU3uXpSYXLGbRppxPSFy/yD5pelC7AxPy2bYHChbWf+9gxfQt+7NrF\nL9WsWfYxSW7f7jqm7uRkRitXr85KVL17899ff+3YdqJ6s2kTtYKFC7nhTJrE2gAA05F69+ZhrFy5\nlGp10dGOXbOefPwxMxZWr6ZVqkoVdovTU9DYE3sJaYAWtsqVmSalFzlyMM0vLEz7sfv3Z4ZAYmL6\nv2/WTAlpXQgJoZaUnhAT4aalR5J6YiJNI+YNUGsSEmiimTAh87xQrThzhr59Z2fTJuYvr14NfPMN\nU3jq1ePPzp0dvToSGBiIwMBADB48+L/Hli5diuDgYAQGBmb5sTlzlsLfPxgDBwbi22/ZC9zXN/25\n8+ThYeXQIfriO3bUrrmB0XjqKfaMX72aJSKPHuW9+9xz9FffvevoFdpG5cqs2pZZxyetGDGCbiI9\nzd56+KUB7tVPPZVx2dgmTVIK7xgZpxPSmzdnbOres4c5rzVraj/vmTM0deuVVzd2LHP3MitzqiU7\nd1LDclbMMQL9+rFd3dy5zO/09AR++YUlFI2QBxkcHIzGjRujT58+CA8PR3BwMML+VR38/f0BAGFh\nYTh48KBFj40bdxCffgq8+KI/+vQBSpcOs8jyUqoUfY2DBzOwpkcP+uVcjb59Wa8/IYGuo8mTeUCJ\njOTGPXs24zGckWzZaAk5etQ+8+XKxd7r/frpZ4HRS0gDrC6WUcGr/PlpYTl0SJ+5tcKphLTI44PG\n5s9ntRk9TMXHjmVsYreVkycZifjTT/aLvBVhYNFzz9lnPi25f58m7XbtGKV57Bh9kubPzmQC/viD\nPikjEB4ejqCgIACAj48PwsPDsXDhQnj9Wz3Gx8cHQUFBWLRo0WMfK1rUBx9/HITRoxdh5Egv/Pgj\nUKmSz39jW8qrr9LKkDMnfYJbt2r4Zg1AxYr8Xq9cmfJYqVJs77pqFYVOnTo81DsjVarwO28v/P15\nad3Yx0zNmvoF+nXqBAQHs1BLetStSzejkXEqIX3uHIVLhQqP/i4piQ039NqY9fJHm0w8+Q8frl19\ncUu4dYs/7WVa1wIRBgdVrsz1h4ayAtzDkcs7dtDEa05FcjR9+vRBnz59AAAHDx5EzZo1ERUVhQKp\nSplFREQgKioK3qly7lI/duwYtZl79yLQvn0UGjRI+7z0GDFixH/Xli1b0vwuTx4eCmfPZne17793\nrZaP777LzflhatXi96N/fwYWDh0K3Llj//XZgr2FNMADzpIl+gi0Z5/lQVEPs3O+fLSuzZ+f/u/t\n6eO3FqcS0ps3UwtIT9sMCeFpOT0BrgXHj+sjpGfN4pfT3sXezVq0s+TMRkQwQnnyZNbdnTs34+DA\nefNo6jbaezt48CBq1KgB33+dx2KhVFy/nnEYXbrQvG9pHe3UQtrPzy/d5zRuzJrZy5axroCrBJW1\nasXo5PQEsKcnvx/Hj7NpSNWqzlPHGXCMkPb2prWvd2/thekTTzDD5OJFbcc18847PIymh9KkNcYS\nU7de6KFJX71KE1JgoP3b7DmTqTsoiFpxsWJsOPG4YLf4eHby0fO7YC3BwcEYPXo0AMDLywuR/0b/\nmLXqhx/z9i6Av//2wrp1kQgKAmrXfvR5d+7cSaORW0OpUsC2bdyI/f2dI3c0M3Ln5l7xOOGbLx8b\nr8ybx6jwt96yX0CWLThCSAM8xJUrB4wZo/3YPj76fe8aNeIhPz3fc/nybLN65Yo+c2uB0whpsz86\nvaCx2Fi2c9QrkjchgV8grfqemunfn2a5qlW1HdcSTp7UL1JdK+LjmT5jPgmPH595If5t2xgspHc+\ne1aZOXMmBv7bhDw4OBidO3dG+L+7Unh4OAICAtI8duZMOEJDA3D3bme0bx+OF19M/3nnz59HQECA\nzet78kn6anv3Zk9eewUm6UnbttwXMsPPjz56b2+gRQvj++iLF+eeZHZZ2QsPD7pIpkyhFUJL9BTS\nnp4scDN37qO/8/AwvjbtNEL6zBmeesqUefR369YxJ7Z4cX3mPneOc2sZKbxiBTdCR/VwNromfewY\nOx5duMDexpbKoTVruNEaiaCgIAwePBjly5eHt7c3PDw8/jN5BwcHw8vLC9WqVfvvsXXrgrF+vRdE\nqmHPHl/kzZv+81I/phV9+7Kgjr+/8wZWmWnVilaY2NjMn5s7N825X31FH/3IkcYtL+rhwYDJ06ft\nP3fJkkzL6tNH2wh5PYU0wIP+H3+kb6qvW9fgfmlHFw+3lPnzH+1kZKZDB5HAQP3mXrtW5LXXtBsv\nKkqkRAmRLVu0GzOrlColcvas4+bPCJNJZM4ckYIF2SwhK80gTCaRsmVFDh/Wb316ExMjEhAg0qWL\nbQ0ObL2116zh3yA42KZhHM6rr7IjXla4coWva9ZM5OpVfdZlK2++KTJ3rmPmTk4WqVtXZOpU7cZc\nuFCkfXvtxkuPBg3S/y5s2cIGNUbFaTTpjHzCd++ymEWHDvrNfe0a/aFaMWUKUwMcVUjk/n2mJKRn\nlXAkiYmswTxlCiNwe/bMWvDXyZOM8neE+0ALEhLYN7xQIeYzO7LxR4sWrOD22WcpZUadkY4dWc43\nKxQvzj3F35/18/fv12dttlCqFAvTOAJPT8bRzJmjXepUuXL6x0K8/TaDCR+mZk26O+Li9J3fWpxe\nSC9fzgCR/Pn1m1tLIX3pEquKDRqkzXjWcPky80TtHaz2OKKiKBguXGAU/7PPZn2MzZt5+DFaVLcl\nmEw0yd28Sd+ZpRHceuLnx9SbDh3ocnBGXnmFqZlZJVs2xkNMmsTykRml8DiKZ55xbC32KlXojvrm\nG23GM5u79UwDbNGC1cceNnnnzs34nH/rBhkOpxLSzz//6ON6R3UD2grpkSOB995j2zlHcesWNU6j\ncO4c/ULPPcdiE08/bd04ISEZl8Y0Ol98wU33jz+MIaDNNG7MNMEWLRynudlC5crU9q5ft+717drx\n8Pfll7yMUqmsVCnHN0wZPpyR8WfP2j5W/vzU0PWMri9alN+HkJBHf9ewoT71w7XAKYR0bCw3iPLl\n0z5+4wZzPFu21Hf+q1e1EdInT1II/Rvk6zBu32aXGyOwfTvb7/XvT63FWgElwsjuV17Rdn32YNYs\nWhBWrWIVMKPRujWbLLz7LvDggaNXkzU8PXkAtKW9Y9Wq3GcuX6ZCEB+v3fqs5ZlnHH9oKlyY7pAh\nQ7QZT+/gMSDjiP+yZYG//9Z3bmtxCiF98iQF9MM+ukWLGMGpd5cjrTTpYcNYzvLfKo8O4/ZtNo93\nNL//Tp/h77/bXszl1Cl+D4yWepUZe/ey6tV33zEFyKgMGMCCE336OF9lspdftr0Hc8GCwIwZNJW2\nbOn4oi8lSjDS2tF88gmzALSIjq5XT//Wom3bslzswxYRR+WeW4JTCOmM/NH2MHUD2gjpAwf4Re7f\nX5s12YIRNOkff+Tfb8sWy9OrHseePc6nRUdE8PsbGGidD96eeHiw2tv58xk3LDAq9evbLqQBpmAu\nXkytq1Ejx7a/zJ+fwW2OThPLlYt+6c8/t/3wlpiof1GRChV4GN67N+3jzz9POWPEA6jTCunwcPoy\nGzfWd24R+rNsFdL/+x99WkbobexoIf3tt8DPP7N4hla52mFh6ccsGBURmo+7dAHatHH0aiwjZ05G\nnX/xhXFNg+lRqxZrEsTE2D5WtmzUqJs1Y+EXR1Wq8vRk7Ma9e46ZPzXdurF95urVto3j5WWfOupt\n2zLgODWFC/Nva23sgp44rZBet443id5pKpGRFKy2FDLZupXFWHr10m5dtuAoIS3Cg8qCBfQfa2ma\nPniQBW2chQULWFBGr85CelGhAtuqfved8fvwmsmVi4FBWh0sPDzYn7t+fWrUjtrY7SXUMiNbNmZV\njBxpmyaaP799WqeahfTDazWqydsphPTRo49qSWvW6NM3+mFsNXWLMLBi5MhHuzU5CkcJ6eHDWSVp\nyxZGWmqF+WbTsPCWrly9Sl/e3LnG6HedVd55hxrc9987eiWWU6CA9qUsBw2iFunvb/8SnQCFtFH6\ngbdpQ3P12rXWj2GvQ0eNGgxGPnky7eNVqhizHK7hhfSDBzypliuX8lhcHH1M6dXx1hpbhfSaNQwy\nMUpvY8AxQnrcOPrzpk7Vfu4bN6iVOtrPbinDhjEq1h6HTD3w8KC7IiSEFiJnoHJl7YU0wL9l+/Z0\nu+nVEzkj8uc3hiYN0Pz+1Ve0MFirTdtLk/bwSN/kbfZLGw3DC+kTJxhUk7rwxo4dTIuwR5S0LULa\nZKI5c9QoYxUOsbeQ/uknYPp0Brpk1F7SFs6e1a9Fqdbs3MkuXR9+6OiV2EapUmybOWCAo1diGXoJ\naYCCqWlToEkT+2q2RtKkAR5WYmJYMMQa7Gm+b9v20eIlytxtJen5ozdu5A1hD2JirE91WLWKFoBW\nrbRdk614eNgvgG3xYgYbBQXplzJy9uyjOfT/Z++842s6/zj+vUmMmJHYOxFqk9ojpEKpraGoVWq0\npUpL0WpRsyhVW6zaicQoip/E3iJBbJKgFTORkIis+/z++PTITXLHOeeedSPv1yuv1rlnPPeec57v\n891aRK+HmXv2bFQ5snW++QYmQ2PFIbSGuzvRv//Kc26dDqb/jh2VzaOuUEEbOdscdnawLIj1TSul\nSROh4uKBA5mDCWvVQsS+1iK8c4W0BRIS4GsRw+zZ8N9prUxlQoIyYzp5ErW4fX1RqEAuHj2Svo2o\nHOzeDV+8FntdiyFfPgSQff+99ia2rFSogMWcXOPU6SCcChRAzXklKpMlJkoTsS4lPXsimFdMu08l\nNemCBdGj3rBFpYsLXBZa6ymueSGdNWjsyRNUZ2rcWJnrp6WJiyA/dw7BJHJXQxODTif/pHrnDl7Y\nTZvkb3hx65a0DVDkQK9HZPvIkdA4cgoff4xFrFgTp1IUKYJqdnIKATs7FOaJjIR/Vm4cHLRV3pcI\nbr0hQ5ABIBSlfexeXghiNUTNxiWm0Px0kVWTDgpCQw2l6hunpoq71qJF8DtqyRfNIbeQjolBrefp\n05WxeDx9qm4tdD7s3o08Y6UsQEphZ4fFx4wZao/EMk2ayGfy5nB0hJtr2zZE78uJFoU0EYJkL17M\nHj1tiaJF0dVQqfrorVtn1/i1UBM9K5oW0snJCDQybKmopKmbSJwm/fAhNIshQ+QZk7XIKaTT0oj6\n9YOZf9gwea6RlSdP5AlIk5Jt2xBEqDXXhxR07460MqEtIZUmNRVBk3JTogSyOmbNkvc30aqQzp8f\nhXr++EPYcXnywF2gVMnV5s0RPJaUlLFNCzXRs6JpIf3sGV5+ThtljOjCBWnKSPJFjCa9fDn8jmrX\n6DaFnEJ68mSUKlSyFWfp0pgYtcq1a2gkokXXhxQ4OBBNnKi9do5ZUdKcWr062nz26iVf+VAnJ202\nZCEi+vJL9G4W6t/18lKuilqhQnDFGS6kcs3dAsmqIUVGYpVlmDMtN2lpwoT0mzcIlNJCjW5TyCWk\nd+yAxrh1q7Jm/nPn8MJpFV9fWFXkro6nJh9/jCh+tRtPmENpn2eXLrAo9e4tj8b79Kl2WmdmpUwZ\nfP/Vq4Udd/myst8pq18619wtkKy+xrNnETqvJELN3Vu3ojyllqON5RDSd+7ABx8QoHxRkZQU7VRz\ny0pqKqqsmXJ96PV4zm/fJrpyBea3q1eJ7t+HwFMjajopCRasu3cxpitXML6HD023qixRAj6+wEBl\nxyoEV1flhdrUqXg2f/hB+nO/eYMIe63yzTcoXiQkO4YxZV1CWf3SWtSkNdRePjtPn2bWpNUQ0kLM\n3YwhYGzOHHnHZC1SC+nUVJj3p0xRp4pWSop2tdTjxxFIV7EimoCEhkLo3byJ4hpPnsAylJaGlJCI\nCCzwTp+GqbBaNZj/2raFebNZM2Q2SBHNnpqKMZ05Q3TpEn7H//0P2x0dkaZSoAAWyvfuQUBXrIix\nt2uHZ6hFCxQKadKEaOBA+GI/+8z6sclBQoJyAacc9vZwAwwahDiVDh2kO/ebN9ouK9ugAZGnJ9H+\n/ehJzhclhXSLFghy437LXCEtkKzm7rNnifr0UXYMQjTp48dxs7UewSu1kJ49GzmGw4dLd04hpKZq\nU5N+8AAR7gkJ0DTbtYPwq1sX/ukqVdAX2Jw2lJSESSMigigkBEJ08GB0dmrSBGludevyH1N8PKKP\nd+xAERIvL6KyZSH869Uj+u03vHOm0sQYQ8GJe/egXV+7hvt/8SLOdfAgzLvt2mkvs0GtQCsXF1Rm\nGzAA5lyperlrXZMmwqJk5Ur+Qlppy1HhwlhknjsHrbp8edRdSE/XzvOraSFtaO5OSoLmoXSnIyGa\ndEAATL5az4OtXVu6SkUhIURLl0JDVCNyOT0dL7ZWXqiYGFRZ27wZwjUmBlWYBg0Slybm6Ahtulo1\ntEckQtTshQtEwcFIdStVimj0aAhsU5XkQkLgH9y2DcL0k08weQqNitfp4NstVozIwyNje2oqtPEb\nN4i++ALv65AhRD4+0Ki0ENWuZjT0Bx/gN//ySyI/P2l+j+RkbWvSRIhVGD0a7pOyZS3vr7S5mwjv\nw7FjENL58uHZfvwYC2gtoGlxYmjuDg3FikfpaEa+mnR8PAJn+veXf0zWIlVVnaQkaAeLFqn3QGvF\nHx0aCjNvv37QUL//Hu1US5cmGj9e2jxue3u4fX78ERrtrFmoZ1+lChqZGFahOnkSEcY9e6I05t27\nRLt2wT0hZdpanjzQ7r/9Fu/A6dMwl/fqhYX1pk3ql7AsXBh/ajFrFiwPUkXBFyuGIi1apmBBLNQ2\nbuS3v1pC2rAvuKcnFhVaQdNC2tDcrYY/moi/Jr17N262VtOuDHFxkSZf9IcfYCJV2gWRlaxlY5Xk\nzBmY9KZNI6pRAxq0nx8iWy9ckF+LtLeHe2XlSpjC791D+g8RBPPs2agpfecO0bhx8gf1NW+O4Fyk\nBwAAIABJREFU4LEqVZCOFxGBGI3Nm7Ht999NB5/JTUyMuguF/PmxWBk7Vhq/5/nz2k495Bg8mGjd\nOn6mbDWEdL16cP9w43v1Spl8er5oWkgbmrvVEtJ8NWk/P/jibAGuRq01HD4Ms+6yZdKMSSz588PM\nqrQZ8/p1FPEYM4aoRw8if3/khhv6G0+fhtBSijp1EE3L1QYPD8e/Bw9WLrCudm1YNjhLjZ0dOkTt\n3w9fOPeb/Pmn8pHWSUnqm4c9PGBtmDHDev+rtW10laJ5c3xXPoVd1BDSZcrguo8f499OTrCMagXN\nC2m1NWkXF8vm1NhYmBW11u3KFMWLWyekk5LgF121isjZWbpxiUGng0lNKe3s5UtMsj17wod14gSq\nKxkL4ImOVq7GPBF+gxEjEMBIBFNzv37Z6xPLiYMDTMrh4dk/e/99LGZWrkTBn6ZNIbSVwtERJmK1\nGTcO85m/v/hzJCQgHkPr5m4ivKOffQZtmu/+SqLTQZu+cgX/1loLUM0Kab0eFcdKlMB/ixSRt5OS\nKV69slwBZ+dORLOq6e8SgrWa9Pz50Mw6dpRuTNZQq5YyRTT+/hspGy9fQhCOHWt6AccYglGUypeP\njoYGnZICKwcRtLVp0+CnXrNGmXEQIVI8Ksr055xwHj0aecRffaXMIisiQn1NmggLmeXLEfEttroW\np0VrISCPDwMHIrDWUtcutbqp1a2LyHsi1BDPFdI8iIuDhpQvH4ItnJzUeSD59Djdts12TN1E1gnp\nf/6BX3HePGnHZA2JifL6kJKSELX/1VeIZF+92rJv99EjCAQlNLf79xHs0rIltBVDQdSuHdHChXDH\nLFgg/1iI8Hxdu2Z+Hzs7BJj5++P+1a+fuW2gHHCLfi3QvDliGcR2y4qOtg1TN0e5coj03r/f/H5l\ny6oTCJpVk841d/PA0NRtrKe0UlgyfTx9igChTp2UG5O1lCsnPrp74kSkkRg2PVGbMmUgFOXg8mUU\naImJge+7VSt+xz14oEy6YHQ0UZs2COIbP974QrZaNaK1axG05Osr/5jKl88cLWsOJyf4p3/9FQvd\n336Tz1f9/Ll2hDQRAuq2bsVzJZRHj/ilNGmJRo3MV6RLS4ObRI3g27p1M4R0ribNkxcvEBlLpG0h\nHRgIs6+p/FQtUqGC8DZyRDBRHjsGQa0l5BLSAQEobThhAtJmhEweT5/KX93q9WuYEb/8kujzz83v\nW748Ilh9fVEVTE7KleMvpDk+/hiadGAgfOkJCdKPq3hxbbU0LV6caOZMxHcINfPaStCYId26QZM2\nFWEfG4t3TI06EzVrIgMiOTlXk+ZNQkJG9xi1hbS5ovy2FNXN4eqKZiVCJga9HgJrzhztNbOoVEla\nczdjSF369luY9gcOFO5qSUhAXrKc/PILhO933/Hbv3JlosWLcdzdu/KNq0QJca6pcuWQY+7kBHOw\nOb+2UBISsAjQkpAmQuT9hQvCF062KKRLl4Yw5GImshITI101NqHkz4+Yp5s3czVp3uj1yAFlTF0h\nbc4nHR0NE4mU9XiVoGhRPJTPnvE/ZsMGaIZceo+WKFeO6NYtac6VkoJKWQEBiMCtX1/ceV68ENZY\nQCh//YUSnEuXChOITZoQ9e0LQZ2eLs/YChQQ30koXz74/IcPRye50FBpxnT/PhZzWgu0srdHkZNJ\nk4TdD1vzSXP06IFAW2PExqonpIkyTN65mjRP9HqYPbh2bKVLqzMOc+bugACkXWm9fq4xXF35ayoJ\nCfB5LlqkzZKn1aqhjrS1vHkDrTk9HdHb1vj8xPQh58vLlwhiW7IEwZVCGT0a955vSoxQ8ua1blGt\n0yFQ7/PPsQCWIk3rwQP+8QRK07kz5plNm/gfkzcvFh22Ro8eKPxkbEESE6NuSicX4V2kCFyCWkGD\nUy5IT4dA4LRotVbA5szdthbVbUi1aqhOxYclS/ByKZnzK4SqVa1vFP/mDb6jXo8gKzHCzxBHR/nS\nfebOxVhbtBB3vL09itD89JP1v5sxUlPRbtNaevSABadbN9MmUr5cuqTdnGKdDm6kKVP4VURjjGjP\nHm23wzVFlSpQuIxF8qtp7ibKiPC2t88IItMCmhXSnLn7+nV1yz6aMnc/eAATa9u2yo9JCtzc8Nta\n4vVr+GW//FL+MYmlbFmkhnEVg4SSnAwTcJEiCBCTQgO2t5cnLezFC3Ts4euHNkWdOijnKkfFOClb\nKHboAIvVpEnWadRXrgjrFqY0LVvinixfbnnf6GgoMGpZF63FlMlbbSFdowbmejs75avhmUPTQtrO\nDjeOi/JWA1Pm7gMHUM1JC80dxFCnjvGqUFlZswbFJ2rXln9MYtHpkO4kxn+p18MHXbIk6ktLZaJ2\ncZGnuM26dRirFClwgwahMYfUvumXL/F8SUXr1ijK0qOHeA09PFzbQpoIxWd8fS1r05cuIVZCa/51\nvvTogUp9WQNX1RbS5cvjt01PzxXSvODM3SEh0nbrEUqRIhkl+Aw5ckR8UJEW4COkU1JQtOSHH5QZ\nkzU0aCBOSP/0EyLdf/9dWh9ysWLSBT0Z8vff0lk16teH9UHqsqHR0dI3sujQAfeob1/hQWmJiUh3\n4hqPaJV69YgqVrTsm+aEtK1Spw5S9CIiMm9XO3AsTx5E/z97Jl9QpRg0K6Q5c/eDB3hw1cLODlqL\noV+aK/nYurVqw7KaqlXxopgrx7hpE/xeWvVFG9KihfDOQr6+SKH76y/pW6CWKyd9UQaurKWUTTv6\n90ejFCl58kSeEr59+yJlqUcPVIHjy7lzWHBqoSSoJcaPR9ldc5pcWJhtC2k7OxTgOXIk83a1NWki\nBOM9epSrSfOC06TVFtJEmMANizNERGBsatQSl4o8eWD+M+WXTk9HMIstaNFEqBe9bRv/tKdTp1CS\n8u+/5alCVbky0aFDwoSJJc6cgUlOygj7Nm2kz5m+elW+d3bsWCwchw/nn+d/8iR8vrbABx8ghc1c\n3rSta9JE+J5ZgwFzhbRxZK6JJB69Hn8pKep3WnJ3x0RWrx7+fewY0jls1SfEUbs2Cik0apT9s8BA\nmAi9vBQflihKlEDk6Llzlifkp08RNLV8OaLc5SBPHjw3N25YXx40LQ2BT5s2wbo0eTKevfz58b2r\nVYO535wPnDGMJTyc6N9/MSHGxGABGhGB++3pKY1r6coVaLtyoNMhj7pVK/jnhwyxfExYGL/9tIBO\nR/T993Azde2a/fOXLyFE5HpulYIrZWvYmtLFRf2yrVoU0prVpPV6RIlWqKC+MOSENIetm7o5zLUK\nXLYM5T/V/u2F0K4dtFdzMIbOSwMHIj9VTpo0QUyFGNLTUaykd28son74AVaPMmVgwalaFZPa48dI\nGevRAxXhst7PmBiUnfz4Y0RInz+fYQVq0AAar50dguZ69cJ+v/0Gv7IY9Hq4UOQM0ipQAFaTCRMs\nB5IlJhIFBWk3R9oYPj5YSBlLU7pyBYtre3vlxyUllSvjPt64gX8zhmBctXO/K1WC1VRLQlqzmnR6\nOtJ/1DZ1E0FIX7yY8e9jx2zHDGyO5s1R/jIrV6+ijq1WWlHypV07dBWaNs30PitXwnqgRB/jxo2h\n2Q8fzv+Y1FRoiAEBmLh69UIRmdKlMTl/843xyOm4OPjXp0/PmOjWr8dfnTrIwa1Xz/ii6+xZFA7x\n9kYRl6AgpD0OG4b+1FWq8B9/eDjahsqtEbm7oylH//74jU0VFDp8GA1SihaVdzxS4uCA+WX1arhx\nDMkJpm4OzuRdsyYWhYUKqZ/Lzgnp3MAxHuj18OdpQUhXrZqhSd+7h8hVWywkkJXq1RFR+eRJ5u2r\nVsE8KHeDCKlp2RJCwlRJv4gIRHNv2ABztNy0aQPtgK/v9MQJog8/hICeNg1WgeHDM/JhixUzLYyc\nnCBQd+/OeGfWrUPZ0MWLzafsFCoEk7qDA8Y8axYi3gsWRCT5xImW+wBzHD8OYa8EgwdDI5syxfQ+\n+/bJbzGRAx8fuCCytpT999/sgttWMQweu3MH86zaVKwIIa1Ei1m+aFpIa0mTvnMH/59T/NFEMHM2\naZLZrPb6NUyfQ4eqNy6x5M+P2uJ79mT/jGua8cMPKFqgBO7u0BIspWKlpMAUPXIkipT873/GJ+LC\nhS0HouXJk7kNKZ/nNDk5e+RzsWIQfhs2ILOhaVN+VZi2b1eubatOhwVlSAjM+FnR62ElsEUh7eyM\n33Hz5szb/f2Nx5DYIh98gPQ/vV47QrpSJSyEcmt384Azd2uhhmr58qge9fp1zvFHc3TsiO/EsX07\nJmS1fUNi8fIi2rgx+/aNG1GXd/RoZcfTuDH8p6Z48gRWi6tXoVWYEyjFi1tuAennl9G2c9gwNNKw\nFPH+9Klp83Tp0nARjBuH9CB/f9PnefgQ36NdO/PXk5KSJVGU5auvspsojx/H4sxWg6yGDkWaIGeJ\niYqCj71mTXXHJRVly+J9vXYNtfe1cJ8KFoRlSUvpepoV0gULwrSnBSFtb5/R3vH48ZwlpFu3Rp4w\nNxH4+grzoWqNbt2gVRkGPsXFIVhs2TLlA2769YMJ3pigjIqCOblaNZipLaWflC6dEWhjjJQUCOmx\nYzOu7ehItGuX6WPevIFgtdRWc+BA1Az/7js8I8bYuhXfR+mGM/37Y1Jduzbz9vXrIcBt1erl5YX7\nc+EC/n3kCEzEtvp9jFGgAN5XrWjSRPCLa6mSpKxC+qgVpYwcHLAyVzuQgMPdHQEqcXFYyVrz3bRE\n3bqY3G/dgt89Ph5mNlv9fgUKIEJ5y5aMbbNno2KVoZlQqe9Xsya0n6yC8uFDoi++wKT788/8cp+b\nNYPWQUQUGBhIwcHB5GsgMU+ehLbdsCH+rdMRde8OX7cpzpxBYFiBApavX68e8ssXLkT1L0PS09GI\npXt3y+eRAsP7p9Ph2lOnZpj6X73Cb96vnzLjkRLuu+l0cIFwOdOHDyvn75cTw3vXsCHcFVrRpImw\n4LNmoSn13KJZIV20KHxlWonKrFoVL4mnJyZUWxViWdHpYPLetw+mzNatsUCy5e83cCB8qUSoQrZ6\nNdGPP2beR8nvN24cNGXOWhEfj0VDmzZoycgXb298n+Dg0P/+jRk7LCyMHj4k2r8fEwznT9PrYca+\ndAmmfmPa/IEDwgRrxYo45siRzOUrAwKQLaCUvzTr/atfH5Hwc+dmjKd1a5R5tDUMv1v37sjnT0mB\n5cVW6haYI6uQvngR301IFoGc5M1rXWCpTQlpayhaFA+mVoS0uzvRzZtE7durPRLp6dQJQtrPj+iT\nT9QejfV4ekI4XbwIE/eIESjTqRadO6NyXlAQBOeAAYje/f57YecpUgRCcOZMfyr2X/hpXJwbjRwZ\nRKNGQcs9fx4aOhFMzwsXIhBm0SKinj1RcpIT4o8fQ3gL1TYrViSaORPWigsXMqrTDRgg7DxSw5ni\nnzyBqfuzz9QdjxRUrgxXm68vIr0tuSVsjfr14Q5yceFnzVGCvHm1ldmioaFkpkgRvPxaEtKRkdrx\nm0iJt3dGq0ZbKZ9oDjs7oo8+gvnz9OmMyHy1sLeHn/jHHxEh/fw5/l+Mb3HoUKKNG+MoIcGZJk+G\nKbtMmRjatg1R4QcOwES6bRtMdp9/juCqpUvx/C5fjkXLhAnYp3VrFEgRSu3aCEybNAmFVCpWhHVA\nTSpUwILj22/hy1UqylxuPvkE1iClXAlKUrAgrB1qNlHKipYENBGRjjG+WZwiTp6TIhxyySWXXHLJ\nhQdSilVZzd2MMdF/d+8yIhJ/vNR/9+8z0ukYPXqk/ljk+KtXj9FHH6k/Dqn+Hj9mVKkSowED1B8L\nY4zS0xk1bsyoZk1Gd+5Ydy4vrwlUqVIQffYZo3XrttPcuXOJMUavXjHq2pVRkybcBMHI05ORv3/m\n47dsYeThgb/YWPHj0OsZde/OqEIFRqdPq/8bM8boxg1G+fMzmjJF/bFI9XfrFqO8eRkFBak/Fjn+\nGjbU1tzTqhXeG2vOISWa9UlzQS5paeqOg+PaNRQYMFZP19b5919UUgsJ0VbNWmtYtQqm+717xdeh\nlhKuYcnQoagmJrbf8u3bRIz1pi++iKT33iP67bcoSktrR0lJCOaqXBl5zUTwSU+enNFtKDwcuc67\ndyPoq1Ur1PUWi58fXAlTp6IqmRaenTlzYOL39UVMS05g1y5E1pvrjGXL/Ptv9qqHasKYtkzemhXS\nr17Bl/fqldojAeHhSBFQouaz0owZM486doRvesaM7Kk9xtJ9+G5Tg9RUCKrvv0cw05o1qg6H0tJQ\njvSbb4jGjEHVMK4DkFDWriX6+GMPcnEhatQomDp1cqJHj+pTr14I5jpzBjWtifD+rFgBP/Wnn+Jz\nd3f8HjVrYkwXL2KSFEpICNHXX0NQf/YZrmUuH1sJoqIgyGbMQNnegAB1x2OMefPmvf1/vu/Qzp2B\n1LhxMPn7a+9ds5YnT1Ak6v59tUeSQUqKtO1grcXq9UJgYCA5OTlRZGQkDRs2TLJte/dGkoPDMIqP\nV7eOamhoKEVFRdGOHbHk6TmMzpyR/jurSVBQEJ04cYjmzx9PYWGhdPgw0c8/e1NkZCSFhYW9Nd14\newvf5uHhocp3OngQUaPnzvlShQpEP/0UQf37zyFXV3Xu3Y4dKL/arh2Cxdatw//Pnw/Nli9xcSim\ns3cvkbMzrs+lYcXH43wvXhC1aBFIW7cS1a3rS/37D6PJk9F4w9U18+Tj4oJGGjt3QuDy5c4d1M1e\nuxbHEyHNbOlSBJGJDUXhBE5ERATNmTOHiITdm1WrnKhNm0hychpGY8YQzZ4dSKVKaetdO3ToEI0f\nP55CQzPS6My9Q1FRjG7cIDpyxJvWrYukgwfDqEQJ7bxrHNw8GRsbK+gdOneOyNU1kO7edaKFCyNp\n7Fj158nXr7MHLMsl5/h8P6vWC2FhYeTm5kbe3t7k5uZGYWFhFBYWRkSZczgNH0i+25KSiIoWDaO4\nOGtGaD1z5swhHx8fevUqjt57L4wuXgyltDTrvx+3TW3S0nQUG4vUstRUPwoJKUbx8URubm4UFBRE\n/v7+5OTkRETCt6nF5s1EVaoEU9u2bWncuGHk7h5JgwYFS/Zsctv4smgRKqFxwqtwYWidmzYJMzcf\nPozFh7H+6kWLQvDfvx+aqVrSqVNhVKcOclCNaQcffWS87rUp7tyBQB8zhqhLl4ztHTqgaItYd1Bw\nMO7XsGHDKDIykoKDhd2vu3eJwsO9ydMT20qVCqWrV4mqVtXOu2YYSOvn5/c2jc7cOzR3rj99+KET\n5c9P1LChG61dq613jYObJ+Pi4gS9Qzt3hlK1akS1a3vT48famCcTEzNbueSUc9w2c1it1E+YMIGI\niCIjI8nDw4P3w2dpW6FCbvTmTZDFWsVyEhAQQI0aNSLGiO7fH0+9enlQ4cL+9Py59d9PCy9XWFgY\nFSrkTcWKIQUiNTWemjd3pq1b8XlMTAzFxcWRs4FUELJNDRISUNSjcuXIt79v+/ZudO1aJM2f7yf6\nPom9d1evwhLUtWvm7aVLY5xr10LL5RN7cfWq8TaVHM2aEd265U/R0Xg+XV3dyN8/yGwv5aZNkS+d\nkGD5+ufPw4/dqxf8vobY2UFor15t+TzGiIzMuF9ubm4UGRlJfn787pefnz/t2eNE06cT1ayJbTt3\n+lPr1k60bZt23jVuUiYiio+Pt/gOPXsWQ+HhcdS7N7Y1aEB09ap23jUObp4kIho/fjx5eHiQv78/\nLzlw+LA/vf++E1WvTpSWpo158uXL7PEVcsk5Pt/PKiHt4eFBrq6u5Ozs/PahsWZSN9yWnk6UN2+M\nqkI6JCSEYmJi6MiRMNLr51HRokQlSsTRvXvWfz9um5rExsbSiROo78zRvTsTPdFqgV27EDA2duyw\nt2akK1dCacKEhnTuXBwVKZJRIFuJe7dlC3KKjQWilC2LYK8TJ9Ag4vFj8+e6dg2BYabIk4fI1TWO\n/vc/jPPAAaLXr2PMNu3Imxe5qlwrVmPo9TBld+6MIiFZBTTHp5/CdM63raUhw4Zl3K/Q0FBq2LAh\nxcXFkYuL5ft19mwcvXrl/LZ4Cbdf9+7Ob7tIaeFdy4qlKOCoKDR7cHPDvxs0QICnxMHDVsPNk2Fh\nYW997nzeoWfPYig6Oo6aNXOm6tVR8EfteZIxuI4Mq/PJKee4beawyicdFxdH7u7u5OvrS8OGDaP3\n33//vy9q/VPE9bdVU0gTERUvXpwKF/YgZ+cgCgwMpFKliC5f1thbYgJjQSXOzs7k4+PzdmW/YEGG\nz9/JyYmqVo2lZ8+IwsIyJkhugjGcNM1te/HiRabJVUk2b0ZZUI7Q0FBq0KABjR/vQUuXEvn6MsVK\nVzKWYdY2RbFiqPY2cyb8vP37Q9gZ8+tev265cpqrK5Fej+fz5EmU6rQUqeriQvTsmfHPoqKwgHBy\nQkUzc8V8ypRBwY1Dh2DeFwN3vzgfq6W5JDERGv769dmbp3h4wEd/7564sUhFVi2aCO+apffq1i0X\n8vDI2JY/fxwVKOBCqanaeNcMKV68OHl4eFBQEOZJIsv37vlzLEKKFEH72OPHlRipeeLjsdg1XGjK\nKef4YFFIm5vofX19acSIEVSkSBFycnKigIAAXg8fn22vX7+gAgVcREWeCsHc93NxcSFXV1eKiiIq\nWdKJLly4QFWrOtGOHdZ/PyVeLnMBCZGRkXT3biQdORJD1arFUlhYGPXu3ZtCQkJoyBBv2rkzkpYu\nbUeMMQoJCXkbpNKuneVtUVFR1E6BfoVZ711yMlF4uDN17uzzdltwcDDNnj2biIi6dHGizZtjafhw\nZe7d3btYkVuK6cmTB2lMFy4Q/fYbBPuwYRmBZhzFilku/F+woBPFxGS8Q/nyWR6no2N2c/vjx0hj\nWrkyIyqdTz3junWx6BArpA3vl6W55MWLODp50oWaNiUqXjz7fnFxsdS3L1Fw8Avy8FBPkEVGRlJk\nZCTFxMRQbGzmd83UOxQeHklRUe1o6VJGN29m7NeqVTsqVYpRZKSy75o5uHmSCPfswoULvORAXJwL\nVaiAbZUrE129GkcffKDugv/JE5QUNswqklPO8fl+FoW0pcizIv+1qeIeorZt2wqe1I1ti4mJonLl\n2smuSZv7fj179qSAgADS64kKF46jxo0bU+XKrrRlSwj9+69130/tl8vHx4cuXyYqUsSXEhPjSafT\nUf369SkkJISqVQumuXOd6L336pOjI8xZwcHB5OTkRPXr1yci/tvkJOu9+/tvpMkVLox/r1q1isb/\nFz4dHBxMn3/em5KTQ2jQIG/q3z+SPvpI3nt39Ch8vnyjnRs1gnl8xw6kSS1cCD9v+/YI/CpbFuUu\njfHwIfKfz53rTY6OIUREVK9eFIWGtqPRo1G3u0UL4606X72C2fv1awSnbdqE87VuDS21bFl+4yci\n+vDDzP3JhZD1flkSZH5+kZSU1I5WrGB05Yrx/by8vGnUqCj6/nt13zUiTPbx8ZnfNVPv0PXrTjRy\nZH3y9CS6eTNjv7Zt69PFi0Rubsq+a+bg5kkiCKLGjRuTq6urxfcqLa0dde2KBUf9+t707Jn68+Tj\nx4jPyVpbQS45x+f7WWXuHj9+PM2bN4/c3Nwyhd5bM6lz2woUcCJHx/oUHm7NCK3D1dX1v5VTIOXL\nF0sff4wJxM0thFatCqYKFcR/Py28XGFhRD17DqMlSzKEHXcPN2zwpsBAmF+5bYYmO77blCQoKKOV\nX1BQEE2cOJHmzp1LsbGxFBAQQB4eHtSoUQg9eBBMp0870aRJ8t67a9eE9x63s4NA9fGBr/rAAURT\n58mD+7VgAdH770NDT0tDWlZEBEzbPj5EixZ5UGhoCAUFEbVq5UTLltWnrVvRaGTGDORJly6NCHF7\ne5iD9+xBTXHOTebjQ9Snj/EockvUqAE/+7Nn0Ej4Yup+mboP69YF06VLTrRrV32qVYvoyhXj++n1\nwfTwoRNVqaLuu0aU2e/O/Zso+zuUkkI0aJA3TZqUfb/z51GoZ/Vqdd81Q7h5MjAwkGJjY98utCy9\nQ7dv16cZM4jOnQuhsLBgSktzInf3+lSokHrzZEwMLF+GcTpyyjk+30/W2t3WsHgxOvQEBmIiUZMO\nHTBRcgX7Z8/GJLRggbrjspbhw1HJaOTI7J/t24d2j9u22U6T+bp1YaJt0sT8frGxCMKZOxeRynJR\nqxZ85NbOMYyh0tjEiTB316mD71CyJARuzZq4j4apVzqdLpvP7J9/IIwfPIBwT0/H37p1mPhbtEAQ\nmbW0aYP8748+sv5cxnj1Cvf4229Rwc0S3t5ocGIugE5LbNwIH3twcPbPkpIQQxAbi7aktkp0NNGg\nQViEctadqlVRA+C999Qb14wZmNs3bszoTa42Gip+lh1HR/gZExOlmTzEEh+fOaq2TRuUdrR1rl9H\nkJIxOnTAxHb8uHBtUA0eP4YQatDA8r7Ozlj8de8OAccV5JCSlBR0nape3fpz6XSYuLp1IwoNzd4b\nmy8VKuDPkNBQWCA+/ND6cXK0bIl8ajmEtF6PALsWLfgJaCKkv12+bBtCmjHEJfznls+GoyOEWXi4\ncr275WDfPpTJNXS/lC+P6ndqCunbt2GpsqaftNRoqPiZcby8MNmpya1bmZvHN2hAdO6c+pHn1sAY\nhHTNmsY/t7dHWc3/Cj9pnnPniHr35l9z9/33oUl36SJP3eAHD6DBSantfPAB0dat0H6lgqvOJiXF\ni2Oyk4OpU9F3ePFi/sd4eNhO3evgYLgyzLX9bNAA5Vxtmb/+yl47gBPSanLrFtquGlql1EbzQrpA\nAQgTtWAMye2GZeIcHPAS7dun3ris5fFj+D/N+Q0HDIB5VAPFmixy4gRRpUrCjunTB9+xXz9xub3m\niI6GBUZKKlWCxitVqgpjEAqffCLN+TgqV5anFvOaNXAfzJsnbPHj4YHn2DD3VassWIASq+ZcTLYu\npBMTEVyY1dJSvjysYWrBuZUqVMgV0oKoVQuVltQiMRE3LKv5o3Nn21mdG+P6dfy25iaB47GnAAAg\nAElEQVSDfPng9zNletMSoaHQjoUydSp+Bx8fabsmPX4Mf7HUtGmDphZScPEiV+tbmvNxlCpF9PSp\ntOfcswclVP/+O7NViw+FC2OBc+2atGOSmnPn4A815YLiaNAAz7utEhRE1Lgxcu8NqVpV3YXUs2dQ\nXAoVyjV388LBAcKxdm11hXR8fPZi60RIizl6FIEctsi1a6ZN3YYMHw7TrZpR9pbQ67HoECOkdTr4\nAB0docFIJahfvRIuTPjQsyfSosxVCOPLr79CIEjd8adIEWmtCHv3wv+8erV4f2WjRvBLa5nJk5Ef\nbykXvnZt3DMttAYVw+7d2U3dRLCOyOUm4cPt20jhTE3N1aR5YW8PM7NWhbSzM8xoR44oPyYp4DRp\nSxQuDLPwDz/IPyaxREdbNt2bw8EBvt779yEETeUiC0Fq8zlH0aKISF++3LrzcC0qv/hCmnEZUqAA\nJjsp2LePaMgQaNLWBErVrAmTt1Y5ehSxN4MHW963UCGYhS2VkdUi6elYdBk2Z+EoVoxUbah06xYW\ngSkpuZo0LwoXhjbi7o4ALbU0VlNCmsi2Td7mgsay8uWX0KRPnpR3TGK5fdt8uUo+5MtHtH07VvOj\nR2OBaA2OjvJo0kRIB7xzR/wCMT0dZv4vv5Qna0KnkyaO4c8/Yd3YswfmUWtwd0c+uRZhDBH7U6fy\nFw6VK6Nkq61x7hzcQP8VKMuEk5O66ba3bmFxmZKSq0nzghPSefLgBbtxQ51x8BHS2sw0Nw1j/M3d\nRBBgv/xCNGGCNr/rnTvSaG5586LiV9688NNaE/yUkpK9apFUFCoEN8TcueI0jxUrYCrt31/6sRGh\nyIpYqwYRnrGZMyG0uF7c1uLmpl0hffAgFoWWfNGGuLrappA2FtXNobYmffs2NOlcczdPOCFNpK7J\n25yQrl4dmtelS8qOyVqePYNQE6Lp9euHieSvv+Qbl1iiojI6BVmLgwPSe4YORevHc+fEnadgQQQd\nykXnzigVOn68MN/k8eMIwFq+XHpfNEdCgvgAoKQk5Ofv2UN0+jQqmElBlSowJ2ttkZmaiu87Z47x\nkq2mqFxZ/cYhYjAnpLWiSScn55q7eWELQlqnQ/qKv7+yY7KWR48gQIRUErO3J/r9d6IpU/AQa4no\naHRgkgqdDk0lVq3C912xQvjkXry4dOMxxfz5EDx84wVu34YwWLIEuaByERODqlhCiYjAwujJE0QA\nS3lPixRBio/W/LhLliDyvGNHYcfZoiZ95w6EcMOGxj93coImrcZCKi0Nv6e7OxaZpuZ8NbAZIa1W\n+oQ5IU2EoKpNm6QtMCE3z5+LEyLe3ljBz58v+ZCs4tEjaSd0js6doXWuWIHSqULKBJYqJf/CMn9+\nLBAjIiwXnbl7F8VpevZEdy05efHCcucvQxiDm6FHD1gwtmyBSV9q8ueXp3CNWJ4+JZo1C41UhJbe\ndXW1PU16zx4EjJmy4OTPj2p1UgRuCuXePfjKHR3xnmdND1MTzQrpIkUyhLSaudKWhHTt2qihfPSo\nYkOyGrFCmgja9IIF2pogkpPlyUkmgvnrzBm8tHXrEu3fz++4cuWUMZm5uOCenD8PH64xLeTyZaK2\nbaGtDRki/5hu3uT/3Z8+xcJh5kzUqx41Sr5a8c7O6vcBMGTWLMQFiDHp22Lg2P79pk3dHGfOqKNJ\n37qVYc0QawmSC80KaUNN2tUVgsXaiFsxWBLSREQDByIS1VawRkhXrgwf2pgxkg7JKu7elde87OiI\nCXXDBgjC7t0tL1JKl0YEvRJCoVw59H2+ehWR3wkJGZ8FBkJA//47gs2UgE8gX3o6xjxkCCLzL14U\nl+cuhGLFtNM04cQJop074U4RQ5kyme+z1rl3D+9D27Zqj8Q4p09nBDvmCmmeODoiQjYtDeaRmjXV\nMXnzEdJ9+yIgwlZemufPrYu+HT8eCyitpJ+9epXRQ1pO2rRBOcPGjeFXmzMnc3N4Q3Q6WICUemZL\nlIDbRafLiNoeNYpo7Vp0GureXZlxEEEAm8scOHECv9+mTUTTp+N3VKKjk1Y06TdvULRk4ULxZtWC\nBfFdbKHUKRGeQx8f7XbuOnsWvd+JcoU0b3Q6rLBjYvBvtfzSfIR0yZJEnp5IF7EFrNGkiZCSNWUK\nCmGorZno9SgcIocP0xj58yNQKzQUUfJVqkDLNmbladdOWTdN/vyITK9dG/8+cYJo6VJ+ncGkIjUV\n70GdOtk/O3UKv8m4cUjnO35cmO/aWipXxqJfbWbOxCLm44/Fn0Ong0tQDeuiUNLSIKQNWmlrivR0\nopCQjFz8XCEtgLx5M3JN1fJL29vz66w0cCDMobbAs2fWm4dbtYIv8ZtvpBmTWFJSYCaVK53IFBUr\nopzosWNYPDZtSjRpUuaObW5u+FwpLl5E45eQEPzbywsTz6JFyhUDunIF7inOspGaiuC2li2hNffu\njaI4ffoo36c8MVF9TfrSJaL//Q9R3dZStKi6ecV8OXAAkfXGFm5a4MYNBHpygvn581whzZuyZTOE\ndOPG6rQxY4yfSalLF1RZUrOLC1+s1aQ5Zs2CmWjXLuvPJRZ7e3Xz1GvUQGemv/5CAFuTJqjrvmMH\nFg+JifIGwqSmwrc5eDDRV18hMIfLZV+0iOjwYSwcKldGQZoHD+QbCxEEcJcuqFA3YQK0xaVLEcfA\n1eBWKwdV7XrXSUmoNzBqFOY2aylaVPpOa3Lg66tdLZoos6mbKFeTFoShkG7QACsyuWoimyJfPn5N\nF/LnR03lzZvlH5O1SCWkCxQgWrcOaUpSdz3ii709zGlqF6lwd0fU+z//EA0aBIHk6Yl8319/RZqU\nVGN88wbnHTECGupvv6Ht34kTENSG1ZJq14awPnYM13//fVhB1q+XVmAnJSHDYeFCBFEOHAihOGcO\nru3jw7/Xt1yoLaQnToQ2KVWlN1sQ0tHReC579+a3vxrvsdaFtMqvjXkMhXTBgkiBOXsWATxKkTcv\n/+IdAwdCU5gwQXlTnhDS0xFEIwUtWxI1b46+zPv3K292trPLmHyFVGySi/z5Ud7x008hPD/7DAJ1\n8WIsJpo3J2rdGtG5VarAJF6smPHnRa+Ha+Kff+DquXoVUbIHDsDVUKsWnjU+1daqV0ccwcSJOP7Y\nMaKffoIvv0sXRKPXqgVTdZky2G5sTCkpyEuPisJYLlxAwGRAALT42Fjkw3p6Kv8sWMLeXr338uBB\nWDwuX5ZuDLYgpNetg/IiJGZE6Xt09iwWt0RYJLx4kSukeVO2bGZTZuvWmFyUFNL58vEX0s2aIfXk\n+HGMVas8fgwtWCqmTiX64ANojJMmSXdevnDatBaEtCHFiiElatIkuGr++QepHnfvwk8bGQmhd/s2\nfLgVK2LSTU7G9oQEbP/wQ0xctWsjl3PdOvHR7PnyEXXrhr958+CPu3QJedZ//4165dHRmKzq14dA\nrlsX/u7q1aEtN2oErbhlSwR3NmhAtGwZzOwvX2r32U9Kwj1RmuhoWDP+/FPa62tdSOv1RGvWaLsi\nY3w8Fpucv/zpU+VqHPBF00K6XDlMHBxeXvCDKokQIa3TIXBnwQLtTlQcUpqVuFaPDRtCg2rZUrpz\n872+Viu+eXkhuCciAiZxY+U4U1KQyvXyJe5Lvnz4K1zYcm9ha7C3h+CvXTu7CTYhISPFhxtTgQJI\nGTKlIW/aJKxJhNK8fIkSnEry5g388p06YSErJWXLarundHAwFhJKZhcI5cIFuIA4oXzvnjIlfYWg\nMYNUZsqWRZtKjhYtsKJXsm1l3rz8fNIcAweiao6azcstIYc5qXx5rJq/+055/3SzZtqrJ87h4ABz\n8tq1pvfJmxfmNVdXmK7LlcNEIaeAtkShQkQVKmA8Varg/jo7mxbQT54gxUrJfGyh8EmnlBLG0A60\nfHm0opSaFy+EzU1KwwWMadn1l9UfHRVlvI2mmmheSBu2+ytUCKt+sZ2JxCBEkyaCtjFiBCo8aRk5\nAjQ6dcLfxx8rKzTv30cwnFYZNgwmalspPCGGtWuRNy9Hf2qpePlSWSG9eDGUivXr5fHP6/XaFYBP\nniCzoF8/tUdinqxC+t49ZEJoCU0L6ZIlEWlnOLl5eSlbJ1uokCZCM4Zt2zIKsWgNnU6+KMrJkxGE\nNHy4cpGaWRdzWqNGDSxeAgPVHok8pKQg77dvX7VHYp74eBQAUYLgYLjmdu+Wr9AOY9oLzuNYtgyu\nDy11k8pKWhostc2bZ2zL1aQF4uCAnFPDCbh1a+WFtFCTUunS6OizfLk8Y7IWOVffdnYIkLl6FYFJ\nSqB1IU2E52HOHPVTxeQgMJDovfeI6tVTeyTmKVFCmajdqChokFu2yDvh6/XaFNKJiZj7Ro0Sfqyc\nLVSzcuEC3kfD5jy5QloEiYmZ/bstWqCiklLtzISkYBny7bco4qBG2zU+yCksChaEBvHHHxmFNeTE\nFoR0x44Y58GDao9EWtLSEN0/ebLaIzEPY6j0Vb68vNdJSEDk/I8/yp+FolUhvXo1cvEtNVnJSnIy\nhKRS9b0PHIASaMi9e7lCWjDVq6P1HUeRIqh7q5RfWoy5mwg5p/XrYzWtNeQ0d3OUL4+qWytXIr1H\nTrIGGGoRnQ6+6UmTtB2RK5QNG5BXLXXkstTExGDBLaf5NSUFldU8PcVpkULRopBOTUV2y4QJwo99\n+hTWDqX87AcPIhuHQ69HgR+lMwAsobFbnJ0aNTILaSJl/dJihTQRtOkFC7Rn4lTqJWjcGMFEXbui\nTZ1c2IImTYTI5ypViLZvV3sk0vD6NdGMGfC9ajWAiSMqSt6AoLQ0+GCfPEHVNSV+Dy0Gjm3bhme8\nUSPhxz59ihraShATgxoBLVpkbIuORh67o6MyY+CL5oV09er4MQ3x8lKucYGjo/jVatu2OFZrJs7q\n1ZWLvu7ShWj+fJiVLPVgFkvlytptgWeIToeF27ffmm5xaUvMmQOTrmHgjVaR04yZno5SsImJWIAZ\nlmWVk+RkZVq08kWvJ5o7V5wWTYQFTsmS0o7JFEFBMMkb3istmrqJbERIZ9WkW7WC4FaiA4yTk3hT\nqk6HUoxTpmhLm37+XFkh0b8/Xtxu3eTReN3dUYpSS7+xKZo3R7vGBQvUHol1XL+O4KBp09QeCT+e\nPJGnqIZej1LAjx/DvaNkbvvTp+L7UcvBjh0IIPzwQ3HHK6lJHziQ2dRNBCGtdCEmPmheSJcvj/xG\nw/J3hQqh25ASQUnlylnn7+zRAyteNTtFZcXJSfkWd6NGEX3+OawgUnczK1kS1bMeP5b2vHIxbx4E\nnJL5/lKi1yPFbto0vB+2wKlTKM4iJYyh5nNEBOYipc2kWmoEkZ6OWvBDh4o3wSulSTMG62bWoLGQ\nEOU0eSFoXkjb2WF1llWb7tVLGd+etf5OOzui2bMR7amFhvNE6ghpIqLRozG5t26NAiRSUru2Ov3G\nxVCiBGo5DxmifFc3KVi3DouiL75QeyT8CQ0l8vCQ7nyMIUjs8mWiffvUKeISEyNdoxxr2bQJz3VW\nwScEpTTp8HAUnXJ3z7z90iUE+2oNzQtpIuPBY507wy8td4H5EiUg0Kzx4XbogPNs3CjduKxBLSFN\nRDRuHJpOeHmhwYRUtGlDdOuWdOeTm08+wZjHj1d7JMIID0cnrVWrtBdZbIqXL2G9qV5dmvPp9QiY\nO3kSnd/U8Aszph1NOjkZLj1rAwiV0qRPnEAXOUMYg5DWYq6/TbxmxoLHihbFRL9nj7zXtrNDsrs1\nplSdDtr0lCnayJuuVk3Z+udZGTOG6JdfkKoSFibNOatUQRlCW0Gnw0R/4ABaGNoCCQnQ/ufPh3XL\nVrh8GV2OpOhn/eYNorgPHEDetVo+4YQENIXQQsCkry9STq315z59qoyQXr8ecSGG3L8PN2qJEvJf\nXyg2I6SzatJEWA0FBMh/fSnycJs3h7lNC1XI8uQhunNH3TEMGIBiJ+3bEx06ZP35mjdHYxNbCB7j\nKFoUz++IEdBQtQxjRIMHo5nJwIFqj0YYYWHodGQtsbGY3NPTER2spqk5NlYbWnRiItHMmVhwWsuT\nJ/KbuyMikAudtUvh5cva1KKJbERI162Lji9Z6doV2tPLl/Je39rgMY6ZM5G2Ivd4LVG+vDaKf/j4\noKRk//7Ir7SGihWhncqV5iUXHh7Iq+3VS/nuYUJYuBD9sOfO1V5uriWOH7dey4uKwkKwaVMiPz/1\nc2m1YupetAjZNlL4+5XQpLdvx7yT1aqiVX80kY0IaXd3oitXsk9iTk4wme7bJ+/1pSqWUbs2/NO/\n/Wb9uayhfHnpI6zF4ulJdOQISkuOHy++L7ROh7x0W4yY7teP6LPPkFOemKj2aLKzfDlK3O7apQ3z\nqhD0ehQ+sqa/+4ULKHoxahQi87Xgi4+NFVcwREqio4k2b5ZGi9brkRoqt7nZz4+od+/s23OFtJXY\n2SHl6uzZ7J8pEeUtlSZNhLSVvXvVrZClFU2ao2ZNpMiEhaHGtTGrCR+aNtVe4Ri+TJiARVyvXtrq\njb11KyxAhw5lbkRgK1y+jN7cYlPFNm1CXXKxDSPk4vZt9S0a336LKnpVq1p/rthYBODJWQjm9m2Y\n1I1ZVXLN3RLQtKlxId21K9rCJSTId20py05Wrgw/7Lhx0pxPDNyiQ0s1pF1cEIxTqxY0BDHpVO3b\nQ0jbkl+aQ6dDnfMqVbDSF9p5TQ727UOa0YEDRG5uao9GHMHBRN7ewo9LSkLO7/Tp0J67dZN+bNZw\n+7bwBhZScugQrFY//ijN+ZTwR/v7I47J3j7z9rg4omfP8O5pEZsX0s7O8BXJafKWUpMmwsr8zBlp\nAqbEkD8/frMnT9S5vikcHFCJa/ZsTKx//CFsIVGlCvIftR6EZQoHhwxXSM+e6mYCcF3Mdu+Ghm+r\n3L4tPHf31i1Y7l6/RoGLunXlGZs1qCmkk5OJRo4kWrwY75sUPHkif3lZPz+kPmblyhVE/2cV3lrB\nZoR048Z4YYz5LOWO8i5XTpr0DY4CBYiWLEG1IrUm4uRkort31bm2JXr1gvl7yxaYv4VYMQYOROSt\nFvH19SVfX1+aOHHi222BgYEUHBxMvr6+RARzX9++gZQnTzB5ePi+DTLMup+pbVKwdi0KlcyaBWFl\nq8TFISBRSIeuLVtgDh01Cv5WLdXGNuTOHWnMzGKYOxcuqs6dpTvnrVvyCsnr1/E8GFsIaNkfTWRD\nQtrZGWbna9eyf9a9O3IW5Qq6qVwZCfBSmiA7dcLq7ddfpTunEGrUkLczlbW4u6NYRLNmSJ/ZsYPf\ncZ6emFy1RnBwMLVt25aGDRtGkZGRFBwcTGH/JYl7/2ePDQsLo9DQUHJwIPL396aSJYk8PcPowIFQ\no/tl3WYtjMG0O306CgXJUetaSfbsQcEYPoI2Lg7a4dy5sHANH66+z9cUqamItFfDBRERgYjuRYuk\nPe+NG5iT5MLfH4t/Y0F/Fy9qu0mMzQhpIpi8z5zJvt3FBSv+/fvluW7+/EjxkVrzXLQIJiM1cpZr\n1MheIEZrODigAMzOnUTff4/a35Yag3h6YgKTspqZFERGRlLQfyq+m5sbRUZGkp+fHzn9Vw3Dzc2N\ngoKCyN/fn5ycnMjenuinn9zI1TWIevXyp+ho4/sZbrOGN29QqOTsWVgx1PR3SkVAANJtLLFvHxbM\njGExrmWtigjpYOXLK9dti4MxVAscP176nss3bkhXES4rjOG57tXL+GfBwdq2GNmUkG7WzLhfmoio\nb1/kQ8pFzZrSC7UKFYh++AFmb6WDnWrW1LYmbUizZoj81umQU20u/sDBAU1NAgOVGx8fhg0bRsOG\nDSMiotDQUGrYsCHFxcWRi0Gya0xMDMXFxZHzf1UydDqi6tVjqGXLOPr5Z2fasIErB5l5P+7YrEyd\nOvXt31EzDdjv30cKWGIi0YYNsFjZOq9eIbWvSxfT+zx/Du3566/xvZct065525Dbt9Uxde/ciWdl\n7Fjpz33zpnya9KlTWNg0bZr9s8hIxL1kreOtJWxKSJsKHiMi+vhj1MZ+9Eiea8sl1EaPRmShn5/0\n5zaHLWjShhQuTLR6NRY0Y8ZAED94YHzfAQNg8tZilHdoaCg1aNCAPP6r/sB4DLJSJaI1a5AGNGsW\n/xQtQyHt5eVldJ8dOxBNzxXpUKNRhBzs3YvnwFjZzvR0ohUr8E6XKoXAISF+a7VRI2gsIQHv3bJl\n0mvwr14hBatiRWnPy7FypWn3xbFjyKHXqmuDiEjCcCj5qVULRThevCAqVizzZ0WLInXF15fo55+l\nv3bNmvJEkDs4YPL9+muUHFSqilClSngxXr4kKlJEmWtKQfv2iN6eOxfBhGPH4rczjDJt0QKRuWfP\nQgtXCmMBXM7OzuRjYHMNDg6m2bNnExGRk5MTxcbGEhFl0qqNbStaNJaCg4l69Iijv/92oQYNMvZ7\n8eJFJo2cD7GxMFveuQPfrZbNfWLw9cWCLitnziAorGBBBBhqMXLbEvfuKa9JT5uGhYw1RWFMcfMm\nFh1yFImJicHzvXCh8c85Ia1pmI3h5cXYgQPGP7tyhbGyZRlLSZH+uhcvMla3rvTn5fj2W8Z69WJM\nr5fvGlnp35+x06eVu57UREYy1rMnY+XLM7Z2LWNpaRmfzZvH2GefqTc2Y6xcufLt/wcFBbHQ0FC2\natUqxhhjc+fOZWFhYby2/fFHGCtRIpR5ea1i8fEZ+xli6tXW6xnz82OsdGnGRo1i7OVLOb6puty9\ny1iJEoy9eZOx7eJFxjp3Zqx1a8Y2bVL2PZOaJk0YO35cueudO8dYp06MPX4sz/k3bGCsb195zr1g\nAWOffmr688qVGbt+XZ5rS4VNmbuJEPZ//rzxz+rUQa7srl3SX7d6dWgdcvWEnjkT5nQl21kWKmSb\nZTQ5XF1RbW77dpiD+/VDsJBeTzRoEJ4DtVpyZiUoKIgmTpxI7u7u5OzsTDqd7q3JOzg4mJycnKh+\n/fq8tn39dX26fduD8uYlcnMLpps3nahuXcvRTvfuoSjHtGkwcy9ebBs+WKGsW4dnIV8+mLJ79IBv\nun17FGbp10/b5k1zJCfDkiRFwxA+vHyJrl9DhshXbESuoDHGYOoeMcL45w8eIA5DroA1yVB7lSCU\n48cZ8/Aw/bmfH1bLctCsGWO3bslzbsYYu3SJseLFoSEqwfr1jPXpo8y15EavZ2zvXsYaNWKsdm08\nB716MbZkidojk5czZxhr2JCxFi0yW0UMX+3nzxn77jvGnJ0Zmz8/s4aZ03jzBu/pX3/h/pcuDW3q\n9Wu1RyYNZ86Yn/+kpn9/xoYPl/caQ4fi3ZWao0cZq1HDtNVkwwbGfHykv67U2Jwm3bQpIvVM9Xfu\n0QOBFWLKSlqiTBkUVJGLevWIJk5EwIvYRhNCMFUP3RbR6ZB7fu4ccn03boT/ceZMovh4tUcnH02b\n4juPGoWYjE6dkPdJhGCfOXOgKSQm4p347jtomDkRxtDs4d9/UdKzYUOkTY4dq37XKqk4c8Z4lLIc\nbNyIZ8mUP1cKGENVOznqZpsLGCOyEX802Vh0NxF6IXt7o3iJqc+HD0cUotQ0aSK/eXjsWERPKlHk\npFo1mIO1Vh7UGnQ6dBr76y80h7C3R6rbmDGoapQTsbMj6tMH7piOHTPSjrjc/lOnEJxYpoy645SL\npCQIlMaNEVDYsSN+i++/zznR6hxKBUPevYsGGlu3Slf60xhRUZjvypeX9rzPnxP9/bf53uf37+cK\nadno0MF84ZLhw/FwSa1BKSGk7eyI/vwThU7k1Nq5aynxndRAp0N5x19+QQSvoyM0zcaNUZM6Jy1M\nOG7cwDPDlZotWhT55efOabMFpjVwBSq++AIT/MmTqDxYpQoWJLaUsSCEs2fl16RTUuCH/ukn+TtD\nyfV9/vwTzZcMSglk4s4dVK+0hbr0NiukDx0ybRIuW5boww9RoEBKGjZE0IbcrQQrVEBQT79+lits\nWUtOMnkbo39/mD+7doUA++UX9Ad+7z08Rxs3yv8by8nTp0S//07k4YHCHNWqZVTGi4iA+dffH8/U\niBEQ2FrMH+cDY2gpOGUKtOVBg2AtuHQJps2oKKJJk2w3KMwS0dFwYchdeOPnn4lKlkRqo9zIIaTT\n02E9+uIL0/vs3QvXkBZ6g1tEbae4WGrXZuzsWdOfHzvGWPXq0qda1KuHlAQlmDSJsX795E0XOXKE\nsQ4d5Du/Fli+nLGOHTNvS0xkbOtWpOW4uTHWrRtjK1Ywdu+eOmMUwr17jP3xB2Pe3ox5ejI2cCBj\nwcGMpadn7JP11X74kLFZsxhr356xChUYGzuWsZMnGUtNVXjwAklMZOzgQcZ++QX3ydUVQXDnzmV+\nL44dw2fJyeqNVW4CArI/x1ITFIQ01qdP5b0OR6NGjJ04Ie05AwKQpmZu3mzThrHdu6W9rlzoGLPN\ndfX48UghmjLF+OeMwcz5++/i+smaYsQImEiUWGW+fo3CHIMHozKZHCQlYdX8778wj+ZE3ryBGXTP\nHuOpKzEx6EO9fz/+W6cONNKWLfFXsaK62llcHEreHj2KinqHDiEVsVs3FMApVCj7MTqdzmg1M8Zg\n5tu+HWUzw8OJ2raF5al5c1SiU1O7SEiAif7oUdRUDgmBlcDHB8U06tbNfi8YI2rVimjYMPM+SFvn\n66/xHI8ZI8/5nz3Db71uHZ4ruXnzBubo58+l83szBuvgpEkIIjZGfDwsS48fy+tvlwqbFdKHD6Pu\ntTlT7YoVmHR37pTuumvXYvJQqtMSV3N2+3ZMRHLw4YeoztS9uzzn1wJLlyIPfelS8/vp9TCpHj0K\nP+epUxDaefKg8UK9eqg+5+YmfVASYxDCN29CeIaEILq2XDl89sEH6OrUqJHl1qmmhHRWHj1C5a3g\nYHzf2FgI/1KlIBDr1sWCRepSkOnp6NEeEQFz9cWL+HvwAN/zvfewuG7VyvgixJADBxDkFB6u3Z7A\nUlCtGkq3/pc2LylpaURffon7PmOG9Oc3Blf9jctGkILDhzGXXb9uerHp5wdXqMToBZ0AACAASURB\nVBwVJOXAZoV0cjI0wMhI06U0ExLgi/T1xQpUCq5dwySmZC/mgwehTV+4gAlbaubPx+8oR0S8Vnjz\nBlriunVEJspYG4UxRIFevoy/S5cgSK5dg+XBzQ2TpoMDUYkSeCYLFUKgmqMjOqilpsIqkpSU+U+v\nRwDLw4d4ns+fxzHVqkHjr1sXcRA1a2KRIAS+Qjorjx5h0rx0CYVArlzB+FNSUEq2UiW8SwULQgsq\nWRKTYd68+LOzw2/N/aWlIUjv6VP8N39+aPD37hEVL47vWaEC2mI2aCD8uzKGRcvEiegrn1OJjISl\nIzpaHkvH118j+2HfPuHPmlgWLsQ8amnhLIT27REgOmSI6X0GDICF0pzPWkvYrJAmws3o2dN4CzKO\nn3/GJLhmjTTX1OuJPvoI0YOlS0tzTj7MmgVz7dGj0ue5XrmCBiVKLjzUwM8PqW0hIdZPdHo9BFpk\nJP4bHQ1zYWIihBEniEuXxvPHCW3DvwoVYG4rVw5/rq7Za9KLRayQNkZaGr7D/fsQrs+eQejGxGBB\nEhEBIZ6cjAVzQgKEcf78+I56PYR5yZIZ39XNTRpT465diNYPCrKRICCRLF8Oq+Gff0p/7sWLYXU8\ndcp4QxK56N0bbpsBA6Q5X2golLKICNNzZFoa3smwMDybNoE6rnBpWLuWsR49zO8TG8uYiwvq+UpF\n9+6Mbd4s3fn4kJ6O644eLX0gmV7PWKlS0v5GWkSvRzWq9evVHon82PirzYvERMYqVmTs8GG1RyI/\nXbsytmWL9Ofdtw9V2ZSqcsih1zPWtq20FRx790ZFPXMcP85Y/frSXVMJbHrt2aMHfGnm8qGLFYMp\nZ/p06a7bti2Cd5SEy58ODydasEDac+t0RJ98Im8/bi2g0+G3mz7dttOucgGzZsEEbEttJsWQkgIL\nmtTBXOHh6CMeGAgrjpLcuYO8fqm6eUVEwJoyfLj5/Q4cMN9jXIvYtJB2coJ/cfdu8/uNGQNfy+3b\n0ly3bVs8EEo7CooUgaD+/XeibdukPXfr1kRbtkh7Ti3StCkC5eRoZ5qLcty5AxPt/Plqj0R+jh9H\nUGfx4tKd8/FjCKvff8dCR2n+9z+8h1JlTcyfDx+zuYYxej3Rpk22F7tg00KaCOUQLQmsokUhqKXS\npqtVw8MlldAXQoUKWHCMHo3as1LRoQMCl2JipDunVpk+Hc+MqW5quWgbxlDyc+JEeQIptca2bdL2\nvU5KQvDrZ5+hspgaHDyIIC8p+OcfxNNYSlM9dgyKna31ELd5Id21K9Hp08i1M8fXX+PBuHHD+mvq\ndBnatBrUrYsX95NPEGUsBQULwpxmySqRE3Bxgdl76FBELudiW2zciMWkXLUDtERKClJIP/lEmvNx\nbVyrVDFdY0JuUlJgHWjbVprz/fQTIvxLljS/3/r1WJjYGjYvpAsWhBYYGGh+vyJFkEv5yy/SXFcN\nv7QhbdpA0HTsiMhiKfDxsfw75hT69IFVYt48tUeSixAePiQaNw4R3VLnbmuRQ4eQOihVJDKX7bJ2\nrXoFek6fRh68qdRZIYSFwc88caL5/RISoICoZTmwBpsX0kT8TN5ESJw/fFga7dPbG/meampi/fqh\nAEHHjmjObi2dOhGdOIEKVzkdnQ5pLSdPIv85F+3DGKwfo0ahsMy7gJ8fUpWkwNcXc9bOnUiPUwvO\nH20tjGHBNmWK5YYqgYFEnp4o1mJr5Agh3aEDii9Y0igLFUI50WnTrL9mqVJoGXfqlPXnsoYJE/Dw\njRmDghnWUKQIVpr+/tKMTetUrEjUty++s7W/XS7ys2kT8rMnTVJ7JMrw5g1qI0gR6LRqFSqJbdhg\n2SwsN1L5o//+G3P+0KGW9/3zT9s0dRPlECGdPz8CIbZvt7zvl19CW7xyxfrrtmwpbclRMeh0iNBM\nTcVvkJRk3fm6dcOK+12hf3+U+hw3Tu2R5GKOa9fgrtq8WbmKWGqzfz+q2VnbB3zlSqKZM2FFlKry\noliioxG8ZW3nq7Q0KFxz51p+Hu7dw3zfubN111SLHCGkiaAR8TF5FyyIyNCpU62/Zo8eENJq12yz\nt0dQRMmS1gvqDz9ERanQUMmGp2k4s/f+/ahelYv2SExEVcH584mqV1d7NMqxc6f1pu6VK5FPrgUB\nTYTvVK6c9QutNWtQOYyP4N24Eb+j1JUalSLHCOk2bWCuvXnT8r5ffIEIw9OnrbtmzZoo7yhlgXix\n2NvDpFO8OBYPb96IP8/QoTCPvSsULUq0dSvRjz+iKEIu2mLkSHQ2GjRI7ZEox/PnMOeaK3lsiRUr\ntCWgieAb9vGx7hyvXkHJmj/fcvAbY7Zt6ibKQUI6Tx4U6F+50vK+jo4INhs5Et14xKLTQSDu2CH+\nHFLi4ACfk5OTdYJ68GAErCQkSDs+LdO0Kbrn9OgBzS0XbbBhA0yVS5aoPRJl+fNPBHI6O4s7fsUK\notmztSWgnz6Fhc7aoLFff0W6qLG2s1k5dQpZAA0bWndNNckxQpoIJeE2buRn7u3XDxrU8uXWXfPj\nj9X3Sxvi4IAAm8KFsWJNThZ+jnLlUIFM6qpmWuerr/Dif/65+i6MXFDyd/x4PIdStwXVMno9lA2x\nXZqWL4eAPnJEOwKaCO6kDh2gJInl33/x/WbO5Lf/n3/CAqNmP3hryVFCunJlmMX4RCfrdGiRNm0a\nuhaJpWFDmF+kKJIiFQ4OCLCpWBHmMjHpWcOHv1smb6IM/3RMDNGiRWqP5t3m+nXEmfj7o8Lfu8SR\nIxBkYoKrli8nmjMH53Bzk35s1hAYaH2k+uTJWLzwyRt//RrX7N/fumuqTY4S0kS4gXy141q14KsY\nP1789ezsYCL9+2/x55CDPHlgIuS04kePhB3fvj3q+4aFyTM+reLoiCC8BQvePUuCVnjyBKbe337D\ns/uusWIF5jGh2t+yZdoV0DExaLX50Ufiz8EVLpkwgd/+u3YRNW5s+6Vjc5yQ5ipw8RUuU6bgobam\nA1SfPkhb0pqJ1N4eL66PD4ro37ol7NihQ9+tdCyOcuWI9u5F2ckTJ9QezbvF69co9TtokHR9hm2J\nx49RbrhfP/7H6PUIety/X5sCmojor79QpVGs20JI4RIOztRt6+Q4IW1vD1MtnwAyIhQ4WbAA/kix\n1cO4LjJqFzYxhk4HE9FPP0ErOXuW/7FDhkCbfBcDqerWhctg5Eiiq1fVHs27gV4PwVytmnp1pdVm\nwwY8c3wF0evXqOt97BjSkrQooIkQX2DNoosrXDJsGL/9HzwgunAB3cNsnRwnpIkQ+OPvz98X27Mn\nUdmyqAcsBp0OWueaNeKOV4IhQzC+Ll1QxYgP5csj7zogQN6xaZV27VDdqkMHdNnJRT4YQ1rNq1dE\nq1fbdqCPWBITkVbEV5g9fEjUqhVRgQIQgmpXEjPF/fvQ8sVWGUtNhUty3jzE2/BhxQoUrrImSE0z\nsBxKz56MLVvGf/9btxhzcWHs33/FXe/JE8aKFmUsPl7c8Upx7hxjpUsz5uvLb//jxxlzdWUsJUXe\ncWmZlSsZq1SJsXv31B4Jf2zp1dbrGRs9mrGGDRmLjVV7NOqxcCFjPj789g0JYax8ecZmzcLvp2Wm\nTWPsq6/EHz9nDmODB/P/nvHxmMsjI8VfU0vYzpsskKAgxurWFfYA//gjY717i79mjx6Y0LXO7duM\ntW3L2PffM5aaanl/b2/+Qj2n8vvvjLm7MxYdrfZI+GErQlqvZ2zkSMaaNGHsxQu1R6MeyckQuhcu\nWN43IICx4sUZCwyUf1zWkp7OWOXKjF28KO74GzcgcKOi+B8zdy5jn34q7npaxDbeZBGkpzP2wQeM\nBQfzPyYxEQ9UUJC4a+7bx1ijRuKOVZrnzxn78EP8Rk+emN/35En8LsnJyoxNq8ybx5iXF2MPHqg9\nEsvYgpBOT2fsyy8Za9qUsbg4tUejLuvWYeFsDr2esZkzIczFCj2lCQpirF49cdp+WhpjzZoxtmQJ\n/2OSkhgrU4axy5eFX0+r5EifNBFSowYOREoCXwoUQH7syJHiioC0b4/ghvBw4ccqjYsLgjFatECu\n97lzpvdt0YKoalVES77LjBsHn76nJ9Ht22qPxrbR6+EzvHwZXZGKFlV7ROqh16OKlrmeyMnJiFTe\nsQPvKp9qW1pg7VrEw4iJMViyBD7oL7/kf8yGDWhKUreu8OtpFrVXCXLCmZBCQvgfo9cz1qkTfD1i\nmDyZsW++EXesWuzezViJEowtX256xXvqFPyy77o2zRhjq1djtX7pktojMY2WX+20NMZGjGCsZUvG\nXr5UezTqs2MHLHCm3r3Hjxlr3hxxNomJyo7NGmJjEafz/LnwY+/ehZn79m3+x6SlMValCuJochI5\nVpMmQs3W774Tpk3rdESLF2MFKKad5eDBSN0Ro4mrRdeuSB9buhTjN1ZWtXlzovfeI1q3TvnxaY3P\nP4fF5cMPrW/S8q7x+jWyKVJSEPFbuLDaI1KX9HS0mp02zbi2eeQIUic7dUI9/QIFlB+jWLZuhXXR\nxUXYcXo9Uq0mTYIFjy+BgeiM5ekp7HqaR+1Vgty8eoUgi1u3hB3355+M1aghbuXq7c3Ytm3Cj1Ob\nhAQEXNSvj5VsVk6fZqxixVxtmiMoiLFy5RjbuFHtkWRHi6/2kyeMNW7M2IABuc8Qx59/QkvOqkWn\npsIqV6YMYwcOqDM2a2nYkLGDB4Uft2IFnpO0NP7H6PWMeXgwtmeP8OtpHe29yTIwZQpjQ4cKO0av\nh8D64gvh19uyxXIQiFbR6xlbvBiR8atXZ5882reHWTwXcPUqUtQmTEAglFbQmpC+cYMxNzfGfv5Z\n+ylDSpGUxFiFCgjMNOTePQjudu0Ye/RInbFZy+nTiNgXImgZY+z+fShVV68KO+7gQcZq19bWOygV\n2nqTZeLZM8aKFROeAx0Xhwl4xw5hxyUl2X6e3pUrWJl27MjYw4cZ28+excTy5o16Y9Maz54x1ro1\nY126aMfHqiUhvWcP/M9r1qg9Em0xdy7SNg0JCEB8yK+/2rbA8fFh7I8/hB2j12O+mTFD+PU++ECb\nFi0p0M6bLDPffMPYuHHCjztzhrGSJRn75x9hx/34I2OzZwu/npZISYEVokQJvACcBvTRR8IKxbwL\nJCczNmwYVvNaWJxpQUinp+P5KVcOgYe5ZBATA43x5k38++lTxvr2xULv7Fl1x2YtERH4bq9eCTtu\n8WIsWoQWTjp7FkGtObXgkvpvskLcv4/V/LNnwo+dMQOakhDTTUQEY87OOaNAw8WLED7duyPSNCSE\nMU9P7WiNWkGvh/bQoQNjf/2l7ljUFtKxsdCKPD1t12QrJ999B1eaXs/Y1q2MlSrF2Lff2lb0tim+\n/pqxiROFHRMaCsF+547w63XvDgGfU9ExprXeTfLx1VeIjpw/X9hx6eno4NKuHdEPP/A/btAgRCdO\nnizselokORkRqGvXIvp93z6iUqWQ35lLZk6cQI7+Rx/hWVMjIlen05Far/bx40QLF6K/+9y5aJua\nSwb37hE1aIB621OmoC782rVETZqoPTLriY0lqlKF6No19EPgw6tX+D2mTUMPcSHcuEHk5UUUFWVb\nke9CeKeE9OPH6CEdGkpUqZKwY//9Fw/S7t38m7HfuoV0gMhIdNvKCZw7h8VH/fpE//sf0ZkzSM3K\nJTNxcVgUXrpEtGULfi8lUUNIJyWhZeK2behC16WLope3GUaNIvrnH7w7X3yB3yxfPrVHJQ2zZxPd\nvCms8NHAgUiXXb1a+PUGDyZyd8dvmGNRU41Xg59+YmzgQHHH7tiBQDIhJQx790Y5yZzE69coel+g\nAGPVqiFQLhfjbNwIM95vvykbCKT0q33+PGPVqzP2ySfiile8K6xezVjBgsie0HIxHDEkJwsv8rN+\nPVJdExKEXy8qirGqVXN+U5Z3TkjHxyMQTGxt1xEjGOvXj//+ly+j69Tr1+Kup2WuXWOsUCG8mLt2\n5abWmCIyEjWIO3ZUrpOWUkI6ORn5vCVL2mZtAKWIjUWdcnt7xoYP59fYxtZYv15Y6umNG1jAhoeL\nu17//lC6cjrvnJBmjLFFizBhiiExkbGaNRnbsIH/Md265dzAhoMHIYBq1MALKjS/8V0hNRWNAlxc\nEPUvdzEPJYT0lSsofNOpk+10B1OatDQU5yhZEimNXbqoPSJ5SE9nrE4dxvbv57f/69ewJojtGnjx\nIpSfdyF49Z0U0m/ewGx95Ii44y9fFhaJeP48cotzapWlfv2Q4vbHH0jXGjUKKSa5ZCciAkLtvffE\nd1vjg5xC+tUrxiZNQr3pNWtyLSjG0OsZ27uXsa5dEeG+fTvmDMOaAzmJXbsQZc33WZg8mbE+fcQ9\nO3o9Y23avDtpoO+kkGaMsc2bUXpO7ASzZAnyGvmmTLRvn3N7Mj97hlXt2bPwR44cidaW8+YJz5V8\nV9i9G7/RJ58IL7LDBzmEdHo6yliWK4eFmRzjzgkcPYqKYbVqQXilpuLfS5eqPTJ5SE+HVrxrF7/9\nly2D5S0+Xtz19u3DIjen5kVn5Z0V0unpMNUFBIg7Xq9HDeKPP+YXEHTiBMoi5kRfFGPwR9asmVGJ\n7Pp1CKCSJWHefRfMUnyZO3cuYwwLvJ49A1iRIkGsZ89Vb3+7gIAAFhQUxFatWvX2GL7bOKQW0mfO\nYFHbqBFKPuaSnQsX0KPdzQ0Bg1xdhRUr0DPbliuImWPbNvNdvAw5cgQ54WLyoRnD/FmrFha57wo5\nuguWOezskOP7ww9EqanCj9fpiHx9iZ4+5Rf+37IlUYUKSE/JiXzyCXLCZ83Cv2vUQNeeI0fQX9vN\njWj6dKQmvcsEBQXRoUOHiIjo5s1Q6tOHKCTEmx49InJzC6OpU0MpPZ3I29ubiIjCwsIoNDSUiCxv\nk4O7d4nGjiXy8UGf9bNniZo1k+VSNgljREePomdyt25EPXogd7d/fyJ7e6KHD/Hcr1qFOSenkZaG\nXO8ZMyz3jI6MJOrTB10C3d3FXW/9enTVepfS+3LgY8Ofdu0gONeuFXd8vnxEO3cSbd/Or4Xj5MlE\nM2eiOEpOQ6cjWraMaPlyosuXM7bXrImX8tQpoogIvJw//4yiB+8iOoOZzM/Pj4oVK0ZVqxJNm+ZG\nXbsG0dq1/jR2rBNt3EhUubIbBQUFkb+/Pzk5ORERkZub6W1ScvMm0YABqAlQvjz+PXBgzhQ0YkhJ\nIdqwAbUTRowgatQIC5ovvkDOLxFaLg4ZgsVNnTrqjlcuNm8mKlkSc6k5Xr3CImbyZKL/1pWCSUzE\ngmD+fMsLgpzEO/3K6XToNe3rS/TypbhzFC9OtHcv0cSJRMeOmd/X25uoaFGiHTvEXUvrlC1LtGQJ\nJvOsPamrVcMq+Px5okePiDp2RFGH8HBVhqoKYWFhbzVfIqL4+HhydnZ++++iRWOoY8c4mjLFmVau\nxAR/8mQMvXgRl2m/mJgYiovLvk0Krl6FttOqFVH16lhYffddbt9njufPoTVWrgwhPX06NOcvvyRy\ndMy87+LFmFfGj1dlqLKTkkI0dSoUD3NCU6/Hgq9ZMyxYxLJ0KZ7LRo3En8MWeaeFNBFRw4ZEdevi\nYRNL9eqoKtW7N9GdO6b30+mwkly9Omdq00REvXrh9/j+e+Ofu7lhUbR9O8xWHTrAFbBxY3bBntOI\nNWI+YEaqgjVqhNKiX39NFBKCEqxbtsjrKggNhUm7bVui99+HcP7xRywqcyG6fp1o+HC4dKKiiA4e\nJAoKIurUybh1ITwcwnzTJiIHB+XHqwRr16LaoKen+f04y9mSJeI14MhIlJidM0fc8bZMDn18hPHr\nr0S1a2O15+Eh7hze3lhVd+6Mcn8GSk4mOnWCuWbNGrz0OQ2dDiUh69cnat8ev4cxKlRArd7Jk2GJ\nWLmS6NtvcQ9GjLDNUqO+vr7Ztjk7O5OPj082LZqIyMnJ6a3gjouLIxcXFyKCMNfpiKpUiaNvvnGh\nK1eIQkJiydWVqGHDOKpd24WcnDKE/osXL94ea8hUg5Wnl5cXeXl5Zfo8KYkoIAC/fZEiMFlu2EBU\nsKA1v0LOQa8nOnQIdcgvXYK2fOsWzLvmePOG6NNPiebNQx3rnEjS/9s78/CYzvaP33kpiVoiQS1B\n7WpJJLbaKhJB0FIpaitKLG3VVl1eXVT1VaVV/CwR1L5rKIIstlBEJCGpxJIEQUJkj2yTmef3x7dj\nkjRIMufMen+u61wnBs9zZjLnfJ/nXnOwCPHxefG/27MHJvHgYI0boKwIAavbZ5+VvZyzSaDvyDVD\nYcMGRK+WtUl5cebOFcLZ+cU50eHhiHo25fKJQUGI4ixLkYvbt4X44gt8Ns7OiBo1ldzy/fv3i/37\n9wsvLy/RsWNHERoaKkJDQ59FZv/8888iLCzsha8lJAjRv//Pok6dMGFvHyomTUJEuPrfFeZFt3Z0\ntBCzZyNvd8AAIXx8TDfroKyoVEglnDVLiPr1hRg9WohNm8pW+nbmTCGGDzft/PHVq5Hd8iLOnUPR\nKG3Ln+7fj8wRU3kWlBWzN3ermTgR3XpK2AyViSVLsCuZPh0rwJJwcEA0tCl0x3oePXsiiGb8+NKb\n9ps1gzkrPh7/V92kYepUomPH0InLWPHw8CAPDw+ysLCg9PR0srCwIMd/zDaBgYFkbW1NHTp0eOFr\nf/8dSB4e1vTgQQf6/ntH+usvorp1A+nUKWuqWvXFHTxycpBZ0KcPUe/eRJaW2N0cO0Y0dKjpmmRL\ngxDYKX/5Jdwx48cTWVujS9WOHXg2WFqWbiw/P6KDB4nWrTPd4KaEBJiwX/T8ioggGjaMaOZMPO/K\nS2Ym0axZCEgt707c2DGrLlgvIyKCyMUFwTOvvVb+cbKy4KcZNer5vtnUVKQpHT2KCFFTpKAAD7gW\nLXBTl4eYGJjUDh5E+7sBAyAq7u5YDJk7N24gs2DLFkRhv/8+FoCNGllQWpqgo0cRqOjvj5iJvn3x\n+ZnrA68wUVFYuOzZgyCokSPx+dnbl09gY2KIunfH97V7d+mv11AYOxbuqsWLS/77uDg8/5Ytw+ep\nDXPm4FlZmuwZk0XfW3lD4/PPy9ZA43nExwthZ/fiKjwbNwrRtavpFjkQQoiEBFSoOnJEmrG8vIRw\ndxeiWjWcvbyESEzUfmxjR6EQwt8f5tlXX4W5u1IlId56C9+zpCR9X6H+UShQlGXpUlTIql8fZv9L\nl7Q3TWdloXa1qdboV3PmDEocP69rVWKiEM2boyKjtqjdgo8faz+WMcM76WI8fYqe0xs3lj+fT01E\nBHYuv/+OlKPiqFRYcU+dih2nqXLhAnZv58+Xv4hBcTIyYKr18SE6fhxm8caNsYLv3t18UoYKCpCX\nfukSIsAjI4n69SPat8+CpkwR5OcHd8PAgTh69zafiG0hiG7ehBUhIAApko0bwxozcCBcMlLkfQuB\nHWOVKoh4NlUzd0EBrH7z58NaU5z0dLhT3nlHu2wZIjwbe/RAGqKnp3ZjGTss0iVw5AjMLNeuld4X\n9TwuXsSXdudOCHZxQkIQAR0VRVSzpnZzGTJr16LYycWL0kcP5+UR/fUXfIhBQURXriANrFcvzVG7\ntrRz6ou8PKLLl/E+z57F+7azg/+vWzcsLCtXRtEUIQQJgUIkR49CzH18IFTduqFQSbducLuYQpGS\n3Fy8x+BgfEZnz+Jh7+aGe8/FRTs31vNYsoTowAHMp+3zwpBZtQpup4CAfy9EcnOx+GnXDv9O24XK\n778jPujcOdP4bmoDi/Rz8PDAA+yzz7Qf6+xZjHfgAJLxizNtGoLWVq3Sfi5DRQiiSZOIsrOxYJHz\nxissZEFBWBjUrYtdfLt2mqNVKwiaoZKZiZ1xRAQWjKmpRIcOYQHy1ltYfPTsWfICRC3SxVEoMN6F\nC/hcLlxAgY4uXTTC7eSENCND3REKgeClW7eI7tzB7/rSJeQyt2qF99KlCz6f5s3lfR8nTsAKFhyM\nxZKp8vgxLIxnzqCKYGEKClAfoXJlae7t27fxPQwKwgLS3GGRfg4PHyJn+sgRaSrcBAQgd/LPP/EF\nLExyMr6M/v7aRUIaOrm5yA1v2lR7c1hZUCoR1BMZWfSIiyNq0kQj2k2aQMzr1cPZ1lbeB7wQMBHe\nu0d0967mHBtLFBYGIWrTBoFM9vb4bnTsWLqAueeJdEkkJUGw1aKdnAzxa9685KNuXfkFXKXSCPHt\n20XPMTFEVasiILF9e1Sz69IF92uVKvJeV2HUgWL797+8oIexM2kSIt5/+aXo6yoVzN+hoUSHD2sf\nkFhQgIXnqFGIDGdYpF/Inj2oFRsaKs3N7+tLNGECfKhOTkX/zssL1YnOnjXcHYwUPHoEX9O8efDF\n65O8PPgsIyPxwI2OJkpMhDgkJCBK/7XXNKJdrx5EKi8PKUsvOiwtUWUpIwNCnJGh+TkrC4vAe/cg\n1I0bEzVqpDm3bg1xbt4cTRrKQ1lEuiRSUiCKhY+YGJzbt4dY2tqiLK6treZo0ACLokqVYB1Sny0t\nEe+Rl4cjNxfnV17BwuTJEywOkpPxc1ISymyqFwYtWhRdKOg7sj8jAy6Gd9/VrtSlMXDpEt5rVFTR\nz12phL84JgbulKpVtZ9rwQIsFI8dYzO3GhbplzB6NB4+UpmifXyQQ+3vX7TovlJJ1LUrcgLHjpVm\nLkMlJgY7jzVrEFBmqOTlQbTVwp2YCHPxo0dY8b/oqFcPZTxr1MCDrXp1zc81asCc3KgRfpZjUaat\nSL+IrCwIaWFhVYtrxYro/KRQIK1Jfa5fH5+bpSXMopUr4+d69TBmccGvX99wg/9ycpAC2LGj6Td7\neN5zSalEUNe9e7A2ShFncuECFj2hofj9M4BF+iWkpsLUuGnTyzu9lJbduxGYdvIkdk1qLl6E77r4\nitUUuXIFD7o//oB5i5EWOUXanFEocI9WrQrLl6nv9tauhXhu2aJZjBQUwCKYkAATtxRWxsxMlBJe\ntgxCzWhgkS4F/v5YNV67Jl0E9tataLSxalVRP/SkSdhd/fqrNPMYMv7+EV9vfgAAIABJREFUWJ2f\nPImgFEY6WKSlR6WCOCUnI8r5lVf0fUXyog7gOn9eU0u/oAD19dWfgVQxAB9+iAXPhg3SjGdKmPg6\nUBrc3GCWldL39MEH6HLUrx+iGNX89BOqSAUHSzeXoeLmhsWIuztKgTKMoSIE0ezZCDbct8/0BVqp\nxILk6681Aq1QwP2XloYsA6kEWp2+9ttv0oxnarBIl5IlS+Ar2b1bujGHD0dtYA8PmI2IkE4zfjzR\nmDEwAZk6Y8YginPAALgWGMYQ+eEHCIlU5l1DZ8UKBC1++in+nJsLF112NuJqivfOLi8PHhB99BGe\ng1IEnpkibO4uA5cvo7LV5cuoXSvluO+8g1q4EybgtUmTYF4zl5q1c+fCeuDnJ90DwJxhc7d0rFqF\nIyhInmIohkZ0NAI7L11CumRqKtGQIQjm2rJFutoCKhXa2fbqVf7a/uYA76TLQOfORF99hRKACoW0\n4546hfSDZcvw2ooVqCYl5c7dkFm6FNHOH38s7WfLMNqwfTu+m35+5iHQBQWw5C1cCIG+fx8i2rEj\nCpVIWfxn5Uqk5f33v9KNaYrwTrqMqFTY9bZogWbwUnL/PnzUb78N33RoKPy1wcFEr78u7VyGSH4+\nLAlZWUR795p2iUW54Z209hw+jDzgkyf/XWXLVFm8GO/Xzw9ZJu7uiJ2ZO1faVDN1x0H1bp15PizS\n5SA1FSvLn34qudC8NiQnEw0ahGhnLy8EUxw8SHT6tHn0/FUoEPGdkoL3LXWdb3OBRVo7/PwQNLV6\ntTQVB42BiAgEcwYHI5Bz2DBUGJO6bkNuLj7TuXM17j3m+bBIl5PQUPhTgoKK5jpLQVYWgsmsrGBu\ne/ddVOnSZSlNfaJUEk2ejBSQo0dNP2dcDliky4+PD6rh+fjgvjMHsrIgnN99BwvWlCl49vTrJ/1c\n8+fDarh5s2kXgpEK9kmXEycn7KQ9PPAFl5KqVWFqa9UKrQUXL8au+tw5aecxVCpUQKtQe3t0dUpJ\n0fcVMebC9u2INj5+3HwEWgi85zffRIESLy+UMJZDoFevxuJn5UoW6NLCO2kt+fBDmG927JD+SycE\n8oiXLUMqhJcXUXg4Ct2bA0IQff45Og35+5tH4I5U8E667KxbR/Tjj/i+mYsPmggZJEuXYuMRGYkc\n6MaNpZ/H3x+FUM6fJ2rWTPrxTRUWaS3JyUGLv8mTiT75RJ45AgLgF2rZErWOd+82n1WoEIg03bUL\nn4MptwOUEhbpsvHzzxDpgADzCmT6+2+0PbWzw/Nl82Z54kCiozHP/v0lt+tlng+bu7XEygoVcxYu\nRO1tOejbF/Vz09MR0LJ+vTzzGCIWFvCTTZqESNMbN/R9RYwpoVSiktiVK4gvMSeBfvoUmSRCEL33\nHjIq5BDo5GSiwYNREIoFuuzwTloijhzBl3D7dnlMRUSo9vPeezAb+fkR9ekjzzyGyo4dqHq0YwcW\nLszz4Z30y8nORsW7tDQ0epGqLr+x0Ls3UqB27kQktxzk58O33aULrBVM2eGdtEQMHowv+sCB8pW3\nrFIF0c5DhmCeY8fkmcdQGTMGq/1x44j+7/+wA2CY8pCYSOTsjHaYJ06Yl0Dn5+N5FRpKdOaMfAKt\nDkirUQPBr0z5YJGWkNmzkWc4bBh6EcuBhQUK/PfqhdSs5cvNS6x690YlNi8vomnT8MBhmLJw/Tri\nSAYNQpnLSpX0fUW6Iy4OLSH9/YkCA9ErWi6WLycKCYHlq0IF+eYxddjcLTFKJQqcWFoSbdsmX7/Z\n3Fw8aBITYfr18jKPwv9qMjOxs87IQDBKrVr6viLDgs3dJXPoEALERo1CJzpzwscHAa4KBRb6/fvL\nN9eRI8g1v3AB5X6Z8sM7aYmpUAF+6Tt3kLQvF5aWMHdXrEj0+DF2mFeuyDefoVGtGh463brB3xUZ\nqe8rYgwZhQIVrmbORCCiOQl0Xh5SOGfNgln/hx/kFehr15CaeuAAC7QUsEjLgJUVVux//IFVu1zU\nrYt5QkNhYnd3R5S5uTSoqFABvq6FC1EHWN3uk2EKEx+PReyNG1jIvvmmvq9Id8TEoChLfDxRu3YI\nNlW3n5SDR4/Q22DFCvP6nOWERVomatVC1Z6FC+UVDycnojVrsBjw80OhgB49zCtVaexYfMbTp6P4\ni0ql7ytiDAVfX6JOnYiGDiX6808iW1t9X5FuUKngAvv4Y3S1atsWLqLVq+WrsZCbiziZ8ePhTmCk\ngX3SMhMcjEjKo0flLdS/YAFE+uRJVBD69luib75BgRW5/OKGxv372CVkZKAogzkXPjF3n3RBAe6B\nbduQYtSrl76vSHfExsL3nJWFZ8G1a2gHGRxMVLu2PHMKgayL/HwUWzKXZ44u4I9SZrp0IdqwAWlT\nsbHyzfPtt0QNGkCYp07V9KJ2cyO6d0++eQ0JOzsExLi4wMKwa5e+r4jRBw8fouZ7SAjM2+Yi0CoV\n0apVeOa4u+MZkJ6OOviHDskn0ESI5L5xA4tjFmiJEYxOWL1aiJYthXjyRL45srKE6N5diJkzhVCp\nhFAohPjf/4SoVUuILVvwmrkQEiJE69ZCjBolREqKvq9G95jrre3vL0TdukIsXChEQYG+r0Z33Lwp\nRM+euP+jo/FaRIQQdeoIcfy4vHMvXy5E8+ZCJCTIO4+5Yp53sp74/HMhevSAmMpFSooQ7dsLsWiR\n5rWwMCHatRPi3XeFePxYvrkNjadPhZgxQ4iGDYUICND31egWcxNphUKI774Tol498/pdFxQIsWyZ\nELa2Qvz2m2ZhcueOEHZ2QuzYIe/869YJ0bgx5mPkgQ0TOmTxYqLu3RHEkp0tzxw1a6KC0qZNmsjy\nDh1g+mvZEqkXW7aYR3BVlSpoieftjWCWOXMQ3MKYFmFhSMV7+BDmbVdXfV+Rbrh+HUGihw+jvOfM\nmch4SEpCKc5584hGj5Zv/q1biRYtQlEUuUohM2Rmy20DoKBAiDFjhHB1xU5PLm7fFqJ+fSH27Cn6\nenCwEF264LhwQb75DY0nT4QYPlyItm1hWTB1zOHWzsoS4rPPYNLdtMl83DlqN5atLdxoSqXm7zIy\nhOjUSYj//lfea9i9G1aLqCh552F4J61zKlTATrZ+fXSgkWtH3awZ0k8++QRR32o6d0YVoI8/JvLw\nQFGHBw/kuQZDwtaWaM8eoi++QDDdkiWoDscYJ8eOIe83IYEoIoJo4kTzaN8aEIBc50uXYB376CNN\noFZeHuoldOiAHa5cHDqEXfuJE0StW8s3DwM4BUtPKJWoyhMfD3OVHC3iiIjOnUPu4uHD/y4ukJkJ\nE7yXF6oxzZmDSmamzt27SFGxskJVODnrF+sLU03BSkxE5azLl+HOcXPT9xXphuhomK+jotBN6t13\niy5KlEqUyc3PRxOaihXluY7jx+E68vUl6thRnjmYovBOWk9UqAC/caNGyKN++lSeeXr2RFrE0KFo\n8F6YatWI/vc/PPCuXCF64w2U8jPBZ3sRGjfGLsDDAzuPcePMw5pgzKhU6KNub0/UpAl2z+Yg0E+e\nEM2YgTSyPn1wDw8b9m+BnjgR9/POnfIJ9KlTsLwdPMgCrUtYpPVIhQrIYXz9dXTkkUuoBw1CJa5P\nPsFKvDhNm0KcN24k+v575BlfvSrPtRgK//kPdgTR0UQNG+Lhv2gRUU6Ovq+MKc716yjr+fvvCFJa\nvNj0m8nk5RH98gsWzkS4b+fMIapcuei/KyiAcCYkoBSnXJYwX1+iCRNQ6rhbN3nmYEqGRVrPqIW6\naVP0iM7Kkmee0aOx2nZ1fX4zChcX1AEfMQLRodOnI1LUlFFbE0JCiMLD8VDcu9f0rQnGQHY2ivT0\n7o0yk+fPE7Vvr++rkhchIIRt22LnGhSEAiUldXkrKIAV6MkTlDyVa+GyZw+eHXv2wDLH6Bg9B64x\n/6BUCvHhh0L06iVEZqZ88+zYgWIP4eEv/nfJyUJ8+ikKoaxYIURurnzXZEicOiWEgwN+D1eu6Ptq\nyo8x39oKhRDr1wvRoIEQs2cL8eCBvq9IN1y+jO9d+/ZC+Pm9+N/m5yNbYcAAIXJy5Lsmb29EcV+9\nKt8czIvhwDEDQqUimjYNubzLl8vXDGDfPvi5fH1RPvNFXL+Oovx//onI6MmTTT+4TKmEdePbb+Eq\n+PFHdBwzJowxcEwIuF3mz0eJ259+QolLUyckBK4WpRLlgydOhIXteeTnw7KQl4fPq7gJXCqWL4cJ\n3d+fqEULeeZgSoGeFwlMMZRKIebNE6JVKyFiY+Wb548/kF966VLp/v3ly0K88w5yr5cvlzfH21BI\nSxNi7lzkoy5ZYlzv2dhu7cBAITp3FsLRUYgTJ8wj5/n8eeyE7eyEWLlSiOzsl/+fvDwhhgzBvSiX\ndUulQvW2li2FuHtXnjmY0mNcd7IZsXIlBDEkRL45Dh8WonZtPCxKS2goyovWrSvE0qXyljg1FG7e\nFGLWLCFee02IH36AK8DQMRaRDgkRws1NiGbNhNi1q2hhDlNEpcKCpE8fIV5/XQgvr9KLbU6OEIMH\n4/7Ly5Pn+pRKFIhxcBAiMVGeOZiyYRx3sply4AB8wr6+8s3h7y9Eo0ZCHDpUtv939Sp8YnXqoGaw\nnI1DDIW//xZiwgQhataErzQ+Xt9X9HwMWaRVKiHOnBHC3V2I/v2FWLMGPlZTRqXCfdy9O3aomzeX\n7T2npgrRty/iROT6rLKzhRgxAhURzbEpjaFiuHcyI4QQ4tw57OA2bJBvjuBgBIesW1f2/xsZKcSk\nSUJYWwsxZQqEzNSJjxdizhyI9YQJQly/ru8r+jeGKNJKJRaD3bqha9L69aYfkJiZid1y375C2NvD\nWlDW7lz37qFBzowZ8nX2SkwUomtXId5/X95ANKbsGN6dzPyL6GghmjSBn0guX93t23hwfv11+eZI\nTBRiwQIsKPr3F+LYMdM3XSYnw/xdpw58hGVxG8iNIYl0Xh5apbZpI4STkxB795p+G8moKOx6bWzg\nQ/bzK9/9cO0aurgtWybfvR8ZCdP7t9+aRyyAsWE4dzLzQhIShOjYEWlacpm7Hj9G440JE8o/R24u\nTHkODkglWbnS9E3h2dlodPD660ihOXJE/w87QxDpmzeF+OILLP7c3SFU+v5c5EShgIvKxQWL1fnz\ntQu8CgxEzMiuXdJdY3H8/DDH1q3yzcFoB6dgGRFZWSg0IgQKC1SvLv0cT58Svf8+kUKBVK1q1co3\njhBEf/1FtGYN0dGjaJH54YdEffu+OL3EmCkowGe2ZAmK0zg7o4hMSYUo5EZfKVjZ2UgL2rAB1dw+\n+AC/d3XlLFMkIQHv18sLJUs/+gilO7VJjdqxAxXG9u5FMRc58PYm+uYbzPHWW/LMwUiAnhcJTBlR\nKGCWatMGZnC55vD0hGkyIUH78VJSsNPs2BGmu2++ESImRvtxDRWVSojTpxGAU6OGEMOGIZJeodDd\nNej61r5yRYjp02HedXfHjlKuCGRDIDsb7/HTTxGPMXWqNAU/VCohFi9GMGdkpPbjlYQ6grtFC1g7\nGMOGd9JGirc3ij6sX4/mGVIjBAoshISgnneHDtKMe/UqajDv2IESj1OnYpdtbS3N+IZGejqsHps3\nE928GUo9esSRk1MKffedJxERHThwgKytrSk2NpY8PbV/TY0udtKpqfg9btyInydNQn3nhg1lnVZv\n5OejVeTu3egq5+SEzlMeHkQ1amg/vkJB9N13KDLk64t2tlKTmUn06adEcXEoP2pjI/0cjLRw7W4j\nxdOT6MgR3HBffy19b2QLC5jCRo9Gt6Hdu6UZ18GB6LffiO7fh1nwxAl0Ahs0COKdkiLNPIZCjRpE\nU6bA9N+580/Upo0H/fprGrVtG0ZffRVKmZlErq6uREQUFhZGoaGhRFS+13SBUkl08iTR2LEw7Z47\nR7R0KVFsLL4vpibQ6vc7ZQpE88cf0ZP9+nU0+/jwQ2kEOjERdfVjY4nOnpVHoP/+G9deuzZ6zLNA\nGwe8kzZyHj8mGjkS/q8dO+QpJXr1KvrXenigA5HUrfAyMuC33r8fO5Vu3Yjeew8WAn34c+Vg//79\nFBcXR/PmzSOlEqUWZ8/+khSKftSwoQu1ahVI1auH0n/+k0xubm7k6upKgYGBFBoaSsnJpXtt3rx5\nz+aTciedn49mDwcP4rC3RzOYsWPlK12rT5RKogsXYAHZt4/Izg5xGiNGYEEpNX/9hbE9PbHQ+Y8M\nW6ft24lmzyZatgzd3xjjQabOo4yuqFMHD/wvv8Qq+cABIkdHaedwcEDP6ZEj8XDevVvaVXj16qhF\nPGoUguN8fSHY33xD1LIlArBcXYm6dpWvTrHchISEEBF2vAEBATRv3jw6eDCNJkywoeRkonXriHbu\nTCYLizSKj8eHq1AQJScnU1paGtkU+sCf91pxFixY8OxnZ2dncnZ2LvX1ZmQQHTsGUT5+nKhNG9SV\nPn2aqFWrsr13Y+DOHdxH/v7YIffuDXN2UJB8dauFQF38H36AFWngQOnnyM2FOAcG4rC3l34ORl5Y\npE2AihWxQu7cGS0mf/kFUbVSYmuLh7V6MeDjI88NX7UqdhUjRqC387lzeLh89hmihbt1Q0tNFxc8\nRI0pUrxWrVrk6OhIAQEBdODAASIiqlxZ0KBBaFrSti1RTAzMp/Pno6WonR1Mn6mpZZ+vsEi/DKWS\nKCwMpt2YGKJdu4h69YI1Y/ly42sw8jLS02Ed8PeH6Tc9HZkH7u7ovW5nJ+/82dmIx7h2DTvpZs2k\nn+POHVikXn8di2wpzPKM7mGRNiFGjsSDftgwolu3iL76Stoes+rFgJMTdrbr1sEELhdWVvCHu7nh\nz2lpRGfOQEgmTiR68AA7HhcXXE+bNvCl6wtvb+9/vWZjY0MeHh5ka2tLTZo0ISIia2trunz5Mllb\nW1PKP074tLQ0sv3HduzmlkJr1hBt3JhGfn62FB5O9M47KdS+PZGdXRpVq2ZL1arRs/+bmpr67P+W\nFpWKKCICQnXqFPygDRoQ9elDNHgwfs/lTb8zRBQKouBgjShHRGDB5+YGk7a9vTxm5pKIjcU92q4d\nzOpy9IE+ehT+8i+/JJo1S7/3BaMd7JM2QdLTEZQVFgZf1MvaUZaHq1ex2+3eHU3pq1aVfo6X8egR\nBFt9ZGVBsAcMgEm2bVvDEZq4uDjav38/zZs3j5YuXUrNmjWjJk2aUEhICHl6etLSpUvJzc2NhBAl\nvnbxYgi1bu1JixYtpawsN/r7b0FVqoRQv36elJW1lIYPd6PhwztQpUqYr7hPOiODKDQU0fo3b8IS\nUrMmRNnFBS6F117Tz2cjNbm5sEKEhWmOChVQA6BfPwhzjx5YBOoaX18sML/+muiTT6QXT4UCWRmb\nNsEt1aOHtOMzuodF2oTZuROr6DlziObNk940nJWF6PLz52EelWMxUBbu3IFY37yJALSoKPjs27VD\nule7djhatdKPb9vb25tsbGwoJCSEFi9e/Oy1pk2bFkmjKs1rSiXRokXelJnZlIKDYyktzZNiYhA/\n4OhItGaNBc2fLyg2lujKFVgdHByIOnaE9aFrV/lNurogI4MoPLyoIN+6BT+yo6PmcHDQr7lXoUBW\nw2+/ISCtZ0/p54iKgpvL0RFCXaeO9HMwuodF2sS5dw83rkpFtHUr/FNSs2cP0YwZRF98gSAVXZkN\nX4ZSCdNiZCTMm5GROOLikD5UWLjbtUOVMGPxcatURA8fEt24AV99dDTSgiIjYT2IibGgxo0FPXwI\nMX7jDQThNW+uORo3lj5SXw4KCvBe4+PxfX70CAvDsDCkLrVvX1SQ27WDj99QiI4mGjcOmQq//y69\nf1+lIlq5EsL8ww9E06axeduUYJE2A5RKBMP8/DOCgMaMkf4mvnMHOdXVqhFt2WLYgUa5uRC34uLd\nvDl2YXXqwPRbp47mKP7n2rXlETilEu6KtDQEi6nF6f79oue0NMzfuvW/j0aNiCpUgLk7JwcLldu3\n/30kJCCvWS3abduiqEzt2hAU9VltQpeDvLyi7/PevX+fHz3CtTRqhOvt2BFnR0csPAx1YaVSwRW0\naBGOKVPkue8mTkSa3JYt+D0ypgWLtBkRHg4htbcnWrsWPkkpKSggWrgQdYw3bkSkrDGRnY2880eP\ncFYfJf05JQXm0zp1YEpNSdE8gC0sih7Pe61ePex+09I0opydjZQ0a2vsCJVK7IQbNix6trMjevXV\n57+X0uRJ5+XhIa8W7fR0FLxIStIcyckIbHJxwb+xsip6VKmCs50dPpuCAlyzUqn5uaAAYq+eIy0N\n5/R0/H2fPvi/DRtqhFh9btgQAW2vvKL971eX3LsH8czJgQVLavEUAn7nL7+EK2vuXMNdrDDawSJt\nZuTkwCzt44NSlf8UrJKUs2dR6GLyZPjD9RFUJjdKJYT58WOcnz7F60IUPV70mqUlBK5mTYiytTUs\nEVK4C6QqZqJSQVRTUrCAyMnRHIX/XLEizhUq4KhYsejZygpxADVqFD2srEzLNCsE0bZtSBmUKxYk\nMRGFT+LjMVf79tKOzxgWLNJmSkAAzG/dusEULnVkb0oKHlAnT2LXPmCAtOMzL0ZfXbDMmaQk5D7f\nugXxlKrefWH27UP8x+TJRN9+K68rgjEMDCTEh9E1ffvCH1u/Plbi69dj1yQVNjYwea9fj3SwsWPx\nEGMYU+TPP+FGatECaW5SC3R8POogrFpFdOgQfNws0OYBi7QZ8+qraI7g74+o0169INxS4uaGMevW\nxWJg2zaNyZdhjJ0nT+A+mjULfZmXLJE2vU+hQGEZR0dE6J84gfQ5xnxgkWbIwQEpLePGIUDoyy/h\nb5SKV1/Fg+bIEZjWBwxAGhTDGCsqFdrFtmmDALyrV7HIlZKgIIizvz8qky1YoJ8CLIx+YZFmiAjB\nStOmYdd77x7ScXx9pZ2jUyeUZnRxQf3vX39F5C/DGBNhYai09/vvKDH622/SVrZ7/Bh9uUePhjAf\nPy5fkw/G8GGRZopQty4qlXl5oZrYxIkQbal45RWYBy9exM76nXeILl2SbnyGkYv0dNwTAwYg6PLc\nOWl9z0olgizbtUPK2vXraJBhStHvTNlhkWZKpF8/7Krt7WFy++orPKSkonlzdLcaPx7NBsaMkXYx\nwDBSIQTqYLdpg0I416+jeYWUlfX8/Yn690er2cBA02twwpQfFmnmuVhZoczntWswwbVsiejS/Hxp\nxrewQMTqjRsQbUdHtGjMzJRmfIbRlqAgpCkeOoQe5+vXo22rVISGYkH88cdwN/n7c94zUxQWaeal\nNGiAdCp/f7TAa9uW6I8/pIvSrlqV6PvvEXwTH48GGBs2wPzHMPogOppoyBAEU86YQbRjB8RaKmJj\nYT0aNIjo3XdR6Y1N20xJsEgzpcbeHkEsq1dDVHv1QtSpVNjZoYTin3/i7OQE0x/D6IrEROxo33oL\nR3Q0xFQq03ZSEtHMmQicbNUKhU+mTze+sqeM7mCRZspMv34w002eTLR4MdHgwYjalopOnYjOnEFF\npV9/xXxSLgYYpjhZWYikVvcgj45GPWypumk9fYoCJG+8gfStqCh8v02xZC4jLVwWlNGK3FwU+v/p\nJwTWfPedtGbB/HzsqhctQoenBQuI3nxTuvFNFS4LWjoyM4nWrIH7pkULfM+kbOeakUG0bh0WndWq\nYXzuVMWUBd5JM1phaYmyn7duIUp71ChUGQsKkmb8SpWwY795E+OPHInuWlLu3BnzIz0dgtm0KWIh\nNm0i2r5dOoFOSiL6+muMHx4Oi9Pu3SzQTNlhkWYkoXJl5I7eugWhnjABLQhPnZImwKxSJYx/8yYC\net57D0E3nGPNlIWUFFh7mjXDdykoCHUB2raVZvx79+BzbtUKJUMvXcL49vbSjM+YH2zuZmShoAAP\np0WL8IAaPhw7YakCZPLyEHF+8CB+njMHvnHuqQvY3F2UBw+wU/75Z6KhQ5H3L+WuNjoadbsPHSKa\nNAmpi/XrSzc+Y76wSDOyolQiWnvFCqLbt2Ea9/Qkql1bmvEVCvgTf/mFKDUVjQ4mTEC9cHOGRRpc\nvIjv3okTaCM5ZQpRkybSjC0Eat6vXo0shBkzkO9sYyPN+AxDxCLN6JCrV4lWroSoDhuGEosODtKM\nLQTRX39BrM+exULgk0+Q422OmLNI5+ej8MiKFfANz5iBCmE1akgzfmYmduVr18KK8/nnRO+/zwtD\nRh5YpBmd8+QJOgitXg2T46efooZ3xYrSjB8Tgwf09u1EHh4oPdqjh3kVijBHkU5KQs35tWvhE545\nU1oXyLVrGHv3biJXV+Q3u7iY1/eK0T0s0ozeUCiIfHwgqLVrw3f9wQfS+QpTU4n27UOXotxcVI8a\nN848ImzNRaSFQPCXry8E2sMDiz6pArVyc7ErX7uW6O5dWGgmTzZfCw2je1ikGYMgPJxo82aiXbuQ\nrzp+PNGIEdKYKIVA8ZVt2zB+s2ZYDIwYYbr+Q1MX6bg45M9v2UJUpQp8zaNHo3uUtgiBXbOvL9Hy\n5eh0NX060dtvS2ftYZjSwiLNGBQKBdGxY3j4BgYSDRwIwe7bVxqzpUKBHsBbt6LEad++2F0PHIg0\nL1PBFEU6MxO72i1bUOv6/fcRJOjkJI3JOSYGi7idO4mys7FjHjmSezkz+oVFmjFYkpPh/9u8mejh\nQ9RQHjUKOxspHsppaXjob9uG9oMjR0Kwu3Qxfj+jqYi0Ukl0+jS+A4cPE/XuDWEeNEiaRdXDh0R7\n90KY796FdWXUKFTNM/bvAGMasEgzRsH165pdlEqFXNehQxEQJoUJMi4OnY62bsXDedw4orFjpS0R\nqUuMWaRzcmBFOXQI6XsdOsDSMWoUUZ062o+fkoK+zbt2wc0yZAjGdnFhczZjeLBIM0aFEESRkShi\ncvAgKjwNHgzB7tcPPbC1HT84GGK9dy9KnHbqBJFo1cp4dlfGJtLJyWiDeugQUUAAeosPGYKjaVPt\nxhYC5vFjx+BnrlSJqHp1CPPAgdI10WAYOWCRZoyau3ex2/LxIbrgjvDwAAAGGUlEQVRyBakxQ4cS\n9e9P9Npr2o2dn0908iTG9vVFtbSBA3E4OyNgyVAxBpGOi4MoHzpU9Hc3aJD2AWBZWdiN+/pCnCtU\nwO/N3R3lajmnmTEWWKQZk0G9G7t4ET5GOzs8+F1d4cvUJlJcvYNXP/RDQ4l69sRD393d8NK6DFGk\nk5JQaOb0aaL4eBSfeftt7Jb79tVu0SMESnOqfz+XLhF17aoR5tatjccKwjCFYZFmTJKCAgjpyZPY\nUV28iFaarq7wPfbooZ1pPC0NZllfXwQfRUUR9eqlOd54Q7+iYAginZSEFo2nT+N87x4WNr174+jU\nqfwR+wUFRBERKMt5/jz8zFFRGlF2cUFrSIYxdlikGbMgL4/owgWNaFepgnaFXbpgx9WlC1Jt/lOO\nvnBCaDoqqY/MTAhSr144OzigU5iu0LVIC4HdcXAwRPn0aaL79/HenZ0hyo6O5Q/MysjAQkstysHB\nKCjSoweO7t2JWrbk3TJjerBIM2ZJVhZRWBge9uojNZWoc2cIdpcuEJWGDcv34L9/XyPYKSnwu7Zo\ngUhlBwfNWYriGyUhp0irVMgpDg3VHGFhEOChQ/E+e/fGeyyPKCcnY5ccEYHWp2fPojlLx44Q4x49\nkCJlayv9e2MYQ4NFmmH+4fFjosuXi4r29evwZ7ZpAxO2+mjSpGwClJuLCOOrV5H2Ex6On6tXLyrc\n9vYYW9uWnlKItFKJ3fHt2xDLpCRYIcLDiWrWRBERR0ecnZyI6tUr2/i5uTBRR0SgwpdamJ8+JWrf\nHoe9vWYeUyo2wzClhUWaYV6A2tdZ/EhMRLDYG29g1129OsyvDRqgj3CtWi83natURHfuaAQ7PBz/\nx9eXqG5diHVJR716Lx+7tCKdnw+fekwMhPjWLY0ox8XhfTRvjt1xp05Ih3J0LN0uVqGAReHOnZKP\nxo2xEFILsvpo1IjN1gyjhkWaYcpBdjbRjRsQ7MePsQN8+JDowQMcWVkQ2sLC3aABhMnKCkFN1aoR\nVa2q+fnVVxFIpVBgBxsXV/KRloZxGjRA7XFbW5yrVYOv3cqKaNo0C9qxQ1DFirjWpCSiR4+wuCh8\npKcj0CojA0LcooVGlJs100RcFxTAr5+Tg/mTk9HNrKRz1apEp05h/Lp1URCmpKNBA94dM8zLYJFm\nGBnIzYVoFxbuhw+xA46KQmBZVhbO6uPpU42Ad+0K8zgRdpXqnaWFBXbgKhXE9+lTzKVQwPxesSLm\nyM62IEtLQSoVxkxPxwKgYkWY0itVQiCbpSX+/MorRG3bwuycmwtBLnwmwr91cIDY29pil13SuV49\nCLCdnfZme4Yxd1ikGcZAUAtvZiZ2rCoVoqbVd2jhsxAQ2qpVcVhZFTWBFzd3q1QQ8vx8nAv/rD4L\nARG3tNQIuPrM5TIZRj+wSDOMCWIIedIMw2hPObJCGYZhGIbRBSzSDMMwDGOgsEgzDMMwjIHCIs0w\nDMMwBgqLNMMwDMMYKCzSDMMwDGOgsEgzDMMwjIHCIs0wDMMwBgqLNMMwDMMYKFzsj2F0xIEDB8ja\n2ppiY2PJ09NTltcYhjEtWKQZRgeEhYVR06ZNydHR8dmf1bi6ulJsbCyFhYU9K+VZntfUYzMMYzqw\nuZthdMQXX3xBRESxsbHk6OhIe/bsoZo1axIRUdOmTSkgIID27t1L1tbW5XqNYRjTg3fSDKMDHB0d\nqUmTJmRjY0Pe3t5ERJSWlkY2NjbP/k1ycrJWrxVnwYIFz352dnYmZ2dnCd8RwzC6gEWaYXRAWloa\nNW/enLy9vcnT05OcnJyIiGTrVMUdsBjGNGCRZhiJUO+QC2NjY0MeHh7k7e1NU6dOperVq5O1tTXt\n37+frK2tKSUlhYgg4ra2tkREZX4tNTX12WsMw5gWLNIMIxEvi7CuXr06EWmCvfr27UshISHP/uzm\n5kZCiDK/FhcXR25ubrp4iwzD6BgWaYbRAfPmzaOlS5dS06ZNKSUl5Zmgh4SEUGBgIFlbW1OHDh20\nfo1hGNPCQrDzimEYhmEMEk7BYhiGYRgDhUWaYRiGYQwUFmmGYRiGMVBYpBmGYRjGQGGRZhiGYRgD\nhUWaYRiGYQyU/wdTZyvgq7IG/AAAAABJRU5ErkJggg==\n", "text": "" }, { "output_type": "pyout", "prompt_number": 13, "text": "" } ], "prompt_number": 13 }, { "cell_type": "code", "collapsed": true, "input": "", "language": "python", "metadata": {}, "outputs": [], "prompt_number": 13 } ], "metadata": {} } ] }sympy-0.7.4.1/examples/notebooks/density.ipynb0000644000175000017500000010752512253362407021621 0ustar georgeskgeorgesk{ "metadata": { "name": "density" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Density operator and matrix " ] }, { "cell_type": "code", "collapsed": true, "input": [ "from sympy import *\n", "from sympy.physics.quantum import *\n", "from sympy.physics.quantum.density import *\n", "from sympy.physics.quantum.spin import (\n", " Jx, Jy, Jz, Jplus, Jminus, J2,\n", " JxBra, JyBra, JzBra,\n", " JxKet, JyKet, JzKet,\n", ")\n", "from IPython.core.display import display_pretty" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 59 }, { "cell_type": "code", "collapsed": true, "input": [ "%load_ext sympyprinting" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 60 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Create n density matrix using symbolic states" ] }, { "cell_type": "code", "collapsed": true, "input": [ "psi = Ket('psi')\n", "phi = Ket('phi')" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 61 }, { "cell_type": "code", "collapsed": false, "input": [ "d = Density((psi,0.5),(phi,0.5)); d" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\rho\\left(\\begin{pmatrix}{\\left|\\psi\\right\\rangle }, & 0.5\\end{pmatrix},\\begin{pmatrix}{\\left|\\phi\\right\\rangle }, & 0.5\\end{pmatrix}\\right)$$" ], "output_type": "pyout", "prompt_number": 62, "text": [ "\u03c1((\u2758\u03c8\u27e9, 0.5),(\u2758\u03c6\u27e9, 0.5))" ] } ], "prompt_number": 62 }, { "cell_type": "code", "collapsed": false, "input": [ "display_pretty(d)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "display_data", "text": [ "\u03c1((\u2758\u03c8\u27e9, 0.5),(\u2758\u03c6\u27e9, 0.5))" ] } ], "prompt_number": 63 }, { "cell_type": "code", "collapsed": false, "input": [ "d.states()" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\begin{pmatrix}{\\left|\\psi\\right\\rangle }, & {\\left|\\phi\\right\\rangle }\\end{pmatrix}$$" ], "output_type": "pyout", "prompt_number": 64, "text": [ "(\u2758\u03c8\u27e9, \u2758\u03c6\u27e9)" ] } ], "prompt_number": 64 }, { "cell_type": "code", "collapsed": false, "input": [ "d.probs()" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\begin{pmatrix}0.5, & 0.5\\end{pmatrix}$$" ], "output_type": "pyout", "prompt_number": 65, "text": [ "(0.5, 0.5)" ] } ], "prompt_number": 65 }, { "cell_type": "code", "collapsed": false, "input": [ "d.doit()" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$0.5 {\\left|\\phi\\right\\rangle }{\\left\\langle \\phi\\right|} + 0.5 {\\left|\\psi\\right\\rangle }{\\left\\langle \\psi\\right|}$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAKoAAAAXCAYAAACbItQpAAAABHNCSVQICAgIfAhkiAAABZhJREFU\naIHt2m2IHdUZB/DfNoYYk1ijgVRtUFpL0ITUVK00GkwQCrVtaPohEVETo7aQUGxDQRFsaSn0Sw1E\nQdRgu2nVFqHS4JdWgm6rokaE+Ja+qEgt1cZopfXdaNIPz8zu3Lln7s7dO7t3U+4flrtzznPm/8x/\nzstznjMMMMAAbbgZJ/bbiZo4knwdD0fqs4z6/YlSxafxa2zDD/ETnFDjhvfhN1iDhbgKD2BTyW4e\nZtV0cnuibB1W1Wz/Axw/QR6687VXTCfd4WeJssnQvhMfBb+LHXUGRvAgtuJHWfnd2jt06obrsQv/\nwg48gp934WwZRyXK7sOGGm2PES//3xPkmUpMN91hZqJsMrTvxNeCohAXYxHuKpTtxJexsQbZTfit\nEHoFbqjrZQKn4flE+Xv4Z1bfCRvwqx54esUXurCdTrrD5/C3RHnT2o/HV4m9Ytko40XcO07bkZoc\nwzi1ht2VOLOi7iTc2KHtEO6s6U8nnmH1fE1hpAvb6aQ7fAunV9Q1qX0dvmGZ3/mMehSWSc8uL6gf\nmzSFpXi6ou4VzMEnK+q/gt83wDMVmG66w2L8uaKuSe3r8I0ij88WitHwdsLmHcwXQe0HHe61Xix5\nQ2Ij8Ev8sb6/zhZL3au4ALdm3D/GmyXbO8RsuC1xn3W4uiGeyUa/dD8Gm3EuduO2Qt2hwv/D+FDM\nejkmon0vfBjrqJ/Kft9JkORlx2F/op4Q+g1cm10vFUH9CjxX0abow3bMxnfFy3tfLDFXCNG/mJXl\neALXiI3Ix4XypWJ2OtgQz2SjX7pvFR16n1iqd4gOsxh/Kdgdrz00moj2vfBhbOnPR+yhhM3MDnU5\n1oiRkuNZ7FFv97ldxD6b8F+sNhaz/ULMEhsT7XbhG6Wyb2sdrU3wTCb6ofs8zMXLWClWkZyjqAnR\nwVKDpBvtm+Ab7ah/FVNuCnPEKHm9op60mK/grMzJKqwR0/yWQtnn8VTh+jUxWsu4F2sL1wuy35Sf\nvfBMJvql+zbx7i8T+dscp+ClwvV+PJ5o3432TfCNdtSDYgqen7CZK17i4Qonvi92qOW2s8Ty8NmK\ndsSo3C3ELfqUv4CjsUTsjFMYKv0/VGHXK08KM8Uyuzfxd3ZF+R4cW7hHP3R/K7vvSpyMewp1H5ds\nV4gYPoW62jfCV0x2Py3yeUXMwHI8WuEEEcjP0560XSbirH0d2i4p3fuMkv1XRWe6P9H2m/hd4fpA\n9rtA+8juhacKB3FeRd2I+jv2fugO54ukfJ71OF3r7nuG2N3/IdG2G+2b4GtJ+D8ldsGzC2XnZI3L\nR1yLCnZ/wiVi1OSYIwLl3dLBdY6HRNyYY7U4oSE2ETeJXeTLibZrtecZbxOxUpM8k41+6E4MzP8U\nrldpjRe3qE41daN9E3wtmC9OCL5TKLsdj5XszhJTdn7TueK47zMFm++JwH5Ba9O2xPNpYlQtz65v\nEUvI4uwhNlf4eo5Y+lLYqX2WmQhP2dduMNKFbT90J579fZHpYOzodp74GOSCCn+71b4XvlG/i0v/\nm/iaOILbkZXNwddLjQ/g73gyu35bpHuuF7PAITH9X6jzRoBIal+E67KHOFfMIodxqdaYsoirjKVk\nyrhH5BaLJyQT5ZkK9EN3YiP3paz9MyI+vFFkIn6qOe175esLhlXPUmeonkGLOEk62ZxjSOu5+UR5\nhk3NjDoVGNb5WVaLbw7GQ6/ad8s3rHSEOh2wEg/XsNsslu4qHM7us7JHnl7w2vgm0wqr1RtcvWrf\nLd8oprqjHtKeksixVMRXnTBbpDheGMduJy7vgYfOvo6HdRNsN1kY71lOFJ8JdkIT2nfDR8Hvqf4W\n8xqRV0vhI51PYYi4bWcNnnfxD3EkV/4usg4PnX090tDpWWZJH+GW0YT23fDx//UOBhhggAEGGGCA\n+vgfqAPSYqWMIZsAAAAASUVORK5CYII=\n", "prompt_number": 66, "text": [ "0.5\u22c5\u2758\u03c6\u27e9\u27e8\u03c6\u2758 + 0.5\u22c5\u2758\u03c8\u27e9\u27e8\u03c8\u2758" ] } ], "prompt_number": 66 }, { "cell_type": "code", "collapsed": false, "input": [ "Dagger(d)" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\rho\\left(\\begin{pmatrix}{\\left|\\psi\\right\\rangle }, & 0.5\\end{pmatrix},\\begin{pmatrix}{\\left|\\phi\\right\\rangle }, & 0.5\\end{pmatrix}\\right)$$" ], "output_type": "pyout", "prompt_number": 67, "text": [ "\u03c1((\u2758\u03c8\u27e9, 0.5),(\u2758\u03c6\u27e9, 0.5))" ] } ], "prompt_number": 67 }, { "cell_type": "code", "collapsed": true, "input": [ "A = Operator('A')" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 68 }, { "cell_type": "code", "collapsed": false, "input": [ "d.apply_op(A)" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\rho\\left(\\begin{pmatrix}A {\\left|\\psi\\right\\rangle }, & 0.5\\end{pmatrix},\\begin{pmatrix}A {\\left|\\phi\\right\\rangle }, & 0.5\\end{pmatrix}\\right)$$" ], "output_type": "pyout", "prompt_number": 69, "text": [ "\u03c1((A\u22c5\u2758\u03c8\u27e9, 0.5),(A\u22c5\u2758\u03c6\u27e9, 0.5))" ] } ], "prompt_number": 69 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now create a density matrix using spin states" ] }, { "cell_type": "code", "collapsed": true, "input": [ "up = JzKet(S(1)/2,S(1)/2)\n", "down = JzKet(S(1)/2,-S(1)/2)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 70 }, { "cell_type": "code", "collapsed": false, "input": [ "d2 = Density((up,0.5),(down,0.5)); d2" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\rho\\left(\\begin{pmatrix}{\\left|\\frac{1}{2},\\frac{1}{2}\\right\\rangle }, & 0.5\\end{pmatrix},\\begin{pmatrix}{\\left|\\frac{1}{2},- \\frac{1}{2}\\right\\rangle }, & 0.5\\end{pmatrix}\\right)$$" ], "output_type": "pyout", "prompt_number": 71, "text": [ "\u03c1((\u27581/2,1/2\u27e9, 0.5),(\u27581/2,-1/2\u27e9, 0.5))" ] } ], "prompt_number": 71 }, { "cell_type": "code", "collapsed": false, "input": [ "represent(d2)" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\left[\\begin{smallmatrix}0.5 & 0\\\\0 & 0.5\\end{smallmatrix}\\right]$$" ], "output_type": "pyout", "prompt_number": 72, "text": [ "\n", "\u23a10.5 0 \u23a4\n", "\u23a2 \u23a5\n", "\u23a3 0 0.5\u23a6" ] } ], "prompt_number": 72 }, { "cell_type": "code", "collapsed": false, "input": [ "d2.apply_op(Jz)" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\rho\\left(\\begin{pmatrix}J_z {\\left|\\frac{1}{2},\\frac{1}{2}\\right\\rangle }, & 0.5\\end{pmatrix},\\begin{pmatrix}J_z {\\left|\\frac{1}{2},- \\frac{1}{2}\\right\\rangle }, & 0.5\\end{pmatrix}\\right)$$" ], "output_type": "pyout", "prompt_number": 73, "text": [ "\n", "\u03c1\u239b\u239bJ \u22c5\u27581/2,1/2\u27e9, 0.5\u239e,\u239bJ \u22c5\u27581/2,-1/2\u27e9, 0.5\u239e\u239e\n", " \u239d\u239d z \u23a0 \u239d z \u23a0\u23a0" ] } ], "prompt_number": 73 }, { "cell_type": "code", "collapsed": false, "input": [ "qapply(_)" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\rho\\left(\\begin{pmatrix}\\frac{1}{2} \\hbar {\\left|\\frac{1}{2},\\frac{1}{2}\\right\\rangle }, & 0.5\\end{pmatrix},\\begin{pmatrix}- \\frac{1}{2} \\hbar {\\left|\\frac{1}{2},- \\frac{1}{2}\\right\\rangle }, & 0.5\\end{pmatrix}\\right)$$" ], "output_type": "pyout", "prompt_number": 74, "text": [ "\n", " \u239b\u239b\u210f\u22c5\u27581/2,1/2\u27e9 \u239e \u239b-\u210f\u22c5\u27581/2,-1/2\u27e9 \u239e\u239e\n", "\u03c1\u239c\u239c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500, 0.5\u239f,\u239c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500, 0.5\u239f\u239f\n", " \u239d\u239d 2 \u23a0 \u239d 2 \u23a0\u23a0" ] } ], "prompt_number": 74 }, { "cell_type": "code", "collapsed": false, "input": [ "qapply((Jy*d2).doit())" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$0.5 J_y {\\left|\\frac{1}{2},- \\frac{1}{2}\\right\\rangle }{\\left\\langle \\frac{1}{2},- \\frac{1}{2}\\right|} + 0.5 J_y {\\left|\\frac{1}{2},\\frac{1}{2}\\right\\rangle }{\\left\\langle \\frac{1}{2},\\frac{1}{2}\\right|}$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAZ0AAAAlCAYAAACZMTQOAAAABHNCSVQICAgIfAhkiAAACElJREFU\neJztnWeMFkUYgB/OO0VBxFgQ2w9RVCxYYtTYTkXsJRorNkw0miixxBbrHxSJPdFoNIIGhaAQjV1B\nwRbFQmJJ7CWKXWNDxYL+eL8vt/fdfvvNzO7Olnmf5MLdMLP73nzz3O7MzsyCoiiKoihKRkwC9kpR\nfhNgSkaxKEpdSeOZOqbUijHAzBTlrwIOyCgWRakraTxTx5TasRAY7lCuC1gErJBtOIpSS1w8C86x\nrqIDyIjtkQ/cllWBwzKOJS/SxDoTONah3D7As8C/FmVCqVNwb3chUhVHfXtWBcfUkxjGAR9a5F8X\nOA94EZiRS0TZkUWsw3H70GcBmxnmDa1Owb7dhUzZHS3KszI7losn3SmDqio/A7cCK2P+gRdFFrH+\nBCwBtgDeMSyzOrAO8K5h/tDqVMkX359REZ6V3bFczleX4TVblgJ/FB2EIVnFOg042SL/sdg9GA2x\nTpX88P0ZFeFZ2R3L5XyhXnRCZD6wB+a926ORrr+iKObYeBakY+0uOrYXozTHWRkY2vI12PL8SmeW\nA08C+xvk3QIZJvg514gUV1xuFtVRP5h6FqxjrQ1uY+AhYCpwBXA9MubYiceBR4DjgFHA2cg0wJM6\nlOsB7gHmAF8AvwJvNc6tZM90zLr+Ext5lXLh6ieooz6ZTmfPgnUs2gXsQbqGlwN3N9IuQ7p/e5M8\npW8lYDxwYOPn/4CLkfHNJP4Gjmx8/yCyQGoH4Huz8BVLPgKGAWsB37XJ0w30Ahd4iilUtgI+Rf6I\nm5DGT1BHfdLJs6Adi/Z0JgAjgPsjafci45OnGhzrKkSG84FtgGss49gdeBU/jbkLWBvYCNgAmRpY\n1pl8Wcd6L/JZt2N/ZHhgueVxQ65TFyYgQyw2+dP4Ceqoz/MleVYVx3I536DI928DXyGLlaK8j0zp\nOyThOAuQK7cr2wCLgcnApQ7lxwG3IcMPJqwLHNWS9jTm04l9knWsQ5AGv2ub/58DXAR8YHncEOvU\ntt1FmYL0HF42zJ/GT1BHfZ8vybOqOJarJz3IVfe2mAJPIvPPk1gQ+d7lIec5SHd/nENZ0EV6ttwF\nbBuTvibwjOdYqkyadjcF2Mkwb1o/QR0tgjjPQnQsdnHoCKTXszSmwFJgNWRMeFnCgSciC4iWA+sj\n3csnDIPqBf4CXjLMr6RjGvJ5LW5JnwDc5z8cpQNZ+AnqqG/iPAveseZFpzkDpl2jBtni4Zs2x/kR\neI++h5KjgdeR8eY3OsTQHCteBPzeOWQlA14AbopJ3w/z5wOKP9L6CepoEcR5FrxjzW52s+HGPdha\nsfFv0uyYw+l/B/R+4+c7DWLYFhFmgUFeW/7L4Ssv8oi1Xby7En/H+hQihe8488JnneZJWj9BHS3i\nfHGeldExr+dr9nQ+oH3XfBVk2uQPCccZFHOir5CxvGHALwllexv/PpsUqCODOmfJnIuw25F1MvAw\nfmOdCNwSkz4DmE3yHyKtUzuuQ6Y0tzISuRDE9RwuRJ7VNEnrJ6ijRZwvzrMyOlaYJ4uBm2PSFwCf\nJ5S7DPgaeUAWZTbSyMd2OO8jwJ/IqmdXQnxI6coQksfl5yJvMlQ642siAbj7CepoESR5Fppj/T77\n6CyWN4ENWzJ3A9sx8IHzpsjdEcjY8DIG3kWNRRa+NafXrYrM8Z8bybMifV3Qqm3AuAqwXtFBOHAk\n/dd6tDIdu41Bs6SqdeoDVz9BHS2CJM+mo44BcC4y9XJIJG0XpKHuFknbsZE2v/HzqQwcoxyKjD9H\nK32/RrmpkbQTGmnjU8ZexF3UY4iwW3s+b1rmISul29ENvEYxm8FWrU599nRc/QR1tIj2lORZaI61\n7encgeytdGYk7TTgOeD5SNrnyN5LzbnmM4Djgc0jeSYhd1/RWRovIA28ufXDpsh4943Iw7Wq8SLw\nMekW3PlmFDJ2324LHIB/kCEb1/UYaahinfrC1U9QR3s9n7eTZ0E7Ft3S4FfgIOSh7Z3I3c0y4NCW\nMl/S/yr5B3BWo9zayN3TZ8jK6eiitd+QxVJXIC8vWgM4kfh1Akcg+zs9RZ88i5CKymLK5grIYrfB\nyNjqEmTc2+aVsZORjRD3zSCeJLKItcnJmG0yOA24BLs/NKHWqS9c/QR11KQ9+fasCo5V0RMnxiLD\nDpcCVzbSxpC8ZYRt1/0c+lYKdyPrFK62ilI4AtkeJE+yirULGa833TtpIbLo0JQQ6zTNkNEF2O29\nVibq6GgRnpXdsVw8KeNL3JYg+1GdQN94cy/27x5PYmdk00OQru48zN4zE6UH2BN5wJsnWcQKMm13\nYeMYJswCjrE4foh1moaplHNfOhPq6GgRnpXdsTJ44o2t6P/e8NlIA2+H7V3UevSfCTQTeMCiPMgY\nuctGj7ZkESvI1hs2d9bDsVuXEWKdhjYNOErdHC3Cs7I7FpQnByI78IJM8fuWgdNFo6T5pTZB1jCM\ncizvE9dYh+N2FzoT2avLlhDqFCoiU07U2VGfnlXFsdp7sjrypsPjkWGITzrkd/2lhgGPAls6lPVN\nmlhPB85wKDceu3euQDh1ChWRKSfq6qhvz6rgWFCedAE3INM2k3D5pQYjr/sd2fh5jGV5n6SNdSFy\nF2ZLF/AKMovFhJDqFComU07UydEiPCu7Y5l7UsaJBEORAEcgu+seTOcGbUs3MvPmdmTq6Ujkjq2M\npI11DDKN1uSdK60sR17aZDK9MqQ6DZ06OlqUZ2V2LBhPepA57qcgi9JGG5TZHlkkZ8pNDNwVNW6r\n/zKQNtZJyGwVVzYGrjXIF1KdNrFtd3Whjo4W6VlZHQvOkzL2whRF6UMdVRRFURRFURRFURRFURRF\nURRFUZQa8j/bVKmwXxjmUgAAAABJRU5ErkJggg==\n", "prompt_number": 75, "text": [ "\n", "0.5\u22c5J \u22c5\u27581/2,-1/2\u27e9\u27e81/2,-1/2\u2758 + 0.5\u22c5J \u22c5\u27581/2,1/2\u27e9\u27e81/2,1/2\u2758\n", " y y " ] } ], "prompt_number": 75 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Evaluate entropy of the density matrices" ] }, { "cell_type": "code", "collapsed": false, "input": [ "entropy(d2)" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\frac{1}{2} \\log{\\left (2 \\right )}$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAEUAAAAiCAYAAAAJfBTLAAAABHNCSVQICAgIfAhkiAAAA+RJREFU\naIHt2HuIVVUUx/GPw4w5TtmkZTVmNSkRJTlkRVmYGFH0+qOggoygwv8qIaI/sockTERlEVH0/Ksw\nKeglg1lCRlSERJAUNOlYGoWpPaSHPaY/1jncfe/cO9eZ5sytvF+43LP3WfucddZZ+7f2PjT5RxyC\n1kY78W/hACzAZhzbWFfGh5Z9sLkSB6G7YF/+kwxqZsr+SzMoVSi6mszFhZiGt/FawfebiL1J+2Jc\nJKb+BqzBT8PYo/hM2YOfcQvmFXyvI3Bd0l6JhbgPq7BUBObwxOZWEZhRMUFEe7QVaDvuHu3N94EW\nPKaU+YvwcoXNMeIlbUj6ZuOJaherx7V4GhvRKyI+Un4fxZiRcA3W44+sfSk6lFfLrfgQ89GZ9fVj\nG3oK9q8qA4rNlHXKp8HjIrNvrrB7Nus/O+mbgydTo0Yu2+cJEZyIL7AWX1fYtIgsOBNfYjUOxVXY\niXswBW3KBXMZPsCLFdebJYKyKenrF8Vg3BlQnim3iUo0NWvPwrs4I7FpwSvow3Qcj0+zcTOy4zZR\n4Vbtgw8zReBer3JuM9orOwcL+KUMKAXlPPwm3njKAuzCcVl7YXadxYnNA/hFbDtyFgnNq8dTIrtm\nVjm3XrwYlIR2QgG/WizDx/iuov8d8bBXZO08sNsTm+8xSSnD4IcaD5pyCS4TL+SrKuePVL5+GRcG\nRKZMwI94s4bdLiGaORvFVMt5A+9XjDkMW4a590lCs2qtk1qxW1KJx1toB8WDH1zlXKf4ZrMn6XtV\naElv9r9DKZNydohsaRdTK2U6nhElOhfXGaJ0f5u1u0WW/pUPakT1WSfSOV8Q5vQk53M6cL14i9W0\nKud5oUlrk752UYJvUF5tLheZmgdlIZ4b4TOMCQNKQjtbLJgWV9jcJcpomkWfKQnvcEwSAcnXKi14\nCfeK8p3/rhZZkdtNzcYNp4FVaceNWIElRrZXmIv7RWpuEXuNVqH0fXg0c3a5WFNMqRi/En+KKbVb\nBGmNEMxKenBTdrxc7ar4eTJmhaTq5NSLUAsewZ2ZUw/iVJyTOVuPbiF0KX3J2E6ciI8M1YNFYiO5\nWpTSNhyd2S8RwXyhYswMUa1OU77xS9mJ97LjmapXo2GZg0+EYEGXiPZZI73QKOjH6TXOPaT6ImxM\nqLch3CqE79esnW+42opyKGGT8hVuyhRDS/OYMVKBWSrEar7id75H4Q5Mxlv4Rmz/zxfZ22volBt3\nThD7k65xvm8HTsa5mQ+j/ig01nSJjzF5udzvv+1Ow+1KOnIKLmicO8VTT1MOFEK7V2k12SXK5bYC\n/Woo9Zb5k/FwRd+g/3FAmjRp0mSs+Rvvgds8ldAxMgAAAABJRU5ErkJggg==\n", "prompt_number": 76, "text": [ "\n", "log(2)\n", "\u2500\u2500\u2500\u2500\u2500\u2500\n", " 2 " ] } ], "prompt_number": 76 }, { "cell_type": "code", "collapsed": false, "input": [ "entropy(represent(d2))" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\frac{1}{2} \\log{\\left (2 \\right )}$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAEUAAAAiCAYAAAAJfBTLAAAABHNCSVQICAgIfAhkiAAAA+RJREFU\naIHt2HuIVVUUx/GPw4w5TtmkZTVmNSkRJTlkRVmYGFH0+qOggoygwv8qIaI/sockTERlEVH0/Ksw\nKeglg1lCRlSERJAUNOlYGoWpPaSHPaY/1jncfe/cO9eZ5sytvF+43LP3WfucddZZ+7f2PjT5RxyC\n1kY78W/hACzAZhzbWFfGh5Z9sLkSB6G7YF/+kwxqZsr+SzMoVSi6mszFhZiGt/FawfebiL1J+2Jc\nJKb+BqzBT8PYo/hM2YOfcQvmFXyvI3Bd0l6JhbgPq7BUBObwxOZWEZhRMUFEe7QVaDvuHu3N94EW\nPKaU+YvwcoXNMeIlbUj6ZuOJaherx7V4GhvRKyI+Un4fxZiRcA3W44+sfSk6lFfLrfgQ89GZ9fVj\nG3oK9q8qA4rNlHXKp8HjIrNvrrB7Nus/O+mbgydTo0Yu2+cJEZyIL7AWX1fYtIgsOBNfYjUOxVXY\niXswBW3KBXMZPsCLFdebJYKyKenrF8Vg3BlQnim3iUo0NWvPwrs4I7FpwSvow3Qcj0+zcTOy4zZR\n4Vbtgw8zReBer3JuM9orOwcL+KUMKAXlPPwm3njKAuzCcVl7YXadxYnNA/hFbDtyFgnNq8dTIrtm\nVjm3XrwYlIR2QgG/WizDx/iuov8d8bBXZO08sNsTm+8xSSnD4IcaD5pyCS4TL+SrKuePVL5+GRcG\nRKZMwI94s4bdLiGaORvFVMt5A+9XjDkMW4a590lCs2qtk1qxW1KJx1toB8WDH1zlXKf4ZrMn6XtV\naElv9r9DKZNydohsaRdTK2U6nhElOhfXGaJ0f5u1u0WW/pUPakT1WSfSOV8Q5vQk53M6cL14i9W0\nKud5oUlrk752UYJvUF5tLheZmgdlIZ4b4TOMCQNKQjtbLJgWV9jcJcpomkWfKQnvcEwSAcnXKi14\nCfeK8p3/rhZZkdtNzcYNp4FVaceNWIElRrZXmIv7RWpuEXuNVqH0fXg0c3a5WFNMqRi/En+KKbVb\nBGmNEMxKenBTdrxc7ar4eTJmhaTq5NSLUAsewZ2ZUw/iVJyTOVuPbiF0KX3J2E6ciI8M1YNFYiO5\nWpTSNhyd2S8RwXyhYswMUa1OU77xS9mJ97LjmapXo2GZg0+EYEGXiPZZI73QKOjH6TXOPaT6ImxM\nqLch3CqE79esnW+42opyKGGT8hVuyhRDS/OYMVKBWSrEar7id75H4Q5Mxlv4Rmz/zxfZ22volBt3\nThD7k65xvm8HTsa5mQ+j/ig01nSJjzF5udzvv+1Ow+1KOnIKLmicO8VTT1MOFEK7V2k12SXK5bYC\n/Woo9Zb5k/FwRd+g/3FAmjRp0mSs+Rvvgds8ldAxMgAAAABJRU5ErkJggg==\n", "prompt_number": 77, "text": [ "\n", "log(2)\n", "\u2500\u2500\u2500\u2500\u2500\u2500\n", " 2 " ] } ], "prompt_number": 77 }, { "cell_type": "code", "collapsed": false, "input": [ "entropy(represent(d2,format=\"numpy\"))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 78, "text": [ "(0.69314718056-0j)" ] } ], "prompt_number": 78 }, { "cell_type": "code", "collapsed": false, "input": [ "entropy(represent(d2,format=\"scipy.sparse\"))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 79, "text": [ "(0.69314718056-0j)" ] } ], "prompt_number": 79 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Density operators with Tensor Products as args" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from sympy.core.trace import Tr\n", "\n", "A, B, C, D = symbols('A B C D',commutative=False)\n", "\n", "t1 = TensorProduct(A,B,C)\n", "\n", "d = Density([t1, 1.0])\n", "d.doit()\n", "\n", "t2 = TensorProduct(A,B)\n", "t3 = TensorProduct(C,D)\n", "\n", "d = Density([t2, 0.5], [t3, 0.5])\n", "d.doit() " ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$0.5 \\left({A A^{\\dagger}}\\right)\\otimes \\left({B B^{\\dagger}}\\right) + 0.5 \\left({C C^{\\dagger}}\\right)\\otimes \\left({D D^{\\dagger}}\\right)$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAUkAAAAaCAYAAAA6565FAAAABHNCSVQICAgIfAhkiAAACTZJREFU\neJztnHuMHVUdxz93i1sW21Ity2tbsCVaRFo1UF6pbW1BNDwkRFAJ0Rof0CogFDSkFZcVC20o4aFE\nUR4tAeVl1NS3KS0qCCrFEnkoyNqGVwElYK0PyvrHdyZ37uw5M2dmzrCz4XySm/bOOb8z537nN2d+\n53fOLAQCgUCgkfQDE0e6E8AY4KGcOrsBF7wGfTHRT9CpSfQzstej6dehn2b4a5JKmnV5744bK4F1\nwIsjdP4kO4CXcuo8H9U5vf7udBB0ahZNuB5Nvg5N0MdEkzUzMg+4bKQ7kWKFQ50xwH3A7Jr7EjOP\noFOTmEdzrkcTr8M8mqOPiSZqZqQF3A3sM5KdSPAT4EZgDbAW+FpO/VnA9+ruFEGnptGU69HU69AU\nfUzUplkPGlVd6bEc3zn1/TDgZwXaTTOhJtsLC7TzADClQj9ccNGpG5ic8RlXg23TdEpS1GdjGxM+\n/XYCGkRczutq17Tr4KLPROz+tofjeaq0UVqznVKFM1HIfC+wHdgXuAj4W06jP0I33s2R7XHAR4EB\n4KZEvWOAewp0NsnxwBXA1BpsDyjQ1uPAJ9BvqwsXnWYAS4Aj0HW6G3g4KmsB84H/oET6dz3ZNk0n\nKO+zUI/fvgFYCMwB3gY8CfQCG4HzgWnAecDHKtg17Tq46HMK8F7gBDTu3Aq8HJX1ooWT+4FL0G/3\n3YYXzcYCTwMnJ44tQXP07pxG1wNDic8rwGmGer8Bji3Q2ZhdgEHg3zXZFhm4LwZuK9GPIhTRaQ36\nbenorwu4A3gVONqTbd06zQD2KlC/is+Cf7+diaKQ7wAH07kw+h7g51F/P1/RbrT66xi0OLLOUNYD\n3IB0yJoFlW3Di2anA9uQ48VMQc5zZk6j69FT7nLgU9gjtkHgUOeutlkObI368qYabH9boL2F6CZ0\n4UNI8FUo2lkJLGb4dC7NIO46DQIbMs4/hKJoH7Z16RRzLsUeolV8Fvz67Zko+l7B8GlyzFejvs2o\naDda/fVg9Du+ZCnvAbYA19fQhhfN/oySnGkeAn6c0+h6x5NvA6Y71o2ZjsLq1Uict9dgW0TAw4Fn\nc+q8FbgGTfPTjv9u4FtoSmvDVad4QOi3lK+Myj/tyda3TmmKDpJVfBb8+e2RKOrOy3sdCTzjwW60\n+usS5FNzM+qspvO3+mqjtGZxWN+NhBo0GGym2JL4+IyyoZxyE6uAL9Lu9J412A4VaHMnsveX7gFc\njW7UHxra3ghchwYn24V21Sm2X28omwacipzjJkN5GVufOlXFp89Ceb+dhG7Kp1EUlsUW2tPEsnZx\nf1xpkr/OQVHzvRl1no36ZIvqy7ZRWrP4P/Hgsc1gsA0JkBdyfw5NGc4CbgFOMtTZSrFI8iPAH4An\naD8ZXAfJIrb/LdCn6eh3mGihgfmUqM55hjoLUOJ5LnAi5hSAq06xwySfkuOj9jcA3wfeD/zLk60v\nnXzgw2ehut9eAuyNpsR5OfOtwKUV7WB0+msL5VfvI/v37hv9O9ZQVqWN0prFq9vx8rnpZoqPTcQe\nBm8GfgX8Mfo+FXgQeAoldWO24D5Ijkf5mgXR9yKRZFHbpxz7BOr/ZkvZO5AGz6EV0w+iFbL4dafj\ngYNQdAuKJD4MfCPVjqtOc9AqXn/i2DQ0Rboa3QA25yhj60snH1T1WfDjt0ejG/bb+V3mH9Gnih2M\nTn+dgQbYu3Lq7YdWrB/13EZpzeJBMn5l51WDQbxKmDUSp7czPIGikW8CByaOr8M9pziAbtTt0fci\ng2RR2wcd+wQS8JeWsll0rqL9AEU1q9DWhMnAlxPl96NpbRoXnXaP+nIhw/OKuwK/R1HRbIYPJGVt\nfenUwrynsSv6pLemgVaek1T1Wajut/ug3O5Gh3P5sIsZjf4aT9WzBrjdgXcBv8Y8Pa7SRmXNxqCb\nod9gsBZNy7IwhcY3IAdOhuezkGPkMZPhCfkD0Y9eXaOtC49id4gBzFtYVqELa7r5rzEcc9EpXn1e\nYClfGpWnt5tUtXUlS6dlaCBOf7YAf7GUnZxqo6rPQnW/PQHptMbhXLvQXp0ua1eGpvjrbcD/yN7e\nsxjpsrjGNlzo0CwWYQdK3O5mMJhA9pRlBbAIbYBN1huHIoYptKcKv0Nh8OHY9y21gCuRGD9NHI8j\nj6xIsoqtC/PRRtOHLeWbgD6UjI85B/gTejXq68AZtKOHLvRifRoXneag6Cpv1a7Ps60LeTpdFH3S\nnAs8gga5PKr4LPjx20eif19w6O8itHm9il1RmuavG4F/Wsq7gE+iB2J6Ou+zjTwyNbsOrW4l6Y46\ndHvq+KFohzvoybsJTdOSPIacLD2tOgzlQUxPKdBO9+WWsu2080e+bfMYi5z7nRl1JqNEfMwyOhcC\npgPXougA4AMox2MiT6cHyF7h24CeqId4ts3DRScbRbcAlfVZ8Oe3z9CZvzQxAW2p8mHnSpP8dX/k\nT5caymLORg/ugyzlPtrII1ezM9DTIPmO89yoY7MSx2ZHx+K8wEI0fUiyK5qy3Gg51zLayeAkk1B+\nw/a2xGPY93xVsXVhAN3EeVyAEsfL0epwmregja69wFVkv29s02kiiqRsDnNSVG4aCKvYuuCqk4mi\ng2RZnwV/frsMTe1tN2YfGpD39mTnSpP89TSk/3EWu6PQvZlOqfhuI49czXpQ2PwV2htKb6dz2gpK\njN6FtkyA3jldTadTXhzVeWPG+ZbS+cc5p6DE8Gcs9btQBLSD4QNhFVsXejFffBPdKOn78Yw6+6Mn\nlst76GmdQNubhtCWjCS9wGfR9OhmzPpXsc2jiE4mig6SZX0W/Pkt0fn/jlaD43xZH1oYuhx7Dq2s\nXR5N89e1DM/ztlDe70qUh87Lu/poIwurZund9XshR3pz9P1FlBg3bbNIMg7lMqaiH7IJTYVezjJK\ncD16q6CFnq5L6fzDCovRk2RS9P05dCOcX9G2LnYGvoAG51vRBXwF6fM+tM1mAPuL/DYWoUGuLzrH\nS7T3c7XQdbgH+AVaqfRl+1pRJCcZU9ZnobrfJjkK5bOOQBHN48Cd6L3rOux8Upe/3oIG2PjaDNLe\n19qDpud3Ip+z5Rl9tFEJ27uiAT9MRZtfD0CRy19R5FP0nebXC6eiQaLsX4oKVCP4ayAQCAQCgUAg\nEAgEAoFAIBAIBAKBQKC5/B+xC2qm0KevJwAAAABJRU5ErkJggg==\n", "prompt_number": 80, "text": [ "\n", " \u239b \u2020\u239e \u239b \u2020\u239e \u239b \u2020\u239e \u239b \u2020\u239e\n", "0.5\u22c5\u239dA\u22c5A \u23a0\u2a02 \u239dB\u22c5B \u23a0 + 0.5\u22c5\u239dC\u22c5C \u23a0\u2a02 \u239dD\u22c5D \u23a0" ] } ], "prompt_number": 80 }, { "cell_type": "code", "collapsed": false, "input": [ "#mixed states\n", "d = Density([t2+t3, 1.0])\n", "d.doit() " ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$1.0 \\left({A A^{\\dagger}}\\right)\\otimes \\left({B B^{\\dagger}}\\right) + 1.0 \\left({A C^{\\dagger}}\\right)\\otimes \\left({B D^{\\dagger}}\\right) + 1.0 \\left({C A^{\\dagger}}\\right)\\otimes \\left({D B^{\\dagger}}\\right) + 1.0 \\left({C C^{\\dagger}}\\right)\\otimes \\left({D D^{\\dagger}}\\right)$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAqYAAAAaCAYAAABl7J64AAAABHNCSVQICAgIfAhkiAAAC35JREFU\neJztnXusHUUZwH+3LW0ttsWUPrQUbQleKLQGpYoEb28ALVpTH7FqkKCYiBafUbAhbfByFdTGGgVs\nGl9AVTQ1PjD1rZVKFAWkFRVErUKLlpdF6gPFQv3j283du3d2d2Z29ux49/slJzdnd2fOnG9++92z\nu7OzoCiKoiiKoigdZwg4ou1GABOBOyq2ORK4pAdtMTGExikmhmi/P2LviyHaj1GWqnipt70j5r4Y\nIo4YpcS+n/eKIdrtl9j7YYi4vIWaMZsQvDl2bAC2A39r6fOzPA4cqNjmoWSbtzTfnFFonOIilv6I\nuS9iiVGWqnipt70j1r6IKUYpMe/nvSKGfom5H2KIj4mYY2ZkEPho243I8WGLbSYCNwOnNdyWlEE0\nTjExSFz9EWNfDBJXjLJUxUu97R2x9cUg8cUoJcb9vFcMEk+/xNgPg8QTHxMxxsxIH/BT4Og2G5Hh\n28DngC3ANuCqiu2XAV9tulFonGIjpv6ItS9iilEWl3ipt80SY1/EFqOUWPfzXhFLv8TaD7HEx0Sj\nMZvu0aCJwJNK1p8CfNej3pQZDZW91KGeXcCCGu2wwSZOk4GjSl5PbqBsbHHK4+MsxOvtDCQBZUnb\nGVtfNO3s9JJyT7Non2282vAW4su3Ze7Zli8ipr6wiVFd90Bzbp4Y3R1v+TaEt3Xq8I7ZpIKNTgBu\nAGZbVjoL2Aw8COwBFgHXJXVkWQncZN3U0awCPg4sbKDsYoe6dgPnAcMe7bDFJk5LgPcApwJPR46e\n7kzW9QGnA/9BBkZ/KVDZ2OKUxdVZiM/bw4A3AAPAM4E/I99nJ3Bx0r6LgHOJry+adnYl8ArgJcg/\n8W3A/cm6pwBPRQbbfwT4reGzbePVa28hjnzr4l4VofJtLN7WdQ8056bE5O54z7chvK1TR7CYLQLe\nCNwHHHKo9GvApsz75wAPMzYx/QR4qUO9KdOAu4F/N1TW5UfHB4Eve7TDBZc4bUG+W/6IewLwFeAJ\nYEWgsk3HaQkiugu+zkJc3i5Fjhq/CJzM6BsTXwB8D9gHvCtZ1lVnfwfcZVg+CRnTdDcw17DeNl6+\nseqlu6G9dXWvjJD5NjZvfd3Lojk3Dne7lG9DeOtTh3fMsp1xPLAWmAr8yqHCxcDLkA5O+QWwH9iY\n23Y+cqTkynok4U1BfqWHLps/fV/GXcgRrw2vQgK+ERmkvAG4AIlxGS5xGgB+Dvwjt/wJpE/6kKOd\nEGWbilPKCiRR2eLrLMTl7TuAW5BLM2cDtyJ9kHJjsn4e8MNkWRednQccy9izKwAHkbMc+4HPG9bb\nxsvHW+idu6G99XGvjJD51rUvfNy19baOe1k057bvbpfybQhvfesIHrNrsD8S+iRyy39+WMAm4JHc\n8n8C/Zb1pvQDW4FrkzYd30DZnznU+XxGTmUXcSwSl1WM7ZyTgE8hl26KsI3TAuR7DRWs35Csf1Og\nsqHjlOdC/M5MgpuzEI+3ZyJJsWo8zpnIGYqULjr7mmTd60rqvwz4L2PHlNnGy8db6J27Ib31da+I\n0PnWti/quGvrbR33UjTntu9u1/JtCG996/COWYh5TE9ExmYczC3fgwwmXppZdgj3wdIbkSO0tNHz\nGijrsnNNojxuc5Ed7Q7gG4a6dwKfRZLT8pL22MQpLX+DYd0i4BxEji8EKhsyTm0Tg7ezkH/i+5Cj\n5jL2IvPVZdtky3hxdiD5u6Ok/vuR73uyoX02dMXbOu4VETrf2vRFXXdtva3jXorm3Hbd7WK+DeGt\nbx3eMQsh8Tzk13uedNmczLIHcDvz9FrkVP+fGDl6sf1h6lL2MYc29SPfw0QfkpzPTra5yLDNGcDL\nEeFeiflyl22cBpAB89kjk+lJ/TuArwNnAf8KVDZUnGIgBm8/hNzZeBnV41AfQAaYp3TR2eVITO8t\nqT+9HDQlt9w2Xl3xto57JprIt1V9EcJdW2/ruJeiObddd7uYb0N461uHd8yK7sp3YS7mBqc7V/ZR\nWXux/wc/HRkLckby3uWMqWvZv1i2CaT9ewrWnQD8Ehn7cR0ynmaYkcdurULG86xN3l+LnCbfnKvH\nNk4DyBHoUGbZIuRSwCZkByiSw6dsqDjFQAzerkAS5Kct6n04eaV0zdlZyBi1LRX1H5P8vTW33DZe\nXfG2jnt5msq3VX0Rwl0bb+u6l6I5t113u5ZvQ3hbpw7vmIX4YXqA0QOHUyYnf7M72nbsx4gOIzvq\no8l7lx+mrmVdBnH3Az8oWLeM0XeiXY8cDW4EbkPm/npfZv1tyOWbPDZxmpO05VLGjlmaiQiyGnmi\nQv7sk2/ZUHHqQ+axyzMheZm8zF/+qUvb3h6NjDnbiduRZUrXnB1AvPlxSf2Tkaeh7GbsI/ps41UW\nK2jf3RDe1nUvT1P5tqovQrhr421d90BzLrTrbhfzbQhv69ThHbMQl/KLPvzw5G92APG3sDurshQ4\nDpk+I8X2Ur5P2cst2pTSj3wPEwuRzsmSBvvNjL3zEMwTK9vEKR33caNh3SPIwPSTgPMDlg0Vp3XI\n5az86+1IjEzrXu3w2Ta07e2zk7+/tqh3GjKtS5auOluWIM9C/slvNayzjVdZrKB9d0N4W9e9LE3m\n26q+COGui7e+7mXr0Jw7ll642+V8G8Jbnzq8YxbijOntSMPypE/+2JdZdgvwd+QOrKI5rvqAK5A7\nvL6TWZ4e7ZX9MK1T1obTEanuLFh/OzKNQ/Y7vxv4DfKIrk8giSA9YpsAPGSoxyZOA8gRbdWdb/MD\nl7WhKk4fSF55LkQm6d3m+bkutO1tOhnxXy3augb4psV2JsaLs8uRS0N/KCl3PnAPZrdsqIoVtO9u\nCG9DuddkvrXpixDu2ngbwj3Nue2628V8G8LbWPIuUD0NxPMYedrDucgp+jzfR4J/WG75KcgYi6If\nxedR/Ev70aRsEXXKVjEFkftZJdschQysTlmPXJ5J6Qc+gxyRAbwYGT9ioipOu5D58IrYgfThcwOX\nrcImTkU0OXVJ1lmIw9v7kImSy5iBTCPjw3hxdibwOGOfBpVldVLW15863kLv3A3lbQj3msq3tn0R\nyt0yb0O5pzm3fXe7lG9DeBtd3r2GYuFOS9alp3aXJO+zc25NRRKT6Y4zkM5Ya1g+Cxk7MdmwDuRX\ne9H8YHXK2jCM7MRVXIIMBL4c89HhM4CrkR32SszjflKK4nQEIkzRnbKrk/WmRFinrA22cTLRVJLM\nOwtxeLseuUu3aILr+cj3sn2ucZ7x4uxKpK/WFJRdhtxU8daSdlVRx1vonbuhvK3rXpP51qUvQrlb\n5G0I9zTnCm2726V8G8LbqPLuJGTMwyFkEtg8cxDZ3plZdhXyCK90rMjbkLvOyubZWsfou/AWIAN9\nTePLQE5p70J24HwyrFPWhtmYO9/EZCR+ry/Z5jjkKMHm+en5OIFMzXIImUoiy2xEkseQO/4OZyx1\nylbhEicTvknSx1lo31uA9yNPzVjFyFii+cjZhY9hHl9kw3hydnNS9sTc8mOQG0n2Io8R9KWut9Bb\nd0N4C/7uNZlvXfsipLumGIVwT3PuCG2725V8G8Lb1vJu9okDc5Bnlc5hRJKDyNxSV1D+yKpJyDNl\nlyB3Zc1E7iy7x7KBVyNPWuhDjmjWMfr08QXIwOBZyfsHkTFNF9cs2xRTgfciCXor8HsklguBFyGD\n3IeRqUNcWIMkufnJZxxgZO6vPmSnugm5NHJ9wLK9wnW8Ux1noV1vs7wQOYtwKnKWaTfwIySB94oY\nnb0SOfNyZLLtvYzc9TkNcWU7Eqf9ju0KTS/drettFlf3upJvQ7inOXcsbbpbt1xImsq3IbxtPe+6\nPMtUcWchckSxGBk/80dk0PLNbTYqYs5BkkTRYG6ledRZP9Td9lF33VFv20WdVRRFURRFURRFURRF\nURRFURRFURRFURRFURRFURRFURRFURRFURRF+X/hf2jkcJDynyQoAAAAAElFTkSuQmCC\n", "prompt_number": 81, "text": [ "\n", " \u239b \u2020\u239e \u239b \u2020\u239e \u239b \u2020\u239e \u239b \u2020\u239e \u239b \u2020\u239e \u239b \u2020\u239e \u239b \u2020\u239e \u239b \n", "1.0\u22c5\u239dA\u22c5A \u23a0\u2a02 \u239dB\u22c5B \u23a0 + 1.0\u22c5\u239dA\u22c5C \u23a0\u2a02 \u239dB\u22c5D \u23a0 + 1.0\u22c5\u239dC\u22c5A \u23a0\u2a02 \u239dD\u22c5B \u23a0 + 1.0\u22c5\u239dC\u22c5C \u23a0\u2a02 \u239dD\u22c5\n", "\n", " \u2020\u239e\n", "D \u23a0" ] } ], "prompt_number": 81 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Trace operators on Density Operators with Spin States" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from sympy.physics.quantum.density import Density\n", "from sympy.physics.quantum.spin import (\n", " Jx, Jy, Jz, Jplus, Jminus, J2,\n", " JxBra, JyBra, JzBra,\n", " JxKet, JyKet, JzKet,\n", ")\n", "from sympy.core.trace import Tr\n", "\n", "d = Density([JzKet(1,1),0.5],[JzKet(1,-1),0.5]);\n", "t = Tr(d); \n", "\n", "display_pretty(t)\n", "print t.doit()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "display_data", "text": [ "Tr(\u03c1((\u27581,1\u27e9, 0.5),(\u27581,-1\u27e9, 0.5)))" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "1.00000000000000" ] } ], "prompt_number": 82 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Partial Trace on Density Operators with Mixed State" ] }, { "cell_type": "code", "collapsed": false, "input": [ "#Partial trace on mixed state\n", "from sympy.core.trace import Tr\n", "\n", "A, B, C, D = symbols('A B C D',commutative=False)\n", "\n", "t1 = TensorProduct(A,B,C)\n", "\n", "d = Density([t1, 1.0])\n", "d.doit()\n", "\n", "t2 = TensorProduct(A,B)\n", "t3 = TensorProduct(C,D)\n", "\n", "d = Density([t2, 0.5], [t3, 0.5])\n", "\n", "\n", "tr = Tr(d,[1])\n", "tr.doit()" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$0.5 A A^{\\dagger} \\mbox{Tr}\\left(B B^{\\dagger}\\right) + 0.5 C C^{\\dagger} \\mbox{Tr}\\left(D D^{\\dagger}\\right)$$" ], "output_type": "pyout", "prompt_number": 83, "text": [ "\n", " \u2020 \u2020 \u2020 \u2020 \n", "0.5\u22c5A\u22c5A \u22c5Tr(B\u22c5B ) + 0.5\u22c5C\u22c5C \u22c5Tr(D\u22c5D )" ] } ], "prompt_number": 83 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Partial Trace on Density Operators with Spin states" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from sympy.physics.quantum.density import Density\n", "from sympy.physics.quantum.spin import (\n", " Jx, Jy, Jz, Jplus, Jminus, J2,\n", " JxBra, JyBra, JzBra,\n", " JxKet, JyKet, JzKet,\n", ")\n", "from sympy.core.trace import Tr\n", "\n", "tp1 = TensorProduct(JzKet(1,1), JzKet(1,-1))\n", "\n", "#trace out 0 index\n", "d = Density([tp1,1]);\n", "t = Tr(d,[0]); \n", "\n", "display_pretty(t)\n", "display_pretty(t.doit())\n", "\n", "#trace out 1 index\n", "t = Tr(d,[1])\n", "display_pretty(t)\n", "t.doit()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "display_data", "text": [ "Tr((\u27581,1\u27e9\u2a02 \u27581,-1\u27e9, 1))" ] }, { "output_type": "display_data", "text": [ "\u27581,-1\u27e9\u27e81,-1\u2758" ] }, { "output_type": "display_data", "text": [ "Tr((\u27581,1\u27e9\u2a02 \u27581,-1\u27e9, 1))" ] }, { "latex": [ "$${\\left|1,1\\right\\rangle }{\\left\\langle 1,1\\right|}$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAEwAAAAXCAYAAACh3qkfAAAABHNCSVQICAgIfAhkiAAAAedJREFU\nWIXt2D+I1EAUx/GPB+ohCqIiKqKNFjanveXaKNhaeI2Kglpb2Z2FnYKVgoqNeCDYXecfUgoWgoJy\nCGJhIQhiYWexFtksa8hO/kzYm2K/TSbz8t77zeMxyYQ5UbzYaAGJMq7LQsmwu+Lh/VjHlo7JQv63\nsKdhnHsdc9TRxLeqLiCbGG/DKXzCEIsthTTxX8LNBrFO4EbHHDH6CrJiUO6wgiNYxQA/W4ho6/8B\nx7C5Jt4VPOpRY+z6xmQVcw9067Cm/mexHPDfhzuROWJ9s2IwrcNmyRrOBOxX5YtKghQKNsRbnKyw\nLeIQvsxUUYAUCgZPcKFifhnPZislTCoF+4NfOFyaH+DV7OVMJ5WCwX1cm7gf4M0GaZlKSgUbYtPE\nfUraxqQk6rq8ywpeyrssKfoo2IEeYmyXH5G+leZfy7/GY+lDI5oVbNfourfCdhzf8bijf8FF+Zuy\nzFOcrxNYk6NOYxN9U8lG152j8bp8bxniN97h0sTzB/EVH/2//zT1N/JbDWhawdGK+RiNbfRRfQIK\nG2pY6ehHs6PR3Yj4BTEas2LQ16YfE+ccngfsP7BV3hUx9LLWcpC/HWKcxvuO+ZfwuUHeh7jcMQdx\nGgno29Ey0AJuRwjp6wdiiFiNtK/LnDlzZsM/36lpQdemeFcAAAAASUVORK5CYII=\n", "prompt_number": 84, "text": [ "\u27581,1\u27e9\u27e81,1\u2758" ] } ], "prompt_number": 84 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Examples of qapply() on Density matrices with spin states" ] }, { "cell_type": "code", "collapsed": false, "input": [ "psi = Ket('psi')\n", "phi = Ket('phi')\n", "\n", "u = UnitaryOperator()\n", "d = Density((psi,0.5),(phi,0.5)); d\n", "\n", "display_pretty(qapply(u*d))\n", " \n", "# another example\n", "up = JzKet(S(1)/2, S(1)/2)\n", "down = JzKet(S(1)/2, -S(1)/2)\n", "d = Density((up,0.5),(down,0.5))\n", "\n", "uMat = Matrix([[0,1],[1,0]])\n", "display_pretty(qapply(uMat*d))\n", "\n", "\n" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "display_data", "text": [ "\u03c1((O\u22c5\u2758\u03c8\u27e9, 0.5),(O\u22c5\u2758\u03c6\u27e9, 0.5))" ] }, { "output_type": "display_data", "text": [ "\n", "\u23a1 0 \u03c1((\u27581/2,1/2\u27e9, 0.5),(\u27581/2,-1/2\u27e9, 0.5))\u23a4\n", "\u23a2 \u23a5\n", "\u23a3\u03c1((\u27581/2,1/2\u27e9, 0.5),(\u27581/2,-1/2\u27e9, 0.5)) 0 \u23a6" ] } ], "prompt_number": 85 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example of qapply() on Density Matrices with qubits" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from sympy.physics.quantum.gate import UGate\n", "from sympy.physics.quantum.qubit import Qubit\n", "\n", "uMat = UGate((0,), Matrix([[0,1],[1,0]]))\n", "d = Density([Qubit('0'),0.5],[Qubit('1'), 0.5])\n", "\n", "display_pretty(d)\n", "\n", "#after applying Not gate\n", "display_pretty(qapply(uMat*d) )" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "display_data", "text": [ "\u03c1((\u27580\u27e9, 0.5),(\u27581\u27e9, 0.5))" ] }, { "output_type": "display_data", "text": [ "\u03c1((\u27581\u27e9, 0.5),(\u27580\u27e9, 0.5))" ] } ], "prompt_number": 90 }, { "cell_type": "code", "collapsed": true, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 86 } ], "metadata": {} } ] }sympy-0.7.4.1/examples/notebooks/spin.ipynb0000644000175000017500000044002612253362407021107 0ustar georgeskgeorgesk{ "metadata": { "name": "spin" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Quantum Angular Momentum Module\n", "\n", "This file will show how to use the various objects and methods in the `sympy.physics.quantum.spin` module, with some examples. Much of the work in this module is based off Varschalovich \"Quantum Theory of Angular Momentum\"." ] }, { "cell_type": "code", "collapsed": false, "input": [ "from sympy.physics.quantum.spin import Jminus, Jx, Jz, J2, J2Op, JzKet, JzKetCoupled, Rotation, WignerD, couple, uncouple\n", "from sympy.physics.quantum import Dagger, hbar, qapply, represent, TensorProduct\n", "from sympy import factor, pi, S, Sum, symbols" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 1 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Basic spin states and operators\n", "\n", "We can define simple spin states and operators and manipulate them with standard quantum machinery." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Define a spin ket:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "jz = JzKet(1,1); jz" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$${\\left|1,1\\right\\rangle }$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAACcAAAAXCAYAAACI2VaYAAAABHNCSVQICAgIfAhkiAAAATlJREFU\nSInt1i1LREEYxfGfy4KyoEUwaDSIZl+SwU+gWAwiBtNi2o/gFzBYBEEQzQYFg8mtYhExWMRosClY\nRNawXrhc7q6zs4texZNmzsx55s8DMwy/SMc/DZBWKTMfbrGvgpsuzmmX30E5pEg9x5vBFRpRWF/n\nl7GSt5DtXFqTOMMm3iOgQvMnWAwpWG/hH4jvXEi+htms2a5z36l9bGTNosA94wVjabMocLCLatoo\nEtw9RjWfHRQLDg6xlkyKBgd9yaBocOua3UPv4CYw0GWNcTziNTFC4ZKDKzlrC7jDUWQ+UVXzxrZU\nPTUewTluNV/3Bp5wgdXUvqlP/yFTKzQPQ9hrB5aF61RbXWRrmMuavbwQ/ZG5EqZxmbeQ1lvkAfO4\njswu4TRk42BE8TK2pd6nDhX82fzXn9AHkLc0qQpHOW4AAAAASUVORK5CYII=\n", "prompt_number": 2, "text": [ "\u27581,1\u27e9" ] } ], "prompt_number": 2 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Find the vector representation of the state:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "represent(jz)" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\left[\\begin{smallmatrix}1\\\\0\\\\0\\end{smallmatrix}\\right]$$" ], "output_type": "pyout", "prompt_number": 3, "text": [ "\n", "\u23a11\u23a4\n", "\u23a2 \u23a5\n", "\u23a20\u23a5\n", "\u23a2 \u23a5\n", "\u23a30\u23a6" ] } ], "prompt_number": 3 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Create and evaluate an innerproduct of a bra and a ket:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "ip = Dagger(jz) * jz; ip" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\left\\langle 1,1 \\right. {\\left|1,1\\right\\rangle }$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAEgAAAAXCAYAAACoNQllAAAABHNCSVQICAgIfAhkiAAAActJREFU\nWIXt2D1rFFEUxvGfMRCJqIUgmJQRRDvxJZWg3yCxSaHBQi0sLPwISa2CNgElEJLKQjHprNxWElDE\nwsJgaWGXQJoQYnFdmYyzs3Pnxt0N2X81c84+9zmc3bv3zNDnQPGm2wXkGSjJPW8RH8aXmn7ttKcT\ntCneLzAYs9gNPCiIX8UqdmMWi9A2ErQp3rcwVZRo1bU7eJS5v4An+IWdyOK6pY3RL2MJr6ssOobZ\nkvyC+t9kO23jP/lW0T/GtXyw6D/oIeYSCjmozONePphv0Ckcx89OVNRjbGATo9lgvkH3hU4eVuaE\nHfSXbIOO4hLWOllRj7GOEWEkwN4GTeBdpyvqQRYx3bzJNqhsaDxsHGleZJvyFpOdr6XnuCv8irC3\nQTv4hMuJBudxLHGNbvmOCSf4VjOQ31avhJOsjGYRwwW5m/gmTKWx2nak+Fb1rjQDPsXZXOwM3uOr\nMI3uCuP7B9zOfO7in/iPGlr+naRTfGO9T+KlCpxT/qhRhZmaukaXfAmPGuP5YNHJ9V2YJlP281CC\nNoW6vgO4go9FiSKWZGaBSK7jc03tdk1dqu8EVmJFrV6YlTGIZzJzRCQnaupSfaNfmPXp02df+A0V\naWV8f9YGUgAAAABJRU5ErkJggg==\n", "prompt_number": 4, "text": [ "\u27e81,1\u27581,1\u27e9" ] } ], "prompt_number": 4 }, { "cell_type": "code", "collapsed": false, "input": [ "ip.doit()" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$1$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAAsAAAASCAYAAACNdSR1AAAABHNCSVQICAgIfAhkiAAAAF5JREFU\nKJFjYKAC4GJgYLhEjEJTBgaG0wwMDP/RJViQ2JoMDAw9DAwMrxkYGP6S4owF2ExmIsWEUcVUV8wB\npblwaRRjYGDYycDAcIUBEnv/GSBRv5+BgSGaFBfQGAAA/84M5lOscPUAAAAASUVORK5CYII=\n", "prompt_number": 5, "text": [ "1" ] } ], "prompt_number": 5 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Apply an angular momentum operator to the state:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "Jz * jz" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$J_z {\\left|1,1\\right\\rangle }$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAADsAAAAZCAYAAACPQVaOAAAABHNCSVQICAgIfAhkiAAAAgtJREFU\nWIXt10+ITWEYx/HPvaZoQqEm+bOQxEgiGVJkVjaKbCyYLJTybzFZWKtZWGChpJSSIiWFskCZu2DB\nSEh2srTAZsTCMGPxntuce7t/zrxnzkzqfut0ep/n/T3v85y398+hw7Rxb7YTSFNu49+Mp3iJcUzg\nHY5ljL+kha8b7zPGmar+MrpiA5fwHW+mqKs0sW/FiPDxYminP4CD9cZ2M1tlAxbjSVRqk/TiEU7i\nb4H6B9gXER+cFr7ininqKi18N8TPbBb9IPrShqwz248/eB6V1uxwHUfThizFlrALr/GzgKSKYhQ/\nsLxqyFLsRmFXHS4oqSK5iuPVRpZi+5N3pYhsCuYTlgnHVKZid2MML4rLqVBuYoD2xZaF9Tri/1qv\n9ZSoLXY7vuFMyrYJi/Bs5vKado4Is1tT7GFhIxpN2QaEw/v2jKU2yVrMyxljNb7gV73jFM6m2nvx\nO7HHUmnhuyNcCrob+PoT391IfZULWNnIMQdDSYcruC+s12YM4St2YKmwDOqp1LV78BgfkkQnkhjD\nOJTqtz6xf47Uw0Jca5F/ZlYJl+35wi2lr0m/Ss5xzuXQDmJbzvFrKGu9o1dyxj8fqSvjViNjLKXk\nGccKzG3QZyxH/J14G6ndj4c5xq5hC04IO2YPLjbptyAyfhcuSc7HCHL9vKfpxTphvb7CR6yZjsAd\nOnTokOYfv15fmYxfZs8AAAAASUVORK5CYII=\n", "prompt_number": 6, "text": [ "\n", "J \u22c5\u27581,1\u27e9\n", " z " ] } ], "prompt_number": 6 }, { "cell_type": "code", "collapsed": false, "input": [ "qapply(Jz * jz)" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\hbar {\\left|1,1\\right\\rangle }$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAADEAAAAXCAYAAACiaac3AAAABHNCSVQICAgIfAhkiAAAAhpJREFU\nWIXt1ztoFUEUxvFfQiR6xYAKikZQoiKKgoUPBA0It1axsVCxsEqZxlZSWSgWNgFF8FEpWMRCEYRc\nRAPBRkREwUc6C9HCF4pILGZX914369y9cQvJH5adOTtn5js7e2Zm+U9ZiB1YX9DmRkVaouhuqe/H\nPUxiTYHf0hnsNTzuQE+R/zn0xHZ0Fp/QW9CmkWPbjoeYjh2oTf+DOJT3IBvZEkwJb6Mb7xL7FDYX\nDL4RZ/AWPyIFl/Efw1VcK+qsFzuFN3ES65JrRU7bxgx9XFJ+JmL8h4V8bSKbE9+wMinfxIvketOB\nqNnmIo63GlsTexAfheRaLEzfZ1z+1+oi+SDo688a84KYQB9O4xTuYl8FAmMZxVDWkA2iD1vxACeE\n7++psNQ+r0ZfFC+Fz76WGrJB7E7qA7ggTNtybMF4dRqjuIKjaSUbxGByf4RXSbmOLtyqRFp7dKWF\n1iDeC7OQUhf2i4lqdEVzTJgN/A6ihm24ji+ZxnXcFjahgVkYfAPmd9jHWmHZ/6UzDWIX5gn7Q8pq\nrMJ9HMH3iAFSgbWcZ3vxTFi2y/inDAkr1B+M4CsWZGz9wlFgTDgaZGlkystwB0+E3XY68RvH4Uy7\nTYn9dUtfsf6EFfR8fmzt0+jAd6QD32HhaNRE62ZXBUWn4yK6hbydzHtQhpj8yGOPsISX4YDmnO2Y\nRSV8eoR/la6/NZyBtn6K5pijJD8BAqxjIW7wMpQAAAAASUVORK5CYII=\n", "prompt_number": 7, "text": [ "\u210f\u22c5\u27581,1\u27e9" ] } ], "prompt_number": 7 }, { "cell_type": "code", "collapsed": false, "input": [ "Jminus * jz" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$J_- {\\left|1,1\\right\\rangle }$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAEIAAAAWCAYAAAB0S0oJAAAABHNCSVQICAgIfAhkiAAAAehJREFU\nWIXt2E+oTGEYx/HPjFt0i0JZkA0LkUT5tyGzslFkY8HNQin/FjcLa2UpSqGUkoVICmWBYhYsNBKy\nlaUFNlcsXFyLd0bHae7MmfedmSPudzPzPr/ze85znt6e98wwQ6ncKruAPNUu+jo8xDP8xBRe4WDi\nfRd20EbxOiF3J/85jCTkVsEnvEhJkqE+TXwDGkLDY+jm34097YRuO6LFaizAg55LK8ZK3MMR/Big\n/w52RuT/zTGhy9tTkmSod9CuiN8RRfzj2JgPFt0RNXzHk57L+vu4jAP5YJFGVLAVz/Glz0WVwQQ+\nY0k2WKQRa4Qp/3gARZXFRRzKBoo0otb8rPe7mhJ5i8XCUYtijdiGSTwdTE2lcRVjrUW3RlSF+dDw\nb8yHPJXWl2wjNuMjjmdiazEfj4ZT11DZL+wK/NmIfcJQnMjExoQXlGtDKa0YKzAnMcdyvMfXduJR\nnMisd+BbM95v6h2068IL0WgbrdbUbkb6W5zG0unEWTjVvOg8bgvzYRDUc+tFuI83wkNM4YNwZO/N\nXLeqGX8X6Yd5uJT6AP2inug/meAdx6Z8MPYn6WEs66A3cCMydxFmR/qqWI+zeSG2ERcifS0mE7xb\n8DLSuwt3E+7dd+ZG+kZwRub875HkP2Zm+F/4BZltWVGn5m3TAAAAAElFTkSuQmCC\n", "prompt_number": 8, "text": [ "\n", "J \u22c5\u27581,1\u27e9\n", " - " ] } ], "prompt_number": 8 }, { "cell_type": "code", "collapsed": false, "input": [ "qapply(Jminus * jz)" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\sqrt{2} \\hbar {\\left|1,0\\right\\rangle }$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAFEAAAAfCAYAAACf3SEqAAAABHNCSVQICAgIfAhkiAAABB5JREFU\naIHt2VuIVlUUwPGfl5oaL6WgomJWTllCZDcLM8Eyqqciih5KIXrQogv60EMXQjLyoTSil0px0i6S\nZahol4f6KpIuCJUVFJUgYZSW4Y3ErB7W+ejMcX9n5sx8MI3OHz7Od9ZaZ+/1rbP3Xmvvj376+T8w\noEnt/NOENprlS59lM07qbSd6i4FNaONavI8/m9DWccsGDO9tJ3qTno7Ey/El9jbBl+OWtRjV2070\nNj0ZiedjJ3Y1yRcYgmk4q8Tm9Sb21+uswmkl+mlYhzexDSswvsT+emwV5dLVJXa1El2rWF6qMln4\nugxLxW8bU7B5GoO70XZD2rC8RH8h3sGp2f1QfIBfcXrJc0uxHy0lNrUG8kvwmeo16yn4CbflZA/g\nK5yYk92IWyq2XcqzOKdEv0kEOs8F4geuSdiPFMnpL/wtArlf/JAitcL9uVl/7fhY9SA+Jl5ufpSN\nxGHcmZMNwstVGh5Wohvfhcb2YwdGF+R7sDth34JLRQAeES+gDWMTtrWSfttVD+J32JiQb8O7BdkC\nsUx1oJhYWvESlpR0uhBPduLYdrGmDCnID+HkhP0hjMu+b8D32efnTvrpKcNEEtuR0O3ERQXZCtxR\nNMwHcbAI0I+YK11Aj8QZIgGUcRkmimDWGScC+2mDZ2Zin0gMI7AaB/BCJ331hInZNVXnHhAxyK/P\nezMfyxIkIlAHcV9CtwizKrn5H0twBNMb6LfiLRHA5ZiC9WIJyFMr6aNdtek8PbNflNCtznTFLD0J\ni/OCVJ34u5jSd+l4sjIUF+O9Ck7WacPdeBxbEvrhmIqPcL9Ye74RmfzbbvTXVY5k11TgT8iugwry\nH8Ssaq0LGhXbz+BsXJOTzRdZuSotIhE9h4ca2MzIfDkTz4spMwbn6d5L6yplG4X6er4voVuFOfWb\nRkH8Ah/inuy+RRTAqSxWxgCsFNN0YYndzOz6uViTYXb2/OaKfVbhFzEKRyR0Q/CHdBDp4vnnzaJm\na8M83FrdR4vxcEE2N2G3Bb/JTRER/N2Onk61kv7aVS9xtordSpEdGs+ClbowneENkebvxU3SRXIZ\nt4uX8GhBPqNw3yrW2ldFQqszW2wZj4hp3gwmO/rweJOoUfMjaxIm4LVEG5NE6XUwoUvyoAjE/Cqe\n4koxil4sfNbglYLtVWL0XJeTTcxk88R2bEJOVyvpd032XGtCNyvTrS3Ix4ppOycnewpf67jtq/NE\nwZ9OGSWK3qpH/3uEw6lPcWQuEqfi+SJ8vFj014ttXZ5a4X403hZbxHofu8RUzC9BUzL5dkczVYzI\nZaK8WicdqOEiQfZ5aj18PlUTdpUFYup3oBn/sfQ1yk6Iyhgo1u5PUoq+xuEePHuFKKO6ww1iX39M\nUHbCVMZgcV7Z3f+3m34o208//fRzLPIv82vR8KM+l6AAAAAASUVORK5CYII=\n", "prompt_number": 9, "text": [ "\n", " ___ \n", "\u2572\u2571 2 \u22c5\u210f\u22c5\u27581,0\u27e9" ] } ], "prompt_number": 9 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also do this for symbolic angular momentum states:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "j, m = symbols('j m')\n", "jz = JzKet(j, m); jz" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$${\\left|j,m\\right\\rangle }$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAC0AAAAXCAYAAACf+8ZRAAAABHNCSVQICAgIfAhkiAAAAlNJREFU\nWIXt11uITVEYB/DfDI1LzNQIIdF4QEK5ltxGklKUS7xNnjyQSJpX5IkhScqTUnhAyIOHoQ6h5IlS\nHigluT5RyG08rDU5rdln763TIPnX7uz1X9/61v9867+/fQ7/AC78aQFl0JiMRxTEH8ODftKS4igG\nZk2kooswBDfqllMOFawrG/i3YADOZE38aqV/J77hHuYVBVb6XcqvoRknUjLT6AlmYDPG4CV2lFiz\nBssxEx1oxQb0YAG6cBU7hYd/FJriPl+q8rzDe4zD81qbVZLxWBwRbDQtbjq9QHBTXEM43lvYhYbI\ndeI1DmFi5AZEcR0Z+SZhfzVR5Ond2IPvcTF8LlizJAptQJtwOoeEL0yoZKvwkD2N3Ld4jc7I90Qo\n3tBaG1aS8ZSq+y6hQkUYI7TGGVHoomT+LO4kXFuMXVUj51Js6R0UVfpRsrBMj36Bj1gWP+9mCKgk\n3Ep8KsjfUGsiTdaLFnzFtpykKS7jesJNFSq6IuG7cTHeTxA8Xo2TquxRtk8vjIkqGXOTMTjhGrE4\nI75d8PTtKq418qfjuFPwdy8mCaf3oZa4LFFwEG/0PaJ2oXLnEn62bD+fSwTDrBjbjLnYmsx3YXw1\nUaZPE3x4088O0ItXeIs5CT8OD/X180icSrj7OI8DQqfZVzXXHK9neeIqGVyz4OftOev25iWtAzsx\nPyVreXq94FVYLdjiSk7yQXVJy0ajcILpafVBt/DG6xHeQi14LL+Si7Cpfo19sBYbywQOxzBcw3Fc\nEn4P1MJAHJbTQ+tAzT8B//EfOfgB1hltMRlg1uwAAAAASUVORK5CYII=\n", "prompt_number": 10, "text": [ "\u2758j,m\u27e9" ] } ], "prompt_number": 10 }, { "cell_type": "code", "collapsed": false, "input": [ "J2 * jz" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$J^2 {\\left|j,m\\right\\rangle }$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAEUAAAAbCAYAAAAqCUKuAAAABHNCSVQICAgIfAhkiAAAA11JREFU\nWIXt2E2IHEUUwPHfbEI0Jk4wojEuomQPKpJo/CYaMys5BAyK4tdBWYIHDwYxiMSj0SCiq4iK4sGL\nYDxERRBRiGgrfhAifoHgQUUQcf26RI3iGtfDq3Z7endmmvlIFOcPTXW/elX1uuq919XFkDnUumx3\nAdZhGS7CPXirX0b9F1mK+wrP1+IARg+POf8O1uAvjKXnOmbE5PxvqYnwyUPvDDEpayu0fX5QRvWT\nkQ71a7EHe4V3zOAjnJnu4U48hA8rjHdsBZ3H8EkFvX7wCBZ227iGn/BBSX4T7lc9YWcVdJ7Co5Ut\n642rcF23jVcLzygm2M1iUuBInFKhn6xbAwbEAuwqCzuFT04jlW+kcgNW4GWcgE1Y2Zt9h4WD2Ifz\nu2n8AqaxBKvws/Cc4lWv0E/WzeADpo4ni4IqSaaGS/A+fsWXOLrPhq3BFuFtU7itg/4V2CgS/gSW\n4xqxOOswiVewTST347EojTFd6mu/WORRfFPV4PxLc2/VBm3I5pGdiIdFKOef99Vt+liU9AnXfxu3\nm0322/E9HjSb5xaIF59o0ecYduYPVXLKeCqzCrrdcAfu0rwh/KON/gYxETURylNiAvItwrTwnF34\nKskOpmtFiz6/EItzVFWjX0xGLqnaoA3ZPLLTCveTYpXbsRKLRcjNYH2p/lm8W5KtSrqXtem3gZvp\n7CkjIp/sE/lkEHxWuG/gzQ763+I3XJrKvaX6hrmTvwm/V+i7RvOkXIgfRXzmnIVj8HqHzvrBsjRe\nJ8NzxvGe5lA7XWwRspLulXgVv+BkkWPKTOBpmiflBpGp9xdkN4pYnLPBGQAXC2OzkvxUsTkskntw\nWXdc5JR3CrLlSf5Met4u3qnImPDAA2WjtqYGOZvFKmxt9RZdkLWpewA/aP5lGBe5YHdJ9xzz55Pd\nmicEzja7jzoPt8wz9iROyh+K+5QnsCMpLBbf7Y0O3eFRI401U5B9J0L63JLuKD41N58cJ4VAgY/x\nnPhHm8Ldpfp6ur7u0u6eyVrI6/gTt7ao3zEQa4Jt4ijkH6r++wyCq0W+gMtF2LzUQveIAdkwIryw\n7HGHlD2pzP+6d4qvzudae8N6XD8ge3o6OugX+T/TUryGx8XmcEsL/YXiAKvbA/ZO9HTINGTIkCFV\n+BvGEKDJmNTtoAAAAABJRU5ErkJggg==\n", "prompt_number": 11, "text": [ "\n", " 2 \n", "J \u22c5\u2758j,m\u27e9" ] } ], "prompt_number": 11 }, { "cell_type": "code", "collapsed": false, "input": [ "qapply(J2 * jz)" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\hbar^{2} j^{2} {\\left|j,m\\right\\rangle } + \\hbar^{2} j {\\left|j,m\\right\\rangle }$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAALEAAAAcCAYAAAAuq171AAAABHNCSVQICAgIfAhkiAAABQVJREFU\neJzt23/oXXMcx/HHfjCG75j8nFgb+dXGCsuv2YQ0y5iffyhpf0hJll9/IivSSBIptS8yJWIUMuoQ\nSlIsRCElbURqftsvf7zPbcfZ/d7vuZ977o/Nfdbpe++5n8/r8zmv9+d+7vvzOefLkCG7OBMS6szH\nGZiGM3EP3qmzU0OSGcamAvvivsL7K/EHZiRozccK3IV1WNBp5/7n1Bmb3Zq52IbZ+fsRbBeGtcPQ\n8PqpKza7PRPEDNpIQ04URs1rU2doeP3UFZvdgn1wGo6pUPZpPJDQRorhLyS0s7vRi9g02GX9XoqP\nxIA6f5yyy3G/9haHj2B9k/NVDM860K+bhzG5B+0U6XVssoQ63WRMzyeW3q/F2/hd61Xtkvzv7ZiC\nmRU7sneuX2Q5NuDWihrt6neDDJf1oJ0i/YhNN+qkkqng+XRswhaRr/6WH5+Wyp0jBt6h+XEJTk/s\n2JJcC/bS2vAssY1uMAlrOtSYq/ps3o/YZIn1ukUlz6eIPHU77sTR+XFYocws/JqXKR4jCZ1q1/As\noY1uskLkp6mMqj5L9jo2DJ7fVPT8Ur1Z0aYYnnW5T+0ygsc7qD+q+iCmd7FpkPWonXZo6nn552yB\nGFzrcYBIppfheVyb2PBcXCdmjY24Gd9gv0S9KvqtWIrzcJK4pum4QgyQM7AKr4lv/YE4GHvmbWwu\n6GwSXs3A9/VcSkt6FZu669TlNxU9/wivC5OewAliQfFLhYtrxuF4SCwgG9tocxK1shr098zLw4d4\nF7fYsYq/Az+KnZKZ+blJwrhmA2U2Vlbsf5lR7c3EvY5NllCnTN1+08Tz4u7ECE7Ge2JluwKf5+Jf\ntuhoK24Tt5WLNzb+SdSqQ/8cYeQEkdJsFAZuzz/fLGaKNfg2P7c1Pw5pove1COzUxP5XZVBi02+/\nGcfzxbn46rxBudA23Nuio604rvB6lfjWpZLVoH+Y2BaaK6717NLnz+L90rlZedmLxtBciOvHabcZ\no6rPxP2ITZZQp0w3/KbkeTEnbjyA87HIWYlcZgJeHaezY/FFqeG69xTb1d+Q/z0Xf+KD0ucLxUAp\nciH+Gke71U2FJ0U+WOZIsdJuNpMtF+lDg0GJzaD4zRiev4+f/XeaXo2fRJ7SCdPEHueNHWhkNeqv\nxVulc8eLGeCC0vl1eDF/fZSdvVgtLZ0YVX0m7kdssoQ6Y1Gn35Q8b+TEU3EKnhNPkzU4T6wct9rx\nM5bCWXlnsg40UvSPFTdRikwUM1u57CKRo71XODc9P/9M/v4O4UWD2WK2KXpWN4MYm375TRPPG4P4\ndOyBlwuFj8IRIjG/xs7bHe2wUMwan3Wg0a7+IvHz93Sp7Dzsr7mpH4rbug1mimC9gVPt3P8b8Fhy\nr6sxiLFpVqcXftPE88YgXoC/Sw1tyTu6WORn3zW/nkosFPf7t49Trk79H0T/TymVnSHMKednB+Gp\n0rlPxD7s/WKhUTRvJD868aUKgxibZnW67Te983wnRoTpN3WokyXq391hu2OxQtwKTmVUe/vE3aCV\nd1lCHbrnN2N4Xn6KrS4uF/kRXCxWkq/0SX9Kje02mChmnPLs0g6bxCq816TEpt9+U4/nlZkjfmZW\nilXsV+r5dq5L0D8bV9fQdplluKoLut2mHe8GyW967Pm+eBOP4iVxD7wOGs9aVNWfjAel/Uf3ePTj\nofg6aCc2g+Q3u67nQ4YMGTJkyJAhQ4Z0nX8BhgWzuUASqbIAAAAASUVORK5CYII=\n", "prompt_number": 12, "text": [ "\n", " 2 2 2 \n", "\u210f \u22c5j \u22c5\u2758j,m\u27e9 + \u210f \u22c5j\u22c5\u2758j,m\u27e9" ] } ], "prompt_number": 12 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Find the matrix representation of a angular momentum operator:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "represent(Jz, j=1)" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\left[\\begin{smallmatrix}\\hbar & 0 & 0\\\\0 & 0 & 0\\\\0 & 0 & - \\hbar\\end{smallmatrix}\\right]$$" ], "output_type": "pyout", "prompt_number": 13, "text": [ "\n", "\u23a1\u210f 0 0 \u23a4\n", "\u23a2 \u23a5\n", "\u23a20 0 0 \u23a5\n", "\u23a2 \u23a5\n", "\u23a30 0 -\u210f\u23a6" ] } ], "prompt_number": 13 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Utilizing different bases\n", "\n", "Angular momentum states and operators are able to go between different spin bases" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can rewrite states as states in another basis:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "jz = JzKet(1, 1)\n", "jz.rewrite(\"Jx\")" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\frac{1}{2} {\\left|1,-1\\right\\rangle } - \\frac{1}{2} \\sqrt{2} {\\left|1,0\\right\\rangle } + \\frac{1}{2} {\\left|1,1\\right\\rangle }$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAOMAAAAkCAYAAAB7Y41AAAAABHNCSVQICAgIfAhkiAAABcdJREFU\neJztnFtoHUUYx39pq2nTxEukFSy22lSrgvdLtV4riD74IOLlQS1IBK20lSr4ICotVgyKLYpQrJYe\nrUpBraDEK+iqIF4oqBERb4VSFK1YSVXUGuvDt4dsJrOzt9md3WR+cEjOzO7Md/7fNzuzM7MLHo/H\n4/F4RulwbYAnFfstlOF97fFY4FVgumsjPOUyxbUBnkQuA94F/nJtiMcz2XkZOMi1EZ7yUXvGHuAF\nYK4DW5pMWbqdC3wODFsud6LT+Di+CViNTBYc5dSSZlGmbs8DsyyXOdGZUHGc9CNerMiOpmHb+ScD\nj1gsb7L5rXFxnGcC5zBDXhcyrHKBqe5HgWkV2mKDO4CHDflnAduA14AhYBMwx3B8GX5bGNqwHlgH\nPA0crhxTV+3LjGNrsZh0RQli0s8EPsHOmlhWkuq+Eri2ZBts9owLgCcN+acBbwKHhN+7gfeAnw02\nBDHpef12MLALuD6SdhfwBXBgJK0K7XW4imOrsZj1RxwPDAIt4EODEWWQtu6pwHMl22KzMT4OHGfI\nH0QabJRTQxu2xpwTKN+L+u1+pPFHr/K9wD5gWSTNhvYnkb13rTqOS4nFvFcUQkNc9Ixp6l6FDO3K\nIktj7DHkzSHZWb8DO4HZSvoe4JeYcwJDeS2y++1r4BVN+hDwtpJWVPsW2S90LuM46XytHpNp0X8T\n0O/Yhi7gWWDAcMztmO8VAXYg92YzlfS/gRm5rUtPD3AMckFQ+QE4XUmrg/Z1QqtHtDFeB2wI/x8A\nlldgVJUMA3sxT3LkIa1u05CG9j2wFP1Cfi9wNLA9oc6zgXlIo2xzBNJAP05ldTHmhX91659/IL+t\nM5JWlvY6mhDH1vQIDHkt6jtMBegD1pZvipFe4E/gNk3eGmBJznIHgBFgcUx+YDi3RTa/LQ6PX6PJ\n2xLmqbOqRbRvYX/NMEior8xhKmj0mEzDVIDvkB6ky6ENvyJD1VsZ+yRFN3AG8E6OMhcgPcADwAdF\nDUzBSPhXF3AHhH+nKul10L5OjNNjsjVGkLWwGxzb8BhwLHBpJO0WZBY1K53IhM9G4O7ipqVityGv\nfR+7V5NXB+3rxBg96rAYeyISSGmft/sUCdwiROtyUf9nwPvACuB1pEFdQvLEjUoHsDks496CNmXh\nJ6RXPFSTNxP4DX1jBLPOTyE7j1TmIrOP/2jy+km+x64z4/TYn/CJEhgKbmmOr4q0dW/G3lApSTeT\nPVcD/yFDzJuRiYesrAXuUdKWxhwbGMppkd1v25HdNyo7iR9q59W+Rbp7xiz+CBLqK/ueERQ92sPU\njoSPLRbi9iHZPuBHZALFBkm6mbR7CVkGWAlcRfxifRw3Io35PiX9vIzlpEHnt0FgEWN/Yx9wJPLE\nhIpt7XUU8UcWbMTxOD1s3zO2DdRd/ZYAXyGzbWVgqrvNMkanvV3zL2LLcmTT8oj58DFcDDwEzAee\niXy2Yt5QEEcev21AhqTR7XArgC+BJzTl1En7JIrGceFYXITsDFgNvAVcEHNcoHyfDbyB7ElsDwV2\nI0OV6NDrhDB9B/ZIWzfI2tdGi3W3SaubjlnAt2S/yu4hfhim9pRtAuW7Db+dgvSQ65G9tNuQnlGl\nqPYt0i9tuIpja7HYzdhdIdcg3aduUVL9EVnRrU1VwSrEUTbJoptrgoLnF/FbUe1bpGuMTYljrR7t\nYep84E5kHAsyOzcDedLcNp3Jh1hnCrKG95HlcqvUzTV5/WZD+2HSvQOoCXEcq0d7aWMIOAfZqgWj\nQ41vNIXty2kEwPnI0kDVXIG8S8Y2WXRzjSu/2dB+ZcrjmhDHmfXYQvyaV54JApCGvw437++s6gFX\nk26uceU3lw8X1zGOM+nRDzxYoLLJitetXjTeH5cz+mjHdCbAC30qwutWLxrpj+hm3guRbUeDyKzU\nRchVZVf1ZjUKr1u9aLw/5iN7CdU1K//yXDNet3rh/eHxeDwej8fj8Xg8Ho/H4ymP/wEzzZd2JaYA\nVQAAAABJRU5ErkJggg==\n", "prompt_number": 14, "text": [ "\n", " ___ \n", "\u27581,-1\u27e9 \u2572\u2571 2 \u22c5\u27581,0\u27e9 \u27581,1\u27e9\n", "\u2500\u2500\u2500\u2500\u2500\u2500 - \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 + \u2500\u2500\u2500\u2500\u2500\n", " 2 2 2 " ] } ], "prompt_number": 14 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vector representation can also be done into different bases:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "represent(jz, basis=Jx)" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\left[\\begin{smallmatrix}\\frac{1}{2}\\\\- \\frac{1}{2} \\sqrt{2}\\\\\\frac{1}{2}\\end{smallmatrix}\\right]$$" ], "output_type": "pyout", "prompt_number": 15, "text": [ "\n", "\u23a1 1/2 \u23a4\n", "\u23a2 \u23a5\n", "\u23a2 ___\u23a5\n", "\u23a2-\u2572\u2571 2 \u23a5\n", "\u23a2\u2500\u2500\u2500\u2500\u2500\u2500\u23a5\n", "\u23a2 2 \u23a5\n", "\u23a2 \u23a5\n", "\u23a3 1/2 \u23a6" ] } ], "prompt_number": 15 }, { "cell_type": "markdown", "metadata": {}, "source": [ "When applying operators in another spin basis, any conversion necessary to apply the state is done, then the states are given back in the original basis. So in the following example, the state returned by `qapply` are in the $J_z$ basis:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "Jx * jz" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$J_x {\\left|1,1\\right\\rangle }$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAD0AAAAZCAYAAACCXybJAAAABHNCSVQICAgIfAhkiAAAAi1JREFU\nWIXt2E+ITWEYx/HPjClMGaEkUmIhksj/RGYhGxrZWDANKRLSZGGtLEUpKaX8KSn/lQXCXbDQSMjG\nQpYImxGNGYzFe+7MmTv33jnz3u6d1P1u7jnP8/7e9/ec95zzvudSp2rcGGsDaRpHyC/FQzzHX/Tj\nNfaOcpxpZXLNeDPK/rLqT6MptuMGfMPLSH2uRHwFuoSLGcNI+m3YXhgcaabzLMJUPIiyNpwFuIcD\n+FNF/R20RfQPDglXc1OkPlcmd0H8TGfRd2JlOpB1plvxG0+jbI0t57EnHchSdAPW4wV+VMFUtenG\nd8zKB7IUvVh4+z6pkqlacBb78ydZim5NfnPVcFMj3mOmsLxlKnoD+vCsep5qwiW0M3LRjcLz3OX/\nfJ4LaWBo0avxFUdSsSWYgse181U1OoTZHlL0TuGF1Z2KtQuL/5WaWRvOfEyosI95+IifhYmDOJo6\n34zeJF4puTK5q8LmorlIrjXJXYvU5zmB2cUS43A8aXAGt4XnuRhrsQunhDtkH25ibon2uYLz6biP\nt4nhfnwRlsUdqXYLk/iHSD204FwJX5lpMbjDaRO+wOAiZpTQ5Coc81gF2k6sSgeybkPT9OJycrwG\nt5LjDnyKtlae8ZG6Riw3ODEDwdHSIxQOG/EoOZ5cRtMXMU6edXgVqd2KuxWMPcAW4ZaZg1/CR3oD\nDpfRTIocqwknk/5jKPonQkxnu7EM7zBRWNJ6cB2fI83VqVOnTjT/AFLpagvFmvsXAAAAAElFTkSu\nQmCC\n", "prompt_number": 16, "text": [ "\n", "J \u22c5\u27581,1\u27e9\n", " x " ] } ], "prompt_number": 16 }, { "cell_type": "code", "collapsed": false, "input": [ "qapply(Jx * jz)" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\frac{1}{2} \\sqrt{2} \\hbar {\\left|1,0\\right\\rangle }$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAFoAAAAkCAYAAAAJgC2zAAAABHNCSVQICAgIfAhkiAAABKpJREFU\naIHt2mmoVVUUwPGfZT17DuUDFRWzfDYJkU1a2ZxRQVBEw4cyCAMtGtAPfWggpSKJZoKoFF/ZINlA\nhjZB3YqkAaGygkZBpCgrwwnFrD6s8/S847nnjvlK7x8O5561h7XfOnuvtfY+jxYtWtTHcfi7CVeL\nCixFv94eRG+x1y7Scy7exeZdpG+PZTEG9fYgepPsjB6IF3BgE3VMwudY18Q+/9dcjVki4BzUxH4X\nYUgT+9ttaKahj8JDTeorTX9MwCEFdV78F/Q2lWYa+inFbmgCXsJrWIF5GFmhzwuwXIzz7IJ6pYKy\nduHOauUwMd4HcL/4+4Zl6jyMvtV01ixDj8XcgvJj8CYOSJ4H4D38UoX++7EBbQV1SmXkx+MTtefk\n+2M1rkjJbsYX2DcluwiXVdNhswz9GA4vKF8iXkaaoxP9C8u06RBB9U/8JYy9QfyxWUqZ5yMSnV34\nUO2GvktMgvRs7cBWXJOS7Y1nq+mwWkMPLCgbWYWyDViFoRn5Wvxapk0bJoox3i5e1FgMz6lbKtDd\npXZDf4NXc+Qr8HZGNkO4xe3Us2FpxzOYU1BnJu6r0M9K4d/6Z+RbsF+ZNlswIvm9GN8l108VdDXK\nQBF4V+WU/YhjM7J5mJoWpA19OR5Nfs/BdTmd9hVG/AFXyt+EdOBgEbCKOAGjhcG7GSGM/3FBu1Ox\nXgSzwViAjXiygr5GGJ3c8/YCG4Ud0vFiXTLGSoG9Ih3YhBtzymbjjDr7nYNtOKmgznK8Low8F+Pw\ninA5aUoFfXSpzXWclNSfnVO2ICnLZh+duLP7od6zjt+F+7gWfVLyAeKU7p06+hwrVtHdWFamziCM\nxwe4SfjCr0RM+boOndWyLbnnvZx9kvveGfn3YoW209ih0iM4FOekZNNFtlErbSJ4Po5bC+qdLMY8\nBk+I5TkMR6rv5VbLmoKy7hizPqfsKUyhMUN/hvdxffLcJjYQeZG5iD6YL9zBzAp1T03un4o4AZOT\nPpbWqLcWfhazeXBOWX/8Id/Q9FzxdR+2XyLy2bGYJgJqrdyJ2zKyK8vUXYbfJMsxYb5IB7NLt1Sg\ns0vt6d1ysSvMskr51TRfxnX0qXCV42WR3tyAi5XfaJTjKvGi7sjIT86p2y78//MiEHczWWzhtwmX\n0gwOs/NHiiUih0/boxOjxIlnlk6Rdm7KKauLW4SxptfY7kwxE5/OXAvxXE79s8QsPC8lG53Ipomt\n8ahUWalA98KkXXtO2RlJ2aKMfLhwEVNSsgfxpZ5b8G7uzYxnOxNFFJ+Ft+zwh5UYIjYNtX6mWqu8\nq8rOcCK12qznZmakCFSviC12mlLmeSjeENv1bj1rxLJPu7xxiXylnRkvZvYDIrV8Sb4xB4nAvhMD\n9NzpXSqmfMMJdy9SarB9Xs5cLTPExN1Ot48eI/LSzuT5dTFzJjWg7P9O0clgEXuJWPJRVkgcjJxo\nR8rUvRy+rVPZf4GtDbQ9RaSQ9XChOIepigUqHwr91yk6XSyirzjvLsq2iqj64H8q7mlAUYsqON+O\n471+mvuRdo8mvZs6TXzfWyKykNPFrF6964e1+zJG7NWz+ewe/U8vLVq0aNGiRYsm8g9t9gZJzKYP\nXwAAAABJRU5ErkJggg==\n", "prompt_number": 17, "text": [ "\n", " ___ \n", "\u2572\u2571 2 \u22c5\u210f\u22c5\u27581,0\u27e9\n", "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", " 2 " ] } ], "prompt_number": 17 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Rewriting states and applying operators between bases can also be done symbolically. In this case, the result is given in terms of Wigner-D matrix elements (see the next section for more information on the `Rotation` operator)." ] }, { "cell_type": "code", "collapsed": false, "input": [ "jz = JzKet(j, m)\n", "jz.rewrite(\"Jx\")" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\sum_{mi=- j}^{j} d^{j}_{mi,m}\\left(\\frac{3}{2} \\pi\\right) {\\left|j,mi\\right\\rangle }$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAALYAAABCCAYAAAD68ywLAAAABHNCSVQICAgIfAhkiAAACjdJREFU\neJztnX2UFlUdxz+7y/Kyygq7WiKVuKsskqxBlFEIu4itlGJ6cH0BJVvIMA4GZOjxnN6OL70g2uZL\nCmimWRYlpZVHrB4tLcKkE2l6ICWPIRb2ImkvENsf3xlnntmZ55lnnzszz+7ezzl7nmfuzHPvnZnf\nvff3cu9dsFgGIDUpltUNHAv8IsUyLZbEORIYnnUlLBaLpd9SlUIZncAwYAawOIXyLJbEOQHoAKqB\n3RnXxWIxxhjnczLw0ywrYhlcVCec/4vOZzvwSMJlWSyvk7Rgu7QBD6dUlsWSCjXAn4ERWVfEMnhI\no8eeDGwH/pVCWRYLkJxg1wLrgJHAecDXEirHYgklqZD6aGAZEvC9wA0JlWOxWCwWi8VisVgslgrm\naaAnwb8vpncrFovHOXhC+CpaQFCMGjQfeyQwFpgEnARcAtwOvOTL869AnfFaWywxWIcniL+l/Mji\nEOA0FHrvAS4qMz+LpU/UAU/hCfctBvNejhqLxZIJk1Co3BXusw3mfSVSVSyWTFiCJ9j/AJoM5Tsc\nWGAorzh0AytSLC+Ki5BaZppW4N0J5Dug2YAn3FuAodlWp09UwmLjy5GdkRRfAqYkmP+AYxSwE0+4\nr8+0Nv2Tk4E1vuMZwPnAIuAuYLaBMuqATcgzZYnJNGAfnnAn2fOYpBMJ0NoM6zAceBw4yJe2B1jo\nfD8LeA0zAnkaUrssJXA5nmC/DLw52+oUpVIWG18KXBZIOw5P0OehTsNUT7uZwrbQdwyVM2CoBh7C\nE+6fk+5uU2GsAZ5H9WkPnKuExcY1wLPAYQWuuRu4wmCZHwFWFzifi5HHDaTrkm0EdgAf96V1k4yh\nHcrh5EcRr06r4AIsBf5DdBBpBfCZ9KqTRwfwYMS5KehFrsVsJLYRLbSOWmiSi5HHeuDLpioUg7HA\nVmSLuJyJWRdzUTqAA0iwDwQqkwXfRKNHFN8HZqVUlyDdwKoi13wY+DVwsMFynyDa/ZczWE6S1KDR\nLFW+gNdr7wYOTbsCPv4EXBVxLuvFxtuQB8TPu9Cod5RzPAE9x3kGy72RaPUmZ7CcpFkOvDOYmORi\n3iuAXznfnwX+mWBZhWgGjiB6+4csFxuPQBPIngqk7weeBHY5x03IePyNwbJ/z8Dwaa8HuoKJSSre\n+1DLbwDmAv9OsCw/s1D0bifyr29DgvKo75pa4GbU2rNcbNyEdP89gfTHgduQbXAAmA6cioynIPXA\nrcDpRAeYeoCZwM98aS8Ax5RY31bgQmR07wY+VuT605H//XjkumxArssepAatBn6E3kMj8AYU3LsQ\nyQ/AROCjKIB2F1Ir/byC1tWORSNz4swH/kLpD68cutAQ/ibn+C3I/xvck7sRCfoy9FCz4r14u2X1\nhSpgI/ApZNfcggI6s4H7gfc536fTe3RuRy7ZMHIhaUegwFs18FYknJMK1G0oXqBuC7JxVuJthLoK\nqYDXAuOctBokpK7/vta5pxrUiKK8MM1oblHizEDzRt6TRmEOx6NWfm4gfQ/wuRTrUQpnAc+U8fsL\ngDm+42/juVfvK/LbKWgkCyMXknYdGgFBI3AP0FIg/5ORTVCFGtCGwPkVTvlvD6T/HfiE870TNViQ\nGznYW/u5jYTn8I9HPXVnkoWEcD9aoFDrS5uIXsCc0F9kz/lI7TDBKOBe53sd8Msi1x+Nnk1tyLlc\nSNoE3/fVqLctxBhkQ7Q65ZwYOP8N4LFAWpNz7fud4yNRQ21CKlkH0bThm8Nv2ng8FPghWtr1LcN5\nF2IUcAqaB7HPl94G/I/Crr4s2Y/2DjfBfDyVawK670K4XqCoXjvI077vbRTfi/FFZJDPcj43B863\n0bsBnYJsMTfvP6L7WISCbJuKlPn6fu8mBXsY0vc2IVdfmhyNWnZQl25H/tq9mJtOa5JXkfEXJM66\n0CCL8aKnYyjuXj0E2R9heRX73duIv8loO3ov//WlHYsCebnAtWcADyAPmttbD0HG5HrUa0e9x4X4\nnACmBLsK+CrSq5caynMonhFRjFecz+d9aSOQJ8B9AZeUUZdW5B4zzW4kKEGqYvz5mYEE4QnnuB4Z\nzoWm4R5C3+bHTEcClwukt4SUV+3ULXhtOxpZ/Z6qBif9687xKtRbdyBvye1IJsLkqxmNEK/5CzbB\nVejGzqb4EBiX5ahHi8N2ZDGPc45rgZvQKLIDPZiXyqjLM8jDYJqdaGJTuerISjRSus9+FxKyQlNd\nD3PKL5U2ZJA/6UtrR6rKnYFrJyM1MRdIb0eeEv/7HYcazIPAO3z5t6B3+wJwMRLwIEuQ+9YoH0I9\n5ZhiF5bAAtSblLJgYTzwA2S9dyM34wfR8LwW9QiVyC7C3WYnoMb9aSS0weikn53IdehSj/TTuQV+\ncw3587/95Ar8bgu9Z/9NRA6D5wLpc4Hf0fs95ui9WLsGeXW+gu7Z7XTd4NoawldUuX58o5yEbug4\nA3lVoZd3L9L7ktjIsgEJy0Y0f7wL+UevR/cyH/gs3gyyahQcWEdI2LaPeQbZQG+V62DyXZSdaJgd\nW/wWY/MA0SH6XER6PTI2l0Wcz2Ii2XLUCRjDbaVxV3ZUoZZ7EBoGW1D0aRGat/AH8o2jqSYr67AI\nqSnb8Vp/HYr+TXOOJ+KFuM9AasydyOdsIs8gXcgw8tOKDKVm57gePRNTLtQhyDUapt9DvmDPw/NX\nL0DqzlHBHzikHS+oxtPJjfBGNOwktRPUNpOV9eFu1uMPvU4j3+d7ARoS3evrkX4XNUmq1DyDNCJV\nzj+9oQr1Qq6R6Eb6JkfkUSpzgO8VOO+61SY55V6JGsEOonvlE9FGSmkSOW21r3NFPol6pHKiZoW4\nMaF896Je+Ce+tNkoquVyHgrjjgb+hgyTjah3qCXfT97XPP28jOyAOXjRwh7y/b6XIR1za5H7i8tC\nChtbZzqfzwE/RurWHchJEGa8DUHPYKWh+sWljcrYWaAiuIN8nfZh9IBAgrcHCbD7kh5DVvpSvCjd\nLCetr3kGOYboFTxdKC5g6p/NNtO/pqVaYvIongenCum+rtU+AvWai/H2I7wZ9dr+zXvuBr5bRp5h\nXENvY+5UvCmZw/HcmeVwDwNjuqolIeIGj+JSixqL20BmIqE+3Pn7AJ4x2lfOpfhUU8sgpgb14qYZ\njXzu45HuHjSqw8LvcWlFtpElBkmFmuOS1bZk52DWp2ypMIYR7dNMg0rYlsxisVjSwXUhNSBjaCbw\neRQpG4ms8PuQ8dKCZtGtRj7dJShgcCveol03r0sp7J7ajxz9QZ9wXDrRaDEDeRssllCSCDUnRaVs\nS2apYNzI4z1IUOvQSmDQGsKteJP3p+JNJXwI9cjtaDOXcrmYwgsBtjh1BIWfN6PRIkvD1VLBuIJt\nMtTciGaylaKK3FRCnd1V3e3AIyX8zjJIMRFqTpMstyWz9CNMhJrTIuttySyWRJhK/no5iyWPJPfu\nM00tWskykmy3JbP0A7LelL0URqMlSbXI2E1i6ZjFYrFYLBaLxWKxWCyWwcL/AdTvRdeK61V4AAAA\nAElFTkSuQmCC\n", "prompt_number": 18, "text": [ "\n", " j \n", " ____ \n", " \u2572 \n", " \u2572 j \u239b3\u22c5\u03c0\u239e \n", " \u2572 d \u239c\u2500\u2500\u2500\u239f\u22c5\u2758j,mi\u27e9\n", " \u2571 mi,m\u239d 2 \u23a0 \n", " \u2571 \n", " \u2571 \n", " \u203e\u203e\u203e\u203e \n", "mi = -j " ] } ], "prompt_number": 18 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Rotation Operator\n", "\n", "Arbitrary rotations of spin states, written in terms of Euler angles, can be modeled using the rotation operator. These methods are utilized to go between spin bases, as seen in the section above." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Define an arbitrary rotation operator. The given angles are Euler angles in the `z-y-z` convention." ] }, { "cell_type": "code", "collapsed": false, "input": [ "a, b, g = symbols('alpha beta gamma')\n", "Rotation(a, b, g)" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\mathcal{R}\\left(\\alpha,\\beta,\\gamma\\right)$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAE0AAAAWCAYAAACFQBGEAAAABHNCSVQICAgIfAhkiAAAA+1JREFU\nWIXt2HmIV1UUwPGPjqVS6hQqLaSY5ZhhtkEYOpSELUq2WhIVRFZUQkWIlRhmhmmLtqlhlrQXERSY\n0kYRLWQQ/WNUtljQItG+ok5/nPdr3jzvez+n34z0x3z/eu+ce88979x7z7n30UOn6dVNdoejFQ93\nk/0iwzATTdgD++NyfNeAzSZci8Voq2rYGydhNTbi16xDG7bhN3yB53FaiY1mPILdGnC4M0zE9eib\nk63AU11guxU3VzUYg5cxH5PwgAjWVByZGZiRyf/OdOcl7KzI2u8KDsbsEh82dtEYd2FCSnEAtuCM\nnOxxsbr6JtqfL4J2T0HeIgK/q1gsdkeeXvhITG5XcCBeTSmm4+KC7B18UmJonAjahQX5vQlZdzEa\nZyXkF+BLDO7CsV7D4fUaNeEXPFaiv1EEtU9BvgkjK+yOxv24EyvxBIbm9J0pTDegX/a8OrP7Hr4V\nhaCKXpiFT/ETluv4LX0yH2vcgptqL8WlXWMc9sTricEuxVE4BVtzuuGiCGwqsTkNL4nVeHVm52Os\nFZMES0v6puiHP7Pn77PnD0T1vKxO32WYI1LJczgbi3L66Xg29/4uxtdzaJbYfqNFMK4TM/kCLirp\nM1F58h2LP8TWyXNINs5ksTrm1XMsYyCuKdEtxzcVfVvxJAbkZEPxIYZk7ysLfSbgq3pOrc2MwBrx\nYVtFEMs4E2+X6NZhs/YVVWNAZnu2CNh+9RzLmIKjS3S1c1UxddSYg/4J+QyR14/DOQXdofi9yqFB\n+EvH88nSzJFnKvpNx5sJ+WBsF9syRRsexcIqpwrMVZ5a1oqzZGdpxn1imxYnt0UuaKmBp2B3kaRr\nzMMPOB1HlAy6BXsl5CNFLtxQ4fBYLKjQFzlITESRfXCCmITO8iP2FcHZVtDtrc7t4kW8kZDPE6ui\neA6biSswqsTwiKxf6njQP3Ow7NTdor1C1hgkJii1/RaKNNBcx0YZm0WeLTJFehchPny72N9FBopy\n3iZK8DDcKma1n1hNX0vnpfXiZJ3nGJG03xeJt7eOueT4bKynC/2mihy4SMcjyjTx0YfthI0y1pfI\n56qo7PPxkPJ742QRuJ+zAVoL+jV2TKLEzD+IVViC20QlbRLB2yACd2yuzxixoj4r2FqQ+XeyOPXf\nLc5py7RXv3o2UozIbKRYJyagW5hk52d1Z5lfeF/SBTZSXIVLEvIh4lTw76ouq0D/lVfENq66FXSW\n/N23WdxUGrFRxol4KyG/UqSjyt9DjTJC5Lmu+Fc3Eefm3k8V1bERGyl643M7+jxK/ObaJYwX16RG\n6IM7dPyQBeKa1IiNFENxe0HWJP7iDNixeQ899PA/5R/opcWRrIlFmgAAAABJRU5ErkJggg==\n", "prompt_number": 19, "text": [ "\u211b (\u03b1,\u03b2,\u03b3)" ] } ], "prompt_number": 19 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Find the Wigner-D matrix elements of the rotation operator as given by $\\langle j, m'|\\mathcal{R}(\\alpha, \\beta, \\gamma)|j,m\\rangle$:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "mp = symbols('mp')\n", "r = Rotation.D(j, m, mp, a, b, g); r" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$D^{j}_{m,mp}\\left(\\alpha,\\beta,\\gamma\\right)$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAHEAAAAgCAYAAAAlrJeCAAAABHNCSVQICAgIfAhkiAAABW1JREFU\naIHt2nmIVXUUwPHPOKNlZZqlWFZWltqmWYlJOqWJFEVClu31R5tERRsRFYWttpsk7XtJe2CQ7WG0\nQSsWFYoRLZbtZthCZX+c+5g7b+59y8ybN0XvC8N7757f75zfvb/lLHdo8J+nuQ425mF7vF4HWw26\niWFYt6cH0aDBv5qmbtQ9E+ugFSd0o53/PXmT+AhGYSf8IvzZ34msP/rhLszFXxn9x2MAnsMKDKnd\nkDvFMLGY7quDrS3Fom3G+hiKk/FNF/U242xchbWVdtolaXxphmwMluKxnL6bJp9j8VLFw+weBuB+\n9K6DrUk4T5xABW7GwzXS3yp7PnI5VUziPjnyPRP5USV0nInZ1RjtBm7GrnWwsx3OybH/UQ3tzMPE\nShs/ij+wXok2S5XeaQsxpVKD3cBIvFAnW1ehV9G1JvGM7qihnW1UcbqtVD63ewU/5MiahR/oW6nB\nbmA+jq2DnVE4OOP6Mfgcm9TY3mLh7kqyvTgqryzT7kOsyZHtjlerGlrtWY7hObJRuBXX4xY8iMEp\neTWR+/nacuE7E71vi40wtEzfJuG6PsEq3ISWlLwlGWOay3Fx4Ufx9i/Qmny+XMJ4H2yLL1PXeuN2\nEb0egXtLDr97GSaCmuUZsul4XuzUM3ASluEpbVWsuVXYWhe/Jd+/T74vEdHprDJ9b8C54thfiEMw\nJyWfiSeK+ryJCeUGtUCkDv1LtNlb7NbbUtc2FrvvNPFwepJJsgOKnfGrOOrSFE6faWL3XFihnQ1F\nAJfFTfi6RN9WPCQWfYHB+BiDkt+3ZPSbiC/KDewLvFumzTxx05PLKeshZuCNjOtP4zMd68b9xP2c\nIyZwswrt7C9cRxaFnK4lR36u7JjhcBwvNsqhGfIdpdxY1nE6XKzEUkdpPxydtOnpPDCPZm0FigKb\niJ32pI5FitXJ5xiR562o0M5YvJMj20ksmD9z5HPEqVDMIpEW7SuyhGLa6cuaxII/XJxjmEhom3Fc\niTY9zbfYqOjacBFIvFWi3864pAo72+q4WIgq1VQ8UIWuAj+Jgska2RWxgcpUgO4WR8CgHPkMUYrb\ntxODqycjdLzRrcW9ZaUDfcUDy6uIjNTxbUx/sViyjsvLxC4cUEZHHp8JP53F/ngtr2MTPhWpQzGD\ncK2I4vascCA9SRO+0tG3PSP8eZrxIgh5TwQSvbT3RZPF5D9S1O8A4UPnaJ+STBeTMLoCHXk8U0J2\ngVT0XFhBW4j8ZnMRmq9KlBQKrX2Sv4eFzyjODQeKpHovkVvuIPzmVsL/DBGr8GdckzGozvRfX6QG\nk8TpsYHwI0+JcH0tnk3kD6VsHSryrtvxo3j4S3CKCFDmiwm9J9VnJb7TMYAZL/K1qYm+Ncm4VmM3\nsUvL6chiaxGh5jFR+Ry+ao4XOeIybbXU9fC7tnxmB9k7vLP9jxYT/ToOSrVZkmozReUrvxKK68BX\n10BHFqfjxBzZIBF11/w1Yj8R0aYT/wnah/jHyH+gnenfX/iXtE+aVqSDOFHyqjbVkk7CB6g8l8zT\nkcciEWBlMRsHpi/kVWyqZbV42/Fi6tpUURUpcIQoIhRHjJ3tvwp7iIkuhNzTxDvMNLPEkdfVlTtJ\n+MwCrUoEFxXqyKKXCGg+yJCNEAtyYZV2K+Ye7YvNi0WySjz478SReVZybQrGdaE/XCTqiMQxs1T4\n0WImCP/ZWVpwnfYL4RLh/7qiI4vBIoAsphk3al/dqTmvansZ3CT8V5/kd18RoJygLWxegMe70J8I\nYK7AkaIGOaY2t9KgGrrymqi3yAPr8W+X/2pq5RM7Q7PSL5zLMQ7vy65oNKgThyn/ri2P0SII+gD7\n1WxEDRo0aNCgQYMGDXqWfwBhwxNCydgNhwAAAABJRU5ErkJggg==\n", "prompt_number": 20, "text": [ "\n", " j \n", "D (\u03b1,\u03b2,\u03b3)\n", " m,mp " ] } ], "prompt_number": 20 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Numerical matrix elements can be evaluated using the `.doit()` method:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "r = Rotation.D(1, 1, 0, pi, pi/2, 0); r" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$D^{1}_{1,0}\\left(\\pi,\\frac{1}{2} \\pi,0\\right)$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAGoAAAAhCAYAAAAxvj44AAAABHNCSVQICAgIfAhkiAAABTdJREFU\naIHt2lnMHXMYx/FPqWrR1lJLUcvbUoJWrYmtSmyxJJagtHphDxIubI3YoqkIIi6IEGs0pUhExNLE\ndsGFfU8qqK3SIrTUUuvFMydnet7/zDk9Z87bSs43OZn3P8/M/3lm/tvv/8xLjx4Vcy4Gr+4gusgE\n7FdkXKuLjofjMWxTQV1XYhH+qqCuPFXG2Cnv4RTsMZBOz8K1+BfbdVjXYbi1wzpSVBljVayH+aID\nDSidvoSheAPrVxJNmjWpoeBY3N54smjOn4edsCt+wWv4J7ONFC1+H27D31VHmuMiMTUt76KPgWI8\nZuML0TlG4VIsbrjuKVyFPnzWSsW7ZxXekLBNxAI83qSOTnrr2iLQTdu8v1UGYkSNxNeYljs3Ex9g\nSOL683Bzq5VfJB7i0AL7/pl9WoGdzl7CEXi+zXtXhYFoqFlYYuUZbGP8ifMT12+Cb7Uo9h7DCrHA\nFbEAL5bYO3kJt+PyNu9dFQaioRaIKa2R9/FCwT1vycn1shY7EG/i15JrlohpsBtMEWvj/53h2AFf\nJmyLsGfBfa+Jd4DihtoZm+GVJkFsLJRZI6fjzuzvG3Fhk3oaGZbF8FGBfQTm4jcxIlK/f0RnK6LV\nGDv1tW12XJawLc/qXzdh+1huT1Wk+g7KjmUNNQTjhIpp5OHsl5p/W6EPf+D7hG0QHsTbQnmegNex\nEBfjDjFl/45XS3y0EmMVvkZkxxUJW03Nbqi/+vtajMRS5gjZPbLkmoNFb7q7WWVtcLhYTFOcgaNy\n5XlCIZJeBzqhCl/7ivd0bcI2N7NtmbBNwQ+1QtmIeg9LSwI4ITvOaRJoO4yUniqIHl5jQ/EMfwvR\nU7WUr8LXdyW22kb+54RtqdxASa1RY7GV8mlvOKZn15SpvnYZKh18I6erC46ddHfz3a6vxWLUbJSw\nrY+fpJ91mRi965BuqNr69HKJ85lZJWe2GOyq8pf0AtvI2eodZbTY7bdCkSjI/6rytVyscWMStnF4\np+C+YdmxMBF9vwi0aGifKNJKRyZsrWaj9xL7pBlijRvXYD9OWqTkOQg/qq8ZU4UyS6nQ8QXnW6VT\nX9fjGyFOaowV7/mCAp8HKEmdDRKKJiWLN8Ut+ERkJRppNRs9RDTC6Ky8t1BSefYRU0IZT+LRXHly\n5vuYhuumZOfnNamvm75Gi+eZnjt3Gz6UTiHB0fi0VqiJiTG4F1sL3b8Uz6lPAUOy36Nig5vaBN+T\nHa8pcFxjshiRNVX3htgzbY/Ps3MLxehcV8j0FBNxTq78tthUNk7ni4XM36tJXGV06utboZJnib3R\ncLEHPVJathMDY2EHMTel2Yg6W3+h8pVI7+dZhN2qC8t1DeV9cYmYBearr83d8NUOs+W+w62OT9uj\n9B+Rv+v/sexV0fver8hvXpxsgONxRVY+Gc+KDeY3Fftql0nqs1RXP8UXsdTKiyrx4hqzEM+orpcf\naGV11YfLxIJONNIw6bW3U1/tMFis0/M7D6eYZlPfIXg3Vx4sRtT4hus2EetAp6N+sJhC8p1jkJj6\naud2EXFP6oKvdjhKCJiukmqoQ4S6Ix5mkbqEnyJS+ike0H/t6gYPCUW7pjBXevtTCbVs9L+Zo3w2\neg6eyJUPxV1iH3UfdiyocwfdyXzkORM36XwUVMVYvLQ6A5jR5n2zcVKVgeQ4Rj27MtSa8U8uj0j8\ny9hAiYm1lX8pLuNqnKa+Qa6KydgcT2MLMdVU7WNVmSryiUVLQdc5VSR622UjkWqqajvRJxKhjfm9\nEWU3dZkJolP26NGjR48ePXpUz3+qbzDxAdRwIAAAAABJRU5ErkJggg==\n", "prompt_number": 21, "text": [ "\n", " 1 \u239b \u03c0 \u239e\n", "D \u239c\u03c0,\u2500,0\u239f\n", " 1,0\u239d 2 \u23a0" ] } ], "prompt_number": 21 }, { "cell_type": "code", "collapsed": false, "input": [ "r.doit()" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\frac{1}{2} \\sqrt{2}$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAACwAAAAkCAYAAADy19hsAAAABHNCSVQICAgIfAhkiAAAAjFJREFU\nWIXt2DloVFEUxvFfNBDRJEggIgYkagpBMERFQcGtSiF2phEEURBEEMFSQU0KURQURNwaNwTFQohb\nCjGlpakEK0khIgTcUHAr7sSM4+QtM5cZIvOHKd6575zzvfvuOe/eocH/wxr8ivCrGQ8xp9ogsyII\nyUI/nuNrjfJVzQO0xwhUOsNtuIfFMYIX2ICX+BAxJtiLY8LC7o4Y9y46I8b7h5iCe3EuUqxpiSn4\nuuTltRb38QhjuIauvEliCe7B1YTxVXiK+YXrVoziXd78sQRfwvKE8WHhoYrpK+S/kydRVsFtCWNd\nuJ3i/wlvsKDEPoH3GfL/IU3wXNzChYR7zmB1Sp4xfMOSEvtbfE7xBTtx0dQrOVDmnmYcwSA+Kv8x\n6BAKKY15WFhiW1TI/yyL4Dx04AsOlhk7ji0Vxj2JH1hfoX8iV/AKTUW2VqGYKqFHWNdDVeqall7h\n9fUX2Q5jewWxWvACZyPoSmTU1Iy24Im/ZzwLTUJHOZHHqdJN8w78FF7nPqFw8zKEoyW2XRXEyUQz\nxnEeI5id03+38jN7OSlhNXwXWuEg9gsVnpWtOI3HuFmiKdNRaB0OCVvMEWzMmLgTr+U//kyYfgkO\npjm3Cj1wkgGhz+beOdWKlULxLCtctwtPOlA3RSk0CUtisiWtEAT31U1RTm4IG5gZwR6ckv8DUBe2\nCYIJVd9dPynlKW70m4Tz17DQNTYLszxee1npLBX2t6X9MMqfHw0aNJjB/AanNX6i8nBFNgAAAABJ\nRU5ErkJggg==\n", "prompt_number": 22, "text": [ "\n", " ___\n", "\u2572\u2571 2 \n", "\u2500\u2500\u2500\u2500\u2500\n", " 2 " ] } ], "prompt_number": 22 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Wigner small-d matrix elements give rotations when $\\alpha=\\gamma=0$. These matrix elements can be found in the same manner as above:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "r = Rotation.d(j, m, mp, b); r" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$d^{j}_{m,mp}\\left(\\beta\\right)$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAEgAAAAgCAYAAACxSj5wAAAABHNCSVQICAgIfAhkiAAABANJREFU\naIHt2VuMXVMYwPHfdAyqelGXuFcopS41qqlJOqWjKUIIRdzaeaCpB5fgoYkHQqtoXULcHkpJkLjE\nQ4mEuqSNqpCIlJB0PEgpqkNQIY1WPXx7Z/bZs8/kzDmnc4ac/8s+6/v2t/ZaK99lrXVoMiCtdern\nUZyA9XXq73/HBOzd6EE0aQAtNdpfjr0wEwtqH85/k4ewCbswKyOfjnMwAj82YFxFTMC8AfRHYjGW\n4hG8ioNELl6kBoe5AdsxMiM7JHm24/1qO64j4/A82sroO3G78PiUp/By8nsmluSNRlT48Rn4BH9l\nZD8kz1lYW2E/u5P7hLf/XaA7Fh3Cc7bndCcnz7UYI+Y6aDbjnjK6VeiqptM6MgnvDqBfpr8ztGAj\nns7IjlZFNBwj8s+cAl0rflIaeo3gcXSX0R2PSwvk8/EtDsjJ1+DUtLFHgWEXFuIbEdefYwfWFbzb\njh6lodcIzhXhVcRcPJj8fkbMZSoOx2nozb2/Dpfgs6LOrsWWxJjI+n8q3SG3YQVGJ4NaWPk8dgsT\n8PMA+sWZ38vFrn8l/sjpUi7G6qKOpogEd2VO3isSYMr+YpVvwi0DDGyo6MRXZXRjcGsZ3ZOKtycz\n8F2RwRv4RWmZnCzyz3mVjLRBzMVHZXTn4/QyumVibvk0c6KIGvRl9nEijlcrLZNnYSc+GMyIh5hW\n/FNG145Py+hOEhvgHTl5STtdoInJh/Kn8VnJB7aJEjgc2Yr9yugmKl68gzEbLxToxovKjL4F+j15\nbsq8OBJnirIHN1c23iFns8iLecaKECuq1DeK/LOsQDce36eNdIF6sAFHJe02PCG25V+L88qWQQ99\naOgRaeDQnLxTVK0lSs9YF4nz2gX4taC/dnycNtLV3YXL8DCOEOG2VHhPt0h0i8TqdgvPul8k8dFi\nYV8XrjtJeOQDBR+vxn6U2Ep04lnsK/Yvb4rd8y68nehfynxrOu4WobRCJN5RIl1MFaFZxIxkbFVx\nnfCuHlyTyPYR55uOpD0ZX9bRfp5YxPViA5e+syHzThdeyX1reSUTynGgqIhVn+pH4zAR9ykdSsvs\nfP0HW4v9WHFbuVWfx8/J9QFviWMRUZXvGGAe5bgLF2YFlZ7mU7bhbLyXkc3GO5n2VXhRcWWpxv43\nnCEWMS3Bc/Tf7V4vQqpFXF18WMmEMhwnFnjVIO368ZzSg+EasV8iJtUrwui2RNaFaTXYw50iJxJh\nsFFfQcnSIfLVYpFvKqUVjwkPr5l1+i7LWkS+2DNpjxTJdoH4l4PwhtdqsCeS8b24WtwETqnHRIYT\n5a4iKqFNbNzq9RfVoBhsDqqGVlGpqmWauHLZWZ/hDD+uEJWrGk4RCf0Lw/vA3KRJkyZNGsG/dQzH\nyXC4f9IAAAAASUVORK5CYII=\n", "prompt_number": 23, "text": [ "\n", " j \n", "d (\u03b2)\n", " m,mp " ] } ], "prompt_number": 23 }, { "cell_type": "code", "collapsed": false, "input": [ "r = Rotation.d(1, 1, 0, pi/2); r" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$d^{1}_{1,0}\\left(\\frac{1}{2} \\pi\\right)$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAEMAAAAhCAYAAACC9hYiAAAABHNCSVQICAgIfAhkiAAAA/NJREFU\naIHt2VuMXVMYwPHf6JSOS6e0SmnGaBXRqFTQxq1mKOoWQlBNTJNqeKiIEOEFL1KESjyQphJBwjRK\nCEI04hLRF5EgvBChYpDUQ1ul0ro8fHvP7O6z957Tc/ZJPMw/2cnZa639rW9/a32XtQ8T/K+5Fd0d\nkLsAZ1cNOKDNCQ7DJvS1KSflPoxgbwfm+AI34PQ25RRyCx7Ev+ivQd5SrOvwHAdjszBwR6hD0Sn4\nFId0cI6UK/FkUUczbrIOWxOFBmpSKM/twhV2dUh+ljewCHNaFbAGf6GnoK/dVZuE73BkxZg6dwbc\nhsdafXgYH5f0tavoJXh3nDF1G2M6fpbzjGazyXn4sEZlslyO9zoku4zfhDEWZxubMcZcHKNzxhjA\nlg7JrmKLJmLgIDbiEawX8WKPxki/Ak+LLTycjNtfekRNMaOkv5k5piZ9fybjiq5/xO7OsgavVCm3\nCr9idnLfhz90buXmay+DdOE1PCBiz3pRm1yEN3FZ8vtcjV5wtSjECjlN7IDlufZteLgNhau4WPhu\nq9yMZZn7l0V2IlJoFQMidoySPQM8hJ0i36ecIiJvp+JFL3a08fzzmd/TxPv8LSrNqlQN25P5R0m3\nzjRcKkrVPZn+CxLhZWm1XaaIBaiDFcbc+WShdxU7xC6anDakxjgh6cjHhgF8JhRuuWKrYC8OKukr\nC4bZK8tqvJ/8nqU8KKekBeTooTA1RrpVt+YGLzHmIndk+po9SZ4hzgFD2CCMnmWXyAZFdDVxpZyP\n48TCSWT2iZ1XRq9IDqNGTWPGNyKy9if3k/GUWLVvMVNkGSJaz8a1uLtisgNF6losguTXeAlnZsb8\nIue3LXKXcPHUNUaEIdKsUkRvMn8hJ+ItPCFWcx5Wiq23AUfkxo9XIi/FV5n7LvyO4zNtM5MXKHOV\nZvleZKaUqfgBV1U8s1KNle94xliNj3JtP4ojdJYRnFoiYxHuFN80Ngt3qIu1ct9POvF5LWWG8Mks\nuzV+WPlEfH36Mtd+KK7Bvcn99XhH7NifatBvIZ7JNrT72a+K7fYNcsQLbsu1va14xefgHnE2IgzR\ng3Nq0K0bZ4ndVgvjuckgPs8psBsn5cZNF1ksv0u7hJukBp2fzLmwNXX3YRler0HOKEXGGDSWLbpF\nPEjTb1qzFPGcxliS5wU8vt9aFjMsisy2qTpJvohXM/cXisPTEJ4VGauIecYKpiJW4VGNbtcKc/FB\nDXKaYqjF59biuoL2K4QxiNqhv0X5KRuV/F1QdwCdJA5JrXA/bhKldMoSHCXqn6PF1p7V+GjTLBdH\njjJ3rZUbcWwbzx8uCrxukU12ajyPlJXv47FAGHyCCSaYYIK6+Q/y58unxR4TVgAAAABJRU5ErkJg\ngg==\n", "prompt_number": 24, "text": [ "\n", " 1 \u239b\u03c0\u239e\n", "d \u239c\u2500\u239f\n", " 1,0\u239d2\u23a0" ] } ], "prompt_number": 24 }, { "cell_type": "code", "collapsed": false, "input": [ "r.doit()" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$- \\frac{1}{2} \\sqrt{2}$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAADsAAAAkCAYAAAA3pUL9AAAABHNCSVQICAgIfAhkiAAAAl5JREFU\naIHt2E+IjVEYx/HPMEXMTJoayZQGs1BiGoqi/FtNkZ3ZKCWKpKQsEWYWIoqSDDb+pchiapBZyCyt\nZFbKSrOQ1JR/URiLM5Prdu9773vvufcy7rfu4j7nPb/397zvOc95z6HO9KSh1gZSMFFrA9XkIWaX\nIzAjkpFK04Nn+FprI9VgEC3lisR8s824j0URNWE9XuJDZN2S2YsTQhHpiKx9D22RNaMQO9kuXIio\nF5XYyd6QPC3W4AEeYRTX0R7x/onETLYT1xLaV+EJ5k3+b8II3kX0kEjMZK9gWUL7kPBAMume9HA3\nkodE0iTbnNDWjjsF+n/CG8zPio/jfZEeyqKYZOfgNi4lXHMOqwvojOIbFmfF3+Jzgb5lsROX/R5C\nB/Nc14ij6MNHuT8UWoWiU4i5WJAVWzjp4WkR/atGK77gUI62k9hcou5p/MC6EvtXjKt45c+dV5NQ\neEqhU5jH/WX6qghdwpDryYgdwfYStGbhOc4nXZT5VFdgQPF73BfYX4KxTEaEubtVMDwoJJ9m79og\nFLzXOF6mn4JMFPHLxw78FIbgPqHQpaUfx7Jiu0rQqTiNGMNFDGNmyv67cSpHfCDfzWrJd2HJ6sMB\noZIWyxacxWPcyog3qvARzlocFrZ5w9iQom+bMN/SHrmMyz9t+lJqFU2TsL5N0SusoVXbfVSTlUKR\nWTr5v0V4ur01c1RBGoRhPLVkLReS7a6ZoypyU/iQn/bswRn/1uF7SWwTkiVU1o7aWclN2kU8HxuF\ns6IhoTpvEt7uWCT9v4Ylwvdt9lpX9qF2nTp16vw3/AJm7nQeoH85UwAAAABJRU5ErkJggg==\n", "prompt_number": 25, "text": [ "\n", " ___\n", "-\u2572\u2571 2 \n", "\u2500\u2500\u2500\u2500\u2500\u2500\n", " 2 " ] } ], "prompt_number": 25 }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also directly create a Wigner-D matrix element:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "WignerD(j, m, mp, a, b, g)" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$D^{j}_{m,mp}\\left(\\alpha,\\beta,\\gamma\\right)$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAHEAAAAgCAYAAAAlrJeCAAAABHNCSVQICAgIfAhkiAAABW1JREFU\naIHt2nmIVXUUwPHPOKNlZZqlWFZWltqmWYlJOqWJFEVClu31R5tERRsRFYWttpsk7XtJe2CQ7WG0\nQSsWFYoRLZbtZthCZX+c+5g7b+59y8ybN0XvC8N7757f75zfvb/lLHdo8J+nuQ425mF7vF4HWw26\niWFYt6cH0aDBv5qmbtQ9E+ugFSd0o53/PXmT+AhGYSf8IvzZ34msP/rhLszFXxn9x2MAnsMKDKnd\nkDvFMLGY7quDrS3Fom3G+hiKk/FNF/U242xchbWVdtolaXxphmwMluKxnL6bJp9j8VLFw+weBuB+\n9K6DrUk4T5xABW7GwzXS3yp7PnI5VUziPjnyPRP5USV0nInZ1RjtBm7GrnWwsx3OybH/UQ3tzMPE\nShs/ij+wXok2S5XeaQsxpVKD3cBIvFAnW1ehV9G1JvGM7qihnW1UcbqtVD63ewU/5MiahR/oW6nB\nbmA+jq2DnVE4OOP6Mfgcm9TY3mLh7kqyvTgqryzT7kOsyZHtjlerGlrtWY7hObJRuBXX4xY8iMEp\neTWR+/nacuE7E71vi40wtEzfJuG6PsEq3ISWlLwlGWOay3Fx4Ufx9i/Qmny+XMJ4H2yLL1PXeuN2\nEb0egXtLDr97GSaCmuUZsul4XuzUM3ASluEpbVWsuVXYWhe/Jd+/T74vEdHprDJ9b8C54thfiEMw\nJyWfiSeK+ryJCeUGtUCkDv1LtNlb7NbbUtc2FrvvNPFwepJJsgOKnfGrOOrSFE6faWL3XFihnQ1F\nAJfFTfi6RN9WPCQWfYHB+BiDkt+3ZPSbiC/KDewLvFumzTxx05PLKeshZuCNjOtP4zMd68b9xP2c\nIyZwswrt7C9cRxaFnK4lR36u7JjhcBwvNsqhGfIdpdxY1nE6XKzEUkdpPxydtOnpPDCPZm0FigKb\niJ32pI5FitXJ5xiR562o0M5YvJMj20ksmD9z5HPEqVDMIpEW7SuyhGLa6cuaxII/XJxjmEhom3Fc\niTY9zbfYqOjacBFIvFWi3864pAo72+q4WIgq1VQ8UIWuAj+Jgska2RWxgcpUgO4WR8CgHPkMUYrb\ntxODqycjdLzRrcW9ZaUDfcUDy6uIjNTxbUx/sViyjsvLxC4cUEZHHp8JP53F/ngtr2MTPhWpQzGD\ncK2I4vascCA9SRO+0tG3PSP8eZrxIgh5TwQSvbT3RZPF5D9S1O8A4UPnaJ+STBeTMLoCHXk8U0J2\ngVT0XFhBW4j8ZnMRmq9KlBQKrX2Sv4eFzyjODQeKpHovkVvuIPzmVsL/DBGr8GdckzGozvRfX6QG\nk8TpsYHwI0+JcH0tnk3kD6VsHSryrtvxo3j4S3CKCFDmiwm9J9VnJb7TMYAZL/K1qYm+Ncm4VmM3\nsUvL6chiaxGh5jFR+Ry+ao4XOeIybbXU9fC7tnxmB9k7vLP9jxYT/ToOSrVZkmozReUrvxKK68BX\n10BHFqfjxBzZIBF11/w1Yj8R0aYT/wnah/jHyH+gnenfX/iXtE+aVqSDOFHyqjbVkk7CB6g8l8zT\nkcciEWBlMRsHpi/kVWyqZbV42/Fi6tpUURUpcIQoIhRHjJ3tvwp7iIkuhNzTxDvMNLPEkdfVlTtJ\n+MwCrUoEFxXqyKKXCGg+yJCNEAtyYZV2K+Ye7YvNi0WySjz478SReVZybQrGdaE/XCTqiMQxs1T4\n0WImCP/ZWVpwnfYL4RLh/7qiI4vBIoAsphk3al/dqTmvansZ3CT8V5/kd18RoJygLWxegMe70J8I\nYK7AkaIGOaY2t9KgGrrymqi3yAPr8W+X/2pq5RM7Q7PSL5zLMQ7vy65oNKgThyn/ri2P0SII+gD7\n1WxEDRo0aNCgQYMGDXqWfwBhwxNCydgNhwAAAABJRU5ErkJggg==\n", "prompt_number": 26, "text": [ "\n", " j \n", "D (\u03b1,\u03b2,\u03b3)\n", " m,mp " ] } ], "prompt_number": 26 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Coupled and Uncoupled States and Operators\n", "\n", "States and operators can also written in terms of coupled or uncoupled angular momentum spaces." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Coupled States and Operators" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Define a simple coupled state of two $j=1$ spin states:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "jzc = JzKetCoupled(1, 0, (1, 1)); jzc" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$${\\left|1,0,j_{1}=1,j_{2}=1\\right\\rangle }$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAI8AAAAaCAYAAACOyA9jAAAABHNCSVQICAgIfAhkiAAAA9pJREFU\naIHt2l2MHXMYx/FPdav0ZYsLlNCNVhq0TRERF96uCRIsSUmo3ggRL5GIK4oQEm1CiHjJ4WopJWlc\nCGHckV5UtBLESwiieiNkCcVx8Z9hdsyZnTlnZs9ZmW9yMmf+zzzPPL//Pjv/Z2YOLS0tLQ3xKA7P\nMxyS2X+5IMgSfNjHyddiJ7bFibyAY/qIk/B4n3nUSb9zkWUUtFCsJ8JVZYJEPcbPwm50Kya1At/g\nmtTY3diHQyvGSngWj/XpWwf9zkUew9bC7HoW4qUygaLM/il4HR28V3CCXjyAHzCWGjsKB3FjxVjD\nZtC5GDWq6NmKdbMFjApsnVlOkMen2JUzvhdvV4w1SnTM/+JJ01GsZxW2ZwezPU+dLMfJ+DrH9h3O\nbPDcLfXyldCnzmicx/KPrYVV8fanHNs0xrEYv5WItQHXYyW+x611JDgk5quWHZjE88lAk8UzHm9/\nz7FNx9sjsH+WOMdhM24X1ul9QqO5t0Iuz+GMCscT/qhRRZ/ZqEMLw9GzC1PmqHj+jLd5a+mieLuw\nRJw7cQ/+wup4LK8gi9hc8fimqEMLw9FzUOhh1wlF32jPc6DAtjTe/lwizlP4Mf5+Xhz3kwHyGibz\nXcsz2JLsNFk8+4WrzpE5tqXCJJYpno9T3y/AuwNnNjzmu5bPsSbZaXLZmsYenJBjW4MPKsZbgY3C\nbWVVnsbpFX3u0NwfdxAtDE/PanzRyxgVOHYUPwtYi8MyY1vxLRZkEujiphL+aS6K/bIPq5YLr1VO\nLPCtm47qc5EmT8vZuE3oid4UlrW5oqPcc6sHsb6XMSpwnIpPsCTHdmFs25EZXyksT9emxrbjIzNf\nT/TyT/OI0COkC3GLMNldTBT41k0/c5Emq2UZHkrZJ/ELjh8403IU6UlYhFeLgkSZ/aPxhtBdd+PP\nAbyDTanjTo3Hv8yJuVF4DL5NaLh2+u9SVuSfsBuv9LDNRfHUMRcJWS0bzLwDG4/jT9aReA/K6km4\nHNcVBYwGTOjehvzH8Qdu6WGf6ytPGapoWSAsW8mV6DRBU9W+pklelHnCXPfd1uIa/a8Qege4RJjY\nvPdko0oVLV2879++4y7h5yt7Gs6xLBPC0/Bfiw6KBjjBubi6Jv/1wkTeL9yZfKb4qjZqV55BtNyA\nh83s7YbNfQoa5YSoz+Bjwn9Kv4Kz/svwFp7Aa8K7oCJGqXgG0XKxUDyEu7WJZlKsxJjiHwn+Q9Rs\nHo0xSsXTL+cLhXNs/LkM5ww1o8ClZmmUE65sNo/a2YQnheKZws3DTadvThKetnczn/Eipzlim+Jb\n+JaWlpaWlpaW/zV/AzTy7joiuTv9AAAAAElFTkSuQmCC\n", "prompt_number": 27, "text": [ "\u27581,0,j\u2081=1,j\u2082=1\u27e9" ] } ], "prompt_number": 27 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that the Hilbert space of coupled states is the direct sum of the coupled spin spaces. This can be seen in the matrix representation of coupled states:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "jzc.hilbert_space" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\mathcal{C}^{5}\\oplus \\mathcal{C}^{3}\\oplus \\mathcal{C}^{1}$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAGYAAAAZCAYAAADDq1t2AAAABHNCSVQICAgIfAhkiAAAAuNJREFU\naIHt2U+oVGUYx/GPpek1I/zbNSIvSa40rbtIEtp4ixQqlMxFlhIiim5EQQKDiYx2IRRERboxKMid\n3oR0EyhKBS5SSUI3Jl4Qysr+GdjieQfPjHPv3OMZ70zN+W7e8z685zm/mec9z3mecyj537AH/+BP\nfI3+9sppyJN4GeuxDwPtlTMi9+BzPFjUUQW9mF3U0W3kMtam41X4XfwBncZ68X9eR19RZ5WiDsaA\n+bg7Hb+AazozMFVuCsz4W3DSg024gqV4B6eKKmsx32WOV4rN9Gt7pLSeuXgLJ/ETfsbzWIO70poB\nfI872iHQ8BrhMWzHR5jcFnXBSBqrjCqVjccbGBT5uR+vp5PfVXuX9SX7wiLKb4FmGrNswLeYMpYC\n5dPYNJVNwKc4g+UZe08afxQpbAb+cCNvXyvwA/IyGo1DWIzz+Aof4BlR/XSCxoPNHNQHZjfuxM46\n+zp8I0rPSSIosATHcDqHaJiIF/GoqO6GRMAHcaLJuc00HsbTuJjsD4mNc7KDNB7KI2QAf2FOnX0G\njmBemj8rcvcOfIxZeS6Cp0QvtCTNK+I2noRX8D6mF9S4BtuwFftFoDpNI7yUfF0Xd9iWRg6P4rN8\n+nOzAm9iXMZWUZtf7xMipzU4v+s0PiKitqpVDhvQK+6weipurkjuFzspS1dprJa5y9LYLHcWYSPe\nHuXaiyKnZ9NBV2msBmZBGi8VdTgCffghx/ov1L7j6iqN1apsZhp78HdRp8MwR1Qr9SwWL/B+qbPf\ni7OZeVdq/ETkxuHews4XFU4R9g5jr2jc9T6OVzPzrtJYTWXH0/iaqL+zzMUB0a8U4ZzaUrEZy/Bl\nZt6VGifjgoj2oXTBfmwW5V8rvrnMMvqK5wG8V2oMHhYPs6v4LTnaJJqqVvEcdhm5R+gVfcDUUuPY\nslTsyifSvKK2q/5QdMntpO0axzVfcluYiNVYJBq1IfG9ZFDx50Sr+C9oLCkpKSkpyc2/Pkneg0NI\nsz0AAAAASUVORK5CYII=\n", "prompt_number": 28, "text": [ "\n", " 5 3 1\n", "C \u2295 C \u2295 C " ] } ], "prompt_number": 28 }, { "cell_type": "code", "collapsed": false, "input": [ "represent(jzc)" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\left[\\begin{smallmatrix}0\\\\0\\\\1\\\\0\\\\0\\\\0\\\\0\\\\0\\\\0\\end{smallmatrix}\\right]$$" ], "output_type": "pyout", "prompt_number": 29, "text": [ "\n", "\u23a10\u23a4\n", "\u23a2 \u23a5\n", "\u23a20\u23a5\n", "\u23a2 \u23a5\n", "\u23a21\u23a5\n", "\u23a2 \u23a5\n", "\u23a20\u23a5\n", "\u23a2 \u23a5\n", "\u23a20\u23a5\n", "\u23a2 \u23a5\n", "\u23a20\u23a5\n", "\u23a2 \u23a5\n", "\u23a20\u23a5\n", "\u23a2 \u23a5\n", "\u23a20\u23a5\n", "\u23a2 \u23a5\n", "\u23a30\u23a6" ] } ], "prompt_number": 29 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also couple more than two spaces together. See the `JzKetCoupled` documentation for more complex coupling schemes involving more than 2 spaces." ] }, { "cell_type": "code", "collapsed": false, "input": [ "jzc = JzKetCoupled(1, 1, (S(1)/2, S(1)/2, 1)); jzc" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$${\\left|1,1,j_{1}=\\frac{1}{2},j_{2}=\\frac{1}{2},j_{3}=1,j_{1,2}=1\\right\\rangle }$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAQMAAAAhCAYAAADdws5CAAAABHNCSVQICAgIfAhkiAAABSNJREFU\neJztnUuoHEUUhr/oFWO8uUaRaCJKjEJ8JWhQVNREowSJokGuIZBo0IggiCTqRtwoRA0+N0GRaBhQ\nLxd8LwQ1YhSCG3GjWSgI9tIXKAgufF0Xp5uZO+npruqqmq4uzgcDPdN1qs/fdbrmVFX3DCiKoozg\nC+DUtp1QFMU7dwILRu08puSz5cBkMHfSYDHwFnBW2454ICUtSjVnAtfbGGTAihCeJMI9wGPAHN0/\nTylpUepZBszYGGRoYJiQ0gWUkhalmlngtLIdZcOEOhYBXzs442oPsM9DHTGRkp4YtPiIMYhDiy11\n2g8Ad5ftsO0MLgM+B1Zb2vmyLzghrycVUtLTthZfMQbta7HFRPtBYAMlE4kThgc5H3gW+AX419JB\nH/bD7PRQR0ykpKctLb5jDLrTLjba54BPgI3AR3UVZ1SPH3t5hU1xtY+FlMbZKWmBdGKsCT3qtS9F\n5g7m0WTOQFGUbvMzkkEsG/zQdJgQA2uAuxABPwK7WvJjG3B1vr0XOIxMNNkSg56UtPgiJS1VvIoM\ng/YUH3SlM1iOzIA+iIyPjiBivrGs5wCw1tJmF/DZwPs38td9lvUM4kOPapmvxQe+4syGtrQfAh4B\nngT+G1UoI745gxeAJfn2Lbn9Kgcf2iYlPTFq6dEsRmPUYksPc+0PA5uKN12ZM3gZ+D3fXofMmn7X\nnjvOpKRHtXSXHjIkArrTGXw7sH0t3Vr7LSMlPaqlu/yK9AEnQ3fmDApOAi5GerQm7AcusbR5iH5Q\nmKRfI58KK8FFj2oJd7G6xpkNbWqfQLT+NqpAhtucwSpgYSD7m3Lbi0r2xfj0Xd25KNNzObAbeYDo\nIJKuxkATLeuAO5AHol4Hbgjm3Xx6uMWojzhrqx17mM8ZbKZmtSSjujOYzQ+2qGTfdfm+NwPZP4OM\n44a/sWJ8+s7kXAzrmUSW+Aq2AH8CZ4Rw0IImWkDS0B359u2IlsUhHBzCNUZd46zNdqzSPsw7wClV\nBTKOFroUuXXxSH6gOeRkHULWqgsuyD//wbN9wZfA2xW+x9QZ1GmBo/WsQZZ5zsnfTyGatoRw0IIm\nWkC+WU/Mt6eBvwnXGfiKMXCPs3G3o6n2QVZgMAzKcL+gHg9gPwX8AzxQYTeOzsA2/Rt1Lsr0LMjr\nL76RLkQ02Y4pTQmpZZgZ4FFL/0LioqUuzsbdjk14AriqrlCG+wW1t76Ikf00/XXe7cgtlGdX2IXu\nDJqkf4PlbfW8Bjxn76YR49KyFlnP3o9Z6jouXNrFNs5CtmMTJoCPTQpmuF1Q1wBbPdivRk76HmTG\n83vqM47QnYFt+jd4Lmz17ASexm5G34ZxagG4F/iKOH5Sz1WLTZyFbscm3Abcb1Iwo/kFNQE8T3Ph\ng/aTyKOWLwLvMXBzRAWhOwOb9G/4XNjouZn+47MLCaMptJYrgJ/of8Oel9c/7cF3F1zapcA0zsbR\njk14F+n4asmIx2lbxj2BGCL9W48E0On5azNwpedjlOFby6XAp8Dx+ftNwF/AuR6P0RZlcbYB+XGR\ngrbasY6VwCumhTO61xlsA15CGmkWwxTIkRDp30rgD/ozwsVryuMxygiVym5HbpDZjczOb/Rc/7ip\nirMZZKkO2mtHE55CMkIjMrrXGYybWNO/JqSkpW121BdpleOAD0ft7MqzCTGxHvl12Q+Q9O9Ghn4k\nokOkpKVtjiWu1ZIybgXetzHQf1QaTczpny0paYmBrbR/p2gd+9D2VRRFURRFURRFURRFURSlCf8D\nTSec3ofIo5IAAAAASUVORK5CYII=\n", "prompt_number": 30, "text": [ "\u27581,1,j\u2081=1/2,j\u2082=1/2,j\u2083=1,j\u2081,\u2082=1\u27e9" ] } ], "prompt_number": 30 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The normal operators are assumed to be diagonal in the corresponding coupled basis:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "qapply(Jz * jzc)" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\hbar {\\left|1,1,j_{1}=\\frac{1}{2},j_{2}=\\frac{1}{2},j_{3}=1,j_{1,2}=1\\right\\rangle }$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAQ0AAAAhCAYAAADDC/7xAAAABHNCSVQICAgIfAhkiAAABelJREFU\neJztnXuIFVUcxz/WRrbubhmhrVLZWthLKaksqmupSC9UYhOhLSmjCCKy+iciSqiUrOwPKWJLbliL\n0MMMispoqz+kiKCHfxRIDfRH9A6iKHtsf/zOcGfHuXfmzDlnZu54PnDZef3OPd85vznzO6+74PF4\nPAWxBzim7Ex4PB7rXAdMaXfyEIOEZwF9BvYHA/3Ai8DxZWfEAnXS4unMccBSFwkHwBwXCdeEG4H7\ngQm6/z7VSYsnnUFgTMdgGnAucHLKdQHegbJQpwetTlo8ndkBzEw6EW+erATeBz7E3Dl6gc9KtAfY\naiGNKlEnPVXQYsPHoBpadEnTvg24IelEvNLYBbwH/I5UHnk5R6UzvyT7kCNUOnWhTnrK1mLLx6B8\nLbpk0b4bWEJCh2hPZPtopMnRi1QmP6njAXBGxsycCjwC/AD8m9HGpn2cdRbSqBJ10lOWFts+Bt1T\nLjraJ4C3geXAm+0uOhxYpC6+DzhJfQbbXB/QuQnTVGnlxdS+KtSpH6BOWqA+PpaHJunaZyB9G5OI\nRhp/IcOoAK8C+2zkzOPxdC3fIxHJIPBteDDep9EAfkM6SKYD25H+jWeLyaMVFgBbkBry8RLzcQ3w\npNreBNyaM50q6KmTFlvUSUsnniHW/OqJXdBAZnoOAJuBjWp7RRG5s8AspMf3DqT9thcR/blmOtuA\nhZo2twPvRvafV59bNNOJYkOP1zJZiw1s+ZkOZWkfB+4GHgL+i58cQEKRe5HKol8d/xT4ICGxgOr1\naWwBjlLbK5T9PIM8lE2d9FRRS5N8PlpFLbo0ya79LuDycCfaPLlQ7Q8Bo0gzZSYyLDNuI5cF8BTw\nq9puIL3EX5aXHWPqpMdr6V6awPXhTrTSaKi/nwBfqe1lyDjt60XkzAJfRLYvprvGzpOokx6vpXv5\nEakrpsPkPo0G8DMSZYQsQ+Zr7Ckqd5Y4EjgTqSHzMAqcpWlzJy3nyRL2tV1FmICJHq/F3UNt6mc6\nlKm9B9H6S/RgL7CfVg95yDfICApIsyVKgFmfxjxgqiP7K5Rt0qS0Kq7WTLsXSXoWAeuRhWS7aUWK\nZZNHSwO4FlkY9xzysiqCJmY+asPPyirHJtn7NFYhnaqTWKoSuCxy7AR17GZgBFkuGyWgc6WxQ9n3\nJpy7RJ17wZH9ZqSdGX8DVnG1ZpZ7EdfThwx9hqwG/gBmu8igBnm0gIS/a9X21YiWftxj6qOmflZm\nOXbSHudlZMb4JDYAfyJz6ENmIzdkFzKsFCfgwBsyA5lyuldlaEKlMY6M9Yecpo5/bdk+5CPgpTbn\noFqVRpoWOFDPAmT4a67aH0A0rXaRQQ3yaAF5U09T28PA37irNGz5GJj7WdHlmFV7lDlYbH4FmD94\nGxzYDwD/ALd1sCui0tANO9vdiyQ9U1T64RvudESTbps3Ky61xBkD7tHMn0tMtKT5WdHlmIcHgQts\nJRZg/uBtSr8kk/0wrXHyEWS+yYkd7FxXGnnCzuj1unq2A4/qZzMTRWlZiMwHGCVbyFwUJuWi62cu\nyzEPPcBbNhMMMHvwLgLWWLCfjxTOA0gP7z7SIxjXlYZu2Bm9F7p61gEPozeCoUORWgBuAj6mGj8l\naapFx89cl2MeriL/koFEAvI/eD3AY+S/QVH7PmQJ7xPAK0QmoXTAdaWhE3bG74WOnitprQuYihtN\nrrWcB3xH6419ikp/2ELeTTApl5CsflZEOeZhJ1JBWiOgOuJ0Kboj1EXYuRhxtGPVZxVwvuXvSMK2\nlrOBd5CfZgCZrrwf+VmGbifJz5YgP4ITUlY5pjEEPG070YDuqzTC1ZoTyJCT1dCrDS7CziFkmv9E\n7DNg8TuScBVCjyATkdYjoxHLLadfNJ38bAwZwoTyyjELG5EI0yoB3VdpFE1Vw8481ElL2axNv6RU\nDgPeaHfS5P+eeDqzGFnw9xoSdl5K+19Bqzp10lI2h1Kt0aEkViLzs6zj/8Nae6ocdupSJy1VYA3l\nz9xNYyu+fD0ej8fj8Xg8Ho/H4/F4PB7Pwcz/kmrHd1bpdrwAAAAASUVORK5CYII=\n", "prompt_number": 31, "text": [ "\u210f\u22c5\u27581,1,j\u2081=1/2,j\u2082=1/2,j\u2083=1,j\u2081,\u2082=1\u27e9" ] } ], "prompt_number": 31 }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Uncoupled States and Operators" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Uncoupled states are defined as tensor products of states:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "jzu = TensorProduct(JzKet(1, 1), JzKet(S(1)/2, -S(1)/2)); jzu" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$${{\\left|1,1\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},- \\frac{1}{2}\\right\\rangle }}$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAHQAAAAgCAYAAADDhVzGAAAABHNCSVQICAgIfAhkiAAABCtJREFU\naIHt2lmIHEUYwPHfxtWEJYl4EI2iWRPEI0QIoiGeaFSCBqMRokRUTES8Hgz44pNRFGM8UVEUo8GL\ngBoVDHiBK6KggooK+mCw34IXokJAYV0fvll2pjPd090zmZ115w/D9FTVd1R/U1VfVTd9+vxP+ASH\nTrYTJbkaA3kNZnTJkV7kCMxuUj4Hr+Lo7rpTyPZRWNE9d6YWCYZTZddhE8aa1O1ritiej5fLKH2t\nLZemFonsGzcZAS1qezsOy6pMT7mHZLQbwtel3Cou/ygG29A93XgW67Mqi6yhp+BDLKnoQCv5EVxW\nUfd05D2cKyM5ygvoCdiJmzFawXBR+TexuoL+6coY3scFzSrzprrvcFHtehuWlTRcVH4Un+NUfFZC\n/0ysxcmik2P4G2/g05K+TjWeE0vVO+mKXlm7tuJ+xQN6PtbhGbwogkmk/euwAbfjt8662TP8LAbC\nfOyur+iVfeif+AtHFmh7KbbgLnxsIphqOp4Ss8N2HFzSjyvxZO16M24pKd8OZW1vFX/cXEYyyrdp\nvHFlKSK/CHe3aHO4mG6GRIeOb9LmRrGnGzZxg5qRmLytSScYEAlSw6DslREKu8TpzVBOm5twH/aI\nwG3E0rr62/CHmIqTWrsF+8DXXmBMrKEr6wt7KaDwPK7KqR/G97Xrf0SA1+MM3IEfNJ6k7MR5Hfey\nd9iGa+sLeiUpqifv8Pnf1O9R3IovsENkuPXsVj47r8oSPK3F4XkdX+GGNm3+KgblQfid3gvoNWLf\nmkV6RtkPj4gEYgUu0RjU+fipoO0iOUJesL7B8oK2OmV7EAeqBZPOTbnHYVabOhaJEbUnp80uLK5d\nH4AnxFHYR+Jg+1ixbRnnQpE4FGGgwKeeTvS5qu1xVuGtPMUjGeXbxb+oWcJyTq3ulRy9efLjPCAe\nD+UxT/EsdwEey9GVqJ7lFulzN9ihxdZspO56nsiivjVxEvMLPhB7pnFOrJX/mNJVVB7mivWnCBfj\nSxyT02Yj3hVrSxaJ6gHN6nM3GRZJUS4jbRi4sw3ZjcolLyvE1mS5xuloDq6v1bV6GyGxd0CX1XzZ\nJKbqs1roaKfPacravgent1I60oZDmyvKzcBLFeRmilcyHsZDte8tOK2gfKIxoLM19mGtWM/zTq+q\n9jlNWduDYgZqSdEEIs2ZuKKi7BpcXlG2HRKNAT1JbIsW1X7PFcvE2gz5dvqcpqztNQoeS86p4Myg\nGCFF919pJusBd6IxoANi2hvvx2JxU5fam3b7nKaMbXhdbFf61JHIT4pewINd8aSc7YUiR+iTIpEd\n0A1iPe7UCCxDK9v36t7p15Qi0Tygq0w8lpqV0WZf0cr2/ng7T0GvHc5PNmeLN+p2ikd1K8XxYa/Y\nXi1e2enThPSb8wvFA/Kx1GduF3wpavvxLvnTp0+fPn36TDv+AxMh2obP427zAAAAAElFTkSuQmCC\n", "prompt_number": 32, "text": [ "\u27581,1\u27e9\u2a02 \u27581/2,-1/2\u27e9" ] } ], "prompt_number": 32 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vector representation of tensor product states gives the vector in the direct product space:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "represent(jzu)" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\left[\\begin{smallmatrix}0\\\\1\\\\0\\\\0\\\\0\\\\0\\end{smallmatrix}\\right]$$" ], "output_type": "pyout", "prompt_number": 33, "text": [ "\n", "\u23a10\u23a4\n", "\u23a2 \u23a5\n", "\u23a21\u23a5\n", "\u23a2 \u23a5\n", "\u23a20\u23a5\n", "\u23a2 \u23a5\n", "\u23a20\u23a5\n", "\u23a2 \u23a5\n", "\u23a20\u23a5\n", "\u23a2 \u23a5\n", "\u23a30\u23a6" ] } ], "prompt_number": 33 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Uncoupled operators are also defined as tensor products:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "jzopu = TensorProduct(Jz, 1); jzopu" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$${J_z}\\otimes {1}$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAADQAAAAYCAYAAAC1Ft6mAAAABHNCSVQICAgIfAhkiAAAAkRJREFU\nWIXt1k+IjVEYx/HPnZGpMRaGJkppFmIi0YTxp8mfbNCEBeXPCiOahYmyUSzGQlFqGgv/ZrKQlWaL\nQilCEYmVspOGBaKYZsbieSe3y33v7b5TE+a7ed/znud9nt855znPOfxnLMFtPMIwRvAc7eMpCrV4\nkcVBDh/xdEzkZGMpnojJ/Y1JZTpZiHpcHCNRldCEMxjAUDGjcge0JnnezaYJ1GA7msUsj+A7+kVq\nF+M1NiXvfVieRcQNDGJKFifYgF6sEmk8ylQcwAVML8NPnyIpVw45fMDDSh0kbMUzNKbYdIoiVF/C\nV58iA6oqQ8giMWtZ0m0m2sTKHMf8P9gcxBfsx6lKA5UzoLXJ816lQXAIp/FNCO8UR8IoR/EJl/A2\nsZuTIV4q/fgh2/65WtCuRjdW4wS2FPSvw94Uf30qTLkqtIq6/7WEbRrDBe0hHEZP0u4v6H+HhkoC\n5Q+oRWz+I3nfFmMa7lTivEgcYoXOoUMUncIVmoX3WQPtFpv/c963PWI2r1XiPI83WJC8T8Z5XMF9\nnMRc7Myz3yiqXSY6cCyvvVnsnY6sjkX69Io72GXFq9w+UQy6S/i7LvZQbZpRNbrE9aJH5HVrin2X\nuIasFGW5pYSINuWdQ7dEmhfSgJt46dcNY0AcJ7tKxC5JI7ahTlSjZWX+t16U5hV+vym0J30zsgjL\nlTZJZXQPFlaxNGqwQ5xDI4mGQZERDzLqyUROpCnMFkL/WprF6T9P5PbZ8ZWTjSZRperwGK9E2Z1g\nggn+AX4CKbZnF2vuuR4AAAAASUVORK5CYII=\n", "prompt_number": 34, "text": [ "\n", "J \u2a02 1\n", " z " ] } ], "prompt_number": 34 }, { "cell_type": "code", "collapsed": false, "input": [ "qapply(jzopu * jzu)" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\hbar {{\\left|1,1\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},- \\frac{1}{2}\\right\\rangle }}$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAH4AAAAgCAYAAADUp8wPAAAABHNCSVQICAgIfAhkiAAABOJJREFU\naIHt22uMXVMUwPHfUFrVVpSUauiYEtqGaIKmHhXpEEQUHxDPaMU7oolIfGorRD0rCFEt9Yx4E/VO\nTIgmCBrEI1Hut8Y7WhokNT6sc9M7Z+6599xz79yZcv/JzZyz91p7rX32a+2zz9ChQ4eqrMXuw+1E\ng5yPrloC27XJkW2ZvTCuSvp4PIN92utOLtt7Y1773PlvUkJ3Ku0iLEF/lbyhJo/tyXii0YJ3xuHY\nv4bMs40Wug1Tkv2Ah6Ph89p+EntkZaan+vl4B+/XKXS3jPSx+LSGXj1q6d+FUU2U/X/jQSxoROEO\n/I7RNWT6qqQdhg9FTyxCPf3TcWbBspuhZNsc8V14U0aQVzniJ2IjrhIj72fRAT6v48B0rMEV2JLH\n44L6L4oZqUM++vEWjq8nOBqzE4XF2C/5Ta4i25dRxmrFR3we/UUi/miE0TgPd2K5mNFuEnXNQ8m2\nOeJhkljrB1G5Zv4lti7wEr5p1rMhYBVuxQc55Y/D2ViJx2ztVOOT9IW4Tsxu/0V+ELPoZGyozEgH\nd3OxSQRYu+JR/IGHh97HXGwU/k3JIXsabsH1eM/AmWQT7seXYkRMbNCPc3Bfcr0MVzao3wyN2l4l\nOnhNPsJrotFXYoZYW39NyfVl6K82tFM9TMMNdWT2xEMiVlmFA6vIXCb2xN22PshqlAzfdN4KykHe\ngEFeeTMBh4jRca1YT78Qlf66LS7mY71YksbWkLkcN2OzaOBFmFWRfw1+E527lMhNHQJfRwL9eB0n\nVCZWNvxRyX0PHhDT4R44CG+3x8fcPCICtiy68VVy/bfoCAtEHReL+KXyzdYa9Lbcy5HDalxYmVAZ\n3M1N/q7Dt8l1r5gqXhlqzwpQ6xDin9T9FlyNj/EcXkjlb5A/ym+Wg7BCnUOUCtbh0iZt/iQG9a6S\nZTvd8L+I0V6mV0S8a5s03GouEPv+LNJB6/ZiO3elOLw41cDGn4zvc9rOE8PUatTPMCenrVbZHoVd\nVMRq5Qc0FofiKbHelenFq2LE9BTxNMUBGNNkGdPECN1cQ2Y9ZibXO+Je8QrzXXHAsb/YzpU5SQRA\neejK8aukFXUuarvMyXi5WsY80ZtOrEibmqRdgnPFUV+ZvgwDTyY61QKvY5O8pzN06+mXuS3lSzUm\nyR/VT8XdNcoqKR7V56lzO3hOxpZ1Kf7EThVpU/Cj2M5NT8n3VVxPElHj56KS/Yne22LPWWZGkv5d\nqqy8+sTOY0X1ug3iFHyCfWvILMIbYu3LoqR4w2fVuZ10i+CuJfQ1obu0Cd1FGgvC5okt2xwDp8Hx\nuDjJq/d1Tcnghp+d+LJELBFz1aaZOqdp1PaNOLJVxvua0F1WUG87PF5Ab7T4FKn8nn65eKN3RE79\nkoENP87AOpwh4o1abxOL1jlNo7ZHiRmtZeQNhNIcjbMK6o6UY9mDxXZxWnI/QSxPZ2ToN1PnNI3a\nPl2LXyePL6AzSoy4vPvXNMP1IUbJwIbvEtNtuR4zxcOfZTDN1jlNI7bhebGN61CAktrB3aO4vS2e\nNGa7R8QwHQpSkt3wC0W80KoR3Qj1bDfyzUGHKpRUb/iTbT3uHJMhM1TUs72DOGXNpPNdfTGOEQdY\na8QR8Amqf6k0XLbni/cvHZog/Z80PeLksj/1m9AGX/LavqdN/nTo0KFDhw4dRhj/AllqCrlzA7Px\nAAAAAElFTkSuQmCC\n", "prompt_number": 35, "text": [ "\u210f\u22c5\u27581,1\u27e9\u2a02 \u27581/2,-1/2\u27e9" ] } ], "prompt_number": 35 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Coupled operators which are diagonalized by uncoupled states (e.g. $J_z$ and uncoupled $J_z$ eigenstates) can also be applied:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "qapply(Jz * jzu)" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\frac{1}{2} \\hbar {{\\left|1,1\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},- \\frac{1}{2}\\right\\rangle }}$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAIcAAAAgCAYAAAA4/mtcAAAABHNCSVQICAgIfAhkiAAABRlJREFU\neJzt22msXVMUwPFfq7RoK0pKCX1eCdUQEkNqqEhLSsT0ATGGijmiiUh8okLMKggxlFJEzETNiRei\nCWIIYkgM95uYYwwSng/rXH3vuPfcfc69792+9v6Tm3fO2Xvttfc6e6+99t7n0aNHjxFlFTbvdiUq\ncDLGNUscn7ufgkex7UjWaC1kK0xuktYtm6bo3QbzUwo7HZdiEH1tVmxdo6axzbpl01S9M/BgmYJT\nGrIx9sIOBXkeK6N0jFNTbLNuDbgUvQ9hi0YJ+WklhSPwKt5ooXizgrSN8H4F3SnyN2FCG2Wva9yN\n01Izp/S2G/ArJhbkGWjyfE+8lempQiv5o3FsxbKrUjN2Pcc4vKRBYFrWc0zDzzhfjN7vRSf5MEF2\nNlbiXPxdUm8Z+aeEd+uRxiBexsGpmfuapE3E3lmeS7B99pvRIO9AgY7lqnuOFPnFIiZKZSJOwo1Y\nKjzjlaKtKdSMXc8B00XsMYyyc/OfYtkGT+OzkvKjxTJcizcT8h6E43EX7re6003Jni/CxcJLrq18\nI7zxDHxVfzh0WjkBt2XXV+G8JgXNwy8iINwUK/Ab7u1sfdviZ1HHrVvkOwrX4DK8brg3+gW342Mx\nqqZVqEeqTTtNFb3LxEBoi7fxvOgYd2FnMc//mMs3UFDGciM7rcAsXF6QviXuEbHTMuzUIM/ZYs+g\nz2pjN6Jm7O8N1QPT/xxG2YB0KnYTo+wiMbd/JAzzaUeq2Dk+F1PgRk3Sz8HV+F10gsXYfUj6hfhJ\nDIBalm/mCNV1TWAQL2Bh/UHZzrFfJtOPO4Xr3QK74JXO1LGj3CcCzUb04ZPs+i/RWU4TbbxExFND\ndw9XYsGI1HLNYTlOrd+UDUjnZX/fwxfZ9QLhkp5tt2YjRLODpX9y93/jAryDx/FkLv0r6auXTrAL\n7lBwMJbjPZzVps7vxODfFD/WO0er+btewXn4QXiNOgtEJL+qzYqNBKeIfZFG5L3memIpe544jDrS\n8A4yA1+X0J0SUxW9+A8wt4S+TuidgE1k8eP4IZmLfsTcvQceFvNvnQV4Toy8/tQWtGBHTGqzjFli\ntP/eJP1zzMmuN8CtYiv5NXFotYNYytY5VARsqbSy6dAX1In2VtGb5zA8U0XpfNErDxnybGb27Eyc\nKI6A6wwUlPVQJtcoWDwwS3ukonyd63L1yTNd+mplJm4uKKum+molpb2jxeOqLdktwR/YcMizrfGt\nWMrOzuUfyN1PF9Hwh8IYg5nsK2JdXmfn7PmXFeWJVdUdCW06HO9iu4I8i/GimIebUVO9czRr72jT\nJwLShuwtDHGpcJ/zmmVMZKBN+SVtyC6WHjzOF8vVuYa73Ck4I0tr9ZVXTePOUcam7bS3Hb11rsC+\njRImi520OseIubrVDmMRA23IMrw+ZRiPB0rKTBSfzNXPVZaKndN9EuVr/t85ytq0anvzVHmXE4R3\nbMiuYmk3K7ufKtz2MW1Uskzwlmd/HFdRdk05si9j03bam6fKuzxawRb7OOGK6m51Tlbg7s0EEphS\nUW6CGL2p6/s83fjYp+b/nSPVpu22N0+Vd/mEWMImsQLXV63dOkhN64C0WzZtpbdfxFVJLBLzbad6\n8rpATXHn6JZNU/Qmf7dymNXHtpOM/ZPG0aKmua26ZdMUveuLE/aGDN1CPkAcoq0Ux9kLNf7Cq0c6\n3bJpqt4jxB5VIf3ihHUw95vaocqu7TT6j7du2bSM3ltGoT49evTo0aNHjx6F/AvANSXYBnfGVAAA\nAABJRU5ErkJggg==\n", "prompt_number": 36, "text": [ "\n", "\u210f\u22c5\u27581,1\u27e9\u2a02 \u27581/2,-1/2\u27e9\n", "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", " 2 " ] } ], "prompt_number": 36 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Rewriting states works as before:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "jzu.rewrite(\"Jx\")" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$- \\frac{1}{4} \\sqrt{2} {{\\left|1,-1\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},- \\frac{1}{2}\\right\\rangle }} - \\frac{1}{4} \\sqrt{2} {{\\left|1,-1\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},\\frac{1}{2}\\right\\rangle }} + \\frac{1}{2} {{\\left|1,0\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},- \\frac{1}{2}\\right\\rangle }} + \\frac{1}{2} {{\\left|1,0\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},\\frac{1}{2}\\right\\rangle }} - \\frac{1}{4} \\sqrt{2} {{\\left|1,1\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},- \\frac{1}{2}\\right\\rangle }} - \\frac{1}{4} \\sqrt{2} {{\\left|1,1\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},\\frac{1}{2}\\right\\rangle }}$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAA8EAAAAkCAYAAACpKTisAAAABHNCSVQICAgIfAhkiAAADd9JREFU\neJztnWusJEUZhp+FhSULu8SVOwJHlovKTRRFEEVYEQWCioJGIgQwCngF0Wg0ClkJaLiJqJEojKC4\nBkSMouB1jAkRjVc08kNixx+iYEAlEEQRf9ScnJk+fa+qrqrp90lOzpnpqa7vdD9TX3d1dTUIIYQQ\nQgghhBBCCCGEEEIIIYQQoXjKwY8QKXAI8l0IIcQwUM4TKSFXRe98G9gqdBBC9IBcF0IIMRSU80RK\nyNc5YbPQATTklcCPgcdDBzIg7gK2Cx1EB04DVoQOwgK53j+hXE/d1XlC7Z2wRe1IN5TzuqE2Kwzy\ntRtRto+pnASfC3w2dBADYxdgm9BBdGA3YEPoICyQ6/0TyvXUXZ0n1N4JW9SOdEM5rxtqs8IgX7sR\nZfvo8iR4DXALsLvDdQK8GPgt8C/H6xXd8LWfXdV9HXBmBHF0Qa7Hh82+rivbh6tlhPwep0bMbV4o\nh+RPc2y3VQwOKOelQ8ztFchXMYvPYyzoKUe+BbgQc7P3guN13wxs73idop6M5fvS536uo03dm4Ad\nI4ijLXI9DBnF+9JmXzct69PVMkJ+j2Mlw70DtsTqkPwpJsN9zozBAeW8OMnQMZptHG2Rr93JcOtr\nLL7N4Fq6g4BPOlzf1xyua97JKN+XIQ9+mtT9CuCDEcTRBteug3xvSkb1vrTZ13Vl+3C1jLrYhuRP\nhj8HbInVoSbbRA4ZbP2JwQHlvLjI0DGabRxt0PmIHRl+fLXyLfZ7gt8LXF6x/IXArcB3gHuALwC7\nVnz+6RXLVmOGOYSiqv6rgZU9xpI63wOOJq3JF1y7DvH6LteXiNlVH/7si/H4SuAK4AaW99AOzQFb\n5JAcitmBMlLLecpb7pCvcrVPSn2L+SR4L+AJ4M8ly58HfAwz1vtVwGHA3sCvaN+j8ALMbG8HdAnU\nAXX1j4HX9RZN+jwFfB/T+5MCfboOYX2X67Ok5ip092db4AeYA4XzgPOBeyfvbTn1uTHDcsAWOSSH\nUnMgtZynvOUW+VqOXHVPb765HH7wOeBZFctvx4g5zcGTGDaVlBnnXj97sp4R8FP6f4B10/o3B27q\nKaZFMtIdagOwA+Ue9BlHE3y4DnH5nqrr4H+ojgtXD6R9725dbOPca1t/LgYeYDbOdcB/gHOm3ps3\nB2zpwyEf/oAcWsT3cGhQzgP3vqWat2Jvr0C+jnOv59lVCDscGkp8C3lJfA3wSMmyXSfL760ofyTw\nQ+AQTFIE0+vyD+DlDWP4A3D85O8RcGjDcq5oWv+TwM8xwy1+1nDdq4BTgOdjJHkK+DdwG3B3t3CT\n4gHMdtsZuD9wLDG4DmF99+k6pO27C1fPx0wSkbkJqRBbf07G7Iv/Tr33EMb9k1l67MQQHbDF1qE+\n/IG4HUrdH+W85dj6pmM0f8jXWWJ2FdL3tRffmpyRrwa+DHy64jOXYzZ0FfdgdsAzc+//FXi0pMy4\nYn0j+r8S3Kb+tZjeqCYcA1yPmc59egz8GuBtwLVU348A6fcygrkH4MMB4wjpOsTre13dbVwHe98z\nwl4JBntXRw3rmabtleB8fW38WTP5fNF34U7gn7n35skBW/pwaNSwjmm6XAnO1xmLQ7HnzL7aEdsY\n5jnn1ZWfJ99s0DHaEqHOR+rK951fIfyVYPDo26mYHtjFS//vKPncykkAGzG9LmsLPrMOc79PHVsD\nO+Xe22USw49Kyowr1jci7pNggMuonxjitZgeqPyXcZrzMDeKr6v4TMZysZruZx90qXsF5v90ee97\nKq5DvL43qbuJ6+DG94ziRtTG97ZlbV0d0TwRNI1tXFNfG3/2n3z+koJlt06Wrcq9n7oDtvTp0Aj3\n/kAaDsWcM/tuR2ximPec16R86r7ZoGO0WUKejzQp32d+Bfe+xuJbJ9YBjwHvLlh2EXBUx/Veirnc\nfXjJ8nFF2RHxnwSvx9x8X8ZOmN6a1ZiZ6YruYTgH83ytBZaGjhWRMR/PgLwAOC5g/aFch3h9b1J3\nnevgzveMOFy3cXWE+/9hXFNfG38On3z+ooJlN06W5Wf4HaIDtnR1aISf/39cU2doh+YxZyrnFTPC\n/4nFEH2zZai+jivKjgjvKsynrzO+hTobfggzBOFcZi+tb4MZU1/V01fGXpiegEuAu2wDjJT7ML1L\nq0uWnwt8HPOFPgfTO3Pw1PILMEPGPo8R8jFgD0+xxsIIOCNg/XK9G3Wuw/z5PiKsqz55cvK7KDFv\nMfm9ee79ITpgywg5NM0Qc+YI5bxQDNE3W0bI1xAMNb+OmPIt5CXha4B9gGOn3jubduPUF1mFme3s\nWvzdXxALNwBvLlm2wNLN+09gBD4TOAL4KPBHZmeFu512k0ykyN8xnj8tYAxyvRtVrsP8+R6Dq754\nsGLZ1pPfRROTDM0BW+TQcoaWM2NwYMg5b2i+2SJfwzHE/Drj2/Ts0AdgdtqKgkJF/BojSVd+A/wE\neCdwB0acY6h+GHURKzCX6+8APmIRTxf63maLlNX3v9zrJ4H3AL/E3NdwW275/fQ7Q3CI7bUS82zJ\nhwPGIde7U1Vf7L63pcjVPF8EDip4f3fMbI9PFCw7C/iFdXR2/A1zBa/oQGdrzCyaZbNzpupALO3d\nNKn6A34citmfrijnhSdV32Jos0LEMGRfU82vXZnxbfok+B7MA57b0mTcetlG/hTwVczQgQ2YXom2\n4+A3YqYG3zj13mmTdfmm6zaz4XTg7SXL8lf2NweuwgzL2AC8hllpd8YcWDTBZj8vEsKxE4BvRRCH\nXG9PlesQr+9dyxa5muf0kvdHNHvEjYvvcRcexUyusVvBsr0wBzJFpOoA2H1nfDmUqj/gx6GY/XHp\ngHJef6TqG8RxjBai3YRh+ppqfnXmm4vh0Csa/JTxdeAvwLuA19P+wdlnYHoqNubeP6LlepqwL7CV\nh/W2YT2mp+WxkuX3AftN/t4S+AxwHaaH60Jgb+BNU58/DjNTWhPa7meX28vGMZcN0FBch/C+17kO\n8fie31ZdPekjWdo43IYif27H9BJP17Eec1JzS8E6UnbAllgd6ssf6MehWPyBONsR5bx2pOybDTpG\nS+98JOX8GotvTvgQRpy2wxmOxozt/lLuZxPwlZIy44r1bcL0LhTdJH7UZNnNLWNsQ1X9i1xGcS/4\nIjvQfCa3PTA9X2VkwELF8ir62F5NWMBc4YiFPl2HeH134Tq48z0jvOsL2Lk6ovv/UMa4YlkXf3bG\nDFmdvgfpKuD3mASbZ2gO2LJAd4dGuPcH4ndo3nLmAsp5ZdjmPB2juWeB4fo6rlhfDK5CHL66dHWB\nHny7nHY9H9tjbq5u26vxMGbDFP3ke2IWGede7wDcCfxuquyDmNngTp363HMm7/+pZYx1NK0fzDPM\nrm2wzhNp9kyv71I9EUHGcmEPnZS9ENPb89KSsj62V9O6p7kY83BvX8TsOsTluw/XwY3vGcWNcxPn\nyrZVW19tXR3RPME0jW2ce+3Cn+diruZdiZlR8laKE/E8OGBLnw6NcO8PpOFQzDmz73akjqHlPB2j\nNUfHaGHPR2I+xoLuvrpqG8G/bxyJGQ/+Mp+VWDC2LH+RiyA6ch7NbzjfgDk4OIzZYQFrgLdOlm1X\ns46MWWG3wTz3bJFTMMMoqh627Wp7dal7JeZL6YvYXYd0fW/jOtj7nrG8cW7r3PS2alvWhasjmp3E\ntIltbBmTjT+pO2BL3w6NcO8PpONQjDkzRDtShXJeNan7ZoOO0doztiyfUn4Fe19t2kbw7xvbYsbS\nj5lf6S6t/4gXNsM8x6wNqzBj368Erpj8/gTVD5qfJmNW2AMxw0TWT16vxfQ0nVKxDlfbq0vdJ2Fu\n3vdBCq5Dmr53cR3sfM9YfgLQ1rnpbdW2rAtXr8Y82L6ONrGNLWPq6s88OGBL3w758AfScii2nBmi\nHSlDOa+aefDNBh2jtWdsWT6l/Ar2vtq0jeDXNwDeh9lIY+KVrukN20W8BHijq0BachLwhp7rzJgV\ndgWzE5Lsh5HuYIpxub3a1g1mkoNtHdWfJwXXIU3fY3Ad2jmX31ZtffXpap42sYXyZx4csCVWh9rG\nNWSHbHNmTA4o51UzD77ZoGO09gzJVbDz1bZtBM858kRg/8nfY+KVbk3HcisxPR9VM4755GpmH2fV\nBxnVw+NupPwZar63V1XdAHtihmf4IBXXIU3fY3Qdyp1rsq2qfPXpahOqYgvlzzw6YEusDtW1xXJo\nCducGcoB5bx65tE3G3SMVs+QXIXuvtq2jeA5R+6CuUS+yJh4pRPNySgX9izMMIgQJ0lN6r4EPw/s\nluvzSUZ142zje11ZX642IeT3ODYy/DlgS6wOyZ9ZMvzlzFAOKOfFS4aO0fLI13jJ8OOrlW8unhN8\nLOZm5A9MfvbBPDfqeAfrFvFxwuT3+zHDTRYiq3sLzJCIuz3UL9eHh43vdWV9ulpHyO9xasTc5oVy\nSP40x3ZbhXRAOS89Ym6vQL6KWXweY0GNby4uiV+fe302cBP2N32L+DgS2BHzeIqdgBdhZt/LIqr7\n1cA3PMUg14eFje9Nyvp0tYqQ3+PUiL3NC+GQ/GmO7bYK7YByXlrE3l6BfBVL+D7Ggh5z5DMwl6Qf\nB77J0hm6SJO7mJ3yfE/gEZY/A21tD7G0qfuaHmKS6/NF3nWw871p2T5czRPyexwzrh2wJVaH5E85\nrnNmTA4o58WHjtHKka/x4dLX2HwTQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBC\nCCGEEEIIIYQQwo7/A1+H0IgzacuTAAAAAElFTkSuQmCC\n", "prompt_number": 37, "text": [ "\n", " ___ ___ \n", " \u2572\u2571 2 \u22c5\u27581,-1\u27e9\u2a02 \u27581/2,-1/2\u27e9 \u2572\u2571 2 \u22c5\u27581,-1\u27e9\u2a02 \u27581/2,1/2\u27e9 \u27581,0\u27e9\u2a02 \u27581/2,-1/2\u27e9 \u27581,\n", "- \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 - \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 + \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 + \u2500\u2500\u2500\n", " 4 4 2 \n", "\n", " ___ ___ \n", "0\u27e9\u2a02 \u27581/2,1/2\u27e9 \u2572\u2571 2 \u22c5\u27581,1\u27e9\u2a02 \u27581/2,-1/2\u27e9 \u2572\u2571 2 \u22c5\u27581,1\u27e9\u2a02 \u27581/2,1/2\u27e9\n", "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 - \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 - \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", " 2 4 4 " ] } ], "prompt_number": 37 }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Coulping and Uncoupling States" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `couple` method will couple an uncoupled state:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "jzu = TensorProduct(JzKet(1, 1), JzKet(S(1)/2, -S(1)/2))\n", "couple(jzu)" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\frac{1}{3} \\sqrt{6} {\\left|\\frac{1}{2},\\frac{1}{2},j_{1}=1,j_{2}=\\frac{1}{2}\\right\\rangle } + \\frac{1}{3} \\sqrt{3} {\\left|\\frac{3}{2},\\frac{1}{2},j_{1}=1,j_{2}=\\frac{1}{2}\\right\\rangle }$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAYcAAAAkCAYAAABi4faKAAAABHNCSVQICAgIfAhkiAAACSZJREFU\neJztnWusHVUVx3+33Kal1iKkPGuxVBRUKFoxVhvLRY1poKl8wKJRXhYIMT6hMVH8IJGo8QUigWCF\nDqBY5OELDIJfKAmWBKLBB0/N+IgGMI2BWFEe1w9rxjvnnJk9e8/eM7PvOeuX3PR2zqx91jpr/8/M\n3nvNvqAoiqIoiqIoihITxwOzAX4UZVJQzSgTwc+AxX07oSjzCNWM0hsLOnqfjcA9wHOB2rsPWB6o\nLWU8OAOY6tuJgKhmlLYxaqari8NHgKsCtncYsDRge8r8ZyXwrr6dCIhqRmkbo2aGLw4vB24BDg/o\nwHrgIeCZgG1W4eN/G7ErYbDJzbXAh7txZ4D5rJkNwOnAOcB3gXc3aEN1Ey91ubHWzDnA55EFrFUB\nHMu5GTgwYHsAKaM++vjfVuyKPy652Qkc3LI/Rea7Zv4BnJn9/j5gL/KFYovqJl5sc+OkmZCJPg74\nZqC2iqRU++jjv3byeLHJzXuAz7TvygjzVTPHAC/Lfj8VeB63i0OO6iZe6nLjpJmQib6e+uHma4Eb\ngWuAK4GvU99BU+K/OFyBTA30zRL8/YghFpvcTAF30/3CdNeaWQfsQPSyA7gJuaiYSDH7eCNwkZWH\no4SKP4Z+BmE0A3HEU5ebSs1Mt+QQwJHAf4E/G855PXAn8H6kmuIQ4JfAs8iQaD6zL1Jt0idvQb5A\njvVsJ4ZYbJgFfoHcDf28Z1+aYKOZNwGfQ+7080qmq4B7kTWEXzu+51rgncC/gEsdbUMTQz8LpRmI\nI546nDQT6i7gauBow+vTwKPAJwvHVgJPA5+oaTsl/pFDn7wOuANIgN2Mx4NQtrk5CJlHbcoa3G+a\nutIMyBf4LHBa4dim7NjlBrsUs4/nAQ/SrKJpHHQzjpoBu9yUasanlNU09bMie/0RwzlnAEcgycj5\nC7IQ18ac6yTxMHAycBbmHIwjTwEvAoc2tL8AeGU4dwbw1QzAr5Aqpj2FY/kX+l4HX9YBTyIaBNiF\njCI2OrQxTqhmhjTT5OKwBPge8GXDORcgawcmPgA8AfyzgQ+KYuIaYGvfThQIpRmQNYn9kHninLWI\nuL/v4NMLwO+Av2X/X40sSLtOSynjgVEzH0TmLmeRIcZHS86ZRuY7v4CsCywrOecA4LYaR6aAfyPz\ncRuAS4DLgB8hc6p1pIwOlWz8r8LHtsgaZNi/E4knBhKaDZFjiaVJbvJFtiY3Pwn2UyRdaqaKI5AR\nwHk156WMxvUh4ELgU8CtyLyzCyF0E0s/K5LQfFoplnhcc+OjmQEOQIawZWsDFwMn1tgvR5x+GDi/\ncHwGEdAbauxT4pvjPAzpDAsQ/2dptqh1LXL35vIzY2gvwb2jxxqLC9uAkxrYJbTTt3w1M8wm5ILz\nEPBZ6kWdopppUzMQbzy2NNXMCNuRBeViCdRSZFGnjoORD+45ZEW/yF+B22vsU+Lr6JcCr8h+34zE\nd1R/7vyfBPeOHmssLixHHiZzJaG9vuWjmSqmkTu+3Zj3TkpRzdiS0OziEGs8tjTVzAjHIcEXF7C2\nIR9KHQsz29+WvLYb+A+wyGCfEl9HL1aZfA1Z5ImBBPeOHmssrtwK7O9ok9Be3/LRjIkNWbu3GM5J\nUc3YktDs4hBrPC400Uwpu5i761mE1MnaPoD0FFKbPcw9SGJM1SYp8XX0Ig8Q6AocgAS/sryYYnFh\nGqnfruI6yofne4DfV7z25gB++WgG5AtozdCxZUiOX6K6FDVFNWNLgn8pa0zx2DKgmbyeu+6DqOq8\n30KezjwS2d3veou2cnYDryk5vggZOTxt2Q6W71kVg49tGfsBb2SwRBekTHEHUpVieshpO3aL8kUu\npJ2HbapieSvw9uz19cjc964Se99YfHKzCfP05JkVxxPkAcy05n2b+uajmWVIKetCZLriD9nxFwvv\nt49lW779PqRuxkkzUB6PrWagP93UacaJaWSN4HJk3tO2Y4KUsu5lcM1hCilt/UGNbYrfXdBRNP8j\nKnW2JyPJOaZwrM8NyhLMncUUT1ksSxksydyC5HFFcxdb4TZkEdiVhHZz5KOZxUi56eMMxpb/xbj7\nDbYpqhlbEpprBkbjGXfNVHIRMpw9v+7EIRYgW2VsKxzbgowYVtXYphbnVHEikrgmQz4b268iMZRd\nmfvo6Duz911S8lpdPGWxrEHy/ers//mUxpYQzgZiFaN3obYktJ+jppoB+CLwMQZzcgNS5bfWYJei\nmrHFRzMwGs+81Exxm4ANwKuQaZ2Z7ETTnG3Ot4Gzhxu24CXkCvsN5IN+Hvkw11E/pC/Ddtj2JLJV\n8fGBbIeZyWz7fPz+IOQLYwVzZcF/QgoAvoM8kAX18cwwGstvgLcBf8z+vzL79/EAfpfhMhzPORcZ\nmrdN15oBKVs9C9ko7wVkP7I9yHrIY45t+fZ7G/tJ0wyMxtO1ZsBdN0bN+O7t3iUpg3cTTYZtFwew\nHWYZItiPV7we6x40ZfHUxZJzA3ZP9jahSW6mgbs83jPBPkeTqpkm9pOgGbDTTZuaAffclGqm+ODM\nDHOlcLPIotd8YTXwaeaGbXciaxnrDTZ5qayPLcjumHkt82Zk9PNTW8cjIY/HNZatwN8ZnBoMSZPc\nbAZ+4vGez2D/d5tnmEzNNLEfV82AWzxtawbcc+OkGZ+93bsgZfBuYgoZRuVzfPnTiVUr/u9Atgn3\ntT02O/cSZPj2BNV3FBDnXVAej2ssm5jbi2Ux7cTlmhuAHyL+d80kacbVflw1A27xdKEZcM+tlWbW\nIle07ZQvxsRCivmDNQ3bppF1jqoyOxfbpcgc85XIvlBnG3yC+Dp6MR6XWE5AOvkh2c8pyJxq29QN\nx1cj88Ndopox24+zZsA+nr40A+bcOmvGZ2/3Lkip7jBbga/Q7K+B+diaCLWxXwysRipjZod+yjaU\nC4lNbr6E3DH1waRqJoR9GaqZMNTlplYzw3u7H404f2ogB0OTUt7RfYZtXQ35FHdscrMQmVvtCtVM\nGHulPepyY9RMviA9Dnu7n4Bs6HcHMmzbiP0ffPGxVdrFNjfvBX7coV+TrpkQ9kp72OTGWjO+e7t3\nyX0M7kDpM2zrc8inmHHJzRUVx9tkUjUTwl5pD9vc9KEZRVEURVEURVEURVEURVEURVEURVEUZdL5\nH3bROXO1FYCoAAAAAElFTkSuQmCC\n", "prompt_number": 38, "text": [ "\n", " ___ ___ \n", "\u2572\u2571 6 \u22c5\u27581/2,1/2,j\u2081=1,j\u2082=1/2\u27e9 \u2572\u2571 3 \u22c5\u27583/2,1/2,j\u2081=1,j\u2082=1/2\u27e9\n", "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 + \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", " 3 3 " ] } ], "prompt_number": 38 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Similarly, the uncouple method will uncouple a coupled state" ] }, { "cell_type": "code", "collapsed": false, "input": [ "jzc = JzKetCoupled(2, 1, (1, S(1)/2, S(1)/2))\n", "uncouple(jzc)" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\frac{1}{2} \\sqrt{2} {{\\left|1,0\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},\\frac{1}{2}\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},\\frac{1}{2}\\right\\rangle }} + \\frac{1}{2} {{\\left|1,1\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},- \\frac{1}{2}\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},\\frac{1}{2}\\right\\rangle }} + \\frac{1}{2} {{\\left|1,1\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},\\frac{1}{2}\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},- \\frac{1}{2}\\right\\rangle }}$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAmYAAAAkCAYAAAAjI/bEAAAABHNCSVQICAgIfAhkiAAACyFJREFU\neJztnWvMHUUZx38thZK2b42VSwsBjrSKiqB4IwWU0IoiElGUaiRCoEalXgtoYjTa5tUUTaFQ8Ua8\nHMFLjVggsYr3Y0xI1CgqGvkgceMHUTCgURptrPXDs8d3d89eZnZ2z87ZeX7Jm/bs7Mw+z+z/mTNn\ndmYWFEVRFEVRFEVRFEVRFEXphucBhxr4UxRFURRFURz5JnBk10YoiqIoiqL4yuIpXecC4EfAv6Z0\nvb5wL3BUB9e9HFjUwXUhTJ+VNF1pwBWNG6VLQtRAL9uKaXXMtgCfnNK1+sRxwIoOrnsCsLGD60KY\nPitputKAKxo3SpeEqIEg2oo54A7gxAYNOBv4UIPlhUQEDHKOu96nqvxrgC/XLNuViHyfwc1vn31u\nI+5mmYh2NOCKzxqKCKut0JiZJCIsDUBP24rkiNkbgWuBV9PsSNq7gJsbLC90XO+TSf6H4rRj6xjY\nEi5+++xzW3HXR7qsK581VERf2wqNGXP6qoEqetdWHKK4B2rLs2i+U/b1hsvzmYjie+F6n6ryvwR4\nr0P5dYkot8vFb199hmrbVPcLNNlG2eKrhiLCaytM/NK4EfqqgYgethVt9ySvBW4oSX8BsBf4FnA/\n8Fng+Ioyn1SStgz4tY2BMafEduwCbgRuY7InuxtYUqPsWeW7wAbCmtjrs89t6N4kf2i6d8VnDbWF\nzz5r3EwHnzXgK4V11mbHbB1wAPhjQfpzkLlnVwEvA9YDTwHuo14P9/nIys/TLPM9Afg+0jHbClwD\nPBAfOyJx3ggZmgyFQ8D3kF59KMyiz3V1b5p/RFi6d2UWNeTKLPqscdMss6iBrimsszY7Zu8Gdpak\nzyOrNf8Wf/4n8E7gaOB6i+s8HdgHvBU4aG8m70E6YHsSxz6FjKJtThy7G7i4RvmzzOeBK7s2Yso0\n4fPptP9r2VX3pvlD1L0rGjf10LiZbULUvSu5deYSBHPAPwrSjo/THyjJfy7wA+SNAA/Hx+5DOmov\ntrDjd8DL4/8PgTMt8gJcCvwE+E/i2KOI7ZeysM3HQeBnyOPXnxqWvRTYBDyXhTcX/Bu4K76m7zyM\n+L0Gmaxoyiz7XdfnJNcA25D5D23hqnvT/HV0D7OtAVfqaGjW60vjJo1+X5gz6367YFxnVRPWlgFf\nAj5ecs4NSCWXcT9S+U/OHP8z8HhJvlFJ2hC71zbNxefn+fJt4O+ZYyuBTxuWfT7SGz6b9DPkOeDN\nwK2Uz3+AbidzjtkAvN+iXFe/owq72pz8P8bW5yxDw+skqbJtVHE9l9eVVeW30T34rQFX2tBQqG1F\nlqHhdcaY2DWquJ4vcROqBrStqKizy5DRoUPIY7235ZyzJC5gHhktW5lzzipkvlYVy4HVmWPHxdf/\nYUm+UUnaELtAe2Z8/o6ctL1x2tLM8Z1UL1B4FTL6l+10JtmKTP5bVXJOxOSNNblPZdjmXxTbafLY\nuwm/I/LF7OJ3mz7nMcS8MTC1bVRxvTa/YMBM9+CvBlxpS0OhthV5DDGLGxu7RhXX8yFuQtWAthWC\na9z8n1XAfmROWJbtwHk1y70eGdY7q+ScUUnaELtAOys+f3tO2u1xWnZ15lrKN8xdjfwCWIasMn1a\nzjlXI3udDCh/I0JEd739JNcBF1ac05TfEbPjcxFDmvdhVHG9tr9gqnQP/dOAK1UaCrWtKGJIeHET\nqga0rUiTqrO6PbRHkceZW0gPP65A5oyVjXgVsQ7pWe5A3n81DcaTN/OC6/D438Myxx9ERvaWFZS5\nBfgI0nG9Gunxn5FIvw55RPoZRFT7gZMs7Z42Q6ondfbN7yE6kTVJle6hfxpwZUi5hvpYX0M0bpLo\n90U+ffTbhSGJOnMZOrsFeCrw0sSxt2A3F2XMUuT1BLfiNkfBlkdK0pbH/+YtcLgNeENBvgELix4O\nIAK8CjgH+CDwe9KvYtiH3WKHLvgropUnlpwzoF9+m/gcGmW6h/5pwJUqDQ3oX31p3Eyi3xeTDOif\n3y6k6sxlVeavgB8DbwfuQTpX51O+oWwei5AhzXuADzjYU4e/IKNleQJajqwQLVp5WrSR3n8znw8i\nr6X6BTJv7a5M+kPYrwyaNkuQ/d4eKzmnb36b+PwF5O0WWU5EVmMdyEnbDPzc2bruKNtA0mcNnIb8\n8DPdAPOXyA9NF6o05HN91UXjJh/9vkjjs9+dtxXjjlnVc/IiAz8GfBV5DLkR+WVg+8x+HlmCPJ84\ndnlcVts8jkw+PCEnbR1S4Xlcgexjk0d2FPIw4CbkMe1G4JWkRbcG6SCaYFK3ZWKqm/8i4BsV+Xz1\nu02fryg4PsRs2b/r/Zw2ZboHfzUAsgp8veG1mrp2lYZ8ri9f42bWYgb0+yIPn/3uvK1YnDix7K+I\nO4E/Ae8AXkN6k1YTrkR6zvOZ4+dYlmPKKcCRmWP7kJ540s+1SGftjpwy1iK99/0F13gQODX+/xHA\nJ4DPIaOL25C3G7w+cf6FyIoME6ruU/ZeZf21zT/GpKPsi9/T9NmVurbZkqd7W6p0D/5qwJW2NORL\nfcHsxM20YgamEzehasAXv2elrbDmfUjnynYobwPyXPWLmb89wFdK8o1K0vYgPda8iZbnxWlfyxxf\ngzyyTM4BuAn4LelXMo3ZSf4I25hjMF9tchIy6lhEBAxK0sso8teWAfJLtoqm/I6YHZ+LGFLfhyJG\nJWl1dG+af0yV7qFfGnBlQLWGQm0rihgSXtyEqgFtKxYYUFJnZyIrI7YhPdMXGRZ6NDJRz7bX+RgL\nu/xm/7IjaElGmc/HIJvB/iaR/xFkZehlifOeER//Q06Zz0ZGznYhq0D2kh9MK5Fnz1W8ArP9Wb5D\n+QTJiEnRmd6nIn9t7/OHkc3/TGjC74j8QDOxuwuf8xhi3liY2jbKfHbVvWl+MNc9+KsBV9rSUKht\nRR5DzOLGxq5R5rOPcROqBrStEArrbAXp91NuQoZeTTaUnDYjx/zbHfJuxXwC4kakk7ee9PDlHPCm\nOO2oijIi0qKrc5+S/trmX4IEhQ2ufkdMBpqt3dP2OcsQsy8YG9tGjjZNS/fgnwZcaVtDobYVWYZU\nx42tXSNHm/T7wjx/H74vXGm0zk5HHkeujT+vRH4JbHI2s3lGjvltXpCeZDGyd5sNS5Fnx7uAG+N/\nP0r5BrpJItKiq3Ofkv7a5r+Eejsmu/gdMRlotnZ34XOS3Uy+1SIPG9tGjjZNU/fglwZcmYaGQm0r\nkpjEja1dI0eb9PsirO8LVxqts0WkJ8CfGhd2RlGGDjGdAJjHC4HX1cx7CfBah2vXISItOtv7lPXX\nNv+dyBLeaRIxGWg2ds+Szza2hax7cNOAKz5raExEGG2FrV0hx01fNZAkIqC24nbs9yObFnM18y1B\neuNFqyKq2I3bvm91iCgf2i+7Tyb+luU/GRlGnjYR1Y8ziuyeVZ/HlNmmuk/jogFXfNRQRHhtBVR/\nV2ncLNBHDUQE0lZsRoYR2zRWMSOiWHSu96kq/w662cgwojzQXPz21WfQuEsS0Z4GXPFVQxHhtRUa\nM2kiwtNARABtxUVxYSCrLAeulilOROTfA9f7VJX/cORNDF0QUeyPi98++6xxlyaiHQ244rOGIsJq\nKzRmJokISwPQ07YiufvuucCxyLYRq4ELkD2+FL9wvU8m+S8G7na2tFlc/PbZZ407c7qsK581VERf\n2wqNGXP6qoEqetFWnIy8EzK7n9jKBo1V7LmX9FJh1/tkmv8WizKbJuszuPnts88ad/k0rQFXfNbQ\nmFDaCo2ZYkLRQBJtKxRFURRFURRFURRFURRFURRFURRFURRFURRFURRFURRFURRFmT7/A1TuLADP\nlUklAAAAAElFTkSuQmCC\n", "prompt_number": 39, "text": [ "\n", " ___ \n", "\u2572\u2571 2 \u22c5\u27581,0\u27e9\u2a02 \u27581/2,1/2\u27e9\u2a02 \u27581/2,1/2\u27e9 \u27581,1\u27e9\u2a02 \u27581/2,-1/2\u27e9\u2a02 \u27581/2,1/2\u27e9 \u27581,1\u27e9\u2a02 \u27581/2\n", "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 + \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 + \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", " 2 2 \n", "\n", " \n", ",1/2\u27e9\u2a02 \u27581/2,-1/2\u27e9\n", "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", " 2 " ] } ], "prompt_number": 39 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Uncoupling can also be done with the `.rewrite` method:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "jzc.rewrite(\"Jz\", coupled=False)" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\frac{1}{2} \\sqrt{2} {{\\left|1,0\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},\\frac{1}{2}\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},\\frac{1}{2}\\right\\rangle }} + \\frac{1}{2} {{\\left|1,1\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},- \\frac{1}{2}\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},\\frac{1}{2}\\right\\rangle }} + \\frac{1}{2} {{\\left|1,1\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},\\frac{1}{2}\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},- \\frac{1}{2}\\right\\rangle }}$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAmYAAAAkCAYAAAAjI/bEAAAABHNCSVQICAgIfAhkiAAACyFJREFU\neJztnWvMHUUZx38thZK2b42VSwsBjrSKiqB4IwWU0IoiElGUaiRCoEalXgtoYjTa5tUUTaFQ8Ua8\nHMFLjVggsYr3Y0xI1CgqGvkgceMHUTCgURptrPXDs8d3d89eZnZ2z87ZeX7Jm/bs7Mw+z+z/mTNn\ndmYWFEVRFEVRFEVRFEVRFEXphucBhxr4UxRFURRFURz5JnBk10YoiqIoiqL4yuIpXecC4EfAv6Z0\nvb5wL3BUB9e9HFjUwXUhTJ+VNF1pwBWNG6VLQtRAL9uKaXXMtgCfnNK1+sRxwIoOrnsCsLGD60KY\nPitputKAKxo3SpeEqIEg2oo54A7gxAYNOBv4UIPlhUQEDHKOu96nqvxrgC/XLNuViHyfwc1vn31u\nI+5mmYh2NOCKzxqKCKut0JiZJCIsDUBP24rkiNkbgWuBV9PsSNq7gJsbLC90XO+TSf6H4rRj6xjY\nEi5+++xzW3HXR7qsK581VERf2wqNGXP6qoEqetdWHKK4B2rLs2i+U/b1hsvzmYjie+F6n6ryvwR4\nr0P5dYkot8vFb199hmrbVPcLNNlG2eKrhiLCaytM/NK4EfqqgYgethVt9ySvBW4oSX8BsBf4FnA/\n8Fng+Ioyn1SStgz4tY2BMafEduwCbgRuY7InuxtYUqPsWeW7wAbCmtjrs89t6N4kf2i6d8VnDbWF\nzz5r3EwHnzXgK4V11mbHbB1wAPhjQfpzkLlnVwEvA9YDTwHuo14P9/nIys/TLPM9Afg+0jHbClwD\nPBAfOyJx3ggZmgyFQ8D3kF59KMyiz3V1b5p/RFi6d2UWNeTKLPqscdMss6iBrimsszY7Zu8Gdpak\nzyOrNf8Wf/4n8E7gaOB6i+s8HdgHvBU4aG8m70E6YHsSxz6FjKJtThy7G7i4RvmzzOeBK7s2Yso0\n4fPptP9r2VX3pvlD1L0rGjf10LiZbULUvSu5deYSBHPAPwrSjo/THyjJfy7wA+SNAA/Hx+5DOmov\ntrDjd8DL4/8PgTMt8gJcCvwE+E/i2KOI7ZeysM3HQeBnyOPXnxqWvRTYBDyXhTcX/Bu4K76m7zyM\n+L0Gmaxoyiz7XdfnJNcA25D5D23hqnvT/HV0D7OtAVfqaGjW60vjJo1+X5gz6367YFxnVRPWlgFf\nAj5ecs4NSCWXcT9S+U/OHP8z8HhJvlFJ2hC71zbNxefn+fJt4O+ZYyuBTxuWfT7SGz6b9DPkOeDN\nwK2Uz3+AbidzjtkAvN+iXFe/owq72pz8P8bW5yxDw+skqbJtVHE9l9eVVeW30T34rQFX2tBQqG1F\nlqHhdcaY2DWquJ4vcROqBrStqKizy5DRoUPIY7235ZyzJC5gHhktW5lzzipkvlYVy4HVmWPHxdf/\nYUm+UUnaELtAe2Z8/o6ctL1x2tLM8Z1UL1B4FTL6l+10JtmKTP5bVXJOxOSNNblPZdjmXxTbafLY\nuwm/I/LF7OJ3mz7nMcS8MTC1bVRxvTa/YMBM9+CvBlxpS0OhthV5DDGLGxu7RhXX8yFuQtWAthWC\na9z8n1XAfmROWJbtwHk1y70eGdY7q+ScUUnaELtAOys+f3tO2u1xWnZ15lrKN8xdjfwCWIasMn1a\nzjlXI3udDCh/I0JEd739JNcBF1ac05TfEbPjcxFDmvdhVHG9tr9gqnQP/dOAK1UaCrWtKGJIeHET\nqga0rUiTqrO6PbRHkceZW0gPP65A5oyVjXgVsQ7pWe5A3n81DcaTN/OC6/D438Myxx9ERvaWFZS5\nBfgI0nG9Gunxn5FIvw55RPoZRFT7gZMs7Z42Q6ondfbN7yE6kTVJle6hfxpwZUi5hvpYX0M0bpLo\n90U+ffTbhSGJOnMZOrsFeCrw0sSxt2A3F2XMUuT1BLfiNkfBlkdK0pbH/+YtcLgNeENBvgELix4O\nIAK8CjgH+CDwe9KvYtiH3WKHLvgropUnlpwzoF9+m/gcGmW6h/5pwJUqDQ3oX31p3Eyi3xeTDOif\n3y6k6sxlVeavgB8DbwfuQTpX51O+oWwei5AhzXuADzjYU4e/IKNleQJajqwQLVp5WrSR3n8znw8i\nr6X6BTJv7a5M+kPYrwyaNkuQ/d4eKzmnb36b+PwF5O0WWU5EVmMdyEnbDPzc2bruKNtA0mcNnIb8\n8DPdAPOXyA9NF6o05HN91UXjJh/9vkjjs9+dtxXjjlnVc/IiAz8GfBV5DLkR+WVg+8x+HlmCPJ84\ndnlcVts8jkw+PCEnbR1S4Xlcgexjk0d2FPIw4CbkMe1G4JWkRbcG6SCaYFK3ZWKqm/8i4BsV+Xz1\nu02fryg4PsRs2b/r/Zw2ZboHfzUAsgp8veG1mrp2lYZ8ri9f42bWYgb0+yIPn/3uvK1YnDix7K+I\nO4E/Ae8AXkN6k1YTrkR6zvOZ4+dYlmPKKcCRmWP7kJ540s+1SGftjpwy1iK99/0F13gQODX+/xHA\nJ4DPIaOL25C3G7w+cf6FyIoME6ruU/ZeZf21zT/GpKPsi9/T9NmVurbZkqd7W6p0D/5qwJW2NORL\nfcHsxM20YgamEzehasAXv2elrbDmfUjnynYobwPyXPWLmb89wFdK8o1K0vYgPda8iZbnxWlfyxxf\ngzyyTM4BuAn4LelXMo3ZSf4I25hjMF9tchIy6lhEBAxK0sso8teWAfJLtoqm/I6YHZ+LGFLfhyJG\nJWl1dG+af0yV7qFfGnBlQLWGQm0rihgSXtyEqgFtKxYYUFJnZyIrI7YhPdMXGRZ6NDJRz7bX+RgL\nu/xm/7IjaElGmc/HIJvB/iaR/xFkZehlifOeER//Q06Zz0ZGznYhq0D2kh9MK5Fnz1W8ArP9Wb5D\n+QTJiEnRmd6nIn9t7/OHkc3/TGjC74j8QDOxuwuf8xhi3liY2jbKfHbVvWl+MNc9+KsBV9rSUKht\nRR5DzOLGxq5R5rOPcROqBrStEArrbAXp91NuQoZeTTaUnDYjx/zbHfJuxXwC4kakk7ee9PDlHPCm\nOO2oijIi0qKrc5+S/trmX4IEhQ2ufkdMBpqt3dP2OcsQsy8YG9tGjjZNS/fgnwZcaVtDobYVWYZU\nx42tXSNHm/T7wjx/H74vXGm0zk5HHkeujT+vRH4JbHI2s3lGjvltXpCeZDGyd5sNS5Fnx7uAG+N/\nP0r5BrpJItKiq3Ofkv7a5r+Eejsmu/gdMRlotnZ34XOS3Uy+1SIPG9tGjjZNU/fglwZcmYaGQm0r\nkpjEja1dI0eb9PsirO8LVxqts0WkJ8CfGhd2RlGGDjGdAJjHC4HX1cx7CfBah2vXISItOtv7lPXX\nNv+dyBLeaRIxGWg2ds+Szza2hax7cNOAKz5raExEGG2FrV0hx01fNZAkIqC24nbs9yObFnM18y1B\neuNFqyKq2I3bvm91iCgf2i+7Tyb+luU/GRlGnjYR1Y8ziuyeVZ/HlNmmuk/jogFXfNRQRHhtBVR/\nV2ncLNBHDUQE0lZsRoYR2zRWMSOiWHSu96kq/w662cgwojzQXPz21WfQuEsS0Z4GXPFVQxHhtRUa\nM2kiwtNARABtxUVxYSCrLAeulilOROTfA9f7VJX/cORNDF0QUeyPi98++6xxlyaiHQ244rOGIsJq\nKzRmJokISwPQ07YiufvuucCxyLYRq4ELkD2+FL9wvU8m+S8G7na2tFlc/PbZZ407c7qsK581VERf\n2wqNGXP6qoEqetFWnIy8EzK7n9jKBo1V7LmX9FJh1/tkmv8WizKbJuszuPnts88ad/k0rQFXfNbQ\nmFDaCo2ZYkLRQBJtKxRFURRFURRFURRFURRFURRFURRFURRFURRFURRFURRFURRFmT7/A1TuLADP\nlUklAAAAAElFTkSuQmCC\n", "prompt_number": 40, "text": [ "\n", " ___ \n", "\u2572\u2571 2 \u22c5\u27581,0\u27e9\u2a02 \u27581/2,1/2\u27e9\u2a02 \u27581/2,1/2\u27e9 \u27581,1\u27e9\u2a02 \u27581/2,-1/2\u27e9\u2a02 \u27581/2,1/2\u27e9 \u27581,1\u27e9\u2a02 \u27581/2\n", "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 + \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 + \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", " 2 2 \n", "\n", " \n", ",1/2\u27e9\u2a02 \u27581/2,-1/2\u27e9\n", "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", " 2 " ] } ], "prompt_number": 40 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `uncouple` method can also uncouple normal states if given a set of spin bases to consider:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "jz = JzKet(2, 1)\n", "uncouple(jz, (1, S(1)/2, S(1)/2))" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\frac{1}{2} \\sqrt{2} {{\\left|1,0\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},\\frac{1}{2}\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},\\frac{1}{2}\\right\\rangle }} + \\frac{1}{2} {{\\left|1,1\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},- \\frac{1}{2}\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},\\frac{1}{2}\\right\\rangle }} + \\frac{1}{2} {{\\left|1,1\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},\\frac{1}{2}\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},- \\frac{1}{2}\\right\\rangle }}$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAmYAAAAkCAYAAAAjI/bEAAAABHNCSVQICAgIfAhkiAAACyFJREFU\neJztnWvMHUUZx38thZK2b42VSwsBjrSKiqB4IwWU0IoiElGUaiRCoEalXgtoYjTa5tUUTaFQ8Ua8\nHMFLjVggsYr3Y0xI1CgqGvkgceMHUTCgURptrPXDs8d3d89eZnZ2z87ZeX7Jm/bs7Mw+z+z/mTNn\ndmYWFEVRFEVRFEVRFEVRFEXphucBhxr4UxRFURRFURz5JnBk10YoiqIoiqL4yuIpXecC4EfAv6Z0\nvb5wL3BUB9e9HFjUwXUhTJ+VNF1pwBWNG6VLQtRAL9uKaXXMtgCfnNK1+sRxwIoOrnsCsLGD60KY\nPitputKAKxo3SpeEqIEg2oo54A7gxAYNOBv4UIPlhUQEDHKOu96nqvxrgC/XLNuViHyfwc1vn31u\nI+5mmYh2NOCKzxqKCKut0JiZJCIsDUBP24rkiNkbgWuBV9PsSNq7gJsbLC90XO+TSf6H4rRj6xjY\nEi5+++xzW3HXR7qsK581VERf2wqNGXP6qoEqetdWHKK4B2rLs2i+U/b1hsvzmYjie+F6n6ryvwR4\nr0P5dYkot8vFb199hmrbVPcLNNlG2eKrhiLCaytM/NK4EfqqgYgethVt9ySvBW4oSX8BsBf4FnA/\n8Fng+Ioyn1SStgz4tY2BMafEduwCbgRuY7InuxtYUqPsWeW7wAbCmtjrs89t6N4kf2i6d8VnDbWF\nzz5r3EwHnzXgK4V11mbHbB1wAPhjQfpzkLlnVwEvA9YDTwHuo14P9/nIys/TLPM9Afg+0jHbClwD\nPBAfOyJx3ggZmgyFQ8D3kF59KMyiz3V1b5p/RFi6d2UWNeTKLPqscdMss6iBrimsszY7Zu8Gdpak\nzyOrNf8Wf/4n8E7gaOB6i+s8HdgHvBU4aG8m70E6YHsSxz6FjKJtThy7G7i4RvmzzOeBK7s2Yso0\n4fPptP9r2VX3pvlD1L0rGjf10LiZbULUvSu5deYSBHPAPwrSjo/THyjJfy7wA+SNAA/Hx+5DOmov\ntrDjd8DL4/8PgTMt8gJcCvwE+E/i2KOI7ZeysM3HQeBnyOPXnxqWvRTYBDyXhTcX/Bu4K76m7zyM\n+L0Gmaxoyiz7XdfnJNcA25D5D23hqnvT/HV0D7OtAVfqaGjW60vjJo1+X5gz6367YFxnVRPWlgFf\nAj5ecs4NSCWXcT9S+U/OHP8z8HhJvlFJ2hC71zbNxefn+fJt4O+ZYyuBTxuWfT7SGz6b9DPkOeDN\nwK2Uz3+AbidzjtkAvN+iXFe/owq72pz8P8bW5yxDw+skqbJtVHE9l9eVVeW30T34rQFX2tBQqG1F\nlqHhdcaY2DWquJ4vcROqBrStqKizy5DRoUPIY7235ZyzJC5gHhktW5lzzipkvlYVy4HVmWPHxdf/\nYUm+UUnaELtAe2Z8/o6ctL1x2tLM8Z1UL1B4FTL6l+10JtmKTP5bVXJOxOSNNblPZdjmXxTbafLY\nuwm/I/LF7OJ3mz7nMcS8MTC1bVRxvTa/YMBM9+CvBlxpS0OhthV5DDGLGxu7RhXX8yFuQtWAthWC\na9z8n1XAfmROWJbtwHk1y70eGdY7q+ScUUnaELtAOys+f3tO2u1xWnZ15lrKN8xdjfwCWIasMn1a\nzjlXI3udDCh/I0JEd739JNcBF1ac05TfEbPjcxFDmvdhVHG9tr9gqnQP/dOAK1UaCrWtKGJIeHET\nqga0rUiTqrO6PbRHkceZW0gPP65A5oyVjXgVsQ7pWe5A3n81DcaTN/OC6/D438Myxx9ERvaWFZS5\nBfgI0nG9Gunxn5FIvw55RPoZRFT7gZMs7Z42Q6ondfbN7yE6kTVJle6hfxpwZUi5hvpYX0M0bpLo\n90U+ffTbhSGJOnMZOrsFeCrw0sSxt2A3F2XMUuT1BLfiNkfBlkdK0pbH/+YtcLgNeENBvgELix4O\nIAK8CjgH+CDwe9KvYtiH3WKHLvgropUnlpwzoF9+m/gcGmW6h/5pwJUqDQ3oX31p3Eyi3xeTDOif\n3y6k6sxlVeavgB8DbwfuQTpX51O+oWwei5AhzXuADzjYU4e/IKNleQJajqwQLVp5WrSR3n8znw8i\nr6X6BTJv7a5M+kPYrwyaNkuQ/d4eKzmnb36b+PwF5O0WWU5EVmMdyEnbDPzc2bruKNtA0mcNnIb8\n8DPdAPOXyA9NF6o05HN91UXjJh/9vkjjs9+dtxXjjlnVc/IiAz8GfBV5DLkR+WVg+8x+HlmCPJ84\ndnlcVts8jkw+PCEnbR1S4Xlcgexjk0d2FPIw4CbkMe1G4JWkRbcG6SCaYFK3ZWKqm/8i4BsV+Xz1\nu02fryg4PsRs2b/r/Zw2ZboHfzUAsgp8veG1mrp2lYZ8ri9f42bWYgb0+yIPn/3uvK1YnDix7K+I\nO4E/Ae8AXkN6k1YTrkR6zvOZ4+dYlmPKKcCRmWP7kJ540s+1SGftjpwy1iK99/0F13gQODX+/xHA\nJ4DPIaOL25C3G7w+cf6FyIoME6ruU/ZeZf21zT/GpKPsi9/T9NmVurbZkqd7W6p0D/5qwJW2NORL\nfcHsxM20YgamEzehasAXv2elrbDmfUjnynYobwPyXPWLmb89wFdK8o1K0vYgPda8iZbnxWlfyxxf\ngzyyTM4BuAn4LelXMo3ZSf4I25hjMF9tchIy6lhEBAxK0sso8teWAfJLtoqm/I6YHZ+LGFLfhyJG\nJWl1dG+af0yV7qFfGnBlQLWGQm0rihgSXtyEqgFtKxYYUFJnZyIrI7YhPdMXGRZ6NDJRz7bX+RgL\nu/xm/7IjaElGmc/HIJvB/iaR/xFkZehlifOeER//Q06Zz0ZGznYhq0D2kh9MK5Fnz1W8ArP9Wb5D\n+QTJiEnRmd6nIn9t7/OHkc3/TGjC74j8QDOxuwuf8xhi3liY2jbKfHbVvWl+MNc9+KsBV9rSUKht\nRR5DzOLGxq5R5rOPcROqBrStEArrbAXp91NuQoZeTTaUnDYjx/zbHfJuxXwC4kakk7ee9PDlHPCm\nOO2oijIi0qKrc5+S/trmX4IEhQ2ufkdMBpqt3dP2OcsQsy8YG9tGjjZNS/fgnwZcaVtDobYVWYZU\nx42tXSNHm/T7wjx/H74vXGm0zk5HHkeujT+vRH4JbHI2s3lGjvltXpCeZDGyd5sNS5Fnx7uAG+N/\nP0r5BrpJItKiq3Ofkv7a5r+Eejsmu/gdMRlotnZ34XOS3Uy+1SIPG9tGjjZNU/fglwZcmYaGQm0r\nkpjEja1dI0eb9PsirO8LVxqts0WkJ8CfGhd2RlGGDjGdAJjHC4HX1cx7CfBah2vXISItOtv7lPXX\nNv+dyBLeaRIxGWg2ds+Szza2hax7cNOAKz5raExEGG2FrV0hx01fNZAkIqC24nbs9yObFnM18y1B\neuNFqyKq2I3bvm91iCgf2i+7Tyb+luU/GRlGnjYR1Y8ziuyeVZ/HlNmmuk/jogFXfNRQRHhtBVR/\nV2ncLNBHDUQE0lZsRoYR2zRWMSOiWHSu96kq/w662cgwojzQXPz21WfQuEsS0Z4GXPFVQxHhtRUa\nM2kiwtNARABtxUVxYSCrLAeulilOROTfA9f7VJX/cORNDF0QUeyPi98++6xxlyaiHQ244rOGIsJq\nKzRmJokISwPQ07YiufvuucCxyLYRq4ELkD2+FL9wvU8m+S8G7na2tFlc/PbZZ407c7qsK581VERf\n2wqNGXP6qoEqetFWnIy8EzK7n9jKBo1V7LmX9FJh1/tkmv8WizKbJuszuPnts88ad/k0rQFXfNbQ\nmFDaCo2ZYkLRQBJtKxRFURRFURRFURRFURRFURRFURRFURRFURRFURRFURRFURRFmT7/A1TuLADP\nlUklAAAAAElFTkSuQmCC\n", "prompt_number": 41, "text": [ "\n", " ___ \n", "\u2572\u2571 2 \u22c5\u27581,0\u27e9\u2a02 \u27581/2,1/2\u27e9\u2a02 \u27581/2,1/2\u27e9 \u27581,1\u27e9\u2a02 \u27581/2,-1/2\u27e9\u2a02 \u27581/2,1/2\u27e9 \u27581,1\u27e9\u2a02 \u27581/2\n", "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 + \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 + \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", " 2 2 \n", "\n", " \n", ",1/2\u27e9\u2a02 \u27581/2,-1/2\u27e9\n", "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", " 2 " ] } ], "prompt_number": 41 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example: Spin-orbit Coupling\n", "\n", "If we start with a hydrogen atom, i.e. a nucleus of charge $Ze$ orbited by a single electron of charge $e$ with reduced mass $\\mu$, ignoring energy from center-of-mass motion, we can write the Hamiltonian in terms of the relative momentum, $p$, and position, $r$, as:\n", "\n", "$$H=\\frac{p^2}{2\\mu} - \\frac{Ze^2}{r}$$\n", "\n", "The resulting eigenfunctions have a seperate radial and angular compents, $\\psi=R_{n,l}(r)Y_{l,m}(\\phi,\\theta)$. While the radial component is a complicated function involving Laguere polynomials, the radial part is the familiar spherical harmonics with orbital angular momentum $\\vec{L}$, where $l$ and $m$ give the orbital angular momentum quantum numbers. We represent this as a angular momentum state:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "l, ml = symbols('l m_l')\n", "orbit = JzKet(l, ml); orbit" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$${\\left|l,m_{l}\\right\\rangle }$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAC8AAAAaCAYAAAAnkAWyAAAABHNCSVQICAgIfAhkiAAAAppJREFU\nWIXt102MjVcYB/DfGJQQbTTxsTJFohqfESRidEYmTRtNRRRLhETCykdiJWFlg7AgFixESrS1E1qL\nZto0bYVFg4aIz1j4WpAgxBAW50zmde57e8/lioX5Jzfve/7Pc57zP885zzn3pRfvFDswsMzQJ2n/\nXOLzA+7iJT5rrK4sdGJJrmMZtuNmg8TUi2b8WGZIM18Nc/B7w+TUhxe4iAmpIUf8EEz1/sTDfqxM\nyRzxrcLSvU/xNzBcUrh9Mzq24xYuZfjORwcmYymGYpFQ7LOwDSewFp9iGPpjObpqxP4Ji3GgmkNn\nCXcGhzOE98fO+H4af2I9miK3UTi1tqMlcs14KEy0FvrhaJGotW0+xhR5W+bLKLgJo3E7Cn0Z7V3C\nShzC9ci9iL/hGfG7hNWvKNxudCbtb+Pg4zOCjxT25KTYpzWxH8ZfCTc6+s7LiA9j9Kxuzcy34w4u\nZAS+hSeYG5+nEnubyuR8jafyD4MrGNvdqCW+DX/E95zsEyb8N54VuPEYoVL8AvyCRxgl1MD/YQyu\nVjMWgw8W9uOa2N6b+I7DgITrg/vYlPCrhckMKnBD8Rzfx/ae+PwK/wr1kmIrJhYHq4Zu22XMxD8F\nW7tw6x1M+kzFJyoz3C6cQI8LXIuQ6ZOYjv8ifxIP8GsSox8+x7lqgtNBN+A4dnl9ol/gHq4l/t/h\nvHBspnFXJVyzcHbvxeZC/EFRfLqqC7GsmvAy8bWwpU7/HHwjXGQpjkhu2Nw/ZtXw0Vv2L0OHyi3T\nItwbT4rk24hvFQqr0egQTqAiVmBfrY6dmQP0Fb5wmmo51olheupocGGsso+kN878c6zTc/U3CrPx\nG6ZhRuTm4ViZcyp+d4PF1IuzQqbn6tkFbap8SfWiFx8aXgFjVHm5OC1AbAAAAABJRU5ErkJggg==\n", "prompt_number": 42, "text": [ "\u2758l,m_l\u27e9" ] } ], "prompt_number": 42 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, the spin orbit interaction arises from the electron experiencing a magnetic field as it orbits the electrically charged nucleus. This magnetic field is:\n", "\n", "$$\\vec{B} = \\frac{1}{c}\\frac{Ze\\vec{v}\\times\\vec{r}}{r^3} = \\frac{Ze\\vec{p}\\times\\vec{r}}{mcr^3}=\\frac{Ze\\vec{L}}{mc\\hbar r^3}$$\n", "\n", "Then the spin-orbit Hamiltonian can be written, using the electron's magnetic dipole moment $\\mu$, as:\n", "\n", "$$H_{SO} = -\\vec{\\mu}\\cdot\\vec{B} = -\\left(-\\frac{g\\mu_B \\vec{S}}{\\hbar}\\right)\\cdot\\left(\\frac{Ze\\vec{L}}{mc\\hbar r^3}\\right)$$\n", "\n", "Ignoring the radial term:\n", "\n", "$$\\propto \\vec{L}\\cdot\\vec{S} = J^2 - L^2 - S^2$$\n", "\n", "for $\\vec{J}$, the coupled angular momentum.\n", "\n", "The electron spin angular momentum is given as $\\vec{S}$, where the spin wavefunction is:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "ms = symbols('m_s')\n", "spin = JzKet(S(1)/2, ms); spin" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$${\\left|\\frac{1}{2},m_{s}\\right\\rangle }$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAADgAAAAgCAYAAABHA7voAAAABHNCSVQICAgIfAhkiAAAAwpJREFU\nWIXt2F2IVVUUB/DftUnyayTzs6iG6SEiMEpF8HtKYiDDD1CCqCjtpXqRHuZVRUH8wgff/AiSLJhQ\nhKJPoiAL6anE9/NWiSloaCR6fVjn6p1zz9w558610Wn+cOCctfbae/332mvvtQ//Q/yE6SPtRAm8\njspgynE5socxOUc+BZ/isfb41TY8ihfKGCToysg2YQuqObqRxhwcK2OQGJzE3UgQPsGsPEXeEr0X\ncQRv5SlGC8Fv8LyczWa0EKziW7yYVYwWgvAB3swKRxPBc7gudtVb6Cho/CqWpO878SMOFLRdjZV4\nBm9gGtaLZbUIe/AFNuMhzMR4EY1rBceo4TA2YnuzRon2HQXjsT99/0VMzPtubwZ9Yub31o15Hy6L\nySiLithwbq3MO71ElwtSFXTjD0GmmuqviYgeExNLLLPrBjnXhkAVX6G3WaNE+yI4BxMwNx18aUb/\nsah969Gdtn2pxTGno7/2cacj+DuuijPqKk5n9CvwfUbWi3/wQ4tjnhe8HqQYwWqBZyj04Gf8Wyd7\nCrM1ElyLL/E3Hhc5WQYdmIqLFCNYKfDU8CQeyNiPwzKNRHpEDp6qk01L5R+l330iH8tgFT5r1iDR\nWg72iGj2Z+Tz5Odfv4Hk4Lm0bScW4N0W/DguJgrFz8Ei+FOs//kZ+SM4qzH/ZuDDjOxXcefcJXbc\nbSV96MIlXGjWKNEYwYXiIN4izpllTey3lnSqDF7GO3hN/u1hBxYP1UliIMHJonqpYQOuiMjkYecg\n8uFiCn5L32eI6qceHfi6SEeJgQTn4gaeSL87RZ5syLFdileKDNICJqS+nRHlXbYQWIf3inSUGEiw\nIpZobbd8WhB8NmPXgX2a/AAaJiqC5Bp8p3GlnBDHw5BINN9Fj4py679EF/4StS1RrNdHqxuH8gzL\n7qIbRXXSV9JuuDiP3SItJmKS20U8vI2DRTtL5EdwlSBIHOZ5bUYC94vKJxdFa9HlIqk/F+VVr8zF\ncgSxGifLGGT/bHeL+1m2/uxsk4PDxQF3jy9jGMMYMrgJOdmQt/74E/MAAAAASUVORK5CYII=\n", "prompt_number": 43, "text": [ "\u27581/2,m_s\u27e9" ] } ], "prompt_number": 43 }, { "cell_type": "markdown", "metadata": {}, "source": [ "From this we build our uncoupled state:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "state = TensorProduct(orbit, spin); state" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$${{\\left|l,m_{l}\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},m_{s}\\right\\rangle }}$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAHsAAAAgCAYAAAAyjgdLAAAABHNCSVQICAgIfAhkiAAABYdJREFU\naIHt22usXUUVwPFfubdApb0GxEoJyKUFFdFGaAwhvEpRqOFdpBgNQuQVCh9oLNEECBBCgAhGXgo+\nquENBYEPoPjKgaiEYIKCiYQEPIkRlEfASKhS6vXD2od775y9z5lzex69yfknNz0zs/bM2jOzZtas\nPWXIkCF+j50HrUSHfBVzqgq36aMis41dMb8kfwHux0f7q04Wu+OIQSsxG6ljPMk7E5dhoqRsa2AR\n7hq0ElvCtzFvAO3WVQ/o1jrYcA8+UlaQLuP3l8jciVfFC+7ZXb2yqOGUAbQ7W1mPr+UI1iryr8Pf\nuqVNh4zgvgG0Wzc7LXsOfqnEUct10A7F493UqAM243l8akDtzzYm8CscmRaMZjw8hv3w/S4r1Qk/\nwlpc0MEz22E1lokOmMB/8RCe6raCWxk/xg14rJVQrSTvaNFRH+u+Th1xt3xH7fPihQ8yfTlbgHPE\nxP1QmzrqZucy3uBO4Z1XUivJuxYvZzZwPG7EE8KZW4arcZXYBo4Vq8mFRf563IG5GXWvwmkZcifi\nGa2dybViX9uphUxd84B+Bd8Tg30Pzs/QZyq97J+UFbi4lUCtJO8PwqrasS2+U/x+Gr/F101a1jeE\nV3+dyU4cwb/lDeJcPNBGZhdh0R8QS/8nSmTOFeflcTFwVdR113p73T8pDUftfb+snYP2QXxGnnN2\nmHiBOViMfwjFJ4ryTcKS7hIdSThfm1WcCxM24QWtHbU1uAbviEFdK/yNBuvwL/yw0OEd7JHRdjfo\ndf+kTIg9e2WVQC1JH1M8tE9G5YvEnrq0eOaQpPxuEW+eyuJC9uiM+mGJSeso47YkPSKWzYNxKU5I\nylfgjIq66rpr2f3on5SdsaGRaGfZh+Of+EtGxa9go+jAjZo93uWaJ9NK/Ef+se5F7NWi/H9JerPw\n4G8u0g8l5a9gYWbbW0o/+ifldTHGO9J+sJcLZ4I86yYmyJN4d0rePmI/rSWyJ+LneFsspyNt6l6C\nl1qUp+8zIlaC88XymVr2IjGZc5jI+Muhl/2TMiq24jfLCqc2Nl9YxnlF+pZE9uPYPsnbpqj4kiR/\njXi5Habk7YT38MUi/d3i3yPxR7GfpVyFT5cpXnAp9i1+b4tbTd+zL8SXp6SvFV+KyqjbsmW8V/3T\nCSdoEZuoTfk9Jgb7KByA06eUHS5m8gbTaQQw0v1oA36X5O1fyI7hsyYnVUOPNAI0Fw9WKV6wUL43\nvofYz6uom/lg97p/cvmpFsfLWpJeh0dxvelL5CfxGv6ayB+HPwurSus9J8kbES95i/hs2Kh/B7yl\n2SpOMn3CVXGcvHP2LxR7WQV1Mx/sXvZPLuP4SSuBWocVXt6hfA5fwM9K8u+VH0E7QhyvDtQcQTu7\nKGt3C6WuebAPEBPlMnGGPbRNHb3oHyL4sganqv7CdaWIIL5PTmy8Fdtt4fNlfE5zTHdcnEs3Ztbx\na3GmPUXExyfEoG8SHvlM4vzzhcP0zSK9WjhPe+PvFc/0on8WiIFcig+L4+b6RGZULP0Xtaqo1kGj\nh+BLHcjn8ifNe+0VWjtmvaBuumUvFUe7JUV6TEyi1RXP96p/5hW6PScczLKAyyoZodxaZoOj4gZJ\n5eW2GbLQ5D7XuP81qvxSRa+pmz7Yc8Qy3njnfcVg76eZXvVPQ495wtP+jYihpzwojlwtqXVVrc5Z\nJbzoZSL4QHw8OH0AutS1dtBuV3487CXjeMOkg3eyZgteLHySJlIP7+YyoT7yrLCKFSYn3nKDuanS\nijNERGxdn9t9Hd8SW8fZ2E3zmJ2FH/RZr1lPXbllH2Mynr59hcygmCucxlKG98Y74zDhED0iwpsr\ntbkg0GeOx8ODVmI2kv6PkMXi23IaDx/rv2qV3GTr0mfIkCFDhgwZMgP+D8enOQ4jSfDWAAAAAElF\nTkSuQmCC\n", "prompt_number": 44, "text": [ "\u2758l,m_l\u27e9\u2a02 \u27581/2,m_s\u27e9" ] } ], "prompt_number": 44 }, { "cell_type": "markdown", "metadata": {}, "source": [ "For clarity we will define $L^2$ and $S^2$ operators. These behave the same as `J2`, they only display differently." ] }, { "cell_type": "code", "collapsed": false, "input": [ "L2 = J2Op('L')\n", "S2 = J2Op('S')" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 45 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We also have the spin-orbit Hamiltonian:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "hso = J2 - TensorProduct(L2, 1) - TensorProduct(1, S2); hso" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$J^2 - {1}\\otimes {S^2} - {L^2}\\otimes {1}$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAKoAAAAZCAYAAAChKLVZAAAABHNCSVQICAgIfAhkiAAABIhJREFU\neJzt2luoVFUcx/HP8ViWmpaZWhGWvSilKVFp5YWkKJSoIIOiJ9OSfCjoIlRIdrNSCiqk+5AvPpWE\nlZcu2EMXArML+GBlT0VYgZhBmZ4e/jM6Z5g5e8+ePTNndL5w2Gfvdfuv33/tvf5rraFLlw6gJ2O5\ny3A5RuMKPIZP8zKqw+kkbTrJ1roZidVl94vwN85ujzmDik7SppNszcQ0HMb5xftR6BMdPd7pJG06\nydZM9IgpoxQ2XCA6OKNtFg0eOkmbTrI1kRnYhi/F29eHb7C0LM96rG2xXcPxbYvbLHESHsImbMA7\nWCGm0k0VeVutzRC8i13CV//hC+HDWQll2+HHajTk2x78gR0VzxfjGdkXZVm4BF8JR7SasdiJZfr3\neQn2iIFboh3alJgn9FmdkK9EO20tp6Zvh6Ss4EKMwdayZwuL1wcwDOdmty8VU/Ae7sahJrdVi/X4\nCOv0F/NV/IMPi/et1qaS2cXrthR5220rKXw7NGVF84rXT4rXuRhfrHwCZuI3/JzNzlTswoLi/wUR\nX9XDMLFQuFgMsj4xuDaK0CaJqbgWq2qk/ygGcTu0qWQuDuLzFPnytDWrxo369ghvi46PwCTsLzOk\n9Dcqa+UZKKhv6r8ab4q9wvLp7RTciVdwekIdi4pt3l4j/WGDQ5sTcEDyIM3b1jw0pn7fHqEHv0vu\neCspSN+ZG/E1zhsgz71imhwzQJ5pxTb34wlcKQbFYGOWsPPpFraZl8Y0MFAvKhZ8MkvhJlGQrjMT\nxFs+HK9jcpU8y3CHiM3WJdS3Rv+vz18ibk0Sv5U8KGxbkJQxJ/LWuCDjQL2nWPCaLIWbREG6zqxy\nVLgT8bL++4T34day+7WYmFDnDDwqjhr/LdrxQQpbWsX7YkEyukXt5a1xQcaBulE4ZESWwk2iIF1n\n3qq478ULYtpeiRsq0q8SWzVpOQs/iD3mU+so1yx6sU9Mw7WYmnObeWtcUMW3Sav+IZgj9rYOJORN\nYqoIqNPu1e3EXQ22ebji/pCYIXaIBeLGivRfVV9xrlB9T/IXvCFi1rRbfbXIQ5/pYjFU64clJ4uF\nzfIsBtYgL40HpHygzhQnK085ekIxHafh43orrsJ3kk9H8qZy8PTieeGo+eJtLxfyTLE9U85IA9s9\nGt/jz4YszUefOcXr9hrpi7G5wTYqyUPjunhRfHKXlD17ThzDTWmk4iZQkG7qXynOsKkeP92vf/y0\nBudU1HGdOHWq9sUch724KYUtrWCj0OWMKmnjxfF32r3ztOShcTkFCb5dLlaMJRaK2DTPaSIvNojO\nDE/IN076FelEEVtV8ix+Eg4oX6BMEiHRI/UY3kSGimPuXVXSZmO3+BjlTR4al5Po2148Lkb8S+Lt\nnFMrcxsYhy1imi1tD+0Vp2W3DVDueun2+LaKMKeS0vbTzeKIdHvRji3ipKrdTBZ27Raa7BO2bRba\n7HFUr0ubZEOjGmf17THHfLwmYsDKU5OlxbSxbbDrWKKpGrf71zKtZBhuEfFTn+j7QTFzfNZGu44l\nuhp36dKlS5cuXbp0OW74H3nWOWF+Ncp/AAAAAElFTkSuQmCC\n", "prompt_number": 46, "text": [ "\n", " 2 2 2 \n", "J - 1\u2a02 S - L \u2a02 1" ] } ], "prompt_number": 46 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we apply this to our state:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "apply1 = qapply(hso * state); apply1" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$- \\hbar^{2} l^{2} {{\\left|l,m_{l}\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},m_{s}\\right\\rangle }} - \\hbar^{2} l {{\\left|l,m_{l}\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},m_{s}\\right\\rangle }} + J^2 {{\\left|l,m_{l}\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},m_{s}\\right\\rangle }} - \\frac{3}{4} \\hbar^{2} {{\\left|l,m_{l}\\right\\rangle }}\\otimes {{\\left|\\frac{1}{2},m_{s}\\right\\rangle }}$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAqIAAAAhCAYAAAACusmsAAAABHNCSVQICAgIfAhkiAAADGxJREFU\neJztnXmwHEUdgL+XlxCSvCQaEYNB8gh4ABINqCQlOQ2IJCRgcVgaEEwMl38YSSQUWgSvRCSJligi\naB4q4TCJQcEDChwojVgR7wOrVMbCAkHU4lIgJs8/fjO1s7MzszM73XPs/r6qV2+np7ent/ebnu2e\n7h5QFEVRFEVRlB7gOGAVsA64G5hTam56j13AAWVnIgPnAH1lZ8JD3TWP+qhUFXXTLlqfdk7d3IQK\n+TkAbAhsnwn8B5iSIQ2VNx8uMFhyHrJwObCw7Exgxl2lFRf10TRaR5rBRd20hdan+XCpl5tQIT+n\nA/uAw7ztCcAwImEaVN78uEQLPB7YBhxSZGZScBCwtexMkN9dJRoX9dEkWkeaw0XdDDMHOBtYAXyD\nzn9YaH2aD5d6uQkVqjv7kNa63z17FCLfjJTv7yZ5NwFjSjiuS6vAK5Dek+GIfVXgFuAVJechr7tV\nR31MTxV8jKOb6kgfdTM9tt18Eniv9/oMpJEzvoN0uqU+VTezUVjdOQ54C/DqFHG/DmzMkHYn8m7L\nkH6RLAHOLeG4LvGSVlXgE4HLCjiOTXd91MdmXNRHk+S5wKubzbiom2Fej9STAKcDe4j/IWq6Pq2i\nn+pmNgqpO5cCDyIFcUKbuMuBq0gevHoT8ISX3qER+9PI63SQbhH0A7eVcFyX+gnch4x1sznQuSh3\nnQxxi0R9TE8RPrZjhpeHnyI9oMPAr4CVoXhZGkxORJi6GY26KbdZL4/ZZ7o+hWr6qW5mozA/NwHP\nAqMT4ixG5APYn+RC2wg8EhGeR96kdIvkY0gLs0hc6inwpcDbLR+jCHedDHGLRn1MTxE+pqEP+Cfw\n84h9aetIHycmXN1spZfdPAZYDVwPjE2IZ7o+dWLCy/ZT3cyGVT8nAU8D/0Na6M96f78NxZuLiDfZ\n+zsVmJWQ7m5kUHQQE/JGpVs0U4HPFnxMl3oKfCAyvsQGRbrrZIhbNOpjekz4OB0YmTONo5Fy2hAK\nz1JH+jgx4epmK93uZhpWIr2eA6FwW/WpExNetp/qZjYi/cxbEfo8h3TBP4AMmL0pEO4zDbiDVnEn\nxqQ5AbkF9eVA2FxksOudiLwzgceRLyYtUemWwV+RzzIG+G/K94xGJh4ciwg3DLwA7ERu03UrTwB7\nkZl3jxlOuyh341Af64cJHz+E+ObmyMc87/8PA2Em6kgfdbN+2KorZwK3e/8fBu4HrgNOonn8po36\nNI4q+KluZsPmtRyA0zA7822Rl95rvO1pwDM0vjj/b0JCGk6KdMvknTRmIbbjBGAL8Faab7eNB85H\nTsaXtUnDpb4tqQXARyylbdtdHydD3DJQH9OT18ch8n++HciEEX8CSSd1pI8TEaZuRtPtbkbxJuBe\nGrfaTwZeBA6PiGu6PoVq+6luZsPmtZzNSJd8P/BSZKD8c8CNHaZ3NfBozjw5OdNdCnweaf0dirRg\nNgDrgfuAU5Be5TVe+FeR2wSjUqY/CtieIt5pwC9IHpC9ChkIPCkhjkurpO8BrkUEvgX4QIr8+Ngu\nnyD+QOcRHby3HUW562SIG4X6mEydfBwi3wWjD1lO5yc50gjiRISpm830iptxLAMuQcpvOzILOgrT\n9SlU28+6uwnd4Scg40W+j4h3A3Ak0pX/7w7T+xlwc848OTnS3Y/G2I/dwI+Qk9BvxVyKdDNvpCFF\nP9IjkbZ1BPJFJw12noy0oMYCXwFeFxHnQmQdsUFExjhczLWWiiqfIKuRlrhpinLXyRA3jPqYTN18\nHCLfZ38DctH5VI40gjgRYeqmGermZl5M16dQfT/r6iZUwE9Tv0gnAG8Efgx8GPlV/3sk03/sIL2J\nXnr3GcpfJ+nORb6QPuSW19+RL2LY278HabVspTH+aq/3l2XR1hsQ+eK4CPg0snjwhUjZBm95rAae\n8tJxvXhTMxy/U4oqnyBDwHkdvjeOMt1VH83RLT6mZb7337GUvrppjl5y03R9GkfV/Kyrm9BFfp6M\nZHoL8kFAMrgPaSlkZbGX3hE58+XkSPcgZADydO89s0P7bwZ2hcKmeXEXZcznHQn7vhba7ke60I8H\nrkBmGwZZQGPGbBgXcy2pIssnyHakpW2KIt11MsQNoz4mUzcfh8j32XciY/TGtYuYEie0rW72rpt5\nMF2f+jih7Sr6WUc3oQJ+BmfNH40MlE277twvgQu813MCYX/xXi/00vpuBxmcj8z0/EMH7zWVrj+j\nawEyGy48m20ecrIFOQl4nmw9uYfRKLMo9oW29wIfRNYO3IFckII8hjxdxTZFlU+QkUhLOHiLJ4+3\nUK676qM5quJjmBuR2+hhDkGePPNixL7lyO3NOEYg3u6meTaySdRNc1TFzbx1ZRpM16dxVM3Purrp\nHwvK9zM3u5CFlYML3G5BBtP3d5DegzSeWJCnV9QxkO7twD2hsCOQ1kB4sPbdwLe811ORz34iclLG\nPeFkPVJBxBHXkpqNLI8R1ZJ6X0xaLs0tqfDs2qi/duQtnyycipy8JinSXSdD3DhMlHeSk+pjevL4\nOES6Xo2ZiIuXBMKOQT7Pxzs8dhROaFvd7F03o0j7+U3Xpz5OaLtq1/K6uwn19pOxSKs+PLj2EWS2\nHDS66NMwgLQWLva2v5Qjb06GdF+LLP4cZATyi/2jofCLaL0tNglZxPd0b/uLoXxEzTAcRePLjOMK\n5JnRIIOKr6N5bMka4N2B7auBV8Wk5dJ5l77N8knLDpJnE2alaHedDHFtl7dDq5PqYzby+DhEus9+\nDXIheH8gbDOS97xDl4I4gdfqZm+72Smm69MgTuB11a7ldXITKuiniclKs5Av4tuBsKnAwcgA2GXI\nYNe0+Hn6E9I1/YCBPLZLdz7wEI2TxWcG8BJaW2Pzab0tNoi0Cu4C3gz8zgsfhwyqvj8iT0uQVkgS\n1yIDmcd6rzcjS0D4fAbp4l6BlPtozD/yzGb5pGUQWRLkXxnfl0SZ7pblI8Q7qT6mZxDzPkbxELAW\neaQiyNi4i5HeBNNDl3zUzc7pJTfDmK5P46jatbwubkIX+3klMlZgTCBsCvAP5MvppNW+GhlP8jny\n/Vh2UqZ7JJLfh0PxlyCPJtsvIt3zQ2H9wDeR1tm6QPrvAL4Xk79baS63OJaQbv2xu0genO7S2pI6\nznvvOqS7fQ6t2Cwfn1OQ1tfZRN+S+CSyCLBJinbXSRnXdnnHOak+NrDt4xDpejX6gU8gPSRfQMaS\nRZVJXpzQtrrZu26mYSMy0SaIjfrUxwltV+laXhc3wb6f7dyEYvysFE7G+FdayMNGosdCDCInUVre\nhiztMIvWJzKs9PYd0CYNl2aBB2h+TvWZyLIRU2Leb6N8QD7Dr73XL6f1ZB+JnJx1x8kY31Z5Rzk5\niProU4SPQ1TrCShOxvjqZve62Y65yESheZaPE8TJGL+oa/kg9XMT7JRPOzchxk9Tz5rvFka3j5KZ\nhcBZEeHLEenScg9ye+MsRLRhROQ9SC9JJ8/bnYas9XY98GdkEeIxSGvltoj4NsoHZLzJBOA3wA+A\nc0P7l9B8u6dXsFXeUU6qjw2K8PFppPeorqib3etmEhORVSBsDQsxRVHX8jq6CXbKp52b0KPXcidD\n3NnAuwwf/0AaXeADgfCRwDbDx0qDS3NLqg/p0vdbZkcR/4xgG+UTzMcYZCbdvTS37kAGgk+0dOwi\ncTLEtVXeUU6qj830io9BnAxx1c3edXMN8iPGobo9okVdy+voJtjzs52bEOOnlWd91pCRyHNgbzWc\n7vHIF3IssmagzyKSF78timFkzTB/eYe1wCaaB1GDvfIBOaGeRGZB7kQGcv8tsH8ashzIUxaOXVVs\nlneUk+pjg0HUxyTUTaEX3VyC3G59wVL6JijyWl43N8Fe+QyS7Cb0cN15RsnHPxxZ0HoNzT/6N9O8\nzlpRuMSPS1sOXEX6xY5NMYCcOMuQMTKrQnlYT3EL+9qmbB8h2kn1sUEv+RhE3WzGRd0M8krgnMC2\nQ7E9omX7qW4m085N6N66U8mIS7TAi2k8Tmz/mDhlMAoZ76J0Jy7qo1JNXNTNIOcBlyE/NtYCjyLj\nGfM84lHpDJd6uQlt/NTJSspc5FnAdwKTkSe5PI7IXjZLab8+m9JdqI9KVellN8OPeLwA2Er22eyK\nHarsJmjdqQTYRfPSENOAZ2h9HNiE4rMWyTVUJy+KedRHpaqom9EcjNz6fR74DtILpxRL3dwErTsV\nRVEURVEURVEURVEURVEURVEURVEURVEURVEURVEURVEURVEURSmC/wOumnJ1lMu6vAAAAABJRU5E\nrkJggg==\n", "prompt_number": 47, "text": [ "\n", " \n", " 2 2 2 2 \n", "- \u210f \u22c5l \u22c5\u2758l,m_l\u27e9\u2a02 \u27581/2,m_s\u27e9 - \u210f \u22c5l\u22c5\u2758l,m_l\u27e9\u2a02 \u27581/2,m_s\u27e9 + J \u22c5\u2758l,m_l\u27e9\u2a02 \u27581/2,m_s\u27e9 -\n", " \n", "\n", " 2 \n", " 3\u22c5\u210f \u22c5\u2758l,m_l\u27e9\u2a02 \u27581/2,m_s\u27e9\n", " \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", " 4 " ] } ], "prompt_number": 47 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note this has not applied the coupled $J^2$ operator to the states, so we couple the states and apply again:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "apply2 = qapply(couple(apply1)); apply2" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$- \\hbar^{2} l^{2} \\sum_{j=m_{l} + m_{s}}^{l + \\frac{1}{2}} C^{j,m_{l} + m_{s}}_{l,m_{l},\\frac{1}{2},m_{s}} {\\left|j,m_{l} + m_{s},j_{1}=l,j_{2}=\\frac{1}{2}\\right\\rangle } - \\hbar^{2} l \\sum_{j=m_{l} + m_{s}}^{l + \\frac{1}{2}} C^{j,m_{l} + m_{s}}_{l,m_{l},\\frac{1}{2},m_{s}} {\\left|j,m_{l} + m_{s},j_{1}=l,j_{2}=\\frac{1}{2}\\right\\rangle } - \\frac{3}{4} \\hbar^{2} \\sum_{j=m_{l} + m_{s}}^{l + \\frac{1}{2}} C^{j,m_{l} + m_{s}}_{l,m_{l},\\frac{1}{2},m_{s}} {\\left|j,m_{l} + m_{s},j_{1}=l,j_{2}=\\frac{1}{2}\\right\\rangle } + \\sum_{j=m_{l} + m_{s}}^{l + \\frac{1}{2}} \\left(\\hbar^{2} j^{2} C^{j,m_{l} + m_{s}}_{l,m_{l},\\frac{1}{2},m_{s}} {\\left|j,m_{l} + m_{s},j_{1}=l,j_{2}=\\frac{1}{2}\\right\\rangle } + \\hbar^{2} j C^{j,m_{l} + m_{s}}_{l,m_{l},\\frac{1}{2},m_{s}} {\\left|j,m_{l} + m_{s},j_{1}=l,j_{2}=\\frac{1}{2}\\right\\rangle }\\right)$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAB24AAABKCAYAAABzXXRYAAAABHNCSVQICAgIfAhkiAAAIABJREFU\neJzt3Xu0LGV55/HvuQCCBxhQ8ShREE0QFBwVgyjiEVAEFYFBdJCAFxwTQUdFYxg1s01AEMU4Bh3v\nbqOjUYy3ZCJeoltE0WU0Rh0l3tJjYhSD0YATUSNn/ni67d69+1Jd17eqv5+19jp9qV3vs+t0/56u\n7uq3QJIkSZIkSZIkSZKkJbQZeHnTRUiSamPuS9LysgdIUprMZ0mSJCVna9MFLKG9gScDOxquQ5JU\nD3NfkpaXPUCS0mQ+S5IkSVpnrekCJEm1Wmu6AElSY9aaLkCSNNFa0wVIkiRJozY3XYAkSZIkSZIk\nSZIkLTs/uF3Mw4AvApc3XYgkqXJmviQtL3uAJKXJfJYkSVKn+cHtYj4M/Bj40Jzlzq+hFklStbJm\nPpj7ktQ19gBJSpP5LEmSpE7zg9vF3Br4j8DVc5a7bQ21SJKqlTXzwdyXpK6xB0hSmsxnSZIkdZof\n3C7mGOBa4OYC69gdeCZwCPDs/nVJUnrKyHww9yWpjewBkpQm81mSJEnSr1xOvLCfZ6XiOiRJ1cua\n+WDuS1LX2AMkKU3msyRJkjpta9MFtMzxwGPHbrs9sdOwaeS2o4FbjVy/Cbi42tIkSSWblPlg7kvS\nMrAHSFKazGdJkiRJAOwH/H3/8rY5y67MuG/njB9JUhoWyXww9yWpS+wBkpQm81mSJEmd5zduszsa\n+BhwX2Dv/uU8tgDnMTx/ymXFS5MklayszAdzX5Laxh4gSWkynyVJktR5m5suoEW+RHzQfSywVmA9\njwDeR+wUHEnscEiS0lJW5oO5L0ltYw+QpDSZz5IkSeo8P7jN7pvAOcBLgVvmLPvTGffdFfjP/cvf\nAu5UvDRJUskWyXww9yWpS+wBkpQm81mSJElS6XYD9uxf/hBwxwZrkSRVz9yXpOVlD5CkNJnPkiRJ\nStKWpgtYQr8Efg48iDhC9IPNliNJqpi5L0nLyx4gSWkynyVJkiT9yt7A7zddhCSpNua+JC0ve4Ak\npcl8liRJkgTAecAu/Z/jG65FklQ9c1+Slpc9QJLSZD5LkiRJ4kzgRuAG4EfAPZstR5JUMXNfkpaX\nPUCS0mQ+S5IkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIk/cpm4E3AYU0XIkmSpMocArwN\n2NJ0IV1xHbCzwp+X1venSJLmMPMlaXnZAyQpTV3O59cApzU4viRJkupxGvBmYFPThXTB4xi+mP9/\nxCfj82wBbgXsCexPHDl5HPBfif+Y60fW+S/AHqVXLUnKw8yXpOVlD5CkNHU1n58OXNzAuJIkSWrG\nJcBzmy6iK97A8AX9l4DdC65vK/Ao4BP9dT614Prq9Erg2U0XIUkVMvOHzHxJy8YeMGQPkJSSruXz\nIcAXgN1qHjc19hpJy84clJbLrYCvAIc3XUgX7AF8leFOwmtLXPeziJ2OtjiAeHDV5fwax5IkMPNH\n1Z35YO5LapY9YMgeICklXcvna4EH1zxmiuw1kpadOSgtn4cCf4NTJpfiMOCnDHcSHlviui8ipuzR\nRitNFyBpKZn5zVlpugBJS88e0JyVpguQlLSu5PNpwMdrGksbrTRdgCQ1bKXpAiRxDXDm+I1bGyik\n7b5MTFvw6v711wGfA75dwrovAk4vYT1VOoOYwucY4CkT7t8XOIc4YvQlwKHEuWQOBP4c2A4cDNwI\nvKykmpoYU9JyMPPTy/wmx5W0XOwB6fUA818SdCOfN/XHemYNY6UsxV7T5LiSlo85KC23i4E/Bt4F\n/HtTRRxJTD2zAnyECKS2ejfDozs/B+zabDm1OBI4AdgMfH/KMucCuwDfAM7q37YH8DPgqP71Q4mp\njRaxMuO+qsYE+DRw2xy/J7XF2aQxHUPq/cHMn6zK/F2ZcZ+5L+WTSuZnlUpvsAdM1kQPqHJMsAeo\n29rWA7Jocz4/FPhmCetJpVfmkWqvqXpce426LOVek2JemoNS9+TJwW8DJ4/eUOc3brcBpwK/179+\nBnAV8OvAdzOu40jgAcDewAOBPwSuLrfMzM4FjiDmnz8CuIzuHyn5HeCzwL2Br01Z5p3AfkSQv61/\n272Iubqv7V8/Avg/M8a5PbEtRx/gR7N+nv+biKMRyhpzmjsSj90bcvyu1AZ3IqYC+2iDNZTRH6pm\n5k9WVv6a+1I9Usj8rFLqDfaAyZroAVXmP9gD1G1t6gFZtTmfzwXeUXAdZfbKJt5vS7XXlDnuJPYa\ndVmqvSalfYtR5qDUPXly8B3Ea8MPVFLRHIcDtwB37V/fizgq8oyMv78NuHTk+hnAvwH7l1VgDkcB\nv2B4hOejGqylTs8GXjTj/rOBt45cfyExBdDAVUSz3GeBMVfm3F/FmAA9YgoIqY32JI5Cv/OMZe4A\nvL2ecqYq2h/qYuZPVlX+rsy539yXNpqX+1Vn/jHAbxE7HG8Dji+wrtR6gz1gsiZ6QFVjgj1A7dWW\n1/1VaGM+bwNuBu5ZcD1l9cqm329LsddUOW4Pe43aq+n9jbxS27cYZw5K7VFFDh5GTJN8mwJ15baJ\nOIJvcFTHPYiAvHfG3081YC9kuIPwQ+IT9a77AHBs//KxwP3G7n8LMQf+wCeAHf3L+xBH0+wCXNC/\n7SLgBXPGXJlz/6JjZtXDRqJ2Opd43uxk/mP4T4kj75pStD/UadkzHzbmfhWZD+a+tKisuV9l5t/A\n8Hn5GOJN3z1zrivF3mAPSKMHVJX/YA9QO7XpdX9V2pbPJzN9SsxFlNUrm36/LcVek2fcrHrYa9RO\nKexv5JXivsUoc1Bqhypz8AfAY3NVlcGtgd8kphmY563A5QusO9WA3Ux87Xmwk3ANsKXRiqq1hXgQ\n7d6//nbgPWPLfIo4sgDi/+urDM81sztxAvOnAIf0b7uImFphlpU59y86ZlY9bCRqtyxv4DyMeLOj\nSlX2hzote+bDxtyvIvPB3Jfympf7VWb+PYm8Bzid+AbUrA9u29Yb7AFp9ICq8h/sAWq3VF73N6Ft\n+fwqYgrKLOrolU2+35Zqr8kzblY97DVqtyb3N2Zp277FgDkotU8VOXgl8Ma8Bc3yaODzRNEPnbPs\nk4lzj8w6Se//IkJrJ3CXCfenFLDbgesZ7iS8uNly5roHsf0/SezcXAm8ntjOm4ijabZP+d0jiNAe\ndc6kBRfwQeafsPl5BccYd1z/50LiyKBpemRvJFcAXypUlSZpcrvOy6F5UnhMZHkDZxPwERY/cXpW\ndfeHqrUt8yF/7k/KfCiW+1kyH9LP/RSe313U9HZdhtyvOvMH3g48f8b9ZfeGutgD7AGQxnO9i5re\nrm3vASm87m9Sm/L5i8S0mPNU3SunPWbzvN9mr7HXtEXT27XtvQbS2d8YVce+xbxtbw6ag23R9HY1\nBye7gOnnui7s5cBPgN1mLPNIIiAhTn594IxlLwf+YcLtKb15M3ACMbXMzv6/85pEE/YiGsZ3gLNY\n//90O+JInjcD14393i7AG4hvTLwceOrIfVuA3ylQ03aicdXtMuAS5h8R1CP7B7dvBP44f0maount\nOi2Hsmi6dsj2Bg7Ei7UTKqyjrv5QlzZkPuTL/VmZD8Vyv6nMh/JzP4XndxelsF2XIferzPz7AM8h\nsmePOcuW3RvqYg/Ixx6geVLYrm3uAam87m9SG/J5V+DnwMMzLl9lr5z0mF30/TZ7zZC9ph1S2K5t\n7jXQ/P7GNFXvW0zb9ubgkDnYDilsV3Nwo5OI89zeKmdNE+0L3Nhf8S1ESP4E+MrYcg8mwnF7/+cU\n4KgZ6/0c8Lax21J882bgMoZHd34fuG2z5axzF2I6g08z/STHdydqv2Ls9tsQR/48A3jW2H2PA/Yv\nUNcTgDOBAwqsY1GnEk+aBxJHP83SI63HWBGHA1ubLqKFJuVQm2R9A2c/Yv79stXZH+qWcuZD/tyf\nlflQLPefQP2ZD8uZ+2Z+finkSxFZcr+qzB/1X4ij3rdNuK+q3lAne8DinoA9oC72gPza3APqet2f\n+uMr9Xy+F1HbvCxsolcu+n6bvWbIXqNFtLnXQD37G4s8vprctzAHh8xBLcIc3Ogu/fXeJ2dNE+1G\nnA9jJ/Dfgbv1f+4wssxBwE0MX0APfvaass69iMB9yshtKb95A3G0zGeJv+vTlPzpeAH7An8HfIM4\ncfgs1xFBW5eziaOKDqxxzAcQDfAk4MQ5y/boRiMBWKU7f0tdJuVQ22R9Awdiqoo7zF1qMXX1hyak\nmvmQbu43kfmwnLm/Sjf+jrqlki9FZM39sjP//sQUlYOpjgZvUJw+YdkqekPd7AGLswfUZ5Vu/B11\na3sPqOt1/+oC4zQh5XyGeB/rFua/0Vt3r1z0/TZ7zXr2GmXV9l4D9exvrGYcA5rbtzAH1zMHlZU5\nONmu/fWWnhWn9ld875LW94j++n6jfz31N28GXkIEdkpHdb6D2FbHZVj2k8B/qLacVunRnQBepTt/\nS13Gc6iNFnkD51jgBRXUUHV/aFKKmQ/mfhE9upGVq3Tj76hbSvmSV9bcLzvzjwA+xnBqsJOIqSDv\nNmX5sntDE+wB3dOjG9m5Sjf+jrq1vQfU9bp/dYFxmpJqPgM8DfhhxmXr6pV53m+z1+TXI/3nUBar\ndOPvqFvbew3Us7+xmnGMgSb2LczB/Hp0Iz9W6cbfUTdzcLp/Ac6Dcr/KfQzxQu9LxFEmrwROA95N\nvpNpPwT4HvD1/vVvE/O+p+zxwJOII0xuaLiWgSOJo10+CfxVhuWvAH5caUXdcjjwROLIie8Dz5yz\n/KOB44npkc4hjs56DPFkfwDwMuIE8s8ips3Yjzja4onAL8ovP9k6F92uVRnPoSxSqf3xwNH9y5cC\n17BxGvRxHwcuBF5MHAVelqr7Q1NSzHww96tk5nc782G5cr/szP9r4E3A+f31HU1MufjNKcuX3Rvq\nZg9YPvYAe8AkKdSf0uv+FKSazwPbgX/OuGwVvXLSY3bR99vsNdWx19hrJkml/qb3N2apat9i2rY3\nB6tjDpqDk6RSf9U5+APitWKpPg9cRYTjG4BDgfcDP8q5vr8mjlxpi2OAfyXmcU/Ja4kAaMMbYCnq\nMf3oiTsCrwA2A/cgtvNhM9a1a395iHncrwEuADb1b3se8eS8fGTMLcQLjzL+/1bJdiRI03Uuul0n\neRPwxQV/dkxYz6I5VEbtZdafx3OIb0mVqYv9IdXMB3O/qB6Ts9LMTzfzwdzPq4rMz6rs3lAne0B3\n9bAH2APqrb+Nr/tXSffbJSnn88AfET0wi7J7ZVnPOXtNMT3sNfaa9ta/qLp6TRX7FrO2vTlYTA9z\n0Bxsb/2LWiQH/4Z4rViavYBfAi8ELmF4pN7fAp/Jsb69iXmuf7uU6qr3G8QRk2c0XcgE3yAezL/W\ndCEt1WN6AP8Rw2kuTia288Ez1vVQ4jxvm4ipkd49dv+zicf9fcdu/zHwu1kLnmGVbM2k6ToX3a5V\nyZNDqdRexG2BK0tcXxf7Q8qZD+Z+UT0mZ6WZv17XMh+WM/fLzvysyu4NdbIHdFsPe8Aoe8BsKdWf\nR94esEqaH9ymns8D/5M49+48VfTKsh6z9ppiethrRtlrZkup/jzq6DVV7VvM2vbmYDE9zMFR5uBs\nKdWfxyI5eC3xWrE0JxEb7M3EuTEAbk98/feSHOt7ZH99h5RSXbVuS0wBV8aTqAo/BX6Scdlfr7KQ\nluoxPYDvPnL5ZcSRMbPcAdid+Gr/TuBBY/e/g407cAf1l33E/FLnWiVbM2m6zkW3a1Xy5FAqtRf1\nZ8RRimXoWn9IPfPB3C+qx+SsNPOHupj5sLy5X2bmZ1V2b6iLPaD7etgDRtkDZkup/rzy9IBV0vvg\ntg35PPAm4BMZlquiV5b1mLXXFNPDXjPKXjNbSvXnVXWvqWrfYta2NweL6WEOjjIHZ0up/ryy5uDV\nwBth/TluDwNex/Ar2vN8keEn48eM3Pbt/uXj++v6y4zrG/UQ4Hrgazl+t067Ae8DPgJc1nAt09xI\nHHU0z67A04FnVFtOp1w3cnkH83e+vtf/91iiwX927P4dxIuMUQ8Hbs6w7lFvIebLH3dn4DeBn0+4\n78kMp2uqq85pFt2uVcmTQ6nUXsRW4uinwXQyRXoDdKs/tCHzwdyviplfrM5pUsrNZcz98cyH4rmf\nRdm9oQ72gOVmDyhW5zQpZag9YKOij6+6tCWfB34O7JJhuSp6ZVmPWXtNNew1xeqcJqWsttdsVEav\nqWrfYta2NwerYQ4Wq3OalHLEHJxtFyY/nnL7NPE17z1GbnszcAMxH/eiPg+8q3851W/dbiKOhvjf\n5PsbJ9mV8ufGfz9xAus95iz3XOA+JY+9iM3Ayxscf5oe84+cGXzF//yM63w/G09cfwhxtMnDxm7/\nCPDe/uUDiMfaw4gXI5dnHG9glcWOjC5aZ1HTtuuexJQSd57xu68n5stf5OfBY+sokkPTaj+SOMn8\nCrHNjmGyovXvzPAzzSmUe4L3rvSHtmQ+mPtF9ZidlXVnPuTL/VWWJ/MhzdzPmvll1J8398vM/EVq\nKLs3VM0eUD57gD1g1DL3gDa+7l8lnW/ctimfB7Ke47bKXrlolo2z1xTTw16Tt84ilrnXlFF/3fsb\nq2R/fFW9bzFp25uDxfQwB/PWWYQ5mH4OlnqO2z2IT4HH517+B+Ct/csHkd024oiV8/rXX1Oouuq8\nGPgCUW9ZnkfMd16mHcTUEE+bscyjiQd4U/Ym5nL/QoM1TNNjfgA/gnhi3nPs9oOBW43dtpk4uuKF\nY7c/jXge3Xrktn2JMBo8Jl49ct8aGwN9nlWyN5Oy6pxl0vYZNWm7nksE8U6qfbNgXg7lqX0bcOnI\n9TOAfwP2L1Rp+d5D/J+WoUv9oS2ZD+Z+UT1m50sTmQ+L5/4qZv4iys79Zcz8rMruDXWwB5TLHmAP\nGGcPaEbeHrBKOh/ctimfB/6A9d8cmaTqXjkty7Lagb2miB72mgF7zZC9Zr1Vsv2f1LFvMemxswNz\nsIge5uCAOThkDsLfAX9Y1sDHERvsxJHbDujf9lTgLOBOC6xvcELxE4hPzJ9QSpXlehLwHWJ+8rKc\nBXyfOLqzbM8BbuqPsXnk9tsRD4SnVzBmHmtNFzBBj/mh9VLgn1k/peBDiOfA+Imn78vkueyvBD41\ndtt9+svuBdyPYajdmjg5+awgm2SV7AFcRp2zTNs+oyZt14Gqm8msHMpb++HEi7q7joyxk2gqqTiQ\neJyUpSv9oW2ZD+Z+ET1m50vdmQ/5cn8VM38RZef+MmZ+VmX3hqrZA6qz1nQBE/SwB+Stcxp7QPd6\nwCppfHDbxnyGeIP2h3OWqbpXznrOZWWvya+HvSZvndPYa5a319SxbzHtsWMO5tfDHMxb5zTmYDdy\n8Edk+//O5EXEfNu7j9y2P7ER30++qSyfQ8xB/z9YH3wpOI742/IemThqE/H17fcSD6orSljnNEcT\nX3n/PPAh4klwOWmdIH2t6QIm6DE/tD5HnGB61KHE4+Tvx24/GfgKG3cE14gXFKO2EP9PryGOfBk8\nF04EPjinpklWyR7AZdQJ8Chix/S3iB3rgWnbZ9Sk7TpQRzOZlkN5a99ENKVBc7kH8Xfcu4xiJ1hk\nqoiBi4EHllhDF/pDWzMfzP28eszOl7ozH/Ll/ir1Zz5Mzv02ZD6Um/t1Zz4snvtlZ/64y4kcGldF\nb6iKPaBaa00XMEEPe8DAGvYAaEcPqPt1/yrNf3Db5nw+hXiTceuMZarulbOec4uw1+TTw14zsIa9\nBtrRa6De/Y1Vsv2f1LFvMeuxYw7m08McHFjDHARzEOKxsRM4NW9xy2zwADo+4/KbiA1+a+Jom4OB\nBxBfQX8V8C3Wz4F9RMn1ts1a0wVM0GN2aO1FTF0w7STzLyq5HogXAG04F9GewJf6l2/H5OY3bfvM\n2651NZNZ8tY+8FYWP09xVnmmitgKfLiietrKzK/eWtMFTNBjer40kfmQL/dXqT8n5+V+mzMfiuV+\nlZkPi+d+1Zn/YOB6YgqxtrIHVG+t6QIm6GEPyMseMF3XXvev0uz/Sdvz+V40+7jOur/aFWtNFzBB\nD3tNXvaa6bq2v7FKGv8nXcjMtaYLmKCHOZiXOThd23PwIOL/ocnzYrfS7YlP/HdW9PPl+v6UzPKc\nlLuItZrGWUSPjaF1OrGzBzEdxi+Bu0z5/Uun3F7E3wJ3z/F7rwS2l1zLLLsT2+/LwMuI59C40e2z\nyHZNoZnkrR3gycBlFJuaapY8U0WcxsaT0y8zM78eazWOlVWP9fnSdOZDvtyvO/Nhfu63OfMhf/1V\nZz4snvtVZv7exM7UGu394NYeUI+1GsfKqoc9IC97wGRdfN3fxONroAv5vCtxLruTahhrYNH91arY\na0IPe01e9prJuri/0WSvqTIzzcHQwxzMyxycrAs5+Ajiw+lFT4+59F4FXFfhz2/X96csZI3FTspd\ndKzU9FgfWocRT8iLiDcmv8n0I0EeBDyu5Hr2YzhlwLaS1122TUQzOQX4GBsb6+j2WWS7QvPNpEjt\njySaCUQQH1hBfXmmingvUb+CmV/feKnpMXxeNp350J3cb3PmQ/7668h8WDz3q8z85wK70e4Pbu0B\n9Y2Xmh72gLzsARv5ur98XcnnLxJTBdZh0edc1daw1/Sw1+Rlr9loGfc3qlRHZq5hDvYwB/MyBzfq\nSg5eAHwtd3VaKnlOyp3H7sRUBNcDz2b9eQma1mP9k30b8FHg1cD7gCdO+b2twMsp/yiP04A3Eic0\nP7bkdZfpQOCHDOftfwzrjzAZ3z5Zt+tAk82kSO0PJhrJ9v7PKcBRlVU6NG+qiIOAN9RQh9JWV+ZD\ne3K/6cyHbuR+mzMf8tffVObD7NyvMvNPZni+wTXa+8HtMrIHhB72gDwOxB4wztf9muVVxDkR67Do\nc65K9prQw16Tx4HYa8Yt4/5G1arOTHMw9DAH8zgQc3Bcl3LwXcTjEKj2q8NqvxOJqe5OnHDfvsA5\nxJPjJcR5ZvYknuB/TjxRDgZuJL62X5a6x+0Rbzr2SlhXGe4GvJA4mfnlxNfzU7SNaBz/COxBvDB5\nBdEEing8cDRxJPQ7gWuAKwqusy4HEdNujB+5tTfxeK3Kk4nnxPOYvv0vIZrgZyusQ+kz80MPcz+P\nKnLfzM9nXu5Xlfl3JM43+Cf962vACmke4a2N7AGhhz0gD3vAer7u1zwnE2/m7dd0ITWz14Qe9po8\n7DXrLeP+RheYg6GHOZiHObhe13LweiIf3lm4OnXerJNynwvsAnyDmGMcIjB+xvCohkOBr5ZcU93j\n9mh+mgApryxTRewCXFVXQUqamR96mPtqr3m5X2XmPxG4EPi9/s8/Aa8jztOi9NkDQg97gNrJ1/3t\nsg24mThX2jKx14Qe9hq1V5P7G11gDoYe5qDaq4ocvCdxftvbFKpMS2PWSbn3BPYHvjty21HAZ0au\nnw1cmWGcnTN+qhw3ix42ErVT1qkiTgd+p8a6lK66Mh+y537dmQ/mvtorS+7Xmfk9nCq5TewBoYc9\nQO3j6/52+lPg4qaLqJm9JvSw16idUtvfaCNzMPQwB9VOVeXgRcAHRm/YmrNAdd9+wF7AdcTRoD8Z\nu/8m4FTiJNgDxxNzjw+cCbwW2Af4EfEAvLn/76gtwHkM59q/bEZdecYt4p/Y+LdLqTsI+AsmTxUx\nbgfw36ouSMmrIvOheO7Xnflg7qudsub+DqrP/F8jpvfZDlzQr+kvKh5TxdgDhuwBahtf97fX64mZ\nKZ7fdCE1sdcM2WvURintb7SVOThkDqqNqszBM4Fn5Sur3V5JnIRb2U06KfexwP1GlnkLMQf+wCcY\nfrNiH+AG4qvhF/Rvuwg4YsK6HgXcqX/5z/pjjio6rqTlYuYvrorMh2Huj69rVu6b+ZKKsAcszh4g\nqQ7m83qbiHPpPbzpQmpSda8ZX5+9RlJqzEFJkzwc+BZL+iXbA4j5plN0ftMFTHE3IrSfC2zu3/Z2\n4D0jy3wKuEP/8iZirvtd+9d3J05g/hTgkP5tH+wvN76uZwK/2798GfEV81FFxz2u/3Mh0WQkdZuZ\nv7gqMh+GuT++rlm5X8a45r60vOwBi7MHSKqD+bzRKcDVDY1dt6p7zfj67DWSUmMOSprkk8Djmy5C\nG600XcCCzpm/yETbieY0aV27EfPpA3wIuGOJ40I0p0tY31wkqQkrTRewoCLZO577o+ual/tFxgVz\nX1KaVpouYEH2AEnLYqXBsa8l3vheVmX2mtH12WsktYU5KC2v44hzX28ev6PrX789gwipY4ijQgb2\nJY5u2TTpl/r+HXgR8Iv+8ucQJx9+CXAoEXwHEkecbAcOBm4EXlZS7U2MOc8WYI+cv/twokkcAPzf\nsXX9rP/zIGCNmOe+rHFPBf6KmDf/D4DH5FyPpPS1OfMHdaaU+0WyF9bn/j+OrWtW7hcd19yXlpM9\noFz2AEllMZ9nexLwDuD+xDkKl0mZvWaR95nsNZJSYQ5Ky2s34BXA2cAtDddSqyOBE4hPq79fcF3n\nEl/5/wZwVv+2PYjwO6p//VBiGoFFrTQwZl6PA/bP+btnA08ldm4mrWtv4PcrGPcB/d8/CTgx5zok\npa/tmV/1uHkUyV5Yn/uT1jUt94uOa+5Ly8ceUD57gKQymM/ZnA9cWuD326rMXjNpffYaSakzB6Xl\ndTHwvKaLaMJgXvZ7Ax8vuK49iTD77shtRwGfGbl+NnDlnPXcnphC4NKRn2vGrj+/5DHb4jxiZ2gX\n4PiGa5HUPm3P/DLHbQtzX1JZ7AHtYw+QloP5nN1rgP9UcB1az14jadmZg1KaTgVWmTHzTJenSv5e\n/9+HAFeP3Xcb4Dlkn5LnJmJjfmzk/uOBj45cPxN4LbAP8KMp67yeOGH3qBUmH91Z1phtcCax4/Qi\nYqqGBzVbjqQWanvmU+K4bWDuSyqTPaBd7AHS8jCfs3sa8Abg68CXc65DQ/YaScvOHJTSdAhwOnEq\njp0N19KoDwDHlrCet7D+pN2fAHb0L+8D3EAcvXJB/7aLgBdkWO9KiWNBlOvwAAABg0lEQVRK0rJr\nKvMhW+6vVDCuJCnYAyQpTeazJEmSlNHmpguo2Bbg/sC1JazrbsCH+5c3AbcDPt2/fnN/jCcAfzny\nO1c1MKYkLaumMx/MfUlqij1AktJkPkuSJEn6lSOATzU09geZPeXPQNknID6u/3MhccSnJC2LJjMf\nsuW+mS9J1bAHSFKazGdJkiRpAV38xu0uxHlB9iTmcv+TBmrYDvyAbHNUv6TksU8gzrfyPuI8MJLU\nZSlkPmTPfTNfkspjD7AHSEqT+Ww+S5IkKactTRdQgX2AZxA7CjcBVzRQw+nA14EbgX+tcdxT++N+\nhTiy88oax5akJqSQ+dBM7pv5kpadPcAeIClN5rP5LEmSpJy2Nl1ABX4IPLDhGm4hjizNMlVyma4H\n7kx8IP+mmseWpCakkPnQTO6b+ZKWnT3AHiApTeaz+SxJkiRJkiRJkiRJkiRJkiRJkiRJkiRJkiRJ\nkiRJkiRJkiRJkiRJkiRJkiRJkiRJkiRJkiRJkiRJkiRJ7fL/AWERTP06yLvtAAAAAElFTkSuQmCC\n", "prompt_number": 48, "text": [ "\n", " \n", " \n", " \n", " \n", " \n", " l + 1/2 l + 1\n", " ____ ___\n", " \u2572 \u2572 \n", " \u2572 j,m_l + m_s \u2572 \n", " 2 2 \u2572 C \u22c5\u2758j,m_l + m_s,j\u2081=l,j\u2082=1/2\u27e9 2 \u2572\n", "- \u210f \u22c5l \u22c5 \u2571 l,m_l,1/2,m_s - \u210f \u22c5l\u22c5 \u2571\n", " \u2571 \u2571 \n", " \u2571 \u2571 \n", " \u203e\u203e\u203e\u203e \u203e\u203e\u203e\n", " j = m_l + m_s j = m_l \n", "\n", " l + 1/2 \n", " ____ \n", " \u2572 \n", " \u2572 j,m_l + m\n", " 2 \u2572 C \n", "/2 3\u22c5\u210f \u22c5 \u2571 l,m_l,1/2\n", "_ \u2571 \n", " \u2571 \n", " j,m_l + m_s \u203e\u203e\u203e\u203e \n", " C \u22c5\u2758j,m_l + m_s,j\u2081=l,j\u2082=1/2\u27e9 j = m_l + m_s \n", " l,m_l,1/2,m_s - \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", " \n", " \n", "\u203e \n", "+ m_s \n", "\n", " \n", " \n", " \n", "_s \n", " \u22c5\u2758j,m_l + m_s,j\u2081=l,j\u2082=1/2\u27e9 \n", ",m_s l + 1/2 \n", " ____ \n", " \u2572 \n", " \u2572 \u239b 2 2 j,m_l + m_s \n", " \u2572 \u239c\u210f \u22c5j \u22c5C \u22c5\u2758j,m_l + \n", "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 + \u2571 \u239d l,m_l,1/2,m_s \n", "4 \u2571 \n", " \u2571 \n", " \u203e\u203e\u203e\u203e \n", " j = m_l + m_s \n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " 2 j,m_l + m_s \u239e\n", "m_s,j\u2081=l,j\u2082=1/2\u27e9 + \u210f \u22c5j\u22c5C \u22c5\u2758j,m_l + m_s,j\u2081=l,j\u2082=1/2\u27e9\u239f\n", " l,m_l,1/2,m_s \u23a0\n", " \n", " \n", " \n", " " ] } ], "prompt_number": 48 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We now collect the terms of the sum, since they share the same limits, and factor the result:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "subs = []\n", "for sum_term in apply2.atoms(Sum):\n", " subs.append((sum_term, sum_term.function))\n", " limits = sum_term.limits\n", "final = Sum(factor(apply2.subs(subs)), limits)\n", "final" ], "language": "python", "metadata": {}, "outputs": [ { "latex": [ "$$\\sum_{j=m_{l} + m_{s}}^{l + \\frac{1}{2}} \\frac{1}{4} \\hbar^{2} \\left(4 j^{2} + 4 j - 4 l^{2} - 4 l -3\\right) C^{j,m_{l} + m_{s}}_{l,m_{l},\\frac{1}{2},m_{s}} {\\left|j,m_{l} + m_{s},j_{1}=l,j_{2}=\\frac{1}{2}\\right\\rangle }$$" ], "output_type": "pyout", "png": "iVBORw0KGgoAAAANSUhEUgAAAioAAABKCAYAAACVW3/AAAAABHNCSVQICAgIfAhkiAAAE+VJREFU\neJztnXmUJWV5h5+enhkcZssMywzrDCORMDoIETOCMBvIEnYOIkGCIHBUIEoQMkHU0yQjwpDROEEM\nitCEiBpQFkUWBZtF0BAMWxQFpROVLSKbKJuQP351c6tvV93aq25P/55z6nRX3br1vVXd31fv937v\nAsYYY4wxphATgE81LYQxxhhj6mVi0wKkYCZwDLCsYTmMMcYYY2IZaloAY4wxxtTLhKYFMMYYY4yJ\no2pFZQ/gbmBNxe0YY4wxZh2kakXlBuBp4PqE806sWA5jjDHGjEGqVlSmAtsDtySct2HFchhjjDFm\nDFK1orIEuAN4ocA1pgAnAdsCJwf7xhhjjDGFWYOUjCQGKpbDGGOMMWOQqvOo7A68q+PYHKS89IWO\n7QK8LrT/HPCJakUzxhhjzHhmY+Dh4PdpCecOdPnstS6bMcYYY9ZhqrSo7ALcBLwFZZe9Ked1+oET\naPumrC4umjHGGGPGAlU6096LFKEVFMsquw9wJVJQFiPFxxhjjDHjgCoVlYeA9wDnAK8mnPv7Lp+9\nHviL4PefAVsUF80YY4wxphzWA6YHv18PbNqgLMYYY4ypkf6mBUjBH4CXgF2RZebaZsUxxhhjjBnJ\nTODjTQthjDHGGBPFCcCkYNu9YVmMMcYYY/6fw4FngV8DTwFvalYcY4wxxhhjjDHGGGOMMcYYY4wx\nxhjTQzxA9zo9Rbdz6rsVY4wxxqxrHEZbqXge2DbFd/pRFeXpwGbAImA34EPARcDjoWv+Bli/dKmN\nMcYYM264gLZicS/twoJ5mQjsB9wcXPN9Ba9njBm7rAVObloIY8zYZn3gR7SVlfNLvPZfI+XHGDM+\nmYcssHVyYs3tGWNqYBEqOthSVt5V4rVXoaUhY4ypg4GmBTBmvDGxhjbuQ+bZ84L9zwN3Aj8v4dqr\ngENKuE6vsgg4AljZtCDGxLARsBo4DnilpjYPRcVKlwTtdjIbVW5fCpwNLER+b/OBbwBzgW1QIsl/\nKEmmJto0xpTM5bStKncCk5sVpzYWo2WqAeDbaHBNwxzgu9hh2PQ+R6AJSB0sBvYEJgCPxZxzLCq3\n8SCSDdSPXgR2CvYXomXprAw00CbA7cCGOb9rTK9zJNDXtBAAfwQM01ZW/rFRaephGnBWaP9Q4Hco\noimJb6EBuQryKk9G+PmN5uvAwTW0s0nwcwekyEfRihr8VejYTsD3Q/tHApcltDUH+CTqw63tto79\n00tuM45hZJ0xZl3kdHqojt9OwMu0lZX9mhUnlunIArRlwetsB7wKvD7Yn4Hu+9CE7+0N/HvBtuMo\nojwZP784dkbLuXVZSk8Gzujy+ZHAJaH9j6Gl4hbXAQcBszK2O9BAm2BFxYxd0rxPNwEurUecdJxG\nW1F5EtiiWXFGcSwajF6j+MDQh2bfLZPWG4Pr7pDwvRuB9xZsO468ypMRfn7x/BDlT6qDq4EVof0V\nwFtD+xcjn5EWNwPLgt9noSKnk4APB8dWAR9N0e5Al8+ytpmFYayomLFHlvfpV5AVsyeYAHyHtrJy\nG0r01mukVVSmAn8G/HGKcy8B1iScMw85Jc5Ocb085FWejPDzi+cUZDWomn7gCUbmZboULT+1+B7t\nZaI+5BvSsvZMQQ6ux9FORLkK2DFF2wNdPsvaZhaGsaJixi5p3qd7IENGzzCXkVlmz2xWnEjSPNgD\ngLuCc9+RcO4xKDoiyWHoeFR+IC2nkO6Pey7ReWfSKE9VU/QeqpbjS+jF+BqwVcdndT+/PDIW4W0o\nK/R5wc+vAm+OOXcJ8AJaHkvDG1GfuBVNXi4DvoDk70MWirkR39sRKQWdvCfiWFquJZ0zX9kReLsF\n22nI2hLHMOkUlar7yHil6edatH83LX+a92kf8vnrCafaFnsiE/prwc+kF33dpLWofAr4LQqXjGNf\npKiAElR1u+6XkQksDfNQeYKBFOd+EfinjmNplacqKXoPdcmxBvhFx7G6n18eGYuwA/BNRiZV+xwK\nsd0+4vypyBq4IuKzMDOQQvI/KEIm3Hc2QpaRixipsE9Cma6noz7XmZW6H/hAQrtxzEVKUROsRg67\nSVaWYdKNR1X2kfFMLzzXIv27afnTvk9XUl0QSW5W07aqPEZvhd8lPdjZaMB+BSlavw22+zvOW4pe\naHOD7UDa4YpR/Jj05q/PB3IOpDw/TBblKYntyJ+Tp8g9lEmSHHcC/xraL/P5pSWrjEX5NKOTNO4b\nHFsb852fAKd2ueZWaEnkdmCDmHP+JGjj3NCxDZAV5YMo4qqTw8jv0HwUcDhSBOvkIDQov53kaKBh\n1o2lnyJjxXin7P5dJ2kVlY2JmKhPKFuajJxOO7rl5+hFP1Z4HlmB+lH0wfbBFrYMLUAz0guAR4Pt\nCuC/Yq7Zh/6YcfkhwhwM3JRDbpDyNAe4BilPe9FeW8/DycDmOb5X5B7KJEmOGci6cHOwX/bzS0NW\nGcvgP5Ey/pvQsdayzu9ivvMU+r+PYjbyYZkE7IOc6aN4ACk8N4aOPYle6GuRAtXJVxgZGpyFV5Gl\npm7L4uPIyXYmcGHNbTdF3rFivFNF/+5FngD+QPXjaWbORkmSesmaAuk0wIMo15FyVnC9pGy705Dy\nA9mtEQuA52hbslrbjCyCdjBI9tlekXsokzRy7BN89gaqeX5ly1glq5EVMc5P5TqkjEfxZSRjmrIX\nt6LcS0YMs25YVAZZN+6jburq31WRJYp2BR0ReE2b4N6NwnB3RqF7vcC7gV2C31sJns6NOXcJemnd\ni5SMtWjmezn5HPtaWWifTTjvNLSuncR2wNFIO30MOAlZrqbnkK1sitxD3XIsR9awnwb7dT+/PDJW\nwVbo//p44J6Yc54ieklnMVqeuZWRlpI4zgWeziHjeCRrHzkAJdd6M/p7zgbeiV4mO6MU/9eiJbYN\nkDl+ctDGy+WL37NyVj32ZCFP/+4F+bO8T1t8F415ZyJrZ6MsAZ5B5tyxyl1oBjkLzXgXAlehwToP\nm6FO2M2ZaHtG+rDEzbA3Rdl/J9AOoV2UU64kBsk2S+qVe0grx38ga0AT9IKM+wJ/jxTyj9B9yfhL\nwA0Rx89HsheJzBnPDBPdx7L2kcm0s4LfiV4cH6a97LUSmd/XhNrrRxOyMv52g6QbK5qWs6yx50Lg\n7ozbsojrZO3fZchflux5OAX485KulZs3AP/L2E6SNQOtpX0MzXZbs+x7GJk2OwutpZ+45zIBRSeE\ns3/Gvbg+Tdt0vn9w3jY55UpikPSKSq/cQ1o5ZqKljveX1G4Wek3GiSh88PvEL9VeQfTSz4NIdvsn\n5GOY6D6WtY+8Ay0t9yG/n8s7Pj8Z/S+9peP408DfZBE4hkHSjRVNy1nn+JlEnv7dS/LnYUNCDuZN\nONNuiOrYnAP8WwPtl8Uu6PktQKGWzyEHy0XE1yBJ4mlUxCxubf59qKO/lOJa59M2nS9BiuFPcspV\nJr1yD2nl2BXN1JpwYus1GV9BlpXFwD/HnDMTOYl2sjlyQP9linbSJE80ImsfuR85gS9CSymf6fj8\nrSjA4a7QsQXo7xoXBFAFTcvZS+Nnnv7dS/Ln4dfo/ToL6vdRWQ+4Es3KVtfcdtm0CtHdjfw+QOup\nfUgRy8NrwMNEZ6Wdi5aWPpfyWuEcFMso5yV2MdFOlFui7LxRL9RjaA8mZd7DIhSumzZS427aM5Is\ncixHL94fp2ynk7xyliFjkWcEChOezMhEUXcHPw9GTr6dkXpbAtdHXPtZZIFMYjLwVygM2SSTtZ8/\nGvxcAfwe+EHH58tQDpswe6FEflnGkKJjRV1yxlHF+JmXPGNQL8mfh4lI6czrRpGbPrTGdg3lpcyf\nTLlr3p2RHFFbi9uRSXL90LGLkCZY5P4uJbo40xFIwbsytF0TyPRAsB9VvbZlNjyxgExJDJLOnNsr\n95BFjrtoW/7ypD4fqzLOQC+IV2jXNQIldWv1hZkd35mELIJRVVCvQg6O60d8FuZU4E9zyFsGE1Ay\nuV5kmO59LGsfuYrRTs3bor/rHh3Hv017OW8eGt/2QEpr1qzMg2TzZysqZ1HinmvawrVfQP4lWbal\nHdco0r+j5E9b/b2o7Fnep50cSEPOy2eiomVp02unYSXJobxVsD6aEXTOdn9Bu3pqXC6JJD4APJTy\n3Pkkh/a2wtrelFOeNAySP+RwPr1xD1FyTENWgBOC/bjljrqYT30yvg4pFg8y0sK3YyBD5wy39dlL\nREdFLUMe/Md3afMAopO51cFM5Pfww4baT2KY7n0sro9sw8jMwiCF7CnkXxfmePT3mxo6Nhu96Frj\n7Hmhz4YYrSwkMUg2f7Yy5OxG1PMJE/Vcyyxcm0RS/84q/1ip/v51QuNOXT4q70UzxH0oL6nbEWhQ\nu7qk62VhJzR7DLc9D63D34Zkyxsedw1ScuKydoaZ1PEzimXIylPn+nIWeuUeouRo9Y+H0Cwkr5N0\nWdQp4wvIj2wtI82vH0J9OCpd/RLgFuSv1ckQcnI8G/WP8NizEfJ92ZLoZG518AyypiSlBuhVljG6\njyxH1rdLOs7dAfnBDXUcX44ibJ4PHZuPLBM3IL+Q1vWnooi0W4oK3oUy5OxG3PMJs4zRz/UC6sv7\n1K1/55F/AeqHLSvpdahgZi9F385ndKLJytkNOfKUMRvuQ4PhFYxOsV0Fa2jHgIc5Aw3k4eqtm6H7\nvIri5vfv0N2EOwM57D6CnsMLwB3IXNbJncDXCsqTxCDZZxa9cg9JcpyCfI4+Q3OZnJuU8Si0ZHsJ\nMhN/lfikUz9AeRO6sQsymd+FfFkuQ/2sVxxoh5oWIIZhuvexqD6yEI1JD3cc3x85q07uOD5EdA2l\ny9BMfoD2/9feKIdJVgZJP1aUIWeL/ZAl5i/RxBnin0+YbmNPHRYViO/feeSvu/p72mWmMJ+gZsWp\n9SCj1qyj6EP/lFPRLGsblNjnWOCzwM8Yub6Vpix7XpYiB6ZlFbYRx56UYz2YgcyhVTsmDlJdh63r\nHkwxFqEBs/OlMtYYalqAGIaJ72NJfeSMCuRZQz4fgkHqz0w7nbZD+EaMVrDink/Sc61LUUkir/xQ\nbfX3PMtME4nOw1QZc9DAlcahJs92X4Wyz0R/3CGaUVQAvkG+hDeH0I6XPwKtb+YpC56FtShKpSya\nuAdTjCuoxl8sr9NmXoZqaicrw4x8KWbpI2fFHC/CPSgqLCtljxVpmIKe330om+2cjs/DzyfLc+0V\nRSWv/FVXf98O+aW1lplm0D1PGChAoMrAj1F8Fq2fVbVVmdzqVBRKPURzisrGyJQeFaocxyL0j7AK\nKVsPUc1sqkrWhXsYb+yPwqCrYojsTptF2upFhmm/FLP0kV1R6YIy2Zj2ckOZwRFV0YeUlQNRYc/w\niz38fLKOPb2gqOSVv47q73mWma5gdDShiWB/2v40QzSnqICWzrLMJKch/5bzUPjq0VUIVTHrwj2M\nJzZCzoVV5WSaihJXdYtsKIMpaCnjcRT9M6X76bUzTPtlkraPTEQOwmXPmA8Gvoiywq4o+dplMx+l\nkWgtSb6T9oy98/lkHXuaVlTyyr8UKSlzg+1AFCBSNUnLTAtoF18dQd1lzXudTZE/zb8E+0PICWio\nGXGMGffsjZZh9474bDbKo7QURRMtRP4I89HS6VxkBn8WmfzLool2h9Gkabik6xVhaxQyfD968TRe\nOK4L05Bi8kuUVmIqqoHTLYdHEq1Ce+9HzuVpCu31CgvQsl2nJWwm1Ua8HYP6xErin/0nkYIVlfrA\nhDgaFYD722B7BJm092lSKGPGMd2cNo9FodoPovV40MvoRdozxIXAj0qWqYl2h2l+mcGYPKRZZpqE\nQqVNDoZpdunHmPFON6fN6SiC4FehYzsxMtfEkYSKm3UhS/bMMttNyzBWVMzYI+0y0yFE52YC6q/1\nM1bYHJmb56LS4tOAbzYqkTHjj41RpMADRNcVeg44CDlIttgdrdO3OBwVaJuFEtetQnloVnVcqx9l\n/2z5pnSrRZan3aI8QnnJMo2pgwXovRm1zNTJMuAjWRtYixzKjDGmKaKcNlegzKMtLmZkva+baVtB\nZ6GsnJPQhAOkoLTyL4WvtR+wRfD714I2wxRt1xhTMvOo3ss+D7XGVxtjGmVrpBCcSjsj56WoDkiL\n7wGbBL/3Ib+QVoTHFOTcehztbNHX0g4iCF/rJJRaHGRN6cyQXLRdUJbu3ZAfXLeSEcaYMcxA0wIY\nYxonb8X0uUjxibrWerSLKV6PIgDLarfFahTZUGcVbmPGPJ0+KoeiDrsEzQZazEazmm7hzK+g5DIv\n00z4XlOhisaY+uhHETZ52AspIfOA/+641ovBtitKR/BIie2CfFpuRH4mf4fyeRhjMrIY1ZiZADxW\n8FpVhe8NNNCmMaZ3OIz8JemPRMXs5sdcaybw8QraBdUsOwyVxYjKCWOMSUFrvXUHVKm1CGWE781B\nZtKzQtttHfunl9ymMWb8cgKa7EwifSFVY0zFhJd+Hg1+Lgdu6ThvA1RqOu3STxnhe48jp7MwA8Rb\nVZoIGTTGrBscjiZGZ6Blnl2bFccY042rKad+Q56wwY8mXHOg5DaNMcYY08NM6NjvB94G3FHCtbcG\nbgh+70PFy24P9l8I2jgK+FboO0VT6OZp0xhjjDFjhB1RfoAmCOc3iGNlBe06t4ExxhjTo0xAL+cL\nkDPq4bQrB9fJXOAJkitanl1B23siX5YrkX+NMcYYY3qEfuS78UGksDxHM+WqDwF+inKcPFNjuwcF\n7d6PLCqOCDLGGGN6iInAk8DbG5bjVWTRSVr6KZvHgS2RwnZhzW0bY4wxxhhjjDHGGGOMMcYYY4wx\nxhhjjDHGGGOMMcYYE83/AYh0fTKA9a30AAAAAElFTkSuQmCC\n", "prompt_number": 49, "text": [ "\n", " l + 1/2 \n", " _____ \n", " \u2572 \n", " \u2572 2 \u239b 2 2 \u239e j,m_l + m_s \n", " \u2572 \u210f \u22c5\u239d4\u22c5j + 4\u22c5j - 4\u22c5l - 4\u22c5l - 3\u23a0\u22c5C \u22c5\u2758j,m_l + m_s,j\u2081=\n", " \u2572 l,m_l,1/2,m_s \n", " \u2571 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", " \u2571 4 \n", " \u2571 \n", " \u2571 \n", " \u203e\u203e\u203e\u203e\u203e \n", "j = m_l + m_s \n", "\n", " \n", " \n", " \n", " \n", "l,j\u2082=1/2\u27e9\n", " \n", "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", " \n", " \n", " \n", " \n", " " ] } ], "prompt_number": 49 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This gives us the modification of the angular part of the spin-orbit Hamiltonian. We see there is now the new $j$ quantum number in the coupled states, which we see from looking at the equation will have values $l\\pm \\frac{1}{2}$, and $m_j=m_l + m_s$. We still have the $l$ and $s$ quantum numbers." ] } ], "metadata": {} } ] }sympy-0.7.4.1/examples/notebooks/qubits.ipynb0000644000175000017500000000517712253362407021451 0ustar georgeskgeorgesk{ "metadata": { "name": "qubits" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "code", "collapsed": false, "input": [ "from sympy import symbols\n", "from sympy.core.trace import Tr\n", "from sympy.matrices.matrices import Matrix\n", "from IPython.core.display import display_pretty\n", "from sympy.printing.latex import *\n", "from sympy.physics.quantum.cartesian import *\n", "from sympy.physics.quantum.qubit import *\n", "from sympy.physics.quantum.density import *\n", "\n", "%load_ext sympyprinting\n", "\n", "#TODO: Add examples of simple qubit usage " ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 4 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Examples of Tr operations on Qubits" ] }, { "cell_type": "code", "collapsed": false, "input": [ "q1 = Qubit('10110')\n", "q2 = Qubit('01010')\n", "d = Density( [q1, 0.6], [q2, 0.4] )\n", "\n", "# Trace one bit \n", "t = Tr(d,[0])\n", "\n", "display_pretty(t.doit())\n", "\n", "\n", "# Partial trace of 3 qubits\n", "# the 0th bit is the right-most bit\n", "t = Tr(d,[2, 1, 3])\n", "display_pretty(t.doit())" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "display_data", "text": [ "0.4\u22c5(\u27580101\u27e9, 1) + 0.6\u22c5(\u27581011\u27e9, 1)" ] }, { "output_type": "display_data", "text": [ "0.4\u22c5(\u275800\u27e9, 1) + 0.6\u22c5(\u275810\u27e9, 1)" ] } ], "prompt_number": 2 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Partial Tr of mixed state" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from sympy import *\n", "q = (1/sqrt(2)) * (Qubit('00') + Qubit('11'))\n", "\n", "d = Density ( [q, 1.0] )\n", "t = Tr(d, [0])\n", "display_pretty(t.doit())" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "display_data", "text": [ "0.5\u22c5(\u27580\u27e9, 1) + 0.5\u22c5(\u27581\u27e9, 1)" ] } ], "prompt_number": 3 }, { "cell_type": "code", "collapsed": true, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 3 } ], "metadata": {} } ] }sympy-0.7.4.1/examples/notebooks/fidelity.ipynb0000644000175000017500000000703512253362407021746 0ustar georgeskgeorgesk{ "metadata": { "name": "fidelity" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "code", "collapsed": false, "input": [ "from sympy import *\n", "from sympy.physics.quantum import *\n", "from sympy.physics.quantum.density import *\n", "from sympy.physics.quantum.spin import (\n", " Jx, Jy, Jz, Jplus, Jminus, J2,\n", " JxBra, JyBra, JzBra,\n", " JxKet, JyKet, JzKet,\n", ")\n", "from IPython.core.display import display_pretty\n", "from sympy.physics.quantum.operator import *\n", "\n", "%load_ext sympyprinting" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 2 }, { "cell_type": "markdown", "metadata": {}, "source": [ "##Fidelity using some kets" ] }, { "cell_type": "code", "collapsed": false, "input": [ "up = JzKet(S(1)/2,S(1)/2)\n", "down = JzKet(S(1)/2,-S(1)/2)\n", "amp = 1/sqrt(2)\n", "updown = (amp * up ) + (amp * down)\n", "\n", "# represent turns Kets into matrices\n", "up_dm = represent(up * Dagger(up))\n", "down_dm = represent(down * Dagger(down)) \n", "updown_dm = represent(updown * Dagger(updown))\n", "updown2 = (sqrt(3)/2 )* up + (1/2)*down\n", "\n", "\n", "display_pretty(fidelity(up_dm, up_dm))\n", "display_pretty(fidelity(up_dm, down_dm)) #orthogonal states\n", "display_pretty(fidelity(up_dm, updown_dm).evalf())\n", "\n", "\n", "# alternatively, puts Kets into Density object and compute fidelity\n", "d1 = Density( [updown, 0.25], [updown2, 0.75])\n", "d2 = Density( [updown, 0.75], [updown2, 0.25])\n", "display_pretty(fidelity(d1, d2))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "display_data", "text": [ "1" ] }, { "output_type": "display_data", "text": [ "0" ] }, { "output_type": "display_data", "text": [ "0.707106781186548" ] }, { "output_type": "display_data", "text": [ "0.817293551913876" ] } ], "prompt_number": 7 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Fidelity on states as Qubits" ] }, { "cell_type": "code", "collapsed": false, "input": [ "\n", "from sympy.physics.quantum.qubit import Qubit\n", "state1 = Qubit('0')\n", "state2 = Qubit('1')\n", "state3 = (1/sqrt(2))*state1 + (1/sqrt(2))*state2\n", "state4 = (sqrt(S(2)/3))*state1 + (1/sqrt(3))*state2\n", "\n", "state1_dm = Density([state1, 1])\n", "state2_dm = Density([state2, 1])\n", "state3_dm = Density([state3, 1])\n", "\n", "# mixed qubit states in density\n", "d1 = Density([state3, 0.70], [state4, 0.30])\n", "d2 = Density([state3, 0.20], [state4, 0.80])\n", "\n", "\n", "display_pretty(fidelity(d1, d2))\n", "\n" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "display_data", "text": [ "0.996370452558227" ] } ], "prompt_number": 9 }, { "cell_type": "code", "collapsed": true, "input": [], "language": "python", "metadata": {}, "outputs": [] } ], "metadata": {} } ] }sympy-0.7.4.1/examples/notebooks/sho1d_example.ipynb0000644000175000017500000006302112253362407022663 0ustar georgeskgeorgesk{ "metadata": { "name": "sho1d_example" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Example Notebook for sho1d.py" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Import the sho1d.py file as well as the test_sho1d.py file" ] }, { "cell_type": "code", "collapsed": false, "input": [ "%load_ext sympy.interactive.ipythonprinting \n", "from sympy import *\n", "from IPython.display import display_pretty\n", "from sympy.physics.quantum import *\n", "from sympy.physics.quantum.sho1d import *\n", "from sympy.physics.quantum.tests.test_sho1d import *" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "The sympy.interactive.ipythonprinting extension is already loaded. To reload it, use:\n", " %reload_ext sympy.interactive.ipythonprinting\n" ] } ], "prompt_number": 1 }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Printing Of Operators" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Create a raising and lowering operator and make sure they print correctly" ] }, { "cell_type": "code", "collapsed": false, "input": [ "ad = RaisingOp('a')\n", "a = LoweringOp('a')" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 2 }, { "cell_type": "code", "collapsed": false, "input": [ "ad" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 3, "text": [ "RaisingOp(a)" ] } ], "prompt_number": 3 }, { "cell_type": "code", "collapsed": false, "input": [ "a" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 4, "text": [ "a" ] } ], "prompt_number": 4 }, { "cell_type": "code", "collapsed": false, "input": [ "print latex(ad)\n", "print latex(a)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "a^{\\dag}\n", "a\n" ] } ], "prompt_number": 5 }, { "cell_type": "code", "collapsed": false, "input": [ "display_pretty(ad)\n", "display_pretty(a)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "display_data", "text": [ "RaisingOp(a)" ] }, { "output_type": "display_data", "text": [ "a" ] } ], "prompt_number": 6 }, { "cell_type": "code", "collapsed": false, "input": [ "print srepr(ad)\n", "print srepr(a)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "RaisingOp(Symbol('a'))\n", "LoweringOp(Symbol('a'))\n" ] } ], "prompt_number": 7 }, { "cell_type": "code", "collapsed": false, "input": [ "print repr(ad)\n", "print repr(a)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "RaisingOp(a)\n", "a\n" ] } ], "prompt_number": 8 }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Printing of States" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Create a simple harmonic state and check its printing" ] }, { "cell_type": "code", "collapsed": false, "input": [ "k = SHOKet('k')\n", "b = SHOBra('b')" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 9 }, { "cell_type": "code", "collapsed": false, "input": [ "k" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 10, "text": [ "|k>" ] } ], "prompt_number": 10 }, { "cell_type": "code", "collapsed": false, "input": [ "b" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 11, "text": [ "" ] } ], "prompt_number": 20 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Taking the InnerProduct of the bra and ket will return the KroneckerDelta function" ] }, { "cell_type": "code", "collapsed": false, "input": [ "InnerProduct(b,k).doit()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 21, "text": [ "KroneckerDelta(k, b)" ] } ], "prompt_number": 21 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Take a look at how the raising and lowering operators act on states. We use qapply to apply an operator to a state" ] }, { "cell_type": "code", "collapsed": false, "input": [ "qapply(ad*k)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 22, "text": [ "sqrt(k + 1)*|k + 1>" ] } ], "prompt_number": 22 }, { "cell_type": "code", "collapsed": false, "input": [ "qapply(a*k)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 23, "text": [ "sqrt(k)*|k - 1>" ] } ], "prompt_number": 23 }, { "cell_type": "markdown", "metadata": {}, "source": [ "But the states may have an explicit energy level. Let's look at the ground and first excited states" ] }, { "cell_type": "code", "collapsed": false, "input": [ "kg = SHOKet(0)\n", "kf = SHOKet(1)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 24 }, { "cell_type": "code", "collapsed": false, "input": [ "qapply(ad*kg)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 25, "text": [ "|1>" ] } ], "prompt_number": 25 }, { "cell_type": "code", "collapsed": false, "input": [ "qapply(ad*kf)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 26, "text": [ "sqrt(2)*|2>" ] } ], "prompt_number": 26 }, { "cell_type": "code", "collapsed": false, "input": [ "qapply(a*kg)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 27, "text": [ "0" ] } ], "prompt_number": 27 }, { "cell_type": "code", "collapsed": false, "input": [ "qapply(a*kf)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 28, "text": [ "|0>" ] } ], "prompt_number": 28 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that a*kg is 0 and a*kf is the |0> the ground state." ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "NumberOp & Hamiltonian" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's look at the Number Operator and Hamiltonian Operator" ] }, { "cell_type": "code", "collapsed": false, "input": [ "k = SHOKet('k')\n", "ad = RaisingOp('a')\n", "a = LoweringOp('a')\n", "N = NumberOp('N')\n", "H = Hamiltonian('H')" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 29 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The number operator is simply expressed as ad*a" ] }, { "cell_type": "code", "collapsed": false, "input": [ "N().rewrite('a').doit()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 30, "text": [ "RaisingOp(a)*a" ] } ], "prompt_number": 30 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The number operator expressed in terms of the position and momentum operators" ] }, { "cell_type": "code", "collapsed": false, "input": [ "N().rewrite('xp').doit()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 31, "text": [ "-1/2 + (m**2*omega**2*X**2 + Px**2)/(2*hbar*m*omega)" ] } ], "prompt_number": 31 }, { "cell_type": "markdown", "metadata": {}, "source": [ "It can also be expressed in terms of the Hamiltonian operator" ] }, { "cell_type": "code", "collapsed": false, "input": [ "N().rewrite('H').doit()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 32, "text": [ "-1/2 + H/(hbar*omega)" ] } ], "prompt_number": 32 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Hamiltonian operator can be expressed in terms of the raising and lowering operators, position and momentum operators, and the number operator" ] }, { "cell_type": "code", "collapsed": false, "input": [ "H().rewrite('a').doit()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 33, "text": [ "hbar*omega*(1/2 + RaisingOp(a)*a)" ] } ], "prompt_number": 33 }, { "cell_type": "code", "collapsed": false, "input": [ "H().rewrite('xp').doit()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 34, "text": [ "(m**2*omega**2*X**2 + Px**2)/(2*m)" ] } ], "prompt_number": 34 }, { "cell_type": "code", "collapsed": false, "input": [ "H().rewrite('N').doit()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 35, "text": [ "hbar*omega*(1/2 + N)" ] } ], "prompt_number": 35 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The raising and lowering operators can also be expressed in terms of the position and momentum operators" ] }, { "cell_type": "code", "collapsed": false, "input": [ "ad().rewrite('xp').doit()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 36, "text": [ "sqrt(2)*(m*omega*X - I*Px)/(2*sqrt(hbar)*sqrt(m*omega))" ] } ], "prompt_number": 36 }, { "cell_type": "code", "collapsed": false, "input": [ "a().rewrite('xp').doit()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 37, "text": [ "sqrt(2)*(m*omega*X + I*Px)/(2*sqrt(hbar)*sqrt(m*omega))" ] } ], "prompt_number": 37 }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Properties" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's take a look at how the NumberOp and Hamiltonian act on states" ] }, { "cell_type": "code", "collapsed": false, "input": [ "qapply(N*k)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 38, "text": [ "k*|k>" ] } ], "prompt_number": 38 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Apply the Number operator to a state returns the state times the ket" ] }, { "cell_type": "code", "collapsed": false, "input": [ "ks = SHOKet(2)\n", "qapply(N*ks)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 39, "text": [ "2*|2>" ] } ], "prompt_number": 39 }, { "cell_type": "code", "collapsed": false, "input": [ "qapply(H*k)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 40, "text": [ "hbar*k*omega*|k> + hbar*omega*|k>/2" ] } ], "prompt_number": 40 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's see how the operators commute with each other" ] }, { "cell_type": "code", "collapsed": false, "input": [ "Commutator(N,ad).doit()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 41, "text": [ "RaisingOp(a)" ] } ], "prompt_number": 41 }, { "cell_type": "code", "collapsed": false, "input": [ "Commutator(N,a).doit()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 42, "text": [ "-a" ] } ], "prompt_number": 42 }, { "cell_type": "code", "collapsed": false, "input": [ "Commutator(N,H).doit()" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 43, "text": [ "0" ] } ], "prompt_number": 43 }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Representation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can express the operators in NumberOp basis. There are different ways to create a matrix in Python, we will use 3 different ways." ] }, { "cell_type": "heading", "level": 4, "metadata": {}, "source": [ "Sympy" ] }, { "cell_type": "code", "collapsed": false, "input": [ "represent(ad, basis=N, ndim=4, format='sympy')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 44, "text": [ "[0, 0, 0, 0]\n", "[1, 0, 0, 0]\n", "[0, sqrt(2), 0, 0]\n", "[0, 0, sqrt(3), 0]" ] } ], "prompt_number": 44 }, { "cell_type": "heading", "level": 4, "metadata": {}, "source": [ "Numpy" ] }, { "cell_type": "code", "collapsed": false, "input": [ "represent(ad, basis=N, ndim=5, format='numpy')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 45, "text": [ "array([[ 0. , 0. , 0. , 0. , 0. ],\n", " [ 1. , 0. , 0. , 0. , 0. ],\n", " [ 0. , 1.41421356, 0. , 0. , 0. ],\n", " [ 0. , 0. , 1.73205081, 0. , 0. ],\n", " [ 0. , 0. , 0. , 2. , 0. ]])" ] } ], "prompt_number": 45 }, { "cell_type": "heading", "level": 4, "metadata": {}, "source": [ "Scipy.Sparse" ] }, { "cell_type": "code", "collapsed": false, "input": [ "represent(ad, basis=N, ndim=4, format='scipy.sparse', spmatrix='lil')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 46, "text": [ "<4x4 sparse matrix of type ''\n", "\twith 3 stored elements in Compressed Sparse Row format>" ] } ], "prompt_number": 46 }, { "cell_type": "code", "collapsed": false, "input": [ "print represent(ad, basis=N, ndim=4, format='scipy.sparse', spmatrix='lil')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ " (1, 0)\t1.0\n", " (2, 1)\t1.41421356237\n", " (3, 2)\t1.73205080757\n" ] } ], "prompt_number": 47 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The same can be done for the other operators" ] }, { "cell_type": "code", "collapsed": false, "input": [ "represent(a, basis=N, ndim=4, format='sympy')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 48, "text": [ "[0, 1, 0, 0]\n", "[0, 0, sqrt(2), 0]\n", "[0, 0, 0, sqrt(3)]\n", "[0, 0, 0, 0]" ] } ], "prompt_number": 48 }, { "cell_type": "code", "collapsed": false, "input": [ "represent(N, basis=N, ndim=4, format='sympy')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 49, "text": [ "[0, 0, 0, 0]\n", "[0, 1, 0, 0]\n", "[0, 0, 2, 0]\n", "[0, 0, 0, 3]" ] } ], "prompt_number": 49 }, { "cell_type": "code", "collapsed": false, "input": [ "represent(H, basis=N, ndim=4, format='sympy')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "pyout", "prompt_number": 50, "text": [ "[hbar*omega/2, 0, 0, 0]\n", "[ 0, 3*hbar*omega/2, 0, 0]\n", "[ 0, 0, 5*hbar*omega/2, 0]\n", "[ 0, 0, 0, 7*hbar*omega/2]" ] } ], "prompt_number": 50 }, { "cell_type": "heading", "level": 4, "metadata": {}, "source": [ "Ket and Bra Representation" ] }, { "cell_type": "code", "collapsed": false, "input": [ "k0 = SHOKet(0)\n", "k1 = SHOKet(1)\n", "b0 = SHOBra(0)\n", "b1 = SHOBra(1)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 51 }, { "cell_type": "code", "collapsed": false, "input": [ "print represent(k0, basis=N, ndim=5, format='sympy')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "[1]\n", "[0]\n", "[0]\n", "[0]\n", "[0]\n" ] } ], "prompt_number": 52 }, { "cell_type": "code", "collapsed": false, "input": [ "print represent(k1, basis=N, ndim=5, format='sympy')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "[0]\n", "[1]\n", "[0]\n", "[0]\n", "[0]\n" ] } ], "prompt_number": 53 }, { "cell_type": "code", "collapsed": false, "input": [ "print represent(b0, basis=N, ndim=5, format='sympy')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "[1, 0, 0, 0, 0]\n" ] } ], "prompt_number": 54 }, { "cell_type": "code", "collapsed": false, "input": [ "print represent(b1, basis=N, ndim=5, format='sympy')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "[0, 1, 0, 0, 0]\n" ] } ], "prompt_number": 55 }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [] } ], "metadata": {} } ] }sympy-0.7.4.1/examples/galgebra/0000755000175000017500000000000012253362407016626 5ustar georgeskgeorgesksympy-0.7.4.1/examples/galgebra/spherical_latex.py0000644000175000017500000000172712253362407022356 0ustar georgeskgeorgesk#!/usr/bin/env python from __future__ import print_function from sympy import symbols, sin, cos from sympy.galgebra import MV, Format from sympy.galgebra import xdvi, Get_Program, Print_Function def derivatives_in_spherical_coordinates(): Print_Function() X = (r, th, phi) = symbols('r theta phi') curv = [[r*cos(phi)*sin(th), r*sin(phi)*sin(th), r*cos(th)], [1, r, r*sin(th)]] (er, eth, ephi, grad) = MV.setup('e_r e_theta e_phi', metric='[1,1,1]', coords=X, curv=curv) f = MV('f', 'scalar', fct=True) A = MV('A', 'vector', fct=True) B = MV('B', 'grade2', fct=True) print('f =', f) print('A =', A) print('B =', B) print('grad*f =', grad*f) print('grad|A =', grad | A) print('-I*(grad^A) =', -MV.I*(grad ^ A)) print('grad^B =', grad ^ B) return def dummy(): return def main(): Get_Program() Format() derivatives_in_spherical_coordinates() xdvi() return if __name__ == "__main__": main() sympy-0.7.4.1/examples/galgebra/manifold_check.py0000755000175000017500000000656312253362407022143 0ustar georgeskgeorgesk#!/usr/bin/env python from __future__ import print_function from sympy import symbols, log, simplify, diff, cos, sin from sympy.galgebra.ga import MV, ReciprocalFrame from sympy.galgebra.debug import oprint from sympy.galgebra.printing import GA_Printer, enhance_print, Get_Program, Print_Function from sympy.galgebra.manifold import Manifold def Test_Reciprocal_Frame(): Print_Function() coords = symbols('x y z') (ex, ey, ez, grad) = MV.setup('ex ey ez', metric='[1,1,1]', coords=coords) mfvar = (u, v) = symbols('u v') eu = ex + ey ev = ex - ey (eu_r, ev_r) = ReciprocalFrame([eu, ev]) oprint('Frame', (eu, ev), 'Reciprocal Frame', (eu_r, ev_r)) print('eu.eu_r =', eu | eu_r) print('eu.ev_r =', eu | ev_r) print('ev.eu_r =', ev | eu_r) print('ev.ev_r =', ev | ev_r) eu = ex + ey + ez ev = ex - ey (eu_r, ev_r) = ReciprocalFrame([eu, ev]) oprint('Frame', (eu, ev), 'Reciprocal Frame', (eu_r, ev_r)) print('eu.eu_r =', eu | eu_r) print('eu.ev_r =', eu | ev_r) print('ev.eu_r =', ev | eu_r) print('ev.ev_r =', ev | ev_r) return def Plot_Mobius_Strip_Manifold(): Print_Function() coords = symbols('x y z') (ex, ey, ez, grad) = MV.setup('ex ey ez', metric='[1,1,1]', coords=coords) mfvar = (u, v) = symbols('u v') X = (cos(u) + v*cos(u/2)*cos(u))*ex + (sin(u) + v*cos(u/2)*sin(u))*ey + v*sin(u/2)*ez MF = Manifold(X, mfvar, True, I=MV.I) MF.Plot2DSurface([0.0, 6.28, 48], [-0.3, 0.3, 12], surf=False, skip=[4, 4], tan=0.15) return def Distorted_manifold_with_scalar_function(): Print_Function() coords = symbols('x y z') (ex, ey, ez, grad) = MV.setup('ex ey ez', metric='[1,1,1]', coords=coords) mfvar = (u, v) = symbols('u v') X = 2*u*ex + 2*v*ey + (u**3 + v**3/2)*ez MF = Manifold(X, mfvar, I=MV.I) (eu, ev) = MF.Basis() g = (v + 1)*log(u) dg = MF.Grad(g) print('g =', g) print('dg =', dg) print('dg(1,0) =', dg.subs({u: 1, v: 0})) G = u*eu + v*ev dG = MF.Grad(G) print('G =', G) print('P(G) =', MF.Proj(G)) print('zcoef =', simplify(2*(u**2 + v**2)*(-4*u**2 - 4*v**2 - 1))) print('dG =', dG) print('P(dG) =', MF.Proj(dG)) PS = u*v*eu ^ ev print('PS =', PS) print('dPS =', MF.Grad(PS)) print('P(dPS) =', MF.Proj(MF.Grad(PS))) return def Simple_manifold_with_scalar_function_derivative(): Print_Function() coords = (x, y, z) = symbols('x y z') basis = (e1, e2, e3, grad) = MV.setup('e_1 e_2 e_3', metric='[1,1,1]', coords=coords) # Define surface mfvar = (u, v) = symbols('u v') X = u*e1 + v*e2 + (u**2 + v**2)*e3 print(X) MF = Manifold(X, mfvar) # Define field on the surface. g = (v + 1)*log(u) # Method 1: Using old Manifold routines. VectorDerivative = (MF.rbasis[0]/MF.E_sq)*diff(g, u) + (MF.rbasis[1]/MF.E_sq)*diff(g, v) print('Vector derivative =', VectorDerivative.subs({u: 1, v: 0})) # Method 2: Using new Manifold routines. dg = MF.Grad(g) print('Vector derivative =', dg.subs({u: 1, v: 0})) return def dummy(): return def main(): Get_Program(True) with GA_Printer(): enhance_print() Test_Reciprocal_Frame() Distorted_manifold_with_scalar_function() Simple_manifold_with_scalar_function_derivative() #Plot_Mobius_Strip_Manifold() return if __name__ == "__main__": main() sympy-0.7.4.1/examples/galgebra/exp_check.py0000644000175000017500000000101212253362407021123 0ustar georgeskgeorgesk#!/usr/bin/env python from __future__ import print_function from sympy import symbols, sin, cos from sympy.galgebra import MV from sympy.galgebra import enhance_print def main(): enhance_print() (ex, ey, ez) = MV.setup('e*x|y|z', metric='[1,1,1]') u = MV('u', 'vector') v = MV('v', 'vector') w = MV('w', 'vector') print(u) print(v) uv = u ^ v print(uv) print(uv.is_blade()) exp_uv = uv.exp() exp_uv.Fmt(2, 'exp(uv)') return if __name__ == "__main__": main() sympy-0.7.4.1/examples/galgebra/latex_check.py0000755000175000017500000002642512253362407021466 0ustar georgeskgeorgesk#!/usr/bin/env python from __future__ import print_function from sympy import Symbol, symbols, sin, cos, Rational, expand, simplify, collect, S from sympy.galgebra import xdvi, Get_Program, Print_Function from sympy.galgebra import MV, Format, Com, Nga def F(x): global n, nbar Fx = Rational(1, 2)*((x*x)*n + 2*x - nbar) return(Fx) def make_vector(a, n=3): if isinstance(a, str): sym_str = '' for i in range(n): sym_str += a + str(i + 1) + ' ' sym_lst = list(symbols(sym_str)) sym_lst.append(S.Zero) sym_lst.append(S.Zero) a = MV(sym_lst, 'vector') return(F(a)) def basic_multivector_operations_3D(): Print_Function() (ex, ey, ez) = MV.setup('e*x|y|z') A = MV('A', 'mv') A.Fmt(1, 'A') A.Fmt(2, 'A') A.Fmt(3, 'A') A.even().Fmt(1, '%A_{+}') A.odd().Fmt(1, '%A_{-}') X = MV('X', 'vector') Y = MV('Y', 'vector') print('g_{ij} = ', MV.metric) X.Fmt(1, 'X') Y.Fmt(1, 'Y') (X*Y).Fmt(2, 'X*Y') (X ^ Y).Fmt(2, 'X^Y') (X | Y).Fmt(2, 'X|Y') return def basic_multivector_operations_2D(): Print_Function() (ex, ey) = MV.setup('e*x|y') print('g_{ij} =', MV.metric) X = MV('X', 'vector') A = MV('A', 'spinor') X.Fmt(1, 'X') A.Fmt(1, 'A') (X | A).Fmt(2, 'X|A') (X < A).Fmt(2, 'X X).Fmt(2, 'A>X') return def basic_multivector_operations_2D_orthogonal(): Print_Function() (ex, ey) = MV.setup('e*x|y', metric='[1,1]') print('g_{ii} =', MV.metric) X = MV('X', 'vector') A = MV('A', 'spinor') X.Fmt(1, 'X') A.Fmt(1, 'A') (X*A).Fmt(2, 'X*A') (X | A).Fmt(2, 'X|A') (X < A).Fmt(2, 'X A).Fmt(2, 'X>A') (A*X).Fmt(2, 'A*X') (A | X).Fmt(2, 'A|X') (A < X).Fmt(2, 'A X).Fmt(2, 'A>X') return def check_generalized_BAC_CAB_formulas(): Print_Function() (a, b, c, d) = MV.setup('a b c d') print('g_{ij} =', MV.metric) print('\\bm{a|(b*c)} =', a | (b*c)) print('\\bm{a|(b^c)} =', a | (b ^ c)) print('\\bm{a|(b^c^d)} =', a | (b ^ c ^ d)) print('\\bm{a|(b^c)+c|(a^b)+b|(c^a)} =', (a | (b ^ c)) + (c | (a ^ b)) + (b | (c ^ a))) print('\\bm{a*(b^c)-b*(a^c)+c*(a^b)} =', a*(b ^ c) - b*(a ^ c) + c*(a ^ b)) print('\\bm{a*(b^c^d)-b*(a^c^d)+c*(a^b^d)-d*(a^b^c)} =', a*(b ^ c ^ d) - b*(a ^ c ^ d) + c*(a ^ b ^ d) - d*(a ^ b ^ c)) print('\\bm{(a^b)|(c^d)} =', (a ^ b) | (c ^ d)) print('\\bm{((a^b)|c)|d} =', ((a ^ b) | c) | d) print('\\bm{(a^b)\\times (c^d)} =', Com(a ^ b, c ^ d)) return def derivatives_in_rectangular_coordinates(): Print_Function() X = (x, y, z) = symbols('x y z') (ex, ey, ez, grad) = MV.setup('e_x e_y e_z', metric='[1,1,1]', coords=X) f = MV('f', 'scalar', fct=True) A = MV('A', 'vector', fct=True) B = MV('B', 'grade2', fct=True) C = MV('C', 'mv') print('f =', f) print('A =', A) print('B =', B) print('C =', C) print('grad*f =', grad*f) print('grad|A =', grad | A) print('grad*A =', grad*A) print('-I*(grad^A) =', -MV.I*(grad ^ A)) print('grad*B =', grad*B) print('grad^B =', grad ^ B) print('grad|B =', grad | B) return def derivatives_in_spherical_coordinates(): Print_Function() X = (r, th, phi) = symbols('r theta phi') curv = [[r*cos(phi)*sin(th), r*sin(phi)*sin(th), r*cos(th)], [1, r, r*sin(th)]] (er, eth, ephi, grad) = MV.setup('e_r e_theta e_phi', metric='[1,1,1]', coords=X, curv=curv) f = MV('f', 'scalar', fct=True) A = MV('A', 'vector', fct=True) B = MV('B', 'grade2', fct=True) print('f =', f) print('A =', A) print('B =', B) print('grad*f =', grad*f) print('grad|A =', grad | A) print('-I*(grad^A) =', (-MV.I*(grad ^ A)).simplify()) print('grad^B =', grad ^ B) def rounding_numerical_components(): Print_Function() (ex, ey, ez) = MV.setup('e_x e_y e_z', metric='[1,1,1]') X = 1.2*ex + 2.34*ey + 0.555*ez Y = 0.333*ex + 4*ey + 5.3*ez print('X =', X) print('Nga(X,2) =', Nga(X, 2)) print('X*Y =', X*Y) print('Nga(X*Y,2) =', Nga(X*Y, 2)) return def noneuclidian_distance_calculation(): Print_Function() from sympy import solve, sqrt metric = '0 # #,# 0 #,# # 1' (X, Y, e) = MV.setup('X Y e', metric) print('g_{ij} =', MV.metric) print('%(X\\W Y)^{2} =', (X ^ Y)*(X ^ Y)) L = X ^ Y ^ e B = L*e # D&L 10.152 Bsq = (B*B).scalar() print('#%L = X\\W Y\\W e \\text{ is a non-euclidian line}') print('B = L*e =', B) BeBr = B*e*B.rev() print('%BeB^{\\dagger} =', BeBr) print('%B^{2} =', B*B) print('%L^{2} =', L*L) # D&L 10.153 (s, c, Binv, M, BigS, BigC, alpha, XdotY, Xdote, Ydote) = symbols('s c (1/B) M S C alpha (X.Y) (X.e) (Y.e)') Bhat = Binv*B # D&L 10.154 R = c + s*Bhat # Rotor R = exp(alpha*Bhat/2) print('#%s = \\f{\\sinh}{\\alpha/2} \\text{ and } c = \\f{\\cosh}{\\alpha/2}') print('%e^{\\alpha B/{2\\abs{B}}} =', R) Z = R*X*R.rev() # D&L 10.155 Z.obj = expand(Z.obj) Z.obj = Z.obj.collect([Binv, s, c, XdotY]) Z.Fmt(3, '%RXR^{\\dagger}') W = Z | Y # Extract scalar part of multivector # From this point forward all calculations are with sympy scalars #print '#Objective is to determine value of C = cosh(alpha) such that W = 0' W = W.scalar() print('%W = Z\\cdot Y =', W) W = expand(W) W = simplify(W) W = W.collect([s*Binv]) M = 1/Bsq W = W.subs(Binv**2, M) W = simplify(W) Bmag = sqrt(XdotY**2 - 2*XdotY*Xdote*Ydote) W = W.collect([Binv*c*s, XdotY]) #Double angle substitutions W = W.subs(2*XdotY**2 - 4*XdotY*Xdote*Ydote, 2/(Binv**2)) W = W.subs(2*c*s, BigS) W = W.subs(c**2, (BigC + 1)/2) W = W.subs(s**2, (BigC - 1)/2) W = simplify(W) W = W.subs(1/Binv, Bmag) W = expand(W) print('#%S = \\f{\\sinh}{\\alpha} \\text{ and } C = \\f{\\cosh}{\\alpha}') print('W =', W) Wd = collect(W, [BigC, BigS], exact=True, evaluate=False) Wd_1 = Wd[S.One] Wd_C = Wd[BigC] Wd_S = Wd[BigS] print('%\\text{Scalar Coefficient} =', Wd_1) print('%\\text{Cosh Coefficient} =', Wd_C) print('%\\text{Sinh Coefficient} =', Wd_S) print('%\\abs{B} =', Bmag) Wd_1 = Wd_1.subs(Bmag, 1/Binv) Wd_C = Wd_C.subs(Bmag, 1/Binv) Wd_S = Wd_S.subs(Bmag, 1/Binv) lhs = Wd_1 + Wd_C*BigC rhs = -Wd_S*BigS lhs = lhs**2 rhs = rhs**2 W = expand(lhs - rhs) W = expand(W.subs(1/Binv**2, Bmag**2)) W = expand(W.subs(BigS**2, BigC**2 - 1)) W = W.collect([BigC, BigC**2], evaluate=False) a = simplify(W[BigC**2]) b = simplify(W[BigC]) c = simplify(W[S.One]) print('#%\\text{Require } aC^{2}+bC+c = 0') print('a =', a) print('b =', b) print('c =', c) x = Symbol('x') C = solve(a*x**2 + b*x + c, x)[0] print('%b^{2}-4ac =', simplify(b**2 - 4*a*c)) print('%\\f{\\cosh}{\\alpha} = C = -b/(2a) =', expand(simplify(expand(C)))) return def conformal_representations_of_circles_lines_spheres_and_planes(): Print_Function() global n, nbar metric = '1 0 0 0 0,0 1 0 0 0,0 0 1 0 0,0 0 0 0 2,0 0 0 2 0' (e1, e2, e3, n, nbar) = MV.setup('e_1 e_2 e_3 n \\bar{n}', metric) print('g_{ij} =', MV.metric) e = n + nbar #conformal representation of points A = make_vector(e1) # point a = (1,0,0) A = F(a) B = make_vector(e2) # point b = (0,1,0) B = F(b) C = make_vector(-e1) # point c = (-1,0,0) C = F(c) D = make_vector(e3) # point d = (0,0,1) D = F(d) X = make_vector('x', 3) print('F(a) =', A) print('F(b) =', B) print('F(c) =', C) print('F(d) =', D) print('F(x) =', X) print('#a = e1, b = e2, c = -e1, and d = e3') print('#A = F(a) = 1/2*(a*a*n+2*a-nbar), etc.') print('#Circle through a, b, and c') print('Circle: A^B^C^X = 0 =', (A ^ B ^ C ^ X)) print('#Line through a and b') print('Line : A^B^n^X = 0 =', (A ^ B ^ n ^ X)) print('#Sphere through a, b, c, and d') print('Sphere: A^B^C^D^X = 0 =', (((A ^ B) ^ C) ^ D) ^ X) print('#Plane through a, b, and d') print('Plane : A^B^n^D^X = 0 =', (A ^ B ^ n ^ D ^ X)) L = (A ^ B ^ e) ^ X L.Fmt(3, 'Hyperbolic\\;\\; Circle: (A^B^e)^X = 0') return def properties_of_geometric_objects(): Print_Function() metric = '# # # 0 0,' + \ '# # # 0 0,' + \ '# # # 0 0,' + \ '0 0 0 0 2,' + \ '0 0 0 2 0' (p1, p2, p3, n, nbar) = MV.setup('p1 p2 p3 n \\bar{n}', metric) print('g_{ij} =', MV.metric) P1 = F(p1) P2 = F(p2) P3 = F(p3) print('#%\\text{Extracting direction of line from }L = P1\\W P2\\W n') L = P1 ^ P2 ^ n delta = (L | n) | nbar print('(L|n)|\\bar{n} =', delta) print('#%\\text{Extracting plane of circle from }C = P1\\W P2\\W P3') C = P1 ^ P2 ^ P3 delta = ((C ^ n) | n) | nbar print('((C^n)|n)|\\bar{n}=', delta) print('(p2-p1)^(p3-p1)=', (p2 - p1) ^ (p3 - p1)) return def extracting_vectors_from_conformal_2_blade(): Print_Function() print(r'B = P1\W P2') metric = ' 0 -1 #,' + \ '-1 0 #,' + \ ' # # #,' (P1, P2, a) = MV.setup('P1 P2 a', metric) print('g_{ij} =', MV.metric) B = P1 ^ P2 Bsq = B*B print('%B^{2} =', Bsq) ap = a - (a ^ B)*B print("a' = a-(a^B)*B =", ap) Ap = ap + ap*B Am = ap - ap*B print("A+ = a'+a'*B =", Ap) print("A- = a'-a'*B =", Am) print('%(A+)^{2} =', Ap*Ap) print('%(A-)^{2} =', Am*Am) aB = a | B print('a|B =', aB) return def reciprocal_frame_test(): Print_Function() metric = '1 # #,' + \ '# 1 #,' + \ '# # 1,' (e1, e2, e3) = MV.setup('e1 e2 e3', metric) print('g_{ij} =', MV.metric) E = e1 ^ e2 ^ e3 Esq = (E*E).scalar() print('E =', E) print('%E^{2} =', Esq) Esq_inv = 1/Esq E1 = (e2 ^ e3)*E E2 = (-1)*(e1 ^ e3)*E E3 = (e1 ^ e2)*E print('E1 = (e2^e3)*E =', E1) print('E2 =-(e1^e3)*E =', E2) print('E3 = (e1^e2)*E =', E3) w = (E1 | e2) w = w.expand() print('E1|e2 =', w) w = (E1 | e3) w = w.expand() print('E1|e3 =', w) w = (E2 | e1) w = w.expand() print('E2|e1 =', w) w = (E2 | e3) w = w.expand() print('E2|e3 =', w) w = (E3 | e1) w = w.expand() print('E3|e1 =', w) w = (E3 | e2) w = w.expand() print('E3|e2 =', w) w = (E1 | e1) w = (w.expand()).scalar() Esq = expand(Esq) print('%(E1\\cdot e1)/E^{2} =', simplify(w/Esq)) w = (E2 | e2) w = (w.expand()).scalar() print('%(E2\\cdot e2)/E^{2} =', simplify(w/Esq)) w = (E3 | e3) w = (w.expand()).scalar() print('%(E3\\cdot e3)/E^{2} =', simplify(w/Esq)) return def dummy(): return def main(): Get_Program() Format() basic_multivector_operations_3D() basic_multivector_operations_2D() basic_multivector_operations_2D_orthogonal() check_generalized_BAC_CAB_formulas() rounding_numerical_components() derivatives_in_rectangular_coordinates() derivatives_in_spherical_coordinates() noneuclidian_distance_calculation() conformal_representations_of_circles_lines_spheres_and_planes() properties_of_geometric_objects() extracting_vectors_from_conformal_2_blade() reciprocal_frame_test() xdvi() return if __name__ == "__main__": main() sympy-0.7.4.1/examples/galgebra/simple_check_latex.py0000755000175000017500000000220412253362407023024 0ustar georgeskgeorgesk#!/usr/bin/env python from __future__ import print_function from sympy.galgebra import xdvi, Get_Program, Print_Function from sympy.galgebra import MV, Format def basic_multivector_operations_3D(): Print_Function() (ex, ey, ez) = MV.setup('e*x|y|z') print('g_{ij} =', MV.metric) A = MV('A', 'mv') A.Fmt(1, 'A') A.Fmt(2, 'A') A.Fmt(3, 'A') A.even().Fmt(1, '%A_{+}') A.odd().Fmt(1, '%A_{-}') X = MV('X', 'vector') Y = MV('Y', 'vector') X.Fmt(1, 'X') Y.Fmt(1, 'Y') (X*Y).Fmt(2, 'X*Y') (X ^ Y).Fmt(2, 'X^Y') (X | Y).Fmt(2, 'X|Y') return def basic_multivector_operations_2D(): Print_Function() (ex, ey) = MV.setup('e*x|y') print('g_{ij} =', MV.metric) X = MV('X', 'vector') A = MV('A', 'spinor') X.Fmt(1, 'X') A.Fmt(1, 'A') (X | A).Fmt(2, 'X|A') (X < A).Fmt(2, 'X X).Fmt(2, 'A>X') return def dummy(): return def main(): Get_Program(True) Format() basic_multivector_operations_3D() basic_multivector_operations_2D() xdvi('simple_test_latex.tex') return if __name__ == "__main__": main() sympy-0.7.4.1/examples/galgebra/mv_setup_options.py0000644000175000017500000000145012253362407022615 0ustar georgeskgeorgesk#!/usr/bin/env python from __future__ import print_function from sympy import symbols from sympy.galgebra import MV from sympy.galgebra import enhance_print, Get_Program, Print_Function def MV_setup_options(): Print_Function() (e1, e2, e3) = MV.setup('e_1 e_2 e_3', '[1,1,1]') v = MV('v', 'vector') print(v) (e1, e2, e3) = MV.setup('e*1|2|3', '[1,1,1]') v = MV('v', 'vector') print(v) (e1, e2, e3) = MV.setup('e*x|y|z', '[1,1,1]') v = MV('v', 'vector') print(v) coords = symbols('x y z') (e1, e2, e3, grad) = MV.setup('e', '[1,1,1]', coords=coords) v = MV('v', 'vector') print(v) return def dummy(): return def main(): Get_Program(True) enhance_print() MV_setup_options() return if __name__ == "__main__": main() sympy-0.7.4.1/examples/galgebra/products_latex.py0000755000175000017500000000513212253362407022244 0ustar georgeskgeorgesk#!/usr/bin/env python from __future__ import print_function from sympy import symbols from sympy.galgebra import MV, Format from sympy.galgebra import xdvi def main(): Format() coords = (x, y, z) = symbols('x y z') (ex, ey, ez, grad) = MV.setup('e*x|y|z', '[1,1,1]', coords=coords) s = MV('s', 'scalar') v = MV('v', 'vector') b = MV('b', 'bivector') print(r'#3D Orthogonal Metric\newline') print('#Multvectors:') print('s =', s) print('v =', v) print('b =', b) print('#Products:') X = ((s, 's'), (v, 'v'), (b, 'b')) for xi in X: print('') for yi in X: print(xi[1] + '*' + yi[1] + ' =', xi[0]*yi[0]) print(xi[1] + '^' + yi[1] + ' =', xi[0] ^ yi[0]) print(xi[1] + '|' + yi[1] + ' =', xi[0] | yi[0]) print(xi[1] + '<' + yi[1] + ' =', xi[0] < yi[0]) print(xi[1] + '>' + yi[1] + ' =', xi[0] > yi[0]) fs = MV('s', 'scalar', fct=True) fv = MV('v', 'vector', fct=True) fb = MV('b', 'bivector', fct=True) print('#Multivector Functions:') print('s(X) =', fs) print('v(X) =', fv) print('b(X) =', fb) print('#Products:') fX = ((grad, 'grad'), (fs, 's'), (fv, 'v'), (fb, 'b')) for xi in fX: print('') for yi in fX: if xi[1] == 'grad' and yi[1] == 'grad': pass else: print(xi[1] + '*' + yi[1] + ' =', xi[0]*yi[0]) print(xi[1] + '^' + yi[1] + ' =', xi[0] ^ yi[0]) print(xi[1] + '|' + yi[1] + ' =', xi[0] | yi[0]) print(xi[1] + '<' + yi[1] + ' =', xi[0] < yi[0]) print(xi[1] + '>' + yi[1] + ' =', xi[0] > yi[0]) (ex, ey, grad) = MV.setup('e', coords=(x, y)) print(r'#General 2D Metric\newline') print('#Multivector Functions:') s = MV('s', 'scalar', fct=True) v = MV('v', 'vector', fct=True) b = MV('v', 'bivector', fct=True) print('s(X) =', s) print('v(X) =', v) print('b(X) =', b) X = ((grad, 'grad'), (s, 's'), (v, 'v')) print('#Products:') for xi in X: print('') for yi in X: if xi[1] == 'grad' and yi[1] == 'grad': pass else: print(xi[1] + '*' + yi[1] + ' =', xi[0]*yi[0]) print(xi[1] + '^' + yi[1] + ' =', xi[0] ^ yi[0]) print(xi[1] + '|' + yi[1] + ' =', xi[0] | yi[0]) print(xi[1] + '<' + yi[1] + ' =', xi[0] < yi[0]) print(xi[1] + '>' + yi[1] + ' =', xi[0] > yi[0]) xdvi(paper='letter') return if __name__ == "__main__": main() sympy-0.7.4.1/examples/galgebra/physics_check_latex.py0000755000175000017500000000625712253362407023231 0ustar georgeskgeorgesk#!/usr/bin/env python from __future__ import print_function from sympy import symbols, sin, cos from sympy.galgebra import xdvi, Get_Program, Print_Function from sympy.galgebra import MV, Format def Maxwells_Equations_in_Geometric_Calculus(): Print_Function() X = symbols('t x y z') (g0, g1, g2, g3, grad) = MV.setup('gamma*t|x|y|z', metric='[1,-1,-1,-1]', coords=X) I = MV.I B = MV('B', 'vector', fct=True) E = MV('E', 'vector', fct=True) B.set_coef(1, 0, 0) E.set_coef(1, 0, 0) B *= g0 E *= g0 J = MV('J', 'vector', fct=True) F = E + I*B print(r'\text{Pseudo Scalar\;\;}I =', I) print('\\text{Magnetic Field Bi-Vector\\;\\;} B = \\bm{B\\gamma_{t}} =', B) print('\\text{Electric Field Bi-Vector\\;\\;} E = \\bm{E\\gamma_{t}} =', E) print('\\text{Electromagnetic Field Bi-Vector\\;\\;} F = E+IB =', F) print('%\\text{Four Current Density\\;\\;} J =', J) gradF = grad*F print('#Geometric Derivative of Electomagnetic Field Bi-Vector') gradF.Fmt(3, 'grad*F') print('#Maxwell Equations') print('grad*F = J') print('#Div $E$ and Curl $H$ Equations') (gradF.grade(1) - J).Fmt(3, '%\\grade{\\nabla F}_{1} -J = 0') print('#Curl $E$ and Div $B$ equations') (gradF.grade(3)).Fmt(3, '%\\grade{\\nabla F}_{3} = 0') return def Dirac_Equation_in_Geometric_Calculus(): Print_Function() vars = symbols('t x y z') (g0, g1, g2, g3, grad) = MV.setup('gamma*t|x|y|z', metric='[1,-1,-1,-1]', coords=vars) I = MV.I (m, e) = symbols('m e') psi = MV('psi', 'spinor', fct=True) A = MV('A', 'vector', fct=True) sig_z = g3*g0 print('\\text{4-Vector Potential\\;\\;}\\bm{A} =', A) print('\\text{8-component real spinor\\;\\;}\\bm{\\psi} =', psi) dirac_eq = (grad*psi)*I*sig_z - e*A*psi - m*psi*g0 dirac_eq.simplify() dirac_eq.Fmt(3, r'%\text{Dirac Equation\;\;}\nabla \bm{\psi} I \sigma_{z}-e\bm{A}\bm{\psi}-m\bm{\psi}\gamma_{t} = 0') return def Lorentz_Tranformation_in_Geometric_Algebra(): Print_Function() (alpha, beta, gamma) = symbols('alpha beta gamma') (x, t, xp, tp) = symbols("x t x' t'") (g0, g1) = MV.setup('gamma*t|x', metric='[1,-1]') from sympy import sinh, cosh R = cosh(alpha/2) + sinh(alpha/2)*(g0 ^ g1) X = t*g0 + x*g1 Xp = tp*g0 + xp*g1 print('R =', R) print(r"#%t\bm{\gamma_{t}}+x\bm{\gamma_{x}} = t'\bm{\gamma'_{t}}+x'\bm{\gamma'_{x}} = R\lp t'\bm{\gamma_{t}}+x'\bm{\gamma_{x}}\rp R^{\dagger}") Xpp = R*Xp*R.rev() Xpp = Xpp.collect([xp, tp]) Xpp = Xpp.subs({2*sinh(alpha/2)*cosh(alpha/2): sinh(alpha), sinh(alpha/2)**2 + cosh(alpha/2)**2: cosh(alpha)}) print(r"%t\bm{\gamma_{t}}+x\bm{\gamma_{x}} =", Xpp) Xpp = Xpp.subs({sinh(alpha): gamma*beta, cosh(alpha): gamma}) print(r'%\f{\sinh}{\alpha} = \gamma\beta') print(r'%\f{\cosh}{\alpha} = \gamma') print(r"%t\bm{\gamma_{t}}+x\bm{\gamma_{x}} =", Xpp.collect(gamma)) return def dummy(): return def main(): Get_Program() Format() Maxwells_Equations_in_Geometric_Calculus() Dirac_Equation_in_Geometric_Calculus() Lorentz_Tranformation_in_Geometric_Algebra() xdvi() return if __name__ == "__main__": main() sympy-0.7.4.1/examples/galgebra/terminal_check.py0000755000175000017500000002603612253362407022162 0ustar georgeskgeorgesk#!/usr/bin/python from __future__ import print_function from sympy import Symbol, symbols, sin, cos, Rational, expand, simplify, collect, S from sympy.galgebra import enhance_print, Get_Program, Print_Function from sympy.galgebra import MV, Format, Com, Nga from sympy.galgebra.printing import GA_Printer def basic_multivector_operations(): Print_Function() (ex, ey, ez) = MV.setup('e*x|y|z') A = MV('A', 'mv') A.Fmt(1, 'A') A.Fmt(2, 'A') A.Fmt(3, 'A') X = MV('X', 'vector') Y = MV('Y', 'vector') print('g_{ij} =\n', MV.metric) X.Fmt(1, 'X') Y.Fmt(1, 'Y') (X*Y).Fmt(2, 'X*Y') (X ^ Y).Fmt(2, 'X^Y') (X | Y).Fmt(2, 'X|Y') (ex, ey) = MV.setup('e*x|y') print('g_{ij} =\n', MV.metric) X = MV('X', 'vector') A = MV('A', 'spinor') X.Fmt(1, 'X') A.Fmt(1, 'A') (X | A).Fmt(2, 'X|A') (X < A).Fmt(2, 'X X).Fmt(2, 'A>X') (ex, ey) = MV.setup('e*x|y', metric='[1,1]') print('g_{ii} =\n', MV.metric) X = MV('X', 'vector') A = MV('A', 'spinor') X.Fmt(1, 'X') A.Fmt(1, 'A') (X*A).Fmt(2, 'X*A') (X | A).Fmt(2, 'X|A') (X < A).Fmt(2, 'X A).Fmt(2, 'X>A') (A*X).Fmt(2, 'A*X') (A | X).Fmt(2, 'A|X') (A < X).Fmt(2, 'A X).Fmt(2, 'A>X') return def check_generalized_BAC_CAB_formulas(): Print_Function() (a, b, c, d, e) = MV.setup('a b c d e') print('g_{ij} =\n', MV.metric) print('a|(b*c) =', a | (b*c)) print('a|(b^c) =', a | (b ^ c)) print('a|(b^c^d) =', a | (b ^ c ^ d)) print('a|(b^c)+c|(a^b)+b|(c^a) =', (a | (b ^ c)) + (c | (a ^ b)) + (b | (c ^ a))) print('a*(b^c)-b*(a^c)+c*(a^b) =', a*(b ^ c) - b*(a ^ c) + c*(a ^ b)) print('a*(b^c^d)-b*(a^c^d)+c*(a^b^d)-d*(a^b^c) =', a*(b ^ c ^ d) - b*(a ^ c ^ d) + c*(a ^ b ^ d) - d*(a ^ b ^ c)) print('(a^b)|(c^d) =', (a ^ b) | (c ^ d)) print('((a^b)|c)|d =', ((a ^ b) | c) | d) print('(a^b)x(c^d) =', Com(a ^ b, c ^ d)) print('(a|(b^c))|(d^e) =', (a | (b ^ c)) | (d ^ e)) return def derivatives_in_rectangular_coordinates(): Print_Function() X = (x, y, z) = symbols('x y z') (ex, ey, ez, grad) = MV.setup('e_x e_y e_z', metric='[1,1,1]', coords=X) f = MV('f', 'scalar', fct=True) A = MV('A', 'vector', fct=True) B = MV('B', 'grade2', fct=True) C = MV('C', 'mv', fct=True) print('f =', f) print('A =', A) print('B =', B) print('C =', C) print('grad*f =', grad*f) print('grad|A =', grad | A) print('grad*A =', grad*A) print('-I*(grad^A) =', -MV.I*(grad ^ A)) print('grad*B =', grad*B) print('grad^B =', grad ^ B) print('grad|B =', grad | B) print('gradA =', grad > A) print('gradB =', grad > B) print('gradC =', grad > C) return def derivatives_in_spherical_coordinates(): Print_Function() X = (r, th, phi) = symbols('r theta phi') curv = [[r*cos(phi)*sin(th), r*sin(phi)*sin(th), r*cos(th)], [1, r, r*sin(th)]] (er, eth, ephi, grad) = MV.setup('e_r e_theta e_phi', metric='[1,1,1]', coords=X, curv=curv) f = MV('f', 'scalar', fct=True) A = MV('A', 'vector', fct=True) B = MV('B', 'grade2', fct=True) print('f =', f) print('A =', A) print('B =', B) print('grad*f =', grad*f) print('grad|A =', grad | A) print('-I*(grad^A) =', -MV.I*(grad ^ A)) print('grad^B =', grad ^ B) return def rounding_numerical_components(): Print_Function() (ex, ey, ez) = MV.setup('e_x e_y e_z', metric='[1,1,1]') X = 1.2*ex + 2.34*ey + 0.555*ez Y = 0.333*ex + 4*ey + 5.3*ez print('X =', X) print('Nga(X,2) =', Nga(X, 2)) print('X*Y =', X*Y) print('Nga(X*Y,2) =', Nga(X*Y, 2)) return def noneuclidian_distance_calculation(): from sympy import solve, sqrt Print_Function() metric = '0 # #,# 0 #,# # 1' (X, Y, e) = MV.setup('X Y e', metric) print('g_{ij} =', MV.metric) print('(X^Y)**2 =', (X ^ Y)*(X ^ Y)) L = X ^ Y ^ e B = L*e # D&L 10.152 print('B =', B) Bsq = B*B print('B**2 =', Bsq) Bsq = Bsq.scalar() print('#L = X^Y^e is a non-euclidian line') print('B = L*e =', B) BeBr = B*e*B.rev() print('B*e*B.rev() =', BeBr) print('B**2 =', B*B) print('L**2 =', L*L) # D&L 10.153 (s, c, Binv, M, BigS, BigC, alpha, XdotY, Xdote, Ydote) = symbols('s c (1/B) M S C alpha (X.Y) (X.e) (Y.e)') Bhat = Binv*B # D&L 10.154 R = c + s*Bhat # Rotor R = exp(alpha*Bhat/2) print('s = sinh(alpha/2) and c = cosh(alpha/2)') print('exp(alpha*B/(2*|B|)) =', R) Z = R*X*R.rev() # D&L 10.155 Z.obj = expand(Z.obj) Z.obj = Z.obj.collect([Binv, s, c, XdotY]) Z.Fmt(3, 'R*X*R.rev()') W = Z | Y # Extract scalar part of multivector # From this point forward all calculations are with sympy scalars print('Objective is to determine value of C = cosh(alpha) such that W = 0') W = W.scalar() print('Z|Y =', W) W = expand(W) W = simplify(W) W = W.collect([s*Binv]) M = 1/Bsq W = W.subs(Binv**2, M) W = simplify(W) Bmag = sqrt(XdotY**2 - 2*XdotY*Xdote*Ydote) W = W.collect([Binv*c*s, XdotY]) #Double angle substitutions W = W.subs(2*XdotY**2 - 4*XdotY*Xdote*Ydote, 2/(Binv**2)) W = W.subs(2*c*s, BigS) W = W.subs(c**2, (BigC + 1)/2) W = W.subs(s**2, (BigC - 1)/2) W = simplify(W) W = W.subs(1/Binv, Bmag) W = expand(W) print('S = sinh(alpha) and C = cosh(alpha)') print('W =', W) Wd = collect(W, [BigC, BigS], exact=True, evaluate=False) Wd_1 = Wd[S.One] Wd_C = Wd[BigC] Wd_S = Wd[BigS] print('Scalar Coefficient =', Wd_1) print('Cosh Coefficient =', Wd_C) print('Sinh Coefficient =', Wd_S) print('|B| =', Bmag) Wd_1 = Wd_1.subs(Bmag, 1/Binv) Wd_C = Wd_C.subs(Bmag, 1/Binv) Wd_S = Wd_S.subs(Bmag, 1/Binv) lhs = Wd_1 + Wd_C*BigC rhs = -Wd_S*BigS lhs = lhs**2 rhs = rhs**2 W = expand(lhs - rhs) W = expand(W.subs(1/Binv**2, Bmag**2)) W = expand(W.subs(BigS**2, BigC**2 - 1)) W = W.collect([BigC, BigC**2], evaluate=False) a = simplify(W[BigC**2]) b = simplify(W[BigC]) c = simplify(W[S.One]) print('Require a*C**2+b*C+c = 0') print('a =', a) print('b =', b) print('c =', c) x = Symbol('x') C = solve(a*x**2 + b*x + c, x)[0] print('cosh(alpha) = C = -b/(2*a) =', expand(simplify(expand(C)))) return def F(x): global n, nbar Fx = Rational(1, 2)*((x*x)*n + 2*x - nbar) return(Fx) def make_vector(a, n=3): if isinstance(a, str): sym_str = '' for i in range(n): sym_str += a + str(i + 1) + ' ' sym_lst = list(symbols(sym_str)) sym_lst.append(S.Zero) sym_lst.append(S.Zero) a = MV(sym_lst, 'vector') return(F(a)) def conformal_representations_of_circles_lines_spheres_and_planes(): global n, nbar Print_Function() metric = '1 0 0 0 0,0 1 0 0 0,0 0 1 0 0,0 0 0 0 2,0 0 0 2 0' (e1, e2, e3, n, nbar) = MV.setup('e_1 e_2 e_3 n nbar', metric) print('g_{ij} =\n', MV.metric) e = n + nbar #conformal representation of points A = make_vector(e1) # point a = (1,0,0) A = F(a) B = make_vector(e2) # point b = (0,1,0) B = F(b) C = make_vector(-e1) # point c = (-1,0,0) C = F(c) D = make_vector(e3) # point d = (0,0,1) D = F(d) X = make_vector('x', 3) print('F(a) =', A) print('F(b) =', B) print('F(c) =', C) print('F(d) =', D) print('F(x) =', X) print('a = e1, b = e2, c = -e1, and d = e3') print('A = F(a) = 1/2*(a*a*n+2*a-nbar), etc.') print('Circle through a, b, and c') print('Circle: A^B^C^X = 0 =', (A ^ B ^ C ^ X)) print('Line through a and b') print('Line : A^B^n^X = 0 =', (A ^ B ^ n ^ X)) print('Sphere through a, b, c, and d') print('Sphere: A^B^C^D^X = 0 =', (((A ^ B) ^ C) ^ D) ^ X) print('Plane through a, b, and d') print('Plane : A^B^n^D^X = 0 =', (A ^ B ^ n ^ D ^ X)) L = (A ^ B ^ e) ^ X L.Fmt(3, 'Hyperbolic Circle: (A^B^e)^X = 0 =') return def properties_of_geometric_objects(): Print_Function() metric = '# # # 0 0,' + \ '# # # 0 0,' + \ '# # # 0 0,' + \ '0 0 0 0 2,' + \ '0 0 0 2 0' (p1, p2, p3, n, nbar) = MV.setup('p1 p2 p3 n nbar', metric) print('g_{ij} =\n', MV.metric) P1 = F(p1) P2 = F(p2) P3 = F(p3) print('Extracting direction of line from L = P1^P2^n') L = P1 ^ P2 ^ n delta = (L | n) | nbar print('(L|n)|nbar =', delta) print('Extracting plane of circle from C = P1^P2^P3') C = P1 ^ P2 ^ P3 delta = ((C ^ n) | n) | nbar print('((C^n)|n)|nbar =', delta) print('(p2-p1)^(p3-p1) =', (p2 - p1) ^ (p3 - p1)) def extracting_vectors_from_conformal_2_blade(): Print_Function() metric = ' 0 -1 #,' + \ '-1 0 #,' + \ ' # # #,' (P1, P2, a) = MV.setup('P1 P2 a', metric) print('g_{ij} =\n', MV.metric) B = P1 ^ P2 Bsq = B*B print('B**2 =', Bsq) ap = a - (a ^ B)*B print("a' = a-(a^B)*B =", ap) Ap = ap + ap*B Am = ap - ap*B print("A+ = a'+a'*B =", Ap) print("A- = a'-a'*B =", Am) print('(A+)^2 =', Ap*Ap) print('(A-)^2 =', Am*Am) aB = a | B print('a|B =', aB) return def reciprocal_frame_test(): Print_Function() metric = '1 # #,' + \ '# 1 #,' + \ '# # 1,' (e1, e2, e3) = MV.setup('e1 e2 e3', metric) print('g_{ij} =\n', MV.metric) E = e1 ^ e2 ^ e3 Esq = (E*E).scalar() print('E =', E) print('E**2 =', Esq) Esq_inv = 1/Esq E1 = (e2 ^ e3)*E E2 = (-1)*(e1 ^ e3)*E E3 = (e1 ^ e2)*E print('E1 = (e2^e3)*E =', E1) print('E2 =-(e1^e3)*E =', E2) print('E3 = (e1^e2)*E =', E3) w = (E1 | e2) w = w.expand() print('E1|e2 =', w) w = (E1 | e3) w = w.expand() print('E1|e3 =', w) w = (E2 | e1) w = w.expand() print('E2|e1 =', w) w = (E2 | e3) w = w.expand() print('E2|e3 =', w) w = (E3 | e1) w = w.expand() print('E3|e1 =', w) w = (E3 | e2) w = w.expand() print('E3|e2 =', w) w = (E1 | e1) w = (w.expand()).scalar() Esq = expand(Esq) print('(E1|e1)/E**2 =', simplify(w/Esq)) w = (E2 | e2) w = (w.expand()).scalar() print('(E2|e2)/E**2 =', simplify(w/Esq)) w = (E3 | e3) w = (w.expand()).scalar() print('(E3|e3)/E**2 =', simplify(w/Esq)) return def dummy(): return def main(): Get_Program(True) with GA_Printer(): enhance_print() basic_multivector_operations() check_generalized_BAC_CAB_formulas() derivatives_in_rectangular_coordinates() derivatives_in_spherical_coordinates() rounding_numerical_components() noneuclidian_distance_calculation() conformal_representations_of_circles_lines_spheres_and_planes() properties_of_geometric_objects() extracting_vectors_from_conformal_2_blade() reciprocal_frame_test() return if __name__ == "__main__": main() sympy-0.7.4.1/examples/galgebra/matrix_latex.py0000755000175000017500000000103612253362407021704 0ustar georgeskgeorgesk#!/usr/bin/env python from __future__ import print_function from sympy import symbols, Matrix from sympy.galgebra import xdvi from sympy.galgebra import Format def main(): Format() a = Matrix( 2, 2, ( 1, 2, 3, 4 ) ) b = Matrix( 2, 1, ( 5, 6 ) ) c = a * b print(a, b, '=', c) x, y = symbols( 'x, y' ) d = Matrix( 1, 2, ( x ** 3, y ** 3 )) e = Matrix( 2, 2, ( x ** 2, 2 * x * y, 2 * x * y, y ** 2 ) ) f = d * e print('%', d, e, '=', f) xdvi() return if __name__ == "__main__": main() sympy-0.7.4.1/examples/galgebra/simple_check.py0000644000175000017500000000113512253362407021626 0ustar georgeskgeorgesk#!/usr/bin/env python from __future__ import print_function from sympy import symbols, sin, cos, simplify from sympy.galgebra.ga import MV from sympy.galgebra.printing import enhance_print def main(): enhance_print() (ex, ey, ez) = MV.setup('e*x|y|z', metric='[1,1,1]') u = MV('u', 'vector') v = MV('v', 'vector') w = MV('w', 'vector') print(u) print(v) print(w) uv = u ^ v print(uv) print(uv.is_blade()) uvw = u ^ v ^ w print(uvw) print(uvw.is_blade()) print(simplify((uv*uv).scalar())) return if __name__ == "__main__": main() sympy-0.7.4.1/examples/galgebra/prob_not_solenoidal.py0000755000175000017500000000156012253362407023240 0ustar georgeskgeorgesk#!/usr/bin/env python from __future__ import print_function from sympy import symbols, sin, cos, factor_terms, simplify from sympy.galgebra import enhance_print from sympy.galgebra import MV def main(): enhance_print() X = (x, y, z) = symbols('x y z') (ex, ey, ez, grad) = MV.setup('e_x e_y e_z', metric='[1,1,1]', coords=(x, y, z)) A = x*(ey ^ ez) + y*(ez ^ ex) + z*(ex ^ ey) print('A =', A) print('grad^A =', (grad ^ A).simplify()) print() f = MV('f', 'scalar', fct=True) f = (x**2 + y**2 + z**2)**(-1.5) print('f =', f) print('grad*f =', (grad*f).expand()) print() B = f*A print('B =', B) print() Curl_B = grad ^ B print('grad^B =', Curl_B.simplify()) def Symplify(A): return(factor_terms(simplify(A))) print(Curl_B.func(Symplify)) return if __name__ == "__main__": main() sympy-0.7.4.1/examples/galgebra/manifold_check_latex.py0000755000175000017500000001107712253362407023334 0ustar georgeskgeorgesk#!/usr/bin/env python from __future__ import print_function from sympy import symbols, log, simplify, diff, cos, sin from sympy.galgebra import MV, ReciprocalFrame, Format from sympy.galgebra import oprint from sympy.galgebra import xdvi, Get_Program, Print_Function from sympy.galgebra import Manifold def Test_Reciprocal_Frame(): Print_Function() Format() coords = symbols('x y z') (ex, ey, ez, grad) = MV.setup('e_x e_y e_z', metric='[1,1,1]', coords=coords) mfvar = (u, v) = symbols('u v') eu = ex + ey ev = ex - ey (eu_r, ev_r) = ReciprocalFrame([eu, ev]) oprint('\\mbox{Frame}', (eu, ev), '\\mbox{Reciprocal Frame}', (eu_r, ev_r)) print(r'%\bm{e}_{u}\cdot\bm{e}^{u} =', (eu | eu_r)) print(r'%\bm{e}_{u}\cdot\bm{e}^{v} =', eu | ev_r) print(r'%\bm{e}_{v}\cdot\bm{e}^{u} =', ev | eu_r) print(r'%\bm{e}_{v}\cdot\bm{e}^{v} =', ev | ev_r) eu = ex + ey + ez ev = ex - ey (eu_r, ev_r) = ReciprocalFrame([eu, ev]) oprint('\\mbox{Frame}', (eu, ev), '\\mbox{Reciprocal Frame}', (eu_r, ev_r)) print(r'%\bm{e}_{u}\cdot\bm{e}^{u} =', eu | eu_r) print(r'%\bm{e}_{u}\cdot\bm{e}^{v} =', eu | ev_r) print(r'%\bm{e}_{v}\cdot\bm{e}^{u} =', ev | eu_r) print(r'%\bm{e}_{v}\cdot\bm{e}^{v} =', ev | ev_r) return def Plot_Mobius_Strip_Manifold(): Print_Function() coords = symbols('x y z') (ex, ey, ez, grad) = MV.setup('e_x e_y e_z', metric='[1,1,1]', coords=coords) mfvar = (u, v) = symbols('u v') X = (cos(u) + v*cos(u/2)*cos(u))*ex + (sin(u) + v*cos(u/2)*sin(u))*ey + v*sin(u/2)*ez MF = Manifold(X, mfvar, True, I=MV.I) MF.Plot2DSurface([0.0, 6.28, 48], [-0.3, 0.3, 12], surf=False, skip=[4, 4], tan=0.15) return def Distorted_manifold_with_scalar_function(): Print_Function() coords = symbols('x y z') (ex, ey, ez, grad) = MV.setup('e_x e_y e_z', metric='[1,1,1]', coords=coords) mfvar = (u, v) = symbols('u v') X = 2*u*ex + 2*v*ey + (u**3 + v**3/2)*ez MF = Manifold(X, mfvar, I=MV.I) (eu, ev) = MF.Basis() g = (v + 1)*log(u) dg = MF.Grad(g) print('g =', g) print('dg =', dg) print('\\eval{dg}{u=1,v=0} =', dg.subs({u: 1, v: 0})) G = u*eu + v*ev dG = MF.Grad(G) print('G =', G) print('P(G) =', MF.Proj(G)) print('dG =', dG) print('P(dG) =', MF.Proj(dG)) PS = u*v*eu ^ ev print('P(S) =', PS) print('dP(S) =', MF.Grad(PS)) print('P(dP(S)) =', MF.Proj(MF.Grad(PS))) return def Simple_manifold_with_scalar_function_derivative(): Print_Function() coords = (x, y, z) = symbols('x y z') basis = (e1, e2, e3, grad) = MV.setup('e_1 e_2 e_3', metric='[1,1,1]', coords=coords) # Define surface mfvar = (u, v) = symbols('u v') X = u*e1 + v*e2 + (u**2 + v**2)*e3 print('\\f{X}{u,v} =', X) MF = Manifold(X, mfvar) (eu, ev) = MF.Basis() # Define field on the surface. g = (v + 1)*log(u) print('\\f{g}{u,v} =', g) # Method 1: Using old Manifold routines. VectorDerivative = (MF.rbasis[0]/MF.E_sq)*diff(g, u) + (MF.rbasis[1]/MF.E_sq)*diff(g, v) print('\\eval{\\nabla g}{u=1,v=0} =', VectorDerivative.subs({u: 1, v: 0})) # Method 2: Using new Manifold routines. dg = MF.Grad(g) print('\\eval{\\f{Grad}{g}}{u=1,v=0} =', dg.subs({u: 1, v: 0})) dg = MF.grad*g print('\\eval{\\nabla g}{u=1,v=0} =', dg.subs({u: 1, v: 0})) return def Simple_manifold_with_vector_function_derivative(): Print_Function() coords = (x, y, z) = symbols('x y z') basis = (ex, ey, ez, grad) = \ MV.setup('e_x e_y e_z', metric='[1,1,1]', coords=coords) # Define surface mfvar = (u, v) = symbols('u v') X = u*ex + v*ey + (u**2 + v**2)*ez print('\\f{X}{u,v} =', X) MF = Manifold(X, mfvar) (eu, ev) = MF.Basis() # Define field on the surface. g = (v + 1)*log(u) print('\\mbox{Scalar Function: } g =', g) dg = MF.grad*g dg.Fmt(3, '\\mbox{Scalar Function Derivative: } \\nabla g') print('\\eval{\\nabla g}{(1,0)} =', dg.subs({u: 1, v: 0})) # Define vector field on the surface G = v**2*eu + u**2*ev print('\\mbox{Vector Function: } G =', G) dG = MF.grad*G dG.Fmt(3, '\\mbox{Vector Function Derivative: } \\nabla G') print('\\eval{\\nabla G}{(1,0)} =', dG.subs({u: 1, v: 0})) return def dummy(): return def main(): Get_Program() Test_Reciprocal_Frame() Distorted_manifold_with_scalar_function() Simple_manifold_with_scalar_function_derivative() Simple_manifold_with_vector_function_derivative() #Plot_Mobius_Strip_Manifold() xdvi() return if __name__ == "__main__": main() sympy-0.7.4.1/examples/galgebra/eval_check.py0000755000175000017500000000230212253362407021264 0ustar georgeskgeorgesk#!/usr/bin/env python from __future__ import print_function from sympy import symbols from sympy.galgebra import MV, ReciprocalFrame from sympy.galgebra import enhance_print from sympy.galgebra import oprint from sympy.galgebra import define_precedence, GAeval def main(): enhance_print() coords = symbols('x y z') (ex, ey, ez, grad) = MV.setup('ex ey ez', metric='[1,1,1]', coords=coords) mfvar = (u, v) = symbols('u v') eu = ex + ey ev = ex - ey (eu_r, ev_r) = ReciprocalFrame([eu, ev]) oprint('Frame', (eu, ev), 'Reciprocal Frame', (eu_r, ev_r)) print('eu.eu_r =', eu | eu_r) print('eu.ev_r =', eu | ev_r) print('ev.eu_r =', ev | eu_r) print('ev.ev_r =', ev | ev_r) eu = ex + ey + ez ev = ex - ey (eu_r, ev_r) = ReciprocalFrame([eu, ev]) oprint('Frame', (eu, ev), 'Reciprocal Frame', (eu_r, ev_r)) print('eu.eu_r =', eu | eu_r) print('eu.ev_r =', eu | ev_r) print('ev.eu_r =', ev | eu_r) print('ev.ev_r =', ev | ev_r) print('eu =', eu) print('ev =', ev) define_precedence(locals()) print(GAeval('eu^ev|ex', True)) print(GAeval('eu^ev|ex*eu', True)) return if __name__ == "__main__": main() sympy-0.7.4.1/examples/galgebra/print_check_latex.py0000755000175000017500000001200712253362407022671 0ustar georgeskgeorgesk#!/usr/bin/env python from __future__ import print_function from sympy import sin, cos, sinh, cosh, symbols, expand, simplify from sympy.galgebra import xdvi from sympy.galgebra import MV, Format, Com def main(): Format() (ex, ey, ez) = MV.setup('e*x|y|z') A = MV('A', 'mv') print(r'\bm{A} =', A) A.Fmt(2, r'\bm{A}') A.Fmt(3, r'\bm{A}') X = (x, y, z) = symbols('x y z') (ex, ey, ez, grad) = MV.setup('e_x e_y e_z', metric='[1,1,1]', coords=X) f = MV('f', 'scalar', fct=True) A = MV('A', 'vector', fct=True) B = MV('B', 'grade2', fct=True) print(r'\bm{A} =', A) print(r'\bm{B} =', B) print('grad*f =', grad*f) print(r'grad|\bm{A} =', grad | A) print(r'grad*\bm{A} =', grad*A) print(r'-I*(grad^\bm{A}) =', -MV.I*(grad ^ A)) print(r'grad*\bm{B} =', grad*B) print(r'grad^\bm{B} =', grad ^ B) print(r'grad|\bm{B} =', grad | B) (a, b, c, d) = MV.setup('a b c d') print('g_{ij} =', MV.metric) print('\\bm{a|(b*c)} =', a | (b*c)) print('\\bm{a|(b^c)} =', a | (b ^ c)) print('\\bm{a|(b^c^d)} =', a | (b ^ c ^ d)) print('\\bm{a|(b^c)+c|(a^b)+b|(c^a)} =', (a | (b ^ c)) + (c | (a ^ b)) + (b | (c ^ a))) print('\\bm{a*(b^c)-b*(a^c)+c*(a^b)} =', a*(b ^ c) - b*(a ^ c) + c*(a ^ b)) print('\\bm{a*(b^c^d)-b*(a^c^d)+c*(a^b^d)-d*(a^b^c)} =', a*(b ^ c ^ d) - b*(a ^ c ^ d) + c*(a ^ b ^ d) - d*(a ^ b ^ c)) print('\\bm{(a^b)|(c^d)} =', (a ^ b) | (c ^ d)) print('\\bm{((a^b)|c)|d} =', ((a ^ b) | c) | d) print('\\bm{(a^b)\\times (c^d)} =', Com(a ^ b, c ^ d)) metric = '1 # #,' + \ '# 1 #,' + \ '# # 1,' (e1, e2, e3) = MV.setup('e1 e2 e3', metric) E = e1 ^ e2 ^ e3 Esq = (E*E).scalar() print('E =', E) print('%E^{2} =', Esq) Esq_inv = 1/Esq E1 = (e2 ^ e3)*E E2 = (-1)*(e1 ^ e3)*E E3 = (e1 ^ e2)*E print('E1 = (e2^e3)*E =', E1) print('E2 =-(e1^e3)*E =', E2) print('E3 = (e1^e2)*E =', E3) print('E1|e2 =', (E1 | e2).expand()) print('E1|e3 =', (E1 | e3).expand()) print('E2|e1 =', (E2 | e1).expand()) print('E2|e3 =', (E2 | e3).expand()) print('E3|e1 =', (E3 | e1).expand()) print('E3|e2 =', (E3 | e2).expand()) w = ((E1 | e1).expand()).scalar() Esq = expand(Esq) print('%(E1\\cdot e1)/E^{2} =', simplify(w/Esq)) w = ((E2 | e2).expand()).scalar() print('%(E2\\cdot e2)/E^{2} =', simplify(w/Esq)) w = ((E3 | e3).expand()).scalar() print('%(E3\\cdot e3)/E^{2} =', simplify(w/Esq)) X = (r, th, phi) = symbols('r theta phi') curv = [[r*cos(phi)*sin(th), r*sin(phi)*sin(th), r*cos(th)], [1, r, r*sin(th)]] (er, eth, ephi, grad) = MV.setup('e_r e_theta e_phi', metric='[1,1,1]', coords=X, curv=curv) f = MV('f', 'scalar', fct=True) A = MV('A', 'vector', fct=True) B = MV('B', 'grade2', fct=True) print('A =', A) print('B =', B) print('grad*f =', grad*f) print('grad|A =', grad | A) print('-I*(grad^A) =', -MV.I*(grad ^ A)) print('grad^B =', grad ^ B) vars = symbols('t x y z') (g0, g1, g2, g3, grad) = MV.setup('gamma*t|x|y|z', metric='[1,-1,-1,-1]', coords=vars) I = MV.I B = MV('B', 'vector', fct=True) E = MV('E', 'vector', fct=True) B.set_coef(1, 0, 0) E.set_coef(1, 0, 0) B *= g0 E *= g0 J = MV('J', 'vector', fct=True) F = E + I*B print('B = \\bm{B\\gamma_{t}} =', B) print('E = \\bm{E\\gamma_{t}} =', E) print('F = E+IB =', F) print('J =', J) gradF = grad*F gradF.Fmt(3, 'grad*F') print('grad*F = J') (gradF.grade(1) - J).Fmt(3, '%\\grade{\\nabla F}_{1} -J = 0') (gradF.grade(3)).Fmt(3, '%\\grade{\\nabla F}_{3} = 0') (alpha, beta, gamma) = symbols('alpha beta gamma') (x, t, xp, tp) = symbols("x t x' t'") (g0, g1) = MV.setup('gamma*t|x', metric='[1,-1]') R = cosh(alpha/2) + sinh(alpha/2)*(g0 ^ g1) X = t*g0 + x*g1 Xp = tp*g0 + xp*g1 print('R =', R) print(r"#%t\bm{\gamma_{t}}+x\bm{\gamma_{x}} = t'\bm{\gamma'_{t}}+x'\bm{\gamma'_{x}} = R\lp t'\bm{\gamma_{t}}+x'\bm{\gamma_{x}}\rp R^{\dagger}") Xpp = R*Xp*R.rev() Xpp = Xpp.collect([xp, tp]) Xpp = Xpp.subs({2*sinh(alpha/2)*cosh(alpha/2): sinh(alpha), sinh(alpha/2)**2 + cosh(alpha/2)**2: cosh(alpha)}) print(r"%t\bm{\gamma_{t}}+x\bm{\gamma_{x}} =", Xpp) Xpp = Xpp.subs({sinh(alpha): gamma*beta, cosh(alpha): gamma}) print(r'%\f{\sinh}{\alpha} = \gamma\beta') print(r'%\f{\cosh}{\alpha} = \gamma') print(r"%t\bm{\gamma_{t}}+x\bm{\gamma_{x}} =", Xpp.collect(gamma)) vars = symbols('t x y z') (g0, g1, g2, g3, grad) = MV.setup('gamma*t|x|y|z', metric='[1,-1,-1,-1]', coords=vars) I = MV.I (m, e) = symbols('m e') psi = MV('psi', 'spinor', fct=True) A = MV('A', 'vector', fct=True) sig_z = g3*g0 print('\\bm{A} =', A) print('\\bm{\\psi} =', psi) dirac_eq = (grad*psi)*I*sig_z - e*A*psi - m*psi*g0 dirac_eq.simplify() dirac_eq.Fmt(3, r'\nabla \bm{\psi} I \sigma_{z}-e\bm{A}\bm{\psi}-m\bm{\psi}\gamma_{t} = 0') xdvi() return if __name__ == "__main__": main() sympy-0.7.4.1/examples/advanced/0000755000175000017500000000000012253362407016627 5ustar georgeskgeorgesksympy-0.7.4.1/examples/advanced/hydrogen.py0000755000175000017500000000127612253362407021031 0ustar georgeskgeorgesk#!/usr/bin/env python """ This example shows how to work with the Hydrogen radial wavefunctions. """ from sympy import Eq, Integral, oo, pprint, symbols from sympy.physics.hydrogen import R_nl def main(): print("Hydrogen radial wavefunctions:") a, r = symbols("a r") print("R_{21}:") pprint(R_nl(2, 1, a, r)) print("R_{60}:") pprint(R_nl(6, 0, a, r)) print("Normalization:") i = Integral(R_nl(1, 0, 1, r)**2 * r**2, (r, 0, oo)) pprint(Eq(i, i.doit())) i = Integral(R_nl(2, 0, 1, r)**2 * r**2, (r, 0, oo)) pprint(Eq(i, i.doit())) i = Integral(R_nl(2, 1, 1, r)**2 * r**2, (r, 0, oo)) pprint(Eq(i, i.doit())) if __name__ == '__main__': main() sympy-0.7.4.1/examples/advanced/dense_coding_example.py0000755000175000017500000000311412253362407023337 0ustar georgeskgeorgesk#!/usr/bin/env python """Demonstration of quantum dense coding.""" from sympy import sqrt, pprint from sympy.physics.quantum import qapply from sympy.physics.quantum.gate import H, X, Z, CNOT from sympy.physics.quantum.qubit import Qubit from sympy.physics.quantum.circuitplot import circuit_plot from sympy.physics.quantum.grover import superposition_basis def main(): psi = superposition_basis(2) psi # Dense coding demo: # Assume Alice has the left QBit in psi print("An even superposition of 2 qubits. Assume Alice has the left QBit.") pprint(psi) # The corresponding gates applied to Alice's QBit are: # Identity Gate (1), Not Gate (X), Z Gate (Z), Z Gate and Not Gate (ZX) # Then there's the controlled not gate (with Alice's as control):CNOT(1, 0) # And the Hadamard gate applied to Alice's Qbit: H(1) # To Send Bob the message |0>|0> print("To Send Bob the message |00>.") circuit = H(1)*CNOT(1, 0) result = qapply(circuit*psi) result pprint(result) # To send Bob the message |0>|1> print("To Send Bob the message |01>.") circuit = H(1)*CNOT(1, 0)*X(1) result = qapply(circuit*psi) result pprint(result) # To send Bob the message |1>|0> print("To Send Bob the message |10>.") circuit = H(1)*CNOT(1, 0)*Z(1) result = qapply(circuit*psi) result pprint(result) # To send Bob the message |1>|1> print("To Send Bob the message |11>.") circuit = H(1)*CNOT(1, 0)*Z(1)*X(1) result = qapply(circuit*psi) result pprint(result) if __name__ == "__main__": main() sympy-0.7.4.1/examples/advanced/pyglet_plotting.py0000755000175000017500000001551512253362407022437 0ustar georgeskgeorgesk#!/usr/bin/env python """ Plotting Examples Suggested Usage: python -i plotting.py """ from sympy import symbols from sympy.plotting.pygletplot import PygletPlot from sympy import sin, cos, pi, sqrt, exp from time import sleep, clock def main(): x, y, z = symbols('x,y,z') # toggle axes visibility with F5, colors with F6 axes_options = 'visible=false; colored=true; label_ticks=true; label_axes=true; overlay=true; stride=0.5' #axes_options = 'colored=false; overlay=false; stride=(1.0, 0.5, 0.5)' p = PygletPlot(width=600, height=500, ortho=False, invert_mouse_zoom=False, axes=axes_options, antialiasing=True) examples = [] def example_wrapper(f): examples.append(f) return f @example_wrapper def mirrored_saddles(): p[5] = x**2 - y**2, [20], [20] p[6] = y**2 - x**2, [20], [20] @example_wrapper def mirrored_saddles_saveimage(): p[5] = x**2 - y**2, [20], [20] p[6] = y**2 - x**2, [20], [20] p.wait_for_calculations() # although the calculation is complete, # we still need to wait for it to be # rendered, so we'll sleep to be sure. sleep(1) p.saveimage("plot_example.png") @example_wrapper def mirrored_ellipsoids(): p[2] = x**2 + y**2, [40], [40], 'color=zfade' p[3] = -x**2 - y**2, [40], [40], 'color=zfade' @example_wrapper def saddle_colored_by_derivative(): f = x**2 - y**2 p[1] = f, 'style=solid' p[1].color = abs(f.diff(x)), abs(f.diff(x) + f.diff(y)), abs(f.diff(y)) @example_wrapper def ding_dong_surface(): f = sqrt(1.0 - y)*y p[1] = f, [x, 0, 2*pi, 40], [y, -1, 4, 100], 'mode=cylindrical; style=solid; color=zfade4' @example_wrapper def polar_circle(): p[7] = 1, 'mode=polar' @example_wrapper def polar_flower(): p[8] = 1.5*sin(4*x), [160], 'mode=polar' p[8].color = z, x, y, (0.5, 0.5, 0.5), (0.8, 0.8, 0.8), (x, y, None, z) # z is used for t @example_wrapper def simple_cylinder(): p[9] = 1, 'mode=cylindrical' @example_wrapper def cylindrical_hyperbola(): ## (note that polar is an alias for cylindrical) p[10] = 1/y, 'mode=polar', [x], [y, -2, 2, 20] @example_wrapper def extruded_hyperbolas(): p[11] = 1/x, [x, -10, 10, 100], [1], 'style=solid' p[12] = -1/x, [x, -10, 10, 100], [1], 'style=solid' @example_wrapper def torus(): a, b = 1, 0.5 # radius, thickness p[13] = (a + b*cos(x))*cos(y), (a + b*cos(x))*sin(y), b*sin(x), [x, 0, pi*2, 40], [y, 0, pi*2, 40] @example_wrapper def warped_torus(): a, b = 2, 1 # radius, thickness p[13] = (a + b*cos(x))*cos(y), (a + b*cos(x))*sin(y), b*sin(x) + 0.5*sin(4*y), [x, 0, pi*2, 40], [y, 0, pi*2, 40] @example_wrapper def parametric_spiral(): p[14] = cos(y), sin(y), y/10.0, [y, -4*pi, 4*pi, 100] p[14].color = x, (0.1, 0.9), y, (0.1, 0.9), z, (0.1, 0.9) @example_wrapper def multistep_gradient(): p[1] = 1, 'mode=spherical', 'style=both' #p[1] = exp(-x**2-y**2+(x*y)/4), [-1.7,1.7,100], [-1.7,1.7,100], 'style=solid' #p[1] = 5*x*y*exp(-x**2-y**2), [-2,2,100], [-2,2,100] gradient = [ 0.0, (0.3, 0.3, 1.0), 0.30, (0.3, 1.0, 0.3), 0.55, (0.95, 1.0, 0.2), 0.65, (1.0, 0.95, 0.2), 0.85, (1.0, 0.7, 0.2), 1.0, (1.0, 0.3, 0.2) ] p[1].color = z, [None, None, z], gradient #p[1].color = 'zfade' #p[1].color = 'zfade3' @example_wrapper def lambda_vs_sympy_evaluation(): start = clock() p[4] = x**2 + y**2, [100], [100], 'style=solid' p.wait_for_calculations() print("lambda-based calculation took %s seconds." % (clock() - start)) start = clock() p[4] = x**2 + y**2, [100], [100], 'style=solid; use_sympy_eval' p.wait_for_calculations() print("sympy substitution-based calculation took %s seconds." % (clock() - start)) @example_wrapper def gradient_vectors(): def gradient_vectors_inner(f, i): from sympy import lambdify from sympy.plotting.plot_interval import PlotInterval from pyglet.gl import glBegin, glColor3f from pyglet.gl import glVertex3f, glEnd, GL_LINES def draw_gradient_vectors(f, iu, iv): """ Create a function which draws vectors representing the gradient of f. """ dx, dy, dz = f.diff(x), f.diff(y), 0 FF = lambdify( [x, y], [x, y, f] ) FG = lambdify( [x, y], [dx, dy, dz] ) iu.v_steps /= 5 iv.v_steps /= 5 Gvl = list(list([FF(u, v), FG(u, v)] for v in iv.frange()) for u in iu.frange()) def draw_arrow(p1, p2): """ Draw a single vector. """ glColor3f(0.4, 0.4, 0.9) glVertex3f(*p1) glColor3f(0.9, 0.4, 0.4) glVertex3f(*p2) def draw(): """ Iterate through the calculated vectors and draw them. """ glBegin(GL_LINES) for u in Gvl: for v in u: point = [ [v[0][0], v[0][1], v[0][2]], [v[0][0] + v[1][0], v[0][1] + v[1][1], v[0][2] + v[1][2]] ] draw_arrow(point[0], point[1]) glEnd() return draw p[i] = f, [-0.5, 0.5, 25], [-0.5, 0.5, 25], 'style=solid' iu = PlotInterval(p[i].intervals[0]) iv = PlotInterval(p[i].intervals[1]) p[i].postdraw.append(draw_gradient_vectors(f, iu, iv)) gradient_vectors_inner( x**2 + y**2, 1) gradient_vectors_inner(-x**2 - y**2, 2) def help_str(): s = ("\nPlot p has been created. Useful commands: \n" " help(p), p[1] = x**2, print p, p.clear() \n\n" "Available examples (see source in plotting.py):\n\n") for i in xrange(len(examples)): s += "(%i) %s\n" % (i, examples[i].__name__) s += "\n" s += "e.g. >>> example(2)\n" s += " >>> ding_dong_surface()\n" return s def example(i): if callable(i): p.clear() i() elif i >= 0 and i < len(examples): p.clear() examples[i]() else: print("Not a valid example.\n") print(p) example(0) # 0 - 15 are defined above print(help_str()) if __name__ == "__main__": main() sympy-0.7.4.1/examples/advanced/identitysearch_example.ipynb0000644000175000017500000001227612253362407024434 0ustar georgeskgeorgesk{ "metadata": { "name": "identitysearch_example" }, "nbformat": 2, "worksheets": [ { "cells": [ { "cell_type": "code", "collapsed": true, "input": [ "\"\"\"Demonstration of quantum gate identity search.\"\"\"", "", "from sympy.physics.quantum.gate import (X, Y, Z, H, S, T, CNOT,", " IdentityGate, CGate, gate_simp)", "from sympy.physics.quantum.identitysearch import *", "from sympy.physics.quantum.dagger import Dagger" ], "language": "python", "outputs": [], "prompt_number": 1 }, { "cell_type": "code", "collapsed": true, "input": [ "# Declare a few quantum gates", "x = X(0)", "y = Y(0)", "z = Z(0)", "h = H(0)", "cnot = CNOT(1,0)", "cgate_z = CGate((0,), Z(1))" ], "language": "python", "outputs": [], "prompt_number": 2 }, { "cell_type": "code", "collapsed": false, "input": [ "# Start with the trivial cases", "gate_list = [x]", "", "bfs_identity_search(gate_list, 1, max_depth=2)" ], "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 6, "text": [ "set([GateIdentity(X(0), X(0))])" ] } ], "prompt_number": 6 }, { "cell_type": "code", "collapsed": false, "input": [ "gate_list = [y]", "", "bfs_identity_search(gate_list, 1, max_depth=2)" ], "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 7, "text": [ "set([GateIdentity(Y(0), Y(0))])" ] } ], "prompt_number": 7 }, { "cell_type": "code", "collapsed": false, "input": [ "# bfs_identity_search looks for circuits that reduce to a", "# scalar value unless told otherwise.", "# The following list should produce 4 identities as a result.", "gate_list = [x, y, z]", "", "bfs_identity_search(gate_list, 2)" ], "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 9, "text": [ "set([GateIdentity(X(0), X(0)),", " GateIdentity(Z(0), Z(0)),", " GateIdentity(X(0), Y(0), Z(0)),", " GateIdentity(Y(0), Y(0))])" ] } ], "prompt_number": 9 }, { "cell_type": "code", "collapsed": false, "input": [ "gate_list = [x, y, z, h]", "", "bfs_identity_search(gate_list, 2)" ], "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 10, "text": [ "set([GateIdentity(Y(0), H(0), Y(0), H(0)),", " GateIdentity(X(0), Y(0), X(0), Y(0)),", " GateIdentity(X(0), Y(0), Z(0)),", " GateIdentity(X(0), H(0), Z(0), H(0)),", " GateIdentity(Z(0), Z(0)),", " GateIdentity(X(0), X(0)),", " GateIdentity(Y(0), Y(0)),", " GateIdentity(X(0), Z(0), X(0), Z(0)),", " GateIdentity(Y(0), Z(0), Y(0), Z(0)),", " GateIdentity(H(0), H(0))])" ] } ], "prompt_number": 10 }, { "cell_type": "code", "collapsed": false, "input": [ "# One has the option to limit the max size of the circuit.", "# The default size is the size of the gate list.", "bfs_identity_search(gate_list, 2, max_depth=3)" ], "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 12, "text": [ "set([GateIdentity(X(0), X(0)),", " GateIdentity(X(0), Y(0), Z(0)),", " GateIdentity(Z(0), Z(0)),", " GateIdentity(H(0), H(0)),", " GateIdentity(Y(0), Y(0))])" ] } ], "prompt_number": 12 }, { "cell_type": "code", "collapsed": false, "input": [ "# One also has the option to find circuits that only reduce", "# to the Identity matrix rather than only scalar matrices.", "bfs_identity_search(gate_list, 2, identity_only=True)" ], "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 13, "text": [ "set([GateIdentity(X(0), X(0)),", " GateIdentity(Z(0), Z(0)),", " GateIdentity(H(0), H(0)),", " GateIdentity(Y(0), Y(0))])" ] } ], "prompt_number": 13 }, { "cell_type": "code", "collapsed": false, "input": [ "gate_list = [cnot, cgate_z, h]", "", "bfs_identity_search(gate_list, 2, max_depth=4)" ], "language": "python", "outputs": [ { "output_type": "pyout", "prompt_number": 14, "text": [ "set([GateIdentity(CNOT(1,0), H(0), C((0),Z(1)), H(0)),", " GateIdentity(H(0), H(0)),", " GateIdentity(C((0),Z(1)), C((0),Z(1))),", " GateIdentity(CNOT(1,0), CNOT(1,0))])" ] } ], "prompt_number": 14 } ] } ] }sympy-0.7.4.1/examples/advanced/autowrap_ufuncify.py0000755000175000017500000000454412253362407022765 0ustar georgeskgeorgesk#!/usr/bin/env python """ Setup ufuncs for the legendre polynomials ----------------------------------------- This example demonstrates how you can use the ufuncify utility in SymPy to create fast, customized universal functions for use with numpy arrays. An autowrapped sympy expression can be significantly faster than what you would get by applying a sequence of the ufuncs shipped with numpy. [0] You need to have numpy installed to run this example, as well as a working fortran compiler. [0]: http://ojensen.wordpress.com/2010/08/10/fast-ufunc-ish-hydrogen-solutions/ """ import sys from sympy.external import import_module np = import_module('numpy') if not np: sys.exit("Cannot import numpy. Exiting.") import sympy.mpmath as mpmath from sympy.utilities.autowrap import ufuncify from sympy.utilities.lambdify import implemented_function from sympy import symbols, legendre, Plot, pprint def main(): print(__doc__) x = symbols('x') # a numpy array we can apply the ufuncs to grid = np.linspace(-1, 1, 1000) # set mpmath precision to 20 significant numbers for verification mpmath.mp.dps = 20 print("Compiling legendre ufuncs and checking results:") # Let's also plot the ufunc's we generate plot1 = Plot(visible=False) for n in range(6): # Setup the SymPy expression to ufuncify expr = legendre(n, x) print("The polynomial of degree %i is" % n) pprint(expr) # This is where the magic happens: binary_poly = ufuncify(x, expr) # It's now ready for use with numpy arrays polyvector = binary_poly(grid) # let's check the values against mpmath's legendre function maxdiff = 0 for j in range(len(grid)): precise_val = mpmath.legendre(n, grid[j]) diff = abs(polyvector[j] - precise_val) if diff > maxdiff: maxdiff = diff print("The largest error in applied ufunc was %e" % maxdiff) assert maxdiff < 1e-14 # We can also attach the autowrapped legendre polynomial to a sympy # function and plot values as they are calculated by the binary function g = implemented_function('g', binary_poly) plot1[n] = g(x), [200] print("Here's a plot with values calculated by the wrapped binary functions") plot1.show() if __name__ == '__main__': main() sympy-0.7.4.1/examples/advanced/curvilinear_coordinates.py0000755000175000017500000000672212253362407024130 0ustar georgeskgeorgesk#!/usr/bin/env python """ This example shows how to work with coordinate transformations, curvilinear coordinates and a little bit with differential geometry. It takes polar, cylindrical, spherical, rotating disk coordinates and others and calculates all kinds of interesting properties, like Jacobian, metric tensor, Laplace operator, ... """ from sympy import var, sin, cos, pprint, Matrix, eye, trigsimp, Eq, \ Function, simplify, sinh, cosh, expand def laplace(f, g_inv, g_det, X): """ Calculates Laplace(f), using the inverse metric g_inv, the determinant of the metric g_det, all in variables X. """ r = 0 for i in range(len(X)): for j in range(len(X)): r += g_inv[i, j]*f.diff(X[i]).diff(X[j]) for sigma in range(len(X)): for alpha in range(len(X)): r += g_det.diff(X[sigma]) * g_inv[sigma, alpha] * \ f.diff(X[alpha]) / (2*g_det) return r def transform(name, X, Y, g_correct=None, recursive=False): """ Transforms from cartesian coordinates X to any curvilinear coordinates Y. It printing useful information, like Jacobian, metric tensor, determinant of metric, Laplace operator in the new coordinates, ... g_correct ... if not None, it will be taken as the metric --- this is useful if sympy's trigsimp() is not powerful enough to simplify the metric so that it is usable for later calculation. Leave it as None, only if the metric that transform() prints is not simplified, you can help it by specifying the correct one. recursive ... apply recursive trigonometric simplification (use only when needed, as it is an expensive operation) """ print("_"*80) print("Transformation:", name) for x, y in zip(X, Y): pprint(Eq(y, x)) J = X.jacobian(Y) print("Jacobian:") pprint(J) g = J.T*eye(J.shape[0])*J g = g.applyfunc(expand) #g = g.applyfunc(trigsimp) print("metric tensor g_{ij}:") pprint(g) if g_correct is not None: g = g_correct print("metric tensor g_{ij} specified by hand:") pprint(g) print("inverse metric tensor g^{ij}:") g_inv = g.inv(method="ADJ") g_inv = g_inv.applyfunc(simplify) pprint(g_inv) print("det g_{ij}:") g_det = g.det() pprint(g_det) f = Function("f")(*list(Y)) print("Laplace:") pprint(laplace(f, g_inv, g_det, Y)) def main(): var("mu nu rho theta phi sigma tau a t x y z w") transform("polar", Matrix([rho*cos(phi), rho*sin(phi)]), [rho, phi]) transform("cylindrical", Matrix([rho*cos(phi), rho*sin(phi), z]), [rho, phi, z]) transform("spherical", Matrix([rho*sin(theta)*cos(phi), rho*sin(theta)*sin(phi), rho*cos(theta)]), [rho, theta, phi], recursive=True ) transform("rotating disk", Matrix([t, x*cos(w*t) - y*sin(w*t), x*sin(w*t) + y*cos(w*t), z]), [t, x, y, z]) transform("parabolic", Matrix([sigma*tau, (tau**2 - sigma**2)/2]), [sigma, tau]) # too complex: #transform("bipolar", # Matrix([a*sinh(tau)/(cosh(tau)-cos(sigma)), # a*sin(sigma)/(cosh(tau)-cos(sigma))]), # [sigma, tau] # ) transform("elliptic", Matrix([a*cosh(mu)*cos(nu), a*sinh(mu)*sin(nu)]), [mu, nu] ) if __name__ == "__main__": main() sympy-0.7.4.1/examples/advanced/grover_example.py0000755000175000017500000000401112253362407022217 0ustar georgeskgeorgesk#!/usr/bin/env python """Grover's quantum search algorithm example.""" from sympy import pprint from sympy.physics.quantum import qapply from sympy.physics.quantum.qubit import IntQubit from sympy.physics.quantum.grover import (OracleGate, superposition_basis, WGate, grover_iteration) def demo_vgate_app(v): for i in range(2**v.nqubits): print('qapply(v*IntQubit(%i, %r))' % (i, v.nqubits)) pprint(qapply(v*IntQubit(i, v.nqubits))) qapply(v*IntQubit(i, v.nqubits)) def black_box(qubits): return True if qubits == IntQubit(1, qubits.nqubits) else False def main(): print() print('Demonstration of Grover\'s Algorithm') print('The OracleGate or V Gate carries the unknown function f(x)') print('> V|x> = ((-1)^f(x))|x> where f(x) = 1 when x = a (True in our case)') print('> and 0 (False in our case) otherwise') print() nqubits = 2 print('nqubits = ', nqubits) v = OracleGate(nqubits, black_box) print('Oracle or v = OracleGate(%r, black_box)' % nqubits) print() psi = superposition_basis(nqubits) print('psi:') pprint(psi) demo_vgate_app(v) print('qapply(v*psi)') pprint(qapply(v*psi)) print() w = WGate(nqubits) print('WGate or w = WGate(%r)' % nqubits) print('On a 2 Qubit system like psi, 1 iteration is enough to yield |1>') print('qapply(w*v*psi)') pprint(qapply(w*v*psi)) print() nqubits = 3 print('On a 3 Qubit system, it requires 2 iterations to achieve') print('|1> with high enough probability') psi = superposition_basis(nqubits) print('psi:') pprint(psi) v = OracleGate(nqubits, black_box) print('Oracle or v = OracleGate(%r, black_box)' % nqubits) print() print('iter1 = grover.grover_iteration(psi, v)') iter1 = qapply(grover_iteration(psi, v)) pprint(iter1) print() print('iter2 = grover.grover_iteration(iter1, v)') iter2 = qapply(grover_iteration(iter1, v)) pprint(iter2) print() if __name__ == "__main__": main() sympy-0.7.4.1/examples/advanced/fem.py0000755000175000017500000001270512253362407017760 0ustar georgeskgeorgesk#!/usr/bin/env python """FEM library Demonstrates some simple finite element definitions, and computes a mass matrix $ python fem.py [ 1/60, 0, -1/360, 0, -1/90, -1/360] [ 0, 4/45, 0, 2/45, 2/45, -1/90] [-1/360, 0, 1/60, -1/90, 0, -1/360] [ 0, 2/45, -1/90, 4/45, 2/45, 0] [ -1/90, 2/45, 0, 2/45, 4/45, 0] [-1/360, -1/90, -1/360, 0, 0, 1/60] """ from sympy import symbols, Symbol, factorial, Rational, zeros, div, eye, \ integrate, diff, pprint, reduced x, y, z = symbols('x,y,z') class ReferenceSimplex: def __init__(self, nsd): self.nsd = nsd if nsd <= 3: coords = symbols('x,y,z')[:nsd] else: coords = [Symbol("x_%d" % d) for d in range(nsd)] self.coords = coords def integrate(self, f): coords = self.coords nsd = self.nsd limit = 1 for p in coords: limit -= p intf = f for d in range(0, nsd): p = coords[d] limit += p intf = integrate(intf, (p, 0, limit)) return intf def bernstein_space(order, nsd): if nsd > 3: raise RuntimeError("Bernstein only implemented in 1D, 2D, and 3D") sum = 0 basis = [] coeff = [] if nsd == 1: b1, b2 = x, 1 - x for o1 in range(0, order + 1): for o2 in range(0, order + 1): if o1 + o2 == order: aij = Symbol("a_%d_%d" % (o1, o2)) sum += aij*binomial(order, o1)*pow(b1, o1)*pow(b2, o2) basis.append(binomial(order, o1)*pow(b1, o1)*pow(b2, o2)) coeff.append(aij) if nsd == 2: b1, b2, b3 = x, y, 1 - x - y for o1 in range(0, order + 1): for o2 in range(0, order + 1): for o3 in range(0, order + 1): if o1 + o2 + o3 == order: aij = Symbol("a_%d_%d_%d" % (o1, o2, o3)) fac = factorial(order) / (factorial(o1)*factorial(o2)*factorial(o3)) sum += aij*fac*pow(b1, o1)*pow(b2, o2)*pow(b3, o3) basis.append(fac*pow(b1, o1)*pow(b2, o2)*pow(b3, o3)) coeff.append(aij) if nsd == 3: b1, b2, b3, b4 = x, y, z, 1 - x - y - z for o1 in range(0, order + 1): for o2 in range(0, order + 1): for o3 in range(0, order + 1): for o4 in range(0, order + 1): if o1 + o2 + o3 + o4 == order: aij = Symbol("a_%d_%d_%d_%d" % (o1, o2, o3, o4)) fac = factorial(order)/ (factorial(o1)*factorial(o2)*factorial(o3)*factorial(o4)) sum += aij*fac*pow(b1, o1)*pow(b2, o2)*pow(b3, o3)*pow(b4, o4) basis.append(fac*pow(b1, o1)*pow(b2, o2)*pow(b3, o3)*pow(b4, o4)) coeff.append(aij) return sum, coeff, basis def create_point_set(order, nsd): h = Rational(1, order) set = [] if nsd == 1: for i in range(0, order + 1): x = i*h if x <= 1: set.append((x, y)) if nsd == 2: for i in range(0, order + 1): x = i*h for j in range(0, order + 1): y = j*h if x + y <= 1: set.append((x, y)) if nsd == 3: for i in range(0, order + 1): x = i*h for j in range(0, order + 1): y = j*h for k in range(0, order + 1): z = j*h if x + y + z <= 1: set.append((x, y, z)) return set def create_matrix(equations, coeffs): A = zeros(len(equations)) i = 0 j = 0 for j in range(0, len(coeffs)): c = coeffs[j] for i in range(0, len(equations)): e = equations[i] d, _ = reduced(e, [c]) A[i, j] = d[0] return A class Lagrange: def __init__(self, nsd, order): self.nsd = nsd self.order = order self.compute_basis() def nbf(self): return len(self.N) def compute_basis(self): order = self.order nsd = self.nsd N = [] pol, coeffs, basis = bernstein_space(order, nsd) points = create_point_set(order, nsd) equations = [] for p in points: ex = pol.subs(x, p[0]) if nsd > 1: ex = ex.subs(y, p[1]) if nsd > 2: ex = ex.subs(z, p[2]) equations.append(ex ) A = create_matrix(equations, coeffs) Ainv = A.inv() b = eye(len(equations)) xx = Ainv*b for i in range(0, len(equations)): Ni = pol for j in range(0, len(coeffs)): Ni = Ni.subs(coeffs[j], xx[j, i]) N.append(Ni) self.N = N def main(): t = ReferenceSimplex(2) fe = Lagrange(2, 2) u = 0 #compute u = sum_i u_i N_i us = [] for i in range(0, fe.nbf()): ui = Symbol("u_%d" % i) us.append(ui) u += ui*fe.N[i] J = zeros(fe.nbf()) for i in range(0, fe.nbf()): Fi = u*fe.N[i] print(Fi) for j in range(0, fe.nbf()): uj = us[j] integrands = diff(Fi, uj) print(integrands) J[j, i] = t.integrate(integrands) pprint(J) if __name__ == "__main__": main() sympy-0.7.4.1/examples/advanced/autowrap_integrators.py0000755000175000017500000002155612253362407023500 0ustar georgeskgeorgesk#!/usr/bin/env python """ Numerical integration with autowrap ----------------------------------- This example demonstrates how you can use the autowrap module in SymPy to create fast, numerical integration routines callable from python. See in the code for detailed explanations of the various steps. An autowrapped sympy expression can be significantly faster than what you would get by applying a sequence of the ufuncs shipped with numpy. [0] We will find the coefficients needed to approximate a quantum mechanical Hydrogen wave function in terms of harmonic oscillator solutions. For the sake of demonstration, this will be done by setting up a simple numerical integration scheme as a SymPy expression, and obtain a binary implementation with autowrap. You need to have numpy installed to run this example, as well as a working fortran compiler. If you have pylab installed, you will be rewarded with a nice plot in the end. [0]: http://ojensen.wordpress.com/2010/08/10/fast-ufunc-ish-hydrogen-solutions/ ---- """ import sys from sympy.external import import_module np = import_module('numpy') if not np: sys.exit("Cannot import numpy. Exiting.") pylab = import_module('pylab', warn_not_installed=True) from sympy.utilities.lambdify import implemented_function from sympy.utilities.autowrap import autowrap, ufuncify from sympy import Idx, IndexedBase, Lambda, pprint, Symbol, oo, Integral,\ Function from sympy.physics.sho import R_nl from sympy.physics.hydrogen import R_nl as hydro_nl # *************************************************************************** # calculation parameters to play with # *************************************************************************** basis_dimension = 5 # Size of h.o. basis (n < basis_dimension) omega2 = 0.1 # in atomic units: twice the oscillator frequency orbital_momentum_l = 1 # the quantum number `l` for angular momentum hydrogen_n = 2 # the nodal quantum number for the Hydrogen wave rmax = 20 # cut off in the radial direction gridsize = 200 # number of points in the grid # *************************************************************************** def main(): print(__doc__) # arrays are represented with IndexedBase, indices with Idx m = Symbol('m', integer=True) i = Idx('i', m) A = IndexedBase('A') B = IndexedBase('B') x = Symbol('x') print("Compiling ufuncs for radial harmonic oscillator solutions") # setup a basis of ho-solutions (for l=0) basis_ho = {} for n in range(basis_dimension): # Setup the radial ho solution for this n expr = R_nl(n, orbital_momentum_l, omega2, x) # Reduce the number of operations in the expression by eval to float expr = expr.evalf(15) print("The h.o. wave function with l = %i and n = %i is" % ( orbital_momentum_l, n)) pprint(expr) # implement, compile and wrap it as a ufunc basis_ho[n] = ufuncify(x, expr) # now let's see if we can express a hydrogen radial wave in terms of # the ho basis. Here's the solution we will approximate: H_ufunc = ufuncify(x, hydro_nl(hydrogen_n, orbital_momentum_l, 1, x)) # The transformation to a different basis can be written like this, # # psi(r) = sum_i c(i) phi_i(r) # # where psi(r) is the hydrogen solution, phi_i(r) are the H.O. solutions # and c(i) are scalar coefficients. # # So in order to express a hydrogen solution in terms of the H.O. basis, we # need to determine the coefficients c(i). In position space, it means # that we need to evaluate an integral: # # psi(r) = sum_i Integral(R**2*conj(phi(R))*psi(R), (R, 0, oo)) phi_i(r) # # To calculate the integral with autowrap, we notice that it contains an # element-wise sum over all vectors. Using the Indexed class, it is # possible to generate autowrapped functions that perform summations in # the low-level code. (In fact, summations are very easy to create, and as # we will see it is often necessary to take extra steps in order to avoid # them.) # we need one integration ufunc for each wave function in the h.o. basis binary_integrator = {} for n in range(basis_dimension): # # setup basis wave functions # # To get inline expressions in the low level code, we attach the # wave function expressions to a regular SymPy function using the # implemented_function utility. This is an extra step needed to avoid # erronous summations in the wave function expressions. # # Such function objects carry around the expression they represent, # but the expression is not exposed unless explicit measures are taken. # The benefit is that the routines that searches for repeated indices # in order to make contractions will not search through the wave # function expression. psi_ho = implemented_function('psi_ho', Lambda(x, R_nl(n, orbital_momentum_l, omega2, x))) # We represent the hydrogen function by an array which will be an input # argument to the binary routine. This will let the integrators find # h.o. basis coefficients for any wave function we throw at them. psi = IndexedBase('psi') # # setup expression for the integration # step = Symbol('step') # use symbolic stepsize for flexibility # let i represent an index of the grid array, and let A represent the # grid array. Then we can approximate the integral by a sum over the # following expression (simplified rectangular rule, ignoring end point # corrections): expr = A[i]**2*psi_ho(A[i])*psi[i]*step if n == 0: print("Setting up binary integrators for the integral:") pprint(Integral(x**2*psi_ho(x)*Function('psi')(x), (x, 0, oo))) # But it needs to be an operation on indexed objects, so that the code # generators will recognize it correctly as an array. # expr = expr.subs(x, A[i]) # Autowrap it. For functions that take more than one argument, it is # a good idea to use the 'args' keyword so that you know the signature # of the wrapped function. (The dimension m will be an optional # argument, but it must be present in the args list.) binary_integrator[n] = autowrap(expr, args=[A.label, psi.label, step, m]) # Lets see how it converges with the grid dimension print("Checking convergence of integrator for n = %i" % n) for g in range(3, 8): grid, step = np.linspace(0, rmax, 2**g, retstep=True) print("grid dimension %5i, integral = %e" % (2**g, binary_integrator[n](grid, H_ufunc(grid), step))) print("A binary integrator has been set up for each basis state") print("We will now use them to reconstruct a hydrogen solution.") # Note: We didn't need to specify grid or use gridsize before now grid, stepsize = np.linspace(0, rmax, gridsize, retstep=True) print("Calculating coefficients with gridsize = %i and stepsize %f" % ( len(grid), stepsize)) coeffs = {} for n in range(basis_dimension): coeffs[n] = binary_integrator[n](grid, H_ufunc(grid), stepsize) print("c(%i) = %e" % (n, coeffs[n])) print("Constructing the approximate hydrogen wave") hydro_approx = 0 all_steps = {} for n in range(basis_dimension): hydro_approx += basis_ho[n](grid)*coeffs[n] all_steps[n] = hydro_approx.copy() if pylab: line = pylab.plot(grid, all_steps[n], ':', label='max n = %i' % n) # check error numerically diff = np.max(np.abs(hydro_approx - H_ufunc(grid))) print("Error estimate: the element with largest deviation misses by %f" % diff) if diff > 0.01: print("This is much, try to increase the basis size or adjust omega") else: print("Ah, that's a pretty good approximation!") # Check visually if pylab: print("Here's a plot showing the contribution for each n") line[0].set_linestyle('-') pylab.plot(grid, H_ufunc(grid), 'r-', label='exact') pylab.legend() pylab.show() print("""Note: These binary integrators were specialized to find coefficients for a harmonic oscillator basis, but they can process any wave function as long as it is available as a vector and defined on a grid with equidistant points. That is, on any grid you get from numpy.linspace. To make the integrators even more flexible, you can setup the harmonic oscillator solutions with symbolic parameters omega and l. Then the autowrapped binary routine will take these scalar variables as arguments, so that the integrators can find coefficients for *any* isotropic harmonic oscillator basis. """) if __name__ == '__main__': main() sympy-0.7.4.1/examples/advanced/pidigits.py0000755000175000017500000000525512253362407021027 0ustar georgeskgeorgesk#!/usr/bin/env python """Pi digits example Example shows arbitrary precision using mpmath with the computation of the digits of pi. """ from sympy.mpmath import libmp, pi from sympy.mpmath import functions as mpf_funs import math from time import clock import sys def display_fraction(digits, skip=0, colwidth=10, columns=5): """Pretty printer for first n digits of a fraction""" perline = colwidth * columns printed = 0 for linecount in range((len(digits) - skip) // (colwidth * columns)): line = digits[skip + linecount*perline:skip + (linecount + 1)*perline] for i in range(columns): print(line[i*colwidth: (i + 1)*colwidth],) print(":", (linecount + 1)*perline) if (linecount + 1) % 10 == 0: print printed += colwidth*columns rem = (len(digits) - skip) % (colwidth * columns) if rem: buf = digits[-rem:] s = "" for i in range(columns): s += buf[:colwidth].ljust(colwidth + 1, " ") buf = buf[colwidth:] print(s + ":", printed + colwidth*columns) def calculateit(func, base, n, tofile): """Writes first n base-digits of a mpmath function to file""" prec = 100 intpart = libmp.numeral(3, base) if intpart == 0: skip = 0 else: skip = len(intpart) print("Step 1 of 2: calculating binary value...") prec = int(n*math.log(base, 2)) + 10 t = clock() a = func(prec) step1_time = clock() - t print("Step 2 of 2: converting to specified base...") t = clock() d = libmp.bin_to_radix(a.man, -a.exp, base, n) d = libmp.numeral(d, base, n) step2_time = clock() - t print("\nWriting output...\n") if tofile: out_ = sys.stdout sys.stdout = tofile print("%i base-%i digits of pi:\n" % (n, base)) print(intpart, ".\n") display_fraction(d, skip, colwidth=10, columns=5) if tofile: sys.stdout = out_ print("\nFinished in %f seconds (%f calc, %f convert)" % \ ((step1_time + step2_time), step1_time, step2_time)) def interactive(): """Simple function to interact with user""" print("Compute digits of pi with SymPy\n") base = input("Which base? (2-36, 10 for decimal) \n> ") digits = input("How many digits? (enter a big number, say, 10000)\n> ") tofile = raw_input("Output to file? (enter a filename, or just press enter\nto print directly to the screen) \n> ") if tofile: tofile = open(tofile, "w") calculateit(pi, base, digits, tofile) def main(): """A non-interactive runner""" base = 16 digits = 500 tofile = None calculateit(pi, base, digits, tofile) if __name__ == "__main__": interactive() sympy-0.7.4.1/examples/advanced/relativity.py0000755000175000017500000001167612253362407021413 0ustar georgeskgeorgesk#!/usr/bin/env python """ This example calculates the Ricci tensor from the metric and does this on the example of Schwarzschild solution. If you want to derive this by hand, follow the wiki page here: http://en.wikipedia.org/wiki/Deriving_the_Schwarzschild_solution Also read the above wiki and follow the references from there if something is not clear, like what the Ricci tensor is, etc. """ from sympy import (exp, Symbol, sin, Rational, Derivative, dsolve, Function, Matrix, Eq, pprint, Pow, classify_ode, solve) def grad(f, X): a = [] for x in X: a.append(f.diff(x)) return a def d(m, x): return grad(m[0, 0], x) class MT(object): def __init__(self, m): self.gdd = m self.guu = m.inv() def __str__(self): return "g_dd =\n" + str(self.gdd) def dd(self, i, j): return self.gdd[i, j] def uu(self, i, j): return self.guu[i, j] class G(object): def __init__(self, g, x): self.g = g self.x = x def udd(self, i, k, l): g = self.g x = self.x r = 0 for m in [0, 1, 2, 3]: r += g.uu(i, m)/2 * (g.dd(m, k).diff(x[l]) + g.dd(m, l).diff(x[k]) - g.dd(k, l).diff(x[m])) return r class Riemann(object): def __init__(self, G, x): self.G = G self.x = x def uddd(self, rho, sigma, mu, nu): G = self.G x = self.x r = G.udd(rho, nu, sigma).diff(x[mu]) - G.udd(rho, mu, sigma).diff(x[nu]) for lam in [0, 1, 2, 3]: r += G.udd(rho, mu, lam)*G.udd(lam, nu, sigma) \ - G.udd(rho, nu, lam)*G.udd(lam, mu, sigma) return r class Ricci(object): def __init__(self, R, x): self.R = R self.x = x self.g = R.G.g def dd(self, mu, nu): R = self.R x = self.x r = 0 for lam in [0, 1, 2, 3]: r += R.uddd(lam, mu, lam, nu) return r def ud(self, mu, nu): r = 0 for lam in [0, 1, 2, 3]: r += self.g.uu(mu, lam)*self.dd(lam, nu) return r.expand() def curvature(Rmn): return Rmn.ud(0, 0) + Rmn.ud(1, 1) + Rmn.ud(2, 2) + Rmn.ud(3, 3) #class nu(Function): # def getname(self): # return r"\nu" # return r"nu" #class lam(Function): # def getname(self): # return r"\lambda" # return r"lambda" nu = Function("nu") lam = Function("lambda") t = Symbol("t") r = Symbol("r") theta = Symbol(r"theta") phi = Symbol(r"phi") #general, spherically symmetric metric gdd = Matrix(( (-exp(nu(r)), 0, 0, 0), (0, exp(lam(r)), 0, 0), (0, 0, r**2, 0), (0, 0, 0, r**2*sin(theta)**2) )) #spherical - flat #gdd=Matrix(( # (-1, 0, 0, 0), # (0, 1, 0, 0), # (0, 0, r**2, 0), # (0, 0, 0, r**2*sin(theta)**2) # )) #polar - flat #gdd=Matrix(( # (-1, 0, 0, 0), # (0, 1, 0, 0), # (0, 0, 1, 0), # (0, 0, 0, r**2) # )) #polar - on the sphere, on the north pole #gdd=Matrix(( # (-1, 0, 0, 0), # (0, 1, 0, 0), # (0, 0, r**2*sin(theta)**2, 0), # (0, 0, 0, r**2) # )) g = MT(gdd) X = (t, r, theta, phi) Gamma = G(g, X) Rmn = Ricci(Riemann(Gamma, X), X) def pprint_Gamma_udd(i, k, l): pprint(Eq(Symbol('Gamma^%i_%i%i' % (i, k, l)), Gamma.udd(i, k, l))) def pprint_Rmn_dd(i, j): pprint(Eq(Symbol('R_%i%i' % (i, j)), Rmn.dd(i, j))) # from Differential Equations example def eq1(): r = Symbol("r") e = Rmn.dd(0, 0) e = e.subs(nu(r), -lam(r)) pprint(dsolve(e, lam(r))) def eq2(): r = Symbol("r") e = Rmn.dd(1, 1) C = Symbol("CC") e = e.subs(nu(r), -lam(r)) pprint(dsolve(e, lam(r))) def eq3(): r = Symbol("r") e = Rmn.dd(2, 2) e = e.subs(nu(r), -lam(r)) pprint(dsolve(e, lam(r))) def eq4(): r = Symbol("r") e = Rmn.dd(3, 3) e = e.subs(nu(r), -lam(r)) pprint(dsolve(e, lam(r))) pprint(dsolve(e, lam(r), 'best')) def main(): print("Initial metric:") pprint(gdd) print("-"*40) print("Christoffel symbols:") pprint_Gamma_udd(0, 1, 0) pprint_Gamma_udd(0, 0, 1) print() pprint_Gamma_udd(1, 0, 0) pprint_Gamma_udd(1, 1, 1) pprint_Gamma_udd(1, 2, 2) pprint_Gamma_udd(1, 3, 3) print() pprint_Gamma_udd(2, 2, 1) pprint_Gamma_udd(2, 1, 2) pprint_Gamma_udd(2, 3, 3) print() pprint_Gamma_udd(3, 2, 3) pprint_Gamma_udd(3, 3, 2) pprint_Gamma_udd(3, 1, 3) pprint_Gamma_udd(3, 3, 1) print("-"*40) print("Ricci tensor:") pprint_Rmn_dd(0, 0) e = Rmn.dd(1, 1) pprint_Rmn_dd(1, 1) pprint_Rmn_dd(2, 2) pprint_Rmn_dd(3, 3) #print() #print "scalar curvature:" #print curvature(Rmn) print("-"*40) print("Solve Einstein's equations:") e = e.subs(nu(r), -lam(r)).doit() l = dsolve(e, lam(r)) pprint(l) lamsol = solve(l, lam(r))[0] metric = gdd.subs(lam(r), lamsol).subs(nu(r), -lamsol) # .combine() print("metric:") pprint(metric) if __name__ == "__main__": main() sympy-0.7.4.1/examples/advanced/gibbs_phenomenon.py0000755000175000017500000000715212253362407022525 0ustar georgeskgeorgesk#!/usr/bin/env python """ This example illustrates the Gibbs phenomenon. It also calculates the Wilbraham-Gibbs constant by two approaches: 1) calculating the fourier series of the step function and determining the first maximum. 2) evaluating the integral for si(pi). See: * http://en.wikipedia.org/wiki/Gibbs_phenomena """ from sympy import var, sqrt, integrate, conjugate, seterr, Abs, pprint, I, pi,\ sin, cos, sign, Plot, lambdify, Integral, S #seterr(True) x = var("x", real=True) def l2_norm(f, lim): """ Calculates L2 norm of the function "f", over the domain lim=(x, a, b). x ...... the independent variable in f over which to integrate a, b ... the limits of the interval Example: >>> from sympy import Symbol >>> from gibbs_phenomenon import l2_norm >>> x = Symbol('x', real=True) >>> l2_norm(1, (x, -1, 1)) sqrt(2) >>> l2_norm(x, (x, -1, 1)) sqrt(6)/3 """ return sqrt(integrate(Abs(f)**2, lim)) def l2_inner_product(a, b, lim): """ Calculates the L2 inner product (a, b) over the domain lim. """ return integrate(conjugate(a)*b, lim) def l2_projection(f, basis, lim): """ L2 projects the function f on the basis over the domain lim. """ r = 0 for b in basis: r += l2_inner_product(f, b, lim) * b return r def l2_gram_schmidt(list, lim): """ Orthonormalizes the "list" of functions using the Gram-Schmidt process. Example: >>> from sympy import Symbol >>> from gibbs_phenomenon import l2_gram_schmidt >>> x = Symbol('x', real=True) # perform computations over reals to save time >>> l2_gram_schmidt([1, x, x**2], (x, -1, 1)) [sqrt(2)/2, sqrt(6)*x/2, 3*sqrt(10)*(x**2 - 1/3)/4] """ r = [] for a in list: if r == []: v = a else: v = a - l2_projection(a, r, lim) v_norm = l2_norm(v, lim) if v_norm == 0: raise ValueError("The sequence is not linearly independent.") r.append(v/v_norm) return r def integ(f): return integrate(f, (x, -pi, 0)) + integrate(-f, (x, 0, pi)) def series(L): """ Normalizes the series. """ r = 0 for b in L: r += integ(b)*b return r def msolve(f, x): """ Finds the first root of f(x) to the left of 0. The x0 and dx below are taylored to get the correct result for our particular function --- the general solver often overshoots the first solution. """ f = lambdify(x, f) x0 = -0.001 dx = 0.001 while f(x0 - dx) * f(x0) > 0: x0 = x0 - dx x_max = x0 - dx x_min = x0 assert f(x_max) > 0 assert f(x_min) < 0 for n in range(100): x0 = (x_max + x_min)/2 if f(x0) > 0: x_max = x0 else: x_min = x0 return x0 def main(): #L = l2_gram_schmidt([1, cos(x), sin(x), cos(2*x), sin(2*x)], (x, -pi, pi)) #L = l2_gram_schmidt([1, cos(x), sin(x)], (x, -pi, pi)) # the code below is equivalen to l2_gram_schmidt(), but faster: L = [1/sqrt(2)] for i in range(1, 100): L.append(cos(i*x)) L.append(sin(i*x)) L = [f/sqrt(pi) for f in L] f = series(L) print("Fourier series of the step function") pprint(f) #Plot(f.diff(x), [x, -5, 5, 3000]) x0 = msolve(f.diff(x), x) print("x-value of the maximum:", x0) max = f.subs(x, x0).evalf() print("y-value of the maximum:", max) g = max*pi/2 print("Wilbraham-Gibbs constant :", g.evalf()) print("Wilbraham-Gibbs constant (exact):", \ Integral(sin(x)/x, (x, 0, pi)).evalf()) if __name__ == "__main__": main() sympy-0.7.4.1/examples/advanced/qft.py0000755000175000017500000000651412253362407020004 0ustar georgeskgeorgesk#!/usr/bin/env python """Quantum field theory example * http://en.wikipedia.org/wiki/Quantum_field_theory This particular example is a work in progress. Currently it calculates the scattering amplitude of the process: electron + positron -> photon -> electron + positron in QED (http://en.wikipedia.org/wiki/Quantum_electrodynamics). The aim is to be able to do any kind of calculations in QED or standard model in SymPy, but that's a long journey. """ from sympy import Basic, exp, Symbol, sin, Rational, I, Mul, Matrix, \ ones, sqrt, pprint, simplify, Eq, sympify from sympy.physics import msigma, mgamma #gamma^mu gamma0 = mgamma(0) gamma1 = mgamma(1) gamma2 = mgamma(2) gamma3 = mgamma(3) gamma5 = mgamma(5) #sigma_i sigma1 = msigma(1) sigma2 = msigma(2) sigma3 = msigma(3) E = Symbol("E", real=True) m = Symbol("m", real=True) def u(p, r): """ p = (p1, p2, p3); r = 0,1 """ assert r in [1, 2] p1, p2, p3 = p if r == 1: ksi = Matrix([[1], [0]]) else: ksi = Matrix([[0], [1]]) a = (sigma1*p1 + sigma2*p2 + sigma3*p3) / (E + m) * ksi if a == 0: a = zeros(2, 1) return sqrt(E + m) * Matrix([[ksi[0, 0]], [ksi[1, 0]], [a[0, 0]], [a[1, 0]]]) def v(p, r): """ p = (p1, p2, p3); r = 0,1 """ assert r in [1, 2] p1, p2, p3 = p if r == 1: ksi = Matrix([[1], [0]]) else: ksi = -Matrix([[0], [1]]) a = (sigma1*p1 + sigma2*p2 + sigma3*p3) / (E + m) * ksi if a == 0: a = zeros(2, 1) return sqrt(E + m) * Matrix([[a[0, 0]], [a[1, 0]], [ksi[0, 0]], [ksi[1, 0]]]) def pslash(p): p1, p2, p3 = p p0 = sqrt(m**2 + p1**2 + p2**2 + p3**2) return gamma0*p0 - gamma1*p1 - gamma2*p2 - gamma3*p3 def Tr(M): return M.trace() def xprint(lhs, rhs): pprint( Eq(sympify(lhs), rhs ) ) def main(): a = Symbol("a", real=True) b = Symbol("b", real=True) c = Symbol("c", real=True) p = (a, b, c) assert u(p, 1).D * u(p, 2) == Matrix(1, 1, [0]) assert u(p, 2).D * u(p, 1) == Matrix(1, 1, [0]) p1, p2, p3 = [Symbol(x, real=True) for x in ["p1", "p2", "p3"]] pp1, pp2, pp3 = [Symbol(x, real=True) for x in ["pp1", "pp2", "pp3"]] k1, k2, k3 = [Symbol(x, real=True) for x in ["k1", "k2", "k3"]] kp1, kp2, kp3 = [Symbol(x, real=True) for x in ["kp1", "kp2", "kp3"]] p = (p1, p2, p3) pp = (pp1, pp2, pp3) k = (k1, k2, k3) kp = (kp1, kp2, kp3) mu = Symbol("mu") e = (pslash(p) + m*ones(4))*(pslash(k) - m*ones(4)) f = pslash(p) + m*ones(4) g = pslash(p) - m*ones(4) #pprint(e) xprint( 'Tr(f*g)', Tr(f*g) ) #print Tr(pslash(p) * pslash(k)).expand() M0 = [ ( v(pp, 1).D * mgamma(mu) * u(p, 1) ) * ( u(k, 1).D * mgamma(mu, True) * v(kp, 1) ) for mu in range(4)] M = M0[0] + M0[1] + M0[2] + M0[3] M = M[0] assert isinstance(M, Basic) #print M #print simplify(M) d = Symbol("d", real=True) # d=E+m xprint('M', M) print("-"*40) M = ((M.subs(E, d - m)).expand() * d**2 ).expand() xprint('M2', 1/(E + m)**2 * M) print("-"*40) x, y = M.as_real_imag() xprint('Re(M)', x) xprint('Im(M)', y) e = x**2 + y**2 xprint('abs(M)**2', e) print("-"*40) xprint('Expand(abs(M)**2)', e.expand()) #print Pauli(1)*Pauli(1) #print Pauli(1)**2 #print Pauli(1)*2*Pauli(1) if __name__ == "__main__": main() sympy-0.7.4.1/examples/README0000644000175000017500000000305312253362407015743 0ustar georgeskgeorgeskThis directory contains SymPy example programs. ------------------------------------------------------------------------------- DIRECTORY STRUCTURE The examples are broken up into three categories based on difficulty of both the mathematics and programming concepts. They roughly follow the following guide: beginner : Simple examples appropriate for first steps in using SymPy, for someone with little or no programming experience. intermediate : Demonstrations of more complex mathematical concepts, but still for someone with little programming experience. advanced : Larger demonstrations of advanced mathematical topics. ------------------------------------------------------------------------------- RUNNING EXAMPLES All the working examples can be run by executing the "all.py" script, use ./all.py -h for usage, if an example is known to be broken it will be commented out in this script. To run the individual examples one needs to have Python version >= 2.6 installed and SymPy must be in your PYTHONPATH environment variable. Most examples can be run from the command line python and the name of the example: aterrel@lilac:~/sympy/examples$ export PYTHONPATH=$PWD/..:$PYTHONPATH aterrel@lilac:~/sympy/examples$ python beginner/basic.py (3*a*b**2)**c Note: on most systems, the current directory is searched by Python automatically, so "python beginner/basic.py" works from the sympy root directory, however there are systems (Ubuntu Intrepid) where this doesn't work by default, unless you put "PYTHONPATH=." into your .bashrc for example. sympy-0.7.4.1/data/0000755000175000017500000000000012253362407014155 5ustar georgeskgeorgesksympy-0.7.4.1/data/Macports/0000755000175000017500000000000012253362407015745 5ustar georgeskgeorgesksympy-0.7.4.1/data/Macports/Portfile0000644000175000017500000000203012253362407017447 0ustar georgeskgeorgesk# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:ft=tcl:et:sw=4:ts=4:sts=4 # $Id$ PortSystem 1.0 PortGroup python25 1.0 name py-sympy version 0.7.2 categories python platforms darwin maintainers nomaintainer homepage http://sympy.org description SymPy is a Python library for symbolic mathematics long_description 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. master_sites googlecode:sympy distname sympy-${version} checksums md5 3847b5ed5c1531bf3c1b849dc2337253 \ sha1 b8609cc7e08d2883f96526ee8a4afdc90d77b911 \ rmd160 ba6f95435d72708a12e4c4285d82d5e51d38bce4 sympy-0.7.4.1/data/TeXmacs/0000755000175000017500000000000012253362407015521 5ustar georgeskgeorgesksympy-0.7.4.1/data/TeXmacs/bin/0000755000175000017500000000000012253362407016271 5ustar georgeskgeorgesksympy-0.7.4.1/data/TeXmacs/bin/tm_sympy0000755000175000017500000000445012253362407020103 0ustar georgeskgeorgesk#!/usr/bin/python # # TeXmacs interface for SymPy # # This plugin supports LaTeX printing of SymPy expressions # using sympy.printing.latex function. It handles pretty # printing of containers. If you wish to display raw # Python code, then use 'print' before an expression. # # TeXmacs encodes new-lines as spaces so we must use # heuristics to know where a multi-line expression is # broken. As a result you can't use more than one space # in a sequence. However you can and must indent your # expression using standard Pyhon rules. # # You can retrive the last output using '_' built-in # symbol. If the previous command did not generate # any ouput, it will be assigned with None. # # For a complete Python interface visit: # # http://dkbza.org/tmPython.html # import os import re import traceback from sympy import __version__ from sympy.printing import latex BEGIN, END = chr(2), chr(5) DATA_COMMAND = chr(16) DATA_ESCAPE = chr(27) def send(kind, output=None): if output is None: output = "" elif kind == "latex": output = latex(output) message = "%s:%s" % (kind, output) os.sys.stdout.write(BEGIN) os.sys.stdout.write(message) os.sys.stdout.write(END) os.sys.stdout.flush() send("verbatim", "Welcome to SymPy " + __version__) _globals = {} _init = \ """ from sympy import * _ = None x, y, z, t = symbols('x,y,z,t') k, i, m, n = symbols('k,i,m,n', integer=True) f = Function('f') Gamma, Zeta = gamma, zeta _greek = 'alpha beta gamma delta epsilon zeta eta ' \ 'theta iota kappa mu nu xi omicron pi rho ' \ 'sigma tau upsilon phi chi psi omega' for _symbol in _greek.split(' '): exec "%s = Symbol('%s')" % (_symbol, _symbol) del _symbol """ eval(compile(_init, 'tm_sympy', 'exec'), _globals) while True: line = os.sys.stdin.readline().strip() if not line: send("verbatim") elif line[0] != DATA_COMMAND: line = re.sub(r' {2}(\s*)', r'\n \1', line) try: output = eval(line, _globals) except SyntaxError: try: output = eval(compile(line, 'tm_sympy', 'exec'), _globals) except (SyntaxError, TypeError) : send("verbatim", traceback.format_exc(limit = 0)) continue _globals['_'] = output send("latex", output) sympy-0.7.4.1/data/TeXmacs/LICENSE0000644000175000017500000000300312253362407016522 0ustar georgeskgeorgeskCopyright (c) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 SymPy developers All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: a. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. b. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. c. Neither the name of the SymPy nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. sympy-0.7.4.1/data/TeXmacs/progs/0000755000175000017500000000000012253362407016653 5ustar georgeskgeorgesksympy-0.7.4.1/data/TeXmacs/progs/init-sympy.scm0000755000175000017500000000017512253362407021507 0ustar georgeskgeorgesk(plugin-configure sympy (:require (url-exists-in-path? "tm_sympy")) (:launch "tm_sympy --texmacs") (:session "SymPy")) sympy-0.7.4.1/data/IPython/0000755000175000017500000000000012253362407015547 5ustar georgeskgeorgesksympy-0.7.4.1/data/IPython/ipythonrc-sympy0000644000175000017500000000224712253362407020675 0ustar georgeskgeorgesk# -*- Mode: Shell-Script -*- Not really, but shows comments correctly #******************************************************************************* # Configuration file for ipython -- ipythonrc format # # The format of this file is one of 'key value' lines. # # Lines containing only whitespace at the beginning and then a '#' are # ignored as comments. But comments can NOT be put on lines with data. #******************************************************************************* # # This configuration file is a customization to turn IPython into a # very capable environment for high-end purely symbolic and numerical # work, using SymPy, similar to Mathematica, AXIOM etc. but with the # beauty and flexibility of the Python language. # # 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 exten- # sible. SymPy is written entirely in Python and does not require any # external libraries. # # For more information visit www.sympy.org # include ipythonrc banner 0 import_all sympy.interactive execute init_session() sympy-0.7.4.1/.gitattributes0000644000175000017500000000002412253362407016133 0ustar georgeskgeorgesk*.py diff=python sympy-0.7.4.1/.travis.yml0000644000175000017500000000505012253362407015355 0ustar georgeskgeorgesklanguage: python env: - TEST_DOCTESTS="true" - SPLIT="1/2" - SPLIT="2/2" python: - 2.6 - 2.7 - 3.2 - 3.3 - "pypy" matrix: include: - python: 2.7 env: - TEST_GMPY="true" - TEST_MATPLOTLIB="true" - TEST_DOCTESTS="true" - python: 2.7 env: - TEST_GMPY="true" - TEST_MATPLOTLIB="true" - SPLIT="1/3" - python: 2.7 env: - TEST_GMPY="true" - TEST_MATPLOTLIB="true" - SPLIT="2/3" - python: 2.7 env: - TEST_GMPY="true" - TEST_MATPLOTLIB="true" - SPLIT="3/3" - python: 3.3 env: - TEST_GMPY="true" - TEST_MATPLOTLIB="true" - TEST_DOCTESTS="true" - python: 3.3 env: - TEST_GMPY="true" - TEST_MATPLOTLIB="true" - SPLIT="1/3" - python: 3.3 env: - TEST_GMPY="true" - TEST_MATPLOTLIB="true" - SPLIT="2/3" - python: 3.3 env: - TEST_GMPY="true" - TEST_MATPLOTLIB="true" - SPLIT="3/3" - python: 2.7 env: TEST_SPHINX="true" - python: 2.7 env: TEST_SLOW="true" - python: 3.3 env: TEST_SLOW="true" - python: 2.7 env: - TEST_THEANO="true" - python: 3.3 env: - TEST_THEANO="true" - python: 3.3 # Dummy value env: - TEST_SAGE="true" allow_failures: - python: "pypy" before_install: - if [[ "${TEST_GMPY}" == "true" ]]; then sudo apt-get update; sudo apt-get install libgmp-dev; pip install "gmpy==1.16"; fi - if [[ "${TEST_SPHINX}" == "true" ]]; then sudo apt-get install --no-install-recommends graphviz inkscape texlive texlive-xetex texlive-fonts-recommended texlive-latex-extra lmodern; pip install "sphinx==1.1.3"; fi - if [[ "${TEST_MATPLOTLIB}" == "true" ]]; then pip install "numpy==1.7.1"; pip install "matplotlib==1.2.1"; fi - if [[ "${TEST_THEANO}" == "true" ]]; then sudo apt-get update; sudo apt-get install -qq libatlas-dev libatlas-base-dev liblapack-dev gfortran; pip install "numpy==1.7.1"; pip install "scipy==0.12.0"; pip install --no-deps https://github.com/Theano/Theano/archive/master.zip; fi - if [[ "${TEST_SAGE}" == "true" ]]; then sudo apt-add-repository -y ppa:aims/sagemath; sudo apt-get update; sudo apt-get install sagemath-upstream-binary; fi install: - if [[ "${TEST_SAGE}" != "true" ]]; then python setup.py install; fi script: - bin/test_travis.sh notifications: email: false sympy-0.7.4.1/MANIFEST.in0000644000175000017500000000032512253362407015002 0ustar georgeskgeorgeskrecursive-include data * recursive-include doc * prune doc/_build recursive-include examples *.py README include sympy/utilities/mathml/data/*.xsl include LICENSE include TODO include AUTHORS include README.rst sympy-0.7.4.1/bin/0000755000175000017500000000000012253362407014014 5ustar georgeskgeorgesksympy-0.7.4.1/bin/mailmap_update.py0000755000175000017500000000600012253362407017347 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: utf-8 -*- """ A tool to help keep .mailmap and AUTHORS up-to-date. """ # TODO: # - Check doc/src/aboutus.rst # - Make it easier to update .mailmap or AUTHORS with the correct entries. from __future__ import print_function import os import sys from fabric.api import local, env from fabric.colors import yellow, blue, green, red from fabric.utils import error mailmap_update_path = os.path.abspath(__file__) mailmap_update_dir = os.path.dirname(mailmap_update_path) sympy_top = os.path.split(mailmap_update_dir)[0] sympy_dir = os.path.join(sympy_top, 'sympy') if os.path.isdir(sympy_dir): sys.path.insert(0, sympy_top) from sympy.utilities.misc import filldedent try: # Only works in newer versions of fabric env.colorize_errors = True except AttributeError: pass git_command = 'git log --format="%aN <%aE>" | sort -u' git_people = unicode(local(git_command, capture=True), 'utf-8').strip().split("\n") with open(os.path.realpath(os.path.join(__file__, os.path.pardir, os.path.pardir, "AUTHORS"))) as fd: AUTHORS = unicode(fd.read(), 'utf-8') firstauthor = u"Ondřej Čertík" authors = AUTHORS[AUTHORS.find(firstauthor):].strip().split('\n') # People who don't want to be listed in AUTHORS authors_skip = ["Kirill Smelkov "] predate_git = 0 exit1 = False print(blue(filldedent("""Read the text at the top of AUTHORS and the text at the top of .mailmap for information on how to fix the below errors. If someone is missing from AUTHORS, add them where they would have been if they were added after their first pull request was merged (checkout the merge commit from the first pull request and see who is at the end of the AUTHORS file at that commit.)"""))) print() print(yellow("People who are in AUTHORS but not in git:")) print() for name in sorted(set(authors) - set(git_people)): if name.startswith("*"): # People who are in AUTHORS but predate git predate_git += 1 continue exit1 = True print(name) print() print(yellow("People who are in git but not in AUTHORS:")) print() for name in sorted(set(git_people) - set(authors) - set(authors_skip)): exit1 = True print(name) # + 1 because the last newline is stripped by strip() authors_count = AUTHORS[AUTHORS.find(firstauthor):].strip().count("\n") + 1 adjusted_authors_count = ( authors_count - predate_git + len(authors_skip) ) git_count = len(git_people) print() print(yellow("There are {git_count} people in git, and {adjusted_authors_count} " "(adjusted) people from AUTHORS".format(git_count=git_count, adjusted_authors_count=adjusted_authors_count))) if git_count != adjusted_authors_count: error("These two numbers are not the same!") else: print() print(green(filldedent("""Congratulations. The AUTHORS and .mailmap files appear to be up to date. You should now verify that doc/src/aboutus has %s people.""" % authors_count))) if exit1: print() print(red("There were errors. Please fix them.")) sys.exit(1) sympy-0.7.4.1/bin/test_isolated0000755000175000017500000000736112253362407016614 0ustar georgeskgeorgesk#!/usr/bin/env python """ Generates a bash script, that executes py.test or nosetest on each file individually. Usage and help: $ bin/test_isolated -h and read the instructions. """ from __future__ import print_function from os import chmod, getcwd from glob import glob from optparse import OptionParser from stat import S_IREAD, S_IWRITE, S_IXUSR, S_IRGRP, S_IROTH, S_IXGRP, S_IXOTH filemode = S_IREAD | S_IWRITE | S_IXUSR | S_IRGRP | S_IROTH | S_IXGRP | S_IXOTH def get_paths(level=6): """ Generates a set of paths for testfiles searching. Example: >>> get_paths(2) ['sympy/test_*.py', 'sympy/*/test_*.py', 'sympy/*/*/test_*.py'] >>> get_paths(6) ['sympy/test_*.py', 'sympy/*/test_*.py', 'sympy/*/*/test_*.py', 'sympy/*/*/*/test_*.py', 'sympy/*/*/*/*/test_*.py', 'sympy/*/*/*/*/*/test_*.py', 'sympy/*/*/*/*/*/*/test_*.py'] """ wildcards = ["/"] for i in range(level): wildcards.append(wildcards[-1] + "*/") my_dir = getcwd() p = [my_dir + "/sympy" + x + "test_*.py" for x in wildcards] return p def generate_test_script1(testlib="py.test"): """Generates a bash script for doing the test. "testlib" is the name of the executable, that is going to execute the test, for example "py.test" or "nosetests". """ g = [] for x in get_paths(10): g.extend(glob(x)) with open("/tmp/test_sympy.sh", "w") as f: f.write("#! /bin/sh\n") f.write("# Autogenerated script for a reliable test of SymPy.\n") f.write("# Execute with 'sh test_sympy.sh' (in any directory).\n\n") for x in g: f.write(testlib + " " + x + "\n") chmod(f.name, filemode) def generate_test_script2(testlib="nosetests"): """Generates a bash script for doing the test. "testlib" is the name of the executable, that is going to execute the test, for example "py.test" or "nosetests". """ g = [] for x in get_paths(10): g.extend(glob(x)) with open("/tmp/test_sympy.sh", "w") as f: f.write("#! /bin/sh\n") f.write("# Autogenerated script for a reliable test of SymPy.\n") f.write("# Execute with 'sh test_sympy.sh' (in any directory).\n\n") for x in g: f.write(testlib + " " + x + " && \\\n") f.write("echo 'all tests passed, ok to commit'") chmod(f.name, filemode) usage = """%prog [options] Generates a bash script, that executes py.test or nosetest on each file individually. Usage: $ bin/test_isolated Generating py.test isolated testsuite... Done. Run (search for 'COMMIT' in the less environment): /tmp/test_sympy.sh | less $ /tmp/test_sympy.sh | less Let the tests run and then search if any test failed (it will write DO NOT COMMIT), so search for COMMIT.""" def main(): parser = OptionParser(usage=usage) parser.add_option("-p", "--py.test", action="store_false", dest="nosetests", help="Use py.test (default)") parser.add_option("-n", "--nosetests", action="store_true", dest="nosetests", help="Use nosetests") parser.add_option("-q", "--quiet", action="store_false", dest="verbose") parser.set_defaults(nosetests=False, verbose=True) options, args = parser.parse_args() if len(args) != 0: parser.error("too many arguments") if options.nosetests: if options.verbose: print "Generating nosetests isolated testsuite..." generate_test_script2("nosetests") else: if options.verbose: print "Generating py.test isolated testsuite..." generate_test_script1("py.test") if options.verbose: print "Done. Run (search for 'COMMIT' in the less environment):" print "/tmp/test_sympy.sh | less" if __name__ == "__main__": main() sympy-0.7.4.1/bin/adapt_paths.py0000644000175000017500000000215112253362407016655 0ustar georgeskgeorgesk""" This script adapts import statements in sympy/mpmath/tests to work properly. We want to have this fully automatic, so that we don't have to do it by hand each time we copy files from mpmath to sympy. Usage: $ python bin/adapt_paths.py > /tmp/x $ patch -p0 < /tmp/x You can use the "--dry-run" parameter to 'patch' to see if all is ok and you should also inspect the /tmp/x that all the changes generated are actually correct. """ from __future__ import print_function from glob import glob import re import difflib def get_files_mpmath(): return glob("sympy/mpmath/tests/test_*.py") def fix_file(filename): with open(filename) as f: orig = f.read() # This converts stuff like "mpmath.dps -> sympy.mpmath.dps", but will leave # "sympy.mpmath.dps" untouched. s = re.sub("(?>>' in obj.__doc__: add_mdt = True elif _is_indirect(name, obj.__doc__): add_idt = True else: f_doctest = True function = True if sphinx: in_sphinx = find_sphinx(obj_name, mod_path) if add_md or add_mdt or add_idt or not in_sphinx: try: line_no = inspect.getsourcelines(obj)[1] except IOError: # Raised when source does not exist # which means the function is not there. return False, False full_name = "LINE %d: %s" % (line_no, full_name) if add_md: f_md.append(full_name) elif add_mdt: f_mdt.append(full_name) elif add_idt: f_idt.append(full_name) if not in_sphinx: sph.append(full_name) return f_doctest, function def process_class(c_name, obj, c_sk, c_md, c_mdt, c_idt, c_has_doctest, mod_path, sph, sphinx=True): """ Extracts information about the class regarding documentation. It is assumed that the function calling this subroutine has already checked that the class is valid. """ # Skip class case if c_name.startswith('_'): c_sk.append(c_name) return False, False, None c = False c_dt = False # Get the line number of class try: source, line_no = inspect.getsourcelines(obj) except IOError: # Raised when source does not exist # which means the class is not there. return False, False, None c = True full_name = "LINE %d: %s" % (line_no, c_name) if not obj.__doc__: c_md.append(full_name) elif not '>>>' in obj.__doc__: c_mdt.append(full_name) elif _is_indirect(c_name, obj.__doc__): c_idt.append(full_name) else: c_dt = True c_has_doctest.append(full_name) in_sphinx = False if sphinx: in_sphinx = find_sphinx(c_name, mod_path) if not in_sphinx: sph.append(full_name) return c_dt, c, source def coverage(module_path, verbose=False, no_color=False, sphinx=True): """ Given a module path, builds an index of all classes and functions contained. It then goes through each of the classes/functions to get the docstring and doctest coverage of the module. """ # Import the package and find membmers m = None try: __import__(module_path) m = sys.modules[module_path] except Exception as a: # Most likely cause, absence of __init__ print("%s could not be loaded due to %s." % (module_path, repr(a))) return 0, 0, 0 c_skipped = [] c_md = [] c_mdt = [] c_has_doctest = [] c_idt = [] classes = 0 c_doctests = 0 c_sph = [] f_skipped = [] f_md = [] f_mdt = [] f_has_doctest = [] f_idt = [] functions = 0 f_doctests = 0 f_sph = [] skip_members = ['__abstractmethods__'] # Get the list of members m_members = dir(m) for member in m_members: # Check for skipped functions first, they throw nasty errors # when combined with getattr if member in skip_members: continue # Identify if the member (class/def) a part of this module obj = getattr(m, member) obj_mod = inspect.getmodule(obj) # Function not a part of this module if not obj_mod or not obj_mod.__name__ == module_path: continue # If it's a function if inspect.isfunction(obj) or inspect.ismethod(obj): f_dt, f = process_function(member, '', obj, module_path, f_skipped, f_md, f_mdt, f_idt, f_has_doctest, skip_members, f_sph, sphinx=sphinx) if f: functions += 1 if f_dt: f_doctests += 1 # If it's a class, look at it's methods too elif inspect.isclass(obj): # Process the class first c_dt, c, source = process_class(member, obj, c_skipped, c_md, c_mdt, c_idt, c_has_doctest, module_path, c_sph, sphinx=sphinx) if not c: continue else: classes += 1 if c_dt: c_doctests += 1 # Iterate through it's members for f_name in obj.__dict__: if f_name in skip_members or f_name.startswith('_'): continue # Check if def funcname appears in source if not ("def " + f_name) in ' '.join(source): continue # Identify the module of the current class member f_obj = getattr(obj, f_name) obj_mod = inspect.getmodule(f_obj) # Function not a part of this module if not obj_mod or not obj_mod.__name__ == module_path: continue # If it's a function if inspect.isfunction(f_obj) or inspect.ismethod(f_obj): f_dt, f = process_function(f_name, member, obj, module_path, f_skipped, f_md, f_mdt, f_idt, f_has_doctest, skip_members, f_sph, sphinx=sphinx) if f: functions += 1 if f_dt: f_doctests += 1 # Evaluate the percent coverage total_doctests = c_doctests + f_doctests total_members = classes + functions if total_members: score = 100 * float(total_doctests) / (total_members) else: score = 100 score = int(score) if sphinx: total_sphinx = len(c_sph) + len(f_sph) if total_members: sphinx_score = 100 - 100 * float(total_sphinx) / total_members else: sphinx_score = 100 sphinx_score = int(sphinx_score) else: total_sphinx = 0 sphinx_score = 0 # Sort functions/classes by line number c_md = sorted(c_md, key=lambda x: int(x.split()[1][:-1])) c_mdt = sorted(c_mdt, key=lambda x: int(x.split()[1][:-1])) c_idt = sorted(c_idt, key=lambda x: int(x.split()[1][:-1])) f_md = sorted(f_md, key=lambda x: int(x.split()[1][:-1])) f_mdt = sorted(f_mdt, key=lambda x: int(x.split()[1][:-1])) f_idt = sorted(f_idt, key=lambda x: int(x.split()[1][:-1])) print_coverage(module_path, classes, c_md, c_mdt, c_idt, c_sph, functions, f_md, f_mdt, f_idt, f_sph, score, total_doctests, total_members, sphinx_score, total_sphinx, verbose=verbose, no_color=no_color, sphinx=sphinx) return total_doctests, total_sphinx, total_members def go(sympy_top, file, verbose=False, no_color=False, exact=True, sphinx=True): if os.path.isdir(file): doctests, total_sphinx, num_functions = 0, 0, 0 for F in os.listdir(file): _doctests, _total_sphinx, _num_functions = go(sympy_top, '%s/%s' % (file, F), verbose=verbose, no_color=no_color, exact=False, sphinx=sphinx) doctests += _doctests total_sphinx += _total_sphinx num_functions += _num_functions return doctests, total_sphinx, num_functions if (not (file.endswith('.py') or file.endswith('.pyx')) or file.endswith('__init__.py') or not exact and ('test_' in file or 'bench_' in file or any(name in file for name in skip_paths))): return 0, 0, 0 if not os.path.exists(file): print("File(%s does not exist." % file) sys.exit(1) # Relpath for constructing the module name return coverage(get_mod_name(file, sympy_top), verbose=verbose, no_color=no_color, sphinx=sphinx) if __name__ == "__main__": bintest_dir = os.path.abspath(os.path.dirname(__file__)) # bin/cover... sympy_top = os.path.split(bintest_dir)[0] # ../ sympy_dir = os.path.join(sympy_top, 'sympy') # ../sympy/ if os.path.isdir(sympy_dir): sys.path.insert(0, sympy_top) skip_paths = ['mpmath'] usage = "usage: ./bin/doctest_coverage.py PATHS" parser = ArgumentParser( description=__doc__, usage=usage, formatter_class=RawDescriptionHelpFormatter, ) parser.add_argument("path", nargs='*', default=[os.path.join(sympy_top, 'sympy')]) parser.add_argument("-v", "--verbose", action="store_true", dest="verbose", default=False) parser.add_argument("--no-colors", action="store_true", dest="no_color", help="use no colors", default=False) parser.add_argument("--no-sphinx", action="store_false", dest="sphinx", help="don't report Sphinx coverage", default=True) args = parser.parse_args() if args.sphinx and not os.path.exists(os.path.join(sympy_top, 'doc', '_build', 'html')): print(""" Cannot check Sphinx coverage without a documentation build. To build the docs, run "cd doc; make html". To skip checking Sphinx coverage, pass --no-sphinx. """) sys.exit(1) full_coverage = True for file in args.path: file = os.path.normpath(file) print('DOCTEST COVERAGE for %s' % (file)) print('='*70) print() doctests, total_sphinx, num_functions = go(sympy_top, file, verbose=args.verbose, no_color=args.no_color, sphinx=args.sphinx) if num_functions == 0: score = 100 sphinx_score = 100 else: score = 100 * float(doctests) / num_functions score = int(score) if doctests < num_functions: full_coverage = False if args.sphinx: sphinx_score = 100 - 100 * float(total_sphinx) / num_functions sphinx_score = int(sphinx_score) if total_sphinx > 0: full_coverage = False print() print('='*70) if args.no_color: print("TOTAL DOCTEST SCORE for %s: %s%% (%s of %s)" % \ (get_mod_name(file, sympy_top), score, doctests, num_functions)) elif score < 100: print("TOTAL DOCTEST SCORE for %s: %s%s%% (%s of %s)%s" % \ (get_mod_name(file, sympy_top), c_color % (colors["Red"]), score, doctests, num_functions, c_normal)) else: print("TOTAL DOCTEST SCORE for %s: %s%s%% (%s of %s)%s" % \ (get_mod_name(file, sympy_top), c_color % (colors["Green"]), score, doctests, num_functions, c_normal)) if args.sphinx: if args.no_color: print("TOTAL SPHINX SCORE for %s: %s%% (%s of %s)" % \ (get_mod_name(file, sympy_top), sphinx_score, num_functions - total_sphinx, num_functions)) elif sphinx_score < 100: print("TOTAL SPHINX SCORE for %s: %s%s%% (%s of %s)%s" % \ (get_mod_name(file, sympy_top), c_color % (colors["Red"]), sphinx_score, num_functions - total_sphinx, num_functions, c_normal)) else: print("TOTAL SPHINX SCORE for %s: %s%s%% (%s of %s)%s" % \ (get_mod_name(file, sympy_top), c_color % (colors["Green"]), sphinx_score, num_functions - total_sphinx, num_functions, c_normal)) print() sys.exit(not full_coverage) sympy-0.7.4.1/bin/test_import.py0000644000175000017500000000025712253362407016743 0ustar georgeskgeorgeskfrom __future__ import print_function from timeit import default_timer as clock from get_sympy import path_hack path_hack() t = clock() import sympy t = clock() - t print(t) sympy-0.7.4.1/bin/test0000755000175000017500000000733112253362407014725 0ustar georgeskgeorgesk#!/usr/bin/env python """ Program to execute tests using the py.test like interface. The advantage over py.test is that it only depends on sympy and should just work in any circumstances. See "sympy.test?" for documentation. """ from __future__ import print_function import sys import os from optparse import OptionParser import re from get_sympy import path_hack path_hack() parser = OptionParser() parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False) parser.add_option("--pdb", action="store_true", dest="pdb", default=False, help="Run post mortem pdb on each failure") parser.add_option("--no-colors", action="store_false", dest="colors", default=True, help="Do not report colored [OK] and [FAIL]") parser.add_option("--force-colors", action="store_true", dest="force_colors", default=False, help="Always use colors, even if the output is not to a terminal.") parser.add_option("-k", dest="kw", help="only run tests matching the given keyword expression", metavar="KEYWORD", default="") parser.add_option("--tb", dest="tb", help="traceback verboseness (short/no) [default: %default]", metavar="TBSTYLE", default="short") parser.add_option("--random", action="store_false", dest="sort", default=True, help="Run tests in random order instead of sorting them") parser.add_option("--seed", dest="seed", type="int", help="use this seed for randomized tests", metavar="SEED") parser.add_option('-t', '--types', dest='types', action='store', default=None, choices=['gmpy', 'gmpy1', 'python'], help='setup ground types: gmpy | gmpy1 | python') parser.add_option('-C', '--no-cache', dest='cache', action='store_false', default=True, help='disable caching mechanism') parser.add_option("--timeout", action="store", dest="timeout", default=False, help="Set a timeout for the all functions, in seconds. By default there is no timeout.", type='int') parser.add_option("--slow", action="store_true", dest="slow", default=False, help="Run only the slow functions.") parser.add_option("--no-subprocess", action="store_false", dest="subprocess", default=True, help="Don't run the tests in a separate " "subprocess. This may prevent hash randomization from being enabled.") parser.add_option("-E", "--enhance-asserts", action="store_true", dest="enhance_asserts", default=False, help="Rewrite assert statements to give more useful error messages.") parser.add_option('--split', action="store", type='str', default=None, help="Only run part of the tests. Should be of the form a/b, e.g., 1/2") parser.set_usage("test [options ...] [tests ...]") parser.epilog = """\ "options" are any of the options above. "tests" are 0 or more glob strings of \ tests to run. If no test arguments are given, all tests will be run.\ """ options, args = parser.parse_args() # Check this again here to give a better error message if options.split: sp = re.compile(r'([0-9]+)/([1-9][0-9]*)') if not sp.match(options.split): parser.error("option --split: must be of the form a/b where a and b " "are integers, not %r" % options.split) if not options.cache: os.environ['SYMPY_USE_CACHE'] = 'no' if options.types: os.environ['SYMPY_GROUND_TYPES'] = options.types import sympy ok = sympy.test(*args, verbose=options.verbose, kw=options.kw, tb=options.tb, pdb=options.pdb, colors=options.colors, force_colors=options.force_colors, sort=options.sort, seed=options.seed, slow=options.slow, timeout=options.timeout, subprocess=options.subprocess, enhance_asserts=options.enhance_asserts, split=options.split) if ok: sys.exit(0) else: sys.exit(1) sympy-0.7.4.1/bin/test_import0000755000175000017500000000134612253362407016317 0ustar georgeskgeorgesk#!/usr/bin/env python """ Tests the speed of "import sympy" by measuring it many times in a row and averaging the values. Usage: $ bin/test_import """ from __future__ import print_function n_tests = 50 from pexpect import run from numpy import mean, std from get_sympy import path_hack def test(): t = run("python bin/test_import.py", cwd=path_hack()) t = float(t) return t tests = [test() for x in range(n_tests + 1)] print("Note: the first run (warm up) was not included in the average + std dev") print("All runs (including warm up):") print(tests) # skip the first run (warm up): tests = tests[1:] print("Number of tests: %d" % (n_tests)) print('The speed of "import sympy" is: %f +- %f' % (mean(tests), std(tests))) sympy-0.7.4.1/bin/isympy0000755000175000017500000002706612253362407015307 0ustar georgeskgeorgesk#!/usr/bin/env python # XXX: Don't put a newline here, or it will add an extra line with # isympy --help # | # v """Python shell for SymPy. This is just a normal Python shell (IPython shell if you have the IPython package installed), that executes the following commands for the user: >>> from __future__ import division >>> 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) So starting 'isympy' is equivalent to starting Python (or IPython) and executing the above commands by hand. It is intended for easy and quick experimentation with SymPy. isympy is a good way to use SymPy as an interactive calculator. If you have IPython and Matplotlib installed, then interactive plotting is enabled by default. COMMAND LINE OPTIONS -------------------- -c CONSOLE, --console=CONSOLE Use the specified shell (Python or IPython) shell as the console backend instead of the default one (IPython if present, Python otherwise), e.g.: $isympy -c python CONSOLE must be one of 'ipython' or 'python' -p PRETTY, --pretty PRETTY Setup pretty-printing in SymPy. When pretty-printing is enabled, expressions can be printed with Unicode or ASCII. The default is to use pretty-printing (with Unicode if the terminal supports it). When this option is 'no', expressions will not be pretty-printed and ASCII will be used: $isympy -p no PRETTY must be one of 'unicode', 'ascii', or 'no' -t TYPES, --types=TYPES Setup the ground types for the polys. By default, gmpy ground types are used if gmpy2 or gmpy is installed, otherwise it falls back to python ground types, which are a little bit slower. You can manually choose python ground types even if gmpy is installed (e.g., for testing purposes): $isympy -t python TYPES must be one of 'gmpy', 'gmpy1' or 'python' Note that the ground type gmpy1 is primarily intended for testing; it forces the use of gmpy version 1 even if gmpy2 is available. This is the same as setting the environment variable SYMPY_GROUND_TYPES to the given ground type (e.g., SYMPY_GROUND_TYPES='gmpy') The ground types can be determined interactively from the variable sympy.polys.domains.GROUND_TYPES. -o ORDER, --order ORDER Setup the ordering of terms for printing. The default is lex, which orders terms lexicographically (e.g., x**2 + x + 1). You can choose other orderings, such as rev-lex, which will use reverse lexicographic ordering (e.g., 1 + x + x**2): $isympy -o rev-lex ORDER must be one of 'lex', 'rev-lex', 'grlex', 'rev-grlex', 'grevlex', 'rev-grevlex', 'old', or 'none'. Note that for very large expressions, ORDER='none' may speed up printing considerably but the terms will have no canonical order. -q, --quiet Print only Python's and SymPy's versions to stdout at startup. -d, --doctest Use the same format that should be used for doctests. This is equivalent to -c python -p no. -C, --no-cache Disable the caching mechanism. Disabling the cache may slow certain operations down considerably. This is useful for testing the cache, or for benchmarking, as the cache can result in deceptive timings. This is equivalent to setting the environment variable SYMPY_USE_CACHE to 'no'. -a, --auto-symbols (requires at least IPython 0.11) Automatically create missing symbols. Normally, typing a name of a Symbol that has not been instantiated first would raise NameError, but with this option enabled, any undefined name will be automatically created as a Symbol. Note that this is intended only for interactive, calculator style usage. In a script that uses SymPy, Symbols should be instantiated at the top, so that it's clear what they are. This will not override any names that are already defined, which includes the single character letters represented by the mnemonic QCOSINE (see the "Gotchas and Pitfalls" document in the documentation). You can delete existing names by executing "del name". If a name is defined, typing "'name' in dir()" will return True. The Symbols that are created using this have default assumptions. If you want to place assumptions on symbols, you should create them using symbols() or var(). Finally, this only works in the top level namespace. So, for example, if you define a function in isympy with an undefined Symbol, it will not work. See also the -i and -I options. -i, --int-to-Integer (requires at least IPython 0.11) Automatically wrap int literals with Integer. This makes it so that things like 1/2 will come out as Rational(1, 2), rather than 0.5. This works by preprocessing the source and wrapping all int literals with Integer. Note that this will not change the behavior of int literals assigned to variables, and it also won't change the behavior of functions that return int literals. If you want an int, you can wrap the literal in int(), e.g. int(3)/int(2) gives 1.5 (with division imported from __future__). -I, --interactive (requires at least IPython 0.11) This is equivalent to --auto-symbols --int-to-Integer. Future options designed for ease of interactive use may be added to this. -D, --debug Enable debugging output. This is the same as setting the environment variable SYMPY_DEBUG to 'True'. The debug status is set in the variable SYMPY_DEBUG within isympy. -- IPython options Additionally you can pass command line options directly to the IPython interpreter (the standard Python shell is not supported). However you need to add the '--' separator between two types of options, e.g the startup banner option and the colors option. You need to enter the options as required by the version of IPython that you are using, too: in IPython 0.11, $isympy -q -- --colors=NoColor or older versions of IPython, $isympy -q -- -colors NoColor See also isympy --help. """ import os import sys import warnings # hook in-tree SymPy into Python path, if possible isympy_path = os.path.abspath(__file__) isympy_dir = os.path.dirname(isympy_path) sympy_top = os.path.split(isympy_dir)[0] sympy_dir = os.path.join(sympy_top, 'sympy') if os.path.isdir(sympy_dir): sys.path.insert(0, sympy_top) # DO NOT IMPORT SYMPY HERE! def main(): from optparse import OptionParser if '-h' in sys.argv or '--help' in sys.argv: # XXX: We can't use description=__doc__ in the OptionParser call # below because optparse line wraps it weird. The argparse module # allows you to disable this, though, but it's only available in # Python 2.7+. print(__doc__) # the docstring of this module above VERSION = None if '--version' in sys.argv: # We cannot import sympy before this is run, because flags like -C and # -t set environment variables that must be set before SymPy is # imported. The only thing we need to import it for is to get the # version, which only matters with the --version flag. import sympy VERSION = sympy.__version__ usage = 'usage: isympy [options] -- [ipython options]' parser = OptionParser( usage=usage, version=VERSION, # XXX: We need a more centralized place to store the version. # It is currently stored in sympy.__version__, but we can't yet # import sympy at this point. ) parser.add_option( '-c', '--console', dest='console', action='store', default=None, choices=['ipython', 'python'], help='select type of interactive session: ipython | python; defaults ' 'to ipython if IPython is installed, otherwise python') parser.add_option( '-p', '--pretty', dest='pretty', action='store', default=None, choices=['unicode', 'ascii', 'no'], help='setup pretty printing: unicode | ascii | no; defaults to ' 'unicode printing if the terminal supports it, otherwise ascii') parser.add_option( '-t', '--types', dest='types', action='store', default=None, choices=['gmpy', 'gmpy1', 'python'], help='setup ground types: gmpy | gmpy1 | python; defaults to gmpy if gmpy2 ' 'or gmpy is installed, otherwise python') parser.add_option( '-o', '--order', dest='order', action='store', default=None, choices=['lex', 'grlex', 'grevlex', 'rev-lex', 'rev-grlex', 'rev-grevlex', 'old', 'none'], help='setup ordering of terms: [rev-]lex | [rev-]grlex | [rev-]grevlex | old | none; defaults to lex') parser.add_option( '-q', '--quiet', dest='quiet', action='store_true', default=False, help='print only version information at startup') parser.add_option( '-d', '--doctest', dest='doctest', action='store_true', default=False, help='use the doctest format for output (you can just copy and paste it)') parser.add_option( '-C', '--no-cache', dest='cache', action='store_false', default=True, help='disable caching mechanism') parser.add_option( '-a', '--auto-symbols', dest='auto_symbols', action='store_true', default=False, help='automatically construct missing symbols') parser.add_option( '-i', '--int-to-Integer', dest='auto_int_to_Integer', action='store_true', default=False, help="automatically wrap int literals with Integer") parser.add_option( '-I', '--interactive', dest='interactive', action='store_true', default=False, help="equivalent to -a -i") parser.add_option( '-D', '--debug', dest='debug', action='store_true', default=False, help='enable debugging output') (options, ipy_args) = parser.parse_args() if not options.cache: os.environ['SYMPY_USE_CACHE'] = 'no' if options.types: os.environ['SYMPY_GROUND_TYPES'] = options.types if options.debug: os.environ['SYMPY_DEBUG'] = str(options.debug) if options.doctest: options.pretty = 'no' options.console = 'python' session = options.console if session is not None: ipython = session == 'ipython' else: try: import IPython ipython = True except ImportError: if not options.quiet: from sympy.interactive.session import no_ipython print(no_ipython) ipython = False args = { 'pretty_print': True, 'use_unicode': None, 'use_latex': None, 'order': None, 'argv': ipy_args, } if options.pretty == 'unicode': args['use_unicode'] = True elif options.pretty == 'ascii': args['use_unicode'] = False elif options.pretty == 'no': args['pretty_print'] = False if options.order is not None: args['order'] = options.order args['quiet'] = options.quiet args['auto_symbols'] = options.auto_symbols or options.interactive args['auto_int_to_Integer'] = options.auto_int_to_Integer or options.interactive from sympy.utilities.exceptions import SymPyDeprecationWarning warnings.simplefilter("always", SymPyDeprecationWarning) from sympy.interactive import init_session init_session(ipython, **args) if __name__ == "__main__": main() sympy-0.7.4.1/bin/strip_whitespace0000755000175000017500000000572612253362407017331 0ustar georgeskgeorgesk#!/usr/bin/env python from __future__ import print_function import os def strip_file(filename, write, report): # .readlines() retains \n chars, while .read().splitlines() does not. # Assuming that whitespace errors will be few, we will thus only need # to re-add \n to a few right-stripped lines. The hit flag will keep us # from unecessarily re-writing files with no changes. # without "b" all lines appear as \n terminated with open(filename, 'rb') as f: lines = f.readlines() hit = False cr = False extra = 0 for index, line in enumerate(lines): if line.endswith(" \n"): if report: print("%s, line %s" % (filename, index + 1)) if write: lines[index] = line.rstrip() + "\n" hit = True if line.endswith("\r\n"): if report and not cr: print("%s, line %s (crlf now silent)" % (filename, index + 1)) cr = True if write: lines[index] = line.rstrip() + "\n" hit = True # correct no newline at eof if lines and not lines[-1].endswith("\n"): lines[-1] += "\n" if report: print("%s, no newline at eof" % filename) if write: hit = True # correct multiple newlines at eof while len(lines) > 1 and lines[-1] == "\n" and lines[-2].endswith("\n"): if write: hit = True lines.pop() extra += 1 if extra > 0 and report: print("%s, %d extra newlines at eof" % (filename, extra)) if write and hit: # without "b" the lines may be written in sys-dep format with open(filename, "wb") as f: f.writelines(lines) def go(path, write, report): allowed_ext = [ ".cpp", ".cc", ".h", ".py", ] for root, dirs, files in os.walk(path): if ".git" in dirs: dirs.remove(".git") for file in files: if os.path.splitext(file)[1] not in allowed_ext: continue filename = os.path.join(root, file) strip_file(filename, write, report) def main(): from optparse import OptionParser p = OptionParser("usage: %prog [options] filename") p.add_option("-d", "--dry", action="store_true", dest="dry", help="Do not modify files.") p.add_option("-v", "--verbose", action="store_true", dest="verbose", help="Report all changes.") p.add_option("-r", "--recursive", action="store_true", dest="recursive", help="Recursively correct all files in a directory.") options, args = p.parse_args() if options.dry: options.verbose = True if len(args) == 1: if options.recursive: go(args[0], not options.dry, options.verbose) else: strip_file(args[0], not options.dry, options.verbose) else: p.print_help() if __name__ == "__main__": main() sympy-0.7.4.1/bin/get_sympy.py0000644000175000017500000000063612253362407016413 0ustar georgeskgeorgesk"""Functions to get the correct sympy version to run tests.""" from __future__ import print_function import os import sys def path_hack(): """ Hack sys.path to import correct (local) sympy. """ this_file = os.path.abspath(__file__) sympy_dir = os.path.join(os.path.dirname(this_file), "..") sympy_dir = os.path.normpath(sympy_dir) sys.path.insert(0, sympy_dir) return sympy_dir sympy-0.7.4.1/bin/doctest0000755000175000017500000000533112253362407015411 0ustar georgeskgeorgesk#!/usr/bin/env python """ Program to execute doctests using the py.test like interface. The advantage over py.test is that it only depends on sympy and should just work in any circumstances. See "sympy.dotest?" for documentation. """ from __future__ import print_function # files listed here can be in unix forward slash format with paths # listed relative to sympy (which contains bin, etc...) blacklist = [] import sys import os from optparse import OptionParser import re from get_sympy import path_hack path_hack() parser = OptionParser() parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False) # if you don't see a -n `default=False`; # if you do see a -n `store_true` means to store a True value for it; # dest is where in options to put it, options.normal will hold the bool; # when the user enters -h or --help, print the `help` text parser.add_option("-n", "--normal", action="store_true", dest="normal", help="run normal doctests; do not require explicit imports", default=False) parser.add_option('-t', '--types', dest='types', action='store', default=None, choices=['gmpy', 'gmpy1', 'python'], help='setup ground types: gmpy | gmpy1 | python') parser.add_option('-C', '--no-cache', dest='cache', action='store_false', default=True, help='disable caching mechanism') parser.add_option("--no-subprocess", action="store_false", dest="subprocess", default=True, help="Don't run the tests in a separate " "subprocess. This may prevent hash randomization from being enabled.") parser.add_option('--split', action="store", type='str', default=None, help="Only run part of the doctests. Should be of the form a/b, e.g., 1/2") parser.set_usage("test [options ...] [files ...]") parser.epilog = """\ "options" are any of the options above. "files" are 0 or more glob strings of \ files to run doctests on. If no file arguments are given, all doctests will be \ run. This program runs both doctests in the source and doctests in the Sphinx \ documentation (doc/src/ directory).\ """ options, args = parser.parse_args() # Check this again here to give a better error message if options.split: sp = re.compile(r'([0-9]+)/([1-9][0-9]*)') if not sp.match(options.split): parser.error("option --split: must be of the form a/b where a and b " "are integers, not %r" % options.split) if not options.cache: os.environ['SYMPY_USE_CACHE'] = 'no' if options.types: os.environ['SYMPY_GROUND_TYPES'] = options.types import sympy ok = sympy.doctest(*args, verbose=options.verbose, blacklist=blacklist, normal=options.normal, subprocess=options.subprocess, split=options.split) if ok: sys.exit(0) else: sys.exit(1) sympy-0.7.4.1/bin/coverage_report.py0000755000175000017500000000661112253362407017563 0ustar georgeskgeorgesk#!/usr/bin/env python """ Script to generate test coverage reports. Usage: $ bin/coverage_report.py This will create a directory covhtml with the coverage reports. To restrict the analysis to a directory, you just need to pass its name as argument. For example: $ bin/coverage_report.py sympy/logic runs only the tests in sympy/logic/ and reports only on the modules in sympy/logic/. You can also get a report on the parts of the whole sympy code covered by the tests in sympy/logic/ by following up the previous command with $ bin/coverage_report.py -c """ from __future__ import print_function import os import re import sys from optparse import OptionParser minver = '3.4' try: import coverage if coverage.__version__ < minver: raise ImportError except ImportError: print( "You need to install module coverage (version %s or newer required).\n" "See http://nedbatchelder.com/code/coverage/ or \n" "https://launchpad.net/ubuntu/+source/python-coverage/" % minver) sys.exit(-1) REPORT_DIR = "covhtml" REFRESH = False omit_dir_patterns = ['.*tests', 'benchmark', 'examples', 'mpmath', 'pyglet', 'test_external'] omit_dir_re = re.compile(r'|'.join(omit_dir_patterns)) source_re = re.compile(r'.*\.py$') def generate_covered_files(top_dir): for dirpath, dirnames, filenames in os.walk(top_dir): omit_dirs = [dirn for dirn in dirnames if omit_dir_re.match(dirn)] for x in omit_dirs: dirnames.remove(x) for filename in filenames: if source_re.match(filename): yield os.path.join(dirpath, filename) def make_report(source_dir, report_dir, use_cache=False): #code adapted from /bin/test bin_dir = os.path.abspath(os.path.dirname(__file__)) # bin/ sympy_top = os.path.split(bin_dir)[0] # ../ sympy_dir = os.path.join(sympy_top, 'sympy') # ../sympy/ if os.path.isdir(sympy_dir): sys.path.insert(0, sympy_top) os.chdir(sympy_top) cov = coverage.coverage() cov.exclude("raise NotImplementedError") cov.exclude("def canonize") # this should be "@decorated" cov.exclude("def __mathml__") if use_cache: cov.load() else: cov.erase() cov.start() import sympy sympy.test(source_dir, subprocess=False) #sympy.doctest() #coverage doesn't play well with doctests cov.stop() cov.save() covered_files = list(generate_covered_files(source_dir)) if report_dir in os.listdir(os.curdir): for f in os.listdir(report_dir): if f.split('.')[-1] in ['html', 'css', 'js']: os.remove(os.path.join(report_dir, f)) cov.html_report(morfs=covered_files, directory=report_dir) if __name__ == '__main__': parser = OptionParser() parser.add_option('-c', '--use-cache', action='store_true', default=False, help='Use cached data.') parser.add_option('-d', '--report-dir', default='covhtml', help='Directory to put the generated report in.') options, args = parser.parse_args() if args: source_dir = args[0] else: source_dir = 'sympy/' make_report(source_dir, **options.__dict__) print("The generated coverage report is in covhtml directory.") print("Open %s in your web browser to view the report" % os.sep.join( 'sympy covhtml index.html'.split())) sympy-0.7.4.1/bin/ask_update.py0000755000175000017500000000132612253362407016513 0ustar georgeskgeorgesk#!/usr/bin/env python """ Update the ask_generated.py file This must be run each time known_facts is changed Should be run from sympy root directory $ python bin/ask_update.py """ # hook in-tree SymPy into Python path, if possible import os import sys isympy_path = os.path.abspath(__file__) isympy_dir = os.path.dirname(isympy_path) sympy_top = os.path.split(isympy_dir)[0] sympy_dir = os.path.join(sympy_top, 'sympy') if os.path.isdir(sympy_dir): sys.path.insert(0, sympy_top) from sympy.assumptions.ask import (compute_known_facts, known_facts, known_facts_keys) with open('sympy/assumptions/ask_generated.py', 'w') as f: code = compute_known_facts(known_facts, known_facts_keys) f.write(code) sympy-0.7.4.1/bin/generate_test_list.py0000644000175000017500000000302712253362407020254 0ustar georgeskgeorgesk""" Execute like this: $ python bin/generate_test_list.py tests = [ 'sympy.concrete.tests', 'sympy.core.tests', 'sympy.functions.combinatorial.tests', 'sympy.functions.elementary.tests', 'sympy.functions.special.tests', 'sympy.geometry.tests', 'sympy.integrals.tests', 'sympy.matrices.tests', 'sympy.ntheory.tests', 'sympy.numerics.tests', 'sympy.parsing.tests', 'sympy.physics.tests', 'sympy.plotting.tests', 'sympy.polynomials.tests', 'sympy.printing.tests', 'sympy.series.tests', 'sympy.simplify.tests', 'sympy.solvers.tests', 'sympy.specfun.tests', 'sympy.statistics.tests', 'sympy.test_external', 'sympy.utilities.tests', ] """ from __future__ import print_function from glob import glob def get_paths(level=15): """ Generates a set of paths for testfiles searching. Example: >>> get_paths(2) ['sympy/test_*.py', 'sympy/*/test_*.py', 'sympy/*/*/test_*.py'] >>> get_paths(6) ['sympy/test_*.py', 'sympy/*/test_*.py', 'sympy/*/*/test_*.py', 'sympy/*/*/*/test_*.py', 'sympy/*/*/*/*/test_*.py', 'sympy/*/*/*/*/*/test_*.py', 'sympy/*/*/*/*/*/*/test_*.py'] """ wildcards = ["/"] for i in range(level): wildcards.append(wildcards[-1] + "*/") p = ["sympy" + x + "test_*.py" for x in wildcards] return p g = [] for x in get_paths(): g.extend(glob(x)) g = [".".join(x.split("/")[:-1]) for x in g] g = list(set(g)) g.sort() print("tests = [") for x in g: print(" '%s'," % x) print(" ]") sympy-0.7.4.1/bin/sympy_time_cache.py0000644000175000017500000000617312253362407017717 0ustar georgeskgeorgeskfrom __future__ import print_function import time import timeit class TreeNode(object): def __init__(self, name): self._name = name self._children = [] self._time = 0 def __str__(self): return "%s: %s" % (self._name, self._time) __repr__ = __str__ def add_child(self, node): self._children.append(node) def children(self): return self._children def child(self, i): return self.children()[i] def set_time(self, time): self._time = time def time(self): return self._time total_time = time def exclusive_time(self): return self.total_time() - sum(child.time() for child in self.children()) def name(self): return self._name def linearize(self): res = [self] for child in self.children(): res.extend(child.linearize()) return res def print_tree(self, level=0, max_depth=None): print(" "*level + str(self)) if max_depth is not None and max_depth <= level: return for child in self.children(): child.print_tree(level + 1, max_depth=max_depth) def print_generic(self, n=50, method="time"): slowest = sorted((getattr(node, method)(), node.name()) for node in self.linearize())[-n:] for time, name in slowest[::-1]: print("%s %s" % (time, name)) def print_slowest(self, n=50): self.print_generic(n=50, method="time") def print_slowest_exclusive(self, n=50): self.print_generic(n, method="exclusive_time") def write_cachegrind(self, f): if isinstance(f, str): f = open(f, "w") f.write("events: Microseconds\n") f.write("fl=sympyallimport\n") must_close = True else: must_close = False f.write("fn=%s\n" % self.name()) f.write("1 %s\n" % self.exclusive_time()) counter = 2 for child in self.children(): f.write("cfn=%s\n" % child.name()) f.write("calls=1 1\n") f.write("%s %s\n" % (counter, child.time())) counter += 1 f.write("\n\n") for child in self.children(): child.write_cachegrind(f) if must_close: f.close() pp = TreeNode(None) # We have to use pp since there is a sage function #called parent that gets imported seen = set() def new_import(name, globals={}, locals={}, fromlist=[]): global pp if name in seen: return old_import(name, globals, locals, fromlist) seen.add(name) node = TreeNode(name) pp.add_child(node) old_pp = pp pp = node #Do the actual import t1 = timeit.default_timer() module = old_import(name, globals, locals, fromlist) t2 = timeit.default_timer() node.set_time(int(1000000*(t2 - t1))) pp = old_pp return module old_import = __builtins__.__import__ __builtins__.__import__ = new_import old_sum = sum from sympy import * sum = old_sum sageall = pp.child(0) sageall.write_cachegrind("sympy.cachegrind") print("Timings saved. Do:\n$ kcachegrind sympy.cachegrind") sympy-0.7.4.1/bin/test_travis.sh0000755000175000017500000000242412253362407016724 0ustar georgeskgeorgesk#! /usr/bin/env bash # Exit on error set -e # Echo each command set -x if [[ "${TEST_SPHINX}" == "true" ]]; then cd doc make html-errors make clean make latex cd _build/latex export LATEXOPTIONS="-interaction=nonstopmode" make all elif [[ "${TEST_SAGE}" == "true" ]]; then sage -v sage -python bin/test sympy/external/tests/test_sage.py else # We change directories to make sure that we test the installed version of # sympy. mkdir empty cd empty if [[ "${TEST_DOCTESTS}" == "true" ]]; then cat << EOF | python import sympy if not sympy.doctest(): raise Exception('Tests failed') EOF cd .. bin/doctest doc/ elif [[ "${TEST_SLOW}" == "true" ]]; then cat << EOF | python import sympy if not sympy.test(slow=True, timeout=180): # Travis times out if no activity is seen for 10 minutes. It also times # out if the whole tests run for more than 50 minutes. raise Exception('Tests failed') EOF elif [[ "${TEST_THEANO}" == "true" ]]; then cat << EOF | python import sympy if not sympy.test('*theano*'): raise Exception('Tests failed') EOF else cat << EOF | python import sympy if not sympy.test(split='${SPLIT}'): raise Exception('Tests failed') EOF fi fi sympy-0.7.4.1/bin/py.bench0000755000175000017500000000077012253362407015454 0ustar georgeskgeorgesk#!/usr/bin/env python # hook in-tree SymPy into Python path, if possible # TODO this should be shared with isympy from __future__ import print_function import os import sys isympy_dir = os.path.dirname(__file__) # bin/isympy sympy_top = os.path.split(isympy_dir)[0] # ../ sympy_dir = os.path.join(sympy_top, 'sympy') # ../sympy/ if os.path.isdir(sympy_dir): sys.path.insert(0, sympy_top) from sympy.utilities import benchmarking if __name__ == '__main__': benchmarking.main() sympy-0.7.4.1/setup.py0000755000175000017500000002207312253362407014765 0ustar georgeskgeorgesk#!/usr/bin/env python """Distutils based setup script for SymPy. This uses Distutils (http://python.org/sigs/distutils-sig/) the standard python mechanism for installing packages. For the easiest installation just type the command (you'll probably need root privileges for that): python setup.py install This will install the library in the default location. For instructions on how to customize the install procedure read the output of: python setup.py --help install In addition, there are some other commands: python setup.py clean -> will clean all trash (*.pyc and stuff) python setup.py test -> will run the complete test suite python setup.py bench -> will run the complete benchmark suite python setup.py audit -> will run pyflakes checker on source code To get a full list of avaiable commands, read the output of: python setup.py --help-commands Or, if all else fails, feel free to write to the sympy list at sympy@googlegroups.com and ask for help. """ from distutils.core import setup from distutils.core import Command from distutils.command.build_scripts import build_scripts import sys import subprocess import os import sympy PY3 = sys.version_info[0] > 2 # Make sure I have the right Python version. if sys.version_info[:2] < (2, 6): print("SymPy requires Python 2.6 or newer. Python %d.%d detected" % sys.version_info[:2]) sys.exit(-1) # Check that this list is uptodate against the result of the command: # for i in `find sympy -name __init__.py | rev | cut -f 2- -d '/' | rev | egrep -v "^sympy$" | egrep -v "tests$" `; do echo "'${i//\//.}',"; done | sort modules = [ 'sympy.assumptions', 'sympy.assumptions.handlers', 'sympy.categories', 'sympy.combinatorics', 'sympy.concrete', 'sympy.core', 'sympy.crypto', 'sympy.diffgeom', 'sympy.external', 'sympy.functions', 'sympy.functions.combinatorial', 'sympy.functions.elementary', 'sympy.functions.special', 'sympy.galgebra', 'sympy.geometry', 'sympy.integrals', 'sympy.interactive', 'sympy.liealgebras', 'sympy.logic', 'sympy.logic.algorithms', 'sympy.logic.utilities', 'sympy.matrices', 'sympy.matrices.expressions', 'sympy.mpmath', 'sympy.mpmath.calculus', 'sympy.mpmath.functions', 'sympy.mpmath.libmp', 'sympy.mpmath.matrices', 'sympy.ntheory', 'sympy.parsing', 'sympy.physics', 'sympy.physics.hep', 'sympy.physics.mechanics', 'sympy.physics.quantum', 'sympy.plotting', 'sympy.plotting.intervalmath', 'sympy.plotting.pygletplot', 'sympy.polys', 'sympy.polys.agca', 'sympy.polys.domains', 'sympy.printing', 'sympy.printing.pretty', 'sympy.series', 'sympy.sets', 'sympy.simplify', 'sympy.solvers', 'sympy.statistics', 'sympy.stats', 'sympy.strategies', 'sympy.strategies.branch', 'sympy.tensor', 'sympy.unify', 'sympy.utilities', 'sympy.utilities.mathml', ] class audit(Command): """Audits SymPy's source code for following issues: - Names which are used but not defined or used before they are defined. - Names which are redefined without having been used. """ description = "Audit SymPy source with PyFlakes" user_options = [] def initialize_options(self): self.all = None def finalize_options(self): pass def run(self): import os try: import pyflakes.scripts.pyflakes as flakes except ImportError: print("In order to run the audit, you need to have PyFlakes installed.") sys.exit(-1) # We don't want to audit external dependencies ext = ('mpmath',) dirs = (os.path.join(*d) for d in (m.split('.') for m in modules) if d[1] not in ext) warns = 0 for dir in dirs: for filename in os.listdir(dir): if filename.endswith('.py') and filename != '__init__.py': warns += flakes.checkPath(os.path.join(dir, filename)) if warns > 0: print("Audit finished with total %d warnings" % warns) class clean(Command): """Cleans *.pyc and debian trashs, so you should get the same copy as is in the VCS. """ description = "remove build files" user_options = [("all", "a", "the same")] def initialize_options(self): self.all = None def finalize_options(self): pass def run(self): import os os.system("py.cleanup") os.system("rm -f python-build-stamp-2.4") os.system("rm -f MANIFEST") os.system("rm -rf build") os.system("rm -rf dist") os.system("rm -rf doc/_build") class test_sympy(Command): """Runs all tests under the sympy/ folder """ description = "run all tests and doctests; also see bin/test and bin/doctest" user_options = [] # distutils complains if this is not here. def __init__(self, *args): self.args = args[0] # so we can pass it to other classes Command.__init__(self, *args) def initialize_options(self): # distutils wants this pass def finalize_options(self): # this too pass def run(self): sympy.utilities.runtests.run_all_tests() class run_benchmarks(Command): """Runs all SymPy benchmarks""" description = "run all benchmarks" user_options = [] # distutils complains if this is not here. def __init__(self, *args): self.args = args[0] # so we can pass it to other classes Command.__init__(self, *args) def initialize_options(self): # distutils wants this pass def finalize_options(self): # this too pass # we use py.test like architecture: # # o collector -- collects benchmarks # o runner -- executes benchmarks # o presenter -- displays benchmarks results # # this is done in sympy.utilities.benchmarking on top of py.test def run(self): from sympy.utilities import benchmarking benchmarking.main(['sympy']) cmdclass = {'test': test_sympy, 'bench': run_benchmarks, 'clean': clean, 'audit': audit} # Check that this list is uptodate against the result of the command: # $ python bin/generate_test_list.py tests = [ 'sympy.assumptions.tests', 'sympy.categories.tests', 'sympy.combinatorics.tests', 'sympy.concrete.tests', 'sympy.core.tests', 'sympy.crypto.tests', 'sympy.diffgeom.tests', 'sympy.external.tests', 'sympy.functions.combinatorial.tests', 'sympy.functions.elementary.tests', 'sympy.functions.special.tests', 'sympy.galgebra.tests', 'sympy.geometry.tests', 'sympy.integrals.tests', 'sympy.interactive.tests', 'sympy.liealgebras.tests', 'sympy.logic.tests', 'sympy.matrices.expressions.tests', 'sympy.matrices.tests', 'sympy.mpmath.tests', 'sympy.ntheory.tests', 'sympy.parsing.tests', 'sympy.physics.hep.tests', 'sympy.physics.mechanics.tests', 'sympy.physics.quantum.tests', 'sympy.physics.tests', 'sympy.plotting.intervalmath.tests', 'sympy.plotting.pygletplot.tests', 'sympy.plotting.tests', 'sympy.polys.agca.tests', 'sympy.polys.domains.tests', 'sympy.polys.tests', 'sympy.printing.pretty.tests', 'sympy.printing.tests', 'sympy.series.tests', 'sympy.sets.tests', 'sympy.simplify.tests', 'sympy.solvers.tests', 'sympy.statistics.tests', 'sympy.stats.tests', 'sympy.strategies.branch.tests', 'sympy.strategies.tests', 'sympy.tensor.tests', 'sympy.unify.tests', 'sympy.utilities.tests', ] classifiers = [ 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Topic :: Scientific/Engineering', 'Topic :: Scientific/Engineering :: Mathematics', 'Topic :: Scientific/Engineering :: Physics', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', ] long_description = '''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.''' setup_args = { "name": 'sympy', "version": sympy.__version__, "description": 'Computer algebra system (CAS) in Python', "long_description": long_description, "author": 'SymPy development team', "author_email": 'sympy@googlegroups.com', "license": 'BSD', "keywords": "Math CAS", "url": 'http://code.google.com/p/sympy', "packages": ['sympy'] + modules + tests, "scripts": ['bin/isympy'], "ext_modules": [], "package_data": { 'sympy.utilities.mathml': ['data/*.xsl'] }, "data_files": [('share/man/man1', ['doc/man/isympy.1'])], "cmdclass": cmdclass, "classifiers": classifiers, } setup(**setup_args) sympy-0.7.4.1/AUTHORS0000644000175000017500000002644112253362407014323 0ustar georgeskgeorgeskAll people who contributed to SymPy by sending at least a patch or more (in the order of the date of their first contribution), except those who explicitly didn't want to be mentioned. You can find missing people from this file by using git log --reverse --format="%aN <%aE>" | awk ' !x[$0]++' (the order from that command will be a little different, but close to the order here). People with a * next to their names are not found in the metadata of the git history. Ondřej Čertík Fabian Pedregosa Jurjen N.E. Bos Mateusz Paprocki *Marc-Etienne M.Leveille Brian Jorgensen Jason Gedge Robert Schwarz Pearu Peterson Fredrik Johansson Chris Wu *Ulrich Hecht Goutham Lakshminarayan David Lawrence Jaroslaw Tworek David Marek Bernhard R. Link Andrej Tokarčík Or Dvory Saroj Adhikari Pauli Virtanen Robert Kern James Aspnes Nimish Telang Abderrahim Kitouni Pan Peng Friedrich Hagedorn Elrond der Elbenfuerst Rizgar Mella Felix Kaiser Roberto Nobrega David Roberts Sebastian Krämer Vinzent Steinberg Riccardo Gori *Case Van Horsen Stepan Roucka Ali Raza Syed Stefano Maggiolo Robert Cimrman Bastian Weber Sebastian Krause Sebastian Kreft *Dan Alan Bromborsky Boris Timokhin Robert Andy R. Terrel Hubert Tsang Konrad Meyer Henrik Johansson Priit Laes Freddie Witherden Brian E. Granger Andrew Straw Kaifeng Zhu Ted Horst Andrew Docherty Akshay Srinivasan Aaron Meurer Barry Wardell Tomasz Buchert Vinay Kumar Johann Cohen-Tanugi Jochen Voss Luke Peterson Chris Smith Thomas Sidoti Florian Mickler Nicolas Pourcelot Ben Goodrich Toon Verstraelen Ronan Lamy James Abbatiello Ryan Krauss Bill Flynn Kevin Goodsell Jorn Baayen Eh Tan Renato Coutinho Oscar Benjamin Øyvind Jensen Julio Idichekop Filho Łukasz Pankowski *Chu-Ching Huang Fernando Perez Raffaele De Feo Christian Muise Matt Curry Kazuo Thow Christian Schubert Jezreel Ng James Pearson Matthew Brett Addison Cugini Nicholas J.S. Kinar Harold Erbin Thomas Dixon Cristóvão Sousa Andre de Fortier Smit Mark Dewing Alexey U. Gudchenko Gary Kerr Sherjil Ozair Oleksandr Gituliar Sean Vig Prafullkumar P. Tale Vladimir Perić Tom Bachmann Yuri Karadzhov Vladimir Lagunov Matthew Rocklin Saptarshi Mandal Gilbert Gede Anatolii Koval Tomo Lazovich Pavel Fedotov Kibeom Kim Gregory Ksionda Tomáš Bambas Jeremias Yehdegho Jack McCaffery Raymond Wong Luca Weihs Shai 'Deshe' Wyborski Thomas Wiecki Óscar Nájera Mario Pernici Benjamin McDonald Sam Magura Stefan Krastanov Bradley Froehle Min Ragan-Kelley Nikhil Sarda Emma Hogan Jason Moore Julien Rioux Roberto Colistete, Jr. Raoul Bourquin Gert-Ludwig Ingold Srinivas Vasudevan Miha Marolt Tim Lahey Luis Garcia Matt Rajca David Li David Ju Alexandr Gudulin Bilal Akhtar Grzegorz Świrski Matt Habel Nikolay Lazarov Nichita Utiu Tristan Hume Imran Ahmed Manzoor Steve Anton Sam Sleight tsmars15 Chancellor Arkantos Stepan Simsa Tobias Lenz Siddhanathan Shanmugam Tiffany Zhu Alexey Subach Joan Creus Geoffry Song Puneeth Chaganti Marcin Kostrzewa <> Jim Zhang Natalia Nawara vishal Shruti Mangipudi Davy Mao Swapnil Agarwal Kendhia jerryma1121 Joachim Durchholz Martin Povišer Siddhant Jain Kevin Hunter Michael Mayorov Nathan Alison Christian Bühler Carsten Knoll Bharath M R Matthias Toews Sergiu Ivanov Jorge E. Cardona Sanket Agarwal Manoj Babu K. Sai Nikhil Aleksandar Makelov Raphael Michel Sachin Irukula Ashwini Oruganti Andreas Kloeckner Prateek Papriwal Arpit Goyal Angadh Nanjangud Comer Duncan Jens H. Nielsen Joseph Dougherty marshall2389 Guru Devanla George Waksman Angus Griffith <16sn6uv@gmail.com> Timothy Reluga Brian Stephanik Ljubiša Moćić <3rdslasher@gmail.com> Piotr Korgul Rom le Clair Alexandr Popov Saurabh Jha Tarun Gaba Takafumi Arakaki Alexander Eberspächer Sachin Joglekar Tyler Pirtle Vasily Povalyaev Colleen Lee Niklas Thörne Huijun Mai Marek Šuppa Prasoon Shukla Sergey B Kirpichev Stefen Yin Thomas Hisch Matthew Hoff Madeleine Ball Case Van Horsen Mary Clark Rishabh Dixit Acebulf Manoj Kumar Akshit Agarwal CJ Carey Patrick Lacasse Ananya H Tarang Patel Christopher Dembia Benjamin Fishbein Sean Ge Ankit Agrawal Amit Jamadagni Björn Dahlgren Christophe Saint-Jean Demian Wassermann Khagesh Patel Stephen Loo hm Katja Sophie Hotz Varun Joshi Chetna Gupta Thilina Rathnayake Shravas K Rao Max Hutchinson Matthew Tadd Alexander Hirzel Randy Heydon Ramana Venkata Oliver Lee Seshagiri Prabhu Pradyumna Erik Welch Eric Nelson Roland Puntaier Chris Conley Tim Swast Dmitry Batkovich Francesco Bonazzi Yuriy Demidov Rick Muller Manish Gill Markus Müller Amit Saha Jeremy QuaBoo Stefan van der Walt David Joyner Lars Buitinck Alkiviadis G. Akritas Vinit Ravishankar Mike Boyle Heiner Kirchhoffer Pablo Puente James Fiedler Harsh Gupta Tuomas Airaksinen rathmann sympy-0.7.4.1/tox.ini.sample0000644000175000017500000000423012253362407016036 0ustar georgeskgeorgesk# Sample tox.ini configuration file. Copy to tox.ini and edit to needs. # More information on the wiki: # https://github.com/sympy/sympy/wiki/Using-Tox # Also see the tox documentation at # http://tox.testrun.org/en/latest/config.html # Note: don't forget to install it: # pip install tox [tox] # Define the environments. By default, py24-py32, jython, pypy. # It's also possible to define a custom environment, like docs. # You must list a defined environment here in order to run it. envlist = py25, py26, py27, docs [testenv] # Commands to be executed, it could be anything. The brackets are # important, as they allow us to call bin/test with arguments we # pass on through tox. Applies to all default environments. # You can run multiple commands by putting them on subsequent lines # Note that you should use bin/test and bin/doctest rather than setup.py # test, because the latter does not work with []. commands = python bin/test [] python bin/doctest [] # Example of a test environment, we use it so we'd test docs just # once. [testenv:docs] commands = python bin/doctest [] # Example of testing a 32bit version of Python. [testenv:py27-32] # basepython allows us to point to an arbitrary Python executable basepython=/Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7 commands = python bin/test [] python bin/doctest [] # Example of testing with a dependancy. [testenv:py27-gmpy] # deps is used to specify the dependancies we need. Tox will # automatically download them from PyPi using easy_install/pip. # Note that you have to provide the download link because # deps = gmpy will try to install gmpy2. deps = http://gmpy.googlecode.com/files/gmpy-1.14.zip commands = python bin/test [] python bin/doctest [] # Usage: # tox # runs all defined environments # tox -e py25,py26 # runs the specified environments, comma-separated # tox --recreate # recreate and then run all virtualenvs # tox hydrogen # runs the "hydrogen" tests on all environments # It's possible to combine commands freely, eg: # tox --recreate -e py25,py27 sympy/core # will recreate the py25 and py27 environments, and then run the # tests in sympy/core in them. sympy-0.7.4.1/LICENSE0000644000175000017500000000274012253362407014254 0ustar georgeskgeorgeskCopyright (c) 2006-2013 SymPy Development Team All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: a. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. b. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. c. Neither the name of SymPy nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. sympy-0.7.4.1/sympy/0000755000175000017500000000000012253362407014425 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/matrices/0000755000175000017500000000000012253362407016234 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/matrices/sparsetools.py0000644000175000017500000000226312253362407021167 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import SparseMatrix def _doktocsr(dok): """Converts a sparse matrix to Compressed Sparse Row (CSR) format. Parameters ========== A : contains non-zero elements sorted by key (row, column) JA : JA[i] is the column corresponding to A[i] IA : IA[i] contains the index in A for the first non-zero element of row[i]. Thus IA[i+1] - IA[i] gives number of non-zero elements row[i]. The length of IA is always 1 more than the number of rows in the matrix. """ row, JA, A = [list(i) for i in zip(*dok.row_list())] IA = [0]*((row[0] if row else 0) + 1) for i, r in enumerate(row): IA.extend([i]*(r - row[i - 1])) # if i = 0 nothing is extended IA.extend([len(A)]*(dok.rows - len(IA) + 1)) shape = [dok.rows, dok.cols] return [A, JA, IA, shape] def _csrtodok(csr): """Converts a CSR representation to DOK representation""" smat = {} A, JA, IA, shape = csr for i in range(len(IA) - 1): indices = slice(IA[i], IA[i + 1]) for l, m in zip(A[indices], JA[indices]): smat[i, m] = l return SparseMatrix(*(shape + [smat])) sympy-0.7.4.1/sympy/matrices/immutable.py0000644000175000017500000000752012253362407020571 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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 class ImmutableMatrix(MatrixExpr, DenseMatrix): """Create an immutable version of a matrix. Examples ======== >>> from sympy import eye >>> from sympy.matrices import ImmutableMatrix >>> ImmutableMatrix(eye(3)) Matrix([ [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 = cls._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__ 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, {}) Matrix([[0]]) >>> ImmutableSparseMatrix(eye(3)) Matrix([ [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 def __hash__(self): return hash((type(self).__name__,) + (self.shape, tuple(self._smat))) sympy-0.7.4.1/sympy/matrices/sparse.py0000644000175000017500000012364112253362407020112 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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.utilities.exceptions import SymPyDeprecationWarning from .matrices import MatrixBase, ShapeError, a2idx from .dense import Matrix import collections class SparseMatrix(MatrixBase): """ A sparse matrix (a matrix with a large number of zero elements). Examples ======== >>> from sympy.matrices import SparseMatrix >>> SparseMatrix(2, 2, range(4)) Matrix([ [0, 1], [2, 3]]) >>> SparseMatrix(2, 2, {(1, 1): 2}) Matrix([ [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 = self._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 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 = self._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 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)] 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) Matrix([[1, 2]]) See Also ======== col row_list """ return self[i,:] 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) Matrix([ [1], [3]]) See Also ======== row col_list """ return self[:, j] 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 Matrix([ [1, 2], [3, 4]]) >>> a.RL [(0, 0, 1), (0, 1, 2), (1, 0, 3), (1, 1, 4)] See Also ======== row_op col_list """ return [tuple(k + (self[k],)) for k in sorted(list(self._smat.keys()), key=lambda k: list(k))] RL = property(row_list, None, None, "Alternate faster representation") 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 Matrix([ [1, 2], [3, 4]]) >>> a.CL [(0, 0, 1), (1, 0, 3), (0, 1, 2), (1, 1, 4)] See Also ======== col_op row_list """ return [tuple(k + (self[k],)) for k in sorted(list(self._smat.keys()), key=lambda k: list(reversed(k)))] 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 Matrix([ [1, 2], [3, 4]]) >>> a.T Matrix([ [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 Matrix([ [1, 2 + I], [3, 4], [I, -I]]) >>> a.C Matrix([ [ 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 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 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 Matrix([ [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, range(1, 5)) >>> S = SparseMatrix(2, 2, 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 Matrix([ [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)) Matrix([ [-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 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))) Matrix([ [2, 1, 1], [1, 2, 1], [1, 1, 2]]) >>> SparseMatrix(eye(3)).add(-SparseMatrix(eye(3))) Matrix([ [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) r, c = rhi - rlo, chi - clo if r*c < len(self._smat): # the subregion is smaller than the number of elements in self if r == 1: getter = lambda i, j: self[rlo, j + clo] elif c == 1: getter = lambda i, j: self[i + rlo, clo] else: getter = lambda i, j: self[i + rlo, j + clo] return self._new(r, c, getter) else: # the number of non-zero elements is smaller than the subregion smat = {} for rk, ck in self._smat: if rlo <= rk < rhi and clo <= ck < chi: smat[(rk-rlo, ck-clo)] = self._smat[(rk, ck)] return self._new(r, c, smat) 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) 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) 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 Matrix([ [0, 1], [2, 3]]) >>> m.applyfunc(lambda i: 2*i) Matrix([ [0, 2], [4, 6]]) """ if not callable(f): 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 def reshape(self, rows, cols): """Reshape matrix while retaining original size. Examples ======== >>> from sympy.matrices import SparseMatrix >>> S = SparseMatrix(4, 2, range(8)) >>> S.reshape(2, 4) Matrix([ [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) 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 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) 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() Matrix([ [ 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) 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 Matrix([ [ 1, 0, 0], [ 3/5, 1, 0], [-1/5, 1/3, 1]]) >>> D Matrix([ [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) 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 Matrix([ [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 Matrix([ [ 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 Matrix([ [ 5/3], [10/3]]) The error is given by S*xy - r: >>> S*xy - r Matrix([ [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 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') Matrix([ [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 Matrix([ [2/3, 1/3, 1/6], [1/3, 2/3, 1/3], [ 0, 0, 1/2]]) >>> A * _ Matrix([ [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 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 Matrix([ [1, 2], [3, 5]]) """ return MutableSparseMatrix(self) def as_immutable(self): """Returns an Immutable version of this Matrix.""" from immutable import ImmutableSparseMatrix return ImmutableSparseMatrix(self) def nnz(self): """Returns the number of non-zero elements in Matrix.""" return len(self._smat) @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(r, c, {}) @classmethod 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)])) class MutableSparseMatrix(SparseMatrix, MatrixBase): @classmethod def _new(cls, *args, **kwargs): return cls(*args) def as_mutable(self): return self.copy() 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 Matrix([ [0, 1], [0, 0]]) >>> M[1, 1] = 2; M Matrix([ [0, 1], [0, 2]]) >>> M = SparseMatrix(2, 2, {}) >>> M[:, 1] = [1, 1]; M Matrix([ [0, 1], [0, 1]]) >>> M = SparseMatrix(2, 2, {}) >>> M[1, :] = [[1, 1]]; M Matrix([ [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 Matrix([ [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 Matrix([ [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)] __hash__ = None 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 Matrix([ [0, 0], [0, 1]]) >>> M.row_del(0) >>> M Matrix([[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 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 Matrix([ [0, 0], [0, 1]]) >>> M.col_del(0) >>> M Matrix([ [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 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 Matrix([ [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 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 Matrix([ [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 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 Matrix([ [1, 0, 1], [0, 1, 0], [1, 1, 0]]) >>> B = SparseMatrix(((1, 0, 0), (0, 1, 0), (0, 0, 1))) >>> B Matrix([ [1, 0, 0], [0, 1, 0], [0, 0, 1]]) >>> C = A.row_join(B); C Matrix([ [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 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 Matrix([ [1, 1, 1], [1, 1, 1], [1, 1, 1]]) >>> B = SparseMatrix.eye(3) >>> B Matrix([ [1, 0, 0], [0, 1, 0], [0, 0, 1]]) >>> C = A.col_join(B); C Matrix([ [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] def zip_row_op(self, i, k, f): """In-place operation on row ``i`` using two-arg functor whose args are interpreted as ``(self[i, j], self[k, j])``. Examples ======== >>> from sympy.matrices import SparseMatrix >>> M = SparseMatrix.eye(3)*2 >>> M[0, 1] = -1 >>> M.zip_row_op(1, 0, lambda v, u: v + 2*u); M Matrix([ [2, -1, 0], [4, 0, 0], [0, 0, 2]]) See Also ======== row row_op col_op """ self.row_op(i, lambda v, j: f(v, self[k, j])) 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 SparseMatrix >>> M = SparseMatrix.eye(3)*2 >>> M[0, 1] = -1 >>> M.row_op(1, lambda v, j: v + 2*M[0, j]); M Matrix([ [2, -1, 0], [4, 0, 0], [0, 0, 2]]) See Also ======== row zip_row_op col_op """ 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)) 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 Matrix([ [ 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)) 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 Matrix([ [0, 0, 0], [0, 0, 0], [0, 0, 0]]) >>> M.fill(1); M Matrix([ [1, 1, 1], [1, 1, 1], [1, 1, 1]]) """ if not value: self._smat = {} else: v = self._sympify(value) self._smat = dict([((i, j), v) for i in range(self.rows) for j in range(self.cols)]) sympy-0.7.4.1/sympy/matrices/expressions/0000755000175000017500000000000012253362407020616 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/matrices/expressions/diagonal.py0000644000175000017500000000105612253362407022750 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.matrices.expressions import MatrixExpr from sympy.core import S class DiagonalMatrix(MatrixExpr): arg = property(lambda self: self.args[0]) shape = property(lambda self: (self.arg.shape[0], self.arg.shape[0])) def _entry(self, i, j): return S.Zero if i != j else self.arg[i, 0] class DiagonalOf(MatrixExpr): arg = property(lambda self: self.args[0]) shape = property(lambda self: (self.arg.shape[0], S.One)) def _entry(self, i, j): return self.arg[i, i] sympy-0.7.4.1/sympy/matrices/expressions/blockmatrix.py0000644000175000017500000003330312253362407023511 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import ask, Q from sympy.core import Tuple, Basic, Add from sympy.strategies import typed, exhaust, condition, debug, do_one, unpack, chain from sympy.strategies.traverse import bottom_up 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, transpose from sympy.matrices.expressions.trace import Trace from sympy.matrices.expressions.determinant import det, Determinant from sympy.matrices.expressions.slice import MatrixSlice from sympy.matrices.expressions.inverse import Inverse from sympy.matrices import Matrix, eye, ShapeError 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) Matrix([ [X, Z], [0, Y]]) >>> C = BlockMatrix([[Identity(n), Z]]) >>> print(C) Matrix([[I, Z]]) >>> print(block_collapse(C*B)) Matrix([[X, Z*Y + Z]]) """ 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") def _eval_determinant(self): if self.blockshape == (2, 2): [[A, B], [C, D]] = self.blocks.tolist() if ask(Q.invertible(A)): return det(A)*det(D - C*A.I*B) elif ask(Q.invertible(D)): return det(D)*det(A - B*D.I*C) return Determinant(self) 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() Matrix([ [X', 0], [Z', Y']]) >>> _.transpose() Matrix([ [X, Z], [0, Y]]) """ return self._eval_transpose() def _entry(self, i, j): # Find row entry for row_block, numrows in enumerate(self.rowblocksizes): if (i < numrows) is not False: break else: i -= numrows for col_block, numcols in enumerate(self.colblocksizes): if (j < numcols) is not False: 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) 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) Matrix([ [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) 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) Matrix([ [X, Z], [0, Y]]) >>> C = BlockMatrix([[Identity(n), Z]]) >>> print(C) Matrix([[I, Z]]) >>> print(block_collapse(C*B)) Matrix([[X, Z*Y + Z]]) """ hasbm = lambda expr: isinstance(expr, MatrixExpr) and expr.has(BlockMatrix) rule = exhaust( bottom_up(exhaust(condition(hasbm, typed( {MatAdd: do_one(bc_matadd, bc_block_plus_ident), MatMul: do_one(bc_matmul, bc_dist), Transpose: bc_transpose, Inverse: bc_inverse, BlockMatrix: do_one(bc_unpack, deblock)}))))) 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) elif isinstance(A, BlockMatrix): matrices[i] = A._blockmul(BlockMatrix([[B]])) matrices.pop(i+1) elif isinstance(B, BlockMatrix): matrices[i] = BlockMatrix([[A]])._blockmul(B) matrices.pop(i+1) else: i+=1 return MatMul(factor, *matrices).doit() def bc_transpose(expr): return BlockMatrix(block_collapse(expr.arg).blocks.applyfunc(transpose).T) def bc_inverse(expr): expr2 = blockinverse_1x1(expr) if expr != expr2: return expr2 return blockinverse_2x2(Inverse(reblock_2x2(expr.arg))) def blockinverse_1x1(expr): if isinstance(expr.arg, BlockMatrix) and expr.arg.blockshape == (1, 1): mat = Matrix([[expr.arg.blocks[0].inverse()]]) return BlockMatrix(mat) return expr def blockinverse_2x2(expr): if isinstance(expr.arg, BlockMatrix) and expr.arg.blockshape == (2, 2): # Cite: The Matrix Cookbook Section 9.1.3 [[A, B], [C, D]] = expr.arg.blocks.tolist() return BlockMatrix([[ (A - B*D.I*C).I, (-A).I*B*(D - C*A.I*B).I], [-(D - C*A.I*B).I*C*A.I, (D - C*A.I*B).I]]) else: return expr def deblock(B): """ Flatten a BlockMatrix of BlockMatrices """ if not isinstance(B, BlockMatrix) or not B.blocks.has(BlockMatrix): return B wrap = lambda x: x if isinstance(x, BlockMatrix) else BlockMatrix([[x]]) bb = B.blocks.applyfunc(wrap) # everything is a block from sympy import Matrix try: MM = Matrix(0, sum(bb[0, i].blocks.shape[1] for i in range(bb.shape[1])), []) for row in range(0, bb.shape[0]): M = Matrix(bb[row, 0].blocks) for col in range(1, bb.shape[1]): M = M.row_join(bb[row, col].blocks) MM = MM.col_join(M) return BlockMatrix(MM) except ShapeError: return B def reblock_2x2(B): """ Reblock a BlockMatrix so that it has 2x2 blocks of block matrices """ if not isinstance(B, BlockMatrix) or not all(d > 2 for d in B.blocks.shape): return B BM = BlockMatrix # for brevity's sake return BM([[ B.blocks[0, 0], BM(B.blocks[0, 1:])], [BM(B.blocks[1:, 0]), BM(B.blocks[1:, 1:])]]) def bounds(sizes): """ Convert sequence of numbers into pairs of low-high pairs >>> from sympy.matrices.expressions.blockmatrix import bounds >>> bounds((1, 10, 50)) [(0, 1), (1, 11), (11, 61)] """ low = 0 rv = [] for size in sizes: rv.append((low, low + size)) low += size return rv def blockcut(expr, rowsizes, colsizes): """ Cut a matrix expression into Blocks >>> from sympy import ImmutableMatrix, blockcut >>> M = ImmutableMatrix(4, 4, range(16)) >>> B = blockcut(M, (1, 3), (1, 3)) >>> type(B).__name__ 'BlockMatrix' >>> ImmutableMatrix(B.blocks[0, 1]) Matrix([[1, 2, 3]]) """ rowbounds = bounds(rowsizes) colbounds = bounds(colsizes) return BlockMatrix([[MatrixSlice(expr, rowbound, colbound) for colbound in colbounds] for rowbound in rowbounds]) sympy-0.7.4.1/sympy/matrices/expressions/slice.py0000644000175000017500000000641712253362407022277 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.matrices.expressions.matexpr import MatrixExpr from sympy import Tuple, Basic from sympy.functions.elementary.integers import floor from sympy.assumptions import Q, ask def normalize(i, parentsize): if isinstance(i, slice): i = (i.start, i.stop, i.step) if not isinstance(i, (tuple, list, Tuple)): if (i < 0) == True: i += parentsize i = (i, i+1, 1) i = list(i) if len(i) == 2: i.append(1) start, stop, step = i start = start or 0 if stop == None: stop = parentsize if (start < 0) == True: start += parentsize if (stop < 0) == True: stop += parentsize step = step or 1 if ((stop - start) * step < 1) == True: raise IndexError() return (start, stop, step) class MatrixSlice(MatrixExpr): """ A MatrixSlice of a Matrix Expression Examples >>> from sympy import MatrixSlice, ImmutableMatrix >>> M = ImmutableMatrix(4, 4, range(16)) >>> print(M) Matrix([ [ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11], [12, 13, 14, 15]]) >>> B = MatrixSlice(M, (0, 2), (2, 4)) >>> print(ImmutableMatrix(B)) Matrix([ [2, 3], [6, 7]]) """ parent = property(lambda self: self.args[0]) rowslice = property(lambda self: self.args[1]) colslice = property(lambda self: self.args[2]) def __new__(cls, parent, rowslice, colslice): rowslice = normalize(rowslice, parent.shape[0]) colslice = normalize(colslice, parent.shape[1]) if not (len(rowslice) == len(colslice) == 3): raise IndexError() if ((0 > rowslice[0]) == True or (parent.shape[0] < rowslice[1]) == True or (0 > colslice[0]) == True or (parent.shape[1] < colslice[1]) == True): raise IndexError() if isinstance(parent, MatrixSlice): return mat_slice_of_slice(parent, rowslice, colslice) return Basic.__new__(cls, parent, Tuple(*rowslice), Tuple(*colslice)) @property def shape(self): rows = self.rowslice[1] - self.rowslice[0] rows = rows if self.rowslice[2] == 1 else floor(rows/self.rowslice[2]) cols = self.colslice[1] - self.colslice[0] cols = cols if self.colslice[2] == 1 else floor(cols/self.colslice[2]) return rows, cols def _entry(self, i, j): return self.parent._entry(i*self.rowslice[2] + self.rowslice[0], j*self.colslice[2] + self.colslice[0]) @property def on_diag(self): return self.rowslice == self.colslice def slice_of_slice(s, t): start1, stop1, step1 = s start2, stop2, step2 = t start = start1 + start2*step1 step = step1 * step2 stop = start1 + step1*stop2 if stop > stop1: raise IndexError() return start, stop, step def mat_slice_of_slice(parent, rowslice, colslice): """ Collapse nested matrix slices >>> from sympy import MatrixSymbol >>> X = MatrixSymbol('X', 10, 10) >>> X[:, 1:5][5:8, :] X[5:8, 1:5] >>> X[1:9:2, 2:6][1:3, 2] X[3:7:2, 4] """ row = slice_of_slice(parent.rowslice, rowslice) col = slice_of_slice(parent.colslice, colslice) return MatrixSlice(parent.parent, row, col) sympy-0.7.4.1/sympy/matrices/expressions/matpow.py0000644000175000017500000000143712253362407022504 0ustar georgeskgeorgeskfrom __future__ import print_function, division from .matexpr import MatrixExpr, ShapeError, Identity from sympy import Pow, S, Basic from sympy.core.sympify import _sympify class MatPow(MatrixExpr): def __new__(cls, base, exp): base = _sympify(base) assert base.is_Matrix exp = _sympify(exp) return super(MatPow, cls).__new__(cls, base, exp) @property def base(self): return self.args[0] @property def exp(self): return self.args[1] @property def shape(self): return self.base.shape def _entry(self, i, j): if self.exp.is_Integer: # Make an explicity MatMul out of the MatPow return MatMul(*[self.base for k in range(self.exp)])._entry(i, j) from .matmul import MatMul sympy-0.7.4.1/sympy/matrices/expressions/matexpr.py0000644000175000017500000002737212253362407022663 0ustar georgeskgeorgeskfrom __future__ import print_function, division from functools import wraps from sympy.core import S, Symbol, sympify, Tuple, Integer, Basic, Expr from sympy.core.decorators import call_highest_priority from sympy.core.sympify import SympifyError, sympify from sympy.functions import conjugate, adjoint from sympy.matrices import ShapeError from sympy.simplify import simplify def _sympifyit(arg, retval=None): # This version of _sympifyit sympifies MutableMatrix objects def deco(func): @wraps(func) def __sympifyit_wrapper(a, b): try: b = sympify(b, strict=True) return func(a, b) except SympifyError: return retval return __sympifyit_wrapper return deco class MatrixExpr(Basic): """ Superclass for Matrix Expressions MatrixExprs represent abstract matrices, linear transformations represented within a particular basis. Examples ======== >>> from sympy import MatrixSymbol >>> A = MatrixSymbol('A', 3, 3) >>> y = MatrixSymbol('y', 3, 1) >>> x = (A.T*A).I * A * y See Also ======== MatrixSymbol MatAdd MatMul Transpose Inverse """ _op_priority = 11.0 is_Matrix = True is_MatrixExpr = True is_Identity = None is_Inverse = False is_Transpose = False is_ZeroMatrix = False is_MatAdd = False is_MatMul = False is_commutative = False # The following is adapted from the core Expr object def __neg__(self): return MatMul(S.NegativeOne, self).doit() def __abs__(self): raise NotImplementedError @_sympifyit('other', NotImplemented) @call_highest_priority('__radd__') def __add__(self, other): return MatAdd(self, other).doit() @_sympifyit('other', NotImplemented) @call_highest_priority('__add__') def __radd__(self, other): return MatAdd(other, self).doit() @_sympifyit('other', NotImplemented) @call_highest_priority('__rsub__') def __sub__(self, other): return MatAdd(self, -other).doit() @_sympifyit('other', NotImplemented) @call_highest_priority('__sub__') def __rsub__(self, other): return MatAdd(other, -self).doit() @_sympifyit('other', NotImplemented) @call_highest_priority('__rmul__') def __mul__(self, other): return MatMul(self, other).doit() @_sympifyit('other', NotImplemented) @call_highest_priority('__mul__') def __rmul__(self, other): return MatMul(other, self).doit() @_sympifyit('other', NotImplemented) @call_highest_priority('__rpow__') def __pow__(self, other): if not self.is_square: raise ShapeError("Power of non-square matrix %s" % self) if other is S.NegativeOne: return Inverse(self) elif other is S.Zero: return Identity(self.rows) elif other is S.One: return self return MatPow(self, other) @_sympifyit('other', NotImplemented) @call_highest_priority('__pow__') def __rpow__(self, other): raise NotImplementedError("Matrix Power not defined") @_sympifyit('other', NotImplemented) @call_highest_priority('__rdiv__') def __div__(self, other): return self * other**S.NegativeOne @_sympifyit('other', NotImplemented) @call_highest_priority('__div__') def __rdiv__(self, other): raise NotImplementedError() #return MatMul(other, Pow(self, S.NegativeOne)) __truediv__ = __div__ __rtruediv__ = __rdiv__ @property def rows(self): return self.shape[0] @property def cols(self): return self.shape[1] @property def is_square(self): return self.rows == self.cols def _eval_conjugate(self): from sympy.matrices.expressions.adjoint import Adjoint from sympy.matrices.expressions.transpose import Transpose return Adjoint(Transpose(self)) def _eval_inverse(self): from sympy.matrices.expressions.inverse import Inverse return Inverse(self) def _eval_transpose(self): return Transpose(self) def _eval_power(self, exp): return MatPow(self, exp) def _eval_simplify(self, **kwargs): if self.is_Atom: return self else: return self.__class__(*[simplify(x, **kwargs) for x in self.args]) def _eval_adjoint(self): from sympy.matrices.expressions.adjoint import Adjoint return Adjoint(self) def _entry(self, i, j): raise NotImplementedError( "Indexing not implemented for %s" % self.__class__.__name__) def adjoint(self): return adjoint(self) def conjugate(self): return conjugate(self) def transpose(self): from sympy.matrices.expressions.transpose import transpose return transpose(self) T = property(transpose, None, None, 'Matrix transposition.') def inverse(self): return self._eval_inverse() @property def I(self): return self.inverse() def valid_index(self, i, j): def is_valid(idx): return isinstance(idx, (int, Integer, Symbol, Expr)) return (is_valid(i) and is_valid(j) and (0 <= i) is not False and (i < self.rows) is not False and (0 <= j) is not False and (j < self.cols) is not False) def __getitem__(self, key): if not isinstance(key, tuple) and isinstance(key, slice): from sympy.matrices.expressions.slice import MatrixSlice return MatrixSlice(self, key, (0, None, 1)) if isinstance(key, tuple) and len(key) == 2: i, j = key if isinstance(i, slice) or isinstance(j, slice): from sympy.matrices.expressions.slice import MatrixSlice return MatrixSlice(self, i, j) i, j = sympify(i), sympify(j) if self.valid_index(i, j) is not False: return self._entry(i, j) else: raise IndexError("Invalid indices (%s, %s)" % (i, j)) raise IndexError("Invalid index, wanted %s[i,j]" % self) def as_explicit(self): """ Returns a dense Matrix with elements represented explicitly Returns an object of type ImmutableMatrix. Examples ======== >>> from sympy import Identity >>> I = Identity(3) >>> I I >>> I.as_explicit() Matrix([ [1, 0, 0], [0, 1, 0], [0, 0, 1]]) See Also ======== as_mutable: returns mutable Matrix type """ from sympy.matrices.immutable import ImmutableMatrix return ImmutableMatrix([[ self[i, j] for j in range(self.cols)] for i in range(self.rows)]) def as_mutable(self): """ Returns a dense, mutable matrix with elements represented explicitly Examples ======== >>> from sympy import Identity >>> I = Identity(3) >>> I I >>> I.shape (3, 3) >>> I.as_mutable() Matrix([ [1, 0, 0], [0, 1, 0], [0, 0, 1]]) See Also ======== as_explicit: returns ImmutableMatrix """ return self.as_explicit().as_mutable() def __array__(self): from numpy import empty a = empty(self.shape, dtype=object) for i in range(self.rows): for j in range(self.cols): a[i, j] = self[i, j] return a def equals(self, other): """ Test elementwise equality between matrices, potentially of different types >>> from sympy import Identity, eye >>> Identity(3).equals(eye(3)) True """ return self.as_explicit().equals(other) def canonicalize(self): return self def as_coeff_mmul(self): return 1, MatMul(self) class MatrixElement(Expr): parent = property(lambda self: self.args[0]) i = property(lambda self: self.args[1]) j = property(lambda self: self.args[2]) _diff_wrt = True class MatrixSymbol(MatrixExpr): """Symbolic representation of a Matrix object Creates a SymPy Symbol to represent a Matrix. This matrix has a shape and can be included in Matrix Expressions >>> from sympy import MatrixSymbol, Identity >>> A = MatrixSymbol('A', 3, 4) # A 3 by 4 Matrix >>> B = MatrixSymbol('B', 4, 3) # A 4 by 3 Matrix >>> A.shape (3, 4) >>> 2*A*B + Identity(3) I + 2*A*B """ is_commutative = False def __new__(cls, name, n, m): n, m = sympify(n), sympify(m) obj = Basic.__new__(cls, name, n, m) return obj def _hashable_content(self): return(self.name, self.shape) @property def shape(self): return self.args[1:3] @property def name(self): return self.args[0] def _eval_subs(self, old, new): # only do substitutions in shape shape = Tuple(*self.shape)._subs(old, new) return MatrixSymbol(self.name, *shape) def __call__(self, *args): raise TypeError( "%s object is not callable" % self.__class__ ) def _entry(self, i, j): return MatrixElement(self, i, j) @property def free_symbols(self): return set((self,)) def doit(self, **hints): if hints.get('deep', True): return type(self)(self.name, self.args[1].doit(**hints), self.args[2].doit(**hints)) else: return self def _eval_simplify(self, **kwargs): return self class Identity(MatrixExpr): """The Matrix Identity I - multiplicative identity >>> from sympy.matrices import Identity, MatrixSymbol >>> A = MatrixSymbol('A', 3, 5) >>> I = Identity(3) >>> I*A A """ is_Identity = True def __new__(cls, n): return super(Identity, cls).__new__(cls, sympify(n)) @property def rows(self): return self.args[0] @property def cols(self): return self.args[0] @property def shape(self): return (self.args[0], self.args[0]) def _eval_transpose(self): return self def _eval_trace(self): return self.rows def _eval_inverse(self): return self def conjugate(self): return self def _entry(self, i, j): if i == j: return S.One else: return S.Zero def _eval_determinant(self): return S.One class ZeroMatrix(MatrixExpr): """The Matrix Zero 0 - additive identity >>> from sympy import MatrixSymbol, ZeroMatrix >>> A = MatrixSymbol('A', 3, 5) >>> Z = ZeroMatrix(3, 5) >>> A+Z A >>> Z*A.T 0 """ is_ZeroMatrix = True def __new__(cls, m, n): return super(ZeroMatrix, cls).__new__(cls, m, n) @property def shape(self): return (self.args[0], self.args[1]) @_sympifyit('other', NotImplemented) @call_highest_priority('__rpow__') def __pow__(self, other): if other != 1 and not self.is_square: raise ShapeError("Power of non-square matrix %s" % self) if other == 0: return Identity(self.rows) return self def _eval_transpose(self): return ZeroMatrix(self.cols, self.rows) def _eval_trace(self): return S.Zero def _eval_determinant(self): return S.Zero def conjugate(self): return self def _entry(self, i, j): return S.Zero def __nonzero__(self): return False __bool__ = __nonzero__ def matrix_symbols(expr): return [sym for sym in expr.free_symbols if sym.is_Matrix] from .matmul import MatMul from .matadd import MatAdd from .matpow import MatPow from .transpose import Transpose from .inverse import Inverse sympy-0.7.4.1/sympy/matrices/expressions/fourier.py0000644000175000017500000000124412253362407022644 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.matrices.expressions import MatrixExpr from sympy import S, I, sqrt, exp class DFT(MatrixExpr): """ Discrete Fourier Transform """ n = property(lambda self: self.args[0]) shape = property(lambda self: (self.n, self.n)) def _entry(self, i, j): w = exp(-2*S.Pi*I/self.n) return w**(i*j) / sqrt(self.n) def _eval_inverse(self): return IDFT(self.n) class IDFT(DFT): """ Inverse Discrete Fourier Transform """ def _entry(self, i, j): w = exp(-2*S.Pi*I/self.n) return w**(-i*j) / sqrt(self.n) def _eval_inverse(self): return DFT(self.n) sympy-0.7.4.1/sympy/matrices/expressions/funcmatrix.py0000644000175000017500000000224712253362407023355 0ustar georgeskgeorgeskfrom __future__ import print_function, division from .matexpr import MatrixExpr from sympy import Basic, sympify class FunctionMatrix(MatrixExpr): """ Represents a Matrix using a function (Lambda) This class is an alternative to SparseMatrix >>> from sympy import FunctionMatrix, symbols, Lambda, MatMul, Matrix >>> i, j = symbols('i,j') >>> X = FunctionMatrix(3, 3, Lambda((i, j), i + j)) >>> Matrix(X) Matrix([ [0, 1, 2], [1, 2, 3], [2, 3, 4]]) >>> Y = FunctionMatrix(1000, 1000, Lambda((i, j), i + j)) >>> isinstance(Y*Y, MatMul) # this is an expression object True >>> (Y**2)[10,10] # So this is evaluated lazily 342923500 """ def __new__(cls, rows, cols, lamda): rows, cols = sympify(rows), sympify(cols) return Basic.__new__(cls, rows, cols, lamda) @property def shape(self): return self.args[0:2] @property def lamda(self): return self.args[2] def _entry(self, i, j): return self.lamda(i, j) def _eval_trace(self): from sympy.matrices.expressions.trace import Trace return Trace._eval_rewrite_as_Sum(Trace(self)).doit() sympy-0.7.4.1/sympy/matrices/expressions/inverse.py0000644000175000017500000000277412253362407022655 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core.sympify import _sympify from sympy.core import S, Basic from sympy.matrices.expressions.matexpr import ShapeError from sympy.matrices.expressions.matpow import MatPow class Inverse(MatPow): """ The multiplicative inverse of a matrix expression This is a symbolic object that simply stores its argument without evaluating it. To actually compute the inverse, use the ``.inverse()`` method of matrices. Examples ======== >>> from sympy import MatrixSymbol, Inverse >>> A = MatrixSymbol('A', 3, 3) >>> B = MatrixSymbol('B', 3, 3) >>> Inverse(A) A^-1 >>> A.inverse() == Inverse(A) True >>> (A*B).inverse() B^-1*A^-1 >>> Inverse(A*B) (A*B)^-1 """ is_Inverse = True exp = S(-1) def __new__(cls, mat): mat = _sympify(mat) assert mat.is_Matrix if not mat.is_square: raise ShapeError("Inverse of non-square matrix %s" % mat) return Basic.__new__(cls, mat) @property def arg(self): return self.args[0] @property def shape(self): return self.arg.shape def _eval_inverse(self): return self.arg def _eval_determinant(self): from sympy.matrices.expressions.determinant import det return 1/det(self.arg) def doit(self, **hints): if hints.get('deep', True): return self.arg.doit(**hints).inverse() else: return self.arg.inverse() sympy-0.7.4.1/sympy/matrices/expressions/adjoint.py0000644000175000017500000000315512253362407022624 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import Basic from sympy.functions import adjoint, conjugate from sympy.matrices.expressions.transpose import transpose from sympy.matrices.expressions.matexpr import MatrixExpr class Adjoint(MatrixExpr): """ The Hermitian adjoint of a matrix expression. This is a symbolic object that simply stores its argument without evaluating it. To actually compute the adjoint, use the ``adjoint()`` function. Examples ======== >>> from sympy.matrices import MatrixSymbol, Adjoint >>> from sympy.functions import adjoint >>> A = MatrixSymbol('A', 3, 5) >>> B = MatrixSymbol('B', 5, 3) >>> Adjoint(A*B) Adjoint(A*B) >>> adjoint(A*B) Adjoint(B)*Adjoint(A) >>> adjoint(A*B) == Adjoint(A*B) False >>> adjoint(A*B) == Adjoint(A*B).doit() True """ is_Adjoint = True def doit(self, **hints): arg = self.arg if hints.get('deep', True) and isinstance(arg, Basic): return adjoint(arg.doit(**hints)) else: return adjoint(self.arg) @property def arg(self): return self.args[0] @property def shape(self): return self.arg.shape[::-1] def _entry(self, i, j): return conjugate(self.arg._entry(j, i)) def _eval_adjoint(self): return self.arg def _eval_conjugate(self): return transpose(self.arg) def _eval_trace(self): from sympy.matrices.expressions.trace import Trace return conjugate(Trace(self.arg)) def _eval_transpose(self): return conjugate(self.arg) sympy-0.7.4.1/sympy/matrices/expressions/determinant.py0000644000175000017500000000220612253362407023502 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import Basic, Expr, S, Q from .matexpr import ShapeError class Determinant(Expr): """Matrix Determinant Represents the determinant of a matrix expression. >>> from sympy import MatrixSymbol, Determinant, eye >>> A = MatrixSymbol('A', 3, 3) >>> Determinant(A) Determinant(A) >>> Determinant(eye(3)).doit() 1 """ def __new__(cls, mat): if not mat.is_Matrix: raise TypeError("Input to Determinant, %s, not a matrix" % str(mat)) if not mat.is_square: raise ShapeError("Det of a non-square matrix") return Basic.__new__(cls, mat) @property def arg(self): return self.args[0] def doit(self, expand=False): try: return self.arg._eval_determinant() except (AttributeError, NotImplementedError): return self def det(matexpr): """ Matrix Determinant >>> from sympy import MatrixSymbol, det, eye >>> A = MatrixSymbol('A', 3, 3) >>> det(A) Determinant(A) >>> det(eye(3)) 1 """ return Determinant(matexpr).doit() sympy-0.7.4.1/sympy/matrices/expressions/hadamard.py0000644000175000017500000000462712253362407022742 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import Mul, Basic, sympify from sympy.strategies import unpack, flatten, sort, condition, exhaust, do_one from sympy.matrices.expressions.matexpr import MatrixExpr, ShapeError def hadamard_product(*matrices): """ Return the elementwise (aka Hadamard) product of matrices. Examples -------- >>> from sympy.matrices import hadamard_product, MatrixSymbol >>> A = MatrixSymbol('A', 2, 3) >>> B = MatrixSymbol('B', 2, 3) >>> hadamard_product(A) A >>> hadamard_product(A, B) A.*B >>> hadamard_product(A, B)[0, 1] A[0, 1]*B[0, 1] """ if not matrices: raise TypeError("Empty Hadamard product is undefined") validate(*matrices) if len(matrices) == 1: return matrices[0] else: return HadamardProduct(*matrices).doit() class HadamardProduct(MatrixExpr): """ Elementwise product of matrix expressions This is a symbolic object that simply stores its argument without evaluating it. To actually compute the product, use the function ``hadamard_product()``. >>> from sympy.matrices import hadamard_product, HadamardProduct, MatrixSymbol >>> A = MatrixSymbol('A', 5, 5) >>> B = MatrixSymbol('B', 5, 5) >>> isinstance(hadamard_product(A, B), HadamardProduct) True """ is_HadamardProduct = True def __new__(cls, *args, **kwargs): args = list(map(sympify, args)) check = kwargs.get('check' , True) if check: validate(*args) return super(HadamardProduct, cls).__new__(cls, *args) @property def shape(self): return self.args[0].shape def _entry(self, i, j): return Mul(*[arg._entry(i, j) for arg in self.args]) def _eval_transpose(self): from sympy.matrices.expressions.transpose import transpose return HadamardProduct(*list(map(transpose, self.args))) def doit(self, **ignored): return canonicalize(self) def validate(*args): if not all(arg.is_Matrix for arg in args): raise TypeError("Mix of Matrix and Scalar symbols") A = args[0] for B in args[1:]: if A.shape != B.shape: raise ShapeError("Matrices %s and %s are not aligned" % (A, B)) rules = (unpack, flatten) canonicalize = exhaust(condition(lambda x: isinstance(x, HadamardProduct), do_one(*rules))) sympy-0.7.4.1/sympy/matrices/expressions/transpose.py0000644000175000017500000000362612253362407023215 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import Basic, Q from sympy.functions import adjoint, conjugate from sympy.matrices.expressions.matexpr import MatrixExpr from sympy.matrices import MatrixBase class Transpose(MatrixExpr): """ The transpose of a matrix expression. This is a symbolic object that simply stores its argument without evaluating it. To actually compute the transpose, use the ``transpose()`` function, or the ``.T`` attribute of matrices. Examples ======== >>> from sympy.matrices import MatrixSymbol, Transpose >>> from sympy.functions import transpose >>> A = MatrixSymbol('A', 3, 5) >>> B = MatrixSymbol('B', 5, 3) >>> Transpose(A) A' >>> A.T == transpose(A) == Transpose(A) True >>> Transpose(A*B) (A*B)' >>> transpose(A*B) B'*A' """ is_Transpose = True def doit(self, **hints): arg = self.arg if hints.get('deep', True) and isinstance(arg, Basic): arg = arg.doit(**hints) try: result = arg._eval_transpose() return result if result is not None else Transpose(arg) except AttributeError: return Transpose(arg) @property def arg(self): return self.args[0] @property def shape(self): return self.arg.shape[::-1] def _entry(self, i, j): return self.arg._entry(j, i) def _eval_adjoint(self): return conjugate(self.arg) def _eval_conjugate(self): return adjoint(self.arg) def _eval_transpose(self): return self.arg def _eval_trace(self): from .trace import Trace return Trace(self.arg) # Trace(X.T) => Trace(X) def _eval_determinant(self): from sympy.matrices.expressions.determinant import det return det(self.arg) def transpose(expr): """ Matrix transpose """ return Transpose(expr).doit() sympy-0.7.4.1/sympy/matrices/expressions/matmul.py0000644000175000017500000001345712253362407022501 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import Mul, Basic, sympify, Add from sympy.functions import transpose, adjoint from sympy.matrices.expressions.transpose import transpose from sympy.strategies import (rm_id, unpack, typed, debug, flatten, exhaust, do_one, new) from sympy.matrices.expressions.matexpr import (MatrixExpr, ShapeError, Identity, ZeroMatrix) class MatMul(MatrixExpr): """ A product of matrix expressions Examples ======== >>> from sympy import MatMul, MatrixSymbol >>> A = MatrixSymbol('A', 5, 4) >>> B = MatrixSymbol('B', 4, 3) >>> C = MatrixSymbol('C', 3, 6) >>> MatMul(A, B, C) A*B*C """ is_MatMul = True def __new__(cls, *args, **kwargs): check = kwargs.get('check', True) args = list(map(sympify, args)) obj = Basic.__new__(cls, *args) factor, matrices = obj.as_coeff_matrices() if check: validate(*matrices) return obj @property def shape(self): matrices = [arg for arg in self.args if arg.is_Matrix] return (matrices[0].rows, matrices[-1].cols) def _entry(self, i, j, expand=True): coeff, matrices = self.as_coeff_matrices() if len(matrices) == 1: # situation like 2*X, matmul is just X return coeff * matrices[0][i, j] head, tail = matrices[0], matrices[1:] assert len(tail) != 0 X = head Y = MatMul(*tail) from sympy.core.symbol import Dummy from sympy.concrete.summations import Sum from sympy.matrices import ImmutableMatrix, MatrixBase k = Dummy('k', integer=True) if X.has(ImmutableMatrix) or Y.has(ImmutableMatrix): return coeff*Add(*[X[i, k]*Y[k, j] for k in range(X.cols)]) result = Sum(coeff*X[i, k]*Y[k, j], (k, 0, X.cols - 1)) return result.doit() if expand else result def as_coeff_matrices(self): scalars = [x for x in self.args if not x.is_Matrix] matrices = [x for x in self.args if x.is_Matrix] coeff = Mul(*scalars) return coeff, matrices def as_coeff_mmul(self): coeff, matrices = self.as_coeff_matrices() return coeff, MatMul(*matrices) def _eval_transpose(self): return MatMul(*[transpose(arg) for arg in self.args[::-1]]).doit() def _eval_adjoint(self): return MatMul(*[adjoint(arg) for arg in self.args[::-1]]).doit() def _eval_trace(self): factor, mmul = self.as_coeff_mmul() if factor != 1: from .trace import Trace return factor * Trace(mmul) else: raise NotImplementedError("Can't simplify any further") def _eval_determinant(self): from sympy.matrices.expressions.determinant import Determinant factor, matrices = self.as_coeff_matrices() square_matrices = only_squares(*matrices) return factor**self.rows * Mul(*list(map(Determinant, square_matrices))) def _eval_inverse(self): try: return MatMul(*[ arg.inverse() if isinstance(arg, MatrixExpr) else arg**-1 for arg in self.args[::-1]]).doit() except ShapeError: from sympy.matrices.expressions.inverse import Inverse return Inverse(self) def doit(self, **kwargs): deep = kwargs.get('deep', False) if deep: args = [arg.doit(**kwargs) for arg in self.args] else: args = self.args return canonicalize(MatMul(*args)) def validate(*matrices): """ Checks for valid shapes for args of MatMul """ for i in range(len(matrices)-1): A, B = matrices[i:i+2] if A.cols != B.rows: raise ShapeError("Matrices %s and %s are not aligned"%(A, B)) # Rules def newmul(*args): if args[0] == 1: args = args[1:] return new(MatMul, *args) def any_zeros(mul): if any([arg.is_zero or (arg.is_Matrix and arg.is_ZeroMatrix) for arg in mul.args]): matrices = [arg for arg in mul.args if arg.is_Matrix] return ZeroMatrix(matrices[0].rows, matrices[-1].cols) return mul def xxinv(mul): """ Y * X * X.I -> Y """ from sympy.matrices.expressions import Inverse factor, matrices = mul.as_coeff_matrices() for i, (X, Y) in enumerate(zip(matrices[:-1], matrices[1:])): try: if X.is_square and Y.is_square and X == Y.inverse(): I = Identity(X.rows) return newmul(factor, *(matrices[:i] + [I] + matrices[i+2:])) except ValueError: # Y might not be invertible pass return mul def remove_ids(mul): """ Remove Identities from a MatMul This is a modified version of sympy.strategies.rm_id. This is necesssary because MatMul may contain both MatrixExprs and Exprs as args. See Also -------- sympy.strategies.rm_id """ # Separate Exprs from MatrixExprs in args factor, mmul = mul.as_coeff_mmul() # Apply standard rm_id for MatMuls result = rm_id(lambda x: x.is_Identity is True)(mmul) if result != mmul: return newmul(factor, *result.args) # Recombine and return else: return mul def factor_in_front(mul): factor, matrices = mul.as_coeff_matrices() if factor != 1: return newmul(factor, *matrices) return mul rules = (any_zeros, remove_ids, xxinv, unpack, rm_id(lambda x: x == 1), factor_in_front, flatten) canonicalize = exhaust(typed({MatMul: do_one(*rules)})) def only_squares(*matrices): """ factor matrices only if they are square """ assert matrices[0].rows == matrices[-1].cols out = [] start = 0 for i, M in enumerate(matrices): if M.cols == matrices[start].rows: out.append(MatMul(*matrices[start:i+1]).doit()) start = i+1 return out sympy-0.7.4.1/sympy/matrices/expressions/matadd.py0000644000175000017500000000436712253362407022434 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import Add, Basic, sympify from sympy.functions import adjoint from sympy.matrices.expressions.transpose import transpose from sympy.strategies import (rm_id, unpack, flatten, sort, condition, debug, exhaust, do_one, glom) from sympy.matrices.expressions.matexpr import MatrixExpr, ShapeError, ZeroMatrix from sympy.utilities import default_sort_key class MatAdd(MatrixExpr): """A Sum of Matrix Expressions MatAdd inherits from and operates like SymPy Add >>> from sympy import MatAdd, MatrixSymbol >>> A = MatrixSymbol('A', 5, 5) >>> B = MatrixSymbol('B', 5, 5) >>> C = MatrixSymbol('C', 5, 5) >>> MatAdd(A, B, C) A + B + C """ is_MatAdd = True def __new__(cls, *args, **kwargs): check = kwargs.get('check', True) obj = Basic.__new__(cls, *args) if check: validate(*args) return obj @property def shape(self): return self.args[0].shape def _entry(self, i, j): return Add(*[arg._entry(i, j) for arg in self.args]) def _eval_transpose(self): return MatAdd(*[transpose(arg) for arg in self.args]).doit() def _eval_adjoint(self): return MatAdd(*[adjoint(arg) for arg in self.args]).doit() def _eval_trace(self): from trace import Trace return MatAdd(*[Trace(arg) for arg in self.args]).doit() def doit(self, **ignored): return canonicalize(self) def validate(*args): if not all(arg.is_Matrix for arg in args): raise TypeError("Mix of Matrix and Scalar symbols") A = args[0] for B in args[1:]: if A.shape != B.shape: raise ShapeError("Matrices %s and %s are not aligned"%(A, B)) factor_of = lambda arg: arg.as_coeff_mmul()[0] matrix_of = lambda arg: unpack(arg.as_coeff_mmul()[1]) def combine(cnt, mat): from .matmul import MatMul if cnt == 1: return mat else: return cnt * mat rules = (rm_id(lambda x: x == 0 or isinstance(x, ZeroMatrix)), unpack, flatten, glom(matrix_of, factor_of, combine), sort(default_sort_key)) canonicalize = exhaust(condition(lambda x: isinstance(x, MatAdd), do_one(*rules))) sympy-0.7.4.1/sympy/matrices/expressions/trace.py0000644000175000017500000000302212253362407022263 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import Basic, Expr from .matexpr import ShapeError class Trace(Expr): """Matrix Trace Represents the trace of a matrix expression. >>> from sympy import MatrixSymbol, Trace, eye >>> A = MatrixSymbol('A', 3, 3) >>> Trace(A) Trace(A) See Also: trace """ is_Trace = True def __new__(cls, mat): if not mat.is_Matrix: raise TypeError("input to Trace, %s, is not a matrix" % str(mat)) if not mat.is_square: raise ShapeError("Trace of a non-square matrix") return Basic.__new__(cls, mat) def _eval_transpose(self): return self @property def arg(self): return self.args[0] def doit(self, **kwargs): if kwargs.get('deep', False): arg = self.arg.doit() else: arg = self.arg try: return arg._eval_trace() except (AttributeError, NotImplementedError): return Trace(arg) def _eval_rewrite_as_Sum(self): from sympy import Sum, Dummy i = Dummy('i') return Sum(self.arg[i, i], (i, 0, self.arg.rows-1)).doit() def trace(expr): """ Trace of a Matrix. Sum of the diagonal elements >>> from sympy import trace, Symbol, MatrixSymbol, pprint, eye >>> n = Symbol('n') >>> X = MatrixSymbol('X', n, n) # A square matrix >>> trace(2*X) 2*Trace(X) >>> trace(eye(3)) 3 See Also: Trace """ return Trace(expr).doit() sympy-0.7.4.1/sympy/matrices/expressions/tests/0000755000175000017500000000000012253362407021760 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/matrices/expressions/tests/test_blockmatrix.py0000644000175000017500000001523212253362407025713 0ustar georgeskgeorgeskfrom sympy.matrices.expressions.blockmatrix import (block_collapse, bc_matmul, bc_block_plus_ident, BlockDiagMatrix, BlockMatrix, bc_dist, bc_matadd, bc_transpose, blockcut, reblock_2x2, deblock) from sympy.matrices.expressions import (MatrixSymbol, Identity, MatMul, Inverse, trace, Transpose, det) from sympy.matrices import Matrix, ImmutableMatrix from sympy.core import Tuple, symbols, Expr from sympy.functions import transpose i, j, k, l, m, n, p = symbols('i:n, p', integer=True) A = MatrixSymbol('A', n, n) B = MatrixSymbol('B', n, n) C = MatrixSymbol('C', n, n) D = MatrixSymbol('D', n, n) G = MatrixSymbol('G', n, n) H = MatrixSymbol('H', n, n) b1 = BlockMatrix([[G, H]]) b2 = BlockMatrix([[G], [H]]) def test_bc_matmul(): assert bc_matmul(H*b1*b2*G) == BlockMatrix([[(H*G*G + H*H*H)*G]]) def test_bc_matadd(): assert bc_matadd(BlockMatrix([[G, H]]) + BlockMatrix([[H, H]])) == \ BlockMatrix([[G+H, H+H]]) def test_bc_transpose(): assert bc_transpose(Transpose(BlockMatrix([[A, B], [C, D]]))) == \ BlockMatrix([[A.T, C.T], [B.T, D.T]]) def test_bc_dist_diag(): A = MatrixSymbol('A', n, n) B = MatrixSymbol('B', m, m) C = MatrixSymbol('C', l, l) X = BlockDiagMatrix(A, B, C) assert bc_dist(X+X).equals(BlockDiagMatrix(2*A, 2*B, 2*C)) def test_block_plus_ident(): A = MatrixSymbol('A', n, n) B = MatrixSymbol('B', n, m) C = MatrixSymbol('C', m, n) D = MatrixSymbol('D', m, m) X = BlockMatrix([[A, B], [C, D]]) assert bc_block_plus_ident(X+Identity(m+n)) == \ BlockDiagMatrix(Identity(n), Identity(m)) + X def test_BlockMatrix(): A = MatrixSymbol('A', n, m) B = MatrixSymbol('B', n, k) C = MatrixSymbol('C', l, m) D = MatrixSymbol('D', l, k) M = MatrixSymbol('M', m + k, p) N = MatrixSymbol('N', l + n, k + m) X = BlockMatrix(Matrix([[A, B], [C, D]])) assert X.__class__(*X.args) == X # block_collapse does nothing on normal inputs E = MatrixSymbol('E', n, m) assert block_collapse(A + 2*E) == A + 2*E F = MatrixSymbol('F', m, m) assert block_collapse(E.T*A*F) == E.T*A*F assert X.shape == (l + n, k + m) assert X.blockshape == (2, 2) assert transpose(X) == BlockMatrix(Matrix([[A.T, C.T], [B.T, D.T]])) assert transpose(X).shape == X.shape[::-1] # Test that BlockMatrices and MatrixSymbols can still mix assert (X*M).is_MatMul assert X._blockmul(M).is_MatMul assert (X*M).shape == (n + l, p) assert (X + N).is_MatAdd assert X._blockadd(N).is_MatAdd assert (X + N).shape == X.shape E = MatrixSymbol('E', m, 1) F = MatrixSymbol('F', k, 1) Y = BlockMatrix(Matrix([[E], [F]])) assert (X*Y).shape == (l + n, 1) assert block_collapse(X*Y).blocks[0, 0] == A*E + B*F assert block_collapse(X*Y).blocks[1, 0] == C*E + D*F # block_collapse passes down into container objects, transposes, and inverse assert block_collapse(transpose(X*Y)) == transpose(block_collapse(X*Y)) assert block_collapse(Tuple(X*Y, 2*X)) == ( block_collapse(X*Y), block_collapse(2*X)) # Make sure that MatrixSymbols will enter 1x1 BlockMatrix if it simplifies Ab = BlockMatrix([[A]]) Z = MatrixSymbol('Z', *A.shape) assert block_collapse(Ab + Z) == A + Z def test_BlockMatrix_trace(): A, B, C, D = map(lambda s: MatrixSymbol(s, 3, 3), 'ABCD') X = BlockMatrix([[A, B], [C, D]]) assert trace(X) == trace(A) + trace(D) def test_BlockMatrix_Determinant(): A, B, C, D = map(lambda s: MatrixSymbol(s, 3, 3), 'ABCD') X = BlockMatrix([[A, B], [C, D]]) from sympy import assuming, Q with assuming(Q.invertible(A)): assert det(X) == det(A) * det(D - C*A.I*B) assert isinstance(det(X), Expr) def test_squareBlockMatrix(): A = MatrixSymbol('A', n, n) B = MatrixSymbol('B', n, m) C = MatrixSymbol('C', m, n) D = MatrixSymbol('D', m, m) X = BlockMatrix([[A, B], [C, D]]) Y = BlockMatrix([[A]]) assert X.is_square assert (block_collapse(X + Identity(m + n)) == BlockMatrix([[A + Identity(n), B], [C, D + Identity(m)]])) Q = X + Identity(m + n) assert (X + MatrixSymbol('Q', n + m, n + m)).is_MatAdd assert (X * MatrixSymbol('Q', n + m, n + m)).is_MatMul assert block_collapse(Y.I) == A.I assert block_collapse(X.inverse()) == BlockMatrix([ [(-B*D.I*C + A).I, -A.I*B*(D + -C*A.I*B).I], [-(D - C*A.I*B).I*C*A.I, (D - C*A.I*B).I]]) assert isinstance(X.inverse(), Inverse) assert not X.is_Identity Z = BlockMatrix([[Identity(n), B], [C, D]]) assert not Z.is_Identity def test_BlockDiagMatrix(): A = MatrixSymbol('A', n, n) B = MatrixSymbol('B', m, m) C = MatrixSymbol('C', l, l) M = MatrixSymbol('M', n + m + l, n + m + l) X = BlockDiagMatrix(A, B, C) Y = BlockDiagMatrix(A, 2*B, 3*C) assert X.blocks[1, 1] == B assert X.shape == (n + m + l, n + m + l) assert all(X.blocks[i, j].is_ZeroMatrix if i != j else X.blocks[i, j] in [A, B, C] for i in range(3) for j in range(3)) assert X.__class__(*X.args) == X assert isinstance(block_collapse(X.I * X), Identity) assert bc_matmul(X*X) == BlockDiagMatrix(A*A, B*B, C*C) assert block_collapse(X*X) == BlockDiagMatrix(A*A, B*B, C*C) #XXX: should be == ?? assert block_collapse(X + X).equals(BlockDiagMatrix(2*A, 2*B, 2*C)) assert block_collapse(X*Y) == BlockDiagMatrix(A*A, 2*B*B, 3*C*C) assert block_collapse(X + Y) == BlockDiagMatrix(2*A, 3*B, 4*C) # Ensure that BlockDiagMatrices can still interact with normal MatrixExprs assert (X*(2*M)).is_MatMul assert (X + (2*M)).is_MatAdd assert (X._blockmul(M)).is_MatMul assert (X._blockadd(M)).is_MatAdd def test_blockcut(): A = MatrixSymbol('A', n, m) B = blockcut(A, (n/2, n/2), (m/2, m/2)) assert A[i, j] == B[i, j] assert B == BlockMatrix([[A[:n/2, :m/2], A[:n/2, m/2:]], [A[n/2:, :m/2], A[n/2:, m/2:]]]) M = ImmutableMatrix(4, 4, range(16)) B = blockcut(M, (2, 2), (2, 2)) assert M == ImmutableMatrix(B) B = blockcut(M, (1, 3), (2, 2)) assert ImmutableMatrix(B.blocks[0, 1]) == ImmutableMatrix([[2, 3]]) def test_reblock_2x2(): B = BlockMatrix([[MatrixSymbol('A_%d%d'%(i,j), 2, 2) for j in range(3)] for i in range(3)]) assert B.blocks.shape == (3, 3) BB = reblock_2x2(B) assert BB.blocks.shape == (2, 2) assert B.shape == BB.shape assert B.as_explicit() == BB.as_explicit() def test_deblock(): B = BlockMatrix([[MatrixSymbol('A_%d%d'%(i,j), n, n) for j in range(4)] for i in range(4)]) assert deblock(reblock_2x2(B)) == B sympy-0.7.4.1/sympy/matrices/expressions/tests/test_diagonal.py0000644000175000017500000000103112253362407025142 0ustar georgeskgeorgeskfrom sympy.matrices.expressions import MatrixSymbol from sympy.matrices.expressions.diagonal import DiagonalMatrix, DiagonalOf from sympy import Symbol, ask, Q n = Symbol('n') x = MatrixSymbol('x', n, 1) X = MatrixSymbol('X', n, n) D = DiagonalMatrix(x) d = DiagonalOf(X) def test_DiagonalMatrix(): assert D.shape == (n, n) assert D[1, 2] == 0 assert D[1, 1] == x[1, 0] def test_DiagonalMatrix_Assumptions(): assert ask(Q.diagonal(D)) def test_DiagonalOf(): assert d.shape == (n, 1) assert d[2, 0] == X[2, 2] sympy-0.7.4.1/sympy/matrices/expressions/tests/test_fourier.py0000644000175000017500000000065612253362407025053 0ustar georgeskgeorgeskfrom sympy import S, I, ask, Q, Abs, simplify, exp, sqrt from sympy.matrices.expressions.fourier import DFT, IDFT from sympy.matrices import det, Matrix, Identity from sympy.abc import n, i, j def test_dft(): assert DFT(4).shape == (4, 4) assert ask(Q.unitary(DFT(4))) assert Abs(simplify(det(Matrix(DFT(4))))) == 1 assert DFT(n)*IDFT(n) == Identity(n) assert DFT(n)[i, j] == exp(-2*S.Pi*I/n)**(i*j) / sqrt(n) sympy-0.7.4.1/sympy/matrices/expressions/tests/test_indexing.py0000644000175000017500000000421112253362407025174 0ustar georgeskgeorgeskfrom sympy import (symbols, MatrixSymbol, Symbol, MatPow, BlockMatrix, Identity, ZeroMatrix, ImmutableMatrix, eye, Sum) from sympy.utilities.pytest import raises k, l, m, n = symbols('k l m n', integer=True) i, j = symbols('i j', integer=True) W = MatrixSymbol('W', k, l) X = MatrixSymbol('X', l, m) Y = MatrixSymbol('Y', l, m) Z = MatrixSymbol('Z', m, n) A = MatrixSymbol('A', 2, 2) B = MatrixSymbol('B', 2, 2) x = MatrixSymbol('x', 1, 2) y = MatrixSymbol('x', 2, 1) def test_symbolic_indexing(): x12 = X[1, 2] assert all(s in str(x12) for s in ['1', '2', X.name]) # We don't care about the exact form of this. We do want to make sure # that all of these features are present def test_add_index(): assert (X + Y)[i, j] == X[i, j] + Y[i, j] def test_mul_index(): assert (A*y)[0, 0] == A[0, 0]*y[0, 0] + A[0, 1]*y[1, 0] assert (A*B).as_mutable() == (A.as_mutable() * B.as_mutable()) X = MatrixSymbol('X', n, m) Y = MatrixSymbol('Y', m, k) result = (X*Y)[4,2] expected = Sum(X[4, i]*Y[i, 2], (i, 0, m - 1)) assert result.args[0].dummy_eq(expected.args[0], i) assert result.args[1][1:] == expected.args[1][1:] def test_pow_index(): Q = MatPow(A, 2) assert Q[0, 0] == A[0, 0]**2 + A[0, 1]*A[1, 0] def test_transpose_index(): assert X.T[i, j] == X[j, i] def test_Identity_index(): I = Identity(3) assert I[0, 0] == I[1, 1] == I[2, 2] == 1 assert I[1, 0] == I[0, 1] == I[2, 1] == 0 raises(IndexError, lambda: I[3, 3]) def test_block_index(): I = Identity(3) Z = ZeroMatrix(3, 3) B = BlockMatrix([[I, I], [I, I]]) e3 = ImmutableMatrix(eye(3)) BB = BlockMatrix([[e3, e3], [e3, e3]]) assert B[0, 0] == B[3, 0] == B[0, 3] == B[3, 3] == 1 assert B[4, 3] == B[5, 1] == 0 BB = BlockMatrix([[e3, e3], [e3, e3]]) assert B.as_explicit() == BB.as_explicit() BI = BlockMatrix([[I, Z], [Z, I]]) assert BI.as_explicit().equals(eye(6)) def test_slicing(): A.as_explicit()[0, :] # does not raise an error def test_errors(): raises(IndexError, lambda: Identity(2)[1, 2, 3, 4, 5]) raises(IndexError, lambda: Identity(2)[[1, 2, 3, 4, 5]]) sympy-0.7.4.1/sympy/matrices/expressions/tests/test_determinant.py0000644000175000017500000000174312253362407025710 0ustar georgeskgeorgeskfrom sympy.core import Lambda, S, symbols from sympy.functions import adjoint, conjugate, transpose from sympy.matrices import eye, Matrix, ShapeError from sympy.matrices.expressions import ( Adjoint, Identity, FunctionMatrix, MatrixExpr, MatrixSymbol, Determinant, det, ZeroMatrix, Transpose ) from sympy.utilities.pytest import raises n = symbols('n', integer=True) A = MatrixSymbol('A', n, n) B = MatrixSymbol('B', n, n) C = MatrixSymbol('C', 3, 4) def test_det(): assert isinstance(Determinant(A), Determinant) assert not isinstance(Determinant(A), MatrixExpr) raises(ShapeError, lambda: Determinant(C)) assert det(eye(3)) == 1 assert det(Matrix(3, 3, [1, 3, 2, 4, 1, 3, 2, 5, 2])) == 17 A / det(A) # Make sure this is possible raises(TypeError, lambda: Determinant(S.One)) assert Determinant(A).arg is A def test_eval_determinant(): assert det(Identity(n)) == 1 assert det(ZeroMatrix(n, n)) == 0 assert det(Transpose(A)) == det(A) sympy-0.7.4.1/sympy/matrices/expressions/tests/test_adjoint.py0000644000175000017500000000205112253362407025017 0ustar georgeskgeorgeskfrom sympy.core import symbols, S from sympy.functions import adjoint, conjugate, transpose from sympy.matrices.expressions import MatrixSymbol, Adjoint, trace, Transpose from sympy.matrices import eye, Matrix n, m, l, k, p = symbols('n m l k p', integer=True) A = MatrixSymbol('A', n, m) B = MatrixSymbol('B', m, l) C = MatrixSymbol('C', n, n) def test_adjoint(): Sq = MatrixSymbol('Sq', n, n) assert Adjoint(A).shape == (m, n) assert Adjoint(A*B).shape == (l, n) assert adjoint(Adjoint(A)) == A assert isinstance(Adjoint(Adjoint(A)), Adjoint) assert conjugate(Adjoint(A)) == Transpose(A) assert transpose(Adjoint(A)) == Adjoint(Transpose(A)) assert Adjoint(eye(3)).doit() == eye(3) assert Adjoint(S(5)).doit() == S(5) assert Adjoint(Matrix([[1, 2], [3, 4]])).doit() == Matrix([[1, 3], [2, 4]]) assert adjoint(trace(Sq)) == conjugate(trace(Sq)) assert trace(adjoint(Sq)) == conjugate(trace(Sq)) assert Adjoint(Sq)[0, 1] == conjugate(Sq[1, 0]) assert Adjoint(A*B).doit() == Adjoint(B) * Adjoint(A) sympy-0.7.4.1/sympy/matrices/expressions/tests/test_inverse.py0000644000175000017500000000232212253362407025043 0ustar georgeskgeorgeskfrom sympy.core import symbols, S from sympy.functions import adjoint, conjugate, transpose from sympy.matrices.expressions import MatrixSymbol, Inverse from sympy.matrices import eye, Identity, Matrix, ShapeError from sympy.utilities.pytest import raises n, m, l = symbols('n m l', integer=True) A = MatrixSymbol('A', n, m) B = MatrixSymbol('B', m, l) C = MatrixSymbol('C', n, n) D = MatrixSymbol('D', n, n) E = MatrixSymbol('E', m, n) def test_inverse(): raises(ShapeError, lambda: Inverse(A)) raises(ShapeError, lambda: Inverse(A*B)) assert Inverse(C).shape == (n, n) assert Inverse(A*E).shape == (n, n) assert Inverse(E*A).shape == (m, m) assert Inverse(C).inverse() == C assert isinstance(Inverse(Inverse(C)), Inverse) assert C.inverse().inverse() == C assert C.inverse()*C == Identity(C.rows) assert Identity(n).inverse() == Identity(n) assert (3*Identity(n)).inverse() == Identity(n)/3 # Simplifies Muls if possible (i.e. submatrices are square) assert (C*D).inverse() == D.I*C.I # But still works when not possible assert isinstance((A*E).inverse(), Inverse) assert Inverse(eye(3)).doit() == eye(3) assert Inverse(eye(3)).doit(deep=False) == eye(3) sympy-0.7.4.1/sympy/matrices/expressions/tests/test_factorizations.py0000644000175000017500000000127112253362407026431 0ustar georgeskgeorgeskfrom sympy.matrices.expressions.factorizations import lu, LofCholesky, qr, svd from sympy import Symbol, MatrixSymbol, ask, Q n = Symbol('n') X = MatrixSymbol('X', n, n) def test_LU(): L, U = lu(X) assert L.shape == U.shape == X.shape assert ask(Q.lower_triangular(L)) assert ask(Q.upper_triangular(U)) def test_Cholesky(): L = LofCholesky(X) def test_QR(): Q_, R = qr(X) assert Q_.shape == R.shape == X.shape assert ask(Q.orthogonal(Q_)) assert ask(Q.upper_triangular(R)) def test_svd(): U, S, V = svd(X) assert U.shape == S.shape == V.shape == X.shape assert ask(Q.orthogonal(U)) assert ask(Q.orthogonal(V)) assert ask(Q.diagonal(S)) sympy-0.7.4.1/sympy/matrices/expressions/tests/test_trace.py0000644000175000017500000000265012253362407024472 0ustar georgeskgeorgeskfrom sympy.core import Lambda, S, symbols from sympy.concrete import Sum from sympy.functions import adjoint, conjugate, transpose from sympy.matrices import eye, Matrix, ShapeError from sympy.matrices.expressions import ( Adjoint, Identity, FunctionMatrix, MatrixExpr, MatrixSymbol, Trace, ZeroMatrix, trace ) from sympy.utilities.pytest import raises, XFAIL n = symbols('n', integer=True) A = MatrixSymbol('A', n, n) B = MatrixSymbol('B', n, n) C = MatrixSymbol('C', 3, 4) def test_Trace(): assert isinstance(Trace(A), Trace) assert not isinstance(Trace(A), MatrixExpr) raises(ShapeError, lambda: Trace(C)) assert trace(eye(3)) == 3 assert trace(Matrix(3, 3, [1, 2, 3, 4, 5, 6, 7, 8, 9])) == 15 assert adjoint(Trace(A)) == trace(Adjoint(A)) assert conjugate(Trace(A)) == trace(Adjoint(A)) assert transpose(Trace(A)) == Trace(A) A / Trace(A) # Make sure this is possible # Some easy simplifications assert trace(Identity(5)) == 5 assert trace(ZeroMatrix(5, 5)) == 0 assert trace(2*A*B) == 2 * trace(A*B) assert trace(A.T) == trace(A) i, j = symbols('i j') F = FunctionMatrix(3, 3, Lambda((i, j), i + j)) assert trace(F) == (0 + 0) + (1 + 1) + (2 + 2) raises(TypeError, lambda: Trace(S.One)) assert Trace(A).arg is A assert str(trace(A)) == str(Trace(A).doit(deep=True)) @XFAIL def test_rewrite(): assert isinstance(trace(A).rewrite(Sum), Sum) sympy-0.7.4.1/sympy/matrices/expressions/tests/test_slice.py0000644000175000017500000000375512253362407024502 0ustar georgeskgeorgeskfrom sympy.matrices.expressions.slice import MatrixSlice from sympy.matrices.expressions import MatrixSymbol from sympy.abc import a, b, c, d, k, l, m, n from sympy.utilities.pytest import raises, XFAIL from sympy.functions.elementary.integers import floor from sympy.assumptions import assuming, Q X = MatrixSymbol('X', n, m) Y = MatrixSymbol('Y', m, k) def test_shape(): B = MatrixSlice(X, (a, b), (c, d)) assert B.shape == (b - a, d - c) def test_entry(): B = MatrixSlice(X, (a, b), (c, d)) assert B[0,0] == X[a, c] assert B[k,l] == X[a+k, c+l] raises(IndexError, lambda : MatrixSlice(X, 1, (2, 5))[1, 0]) assert X[1::2, :][1, 3] == X[1+2, 3] assert X[:, 1::2][3, 1] == X[3, 1+2] def test_on_diag(): assert not MatrixSlice(X, (a, b), (c, d)).on_diag assert MatrixSlice(X, (a, b), (a, b)).on_diag def test_inputs(): assert MatrixSlice(X, 1, (2, 5)) == MatrixSlice(X, (1, 2), (2, 5)) assert MatrixSlice(X, 1, (2, 5)).shape == (1, 3) def test_slicing(): assert X[1:5, 2:4] == MatrixSlice(X, (1, 5), (2, 4)) assert X[1, 2:4] == MatrixSlice(X, 1, (2, 4)) assert X[1:5, :].shape == (4, X.shape[1]) assert X[:, 1:5].shape == (X.shape[0], 4) assert X[::2, ::2].shape == (floor(n/2), floor(m/2)) assert X[2, :] == MatrixSlice(X, 2, (0, m)) assert X[k, :] == MatrixSlice(X, k, (0, m)) def test_exceptions(): X = MatrixSymbol('x', 10, 20) raises(IndexError, lambda: X[0:12, 2]) raises(IndexError, lambda: X[0:9, 22]) raises(IndexError, lambda: X[-1:5, 2]) @XFAIL def test_symmetry(): X = MatrixSymbol('x', 10, 10) Y = X[:5, 5:] with assuming(Q.symmetric(X)): assert Y.T == X[5:, :5] def test_slice_of_slice(): X = MatrixSymbol('x', 10, 10) assert X[2, :][:, 3][0, 0] == X[2, 3] assert X[:5, :5][:4, :4] == X[:4, :4] assert X[1:5, 2:6][1:3, 2] == X[2:4, 4] assert X[1:9:2, 2:6][1:3, 2] == X[3:7:2, 4] def test_negative_index(): X = MatrixSymbol('x', 10, 10) assert X[-1, :] == X[9, :] sympy-0.7.4.1/sympy/matrices/expressions/tests/test_matrix_exprs.py0000644000175000017500000001165612253362407026127 0ustar georgeskgeorgeskfrom sympy.core import S, symbols, Add, Mul from sympy.functions import transpose, sin, cos, sqrt from sympy.simplify import simplify from sympy.matrices import (Identity, ImmutableMatrix, Inverse, MatAdd, MatMul, MatPow, Matrix, MatrixExpr, MatrixSymbol, ShapeError, ZeroMatrix, Transpose, Adjoint) from sympy.utilities.pytest import raises n, m, l, k, p = symbols('n m l k p', integer=True) x = symbols('x') A = MatrixSymbol('A', n, m) B = MatrixSymbol('B', m, l) C = MatrixSymbol('C', n, n) D = MatrixSymbol('D', n, n) E = MatrixSymbol('E', m, n) def test_shape(): assert A.shape == (n, m) assert (A*B).shape == (n, l) raises(ShapeError, lambda: B*A) def test_matexpr(): assert (x*A).shape == A.shape assert (x*A).__class__ == MatMul assert 2*A - A - A == ZeroMatrix(*A.shape) assert (A*B).shape == (n, l) def test_subs(): A = MatrixSymbol('A', n, m) B = MatrixSymbol('B', m, l) C = MatrixSymbol('C', m, l) assert A.subs(n, m).shape == (m, m) assert (A*B).subs(B, C) == A*C assert (A*B).subs(l, n).is_square def test_ZeroMatrix(): A = MatrixSymbol('A', n, m) Z = ZeroMatrix(n, m) assert A + Z == A assert A*Z.T == ZeroMatrix(n, n) assert Z*A.T == ZeroMatrix(n, n) assert A - A == ZeroMatrix(*A.shape) assert not Z assert transpose(Z) == ZeroMatrix(m, n) assert Z.conjugate() == Z assert ZeroMatrix(n, n)**0 == Identity(n) with raises(ShapeError): Z**0 with raises(ShapeError): Z**2 def test_ZeroMatrix_doit(): Znn = ZeroMatrix(Add(n, n, evaluate=False), n) assert isinstance(Znn.rows, Add) assert Znn.doit() == ZeroMatrix(2*n, n) assert isinstance(Znn.doit().rows, Mul) def test_Identity(): A = MatrixSymbol('A', n, m) In = Identity(n) Im = Identity(m) assert A*Im == A assert In*A == A assert transpose(In) == In assert In.inverse() == In assert In.conjugate() == In def test_Identity_doit(): Inn = Identity(Add(n, n, evaluate=False)) assert isinstance(Inn.rows, Add) assert Inn.doit() == Identity(2*n) assert isinstance(Inn.doit().rows, Mul) def test_addition(): A = MatrixSymbol('A', n, m) B = MatrixSymbol('B', n, m) assert isinstance(A + B, MatAdd) assert (A + B).shape == A.shape assert isinstance(A - A + 2*B, MatMul) raises(ShapeError, lambda: A + B.T) raises(TypeError, lambda: A + 1) raises(TypeError, lambda: 5 + A) raises(TypeError, lambda: 5 - A) assert A + ZeroMatrix(n, m) - A == ZeroMatrix(n, m) with raises(TypeError): ZeroMatrix(n,m) + S(0) def test_multiplication(): A = MatrixSymbol('A', n, m) B = MatrixSymbol('B', m, l) C = MatrixSymbol('C', n, n) assert (2*A*B).shape == (n, l) assert (A*0*B) == ZeroMatrix(n, l) raises(ShapeError, lambda: B*A) assert (2*A).shape == A.shape assert A * ZeroMatrix(m, m) * B == ZeroMatrix(n, l) assert C * Identity(n) * C.I == Identity(n) assert B/2 == S.Half*B raises(NotImplementedError, lambda: 2/B) A = MatrixSymbol('A', n, n) B = MatrixSymbol('B', n, n) assert Identity(n) * (A + B) == A + B def test_MatPow(): A = MatrixSymbol('A', n, n) AA = MatPow(A, 2) assert AA.exp == 2 assert AA.base == A assert (A**n).exp == n assert A**0 == Identity(n) assert A**1 == A assert A**2 == AA assert A**-1 == Inverse(A) assert A**S.Half == sqrt(A) raises(ShapeError, lambda: MatrixSymbol('B', 3, 2)**2) def test_MatrixSymbol(): n, m, t = symbols('n,m,t') X = MatrixSymbol('X', n, m) assert X.shape == (n, m) raises(TypeError, lambda: MatrixSymbol('X', n, m)(t)) # issue 2756 assert X.doit() == X def test_dense_conversion(): X = MatrixSymbol('X', 2, 2) assert ImmutableMatrix(X) == ImmutableMatrix(2, 2, lambda i, j: X[i, j]) assert Matrix(X) == Matrix(2, 2, lambda i, j: X[i, j]) def test_free_symbols(): assert (C*D).free_symbols == set((C, D)) def test_zero_matmul(): assert isinstance(S.Zero * MatrixSymbol('X', 2, 2), MatrixExpr) def test_matadd_simplify(): A = MatrixSymbol('A', 1, 1) assert simplify(MatAdd(A, ImmutableMatrix([[sin(x)**2 + cos(x)**2]]))) == \ MatAdd(A, ImmutableMatrix([[1]])) def test_matmul_simplify(): A = MatrixSymbol('A', 1, 1) assert simplify(MatMul(A, ImmutableMatrix([[sin(x)**2 + cos(x)**2]]))) == \ MatMul(A, ImmutableMatrix([[1]])) def test_invariants(): A = MatrixSymbol('A', n, m) B = MatrixSymbol('B', m, l) X = MatrixSymbol('X', n, n) objs = [Identity(n), ZeroMatrix(m, n), A, MatMul(A, B), MatAdd(A, A), Transpose(A), Adjoint(A), Inverse(X), MatPow(X, 2), MatPow(X, -1), MatPow(X, 0)] for obj in objs: assert obj == obj.__class__(*obj.args) def test_indexing(): A = MatrixSymbol('A', n, m) A[1, 2] A[l, k] A[l+1, k+1] def test_MatrixElement_diff(): assert (A[3, 0]*A[0, 0]).diff(A[0, 0]) == A[3, 0] sympy-0.7.4.1/sympy/matrices/expressions/tests/test_funcmatrix.py0000644000175000017500000000060212253362407025547 0ustar georgeskgeorgeskfrom sympy import (symbols, FunctionMatrix, MatrixExpr, Lambda, Matrix) def test_funcmatrix(): i, j = symbols('i,j') X = FunctionMatrix(3, 3, Lambda((i, j), i - j)) assert X[1, 1] == 0 assert X[1, 2] == -1 assert X.shape == (3, 3) assert X.rows == X.cols == 3 assert Matrix(X) == Matrix(3, 3, lambda i, j: i - j) assert isinstance(X*X + X, MatrixExpr) sympy-0.7.4.1/sympy/matrices/expressions/tests/test_matadd.py0000644000175000017500000000027112253362407024623 0ustar georgeskgeorgeskfrom sympy.matrices.expressions import MatrixSymbol, MatAdd X = MatrixSymbol('X', 2, 2) Y = MatrixSymbol('Y', 2, 2) def test_sort_key(): assert MatAdd(Y, X).doit().args == (X, Y) sympy-0.7.4.1/sympy/matrices/expressions/tests/test_transpose.py0000644000175000017500000000213212253362407025405 0ustar georgeskgeorgeskfrom sympy.functions import adjoint, conjugate, transpose from sympy.matrices.expressions import MatrixSymbol, Adjoint, trace, Transpose from sympy.matrices import eye, Matrix from sympy import symbols, S, assuming, Q n, m, l, k, p = symbols('n m l k p', integer=True) A = MatrixSymbol('A', n, m) B = MatrixSymbol('B', m, l) C = MatrixSymbol('C', n, n) def test_transpose(): Sq = MatrixSymbol('Sq', n, n) assert transpose(A) == Transpose(A) assert Transpose(A).shape == (m, n) assert Transpose(A*B).shape == (l, n) assert transpose(Transpose(A)) == A assert isinstance(Transpose(Transpose(A)), Transpose) assert adjoint(Transpose(A)) == Adjoint(Transpose(A)) assert conjugate(Transpose(A)) == Adjoint(A) assert Transpose(eye(3)).doit() == eye(3) assert Transpose(S(5)).doit() == S(5) assert Transpose(Matrix([[1, 2], [3, 4]])).doit() == Matrix([[1, 3], [2, 4]]) assert transpose(trace(Sq)) == trace(Sq) assert trace(Transpose(Sq)) == trace(Sq) assert Transpose(Sq)[0, 1] == Sq[1, 0] assert Transpose(A*B).doit() == Transpose(B) * Transpose(A) sympy-0.7.4.1/sympy/matrices/expressions/tests/test_matmul.py0000644000175000017500000000565212253362407024700 0ustar georgeskgeorgeskfrom sympy.core import I, symbols from sympy.functions import adjoint, transpose from sympy.matrices import Identity, Inverse, Matrix, MatrixSymbol, ZeroMatrix from sympy.matrices.expressions import Adjoint, Transpose, det from sympy.matrices.expressions.matmul import (factor_in_front, remove_ids, MatMul, xxinv, any_zeros, unpack, only_squares) from sympy.strategies import null_safe n, m, l, k = symbols('n m l k', integer=True) A = MatrixSymbol('A', n, m) B = MatrixSymbol('B', m, l) C = MatrixSymbol('C', n, n) D = MatrixSymbol('D', n, n) E = MatrixSymbol('E', m, n) def test_adjoint(): assert adjoint(A*B) == Adjoint(B)*Adjoint(A) assert adjoint(2*A*B) == 2*Adjoint(B)*Adjoint(A) assert adjoint(2*I*C) == -2*I*Adjoint(C) M = Matrix(2, 2, [1, 2 + I, 3, 4]) MA = Matrix(2, 2, [1, 3, 2 - I, 4]) assert adjoint(M) == MA assert adjoint(2*M) == 2*MA assert adjoint(MatMul(2, M)) == MatMul(2, MA) def test_transpose(): assert transpose(A*B) == Transpose(B)*Transpose(A) assert transpose(2*A*B) == 2*Transpose(B)*Transpose(A) assert transpose(2*I*C) == 2*I*Transpose(C) M = Matrix(2, 2, [1, 2 + I, 3, 4]) MT = Matrix(2, 2, [1, 3, 2 + I, 4]) assert transpose(M) == MT assert transpose(2*M) == 2*MT assert transpose(MatMul(2, M)) == MatMul(2, MT) def test_factor_in_front(): assert factor_in_front(MatMul(A, 2, B, evaluate=False)) ==\ MatMul(2, A, B, evaluate=False) def test_remove_ids(): assert remove_ids(MatMul(A, Identity(m), B, evaluate=False)) == \ MatMul(A, B, evaluate=False) assert null_safe(remove_ids)(MatMul(Identity(n), evaluate=False)) == \ MatMul(Identity(n), evaluate=False) def test_xxinv(): assert xxinv(MatMul(D, Inverse(D), D, evaluate=False)) == \ MatMul(Identity(n), D, evaluate=False) def test_any_zeros(): assert any_zeros(MatMul(A, ZeroMatrix(m, k), evaluate=False)) == \ ZeroMatrix(n, k) def test_unpack(): assert unpack(MatMul(A, evaluate=False)) == A x = MatMul(A, B) assert unpack(x) == x def test_only_squares(): A = MatrixSymbol('A', n, m) C = MatrixSymbol('C', n, n) D = MatrixSymbol('D', n, n) assert only_squares(C) == [C] assert only_squares(C, D) == [C, D] assert only_squares(C, A, A.T, D) == [C, A*A.T, D] def test_determinant(): A = MatrixSymbol('A', n, m) C = MatrixSymbol('C', n, n) D = MatrixSymbol('D', n, n) assert det(2*C) == 2**n*det(C) assert det(2*C*D) == 2**n*det(C)*det(D) assert det(3*C*A*A.T*D) == 3**n*det(C)*det(A*A.T)*det(D) def test_doit(): C = MatrixSymbol('C', n, n) D = MatrixSymbol('D', n, n) assert MatMul(C, 2, D).args == (C, 2, D) assert MatMul(C, 2, D).doit().args == (2, C, D) assert MatMul(C, Transpose(D*C)).args == (C, Transpose(D*C)) assert MatMul(C, Transpose(D*C)).doit(deep=True).args == (C, C.T, D.T) sympy-0.7.4.1/sympy/matrices/expressions/tests/__init__.py0000644000175000017500000000000012253362407024057 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/matrices/expressions/tests/test_hadamard.py0000644000175000017500000000346012253362407025135 0ustar georgeskgeorgeskfrom sympy.core import symbols from sympy.utilities.pytest import raises from sympy.matrices import ShapeError, MatrixSymbol from sympy.matrices.expressions import HadamardProduct, hadamard_product n, m, k = symbols('n,m,k') Z = MatrixSymbol('Z', n, n) A = MatrixSymbol('A', n, m) B = MatrixSymbol('B', n, m) C = MatrixSymbol('C', m, k) def test_HadamardProduct(): assert HadamardProduct(A, B, A).shape == A.shape raises(ShapeError, lambda: HadamardProduct(A, B.T)) raises(TypeError, lambda: HadamardProduct(A, n)) raises(TypeError, lambda: HadamardProduct(A, 1)) assert HadamardProduct(A, 2*B, -A)[1, 1] == \ -2 * A[1, 1] * B[1, 1] * A[1, 1] mix = HadamardProduct(Z*A, B)*C assert mix.shape == (n, k) assert set(HadamardProduct(A, B, A).T.args) == set((A.T, A.T, B.T)) def test_HadamardProduct_isnt_commutative(): assert HadamardProduct(A, B) != HadamardProduct(B, A) def test_mixed_indexing(): X = MatrixSymbol('X', 2, 2) Y = MatrixSymbol('Y', 2, 2) Z = MatrixSymbol('Z', 2, 2) assert (X*HadamardProduct(Y, Z))[0, 0] == \ X[0, 0]*Y[0, 0]*Z[0, 0] + X[0, 1]*Y[1, 0]*Z[1, 0] def test_canonicalize(): X = MatrixSymbol('X', 2, 2) expr = HadamardProduct(X, check=False) assert isinstance(expr, HadamardProduct) expr2 = expr.doit() # unpack is called assert isinstance(expr2, MatrixSymbol) def test_hadamard(): m, n, p = symbols('m, n, p', integer=True) A = MatrixSymbol('A', m, n) B = MatrixSymbol('B', m, n) C = MatrixSymbol('C', m, p) with raises(TypeError): hadamard_product() assert hadamard_product(A) == A assert isinstance(hadamard_product(A, B), HadamardProduct) assert hadamard_product(A, B).doit() == hadamard_product(A, B) with raises(ShapeError): hadamard_product(A, C) sympy-0.7.4.1/sympy/matrices/expressions/__init__.py0000644000175000017500000000120312253362407022723 0ustar georgeskgeorgesk""" A module which handles Matrix Expressions """ from .slice import MatrixSlice from .blockmatrix import BlockMatrix, BlockDiagMatrix, block_collapse, blockcut 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, trace from .determinant import Determinant, det from .transpose import Transpose from .adjoint import Adjoint from .hadamard import hadamard_product, HadamardProduct from .diagonal import DiagonalMatrix, DiagonalOf sympy-0.7.4.1/sympy/matrices/expressions/factorizations.py0000644000175000017500000000213112253362407024224 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.matrices.expressions import MatrixExpr from sympy import Q class Factorization(MatrixExpr): arg = property(lambda self: self.args[0]) shape = property(lambda self: self.arg.shape) class LofLU(Factorization): predicates = Q.lower_triangular, class UofLU(Factorization): predicates = Q.upper_triangular, class LofCholesky(LofLU): pass class UofCholesky(UofLU): pass class QofQR(Factorization): predicates = Q.orthogonal, class RofQR(Factorization): predicates = Q.upper_triangular, class EigenVectors(Factorization): predicates = Q.orthogonal, class EigenValues(Factorization): predicates = Q.diagonal, class UofSVD(Factorization): predicates = Q.orthogonal, class SofSVD(Factorization): predicates = Q.diagonal, class VofSVD(Factorization): predicates = Q.orthogonal, def lu(expr): return LofLU(expr), UofLU(expr) def qr(expr): return QofQR(expr), RofQR(expr) def eig(expr): return EigenValues(expr), EigenVectors(expr) def svd(expr): return UofSVD(expr), SofSVD(expr), VofSVD(expr) sympy-0.7.4.1/sympy/matrices/benchmarks/0000755000175000017500000000000012253362407020351 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/matrices/benchmarks/bench_matrix.py0000644000175000017500000000046412253362407023372 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import eye, zeros, Integer i3 = Integer(3) M = eye(100) def timeit_Matrix__getitem_ii(): M[3, 3] def timeit_Matrix__getitem_II(): M[i3, i3] def timeit_Matrix__getslice(): M[:, :] def timeit_Matrix_zeronm(): zeros(100, 100) sympy-0.7.4.1/sympy/matrices/dense.py0000644000175000017500000012540512253362407017713 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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.utilities.decorator import doctest_depends_on from sympy.matrices.matrices import (MatrixBase, ShapeError, a2idx, classof) 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] Matrix([ [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() @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, range(9)) >>> m Matrix([ [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) Matrix([[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) Matrix([ [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_determinant(self): return self.det() def _eval_transpose(self): """Matrix transposition. Examples ======== >>> from sympy import Matrix, I >>> m=Matrix(((1, 2+I), (3, 4))) >>> m Matrix([ [1, 2 + I], [3, 4]]) >>> m.transpose() Matrix([ [ 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(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 Matrix([ [0, 1], [2, 3]]) >>> m.applyfunc(lambda i: 2*i) Matrix([ [0, 2], [4, 6]]) """ if not callable(f): 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 Matrix([ [1, 1, 1], [1, 1, 1]]) >>> m.reshape(1, 6) Matrix([[1, 1, 1, 1, 1, 1]]) >>> m.reshape(3, 2) Matrix([ [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 Matrix([ [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, [cls._sympify(0)]*r*c) @classmethod def eye(cls, n): """Return an n x n identity matrix.""" n = as_int(n) mat = [cls._sympify(0)]*n*n mat[::n + 1] = [cls._sympify(1)]*n return cls._new(n, n, mat) ############################ # Mutable matrix operators # ############################ @call_highest_priority('__radd__') def __add__(self, other): return super(DenseMatrix, self).__add__(_force_mutable(other)) @call_highest_priority('__add__') def __radd__(self, other): return super(DenseMatrix, self).__radd__(_force_mutable(other)) @call_highest_priority('__rsub__') def __sub__(self, other): return super(DenseMatrix, self).__sub__(_force_mutable(other)) @call_highest_priority('__sub__') def __rsub__(self, other): return super(DenseMatrix, self).__rsub__(_force_mutable(other)) @call_highest_priority('__rmul__') def __mul__(self, other): return super(DenseMatrix, self).__mul__(_force_mutable(other)) @call_highest_priority('__mul__') def __rmul__(self, other): return super(DenseMatrix, self).__rmul__(_force_mutable(other)) @call_highest_priority('__div__') def __div__(self, other): return super(DenseMatrix, self).__div__(_force_mutable(other)) @call_highest_priority('__truediv__') def __truediv__(self, other): return super(DenseMatrix, self).__truediv__(_force_mutable(other)) @call_highest_priority('__rpow__') def __pow__(self, other): return super(DenseMatrix, self).__pow__(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 class MutableDenseMatrix(DenseMatrix, MatrixBase): @classmethod def _new(cls, *args, **kwargs): rows, cols, flat_list = cls._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 as_mutable(self): return self.copy() def __setitem__(self, key, value): """ Examples ======== >>> from sympy import Matrix, I, zeros, ones >>> m = Matrix(((1, 2+I), (3, 4))) >>> m Matrix([ [1, 2 + I], [3, 4]]) >>> m[1, 0] = 9 >>> m Matrix([ [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 Matrix([ [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 Matrix([ [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 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 Matrix([ [0, 1, 0], [2, 3, 0], [4, 5, 1]]) >>> I[0, 1] = M >>> I Matrix([ [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] 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 Matrix([ [1, 0, 0], [2, 1, 0], [0, 0, 1]]) >>> I[1, :2] = [[3, 4]] >>> I Matrix([ [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)) def zip_row_op(self, i, k, f): """In-place operation on row ``i`` using two-arg functor whose args are interpreted as ``(self[i, j], self[k, j])``. Examples ======== >>> from sympy.matrices import eye >>> M = eye(3) >>> M.zip_row_op(1, 0, lambda v, u: v + 2*u); M Matrix([ [1, 0, 0], [2, 1, 0], [0, 0, 1]]) See Also ======== row row_op col_op """ i0 = i*self.cols k0 = k*self.cols ri = self._mat[i0: i0 + self.cols] rk = self._mat[k0: k0 + self.cols] self._mat[i0: i0 + self.cols] = [ f(x, y) for x, y in zip(ri, rk) ] 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 Matrix([ [1, 0, 0], [2, 1, 0], [0, 0, 1]]) See Also ======== row zip_row_op col_op """ i0 = i*self.cols ri = self._mat[i0: i0 + self.cols] self._mat[i0: i0 + self.cols] = [ f(x, j) for x, j in zip(ri, list(range(self.cols))) ] 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 Matrix([ [1, 2, 0], [0, 1, 0], [0, 0, 1]]) See Also ======== col row_op """ self._mat[j::self.cols] = list(map(lambda t: f(*t), list(zip(self._mat[j::self.cols], list(range(self.rows)))))) 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 Matrix([ [0, 1], [1, 0]]) >>> M.row_swap(0, 1) >>> M Matrix([ [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] 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 Matrix([ [1, 0], [1, 0]]) >>> M.col_swap(0, 1) >>> M Matrix([ [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] def row_del(self, i): """Delete the given row. Examples ======== >>> from sympy.matrices import eye >>> M = eye(3) >>> M.row_del(1) >>> M Matrix([ [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 def col_del(self, i): """Delete the given column. Examples ======== >>> from sympy.matrices import eye >>> M = eye(3) >>> M.col_del(1) >>> M Matrix([ [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 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) 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] ########### 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 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 @doctest_depends_on(modules=('numpy',)) 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) [_0 _1 _2] If you want multiple symarrays to contain distinct symbols, you *must* provide unique prefixes: >>> a = symarray('', 3) >>> b = symarray('', 3) >>> a[0] == b[0] True >>> a = symarray('a', 3) >>> b = symarray('b', 3) >>> a[0] == b[0] False Creating symarrays with a prefix: >>> symarray('a', 3) [a_0 a_1 a_2] For more than one dimension, the shape must be given as a tuple: >>> symarray('a', (2, 3)) [[a_0_0 a_0_1 a_0_2] [a_1_0 a_1_1 a_1_2]] >>> symarray('a', (2, 3, 2)) [[[a_0_0_0 a_0_0_1] [a_0_1_0 a_0_1_1] [a_0_2_0 a_0_2_1]] [[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 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) Matrix([ [ 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) Matrix([ [ 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) 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) Matrix([ [ 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) Matrix([ [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) 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) Matrix([ [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) Matrix([ [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 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) Matrix([ [ 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]) 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) 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) 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) 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) Matrix([ [1, 0, 0], [0, 2, 0], [0, 0, 3]]) >>> diag(*[1, 2, 3]) Matrix([ [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) Matrix([ [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) Matrix([ [1, 0], [2, 0], [3, 0], [0, 4]]) >>> diag([[1, 2, 3]], 4) Matrix([ [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) Matrix([ [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)) >>> from sympy.matrices import ImmutableMatrix >>> type(diag(1, cls=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(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) 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) Matrix([ [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 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 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 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 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) 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() 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 prng.shuffle(m._mat) return m sympy-0.7.4.1/sympy/matrices/tests/0000755000175000017500000000000012253362407017376 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/matrices/tests/test_immutable.py0000644000175000017500000000540512253362407022772 0ustar georgeskgeorgeskfrom sympy import ImmutableMatrix, Matrix, eye, zeros from sympy.abc import x, y from sympy.utilities.pytest import raises IM = ImmutableMatrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) ieye = ImmutableMatrix(eye(3)) def test_immutable_creation(): assert IM.shape == (3, 3) assert IM[1, 2] == 6 assert IM[2, 2] == 9 def test_immutability(): with raises(TypeError): IM[2, 2] = 5 def test_slicing(): assert IM[1, :] == ImmutableMatrix([[4, 5, 6]]) assert IM[:2, :2] == ImmutableMatrix([[1, 2], [4, 5]]) def test_subs(): A = ImmutableMatrix([[1, 2], [3, 4]]) B = ImmutableMatrix([[1, 2], [x, 4]]) C = ImmutableMatrix([[-x, x*y], [-(x + y), y**2]]) assert B.subs(x, 3) == A assert (x*B).subs(x, 3) == 3*A assert (x*eye(2) + B).subs(x, 3) == 3*eye(2) + A assert C.subs([[x, -1], [y, -2]]) == A assert C.subs([(x, -1), (y, -2)]) == A assert C.subs({x: -1, y: -2}) == A assert C.subs({x: y - 1, y: x - 1}, simultaneous=True) == \ ImmutableMatrix([[1 - y, (x - 1)*(y - 1)], [2 - x - y, (x - 1)**2]]) def test_as_immutable(): X = Matrix([[1, 2], [3, 4]]) assert X.as_immutable() == ImmutableMatrix([[1, 2], [3, 4]]) def test_function_return_types(): # Lets ensure that decompositions of immutable matrices remain immutable # I.e. do MatrixBase methods return the correct class? X = ImmutableMatrix([[1, 2], [3, 4]]) Y = ImmutableMatrix([[1], [0]]) q, r = X.QRdecomposition() assert (type(q), type(r)) == (ImmutableMatrix, ImmutableMatrix) assert type(X.LUsolve(Y)) == ImmutableMatrix assert type(X.QRsolve(Y)) == ImmutableMatrix X = ImmutableMatrix([[1, 2], [2, 1]]) assert X.T == X assert X.is_symmetric assert type(X.cholesky()) == ImmutableMatrix L, D = X.LDLdecomposition() assert (type(L), type(D)) == (ImmutableMatrix, ImmutableMatrix) assert X.is_diagonalizable() assert X.berkowitz_det() == -3 assert X.norm(2) == 3 assert type(X.eigenvects()[0][2][0]) == ImmutableMatrix assert type(zeros(3, 3).as_immutable().nullspace()[0]) == ImmutableMatrix X = ImmutableMatrix([[1, 0], [2, 1]]) assert type(X.lower_triangular_solve(Y)) == ImmutableMatrix assert type(X.T.upper_triangular_solve(Y)) == ImmutableMatrix assert type(X.minorMatrix(0, 0)) == ImmutableMatrix # Issue 3180 # http://code.google.com/p/sympy/issues/detail?id=3180 # Test that Immutable _op_ Immutable => Immutable and not MatExpr def test_immutable_evaluation(): X = ImmutableMatrix(eye(3)) A = ImmutableMatrix(3, 3, range(9)) assert isinstance(X + A, ImmutableMatrix) assert isinstance(X * A, ImmutableMatrix) assert isinstance(X * 2, ImmutableMatrix) assert isinstance(2 * X, ImmutableMatrix) assert isinstance(A**2, ImmutableMatrix) sympy-0.7.4.1/sympy/matrices/tests/test_interactions.py0000644000175000017500000000345612253362407023521 0ustar georgeskgeorgesk""" We have a few different kind of Matrices Matrix, ImmutableMatrix, MatrixExpr Here we test the extent to which they cooperate """ from sympy import symbols from sympy.matrices import (Matrix, MatrixSymbol, eye, Identity, ImmutableMatrix) from sympy.matrices.expressions import MatrixExpr, MatAdd from sympy.matrices.matrices import classof from sympy.utilities.pytest import raises SM = MatrixSymbol('X', 3, 3) MM = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) IM = ImmutableMatrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) meye = eye(3) imeye = ImmutableMatrix(eye(3)) ideye = Identity(3) a, b, c = symbols('a,b,c') def test_IM_MM(): assert isinstance(MM + IM, ImmutableMatrix) assert isinstance(IM + MM, ImmutableMatrix) assert isinstance(2*IM + MM, ImmutableMatrix) assert MM.equals(IM) def test_ME_MM(): assert isinstance(Identity(3) + MM, MatrixExpr) assert isinstance(SM + MM, MatAdd) assert isinstance(MM + SM, MatAdd) assert (Identity(3) + MM)[1, 1] == 6 def test_equality(): a, b, c = Identity(3), eye(3), ImmutableMatrix(eye(3)) for x in [a, b, c]: for y in [a, b, c]: assert x.equals(y) def test_matrix_symbol_MM(): X = MatrixSymbol('X', 3, 3) Y = eye(3) + X assert Y[1, 1] == 1 + X[1, 1] def test_indexing_interactions(): assert (a * IM)[1, 1] == 5*a assert (SM + IM)[1, 1] == SM[1, 1] + IM[1, 1] assert (SM * IM)[1, 1] == SM[1, 0]*IM[0, 1] + SM[1, 1]*IM[1, 1] + \ SM[1, 2]*IM[2, 1] def test_classof(): A = Matrix(3, 3, range(9)) B = ImmutableMatrix(3, 3, range(9)) C = MatrixSymbol('C', 3, 3) assert classof(A, A) == Matrix assert classof(B, B) == ImmutableMatrix assert classof(A, B) == ImmutableMatrix assert classof(B, A) == ImmutableMatrix raises(TypeError, lambda: classof(A, C)) sympy-0.7.4.1/sympy/matrices/tests/test_matrices.py0000644000175000017500000021031212253362407022615 0ustar georgeskgeorgeskimport collections from sympy import ( Abs, E, Float, I, Integer, Max, Min, N, Poly, Pow, PurePoly, Rational, S, Symbol, cos, exp, oo, pi, signsimp, simplify, sin, sqrt, symbols, sympify, trigsimp) from sympy.matrices.matrices import (ShapeError, MatrixError, NonSquareMatrixError, DeferredVector) from sympy.matrices import ( GramSchmidt, ImmutableMatrix, ImmutableSparseMatrix, Matrix, SparseMatrix, casoratian, diag, eye, hessian, matrix_multiply_elementwise, ones, randMatrix, rot_axis1, rot_axis2, rot_axis3, wronskian, zeros) from sympy.core.compatibility import long from sympy.utilities.iterables import flatten, capture from sympy.utilities.pytest import raises, XFAIL, slow from sympy.abc import x, y, z # don't re-order this list classes = (Matrix, SparseMatrix, ImmutableMatrix, ImmutableSparseMatrix) def test_args(): for c, cls in enumerate(classes): m = cls.zeros(3, 2) # all should give back the same type of arguments, e.g. ints for shape assert m.shape == (3, 2) and all(type(i) is int for i in m.shape) assert m.rows == 3 and type(m.rows) is int assert m.cols == 2 and type(m.cols) is int if not c % 2: assert type(m._mat) is list else: assert type(m._smat) is dict def test_division(): v = Matrix(1, 2, [x, y]) assert v.__div__(z) == Matrix(1, 2, [x/z, y/z]) assert v.__truediv__(z) == Matrix(1, 2, [x/z, y/z]) assert v/z == Matrix(1, 2, [x/z, y/z]) def test_sum(): m = Matrix([[1, 2, 3], [x, y, x], [2*y, -50, z*x]]) assert m + m == Matrix([[2, 4, 6], [2*x, 2*y, 2*x], [4*y, -100, 2*z*x]]) n = Matrix(1, 2, [1, 2]) raises(ShapeError, lambda: m + n) def test_addition(): a = Matrix(( (1, 2), (3, 1), )) b = Matrix(( (1, 2), (3, 0), )) assert a + b == a.add(b) == Matrix([[2, 4], [6, 1]]) def test_multiplication(): a = Matrix(( (1, 2), (3, 1), (0, 6), )) b = Matrix(( (1, 2), (3, 0), )) c = a*b assert c[0, 0] == 7 assert c[0, 1] == 2 assert c[1, 0] == 6 assert c[1, 1] == 6 assert c[2, 0] == 18 assert c[2, 1] == 0 h = matrix_multiply_elementwise(a, c) assert h == a.multiply_elementwise(c) assert h[0, 0] == 7 assert h[0, 1] == 4 assert h[1, 0] == 18 assert h[1, 1] == 6 assert h[2, 0] == 0 assert h[2, 1] == 0 raises(ShapeError, lambda: matrix_multiply_elementwise(a, b)) c = b * Symbol("x") assert isinstance(c, Matrix) assert c[0, 0] == x assert c[0, 1] == 2*x assert c[1, 0] == 3*x assert c[1, 1] == 0 c2 = x * b assert c == c2 c = 5 * b assert isinstance(c, Matrix) assert c[0, 0] == 5 assert c[0, 1] == 2*5 assert c[1, 0] == 3*5 assert c[1, 1] == 0 def test_power(): raises(NonSquareMatrixError, lambda: Matrix((1, 2))**2) R = Rational A = Matrix([[2, 3], [4, 5]]) assert (A**-3)[:] == [R(-269)/8, R(153)/8, R(51)/2, R(-29)/2] assert (A**5)[:] == [6140, 8097, 10796, 14237] A = Matrix([[2, 1, 3], [4, 2, 4], [6, 12, 1]]) assert (A**3)[:] == [290, 262, 251, 448, 440, 368, 702, 954, 433] assert A**0 == eye(3) assert A**1 == A assert (Matrix([[2]]) ** 100)[0, 0] == 2**100 assert eye(2)**10000000 == eye(2) assert Matrix([[1, 2], [3, 4]])**Integer(2) == Matrix([[7, 10], [15, 22]]) A = Matrix([[33, 24], [48, 57]]) assert (A**(S(1)/2))[:] == [5, 2, 4, 7] A = Matrix([[0, 4], [-1, 5]]) assert (A**(S(1)/2))**2 == A def test_creation(): raises(ValueError, lambda: Matrix(5, 5, range(20))) raises(IndexError, lambda: Matrix((1, 2))[2]) with raises(IndexError): Matrix((1, 2))[1:2] = 5 with raises(IndexError): Matrix((1, 2))[3] = 5 a = Matrix([[x, 0], [0, 0]]) m = a assert m.cols == m.rows assert m.cols == 2 assert m[:] == [x, 0, 0, 0] b = Matrix(2, 2, [x, 0, 0, 0]) m = b assert m.cols == m.rows assert m.cols == 2 assert m[:] == [x, 0, 0, 0] assert a == b assert Matrix(b) == b c = Matrix(( Matrix(( (1, 2, 3), (4, 5, 6) )), (7, 8, 9) )) assert c.cols == 3 assert c.rows == 3 assert c[:] == [1, 2, 3, 4, 5, 6, 7, 8, 9] assert Matrix(eye(2)) == eye(2) assert ImmutableMatrix(ImmutableMatrix(eye(2))) == ImmutableMatrix(eye(2)) assert ImmutableMatrix(c) == c.as_immutable() assert Matrix(ImmutableMatrix(c)) == ImmutableMatrix(c).as_mutable() assert c is not Matrix(c) def test_tolist(): lst = [[S.One, S.Half, x*y, S.Zero], [x, y, z, x**2], [y, -S.One, z*x, 3]] m = Matrix(lst) assert m.tolist() == lst def test_as_mutable(): assert zeros(0, 3).as_mutable() == zeros(0, 3) assert zeros(0, 3).as_immutable() == ImmutableMatrix(zeros(0, 3)) def test_determinant(): for M in [Matrix(), Matrix([[1]])]: assert ( M.det() == M.det_bareis() == M.berkowitz_det() == M.det_LU_decomposition() == 1) M = Matrix(( (-3, 2), ( 8, -5) )) assert M.det(method="bareis") == -1 assert M.det(method="berkowitz") == -1 M = Matrix(( (x, 1), (y, 2*y) )) assert M.det(method="bareis") == 2*x*y - y assert M.det(method="berkowitz") == 2*x*y - y M = Matrix(( (1, 1, 1), (1, 2, 3), (1, 3, 6) )) assert M.det(method="bareis") == 1 assert M.det(method="berkowitz") == 1 M = Matrix(( ( 3, -2, 0, 5), (-2, 1, -2, 2), ( 0, -2, 5, 0), ( 5, 0, 3, 4) )) assert M.det(method="bareis") == -289 assert M.det(method="berkowitz") == -289 M = Matrix(( ( 1, 2, 3, 4), ( 5, 6, 7, 8), ( 9, 10, 11, 12), (13, 14, 15, 16) )) assert M.det(method="bareis") == 0 assert M.det(method="berkowitz") == 0 M = Matrix(( (3, 2, 0, 0, 0), (0, 3, 2, 0, 0), (0, 0, 3, 2, 0), (0, 0, 0, 3, 2), (2, 0, 0, 0, 3) )) assert M.det(method="bareis") == 275 assert M.det(method="berkowitz") == 275 M = Matrix(( (1, 0, 1, 2, 12), (2, 0, 1, 1, 4), (2, 1, 1, -1, 3), (3, 2, -1, 1, 8), (1, 1, 1, 0, 6) )) assert M.det(method="bareis") == -55 assert M.det(method="berkowitz") == -55 M = Matrix(( (-5, 2, 3, 4, 5), ( 1, -4, 3, 4, 5), ( 1, 2, -3, 4, 5), ( 1, 2, 3, -2, 5), ( 1, 2, 3, 4, -1) )) assert M.det(method="bareis") == 11664 assert M.det(method="berkowitz") == 11664 M = Matrix(( ( 2, 7, -1, 3, 2), ( 0, 0, 1, 0, 1), (-2, 0, 7, 0, 2), (-3, -2, 4, 5, 3), ( 1, 0, 0, 0, 1) )) assert M.det(method="bareis") == 123 assert M.det(method="berkowitz") == 123 M = Matrix(( (x, y, z), (1, 0, 0), (y, z, x) )) assert M.det(method="bareis") == z**2 - x*y assert M.det(method="berkowitz") == z**2 - x*y def test_det_LU_decomposition(): for M in [Matrix(), Matrix([[1]])]: assert M.det(method="det_LU") == 1 M = Matrix(( (-3, 2), ( 8, -5) )) assert M.det(method="det_LU") == -1 M = Matrix(( (x, 1), (y, 2*y) )) assert M.det(method="det_LU") == 2*x*y - y M = Matrix(( (1, 1, 1), (1, 2, 3), (1, 3, 6) )) assert M.det(method="det_LU") == 1 M = Matrix(( ( 3, -2, 0, 5), (-2, 1, -2, 2), ( 0, -2, 5, 0), ( 5, 0, 3, 4) )) assert M.det(method="det_LU") == -289 M = Matrix(( (3, 2, 0, 0, 0), (0, 3, 2, 0, 0), (0, 0, 3, 2, 0), (0, 0, 0, 3, 2), (2, 0, 0, 0, 3) )) assert M.det(method="det_LU") == 275 M = Matrix(( (1, 0, 1, 2, 12), (2, 0, 1, 1, 4), (2, 1, 1, -1, 3), (3, 2, -1, 1, 8), (1, 1, 1, 0, 6) )) assert M.det(method="det_LU") == -55 M = Matrix(( (-5, 2, 3, 4, 5), ( 1, -4, 3, 4, 5), ( 1, 2, -3, 4, 5), ( 1, 2, 3, -2, 5), ( 1, 2, 3, 4, -1) )) assert M.det(method="det_LU") == 11664 M = Matrix(( ( 2, 7, -1, 3, 2), ( 0, 0, 1, 0, 1), (-2, 0, 7, 0, 2), (-3, -2, 4, 5, 3), ( 1, 0, 0, 0, 1) )) assert M.det(method="det_LU") == 123 M = Matrix(( (x, y, z), (1, 0, 0), (y, z, x) )) assert M.det(method="det_LU") == z**2 - x*y def test_berkowitz_minors(): B = Matrix(2, 2, [1, 2, 2, 1]) assert B.berkowitz_minors() == (1, -3) def test_submatrix(): m0 = eye(4) assert m0[:3, :3] == eye(3) assert m0[2:4, 0:2] == zeros(2) m1 = Matrix(3, 3, lambda i, j: i + j) assert m1[0, :] == Matrix(1, 3, (0, 1, 2)) assert m1[1:3, 1] == Matrix(2, 1, (2, 3)) m2 = Matrix([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15]]) assert m2[:, -1] == Matrix(4, 1, [3, 7, 11, 15]) assert m2[-2:, :] == Matrix([[8, 9, 10, 11], [12, 13, 14, 15]]) def test_submatrix_assignment(): m = zeros(4) m[2:4, 2:4] = eye(2) assert m == Matrix(((0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1))) m[:2, :2] = eye(2) assert m == eye(4) m[:, 0] = Matrix(4, 1, (1, 2, 3, 4)) assert m == Matrix(((1, 0, 0, 0), (2, 1, 0, 0), (3, 0, 1, 0), (4, 0, 0, 1))) m[:, :] = zeros(4) assert m == zeros(4) m[:, :] = [(1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12), (13, 14, 15, 16)] assert m == Matrix(((1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12), (13, 14, 15, 16))) m[:2, 0] = [0, 0] assert m == Matrix(((0, 2, 3, 4), (0, 6, 7, 8), (9, 10, 11, 12), (13, 14, 15, 16))) def test_extract(): m = Matrix(4, 3, lambda i, j: i*3 + j) assert m.extract([0, 1, 3], [0, 1]) == Matrix(3, 2, [0, 1, 3, 4, 9, 10]) assert m.extract([0, 3], [0, 0, 2]) == Matrix(2, 3, [0, 0, 2, 9, 9, 11]) assert m.extract(range(4), range(3)) == m raises(IndexError, lambda: m.extract([4], [0])) raises(IndexError, lambda: m.extract([0], [3])) def test_reshape(): m0 = eye(3) assert m0.reshape(1, 9) == Matrix(1, 9, (1, 0, 0, 0, 1, 0, 0, 0, 1)) m1 = Matrix(3, 4, lambda i, j: i + j) assert m1.reshape( 4, 3) == Matrix(((0, 1, 2), (3, 1, 2), (3, 4, 2), (3, 4, 5))) assert m1.reshape(2, 6) == Matrix(((0, 1, 2, 3, 1, 2), (3, 4, 2, 3, 4, 5))) def test_applyfunc(): m0 = eye(3) assert m0.applyfunc(lambda x: 2*x) == eye(3)*2 assert m0.applyfunc(lambda x: 0) == zeros(3) def test_expand(): m0 = Matrix([[x*(x + y), 2], [((x + y)*y)*x, x*(y + x*(x + y))]]) # Test if expand() returns a matrix m1 = m0.expand() assert m1 == Matrix( [[x*y + x**2, 2], [x*y**2 + y*x**2, x*y + y*x**2 + x**3]]) a = Symbol('a', real=True) assert Matrix([exp(I*a)]).expand(complex=True) == \ Matrix([cos(a) + I*sin(a)]) assert Matrix([[0, 1, 2], [0, 0, -1], [0, 0, 0]]).exp() == Matrix([ [1, 1, Rational(3, 2)], [0, 1, -1], [0, 0, 1]] ) def test_random(): M = randMatrix(3, 3) M = randMatrix(3, 3, seed=3) M = randMatrix(3, 4, 0, 150) M = randMatrix(3, symmetric=True) S = M.copy() S.simplify() assert S == M # doesn't fail when elements are Numbers, not int def test_LUdecomp(): testmat = Matrix([[0, 2, 5, 3], [3, 3, 7, 4], [8, 4, 0, 2], [-2, 6, 3, 4]]) L, U, p = testmat.LUdecomposition() assert L.is_lower assert U.is_upper assert (L*U).permuteBkwd(p) - testmat == zeros(4) testmat = Matrix([[6, -2, 7, 4], [0, 3, 6, 7], [1, -2, 7, 4], [-9, 2, 6, 3]]) L, U, p = testmat.LUdecomposition() assert L.is_lower assert U.is_upper assert (L*U).permuteBkwd(p) - testmat == zeros(4) M = Matrix(((1, x, 1), (2, y, 0), (y, 0, z))) L, U, p = M.LUdecomposition() assert L.is_lower assert U.is_upper assert (L*U).permuteBkwd(p) - M == zeros(3) mL = Matrix(( (1, 0, 0), (2, 3, 0), )) assert mL.is_lower is True assert mL.is_upper is False mU = Matrix(( (1, 2, 3), (0, 4, 5), )) assert mU.is_lower is False assert mU.is_upper is True # test FF LUdecomp M = Matrix([[1, 3, 3], [3, 2, 6], [3, 2, 2]]) P, L, Dee, U = M.LUdecompositionFF() assert P*M == L*Dee.inv()*U M = Matrix([[1, 2, 3, 4], [3, -1, 2, 3], [3, 1, 3, -2], [6, -1, 0, 2]]) P, L, Dee, U = M.LUdecompositionFF() assert P*M == L*Dee.inv()*U M = Matrix([[0, 0, 1], [2, 3, 0], [3, 1, 4]]) P, L, Dee, U = M.LUdecompositionFF() assert P*M == L*Dee.inv()*U def test_LUsolve(): 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) assert soln == x A = Matrix([[0, -1, 2], [5, 10, 7], [8, 3, 4]]) x = Matrix(3, 1, [-1, 2, 5]) b = A*x soln = A.LUsolve(b) assert soln == x def test_QRsolve(): A = Matrix([[2, 3, 5], [3, 6, 2], [8, 3, 6]]) x = Matrix(3, 1, [3, 7, 5]) b = A*x soln = A.QRsolve(b) assert soln == x x = Matrix([[1, 2], [3, 4], [5, 6]]) b = A*x soln = A.QRsolve(b) assert soln == x A = Matrix([[0, -1, 2], [5, 10, 7], [8, 3, 4]]) x = Matrix(3, 1, [-1, 2, 5]) b = A*x soln = A.QRsolve(b) assert soln == x x = Matrix([[7, 8], [9, 10], [11, 12]]) b = A*x soln = A.QRsolve(b) assert soln == x def test_inverse(): A = eye(4) assert A.inv() == eye(4) assert A.inv(method="LU") == eye(4) assert A.inv(method="ADJ") == eye(4) A = Matrix([[2, 3, 5], [3, 6, 2], [8, 3, 6]]) Ainv = A.inv() assert A*Ainv == eye(3) assert A.inv(method="LU") == Ainv assert A.inv(method="ADJ") == Ainv # test that immutability is not a problem cls = ImmutableMatrix m = cls([[48, 49, 31], [ 9, 71, 94], [59, 28, 65]]) assert all(type(m.inv(s)) is cls for s in 'GE ADJ LU'.split()) cls = ImmutableSparseMatrix m = cls([[48, 49, 31], [ 9, 71, 94], [59, 28, 65]]) assert all(type(m.inv(s)) is cls for s in 'CH LDL'.split()) def test_matrix_inverse_mod(): A = Matrix(2, 1, [1, 0]) raises(NonSquareMatrixError, lambda: A.inv_mod(2)) A = Matrix(2, 2, [1, 0, 0, 0]) raises(ValueError, lambda: A.inv_mod(2)) A = Matrix(2, 2, [1, 2, 3, 4]) Ai = Matrix(2, 2, [1, 1, 0, 1]) assert A.inv_mod(3) == Ai A = Matrix(2, 2, [1, 0, 0, 1]) assert A.inv_mod(2) == A def test_util(): R = Rational v1 = Matrix(1, 3, [1, 2, 3]) v2 = Matrix(1, 3, [3, 4, 5]) assert v1.norm() == sqrt(14) assert v1.project(v2) == Matrix(1, 3, [R(39)/25, R(52)/25, R(13)/5]) assert Matrix.zeros(1, 2) == Matrix(1, 2, [0, 0]) assert ones(1, 2) == Matrix(1, 2, [1, 1]) assert v1.copy() == v1 # cofactor assert eye(3) == eye(3).cofactorMatrix() test = Matrix([[1, 3, 2], [2, 6, 3], [2, 3, 6]]) assert test.cofactorMatrix() == \ Matrix([[27, -6, -6], [-12, 2, 3], [-3, 1, 0]]) test = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) assert test.cofactorMatrix() == \ Matrix([[-3, 6, -3], [6, -12, 6], [-3, 6, -3]]) def test_jacobian_hessian(): L = Matrix(1, 2, [x**2*y, 2*y**2 + x*y]) syms = [x, y] assert L.jacobian(syms) == Matrix([[2*x*y, x**2], [y, 4*y + x]]) L = Matrix(1, 2, [x, x**2*y**3]) assert L.jacobian(syms) == Matrix([[1, 0], [2*x*y**3, x**2*3*y**2]]) f = x**2*y syms = [x, y] assert hessian(f, syms) == Matrix([[2*y, 2*x], [2*x, 0]]) f = x**2*y**3 assert hessian(f, syms) == \ Matrix([[2*y**3, 6*x*y**2], [6*x*y**2, 6*x**2*y]]) f = z + x*y**2 g = x**2 + 2*y**3 ans = Matrix([[0, 2*y], [2*y, 2*x]]) assert ans == hessian(f, Matrix([x, y])) assert ans == hessian(f, Matrix([x, y]).T) assert hessian(f, (y, x), [g]) == Matrix([ [ 0, 6*y**2, 2*x], [6*y**2, 2*x, 2*y], [ 2*x, 2*y, 0]]) def test_QR(): A = Matrix([[1, 2], [2, 3]]) Q, S = A.QRdecomposition() R = Rational assert Q == Matrix([ [ 5**R(-1, 2), (R(2)/5)*(R(1)/5)**R(-1, 2)], [2*5**R(-1, 2), (-R(1)/5)*(R(1)/5)**R(-1, 2)]]) assert S == Matrix([[5**R(1, 2), 8*5**R(-1, 2)], [0, (R(1)/5)**R(1, 2)]]) assert Q*S == A assert Q.T * Q == eye(2) A = Matrix([[1, 1, 1], [1, 1, 3], [2, 3, 4]]) Q, R = A.QRdecomposition() assert Q.T * Q == eye(Q.cols) assert R.is_upper assert A == Q*R def test_QR_non_square(): A = Matrix([[9, 0, 26], [12, 0, -7], [0, 4, 4], [0, -3, -3]]) Q, R = A.QRdecomposition() assert Q.T * Q == eye(Q.cols) assert R.is_upper assert A == Q*R A = Matrix([[1, -1, 4], [1, 4, -2], [1, 4, 2], [1, -1, 0]]) Q, R = A.QRdecomposition() assert Q.T * Q == eye(Q.cols) assert R.is_upper assert A == Q*R def test_nullspace(): # first test reduced row-ech form R = Rational M = Matrix([[5, 7, 2, 1], [1, 6, 2, -1]]) out, tmp = M.rref() assert out == Matrix([[1, 0, -R(2)/23, R(13)/23], [0, 1, R(8)/23, R(-6)/23]]) M = Matrix([[-5, -1, 4, -3, -1], [ 1, -1, -1, 1, 0], [-1, 0, 0, 0, 0], [ 4, 1, -4, 3, 1], [-2, 0, 2, -2, -1]]) assert M*M.nullspace()[0] == Matrix(5, 1, [0]*5) M = Matrix([[ 1, 3, 0, 2, 6, 3, 1], [-2, -6, 0, -2, -8, 3, 1], [ 3, 9, 0, 0, 6, 6, 2], [-1, -3, 0, 1, 0, 9, 3]]) out, tmp = M.rref() assert out == Matrix([[1, 3, 0, 0, 2, 0, 0], [0, 0, 0, 1, 2, 0, 0], [0, 0, 0, 0, 0, 1, R(1)/3], [0, 0, 0, 0, 0, 0, 0]]) # now check the vectors basis = M.nullspace() assert basis[0] == Matrix([-3, 1, 0, 0, 0, 0, 0]) assert basis[1] == Matrix([0, 0, 1, 0, 0, 0, 0]) assert basis[2] == Matrix([-2, 0, 0, -2, 1, 0, 0]) assert basis[3] == Matrix([0, 0, 0, 0, 0, R(-1)/3, 1]) # issue 1698; just see that we can do it when rows > cols M = Matrix([[1, 2], [2, 4], [3, 6]]) assert M.nullspace() def test_wronskian(): assert wronskian([cos(x), sin(x)], x) == cos(x)**2 + sin(x)**2 assert wronskian([exp(x), exp(2*x)], x) == exp(3*x) assert wronskian([exp(x), x], x) == exp(x) - x*exp(x) assert wronskian([1, x, x**2], x) == 2 w1 = -6*exp(x)*sin(x)*x + 6*cos(x)*exp(x)*x**2 - 6*exp(x)*cos(x)*x - \ exp(x)*cos(x)*x**3 + exp(x)*sin(x)*x**3 assert wronskian([exp(x), cos(x), x**3], x).expand() == w1 assert wronskian([exp(x), cos(x), x**3], x, method='berkowitz').expand() \ == w1 w2 = -x**3*cos(x)**2 - x**3*sin(x)**2 - 6*x*cos(x)**2 - 6*x*sin(x)**2 assert wronskian([sin(x), cos(x), x**3], x).expand() == w2 assert wronskian([sin(x), cos(x), x**3], x, method='berkowitz').expand() \ == w2 assert wronskian([], x) == 1 def test_eigen(): R = Rational assert eye(3).charpoly(x) == Poly((x - 1)**3, x) assert eye(3).charpoly(y) == Poly((y - 1)**3, y) M = Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) assert M.eigenvals(multiple=False) == {S.One: 3} assert M.eigenvects() == ( [(1, 3, [Matrix([1, 0, 0]), Matrix([0, 1, 0]), Matrix([0, 0, 1])])]) M = Matrix([[0, 1, 1], [1, 0, 0], [1, 1, 1]]) assert M.eigenvals() == {2*S.One: 1, -S.One: 1, S.Zero: 1} assert M.eigenvects() == ( [ (-1, 1, [Matrix([-1, 1, 0])]), ( 0, 1, [Matrix([0, -1, 1])]), ( 2, 1, [Matrix([R(2, 3), R(1, 3), 1])]) ]) M = Matrix([[1, -1], [1, 3]]) assert M.eigenvects() == ([(2, 2, [Matrix(2, 1, [-1, 1])])]) M = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) a = R(15, 2) b = 3*33**R(1, 2) c = R(13, 2) d = (R(33, 8) + 3*b/8) e = (R(33, 8) - 3*b/8) def NS(e, n): return str(N(e, n)) r = [ (a - b/2, 1, [Matrix([(12 + 24/(c - b/2))/((c - b/2)*e) + 3/(c - b/2), (6 + 12/(c - b/2))/e, 1])]), ( 0, 1, [Matrix([1, -2, 1])]), (a + b/2, 1, [Matrix([(12 + 24/(c + b/2))/((c + b/2)*d) + 3/(c + b/2), (6 + 12/(c + b/2))/d, 1])]), ] r1 = [(NS(r[i][0], 2), NS(r[i][1], 2), [NS(j, 2) for j in r[i][2][0]]) for i in range(len(r))] r = M.eigenvects() r2 = [(NS(r[i][0], 2), NS(r[i][1], 2), [NS(j, 2) for j in r[i][2][0]]) for i in range(len(r))] assert sorted(r1) == sorted(r2) eps = Symbol('eps', real=True) M = Matrix([[abs(eps), I*eps ], [-I*eps, abs(eps) ]]) assert M.eigenvects() == ( [ ( 0, 1, [Matrix([[-I*eps/abs(eps)], [1]])]), ( 2*abs(eps), 1, [ Matrix([[I*eps/abs(eps)], [1]]) ] ), ]) M = Matrix(3, 3, [1, 2, 0, 0, 3, 0, 2, -4, 2]) M._eigenvects = M.eigenvects(simplify=False) assert max(i.q for i in M._eigenvects[0][2][0]) > 1 M._eigenvects = M.eigenvects(simplify=True) assert max(i.q for i in M._eigenvects[0][2][0]) == 1 M = Matrix([[S(1)/4, 1], [1, 1]]) assert M.eigenvects(simplify=True) == [ (S(5)/8 + sqrt(73)/8, 1, [Matrix([[8/(3 + sqrt(73))], [1]])]), (-sqrt(73)/8 + S(5)/8, 1, [Matrix([[8/(-sqrt(73) + 3)], [1]])])] assert M.eigenvects(simplify=False) == [ (Rational(5, 8) + sqrt(73)/8, 1, [Matrix([[-1/(-sqrt(73)/8 + Rational(-3, 8))], [1]])]), (-sqrt(73)/8 + Rational(5, 8), 1, [Matrix([[-1/(Rational(-3, 8) + sqrt(73)/8)], [1]])]), ] m = Matrix([[1, .6, .6], [.6, .9, .9], [.9, .6, .6]]) evals = {-sqrt(385)/20 + S(5)/4: 1, sqrt(385)/20 + S(5)/4: 1, S.Zero: 1} assert m.eigenvals() == evals nevals = list(sorted(m.eigenvals(rational=False).keys())) sevals = list(sorted(evals.keys())) assert all(abs(nevals[i] - sevals[i]) < 1e-9 for i in range(len(nevals))) def test_subs(): assert Matrix([[1, x], [x, 4]]).subs(x, 5) == Matrix([[1, 5], [5, 4]]) assert Matrix([[x, 2], [x + y, 4]]).subs([[x, -1], [y, -2]]) == \ Matrix([[-1, 2], [-3, 4]]) assert Matrix([[x, 2], [x + y, 4]]).subs([(x, -1), (y, -2)]) == \ Matrix([[-1, 2], [-3, 4]]) assert Matrix([[x, 2], [x + y, 4]]).subs({x: -1, y: -2}) == \ Matrix([[-1, 2], [-3, 4]]) assert Matrix([x*y]).subs({x: y - 1, y: x - 1}, simultaneous=True) == \ Matrix([(x - 1)*(y - 1)]) for cls in classes: assert Matrix([[2, 0], [0, 2]]) == cls.eye(2).subs(1, 2) def test_simplify(): f, n = symbols('f, n') m = Matrix([[1, x], [x + 1/x, x - 1]]) m = m.row_join(eye(m.cols)) raw = m.rref(simplify=lambda x: x)[0] assert raw != m.rref(simplify=True)[0] M = Matrix([[ 1/x + 1/y, (x + x*y) / x ], [ (f(x) + y*f(x))/f(x), 2 * (1/n - cos(n * pi)/n) / pi ]]) M.simplify() assert M == Matrix([[ (x + y)/(x * y), 1 + y ], [ 1 + y, 2*((1 - 1*cos(pi*n))/(pi*n)) ]]) M = Matrix([[(1 + x)**2]]) M.simplify() assert M == Matrix([[(1 + x)**2]]) M.simplify(ratio=oo) assert M == Matrix([[1 + 2*x + x**2]]) def test_transpose(): M = Matrix([[1, 2, 3, 4, 5, 6, 7, 8, 9, 0], [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]]) assert M.T == Matrix( [ [1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6, 6], [7, 7], [8, 8], [9, 9], [0, 0] ]) assert M.T.T == M assert M.T == M.transpose() def test_conjugate(): M = Matrix([[0, I, 5], [1, 2, 0]]) assert M.T == Matrix([[0, 1], [I, 2], [5, 0]]) assert M.C == Matrix([[0, -I, 5], [1, 2, 0]]) assert M.C == M.conjugate() assert M.H == M.T.C assert M.H == Matrix([[ 0, 1], [-I, 2], [ 5, 0]]) def test_conj_dirac(): raises(AttributeError, lambda: eye(3).D) M = Matrix([[1, I, I, I], [0, 1, I, I], [0, 0, 1, I], [0, 0, 0, 1]]) assert M.D == Matrix([[ 1, 0, 0, 0], [-I, 1, 0, 0], [-I, -I, -1, 0], [-I, -I, I, -1]]) def test_trace(): M = Matrix([[1, 0, 0], [0, 5, 0], [0, 0, 8]]) assert M.trace() == 14 def test_shape(): M = Matrix([[x, 0, 0], [0, y, 0]]) assert M.shape == (2, 3) def test_col_row_op(): M = Matrix([[x, 0, 0], [0, y, 0]]) M.row_op(1, lambda r, j: r + j + 1) assert M == Matrix([[x, 0, 0], [1, y + 2, 3]]) M.col_op(0, lambda c, j: c + y**j) assert M == Matrix([[x + 1, 0, 0], [1 + y, y + 2, 3]]) # neither row nor slice give copies that allow the original matrix to # be changed assert M.row(0) == Matrix([[x + 1, 0, 0]]) r1 = M.row(0) r1[0] = 42 assert M[0, 0] == x + 1 r1 = M[0, :-1] # also testing negative slice r1[0] = 42 assert M[0, 0] == x + 1 c1 = M.col(0) assert c1 == Matrix([x + 1, 1 + y]) c1[0] = 0 assert M[0, 0] == x + 1 c1 = M[:, 0] c1[0] = 42 assert M[0, 0] == x + 1 def test_zip_row_op(): for cls in classes[:2]: # XXX: immutable matrices don't support row ops M = cls.eye(3) M.zip_row_op(1, 0, lambda v, u: v + 2*u) assert M == cls([[1, 0, 0], [2, 1, 0], [0, 0, 1]]) M = cls.eye(3)*2 M[0, 1] = -1 M.zip_row_op(1, 0, lambda v, u: v + 2*u); M assert M == cls([[2, -1, 0], [4, 0, 0], [0, 0, 2]]) def test_issue851(): m = Matrix([1, 2, 3]) a = Matrix([1, 2, 3]) b = Matrix([2, 2, 3]) assert not (m in []) assert not (m in [1]) assert m != 1 assert m == a assert m != b def test_issue882(): class Index1(object): def __index__(self): return 1 class Index2(object): def __index__(self): return 2 index1 = Index1() index2 = Index2() m = Matrix([1, 2, 3]) assert m[index2] == 3 m[index2] = 5 assert m[2] == 5 m = Matrix([[1, 2, 3], [4, 5, 6]]) assert m[index1, index2] == 6 assert m[1, index2] == 6 assert m[index1, 2] == 6 m[index1, index2] = 4 assert m[1, 2] == 4 m[1, index2] = 6 assert m[1, 2] == 6 m[index1, 2] = 8 assert m[1, 2] == 8 def test_evalf(): a = Matrix([sqrt(5), 6]) assert all(a.evalf()[i] == a[i].evalf() for i in range(2)) assert all(a.evalf(2)[i] == a[i].evalf(2) for i in range(2)) assert all(a.n(2)[i] == a[i].n(2) for i in range(2)) def test_is_symbolic(): a = Matrix([[x, x], [x, x]]) assert a.is_symbolic() is True a = Matrix([[1, 2, 3, 4], [5, 6, 7, 8]]) assert a.is_symbolic() is False a = Matrix([[1, 2, 3, 4], [5, 6, x, 8]]) assert a.is_symbolic() is True a = Matrix([[1, x, 3]]) assert a.is_symbolic() is True a = Matrix([[1, 2, 3]]) assert a.is_symbolic() is False a = Matrix([[1], [x], [3]]) assert a.is_symbolic() is True a = Matrix([[1], [2], [3]]) assert a.is_symbolic() is False def test_is_upper(): a = Matrix([[1, 2, 3]]) assert a.is_upper is True a = Matrix([[1], [2], [3]]) assert a.is_upper is False def test_is_lower(): a = Matrix([[1, 2, 3]]) assert a.is_lower is False a = Matrix([[1], [2], [3]]) assert a.is_lower is True def test_is_nilpotent(): a = Matrix(4, 4, [0, 2, 1, 6, 0, 0, 1, 2, 0, 0, 0, 3, 0, 0, 0, 0]) assert a.is_nilpotent() a = Matrix([[1, 0], [0, 1]]) assert not a.is_nilpotent() def test_zeros_ones_fill(): n, m = 3, 5 a = zeros(n, m) a.fill( 5 ) b = 5 * ones(n, m) assert a == b assert a.rows == b.rows == 3 assert a.cols == b.cols == 5 assert a.shape == b.shape == (3, 5) assert zeros(2) == zeros(2, 2) assert ones(2) == ones(2, 2) assert zeros(2, 3) == Matrix(2, 3, [0]*6) assert ones(2, 3) == Matrix(2, 3, [1]*6) def test_empty_zeros(): a = zeros(0) assert a == Matrix() a = zeros(0, 2) assert a.rows == 0 assert a.cols == 2 a = zeros(2, 0) assert a.rows == 2 assert a.cols == 0 def test_issue650(): a = Matrix([[x**2, x*y], [x*sin(y), x*cos(y)]]) assert a.diff(x) == Matrix([[2*x, y], [sin(y), cos(y)]]) assert Matrix([ [x, -x, x**2], [exp(x), 1/x - exp(-x), x + 1/x]]).limit(x, oo) == \ Matrix([[oo, -oo, oo], [oo, 0, oo]]) assert Matrix([ [(exp(x) - 1)/x, 2*x + y*x, x**x ], [1/x, abs(x), abs(sin(x + 1))]]).limit(x, 0) == \ Matrix([[1, 0, 1], [oo, 0, sin(1)]]) assert a.integrate(x) == Matrix([ [Rational(1, 3)*x**3, y*x**2/2], [x**2*sin(y)/2, x**2*cos(y)/2]]) def test_inv_iszerofunc(): A = eye(4) A.col_swap(0, 1) for method in "GE", "LU": assert A.inv(method=method, iszerofunc=lambda x: x == 0) == \ A.inv(method="ADJ") def test_jacobian_metrics(): rho, phi = symbols("rho,phi") X = Matrix([rho*cos(phi), rho*sin(phi)]) Y = Matrix([rho, phi]) J = X.jacobian(Y) assert J == X.jacobian(Y.T) assert J == (X.T).jacobian(Y) assert J == (X.T).jacobian(Y.T) g = J.T*eye(J.shape[0])*J g = g.applyfunc(trigsimp) assert g == Matrix([[1, 0], [0, rho**2]]) def test_jacobian2(): rho, phi = symbols("rho,phi") X = Matrix([rho*cos(phi), rho*sin(phi), rho**2]) Y = Matrix([rho, phi]) J = Matrix([ [cos(phi), -rho*sin(phi)], [sin(phi), rho*cos(phi)], [ 2*rho, 0], ]) assert X.jacobian(Y) == J def test_issue1465(): X = Matrix([exp(x + y + z), exp(x + y + z), exp(x + y + z)]) Y = Matrix([x, y, z]) for i in range(1, 3): for j in range(1, 3): X_slice = X[:i, :] Y_slice = Y[:j, :] J = X_slice.jacobian(Y_slice) assert J.rows == i assert J.cols == j for k in range(j): assert J[:, k] == X_slice def test_nonvectorJacobian(): X = Matrix([[exp(x + y + z), exp(x + y + z)], [exp(x + y + z), exp(x + y + z)]]) raises(TypeError, lambda: X.jacobian(Matrix([x, y, z]))) X = X[0, :] Y = Matrix([[x, y], [x, z]]) raises(TypeError, lambda: X.jacobian(Y)) raises(TypeError, lambda: X.jacobian(Matrix([ [x, y], [x, z] ]))) def test_vec(): m = Matrix([[1, 3], [2, 4]]) m_vec = m.vec() assert m_vec.cols == 1 for i in range(4): assert m_vec[i] == i + 1 def test_vech(): m = Matrix([[1, 2], [2, 3]]) m_vech = m.vech() assert m_vech.cols == 1 for i in range(3): assert m_vech[i] == i + 1 m_vech = m.vech(diagonal=False) assert m_vech[0] == 2 m = Matrix([[1, x*(x + y)], [y*x + x**2, 1]]) m_vech = m.vech(diagonal=False) assert m_vech[0] == x*(x + y) m = Matrix([[1, x*(x + y)], [y*x, 1]]) m_vech = m.vech(diagonal=False, check_symmetry=False) assert m_vech[0] == y*x def test_vech_errors(): m = Matrix([[1, 3]]) raises(ShapeError, lambda: m.vech()) m = Matrix([[1, 3], [2, 4]]) raises(ValueError, lambda: m.vech()) raises(ShapeError, lambda: Matrix([ [1, 3] ]).vech()) raises(ValueError, lambda: Matrix([ [1, 3], [2, 4] ]).vech()) def test_diag(): a = Matrix([[1, 2], [2, 3]]) b = Matrix([[3, x], [y, 3]]) c = Matrix([[3, x, 3], [y, 3, z], [x, y, z]]) assert diag(a, b, b) == Matrix([ [1, 2, 0, 0, 0, 0], [2, 3, 0, 0, 0, 0], [0, 0, 3, x, 0, 0], [0, 0, y, 3, 0, 0], [0, 0, 0, 0, 3, x], [0, 0, 0, 0, y, 3], ]) assert diag(a, b, c) == Matrix([ [1, 2, 0, 0, 0, 0, 0], [2, 3, 0, 0, 0, 0, 0], [0, 0, 3, x, 0, 0, 0], [0, 0, y, 3, 0, 0, 0], [0, 0, 0, 0, 3, x, 3], [0, 0, 0, 0, y, 3, z], [0, 0, 0, 0, x, y, z], ]) assert diag(a, c, b) == Matrix([ [1, 2, 0, 0, 0, 0, 0], [2, 3, 0, 0, 0, 0, 0], [0, 0, 3, x, 3, 0, 0], [0, 0, y, 3, z, 0, 0], [0, 0, x, y, z, 0, 0], [0, 0, 0, 0, 0, 3, x], [0, 0, 0, 0, 0, y, 3], ]) a = Matrix([x, y, z]) b = Matrix([[1, 2], [3, 4]]) c = Matrix([[5, 6]]) assert diag(a, 7, b, c) == Matrix([ [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], ]) assert diag(1, [2, 3], [[4, 5]]) == Matrix([ [1, 0, 0, 0], [0, 2, 0, 0], [0, 3, 0, 0], [0, 0, 4, 5]]) def test_get_diag_blocks1(): a = Matrix([[1, 2], [2, 3]]) b = Matrix([[3, x], [y, 3]]) c = Matrix([[3, x, 3], [y, 3, z], [x, y, z]]) assert a.get_diag_blocks() == [a] assert b.get_diag_blocks() == [b] assert c.get_diag_blocks() == [c] def test_get_diag_blocks2(): a = Matrix([[1, 2], [2, 3]]) b = Matrix([[3, x], [y, 3]]) c = Matrix([[3, x, 3], [y, 3, z], [x, y, z]]) assert diag(a, b, b).get_diag_blocks() == [a, b, b] assert diag(a, b, c).get_diag_blocks() == [a, b, c] assert diag(a, c, b).get_diag_blocks() == [a, c, b] assert diag(c, c, b).get_diag_blocks() == [c, c, b] def test_inv_block(): a = Matrix([[1, 2], [2, 3]]) b = Matrix([[3, x], [y, 3]]) c = Matrix([[3, x, 3], [y, 3, z], [x, y, z]]) A = diag(a, b, b) assert A.inv(try_block_diag=True) == diag(a.inv(), b.inv(), b.inv()) A = diag(a, b, c) assert A.inv(try_block_diag=True) == diag(a.inv(), b.inv(), c.inv()) A = diag(a, c, b) assert A.inv(try_block_diag=True) == diag(a.inv(), c.inv(), b.inv()) A = diag(a, a, b, a, c, a) assert A.inv(try_block_diag=True) == diag( a.inv(), a.inv(), b.inv(), a.inv(), c.inv(), a.inv()) assert A.inv(try_block_diag=True, method="ADJ") == diag( a.inv(method="ADJ"), a.inv(method="ADJ"), b.inv(method="ADJ"), a.inv(method="ADJ"), c.inv(method="ADJ"), a.inv(method="ADJ")) def test_creation_args(): """ Check that matrix dimensions can be specified using any reasonable type (see issue 1515). """ raises(ValueError, lambda: zeros(3, -1)) raises(TypeError, lambda: zeros(1, 2, 3, 4)) assert zeros(long(3)) == zeros(3) assert zeros(Integer(3)) == zeros(3) assert zeros(3.) == zeros(3) assert eye(long(3)) == eye(3) assert eye(Integer(3)) == eye(3) assert eye(3.) == eye(3) assert ones(long(3), Integer(4)) == ones(3, 4) raises(TypeError, lambda: Matrix(5)) raises(TypeError, lambda: Matrix(1, 2)) def test_diagonal_symmetrical(): m = Matrix(2, 2, [0, 1, 1, 0]) assert not m.is_diagonal() assert m.is_symmetric() assert m.is_symmetric(simplify=False) m = Matrix(2, 2, [1, 0, 0, 1]) assert m.is_diagonal() m = diag(1, 2, 3) assert m.is_diagonal() assert m.is_symmetric() m = Matrix(3, 3, [1, 0, 0, 0, 2, 0, 0, 0, 3]) assert m == diag(1, 2, 3) m = Matrix(2, 3, zeros(2, 3)) assert not m.is_symmetric() assert m.is_diagonal() m = Matrix(((5, 0), (0, 6), (0, 0))) assert m.is_diagonal() m = Matrix(((5, 0, 0), (0, 6, 0))) assert m.is_diagonal() m = Matrix(3, 3, [1, x**2 + 2*x + 1, y, (x + 1)**2, 2, 0, y, 0, 3]) assert m.is_symmetric() assert not m.is_symmetric(simplify=False) assert m.expand().is_symmetric(simplify=False) def test_diagonalization(): m = Matrix(3, 2, [-3, 1, -3, 20, 3, 10]) assert not m.is_diagonalizable() assert not m.is_symmetric() raises(NonSquareMatrixError, lambda: m.diagonalize()) # diagonalizable m = diag(1, 2, 3) (P, D) = m.diagonalize() assert P == eye(3) assert D == m m = Matrix(2, 2, [0, 1, 1, 0]) assert m.is_symmetric() assert m.is_diagonalizable() (P, D) = m.diagonalize() assert P.inv() * m * P == D m = Matrix(2, 2, [1, 0, 0, 3]) assert m.is_symmetric() assert m.is_diagonalizable() (P, D) = m.diagonalize() assert P.inv() * m * P == D assert P == eye(2) assert D == m m = Matrix(2, 2, [1, 1, 0, 0]) assert m.is_diagonalizable() (P, D) = m.diagonalize() assert P.inv() * m * P == D m = Matrix(3, 3, [1, 2, 0, 0, 3, 0, 2, -4, 2]) assert m.is_diagonalizable() (P, D) = m.diagonalize() assert P.inv() * m * P == D for i in P: assert i.as_numer_denom()[1] == 1 m = Matrix(2, 2, [1, 0, 0, 0]) assert m.is_diagonal() assert m.is_diagonalizable() (P, D) = m.diagonalize() assert P.inv() * m * P == D assert P == Matrix([[0, 1], [1, 0]]) # diagonalizable, complex only m = Matrix(2, 2, [0, 1, -1, 0]) assert not m.is_diagonalizable(True) raises(MatrixError, lambda: m.diagonalize(True)) assert m.is_diagonalizable() (P, D) = m.diagonalize() assert P.inv() * m * P == D # not diagonalizable m = Matrix(2, 2, [0, 1, 0, 0]) assert not m.is_diagonalizable() raises(MatrixError, lambda: m.diagonalize()) m = Matrix(3, 3, [-3, 1, -3, 20, 3, 10, 2, -2, 4]) assert not m.is_diagonalizable() raises(MatrixError, lambda: m.diagonalize()) # symbolic a, b, c, d = symbols('a b c d') m = Matrix(2, 2, [a, c, c, b]) assert m.is_symmetric() assert m.is_diagonalizable() @XFAIL def test_eigen_vects(): m = Matrix(2, 2, [1, 0, 0, I]) raises(NotImplementedError, lambda: m.is_diagonalizable(True)) # !!! bug because of eigenvects() or roots(x**2 + (-1 - I)*x + I, x) # see issue 2193 assert not m.is_diagonalizable(True) raises(MatrixError, lambda: m.diagonalize(True)) (P, D) = m.diagonalize(True) def test_jordan_form(): m = Matrix(3, 2, [-3, 1, -3, 20, 3, 10]) raises(NonSquareMatrixError, lambda: m.jordan_form()) # diagonalizable m = Matrix(3, 3, [7, -12, 6, 10, -19, 10, 12, -24, 13]) Jmust = Matrix(3, 3, [-1, 0, 0, 0, 1, 0, 0, 0, 1]) P, J = m.jordan_form() assert Jmust == J assert Jmust == m.diagonalize()[1] # m = Matrix(3, 3, [0, 6, 3, 1, 3, 1, -2, 2, 1]) # m.jordan_form() # very long # m.jordan_form() # # diagonalizable, complex only # Jordan cells # complexity: one of eigenvalues is zero m = Matrix(3, 3, [0, 1, 0, -4, 4, 0, -2, 1, 2]) # The blocks are ordered according to the value of their eigenvalues, # in order to make the matrix compatible with .diagonalize() Jmust = Matrix(3, 3, [2, 1, 0, 0, 2, 0, 0, 0, 2]) P, J = m.jordan_form() assert Jmust == J P, Jcells = m.jordan_cells() # same here see 1456ff assert Jcells[1] == Matrix(1, 1, [2]) assert Jcells[0] == Matrix(2, 2, [2, 1, 0, 2]) # complexity: all of eigenvalues are equal m = Matrix(3, 3, [2, 6, -15, 1, 1, -5, 1, 2, -6]) # Jmust = Matrix(3, 3, [-1, 0, 0, 0, -1, 1, 0, 0, -1]) # same here see 1456ff Jmust = Matrix(3, 3, [-1, 1, 0, 0, -1, 0, 0, 0, -1]) P, J = m.jordan_form() assert Jmust == J # complexity: two of eigenvalues are zero m = Matrix(3, 3, [4, -5, 2, 5, -7, 3, 6, -9, 4]) Jmust = Matrix(3, 3, [0, 1, 0, 0, 0, 0, 0, 0, 1]) P, J = m.jordan_form() assert Jmust == J m = Matrix(4, 4, [6, 5, -2, -3, -3, -1, 3, 3, 2, 1, -2, -3, -1, 1, 5, 5]) Jmust = Matrix(4, 4, [2, 1, 0, 0, 0, 2, 0, 0, 0, 0, 2, 1, 0, 0, 0, 2] ) P, J = m.jordan_form() assert Jmust == J m = Matrix(4, 4, [6, 2, -8, -6, -3, 2, 9, 6, 2, -2, -8, -6, -1, 0, 3, 4]) # Jmust = Matrix(4, 4, [2, 0, 0, 0, 0, 2, 1, 0, 0, 0, 2, 0, 0, 0, 0, -2]) # same here see 1456ff Jmust = Matrix(4, 4, [-2, 0, 0, 0, 0, 2, 1, 0, 0, 0, 2, 0, 0, 0, 0, 2]) P, J = m.jordan_form() assert Jmust == J m = Matrix(4, 4, [5, 4, 2, 1, 0, 1, -1, -1, -1, -1, 3, 0, 1, 1, -1, 2]) assert not m.is_diagonalizable() Jmust = Matrix(4, 4, [1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 4, 1, 0, 0, 0, 4]) P, J = m.jordan_form() assert Jmust == J # the following tests are new and include (some) test the cases where the old # algorithm failed due to the fact that the block structure can # *NOT* be determined from algebraic and geometric multiplicity alone # This can be seen most easily when one lets compute the J.c.f. of a matrix that # is in J.c.f already. m = Matrix(4, 4, [2, 1, 0, 0, 0, 2, 1, 0, 0, 0, 2, 0, 0, 0, 0, 2 ]) P, J = m.jordan_form() assert m == J m = Matrix(4, 4, [2, 1, 0, 0, 0, 2, 0, 0, 0, 0, 2, 1, 0, 0, 0, 2 ]) P, J = m.jordan_form() assert m == J def test_Matrix_berkowitz_charpoly(): UA, K_i, K_w = symbols('UA K_i K_w') A = Matrix([[-K_i - UA + K_i**2/(K_i + K_w), K_i*K_w/(K_i + K_w)], [ K_i*K_w/(K_i + K_w), -K_w + K_w**2/(K_i + K_w)]]) charpoly = A.berkowitz_charpoly(x) assert charpoly == \ Poly(x**2 + (K_i*UA + K_w*UA + 2*K_i*K_w)/(K_i + K_w)*x + K_i*K_w*UA/(K_i + K_w), x, domain='ZZ(K_i,K_w,UA)') assert type(charpoly) is PurePoly A = Matrix([[1, 3], [2, 0]]) assert A.charpoly() == A.charpoly(x) == PurePoly(x**2 - x - 6) def test_exp(): m = Matrix([[3, 4], [0, -2]]) m_exp = Matrix([[exp(3), -4*exp(-2)/5 + 4*exp(3)/5], [0, exp(-2)]]) assert m.exp() == m_exp assert exp(m) == m_exp m = Matrix([[1, 0], [0, 1]]) assert m.exp() == Matrix([[E, 0], [0, E]]) assert exp(m) == Matrix([[E, 0], [0, E]]) def test_has(): A = Matrix(((x, y), (2, 3))) assert A.has(x) assert not A.has(z) assert A.has(Symbol) A = A.subs(x, 2) assert not A.has(x) def test_errors(): raises(ValueError, lambda: Matrix([[1, 2], [1]])) raises(IndexError, lambda: Matrix([[1, 2]])[1.2, 5]) raises(IndexError, lambda: Matrix([[1, 2]])[1, 5.2]) raises(ValueError, lambda: randMatrix(3, c=4, symmetric=True)) raises(ValueError, lambda: Matrix([1, 2]).reshape(4, 6)) raises(ShapeError, lambda: Matrix([[1, 2], [3, 4]]).copyin_matrix([1, 0], Matrix([1, 2]))) raises(TypeError, lambda: Matrix([[1, 2], [3, 4]]).copyin_list([0, 1], set([]))) raises(NonSquareMatrixError, lambda: Matrix([[1, 2, 3], [2, 3, 0]]).inv()) raises(ShapeError, lambda: Matrix(1, 2, [1, 2]).row_join(Matrix([[1, 2], [3, 4]]))) raises( ShapeError, lambda: Matrix([1, 2]).col_join(Matrix([[1, 2], [3, 4]]))) raises(ShapeError, lambda: Matrix([1]).row_insert(1, Matrix([[1, 2], [3, 4]]))) raises(ShapeError, lambda: Matrix([1]).col_insert(1, Matrix([[1, 2], [3, 4]]))) raises(NonSquareMatrixError, lambda: Matrix([1, 2]).trace()) raises(TypeError, lambda: Matrix([1]).applyfunc(1)) raises(ShapeError, lambda: Matrix([1]).LUsolve(Matrix([[1, 2], [3, 4]]))) raises(MatrixError, lambda: Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9] ]).QRdecomposition()) raises(MatrixError, lambda: Matrix(1, 2, [1, 2]).QRdecomposition()) raises( NonSquareMatrixError, lambda: Matrix([1, 2]).LUdecomposition_Simple()) raises(ValueError, lambda: Matrix([[1, 2], [3, 4]]).minorEntry(4, 5)) raises(ValueError, lambda: Matrix([[1, 2], [3, 4]]).minorMatrix(4, 5)) raises(TypeError, lambda: Matrix([1, 2, 3]).cross(1)) raises(TypeError, lambda: Matrix([1, 2, 3]).dot(1)) raises(ShapeError, lambda: Matrix([1, 2, 3]).dot(Matrix([1, 2]))) raises(ShapeError, lambda: Matrix([1, 2]).dot([])) raises(TypeError, lambda: Matrix([1, 2]).dot('a')) raises(NonSquareMatrixError, lambda: Matrix([1, 2, 3]).exp()) raises(ShapeError, lambda: Matrix([[1, 2], [3, 4]]).normalized()) raises(ValueError, lambda: Matrix([1, 2]).inv(method='not a method')) raises(NonSquareMatrixError, lambda: Matrix([1, 2]).inverse_GE()) raises(ValueError, lambda: Matrix([[1, 2], [1, 2]]).inverse_GE()) raises(NonSquareMatrixError, lambda: Matrix([1, 2]).inverse_ADJ()) raises(ValueError, lambda: Matrix([[1, 2], [1, 2]]).inverse_ADJ()) raises(NonSquareMatrixError, lambda: Matrix([1, 2]).inverse_LU()) raises(NonSquareMatrixError, lambda: Matrix([1, 2]).is_nilpotent()) raises(NonSquareMatrixError, lambda: Matrix([1, 2]).det()) raises(ValueError, lambda: Matrix([[1, 2], [3, 4]]).det(method='Not a real method')) raises(NonSquareMatrixError, lambda: Matrix([1, 2]).det_bareis()) raises(NonSquareMatrixError, lambda: Matrix([1, 2]).berkowitz()) raises(NonSquareMatrixError, lambda: Matrix([1, 2]).berkowitz_det()) raises(ValueError, lambda: hessian(Matrix([[1, 2], [3, 4]]), Matrix([[1, 2], [2, 1]]))) raises(ValueError, lambda: hessian(Matrix([[1, 2], [3, 4]]), [])) raises(ValueError, lambda: hessian(Symbol('x')**2, 'a')) raises(ValueError, lambda: Matrix([[5, 10, 7], [0, -1, 2], [8, 3, 4]] ).LUdecomposition_Simple(iszerofunc=lambda x: abs(x) <= 4)) raises(NotImplementedError, lambda: Matrix([[1, 0], [1, 1]])**(S(1)/2)) raises(NotImplementedError, lambda: Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]])**(0.5)) raises(IndexError, lambda: eye(3)[5, 2]) raises(IndexError, lambda: eye(3)[2, 5]) M = Matrix(((1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12), (13, 14, 15, 16))) raises(ValueError, lambda: M.det('method=LU_decomposition()')) def test_len(): assert len(Matrix()) == 0 assert len(Matrix([[1, 2]])) == len(Matrix([[1], [2]])) == 2 assert len(Matrix(0, 2, lambda i, j: 0)) == \ len(Matrix(2, 0, lambda i, j: 0)) == 0 assert len(Matrix([[0, 1, 2], [3, 4, 5]])) == 6 assert Matrix([1]) == Matrix([[1]]) assert not Matrix() assert Matrix() == Matrix([]) def test_integrate(): A = Matrix(((1, 4, x), (y, 2, 4), (10, 5, x**2))) assert A.integrate(x) == \ Matrix(((x, 4*x, x**2/2), (x*y, 2*x, 4*x), (10*x, 5*x, x**3/3))) assert A.integrate(y) == \ Matrix(((y, 4*y, x*y), (y**2/2, 2*y, 4*y), (10*y, 5*y, y*x**2))) def test_limit(): A = Matrix(((1, 4, sin(x)/x), (y, 2, 4), (10, 5, x**2 + 1))) assert A.limit(x, 0) == Matrix(((1, 4, 1), (y, 2, 4), (10, 5, 1))) def test_diff(): A = Matrix(((1, 4, x), (y, 2, 4), (10, 5, x**2 + 1))) assert A.diff(x) == Matrix(((0, 0, 1), (0, 0, 0), (0, 0, 2*x))) assert A.diff(y) == Matrix(((0, 0, 0), (1, 0, 0), (0, 0, 0))) def test_getattr(): A = Matrix(((1, 4, x), (y, 2, 4), (10, 5, x**2 + 1))) raises(AttributeError, lambda: A.nonexistantattribute) assert getattr(A, 'diff')(x) == Matrix(((0, 0, 1), (0, 0, 0), (0, 0, 2*x))) def test_hessenberg(): A = Matrix([[3, 4, 1], [2, 4, 5], [0, 1, 2]]) assert A.is_upper_hessenberg A = A.T assert A.is_lower_hessenberg A[0, -1] = 1 assert A.is_lower_hessenberg is False A = Matrix([[3, 4, 1], [2, 4, 5], [3, 1, 2]]) assert not A.is_upper_hessenberg def test_cholesky(): raises(NonSquareMatrixError, lambda: Matrix((1, 2)).cholesky()) raises(ValueError, lambda: Matrix(((1, 2), (3, 4))).cholesky()) A = Matrix(((25, 15, -5), (15, 18, 0), (-5, 0, 11))) assert A.cholesky() * A.cholesky().T == A assert A.cholesky().is_lower assert A.cholesky() == Matrix([[5, 0, 0], [3, 3, 0], [-1, 1, 3]]) def test_LDLdecomposition(): raises(NonSquareMatrixError, lambda: Matrix((1, 2)).LDLdecomposition()) raises(ValueError, lambda: Matrix(((1, 2), (3, 4))).LDLdecomposition()) A = Matrix(((25, 15, -5), (15, 18, 0), (-5, 0, 11))) L, D = A.LDLdecomposition() assert L * D * L.T == A assert L.is_lower assert L == Matrix([[1, 0, 0], [ S(3)/5, 1, 0], [S(-1)/5, S(1)/3, 1]]) assert D.is_diagonal() assert D == Matrix([[25, 0, 0], [0, 9, 0], [0, 0, 9]]) def test_cholesky_solve(): A = Matrix([[2, 3, 5], [3, 6, 2], [8, 3, 6]]) x = Matrix(3, 1, [3, 7, 5]) b = A*x soln = A.cholesky_solve(b) assert soln == x A = Matrix([[0, -1, 2], [5, 10, 7], [8, 3, 4]]) x = Matrix(3, 1, [-1, 2, 5]) b = A*x soln = A.cholesky_solve(b) assert soln == x def test_LDLsolve(): A = Matrix([[2, 3, 5], [3, 6, 2], [8, 3, 6]]) x = Matrix(3, 1, [3, 7, 5]) b = A*x soln = A.LDLsolve(b) assert soln == x A = Matrix([[0, -1, 2], [5, 10, 7], [8, 3, 4]]) x = Matrix(3, 1, [-1, 2, 5]) b = A*x soln = A.LDLsolve(b) assert soln == x def test_lower_triangular_solve(): raises(NonSquareMatrixError, lambda: Matrix([1, 0]).lower_triangular_solve(Matrix([0, 1]))) raises(ShapeError, lambda: Matrix([[1, 0], [0, 1]]).lower_triangular_solve(Matrix([1]))) raises(ValueError, lambda: Matrix([[2, 1], [1, 2]]).lower_triangular_solve( Matrix([[1, 0], [0, 1]]))) A = Matrix([[1, 0], [0, 1]]) B = Matrix([[x, y], [y, x]]) C = Matrix([[4, 8], [2, 9]]) assert A.lower_triangular_solve(B) == B assert A.lower_triangular_solve(C) == C def test_upper_triangular_solve(): raises(NonSquareMatrixError, lambda: Matrix([1, 0]).upper_triangular_solve(Matrix([0, 1]))) raises(TypeError, lambda: Matrix([[1, 0], [0, 1]]).upper_triangular_solve(Matrix([1]))) raises(TypeError, lambda: Matrix([[2, 1], [1, 2]]).upper_triangular_solve( Matrix([[1, 0], [0, 1]]))) A = Matrix([[1, 0], [0, 1]]) B = Matrix([[x, y], [y, x]]) C = Matrix([[2, 4], [3, 8]]) assert A.upper_triangular_solve(B) == B assert A.upper_triangular_solve(C) == C def test_diagonal_solve(): raises(TypeError, lambda: Matrix([1, 1]).diagonal_solve(Matrix([1]))) A = Matrix([[1, 0], [0, 1]])*2 B = Matrix([[x, y], [y, x]]) assert A.diagonal_solve(B) == B/2 def test_matrix_norm(): # Vector Tests # Test columns and symbols x = Symbol('x', real=True) v = Matrix([cos(x), sin(x)]) assert trigsimp(v.norm(2)) == 1 assert v.norm(10) == Pow(cos(x)**10 + sin(x)**10, S(1)/10) # Test Rows A = Matrix([[5, Rational(3, 2)]]) assert A.norm() == Pow(25 + Rational(9, 4), S(1)/2) assert A.norm(oo) == max(A._mat) assert A.norm(-oo) == min(A._mat) # Matrix Tests # Intuitive test A = Matrix([[1, 1], [1, 1]]) assert A.norm(2) == 2 assert A.norm(-2) == 0 assert A.norm('frobenius') == 2 assert eye(10).norm(2) == eye(10).norm(-2) == 1 # Test with Symbols and more complex entries A = Matrix([[3, y, y], [x, S(1)/2, -pi]]) assert (A.norm('fro') == sqrt(S(37)/4 + 2*abs(y)**2 + pi**2 + x**2)) # Check non-square A = Matrix([[1, 2, -3], [4, 5, Rational(13, 2)]]) assert A.norm(2) == sqrt(S(389)/8 + sqrt(78665)/8) assert A.norm(-2) == S(0) assert A.norm('frobenius') == sqrt(389)/2 # Test properties of matrix norms # http://en.wikipedia.org/wiki/Matrix_norm#Definition # Two matrices A = Matrix([[1, 2], [3, 4]]) B = Matrix([[5, 5], [-2, 2]]) C = Matrix([[0, -I], [I, 0]]) D = Matrix([[1, 0], [0, -1]]) L = [A, B, C, D] alpha = Symbol('alpha', real=True) for order in ['fro', 2, -2]: # Zero Check assert zeros(3).norm(order) == S(0) # Check Triangle Inequality for all Pairs of Matrices for X in L: for Y in L: assert X.norm(order) + Y.norm(order) >= (X + Y).norm(order) # Scalar multiplication linearity for M in [A, B, C, D]: if order in [2, -2]: # Abs is causing tests to fail when Abs(alpha) is inside a Max # or Min. The tests produce mathematically true statements that # are too complex to be simplified well. continue try: assert ((alpha*M).norm(order) == abs(alpha) * M.norm(order)) except NotImplementedError: pass # Some Norms fail on symbolic matrices due to Max issue # Test Properties of Vector Norms # http://en.wikipedia.org/wiki/Vector_norm # Two column vectors a = Matrix([1, 1 - 1*I, -3]) b = Matrix([S(1)/2, 1*I, 1]) c = Matrix([-1, -1, -1]) d = Matrix([3, 2, I]) e = Matrix([Integer(1e2), Rational(1, 1e2), 1]) L = [a, b, c, d, e] alpha = Symbol('alpha', real=True) for order in [1, 2, -1, -2, S.Infinity, S.NegativeInfinity, pi]: # Zero Check if order > 0: assert Matrix([0, 0, 0]).norm(order) == S(0) # Triangle inequality on all pairs if order >= 1: # Triangle InEq holds only for these norms for v in L: for w in L: assert v.norm(order) + w.norm(order) >= (v + w).norm(order) # Linear to scalar multiplication if order in [1, 2, -1, -2, S.Infinity, S.NegativeInfinity]: for vec in L: try: assert simplify((alpha*v).norm(order) - (abs(alpha) * v.norm(order))) == 0 except NotImplementedError: pass # Some Norms fail on symbolics due to Max issue def test_singular_values(): x = Symbol('x', real=True) A = Matrix([[0, 1*I], [2, 0]]) assert A.singular_values() == [2, 1] A = eye(3) A[1, 1] = x A[2, 2] = 5 vals = A.singular_values() assert 1 in vals and 5 in vals and abs(x) in vals A = Matrix([[sin(x), cos(x)], [-cos(x), sin(x)]]) vals = [sv.trigsimp() for sv in A.singular_values()] assert vals == [S(1), S(1)] def test_condition_number(): x = Symbol('x', real=True) A = eye(3) A[0, 0] = 10 A[2, 2] = S(1)/10 assert A.condition_number() == 100 A[1, 1] = x assert A.condition_number() == Max(10, Abs(x)) / Min(S(1)/10, Abs(x)) M = Matrix([[cos(x), sin(x)], [-sin(x), cos(x)]]) Mc = M.condition_number() assert all(Float(1.).epsilon_eq(Mc.subs(x, val).evalf()) for val in [Rational(1, 5), Rational(1, 2), Rational(1, 10), pi/2, pi, 7*pi/4 ]) def test_equality(): A = Matrix(((1, 2, 3), (4, 5, 6), (7, 8, 9))) B = Matrix(((9, 8, 7), (6, 5, 4), (3, 2, 1))) assert A == A[:, :] assert not A != A[:, :] assert not A == B assert A != B assert A != 10 assert not A == 10 # A SparseMatrix can be equal to a Matrix C = SparseMatrix(((1, 0, 0), (0, 1, 0), (0, 0, 1))) D = Matrix(((1, 0, 0), (0, 1, 0), (0, 0, 1))) assert C == D assert not C != D def test_col_join(): assert eye(3).col_join(Matrix([[7, 7, 7]])) == \ Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1], [7, 7, 7]]) def test_row_insert(): r4 = Matrix([[4, 4, 4]]) for i in range(-4, 5): l = [1, 0, 0] l.insert(i, 4) assert flatten(eye(3).row_insert(i, r4).col(0).tolist()) == l def test_col_insert(): c4 = Matrix([4, 4, 4]) for i in range(-4, 5): l = [0, 0, 0] l.insert(i, 4) assert flatten(zeros(3).col_insert(i, c4).row(0).tolist()) == l def test_normalized(): assert Matrix([3, 4]).normalized() == \ Matrix([Rational(3, 5), Rational(4, 5)]) def test_print_nonzero(): assert capture(lambda: eye(3).print_nonzero()) == \ '[X ]\n[ X ]\n[ X]\n' assert capture(lambda: eye(3).print_nonzero('.')) == \ '[. ]\n[ . ]\n[ .]\n' def test_zeros_eye(): assert Matrix.eye(3) == eye(3) assert Matrix.zeros(3) == zeros(3) assert ones(3, 4) == Matrix(3, 4, [1]*12) i = Matrix([[1, 0], [0, 1]]) z = Matrix([[0, 0], [0, 0]]) for cls in classes: m = cls.eye(2) assert i == m # but m == i will fail if m is immutable assert i == eye(2, cls=cls) assert type(m) == cls m = cls.zeros(2) assert z == m assert z == zeros(2, cls=cls) assert type(m) == cls def test_is_zero(): assert Matrix().is_zero assert Matrix([[0, 0], [0, 0]]).is_zero assert zeros(3, 4).is_zero assert not eye(3).is_zero def test_rotation_matrices(): # This tests the rotation matrices by rotating about an axis and back. theta = pi/3 r3_plus = rot_axis3(theta) r3_minus = rot_axis3(-theta) r2_plus = rot_axis2(theta) r2_minus = rot_axis2(-theta) r1_plus = rot_axis1(theta) r1_minus = rot_axis1(-theta) assert r3_minus*r3_plus*eye(3) == eye(3) assert r2_minus*r2_plus*eye(3) == eye(3) assert r1_minus*r1_plus*eye(3) == eye(3) # Check the correctness of the trace of the rotation matrix assert r1_plus.trace() == 1 + 2*cos(theta) assert r2_plus.trace() == 1 + 2*cos(theta) assert r3_plus.trace() == 1 + 2*cos(theta) # Check that a rotation with zero angle doesn't change anything. assert rot_axis1(0) == eye(3) assert rot_axis2(0) == eye(3) assert rot_axis3(0) == eye(3) def test_DeferredVector(): assert str(DeferredVector("vector")[4]) == "vector[4]" assert sympify(DeferredVector("d")) == DeferredVector("d") def test_DeferredVector_Matrix(): raises(TypeError, lambda: Matrix(DeferredVector("V"))) def test_GramSchmidt(): R = Rational m1 = Matrix(1, 2, [1, 2]) m2 = Matrix(1, 2, [2, 3]) assert GramSchmidt([m1, m2]) == \ [Matrix(1, 2, [1, 2]), Matrix(1, 2, [R(2)/5, R(-1)/5])] assert GramSchmidt([m1.T, m2.T]) == \ [Matrix(2, 1, [1, 2]), Matrix(2, 1, [R(2)/5, R(-1)/5])] # from wikipedia assert GramSchmidt([Matrix([3, 1]), Matrix([2, 2])], True) == [ Matrix([3*sqrt(10)/10, sqrt(10)/10]), Matrix([-sqrt(10)/10, 3*sqrt(10)/10])] def test_casoratian(): assert casoratian([1, 2, 3, 4], 1) == 0 assert casoratian([1, 2, 3, 4], 1, zero=False) == 0 def test_zero_dimension_multiply(): assert (Matrix()*zeros(0, 3)).shape == (0, 3) assert zeros(3, 0)*zeros(0, 3) == zeros(3, 3) assert zeros(0, 3)*zeros(3, 0) == Matrix() def test_slice_issue_2884(): m = Matrix(2, 2, range(4)) assert m[1, :] == Matrix([[2, 3]]) assert m[-1, :] == Matrix([[2, 3]]) assert m[:, 1] == Matrix([[1, 3]]).T assert m[:, -1] == Matrix([[1, 3]]).T raises(IndexError, lambda: m[2, :]) raises(IndexError, lambda: m[2, 2]) def test_slice_issue_3401(): assert zeros(0, 3)[:, -1].shape == (0, 1) assert zeros(3, 0)[0, :] == Matrix(1, 0, []) def test_copyin(): s = zeros(3, 3) s[3] = 1 assert s[:, 0] == Matrix([0, 1, 0]) assert s[3] == 1 assert s[3: 4] == [1] s[1, 1] = 42 assert s[1, 1] == 42 assert s[1, 1:] == Matrix([[42, 0]]) s[1, 1:] = Matrix([[5, 6]]) assert s[1, :] == Matrix([[1, 5, 6]]) s[1, 1:] = [[42, 43]] assert s[1, :] == Matrix([[1, 42, 43]]) s[0, 0] = 17 assert s[:, :1] == Matrix([17, 1, 0]) s[0, 0] = [1, 1, 1] assert s[:, 0] == Matrix([1, 1, 1]) s[0, 0] = Matrix([1, 1, 1]) assert s[:, 0] == Matrix([1, 1, 1]) s[0, 0] = SparseMatrix([1, 1, 1]) assert s[:, 0] == Matrix([1, 1, 1]) def test_invertible_check(): # sometimes a singular matrix will have a pivot vector shorter than # the number of rows in a matrix... assert Matrix([[1, 2], [1, 2]]).rref() == (Matrix([[1, 2], [0, 0]]), [0]) raises(ValueError, lambda: Matrix([[1, 2], [1, 2]]).inv()) # ... but sometimes it won't, so that is an insufficient test of # whether something is invertible. m = Matrix([ [-1, -1, 0], [ x, 1, 1], [ 1, x, -1], ]) assert len(m.rref()[1]) == m.rows # in addition, unless simplify=True in the call to rref, the identity # matrix will be returned even though m is not invertible assert m.rref()[0] == eye(3) assert m.rref(simplify=signsimp)[0] != eye(3) raises(ValueError, lambda: m.inv(method="ADJ")) raises(ValueError, lambda: m.inv(method="GE")) raises(ValueError, lambda: m.inv(method="LU")) @XFAIL def test_issue_860(): x, y = symbols('x, y') e = x*y assert e.subs(x, Matrix([3, 5, 3])) == Matrix([3, 5, 3])*y def test_issue_2865(): assert str(Matrix([[1, 2], [3, 4]])) == 'Matrix([[1, 2], [3, 4]])' def test_is_Identity(): assert eye(3).is_Identity assert eye(3).as_immutable().is_Identity assert not zeros(3).is_Identity assert not ones(3).is_Identity # issue 3143 assert not Matrix([[1, 0, 0]]).is_Identity def test_dot(): assert ones(1, 3).dot(ones(3, 1)) == 3 assert ones(1, 3).dot([1, 1, 1]) == 3 def test_dual(): B_x, B_y, B_z, E_x, E_y, E_z = symbols( 'B_x B_y B_z E_x E_y E_z', real=True) F = Matrix(( ( 0, E_x, E_y, E_z), (-E_x, 0, B_z, -B_y), (-E_y, -B_z, 0, B_x), (-E_z, B_y, -B_x, 0) )) Fd = Matrix(( ( 0, -B_x, -B_y, -B_z), (B_x, 0, E_z, -E_y), (B_y, -E_z, 0, E_x), (B_z, E_y, -E_x, 0) )) assert F.dual().equals(Fd) assert eye(3).dual().equals(zeros(3)) assert F.dual().dual().equals(-F) def test_anti_symmetric(): assert Matrix([1, 2]).is_anti_symmetric() is False m = Matrix(3, 3, [0, x**2 + 2*x + 1, y, -(x + 1)**2, 0, x*y, -y, -x*y, 0]) assert m.is_anti_symmetric() is True assert m.is_anti_symmetric(simplify=False) is False assert m.is_anti_symmetric(simplify=lambda x: x) is False # tweak to fail m[2, 1] = -m[2, 1] assert m.is_anti_symmetric() is False # untweak m[2, 1] = -m[2, 1] m = m.expand() assert m.is_anti_symmetric(simplify=False) is True m[0, 0] = 1 assert m.is_anti_symmetric() is False def test_normalize_sort_diogonalization(): A = Matrix(((1, 2), (2, 1))) P, Q = A.diagonalize(normalize=True) assert P*P.T == P.T*P == eye(P.cols) P, Q = A.diagonalize(normalize=True, sort=True) assert P*P.T == P.T*P == eye(P.cols) assert P*Q*P.inv() == A def test_issue2222(): raises(ValueError, lambda: Matrix([[1, 2, 3], Matrix(0, 1, [])])) def test_issue2221(): assert Matrix.hstack(eye(2), 2*eye(2)) == Matrix([ [1, 0, 2, 0], [0, 1, 0, 2] ]) assert Matrix.vstack(eye(2), 2*eye(2)) == Matrix([ [1, 0], [0, 1], [2, 0], [0, 2] ]) cls = SparseMatrix assert cls.hstack(cls(eye(2)), cls(2*eye(2))) == Matrix([ [1, 0, 2, 0], [0, 1, 0, 2] ]) def test_cross(): a = [1, 2, 3] b = [3, 4, 5] col = Matrix([-2, 4, -2]) row = col.T def test(M, ans): assert ans == M assert type(M) == cls for cls in classes: A = cls(a) B = cls(b) test(A.cross(B), col) test(A.cross(B.T), col) test(A.T.cross(B.T), row) test(A.T.cross(B), row) raises(ShapeError, lambda: Matrix(1, 2, [1, 1]).cross(Matrix(1, 2, [1, 1]))) def test_hash(): for cls in classes[-2:]: s = set([cls.eye(1), cls.eye(1)]) assert len(s) == 1 and s.pop() == cls.eye(1) # issue 880 for cls in classes[:2]: assert not isinstance(cls.eye(1), collections.Hashable) @XFAIL def test_issue880(): # when this passes, delete this and change the [1:2] # to [:2] in the test_hash above for issue 880 cls = classes[0] raises(AttributeError, lambda: hash(cls.eye(1))) def test_adjoint(): dat = [[0, I], [1, 0]] ans = Matrix([[0, 1], [-I, 0]]) for cls in classes: assert ans == cls(dat).adjoint() def test_simplify(): from sympy import simplify, sin, cos assert simplify(ImmutableMatrix([[sin(x)**2 + cos(x)**2]])) == \ ImmutableMatrix([[1]]) def test_rank(): from sympy.abc import x m = Matrix([[1, 2], [x, 1 - 1/x]]) assert m.rank() == 2 n = Matrix(3, 3, range(1, 10)) assert n.rank() == 2 p = zeros(3) assert p.rank() == 0 def test_replace(): from sympy import symbols, Function, Matrix F, G = symbols('F, G', cls=Function) K = Matrix(2, 2, lambda i, j: G(i+j)) M = Matrix(2, 2, lambda i, j: F(i+j)) N = M.replace(F, G) assert N == K def test_replace_map(): from sympy import symbols, Function, Matrix F, G = symbols('F, G', cls=Function) K = Matrix(2, 2, [(G(0), {F(0): G(0)}), (G(1), {F(1): G(1)}), (G(1), {F(1)\ : G(1)}), (G(2), {F(2): G(2)})]) M = Matrix(2, 2, lambda i, j: F(i+j)) N = M.replace(F, G, True) assert N == K def test_atoms(): from sympy.abc import x m = Matrix([[1, 2], [x, 1 - 1/x]]) assert m.atoms() == set([S(1),S(2),S(-1), x]) assert m.atoms(Symbol) == set([x]) @slow def test_pinv(): from sympy.abc import a, b, c, d, e, f # Pseudoinverse of an invertible matrix is the inverse. A1 = Matrix([[a, b], [c, d]]) assert simplify(A1.pinv()) == simplify(A1.inv()) # Test the four properties of the pseudoinverse for various matrices. As = [Matrix([[13, 104], [2212, 3], [-3, 5]]), Matrix([[1, 7, 9], [11, 17, 19]]), Matrix([a, b])] for A in As: A_pinv = A.pinv() AAp = A * A_pinv ApA = A_pinv * A assert simplify(AAp * A) == A assert simplify(ApA * A_pinv) == A_pinv assert AAp.H == AAp assert ApA.H == ApA def test_pinv_solve(): # Fully determined system (unique result, identical to other solvers). A = Matrix([[1, 5], [7, 9]]) B = Matrix([12, 13]) assert A.pinv_solve(B) == A.cholesky_solve(B) assert A.pinv_solve(B) == A.LDLsolve(B) assert A.pinv_solve(B) == Matrix([sympify('-43/26'), sympify('71/26')]) assert A * A.pinv() * B == B # Fully determined, with two-dimensional B matrix. B = Matrix([[12, 13, 14], [15, 16, 17]]) assert A.pinv_solve(B) == A.cholesky_solve(B) assert A.pinv_solve(B) == A.LDLsolve(B) assert A.pinv_solve(B) == Matrix([[-33, -37, -41], [69, 75, 81]]) / 26 assert A * A.pinv() * B == B # Underdetermined system (infinite results). A = Matrix([[1, 0, 1], [0, 1, 1]]) B = Matrix([5, 7]) solution = A.pinv_solve(B) w = {} for s in solution.atoms(Symbol): # Extract dummy symbols used in the solution. w[s.name] = s assert solution == Matrix([[w['w0_0']/3 + w['w1_0']/3 - w['w2_0']/3 + 1], [w['w0_0']/3 + w['w1_0']/3 - w['w2_0']/3 + 3], [-w['w0_0']/3 - w['w1_0']/3 + w['w2_0']/3 + 4]]) assert A * A.pinv() * B == B # Overdetermined system (least squares results). A = Matrix([[1, 0], [0, 0], [0, 1]]) B = Matrix([3, 2, 1]) assert A.pinv_solve(B) == Matrix([3, 1]) # Proof the solution is not exact. assert A * A.pinv() * B != B @XFAIL def test_pinv_rank_deficient(): # Test the four properties of the pseudoinverse for various matrices. As = [Matrix([[1, 1, 1], [2, 2, 2]]), Matrix([[1, 0], [0, 0]])] for A in As: A_pinv = A.pinv() AAp = A * A_pinv ApA = A_pinv * A assert simplify(AAp * A) == A assert simplify(ApA * A_pinv) == A_pinv assert AAp.H == AAp assert ApA.H == ApA # Test solving with rank-deficient matrices. A = Matrix([[1, 0], [0, 0]]) # Exact, non-unique solution. B = Matrix([3, 0]) solution = A.pinv_solve(B) w1 = solution.atoms(Symbol).pop() assert w1.name == 'w1_0' assert solution == Matrix([3, w1]) assert A * A.pinv() * B == B # Least squares, non-unique solution. B = Matrix([3, 1]) solution = A.pinv_solve(B) w1 = solution.atoms(Symbol).pop() assert w1.name == 'w1_0' assert solution == Matrix([3, w1]) assert A * A.pinv() * B != B def test_issue4102(): assert ones(0, 1) + ones(0, 1) == Matrix(0, 1, []) assert ones(1, 0) + ones(1, 0) == Matrix(1, 0, []) sympy-0.7.4.1/sympy/matrices/tests/test_sparsetools.py0000644000175000017500000000347112253362407023372 0ustar georgeskgeorgeskfrom sympy.matrices.sparsetools import _doktocsr, _csrtodok from sympy import SparseMatrix def test_doktocsr(): a = SparseMatrix([[1, 2, 0, 0], [0, 3, 9, 0], [0, 1, 4, 0]]) b = SparseMatrix(4, 6, [10, 20, 0, 0, 0, 0, 0, 30, 0, 40, 0, 0, 0, 0, 50, 60, 70, 0, 0, 0, 0, 0, 0, 80]) c = SparseMatrix(4, 4, [0, 0, 0, 0, 0, 12, 0, 2, 15, 0, 12, 0, 0, 0, 0, 4]) d = SparseMatrix(10, 10, {(1, 1): 12, (3, 5): 7, (7, 8): 12}) e = SparseMatrix([[0, 0, 0], [1, 0, 2], [3, 0, 0]]) f = SparseMatrix(7, 8, {(2, 3): 5, (4, 5):12}) assert _doktocsr(a) == [[1, 2, 3, 9, 1, 4], [0, 1, 1, 2, 1, 2], [0, 2, 4, 6], [3, 4]] assert _doktocsr(b) == [[10, 20, 30, 40, 50, 60, 70, 80], [0, 1, 1, 3, 2, 3, 4, 5], [0, 2, 4, 7, 8], [4, 6]] assert _doktocsr(c) == [[12, 2, 15, 12, 4], [1, 3, 0, 2, 3], [0, 0, 2, 4, 5], [4, 4]] assert _doktocsr(d) == [[12, 7, 12], [1, 5, 8], [0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3], [10, 10]] assert _doktocsr(e) == [[1, 2, 3], [0, 2, 0], [0, 0, 2, 3], [3, 3]] assert _doktocsr(f) == [[5, 12], [3, 5], [0, 0, 0, 1, 1, 2, 2, 2], [7, 8]] def test_csrtodok(): h = [[5, 7, 5], [2, 1, 3], [0, 1, 1, 3], [3, 4]] g = [[12, 5, 4], [2, 4, 2], [0, 1, 2, 3], [3, 7]] i = [[1, 3, 12], [0, 2, 4], [0, 2, 3], [2, 5]] j = [[11, 15, 12, 15], [2, 4, 1, 2], [0, 1, 1, 2, 3, 4], [5, 8]] k = [[1, 3], [2, 1], [0, 1, 1, 2], [3, 3]] assert _csrtodok(h) == SparseMatrix(3, 4, {(0, 2): 5, (2, 1): 7, (2, 3): 5}) assert _csrtodok(g) == SparseMatrix(3, 7, {(0, 2): 12, (1, 4): 5, (2, 2): 4}) assert _csrtodok(i) == SparseMatrix([[1, 0, 3, 0, 0], [0, 0, 0, 0, 12]]) assert _csrtodok(j) == SparseMatrix(5, 8, {(0, 2): 11, (2, 4): 15, (3, 1): 12, (4, 2): 15}) assert _csrtodok(k) == SparseMatrix(3, 3, {(0, 2): 1, (2, 1): 3}) sympy-0.7.4.1/sympy/matrices/tests/__init__.py0000644000175000017500000000000012253362407021475 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/matrices/tests/test_sparse.py0000644000175000017500000004131712253362407022312 0ustar georgeskgeorgeskfrom sympy import S, Symbol, I, Rational, PurePoly from sympy.matrices import Matrix, SparseMatrix, eye, zeros, ShapeError from sympy.utilities.pytest import raises, XFAIL def test_sparse_matrix(): def sparse_eye(n): return SparseMatrix.eye(n) def sparse_zeros(n): return SparseMatrix.zeros(n) # creation args raises(TypeError, lambda: SparseMatrix(1, 2)) a = SparseMatrix(( (1, 0), (0, 1) )) assert SparseMatrix(a) == a # test element assignment a = SparseMatrix(( (1, 0), (0, 1) )) a[3] = 4 assert a[1, 1] == 4 a[3] = 1 a[0, 0] = 2 assert a == SparseMatrix(( (2, 0), (0, 1) )) a[1, 0] = 5 assert a == SparseMatrix(( (2, 0), (5, 1) )) a[1, 1] = 0 assert a == SparseMatrix(( (2, 0), (5, 0) )) assert a._smat == {(0, 0): 2, (1, 0): 5} # test_multiplication a = SparseMatrix(( (1, 2), (3, 1), (0, 6), )) b = SparseMatrix(( (1, 2), (3, 0), )) c = a*b assert c[0, 0] == 7 assert c[0, 1] == 2 assert c[1, 0] == 6 assert c[1, 1] == 6 assert c[2, 0] == 18 assert c[2, 1] == 0 x = Symbol("x") c = b * Symbol("x") assert isinstance(c, SparseMatrix) assert c[0, 0] == x assert c[0, 1] == 2*x assert c[1, 0] == 3*x assert c[1, 1] == 0 c = 5 * b assert isinstance(c, SparseMatrix) assert c[0, 0] == 5 assert c[0, 1] == 2*5 assert c[1, 0] == 3*5 assert c[1, 1] == 0 #test_power A = SparseMatrix([[2, 3], [4, 5]]) assert (A**5)[:] == [6140, 8097, 10796, 14237] A = SparseMatrix([[2, 1, 3], [4, 2, 4], [6, 12, 1]]) assert (A**3)[:] == [290, 262, 251, 448, 440, 368, 702, 954, 433] # test_creation x = Symbol("x") a = SparseMatrix([[x, 0], [0, 0]]) m = a assert m.cols == m.rows assert m.cols == 2 assert m[:] == [x, 0, 0, 0] b = SparseMatrix(2, 2, [x, 0, 0, 0]) m = b assert m.cols == m.rows assert m.cols == 2 assert m[:] == [x, 0, 0, 0] assert a == b S = sparse_eye(3) S.row_del(1) assert S == SparseMatrix([ [1, 0, 0], [0, 0, 1]]) S = sparse_eye(3) S.col_del(1) assert S == SparseMatrix([ [1, 0], [0, 0], [0, 1]]) S = SparseMatrix.eye(3) S[2, 1] = 2 S.col_swap(1, 0) assert S == SparseMatrix([ [0, 1, 0], [1, 0, 0], [2, 0, 1]]) a = SparseMatrix(1, 2, [1, 2]) b = a.copy() c = a.copy() assert a[0] == 1 a.row_del(0) assert a == SparseMatrix(0, 2, []) b.col_del(1) assert b == SparseMatrix(1, 1, [1]) # test_determinant x, y = Symbol('x'), Symbol('y') assert SparseMatrix(1, 1, [0]).det() == 0 assert SparseMatrix([[1]]).det() == 1 assert SparseMatrix(((-3, 2), (8, -5))).det() == -1 assert SparseMatrix(((x, 1), (y, 2*y))).det() == 2*x*y - y assert SparseMatrix(( (1, 1, 1), (1, 2, 3), (1, 3, 6) )).det() == 1 assert SparseMatrix(( ( 3, -2, 0, 5), (-2, 1, -2, 2), ( 0, -2, 5, 0), ( 5, 0, 3, 4) )).det() == -289 assert SparseMatrix(( ( 1, 2, 3, 4), ( 5, 6, 7, 8), ( 9, 10, 11, 12), (13, 14, 15, 16) )).det() == 0 assert SparseMatrix(( (3, 2, 0, 0, 0), (0, 3, 2, 0, 0), (0, 0, 3, 2, 0), (0, 0, 0, 3, 2), (2, 0, 0, 0, 3) )).det() == 275 assert SparseMatrix(( (1, 0, 1, 2, 12), (2, 0, 1, 1, 4), (2, 1, 1, -1, 3), (3, 2, -1, 1, 8), (1, 1, 1, 0, 6) )).det() == -55 assert SparseMatrix(( (-5, 2, 3, 4, 5), ( 1, -4, 3, 4, 5), ( 1, 2, -3, 4, 5), ( 1, 2, 3, -2, 5), ( 1, 2, 3, 4, -1) )).det() == 11664 assert SparseMatrix(( ( 2, 7, -1, 3, 2), ( 0, 0, 1, 0, 1), (-2, 0, 7, 0, 2), (-3, -2, 4, 5, 3), ( 1, 0, 0, 0, 1) )).det() == 123 # test_submatrix m0 = sparse_eye(4) assert m0[:3, :3] == sparse_eye(3) assert m0[2:4, 0:2] == sparse_zeros(2) m1 = SparseMatrix(3, 3, lambda i, j: i + j) assert m1[0, :] == SparseMatrix(1, 3, (0, 1, 2)) assert m1[1:3, 1] == SparseMatrix(2, 1, (2, 3)) m2 = SparseMatrix( [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15]]) assert m2[:, -1] == SparseMatrix(4, 1, [3, 7, 11, 15]) assert m2[-2:, :] == SparseMatrix([[8, 9, 10, 11], [12, 13, 14, 15]]) assert SparseMatrix([[1, 2], [3, 4]]).submatrix([1, 1]) == Matrix([[4]]) # test_submatrix_assignment m = sparse_zeros(4) m[2:4, 2:4] = sparse_eye(2) assert m == SparseMatrix([(0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)]) assert len(m._smat) == 2 m[:2, :2] = sparse_eye(2) assert m == sparse_eye(4) m[:, 0] = SparseMatrix(4, 1, (1, 2, 3, 4)) assert m == SparseMatrix([(1, 0, 0, 0), (2, 1, 0, 0), (3, 0, 1, 0), (4, 0, 0, 1)]) m[:, :] = sparse_zeros(4) assert m == sparse_zeros(4) m[:, :] = ((1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12), (13, 14, 15, 16)) assert m == SparseMatrix((( 1, 2, 3, 4), ( 5, 6, 7, 8), ( 9, 10, 11, 12), (13, 14, 15, 16))) m[:2, 0] = [0, 0] assert m == SparseMatrix((( 0, 2, 3, 4), ( 0, 6, 7, 8), ( 9, 10, 11, 12), (13, 14, 15, 16))) # test_reshape m0 = sparse_eye(3) assert m0.reshape(1, 9) == SparseMatrix(1, 9, (1, 0, 0, 0, 1, 0, 0, 0, 1)) m1 = SparseMatrix(3, 4, lambda i, j: i + j) assert m1.reshape(4, 3) == \ SparseMatrix([(0, 1, 2), (3, 1, 2), (3, 4, 2), (3, 4, 5)]) assert m1.reshape(2, 6) == \ SparseMatrix([(0, 1, 2, 3, 1, 2), (3, 4, 2, 3, 4, 5)]) # test_applyfunc m0 = sparse_eye(3) assert m0.applyfunc(lambda x: 2*x) == sparse_eye(3)*2 assert m0.applyfunc(lambda x: 0 ) == sparse_zeros(3) # test_LUdecomp testmat = SparseMatrix([[ 0, 2, 5, 3], [ 3, 3, 7, 4], [ 8, 4, 0, 2], [-2, 6, 3, 4]]) L, U, p = testmat.LUdecomposition() assert L.is_lower assert U.is_upper assert (L*U).permuteBkwd(p) - testmat == sparse_zeros(4) testmat = SparseMatrix([[ 6, -2, 7, 4], [ 0, 3, 6, 7], [ 1, -2, 7, 4], [-9, 2, 6, 3]]) L, U, p = testmat.LUdecomposition() assert L.is_lower assert U.is_upper assert (L*U).permuteBkwd(p) - testmat == sparse_zeros(4) x, y, z = Symbol('x'), Symbol('y'), Symbol('z') M = Matrix(((1, x, 1), (2, y, 0), (y, 0, z))) L, U, p = M.LUdecomposition() assert L.is_lower assert U.is_upper assert (L*U).permuteBkwd(p) - M == sparse_zeros(3) # test_LUsolve A = SparseMatrix([[2, 3, 5], [3, 6, 2], [8, 3, 6]]) x = SparseMatrix(3, 1, [3, 7, 5]) b = A*x soln = A.LUsolve(b) assert soln == x A = SparseMatrix([[0, -1, 2], [5, 10, 7], [8, 3, 4]]) x = SparseMatrix(3, 1, [-1, 2, 5]) b = A*x soln = A.LUsolve(b) assert soln == x # test_inverse A = sparse_eye(4) assert A.inv() == sparse_eye(4) assert A.inv(method="CH") == sparse_eye(4) assert A.inv(method="LDL") == sparse_eye(4) A = SparseMatrix([[2, 3, 5], [3, 6, 2], [7, 2, 6]]) Ainv = SparseMatrix(Matrix(A).inv()) assert A*Ainv == sparse_eye(3) assert A.inv(method="CH") == Ainv assert A.inv(method="LDL") == Ainv A = SparseMatrix([[2, 3, 5], [3, 6, 2], [5, 2, 6]]) Ainv = SparseMatrix(Matrix(A).inv()) assert A*Ainv == sparse_eye(3) assert A.inv(method="CH") == Ainv assert A.inv(method="LDL") == Ainv # test_cross v1 = Matrix(1, 3, [1, 2, 3]) v2 = Matrix(1, 3, [3, 4, 5]) assert v1.cross(v2) == Matrix(1, 3, [-2, 4, -2]) assert v1.norm(2)**2 == 14 # conjugate a = SparseMatrix(((1, 2 + I), (3, 4))) assert a.C == SparseMatrix([ [1, 2 - I], [3, 4] ]) # mul assert a*Matrix(2, 2, [1, 0, 0, 1]) == a assert a + Matrix(2, 2, [1, 1, 1, 1]) == SparseMatrix([ [2, 3 + I], [4, 5] ]) # col join assert a.col_join(sparse_eye(2)) == SparseMatrix([ [1, 2 + I], [3, 4], [1, 0], [0, 1] ]) # symmetric assert not a.is_symmetric(simplify=False) # test_cofactor assert sparse_eye(3) == sparse_eye(3).cofactorMatrix() test = SparseMatrix([[1, 3, 2], [2, 6, 3], [2, 3, 6]]) assert test.cofactorMatrix() == \ SparseMatrix([[27, -6, -6], [-12, 2, 3], [-3, 1, 0]]) test = SparseMatrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) assert test.cofactorMatrix() == \ SparseMatrix([[-3, 6, -3], [6, -12, 6], [-3, 6, -3]]) # test_jacobian x = Symbol('x') y = Symbol('y') L = SparseMatrix(1, 2, [x**2*y, 2*y**2 + x*y]) syms = [x, y] assert L.jacobian(syms) == Matrix([[2*x*y, x**2], [y, 4*y + x]]) L = SparseMatrix(1, 2, [x, x**2*y**3]) assert L.jacobian(syms) == SparseMatrix([[1, 0], [2*x*y**3, x**2*3*y**2]]) # test_QR A = Matrix([[1, 2], [2, 3]]) Q, S = A.QRdecomposition() R = Rational assert Q == Matrix([ [ 5**R(-1, 2), (R(2)/5)*(R(1)/5)**R(-1, 2)], [2*5**R(-1, 2), (-R(1)/5)*(R(1)/5)**R(-1, 2)]]) assert S == Matrix([ [5**R(1, 2), 8*5**R(-1, 2)], [ 0, (R(1)/5)**R(1, 2)]]) assert Q*S == A assert Q.T * Q == sparse_eye(2) R = Rational # test nullspace # first test reduced row-ech form M = SparseMatrix([[5, 7, 2, 1], [1, 6, 2, -1]]) out, tmp = M.rref() assert out == Matrix([[1, 0, -R(2)/23, R(13)/23], [0, 1, R(8)/23, R(-6)/23]]) M = SparseMatrix([[ 1, 3, 0, 2, 6, 3, 1], [-2, -6, 0, -2, -8, 3, 1], [ 3, 9, 0, 0, 6, 6, 2], [-1, -3, 0, 1, 0, 9, 3]]) out, tmp = M.rref() assert out == Matrix([[1, 3, 0, 0, 2, 0, 0], [0, 0, 0, 1, 2, 0, 0], [0, 0, 0, 0, 0, 1, R(1)/3], [0, 0, 0, 0, 0, 0, 0]]) # now check the vectors basis = M.nullspace() assert basis[0] == Matrix([-3, 1, 0, 0, 0, 0, 0]) assert basis[1] == Matrix([0, 0, 1, 0, 0, 0, 0]) assert basis[2] == Matrix([-2, 0, 0, -2, 1, 0, 0]) assert basis[3] == Matrix([0, 0, 0, 0, 0, R(-1)/3, 1]) # test eigen x = Symbol('x') y = Symbol('y') sparse_eye3 = sparse_eye(3) assert sparse_eye3.charpoly(x) == PurePoly(((x - 1)**3)) assert sparse_eye3.charpoly(y) == PurePoly(((y - 1)**3)) # test values M = Matrix([( 0, 1, -1), ( 1, 1, 0), (-1, 0, 1)]) vals = M.eigenvals() assert sorted(vals.keys()) == [-1, 1, 2] R = Rational M = Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) assert M.eigenvects() == [(1, 3, [ Matrix([1, 0, 0]), Matrix([0, 1, 0]), Matrix([0, 0, 1])])] M = Matrix([[5, 0, 2], [3, 2, 0], [0, 0, 1]]) assert M.eigenvects() == [(1, 1, [Matrix([R(-1)/2, R(3)/2, 1])]), (2, 1, [Matrix([0, 1, 0])]), (5, 1, [Matrix([1, 1, 0])])] assert M.zeros(3, 5) == SparseMatrix(3, 5, {}) A = SparseMatrix(10, 10, {(0, 0): 18, (0, 9): 12, (1, 4): 18, (2, 7): 16, (3, 9): 12, (4, 2): 19, (5, 7): 16, (6, 2): 12, (9, 7): 18}) assert A.row_list() == [(0, 0, 18), (0, 9, 12), (1, 4, 18), (2, 7, 16), (3, 9, 12), (4, 2, 19), (5, 7, 16), (6, 2, 12), (9, 7, 18)] assert A.col_list() == [(0, 0, 18), (4, 2, 19), (6, 2, 12), (1, 4, 18), (2, 7, 16), (5, 7, 16), (9, 7, 18), (0, 9, 12), (3, 9, 12)] assert SparseMatrix.eye(2).nnz() == 2 def test_transpose(): assert SparseMatrix(((1, 2), (3, 4))).transpose() == \ SparseMatrix(((1, 3), (2, 4))) def test_trace(): assert SparseMatrix(((1, 2), (3, 4))).trace() == 5 assert SparseMatrix(((0, 0), (0, 4))).trace() == 4 def test_CL_RL(): assert SparseMatrix(((1, 2), (3, 4))).row_list() == \ [(0, 0, 1), (0, 1, 2), (1, 0, 3), (1, 1, 4)] assert SparseMatrix(((1, 2), (3, 4))).col_list() == \ [(0, 0, 1), (1, 0, 3), (0, 1, 2), (1, 1, 4)] def test_add(): assert SparseMatrix(((1, 0), (0, 1))) + SparseMatrix(((0, 1), (1, 0))) == \ SparseMatrix(((1, 1), (1, 1))) a = SparseMatrix(100, 100, lambda i, j: int(j != 0 and i % j == 0)) b = SparseMatrix(100, 100, lambda i, j: int(i != 0 and j % i == 0)) assert (len(a._smat) + len(b._smat) - len((a + b)._smat) > 0) def test_errors(): raises(ValueError, lambda: SparseMatrix(1.4, 2, lambda i, j: 0)) raises(TypeError, lambda: SparseMatrix([1, 2, 3], [1, 2])) raises(ValueError, lambda: SparseMatrix([[1, 2], [3, 4]])[(1, 2, 3)]) raises(IndexError, lambda: SparseMatrix([[1, 2], [3, 4]])[5]) raises(ValueError, lambda: SparseMatrix([[1, 2], [3, 4]])[1, 2, 3]) raises(TypeError, lambda: SparseMatrix([[1, 2], [3, 4]]).copyin_list([0, 1], set([]))) raises( IndexError, lambda: SparseMatrix([[1, 2], [3, 4]]).submatrix((1, 2))) raises(TypeError, lambda: SparseMatrix([1, 2, 3]).cross(1)) raises(IndexError, lambda: SparseMatrix(1, 2, [1, 2])[3]) raises(ShapeError, lambda: SparseMatrix(1, 2, [1, 2]) + SparseMatrix(2, 1, [2, 1])) def test_len(): assert not SparseMatrix() assert SparseMatrix() == SparseMatrix([]) @XFAIL def test_len_different_shapes(): assert Matrix() == Matrix([[]]) assert SparseMatrix() == SparseMatrix([[]]) def test_sparse_zeros_sparse_eye(): assert SparseMatrix.eye(3) == eye(3, cls=SparseMatrix) assert len(SparseMatrix.eye(3)._smat) == 3 assert SparseMatrix.zeros(3) == zeros(3, cls=SparseMatrix) assert len(SparseMatrix.zeros(3)._smat) == 0 def test_copyin(): s = SparseMatrix(3, 3, {}) s[1, 0] = 1 assert s[:, 0] == SparseMatrix(Matrix([0, 1, 0])) assert s[3] == 1 assert s[3: 4] == [1] s[1, 1] = 42 assert s[1, 1] == 42 assert s[1, 1:] == SparseMatrix([[42, 0]]) s[1, 1:] = Matrix([[5, 6]]) assert s[1, :] == SparseMatrix([[1, 5, 6]]) s[1, 1:] = [[42, 43]] assert s[1, :] == SparseMatrix([[1, 42, 43]]) s[0, 0] = 17 assert s[:, :1] == SparseMatrix([17, 1, 0]) s[0, 0] = [1, 1, 1] assert s[:, 0] == SparseMatrix([1, 1, 1]) s[0, 0] = Matrix([1, 1, 1]) assert s[:, 0] == SparseMatrix([1, 1, 1]) s[0, 0] = SparseMatrix([1, 1, 1]) assert s[:, 0] == SparseMatrix([1, 1, 1]) def test_sparse_solve(): from sympy.matrices import SparseMatrix A = SparseMatrix(((25, 15, -5), (15, 18, 0), (-5, 0, 11))) assert A.cholesky() == Matrix([ [ 5, 0, 0], [ 3, 3, 0], [-1, 1, 3]]) assert A.cholesky() * A.cholesky().T == Matrix([ [25, 15, -5], [15, 18, 0], [-5, 0, 11]]) A = SparseMatrix(((25, 15, -5), (15, 18, 0), (-5, 0, 11))) L, D = A.LDLdecomposition() assert 15*L == Matrix([ [15, 0, 0], [ 9, 15, 0], [-3, 5, 15]]) assert D == Matrix([ [25, 0, 0], [ 0, 9, 0], [ 0, 0, 9]]) assert L * D * L.T == A A = SparseMatrix(((3, 0, 2), (0, 0, 1), (1, 2, 0))) assert A.inv() * A == SparseMatrix(eye(3)) A = SparseMatrix([ [ 2, -1, 0], [-1, 2, -1], [ 0, 0, 2]]) ans = SparseMatrix([ [S(2)/3, S(1)/3, S(1)/6], [S(1)/3, S(2)/3, S(1)/3], [ 0, 0, S(1)/2]]) assert A.inv(method='CH') == ans assert A.inv(method='LDL') == ans assert A * ans == SparseMatrix(eye(3)) s = A.solve(A[:, 0], 'LDL') assert A*s == A[:, 0] s = A.solve(A[:, 0], 'CH') assert A*s == A[:, 0] A = A.col_join(A) s = A.solve_least_squares(A[:, 0], 'CH') assert A*s == A[:, 0] s = A.solve_least_squares(A[:, 0], 'LDL') assert A*s == A[:, 0] sympy-0.7.4.1/sympy/matrices/__init__.py0000644000175000017500000000205512253362407020347 0ustar georgeskgeorgesk"""A module that handles matrices. Includes functions for fast creating matrices like zero, one/eye, random matrix, etc. """ from .matrices import (DeferredVector, ShapeError, NonSquareMatrixError, MatrixBase) from .dense import ( GramSchmidt, Matrix, casoratian, diag, eye, hessian, jordan_cell, list2numpy, matrix2numpy, matrix_multiply_elementwise, ones, randMatrix, rot_axis1, rot_axis2, rot_axis3, symarray, wronskian, zeros) MutableDenseMatrix = MutableMatrix = Matrix from .sparse import MutableSparseMatrix SparseMatrix = MutableSparseMatrix from .immutable import ImmutableMatrix, ImmutableSparseMatrix MutableSparseMatrix = SparseMatrix ImmutableDenseMatrix = ImmutableMatrix from .expressions import (MatrixSlice, BlockDiagMatrix, BlockMatrix, FunctionMatrix, Identity, Inverse, MatAdd, MatMul, MatPow, MatrixExpr, MatrixSymbol, Trace, Transpose, ZeroMatrix, blockcut, block_collapse, matrix_symbols, Adjoint, hadamard_product, HadamardProduct, Determinant, det, DiagonalMatrix, DiagonalOf, trace) sympy-0.7.4.1/sympy/matrices/matrices.py0000644000175000017500000037205712253362407020433 0ustar georgeskgeorgeskfrom __future__ import print_function, division import collections from sympy.core.add import Add from sympy.core.basic import Basic, C, Atom 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, symbols from sympy.core.numbers import Integer, ilcm, Rational, Float from sympy.core.singleton import S from sympy.core.sympify import sympify from sympy.core.compatibility import is_sequence, default_sort_key, xrange from sympy.polys import PurePoly, roots, cancel, gcd 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.functions import exp, factorial from sympy.printing import sstr from sympy.core.compatibility import reduce, as_int from sympy.utilities.exceptions import SymPyDeprecationWarning from types import FunctionType def _iszero(x): """Returns True if x is zero.""" return x.is_zero class MatrixError(Exception): pass class ShapeError(ValueError, MatrixError): """Wrong matrix shape""" pass 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) class MatrixBase(object): # Added just for numpy compatibility __array_priority__ = 11 is_Matrix = True is_Identity = None _class_priority = 3 _sympify = staticmethod(sympify) __hash__ = None # Mutable @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)) ) Matrix([ [1, 2 + I], [3, 4]]) * from un-nested iterable (interpreted as a column) >>> Matrix( [1, 2] ) Matrix([ [1], [2]]) * from un-nested iterable with dimensions >>> Matrix(1, 2, [1, 2] ) Matrix([[1, 2]]) * from no arguments (a 0 x 0 matrix) >>> Matrix() Matrix(0, 0, []) * from a rule >>> Matrix(2, 2, lambda i, j: i/(j + 1) ) Matrix([ [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([cls._sympify(operation(cls._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 = [cls._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 = [cls._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] = cls._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])\ and not isinstance(args[0], DeferredVector): 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 = [cls._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(cls._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 Matrix([ [1, 2 + I], [3, 4]]) >>> m[1, 0] = 9 >>> m Matrix([ [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 Matrix([ [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 Matrix([ [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, Basic) 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, self._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 inv_mod(self, m): r""" Returns the inverse of the matrix `K` (mod `m`), if it exists. Method to find the matrix inverse of `K` (mod `m`) implemented in this function: * Compute `\mathrm{adj}(K) = \mathrm{cof}(K)^t`, the adjoint matrix of `K`. * Compute `r = 1/\mathrm{det}(K) \pmod m`. * `K^{-1} = r\cdot \mathrm{adj}(K) \pmod m`. Examples ======== >>> from sympy import Matrix >>> A = Matrix(2, 2, [1, 2, 3, 4]) >>> A.inv_mod(5) Matrix([ [3, 1], [4, 2]]) >>> A.inv_mod(3) Matrix([ [1, 1], [0, 1]]) """ from sympy.ntheory import totient if not self.is_square: raise NonSquareMatrixError() N = self.cols phi = totient(m) det_K = self.det() if gcd(det_K, m) != 1: raise ValueError('Matrix is not invertible (mod %d)' % m) det_inv = pow(int(det_K), int(phi - 1), int(m)) K_adj = self.cofactorMatrix().transpose() K_inv = self.__class__(N, N, [det_inv*K_adj[i, j] % m for i in range(N) for j in range(N)]) return K_inv 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.") def adjoint(self): """Conjugate transpose or Hermitian conjugation.""" return self.T.C @property def H(self): """Return Hermite conjugate. Examples ======== >>> from sympy import Matrix, I >>> m = Matrix((0, 1 + I, 2, 3)) >>> m Matrix([ [ 0], [1 + I], [ 2], [ 3]]) >>> m.H Matrix([[0, 1 - I, 2, 3]]) See Also ======== conjugate: By-element conjugation D: Dirac conjugation """ return self.T.C @property 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 Matrix([[0, 1 - I, -2, -3]]) >>> m = (eye(4) + I*eye(4)) >>> m[0, 3] = 2 >>> m.D Matrix([ [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 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 Matrix([ [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("Matrix 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])) rv = classof(A, B)._new(ret) if not A.rows: rv = rv.reshape(*A.shape) return rv 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 def multiply(self, b): """Returns self*b See Also ======== dot cross multiply_elementwise """ return self*b def add(self, b): """Return self + b """ return self + b def table(self, printer, rowsep='\n', colsep=', ', align='right'): r""" String form of Matrix as a table. ``printer`` is the printer to use for on the elements (generally something like StrPrinter()) ``rowsep`` is the string used to separate rows (by default a newline). ``colsep`` is the string used to separate columns (by default ', '). ``align`` defines how the elements are aligned. Must be one of 'left', 'right', or 'center'. You can also use '<', '>', and '^' to mean the same thing, respectively. This is used by the string printer for Matrix. Examples ======== >>> from sympy import Matrix >>> from sympy.printing.str import StrPrinter >>> M = Matrix([[1, 2], [-33, 4]]) >>> printer = StrPrinter() >>> M.table(printer) '[ 1, 2]\n[-33, 4]' >>> print(M.table(printer)) [ 1, 2] [-33, 4] >>> print(M.table(printer, rowsep=',\n')) [ 1, 2], [-33, 4] >>> print('[%s]' % M.table(printer, rowsep=',\n')) [[ 1, 2], [-33, 4]] >>> print(M.table(printer, colsep=' ')) [ 1 2] [-33 4] >>> print(M.table(printer, align='center')) [ 1 , 2] [-33, 4] """ # 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): s = printer._print(self[i,j]) res[-1].append(s) maxlen[j] = max(len(s), maxlen[j]) # Patch strings together align = { 'left': str.ljust, 'right': str.rjust, 'center': str.center, '<': str.ljust, '>': str.rjust, '^': str.center, }[align] for i, row in enumerate(res): for j, elem in enumerate(row): row[j] = align(elem, maxlen[j]) res[i] = "[" + colsep.join(row) + "]" return rowsep.join(res) def _format_str(self, printer=None): if not printer: from sympy.printing.str import StrPrinter printer = StrPrinter() # Handle zero dimensions: if self.rows == 0 or self.cols == 0: return 'Matrix(%s, %s, [])' % (self.rows, self.cols) if self.rows == 1: return "Matrix([%s])" % self.table(printer, rowsep=',\n') return "Matrix([\n%s])" % self.table(printer, rowsep=',\n') def __str__(self): if self.rows == 0 or self.cols == 0: return 'Matrix(%s, %s, [])' % (self.rows, self.cols) return "Matrix(%s)" % str(self.tolist()) def __repr__(self): return sstr(self) 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() Matrix([ [ 5, 0, 0], [ 3, 3, 0], [-1, 1, 3]]) >>> A.cholesky() * A.cholesky().T Matrix([ [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() 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 Matrix([ [ 1, 0, 0], [ 3/5, 1, 0], [-1/5, 1/3, 1]]) >>> D Matrix([ [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() 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 pinv_solve """ 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) 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 pinv_solve """ 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) 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 pinv_solve """ 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) 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 pinv_solve """ 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) 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 pinv_solve """ 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) 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, ones >>> A = Matrix([1, 2, 3]) >>> B = Matrix([2, 3, 4]) >>> S = Matrix(A.row_join(B)) >>> S Matrix([ [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 Matrix([ [ 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 Matrix([ [ 5/3], [10/3]]) The error is given by S*xy - r: >>> S*xy - r Matrix([ [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 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 += "" for j in range(self.cols): mml += self[i, j].__mathml__() mml += "" return "" + mml + "" 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 Matrix([ [0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6]]) >>> m[:1, 1] Matrix([[1]]) >>> m[:2, :1] Matrix([ [0], [1]]) >>> m[2:4, 2:4] Matrix([ [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) 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, range(12)) >>> m Matrix([ [0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]) >>> m.extract([0, 1, 3], [0, 1]) Matrix([ [0, 1], [3, 4], [9, 10]]) Rows or columns can be repeated: >>> m.extract([0, 0, 1], [-1]) Matrix([ [2], [2], [5]]) Every other row can be taken by using range to provide the indices: >>> m.extract(range(0, m.rows, 2), [-1]) Matrix([ [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]]) 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 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) 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 def atoms(self, *types): """Returns the atoms that form the current object. >>> from sympy.abc import x, y >>> from sympy.matrices import Matrix >>> Matrix([[x]]) Matrix([[x]]) >>> _.atoms() set([x]) """ if types: types = tuple( [t if isinstance(t, type) else type(t) for t in types]) else: types = (Atom,) result = set() for i in self: result.update( i.atoms(*types) ) return result 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]) Matrix([[x]]) >>> _.subs(x, y) Matrix([[y]]) >>> Matrix(_).subs(y, x) Matrix([[x]]) """ return self.applyfunc(lambda x: x.subs(*args, **kwargs)) 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)]) Matrix([[x*(x + 1)]]) >>> _.expand() Matrix([[x**2 + x]]) """ return self.applyfunc(lambda x: x.expand( deep, modulus, power_base, power_exp, mul, log, multinomial, basic, **hints)) 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]) Matrix([[x*sin(y)**2 + x*cos(y)**2]]) >>> _.simplify() Matrix([[x]]) """ return self.applyfunc(lambda x: x.simplify(ratio, measure)) _eval_simplify = simplify def doit(self, **kwargs): return self 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 Matrix([ [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)) 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 pinv_solve 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 xrange(n): for j in xrange(i): scale = A[i, j] b.zip_row_op(i, j, lambda x, y: x - y*scale) # backward substitution for i in xrange(n - 1, -1, -1): for j in xrange(i + 1, n): scale = A[i, j] b.zip_row_op(i, j, lambda x, y: x - y*scale) scale = A[i, i] b.row_op(i, lambda x, _: x/scale) return rhs.__class__(b) 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 Matrix([ [ 1, 0], [3/2, 1]]) >>> U Matrix([ [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 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 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 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 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) 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) 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) 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) Matrix([ [cos(phi), -rho*sin(phi)], [sin(phi), rho*cos(phi)], [ 2*rho, 0]]) >>> X = Matrix([rho*cos(phi), rho*sin(phi)]) >>> X.jacobian(Y) Matrix([ [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])) 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 >>> A = Matrix([[12, -51, 4], [6, 167, -68], [-4, 24, -41]]) >>> Q, R = A.QRdecomposition() >>> Q Matrix([ [ 6/7, -69/175, -58/175], [ 3/7, 158/175, 6/175], [-2/7, 6/35, -33/35]]) >>> R Matrix([ [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 Matrix([ [1, 0, 0], [0, 1, 0], [0, 0, 1]]) >>> R Matrix([ [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) 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 pinv_solve 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)]) def cross(self, b): """Return the cross product of `self` and `b` relaxing the condition of compatible dimensions: if each has 3 elements, a matrix of the same type and shape as `self` will be returned. If `b` has the same shape as `self` then common identities for the cross product (like `a x b = - b x a`) will hold. 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 * self.cols == b.rows * b.cols == 3): raise ShapeError("Dimensions incorrect for cross product.") else: return self._new(self.rows, self.cols, ( (self[1]*b[2] - self[2]*b[1]), (self[2]*b[0] - self[0]*b[2]), (self[0]*b[1] - self[1]*b[0]))) 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.") 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) Matrix([ [ 0, 10, 200], [300, 40, 5]]) See Also ======== cross dot multiply """ from sympy.matrices import matrix_multiply_elementwise return matrix_multiply_elementwise(self, b) def values(self): """Return non-zero values of self.""" return [i for i in flatten(self.tolist()) if not i.is_zero] 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() 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") 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 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) Matrix([[sqrt(3)/2, 0]]) >>> V.project(-x) Matrix([[sqrt(3)/2, 0]]) """ return v*(self.dot(v) / v.dot(v)) 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]]) Matrix([ [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 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]]) Matrix([ [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 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: P, cells = self.jordan_cells() except MatrixError: raise NotImplementedError("Exponentiation is implemented only for matrices for which the Jordan normal form can be computed") def _jblock_exponential(b): # This function computes the matrix exponential for one single Jordan block nr = b.rows l = b[0, 0] if nr == 1: res = C.exp(l) else: from sympy import eye # extract the diagonal part d = b[0, 0]*eye(nr) #and the nilpotent part n = b-d # compute its exponential nex = eye(nr) for i in range(1, nr): nex = nex+n**i/factorial(i) # combine the two parts res = exp(b[0, 0])*nex return(res) blocks = list(map(_jblock_exponential, cells)) from sympy.matrices import diag eJ = diag(* blocks) # n = self.rows ret = P*eJ*P.inv() return type(self)(ret) @property 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 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()) 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 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 Matrix([ [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 Matrix([ [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 Matrix([ [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 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 Matrix([ [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 Matrix([ [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 Matrix([ [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 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 Matrix([ [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 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 Matrix([ [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)) 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 self.values()) 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 Matrix([ [0, 1], [1, 2]]) >>> m.is_symmetric() True >>> m = Matrix(2, 2, [0, 1, 2, 0]) >>> m Matrix([ [0, 1], [2, 0]]) >>> m.is_symmetric() False >>> m = Matrix(2, 3, [0, 0, 0, 0, 0, 0]) >>> m Matrix([ [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 Matrix([ [ 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() 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 Matrix([ [ 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 Matrix([ [ 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 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 Matrix([ [1, 0], [0, 2]]) >>> m.is_diagonal() True >>> m = Matrix(2, 2, [1, 1, 0, 2]) >>> m Matrix([ [1, 1], [0, 2]]) >>> m.is_diagonal() False >>> m = diag(1, 2, 3) >>> m Matrix([ [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 def det(self, method="bareis"): """Computes the matrix determinant using the method "method". Possible values for "method": bareis ... det_bareis berkowitz ... berkowitz_det det_LU ... det_LU_decomposition 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) 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] elif n == 3: det = (M[0, 0]*M[1, 1]*M[2, 2] + M[0, 1]*M[1, 2]*M[2, 0] + M[0, 2]*M[1, 0]*M[2, 1]) - \ (M[0, 2]*M[1, 1]*M[2, 0] + M[0, 0]*M[1, 2]*M[2, 1] + M[0, 1]*M[1, 0]*M[2, 2]) 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() 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() 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 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) 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:]) 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 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() (Matrix([ [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 xrange(r.cols): if pivot == r.rows: break if simplify: r[pivot, i] = simpfunc(r[pivot, i]) if iszerofunc(r[pivot, i]): for k in xrange(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 xrange(r.rows): if j == pivot: continue scale = r[j, i] r.zip_row_op(j, pivot, lambda x, y: x - scale*y) pivotlist.append(i) pivot += 1 return self._new(r), pivotlist def rank(self, simplified=False, iszerofunc=_iszero, simplify=False): """ Returns the rank of a matrix >>> from sympy import Matrix >>> from sympy.abc import x >>> m = Matrix([[1, 2], [x, 1 - 1/x]]) >>> m.rank() 2 >>> n = Matrix(3, 3, range(1, 10)) >>> n.rank() 2 """ row_reduced = self.rref(simplified=simplified, iszerofunc=iszerofunc, simplify=simplify) rank = len(row_reduced[-1]) return rank 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] 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)) 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] 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) 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 def berkowitz_eigenvals(self, **flags): """Computes eigenvalues of a Matrix using Berkowitz method. See Also ======== berkowitz """ return roots(self.berkowitz_charpoly(Dummy('x')), **flags) 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) 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 def singular_values(self): """Compute the singular values of a Matrix Examples ======== >>> from sympy import Matrix, Symbol >>> 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 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 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)) 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, )) Matrix([ [x**2/2, x*y], [ x, 0]]) >>> M.integrate((x, 0, 2)) Matrix([ [2, 2*y], [2, 0]]) See Also ======== limit diff """ return self._new(self.rows, self.cols, lambda i, j: self[i, j].integrate(*args)) 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) Matrix([ [2, y], [1, 0]]) See Also ======== integrate diff """ return self._new(self.rows, self.cols, lambda i, j: self[i, j].limit(*args)) 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) Matrix([ [1, 0], [0, 0]]) See Also ======== integrate limit """ return self._new(self.rows, self.cols, lambda i, j: self[i, j].diff(*args)) 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 Matrix([ [1, 3], [2, 4]]) >>> m.vec() Matrix([ [1], [2], [3], [4]]) See Also ======== vech """ return self.T.reshape(len(self), 1) 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 Matrix([ [1, 2], [2, 3]]) >>> m.vech() Matrix([ [1], [2], [3]]) >>> m.vech(diagonal=False) Matrix([[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 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 >>> 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 Matrix([ [1, 3], [y, z**2]]) >>> a2 Matrix([[x]]) >>> a3 Matrix([[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 def diagonalize(self, reals_only=False, sort=False, normalize=False): """ Return (P, D), where D is diagonal and 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 Matrix([ [1, 2, 0], [0, 3, 0], [2, -4, 2]]) >>> (P, D) = m.diagonalize() >>> D Matrix([ [1, 0, 0], [0, 2, 0], [0, 0, 3]]) >>> P Matrix([ [-1, 0, -1], [ 0, 0, -1], [ 2, 1, 2]]) >>> P.inv() * m * P Matrix([ [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) 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 Matrix([ [1, 2, 0], [0, 3, 0], [2, -4, 2]]) >>> m.is_diagonalizable() True >>> m = Matrix(2, 2, [0, 1, 0, 0]) >>> m Matrix([ [0, 1], [0, 0]]) >>> m.is_diagonalizable() False >>> m = Matrix(2, 2, [0, 1, -1, 0]) >>> m Matrix([ [ 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 def jordan_cell(self, eigenval, n): n = int(n) from sympy.matrices import MutableMatrix out = MutableMatrix.zeros(n) for i in range(n-1): out[i, i] = eigenval out[i, i+1] = 1 out[n-1, n-1] = eigenval return type(self)(out) def _jordan_block_structure(self): # To every eingenvalue may belong `i` blocks with size s(i) # and a chain of generalized eigenvectors # which will be determined by the following computations: # for every eigenvalue we will add a dictionary # containing, for all blocks, the blockssizes and the attached chain vectors # that will eventually be used to form the transformation P jordan_block_structures = {} _eigenvects = self.eigenvects() ev = self.eigenvals() if len(ev) == 0: raise AttributeError("could not compute the eigenvalues") for eigenval, multiplicity, vects in _eigenvects: l_jordan_chains={} geometrical = len(vects) if geometrical == multiplicity: # The Jordan chains have all length 1 and consist of only one vector # which is the eigenvector of course chains = [] for v in vects: chain=[v] chains.append(chain) l_jordan_chains[1] = chains jordan_block_structures[eigenval] = l_jordan_chains elif geometrical == 0: raise MatrixError("Matrix has the eigen vector with geometrical multiplicity equal zero.") else: # Up to now we know nothing about the sizes of the blocks of our Jordan matrix. # Note that knowledge of algebraic and geometrical multiplicity # will *NOT* be sufficient to determine this structure. # The blocksize `s` could be defined as the minimal `k` where # `kernel(self-lI)^k = kernel(self-lI)^(k+1)` # The extreme case would be that k = (multiplicity-geometrical+1) # but the blocks could be smaller. # Consider for instance the following matrix # [2 1 0 0] # [0 2 1 0] # [0 0 2 0] # [0 0 0 2] # which coincides with it own Jordan canonical form. # It has only one eigenvalue l=2 of (algebraic) multiplicity=4. # It has two eigenvectors, one belonging to the last row (blocksize 1) # and one being the last part of a jordan chain of length 3 (blocksize of the first block). # Note again that it is not not possible to obtain this from the algebraic and geometrical # multiplicity alone. This only gives us an upper limit for the dimension of one of # the subspaces (blocksize of according jordan block) given by # max=(multiplicity-geometrical+1) which is reached for our matrix # but not for # [2 1 0 0] # [0 2 0 0] # [0 0 2 1] # [0 0 0 2] # although multiplicity=4 and geometrical=2 are the same for this matrix. from sympy.matrices import MutableMatrix I = MutableMatrix.eye(self.rows) l = eigenval M = (self-l*I) # We will store the matrices `(self-l*I)^k` for further computations # for convenience only we store `Ms[0]=(sefl-lI)^0=I` # so the index is the same as the power for all further Ms entries # We also store the vectors that span these kernels (Ns[0] = []) # and also their dimensions `a_s` # this is mainly done for debugging since the number of blocks of a given size # can be computed from the a_s, in order to check our result which is obtained simpler # by counting the number of jordanchains for `a` given `s` # `a_0` is `dim(Kernel(Ms[0]) = dim (Kernel(I)) = 0` since `I` is regular l_jordan_chains={} chain_vectors=[] Ms = [I] Ns = [[]] a = [0] smax = 0 M_new = Ms[-1]*M Ns_new = M_new.nullspace() a_new = len(Ns_new) Ms.append(M_new) Ns.append(Ns_new) while a_new > a[-1]: # as long as the nullspaces increase compute further powers a.append(a_new) M_new = Ms[-1]*M Ns_new = M_new.nullspace() a_new=len(Ns_new) Ms.append(M_new) Ns.append(Ns_new) smax += 1 # We now have `Ms[-1]=((self-l*I)**s)=Z=0` # We now know the size of the biggest jordan block # associatet with `l` to be `s` # now let us proceed with the computation of the associate part of the transformation matrix `P` # We already know the kernel (=nullspace) `K_l` of (self-lI) which consists of the # eigenvectors belonging to eigenvalue `l` # The dimension of this space is the geometric multiplicity of eigenvalue `l`. # For every eigenvector ev out of `K_l`, there exists a subspace that is # spanned by the jordan chain of ev. The dimension of this subspace is # represented by the length s of the jordan block. # The chain itself is given by `{e_0,..,e_s-1}` where: # `e_k+1 =(self-lI)e_k (*)` # and # `e_s-1=ev` # So it would be possible to start with the already known `ev` and work backwards until one # reaches `e_0`. Unfortunately this can not be done by simply solving system (*) since its matrix # is singular (by definition of the eigenspaces). # This approach would force us a choose in every step the degree of freedom undetermined # by (*). This is difficult to implement with computer algebra systems and also quite unefficient. # We therefore reformulate the problem in terms of nullspaces. # To do so we start from the other end and choose `e0`'s out of # `E=Kernel(self-lI)^s / Kernel(self-lI)^(s-1)` # Note that `Kernel(self-lI)^s = Kernel(Z) = V` (the whole vector space). # So in the first step `s=smax` this restriction turns out to actually restrict nothing at all # and the only remaining condition is to choose vectors in `Kernel(self-lI)^(s-1)`. # Subsequently we compute `e_1=(self-lI)e_0`, `e_2=(self-lI)*e_1` and so on. # The subspace `E` can have a dimension larger than one. # That means that we have more than one Jordanblocks of size `s` for the eigenvalue `l` # and as many jordanchains (This is the case in the second example). # In this case we start as many jordan chains and have as many blocks of size s in the jcf. # We now have all the jordanblocks of size `s` but there might be others attached to the same # eigenvalue that are smaller. # So we will do the same procedure also for `s-1` and so on until 1 the lowest possible order # where the jordanchain is of lenght 1 and just represented by the eigenvector. for s in reversed(xrange(1, smax+1)): S = Ms[s] # We want the vectors in `Kernel((self-lI)^s)` (**), # but without those in `Kernel(self-lI)^s-1` so we will add these as additional equations # to the sytem formed by `S` (`S` will no longer be quadratic but this does not harm # since S is rank deficiant). exclude_vectors = Ns[s-1] for k in range(0, a[s-1]): S = S.col_join((exclude_vectors[k]).transpose()) # We also want to exclude the vectors in the chains for the bigger blogs # that we have already computed (if there are any). # (That is why we start wiht the biggest s). ######## Implementation remark: ######## # Doing so for *ALL* already computed chain vectors # we actually exclude some vectors twice because they are already excluded # by the condition (**). # This happens if there are more than one blocks attached to the same eigenvalue *AND* # the current blocksize is smaller than the block whose chain vectors we exclude. # If the current block has size `s_i` and the next bigger block has size `s_i-1` then # the first `s_i-s_i-1` chainvectors of the bigger block are allready excluded by (**). # The unnecassary adding of these equations could be avoided if the algorithm would # take into account the lengths of the already computed chains which are already stored # and add only the last `s` items. # However the following loop would be a good deal more nested to do so. # Since adding a linear dependent equation does not change the result, # it can harm only in terms of efficiency. # So to be sure i let it there for the moment # A more elegant alternative approach might be to drop condition (**) altogether # because it is added implicitly by excluding the chainvectors but the original author # of this code was not sure if this is correct in all cases. l = len(chain_vectors) if l > 0: for k in range(0, l): old = chain_vectors[k].transpose() S = S.col_join(old) e0s = S.nullspace() # Determine the number of chain leaders which equals the number of blocks with that size. n_e0 = len(e0s) s_chains = [] # s_cells=[] for i in range(0, n_e0): chain=[e0s[i]] for k in range(1, s): v = M*chain[k-1] chain.append(v) # We want the chain leader appear as the last of the block. chain.reverse() chain_vectors += chain s_chains.append(chain) l_jordan_chains[s] = s_chains jordan_block_structures[eigenval] = l_jordan_chains return jordan_block_structures def jordan_form(self, calc_transformation=True): r"""Return Jordan form J of current matrix. Also the transformation P such that `J = P^{-1} \cdot M \cdot P` and the jordan blocks forming J will be calculated. 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 Matrix([ [2, 1, 0, 0], [0, 2, 0, 0], [0, 0, 2, 1], [0, 0, 0, 2]]) See Also ======== jordan_cells """ P, Jcells = self.jordan_cells() from sympy.matrices import diag J = diag(*Jcells) return P, type(self)(J) def jordan_cells(self, calc_transformation=True): r"""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} \cdot M \cdot 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] Matrix([ [2, 1], [0, 2]]) >>> Jcells[1] Matrix([ [2, 1], [0, 2]]) See Also ======== jordan_form """ n = self.rows Jcells = [] Pcols_new = [] jordan_block_structures = self._jordan_block_structure() from sympy.matrices import MutableMatrix # Order according to default_sort_key, this makes sure the order is the same as in .diagonalize(): for eigenval in (sorted(list(jordan_block_structures.keys()), key=default_sort_key)): l_jordan_chains = jordan_block_structures[eigenval] for s in reversed(sorted((l_jordan_chains).keys())): # Start with the biggest block s_chains = l_jordan_chains[s] block = self.jordan_cell(eigenval, s) number_of_s_chains=len(s_chains) for i in range(0, number_of_s_chains): Jcells.append(type(self)(block)) chain_vectors = s_chains[i] lc = len(chain_vectors) assert lc == s for j in range(0, lc): generalized_eigen_vector = chain_vectors[j] Pcols_new.append(generalized_eigen_vector) P = MutableMatrix.zeros(n) for j in range(0, n): P[:, j] = Pcols_new[j] return type(self)(P), 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 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) 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 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)) Matrix([ [1, 0, 2, 0], [0, 1, 0, 2]]) """ return reduce(cls.row_join, args) @classmethod 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)) Matrix([ [1, 0], [0, 1], [2, 0], [0, 2]]) """ return reduce(cls.col_join, args) def row_join(self, rhs): """Concatenates two matrices along self's last and rhs's first column Examples ======== >>> from sympy import zeros, ones >>> M = zeros(3) >>> V = ones(3, 1) >>> M.row_join(V) Matrix([ [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.") from sympy.matrices import MutableMatrix newmat = MutableMatrix.zeros(self.rows, self.cols + rhs.cols) newmat[:, :self.cols] = self newmat[:, self.cols:] = rhs return type(self)(newmat) def col_join(self, bott): """Concatenates two matrices along self's last and bott's first row Examples ======== >>> from sympy import zeros, ones >>> M = zeros(3) >>> V = ones(1, 3) >>> M.col_join(V) Matrix([ [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.") from sympy.matrices import MutableMatrix newmat = MutableMatrix.zeros(self.rows + bott.rows, self.cols) newmat[:self.rows, :] = self newmat[self.rows:, :] = bott return type(self)(newmat) def row_insert(self, pos, mti): """Insert one or more rows at the given row position. Examples ======== >>> from sympy import zeros, ones >>> M = zeros(3) >>> V = ones(1, 3) >>> M.row_insert(1, V) Matrix([ [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 def col_insert(self, pos, mti): """Insert one or more columns at the given column position. Examples ======== >>> from sympy import zeros, ones >>> M = zeros(3) >>> V = ones(3, 1) >>> M.col_insert(1, V) Matrix([ [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.") from sympy.matrices import MutableMatrix newmat = MutableMatrix.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 type(self)(newmat) def replace(self, F, G, map=False): """Replaces Function F in Matrix entries with Function G. Examples ======== >>> from sympy import symbols, Function, Matrix >>> F, G = symbols('F, G', cls=Function) >>> M = Matrix(2, 2, lambda i, j: F(i+j)) ; M Matrix([ [F(0), F(1)], [F(1), F(2)]]) >>> N = M.replace(F,G) >>> N Matrix([ [G(0), G(1)], [G(1), G(2)]]) """ M = self[:, :] return M.applyfunc(lambda x: x.replace(F, G, map)) def pinv(self): """Calculate the Moore-Penrose pseudoinverse of the matrix. The Moore-Penrose pseudoinverse exists and is unique for any matrix. If the matrix is invertible, the pseudoinverse is the same as the inverse. Examples ======== >>> from sympy import Matrix >>> Matrix([[1, 2, 3], [4, 5, 6]]).pinv() Matrix([ [-17/18, 4/9], [ -1/9, 1/9], [ 13/18, -2/9]]) See Also ======== inv pinv_solve References ========== .. [1] https://en.wikipedia.org/wiki/Moore-Penrose_pseudoinverse """ A = self AH = self.H # Trivial case: pseudoinverse of all-zero matrix is its transpose. if A.is_zero: return AH try: if self.rows >= self.cols: return (AH * A).inv() * AH else: return AH * (A * AH).inv() except ValueError: # Matrix is not full rank, so A*AH cannot be inverted. raise NotImplementedError('Rank-deficient matrices are not yet ' 'supported.') def pinv_solve(self, B, arbitrary_matrix=None): """Solve Ax = B using the Moore-Penrose pseudoinverse. There may be zero, one, or infinite solutions. If one solution exists, it will be returned. If infinite solutions exist, one will be returned based on the value of arbitrary_matrix. If no solutions exist, the least-squares solution is returned. Parameters ========== B : Matrix The right hand side of the equation to be solved for. Must have the same number of rows as matrix A. arbitrary_matrix : Matrix If the system is underdetermined (e.g. A has more columns than rows), infinite solutions are possible, in terms of an arbitrary matrix. This parameter may be set to a specific matrix to use for that purpose; if so, it must be the same shape as x, with as many rows as matrix A has columns, and as many columns as matrix B. If left as None, an appropriate matrix containing dummy symbols in the form of ``wn_m`` will be used, with n and m being row and column position of each symbol. Returns ======= x : Matrix The matrix that will satisfy Ax = B. Will have as many rows as matrix A has columns, and as many columns as matrix B. Examples ======== >>> from sympy import Matrix >>> A = Matrix([[1, 2, 3], [4, 5, 6]]) >>> B = Matrix([7, 8]) >>> A.pinv_solve(B) Matrix([ [ _w0_0/6 - _w1_0/3 + _w2_0/6 - 55/18], [-_w0_0/3 + 2*_w1_0/3 - _w2_0/3 + 1/9], [ _w0_0/6 - _w1_0/3 + _w2_0/6 + 59/18]]) >>> A.pinv_solve(B, arbitrary_matrix=Matrix([0, 0, 0])) Matrix([ [-55/18], [ 1/9], [ 59/18]]) See Also ======== lower_triangular_solve upper_triangular_solve cholesky_solve diagonal_solve LDLsolve LUsolve QRsolve pinv Notes ===== This may return either exact solutions or least squares solutions. To determine which, check ``A * A.pinv() * B == B``. It will be True if exact solutions exist, and False if only a least-squares solution exists. Be aware that the left hand side of that equation may need to be simplified to correctly compare to the right hand side. References ========== .. [1] https://en.wikipedia.org/wiki/Moore-Penrose_pseudoinverse#Obtaining_all_solutions_of_a_linear_system """ from sympy.matrices import eye A = self A_pinv = self.pinv() if arbitrary_matrix is None: rows, cols = A.cols, B.cols w = symbols('w:{0}_:{1}'.format(rows, cols), cls=Dummy) arbitrary_matrix = self.__class__(cols, rows, w).T return A_pinv * B + (eye(A.cols) - A_pinv*A) * arbitrary_matrix 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) """ 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__)) 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) sympy-0.7.4.1/sympy/crypto/0000755000175000017500000000000012253362407015745 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/crypto/crypto.py0000644000175000017500000013526312253362407017651 0ustar georgeskgeorgesk""" Classical ciphers and LFSRs """ from __future__ import print_function from random import randrange from sympy import nextprime from sympy.core import Rational, S, Symbol from sympy.core.numbers import igcdex from sympy.matrices import Matrix from sympy.ntheory import isprime, totient, primitive_root from sympy.polys.domains import FF from sympy.polys.polytools import gcd, Poly from sympy.utilities.iterables import flatten, uniq def alphabet_of_cipher(symbols="ABCDEFGHIJKLMNOPQRSTUVWXYZ"): """ Returns the list of characters in the string input defining the alphabet. Notes ===== First, some basic definitions. A *substitution cipher* is a method of encryption by which "units" (not necessarily characters) of plaintext are replaced with ciphertext according to a regular system. The "units" may be characters (ie, words of length `1`), words of length `2`, and so forth. A *transposition cipher* is a method of encryption by which the positions held by "units" of plaintext are replaced by a permutation of the plaintext. That is, the order of the units is changed using a bijective function on the characters' positions to perform the encryption. A *monoalphabetic cipher* uses fixed substitution over the entire message, whereas a *polyalphabetic cipher* uses a number of substitutions at different times in the message. Each of these ciphers require an alphabet for the messages to be constructed from. Examples ======== >>> from sympy.crypto.crypto import alphabet_of_cipher >>> alphabet_of_cipher() ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] >>> L = [str(i) for i in range(10)] + ['a', 'b', 'c']; L ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c'] >>> A = "".join(L); A '0123456789abc' >>> alphabet_of_cipher(A) ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c'] >>> alphabet_of_cipher() ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] """ symbols = "".join(symbols) return list(symbols) ######## shift cipher examples ############ def cycle_list(k, n): """ Returns the cyclic shift of the list range(n) by k. Examples ======== >>> from sympy.crypto.crypto import cycle_list, alphabet_of_cipher >>> L = cycle_list(3,26); L [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 1, 2] >>> A = alphabet_of_cipher(); A ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] >>> [A[i] for i in L] ['D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'A', 'B', 'C'] """ L = list(range(n)) return L[k:] + L[:k] def encipher_shift(pt, key, symbols="ABCDEFGHIJKLMNOPQRSTUVWXYZ"): """ Performs shift cipher encryption on plaintext pt, and returns the ciphertext. Notes ===== The shift cipher is also called the Caesar cipher, after Julius Caesar, who, according to Suetonius, used it with a shift of three to protect messages of military significance. Caesar's nephew Augustus reportedtly used a similar cipher, but with a right shift of 1. ALGORITHM: INPUT: ``k``: an integer from 0 to 25 (the secret key) ``m``: string of upper-case letters (the plaintext message) OUTPUT: ``c``: string of upper-case letters (the ciphertext message) STEPS: 0. Identify the alphabet A, ..., Z with the integers 0, ..., 25. 1. Compute from the string ``m`` a list ``L1`` of corresponding integers. 2. Compute from the list ``L1`` a new list ``L2``, given by adding ``(k mod 26)`` to each element in ``L1``. 3. Compute from the list ``L2`` a string ``c`` of corresponding letters. Examples ======== >>> from sympy.crypto.crypto import encipher_shift >>> pt = "GONAVYBEATARMY" >>> encipher_shift(pt, 1) 'HPOBWZCFBUBSNZ' >>> encipher_shift(pt, 0) 'GONAVYBEATARMY' >>> encipher_shift(pt, -1) 'FNMZUXADZSZQLX' """ symbols = "".join(symbols) A = alphabet_of_cipher(symbols) n = len(A) L = cycle_list(key, n) C = [A[(A.index(pt[i]) + key) % n] for i in range(len(pt))] return "".join(C) ######## affine cipher examples ############ def encipher_affine(pt, key, symbols="ABCDEFGHIJKLMNOPQRSTUVWXYZ"): r""" Performs the affine cipher encryption on plaintext ``pt``, and returns the ciphertext. Encryption is based on the map `x \rightarrow ax+b` (mod `26`). Decryption is based on the map `x \rightarrow cx+d` (mod `26`), where `c = a^{-1}` (mod `26`) and `d = -a^{-1}c` (mod `26`). (In particular, for the map to be invertible, we need `\mathrm{gcd}(a, 26) = 1.`) Notes ===== This is a straightforward generalization of the shift cipher. ALGORITHM: INPUT: ``a, b``: a pair integers, where ``gcd(a, 26) = 1`` (the secret key) ``m``: string of upper-case letters (the plaintext message) OUTPUT: ``c``: string of upper-case letters (the ciphertext message) STEPS: 0. Identify the alphabet "A", ..., "Z" with the integers 0, ..., 25. 1. Compute from the string ``m`` a list ``L1`` of corresponding integers. 2. Compute from the list ``L1`` a new list ``L2``, given by replacing ``x`` by ``a*x + b (mod 26)``, for each element ``x`` in ``L1``. 3. Compute from the list ``L2`` a string ``c`` of corresponding letters. Examples ======== >>> from sympy.crypto.crypto import encipher_affine >>> pt = "GONAVYBEATARMY" >>> encipher_affine(pt, (1, 1)) 'HPOBWZCFBUBSNZ' >>> encipher_affine(pt, (1, 0)) 'GONAVYBEATARMY' >>> pt = "GONAVYBEATARMY" >>> encipher_affine(pt, (3, 1)) 'TROBMVENBGBALV' >>> ct = "TROBMVENBGBALV" >>> encipher_affine(ct, (9, 17)) 'GONAVYBEATARMY' """ symbols = "".join(symbols) A = alphabet_of_cipher(symbols) n = len(A) k1 = key[0] # multiplicative coeff "a" k2 = key[1] # additive coeff "b" L = cycle_list(k2, n) C = [A[(k1*A.index(pt[i]) + k2) % n] for i in range(len(pt))] return "".join(C) #################### substitution cipher ########################### def encipher_substitution(pt, key, symbols="ABCDEFGHIJKLMNOPQRSTUVWXYZ"): """ Performs the substitution cipher encryption on plaintext ``pt``, and returns the ciphertext. Assumes the ``pt`` has only letters taken from ``symbols``. Assumes ``key`` is a permutation of the symbols. This funciton permutes the letters of the plaintext using the permutation given in ``key``. The decription uses the inverse permutation. Note that if the permutation in key is order 2 (eg, a transposition) then the encryption permutation and the decryption permutation are the same. Examples ======== >>> from sympy.crypto.crypto import alphabet_of_cipher, encipher_substitution >>> symbols = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" >>> A = alphabet_of_cipher(symbols) >>> key = "BACDEFGHIJKLMNOPQRSTUVWXYZ" >>> pt = "go navy! beat army!" >>> encipher_substitution(pt, key) 'GONBVYAEBTBRMY' >>> ct = 'GONBVYAEBTBRMY' >>> encipher_substitution(ct, key) 'GONAVYBEATARMY' """ symbols = "".join(symbols) A = alphabet_of_cipher(symbols) n = len(A) pt0 = [x.capitalize() for x in pt if x.isalnum()] ct = [key[A.index(x)] for x in pt0] return "".join(ct) ###################################################################### #################### Vigenere cipher examples ######################## ###################################################################### def encipher_vigenere(pt, key, symbols="ABCDEFGHIJKLMNOPQRSTUVWXYZ"): """ Performs the Vigenere cipher encryption on plaintext ``pt``, and returns the ciphertext. Notes ===== The Vigenere cipher is named after Blaise de Vigenere, a sixteenth century diplomat and cryptographer, by a historical accident. Vigene`re actually invented a different and more complicated cipher. The so-called *Vigenere cipher* cipher was actually invented by Giovan Batista Belaso in 1553. This cipher was used in the 1700's, for example, during the American Civil War. The Confederacy used a brass cipher disk to implement the Vigenere cipher (now on display in the NSA Museum in Fort Meade) [1]_. The Vigenere cipher is a generalization of the shift cipher. Whereas the shift cipher shifts each letter by the same amount (that amount being the key of the shift cipher) the Vigenere cipher shifts a letter by an amount determined by the key, which is a word or phrase known only to the sender and receiver). For example, if the key was a single letter, such as "C", then the so-called Vigenere cipher is actually a shift cipher with a shift of `2` (since "C" is the 2nd letter of the alphabet, if you start counting at `0`). If the key was a word with two letters, such as "CA", then the so-called Vigenere cipher will shift letters in even positions by `2` and letters in odd positions are left alone (shifted by `0`, since "A" is the 0th letter, if you start counting at `0`). ALGORITHM: INPUT: ``key``: a string of upper-case letters (the secret key) ``m``: string of upper-case letters (the plaintext message) OUTPUT: ``c``: string of upper-case letters (the ciphertext message) STEPS: 0. Identify the alphabet A, ..., Z with the integers 0, ..., 25. 1. Compute from the string ``key`` a list ``L1`` of corresponding integers. Let ``n1 = len(L1)``. 2. Compute from the string ``m`` a list ``L2`` of corresponding integers. Let ``n2 = len(L2)``. 3. Break ``L2`` up sequencially into sublists of size ``n1``, and one sublist at the end of size smaller or equal to ``n1``. 4. For each of these sublists ``L`` of ``L2``, compute a new list ``C`` given by ``C[i] = L[i] + L1[i] (mod 26)`` to the ``i``-th element in the sublist, for each ``i``. 5. Assemble these lists ``C`` by concatenation into a new list of length ``n2``. 6. Compute from the new list a string ``c`` of corresponding letters. Once it is known that the key is, say, `n` characters long, frequency analysis can be applied to every `n`-th letter of the ciphertext to determine the plaintext. This method is called *Kasiski examination* (although it was first discovered by Babbage). The cipher Vigenere actually discovered is an "auto-key" cipher described as follows. ALGORITHM: INPUT: ``key``: a string of upper-case letters (the secret key) ``m``: string of upper-case letters (the plaintext message) OUTPUT: ``c``: string of upper-case letters (the ciphertext message) STEPS: 0. Identify the alphabet A, ..., Z with the integers 0, ..., 25. 1. Compute from the string ``m`` a list ``L2`` of corresponding integers. Let ``n2 = len(L2)``. 2. Let ``n1`` be the length of the key. Concatenate the string ``key`` with the first ``n2 - n1`` characters of the plaintext message. Compute from this string of length ``n2`` a list ``L1`` of corresponding integers. Note ``n2 = len(L1)``. 3. Compute a new list ``C`` given by ``C[i] = L1[i] + L2[i] (mod 26)``, for each ``i``. Note ``n2 = len(C)``. 4. Compute from the new list a string ``c`` of corresponding letters. References ========== .. [1] http://en.wikipedia.org/wiki/Vigenere_cipher Examples ======== >>> from sympy.crypto.crypto import encipher_vigenere >>> key = "encrypt" >>> pt = "meet me on monday" >>> encipher_vigenere(pt, key) 'QRGKKTHRZQEBPR' """ symbols = "".join(symbols) A = alphabet_of_cipher(symbols) N = len(A) # normally, 26 key0 = uniq(key) key0 = [x.capitalize() for x in key0 if x.isalnum()] K = [A.index(x) for x in key0] k = len(K) pt0 = [x.capitalize() for x in pt if x.isalnum()] P = [A.index(x) for x in pt0] n = len(P) #m = n//k C = [(K[i % k] + P[i]) % N for i in range(n)] return "".join([str(A[x]) for x in C]) def decipher_vigenere(ct, key, symbols="ABCDEFGHIJKLMNOPQRSTUVWXYZ"): """ Decode using the Vigenere cipher. Examples ======== >>> from sympy.crypto.crypto import decipher_vigenere >>> key = "encrypt" >>> ct = "QRGK kt HRZQE BPR" >>> decipher_vigenere(ct, key) 'MEETMEONMONDAY' """ symbols = "".join(symbols) A = alphabet_of_cipher(symbols) N = len(A) # normally, 26 key0 = uniq(key) key0 = [x.capitalize() for x in key0 if x.isalnum()] K = [A.index(x) for x in key0] k = len(K) ct0 = [x.capitalize() for x in ct if x.isalnum()] C = [A.index(x) for x in ct0] n = len(C) #m = n//k P = [(-K[i % k] + C[i]) % N for i in range(n)] return "".join([str(A[x]) for x in P]) #################### Hill cipher ######################## def encipher_hill(pt, key, symbols="ABCDEFGHIJKLMNOPQRSTUVWXYZ"): r""" Performs the Hill cipher encryption on plaintext ``pt``, and returns the ciphertext. Notes ===== The Hill cipher [1]_, invented by Lester S. Hill in 1920's [2]_, was the first polygraphic cipher in which it was practical (though barely) to operate on more than three symbols at once. The following discussion assumes an elementary knowledge of matrices. First, each letter is first encoded as a number. We assume here that "A" `\leftrightarrow` 0, "B" `\leftrightarrow` 1, ..., "Z" `\leftrightarrow` 25. We denote the integers `\{0, 1, ..., 25\}` by `Z_{26}`. Suppose your message m consists of `n` capital letters, with no spaces. This may be regarded an `n`-tuple M of elements of `Z_{26}`. A key in the Hill cipher is a `k x k` matrix `K`, all of whose entries are in `Z_{26}`, such that the matrix `K` is invertible (ie, that the linear transformation `K: Z_{26}^k \rightarrow Z_{26}^k` is one-to-one). ALGORITHM: INPUT: ``key``: a `k x k` invertible matrix `K`, all of whose entries are in `Z_{26}` ``m``: string of `n` upper-case letters (the plaintext message) (Note: Sage assumes that `n` is a multiple of `k`.) OUTPUT: ``c``: string of upper-case letters (the ciphertext message) STEPS: 0. Identify the alphabet A, ..., Z with the integers 0, ..., 25. 1. Compute from the string ``m`` a list ``L`` of corresponding integers. Let ``n = len(L)``. 2. Break the list ``L`` up into ``t = ceiling(n/k)`` sublists ``L_1``, ..., ``L_t`` of size ``k`` (where the last list might be "padded" by 0's to ensure it is size ``k``). 3. Compute new list ``C_1``, ..., ``C_t`` given by ``C[i] = K*L_i`` (arithmetic is done mod 26), for each ``i``. 4. Concatenate these into a list ``C = C_1 + ... + C_t``. 5. Compute from ``C`` a string ``c`` of corresponding letters. This has length ``k*t``. References ========== .. [1] en.wikipedia.org/wiki/Hill_cipher .. [2] Lester S. Hill, Cryptography in an Algebraic Alphabet, The American Mathematical Monthly Vol.36, June-July 1929, pp.306-312. Examples ======== >>> from sympy.crypto.crypto import encipher_hill >>> from sympy import Matrix >>> pt = "meet me on monday" >>> key = Matrix([[1, 2], [3, 5]]) >>> encipher_hill(pt, key) 'UEQDUEODOCTCWQ' >>> pt = "meet me on tuesday" >>> encipher_hill(pt, key) 'UEQDUEODHBOYDJYU' >>> pt = "GONAVYBEATARMY" >>> key = Matrix([[1, 0, 1], [0, 1, 1], [2, 2, 3]]) >>> encipher_hill(pt, key) 'TBBYTKBEKKRLMYU' """ symbols = "".join(symbols) A = alphabet_of_cipher(symbols) N = len(A) # normally, 26 k = key.cols pt0 = [x.capitalize() for x in pt if x.isalnum()] P = [A.index(x) for x in pt0] n = len(P) m = n//k if n > m*k: P = P + [0]*(n - m*k) m = m + 1 C = [list(key*Matrix(k, 1, [P[i] for i in range(k*j, k*(j + 1))])) for j in range(m)] C = flatten(C) return "".join([A[i % N] for i in C]) def decipher_hill(ct, key, symbols="ABCDEFGHIJKLMNOPQRSTUVWXYZ"): """ Deciphering is the same as ciphering but using the inverse of the key matrix. Examples ======== >>> from sympy.crypto.crypto import decipher_hill >>> from sympy import Matrix >>> ct = "UEQDUEODOCTCWQ" >>> key = Matrix([[1, 2], [3, 5]]) >>> decipher_hill(ct, key) 'MEETMEONMONDAY' >>> ct = "UEQDUEODHBOYDJYU" >>> decipher_hill(ct, key) 'MEETMEONTUESDAYA' """ symbols = "".join(symbols) A = alphabet_of_cipher(symbols) N = len(A) # normally, 26 k = key.cols ct0 = [x.capitalize() for x in ct if x.isalnum()] C = [A.index(x) for x in ct0] n = len(C) m = n//k if n > m*k: C = C + [0]*(n - m*k) m = m + 1 key_inv = key.inv_mod(N) P = [list(key_inv*Matrix(k, 1, [C[i] for i in range(k*j, k*(j + 1))])) for j in range(m)] P = flatten(P) return "".join([A[i % N] for i in P]) #################### Bifid cipher ######################## def encipher_bifid5(pt, key): r""" Performs the Bifid cipher encryption on plaintext ``pt``, and returns the ciphertext. This is the version of the Bifid cipher that uses the `5 \times 5` Polybius square. Notes ===== The Bifid cipher was invented around 1901 by Felix Delastelle. It is a *fractional substitution* cipher, where letters are replaced by pairs of symbols from a smaller alphabet. The cipher uses a `5 \times 5` square filled with some ordering of the alphabet, except that "i"s and "j"s are identified (this is a so-called Polybius square; there is a `6 \times 6` analog if you add back in "j" and also append onto the usual 26 letter alphabet, the digits 0, 1, ..., 9). According to Helen Gaines' book *Cryptanalysis*, this type of cipher was used in the field by the German Army during World War I. ALGORITHM: (5x5 case) INPUT: ``pt``: plaintext string (no "j"s) ``key``: short string for key (no repetitions, no "j"s) OUTPUT: ciphertext (using Bifid5 cipher in all caps, no spaces, no "J"s) STEPS: 1. Create the `5 \times 5` Polybius square ``S`` associated to the ``k`` as follows: a) starting top left, moving left-to-right, top-to-bottom, place the letters of the key into a 5x5 matrix, b) when finished, add the letters of the alphabet not in the key until the 5x5 square is filled 2. Create a list ``P`` of pairs of numbers which are the coordinates in the Polybius square of the letters in ``pt``. 3. Let ``L1`` be the list of all first coordinates of ``P`` (length of ``L1 = n``), let ``L2`` be the list of all second coordinates of ``P`` (so the length of ``L2`` is also ``n``). 4. Let ``L`` be the concatenation of ``L1`` and ``L2`` (length ``L = 2*n``), except that consecutive numbers are paired ``(L[2*i], L[2*i + 1])``. You can regard ``L`` as a list of pairs of length ``n``. 5. Let ``C`` be the list of all letters which are of the form ``S[i, j]``, for all ``(i, j)`` in ``L``. As a string, this is the ciphertext ``ct``. Examples ======== >>> from sympy.crypto.crypto import encipher_bifid5 >>> pt = "meet me on monday" >>> key = "encrypt" >>> encipher_bifid5(pt, key) 'LNLLQNPPNPGADK' >>> pt = "meet me on friday" >>> encipher_bifid5(pt, key) 'LNLLFGPPNPGRSK' """ A = alphabet_of_cipher() # first make sure the letters are capitalized # and text has no spaces key = uniq(key) key0 = [x.capitalize() for x in key if x.isalnum()] pt0 = [x.capitalize() for x in pt if x.isalnum()] # create long key long_key = key0 + [x for x in A if (not(x in key0) and x != "J")] n = len(pt0) # the fractionalization pairs = [[long_key.index(x)//5, long_key.index(x) % 5] for x in pt0] tmp_cipher = flatten([x[0] for x in pairs] + [x[1] for x in pairs]) ct = "".join([long_key[5*tmp_cipher[2*i] + tmp_cipher[2*i + 1]] for i in range(n)]) return ct def decipher_bifid5(ct, key): r""" Performs the Bifid cipher decryption on ciphertext ``ct``, and returns the plaintext. This is the version of the Bifid cipher that uses the `5 \times 5` Polybius square. INPUT: ``ct``: ciphertext string (digits okay) ``key``: short string for key (no repetitions, digits okay) OUTPUT: plaintext from Bifid5 cipher (all caps, no spaces, no "J"s) Examples ======== >>> from sympy.crypto.crypto import encipher_bifid5, decipher_bifid5 >>> key = "encrypt" >>> pt = "meet me on monday" >>> encipher_bifid5(pt, key) 'LNLLQNPPNPGADK' >>> ct = 'LNLLQNPPNPGADK' >>> decipher_bifid5(ct, key) 'MEETMEONMONDAY' """ A = alphabet_of_cipher() # first make sure the letters are capitalized # and text has no spaces key = uniq(key) key0 = [x.capitalize() for x in key if x.isalnum()] ct0 = [x.capitalize() for x in ct if x.isalnum()] # create long key long_key = key0 + [x for x in A if (not(x in key0) and x != "J")] n = len(ct0) # the fractionalization pairs = flatten([[long_key.index(x)//5, long_key.index(x) % 5] for x in ct0 if x != "J"]) tmp_plain = flatten([[pairs[i], pairs[n + i]] for i in range(n)]) pt = "".join([long_key[5*tmp_plain[2*i] + tmp_plain[2*i + 1]] for i in range(n)]) return pt def bifid5_square(key): r""" 5x5 Polybius square. Produce the Polybius square for the `5 \times 5` Bifid cipher. Examples ======== >>> from sympy.crypto.crypto import bifid5_square >>> bifid5_square("gold bug") Matrix([ [G, O, L, D, B], [U, A, C, E, F], [H, I, K, M, N], [P, Q, R, S, T], [V, W, X, Y, Z]]) """ A = alphabet_of_cipher() # first make sure the letters are capitalized # and key has no spaces or duplicates key = uniq(key) key0 = [x.capitalize() for x in key if x.isalnum()] # create long key long_key = key0 + [x for x in A if (not(x in key0) and x != "J")] f = lambda i, j: Symbol(long_key[5*i + j]) M = Matrix(5, 5, f) return M def encipher_bifid6(pt, key): r""" Performs the Bifid cipher encryption on plaintext ``pt``, and returns the ciphertext. This is the version of the Bifid cipher that uses the `6 \times 6` Polybius square. Assumes alphabet of symbols is "A", ..., "Z", "0", ..., "9". INPUT: ``pt``: plaintext string (digits okay) ``key``: short string for key (no repetitions, digits okay) OUTPUT: ciphertext from Bifid cipher (all caps, no spaces) Examples ======== >>> from sympy.crypto.crypto import encipher_bifid6 >>> key = "encrypt" >>> pt = "meet me on monday at 8am" >>> encipher_bifid6(pt, key) 'HNHOKNTA5MEPEGNQZYG' >>> encipher_bifid6(pt, key) 'HNHOKNTA5MEPEGNQZYG' """ A = alphabet_of_cipher() + [str(a) for a in range(10)] # first make sure the letters are capitalized # and text has no spaces key = uniq(key) key0 = [x.capitalize() for x in key if x.isalnum()] pt0 = [x.capitalize() for x in pt if x.isalnum()] # create long key long_key = key0 + [x for x in A if not(x in key0)] n = len(pt0) # the fractionalization pairs = [[long_key.index(x)//6, long_key.index(x) % 6] for x in pt0] tmp_cipher = flatten([x[0] for x in pairs] + [x[1] for x in pairs]) ct = "".join([long_key[6*tmp_cipher[2*i] + tmp_cipher[2*i + 1]] for i in range(n)]) return ct def decipher_bifid6(ct, key): r""" Performs the Bifid cipher decryption on ciphertext ``ct``, and returns the plaintext. This is the version of the Bifid cipher that uses the `6 \times 6` Polybius square. Assumes alphabet of symbols is "A", ..., "Z", "0", ..., "9". INPUT: ``ct``: ciphertext string (digits okay) ``key``: short string for key (no repetitions, digits okay) OUTPUT: plaintext from Bifid cipher (all caps, no spaces) Examples ======== >>> from sympy.crypto.crypto import encipher_bifid6, decipher_bifid6 >>> key = "encrypt" >>> pt = "meet me on monday at 8am" >>> encipher_bifid6(pt, key) 'HNHOKNTA5MEPEGNQZYG' >>> ct = "HNHOKNTA5MEPEGNQZYG" >>> decipher_bifid6(ct, key) 'MEETMEONMONDAYAT8AM' """ A = alphabet_of_cipher() + [str(a) for a in range(10)] # first make sure the letters are capitalized # and text has no spaces key = uniq(key) key0 = [x.capitalize() for x in key if x.isalnum()] ct0 = [x.capitalize() for x in ct if x.isalnum()] # create long key long_key = key0 + [x for x in A if not(x in key0)] n = len(ct0) # the fractionalization pairs = flatten([[long_key.index(x)//6, long_key.index(x) % 6] for x in ct0]) tmp_plain = flatten([[pairs[i], pairs[n + i]] for i in range(n)]) pt = "".join([long_key[6*tmp_plain[2*i] + tmp_plain[2*i + 1]] for i in range(n)]) return pt def bifid6_square(key): r""" 6x6 Polybius square. Produces the Polybius square for the `6 \times 6` Bifid cipher. Assumes alphabet of symbols is "A", ..., "Z", "0", ..., "9". Examples ======== >>> from sympy.crypto.crypto import bifid6_square >>> key = "encrypt" >>> bifid6_square(key) Matrix([ [E, N, C, R, Y, P], [T, A, B, D, F, G], [H, I, J, K, L, M], [O, Q, S, U, V, W], [X, Z, 0, 1, 2, 3], [4, 5, 6, 7, 8, 9]]) """ A = alphabet_of_cipher() + [str(a) for a in range(10)] # first make sure the letters are capitalized # and text has no spaces key = uniq(key) key0 = [x.capitalize() for x in key if x.isalnum()] # create long key long_key = key0 + [x for x in A if not(x in key0)] f = lambda i, j: Symbol(long_key[6*i + j]) M = Matrix(6, 6, f) return M def encipher_bifid7(pt, key): r""" Performs the Bifid cipher encryption on plaintext ``pt``, and returns the ciphertext. This is the version of the Bifid cipher that uses the `7 \times 7` Polybius square. Assumes alphabet of symbols is "A", ..., "Z", "0", ..., "22". (Also, assumes you have some way of distinguishing "22" from "2", "2" juxtaposed together for deciphering...) INPUT: ``pt``: plaintext string (digits okay) ``key``: short string for key (no repetitions, digits okay) OUTPUT: ciphertext from Bifid7 cipher (all caps, no spaces) Examples ======== >>> from sympy.crypto.crypto import encipher_bifid7 >>> key = "encrypt" >>> pt = "meet me on monday at 8am" >>> encipher_bifid7(pt, key) 'JEJJLNAA3ME19YF3J222R' """ A = alphabet_of_cipher() + [str(a) for a in range(23)] # first make sure the letters are capitalized # and text has no spaces key = uniq(key) key0 = [x.capitalize() for x in key if x.isalnum()] pt0 = [x.capitalize() for x in pt if x.isalnum()] # create long key long_key = key0 + [x for x in A if not(x in key0)] n = len(pt0) # the fractionalization pairs = [[long_key.index(x)//7, long_key.index(x) % 7] for x in pt0] tmp_cipher = flatten([x[0] for x in pairs] + [x[1] for x in pairs]) ct = "".join([long_key[7*tmp_cipher[2*i] + tmp_cipher[2*i + 1]] for i in range(n)]) return ct def bifid7_square(key): r""" 7x7 Polybius square. Produce the Polybius square for the `7 \times 7` Bifid cipher. Assumes alphabet of symbols is "A", ..., "Z", "0", ..., "22". (Also, assumes you have some way of distinguishing "22" from "2", "2" juxtaposed together for deciphering...) Examples ======== >>> from sympy.crypto.crypto import bifid7_square >>> bifid7_square("gold bug") Matrix([ [ G, O, L, D, B, U, A], [ C, E, F, H, I, J, K], [ M, N, P, Q, R, S, T], [ V, W, X, Y, Z, 0, 1], [ 2, 3, 4, 5, 6, 7, 8], [ 9, 10, 11, 12, 13, 14, 15], [16, 17, 18, 19, 20, 21, 22]]) """ A = alphabet_of_cipher() + [str(a) for a in range(23)] # first make sure the letters are capitalized # and text has no spaces key = uniq(key) key0 = [x.capitalize() for x in key if x.isalnum()] # create long key long_key = key0 + [x for x in A if (not(x in key0))] f = lambda i, j: Symbol(long_key[7*i + j]) M = Matrix(7, 7, f) return M #################### RSA ############################# def rsa_public_key(p, q, e): r""" The RSA *public key* is the pair `(n,e)`, where `n` is a product of two primes and `e` is relatively prime to the Euler totient `\phi(n)`. Examples ======== >>> from sympy.crypto.crypto import rsa_public_key >>> p, q, e = 3, 5, 7 >>> n, e = rsa_public_key(p, q, e) >>> n 15 >>> e 7 """ n = p*q phi = totient(n) if isprime(p) and isprime(q) and gcd(e, phi) == 1: return n, e return False def rsa_private_key(p, q, e): r""" The RSA *private key* is the pair `(n,d)`, where `n` is a product of two primes and `d` is the inverse of `e` (mod `\phi(n)`). Examples ======== >>> from sympy.crypto.crypto import rsa_private_key >>> p, q, e = 3, 5, 7 >>> rsa_private_key(p, q, e) (15, 7) """ n = p*q phi = totient(n) if isprime(p) and isprime(q) and gcd(e, phi) == 1: return n, pow(e, phi - 1, phi) return False def encipher_rsa(pt, puk): """ In RSA, a message `m` is encrypted by computing `m^e` (mod `n`), where ``puk`` is the public key `(n,e)`. Examples ======== >>> from sympy.crypto.crypto import encipher_rsa, rsa_public_key >>> p, q, e = 3, 5, 7 >>> puk = rsa_public_key(p, q, e) >>> pt = 12 >>> encipher_rsa(pt, puk) 3 """ n, e = puk return pow(pt, e, n) def decipher_rsa(ct, prk): """ In RSA, a ciphertext `c` is decrypted by computing `c^d` (mod `n`), where ``prk`` is the private key `(n, d)`. Examples ======== >>> from sympy.crypto.crypto import decipher_rsa, rsa_private_key >>> p, q, e = 3, 5, 7 >>> prk = rsa_private_key(p, q, e) >>> ct = 3 >>> decipher_rsa(ct, prk) 12 """ n, d = prk return pow(ct, d, n) #################### kid krypto (kid RSA) ############################# def kid_rsa_public_key(a, b, A, B): r""" Kid RSA is a version of RSA useful to teach grade school children since it does not involve exponentiation. Alice wants to talk to Bob. Bob generates keys as follows. Key generation: * Select positive integers `a, b, A, B` at random. * Compute `M = a b - 1`, `e = A M + a`, `d = B M + b`, `n = (e d - 1) /M`. * The *public key* is `(n, e)`. Bob sends these to Alice. * The *private key* is `d`, which Bob keeps secret. Encryption: If `m` is the plaintext message then the ciphertext is `c = m e \pmod n`. Decryption: If `c` is the ciphertext message then the plaintext is `m = c d \pmod n`. Examples ======== >>> from sympy.crypto.crypto import kid_rsa_public_key >>> a, b, A, B = 3, 4, 5, 6 >>> kid_rsa_public_key(a, b, A, B) (369, 58) """ M = S(a*b - 1) e = S(A*M + a) d = S(B*M + b) n = S((e*d - 1)//M) return n, e def kid_rsa_private_key(a, b, A, B): """ Compute `M = a b - 1`, `e = A M + a`, `d = B M + b`, `n = (e d - 1) / M`. The *private key* is `d`, which Bob keeps secret. Examples ======== >>> from sympy.crypto.crypto import kid_rsa_private_key >>> a, b, A, B = 3, 4, 5, 6 >>> kid_rsa_private_key(a, b, A, B) (369, 70) """ M = S(a*b - 1) e = S(A*M + a) d = S(B*M + b) n = S((e*d - 1)//M) return n, d def encipher_kid_rsa(pt, puk): """ Here ``pt`` is the plaintext and ``puk`` is the public key. Examples ======== >>> from sympy.crypto.crypto import encipher_kid_rsa, kid_rsa_public_key >>> pt = 200 >>> a, b, A, B = 3, 4, 5, 6 >>> pk = kid_rsa_public_key(a, b, A, B) >>> encipher_kid_rsa(pt, pk) 161 """ return (pt*puk[1]) % puk[0] def decipher_kid_rsa(ct, prk): """ Here ``pt`` is the plaintext and ``prk`` is the private key. Examples ======== >>> from sympy.crypto.crypto import kid_rsa_public_key, kid_rsa_private_key, decipher_kid_rsa, encipher_kid_rsa >>> a, b, A, B = 3, 4, 5, 6 >>> d = kid_rsa_private_key(a, b, A, B) >>> pt = 200 >>> pk = kid_rsa_public_key(a, b, A, B) >>> prk = kid_rsa_private_key(a, b, A, B) >>> ct = encipher_kid_rsa(pt, pk) >>> decipher_kid_rsa(ct, prk) 200 """ n = prk[0] d = prk[1] return (ct*d) % n #################### Morse Code ###################################### def encode_morse(pt): """ Encodes a plaintext into popular Morse Code with letters separated by "|" and words by "||". References ========== .. [1] http://en.wikipedia.org/wiki/Morse_code Examples ======== >>> from sympy.crypto.crypto import encode_morse >>> pt = 'ATTACK THE RIGHT FLANK' >>> encode_morse(pt) '.-|-|-|.-|-.-.|-.-||-|....|.||.-.|..|--.|....|-||..-.|.-..|.-|-.|-.-' """ morse_encoding_map = {"A": ".-", "B": "-...", "C": "-.-.", "D": "-..", "E": ".", "F": "..-.", "G": "--.", "H": "....", "I": "..", "J": ".---", "K": "-.-", "L": ".-..", "M": "--", "N": "-.", "O": "---", "P": ".--.", "Q": "--.-", "R": ".-.", "S": "...", "T": "-", "U": "..-", "V": "...-", "W": ".--", "X": "-..-", "Y": "-.--", "Z": "--..", "0": "-----", "1": ".----", "2": "..---", "3": "...--", "4": "....-", "5": ".....", "6": "-....", "7": "--...", "8": "---..", "9": "----.", ".": ".-.-.-", ",": "--..--", ":": "---...", ";": "-.-.-.", "?": "..--..", "-": "-...-", "_": "..--.-", "(": "-.--.", ")": "-.--.-", "'": ".----.", "=": "-...-", "+": ".-.-.", "/": "-..-.", "@": ".--.-.", "$": "...-..-", "!": "-.-.--" } unusable_chars = "\"#%&*<>[\]^`{|}~" morsestring = [] for i in unusable_chars: pt = pt.replace(i, "") pt = pt.upper() words = pt.split(" ") for word in words: letters = list(word) morseword = [] for letter in letters: morseletter = morse_encoding_map[letter] morseword.append(morseletter) word = "|".join(morseword) morsestring.append(word) return "||".join(morsestring) def decode_morse(mc): """ Decodes a Morse Code with letters separated by "|" and words by "||" into plaintext. References ========== .. [1] http://en.wikipedia.org/wiki/Morse_code Examples ======== >>> from sympy.crypto.crypto import decode_morse >>> mc = '--|---|...-|.||.|.-|...|-' >>> decode_morse(mc) 'MOVE EAST' """ morse_decoding_map = {".-": "A", "-...": "B", "-.-.": "C", "-..": "D", ".": "E", "..-.": "F", "--.": "G", "....": "H", "..": "I", ".---": "J", "-.-": "K", ".-..": "L", "--": "M", "-.": "N", "---": "O", ".--.": "P", "--.-": "Q", ".-.": "R", "...": "S", "-": "T", "..-": "U", "...-": "V", ".--": "W", "-..-": "X", "-.--": "Y", "--..": "Z", "-----": "0", "----": "1", "..---": "2", "...--": "3", "....-": "4", ".....": "5", "-....": "6", "--...": "7", "---..": "8", "----.": "9", ".-.-.-": ".", "--..--": ",", "---...": ":", "-.-.-.": ";", "..--..": "?", "-...-": "-", "..--.-": "_", "-.--.": "(", "-.--.-": ")", ".----.": "'", "-...-": "=", ".-.-.": "+", "-..-.": "/", ".--.-.": "@", "...-..-": "$", "-.-.--": "!"} characterstring = [] if mc[-1] == "|" and mc[-2] == "|": mc = mc[:-2] words = mc.split("||") for word in words: letters = word.split("|") characterword = [] for letter in letters: try: characterletter = morse_decoding_map[letter] except KeyError: return "Invalid Morse Code" characterword.append(characterletter) word = "".join(characterword) characterstring.append(word) return " ".join(characterstring) #################### LFSRs ########################################## def lfsr_sequence(key, fill, n): r""" This function creates an lfsr sequence. INPUT: ``key``: a list of finite field elements, `[c_0, c_1, \ldots, c_k].` ``fill``: the list of the initial terms of the lfsr sequence, `[x_0, x_1, \ldots, x_k].` ``n``: number of terms of the sequence that the function returns. OUTPUT: The lfsr sequence defined by `x_{n+1} = c_k x_n + \ldots + c_0 x_{n-k}`, for `n \leq k`. Notes ===== S. Golomb [G]_ gives a list of three statistical properties a sequence of numbers `a = \{a_n\}_{n=1}^\infty`, `a_n \in \{0,1\}`, should display to be considered "random". Define the autocorrelation of `a` to be .. math:: C(k) = C(k,a) = \lim_{N\rightarrow \infty} {1\over N}\sum_{n=1}^N (-1)^{a_n + a_{n+k}}. In the case where `a` is periodic with period `P` then this reduces to .. math:: C(k) = {1\over P}\sum_{n=1}^P (-1)^{a_n + a_{n+k}}. Assume `a` is periodic with period `P`. - balance: .. math:: \left|\sum_{n=1}^P(-1)^{a_n}\right| \leq 1. - low autocorrelation: .. math:: C(k) = \left\{ \begin{array}{cc} 1,& k = 0,\\ \epsilon, & k \ne 0. \end{array} \right. (For sequences satisfying these first two properties, it is known that `\epsilon = -1/P` must hold.) - proportional runs property: In each period, half the runs have length `1`, one-fourth have length `2`, etc. Moreover, there are as many runs of `1`'s as there are of `0`'s. References ========== .. [G] Solomon Golomb, Shift register sequences, Aegean Park Press, Laguna Hills, Ca, 1967 Examples ======== >>> from sympy.crypto.crypto import lfsr_sequence >>> from sympy.polys.domains import FF >>> F = FF(2) >>> fill = [F(1), F(1), F(0), F(1)] >>> key = [F(1), F(0), F(0), F(1)] >>> lfsr_sequence(key, fill, 10) [1 mod 2, 1 mod 2, 0 mod 2, 1 mod 2, 0 mod 2, 1 mod 2, 1 mod 2, 0 mod 2, 0 mod 2, 1 mod 2] """ if not isinstance(key, list): raise TypeError("key must be a list") if not isinstance(fill, list): raise TypeError("fill must be a list") p = key[0].mod F = FF(p) s = fill k = len(fill) L = [] for i in range(n): s0 = s[:] L.append(s[0]) s = s[1:k] x = sum([int(key[i]*s0[i]) for i in range(k)]) s.append(F(x)) return L # use [x.to_int() for x in L] for int version def lfsr_autocorrelation(L, P, k): """ This function computes the lsfr autocorrelation function. INPUT: ``L``: is a periodic sequence of elements of `GF(2)`. ``L`` must have length larger than ``P``. ``P``: the period of ``L`` ``k``: an integer (`0 < k < p`) OUTPUT: the ``k``-th value of the autocorrelation of the LFSR ``L`` Examples ======== >>> from sympy.crypto.crypto import lfsr_sequence, lfsr_autocorrelation >>> from sympy.polys.domains import FF >>> F = FF(2) >>> fill = [F(1), F(1), F(0), F(1)] >>> key = [F(1), F(0), F(0), F(1)] >>> s = lfsr_sequence(key, fill, 20) >>> lfsr_autocorrelation(s, 15, 7) -1/15 >>> lfsr_autocorrelation(s, 15, 0) 1 """ if not isinstance(L, list): raise TypeError("L (=%s) must be a list" % L) P = int(P) k = int(k) L0 = L[:P] # slices makes a copy L1 = L0 + L0[:k] L2 = [(-1)**(L1[i].to_int() + L1[i + k].to_int()) for i in range(P)] tot = sum(L2) return Rational(tot, P) def lfsr_connection_polynomial(s): """ This function computes the lsfr connection polynomial. INPUT: ``s``: a sequence of elements of even length, with entries in a finite field OUTPUT: ``C(x)``: the connection polynomial of a minimal LFSR yielding ``s``. This implements the algorithm in section 3 of J. L. Massey's article [M]_. References ========== .. [M] James L. Massey, "Shift-Register Synthesis and BCH Decoding." IEEE Trans. on Information Theory, vol. 15(1), pp. 122-127, Jan 1969. Examples ======== >>> from sympy.crypto.crypto import lfsr_sequence, lfsr_connection_polynomial >>> from sympy.polys.domains import FF >>> F = FF(2) >>> fill = [F(1), F(1), F(0), F(1)] >>> key = [F(1), F(0), F(0), F(1)] >>> s = lfsr_sequence(key, fill, 20) >>> lfsr_connection_polynomial(s) x**4 + x + 1 >>> fill = [F(1), F(0), F(0), F(1)] >>> key = [F(1), F(1), F(0), F(1)] >>> s = lfsr_sequence(key, fill, 20) >>> lfsr_connection_polynomial(s) x**3 + 1 >>> fill = [F(1), F(0), F(1)] >>> key = [F(1), F(1), F(0)] >>> s = lfsr_sequence(key, fill, 20) >>> lfsr_connection_polynomial(s) x**3 + x**2 + 1 >>> fill = [F(1), F(0), F(1)] >>> key = [F(1), F(0), F(1)] >>> s = lfsr_sequence(key, fill, 20) >>> lfsr_connection_polynomial(s) x**3 + x + 1 """ # Initialization: p = s[0].mod F = FF(p) x = Symbol("x") C = 1*x**0 B = 1*x**0 m = 1 b = 1*x**0 L = 0 N = 0 while N < len(s): if L > 0: dC = Poly(C).degree() r = min(L + 1, dC + 1) coeffsC = [C.subs(x, 0)] + [C.coeff(x**i) for i in range(1, dC + 1)] d = (s[N].to_int() + sum([coeffsC[i]*s[N - i].to_int() for i in range(1, r)])) % p if L == 0: d = s[N].to_int()*x**0 if d == 0: m += 1 N += 1 if d > 0: if 2*L > N: C = (C - d*((b**(p - 2)) % p)*x**m*B).expand() m += 1 N += 1 else: T = C C = (C - d*((b**(p - 2)) % p)*x**m*B).expand() L = N + 1 - L m = 1 b = d B = T N += 1 dC = Poly(C).degree() coeffsC = [C.subs(x, 0)] + [C.coeff(x**i) for i in range(1, dC + 1)] return sum([coeffsC[i] % p*x**i for i in range(dC + 1) if coeffsC[i] is not None]) #################### ElGamal ############################# def elgamal_private_key(digit=10): """ Return three number tuple as private key. Elgamal encryption is based on mathmatical problem Discrete Logarithm Problem (DLP). For example, `a^{b} \equiv c \pmod p` In general, if a and b are known, c is easily calculated. If b is unknown, it is hard to use a and c to get b. Parameters ========== digit : Key length in binary Returns ======= (p, r, d) : p = prime number, r = primitive root, d = random number Examples ======== >>> from sympy.crypto.crypto import elgamal_private_key >>> from sympy.ntheory import is_primitive_root, isprime >>> a, b, _ = elgamal_private_key() >>> isprime(a) True >>> is_primitive_root(b, a) True """ p = nextprime(2**digit) return p, primitive_root(p), randrange(2, p) def elgamal_public_key(prk): """ Return three number tuple as public key. Parameters ========== prk : Tuple (p, r, e) generated by ``elgamal_private_key`` Returns ======= (p, r, e = r**d mod p) : d is a random number in private key. Examples ======== >>> from sympy.crypto.crypto import elgamal_public_key >>> elgamal_public_key((1031, 14, 636)) (1031, 14, 212) """ return prk[0], prk[1], pow(prk[1], prk[2], prk[0]) def encipher_elgamal(m, puk): """ Encrypt message with public key m is plain text message in int. puk is public key (p, r, e). In order to encrypt a message, random a number ``a`` between ``2`` and ``p``, encryped message is `c_{1}` and `c_{2}` `c_{1} \equiv r^{a} \pmod p` `c_{2} \equiv m e^{a} \pmod p` Parameters ========== m : int of encoded message puk : public key Returns ======= (c1, c2) : Encipher into two number Examples ======== >>> from sympy.crypto.crypto import encipher_elgamal >>> encipher_elgamal(100, (1031, 14, 212)) # doctest: +SKIP (835, 271) """ if m > puk[0]: ValueError('Message {} should be less than prime {}'.format(m, puk[0])) r = randrange(2, puk[0]) return pow(puk[1], r, puk[0]), m * pow(puk[2], r, puk[0]) % puk[0] def decipher_elgamal(ct, prk): r""" Decrypt message with private key `ct = (c_{1}, c_{2})` `prk = (p, r, d)` According to extended Eucliden theorem, `u c_{1}^{d} + p n = 1` `u \equiv 1/{{c_{1}}^d} \pmod p` `u c_{2} \equiv \frac{1}{c_{1}^d} c_{2} \equiv \frac{1}{r^{ad}} c_{2} \pmod p` `\frac{1}{r^{ad}} m e^a \equiv \frac{1}{r^{ad}} m {r^{d a}} \equiv m \pmod p` Examples ======== >>> from sympy.crypto.crypto import decipher_elgamal >>> decipher_elgamal((835, 271), (1031, 14, 636)) 100 """ u = igcdex(ct[0] ** prk[2], prk[0])[0] return u * ct[1] % prk[0] sympy-0.7.4.1/sympy/crypto/tests/0000755000175000017500000000000012253362407017107 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/crypto/tests/test_crypto.py0000644000175000017500000002052012253362407022037 0ustar georgeskgeorgeskfrom sympy.core import symbols from sympy.crypto.crypto import (alphabet_of_cipher, cycle_list, encipher_shift, encipher_affine, encipher_substitution, encipher_vigenere, decipher_vigenere, encipher_hill, decipher_hill, encipher_bifid5, encipher_bifid6, bifid5_square, bifid6_square, bifid7_square, encipher_bifid7, decipher_bifid5, decipher_bifid6, encipher_kid_rsa, decipher_kid_rsa, kid_rsa_private_key, kid_rsa_public_key, decipher_rsa, rsa_private_key, rsa_public_key, encipher_rsa, lfsr_connection_polynomial, lfsr_autocorrelation, lfsr_sequence, encode_morse, decode_morse, elgamal_private_key, elgamal_public_key, encipher_elgamal, decipher_elgamal) from sympy.matrices import Matrix from sympy.ntheory import isprime, is_primitive_root from sympy.polys.domains import FF from sympy.utilities.pytest import raises def test_alphabet_of_cipher(): assert alphabet_of_cipher()[0] == "A" assert alphabet_of_cipher(symbols="1z") == ["1", "z"] def test_cycle_list(): assert cycle_list(3, 4) == [3, 0, 1, 2] assert cycle_list(-1, 4) == [3, 0, 1, 2] assert cycle_list(1, 4) == [1, 2, 3, 0] def test_encipher_shift(): assert encipher_shift("ABC", 0) == "ABC" assert encipher_shift("ABC", 1) == "BCD" assert encipher_shift("ABC", -1) == "ZAB" def test_encipher_affine(): assert encipher_affine("ABC", (1, 0)) == "ABC" assert encipher_affine("ABC", (1, 1)) == "BCD" assert encipher_affine("ABC", (-1, 0)) == "AZY" assert encipher_affine("ABC", (-1, 1), symbols="ABCD") == "BAD" assert encipher_affine("123", (-1, 1), symbols="1234") == "214" def test_encipher_substitution(): assert encipher_substitution("ABC", "BAC", symbols="ABC") == "BAC" assert encipher_substitution("123", "124", symbols="1234") == "124" def test_encipher_vigenere(): assert encipher_vigenere("ABC", "ABC") == "ACE" assert encipher_vigenere("ABC", "ABC", symbols="ABCD") == "ACA" assert encipher_vigenere("ABC", "AB", symbols="ABCD") == "ACC" assert encipher_vigenere("AB", "ABC", symbols="ABCD") == "AC" assert encipher_vigenere("A", "ABC", symbols="ABCD") == "A" def test_decipher_vigenere(): assert decipher_vigenere("ABC", "ABC") == "AAA" assert decipher_vigenere("ABC", "ABC", symbols="ABCD") == "AAA" assert decipher_vigenere("ABC", "AB", symbols="ABCD") == "AAC" assert decipher_vigenere("AB", "ABC", symbols="ABCD") == "AA" assert decipher_vigenere("A", "ABC", symbols="ABCD") == "A" def test_encipher_hill(): A = Matrix(2, 2, [1, 2, 3, 5]) assert encipher_hill("ABCD", A) == "CFIV" A = Matrix(2, 2, [1, 0, 0, 1]) assert encipher_hill("ABCD", A) == "ABCD" assert encipher_hill("ABCD", A, symbols="ABCD") == "ABCD" A = Matrix(2, 2, [1, 2, 3, 5]) assert encipher_hill("ABCD", A, symbols="ABCD") == "CBAB" assert encipher_hill("AB", A, symbols="ABCD") == "CB" # n does not need to be a multiple of k assert encipher_hill("ABA", A) == "CFAA" def test_decipher_hill(): A = Matrix(2, 2, [1, 2, 3, 5]) assert decipher_hill("CFIV", A) == "ABCD" A = Matrix(2, 2, [1, 0, 0, 1]) assert decipher_hill("ABCD", A) == "ABCD" assert decipher_hill("ABCD", A, symbols="ABCD") == "ABCD" A = Matrix(2, 2, [1, 2, 3, 5]) assert decipher_hill("CBAB", A, symbols="ABCD") == "ABCD" assert decipher_hill("CB", A, symbols="ABCD") == "AB" # n does not need to be a multiple of k assert decipher_hill("CFA", A) == "ABAA" def test_encipher_bifid5(): assert encipher_bifid5("AB", "AB") == "AB" assert encipher_bifid5("AB", "CD") == "CO" assert encipher_bifid5("ab", "c") == "CH" assert encipher_bifid5("a bc", "b") == "BAC" def test_bifid5_square(): A = alphabet_of_cipher() A.remove("J") f = lambda i, j: symbols(A[5*i + j]) M = Matrix(5, 5, f) assert bifid5_square("") == M def test_decipher_bifid5(): assert decipher_bifid5("AB", "AB") == "AB" assert decipher_bifid5("CO", "CD") == "AB" assert decipher_bifid5("ch", "c") == "AB" assert decipher_bifid5("b ac", "b") == "ABC" def test_bifid7_square(): A = alphabet_of_cipher() + [str(a) for a in range(23)] f = lambda i, j: symbols(A[7*i + j]) M = Matrix(7, 7, f) assert bifid7_square("") == M def test_encipher_bifid7(): assert encipher_bifid7("AB", "AB") == "AB" assert encipher_bifid7("AB", "CD") == "CR" assert encipher_bifid7("ab", "c") == "CJ" assert encipher_bifid7("a bc", "b") == "BAC" def test_encipher_bifid6(): assert encipher_bifid6("AB", "AB") == "AB" assert encipher_bifid6("AB", "CD") == "CP" assert encipher_bifid6("ab", "c") == "CI" assert encipher_bifid6("a bc", "b") == "BAC" def test_decipher_bifid6(): assert decipher_bifid6("AB", "AB") == "AB" assert decipher_bifid6("CP", "CD") == "AB" assert decipher_bifid6("ci", "c") == "AB" assert decipher_bifid6("b ac", "b") == "ABC" def test_bifid6_square(): A = alphabet_of_cipher() + [str(a) for a in range(10)] f = lambda i, j: symbols(A[6*i + j]) M = Matrix(6, 6, f) assert bifid6_square("") == M def test_rsa_public_key(): assert rsa_public_key(2, 2, 1) == (4, 1) assert rsa_public_key(2, 3, 1) == (6, 1) assert rsa_public_key(5, 3, 3) == (15, 3) assert rsa_public_key(8, 8, 8) is False def test_rsa_private_key(): assert rsa_private_key(2, 2, 1) == (4, 1) assert rsa_private_key(2, 3, 1) == (6, 1) assert rsa_private_key(5, 3, 3) == (15, 3) assert rsa_private_key(8, 8, 8) is False def test_encipher_rsa(): puk = rsa_public_key(2, 2, 1) assert encipher_rsa(2, puk) == 2 puk = rsa_public_key(2, 3, 1) assert encipher_rsa(2, puk) == 2 puk = rsa_public_key(5, 3, 3) assert encipher_rsa(2, puk) == 8 def test_decipher_rsa(): prk = rsa_private_key(2, 2, 1) assert decipher_rsa(2, prk) == 2 prk = rsa_private_key(2, 3, 1) assert decipher_rsa(2, prk) == 2 prk = rsa_private_key(5, 3, 3) assert decipher_rsa(8, prk) == 2 def test_kid_rsa_public_key(): assert kid_rsa_public_key(1, 2, 1, 1) == (5, 2) assert kid_rsa_public_key(1, 2, 2, 1) == (8, 3) assert kid_rsa_public_key(1, 2, 1, 2) == (7, 2) def test_kid_rsa_private_key(): assert kid_rsa_private_key(1, 2, 1, 1) == (5, 3) assert kid_rsa_private_key(1, 2, 2, 1) == (8, 3) assert kid_rsa_private_key(1, 2, 1, 2) == (7, 4) def test_encipher_kid_rsa(): assert encipher_kid_rsa(1, (5, 2)) == 2 assert encipher_kid_rsa(1, (8, 3)) == 3 assert encipher_kid_rsa(1, (7, 2)) == 2 def test_decipher_kid_rsa(): assert decipher_kid_rsa(2, (5, 3)) == 1 assert decipher_kid_rsa(3, (8, 3)) == 1 assert decipher_kid_rsa(2, (7, 4)) == 1 def test_encode_morse(): assert encode_morse('ABC') == '.-|-...|-.-.' assert encode_morse('SMS ') == '...|--|...||' assert encode_morse('!@#$%^&*()_+') == '-.-.--|.--.-.|...-..-|-.--.|-.--.-|..--.-|.-.-.' def test_decode_morse(): assert decode_morse('-.-|.|-.--') == 'KEY' assert decode_morse('.-.|..-|-.||') == 'RUN' assert decode_morse('.....----') == 'Invalid Morse Code' def test_lfsr_sequence(): raises(TypeError, lambda: lfsr_sequence(1, [1], 1)) raises(TypeError, lambda: lfsr_sequence([1], 1, 1)) F = FF(2) assert lfsr_sequence([F(1)], [F(1)], 2) == [F(1), F(1)] assert lfsr_sequence([F(0)], [F(1)], 2) == [F(1), F(0)] F = FF(3) assert lfsr_sequence([F(1)], [F(1)], 2) == [F(1), F(1)] assert lfsr_sequence([F(0)], [F(2)], 2) == [F(2), F(0)] assert lfsr_sequence([F(1)], [F(2)], 2) == [F(2), F(2)] def test_lfsr_autocorrelation(): raises(TypeError, lambda: lfsr_autocorrelation(1, 2, 3)) F = FF(2) s = lfsr_sequence([F(1), F(0)], [F(0), F(1)], 5) assert lfsr_autocorrelation(s, 2, 0) == 1 assert lfsr_autocorrelation(s, 2, 1) == -1 def test_lfsr_connection_polynomial(): F = FF(2) x = symbols("x") s = lfsr_sequence([F(1), F(0)], [F(0), F(1)], 5) assert lfsr_connection_polynomial(s) == x**2 + 1 s = lfsr_sequence([F(1), F(1)], [F(0), F(1)], 5) assert lfsr_connection_polynomial(s) == x**2 + x + 1 def test_elgamal_private_key(): a, b, _ = elgamal_private_key(digit=100) assert isprime(a) assert is_primitive_root(b, a) assert len(bin(a)) >= 102 def test_elgamal(): dk = elgamal_private_key(20) ek = elgamal_public_key(dk) m = 12345 assert m == decipher_elgamal(encipher_elgamal(m, ek), dk) sympy-0.7.4.1/sympy/crypto/tests/__init__.py0000644000175000017500000000000012253362407021206 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/crypto/__init__.py0000644000175000017500000000125712253362407020063 0ustar georgeskgeorgeskfrom sympy.crypto.crypto import (alphabet_of_cipher, cycle_list, encipher_shift, encipher_affine, encipher_substitution, encipher_vigenere, decipher_vigenere, bifid5_square, bifid6_square, bifid7_square, encipher_hill, decipher_hill, encipher_bifid5, encipher_bifid6, encipher_bifid7, decipher_bifid5, decipher_bifid6, encipher_kid_rsa, decipher_kid_rsa, kid_rsa_private_key, kid_rsa_public_key, decipher_rsa, rsa_private_key, rsa_public_key, encipher_rsa, lfsr_connection_polynomial, lfsr_autocorrelation, lfsr_sequence, encode_morse, decode_morse, elgamal_private_key, elgamal_public_key, decipher_elgamal, encipher_elgamal) sympy-0.7.4.1/sympy/ntheory/0000755000175000017500000000000012253362407016115 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/ntheory/partitions_.py0000644000175000017500000000606412253362407021030 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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 from sympy.core.compatibility import xrange 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 xrange(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 xrange(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) 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 xrange(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 return int(to_int(mpf_add(s, fhalf, prec))) __all__ = ['npartitions'] sympy-0.7.4.1/sympy/ntheory/bbp_pi.py0000644000175000017500000000726712253362407017736 0ustar georgeskgeorgesk''' This implementation is a heavily modified fixed point implementation of BBP_formula for calculating the nth position of pi. The original hosted at: http://en.literateprograms.org/Pi_with_the_BBP_formula_(Python) # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sub-license, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Modifications: 1.Once the nth digit is selected the number of digits of working precision is calculated to ensure that the 14 Hexadecimal representation of that region is accurate. This was found empirically to be int((math.log10(n//1000))+18). This was found by searching for a value of working precision for the n = 0 and n = 1 then n was increased until the result was less precise, therefore increased again this was repeated for increasing n and an effective fit was found between n and the working precision value. 2. The while loop to evaluate whether the series has converged has be replaced with a fixed for loop, that option was selected because in a very large number of cases the loop converged to a point where no difference can be detected in less than 15 iterations. (done for more accurate memory and time banking). 3. output hex string constrained to 14 characters (accuracy assured to be n = 10**7) 4. pi_hex_digits(n) changed to have coefficient to the formula in an array (perhaps just a matter of preference). ''' from __future__ import print_function, division import math def _series(j, n): # Left sum from the bbp algorithm s = 0 D = _dn(n) for k in range(n + 1): r = 8*k + j s += (pow(16, n - k, r) << 4 * D) // r # Right sum. should iterate to infinty, but now just iterates to the point where # one iterations change is beyond the resolution of the data type used t = 0 for k in range(n + 1, n + 15): xp = int(16**(n - k) * 16**D) t += xp // (8 * k + j) total = s + t return total def pi_hex_digits(n): """Returns a string containing 14 digits after the nth value of pi in hex The decimal has been taken out of the number, so n = 0[0] = 3 # First digit of pi in hex, 3 Examples ======== >>> from sympy.ntheory.bbp_pi import pi_hex_digits >>> pi_hex_digits(0) '3243f6a8885a30' >>> pi_hex_digits(10) '5a308d313198a2' """ # main of implementation arrays holding formulae coefficients n -= 1 a = [4, 2, 1, 1] j = [1, 4, 5, 6] #formulae x = + (a[0]*_series(j[0], n) - a[1]*_series(j[1], n) - a[2]*_series(j[2], n) - a[3]*_series(j[3], n)) & (16**(_dn(n)) - 1) s = ("%014x" % x) #s is constrained between 0 and 14 return s[:14] def _dn(n): # controller for n dependence on precision if (n < 1000): return 16 return int(math.log10(n//1000) + 18) sympy-0.7.4.1/sympy/ntheory/factor_.py0000644000175000017500000012167712253362407020122 0ustar georgeskgeorgesk""" Integer factorization """ from __future__ import print_function, division import random import math from .primetest import isprime from .generate import sieve, primerange, nextprime from sympy.core import sympify from sympy.core.evalf import bitcount from sympy.core.numbers import igcd, oo, Rational from sympy.core.power import integer_nthroot, Pow from sympy.core.mul import Mul from sympy.core.compatibility import as_int, SYMPY_INTS, xrange from sympy.core.singleton import S from sympy.core.function import Function small_trailing = [i and max(int(not i % 2**j) and j for j in range(1, 8)) for i in range(256)] 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) 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 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) 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 isinstance(z, SYMPY_INTS): 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 def multiplicity(p, n): """ Find the greatest integer m such that p**m divides n. Examples ======== >>> from sympy.ntheory import multiplicity >>> from sympy.core.numbers import Rational as R >>> [multiplicity(5, n) for n in [8, 5, 25, 125, 250]] [0, 1, 2, 3, 3] >>> multiplicity(3, R(1, 9)) -2 """ try: p, n = as_int(p), as_int(n) except ValueError: if all(isinstance(i, (SYMPY_INTS, Rational)) for i in (p, n)): try: p = Rational(p) n = Rational(n) if p.q == 1: if n.p == 1: return -multiplicity(p.p, n.q) return S.Zero elif p.p == 1: return multiplicity(p.q, n.q) else: like = min( multiplicity(p.p, n.p), multiplicity(p.q, n.q)) cross = min( multiplicity(p.q, n.p), multiplicity(p.p, n.q)) return like - cross except AttributeError: pass raise ValueError('expecting ints or fractions, got %s and %s' % (p, 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 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 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) ... 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 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 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) 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 >>> from sympy.core.compatibility import long >>> a = 1407633717262338957430697921446883 >>> f = factorint(a, limit=10000) >>> f == {991: 1, long(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, 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('%s %s' % (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 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 = as_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 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) 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 xrange(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 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 = int(abs(n)) if isprime(n): return [1, n] if n == 1: return [1] if n == 0: return [] rv = _divisors(n) if not generator: return sorted(rv) return rv 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 factorint(n).items() if k > 1]) class totient(Function): """ Calculate the Euler totient function phi(n) >>> from sympy.ntheory import totient >>> totient(1) 1 >>> totient(25) 20 See Also ======== divisor_count """ @classmethod def eval(cls, n): n = sympify(n) if n.is_Integer: 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 sympy-0.7.4.1/sympy/ntheory/multinomial.py0000644000175000017500000001533412253362407021027 0ustar georgeskgeorgeskfrom __future__ import print_function, division from collections import defaultdict from sympy.core.compatibility import xrange def binomial_coefficients(n): """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 xrange(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): """ 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 xrange(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 {} 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] = r.items() for k in xrange(1, n*(m - 1) + 1): d = defaultdict(int) for i in xrange(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): 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 {} 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 xrange(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): """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]) sympy-0.7.4.1/sympy/ntheory/residue_ntheory.py0000644000175000017500000005210712253362407021704 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core.numbers import igcd, igcdex from sympy.core.compatibility import as_int, xrange from .primetest import isprime from .factor_ import factorint, trailing, totient from random import randint 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 """ from collections import defaultdict a, n = as_int(a), as_int(n) if igcd(a, n) != 1: raise ValueError("The two numbers should be relatively prime") factors = defaultdict(int) f = factorint(n) for px, kx in f.items(): if kx > 1: factors[px] += kx - 1 fpx = factorint(px - 1) for py, ky in fpx.items(): factors[py] += ky group_order = 1 for px, kx in factors.items(): group_order *= px**kx order = 1 if a > n: a = a % n for p, e in factors.items(): exponent = group_order for f in xrange(e + 1): if pow(a, exponent, n) != 1: order *= p ** (e - f + 1) break exponent = exponent // p return order def _primitive_root_prime_iter(p): """ Generates the primitive roots for a prime ``p`` References ========== [1] W. Stein "Elementary Number Theory" (2011), page 44 Examples ======== >>> from sympy.ntheory.residue_ntheory import _primitive_root_prime_iter >>> list(_primitive_root_prime_iter(19)) [2, 3, 10, 13, 14, 15] """ p = as_int(p) v = [(p - 1) // i for i in factorint(p - 1).keys()] a = 2 while a < p: for pw in v: if pow(a, pw, p) == 1: break else: yield a a += 1 def primitive_root(p): """ Returns the smallest primitive root or None References ========== [1] W. Stein "Elementary Number Theory" (2011), page 44 [2] P. Hackman "Elementary Number Theory" (2009), Chapter C Parameters ========== p : positive integer Examples ======== >>> from sympy.ntheory.residue_ntheory import primitive_root >>> primitive_root(19) 2 """ p = as_int(p) if p < 1: raise ValueError('p is required to be positive') if p <= 2: return 1 f = factorint(p) if len(f) > 2: return None if len(f) == 2: if 2 not in f or f[2] > 1: return None # case p = 2*p1**k, p1 prime for p1, e1 in f.items(): if p1 != 2: break i = 1 while i < p: i += 2 if i % p1 == 0: continue if is_primitive_root(i, p): return i else: if 2 in f: if p == 4: return 3 return None p1, n = list(f.items())[0] if n > 1: # see Ref [2], page 81 g = primitive_root(p1) if is_primitive_root(g, p1**2): return g else: for i in xrange(2, g + p1 + 1): if igcd(i, p) == 1 and is_primitive_root(i, p): return i return next(_primitive_root_prime_iter(p)) 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 return n_order(a, p) == totient(p) def _sqrt_mod_tonelli_shanks(a, p): """ Returns the square root in the case of ``p`` prime with ``p == 1 (mod 8)`` References ========== R. Crandall and C. Pomerance "Prime Numbers", 2nt Ed., page 101 """ s = trailing(p - 1) t = p >> s # find a non-quadratic residue while 1: d = randint(2, p - 1) r = legendre_symbol(d, p) if r == -1: break #assert legendre_symbol(d, p) == -1 A = pow(a, t, p) D = pow(d, t, p) m = 0 for i in xrange(s): adm = A*pow(D, m, p) % p adm = pow(adm, 2**(s - 1 - i), p) if adm % p == p - 1: m += 2**i #assert A*pow(D, m, p) % p == 1 x = pow(a, (t + 1)//2, p)*pow(D, m//2, p) % p return x def sqrt_mod(a, p, all_roots=False): """ find a root of ``x**2 = a mod p`` Parameters ========== a : integer p : positive integer all_roots : if True the list of roots is returned or None Notes ===== If there is no root it is returned None; else the returned root is less or equal to ``p // 2``; in general is not the smallest one. It is returned ``p // 2`` only if it is the only root. Use ``all_roots`` only when it is expected that all the roots fit in memory; otherwise use ``sqrt_mod_iter``. Examples ======== >>> from sympy.ntheory import sqrt_mod >>> sqrt_mod(11, 43) 21 >>> sqrt_mod(17, 32, True) [7, 9, 23, 25] """ if all_roots: return sorted(list(sqrt_mod_iter(a, p))) try: p = abs(as_int(p)) it = sqrt_mod_iter(a, p) r = next(it) if r > p // 2: return p - r elif r < p // 2: return r else: try: r = next(it) if r > p // 2: return p - r except StopIteration: pass return r except StopIteration: return None def _product(*iters): """ cartesian product generator Notes ===== Unlike itertools.product, it works also with iterables which do not fit in memory. See http://bugs.python.org/issue10109 Author: Fernando Sumudu with small changes """ import itertools inf_iters = tuple(itertools.cycle(enumerate(it)) for it in iters) num_iters = len(inf_iters) cur_val = [None]*num_iters first_v = True while True: i, p = 0, num_iters while p and not i: p -= 1 i, cur_val[p] = next(inf_iters[p]) if not p and not i: if first_v: first_v = False else: break yield cur_val def sqrt_mod_iter(a, p, domain=int): """ iterate over solutions to ``x**2 = a mod p`` Parameters ========== a : integer p : positive integer domain : integer domain, ``int``, ``ZZ`` or ``Integer`` Examples ======== >>> from sympy.ntheory.residue_ntheory import sqrt_mod_iter >>> list(sqrt_mod_iter(11, 43)) [21, 22] """ from sympy.polys.galoistools import gf_crt, gf_crt1, gf_crt2 from sympy.polys.domains import ZZ a, p = as_int(a), abs(as_int(p)) if isprime(p): a = a % p if a == 0: res = _sqrt_mod1(a, p, 1) else: res = _sqrt_mod_prime_power(a, p, 1) if res: if domain is ZZ: for x in res: yield x else: for x in res: yield domain(x) else: f = factorint(p) v = [] pv = [] for px, ex in f.items(): if a % px == 0: rx = _sqrt_mod1(a, px, ex) if not rx: raise StopIteration else: rx = _sqrt_mod_prime_power(a, px, ex) if not rx: raise StopIteration v.append(rx) pv.append(px**ex) mm, e, s = gf_crt1(pv, ZZ) if domain is ZZ: for vx in _product(*v): r = gf_crt2(vx, pv, mm, e, s, ZZ) yield r else: for vx in _product(*v): r = gf_crt2(vx, pv, mm, e, s, ZZ) yield domain(r) def _sqrt_mod_prime_power(a, p, k): """ find the solutions to ``x**2 = a mod p**k`` when ``a % p != 0`` Parameters ========== a : integer p : prime number k : positive integer References ========== [1] P. Hackman "Elementary Number Theory" (2009), page 160 [2] http://www.numbertheory.org/php/squareroot.html [3] [Gathen99]_ Examples ======== >>> from sympy.ntheory.residue_ntheory import _sqrt_mod_prime_power >>> _sqrt_mod_prime_power(11, 43, 1) [21, 22] """ from sympy.core.numbers import igcdex from sympy.polys.domains import ZZ pk = p**k a = a % pk if k == 1: if p == 2: return [ZZ(a)] if not is_quad_residue(a, p): return None if p % 4 == 3: res = pow(a, (p + 1) // 4, p) elif p % 8 == 5: sign = pow(a, (p - 1) // 4, p) if sign == 1: res = pow(a, (p + 3) // 8, p) else: b = pow(4*a, (p - 5) // 8, p) x = (2*a*b) % p if pow(x, 2, p) == a: res = x else: res = _sqrt_mod_tonelli_shanks(a, p) # ``_sqrt_mod_tonelli_shanks(a, p)`` is not deterministic; # sort to get always the same result return sorted([ZZ(res), ZZ(p - res)]) if k > 1: f = factorint(a) # see Ref.[2] if p == 2: if a % 8 != 1: return None if k <= 3: s = set() for i in xrange(0, pk, 4): s.add(1 + i) s.add(-1 + i) return list(s) # according to Ref.[2] for k > 2 there are two solutions # (mod 2**k-1), that is four solutions (mod 2**k), which can be # obtained from the roots of x**2 = 0 (mod 8) rv = [ZZ(1), ZZ(3), ZZ(5), ZZ(7)] # hensel lift them to solutions of x**2 = 0 (mod 2**k) # if r**2 - a = 0 mod 2**nx but not mod 2**(nx+1) # then r + 2**(nx - 1) is a root mod 2**(nx+1) n = 3 res = [] for r in rv: nx = n while nx < k: r1 = (r**2 - a) >> nx if r1 % 2: r = r + (1 << (nx - 1)) #assert (r**2 - a)% (1 << (nx + 1)) == 0 nx += 1 if r not in res: res.append(r) x = r + (1 << (k - 1)) #assert (x**2 - a) % pk == 0 if x < (1 << nx) and x not in res: if (x**2 - a) % pk == 0: res.append(x) return res rv = _sqrt_mod_prime_power(a, p, 1) if not rv: return None r = rv[0] fr = r**2 - a # hensel lifting with Newton iteration, see Ref.[3] chapter 9 # with f(x) = x**2 - a; one has f'(a) != 0 (mod p) for p != 2 n = 1 px = p while 1: n1 = n n1 *= 2 if n1 > k: break n = n1 px = px**2 frinv = igcdex(2*r, px)[0] r = (r - fr*frinv) % px fr = r**2 - a if n < k: px = p**k frinv = igcdex(2*r, px)[0] r = (r - fr*frinv) % px return [r, px - r] def _sqrt_mod1(a, p, n): """ find solution to ``x**2 == a mod p**n`` when ``a % p == 0`` see http://www.numbertheory.org/php/squareroot.html """ pn = p**n a = a % pn if a == 0: # case gcd(a, p**k) = p**n m = n // 2 if n % 2 == 1: pm1 = p**(m + 1) def _iter0a(): i = 0 while i < pn: yield i i += pm1 return _iter0a() else: pm = p**m def _iter0b(): i = 0 while i < pn: yield i i += pm return _iter0b() # case gcd(a, p**k) = p**r, r < n f = factorint(a) r = f[p] if r % 2 == 1: return None m = r // 2 a1 = a >> r if p == 2: if n - r == 1: pnm1 = 1 << (n - m + 1) pm1 = 1 << (m + 1) def _iter1(): k = 1 << (m + 2) i = 1 << m while i < pnm1: j = i while j < pn: yield j j += k i += pm1 return _iter1() if n - r == 2: res = _sqrt_mod_prime_power(a1, p, n - r) if res is None: return None pnm = 1 << (n - m) def _iter2(): s = set() for r in res: i = 0 while i < pn: x = (r << m) + i if x not in s: s.add(x) yield x i += pnm return _iter2() if n - r > 2: res = _sqrt_mod_prime_power(a1, p, n - r) if res is None: return None pnm1 = 1 << (n - m - 1) def _iter3(): s = set() for r in res: i = 0 while i < pn: x = ((r << m) + i) % pn if x not in s: s.add(x) yield x i += pnm1 return _iter3() else: m = r // 2 a1 = a // p**r res1 = _sqrt_mod_prime_power(a1, p, n - r) if res1 is None: return None pm = p**m pnr = p**(n-r) pnm = p**(n-m) def _iter4(): s = set() pm = p**m for rx in res1: i = 0 while i < pnm: x = ((rx + i) % pn) if x not in s: s.add(x) yield x*pm i += pnr return _iter4() 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 r = sqrt_mod(a, p) if r is None: return False else: return True return pow(a, (p - 1) // 2, p) == 1 def is_nthpow_residue(a, n, m): """ Returns True if ``x**n == a (mod m)`` has solutions. References ========== P. Hackman "Elementary Number Theory" (2009), page 76 """ if n == 1: return True if n == 2: return is_quad_residue(a, m) f = totient(m) k = f // igcd(f, n) return pow(a, k, m) == 1 def _nthroot_mod2(s, q, p): f = factorint(q) v = [] for b, e in f.items(): v.extend([b]*e) for qx in v: s = _nthroot_mod1(s, qx, p, False) return s def _nthroot_mod1(s, q, p, all_roots): """ Root of ``x**q = s mod p``, ``p`` prime and ``q`` divides ``p - 1`` References ========== [1] A. M. Johnston "A Generalized qth Root Algorithm" """ g = primitive_root(p) if not isprime(q): r = _nthroot_mod2(s, q, p) else: f = p - 1 assert (p - 1) % q == 0 # determine k k = 0 while f % q == 0: k += 1 f = f // q # find z, x, r1 f1 = igcdex(-f, q)[0] % q z = f*f1 x = (1 + z) // q w = pow(g, z, p) r1 = pow(s, x, p) s1 = pow(s, f, p) y = pow(g, f, p) h = pow(g, f*q, p) # find t discrete log of s1 base h, h**x = s1 mod p # used a naive implementation # TODO implement using Ref [1] pr = 1 for t in xrange(p): if pr == s1: break pr = pr*h % p g2 = pow(g, z*t, p) g3 = igcdex(g2, p)[0] r = r1*g3 % p #assert pow(r, q, p) == s res = [r] h = pow(g, (p - 1) // q, p) #assert pow(h, q, p) == 1 hx = r for i in range(q - 1): hx = (hx*h) % p res.append(hx) if all_roots: res.sort() return res return min(res) def nthroot_mod(a, n, p, all_roots=False): """ find the solutions to ``x**n = a mod p`` Parameters ========== a : integer n : positive integer p : positive integer all_roots : if False returns the smallest root, else the list of roots Examples ======== >>> from sympy.ntheory.residue_ntheory import nthroot_mod >>> nthroot_mod(11, 4, 19) 8 >>> nthroot_mod(11, 4, 19, True) [8, 11] >>> nthroot_mod(68, 3, 109) 23 """ from sympy.core.numbers import igcdex if n == 2: return sqrt_mod(a, p , all_roots) f = totient(p) # see Hackman "Elementary Number Theory" (2009), page 76 if pow(a, f // igcd(f, n), p) != 1: return None if not isprime(p): raise NotImplementedError if (p - 1) % n == 0: return _nthroot_mod1(a, n, p, all_roots) # The roots of ``x**n - a = 0 (mod p)`` are roots of # ``gcd(x**n - a, x**(p - 1) - 1) = 0 (mod p)`` pa = n pb = p - 1 b = 1 if pa < pb: a, pa, b, pb = b, pb, a, pa while pb: # x**pa - a = 0; x**pb - b = 0 # x**pa - a = x**(q*pb + r) - a = (x**pb)**q * x**r - a = # b**q * x**r - a; x**r - c = 0; c = b**-q * a mod p q, r = divmod(pa, pb) c = pow(b, q, p) c = igcdex(c, p)[0] c = (c * a) % p pa, pb = pb, r a, b = b, c if pa == 1: if all_roots: res = [a] else: res = a elif pa == 2: return sqrt_mod(a, p , all_roots) else: res = _nthroot_mod1(a, pa, p, all_roots) return res def quadratic_residues(p): """ Returns the list of quadratic residues. Examples ======== >>> from sympy.ntheory.residue_ntheory import quadratic_residues >>> quadratic_residues(7) [0, 1, 2, 4] """ r = set() for i in xrange(p // 2 + 1): r.add(pow(i, 2, p)) return sorted(list(r)) 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 = a % p if not a: return 0 if is_quad_residue(a, p): return 1 return -1 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 sympy-0.7.4.1/sympy/ntheory/modular.py0000644000175000017500000001677412253362407020151 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core.numbers import igcdex, igcd from sympy.core.mul import prod from sympy.core.compatibility import as_int, reduce from sympy.ntheory.primetest import isprime from sympy.polys.domains import ZZ from sympy.polys.galoistools import gf_crt, gf_crt1, gf_crt2 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 return a - m 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)), check=False, symmetric=symmetric) if result is None: return result result, mm = result if symmetric: return symmetric_residue(result, mm), mm return result, mm 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) 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 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(*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 sympy-0.7.4.1/sympy/ntheory/primetest.py0000644000175000017500000002656112253362407020515 0ustar georgeskgeorgesk""" Primality testing """ from __future__ import print_function, division from sympy.core.compatibility import xrange # 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, s, t): """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. """ # do the Fermat test b = pow(base, t, n) if b == 1 or b == n - 1: return True else: for j in xrange(1, s): b = pow(b, 2, n) if b == n - 1: return True # see I. Niven et al. "An Introduction to Theory of Numbers", page 78 if b == 1: return False return False 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 """ from sympy.ntheory.factor_ import trailing from sympy.polys.domains import ZZ n = int(n) if n < 2: return False # remove powers of 2 from n = t * 2**s + 1 s = trailing(n - 1) t = n >> s for base in bases: base = ZZ(base) if not _test(n, base, s, t): 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") 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 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: # prime list to use when number must be tested as a probable prime; # these are the 46 primes less than 200 bases = [ 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] return mr(n, bases) 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) sympy-0.7.4.1/sympy/ntheory/tests/0000755000175000017500000000000012253362407017257 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/ntheory/tests/test_ntheory.py0000644000175000017500000006514412253362407022372 0ustar georgeskgeorgeskfrom collections import defaultdict from sympy import Sieve, binomial_coefficients, binomial_coefficients_list, \ multinomial_coefficients, Mul, S, Pow, sieve, Symbol, summation, Dummy, \ factorial as fac, Rational from sympy.core.numbers import Integer, igcd from sympy.core.compatibility import long from sympy.ntheory import isprime, n_order, is_primitive_root, \ is_quad_residue, legendre_symbol, jacobi_symbol, npartitions, totient, \ factorint, primefactors, divisors, randprime, nextprime, prevprime, \ primerange, primepi, prime, pollard_rho, perfect_power, multiplicity, \ trailing, divisor_count, primorial, pollard_pm1, \ sqrt_mod, primitive_root, quadratic_residues, is_nthpow_residue, \ nthroot_mod, sqrt_mod_iter from sympy.ntheory.residue_ntheory import _primitive_root_prime_iter from sympy.ntheory.factor_ import smoothness, smoothness_p from sympy.ntheory.generate import cycle_length from sympy.ntheory.primetest import _mr_safe_helper, mr from sympy.ntheory.bbp_pi import pi_hex_digits from sympy.ntheory.modular import crt, crt1, crt2, solve_congruence from sympy.polys.domains import ZZ from sympy.utilities.pytest import raises from sympy.utilities.iterables import capture from sympy.ntheory.multinomial import multinomial_coefficients_iterator def test_trailing(): assert trailing(0) == 0 assert trailing(1) == 0 assert trailing(-1) == 0 assert trailing(2) == 1 assert trailing(7) == 0 assert trailing(-7) == 0 for i in range(100): assert trailing((1 << i)) == i assert trailing((1 << i) * 31337) == i assert trailing((1 << 1000001)) == 1000001 assert trailing((1 << 273956)*7**37) == 273956 def test_multiplicity(): for b in range(2, 20): for i in range(100): assert multiplicity(b, b**i) == i assert multiplicity(b, (b**i) * 23) == i assert multiplicity(b, (b**i) * 1000249) == i # Should be fast assert multiplicity(10, 10**10023) == 10023 # Should exit quickly assert multiplicity(10**10, 10**10) == 1 # Should raise errors for bad input raises(ValueError, lambda: multiplicity(1, 1)) raises(ValueError, lambda: multiplicity(1, 2)) raises(ValueError, lambda: multiplicity(1.3, 2)) # handles Rationals assert multiplicity(10, Rational(30, 7)) == 0 assert multiplicity(Rational(2, 7), Rational(4, 7)) == 1 assert multiplicity(Rational(1, 7), Rational(3, 49)) == 2 assert multiplicity(Rational(2, 7), Rational(7, 2)) == -1 assert multiplicity(3, Rational(1, 9)) == -2 def test_perfect_power(): assert perfect_power(0) is False assert perfect_power(1) is False assert perfect_power(2) is False assert perfect_power(3) is False assert perfect_power(4) == (2, 2) assert perfect_power(14) is False assert perfect_power(25) == (5, 2) assert perfect_power(22) is False assert perfect_power(22, [2]) is False assert perfect_power(137**(3*5*13)) == (137, 3*5*13) assert perfect_power(137**(3*5*13) + 1) is False assert perfect_power(137**(3*5*13) - 1) is False assert perfect_power(103005006004**7) == (103005006004, 7) assert perfect_power(103005006004**7 + 1) is False assert perfect_power(103005006004**7 - 1) is False assert perfect_power(103005006004**12) == (103005006004, 12) assert perfect_power(103005006004**12 + 1) is False assert perfect_power(103005006004**12 - 1) is False assert perfect_power(2**10007) == (2, 10007) assert perfect_power(2**10007 + 1) is False assert perfect_power(2**10007 - 1) is False assert perfect_power((9**99 + 1)**60) == (9**99 + 1, 60) assert perfect_power((9**99 + 1)**60 + 1) is False assert perfect_power((9**99 + 1)**60 - 1) is False assert perfect_power((10**40000)**2, big=False) == (10**40000, 2) assert perfect_power(10**100000) == (10, 100000) assert perfect_power(10**100001) == (10, 100001) assert perfect_power(13**4, [3, 5]) is False assert perfect_power(3**4, [3, 10], factor=0) is False assert perfect_power(3**3*5**3) == (15, 3) assert perfect_power(2**3*5**5) is False assert perfect_power(2*13**4) is False assert perfect_power(2**5*3**3) is False def test_isprime(): s = Sieve() s.extend(100000) ps = set(s.primerange(2, 100001)) for n in range(100001): # if (n in ps) != isprime(n): print n assert (n in ps) == isprime(n) assert isprime(179424673) # Some Mersenne primes assert isprime(2**61 - 1) assert isprime(2**89 - 1) assert isprime(2**607 - 1) assert not isprime(2**601 - 1) #Arnault's number assert isprime(int(''' 803837457453639491257079614341942108138837688287558145837488917522297\ 427376533365218650233616396004545791504202360320876656996676098728404\ 396540823292873879185086916685732826776177102938969773947016708230428\ 687109997439976544144845341155872450633409279022275296229414984230688\ 1685404326457534018329786111298960644845216191652872597534901''')) # pseudoprime that passes the base set [2, 3, 7, 61, 24251] assert not isprime(9188353522314541) assert _mr_safe_helper( "if n < 170584961: return mr(n, [350, 3958281543])") == \ ' # [350, 3958281543] stot = 1 clear [2, 3, 5, 7, 29, 67, 679067]' assert _mr_safe_helper( "if n < 3474749660383: return mr(n, [2, 3, 5, 7, 11, 13])") == \ ' # [2, 3, 5, 7, 11, 13] stot = 7 clear == bases' def test_prime(): assert prime(1) == 2 assert prime(2) == 3 assert prime(5) == 11 assert prime(11) == 31 assert prime(57) == 269 assert prime(296) == 1949 assert prime(559) == 4051 assert prime(3000) == 27449 assert prime(4096) == 38873 assert prime(9096) == 94321 assert prime(25023) == 287341 raises(ValueError, lambda: prime(0)) def test_primepi(): assert primepi(1) == 0 assert primepi(2) == 1 assert primepi(5) == 3 assert primepi(11) == 5 assert primepi(57) == 16 assert primepi(296) == 62 assert primepi(559) == 102 assert primepi(3000) == 430 assert primepi(4096) == 564 assert primepi(9096) == 1128 assert primepi(25023) == 2763 def test_generate(): assert nextprime(-4) == 2 assert nextprime(2) == 3 assert nextprime(5) == 7 assert nextprime(12) == 13 assert nextprime(90) == 97 assert nextprime(10**40) == (10**40 + 121) assert prevprime(3) == 2 assert prevprime(7) == 5 assert prevprime(13) == 11 assert prevprime(97) == 89 assert prevprime(10**40) == (10**40 - 17) assert list(primerange(2, 7)) == [2, 3, 5] assert list(primerange(2, 10)) == [2, 3, 5, 7] assert list(primerange(1050, 1100)) == [1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097] s = Sieve() for i in range(30, 2350, 376): for j in range(2, 5096, 1139): A = list(s.primerange(i, i + j)) B = list(primerange(i, i + j)) assert A == B s = Sieve() assert s[10] == 29 assert nextprime(2, 2) == 5 raises(ValueError, lambda: totient(0)) raises(ValueError, lambda: primorial(0)) assert mr(1, [2]) is False func = lambda i: (i**2 + 1) % 51 assert next(cycle_length(func, 4)) == (6, 2) assert list(cycle_length(func, 4, values=True)) == \ [17, 35, 2, 5, 26, 14, 44, 50, 2, 5, 26, 14] assert next(cycle_length(func, 4, nmax=5)) == (5, None) assert list(cycle_length(func, 4, nmax=5, values=True)) == \ [17, 35, 2, 5, 26] def test_randprime(): import random random.seed(1234) assert randprime(2, 3) == 2 assert randprime(1, 3) == 2 assert randprime(3, 5) == 3 raises(ValueError, lambda: randprime(20, 22)) for a in [100, 300, 500, 250000]: for b in [100, 300, 500, 250000]: p = randprime(a, a + b) assert a <= p < (a + b) and isprime(p) def fac_multiplicity(n, p): """Return the power of the prime number p in the factorization of n!""" if p > n: return 0 if p > n//2: return 1 q, m = n, 0 while q >= p: q //= p m += q return m def multiproduct(seq=(), start=1): """ Return the product of a sequence of factors with multiplicities, times the value of the parameter ``start``. The input may be a sequence of (factor, exponent) pairs or a dict of such pairs. >>> multiproduct({3:7, 2:5}, 4) # = 3**7 * 2**5 * 4 279936 """ if not seq: return start if isinstance(seq, dict): seq = iter(seq.items()) units = start multi = [] for base, exp in seq: if not exp: continue elif exp == 1: units *= base else: if exp % 2: units *= base multi.append((base, exp//2)) return units * multiproduct(multi)**2 def test_factorint(): assert primefactors(123456) == [2, 3, 643] assert factorint(0) == {0: 1} assert factorint(1) == {} assert factorint(-1) == {-1: 1} assert factorint(-2) == {-1: 1, 2: 1} assert factorint(-16) == {-1: 1, 2: 4} assert factorint(2) == {2: 1} assert factorint(126) == {2: 1, 3: 2, 7: 1} assert factorint(123456) == {2: 6, 3: 1, 643: 1} assert factorint(5951757) == {3: 1, 7: 1, 29: 2, 337: 1} assert factorint(64015937) == {7993: 1, 8009: 1} assert factorint(2**(2**6) + 1) == {274177: 1, 67280421310721: 1} assert multiproduct(factorint(fac(200))) == fac(200) for b, e in factorint(fac(150)).items(): assert e == fac_multiplicity(150, b) assert factorint(103005006059**7) == {103005006059: 7} assert factorint(31337**191) == {31337: 191} assert factorint(2**1000 * 3**500 * 257**127 * 383**60) == \ {2: 1000, 3: 500, 257: 127, 383: 60} assert len(factorint(fac(10000))) == 1229 assert factorint(12932983746293756928584532764589230) == \ {2: 1, 5: 1, 73: 1, 727719592270351: 1, 63564265087747: 1, 383: 1} assert factorint(727719592270351) == {727719592270351: 1} assert factorint(2**64 + 1, use_trial=False) == factorint(2**64 + 1) for n in range(60000): assert multiproduct(factorint(n)) == n assert pollard_rho(2**64 + 1, seed=1) == 274177 assert pollard_rho(19, seed=1) is None assert factorint(3, limit=2) == {3: 1} assert factorint(12345) == {3: 1, 5: 1, 823: 1} assert factorint( 12345, limit=3) == {4115: 1, 3: 1} # the 5 is greater than the limit assert factorint(1, limit=1) == {} assert factorint(12, limit=1) == {12: 1} assert factorint(30, limit=2) == {2: 1, 15: 1} assert factorint(16, limit=2) == {2: 4} assert factorint(124, limit=3) == {2: 2, 31: 1} assert factorint(4*31**2, limit=3) == {2: 2, 31: 2} p1 = nextprime(2**32) p2 = nextprime(2**16) p3 = nextprime(p2) assert factorint(p1*p2*p3) == {p1: 1, p2: 1, p3: 1} assert factorint(13*17*19, limit=15) == {13: 1, 17*19: 1} assert factorint(1951*15013*15053, limit=2000) == {225990689: 1, 1951: 1} assert factorint(primorial(17) + 1, use_pm1=0) == \ {long(19026377261): 1, 3467: 1, 277: 1, 105229: 1} # when prime b is closer than approx sqrt(8*p) to prime p then they are # "close" and have a trivial factorization a = nextprime(2**2**8) # 78 digits b = nextprime(a + 2**2**4) assert 'Fermat' in capture(lambda: factorint(a*b, verbose=1)) raises(ValueError, lambda: pollard_rho(4)) raises(ValueError, lambda: pollard_pm1(3)) raises(ValueError, lambda: pollard_pm1(10, B=2)) # verbose coverage n = nextprime(2**16)*nextprime(2**17)*nextprime(1901) assert 'with primes' in capture(lambda: factorint(n, verbose=1)) capture(lambda: factorint(nextprime(2**16)*1012, verbose=1)) n = nextprime(2**17) capture(lambda: factorint(n**3, verbose=1)) # perfect power termination capture(lambda: factorint(2*n, verbose=1)) # factoring complete msg # exceed 1st n = nextprime(2**17) n *= nextprime(n) assert '1000' in capture(lambda: factorint(n, limit=1000, verbose=1)) n *= nextprime(n) assert len(factorint(n)) == 3 assert len(factorint(n, limit=p1)) == 3 n *= nextprime(2*n) # exceed 2nd assert '2001' in capture(lambda: factorint(n, limit=2000, verbose=1)) assert capture( lambda: factorint(n, limit=4000, verbose=1)).count('Pollard') == 2 # non-prime pm1 result n = nextprime(8069) n *= nextprime(2*n)*nextprime(2*n, 2) capture(lambda: factorint(n, verbose=1)) # non-prime pm1 result # factor fermat composite p1 = nextprime(2**17) p2 = nextprime(2*p1) assert factorint((p1*p2**2)**3) == {p1: 3, p2: 6} # Test for non integer input raises(ValueError, lambda: factorint(4.5)) def test_divisors_and_divisor_count(): assert divisors(-1) == [1] assert divisors(0) == [] assert divisors(1) == [1] assert divisors(2) == [1, 2] assert divisors(3) == [1, 3] assert divisors(17) == [1, 17] assert divisors(10) == [1, 2, 5, 10] assert divisors(100) == [1, 2, 4, 5, 10, 20, 25, 50, 100] assert divisors(101) == [1, 101] assert divisor_count(0) == 0 assert divisor_count(-1) == 1 assert divisor_count(1) == 1 assert divisor_count(6) == 4 assert divisor_count(12) == 6 assert divisor_count(180, 3) == divisor_count(180//3) assert divisor_count(2*3*5, 7) == 0 def test_issue3882(): S = set(divisors(4)).union(set(divisors(Integer(2)))) assert S == set([1,2,4]) def test_totient(): assert [totient(k) for k in range(1, 12)] == \ [1, 1, 2, 2, 4, 2, 6, 4, 6, 4, 10] assert totient(5005) == 2880 assert totient(5006) == 2502 assert totient(5009) == 5008 assert totient(2**100) == 2**99 m = Symbol("m", integer=True) assert totient(m) assert totient(m).subs(m, 3**10) == 3**10 - 3**9 assert summation(totient(m), (m, 1, 11)) == 42 def test_partitions(): assert [npartitions(k) for k in range(13)] == \ [1, 1, 2, 3, 5, 7, 11, 15, 22, 30, 42, 56, 77] assert npartitions(100) == 190569292 assert npartitions(200) == 3972999029388 assert npartitions(1000) == 24061467864032622473692149727991 assert npartitions(2000) == 4720819175619413888601432406799959512200344166 assert npartitions(10000) % 10**10 == 6916435144 assert npartitions(100000) % 10**10 == 9421098519 def test_residue(): assert n_order(2, 13) == 12 assert [n_order(a, 7) for a in range(1, 7)] == \ [1, 3, 6, 3, 6, 2] assert n_order(5, 17) == 16 assert n_order(17, 11) == n_order(6, 11) assert n_order(101, 119) == 6 assert n_order(11, (10**50 + 151)**2) == 10000000000000000000000000000000000000000000000030100000000000000000000000000000000000000000000022650 raises(ValueError, lambda: n_order(6, 9)) assert is_primitive_root(2, 7) is False assert is_primitive_root(3, 8) is False assert is_primitive_root(11, 14) is False assert is_primitive_root(12, 17) == is_primitive_root(29, 17) raises(ValueError, lambda: is_primitive_root(3, 6)) assert [primitive_root(i) for i in range(2, 31)] == [1, 2, 3, 2, 5, 3, \ None, 2, 3, 2, None, 2, 3, None, None, 3, 5, 2, None, None, 7, 5, \ None, 2, 7, 2, None, 2, None] for p in primerange(3, 100): it = _primitive_root_prime_iter(p) assert len(list(it)) == totient(totient(p)) assert primitive_root(97) == 5 assert primitive_root(97**2) == 5 assert primitive_root(40487) == 5 # note that primitive_root(40487) + 40487 = 40492 is a primitive root # of 40487**2, but it is not the smallest assert primitive_root(40487**2) == 10 assert primitive_root(82) == 7 p = 10**50 + 151 assert primitive_root(p) == 11 assert primitive_root(2*p) == 11 assert primitive_root(p**2) == 11 raises(ValueError, lambda: primitive_root(-3)) assert is_quad_residue(3, 7) is False assert is_quad_residue(10, 13) is True assert is_quad_residue(12364, 139) == is_quad_residue(12364 % 139, 139) assert is_quad_residue(207, 251) is True assert is_quad_residue(0, 1) is True assert is_quad_residue(1, 1) is True assert is_quad_residue(0, 2) == is_quad_residue(1, 2) is True assert is_quad_residue(1, 4) is True assert is_quad_residue(2, 27) is False assert is_quad_residue(13122380800, 13604889600) is True assert [j for j in range(14) if is_quad_residue(j, 14)] == \ [0, 1, 2, 4, 7, 8, 9, 11] raises(ValueError, lambda: is_quad_residue(1.1, 2)) raises(ValueError, lambda: is_quad_residue(2, 0)) assert quadratic_residues(12) == [0, 1, 4, 9] assert quadratic_residues(13) == [0, 1, 3, 4, 9, 10, 12] assert [len(quadratic_residues(i)) for i in range(1, 20)] == \ [1, 2, 2, 2, 3, 4, 4, 3, 4, 6, 6, 4, 7, 8, 6, 4, 9, 8, 10] assert list(sqrt_mod_iter(6, 2)) == [0] assert sqrt_mod(3, 13) == 4 assert sqrt_mod(3, -13) == 4 assert sqrt_mod(6, 23) == 11 assert sqrt_mod(345, 690) == 345 for p in range(3, 100): d = defaultdict(list) for i in range(p): d[pow(i, 2, p)].append(i) for i in range(1, p): it = sqrt_mod_iter(i, p) v = sqrt_mod(i, p, True) if v: v = sorted(v) assert d[i] == v else: assert not d[i] assert sqrt_mod(9, 27, True) == [3, 6, 12, 15, 21, 24] assert sqrt_mod(9, 81, True) == [3, 24, 30, 51, 57, 78] assert sqrt_mod(9, 3**5, True) == [3, 78, 84, 159, 165, 240] assert sqrt_mod(81, 3**4, True) == [0, 9, 18, 27, 36, 45, 54, 63, 72] assert sqrt_mod(81, 3**5, True) == [9, 18, 36, 45, 63, 72, 90, 99, 117,\ 126, 144, 153, 171, 180, 198, 207, 225, 234] assert sqrt_mod(81, 3**6, True) == [9, 72, 90, 153, 171, 234, 252, 315,\ 333, 396, 414, 477, 495, 558, 576, 639, 657, 720] assert sqrt_mod(81, 3**7, True) == [9, 234, 252, 477, 495, 720, 738, 963,\ 981, 1206, 1224, 1449, 1467, 1692, 1710, 1935, 1953, 2178] for a, p in [(26214400, 32768000000), (26214400, 16384000000), (262144, 1048576), (87169610025, 163443018796875), (22315420166400, 167365651248000000)]: assert pow(sqrt_mod(a, p), 2, p) == a n = 70 a, p = 5**2*3**n*2**n, 5**6*3**(n+1)*2**(n+2) it = sqrt_mod_iter(a, p) for i in range(10): assert pow(next(it), 2, p) == a a, p = 5**2*3**n*2**n, 5**6*3**(n+1)*2**(n+3) it = sqrt_mod_iter(a, p) for i in range(2): assert pow(next(it), 2, p) == a n = 100 a, p = 5**2*3**n*2**n, 5**6*3**(n+1)*2**(n+1) it = sqrt_mod_iter(a, p) for i in range(2): assert pow(next(it), 2, p) == a assert type(next(sqrt_mod_iter(9, 27))) is int assert type(next(sqrt_mod_iter(9, 27, ZZ))) is type(ZZ(1)) assert type(next(sqrt_mod_iter(1, 7, ZZ))) is type(ZZ(1)) assert is_nthpow_residue(2, 1, 5) assert not is_nthpow_residue(2, 2, 5) assert is_nthpow_residue(8547, 12, 10007) assert nthroot_mod(1801, 11, 2663) == 44 for a, q, p in [(51922, 2, 203017), (43, 3, 109), (1801, 11, 2663), (26118163, 1303, 33333347), (1499, 7, 2663), (595, 6, 2663), (1714, 12, 2663), (28477, 9, 33343)]: r = nthroot_mod(a, q, p) assert pow(r, q, p) == a assert nthroot_mod(11, 3, 109) is None for p in primerange(5, 100): qv = range(3, p, 4) for q in qv: d = defaultdict(list) for i in range(p): d[pow(i, q, p)].append(i) for a in range(1, p - 1): res = nthroot_mod(a, q, p, True) if d[a]: assert d[a] == res else: assert res is None assert legendre_symbol(5, 11) == 1 assert legendre_symbol(25, 41) == 1 assert legendre_symbol(67, 101) == -1 assert legendre_symbol(0, 13) == 0 assert legendre_symbol(9, 3) == 0 raises(ValueError, lambda: legendre_symbol(2, 4)) assert jacobi_symbol(25, 41) == 1 assert jacobi_symbol(-23, 83) == -1 assert jacobi_symbol(3, 9) == 0 assert jacobi_symbol(42, 97) == -1 assert jacobi_symbol(3, 5) == -1 assert jacobi_symbol(7, 9) == 1 assert jacobi_symbol(0, 3) == 0 assert jacobi_symbol(0, 1) == 1 assert jacobi_symbol(2, 1) == 1 assert jacobi_symbol(1, 3) == 1 raises(ValueError, lambda: jacobi_symbol(3, 8)) def test_hex_pi_nth_digits(): assert pi_hex_digits(0) == '3243f6a8885a30' assert pi_hex_digits(1) == '243f6a8885a308' assert pi_hex_digits(10000) == '68ac8fcfb8016c' def test_crt(): def mcrt(m, v, r, symmetric=False): assert crt(m, v, symmetric)[0] == r mm, e, s = crt1(m) assert crt2(m, v, mm, e, s, symmetric) == (r, mm) mcrt([2, 3, 5], [0, 0, 0], 0) mcrt([2, 3, 5], [1, 1, 1], 1) mcrt([2, 3, 5], [-1, -1, -1], -1, True) mcrt([2, 3, 5], [-1, -1, -1], 2*3*5 - 1, False) assert crt([656, 350], [811, 133], symmetric=True) == (-56917, 114800) def test_binomial_coefficients_list(): assert binomial_coefficients_list(0) == [1] assert binomial_coefficients_list(1) == [1, 1] assert binomial_coefficients_list(2) == [1, 2, 1] assert binomial_coefficients_list(3) == [1, 3, 3, 1] assert binomial_coefficients_list(4) == [1, 4, 6, 4, 1] assert binomial_coefficients_list(5) == [1, 5, 10, 10, 5, 1] assert binomial_coefficients_list(6) == [1, 6, 15, 20, 15, 6, 1] def test_binomial_coefficients(): for n in range(15): c = binomial_coefficients(n) l = [c[k] for k in sorted(c)] assert l == binomial_coefficients_list(n) def test_multinomial_coefficients(): assert multinomial_coefficients(1, 1) == {(1,): 1} assert multinomial_coefficients(1, 2) == {(2,): 1} assert multinomial_coefficients(1, 3) == {(3,): 1} assert multinomial_coefficients(2, 0) == {(0, 0): 1} assert multinomial_coefficients(2, 1) == {(0, 1): 1, (1, 0): 1} assert multinomial_coefficients(2, 2) == {(2, 0): 1, (0, 2): 1, (1, 1): 2} assert multinomial_coefficients(2, 3) == {(3, 0): 1, (1, 2): 3, (0, 3): 1, (2, 1): 3} assert multinomial_coefficients(3, 1) == {(1, 0, 0): 1, (0, 1, 0): 1, (0, 0, 1): 1} assert multinomial_coefficients(3, 2) == {(0, 1, 1): 2, (0, 0, 2): 1, (1, 1, 0): 2, (0, 2, 0): 1, (1, 0, 1): 2, (2, 0, 0): 1} mc = multinomial_coefficients(3, 3) assert mc == {(2, 1, 0): 3, (0, 3, 0): 1, (1, 0, 2): 3, (0, 2, 1): 3, (0, 1, 2): 3, (3, 0, 0): 1, (2, 0, 1): 3, (1, 2, 0): 3, (1, 1, 1): 6, (0, 0, 3): 1} assert dict(multinomial_coefficients_iterator(2, 0)) == {(0, 0): 1} assert dict( multinomial_coefficients_iterator(2, 1)) == {(0, 1): 1, (1, 0): 1} assert dict(multinomial_coefficients_iterator(2, 2)) == \ {(2, 0): 1, (0, 2): 1, (1, 1): 2} assert dict(multinomial_coefficients_iterator(3, 3)) == mc it = multinomial_coefficients_iterator(7, 2) assert [next(it) for i in range(4)] == \ [((2, 0, 0, 0, 0, 0, 0), 1), ((1, 1, 0, 0, 0, 0, 0), 2), ((0, 2, 0, 0, 0, 0, 0), 1), ((1, 0, 1, 0, 0, 0, 0), 2)] def test_issue1257(): assert factorint(1030903) == {53: 2, 367: 1} def test_divisors(): assert divisors(28) == [1, 2, 4, 7, 14, 28] assert [x for x in divisors(3*5*7, 1)] == [1, 3, 5, 15, 7, 21, 35, 105] assert divisors(0) == [] def test_divisor_count(): assert divisor_count(0) == 0 assert divisor_count(6) == 4 def test_primorial(): assert primorial(1) == 2 assert primorial(1, nth=0) == 1 assert primorial(2) == 6 assert primorial(2, nth=0) == 2 assert primorial(4, nth=0) == 6 def test_smoothness_and_smoothness_p(): assert smoothness(1) == (1, 1) assert smoothness(2**4*3**2) == (3, 16) assert smoothness_p(10431, m=1) == \ (1, [(3, (2, 2, 4)), (19, (1, 5, 5)), (61, (1, 31, 31))]) assert smoothness_p(10431) == \ (-1, [(3, (2, 2, 2)), (19, (1, 3, 9)), (61, (1, 5, 5))]) assert smoothness_p(10431, power=1) == \ (-1, [(3, (2, 2, 2)), (61, (1, 5, 5)), (19, (1, 3, 9))]) assert smoothness_p(21477639576571, visual=1) == \ 'p**i=4410317**1 has p-1 B=1787, B-pow=1787\n' + \ 'p**i=4869863**1 has p-1 B=2434931, B-pow=2434931' def test_visual_factorint(): assert factorint(1, visual=1) == 1 forty2 = factorint(42, visual=True) assert type(forty2) == Mul assert str(forty2) == '2**1*3**1*7**1' assert factorint(1, visual=True) is S.One no = dict(evaluate=False) assert factorint(42**2, visual=True) == Mul(Pow(2, 2, **no), Pow(3, 2, **no), Pow(7, 2, **no), **no) assert -1 in factorint(-42, visual=True).args def test_visual_io(): sm = smoothness_p fi = factorint # with smoothness_p n = 124 d = fi(n) m = fi(d, visual=True) t = sm(n) s = sm(t) for th in [d, s, t, n, m]: assert sm(th, visual=True) == s assert sm(th, visual=1) == s for th in [d, s, t, n, m]: assert sm(th, visual=False) == t assert [sm(th, visual=None) for th in [d, s, t, n, m]] == [s, d, s, t, t] assert [sm(th, visual=2) for th in [d, s, t, n, m]] == [s, d, s, t, t] # with factorint for th in [d, m, n]: assert fi(th, visual=True) == m assert fi(th, visual=1) == m for th in [d, m, n]: assert fi(th, visual=False) == d assert [fi(th, visual=None) for th in [d, m, n]] == [m, d, d] assert [fi(th, visual=0) for th in [d, m, n]] == [m, d, d] # test reevaluation no = dict(evaluate=False) assert sm({4: 2}, visual=False) == sm(16) assert sm(Mul(*[Pow(k, v, **no) for k, v in {4: 2, 2: 6}.items()], **no), visual=False) == sm(2**10) assert fi({4: 2}, visual=False) == fi(16) assert fi(Mul(*[Pow(k, v, **no) for k, v in {4: 2, 2: 6}.items()], **no), visual=False) == fi(2**10) def test_modular(): assert solve_congruence(*list(zip([3, 4, 2], [12, 35, 17]))) == (1719, 7140) assert solve_congruence(*list(zip([3, 4, 2], [12, 6, 17]))) is None assert solve_congruence(*list(zip([3, 4, 2], [13, 7, 17]))) == (172, 1547) assert solve_congruence(*list(zip([-10, -3, -15], [13, 7, 17]))) == (172, 1547) assert solve_congruence(*list(zip([-10, -3, 1, -15], [13, 7, 7, 17]))) is None assert solve_congruence( *list(zip([-10, -5, 2, -15], [13, 7, 7, 17]))) == (835, 1547) assert solve_congruence( *list(zip([-10, -5, 2, -15], [13, 7, 14, 17]))) == (2382, 3094) assert solve_congruence( *list(zip([-10, 2, 2, -15], [13, 7, 14, 17]))) == (2382, 3094) assert solve_congruence(*list(zip((1, 1, 2), (3, 2, 4)))) is None raises( ValueError, lambda: solve_congruence(*list(zip([3, 4, 2], [12.1, 35, 17])))) def test_search(): assert 2 in sieve assert 2.1 not in sieve assert 1 not in sieve assert 2**1000 not in sieve raises(ValueError, lambda: sieve.search(1)) sympy-0.7.4.1/sympy/ntheory/tests/__init__.py0000644000175000017500000000000012253362407021356 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/ntheory/generate.py0000644000175000017500000004165212253362407020271 0ustar georgeskgeorgesk""" Generating and counting primes. """ from __future__ import print_function, division import random from bisect import bisect # Using arrays for sieving instead of lists greatly reduces # memory consumption from array import array as _array from .primetest import isprime from sympy.core.compatibility import as_int, xrange def _arange(a, b): ar = _array('l', [0]*(b - a)) for i, e in enumerate(range(a, b)): ar[i] = e return ar 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 "" % \ (len(self._list), self._list[-2], self._list[-1]) 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 xrange(startindex, len(newsieve), p): newsieve[i] = 0 # Merge the sieves self._list += _array('l', [x for x in newsieve if x]) 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)) 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 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() 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] 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: return sieve.search(n)[0] 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 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 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 modify 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 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 = 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 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 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 sympy-0.7.4.1/sympy/ntheory/__init__.py0000644000175000017500000000130212253362407020222 0ustar georgeskgeorgesk""" Number theory module (primes, etc) """ from .generate import nextprime, prevprime, prime, primepi, primerange, \ randprime, Sieve, sieve, primorial, cycle_length from .primetest import isprime from .factor_ import divisors, factorint, multiplicity, perfect_power, \ pollard_pm1, pollard_rho, primefactors, totient, trailing, divisor_count from .partitions_ import npartitions from .residue_ntheory import is_primitive_root, is_quad_residue, \ legendre_symbol, jacobi_symbol, n_order, sqrt_mod, quadratic_residues, \ primitive_root, nthroot_mod, is_nthpow_residue, sqrt_mod_iter from .multinomial import binomial_coefficients, binomial_coefficients_list, \ multinomial_coefficients sympy-0.7.4.1/sympy/statistics/0000755000175000017500000000000012253362407016617 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/statistics/distributions.py0000644000175000017500000003474112253362407022104 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import sympify, Lambda, Dummy, Integer, Rational, oo, Float, pi from sympy.core.compatibility import xrange from sympy.functions import sqrt, exp, erf from sympy.printing import sstr from sympy.utilities import default_sort_key import random 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, key=default_sort_key)) 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) class ContinuousProbability(object): """Base class for continuous probability distributions""" 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) 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 xrange(n)]) def __repr__(self): return sstr(self) def __str__(self): return sstr(self) 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) 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)) 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)) 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 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) 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)) 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) 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))) 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 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) 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 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 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 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]))) sympy-0.7.4.1/sympy/statistics/tests/0000755000175000017500000000000012253362407017761 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/statistics/tests/test_statistics.py0000644000175000017500000001042412253362407023565 0ustar georgeskgeorgeskfrom sympy import sqrt, Rational, oo, Symbol, exp, pi, symbols from sympy.functions import erf from operator import abs from sympy.mpmath import mp from sympy.utilities.tests.test_pickling import check # Disable sympy.statistics deprecation warning for the tests # The warning is in __init__.py, so we only need to disable it for imports import warnings from sympy.utilities.exceptions import SymPyDeprecationWarning warnings.filterwarnings("ignore", message="sympy.statistics has been deprecated since SymPy 0.7.2", category=SymPyDeprecationWarning) from sympy.statistics.distributions import (Normal, Uniform, Sample, PDF, ContinuousProbability) warnings.simplefilter("error", category=SymPyDeprecationWarning) x, y, z = symbols('x y z') def test_normal(): dps, mp.dps = mp.dps, 20 N = Normal(0, 1) assert N.random() assert N.mean == 0 assert N.variance == 1 assert N.probability(-1, 1) == erf(1/sqrt(2)) assert N.probability(-1, 0) == erf(1/sqrt(2))/2 N = Normal(2, 4) assert N.mean == 2 assert N.variance == 16 assert N.confidence(1) == (-oo, oo) assert N.probability(1, 3) == erf(1/sqrt(32)) assert N.pdf(1).evalf() == (exp(Rational(-1, 32)) / (4*sqrt(2*pi))).evalf() for p in [0.1, 0.3, 0.7, 0.9, 0.995]: a, b = N.confidence(p) assert abs(float(N.probability(a, b).evalf()) - p) < 1e-10 N = Normal(0, 2/sqrt(2*pi)) assert N.pdf(0) == Rational(1, 2) mp.dps = dps def test_uniform(): U = Uniform(-3, -1) assert str(U) == "Uniform(-3, -1)" assert repr(U) == "Uniform(-3, -1)" x = U.random() assert x < -1 and x > -3 assert U.mean == -2 assert U.confidence(1) == (-3, -1) assert U.confidence(Rational(1, 2)) == (Rational(-5, 2), Rational(-3, 2)) assert U.pdf(-4) == 0 assert U.pdf(-Rational(3, 2)) == Rational(1, 2) assert U.pdf(0) == 0 assert U.cdf(-4) == 0 assert U.cdf(-Rational(3, 2)) == Rational(3, 4) assert U.cdf(0) == 1 def test_fit(): import random random.seed(1234) n = Normal.fit(Uniform.fit(Normal(2, 1.5).random(1000))) #print n.mean #print n.stddev assert abs(n.mean - 2) < 0.3 assert abs(n.stddev - 1.5) < 0.3 n = Normal.fit([1, 2, 3, 4, 5]) assert n.mean == 3 assert n.stddev == sqrt(2) n = Uniform.fit([1, 2, 3, 4, 5]) assert n.mean == 3 assert n.stddev == sqrt(2) def test_sample(): s = Sample([0, 1]) assert str(s) == "Sample([0, 1])" assert repr(s) == "Sample([0, 1])" assert s.mean == Rational(1, 2) assert s.median == Rational(1, 2) s = Sample([4, 2, 3]) assert s == Sample([2, 3, 4]) assert s.median == 3 s = Sample([4, 2, 3, 1]) assert s.median == Rational(5, 2) def test_PDF(): a = Symbol('a', positive=True) x = Symbol('x', real=True) exponential = PDF(exp(-x/a), (x, 0, oo)) exponential = exponential.normalize() assert exponential.pdf(x) == 1/a*exp(-x/a) assert exponential.cdf(x) == 1 - exp(-x/a) assert exponential.mean == a assert exponential.variance == a**2 assert exponential.stddev == a exponential = PDF(exp(-x/a), x) assert exponential.pdf(x) == exp(-x/a) assert exponential.cdf(x) == -a*exp(-x/a) + oo assert exponential.mean == -oo exponential = PDF(1, (x, 1, 2)) assert exponential.normalize() == exponential assert exponential._get_stddev() == sqrt(3)/6 assert exponential._get_stddev() == sqrt(3)/6 #This test is intentionally repeated to test PDF._get_stddev() properly. exponential = exponential.transform(x, x) assert exponential.pdf(x) == 1 assert exponential.cdf(x) == x - 1 # These last two tests are here instead of test_str.py and test_pickling.py # because this module is deprecated. def test_printing(): assert str(Normal(x + y, z)) == "Normal(x + y, z)" assert str(Sample([x, y, 1])) in [ "Sample([x, y, 1])", "Sample([y, 1, x])", "Sample([1, x, y])", "Sample([y, x, 1])", "Sample([x, 1, y])", "Sample([1, y, x])", ] assert str(Uniform(x, y)) == "Uniform(x, y)" assert str(Uniform(x + y, y)) == "Uniform(x + y, y)" def test_pickling(): for c in (ContinuousProbability, ContinuousProbability(), Normal, Normal(x, y), Sample, Sample([1, 3, 4]), Uniform, Uniform(x, y)): check(c) sympy-0.7.4.1/sympy/statistics/tests/__init__.py0000644000175000017500000000000012253362407022060 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/statistics/__init__.py0000644000175000017500000000052712253362407020734 0ustar georgeskgeorgesk""" SymPy statistics module Deprecated See sympy.stats """ from sympy.utilities.exceptions import SymPyDeprecationWarning SymPyDeprecationWarning( feature="sympy.statistics", useinstead="sympy.stats", issue=3386, deprecated_since_version="0.7.2", ).warn() from .distributions import Normal, Uniform del SymPyDeprecationWarning sympy-0.7.4.1/sympy/liealgebras/0000755000175000017500000000000012253362407016677 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/liealgebras/type_f.py0000644000175000017500000001071612253362407020544 0ustar georgeskgeorgeskfrom sympy.core import Set, Dict, Tuple, Rational from .cartan_type import Standard_Cartan from sympy.matrices import Matrix class TypeF(Standard_Cartan): def __new__(cls, n): assert n == 4 return Standard_Cartan.__new__(cls, "F", 4) def dimension(self): """ Returns the dimension of the vector space V underlying the Lie algebra Example ======== >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType("F4") >>> c.dimension() 4 """ return 4 def basic_root(self, i, j): """ This is a method just to generate roots with a 1 iin the ith position and a -1 in the jth postion. """ n = self.n root = [0]*n root[i] = 1 root[j] = -1 return root def simple_root(self, i): """ Every lie algebra has a unique root system. Given a root system Q, there is a subset of the roots such that an element of Q is called a simple root if it cannot be written as the sum of two elements in Q. If we let D denote the set of simple roots, then it is clear that every element of Q can be written as a linear combination of elements of D with all coefficients non-negative. This method returns the ith simple root of F_4 Example ======= >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType("F4") >>> c.simple_root(3) [0, 0, 0, 1] """ if i < 3: return basic_root(i-1, i) if i == 3: root = [0]*4 root[3] = 1 return root if i == 4: root = [Rational(-1, 2)]*4 return root def positive_roots(self): """ This method generates all the positive roots of A_n. This is half of all of the roots of F_4; by multiplying all the positive roots by -1 we get the negative roots. Example ====== >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType("A3") >>> c.positive_roots() {1: [1, -1, 0, 0], 2: [1, 0, -1, 0], 3: [1, 0, 0, -1], 4: [0, 1, -1, 0], 5: [0, 1, 0, -1], 6: [0, 0, 1, -1]} """ n = self.n posroots = {} k = 0 for i in range(0, n-1): for j in range(i+1, n): k += 1 posroots[k] = self.basic_root(i, j) k += 1 root = self.basic_root(i, j) root[j] = 1 posroots[k] = root for i in range(0, n): k += 1 root = [0]*n root[i] = 1 posroots[k] = root k += 1 root = [Rational(1, 2)]*n posroots[k] = root for i in range(1, 4): k += 1 root = [Rational(1, 2)]*n root[i] = Rational(-1, 2) posroots[k] = root posroots[k+1] = [Rational(1, 2), Rational(1, 2), Rational(-1, 2), Rational(-1, 2)] posroots[k+2] = [Rational(1, 2), Rational(-1, 2), Rational(1, 2), Rational(-1, 2)] posroots[k+3] = [Rational(1, 2), Rational(-1, 2), Rational(-1, 2), Rational(1, 2)] posroots[k+4] = [Rational(1, 2), Rational(-1, 2), Rational(-1, 2), Rational(-1, 2)] return posroots def roots(self): """ Returns the total number of roots for F_4 """ return 48 def cartan_matrix(self): """ Returns the Cartan matrix for F_4 The Cartan matrix matrix for a Lie algebra is generated by assigning an ordering to the simple roots, (alpha[1], ...., alpha[l]). Then the ijth entry of the Cartan matrix is (). Example ======= >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType('A4') >>> c.cartan_matrix() Matrix([ [ 2, -1, 0, 0], [-1, 2, -1, 0], [ 0, -1, 2, -1], [ 0, 0, -1, 2]]) """ m = Matrix( 4, 4, [2, -1, 0, 0, -1, 2, -2, 0, 0, -1, 2, -1, 0, 0, -1, 2]) return m def basis(self): """ Returns the number of independent generators of F_4 """ return 52 def dynkin_diagram(self): diag = "0---0=>=0---0\n" diag += " ".join(str(i) for i in range(1, 5)) return diag sympy-0.7.4.1/sympy/liealgebras/type_c.py0000644000175000017500000001074012253362407020536 0ustar georgeskgeorgeskfrom sympy.core import Set, Dict, Tuple from .cartan_type import Standard_Cartan from sympy.matrices import eye class TypeC(Standard_Cartan): def __new__(cls, n): assert n >= 3 return Standard_Cartan.__new__(cls, "C", n) def dimension(self): """ Return the dimension of the vector space V underlying the Lie algebra Example ======== >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType("C3") >>> c.dimension() 3 """ n = self.n return n def basic_root(self, i, j): """ This is a method just to generate roots with a 1 iin the ith position and a -1 in the jth postion. """ n = self.n root = [0]*n root[i] = 1 root[j] = -1 return root def simple_root(self, i): """ Every lie algebra has a unique root system. Given a root system Q, there is a subset of the roots such that an element of Q is called a simple root if it cannot be written as the sum of two elements in Q. If we let D denote the set of simple roots, then it is clear that every element of Q can be written as a linear combination of elements of D with all coefficients non-negative. In C_n, the first n-1 simple roots are the same as the roots in A_(n-1) (a 1 in the ith position, a -1 in the (i+1)th position, and zeroes elsewhere). The nth simple root is the root in which there is a 2 in the nth position and zeroes elsewhere. This method returns the ith simple root for the C series Example ======== >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType("C3") >>> c.simple_root(2) [0, 1, -1] """ n = self.n if i < n: return self.basic_root(i-1,i) else: root = [0]*self.n root[n-1] = 2 return root def positive_roots(self): """ This method generates all the positive roots of A_n. This is half of all of the roots of C_n; by multiplying all the positive roots by -1 we get the negative roots. Example ====== >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType("A3") >>> c.positive_roots() {1: [1, -1, 0, 0], 2: [1, 0, -1, 0], 3: [1, 0, 0, -1], 4: [0, 1, -1, 0], 5: [0, 1, 0, -1], 6: [0, 0, 1, -1]} """ n = self.n posroots = {} k = 0 for i in range(0, n-1): for j in range(i+1, n): k += 1 posroots[k] = self.basic_root(i, j) k += 1 root = self.basic_root(i, j) root[j] = 1 posroots[k] = root for i in range(0, n): k += 1 root = [0]*n root[i] = 2 posroots[k] = root return posroots def roots(self): """ Returns the total number of roots for C_n" """ n = self.n return 2*(n**2) def cartan_matrix(self): """ Returns the Cartan matrix for C_n. The Cartan matrix matrix for a Lie algebra is generated by assigning an ordering to the simple roots, (alpha[1], ...., alpha[l]). Then the ijth entry of the Cartan matrix is (). Example ======= >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType('C4') >>> c.cartan_matrix() Matrix([ [ 2, -1, 0, 0], [-1, 2, -1, 0], [ 0, -1, 2, -1], [ 0, 0, -2, 2]]) """ n = self.n m = 2 * eye(n) i = 1 while i < n-1: m[i, i+1] = -1 m[i, i-1] = -1 i += 1 m[0,1] = -1 m[n-1, n-2] = -2 return m def basis(self): """ Returns the number of independent generators of C_n """ n = self.n return n*(2*n + 1) def lie_algebra(self): """ Returns the Lie algebra associated with C_n" """ n = self.n return "sp(" + str(2*n) + ")" def dynkin_diagram(self): n = self.n diag = "---".join("0" for i in range (1, n)) + "=<=0\n" diag += " ".join(str(i) for i in range (1, n+1)) return diag sympy-0.7.4.1/sympy/liealgebras/cartan_type.py0000644000175000017500000000340412253362407021563 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import Basic, Symbol, Dict, Tuple class CartanType_generator(Basic): """ Constructor for actually creating things """ def __call__(self, *args): c = args[0] c = list(c) letter, n = c[0], int(c[1]) if n < 0: raise ValueError("Lie algebra rank cannot be negative") if letter == "A": if n >= 0: from . import type_a return type_a.TypeA(n) if letter == "B": if n >= 0: from . import type_b return type_b.TypeB(n) if letter == "C": if n >= 0: from . import type_c return type_c.TypeC(n) if letter == "D": if n >= 0: from . import type_d return type_d.TypeD(n) if letter == "E": if n >= 6 and n <= 8: from . import type_e return type_e.TypeE(n) if letter == "F": if n == 4: from . import type_f return type_f.TypeF(n) if letter == "G": if n == 2: from . import type_g return type_g.TypeG(n) CartanType = CartanType_generator() class Standard_Cartan(Basic): """ Concrete base class for Cartan types such as A4, etc """ def __new__(cls, series, n): obj = Basic.__new__(cls, series, n) obj.n = n obj.series = series return obj def rank(self): """ Returns the rank of the Lie algebra """ return self.n def series(self): """ Returns the type of the Lie algebra """ return self.series sympy-0.7.4.1/sympy/liealgebras/root_system.py0000644000175000017500000001547512253362407021654 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- from .cartan_type import CartanType from sympy.core import Basic class RootSystem(Basic): """ Every simple Lie algebra has a unique root system. To find the root system, we first consider the Cartan subalgebra of g, which is the maximal abelian subalgebra, and consider the adjoint action of g on this subalgebra.  There is a root system associated with this action.  Now, a root system over a vector space V is a set of finite vectors Φ(called roots), which satisfy: 1. The roots span V 2. The only scalar multiples of x in Φ are x and -x 3. For every x in Φ, the set Φ is closed under reflection through the hyperplane perpendicular to x. 4. If x and y are roots in Φ, then the projection of y onto the line through x is a half-integral multiple of x. Now, there is a subset of Φ, which we will call Δ, such that: 1. Δ is a basis of V 2. Each root x in Φ can be written x = Σ k_y y for y in Δ The elements of Δ are called the simple roots. Therefore, we see that the simple roots span the root space of a given simple Lie algebra. References: https://en.wikipedia.org/wiki/Root_system Lie Algebras and Representation Theory - Humphreys """ def __new__(cls, cartantype): """ Creates a new RootSystem object. This method assigns an attribute called cartan_type to each instance of a RootSystem object. When an instance of RootSystem is called, it needs an argument, which should be an instance of a simple Lie algebra. We then take the CartanType of this argument and set it as the cartan_type attribute of the RootSystem instance. """ obj = Basic.__new__(cls, cartantype) obj.cartan_type = CartanType(cartantype) return obj def simple_roots(self): """ This method generates and returns the simple roots of the Lie algebra. The rank of the Lie algebra determines the number of simple roots that it has. This method obtains the rank of the Lie algebra, and then uses the simple_root method from the Lie algebra classes to generate all the simple roots. Example ==== >>> from sympy.liealgebras.root_system import RootSystem >>> c = RootSystem("A3") >>> roots = c.simple_roots() >>> roots {1: [1, -1, 0, 0], 2: [0, 1, -1, 0], 3: [0, 0, 1, -1]} """ n = self.cartan_type.rank() roots = {} for i in range(1, n+1): root = self.cartan_type.simple_root(i) roots[i] = root return roots def all_roots(self): """ This method generates all the roots of a given root system, and stores them in a dictionary where the keys are integer numbers. It generates the roots by getting the dictionary of all positive roots from the bases classes, and then taking each root, and multiplying it by -1 and adding it to the dictionary. In this way all the negative roots are generated. """ alpha = self.cartan_type.positive_roots() keys = list(alpha.keys()) k = max(keys) for val in keys: k += 1 root = alpha[val] newroot = [-x for x in root] alpha[k] = newroot return alpha def root_space(self): """ The root space is the vector space spanned by the simple roots, i.e. it is a vector space with a distinguished basis, the simple roots. This method returns a string that represents the root space as the span of the simple roots, alpha[1],...., alpha[n]. Example ======= >>> from sympy.liealgebras.root_system import RootSystem >>> c = RootSystem("A3") >>> c.root_space() 'alpha[1] + alpha[2] + alpha[3]' """ n = self.cartan_type.rank() rs = " + ".join("alpha["+str(i) +"]" for i in range(1, n+1)) return rs def add_simple_roots(self, root1, root2): """ This is a method for adding two simple roots together. The function takes as input two integers, root1 and root2. It then uses these integers as keys in the dictionary of simple roots, and gets the corresponding simple roots, and then adds them together. Example ======= >>> from sympy.liealgebras.root_system import RootSystem >>> c = RootSystem("A3") >>> newroot = c.add_simple_roots(1, 2) >>> newroot [1, 0, -1, 0] """ alpha = self.simple_roots() if root1 > len(alpha) or root2 > len(alpha): raise ValueError("You've used a root that doesn't exist!") a1 = alpha[root1] a2 = alpha[root2] newroot = [] length = len(a1) for i in range(length): newroot.append(a1[i] + a2[i]) return newroot def add_as_roots(self, root1, root2): """ This is a method that takes two roots and adds them together if and only if their sum is also a root. It takes as input two vectors which should be roots. It then computes their sum and checks if it is in the list of all possible roots. If it is, it returns the sum. Otherwise it returns a string saying that the sum is not a root. Example ======= >>> from sympy.liealgebras.root_system import RootSystem >>> c = RootSystem("A3") >>> c.add_as_roots([1, 0, -1, 0], [0, 0, 1, -1]) [1, 0, 0, -1] >>> c.add_as_roots([1, -1, 0, 0], [0, 0, -1, 1]) 'The sum of these two roots is not a root' """ alpha = self.all_roots() newroot = [] for entry in range(len(root1)): newroot.append(root1[entry] + root2[entry]) if newroot in alpha.values(): return newroot else: return "The sum of these two roots is not a root" def cartan_matrix(self): """ Return the Cartan matrix of Lie algebra associated with this root system. Example ======= >>> from sympy.liealgebras.root_system import RootSystem >>> c = RootSystem("A3") >>> c.cartan_matrix() Matrix([ [ 2, -1, 0], [-1, 2, -1], [ 0, -1, 2]]) """ return self.cartan_type.cartan_matrix() def dynkin_diagram(self): """ Return the Dynkin diagram of the Lie algebra associated with this root system. Example ======= >>> from sympy.liealgebras.root_system import RootSystem >>> c = RootSystem("A3") >>> print(c.dynkin_diagram()) 0---0---0 1 2 3 """ return self.cartan_type.dynkin_diagram() sympy-0.7.4.1/sympy/liealgebras/type_a.py0000644000175000017500000001043012253362407020530 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import Set, Dict, Tuple from sympy.liealgebras.cartan_type import Standard_Cartan, CartanType from sympy.matrices import eye class TypeA(Standard_Cartan): """ This class contains the information about the A series of simple Lie algebras. ==== """ def __new__(cls, n): assert n >= 1 return Standard_Cartan.__new__(cls, "A", n) def dimension(self): """ Return the dimension of the vector space V underlying the Lie algebra Example ======== >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType("A4") >>> c.dimension() 5 """ return self.n+1 def basic_root(self, i, j): """ This is a method just to generate roots with a 1 iin the ith position and a -1 in the jth postion. """ n = self.n root = [0]*(n+1) root[i] = 1 root[j] = -1 return root def simple_root(self, i): """ Every lie algebra has a unique root system. Given a root system Q, there is a subset of the roots such that an element of Q is called a simple root if it cannot be written as the sum of two elements in Q. If we let D denote the set of simple roots, then it is clear that every element of Q can be written as a linear combination of elements of D with all coefficients non-negative. In A_n the ith simple root is the root which has a 1 in the ith position, a -1 in the (i+1)th position, and zeroes elsewhere. This method returns the ith simple root for the A series. Examples ======== >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType("A4") >>> c.simple_root(1) [1, -1, 0, 0, 0] """ return self.basic_root(i-1, i) def positive_roots(self): """ This method generates all the positive roots of A_n. This is half of all of the roots of A_n; by multiplying all the positive roots by -1 we get the negative roots. Example ====== >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType("A3") >>> c.positive_roots() {1: [1, -1, 0, 0], 2: [1, 0, -1, 0], 3: [1, 0, 0, -1], 4: [0, 1, -1, 0], 5: [0, 1, 0, -1], 6: [0, 0, 1, -1]} """ n = self.n posroots = {} k = 0 for i in range(0, n): for j in range(i+1, n+1): k += 1 posroots[k] = self.basic_root(i, j) return posroots def highest_root(self): """ Returns the heighest weight root for A_n """ return self.basic_root(0, self.n) def roots(self): """ Returns the total number of roots for A_n """ n = self.n return n*(n+1) def cartan_matrix(self): """ Returns the Cartan matrix for A_n. The Cartan matrix matrix for a Lie algebra is generated by assigning an ordering to the simple roots, (alpha[1], ...., alpha[l]). Then the ijth entry of the Cartan matrix is (). Example ======= >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType('A4') >>> c.cartan_matrix() Matrix([ [ 2, -1, 0, 0], [-1, 2, -1, 0], [ 0, -1, 2, -1], [ 0, 0, -1, 2]]) """ n = self.n m = 2 * eye(n) i = 1 while i < n-1: m[i, i+1] = -1 m[i, i-1] = -1 i += 1 m[0,1] = -1 m[n-1, n-2] = -1 return m def basis(self): """ Returns the number of independent generators of A_n """ n = self.n return n**2 - 1 def lie_algebra(self): """ Returns the Lie algebra associated with A_n """ n = self.n return "su(" + str(n + 1) + ")" def dynkin_diagram(self): n = self.n diag = "---".join("0" for i in range(1, n+1)) + "\n" diag += " ".join(str(i) for i in range(1, n+1)) return diag sympy-0.7.4.1/sympy/liealgebras/cartan_matrix.py0000644000175000017500000000107112253362407022104 0ustar georgeskgeorgeskfrom .cartan_type import CartanType def CartanMatrix(ct): """ This is a method that allows a user to access the Cartan matrix of a specific Lie algebra. Example ======= >>> from sympy.liealgebras.cartan_matrix import CartanMatrix >>> CartanMatrix("A2") Matrix([ [ 2, -1], [-1, 2]]) >>> CartanMatrix(['C', 3]) Matrix([ [ 2, -1, 0], [-1, 2, -1], [ 0, -2, 2]]) This method works by returning the Cartan matrix which corresponds to Cartan type t. """ return CartanType(ct).cartan_matrix() sympy-0.7.4.1/sympy/liealgebras/type_b.py0000644000175000017500000001100512253362407020530 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import Set, Dict, Tuple from .cartan_type import Standard_Cartan from sympy.matrices import eye class TypeB(Standard_Cartan): def __new__(cls, n): assert n >= 2 return Standard_Cartan.__new__(cls, "B", n) def dimension(self): """ Return the dimension of the vector space V underlying the Lie algebra Example ======== >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType("B3") >>> c.dimension() 3 """ return self.n def basic_root(self, i, j): """ This is a method just to generate roots with a 1 iin the ith position and a -1 in the jth postion. """ root = [0]*self.n root[i] = 1 root[j] = -1 return root def simple_root(self, i): """ Every lie algebra has a unique root system. Given a root system Q, there is a subset of the roots such that an element of Q is called a simple root if it cannot be written as the sum of two elements in Q. If we let D denote the set of simple roots, then it is clear that every element of Q can be written as a linear combination of elements of D with all coefficients non-negative. In B_n the first n-1 simple roots are the same as the roots in A_(n-1) (a 1 in the ith position, a -1 in the (i+1)th position, and zeroes elsewhere). The n-th simple root is the root with a 1 in the nth position and zeroes elsewhere. This method returns the ith simple root for the B series. Example ======== >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType("B3") >>> c.simple_root(2) [0, 1, -1] """ n = self.n if i < n: return self.basic_root(i-1, i) else: root = [0]*self.n root[n-1] = 1 return root def positive_roots(self): """ This method generates all the positive roots of A_n. This is half of all of the roots of B_n; by multiplying all the positive roots by -1 we get the negative roots. Example ====== >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType("A3") >>> c.positive_roots() {1: [1, -1, 0, 0], 2: [1, 0, -1, 0], 3: [1, 0, 0, -1], 4: [0, 1, -1, 0], 5: [0, 1, 0, -1], 6: [0, 0, 1, -1]} """ n = self.n posroots = {} k = 0 for i in range(0, n-1): for j in range(i+1, n): k += 1 posroots[k] = self.basic_root(i, j) k += 1 root = self.basic_root(i, j) root[j] = 1 posroots[k] = root for i in range(0, n): k += 1 root = [0]*n root[i] = 1 posroots[k] = root return posroots def roots(self): """ Returns the total number of roots for B_n" """ n = self.n return 2*(n**2) def cartan_matrix(self): """ Returns the Cartan matrix for B_n. The Cartan matrix matrix for a Lie algebra is generated by assigning an ordering to the simple roots, (alpha[1], ...., alpha[l]). Then the ijth entry of the Cartan matrix is (). Example ======= >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType('B4') >>> c.cartan_matrix() Matrix([ [ 2, -1, 0, 0], [-1, 2, -1, 0], [ 0, -1, 2, -2], [ 0, 0, -1, 2]]) """ n = self.n m = 2* eye(n) i = 1 while i < n-1: m[i, i+1] = -1 m[i, i-1] = -1 i += 1 m[0, 1] = -1 m[n-2, n-1] = -2 m[n-1, n-2] = -1 return m def basis(self): """ Returns the number of independent generators of B_n """ n = self.n return (n**2 - n)/2 def lie_algebra(self): """ Returns the Lie algebra associated with B_n """ n = self.n return "so(" + str(2*n) + ")" def dynkin_diagram(self): n = self.n diag = "---".join("0" for i in range (1, n)) + "=>=0\n" diag += " ".join(str(i) for i in range (1, n+1)) return diag sympy-0.7.4.1/sympy/liealgebras/type_d.py0000644000175000017500000001112612253362407020536 0ustar georgeskgeorgeskfrom sympy.core import Set, Dict, Tuple from .cartan_type import Standard_Cartan from sympy.matrices import eye class TypeD(Standard_Cartan): def __new__(cls, n): assert n >= 3 return Standard_Cartan.__new__(cls, "D", n) def dimension(self): """ Return the dimension of the vector space V underlying the Lie algebra Example ======== >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType("D4") >>> c.dimension() 4 """ return self.n def basic_root(self, i, j): """ This is a method just to generate roots with a 1 iin the ith position and a -1 in the jth postion. """ n = self.n root = [0]*n root[i] = 1 root[j] = -1 return root def simple_root(self, i): """ Every lie algebra has a unique root system. Given a root system Q, there is a subset of the roots such that an element of Q is called a simple root if it cannot be written as the sum of two elements in Q. If we let D denote the set of simple roots, then it is clear that every element of Q can be written as a linear combination of elements of D with all coefficients non-negative. In D_n, the first n-1 simple roots are the same as the roots in A_(n-1) (a 1 in the ith position, a -1 in the (i+1)th position, and zeroes elsewhere). The nth simple root is the root in which there 1s in the nth and (n-1)th positions, and zeroes elsewhere. This method returns the ith simple root for the D series. Example ======== >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType("D4") >>> c.simple_root(2) [0, 1, -1, 0] """ n = self.n if i < n: return self.basic_root(i-1, i) else: root = [0]*n root[n-2] = 1 root[n-1] = 1 return root def positive_roots(self): """ This method generates all the positive roots of A_n. This is half of all of the roots of D_n by multiplying all the positive roots by -1 we get the negative roots. Example ====== >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType("A3") >>> c.positive_roots() {1: [1, -1, 0, 0], 2: [1, 0, -1, 0], 3: [1, 0, 0, -1], 4: [0, 1, -1, 0], 5: [0, 1, 0, -1], 6: [0, 0, 1, -1]} """ n = self.n posroots = {} k = 0 for i in range(0, n-1): for j in range(i+1, n): k += 1 posroots[k] = self.basic_root(i, j) k += 1 root = self.basic_root(i, j) root[j] = 1 posroots[k] = root return posroots def roots(self): """ Returns the total number of roots for D_n" """ n = self.n return 2*n*(n-1) def cartan_matrix(self): """ Returns the Cartan matrix for D_n. The Cartan matrix matrix for a Lie algebra is generated by assigning an ordering to the simple roots, (alpha[1], ...., alpha[l]). Then the ijth entry of the Cartan matrix is (). Example ======= >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType('D4') >>> c.cartan_matrix() Matrix([ [ 2, -1, 0, 0], [-1, 2, -1, -1], [ 0, -1, 2, 0], [ 0, -1, 0, 2]]) """ n = self.n m = 2*eye(n) i = 1 while i < n-2: m[i,i+1] = -1 m[i,i-1] = -1 i += 1 m[n-2, n-3] = -1 m[n-3, n-1] = -1 m[n-1, n-3] = -1 m[0, 1] = -1 return m def basis(self): """ Returns the number of independent generators of D_n """ n = self.n return n*(n-1)/2 def lie_algebra(self): """ Returns the Lie algebra associated with D_n" """ n = self.n return "so(" + str(2*n) + ")" def dynkin_diagram(self): n = self.n diag = " "*4*(n-3) + str(n-1) + "\n" diag += " "*4*(n-3) + "0\n" diag += " "*4*(n-3) +"|\n" diag += " "*4*(n-3) + "|\n" diag += "---".join("0" for i in range(1,n)) + "\n" diag += " ".join(str(i) for i in range(1, n-1)) + " "+str(n) return diag sympy-0.7.4.1/sympy/liealgebras/type_e.py0000644000175000017500000002311112253362407020534 0ustar georgeskgeorgeskfrom sympy.core import Set, Dict, Tuple, Rational from .cartan_type import Standard_Cartan from sympy.matrices import eye class TypeE(Standard_Cartan): def __new__(cls, n): assert n >= 6 assert n <= 8 return Standard_Cartan.__new__(cls, "E", n) def dimension(self): """ Returns the dimension of the vector space V underlying the Lie algebra Example ======== >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType("E6") >>> c.dimension() 8 """ return 8 def basic_root(self, i, j): """ This is a method just to generate roots with a -1 in the ith position and a 1 in the jth postion. """ root = [0]*8 root[i] = -1 root[j] = 1 return root def simple_root(self, i): """ Every lie algebra has a unique root system. Given a root system Q, there is a subset of the roots such that an element of Q is called a simple root if it cannot be written as the sum of two elements in Q. If we let D denote the set of simple roots, then it is clear that every element of Q can be written as a linear combination of elements of D with all coefficients non-negative. This method returns the ith simple root for E_n. Examples ======== >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType("E6") >>> c.simple_root(2) [1, 1, 0, 0, 0, 0, 0, 0] """ n = self.n if i == 1: root = [-0.5]*8 root[0] = 0.5 root[7] = 0.5 return root elif i == 2: root = [0]*8 root[1] = 1 root[0] = 1 return root else: if i == 7 or i == 8 and n == 6: raise ValueError("E6 only has six simple roots!") if i == 8 and n == 7: raise ValueError("E7 has only 7 simple roots!") return self.basic_root(i-3, i-2) def positive_roots(self): """ This method generates all the positive roots of A_n. This is half of all of the roots of E_n; by multiplying all the positive roots by -1 we get the negative roots. Example ====== >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType("A3") >>> c.positive_roots() {1: [1, -1, 0, 0], 2: [1, 0, -1, 0], 3: [1, 0, 0, -1], 4: [0, 1, -1, 0], 5: [0, 1, 0, -1], 6: [0, 0, 1, -1]} """ n = self.n if n == 6: posroots = {} k = 0 for i in range(n-1): for j in range(i+1, n-1): k += 1 root = self.basic_root(i, j) posroots[k] = root k += 1 root = self.basic_root(i, j) root[i] = 1 posroots[k] = root root = [Rational(1, 2), Rational(1, 2), Rational(1, 2), Rational(1, 2), Rational(1, 2), Rational(-1, 2), Rational(-1, 2), Rational(1, 2)] for a in range(0, 2): for b in range(0, 2): for c in range(0, 2): for d in range(0, 2): for e in range(0, 2): if (a + b + c + d + e)%2 == 0: k += 1 if a == 1: root[0] = Rational(-1, 2) if b == 1: root[1] = Rational(-1, 2) if c == 1: root[2] = Rational(-1, 2) if d == 1: root[3] = Rational(-1, 2) if e == 1: root[4] = Rational(-1, 2) posroots[k] = root return posroots if n == 7: posroots = {} k = 0 for i in range(n-1): for j in range(i+1, n-1): k += 1 root = self.basic_root(i, j) posroots[k] = root k += 1 root = self.basic_root(i, j) root[i] = 1 posroots[k] = root k += 1 posroots[k] = [0, 0, 0, 0, 0, 1, 1, 0] root = [Rational(1, 2), Rational(1, 2), Rational(1, 2), Rational(1, 2), Rational(1, 2), Rational(-1, 2), Rational(-1, 2), Rational(1, 2)] for a in range(0, 2): for b in range(0, 2): for c in range(0, 2): for d in range(0, 2): for e in range(0, 2): for f in range(0, 2): if (a + b + c + d + e + f)%2 == 0: k += 1 if a == 1: root[0] = Rational(-1, 2) if b == 1: root[1] = Rational(-1, 2) if c == 1: root[2] = Rational(-1, 2) if d == 1: root[3] = Rational(-1, 2) if e == 1: root[4] = Rational(-1, 2) if f == 1: root[5] = Rational(1, 2) posroots[k] = root return posroots if n == 8: posroots = {} k = 0 for i in range(n): for j in range(i+1, n): k += 1 root = self.basic_root(i, j) posroots[k] = root k += 1 root = self.basic_root(i, j) root[i] = 1 posroots[k] = root root = [Rational(1, 2), Rational(1, 2), Rational(1, 2), Rational(1, 2), Rational(1, 2), Rational(-1, 2), Rational(-1, 2), Rational(1, 2)] for a in range(0, 2): for b in range(0, 2): for c in range(0, 2): for d in range(0, 2): for e in range(0, 2): for f in range(0, 2): for g in range(0, 2): if (a + b + c + d + e + f + g)%2 == 0: k += 1 if a == 1: root[0] = Rational(-1, 2) if b == 1: root[1] = Rational(-1, 2) if c == 1: root[2] = Rational(-1, 2) if d == 1: root[3] = Rational(-1, 2) if e == 1: root[4] = Rational(-1, 2) if f == 1: root[5] = Rational(1, 2) if g == 1: root[6] = Rational(1, 2) posroots[k] = root return posroots def roots(self): """ Returns the total number of roots of E_n """ n = self.n if n == 6: return 72 if n == 7: return 126 if n == 8: return 240 def cartan_matrix(self): """ Returns the Cartan matrix for G_2 The Cartan matrix matrix for a Lie algebra is generated by assigning an ordering to the simple roots, (alpha[1], ...., alpha[l]). Then the ijth entry of the Cartan matrix is (). Example ======= >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType('A4') >>> c.cartan_matrix() Matrix([ [ 2, -1, 0, 0], [-1, 2, -1, 0], [ 0, -1, 2, -1], [ 0, 0, -1, 2]]) """ n = self.n m = 2*eye(n) i = 3 while i < n-1: m[i, i+1] = -1 m[i, i-1] = -1 i += 1 m[0, 2] = m[2, 0] = -1 m[1, 3] = m[3, 1] = -1 m[2, 3] = -1 m[n-1, n-2] = -1 return m def basis(self): """ Returns the number of independent generators of E_n """ n = self.n if n == 6: return 78 if n == 7: return 133 if n == 8: return 248 def dynkin_diagram(self): n = self.n diag = " "*8 + str(2) + "\n" diag += " "*8 + "0\n" diag += " "*8 + "|\n" diag += " "*8 + "|\n" diag += "---".join("0" for i in range (1, n)) + "\n" diag += "1 " + " ".join(str(i) for i in range(3, n+1)) return diag sympy-0.7.4.1/sympy/liealgebras/type_g.py0000644000175000017500000000575012253362407020547 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- from sympy.core import Set, Dict, Tuple from .cartan_type import Standard_Cartan from sympy.matrices import Matrix class TypeG(Standard_Cartan): def __new__(cls, n): assert n == 2 return Standard_Cartan.__new__(cls, "G", 2) def dimension(self): """ Returns the dimension of the vector space V underlying the Lie algebra Example ======== >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType("G2") >>> c.dimension() 3 """ return 3 def simple_root(self, i): """ Every lie algebra has a unique root system. Given a root system Q, there is a subset of the roots such that an element of Q is called a simple root if it cannot be written as the sum of two elements in Q. If we let D denote the set of simple roots, then it is clear that every element of Q can be written as a linear combination of elements of D with all coefficients non-negative. This method returns the ith simple root of G_2 Example ======= >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType("G2") >>> c.simple_root(1) [0, 1, -1] """ if i == 1: return [0, 1, -1] else: return [1, -2, 1] def positive_roots(self): """ This method generates all the positive roots of A_n. This is half of all of the roots of A_n; by multiplying all the positive roots by -1 we get the negative roots. Example ====== >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType("A3") >>> c.positive_roots() {1: [1, -1, 0, 0], 2: [1, 0, -1, 0], 3: [1, 0, 0, -1], 4: [0, 1, -1, 0], 5: [0, 1, 0, -1], 6: [0, 0, 1, -1]} """ roots = {1: [0, 1, -1], 2: [1, -2, 1], 3: [1, -1, 0], 4: [1, 0, 1], 5: [1, 1, -2], 6: [2, -1, -1]} return roots def roots(self): """ Returns the total number of roots of G_2" """ return 12 def cartan_matrix(self): """ Returns the Cartan matrix for G_2 The Cartan matrix matrix for a Lie algebra is generated by assigning an ordering to the simple roots, (alpha[1], ...., alpha[l]). Then the ijth entry of the Cartan matrix is (). Example ======= >>> from sympy.liealgebras.cartan_type import CartanType >>> c = CartanType("G2") >>> c.cartan_matrix() Matrix([ [ 2, -1], [-3, 2]]) """ m = Matrix( 2, 2, [2, -1, -3, 2]) return m def basis(self): """ Returns the number of independent generators of G_2 """ return 14 def dynkin_diagram(self): diag = "0≡<≡0\n1 2" return diag sympy-0.7.4.1/sympy/liealgebras/weyl_group.py0000644000175000017500000003473512253362407021461 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- from sympy.core import Basic, Rational from sympy.core.numbers import igcd from .cartan_type import CartanType from sympy.mpmath import fac from operator import itemgetter from itertools import groupby from sympy.matrices import Matrix, eye class WeylGroup(Basic): """ For each semisimple Lie group, we have a Weyl group. It is a subgroup of the isometry group of the root system. Specifically, it’s the subgroup that is generated by reflections through the hyperplanes orthogonal to the roots. Therefore, Weyl groups are reflection groups, and so a Weyl group is a finite Coxeter group. """ def __new__(cls, cartantype): obj = Basic.__new__(cls, cartantype) obj.cartan_type = CartanType(cartantype) return obj def generators(self): """ This method creates the generating reflections of the Weyl group for a given Lie algebra. For a Lie algebra of rank n, there are n different generating reflections. This function returns them as a list. Example ======= >>> from sympy.liealgebras.weyl_group import WeylGroup >>> c = WeylGroup("F4") >>> c.generators() ['r1', 'r2', 'r3', 'r4'] """ n = self.cartan_type.rank() generators = [] for i in range(1, n+1): reflection = "r"+str(i) generators.append(reflection) return generators def group_order(self): """ This method returns the order of the Weyl group. For types A, B, C, D, and E the order depends on the rank of the Lie algebra. For types F and G, the order is fixed. Example ======= >>> from sympy.liealgebras.weyl_group import WeylGroup >>> c = WeylGroup("D4") >>> c.group_order() 192.0 """ n = self.cartan_type.rank() if self.cartan_type.series == "A": return fac(n+1) if self.cartan_type.series == "B" or self.cartan_type.series == "C": return fac(n)*(2**n) if self.cartan_type.series == "D": return fac(n)*(2**(n-1)) if self.cartan_type.series == "E": if n == 6: return 51840 if n == 7: return 2903040 if n == 8: return 696729600 if self.cartan_type.series == "F": return 1152 if self.cartan_type.series == "G": return 12 def group_name(self): """ This method returns some general information about the Weyl group for a given Lie algebra. It returns the name of the group and the elements it acts on, if relevant. """ n = self.cartan_type.rank() if self.cartan_type.series == "A": return "S"+str(n+1) + ": the symmetric group acting on " + str(n+1) + " elements." if self.cartan_type.series == "B" or self.cartan_type.series == "C": return "The hyperoctahedral group acting on " + str(2*n) + " elements." if self.cartan_type.series == "D": return "The symmetry group of the " + str(n) + "-dimensional demihypercube." if self.cartan_type.series == "E": if n == 6: return "The symmetry group of the 6-polytope." if n == 7: return "The symmetry group of the 7-polytope." if n == 8: return "The symmetry group of the 8-polytope." if self.cartan_type.series == "F": return "The symmetry group of the 24-cell, or icositetrachoron." if self.cartan_type.series == "G": return "D6, the dihedral group of order 12, and symmetry group of the hexagon." def element_order(self, weylelt): """ This method returns the order of a given Weyl group element, which should be specified by the user in the form of products of the generating reflections, i.e. of the form r1*r2 etc. For types A-F, this method current works by taking the matrix form of the specified element, and then finding what power of the matrix is the identity. It then returns this power. Example ======== >>> from sympy.liealgebras.weyl_group import WeylGroup >>> b = WeylGroup("B4") >>> b.element_order('r1*r4*r2') 4 """ n = self.cartan_type.rank() if self.cartan_type.series == "A": a = self.matrix_form(weylelt) order = 1 while a != eye(n+1): a *= self.matrix_form(weylelt) order += 1 return order if self.cartan_type.series == "D": a = self.matrix_form(weylelt) order = 1 while a != eye(n): a *= self.matrix_form(weylelt) order += 1 return order if self.cartan_type.series == "E": a = self.matrix_form(weylelt) order = 1 while a != eye(8): a *= self.matrix_form(weylelt) order += 1 return order if self.cartan_type.series == "G": elts = list(weylelt) reflections = elts[1::3] m = self.delete_doubles(reflections) while self.delete_doubles(m) != m: m = self.delete_doubles(m) reflections = m if len(reflections) % 2 == 1: return 2 elif len(reflections) == 0: return 1 else: if len(reflections) == 1: return 2 else: m = len(reflections) / 2 lcm = (6 * m)/ igcd(m, 6) order = lcm / m return order if self.cartan_type.series == 'F': a = self.matrix_form(weylelt) order = 1 while a != eye(4): a *= self.matrix_form(weylelt) order += 1 return order if self.cartan_type.series == "B" or self.cartan_type.series == "C": a = self.matrix_form(weylelt) order = 1 while a != eye(n): a *= self.matrix_form(weylelt) order += 1 return order def delete_doubles(self, reflections): """ This is a helper method for determining the order of an element in the Weyl group of G2. It takes a Weyl element and if repeated simple reflections in it, it deletes them. """ counter = 0 copy = list(reflections) for elt in copy: if counter < len(copy)-1: if copy[counter + 1] == elt: del copy[counter] del copy[counter] counter += 1 return copy def matrix_form(self, weylelt): """ This method takes input from the user in the form of products of the generating reflections, and returns the matrix corresponding to the element of the Weyl group. Since each element of the Weyl group is a reflection of some type, there is a corresponding matrix representation. This method uses the standard representation for all the generating reflections. Example ======= >>> from sympy.liealgebras.weyl_group import WeylGroup >>> f = WeylGroup("F4") >>> f.matrix_form('r2*r3') Matrix([ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, -1], [0, 0, 1, 0]]) """ elts = list(weylelt) reflections = elts[1::3] n = self.cartan_type.rank() if self.cartan_type.series == 'A': matrixform = eye(n+1) for elt in reflections: a = int(elt) mat = eye(n+1) mat[a-1, a-1] = 0 mat[a-1, a] = 1 mat[a, a-1] = 1 mat[a, a] = 0 matrixform *= mat return matrixform if self.cartan_type.series == 'D': matrixform = eye(n) for elt in reflections: a = int(elt) mat = eye(n) if a < n: mat[a-1, a-1] = 0 mat[a-1, a] = 1 mat[a, a-1] = 1 mat[a, a] = 0 matrixform *= mat else: mat[n-2, n-1] = -1 mat[n-2, n-2] = 0 mat[n-1, n-2] = -1 mat[n-1, n-1] = 0 matrixform *= mat return matrixform if self.cartan_type.series == 'G': matrixform = eye(3) for elt in reflections: a = int(elt) if a == 1: gen1 = Matrix([[1, 0, 0], [0, 0, 1], [0, 1, 0]]) matrixform *= gen1 else: gen2 = Matrix([[Rational(2, 3), Rational(2, 3), -Rational(1, 3)], [Rational(2, 3), Rational(-1, 3), Rational(2, 3)], [Rational(-1, 3), Rational(2, 3), Rational(2, 3)]]) matrixform *= gen2 return matrixform if self.cartan_type.series == 'F': matrixform = eye(4) for elt in reflections: a = int(elt) if a == 1: mat = Matrix([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) matrixform *= mat elif a == 2: mat = Matrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]) matrixform *= mat elif a == 3: mat = Matrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, -1]]) matrixform *= mat else: mat = Matrix([[Rational(1, 2), Rational(1, 2), Rational(1, 2), Rational(1, 2)], [Rational(1, 2), Rational(1, 2), Rational(-1, 2), Rational(-1, 2)], [Rational(1, 2), Rational(-1, 2), Rational(1, 2), Rational(-1, 2)], [Rational(1, 2), Rational(-1, 2), Rational(-1, 2), Rational(1, 2)]]) matrixform *= mat return matrixform if self.cartan_type.series == 'E': matrixform = eye(8) for elt in reflections: a = int(elt) if a == 1: mat = Matrix([[Rational(3, 4), Rational(1, 4), Rational(1, 4), Rational(1, 4), Rational(1, 4), Rational(1, 4), Rational(1, 4), Rational(-1, 4)], [Rational(1, 4), Rational(3, 4), Rational(-1, 4), Rational(-1, 4), Rational(-1, 4), Rational(-1, 4), Rational(1, 4), Rational(-1, 4)], [Rational(1, 4), Rational(-1, 4), Rational(3, 4), Rational(-1, 4), Rational(-1, 4), Rational(-1, 4), Rational(-1, 4), Rational(1, 4)], [Rational(1, 4), Rational(-1, 4), Rational(-1, 4), Rational(3, 4), Rational(-1, 4), Rational(-1, 4), Rational(-1, 4), Rational(1, 4)], [Rational(1, 4), Rational(-1, 4), Rational(-1, 4), Rational(-1, 4), Rational(3, 4), Rational(-1, 4), Rational(-1, 4), Rational(1, 4)], [Rational(1, 4), Rational(-1, 4), Rational(-1, 4), Rational(-1, 4), Rational(-1, 4), Rational(3, 4), Rational(-1, 4), Rational(1, 4)], [Rational(1, 4), Rational(-1, 4), Rational(-1, 4), Rational(-1, 4), Rational(-1, 4), Rational(-1, 4), Rational(-3, 4), Rational(1, 4)], [Rational(1, 4), Rational(-1, 4), Rational(-1, 4), Rational(-1, 4), Rational(-1, 4), Rational(-1, 4), Rational(-1, 4), Rational(3, 4)]]) matrixform *= mat elif a == 2: mat = eye(8) mat[0, 0] = 0 mat[0, 1] = -1 mat[1, 0] = -1 mat[1, 1] = 0 matrixform *= mat else: mat = eye(8) mat[a-3, a-3] = 0 mat[a-3, a-2] = 1 mat[a-2, a-3] = 1 mat[a-2, a-2] = 0 matrixform *= mat return matrixform if self.cartan_type.series == 'B' or self.cartan_type.series == 'C': matrixform = eye(n) for elt in reflections: a = int(elt) mat = eye(n) if a == 1: mat[0, 0] = -1 matrixform *= mat else: mat[a - 2, a - 2] = 0 mat[a-2, a-1] = 1 mat[a - 1, a - 2] = 1 mat[a -1, a - 1] = 0 matrixform *= mat return matrixform def coxeter_diagram(self): """ This method returns the Coxeter diagram corresponding to a Weyl group. The Coxeter diagram can be obtained from a Lie algebra's Dynkin diagram by deleting all arrows; the Coxeter diagram is the undirected graph. The vertices of the Coxeter diagram represent the generating reflections of the Weyl group, , s_i. An edge is drawn between s_i and s_j if the order m(i, j) of s_i*s_j is greater than two. If there is one edge, the order m(i, j) is 3. If there are two edges, the order m(i, j) is 4, and if there are three edges, the order m(i, j) is 6. Example ======== >>> from sympy.liealgebras.weyl_group import WeylGroup >>> c = WeylGroup("B3") >>> print(c.coxeter_diagram()) 0---0===0 1 2 3 """ n = self.cartan_type.rank() if self.cartan_type.series == "A" or self.cartan_type.series == "D" or self.cartan_type.series == "E": return self.cartan_type.dynkin_diagram() if self.cartan_type.series == "B" or self.cartan_type.series == "C": diag = "---".join("0" for i in range (1, n)) + "===0\n" diag += " ".join(str(i) for i in range (1, n+1)) return diag if self.cartan_type.series == "F": diag = "0---0===0---0\n" diag += " ".join(str(i) for i in range(1, 5)) return diag if self.cartan_type.series == "G": diag = "0≡≡≡0\n1 2" return diag sympy-0.7.4.1/sympy/liealgebras/dynkin_diagram.py0000644000175000017500000000106112253362407022227 0ustar georgeskgeorgeskfrom .cartan_type import CartanType def DynkinDiagram(t): """ Method for displaying the Dynkin diagram of a given Lie algebra. Works by generating the CartanType for the input, t, and then returning the Dynkin diagram method from the individual classes. Examples ===== >>> from sympy.liealgebras.dynkin_diagram import DynkinDiagram >>> print(DynkinDiagram("A3")) 0---0---0 1 2 3 >>> print(DynkinDiagram("B4")) 0---0---0=>=0 1 2 3 4 """ return CartanType(t).dynkin_diagram() sympy-0.7.4.1/sympy/liealgebras/tests/0000755000175000017500000000000012253362407020041 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/liealgebras/tests/test_type_E.py0000644000175000017500000000140712253362407022701 0ustar georgeskgeorgeskfrom sympy.liealgebras.cartan_type import CartanType from sympy.matrices import Matrix def test_type_E(): c = CartanType("E6") m = Matrix(6, 6, [2, 0, -1, 0, 0, 0, 0, 2, 0, -1, 0, 0, -1, 0, 2, -1, 0, 0, 0, -1, -1, 2, -1, 0, 0, 0, 0, -1, 2, -1, 0, 0, 0, 0, -1, 2]) assert c.cartan_matrix() == m assert c.dimension() == 8 assert c.simple_root(6) == [0, 0, 0, -1, 1, 0, 0, 0] assert c.roots() == 72 assert c.basis() == 78 diag = " "*8 + "2\n" + " "*8 + "0\n" + " "*8 + "|\n" + " "*8 + "|\n" diag += "---".join("0" for i in range(1, 6))+"\n" diag += "1 " + " ".join(str(i) for i in range(3, 7)) assert c.dynkin_diagram() == diag posroots = c.positive_roots() assert posroots[8] == [1, 0, 0, 0, 1, 0, 0, 0] sympy-0.7.4.1/sympy/liealgebras/tests/test_type_D.py0000644000175000017500000000137512253362407022704 0ustar georgeskgeorgeskfrom sympy.liealgebras.cartan_type import CartanType from sympy.matrices import Matrix def test_type_D(): c = CartanType("D4") m = Matrix(4, 4, [2, -1, 0, 0, -1, 2, -1, -1, 0, -1, 2, 0, 0, -1, 0 , 2]) assert c.cartan_matrix() == m assert c.basis() == 6 assert c.lie_algebra() == "so(8)" assert c.roots() == 24 assert c.simple_root(3) == [0, 0, 1, -1] diag = " 3\n 0\n |\n |\n0---0---0\n1 2 4" assert diag == c.dynkin_diagram() assert c.positive_roots() == {1: [1, -1, 0, 0], 2: [1, 1, 0, 0], 3: [1, 0, -1, 0], 4: [1, 0, 1, 0], 5: [1, 0, 0, -1], 6: [1, 0, 0, 1], 7: [0, 1, -1, 0], 8: [0, 1, 1, 0], 9: [0, 1, 0, -1], 10: [0, 1, 0, 1], 11: [0, 0, 1, -1], 12: [0, 0, 1, 1]} sympy-0.7.4.1/sympy/liealgebras/tests/test_cartan_type.py0000644000175000017500000000050312253362407023761 0ustar georgeskgeorgeskfrom sympy.liealgebras.cartan_type import CartanType, Standard_Cartan from sympy.matrices import Matrix from sympy.core import Basic def test_Standard_Cartan(): c = CartanType("A4") assert c.rank() == 4 assert c.series == "A" m = Standard_Cartan("A", 2) assert m.rank() == 2 assert c.series == "A" sympy-0.7.4.1/sympy/liealgebras/tests/test_type_F.py0000644000175000017500000000217312253362407022703 0ustar georgeskgeorgeskfrom sympy.liealgebras.cartan_type import CartanType from sympy.matrices import Matrix def test_type_F(): c = CartanType("F4") m = Matrix(4, 4, [2, -1, 0, 0, -1, 2, -2, 0, 0, -1, 2, -1, 0, 0, -1, 2]) assert c.cartan_matrix() == m assert c.dimension() == 4 assert c.simple_root(3) == [0, 0, 0, 1] assert c.simple_root(4) == [-0.5, -0.5, -0.5, -0.5] assert c.roots() == 48 assert c.basis() == 52 diag = "0---0=>=0---0\n" + " ".join(str(i) for i in range(1, 5)) assert c.dynkin_diagram() == diag assert c.positive_roots() == {1: [1, -1, 0, 0], 2: [1, 1, 0, 0], 3: [1, 0, -1, 0], 4: [1, 0, 1, 0], 5: [1, 0, 0, -1], 6: [1, 0, 0, 1], 7: [0, 1, -1, 0], 8: [0, 1, 1, 0], 9: [0, 1, 0, -1], 10: [0, 1, 0, 1], 11: [0, 0, 1, -1], 12: [0, 0, 1, 1], 13: [1, 0, 0, 0], 14: [0, 1, 0, 0], 15: [0, 0, 1, 0], 16: [0, 0, 0, 1], 17: [1/2, 1/2, 1/2, 1/2], 18: [1/2, -1/2, 1/2, 1/2], 19: [1/2, 1/2, -1/2, 1/2], 20: [1/2, 1/2, 1/2, -1/2], 21: [1/2, 1/2, -1/2, -1/2], 22: [1/2, -1/2, 1/2, -1/2], 23: [1/2, -1/2, -1/2, 1/2], 24: [1/2, -1/2, -1/2, -1/2]} sympy-0.7.4.1/sympy/liealgebras/tests/test_weyl_group.py0000644000175000017500000000306312253362407023650 0ustar georgeskgeorgeskfrom sympy.liealgebras.weyl_group import WeylGroup from sympy.liealgebras.type_a import TypeA from sympy.liealgebras.type_b import TypeB from sympy.matrices import Matrix def test_weyl_group(): c = WeylGroup("A3") assert c.matrix_form('r1*r2') == Matrix([[0, 0, 1, 0], [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) assert c.generators() == ['r1', 'r2', 'r3'] assert c.group_order() == 24.0 assert c.group_name() == "S4: the symmetric group acting on 4 elements." assert c.coxeter_diagram() == "0---0---0\n1 2 3" assert c.element_order('r1*r2*r3') == 4 assert c.element_order('r1*r3*r2*r3') == 3 d = WeylGroup("B5") assert d.group_order() == 3840 assert d.element_order('r1*r2*r4*r5') == 12 assert d.matrix_form('r2*r3') == Matrix([[0, 0, 1, 0, 0], [1, 0, 0, 0, 0], [0, 1, 0, 0, 0], [0, 0, 0, 1, 0], [0, 0, 0, 0, 1]]) assert d.element_order('r1*r2*r1*r3*r5') == 6 e = WeylGroup("D5") assert e.element_order('r2*r3*r5') == 4 assert e.matrix_form('r2*r3*r5') == Matrix([[1, 0, 0, 0, 0], [0, 0, 0, 0, -1], [0, 1, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, -1, 0]]) f = WeylGroup("G2") assert f.element_order('r1*r2*r1*r2') == 3 assert f.element_order('r2*r1*r1*r2') == 1 assert f.matrix_form('r1*r2*r1*r2') == Matrix([[0, 1, 0], [0, 0, 1], [1, 0, 0]]) g = WeylGroup("F4") assert g.matrix_form('r2*r3') == Matrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, -1], [0, 0, 1, 0]]) assert g.element_order('r2*r3') == 4 h = WeylGroup("E6") assert h.group_order() == 51840 sympy-0.7.4.1/sympy/liealgebras/tests/test_type_C.py0000644000175000017500000000163712253362407022704 0ustar georgeskgeorgeskfrom sympy.liealgebras.cartan_type import CartanType from sympy.matrices import Matrix def test_type_C(): c = CartanType("C4") m = Matrix(4, 4, [2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2, -1, 0, 0, -2, 2]) assert c.cartan_matrix() == m assert c.dimension() == 4 assert c.simple_root(4) == [0, 0, 0, 2] assert c.roots() == 32 assert c.basis() == 36 assert c.lie_algebra() == "sp(8)" t = CartanType(['C', 3]) assert t.dimension() == 3 diag = "0---0---0=<=0\n1 2 3 4" assert c.dynkin_diagram() == diag assert c.positive_roots() == {1: [1, -1, 0, 0], 2: [1, 1, 0, 0], 3: [1, 0, -1, 0], 4: [1, 0, 1, 0], 5: [1, 0, 0, -1], 6: [1, 0, 0, 1], 7: [0, 1, -1, 0], 8: [0, 1, 1, 0], 9: [0, 1, 0, -1], 10: [0, 1, 0, 1], 11: [0, 0, 1, -1], 12: [0, 0, 1, 1], 13: [2, 0, 0, 0], 14: [0, 2, 0, 0], 15: [0, 0, 2, 0], 16: [0, 0, 0, 2]} sympy-0.7.4.1/sympy/liealgebras/tests/test_type_B.py0000644000175000017500000000120212253362407022667 0ustar georgeskgeorgeskfrom sympy.liealgebras.cartan_type import CartanType from sympy.matrices import Matrix def test_type_B(): c = CartanType("B3") m = Matrix(3, 3, [2, -1, 0, -1, 2, -2, 0, -1, 2]) assert m == c.cartan_matrix() assert c.dimension() == 3 assert c.roots() == 18 assert c.simple_root(3) == [0, 0, 1] assert c.basis() == 3 assert c.lie_algebra() == "so(6)" diag = "0---0=>=0\n1 2 3" assert c.dynkin_diagram() == diag assert c.positive_roots() == {1: [1, -1, 0], 2: [1, 1, 0], 3: [1, 0, -1], 4: [1, 0, 1], 5: [0, 1, -1], 6: [0, 1, 1], 7: [1, 0, 0], 8: [0, 1, 0], 9: [0, 0, 1]} sympy-0.7.4.1/sympy/liealgebras/tests/test_type_A.py0000644000175000017500000000122112253362407022667 0ustar georgeskgeorgeskfrom sympy.liealgebras.cartan_type import CartanType from sympy.matrices import Matrix def test_type_A(): c = CartanType("A3") m = Matrix(3, 3, [2, -1, 0, -1, 2, -1, 0, -1, 2]) assert m == c.cartan_matrix() assert c.basis() == 8 assert c.roots() == 12 assert c.dimension() == 4 assert c.simple_root(1) == [1, -1, 0, 0] assert c.highest_root() == [1, 0, 0, -1] assert c.lie_algebra() == "su(4)" diag = "0---0---0\n1 2 3" assert c.dynkin_diagram() == diag assert c.positive_roots() == {1: [1, -1, 0, 0], 2: [1, 0, -1, 0], 3: [1, 0, 0, -1], 4: [0, 1, -1, 0], 5: [0, 1, 0, -1], 6: [0, 0, 1, -1]} sympy-0.7.4.1/sympy/liealgebras/tests/test_type_G.py0000644000175000017500000000102512253362407022677 0ustar georgeskgeorgeskfrom sympy.liealgebras.cartan_type import CartanType from sympy.matrices import Matrix def test_type_G(): c = CartanType("G2") m = Matrix(2, 2, [2, -1, -3, 2]) assert c.cartan_matrix() == m assert c.simple_root(2) == [1, -2, 1] assert c.basis() == 14 assert c.roots() == 12 assert c.dimension() == 3 diag = "0≡<≡0\n1 2" assert diag == c.dynkin_diagram() assert c.positive_roots() == {1: [0, 1, -1], 2: [1, -2, 1], 3: [1, -1, 0], 4: [1, 0, 1], 5: [1, 1, -2], 6: [2, -1, -1]} sympy-0.7.4.1/sympy/liealgebras/tests/test_dynkin_diagram.py0000644000175000017500000000040412253362407024430 0ustar georgeskgeorgeskfrom sympy.liealgebras.dynkin_diagram import DynkinDiagram def test_DynkinDiagram(): c = DynkinDiagram("A3") diag = "0---0---0\n1 2 3" assert c == diag ct = DynkinDiagram(["B", 3]) diag2 = "0---0=>=0\n1 2 3" assert ct == diag2 sympy-0.7.4.1/sympy/liealgebras/tests/test_root_system.py0000644000175000017500000000163712253362407024050 0ustar georgeskgeorgeskfrom sympy.liealgebras.root_system import RootSystem from sympy.liealgebras.type_a import TypeA from sympy.matrices import Matrix def test_root_system(): c = RootSystem("A3") assert c.cartan_type == TypeA(3) assert c.simple_roots() == {1: [1, -1, 0, 0], 2: [0, 1, -1, 0], 3: [0, 0, 1, -1]} assert c.root_space() == "alpha[1] + alpha[2] + alpha[3]" assert c.cartan_matrix() == Matrix([[ 2, -1, 0], [-1, 2, -1], [ 0, -1, 2]]) assert c.dynkin_diagram() == "0---0---0\n1 2 3" assert c.add_simple_roots(1, 2) == [1, 0, -1, 0] assert c.all_roots() == {1: [1, -1, 0, 0], 2: [1, 0, -1, 0], 3: [1, 0, 0, -1], 4: [0, 1, -1, 0], 5: [0, 1, 0, -1], 6: [0, 0, 1, -1], 7: [-1, 1, 0, 0], 8: [-1, 0, 1, 0], 9: [-1, 0, 0, 1], 10: [0, -1, 1, 0], 11: [0, -1, 0, 1], 12: [0, 0, -1, 1]} assert c.add_as_roots([1, 0, -1, 0], [0, 0, 1, -1]) == [1, 0, 0, -1] sympy-0.7.4.1/sympy/liealgebras/tests/test_cartan_matrix.py0000644000175000017500000000045712253362407024314 0ustar georgeskgeorgeskfrom sympy.liealgebras.cartan_matrix import CartanMatrix from sympy.matrices import Matrix def test_CartanMatrix(): c = CartanMatrix("A3") m = Matrix(3, 3, [2, -1, 0, -1, 2, -1, 0, -1, 2]) assert c == m a = CartanMatrix(["G",2]) mt = Matrix(2, 2, [2, -1, -3, 2]) assert a == mt sympy-0.7.4.1/sympy/liealgebras/tests/__init__.py0000644000175000017500000000000012253362407022140 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/liealgebras/__init__.py0000644000175000017500000000006512253362407021011 0ustar georgeskgeorgeskfrom sympy.liealgebras.cartan_type import CartanType sympy-0.7.4.1/sympy/conftest.py0000644000175000017500000000266512253362407016635 0ustar georgeskgeorgeskfrom __future__ import print_function, division import sys sys._running_pytest = True import pytest from sympy.core.cache import clear_cache def pytest_report_header(config): from sympy.utilities.misc import ARCH s = "architecture: %s\n" % ARCH from sympy.core.cache import USE_CACHE s += "cache: %s\n" % USE_CACHE from sympy.core.compatibility import GROUND_TYPES, HAS_GMPY version = '' if GROUND_TYPES =='gmpy': if HAS_GMPY == 1: import gmpy elif HAS_GMPY == 2: import gmpy2 as gmpy version = gmpy.version() s += "ground types: %s %s\n" % (GROUND_TYPES, version) return s def pytest_addoption(parser): parser.addoption("--slow", dest="runslow", action="store_true", help="allow slow tests to run") def pytest_configure(config): # register an additional marker config.addinivalue_line("markers", "slow: slow test") def pytest_runtest_setup(item): if not isinstance(item, pytest.Function): return if not item.config.getvalue("runslow") and hasattr(item.obj, 'slow'): pytest.skip("slow test: pass --slow to run") def pytest_terminal_summary(terminalreporter): if (terminalreporter.stats.get('error', None) or terminalreporter.stats.get('failed', None)): terminalreporter.write_sep( ' ', 'DO *NOT* COMMIT!', red=True, bold=True) def pytest_runtest_teardown(): clear_cache() sympy-0.7.4.1/sympy/combinatorics/0000755000175000017500000000000012253362407017261 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/combinatorics/testutil.py0000644000175000017500000002525412253362407021520 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.combinatorics.util import _distribute_gens_by_base from sympy.combinatorics import Permutation rmul = Permutation.rmul 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]) 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) 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 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) 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 sympy-0.7.4.1/sympy/combinatorics/named_groups.py0000644000175000017500000001717612253362407022332 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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 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 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 G._is_nilpotent = True else: G._is_abelian = False G._is_nilpotent = False if n < 5: G._is_solvable = True else: G._is_solvable = False G._degree = n G._is_transitive = True G._is_alt = True return G 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._is_nilpotent = True G._is_solvable = True G._degree = n G._is_transitive = True G._order = n return G 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]) # if n is a power of 2, group is nilpotent if n & (n-1) == 0: G._is_nilpotent = True else: G._is_nilpotent = False G._is_abelian = False G._is_solvable = True G._degree = n G._is_transitive = True G._order = 2*n return G 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 G._is_nilpotent = True else: G._is_abelian = False G._is_nilpotent = False if n < 5: G._is_solvable = True else: G._is_solvable = 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)) sympy-0.7.4.1/sympy/combinatorics/partitions.py0000644000175000017500000004673612253362407022047 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import Basic, C, Dict, sympify from sympy.core.compatibility import as_int, default_sort_key, xrange 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 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 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 list(map(default_sort_key, (self.size, members, self.rank))) @property 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, key=default_sort_key) 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 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 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], key=default_sort_key)]) @classmethod 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 xrange(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) 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 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) 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) 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 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)) 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)) 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 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) Matrix([ [ 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 xrange(0, m + 1): d[0, i] = 1 for i in xrange(1, m + 1): for j in xrange(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 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) 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 xrange(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:]] 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 xrange(1, rgs_size): n = len(rgs[(i + 1):]) m = max(rgs[0:i]) rank += D[n, m + 1] * rgs[i] return rank sympy-0.7.4.1/sympy/combinatorics/subsets.py0000644000175000017500000003646012253362407021334 0ustar georgeskgeorgeskfrom __future__ import print_function, division from itertools import combinations from sympy.core import Basic from sympy.combinatorics.graycode import GrayCode from sympy.core.compatibility import xrange 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 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) 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) 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) def next_lexicographic(self): """ Generates the next lexicographically ordered subset. Examples ======== >>> from sympy.combinatorics.subsets import Subset >>> a = Subset(['c','d'], ['a','b','c','d']) >>> a.next_lexicographic().subset ['d'] >>> a = Subset(['d'], ['a','b','c','d']) >>> a.next_lexicographic().subset [] See Also ======== prev_lexicographic """ i = self.superset_size - 1 indices = Subset.subset_indices(self.subset, self.superset) if i in indices: if i - 1 in indices: indices.remove(i - 1) else: indices.remove(i) i = i - 1 while not i in indices and i >= 0: i = i - 1 if i >= 0: indices.remove(i) indices.append(i+1) else: while i not in indices and i >= 0: i = i - 1 indices.append(i + 1) ret_set = [] super_set = self.superset for i in indices: ret_set.append(super_set[i]) return Subset(ret_set, super_set) def prev_lexicographic(self): """ Generates the previous lexicographically ordered subset. Examples ======== >>> from sympy.combinatorics.subsets import Subset >>> a = Subset([], ['a','b','c','d']) >>> a.prev_lexicographic().subset ['d'] >>> a = Subset(['c','d'], ['a','b','c','d']) >>> a.prev_lexicographic().subset ['c'] See Also ======== next_lexicographic """ i = self.superset_size - 1 indices = Subset.subset_indices(self.subset, self.superset) while i not in indices and i >= 0: i = i - 1 if i - 1 in indices or i == 0: indices.remove(i) else: if i >= 0: indices.remove(i) indices.append(i - 1) indices.append(self.superset_size - 1) ret_set = [] super_set = self.superset for i in indices: ret_set.append(super_set[i]) return Subset(ret_set, super_set) 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) 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) 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 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 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 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 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 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 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 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 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 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 xrange(len(bitlist)): if bitlist[i] == '1': ret_set.append(super_set[i]) return Subset(ret_set, super_set) @classmethod 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 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 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 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) sympy-0.7.4.1/sympy/combinatorics/util.py0000644000175000017500000004046612253362407020622 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.ntheory import isprime from sympy.combinatorics.permutations import Permutation, _af_invert, _af_rmul from sympy.core.compatibility import xrange rmul = Permutation.rmul _af_new = Permutation._af_new ############################################ ### ### Utilities for computational group theory ### ############################################ 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>> 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 xrange(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 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 xrange(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 xrange(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 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 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 xrange(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 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 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 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 sympy-0.7.4.1/sympy/combinatorics/tensor_can.py0000644000175000017500000011765712253362407022007 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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 Parameters ========== dummies : list of dummy indices `dummies[2k], dummies[2k+1]` are paired indices sym : symmetry under interchange of contracted dummies:: * None no symmetry * 0 commuting * 1 anticommuting 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(range(2, 8), 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 is not 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([list(range(2, 8))], [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] 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([_ is not 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] is not 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 in the free indices ``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 a variation of the one 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]`` In particular ``a`` is position ``0``, ``b`` is in position ``9``. Use the slot symmetries to get `T` is a form which is the minimal in lexicographic order in the free indices ``a`` and ``b``, e.g. ``-R^{a}_{d0}^{d1,d2}*R^{b,d0}_{d2,d1}`` corresponding to ``[0, 3, 4, 6, 1, 2, 7, 5, 9, 8]`` >>> 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, 7, 5, 9, 8] """ g = g.array_form size = len(g) if not base: return g[:] transversals = get_transversals(base, gens) m = len(base) for x in sorted(g[:-2]): if x not in base: base.append(x) h = g for i, transv in enumerate(transversals): b = base[i] h_i = [size]*num_free # find the element s in transversals[i] such that # _af_rmul(h, s) has its free elements with the lowest position in h s = None for sk in transv.values(): h1 = _af_rmul(h, sk) hi = [h1.index(ix) for ix in range(num_free)] if hi < h_i: h_i = hi s = sk if s: h = _af_rmul(h, s) return h 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 def canonicalize(g, dummies, msym, *v): """ canonicalize tensor formed by tensors Parameters ========== 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 : list, (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 of 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 Returns ======= 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, 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, 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 = [range(2, 10), 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 and not isinstance(msym, list): 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 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 = list(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] def get_symmetric_group_sgs(n, antisym=False): """ Return base, gens of the minimal BSGS for (anti)symmetric tensor ``n`` rank of the tensor ``antisym = False`` symmetric tensor ``antisym = True`` antisymmetric tensor 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 antisym == 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 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 is 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)) res_base = list(res_base) # 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 sympy-0.7.4.1/sympy/combinatorics/prufer.py0000644000175000017500000002715412253362407021147 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import Basic from sympy.core.compatibility import iterable, as_int, xrange from sympy.utilities.iterables import flatten from collections import defaultdict 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 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 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 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 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 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 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 xrange(n - 2): # find the smallest leaf for x in xrange(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 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 xrange(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 xrange(n) if d[i] == 1] or [0, 1] tree.append(last) return tree @staticmethod 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], [1, 2], [1, 3], [3, 4]], 5) Duplicate edges are removed: >>> Prufer.edges([0, 1, 2, 3], [1, 4, 5], [1, 4, 6]) # a K ([[0, 1], [1, 2], [1, 4], [2, 3], [4, 5], [4, 6]], 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 for ei in e: 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 sorted(rv), nmax + 1 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 xrange(self.nodes - 3, -1, -1): r += p*self.prufer_repr[i] p *= self.nodes return r @classmethod 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 xrange(n - 3, -1, -1): L[i] = rank % n rank = (rank - L[i])//n return Prufer([L[i] for i in xrange(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 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) 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) sympy-0.7.4.1/sympy/combinatorics/perm_groups.py0000644000175000017500000035206112253362407022204 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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 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 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 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 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 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 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 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) 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])) 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 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 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 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 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 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 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 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) 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) 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 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 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 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 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 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 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 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 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 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) 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 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 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 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 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 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])) 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) 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))) 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) 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) 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 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) 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 def random(self, af=False): """Return a random group element """ rank = randrange(self.order()) return self.coset_unrank(rank, af) 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]) 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) 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] 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 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 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 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 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 def subgroup_search(self, prop, base=None, strong_gens=None, tests=None, init_subgroup=None): """Find the subgroup of all elements satisfying the property ``prop``. This is done by a depth-first search with respect to base images that uses several tests to prune the search tree. Parameters ========== prop The property to be used. Has to be callable on group elements and always return ``True`` or ``False``. It is assumed that all group elements satisfying ``prop`` indeed form a subgroup. base A base for the supergroup. strong_gens A strong generating set for the supergroup. tests A list of callables of length equal to the length of ``base``. These are used to rule out group elements by partial base images, so that ``tests[l](g)`` returns False if the element ``g`` is known not to satisfy prop base on where g sends the first ``l + 1`` base points. init_subgroup if a subgroup of the sought group is known in advance, it can be passed to the function as this parameter. Returns ======= res The subgroup of all elements satisfying ``prop``. The generating set for this group is guaranteed to be a strong generating set relative to the base ``base``. Examples ======== >>> from sympy.combinatorics.named_groups import (SymmetricGroup, ... AlternatingGroup) >>> from sympy.combinatorics.perm_groups import PermutationGroup >>> from sympy.combinatorics.testutil import _verify_bsgs >>> S = SymmetricGroup(7) >>> prop_even = lambda x: x.is_even >>> base, strong_gens = S.schreier_sims_incremental() >>> G = S.subgroup_search(prop_even, base=base, strong_gens=strong_gens) >>> G.is_subgroup(AlternatingGroup(7)) True >>> _verify_bsgs(G, base, G.generators) True Notes ===== This function is extremely lenghty and complicated and will require some careful attention. The implementation is described in [1], pp. 114-117, and the comments for the code here follow the lines of the pseudocode in the book for clarity. The complexity is exponential in general, since the search process by itself visits all members of the supergroup. However, there are a lot of tests which are used to prune the search tree, and users can define their own tests via the ``tests`` parameter, so in practice, and for some computations, it's not terrible. A crucial part in the procedure is the frequent base change performed (this is line 11 in the pseudocode) in order to obtain a new basic stabilizer. The book mentiones that this can be done by using ``.baseswap(...)``, however the current imlementation uses a more straightforward way to find the next basic stabilizer - calling the function ``.stabilizer(...)`` on the previous basic stabilizer. """ # initialize BSGS and basic group properties def get_reps(orbits): # get the minimal element in the base ordering return [min(orbit, key = lambda x: base_ordering[x]) \ for orbit in orbits] def update_nu(l): temp_index = len(basic_orbits[l]) + 1 -\ len(res_basic_orbits_init_base[l]) # this corresponds to the element larger than all points if temp_index >= len(sorted_orbits[l]): nu[l] = base_ordering[degree] else: nu[l] = sorted_orbits[l][temp_index] if base is None: base, strong_gens = self.schreier_sims_incremental() base_len = len(base) degree = self.degree identity = _af_new(list(range(degree))) base_ordering = _base_ordering(base, degree) # add an element larger than all points base_ordering.append(degree) # add an element smaller than all points base_ordering.append(-1) # compute BSGS-related structures strong_gens_distr = _distribute_gens_by_base(base, strong_gens) basic_orbits, transversals = _orbits_transversals_from_bsgs(base, strong_gens_distr) # handle subgroup initialization and tests if init_subgroup is None: init_subgroup = PermutationGroup([identity]) if tests is None: trivial_test = lambda x: True tests = [] for i in range(base_len): tests.append(trivial_test) # line 1: more initializations. res = init_subgroup f = base_len - 1 l = base_len - 1 # line 2: set the base for K to the base for G res_base = base[:] # line 3: compute BSGS and related structures for K res_base, res_strong_gens = res.schreier_sims_incremental( base=res_base) res_strong_gens_distr = _distribute_gens_by_base(res_base, res_strong_gens) res_generators = res.generators res_basic_orbits_init_base = \ [_orbit(degree, res_strong_gens_distr[i], res_base[i])\ for i in range(base_len)] # initialize orbit representatives orbit_reps = [None]*base_len # line 4: orbit representatives for f-th basic stabilizer of K orbits = _orbits(degree, res_strong_gens_distr[f]) orbit_reps[f] = get_reps(orbits) # line 5: remove the base point from the representatives to avoid # getting the identity element as a generator for K orbit_reps[f].remove(base[f]) # line 6: more initializations c = [0]*base_len u = [identity]*base_len sorted_orbits = [None]*base_len for i in range(base_len): sorted_orbits[i] = basic_orbits[i][:] sorted_orbits[i].sort(key=lambda point: base_ordering[point]) # line 7: initializations mu = [None]*base_len nu = [None]*base_len # this corresponds to the element smaller than all points mu[l] = degree + 1 update_nu(l) # initialize computed words computed_words = [identity]*base_len # line 8: main loop while True: # apply all the tests while l < base_len - 1 and \ computed_words[l](base[l]) in orbit_reps[l] and \ base_ordering[mu[l]] < \ base_ordering[computed_words[l](base[l])] < \ base_ordering[nu[l]] and \ tests[l](computed_words): # line 11: change the (partial) base of K new_point = computed_words[l](base[l]) res_base[l] = new_point new_stab_gens = _stabilizer(degree, res_strong_gens_distr[l], new_point) res_strong_gens_distr[l + 1] = new_stab_gens # line 12: calculate minimal orbit representatives for the # l+1-th basic stabilizer orbits = _orbits(degree, new_stab_gens) orbit_reps[l + 1] = get_reps(orbits) # line 13: amend sorted orbits l += 1 temp_orbit = [computed_words[l - 1](point) for point in basic_orbits[l]] temp_orbit.sort(key=lambda point: base_ordering[point]) sorted_orbits[l] = temp_orbit # lines 14 and 15: update variables used minimality tests new_mu = degree + 1 for i in range(l): if base[l] in res_basic_orbits_init_base[i]: candidate = computed_words[i](base[i]) if base_ordering[candidate] > base_ordering[new_mu]: new_mu = candidate mu[l] = new_mu update_nu(l) # line 16: determine the new transversal element c[l] = 0 temp_point = sorted_orbits[l][c[l]] gamma = computed_words[l - 1]._array_form.index(temp_point) u[l] = transversals[l][gamma] # update computed words computed_words[l] = rmul(computed_words[l - 1], u[l]) # lines 17 & 18: apply the tests to the group element found g = computed_words[l] temp_point = g(base[l]) if l == base_len - 1 and \ base_ordering[mu[l]] < \ base_ordering[temp_point] < base_ordering[nu[l]] and \ temp_point in orbit_reps[l] and \ tests[l](computed_words) and \ prop(g): # line 19: reset the base of K res_generators.append(g) res_base = base[:] # line 20: recalculate basic orbits (and transversals) res_strong_gens.append(g) res_strong_gens_distr = _distribute_gens_by_base(res_base, res_strong_gens) res_basic_orbits_init_base = \ [_orbit(degree, res_strong_gens_distr[i], res_base[i]) \ for i in range(base_len)] # line 21: recalculate orbit representatives # line 22: reset the search depth orbit_reps[f] = get_reps(orbits) l = f # line 23: go up the tree until in the first branch not fully # searched while l >= 0 and c[l] == len(basic_orbits[l]) - 1: l = l - 1 # line 24: if the entire tree is traversed, return K if l == -1: return PermutationGroup(res_generators) # lines 25-27: update orbit representatives if l < f: # line 26 f = l c[l] = 0 # line 27 temp_orbits = _orbits(degree, res_strong_gens_distr[f]) orbit_reps[f] = get_reps(temp_orbits) # line 28: update variables used for minimality testing mu[l] = degree + 1 temp_index = len(basic_orbits[l]) + 1 - \ len(res_basic_orbits_init_base[l]) if temp_index >= len(sorted_orbits[l]): nu[l] = base_ordering[degree] else: nu[l] = sorted_orbits[l][temp_index] # line 29: set the next element from the current branch and update # accorndingly c[l] += 1 if l == 0: gamma = sorted_orbits[l][c[l]] else: gamma = computed_words[l - 1]._array_form.index(sorted_orbits[l][c[l]]) u[l] = transversals[l][gamma] if l == 0: computed_words[l] = u[l] else: computed_words[l] = rmul(computed_words[l - 1], u[l]) @property 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 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=[range(n), 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=[[], 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 sympy-0.7.4.1/sympy/combinatorics/generators.py0000644000175000017500000002011712253362407022005 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.combinatorics.permutations import Permutation from sympy.utilities.iterables import variations, rotate_left from sympy.core.symbol import symbols from sympy.matrices import Matrix from sympy.core.compatibility import xrange def symmetric(n): """ Generates the symmetric group of order n, Sn. Examples ======== >>> from sympy.combinatorics.permutations import Permutation >>> Permutation.print_cyclic = True >>> from sympy.combinatorics.generators import symmetric >>> list(symmetric(3)) [Permutation(2), Permutation(1, 2), Permutation(2)(0, 1), Permutation(0, 1, 2), Permutation(0, 2, 1), Permutation(0, 2)] """ for perm in variations(list(range(n)), n): yield Permutation(perm) def cyclic(n): """ Generates the cyclic group of order n, Cn. Examples ======== >>> from sympy.combinatorics.permutations import Permutation >>> Permutation.print_cyclic = True >>> from sympy.combinatorics.generators import cyclic >>> list(cyclic(5)) [Permutation(4), Permutation(0, 1, 2, 3, 4), Permutation(0, 2, 4, 1, 3), Permutation(0, 3, 1, 4, 2), Permutation(0, 4, 3, 2, 1)] See Also ======== dihedral """ gen = list(range(n)) for i in xrange(n): yield Permutation(gen) gen = rotate_left(gen, 1) def alternating(n): """ Generates the alternating group of order n, An. Examples ======== >>> from sympy.combinatorics.permutations import Permutation >>> Permutation.print_cyclic = True >>> from sympy.combinatorics.generators import alternating >>> list(alternating(3)) [Permutation(2), Permutation(0, 1, 2), Permutation(0, 2, 1)] """ for perm in variations(list(range(n)), n): p = Permutation(perm) if p.is_even: yield p def dihedral(n): """ Generates the dihedral group of order 2n, Dn. The result is given as a subgroup of Sn, except for the special cases n=1 (the group S2) and n=2 (the Klein 4-group) where that's not possible and embeddings in S2 and S4 respectively are given. Examples ======== >>> from sympy.combinatorics.permutations import Permutation >>> Permutation.print_cyclic = True >>> from sympy.combinatorics.generators import dihedral >>> list(dihedral(3)) [Permutation(2), Permutation(0, 2), Permutation(0, 1, 2), Permutation(1, 2), Permutation(0, 2, 1), Permutation(2)(0, 1)] See Also ======== cyclic """ if n == 1: yield Permutation([0, 1]) yield Permutation([1, 0]) elif n == 2: yield Permutation([0, 1, 2, 3]) yield Permutation([1, 0, 3, 2]) yield Permutation([2, 3, 0, 1]) yield Permutation([3, 2, 1, 0]) else: gen = list(range(n)) for i in xrange(n): yield Permutation(gen) yield Permutation(gen[::-1]) gen = rotate_left(gen, 1) def rubik_cube_generators(): """Return the permutations of the 3x3 Rubik's cube, see http://www.gap-system.org/Doc/Examples/rubik.html """ a = [ [(1, 3, 8, 6), (2, 5, 7, 4), (9, 33, 25, 17), (10, 34, 26, 18), (11, 35, 27, 19)], [(9, 11, 16, 14), (10, 13, 15, 12), (1, 17, 41, 40), (4, 20, 44, 37), (6, 22, 46, 35)], [(17, 19, 24, 22), (18, 21, 23, 20), (6, 25, 43, 16), (7, 28, 42, 13), (8, 30, 41, 11)], [(25, 27, 32, 30), (26, 29, 31, 28), (3, 38, 43, 19), (5, 36, 45, 21), (8, 33, 48, 24)], [(33, 35, 40, 38), (34, 37, 39, 36), (3, 9, 46, 32), (2, 12, 47, 29), (1, 14, 48, 27)], [(41, 43, 48, 46), (42, 45, 47, 44), (14, 22, 30, 38), (15, 23, 31, 39), (16, 24, 32, 40)] ] return [Permutation([[i - 1 for i in xi] for xi in x], size=48) for x in a] def rubik(n): """Return permutations for an nxn Rubik's cube. Permutations returned are for rotation of each of the slice from the face up to the last face for each of the 3 sides (in this order): front, right and bottom. Hence, the first n - 1 permutations are for the slices from the front. """ if n < 2: raise ValueError('dimension of cube must be > 1') # 1-based reference to rows and columns in Matrix def getr(f, i): return faces[f].col(n - i) def getl(f, i): return faces[f].col(i - 1) def getu(f, i): return faces[f].row(i - 1) def getd(f, i): return faces[f].row(n - i) def setr(f, i, s): faces[f][:, n - i] = Matrix(n, 1, s) def setl(f, i, s): faces[f][:, i - 1] = Matrix(n, 1, s) def setu(f, i, s): faces[f][i - 1, :] = Matrix(1, n, s) def setd(f, i, s): faces[f][n - i, :] = Matrix(1, n, s) # motion of a single face def cw(F, r=1): for _ in range(r): face = faces[F] rv = [] for c in range(n): for r in range(n - 1, -1, -1): rv.append(face[r, c]) faces[F] = Matrix(n, n, rv) def ccw(F): cw(F, 3) # motion of plane i from the F side; # fcw(0) moves the F face, fcw(1) moves the plane # just behind the front face, etc... def fcw(i, r=1): for _ in range(r): if i == 0: cw(F) i += 1 temp = getr(L, i) setr(L, i, list((getu(D, i)))) setu(D, i, list(reversed(getl(R, i)))) setl(R, i, list((getd(U, i)))) setd(U, i, list(reversed(temp))) i -= 1 def fccw(i): fcw(i, 3) # motion of the entire cube from the F side def FCW(r=1): for _ in range(r): cw(F) ccw(B) cw(U) t = faces[U] cw(L) faces[U] = faces[L] cw(D) faces[L] = faces[D] cw(R) faces[D] = faces[R] faces[R] = t def FCCW(): FCW(3) # motion of the entire cube from the U side def UCW(r=1): for _ in range(r): cw(U) ccw(D) t = faces[F] faces[F] = faces[R] faces[R] = faces[B] faces[B] = faces[L] faces[L] = t def UCCW(): UCW(3) # defining the permutations for the cube U, F, R, B, L, D = names = symbols('U, F, R, B, L, D') # the faces are represented by nxn matrices faces = {} count = 0 for fi in range(6): f = [] for a in range(n**2): f.append(count) count += 1 faces[names[fi]] = Matrix(n, n, f) # this will either return the value of the current permutation # (show != 1) or else append the permutation to the group, g def perm(show=0): # add perm to the list of perms p = [] for f in names: p.extend(faces[f]) if show: return p g.append(Permutation(p)) g = [] # container for the group's permutations I = list(range(6*n**2)) # the identity permutation used for checking # define permutations corresonding to cw rotations of the planes # up TO the last plane from that direction; by not including the # last plane, the orientation of the cube is maintained. # F slices for i in range(n - 1): fcw(i) perm() fccw(i) # restore assert perm(1) == I # R slices # bring R to front UCW() for i in range(n - 1): fcw(i) # put it back in place UCCW() # record perm() # restore # bring face to fron UCW() fccw(i) # restore UCCW() assert perm(1) == I # D slices # bring up bottom FCW() UCCW() FCCW() for i in range(n - 1): # turn strip fcw(i) # put bottom back on the bottom FCW() UCW() FCCW() # record perm() # restore # bring up bottom FCW() UCCW() FCCW() # turn strip fccw(i) # put bottom back on the bottom FCW() UCW() FCCW() assert perm(1) == I return g sympy-0.7.4.1/sympy/combinatorics/graycode.py0000644000175000017500000002570212253362407021436 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import Basic from sympy.core.compatibility import xrange import random 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 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 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 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] 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 xrange(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 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 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 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 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 xrange(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 xrange(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 xrange(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) sympy-0.7.4.1/sympy/combinatorics/permutations.py0000644000175000017500000022710212253362407022371 0ustar georgeskgeorgeskfrom __future__ import print_function, division import random from collections import defaultdict from sympy.core import Basic from sympy.core.compatibility import is_sequence, reduce, xrange 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 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)) 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 self.keys()]): rv[k] = v return rv 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 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) 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(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([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 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[:] 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 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 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 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 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 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 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 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) 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) 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 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(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) 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) 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 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) 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 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) 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 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)) 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 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 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 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 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 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 xrange(self.size)) 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 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 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 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 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 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]) 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)^ 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 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) 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 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 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 See Also ======== sympy.functions.combinatorial.numbers.stirling """ return len(self.full_cyclic_form) 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]]) 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) 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('%s %s %s' % (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 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 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) 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) 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() 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 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 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() 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() 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 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 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 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 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 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 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 sympy-0.7.4.1/sympy/combinatorics/polyhedron.py0000644000175000017500000006677412253362407022042 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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 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(range(len(corners)))] )) return obj @property 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 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 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 def size(self): """ Get the number of corners of the Polyhedron. """ return len(self._corners) @property def faces(self): """ Get the faces of the Polyhedron. """ return self._faces @property def pgroup(self): """ Get the permutations of the Polyhedron. """ return self._pgroup @property 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 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) 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(zip(ordered_faces, 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( 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( 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( 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(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( 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( 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() sympy-0.7.4.1/sympy/combinatorics/group_constructs.py0000644000175000017500000000376012253362407023264 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.combinatorics.perm_groups import PermutationGroup from sympy.combinatorics.permutations import Permutation from sympy.utilities.iterables import uniq from sympy.core.compatibility import xrange _af_new = Permutation._af_new 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 xrange(len(gens_count)): for j in xrange(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) sympy-0.7.4.1/sympy/combinatorics/tests/0000755000175000017500000000000012253362407020423 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/combinatorics/tests/test_permutations.py0000644000175000017500000003400212253362407024565 0ustar georgeskgeorgeskfrom itertools import permutations from sympy.combinatorics.permutations import (Permutation, _af_parity, _af_rmul, _af_rmuln, Cycle) from sympy.utilities.pytest import raises rmul = Permutation.rmul def test_Permutation(): # don't auto fill 0 raises(ValueError, lambda: Permutation([1])) p = Permutation([0, 1, 2, 3]) # call as bijective assert [p(i) for i in range(p.size)] == list(p) # call as operator assert p(list(range(p.size))) == list(p) # call as function assert list(p(1, 2)) == [0, 2, 1, 3] # conversion to list assert list(p) == list(range(4)) assert Permutation(size=4) == Permutation(3) assert Permutation(Permutation(3), size=5) == Permutation(4) # cycle form with size assert Permutation([[1, 2]], size=4) == Permutation([[1, 2], [0], [3]]) # random generation assert Permutation.random(2) in (Permutation([1, 0]), Permutation([0, 1])) p = Permutation([2, 5, 1, 6, 3, 0, 4]) q = Permutation([[1], [0, 3, 5, 6, 2, 4]]) assert len(set([p, p])) == 1 r = Permutation([1, 3, 2, 0, 4, 6, 5]) ans = Permutation(_af_rmuln(*[w.array_form for w in (p, q, r)])).array_form assert rmul(p, q, r).array_form == ans # make sure no other permutation of p, q, r could have given # that answer for a, b, c in permutations((p, q, r)): if (a, b, c) == (p, q, r): continue assert rmul(a, b, c).array_form != ans assert p.support() == list(range(7)) assert q.support() == [0, 2, 3, 4, 5, 6] assert Permutation(p.cyclic_form).array_form == p.array_form assert p.cardinality == 5040 assert q.cardinality == 5040 assert q.cycles == 2 assert rmul(q, p) == Permutation([4, 6, 1, 2, 5, 3, 0]) assert rmul(p, q) == Permutation([6, 5, 3, 0, 2, 4, 1]) assert _af_rmul(p.array_form, q.array_form) == \ [6, 5, 3, 0, 2, 4, 1] assert rmul(Permutation([[1, 2, 3], [0, 4]]), Permutation([[1, 2, 4], [0], [3]])).cyclic_form == \ [[0, 4, 2], [1, 3]] assert q.array_form == [3, 1, 4, 5, 0, 6, 2] assert q.cyclic_form == [[0, 3, 5, 6, 2, 4]] assert q.full_cyclic_form == [[0, 3, 5, 6, 2, 4], [1]] assert p.cyclic_form == [[0, 2, 1, 5], [3, 6, 4]] t = p.transpositions() assert t == [(0, 5), (0, 1), (0, 2), (3, 4), (3, 6)] assert Permutation.rmul(*[Permutation(Cycle(*ti)) for ti in (t)]) assert Permutation([1, 0]).transpositions() == [(0, 1)] assert p**13 == p assert q**0 == Permutation(list(range(q.size))) assert q**-2 == ~q**2 assert q**2 == Permutation([5, 1, 0, 6, 3, 2, 4]) assert q**3 == q**2*q assert q**4 == q**2*q**2 a = Permutation(1, 3) b = Permutation(2, 0, 3) I = Permutation(3) assert ~a == a**-1 assert a*~a == I assert a*b**-1 == a*~b ans = Permutation(0, 5, 3, 1, 6)(2, 4) assert (p + q.rank()).rank() == ans.rank() assert (p + q.rank())._rank == ans.rank() assert (q + p.rank()).rank() == ans.rank() raises(TypeError, lambda: p + Permutation(list(range(10)))) assert (p - q.rank()).rank() == Permutation(0, 6, 3, 1, 2, 5, 4).rank() assert p.rank() - q.rank() < 0 # for coverage: make sure mod is used assert (q - p.rank()).rank() == Permutation(1, 4, 6, 2)(3, 5).rank() assert p*q == Permutation(_af_rmuln(*[list(w) for w in (q, p)])) assert p*Permutation([]) == p assert Permutation([])*p == p assert p*Permutation([[0, 1]]) == Permutation([2, 5, 0, 6, 3, 1, 4]) assert Permutation([[0, 1]])*p == Permutation([5, 2, 1, 6, 3, 0, 4]) pq = p ^ q assert pq == Permutation([5, 6, 0, 4, 1, 2, 3]) assert pq == rmul(q, p, ~q) qp = q ^ p assert qp == Permutation([4, 3, 6, 2, 1, 5, 0]) assert qp == rmul(p, q, ~p) raises(ValueError, lambda: p ^ Permutation([])) assert p.commutator(q) == Permutation(0, 1, 3, 4, 6, 5, 2) assert q.commutator(p) == Permutation(0, 2, 5, 6, 4, 3, 1) assert p.commutator(q) == ~q.commutator(p) raises(ValueError, lambda: p.commutator(Permutation([]))) assert len(p.atoms()) == 7 assert q.atoms() == set([0, 1, 2, 3, 4, 5, 6]) assert p.inversion_vector() == [2, 4, 1, 3, 1, 0] assert q.inversion_vector() == [3, 1, 2, 2, 0, 1] assert Permutation.from_inversion_vector(p.inversion_vector()) == p assert Permutation.from_inversion_vector(q.inversion_vector()).array_form\ == q.array_form raises(ValueError, lambda: Permutation.from_inversion_vector([0, 2])) assert Permutation([i for i in range(500, -1, -1)]).inversions() == 125250 s = Permutation([0, 4, 1, 3, 2]) assert s.parity() == 0 _ = s.cyclic_form # needed to create a value for _cyclic_form assert len(s._cyclic_form) != s.size and s.parity() == 0 assert not s.is_odd assert s.is_even assert Permutation([0, 1, 4, 3, 2]).parity() == 1 assert _af_parity([0, 4, 1, 3, 2]) == 0 assert _af_parity([0, 1, 4, 3, 2]) == 1 s = Permutation([0]) assert s.is_Singleton assert Permutation([]).is_Empty r = Permutation([3, 2, 1, 0]) assert (r**2).is_Identity assert rmul(~p, p).is_Identity assert (~p)**13 == Permutation([5, 2, 0, 4, 6, 1, 3]) assert ~(r**2).is_Identity assert p.max() == 6 assert p.min() == 0 q = Permutation([[6], [5], [0, 1, 2, 3, 4]]) assert q.max() == 4 assert q.min() == 0 p = Permutation([1, 5, 2, 0, 3, 6, 4]) q = Permutation([[1, 2, 3, 5, 6], [0, 4]]) assert p.ascents() == [0, 3, 4] assert q.ascents() == [1, 2, 4] assert r.ascents() == [] assert p.descents() == [1, 2, 5] assert q.descents() == [0, 3, 5] assert Permutation(r.descents()).is_Identity assert p.inversions() == 7 # test the merge-sort with a longer permutation big = list(p) + list(range(p.max() + 1, p.max() + 130)) assert Permutation(big).inversions() == 7 assert p.signature() == -1 assert q.inversions() == 11 assert q.signature() == -1 assert rmul(p, ~p).inversions() == 0 assert rmul(p, ~p).signature() == 1 assert p.order() == 6 assert q.order() == 10 assert (p**(p.order())).is_Identity assert p.length() == 6 assert q.length() == 7 assert r.length() == 4 assert p.runs() == [[1, 5], [2], [0, 3, 6], [4]] assert q.runs() == [[4], [2, 3, 5], [0, 6], [1]] assert r.runs() == [[3], [2], [1], [0]] assert p.index() == 8 assert q.index() == 8 assert r.index() == 3 assert p.get_precedence_distance(q) == q.get_precedence_distance(p) assert p.get_adjacency_distance(q) == p.get_adjacency_distance(q) assert p.get_positional_distance(q) == p.get_positional_distance(q) p = Permutation([0, 1, 2, 3]) q = Permutation([3, 2, 1, 0]) assert p.get_precedence_distance(q) == 6 assert p.get_adjacency_distance(q) == 3 assert p.get_positional_distance(q) == 8 p = Permutation([0, 3, 1, 2, 4]) q = Permutation.josephus(4, 5, 2) assert p.get_adjacency_distance(q) == 3 raises(ValueError, lambda: p.get_adjacency_distance(Permutation([]))) raises(ValueError, lambda: p.get_positional_distance(Permutation([]))) raises(ValueError, lambda: p.get_precedence_distance(Permutation([]))) a = [Permutation.unrank_nonlex(4, i) for i in range(5)] iden = Permutation([0, 1, 2, 3]) for i in range(5): for j in range(i + 1, 5): assert a[i].commutes_with(a[j]) == \ (rmul(a[i], a[j]) == rmul(a[j], a[i])) if a[i].commutes_with(a[j]): assert a[i].commutator(a[j]) == iden assert a[j].commutator(a[i]) == iden a = Permutation(3) b = Permutation(0, 6, 3)(1, 2) assert a.cycle_structure == {1: 4} assert b.cycle_structure == {2: 1, 3: 1, 1: 2} def test_josephus(): assert Permutation.josephus(4, 6, 1) == Permutation([3, 1, 0, 2, 5, 4]) assert Permutation.josephus(1, 5, 1).is_Identity def test_ranking(): assert Permutation.unrank_lex(5, 10).rank() == 10 p = Permutation.unrank_lex(15, 225) assert p.rank() == 225 p1 = p.next_lex() assert p1.rank() == 226 assert Permutation.unrank_lex(15, 225).rank() == 225 assert Permutation.unrank_lex(10, 0).is_Identity p = Permutation.unrank_lex(4, 23) assert p.rank() == 23 assert p.array_form == [3, 2, 1, 0] assert p.next_lex() is None p = Permutation([1, 5, 2, 0, 3, 6, 4]) q = Permutation([[1, 2, 3, 5, 6], [0, 4]]) a = [Permutation.unrank_trotterjohnson(4, i).array_form for i in range(5)] assert a == [[0, 1, 2, 3], [0, 1, 3, 2], [0, 3, 1, 2], [3, 0, 1, 2], [3, 0, 2, 1] ] assert [Permutation(pa).rank_trotterjohnson() for pa in a] == list(range(5)) assert Permutation([0, 1, 2, 3]).next_trotterjohnson() == \ Permutation([0, 1, 3, 2]) assert q.rank_trotterjohnson() == 2283 assert p.rank_trotterjohnson() == 3389 assert Permutation([1, 0]).rank_trotterjohnson() == 1 a = Permutation(list(range(3))) b = a l = [] tj = [] for i in range(6): l.append(a) tj.append(b) a = a.next_lex() b = b.next_trotterjohnson() assert a == b is None assert set([tuple(a) for a in l]) == set([tuple(a) for a in tj]) p = Permutation([2, 5, 1, 6, 3, 0, 4]) q = Permutation([[6], [5], [0, 1, 2, 3, 4]]) assert p.rank() == 1964 assert q.rank() == 870 assert Permutation([]).rank_nonlex() == 0 prank = p.rank_nonlex() assert prank == 1600 assert Permutation.unrank_nonlex(7, 1600) == p qrank = q.rank_nonlex() assert qrank == 41 assert Permutation.unrank_nonlex(7, 41) == Permutation(q.array_form) a = [Permutation.unrank_nonlex(4, i).array_form for i in range(24)] assert a == [ [1, 2, 3, 0], [3, 2, 0, 1], [1, 3, 0, 2], [1, 2, 0, 3], [2, 3, 1, 0], [2, 0, 3, 1], [3, 0, 1, 2], [2, 0, 1, 3], [1, 3, 2, 0], [3, 0, 2, 1], [1, 0, 3, 2], [1, 0, 2, 3], [2, 1, 3, 0], [2, 3, 0, 1], [3, 1, 0, 2], [2, 1, 0, 3], [3, 2, 1, 0], [0, 2, 3, 1], [0, 3, 1, 2], [0, 2, 1, 3], [3, 1, 2, 0], [0, 3, 2, 1], [0, 1, 3, 2], [0, 1, 2, 3]] N = 10 p1 = Permutation(a[0]) for i in range(1, N+1): p1 = p1*Permutation(a[i]) p2 = Permutation.rmul_with_af(*[Permutation(h) for h in a[N::-1]]) assert p1 == p2 ok = [] p = Permutation([1, 0]) for i in range(3): ok.append(p.array_form) p = p.next_nonlex() if p is None: ok.append(None) break assert ok == [[1, 0], [0, 1], None] assert Permutation([3, 2, 0, 1]).next_nonlex() == Permutation([1, 3, 0, 2]) assert [Permutation(pa).rank_nonlex() for pa in a] == list(range(24)) def test_mul(): a, b = [0, 2, 1, 3], [0, 1, 3, 2] assert _af_rmul(a, b) == [0, 2, 3, 1] assert _af_rmuln(a, b, list(range(4))) == [0, 2, 3, 1] assert rmul(Permutation(a), Permutation(b)).array_form == [0, 2, 3, 1] a = Permutation([0, 2, 1, 3]) b = (0, 1, 3, 2) c = (3, 1, 2, 0) assert Permutation.rmul(a, b, c) == Permutation([1, 2, 3, 0]) assert Permutation.rmul(a, c) == Permutation([3, 2, 1, 0]) raises(TypeError, lambda: Permutation.rmul(b, c)) n = 6 m = 8 a = [Permutation.unrank_nonlex(n, i).array_form for i in range(m)] h = list(range(n)) for i in range(m): h = _af_rmul(h, a[i]) h2 = _af_rmuln(*a[:i + 1]) assert h == h2 def test_args(): p = Permutation([(0, 3, 1, 2), (4, 5)]) assert p._cyclic_form is None assert Permutation(p) == p assert p.cyclic_form == [[0, 3, 1, 2], [4, 5]] assert p._array_form == [3, 2, 0, 1, 5, 4] p = Permutation((0, 3, 1, 2)) assert p._cyclic_form is None assert p._array_form == [0, 3, 1, 2] assert Permutation([0]) == Permutation((0, )) assert Permutation([[0], [1]]) == Permutation(((0, ), (1, ))) == \ Permutation(((0, ), [1])) assert Permutation([[1, 2]]) == Permutation([0, 2, 1]) assert Permutation([[1], [4, 2]]) == Permutation([0, 1, 4, 3, 2]) assert Permutation([[1], [4, 2]], size=1) == Permutation([0, 1, 4, 3, 2]) assert Permutation( [[1], [4, 2]], size=6) == Permutation([0, 1, 4, 3, 2, 5]) assert Permutation([], size=3) == Permutation([0, 1, 2]) assert Permutation(3).list(5) == [0, 1, 2, 3, 4] assert Permutation(3).list(-1) == [] assert Permutation(5)(1, 2).list(-1) == [0, 2, 1] assert Permutation(5)(1, 2).list() == [0, 2, 1, 3, 4, 5] raises(TypeError, lambda: Permutation([1, 2], [0])) # enclosing brackets needed raises(ValueError, lambda: Permutation([[1, 2], 0])) # enclosing brackets needed on 0 raises(ValueError, lambda: Permutation([1, 1, 0])) raises(ValueError, lambda: Permutation([[1], [1, 2]])) raises(ValueError, lambda: Permutation([4, 5], size=10)) # where are 0-3? # but this is ok because cycles imply that only those listed moved assert Permutation(4, 5) == Permutation([0, 1, 2, 3, 5, 4]) def test_Cycle(): assert str(Cycle()) == 'Cycle()' assert Cycle(Cycle(1,2)) == Cycle(1, 2) assert Cycle(1,2).copy() == Cycle(1,2) assert list(Cycle(1, 3, 2)) == [0, 3, 1, 2] assert Cycle(1, 2)(2, 3) == Cycle(1, 3, 2) assert Cycle(1, 2)(2, 3)(4, 5) == Cycle(1, 3, 2)(4, 5) assert Permutation(Cycle(1, 2)(2, 1, 0, 3)).cyclic_form, Cycle(0, 2, 1) raises(ValueError, lambda: Cycle().list()) assert Cycle(1, 2).list() == [0, 2, 1] assert Cycle(1, 2).list(4) == [0, 2, 1, 3] assert Permutation(Cycle(1, 2), size=4) == \ Permutation([0, 2, 1, 3]) assert str(Cycle(1, 2)(4, 5)) == 'Cycle(1, 2)(4, 5)' assert str(Cycle(1, 2)) == 'Cycle(1, 2)' assert Cycle(Permutation(list(range(3)))) == Cycle() assert Cycle(1, 2).list() == [0, 2, 1] assert Cycle(1, 2).list(4) == [0, 2, 1, 3] raises(TypeError, lambda: Cycle((1, 2))) raises(ValueError, lambda: Cycle(1, 2, 1)) raises(TypeError, lambda: Cycle(1, 2)*{}) # check round-trip p = Permutation([[1, 2], [4, 3]], size=5) assert Permutation(Cycle(p)) == p def test_from_sequence(): assert Permutation.from_sequence('SymPy') == Permutation(4)(0, 1, 3) assert Permutation.from_sequence('SymPy', key=lambda x: x.lower()) == \ Permutation(4)(0, 2)(1, 3) sympy-0.7.4.1/sympy/combinatorics/tests/test_partitions.py0000644000175000017500000000642212253362407024234 0ustar georgeskgeorgeskfrom sympy.combinatorics.partitions import (Partition, IntegerPartition, RGS_enum, RGS_unrank, RGS_rank, random_integer_partition) from sympy.utilities.pytest import raises from sympy.utilities.iterables import default_sort_key, partitions def test_partition(): from sympy.abc import x raises(ValueError, lambda: Partition(list(range(3)))) raises(ValueError, lambda: Partition([[1, 1, 2]])) a = Partition([[1, 2, 3], [4]]) b = Partition([[1, 2], [3, 4]]) c = Partition([[x]]) l = [a, b, c] l.sort(key=default_sort_key) assert l == [c, a, b] l.sort(key=lambda w: default_sort_key(w, order='rev-lex')) assert l == [c, a, b] assert (a == b) is False assert a <= b assert (a > b) is False assert a != b assert (a + 2).partition == [[1, 2], [3, 4]] assert (b - 1).partition == [[1, 2, 4], [3]] assert (a - 1).partition == [[1, 2, 3, 4]] assert (a + 1).partition == [[1, 2, 4], [3]] assert (b + 1).partition == [[1, 2], [3], [4]] assert a.rank == 1 assert b.rank == 3 assert a.RGS == (0, 0, 0, 1) assert b.RGS == (0, 0, 1, 1) def test_integer_partition(): # no zeros in partition raises(ValueError, lambda: IntegerPartition(list(range(3)))) # check fails since 1 + 2 != 100 raises(ValueError, lambda: IntegerPartition(100, list(range(1, 3)))) a = IntegerPartition(8, [1, 3, 4]) b = a.next_lex() c = IntegerPartition([1, 3, 4]) d = IntegerPartition(8, {1: 3, 3: 1, 2: 1}) assert a == c assert a.integer == d.integer assert a.conjugate == [3, 2, 2, 1] assert (a == b) is False assert a <= b assert (a > b) is False assert a != b for i in range(1, 11): next = set() prev = set() a = IntegerPartition([i]) ans = set([IntegerPartition(p) for p in partitions(i)]) n = len(ans) for j in range(n): next.add(a) a = a.next_lex() IntegerPartition(i, a.partition) # check it by giving i for j in range(n): prev.add(a) a = a.prev_lex() IntegerPartition(i, a.partition) # check it by giving i assert next == ans assert prev == ans assert IntegerPartition([1, 2, 3]).as_ferrers() == '###\n##\n#' assert IntegerPartition([1, 1, 3]).as_ferrers('o') == 'ooo\no\no' assert str(IntegerPartition([1, 1, 3])) == '[3, 1, 1]' assert IntegerPartition([1, 1, 3]).partition == [3, 1, 1] raises(ValueError, lambda: random_integer_partition(-1)) assert random_integer_partition(1) == [1] assert random_integer_partition(10, seed=[1, 3, 2, 1, 5, 1] ) == [5, 2, 1, 1, 1] def test_rgs(): raises(ValueError, lambda: RGS_unrank(-1, 3)) raises(ValueError, lambda: RGS_unrank(3, 0)) raises(ValueError, lambda: RGS_unrank(10, 1)) raises(ValueError, lambda: Partition.from_rgs(list(range(3)), list(range(2)))) raises(ValueError, lambda: Partition.from_rgs(list(range(1, 3)), list(range(2)))) assert RGS_enum(-1) == 0 assert RGS_enum(1) == 1 assert RGS_unrank(7, 5) == [0, 0, 1, 0, 2] assert RGS_unrank(23, 14) == [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 2] assert RGS_rank(RGS_unrank(40, 100)) == 40 sympy-0.7.4.1/sympy/combinatorics/tests/test_util.py0000644000175000017500000001062312253362407023013 0ustar georgeskgeorgeskfrom sympy.combinatorics.named_groups import SymmetricGroup, DihedralGroup,\ AlternatingGroup from sympy.combinatorics.permutations import Permutation from sympy.combinatorics.util import _check_cycles_alt_sym, _strip,\ _distribute_gens_by_base, _strong_gens_from_distr,\ _orbits_transversals_from_bsgs, _handle_precomputed_bsgs, _base_ordering,\ _remove_gens from sympy.combinatorics.testutil import _verify_bsgs def test_check_cycles_alt_sym(): perm1 = Permutation([[0, 1, 2, 3, 4, 5, 6], [7], [8], [9]]) perm2 = Permutation([[0, 1, 2, 3, 4, 5], [6, 7, 8, 9]]) perm3 = Permutation([[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]) assert _check_cycles_alt_sym(perm1) is True assert _check_cycles_alt_sym(perm2) is False assert _check_cycles_alt_sym(perm3) is False def test_strip(): D = DihedralGroup(5) D.schreier_sims() member = Permutation([4, 0, 1, 2, 3]) not_member1 = Permutation([0, 1, 4, 3, 2]) not_member2 = Permutation([3, 1, 4, 2, 0]) identity = Permutation([0, 1, 2, 3, 4]) res1 = _strip(member, D.base, D.basic_orbits, D.basic_transversals) res2 = _strip(not_member1, D.base, D.basic_orbits, D.basic_transversals) res3 = _strip(not_member2, D.base, D.basic_orbits, D.basic_transversals) assert res1[0] == identity assert res1[1] == len(D.base) + 1 assert res2[0] == not_member1 assert res2[1] == len(D.base) + 1 assert res3[0] != identity assert res3[1] == 2 def test_distribute_gens_by_base(): base = [0, 1, 2] gens = [Permutation([0, 1, 2, 3]), Permutation([0, 1, 3, 2]), Permutation([0, 2, 3, 1]), Permutation([3, 2, 1, 0])] assert _distribute_gens_by_base(base, gens) == [gens, [Permutation([0, 1, 2, 3]), Permutation([0, 1, 3, 2]), Permutation([0, 2, 3, 1])], [Permutation([0, 1, 2, 3]), Permutation([0, 1, 3, 2])]] def test_strong_gens_from_distr(): strong_gens_distr = [[Permutation([0, 2, 1]), Permutation([1, 2, 0]), Permutation([1, 0, 2])], [Permutation([0, 2, 1])]] assert _strong_gens_from_distr(strong_gens_distr) == \ [Permutation([0, 2, 1]), Permutation([1, 2, 0]), Permutation([1, 0, 2])] def test_orbits_transversals_from_bsgs(): S = SymmetricGroup(4) S.schreier_sims() base = S.base strong_gens = S.strong_gens strong_gens_distr = _distribute_gens_by_base(base, strong_gens) result = _orbits_transversals_from_bsgs(base, strong_gens_distr) orbits = result[0] transversals = result[1] base_len = len(base) for i in range(base_len): for el in orbits[i]: assert transversals[i][el](base[i]) == el for j in range(i): assert transversals[i][el](base[j]) == base[j] order = 1 for i in range(base_len): order *= len(orbits[i]) assert S.order() == order def test_handle_precomputed_bsgs(): A = AlternatingGroup(5) A.schreier_sims() base = A.base strong_gens = A.strong_gens result = _handle_precomputed_bsgs(base, strong_gens) strong_gens_distr = _distribute_gens_by_base(base, strong_gens) assert strong_gens_distr == result[2] transversals = result[0] orbits = result[1] base_len = len(base) for i in range(base_len): for el in orbits[i]: assert transversals[i][el](base[i]) == el for j in range(i): assert transversals[i][el](base[j]) == base[j] order = 1 for i in range(base_len): order *= len(orbits[i]) assert A.order() == order def test_base_ordering(): base = [2, 4, 5] degree = 7 assert _base_ordering(base, degree) == [3, 4, 0, 5, 1, 2, 6] def test_remove_gens(): S = SymmetricGroup(10) base, strong_gens = S.schreier_sims_incremental() new_gens = _remove_gens(base, strong_gens) assert _verify_bsgs(S, base, new_gens) is True A = AlternatingGroup(7) base, strong_gens = A.schreier_sims_incremental() new_gens = _remove_gens(base, strong_gens) assert _verify_bsgs(A, base, new_gens) is True D = DihedralGroup(2) base, strong_gens = D.schreier_sims_incremental() new_gens = _remove_gens(base, strong_gens) assert _verify_bsgs(D, base, new_gens) is True sympy-0.7.4.1/sympy/combinatorics/tests/test_polyhedron.py0000644000175000017500000000620212253362407024217 0ustar georgeskgeorgeskfrom sympy import symbols, FiniteSet from sympy.combinatorics.polyhedron import (Polyhedron, tetrahedron, cube as square, octahedron, dodecahedron, icosahedron, cube_faces) from sympy.combinatorics.permutations import Permutation from sympy.combinatorics.perm_groups import PermutationGroup from sympy.utilities.pytest import raises rmul = Permutation.rmul def test_polyhedron(): raises(ValueError, lambda: Polyhedron(list('ab'), pgroup=[Permutation([0])])) pgroup = [Permutation([[0, 7, 2, 5], [6, 1, 4, 3]]), Permutation([[0, 7, 1, 6], [5, 2, 4, 3]]), Permutation([[3, 6, 0, 5], [4, 1, 7, 2]]), Permutation([[7, 4, 5], [1, 3, 0], [2], [6]]), Permutation([[1, 3, 2], [7, 6, 5], [4], [0]]), Permutation([[4, 7, 6], [2, 0, 3], [1], [5]]), Permutation([[1, 2, 0], [4, 5, 6], [3], [7]]), Permutation([[4, 2], [0, 6], [3, 7], [1, 5]]), Permutation([[3, 5], [7, 1], [2, 6], [0, 4]]), Permutation([[2, 5], [1, 6], [0, 4], [3, 7]]), Permutation([[4, 3], [7, 0], [5, 1], [6, 2]]), Permutation([[4, 1], [0, 5], [6, 2], [7, 3]]), Permutation([[7, 2], [3, 6], [0, 4], [1, 5]]), Permutation([0, 1, 2, 3, 4, 5, 6, 7])] corners = tuple(symbols('A:H')) faces = cube_faces cube = Polyhedron(corners, faces, pgroup) assert cube.edges == FiniteSet(*( (0, 1), (6, 7), (1, 2), (5, 6), (0, 3), (2, 3), (4, 7), (4, 5), (3, 7), (1, 5), (0, 4), (2, 6))) for i in range(3): # add 180 degree face rotations cube.rotate(cube.pgroup[i]**2) assert cube.corners == corners for i in range(3, 7): # add 240 degree axial corner rotations cube.rotate(cube.pgroup[i]**2) assert cube.corners == corners cube.rotate(1) raises(ValueError, lambda: cube.rotate(Permutation([0, 1]))) assert cube.corners != corners assert cube.array_form == [7, 6, 4, 5, 3, 2, 0, 1] assert cube.cyclic_form == [[0, 7, 1, 6], [2, 4, 3, 5]] cube.reset() assert cube.corners == corners def check(h, size, rpt, target): assert len(h.faces) + len(h.vertices) - len(h.edges) == 2 assert h.size == size got = set() for p in h.pgroup: # make sure it restores original P = h.copy() hit = P.corners for i in range(rpt): P.rotate(p) if P.corners == hit: break else: print('error in permutation', p.array_form) for i in range(rpt): P.rotate(p) got.add(tuple(P.corners)) c = P.corners f = [[c[i] for i in f] for f in P.faces] assert h.faces == Polyhedron(c, f).faces assert len(got) == target assert PermutationGroup([Permutation(g) for g in got]).is_group() for h, size, rpt, target in zip( (tetrahedron, square, octahedron, dodecahedron, icosahedron), (4, 8, 6, 20, 12), (3, 4, 4, 5, 5), (12, 24, 24, 60, 60)): check(h, size, rpt, target) sympy-0.7.4.1/sympy/combinatorics/tests/test_group_constructs.py0000644000175000017500000000070212253362407025456 0ustar georgeskgeorgeskfrom sympy.combinatorics.group_constructs import DirectProduct from sympy.combinatorics.named_groups import CyclicGroup, DihedralGroup def test_direct_product_n(): C = CyclicGroup(4) D = DihedralGroup(4) G = DirectProduct(C, C, C) assert G.order() == 64 assert G.degree == 12 assert len(G.orbits()) == 3 assert G.is_abelian is True H = DirectProduct(D, C) assert H.order() == 32 assert H.is_abelian is False sympy-0.7.4.1/sympy/combinatorics/tests/test_subsets.py0000644000175000017500000000357612253362407023537 0ustar georgeskgeorgeskfrom sympy.combinatorics import Subset def test_subset(): a = Subset(['c', 'd'], ['a', 'b', 'c', 'd']) assert a.next_binary() == Subset(['b'], ['a', 'b', 'c', 'd']) assert a.prev_binary() == Subset(['c'], ['a', 'b', 'c', 'd']) assert a.next_lexicographic() == Subset(['d'], ['a', 'b', 'c', 'd']) assert a.prev_lexicographic() == Subset(['c'], ['a', 'b', 'c', 'd']) assert a.next_gray() == Subset(['c'], ['a', 'b', 'c', 'd']) assert a.prev_gray() == Subset(['d'], ['a', 'b', 'c', 'd']) assert a.rank_binary == 3 assert a.rank_lexicographic == 14 assert a.rank_gray == 2 assert a.cardinality == 16 a = Subset([2, 5, 7], [1, 2, 3, 4, 5, 6, 7]) assert a.next_binary() == Subset([2, 5, 6], [1, 2, 3, 4, 5, 6, 7]) assert a.prev_binary() == Subset([2, 5], [1, 2, 3, 4, 5, 6, 7]) assert a.next_lexicographic() == Subset([2, 6], [1, 2, 3, 4, 5, 6, 7]) assert a.prev_lexicographic() == Subset([2, 5, 6, 7], [1, 2, 3, 4, 5, 6, 7]) assert a.next_gray() == Subset([2, 5, 6, 7], [1, 2, 3, 4, 5, 6, 7]) assert a.prev_gray() == Subset([2, 5], [1, 2, 3, 4, 5, 6, 7]) assert a.rank_binary == 37 assert a.rank_lexicographic == 93 assert a.rank_gray == 57 assert a.cardinality == 128 superset = ['a', 'b', 'c', 'd'] assert Subset.unrank_binary(4, superset).rank_binary == 4 assert Subset.unrank_gray(10, superset).rank_gray == 10 superset = [1, 2, 3, 4, 5, 6, 7, 8, 9] assert Subset.unrank_binary(33, superset).rank_binary == 33 assert Subset.unrank_gray(25, superset).rank_gray == 25 a = Subset([], ['a', 'b', 'c', 'd']) i = 1 while a.subset != Subset(['d'], ['a', 'b', 'c', 'd']).subset: a = a.next_lexicographic() i = i + 1 assert i == 16 i = 1 while a.subset != Subset([], ['a', 'b', 'c', 'd']).subset: a = a.prev_lexicographic() i = i + 1 assert i == 16 sympy-0.7.4.1/sympy/combinatorics/tests/test_perm_groups.py0000644000175000017500000005674412253362407024416 0ustar georgeskgeorgeskfrom sympy.combinatorics.perm_groups import PermutationGroup from sympy.combinatorics.named_groups import SymmetricGroup, CyclicGroup,\ DihedralGroup, AlternatingGroup, AbelianGroup, RubikGroup from sympy.combinatorics.permutations import Permutation, _af_rmuln from sympy.utilities.pytest import skip, XFAIL from sympy.combinatorics.generators import rubik_cube_generators from sympy.combinatorics.polyhedron import tetrahedron as Tetra, cube from sympy.combinatorics.testutil import _verify_bsgs, _verify_centralizer,\ _verify_normal_closure rmul = Permutation.rmul def test_has(): # return p in self.generators a = Permutation([1, 0]) G = PermutationGroup([a]) assert G.is_abelian a = Permutation([2, 0, 1]) b = Permutation([2, 1, 0]) G = PermutationGroup([a, b]) assert not G.is_abelian G = PermutationGroup([a]) assert G.has(a) assert not G.has(b) a = Permutation([2, 0, 1, 3, 4, 5]) b = Permutation([0, 2, 1, 3, 4]) assert PermutationGroup(a, b).degree == \ PermutationGroup(a, b).degree == 6 def test_generate(): a = Permutation([1, 0]) g = list(PermutationGroup([a]).generate()) assert g == [Permutation([0, 1]), Permutation([1, 0])] assert len(list(PermutationGroup(Permutation((0, 1))).generate())) == 1 g = PermutationGroup([a]).generate(method='dimino') assert list(g) == [Permutation([0, 1]), Permutation([1, 0])] a = Permutation([2, 0, 1]) b = Permutation([2, 1, 0]) G = PermutationGroup([a, b]) g = G.generate() v1 = [p.array_form for p in list(g)] v1.sort() assert v1 == [[0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1], [2, 1, 0]] v2 = list(G.generate(method='dimino', af=True)) assert v1 == sorted(v2) a = Permutation([2, 0, 1, 3, 4, 5]) b = Permutation([2, 1, 3, 4, 5, 0]) g = PermutationGroup([a, b]).generate(af=True) assert len(list(g)) == 360 def test_order(): a = Permutation([2, 0, 1, 3, 4, 5, 6, 7, 8, 9]) b = Permutation([2, 1, 3, 4, 5, 6, 7, 8, 9, 0]) g = PermutationGroup([a, b]) assert g.order() == 1814400 def test_stabilizer(): S = SymmetricGroup(2) H = S.stabilizer(0) assert H.generators == [Permutation(1)] a = Permutation([2, 0, 1, 3, 4, 5]) b = Permutation([2, 1, 3, 4, 5, 0]) G = PermutationGroup([a, b]) G0 = G.stabilizer(0) assert G0.order() == 60 gens_cube = [[1, 3, 5, 7, 0, 2, 4, 6], [1, 3, 0, 2, 5, 7, 4, 6]] gens = [Permutation(p) for p in gens_cube] G = PermutationGroup(gens) G2 = G.stabilizer(2) assert G2.order() == 6 G2_1 = G2.stabilizer(1) v = list(G2_1.generate(af=True)) assert v == [[0, 1, 2, 3, 4, 5, 6, 7], [3, 1, 2, 0, 7, 5, 6, 4]] gens = ( (1, 2, 0, 4, 5, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19), (0, 1, 2, 3, 4, 5, 19, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 7, 17, 18), (0, 1, 2, 3, 4, 5, 6, 7, 9, 18, 16, 11, 12, 13, 14, 15, 8, 17, 10, 19)) gens = [Permutation(p) for p in gens] G = PermutationGroup(gens) G2 = G.stabilizer(2) assert G2.order() == 181440 S = SymmetricGroup(3) assert [G.order() for G in S.basic_stabilizers] == [6, 2] def test_center(): # the center of the dihedral group D_n is of order 2 for even n for i in (4, 6, 10): D = DihedralGroup(i) assert (D.center()).order() == 2 # the center of the dihedral group D_n is of order 1 for odd n>2 for i in (3, 5, 7): D = DihedralGroup(i) assert (D.center()).order() == 1 # the center of an abelian group is the group itself for i in (2, 3, 5): for j in (1, 5, 7): for k in (1, 1, 11): G = AbelianGroup(i, j, k) assert G.center().is_subgroup(G) # the center of a nonabelian simple group is trivial for i in(1, 5, 9): A = AlternatingGroup(i) assert (A.center()).order() == 1 # brute-force verifications D = DihedralGroup(5) A = AlternatingGroup(3) C = CyclicGroup(4) G.is_subgroup(D*A*C) assert _verify_centralizer(G, G) def test_centralizer(): # the centralizer of the trivial group is the entire group S = SymmetricGroup(2) assert S.centralizer(Permutation(list(range(2)))).is_subgroup(S) A = AlternatingGroup(5) assert A.centralizer(Permutation(list(range(5)))).is_subgroup(A) # a centralizer in the trivial group is the trivial group itself triv = PermutationGroup([Permutation([0, 1, 2, 3])]) D = DihedralGroup(4) assert triv.centralizer(D).is_subgroup(triv) # brute-force verifications for centralizers of groups for i in (4, 5, 6): S = SymmetricGroup(i) A = AlternatingGroup(i) C = CyclicGroup(i) D = DihedralGroup(i) for gp in (S, A, C, D): for gp2 in (S, A, C, D): if not gp2.is_subgroup(gp): assert _verify_centralizer(gp, gp2) # verify the centralizer for all elements of several groups S = SymmetricGroup(5) elements = list(S.generate_dimino()) for element in elements: assert _verify_centralizer(S, element) A = AlternatingGroup(5) elements = list(A.generate_dimino()) for element in elements: assert _verify_centralizer(A, element) D = DihedralGroup(7) elements = list(D.generate_dimino()) for element in elements: assert _verify_centralizer(D, element) # verify centralizers of small groups within small groups small = [] for i in (1, 2, 3): small.append(SymmetricGroup(i)) small.append(AlternatingGroup(i)) small.append(DihedralGroup(i)) small.append(CyclicGroup(i)) for gp in small: for gp2 in small: if gp.degree == gp2.degree: assert _verify_centralizer(gp, gp2) def test_coset_rank(): gens_cube = [[1, 3, 5, 7, 0, 2, 4, 6], [1, 3, 0, 2, 5, 7, 4, 6]] gens = [Permutation(p) for p in gens_cube] G = PermutationGroup(gens) i = 0 for h in G.generate(af=True): rk = G.coset_rank(h) assert rk == i h1 = G.coset_unrank(rk, af=True) assert h == h1 i += 1 assert G.coset_unrank(48) == None assert G.coset_unrank(G.coset_rank(gens[0])) == gens[0] def test_coset_factor(): a = Permutation([0, 2, 1]) G = PermutationGroup([a]) c = Permutation([2, 1, 0]) assert not G.coset_factor(c) assert G.coset_rank(c) is None a = Permutation([2,0,1,3,4,5]) b = Permutation([2,1,3,4,5,0]) g = PermutationGroup([a, b]) assert g.order() == 360 d = Permutation([1,0,2,3,4,5]) assert not g.coset_factor(d.array_form) assert not g.contains(d) c = Permutation([1,0,2,3,5,4]) v = g.coset_factor(c, True) tr = g.basic_transversals p = Permutation.rmul(*[tr[i][v[i]] for i in range(len(g.base))]) assert p == c v = g.coset_factor(c) p = Permutation.rmul(*v) assert p == c assert g.contains(c) G = PermutationGroup([Permutation([2,1,0])]) p = Permutation([1,0,2]) assert G.coset_factor(p) == [] def test_orbits(): a = Permutation([2, 0, 1]) b = Permutation([2, 1, 0]) g = PermutationGroup([a, b]) assert g.orbit(0) == set([0, 1, 2]) assert g.orbits() == [set([0, 1, 2])] assert g.is_transitive() and g.is_transitive(strict=False) assert g.orbit_transversal(0) == \ [Permutation( [0, 1, 2]), Permutation([2, 0, 1]), Permutation([1, 2, 0])] assert g.orbit_transversal(0, True) == \ [(0, Permutation([0, 1, 2])), (2, Permutation([2, 0, 1])), (1, Permutation([1, 2, 0]))] a = Permutation(list(range(1, 100)) + [0]) G = PermutationGroup([a]) assert [min(o) for o in G.orbits()] == [0] G = PermutationGroup(rubik_cube_generators()) assert [min(o) for o in G.orbits()] == [0, 1] assert not G.is_transitive() and not G.is_transitive(strict=False) G = PermutationGroup([Permutation(0, 1, 3), Permutation(3)(0, 1)]) assert not G.is_transitive() and G.is_transitive(strict=False) assert PermutationGroup( Permutation(3)).is_transitive(strict=False) is False def test_is_normal(): gens_s5 = [Permutation(p) for p in [[1, 2, 3, 4, 0], [2, 1, 4, 0, 3]]] G1 = PermutationGroup(gens_s5) assert G1.order() == 120 gens_a5 = [Permutation(p) for p in [[1, 0, 3, 2, 4], [2, 1, 4, 3, 0]]] G2 = PermutationGroup(gens_a5) assert G2.order() == 60 assert G2.is_normal(G1) gens3 = [Permutation(p) for p in [[2, 1, 3, 0, 4], [1, 2, 0, 3, 4]]] G3 = PermutationGroup(gens3) assert not G3.is_normal(G1) assert G3.order() == 12 G4 = G1.normal_closure(G3.generators) assert G4.order() == 60 gens5 = [Permutation(p) for p in [[1, 2, 3, 0, 4], [1, 2, 0, 3, 4]]] G5 = PermutationGroup(gens5) assert G5.order() == 24 G6 = G1.normal_closure(G5.generators) assert G6.order() == 120 assert G1.is_subgroup(G6) assert not G1.is_subgroup(G4) assert G2.is_subgroup(G4) def test_eq(): a = [[1, 2, 0, 3, 4, 5], [1, 0, 2, 3, 4, 5], [2, 1, 0, 3, 4, 5], [ 1, 2, 0, 3, 4, 5]] a = [Permutation(p) for p in a + [[1, 2, 3, 4, 5, 0]]] g = Permutation([1, 2, 3, 4, 5, 0]) G1, G2, G3 = [PermutationGroup(x) for x in [a[:2], a[2:4], [g, g**2]]] assert G1.order() == G2.order() == G3.order() == 6 assert G1.is_subgroup(G2) assert not G1.is_subgroup(G3) G4 = PermutationGroup([Permutation([0, 1])]) assert not G1.is_subgroup(G4) assert G4.is_subgroup(G1, 0) assert PermutationGroup(g, g).is_subgroup(PermutationGroup(g)) assert SymmetricGroup(3).is_subgroup(SymmetricGroup(4), 0) assert SymmetricGroup(3).is_subgroup(SymmetricGroup(3)*CyclicGroup(5), 0) assert not CyclicGroup(5).is_subgroup(SymmetricGroup(3)*CyclicGroup(5), 0) assert CyclicGroup(3).is_subgroup(SymmetricGroup(3)*CyclicGroup(5), 0) def test_derived_subgroup(): a = Permutation([1, 0, 2, 4, 3]) b = Permutation([0, 1, 3, 2, 4]) G = PermutationGroup([a, b]) C = G.derived_subgroup() assert C.order() == 3 assert C.is_normal(G) assert C.is_subgroup(G, 0) assert not G.is_subgroup(C, 0) gens_cube = [[1, 3, 5, 7, 0, 2, 4, 6], [1, 3, 0, 2, 5, 7, 4, 6]] gens = [Permutation(p) for p in gens_cube] G = PermutationGroup(gens) C = G.derived_subgroup() assert C.order() == 12 def test_is_solvable(): a = Permutation([1, 2, 0]) b = Permutation([1, 0, 2]) G = PermutationGroup([a, b]) assert G.is_solvable a = Permutation([1, 2, 3, 4, 0]) b = Permutation([1, 0, 2, 3, 4]) G = PermutationGroup([a, b]) assert not G.is_solvable def test_rubik1(): gens = rubik_cube_generators() gens1 = [gens[-1]] + [p**2 for p in gens[1:]] G1 = PermutationGroup(gens1) assert G1.order() == 19508428800 gens2 = [p**2 for p in gens] G2 = PermutationGroup(gens2) assert G2.order() == 663552 assert G2.is_subgroup(G1, 0) C1 = G1.derived_subgroup() assert C1.order() == 4877107200 assert C1.is_subgroup(G1, 0) assert not G2.is_subgroup(C1, 0) G = RubikGroup(2) assert G.order() == 3674160 @XFAIL def test_rubik(): skip('takes too much time') G = PermutationGroup(rubik_cube_generators()) assert G.order() == 43252003274489856000 G1 = PermutationGroup(G[:3]) assert G1.order() == 170659735142400 assert not G1.is_normal(G) G2 = G.normal_closure(G1.generators) assert G2.is_subgroup(G) def test_direct_product(): C = CyclicGroup(4) D = DihedralGroup(4) G = C*C*C assert G.order() == 64 assert G.degree == 12 assert len(G.orbits()) == 3 assert G.is_abelian is True H = D*C assert H.order() == 32 assert H.is_abelian is False def test_orbit_rep(): G = DihedralGroup(6) assert G.orbit_rep(1, 3) in [Permutation([2, 3, 4, 5, 0, 1]), Permutation([4, 3, 2, 1, 0, 5])] H = CyclicGroup(4)*G assert H.orbit_rep(1, 5) is False def test_schreier_vector(): G = CyclicGroup(50) v = [0]*50 v[23] = -1 assert G.schreier_vector(23) == v H = DihedralGroup(8) assert H.schreier_vector(2) == [0, 1, -1, 0, 0, 1, 0, 0] L = SymmetricGroup(4) assert L.schreier_vector(1) == [1, -1, 0, 0] def test_random_pr(): D = DihedralGroup(6) r = 11 n = 3 _random_prec_n = {} _random_prec_n[0] = {'s': 7, 't': 3, 'x': 2, 'e': -1} _random_prec_n[1] = {'s': 5, 't': 5, 'x': 1, 'e': -1} _random_prec_n[2] = {'s': 3, 't': 4, 'x': 2, 'e': 1} D._random_pr_init(r, n, _random_prec_n=_random_prec_n) assert D._random_gens[11] == [0, 1, 2, 3, 4, 5] _random_prec = {'s': 2, 't': 9, 'x': 1, 'e': -1} assert D.random_pr(_random_prec=_random_prec) == \ Permutation([0, 5, 4, 3, 2, 1]) def test_is_alt_sym(): G = DihedralGroup(10) assert G.is_alt_sym() is False S = SymmetricGroup(10) N_eps = 10 _random_prec = {'N_eps': N_eps, 0: Permutation([[2], [1, 4], [0, 6, 7, 8, 9, 3, 5]]), 1: Permutation([[1, 8, 7, 6, 3, 5, 2, 9], [0, 4]]), 2: Permutation([[5, 8], [4, 7], [0, 1, 2, 3, 6, 9]]), 3: Permutation([[3], [0, 8, 2, 7, 4, 1, 6, 9, 5]]), 4: Permutation([[8], [4, 7, 9], [3, 6], [0, 5, 1, 2]]), 5: Permutation([[6], [0, 2, 4, 5, 1, 8, 3, 9, 7]]), 6: Permutation([[6, 9, 8], [4, 5], [1, 3, 7], [0, 2]]), 7: Permutation([[4], [0, 2, 9, 1, 3, 8, 6, 5, 7]]), 8: Permutation([[1, 5, 6, 3], [0, 2, 7, 8, 4, 9]]), 9: Permutation([[8], [6, 7], [2, 3, 4, 5], [0, 1, 9]])} assert S.is_alt_sym(_random_prec=_random_prec) is True A = AlternatingGroup(10) _random_prec = {'N_eps': N_eps, 0: Permutation([[1, 6, 4, 2, 7, 8, 5, 9, 3], [0]]), 1: Permutation([[1], [0, 5, 8, 4, 9, 2, 3, 6, 7]]), 2: Permutation([[1, 9, 8, 3, 2, 5], [0, 6, 7, 4]]), 3: Permutation([[6, 8, 9], [4, 5], [1, 3, 7, 2], [0]]), 4: Permutation([[8], [5], [4], [2, 6, 9, 3], [1], [0, 7]]), 5: Permutation([[3, 6], [0, 8, 1, 7, 5, 9, 4, 2]]), 6: Permutation([[5], [2, 9], [1, 8, 3], [0, 4, 7, 6]]), 7: Permutation([[1, 8, 4, 7, 2, 3], [0, 6, 9, 5]]), 8: Permutation([[5, 8, 7], [3], [1, 4, 2, 6], [0, 9]]), 9: Permutation([[4, 9, 6], [3, 8], [1, 2], [0, 5, 7]])} assert A.is_alt_sym(_random_prec=_random_prec) is False def test_minimal_block(): D = DihedralGroup(6) block_system = D.minimal_block([0, 3]) for i in range(3): assert block_system[i] == block_system[i + 3] S = SymmetricGroup(6) assert S.minimal_block([0, 1]) == [0, 0, 0, 0, 0, 0] assert Tetra.pgroup.minimal_block([0, 1]) == [0, 0, 0, 0] def test_max_div(): S = SymmetricGroup(10) assert S.max_div == 5 def test_is_primitive(): S = SymmetricGroup(5) assert S.is_primitive() is True C = CyclicGroup(7) assert C.is_primitive() is True def test_random_stab(): S = SymmetricGroup(5) _random_el = Permutation([1, 3, 2, 0, 4]) _random_prec = {'rand': _random_el} g = S.random_stab(2, _random_prec=_random_prec) assert g == Permutation([1, 3, 2, 0, 4]) h = S.random_stab(1) assert h(1) == 1 def test_transitivity_degree(): perm = Permutation([1, 2, 0]) C = PermutationGroup([perm]) assert C.transitivity_degree == 1 gen1 = Permutation([1, 2, 0, 3, 4]) gen2 = Permutation([1, 2, 3, 4, 0]) # alternating group of degree 5 Alt = PermutationGroup([gen1, gen2]) assert Alt.transitivity_degree == 3 def test_schreier_sims_random(): assert sorted(Tetra.pgroup.base) == [0, 1] S = SymmetricGroup(3) base = [0, 1] strong_gens = [Permutation([1, 2, 0]), Permutation([1, 0, 2]), Permutation([0, 2, 1])] assert S.schreier_sims_random(base, strong_gens, 5) == (base, strong_gens) D = DihedralGroup(3) _random_prec = {'g': [Permutation([2, 0, 1]), Permutation([1, 2, 0]), Permutation([1, 0, 2])]} base = [0, 1] strong_gens = [Permutation([1, 2, 0]), Permutation([2, 1, 0]), Permutation([0, 2, 1])] assert D.schreier_sims_random([], D.generators, 2, _random_prec=_random_prec) == (base, strong_gens) def test_baseswap(): S = SymmetricGroup(4) S.schreier_sims() base = S.base strong_gens = S.strong_gens assert base == [0, 1, 2] deterministic = S.baseswap(base, strong_gens, 1, randomized=False) randomized = S.baseswap(base, strong_gens, 1) assert deterministic[0] == [0, 2, 1] assert _verify_bsgs(S, deterministic[0], deterministic[1]) is True assert randomized[0] == [0, 2, 1] assert _verify_bsgs(S, randomized[0], randomized[1]) is True def test_schreier_sims_incremental(): identity = Permutation([0, 1, 2, 3, 4]) TrivialGroup = PermutationGroup([identity]) base, strong_gens = TrivialGroup.schreier_sims_incremental(base=[0, 1, 2]) assert _verify_bsgs(TrivialGroup, base, strong_gens) is True S = SymmetricGroup(5) base, strong_gens = S.schreier_sims_incremental(base=[0, 1, 2]) assert _verify_bsgs(S, base, strong_gens) is True D = DihedralGroup(2) base, strong_gens = D.schreier_sims_incremental(base=[1]) assert _verify_bsgs(D, base, strong_gens) is True A = AlternatingGroup(7) gens = A.generators[:] gen0 = gens[0] gen1 = gens[1] gen1 = rmul(gen1, ~gen0) gen0 = rmul(gen0, gen1) gen1 = rmul(gen0, gen1) base, strong_gens = A.schreier_sims_incremental(base=[0, 1], gens=gens) assert _verify_bsgs(A, base, strong_gens) is True C = CyclicGroup(11) gen = C.generators[0] base, strong_gens = C.schreier_sims_incremental(gens=[gen**3]) assert _verify_bsgs(C, base, strong_gens) is True def _subgroup_search(i, j, k): prop_true = lambda x: True prop_fix_points = lambda x: [x(point) for point in points] == points prop_comm_g = lambda x: rmul(x, g) == rmul(g, x) prop_even = lambda x: x.is_even for i in range(i, j, k): S = SymmetricGroup(i) A = AlternatingGroup(i) C = CyclicGroup(i) Sym = S.subgroup_search(prop_true) assert Sym.is_subgroup(S) Alt = S.subgroup_search(prop_even) assert Alt.is_subgroup(A) Sym = S.subgroup_search(prop_true, init_subgroup=C) assert Sym.is_subgroup(S) points = [7] assert S.stabilizer(7).is_subgroup(S.subgroup_search(prop_fix_points)) points = [3, 4] assert S.stabilizer(3).stabilizer(4).is_subgroup( S.subgroup_search(prop_fix_points)) points = [3, 5] fix35 = A.subgroup_search(prop_fix_points) points = [5] fix5 = A.subgroup_search(prop_fix_points) assert A.subgroup_search(prop_fix_points, init_subgroup=fix35 ).is_subgroup(fix5) base, strong_gens = A.schreier_sims_incremental() g = A.generators[0] comm_g = \ A.subgroup_search(prop_comm_g, base=base, strong_gens=strong_gens) assert _verify_bsgs(comm_g, base, comm_g.generators) is True assert [prop_comm_g(gen) is True for gen in comm_g.generators] def test_subgroup_search(): _subgroup_search(10, 15, 2) @XFAIL def test_subgroup_search2(): skip('takes too much time') _subgroup_search(16, 17, 1) def test_normal_closure(): # the normal closure of the trivial group is trivial S = SymmetricGroup(3) identity = Permutation([0, 1, 2]) closure = S.normal_closure(identity) assert closure.is_trivial # the normal closure of the entire group is the entire group A = AlternatingGroup(4) assert A.normal_closure(A).is_subgroup(A) # brute-force verifications for subgroups for i in (3, 4, 5): S = SymmetricGroup(i) A = AlternatingGroup(i) D = DihedralGroup(i) C = CyclicGroup(i) for gp in (A, D, C): assert _verify_normal_closure(S, gp) # brute-force verifications for all elements of a group S = SymmetricGroup(5) elements = list(S.generate_dimino()) for element in elements: assert _verify_normal_closure(S, element) # small groups small = [] for i in (1, 2, 3): small.append(SymmetricGroup(i)) small.append(AlternatingGroup(i)) small.append(DihedralGroup(i)) small.append(CyclicGroup(i)) for gp in small: for gp2 in small: if gp2.is_subgroup(gp, 0) and gp2.degree == gp.degree: assert _verify_normal_closure(gp, gp2) def test_derived_series(): # the derived series of the trivial group consists only of the trivial group triv = PermutationGroup([Permutation([0, 1, 2])]) assert triv.derived_series()[0].is_subgroup(triv) # the derived series for a simple group consists only of the group itself for i in (5, 6, 7): A = AlternatingGroup(i) assert A.derived_series()[0].is_subgroup(A) # the derived series for S_4 is S_4 > A_4 > K_4 > triv S = SymmetricGroup(4) series = S.derived_series() assert series[1].is_subgroup(AlternatingGroup(4)) assert series[2].is_subgroup(DihedralGroup(2)) assert series[3].is_trivial def test_lower_central_series(): # the lower central series of the trivial group consists of the trivial # group triv = PermutationGroup([Permutation([0, 1, 2])]) assert triv.lower_central_series()[0].is_subgroup(triv) # the lower central series of a simple group consists of the group itself for i in (5, 6, 7): A = AlternatingGroup(i) assert A.lower_central_series()[0].is_subgroup(A) # GAP-verified example S = SymmetricGroup(6) series = S.lower_central_series() assert len(series) == 2 assert series[1].is_subgroup(AlternatingGroup(6)) def test_commutator(): # the commutator of the trivial group and the trivial group is trivial S = SymmetricGroup(3) triv = PermutationGroup([Permutation([0, 1, 2])]) assert S.commutator(triv, triv).is_subgroup(triv) # the commutator of the trivial group and any other group is again trivial A = AlternatingGroup(3) assert S.commutator(triv, A).is_subgroup(triv) # the commutator is commutative for i in (3, 4, 5): S = SymmetricGroup(i) A = AlternatingGroup(i) D = DihedralGroup(i) assert S.commutator(A, D).is_subgroup(S.commutator(D, A)) # the commutator of an abelian group is trivial S = SymmetricGroup(7) A1 = AbelianGroup(2, 5) A2 = AbelianGroup(3, 4) triv = PermutationGroup([Permutation([0, 1, 2, 3, 4, 5, 6])]) assert S.commutator(A1, A1).is_subgroup(triv) assert S.commutator(A2, A2).is_subgroup(triv) # examples calculated by hand S = SymmetricGroup(3) A = AlternatingGroup(3) assert S.commutator(A, S).is_subgroup(A) def test_is_nilpotent(): # every abelian group is nilpotent for i in (1, 2, 3): C = CyclicGroup(i) Ab = AbelianGroup(i, i + 2) assert C.is_nilpotent assert Ab.is_nilpotent Ab = AbelianGroup(5, 7, 10) assert Ab.is_nilpotent # A_5 is not solvable and thus not nilpotent assert AlternatingGroup(5).is_nilpotent is False def test_is_trivial(): for i in range(5): triv = PermutationGroup([Permutation(list(range(i)))]) assert triv.is_trivial def test_pointwise_stabilizer(): S = SymmetricGroup(2) stab = S.pointwise_stabilizer([0]) assert stab.generators == [Permutation(1)] S = SymmetricGroup(5) points = [] stab = S for point in (2, 0, 3, 4, 1): stab = stab.stabilizer(point) points.append(point) assert S.pointwise_stabilizer(points).is_subgroup(stab) def test_make_perm(): assert cube.pgroup.make_perm(5, seed=list(range(5))) == \ Permutation([4, 7, 6, 5, 0, 3, 2, 1]) assert cube.pgroup.make_perm(7, seed=list(range(7))) == \ Permutation([6, 7, 3, 2, 5, 4, 0, 1]) sympy-0.7.4.1/sympy/combinatorics/tests/test_graycode.py0000644000175000017500000000417512253362407023640 0ustar georgeskgeorgeskfrom sympy.combinatorics.graycode import (GrayCode, bin_to_gray, random_bitstring, get_subset_from_bitstring, graycode_subsets) def test_graycode(): g = GrayCode(2) got = [] for i in g.generate_gray(): if i.startswith('0'): g.skip() got.append(i) assert got == '00 11 10'.split() a = GrayCode(6) assert a.current == '0'*6 assert a.rank == 0 assert len(list(a.generate_gray())) == 64 codes = ['011001', '011011', '011010', '011110', '011111', '011101', '011100', '010100', '010101', '010111', '010110', '010010', '010011', '010001', '010000', '110000', '110001', '110011', '110010', '110110', '110111', '110101', '110100', '111100', '111101', '111111', '111110', '111010', '111011', '111001', '111000', '101000', '101001', '101011', '101010', '101110', '101111', '101101', '101100', '100100', '100101', '100111', '100110', '100010', '100011', '100001', '100000'] assert list(a.generate_gray(start='011001')) == codes assert list( a.generate_gray(rank=GrayCode(6, start='011001').rank)) == codes assert a.next().current == '000001' assert a.next(2).current == '000011' assert a.next(-1).current == '100000' a = GrayCode(5, start='10010') assert a.rank == 28 a = GrayCode(6, start='101000') assert a.rank == 48 assert GrayCode(6, rank=4).current == '000110' assert GrayCode(6, rank=4).rank == 4 assert [GrayCode(4, start=s).rank for s in GrayCode(4).generate_gray()] == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] a = GrayCode(15, rank=15) assert a.current == '000000000001000' assert bin_to_gray('111') == '100' a = random_bitstring(5) assert type(a) is str assert len(a) == 5 assert all(i in ['0', '1'] for i in a) assert get_subset_from_bitstring( ['a', 'b', 'c', 'd'], '0011') == ['c', 'd'] assert get_subset_from_bitstring('abcd', '1001') == ['a', 'd'] assert list(graycode_subsets(['a', 'b', 'c'])) == \ [[], ['c'], ['b', 'c'], ['b'], ['a', 'b'], ['a', 'b', 'c'], ['a', 'c'], ['a']] sympy-0.7.4.1/sympy/combinatorics/tests/test_tensor_can.py0000644000175000017500000006037412253362407024201 0ustar georgeskgeorgeskfrom sympy.combinatorics.perm_groups import PermutationGroup from sympy.combinatorics.permutations import Permutation, Perm from sympy.combinatorics.tensor_can import (perm_af_direct_product, dummy_sgs, riemann_bsgs, get_symmetric_group_sgs, gens_products, canonicalize, bsgs_direct_product) from sympy.combinatorics.testutil import canonicalize_naive, graph_certificate from sympy.utilities.pytest import skip, XFAIL def test_perm_af_direct_product(): gens1 = [[1,0,2,3], [0,1,3,2]] gens2 = [[1,0]] assert perm_af_direct_product(gens1, gens2, 0) == [[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]] assert [[1, 0, 2, 3, 4, 5, 7, 6], [0, 1, 3, 2, 4, 5, 6, 7], [0, 1, 2, 3, 5, 4, 6, 7]] def test_dummy_sgs(): a = dummy_sgs([1,2], 0, 4) assert a == [[0,2,1,3,4,5]] a = dummy_sgs([2,3,4,5], 0, 8) assert a == [x._array_form for x in [Perm(9)(2,3), Perm(9)(4,5), Perm(9)(2,4)(3,5)]] a = dummy_sgs([2,3,4,5], 1, 8) assert a == [x._array_form for x in [Perm(2,3)(8,9), Perm(4,5)(8,9), Perm(9)(2,4)(3,5)]] def test_get_symmetric_group_sgs(): assert get_symmetric_group_sgs(2) == ([0], [Permutation(3)(0,1)]) assert get_symmetric_group_sgs(2, 1) == ([0], [Permutation(0,1)(2,3)]) assert get_symmetric_group_sgs(3) == ([0,1], [Permutation(4)(0,1), Permutation(4)(1,2)]) assert get_symmetric_group_sgs(3, 1) == ([0,1], [Permutation(0,1)(3,4), Permutation(1,2)(3,4)]) assert get_symmetric_group_sgs(4) == ([0,1,2], [Permutation(5)(0,1), Permutation(5)(1,2), Permutation(5)(2,3)]) assert get_symmetric_group_sgs(4, 1) == ([0,1,2], [Permutation(0,1)(4,5), Permutation(1,2)(4,5), Permutation(2,3)(4,5)]) def test_canonicalize_no_slot_sym(): # cases in which there is no slot symmetry after fixing the # free indices; here and in the following if the symmetry of the # metric is not specified, it is assumed to be symmetric. # If it is not specified, tensors are commuting. # A_d0 * B^d0; g = [1,0, 2,3]; T_c = A^d0*B_d0; can = [0,1,2,3] base1, gens1 = get_symmetric_group_sgs(1) dummies = [0, 1] g = Permutation([1,0,2,3]) can = canonicalize(g, dummies, 0, (base1,gens1,1,0), (base1,gens1,1,0)) assert can == [0,1,2,3] # equivalently can = canonicalize(g, dummies, 0, (base1, gens1, 2, None)) assert can == [0,1,2,3] # with antisymmetric metric; T_c = -A^d0*B_d0; can = [0,1,3,2] can = canonicalize(g, dummies, 1, (base1,gens1,1,0), (base1,gens1,1,0)) assert can == [0,1,3,2] # A^a * B^b; ord = [a,b]; g = [0,1,2,3]; can = g g = Permutation([0,1,2,3]) dummies = [] t0 = t1 = (base1, gens1, 1, 0) can = canonicalize(g, dummies, 0, t0, t1) assert can == [0,1,2,3] # B^b * A^a g = Permutation([1,0,2,3]) can = canonicalize(g, dummies, 0, t0, t1) assert can == [1,0,2,3] # A symmetric # A^{b}_{d0}*A^{d0, a} order a,b,d0,-d0; T_c = A^{a d0}*A{b}_{d0} # g = [1,3,2,0,4,5]; can = [0,2,1,3,4,5] base2, gens2 = get_symmetric_group_sgs(2) dummies = [2,3] g = Permutation([1,3,2,0,4,5]) can = canonicalize(g, dummies, 0, (base2, gens2, 2, 0)) assert can == [0, 2, 1, 3, 4, 5] # with antisymmetric metric can = canonicalize(g, dummies, 1, (base2, gens2, 2, 0)) assert can == [0, 2, 1, 3, 4, 5] # A^{a}_{d0}*A^{d0, b} g = Permutation([0,3,2,1,4,5]) can = canonicalize(g, dummies, 1, (base2, gens2, 2, 0)) assert can == [0, 2, 1, 3, 5, 4] # A, B symmetric # A^b_d0*B^{d0,a}; g=[1,3,2,0,4,5] # T_c = A^{b,d0}*B_{a,d0}; can = [1,2,0,3,4,5] dummies = [2,3] g = Permutation([1,3,2,0,4,5]) can = canonicalize(g, dummies, 0, (base2,gens2,1,0), (base2,gens2,1,0)) assert can == [1,2,0,3,4,5] # same with antisymmetric metric can = canonicalize(g, dummies, 1, (base2,gens2,1,0), (base2,gens2,1,0)) assert can == [1,2,0,3,5,4] # A^{d1}_{d0}*B^d0*C_d1 ord=[d0,-d0,d1,-d1]; g = [2,1,0,3,4,5] # T_c = A^{d0 d1}*B_d0*C_d1; can = [0,2,1,3,4,5] base1, gens1 = get_symmetric_group_sgs(1) base2, gens2 = get_symmetric_group_sgs(2) g = Permutation([2,1,0,3,4,5]) dummies = [0,1,2,3] t0 = (base2, gens2, 1, 0) t1 = t2 = (base1, gens1, 1, 0) can = canonicalize(g, dummies, 0, t0, t1, t2) assert can == [0, 2, 1, 3, 4, 5] # A without symmetry # A^{d1}_{d0}*B^d0*C_d1 ord=[d0,-d0,d1,-d1]; g = [2,1,0,3,4,5] # T_c = A^{d0 d1}*B_d1*C_d0; can = [0,2,3,1,4,5] g = Permutation([2,1,0,3,4,5]) dummies = [0,1,2,3] t0 = ([], [Permutation(list(range(4)))], 1, 0) can = canonicalize(g, dummies, 0, t0, t1, t2) assert can == [0,2,3,1,4,5] # A, B without symmetry # A^{d1}_{d0}*B_{d1}^{d0}; g = [2,1,3,0,4,5] # T_c = A^{d0 d1}*B_{d0 d1}; can = [0,2,1,3,4,5] t0 = t1 = ([], [Permutation(list(range(4)))], 1, 0) dummies = [0,1,2,3] g = Permutation([2,1,3,0,4,5]) can = canonicalize(g, dummies, 0, t0, t1) assert can == [0, 2, 1, 3, 4, 5] # A_{d0}^{d1}*B_{d1}^{d0}; g = [1,2,3,0,4,5] # T_c = A^{d0 d1}*B_{d1 d0}; can = [0,2,3,1,4,5] g = Permutation([1,2,3,0,4,5]) can = canonicalize(g, dummies, 0, t0, t1) assert can == [0,2,3,1,4,5] # A, B, C without symmetry # A^{d1 d0}*B_{a d0}*C_{d1 b} ord=[a,b,d0,-d0,d1,-d1] # g=[4,2,0,3,5,1,6,7] # T_c=A^{d0 d1}*B_{a d1}*C_{d0 b}; can = [2,4,0,5,3,1,6,7] t0 = t1 = t2 = ([], [Permutation(list(range(4)))], 1, 0) dummies = [2,3,4,5] g = Permutation([4,2,0,3,5,1,6,7]) can = canonicalize(g, dummies, 0, t0, t1, t2) assert can == [2,4,0,5,3,1,6,7] # A symmetric, B and C without symmetry # A^{d1 d0}*B_{a d0}*C_{d1 b} ord=[a,b,d0,-d0,d1,-d1] # g=[4,2,0,3,5,1,6,7] # T_c = A^{d0 d1}*B_{a d0}*C_{d1 b}; can = [2,4,0,3,5,1,6,7] t0 = (base2,gens2,1,0) t1 = t2 = ([], [Permutation(list(range(4)))], 1, 0) dummies = [2,3,4,5] g = Permutation([4,2,0,3,5,1,6,7]) can = canonicalize(g, dummies, 0, t0, t1, t2) assert can == [2,4,0,3,5,1,6,7] # A and C symmetric, B without symmetry # A^{d1 d0}*B_{a d0}*C_{d1 b} ord=[a,b,d0,-d0,d1,-d1] # g=[4,2,0,3,5,1,6,7] # T_c = A^{d0 d1}*B_{a d0}*C_{b d1}; can = [2,4,0,3,1,5,6,7] t0 = t2 = (base2,gens2,1,0) t1 = ([], [Permutation(list(range(4)))], 1, 0) dummies = [2,3,4,5] g = Permutation([4,2,0,3,5,1,6,7]) can = canonicalize(g, dummies, 0, t0, t1, t2) assert can == [2,4,0,3,1,5,6,7] # A symmetric, B without symmetry, C antisymmetric # A^{d1 d0}*B_{a d0}*C_{d1 b} ord=[a,b,d0,-d0,d1,-d1] # g=[4,2,0,3,5,1,6,7] # T_c = -A^{d0 d1}*B_{a d0}*C_{b d1}; can = [2,4,0,3,1,5,7,6] t0 = (base2,gens2, 1, 0) t1 = ([], [Permutation(list(range(4)))], 1, 0) base2a, gens2a = get_symmetric_group_sgs(2, 1) t2 = (base2a, gens2a, 1, 0) dummies = [2,3,4,5] g = Permutation([4,2,0,3,5,1,6,7]) can = canonicalize(g, dummies, 0, t0, t1, t2) assert can == [2,4,0,3,1,5,7,6] def test_canonicalize_no_dummies(): base1, gens1 = get_symmetric_group_sgs(1) base2, gens2 = get_symmetric_group_sgs(2) base2a, gens2a = get_symmetric_group_sgs(2, 1) # A commuting # A^c A^b A^a; ord = [a,b,c]; g = [2,1,0,3,4] # T_c = A^a A^b A^c; can = list(range(5)) g = Permutation([2,1,0,3,4]) can = canonicalize(g, [], 0, (base1, gens1, 3, 0)) assert can == list(range(5)) # A anticommuting # A^c A^b A^a; ord = [a,b,c]; g = [2,1,0,3,4] # T_c = -A^a A^b A^c; can = [0,1,2,4,3] g = Permutation([2,1,0,3,4]) can = canonicalize(g, [], 0, (base1, gens1, 3, 1)) assert can == [0,1,2,4,3] # A commuting and symmetric # A^{b,d}*A^{c,a}; ord = [a,b,c,d]; g = [1,3,2,0,4,5] # T_c = A^{a c}*A^{b d}; can = [0,2,1,3,4,5] g = Permutation([1,3,2,0,4,5]) can = canonicalize(g, [], 0, (base2, gens2, 2, 0)) assert can == [0,2,1,3,4,5] # A anticommuting and symmetric # A^{b,d}*A^{c,a}; ord = [a,b,c,d]; g = [1,3,2,0,4,5] # T_c = -A^{a c}*A^{b d}; can = [0,2,1,3,5,4] g = Permutation([1,3,2,0,4,5]) can = canonicalize(g, [], 0, (base2, gens2, 2, 1)) assert can == [0,2,1,3,5,4] # A^{c,a}*A^{b,d} ; g = [2,0,1,3,4,5] # T_c = A^{a c}*A^{b d}; can = [0,2,1,3,4,5] g = Permutation([2,0,1,3,4,5]) can = canonicalize(g, [], 0, (base2, gens2, 2, 1)) assert can == [0,2,1,3,4,5] def test_no_metric_symmetry(): # no metric symmetry # A^d1_d0 * A^d0_d1; ord = [d0,-d0,d1,-d1]; g= [2,1,0,3,4,5] # T_c = A^d0_d1 * A^d1_d0; can = [0,3,2,1,4,5] g = Permutation([2,1,0,3,4,5]) can = canonicalize(g, list(range(4)), None, [[], [Permutation(list(range(4)))], 2, 0]) assert can == [0,3,2,1,4,5] # A^d1_d2 * A^d0_d3 * A^d2_d1 * A^d3_d0 # ord = [d0,-d0,d1,-d1,d2,-d2,d3,-d3] # 0 1 2 3 4 5 6 7 # g = [2,5,0,7,4,3,6,1,8,9] # T_c = A^d0_d1 * A^d1_d0 * A^d2_d3 * A^d3_d2 # can = [0,3,2,1,4,7,6,5,8,9] g = Permutation([2,5,0,7,4,3,6,1,8,9]) #can = canonicalize(g, list(range(8)), 0, [[], [list(range(4))], 4, 0]) #assert can == [0, 2, 3, 1, 4, 6, 7, 5, 8, 9] can = canonicalize(g, list(range(8)), None, [[], [Permutation(list(range(4)))], 4, 0]) assert can == [0, 3, 2, 1, 4, 7, 6, 5, 8, 9] # A^d0_d2 * A^d1_d3 * A^d3_d0 * A^d2_d1 # g = [0,5,2,7,6,1,4,3,8,9] # T_c = A^d0_d1 * A^d1_d2 * A^d2_d3 * A^d3_d0 # can = [0,3,2,5,4,7,6,1,8,9] g = Permutation([0,5,2,7,6,1,4,3,8,9]) can = canonicalize(g, list(range(8)), None, [[], [Permutation(list(range(4)))], 4, 0]) assert can == [0,3,2,5,4,7,6,1,8,9] g = Permutation([12,7,10,3,14,13,4,11,6,1,2,9,0,15,8,5,16,17]) can = canonicalize(g, list(range(16)), None, [[], [Permutation(list(range(4)))], 8, 0]) assert can == [0,3,2,5,4,7,6,1,8,11,10,13,12,15,14,9,16,17] def test_canonical_free(): # t = A^{d0 a1}*A_d0^a0 # ord = [a0,a1,d0,-d0]; g = [2,1,3,0,4,5]; dummies = [[2,3]] # t_c = A_d0^a0*A^{d0 a1} # can = [3,0, 2,1, 4,5] base = [0] gens = [Permutation(5)(0,2)(1,3)] g = Permutation([2,1,3,0,4,5]) num_free = 2 dummies = [[2,3]] can = canonicalize(g, dummies, [None], ([], [Permutation(3)], 2, 0)) assert can == [3,0, 2,1, 4,5] def test_canonicalize1(): base1, gens1 = get_symmetric_group_sgs(1) base1a, gens1a = get_symmetric_group_sgs(1, 1) base2, gens2 = get_symmetric_group_sgs(2) base3, gens3 = get_symmetric_group_sgs(3) base2a, gens2a = get_symmetric_group_sgs(2, 1) base3a, gens3a = get_symmetric_group_sgs(3, 1) # A_d0*A^d0; ord = [d0,-d0]; g = [1,0,2,3] # T_c = A^d0*A_d0; can = [0,1,2,3] g = Permutation([1,0,2,3]) can = canonicalize(g, [0, 1], 0, (base1, gens1, 2, 0)) assert can == list(range(4)) # A commuting # A_d0*A_d1*A_d2*A^d2*A^d1*A^d0; ord=[d0,-d0,d1,-d1,d2,-d2] # g = [1,3,5,4,2,0,6,7] # T_c = A^d0*A_d0*A^d1*A_d1*A^d2*A_d2; can = list(range(8)) g = Permutation([1,3,5,4,2,0,6,7]) can = canonicalize(g, list(range(6)), 0, (base1, gens1, 6, 0)) assert can == list(range(8)) # A anticommuting # A_d0*A_d1*A_d2*A^d2*A^d1*A^d0; ord=[d0,-d0,d1,-d1,d2,-d2] # g = [1,3,5,4,2,0,6,7] # T_c 0; can = 0 g = Permutation([1,3,5,4,2,0,6,7]) can = canonicalize(g, list(range(6)), 0, (base1, gens1, 6, 1)) assert can == 0 can1 = canonicalize_naive(g, list(range(6)), 0, (base1, gens1, 6, 1)) assert can1 == 0 # A commuting symmetric # A^{d0 b}*A^a_d1*A^d1_d0; ord=[a,b,d0,-d0,d1,-d1] # g = [2,1,0,5,4,3,6,7] # T_c = A^{a d0}*A^{b d1}*A_{d0 d1}; can = [0,2,1,4,3,5,6,7] g = Permutation([2,1,0,5,4,3,6,7]) can = canonicalize(g, list(range(2,6)), 0, (base2, gens2, 3, 0)) assert can == [0,2,1,4,3,5,6,7] # A, B commuting symmetric # A^{d0 b}*A^d1_d0*B^a_d1; ord=[a,b,d0,-d0,d1,-d1] # g = [2,1,4,3,0,5,6,7] # T_c = A^{b d0}*A_d0^d1*B^a_d1; can = [1,2,3,4,0,5,6,7] g = Permutation([2,1,4,3,0,5,6,7]) can = canonicalize(g, list(range(2,6)), 0, (base2,gens2,2,0), (base2,gens2,1,0)) assert can == [1,2,3,4,0,5,6,7] # A commuting symmetric # A^{d1 d0 b}*A^{a}_{d1 d0}; ord=[a,b, d0,-d0,d1,-d1] # g = [4,2,1,0,5,3,6,7] # T_c = A^{a d0 d1}*A^{b}_{d0 d1}; can = [0,2,4,1,3,5,6,7] g = Permutation([4,2,1,0,5,3,6,7]) can = canonicalize(g, list(range(2,6)), 0, (base3, gens3, 2, 0)) assert can == [0,2,4,1,3,5,6,7] # A^{d3 d0 d2}*A^a0_{d1 d2}*A^d1_d3^a1*A^{a2 a3}_d0 # ord = [a0,a1,a2,a3,d0,-d0,d1,-d1,d2,-d2,d3,-d3] # 0 1 2 3 4 5 6 7 8 9 10 11 # g = [10,4,8, 0,7,9, 6,11,1, 2,3,5, 12,13] # T_c = A^{a0 d0 d1}*A^a1_d0^d2*A^{a2 a3 d3}*A_{d1 d2 d3} # can = [0,4,6, 1,5,8, 2,3,10, 7,9,11, 12,13] g = Permutation([10,4,8, 0,7,9, 6,11,1, 2,3,5, 12,13]) can = canonicalize(g, list(range(4,12)), 0, (base3, gens3, 4, 0)) assert can == [0,4,6, 1,5,8, 2,3,10, 7,9,11, 12,13] # A commuting symmetric, B antisymmetric # A^{d0 d1 d2} * A_{d2 d3 d1} * B_d0^d3 # ord = [d0,-d0,d1,-d1,d2,-d2,d3,-d3] # g = [0,2,4,5,7,3,1,6,8,9] # in this esxample and in the next three, # renaming dummy indices and using symmetry of A, # T = A^{d0 d1 d2} * A_{d0 d1 d3} * B_d2^d3 # can = 0 g = Permutation([0,2,4,5,7,3,1,6,8,9]) can = canonicalize(g, list(range(8)), 0, (base3, gens3,2,0), (base2a,gens2a,1,0)) assert can == 0 # A anticommuting symmetric, B anticommuting # A^{d0 d1 d2} * A_{d2 d3 d1} * B_d0^d3 # T_c = A^{d0 d1 d2} * A_{d0 d1}^d3 * B_{d2 d3} # can = [0,2,4, 1,3,6, 5,7, 8,9] can = canonicalize(g, list(range(8)), 0, (base3, gens3,2,1), (base2a,gens2a,1,0)) assert can == [0,2,4, 1,3,6, 5,7, 8,9] # A anticommuting symmetric, B antisymmetric commuting, antisymmetric metric # A^{d0 d1 d2} * A_{d2 d3 d1} * B_d0^d3 # T_c = -A^{d0 d1 d2} * A_{d0 d1}^d3 * B_{d2 d3} # can = [0,2,4, 1,3,6, 5,7, 9,8] can = canonicalize(g, list(range(8)), 1, (base3, gens3,2,1), (base2a,gens2a,1,0)) assert can == [0,2,4, 1,3,6, 5,7, 9,8] # A anticommuting symmetric, B anticommuting anticommuting, # no metric symmetry # A^{d0 d1 d2} * A_{d2 d3 d1} * B_d0^d3 # T_c = A^{d0 d1 d2} * A_{d0 d1 d3} * B_d2^d3 # can = [0,2,4, 1,3,7, 5,6, 8,9] can = canonicalize(g, list(range(8)), None, (base3, gens3,2,1), (base2a,gens2a,1,0)) assert can == [0,2,4,1,3,7,5,6,8,9] # Gamma anticommuting # Gamma_{mu nu} * gamma^rho * Gamma^{nu mu alpha} # ord = [alpha, rho, mu,-mu,nu,-nu] # g = [3,5,1,4,2,0,6,7] # T_c = -Gamma^{mu nu} * gamma^rho * Gamma_{alpha mu nu} # can = [2,4,1,0,3,5,7,6]] g = Permutation([3,5,1,4,2,0,6,7]) t0 = (base2a, gens2a, 1, None) t1 = (base1, gens1, 1, None) t2 = (base3a, gens3a, 1, None) can = canonicalize(g, list(range(2, 6)), 0, t0, t1, t2) assert can == [2,4,1,0,3,5,7,6] # Gamma_{mu nu} * Gamma^{gamma beta} * gamma_rho * Gamma^{nu mu alpha} # ord = [alpha, beta, gamma, -rho, mu,-mu,nu,-nu] # 0 1 2 3 4 5 6 7 # g = [5,7,2,1,3,6,4,0,8,9] # T_c = Gamma^{mu nu} * Gamma^{beta gamma} * gamma_rho * Gamma^alpha_{mu nu} # can = [4,6,1,2,3,0,5,7,8,9] t0 = (base2a, gens2a, 2, None) g = Permutation([5,7,2,1,3,6,4,0,8,9]) can = canonicalize(g, list(range(4, 8)), 0, t0, t1, t2) assert can == [4,6,1,2,3,0,5,7,8,9] # f^a_{b,c} antisymmetric in b,c; A_mu^a no symmetry # f^c_{d a} * f_{c e b} * A_mu^d * A_nu^a * A^{nu e} * A^{mu b} # ord = [mu,-mu,nu,-nu,a,-a,b,-b,c,-c,d,-d, e, -e] # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 # g = [8,11,5, 9,13,7, 1,10, 3,4, 2,12, 0,6, 14,15] # T_c = -f^{a b c} * f_a^{d e} * A^mu_b * A_{mu d} * A^nu_c * A_{nu e} # can = [4,6,8, 5,10,12, 0,7, 1,11, 2,9, 3,13, 15,14] g = Permutation([8,11,5, 9,13,7, 1,10, 3,4, 2,12, 0,6, 14,15]) base_f, gens_f = bsgs_direct_product(base1, gens1, base2a, gens2a) 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) can = canonicalize(g, [list(range(4)), list(range(4, 14))], [0, 0], t0, t1) assert can == [4,6,8, 5,10,12, 0,7, 1,11, 2,9, 3,13, 15,14] def test_riemann_invariants(): baser, gensr = riemann_bsgs # R^{d0 d1}_{d1 d0}; ord = [d0,-d0,d1,-d1]; g = [0,2,3,1,4,5] # T_c = -R^{d0 d1}_{d0 d1}; can = [0,2,1,3,5,4] g = Permutation([0,2,3,1,4,5]) can = canonicalize(g, list(range(2, 4)), 0, (baser, gensr, 1, 0)) assert can == [0,2,1,3,5,4] # use a non minimal BSGS can = canonicalize(g, list(range(2, 4)), 0, ([2, 0], [Permutation([1,0,2,3,5,4]), Permutation([2,3,0,1,4,5])], 1, 0)) assert can == [0,2,1,3,5,4] """ The following tests in test_riemann_invariants and in test_riemann_invariants1 have been checked using xperm.c from XPerm in in [1] and with an older version contained in [2] [1] xperm.c part of xPerm written by J. M. Martin-Garcia http://www.xact.es/index.html [2] test_xperm.cc in cadabra by Kasper Peeters, http://cadabra.phi-sci.com/ """ # R_d11^d1_d0^d5 * R^{d6 d4 d0}_d5 * R_{d7 d2 d8 d9} * # R_{d10 d3 d6 d4} * R^{d2 d7 d11}_d1 * R^{d8 d9 d3 d10} # ord: contravariant d_k ->2*k, covariant d_k -> 2*k+1 # T_c = R^{d0 d1 d2 d3} * R_{d0 d1}^{d4 d5} * R_{d2 d3}^{d6 d7} * # R_{d4 d5}^{d8 d9} * R_{d6 d7}^{d10 d11} * R_{d8 d9 d10 d11} g = Permutation([23,2,1,10,12,8,0,11,15,5,17,19,21,7,13,9,4,14,22,3,16,18,6,20,24,25]) can = canonicalize(g, list(range(24)), 0, (baser, gensr, 6, 0)) assert can == [0,2,4,6,1,3,8,10,5,7,12,14,9,11,16,18,13,15,20,22,17,19,21,23,24,25] # use a non minimal BSGS can = canonicalize(g, list(range(24)), 0, ([2, 0], [Permutation([1,0,2,3,5,4]), Permutation([2,3,0,1,4,5])], 6, 0)) assert can == [0,2,4,6,1,3,8,10,5,7,12,14,9,11,16,18,13,15,20,22,17,19,21,23,24,25] g = Permutation([0,2,5,7,4,6,9,11,8,10,13,15,12,14,17,19,16,18,21,23,20,22,25,27,24,26,29,31,28,30,33,35,32,34,37,39,36,38,1,3,40,41]) can = canonicalize(g, list(range(40)), 0, (baser, gensr, 10, 0)) assert can == [0,2,4,6,1,3,8,10,5,7,12,14,9,11,16,18,13,15,20,22,17,19,24,26,21,23,28,30,25,27,32,34,29,31,36,38,33,35,37,39,40,41] @XFAIL def test_riemann_invariants1(): skip('takes too much time') baser, gensr = riemann_bsgs g = Permutation([17, 44, 11, 3, 0, 19, 23, 15, 38, 4, 25, 27, 43, 36, 22, 14, 8, 30, 41, 20, 2, 10, 12, 28, 18, 1, 29, 13, 37, 42, 33, 7, 9, 31, 24, 26, 39, 5, 34, 47, 32, 6, 21, 40, 35, 46, 45, 16, 48, 49]) can = canonicalize(g, list(range(48)), 0, (baser, gensr, 12, 0)) assert can == [0, 2, 4, 6, 1, 3, 8, 10, 5, 7, 12, 14, 9, 11, 16, 18, 13, 15, 20, 22, 17, 19, 24, 26, 21, 23, 28, 30, 25, 27, 32, 34, 29, 31, 36, 38, 33, 35, 40, 42, 37, 39, 44, 46, 41, 43, 45, 47, 48, 49] g = Permutation([0,2,4,6, 7,8,10,12, 14,16,18,20, 19,22,24,26, 5,21,28,30, 32,34,36,38, 40,42,44,46, 13,48,50,52, 15,49,54,56, 17,33,41,58, 9,23,60,62, 29,35,63,64, 3,45,66,68, 25,37,47,57, 11,31,69,70, 27,39,53,72, 1,59,73,74, 55,61,67,76, 43,65,75,78, 51,71,77,79, 80,81]) can = canonicalize(g, list(range(80)), 0, (baser, gensr, 20, 0)) assert can == [0,2,4,6, 1,8,10,12, 3,14,16,18, 5,20,22,24, 7,26,28,30, 9,15,32,34, 11,36,23,38, 13,40,42,44, 17,39,29,46, 19,48,43,50, 21,45,52,54, 25,56,33,58, 27,60,53,62, 31,51,64,66, 35,65,47,68, 37,70,49,72, 41,74,57,76, 55,67,59,78, 61,69,71,75, 63,79,73,77, 80,81] def test_riemann_products(): baser, gensr = riemann_bsgs base1, gens1 = get_symmetric_group_sgs(1) base2, gens2 = get_symmetric_group_sgs(2) base2a, gens2a = get_symmetric_group_sgs(2, 1) # R^{a b d0}_d0 = 0 g = Permutation([0,1,2,3,4,5]) can = canonicalize(g, list(range(2,4)), 0, (baser, gensr, 1, 0)) assert can == 0 # R^{d0 b a}_d0 ; ord = [a,b,d0,-d0}; g = [2,1,0,3,4,5] # T_c = -R^{a d0 b}_d0; can = [0,2,1,3,5,4] g = Permutation([2,1,0,3,4,5]) can = canonicalize(g, list(range(2, 4)), 0, (baser, gensr, 1, 0)) assert can == [0,2,1,3,5,4] # R^d1_d2^b_d0 * R^{d0 a}_d1^d2; ord=[a,b,d0,-d0,d1,-d1,d2,-d2] # g = [4,7,1,3,2,0,5,6,8,9] # T_c = -R^{a d0 d1 d2}* R^b_{d0 d1 d2} # can = [0,2,4,6,1,3,5,7,9,8] g = Permutation([4,7,1,3,2,0,5,6,8,9]) can = canonicalize(g, list(range(2,8)), 0, (baser, gensr, 2, 0)) assert can == [0,2,4,6,1,3,5,7,9,8] can1 = canonicalize_naive(g, list(range(2,8)), 0, (baser, gensr, 2, 0)) assert can == can1 # A symmetric commuting # R^{d6 d5}_d2^d1 * R^{d4 d0 d2 d3} * A_{d6 d0} A_{d3 d1} * A_{d4 d5} # g = [12,10,5,2, 8,0,4,6, 13,1, 7,3, 9,11,14,15] # T_c = -R^{d0 d1 d2 d3} * R_d0^{d4 d5 d6} * A_{d1 d4}*A_{d2 d5}*A_{d3 d6} g = Permutation([12,10,5,2,8,0,4,6,13,1,7,3,9,11,14,15]) can = canonicalize(g, list(range(14)), 0, ((baser,gensr,2,0)), (base2,gens2,3,0)) assert can == [0, 2, 4, 6, 1, 8, 10, 12, 3, 9, 5, 11, 7, 13, 15, 14] # R^{d2 a0 a2 d0} * R^d1_d2^{a1 a3} * R^{a4 a5}_{d0 d1} # ord = [a0,a1,a2,a3,a4,a5,d0,-d0,d1,-d1,d2,-d2] # 0 1 2 3 4 5 6 7 8 9 10 11 # can = [0, 6, 2, 8, 1, 3, 7, 10, 4, 5, 9, 11, 12, 13] # T_c = R^{a0 d0 a2 d1}*R^{a1 a3}_d0^d2*R^{a4 a5}_{d1 d2} g = Permutation([10,0,2,6,8,11,1,3,4,5,7,9,12,13]) can = canonicalize(g, list(range(6,12)), 0, (baser, gensr, 3, 0)) assert can == [0, 6, 2, 8, 1, 3, 7, 10, 4, 5, 9, 11, 12, 13] #can1 = canonicalize_naive(g, list(range(6,12)), 0, (baser, gensr, 3, 0)) #assert can == can1 # A^n_{i, j} antisymmetric in i,j # A_m0^d0_a1 * A_m1^a0_d0; ord = [m0,m1,a0,a1,d0,-d0] # g = [0,4,3,1,2,5,6,7] # T_c = -A_{m a1}^d0 * A_m1^a0_d0 # can = [0,3,4,1,2,5,7,6] base, gens = bsgs_direct_product(base1, gens1, base2a, gens2a) dummies = list(range(4, 6)) g = Permutation([0,4,3,1,2,5,6,7]) can = canonicalize(g, dummies, 0, (base, gens, 2, 0)) assert can == [0, 3, 4, 1, 2, 5, 7, 6] # A^n_{i, j} symmetric in i,j # A^m0_a0^d2 * A^n0_d2^d1 * A^n1_d1^d0 * A_{m0 d0}^a1 # ordering: first the free indices; then first n, then d # ord=[n0,n1,a0,a1, m0,-m0,d0,-d0,d1,-d1,d2,-d2] # 0 1 2 3 4 5 6 7 8 9 10 11] # g = [4,2,10, 0,11,8, 1,9,6, 5,7,3, 12,13] # if the dummy indices m_i and d_i were separated, # one gets # T_c = A^{n0 d0 d1} * A^n1_d0^d2 * A^m0^a0_d1 * A_m0^a1_d2 # can = [0, 6, 8, 1, 7, 10, 4, 2, 9, 5, 3, 11, 12, 13] # If they are not, so can is # T_c = A^{n0 m0 d0} A^n1_m0^d1 A^{d2 a0}_d0 A_d2^a1_d1 # can = [0, 4, 6, 1, 5, 8, 10, 2, 7, 11, 3, 9, 12, 13] # case with single type of indices base, gens = bsgs_direct_product(base1, gens1, base2, gens2) dummies = list(range(4, 12)) g = Permutation([4,2,10, 0,11,8, 1,9,6, 5,7,3, 12,13]) can = canonicalize(g, dummies, 0, (base, gens, 4, 0)) assert can == [0, 4, 6, 1, 5, 8, 10, 2, 7, 11, 3, 9, 12, 13] # case with separated indices dummies = [list(range(4, 6)), list(range(6,12))] sym = [0, 0] can = canonicalize(g, dummies, sym, (base, gens, 4, 0)) assert can == [0, 6, 8, 1, 7, 10, 4, 2, 9, 5, 3, 11, 12, 13] # case with separated indices with the second type of index # with antisymmetric metric: there is a sign change sym = [0, 1] can = canonicalize(g, dummies, sym, (base, gens, 4, 0)) assert can == [0, 6, 8, 1, 7, 10, 4, 2, 9, 5, 3, 11, 13, 12] def test_graph_certificate(): # test tensor invariants constructed from random regular graphs; # checked graph isomorphism with networkx import random def randomize_graph(size, g): p = list(range(size)) random.shuffle(p) g1a = {} for k, v in g1.items(): g1a[p[k]] = [p[i] for i in v] return g1a g1 = {0: [2, 3, 7], 1: [4, 5, 7], 2: [0, 4, 6], 3: [0, 6, 7], 4: [1, 2, 5], 5: [1, 4, 6], 6: [2, 3, 5], 7: [0, 1, 3]} g2 = {0: [2, 3, 7], 1: [2, 4, 5], 2: [0, 1, 5], 3: [0, 6, 7], 4: [1, 5, 6], 5: [1, 2, 4], 6: [3, 4, 7], 7: [0, 3, 6]} c1 = graph_certificate(g1) c2 = graph_certificate(g2) assert c1 != c2 g1a = randomize_graph(8, g1) c1a = graph_certificate(g1a) assert c1 == c1a g1 = {0: [8, 1, 9, 7], 1: [0, 9, 3, 4], 2: [3, 4, 6, 7], 3: [1, 2, 5, 6], 4: [8, 1, 2, 5], 5: [9, 3, 4, 7], 6: [8, 2, 3, 7], 7: [0, 2, 5, 6], 8: [0, 9, 4, 6], 9: [8, 0, 5, 1]} g2 = {0: [1, 2, 5, 6], 1: [0, 9, 5, 7], 2: [0, 4, 6, 7], 3: [8, 9, 6, 7], 4: [8, 2, 6, 7], 5: [0, 9, 8, 1], 6: [0, 2, 3, 4], 7: [1, 2, 3, 4], 8: [9, 3, 4, 5], 9: [8, 1, 3, 5]} c1 = graph_certificate(g1) c2 = graph_certificate(g2) assert c1 != c2 g1a = randomize_graph(10, g1) c1a = graph_certificate(g1a) assert c1 == c1a sympy-0.7.4.1/sympy/combinatorics/tests/test_prufer.py0000644000175000017500000000441112253362407023337 0ustar georgeskgeorgeskfrom sympy.combinatorics.prufer import Prufer from sympy.utilities.pytest import raises def test_prufer(): # number of nodes is optional assert Prufer([[0, 1], [0, 2], [0, 3], [0, 4]], 5).nodes == 5 assert Prufer([[0, 1], [0, 2], [0, 3], [0, 4]]).nodes == 5 a = Prufer([[0, 1], [0, 2], [0, 3], [0, 4]]) assert a.rank == 0 assert a.nodes == 5 assert a.prufer_repr == [0, 0, 0] a = Prufer([[2, 4], [1, 4], [1, 3], [0, 5], [0, 4]]) assert a.rank == 924 assert a.nodes == 6 assert a.tree_repr == [[2, 4], [1, 4], [1, 3], [0, 5], [0, 4]] assert a.prufer_repr == [4, 1, 4, 0] assert Prufer.edges([0, 1, 2, 3], [1, 4, 5], [1, 4, 6]) == \ ([[0, 1], [1, 2], [1, 4], [2, 3], [4, 5], [4, 6]], 7) assert Prufer([0]*4).size == Prufer([6]*4).size == 1296 # accept iterables but convert to list of lists tree = [(0, 1), (1, 5), (0, 3), (0, 2), (2, 6), (4, 7), (2, 4)] Prufer(tree).tree_repr == tree Prufer(set(tree)).tree_repr == tree raises(ValueError, lambda: Prufer([[1, 2], [3, 4]])) # 0 is missing assert Prufer(*Prufer.edges([1, 2], [3, 4])).prufer_repr == [1, 3] raises(ValueError, lambda: Prufer.edges( [1, 3], [3, 4])) # a broken tree but edges doesn't care raises(ValueError, lambda: Prufer.edges([1, 2], [5, 6])) def test_round_trip(): def doit(t, b): e, n = Prufer.edges(*t) t = Prufer(e, n) a = sorted(t.tree_repr) b = [i - 1 for i in b] assert t.prufer_repr == b assert sorted(Prufer(b).tree_repr) == a assert Prufer.unrank(t.rank, n).prufer_repr == b doit([[1, 2]], []) doit([[2, 1, 3]], [1]) doit([[1, 3, 2]], [3]) doit([[1, 2, 3]], [2]) doit([[2, 1, 4], [1, 3]], [1, 1]) doit([[3, 2, 1, 4]], [2, 1]) doit([[3, 2, 1], [2, 4]], [2, 2]) doit([[1, 3, 2, 4]], [3, 2]) doit([[1, 4, 2, 3]], [4, 2]) doit([[3, 1, 4, 2]], [4, 1]) doit([[4, 2, 1, 3]], [1, 2]) doit([[1, 2, 4, 3]], [2, 4]) doit([[1, 3, 4, 2]], [3, 4]) doit([[2, 4, 1], [4, 3]], [4, 4]) doit([[1, 2, 3, 4]], [2, 3]) doit([[2, 3, 1], [3, 4]], [3, 3]) doit([[1, 4, 3, 2]], [4, 3]) doit([[2, 1, 4, 3]], [1, 4]) doit([[2, 1, 3, 4]], [1, 3]) doit([[6, 2, 1, 4], [1, 3, 5, 8], [3, 7]], [1, 2, 1, 3, 3, 5]) sympy-0.7.4.1/sympy/combinatorics/tests/test_named_groups.py0000644000175000017500000000326412253362407024524 0ustar georgeskgeorgeskfrom sympy.combinatorics.named_groups import (SymmetricGroup, CyclicGroup, DihedralGroup, AlternatingGroup, AbelianGroup) def test_SymmetricGroup(): G = SymmetricGroup(5) elements = list(G.generate()) assert (G.generators[0]).size == 5 assert len(elements) == 120 assert G.is_solvable is False assert G.is_abelian is False assert G.is_nilpotent is False assert G.is_transitive() is True H = SymmetricGroup(1) assert H.order() == 1 L = SymmetricGroup(2) assert L.order() == 2 def test_CyclicGroup(): G = CyclicGroup(10) elements = list(G.generate()) assert len(elements) == 10 assert (G.derived_subgroup()).order() == 1 assert G.is_abelian is True assert G.is_solvable is True assert G.is_nilpotent is True H = CyclicGroup(1) assert H.order() == 1 L = CyclicGroup(2) assert L.order() == 2 def test_DihedralGroup(): G = DihedralGroup(6) elements = list(G.generate()) assert len(elements) == 12 assert G.is_transitive() is True assert G.is_abelian is False assert G.is_solvable is True assert G.is_nilpotent is False H = DihedralGroup(1) assert H.order() == 2 L = DihedralGroup(2) assert L.order() == 4 assert L.is_abelian is True assert L.is_nilpotent is True def test_AlternatingGroup(): G = AlternatingGroup(5) elements = list(G.generate()) assert len(elements) == 60 assert [perm.is_even for perm in elements] == [True]*60 H = AlternatingGroup(1) assert H.order() == 1 L = AlternatingGroup(2) assert L.order() == 1 def test_AbelianGroup(): A = AbelianGroup(3, 3, 3) assert A.order() == 27 assert A.is_abelian is True sympy-0.7.4.1/sympy/combinatorics/tests/test_generators.py0000644000175000017500000000662112253362407024212 0ustar georgeskgeorgeskfrom sympy.combinatorics.generators import symmetric, cyclic, alternating, dihedral from sympy.combinatorics.permutations import Permutation def test_generators(): assert list(cyclic(6)) == [ Permutation([0, 1, 2, 3, 4, 5]), Permutation([1, 2, 3, 4, 5, 0]), Permutation([2, 3, 4, 5, 0, 1]), Permutation([3, 4, 5, 0, 1, 2]), Permutation([4, 5, 0, 1, 2, 3]), Permutation([5, 0, 1, 2, 3, 4])] assert list(cyclic(10)) == [ Permutation([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), Permutation([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]), Permutation([2, 3, 4, 5, 6, 7, 8, 9, 0, 1]), Permutation([3, 4, 5, 6, 7, 8, 9, 0, 1, 2]), Permutation([4, 5, 6, 7, 8, 9, 0, 1, 2, 3]), Permutation([5, 6, 7, 8, 9, 0, 1, 2, 3, 4]), Permutation([6, 7, 8, 9, 0, 1, 2, 3, 4, 5]), Permutation([7, 8, 9, 0, 1, 2, 3, 4, 5, 6]), Permutation([8, 9, 0, 1, 2, 3, 4, 5, 6, 7]), Permutation([9, 0, 1, 2, 3, 4, 5, 6, 7, 8])] assert list(alternating(4)) == [ Permutation([0, 1, 2, 3]), Permutation([0, 2, 3, 1]), Permutation([0, 3, 1, 2]), Permutation([1, 0, 3, 2]), Permutation([1, 2, 0, 3]), Permutation([1, 3, 2, 0]), Permutation([2, 0, 1, 3]), Permutation([2, 1, 3, 0]), Permutation([2, 3, 0, 1]), Permutation([3, 0, 2, 1]), Permutation([3, 1, 0, 2]), Permutation([3, 2, 1, 0])] assert list(symmetric(3)) == [ Permutation([0, 1, 2]), Permutation([0, 2, 1]), Permutation([1, 0, 2]), Permutation([1, 2, 0]), Permutation([2, 0, 1]), Permutation([2, 1, 0])] assert list(symmetric(4)) == [ Permutation([0, 1, 2, 3]), Permutation([0, 1, 3, 2]), Permutation([0, 2, 1, 3]), Permutation([0, 2, 3, 1]), Permutation([0, 3, 1, 2]), Permutation([0, 3, 2, 1]), Permutation([1, 0, 2, 3]), Permutation([1, 0, 3, 2]), Permutation([1, 2, 0, 3]), Permutation([1, 2, 3, 0]), Permutation([1, 3, 0, 2]), Permutation([1, 3, 2, 0]), Permutation([2, 0, 1, 3]), Permutation([2, 0, 3, 1]), Permutation([2, 1, 0, 3]), Permutation([2, 1, 3, 0]), Permutation([2, 3, 0, 1]), Permutation([2, 3, 1, 0]), Permutation([3, 0, 1, 2]), Permutation([3, 0, 2, 1]), Permutation([3, 1, 0, 2]), Permutation([3, 1, 2, 0]), Permutation([3, 2, 0, 1]), Permutation([3, 2, 1, 0])] assert list(dihedral(1)) == [ Permutation([0, 1]), Permutation([1, 0])] assert list(dihedral(2)) == [ Permutation([0, 1, 2, 3]), Permutation([1, 0, 3, 2]), Permutation([2, 3, 0, 1]), Permutation([3, 2, 1, 0])] assert list(dihedral(3)) == [ Permutation([0, 1, 2]), Permutation([2, 1, 0]), Permutation([1, 2, 0]), Permutation([0, 2, 1]), Permutation([2, 0, 1]), Permutation([1, 0, 2])] assert list(dihedral(5)) == [ Permutation([0, 1, 2, 3, 4]), Permutation([4, 3, 2, 1, 0]), Permutation([1, 2, 3, 4, 0]), Permutation([0, 4, 3, 2, 1]), Permutation([2, 3, 4, 0, 1]), Permutation([1, 0, 4, 3, 2]), Permutation([3, 4, 0, 1, 2]), Permutation([2, 1, 0, 4, 3]), Permutation([4, 0, 1, 2, 3]), Permutation([3, 2, 1, 0, 4])] sympy-0.7.4.1/sympy/combinatorics/tests/test_testutil.py0000644000175000017500000000326612253362407023720 0ustar georgeskgeorgeskfrom sympy.combinatorics.named_groups import SymmetricGroup, AlternatingGroup,\ CyclicGroup from sympy.combinatorics.testutil import _verify_bsgs, _cmp_perm_lists,\ _naive_list_centralizer, _verify_centralizer,\ _verify_normal_closure from sympy.combinatorics.permutations import Permutation from sympy.combinatorics.perm_groups import PermutationGroup from random import shuffle def test_cmp_perm_lists(): S = SymmetricGroup(4) els = list(S.generate_dimino()) other = els[:] shuffle(other) assert _cmp_perm_lists(els, other) is True def test_naive_list_centralizer(): # verified by GAP S = SymmetricGroup(3) A = AlternatingGroup(3) assert _naive_list_centralizer(S, S) == [Permutation([0, 1, 2])] assert PermutationGroup(_naive_list_centralizer(S, A)).is_subgroup(A) def test_verify_bsgs(): S = SymmetricGroup(5) S.schreier_sims() base = S.base strong_gens = S.strong_gens assert _verify_bsgs(S, base, strong_gens) is True assert _verify_bsgs(S, base[:-1], strong_gens) is False assert _verify_bsgs(S, base, S.generators) is False def test_verify_centralizer(): # verified by GAP S = SymmetricGroup(3) A = AlternatingGroup(3) triv = PermutationGroup([Permutation([0, 1, 2])]) assert _verify_centralizer(S, S, centr=triv) assert _verify_centralizer(S, A, centr=A) def test_verify_normal_closure(): # verified by GAP S = SymmetricGroup(3) A = AlternatingGroup(3) assert _verify_normal_closure(S, A, closure=A) S = SymmetricGroup(5) A = AlternatingGroup(5) C = CyclicGroup(5) assert _verify_normal_closure(S, A, closure=A) assert _verify_normal_closure(S, C, closure=A) sympy-0.7.4.1/sympy/combinatorics/tests/__init__.py0000644000175000017500000000000012253362407022522 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/combinatorics/__init__.py0000644000175000017500000000141412253362407021372 0ustar georgeskgeorgeskfrom 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) sympy-0.7.4.1/sympy/mpmath/0000755000175000017500000000000012253362407015713 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/mpmath/matrices/0000755000175000017500000000000012253362407017522 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/mpmath/matrices/linalg.py0000644000175000017500000004553412253362407021355 0ustar georgeskgeorgesk""" Linear algebra -------------- 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 choose a faster data type using the keyword ``force_type``:: >>> lu_solve(A, b, force_type=float) matrix( [[29.999999999999996], [-19.999999999999996]]) ``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 matrices ----------------- Matrices may contain interval elements. This allows one to perform basic linear algebra operations such as matrix multiplication and equation solving with rigorous error bounds:: >>> a = matrix([['0.1','0.3','1.0'], ... ['7.1','5.5','4.8'], ... ['3.2','4.4','5.6']], force_type=mpi) >>> >>> b = matrix(['4','0.6','0.5'], force_type=mpi) >>> c = lu_solve(a, b) >>> c matrix( [[[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]] """ # TODO: # *implement high-level qr() # *test unitvector # *iterative solving from copy import copy from ..libmp.backend import xrange class LinearAlgebraMethods(object): def LU_decomp(ctx, A, overwrite=False, use_cache=True): """ LU-factorization of a n*n matrix using the Gauss algorithm. Returns L and U in one matrix and the pivot indices. Use overwrite to specify whether A will be overwritten with L and U. """ if not A.rows == A.cols: raise ValueError('need n*n matrix') # get from cache if possible if use_cache and isinstance(A, ctx.matrix) and A._LU: return A._LU if not overwrite: orig = A A = A.copy() tol = ctx.absmin(ctx.mnorm(A,1) * ctx.eps) # each pivot element has to be bigger n = A.rows p = [None]*(n - 1) for j in xrange(n - 1): # pivoting, choose max(abs(reciprocal row sum)*abs(pivot element)) biggest = 0 for k in xrange(j, n): s = ctx.fsum([ctx.absmin(A[k,l]) for l in xrange(j, n)]) if ctx.absmin(s) <= tol: raise ZeroDivisionError('matrix is numerically singular') current = 1/s * ctx.absmin(A[k,j]) if current > biggest: # TODO: what if equal? biggest = current p[j] = k # swap rows according to p ctx.swap_row(A, j, p[j]) if ctx.absmin(A[j,j]) <= tol: raise ZeroDivisionError('matrix is numerically singular') # calculate elimination factors and add rows for i in xrange(j + 1, n): A[i,j] /= A[j,j] for k in xrange(j + 1, n): A[i,k] -= A[i,j]*A[j,k] if ctx.absmin(A[n - 1,n - 1]) <= tol: raise ZeroDivisionError('matrix is numerically singular') # cache decomposition if not overwrite and isinstance(orig, ctx.matrix): orig._LU = (A, p) return A, p def L_solve(ctx, L, b, p=None): """ Solve the lower part of a LU factorized matrix for y. """ assert L.rows == L.cols, 'need n*n matrix' n = L.rows assert len(b) == n b = copy(b) if p: # swap b according to p for k in xrange(0, len(p)): ctx.swap_row(b, k, p[k]) # solve for i in xrange(1, n): for j in xrange(i): b[i] -= L[i,j] * b[j] return b def U_solve(ctx, U, y): """ Solve the upper part of a LU factorized matrix for x. """ assert U.rows == U.cols, 'need n*n matrix' n = U.rows assert len(y) == n x = copy(y) for i in xrange(n - 1, -1, -1): for j in xrange(i + 1, n): x[i] -= U[i,j] * x[j] x[i] /= U[i,i] return x def lu_solve(ctx, A, b, **kwargs): """ Ax = b => x Solve a determined or overdetermined linear equations system. Fast LU decomposition is used, which is less accurate than QR decomposition (especially for overdetermined systems), but it's twice as efficient. Use qr_solve if you want more precision or have to solve a very ill- conditioned system. If you specify real=True, it does not check for overdeterminded complex systems. """ prec = ctx.prec try: ctx.prec += 10 # do not overwrite A nor b A, b = ctx.matrix(A, **kwargs).copy(), ctx.matrix(b, **kwargs).copy() if A.rows < A.cols: raise ValueError('cannot solve underdetermined system') if A.rows > A.cols: # use least-squares method if overdetermined # (this increases errors) AH = A.H A = AH * A b = AH * b if (kwargs.get('real', False) or not sum(type(i) is ctx.mpc for i in A)): # TODO: necessary to check also b? x = ctx.cholesky_solve(A, b) else: x = ctx.lu_solve(A, b) else: # LU factorization A, p = ctx.LU_decomp(A) b = ctx.L_solve(A, b, p) x = ctx.U_solve(A, b) finally: ctx.prec = prec return x def improve_solution(ctx, A, x, b, maxsteps=1): """ Improve a solution to a linear equation system iteratively. This re-uses the LU decomposition and is thus cheap. Usually 3 up to 4 iterations are giving the maximal improvement. """ assert A.rows == A.cols, 'need n*n matrix' # TODO: really? for _ in xrange(maxsteps): r = ctx.residual(A, x, b) if ctx.norm(r, 2) < 10*ctx.eps: break # this uses cached LU decomposition and is thus cheap dx = ctx.lu_solve(A, -r) x += dx return x def lu(ctx, A): """ A -> P, L, U LU factorisation of a square matrix A. L is the lower, U the upper part. P is the permutation matrix indicating the row swaps. P*A = L*U If you need efficiency, use the low-level method LU_decomp instead, it's much more memory efficient. """ # get factorization A, p = ctx.LU_decomp(A) n = A.rows L = ctx.matrix(n) U = ctx.matrix(n) for i in xrange(n): for j in xrange(n): if i > j: L[i,j] = A[i,j] elif i == j: L[i,j] = 1 U[i,j] = A[i,j] else: U[i,j] = A[i,j] # calculate permutation matrix P = ctx.eye(n) for k in xrange(len(p)): ctx.swap_row(P, k, p[k]) return P, L, U def unitvector(ctx, n, i): """ Return the i-th n-dimensional unit vector. """ assert 0 < i <= n, 'this unit vector does not exist' return [ctx.zero]*(i-1) + [ctx.one] + [ctx.zero]*(n-i) def inverse(ctx, A, **kwargs): """ Calculate the inverse of a matrix. If you want to solve an equation system Ax = b, it's recommended to use solve(A, b) instead, it's about 3 times more efficient. """ prec = ctx.prec try: ctx.prec += 10 # do not overwrite A A = ctx.matrix(A, **kwargs).copy() n = A.rows # get LU factorisation A, p = ctx.LU_decomp(A) cols = [] # calculate unit vectors and solve corresponding system to get columns for i in xrange(1, n + 1): e = ctx.unitvector(n, i) y = ctx.L_solve(A, e, p) cols.append(ctx.U_solve(A, y)) # convert columns to matrix inv = [] for i in xrange(n): row = [] for j in xrange(n): row.append(cols[j][i]) inv.append(row) result = ctx.matrix(inv, **kwargs) finally: ctx.prec = prec return result def householder(ctx, A): """ (A|b) -> H, p, x, res (A|b) is the coefficient matrix with left hand side of an optionally overdetermined linear equation system. H and p contain all information about the transformation matrices. x is the solution, res the residual. """ assert isinstance(A, ctx.matrix) m = A.rows n = A.cols assert m >= n - 1 # calculate Householder matrix p = [] for j in xrange(0, n - 1): s = ctx.fsum((A[i,j])**2 for i in xrange(j, m)) if not abs(s) > ctx.eps: raise ValueError('matrix is numerically singular') p.append(-ctx.sign(A[j,j]) * ctx.sqrt(s)) kappa = ctx.one / (s - p[j] * A[j,j]) A[j,j] -= p[j] for k in xrange(j+1, n): y = ctx.fsum(A[i,j] * A[i,k] for i in xrange(j, m)) * kappa for i in xrange(j, m): A[i,k] -= A[i,j] * y # solve Rx = c1 x = [A[i,n - 1] for i in xrange(n - 1)] for i in xrange(n - 2, -1, -1): x[i] -= ctx.fsum(A[i,j] * x[j] for j in xrange(i + 1, n - 1)) x[i] /= p[i] # calculate residual if not m == n - 1: r = [A[m-1-i, n-1] for i in xrange(m - n + 1)] else: # determined system, residual should be 0 r = [0]*m # maybe a bad idea, changing r[i] will change all elements return A, p, x, r #def qr(ctx, A): # """ # A -> Q, R # # QR factorisation of a square matrix A using Householder decomposition. # Q is orthogonal, this leads to very few numerical errors. # # A = Q*R # """ # H, p, x, res = householder(A) # TODO: implement this def residual(ctx, A, x, b, **kwargs): """ Calculate the residual of a solution to a linear equation system. r = A*x - b for A*x = b """ oldprec = ctx.prec try: ctx.prec *= 2 A, x, b = ctx.matrix(A, **kwargs), ctx.matrix(x, **kwargs), ctx.matrix(b, **kwargs) return A*x - b finally: ctx.prec = oldprec def qr_solve(ctx, A, b, norm=None, **kwargs): """ Ax = b => x, ||Ax - b|| Solve a determined or overdetermined linear equations system and calculate the norm of the residual (error). QR decomposition using Householder factorization is applied, which gives very accurate results even for ill-conditioned matrices. qr_solve is twice as efficient. """ if norm is None: norm = ctx.norm prec = ctx.prec try: ctx.prec += 10 # do not overwrite A nor b A, b = ctx.matrix(A, **kwargs).copy(), ctx.matrix(b, **kwargs).copy() if A.rows < A.cols: raise ValueError('cannot solve underdetermined system') H, p, x, r = ctx.householder(ctx.extend(A, b)) res = ctx.norm(r) # calculate residual "manually" for determined systems if res == 0: res = ctx.norm(ctx.residual(A, x, b)) return ctx.matrix(x, **kwargs), res finally: ctx.prec = prec def cholesky(ctx, A, tol=None): r""" Cholesky decomposition of a symmetric positive-definite matrix `A`. Returns a lower triangular matrix `L` such that `A = L \times L^T`. More generally, for a complex Hermitian positive-definite matrix, a Cholesky decomposition satisfying `A = L \times L^H` is returned. The Cholesky decomposition can be used to solve linear equation systems twice as efficiently as LU decomposition, or to test whether `A` is positive-definite. The optional parameter ``tol`` determines the tolerance for verifying positive-definiteness. **Examples** Cholesky decomposition of a positive-definite symmetric matrix:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> A = eye(3) + hilbert(3) >>> nprint(A) [ 2.0 0.5 0.333333] [ 0.5 1.33333 0.25] [0.333333 0.25 1.2] >>> L = cholesky(A) >>> nprint(L) [ 1.41421 0.0 0.0] [0.353553 1.09924 0.0] [0.235702 0.15162 1.05899] >>> chop(A - L*L.T) [0.0 0.0 0.0] [0.0 0.0 0.0] [0.0 0.0 0.0] Cholesky decomposition of a Hermitian matrix:: >>> A = eye(3) + matrix([[0,0.25j,-0.5j],[-0.25j,0,0],[0.5j,0,0]]) >>> L = cholesky(A) >>> nprint(L) [ 1.0 0.0 0.0] [(0.0 - 0.25j) (0.968246 + 0.0j) 0.0] [ (0.0 + 0.5j) (0.129099 + 0.0j) (0.856349 + 0.0j)] >>> chop(A - L*L.H) [0.0 0.0 0.0] [0.0 0.0 0.0] [0.0 0.0 0.0] Attempted Cholesky decomposition of a matrix that is not positive definite:: >>> A = -eye(3) + hilbert(3) >>> L = cholesky(A) Traceback (most recent call last): ... ValueError: matrix is not positive-definite **References** 1. [Wikipedia]_ http://en.wikipedia.org/wiki/Cholesky_decomposition """ assert isinstance(A, ctx.matrix) if not A.rows == A.cols: raise ValueError('need n*n matrix') if tol is None: tol = +ctx.eps n = A.rows L = ctx.matrix(n) for j in xrange(n): c = ctx.re(A[j,j]) if abs(c-A[j,j]) > tol: raise ValueError('matrix is not Hermitian') s = c - ctx.fsum((L[j,k] for k in xrange(j)), absolute=True, squared=True) if s < tol: raise ValueError('matrix is not positive-definite') L[j,j] = ctx.sqrt(s) for i in xrange(j, n): it1 = (L[i,k] for k in xrange(j)) it2 = (L[j,k] for k in xrange(j)) t = ctx.fdot(it1, it2, conjugate=True) L[i,j] = (A[i,j] - t) / L[j,j] return L def cholesky_solve(ctx, A, b, **kwargs): """ Ax = b => x Solve a symmetric positive-definite linear equation system. This is twice as efficient as lu_solve. Typical use cases: * A.T*A * Hessian matrix * differential equations """ prec = ctx.prec try: ctx.prec += 10 # do not overwrite A nor b A, b = ctx.matrix(A, **kwargs).copy(), ctx.matrix(b, **kwargs).copy() if A.rows != A.cols: raise ValueError('can only solve determined system') # Cholesky factorization L = ctx.cholesky(A) # solve n = L.rows assert len(b) == n for i in xrange(n): b[i] -= ctx.fsum(L[i,j] * b[j] for j in xrange(i)) b[i] /= L[i,i] x = ctx.U_solve(L.T, b) return x finally: ctx.prec = prec def det(ctx, A): """ Calculate the determinant of a matrix. """ prec = ctx.prec try: # do not overwrite A A = ctx.matrix(A).copy() # use LU factorization to calculate determinant try: R, p = ctx.LU_decomp(A) except ZeroDivisionError: return 0 z = 1 for i, e in enumerate(p): if i != e: z *= -1 for i in xrange(A.rows): z *= R[i,i] return z finally: ctx.prec = prec def cond(ctx, A, norm=None): """ Calculate the condition number of a matrix using a specified matrix norm. The condition number estimates the sensitivity of a matrix to errors. Example: small input errors for ill-conditioned coefficient matrices alter the solution of the system dramatically. For ill-conditioned matrices it's recommended to use qr_solve() instead of lu_solve(). This does not help with input errors however, it just avoids to add additional errors. Definition: cond(A) = ||A|| * ||A**-1|| """ if norm is None: norm = lambda x: ctx.mnorm(x,1) return norm(A) * norm(ctx.inverse(A)) def lu_solve_mat(ctx, a, b): """Solve a * x = b where a and b are matrices.""" r = ctx.matrix(a.rows, b.cols) for i in range(b.cols): c = ctx.lu_solve(a, b.column(i)) for j in range(len(c)): r[j, i] = c[j] return r sympy-0.7.4.1/sympy/mpmath/matrices/calculus.py0000644000175000017500000004412712253362407021717 0ustar georgeskgeorgeskfrom ..libmp.backend import xrange # TODO: should use diagonalization-based algorithms class MatrixCalculusMethods: def _exp_pade(ctx, a): """ Exponential of a matrix using Pade approximants. See G. H. Golub, C. F. van Loan 'Matrix Computations', third Ed., page 572 TODO: - find a good estimate for q - reduce the number of matrix multiplications to improve performance """ def eps_pade(p): return ctx.mpf(2)**(3-2*p) * \ ctx.factorial(p)**2/(ctx.factorial(2*p)**2 * (2*p + 1)) q = 4 extraq = 8 while 1: if eps_pade(q) < ctx.eps: break q += 1 q += extraq j = int(max(1, ctx.mag(ctx.mnorm(a,'inf')))) extra = q prec = ctx.prec ctx.dps += extra + 3 try: a = a/2**j na = a.rows den = ctx.eye(na) num = ctx.eye(na) x = ctx.eye(na) c = ctx.mpf(1) for k in range(1, q+1): c *= ctx.mpf(q - k + 1)/((2*q - k + 1) * k) x = a*x cx = c*x num += cx den += (-1)**k * cx f = ctx.lu_solve_mat(den, num) for k in range(j): f = f*f finally: ctx.prec = prec return f*1 def expm(ctx, A, method='taylor'): r""" Computes the matrix exponential of a square matrix `A`, which is defined by the power series .. math :: \exp(A) = I + A + \frac{A^2}{2!} + \frac{A^3}{3!} + \ldots With method='taylor', the matrix exponential is computed using the Taylor series. With method='pade', Pade approximants are used instead. **Examples** Basic examples:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> expm(zeros(3)) [1.0 0.0 0.0] [0.0 1.0 0.0] [0.0 0.0 1.0] >>> expm(eye(3)) [2.71828182845905 0.0 0.0] [ 0.0 2.71828182845905 0.0] [ 0.0 0.0 2.71828182845905] >>> expm([[1,1,0],[1,0,1],[0,1,0]]) [ 3.86814500615414 2.26812870852145 0.841130841230196] [ 2.26812870852145 2.44114713886289 1.42699786729125] [0.841130841230196 1.42699786729125 1.6000162976327] >>> expm([[1,1,0],[1,0,1],[0,1,0]], method='pade') [ 3.86814500615414 2.26812870852145 0.841130841230196] [ 2.26812870852145 2.44114713886289 1.42699786729125] [0.841130841230196 1.42699786729125 1.6000162976327] >>> expm([[1+j, 0], [1+j,1]]) [(1.46869393991589 + 2.28735528717884j) 0.0] [ (1.03776739863568 + 3.536943175722j) (2.71828182845905 + 0.0j)] Matrices with large entries are allowed:: >>> expm(matrix([[1,2],[2,3]])**25) [5.65024064048415e+2050488462815550 9.14228140091932e+2050488462815550] [9.14228140091932e+2050488462815550 1.47925220414035e+2050488462815551] The identity `\exp(A+B) = \exp(A) \exp(B)` does not hold for noncommuting matrices:: >>> A = hilbert(3) >>> B = A + eye(3) >>> chop(mnorm(A*B - B*A)) 0.0 >>> chop(mnorm(expm(A+B) - expm(A)*expm(B))) 0.0 >>> B = A + ones(3) >>> mnorm(A*B - B*A) 1.8 >>> mnorm(expm(A+B) - expm(A)*expm(B)) 42.0927851137247 """ if method == 'pade': prec = ctx.prec ctx.prec += 2*A.rows A = ctx.matrix(A) res = ctx._exp_pade(A) return res A = ctx.matrix(A) prec = ctx.prec j = int(max(1, ctx.mag(ctx.mnorm(A,'inf')))) j += int(0.5*prec**0.5) try: ctx.prec += 10 + 2*j tol = +ctx.eps A = A/2**j T = A Y = A**0 + A k = 2 while 1: T *= A * (1/ctx.mpf(k)) if ctx.mnorm(T, 'inf') < tol: break Y += T k += 1 for k in xrange(j): Y = Y*Y finally: ctx.prec = prec Y *= 1 return Y def cosm(ctx, A): r""" Gives the cosine of a square matrix `A`, defined in analogy with the matrix exponential. Examples:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> X = eye(3) >>> cosm(X) [0.54030230586814 0.0 0.0] [ 0.0 0.54030230586814 0.0] [ 0.0 0.0 0.54030230586814] >>> X = hilbert(3) >>> cosm(X) [ 0.424403834569555 -0.316643413047167 -0.221474945949293] [-0.316643413047167 0.820646708837824 -0.127183694770039] [-0.221474945949293 -0.127183694770039 0.909236687217541] >>> X = matrix([[1+j,-2],[0,-j]]) >>> cosm(X) [(0.833730025131149 - 0.988897705762865j) (1.07485840848393 - 0.17192140544213j)] [ 0.0 (1.54308063481524 + 0.0j)] """ B = 0.5 * (ctx.expm(A*ctx.j) + ctx.expm(A*(-ctx.j))) if not sum(A.apply(ctx.im).apply(abs)): B = B.apply(ctx.re) return B def sinm(ctx, A): r""" Gives the sine of a square matrix `A`, defined in analogy with the matrix exponential. Examples:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> X = eye(3) >>> sinm(X) [0.841470984807897 0.0 0.0] [ 0.0 0.841470984807897 0.0] [ 0.0 0.0 0.841470984807897] >>> X = hilbert(3) >>> sinm(X) [0.711608512150994 0.339783913247439 0.220742837314741] [0.339783913247439 0.244113865695532 0.187231271174372] [0.220742837314741 0.187231271174372 0.155816730769635] >>> X = matrix([[1+j,-2],[0,-j]]) >>> sinm(X) [(1.29845758141598 + 0.634963914784736j) (-1.96751511930922 + 0.314700021761367j)] [ 0.0 (0.0 - 1.1752011936438j)] """ B = (-0.5j) * (ctx.expm(A*ctx.j) - ctx.expm(A*(-ctx.j))) if not sum(A.apply(ctx.im).apply(abs)): B = B.apply(ctx.re) return B def _sqrtm_rot(ctx, A, _may_rotate): # If the iteration fails to converge, cheat by performing # a rotation by a complex number u = ctx.j**0.3 return ctx.sqrtm(u*A, _may_rotate) / ctx.sqrt(u) def sqrtm(ctx, A, _may_rotate=2): r""" Computes a square root of the square matrix `A`, i.e. returns a matrix `B = A^{1/2}` such that `B^2 = A`. The square root of a matrix, if it exists, is not unique. **Examples** Square roots of some simple matrices:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> sqrtm([[1,0], [0,1]]) [1.0 0.0] [0.0 1.0] >>> sqrtm([[0,0], [0,0]]) [0.0 0.0] [0.0 0.0] >>> sqrtm([[2,0],[0,1]]) [1.4142135623731 0.0] [ 0.0 1.0] >>> sqrtm([[1,1],[1,0]]) [ (0.920442065259926 - 0.21728689675164j) (0.568864481005783 + 0.351577584254143j)] [(0.568864481005783 + 0.351577584254143j) (0.351577584254143 - 0.568864481005783j)] >>> sqrtm([[1,0],[0,1]]) [1.0 0.0] [0.0 1.0] >>> sqrtm([[-1,0],[0,1]]) [(0.0 - 1.0j) 0.0] [ 0.0 (1.0 + 0.0j)] >>> sqrtm([[j,0],[0,j]]) [(0.707106781186547 + 0.707106781186547j) 0.0] [ 0.0 (0.707106781186547 + 0.707106781186547j)] A square root of a rotation matrix, giving the corresponding half-angle rotation matrix:: >>> t1 = 0.75 >>> t2 = t1 * 0.5 >>> A1 = matrix([[cos(t1), -sin(t1)], [sin(t1), cos(t1)]]) >>> A2 = matrix([[cos(t2), -sin(t2)], [sin(t2), cos(t2)]]) >>> sqrtm(A1) [0.930507621912314 -0.366272529086048] [0.366272529086048 0.930507621912314] >>> A2 [0.930507621912314 -0.366272529086048] [0.366272529086048 0.930507621912314] The identity `(A^2)^{1/2} = A` does not necessarily hold:: >>> A = matrix([[4,1,4],[7,8,9],[10,2,11]]) >>> sqrtm(A**2) [ 4.0 1.0 4.0] [ 7.0 8.0 9.0] [10.0 2.0 11.0] >>> sqrtm(A)**2 [ 4.0 1.0 4.0] [ 7.0 8.0 9.0] [10.0 2.0 11.0] >>> A = matrix([[-4,1,4],[7,-8,9],[10,2,11]]) >>> sqrtm(A**2) [ 7.43715112194995 -0.324127569985474 1.8481718827526] [-0.251549715716942 9.32699765900402 2.48221180985147] [ 4.11609388833616 0.775751877098258 13.017955697342] >>> chop(sqrtm(A)**2) [-4.0 1.0 4.0] [ 7.0 -8.0 9.0] [10.0 2.0 11.0] For some matrices, a square root does not exist:: >>> sqrtm([[0,1], [0,0]]) Traceback (most recent call last): ... ZeroDivisionError: matrix is numerically singular Two examples from the documentation for Matlab's ``sqrtm``:: >>> mp.dps = 15; mp.pretty = True >>> sqrtm([[7,10],[15,22]]) [1.56669890360128 1.74077655955698] [2.61116483933547 4.17786374293675] >>> >>> X = matrix(\ ... [[5,-4,1,0,0], ... [-4,6,-4,1,0], ... [1,-4,6,-4,1], ... [0,1,-4,6,-4], ... [0,0,1,-4,5]]) >>> Y = matrix(\ ... [[2,-1,-0,-0,-0], ... [-1,2,-1,0,-0], ... [0,-1,2,-1,0], ... [-0,0,-1,2,-1], ... [-0,-0,-0,-1,2]]) >>> mnorm(sqrtm(X) - Y) 4.53155328326114e-19 """ A = ctx.matrix(A) # Trivial if A*0 == A: return A prec = ctx.prec if _may_rotate: d = ctx.det(A) if abs(ctx.im(d)) < 16*ctx.eps and ctx.re(d) < 0: return ctx._sqrtm_rot(A, _may_rotate-1) try: ctx.prec += 10 tol = ctx.eps * 128 Y = A Z = I = A**0 k = 0 # Denman-Beavers iteration while 1: Yprev = Y try: Y, Z = 0.5*(Y+ctx.inverse(Z)), 0.5*(Z+ctx.inverse(Y)) except ZeroDivisionError: if _may_rotate: Y = ctx._sqrtm_rot(A, _may_rotate-1) break else: raise mag1 = ctx.mnorm(Y-Yprev, 'inf') mag2 = ctx.mnorm(Y, 'inf') if mag1 <= mag2*tol: break if _may_rotate and k > 6 and not mag1 < mag2 * 0.001: return ctx._sqrtm_rot(A, _may_rotate-1) k += 1 if k > ctx.prec: raise ctx.NoConvergence finally: ctx.prec = prec Y *= 1 return Y def logm(ctx, A): r""" Computes a logarithm of the square matrix `A`, i.e. returns a matrix `B = \log(A)` such that `\exp(B) = A`. The logarithm of a matrix, if it exists, is not unique. **Examples** Logarithms of some simple matrices:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> X = eye(3) >>> logm(X) [0.0 0.0 0.0] [0.0 0.0 0.0] [0.0 0.0 0.0] >>> logm(2*X) [0.693147180559945 0.0 0.0] [ 0.0 0.693147180559945 0.0] [ 0.0 0.0 0.693147180559945] >>> logm(expm(X)) [1.0 0.0 0.0] [0.0 1.0 0.0] [0.0 0.0 1.0] A logarithm of a complex matrix:: >>> X = matrix([[2+j, 1, 3], [1-j, 1-2*j, 1], [-4, -5, j]]) >>> B = logm(X) >>> nprint(B) [ (0.808757 + 0.107759j) (2.20752 + 0.202762j) (1.07376 - 0.773874j)] [ (0.905709 - 0.107795j) (0.0287395 - 0.824993j) (0.111619 + 0.514272j)] [(-0.930151 + 0.399512j) (-2.06266 - 0.674397j) (0.791552 + 0.519839j)] >>> chop(expm(B)) [(2.0 + 1.0j) 1.0 3.0] [(1.0 - 1.0j) (1.0 - 2.0j) 1.0] [ -4.0 -5.0 (0.0 + 1.0j)] A matrix `X` close to the identity matrix, for which `\log(\exp(X)) = \exp(\log(X)) = X` holds:: >>> X = eye(3) + hilbert(3)/4 >>> X [ 1.25 0.125 0.0833333333333333] [ 0.125 1.08333333333333 0.0625] [0.0833333333333333 0.0625 1.05] >>> logm(expm(X)) [ 1.25 0.125 0.0833333333333333] [ 0.125 1.08333333333333 0.0625] [0.0833333333333333 0.0625 1.05] >>> expm(logm(X)) [ 1.25 0.125 0.0833333333333333] [ 0.125 1.08333333333333 0.0625] [0.0833333333333333 0.0625 1.05] A logarithm of a rotation matrix, giving back the angle of the rotation:: >>> t = 3.7 >>> A = matrix([[cos(t),sin(t)],[-sin(t),cos(t)]]) >>> chop(logm(A)) [ 0.0 -2.58318530717959] [2.58318530717959 0.0] >>> (2*pi-t) 2.58318530717959 For some matrices, a logarithm does not exist:: >>> logm([[1,0], [0,0]]) Traceback (most recent call last): ... ZeroDivisionError: matrix is numerically singular Logarithm of a matrix with large entries:: >>> logm(hilbert(3) * 10**20).apply(re) [ 45.5597513593433 1.27721006042799 0.317662687717978] [ 1.27721006042799 42.5222778973542 2.24003708791604] [0.317662687717978 2.24003708791604 42.395212822267] """ A = ctx.matrix(A) prec = ctx.prec try: ctx.prec += 10 tol = ctx.eps * 128 I = A**0 B = A n = 0 while 1: B = ctx.sqrtm(B) n += 1 if ctx.mnorm(B-I, 'inf') < 0.125: break T = X = B-I L = X*0 k = 1 while 1: if k & 1: L += T / k else: L -= T / k T *= X if ctx.mnorm(T, 'inf') < tol: break k += 1 if k > ctx.prec: raise ctx.NoConvergence finally: ctx.prec = prec L *= 2**n return L def powm(ctx, A, r): r""" Computes `A^r = \exp(A \log r)` for a matrix `A` and complex number `r`. **Examples** Powers and inverse powers of a matrix:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> A = matrix([[4,1,4],[7,8,9],[10,2,11]]) >>> powm(A, 2) [ 63.0 20.0 69.0] [174.0 89.0 199.0] [164.0 48.0 179.0] >>> chop(powm(powm(A, 4), 1/4.)) [ 4.0 1.0 4.0] [ 7.0 8.0 9.0] [10.0 2.0 11.0] >>> powm(extraprec(20)(powm)(A, -4), -1/4.) [ 4.0 1.0 4.0] [ 7.0 8.0 9.0] [10.0 2.0 11.0] >>> chop(powm(powm(A, 1+0.5j), 1/(1+0.5j))) [ 4.0 1.0 4.0] [ 7.0 8.0 9.0] [10.0 2.0 11.0] >>> powm(extraprec(5)(powm)(A, -1.5), -1/(1.5)) [ 4.0 1.0 4.0] [ 7.0 8.0 9.0] [10.0 2.0 11.0] A Fibonacci-generating matrix:: >>> powm([[1,1],[1,0]], 10) [89.0 55.0] [55.0 34.0] >>> fib(10) 55.0 >>> powm([[1,1],[1,0]], 6.5) [(16.5166626964253 - 0.0121089837381789j) (10.2078589271083 + 0.0195927472575932j)] [(10.2078589271083 + 0.0195927472575932j) (6.30880376931698 - 0.0317017309957721j)] >>> (phi**6.5 - (1-phi)**6.5)/sqrt(5) (10.2078589271083 - 0.0195927472575932j) >>> powm([[1,1],[1,0]], 6.2) [ (14.3076953002666 - 0.008222855781077j) (8.81733464837593 + 0.0133048601383712j)] [(8.81733464837593 + 0.0133048601383712j) (5.49036065189071 - 0.0215277159194482j)] >>> (phi**6.2 - (1-phi)**6.2)/sqrt(5) (8.81733464837593 - 0.0133048601383712j) """ A = ctx.matrix(A) r = ctx.convert(r) prec = ctx.prec try: ctx.prec += 10 if ctx.isint(r): v = A ** int(r) elif ctx.isint(r*2): y = int(r*2) v = ctx.sqrtm(A) ** y else: v = ctx.expm(r*ctx.logm(A)) finally: ctx.prec = prec v *= 1 return v sympy-0.7.4.1/sympy/mpmath/matrices/__init__.py0000644000175000017500000000000112253362407021622 0ustar georgeskgeorgesk sympy-0.7.4.1/sympy/mpmath/matrices/matrices.py0000644000175000017500000007513112253362407021712 0ustar georgeskgeorgeskfrom ..libmp.backend import xrange # TODO: interpret list as vectors (for multiplication) rowsep = '\n' colsep = ' ' class _matrix(object): """ Numerical matrix. Specify the dimensions or the data as a nested list. Elements default to zero. Use a flat list to create a column vector easily. By default, only mpf is used to store the data. You can specify another type using force_type=type. It's possible to specify None. Make sure force_type(force_type()) is fast. Creating matrices ----------------- 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 >>> 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 ``mpmathify`` is used 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 >>> A matrix( [['0.0', '0.0'], ['0.0', '(1.0 + 1.0j)']]) You can use the keyword ``force_type`` to change the function which is called on every new element: >>> matrix(2, 5, force_type=int) matrix( [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]) 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']]) If you want to preserve the type of the elements you can use ``force_type=None``: >>> matrix([[1, 2.5], [1j, mpf(2)]], force_type=None) matrix( [[1, 2.5], [1j, '2.0']]) Convenient advanced functions are available for creating various standard matrices, see ``zeros``, ``ones``, ``diag``, ``eye``, ``randmatrix`` and ``hilbert``. 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 subtract 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): ... 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']]) >>> A * A**-1 matrix( [['1.0', '1.0842021724855e-19'], ['-2.16840434497101e-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. For vectors the p-norm is intended, usually the 1-, the 2- and the oo-norm are used. >>> x = matrix([-10, 2, 100]) >>> norm(x, 1) mpf('112.0') >>> norm(x, 2) mpf('100.5186549850325') >>> norm(x, inf) mpf('100.0') Please note that the 2-norm is the most used one, though it is more expensive to calculate than the 1- or oo-norm. It is possible to generalize some vector norms to matrix norm:: >>> A = matrix([[1, -1000], [100, 50]]) >>> mnorm(A, 1) mpf('1050.0') >>> mnorm(A, inf) mpf('1001.0') >>> mnorm(A, 'F') mpf('1006.2310867787777') The last norm (the "Frobenius-norm") is an approximation for the 2-norm, which is hard to calculate and not available. The Frobenius-norm lacks some mathematical properties you might expect from a norm. """ def __init__(self, *args, **kwargs): self.__data = {} # LU decompostion cache, this is useful when solving the same system # multiple times, when calculating the inverse and when calculating the # determinant self._LU = None convert = kwargs.get('force_type', self.ctx.convert) if isinstance(args[0], (list, tuple)): if isinstance(args[0][0], (list, tuple)): # interpret nested list as matrix A = args[0] self.__rows = len(A) self.__cols = len(A[0]) for i, row in enumerate(A): for j, a in enumerate(row): self[i, j] = convert(a) else: # interpret list as row vector v = args[0] self.__rows = len(v) self.__cols = 1 for i, e in enumerate(v): self[i, 0] = e elif isinstance(args[0], int): # create empty matrix of given dimensions if len(args) == 1: self.__rows = self.__cols = args[0] else: assert isinstance(args[1], int), 'expected int' self.__rows = args[0] self.__cols = args[1] elif isinstance(args[0], _matrix): A = args[0].copy() self.__data = A._matrix__data self.__rows = A._matrix__rows self.__cols = A._matrix__cols convert = kwargs.get('force_type', self.ctx.convert) for i in xrange(A.__rows): for j in xrange(A.__cols): A[i,j] = convert(A[i,j]) elif hasattr(args[0], 'tolist'): A = self.ctx.matrix(args[0].tolist()) self.__data = A._matrix__data self.__rows = A._matrix__rows self.__cols = A._matrix__cols else: raise TypeError('could not interpret given arguments') def apply(self, f): """ Return a copy of self with the function `f` applied elementwise. """ new = self.ctx.matrix(self.__rows, self.__cols) for i in xrange(self.__rows): for j in xrange(self.__cols): new[i,j] = f(self[i,j]) return new def __nstr__(self, n=None, **kwargs): # 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): if n: string = self.ctx.nstr(self[i,j], n, **kwargs) else: string = str(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] = "[" + colsep.join(row) + "]" return rowsep.join(res) def __str__(self): return self.__nstr__() def _toliststr(self, avoid_type=False): """ Create a list string from a matrix. If avoid_type: avoid multiple 'mpf's. """ # XXX: should be something like self.ctx._types typ = self.ctx.mpf s = '[' for i in xrange(self.__rows): s += '[' for j in xrange(self.__cols): if not avoid_type or not isinstance(self[i,j], typ): a = repr(self[i,j]) else: a = "'" + str(self[i,j]) + "'" s += a + ', ' s = s[:-2] s += '],\n ' s = s[:-3] s += ']' return s def tolist(self): """ Convert the matrix to a nested list. """ return [[self[i,j] for j in range(self.__cols)] for i in range(self.__rows)] def __repr__(self): if self.ctx.pretty: return self.__str__() s = 'matrix(\n' s += self._toliststr(avoid_type=True) + ')' return s def __get_element(self, key): ''' Fast extraction of the i,j element from the matrix This function is for private use only because is unsafe: 1. Does not check on the value of key it expects key to be a integer tuple (i,j) 2. Does not check bounds ''' if key in self.__data: return self.__data[key] else: return self.ctx.zero def __set_element(self, key, value): ''' Fast assignment of the i,j element in the matrix This function is unsafe: 1. Does not check on the value of key it expects key to be a integer tuple (i,j) 2. Does not check bounds 3. Does not check the value type ''' if value: # only store non-zeros self.__data[key] = value elif key in self.__data: del self.__data[key] def __getitem__(self, key): ''' Getitem function for mp matrix class with slice index enabled it allows the following assingments scalar to a slice of the matrix B = A[:,2:6] ''' # Convert vector to matrix indexing if isinstance(key, int) or isinstance(key,slice): # only sufficent for vectors if self.__rows == 1: key = (0, key) elif self.__cols == 1: key = (key, 0) else: raise IndexError('insufficient indices for matrix') if isinstance(key[0],slice) or isinstance(key[1],slice): #Rows if isinstance(key[0],slice): #Check bounds if (key[0].start is None or key[0].start >= 0) and \ (key[0].stop is None or key[0].stop <= self.__rows+1): # Generate indices rows = xrange(*key[0].indices(self.__rows)) else: raise IndexError('Row index out of bounds') else: # Single row rows = [key[0]] # Columns if isinstance(key[1],slice): # Check bounds if (key[1].start is None or key[1].start >= 0) and \ (key[1].stop is None or key[1].stop <= self.__cols+1): # Generate indices columns = xrange(*key[1].indices(self.__cols)) else: raise IndexError('Column index out of bounds') else: # Single column columns = [key[1]] # Create matrix slice m = self.ctx.matrix(len(rows),len(columns)) # Assign elements to the output matrix for i,x in enumerate(rows): for j,y in enumerate(columns): m.__set_element((i,j),self.__get_element((x,y))) return m else: # single element extraction if key[0] >= self.__rows or key[1] >= self.__cols: raise IndexError('matrix index out of range') if key in self.__data: return self.__data[key] else: return self.ctx.zero def __setitem__(self, key, value): # setitem function for mp matrix class with slice index enabled # it allows the following assingments # scalar to a slice of the matrix # A[:,2:6] = 2.5 # submatrix to matrix (the value matrix should be the same size as the slice size) # A[3,:] = B where A is n x m and B is n x 1 # Convert vector to matrix indexing if isinstance(key, int) or isinstance(key,slice): # only sufficent for vectors if self.__rows == 1: key = (0, key) elif self.__cols == 1: key = (key, 0) else: raise IndexError('insufficient indices for matrix') # Slice indexing if isinstance(key[0],slice) or isinstance(key[1],slice): # Rows if isinstance(key[0],slice): # Check bounds if (key[0].start is None or key[0].start >= 0) and \ (key[0].stop is None or key[0].stop <= self.__rows+1): # generate row indices rows = xrange(*key[0].indices(self.__rows)) else: raise IndexError('Row index out of bounds') else: # Single row rows = [key[0]] # Columns if isinstance(key[1],slice): # Check bounds if (key[1].start is None or key[1].start >= 0) and \ (key[1].stop is None or key[1].stop <= self.__cols+1): # Generate column indices columns = xrange(*key[1].indices(self.__cols)) else: raise IndexError('Column index out of bounds') else: # Single column columns = [key[1]] # Assign slice with a scalar if isinstance(value,self.ctx.matrix): # Assign elements to matrix if input and output dimensions match if len(rows) == value.rows and len(columns) == value.cols: for i,x in enumerate(rows): for j,y in enumerate(columns): self.__set_element((x,y), value.__get_element((i,j))) else: raise ValueError('Dimensions do not match') else: # Assign slice with scalars value = self.ctx.convert(value) for i in rows: for j in columns: self.__set_element((i,j), value) else: # Single element assingment # Check bounds if key[0] >= self.__rows or key[1] >= self.__cols: raise IndexError('matrix index out of range') # Convert and store value value = self.ctx.convert(value) if value: # only store non-zeros self.__data[key] = value elif key in self.__data: del self.__data[key] if self._LU: self._LU = None return def __iter__(self): for i in xrange(self.__rows): for j in xrange(self.__cols): yield self[i,j] def __mul__(self, other): if isinstance(other, self.ctx.matrix): # dot multiplication TODO: use Strassen's method? if self.__cols != other.__rows: raise ValueError('dimensions not compatible for multiplication') new = self.ctx.matrix(self.__rows, other.__cols) for i in xrange(self.__rows): for j in xrange(other.__cols): new[i, j] = self.ctx.fdot((self[i,k], other[k,j]) for k in xrange(other.__rows)) return new else: # try scalar multiplication new = self.ctx.matrix(self.__rows, self.__cols) for i in xrange(self.__rows): for j in xrange(self.__cols): new[i, j] = other * self[i, j] return new def __rmul__(self, other): # assume other is scalar and thus commutative assert not isinstance(other, self.ctx.matrix) return self.__mul__(other) def __pow__(self, other): # avoid cyclic import problems #from linalg import inverse if not isinstance(other, int): raise ValueError('only integer exponents are supported') if not self.__rows == self.__cols: raise ValueError('only powers of square matrices are defined') n = other if n == 0: return self.ctx.eye(self.__rows) if n < 0: n = -n neg = True else: neg = False i = n y = 1 z = self.copy() while i != 0: if i % 2 == 1: y = y * z z = z*z i = i // 2 if neg: y = self.ctx.inverse(y) return y def __div__(self, other): # assume other is scalar and do element-wise divison assert not isinstance(other, self.ctx.matrix) new = self.ctx.matrix(self.__rows, self.__cols) for i in xrange(self.__rows): for j in xrange(self.__cols): new[i,j] = self[i,j] / other return new __truediv__ = __div__ def __add__(self, other): if isinstance(other, self.ctx.matrix): if not (self.__rows == other.__rows and self.__cols == other.__cols): raise ValueError('incompatible dimensions for addition') new = self.ctx.matrix(self.__rows, self.__cols) for i in xrange(self.__rows): for j in xrange(self.__cols): new[i,j] = self[i,j] + other[i,j] return new else: # assume other is scalar and add element-wise new = self.ctx.matrix(self.__rows, self.__cols) for i in xrange(self.__rows): for j in xrange(self.__cols): new[i,j] += self[i,j] + other return new def __radd__(self, other): return self.__add__(other) def __sub__(self, other): if isinstance(other, self.ctx.matrix) and not (self.__rows == other.__rows and self.__cols == other.__cols): raise ValueError('incompatible dimensions for substraction') return self.__add__(other * (-1)) def __neg__(self): return (-1) * self def __rsub__(self, other): return -self + other def __eq__(self, other): return self.__rows == other.__rows and self.__cols == other.__cols \ and self.__data == other.__data def __len__(self): if self.rows == 1: return self.cols elif self.cols == 1: return self.rows else: return self.rows # do it like numpy def __getrows(self): return self.__rows def __setrows(self, value): for key in self.__data.copy(): if key[0] >= value: del self.__data[key] self.__rows = value rows = property(__getrows, __setrows, doc='number of rows') def __getcols(self): return self.__cols def __setcols(self, value): for key in self.__data.copy(): if key[1] >= value: del self.__data[key] self.__cols = value cols = property(__getcols, __setcols, doc='number of columns') def transpose(self): new = self.ctx.matrix(self.__cols, self.__rows) for i in xrange(self.__rows): for j in xrange(self.__cols): new[j,i] = self[i,j] return new T = property(transpose) def conjugate(self): return self.apply(self.ctx.conj) def transpose_conj(self): return self.conjugate().transpose() H = property(transpose_conj) def copy(self): new = self.ctx.matrix(self.__rows, self.__cols) new.__data = self.__data.copy() return new __copy__ = copy def column(self, n): m = self.ctx.matrix(self.rows, 1) for i in range(self.rows): m[i] = self[i,n] return m class MatrixMethods(object): def __init__(ctx): # XXX: subclass ctx.matrix = type('matrix', (_matrix,), {}) ctx.matrix.ctx = ctx ctx.matrix.convert = ctx.convert def eye(ctx, n, **kwargs): """ Create square identity matrix n x n. """ A = ctx.matrix(n, **kwargs) for i in xrange(n): A[i,i] = 1 return A def diag(ctx, diagonal, **kwargs): """ Create square diagonal matrix using given list. Examples: >>> from mpmath import diag, mp >>> mp.pretty = False >>> diag([1, 2, 3]) matrix( [['1.0', '0.0', '0.0'], ['0.0', '2.0', '0.0'], ['0.0', '0.0', '3.0']]) """ A = ctx.matrix(len(diagonal), **kwargs) for i in xrange(len(diagonal)): A[i,i] = diagonal[i] return A def zeros(ctx, *args, **kwargs): """ Create matrix m x n filled with zeros. One given dimension will create square matrix n x n. Examples: >>> from mpmath import zeros, mp >>> mp.pretty = False >>> zeros(2) matrix( [['0.0', '0.0'], ['0.0', '0.0']]) """ if len(args) == 1: m = n = args[0] elif len(args) == 2: m = args[0] n = args[1] else: raise TypeError('zeros expected at most 2 arguments, got %i' % len(args)) A = ctx.matrix(m, n, **kwargs) for i in xrange(m): for j in xrange(n): A[i,j] = 0 return A def ones(ctx, *args, **kwargs): """ Create matrix m x n filled with ones. One given dimension will create square matrix n x n. Examples: >>> from mpmath import ones, mp >>> mp.pretty = False >>> ones(2) matrix( [['1.0', '1.0'], ['1.0', '1.0']]) """ if len(args) == 1: m = n = args[0] elif len(args) == 2: m = args[0] n = args[1] else: raise TypeError('ones expected at most 2 arguments, got %i' % len(args)) A = ctx.matrix(m, n, **kwargs) for i in xrange(m): for j in xrange(n): A[i,j] = 1 return A def hilbert(ctx, m, n=None): """ Create (pseudo) hilbert matrix m x n. One given dimension will create hilbert matrix n x n. The matrix is very ill-conditioned and symmetric, positive definite if square. """ if n is None: n = m A = ctx.matrix(m, n) for i in xrange(m): for j in xrange(n): A[i,j] = ctx.one / (i + j + 1) return A def randmatrix(ctx, m, n=None, min=0, max=1, **kwargs): """ Create a random m x n matrix. All values are >= min and >> from mpmath import randmatrix >>> randmatrix(2) # doctest:+SKIP matrix( [['0.53491598236191806', '0.57195669543302752'], ['0.85589992269513615', '0.82444367501382143']]) """ if not n: n = m A = ctx.matrix(m, n, **kwargs) for i in xrange(m): for j in xrange(n): A[i,j] = ctx.rand() * (max - min) + min return A def swap_row(ctx, A, i, j): """ Swap row i with row j. """ if i == j: return if isinstance(A, ctx.matrix): for k in xrange(A.cols): A[i,k], A[j,k] = A[j,k], A[i,k] elif isinstance(A, list): A[i], A[j] = A[j], A[i] else: raise TypeError('could not interpret type') def extend(ctx, A, b): """ Extend matrix A with column b and return result. """ assert isinstance(A, ctx.matrix) assert A.rows == len(b) A = A.copy() A.cols += 1 for i in xrange(A.rows): A[i, A.cols-1] = b[i] return A def norm(ctx, x, p=2): r""" Gives the entrywise `p`-norm of an iterable *x*, i.e. the vector norm `\left(\sum_k |x_k|^p\right)^{1/p}`, for any given `1 \le p \le \infty`. Special cases: If *x* is not iterable, this just returns ``absmax(x)``. ``p=1`` gives the sum of absolute values. ``p=2`` is the standard Euclidean vector norm. ``p=inf`` gives the magnitude of the largest element. For *x* a matrix, ``p=2`` is the Frobenius norm. For operator matrix norms, use :func:`~mpmath.mnorm` instead. You can use the string 'inf' as well as float('inf') or mpf('inf') to specify the infinity norm. **Examples** >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> x = matrix([-10, 2, 100]) >>> norm(x, 1) mpf('112.0') >>> norm(x, 2) mpf('100.5186549850325') >>> norm(x, inf) mpf('100.0') """ try: iter(x) except TypeError: return ctx.absmax(x) if type(p) is not int: p = ctx.convert(p) if p == ctx.inf: return max(ctx.absmax(i) for i in x) elif p == 1: return ctx.fsum(x, absolute=1) elif p == 2: return ctx.sqrt(ctx.fsum(x, absolute=1, squared=1)) elif p > 1: return ctx.nthroot(ctx.fsum(abs(i)**p for i in x), p) else: raise ValueError('p has to be >= 1') def mnorm(ctx, A, p=1): r""" Gives the matrix (operator) `p`-norm of A. Currently ``p=1`` and ``p=inf`` are supported: ``p=1`` gives the 1-norm (maximal column sum) ``p=inf`` gives the `\infty`-norm (maximal row sum). You can use the string 'inf' as well as float('inf') or mpf('inf') ``p=2`` (not implemented) for a square matrix is the usual spectral matrix norm, i.e. the largest singular value. ``p='f'`` (or 'F', 'fro', 'Frobenius, 'frobenius') gives the Frobenius norm, which is the elementwise 2-norm. The Frobenius norm is an approximation of the spectral norm and satisfies .. math :: \frac{1}{\sqrt{\mathrm{rank}(A)}} \|A\|_F \le \|A\|_2 \le \|A\|_F The Frobenius norm lacks some mathematical properties that might be expected of a norm. For general elementwise `p`-norms, use :func:`~mpmath.norm` instead. **Examples** >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> A = matrix([[1, -1000], [100, 50]]) >>> mnorm(A, 1) mpf('1050.0') >>> mnorm(A, inf) mpf('1001.0') >>> mnorm(A, 'F') mpf('1006.2310867787777') """ A = ctx.matrix(A) if type(p) is not int: if type(p) is str and 'frobenius'.startswith(p.lower()): return ctx.norm(A, 2) p = ctx.convert(p) m, n = A.rows, A.cols if p == 1: return max(ctx.fsum((A[i,j] for i in xrange(m)), absolute=1) for j in xrange(n)) elif p == ctx.inf: return max(ctx.fsum((A[i,j] for j in xrange(n)), absolute=1) for i in xrange(m)) else: raise NotImplementedError("matrix p-norm for arbitrary p") if __name__ == '__main__': import doctest doctest.testmod() sympy-0.7.4.1/sympy/mpmath/ctx_fp.py0000644000175000017500000001471512253362407017560 0ustar georgeskgeorgeskfrom .ctx_base import StandardBaseContext import math import cmath from . import math2 from . import function_docs from .libmp import mpf_bernoulli, to_float, int_types from . import libmp class FPContext(StandardBaseContext): """ Context for fast low-precision arithmetic (53-bit precision, giving at most about 15-digit accuracy), using Python's builtin float and complex. """ def __init__(ctx): StandardBaseContext.__init__(ctx) # Override SpecialFunctions implementation ctx.loggamma = math2.loggamma ctx._bernoulli_cache = {} ctx.pretty = False ctx._init_aliases() _mpq = lambda cls, x: float(x[0])/x[1] NoConvergence = libmp.NoConvergence def _get_prec(ctx): return 53 def _set_prec(ctx, p): return def _get_dps(ctx): return 15 def _set_dps(ctx, p): return _fixed_precision = True prec = property(_get_prec, _set_prec) dps = property(_get_dps, _set_dps) zero = 0.0 one = 1.0 eps = math2.EPS inf = math2.INF ninf = math2.NINF nan = math2.NAN j = 1j # Called by SpecialFunctions.__init__() @classmethod def _wrap_specfun(cls, name, f, wrap): if wrap: def f_wrapped(ctx, *args, **kwargs): convert = ctx.convert args = [convert(a) for a in args] return f(ctx, *args, **kwargs) else: f_wrapped = f f_wrapped.__doc__ = function_docs.__dict__.get(name, f.__doc__) setattr(cls, name, f_wrapped) def bernoulli(ctx, n): cache = ctx._bernoulli_cache if n in cache: return cache[n] cache[n] = to_float(mpf_bernoulli(n, 53, 'n'), strict=True) return cache[n] pi = math2.pi e = math2.e euler = math2.euler sqrt2 = 1.4142135623730950488 sqrt5 = 2.2360679774997896964 phi = 1.6180339887498948482 ln2 = 0.69314718055994530942 ln10 = 2.302585092994045684 euler = 0.57721566490153286061 catalan = 0.91596559417721901505 khinchin = 2.6854520010653064453 apery = 1.2020569031595942854 glaisher = 1.2824271291006226369 absmin = absmax = abs def is_special(ctx, x): return x - x != 0.0 def isnan(ctx, x): return x != x def isinf(ctx, x): return abs(x) == math2.INF def isnormal(ctx, x): if x: return x - x == 0.0 return False def isnpint(ctx, x): if type(x) is complex: if x.imag: return False x = x.real return x <= 0.0 and round(x) == x mpf = float mpc = complex def convert(ctx, x): try: return float(x) except: return complex(x) power = staticmethod(math2.pow) sqrt = staticmethod(math2.sqrt) exp = staticmethod(math2.exp) ln = log = staticmethod(math2.log) cos = staticmethod(math2.cos) sin = staticmethod(math2.sin) tan = staticmethod(math2.tan) cos_sin = staticmethod(math2.cos_sin) acos = staticmethod(math2.acos) asin = staticmethod(math2.asin) atan = staticmethod(math2.atan) cosh = staticmethod(math2.cosh) sinh = staticmethod(math2.sinh) tanh = staticmethod(math2.tanh) gamma = staticmethod(math2.gamma) rgamma = staticmethod(math2.rgamma) fac = factorial = staticmethod(math2.factorial) floor = staticmethod(math2.floor) ceil = staticmethod(math2.ceil) cospi = staticmethod(math2.cospi) sinpi = staticmethod(math2.sinpi) cbrt = staticmethod(math2.cbrt) _nthroot = staticmethod(math2.nthroot) _ei = staticmethod(math2.ei) _e1 = staticmethod(math2.e1) _zeta = _zeta_int = staticmethod(math2.zeta) # XXX: math2 def arg(ctx, z): z = complex(z) return math.atan2(z.imag, z.real) def expj(ctx, x): return ctx.exp(ctx.j*x) def expjpi(ctx, x): return ctx.exp(ctx.j*ctx.pi*x) ldexp = math.ldexp frexp = math.frexp def mag(ctx, z): if z: return ctx.frexp(abs(z))[1] return ctx.ninf def isint(ctx, z): if hasattr(z, "imag"): # float/int don't have .real/.imag in py2.5 if z.imag: return False z = z.real try: return z == int(z) except: return False def nint_distance(ctx, z): if hasattr(z, "imag"): # float/int don't have .real/.imag in py2.5 n = round(z.real) else: n = round(z) if n == z: return n, ctx.ninf return n, ctx.mag(abs(z-n)) def _convert_param(ctx, z): if type(z) is tuple: p, q = z return ctx.mpf(p) / q, 'R' if hasattr(z, "imag"): # float/int don't have .real/.imag in py2.5 intz = int(z.real) else: intz = int(z) if z == intz: return intz, 'Z' return z, 'R' def _is_real_type(ctx, z): return isinstance(z, float) or isinstance(z, int_types) def _is_complex_type(ctx, z): return isinstance(z, complex) def hypsum(ctx, p, q, types, coeffs, z, maxterms=6000, **kwargs): coeffs = list(coeffs) num = range(p) den = range(p,p+q) tol = ctx.eps s = t = 1.0 k = 0 while 1: for i in num: t *= (coeffs[i]+k) for i in den: t /= (coeffs[i]+k) k += 1 t /= k t *= z s += t if abs(t) < tol: return s if k > maxterms: raise ctx.NoConvergence def atan2(ctx, x, y): return math.atan2(x, y) def psi(ctx, m, z): m = int(m) if m == 0: return ctx.digamma(z) return (-1)**(m+1) * ctx.fac(m) * ctx.zeta(m+1, z) digamma = staticmethod(math2.digamma) def harmonic(ctx, x): x = ctx.convert(x) if x == 0 or x == 1: return x return ctx.digamma(x+1) + ctx.euler nstr = str def to_fixed(ctx, x, prec): return int(math.ldexp(x, prec)) def rand(ctx): import random return random.random() _erf = staticmethod(math2.erf) _erfc = staticmethod(math2.erfc) def sum_accurately(ctx, terms, check_step=1): s = ctx.zero k = 0 for term in terms(): s += term if (not k % check_step) and term: if abs(term) <= 1e-18*abs(s): break k += 1 return s sympy-0.7.4.1/sympy/mpmath/identification.py0000644000175000017500000007040112253362407021260 0ustar georgeskgeorgesk""" Implements the PSLQ algorithm for integer relation detection, and derivative algorithms for constant recognition. """ from .libmp.backend import xrange from .libmp import int_types, sqrt_fixed # round to nearest integer (can be done more elegantly...) def round_fixed(x, prec): return ((x + (1<<(prec-1))) >> prec) << prec class IdentificationMethods(object): pass def pslq(ctx, x, tol=None, maxcoeff=1000, maxsteps=100, verbose=False): r""" Given a vector of real numbers `x = [x_0, x_1, ..., x_n]`, ``pslq(x)`` uses the PSLQ algorithm to find a list of integers `[c_0, c_1, ..., c_n]` such that .. math :: |c_1 x_1 + c_2 x_2 + ... + c_n x_n| < \mathrm{tol} and such that `\max |c_k| < \mathrm{maxcoeff}`. If no such vector exists, :func:`~mpmath.pslq` returns ``None``. The tolerance defaults to 3/4 of the working precision. **Examples** Find rational approximations for `\pi`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> pslq([-1, pi], tol=0.01) [22, 7] >>> pslq([-1, pi], tol=0.001) [355, 113] >>> mpf(22)/7; mpf(355)/113; +pi 3.14285714285714 3.14159292035398 3.14159265358979 Pi is not a rational number with denominator less than 1000:: >>> pslq([-1, pi]) >>> To within the standard precision, it can however be approximated by at least one rational number with denominator less than `10^{12}`:: >>> p, q = pslq([-1, pi], maxcoeff=10**12) >>> print(p); print(q) 238410049439 75888275702 >>> mpf(p)/q 3.14159265358979 The PSLQ algorithm can be applied to long vectors. For example, we can investigate the rational (in)dependence of integer square roots:: >>> mp.dps = 30 >>> pslq([sqrt(n) for n in range(2, 5+1)]) >>> >>> pslq([sqrt(n) for n in range(2, 6+1)]) >>> >>> pslq([sqrt(n) for n in range(2, 8+1)]) [2, 0, 0, 0, 0, 0, -1] **Machin formulas** A famous formula for `\pi` is Machin's, .. math :: \frac{\pi}{4} = 4 \operatorname{acot} 5 - \operatorname{acot} 239 There are actually infinitely many formulas of this type. Two others are .. math :: \frac{\pi}{4} = \operatorname{acot} 1 \frac{\pi}{4} = 12 \operatorname{acot} 49 + 32 \operatorname{acot} 57 + 5 \operatorname{acot} 239 + 12 \operatorname{acot} 110443 We can easily verify the formulas using the PSLQ algorithm:: >>> mp.dps = 30 >>> pslq([pi/4, acot(1)]) [1, -1] >>> pslq([pi/4, acot(5), acot(239)]) [1, -4, 1] >>> pslq([pi/4, acot(49), acot(57), acot(239), acot(110443)]) [1, -12, -32, 5, -12] We could try to generate a custom Machin-like formula by running the PSLQ algorithm with a few inverse cotangent values, for example acot(2), acot(3) ... acot(10). Unfortunately, there is a linear dependence among these values, resulting in only that dependence being detected, with a zero coefficient for `\pi`:: >>> pslq([pi] + [acot(n) for n in range(2,11)]) [0, 1, -1, 0, 0, 0, -1, 0, 0, 0] We get better luck by removing linearly dependent terms:: >>> pslq([pi] + [acot(n) for n in range(2,11) if n not in (3, 5)]) [1, -8, 0, 0, 4, 0, 0, 0] In other words, we found the following formula:: >>> 8*acot(2) - 4*acot(7) 3.14159265358979323846264338328 >>> +pi 3.14159265358979323846264338328 **Algorithm** This is a fairly direct translation to Python of the pseudocode given by David Bailey, "The PSLQ Integer Relation Algorithm": http://www.cecm.sfu.ca/organics/papers/bailey/paper/html/node3.html The present implementation uses fixed-point instead of floating-point arithmetic, since this is significantly (about 7x) faster. """ n = len(x) assert n >= 2 # At too low precision, the algorithm becomes meaningless prec = ctx.prec assert prec >= 53 if verbose and prec // max(2,n) < 5: print("Warning: precision for PSLQ may be too low") target = int(prec * 0.75) if tol is None: tol = ctx.mpf(2)**(-target) else: tol = ctx.convert(tol) extra = 60 prec += extra if verbose: print("PSLQ using prec %i and tol %s" % (prec, ctx.nstr(tol))) tol = ctx.to_fixed(tol, prec) assert tol # Convert to fixed-point numbers. The dummy None is added so we can # use 1-based indexing. (This just allows us to be consistent with # Bailey's indexing. The algorithm is 100 lines long, so debugging # a single wrong index can be painful.) x = [None] + [ctx.to_fixed(ctx.mpf(xk), prec) for xk in x] # Sanity check on magnitudes minx = min(abs(xx) for xx in x[1:]) if not minx: raise ValueError("PSLQ requires a vector of nonzero numbers") if minx < tol//100: if verbose: print("STOPPING: (one number is too small)") return None g = sqrt_fixed((4<> prec) s[k] = sqrt_fixed(t, prec) t = s[1] y = x[:] for k in xrange(1, n+1): y[k] = (x[k] << prec) // t s[k] = (s[k] << prec) // t # step 3 for i in xrange(1, n+1): for j in xrange(i+1, n): H[i,j] = 0 if i <= n-1: if s[i]: H[i,i] = (s[i+1] << prec) // s[i] else: H[i,i] = 0 for j in range(1, i): sjj1 = s[j]*s[j+1] if sjj1: H[i,j] = ((-y[i]*y[j])<> prec) for k in xrange(1, j+1): H[i,k] = H[i,k] - (t*H[j,k] >> prec) for k in xrange(1, n+1): A[i,k] = A[i,k] - (t*A[j,k] >> prec) B[k,j] = B[k,j] + (t*B[k,i] >> prec) # Main algorithm for REP in range(maxsteps): # Step 1 m = -1 szmax = -1 for i in range(1, n): h = H[i,i] sz = (g**i * abs(h)) >> (prec*(i-1)) if sz > szmax: m = i szmax = sz # Step 2 y[m], y[m+1] = y[m+1], y[m] tmp = {} for i in xrange(1,n+1): H[m,i], H[m+1,i] = H[m+1,i], H[m,i] for i in xrange(1,n+1): A[m,i], A[m+1,i] = A[m+1,i], A[m,i] for i in xrange(1,n+1): B[i,m], B[i,m+1] = B[i,m+1], B[i,m] # Step 3 if m <= n - 2: t0 = sqrt_fixed((H[m,m]**2 + H[m,m+1]**2)>>prec, prec) # A zero element probably indicates that the precision has # been exhausted. XXX: this could be spurious, due to # using fixed-point arithmetic if not t0: break t1 = (H[m,m] << prec) // t0 t2 = (H[m,m+1] << prec) // t0 for i in xrange(m, n+1): t3 = H[i,m] t4 = H[i,m+1] H[i,m] = (t1*t3+t2*t4) >> prec H[i,m+1] = (-t2*t3+t1*t4) >> prec # Step 4 for i in xrange(m+1, n+1): for j in xrange(min(i-1, m+1), 0, -1): try: t = round_fixed((H[i,j] << prec)//H[j,j], prec) # Precision probably exhausted except ZeroDivisionError: break y[j] = y[j] + ((t*y[i]) >> prec) for k in xrange(1, j+1): H[i,k] = H[i,k] - (t*H[j,k] >> prec) for k in xrange(1, n+1): A[i,k] = A[i,k] - (t*A[j,k] >> prec) B[k,j] = B[k,j] + (t*B[k,i] >> prec) # Until a relation is found, the error typically decreases # slowly (e.g. a factor 1-10) with each step TODO: we could # compare err from two successive iterations. If there is a # large drop (several orders of magnitude), that indicates a # "high quality" relation was detected. Reporting this to # the user somehow might be useful. best_err = maxcoeff<> prec) for j in \ range(1,n+1)] if max(abs(v) for v in vec) < maxcoeff: if verbose: print("FOUND relation at iter %i/%i, error: %s" % \ (REP, maxsteps, ctx.nstr(err / ctx.mpf(2)**prec, 1))) return vec best_err = min(err, best_err) # Calculate a lower bound for the norm. We could do this # more exactly (using the Euclidean norm) but there is probably # no practical benefit. recnorm = max(abs(h) for h in H.values()) if recnorm: norm = ((1 << (2*prec)) // recnorm) >> prec norm //= 100 else: norm = ctx.inf if verbose: print("%i/%i: Error: %8s Norm: %s" % \ (REP, maxsteps, ctx.nstr(best_err / ctx.mpf(2)**prec, 1), norm)) if norm >= maxcoeff: break if verbose: print("CANCELLING after step %i/%i." % (REP, maxsteps)) print("Could not find an integer relation. Norm bound: %s" % norm) return None def findpoly(ctx, x, n=1, **kwargs): r""" ``findpoly(x, n)`` returns the coefficients of an integer polynomial `P` of degree at most `n` such that `P(x) \approx 0`. If no polynomial having `x` as a root can be found, :func:`~mpmath.findpoly` returns ``None``. :func:`~mpmath.findpoly` works by successively calling :func:`~mpmath.pslq` with the vectors `[1, x]`, `[1, x, x^2]`, `[1, x, x^2, x^3]`, ..., `[1, x, x^2, .., x^n]` as input. Keyword arguments given to :func:`~mpmath.findpoly` are forwarded verbatim to :func:`~mpmath.pslq`. In particular, you can specify a tolerance for `P(x)` with ``tol`` and a maximum permitted coefficient size with ``maxcoeff``. For large values of `n`, it is recommended to run :func:`~mpmath.findpoly` at high precision; preferably 50 digits or more. **Examples** By default (degree `n = 1`), :func:`~mpmath.findpoly` simply finds a linear polynomial with a rational root:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> findpoly(0.7) [-10, 7] The generated coefficient list is valid input to ``polyval`` and ``polyroots``:: >>> nprint(polyval(findpoly(phi, 2), phi), 1) -2.0e-16 >>> for r in polyroots(findpoly(phi, 2)): ... print(r) ... -0.618033988749895 1.61803398874989 Numbers of the form `m + n \sqrt p` for integers `(m, n, p)` are solutions to quadratic equations. As we find here, `1+\sqrt 2` is a root of the polynomial `x^2 - 2x - 1`:: >>> findpoly(1+sqrt(2), 2) [1, -2, -1] >>> findroot(lambda x: x**2 - 2*x - 1, 1) 2.4142135623731 Despite only containing square roots, the following number results in a polynomial of degree 4:: >>> findpoly(sqrt(2)+sqrt(3), 4) [1, 0, -10, 0, 1] In fact, `x^4 - 10x^2 + 1` is the *minimal polynomial* of `r = \sqrt 2 + \sqrt 3`, meaning that a rational polynomial of lower degree having `r` as a root does not exist. Given sufficient precision, :func:`~mpmath.findpoly` will usually find the correct minimal polynomial of a given algebraic number. **Non-algebraic numbers** If :func:`~mpmath.findpoly` fails to find a polynomial with given coefficient size and tolerance constraints, that means no such polynomial exists. We can verify that `\pi` is not an algebraic number of degree 3 with coefficients less than 1000:: >>> mp.dps = 15 >>> findpoly(pi, 3) >>> It is always possible to find an algebraic approximation of a number using one (or several) of the following methods: 1. Increasing the permitted degree 2. Allowing larger coefficients 3. Reducing the tolerance One example of each method is shown below:: >>> mp.dps = 15 >>> findpoly(pi, 4) [95, -545, 863, -183, -298] >>> findpoly(pi, 3, maxcoeff=10000) [836, -1734, -2658, -457] >>> findpoly(pi, 3, tol=1e-7) [-4, 22, -29, -2] It is unknown whether Euler's constant is transcendental (or even irrational). We can use :func:`~mpmath.findpoly` to check that if is an algebraic number, its minimal polynomial must have degree at least 7 and a coefficient of magnitude at least 1000000:: >>> mp.dps = 200 >>> findpoly(euler, 6, maxcoeff=10**6, tol=1e-100, maxsteps=1000) >>> Note that the high precision and strict tolerance is necessary for such high-degree runs, since otherwise unwanted low-accuracy approximations will be detected. It may also be necessary to set maxsteps high to prevent a premature exit (before the coefficient bound has been reached). Running with ``verbose=True`` to get an idea what is happening can be useful. """ x = ctx.mpf(x) assert n >= 1 if x == 0: return [1, 0] xs = [ctx.mpf(1)] for i in range(1,n+1): xs.append(x**i) a = ctx.pslq(xs, **kwargs) if a is not None: return a[::-1] def fracgcd(p, q): x, y = p, q while y: x, y = y, x % y if x != 1: p //= x q //= x if q == 1: return p return p, q def pslqstring(r, constants): q = r[0] r = r[1:] s = [] for i in range(len(r)): p = r[i] if p: z = fracgcd(-p,q) cs = constants[i][1] if cs == '1': cs = '' else: cs = '*' + cs if isinstance(z, int_types): if z > 0: term = str(z) + cs else: term = ("(%s)" % z) + cs else: term = ("(%s/%s)" % z) + cs s.append(term) s = ' + '.join(s) if '+' in s or '*' in s: s = '(' + s + ')' return s or '0' def prodstring(r, constants): q = r[0] r = r[1:] num = [] den = [] for i in range(len(r)): p = r[i] if p: z = fracgcd(-p,q) cs = constants[i][1] if isinstance(z, int_types): if abs(z) == 1: t = cs else: t = '%s**%s' % (cs, abs(z)) ([num,den][z<0]).append(t) else: t = '%s**(%s/%s)' % (cs, abs(z[0]), z[1]) ([num,den][z[0]<0]).append(t) num = '*'.join(num) den = '*'.join(den) if num and den: return "(%s)/(%s)" % (num, den) if num: return num if den: return "1/(%s)" % den def quadraticstring(ctx,t,a,b,c): if c < 0: a,b,c = -a,-b,-c u1 = (-b+ctx.sqrt(b**2-4*a*c))/(2*c) u2 = (-b-ctx.sqrt(b**2-4*a*c))/(2*c) if abs(u1-t) < abs(u2-t): if b: s = '((%s+sqrt(%s))/%s)' % (-b,b**2-4*a*c,2*c) else: s = '(sqrt(%s)/%s)' % (-4*a*c,2*c) else: if b: s = '((%s-sqrt(%s))/%s)' % (-b,b**2-4*a*c,2*c) else: s = '(-sqrt(%s)/%s)' % (-4*a*c,2*c) return s # Transformation y = f(x,c), with inverse function x = f(y,c) # The third entry indicates whether the transformation is # redundant when c = 1 transforms = [ (lambda ctx,x,c: x*c, '$y/$c', 0), (lambda ctx,x,c: x/c, '$c*$y', 1), (lambda ctx,x,c: c/x, '$c/$y', 0), (lambda ctx,x,c: (x*c)**2, 'sqrt($y)/$c', 0), (lambda ctx,x,c: (x/c)**2, '$c*sqrt($y)', 1), (lambda ctx,x,c: (c/x)**2, '$c/sqrt($y)', 0), (lambda ctx,x,c: c*x**2, 'sqrt($y)/sqrt($c)', 1), (lambda ctx,x,c: x**2/c, 'sqrt($c)*sqrt($y)', 1), (lambda ctx,x,c: c/x**2, 'sqrt($c)/sqrt($y)', 1), (lambda ctx,x,c: ctx.sqrt(x*c), '$y**2/$c', 0), (lambda ctx,x,c: ctx.sqrt(x/c), '$c*$y**2', 1), (lambda ctx,x,c: ctx.sqrt(c/x), '$c/$y**2', 0), (lambda ctx,x,c: c*ctx.sqrt(x), '$y**2/$c**2', 1), (lambda ctx,x,c: ctx.sqrt(x)/c, '$c**2*$y**2', 1), (lambda ctx,x,c: c/ctx.sqrt(x), '$c**2/$y**2', 1), (lambda ctx,x,c: ctx.exp(x*c), 'log($y)/$c', 0), (lambda ctx,x,c: ctx.exp(x/c), '$c*log($y)', 1), (lambda ctx,x,c: ctx.exp(c/x), '$c/log($y)', 0), (lambda ctx,x,c: c*ctx.exp(x), 'log($y/$c)', 1), (lambda ctx,x,c: ctx.exp(x)/c, 'log($c*$y)', 1), (lambda ctx,x,c: c/ctx.exp(x), 'log($c/$y)', 0), (lambda ctx,x,c: ctx.ln(x*c), 'exp($y)/$c', 0), (lambda ctx,x,c: ctx.ln(x/c), '$c*exp($y)', 1), (lambda ctx,x,c: ctx.ln(c/x), '$c/exp($y)', 0), (lambda ctx,x,c: c*ctx.ln(x), 'exp($y/$c)', 1), (lambda ctx,x,c: ctx.ln(x)/c, 'exp($c*$y)', 1), (lambda ctx,x,c: c/ctx.ln(x), 'exp($c/$y)', 0), ] def identify(ctx, x, constants=[], tol=None, maxcoeff=1000, full=False, verbose=False): """ Given a real number `x`, ``identify(x)`` attempts to find an exact formula for `x`. This formula is returned as a string. If no match is found, ``None`` is returned. With ``full=True``, a list of matching formulas is returned. As a simple example, :func:`~mpmath.identify` will find an algebraic formula for the golden ratio:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> identify(phi) '((1+sqrt(5))/2)' :func:`~mpmath.identify` can identify simple algebraic numbers and simple combinations of given base constants, as well as certain basic transformations thereof. More specifically, :func:`~mpmath.identify` looks for the following: 1. Fractions 2. Quadratic algebraic numbers 3. Rational linear combinations of the base constants 4. Any of the above after first transforming `x` into `f(x)` where `f(x)` is `1/x`, `\sqrt x`, `x^2`, `\log x` or `\exp x`, either directly or with `x` or `f(x)` multiplied or divided by one of the base constants 5. Products of fractional powers of the base constants and small integers Base constants can be given as a list of strings representing mpmath expressions (:func:`~mpmath.identify` will ``eval`` the strings to numerical values and use the original strings for the output), or as a dict of formula:value pairs. In order not to produce spurious results, :func:`~mpmath.identify` should be used with high precision; preferably 50 digits or more. **Examples** Simple identifications can be performed safely at standard precision. Here the default recognition of rational, algebraic, and exp/log of algebraic numbers is demonstrated:: >>> mp.dps = 15 >>> identify(0.22222222222222222) '(2/9)' >>> identify(1.9662210973805663) 'sqrt(((24+sqrt(48))/8))' >>> identify(4.1132503787829275) 'exp((sqrt(8)/2))' >>> identify(0.881373587019543) 'log(((2+sqrt(8))/2))' By default, :func:`~mpmath.identify` does not recognize `\pi`. At standard precision it finds a not too useful approximation. At slightly increased precision, this approximation is no longer accurate enough and :func:`~mpmath.identify` more correctly returns ``None``:: >>> identify(pi) '(2**(176/117)*3**(20/117)*5**(35/39))/(7**(92/117))' >>> mp.dps = 30 >>> identify(pi) >>> Numbers such as `\pi`, and simple combinations of user-defined constants, can be identified if they are provided explicitly:: >>> identify(3*pi-2*e, ['pi', 'e']) '(3*pi + (-2)*e)' Here is an example using a dict of constants. Note that the constants need not be "atomic"; :func:`~mpmath.identify` can just as well express the given number in terms of expressions given by formulas:: >>> identify(pi+e, {'a':pi+2, 'b':2*e}) '((-2) + 1*a + (1/2)*b)' Next, we attempt some identifications with a set of base constants. It is necessary to increase the precision a bit. >>> mp.dps = 50 >>> base = ['sqrt(2)','pi','log(2)'] >>> identify(0.25, base) '(1/4)' >>> identify(3*pi + 2*sqrt(2) + 5*log(2)/7, base) '(2*sqrt(2) + 3*pi + (5/7)*log(2))' >>> identify(exp(pi+2), base) 'exp((2 + 1*pi))' >>> identify(1/(3+sqrt(2)), base) '((3/7) + (-1/7)*sqrt(2))' >>> identify(sqrt(2)/(3*pi+4), base) 'sqrt(2)/(4 + 3*pi)' >>> identify(5**(mpf(1)/3)*pi*log(2)**2, base) '5**(1/3)*pi*log(2)**2' An example of an erroneous solution being found when too low precision is used:: >>> mp.dps = 15 >>> identify(1/(3*pi-4*e+sqrt(8)), ['pi', 'e', 'sqrt(2)']) '((11/25) + (-158/75)*pi + (76/75)*e + (44/15)*sqrt(2))' >>> mp.dps = 50 >>> identify(1/(3*pi-4*e+sqrt(8)), ['pi', 'e', 'sqrt(2)']) '1/(3*pi + (-4)*e + 2*sqrt(2))' **Finding approximate solutions** The tolerance ``tol`` defaults to 3/4 of the working precision. Lowering the tolerance is useful for finding approximate matches. We can for example try to generate approximations for pi:: >>> mp.dps = 15 >>> identify(pi, tol=1e-2) '(22/7)' >>> identify(pi, tol=1e-3) '(355/113)' >>> identify(pi, tol=1e-10) '(5**(339/269))/(2**(64/269)*3**(13/269)*7**(92/269))' With ``full=True``, and by supplying a few base constants, ``identify`` can generate almost endless lists of approximations for any number (the output below has been truncated to show only the first few):: >>> for p in identify(pi, ['e', 'catalan'], tol=1e-5, full=True): ... print(p) ... # doctest: +ELLIPSIS e/log((6 + (-4/3)*e)) (3**3*5*e*catalan**2)/(2*7**2) sqrt(((-13) + 1*e + 22*catalan)) log(((-6) + 24*e + 4*catalan)/e) exp(catalan*((-1/5) + (8/15)*e)) catalan*(6 + (-6)*e + 15*catalan) sqrt((5 + 26*e + (-3)*catalan))/e e*sqrt(((-27) + 2*e + 25*catalan)) log(((-1) + (-11)*e + 59*catalan)) ((3/20) + (21/20)*e + (3/20)*catalan) ... The numerical values are roughly as close to `\pi` as permitted by the specified tolerance: >>> e/log(6-4*e/3) 3.14157719846001 >>> 135*e*catalan**2/98 3.14166950419369 >>> sqrt(e-13+22*catalan) 3.14158000062992 >>> log(24*e-6+4*catalan)-1 3.14158791577159 **Symbolic processing** The output formula can be evaluated as a Python expression. Note however that if fractions (like '2/3') are present in the formula, Python's :func:`~mpmath.eval()` may erroneously perform integer division. Note also that the output is not necessarily in the algebraically simplest form:: >>> identify(sqrt(2)) '(sqrt(8)/2)' As a solution to both problems, consider using SymPy's :func:`~mpmath.sympify` to convert the formula into a symbolic expression. SymPy can be used to pretty-print or further simplify the formula symbolically:: >>> from sympy import sympify >>> sympify(identify(sqrt(2))) sqrt(2) Sometimes :func:`~mpmath.identify` can simplify an expression further than a symbolic algorithm:: >>> from sympy import simplify >>> x = sympify('-1/(-3/2+(1/2)*sqrt(5))*sqrt(3/2-1/2*sqrt(5))') >>> x 1/sqrt(3/2 - sqrt(5)/2) >>> x = simplify(x) >>> x 2/sqrt(6 - 2*sqrt(5)) >>> mp.dps = 30 >>> x = sympify(identify(x.evalf(30))) >>> x 1/2 + sqrt(5)/2 (In fact, this functionality is available directly in SymPy as the function :func:`~mpmath.nsimplify`, which is essentially a wrapper for :func:`~mpmath.identify`.) **Miscellaneous issues and limitations** The input `x` must be a real number. All base constants must be positive real numbers and must not be rationals or rational linear combinations of each other. The worst-case computation time grows quickly with the number of base constants. Already with 3 or 4 base constants, :func:`~mpmath.identify` may require several seconds to finish. To search for relations among a large number of constants, you should consider using :func:`~mpmath.pslq` directly. The extended transformations are applied to x, not the constants separately. As a result, ``identify`` will for example be able to recognize ``exp(2*pi+3)`` with ``pi`` given as a base constant, but not ``2*exp(pi)+3``. It will be able to recognize the latter if ``exp(pi)`` is given explicitly as a base constant. """ solutions = [] def addsolution(s): if verbose: print("Found: ", s) solutions.append(s) x = ctx.mpf(x) # Further along, x will be assumed positive if x == 0: if full: return ['0'] else: return '0' if x < 0: sol = ctx.identify(-x, constants, tol, maxcoeff, full, verbose) if sol is None: return sol if full: return ["-(%s)"%s for s in sol] else: return "-(%s)" % sol if tol: tol = ctx.mpf(tol) else: tol = ctx.eps**0.7 M = maxcoeff if constants: if isinstance(constants, dict): constants = [(ctx.mpf(v), name) for (name, v) in constants.items()] else: namespace = dict((name, getattr(ctx,name)) for name in dir(ctx)) constants = [(eval(p, namespace), p) for p in constants] else: constants = [] # We always want to find at least rational terms if 1 not in [value for (name, value) in constants]: constants = [(ctx.mpf(1), '1')] + constants # PSLQ with simple algebraic and functional transformations for ft, ftn, red in transforms: for c, cn in constants: if red and cn == '1': continue t = ft(ctx,x,c) # Prevent exponential transforms from wreaking havoc if abs(t) > M**2 or abs(t) < tol: continue # Linear combination of base constants r = ctx.pslq([t] + [a[0] for a in constants], tol, M) s = None if r is not None and max(abs(uw) for uw in r) <= M and r[0]: s = pslqstring(r, constants) # Quadratic algebraic numbers else: q = ctx.pslq([ctx.one, t, t**2], tol, M) if q is not None and len(q) == 3 and q[2]: aa, bb, cc = q if max(abs(aa),abs(bb),abs(cc)) <= M: s = quadraticstring(ctx,t,aa,bb,cc) if s: if cn == '1' and ('/$c' in ftn): s = ftn.replace('$y', s).replace('/$c', '') else: s = ftn.replace('$y', s).replace('$c', cn) addsolution(s) if not full: return solutions[0] if verbose: print(".") # Check for a direct multiplicative formula if x != 1: # Allow fractional powers of fractions ilogs = [2,3,5,7] # Watch out for existing fractional powers of fractions logs = [] for a, s in constants: if not sum(bool(ctx.findpoly(ctx.ln(a)/ctx.ln(i),1)) for i in ilogs): logs.append((ctx.ln(a), s)) logs = [(ctx.ln(i),str(i)) for i in ilogs] + logs r = ctx.pslq([ctx.ln(x)] + [a[0] for a in logs], tol, M) if r is not None and max(abs(uw) for uw in r) <= M and r[0]: addsolution(prodstring(r, logs)) if not full: return solutions[0] if full: return sorted(solutions, key=len) else: return None IdentificationMethods.pslq = pslq IdentificationMethods.findpoly = findpoly IdentificationMethods.identify = identify if __name__ == '__main__': import doctest doctest.testmod() sympy-0.7.4.1/sympy/mpmath/libmp/0000755000175000017500000000000012253362407017016 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/mpmath/libmp/exec_py2.py0000644000175000017500000000010712253362407021104 0ustar georgeskgeorgeskdef exec_(string, globals, locals): exec string in globals, locals sympy-0.7.4.1/sympy/mpmath/libmp/backend.py0000644000175000017500000000551112253362407020761 0ustar georgeskgeorgeskimport os import sys #----------------------------------------------------------------------------# # Support GMPY for high-speed large integer arithmetic. # # # # To allow an external module to handle arithmetic, we need to make sure # # that all high-precision variables are declared of the correct type. MPZ # # is the constructor for the high-precision type. It defaults to Python's # # long type but can be assinged another type, typically gmpy.mpz. # # # # MPZ must be used for the mantissa component of an mpf and must be used # # for internal fixed-point operations. # # # # Side-effects # # 1) "is" cannot be used to test for special values. Must use "==". # # 2) There are bugs in GMPY prior to v1.02 so we must use v1.03 or later. # #----------------------------------------------------------------------------# # So we can import it from this module gmpy = None sage = None sage_utils = None try: xrange python3 = False except NameError: python3 = True BACKEND = 'python' if not python3: MPZ = long xrange = xrange basestring = basestring from .exec_py2 import exec_ else: MPZ = int xrange = range basestring = str from .exec_py3 import exec_ # Define constants for calculating hash on Python 3.2. if sys.version >= "3.2": HASH_MODULUS = sys.hash_info.modulus if sys.hash_info.width == 32: HASH_BITS = 31 else: HASH_BITS = 61 else: HASH_MODULUS = None HASH_BITS = None if 'MPMATH_NOGMPY' not in os.environ: try: try: import gmpy2 as gmpy except ImportError: try: import gmpy except ImportError: raise ImportError if gmpy.version() >= '1.13': BACKEND = 'gmpy' MPZ = gmpy.mpz except: pass if 'MPMATH_NOSAGE' not in os.environ: try: import sage.all import sage.libs.mpmath.utils as _sage_utils sage = sage.all sage_utils = _sage_utils BACKEND = 'sage' MPZ = sage.Integer except: pass if 'MPMATH_STRICT' in os.environ: STRICT = True else: STRICT = False MPZ_TYPE = type(MPZ(0)) MPZ_ZERO = MPZ(0) MPZ_ONE = MPZ(1) MPZ_TWO = MPZ(2) MPZ_THREE = MPZ(3) MPZ_FIVE = MPZ(5) try: if BACKEND == 'python': int_types = (int, long) else: int_types = (int, long, MPZ_TYPE) except NameError: if BACKEND == 'python': int_types = (int,) else: int_types = (int, MPZ_TYPE) sympy-0.7.4.1/sympy/mpmath/libmp/libhyper.py0000644000175000017500000010732412253362407021215 0ustar georgeskgeorgesk""" This module implements computation of hypergeometric and related functions. In particular, it provides code for generic summation of hypergeometric series. Optimized versions for various special cases are also provided. """ import operator import math from .backend import MPZ_ZERO, MPZ_ONE, BACKEND, xrange, exec_ from .libintmath import gcd from .libmpf import (\ ComplexResult, round_fast, round_nearest, negative_rnd, bitcount, to_fixed, from_man_exp, from_int, to_int, from_rational, fzero, fone, fnone, ftwo, finf, fninf, fnan, mpf_sign, mpf_add, mpf_abs, mpf_pos, mpf_cmp, mpf_lt, mpf_le, mpf_gt, mpf_min_max, mpf_perturb, mpf_neg, mpf_shift, mpf_sub, mpf_mul, mpf_div, sqrt_fixed, mpf_sqrt, mpf_rdiv_int, mpf_pow_int, to_rational, ) from .libelefun import (\ mpf_pi, mpf_exp, mpf_log, pi_fixed, mpf_cos_sin, mpf_cos, mpf_sin, mpf_sqrt, agm_fixed, ) from .libmpc import (\ mpc_one, mpc_sub, mpc_mul_mpf, mpc_mul, mpc_neg, complex_int_pow, mpc_div, mpc_add_mpf, mpc_sub_mpf, mpc_log, mpc_add, mpc_pos, mpc_shift, mpc_is_infnan, mpc_zero, mpc_sqrt, mpc_abs, mpc_mpf_div, mpc_square, mpc_exp ) from .libintmath import ifac from .gammazeta import mpf_gamma_int, mpf_euler, euler_fixed class NoConvergence(Exception): pass #-----------------------------------------------------------------------# # # # Generic hypergeometric series # # # #-----------------------------------------------------------------------# """ TODO: 1. proper mpq parsing 2. imaginary z special-cased (also: rational, integer?) 3. more clever handling of series that don't converge because of stupid upwards rounding 4. checking for cancellation """ def make_hyp_summator(key): """ Returns a function that sums a generalized hypergeometric series, for given parameter types (integer, rational, real, complex). """ p, q, param_types, ztype = key pstring = "".join(param_types) fname = "hypsum_%i_%i_%s_%s_%s" % (p, q, pstring[:p], pstring[p:], ztype) #print "generating hypsum", fname have_complex_param = 'C' in param_types have_complex_arg = ztype == 'C' have_complex = have_complex_param or have_complex_arg source = [] add = source.append aint = [] arat = [] bint = [] brat = [] areal = [] breal = [] acomplex = [] bcomplex = [] #add("wp = prec + 40") add("MAX = kwargs.get('maxterms', wp*100)") add("HIGH = MPZ_ONE<= 0:") add(" ZRE = xm << offset") add("else:") add(" ZRE = xm >> (-offset)") if have_complex_arg: add("offset = ye + wp") add("if offset >= 0:") add(" ZIM = ym << offset") add("else:") add(" ZIM = ym >> (-offset)") for i, flag in enumerate(param_types): W = ["A", "B"][i >= p] if flag == 'Z': ([aint,bint][i >= p]).append(i) add("%sINT_%i = coeffs[%i]" % (W, i, i)) elif flag == 'Q': ([arat,brat][i >= p]).append(i) add("%sP_%i, %sQ_%i = coeffs[%i]._mpq_" % (W, i, W, i, i)) elif flag == 'R': ([areal,breal][i >= p]).append(i) add("xsign, xm, xe, xbc = coeffs[%i]._mpf_" % i) add("if xsign: xm = -xm") add("offset = xe + wp") add("if offset >= 0:") add(" %sREAL_%i = xm << offset" % (W, i)) add("else:") add(" %sREAL_%i = xm >> (-offset)" % (W, i)) elif flag == 'C': ([acomplex,bcomplex][i >= p]).append(i) add("__re, __im = coeffs[%i]._mpc_" % i) add("xsign, xm, xe, xbc = __re") add("if xsign: xm = -xm") add("ysign, ym, ye, ybc = __im") add("if ysign: ym = -ym") add("offset = xe + wp") add("if offset >= 0:") add(" %sCRE_%i = xm << offset" % (W, i)) add("else:") add(" %sCRE_%i = xm >> (-offset)" % (W, i)) add("offset = ye + wp") add("if offset >= 0:") add(" %sCIM_%i = ym << offset" % (W, i)) add("else:") add(" %sCIM_%i = ym >> (-offset)" % (W, i)) else: raise ValueError l_areal = len(areal) l_breal = len(breal) cancellable_real = min(l_areal, l_breal) noncancellable_real_num = areal[cancellable_real:] noncancellable_real_den = breal[cancellable_real:] # LOOP add("for n in xrange(1,10**8):") add(" if n in magnitude_check:") add(" p_mag = bitcount(abs(PRE))") if have_complex: add(" p_mag = max(p_mag, bitcount(abs(PIM)))") add(" magnitude_check[n] = wp-p_mag") # Real factors multiplier = " * ".join(["AINT_#".replace("#", str(i)) for i in aint] + \ ["AP_#".replace("#", str(i)) for i in arat] + \ ["BQ_#".replace("#", str(i)) for i in brat]) divisor = " * ".join(["BINT_#".replace("#", str(i)) for i in bint] + \ ["BP_#".replace("#", str(i)) for i in brat] + \ ["AQ_#".replace("#", str(i)) for i in arat] + ["n"]) if multiplier: add(" mul = " + multiplier) add(" div = " + divisor) # Check for singular terms add(" if not div:") if multiplier: add(" if not mul:") add(" break") add(" raise ZeroDivisionError") # Update product if have_complex: # TODO: when there are several real parameters and just a few complex # (maybe just the complex argument), we only need to do about # half as many ops if we accumulate the real factor in a single real variable for k in range(cancellable_real): add(" PRE = PRE * AREAL_%i // BREAL_%i" % (areal[k], breal[k])) for i in noncancellable_real_num: add(" PRE = (PRE * AREAL_#) >> wp".replace("#", str(i))) for i in noncancellable_real_den: add(" PRE = (PRE << wp) // BREAL_#".replace("#", str(i))) for k in range(cancellable_real): add(" PIM = PIM * AREAL_%i // BREAL_%i" % (areal[k], breal[k])) for i in noncancellable_real_num: add(" PIM = (PIM * AREAL_#) >> wp".replace("#", str(i))) for i in noncancellable_real_den: add(" PIM = (PIM << wp) // BREAL_#".replace("#", str(i))) if multiplier: if have_complex_arg: add(" PRE, PIM = (mul*(PRE*ZRE-PIM*ZIM))//div, (mul*(PIM*ZRE+PRE*ZIM))//div") add(" PRE >>= wp") add(" PIM >>= wp") else: add(" PRE = ((mul * PRE * ZRE) >> wp) // div") add(" PIM = ((mul * PIM * ZRE) >> wp) // div") else: if have_complex_arg: add(" PRE, PIM = (PRE*ZRE-PIM*ZIM)//div, (PIM*ZRE+PRE*ZIM)//div") add(" PRE >>= wp") add(" PIM >>= wp") else: add(" PRE = ((PRE * ZRE) >> wp) // div") add(" PIM = ((PIM * ZRE) >> wp) // div") for i in acomplex: add(" PRE, PIM = PRE*ACRE_#-PIM*ACIM_#, PIM*ACRE_#+PRE*ACIM_#".replace("#", str(i))) add(" PRE >>= wp") add(" PIM >>= wp") for i in bcomplex: add(" mag = BCRE_#*BCRE_#+BCIM_#*BCIM_#".replace("#", str(i))) add(" re = PRE*BCRE_# + PIM*BCIM_#".replace("#", str(i))) add(" im = PIM*BCRE_# - PRE*BCIM_#".replace("#", str(i))) add(" PRE = (re << wp) // mag".replace("#", str(i))) add(" PIM = (im << wp) // mag".replace("#", str(i))) else: for k in range(cancellable_real): add(" PRE = PRE * AREAL_%i // BREAL_%i" % (areal[k], breal[k])) for i in noncancellable_real_num: add(" PRE = (PRE * AREAL_#) >> wp".replace("#", str(i))) for i in noncancellable_real_den: add(" PRE = (PRE << wp) // BREAL_#".replace("#", str(i))) if multiplier: add(" PRE = ((PRE * mul * ZRE) >> wp) // div") else: add(" PRE = ((PRE * ZRE) >> wp) // div") # Add product to sum if have_complex: add(" SRE += PRE") add(" SIM += PIM") add(" if (HIGH > PRE > LOW) and (HIGH > PIM > LOW):") add(" break") else: add(" SRE += PRE") add(" if HIGH > PRE > LOW:") add(" break") #add(" from mpmath import nprint, log, ldexp") #add(" nprint([n, log(abs(PRE),2), ldexp(PRE,-wp)])") add(" if n > MAX:") add(" raise NoConvergence('Hypergeometric series converges too slowly. Try increasing maxterms.')") # +1 all parameters for next loop for i in aint: add(" AINT_# += 1".replace("#", str(i))) for i in bint: add(" BINT_# += 1".replace("#", str(i))) for i in arat: add(" AP_# += AQ_#".replace("#", str(i))) for i in brat: add(" BP_# += BQ_#".replace("#", str(i))) for i in areal: add(" AREAL_# += one".replace("#", str(i))) for i in breal: add(" BREAL_# += one".replace("#", str(i))) for i in acomplex: add(" ACRE_# += one".replace("#", str(i))) for i in bcomplex: add(" BCRE_# += one".replace("#", str(i))) if have_complex: add("a = from_man_exp(SRE, -wp, prec, 'n')") add("b = from_man_exp(SIM, -wp, prec, 'n')") add("if SRE:") add(" if SIM:") add(" magn = max(a[2]+a[3], b[2]+b[3])") add(" else:") add(" magn = a[2]+a[3]") add("elif SIM:") add(" magn = b[2]+b[3]") add("else:") add(" magn = -wp+1") add("return (a, b), True, magn") else: add("a = from_man_exp(SRE, -wp, prec, 'n')") add("if SRE:") add(" magn = a[2]+a[3]") add("else:") add(" magn = -wp+1") add("return a, False, magn") source = "\n".join((" " + line) for line in source) source = ("def %s(coeffs, z, prec, wp, epsshift, magnitude_check, **kwargs):\n" % fname) + source namespace = {} exec_(source, globals(), namespace) #print source return source, namespace[fname] if BACKEND == 'sage': def make_hyp_summator(key): """ Returns a function that sums a generalized hypergeometric series, for given parameter types (integer, rational, real, complex). """ from sage.libs.mpmath.ext_main import hypsum_internal p, q, param_types, ztype = key def _hypsum(coeffs, z, prec, wp, epsshift, magnitude_check, **kwargs): return hypsum_internal(p, q, param_types, ztype, coeffs, z, prec, wp, epsshift, magnitude_check, kwargs) return "(none)", _hypsum #-----------------------------------------------------------------------# # # # Error functions # # # #-----------------------------------------------------------------------# # TODO: mpf_erf should call mpf_erfc when appropriate (currently # only the converse delegation is implemented) def mpf_erf(x, prec, rnd=round_fast): sign, man, exp, bc = x if not man: if x == fzero: return fzero if x == finf: return fone if x== fninf: return fnone return fnan size = exp + bc lg = math.log # The approximation erf(x) = 1 is accurate to > x^2 * log(e,2) bits if size > 3 and 2*(size-1) + 0.528766 > lg(prec,2): if sign: return mpf_perturb(fnone, 0, prec, rnd) else: return mpf_perturb(fone, 1, prec, rnd) # erf(x) ~ 2*x/sqrt(pi) close to 0 if size < -prec: # 2*x x = mpf_shift(x,1) c = mpf_sqrt(mpf_pi(prec+20), prec+20) # TODO: interval rounding return mpf_div(x, c, prec, rnd) wp = prec + abs(size) + 25 # Taylor series for erf, fixed-point summation t = abs(to_fixed(x, wp)) t2 = (t*t) >> wp s, term, k = t, 12345, 1 while term: t = ((t * t2) >> wp) // k term = t // (2*k+1) if k & 1: s -= term else: s += term k += 1 s = (s << (wp+1)) // sqrt_fixed(pi_fixed(wp), wp) if sign: s = -s return from_man_exp(s, -wp, prec, rnd) # If possible, we use the asymptotic series for erfc. # This is an alternating divergent asymptotic series, so # the error is at most equal to the first omitted term. # Here we check if the smallest term is small enough # for a given x and precision def erfc_check_series(x, prec): n = to_int(x) if n**2 * 1.44 > prec: return True return False def mpf_erfc(x, prec, rnd=round_fast): sign, man, exp, bc = x if not man: if x == fzero: return fone if x == finf: return fzero if x == fninf: return ftwo return fnan wp = prec + 20 mag = bc+exp # Preserve full accuracy when exponent grows huge wp += max(0, 2*mag) regular_erf = sign or mag < 2 if regular_erf or not erfc_check_series(x, wp): if regular_erf: return mpf_sub(fone, mpf_erf(x, prec+10, negative_rnd[rnd]), prec, rnd) # 1-erf(x) ~ exp(-x^2), increase prec to deal with cancellation n = to_int(x)+1 return mpf_sub(fone, mpf_erf(x, prec + int(n**2*1.44) + 10), prec, rnd) s = term = MPZ_ONE << wp term_prev = 0 t = (2 * to_fixed(x, wp) ** 2) >> wp k = 1 while 1: term = ((term * (2*k - 1)) << wp) // t if k > 4 and term > term_prev or not term: break if k & 1: s -= term else: s += term term_prev = term #print k, to_str(from_man_exp(term, -wp, 50), 10) k += 1 s = (s << wp) // sqrt_fixed(pi_fixed(wp), wp) s = from_man_exp(s, -wp, wp) z = mpf_exp(mpf_neg(mpf_mul(x,x,wp),wp),wp) y = mpf_div(mpf_mul(z, s, wp), x, prec, rnd) return y #-----------------------------------------------------------------------# # # # Exponential integrals # # # #-----------------------------------------------------------------------# def ei_taylor(x, prec): s = t = x k = 2 while t: t = ((t*x) >> prec) // k s += t // k k += 1 return s def complex_ei_taylor(zre, zim, prec): _abs = abs sre = tre = zre sim = tim = zim k = 2 while _abs(tre) + _abs(tim) > 5: tre, tim = ((tre*zre-tim*zim)//k)>>prec, ((tre*zim+tim*zre)//k)>>prec sre += tre // k sim += tim // k k += 1 return sre, sim def ei_asymptotic(x, prec): one = MPZ_ONE << prec x = t = ((one << prec) // x) s = one + x k = 2 while t: t = (k*t*x) >> prec s += t k += 1 return s def complex_ei_asymptotic(zre, zim, prec): _abs = abs one = MPZ_ONE << prec M = (zim*zim + zre*zre) >> prec # 1 / z xre = tre = (zre << prec) // M xim = tim = ((-zim) << prec) // M sre = one + xre sim = xim k = 2 while _abs(tre) + _abs(tim) > 1000: #print tre, tim tre, tim = ((tre*xre-tim*xim)*k)>>prec, ((tre*xim+tim*xre)*k)>>prec sre += tre sim += tim k += 1 if k > prec: raise NoConvergence return sre, sim def mpf_ei(x, prec, rnd=round_fast, e1=False): if e1: x = mpf_neg(x) sign, man, exp, bc = x if e1 and not sign: if x == fzero: return finf raise ComplexResult("E1(x) for x < 0") if man: xabs = 0, man, exp, bc xmag = exp+bc wp = prec + 20 can_use_asymp = xmag > wp if not can_use_asymp: if exp >= 0: xabsint = man << exp else: xabsint = man >> (-exp) can_use_asymp = xabsint > int(wp*0.693) + 10 if can_use_asymp: if xmag > wp: v = fone else: v = from_man_exp(ei_asymptotic(to_fixed(x, wp), wp), -wp) v = mpf_mul(v, mpf_exp(x, wp), wp) v = mpf_div(v, x, prec, rnd) else: wp += 2*int(to_int(xabs)) u = to_fixed(x, wp) v = ei_taylor(u, wp) + euler_fixed(wp) t1 = from_man_exp(v,-wp) t2 = mpf_log(xabs,wp) v = mpf_add(t1, t2, prec, rnd) else: if x == fzero: v = fninf elif x == finf: v = finf elif x == fninf: v = fzero else: v = fnan if e1: v = mpf_neg(v) return v def mpc_ei(z, prec, rnd=round_fast, e1=False): if e1: z = mpc_neg(z) a, b = z asign, aman, aexp, abc = a bsign, bman, bexp, bbc = b if b == fzero: if e1: x = mpf_neg(mpf_ei(a, prec, rnd)) if not asign: y = mpf_neg(mpf_pi(prec, rnd)) else: y = fzero return x, y else: return mpf_ei(a, prec, rnd), fzero if a != fzero: if not aman or not bman: return (fnan, fnan) wp = prec + 40 amag = aexp+abc bmag = bexp+bbc zmag = max(amag, bmag) can_use_asymp = zmag > wp if not can_use_asymp: zabsint = abs(to_int(a)) + abs(to_int(b)) can_use_asymp = zabsint > int(wp*0.693) + 20 try: if can_use_asymp: if zmag > wp: v = fone, fzero else: zre = to_fixed(a, wp) zim = to_fixed(b, wp) vre, vim = complex_ei_asymptotic(zre, zim, wp) v = from_man_exp(vre, -wp), from_man_exp(vim, -wp) v = mpc_mul(v, mpc_exp(z, wp), wp) v = mpc_div(v, z, wp) if e1: v = mpc_neg(v, prec, rnd) else: x, y = v if bsign: v = mpf_pos(x, prec, rnd), mpf_sub(y, mpf_pi(wp), prec, rnd) else: v = mpf_pos(x, prec, rnd), mpf_add(y, mpf_pi(wp), prec, rnd) return v except NoConvergence: pass #wp += 2*max(0,zmag) wp += 2*int(to_int(mpc_abs(z, 5))) zre = to_fixed(a, wp) zim = to_fixed(b, wp) vre, vim = complex_ei_taylor(zre, zim, wp) vre += euler_fixed(wp) v = from_man_exp(vre,-wp), from_man_exp(vim,-wp) if e1: u = mpc_log(mpc_neg(z),wp) else: u = mpc_log(z,wp) v = mpc_add(v, u, prec, rnd) if e1: v = mpc_neg(v) return v def mpf_e1(x, prec, rnd=round_fast): return mpf_ei(x, prec, rnd, True) def mpc_e1(x, prec, rnd=round_fast): return mpc_ei(x, prec, rnd, True) def mpf_expint(n, x, prec, rnd=round_fast, gamma=False): """ E_n(x), n an integer, x real With gamma=True, computes Gamma(n,x) (upper incomplete gamma function) Returns (real, None) if real, otherwise (real, imag) The imaginary part is an optional branch cut term """ sign, man, exp, bc = x if not man: if gamma: if x == fzero: # Actually gamma function pole if n <= 0: return finf, None return mpf_gamma_int(n, prec, rnd), None if x == finf: return fzero, None # TODO: could return finite imaginary value at -inf return fnan, fnan else: if x == fzero: if n > 1: return from_rational(1, n-1, prec, rnd), None else: return finf, None if x == finf: return fzero, None return fnan, fnan n_orig = n if gamma: n = 1-n wp = prec + 20 xmag = exp + bc # Beware of near-poles if xmag < -10: raise NotImplementedError nmag = bitcount(abs(n)) have_imag = n > 0 and sign negx = mpf_neg(x) # Skip series if direct convergence if n == 0 or 2*nmag - xmag < -wp: if gamma: v = mpf_exp(negx, wp) re = mpf_mul(v, mpf_pow_int(x, n_orig-1, wp), prec, rnd) else: v = mpf_exp(negx, wp) re = mpf_div(v, x, prec, rnd) else: # Finite number of terms, or... can_use_asymptotic_series = -3*wp < n <= 0 # ...large enough? if not can_use_asymptotic_series: xi = abs(to_int(x)) m = min(max(1, xi-n), 2*wp) siz = -n*nmag + (m+n)*bitcount(abs(m+n)) - m*xmag - (144*m//100) tol = -wp-10 can_use_asymptotic_series = siz < tol if can_use_asymptotic_series: r = ((-MPZ_ONE) << (wp+wp)) // to_fixed(x, wp) m = n t = r*m s = MPZ_ONE << wp while m and t: s += t m += 1 t = (m*r*t) >> wp v = mpf_exp(negx, wp) if gamma: # ~ exp(-x) * x^(n-1) * (1 + ...) v = mpf_mul(v, mpf_pow_int(x, n_orig-1, wp), wp) else: # ~ exp(-x)/x * (1 + ...) v = mpf_div(v, x, wp) re = mpf_mul(v, from_man_exp(s, -wp), prec, rnd) elif n == 1: re = mpf_neg(mpf_ei(negx, prec, rnd)) elif n > 0 and n < 3*wp: T1 = mpf_neg(mpf_ei(negx, wp)) if gamma: if n_orig & 1: T1 = mpf_neg(T1) else: T1 = mpf_mul(T1, mpf_pow_int(negx, n-1, wp), wp) r = t = to_fixed(x, wp) facs = [1] * (n-1) for k in range(1,n-1): facs[k] = facs[k-1] * k facs = facs[::-1] s = facs[0] << wp for k in range(1, n-1): if k & 1: s -= facs[k] * t else: s += facs[k] * t t = (t*r) >> wp T2 = from_man_exp(s, -wp, wp) T2 = mpf_mul(T2, mpf_exp(negx, wp)) if gamma: T2 = mpf_mul(T2, mpf_pow_int(x, n_orig, wp), wp) R = mpf_add(T1, T2) re = mpf_div(R, from_int(ifac(n-1)), prec, rnd) else: raise NotImplementedError if have_imag: M = from_int(-ifac(n-1)) if gamma: im = mpf_div(mpf_pi(wp), M, prec, rnd) else: im = mpf_div(mpf_mul(mpf_pi(wp), mpf_pow_int(negx, n_orig-1, wp), wp), M, prec, rnd) return re, im else: return re, None def mpf_ci_si_taylor(x, wp, which=0): """ 0 - Ci(x) - (euler+log(x)) 1 - Si(x) """ x = to_fixed(x, wp) x2 = -(x*x) >> wp if which == 0: s, t, k = 0, (MPZ_ONE<>wp s += t//k k += 2 return from_man_exp(s, -wp) def mpc_ci_si_taylor(re, im, wp, which=0): # The following code is only designed for small arguments, # and not too small arguments (for relative accuracy) if re[1]: mag = re[2]+re[3] elif im[1]: mag = im[2]+im[3] if im[1]: mag = max(mag, im[2]+im[3]) if mag > 2 or mag < -wp: raise NotImplementedError wp += (2-mag) zre = to_fixed(re, wp) zim = to_fixed(im, wp) z2re = (zim*zim-zre*zre)>>wp z2im = (-2*zre*zim)>>wp tre = zre tim = zim one = MPZ_ONE< 2: f = k*(k-1) tre, tim = ((tre*z2re-tim*z2im)//f)>>wp, ((tre*z2im+tim*z2re)//f)>>wp sre += tre//k sim += tim//k k += 2 return from_man_exp(sre, -wp), from_man_exp(sim, -wp) def mpf_ci_si(x, prec, rnd=round_fast, which=2): """ Calculation of Ci(x), Si(x) for real x. which = 0 -- returns (Ci(x), -) which = 1 -- returns (Si(x), -) which = 2 -- returns (Ci(x), Si(x)) Note: if x < 0, Ci(x) needs an additional imaginary term, pi*i. """ wp = prec + 20 sign, man, exp, bc = x ci, si = None, None if not man: if x == fzero: return (fninf, fzero) if x == fnan: return (x, x) ci = fzero if which != 0: if x == finf: si = mpf_shift(mpf_pi(prec, rnd), -1) if x == fninf: si = mpf_neg(mpf_shift(mpf_pi(prec, negative_rnd[rnd]), -1)) return (ci, si) # For small x: Ci(x) ~ euler + log(x), Si(x) ~ x mag = exp+bc if mag < -wp: if which != 0: si = mpf_perturb(x, 1-sign, prec, rnd) if which != 1: y = mpf_euler(wp) xabs = mpf_abs(x) ci = mpf_add(y, mpf_log(xabs, wp), prec, rnd) return ci, si # For huge x: Ci(x) ~ sin(x)/x, Si(x) ~ pi/2 elif mag > wp: if which != 0: if sign: si = mpf_neg(mpf_pi(prec, negative_rnd[rnd])) else: si = mpf_pi(prec, rnd) si = mpf_shift(si, -1) if which != 1: ci = mpf_div(mpf_sin(x, wp), x, prec, rnd) return ci, si else: wp += abs(mag) # Use an asymptotic series? The smallest value of n!/x^n # occurs for n ~ x, where the magnitude is ~ exp(-x). asymptotic = mag-1 > math.log(wp, 2) # Case 1: convergent series near 0 if not asymptotic: if which != 0: si = mpf_pos(mpf_ci_si_taylor(x, wp, 1), prec, rnd) if which != 1: ci = mpf_ci_si_taylor(x, wp, 0) ci = mpf_add(ci, mpf_euler(wp), wp) ci = mpf_add(ci, mpf_log(mpf_abs(x), wp), prec, rnd) return ci, si x = mpf_abs(x) # Case 2: asymptotic series for x >> 1 xf = to_fixed(x, wp) xr = (MPZ_ONE<<(2*wp)) // xf # 1/x s1 = (MPZ_ONE << wp) s2 = xr t = xr k = 2 while t: t = -t t = (t*xr*k)>>wp k += 1 s1 += t t = (t*xr*k)>>wp k += 1 s2 += t s1 = from_man_exp(s1, -wp) s2 = from_man_exp(s2, -wp) s1 = mpf_div(s1, x, wp) s2 = mpf_div(s2, x, wp) cos, sin = mpf_cos_sin(x, wp) # Ci(x) = sin(x)*s1-cos(x)*s2 # Si(x) = pi/2-cos(x)*s1-sin(x)*s2 if which != 0: si = mpf_add(mpf_mul(cos, s1), mpf_mul(sin, s2), wp) si = mpf_sub(mpf_shift(mpf_pi(wp), -1), si, wp) if sign: si = mpf_neg(si) si = mpf_pos(si, prec, rnd) if which != 1: ci = mpf_sub(mpf_mul(sin, s1), mpf_mul(cos, s2), prec, rnd) return ci, si def mpf_ci(x, prec, rnd=round_fast): if mpf_sign(x) < 0: raise ComplexResult return mpf_ci_si(x, prec, rnd, 0)[0] def mpf_si(x, prec, rnd=round_fast): return mpf_ci_si(x, prec, rnd, 1)[1] def mpc_ci(z, prec, rnd=round_fast): re, im = z if im == fzero: ci = mpf_ci_si(re, prec, rnd, 0)[0] if mpf_sign(re) < 0: return (ci, mpf_pi(prec, rnd)) return (ci, fzero) wp = prec + 20 cre, cim = mpc_ci_si_taylor(re, im, wp, 0) cre = mpf_add(cre, mpf_euler(wp), wp) ci = mpc_add((cre, cim), mpc_log(z, wp), prec, rnd) return ci def mpc_si(z, prec, rnd=round_fast): re, im = z if im == fzero: return (mpf_ci_si(re, prec, rnd, 1)[1], fzero) wp = prec + 20 z = mpc_ci_si_taylor(re, im, wp, 1) return mpc_pos(z, prec, rnd) #-----------------------------------------------------------------------# # # # Bessel functions # # # #-----------------------------------------------------------------------# # A Bessel function of the first kind of integer order, J_n(x), is # given by the power series # oo # ___ k 2 k + n # \ (-1) / x \ # J_n(x) = ) ----------- | - | # /___ k! (k + n)! \ 2 / # k = 0 # Simplifying the quotient between two successive terms gives the # ratio x^2 / (-4*k*(k+n)). Hence, we only need one full-precision # multiplication and one division by a small integer per term. # The complex version is very similar, the only difference being # that the multiplication is actually 4 multiplies. # In the general case, we have # J_v(x) = (x/2)**v / v! * 0F1(v+1, (-1/4)*z**2) # TODO: for extremely large x, we could use an asymptotic # trigonometric approximation. # TODO: recompute at higher precision if the fixed-point mantissa # is very small def mpf_besseljn(n, x, prec, rounding=round_fast): prec += 50 negate = n < 0 and n & 1 mag = x[2]+x[3] n = abs(n) wp = prec + 20 + n*bitcount(n) if mag < 0: wp -= n * mag x = to_fixed(x, wp) x2 = (x**2) >> wp if not n: s = t = MPZ_ONE << wp else: s = t = (x**n // ifac(n)) >> ((n-1)*wp + n) k = 1 while t: t = ((t * x2) // (-4*k*(k+n))) >> wp s += t k += 1 if negate: s = -s return from_man_exp(s, -wp, prec, rounding) def mpc_besseljn(n, z, prec, rounding=round_fast): negate = n < 0 and n & 1 n = abs(n) origprec = prec zre, zim = z mag = max(zre[2]+zre[3], zim[2]+zim[3]) prec += 20 + n*bitcount(n) + abs(mag) if mag < 0: prec -= n * mag zre = to_fixed(zre, prec) zim = to_fixed(zim, prec) z2re = (zre**2 - zim**2) >> prec z2im = (zre*zim) >> (prec-1) if not n: sre = tre = MPZ_ONE << prec sim = tim = MPZ_ZERO else: re, im = complex_int_pow(zre, zim, n) sre = tre = (re // ifac(n)) >> ((n-1)*prec + n) sim = tim = (im // ifac(n)) >> ((n-1)*prec + n) k = 1 while abs(tre) + abs(tim) > 3: p = -4*k*(k+n) tre, tim = tre*z2re - tim*z2im, tim*z2re + tre*z2im tre = (tre // p) >> prec tim = (tim // p) >> prec sre += tre sim += tim k += 1 if negate: sre = -sre sim = -sim re = from_man_exp(sre, -prec, origprec, rounding) im = from_man_exp(sim, -prec, origprec, rounding) return (re, im) def mpf_agm(a, b, prec, rnd=round_fast): """ Computes the arithmetic-geometric mean agm(a,b) for nonnegative mpf values a, b. """ asign, aman, aexp, abc = a bsign, bman, bexp, bbc = b if asign or bsign: raise ComplexResult("agm of a negative number") # Handle inf, nan or zero in either operand if not (aman and bman): if a == fnan or b == fnan: return fnan if a == finf: if b == fzero: return fnan return finf if b == finf: if a == fzero: return fnan return finf # agm(0,x) = agm(x,0) = 0 return fzero wp = prec + 20 amag = aexp+abc bmag = bexp+bbc mag_delta = amag - bmag # Reduce to roughly the same magnitude using floating-point AGM abs_mag_delta = abs(mag_delta) if abs_mag_delta > 10: while abs_mag_delta > 10: a, b = mpf_shift(mpf_add(a,b,wp),-1), \ mpf_sqrt(mpf_mul(a,b,wp),wp) abs_mag_delta //= 2 asign, aman, aexp, abc = a bsign, bman, bexp, bbc = b amag = aexp+abc bmag = bexp+bbc mag_delta = amag - bmag #print to_float(a), to_float(b) # Use agm(a,b) = agm(x*a,x*b)/x to obtain a, b ~= 1 min_mag = min(amag,bmag) max_mag = max(amag,bmag) n = 0 # If too small, we lose precision when going to fixed-point if min_mag < -8: n = -min_mag # If too large, we waste time using fixed-point with large numbers elif max_mag > 20: n = -max_mag if n: a = mpf_shift(a, n) b = mpf_shift(b, n) #print to_float(a), to_float(b) af = to_fixed(a, wp) bf = to_fixed(b, wp) g = agm_fixed(af, bf, wp) return from_man_exp(g, -wp-n, prec, rnd) def mpf_agm1(a, prec, rnd=round_fast): """ Computes the arithmetic-geometric mean agm(1,a) for a nonnegative mpf value a. """ return mpf_agm(fone, a, prec, rnd) def mpc_agm(a, b, prec, rnd=round_fast): """ Complex AGM. TODO: * check that convergence works as intended * optimize * select a nonarbitrary branch """ if mpc_is_infnan(a) or mpc_is_infnan(b): return fnan, fnan if mpc_zero in (a, b): return fzero, fzero if mpc_neg(a) == b: return fzero, fzero wp = prec+20 eps = mpf_shift(fone, -wp+10) while 1: a1 = mpc_shift(mpc_add(a, b, wp), -1) b1 = mpc_sqrt(mpc_mul(a, b, wp), wp) a, b = a1, b1 size = mpf_min_max([mpc_abs(a,10), mpc_abs(b,10)])[1] err = mpc_abs(mpc_sub(a, b, 10), 10) if size == fzero or mpf_lt(err, mpf_mul(eps, size)): return a def mpc_agm1(a, prec, rnd=round_fast): return mpc_agm(mpc_one, a, prec, rnd) def mpf_ellipk(x, prec, rnd=round_fast): if not x[1]: if x == fzero: return mpf_shift(mpf_pi(prec, rnd), -1) if x == fninf: return fzero if x == fnan: return x if x == fone: return finf # TODO: for |x| << 1/2, one could use fall back to # pi/2 * hyp2f1_rat((1,2),(1,2),(1,1), x) wp = prec + 15 # Use K(x) = pi/2/agm(1,a) where a = sqrt(1-x) # The sqrt raises ComplexResult if x > 0 a = mpf_sqrt(mpf_sub(fone, x, wp), wp) v = mpf_agm1(a, wp) r = mpf_div(mpf_pi(wp), v, prec, rnd) return mpf_shift(r, -1) def mpc_ellipk(z, prec, rnd=round_fast): re, im = z if im == fzero: if re == finf: return mpc_zero if mpf_le(re, fone): return mpf_ellipk(re, prec, rnd), fzero wp = prec + 15 a = mpc_sqrt(mpc_sub(mpc_one, z, wp), wp) v = mpc_agm1(a, wp) r = mpc_mpf_div(mpf_pi(wp), v, prec, rnd) return mpc_shift(r, -1) def mpf_ellipe(x, prec, rnd=round_fast): # http://functions.wolfram.com/EllipticIntegrals/ # EllipticK/20/01/0001/ # E = (1-m)*(K'(m)*2*m + K(m)) sign, man, exp, bc = x if not man: if x == fzero: return mpf_shift(mpf_pi(prec, rnd), -1) if x == fninf: return finf if x == fnan: return x if x == finf: raise ComplexResult if x == fone: return fone wp = prec+20 mag = exp+bc if mag < -wp: return mpf_shift(mpf_pi(prec, rnd), -1) # Compute a finite difference for K' p = max(mag, 0) - wp h = mpf_shift(fone, p) K = mpf_ellipk(x, 2*wp) Kh = mpf_ellipk(mpf_sub(x, h), 2*wp) Kdiff = mpf_shift(mpf_sub(K, Kh), -p) t = mpf_sub(fone, x) b = mpf_mul(Kdiff, mpf_shift(x,1), wp) return mpf_mul(t, mpf_add(K, b), prec, rnd) def mpc_ellipe(z, prec, rnd=round_fast): re, im = z if im == fzero: if re == finf: return (fzero, finf) if mpf_le(re, fone): return mpf_ellipe(re, prec, rnd), fzero wp = prec + 15 mag = mpc_abs(z, 1) p = max(mag[2]+mag[3], 0) - wp h = mpf_shift(fone, p) K = mpc_ellipk(z, 2*wp) Kh = mpc_ellipk(mpc_add_mpf(z, h, 2*wp), 2*wp) Kdiff = mpc_shift(mpc_sub(Kh, K, wp), -p) t = mpc_sub(mpc_one, z, wp) b = mpc_mul(Kdiff, mpc_shift(z,1), wp) return mpc_mul(t, mpc_add(K, b, wp), prec, rnd) sympy-0.7.4.1/sympy/mpmath/libmp/libintmath.py0000644000175000017500000003643112253362407021532 0ustar georgeskgeorgesk""" Utility functions for integer math. TODO: rename, cleanup, perhaps move the gmpy wrapper code here from settings.py """ import math from bisect import bisect from .backend import xrange from .backend import BACKEND, gmpy, sage, sage_utils, MPZ, MPZ_ONE, MPZ_ZERO def giant_steps(start, target, n=2): """ Return a list of integers ~= [start, n*start, ..., target/n^2, target/n, target] but conservatively rounded so that the quotient between two successive elements is actually slightly less than n. With n = 2, this describes suitable precision steps for a quadratically convergent algorithm such as Newton's method; with n = 3 steps for cubic convergence (Halley's method), etc. >>> giant_steps(50,1000) [66, 128, 253, 502, 1000] >>> giant_steps(50,1000,4) [65, 252, 1000] """ L = [target] while L[-1] > start*n: L = L + [L[-1]//n + 2] return L[::-1] def rshift(x, n): """For an integer x, calculate x >> n with the fastest (floor) rounding. Unlike the plain Python expression (x >> n), n is allowed to be negative, in which case a left shift is performed.""" if n >= 0: return x >> n else: return x << (-n) def lshift(x, n): """For an integer x, calculate x << n. Unlike the plain Python expression (x << n), n is allowed to be negative, in which case a right shift with default (floor) rounding is performed.""" if n >= 0: return x << n else: return x >> (-n) if BACKEND == 'sage': import operator rshift = operator.rshift lshift = operator.lshift def python_trailing(n): """Count the number of trailing zero bits in abs(n).""" if not n: return 0 t = 0 while not n & 1: n >>= 1 t += 1 return t if BACKEND == 'gmpy': if gmpy.version() >= '2': def gmpy_trailing(n): """Count the number of trailing zero bits in abs(n) using gmpy.""" if n: return MPZ(n).bit_scan1() else: return 0 else: def gmpy_trailing(n): """Count the number of trailing zero bits in abs(n) using gmpy.""" if n: return MPZ(n).scan1() else: return 0 # Small powers of 2 powers = [1<<_ for _ in range(300)] def python_bitcount(n): """Calculate bit size of the nonnegative integer n.""" bc = bisect(powers, n) if bc != 300: return bc bc = int(math.log(n, 2)) - 4 return bc + bctable[n>>bc] def gmpy_bitcount(n): """Calculate bit size of the nonnegative integer n.""" if n: return MPZ(n).numdigits(2) else: return 0 #def sage_bitcount(n): # if n: return MPZ(n).nbits() # else: return 0 def sage_trailing(n): return MPZ(n).trailing_zero_bits() if BACKEND == 'gmpy': bitcount = gmpy_bitcount trailing = gmpy_trailing elif BACKEND == 'sage': sage_bitcount = sage_utils.bitcount bitcount = sage_bitcount trailing = sage_trailing else: bitcount = python_bitcount trailing = python_trailing if BACKEND == 'gmpy' and 'bit_length' in dir(gmpy): bitcount = gmpy.bit_length # Used to avoid slow function calls as far as possible trailtable = [trailing(n) for n in range(256)] bctable = [bitcount(n) for n in range(1024)] # TODO: speed up for bases 2, 4, 8, 16, ... def bin_to_radix(x, xbits, base, bdigits): """Changes radix of a fixed-point number; i.e., converts x * 2**xbits to floor(x * 10**bdigits).""" return x * (MPZ(base)**bdigits) >> xbits stddigits = '0123456789abcdefghijklmnopqrstuvwxyz' def small_numeral(n, base=10, digits=stddigits): """Return the string numeral of a positive integer in an arbitrary base. Most efficient for small input.""" if base == 10: return str(n) digs = [] while n: n, digit = divmod(n, base) digs.append(digits[digit]) return "".join(digs[::-1]) def numeral_python(n, base=10, size=0, digits=stddigits): """Represent the integer n as a string of digits in the given base. Recursive division is used to make this function about 3x faster than Python's str() for converting integers to decimal strings. The 'size' parameters specifies the number of digits in n; this number is only used to determine splitting points and need not be exact.""" if n <= 0: if not n: return "0" return "-" + numeral(-n, base, size, digits) # Fast enough to do directly if size < 250: return small_numeral(n, base, digits) # Divide in half half = (size // 2) + (size & 1) A, B = divmod(n, base**half) ad = numeral(A, base, half, digits) bd = numeral(B, base, half, digits).rjust(half, "0") return ad + bd def numeral_gmpy(n, base=10, size=0, digits=stddigits): """Represent the integer n as a string of digits in the given base. Recursive division is used to make this function about 3x faster than Python's str() for converting integers to decimal strings. The 'size' parameters specifies the number of digits in n; this number is only used to determine splitting points and need not be exact.""" if n < 0: return "-" + numeral(-n, base, size, digits) # gmpy.digits() may cause a segmentation fault when trying to convert # extremely large values to a string. The size limit may need to be # adjusted on some platforms, but 1500000 works on Windows and Linux. if size < 1500000: return gmpy.digits(n, base) # Divide in half half = (size // 2) + (size & 1) A, B = divmod(n, MPZ(base)**half) ad = numeral(A, base, half, digits) bd = numeral(B, base, half, digits).rjust(half, "0") return ad + bd if BACKEND == "gmpy": numeral = numeral_gmpy else: numeral = numeral_python _1_800 = 1<<800 _1_600 = 1<<600 _1_400 = 1<<400 _1_200 = 1<<200 _1_100 = 1<<100 _1_50 = 1<<50 def isqrt_small_python(x): """ Correctly (floor) rounded integer square root, using division. Fast up to ~200 digits. """ if not x: return x if x < _1_800: # Exact with IEEE double precision arithmetic if x < _1_50: return int(x**0.5) # Initial estimate can be any integer >= the true root; round up r = int(x**0.5 * 1.00000000000001) + 1 else: bc = bitcount(x) n = bc//2 r = int((x>>(2*n-100))**0.5+2)<<(n-50) # +2 is to round up # The following iteration now precisely computes floor(sqrt(x)) # See e.g. Crandall & Pomerance, "Prime Numbers: A Computational # Perspective" while 1: y = (r+x//r)>>1 if y >= r: return r r = y def isqrt_fast_python(x): """ Fast approximate integer square root, computed using division-free Newton iteration for large x. For random integers the result is almost always correct (floor(sqrt(x))), but is 1 ulp too small with a roughly 0.1% probability. If x is very close to an exact square, the answer is 1 ulp wrong with high probability. With 0 guard bits, the largest error over a set of 10^5 random inputs of size 1-10^5 bits was 3 ulp. The use of 10 guard bits almost certainly guarantees a max 1 ulp error. """ # Use direct division-based iteration if sqrt(x) < 2^400 # Assume floating-point square root accurate to within 1 ulp, then: # 0 Newton iterations good to 52 bits # 1 Newton iterations good to 104 bits # 2 Newton iterations good to 208 bits # 3 Newton iterations good to 416 bits if x < _1_800: y = int(x**0.5) if x >= _1_100: y = (y + x//y) >> 1 if x >= _1_200: y = (y + x//y) >> 1 if x >= _1_400: y = (y + x//y) >> 1 return y bc = bitcount(x) guard_bits = 10 x <<= 2*guard_bits bc += 2*guard_bits bc += (bc&1) hbc = bc//2 startprec = min(50, hbc) # Newton iteration for 1/sqrt(x), with floating-point starting value r = int(2.0**(2*startprec) * (x >> (bc-2*startprec)) ** -0.5) pp = startprec for p in giant_steps(startprec, hbc): # r**2, scaled from real size 2**(-bc) to 2**p r2 = (r*r) >> (2*pp - p) # x*r**2, scaled from real size ~1.0 to 2**p xr2 = ((x >> (bc-p)) * r2) >> p # New value of r, scaled from real size 2**(-bc/2) to 2**p r = (r * ((3<> (pp+1) pp = p # (1/sqrt(x))*x = sqrt(x) return (r*(x>>hbc)) >> (p+guard_bits) def sqrtrem_python(x): """Correctly rounded integer (floor) square root with remainder.""" # to check cutoff: # plot(lambda x: timing(isqrt, 2**int(x)), [0,2000]) if x < _1_600: y = isqrt_small_python(x) return y, x - y*y y = isqrt_fast_python(x) + 1 rem = x - y*y # Correct remainder while rem < 0: y -= 1 rem += (1+2*y) else: if rem: while rem > 2*(1+y): y += 1 rem -= (1+2*y) return y, rem def isqrt_python(x): """Integer square root with correct (floor) rounding.""" return sqrtrem_python(x)[0] def sqrt_fixed(x, prec): return isqrt_fast(x<= "2": isqrt_small = isqrt_fast = isqrt = gmpy.isqrt sqrtrem = gmpy.isqrt_rem else: isqrt_small = isqrt_fast = isqrt = gmpy.sqrt sqrtrem = gmpy.sqrtrem elif BACKEND == 'sage': isqrt_small = isqrt_fast = isqrt = \ getattr(sage_utils, "isqrt", lambda n: MPZ(n).isqrt()) sqrtrem = lambda n: MPZ(n).sqrtrem() else: isqrt_small = isqrt_small_python isqrt_fast = isqrt_fast_python isqrt = isqrt_python sqrtrem = sqrtrem_python def ifib(n, _cache={}): """Computes the nth Fibonacci number as an integer, for integer n.""" if n < 0: return (-1)**(-n+1) * ifib(-n) if n in _cache: return _cache[n] m = n # Use Dijkstra's logarithmic algorithm # The following implementation is basically equivalent to # http://en.literateprograms.org/Fibonacci_numbers_(Scheme) a, b, p, q = MPZ_ONE, MPZ_ZERO, MPZ_ZERO, MPZ_ONE while n: if n & 1: aq = a*q a, b = b*q+aq+a*p, b*p+aq n -= 1 else: qq = q*q p, q = p*p+qq, qq+2*p*q n >>= 1 if m < 250: _cache[m] = b return b MAX_FACTORIAL_CACHE = 1000 def ifac(n, memo={0:1, 1:1}): """Return n factorial (for integers n >= 0 only).""" f = memo.get(n) if f: return f k = len(memo) p = memo[k-1] MAX = MAX_FACTORIAL_CACHE while k <= n: p *= k if k <= MAX: memo[k] = p k += 1 return p def ifac2(n, memo_pair=[{0:1}, {1:1}]): """Return n!! (double factorial), integers n >= 0 only.""" memo = memo_pair[n&1] f = memo.get(n) if f: return f k = max(memo) p = memo[k] MAX = MAX_FACTORIAL_CACHE while k < n: k += 2 p *= k if k <= MAX: memo[k] = p return p if BACKEND == 'gmpy': ifac = gmpy.fac elif BACKEND == 'sage': ifac = lambda n: int(sage.factorial(n)) ifib = sage.fibonacci def list_primes(n): n = n + 1 sieve = list(xrange(n)) sieve[:2] = [0, 0] for i in xrange(2, int(n**0.5)+1): if sieve[i]: for j in xrange(i**2, n, i): sieve[j] = 0 return [p for p in sieve if p] if BACKEND == 'sage': # Note: it is *VERY* important for performance that we convert # the list to Python ints. def list_primes(n): return [int(_) for _ in sage.primes(n+1)] small_odd_primes = (3,5,7,11,13,17,19,23,29,31,37,41,43,47) small_odd_primes_set = set(small_odd_primes) def isprime(n): """ Determines whether n is a prime number. A probabilistic test is performed if n is very large. No special trick is used for detecting perfect powers. >>> sum(list_primes(100000)) 454396537 >>> sum(n*isprime(n) for n in range(100000)) 454396537 """ n = int(n) if not n & 1: return n == 2 if n < 50: return n in small_odd_primes_set for p in small_odd_primes: if not n % p: return False m = n-1 s = trailing(m) d = m >> s def test(a): x = pow(a,d,n) if x == 1 or x == m: return True for r in xrange(1,s): x = x**2 % n if x == m: return True return False # See http://primes.utm.edu/prove/prove2_3.html if n < 1373653: witnesses = [2,3] elif n < 341550071728321: witnesses = [2,3,5,7,11,13,17] else: witnesses = small_odd_primes for a in witnesses: if not test(a): return False return True def moebius(n): """ Evaluates the Moebius function which is `mu(n) = (-1)^k` if `n` is a product of `k` distinct primes and `mu(n) = 0` otherwise. TODO: speed up using factorization """ n = abs(int(n)) if n < 2: return n factors = [] for p in xrange(2, n+1): if not (n % p): if not (n % p**2): return 0 if not sum(p % f for f in factors): factors.append(p) return (-1)**len(factors) def gcd(*args): a = 0 for b in args: if a: while b: a, b = b, a % b else: a = b return a # Comment by Juan Arias de Reyna: # # I learn this method to compute EulerE[2n] from van de Lune. # # We apply the formula EulerE[2n] = (-1)^n 2**(-2n) sum_{j=0}^n a(2n,2j+1) # # where the numbers a(n,j) vanish for j > n+1 or j <= -1 and satisfies # # a(0,-1) = a(0,0) = 0; a(0,1)= 1; a(0,2) = a(0,3) = 0 # # a(n,j) = a(n-1,j) when n+j is even # a(n,j) = (j-1) a(n-1,j-1) + (j+1) a(n-1,j+1) when n+j is odd # # # But we can use only one array unidimensional a(j) since to compute # a(n,j) we only need to know a(n-1,k) where k and j are of different parity # and we have not to conserve the used values. # # We cached up the values of Euler numbers to sufficiently high order. # # Important Observation: If we pretend to use the numbers # EulerE[1], EulerE[2], ... , EulerE[n] # it is convenient to compute first EulerE[n], since the algorithm # computes first all # the previous ones, and keeps them in the CACHE MAX_EULER_CACHE = 500 def eulernum(m, _cache={0:MPZ_ONE}): r""" Computes the Euler numbers `E(n)`, which can be defined as coefficients of the Taylor expansion of `1/cosh x`: .. math :: \frac{1}{\cosh x} = \sum_{n=0}^\infty \frac{E_n}{n!} x^n Examples:: >>> [int(eulernum(n)) for n in range(11)] [1, 0, -1, 0, 5, 0, -61, 0, 1385, 0, -50521] >>> [int(eulernum(n)) for n in range(11)] # test cache [1, 0, -1, 0, 5, 0, -61, 0, 1385, 0, -50521] """ # for odd m > 1, the Euler numbers are zero if m & 1: return MPZ_ZERO f = _cache.get(m) if f: return f MAX = MAX_EULER_CACHE n = m a = [MPZ(_) for _ in [0,0,1,0,0,0]] for n in range(1, m+1): for j in range(n+1, -1, -2): a[j+1] = (j-1)*a[j] + (j+1)*a[j+2] a.append(0) suma = 0 for k in range(n+1, -1, -2): suma += a[k+1] if n <= MAX: _cache[n] = ((-1)**(n//2))*(suma // 2**n) if n == m: return ((-1)**(n//2))*suma // 2**n sympy-0.7.4.1/sympy/mpmath/libmp/exec_py3.py0000644000175000017500000000001512253362407021103 0ustar georgeskgeorgeskexec_ = exec sympy-0.7.4.1/sympy/mpmath/libmp/libmpf.py0000644000175000017500000012526412253362407020653 0ustar georgeskgeorgesk""" Low-level functions for arbitrary-precision floating-point arithmetic. """ __docformat__ = 'plaintext' import math from bisect import bisect import sys # Importing random is slow #from random import getrandbits getrandbits = None from .backend import (MPZ, MPZ_TYPE, MPZ_ZERO, MPZ_ONE, MPZ_TWO, MPZ_FIVE, BACKEND, STRICT, HASH_MODULUS, HASH_BITS, gmpy, sage, sage_utils) from .libintmath import (giant_steps, trailtable, bctable, lshift, rshift, bitcount, trailing, sqrt_fixed, numeral, isqrt, isqrt_fast, sqrtrem, bin_to_radix) # We don't pickle tuples directly for the following reasons: # 1: pickle uses str() for ints, which is inefficient when they are large # 2: pickle doesn't work for gmpy mpzs # Both problems are solved by using hex() if BACKEND == 'sage': def to_pickable(x): sign, man, exp, bc = x return sign, hex(man), exp, bc else: def to_pickable(x): sign, man, exp, bc = x return sign, hex(man)[2:], exp, bc def from_pickable(x): sign, man, exp, bc = x return (sign, MPZ(man, 16), exp, bc) class ComplexResult(ValueError): pass try: intern except NameError: intern = lambda x: x # All supported rounding modes round_nearest = intern('n') round_floor = intern('f') round_ceiling = intern('c') round_up = intern('u') round_down = intern('d') round_fast = round_down def prec_to_dps(n): """Return number of accurate decimals that can be represented with a precision of n bits.""" return max(1, int(round(int(n)/3.3219280948873626)-1)) def dps_to_prec(n): """Return the number of bits required to represent n decimals accurately.""" return max(1, int(round((int(n)+1)*3.3219280948873626))) def repr_dps(n): """Return the number of decimal digits required to represent a number with n-bit precision so that it can be uniquely reconstructed from the representation.""" dps = prec_to_dps(n) if dps == 15: return 17 return dps + 3 #----------------------------------------------------------------------------# # Some commonly needed float values # #----------------------------------------------------------------------------# # Regular number format: # (-1)**sign * mantissa * 2**exponent, plus bitcount of mantissa fzero = (0, MPZ_ZERO, 0, 0) fnzero = (1, MPZ_ZERO, 0, 0) fone = (0, MPZ_ONE, 0, 1) fnone = (1, MPZ_ONE, 0, 1) ftwo = (0, MPZ_ONE, 1, 1) ften = (0, MPZ_FIVE, 1, 3) fhalf = (0, MPZ_ONE, -1, 1) # Arbitrary encoding for special numbers: zero mantissa, nonzero exponent fnan = (0, MPZ_ZERO, -123, -1) finf = (0, MPZ_ZERO, -456, -2) fninf = (1, MPZ_ZERO, -789, -3) # Was 1e1000; this is broken in Python 2.4 math_float_inf = 1e300 * 1e300 #----------------------------------------------------------------------------# # Rounding # #----------------------------------------------------------------------------# # This function can be used to round a mantissa generally. However, # we will try to do most rounding inline for efficiency. def round_int(x, n, rnd): if rnd == round_nearest: if x >= 0: t = x >> (n-1) if t & 1 and ((t & 2) or (x & h_mask[n<300][n])): return (t>>1)+1 else: return t>>1 else: return -round_int(-x, n, rnd) if rnd == round_floor: return x >> n if rnd == round_ceiling: return -((-x) >> n) if rnd == round_down: if x >= 0: return x >> n return -((-x) >> n) if rnd == round_up: if x >= 0: return -((-x) >> n) return x >> n # These masks are used to pick out segments of numbers to determine # which direction to round when rounding to nearest. class h_mask_big: def __getitem__(self, n): return (MPZ_ONE<<(n-1))-1 h_mask_small = [0]+[((MPZ_ONE<<(_-1))-1) for _ in range(1, 300)] h_mask = [h_mask_big(), h_mask_small] # The >> operator rounds to floor. shifts_down[rnd][sign] # tells whether this is the right direction to use, or if the # number should be negated before shifting shifts_down = {round_floor:(1,0), round_ceiling:(0,1), round_down:(1,1), round_up:(0,0)} #----------------------------------------------------------------------------# # Normalization of raw mpfs # #----------------------------------------------------------------------------# # This function is called almost every time an mpf is created. # It has been optimized accordingly. def _normalize(sign, man, exp, bc, prec, rnd): """ Create a raw mpf tuple with value (-1)**sign * man * 2**exp and normalized mantissa. The mantissa is rounded in the specified direction if its size exceeds the precision. Trailing zero bits are also stripped from the mantissa to ensure that the representation is canonical. Conditions on the input: * The input must represent a regular (finite) number * The sign bit must be 0 or 1 * The mantissa must be positive * The exponent must be an integer * The bitcount must be exact If these conditions are not met, use from_man_exp, mpf_pos, or any of the conversion functions to create normalized raw mpf tuples. """ if not man: return fzero # Cut mantissa down to size if larger than target precision n = bc - prec if n > 0: if rnd == round_nearest: t = man >> (n-1) if t & 1 and ((t & 2) or (man & h_mask[n<300][n])): man = (t>>1)+1 else: man = t>>1 elif shifts_down[rnd][sign]: man >>= n else: man = -((-man)>>n) exp += n bc = prec # Strip trailing bits if not man & 1: t = trailtable[int(man & 255)] if not t: while not man & 255: man >>= 8 exp += 8 bc -= 8 t = trailtable[int(man & 255)] man >>= t exp += t bc -= t # Bit count can be wrong if the input mantissa was 1 less than # a power of 2 and got rounded up, thereby adding an extra bit. # With trailing bits removed, all powers of two have mantissa 1, # so this is easy to check for. if man == 1: bc = 1 return sign, man, exp, bc def _normalize1(sign, man, exp, bc, prec, rnd): """same as normalize, but with the added condition that man is odd or zero """ if not man: return fzero if bc <= prec: return sign, man, exp, bc n = bc - prec if rnd == round_nearest: t = man >> (n-1) if t & 1 and ((t & 2) or (man & h_mask[n<300][n])): man = (t>>1)+1 else: man = t>>1 elif shifts_down[rnd][sign]: man >>= n else: man = -((-man)>>n) exp += n bc = prec # Strip trailing bits if not man & 1: t = trailtable[int(man & 255)] if not t: while not man & 255: man >>= 8 exp += 8 bc -= 8 t = trailtable[int(man & 255)] man >>= t exp += t bc -= t # Bit count can be wrong if the input mantissa was 1 less than # a power of 2 and got rounded up, thereby adding an extra bit. # With trailing bits removed, all powers of two have mantissa 1, # so this is easy to check for. if man == 1: bc = 1 return sign, man, exp, bc try: _exp_types = (int, long) except NameError: _exp_types = (int,) def strict_normalize(sign, man, exp, bc, prec, rnd): """Additional checks on the components of an mpf. Enable tests by setting the environment variable MPMATH_STRICT to Y.""" assert type(man) == MPZ_TYPE assert type(bc) in _exp_types assert type(exp) in _exp_types assert bc == bitcount(man) return _normalize(sign, man, exp, bc, prec, rnd) def strict_normalize1(sign, man, exp, bc, prec, rnd): """Additional checks on the components of an mpf. Enable tests by setting the environment variable MPMATH_STRICT to Y.""" assert type(man) == MPZ_TYPE assert type(bc) in _exp_types assert type(exp) in _exp_types assert bc == bitcount(man) assert (not man) or (man & 1) return _normalize1(sign, man, exp, bc, prec, rnd) if BACKEND == 'gmpy' and '_mpmath_normalize' in dir(gmpy): _normalize = gmpy._mpmath_normalize _normalize1 = gmpy._mpmath_normalize if BACKEND == 'sage': _normalize = _normalize1 = sage_utils.normalize if STRICT: normalize = strict_normalize normalize1 = strict_normalize1 else: normalize = _normalize normalize1 = _normalize1 #----------------------------------------------------------------------------# # Conversion functions # #----------------------------------------------------------------------------# def from_man_exp(man, exp, prec=None, rnd=round_fast): """Create raw mpf from (man, exp) pair. The mantissa may be signed. If no precision is specified, the mantissa is stored exactly.""" man = MPZ(man) sign = 0 if man < 0: sign = 1 man = -man if man < 1024: bc = bctable[int(man)] else: bc = bitcount(man) if not prec: if not man: return fzero if not man & 1: if man & 2: return (sign, man >> 1, exp + 1, bc - 1) t = trailtable[int(man & 255)] if not t: while not man & 255: man >>= 8 exp += 8 bc -= 8 t = trailtable[int(man & 255)] man >>= t exp += t bc -= t return (sign, man, exp, bc) return normalize(sign, man, exp, bc, prec, rnd) int_cache = dict((n, from_man_exp(n, 0)) for n in range(-10, 257)) if BACKEND == 'gmpy' and '_mpmath_create' in dir(gmpy): from_man_exp = gmpy._mpmath_create if BACKEND == 'sage': from_man_exp = sage_utils.from_man_exp def from_int(n, prec=0, rnd=round_fast): """Create a raw mpf from an integer. If no precision is specified, the mantissa is stored exactly.""" if not prec: if n in int_cache: return int_cache[n] return from_man_exp(n, 0, prec, rnd) def to_man_exp(s): """Return (man, exp) of a raw mpf. Raise an error if inf/nan.""" sign, man, exp, bc = s if (not man) and exp: raise ValueError("mantissa and exponent are undefined for %s" % man) return man, exp def to_int(s, rnd=None): """Convert a raw mpf to the nearest int. Rounding is done down by default (same as int(float) in Python), but can be changed. If the input is inf/nan, an exception is raised.""" sign, man, exp, bc = s if (not man) and exp: raise ValueError("cannot convert %s to int" % man) if exp >= 0: if sign: return (-man) << exp return man << exp # Make default rounding fast if not rnd: if sign: return -(man >> (-exp)) else: return man >> (-exp) if sign: return round_int(-man, -exp, rnd) else: return round_int(man, -exp, rnd) def mpf_round_int(s, rnd): sign, man, exp, bc = s if (not man) and exp: return s if exp >= 0: return s mag = exp+bc if mag < 1: if rnd == round_ceiling: if sign: return fzero else: return fone elif rnd == round_floor: if sign: return fnone else: return fzero elif rnd == round_nearest: if mag < 0 or man == MPZ_ONE: return fzero elif sign: return fnone else: return fone else: raise NotImplementedError return mpf_pos(s, min(bc, mag), rnd) def mpf_floor(s, prec=0, rnd=round_fast): v = mpf_round_int(s, round_floor) if prec: v = mpf_pos(v, prec, rnd) return v def mpf_ceil(s, prec=0, rnd=round_fast): v = mpf_round_int(s, round_ceiling) if prec: v = mpf_pos(v, prec, rnd) return v def mpf_nint(s, prec=0, rnd=round_fast): v = mpf_round_int(s, round_nearest) if prec: v = mpf_pos(v, prec, rnd) return v def mpf_frac(s, prec=0, rnd=round_fast): return mpf_sub(s, mpf_floor(s), prec, rnd) def from_float(x, prec=53, rnd=round_fast): """Create a raw mpf from a Python float, rounding if necessary. If prec >= 53, the result is guaranteed to represent exactly the same number as the input. If prec is not specified, use prec=53.""" # frexp only raises an exception for nan on some platforms if x != x: return fnan # in Python2.5 math.frexp gives an exception for float infinity # in Python2.6 it returns (float infinity, 0) try: m, e = math.frexp(x) except: if x == math_float_inf: return finf if x == -math_float_inf: return fninf return fnan if x == math_float_inf: return finf if x == -math_float_inf: return fninf return from_man_exp(int(m*(1<<53)), e-53, prec, rnd) def to_float(s, strict=False): """ Convert a raw mpf to a Python float. The result is exact if the bitcount of s is <= 53 and no underflow/overflow occurs. If the number is too large or too small to represent as a regular float, it will be converted to inf or 0.0. Setting strict=True forces an OverflowError to be raised instead. """ sign, man, exp, bc = s if not man: if s == fzero: return 0.0 if s == finf: return math_float_inf if s == fninf: return -math_float_inf return math_float_inf/math_float_inf if sign: man = -man try: if bc < 100: return math.ldexp(man, exp) # Try resizing the mantissa. Overflow may still happen here. n = bc - 53 m = man >> n return math.ldexp(m, exp + n) except OverflowError: if strict: raise # Overflow to infinity if exp + bc > 0: if sign: return -math_float_inf else: return math_float_inf # Underflow to zero return 0.0 def from_rational(p, q, prec, rnd=round_fast): """Create a raw mpf from a rational number p/q, round if necessary.""" return mpf_div(from_int(p), from_int(q), prec, rnd) def to_rational(s): """Convert a raw mpf to a rational number. Return integers (p, q) such that s = p/q exactly.""" sign, man, exp, bc = s if sign: man = -man if bc == -1: raise ValueError("cannot convert %s to a rational number" % man) if exp >= 0: return man * (1<= 0: return (-man) << offset else: return (-man) >> (-offset) else: if offset >= 0: return man << offset else: return man >> (-offset) ############################################################################## ############################################################################## #----------------------------------------------------------------------------# # Arithmetic operations, etc. # #----------------------------------------------------------------------------# def mpf_rand(prec): """Return a raw mpf chosen randomly from [0, 1), with prec bits in the mantissa.""" global getrandbits if not getrandbits: import random getrandbits = random.getrandbits return from_man_exp(getrandbits(prec), -prec, prec, round_floor) def mpf_eq(s, t): """Test equality of two raw mpfs. This is simply tuple comparion unless either number is nan, in which case the result is False.""" if not s[1] or not t[1]: if s == fnan or t == fnan: return False return s == t def mpf_hash(s): # Duplicate the new hash algorithm introduces in Python 3.2. if sys.version >= "3.2": ssign, sman, sexp, sbc = s # Handle special numbers if not sman: if s == fnan: return sys.hash_info.nan if s == finf: return sys.hash_info.inf if s == fninf: return -sys.hash_info.inf h = sman % HASH_MODULUS if sexp >= 0: sexp = sexp % HASH_BITS else: sexp = HASH_BITS - 1 - ((-1 - sexp) % HASH_BITS) h = (h << sexp) % HASH_MODULUS if ssign: h = -h if h == -1: h == -2 return int(h) else: try: # Try to be compatible with hash values for floats and ints return hash(to_float(s, strict=1)) except OverflowError: # We must unfortunately sacrifice compatibility with ints here. # We could do hash(man << exp) when the exponent is positive, but # this would cause unreasonable inefficiency for large numbers. return hash(s) def mpf_cmp(s, t): """Compare the raw mpfs s and t. Return -1 if s < t, 0 if s == t, and 1 if s > t. (Same convention as Python's cmp() function.)""" # In principle, a comparison amounts to determining the sign of s-t. # A full subtraction is relatively slow, however, so we first try to # look at the components. ssign, sman, sexp, sbc = s tsign, tman, texp, tbc = t # Handle zeros and special numbers if not sman or not tman: if s == fzero: return -mpf_sign(t) if t == fzero: return mpf_sign(s) if s == t: return 0 # Follow same convention as Python's cmp for float nan if t == fnan: return 1 if s == finf: return 1 if t == fninf: return 1 return -1 # Different sides of zero if ssign != tsign: if not ssign: return 1 return -1 # This reduces to direct integer comparison if sexp == texp: if sman == tman: return 0 if sman > tman: if ssign: return -1 else: return 1 else: if ssign: return 1 else: return -1 # Check position of the highest set bit in each number. If # different, there is certainly an inequality. a = sbc + sexp b = tbc + texp if ssign: if a < b: return 1 if a > b: return -1 else: if a < b: return -1 if a > b: return 1 # Both numbers have the same highest bit. Subtract to find # how the lower bits compare. delta = mpf_sub(s, t, 5, round_floor) if delta[0]: return -1 return 1 def mpf_lt(s, t): if s == fnan or t == fnan: return False return mpf_cmp(s, t) < 0 def mpf_le(s, t): if s == fnan or t == fnan: return False return mpf_cmp(s, t) <= 0 def mpf_gt(s, t): if s == fnan or t == fnan: return False return mpf_cmp(s, t) > 0 def mpf_ge(s, t): if s == fnan or t == fnan: return False return mpf_cmp(s, t) >= 0 def mpf_min_max(seq): min = max = seq[0] for x in seq[1:]: if mpf_lt(x, min): min = x if mpf_gt(x, max): max = x return min, max def mpf_pos(s, prec=0, rnd=round_fast): """Calculate 0+s for a raw mpf (i.e., just round s to the specified precision).""" if prec: sign, man, exp, bc = s if (not man) and exp: return s return normalize1(sign, man, exp, bc, prec, rnd) return s def mpf_neg(s, prec=None, rnd=round_fast): """Negate a raw mpf (return -s), rounding the result to the specified precision. The prec argument can be omitted to do the operation exactly.""" sign, man, exp, bc = s if not man: if exp: if s == finf: return fninf if s == fninf: return finf return s if not prec: return (1-sign, man, exp, bc) return normalize1(1-sign, man, exp, bc, prec, rnd) def mpf_abs(s, prec=None, rnd=round_fast): """Return abs(s) of the raw mpf s, rounded to the specified precision. The prec argument can be omitted to generate an exact result.""" sign, man, exp, bc = s if (not man) and exp: if s == fninf: return finf return s if not prec: if sign: return (0, man, exp, bc) return s return normalize1(0, man, exp, bc, prec, rnd) def mpf_sign(s): """Return -1, 0, or 1 (as a Python int, not a raw mpf) depending on whether s is negative, zero, or positive. (Nan is taken to give 0.)""" sign, man, exp, bc = s if not man: if s == finf: return 1 if s == fninf: return -1 return 0 return (-1) ** sign def mpf_add(s, t, prec=0, rnd=round_fast, _sub=0): """ Add the two raw mpf values s and t. With prec=0, no rounding is performed. Note that this can produce a very large mantissa (potentially too large to fit in memory) if exponents are far apart. """ ssign, sman, sexp, sbc = s tsign, tman, texp, tbc = t tsign ^= _sub # Standard case: two nonzero, regular numbers if sman and tman: offset = sexp - texp if offset: if offset > 0: # Outside precision range; only need to perturb if offset > 100 and prec: delta = sbc + sexp - tbc - texp if delta > prec + 4: offset = prec + 4 sman <<= offset if tsign == ssign: sman += 1 else: sman -= 1 return normalize1(ssign, sman, sexp-offset, bitcount(sman), prec, rnd) # Add if ssign == tsign: man = tman + (sman << offset) # Subtract else: if ssign: man = tman - (sman << offset) else: man = (sman << offset) - tman if man >= 0: ssign = 0 else: man = -man ssign = 1 bc = bitcount(man) return normalize1(ssign, man, texp, bc, prec or bc, rnd) elif offset < 0: # Outside precision range; only need to perturb if offset < -100 and prec: delta = tbc + texp - sbc - sexp if delta > prec + 4: offset = prec + 4 tman <<= offset if ssign == tsign: tman += 1 else: tman -= 1 return normalize1(tsign, tman, texp-offset, bitcount(tman), prec, rnd) # Add if ssign == tsign: man = sman + (tman << -offset) # Subtract else: if tsign: man = sman - (tman << -offset) else: man = (tman << -offset) - sman if man >= 0: ssign = 0 else: man = -man ssign = 1 bc = bitcount(man) return normalize1(ssign, man, sexp, bc, prec or bc, rnd) # Equal exponents; no shifting necessary if ssign == tsign: man = tman + sman else: if ssign: man = tman - sman else: man = sman - tman if man >= 0: ssign = 0 else: man = -man ssign = 1 bc = bitcount(man) return normalize(ssign, man, texp, bc, prec or bc, rnd) # Handle zeros and special numbers if _sub: t = mpf_neg(t) if not sman: if sexp: if s == t or tman or not texp: return s return fnan if tman: return normalize1(tsign, tman, texp, tbc, prec or tbc, rnd) return t if texp: return t if sman: return normalize1(ssign, sman, sexp, sbc, prec or sbc, rnd) return s def mpf_sub(s, t, prec=0, rnd=round_fast): """Return the difference of two raw mpfs, s-t. This function is simply a wrapper of mpf_add that changes the sign of t.""" return mpf_add(s, t, prec, rnd, 1) def mpf_sum(xs, prec=0, rnd=round_fast, absolute=False): """ Sum a list of mpf values efficiently and accurately (typically no temporary roundoff occurs). If prec=0, the final result will not be rounded either. There may be roundoff error or cancellation if extremely large exponent differences occur. With absolute=True, sums the absolute values. """ man = 0 exp = 0 max_extra_prec = prec*2 or 1000000 # XXX special = None for x in xs: xsign, xman, xexp, xbc = x if xman: if xsign and not absolute: xman = -xman delta = xexp - exp if xexp >= exp: # x much larger than existing sum? # first: quick test if (delta > max_extra_prec) and \ ((not man) or delta-bitcount(abs(man)) > max_extra_prec): man = xman exp = xexp else: man += (xman << delta) else: delta = -delta # x much smaller than existing sum? if delta-xbc > max_extra_prec: if not man: man, exp = xman, xexp else: man = (man << delta) + xman exp = xexp elif xexp: if absolute: x = mpf_abs(x) special = mpf_add(special or fzero, x, 1) # Will be inf or nan if special: return special return from_man_exp(man, exp, prec, rnd) def gmpy_mpf_mul(s, t, prec=0, rnd=round_fast): """Multiply two raw mpfs""" ssign, sman, sexp, sbc = s tsign, tman, texp, tbc = t sign = ssign ^ tsign man = sman*tman if man: bc = bitcount(man) if prec: return normalize1(sign, man, sexp+texp, bc, prec, rnd) else: return (sign, man, sexp+texp, bc) s_special = (not sman) and sexp t_special = (not tman) and texp if not s_special and not t_special: return fzero if fnan in (s, t): return fnan if (not tman) and texp: s, t = t, s if t == fzero: return fnan return {1:finf, -1:fninf}[mpf_sign(s) * mpf_sign(t)] def gmpy_mpf_mul_int(s, n, prec, rnd=round_fast): """Multiply by a Python integer.""" sign, man, exp, bc = s if not man: return mpf_mul(s, from_int(n), prec, rnd) if not n: return fzero if n < 0: sign ^= 1 n = -n man *= n return normalize(sign, man, exp, bitcount(man), prec, rnd) def python_mpf_mul(s, t, prec=0, rnd=round_fast): """Multiply two raw mpfs""" ssign, sman, sexp, sbc = s tsign, tman, texp, tbc = t sign = ssign ^ tsign man = sman*tman if man: bc = sbc + tbc - 1 bc += int(man>>bc) if prec: return normalize1(sign, man, sexp+texp, bc, prec, rnd) else: return (sign, man, sexp+texp, bc) s_special = (not sman) and sexp t_special = (not tman) and texp if not s_special and not t_special: return fzero if fnan in (s, t): return fnan if (not tman) and texp: s, t = t, s if t == fzero: return fnan return {1:finf, -1:fninf}[mpf_sign(s) * mpf_sign(t)] def python_mpf_mul_int(s, n, prec, rnd=round_fast): """Multiply by a Python integer.""" sign, man, exp, bc = s if not man: return mpf_mul(s, from_int(n), prec, rnd) if not n: return fzero if n < 0: sign ^= 1 n = -n man *= n # Generally n will be small if n < 1024: bc += bctable[int(n)] - 1 else: bc += bitcount(n) - 1 bc += int(man>>bc) return normalize(sign, man, exp, bc, prec, rnd) if BACKEND == 'gmpy': mpf_mul = gmpy_mpf_mul mpf_mul_int = gmpy_mpf_mul_int else: mpf_mul = python_mpf_mul mpf_mul_int = python_mpf_mul_int def mpf_shift(s, n): """Quickly multiply the raw mpf s by 2**n without rounding.""" sign, man, exp, bc = s if not man: return s return sign, man, exp+n, bc def mpf_frexp(x): """Convert x = y*2**n to (y, n) with abs(y) in [0.5, 1) if nonzero""" sign, man, exp, bc = x if not man: if x == fzero: return (fzero, 0) else: raise ValueError return mpf_shift(x, -bc-exp), bc+exp def mpf_div(s, t, prec, rnd=round_fast): """Floating-point division""" ssign, sman, sexp, sbc = s tsign, tman, texp, tbc = t if not sman or not tman: if s == fzero: if t == fzero: raise ZeroDivisionError if t == fnan: return fnan return fzero if t == fzero: raise ZeroDivisionError s_special = (not sman) and sexp t_special = (not tman) and texp if s_special and t_special: return fnan if s == fnan or t == fnan: return fnan if not t_special: if t == fzero: return fnan return {1:finf, -1:fninf}[mpf_sign(s) * mpf_sign(t)] return fzero sign = ssign ^ tsign if tman == 1: return normalize1(sign, sman, sexp-texp, sbc, prec, rnd) # Same strategy as for addition: if there is a remainder, perturb # the result a few bits outside the precision range before rounding extra = prec - sbc + tbc + 5 if extra < 5: extra = 5 quot, rem = divmod(sman< sexp+sbc: return s # Another important special case: this allows us to do e.g. x % 1.0 # to find the fractional part of x, and it will work when x is huge. if tman == 1 and sexp > texp+tbc: return fzero base = min(sexp, texp) sman = (-1)**ssign * sman tman = (-1)**tsign * tman man = (sman << (sexp-base)) % (tman << (texp-base)) if man >= 0: sign = 0 else: man = -man sign = 1 return normalize(sign, man, base, bitcount(man), prec, rnd) reciprocal_rnd = { round_down : round_up, round_up : round_down, round_floor : round_ceiling, round_ceiling : round_floor, round_nearest : round_nearest } negative_rnd = { round_down : round_down, round_up : round_up, round_floor : round_ceiling, round_ceiling : round_floor, round_nearest : round_nearest } def mpf_pow_int(s, n, prec, rnd=round_fast): """Compute s**n, where s is a raw mpf and n is a Python integer.""" sign, man, exp, bc = s if (not man) and exp: if s == finf: if n > 0: return s if n == 0: return fnan return fzero if s == fninf: if n > 0: return [finf, fninf][n & 1] if n == 0: return fnan return fzero return fnan n = int(n) if n == 0: return fone if n == 1: return mpf_pos(s, prec, rnd) if n == 2: _, man, exp, bc = s if not man: return fzero man = man*man if man == 1: return (0, MPZ_ONE, exp+exp, 1) bc = bc + bc - 2 bc += bctable[int(man>>bc)] return normalize1(0, man, exp+exp, bc, prec, rnd) if n == -1: return mpf_div(fone, s, prec, rnd) if n < 0: inverse = mpf_pow_int(s, -n, prec+5, reciprocal_rnd[rnd]) return mpf_div(fone, inverse, prec, rnd) result_sign = sign & n # Use exact integer power when the exact mantissa is small if man == 1: return (result_sign, MPZ_ONE, exp*n, 1) if bc*n < 1000: man **= n return normalize1(result_sign, man, exp*n, bitcount(man), prec, rnd) # Use directed rounding all the way through to maintain rigorous # bounds for interval arithmetic rounds_down = (rnd == round_nearest) or \ shifts_down[rnd][result_sign] # Now we perform binary exponentiation. Need to estimate precision # to avoid rounding errors from temporary operations. Roughly log_2(n) # operations are performed. workprec = prec + 4*bitcount(n) + 4 _, pm, pe, pbc = fone while 1: if n & 1: pm = pm*man pe = pe+exp pbc += bc - 2 pbc = pbc + bctable[int(pm >> pbc)] if pbc > workprec: if rounds_down: pm = pm >> (pbc-workprec) else: pm = -((-pm) >> (pbc-workprec)) pe += pbc - workprec pbc = workprec n -= 1 if not n: break man = man*man exp = exp+exp bc = bc + bc - 2 bc = bc + bctable[int(man >> bc)] if bc > workprec: if rounds_down: man = man >> (bc-workprec) else: man = -((-man) >> (bc-workprec)) exp += bc - workprec bc = workprec n = n // 2 return normalize(result_sign, pm, pe, pbc, prec, rnd) def mpf_perturb(x, eps_sign, prec, rnd): """ For nonzero x, calculate x + eps with directed rounding, where eps < prec relatively and eps has the given sign (0 for positive, 1 for negative). With rounding to nearest, this is taken to simply normalize x to the given precision. """ if rnd == round_nearest: return mpf_pos(x, prec, rnd) sign, man, exp, bc = x eps = (eps_sign, MPZ_ONE, exp+bc-prec-1, 1) if sign: away = (rnd in (round_down, round_ceiling)) ^ eps_sign else: away = (rnd in (round_up, round_ceiling)) ^ eps_sign if away: return mpf_add(x, eps, prec, rnd) else: return mpf_pos(x, prec, rnd) #----------------------------------------------------------------------------# # Radix conversion # #----------------------------------------------------------------------------# def to_digits_exp(s, dps): """Helper function for representing the floating-point number s as a decimal with dps digits. Returns (sign, string, exponent) where sign is '' or '-', string is the digit string, and exponent is the decimal exponent as an int. If inexact, the decimal representation is rounded toward zero.""" # Extract sign first so it doesn't mess up the string digit count if s[0]: sign = '-' s = mpf_neg(s) else: sign = '' _sign, man, exp, bc = s if not man: return '', '0', 0 bitprec = int(dps * math.log(10,2)) + 10 # Cut down to size # TODO: account for precision when doing this exp_from_1 = exp + bc if abs(exp_from_1) > 3500: from .libelefun import mpf_ln2, mpf_ln10 # Set b = int(exp * log(2)/log(10)) # If exp is huge, we must use high-precision arithmetic to # find the nearest power of ten expprec = bitcount(abs(exp)) + 5 tmp = from_int(exp) tmp = mpf_mul(tmp, mpf_ln2(expprec)) tmp = mpf_div(tmp, mpf_ln10(expprec), expprec) b = to_int(tmp) s = mpf_div(s, mpf_pow_int(ften, b, bitprec), bitprec) _sign, man, exp, bc = s exponent = b else: exponent = 0 # First, calculate mantissa digits by converting to a binary # fixed-point number and then converting that number to # a decimal fixed-point number. fixprec = max(bitprec - exp - bc, 0) fixdps = int(fixprec / math.log(10,2) + 0.5) sf = to_fixed(s, fixprec) sd = bin_to_radix(sf, fixprec, 10, fixdps) digits = numeral(sd, base=10, size=dps) exponent += len(digits) - fixdps - 1 return sign, digits, exponent def to_str(s, dps, strip_zeros=True, min_fixed=None, max_fixed=None, show_zero_exponent=False): """ Convert a raw mpf to a decimal floating-point literal with at most `dps` decimal digits in the mantissa (not counting extra zeros that may be inserted for visual purposes). The number will be printed in fixed-point format if the position of the leading digit is strictly between min_fixed (default = min(-dps/3,-5)) and max_fixed (default = dps). To force fixed-point format always, set min_fixed = -inf, max_fixed = +inf. To force floating-point format, set min_fixed >= max_fixed. The literal is formatted so that it can be parsed back to a number by to_str, float() or Decimal(). """ # Special numbers if not s[1]: if s == fzero: if dps: t = '0.0' else: t = '.0' if show_zero_exponent: t += 'e+0' return t if s == finf: return '+inf' if s == fninf: return '-inf' if s == fnan: return 'nan' raise ValueError if min_fixed is None: min_fixed = min(-(dps//3), -5) if max_fixed is None: max_fixed = dps # to_digits_exp rounds to floor. # This sometimes kills some instances of "...00001" sign, digits, exponent = to_digits_exp(s, dps+3) # No digits: show only .0; round exponent to nearest if not dps: if digits[0] in '56789': exponent += 1 digits = ".0" else: # Rounding up kills some instances of "...99999" if len(digits) > dps and digits[dps] in '56789' and \ (dps < 500 or digits[dps-4:dps] == '9999'): digits2 = str(int(digits[:dps]) + 1) if len(digits2) > dps: digits2 = digits2[:dps] exponent += 1 digits = digits2 else: digits = digits[:dps] # Prettify numbers close to unit magnitude if min_fixed < exponent < max_fixed: if exponent < 0: digits = ("0"*int(-exponent)) + digits split = 1 else: split = exponent + 1 if split > dps: digits += "0"*(split-dps) exponent = 0 else: split = 1 digits = (digits[:split] + "." + digits[split:]) if strip_zeros: # Clean up trailing zeros digits = digits.rstrip('0') if digits[-1] == ".": digits += "0" if exponent == 0 and dps and not show_zero_exponent: return sign + digits if exponent >= 0: return sign + digits + "e+" + str(exponent) if exponent < 0: return sign + digits + "e" + str(exponent) def str_to_man_exp(x, base=10): """Helper function for from_str.""" # Verify that the input is a valid float literal float(x) # Split into mantissa, exponent x = x.lower() parts = x.split('e') if len(parts) == 1: exp = 0 else: # == 2 x = parts[0] exp = int(parts[1]) # Look for radix point in mantissa parts = x.split('.') if len(parts) == 2: a, b = parts[0], parts[1].rstrip('0') exp -= len(b) x = a + b x = MPZ(int(x, base)) return x, exp special_str = {'inf':finf, '+inf':finf, '-inf':fninf, 'nan':fnan} def from_str(x, prec, rnd=round_fast): """Create a raw mpf from a decimal literal, rounding in the specified direction if the input number cannot be represented exactly as a binary floating-point number with the given number of bits. The literal syntax accepted is the same as for Python floats. TODO: the rounding does not work properly for large exponents. """ x = x.strip() if x in special_str: return special_str[x] if '/' in x: p, q = x.split('/') return from_rational(int(p), int(q), prec, rnd) man, exp = str_to_man_exp(x, base=10) # XXX: appropriate cutoffs & track direction # note no factors of 5 if abs(exp) > 400: s = from_int(man, prec+10) s = mpf_mul(s, mpf_pow_int(ften, exp, prec+10), prec, rnd) else: if exp >= 0: s = from_int(man * 10**exp, prec, rnd) else: s = from_rational(man, 10**-exp, prec, rnd) return s # Binary string conversion. These are currently mainly used for debugging # and could use some improvement in the future def from_bstr(x): man, exp = str_to_man_exp(x, base=2) man = MPZ(man) sign = 0 if man < 0: man = -man sign = 1 bc = bitcount(man) return normalize(sign, man, exp, bc, bc, round_floor) def to_bstr(x): sign, man, exp, bc = x return ['','-'][sign] + numeral(man, size=bitcount(man), base=2) + ("e%i" % exp) #----------------------------------------------------------------------------# # Square roots # #----------------------------------------------------------------------------# def mpf_sqrt(s, prec, rnd=round_fast): """ Compute the square root of a nonnegative mpf value. The result is correctly rounded. """ sign, man, exp, bc = s if sign: raise ComplexResult("square root of a negative number") if not man: return s if exp & 1: exp -= 1 man <<= 1 bc += 1 elif man == 1: return normalize1(sign, man, exp//2, bc, prec, rnd) shift = max(4, 2*prec-bc+4) shift += shift & 1 if rnd in 'fd': man = isqrt(man<> (20 + 6) # Khinchin's constant is relatively difficult to compute. Here # we use the rational zeta series # oo 2*n-1 # ___ ___ # \ ` zeta(2*n)-1 \ ` (-1)^(k+1) # log(K)*log(2) = ) ------------ ) ---------- # /___. n /___. k # n = 1 k = 1 # which adds half a digit per term. The essential trick for achieving # reasonable efficiency is to recycle both the values of the zeta # function (essentially Bernoulli numbers) and the partial terms of # the inner sum. # An alternative might be to use K = 2*exp[1/log(2) X] where # / 1 1 [ pi*x*(1-x^2) ] # X = | ------ log [ ------------ ]. # / 0 x(1+x) [ sin(pi*x) ] # and integrate numerically. In practice, this seems to be slightly # slower than the zeta series at high precision. @constant_memo def khinchin_fixed(prec): wp = int(prec + prec**0.5 + 15) s = MPZ_ZERO fac = from_int(4) t = ONE = MPZ_ONE << wp pi = mpf_pi(wp) pipow = twopi2 = mpf_shift(mpf_mul(pi, pi, wp), 2) n = 1 while 1: zeta2n = mpf_abs(mpf_bernoulli(2*n, wp)) zeta2n = mpf_mul(zeta2n, pipow, wp) zeta2n = mpf_div(zeta2n, fac, wp) zeta2n = to_fixed(zeta2n, wp) term = (((zeta2n - ONE) * t) // n) >> wp if term < 100: break #if not n % 10: # print n, math.log(int(abs(term))) s += term t += ONE//(2*n+1) - ONE//(2*n) n += 1 fac = mpf_mul_int(fac, (2*n)*(2*n-1), wp) pipow = mpf_mul(pipow, twopi2, wp) s = (s << wp) // ln2_fixed(wp) K = mpf_exp(from_man_exp(s, -wp), wp) K = to_fixed(K, prec) return K # Glaisher's constant is defined as A = exp(1/2 - zeta'(-1)). # One way to compute it would be to perform direct numerical # differentiation, but computing arbitrary Riemann zeta function # values at high precision is expensive. We instead use the formula # A = exp((6 (-zeta'(2))/pi^2 + log 2 pi + gamma)/12) # and compute zeta'(2) from the series representation # oo # ___ # \ log k # -zeta'(2) = ) ----- # /___ 2 # k # k = 2 # This series converges exceptionally slowly, but can be accelerated # using Euler-Maclaurin formula. The important insight is that the # E-M integral can be done in closed form and that the high order # are given by # n / \ # d | log x | a + b log x # --- | ----- | = ----------- # n | 2 | 2 + n # dx \ x / x # where a and b are integers given by a simple recurrence. Note # that just one logarithm is needed. However, lots of integer # logarithms are required for the initial summation. # This algorithm could possibly be turned into a faster algorithm # for general evaluation of zeta(s) or zeta'(s); this should be # looked into. @constant_memo def glaisher_fixed(prec): wp = prec + 30 # Number of direct terms to sum before applying the Euler-Maclaurin # formula to the tail. TODO: choose more intelligently N = int(0.33*prec + 5) ONE = MPZ_ONE << wp # Euler-Maclaurin, step 1: sum log(k)/k**2 for k from 2 to N-1 s = MPZ_ZERO for k in range(2, N): #print k, N s += log_int_fixed(k, wp) // k**2 logN = log_int_fixed(N, wp) #logN = to_fixed(mpf_log(from_int(N), wp+20), wp) # E-M step 2: integral of log(x)/x**2 from N to inf s += (ONE + logN) // N # E-M step 3: endpoint correction term f(N)/2 s += logN // (N**2 * 2) # E-M step 4: the series of derivatives pN = N**3 a = 1 b = -2 j = 3 fac = from_int(2) k = 1 while 1: # D(2*k-1) * B(2*k) / fac(2*k) [D(n) = nth derivative] D = ((a << wp) + b*logN) // pN D = from_man_exp(D, -wp) B = mpf_bernoulli(2*k, wp) term = mpf_mul(B, D, wp) term = mpf_div(term, fac, wp) term = to_fixed(term, wp) if abs(term) < 100: break #if not k % 10: # print k, math.log(int(abs(term)), 10) s -= term # Advance derivative twice a, b, pN, j = b-a*j, -j*b, pN*N, j+1 a, b, pN, j = b-a*j, -j*b, pN*N, j+1 k += 1 fac = mpf_mul_int(fac, (2*k)*(2*k-1), wp) # A = exp((6*s/pi**2 + log(2*pi) + euler)/12) pi = pi_fixed(wp) s *= 6 s = (s << wp) // (pi**2 >> wp) s += euler_fixed(wp) s += to_fixed(mpf_log(from_man_exp(2*pi, -wp), wp), wp) s //= 12 A = mpf_exp(from_man_exp(s, -wp), wp) return to_fixed(A, prec) # Apery's constant can be computed using the very rapidly convergent # series # oo # ___ 2 10 # \ n 205 n + 250 n + 77 (n!) # zeta(3) = ) (-1) ------------------- ---------- # /___ 64 5 # n = 0 ((2n+1)!) @constant_memo def apery_fixed(prec): prec += 20 d = MPZ_ONE << prec term = MPZ(77) << prec n = 1 s = MPZ_ZERO while term: s += term d *= (n**10) d //= (((2*n+1)**5) * (2*n)**5) term = (-1)**n * (205*(n**2) + 250*n + 77) * d n += 1 return s >> (20 + 6) """ Euler's constant (gamma) is computed using the Brent-McMillan formula, gamma ~= I(n)/J(n) - log(n), where I(n) = sum_{k=0,1,2,...} (n**k / k!)**2 * H(k) J(n) = sum_{k=0,1,2,...} (n**k / k!)**2 H(k) = 1 + 1/2 + 1/3 + ... + 1/k The error is bounded by O(exp(-4n)). Choosing n to be a power of two, 2**p, the logarithm becomes particularly easy to calculate.[1] We use the formulation of Algorithm 3.9 in [2] to make the summation more efficient. Reference: [1] Xavier Gourdon & Pascal Sebah, The Euler constant: gamma http://numbers.computation.free.fr/Constants/Gamma/gamma.pdf [2] Jonathan Borwein & David Bailey, Mathematics by Experiment, A K Peters, 2003 """ @constant_memo def euler_fixed(prec): extra = 30 prec += extra # choose p such that exp(-4*(2**p)) < 2**-n p = int(math.log((prec/4) * math.log(2), 2)) + 1 n = 2**p A = U = -p*ln2_fixed(prec) B = V = MPZ_ONE << prec k = 1 while 1: B = B*n**2//k**2 A = (A*n**2//k + B)//k U += A V += B if max(abs(A), abs(B)) < 100: break k += 1 return (U<<(prec-extra))//V # Use zeta accelerated formulas for the Mertens and twin # prime constants; see # http://mathworld.wolfram.com/MertensConstant.html # http://mathworld.wolfram.com/TwinPrimesConstant.html @constant_memo def mertens_fixed(prec): wp = prec + 20 m = 2 s = mpf_euler(wp) while 1: t = mpf_zeta_int(m, wp) if t == fone: break t = mpf_log(t, wp) t = mpf_mul_int(t, moebius(m), wp) t = mpf_div(t, from_int(m), wp) s = mpf_add(s, t) m += 1 return to_fixed(s, prec) @constant_memo def twinprime_fixed(prec): def I(n): return sum(moebius(d)<<(n//d) for d in xrange(1,n+1) if not n%d)//n wp = 2*prec + 30 res = fone primes = [from_rational(1,p,wp) for p in [2,3,5,7]] ppowers = [mpf_mul(p,p,wp) for p in primes] n = 2 while 1: a = mpf_zeta_int(n, wp) for i in range(4): a = mpf_mul(a, mpf_sub(fone, ppowers[i]), wp) ppowers[i] = mpf_mul(ppowers[i], primes[i], wp) a = mpf_pow_int(a, -I(n), wp) if mpf_pos(a, prec+10, 'n') == fone: break #from libmpf import to_str #print n, to_str(mpf_sub(fone, a), 6) res = mpf_mul(res, a, wp) n += 1 res = mpf_mul(res, from_int(3*15*35), wp) res = mpf_div(res, from_int(4*16*36), wp) return to_fixed(res, prec) mpf_euler = def_mpf_constant(euler_fixed) mpf_apery = def_mpf_constant(apery_fixed) mpf_khinchin = def_mpf_constant(khinchin_fixed) mpf_glaisher = def_mpf_constant(glaisher_fixed) mpf_catalan = def_mpf_constant(catalan_fixed) mpf_mertens = def_mpf_constant(mertens_fixed) mpf_twinprime = def_mpf_constant(twinprime_fixed) #-----------------------------------------------------------------------# # # # Bernoulli numbers # # # #-----------------------------------------------------------------------# MAX_BERNOULLI_CACHE = 3000 """ Small Bernoulli numbers and factorials are used in numerous summations, so it is critical for speed that sequential computation is fast and that values are cached up to a fairly high threshold. On the other hand, we also want to support fast computation of isolated large numbers. Currently, no such acceleration is provided for integer factorials (though it is for large floating-point factorials, which are computed via gamma if the precision is low enough). For sequential computation of Bernoulli numbers, we use 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 For isolated large Bernoulli numbers, we use the Riemann zeta function to calculate a numerical value for B_n. The von Staudt-Clausen theorem can then be used to optionally find the exact value of the numerator and denominator. """ bernoulli_cache = {} f3 = from_int(3) f6 = from_int(6) def bernoulli_size(n): """Accurately estimate the size of B_n (even n > 2 only)""" lgn = math.log(n,2) return int(2.326 + 0.5*lgn + n*(lgn - 4.094)) BERNOULLI_PREC_CUTOFF = bernoulli_size(MAX_BERNOULLI_CACHE) def mpf_bernoulli(n, prec, rnd=None): """Computation of Bernoulli numbers (numerically)""" if n < 2: if n < 0: raise ValueError("Bernoulli numbers only defined for n >= 0") if n == 0: return fone if n == 1: return mpf_neg(fhalf) # For odd n > 1, the Bernoulli numbers are zero if n & 1: return fzero # If precision is extremely high, we can save time by computing # the Bernoulli number at a lower precision that is sufficient to # obtain the exact fraction, round to the exact fraction, and # convert the fraction back to an mpf value at the original precision if prec > BERNOULLI_PREC_CUTOFF and prec > bernoulli_size(n)*1.1 + 1000: p, q = bernfrac(n) return from_rational(p, q, prec, rnd or round_floor) if n > MAX_BERNOULLI_CACHE: return mpf_bernoulli_huge(n, prec, rnd) wp = prec + 30 # Reuse nearby precisions wp += 32 - (prec & 31) cached = bernoulli_cache.get(wp) if cached: numbers, state = cached if n in numbers: if not rnd: return numbers[n] return mpf_pos(numbers[n], prec, rnd) m, bin, bin1 = state if n - m > 10: return mpf_bernoulli_huge(n, prec, rnd) else: if n > 10: return mpf_bernoulli_huge(n, prec, rnd) numbers = {0:fone} m, bin, bin1 = state = [2, MPZ(10), MPZ_ONE] bernoulli_cache[wp] = (numbers, state) while m <= n: #print m case = m % 6 # Accurately estimate size of B_m so we can use # fixed point math without using too much precision szbm = bernoulli_size(m) s = 0 sexp = max(0, szbm) - wp if m < 6: a = MPZ_ZERO else: a = bin1 for j in xrange(1, m//6+1): usign, uman, uexp, ubc = u = numbers[m-6*j] if usign: uman = -uman s += lshift(a*uman, uexp-sexp) # Update inner binomial coefficient j6 = 6*j a *= ((m-5-j6)*(m-4-j6)*(m-3-j6)*(m-2-j6)*(m-1-j6)*(m-j6)) a //= ((4+j6)*(5+j6)*(6+j6)*(7+j6)*(8+j6)*(9+j6)) if case == 0: b = mpf_rdiv_int(m+3, f3, wp) if case == 2: b = mpf_rdiv_int(m+3, f3, wp) if case == 4: b = mpf_rdiv_int(-m-3, f6, wp) s = from_man_exp(s, sexp, wp) b = mpf_div(mpf_sub(b, s, wp), from_int(bin), wp) numbers[m] = b m += 2 # Update outer binomial coefficient bin = bin * ((m+2)*(m+3)) // (m*(m-1)) if m > 6: bin1 = bin1 * ((2+m)*(3+m)) // ((m-7)*(m-6)) state[:] = [m, bin, bin1] return numbers[n] def mpf_bernoulli_huge(n, prec, rnd=None): wp = prec + 10 piprec = wp + int(math.log(n,2)) v = mpf_gamma_int(n+1, wp) v = mpf_mul(v, mpf_zeta_int(n, wp), wp) v = mpf_mul(v, mpf_pow_int(mpf_pi(piprec), -n, wp)) v = mpf_shift(v, 1-n) if not n & 3: v = mpf_neg(v) return mpf_pos(v, prec, rnd or round_fast) def bernfrac(n): r""" Returns a tuple of integers `(p, q)` such that `p/q = B_n` exactly, where `B_n` denotes the `n`-th Bernoulli number. The fraction is always reduced to lowest terms. Note that for `n > 1` and `n` odd, `B_n = 0`, and `(0, 1)` is returned. **Examples** The first few Bernoulli numbers are exactly:: >>> from mpmath import * >>> for n in range(15): ... p, q = bernfrac(n) ... print("%s %s/%s" % (n, p, q)) ... 0 1/1 1 -1/2 2 1/6 3 0/1 4 -1/30 5 0/1 6 1/42 7 0/1 8 -1/30 9 0/1 10 5/66 11 0/1 12 -691/2730 13 0/1 14 7/6 This function works for arbitrarily large `n`:: >>> p, q = bernfrac(10**4) >>> print(q) 2338224387510 >>> print(len(str(p))) 27692 >>> mp.dps = 15 >>> print(mpf(p) / q) -9.04942396360948e+27677 >>> print(bernoulli(10**4)) -9.04942396360948e+27677 .. note :: :func:`~mpmath.bernoulli` computes a floating-point approximation directly, without computing the exact fraction first. This is much faster for large `n`. **Algorithm** :func:`~mpmath.bernfrac` works by computing the value of `B_n` numerically and then using the von Staudt-Clausen theorem [1] to reconstruct the exact fraction. For large `n`, this is significantly faster than computing `B_1, B_2, \ldots, B_2` recursively with exact arithmetic. The implementation has been tested for `n = 10^m` up to `m = 6`. In practice, :func:`~mpmath.bernfrac` appears to be about three times slower than the specialized program calcbn.exe [2] **References** 1. MathWorld, von Staudt-Clausen Theorem: http://mathworld.wolfram.com/vonStaudt-ClausenTheorem.html 2. The Bernoulli Number Page: http://www.bernoulli.org/ """ n = int(n) if n < 3: return [(1, 1), (-1, 2), (1, 6)][n] if n & 1: return (0, 1) q = 1 for k in list_primes(n+1): if not (n % (k-1)): q *= k prec = bernoulli_size(n) + int(math.log(q,2)) + 20 b = mpf_bernoulli(n, prec) p = mpf_mul(b, from_int(q)) pint = to_int(p, round_nearest) return (pint, q) #-----------------------------------------------------------------------# # # # The gamma function (OLD IMPLEMENTATION) # # # #-----------------------------------------------------------------------# """ We compute the real factorial / gamma function using Spouge's approximation x! = (x+a)**(x+1/2) * exp(-x-a) * [c_0 + S(x) + eps] where S(x) is the sum of c_k/(x+k) from k = 1 to a-1 and the coefficients are given by c_0 = sqrt(2*pi) (-1)**(k-1) c_k = ----------- (a-k)**(k-1/2) exp(-k+a), k = 1,2,...,a-1 (k - 1)! As proved by Spouge, if we choose a = log(2)/log(2*pi)*n = 0.38*n, the relative error eps is less than 2^(-n) for any x in the right complex half-plane (assuming a > 2). In practice, it seems that a can be chosen quite a bit lower still (30-50%); this possibility should be investigated. For negative x, we use the reflection formula. References: ----------- John L. Spouge, "Computation of the gamma, digamma, and trigamma functions", SIAM Journal on Numerical Analysis 31 (1994), no. 3, 931-944. """ spouge_cache = {} def calc_spouge_coefficients(a, prec): wp = prec + int(a*1.4) c = [0] * a # b = exp(a-1) b = mpf_exp(from_int(a-1), wp) # e = exp(1) e = mpf_exp(fone, wp) # sqrt(2*pi) sq2pi = mpf_sqrt(mpf_shift(mpf_pi(wp), 1), wp) c[0] = to_fixed(sq2pi, prec) for k in xrange(1, a): # c[k] = ((-1)**(k-1) * (a-k)**k) * b / sqrt(a-k) term = mpf_mul_int(b, ((-1)**(k-1) * (a-k)**k), wp) term = mpf_div(term, mpf_sqrt(from_int(a-k), wp), wp) c[k] = to_fixed(term, prec) # b = b / (e * k) b = mpf_div(b, mpf_mul(e, from_int(k), wp), wp) return c # Cached lookup of coefficients def get_spouge_coefficients(prec): # This exact precision has been used before if prec in spouge_cache: return spouge_cache[prec] for p in spouge_cache: if 0.8 <= prec/float(p) < 1: return spouge_cache[p] # Here we estimate the value of a based on Spouge's inequality for # the relative error a = max(3, int(0.38*prec)) # 0.38 = log(2)/log(2*pi), ~= 1.26*n coefs = calc_spouge_coefficients(a, prec) spouge_cache[prec] = (prec, a, coefs) return spouge_cache[prec] def spouge_sum_real(x, prec, a, c): x = to_fixed(x, prec) s = c[0] for k in xrange(1, a): s += (c[k] << prec) // (x + (k << prec)) return from_man_exp(s, -prec, prec, round_floor) # Unused: for fast computation of gamma(p/q) def spouge_sum_rational(p, q, prec, a, c): s = c[0] for k in xrange(1, a): s += c[k] * q // (p+q*k) return from_man_exp(s, -prec, prec, round_floor) # For a complex number a + b*I, we have # # c_k (a+k)*c_k b * c_k # ------------- = --------- - ------- * I # (a + b*I) + k M M # # 2 2 2 2 2 # where M = (a+k) + b = (a + b ) + (2*a*k + k ) def spouge_sum_complex(re, im, prec, a, c): re = to_fixed(re, prec) im = to_fixed(im, prec) sre, sim = c[0], 0 mag = ((re**2)>>prec) + ((im**2)>>prec) for k in xrange(1, a): M = mag + re*(2*k) + ((k**2) << prec) sre += (c[k] * (re + (k << prec))) // M sim -= (c[k] * im) // M re = from_man_exp(sre, -prec, prec, round_floor) im = from_man_exp(sim, -prec, prec, round_floor) return re, im def mpf_gamma_int_old(n, prec, rounding=round_fast): if n < 1000: return from_int(ifac(n-1), prec, rounding) # XXX: choose the cutoff less arbitrarily size = int(n*math.log(n,2)) if prec > size/20.0: return from_int(ifac(n-1), prec, rounding) return mpf_gamma(from_int(n), prec, rounding) def mpf_factorial_old(x, prec, rounding=round_fast): return mpf_gamma_old(x, prec, rounding, p1=0) def mpc_factorial_old(x, prec, rounding=round_fast): return mpc_gamma_old(x, prec, rounding, p1=0) def mpf_gamma_old(x, prec, rounding=round_fast, p1=1): """ Computes the gamma function of a real floating-point argument. With p1=0, computes a factorial instead. """ sign, man, exp, bc = x if not man: if x == finf: return finf if x == fninf or x == fnan: return fnan # More precision is needed for enormous x. TODO: # use Stirling's formula + Euler-Maclaurin summation size = exp + bc if size > 5: size = int(size * math.log(size,2)) wp = prec + max(0, size) + 15 if exp >= 0: if sign or (p1 and not man): raise ValueError("gamma function pole") # A direct factorial is fastest if exp + bc <= 10: return from_int(ifac((man< 5: size = int(size * math.log(size,2)) reflect = sign or (exp+bc < -1) wp = prec + max(0, size) + 25 # Near x = 0 pole (TODO: other poles) if p1: if size < -prec-5: return mpc_add_mpf(mpc_div(mpc_one, x, 2*prec+10), \ mpf_neg(mpf_euler(2*prec+10)), prec, rounding) elif size < -5: wp += (-2*size) if p1: # Should be done exactly! re_orig = re re = mpf_sub(re, fone, bc+abs(exp)+2) x = re, im if reflect: # Reflection formula wp += 15 pi = mpf_pi(wp), fzero pix = mpc_mul(x, pi, wp) t = mpc_sin_pi(x, wp) u = mpc_sub(mpc_one, x, wp) g = mpc_gamma_old(u, wp) w = mpc_mul(t, g, wp) return mpc_div(pix, w, wp) # Extremely close to the real line? # XXX: reflection formula if iexp+ibc < -wp: a = mpf_gamma_old(re_orig, wp) b = mpf_psi0(re_orig, wp) gamma_diff = mpf_div(a, b, wp) return mpf_pos(a, prec, rounding), mpf_mul(gamma_diff, im, prec, rounding) sprec, a, c = get_spouge_coefficients(wp) s = spouge_sum_complex(re, im, sprec, a, c) # gamma = exp(log(x+a)*(x+0.5) - xpa) * s repa = mpf_add(re, from_int(a), wp) logxpa = mpc_log((repa, im), wp) reph = mpf_add(re, fhalf, wp) t = mpc_sub(mpc_mul(logxpa, (reph, im), wp), (repa, im), wp) t = mpc_mul(mpc_exp(t, wp), s, prec, rounding) return t #-----------------------------------------------------------------------# # # # Polygamma functions # # # #-----------------------------------------------------------------------# """ For all polygamma (psi) functions, we use the Euler-Maclaurin summation formula. It looks slightly different in the m = 0 and m > 0 cases. For m = 0, we have oo ___ B (0) 1 \ 2 k -2 k psi (z) ~ log z + --- - ) ------ z 2 z /___ (2 k)! k = 1 Experiment shows that the minimum term of the asymptotic series reaches 2^(-p) when Re(z) > 0.11*p. So we simply use the recurrence for psi (equivalent, in fact, to summing to the first few terms directly before applying E-M) to obtain z large enough. Since, very crudely, log z ~= 1 for Re(z) > 1, we can use fixed-point arithmetic (if z is extremely large, log(z) itself is a sufficient approximation, so we can stop there already). For Re(z) << 0, we could use recurrence, but this is of course inefficient for large negative z, so there we use the reflection formula instead. For m > 0, we have N - 1 ___ ~~~(m) [ \ 1 ] 1 1 psi (z) ~ [ ) -------- ] + ---------- + -------- + [ /___ m+1 ] m+1 m k = 1 (z+k) ] 2 (z+N) m (z+N) oo ___ B \ 2 k (m+1) (m+2) ... (m+2k-1) + ) ------ ------------------------ /___ (2 k)! m + 2 k k = 1 (z+N) where ~~~ denotes the function rescaled by 1/((-1)^(m+1) m!). Here again N is chosen to make z+N large enough for the minimum term in the last series to become smaller than eps. TODO: the current estimation of N for m > 0 is *very suboptimal*. TODO: implement the reflection formula for m > 0, Re(z) << 0. It is generally a combination of multiple cotangents. Need to figure out a reasonably simple way to generate these formulas on the fly. TODO: maybe use exact algorithms to compute psi for integral and certain rational arguments, as this can be much more efficient. (On the other hand, the availability of these special values provides a convenient way to test the general algorithm.) """ # Harmonic numbers are just shifted digamma functions # We should calculate these exactly when x is an integer # and when doing so is faster. def mpf_harmonic(x, prec, rnd): if x in (fzero, fnan, finf): return x a = mpf_psi0(mpf_add(fone, x, prec+5), prec) return mpf_add(a, mpf_euler(prec+5, rnd), prec, rnd) def mpc_harmonic(z, prec, rnd): if z[1] == fzero: return (mpf_harmonic(z[0], prec, rnd), fzero) a = mpc_psi0(mpc_add_mpf(z, fone, prec+5), prec) return mpc_add_mpf(a, mpf_euler(prec+5, rnd), prec, rnd) def mpf_psi0(x, prec, rnd=round_fast): """ Computation of the digamma function (psi function of order 0) of a real argument. """ sign, man, exp, bc = x wp = prec + 10 if not man: if x == finf: return x if x == fninf or x == fnan: return fnan if x == fzero or (exp >= 0 and sign): raise ValueError("polygamma pole") # Reflection formula if sign and exp+bc > 3: c, s = mpf_cos_sin_pi(x, wp) q = mpf_mul(mpf_div(c, s, wp), mpf_pi(wp), wp) p = mpf_psi0(mpf_sub(fone, x, wp), wp) return mpf_sub(p, q, prec, rnd) # The logarithmic term is accurate enough if (not sign) and bc + exp > wp: return mpf_log(mpf_sub(x, fone, wp), prec, rnd) # Initial recurrence to obtain a large enough x m = to_int(x) n = int(0.11*wp) + 2 s = MPZ_ZERO x = to_fixed(x, wp) one = MPZ_ONE << wp if m < n: for k in xrange(m, n): s -= (one << wp) // x x += one x -= one # Logarithmic term s += to_fixed(mpf_log(from_man_exp(x, -wp, wp), wp), wp) # Endpoint term in Euler-Maclaurin expansion s += (one << wp) // (2*x) # Euler-Maclaurin remainder sum x2 = (x*x) >> wp t = one prev = 0 k = 1 while 1: t = (t*x2) >> wp bsign, bman, bexp, bbc = mpf_bernoulli(2*k, wp) offset = (bexp + 2*wp) if offset >= 0: term = (bman << offset) // (t*(2*k)) else: term = (bman >> (-offset)) // (t*(2*k)) if k & 1: s -= term else: s += term if k > 2 and term >= prev: break prev = term k += 1 return from_man_exp(s, -wp, wp, rnd) def mpc_psi0(z, prec, rnd=round_fast): """ Computation of the digamma function (psi function of order 0) of a complex argument. """ re, im = z # Fall back to the real case if im == fzero: return (mpf_psi0(re, prec, rnd), fzero) wp = prec + 20 sign, man, exp, bc = re # Reflection formula if sign and exp+bc > 3: c = mpc_cos_pi(z, wp) s = mpc_sin_pi(z, wp) q = mpc_mul_mpf(mpc_div(c, s, wp), mpf_pi(wp), wp) p = mpc_psi0(mpc_sub(mpc_one, z, wp), wp) return mpc_sub(p, q, prec, rnd) # Just the logarithmic term if (not sign) and bc + exp > wp: return mpc_log(mpc_sub(z, mpc_one, wp), prec, rnd) # Initial recurrence to obtain a large enough z w = to_int(re) n = int(0.11*wp) + 2 s = mpc_zero if w < n: for k in xrange(w, n): s = mpc_sub(s, mpc_reciprocal(z, wp), wp) z = mpc_add_mpf(z, fone, wp) z = mpc_sub(z, mpc_one, wp) # Logarithmic and endpoint term s = mpc_add(s, mpc_log(z, wp), wp) s = mpc_add(s, mpc_div(mpc_half, z, wp), wp) # Euler-Maclaurin remainder sum z2 = mpc_square(z, wp) t = mpc_one prev = mpc_zero k = 1 eps = mpf_shift(fone, -wp+2) while 1: t = mpc_mul(t, z2, wp) bern = mpf_bernoulli(2*k, wp) term = mpc_mpf_div(bern, mpc_mul_int(t, 2*k, wp), wp) s = mpc_sub(s, term, wp) szterm = mpc_abs(term, 10) if k > 2 and mpf_le(szterm, eps): break prev = term k += 1 return s # Currently unoptimized def mpf_psi(m, x, prec, rnd=round_fast): """ Computation of the polygamma function of arbitrary integer order m >= 0, for a real argument x. """ if m == 0: return mpf_psi0(x, prec, rnd=round_fast) return mpc_psi(m, (x, fzero), prec, rnd)[0] def mpc_psi(m, z, prec, rnd=round_fast): """ Computation of the polygamma function of arbitrary integer order m >= 0, for a complex argument z. """ if m == 0: return mpc_psi0(z, prec, rnd) re, im = z wp = prec + 20 sign, man, exp, bc = re if not im[1]: if im in (finf, fninf, fnan): return (fnan, fnan) if not man: if re == finf and im == fzero: return (fzero, fzero) if re == fnan: return (fnan, fnan) # Recurrence w = to_int(re) n = int(0.4*wp + 4*m) s = mpc_zero if w < n: for k in xrange(w, n): t = mpc_pow_int(z, -m-1, wp) s = mpc_add(s, t, wp) z = mpc_add_mpf(z, fone, wp) zm = mpc_pow_int(z, -m, wp) z2 = mpc_pow_int(z, -2, wp) # 1/m*(z+N)^m integral_term = mpc_div_mpf(zm, from_int(m), wp) s = mpc_add(s, integral_term, wp) # 1/2*(z+N)^(-(m+1)) s = mpc_add(s, mpc_mul_mpf(mpc_div(zm, z, wp), fhalf, wp), wp) a = m + 1 b = 2 k = 1 # Important: we want to sum up to the *relative* error, # not the absolute error, because psi^(m)(z) might be tiny magn = mpc_abs(s, 10) magn = magn[2]+magn[3] eps = mpf_shift(fone, magn-wp+2) while 1: zm = mpc_mul(zm, z2, wp) bern = mpf_bernoulli(2*k, wp) scal = mpf_mul_int(bern, a, wp) scal = mpf_div(scal, from_int(b), wp) term = mpc_mul_mpf(zm, scal, wp) s = mpc_add(s, term, wp) szterm = mpc_abs(term, 10) if k > 2 and mpf_le(szterm, eps): break #print k, to_str(szterm, 10), to_str(eps, 10) a *= (m+2*k)*(m+2*k+1) b *= (2*k+1)*(2*k+2) k += 1 # Scale and sign factor v = mpc_mul_mpf(s, mpf_gamma(from_int(m+1), wp), prec, rnd) if not (m & 1): v = mpf_neg(v[0]), mpf_neg(v[1]) return v #-----------------------------------------------------------------------# # # # Riemann zeta function # # # #-----------------------------------------------------------------------# """ We use zeta(s) = eta(s) / (1 - 2**(1-s)) and Borwein's approximation n-1 ___ k -1 \ (-1) (d_k - d_n) eta(s) ~= ---- ) ------------------ d_n /___ s k = 0 (k + 1) where k ___ i \ (n + i - 1)! 4 d_k = n ) ---------------. /___ (n - i)! (2i)! i = 0 If s = a + b*I, the absolute error for eta(s) is bounded by 3 (1 + 2|b|) ------------ * exp(|b| pi/2) n (3+sqrt(8)) Disregarding the linear term, we have approximately, log(err) ~= log(exp(1.58*|b|)) - log(5.8**n) log(err) ~= 1.58*|b| - log(5.8)*n log(err) ~= 1.58*|b| - 1.76*n log2(err) ~= 2.28*|b| - 2.54*n So for p bits, we should choose n > (p + 2.28*|b|) / 2.54. References: ----------- Peter Borwein, "An Efficient Algorithm for the Riemann Zeta Function" http://www.cecm.sfu.ca/personal/pborwein/PAPERS/P117.ps http://en.wikipedia.org/wiki/Dirichlet_eta_function """ borwein_cache = {} def borwein_coefficients(n): if n in borwein_cache: return borwein_cache[n] ds = [MPZ_ZERO] * (n+1) d = MPZ_ONE s = ds[0] = MPZ_ONE for i in range(1, n+1): d = d * 4 * (n+i-1) * (n-i+1) d //= ((2*i) * ((2*i)-1)) s += d ds[i] = s borwein_cache[n] = ds return ds ZETA_INT_CACHE_MAX_PREC = 1000 zeta_int_cache = {} def mpf_zeta_int(s, prec, rnd=round_fast): """ Optimized computation of zeta(s) for an integer s. """ wp = prec + 20 s = int(s) if s in zeta_int_cache and zeta_int_cache[s][0] >= wp: return mpf_pos(zeta_int_cache[s][1], prec, rnd) if s < 2: if s == 1: raise ValueError("zeta(1) pole") if not s: return mpf_neg(fhalf) return mpf_div(mpf_bernoulli(-s+1, wp), from_int(s-1), prec, rnd) # 2^-s term vanishes? if s >= wp: return mpf_perturb(fone, 0, prec, rnd) # 5^-s term vanishes? elif s >= wp*0.431: t = one = 1 << wp t += 1 << (wp - s) t += one // (MPZ_THREE ** s) t += 1 << max(0, wp - s*2) return from_man_exp(t, -wp, prec, rnd) else: # Fast enough to sum directly? # Even better, we use the Euler product (idea stolen from pari) m = (float(wp)/(s-1) + 1) if m < 30: needed_terms = int(2.0**m + 1) if needed_terms < int(wp/2.54 + 5) / 10: t = fone for k in list_primes(needed_terms): #print k, needed_terms powprec = int(wp - s*math.log(k,2)) if powprec < 2: break a = mpf_sub(fone, mpf_pow_int(from_int(k), -s, powprec), wp) t = mpf_mul(t, a, wp) return mpf_div(fone, t, wp) # Use Borwein's algorithm n = int(wp/2.54 + 5) d = borwein_coefficients(n) t = MPZ_ZERO s = MPZ(s) for k in xrange(n): t += (((-1)**k * (d[k] - d[n])) << wp) // (k+1)**s t = (t << wp) // (-d[n]) t = (t << wp) // ((1 << wp) - (1 << (wp+1-s))) if (s in zeta_int_cache and zeta_int_cache[s][0] < wp) or (s not in zeta_int_cache): zeta_int_cache[s] = (wp, from_man_exp(t, -wp-wp)) return from_man_exp(t, -wp-wp, prec, rnd) def mpf_zeta(s, prec, rnd=round_fast, alt=0): sign, man, exp, bc = s if not man: if s == fzero: if alt: return fhalf else: return mpf_neg(fhalf) if s == finf: return fone return fnan wp = prec + 20 # First term vanishes? if (not sign) and (exp + bc > (math.log(wp,2) + 2)): return mpf_perturb(fone, alt, prec, rnd) # Optimize for integer arguments elif exp >= 0: if alt: if s == fone: return mpf_ln2(prec, rnd) z = mpf_zeta_int(to_int(s), wp, negative_rnd[rnd]) q = mpf_sub(fone, mpf_pow(ftwo, mpf_sub(fone, s, wp), wp), wp) return mpf_mul(z, q, prec, rnd) else: return mpf_zeta_int(to_int(s), prec, rnd) # Negative: use the reflection formula # Borwein only proves the accuracy bound for x >= 1/2. However, based on # tests, the accuracy without reflection is quite good even some distance # to the left of 1/2. XXX: verify this. if sign: # XXX: could use the separate refl. formula for Dirichlet eta if alt: q = mpf_sub(fone, mpf_pow(ftwo, mpf_sub(fone, s, wp), wp), wp) return mpf_mul(mpf_zeta(s, wp), q, prec, rnd) # XXX: -1 should be done exactly y = mpf_sub(fone, s, 10*wp) a = mpf_gamma(y, wp) b = mpf_zeta(y, wp) c = mpf_sin_pi(mpf_shift(s, -1), wp) wp2 = wp + (exp+bc) pi = mpf_pi(wp+wp2) d = mpf_div(mpf_pow(mpf_shift(pi, 1), s, wp2), pi, wp2) return mpf_mul(a,mpf_mul(b,mpf_mul(c,d,wp),wp),prec,rnd) # Near pole r = mpf_sub(fone, s, wp) asign, aman, aexp, abc = mpf_abs(r) pole_dist = -2*(aexp+abc) if pole_dist > wp: if alt: return mpf_ln2(prec, rnd) else: q = mpf_neg(mpf_div(fone, r, wp)) return mpf_add(q, mpf_euler(wp), prec, rnd) else: wp += max(0, pole_dist) t = MPZ_ZERO #wp += 16 - (prec & 15) # Use Borwein's algorithm n = int(wp/2.54 + 5) d = borwein_coefficients(n) t = MPZ_ZERO sf = to_fixed(s, wp) ln2 = ln2_fixed(wp) for k in xrange(n): u = (-sf*log_int_fixed(k+1, wp, ln2)) >> wp #esign, eman, eexp, ebc = mpf_exp(u, wp) #offset = eexp + wp #if offset >= 0: # w = ((d[k] - d[n]) * eman) << offset #else: # w = ((d[k] - d[n]) * eman) >> (-offset) eman = exp_fixed(u, wp, ln2) w = (d[k] - d[n]) * eman if k & 1: t -= w else: t += w t = t // (-d[n]) t = from_man_exp(t, -wp, wp) if alt: return mpf_pos(t, prec, rnd) else: q = mpf_sub(fone, mpf_pow(ftwo, mpf_sub(fone, s, wp), wp), wp) return mpf_div(t, q, prec, rnd) def mpc_zeta(s, prec, rnd=round_fast, alt=0, force=False): re, im = s if im == fzero: return mpf_zeta(re, prec, rnd, alt), fzero # slow for large s if (not force) and mpf_gt(mpc_abs(s, 10), from_int(prec)): raise NotImplementedError wp = prec + 20 # Near pole r = mpc_sub(mpc_one, s, wp) asign, aman, aexp, abc = mpc_abs(r, 10) pole_dist = -2*(aexp+abc) if pole_dist > wp: if alt: q = mpf_ln2(wp) y = mpf_mul(q, mpf_euler(wp), wp) g = mpf_shift(mpf_mul(q, q, wp), -1) g = mpf_sub(y, g) z = mpc_mul_mpf(r, mpf_neg(g), wp) z = mpc_add_mpf(z, q, wp) return mpc_pos(z, prec, rnd) else: q = mpc_neg(mpc_div(mpc_one, r, wp)) q = mpc_add_mpf(q, mpf_euler(wp), wp) return mpc_pos(q, prec, rnd) else: wp += max(0, pole_dist) # Reflection formula. To be rigorous, we should reflect to the left of # re = 1/2 (see comments for mpf_zeta), but this leads to unnecessary # slowdown for interesting values of s if mpf_lt(re, fzero): # XXX: could use the separate refl. formula for Dirichlet eta if alt: q = mpc_sub(mpc_one, mpc_pow(mpc_two, mpc_sub(mpc_one, s, wp), wp), wp) return mpc_mul(mpc_zeta(s, wp), q, prec, rnd) # XXX: -1 should be done exactly y = mpc_sub(mpc_one, s, 10*wp) a = mpc_gamma(y, wp) b = mpc_zeta(y, wp) c = mpc_sin_pi(mpc_shift(s, -1), wp) rsign, rman, rexp, rbc = re isign, iman, iexp, ibc = im mag = max(rexp+rbc, iexp+ibc) wp2 = wp + mag pi = mpf_pi(wp+wp2) pi2 = (mpf_shift(pi, 1), fzero) d = mpc_div_mpf(mpc_pow(pi2, s, wp2), pi, wp2) return mpc_mul(a,mpc_mul(b,mpc_mul(c,d,wp),wp),prec,rnd) n = int(wp/2.54 + 5) n += int(0.9*abs(to_int(im))) d = borwein_coefficients(n) ref = to_fixed(re, wp) imf = to_fixed(im, wp) tre = MPZ_ZERO tim = MPZ_ZERO one = MPZ_ONE << wp one_2wp = MPZ_ONE << (2*wp) critical_line = re == fhalf ln2 = ln2_fixed(wp) pi2 = pi_fixed(wp-1) wp2 = wp+wp for k in xrange(n): log = log_int_fixed(k+1, wp, ln2) # A square root is much cheaper than an exp if critical_line: w = one_2wp // isqrt_fast((k+1) << wp2) else: w = exp_fixed((-ref*log) >> wp, wp) if k & 1: w *= (d[n] - d[k]) else: w *= (d[k] - d[n]) wre, wim = cos_sin_fixed((-imf*log)>>wp, wp, pi2) tre += (w * wre) >> wp tim += (w * wim) >> wp tre //= (-d[n]) tim //= (-d[n]) tre = from_man_exp(tre, -wp, wp) tim = from_man_exp(tim, -wp, wp) if alt: return mpc_pos((tre, tim), prec, rnd) else: q = mpc_sub(mpc_one, mpc_pow(mpc_two, r, wp), wp) return mpc_div((tre, tim), q, prec, rnd) def mpf_altzeta(s, prec, rnd=round_fast): return mpf_zeta(s, prec, rnd, 1) def mpc_altzeta(s, prec, rnd=round_fast): return mpc_zeta(s, prec, rnd, 1) # Not optimized currently mpf_zetasum = None def pow_fixed(x, n, wp): if n == 1: return x y = MPZ_ONE << wp while n: if n & 1: y = (y*x) >> wp n -= 1 x = (x*x) >> wp n //= 2 return y # TODO: optimize / cleanup interface / unify with list_primes sieve_cache = [] primes_cache = [] mult_cache = [] def primesieve(n): global sieve_cache, primes_cache, mult_cache if n < len(sieve_cache): sieve = sieve_cache#[:n+1] primes = primes_cache[:primes_cache.index(max(sieve))+1] mult = mult_cache#[:n+1] return sieve, primes, mult sieve = [0] * (n+1) mult = [0] * (n+1) primes = list_primes(n) for p in primes: #sieve[p::p] = p for k in xrange(p,n+1,p): sieve[k] = p for i, p in enumerate(sieve): if i >= 2: m = 1 n = i // p while not n % p: n //= p m += 1 mult[i] = m sieve_cache = sieve primes_cache = primes mult_cache = mult return sieve, primes, mult def zetasum_sieved(critical_line, sre, sim, a, n, wp): assert a >= 1 sieve, primes, mult = primesieve(a+n) basic_powers = {} one = MPZ_ONE << wp one_2wp = MPZ_ONE << (2*wp) wp2 = wp+wp ln2 = ln2_fixed(wp) pi2 = pi_fixed(wp-1) for p in primes: if p*2 > a+n: break log = log_int_fixed(p, wp, ln2) cos, sin = cos_sin_fixed((-sim*log)>>wp, wp, pi2) if critical_line: u = one_2wp // isqrt_fast(p<>wp, wp) pre = (u*cos) >> wp pim = (u*sin) >> wp basic_powers[p] = [(pre, pim)] tre, tim = pre, pim for m in range(1,int(math.log(a+n,p)+0.01)+1): tre, tim = ((pre*tre-pim*tim)>>wp), ((pim*tre+pre*tim)>>wp) basic_powers[p].append((tre,tim)) xre = MPZ_ZERO xim = MPZ_ZERO if a == 1: xre += one aa = max(a,2) for k in xrange(aa, a+n+1): p = sieve[k] if p in basic_powers: m = mult[k] tre, tim = basic_powers[p][m-1] while 1: k //= p**m if k == 1: break p = sieve[k] m = mult[k] pre, pim = basic_powers[p][m-1] tre, tim = ((pre*tre-pim*tim)>>wp), ((pim*tre+pre*tim)>>wp) else: log = log_int_fixed(k, wp, ln2) cos, sin = cos_sin_fixed((-sim*log)>>wp, wp, pi2) if critical_line: u = one_2wp // isqrt_fast(k<>wp, wp) tre = (u*cos) >> wp tim = (u*sin) >> wp xre += tre xim += tim return xre, xim # Set to something large to disable ZETASUM_SIEVE_CUTOFF = 10 def mpc_zetasum(s, a, n, derivatives, reflect, prec): """ Fast version of mp._zetasum, assuming s = complex, a = integer. """ wp = prec + 10 have_derivatives = derivatives != [0] have_one_derivative = len(derivatives) == 1 # parse s sre, sim = s critical_line = (sre == fhalf) sre = to_fixed(sre, wp) sim = to_fixed(sim, wp) if a > 0 and n > ZETASUM_SIEVE_CUTOFF and not have_derivatives and not reflect: re, im = zetasum_sieved(critical_line, sre, sim, a, n, wp) xs = [(from_man_exp(re, -wp, prec, 'n'), from_man_exp(im, -wp, prec, 'n'))] return xs, [] maxd = max(derivatives) if not have_one_derivative: derivatives = range(maxd+1) # x_d = 0, y_d = 0 xre = [MPZ_ZERO for d in derivatives] xim = [MPZ_ZERO for d in derivatives] if reflect: yre = [MPZ_ZERO for d in derivatives] yim = [MPZ_ZERO for d in derivatives] else: yre = yim = [] one = MPZ_ONE << wp one_2wp = MPZ_ONE << (2*wp) ln2 = ln2_fixed(wp) pi2 = pi_fixed(wp-1) wp2 = wp+wp for w in xrange(a, a+n+1): log = log_int_fixed(w, wp, ln2) cos, sin = cos_sin_fixed((-sim*log)>>wp, wp, pi2) if critical_line: u = one_2wp // isqrt_fast(w<>wp, wp) xterm_re = (u * cos) >> wp xterm_im = (u * sin) >> wp if reflect: reciprocal = (one_2wp // (u*w)) yterm_re = (reciprocal * cos) >> wp yterm_im = (reciprocal * sin) >> wp if have_derivatives: if have_one_derivative: log = pow_fixed(log, maxd, wp) xre[0] += (xterm_re * log) >> wp xim[0] += (xterm_im * log) >> wp if reflect: yre[0] += (yterm_re * log) >> wp yim[0] += (yterm_im * log) >> wp else: t = MPZ_ONE << wp for d in derivatives: xre[d] += (xterm_re * t) >> wp xim[d] += (xterm_im * t) >> wp if reflect: yre[d] += (yterm_re * t) >> wp yim[d] += (yterm_im * t) >> wp t = (t * log) >> wp else: xre[0] += xterm_re xim[0] += xterm_im if reflect: yre[0] += yterm_re yim[0] += yterm_im if have_derivatives: if have_one_derivative: if maxd % 2: xre[0] = -xre[0] xim[0] = -xim[0] if reflect: yre[0] = -yre[0] yim[0] = -yim[0] else: xre = [(-1)**d * xre[d] for d in derivatives] xim = [(-1)**d * xim[d] for d in derivatives] if reflect: yre = [(-1)**d * yre[d] for d in derivatives] yim = [(-1)**d * yim[d] for d in derivatives] xs = [(from_man_exp(xa, -wp, prec, 'n'), from_man_exp(xb, -wp, prec, 'n')) for (xa, xb) in zip(xre, xim)] ys = [(from_man_exp(ya, -wp, prec, 'n'), from_man_exp(yb, -wp, prec, 'n')) for (ya, yb) in zip(yre, yim)] return xs, ys #-----------------------------------------------------------------------# # # # The gamma function (NEW IMPLEMENTATION) # # # #-----------------------------------------------------------------------# # Higher means faster, but more precomputation time MAX_GAMMA_TAYLOR_PREC = 5000 # Need to derive higher bounds for Taylor series to go higher assert MAX_GAMMA_TAYLOR_PREC < 15000 # Use Stirling's series if abs(x) > beta*prec # Important: must be large enough for convergence! GAMMA_STIRLING_BETA = 0.2 SMALL_FACTORIAL_CACHE_SIZE = 150 gamma_taylor_cache = {} gamma_stirling_cache = {} small_factorial_cache = [from_int(ifac(n)) for \ n in range(SMALL_FACTORIAL_CACHE_SIZE+1)] def zeta_array(N, prec): """ zeta(n) = A * pi**n / n! + B where A is a rational number (A = Bernoulli number for n even) and B is an infinite sum over powers of exp(2*pi). (B = 0 for n even). TODO: this is currently only used for gamma, but could be very useful elsewhere. """ extra = 30 wp = prec+extra zeta_values = [MPZ_ZERO] * (N+2) pi = pi_fixed(wp) # STEP 1: one = MPZ_ONE << wp zeta_values[0] = -one//2 f_2pi = mpf_shift(mpf_pi(wp),1) exp_2pi_k = exp_2pi = mpf_exp(f_2pi, wp) # Compute exponential series # Store values of 1/(exp(2*pi*k)-1), # exp(2*pi*k)/(exp(2*pi*k)-1)**2, 1/(exp(2*pi*k)-1)**2 # pi*k*exp(2*pi*k)/(exp(2*pi*k)-1)**2 exps3 = [] k = 1 while 1: tp = wp - 9*k if tp < 1: break # 1/(exp(2*pi*k-1) q1 = mpf_div(fone, mpf_sub(exp_2pi_k, fone, tp), tp) # pi*k*exp(2*pi*k)/(exp(2*pi*k)-1)**2 q2 = mpf_mul(exp_2pi_k, mpf_mul(q1,q1,tp), tp) q1 = to_fixed(q1, wp) q2 = to_fixed(q2, wp) q2 = (k * q2 * pi) >> wp exps3.append((q1, q2)) # Multiply for next round exp_2pi_k = mpf_mul(exp_2pi_k, exp_2pi, wp) k += 1 # Exponential sum for n in xrange(3, N+1, 2): s = MPZ_ZERO k = 1 for e1, e2 in exps3: if n%4 == 3: t = e1 // k**n else: U = (n-1)//4 t = (e1 + e2//U) // k**n if not t: break s += t k += 1 zeta_values[n] = -2*s # Even zeta values B = [mpf_abs(mpf_bernoulli(k,wp)) for k in xrange(N+2)] pi_pow = fpi = mpf_pow_int(mpf_shift(mpf_pi(wp), 1), 2, wp) pi_pow = mpf_div(pi_pow, from_int(4), wp) for n in xrange(2,N+2,2): z = mpf_mul(B[n], pi_pow, wp) zeta_values[n] = to_fixed(z, wp) pi_pow = mpf_mul(pi_pow, fpi, wp) pi_pow = mpf_div(pi_pow, from_int((n+1)*(n+2)), wp) # Zeta sum reciprocal_pi = (one << wp) // pi for n in xrange(3, N+1, 4): U = (n-3)//4 s = zeta_values[4*U+4]*(4*U+7)//4 for k in xrange(1, U+1): s -= (zeta_values[4*k] * zeta_values[4*U+4-4*k]) >> wp zeta_values[n] += (2*s*reciprocal_pi) >> wp for n in xrange(5, N+1, 4): U = (n-1)//4 s = zeta_values[4*U+2]*(2*U+1) for k in xrange(1, 2*U+1): s += ((-1)**k*2*k* zeta_values[2*k] * zeta_values[4*U+2-2*k])>>wp zeta_values[n] += ((s*reciprocal_pi)>>wp)//(2*U) return [x>>extra for x in zeta_values] def gamma_taylor_coefficients(inprec): """ Gives the Taylor coefficients of 1/gamma(1+x) as a list of fixed-point numbers. Enough coefficients are returned to ensure that the series converges to the given precision when x is in [0.5, 1.5]. """ # Reuse nearby cache values (small case) if inprec < 400: prec = inprec + (10-(inprec%10)) elif inprec < 1000: prec = inprec + (30-(inprec%30)) else: prec = inprec if prec in gamma_taylor_cache: return gamma_taylor_cache[prec], prec # Experimentally determined bounds if prec < 1000: N = int(prec**0.76 + 2) else: # Valid to at least 15000 bits N = int(prec**0.787 + 2) # Reuse higher precision values for cprec in gamma_taylor_cache: if cprec > prec: coeffs = [x>>(cprec-prec) for x in gamma_taylor_cache[cprec][-N:]] if inprec < 1000: gamma_taylor_cache[prec] = coeffs return coeffs, prec # Cache at a higher precision (large case) if prec > 1000: prec = int(prec * 1.2) wp = prec + 20 A = [0] * N A[0] = MPZ_ZERO A[1] = MPZ_ONE << wp A[2] = euler_fixed(wp) # SLOW, reference implementation #zeta_values = [0,0]+[to_fixed(mpf_zeta_int(k,wp),wp) for k in xrange(2,N)] zeta_values = zeta_array(N, wp) for k in xrange(3, N): a = (-A[2]*A[k-1])>>wp for j in xrange(2,k): a += ((-1)**j * zeta_values[j] * A[k-j]) >> wp a //= (1-k) A[k] = a A = [a>>20 for a in A] A = A[::-1] A = A[:-1] gamma_taylor_cache[prec] = A #return A, prec return gamma_taylor_coefficients(inprec) def gamma_fixed_taylor(xmpf, x, wp, prec, rnd, type): # Determine nearest multiple of N/2 #n = int(x >> (wp-1)) #steps = (n-1)>>1 nearest_int = ((x >> (wp-1)) + MPZ_ONE) >> 1 one = MPZ_ONE << wp coeffs, cwp = gamma_taylor_coefficients(wp) if nearest_int > 0: r = one for i in xrange(nearest_int-1): x -= one r = (r*x) >> wp x -= one p = MPZ_ZERO for c in coeffs: p = c + ((x*p)>>wp) p >>= (cwp-wp) if type == 0: return from_man_exp((r<> wp x += one p = MPZ_ZERO for c in coeffs: p = c + ((x*p)>>wp) p >>= (cwp-wp) if wp - bitcount(abs(x)) > 10: # pass very close to 0, so do floating-point multiply g = mpf_add(xmpf, from_int(-nearest_int)) # exact r = from_man_exp(p*r,-wp-wp) r = mpf_mul(r, g, wp) if type == 0: return mpf_div(fone, r, prec, rnd) if type == 2: return mpf_pos(r, prec, rnd) if type == 3: return mpf_log(mpf_abs(mpf_div(fone, r, wp)), prec, rnd) else: r = from_man_exp(x*p*r,-3*wp) if type == 0: return mpf_div(fone, r, prec, rnd) if type == 2: return mpf_pos(r, prec, rnd) if type == 3: return mpf_neg(mpf_log(mpf_abs(r), prec, rnd)) def stirling_coefficient(n): if n in gamma_stirling_cache: return gamma_stirling_cache[n] p, q = bernfrac(n) q *= MPZ(n*(n-1)) gamma_stirling_cache[n] = p, q, bitcount(abs(p)), bitcount(q) return gamma_stirling_cache[n] def real_stirling_series(x, prec): """ Sums the rational part of Stirling's expansion, log(sqrt(2*pi)) - z + 1/(12*z) - 1/(360*z^3) + ... """ t = (MPZ_ONE<<(prec+prec)) // x # t = 1/x u = (t*t)>>prec # u = 1/x**2 s = ln_sqrt2pi_fixed(prec) - x # Add initial terms of Stirling's series s += t//12 t = (t*u)>>prec s -= t//360 t = (t*u)>>prec s += t//1260 t = (t*u)>>prec s -= t//1680 t = (t*u)>>prec if not t: return s s += t//1188 t = (t*u)>>prec s -= 691*t//360360 t = (t*u)>>prec s += t//156 t = (t*u)>>prec if not t: return s s -= 3617*t//122400 t = (t*u)>>prec s += 43867*t//244188 t = (t*u)>>prec s -= 174611*t//125400 t = (t*u)>>prec if not t: return s k = 22 # From here on, the coefficients are growing, so we # have to keep t at a roughly constant size usize = bitcount(abs(u)) tsize = bitcount(abs(t)) texp = 0 while 1: p, q, pb, qb = stirling_coefficient(k) term_mag = tsize + pb + texp shift = -texp m = pb - term_mag if m > 0 and shift < m: p >>= m shift -= m m = tsize - term_mag if m > 0 and shift < m: w = t >> m shift -= m else: w = t term = (t*p//q) >> shift if not term: break s += term t = (t*u) >> usize texp -= (prec - usize) k += 2 return s def complex_stirling_series(x, y, prec): # t = 1/z _m = (x*x + y*y) >> prec tre = (x << prec) // _m tim = (-y << prec) // _m # u = 1/z**2 ure = (tre*tre - tim*tim) >> prec uim = tim*tre >> (prec-1) # s = log(sqrt(2*pi)) - z sre = ln_sqrt2pi_fixed(prec) - x sim = -y # Add initial terms of Stirling's series sre += tre//12 sim += tim//12 tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) sre -= tre//360 sim -= tim//360 tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) sre += tre//1260 sim += tim//1260 tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) sre -= tre//1680 sim -= tim//1680 tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) if abs(tre) + abs(tim) < 5: return sre, sim sre += tre//1188 sim += tim//1188 tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) sre -= 691*tre//360360 sim -= 691*tim//360360 tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) sre += tre//156 sim += tim//156 tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) if abs(tre) + abs(tim) < 5: return sre, sim sre -= 3617*tre//122400 sim -= 3617*tim//122400 tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) sre += 43867*tre//244188 sim += 43867*tim//244188 tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) sre -= 174611*tre//125400 sim -= 174611*tim//125400 tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) if abs(tre) + abs(tim) < 5: return sre, sim k = 22 # From here on, the coefficients are growing, so we # have to keep t at a roughly constant size usize = bitcount(max(abs(ure), abs(uim))) tsize = bitcount(max(abs(tre), abs(tim))) texp = 0 while 1: p, q, pb, qb = stirling_coefficient(k) term_mag = tsize + pb + texp shift = -texp m = pb - term_mag if m > 0 and shift < m: p >>= m shift -= m m = tsize - term_mag if m > 0 and shift < m: wre = tre >> m wim = tim >> m shift -= m else: wre = tre wim = tim termre = (tre*p//q) >> shift termim = (tim*p//q) >> shift if abs(termre) + abs(termim) < 5: break sre += termre sim += termim tre, tim = ((tre*ure - tim*uim)>>usize), \ ((tre*uim + tim*ure)>>usize) texp -= (prec - usize) k += 2 return sre, sim def mpf_gamma(x, prec, rnd='d', type=0): """ This function implements multipurpose evaluation of the gamma function, G(x), as well as the following versions of the same: type = 0 -- G(x) [standard gamma function] type = 1 -- G(x+1) = x*G(x+1) = x! [factorial] type = 2 -- 1/G(x) [reciprocal gamma function] type = 3 -- log(|G(x)|) [log-gamma function, real part] """ # Specal values sign, man, exp, bc = x if not man: if x == fzero: if type == 1: return fone if type == 2: return fzero raise ValueError("gamma function pole") if x == finf: if type == 2: return fzero return finf return fnan # First of all, for log gamma, numbers can be well beyond the fixed-point # range, so we must take care of huge numbers before e.g. trying # to convert x to the nearest integer if type == 3: wp = prec+20 if exp+bc > wp and not sign: return mpf_sub(mpf_mul(x, mpf_log(x, wp), wp), x, prec, rnd) # We strongly want to special-case small integers is_integer = exp >= 0 if is_integer: # Poles if sign: if type == 2: return fzero raise ValueError("gamma function pole") # n = x n = man << exp if n < SMALL_FACTORIAL_CACHE_SIZE: if type == 0: return mpf_pos(small_factorial_cache[n-1], prec, rnd) if type == 1: return mpf_pos(small_factorial_cache[n], prec, rnd) if type == 2: return mpf_div(fone, small_factorial_cache[n-1], prec, rnd) if type == 3: return mpf_log(small_factorial_cache[n-1], prec, rnd) else: # floor(abs(x)) n = int(man >> (-exp)) # Estimate size and precision # Estimate log(gamma(|x|),2) as x*log(x,2) mag = exp + bc gamma_size = n*mag if type == 3: wp = prec + 20 else: wp = prec + bitcount(gamma_size) + 20 # Very close to 0, pole if mag < -wp: if type == 0: return mpf_sub(mpf_div(fone,x, wp),mpf_shift(fone,-wp),prec,rnd) if type == 1: return mpf_sub(fone, x, prec, rnd) if type == 2: return mpf_add(x, mpf_shift(fone,mag-wp), prec, rnd) if type == 3: return mpf_neg(mpf_log(mpf_abs(x), prec, rnd)) # From now on, we assume having a gamma function if type == 1: return mpf_gamma(mpf_add(x, fone), prec, rnd, 0) # Special case integers (those not small enough to be caught above, # but still small enough for an exact factorial to be faster # than an approximate algorithm), and half-integers if exp >= -1: if is_integer: if gamma_size < 10*wp: if type == 0: return from_int(ifac(n-1), prec, rnd) if type == 2: return from_rational(MPZ_ONE, ifac(n-1), prec, rnd) if type == 3: return mpf_log(from_int(ifac(n-1)), prec, rnd) # half-integer if n < 100 or gamma_size < 10*wp: if sign: w = sqrtpi_fixed(wp) if n % 2: f = ifac2(2*n+1) else: f = -ifac2(2*n+1) if type == 0: return mpf_shift(from_rational(w, f, prec, rnd), -wp+n+1) if type == 2: return mpf_shift(from_rational(f, w, prec, rnd), wp-n-1) if type == 3: return mpf_log(mpf_shift(from_rational(w, abs(f), prec, rnd), -wp+n+1), prec, rnd) elif n == 0: if type == 0: return mpf_sqrtpi(prec, rnd) if type == 2: return mpf_div(fone, mpf_sqrtpi(wp), prec, rnd) if type == 3: return mpf_log(mpf_sqrtpi(wp), prec, rnd) else: w = sqrtpi_fixed(wp) w = from_man_exp(w * ifac2(2*n-1), -wp-n) if type == 0: return mpf_pos(w, prec, rnd) if type == 2: return mpf_div(fone, w, prec, rnd) if type == 3: return mpf_log(mpf_abs(w), prec, rnd) # Convert to fixed point offset = exp + wp if offset >= 0: absxman = man << offset else: absxman = man >> (-offset) # For log gamma, provide accurate evaluation for x = 1+eps and 2+eps if type == 3 and not sign: one = MPZ_ONE << wp one_dist = abs(absxman-one) two_dist = abs(absxman-2*one) cancellation = (wp - bitcount(min(one_dist, two_dist))) if cancellation > 10: xsub1 = mpf_sub(fone, x) xsub2 = mpf_sub(ftwo, x) xsub1mag = xsub1[2]+xsub1[3] xsub2mag = xsub2[2]+xsub2[3] if xsub1mag < -wp: return mpf_mul(mpf_euler(wp), mpf_sub(fone, x), prec, rnd) if xsub2mag < -wp: return mpf_mul(mpf_sub(fone, mpf_euler(wp)), mpf_sub(x, ftwo), prec, rnd) # Proceed but increase precision wp += max(-xsub1mag, -xsub2mag) offset = exp + wp if offset >= 0: absxman = man << offset else: absxman = man >> (-offset) # Use Taylor series if appropriate n_for_stirling = int(GAMMA_STIRLING_BETA*wp) if n < max(100, n_for_stirling) and wp < MAX_GAMMA_TAYLOR_PREC: if sign: absxman = -absxman return gamma_fixed_taylor(x, absxman, wp, prec, rnd, type) # Use Stirling's series # First ensure that |x| is large enough for rapid convergence xorig = x # Argument reduction r = 0 if n < n_for_stirling: r = one = MPZ_ONE << wp d = n_for_stirling - n for k in xrange(d): r = (r * absxman) >> wp absxman += one x = xabs = from_man_exp(absxman, -wp) if sign: x = mpf_neg(x) else: xabs = mpf_abs(x) # Asymptotic series y = real_stirling_series(absxman, wp) u = to_fixed(mpf_log(xabs, wp), wp) u = ((absxman - (MPZ_ONE<<(wp-1))) * u) >> wp y += u w = from_man_exp(y, -wp) # Compute final value if sign: # Reflection formula A = mpf_mul(mpf_sin_pi(xorig, wp), xorig, wp) B = mpf_neg(mpf_pi(wp)) if type == 0 or type == 2: A = mpf_mul(A, mpf_exp(w, wp)) if r: B = mpf_mul(B, from_man_exp(r, -wp), wp) if type == 0: return mpf_div(B, A, prec, rnd) if type == 2: return mpf_div(A, B, prec, rnd) if type == 3: if r: B = mpf_mul(B, from_man_exp(r, -wp), wp) A = mpf_add(mpf_log(mpf_abs(A), wp), w, wp) return mpf_sub(mpf_log(mpf_abs(B), wp), A, prec, rnd) else: if type == 0: if r: return mpf_div(mpf_exp(w, wp), from_man_exp(r, -wp), prec, rnd) return mpf_exp(w, prec, rnd) if type == 2: if r: return mpf_div(from_man_exp(r, -wp), mpf_exp(w, wp), prec, rnd) return mpf_exp(mpf_neg(w), prec, rnd) if type == 3: if r: return mpf_sub(w, mpf_log(from_man_exp(r,-wp), wp), prec, rnd) return mpf_pos(w, prec, rnd) def mpc_gamma(z, prec, rnd='d', type=0): a, b = z asign, aman, aexp, abc = a bsign, bman, bexp, bbc = b if b == fzero: # Imaginary part on negative half-axis for log-gamma function if type == 3 and asign: re = mpf_gamma(a, prec, rnd, 3) n = (-aman) >> (-aexp) im = mpf_mul_int(mpf_pi(prec+10), n, prec, rnd) return re, im return mpf_gamma(a, prec, rnd, type), fzero # Some kind of complex inf/nan if (not aman and aexp) or (not bman and bexp): return (fnan, fnan) # Initial working precision wp = prec + 20 amag = aexp+abc bmag = bexp+bbc if aman: mag = max(amag, bmag) else: mag = bmag # Close to 0 if mag < -8: if mag < -wp: # 1/gamma(z) = z + euler*z^2 + O(z^3) v = mpc_add(z, mpc_mul_mpf(mpc_mul(z,z,wp),mpf_euler(wp),wp), wp) if type == 0: return mpc_reciprocal(v, prec, rnd) if type == 1: return mpc_div(z, v, prec, rnd) if type == 2: return mpc_pos(v, prec, rnd) if type == 3: return mpc_log(mpc_reciprocal(v, prec), prec, rnd) elif type != 1: wp += (-mag) # Handle huge log-gamma values; must do this before converting to # a fixed-point value. TODO: determine a precise cutoff of validity # depending on amag and bmag if type == 3 and mag > wp and ((not asign) or (bmag >= amag)): return mpc_sub(mpc_mul(z, mpc_log(z, wp), wp), z, prec, rnd) # From now on, we assume having a gamma function if type == 1: return mpc_gamma((mpf_add(a, fone), b), prec, rnd, 0) an = abs(to_int(a)) bn = abs(to_int(b)) absn = max(an, bn) gamma_size = absn*mag if type == 3: pass else: wp += bitcount(gamma_size) # Reflect to the right half-plane. Note that Stirling's expansion # is valid in the left half-plane too, as long as we're not too close # to the real axis, but in order to use this argument reduction # in the negative direction must be implemented. #need_reflection = asign and ((bmag < 0) or (amag-bmag > 4)) need_reflection = asign zorig = z if need_reflection: z = mpc_neg(z) asign, aman, aexp, abc = a = z[0] bsign, bman, bexp, bbc = b = z[1] # Imaginary part very small compared to real one? yfinal = 0 balance_prec = 0 if bmag < -10: # Check z ~= 1 and z ~= 2 for loggamma if type == 3: zsub1 = mpc_sub_mpf(z, fone) if zsub1[0] == fzero: cancel1 = -bmag else: cancel1 = -max(zsub1[0][2]+zsub1[0][3], bmag) if cancel1 > wp: pi = mpf_pi(wp) x = mpc_mul_mpf(zsub1, pi, wp) x = mpc_mul(x, x, wp) x = mpc_div_mpf(x, from_int(12), wp) y = mpc_mul_mpf(zsub1, mpf_neg(mpf_euler(wp)), wp) yfinal = mpc_add(x, y, wp) if not need_reflection: return mpc_pos(yfinal, prec, rnd) elif cancel1 > 0: wp += cancel1 zsub2 = mpc_sub_mpf(z, ftwo) if zsub2[0] == fzero: cancel2 = -bmag else: cancel2 = -max(zsub2[0][2]+zsub2[0][3], bmag) if cancel2 > wp: pi = mpf_pi(wp) t = mpf_sub(mpf_mul(pi, pi), from_int(6)) x = mpc_mul_mpf(mpc_mul(zsub2, zsub2, wp), t, wp) x = mpc_div_mpf(x, from_int(12), wp) y = mpc_mul_mpf(zsub2, mpf_sub(fone, mpf_euler(wp)), wp) yfinal = mpc_add(x, y, wp) if not need_reflection: return mpc_pos(yfinal, prec, rnd) elif cancel2 > 0: wp += cancel2 if bmag < -wp: # Compute directly from the real gamma function. pp = 2*(wp+10) aabs = mpf_abs(a) eps = mpf_shift(fone, amag-wp) x1 = mpf_gamma(aabs, pp, type=type) x2 = mpf_gamma(mpf_add(aabs, eps), pp, type=type) xprime = mpf_div(mpf_sub(x2, x1, pp), eps, pp) y = mpf_mul(b, xprime, prec, rnd) yfinal = (x1, y) # Note: we still need to use the reflection formula for # near-poles, and the correct branch of the log-gamma function if not need_reflection: return mpc_pos(yfinal, prec, rnd) else: balance_prec += (-bmag) wp += balance_prec n_for_stirling = int(GAMMA_STIRLING_BETA*wp) need_reduction = absn < n_for_stirling afix = to_fixed(a, wp) bfix = to_fixed(b, wp) r = 0 if not yfinal: zprered = z # Argument reduction if absn < n_for_stirling: absn = complex(an, bn) d = int((1 + n_for_stirling**2 - bn**2)**0.5 - an) rre = one = MPZ_ONE << wp rim = MPZ_ZERO for k in xrange(d): rre, rim = ((afix*rre-bfix*rim)>>wp), ((afix*rim + bfix*rre)>>wp) afix += one r = from_man_exp(rre, -wp), from_man_exp(rim, -wp) a = from_man_exp(afix, -wp) z = a, b yre, yim = complex_stirling_series(afix, bfix, wp) # (z-1/2)*log(z) + S lre, lim = mpc_log(z, wp) lre = to_fixed(lre, wp) lim = to_fixed(lim, wp) yre = ((lre*afix - lim*bfix)>>wp) - (lre>>1) + yre yim = ((lre*bfix + lim*afix)>>wp) - (lim>>1) + yim y = from_man_exp(yre, -wp), from_man_exp(yim, -wp) if r and type == 3: # If re(z) > 0 and abs(z) <= 4, the branches of loggamma(z) # and log(gamma(z)) coincide. Otherwise, use the zeroth order # Stirling expansion to compute the correct imaginary part. y = mpc_sub(y, mpc_log(r, wp), wp) zfa = to_float(zprered[0]) zfb = to_float(zprered[1]) zfabs = math.hypot(zfa,zfb) #if not (zfa > 0.0 and zfabs <= 4): yfb = to_float(y[1]) u = math.atan2(zfb, zfa) if zfabs <= 0.5: gi = 0.577216*zfb - u else: gi = -zfb - 0.5*u + zfa*u + zfb*math.log(zfabs) n = int(math.floor((gi-yfb)/(2*math.pi)+0.5)) y = (y[0], mpf_add(y[1], mpf_mul_int(mpf_pi(wp), 2*n, wp), wp)) if need_reflection: if type == 0 or type == 2: A = mpc_mul(mpc_sin_pi(zorig, wp), zorig, wp) B = (mpf_neg(mpf_pi(wp)), fzero) if yfinal: if type == 2: A = mpc_div(A, yfinal, wp) else: A = mpc_mul(A, yfinal, wp) else: A = mpc_mul(A, mpc_exp(y, wp), wp) if r: B = mpc_mul(B, r, wp) if type == 0: return mpc_div(B, A, prec, rnd) if type == 2: return mpc_div(A, B, prec, rnd) # Reflection formula for the log-gamma function with correct branch # http://functions.wolfram.com/GammaBetaErf/LogGamma/16/01/01/0006/ # LogGamma[z] == -LogGamma[-z] - Log[-z] + # Sign[Im[z]] Floor[Re[z]] Pi I + Log[Pi] - # Log[Sin[Pi (z - Floor[Re[z]])]] - # Pi I (1 - Abs[Sign[Im[z]]]) Abs[Floor[Re[z]]] if type == 3: if yfinal: s1 = mpc_neg(yfinal) else: s1 = mpc_neg(y) # s -= log(-z) s1 = mpc_sub(s1, mpc_log(mpc_neg(zorig), wp), wp) # floor(re(z)) rezfloor = mpf_floor(zorig[0]) imzsign = mpf_sign(zorig[1]) pi = mpf_pi(wp) t = mpf_mul(pi, rezfloor) t = mpf_mul_int(t, imzsign, wp) s1 = (s1[0], mpf_add(s1[1], t, wp)) s1 = mpc_add_mpf(s1, mpf_log(pi, wp), wp) t = mpc_sin_pi(mpc_sub_mpf(zorig, rezfloor), wp) t = mpc_log(t, wp) s1 = mpc_sub(s1, t, wp) # Note: may actually be unused, because we fall back # to the mpf_ function for real arguments if not imzsign: t = mpf_mul(pi, mpf_floor(rezfloor), wp) s1 = (s1[0], mpf_sub(s1[1], t, wp)) return mpc_pos(s1, prec, rnd) else: if type == 0: if r: return mpc_div(mpc_exp(y, wp), r, prec, rnd) return mpc_exp(y, prec, rnd) if type == 2: if r: return mpc_div(r, mpc_exp(y, wp), prec, rnd) return mpc_exp(mpc_neg(y), prec, rnd) if type == 3: return mpc_pos(y, prec, rnd) def mpf_factorial(x, prec, rnd='d'): return mpf_gamma(x, prec, rnd, 1) def mpc_factorial(x, prec, rnd='d'): return mpc_gamma(x, prec, rnd, 1) def mpf_rgamma(x, prec, rnd='d'): return mpf_gamma(x, prec, rnd, 2) def mpc_rgamma(x, prec, rnd='d'): return mpc_gamma(x, prec, rnd, 2) def mpf_loggamma(x, prec, rnd='d'): sign, man, exp, bc = x if sign: raise ComplexResult return mpf_gamma(x, prec, rnd, 3) def mpc_loggamma(z, prec, rnd='d'): a, b = z asign, aman, aexp, abc = a bsign, bman, bexp, bbc = b if b == fzero and asign: re = mpf_gamma(a, prec, rnd, 3) n = (-aman) >> (-aexp) im = mpf_mul_int(mpf_pi(prec+10), n, prec, rnd) return re, im return mpc_gamma(z, prec, rnd, 3) def mpf_gamma_int(n, prec, rnd=round_fast): if n < SMALL_FACTORIAL_CACHE_SIZE: return mpf_pos(small_factorial_cache[n-1], prec, rnd) return mpf_gamma(from_int(n), prec, rnd) sympy-0.7.4.1/sympy/mpmath/libmp/libmpi.py0000644000175000017500000006571112253362407020656 0ustar georgeskgeorgesk""" Computational functions for interval arithmetic. """ from .backend import xrange from .libmpf import ( ComplexResult, round_down, round_up, round_floor, round_ceiling, round_nearest, prec_to_dps, repr_dps, dps_to_prec, bitcount, from_float, fnan, finf, fninf, fzero, fhalf, fone, fnone, mpf_sign, mpf_lt, mpf_le, mpf_gt, mpf_ge, mpf_eq, mpf_cmp, mpf_min_max, mpf_floor, from_int, to_int, to_str, from_str, mpf_abs, mpf_neg, mpf_pos, mpf_add, mpf_sub, mpf_mul, mpf_mul_int, mpf_div, mpf_shift, mpf_pow_int, from_man_exp, MPZ_ONE) from .libelefun import ( mpf_log, mpf_exp, mpf_sqrt, mpf_atan, mpf_atan2, mpf_pi, mod_pi2, mpf_cos_sin ) from .gammazeta import mpf_gamma, mpf_rgamma, mpf_loggamma, mpc_loggamma def mpi_str(s, prec): sa, sb = s dps = prec_to_dps(prec) + 5 return "[%s, %s]" % (to_str(sa, dps), to_str(sb, dps)) #dps = prec_to_dps(prec) #m = mpi_mid(s, prec) #d = mpf_shift(mpi_delta(s, 20), -1) #return "%s +/- %s" % (to_str(m, dps), to_str(d, 3)) mpi_zero = (fzero, fzero) mpi_one = (fone, fone) def mpi_eq(s, t): return s == t def mpi_ne(s, t): return s != t def mpi_lt(s, t): sa, sb = s ta, tb = t if mpf_lt(sb, ta): return True if mpf_ge(sa, tb): return False return None def mpi_le(s, t): sa, sb = s ta, tb = t if mpf_le(sb, ta): return True if mpf_gt(sa, tb): return False return None def mpi_gt(s, t): return mpi_lt(t, s) def mpi_ge(s, t): return mpi_le(t, s) def mpi_add(s, t, prec=0): sa, sb = s ta, tb = t a = mpf_add(sa, ta, prec, round_floor) b = mpf_add(sb, tb, prec, round_ceiling) if a == fnan: a = fninf if b == fnan: b = finf return a, b def mpi_sub(s, t, prec=0): sa, sb = s ta, tb = t a = mpf_sub(sa, tb, prec, round_floor) b = mpf_sub(sb, ta, prec, round_ceiling) if a == fnan: a = fninf if b == fnan: b = finf return a, b def mpi_delta(s, prec): sa, sb = s return mpf_sub(sb, sa, prec, round_up) def mpi_mid(s, prec): sa, sb = s return mpf_shift(mpf_add(sa, sb, prec, round_nearest), -1) def mpi_pos(s, prec): sa, sb = s a = mpf_pos(sa, prec, round_floor) b = mpf_pos(sb, prec, round_ceiling) return a, b def mpi_neg(s, prec=0): sa, sb = s a = mpf_neg(sb, prec, round_floor) b = mpf_neg(sa, prec, round_ceiling) return a, b def mpi_abs(s, prec=0): sa, sb = s sas = mpf_sign(sa) sbs = mpf_sign(sb) # Both points nonnegative? if sas >= 0: a = mpf_pos(sa, prec, round_floor) b = mpf_pos(sb, prec, round_ceiling) # Upper point nonnegative? elif sbs >= 0: a = fzero negsa = mpf_neg(sa) if mpf_lt(negsa, sb): b = mpf_pos(sb, prec, round_ceiling) else: b = mpf_pos(negsa, prec, round_ceiling) # Both negative? else: a = mpf_neg(sb, prec, round_floor) b = mpf_neg(sa, prec, round_ceiling) return a, b # TODO: optimize def mpi_mul_mpf(s, t, prec): return mpi_mul(s, (t, t), prec) def mpi_div_mpf(s, t, prec): return mpi_div(s, (t, t), prec) def mpi_mul(s, t, prec=0): sa, sb = s ta, tb = t sas = mpf_sign(sa) sbs = mpf_sign(sb) tas = mpf_sign(ta) tbs = mpf_sign(tb) if sas == sbs == 0: # Should maybe be undefined if ta == fninf or tb == finf: return fninf, finf return fzero, fzero if tas == tbs == 0: # Should maybe be undefined if sa == fninf or sb == finf: return fninf, finf return fzero, fzero if sas >= 0: # positive * positive if tas >= 0: a = mpf_mul(sa, ta, prec, round_floor) b = mpf_mul(sb, tb, prec, round_ceiling) if a == fnan: a = fzero if b == fnan: b = finf # positive * negative elif tbs <= 0: a = mpf_mul(sb, ta, prec, round_floor) b = mpf_mul(sa, tb, prec, round_ceiling) if a == fnan: a = fninf if b == fnan: b = fzero # positive * both signs else: a = mpf_mul(sb, ta, prec, round_floor) b = mpf_mul(sb, tb, prec, round_ceiling) if a == fnan: a = fninf if b == fnan: b = finf elif sbs <= 0: # negative * positive if tas >= 0: a = mpf_mul(sa, tb, prec, round_floor) b = mpf_mul(sb, ta, prec, round_ceiling) if a == fnan: a = fninf if b == fnan: b = fzero # negative * negative elif tbs <= 0: a = mpf_mul(sb, tb, prec, round_floor) b = mpf_mul(sa, ta, prec, round_ceiling) if a == fnan: a = fzero if b == fnan: b = finf # negative * both signs else: a = mpf_mul(sa, tb, prec, round_floor) b = mpf_mul(sa, ta, prec, round_ceiling) if a == fnan: a = fninf if b == fnan: b = finf else: # General case: perform all cross-multiplications and compare # Since the multiplications can be done exactly, we need only # do 4 (instead of 8: two for each rounding mode) cases = [mpf_mul(sa, ta), mpf_mul(sa, tb), mpf_mul(sb, ta), mpf_mul(sb, tb)] if fnan in cases: a, b = (fninf, finf) else: a, b = mpf_min_max(cases) a = mpf_pos(a, prec, round_floor) b = mpf_pos(b, prec, round_ceiling) return a, b def mpi_square(s, prec=0): sa, sb = s if mpf_ge(sa, fzero): a = mpf_mul(sa, sa, prec, round_floor) b = mpf_mul(sb, sb, prec, round_ceiling) elif mpf_le(sb, fzero): a = mpf_mul(sb, sb, prec, round_floor) b = mpf_mul(sa, sa, prec, round_ceiling) else: sa = mpf_neg(sa) sa, sb = mpf_min_max([sa, sb]) a = fzero b = mpf_mul(sb, sb, prec, round_ceiling) return a, b def mpi_div(s, t, prec): sa, sb = s ta, tb = t sas = mpf_sign(sa) sbs = mpf_sign(sb) tas = mpf_sign(ta) tbs = mpf_sign(tb) # 0 / X if sas == sbs == 0: # 0 / if (tas < 0 and tbs > 0) or (tas == 0 or tbs == 0): return fninf, finf return fzero, fzero # Denominator contains both negative and positive numbers; # this should properly be a multi-interval, but the closest # match is the entire (extended) real line if tas < 0 and tbs > 0: return fninf, finf # Assume denominator to be nonnegative if tas < 0: return mpi_div(mpi_neg(s), mpi_neg(t), prec) # Division by zero # XXX: make sure all results make sense if tas == 0: # Numerator contains both signs? if sas < 0 and sbs > 0: return fninf, finf if tas == tbs: return fninf, finf # Numerator positive? if sas >= 0: a = mpf_div(sa, tb, prec, round_floor) b = finf if sbs <= 0: a = fninf b = mpf_div(sb, tb, prec, round_ceiling) # Division with positive denominator # We still have to handle nans resulting from inf/0 or inf/inf else: # Nonnegative numerator if sas >= 0: a = mpf_div(sa, tb, prec, round_floor) b = mpf_div(sb, ta, prec, round_ceiling) if a == fnan: a = fzero if b == fnan: b = finf # Nonpositive numerator elif sbs <= 0: a = mpf_div(sa, ta, prec, round_floor) b = mpf_div(sb, tb, prec, round_ceiling) if a == fnan: a = fninf if b == fnan: b = fzero # Numerator contains both signs? else: a = mpf_div(sa, ta, prec, round_floor) b = mpf_div(sb, ta, prec, round_ceiling) if a == fnan: a = fninf if b == fnan: b = finf return a, b def mpi_pi(prec): a = mpf_pi(prec, round_floor) b = mpf_pi(prec, round_ceiling) return a, b def mpi_exp(s, prec): sa, sb = s # exp is monotonic a = mpf_exp(sa, prec, round_floor) b = mpf_exp(sb, prec, round_ceiling) return a, b def mpi_log(s, prec): sa, sb = s # log is monotonic a = mpf_log(sa, prec, round_floor) b = mpf_log(sb, prec, round_ceiling) return a, b def mpi_sqrt(s, prec): sa, sb = s # sqrt is monotonic a = mpf_sqrt(sa, prec, round_floor) b = mpf_sqrt(sb, prec, round_ceiling) return a, b def mpi_atan(s, prec): sa, sb = s a = mpf_atan(sa, prec, round_floor) b = mpf_atan(sb, prec, round_ceiling) return a, b def mpi_pow_int(s, n, prec): sa, sb = s if n < 0: return mpi_div((fone, fone), mpi_pow_int(s, -n, prec+20), prec) if n == 0: return (fone, fone) if n == 1: return s if n == 2: return mpi_square(s, prec) # Odd -- signs are preserved if n & 1: a = mpf_pow_int(sa, n, prec, round_floor) b = mpf_pow_int(sb, n, prec, round_ceiling) # Even -- important to ensure positivity else: sas = mpf_sign(sa) sbs = mpf_sign(sb) # Nonnegative? if sas >= 0: a = mpf_pow_int(sa, n, prec, round_floor) b = mpf_pow_int(sb, n, prec, round_ceiling) # Nonpositive? elif sbs <= 0: a = mpf_pow_int(sb, n, prec, round_floor) b = mpf_pow_int(sa, n, prec, round_ceiling) # Mixed signs? else: a = fzero # max(-a,b)**n sa = mpf_neg(sa) if mpf_ge(sa, sb): b = mpf_pow_int(sa, n, prec, round_ceiling) else: b = mpf_pow_int(sb, n, prec, round_ceiling) return a, b def mpi_pow(s, t, prec): ta, tb = t if ta == tb and ta not in (finf, fninf): if ta == from_int(to_int(ta)): return mpi_pow_int(s, to_int(ta), prec) if ta == fhalf: return mpi_sqrt(s, prec) u = mpi_log(s, prec + 20) v = mpi_mul(u, t, prec + 20) return mpi_exp(v, prec) def MIN(x, y): if mpf_le(x, y): return x return y def MAX(x, y): if mpf_ge(x, y): return x return y def cos_sin_quadrant(x, wp): sign, man, exp, bc = x if x == fzero: return fone, fzero, 0 # TODO: combine evaluation code to avoid duplicate modulo c, s = mpf_cos_sin(x, wp) t, n, wp_ = mod_pi2(man, exp, exp+bc, 15) if sign: n = -1-n return c, s, n def mpi_cos_sin(x, prec): a, b = x if a == b == fzero: return (fone, fone), (fzero, fzero) # Guaranteed to contain both -1 and 1 if (finf in x) or (fninf in x): return (fnone, fone), (fnone, fone) wp = prec + 20 ca, sa, na = cos_sin_quadrant(a, wp) cb, sb, nb = cos_sin_quadrant(b, wp) ca, cb = mpf_min_max([ca, cb]) sa, sb = mpf_min_max([sa, sb]) # Both functions are monotonic within one quadrant if na == nb: pass # Guaranteed to contain both -1 and 1 elif nb - na >= 4: return (fnone, fone), (fnone, fone) else: # cos has maximum between a and b if na//4 != nb//4: cb = fone # cos has minimum if (na-2)//4 != (nb-2)//4: ca = fnone # sin has maximum if (na-1)//4 != (nb-1)//4: sb = fone # sin has minimum if (na-3)//4 != (nb-3)//4: sa = fnone # Perturb to force interval rounding more = from_man_exp((MPZ_ONE<= 1: if sign: return fnone return fone return v ca = finalize(ca, round_floor) cb = finalize(cb, round_ceiling) sa = finalize(sa, round_floor) sb = finalize(sb, round_ceiling) return (ca,cb), (sa,sb) def mpi_cos(x, prec): return mpi_cos_sin(x, prec)[0] def mpi_sin(x, prec): return mpi_cos_sin(x, prec)[1] def mpi_tan(x, prec): cos, sin = mpi_cos_sin(x, prec+20) return mpi_div(sin, cos, prec) def mpi_cot(x, prec): cos, sin = mpi_cos_sin(x, prec+20) return mpi_div(cos, sin, prec) def mpi_from_str_a_b(x, y, percent, prec): wp = prec + 20 xa = from_str(x, wp, round_floor) xb = from_str(x, wp, round_ceiling) #ya = from_str(y, wp, round_floor) y = from_str(y, wp, round_ceiling) assert mpf_ge(y, fzero) if percent: y = mpf_mul(MAX(mpf_abs(xa), mpf_abs(xb)), y, wp, round_ceiling) y = mpf_div(y, from_int(100), wp, round_ceiling) a = mpf_sub(xa, y, prec, round_floor) b = mpf_add(xb, y, prec, round_ceiling) return a, b def mpi_from_str(s, prec): """ Parse an interval number given as a string. Allowed forms are "-1.23e-27" Any single decimal floating-point literal. "a +- b" or "a (b)" a is the midpoint of the interval and b is the half-width "a +- b%" or "a (b%)" a is the midpoint of the interval and the half-width is b percent of a (`a \times b / 100`). "[a, b]" The interval indicated directly. "x[y,z]e" x are shared digits, y and z are unequal digits, e is the exponent. """ e = ValueError("Improperly formed interval number '%s'" % s) s = s.replace(" ", "") wp = prec + 20 if "+-" in s: x, y = s.split("+-") return mpi_from_str_a_b(x, y, False, prec) # case 2 elif "(" in s: # Don't confuse with a complex number (x,y) if s[0] == "(" or ")" not in s: raise e s = s.replace(")", "") percent = False if "%" in s: if s[-1] != "%": raise e percent = True s = s.replace("%", "") x, y = s.split("(") return mpi_from_str_a_b(x, y, percent, prec) elif "," in s: if ('[' not in s) or (']' not in s): raise e if s[0] == '[': # case 3 s = s.replace("[", "") s = s.replace("]", "") a, b = s.split(",") a = from_str(a, prec, round_floor) b = from_str(b, prec, round_ceiling) return a, b else: # case 4 x, y = s.split('[') y, z = y.split(',') if 'e' in s: z, e = z.split(']') else: z, e = z.rstrip(']'), '' a = from_str(x+y+e, prec, round_floor) b = from_str(x+z+e, prec, round_ceiling) return a, b else: a = from_str(s, prec, round_floor) b = from_str(s, prec, round_ceiling) return a, b def mpi_to_str(x, dps, use_spaces=True, brackets='[]', mode='brackets', error_dps=4, **kwargs): """ Convert a mpi interval to a string. **Arguments** *dps* decimal places to use for printing *use_spaces* use spaces for more readable output, defaults to true *brackets* pair of strings (or two-character string) giving left and right brackets *mode* mode of display: 'plusminus', 'percent', 'brackets' (default) or 'diff' *error_dps* limit the error to *error_dps* digits (mode 'plusminus and 'percent') Additional keyword arguments are forwarded to the mpf-to-string conversion for the components of the output. **Examples** >>> from mpmath import mpi, mp >>> mp.dps = 30 >>> x = mpi(1, 2) >>> mpi_to_str(x, mode='plusminus') '1.5 +- 5.0e-1' >>> mpi_to_str(x, mode='percent') '1.5 (33.33%)' >>> mpi_to_str(x, mode='brackets') '[1.0, 2.0]' >>> mpi_to_str(x, mode='brackets' , brackets=('<', '>')) '<1.0, 2.0>' >>> x = mpi('5.2582327113062393041', '5.2582327113062749951') >>> mpi_to_str(x, mode='diff') '5.2582327113062[4, 7]' >>> mpi_to_str(mpi(0), mode='percent') '0.0 (0%)' """ prec = dps_to_prec(dps) wp = prec + 20 a, b = x mid = mpi_mid(x, prec) delta = mpi_delta(x, prec) a_str = to_str(a, dps, **kwargs) b_str = to_str(b, dps, **kwargs) mid_str = to_str(mid, dps, **kwargs) sp = "" if use_spaces: sp = " " br1, br2 = brackets if mode == 'plusminus': delta_str = to_str(mpf_shift(delta,-1), dps, **kwargs) s = mid_str + sp + "+-" + sp + delta_str elif mode == 'percent': if mid == fzero: p = fzero else: # p = 100 * delta(x) / (2*mid(x)) p = mpf_mul(delta, from_int(100)) p = mpf_div(p, mpf_mul(mid, from_int(2)), wp) s = mid_str + sp + "(" + to_str(p, error_dps) + "%)" elif mode == 'brackets': s = br1 + a_str + "," + sp + b_str + br2 elif mode == 'diff': # use more digits if str(x.a) and str(x.b) are equal if a_str == b_str: a_str = to_str(a, dps+3, **kwargs) b_str = to_str(b, dps+3, **kwargs) # separate mantissa and exponent a = a_str.split('e') if len(a) == 1: a.append('') b = b_str.split('e') if len(b) == 1: b.append('') if a[1] == b[1]: if a[0] != b[0]: for i in xrange(len(a[0]) + 1): if a[0][i] != b[0][i]: break s = (a[0][:i] + br1 + a[0][i:] + ',' + sp + b[0][i:] + br2 + 'e'*min(len(a[1]), 1) + a[1]) else: # no difference s = a[0] + br1 + br2 + 'e'*min(len(a[1]), 1) + a[1] else: s = br1 + 'e'.join(a) + ',' + sp + 'e'.join(b) + br2 else: raise ValueError("'%s' is unknown mode for printing mpi" % mode) return s def mpci_add(x, y, prec): a, b = x c, d = y return mpi_add(a, c, prec), mpi_add(b, d, prec) def mpci_sub(x, y, prec): a, b = x c, d = y return mpi_sub(a, c, prec), mpi_sub(b, d, prec) def mpci_neg(x, prec=0): a, b = x return mpi_neg(a, prec), mpi_neg(b, prec) def mpci_pos(x, prec): a, b = x return mpi_pos(a, prec), mpi_pos(b, prec) def mpci_mul(x, y, prec): # TODO: optimize for real/imag cases a, b = x c, d = y r1 = mpi_mul(a,c) r2 = mpi_mul(b,d) re = mpi_sub(r1,r2,prec) i1 = mpi_mul(a,d) i2 = mpi_mul(b,c) im = mpi_add(i1,i2,prec) return re, im def mpci_div(x, y, prec): # TODO: optimize for real/imag cases a, b = x c, d = y wp = prec+20 m1 = mpi_square(c) m2 = mpi_square(d) m = mpi_add(m1,m2,wp) re = mpi_add(mpi_mul(a,c), mpi_mul(b,d), wp) im = mpi_sub(mpi_mul(b,c), mpi_mul(a,d), wp) re = mpi_div(re, m, prec) im = mpi_div(im, m, prec) return re, im def mpci_exp(x, prec): a, b = x wp = prec+20 r = mpi_exp(a, wp) c, s = mpi_cos_sin(b, wp) a = mpi_mul(r, c, prec) b = mpi_mul(r, s, prec) return a, b def mpi_shift(x, n): a, b = x return mpf_shift(a,n), mpf_shift(b,n) def mpi_cosh_sinh(x, prec): # TODO: accuracy for small x wp = prec+20 e1 = mpi_exp(x, wp) e2 = mpi_div(mpi_one, e1, wp) c = mpi_add(e1, e2, prec) s = mpi_sub(e1, e2, prec) c = mpi_shift(c, -1) s = mpi_shift(s, -1) return c, s def mpci_cos(x, prec): a, b = x wp = prec+10 c, s = mpi_cos_sin(a, wp) ch, sh = mpi_cosh_sinh(b, wp) re = mpi_mul(c, ch, prec) im = mpi_mul(s, sh, prec) return re, mpi_neg(im) def mpci_sin(x, prec): a, b = x wp = prec+10 c, s = mpi_cos_sin(a, wp) ch, sh = mpi_cosh_sinh(b, wp) re = mpi_mul(s, ch, prec) im = mpi_mul(c, sh, prec) return re, im def mpci_abs(x, prec): a, b = x if a == mpi_zero: return mpi_abs(b) if b == mpi_zero: return mpi_abs(a) # Important: nonnegative a = mpi_square(a) b = mpi_square(b) t = mpi_add(a, b, prec+20) return mpi_sqrt(t, prec) def mpi_atan2(y, x, prec): ya, yb = y xa, xb = x # Constrained to the real line if ya == yb == fzero: if mpf_ge(xa, fzero): return mpi_zero return mpi_pi(prec) # Right half-plane if mpf_ge(xa, fzero): if mpf_ge(ya, fzero): a = mpf_atan2(ya, xb, prec, round_floor) else: a = mpf_atan2(ya, xa, prec, round_floor) if mpf_ge(yb, fzero): b = mpf_atan2(yb, xa, prec, round_ceiling) else: b = mpf_atan2(yb, xb, prec, round_ceiling) # Upper half-plane elif mpf_ge(ya, fzero): b = mpf_atan2(ya, xa, prec, round_ceiling) if mpf_le(xb, fzero): a = mpf_atan2(yb, xb, prec, round_floor) else: a = mpf_atan2(ya, xb, prec, round_floor) # Lower half-plane elif mpf_le(yb, fzero): a = mpf_atan2(yb, xa, prec, round_floor) if mpf_le(xb, fzero): b = mpf_atan2(ya, xb, prec, round_ceiling) else: b = mpf_atan2(yb, xb, prec, round_ceiling) # Covering the origin else: b = mpf_pi(prec, round_ceiling) a = mpf_neg(b) return a, b def mpci_arg(z, prec): x, y = z return mpi_atan2(y, x, prec) def mpci_log(z, prec): x, y = z re = mpi_log(mpci_abs(z, prec+20), prec) im = mpci_arg(z, prec) return re, im def mpci_pow(x, y, prec): # TODO: recognize/speed up real cases, integer y yre, yim = y if yim == mpi_zero: ya, yb = yre if ya == yb: sign, man, exp, bc = yb if man and exp >= 0: return mpci_pow_int(x, (-1)**sign * int(man<>= 1 return mpci_pos(result, prec) gamma_min_a = from_float(1.46163214496) gamma_min_b = from_float(1.46163214497) gamma_min = (gamma_min_a, gamma_min_b) gamma_mono_imag_a = from_float(-1.1) gamma_mono_imag_b = from_float(1.1) def mpi_overlap(x, y): a, b = x c, d = y if mpf_lt(d, a): return False if mpf_gt(c, b): return False return True # type = 0 -- gamma # type = 1 -- factorial # type = 2 -- 1/gamma # type = 3 -- log-gamma def mpi_gamma(z, prec, type=0): a, b = z wp = prec+20 if type == 1: return mpi_gamma(mpi_add(z, mpi_one, wp), prec, 0) # increasing if mpf_gt(a, gamma_min_b): if type == 0: c = mpf_gamma(a, prec, round_floor) d = mpf_gamma(b, prec, round_ceiling) elif type == 2: c = mpf_rgamma(b, prec, round_floor) d = mpf_rgamma(a, prec, round_ceiling) elif type == 3: c = mpf_loggamma(a, prec, round_floor) d = mpf_loggamma(b, prec, round_ceiling) # decreasing elif mpf_gt(a, fzero) and mpf_lt(b, gamma_min_a): if type == 0: c = mpf_gamma(b, prec, round_floor) d = mpf_gamma(a, prec, round_ceiling) elif type == 2: c = mpf_rgamma(a, prec, round_floor) d = mpf_rgamma(b, prec, round_ceiling) elif type == 3: c = mpf_loggamma(b, prec, round_floor) d = mpf_loggamma(a, prec, round_ceiling) else: # TODO: reflection formula znew = mpi_add(z, mpi_one, wp) if type == 0: return mpi_div(mpi_gamma(znew, prec+2, 0), z, prec) if type == 2: return mpi_mul(mpi_gamma(znew, prec+2, 2), z, prec) if type == 3: return mpi_sub(mpi_gamma(znew, prec+2, 3), mpi_log(z, prec+2), prec) return c, d def mpci_gamma(z, prec, type=0): (a1,a2), (b1,b2) = z # Real case if b1 == b2 == fzero and (type != 3 or mpf_gt(a1,fzero)): return mpi_gamma(z, prec, type), mpi_zero # Estimate precision wp = prec+20 if type != 3: amag = a2[2]+a2[3] bmag = b2[2]+b2[3] if a2 != fzero: mag = max(amag, bmag) else: mag = bmag an = abs(to_int(a2)) bn = abs(to_int(b2)) absn = max(an, bn) gamma_size = max(0,absn*mag) wp += bitcount(gamma_size) # Assume type != 1 if type == 1: (a1,a2) = mpi_add((a1,a2), mpi_one, wp) z = (a1,a2), (b1,b2) type = 0 # Avoid non-monotonic region near the negative real axis if mpf_lt(a1, gamma_min_b): if mpi_overlap((b1,b2), (gamma_mono_imag_a, gamma_mono_imag_b)): # TODO: reflection formula #if mpf_lt(a2, mpf_shift(fone,-1)): # znew = mpci_sub((mpi_one,mpi_zero),z,wp) # ... # Recurrence: # gamma(z) = gamma(z+1)/z znew = mpi_add((a1,a2), mpi_one, wp), (b1,b2) if type == 0: return mpci_div(mpci_gamma(znew, prec+2, 0), z, prec) if type == 2: return mpci_mul(mpci_gamma(znew, prec+2, 2), z, prec) if type == 3: return mpci_sub(mpci_gamma(znew, prec+2, 3), mpci_log(z,prec+2), prec) # Use monotonicity (except for a small region close to the # origin and near poles) # upper half-plane if mpf_ge(b1, fzero): minre = mpc_loggamma((a1,b2), wp, round_floor) maxre = mpc_loggamma((a2,b1), wp, round_ceiling) minim = mpc_loggamma((a1,b1), wp, round_floor) maxim = mpc_loggamma((a2,b2), wp, round_ceiling) # lower half-plane elif mpf_le(b2, fzero): minre = mpc_loggamma((a1,b1), wp, round_floor) maxre = mpc_loggamma((a2,b2), wp, round_ceiling) minim = mpc_loggamma((a2,b1), wp, round_floor) maxim = mpc_loggamma((a1,b2), wp, round_ceiling) # crosses real axis else: maxre = mpc_loggamma((a2,fzero), wp, round_ceiling) # stretches more into the lower half-plane if mpf_gt(mpf_neg(b1), b2): minre = mpc_loggamma((a1,b1), wp, round_ceiling) else: minre = mpc_loggamma((a1,b2), wp, round_ceiling) minim = mpc_loggamma((a2,b1), wp, round_floor) maxim = mpc_loggamma((a2,b2), wp, round_floor) w = (minre[0], maxre[0]), (minim[1], maxim[1]) if type == 3: return mpi_pos(w[0], prec), mpi_pos(w[1], prec) if type == 2: w = mpci_neg(w) return mpci_exp(w, prec) def mpi_loggamma(z, prec): return mpi_gamma(z, prec, type=3) def mpci_loggamma(z, prec): return mpci_gamma(z, prec, type=3) def mpi_rgamma(z, prec): return mpi_gamma(z, prec, type=2) def mpci_rgamma(z, prec): return mpci_gamma(z, prec, type=2) def mpi_factorial(z, prec): return mpi_gamma(z, prec, type=1) def mpci_factorial(z, prec): return mpci_gamma(z, prec, type=1) sympy-0.7.4.1/sympy/mpmath/libmp/libmpc.py0000644000175000017500000006433312253362407020647 0ustar georgeskgeorgesk""" Low-level functions for complex arithmetic. """ import sys from .backend import MPZ, MPZ_ZERO, MPZ_ONE, MPZ_TWO, BACKEND from .libmpf import (\ round_floor, round_ceiling, round_down, round_up, round_nearest, round_fast, bitcount, bctable, normalize, normalize1, reciprocal_rnd, rshift, lshift, giant_steps, negative_rnd, to_str, to_fixed, from_man_exp, from_float, to_float, from_int, to_int, fzero, fone, ftwo, fhalf, finf, fninf, fnan, fnone, mpf_abs, mpf_pos, mpf_neg, mpf_add, mpf_sub, mpf_mul, mpf_div, mpf_mul_int, mpf_shift, mpf_sqrt, mpf_hypot, mpf_rdiv_int, mpf_floor, mpf_ceil, mpf_nint, mpf_frac, mpf_sign, mpf_hash, ComplexResult ) from .libelefun import (\ mpf_pi, mpf_exp, mpf_log, mpf_cos_sin, mpf_cosh_sinh, mpf_tan, mpf_pow_int, mpf_log_hypot, mpf_cos_sin_pi, mpf_phi, mpf_cos, mpf_sin, mpf_cos_pi, mpf_sin_pi, mpf_atan, mpf_atan2, mpf_cosh, mpf_sinh, mpf_tanh, mpf_asin, mpf_acos, mpf_acosh, mpf_nthroot, mpf_fibonacci ) # An mpc value is a (real, imag) tuple mpc_one = fone, fzero mpc_zero = fzero, fzero mpc_two = ftwo, fzero mpc_half = (fhalf, fzero) _infs = (finf, fninf) _infs_nan = (finf, fninf, fnan) def mpc_is_inf(z): """Check if either real or imaginary part is infinite""" re, im = z if re in _infs: return True if im in _infs: return True return False def mpc_is_infnan(z): """Check if either real or imaginary part is infinite or nan""" re, im = z if re in _infs_nan: return True if im in _infs_nan: return True return False def mpc_to_str(z, dps, **kwargs): re, im = z rs = to_str(re, dps) if im[0]: return rs + " - " + to_str(mpf_neg(im), dps, **kwargs) + "j" else: return rs + " + " + to_str(im, dps, **kwargs) + "j" def mpc_to_complex(z, strict=False): re, im = z return complex(to_float(re, strict), to_float(im, strict)) def mpc_hash(z): if sys.version >= "3.2": re, im = z h = mpf_hash(re) + sys.hash_info.imag * mpf_hash(im) # Need to reduce either module 2^32 or 2^64 h = h % (2**sys.hash_info.width) return int(h) else: try: return hash(mpc_to_complex(z, strict=True)) except OverflowError: return hash(z) def mpc_conjugate(z, prec, rnd=round_fast): re, im = z return re, mpf_neg(im, prec, rnd) def mpc_is_nonzero(z): return z != mpc_zero def mpc_add(z, w, prec, rnd=round_fast): a, b = z c, d = w return mpf_add(a, c, prec, rnd), mpf_add(b, d, prec, rnd) def mpc_add_mpf(z, x, prec, rnd=round_fast): a, b = z return mpf_add(a, x, prec, rnd), b def mpc_sub(z, w, prec=0, rnd=round_fast): a, b = z c, d = w return mpf_sub(a, c, prec, rnd), mpf_sub(b, d, prec, rnd) def mpc_sub_mpf(z, p, prec=0, rnd=round_fast): a, b = z return mpf_sub(a, p, prec, rnd), b def mpc_pos(z, prec, rnd=round_fast): a, b = z return mpf_pos(a, prec, rnd), mpf_pos(b, prec, rnd) def mpc_neg(z, prec=None, rnd=round_fast): a, b = z return mpf_neg(a, prec, rnd), mpf_neg(b, prec, rnd) def mpc_shift(z, n): a, b = z return mpf_shift(a, n), mpf_shift(b, n) def mpc_abs(z, prec, rnd=round_fast): """Absolute value of a complex number, |a+bi|. Returns an mpf value.""" a, b = z return mpf_hypot(a, b, prec, rnd) def mpc_arg(z, prec, rnd=round_fast): """Argument of a complex number. Returns an mpf value.""" a, b = z return mpf_atan2(b, a, prec, rnd) def mpc_floor(z, prec, rnd=round_fast): a, b = z return mpf_floor(a, prec, rnd), mpf_floor(b, prec, rnd) def mpc_ceil(z, prec, rnd=round_fast): a, b = z return mpf_ceil(a, prec, rnd), mpf_ceil(b, prec, rnd) def mpc_nint(z, prec, rnd=round_fast): a, b = z return mpf_nint(a, prec, rnd), mpf_nint(b, prec, rnd) def mpc_frac(z, prec, rnd=round_fast): a, b = z return mpf_frac(a, prec, rnd), mpf_frac(b, prec, rnd) def mpc_mul(z, w, prec, rnd=round_fast): """ Complex multiplication. Returns the real and imaginary part of (a+bi)*(c+di), rounded to the specified precision. The rounding mode applies to the real and imaginary parts separately. """ a, b = z c, d = w p = mpf_mul(a, c) q = mpf_mul(b, d) r = mpf_mul(a, d) s = mpf_mul(b, c) re = mpf_sub(p, q, prec, rnd) im = mpf_add(r, s, prec, rnd) return re, im def mpc_square(z, prec, rnd=round_fast): # (a+b*I)**2 == a**2 - b**2 + 2*I*a*b a, b = z p = mpf_mul(a,a) q = mpf_mul(b,b) r = mpf_mul(a,b, prec, rnd) re = mpf_sub(p, q, prec, rnd) im = mpf_shift(r, 1) return re, im def mpc_mul_mpf(z, p, prec, rnd=round_fast): a, b = z re = mpf_mul(a, p, prec, rnd) im = mpf_mul(b, p, prec, rnd) return re, im def mpc_mul_imag_mpf(z, x, prec, rnd=round_fast): """ Multiply the mpc value z by I*x where x is an mpf value. """ a, b = z re = mpf_neg(mpf_mul(b, x, prec, rnd)) im = mpf_mul(a, x, prec, rnd) return re, im def mpc_mul_int(z, n, prec, rnd=round_fast): a, b = z re = mpf_mul_int(a, n, prec, rnd) im = mpf_mul_int(b, n, prec, rnd) return re, im def mpc_div(z, w, prec, rnd=round_fast): a, b = z c, d = w wp = prec + 10 # mag = c*c + d*d mag = mpf_add(mpf_mul(c, c), mpf_mul(d, d), wp) # (a*c+b*d)/mag, (b*c-a*d)/mag t = mpf_add(mpf_mul(a,c), mpf_mul(b,d), wp) u = mpf_sub(mpf_mul(b,c), mpf_mul(a,d), wp) return mpf_div(t,mag,prec,rnd), mpf_div(u,mag,prec,rnd) def mpc_div_mpf(z, p, prec, rnd=round_fast): """Calculate z/p where p is real""" a, b = z re = mpf_div(a, p, prec, rnd) im = mpf_div(b, p, prec, rnd) return re, im def mpc_reciprocal(z, prec, rnd=round_fast): """Calculate 1/z efficiently""" a, b = z m = mpf_add(mpf_mul(a,a),mpf_mul(b,b),prec+10) re = mpf_div(a, m, prec, rnd) im = mpf_neg(mpf_div(b, m, prec, rnd)) return re, im def mpc_mpf_div(p, z, prec, rnd=round_fast): """Calculate p/z where p is real efficiently""" a, b = z m = mpf_add(mpf_mul(a,a),mpf_mul(b,b), prec+10) re = mpf_div(mpf_mul(a,p), m, prec, rnd) im = mpf_div(mpf_neg(mpf_mul(b,p)), m, prec, rnd) return re, im def complex_int_pow(a, b, n): """Complex integer power: computes (a+b*I)**n exactly for nonnegative n (a and b must be Python ints).""" wre = 1 wim = 0 while n: if n & 1: wre, wim = wre*a - wim*b, wim*a + wre*b n -= 1 a, b = a*a - b*b, 2*a*b n //= 2 return wre, wim def mpc_pow(z, w, prec, rnd=round_fast): if w[1] == fzero: return mpc_pow_mpf(z, w[0], prec, rnd) return mpc_exp(mpc_mul(mpc_log(z, prec+10), w, prec+10), prec, rnd) def mpc_pow_mpf(z, p, prec, rnd=round_fast): psign, pman, pexp, pbc = p if pexp >= 0: return mpc_pow_int(z, (-1)**psign * (pman< 0: aman <<= de aexp = bexp else: bman <<= (-de) bexp = aexp re, im = complex_int_pow(aman, bman, n) re = from_man_exp(re, int(n*aexp), prec, rnd) im = from_man_exp(im, int(n*bexp), prec, rnd) return re, im return mpc_exp(mpc_mul_int(mpc_log(z, prec+10), n, prec+10), prec, rnd) def mpc_sqrt(z, prec, rnd=round_fast): """Complex square root (principal branch). We have sqrt(a+bi) = sqrt((r+a)/2) + b/sqrt(2*(r+a))*i where r = abs(a+bi), when a+bi is not a negative real number.""" a, b = z if b == fzero: if a == fzero: return (a, b) # When a+bi is a negative real number, we get a real sqrt times i if a[0]: im = mpf_sqrt(mpf_neg(a), prec, rnd) return (fzero, im) else: re = mpf_sqrt(a, prec, rnd) return (re, fzero) wp = prec+20 if not a[0]: # case a positive t = mpf_add(mpc_abs((a, b), wp), a, wp) # t = abs(a+bi) + a u = mpf_shift(t, -1) # u = t/2 re = mpf_sqrt(u, prec, rnd) # re = sqrt(u) v = mpf_shift(t, 1) # v = 2*t w = mpf_sqrt(v, wp) # w = sqrt(v) im = mpf_div(b, w, prec, rnd) # im = b / w else: # case a negative t = mpf_sub(mpc_abs((a, b), wp), a, wp) # t = abs(a+bi) - a u = mpf_shift(t, -1) # u = t/2 im = mpf_sqrt(u, prec, rnd) # im = sqrt(u) v = mpf_shift(t, 1) # v = 2*t w = mpf_sqrt(v, wp) # w = sqrt(v) re = mpf_div(b, w, prec, rnd) # re = b/w if b[0]: re = mpf_neg(re) im = mpf_neg(im) return re, im def mpc_nthroot_fixed(a, b, n, prec): # a, b signed integers at fixed precision prec start = 50 a1 = int(rshift(a, prec - n*start)) b1 = int(rshift(b, prec - n*start)) try: r = (a1 + 1j * b1)**(1.0/n) re = r.real im = r.imag re = MPZ(int(re)) im = MPZ(int(im)) except OverflowError: a1 = from_int(a1, start) b1 = from_int(b1, start) fn = from_int(n) nth = mpf_rdiv_int(1, fn, start) re, im = mpc_pow((a1, b1), (nth, fzero), start) re = to_int(re) im = to_int(im) extra = 10 prevp = start extra1 = n for p in giant_steps(start, prec+extra): # this is slow for large n, unlike int_pow_fixed re2, im2 = complex_int_pow(re, im, n-1) re2 = rshift(re2, (n-1)*prevp - p - extra1) im2 = rshift(im2, (n-1)*prevp - p - extra1) r4 = (re2*re2 + im2*im2) >> (p + extra1) ap = rshift(a, prec - p) bp = rshift(b, prec - p) rec = (ap * re2 + bp * im2) >> p imc = (-ap * im2 + bp * re2) >> p reb = (rec << p) // r4 imb = (imc << p) // r4 re = (reb + (n-1)*lshift(re, p-prevp))//n im = (imb + (n-1)*lshift(im, p-prevp))//n prevp = p return re, im def mpc_nthroot(z, n, prec, rnd=round_fast): """ Complex n-th root. Use Newton method as in the real case when it is faster, otherwise use z**(1/n) """ a, b = z if a[0] == 0 and b == fzero: re = mpf_nthroot(a, n, prec, rnd) return (re, fzero) if n < 2: if n == 0: return mpc_one if n == 1: return mpc_pos((a, b), prec, rnd) if n == -1: return mpc_div(mpc_one, (a, b), prec, rnd) inverse = mpc_nthroot((a, b), -n, prec+5, reciprocal_rnd[rnd]) return mpc_div(mpc_one, inverse, prec, rnd) if n <= 20: prec2 = int(1.2 * (prec + 10)) asign, aman, aexp, abc = a bsign, bman, bexp, bbc = b pf = mpc_abs((a,b), prec) if pf[-2] + pf[-1] > -10 and pf[-2] + pf[-1] < prec: af = to_fixed(a, prec2) bf = to_fixed(b, prec2) re, im = mpc_nthroot_fixed(af, bf, n, prec2) extra = 10 re = from_man_exp(re, -prec2-extra, prec2, rnd) im = from_man_exp(im, -prec2-extra, prec2, rnd) return re, im fn = from_int(n) prec2 = prec+10 + 10 nth = mpf_rdiv_int(1, fn, prec2) re, im = mpc_pow((a, b), (nth, fzero), prec2, rnd) re = normalize(re[0], re[1], re[2], re[3], prec, rnd) im = normalize(im[0], im[1], im[2], im[3], prec, rnd) return re, im def mpc_cbrt(z, prec, rnd=round_fast): """ Complex cubic root. """ return mpc_nthroot(z, 3, prec, rnd) def mpc_exp(z, prec, rnd=round_fast): """ Complex exponential function. We use the direct formula exp(a+bi) = exp(a) * (cos(b) + sin(b)*i) for the computation. This formula is very nice because it is pefectly stable; since we just do real multiplications, the only numerical errors that can creep in are single-ulp rounding errors. The formula is efficient since mpmath's real exp is quite fast and since we can compute cos and sin simultaneously. It is no problem if a and b are large; if the implementations of exp/cos/sin are accurate and efficient for all real numbers, then so is this function for all complex numbers. """ a, b = z if a == fzero: return mpf_cos_sin(b, prec, rnd) if b == fzero: return mpf_exp(a, prec, rnd), fzero mag = mpf_exp(a, prec+4, rnd) c, s = mpf_cos_sin(b, prec+4, rnd) re = mpf_mul(mag, c, prec, rnd) im = mpf_mul(mag, s, prec, rnd) return re, im def mpc_log(z, prec, rnd=round_fast): re = mpf_log_hypot(z[0], z[1], prec, rnd) im = mpc_arg(z, prec, rnd) return re, im def mpc_cos(z, prec, rnd=round_fast): """Complex cosine. The formula used is cos(a+bi) = cos(a)*cosh(b) - sin(a)*sinh(b)*i. The same comments apply as for the complex exp: only real multiplications are pewrormed, so no cancellation errors are possible. The formula is also efficient since we can compute both pairs (cos, sin) and (cosh, sinh) in single stwps.""" a, b = z if b == fzero: return mpf_cos(a, prec, rnd), fzero if a == fzero: return mpf_cosh(b, prec, rnd), fzero wp = prec + 6 c, s = mpf_cos_sin(a, wp) ch, sh = mpf_cosh_sinh(b, wp) re = mpf_mul(c, ch, prec, rnd) im = mpf_mul(s, sh, prec, rnd) return re, mpf_neg(im) def mpc_sin(z, prec, rnd=round_fast): """Complex sine. We have sin(a+bi) = sin(a)*cosh(b) + cos(a)*sinh(b)*i. See the docstring for mpc_cos for additional comments.""" a, b = z if b == fzero: return mpf_sin(a, prec, rnd), fzero if a == fzero: return fzero, mpf_sinh(b, prec, rnd) wp = prec + 6 c, s = mpf_cos_sin(a, wp) ch, sh = mpf_cosh_sinh(b, wp) re = mpf_mul(s, ch, prec, rnd) im = mpf_mul(c, sh, prec, rnd) return re, im def mpc_tan(z, prec, rnd=round_fast): """Complex tangent. Computed as tan(a+bi) = sin(2a)/M + sinh(2b)/M*i where M = cos(2a) + cosh(2b).""" a, b = z asign, aman, aexp, abc = a bsign, bman, bexp, bbc = b if b == fzero: return mpf_tan(a, prec, rnd), fzero if a == fzero: return fzero, mpf_tanh(b, prec, rnd) wp = prec + 15 a = mpf_shift(a, 1) b = mpf_shift(b, 1) c, s = mpf_cos_sin(a, wp) ch, sh = mpf_cosh_sinh(b, wp) # TODO: handle cancellation when c ~= -1 and ch ~= 1 mag = mpf_add(c, ch, wp) re = mpf_div(s, mag, prec, rnd) im = mpf_div(sh, mag, prec, rnd) return re, im def mpc_cos_pi(z, prec, rnd=round_fast): a, b = z if b == fzero: return mpf_cos_pi(a, prec, rnd), fzero b = mpf_mul(b, mpf_pi(prec+5), prec+5) if a == fzero: return mpf_cosh(b, prec, rnd), fzero wp = prec + 6 c, s = mpf_cos_sin_pi(a, wp) ch, sh = mpf_cosh_sinh(b, wp) re = mpf_mul(c, ch, prec, rnd) im = mpf_mul(s, sh, prec, rnd) return re, mpf_neg(im) def mpc_sin_pi(z, prec, rnd=round_fast): a, b = z if b == fzero: return mpf_sin_pi(a, prec, rnd), fzero b = mpf_mul(b, mpf_pi(prec+5), prec+5) if a == fzero: return fzero, mpf_sinh(b, prec, rnd) wp = prec + 6 c, s = mpf_cos_sin_pi(a, wp) ch, sh = mpf_cosh_sinh(b, wp) re = mpf_mul(s, ch, prec, rnd) im = mpf_mul(c, sh, prec, rnd) return re, im def mpc_cos_sin(z, prec, rnd=round_fast): a, b = z if a == fzero: ch, sh = mpf_cosh_sinh(b, prec, rnd) return (ch, fzero), (fzero, sh) if b == fzero: c, s = mpf_cos_sin(a, prec, rnd) return (c, fzero), (s, fzero) wp = prec + 6 c, s = mpf_cos_sin(a, wp) ch, sh = mpf_cosh_sinh(b, wp) cre = mpf_mul(c, ch, prec, rnd) cim = mpf_mul(s, sh, prec, rnd) sre = mpf_mul(s, ch, prec, rnd) sim = mpf_mul(c, sh, prec, rnd) return (cre, mpf_neg(cim)), (sre, sim) def mpc_cos_sin_pi(z, prec, rnd=round_fast): a, b = z if b == fzero: c, s = mpf_cos_sin_pi(a, prec, rnd) return (c, fzero), (s, fzero) b = mpf_mul(b, mpf_pi(prec+5), prec+5) if a == fzero: ch, sh = mpf_cosh_sinh(b, prec, rnd) return (ch, fzero), (fzero, sh) wp = prec + 6 c, s = mpf_cos_sin_pi(a, wp) ch, sh = mpf_cosh_sinh(b, wp) cre = mpf_mul(c, ch, prec, rnd) cim = mpf_mul(s, sh, prec, rnd) sre = mpf_mul(s, ch, prec, rnd) sim = mpf_mul(c, sh, prec, rnd) return (cre, mpf_neg(cim)), (sre, sim) def mpc_cosh(z, prec, rnd=round_fast): """Complex hyperbolic cosine. Computed as cosh(z) = cos(z*i).""" a, b = z return mpc_cos((b, mpf_neg(a)), prec, rnd) def mpc_sinh(z, prec, rnd=round_fast): """Complex hyperbolic sine. Computed as sinh(z) = -i*sin(z*i).""" a, b = z b, a = mpc_sin((b, a), prec, rnd) return a, b def mpc_tanh(z, prec, rnd=round_fast): """Complex hyperbolic tangent. Computed as tanh(z) = -i*tan(z*i).""" a, b = z b, a = mpc_tan((b, a), prec, rnd) return a, b # TODO: avoid loss of accuracy def mpc_atan(z, prec, rnd=round_fast): a, b = z # atan(z) = (I/2)*(log(1-I*z) - log(1+I*z)) # x = 1-I*z = 1 + b - I*a # y = 1+I*z = 1 - b + I*a wp = prec + 15 x = mpf_add(fone, b, wp), mpf_neg(a) y = mpf_sub(fone, b, wp), a l1 = mpc_log(x, wp) l2 = mpc_log(y, wp) a, b = mpc_sub(l1, l2, prec, rnd) # (I/2) * (a+b*I) = (-b/2 + a/2*I) v = mpf_neg(mpf_shift(b,-1)), mpf_shift(a,-1) # Subtraction at infinity gives correct real part but # wrong imaginary part (should be zero) if v[1] == fnan and mpc_is_inf(z): v = (v[0], fzero) return v beta_crossover = from_float(0.6417) alpha_crossover = from_float(1.5) def acos_asin(z, prec, rnd, n): """ complex acos for n = 0, asin for n = 1 The algorithm is described in T.E. Hull, T.F. Fairgrieve and P.T.P. Tang 'Implementing the Complex Arcsine and Arcosine Functions using Exception Handling', ACM Trans. on Math. Software Vol. 23 (1997), p299 The complex acos and asin can be defined as acos(z) = acos(beta) - I*sign(a)* log(alpha + sqrt(alpha**2 -1)) asin(z) = asin(beta) + I*sign(a)* log(alpha + sqrt(alpha**2 -1)) where z = a + I*b alpha = (1/2)*(r + s); beta = (1/2)*(r - s) = a/alpha r = sqrt((a+1)**2 + y**2); s = sqrt((a-1)**2 + y**2) These expressions are rewritten in different ways in different regions, delimited by two crossovers alpha_crossover and beta_crossover, and by abs(a) <= 1, in order to improve the numerical accuracy. """ a, b = z wp = prec + 10 # special cases with real argument if b == fzero: am = mpf_sub(fone, mpf_abs(a), wp) # case abs(a) <= 1 if not am[0]: if n == 0: return mpf_acos(a, prec, rnd), fzero else: return mpf_asin(a, prec, rnd), fzero # cases abs(a) > 1 else: # case a < -1 if a[0]: pi = mpf_pi(prec, rnd) c = mpf_acosh(mpf_neg(a), prec, rnd) if n == 0: return pi, mpf_neg(c) else: return mpf_neg(mpf_shift(pi, -1)), c # case a > 1 else: c = mpf_acosh(a, prec, rnd) if n == 0: return fzero, c else: pi = mpf_pi(prec, rnd) return mpf_shift(pi, -1), mpf_neg(c) asign = bsign = 0 if a[0]: a = mpf_neg(a) asign = 1 if b[0]: b = mpf_neg(b) bsign = 1 am = mpf_sub(fone, a, wp) ap = mpf_add(fone, a, wp) r = mpf_hypot(ap, b, wp) s = mpf_hypot(am, b, wp) alpha = mpf_shift(mpf_add(r, s, wp), -1) beta = mpf_div(a, alpha, wp) b2 = mpf_mul(b,b, wp) # case beta <= beta_crossover if not mpf_sub(beta_crossover, beta, wp)[0]: if n == 0: re = mpf_acos(beta, wp) else: re = mpf_asin(beta, wp) else: # to compute the real part in this region use the identity # asin(beta) = atan(beta/sqrt(1-beta**2)) # beta/sqrt(1-beta**2) = (alpha + a) * (alpha - a) # alpha + a is numerically accurate; alpha - a can have # cancellations leading to numerical inaccuracies, so rewrite # it in differente ways according to the region Ax = mpf_add(alpha, a, wp) # case a <= 1 if not am[0]: # c = b*b/(r + (a+1)); d = (s + (1-a)) # alpha - a = (1/2)*(c + d) # case n=0: re = atan(sqrt((1/2) * Ax * (c + d))/a) # case n=1: re = atan(a/sqrt((1/2) * Ax * (c + d))) c = mpf_div(b2, mpf_add(r, ap, wp), wp) d = mpf_add(s, am, wp) re = mpf_shift(mpf_mul(Ax, mpf_add(c, d, wp), wp), -1) if n == 0: re = mpf_atan(mpf_div(mpf_sqrt(re, wp), a, wp), wp) else: re = mpf_atan(mpf_div(a, mpf_sqrt(re, wp), wp), wp) else: # c = Ax/(r + (a+1)); d = Ax/(s - (1-a)) # alpha - a = (1/2)*(c + d) # case n = 0: re = atan(b*sqrt(c + d)/2/a) # case n = 1: re = atan(a/(b*sqrt(c + d)/2) c = mpf_div(Ax, mpf_add(r, ap, wp), wp) d = mpf_div(Ax, mpf_sub(s, am, wp), wp) re = mpf_shift(mpf_add(c, d, wp), -1) re = mpf_mul(b, mpf_sqrt(re, wp), wp) if n == 0: re = mpf_atan(mpf_div(re, a, wp), wp) else: re = mpf_atan(mpf_div(a, re, wp), wp) # to compute alpha + sqrt(alpha**2 - 1), if alpha <= alpha_crossover # replace it with 1 + Am1 + sqrt(Am1*(alpha+1))) # where Am1 = alpha -1 # if alpha <= alpha_crossover: if not mpf_sub(alpha_crossover, alpha, wp)[0]: c1 = mpf_div(b2, mpf_add(r, ap, wp), wp) # case a < 1 if mpf_neg(am)[0]: # Am1 = (1/2) * (b*b/(r + (a+1)) + b*b/(s + (1-a)) c2 = mpf_add(s, am, wp) c2 = mpf_div(b2, c2, wp) Am1 = mpf_shift(mpf_add(c1, c2, wp), -1) else: # Am1 = (1/2) * (b*b/(r + (a+1)) + (s - (1-a))) c2 = mpf_sub(s, am, wp) Am1 = mpf_shift(mpf_add(c1, c2, wp), -1) # im = log(1 + Am1 + sqrt(Am1*(alpha+1))) im = mpf_mul(Am1, mpf_add(alpha, fone, wp), wp) im = mpf_log(mpf_add(fone, mpf_add(Am1, mpf_sqrt(im, wp), wp), wp), wp) else: # im = log(alpha + sqrt(alpha*alpha - 1)) im = mpf_sqrt(mpf_sub(mpf_mul(alpha, alpha, wp), fone, wp), wp) im = mpf_log(mpf_add(alpha, im, wp), wp) if asign: if n == 0: re = mpf_sub(mpf_pi(wp), re, wp) else: re = mpf_neg(re) if not bsign and n == 0: im = mpf_neg(im) if bsign and n == 1: im = mpf_neg(im) re = normalize(re[0], re[1], re[2], re[3], prec, rnd) im = normalize(im[0], im[1], im[2], im[3], prec, rnd) return re, im def mpc_acos(z, prec, rnd=round_fast): return acos_asin(z, prec, rnd, 0) def mpc_asin(z, prec, rnd=round_fast): return acos_asin(z, prec, rnd, 1) def mpc_asinh(z, prec, rnd=round_fast): # asinh(z) = I * asin(-I z) a, b = z a, b = mpc_asin((b, mpf_neg(a)), prec, rnd) return mpf_neg(b), a def mpc_acosh(z, prec, rnd=round_fast): # acosh(z) = -I * acos(z) for Im(acos(z)) <= 0 # +I * acos(z) otherwise a, b = mpc_acos(z, prec, rnd) if b[0] or b == fzero: return mpf_neg(b), a else: return b, mpf_neg(a) def mpc_atanh(z, prec, rnd=round_fast): # atanh(z) = (log(1+z)-log(1-z))/2 wp = prec + 15 a = mpc_add(z, mpc_one, wp) b = mpc_sub(mpc_one, z, wp) a = mpc_log(a, wp) b = mpc_log(b, wp) v = mpc_shift(mpc_sub(a, b, wp), -1) # Subtraction at infinity gives correct imaginary part but # wrong real part (should be zero) if v[0] == fnan and mpc_is_inf(z): v = (fzero, v[1]) return v def mpc_fibonacci(z, prec, rnd=round_fast): re, im = z if im == fzero: return (mpf_fibonacci(re, prec, rnd), fzero) size = max(abs(re[2]+re[3]), abs(re[2]+re[3])) wp = prec + size + 20 a = mpf_phi(wp) b = mpf_add(mpf_shift(a, 1), fnone, wp) u = mpc_pow((a, fzero), z, wp) v = mpc_cos_pi(z, wp) v = mpc_div(v, u, wp) u = mpc_sub(u, v, wp) u = mpc_div_mpf(u, b, prec, rnd) return u def mpf_expj(x, prec, rnd='f'): raise ComplexResult def mpc_expj(z, prec, rnd='f'): re, im = z if im == fzero: return mpf_cos_sin(re, prec, rnd) if re == fzero: return mpf_exp(mpf_neg(im), prec, rnd), fzero ey = mpf_exp(mpf_neg(im), prec+10) c, s = mpf_cos_sin(re, prec+10) re = mpf_mul(ey, c, prec, rnd) im = mpf_mul(ey, s, prec, rnd) return re, im def mpf_expjpi(x, prec, rnd='f'): raise ComplexResult def mpc_expjpi(z, prec, rnd='f'): re, im = z if im == fzero: return mpf_cos_sin_pi(re, prec, rnd) sign, man, exp, bc = im wp = prec+10 if man: wp += max(0, exp+bc) im = mpf_neg(mpf_mul(mpf_pi(wp), im, wp)) if re == fzero: return mpf_exp(im, prec, rnd), fzero ey = mpf_exp(im, prec+10) c, s = mpf_cos_sin_pi(re, prec+10) re = mpf_mul(ey, c, prec, rnd) im = mpf_mul(ey, s, prec, rnd) return re, im if BACKEND == 'sage': try: import sage.libs.mpmath.ext_libmp as _lbmp mpc_exp = _lbmp.mpc_exp mpc_sqrt = _lbmp.mpc_sqrt except (ImportError, AttributeError): print("Warning: Sage imports in libmpc failed") sympy-0.7.4.1/sympy/mpmath/libmp/libelefun.py0000644000175000017500000012565212253362407021350 0ustar georgeskgeorgesk""" This module implements computation of elementary transcendental functions (powers, logarithms, trigonometric and hyperbolic functions, inverse trigonometric and hyperbolic) for real floating-point numbers. For complex and interval implementations of the same functions, see libmpc and libmpi. """ import math from bisect import bisect from .backend import xrange from .backend import MPZ, MPZ_ZERO, MPZ_ONE, MPZ_TWO, MPZ_FIVE, BACKEND from .libmpf import ( round_floor, round_ceiling, round_down, round_up, round_nearest, round_fast, ComplexResult, bitcount, bctable, lshift, rshift, giant_steps, sqrt_fixed, from_int, to_int, from_man_exp, to_fixed, to_float, from_float, from_rational, normalize, fzero, fone, fnone, fhalf, finf, fninf, fnan, mpf_cmp, mpf_sign, mpf_abs, mpf_pos, mpf_neg, mpf_add, mpf_sub, mpf_mul, mpf_div, mpf_shift, mpf_rdiv_int, mpf_pow_int, mpf_sqrt, reciprocal_rnd, negative_rnd, mpf_perturb, isqrt_fast ) from .libintmath import ifib #------------------------------------------------------------------------------- # Tuning parameters #------------------------------------------------------------------------------- # Cutoff for computing exp from cosh+sinh. This reduces the # number of terms by half, but also requires a square root which # is expensive with the pure-Python square root code. if BACKEND == 'python': EXP_COSH_CUTOFF = 600 else: EXP_COSH_CUTOFF = 400 # Cutoff for using more than 2 series EXP_SERIES_U_CUTOFF = 1500 # Also basically determined by sqrt if BACKEND == 'python': COS_SIN_CACHE_PREC = 400 else: COS_SIN_CACHE_PREC = 200 COS_SIN_CACHE_STEP = 8 cos_sin_cache = {} # Number of integer logarithms to cache (for zeta sums) MAX_LOG_INT_CACHE = 2000 log_int_cache = {} LOG_TAYLOR_PREC = 2500 # Use Taylor series with caching up to this prec LOG_TAYLOR_SHIFT = 9 # Cache log values in steps of size 2^-N log_taylor_cache = {} # prec/size ratio of x for fastest convergence in AGM formula LOG_AGM_MAG_PREC_RATIO = 20 ATAN_TAYLOR_PREC = 3000 # Same as for log ATAN_TAYLOR_SHIFT = 7 # steps of size 2^-N atan_taylor_cache = {} # ~= next power of two + 20 cache_prec_steps = [22,22] for k in xrange(1, bitcount(LOG_TAYLOR_PREC)+1): cache_prec_steps += [min(2**k,LOG_TAYLOR_PREC)+20] * 2**(k-1) #----------------------------------------------------------------------------# # # # Elementary mathematical constants # # # #----------------------------------------------------------------------------# def constant_memo(f): """ Decorator for caching computed values of mathematical constants. This decorator should be applied to a function taking a single argument prec as input and returning a fixed-point value with the given precision. """ f.memo_prec = -1 f.memo_val = None def g(prec, **kwargs): memo_prec = f.memo_prec if prec <= memo_prec: return f.memo_val >> (memo_prec-prec) newprec = int(prec*1.05+10) f.memo_val = f(newprec, **kwargs) f.memo_prec = newprec return f.memo_val >> (newprec-prec) g.__name__ = f.__name__ g.__doc__ = f.__doc__ return g def def_mpf_constant(fixed): """ Create a function that computes the mpf value for a mathematical constant, given a function that computes the fixed-point value. Assumptions: the constant is positive and has magnitude ~= 1; the fixed-point function rounds to floor. """ def f(prec, rnd=round_fast): wp = prec + 20 v = fixed(wp) if rnd in (round_up, round_ceiling): v += 1 return normalize(0, v, -wp, bitcount(v), prec, rnd) f.__doc__ = fixed.__doc__ return f def bsp_acot(q, a, b, hyperbolic): if b - a == 1: a1 = MPZ(2*a + 3) if hyperbolic or a&1: return MPZ_ONE, a1 * q**2, a1 else: return -MPZ_ONE, a1 * q**2, a1 m = (a+b)//2 p1, q1, r1 = bsp_acot(q, a, m, hyperbolic) p2, q2, r2 = bsp_acot(q, m, b, hyperbolic) return q2*p1 + r1*p2, q1*q2, r1*r2 # the acoth(x) series converges like the geometric series for x^2 # N = ceil(p*log(2)/(2*log(x))) def acot_fixed(a, prec, hyperbolic): """ Compute acot(a) or acoth(a) for an integer a with binary splitting; see http://numbers.computation.free.fr/Constants/Algorithms/splitting.html """ N = int(0.35 * prec/math.log(a) + 20) p, q, r = bsp_acot(a, 0,N, hyperbolic) return ((p+q)<> extraprec) # Logarithms of integers are needed for various computations involving # logarithms, powers, radix conversion, etc @constant_memo def ln2_fixed(prec): """ Computes ln(2). This is done with a hyperbolic Machin-type formula, with binary splitting at high precision. """ return machin([(18, 26), (-2, 4801), (8, 8749)], prec, True) @constant_memo def ln10_fixed(prec): """ Computes ln(10). This is done with a hyperbolic Machin-type formula. """ return machin([(46, 31), (34, 49), (20, 161)], prec, True) """ For computation of pi, we use the Chudnovsky series: oo ___ k 1 \ (-1) (6 k)! (A + B k) ----- = ) ----------------------- 12 pi /___ 3 3k+3/2 (3 k)! (k!) C k = 0 where A, B, and C are certain integer constants. This series adds roughly 14 digits per term. Note that C^(3/2) can be extracted so that the series contains only rational terms. This makes binary splitting very efficient. The recurrence formulas for the binary splitting were taken from ftp://ftp.gmplib.org/pub/src/gmp-chudnovsky.c Previously, Machin's formula was used at low precision and the AGM iteration was used at high precision. However, the Chudnovsky series is essentially as fast as the Machin formula at low precision and in practice about 3x faster than the AGM at high precision (despite theoretically having a worse asymptotic complexity), so there is no reason not to use it in all cases. """ # Constants in Chudnovsky's series CHUD_A = MPZ(13591409) CHUD_B = MPZ(545140134) CHUD_C = MPZ(640320) CHUD_D = MPZ(12) def bs_chudnovsky(a, b, level, verbose): """ Computes the sum from a to b of the series in the Chudnovsky formula. Returns g, p, q where p/q is the sum as an exact fraction and g is a temporary value used to save work for recursive calls. """ if b-a == 1: g = MPZ((6*b-5)*(2*b-1)*(6*b-1)) p = b**3 * CHUD_C**3 // 24 q = (-1)**b * g * (CHUD_A+CHUD_B*b) else: if verbose and level < 4: print(" binary splitting", a, b) mid = (a+b)//2 g1, p1, q1 = bs_chudnovsky(a, mid, level+1, verbose) g2, p2, q2 = bs_chudnovsky(mid, b, level+1, verbose) p = p1*p2 g = g1*g2 q = q1*p2 + q2*g1 return g, p, q @constant_memo def pi_fixed(prec, verbose=False, verbose_base=None): """ Compute floor(pi * 2**prec) as a big integer. This is done using Chudnovsky's series (see comments in libelefun.py for details). """ # The Chudnovsky series gives 14.18 digits per term N = int(prec/3.3219280948/14.181647462 + 2) if verbose: print("binary splitting with N =", N) g, p, q = bs_chudnovsky(0, N, 0, verbose) sqrtC = isqrt_fast(CHUD_C<<(2*prec)) v = p*CHUD_C*sqrtC//((q+CHUD_A*p)*CHUD_D) return v def degree_fixed(prec): return pi_fixed(prec)//180 def bspe(a, b): """ Sum series for exp(1)-1 between a, b, returning the result as an exact fraction (p, q). """ if b-a == 1: return MPZ_ONE, MPZ(b) m = (a+b)//2 p1, q1 = bspe(a, m) p2, q2 = bspe(m, b) return p1*q2+p2, q1*q2 @constant_memo def e_fixed(prec): """ Computes exp(1). This is done using the ordinary Taylor series for exp, with binary splitting. For a description of the algorithm, see: http://numbers.computation.free.fr/Constants/ Algorithms/splitting.html """ # Slight overestimate of N needed for 1/N! < 2**(-prec) # This could be tightened for large N. N = int(1.1*prec/math.log(prec) + 20) p, q = bspe(0,N) return ((p+q)<> 11 mpf_phi = def_mpf_constant(phi_fixed) mpf_pi = def_mpf_constant(pi_fixed) mpf_e = def_mpf_constant(e_fixed) mpf_degree = def_mpf_constant(degree_fixed) mpf_ln2 = def_mpf_constant(ln2_fixed) mpf_ln10 = def_mpf_constant(ln10_fixed) @constant_memo def ln_sqrt2pi_fixed(prec): wp = prec + 10 # ln(sqrt(2*pi)) = ln(2*pi)/2 return to_fixed(mpf_log(mpf_shift(mpf_pi(wp), 1), wp), prec-1) @constant_memo def sqrtpi_fixed(prec): return sqrt_fixed(pi_fixed(prec), prec) mpf_sqrtpi = def_mpf_constant(sqrtpi_fixed) mpf_ln_sqrt2pi = def_mpf_constant(ln_sqrt2pi_fixed) #----------------------------------------------------------------------------# # # # Powers # # # #----------------------------------------------------------------------------# def mpf_pow(s, t, prec, rnd=round_fast): """ Compute s**t. Raises ComplexResult if s is negative and t is fractional. """ ssign, sman, sexp, sbc = s tsign, tman, texp, tbc = t if ssign and texp < 0: raise ComplexResult("negative number raised to a fractional power") if texp >= 0: return mpf_pow_int(s, (-1)**tsign * (tman<> pbc)] if pbc > workprec: pm = pm >> (pbc-workprec) pe += pbc - workprec pbc = workprec n -= 1 if not n: break y = y*y exp = exp+exp bc = bc + bc - 2 bc = bc + bctable[int(y >> bc)] if bc > workprec: y = y >> (bc-workprec) exp += bc - workprec bc = workprec n = n // 2 return pm, pe # froot(s, n, prec, rnd) computes the real n-th root of a # positive mpf tuple s. # To compute the root we start from a 50-bit estimate for r # generated with ordinary floating-point arithmetic, and then refine # the value to full accuracy using the iteration # 1 / y \ # r = --- | (n-1) * r + ---------- | # n+1 n \ n r_n**(n-1) / # which is simply Newton's method applied to the equation r**n = y. # With giant_steps(start, prec+extra) = [p0,...,pm, prec+extra] # and y = man * 2**-shift one has # (man * 2**exp)**(1/n) = # y**(1/n) * 2**(start-prec/n) * 2**(p0-start) * ... * 2**(prec+extra-pm) * # 2**((exp+shift-(n-1)*prec)/n -extra)) # The last factor is accounted for in the last line of froot. def nthroot_fixed(y, n, prec, exp1): start = 50 try: y1 = rshift(y, prec - n*start) r = MPZ(int(y1**(1.0/n))) except OverflowError: y1 = from_int(y1, start) fn = from_int(n) fn = mpf_rdiv_int(1, fn, start) r = mpf_pow(y1, fn, start) r = to_int(r) extra = 10 extra1 = n prevp = start for p in giant_steps(start, prec+extra): pm, pe = int_pow_fixed(r, n-1, prevp) r2 = rshift(pm, (n-1)*prevp - p - pe - extra1) B = lshift(y, 2*p-prec+extra1)//r2 r = (B + (n-1) * lshift(r, p-prevp))//n prevp = p return r def mpf_nthroot(s, n, prec, rnd=round_fast): """nth-root of a positive number Use the Newton method when faster, otherwise use x**(1/n) """ sign, man, exp, bc = s if sign: raise ComplexResult("nth root of a negative number") if not man: if s == fnan: return fnan if s == fzero: if n > 0: return fzero if n == 0: return fone return finf # Infinity if not n: return fnan if n < 0: return fzero return finf flag_inverse = False if n < 2: if n == 0: return fone if n == 1: return mpf_pos(s, prec, rnd) if n == -1: return mpf_div(fone, s, prec, rnd) # n < 0 rnd = reciprocal_rnd[rnd] flag_inverse = True extra_inverse = 5 prec += extra_inverse n = -n if n > 20 and (n >= 20000 or prec < int(233 + 28.3 * n**0.62)): prec2 = prec + 10 fn = from_int(n) nth = mpf_rdiv_int(1, fn, prec2) r = mpf_pow(s, nth, prec2, rnd) s = normalize(r[0], r[1], r[2], r[3], prec, rnd) if flag_inverse: return mpf_div(fone, s, prec-extra_inverse, rnd) else: return s # Convert to a fixed-point number with prec2 bits. prec2 = prec + 2*n - (prec%n) # a few tests indicate that # for 10 < n < 10**4 a bit more precision is needed if n > 10: prec2 += prec2//10 prec2 = prec2 - prec2%n # Mantissa may have more bits than we need. Trim it down. shift = bc - prec2 # Adjust exponents to make prec2 and exp+shift multiples of n. sign1 = 0 es = exp+shift if es < 0: sign1 = 1 es = -es if sign1: shift += es%n else: shift -= es%n man = rshift(man, shift) extra = 10 exp1 = ((exp+shift-(n-1)*prec2)//n) - extra rnd_shift = 0 if flag_inverse: if rnd == 'u' or rnd == 'c': rnd_shift = 1 else: if rnd == 'd' or rnd == 'f': rnd_shift = 1 man = nthroot_fixed(man+rnd_shift, n, prec2, exp1) s = from_man_exp(man, exp1, prec, rnd) if flag_inverse: return mpf_div(fone, s, prec-extra_inverse, rnd) else: return s def mpf_cbrt(s, prec, rnd=round_fast): """cubic root of a positive number""" return mpf_nthroot(s, 3, prec, rnd) #----------------------------------------------------------------------------# # # # Logarithms # # # #----------------------------------------------------------------------------# def log_int_fixed(n, prec, ln2=None): """ Fast computation of log(n), caching the value for small n, intended for zeta sums. """ if n in log_int_cache: value, vprec = log_int_cache[n] if vprec >= prec: return value >> (vprec - prec) wp = prec + 10 if wp <= LOG_TAYLOR_SHIFT: if ln2 is None: ln2 = ln2_fixed(wp) r = bitcount(n) x = n << (wp-r) v = log_taylor_cached(x, wp) + r*ln2 else: v = to_fixed(mpf_log(from_int(n), wp+5), wp) if n < MAX_LOG_INT_CACHE: log_int_cache[n] = (v, wp) return v >> (wp-prec) def agm_fixed(a, b, prec): """ Fixed-point computation of agm(a,b), assuming a, b both close to unit magnitude. """ i = 0 while 1: anew = (a+b)>>1 if i > 4 and abs(a-anew) < 8: return a b = isqrt_fast(a*b) a = anew i += 1 return a def log_agm(x, prec): """ Fixed-point computation of -log(x) = log(1/x), suitable for large precision. It is required that 0 < x < 1. The algorithm used is the Sasaki-Kanada formula -log(x) = pi/agm(theta2(x)^2,theta3(x)^2). [1] For faster convergence in the theta functions, x should be chosen closer to 0. Guard bits must be added by the caller. HYPOTHESIS: if x = 2^(-n), n bits need to be added to account for the truncation to a fixed-point number, and this is the only significant cancellation error. The number of bits lost to roundoff is small and can be considered constant. [1] Richard P. Brent, "Fast Algorithms for High-Precision Computation of Elementary Functions (extended abstract)", http://wwwmaths.anu.edu.au/~brent/pd/RNC7-Brent.pdf """ x2 = (x*x) >> prec # Compute jtheta2(x)**2 s = a = b = x2 while a: b = (b*x2) >> prec a = (a*b) >> prec s += a s += (MPZ_ONE<>(prec-2) s = (s*isqrt_fast(x<>prec # Compute jtheta3(x)**2 t = a = b = x while a: b = (b*x2) >> prec a = (a*b) >> prec t += a t = (MPZ_ONE<>prec # Final formula p = agm_fixed(s, t, prec) return (pi_fixed(prec) << prec) // p def log_taylor(x, prec, r=0): """ Fixed-point calculation of log(x). It is assumed that x is close enough to 1 for the Taylor series to converge quickly. Convergence can be improved by specifying r > 0 to compute log(x^(1/2^r))*2^r, at the cost of performing r square roots. The caller must provide sufficient guard bits. """ for i in xrange(r): x = isqrt_fast(x<> prec v4 = (v2*v2) >> prec s0 = v s1 = v//3 v = (v*v4) >> prec k = 5 while v: s0 += v // k k += 2 s1 += v // k v = (v*v4) >> prec k += 2 s1 = (s1*v2) >> prec s = (s0+s1) << (1+r) if sign: return -s return s def log_taylor_cached(x, prec): """ Fixed-point computation of log(x), assuming x in (0.5, 2) and prec <= LOG_TAYLOR_PREC. """ n = x >> (prec-LOG_TAYLOR_SHIFT) cached_prec = cache_prec_steps[prec] dprec = cached_prec - prec if (n, cached_prec) in log_taylor_cache: a, log_a = log_taylor_cache[n, cached_prec] else: a = n << (cached_prec - LOG_TAYLOR_SHIFT) log_a = log_taylor(a, cached_prec, 8) log_taylor_cache[n, cached_prec] = (a, log_a) a >>= dprec log_a >>= dprec u = ((x - a) << prec) // a v = (u << prec) // ((MPZ_TWO << prec) + u) v2 = (v*v) >> prec v4 = (v2*v2) >> prec s0 = v s1 = v//3 v = (v*v4) >> prec k = 5 while v: s0 += v//k k += 2 s1 += v//k v = (v*v4) >> prec k += 2 s1 = (s1*v2) >> prec s = (s0+s1) << 1 return log_a + s def mpf_log(x, prec, rnd=round_fast): """ Compute the natural logarithm of the mpf value x. If x is negative, ComplexResult is raised. """ sign, man, exp, bc = x #------------------------------------------------------------------ # Handle special values if not man: if x == fzero: return fninf if x == finf: return finf if x == fnan: return fnan if sign: raise ComplexResult("logarithm of a negative number") wp = prec + 20 #------------------------------------------------------------------ # Handle log(2^n) = log(n)*2. # Here we catch the only possible exact value, log(1) = 0 if man == 1: if not exp: return fzero return from_man_exp(exp*ln2_fixed(wp), -wp, prec, rnd) mag = exp+bc abs_mag = abs(mag) #------------------------------------------------------------------ # Handle x = 1+eps, where log(x) ~ x. We need to check for # cancellation when moving to fixed-point math and compensate # by increasing the precision. Note that abs_mag in (0, 1) <=> # 0.5 < x < 2 and x != 1 if abs_mag <= 1: # Calculate t = x-1 to measure distance from 1 in bits tsign = 1-abs_mag if tsign: tman = (MPZ_ONE< wp: t = normalize(tsign, tman, abs_mag-bc, tbc, tbc, 'n') return mpf_perturb(t, tsign, prec, rnd) else: wp += cancellation # TODO: if close enough to 1, we could use Taylor series # even in the AGM precision range, since the Taylor series # converges rapidly #------------------------------------------------------------------ # Another special case: # n*log(2) is a good enough approximation if abs_mag > 10000: if bitcount(abs_mag) > wp: return from_man_exp(exp*ln2_fixed(wp), -wp, prec, rnd) #------------------------------------------------------------------ # General case. # Perform argument reduction using log(x) = log(x*2^n) - n*log(2): # If we are in the Taylor precision range, choose magnitude 0 or 1. # If we are in the AGM precision range, choose magnitude -m for # some large m; benchmarking on one machine showed m = prec/20 to be # optimal between 1000 and 100,000 digits. if wp <= LOG_TAYLOR_PREC: m = log_taylor_cached(lshift(man, wp-bc), wp) if mag: m += mag*ln2_fixed(wp) else: optimal_mag = -wp//LOG_AGM_MAG_PREC_RATIO n = optimal_mag - mag x = mpf_shift(x, n) wp += (-optimal_mag) m = -log_agm(to_fixed(x, wp), wp) m -= n*ln2_fixed(wp) return from_man_exp(m, -wp, prec, rnd) def mpf_log_hypot(a, b, prec, rnd): """ Computes log(sqrt(a^2+b^2)) accurately. """ # If either a or b is inf/nan/0, assume it to be a if not b[1]: a, b = b, a # a is inf/nan/0 if not a[1]: # both are inf/nan/0 if not b[1]: if a == b == fzero: return fninf if fnan in (a, b): return fnan # at least one term is (+/- inf)^2 return finf # only a is inf/nan/0 if a == fzero: # log(sqrt(0+b^2)) = log(|b|) return mpf_log(mpf_abs(b), prec, rnd) if a == fnan: return fnan return finf # Exact a2 = mpf_mul(a,a) b2 = mpf_mul(b,b) extra = 20 # Not exact h2 = mpf_add(a2, b2, prec+extra) cancelled = mpf_add(h2, fnone, 10) mag_cancelled = cancelled[2]+cancelled[3] # Just redo the sum exactly if necessary (could be smarter # and avoid memory allocation when a or b is precisely 1 # and the other is tiny...) if cancelled == fzero or mag_cancelled < -extra//2: h2 = mpf_add(a2, b2, prec+extra-min(a2[2],b2[2])) return mpf_shift(mpf_log(h2, prec, rnd), -1) #---------------------------------------------------------------------- # Inverse tangent # def atan_newton(x, prec): if prec >= 100: r = math.atan((x>>(prec-53))/2.0**53) else: r = math.atan(x/2.0**prec) prevp = 50 r = MPZ(int(r * 2.0**53) >> (53-prevp)) extra_p = 50 for wp in giant_steps(prevp, prec): wp += extra_p r = r << (wp-prevp) cos, sin = cos_sin_fixed(r, wp) tan = (sin << wp) // cos a = ((tan-rshift(x, prec-wp)) << wp) // ((MPZ_ONE<>wp)) r = r - a prevp = wp return rshift(r, prevp-prec) def atan_taylor_get_cached(n, prec): # Taylor series with caching wins up to huge precisions # To avoid unnecessary precomputation at low precision, we # do it in steps # Round to next power of 2 prec2 = (1<<(bitcount(prec-1))) + 20 dprec = prec2 - prec if (n, prec2) in atan_taylor_cache: a, atan_a = atan_taylor_cache[n, prec2] else: a = n << (prec2 - ATAN_TAYLOR_SHIFT) atan_a = atan_newton(a, prec2) atan_taylor_cache[n, prec2] = (a, atan_a) return (a >> dprec), (atan_a >> dprec) def atan_taylor(x, prec): n = (x >> (prec-ATAN_TAYLOR_SHIFT)) a, atan_a = atan_taylor_get_cached(n, prec) d = x - a s0 = v = (d << prec) // ((a**2 >> prec) + (a*d >> prec) + (MPZ_ONE << prec)) v2 = (v**2 >> prec) v4 = (v2 * v2) >> prec s1 = v//3 v = (v * v4) >> prec k = 5 while v: s0 += v // k k += 2 s1 += v // k v = (v * v4) >> prec k += 2 s1 = (s1 * v2) >> prec s = s0 - s1 return atan_a + s def atan_inf(sign, prec, rnd): if not sign: return mpf_shift(mpf_pi(prec, rnd), -1) return mpf_neg(mpf_shift(mpf_pi(prec, negative_rnd[rnd]), -1)) def mpf_atan(x, prec, rnd=round_fast): sign, man, exp, bc = x if not man: if x == fzero: return fzero if x == finf: return atan_inf(0, prec, rnd) if x == fninf: return atan_inf(1, prec, rnd) return fnan mag = exp + bc # Essentially infinity if mag > prec+20: return atan_inf(sign, prec, rnd) # Essentially ~ x if -mag > prec+20: return mpf_perturb(x, 1-sign, prec, rnd) wp = prec + 30 + abs(mag) # For large x, use atan(x) = pi/2 - atan(1/x) if mag >= 2: x = mpf_rdiv_int(1, x, wp) reciprocal = True else: reciprocal = False t = to_fixed(x, wp) if sign: t = -t if wp < ATAN_TAYLOR_PREC: a = atan_taylor(t, wp) else: a = atan_newton(t, wp) if reciprocal: a = ((pi_fixed(wp)>>1)+1) - a if sign: a = -a return from_man_exp(a, -wp, prec, rnd) # TODO: cleanup the special cases def mpf_atan2(y, x, prec, rnd=round_fast): xsign, xman, xexp, xbc = x ysign, yman, yexp, ybc = y if not yman: if y == fzero and x != fnan: if mpf_sign(x) >= 0: return fzero return mpf_pi(prec, rnd) if y in (finf, fninf): if x in (finf, fninf): return fnan # pi/2 if y == finf: return mpf_shift(mpf_pi(prec, rnd), -1) # -pi/2 return mpf_neg(mpf_shift(mpf_pi(prec, negative_rnd[rnd]), -1)) return fnan if ysign: return mpf_neg(mpf_atan2(mpf_neg(y), x, prec, negative_rnd[rnd])) if not xman: if x == fnan: return fnan if x == finf: return fzero if x == fninf: return mpf_pi(prec, rnd) if y == fzero: return fzero return mpf_shift(mpf_pi(prec, rnd), -1) tquo = mpf_atan(mpf_div(y, x, prec+4), prec+4) if xsign: return mpf_add(mpf_pi(prec+4), tquo, prec, rnd) else: return mpf_pos(tquo, prec, rnd) def mpf_asin(x, prec, rnd=round_fast): sign, man, exp, bc = x if bc+exp > 0 and x not in (fone, fnone): raise ComplexResult("asin(x) is real only for -1 <= x <= 1") # asin(x) = 2*atan(x/(1+sqrt(1-x**2))) wp = prec + 15 a = mpf_mul(x, x) b = mpf_add(fone, mpf_sqrt(mpf_sub(fone, a, wp), wp), wp) c = mpf_div(x, b, wp) return mpf_shift(mpf_atan(c, prec, rnd), 1) def mpf_acos(x, prec, rnd=round_fast): # acos(x) = 2*atan(sqrt(1-x**2)/(1+x)) sign, man, exp, bc = x if bc + exp > 0: if x not in (fone, fnone): raise ComplexResult("acos(x) is real only for -1 <= x <= 1") if x == fnone: return mpf_pi(prec, rnd) wp = prec + 15 a = mpf_mul(x, x) b = mpf_sqrt(mpf_sub(fone, a, wp), wp) c = mpf_div(b, mpf_add(fone, x, wp), wp) return mpf_shift(mpf_atan(c, prec, rnd), 1) def mpf_asinh(x, prec, rnd=round_fast): wp = prec + 20 sign, man, exp, bc = x mag = exp+bc if mag < -8: if mag < -wp: return mpf_perturb(x, 1-sign, prec, rnd) wp += (-mag) # asinh(x) = log(x+sqrt(x**2+1)) # use reflection symmetry to avoid cancellation q = mpf_sqrt(mpf_add(mpf_mul(x, x), fone, wp), wp) q = mpf_add(mpf_abs(x), q, wp) if sign: return mpf_neg(mpf_log(q, prec, negative_rnd[rnd])) else: return mpf_log(q, prec, rnd) def mpf_acosh(x, prec, rnd=round_fast): # acosh(x) = log(x+sqrt(x**2-1)) wp = prec + 15 if mpf_cmp(x, fone) == -1: raise ComplexResult("acosh(x) is real only for x >= 1") q = mpf_sqrt(mpf_add(mpf_mul(x,x), fnone, wp), wp) return mpf_log(mpf_add(x, q, wp), prec, rnd) def mpf_atanh(x, prec, rnd=round_fast): # atanh(x) = log((1+x)/(1-x))/2 sign, man, exp, bc = x if (not man) and exp: if x in (fzero, fnan): return x raise ComplexResult("atanh(x) is real only for -1 <= x <= 1") mag = bc + exp if mag > 0: if mag == 1 and man == 1: return [finf, fninf][sign] raise ComplexResult("atanh(x) is real only for -1 <= x <= 1") wp = prec + 15 if mag < -8: if mag < -wp: return mpf_perturb(x, sign, prec, rnd) wp += (-mag) a = mpf_add(x, fone, wp) b = mpf_sub(fone, x, wp) return mpf_shift(mpf_log(mpf_div(a, b, wp), prec, rnd), -1) def mpf_fibonacci(x, prec, rnd=round_fast): sign, man, exp, bc = x if not man: if x == fninf: return fnan return x # F(2^n) ~= 2^(2^n) size = abs(exp+bc) if exp >= 0: # Exact if size < 10 or size <= bitcount(prec): return from_int(ifib(to_int(x)), prec, rnd) # Use the modified Binet formula wp = prec + size + 20 a = mpf_phi(wp) b = mpf_add(mpf_shift(a, 1), fnone, wp) u = mpf_pow(a, x, wp) v = mpf_cos_pi(x, wp) v = mpf_div(v, u, wp) u = mpf_sub(u, v, wp) u = mpf_div(u, b, prec, rnd) return u #------------------------------------------------------------------------------- # Exponential-type functions #------------------------------------------------------------------------------- def exponential_series(x, prec, type=0): """ Taylor series for cosh/sinh or cos/sin. type = 0 -- returns exp(x) (slightly faster than cosh+sinh) type = 1 -- returns (cosh(x), sinh(x)) type = 2 -- returns (cos(x), sin(x)) """ if x < 0: x = -x sign = 1 else: sign = 0 r = int(0.5*prec**0.5) xmag = bitcount(x) - prec r = max(0, xmag + r) extra = 10 + 2*max(r,-xmag) wp = prec + extra x <<= (extra - r) one = MPZ_ONE << wp alt = (type == 2) if prec < EXP_SERIES_U_CUTOFF: x2 = a = (x*x) >> wp x4 = (x2*x2) >> wp s0 = s1 = MPZ_ZERO k = 2 while a: a //= (k-1)*k s0 += a k += 2 a //= (k-1)*k s1 += a k += 2 a = (a*x4) >> wp s1 = (x2*s1) >> wp if alt: c = s1 - s0 + one else: c = s1 + s0 + one else: u = int(0.3*prec**0.35) x2 = a = (x*x) >> wp xpowers = [one, x2] for i in xrange(1, u): xpowers.append((xpowers[-1]*x2)>>wp) sums = [MPZ_ZERO] * u k = 2 while a: for i in xrange(u): a //= (k-1)*k if alt and k & 2: sums[i] -= a else: sums[i] += a k += 2 a = (a*xpowers[-1]) >> wp for i in xrange(1, u): sums[i] = (sums[i]*xpowers[i]) >> wp c = sum(sums) + one if type == 0: s = isqrt_fast(c*c - (one<> wp return v >> extra else: # Repeatedly apply the double-angle formula # cosh(2*x) = 2*cosh(x)^2 - 1 # cos(2*x) = 2*cos(x)^2 - 1 pshift = wp-1 for i in xrange(r): c = ((c*c) >> pshift) - one # With the abs, this is the same for sinh and sin s = isqrt_fast(abs((one<>extra), (s>>extra) def exp_basecase(x, prec): """ Compute exp(x) as a fixed-point number. Works for any x, but for speed should have |x| < 1. For an arbitrary number, use exp(x) = exp(x-m*log(2)) * 2^m where m = floor(x/log(2)). """ if prec > EXP_COSH_CUTOFF: return exponential_series(x, prec, 0) r = int(prec**0.5) prec += r s0 = s1 = (MPZ_ONE << prec) k = 2 a = x2 = (x*x) >> prec while a: a //= k s0 += a k += 1 a //= k s1 += a k += 1 a = (a*x2) >> prec s1 = (s1*x) >> prec s = s0 + s1 u = r while r: s = (s*s) >> prec r -= 1 return s >> u def exp_expneg_basecase(x, prec): """ Computation of exp(x), exp(-x) """ if prec > EXP_COSH_CUTOFF: cosh, sinh = exponential_series(x, prec, 1) return cosh+sinh, cosh-sinh a = exp_basecase(x, prec) b = (MPZ_ONE << (prec+prec)) // a return a, b def cos_sin_basecase(x, prec): """ Compute cos(x), sin(x) as fixed-point numbers, assuming x in [0, pi/2). For an arbitrary number, use x' = x - m*(pi/2) where m = floor(x/(pi/2)) along with quarter-period symmetries. """ if prec > COS_SIN_CACHE_PREC: return exponential_series(x, prec, 2) precs = prec - COS_SIN_CACHE_STEP t = x >> precs n = int(t) if n not in cos_sin_cache: w = t<<(10+COS_SIN_CACHE_PREC-COS_SIN_CACHE_STEP) cos_t, sin_t = exponential_series(w, 10+COS_SIN_CACHE_PREC, 2) cos_sin_cache[n] = (cos_t>>10), (sin_t>>10) cos_t, sin_t = cos_sin_cache[n] offset = COS_SIN_CACHE_PREC - prec cos_t >>= offset sin_t >>= offset x -= t << precs cos = MPZ_ONE << prec sin = x k = 2 a = -((x*x) >> prec) while a: a //= k cos += a k += 1 a = (a*x) >> prec a //= k sin += a k += 1 a = -((a*x) >> prec) return ((cos*cos_t-sin*sin_t) >> prec), ((sin*cos_t+cos*sin_t) >> prec) def mpf_exp(x, prec, rnd=round_fast): sign, man, exp, bc = x if man: mag = bc + exp wp = prec + 14 if sign: man = -man # TODO: the best cutoff depends on both x and the precision. if prec > 600 and exp >= 0: # Need about log2(exp(n)) ~= 1.45*mag extra precision e = mpf_e(wp+int(1.45*mag)) return mpf_pow_int(e, man<= 2 if mag > 1: # For large arguments: exp(2^mag*(1+eps)) = # exp(2^mag)*exp(2^mag*eps) = exp(2^mag)*(1 + 2^mag*eps + ...) # so about mag extra bits is required. wpmod = wp + mag offset = exp + wpmod if offset >= 0: t = man << offset else: t = man >> (-offset) lg2 = ln2_fixed(wpmod) n, t = divmod(t, lg2) n = int(n) t >>= mag else: offset = exp + wp if offset >= 0: t = man << offset else: t = man >> (-offset) n = 0 man = exp_basecase(t, wp) return from_man_exp(man, n-wp, prec, rnd) if not exp: return fone if x == fninf: return fzero return x def mpf_cosh_sinh(x, prec, rnd=round_fast, tanh=0): """Simultaneously compute (cosh(x), sinh(x)) for real x""" sign, man, exp, bc = x if (not man) and exp: if tanh: if x == finf: return fone if x == fninf: return fnone return fnan if x == finf: return (finf, finf) if x == fninf: return (finf, fninf) return fnan, fnan mag = exp+bc wp = prec+14 if mag < -4: # Extremely close to 0, sinh(x) ~= x and cosh(x) ~= 1 if mag < -wp: if tanh: return mpf_perturb(x, 1-sign, prec, rnd) cosh = mpf_perturb(fone, 0, prec, rnd) sinh = mpf_perturb(x, sign, prec, rnd) return cosh, sinh # Fix for cancellation when computing sinh wp += (-mag) # Does exp(-2*x) vanish? if mag > 10: if 3*(1<<(mag-1)) > wp: # XXX: rounding if tanh: return mpf_perturb([fone,fnone][sign], 1-sign, prec, rnd) c = s = mpf_shift(mpf_exp(mpf_abs(x), prec, rnd), -1) if sign: s = mpf_neg(s) return c, s # |x| > 1 if mag > 1: wpmod = wp + mag offset = exp + wpmod if offset >= 0: t = man << offset else: t = man >> (-offset) lg2 = ln2_fixed(wpmod) n, t = divmod(t, lg2) n = int(n) t >>= mag else: offset = exp + wp if offset >= 0: t = man << offset else: t = man >> (-offset) n = 0 a, b = exp_expneg_basecase(t, wp) # TODO: optimize division precision cosh = a + (b>>(2*n)) sinh = a - (b>>(2*n)) if sign: sinh = -sinh if tanh: man = (sinh << wp) // cosh return from_man_exp(man, -wp, prec, rnd) else: cosh = from_man_exp(cosh, n-wp-1, prec, rnd) sinh = from_man_exp(sinh, n-wp-1, prec, rnd) return cosh, sinh def mod_pi2(man, exp, mag, wp): # Reduce to standard interval if mag > 0: i = 0 while 1: cancellation_prec = 20 << i wpmod = wp + mag + cancellation_prec pi2 = pi_fixed(wpmod-1) pi4 = pi2 >> 1 offset = wpmod + exp if offset >= 0: t = man << offset else: t = man >> (-offset) n, y = divmod(t, pi2) if y > pi4: small = pi2 - y else: small = y if small >> (wp+mag-10): n = int(n) t = y >> mag wp = wpmod - mag break i += 1 else: wp += (-mag) offset = exp + wp if offset >= 0: t = man << offset else: t = man >> (-offset) n = 0 return t, n, wp def mpf_cos_sin(x, prec, rnd=round_fast, which=0, pi=False): """ which: 0 -- return cos(x), sin(x) 1 -- return cos(x) 2 -- return sin(x) 3 -- return tan(x) if pi=True, compute for pi*x """ sign, man, exp, bc = x if not man: if exp: c, s = fnan, fnan else: c, s = fone, fzero if which == 0: return c, s if which == 1: return c if which == 2: return s if which == 3: return s mag = bc + exp wp = prec + 10 # Extremely small? if mag < 0: if mag < -wp: if pi: x = mpf_mul(x, mpf_pi(wp)) c = mpf_perturb(fone, 1, prec, rnd) s = mpf_perturb(x, 1-sign, prec, rnd) if which == 0: return c, s if which == 1: return c if which == 2: return s if which == 3: return mpf_perturb(x, sign, prec, rnd) if pi: if exp >= -1: if exp == -1: c = fzero s = (fone, fnone)[bool(man & 2) ^ sign] elif exp == 0: c, s = (fnone, fzero) else: c, s = (fone, fzero) if which == 0: return c, s if which == 1: return c if which == 2: return s if which == 3: return mpf_div(s, c, prec, rnd) # Subtract nearest half-integer (= mod by pi/2) n = ((man >> (-exp-2)) + 1) >> 1 man = man - (n << (-exp-1)) mag2 = bitcount(man) + exp wp = prec + 10 - mag2 offset = exp + wp if offset >= 0: t = man << offset else: t = man >> (-offset) t = (t*pi_fixed(wp)) >> wp else: t, n, wp = mod_pi2(man, exp, mag, wp) c, s = cos_sin_basecase(t, wp) m = n & 3 if m == 1: c, s = -s, c elif m == 2: c, s = -c, -s elif m == 3: c, s = s, -c if sign: s = -s if which == 0: c = from_man_exp(c, -wp, prec, rnd) s = from_man_exp(s, -wp, prec, rnd) return c, s if which == 1: return from_man_exp(c, -wp, prec, rnd) if which == 2: return from_man_exp(s, -wp, prec, rnd) if which == 3: return from_rational(s, c, prec, rnd) def mpf_cos(x, prec, rnd=round_fast): return mpf_cos_sin(x, prec, rnd, 1) def mpf_sin(x, prec, rnd=round_fast): return mpf_cos_sin(x, prec, rnd, 2) def mpf_tan(x, prec, rnd=round_fast): return mpf_cos_sin(x, prec, rnd, 3) def mpf_cos_sin_pi(x, prec, rnd=round_fast): return mpf_cos_sin(x, prec, rnd, 0, 1) def mpf_cos_pi(x, prec, rnd=round_fast): return mpf_cos_sin(x, prec, rnd, 1, 1) def mpf_sin_pi(x, prec, rnd=round_fast): return mpf_cos_sin(x, prec, rnd, 2, 1) def mpf_cosh(x, prec, rnd=round_fast): return mpf_cosh_sinh(x, prec, rnd)[0] def mpf_sinh(x, prec, rnd=round_fast): return mpf_cosh_sinh(x, prec, rnd)[1] def mpf_tanh(x, prec, rnd=round_fast): return mpf_cosh_sinh(x, prec, rnd, tanh=1) # Low-overhead fixed-point versions def cos_sin_fixed(x, prec, pi2=None): if pi2 is None: pi2 = pi_fixed(prec-1) n, t = divmod(x, pi2) n = int(n) c, s = cos_sin_basecase(t, prec) m = n & 3 if m == 0: return c, s if m == 1: return -s, c if m == 2: return -c, -s if m == 3: return s, -c def exp_fixed(x, prec, ln2=None): if ln2 is None: ln2 = ln2_fixed(prec) n, t = divmod(x, ln2) n = int(n) v = exp_basecase(t, prec) if n >= 0: return v << n else: return v >> (-n) if BACKEND == 'sage': try: import sage.libs.mpmath.ext_libmp as _lbmp mpf_sqrt = _lbmp.mpf_sqrt mpf_exp = _lbmp.mpf_exp mpf_log = _lbmp.mpf_log mpf_cos = _lbmp.mpf_cos mpf_sin = _lbmp.mpf_sin mpf_pow = _lbmp.mpf_pow exp_fixed = _lbmp.exp_fixed cos_sin_fixed = _lbmp.cos_sin_fixed log_int_fixed = _lbmp.log_int_fixed except (ImportError, AttributeError): print("Warning: Sage imports in libelefun failed") sympy-0.7.4.1/sympy/mpmath/libmp/__init__.py0000644000175000017500000000734212253362407021135 0ustar georgeskgeorgeskfrom .libmpf import (prec_to_dps, dps_to_prec, repr_dps, round_down, round_up, round_floor, round_ceiling, round_nearest, to_pickable, from_pickable, ComplexResult, fzero, fnzero, fone, fnone, ftwo, ften, fhalf, fnan, finf, fninf, math_float_inf, round_int, normalize, normalize1, from_man_exp, from_int, to_man_exp, to_int, mpf_ceil, mpf_floor, mpf_nint, mpf_frac, from_float, to_float, from_rational, to_rational, to_fixed, mpf_rand, mpf_eq, mpf_hash, mpf_cmp, mpf_lt, mpf_le, mpf_gt, mpf_ge, mpf_pos, mpf_neg, mpf_abs, mpf_sign, mpf_add, mpf_sub, mpf_sum, mpf_mul, mpf_mul_int, mpf_shift, mpf_frexp, mpf_div, mpf_rdiv_int, mpf_mod, mpf_pow_int, mpf_perturb, to_digits_exp, to_str, str_to_man_exp, from_str, from_bstr, to_bstr, mpf_sqrt, mpf_hypot) from .libmpc import (mpc_one, mpc_zero, mpc_two, mpc_half, mpc_is_inf, mpc_is_infnan, mpc_to_str, mpc_to_complex, mpc_hash, mpc_conjugate, mpc_is_nonzero, mpc_add, mpc_add_mpf, mpc_sub, mpc_sub_mpf, mpc_pos, mpc_neg, mpc_shift, mpc_abs, mpc_arg, mpc_floor, mpc_ceil, mpc_nint, mpc_frac, mpc_mul, mpc_square, mpc_mul_mpf, mpc_mul_imag_mpf, mpc_mul_int, mpc_div, mpc_div_mpf, mpc_reciprocal, mpc_mpf_div, complex_int_pow, mpc_pow, mpc_pow_mpf, mpc_pow_int, mpc_sqrt, mpc_nthroot, mpc_cbrt, mpc_exp, mpc_log, mpc_cos, mpc_sin, mpc_tan, mpc_cos_pi, mpc_sin_pi, mpc_cosh, mpc_sinh, mpc_tanh, mpc_atan, mpc_acos, mpc_asin, mpc_asinh, mpc_acosh, mpc_atanh, mpc_fibonacci, mpf_expj, mpf_expjpi, mpc_expj, mpc_expjpi, mpc_cos_sin, mpc_cos_sin_pi) from .libelefun import (ln2_fixed, mpf_ln2, ln10_fixed, mpf_ln10, pi_fixed, mpf_pi, e_fixed, mpf_e, phi_fixed, mpf_phi, degree_fixed, mpf_degree, mpf_pow, mpf_nthroot, mpf_cbrt, log_int_fixed, agm_fixed, mpf_log, mpf_log_hypot, mpf_exp, mpf_cos_sin, mpf_cos, mpf_sin, mpf_tan, mpf_cos_sin_pi, mpf_cos_pi, mpf_sin_pi, mpf_cosh_sinh, mpf_cosh, mpf_sinh, mpf_tanh, mpf_atan, mpf_atan2, mpf_asin, mpf_acos, mpf_asinh, mpf_acosh, mpf_atanh, mpf_fibonacci) from .libhyper import (NoConvergence, make_hyp_summator, mpf_erf, mpf_erfc, mpf_ei, mpc_ei, mpf_e1, mpc_e1, mpf_expint, mpf_ci_si, mpf_ci, mpf_si, mpc_ci, mpc_si, mpf_besseljn, mpc_besseljn, mpf_agm, mpf_agm1, mpc_agm, mpc_agm1, mpf_ellipk, mpc_ellipk, mpf_ellipe, mpc_ellipe) from .gammazeta import (catalan_fixed, mpf_catalan, khinchin_fixed, mpf_khinchin, glaisher_fixed, mpf_glaisher, apery_fixed, mpf_apery, euler_fixed, mpf_euler, mertens_fixed, mpf_mertens, twinprime_fixed, mpf_twinprime, mpf_bernoulli, bernfrac, mpf_gamma_int, mpf_factorial, mpc_factorial, mpf_gamma, mpc_gamma, mpf_loggamma, mpc_loggamma, mpf_rgamma, mpc_rgamma, mpf_gamma_old, mpc_gamma_old, mpf_factorial_old, mpc_factorial_old, mpf_harmonic, mpc_harmonic, mpf_psi0, mpc_psi0, mpf_psi, mpc_psi, mpf_zeta_int, mpf_zeta, mpc_zeta, mpf_altzeta, mpc_altzeta, mpf_zetasum, mpc_zetasum) from .libmpi import (mpi_str, mpi_from_str, mpi_to_str, mpi_eq, mpi_ne, mpi_lt, mpi_le, mpi_gt, mpi_ge, mpi_add, mpi_sub, mpi_delta, mpi_mid, mpi_pos, mpi_neg, mpi_abs, mpi_mul, mpi_div, mpi_exp, mpi_log, mpi_sqrt, mpi_pow_int, mpi_pow, mpi_cos_sin, mpi_cos, mpi_sin, mpi_tan, mpi_cot, mpi_atan, mpi_atan2, mpci_pos, mpci_neg, mpci_add, mpci_sub, mpci_mul, mpci_div, mpci_pow, mpci_abs, mpci_pow, mpci_exp, mpci_log, mpci_cos, mpci_sin, mpi_gamma, mpci_gamma, mpi_loggamma, mpci_loggamma, mpi_rgamma, mpci_rgamma, mpi_factorial, mpci_factorial) from .libintmath import (trailing, bitcount, numeral, bin_to_radix, isqrt, isqrt_small, isqrt_fast, sqrt_fixed, sqrtrem, ifib, ifac, list_primes, isprime, moebius, gcd, eulernum) from .backend import (gmpy, sage, BACKEND, STRICT, MPZ, MPZ_TYPE, MPZ_ZERO, MPZ_ONE, MPZ_TWO, MPZ_THREE, MPZ_FIVE, int_types, HASH_MODULUS, HASH_BITS) sympy-0.7.4.1/sympy/mpmath/calculus/0000755000175000017500000000000012253362407017526 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/mpmath/calculus/extrapolation.py0000644000175000017500000013236312253362407023001 0ustar georgeskgeorgesktry: from itertools import izip except ImportError: izip = zip from ..libmp.backend import xrange from .calculus import defun try: next = next except NameError: next = lambda _: _.next() @defun def richardson(ctx, seq): r""" Given a list ``seq`` of the first `N` elements of a slowly convergent infinite sequence, :func:`~mpmath.richardson` computes the `N`-term Richardson extrapolate for the limit. :func:`~mpmath.richardson` returns `(v, c)` where `v` is the estimated limit and `c` is the magnitude of the largest weight used during the computation. The weight provides an estimate of the precision lost to cancellation. Due to cancellation effects, the sequence must be typically be computed at a much higher precision than the target accuracy of the extrapolation. **Applicability and issues** The `N`-step Richardson extrapolation algorithm used by :func:`~mpmath.richardson` is described in [1]. Richardson extrapolation only works for a specific type of sequence, namely one converging like partial sums of `P(1)/Q(1) + P(2)/Q(2) + \ldots` where `P` and `Q` are polynomials. When the sequence does not convergence at such a rate :func:`~mpmath.richardson` generally produces garbage. Richardson extrapolation has the advantage of being fast: the `N`-term extrapolate requires only `O(N)` arithmetic operations, and usually produces an estimate that is accurate to `O(N)` digits. Contrast with the Shanks transformation (see :func:`~mpmath.shanks`), which requires `O(N^2)` operations. :func:`~mpmath.richardson` is unable to produce an estimate for the approximation error. One way to estimate the error is to perform two extrapolations with slightly different `N` and comparing the results. Richardson extrapolation does not work for oscillating sequences. As a simple workaround, :func:`~mpmath.richardson` detects if the last three elements do not differ monotonically, and in that case applies extrapolation only to the even-index elements. **Examples** Applying Richardson extrapolation to the Leibniz series for `\pi`:: >>> from mpmath import * >>> mp.dps = 30; mp.pretty = True >>> S = [4*sum(mpf(-1)**n/(2*n+1) for n in range(m)) ... for m in range(1,30)] >>> v, c = richardson(S[:10]) >>> v 3.2126984126984126984126984127 >>> nprint([v-pi, c]) [0.0711058, 2.0] >>> v, c = richardson(S[:30]) >>> v 3.14159265468624052829954206226 >>> nprint([v-pi, c]) [1.09645e-9, 20833.3] **References** 1. [BenderOrszag]_ pp. 375-376 """ assert len(seq) >= 3 if ctx.sign(seq[-1]-seq[-2]) != ctx.sign(seq[-2]-seq[-3]): seq = seq[::2] N = len(seq)//2-1 s = ctx.zero # The general weight is c[k] = (N+k)**N * (-1)**(k+N) / k! / (N-k)! # To avoid repeated factorials, we simplify the quotient # of successive weights to obtain a recurrence relation c = (-1)**N * N**N / ctx.mpf(ctx._ifac(N)) maxc = 1 for k in xrange(N+1): s += c * seq[N+k] maxc = max(abs(c), maxc) c *= (k-N)*ctx.mpf(k+N+1)**N c /= ((1+k)*ctx.mpf(k+N)**N) return s, maxc @defun def shanks(ctx, seq, table=None, randomized=False): r""" Given a list ``seq`` of the first `N` elements of a slowly convergent infinite sequence `(A_k)`, :func:`~mpmath.shanks` computes the iterated Shanks transformation `S(A), S(S(A)), \ldots, S^{N/2}(A)`. The Shanks transformation often provides strong convergence acceleration, especially if the sequence is oscillating. The iterated Shanks transformation is computed using the Wynn epsilon algorithm (see [1]). :func:`~mpmath.shanks` returns the full epsilon table generated by Wynn's algorithm, which can be read off as follows: * The table is a list of lists forming a lower triangular matrix, where higher row and column indices correspond to more accurate values. * The columns with even index hold dummy entries (required for the computation) and the columns with odd index hold the actual extrapolates. * The last element in the last row is typically the most accurate estimate of the limit. * The difference to the third last element in the last row provides an estimate of the approximation error. * The magnitude of the second last element provides an estimate of the numerical accuracy lost to cancellation. For convenience, so the extrapolation is stopped at an odd index so that ``shanks(seq)[-1][-1]`` always gives an estimate of the limit. Optionally, an existing table can be passed to :func:`~mpmath.shanks`. This can be used to efficiently extend a previous computation after new elements have been appended to the sequence. The table will then be updated in-place. **The Shanks transformation** The Shanks transformation is defined as follows (see [2]): given the input sequence `(A_0, A_1, \ldots)`, the transformed sequence is given by .. math :: S(A_k) = \frac{A_{k+1}A_{k-1}-A_k^2}{A_{k+1}+A_{k-1}-2 A_k} The Shanks transformation gives the exact limit `A_{\infty}` in a single step if `A_k = A + a q^k`. Note in particular that it extrapolates the exact sum of a geometric series in a single step. Applying the Shanks transformation once often improves convergence substantially for an arbitrary sequence, but the optimal effect is obtained by applying it iteratively: `S(S(A_k)), S(S(S(A_k))), \ldots`. Wynn's epsilon algorithm provides an efficient way to generate the table of iterated Shanks transformations. It reduces the computation of each element to essentially a single division, at the cost of requiring dummy elements in the table. See [1] for details. **Precision issues** Due to cancellation effects, the sequence must be typically be computed at a much higher precision than the target accuracy of the extrapolation. If the Shanks transformation converges to the exact limit (such as if the sequence is a geometric series), then a division by zero occurs. By default, :func:`~mpmath.shanks` handles this case by terminating the iteration and returning the table it has generated so far. With *randomized=True*, it will instead replace the zero by a pseudorandom number close to zero. (TODO: find a better solution to this problem.) **Examples** We illustrate by applying Shanks transformation to the Leibniz series for `\pi`:: >>> from mpmath import * >>> mp.dps = 50 >>> S = [4*sum(mpf(-1)**n/(2*n+1) for n in range(m)) ... for m in range(1,30)] >>> >>> T = shanks(S[:7]) >>> for row in T: ... nprint(row) ... [-0.75] [1.25, 3.16667] [-1.75, 3.13333, -28.75] [2.25, 3.14524, 82.25, 3.14234] [-2.75, 3.13968, -177.75, 3.14139, -969.937] [3.25, 3.14271, 327.25, 3.14166, 3515.06, 3.14161] The extrapolated accuracy is about 4 digits, and about 4 digits may have been lost due to cancellation:: >>> L = T[-1] >>> nprint([abs(L[-1] - pi), abs(L[-1] - L[-3]), abs(L[-2])]) [2.22532e-5, 4.78309e-5, 3515.06] Now we extend the computation:: >>> T = shanks(S[:25], T) >>> L = T[-1] >>> nprint([abs(L[-1] - pi), abs(L[-1] - L[-3]), abs(L[-2])]) [3.75527e-19, 1.48478e-19, 2.96014e+17] The value for pi is now accurate to 18 digits. About 18 digits may also have been lost to cancellation. Here is an example with a geometric series, where the convergence is immediate (the sum is exactly 1):: >>> mp.dps = 15 >>> for row in shanks([0.5, 0.75, 0.875, 0.9375, 0.96875]): ... nprint(row) [4.0] [8.0, 1.0] **References** 1. [GravesMorris]_ 2. [BenderOrszag]_ pp. 368-375 """ assert len(seq) >= 2 if table: START = len(table) else: START = 0 table = [] STOP = len(seq) - 1 if STOP & 1: STOP -= 1 one = ctx.one eps = +ctx.eps if randomized: from random import Random rnd = Random() rnd.seed(START) for i in xrange(START, STOP): row = [] for j in xrange(i+1): if j == 0: a, b = 0, seq[i+1]-seq[i] else: if j == 1: a = seq[i] else: a = table[i-1][j-2] b = row[j-1] - table[i-1][j-1] if not b: if randomized: b = rnd.getrandbits(10)*eps elif i & 1: return table[:-1] else: return table row.append(a + one/b) table.append(row) return table @defun def sumap(ctx, f, interval, integral=None, error=False): r""" Evaluates an infinite series of an analytic summand *f* using the Abel-Plana formula .. math :: \sum_{k=0}^{\infty} f(k) = \int_0^{\infty} f(t) dt + \frac{1}{2} f(0) + i \int_0^{\infty} \frac{f(it)-f(-it)}{e^{2\pi t}-1} dt. Unlike the Euler-Maclaurin formula (see :func:`~mpmath.sumem`), the Abel-Plana formula does not require derivatives. However, it only works when `|f(it)-f(-it)|` does not increase too rapidly with `t`. **Examples** The Abel-Plana formula is particularly useful when the summand decreases like a power of `k`; for example when the sum is a pure zeta function:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> sumap(lambda k: 1/k**2.5, [1,inf]) 1.34148725725091717975677 >>> zeta(2.5) 1.34148725725091717975677 >>> sumap(lambda k: 1/(k+1j)**(2.5+2.5j), [1,inf]) (-3.385361068546473342286084 - 0.7432082105196321803869551j) >>> zeta(2.5+2.5j, 1+1j) (-3.385361068546473342286084 - 0.7432082105196321803869551j) If the series is alternating, numerical quadrature along the real line is likely to give poor results, so it is better to evaluate the first term symbolically whenever possible: >>> n=3; z=-0.75 >>> I = expint(n,-log(z)) >>> chop(sumap(lambda k: z**k / k**n, [1,inf], integral=I)) -0.6917036036904594510141448 >>> polylog(n,z) -0.6917036036904594510141448 """ prec = ctx.prec try: ctx.prec += 10 a, b = interval assert b == ctx.inf g = lambda x: f(x+a) if integral is None: i1, err1 = ctx.quad(g, [0,ctx.inf], error=True) else: i1, err1 = integral, 0 j = ctx.j p = ctx.pi * 2 if ctx._is_real_type(i1): h = lambda t: -2 * ctx.im(g(j*t)) / ctx.expm1(p*t) else: h = lambda t: j*(g(j*t)-g(-j*t)) / ctx.expm1(p*t) i2, err2 = ctx.quad(h, [0,ctx.inf], error=True) err = err1+err2 v = i1+i2+0.5*g(ctx.mpf(0)) finally: ctx.prec = prec if error: return +v, err return +v @defun def sumem(ctx, f, interval, tol=None, reject=10, integral=None, adiffs=None, bdiffs=None, verbose=False, error=False, _fast_abort=False): r""" Uses the Euler-Maclaurin formula to compute an approximation accurate to within ``tol`` (which defaults to the present epsilon) of the sum .. math :: S = \sum_{k=a}^b f(k) where `(a,b)` are given by ``interval`` and `a` or `b` may be infinite. The approximation is .. math :: S \sim \int_a^b f(x) \,dx + \frac{f(a)+f(b)}{2} + \sum_{k=1}^{\infty} \frac{B_{2k}}{(2k)!} \left(f^{(2k-1)}(b)-f^{(2k-1)}(a)\right). The last sum in the Euler-Maclaurin formula is not generally convergent (a notable exception is if `f` is a polynomial, in which case Euler-Maclaurin actually gives an exact result). The summation is stopped as soon as the quotient between two consecutive terms falls below *reject*. That is, by default (*reject* = 10), the summation is continued as long as each term adds at least one decimal. Although not convergent, convergence to a given tolerance can often be "forced" if `b = \infty` by summing up to `a+N` and then applying the Euler-Maclaurin formula to the sum over the range `(a+N+1, \ldots, \infty)`. This procedure is implemented by :func:`~mpmath.nsum`. By default numerical quadrature and differentiation is used. If the symbolic values of the integral and endpoint derivatives are known, it is more efficient to pass the value of the integral explicitly as ``integral`` and the derivatives explicitly as ``adiffs`` and ``bdiffs``. The derivatives should be given as iterables that yield `f(a), f'(a), f''(a), \ldots` (and the equivalent for `b`). **Examples** Summation of an infinite series, with automatic and symbolic integral and derivative values (the second should be much faster):: >>> from mpmath import * >>> mp.dps = 50; mp.pretty = True >>> sumem(lambda n: 1/n**2, [32, inf]) 0.03174336652030209012658168043874142714132886413417 >>> I = mpf(1)/32 >>> D = adiffs=((-1)**n*fac(n+1)*32**(-2-n) for n in range(999)) >>> sumem(lambda n: 1/n**2, [32, inf], integral=I, adiffs=D) 0.03174336652030209012658168043874142714132886413417 An exact evaluation of a finite polynomial sum:: >>> sumem(lambda n: n**5-12*n**2+3*n, [-100000, 200000]) 10500155000624963999742499550000.0 >>> print(sum(n**5-12*n**2+3*n for n in range(-100000, 200001))) 10500155000624963999742499550000 """ tol = tol or +ctx.eps interval = ctx._as_points(interval) a = ctx.convert(interval[0]) b = ctx.convert(interval[-1]) err = ctx.zero prev = 0 M = 10000 if a == ctx.ninf: adiffs = (0 for n in xrange(M)) else: adiffs = adiffs or ctx.diffs(f, a) if b == ctx.inf: bdiffs = (0 for n in xrange(M)) else: bdiffs = bdiffs or ctx.diffs(f, b) orig = ctx.prec #verbose = 1 try: ctx.prec += 10 s = ctx.zero for k, (da, db) in enumerate(izip(adiffs, bdiffs)): if k & 1: term = (db-da) * ctx.bernoulli(k+1) / ctx.factorial(k+1) mag = abs(term) if verbose: print("term", k, "magnitude =", ctx.nstr(mag)) if k > 4 and mag < tol: s += term break elif k > 4 and abs(prev) / mag < reject: err += mag if _fast_abort: return [s, (s, err)][error] if verbose: print("Failed to converge") break else: s += term prev = term # Endpoint correction if a != ctx.ninf: s += f(a)/2 if b != ctx.inf: s += f(b)/2 # Tail integral if verbose: print("Integrating f(x) from x = %s to %s" % (ctx.nstr(a), ctx.nstr(b))) if integral: s += integral else: integral, ierr = ctx.quad(f, interval, error=True) if verbose: print("Integration error:", ierr) s += integral err += ierr finally: ctx.prec = orig if error: return s, err else: return s @defun def adaptive_extrapolation(ctx, update, emfun, kwargs): option = kwargs.get if ctx._fixed_precision: tol = option('tol', ctx.eps*2**10) else: tol = option('tol', ctx.eps/2**10) verbose = option('verbose', False) maxterms = option('maxterms', ctx.dps*10) method = option('method', 'r+s').split('+') skip = option('skip', 0) steps = iter(option('steps', xrange(10, 10**9, 10))) strict = option('strict') #steps = (10 for i in xrange(1000)) if 'd' in method or 'direct' in method: TRY_RICHARDSON = TRY_SHANKS = TRY_EULER_MACLAURIN = False else: TRY_RICHARDSON = ('r' in method) or ('richardson' in method) TRY_SHANKS = ('s' in method) or ('shanks' in method) TRY_EULER_MACLAURIN = ('e' in method) or \ ('euler-maclaurin' in method) last_richardson_value = 0 shanks_table = [] index = 0 step = 10 partial = [] best = ctx.zero orig = ctx.prec try: if 'workprec' in kwargs: ctx.prec = kwargs['workprec'] elif TRY_RICHARDSON or TRY_SHANKS: ctx.prec = (ctx.prec+10) * 4 else: ctx.prec += 30 while 1: if index >= maxterms: break # Get new batch of terms try: step = next(steps) except StopIteration: pass if verbose: print("-"*70) print("Adding terms #%i-#%i" % (index, index+step)) update(partial, xrange(index, index+step)) index += step # Check direct error best = partial[-1] error = abs(best - partial[-2]) if verbose: print("Direct error: %s" % ctx.nstr(error)) if error <= tol: return best # Check each extrapolation method if TRY_RICHARDSON: value, maxc = ctx.richardson(partial) # Convergence richardson_error = abs(value - last_richardson_value) if verbose: print("Richardson error: %s" % ctx.nstr(richardson_error)) # Convergence if richardson_error <= tol: return value last_richardson_value = value # Unreliable due to cancellation if ctx.eps*maxc > tol: if verbose: print("Ran out of precision for Richardson") TRY_RICHARDSON = False if richardson_error < error: error = richardson_error best = value if TRY_SHANKS: shanks_table = ctx.shanks(partial, shanks_table, randomized=True) row = shanks_table[-1] if len(row) == 2: est1 = row[-1] shanks_error = 0 else: est1, maxc, est2 = row[-1], abs(row[-2]), row[-3] shanks_error = abs(est1-est2) if verbose: print("Shanks error: %s" % ctx.nstr(shanks_error)) if shanks_error <= tol: return est1 if ctx.eps*maxc > tol: if verbose: print("Ran out of precision for Shanks") TRY_SHANKS = False if shanks_error < error: error = shanks_error best = est1 if TRY_EULER_MACLAURIN: if ctx.mpc(ctx.sign(partial[-1]) / ctx.sign(partial[-2])).ae(-1): if verbose: print ("NOT using Euler-Maclaurin: the series appears" " to be alternating, so numerical\n quadrature" " will most likely fail") TRY_EULER_MACLAURIN = False else: value, em_error = emfun(index, tol) value += partial[-1] if verbose: print("Euler-Maclaurin error: %s" % ctx.nstr(em_error)) if em_error <= tol: return value if em_error < error: best = value finally: ctx.prec = orig if strict: raise ctx.NoConvergence if verbose: print("Warning: failed to converge to target accuracy") return best @defun def nsum(ctx, f, *intervals, **options): r""" Computes the sum .. math :: S = \sum_{k=a}^b f(k) where `(a, b)` = *interval*, and where `a = -\infty` and/or `b = \infty` are allowed, or more generally .. math :: S = \sum_{k_1=a_1}^{b_1} \cdots \sum_{k_n=a_n}^{b_n} f(k_1,\ldots,k_n) if multiple intervals are given. Two examples of infinite series that can be summed by :func:`~mpmath.nsum`, where the first converges rapidly and the second converges slowly, are:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> nsum(lambda n: 1/fac(n), [0, inf]) 2.71828182845905 >>> nsum(lambda n: 1/n**2, [1, inf]) 1.64493406684823 When appropriate, :func:`~mpmath.nsum` applies convergence acceleration to accurately estimate the sums of slowly convergent series. If the series is finite, :func:`~mpmath.nsum` currently does not attempt to perform any extrapolation, and simply calls :func:`~mpmath.fsum`. Multidimensional infinite series are reduced to a single-dimensional series over expanding hypercubes; if both infinite and finite dimensions are present, the finite ranges are moved innermost. For more advanced control over the summation order, use nested calls to :func:`~mpmath.nsum`, or manually rewrite the sum as a single-dimensional series. **Options** *tol* Desired maximum final error. Defaults roughly to the epsilon of the working precision. *method* Which summation algorithm to use (described below). Default: ``'richardson+shanks'``. *maxterms* Cancel after at most this many terms. Default: 10*dps. *steps* An iterable giving the number of terms to add between each extrapolation attempt. The default sequence is [10, 20, 30, 40, ...]. For example, if you know that approximately 100 terms will be required, efficiency might be improved by setting this to [100, 10]. Then the first extrapolation will be performed after 100 terms, the second after 110, etc. *verbose* Print details about progress. *ignore* If enabled, any term that raises ``ArithmeticError`` or ``ValueError`` (e.g. through division by zero) is replaced by a zero. This is convenient for lattice sums with a singular term near the origin. **Methods** Unfortunately, an algorithm that can efficiently sum any infinite series does not exist. :func:`~mpmath.nsum` implements several different algorithms that each work well in different cases. The *method* keyword argument selects a method. The default method is ``'r+s'``, i.e. both Richardson extrapolation and Shanks transformation is attempted. A slower method that handles more cases is ``'r+s+e'``. For very high precision summation, or if the summation needs to be fast (for example if multiple sums need to be evaluated), it is a good idea to investigate which one method works best and only use that. ``'richardson'`` / ``'r'``: Uses Richardson extrapolation. Provides useful extrapolation when `f(k) \sim P(k)/Q(k)` or when `f(k) \sim (-1)^k P(k)/Q(k)` for polynomials `P` and `Q`. See :func:`~mpmath.richardson` for additional information. ``'shanks'`` / ``'s'``: Uses Shanks transformation. Typically provides useful extrapolation when `f(k) \sim c^k` or when successive terms alternate signs. Is able to sum some divergent series. See :func:`~mpmath.shanks` for additional information. ``'euler-maclaurin'`` / ``'e'``: Uses the Euler-Maclaurin summation formula to approximate the remainder sum by an integral. This requires high-order numerical derivatives and numerical integration. The advantage of this algorithm is that it works regardless of the decay rate of `f`, as long as `f` is sufficiently smooth. See :func:`~mpmath.sumem` for additional information. ``'direct'`` / ``'d'``: Does not perform any extrapolation. This can be used (and should only be used for) rapidly convergent series. The summation automatically stops when the terms decrease below the target tolerance. **Basic examples** A finite sum:: >>> nsum(lambda k: 1/k, [1, 6]) 2.45 Summation of a series going to negative infinity and a doubly infinite series:: >>> nsum(lambda k: 1/k**2, [-inf, -1]) 1.64493406684823 >>> nsum(lambda k: 1/(1+k**2), [-inf, inf]) 3.15334809493716 :func:`~mpmath.nsum` handles sums of complex numbers:: >>> nsum(lambda k: (0.5+0.25j)**k, [0, inf]) (1.6 + 0.8j) The following sum converges very rapidly, so it is most efficient to sum it by disabling convergence acceleration:: >>> mp.dps = 1000 >>> a = nsum(lambda k: -(-1)**k * k**2 / fac(2*k), [1, inf], ... method='direct') >>> b = (cos(1)+sin(1))/4 >>> abs(a-b) < mpf('1e-998') True **Examples with Richardson extrapolation** Richardson extrapolation works well for sums over rational functions, as well as their alternating counterparts:: >>> mp.dps = 50 >>> nsum(lambda k: 1 / k**3, [1, inf], ... method='richardson') 1.2020569031595942853997381615114499907649862923405 >>> zeta(3) 1.2020569031595942853997381615114499907649862923405 >>> nsum(lambda n: (n + 3)/(n**3 + n**2), [1, inf], ... method='richardson') 2.9348022005446793094172454999380755676568497036204 >>> pi**2/2-2 2.9348022005446793094172454999380755676568497036204 >>> nsum(lambda k: (-1)**k / k**3, [1, inf], ... method='richardson') -0.90154267736969571404980362113358749307373971925537 >>> -3*zeta(3)/4 -0.90154267736969571404980362113358749307373971925538 **Examples with Shanks transformation** The Shanks transformation works well for geometric series and typically provides excellent acceleration for Taylor series near the border of their disk of convergence. Here we apply it to a series for `\log(2)`, which can be seen as the Taylor series for `\log(1+x)` with `x = 1`:: >>> nsum(lambda k: -(-1)**k/k, [1, inf], ... method='shanks') 0.69314718055994530941723212145817656807550013436025 >>> log(2) 0.69314718055994530941723212145817656807550013436025 Here we apply it to a slowly convergent geometric series:: >>> nsum(lambda k: mpf('0.995')**k, [0, inf], ... method='shanks') 200.0 Finally, Shanks' method works very well for alternating series where `f(k) = (-1)^k g(k)`, and often does so regardless of the exact decay rate of `g(k)`:: >>> mp.dps = 15 >>> nsum(lambda k: (-1)**(k+1) / k**1.5, [1, inf], ... method='shanks') 0.765147024625408 >>> (2-sqrt(2))*zeta(1.5)/2 0.765147024625408 The following slowly convergent alternating series has no known closed-form value. Evaluating the sum a second time at higher precision indicates that the value is probably correct:: >>> nsum(lambda k: (-1)**k / log(k), [2, inf], ... method='shanks') 0.924299897222939 >>> mp.dps = 30 >>> nsum(lambda k: (-1)**k / log(k), [2, inf], ... method='shanks') 0.92429989722293885595957018136 **Examples with Euler-Maclaurin summation** The sum in the following example has the wrong rate of convergence for either Richardson or Shanks to be effective. >>> f = lambda k: log(k)/k**2.5 >>> mp.dps = 15 >>> nsum(f, [1, inf], method='euler-maclaurin') 0.38734195032621 >>> -diff(zeta, 2.5) 0.38734195032621 Increasing ``steps`` improves speed at higher precision:: >>> mp.dps = 50 >>> nsum(f, [1, inf], method='euler-maclaurin', steps=[250]) 0.38734195032620997271199237593105101319948228874688 >>> -diff(zeta, 2.5) 0.38734195032620997271199237593105101319948228874688 **Divergent series** The Shanks transformation is able to sum some *divergent* series. In particular, it is often able to sum Taylor series beyond their radius of convergence (this is due to a relation between the Shanks transformation and Pade approximations; see :func:`~mpmath.pade` for an alternative way to evaluate divergent Taylor series). Here we apply it to `\log(1+x)` far outside the region of convergence:: >>> mp.dps = 50 >>> nsum(lambda k: -(-9)**k/k, [1, inf], ... method='shanks') 2.3025850929940456840179914546843642076011014886288 >>> log(10) 2.3025850929940456840179914546843642076011014886288 A particular type of divergent series that can be summed using the Shanks transformation is geometric series. The result is the same as using the closed-form formula for an infinite geometric series:: >>> mp.dps = 15 >>> for n in range(-8, 8): ... if n == 1: ... continue ... print("%s %s %s" % (mpf(n), mpf(1)/(1-n), ... nsum(lambda k: n**k, [0, inf], method='shanks'))) ... -8.0 0.111111111111111 0.111111111111111 -7.0 0.125 0.125 -6.0 0.142857142857143 0.142857142857143 -5.0 0.166666666666667 0.166666666666667 -4.0 0.2 0.2 -3.0 0.25 0.25 -2.0 0.333333333333333 0.333333333333333 -1.0 0.5 0.5 0.0 1.0 1.0 2.0 -1.0 -1.0 3.0 -0.5 -0.5 4.0 -0.333333333333333 -0.333333333333333 5.0 -0.25 -0.25 6.0 -0.2 -0.2 7.0 -0.166666666666667 -0.166666666666667 **Multidimensional sums** Any combination of finite and infinite ranges is allowed for the summation indices:: >>> mp.dps = 15 >>> nsum(lambda x,y: x+y, [2,3], [4,5]) 28.0 >>> nsum(lambda x,y: x/2**y, [1,3], [1,inf]) 6.0 >>> nsum(lambda x,y: y/2**x, [1,inf], [1,3]) 6.0 >>> nsum(lambda x,y,z: z/(2**x*2**y), [1,inf], [1,inf], [3,4]) 7.0 >>> nsum(lambda x,y,z: y/(2**x*2**z), [1,inf], [3,4], [1,inf]) 7.0 >>> nsum(lambda x,y,z: x/(2**z*2**y), [3,4], [1,inf], [1,inf]) 7.0 Some nice examples of double series with analytic solutions or reductions to single-dimensional series (see [1]):: >>> nsum(lambda m, n: 1/2**(m*n), [1,inf], [1,inf]) 1.60669515241529 >>> nsum(lambda n: 1/(2**n-1), [1,inf]) 1.60669515241529 >>> nsum(lambda i,j: (-1)**(i+j)/(i**2+j**2), [1,inf], [1,inf]) 0.278070510848213 >>> pi*(pi-3*ln2)/12 0.278070510848213 >>> nsum(lambda i,j: (-1)**(i+j)/(i+j)**2, [1,inf], [1,inf]) 0.129319852864168 >>> altzeta(2) - altzeta(1) 0.129319852864168 >>> nsum(lambda i,j: (-1)**(i+j)/(i+j)**3, [1,inf], [1,inf]) 0.0790756439455825 >>> altzeta(3) - altzeta(2) 0.0790756439455825 >>> nsum(lambda m,n: m**2*n/(3**m*(n*3**m+m*3**n)), ... [1,inf], [1,inf]) 0.28125 >>> mpf(9)/32 0.28125 >>> nsum(lambda i,j: fac(i-1)*fac(j-1)/fac(i+j), ... [1,inf], [1,inf], workprec=400) 1.64493406684823 >>> zeta(2) 1.64493406684823 A hard example of a multidimensional sum is the Madelung constant in three dimensions (see [2]). The defining sum converges very slowly and only conditionally, so :func:`~mpmath.nsum` is lucky to obtain an accurate value through convergence acceleration. The second evaluation below uses a much more efficient, rapidly convergent 2D sum:: >>> nsum(lambda x,y,z: (-1)**(x+y+z)/(x*x+y*y+z*z)**0.5, ... [-inf,inf], [-inf,inf], [-inf,inf], ignore=True) -1.74756459463318 >>> nsum(lambda x,y: -12*pi*sech(0.5*pi * \ ... sqrt((2*x+1)**2+(2*y+1)**2))**2, [0,inf], [0,inf]) -1.74756459463318 Another example of a lattice sum in 2D:: >>> nsum(lambda x,y: (-1)**(x+y) / (x**2+y**2), [-inf,inf], ... [-inf,inf], ignore=True) -2.1775860903036 >>> -pi*ln2 -2.1775860903036 An example of an Eisenstein series:: >>> nsum(lambda m,n: (m+n*1j)**(-4), [-inf,inf], [-inf,inf], ... ignore=True) (3.1512120021539 + 0.0j) **References** 1. [Weisstein]_ http://mathworld.wolfram.com/DoubleSeries.html, 2. [Weisstein]_ http://mathworld.wolfram.com/MadelungConstants.html """ infinite, g = standardize(ctx, f, intervals, options) if not infinite: return +g() def update(partial_sums, indices): if partial_sums: psum = partial_sums[-1] else: psum = ctx.zero for k in indices: psum = psum + g(ctx.mpf(k)) partial_sums.append(psum) prec = ctx.prec def emfun(point, tol): workprec = ctx.prec ctx.prec = prec + 10 v = ctx.sumem(g, [point, ctx.inf], tol, error=1) ctx.prec = workprec return v return +ctx.adaptive_extrapolation(update, emfun, options) def wrapsafe(f): def g(*args): try: return f(*args) except (ArithmeticError, ValueError): return 0 return g def standardize(ctx, f, intervals, options): if options.get("ignore"): f = wrapsafe(f) finite = [] infinite = [] for k, points in enumerate(intervals): a, b = ctx._as_points(points) if b < a: return False, (lambda: ctx.zero) if a == ctx.ninf or b == ctx.inf: infinite.append((k, (a,b))) else: finite.append((k, (int(a), int(b)))) if finite: f = fold_finite(ctx, f, finite) if not infinite: return False, lambda: f(*([0]*len(intervals))) if infinite: f = standardize_infinite(ctx, f, infinite) f = fold_infinite(ctx, f, infinite) args = [0] * len(intervals) d = infinite[0][0] def g(k): args[d] = k return f(*args) return True, g # backwards compatible itertools.product def cartesian_product(args): pools = map(tuple, args) result = [[]] for pool in pools: result = [x+[y] for x in result for y in pool] for prod in result: yield tuple(prod) def fold_finite(ctx, f, intervals): if not intervals: return f indices = [v[0] for v in intervals] points = [v[1] for v in intervals] ranges = [xrange(a, b+1) for (a,b) in points] def g(*args): args = list(args) s = ctx.zero for xs in cartesian_product(ranges): for dim, x in zip(indices, xs): args[dim] = ctx.mpf(x) s += f(*args) return s #print "Folded finite", indices return g # Standardize each interval to [0,inf] def standardize_infinite(ctx, f, intervals): if not intervals: return f dim, [a,b] = intervals[-1] if a == ctx.ninf: if b == ctx.inf: def g(*args): args = list(args) k = args[dim] if k: s = f(*args) args[dim] = -k s += f(*args) return s else: return f(*args) else: def g(*args): args = list(args) args[dim] = b - args[dim] return f(*args) else: def g(*args): args = list(args) args[dim] += a return f(*args) #print "Standardized infinity along dimension", dim, a, b return standardize_infinite(ctx, g, intervals[:-1]) def fold_infinite(ctx, f, intervals): if len(intervals) < 2: return f dim1 = intervals[-2][0] dim2 = intervals[-1][0] # Assume intervals are [0,inf] x [0,inf] x ... def g(*args): args = list(args) #args.insert(dim2, None) n = int(args[dim1]) s = ctx.zero #y = ctx.mpf(n) args[dim2] = ctx.mpf(n) #y for x in xrange(n+1): args[dim1] = ctx.mpf(x) s += f(*args) args[dim1] = ctx.mpf(n) #ctx.mpf(n) for y in xrange(n): args[dim2] = ctx.mpf(y) s += f(*args) return s #print "Folded infinite from", len(intervals), "to", (len(intervals)-1) return fold_infinite(ctx, g, intervals[:-1]) @defun def nprod(ctx, f, interval, nsum=False, **kwargs): r""" Computes the product .. math :: P = \prod_{k=a}^b f(k) where `(a, b)` = *interval*, and where `a = -\infty` and/or `b = \infty` are allowed. By default, :func:`~mpmath.nprod` uses the same extrapolation methods as :func:`~mpmath.nsum`, except applied to the partial products rather than partial sums, and the same keyword options as for :func:`~mpmath.nsum` are supported. If ``nsum=True``, the product is instead computed via :func:`~mpmath.nsum` as .. math :: P = \exp\left( \sum_{k=a}^b \log(f(k)) \right). This is slower, but can sometimes yield better results. It is also required (and used automatically) when Euler-Maclaurin summation is requested. **Examples** A simple finite product:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> nprod(lambda k: k, [1, 4]) 24.0 A large number of infinite products have known exact values, and can therefore be used as a reference. Most of the following examples are taken from MathWorld [1]. A few infinite products with simple values are:: >>> 2*nprod(lambda k: (4*k**2)/(4*k**2-1), [1, inf]) 3.141592653589793238462643 >>> nprod(lambda k: (1+1/k)**2/(1+2/k), [1, inf]) 2.0 >>> nprod(lambda k: (k**3-1)/(k**3+1), [2, inf]) 0.6666666666666666666666667 >>> nprod(lambda k: (1-1/k**2), [2, inf]) 0.5 Next, several more infinite products with more complicated values:: >>> nprod(lambda k: exp(1/k**2), [1, inf]); exp(pi**2/6) 5.180668317897115748416626 5.180668317897115748416626 >>> nprod(lambda k: (k**2-1)/(k**2+1), [2, inf]); pi*csch(pi) 0.2720290549821331629502366 0.2720290549821331629502366 >>> nprod(lambda k: (k**4-1)/(k**4+1), [2, inf]) 0.8480540493529003921296502 >>> pi*sinh(pi)/(cosh(sqrt(2)*pi)-cos(sqrt(2)*pi)) 0.8480540493529003921296502 >>> nprod(lambda k: (1+1/k+1/k**2)**2/(1+2/k+3/k**2), [1, inf]) 1.848936182858244485224927 >>> 3*sqrt(2)*cosh(pi*sqrt(3)/2)**2*csch(pi*sqrt(2))/pi 1.848936182858244485224927 >>> nprod(lambda k: (1-1/k**4), [2, inf]); sinh(pi)/(4*pi) 0.9190194775937444301739244 0.9190194775937444301739244 >>> nprod(lambda k: (1-1/k**6), [2, inf]) 0.9826842777421925183244759 >>> (1+cosh(pi*sqrt(3)))/(12*pi**2) 0.9826842777421925183244759 >>> nprod(lambda k: (1+1/k**2), [2, inf]); sinh(pi)/(2*pi) 1.838038955187488860347849 1.838038955187488860347849 >>> nprod(lambda n: (1+1/n)**n * exp(1/(2*n)-1), [1, inf]) 1.447255926890365298959138 >>> exp(1+euler/2)/sqrt(2*pi) 1.447255926890365298959138 The following two products are equivalent and can be evaluated in terms of a Jacobi theta function. Pi can be replaced by any value (as long as convergence is preserved):: >>> nprod(lambda k: (1-pi**-k)/(1+pi**-k), [1, inf]) 0.3838451207481672404778686 >>> nprod(lambda k: tanh(k*log(pi)/2), [1, inf]) 0.3838451207481672404778686 >>> jtheta(4,0,1/pi) 0.3838451207481672404778686 This product does not have a known closed form value:: >>> nprod(lambda k: (1-1/2**k), [1, inf]) 0.2887880950866024212788997 A product taken from `-\infty`:: >>> nprod(lambda k: 1-k**(-3), [-inf,-2]) 0.8093965973662901095786805 >>> cosh(pi*sqrt(3)/2)/(3*pi) 0.8093965973662901095786805 A doubly infinite product:: >>> nprod(lambda k: exp(1/(1+k**2)), [-inf, inf]) 23.41432688231864337420035 >>> exp(pi/tanh(pi)) 23.41432688231864337420035 A product requiring the use of Euler-Maclaurin summation to compute an accurate value:: >>> nprod(lambda k: (1-1/k**2.5), [2, inf], method='e') 0.696155111336231052898125 **References** 1. [Weisstein]_ http://mathworld.wolfram.com/InfiniteProduct.html """ if nsum or ('e' in kwargs.get('method', '')): orig = ctx.prec try: # TODO: we are evaluating log(1+eps) -> eps, which is # inaccurate. This currently works because nsum greatly # increases the working precision. But we should be # more intelligent and handle the precision here. ctx.prec += 10 v = ctx.nsum(lambda n: ctx.ln(f(n)), interval, **kwargs) finally: ctx.prec = orig return +ctx.exp(v) a, b = ctx._as_points(interval) if a == ctx.ninf: if b == ctx.inf: return f(0) * ctx.nprod(lambda k: f(-k) * f(k), [1, ctx.inf], **kwargs) return ctx.nprod(f, [-b, ctx.inf], **kwargs) elif b != ctx.inf: return ctx.fprod(f(ctx.mpf(k)) for k in xrange(int(a), int(b)+1)) a = int(a) def update(partial_products, indices): if partial_products: pprod = partial_products[-1] else: pprod = ctx.one for k in indices: pprod = pprod * f(a + ctx.mpf(k)) partial_products.append(pprod) return +ctx.adaptive_extrapolation(update, None, kwargs) @defun def limit(ctx, f, x, direction=1, exp=False, **kwargs): r""" Computes an estimate of the limit .. math :: \lim_{t \to x} f(t) where `x` may be finite or infinite. For finite `x`, :func:`~mpmath.limit` evaluates `f(x + d/n)` for consecutive integer values of `n`, where the approach direction `d` may be specified using the *direction* keyword argument. For infinite `x`, :func:`~mpmath.limit` evaluates values of `f(\mathrm{sign}(x) \cdot n)`. If the approach to the limit is not sufficiently fast to give an accurate estimate directly, :func:`~mpmath.limit` attempts to find the limit using Richardson extrapolation or the Shanks transformation. You can select between these methods using the *method* keyword (see documentation of :func:`~mpmath.nsum` for more information). **Options** The following options are available with essentially the same meaning as for :func:`~mpmath.nsum`: *tol*, *method*, *maxterms*, *steps*, *verbose*. If the option *exp=True* is set, `f` will be sampled at exponentially spaced points `n = 2^1, 2^2, 2^3, \ldots` instead of the linearly spaced points `n = 1, 2, 3, \ldots`. This can sometimes improve the rate of convergence so that :func:`~mpmath.limit` may return a more accurate answer (and faster). However, do note that this can only be used if `f` supports fast and accurate evaluation for arguments that are extremely close to the limit point (or if infinite, very large arguments). **Examples** A basic evaluation of a removable singularity:: >>> from mpmath import * >>> mp.dps = 30; mp.pretty = True >>> limit(lambda x: (x-sin(x))/x**3, 0) 0.166666666666666666666666666667 Computing the exponential function using its limit definition:: >>> limit(lambda n: (1+3/n)**n, inf) 20.0855369231876677409285296546 >>> exp(3) 20.0855369231876677409285296546 A limit for `\pi`:: >>> f = lambda n: 2**(4*n+1)*fac(n)**4/(2*n+1)/fac(2*n)**2 >>> limit(f, inf) 3.14159265358979323846264338328 Calculating the coefficient in Stirling's formula:: >>> limit(lambda n: fac(n) / (sqrt(n)*(n/e)**n), inf) 2.50662827463100050241576528481 >>> sqrt(2*pi) 2.50662827463100050241576528481 Evaluating Euler's constant `\gamma` using the limit representation .. math :: \gamma = \lim_{n \rightarrow \infty } \left[ \left( \sum_{k=1}^n \frac{1}{k} \right) - \log(n) \right] (which converges notoriously slowly):: >>> f = lambda n: sum([mpf(1)/k for k in range(1,int(n)+1)]) - log(n) >>> limit(f, inf) 0.577215664901532860606512090082 >>> +euler 0.577215664901532860606512090082 With default settings, the following limit converges too slowly to be evaluated accurately. Changing to exponential sampling however gives a perfect result:: >>> f = lambda x: sqrt(x**3+x**2)/(sqrt(x**3)+x) >>> limit(f, inf) 0.992831158558330281129249686491 >>> limit(f, inf, exp=True) 1.0 """ if ctx.isinf(x): direction = ctx.sign(x) g = lambda k: f(ctx.mpf(k+1)*direction) else: direction *= ctx.one g = lambda k: f(x + direction/(k+1)) if exp: h = g g = lambda k: h(2**k) def update(values, indices): for k in indices: values.append(g(k+1)) # XXX: steps used by nsum don't work well if not 'steps' in kwargs: kwargs['steps'] = [10] return +ctx.adaptive_extrapolation(update, None, kwargs) sympy-0.7.4.1/sympy/mpmath/calculus/quadrature.py0000644000175000017500000011260212253362407022257 0ustar georgeskgeorgeskimport math from ..libmp.backend import xrange 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 = {} def clear(self): """ Delete cached node data. """ self.standard_cache = {} self.transformed_cache = {} self.interval_count = {} 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 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 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 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 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) 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 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) 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 """ 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 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 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 """ 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() sympy-0.7.4.1/sympy/mpmath/calculus/approximation.py0000644000175000017500000002115512253362407022776 0ustar georgeskgeorgeskfrom ..libmp.backend import xrange from .calculus import defun #----------------------------------------------------------------------------# # Approximation methods # #----------------------------------------------------------------------------# # The Chebyshev approximation formula is given at: # http://mathworld.wolfram.com/ChebyshevApproximationFormula.html # The only major changes in the following code is that we return the # expanded polynomial coefficients instead of Chebyshev coefficients, # and that we automatically transform [a,b] -> [-1,1] and back # for convenience. # Coefficient in Chebyshev approximation def chebcoeff(ctx,f,a,b,j,N): s = ctx.mpf(0) h = ctx.mpf(0.5) for k in range(1, N+1): t = ctx.cospi((k-h)/N) s += f(t*(b-a)*h + (b+a)*h) * ctx.cospi(j*(k-h)/N) return 2*s/N # Generate Chebyshev polynomials T_n(ax+b) in expanded form def chebT(ctx, a=1, b=0): Tb = [1] yield Tb Ta = [b, a] while 1: yield Ta # Recurrence: T[n+1](ax+b) = 2*(ax+b)*T[n](ax+b) - T[n-1](ax+b) Tmp = [0] + [2*a*t for t in Ta] for i, c in enumerate(Ta): Tmp[i] += 2*b*c for i, c in enumerate(Tb): Tmp[i] -= c Ta, Tb = Tmp, Ta @defun def chebyfit(ctx, f, interval, N, error=False): r""" Computes a polynomial of degree `N-1` that approximates the given function `f` on the interval `[a, b]`. With ``error=True``, :func:`~mpmath.chebyfit` also returns an accurate estimate of the maximum absolute error; that is, the maximum value of `|f(x) - P(x)|` for `x \in [a, b]`. :func:`~mpmath.chebyfit` uses the Chebyshev approximation formula, which gives a nearly optimal solution: that is, the maximum error of the approximating polynomial is very close to the smallest possible for any polynomial of the same degree. Chebyshev approximation is very useful if one needs repeated evaluation of an expensive function, such as function defined implicitly by an integral or a differential equation. (For example, it could be used to turn a slow mpmath function into a fast machine-precision version of the same.) **Examples** Here we use :func:`~mpmath.chebyfit` to generate a low-degree approximation of `f(x) = \cos(x)`, valid on the interval `[1, 2]`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> poly, err = chebyfit(cos, [1, 2], 5, error=True) >>> nprint(poly) [0.00291682, 0.146166, -0.732491, 0.174141, 0.949553] >>> nprint(err, 12) 1.61351758081e-5 The polynomial can be evaluated using ``polyval``:: >>> nprint(polyval(poly, 1.6), 12) -0.0291858904138 >>> nprint(cos(1.6), 12) -0.0291995223013 Sampling the true error at 1000 points shows that the error estimate generated by ``chebyfit`` is remarkably good:: >>> error = lambda x: abs(cos(x) - polyval(poly, x)) >>> nprint(max([error(1+n/1000.) for n in range(1000)]), 12) 1.61349954245e-5 **Choice of degree** The degree `N` can be set arbitrarily high, to obtain an arbitrarily good approximation. As a rule of thumb, an `N`-term Chebyshev approximation is good to `N/(b-a)` decimal places on a unit interval (although this depends on how well-behaved `f` is). The cost grows accordingly: ``chebyfit`` evaluates the function `(N^2)/2` times to compute the coefficients and an additional `N` times to estimate the error. **Possible issues** One should be careful to use a sufficiently high working precision both when calling ``chebyfit`` and when evaluating the resulting polynomial, as the polynomial is sometimes ill-conditioned. It is for example difficult to reach 15-digit accuracy when evaluating the polynomial using machine precision floats, no matter the theoretical accuracy of the polynomial. (The option to return the coefficients in Chebyshev form should be made available in the future.) It is important to note the Chebyshev approximation works poorly if `f` is not smooth. A function containing singularities, rapid oscillation, etc can be approximated more effectively by multiplying it by a weight function that cancels out the nonsmooth features, or by dividing the interval into several segments. """ a, b = ctx._as_points(interval) orig = ctx.prec try: ctx.prec = orig + int(N**0.5) + 20 c = [chebcoeff(ctx,f,a,b,k,N) for k in range(N)] d = [ctx.zero] * N d[0] = -c[0]/2 h = ctx.mpf(0.5) T = chebT(ctx, ctx.mpf(2)/(b-a), ctx.mpf(-1)*(b+a)/(b-a)) for (k, Tk) in zip(range(N), T): for i in range(len(Tk)): d[i] += c[k]*Tk[i] d = d[::-1] # Estimate maximum error err = ctx.zero for k in range(N): x = ctx.cos(ctx.pi*k/N) * (b-a)*h + (b+a)*h err = max(err, abs(f(x) - ctx.polyval(d, x))) finally: ctx.prec = orig if error: return d, +err else: return d @defun def fourier(ctx, f, interval, N): r""" Computes the Fourier series of degree `N` of the given function on the interval `[a, b]`. More precisely, :func:`~mpmath.fourier` returns two lists `(c, s)` of coefficients (the cosine series and sine series, respectively), such that .. math :: f(x) \sim \sum_{k=0}^N c_k \cos(k m) + s_k \sin(k m) where `m = 2 \pi / (b-a)`. Note that many texts define the first coefficient as `2 c_0` instead of `c_0`. The easiest way to evaluate the computed series correctly is to pass it to :func:`~mpmath.fourierval`. **Examples** The function `f(x) = x` has a simple Fourier series on the standard interval `[-\pi, \pi]`. The cosine coefficients are all zero (because the function has odd symmetry), and the sine coefficients are rational numbers:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> c, s = fourier(lambda x: x, [-pi, pi], 5) >>> nprint(c) [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] >>> nprint(s) [0.0, 2.0, -1.0, 0.666667, -0.5, 0.4] This computes a Fourier series of a nonsymmetric function on a nonstandard interval:: >>> I = [-1, 1.5] >>> f = lambda x: x**2 - 4*x + 1 >>> cs = fourier(f, I, 4) >>> nprint(cs[0]) [0.583333, 1.12479, -1.27552, 0.904708, -0.441296] >>> nprint(cs[1]) [0.0, -2.6255, 0.580905, 0.219974, -0.540057] It is instructive to plot a function along with its truncated Fourier series:: >>> plot([f, lambda x: fourierval(cs, I, x)], I) #doctest: +SKIP Fourier series generally converge slowly (and may not converge pointwise). For example, if `f(x) = \cosh(x)`, a 10-term Fourier series gives an `L^2` error corresponding to 2-digit accuracy:: >>> I = [-1, 1] >>> cs = fourier(cosh, I, 9) >>> g = lambda x: (cosh(x) - fourierval(cs, I, x))**2 >>> nprint(sqrt(quad(g, I))) 0.00467963 :func:`~mpmath.fourier` uses numerical quadrature. For nonsmooth functions, the accuracy (and speed) can be improved by including all singular points in the interval specification:: >>> nprint(fourier(abs, [-1, 1], 0), 10) ([0.5000441648], [0.0]) >>> nprint(fourier(abs, [-1, 0, 1], 0), 10) ([0.5], [0.0]) """ interval = ctx._as_points(interval) a = interval[0] b = interval[-1] L = b-a cos_series = [] sin_series = [] cutoff = ctx.eps*10 for n in xrange(N+1): m = 2*n*ctx.pi/L an = 2*ctx.quadgl(lambda t: f(t)*ctx.cos(m*t), interval)/L bn = 2*ctx.quadgl(lambda t: f(t)*ctx.sin(m*t), interval)/L if n == 0: an /= 2 if abs(an) < cutoff: an = ctx.zero if abs(bn) < cutoff: bn = ctx.zero cos_series.append(an) sin_series.append(bn) return cos_series, sin_series @defun def fourierval(ctx, series, interval, x): """ Evaluates a Fourier series (in the format computed by by :func:`~mpmath.fourier` for the given interval) at the point `x`. The series should be a pair `(c, s)` where `c` is the cosine series and `s` is the sine series. The two lists need not have the same length. """ cs, ss = series ab = ctx._as_points(interval) a = interval[0] b = interval[-1] m = 2*ctx.pi/(ab[-1]-ab[0]) s = ctx.zero s += ctx.fsum(cs[n]*ctx.cos(m*n*x) for n in xrange(len(cs)) if cs[n]) s += ctx.fsum(ss[n]*ctx.sin(m*n*x) for n in xrange(len(ss)) if ss[n]) return s sympy-0.7.4.1/sympy/mpmath/calculus/calculus.py0000644000175000017500000000014312253362407021711 0ustar georgeskgeorgeskclass CalculusMethods(object): pass def defun(f): setattr(CalculusMethods, f.__name__, f) sympy-0.7.4.1/sympy/mpmath/calculus/optimization.py0000644000175000017500000007661612253362407022646 0ustar georgeskgeorgeskfrom copy import copy from ..libmp.backend import xrange class OptimizationMethods(object): def __init__(ctx): pass ############## # 1D-SOLVERS # ############## 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) 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) 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 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 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 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 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) 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) 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 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 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 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() sympy-0.7.4.1/sympy/mpmath/calculus/differentiation.py0000644000175000017500000004727712253362407023273 0ustar georgeskgeorgeskfrom ..libmp.backend import xrange from .calculus import defun try: iteritems = dict.iteritems except AttributeError: iteritems = dict.items #----------------------------------------------------------------------------# # Differentiation # #----------------------------------------------------------------------------# @defun def difference(ctx, s, n): r""" Given a sequence `(s_k)` containing at least `n+1` items, returns the `n`-th forward difference, .. math :: \Delta^n = \sum_{k=0}^{\infty} (-1)^{k+n} {n \choose k} s_k. """ n = int(n) d = ctx.zero b = (-1) ** (n & 1) for k in xrange(n+1): d += b * s[k] b = (b * (k-n)) // (k+1) return d def hsteps(ctx, f, x, n, prec, **options): singular = options.get('singular') addprec = options.get('addprec', 10) direction = options.get('direction', 0) workprec = (prec+2*addprec) * (n+1) orig = ctx.prec try: ctx.prec = workprec h = options.get('h') if h is None: if options.get('relative'): hextramag = int(ctx.mag(x)) else: hextramag = 0 h = ctx.ldexp(1, -prec-addprec-hextramag) else: h = ctx.convert(h) # Directed: steps x, x+h, ... x+n*h direction = options.get('direction', 0) if direction: h *= ctx.sign(direction) steps = xrange(n+1) norm = h # Central: steps x-n*h, x-(n-2)*h ..., x, ..., x+(n-2)*h, x+n*h else: steps = xrange(-n, n+1, 2) norm = (2*h) # Perturb if singular: x += 0.5*h values = [f(x+k*h) for k in steps] return values, norm, workprec finally: ctx.prec = orig @defun def diff(ctx, f, x, n=1, **options): r""" Numerically computes the derivative of `f`, `f'(x)`, or generally for an integer `n \ge 0`, the `n`-th derivative `f^{(n)}(x)`. A few basic examples are:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> diff(lambda x: x**2 + x, 1.0) 3.0 >>> diff(lambda x: x**2 + x, 1.0, 2) 2.0 >>> diff(lambda x: x**2 + x, 1.0, 3) 0.0 >>> nprint([diff(exp, 3, n) for n in range(5)]) # exp'(x) = exp(x) [20.0855, 20.0855, 20.0855, 20.0855, 20.0855] Even more generally, given a tuple of arguments `(x_1, \ldots, x_k)` and order `(n_1, \ldots, n_k)`, the partial derivative `f^{(n_1,\ldots,n_k)}(x_1,\ldots,x_k)` is evaluated. For example:: >>> diff(lambda x,y: 3*x*y + 2*y - x, (0.25, 0.5), (0,1)) 2.75 >>> diff(lambda x,y: 3*x*y + 2*y - x, (0.25, 0.5), (1,1)) 3.0 **Options** The following optional keyword arguments are recognized: ``method`` Supported methods are ``'step'`` or ``'quad'``: derivatives may be computed using either a finite difference with a small step size `h` (default), or numerical quadrature. ``direction`` Direction of finite difference: can be -1 for a left difference, 0 for a central difference (default), or +1 for a right difference; more generally can be any complex number. ``addprec`` Extra precision for `h` used to account for the function's sensitivity to perturbations (default = 10). ``relative`` Choose `h` relative to the magnitude of `x`, rather than an absolute value; useful for large or tiny `x` (default = False). ``h`` As an alternative to ``addprec`` and ``relative``, manually select the step size `h`. ``singular`` If True, evaluation exactly at the point `x` is avoided; this is useful for differentiating functions with removable singularities. Default = False. ``radius`` Radius of integration contour (with ``method = 'quad'``). Default = 0.25. A larger radius typically is faster and more accurate, but it must be chosen so that `f` has no singularities within the radius from the evaluation point. A finite difference requires `n+1` function evaluations and must be performed at `(n+1)` times the target precision. Accordingly, `f` must support fast evaluation at high precision. With integration, a larger number of function evaluations is required, but not much extra precision is required. For high order derivatives, this method may thus be faster if f is very expensive to evaluate at high precision. **Further examples** The direction option is useful for computing left- or right-sided derivatives of nonsmooth functions:: >>> diff(abs, 0, direction=0) 0.0 >>> diff(abs, 0, direction=1) 1.0 >>> diff(abs, 0, direction=-1) -1.0 More generally, if the direction is nonzero, a right difference is computed where the step size is multiplied by sign(direction). For example, with direction=+j, the derivative from the positive imaginary direction will be computed:: >>> diff(abs, 0, direction=j) (0.0 - 1.0j) With integration, the result may have a small imaginary part even even if the result is purely real:: >>> diff(sqrt, 1, method='quad') # doctest:+ELLIPSIS (0.5 - 4.59...e-26j) >>> chop(_) 0.5 Adding precision to obtain an accurate value:: >>> diff(cos, 1e-30) 0.0 >>> diff(cos, 1e-30, h=0.0001) -9.99999998328279e-31 >>> diff(cos, 1e-30, addprec=100) -1.0e-30 """ partial = False try: orders = list(n) x = list(x) partial = True except TypeError: pass if partial: x = [ctx.convert(_) for _ in x] return _partial_diff(ctx, f, x, orders, options) method = options.get('method', 'step') if n == 0 and method != 'quad' and not options.get('singular'): return f(ctx.convert(x)) prec = ctx.prec try: if method == 'step': values, norm, workprec = hsteps(ctx, f, x, n, prec, **options) ctx.prec = workprec v = ctx.difference(values, n) / norm**n elif method == 'quad': ctx.prec += 10 radius = ctx.convert(options.get('radius', 0.25)) def g(t): rei = radius*ctx.expj(t) z = x + rei return f(z) / rei**n d = ctx.quadts(g, [0, 2*ctx.pi]) v = d * ctx.factorial(n) / (2*ctx.pi) else: raise ValueError("unknown method: %r" % method) finally: ctx.prec = prec return +v def _partial_diff(ctx, f, xs, orders, options): if not orders: return f() if not sum(orders): return f(*xs) i = 0 for i in range(len(orders)): if orders[i]: break order = orders[i] def fdiff_inner(*f_args): def inner(t): return f(*(f_args[:i] + (t,) + f_args[i+1:])) return ctx.diff(inner, f_args[i], order, **options) orders[i] = 0 return _partial_diff(ctx, fdiff_inner, xs, orders, options) @defun def diffs(ctx, f, x, n=None, **options): r""" Returns a generator that yields the sequence of derivatives .. math :: f(x), f'(x), f''(x), \ldots, f^{(k)}(x), \ldots With ``method='step'``, :func:`~mpmath.diffs` uses only `O(k)` function evaluations to generate the first `k` derivatives, rather than the roughly `O(k^2)` evaluations required if one calls :func:`~mpmath.diff` `k` separate times. With `n < \infty`, the generator stops as soon as the `n`-th derivative has been generated. If the exact number of needed derivatives is known in advance, this is further slightly more efficient. Options are the same as for :func:`~mpmath.diff`. **Examples** >>> from mpmath import * >>> mp.dps = 15 >>> nprint(list(diffs(cos, 1, 5))) [0.540302, -0.841471, -0.540302, 0.841471, 0.540302, -0.841471] >>> for i, d in zip(range(6), diffs(cos, 1)): ... print("%s %s" % (i, d)) ... 0 0.54030230586814 1 -0.841470984807897 2 -0.54030230586814 3 0.841470984807897 4 0.54030230586814 5 -0.841470984807897 """ if n is None: n = ctx.inf else: n = int(n) if options.get('method', 'step') != 'step': k = 0 while k < n: yield ctx.diff(f, x, k, **options) k += 1 return singular = options.get('singular') if singular: yield ctx.diff(f, x, 0, singular=True) else: yield f(ctx.convert(x)) if n < 1: return if n == ctx.inf: A, B = 1, 2 else: A, B = 1, n+1 while 1: callprec = ctx.prec y, norm, workprec = hsteps(ctx, f, x, B, callprec, **options) for k in xrange(A, B): try: ctx.prec = workprec d = ctx.difference(y, k) / norm**k finally: ctx.prec = callprec yield +d if k >= n: return A, B = B, int(A*1.4+1) B = min(B, n) def iterable_to_function(gen): gen = iter(gen) data = [] def f(k): for i in xrange(len(data), k+1): data.append(next(gen)) return data[k] return f @defun def diffs_prod(ctx, factors): r""" Given a list of `N` iterables or generators yielding `f_k(x), f'_k(x), f''_k(x), \ldots` for `k = 1, \ldots, N`, generate `g(x), g'(x), g''(x), \ldots` where `g(x) = f_1(x) f_2(x) \cdots f_N(x)`. At high precision and for large orders, this is typically more efficient than numerical differentiation if the derivatives of each `f_k(x)` admit direct computation. Note: This function does not increase the working precision internally, so guard digits may have to be added externally for full accuracy. **Examples** >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> f = lambda x: exp(x)*cos(x)*sin(x) >>> u = diffs(f, 1) >>> v = mp.diffs_prod([diffs(exp,1), diffs(cos,1), diffs(sin,1)]) >>> next(u); next(v) 1.23586333600241 1.23586333600241 >>> next(u); next(v) 0.104658952245596 0.104658952245596 >>> next(u); next(v) -5.96999877552086 -5.96999877552086 >>> next(u); next(v) -12.4632923122697 -12.4632923122697 """ N = len(factors) if N == 1: for c in factors[0]: yield c else: u = iterable_to_function(ctx.diffs_prod(factors[:N//2])) v = iterable_to_function(ctx.diffs_prod(factors[N//2:])) n = 0 while 1: #yield sum(binomial(n,k)*u(n-k)*v(k) for k in xrange(n+1)) s = u(n) * v(0) a = 1 for k in xrange(1,n+1): a = a * (n-k+1) // k s += a * u(n-k) * v(k) yield s n += 1 def dpoly(n, _cache={}): """ nth differentiation polynomial for exp (Faa di Bruno's formula). TODO: most exponents are zero, so maybe a sparse representation would be better. """ if n in _cache: return _cache[n] if not _cache: _cache[0] = {(0,):1} R = dpoly(n-1) R = dict((c+(0,),v) for (c,v) in iteritems(R)) Ra = {} for powers, count in iteritems(R): powers1 = (powers[0]+1,) + powers[1:] if powers1 in Ra: Ra[powers1] += count else: Ra[powers1] = count for powers, count in iteritems(R): if not sum(powers): continue for k,p in enumerate(powers): if p: powers2 = powers[:k] + (p-1,powers[k+1]+1) + powers[k+2:] if powers2 in Ra: Ra[powers2] += p*count else: Ra[powers2] = p*count _cache[n] = Ra return _cache[n] @defun def diffs_exp(ctx, fdiffs): r""" Given an iterable or generator yielding `f(x), f'(x), f''(x), \ldots` generate `g(x), g'(x), g''(x), \ldots` where `g(x) = \exp(f(x))`. At high precision and for large orders, this is typically more efficient than numerical differentiation if the derivatives of `f(x)` admit direct computation. Note: This function does not increase the working precision internally, so guard digits may have to be added externally for full accuracy. **Examples** The derivatives of the gamma function can be computed using logarithmic differentiation:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> >>> def diffs_loggamma(x): ... yield loggamma(x) ... i = 0 ... while 1: ... yield psi(i,x) ... i += 1 ... >>> u = diffs_exp(diffs_loggamma(3)) >>> v = diffs(gamma, 3) >>> next(u); next(v) 2.0 2.0 >>> next(u); next(v) 1.84556867019693 1.84556867019693 >>> next(u); next(v) 2.49292999190269 2.49292999190269 >>> next(u); next(v) 3.44996501352367 3.44996501352367 """ fn = iterable_to_function(fdiffs) f0 = ctx.exp(fn(0)) yield f0 i = 1 while 1: s = ctx.mpf(0) for powers, c in iteritems(dpoly(i)): s += c*ctx.fprod(fn(k+1)**p for (k,p) in enumerate(powers) if p) yield s * f0 i += 1 @defun def differint(ctx, f, x, n=1, x0=0): r""" Calculates the Riemann-Liouville differintegral, or fractional derivative, defined by .. math :: \,_{x_0}{\mathbb{D}}^n_xf(x) \frac{1}{\Gamma(m-n)} \frac{d^m}{dx^m} \int_{x_0}^{x}(x-t)^{m-n-1}f(t)dt where `f` is a given (presumably well-behaved) function, `x` is the evaluation point, `n` is the order, and `x_0` is the reference point of integration (`m` is an arbitrary parameter selected automatically). With `n = 1`, this is just the standard derivative `f'(x)`; with `n = 2`, the second derivative `f''(x)`, etc. With `n = -1`, it gives `\int_{x_0}^x f(t) dt`, with `n = -2` it gives `\int_{x_0}^x \left( \int_{x_0}^t f(u) du \right) dt`, etc. As `n` is permitted to be any number, this operator generalizes iterated differentiation and iterated integration to a single operator with a continuous order parameter. **Examples** There is an exact formula for the fractional derivative of a monomial `x^p`, which may be used as a reference. For example, the following gives a half-derivative (order 0.5):: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> x = mpf(3); p = 2; n = 0.5 >>> differint(lambda t: t**p, x, n) 7.81764019044672 >>> gamma(p+1)/gamma(p-n+1) * x**(p-n) 7.81764019044672 Another useful test function is the exponential function, whose integration / differentiation formula easy generalizes to arbitrary order. Here we first compute a third derivative, and then a triply nested integral. (The reference point `x_0` is set to `-\infty` to avoid nonzero endpoint terms.):: >>> differint(lambda x: exp(pi*x), -1.5, 3) 0.278538406900792 >>> exp(pi*-1.5) * pi**3 0.278538406900792 >>> differint(lambda x: exp(pi*x), 3.5, -3, -inf) 1922.50563031149 >>> exp(pi*3.5) / pi**3 1922.50563031149 However, for noninteger `n`, the differentiation formula for the exponential function must be modified to give the same result as the Riemann-Liouville differintegral:: >>> x = mpf(3.5) >>> c = pi >>> n = 1+2*j >>> differint(lambda x: exp(c*x), x, n) (-123295.005390743 + 140955.117867654j) >>> x**(-n) * exp(c)**x * (x*c)**n * gammainc(-n, 0, x*c) / gamma(-n) (-123295.005390743 + 140955.117867654j) """ m = max(int(ctx.ceil(ctx.re(n)))+1, 1) r = m-n-1 g = lambda x: ctx.quad(lambda t: (x-t)**r * f(t), [x0, x]) return ctx.diff(g, x, m) / ctx.gamma(m-n) @defun def diffun(ctx, f, n=1, **options): r""" Given a function `f`, returns a function `g(x)` that evaluates the nth derivative `f^{(n)}(x)`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> cos2 = diffun(sin) >>> sin2 = diffun(sin, 4) >>> cos(1.3), cos2(1.3) (0.267498828624587, 0.267498828624587) >>> sin(1.3), sin2(1.3) (0.963558185417193, 0.963558185417193) The function `f` must support arbitrary precision evaluation. See :func:`~mpmath.diff` for additional details and supported keyword options. """ if n == 0: return f def g(x): return ctx.diff(f, x, n, **options) return g @defun def taylor(ctx, f, x, n, **options): r""" Produces a degree-`n` Taylor polynomial around the point `x` of the given function `f`. The coefficients are returned as a list. >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> nprint(chop(taylor(sin, 0, 5))) [0.0, 1.0, 0.0, -0.166667, 0.0, 0.00833333] The coefficients are computed using high-order numerical differentiation. The function must be possible to evaluate to arbitrary precision. See :func:`~mpmath.diff` for additional details and supported keyword options. Note that to evaluate the Taylor polynomial as an approximation of `f`, e.g. with :func:`~mpmath.polyval`, the coefficients must be reversed, and the point of the Taylor expansion must be subtracted from the argument: >>> p = taylor(exp, 2.0, 10) >>> polyval(p[::-1], 2.5 - 2.0) 12.1824939606092 >>> exp(2.5) 12.1824939607035 """ gen = enumerate(ctx.diffs(f, x, n, **options)) if options.get("chop", True): return [ctx.chop(d)/ctx.factorial(i) for i, d in gen] else: return [d/ctx.factorial(i) for i, d in gen] @defun def pade(ctx, a, L, M): r""" Computes a Pade approximation of degree `(L, M)` to a function. Given at least `L+M+1` Taylor coefficients `a` approximating a function `A(x)`, :func:`~mpmath.pade` returns coefficients of polynomials `P, Q` satisfying .. math :: P = \sum_{k=0}^L p_k x^k Q = \sum_{k=0}^M q_k x^k Q_0 = 1 A(x) Q(x) = P(x) + O(x^{L+M+1}) `P(x)/Q(x)` can provide a good approximation to an analytic function beyond the radius of convergence of its Taylor series (example from G.A. Baker 'Essentials of Pade Approximants' Academic Press, Ch.1A):: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> one = mpf(1) >>> def f(x): ... return sqrt((one + 2*x)/(one + x)) ... >>> a = taylor(f, 0, 6) >>> p, q = pade(a, 3, 3) >>> x = 10 >>> polyval(p[::-1], x)/polyval(q[::-1], x) 1.38169105566806 >>> f(x) 1.38169855941551 """ # To determine L+1 coefficients of P and M coefficients of Q # L+M+1 coefficients of A must be provided assert(len(a) >= L+M+1) if M == 0: if L == 0: return [ctx.one], [ctx.one] else: return a[:L+1], [ctx.one] # Solve first # a[L]*q[1] + ... + a[L-M+1]*q[M] = -a[L+1] # ... # a[L+M-1]*q[1] + ... + a[L]*q[M] = -a[L+M] A = ctx.matrix(M) for j in range(M): for i in range(min(M, L+j+1)): A[j, i] = a[L+j-i] v = -ctx.matrix(a[(L+1):(L+M+1)]) x = ctx.lu_solve(A, v) q = [ctx.one] + list(x) # compute p p = [0]*(L+1) for i in range(L+1): s = a[i] for j in range(1, min(M,i) + 1): s += q[j]*a[i-j] p[i] = s return p, q sympy-0.7.4.1/sympy/mpmath/calculus/polynomials.py0000644000175000017500000001475012253362407022455 0ustar georgeskgeorgeskfrom ..libmp.backend import xrange from .calculus import defun #----------------------------------------------------------------------------# # Polynomials # #----------------------------------------------------------------------------# # XXX: extra precision @defun def polyval(ctx, coeffs, x, derivative=False): r""" Given coefficients `[c_n, \ldots, c_2, c_1, c_0]` and a number `x`, :func:`~mpmath.polyval` evaluates the polynomial .. math :: P(x) = c_n x^n + \ldots + c_2 x^2 + c_1 x + c_0. If *derivative=True* is set, :func:`~mpmath.polyval` simultaneously evaluates `P(x)` with the derivative, `P'(x)`, and returns the tuple `(P(x), P'(x))`. >>> from mpmath import * >>> mp.pretty = True >>> polyval([3, 0, 2], 0.5) 2.75 >>> polyval([3, 0, 2], 0.5, derivative=True) (2.75, 3.0) The coefficients and the evaluation point may be any combination of real or complex numbers. """ if not coeffs: return ctx.zero p = ctx.convert(coeffs[0]) q = ctx.zero for c in coeffs[1:]: if derivative: q = p + x*q p = c + x*p if derivative: return p, q else: return p @defun def polyroots(ctx, coeffs, maxsteps=50, cleanup=True, extraprec=10, error=False): """ Computes all roots (real or complex) of a given polynomial. The roots are returned as a sorted list, where real roots appear first followed by complex conjugate roots as adjacent elements. The polynomial should be given as a list of coefficients, in the format used by :func:`~mpmath.polyval`. The leading coefficient must be nonzero. With *error=True*, :func:`~mpmath.polyroots` returns a tuple *(roots, err)* where *err* is an estimate of the maximum error among the computed roots. **Examples** Finding the three real roots of `x^3 - x^2 - 14x + 24`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> nprint(polyroots([1,-1,-14,24]), 4) [-4.0, 2.0, 3.0] Finding the two complex conjugate roots of `4x^2 + 3x + 2`, with an error estimate:: >>> roots, err = polyroots([4,3,2], error=True) >>> for r in roots: ... print(r) ... (-0.375 + 0.59947894041409j) (-0.375 - 0.59947894041409j) >>> >>> err 2.22044604925031e-16 >>> >>> polyval([4,3,2], roots[0]) (2.22044604925031e-16 + 0.0j) >>> polyval([4,3,2], roots[1]) (2.22044604925031e-16 + 0.0j) The following example computes all the 5th roots of unity; that is, the roots of `x^5 - 1`:: >>> mp.dps = 20 >>> for r in polyroots([1, 0, 0, 0, 0, -1]): ... print(r) ... 1.0 (-0.8090169943749474241 + 0.58778525229247312917j) (-0.8090169943749474241 - 0.58778525229247312917j) (0.3090169943749474241 + 0.95105651629515357212j) (0.3090169943749474241 - 0.95105651629515357212j) **Precision and conditioning** Provided there are no repeated roots, :func:`~mpmath.polyroots` can typically compute all roots of an arbitrary polynomial to high precision:: >>> mp.dps = 60 >>> for r in polyroots([1, 0, -10, 0, 1]): ... print r ... -3.14626436994197234232913506571557044551247712918732870123249 -0.317837245195782244725757617296174288373133378433432554879127 0.317837245195782244725757617296174288373133378433432554879127 3.14626436994197234232913506571557044551247712918732870123249 >>> >>> sqrt(3) + sqrt(2) 3.14626436994197234232913506571557044551247712918732870123249 >>> sqrt(3) - sqrt(2) 0.317837245195782244725757617296174288373133378433432554879127 **Algorithm** :func:`~mpmath.polyroots` implements the Durand-Kerner method [1], which uses complex arithmetic to locate all roots simultaneously. The Durand-Kerner method can be viewed as approximately performing simultaneous Newton iteration for all the roots. In particular, the convergence to simple roots is quadratic, just like Newton's method. Although all roots are internally calculated using complex arithmetic, any root found to have an imaginary part smaller than the estimated numerical error is truncated to a real number. Real roots are placed first in the returned list, sorted by value. The remaining complex roots are sorted by real their parts so that conjugate roots end up next to each other. **References** 1. http://en.wikipedia.org/wiki/Durand-Kerner_method """ if len(coeffs) <= 1: if not coeffs or not coeffs[0]: raise ValueError("Input to polyroots must not be the zero polynomial") # Constant polynomial with no roots return [] orig = ctx.prec weps = +ctx.eps try: ctx.prec += 10 tol = ctx.eps * 128 deg = len(coeffs) - 1 # Must be monic lead = ctx.convert(coeffs[0]) if lead == 1: coeffs = [ctx.convert(c) for c in coeffs] else: coeffs = [c/lead for c in coeffs] f = lambda x: ctx.polyval(coeffs, x) roots = [ctx.mpc((0.4+0.9j)**n) for n in xrange(deg)] err = [ctx.one for n in xrange(deg)] # Durand-Kerner iteration until convergence for step in xrange(maxsteps): if abs(max(err)) < tol: break for i in xrange(deg): if not abs(err[i]) < tol: p = roots[i] x = f(p) for j in range(deg): if i != j: try: x /= (p-roots[j]) except ZeroDivisionError: continue roots[i] = p - x err[i] = abs(x) # Remove small imaginary parts if cleanup: for i in xrange(deg): if abs(ctx._im(roots[i])) < weps: roots[i] = roots[i].real elif abs(ctx._re(roots[i])) < weps: roots[i] = roots[i].imag * 1j roots.sort(key=lambda x: (abs(ctx._im(x)), ctx._re(x))) finally: ctx.prec = orig if error: err = max(err) err = max(err, ctx.ldexp(1, -orig+1)) return [+r for r in roots], +err else: return [+r for r in roots] sympy-0.7.4.1/sympy/mpmath/calculus/odes.py0000644000175000017500000002326412253362407021041 0ustar georgeskgeorgeskfrom bisect import bisect from ..libmp.backend import xrange class ODEMethods(object): pass def ode_taylor(ctx, derivs, x0, y0, tol_prec, n): h = tol = ctx.ldexp(1, -tol_prec) dim = len(y0) xs = [x0] ys = [y0] x = x0 y = y0 orig = ctx.prec try: ctx.prec = orig*(1+n) # Use n steps with Euler's method to get # evaluation points for derivatives for i in range(n): fxy = derivs(x, y) y = [y[i]+h*fxy[i] for i in xrange(len(y))] x += h xs.append(x) ys.append(y) # Compute derivatives ser = [[] for d in range(dim)] for j in range(n+1): s = [0]*dim b = (-1) ** (j & 1) k = 1 for i in range(j+1): for d in range(dim): s[d] += b * ys[i][d] b = (b * (j-k+1)) // (-k) k += 1 scale = h**(-j) / ctx.fac(j) for d in range(dim): s[d] = s[d] * scale ser[d].append(s[d]) finally: ctx.prec = orig # Estimate radius for which we can get full accuracy. # XXX: do this right for zeros radius = ctx.one for ts in ser: if ts[-1]: radius = min(radius, ctx.nthroot(tol/abs(ts[-1]), n)) radius /= 2 # XXX return ser, x0+radius def odefun(ctx, F, x0, y0, tol=None, degree=None, method='taylor', verbose=False): r""" Returns a function `y(x) = [y_0(x), y_1(x), \ldots, y_n(x)]` that is a numerical solution of the `n+1`-dimensional first-order ordinary differential equation (ODE) system .. math :: y_0'(x) = F_0(x, [y_0(x), y_1(x), \ldots, y_n(x)]) y_1'(x) = F_1(x, [y_0(x), y_1(x), \ldots, y_n(x)]) \vdots y_n'(x) = F_n(x, [y_0(x), y_1(x), \ldots, y_n(x)]) The derivatives are specified by the vector-valued function *F* that evaluates `[y_0', \ldots, y_n'] = F(x, [y_0, \ldots, y_n])`. The initial point `x_0` is specified by the scalar argument *x0*, and the initial value `y(x_0) = [y_0(x_0), \ldots, y_n(x_0)]` is specified by the vector argument *y0*. For convenience, if the system is one-dimensional, you may optionally provide just a scalar value for *y0*. In this case, *F* should accept a scalar *y* argument and return a scalar. The solution function *y* will return scalar values instead of length-1 vectors. Evaluation of the solution function `y(x)` is permitted for any `x \ge x_0`. A high-order ODE can be solved by transforming it into first-order vector form. This transformation is described in standard texts on ODEs. Examples will also be given below. **Options, speed and accuracy** By default, :func:`~mpmath.odefun` uses a high-order Taylor series method. For reasonably well-behaved problems, the solution will be fully accurate to within the working precision. Note that *F* must be possible to evaluate to very high precision for the generation of Taylor series to work. To get a faster but less accurate solution, you can set a large value for *tol* (which defaults roughly to *eps*). If you just want to plot the solution or perform a basic simulation, *tol = 0.01* is likely sufficient. The *degree* argument controls the degree of the solver (with *method='taylor'*, this is the degree of the Taylor series expansion). A higher degree means that a longer step can be taken before a new local solution must be generated from *F*, meaning that fewer steps are required to get from `x_0` to a given `x_1`. On the other hand, a higher degree also means that each local solution becomes more expensive (i.e., more evaluations of *F* are required per step, and at higher precision). The optimal setting therefore involves a tradeoff. Generally, decreasing the *degree* for Taylor series is likely to give faster solution at low precision, while increasing is likely to be better at higher precision. The function object returned by :func:`~mpmath.odefun` caches the solutions at all step points and uses polynomial interpolation between step points. Therefore, once `y(x_1)` has been evaluated for some `x_1`, `y(x)` can be evaluated very quickly for any `x_0 \le x \le x_1`. and continuing the evaluation up to `x_2 > x_1` is also fast. **Examples of first-order ODEs** We will solve the standard test problem `y'(x) = y(x), y(0) = 1` which has explicit solution `y(x) = \exp(x)`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> f = odefun(lambda x, y: y, 0, 1) >>> for x in [0, 1, 2.5]: ... print((f(x), exp(x))) ... (1.0, 1.0) (2.71828182845905, 2.71828182845905) (12.1824939607035, 12.1824939607035) The solution with high precision:: >>> mp.dps = 50 >>> f = odefun(lambda x, y: y, 0, 1) >>> f(1) 2.7182818284590452353602874713526624977572470937 >>> exp(1) 2.7182818284590452353602874713526624977572470937 Using the more general vectorized form, the test problem can be input as (note that *f* returns a 1-element vector):: >>> mp.dps = 15 >>> f = odefun(lambda x, y: [y[0]], 0, [1]) >>> f(1) [2.71828182845905] :func:`~mpmath.odefun` can solve nonlinear ODEs, which are generally impossible (and at best difficult) to solve analytically. As an example of a nonlinear ODE, we will solve `y'(x) = x \sin(y(x))` for `y(0) = \pi/2`. An exact solution happens to be known for this problem, and is given by `y(x) = 2 \tan^{-1}\left(\exp\left(x^2/2\right)\right)`:: >>> f = odefun(lambda x, y: x*sin(y), 0, pi/2) >>> for x in [2, 5, 10]: ... print((f(x), 2*atan(exp(mpf(x)**2/2)))) ... (2.87255666284091, 2.87255666284091) (3.14158520028345, 3.14158520028345) (3.14159265358979, 3.14159265358979) If `F` is independent of `y`, an ODE can be solved using direct integration. We can therefore obtain a reference solution with :func:`~mpmath.quad`:: >>> f = lambda x: (1+x**2)/(1+x**3) >>> g = odefun(lambda x, y: f(x), pi, 0) >>> g(2*pi) 0.72128263801696 >>> quad(f, [pi, 2*pi]) 0.72128263801696 **Examples of second-order ODEs** We will solve the harmonic oscillator equation `y''(x) + y(x) = 0`. To do this, we introduce the helper functions `y_0 = y, y_1 = y_0'` whereby the original equation can be written as `y_1' + y_0' = 0`. Put together, we get the first-order, two-dimensional vector ODE .. math :: \begin{cases} y_0' = y_1 \\ y_1' = -y_0 \end{cases} To get a well-defined IVP, we need two initial values. With `y(0) = y_0(0) = 1` and `-y'(0) = y_1(0) = 0`, the problem will of course be solved by `y(x) = y_0(x) = \cos(x)` and `-y'(x) = y_1(x) = \sin(x)`. We check this:: >>> f = odefun(lambda x, y: [-y[1], y[0]], 0, [1, 0]) >>> for x in [0, 1, 2.5, 10]: ... nprint(f(x), 15) ... nprint([cos(x), sin(x)], 15) ... print("---") ... [1.0, 0.0] [1.0, 0.0] --- [0.54030230586814, 0.841470984807897] [0.54030230586814, 0.841470984807897] --- [-0.801143615546934, 0.598472144103957] [-0.801143615546934, 0.598472144103957] --- [-0.839071529076452, -0.54402111088937] [-0.839071529076452, -0.54402111088937] --- Note that we get both the sine and the cosine solutions simultaneously. **TODO** * Better automatic choice of degree and step size * Make determination of Taylor series convergence radius more robust * Allow solution for `x < x_0` * Allow solution for complex `x` * Test for difficult (ill-conditioned) problems * Implement Runge-Kutta and other algorithms """ if tol: tol_prec = int(-ctx.log(tol, 2))+10 else: tol_prec = ctx.prec+10 degree = degree or (3 + int(3*ctx.dps/2.)) workprec = ctx.prec + 40 try: len(y0) return_vector = True except TypeError: F_ = F F = lambda x, y: [F_(x, y[0])] y0 = [y0] return_vector = False ser, xb = ode_taylor(ctx, F, x0, y0, tol_prec, degree) series_boundaries = [x0, xb] series_data = [(ser, x0, xb)] # We will be working with vectors of Taylor series def mpolyval(ser, a): return [ctx.polyval(s[::-1], a) for s in ser] # Find nearest expansion point; compute if necessary def get_series(x): if x < x0: raise ValueError n = bisect(series_boundaries, x) if n < len(series_boundaries): return series_data[n-1] while 1: ser, xa, xb = series_data[-1] if verbose: print("Computing Taylor series for [%f, %f]" % (xa, xb)) y = mpolyval(ser, xb-xa) xa = xb ser, xb = ode_taylor(ctx, F, xb, y, tol_prec, degree) series_boundaries.append(xb) series_data.append((ser, xa, xb)) if x <= xb: return series_data[-1] # Evaluation function def interpolant(x): x = ctx.convert(x) orig = ctx.prec try: ctx.prec = workprec ser, xa, xb = get_series(x) y = mpolyval(ser, x-xa) finally: ctx.prec = orig if return_vector: return [+yk for yk in y] else: return +y[0] return interpolant ODEMethods.odefun = odefun if __name__ == "__main__": import doctest doctest.testmod() sympy-0.7.4.1/sympy/mpmath/calculus/__init__.py0000644000175000017500000000024212253362407021635 0ustar georgeskgeorgeskfrom . import calculus # XXX: hack to set methods from . import approximation from . import differentiation from . import extrapolation from . import polynomials sympy-0.7.4.1/sympy/mpmath/ctx_mp.py0000644000175000017500000013405212253362407017564 0ustar georgeskgeorgesk""" This module defines the mpf, mpc classes, and standard functions for operating with them. """ __docformat__ = 'plaintext' import re from .ctx_base import StandardBaseContext from .libmp.backend import basestring from . import libmp from .libmp import (MPZ, MPZ_ZERO, MPZ_ONE, int_types, repr_dps, round_floor, round_ceiling, dps_to_prec, round_nearest, prec_to_dps, ComplexResult, to_pickable, from_pickable, normalize, from_int, from_float, from_str, to_int, to_float, to_str, from_rational, from_man_exp, fone, fzero, finf, fninf, fnan, mpf_abs, mpf_pos, mpf_neg, mpf_add, mpf_sub, mpf_mul, mpf_mul_int, mpf_div, mpf_rdiv_int, mpf_pow_int, mpf_mod, mpf_eq, mpf_cmp, mpf_lt, mpf_gt, mpf_le, mpf_ge, mpf_hash, mpf_rand, mpf_sum, bitcount, to_fixed, mpc_to_str, mpc_to_complex, mpc_hash, mpc_pos, mpc_is_nonzero, mpc_neg, mpc_conjugate, mpc_abs, mpc_add, mpc_add_mpf, mpc_sub, mpc_sub_mpf, mpc_mul, mpc_mul_mpf, mpc_mul_int, mpc_div, mpc_div_mpf, mpc_pow, mpc_pow_mpf, mpc_pow_int, mpc_mpf_div, mpf_pow, mpf_pi, mpf_degree, mpf_e, mpf_phi, mpf_ln2, mpf_ln10, mpf_euler, mpf_catalan, mpf_apery, mpf_khinchin, mpf_glaisher, mpf_twinprime, mpf_mertens, int_types) from . import function_docs from . import rational new = object.__new__ get_complex = re.compile(r'^\(?(?P[\+\-]?\d*\.?\d*(e[\+\-]?\d+)?)??' r'(?P[\+\-]?\d*\.?\d*(e[\+\-]?\d+)?j)?\)?$') from .ctx_mp_python import PythonMPContext as BaseMPContext from . import ctx_mp_python as _mpf_module from .ctx_mp_python import _mpf, _mpc, mpnumeric class MPContext(BaseMPContext, StandardBaseContext): """ Context for multiprecision arithmetic with a global precision. """ def __init__(ctx): BaseMPContext.__init__(ctx) ctx.trap_complex = False ctx.pretty = False ctx.types = [ctx.mpf, ctx.mpc, ctx.constant] ctx._mpq = rational.mpq ctx.default() StandardBaseContext.__init__(ctx) ctx.mpq = rational.mpq ctx.init_builtins() ctx.hyp_summators = {} ctx._init_aliases() # XXX: automate try: ctx.bernoulli.im_func.func_doc = function_docs.bernoulli ctx.primepi.im_func.func_doc = function_docs.primepi ctx.psi.im_func.func_doc = function_docs.psi ctx.atan2.im_func.func_doc = function_docs.atan2 except AttributeError: # python 3 ctx.bernoulli.__func__.func_doc = function_docs.bernoulli ctx.primepi.__func__.func_doc = function_docs.primepi ctx.psi.__func__.func_doc = function_docs.psi ctx.atan2.__func__.func_doc = function_docs.atan2 ctx.digamma.func_doc = function_docs.digamma ctx.cospi.func_doc = function_docs.cospi ctx.sinpi.func_doc = function_docs.sinpi def init_builtins(ctx): mpf = ctx.mpf mpc = ctx.mpc # Exact constants ctx.one = ctx.make_mpf(fone) ctx.zero = ctx.make_mpf(fzero) ctx.j = ctx.make_mpc((fzero,fone)) ctx.inf = ctx.make_mpf(finf) ctx.ninf = ctx.make_mpf(fninf) ctx.nan = ctx.make_mpf(fnan) eps = ctx.constant(lambda prec, rnd: (0, MPZ_ONE, 1-prec, 1), "epsilon of working precision", "eps") ctx.eps = eps # Approximate constants ctx.pi = ctx.constant(mpf_pi, "pi", "pi") ctx.ln2 = ctx.constant(mpf_ln2, "ln(2)", "ln2") ctx.ln10 = ctx.constant(mpf_ln10, "ln(10)", "ln10") ctx.phi = ctx.constant(mpf_phi, "Golden ratio phi", "phi") ctx.e = ctx.constant(mpf_e, "e = exp(1)", "e") ctx.euler = ctx.constant(mpf_euler, "Euler's constant", "euler") ctx.catalan = ctx.constant(mpf_catalan, "Catalan's constant", "catalan") ctx.khinchin = ctx.constant(mpf_khinchin, "Khinchin's constant", "khinchin") ctx.glaisher = ctx.constant(mpf_glaisher, "Glaisher's constant", "glaisher") ctx.apery = ctx.constant(mpf_apery, "Apery's constant", "apery") ctx.degree = ctx.constant(mpf_degree, "1 deg = pi / 180", "degree") ctx.twinprime = ctx.constant(mpf_twinprime, "Twin prime constant", "twinprime") ctx.mertens = ctx.constant(mpf_mertens, "Mertens' constant", "mertens") # Standard functions ctx.sqrt = ctx._wrap_libmp_function(libmp.mpf_sqrt, libmp.mpc_sqrt) ctx.cbrt = ctx._wrap_libmp_function(libmp.mpf_cbrt, libmp.mpc_cbrt) ctx.ln = ctx._wrap_libmp_function(libmp.mpf_log, libmp.mpc_log) ctx.atan = ctx._wrap_libmp_function(libmp.mpf_atan, libmp.mpc_atan) ctx.exp = ctx._wrap_libmp_function(libmp.mpf_exp, libmp.mpc_exp) ctx.expj = ctx._wrap_libmp_function(libmp.mpf_expj, libmp.mpc_expj) ctx.expjpi = ctx._wrap_libmp_function(libmp.mpf_expjpi, libmp.mpc_expjpi) ctx.sin = ctx._wrap_libmp_function(libmp.mpf_sin, libmp.mpc_sin) ctx.cos = ctx._wrap_libmp_function(libmp.mpf_cos, libmp.mpc_cos) ctx.tan = ctx._wrap_libmp_function(libmp.mpf_tan, libmp.mpc_tan) ctx.sinh = ctx._wrap_libmp_function(libmp.mpf_sinh, libmp.mpc_sinh) ctx.cosh = ctx._wrap_libmp_function(libmp.mpf_cosh, libmp.mpc_cosh) ctx.tanh = ctx._wrap_libmp_function(libmp.mpf_tanh, libmp.mpc_tanh) ctx.asin = ctx._wrap_libmp_function(libmp.mpf_asin, libmp.mpc_asin) ctx.acos = ctx._wrap_libmp_function(libmp.mpf_acos, libmp.mpc_acos) ctx.atan = ctx._wrap_libmp_function(libmp.mpf_atan, libmp.mpc_atan) ctx.asinh = ctx._wrap_libmp_function(libmp.mpf_asinh, libmp.mpc_asinh) ctx.acosh = ctx._wrap_libmp_function(libmp.mpf_acosh, libmp.mpc_acosh) ctx.atanh = ctx._wrap_libmp_function(libmp.mpf_atanh, libmp.mpc_atanh) ctx.sinpi = ctx._wrap_libmp_function(libmp.mpf_sin_pi, libmp.mpc_sin_pi) ctx.cospi = ctx._wrap_libmp_function(libmp.mpf_cos_pi, libmp.mpc_cos_pi) ctx.floor = ctx._wrap_libmp_function(libmp.mpf_floor, libmp.mpc_floor) ctx.ceil = ctx._wrap_libmp_function(libmp.mpf_ceil, libmp.mpc_ceil) ctx.nint = ctx._wrap_libmp_function(libmp.mpf_nint, libmp.mpc_nint) ctx.frac = ctx._wrap_libmp_function(libmp.mpf_frac, libmp.mpc_frac) ctx.fib = ctx.fibonacci = ctx._wrap_libmp_function(libmp.mpf_fibonacci, libmp.mpc_fibonacci) ctx.gamma = ctx._wrap_libmp_function(libmp.mpf_gamma, libmp.mpc_gamma) ctx.rgamma = ctx._wrap_libmp_function(libmp.mpf_rgamma, libmp.mpc_rgamma) ctx.loggamma = ctx._wrap_libmp_function(libmp.mpf_loggamma, libmp.mpc_loggamma) ctx.fac = ctx.factorial = ctx._wrap_libmp_function(libmp.mpf_factorial, libmp.mpc_factorial) ctx.gamma_old = ctx._wrap_libmp_function(libmp.mpf_gamma_old, libmp.mpc_gamma_old) ctx.fac_old = ctx.factorial_old = ctx._wrap_libmp_function(libmp.mpf_factorial_old, libmp.mpc_factorial_old) ctx.digamma = ctx._wrap_libmp_function(libmp.mpf_psi0, libmp.mpc_psi0) ctx.harmonic = ctx._wrap_libmp_function(libmp.mpf_harmonic, libmp.mpc_harmonic) ctx.ei = ctx._wrap_libmp_function(libmp.mpf_ei, libmp.mpc_ei) ctx.e1 = ctx._wrap_libmp_function(libmp.mpf_e1, libmp.mpc_e1) ctx._ci = ctx._wrap_libmp_function(libmp.mpf_ci, libmp.mpc_ci) ctx._si = ctx._wrap_libmp_function(libmp.mpf_si, libmp.mpc_si) ctx.ellipk = ctx._wrap_libmp_function(libmp.mpf_ellipk, libmp.mpc_ellipk) ctx._ellipe = ctx._wrap_libmp_function(libmp.mpf_ellipe, libmp.mpc_ellipe) ctx.agm1 = ctx._wrap_libmp_function(libmp.mpf_agm1, libmp.mpc_agm1) ctx._erf = ctx._wrap_libmp_function(libmp.mpf_erf, None) ctx._erfc = ctx._wrap_libmp_function(libmp.mpf_erfc, None) ctx._zeta = ctx._wrap_libmp_function(libmp.mpf_zeta, libmp.mpc_zeta) ctx._altzeta = ctx._wrap_libmp_function(libmp.mpf_altzeta, libmp.mpc_altzeta) # Faster versions ctx.sqrt = getattr(ctx, "_sage_sqrt", ctx.sqrt) ctx.exp = getattr(ctx, "_sage_exp", ctx.exp) ctx.ln = getattr(ctx, "_sage_ln", ctx.ln) ctx.cos = getattr(ctx, "_sage_cos", ctx.cos) ctx.sin = getattr(ctx, "_sage_sin", ctx.sin) def to_fixed(ctx, x, prec): return x.to_fixed(prec) def hypot(ctx, x, y): r""" Computes the Euclidean norm of the vector `(x, y)`, equal to `\sqrt{x^2 + y^2}`. Both `x` and `y` must be real.""" x = ctx.convert(x) y = ctx.convert(y) return ctx.make_mpf(libmp.mpf_hypot(x._mpf_, y._mpf_, *ctx._prec_rounding)) def _gamma_upper_int(ctx, n, z): n = int(ctx._re(n)) if n == 0: return ctx.e1(z) if not hasattr(z, '_mpf_'): raise NotImplementedError prec, rounding = ctx._prec_rounding real, imag = libmp.mpf_expint(n, z._mpf_, prec, rounding, gamma=True) if imag is None: return ctx.make_mpf(real) else: return ctx.make_mpc((real, imag)) def _expint_int(ctx, n, z): n = int(n) if n == 1: return ctx.e1(z) if not hasattr(z, '_mpf_'): raise NotImplementedError prec, rounding = ctx._prec_rounding real, imag = libmp.mpf_expint(n, z._mpf_, prec, rounding) if imag is None: return ctx.make_mpf(real) else: return ctx.make_mpc((real, imag)) def _nthroot(ctx, x, n): if hasattr(x, '_mpf_'): try: return ctx.make_mpf(libmp.mpf_nthroot(x._mpf_, n, *ctx._prec_rounding)) except ComplexResult: if ctx.trap_complex: raise x = (x._mpf_, libmp.fzero) else: x = x._mpc_ return ctx.make_mpc(libmp.mpc_nthroot(x, n, *ctx._prec_rounding)) def _besselj(ctx, n, z): prec, rounding = ctx._prec_rounding if hasattr(z, '_mpf_'): return ctx.make_mpf(libmp.mpf_besseljn(n, z._mpf_, prec, rounding)) elif hasattr(z, '_mpc_'): return ctx.make_mpc(libmp.mpc_besseljn(n, z._mpc_, prec, rounding)) def _agm(ctx, a, b=1): prec, rounding = ctx._prec_rounding if hasattr(a, '_mpf_') and hasattr(b, '_mpf_'): try: v = libmp.mpf_agm(a._mpf_, b._mpf_, prec, rounding) return ctx.make_mpf(v) except ComplexResult: pass if hasattr(a, '_mpf_'): a = (a._mpf_, libmp.fzero) else: a = a._mpc_ if hasattr(b, '_mpf_'): b = (b._mpf_, libmp.fzero) else: b = b._mpc_ return ctx.make_mpc(libmp.mpc_agm(a, b, prec, rounding)) def bernoulli(ctx, n): return ctx.make_mpf(libmp.mpf_bernoulli(int(n), *ctx._prec_rounding)) def _zeta_int(ctx, n): return ctx.make_mpf(libmp.mpf_zeta_int(int(n), *ctx._prec_rounding)) def atan2(ctx, y, x): x = ctx.convert(x) y = ctx.convert(y) return ctx.make_mpf(libmp.mpf_atan2(y._mpf_, x._mpf_, *ctx._prec_rounding)) def psi(ctx, m, z): z = ctx.convert(z) m = int(m) if ctx._is_real_type(z): return ctx.make_mpf(libmp.mpf_psi(m, z._mpf_, *ctx._prec_rounding)) else: return ctx.make_mpc(libmp.mpc_psi(m, z._mpc_, *ctx._prec_rounding)) def cos_sin(ctx, x, **kwargs): if type(x) not in ctx.types: x = ctx.convert(x) prec, rounding = ctx._parse_prec(kwargs) if hasattr(x, '_mpf_'): c, s = libmp.mpf_cos_sin(x._mpf_, prec, rounding) return ctx.make_mpf(c), ctx.make_mpf(s) elif hasattr(x, '_mpc_'): c, s = libmp.mpc_cos_sin(x._mpc_, prec, rounding) return ctx.make_mpc(c), ctx.make_mpc(s) else: return ctx.cos(x, **kwargs), ctx.sin(x, **kwargs) def cospi_sinpi(ctx, x, **kwargs): if type(x) not in ctx.types: x = ctx.convert(x) prec, rounding = ctx._parse_prec(kwargs) if hasattr(x, '_mpf_'): c, s = libmp.mpf_cos_sin_pi(x._mpf_, prec, rounding) return ctx.make_mpf(c), ctx.make_mpf(s) elif hasattr(x, '_mpc_'): c, s = libmp.mpc_cos_sin_pi(x._mpc_, prec, rounding) return ctx.make_mpc(c), ctx.make_mpc(s) else: return ctx.cos(x, **kwargs), ctx.sin(x, **kwargs) def clone(ctx): """ Create a copy of the context, with the same working precision. """ a = ctx.__class__() a.prec = ctx.prec return a # Several helper methods # TODO: add more of these, make consistent, write docstrings, ... def _is_real_type(ctx, x): if hasattr(x, '_mpc_') or type(x) is complex: return False return True def _is_complex_type(ctx, x): if hasattr(x, '_mpc_') or type(x) is complex: return True return False def isnpint(ctx, x): """ Determine if *x* is a nonpositive integer. """ if not x: return True if hasattr(x, '_mpf_'): sign, man, exp, bc = x._mpf_ return sign and exp >= 0 if hasattr(x, '_mpc_'): return not x.imag and ctx.isnpint(x.real) if type(x) in int_types: return x <= 0 if isinstance(x, ctx.mpq): p, q = x._mpq_ if not p: return True return q == 1 and p <= 0 return ctx.isnpint(ctx.convert(x)) def __str__(ctx): lines = ["Mpmath settings:", (" mp.prec = %s" % ctx.prec).ljust(30) + "[default: 53]", (" mp.dps = %s" % ctx.dps).ljust(30) + "[default: 15]", (" mp.trap_complex = %s" % ctx.trap_complex).ljust(30) + "[default: False]", ] return "\n".join(lines) @property def _repr_digits(ctx): return repr_dps(ctx._prec) @property def _str_digits(ctx): return ctx._dps def extraprec(ctx, n, normalize_output=False): """ The block with extraprec(n): increases the precision n bits, executes , and then restores the precision. extraprec(n)(f) returns a decorated version of the function f that increases the working precision by n bits before execution, and restores the parent precision afterwards. With normalize_output=True, it rounds the return value to the parent precision. """ return PrecisionManager(ctx, lambda p: p + n, None, normalize_output) def extradps(ctx, n, normalize_output=False): """ This function is analogous to extraprec (see documentation) but changes the decimal precision instead of the number of bits. """ return PrecisionManager(ctx, None, lambda d: d + n, normalize_output) def workprec(ctx, n, normalize_output=False): """ The block with workprec(n): sets the precision to n bits, executes , and then restores the precision. workprec(n)(f) returns a decorated version of the function f that sets the precision to n bits before execution, and restores the precision afterwards. With normalize_output=True, it rounds the return value to the parent precision. """ return PrecisionManager(ctx, lambda p: n, None, normalize_output) def workdps(ctx, n, normalize_output=False): """ This function is analogous to workprec (see documentation) but changes the decimal precision instead of the number of bits. """ return PrecisionManager(ctx, None, lambda d: n, normalize_output) def autoprec(ctx, f, maxprec=None, catch=(), verbose=False): """ Return a wrapped copy of *f* that repeatedly evaluates *f* with increasing precision until the result converges to the full precision used at the point of the call. This heuristically protects against rounding errors, at the cost of roughly a 2x slowdown compared to manually setting the optimal precision. This method can, however, easily be fooled if the results from *f* depend "discontinuously" on the precision, for instance if catastrophic cancellation can occur. Therefore, :func:`~mpmath.autoprec` should be used judiciously. **Examples** Many functions are sensitive to perturbations of the input arguments. If the arguments are decimal numbers, they may have to be converted to binary at a much higher precision. If the amount of required extra precision is unknown, :func:`~mpmath.autoprec` is convenient:: >>> from mpmath import * >>> mp.dps = 15 >>> mp.pretty = True >>> besselj(5, 125 * 10**28) # Exact input -8.03284785591801e-17 >>> besselj(5, '1.25e30') # Bad 7.12954868316652e-16 >>> autoprec(besselj)(5, '1.25e30') # Good -8.03284785591801e-17 The following fails to converge because `\sin(\pi) = 0` whereas all finite-precision approximations of `\pi` give nonzero values:: >>> autoprec(sin)(pi) Traceback (most recent call last): ... NoConvergence: autoprec: prec increased to 2910 without convergence As the following example shows, :func:`~mpmath.autoprec` can protect against cancellation, but is fooled by too severe cancellation:: >>> x = 1e-10 >>> exp(x)-1; expm1(x); autoprec(lambda t: exp(t)-1)(x) 1.00000008274037e-10 1.00000000005e-10 1.00000000005e-10 >>> x = 1e-50 >>> exp(x)-1; expm1(x); autoprec(lambda t: exp(t)-1)(x) 0.0 1.0e-50 0.0 With *catch*, an exception or list of exceptions to intercept may be specified. The raised exception is interpreted as signaling insufficient precision. This permits, for example, evaluating a function where a too low precision results in a division by zero:: >>> f = lambda x: 1/(exp(x)-1) >>> f(1e-30) Traceback (most recent call last): ... ZeroDivisionError >>> autoprec(f, catch=ZeroDivisionError)(1e-30) 1.0e+30 """ def f_autoprec_wrapped(*args, **kwargs): prec = ctx.prec if maxprec is None: maxprec2 = ctx._default_hyper_maxprec(prec) else: maxprec2 = maxprec try: ctx.prec = prec + 10 try: v1 = f(*args, **kwargs) except catch: v1 = ctx.nan prec2 = prec + 20 while 1: ctx.prec = prec2 try: v2 = f(*args, **kwargs) except catch: v2 = ctx.nan if v1 == v2: break err = ctx.mag(v2-v1) - ctx.mag(v2) if err < (-prec): break if verbose: print("autoprec: target=%s, prec=%s, accuracy=%s" \ % (prec, prec2, -err)) v1 = v2 if prec2 >= maxprec2: raise ctx.NoConvergence(\ "autoprec: prec increased to %i without convergence"\ % prec2) prec2 += int(prec2*2) prec2 = min(prec2, maxprec2) finally: ctx.prec = prec return +v2 return f_autoprec_wrapped def nstr(ctx, x, n=6, **kwargs): """ Convert an ``mpf`` or ``mpc`` to a decimal string literal with *n* significant digits. The small default value for *n* is chosen to make this function useful for printing collections of numbers (lists, matrices, etc). If *x* is a list or tuple, :func:`~mpmath.nstr` is applied recursively to each element. For unrecognized classes, :func:`~mpmath.nstr` simply returns ``str(x)``. The companion function :func:`~mpmath.nprint` prints the result instead of returning it. >>> from mpmath import * >>> nstr([+pi, ldexp(1,-500)]) '[3.14159, 3.05494e-151]' >>> nprint([+pi, ldexp(1,-500)]) [3.14159, 3.05494e-151] """ if isinstance(x, list): return "[%s]" % (", ".join(ctx.nstr(c, n, **kwargs) for c in x)) if isinstance(x, tuple): return "(%s)" % (", ".join(ctx.nstr(c, n, **kwargs) for c in x)) if hasattr(x, '_mpf_'): return to_str(x._mpf_, n, **kwargs) if hasattr(x, '_mpc_'): return "(" + mpc_to_str(x._mpc_, n, **kwargs) + ")" if isinstance(x, basestring): return repr(x) if isinstance(x, ctx.matrix): return x.__nstr__(n, **kwargs) return str(x) def _convert_fallback(ctx, x, strings): if strings and isinstance(x, basestring): if 'j' in x.lower(): x = x.lower().replace(' ', '') match = get_complex.match(x) re = match.group('re') if not re: re = 0 im = match.group('im').rstrip('j') return ctx.mpc(ctx.convert(re), ctx.convert(im)) if hasattr(x, "_mpi_"): a, b = x._mpi_ if a == b: return ctx.make_mpf(a) else: raise ValueError("can only create mpf from zero-width interval") raise TypeError("cannot create mpf from " + repr(x)) def mpmathify(ctx, *args, **kwargs): return ctx.convert(*args, **kwargs) def _parse_prec(ctx, kwargs): if kwargs: if kwargs.get('exact'): return 0, 'f' prec, rounding = ctx._prec_rounding if 'rounding' in kwargs: rounding = kwargs['rounding'] if 'prec' in kwargs: prec = kwargs['prec'] if prec == ctx.inf: return 0, 'f' else: prec = int(prec) elif 'dps' in kwargs: dps = kwargs['dps'] if dps == ctx.inf: return 0, 'f' prec = dps_to_prec(dps) return prec, rounding return ctx._prec_rounding _exact_overflow_msg = "the exact result does not fit in memory" _hypsum_msg = """hypsum() failed to converge to the requested %i bits of accuracy using a working precision of %i bits. Try with a higher maxprec, maxterms, or set zeroprec.""" def hypsum(ctx, p, q, flags, coeffs, z, accurate_small=True, **kwargs): if hasattr(z, "_mpf_"): key = p, q, flags, 'R' v = z._mpf_ elif hasattr(z, "_mpc_"): key = p, q, flags, 'C' v = z._mpc_ if key not in ctx.hyp_summators: ctx.hyp_summators[key] = libmp.make_hyp_summator(key)[1] summator = ctx.hyp_summators[key] prec = ctx.prec maxprec = kwargs.get('maxprec', ctx._default_hyper_maxprec(prec)) extraprec = 50 epsshift = 25 # Jumps in magnitude occur when parameters are close to negative # integers. We must ensure that these terms are included in # the sum and added accurately magnitude_check = {} max_total_jump = 0 for i, c in enumerate(coeffs): if flags[i] == 'Z': if i >= p and c <= 0: ok = False for ii, cc in enumerate(coeffs[:p]): # Note: c <= cc or c < cc, depending on convention if flags[ii] == 'Z' and cc <= 0 and c <= cc: ok = True if not ok: raise ZeroDivisionError("pole in hypergeometric series") continue n, d = ctx.nint_distance(c) n = -int(n) d = -d if i >= p and n >= 0 and d > 4: if n in magnitude_check: magnitude_check[n] += d else: magnitude_check[n] = d extraprec = max(extraprec, d - prec + 60) max_total_jump += abs(d) while 1: if extraprec > maxprec: raise ValueError(ctx._hypsum_msg % (prec, prec+extraprec)) wp = prec + extraprec if magnitude_check: mag_dict = dict((n,None) for n in magnitude_check) else: mag_dict = {} zv, have_complex, magnitude = summator(coeffs, v, prec, wp, \ epsshift, mag_dict, **kwargs) cancel = -magnitude jumps_resolved = True if extraprec < max_total_jump: for n in mag_dict.values(): if (n is None) or (n < prec): jumps_resolved = False break accurate = (cancel < extraprec-25-5 or not accurate_small) if jumps_resolved: if accurate: break # zero? zeroprec = kwargs.get('zeroprec') if zeroprec is not None: if cancel > zeroprec: if have_complex: return ctx.mpc(0) else: return ctx.zero # Some near-singularities were not included, so increase # precision and repeat until they are extraprec *= 2 # Possible workaround for bad roundoff in fixed-point arithmetic epsshift += 5 extraprec += 5 if type(zv) is tuple: if have_complex: return ctx.make_mpc(zv) else: return ctx.make_mpf(zv) else: return zv def ldexp(ctx, x, n): r""" Computes `x 2^n` efficiently. No rounding is performed. The argument `x` must be a real floating-point number (or possible to convert into one) and `n` must be a Python ``int``. >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> ldexp(1, 10) mpf('1024.0') >>> ldexp(1, -3) mpf('0.125') """ x = ctx.convert(x) return ctx.make_mpf(libmp.mpf_shift(x._mpf_, n)) def frexp(ctx, x): r""" Given a real number `x`, returns `(y, n)` with `y \in [0.5, 1)`, `n` a Python integer, and such that `x = y 2^n`. No rounding is performed. >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> frexp(7.5) (mpf('0.9375'), 3) """ x = ctx.convert(x) y, n = libmp.mpf_frexp(x._mpf_) return ctx.make_mpf(y), n def fneg(ctx, x, **kwargs): """ Negates the number *x*, giving a floating-point result, optionally using a custom precision and rounding mode. See the documentation of :func:`~mpmath.fadd` for a detailed description of how to specify precision and rounding. **Examples** An mpmath number is returned:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> fneg(2.5) mpf('-2.5') >>> fneg(-5+2j) mpc(real='5.0', imag='-2.0') Precise control over rounding is possible:: >>> x = fadd(2, 1e-100, exact=True) >>> fneg(x) mpf('-2.0') >>> fneg(x, rounding='f') mpf('-2.0000000000000004') Negating with and without roundoff:: >>> n = 200000000000000000000001 >>> print(int(-mpf(n))) -200000000000000016777216 >>> print(int(fneg(n))) -200000000000000016777216 >>> print(int(fneg(n, prec=log(n,2)+1))) -200000000000000000000001 >>> print(int(fneg(n, dps=log(n,10)+1))) -200000000000000000000001 >>> print(int(fneg(n, prec=inf))) -200000000000000000000001 >>> print(int(fneg(n, dps=inf))) -200000000000000000000001 >>> print(int(fneg(n, exact=True))) -200000000000000000000001 """ prec, rounding = ctx._parse_prec(kwargs) x = ctx.convert(x) if hasattr(x, '_mpf_'): return ctx.make_mpf(mpf_neg(x._mpf_, prec, rounding)) if hasattr(x, '_mpc_'): return ctx.make_mpc(mpc_neg(x._mpc_, prec, rounding)) raise ValueError("Arguments need to be mpf or mpc compatible numbers") def fadd(ctx, x, y, **kwargs): """ Adds the numbers *x* and *y*, giving a floating-point result, optionally using a custom precision and rounding mode. The default precision is the working precision of the context. You can specify a custom precision in bits by passing the *prec* keyword argument, or by providing an equivalent decimal precision with the *dps* keyword argument. If the precision is set to ``+inf``, or if the flag *exact=True* is passed, an exact addition with no rounding is performed. When the precision is finite, the optional *rounding* keyword argument specifies the direction of rounding. Valid options are ``'n'`` for nearest (default), ``'f'`` for floor, ``'c'`` for ceiling, ``'d'`` for down, ``'u'`` for up. **Examples** Using :func:`~mpmath.fadd` with precision and rounding control:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> fadd(2, 1e-20) mpf('2.0') >>> fadd(2, 1e-20, rounding='u') mpf('2.0000000000000004') >>> nprint(fadd(2, 1e-20, prec=100), 25) 2.00000000000000000001 >>> nprint(fadd(2, 1e-20, dps=15), 25) 2.0 >>> nprint(fadd(2, 1e-20, dps=25), 25) 2.00000000000000000001 >>> nprint(fadd(2, 1e-20, exact=True), 25) 2.00000000000000000001 Exact addition avoids cancellation errors, enforcing familiar laws of numbers such as `x+y-x = y`, which don't hold in floating-point arithmetic with finite precision:: >>> x, y = mpf(2), mpf('1e-1000') >>> print(x + y - x) 0.0 >>> print(fadd(x, y, prec=inf) - x) 1.0e-1000 >>> print(fadd(x, y, exact=True) - x) 1.0e-1000 Exact addition can be inefficient and may be impossible to perform with large magnitude differences:: >>> fadd(1, '1e-100000000000000000000', prec=inf) Traceback (most recent call last): ... OverflowError: the exact result does not fit in memory """ prec, rounding = ctx._parse_prec(kwargs) x = ctx.convert(x) y = ctx.convert(y) try: if hasattr(x, '_mpf_'): if hasattr(y, '_mpf_'): return ctx.make_mpf(mpf_add(x._mpf_, y._mpf_, prec, rounding)) if hasattr(y, '_mpc_'): return ctx.make_mpc(mpc_add_mpf(y._mpc_, x._mpf_, prec, rounding)) if hasattr(x, '_mpc_'): if hasattr(y, '_mpf_'): return ctx.make_mpc(mpc_add_mpf(x._mpc_, y._mpf_, prec, rounding)) if hasattr(y, '_mpc_'): return ctx.make_mpc(mpc_add(x._mpc_, y._mpc_, prec, rounding)) except (ValueError, OverflowError): raise OverflowError(ctx._exact_overflow_msg) raise ValueError("Arguments need to be mpf or mpc compatible numbers") def fsub(ctx, x, y, **kwargs): """ Subtracts the numbers *x* and *y*, giving a floating-point result, optionally using a custom precision and rounding mode. See the documentation of :func:`~mpmath.fadd` for a detailed description of how to specify precision and rounding. **Examples** Using :func:`~mpmath.fsub` with precision and rounding control:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> fsub(2, 1e-20) mpf('2.0') >>> fsub(2, 1e-20, rounding='d') mpf('1.9999999999999998') >>> nprint(fsub(2, 1e-20, prec=100), 25) 1.99999999999999999999 >>> nprint(fsub(2, 1e-20, dps=15), 25) 2.0 >>> nprint(fsub(2, 1e-20, dps=25), 25) 1.99999999999999999999 >>> nprint(fsub(2, 1e-20, exact=True), 25) 1.99999999999999999999 Exact subtraction avoids cancellation errors, enforcing familiar laws of numbers such as `x-y+y = x`, which don't hold in floating-point arithmetic with finite precision:: >>> x, y = mpf(2), mpf('1e1000') >>> print(x - y + y) 0.0 >>> print(fsub(x, y, prec=inf) + y) 2.0 >>> print(fsub(x, y, exact=True) + y) 2.0 Exact addition can be inefficient and may be impossible to perform with large magnitude differences:: >>> fsub(1, '1e-100000000000000000000', prec=inf) Traceback (most recent call last): ... OverflowError: the exact result does not fit in memory """ prec, rounding = ctx._parse_prec(kwargs) x = ctx.convert(x) y = ctx.convert(y) try: if hasattr(x, '_mpf_'): if hasattr(y, '_mpf_'): return ctx.make_mpf(mpf_sub(x._mpf_, y._mpf_, prec, rounding)) if hasattr(y, '_mpc_'): return ctx.make_mpc(mpc_sub((x._mpf_, fzero), y._mpc_, prec, rounding)) if hasattr(x, '_mpc_'): if hasattr(y, '_mpf_'): return ctx.make_mpc(mpc_sub_mpf(x._mpc_, y._mpf_, prec, rounding)) if hasattr(y, '_mpc_'): return ctx.make_mpc(mpc_sub(x._mpc_, y._mpc_, prec, rounding)) except (ValueError, OverflowError): raise OverflowError(ctx._exact_overflow_msg) raise ValueError("Arguments need to be mpf or mpc compatible numbers") def fmul(ctx, x, y, **kwargs): """ Multiplies the numbers *x* and *y*, giving a floating-point result, optionally using a custom precision and rounding mode. See the documentation of :func:`~mpmath.fadd` for a detailed description of how to specify precision and rounding. **Examples** The result is an mpmath number:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> fmul(2, 5.0) mpf('10.0') >>> fmul(0.5j, 0.5) mpc(real='0.0', imag='0.25') Avoiding roundoff:: >>> x, y = 10**10+1, 10**15+1 >>> print(x*y) 10000000001000010000000001 >>> print(mpf(x) * mpf(y)) 1.0000000001e+25 >>> print(int(mpf(x) * mpf(y))) 10000000001000011026399232 >>> print(int(fmul(x, y))) 10000000001000011026399232 >>> print(int(fmul(x, y, dps=25))) 10000000001000010000000001 >>> print(int(fmul(x, y, exact=True))) 10000000001000010000000001 Exact multiplication with complex numbers can be inefficient and may be impossible to perform with large magnitude differences between real and imaginary parts:: >>> x = 1+2j >>> y = mpc(2, '1e-100000000000000000000') >>> fmul(x, y) mpc(real='2.0', imag='4.0') >>> fmul(x, y, rounding='u') mpc(real='2.0', imag='4.0000000000000009') >>> fmul(x, y, exact=True) Traceback (most recent call last): ... OverflowError: the exact result does not fit in memory """ prec, rounding = ctx._parse_prec(kwargs) x = ctx.convert(x) y = ctx.convert(y) try: if hasattr(x, '_mpf_'): if hasattr(y, '_mpf_'): return ctx.make_mpf(mpf_mul(x._mpf_, y._mpf_, prec, rounding)) if hasattr(y, '_mpc_'): return ctx.make_mpc(mpc_mul_mpf(y._mpc_, x._mpf_, prec, rounding)) if hasattr(x, '_mpc_'): if hasattr(y, '_mpf_'): return ctx.make_mpc(mpc_mul_mpf(x._mpc_, y._mpf_, prec, rounding)) if hasattr(y, '_mpc_'): return ctx.make_mpc(mpc_mul(x._mpc_, y._mpc_, prec, rounding)) except (ValueError, OverflowError): raise OverflowError(ctx._exact_overflow_msg) raise ValueError("Arguments need to be mpf or mpc compatible numbers") def fdiv(ctx, x, y, **kwargs): """ Divides the numbers *x* and *y*, giving a floating-point result, optionally using a custom precision and rounding mode. See the documentation of :func:`~mpmath.fadd` for a detailed description of how to specify precision and rounding. **Examples** The result is an mpmath number:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> fdiv(3, 2) mpf('1.5') >>> fdiv(2, 3) mpf('0.66666666666666663') >>> fdiv(2+4j, 0.5) mpc(real='4.0', imag='8.0') The rounding direction and precision can be controlled:: >>> fdiv(2, 3, dps=3) # Should be accurate to at least 3 digits mpf('0.6666259765625') >>> fdiv(2, 3, rounding='d') mpf('0.66666666666666663') >>> fdiv(2, 3, prec=60) mpf('0.66666666666666667') >>> fdiv(2, 3, rounding='u') mpf('0.66666666666666674') Checking the error of a division by performing it at higher precision:: >>> fdiv(2, 3) - fdiv(2, 3, prec=100) mpf('-3.7007434154172148e-17') Unlike :func:`~mpmath.fadd`, :func:`~mpmath.fmul`, etc., exact division is not allowed since the quotient of two floating-point numbers generally does not have an exact floating-point representation. (In the future this might be changed to allow the case where the division is actually exact.) >>> fdiv(2, 3, exact=True) Traceback (most recent call last): ... ValueError: division is not an exact operation """ prec, rounding = ctx._parse_prec(kwargs) if not prec: raise ValueError("division is not an exact operation") x = ctx.convert(x) y = ctx.convert(y) if hasattr(x, '_mpf_'): if hasattr(y, '_mpf_'): return ctx.make_mpf(mpf_div(x._mpf_, y._mpf_, prec, rounding)) if hasattr(y, '_mpc_'): return ctx.make_mpc(mpc_div((x._mpf_, fzero), y._mpc_, prec, rounding)) if hasattr(x, '_mpc_'): if hasattr(y, '_mpf_'): return ctx.make_mpc(mpc_div_mpf(x._mpc_, y._mpf_, prec, rounding)) if hasattr(y, '_mpc_'): return ctx.make_mpc(mpc_div(x._mpc_, y._mpc_, prec, rounding)) raise ValueError("Arguments need to be mpf or mpc compatible numbers") def nint_distance(ctx, x): r""" Return `(n,d)` where `n` is the nearest integer to `x` and `d` is an estimate of `\log_2(|x-n|)`. If `d < 0`, `-d` gives the precision (measured in bits) lost to cancellation when computing `x-n`. >>> from mpmath import * >>> n, d = nint_distance(5) >>> print(n); print(d) 5 -inf >>> n, d = nint_distance(mpf(5)) >>> print(n); print(d) 5 -inf >>> n, d = nint_distance(mpf(5.00000001)) >>> print(n); print(d) 5 -26 >>> n, d = nint_distance(mpf(4.99999999)) >>> print(n); print(d) 5 -26 >>> n, d = nint_distance(mpc(5,10)) >>> print(n); print(d) 5 4 >>> n, d = nint_distance(mpc(5,0.000001)) >>> print(n); print(d) 5 -19 """ typx = type(x) if typx in int_types: return int(x), ctx.ninf elif typx is rational.mpq: p, q = x._mpq_ n, r = divmod(p, q) if 2*r >= q: n += 1 elif not r: return n, ctx.ninf # log(p/q-n) = log((p-nq)/q) = log(p-nq) - log(q) d = bitcount(abs(p-n*q)) - bitcount(q) return n, d if hasattr(x, "_mpf_"): re = x._mpf_ im_dist = ctx.ninf elif hasattr(x, "_mpc_"): re, im = x._mpc_ isign, iman, iexp, ibc = im if iman: im_dist = iexp + ibc elif im == fzero: im_dist = ctx.ninf else: raise ValueError("requires a finite number") else: x = ctx.convert(x) if hasattr(x, "_mpf_") or hasattr(x, "_mpc_"): return ctx.nint_distance(x) else: raise TypeError("requires an mpf/mpc") sign, man, exp, bc = re mag = exp+bc # |x| < 0.5 if mag < 0: n = 0 re_dist = mag elif man: # exact integer if exp >= 0: n = man << exp re_dist = ctx.ninf # exact half-integer elif exp == -1: n = (man>>1)+1 re_dist = 0 else: d = (-exp-1) t = man >> d if t & 1: t += 1 man = (t<>1 # int(t)>>1 re_dist = exp+bitcount(man) if sign: n = -n elif re == fzero: re_dist = ctx.ninf n = 0 else: raise ValueError("requires a finite number") return n, max(re_dist, im_dist) def fprod(ctx, factors): r""" Calculates a product containing a finite number of factors (for infinite products, see :func:`~mpmath.nprod`). The factors will be converted to mpmath numbers. >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> fprod([1, 2, 0.5, 7]) mpf('7.0') """ orig = ctx.prec try: v = ctx.one for p in factors: v *= p finally: ctx.prec = orig return +v def rand(ctx): """ Returns an ``mpf`` with value chosen randomly from `[0, 1)`. The number of randomly generated bits in the mantissa is equal to the working precision. """ return ctx.make_mpf(mpf_rand(ctx._prec)) def fraction(ctx, p, q): """ Given Python integers `(p, q)`, returns a lazy ``mpf`` representing the fraction `p/q`. The value is updated with the precision. >>> from mpmath import * >>> mp.dps = 15 >>> a = fraction(1,100) >>> b = mpf(1)/100 >>> print(a); print(b) 0.01 0.01 >>> mp.dps = 30 >>> print(a); print(b) # a will be accurate 0.01 0.0100000000000000002081668171172 >>> mp.dps = 15 """ return ctx.constant(lambda prec, rnd: from_rational(p, q, prec, rnd), '%s/%s' % (p, q)) def absmin(ctx, x): return abs(ctx.convert(x)) def absmax(ctx, x): return abs(ctx.convert(x)) def _as_points(ctx, x): # XXX: remove this? if hasattr(x, '_mpi_'): a, b = x._mpi_ return [ctx.make_mpf(a), ctx.make_mpf(b)] return x ''' def _zetasum(ctx, s, a, b): """ Computes sum of k^(-s) for k = a, a+1, ..., b with a, b both small integers. """ a = int(a) b = int(b) s = ctx.convert(s) prec, rounding = ctx._prec_rounding if hasattr(s, '_mpf_'): v = ctx.make_mpf(libmp.mpf_zetasum(s._mpf_, a, b, prec)) elif hasattr(s, '_mpc_'): v = ctx.make_mpc(libmp.mpc_zetasum(s._mpc_, a, b, prec)) return v ''' def _zetasum_fast(ctx, s, a, n, derivatives=[0], reflect=False): if not (ctx.isint(a) and hasattr(s, "_mpc_")): raise NotImplementedError a = int(a) prec = ctx._prec xs, ys = libmp.mpc_zetasum(s._mpc_, a, n, derivatives, reflect, prec) xs = [ctx.make_mpc(x) for x in xs] ys = [ctx.make_mpc(y) for y in ys] return xs, ys class PrecisionManager: def __init__(self, ctx, precfun, dpsfun, normalize_output=False): self.ctx = ctx self.precfun = precfun self.dpsfun = dpsfun self.normalize_output = normalize_output def __call__(self, f): def g(*args, **kwargs): orig = self.ctx.prec try: if self.precfun: self.ctx.prec = self.precfun(self.ctx.prec) else: self.ctx.dps = self.dpsfun(self.ctx.dps) if self.normalize_output: v = f(*args, **kwargs) if type(v) is tuple: return tuple([+a for a in v]) return +v else: return f(*args, **kwargs) finally: self.ctx.prec = orig g.__name__ = f.__name__ g.__doc__ = f.__doc__ return g def __enter__(self): self.origp = self.ctx.prec if self.precfun: self.ctx.prec = self.precfun(self.ctx.prec) else: self.ctx.dps = self.dpsfun(self.ctx.dps) def __exit__(self, exc_type, exc_val, exc_tb): self.ctx.prec = self.origp return False if __name__ == '__main__': import doctest doctest.testmod() sympy-0.7.4.1/sympy/mpmath/visualization.py0000644000175000017500000002177412253362407021201 0ustar georgeskgeorgesk""" Plotting (requires matplotlib) """ from colorsys import hsv_to_rgb, hls_to_rgb from .libmp import NoConvergence from .libmp.backend import xrange class VisualizationMethods(object): plot_ignore = (ValueError, ArithmeticError, ZeroDivisionError, NoConvergence) def plot(ctx, f, xlim=[-5,5], ylim=None, points=200, file=None, dpi=None, singularities=[], axes=None): r""" Shows a simple 2D plot of a function `f(x)` or list of functions `[f_0(x), f_1(x), \ldots, f_n(x)]` over a given interval specified by *xlim*. Some examples:: plot(lambda x: exp(x)*li(x), [1, 4]) plot([cos, sin], [-4, 4]) plot([fresnels, fresnelc], [-4, 4]) plot([sqrt, cbrt], [-4, 4]) plot(lambda t: zeta(0.5+t*j), [-20, 20]) plot([floor, ceil, abs, sign], [-5, 5]) Points where the function raises a numerical exception or returns an infinite value are removed from the graph. Singularities can also be excluded explicitly as follows (useful for removing erroneous vertical lines):: plot(cot, ylim=[-5, 5]) # bad plot(cot, ylim=[-5, 5], singularities=[-pi, 0, pi]) # good For parts where the function assumes complex values, the real part is plotted with dashes and the imaginary part is plotted with dots. .. note :: This function requires matplotlib (pylab). """ if file: axes = None fig = None if not axes: import pylab fig = pylab.figure() axes = fig.add_subplot(111) if not isinstance(f, (tuple, list)): f = [f] a, b = xlim colors = ['b', 'r', 'g', 'm', 'k'] for n, func in enumerate(f): x = ctx.arange(a, b, (b-a)/float(points)) segments = [] segment = [] in_complex = False for i in xrange(len(x)): try: if i != 0: for sing in singularities: if x[i-1] <= sing and x[i] >= sing: raise ValueError v = func(x[i]) if ctx.isnan(v) or abs(v) > 1e300: raise ValueError if hasattr(v, "imag") and v.imag: re = float(v.real) im = float(v.imag) if not in_complex: in_complex = True segments.append(segment) segment = [] segment.append((float(x[i]), re, im)) else: if in_complex: in_complex = False segments.append(segment) segment = [] if hasattr(v, "real"): v = v.real segment.append((float(x[i]), v)) except ctx.plot_ignore: if segment: segments.append(segment) segment = [] if segment: segments.append(segment) for segment in segments: x = [s[0] for s in segment] y = [s[1] for s in segment] if not x: continue c = colors[n % len(colors)] if len(segment[0]) == 3: z = [s[2] for s in segment] axes.plot(x, y, '--'+c, linewidth=3) axes.plot(x, z, ':'+c, linewidth=3) else: axes.plot(x, y, c, linewidth=3) axes.set_xlim([float(_) for _ in xlim]) if ylim: axes.set_ylim([float(_) for _ in ylim]) axes.set_xlabel('x') axes.set_ylabel('f(x)') axes.grid(True) if fig: if file: pylab.savefig(file, dpi=dpi) else: pylab.show() def default_color_function(ctx, z): if ctx.isinf(z): return (1.0, 1.0, 1.0) if ctx.isnan(z): return (0.5, 0.5, 0.5) pi = 3.1415926535898 a = (float(ctx.arg(z)) + ctx.pi) / (2*ctx.pi) a = (a + 0.5) % 1.0 b = 1.0 - float(1/(1.0+abs(z)**0.3)) return hls_to_rgb(a, b, 0.8) def cplot(ctx, f, re=[-5,5], im=[-5,5], points=2000, color=None, verbose=False, file=None, dpi=None, axes=None): """ Plots the given complex-valued function *f* over a rectangular part of the complex plane specified by the pairs of intervals *re* and *im*. For example:: cplot(lambda z: z, [-2, 2], [-10, 10]) cplot(exp) cplot(zeta, [0, 1], [0, 50]) By default, the complex argument (phase) is shown as color (hue) and the magnitude is show as brightness. You can also supply a custom color function (*color*). This function should take a complex number as input and return an RGB 3-tuple containing floats in the range 0.0-1.0. To obtain a sharp image, the number of points may need to be increased to 100,000 or thereabout. Since evaluating the function that many times is likely to be slow, the 'verbose' option is useful to display progress. .. note :: This function requires matplotlib (pylab). """ if color is None: color = ctx.default_color_function import pylab if file: axes = None fig = None if not axes: fig = pylab.figure() axes = fig.add_subplot(111) rea, reb = re ima, imb = im dre = reb - rea dim = imb - ima M = int(ctx.sqrt(points*dre/dim)+1) N = int(ctx.sqrt(points*dim/dre)+1) x = pylab.linspace(rea, reb, M) y = pylab.linspace(ima, imb, N) # Note: we have to be careful to get the right rotation. # Test with these plots: # cplot(lambda z: z if z.real < 0 else 0) # cplot(lambda z: z if z.imag < 0 else 0) w = pylab.zeros((N, M, 3)) for n in xrange(N): for m in xrange(M): z = ctx.mpc(x[m], y[n]) try: v = color(f(z)) except ctx.plot_ignore: v = (0.5, 0.5, 0.5) w[n,m] = v if verbose: print(n, "of", N) rea, reb, ima, imb = [float(_) for _ in [rea, reb, ima, imb]] axes.imshow(w, extent=(rea, reb, ima, imb), origin='lower') axes.set_xlabel('Re(z)') axes.set_ylabel('Im(z)') if fig: if file: pylab.savefig(file, dpi=dpi) else: pylab.show() def splot(ctx, f, u=[-5,5], v=[-5,5], points=100, keep_aspect=True, \ wireframe=False, file=None, dpi=None, axes=None): """ Plots the surface defined by `f`. If `f` returns a single component, then this plots the surface defined by `z = f(x,y)` over the rectangular domain with `x = u` and `y = v`. If `f` returns three components, then this plots the parametric surface `x, y, z = f(u,v)` over the pairs of intervals `u` and `v`. For example, to plot a simple function:: >>> from mpmath import * >>> f = lambda x, y: sin(x+y)*cos(y) >>> splot(f, [-pi,pi], [-pi,pi]) # doctest: +SKIP Plotting a donut:: >>> r, R = 1, 2.5 >>> f = lambda u, v: [r*cos(u), (R+r*sin(u))*cos(v), (R+r*sin(u))*sin(v)] >>> splot(f, [0, 2*pi], [0, 2*pi]) # doctest: +SKIP .. note :: This function requires matplotlib (pylab) 0.98.5.3 or higher. """ import pylab import mpl_toolkits.mplot3d as mplot3d if file: axes = None fig = None if not axes: fig = pylab.figure() axes = mplot3d.axes3d.Axes3D(fig) ua, ub = u va, vb = v du = ub - ua dv = vb - va if not isinstance(points, (list, tuple)): points = [points, points] M, N = points u = pylab.linspace(ua, ub, M) v = pylab.linspace(va, vb, N) x, y, z = [pylab.zeros((M, N)) for i in xrange(3)] xab, yab, zab = [[0, 0] for i in xrange(3)] for n in xrange(N): for m in xrange(M): fdata = f(ctx.convert(u[m]), ctx.convert(v[n])) try: x[m,n], y[m,n], z[m,n] = fdata except TypeError: x[m,n], y[m,n], z[m,n] = u[m], v[n], fdata for c, cab in [(x[m,n], xab), (y[m,n], yab), (z[m,n], zab)]: if c < cab[0]: cab[0] = c if c > cab[1]: cab[1] = c if wireframe: axes.plot_wireframe(x, y, z, rstride=4, cstride=4) else: axes.plot_surface(x, y, z, rstride=4, cstride=4) axes.set_xlabel('x') axes.set_ylabel('y') axes.set_zlabel('z') if keep_aspect: dx, dy, dz = [cab[1] - cab[0] for cab in [xab, yab, zab]] maxd = max(dx, dy, dz) if dx < maxd: delta = maxd - dx axes.set_xlim3d(xab[0] - delta / 2.0, xab[1] + delta / 2.0) if dy < maxd: delta = maxd - dy axes.set_ylim3d(yab[0] - delta / 2.0, yab[1] + delta / 2.0) if dz < maxd: delta = maxd - dz axes.set_zlim3d(zab[0] - delta / 2.0, zab[1] + delta / 2.0) if fig: if file: pylab.savefig(file, dpi=dpi) else: pylab.show() VisualizationMethods.plot = plot VisualizationMethods.default_color_function = default_color_function VisualizationMethods.cplot = cplot VisualizationMethods.splot = splot sympy-0.7.4.1/sympy/mpmath/ctx_mp_python.py0000644000175000017500000010752012253362407021165 0ustar georgeskgeorgesk#from ctx_base import StandardBaseContext from .libmp.backend import basestring, exec_ from .libmp import (MPZ, MPZ_ZERO, MPZ_ONE, int_types, repr_dps, round_floor, round_ceiling, dps_to_prec, round_nearest, prec_to_dps, ComplexResult, to_pickable, from_pickable, normalize, from_int, from_float, from_str, to_int, to_float, to_str, from_rational, from_man_exp, fone, fzero, finf, fninf, fnan, mpf_abs, mpf_pos, mpf_neg, mpf_add, mpf_sub, mpf_mul, mpf_mul_int, mpf_div, mpf_rdiv_int, mpf_pow_int, mpf_mod, mpf_eq, mpf_cmp, mpf_lt, mpf_gt, mpf_le, mpf_ge, mpf_hash, mpf_rand, mpf_sum, bitcount, to_fixed, mpc_to_str, mpc_to_complex, mpc_hash, mpc_pos, mpc_is_nonzero, mpc_neg, mpc_conjugate, mpc_abs, mpc_add, mpc_add_mpf, mpc_sub, mpc_sub_mpf, mpc_mul, mpc_mul_mpf, mpc_mul_int, mpc_div, mpc_div_mpf, mpc_pow, mpc_pow_mpf, mpc_pow_int, mpc_mpf_div, mpf_pow, mpf_pi, mpf_degree, mpf_e, mpf_phi, mpf_ln2, mpf_ln10, mpf_euler, mpf_catalan, mpf_apery, mpf_khinchin, mpf_glaisher, mpf_twinprime, mpf_mertens, int_types) from . import rational from . import function_docs new = object.__new__ class mpnumeric(object): """Base class for mpf and mpc.""" __slots__ = [] def __new__(cls, val): raise NotImplementedError class _mpf(mpnumeric): """ An mpf instance holds a real-valued floating-point number. mpf:s work analogously to Python floats, but support arbitrary-precision arithmetic. """ __slots__ = ['_mpf_'] def __new__(cls, val=fzero, **kwargs): """A new mpf can be created from a Python float, an int, a or a decimal string representing a number in floating-point format.""" prec, rounding = cls.context._prec_rounding if kwargs: prec = kwargs.get('prec', prec) if 'dps' in kwargs: prec = dps_to_prec(kwargs['dps']) rounding = kwargs.get('rounding', rounding) if type(val) is cls: sign, man, exp, bc = val._mpf_ if (not man) and exp: return val v = new(cls) v._mpf_ = normalize(sign, man, exp, bc, prec, rounding) return v elif type(val) is tuple: if len(val) == 2: v = new(cls) v._mpf_ = from_man_exp(val[0], val[1], prec, rounding) return v if len(val) == 4: sign, man, exp, bc = val v = new(cls) v._mpf_ = normalize(sign, MPZ(man), exp, bc, prec, rounding) return v raise ValueError else: v = new(cls) v._mpf_ = mpf_pos(cls.mpf_convert_arg(val, prec, rounding), prec, rounding) return v @classmethod def mpf_convert_arg(cls, x, prec, rounding): if isinstance(x, int_types): return from_int(x) if isinstance(x, float): return from_float(x) if isinstance(x, basestring): return from_str(x, prec, rounding) if isinstance(x, cls.context.constant): return x.func(prec, rounding) if hasattr(x, '_mpf_'): return x._mpf_ if hasattr(x, '_mpmath_'): t = cls.context.convert(x._mpmath_(prec, rounding)) if hasattr(t, '_mpf_'): return t._mpf_ if hasattr(x, '_mpi_'): a, b = x._mpi_ if a == b: return a raise ValueError("can only create mpf from zero-width interval") raise TypeError("cannot create mpf from " + repr(x)) @classmethod def mpf_convert_rhs(cls, x): if isinstance(x, int_types): return from_int(x) if isinstance(x, float): return from_float(x) if isinstance(x, complex_types): return cls.context.mpc(x) if isinstance(x, rational.mpq): p, q = x._mpq_ return from_rational(p, q, cls.context.prec) if hasattr(x, '_mpf_'): return x._mpf_ if hasattr(x, '_mpmath_'): t = cls.context.convert(x._mpmath_(*cls.context._prec_rounding)) if hasattr(t, '_mpf_'): return t._mpf_ return t return NotImplemented @classmethod def mpf_convert_lhs(cls, x): x = cls.mpf_convert_rhs(x) if type(x) is tuple: return cls.context.make_mpf(x) return x man_exp = property(lambda self: self._mpf_[1:3]) man = property(lambda self: self._mpf_[1]) exp = property(lambda self: self._mpf_[2]) bc = property(lambda self: self._mpf_[3]) real = property(lambda self: self) imag = property(lambda self: self.context.zero) conjugate = lambda self: self def __getstate__(self): return to_pickable(self._mpf_) def __setstate__(self, val): self._mpf_ = from_pickable(val) def __repr__(s): if s.context.pretty: return str(s) return "mpf('%s')" % to_str(s._mpf_, s.context._repr_digits) def __str__(s): return to_str(s._mpf_, s.context._str_digits) def __hash__(s): return mpf_hash(s._mpf_) def __int__(s): return int(to_int(s._mpf_)) def __long__(s): return long(to_int(s._mpf_)) def __float__(s): return to_float(s._mpf_) def __complex__(s): return complex(float(s)) def __nonzero__(s): return s._mpf_ != fzero __bool__ = __nonzero__ def __abs__(s): cls, new, (prec, rounding) = s._ctxdata v = new(cls) v._mpf_ = mpf_abs(s._mpf_, prec, rounding) return v def __pos__(s): cls, new, (prec, rounding) = s._ctxdata v = new(cls) v._mpf_ = mpf_pos(s._mpf_, prec, rounding) return v def __neg__(s): cls, new, (prec, rounding) = s._ctxdata v = new(cls) v._mpf_ = mpf_neg(s._mpf_, prec, rounding) return v def _cmp(s, t, func): if hasattr(t, '_mpf_'): t = t._mpf_ else: t = s.mpf_convert_rhs(t) if t is NotImplemented: return t return func(s._mpf_, t) def __cmp__(s, t): return s._cmp(t, mpf_cmp) def __lt__(s, t): return s._cmp(t, mpf_lt) def __gt__(s, t): return s._cmp(t, mpf_gt) def __le__(s, t): return s._cmp(t, mpf_le) def __ge__(s, t): return s._cmp(t, mpf_ge) def __ne__(s, t): v = s.__eq__(t) if v is NotImplemented: return v return not v def __rsub__(s, t): cls, new, (prec, rounding) = s._ctxdata if type(t) in int_types: v = new(cls) v._mpf_ = mpf_sub(from_int(t), s._mpf_, prec, rounding) return v t = s.mpf_convert_lhs(t) if t is NotImplemented: return t return t - s def __rdiv__(s, t): cls, new, (prec, rounding) = s._ctxdata if isinstance(t, int_types): v = new(cls) v._mpf_ = mpf_rdiv_int(t, s._mpf_, prec, rounding) return v t = s.mpf_convert_lhs(t) if t is NotImplemented: return t return t / s def __rpow__(s, t): t = s.mpf_convert_lhs(t) if t is NotImplemented: return t return t ** s def __rmod__(s, t): t = s.mpf_convert_lhs(t) if t is NotImplemented: return t return t % s def sqrt(s): return s.context.sqrt(s) def ae(s, t, rel_eps=None, abs_eps=None): return s.context.almosteq(s, t, rel_eps, abs_eps) def to_fixed(self, prec): return to_fixed(self._mpf_, prec) def __round__(self, *args): return round(float(self), *args) mpf_binary_op = """ def %NAME%(self, other): mpf, new, (prec, rounding) = self._ctxdata sval = self._mpf_ if hasattr(other, '_mpf_'): tval = other._mpf_ %WITH_MPF% ttype = type(other) if ttype in int_types: %WITH_INT% elif ttype is float: tval = from_float(other) %WITH_MPF% elif hasattr(other, '_mpc_'): tval = other._mpc_ mpc = type(other) %WITH_MPC% elif ttype is complex: tval = from_float(other.real), from_float(other.imag) mpc = self.context.mpc %WITH_MPC% if isinstance(other, mpnumeric): return NotImplemented try: other = mpf.context.convert(other, strings=False) except TypeError: return NotImplemented return self.%NAME%(other) """ return_mpf = "; obj = new(mpf); obj._mpf_ = val; return obj" return_mpc = "; obj = new(mpc); obj._mpc_ = val; return obj" mpf_pow_same = """ try: val = mpf_pow(sval, tval, prec, rounding) %s except ComplexResult: if mpf.context.trap_complex: raise mpc = mpf.context.mpc val = mpc_pow((sval, fzero), (tval, fzero), prec, rounding) %s """ % (return_mpf, return_mpc) def binary_op(name, with_mpf='', with_int='', with_mpc=''): code = mpf_binary_op code = code.replace("%WITH_INT%", with_int) code = code.replace("%WITH_MPC%", with_mpc) code = code.replace("%WITH_MPF%", with_mpf) code = code.replace("%NAME%", name) np = {} exec_(code, globals(), np) return np[name] _mpf.__eq__ = binary_op('__eq__', 'return mpf_eq(sval, tval)', 'return mpf_eq(sval, from_int(other))', 'return (tval[1] == fzero) and mpf_eq(tval[0], sval)') _mpf.__add__ = binary_op('__add__', 'val = mpf_add(sval, tval, prec, rounding)' + return_mpf, 'val = mpf_add(sval, from_int(other), prec, rounding)' + return_mpf, 'val = mpc_add_mpf(tval, sval, prec, rounding)' + return_mpc) _mpf.__sub__ = binary_op('__sub__', 'val = mpf_sub(sval, tval, prec, rounding)' + return_mpf, 'val = mpf_sub(sval, from_int(other), prec, rounding)' + return_mpf, 'val = mpc_sub((sval, fzero), tval, prec, rounding)' + return_mpc) _mpf.__mul__ = binary_op('__mul__', 'val = mpf_mul(sval, tval, prec, rounding)' + return_mpf, 'val = mpf_mul_int(sval, other, prec, rounding)' + return_mpf, 'val = mpc_mul_mpf(tval, sval, prec, rounding)' + return_mpc) _mpf.__div__ = binary_op('__div__', 'val = mpf_div(sval, tval, prec, rounding)' + return_mpf, 'val = mpf_div(sval, from_int(other), prec, rounding)' + return_mpf, 'val = mpc_mpf_div(sval, tval, prec, rounding)' + return_mpc) _mpf.__mod__ = binary_op('__mod__', 'val = mpf_mod(sval, tval, prec, rounding)' + return_mpf, 'val = mpf_mod(sval, from_int(other), prec, rounding)' + return_mpf, 'raise NotImplementedError("complex modulo")') _mpf.__pow__ = binary_op('__pow__', mpf_pow_same, 'val = mpf_pow_int(sval, other, prec, rounding)' + return_mpf, 'val = mpc_pow((sval, fzero), tval, prec, rounding)' + return_mpc) _mpf.__radd__ = _mpf.__add__ _mpf.__rmul__ = _mpf.__mul__ _mpf.__truediv__ = _mpf.__div__ _mpf.__rtruediv__ = _mpf.__rdiv__ class _constant(_mpf): """Represents a mathematical constant with dynamic precision. When printed or used in an arithmetic operation, a constant is converted to a regular mpf at the working precision. A regular mpf can also be obtained using the operation +x.""" def __new__(cls, func, name, docname=''): a = object.__new__(cls) a.name = name a.func = func a.__doc__ = getattr(function_docs, docname, '') return a def __call__(self, prec=None, dps=None, rounding=None): prec2, rounding2 = self.context._prec_rounding if not prec: prec = prec2 if not rounding: rounding = rounding2 if dps: prec = dps_to_prec(dps) return self.context.make_mpf(self.func(prec, rounding)) @property def _mpf_(self): prec, rounding = self.context._prec_rounding return self.func(prec, rounding) def __repr__(self): return "<%s: %s~>" % (self.name, self.context.nstr(self)) class _mpc(mpnumeric): """ An mpc represents a complex number using a pair of mpf:s (one for the real part and another for the imaginary part.) The mpc class behaves fairly similarly to Python's complex type. """ __slots__ = ['_mpc_'] def __new__(cls, real=0, imag=0): s = object.__new__(cls) if isinstance(real, complex_types): real, imag = real.real, real.imag elif hasattr(real, '_mpc_'): s._mpc_ = real._mpc_ return s real = cls.context.mpf(real) imag = cls.context.mpf(imag) s._mpc_ = (real._mpf_, imag._mpf_) return s real = property(lambda self: self.context.make_mpf(self._mpc_[0])) imag = property(lambda self: self.context.make_mpf(self._mpc_[1])) def __getstate__(self): return to_pickable(self._mpc_[0]), to_pickable(self._mpc_[1]) def __setstate__(self, val): self._mpc_ = from_pickable(val[0]), from_pickable(val[1]) def __repr__(s): if s.context.pretty: return str(s) r = repr(s.real)[4:-1] i = repr(s.imag)[4:-1] return "%s(real=%s, imag=%s)" % (type(s).__name__, r, i) def __str__(s): return "(%s)" % mpc_to_str(s._mpc_, s.context._str_digits) def __complex__(s): return mpc_to_complex(s._mpc_) def __pos__(s): cls, new, (prec, rounding) = s._ctxdata v = new(cls) v._mpc_ = mpc_pos(s._mpc_, prec, rounding) return v def __abs__(s): prec, rounding = s.context._prec_rounding v = new(s.context.mpf) v._mpf_ = mpc_abs(s._mpc_, prec, rounding) return v def __neg__(s): cls, new, (prec, rounding) = s._ctxdata v = new(cls) v._mpc_ = mpc_neg(s._mpc_, prec, rounding) return v def conjugate(s): cls, new, (prec, rounding) = s._ctxdata v = new(cls) v._mpc_ = mpc_conjugate(s._mpc_, prec, rounding) return v def __nonzero__(s): return mpc_is_nonzero(s._mpc_) __bool__ = __nonzero__ def __hash__(s): return mpc_hash(s._mpc_) @classmethod def mpc_convert_lhs(cls, x): try: y = cls.context.convert(x) return y except TypeError: return NotImplemented def __eq__(s, t): if not hasattr(t, '_mpc_'): if isinstance(t, str): return False t = s.mpc_convert_lhs(t) if t is NotImplemented: return t return s.real == t.real and s.imag == t.imag def __ne__(s, t): b = s.__eq__(t) if b is NotImplemented: return b return not b def _compare(*args): raise TypeError("no ordering relation is defined for complex numbers") __gt__ = _compare __le__ = _compare __gt__ = _compare __ge__ = _compare def __add__(s, t): cls, new, (prec, rounding) = s._ctxdata if not hasattr(t, '_mpc_'): t = s.mpc_convert_lhs(t) if t is NotImplemented: return t if hasattr(t, '_mpf_'): v = new(cls) v._mpc_ = mpc_add_mpf(s._mpc_, t._mpf_, prec, rounding) return v v = new(cls) v._mpc_ = mpc_add(s._mpc_, t._mpc_, prec, rounding) return v def __sub__(s, t): cls, new, (prec, rounding) = s._ctxdata if not hasattr(t, '_mpc_'): t = s.mpc_convert_lhs(t) if t is NotImplemented: return t if hasattr(t, '_mpf_'): v = new(cls) v._mpc_ = mpc_sub_mpf(s._mpc_, t._mpf_, prec, rounding) return v v = new(cls) v._mpc_ = mpc_sub(s._mpc_, t._mpc_, prec, rounding) return v def __mul__(s, t): cls, new, (prec, rounding) = s._ctxdata if not hasattr(t, '_mpc_'): if isinstance(t, int_types): v = new(cls) v._mpc_ = mpc_mul_int(s._mpc_, t, prec, rounding) return v t = s.mpc_convert_lhs(t) if t is NotImplemented: return t if hasattr(t, '_mpf_'): v = new(cls) v._mpc_ = mpc_mul_mpf(s._mpc_, t._mpf_, prec, rounding) return v t = s.mpc_convert_lhs(t) v = new(cls) v._mpc_ = mpc_mul(s._mpc_, t._mpc_, prec, rounding) return v def __div__(s, t): cls, new, (prec, rounding) = s._ctxdata if not hasattr(t, '_mpc_'): t = s.mpc_convert_lhs(t) if t is NotImplemented: return t if hasattr(t, '_mpf_'): v = new(cls) v._mpc_ = mpc_div_mpf(s._mpc_, t._mpf_, prec, rounding) return v v = new(cls) v._mpc_ = mpc_div(s._mpc_, t._mpc_, prec, rounding) return v def __pow__(s, t): cls, new, (prec, rounding) = s._ctxdata if isinstance(t, int_types): v = new(cls) v._mpc_ = mpc_pow_int(s._mpc_, t, prec, rounding) return v t = s.mpc_convert_lhs(t) if t is NotImplemented: return t v = new(cls) if hasattr(t, '_mpf_'): v._mpc_ = mpc_pow_mpf(s._mpc_, t._mpf_, prec, rounding) else: v._mpc_ = mpc_pow(s._mpc_, t._mpc_, prec, rounding) return v __radd__ = __add__ def __rsub__(s, t): t = s.mpc_convert_lhs(t) if t is NotImplemented: return t return t - s def __rmul__(s, t): cls, new, (prec, rounding) = s._ctxdata if isinstance(t, int_types): v = new(cls) v._mpc_ = mpc_mul_int(s._mpc_, t, prec, rounding) return v t = s.mpc_convert_lhs(t) if t is NotImplemented: return t return t * s def __rdiv__(s, t): t = s.mpc_convert_lhs(t) if t is NotImplemented: return t return t / s def __rpow__(s, t): t = s.mpc_convert_lhs(t) if t is NotImplemented: return t return t ** s __truediv__ = __div__ __rtruediv__ = __rdiv__ def ae(s, t, rel_eps=None, abs_eps=None): return s.context.almosteq(s, t, rel_eps, abs_eps) complex_types = (complex, _mpc) class PythonMPContext: def __init__(ctx): ctx._prec_rounding = [53, round_nearest] ctx.mpf = type('mpf', (_mpf,), {}) ctx.mpc = type('mpc', (_mpc,), {}) ctx.mpf._ctxdata = [ctx.mpf, new, ctx._prec_rounding] ctx.mpc._ctxdata = [ctx.mpc, new, ctx._prec_rounding] ctx.mpf.context = ctx ctx.mpc.context = ctx ctx.constant = type('constant', (_constant,), {}) ctx.constant._ctxdata = [ctx.mpf, new, ctx._prec_rounding] ctx.constant.context = ctx def make_mpf(ctx, v): a = new(ctx.mpf) a._mpf_ = v return a def make_mpc(ctx, v): a = new(ctx.mpc) a._mpc_ = v return a def default(ctx): ctx._prec = ctx._prec_rounding[0] = 53 ctx._dps = 15 ctx.trap_complex = False def _set_prec(ctx, n): ctx._prec = ctx._prec_rounding[0] = max(1, int(n)) ctx._dps = prec_to_dps(n) def _set_dps(ctx, n): ctx._prec = ctx._prec_rounding[0] = dps_to_prec(n) ctx._dps = max(1, int(n)) prec = property(lambda ctx: ctx._prec, _set_prec) dps = property(lambda ctx: ctx._dps, _set_dps) def convert(ctx, x, strings=True): """ Converts *x* to an ``mpf`` or ``mpc``. If *x* is of type ``mpf``, ``mpc``, ``int``, ``float``, ``complex``, the conversion will be performed losslessly. If *x* is a string, the result will be rounded to the present working precision. Strings representing fractions or complex numbers are permitted. >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> mpmathify(3.5) mpf('3.5') >>> mpmathify('2.1') mpf('2.1000000000000001') >>> mpmathify('3/4') mpf('0.75') >>> mpmathify('2+3j') mpc(real='2.0', imag='3.0') """ if type(x) in ctx.types: return x if isinstance(x, int_types): return ctx.make_mpf(from_int(x)) if isinstance(x, float): return ctx.make_mpf(from_float(x)) if isinstance(x, complex): return ctx.make_mpc((from_float(x.real), from_float(x.imag))) prec, rounding = ctx._prec_rounding if isinstance(x, rational.mpq): p, q = x._mpq_ return ctx.make_mpf(from_rational(p, q, prec)) if strings and isinstance(x, basestring): try: _mpf_ = from_str(x, prec, rounding) return ctx.make_mpf(_mpf_) except ValueError: pass if hasattr(x, '_mpf_'): return ctx.make_mpf(x._mpf_) if hasattr(x, '_mpc_'): return ctx.make_mpc(x._mpc_) if hasattr(x, '_mpmath_'): return ctx.convert(x._mpmath_(prec, rounding)) return ctx._convert_fallback(x, strings) def isnan(ctx, x): """ Return *True* if *x* is a NaN (not-a-number), or for a complex number, whether either the real or complex part is NaN; otherwise return *False*:: >>> from mpmath import * >>> isnan(3.14) False >>> isnan(nan) True >>> isnan(mpc(3.14,2.72)) False >>> isnan(mpc(3.14,nan)) True """ if hasattr(x, "_mpf_"): return x._mpf_ == fnan if hasattr(x, "_mpc_"): return fnan in x._mpc_ if isinstance(x, int_types) or isinstance(x, rational.mpq): return False x = ctx.convert(x) if hasattr(x, '_mpf_') or hasattr(x, '_mpc_'): return ctx.isnan(x) raise TypeError("isnan() needs a number as input") def isinf(ctx, x): """ Return *True* if the absolute value of *x* is infinite; otherwise return *False*:: >>> from mpmath import * >>> isinf(inf) True >>> isinf(-inf) True >>> isinf(3) False >>> isinf(3+4j) False >>> isinf(mpc(3,inf)) True >>> isinf(mpc(inf,3)) True """ if hasattr(x, "_mpf_"): return x._mpf_ in (finf, fninf) if hasattr(x, "_mpc_"): re, im = x._mpc_ return re in (finf, fninf) or im in (finf, fninf) if isinstance(x, int_types) or isinstance(x, rational.mpq): return False x = ctx.convert(x) if hasattr(x, '_mpf_') or hasattr(x, '_mpc_'): return ctx.isinf(x) raise TypeError("isinf() needs a number as input") def isnormal(ctx, x): """ Determine whether *x* is "normal" in the sense of floating-point representation; that is, return *False* if *x* is zero, an infinity or NaN; otherwise return *True*. By extension, a complex number *x* is considered "normal" if its magnitude is normal:: >>> from mpmath import * >>> isnormal(3) True >>> isnormal(0) False >>> isnormal(inf); isnormal(-inf); isnormal(nan) False False False >>> isnormal(0+0j) False >>> isnormal(0+3j) True >>> isnormal(mpc(2,nan)) False """ if hasattr(x, "_mpf_"): return bool(x._mpf_[1]) if hasattr(x, "_mpc_"): re, im = x._mpc_ re_normal = bool(re[1]) im_normal = bool(im[1]) if re == fzero: return im_normal if im == fzero: return re_normal return re_normal and im_normal if isinstance(x, int_types) or isinstance(x, rational.mpq): return bool(x) x = ctx.convert(x) if hasattr(x, '_mpf_') or hasattr(x, '_mpc_'): return ctx.isnormal(x) raise TypeError("isnormal() needs a number as input") def isint(ctx, x, gaussian=False): """ Return *True* if *x* is integer-valued; otherwise return *False*:: >>> from mpmath import * >>> isint(3) True >>> isint(mpf(3)) True >>> isint(3.2) False >>> isint(inf) False Optionally, Gaussian integers can be checked for:: >>> isint(3+0j) True >>> isint(3+2j) False >>> isint(3+2j, gaussian=True) True """ if isinstance(x, int_types): return True if hasattr(x, "_mpf_"): sign, man, exp, bc = xval = x._mpf_ return bool((man and exp >= 0) or xval == fzero) if hasattr(x, "_mpc_"): re, im = x._mpc_ rsign, rman, rexp, rbc = re isign, iman, iexp, ibc = im re_isint = (rman and rexp >= 0) or re == fzero if gaussian: im_isint = (iman and iexp >= 0) or im == fzero return re_isint and im_isint return re_isint and im == fzero if isinstance(x, rational.mpq): p, q = x._mpq_ return p % q == 0 x = ctx.convert(x) if hasattr(x, '_mpf_') or hasattr(x, '_mpc_'): return ctx.isint(x, gaussian) raise TypeError("isint() needs a number as input") def fsum(ctx, terms, absolute=False, squared=False): """ Calculates a sum containing a finite number of terms (for infinite series, see :func:`~mpmath.nsum`). The terms will be converted to mpmath numbers. For len(terms) > 2, this function is generally faster and produces more accurate results than the builtin Python function :func:`sum`. >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> fsum([1, 2, 0.5, 7]) mpf('10.5') With squared=True each term is squared, and with absolute=True the absolute value of each term is used. """ prec, rnd = ctx._prec_rounding real = [] imag = [] other = 0 for term in terms: reval = imval = 0 if hasattr(term, "_mpf_"): reval = term._mpf_ elif hasattr(term, "_mpc_"): reval, imval = term._mpc_ else: term = ctx.convert(term) if hasattr(term, "_mpf_"): reval = term._mpf_ elif hasattr(term, "_mpc_"): reval, imval = term._mpc_ else: if absolute: term = ctx.absmax(term) if squared: term = term**2 other += term continue if imval: if squared: if absolute: real.append(mpf_mul(reval,reval)) real.append(mpf_mul(imval,imval)) else: reval, imval = mpc_pow_int((reval,imval),2,prec+10) real.append(reval) imag.append(imval) elif absolute: real.append(mpc_abs((reval,imval), prec)) else: real.append(reval) imag.append(imval) else: if squared: reval = mpf_mul(reval, reval) elif absolute: reval = mpf_abs(reval) real.append(reval) s = mpf_sum(real, prec, rnd, absolute) if imag: s = ctx.make_mpc((s, mpf_sum(imag, prec, rnd))) else: s = ctx.make_mpf(s) if other == 0: return s else: return s + other def fdot(ctx, A, B=None, conjugate=False): r""" Computes the dot product of the iterables `A` and `B`, .. math :: \sum_{k=0} A_k B_k. Alternatively, :func:`~mpmath.fdot` accepts a single iterable of pairs. In other words, ``fdot(A,B)`` and ``fdot(zip(A,B))`` are equivalent. The elements are automatically converted to mpmath numbers. With ``conjugate=True``, the elements in the second vector will be conjugated: .. math :: \sum_{k=0} A_k \overline{B_k} **Examples** >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> A = [2, 1.5, 3] >>> B = [1, -1, 2] >>> fdot(A, B) mpf('6.5') >>> list(zip(A, B)) [(2, 1), (1.5, -1), (3, 2)] >>> fdot(_) mpf('6.5') >>> A = [2, 1.5, 3j] >>> B = [1+j, 3, -1-j] >>> fdot(A, B) mpc(real='9.5', imag='-1.0') >>> fdot(A, B, conjugate=True) mpc(real='3.5', imag='-5.0') """ if B: A = zip(A, B) prec, rnd = ctx._prec_rounding real = [] imag = [] other = 0 hasattr_ = hasattr types = (ctx.mpf, ctx.mpc) for a, b in A: if type(a) not in types: a = ctx.convert(a) if type(b) not in types: b = ctx.convert(b) a_real = hasattr_(a, "_mpf_") b_real = hasattr_(b, "_mpf_") if a_real and b_real: real.append(mpf_mul(a._mpf_, b._mpf_)) continue a_complex = hasattr_(a, "_mpc_") b_complex = hasattr_(b, "_mpc_") if a_real and b_complex: aval = a._mpf_ bre, bim = b._mpc_ if conjugate: bim = mpf_neg(bim) real.append(mpf_mul(aval, bre)) imag.append(mpf_mul(aval, bim)) elif b_real and a_complex: are, aim = a._mpc_ bval = b._mpf_ real.append(mpf_mul(are, bval)) imag.append(mpf_mul(aim, bval)) elif a_complex and b_complex: #re, im = mpc_mul(a._mpc_, b._mpc_, prec+20) are, aim = a._mpc_ bre, bim = b._mpc_ if conjugate: bim = mpf_neg(bim) real.append(mpf_mul(are, bre)) real.append(mpf_neg(mpf_mul(aim, bim))) imag.append(mpf_mul(are, bim)) imag.append(mpf_mul(aim, bre)) else: if conjugate: other += a*ctx.conj(b) else: other += a*b s = mpf_sum(real, prec, rnd) if imag: s = ctx.make_mpc((s, mpf_sum(imag, prec, rnd))) else: s = ctx.make_mpf(s) if other == 0: return s else: return s + other def _wrap_libmp_function(ctx, mpf_f, mpc_f=None, mpi_f=None, doc=""): """ Given a low-level mpf_ function, and optionally similar functions for mpc_ and mpi_, defines the function as a context method. It is assumed that the return type is the same as that of the input; the exception is that propagation from mpf to mpc is possible by raising ComplexResult. """ def f(x, **kwargs): if type(x) not in ctx.types: x = ctx.convert(x) prec, rounding = ctx._prec_rounding if kwargs: prec = kwargs.get('prec', prec) if 'dps' in kwargs: prec = dps_to_prec(kwargs['dps']) rounding = kwargs.get('rounding', rounding) if hasattr(x, '_mpf_'): try: return ctx.make_mpf(mpf_f(x._mpf_, prec, rounding)) except ComplexResult: # Handle propagation to complex if ctx.trap_complex: raise return ctx.make_mpc(mpc_f((x._mpf_, fzero), prec, rounding)) elif hasattr(x, '_mpc_'): return ctx.make_mpc(mpc_f(x._mpc_, prec, rounding)) raise NotImplementedError("%s of a %s" % (name, type(x))) name = mpf_f.__name__[4:] f.__doc__ = function_docs.__dict__.get(name, "Computes the %s of x" % doc) return f # Called by SpecialFunctions.__init__() @classmethod def _wrap_specfun(cls, name, f, wrap): if wrap: def f_wrapped(ctx, *args, **kwargs): convert = ctx.convert args = [convert(a) for a in args] prec = ctx.prec try: ctx.prec += 10 retval = f(ctx, *args, **kwargs) finally: ctx.prec = prec return +retval else: f_wrapped = f f_wrapped.__doc__ = function_docs.__dict__.get(name, f.__doc__) setattr(cls, name, f_wrapped) def _convert_param(ctx, x): if hasattr(x, "_mpc_"): v, im = x._mpc_ if im != fzero: return x, 'C' elif hasattr(x, "_mpf_"): v = x._mpf_ else: if type(x) in int_types: return int(x), 'Z' p = None if isinstance(x, tuple): p, q = x elif hasattr(x, '_mpq_'): p, q = x._mpq_ elif isinstance(x, basestring) and '/' in x: p, q = x.split('/') p = int(p) q = int(q) if p is not None: if not p % q: return p // q, 'Z' return ctx.mpq(p,q), 'Q' x = ctx.convert(x) if hasattr(x, "_mpc_"): v, im = x._mpc_ if im != fzero: return x, 'C' elif hasattr(x, "_mpf_"): v = x._mpf_ else: return x, 'U' sign, man, exp, bc = v if man: if exp >= -4: if sign: man = -man if exp >= 0: return int(man) << exp, 'Z' if exp >= -4: p, q = int(man), (1<<(-exp)) return ctx.mpq(p,q), 'Q' x = ctx.make_mpf(v) return x, 'R' elif not exp: return 0, 'Z' else: return x, 'U' def _mpf_mag(ctx, x): sign, man, exp, bc = x if man: return exp+bc if x == fzero: return ctx.ninf if x == finf or x == fninf: return ctx.inf return ctx.nan def mag(ctx, x): """ Quick logarithmic magnitude estimate of a number. Returns an integer or infinity `m` such that `|x| <= 2^m`. It is not guaranteed that `m` is an optimal bound, but it will never be too large by more than 2 (and probably not more than 1). **Examples** >>> from mpmath import * >>> mp.pretty = True >>> mag(10), mag(10.0), mag(mpf(10)), int(ceil(log(10,2))) (4, 4, 4, 4) >>> mag(10j), mag(10+10j) (4, 5) >>> mag(0.01), int(ceil(log(0.01,2))) (-6, -6) >>> mag(0), mag(inf), mag(-inf), mag(nan) (-inf, +inf, +inf, nan) """ if hasattr(x, "_mpf_"): return ctx._mpf_mag(x._mpf_) elif hasattr(x, "_mpc_"): r, i = x._mpc_ if r == fzero: return ctx._mpf_mag(i) if i == fzero: return ctx._mpf_mag(r) return 1+max(ctx._mpf_mag(r), ctx._mpf_mag(i)) elif isinstance(x, int_types): if x: return bitcount(abs(x)) return ctx.ninf elif isinstance(x, rational.mpq): p, q = x._mpq_ if p: return 1 + bitcount(abs(p)) - bitcount(q) return ctx.ninf else: x = ctx.convert(x) if hasattr(x, "_mpf_") or hasattr(x, "_mpc_"): return ctx.mag(x) else: raise TypeError("requires an mpf/mpc") sympy-0.7.4.1/sympy/mpmath/math2.py0000644000175000017500000004422612253362407017310 0ustar georgeskgeorgesk""" This module complements the math and cmath builtin modules by providing fast machine precision versions of some additional functions (gamma, ...) and wrapping math/cmath functions so that they can be called with either real or complex arguments. """ import operator import math import cmath # Irrational (?) constants pi = 3.1415926535897932385 e = 2.7182818284590452354 sqrt2 = 1.4142135623730950488 sqrt5 = 2.2360679774997896964 phi = 1.6180339887498948482 ln2 = 0.69314718055994530942 ln10 = 2.302585092994045684 euler = 0.57721566490153286061 catalan = 0.91596559417721901505 khinchin = 2.6854520010653064453 apery = 1.2020569031595942854 logpi = 1.1447298858494001741 def _mathfun_real(f_real, f_complex): def f(x, **kwargs): if type(x) is float: return f_real(x) if type(x) is complex: return f_complex(x) try: x = float(x) return f_real(x) except (TypeError, ValueError): x = complex(x) return f_complex(x) f.__name__ = f_real.__name__ return f def _mathfun(f_real, f_complex): def f(x, **kwargs): if type(x) is complex: return f_complex(x) try: return f_real(float(x)) except (TypeError, ValueError): return f_complex(complex(x)) f.__name__ = f_real.__name__ return f def _mathfun_n(f_real, f_complex): def f(*args, **kwargs): try: return f_real(*(float(x) for x in args)) except (TypeError, ValueError): return f_complex(*(complex(x) for x in args)) f.__name__ = f_real.__name__ return f # Workaround for non-raising log and sqrt in Python 2.5 and 2.4 # on Unix system try: math.log(-2.0) def math_log(x): if x <= 0.0: raise ValueError("math domain error") return math.log(x) def math_sqrt(x): if x < 0.0: raise ValueError("math domain error") return math.sqrt(x) except (ValueError, TypeError): math_log = math.log math_sqrt = math.sqrt pow = _mathfun_n(operator.pow, lambda x, y: complex(x)**y) log = _mathfun_n(math_log, cmath.log) sqrt = _mathfun(math_sqrt, cmath.sqrt) exp = _mathfun_real(math.exp, cmath.exp) cos = _mathfun_real(math.cos, cmath.cos) sin = _mathfun_real(math.sin, cmath.sin) tan = _mathfun_real(math.tan, cmath.tan) acos = _mathfun(math.acos, cmath.acos) asin = _mathfun(math.asin, cmath.asin) atan = _mathfun_real(math.atan, cmath.atan) cosh = _mathfun_real(math.cosh, cmath.cosh) sinh = _mathfun_real(math.sinh, cmath.sinh) tanh = _mathfun_real(math.tanh, cmath.tanh) floor = _mathfun_real(math.floor, lambda z: complex(math.floor(z.real), math.floor(z.imag))) ceil = _mathfun_real(math.ceil, lambda z: complex(math.ceil(z.real), math.ceil(z.imag))) cos_sin = _mathfun_real(lambda x: (math.cos(x), math.sin(x)), lambda z: (cmath.cos(z), cmath.sin(z))) cbrt = _mathfun(lambda x: x**(1./3), lambda z: z**(1./3)) def nthroot(x, n): r = 1./n try: return float(x) ** r except (ValueError, TypeError): return complex(x) ** r def _sinpi_real(x): if x < 0: return -_sinpi_real(-x) n, r = divmod(x, 0.5) r *= pi n %= 4 if n == 0: return math.sin(r) if n == 1: return math.cos(r) if n == 2: return -math.sin(r) if n == 3: return -math.cos(r) def _cospi_real(x): if x < 0: x = -x n, r = divmod(x, 0.5) r *= pi n %= 4 if n == 0: return math.cos(r) if n == 1: return -math.sin(r) if n == 2: return -math.cos(r) if n == 3: return math.sin(r) def _sinpi_complex(z): if z.real < 0: return -_sinpi_complex(-z) n, r = divmod(z.real, 0.5) z = pi*complex(r, z.imag) n %= 4 if n == 0: return cmath.sin(z) if n == 1: return cmath.cos(z) if n == 2: return -cmath.sin(z) if n == 3: return -cmath.cos(z) def _cospi_complex(z): if z.real < 0: z = -z n, r = divmod(z.real, 0.5) z = pi*complex(r, z.imag) n %= 4 if n == 0: return cmath.cos(z) if n == 1: return -cmath.sin(z) if n == 2: return -cmath.cos(z) if n == 3: return cmath.sin(z) cospi = _mathfun_real(_cospi_real, _cospi_complex) sinpi = _mathfun_real(_sinpi_real, _sinpi_complex) def tanpi(x): try: return sinpi(x) / cospi(x) except OverflowError: if complex(x).imag > 10: return 1j if complex(x).imag < 10: return -1j raise def cotpi(x): try: return cospi(x) / sinpi(x) except OverflowError: if complex(x).imag > 10: return -1j if complex(x).imag < 10: return 1j raise INF = 1e300*1e300 NINF = -INF NAN = INF-INF EPS = 2.2204460492503131e-16 _exact_gamma = (INF, 1.0, 1.0, 2.0, 6.0, 24.0, 120.0, 720.0, 5040.0, 40320.0, 362880.0, 3628800.0, 39916800.0, 479001600.0, 6227020800.0, 87178291200.0, 1307674368000.0, 20922789888000.0, 355687428096000.0, 6402373705728000.0, 121645100408832000.0, 2432902008176640000.0) _max_exact_gamma = len(_exact_gamma)-1 # Lanczos coefficients used by the GNU Scientific Library _lanczos_g = 7 _lanczos_p = (0.99999999999980993, 676.5203681218851, -1259.1392167224028, 771.32342877765313, -176.61502916214059, 12.507343278686905, -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7) def _gamma_real(x): _intx = int(x) if _intx == x: if _intx <= 0: #return (-1)**_intx * INF raise ZeroDivisionError("gamma function pole") if _intx <= _max_exact_gamma: return _exact_gamma[_intx] if x < 0.5: # TODO: sinpi return pi / (_sinpi_real(x)*_gamma_real(1-x)) else: x -= 1.0 r = _lanczos_p[0] for i in range(1, _lanczos_g+2): r += _lanczos_p[i]/(x+i) t = x + _lanczos_g + 0.5 return 2.506628274631000502417 * t**(x+0.5) * math.exp(-t) * r def _gamma_complex(x): if not x.imag: return complex(_gamma_real(x.real)) if x.real < 0.5: # TODO: sinpi return pi / (_sinpi_complex(x)*_gamma_complex(1-x)) else: x -= 1.0 r = _lanczos_p[0] for i in range(1, _lanczos_g+2): r += _lanczos_p[i]/(x+i) t = x + _lanczos_g + 0.5 return 2.506628274631000502417 * t**(x+0.5) * cmath.exp(-t) * r gamma = _mathfun_real(_gamma_real, _gamma_complex) def rgamma(x): try: return 1./gamma(x) except ZeroDivisionError: return x*0.0 def factorial(x): return gamma(x+1.0) def arg(x): if type(x) is float: return math.atan2(0.0,x) return math.atan2(x.imag,x.real) # XXX: broken for negatives def loggamma(x): if type(x) not in (float, complex): try: x = float(x) except (ValueError, TypeError): x = complex(x) try: xreal = x.real ximag = x.imag except AttributeError: # py2.5 xreal = x ximag = 0.0 # Reflection formula # http://functions.wolfram.com/GammaBetaErf/LogGamma/16/01/01/0003/ if xreal < 0.0: if abs(x) < 0.5: v = log(gamma(x)) if ximag == 0: v = v.conjugate() return v z = 1-x try: re = z.real im = z.imag except AttributeError: # py2.5 re = z im = 0.0 refloor = floor(re) if im == 0.0: imsign = 0 elif im < 0.0: imsign = -1 else: imsign = 1 return (-pi*1j)*abs(refloor)*(1-abs(imsign)) + logpi - \ log(sinpi(z-refloor)) - loggamma(z) + 1j*pi*refloor*imsign if x == 1.0 or x == 2.0: return x*0 p = 0. while abs(x) < 11: p -= log(x) x += 1.0 s = 0.918938533204672742 + (x-0.5)*log(x) - x r = 1./x r2 = r*r s += 0.083333333333333333333*r r *= r2 s += -0.0027777777777777777778*r r *= r2 s += 0.00079365079365079365079*r r *= r2 s += -0.0005952380952380952381*r r *= r2 s += 0.00084175084175084175084*r r *= r2 s += -0.0019175269175269175269*r r *= r2 s += 0.0064102564102564102564*r r *= r2 s += -0.02955065359477124183*r return s + p _psi_coeff = [ 0.083333333333333333333, -0.0083333333333333333333, 0.003968253968253968254, -0.0041666666666666666667, 0.0075757575757575757576, -0.021092796092796092796, 0.083333333333333333333, -0.44325980392156862745, 3.0539543302701197438, -26.456212121212121212] def _digamma_real(x): _intx = int(x) if _intx == x: if _intx <= 0: raise ZeroDivisionError("polygamma pole") if x < 0.5: x = 1.0-x s = pi*cotpi(x) else: s = 0.0 while x < 10.0: s -= 1.0/x x += 1.0 x2 = x**-2 t = x2 for c in _psi_coeff: s -= c*t if t < 1e-20: break t *= x2 return s + math_log(x) - 0.5/x def _digamma_complex(x): if not x.imag: return complex(_digamma_real(x.real)) if x.real < 0.5: x = 1.0-x s = pi*cotpi(x) else: s = 0.0 while abs(x) < 10.0: s -= 1.0/x x += 1.0 x2 = x**-2 t = x2 for c in _psi_coeff: s -= c*t if abs(t) < 1e-20: break t *= x2 return s + cmath.log(x) - 0.5/x digamma = _mathfun_real(_digamma_real, _digamma_complex) # TODO: could implement complex erf and erfc here. Need # to find an accurate method (avoiding cancellation) # for approx. 1 < abs(x) < 9. _erfc_coeff_P = [ 1.0000000161203922312, 2.1275306946297962644, 2.2280433377390253297, 1.4695509105618423961, 0.66275911699770787537, 0.20924776504163751585, 0.045459713768411264339, 0.0063065951710717791934, 0.00044560259661560421715][::-1] _erfc_coeff_Q = [ 1.0000000000000000000, 3.2559100272784894318, 4.9019435608903239131, 4.4971472894498014205, 2.7845640601891186528, 1.2146026030046904138, 0.37647108453729465912, 0.080970149639040548613, 0.011178148899483545902, 0.00078981003831980423513][::-1] def _polyval(coeffs, x): p = coeffs[0] for c in coeffs[1:]: p = c + x*p return p def _erf_taylor(x): # Taylor series assuming 0 <= x <= 1 x2 = x*x s = t = x n = 1 while abs(t) > 1e-17: t *= x2/n s -= t/(n+n+1) n += 1 t *= x2/n s += t/(n+n+1) n += 1 return 1.1283791670955125739*s def _erfc_mid(x): # Rational approximation assuming 0 <= x <= 9 return exp(-x*x)*_polyval(_erfc_coeff_P,x)/_polyval(_erfc_coeff_Q,x) def _erfc_asymp(x): # Asymptotic expansion assuming x >= 9 x2 = x*x v = exp(-x2)/x*0.56418958354775628695 r = t = 0.5 / x2 s = 1.0 for n in range(1,22,4): s -= t t *= r * (n+2) s += t t *= r * (n+4) if abs(t) < 1e-17: break return s * v def erf(x): """ erf of a real number. """ x = float(x) if x != x: return x if x < 0.0: return -erf(-x) if x >= 1.0: if x >= 6.0: return 1.0 return 1.0 - _erfc_mid(x) return _erf_taylor(x) def erfc(x): """ erfc of a real number. """ x = float(x) if x != x: return x if x < 0.0: if x < -6.0: return 2.0 return 2.0-erfc(-x) if x > 9.0: return _erfc_asymp(x) if x >= 1.0: return _erfc_mid(x) return 1.0 - _erf_taylor(x) gauss42 = [\ (0.99839961899006235, 0.0041059986046490839), (-0.99839961899006235, 0.0041059986046490839), (0.9915772883408609, 0.009536220301748501), (-0.9915772883408609,0.009536220301748501), (0.97934250806374812, 0.014922443697357493), (-0.97934250806374812, 0.014922443697357493), (0.96175936533820439,0.020227869569052644), (-0.96175936533820439, 0.020227869569052644), (0.93892355735498811, 0.025422959526113047), (-0.93892355735498811,0.025422959526113047), (0.91095972490412735, 0.030479240699603467), (-0.91095972490412735, 0.030479240699603467), (0.87802056981217269,0.03536907109759211), (-0.87802056981217269, 0.03536907109759211), (0.8402859832618168, 0.040065735180692258), (-0.8402859832618168,0.040065735180692258), (0.7979620532554873, 0.044543577771965874), (-0.7979620532554873, 0.044543577771965874), (0.75127993568948048,0.048778140792803244), (-0.75127993568948048, 0.048778140792803244), (0.70049459055617114, 0.052746295699174064), (-0.70049459055617114,0.052746295699174064), (0.64588338886924779, 0.056426369358018376), (-0.64588338886924779, 0.056426369358018376), (0.58774459748510932, 0.059798262227586649), (-0.58774459748510932, 0.059798262227586649), (0.5263957499311922, 0.062843558045002565), (-0.5263957499311922, 0.062843558045002565), (0.46217191207042191, 0.065545624364908975), (-0.46217191207042191, 0.065545624364908975), (0.39542385204297503, 0.067889703376521934), (-0.39542385204297503, 0.067889703376521934), (0.32651612446541151, 0.069862992492594159), (-0.32651612446541151, 0.069862992492594159), (0.25582507934287907, 0.071454714265170971), (-0.25582507934287907, 0.071454714265170971), (0.18373680656485453, 0.072656175243804091), (-0.18373680656485453, 0.072656175243804091), (0.11064502720851986, 0.073460813453467527), (-0.11064502720851986, 0.073460813453467527), (0.036948943165351772, 0.073864234232172879), (-0.036948943165351772, 0.073864234232172879)] EI_ASYMP_CONVERGENCE_RADIUS = 40.0 def ei_asymp(z, _e1=False): r = 1./z s = t = 1.0 k = 1 while 1: t *= k*r s += t if abs(t) < 1e-16: break k += 1 v = s*exp(z)/z if _e1: if type(z) is complex: zreal = z.real zimag = z.imag else: zreal = z zimag = 0.0 if zimag == 0.0 and zreal > 0.0: v += pi*1j else: if type(z) is complex: if z.imag > 0: v += pi*1j if z.imag < 0: v -= pi*1j return v def ei_taylor(z, _e1=False): s = t = z k = 2 while 1: t = t*z/k term = t/k if abs(term) < 1e-17: break s += term k += 1 s += euler if _e1: s += log(-z) else: if type(z) is float or z.imag == 0.0: s += math_log(abs(z)) else: s += cmath.log(z) return s def ei(z, _e1=False): typez = type(z) if typez not in (float, complex): try: z = float(z) typez = float except (TypeError, ValueError): z = complex(z) typez = complex if not z: return -INF absz = abs(z) if absz > EI_ASYMP_CONVERGENCE_RADIUS: return ei_asymp(z, _e1) elif absz <= 2.0 or (typez is float and z > 0.0): return ei_taylor(z, _e1) # Integrate, starting from whichever is smaller of a Taylor # series value or an asymptotic series value if typez is complex and z.real > 0.0: zref = z / absz ref = ei_taylor(zref, _e1) else: zref = EI_ASYMP_CONVERGENCE_RADIUS * z / absz ref = ei_asymp(zref, _e1) C = (zref-z)*0.5 D = (zref+z)*0.5 s = 0.0 if type(z) is complex: _exp = cmath.exp else: _exp = math.exp for x,w in gauss42: t = C*x+D s += w*_exp(t)/t ref -= C*s return ref def e1(z): # hack to get consistent signs if the imaginary part if 0 # and signed typez = type(z) if type(z) not in (float, complex): try: z = float(z) typez = float except (TypeError, ValueError): z = complex(z) typez = complex if typez is complex and not z.imag: z = complex(z.real, 0.0) # end hack return -ei(-z, _e1=True) _zeta_int = [\ -0.5, 0.0, 1.6449340668482264365,1.2020569031595942854,1.0823232337111381915, 1.0369277551433699263,1.0173430619844491397,1.0083492773819228268, 1.0040773561979443394,1.0020083928260822144,1.0009945751278180853, 1.0004941886041194646,1.0002460865533080483,1.0001227133475784891, 1.0000612481350587048,1.0000305882363070205,1.0000152822594086519, 1.0000076371976378998,1.0000038172932649998,1.0000019082127165539, 1.0000009539620338728,1.0000004769329867878,1.0000002384505027277, 1.0000001192199259653,1.0000000596081890513,1.0000000298035035147, 1.0000000149015548284] _zeta_P = [-3.50000000087575873, -0.701274355654678147, -0.0672313458590012612, -0.00398731457954257841, -0.000160948723019303141, -4.67633010038383371e-6, -1.02078104417700585e-7, -1.68030037095896287e-9, -1.85231868742346722e-11][::-1] _zeta_Q = [1.00000000000000000, -0.936552848762465319, -0.0588835413263763741, -0.00441498861482948666, -0.000143416758067432622, -5.10691659585090782e-6, -9.58813053268913799e-8, -1.72963791443181972e-9, -1.83527919681474132e-11][::-1] _zeta_1 = [3.03768838606128127e-10, -1.21924525236601262e-8, 2.01201845887608893e-7, -1.53917240683468381e-6, -5.09890411005967954e-7, 0.000122464707271619326, -0.000905721539353130232, -0.00239315326074843037, 0.084239750013159168, 0.418938517907442414, 0.500000001921884009] _zeta_0 = [-3.46092485016748794e-10, -6.42610089468292485e-9, 1.76409071536679773e-7, -1.47141263991560698e-6, -6.38880222546167613e-7, 0.000122641099800668209, -0.000905894913516772796, -0.00239303348507992713, 0.0842396947501199816, 0.418938533204660256, 0.500000000000000052] def zeta(s): """ Riemann zeta function, real argument """ if not isinstance(s, (float, int)): try: s = float(s) except (ValueError, TypeError): try: s = complex(s) if not s.imag: return complex(zeta(s.real)) except (ValueError, TypeError): pass raise NotImplementedError if s == 1: raise ValueError("zeta(1) pole") if s >= 27: return 1.0 + 2.0**(-s) + 3.0**(-s) n = int(s) if n == s: if n >= 0: return _zeta_int[n] if not (n % 2): return 0.0 if s <= 0.0: return 2.**s*pi**(s-1)*_sinpi_real(0.5*s)*_gamma_real(1-s)*zeta(1-s) if s <= 2.0: if s <= 1.0: return _polyval(_zeta_0,s)/(s-1) return _polyval(_zeta_1,s)/(s-1) z = _polyval(_zeta_P,s) / _polyval(_zeta_Q,s) return 1.0 + 2.0**(-s) + 3.0**(-s) + 4.0**(-s)*z sympy-0.7.4.1/sympy/mpmath/rational.py0000644000175000017500000001267212253362407020106 0ustar georgeskgeorgeskimport operator import sys from .libmp import int_types, mpf_hash, bitcount, from_man_exp, HASH_MODULUS new = object.__new__ def create_reduced(p, q, _cache={}): key = p, q if key in _cache: return _cache[key] x, y = p, q while y: x, y = y, x % y if x != 1: p //= x q //= x v = new(mpq) v._mpq_ = p, q # Speedup integers, half-integers and other small fractions if q <= 4 and abs(key[0]) < 100: _cache[key] = v return v class mpq(object): """ Exact rational type, currently only intended for internal use. """ __slots__ = ["_mpq_"] def __new__(cls, p, q=1): if type(p) is tuple: p, q = p elif hasattr(p, '_mpq_'): p, q = p._mpq_ return create_reduced(p, q) def __repr__(s): return "mpq(%s,%s)" % s._mpq_ def __str__(s): return "(%s/%s)" % s._mpq_ def __int__(s): a, b = s._mpq_ return a // b def __nonzero__(s): return bool(s._mpq_[0]) __bool__ = __nonzero__ def __hash__(s): a, b = s._mpq_ if sys.version >= "3.2": inverse = pow(b, HASH_MODULUS-2, HASH_MODULUS) if not inverse: h = sys.hash_info.inf else: h = (abs(a) * inverse) % HASH_MODULUS if a < 0: h = -h if h == -1: h = -2 return h else: if b == 1: return hash(a) # Power of two: mpf compatible hash if not (b & (b-1)): return mpf_hash(from_man_exp(a, 1-bitcount(b))) return hash((a,b)) def __eq__(s, t): ttype = type(t) if ttype is mpq: return s._mpq_ == t._mpq_ if ttype in int_types: a, b = s._mpq_ if b != 1: return False return a == t return NotImplemented def __ne__(s, t): ttype = type(t) if ttype is mpq: return s._mpq_ != t._mpq_ if ttype in int_types: a, b = s._mpq_ if b != 1: return True return a != t return NotImplemented def _cmp(s, t, op): ttype = type(t) if ttype in int_types: a, b = s._mpq_ return op(a, t*b) if ttype is mpq: a, b = s._mpq_ c, d = t._mpq_ return op(a*d, b*c) return NotImplementedError def __lt__(s, t): return s._cmp(t, operator.lt) def __le__(s, t): return s._cmp(t, operator.le) def __gt__(s, t): return s._cmp(t, operator.gt) def __ge__(s, t): return s._cmp(t, operator.ge) def __abs__(s): a, b = s._mpq_ if a >= 0: return s v = new(mpq) v._mpq_ = -a, b return v def __neg__(s): a, b = s._mpq_ v = new(mpq) v._mpq_ = -a, b return v def __pos__(s): return s def __add__(s, t): ttype = type(t) if ttype is mpq: a, b = s._mpq_ c, d = t._mpq_ return create_reduced(a*d+b*c, b*d) if ttype in int_types: a, b = s._mpq_ v = new(mpq) v._mpq_ = a+b*t, b return v return NotImplemented __radd__ = __add__ def __sub__(s, t): ttype = type(t) if ttype is mpq: a, b = s._mpq_ c, d = t._mpq_ return create_reduced(a*d-b*c, b*d) if ttype in int_types: a, b = s._mpq_ v = new(mpq) v._mpq_ = a-b*t, b return v return NotImplemented def __rsub__(s, t): ttype = type(t) if ttype is mpq: a, b = s._mpq_ c, d = t._mpq_ return create_reduced(b*c-a*d, b*d) if ttype in int_types: a, b = s._mpq_ v = new(mpq) v._mpq_ = b*t-a, b return v return NotImplemented def __mul__(s, t): ttype = type(t) if ttype is mpq: a, b = s._mpq_ c, d = t._mpq_ return create_reduced(a*c, b*d) if ttype in int_types: a, b = s._mpq_ return create_reduced(a*t, b) return NotImplemented __rmul__ = __mul__ def __div__(s, t): ttype = type(t) if ttype is mpq: a, b = s._mpq_ c, d = t._mpq_ return create_reduced(a*d, b*c) if ttype in int_types: a, b = s._mpq_ return create_reduced(a, b*t) return NotImplemented def __rdiv__(s, t): ttype = type(t) if ttype is mpq: a, b = s._mpq_ c, d = t._mpq_ return create_reduced(b*c, a*d) if ttype in int_types: a, b = s._mpq_ return create_reduced(b*t, a) return NotImplemented def __pow__(s, t): ttype = type(t) if ttype in int_types: a, b = s._mpq_ if t: if t < 0: a, b, t = b, a, -t v = new(mpq) v._mpq_ = a**t, b**t return v raise ZeroDivisionError return NotImplemented mpq_1 = mpq((1,1)) mpq_0 = mpq((0,1)) mpq_1_2 = mpq((1,2)) mpq_3_2 = mpq((3,2)) mpq_1_4 = mpq((1,4)) mpq_1_16 = mpq((1,16)) mpq_3_16 = mpq((3,16)) mpq_5_2 = mpq((5,2)) mpq_3_4 = mpq((3,4)) mpq_7_4 = mpq((7,4)) mpq_5_4 = mpq((5,4)) sympy-0.7.4.1/sympy/mpmath/ctx_base.py0000644000175000017500000003637312253362407020071 0ustar georgeskgeorgeskfrom operator import gt, lt from .libmp.backend import xrange from .functions.functions import SpecialFunctions from .functions.rszeta import RSCache from .calculus.quadrature import QuadratureMethods from .calculus.calculus import CalculusMethods from .calculus.optimization import OptimizationMethods from .calculus.odes import ODEMethods from .matrices.matrices import MatrixMethods from .matrices.calculus import MatrixCalculusMethods from .matrices.linalg import LinearAlgebraMethods from .identification import IdentificationMethods from .visualization import VisualizationMethods from . import libmp class Context(object): pass class StandardBaseContext(Context, SpecialFunctions, RSCache, QuadratureMethods, CalculusMethods, MatrixMethods, MatrixCalculusMethods, LinearAlgebraMethods, IdentificationMethods, OptimizationMethods, ODEMethods, VisualizationMethods): NoConvergence = libmp.NoConvergence ComplexResult = libmp.ComplexResult def __init__(ctx): ctx._aliases = {} # Call those that need preinitialization (e.g. for wrappers) SpecialFunctions.__init__(ctx) RSCache.__init__(ctx) QuadratureMethods.__init__(ctx) CalculusMethods.__init__(ctx) MatrixMethods.__init__(ctx) def _init_aliases(ctx): for alias, value in ctx._aliases.items(): try: setattr(ctx, alias, getattr(ctx, value)) except AttributeError: pass _fixed_precision = False # XXX verbose = False def warn(ctx, msg): print("Warning:", msg) def bad_domain(ctx, msg): raise ValueError(msg) def _re(ctx, x): if hasattr(x, "real"): return x.real return x def _im(ctx, x): if hasattr(x, "imag"): return x.imag return ctx.zero def _as_points(ctx, x): return x def fneg(ctx, x, **kwargs): return -ctx.convert(x) def fadd(ctx, x, y, **kwargs): return ctx.convert(x)+ctx.convert(y) def fsub(ctx, x, y, **kwargs): return ctx.convert(x)-ctx.convert(y) def fmul(ctx, x, y, **kwargs): return ctx.convert(x)*ctx.convert(y) def fdiv(ctx, x, y, **kwargs): return ctx.convert(x)/ctx.convert(y) def fsum(ctx, args, absolute=False, squared=False): if absolute: if squared: return sum((abs(x)**2 for x in args), ctx.zero) return sum((abs(x) for x in args), ctx.zero) if squared: return sum((x**2 for x in args), ctx.zero) return sum(args, ctx.zero) def fdot(ctx, xs, ys=None, conjugate=False): if ys is not None: xs = zip(xs, ys) if conjugate: cf = ctx.conj return sum((x*cf(y) for (x,y) in xs), ctx.zero) else: return sum((x*y for (x,y) in xs), ctx.zero) def fprod(ctx, args): prod = ctx.one for arg in args: prod *= arg return prod def nprint(ctx, x, n=6, **kwargs): """ Equivalent to ``print(nstr(x, n))``. """ print(ctx.nstr(x, n, **kwargs)) def chop(ctx, x, tol=None): """ Chops off small real or imaginary parts, or converts numbers close to zero to exact zeros. The input can be a single number or an iterable:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> chop(5+1e-10j, tol=1e-9) mpf('5.0') >>> nprint(chop([1.0, 1e-20, 3+1e-18j, -4, 2])) [1.0, 0.0, 3.0, -4.0, 2.0] The tolerance defaults to ``100*eps``. """ if tol is None: tol = 100*ctx.eps try: x = ctx.convert(x) absx = abs(x) if abs(x) < tol: return ctx.zero if ctx._is_complex_type(x): #part_tol = min(tol, absx*tol) part_tol = max(tol, absx*tol) if abs(x.imag) < part_tol: return x.real if abs(x.real) < part_tol: return ctx.mpc(0, x.imag) except TypeError: if isinstance(x, ctx.matrix): return x.apply(lambda a: ctx.chop(a, tol)) if hasattr(x, "__iter__"): return [ctx.chop(a, tol) for a in x] return x def almosteq(ctx, s, t, rel_eps=None, abs_eps=None): r""" Determine whether the difference between `s` and `t` is smaller than a given epsilon, either relatively or absolutely. Both a maximum relative difference and a maximum difference ('epsilons') may be specified. The absolute difference is defined as `|s-t|` and the relative difference is defined as `|s-t|/\max(|s|, |t|)`. If only one epsilon is given, both are set to the same value. If none is given, both epsilons are set to `2^{-p+m}` where `p` is the current working precision and `m` is a small integer. The default setting typically allows :func:`~mpmath.almosteq` to be used to check for mathematical equality in the presence of small rounding errors. **Examples** >>> from mpmath import * >>> mp.dps = 15 >>> almosteq(3.141592653589793, 3.141592653589790) True >>> almosteq(3.141592653589793, 3.141592653589700) False >>> almosteq(3.141592653589793, 3.141592653589700, 1e-10) True >>> almosteq(1e-20, 2e-20) True >>> almosteq(1e-20, 2e-20, rel_eps=0, abs_eps=0) False """ t = ctx.convert(t) if abs_eps is None and rel_eps is None: rel_eps = abs_eps = ctx.ldexp(1, -ctx.prec+4) if abs_eps is None: abs_eps = rel_eps elif rel_eps is None: rel_eps = abs_eps diff = abs(s-t) if diff <= abs_eps: return True abss = abs(s) abst = abs(t) if abss < abst: err = diff/abst else: err = diff/abss return err <= rel_eps def arange(ctx, *args): r""" This is a generalized version of Python's :func:`~mpmath.range` function that accepts fractional endpoints and step sizes and returns a list of ``mpf`` instances. Like :func:`~mpmath.range`, :func:`~mpmath.arange` can be called with 1, 2 or 3 arguments: ``arange(b)`` `[0, 1, 2, \ldots, x]` ``arange(a, b)`` `[a, a+1, a+2, \ldots, x]` ``arange(a, b, h)`` `[a, a+h, a+h, \ldots, x]` where `b-1 \le x < b` (in the third case, `b-h \le x < b`). Like Python's :func:`~mpmath.range`, the endpoint is not included. To produce ranges where the endpoint is included, :func:`~mpmath.linspace` is more convenient. **Examples** >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> arange(4) [mpf('0.0'), mpf('1.0'), mpf('2.0'), mpf('3.0')] >>> arange(1, 2, 0.25) [mpf('1.0'), mpf('1.25'), mpf('1.5'), mpf('1.75')] >>> arange(1, -1, -0.75) [mpf('1.0'), mpf('0.25'), mpf('-0.5')] """ if not len(args) <= 3: raise TypeError('arange expected at most 3 arguments, got %i' % len(args)) if not len(args) >= 1: raise TypeError('arange expected at least 1 argument, got %i' % len(args)) # set default a = 0 dt = 1 # interpret arguments if len(args) == 1: b = args[0] elif len(args) >= 2: a = args[0] b = args[1] if len(args) == 3: dt = args[2] a, b, dt = ctx.mpf(a), ctx.mpf(b), ctx.mpf(dt) assert a + dt != a, 'dt is too small and would cause an infinite loop' # adapt code for sign of dt if a > b: if dt > 0: return [] op = gt else: if dt < 0: return [] op = lt # create list result = [] i = 0 t = a while 1: t = a + dt*i i += 1 if op(t, b): result.append(t) else: break return result def linspace(ctx, *args, **kwargs): """ ``linspace(a, b, n)`` returns a list of `n` evenly spaced samples from `a` to `b`. The syntax ``linspace(mpi(a,b), n)`` is also valid. This function is often more convenient than :func:`~mpmath.arange` for partitioning an interval into subintervals, since the endpoint is included:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> linspace(1, 4, 4) [mpf('1.0'), mpf('2.0'), mpf('3.0'), mpf('4.0')] You may also provide the keyword argument ``endpoint=False``:: >>> linspace(1, 4, 4, endpoint=False) [mpf('1.0'), mpf('1.75'), mpf('2.5'), mpf('3.25')] """ if len(args) == 3: a = ctx.mpf(args[0]) b = ctx.mpf(args[1]) n = int(args[2]) elif len(args) == 2: assert hasattr(args[0], '_mpi_') a = args[0].a b = args[0].b n = int(args[1]) else: raise TypeError('linspace expected 2 or 3 arguments, got %i' \ % len(args)) if n < 1: raise ValueError('n must be greater than 0') if not 'endpoint' in kwargs or kwargs['endpoint']: if n == 1: return [ctx.mpf(a)] step = (b - a) / ctx.mpf(n - 1) y = [i*step + a for i in xrange(n)] y[-1] = b else: step = (b - a) / ctx.mpf(n) y = [i*step + a for i in xrange(n)] return y def cos_sin(ctx, z, **kwargs): return ctx.cos(z, **kwargs), ctx.sin(z, **kwargs) def cospi_sinpi(ctx, z, **kwargs): return ctx.cospi(z, **kwargs), ctx.sinpi(z, **kwargs) def _default_hyper_maxprec(ctx, p): return int(1000 * p**0.25 + 4*p) _gcd = staticmethod(libmp.gcd) list_primes = staticmethod(libmp.list_primes) isprime = staticmethod(libmp.isprime) bernfrac = staticmethod(libmp.bernfrac) moebius = staticmethod(libmp.moebius) _ifac = staticmethod(libmp.ifac) _eulernum = staticmethod(libmp.eulernum) def sum_accurately(ctx, terms, check_step=1): prec = ctx.prec try: extraprec = 10 while 1: ctx.prec = prec + extraprec + 5 max_mag = ctx.ninf s = ctx.zero k = 0 for term in terms(): s += term if (not k % check_step) and term: term_mag = ctx.mag(term) max_mag = max(max_mag, term_mag) sum_mag = ctx.mag(s) if sum_mag - term_mag > ctx.prec: break k += 1 cancellation = max_mag - sum_mag if cancellation != cancellation: break if cancellation < extraprec or ctx._fixed_precision: break extraprec += min(ctx.prec, cancellation) return s finally: ctx.prec = prec def mul_accurately(ctx, factors, check_step=1): prec = ctx.prec try: extraprec = 10 while 1: ctx.prec = prec + extraprec + 5 max_mag = ctx.ninf one = ctx.one s = one k = 0 for factor in factors(): s *= factor term = factor - one if (not k % check_step): term_mag = ctx.mag(term) max_mag = max(max_mag, term_mag) sum_mag = ctx.mag(s-one) #if sum_mag - term_mag > ctx.prec: # break if -term_mag > ctx.prec: break k += 1 cancellation = max_mag - sum_mag if cancellation != cancellation: break if cancellation < extraprec or ctx._fixed_precision: break extraprec += min(ctx.prec, cancellation) return s finally: ctx.prec = prec def power(ctx, x, y): r"""Converts `x` and `y` to mpmath numbers and evaluates `x^y = \exp(y \log(x))`:: >>> from mpmath import * >>> mp.dps = 30; mp.pretty = True >>> power(2, 0.5) 1.41421356237309504880168872421 This shows the leading few digits of a large Mersenne prime (performing the exact calculation ``2**43112609-1`` and displaying the result in Python would be very slow):: >>> power(2, 43112609)-1 3.16470269330255923143453723949e+12978188 """ return ctx.convert(x) ** ctx.convert(y) def _zeta_int(ctx, n): return ctx.zeta(n) def maxcalls(ctx, f, N): """ Return a wrapped copy of *f* that raises ``NoConvergence`` when *f* has been called more than *N* times:: >>> from mpmath import * >>> mp.dps = 15 >>> f = maxcalls(sin, 10) >>> print(sum(f(n) for n in range(10))) 1.95520948210738 >>> f(10) Traceback (most recent call last): ... NoConvergence: maxcalls: function evaluated 10 times """ counter = [0] def f_maxcalls_wrapped(*args, **kwargs): counter[0] += 1 if counter[0] > N: raise ctx.NoConvergence("maxcalls: function evaluated %i times" % N) return f(*args, **kwargs) return f_maxcalls_wrapped def memoize(ctx, f): """ Return a wrapped copy of *f* that caches computed values, i.e. a memoized copy of *f*. Values are only reused if the cached precision is equal to or higher than the working precision:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> f = memoize(maxcalls(sin, 1)) >>> f(2) 0.909297426825682 >>> f(2) 0.909297426825682 >>> mp.dps = 25 >>> f(2) Traceback (most recent call last): ... NoConvergence: maxcalls: function evaluated 1 times """ f_cache = {} def f_cached(*args, **kwargs): if kwargs: key = args, tuple(kwargs.items()) else: key = args prec = ctx.prec if key in f_cache: cprec, cvalue = f_cache[key] if cprec >= prec: return +cvalue value = f(*args, **kwargs) f_cache[key] = (prec, value) return value f_cached.__name__ = f.__name__ f_cached.__doc__ = f.__doc__ return f_cached sympy-0.7.4.1/sympy/mpmath/functions/0000755000175000017500000000000012253362407017723 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/mpmath/functions/functions.py0000644000175000017500000004201012253362407022302 0ustar georgeskgeorgeskfrom ..libmp.backend import xrange class SpecialFunctions(object): """ This class implements special functions using high-level code. Elementary and some other functions (e.g. gamma function, basecase hypergeometric series) are assumed to be predefined by the context as "builtins" or "low-level" functions. """ defined_functions = {} # The series for the Jacobi theta functions converge for |q| < 1; # in the current implementation they throw a ValueError for # abs(q) > THETA_Q_LIM THETA_Q_LIM = 1 - 10**-7 def __init__(self): cls = self.__class__ for name in cls.defined_functions: f, wrap = cls.defined_functions[name] cls._wrap_specfun(name, f, wrap) self.mpq_1 = self._mpq((1,1)) self.mpq_0 = self._mpq((0,1)) self.mpq_1_2 = self._mpq((1,2)) self.mpq_3_2 = self._mpq((3,2)) self.mpq_1_4 = self._mpq((1,4)) self.mpq_1_16 = self._mpq((1,16)) self.mpq_3_16 = self._mpq((3,16)) self.mpq_5_2 = self._mpq((5,2)) self.mpq_3_4 = self._mpq((3,4)) self.mpq_7_4 = self._mpq((7,4)) self.mpq_5_4 = self._mpq((5,4)) self.mpq_1_3 = self._mpq((1,3)) self.mpq_2_3 = self._mpq((2,3)) self.mpq_4_3 = self._mpq((4,3)) self.mpq_1_6 = self._mpq((1,6)) self.mpq_5_6 = self._mpq((5,6)) self.mpq_5_3 = self._mpq((5,3)) self._misc_const_cache = {} self._aliases.update({ 'phase' : 'arg', 'conjugate' : 'conj', 'nthroot' : 'root', 'polygamma' : 'psi', 'hurwitz' : 'zeta', #'digamma' : 'psi0', #'trigamma' : 'psi1', #'tetragamma' : 'psi2', #'pentagamma' : 'psi3', 'fibonacci' : 'fib', 'factorial' : 'fac', }) self.zetazero_memoized = self.memoize(self.zetazero) # Default -- do nothing @classmethod def _wrap_specfun(cls, name, f, wrap): setattr(cls, name, f) # Optional fast versions of common functions in common cases. # If not overridden, default (generic hypergeometric series) # implementations will be used def _besselj(ctx, n, z): raise NotImplementedError def _erf(ctx, z): raise NotImplementedError def _erfc(ctx, z): raise NotImplementedError def _gamma_upper_int(ctx, z, a): raise NotImplementedError def _expint_int(ctx, n, z): raise NotImplementedError def _zeta(ctx, s): raise NotImplementedError def _zetasum_fast(ctx, s, a, n, derivatives, reflect): raise NotImplementedError def _ei(ctx, z): raise NotImplementedError def _e1(ctx, z): raise NotImplementedError def _ci(ctx, z): raise NotImplementedError def _si(ctx, z): raise NotImplementedError def _altzeta(ctx, s): raise NotImplementedError def defun_wrapped(f): SpecialFunctions.defined_functions[f.__name__] = f, True def defun(f): SpecialFunctions.defined_functions[f.__name__] = f, False def defun_static(f): setattr(SpecialFunctions, f.__name__, f) @defun_wrapped def cot(ctx, z): return ctx.one / ctx.tan(z) @defun_wrapped def sec(ctx, z): return ctx.one / ctx.cos(z) @defun_wrapped def csc(ctx, z): return ctx.one / ctx.sin(z) @defun_wrapped def coth(ctx, z): return ctx.one / ctx.tanh(z) @defun_wrapped def sech(ctx, z): return ctx.one / ctx.cosh(z) @defun_wrapped def csch(ctx, z): return ctx.one / ctx.sinh(z) @defun_wrapped def acot(ctx, z): return ctx.atan(ctx.one / z) @defun_wrapped def asec(ctx, z): return ctx.acos(ctx.one / z) @defun_wrapped def acsc(ctx, z): return ctx.asin(ctx.one / z) @defun_wrapped def acoth(ctx, z): return ctx.atanh(ctx.one / z) @defun_wrapped def asech(ctx, z): return ctx.acosh(ctx.one / z) @defun_wrapped def acsch(ctx, z): return ctx.asinh(ctx.one / z) @defun def sign(ctx, x): x = ctx.convert(x) if not x or ctx.isnan(x): return x if ctx._is_real_type(x): if x > 0: return ctx.one else: return -ctx.one return x / abs(x) @defun def agm(ctx, a, b=1): if b == 1: return ctx.agm1(a) a = ctx.convert(a) b = ctx.convert(b) return ctx._agm(a, b) @defun_wrapped def sinc(ctx, x): if ctx.isinf(x): return 1/x if not x: return x+1 return ctx.sin(x)/x @defun_wrapped def sincpi(ctx, x): if ctx.isinf(x): return 1/x if not x: return x+1 return ctx.sinpi(x)/(ctx.pi*x) # TODO: tests; improve implementation @defun_wrapped def expm1(ctx, x): if not x: return ctx.zero # exp(x) - 1 ~ x if ctx.mag(x) < -ctx.prec: return x + 0.5*x**2 # TODO: accurately eval the smaller of the real/imag parts return ctx.sum_accurately(lambda: iter([ctx.exp(x),-1]),1) @defun_wrapped def powm1(ctx, x, y): mag = ctx.mag one = ctx.one w = x**y - one M = mag(w) # Only moderate cancellation if M > -8: return w # Check for the only possible exact cases if not w: if (not y) or (x in (1, -1, 1j, -1j) and ctx.isint(y)): return w x1 = x - one magy = mag(y) lnx = ctx.ln(x) # Small y: x^y - 1 ~ log(x)*y + O(log(x)^2 * y^2) if magy + mag(lnx) < -ctx.prec: return lnx*y + (lnx*y)**2/2 # TODO: accurately eval the smaller of the real/imag part return ctx.sum_accurately(lambda: iter([x**y, -1]), 1) @defun def _rootof1(ctx, k, n): k = int(k) n = int(n) k %= n if not k: return ctx.one elif 2*k == n: return -ctx.one elif 4*k == n: return ctx.j elif 4*k == 3*n: return -ctx.j return ctx.expjpi(2*ctx.mpf(k)/n) @defun def root(ctx, x, n, k=0): n = int(n) x = ctx.convert(x) if k: # Special case: there is an exact real root if (n & 1 and 2*k == n-1) and (not ctx.im(x)) and (ctx.re(x) < 0): return -ctx.root(-x, n) # Multiply by root of unity prec = ctx.prec try: ctx.prec += 10 v = ctx.root(x, n, 0) * ctx._rootof1(k, n) finally: ctx.prec = prec return +v return ctx._nthroot(x, n) @defun def unitroots(ctx, n, primitive=False): gcd = ctx._gcd prec = ctx.prec try: ctx.prec += 10 if primitive: v = [ctx._rootof1(k,n) for k in range(n) if gcd(k,n) == 1] else: # TODO: this can be done *much* faster v = [ctx._rootof1(k,n) for k in range(n)] finally: ctx.prec = prec return [+x for x in v] @defun def arg(ctx, x): x = ctx.convert(x) re = ctx._re(x) im = ctx._im(x) return ctx.atan2(im, re) @defun def fabs(ctx, x): return abs(ctx.convert(x)) @defun def re(ctx, x): x = ctx.convert(x) if hasattr(x, "real"): # py2.5 doesn't have .real/.imag for all numbers return x.real return x @defun def im(ctx, x): x = ctx.convert(x) if hasattr(x, "imag"): # py2.5 doesn't have .real/.imag for all numbers return x.imag return ctx.zero @defun def conj(ctx, x): x = ctx.convert(x) try: return x.conjugate() except AttributeError: return x @defun def polar(ctx, z): return (ctx.fabs(z), ctx.arg(z)) @defun_wrapped def rect(ctx, r, phi): return r * ctx.mpc(*ctx.cos_sin(phi)) @defun def log(ctx, x, b=None): if b is None: return ctx.ln(x) wp = ctx.prec + 20 return ctx.ln(x, prec=wp) / ctx.ln(b, prec=wp) @defun def log10(ctx, x): return ctx.log(x, 10) @defun def fmod(ctx, x, y): return ctx.convert(x) % ctx.convert(y) @defun def degrees(ctx, x): return x / ctx.degree @defun def radians(ctx, x): return x * ctx.degree def _lambertw_special(ctx, z, k): # W(0,0) = 0; all other branches are singular if not z: if not k: return z return ctx.ninf + z if z == ctx.inf: if k == 0: return z else: return z + 2*k*ctx.pi*ctx.j if z == ctx.ninf: return (-z) + (2*k+1)*ctx.pi*ctx.j # Some kind of nan or complex inf/nan? return ctx.ln(z) import math import cmath def _lambertw_approx_hybrid(z, k): imag_sign = 0 if hasattr(z, "imag"): x = float(z.real) y = z.imag if y: imag_sign = (-1) ** (y < 0) y = float(y) else: x = float(z) y = 0.0 imag_sign = 0 # hack to work regardless of whether Python supports -0.0 if not y: y = 0.0 z = complex(x,y) if k == 0: if -4.0 < y < 4.0 and -1.0 < x < 2.5: if imag_sign: # Taylor series in upper/lower half-plane if y > 1.00: return (0.876+0.645j) + (0.118-0.174j)*(z-(0.75+2.5j)) if y > 0.25: return (0.505+0.204j) + (0.375-0.132j)*(z-(0.75+0.5j)) if y < -1.00: return (0.876-0.645j) + (0.118+0.174j)*(z-(0.75-2.5j)) if y < -0.25: return (0.505-0.204j) + (0.375+0.132j)*(z-(0.75-0.5j)) # Taylor series near -1 if x < -0.5: if imag_sign >= 0: return (-0.318+1.34j) + (-0.697-0.593j)*(z+1) else: return (-0.318-1.34j) + (-0.697+0.593j)*(z+1) # return real type r = -0.367879441171442 if (not imag_sign) and x > r: z = x # Singularity near -1/e if x < -0.2: return -1 + 2.33164398159712*(z-r)**0.5 - 1.81218788563936*(z-r) # Taylor series near 0 if x < 0.5: return z # Simple linear approximation return 0.2 + 0.3*z if (not imag_sign) and x > 0.0: L1 = math.log(x) L2 = math.log(L1) else: L1 = cmath.log(z) L2 = cmath.log(L1) elif k == -1: # return real type r = -0.367879441171442 if (not imag_sign) and r < x < 0.0: z = x if (imag_sign >= 0) and y < 0.1 and -0.6 < x < -0.2: return -1 - 2.33164398159712*(z-r)**0.5 - 1.81218788563936*(z-r) if (not imag_sign) and -0.2 <= x < 0.0: L1 = math.log(-x) return L1 - math.log(-L1) else: if imag_sign == -1 and (not y) and x < 0.0: L1 = cmath.log(z) - 3.1415926535897932j else: L1 = cmath.log(z) - 6.2831853071795865j L2 = cmath.log(L1) return L1 - L2 + L2/L1 + L2*(L2-2)/(2*L1**2) def _lambertw_series(ctx, z, k, tol): """ Return rough approximation for W_k(z) from an asymptotic series, sufficiently accurate for the Halley iteration to converge to the correct value. """ magz = ctx.mag(z) if (-10 < magz < 900) and (-1000 < k < 1000): # Near the branch point at -1/e if magz < 1 and abs(z+0.36787944117144) < 0.05: if k == 0 or (k == -1 and ctx._im(z) >= 0) or \ (k == 1 and ctx._im(z) < 0): delta = ctx.sum_accurately(lambda: [z, ctx.exp(-1)]) cancellation = -ctx.mag(delta) ctx.prec += cancellation # Use series given in Corless et al. p = ctx.sqrt(2*(ctx.e*z+1)) ctx.prec -= cancellation u = {0:ctx.mpf(-1), 1:ctx.mpf(1)} a = {0:ctx.mpf(2), 1:ctx.mpf(-1)} if k != 0: p = -p s = ctx.zero # The series converges, so we could use it directly, but unless # *extremely* close, it is better to just use the first few # terms to get a good approximation for the iteration for l in xrange(max(2,cancellation)): if l not in u: a[l] = ctx.fsum(u[j]*u[l+1-j] for j in xrange(2,l)) u[l] = (l-1)*(u[l-2]/2+a[l-2]/4)/(l+1)-a[l]/2-u[l-1]/(l+1) term = u[l] * p**l s += term if ctx.mag(term) < -tol: return s, True l += 1 ctx.prec += cancellation//2 return s, False if k == 0 or k == -1: return _lambertw_approx_hybrid(z, k), False if k == 0: if magz < -1: return z*(1-z), False L1 = ctx.ln(z) L2 = ctx.ln(L1) elif k == -1 and (not ctx._im(z)) and (-0.36787944117144 < ctx._re(z) < 0): L1 = ctx.ln(-z) return L1 - ctx.ln(-L1), False else: # This holds both as z -> 0 and z -> inf. # Relative error is O(1/log(z)). L1 = ctx.ln(z) + 2j*ctx.pi*k L2 = ctx.ln(L1) return L1 - L2 + L2/L1 + L2*(L2-2)/(2*L1**2), False @defun def lambertw(ctx, z, k=0): z = ctx.convert(z) k = int(k) if not ctx.isnormal(z): return _lambertw_special(ctx, z, k) prec = ctx.prec ctx.prec += 20 + ctx.mag(k or 1) wp = ctx.prec tol = wp - 5 w, done = _lambertw_series(ctx, z, k, tol) if not done: # Use Halley iteration to solve w*exp(w) = z two = ctx.mpf(2) for i in xrange(100): ew = ctx.exp(w) wew = w*ew wewz = wew-z wn = w - wewz/(wew+ew-(w+two)*wewz/(two*w+two)) if ctx.mag(wn-w) <= ctx.mag(wn) - tol: w = wn break else: w = wn if i == 100: ctx.warn("Lambert W iteration failed to converge for z = %s" % z) ctx.prec = prec return +w @defun_wrapped def bell(ctx, n, x=1): x = ctx.convert(x) if not n: if ctx.isnan(x): return x return type(x)(1) if ctx.isinf(x) or ctx.isinf(n) or ctx.isnan(x) or ctx.isnan(n): return x**n if n == 1: return x if n == 2: return x*(x+1) if x == 0: return ctx.sincpi(n) return _polyexp(ctx, n, x, True) / ctx.exp(x) def _polyexp(ctx, n, x, extra=False): def _terms(): if extra: yield ctx.sincpi(n) t = x k = 1 while 1: yield k**n * t k += 1 t = t*x/k return ctx.sum_accurately(_terms, check_step=4) @defun_wrapped def polyexp(ctx, s, z): if ctx.isinf(z) or ctx.isinf(s) or ctx.isnan(z) or ctx.isnan(s): return z**s if z == 0: return z*s if s == 0: return ctx.expm1(z) if s == 1: return ctx.exp(z)*z if s == 2: return ctx.exp(z)*z*(z+1) return _polyexp(ctx, s, z) @defun_wrapped def cyclotomic(ctx, n, z): n = int(n) assert n >= 0 p = ctx.one if n == 0: return p if n == 1: return z - p if n == 2: return z + p # Use divisor product representation. Unfortunately, this sometimes # includes singularities for roots of unity, which we have to cancel out. # Matching zeros/poles pairwise, we have (1-z^a)/(1-z^b) ~ a/b + O(z-1). a_prod = 1 b_prod = 1 num_zeros = 0 num_poles = 0 for d in range(1,n+1): if not n % d: w = ctx.moebius(n//d) # Use powm1 because it is important that we get 0 only # if it really is exactly 0 b = -ctx.powm1(z, d) if b: p *= b**w else: if w == 1: a_prod *= d num_zeros += 1 elif w == -1: b_prod *= d num_poles += 1 #print n, num_zeros, num_poles if num_zeros: if num_zeros > num_poles: p *= 0 else: p *= a_prod p /= b_prod return p @defun def mangoldt(ctx, n): r""" Evaluates the von Mangoldt function `\Lambda(n) = \log p` if `n = p^k` a power of a prime, and `\Lambda(n) = 0` otherwise. **Examples** >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> [mangoldt(n) for n in range(-2,3)] [0.0, 0.0, 0.0, 0.0, 0.6931471805599453094172321] >>> mangoldt(6) 0.0 >>> mangoldt(7) 1.945910149055313305105353 >>> mangoldt(8) 0.6931471805599453094172321 >>> fsum(mangoldt(n) for n in range(101)) 94.04531122935739224600493 >>> fsum(mangoldt(n) for n in range(10001)) 10013.39669326311478372032 """ n = int(n) if n < 2: return ctx.zero if n % 2 == 0: # Must be a power of two if n & (n-1) == 0: return +ctx.ln2 else: return ctx.zero # TODO: the following could be generalized into a perfect # power testing function # --- # Look for a small factor for p in (3,5,7,11,13,17,19,23,29,31): if not n % p: q, r = n // p, 0 while q > 1: q, r = divmod(q, p) if r: return ctx.zero return ctx.ln(p) if ctx.isprime(n): return ctx.ln(n) # Obviously, we could use arbitrary-precision arithmetic for this... if n > 10**30: raise NotImplementedError k = 2 while 1: p = int(n**(1./k) + 0.5) if p < 2: return ctx.zero if p ** k == n: if ctx.isprime(p): return ctx.ln(p) k += 1 sympy-0.7.4.1/sympy/mpmath/functions/theta.py0000644000175000017500000011071012253362407021402 0ustar georgeskgeorgeskfrom .functions import defun, defun_wrapped @defun def _jacobi_theta2(ctx, z, q): extra1 = 10 extra2 = 20 # the loops below break when the fixed precision quantities # a and b go to zero; # right shifting small negative numbers by wp one obtains -1, not zero, # so the condition a**2 + b**2 > MIN is used to break the loops. MIN = 2 if z == ctx.zero: if (not ctx._im(q)): wp = ctx.prec + extra1 x = ctx.to_fixed(ctx._re(q), wp) x2 = (x*x) >> wp a = b = x2 s = x2 while abs(a) > MIN: b = (b*x2) >> wp a = (a*b) >> wp s += a s = (1 << (wp+1)) + (s << 1) s = ctx.ldexp(s, -wp) else: wp = ctx.prec + extra1 xre = ctx.to_fixed(ctx._re(q), wp) xim = ctx.to_fixed(ctx._im(q), wp) x2re = (xre*xre - xim*xim) >> wp x2im = (xre*xim) >> (wp-1) are = bre = x2re aim = bim = x2im sre = (1< MIN: bre, bim = (bre * x2re - bim * x2im) >> wp, \ (bre * x2im + bim * x2re) >> wp are, aim = (are * bre - aim * bim) >> wp, \ (are * bim + aim * bre) >> wp sre += are sim += aim sre = (sre << 1) sim = (sim << 1) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) else: if (not ctx._im(q)) and (not ctx._im(z)): wp = ctx.prec + extra1 x = ctx.to_fixed(ctx._re(q), wp) x2 = (x*x) >> wp a = b = x2 c1, s1 = ctx.cos_sin(ctx._re(z), prec=wp) cn = c1 = ctx.to_fixed(c1, wp) sn = s1 = ctx.to_fixed(s1, wp) c2 = (c1*c1 - s1*s1) >> wp s2 = (c1 * s1) >> (wp - 1) cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp s = c1 + ((a * cn) >> wp) while abs(a) > MIN: b = (b*x2) >> wp a = (a*b) >> wp cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp s += (a * cn) >> wp s = (s << 1) s = ctx.ldexp(s, -wp) s *= ctx.nthroot(q, 4) return s # case z real, q complex elif not ctx._im(z): wp = ctx.prec + extra2 xre = ctx.to_fixed(ctx._re(q), wp) xim = ctx.to_fixed(ctx._im(q), wp) x2re = (xre*xre - xim*xim) >> wp x2im = (xre*xim) >> (wp - 1) are = bre = x2re aim = bim = x2im c1, s1 = ctx.cos_sin(ctx._re(z), prec=wp) cn = c1 = ctx.to_fixed(c1, wp) sn = s1 = ctx.to_fixed(s1, wp) c2 = (c1*c1 - s1*s1) >> wp s2 = (c1 * s1) >> (wp - 1) cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp sre = c1 + ((are * cn) >> wp) sim = ((aim * cn) >> wp) while are**2 + aim**2 > MIN: bre, bim = (bre * x2re - bim * x2im) >> wp, \ (bre * x2im + bim * x2re) >> wp are, aim = (are * bre - aim * bim) >> wp, \ (are * bim + aim * bre) >> wp cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp sre += ((are * cn) >> wp) sim += ((aim * cn) >> wp) sre = (sre << 1) sim = (sim << 1) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) #case z complex, q real elif not ctx._im(q): wp = ctx.prec + extra2 x = ctx.to_fixed(ctx._re(q), wp) x2 = (x*x) >> wp a = b = x2 prec0 = ctx.prec ctx.prec = wp c1, s1 = ctx.cos_sin(z) ctx.prec = prec0 cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) snre = s1re = ctx.to_fixed(ctx._re(s1), wp) snim = s1im = ctx.to_fixed(ctx._im(s1), wp) #c2 = (c1*c1 - s1*s1) >> wp c2re = (c1re*c1re - c1im*c1im - s1re*s1re + s1im*s1im) >> wp c2im = (c1re*c1im - s1re*s1im) >> (wp - 1) #s2 = (c1 * s1) >> (wp - 1) s2re = (c1re*s1re - c1im*s1im) >> (wp - 1) s2im = (c1re*s1im + c1im*s1re) >> (wp - 1) #cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp cnre = t1 cnim = t2 snre = t3 snim = t4 sre = c1re + ((a * cnre) >> wp) sim = c1im + ((a * cnim) >> wp) while abs(a) > MIN: b = (b*x2) >> wp a = (a*b) >> wp t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp cnre = t1 cnim = t2 snre = t3 snim = t4 sre += ((a * cnre) >> wp) sim += ((a * cnim) >> wp) sre = (sre << 1) sim = (sim << 1) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) # case z and q complex else: wp = ctx.prec + extra2 xre = ctx.to_fixed(ctx._re(q), wp) xim = ctx.to_fixed(ctx._im(q), wp) x2re = (xre*xre - xim*xim) >> wp x2im = (xre*xim) >> (wp - 1) are = bre = x2re aim = bim = x2im prec0 = ctx.prec ctx.prec = wp # cos(z), sin(z) with z complex c1, s1 = ctx.cos_sin(z) ctx.prec = prec0 cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) snre = s1re = ctx.to_fixed(ctx._re(s1), wp) snim = s1im = ctx.to_fixed(ctx._im(s1), wp) c2re = (c1re*c1re - c1im*c1im - s1re*s1re + s1im*s1im) >> wp c2im = (c1re*c1im - s1re*s1im) >> (wp - 1) s2re = (c1re*s1re - c1im*s1im) >> (wp - 1) s2im = (c1re*s1im + c1im*s1re) >> (wp - 1) t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp cnre = t1 cnim = t2 snre = t3 snim = t4 n = 1 termre = c1re termim = c1im sre = c1re + ((are * cnre - aim * cnim) >> wp) sim = c1im + ((are * cnim + aim * cnre) >> wp) n = 3 termre = ((are * cnre - aim * cnim) >> wp) termim = ((are * cnim + aim * cnre) >> wp) sre = c1re + ((are * cnre - aim * cnim) >> wp) sim = c1im + ((are * cnim + aim * cnre) >> wp) n = 5 while are**2 + aim**2 > MIN: bre, bim = (bre * x2re - bim * x2im) >> wp, \ (bre * x2im + bim * x2re) >> wp are, aim = (are * bre - aim * bim) >> wp, \ (are * bim + aim * bre) >> wp #cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp cnre = t1 cnim = t2 snre = t3 snim = t4 termre = ((are * cnre - aim * cnim) >> wp) termim = ((aim * cnre + are * cnim) >> wp) sre += ((are * cnre - aim * cnim) >> wp) sim += ((aim * cnre + are * cnim) >> wp) n += 2 sre = (sre << 1) sim = (sim << 1) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) s *= ctx.nthroot(q, 4) return s @defun def _djacobi_theta2(ctx, z, q, nd): MIN = 2 extra1 = 10 extra2 = 20 if (not ctx._im(q)) and (not ctx._im(z)): wp = ctx.prec + extra1 x = ctx.to_fixed(ctx._re(q), wp) x2 = (x*x) >> wp a = b = x2 c1, s1 = ctx.cos_sin(ctx._re(z), prec=wp) cn = c1 = ctx.to_fixed(c1, wp) sn = s1 = ctx.to_fixed(s1, wp) c2 = (c1*c1 - s1*s1) >> wp s2 = (c1 * s1) >> (wp - 1) cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp if (nd&1): s = s1 + ((a * sn * 3**nd) >> wp) else: s = c1 + ((a * cn * 3**nd) >> wp) n = 2 while abs(a) > MIN: b = (b*x2) >> wp a = (a*b) >> wp cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp if nd&1: s += (a * sn * (2*n+1)**nd) >> wp else: s += (a * cn * (2*n+1)**nd) >> wp n += 1 s = -(s << 1) s = ctx.ldexp(s, -wp) # case z real, q complex elif not ctx._im(z): wp = ctx.prec + extra2 xre = ctx.to_fixed(ctx._re(q), wp) xim = ctx.to_fixed(ctx._im(q), wp) x2re = (xre*xre - xim*xim) >> wp x2im = (xre*xim) >> (wp - 1) are = bre = x2re aim = bim = x2im c1, s1 = ctx.cos_sin(ctx._re(z), prec=wp) cn = c1 = ctx.to_fixed(c1, wp) sn = s1 = ctx.to_fixed(s1, wp) c2 = (c1*c1 - s1*s1) >> wp s2 = (c1 * s1) >> (wp - 1) cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp if (nd&1): sre = s1 + ((are * sn * 3**nd) >> wp) sim = ((aim * sn * 3**nd) >> wp) else: sre = c1 + ((are * cn * 3**nd) >> wp) sim = ((aim * cn * 3**nd) >> wp) n = 5 while are**2 + aim**2 > MIN: bre, bim = (bre * x2re - bim * x2im) >> wp, \ (bre * x2im + bim * x2re) >> wp are, aim = (are * bre - aim * bim) >> wp, \ (are * bim + aim * bre) >> wp cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp if (nd&1): sre += ((are * sn * n**nd) >> wp) sim += ((aim * sn * n**nd) >> wp) else: sre += ((are * cn * n**nd) >> wp) sim += ((aim * cn * n**nd) >> wp) n += 2 sre = -(sre << 1) sim = -(sim << 1) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) #case z complex, q real elif not ctx._im(q): wp = ctx.prec + extra2 x = ctx.to_fixed(ctx._re(q), wp) x2 = (x*x) >> wp a = b = x2 prec0 = ctx.prec ctx.prec = wp c1, s1 = ctx.cos_sin(z) ctx.prec = prec0 cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) snre = s1re = ctx.to_fixed(ctx._re(s1), wp) snim = s1im = ctx.to_fixed(ctx._im(s1), wp) #c2 = (c1*c1 - s1*s1) >> wp c2re = (c1re*c1re - c1im*c1im - s1re*s1re + s1im*s1im) >> wp c2im = (c1re*c1im - s1re*s1im) >> (wp - 1) #s2 = (c1 * s1) >> (wp - 1) s2re = (c1re*s1re - c1im*s1im) >> (wp - 1) s2im = (c1re*s1im + c1im*s1re) >> (wp - 1) #cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp cnre = t1 cnim = t2 snre = t3 snim = t4 if (nd&1): sre = s1re + ((a * snre * 3**nd) >> wp) sim = s1im + ((a * snim * 3**nd) >> wp) else: sre = c1re + ((a * cnre * 3**nd) >> wp) sim = c1im + ((a * cnim * 3**nd) >> wp) n = 5 while abs(a) > MIN: b = (b*x2) >> wp a = (a*b) >> wp t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp cnre = t1 cnim = t2 snre = t3 snim = t4 if (nd&1): sre += ((a * snre * n**nd) >> wp) sim += ((a * snim * n**nd) >> wp) else: sre += ((a * cnre * n**nd) >> wp) sim += ((a * cnim * n**nd) >> wp) n += 2 sre = -(sre << 1) sim = -(sim << 1) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) # case z and q complex else: wp = ctx.prec + extra2 xre = ctx.to_fixed(ctx._re(q), wp) xim = ctx.to_fixed(ctx._im(q), wp) x2re = (xre*xre - xim*xim) >> wp x2im = (xre*xim) >> (wp - 1) are = bre = x2re aim = bim = x2im prec0 = ctx.prec ctx.prec = wp # cos(2*z), sin(2*z) with z complex c1, s1 = ctx.cos_sin(z) ctx.prec = prec0 cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) snre = s1re = ctx.to_fixed(ctx._re(s1), wp) snim = s1im = ctx.to_fixed(ctx._im(s1), wp) c2re = (c1re*c1re - c1im*c1im - s1re*s1re + s1im*s1im) >> wp c2im = (c1re*c1im - s1re*s1im) >> (wp - 1) s2re = (c1re*s1re - c1im*s1im) >> (wp - 1) s2im = (c1re*s1im + c1im*s1re) >> (wp - 1) t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp cnre = t1 cnim = t2 snre = t3 snim = t4 if (nd&1): sre = s1re + (((are * snre - aim * snim) * 3**nd) >> wp) sim = s1im + (((are * snim + aim * snre)* 3**nd) >> wp) else: sre = c1re + (((are * cnre - aim * cnim) * 3**nd) >> wp) sim = c1im + (((are * cnim + aim * cnre)* 3**nd) >> wp) n = 5 while are**2 + aim**2 > MIN: bre, bim = (bre * x2re - bim * x2im) >> wp, \ (bre * x2im + bim * x2re) >> wp are, aim = (are * bre - aim * bim) >> wp, \ (are * bim + aim * bre) >> wp #cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp cnre = t1 cnim = t2 snre = t3 snim = t4 if (nd&1): sre += (((are * snre - aim * snim) * n**nd) >> wp) sim += (((aim * snre + are * snim) * n**nd) >> wp) else: sre += (((are * cnre - aim * cnim) * n**nd) >> wp) sim += (((aim * cnre + are * cnim) * n**nd) >> wp) n += 2 sre = -(sre << 1) sim = -(sim << 1) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) s *= ctx.nthroot(q, 4) if (nd&1): return (-1)**(nd//2) * s else: return (-1)**(1 + nd//2) * s @defun def _jacobi_theta3(ctx, z, q): extra1 = 10 extra2 = 20 MIN = 2 if z == ctx.zero: if not ctx._im(q): wp = ctx.prec + extra1 x = ctx.to_fixed(ctx._re(q), wp) s = x a = b = x x2 = (x*x) >> wp while abs(a) > MIN: b = (b*x2) >> wp a = (a*b) >> wp s += a s = (1 << wp) + (s << 1) s = ctx.ldexp(s, -wp) return s else: wp = ctx.prec + extra1 xre = ctx.to_fixed(ctx._re(q), wp) xim = ctx.to_fixed(ctx._im(q), wp) x2re = (xre*xre - xim*xim) >> wp x2im = (xre*xim) >> (wp - 1) sre = are = bre = xre sim = aim = bim = xim while are**2 + aim**2 > MIN: bre, bim = (bre * x2re - bim * x2im) >> wp, \ (bre * x2im + bim * x2re) >> wp are, aim = (are * bre - aim * bim) >> wp, \ (are * bim + aim * bre) >> wp sre += are sim += aim sre = (1 << wp) + (sre << 1) sim = (sim << 1) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) return s else: if (not ctx._im(q)) and (not ctx._im(z)): s = 0 wp = ctx.prec + extra1 x = ctx.to_fixed(ctx._re(q), wp) a = b = x x2 = (x*x) >> wp c1, s1 = ctx.cos_sin(ctx._re(z)*2, prec=wp) c1 = ctx.to_fixed(c1, wp) s1 = ctx.to_fixed(s1, wp) cn = c1 sn = s1 s += (a * cn) >> wp while abs(a) > MIN: b = (b*x2) >> wp a = (a*b) >> wp cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp s += (a * cn) >> wp s = (1 << wp) + (s << 1) s = ctx.ldexp(s, -wp) return s # case z real, q complex elif not ctx._im(z): wp = ctx.prec + extra2 xre = ctx.to_fixed(ctx._re(q), wp) xim = ctx.to_fixed(ctx._im(q), wp) x2re = (xre*xre - xim*xim) >> wp x2im = (xre*xim) >> (wp - 1) are = bre = xre aim = bim = xim c1, s1 = ctx.cos_sin(ctx._re(z)*2, prec=wp) c1 = ctx.to_fixed(c1, wp) s1 = ctx.to_fixed(s1, wp) cn = c1 sn = s1 sre = (are * cn) >> wp sim = (aim * cn) >> wp while are**2 + aim**2 > MIN: bre, bim = (bre * x2re - bim * x2im) >> wp, \ (bre * x2im + bim * x2re) >> wp are, aim = (are * bre - aim * bim) >> wp, \ (are * bim + aim * bre) >> wp cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp sre += (are * cn) >> wp sim += (aim * cn) >> wp sre = (1 << wp) + (sre << 1) sim = (sim << 1) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) return s #case z complex, q real elif not ctx._im(q): wp = ctx.prec + extra2 x = ctx.to_fixed(ctx._re(q), wp) a = b = x x2 = (x*x) >> wp prec0 = ctx.prec ctx.prec = wp c1, s1 = ctx.cos_sin(2*z) ctx.prec = prec0 cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) snre = s1re = ctx.to_fixed(ctx._re(s1), wp) snim = s1im = ctx.to_fixed(ctx._im(s1), wp) sre = (a * cnre) >> wp sim = (a * cnim) >> wp while abs(a) > MIN: b = (b*x2) >> wp a = (a*b) >> wp t1 = (cnre*c1re - cnim*c1im - snre*s1re + snim*s1im) >> wp t2 = (cnre*c1im + cnim*c1re - snre*s1im - snim*s1re) >> wp t3 = (snre*c1re - snim*c1im + cnre*s1re - cnim*s1im) >> wp t4 = (snre*c1im + snim*c1re + cnre*s1im + cnim*s1re) >> wp cnre = t1 cnim = t2 snre = t3 snim = t4 sre += (a * cnre) >> wp sim += (a * cnim) >> wp sre = (1 << wp) + (sre << 1) sim = (sim << 1) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) return s # case z and q complex else: wp = ctx.prec + extra2 xre = ctx.to_fixed(ctx._re(q), wp) xim = ctx.to_fixed(ctx._im(q), wp) x2re = (xre*xre - xim*xim) >> wp x2im = (xre*xim) >> (wp - 1) are = bre = xre aim = bim = xim prec0 = ctx.prec ctx.prec = wp # cos(2*z), sin(2*z) with z complex c1, s1 = ctx.cos_sin(2*z) ctx.prec = prec0 cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) snre = s1re = ctx.to_fixed(ctx._re(s1), wp) snim = s1im = ctx.to_fixed(ctx._im(s1), wp) sre = (are * cnre - aim * cnim) >> wp sim = (aim * cnre + are * cnim) >> wp while are**2 + aim**2 > MIN: bre, bim = (bre * x2re - bim * x2im) >> wp, \ (bre * x2im + bim * x2re) >> wp are, aim = (are * bre - aim * bim) >> wp, \ (are * bim + aim * bre) >> wp t1 = (cnre*c1re - cnim*c1im - snre*s1re + snim*s1im) >> wp t2 = (cnre*c1im + cnim*c1re - snre*s1im - snim*s1re) >> wp t3 = (snre*c1re - snim*c1im + cnre*s1re - cnim*s1im) >> wp t4 = (snre*c1im + snim*c1re + cnre*s1im + cnim*s1re) >> wp cnre = t1 cnim = t2 snre = t3 snim = t4 sre += (are * cnre - aim * cnim) >> wp sim += (aim * cnre + are * cnim) >> wp sre = (1 << wp) + (sre << 1) sim = (sim << 1) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) return s @defun def _djacobi_theta3(ctx, z, q, nd): """nd=1,2,3 order of the derivative with respect to z""" MIN = 2 extra1 = 10 extra2 = 20 if (not ctx._im(q)) and (not ctx._im(z)): s = 0 wp = ctx.prec + extra1 x = ctx.to_fixed(ctx._re(q), wp) a = b = x x2 = (x*x) >> wp c1, s1 = ctx.cos_sin(ctx._re(z)*2, prec=wp) c1 = ctx.to_fixed(c1, wp) s1 = ctx.to_fixed(s1, wp) cn = c1 sn = s1 if (nd&1): s += (a * sn) >> wp else: s += (a * cn) >> wp n = 2 while abs(a) > MIN: b = (b*x2) >> wp a = (a*b) >> wp cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp if nd&1: s += (a * sn * n**nd) >> wp else: s += (a * cn * n**nd) >> wp n += 1 s = -(s << (nd+1)) s = ctx.ldexp(s, -wp) # case z real, q complex elif not ctx._im(z): wp = ctx.prec + extra2 xre = ctx.to_fixed(ctx._re(q), wp) xim = ctx.to_fixed(ctx._im(q), wp) x2re = (xre*xre - xim*xim) >> wp x2im = (xre*xim) >> (wp - 1) are = bre = xre aim = bim = xim c1, s1 = ctx.cos_sin(ctx._re(z)*2, prec=wp) c1 = ctx.to_fixed(c1, wp) s1 = ctx.to_fixed(s1, wp) cn = c1 sn = s1 if (nd&1): sre = (are * sn) >> wp sim = (aim * sn) >> wp else: sre = (are * cn) >> wp sim = (aim * cn) >> wp n = 2 while are**2 + aim**2 > MIN: bre, bim = (bre * x2re - bim * x2im) >> wp, \ (bre * x2im + bim * x2re) >> wp are, aim = (are * bre - aim * bim) >> wp, \ (are * bim + aim * bre) >> wp cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp if nd&1: sre += (are * sn * n**nd) >> wp sim += (aim * sn * n**nd) >> wp else: sre += (are * cn * n**nd) >> wp sim += (aim * cn * n**nd) >> wp n += 1 sre = -(sre << (nd+1)) sim = -(sim << (nd+1)) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) #case z complex, q real elif not ctx._im(q): wp = ctx.prec + extra2 x = ctx.to_fixed(ctx._re(q), wp) a = b = x x2 = (x*x) >> wp prec0 = ctx.prec ctx.prec = wp c1, s1 = ctx.cos_sin(2*z) ctx.prec = prec0 cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) snre = s1re = ctx.to_fixed(ctx._re(s1), wp) snim = s1im = ctx.to_fixed(ctx._im(s1), wp) if (nd&1): sre = (a * snre) >> wp sim = (a * snim) >> wp else: sre = (a * cnre) >> wp sim = (a * cnim) >> wp n = 2 while abs(a) > MIN: b = (b*x2) >> wp a = (a*b) >> wp t1 = (cnre*c1re - cnim*c1im - snre*s1re + snim*s1im) >> wp t2 = (cnre*c1im + cnim*c1re - snre*s1im - snim*s1re) >> wp t3 = (snre*c1re - snim*c1im + cnre*s1re - cnim*s1im) >> wp t4 = (snre*c1im + snim*c1re + cnre*s1im + cnim*s1re) >> wp cnre = t1 cnim = t2 snre = t3 snim = t4 if (nd&1): sre += (a * snre * n**nd) >> wp sim += (a * snim * n**nd) >> wp else: sre += (a * cnre * n**nd) >> wp sim += (a * cnim * n**nd) >> wp n += 1 sre = -(sre << (nd+1)) sim = -(sim << (nd+1)) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) # case z and q complex else: wp = ctx.prec + extra2 xre = ctx.to_fixed(ctx._re(q), wp) xim = ctx.to_fixed(ctx._im(q), wp) x2re = (xre*xre - xim*xim) >> wp x2im = (xre*xim) >> (wp - 1) are = bre = xre aim = bim = xim prec0 = ctx.prec ctx.prec = wp # cos(2*z), sin(2*z) with z complex c1, s1 = ctx.cos_sin(2*z) ctx.prec = prec0 cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) snre = s1re = ctx.to_fixed(ctx._re(s1), wp) snim = s1im = ctx.to_fixed(ctx._im(s1), wp) if (nd&1): sre = (are * snre - aim * snim) >> wp sim = (aim * snre + are * snim) >> wp else: sre = (are * cnre - aim * cnim) >> wp sim = (aim * cnre + are * cnim) >> wp n = 2 while are**2 + aim**2 > MIN: bre, bim = (bre * x2re - bim * x2im) >> wp, \ (bre * x2im + bim * x2re) >> wp are, aim = (are * bre - aim * bim) >> wp, \ (are * bim + aim * bre) >> wp t1 = (cnre*c1re - cnim*c1im - snre*s1re + snim*s1im) >> wp t2 = (cnre*c1im + cnim*c1re - snre*s1im - snim*s1re) >> wp t3 = (snre*c1re - snim*c1im + cnre*s1re - cnim*s1im) >> wp t4 = (snre*c1im + snim*c1re + cnre*s1im + cnim*s1re) >> wp cnre = t1 cnim = t2 snre = t3 snim = t4 if(nd&1): sre += ((are * snre - aim * snim) * n**nd) >> wp sim += ((aim * snre + are * snim) * n**nd) >> wp else: sre += ((are * cnre - aim * cnim) * n**nd) >> wp sim += ((aim * cnre + are * cnim) * n**nd) >> wp n += 1 sre = -(sre << (nd+1)) sim = -(sim << (nd+1)) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) if (nd&1): return (-1)**(nd//2) * s else: return (-1)**(1 + nd//2) * s @defun def _jacobi_theta2a(ctx, z, q): """ case ctx._im(z) != 0 theta(2, z, q) = q**1/4 * Sum(q**(n*n + n) * exp(j*(2*n + 1)*z), n=-inf, inf) max term for minimum (2*n+1)*log(q).real - 2* ctx._im(z) n0 = int(ctx._im(z)/log(q).real - 1/2) theta(2, z, q) = q**1/4 * Sum(q**(n*n + n) * exp(j*(2*n + 1)*z), n=n0, inf) + q**1/4 * Sum(q**(n*n + n) * exp(j*(2*n + 1)*z), n, n0-1, -inf) """ n = n0 = int(ctx._im(z)/ctx._re(ctx.log(q)) - 1/2) e2 = ctx.expj(2*z) e = e0 = ctx.expj((2*n+1)*z) a = q**(n*n + n) # leading term term = a * e s = term eps1 = ctx.eps*abs(term) while 1: n += 1 e = e * e2 term = q**(n*n + n) * e if abs(term) < eps1: break s += term e = e0 e2 = ctx.expj(-2*z) n = n0 while 1: n -= 1 e = e * e2 term = q**(n*n + n) * e if abs(term) < eps1: break s += term s = s * ctx.nthroot(q, 4) return s @defun def _jacobi_theta3a(ctx, z, q): """ case ctx._im(z) != 0 theta3(z, q) = Sum(q**(n*n) * exp(j*2*n*z), n, -inf, inf) max term for n*abs(log(q).real) + ctx._im(z) ~= 0 n0 = int(- ctx._im(z)/abs(log(q).real)) """ n = n0 = int(-ctx._im(z)/abs(ctx._re(ctx.log(q)))) e2 = ctx.expj(2*z) e = e0 = ctx.expj(2*n*z) s = term = q**(n*n) * e eps1 = ctx.eps*abs(term) while 1: n += 1 e = e * e2 term = q**(n*n) * e if abs(term) < eps1: break s += term e = e0 e2 = ctx.expj(-2*z) n = n0 while 1: n -= 1 e = e * e2 term = q**(n*n) * e if abs(term) < eps1: break s += term return s @defun def _djacobi_theta2a(ctx, z, q, nd): """ case ctx._im(z) != 0 dtheta(2, z, q, nd) = j* q**1/4 * Sum(q**(n*n + n) * (2*n+1)*exp(j*(2*n + 1)*z), n=-inf, inf) max term for (2*n0+1)*log(q).real - 2* ctx._im(z) ~= 0 n0 = int(ctx._im(z)/log(q).real - 1/2) """ n = n0 = int(ctx._im(z)/ctx._re(ctx.log(q)) - 1/2) e2 = ctx.expj(2*z) e = e0 = ctx.expj((2*n + 1)*z) a = q**(n*n + n) # leading term term = (2*n+1)**nd * a * e s = term eps1 = ctx.eps*abs(term) while 1: n += 1 e = e * e2 term = (2*n+1)**nd * q**(n*n + n) * e if abs(term) < eps1: break s += term e = e0 e2 = ctx.expj(-2*z) n = n0 while 1: n -= 1 e = e * e2 term = (2*n+1)**nd * q**(n*n + n) * e if abs(term) < eps1: break s += term return ctx.j**nd * s * ctx.nthroot(q, 4) @defun def _djacobi_theta3a(ctx, z, q, nd): """ case ctx._im(z) != 0 djtheta3(z, q, nd) = (2*j)**nd * Sum(q**(n*n) * n**nd * exp(j*2*n*z), n, -inf, inf) max term for minimum n*abs(log(q).real) + ctx._im(z) """ n = n0 = int(-ctx._im(z)/abs(ctx._re(ctx.log(q)))) e2 = ctx.expj(2*z) e = e0 = ctx.expj(2*n*z) a = q**(n*n) * e s = term = n**nd * a if n != 0: eps1 = ctx.eps*abs(term) else: eps1 = ctx.eps*abs(a) while 1: n += 1 e = e * e2 a = q**(n*n) * e term = n**nd * a if n != 0: aterm = abs(term) else: aterm = abs(a) if aterm < eps1: break s += term e = e0 e2 = ctx.expj(-2*z) n = n0 while 1: n -= 1 e = e * e2 a = q**(n*n) * e term = n**nd * a if n != 0: aterm = abs(term) else: aterm = abs(a) if aterm < eps1: break s += term return (2*ctx.j)**nd * s @defun def jtheta(ctx, n, z, q, derivative=0): if derivative: return ctx._djtheta(n, z, q, derivative) z = ctx.convert(z) q = ctx.convert(q) # Implementation note # If ctx._im(z) is close to zero, _jacobi_theta2 and _jacobi_theta3 # are used, # which compute the series starting from n=0 using fixed precision # numbers; # otherwise _jacobi_theta2a and _jacobi_theta3a are used, which compute # the series starting from n=n0, which is the largest term. # TODO: write _jacobi_theta2a and _jacobi_theta3a using fixed-point if abs(q) > ctx.THETA_Q_LIM: raise ValueError('abs(q) > THETA_Q_LIM = %f' % ctx.THETA_Q_LIM) extra = 10 if z: M = ctx.mag(z) if M > 5 or (n == 1 and M < -5): extra += 2*abs(M) cz = 0.5 extra2 = 50 prec0 = ctx.prec try: ctx.prec += extra if n == 1: if ctx._im(z): if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): ctx.dps += extra2 res = ctx._jacobi_theta2(z - ctx.pi/2, q) else: ctx.dps += 10 res = ctx._jacobi_theta2a(z - ctx.pi/2, q) else: res = ctx._jacobi_theta2(z - ctx.pi/2, q) elif n == 2: if ctx._im(z): if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): ctx.dps += extra2 res = ctx._jacobi_theta2(z, q) else: ctx.dps += 10 res = ctx._jacobi_theta2a(z, q) else: res = ctx._jacobi_theta2(z, q) elif n == 3: if ctx._im(z): if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): ctx.dps += extra2 res = ctx._jacobi_theta3(z, q) else: ctx.dps += 10 res = ctx._jacobi_theta3a(z, q) else: res = ctx._jacobi_theta3(z, q) elif n == 4: if ctx._im(z): if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): ctx.dps += extra2 res = ctx._jacobi_theta3(z, -q) else: ctx.dps += 10 res = ctx._jacobi_theta3a(z, -q) else: res = ctx._jacobi_theta3(z, -q) else: raise ValueError finally: ctx.prec = prec0 return res @defun def _djtheta(ctx, n, z, q, derivative=1): z = ctx.convert(z) q = ctx.convert(q) nd = int(derivative) if abs(q) > ctx.THETA_Q_LIM: raise ValueError('abs(q) > THETA_Q_LIM = %f' % ctx.THETA_Q_LIM) extra = 10 + ctx.prec * nd // 10 if z: M = ctx.mag(z) if M > 5 or (n != 1 and M < -5): extra += 2*abs(M) cz = 0.5 extra2 = 50 prec0 = ctx.prec try: ctx.prec += extra if n == 1: if ctx._im(z): if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): ctx.dps += extra2 res = ctx._djacobi_theta2(z - ctx.pi/2, q, nd) else: ctx.dps += 10 res = ctx._djacobi_theta2a(z - ctx.pi/2, q, nd) else: res = ctx._djacobi_theta2(z - ctx.pi/2, q, nd) elif n == 2: if ctx._im(z): if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): ctx.dps += extra2 res = ctx._djacobi_theta2(z, q, nd) else: ctx.dps += 10 res = ctx._djacobi_theta2a(z, q, nd) else: res = ctx._djacobi_theta2(z, q, nd) elif n == 3: if ctx._im(z): if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): ctx.dps += extra2 res = ctx._djacobi_theta3(z, q, nd) else: ctx.dps += 10 res = ctx._djacobi_theta3a(z, q, nd) else: res = ctx._djacobi_theta3(z, q, nd) elif n == 4: if ctx._im(z): if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): ctx.dps += extra2 res = ctx._djacobi_theta3(z, -q, nd) else: ctx.dps += 10 res = ctx._djacobi_theta3a(z, -q, nd) else: res = ctx._djacobi_theta3(z, -q, nd) else: raise ValueError finally: ctx.prec = prec0 return +res sympy-0.7.4.1/sympy/mpmath/functions/qfunctions.py0000644000175000017500000001666212253362407022501 0ustar georgeskgeorgeskfrom .functions import defun, defun_wrapped @defun def qp(ctx, a, q=None, n=None, **kwargs): r""" Evaluates the q-Pochhammer symbol (or q-rising factorial) .. math :: (a; q)_n = \prod_{k=0}^{n-1} (1-a q^k) where `n = \infty` is permitted if `|q| < 1`. Called with two arguments, ``qp(a,q)`` computes `(a;q)_{\infty}`; with a single argument, ``qp(q)`` computes `(q;q)_{\infty}`. The special case .. math :: \phi(q) = (q; q)_{\infty} = \prod_{k=1}^{\infty} (1-q^k) = \sum_{k=-\infty}^{\infty} (-1)^k q^{(3k^2-k)/2} is also known as the Euler function, or (up to a factor `q^{-1/24}`) the Dirichlet eta function. **Examples** If `n` is a positive integer, the function amounts to a finite product:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> qp(2,3,5) -725305.0 >>> fprod(1-2*3**k for k in range(5)) -725305.0 >>> qp(2,3,0) 1.0 Complex arguments are allowed:: >>> qp(2-1j, 0.75j) (0.4628842231660149089976379 + 4.481821753552703090628793j) The regular Pochhammer symbol `(a)_n` is obtained in the following limit as `q \to 1`:: >>> a, n = 4, 7 >>> limit(lambda q: qp(q**a,q,n) / (1-q)**n, 1) 604800.0 >>> rf(a,n) 604800.0 The Taylor series of the reciprocal Euler function gives the partition function `P(n)`, i.e. the number of ways of writing `n` as a sum of positive integers:: >>> taylor(lambda q: 1/qp(q), 0, 10) [1.0, 1.0, 2.0, 3.0, 5.0, 7.0, 11.0, 15.0, 22.0, 30.0, 42.0] Special values include:: >>> qp(0) 1.0 >>> findroot(diffun(qp), -0.4) # location of maximum -0.4112484791779547734440257 >>> qp(_) 1.228348867038575112586878 The q-Pochhammer symbol is related to the Jacobi theta functions. For example, the following identity holds:: >>> q = mpf(0.5) # arbitrary >>> qp(q) 0.2887880950866024212788997 >>> root(3,-2)*root(q,-24)*jtheta(2,pi/6,root(q,6)) 0.2887880950866024212788997 """ a = ctx.convert(a) if n is None: n = ctx.inf else: n = ctx.convert(n) assert n >= 0 if q is None: q = a else: q = ctx.convert(q) if n == 0: return ctx.one + 0*(a+q) infinite = (n == ctx.inf) same = (a == q) if infinite: if abs(q) >= 1: if same and (q == -1 or q == 1): return ctx.zero * q raise ValueError("q-function only defined for |q| < 1") elif q == 0: return ctx.one - a maxterms = kwargs.get('maxterms', 50*ctx.prec) if infinite and same: # Euler's pentagonal theorem def terms(): t = 1 yield t k = 1 x1 = q x2 = q**2 while 1: yield (-1)**k * x1 yield (-1)**k * x2 x1 *= q**(3*k+1) x2 *= q**(3*k+2) k += 1 if k > maxterms: raise ctx.NoConvergence return ctx.sum_accurately(terms) # return ctx.nprod(lambda k: 1-a*q**k, [0,n-1]) def factors(): k = 0 r = ctx.one while 1: yield 1 - a*r r *= q k += 1 if k >= n: raise StopIteration if k > maxterms: raise ctx.NoConvergence return ctx.mul_accurately(factors) @defun_wrapped def qgamma(ctx, z, q, **kwargs): r""" Evaluates the q-gamma function .. math :: \Gamma_q(z) = \frac{(q; q)_{\infty}}{(q^z; q)_{\infty}} (1-q)^{1-z}. **Examples** Evaluation for real and complex arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> qgamma(4,0.75) 4.046875 >>> qgamma(6,6) 121226245.0 >>> qgamma(3+4j, 0.5j) (0.1663082382255199834630088 + 0.01952474576025952984418217j) The q-gamma function satisfies a functional equation similar to that of the ordinary gamma function:: >>> q = mpf(0.25) >>> z = mpf(2.5) >>> qgamma(z+1,q) 1.428277424823760954685912 >>> (1-q**z)/(1-q)*qgamma(z,q) 1.428277424823760954685912 """ if abs(q) > 1: return ctx.qgamma(z,1/q)*q**((z-2)*(z-1)*0.5) return ctx.qp(q, q, None, **kwargs) / \ ctx.qp(q**z, q, None, **kwargs) * (1-q)**(1-z) @defun_wrapped def qfac(ctx, z, q, **kwargs): r""" Evaluates the q-factorial, .. math :: [n]_q! = (1+q)(1+q+q^2)\cdots(1+q+\cdots+q^{n-1}) or more generally .. math :: [z]_q! = \frac{(q;q)_z}{(1-q)^z}. **Examples** >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> qfac(0,0) 1.0 >>> qfac(4,3) 2080.0 >>> qfac(5,6) 121226245.0 >>> qfac(1+1j, 2+1j) (0.4370556551322672478613695 + 0.2609739839216039203708921j) """ if ctx.isint(z) and ctx._re(z) > 0: n = int(ctx._re(z)) return ctx.qp(q, q, n, **kwargs) / (1-q)**n return ctx.qgamma(z+1, q, **kwargs) @defun def qhyper(ctx, a_s, b_s, q, z, **kwargs): r""" Evaluates the basic hypergeometric series or hypergeometric q-series .. math :: \,_r\phi_s \left[\begin{matrix} a_1 & a_2 & \ldots & a_r \\ b_1 & b_2 & \ldots & b_s \end{matrix} ; q,z \right] = \sum_{n=0}^\infty \frac{(a_1;q)_n, \ldots, (a_r;q)_n} {(b_1;q)_n, \ldots, (b_s;q)_n} \left((-1)^n q^{n\choose 2}\right)^{1+s-r} \frac{z^n}{(q;q)_n} where `(a;q)_n` denotes the q-Pochhammer symbol (see :func:`~mpmath.qp`). **Examples** Evaluation works for real and complex arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> qhyper([0.5], [2.25], 0.25, 4) -0.1975849091263356009534385 >>> qhyper([0.5], [2.25], 0.25-0.25j, 4) (2.806330244925716649839237 + 3.568997623337943121769938j) >>> qhyper([1+j], [2,3+0.5j], 0.25, 3+4j) (9.112885171773400017270226 - 1.272756997166375050700388j) Comparing with a summation of the defining series, using :func:`~mpmath.nsum`:: >>> b, q, z = 3, 0.25, 0.5 >>> qhyper([], [b], q, z) 0.6221136748254495583228324 >>> nsum(lambda n: z**n / qp(q,q,n)/qp(b,q,n) * q**(n*(n-1)), [0,inf]) 0.6221136748254495583228324 """ #a_s = [ctx._convert_param(a)[0] for a in a_s] #b_s = [ctx._convert_param(b)[0] for b in b_s] #q = ctx._convert_param(q)[0] a_s = [ctx.convert(a) for a in a_s] b_s = [ctx.convert(b) for b in b_s] q = ctx.convert(q) z = ctx.convert(z) r = len(a_s) s = len(b_s) d = 1+s-r maxterms = kwargs.get('maxterms', 50*ctx.prec) def terms(): t = ctx.one yield t qk = 1 k = 0 x = 1 while 1: for a in a_s: p = 1 - a*qk t *= p for b in b_s: p = 1 - b*qk if not p: raise ValueError t /= p t *= z x *= (-1)**d * qk ** d qk *= q t /= (1 - qk) k += 1 yield t * x if k > maxterms: raise ctx.NoConvergence return ctx.sum_accurately(terms) sympy-0.7.4.1/sympy/mpmath/functions/rszeta.py0000644000175000017500000013237012253362407021613 0ustar georgeskgeorgesk""" --------------------------------------------------------------------- .. sectionauthor:: Juan Arias de Reyna This module implements zeta-related functions using the Riemann-Siegel expansion: zeta_offline(s,k=0) * coef(J, eps): Need in the computation of Rzeta(s,k) * Rzeta_simul(s, der=0) computes Rzeta^(k)(s) and Rzeta^(k)(1-s) simultaneously for 0 <= k <= der. Used by zeta_offline and z_offline * Rzeta_set(s, derivatives) computes Rzeta^(k)(s) for given derivatives, used by z_half(t,k) and zeta_half * z_offline(w,k): Z(w) and its derivatives of order k <= 4 * z_half(t,k): Z(t) (Riemann Siegel function) and its derivatives of order k <= 4 * zeta_offline(s): zeta(s) and its derivatives of order k<= 4 * zeta_half(1/2+it,k): zeta(s) and its derivatives of order k<= 4 * rs_zeta(s,k=0) Computes zeta^(k)(s) Unifies zeta_half and zeta_offline * rs_z(w,k=0) Computes Z^(k)(w) Unifies z_offline and z_half ---------------------------------------------------------------------- This program uses Riemann-Siegel expansion even to compute zeta(s) on points s = sigma + i t with sigma arbitrary not necessarily equal to 1/2. It is founded on a new deduction of the formula, with rigorous and sharp bounds for the terms and rest of this expansion. More information on the papers: J. Arias de Reyna, High Precision Computation of Riemann's Zeta Function by the Riemann-Siegel Formula I, II We refer to them as I, II. In them we shall find detailed explanation of all the procedure. The program uses Riemann-Siegel expansion. This is useful when t is big, ( say t > 10000 ). The precision is limited, roughly it can compute zeta(sigma+it) with an error less than exp(-c t) for some constant c depending on sigma. The program gives an error when the Riemann-Siegel formula can not compute to the wanted precision. """ import math class RSCache: def __init__(ctx): ctx._rs_cache = [0, 10, {}, {}] from .functions import defun #-------------------------------------------------------------------------------# # # # coef(ctx, J, eps, _cache=[0, 10, {} ] ) # # # #-------------------------------------------------------------------------------# # This function computes the coefficients c[n] defined on (I, equation (47)) # but see also (II, section 3.14). # # Since these coefficients are very difficult to compute we save the values # in a cache. So if we compute several values of the functions Rzeta(s) for # near values of s, we do not recompute these coefficients. # # c[n] are the Taylor coefficients of the function: # # F(z):= (exp(pi*j*(z*z/2+3/8))-j* sqrt(2) cos(pi*z/2))/(2*cos(pi *z)) # # def _coef(ctx, J, eps): r""" Computes the coefficients `c_n` for `0\le n\le 2J` with error less than eps **Definition** The coefficients c_n are defined by .. math :: \begin{equation} F(z)=\frac{e^{\pi i \bigl(\frac{z^2}{2}+\frac38\bigr)}-i\sqrt{2}\cos\frac{\pi}{2}z}{2\cos\pi z}=\sum_{n=0}^\infty c_{2n} z^{2n} \end{equation} they are computed applying the relation .. math :: \begin{multline} c_{2n}=-\frac{i}{\sqrt{2}}\Bigl(\frac{\pi}{2}\Bigr)^{2n} \sum_{k=0}^n\frac{(-1)^k}{(2k)!} 2^{2n-2k}\frac{(-1)^{n-k}E_{2n-2k}}{(2n-2k)!}+\\ +e^{3\pi i/8}\sum_{j=0}^n(-1)^j\frac{ E_{2j}}{(2j)!}\frac{i^{n-j}\pi^{n+j}}{(n-j)!2^{n-j+1}}. \end{multline} """ newJ = J+2 # compute more coefficients that are needed neweps6 = eps/2. # compute with a slight more precision # that are needed # PREPARATION FOR THE COMPUTATION OF V(N) AND W(N) # See II Section 3.16 # # Computing the exponent wpvw of the error II equation (81) wpvw = max(ctx.mag(10*(newJ+3)), 4*newJ+5-ctx.mag(neweps6)) # Preparation of Euler numbers (we need until the 2*RS_NEWJ) E = ctx._eulernum(2*newJ) # Now we have in the cache all the needed Euler numbers. # # Computing the powers of pi # # We need to compute the powers pi**n for 1<= n <= 2*J # with relative error less than 2**(-wpvw) # it is easy to show that this is obtained # taking wppi as the least d with # 2**d>40*J and 2**d> 4.24 *newJ + 2**wpvw # In II Section 3.9 we need also that # wppi > wptcoef[0], and that the powers # here computed 0<= k <= 2*newJ are more # than those needed there that are 2*L-2. # so we need J >= L this will be checked # before computing tcoef[] wppi = max(ctx.mag(40*newJ), ctx.mag(newJ)+3 +wpvw) ctx.prec = wppi pipower = {} pipower[0] = ctx.one pipower[1] = ctx.pi for n in range(2,2*newJ+1): pipower[n] = pipower[n-1]*ctx.pi # COMPUTING THE COEFFICIENTS v(n) AND w(n) # see II equation (61) and equations (81) and (82) ctx.prec = wpvw+2 v={} w={} for n in range(0,newJ+1): va = (-1)**n * ctx._eulernum(2*n) va = ctx.mpf(va)/ctx.fac(2*n) v[n]=va*pipower[2*n] for n in range(0,2*newJ+1): wa = ctx.one/ctx.fac(n) wa=wa/(2**n) w[n]=wa*pipower[n] # COMPUTATION OF THE CONVOLUTIONS RS_P1 AND RS_P2 # See II Section 3.16 ctx.prec = 15 wpp1a = 9 - ctx.mag(neweps6) P1 = {} for n in range(0,newJ+1): ctx.prec = 15 wpp1 = max(ctx.mag(10*(n+4)),4*n+wpp1a) ctx.prec = wpp1 sump = 0 for k in range(0,n+1): sump += ((-1)**k) * v[k]*w[2*n-2*k] P1[n]=((-1)**(n+1))*ctx.j*sump P2={} for n in range(0,newJ+1): ctx.prec = 15 wpp2 = max(ctx.mag(10*(n+4)),4*n+wpp1a) ctx.prec = wpp2 sump = 0 for k in range(0,n+1): sump += (ctx.j**(n-k)) * v[k]*w[n-k] P2[n]=sump # COMPUTING THE COEFFICIENTS c[2n] # See II Section 3.14 ctx.prec = 15 wpc0 = 5 - ctx.mag(neweps6) wpc = max(6,4*newJ+wpc0) ctx.prec = wpc mu = ctx.sqrt(ctx.mpf('2'))/2 nu = ctx.expjpi(3./8)/2 c={} for n in range(0,newJ): ctx.prec = 15 wpc = max(6,4*n+wpc0) ctx.prec = wpc c[2*n] = mu*P1[n]+nu*P2[n] for n in range(1,2*newJ,2): c[n] = 0 return [newJ, neweps6, c, pipower] def coef(ctx, J, eps): _cache = ctx._rs_cache if J <= _cache[0] and eps >= _cache[1]: return _cache[2], _cache[3] orig = ctx._mp.prec try: data = _coef(ctx._mp, J, eps) finally: ctx._mp.prec = orig if ctx is not ctx._mp: data[2] = dict((k,ctx.convert(v)) for (k,v) in data[2].items()) data[3] = dict((k,ctx.convert(v)) for (k,v) in data[3].items()) ctx._rs_cache[:] = data return ctx._rs_cache[2], ctx._rs_cache[3] #-------------------------------------------------------------------------------# # # # Rzeta_simul(s,k=0) # # # #-------------------------------------------------------------------------------# # This function return a list with the values: # Rzeta(sigma+it), conj(Rzeta(1-sigma+it)),Rzeta'(sigma+it), conj(Rzeta'(1-sigma+it)), # .... , Rzeta^{(k)}(sigma+it), conj(Rzeta^{(k)}(1-sigma+it)) # # Useful to compute the function zeta(s) and Z(w) or its derivatives. # def aux_M_Fp(ctx, xA, xeps4, a, xB1, xL): # COMPUTING M NUMBER OF DERIVATIVES Fp[m] TO COMPUTE # See II Section 3.11 equations (47) and (48) aux1 = 126.0657606*xA/xeps4 # 126.06.. = 316/sqrt(2*pi) aux1 = ctx.ln(aux1) aux2 = (2*ctx.ln(ctx.pi)+ctx.ln(xB1)+ctx.ln(a))/3 -ctx.ln(2*ctx.pi)/2 m = 3*xL-3 aux3= (ctx.loggamma(m+1)-ctx.loggamma(m/3.0+2))/2 -ctx.loggamma((m+1)/2.) while((aux1 < m*aux2+ aux3)and (m>1)): m = m - 1 aux3 = (ctx.loggamma(m+1)-ctx.loggamma(m/3.0+2))/2 -ctx.loggamma((m+1)/2.) xM = m return xM def aux_J_needed(ctx, xA, xeps4, a, xB1, xM): # DETERMINATION OF J THE NUMBER OF TERMS NEEDED # IN THE TAYLOR SERIES OF F. # See II Section 3.11 equation (49)) # Only determine one h1 = xeps4/(632*xA) h2 = xB1*a * 126.31337419529260248 # = pi^2*e^2*sqrt(3) h2 = h1 * ctx.power((h2/xM**2),(xM-1)/3) / xM h3 = min(h1,h2) return h3 def Rzeta_simul(ctx, s, der=0): # First we take the value of ctx.prec wpinitial = ctx.prec # INITIALIZATION # Take the real and imaginary part of s t = ctx._im(s) xsigma = ctx._re(s) ysigma = 1 - xsigma # Now compute several parameter that appear on the program ctx.prec = 15 a = ctx.sqrt(t/(2*ctx.pi)) xasigma = a ** xsigma yasigma = a ** ysigma # We need a simple bound A1 < asigma (see II Section 3.1 and 3.3) xA1=ctx.power(2, ctx.mag(xasigma)-1) yA1=ctx.power(2, ctx.mag(yasigma)-1) # We compute various epsilon's (see II end of Section 3.1) eps = ctx.power(2, -wpinitial) eps1 = eps/6. xeps2 = eps * xA1/3. yeps2 = eps * yA1/3. # COMPUTING SOME COEFFICIENTS THAT DEPENDS # ON sigma # constant b and c (see I Theorem 2 formula (26) ) # coefficients A and B1 (see I Section 6.1 equation (50)) # # here we not need high precision ctx.prec = 15 if xsigma > 0: xb = 2. xc = math.pow(9,xsigma)/4.44288 # 4.44288 =(math.sqrt(2)*math.pi) xA = math.pow(9,xsigma) xB1 = 1 else: xb = 2.25158 # math.sqrt( (3-2* math.log(2))*math.pi ) xc = math.pow(2,-xsigma)/4.44288 xA = math.pow(2,-xsigma) xB1 = 1.10789 # = 2*sqrt(1-log(2)) if(ysigma > 0): yb = 2. yc = math.pow(9,ysigma)/4.44288 # 4.44288 =(math.sqrt(2)*math.pi) yA = math.pow(9,ysigma) yB1 = 1 else: yb = 2.25158 # math.sqrt( (3-2* math.log(2))*math.pi ) yc = math.pow(2,-ysigma)/4.44288 yA = math.pow(2,-ysigma) yB1 = 1.10789 # = 2*sqrt(1-log(2)) # COMPUTING L THE NUMBER OF TERMS NEEDED IN THE RIEMANN-SIEGEL # CORRECTION # See II Section 3.2 ctx.prec = 15 xL = 1 while 3*xc*ctx.gamma(xL*0.5) * ctx.power(xb*a,-xL) >= xeps2: xL = xL+1 xL = max(2,xL) yL = 1 while 3*yc*ctx.gamma(yL*0.5) * ctx.power(yb*a,-yL) >= yeps2: yL = yL+1 yL = max(2,yL) # The number L has to satify some conditions. # If not RS can not compute Rzeta(s) with the prescribed precision # (see II, Section 3.2 condition (20) ) and # (II, Section 3.3 condition (22) ). Also we have added # an additional technical condition in Section 3.17 Proposition 17 if ((3*xL >= 2*a*a/25.) or (3*xL+2+xsigma<0) or (abs(xsigma) > a/2.) or \ (3*yL >= 2*a*a/25.) or (3*yL+2+ysigma<0) or (abs(ysigma) > a/2.)): ctx.prec = wpinitial raise NotImplementedError("Riemann-Siegel can not compute with such precision") # We take the maximum of the two values L = max(xL, yL) # INITIALIZATION (CONTINUATION) # # eps3 is the constant defined on (II, Section 3.5 equation (27) ) # each term of the RS correction must be computed with error <= eps3 xeps3 = xeps2/(4*xL) yeps3 = yeps2/(4*yL) # eps4 is defined on (II Section 3.6 equation (30) ) # each component of the formula (II Section 3.6 equation (29) ) # must be computed with error <= eps4 xeps4 = xeps3/(3*xL) yeps4 = yeps3/(3*yL) # COMPUTING M NUMBER OF DERIVATIVES Fp[m] TO COMPUTE xM = aux_M_Fp(ctx, xA, xeps4, a, xB1, xL) yM = aux_M_Fp(ctx, yA, yeps4, a, yB1, yL) M = max(xM, yM) # COMPUTING NUMBER OF TERMS J NEEDED h3 = aux_J_needed(ctx, xA, xeps4, a, xB1, xM) h4 = aux_J_needed(ctx, yA, yeps4, a, yB1, yM) h3 = min(h3,h4) J = 12 jvalue = (2*ctx.pi)**J / ctx.gamma(J+1) while jvalue > h3: J = J+1 jvalue = (2*ctx.pi)*jvalue/J # COMPUTING eps5[m] for 1 <= m <= 21 # See II Section 10 equation (43) # We choose the minimum of the two possibilities eps5={} xforeps5 = math.pi*math.pi*xB1*a yforeps5 = math.pi*math.pi*yB1*a for m in range(0,22): xaux1 = math.pow(xforeps5, m/3)/(316.*xA) yaux1 = math.pow(yforeps5, m/3)/(316.*yA) aux1 = min(xaux1, yaux1) aux2 = ctx.gamma(m+1)/ctx.gamma(m/3.0+0.5) aux2 = math.sqrt(aux2) eps5[m] = (aux1*aux2*min(xeps4,yeps4)) # COMPUTING wpfp # See II Section 3.13 equation (59) twenty = min(3*L-3, 21)+1 aux = 6812*J wpfp = ctx.mag(44*J) for m in range(0,twenty): wpfp = max(wpfp, ctx.mag(aux*ctx.gamma(m+1)/eps5[m])) # COMPUTING N AND p # See II Section ctx.prec = wpfp + ctx.mag(t)+20 a = ctx.sqrt(t/(2*ctx.pi)) N = ctx.floor(a) p = 1-2*(a-N) # now we get a rounded version of p # to the precision wpfp # this possibly is not necessary num=ctx.floor(p*(ctx.mpf('2')**wpfp)) difference = p * (ctx.mpf('2')**wpfp)-num if (difference < 0.5): num = num else: num = num+1 p = ctx.convert(num * (ctx.mpf('2')**(-wpfp))) # COMPUTING THE COEFFICIENTS c[n] = cc[n] # We shall use the notation cc[n], since there is # a constant that is called c # See II Section 3.14 # We compute the coefficients and also save then in a # cache. The bulk of the computation is passed to # the function coef() # # eps6 is defined in II Section 3.13 equation (58) eps6 = ctx.power(ctx.convert(2*ctx.pi), J)/(ctx.gamma(J+1)*3*J) # Now we compute the coefficients cc = {} cont = {} cont, pipowers = coef(ctx, J, eps6) cc=cont.copy() # we need a copy since we have # to change his values. Fp={} # this is the adequate locus of this for n in range(M, 3*L-2): Fp[n] = 0 Fp={} ctx.prec = wpfp for m in range(0,M+1): sumP = 0 for k in range(2*J-m-1,-1,-1): sumP = (sumP * p)+ cc[k] Fp[m] = sumP # preparation of the new coefficients for k in range(0,2*J-m-1): cc[k] = (k+1)* cc[k+1] # COMPUTING THE NUMBERS xd[u,n,k], yd[u,n,k] # See II Section 3.17 # # First we compute the working precisions xwpd[k] # Se II equation (92) xwpd={} d1 = max(6,ctx.mag(40*L*L)) xd2 = 13+ctx.mag((1+abs(xsigma))*xA)-ctx.mag(xeps4)-1 xconst = ctx.ln(8/(ctx.pi*ctx.pi*a*a*xB1*xB1)) /2 for n in range(0,L): xd3 = ctx.mag(ctx.sqrt(ctx.gamma(n-0.5)))-ctx.floor(n*xconst)+xd2 xwpd[n]=max(xd3,d1) # procedure of II Section 3.17 ctx.prec = xwpd[1]+10 xpsigma = 1-(2*xsigma) xd = {} xd[0,0,-2]=0 xd[0,0,-1]=0 xd[0,0,0]=1 xd[0,0,1]=0 xd[0,-1,-2]=0 xd[0,-1,-1]=0 xd[0,-1,0]=1 xd[0,-1,1]=0 for n in range(1,L): ctx.prec = xwpd[n]+10 for k in range(0,3*n//2+1): m = 3*n-2*k if(m!=0): m1 = ctx.one/m c1= m1/4 c2=(xpsigma*m1)/2 c3=-(m+1) xd[0,n,k]=c3*xd[0,n-1,k-2]+c1*xd[0,n-1,k]+c2*xd[0,n-1,k-1] else: xd[0,n,k]=0 for r in range(0,k): add=xd[0,n,r]*(ctx.mpf('1.0')*ctx.fac(2*k-2*r)/ctx.fac(k-r)) xd[0,n,k] -= ((-1)**(k-r))*add xd[0,n,-2]=0 xd[0,n,-1]=0 xd[0,n,3*n//2+1]=0 for mu in range(-2,der+1): for n in range(-2,L): for k in range(-3,max(1,3*n//2+2)): if( (mu<0)or (n<0) or(k<0)or (k>3*n//2)): xd[mu,n,k] = 0 for mu in range(1,der+1): for n in range(0,L): ctx.prec = xwpd[n]+10 for k in range(0,3*n//2+1): aux=(2*mu-2)*xd[mu-2,n-2,k-3]+2*(xsigma+n-2)*xd[mu-1,n-2,k-3] xd[mu,n,k] = aux - xd[mu-1,n-1,k-1] # Now we compute the working precisions ywpd[k] # Se II equation (92) ywpd={} d1 = max(6,ctx.mag(40*L*L)) yd2 = 13+ctx.mag((1+abs(ysigma))*yA)-ctx.mag(yeps4)-1 yconst = ctx.ln(8/(ctx.pi*ctx.pi*a*a*yB1*yB1)) /2 for n in range(0,L): yd3 = ctx.mag(ctx.sqrt(ctx.gamma(n-0.5)))-ctx.floor(n*yconst)+yd2 ywpd[n]=max(yd3,d1) # procedure of II Section 3.17 ctx.prec = ywpd[1]+10 ypsigma = 1-(2*ysigma) yd = {} yd[0,0,-2]=0 yd[0,0,-1]=0 yd[0,0,0]=1 yd[0,0,1]=0 yd[0,-1,-2]=0 yd[0,-1,-1]=0 yd[0,-1,0]=1 yd[0,-1,1]=0 for n in range(1,L): ctx.prec = ywpd[n]+10 for k in range(0,3*n//2+1): m = 3*n-2*k if(m!=0): m1 = ctx.one/m c1= m1/4 c2=(ypsigma*m1)/2 c3=-(m+1) yd[0,n,k]=c3*yd[0,n-1,k-2]+c1*yd[0,n-1,k]+c2*yd[0,n-1,k-1] else: yd[0,n,k]=0 for r in range(0,k): add=yd[0,n,r]*(ctx.mpf('1.0')*ctx.fac(2*k-2*r)/ctx.fac(k-r)) yd[0,n,k] -= ((-1)**(k-r))*add yd[0,n,-2]=0 yd[0,n,-1]=0 yd[0,n,3*n//2+1]=0 for mu in range(-2,der+1): for n in range(-2,L): for k in range(-3,max(1,3*n//2+2)): if( (mu<0)or (n<0) or(k<0)or (k>3*n//2)): yd[mu,n,k] = 0 for mu in range(1,der+1): for n in range(0,L): ctx.prec = ywpd[n]+10 for k in range(0,3*n//2+1): aux=(2*mu-2)*yd[mu-2,n-2,k-3]+2*(ysigma+n-2)*yd[mu-1,n-2,k-3] yd[mu,n,k] = aux - yd[mu-1,n-1,k-1] # COMPUTING THE COEFFICIENTS xtcoef[k,l] # See II Section 3.9 # # computing the needed wp xwptcoef={} xwpterm={} ctx.prec = 15 c1 = ctx.mag(40*(L+2)) xc2 = ctx.mag(68*(L+2)*xA) xc4 = ctx.mag(xB1*a*math.sqrt(ctx.pi))-1 for k in range(0,L): xc3 = xc2 - k*xc4+ctx.mag(ctx.fac(k+0.5))/2. xwptcoef[k] = (max(c1,xc3-ctx.mag(xeps4)+1)+1 +20)*1.5 xwpterm[k] = (max(c1,ctx.mag(L+2)+xc3-ctx.mag(xeps3)+1)+1 +20) ywptcoef={} ywpterm={} ctx.prec = 15 c1 = ctx.mag(40*(L+2)) yc2 = ctx.mag(68*(L+2)*yA) yc4 = ctx.mag(yB1*a*math.sqrt(ctx.pi))-1 for k in range(0,L): yc3 = yc2 - k*yc4+ctx.mag(ctx.fac(k+0.5))/2. ywptcoef[k] = ((max(c1,yc3-ctx.mag(yeps4)+1))+10)*1.5 ywpterm[k] = (max(c1,ctx.mag(L+2)+yc3-ctx.mag(yeps3)+1)+1)+10 # check of power of pi # computing the fortcoef[mu,k,ell] xfortcoef={} for mu in range(0,der+1): for k in range(0,L): for ell in range(-2,3*k//2+1): xfortcoef[mu,k,ell]=0 for mu in range(0,der+1): for k in range(0,L): ctx.prec = xwptcoef[k] for ell in range(0,3*k//2+1): xfortcoef[mu,k,ell]=xd[mu,k,ell]*Fp[3*k-2*ell]/pipowers[2*k-ell] xfortcoef[mu,k,ell]=xfortcoef[mu,k,ell]/((2*ctx.j)**ell) def trunc_a(t): wp = ctx.prec ctx.prec = wp + 2 aa = ctx.sqrt(t/(2*ctx.pi)) ctx.prec = wp return aa # computing the tcoef[k,ell] xtcoef={} for mu in range(0,der+1): for k in range(0,L): for ell in range(-2,3*k//2+1): xtcoef[mu,k,ell]=0 ctx.prec = max(xwptcoef[0],ywptcoef[0])+3 aa= trunc_a(t) la = -ctx.ln(aa) for chi in range(0,der+1): for k in range(0,L): ctx.prec = xwptcoef[k] for ell in range(0,3*k//2+1): xtcoef[chi,k,ell] =0 for mu in range(0, chi+1): tcoefter=ctx.binomial(chi,mu)*ctx.power(la,mu)*xfortcoef[chi-mu,k,ell] xtcoef[chi,k,ell] += tcoefter # COMPUTING THE COEFFICIENTS ytcoef[k,l] # See II Section 3.9 # # computing the needed wp # check of power of pi # computing the fortcoef[mu,k,ell] yfortcoef={} for mu in range(0,der+1): for k in range(0,L): for ell in range(-2,3*k//2+1): yfortcoef[mu,k,ell]=0 for mu in range(0,der+1): for k in range(0,L): ctx.prec = ywptcoef[k] for ell in range(0,3*k//2+1): yfortcoef[mu,k,ell]=yd[mu,k,ell]*Fp[3*k-2*ell]/pipowers[2*k-ell] yfortcoef[mu,k,ell]=yfortcoef[mu,k,ell]/((2*ctx.j)**ell) # computing the tcoef[k,ell] ytcoef={} for chi in range(0,der+1): for k in range(0,L): for ell in range(-2,3*k//2+1): ytcoef[chi,k,ell]=0 for chi in range(0,der+1): for k in range(0,L): ctx.prec = ywptcoef[k] for ell in range(0,3*k//2+1): ytcoef[chi,k,ell] =0 for mu in range(0, chi+1): tcoefter=ctx.binomial(chi,mu)*ctx.power(la,mu)*yfortcoef[chi-mu,k,ell] ytcoef[chi,k,ell] += tcoefter # COMPUTING tv[k,ell] # See II Section 3.8 # # a has a good value ctx.prec = max(xwptcoef[0], ywptcoef[0])+2 av = {} av[0] = 1 av[1] = av[0]/a ctx.prec = max(xwptcoef[0],ywptcoef[0]) for k in range(2,L): av[k] = av[k-1] * av[1] # Computing the quotients xtv = {} for chi in range(0,der+1): for k in range(0,L): ctx.prec = xwptcoef[k] for ell in range(0,3*k//2+1): xtv[chi,k,ell] = xtcoef[chi,k,ell]* av[k] # Computing the quotients ytv = {} for chi in range(0,der+1): for k in range(0,L): ctx.prec = ywptcoef[k] for ell in range(0,3*k//2+1): ytv[chi,k,ell] = ytcoef[chi,k,ell]* av[k] # COMPUTING THE TERMS xterm[k] # See II Section 3.6 xterm = {} for chi in range(0,der+1): for n in range(0,L): ctx.prec = xwpterm[n] te = 0 for k in range(0, 3*n//2+1): te += xtv[chi,n,k] xterm[chi,n] = te # COMPUTING THE TERMS yterm[k] # See II Section 3.6 yterm = {} for chi in range(0,der+1): for n in range(0,L): ctx.prec = ywpterm[n] te = 0 for k in range(0, 3*n//2+1): te += ytv[chi,n,k] yterm[chi,n] = te # COMPUTING rssum # See II Section 3.5 xrssum={} ctx.prec=15 xrsbound = math.sqrt(ctx.pi) * xc /(xb*a) ctx.prec=15 xwprssum = ctx.mag(4.4*((L+3)**2)*xrsbound / xeps2) xwprssum = max(xwprssum, ctx.mag(10*(L+1))) ctx.prec = xwprssum for chi in range(0,der+1): xrssum[chi] = 0 for k in range(1,L+1): xrssum[chi] += xterm[chi,L-k] yrssum={} ctx.prec=15 yrsbound = math.sqrt(ctx.pi) * yc /(yb*a) ctx.prec=15 ywprssum = ctx.mag(4.4*((L+3)**2)*yrsbound / yeps2) ywprssum = max(ywprssum, ctx.mag(10*(L+1))) ctx.prec = ywprssum for chi in range(0,der+1): yrssum[chi] = 0 for k in range(1,L+1): yrssum[chi] += yterm[chi,L-k] # COMPUTING S3 # See II Section 3.19 ctx.prec = 15 A2 = 2**(max(ctx.mag(abs(xrssum[0])), ctx.mag(abs(yrssum[0])))) eps8 = eps/(3*A2) T = t *ctx.ln(t/(2*ctx.pi)) xwps3 = 5 + ctx.mag((1+(2/eps8)*ctx.power(a,-xsigma))*T) ywps3 = 5 + ctx.mag((1+(2/eps8)*ctx.power(a,-ysigma))*T) ctx.prec = max(xwps3, ywps3) tpi = t/(2*ctx.pi) arg = (t/2)*ctx.ln(tpi)-(t/2)-ctx.pi/8 U = ctx.expj(-arg) a = trunc_a(t) xasigma = ctx.power(a, -xsigma) yasigma = ctx.power(a, -ysigma) xS3 = ((-1)**(N-1)) * xasigma * U yS3 = ((-1)**(N-1)) * yasigma * U # COMPUTING S1 the zetasum # See II Section 3.18 ctx.prec = 15 xwpsum = 4+ ctx.mag((N+ctx.power(N,1-xsigma))*ctx.ln(N) /eps1) ywpsum = 4+ ctx.mag((N+ctx.power(N,1-ysigma))*ctx.ln(N) /eps1) wpsum = max(xwpsum, ywpsum) ctx.prec = wpsum +10 ''' # This can be improved xS1={} yS1={} for chi in range(0,der+1): xS1[chi] = 0 yS1[chi] = 0 for n in range(1,int(N)+1): ln = ctx.ln(n) xexpn = ctx.exp(-ln*(xsigma+ctx.j*t)) yexpn = ctx.conj(1/(n*xexpn)) for chi in range(0,der+1): pown = ctx.power(-ln, chi) xterm = pown*xexpn yterm = pown*yexpn xS1[chi] += xterm yS1[chi] += yterm ''' xS1, yS1 = ctx._zetasum(s, 1, int(N)-1, range(0,der+1), True) # END OF COMPUTATION of xrz, yrz # See II Section 3.1 ctx.prec = 15 xabsS1 = abs(xS1[der]) xabsS2 = abs(xrssum[der] * xS3) xwpend = max(6, wpinitial+ctx.mag(6*(3*xabsS1+7*xabsS2) ) ) ctx.prec = xwpend xrz={} for chi in range(0,der+1): xrz[chi] = xS1[chi]+xrssum[chi]*xS3 ctx.prec = 15 yabsS1 = abs(yS1[der]) yabsS2 = abs(yrssum[der] * yS3) ywpend = max(6, wpinitial+ctx.mag(6*(3*yabsS1+7*yabsS2) ) ) ctx.prec = ywpend yrz={} for chi in range(0,der+1): yrz[chi] = yS1[chi]+yrssum[chi]*yS3 yrz[chi] = ctx.conj(yrz[chi]) ctx.prec = wpinitial return xrz, yrz def Rzeta_set(ctx, s, derivatives=[0]): r""" Computes several derivatives of the auxiliary function of Riemann `R(s)`. **Definition** The function is defined by .. math :: \begin{equation} {\mathop{\mathcal R }\nolimits}(s)= \int_{0\swarrow1}\frac{x^{-s} e^{\pi i x^2}}{e^{\pi i x}- e^{-\pi i x}}\,dx \end{equation} To this function we apply the Riemann-Siegel expansion. """ der = max(derivatives) # First we take the value of ctx.prec # During the computation we will change ctx.prec, and finally we will # restaurate the initial value wpinitial = ctx.prec # Take the real and imaginary part of s t = ctx._im(s) sigma = ctx._re(s) # Now compute several parameter that appear on the program ctx.prec = 15 a = ctx.sqrt(t/(2*ctx.pi)) # Careful asigma = ctx.power(a, sigma) # Careful # We need a simple bound A1 < asigma (see II Section 3.1 and 3.3) A1 = ctx.power(2, ctx.mag(asigma)-1) # We compute various epsilon's (see II end of Section 3.1) eps = ctx.power(2, -wpinitial) eps1 = eps/6. eps2 = eps * A1/3. # COMPUTING SOME COEFFICIENTS THAT DEPENDS # ON sigma # constant b and c (see I Theorem 2 formula (26) ) # coefficients A and B1 (see I Section 6.1 equation (50)) # here we not need high precision ctx.prec = 15 if sigma > 0: b = 2. c = math.pow(9,sigma)/4.44288 # 4.44288 =(math.sqrt(2)*math.pi) A = math.pow(9,sigma) B1 = 1 else: b = 2.25158 # math.sqrt( (3-2* math.log(2))*math.pi ) c = math.pow(2,-sigma)/4.44288 A = math.pow(2,-sigma) B1 = 1.10789 # = 2*sqrt(1-log(2)) # COMPUTING L THE NUMBER OF TERMS NEEDED IN THE RIEMANN-SIEGEL # CORRECTION # See II Section 3.2 ctx.prec = 15 L = 1 while 3*c*ctx.gamma(L*0.5) * ctx.power(b*a,-L) >= eps2: L = L+1 L = max(2,L) # The number L has to satify some conditions. # If not RS can not compute Rzeta(s) with the prescribed precision # (see II, Section 3.2 condition (20) ) and # (II, Section 3.3 condition (22) ). Also we have added # an additional technical condition in Section 3.17 Proposition 17 if ((3*L >= 2*a*a/25.) or (3*L+2+sigma<0) or (abs(sigma)> a/2.)): #print 'Error Riemann-Siegel can not compute with such precision' ctx.prec = wpinitial raise NotImplementedError("Riemann-Siegel can not compute with such precision") # INITIALIZATION (CONTINUATION) # # eps3 is the constant defined on (II, Section 3.5 equation (27) ) # each term of the RS correction must be computed with error <= eps3 eps3 = eps2/(4*L) # eps4 is defined on (II Section 3.6 equation (30) ) # each component of the formula (II Section 3.6 equation (29) ) # must be computed with error <= eps4 eps4 = eps3/(3*L) # COMPUTING M. NUMBER OF DERIVATIVES Fp[m] TO COMPUTE M = aux_M_Fp(ctx, A, eps4, a, B1, L) Fp = {} for n in range(M, 3*L-2): Fp[n] = 0 # But I have not seen an instance of M != 3*L-3 # # DETERMINATION OF J THE NUMBER OF TERMS NEEDED # IN THE TAYLOR SERIES OF F. # See II Section 3.11 equation (49)) h1 = eps4/(632*A) h2 = ctx.pi*ctx.pi*B1*a *ctx.sqrt(3)*math.e*math.e h2 = h1 * ctx.power((h2/M**2),(M-1)/3) / M h3 = min(h1,h2) J=12 jvalue = (2*ctx.pi)**J / ctx.gamma(J+1) while jvalue > h3: J = J+1 jvalue = (2*ctx.pi)*jvalue/J # COMPUTING eps5[m] for 1 <= m <= 21 # See II Section 10 equation (43) eps5={} foreps5 = math.pi*math.pi*B1*a for m in range(0,22): aux1 = math.pow(foreps5, m/3)/(316.*A) aux2 = ctx.gamma(m+1)/ctx.gamma(m/3.0+0.5) aux2 = math.sqrt(aux2) eps5[m] = aux1*aux2*eps4 # COMPUTING wpfp # See II Section 3.13 equation (59) twenty = min(3*L-3, 21)+1 aux = 6812*J wpfp = ctx.mag(44*J) for m in range(0, twenty): wpfp = max(wpfp, ctx.mag(aux*ctx.gamma(m+1)/eps5[m])) # COMPUTING N AND p # See II Section ctx.prec = wpfp + ctx.mag(t) + 20 a = ctx.sqrt(t/(2*ctx.pi)) N = ctx.floor(a) p = 1-2*(a-N) # now we get a rounded version of p to the precision wpfp # this possibly is not necessary num = ctx.floor(p*(ctx.mpf(2)**wpfp)) difference = p * (ctx.mpf(2)**wpfp)-num if difference < 0.5: num = num else: num = num+1 p = ctx.convert(num * (ctx.mpf(2)**(-wpfp))) # COMPUTING THE COEFFICIENTS c[n] = cc[n] # We shall use the notation cc[n], since there is # a constant that is called c # See II Section 3.14 # We compute the coefficients and also save then in a # cache. The bulk of the computation is passed to # the function coef() # # eps6 is defined in II Section 3.13 equation (58) eps6 = ctx.power(2*ctx.pi, J)/(ctx.gamma(J+1)*3*J) # Now we compute the coefficients cc={} cont={} cont, pipowers = coef(ctx, J, eps6) cc = cont.copy() # we need a copy since we have Fp={} for n in range(M, 3*L-2): Fp[n] = 0 ctx.prec = wpfp for m in range(0,M+1): sumP = 0 for k in range(2*J-m-1,-1,-1): sumP = (sumP * p) + cc[k] Fp[m] = sumP # preparation of the new coefficients for k in range(0, 2*J-m-1): cc[k] = (k+1) * cc[k+1] # COMPUTING THE NUMBERS d[n,k] # See II Section 3.17 # First we compute the working precisions wpd[k] # Se II equation (92) wpd = {} d1 = max(6, ctx.mag(40*L*L)) d2 = 13+ctx.mag((1+abs(sigma))*A)-ctx.mag(eps4)-1 const = ctx.ln(8/(ctx.pi*ctx.pi*a*a*B1*B1)) /2 for n in range(0,L): d3 = ctx.mag(ctx.sqrt(ctx.gamma(n-0.5)))-ctx.floor(n*const)+d2 wpd[n] = max(d3,d1) # procedure of II Section 3.17 ctx.prec = wpd[1]+10 psigma = 1-(2*sigma) d = {} d[0,0,-2]=0 d[0,0,-1]=0 d[0,0,0]=1 d[0,0,1]=0 d[0,-1,-2]=0 d[0,-1,-1]=0 d[0,-1,0]=1 d[0,-1,1]=0 for n in range(1,L): ctx.prec = wpd[n]+10 for k in range(0,3*n//2+1): m = 3*n-2*k if (m!=0): m1 = ctx.one/m c1 = m1/4 c2 = (psigma*m1)/2 c3 = -(m+1) d[0,n,k] = c3*d[0,n-1,k-2]+c1*d[0,n-1,k]+c2*d[0,n-1,k-1] else: d[0,n,k]=0 for r in range(0,k): add = d[0,n,r]*(ctx.one*ctx.fac(2*k-2*r)/ctx.fac(k-r)) d[0,n,k] -= ((-1)**(k-r))*add d[0,n,-2]=0 d[0,n,-1]=0 d[0,n,3*n//2+1]=0 for mu in range(-2,der+1): for n in range(-2,L): for k in range(-3,max(1,3*n//2+2)): if ((mu<0)or (n<0) or(k<0)or (k>3*n//2)): d[mu,n,k] = 0 for mu in range(1,der+1): for n in range(0,L): ctx.prec = wpd[n]+10 for k in range(0,3*n//2+1): aux=(2*mu-2)*d[mu-2,n-2,k-3]+2*(sigma+n-2)*d[mu-1,n-2,k-3] d[mu,n,k] = aux - d[mu-1,n-1,k-1] # COMPUTING THE COEFFICIENTS t[k,l] # See II Section 3.9 # # computing the needed wp wptcoef = {} wpterm = {} ctx.prec = 15 c1 = ctx.mag(40*(L+2)) c2 = ctx.mag(68*(L+2)*A) c4 = ctx.mag(B1*a*math.sqrt(ctx.pi))-1 for k in range(0,L): c3 = c2 - k*c4+ctx.mag(ctx.fac(k+0.5))/2. wptcoef[k] = max(c1,c3-ctx.mag(eps4)+1)+1 +10 wpterm[k] = max(c1,ctx.mag(L+2)+c3-ctx.mag(eps3)+1)+1 +10 # check of power of pi # computing the fortcoef[mu,k,ell] fortcoef={} for mu in derivatives: for k in range(0,L): for ell in range(-2,3*k//2+1): fortcoef[mu,k,ell]=0 for mu in derivatives: for k in range(0,L): ctx.prec = wptcoef[k] for ell in range(0,3*k//2+1): fortcoef[mu,k,ell]=d[mu,k,ell]*Fp[3*k-2*ell]/pipowers[2*k-ell] fortcoef[mu,k,ell]=fortcoef[mu,k,ell]/((2*ctx.j)**ell) def trunc_a(t): wp = ctx.prec ctx.prec = wp + 2 aa = ctx.sqrt(t/(2*ctx.pi)) ctx.prec = wp return aa # computing the tcoef[chi,k,ell] tcoef={} for chi in derivatives: for k in range(0,L): for ell in range(-2,3*k//2+1): tcoef[chi,k,ell]=0 ctx.prec = wptcoef[0]+3 aa = trunc_a(t) la = -ctx.ln(aa) for chi in derivatives: for k in range(0,L): ctx.prec = wptcoef[k] for ell in range(0,3*k//2+1): tcoef[chi,k,ell] = 0 for mu in range(0, chi+1): tcoefter = ctx.binomial(chi,mu) * la**mu * \ fortcoef[chi-mu,k,ell] tcoef[chi,k,ell] += tcoefter # COMPUTING tv[k,ell] # See II Section 3.8 # Computing the powers av[k] = a**(-k) ctx.prec = wptcoef[0] + 2 # a has a good value of a. # See II Section 3.6 av = {} av[0] = 1 av[1] = av[0]/a ctx.prec = wptcoef[0] for k in range(2,L): av[k] = av[k-1] * av[1] # Computing the quotients tv = {} for chi in derivatives: for k in range(0,L): ctx.prec = wptcoef[k] for ell in range(0,3*k//2+1): tv[chi,k,ell] = tcoef[chi,k,ell]* av[k] # COMPUTING THE TERMS term[k] # See II Section 3.6 term = {} for chi in derivatives: for n in range(0,L): ctx.prec = wpterm[n] te = 0 for k in range(0, 3*n//2+1): te += tv[chi,n,k] term[chi,n] = te # COMPUTING rssum # See II Section 3.5 rssum={} ctx.prec=15 rsbound = math.sqrt(ctx.pi) * c /(b*a) ctx.prec=15 wprssum = ctx.mag(4.4*((L+3)**2)*rsbound / eps2) wprssum = max(wprssum, ctx.mag(10*(L+1))) ctx.prec = wprssum for chi in derivatives: rssum[chi] = 0 for k in range(1,L+1): rssum[chi] += term[chi,L-k] # COMPUTING S3 # See II Section 3.19 ctx.prec = 15 A2 = 2**(ctx.mag(rssum[0])) eps8 = eps/(3* A2) T = t * ctx.ln(t/(2*ctx.pi)) wps3 = 5 + ctx.mag((1+(2/eps8)*ctx.power(a,-sigma))*T) ctx.prec = wps3 tpi = t/(2*ctx.pi) arg = (t/2)*ctx.ln(tpi)-(t/2)-ctx.pi/8 U = ctx.expj(-arg) a = trunc_a(t) asigma = ctx.power(a, -sigma) S3 = ((-1)**(N-1)) * asigma * U # COMPUTING S1 the zetasum # See II Section 3.18 ctx.prec = 15 wpsum = 4 + ctx.mag((N+ctx.power(N,1-sigma))*ctx.ln(N)/eps1) ctx.prec = wpsum + 10 ''' # This can be improved S1 = {} for chi in derivatives: S1[chi] = 0 for n in range(1,int(N)+1): ln = ctx.ln(n) expn = ctx.exp(-ln*(sigma+ctx.j*t)) for chi in derivatives: term = ctx.power(-ln, chi)*expn S1[chi] += term ''' S1 = ctx._zetasum(s, 1, int(N)-1, derivatives)[0] # END OF COMPUTATION # See II Section 3.1 ctx.prec = 15 absS1 = abs(S1[der]) absS2 = abs(rssum[der] * S3) wpend = max(6, wpinitial + ctx.mag(6*(3*absS1+7*absS2))) ctx.prec = wpend rz = {} for chi in derivatives: rz[chi] = S1[chi]+rssum[chi]*S3 ctx.prec = wpinitial return rz def z_half(ctx,t,der=0): r""" z_half(t,der=0) Computes Z^(der)(t) """ s=ctx.mpf('0.5')+ctx.j*t wpinitial = ctx.prec ctx.prec = 15 tt = t/(2*ctx.pi) wptheta = wpinitial +1 + ctx.mag(3*(tt**1.5)*ctx.ln(tt)) wpz = wpinitial + 1 + ctx.mag(12*tt*ctx.ln(tt)) ctx.prec = wptheta theta = ctx.siegeltheta(t) ctx.prec = wpz rz = Rzeta_set(ctx,s, range(der+1)) if der > 0: ps1 = ctx._re(ctx.psi(0,s/2)/2 - ctx.ln(ctx.pi)/2) if der > 1: ps2 = ctx._re(ctx.j*ctx.psi(1,s/2)/4) if der > 2: ps3 = ctx._re(-ctx.psi(2,s/2)/8) if der > 3: ps4 = ctx._re(-ctx.j*ctx.psi(3,s/2)/16) exptheta = ctx.expj(theta) if der == 0: z = 2*exptheta*rz[0] if der == 1: zf = 2j*exptheta z = zf*(ps1*rz[0]+rz[1]) if der == 2: zf = 2 * exptheta z = -zf*(2*rz[1]*ps1+rz[0]*ps1**2+rz[2]-ctx.j*rz[0]*ps2) if der == 3: zf = -2j*exptheta z = 3*rz[1]*ps1**2+rz[0]*ps1**3+3*ps1*rz[2] z = zf*(z-3j*rz[1]*ps2-3j*rz[0]*ps1*ps2+rz[3]-rz[0]*ps3) if der == 4: zf = 2*exptheta z = 4*rz[1]*ps1**3+rz[0]*ps1**4+6*ps1**2*rz[2] z = z-12j*rz[1]*ps1*ps2-6j*rz[0]*ps1**2*ps2-6j*rz[2]*ps2-3*rz[0]*ps2*ps2 z = z + 4*ps1*rz[3]-4*rz[1]*ps3-4*rz[0]*ps1*ps3+rz[4]+ctx.j*rz[0]*ps4 z = zf*z ctx.prec = wpinitial return ctx._re(z) def zeta_half(ctx, s, k=0): """ zeta_half(s,k=0) Computes zeta^(k)(s) when Re s = 0.5 """ wpinitial = ctx.prec sigma = ctx._re(s) t = ctx._im(s) #--- compute wptheta, wpR, wpbasic --- ctx.prec = 53 # X see II Section 3.21 (109) and (110) if sigma > 0: X = ctx.sqrt(abs(s)) else: X = (2*ctx.pi)**(sigma-1) * abs(1-s)**(0.5-sigma) # M1 see II Section 3.21 (111) and (112) if sigma > 0: M1 = 2*ctx.sqrt(t/(2*ctx.pi)) else: M1 = 4 * t * X # T see II Section 3.21 (113) abst = abs(0.5-s) T = 2* abst*math.log(abst) # computing wpbasic, wptheta, wpR see II Section 3.21 wpbasic = max(6,3+ctx.mag(t)) wpbasic2 = 2+ctx.mag(2.12*M1+21.2*M1*X+1.3*M1*X*T)+wpinitial+1 wpbasic = max(wpbasic, wpbasic2) wptheta = max(4, 3+ctx.mag(2.7*M1*X)+wpinitial+1) wpR = 3+ctx.mag(1.1+2*X)+wpinitial+1 ctx.prec = wptheta theta = ctx.siegeltheta(t-ctx.j*(sigma-ctx.mpf('0.5'))) if k > 0: ps1 = (ctx._re(ctx.psi(0,s/2)))/2 - ctx.ln(ctx.pi)/2 if k > 1: ps2 = -(ctx._im(ctx.psi(1,s/2)))/4 if k > 2: ps3 = -(ctx._re(ctx.psi(2,s/2)))/8 if k > 3: ps4 = (ctx._im(ctx.psi(3,s/2)))/16 ctx.prec = wpR xrz = Rzeta_set(ctx,s,range(k+1)) yrz={} for chi in range(0,k+1): yrz[chi] = ctx.conj(xrz[chi]) ctx.prec = wpbasic exptheta = ctx.expj(-2*theta) if k==0: zv = xrz[0]+exptheta*yrz[0] if k==1: zv1 = -yrz[1] - 2*yrz[0]*ps1 zv = xrz[1] + exptheta*zv1 if k==2: zv1 = 4*yrz[1]*ps1+4*yrz[0]*(ps1**2)+yrz[2]+2j*yrz[0]*ps2 zv = xrz[2]+exptheta*zv1 if k==3: zv1 = -12*yrz[1]*ps1**2-8*yrz[0]*ps1**3-6*yrz[2]*ps1-6j*yrz[1]*ps2 zv1 = zv1 - 12j*yrz[0]*ps1*ps2-yrz[3]+2*yrz[0]*ps3 zv = xrz[3]+exptheta*zv1 if k == 4: zv1 = 32*yrz[1]*ps1**3 +16*yrz[0]*ps1**4+24*yrz[2]*ps1**2 zv1 = zv1 +48j*yrz[1]*ps1*ps2+48j*yrz[0]*(ps1**2)*ps2 zv1 = zv1+12j*yrz[2]*ps2-12*yrz[0]*ps2**2+8*yrz[3]*ps1-8*yrz[1]*ps3 zv1 = zv1-16*yrz[0]*ps1*ps3+yrz[4]-2j*yrz[0]*ps4 zv = xrz[4]+exptheta*zv1 ctx.prec = wpinitial return zv def zeta_offline(ctx, s, k=0): """ Computes zeta^(k)(s) off the line """ wpinitial = ctx.prec sigma = ctx._re(s) t = ctx._im(s) #--- compute wptheta, wpR, wpbasic --- ctx.prec = 53 # X see II Section 3.21 (109) and (110) if sigma > 0: X = ctx.power(abs(s), 0.5) else: X = ctx.power(2*ctx.pi, sigma-1)*ctx.power(abs(1-s),0.5-sigma) # M1 see II Section 3.21 (111) and (112) if (sigma > 0): M1 = 2*ctx.sqrt(t/(2*ctx.pi)) else: M1 = 4 * t * X # M2 see II Section 3.21 (111) and (112) if (1-sigma > 0): M2 = 2*ctx.sqrt(t/(2*ctx.pi)) else: M2 = 4*t*ctx.power(2*ctx.pi, -sigma)*ctx.power(abs(s),sigma-0.5) # T see II Section 3.21 (113) abst = abs(0.5-s) T = 2* abst*math.log(abst) # computing wpbasic, wptheta, wpR see II Section 3.21 wpbasic = max(6,3+ctx.mag(t)) wpbasic2 = 2+ctx.mag(2.12*M1+21.2*M2*X+1.3*M2*X*T)+wpinitial+1 wpbasic = max(wpbasic, wpbasic2) wptheta = max(4, 3+ctx.mag(2.7*M2*X)+wpinitial+1) wpR = 3+ctx.mag(1.1+2*X)+wpinitial+1 ctx.prec = wptheta theta = ctx.siegeltheta(t-ctx.j*(sigma-ctx.mpf('0.5'))) s1 = s s2 = ctx.conj(1-s1) ctx.prec = wpR xrz, yrz = Rzeta_simul(ctx, s, k) if k > 0: ps1 = (ctx.psi(0,s1/2)+ctx.psi(0,(1-s1)/2))/4 - ctx.ln(ctx.pi)/2 if k > 1: ps2 = ctx.j*(ctx.psi(1,s1/2)-ctx.psi(1,(1-s1)/2))/8 if k > 2: ps3 = -(ctx.psi(2,s1/2)+ctx.psi(2,(1-s1)/2))/16 if k > 3: ps4 = -ctx.j*(ctx.psi(3,s1/2)-ctx.psi(3,(1-s1)/2))/32 ctx.prec = wpbasic exptheta = ctx.expj(-2*theta) if k == 0: zv = xrz[0]+exptheta*yrz[0] if k == 1: zv1 = -yrz[1]-2*yrz[0]*ps1 zv = xrz[1]+exptheta*zv1 if k == 2: zv1 = 4*yrz[1]*ps1+4*yrz[0]*(ps1**2) +yrz[2]+2j*yrz[0]*ps2 zv = xrz[2]+exptheta*zv1 if k == 3: zv1 = -12*yrz[1]*ps1**2 -8*yrz[0]*ps1**3-6*yrz[2]*ps1-6j*yrz[1]*ps2 zv1 = zv1 - 12j*yrz[0]*ps1*ps2-yrz[3]+2*yrz[0]*ps3 zv = xrz[3]+exptheta*zv1 if k == 4: zv1 = 32*yrz[1]*ps1**3 +16*yrz[0]*ps1**4+24*yrz[2]*ps1**2 zv1 = zv1 +48j*yrz[1]*ps1*ps2+48j*yrz[0]*(ps1**2)*ps2 zv1 = zv1+12j*yrz[2]*ps2-12*yrz[0]*ps2**2+8*yrz[3]*ps1-8*yrz[1]*ps3 zv1 = zv1-16*yrz[0]*ps1*ps3+yrz[4]-2j*yrz[0]*ps4 zv = xrz[4]+exptheta*zv1 ctx.prec = wpinitial return zv def z_offline(ctx, w, k=0): r""" Computes Z(w) and its derivatives off the line """ s = ctx.mpf('0.5')+ctx.j*w s1 = s s2 = ctx.conj(1-s1) wpinitial = ctx.prec ctx.prec = 35 # X see II Section 3.21 (109) and (110) # M1 see II Section 3.21 (111) and (112) if (ctx._re(s1) >= 0): M1 = 2*ctx.sqrt(ctx._im(s1)/(2 * ctx.pi)) X = ctx.sqrt(abs(s1)) else: X = (2*ctx.pi)**(ctx._re(s1)-1) * abs(1-s1)**(0.5-ctx._re(s1)) M1 = 4 * ctx._im(s1)*X # M2 see II Section 3.21 (111) and (112) if (ctx._re(s2) >= 0): M2 = 2*ctx.sqrt(ctx._im(s2)/(2 * ctx.pi)) else: M2 = 4 * ctx._im(s2)*(2*ctx.pi)**(ctx._re(s2)-1)*abs(1-s2)**(0.5-ctx._re(s2)) # T see II Section 3.21 Prop. 27 T = 2*abs(ctx.siegeltheta(w)) # defining some precisions # see II Section 3.22 (115), (116), (117) aux1 = ctx.sqrt(X) aux2 = aux1*(M1+M2) aux3 = 3 +wpinitial wpbasic = max(6, 3+ctx.mag(T), ctx.mag(aux2*(26+2*T))+aux3) wptheta = max(4,ctx.mag(2.04*aux2)+aux3) wpR = ctx.mag(4*aux1)+aux3 # now the computations ctx.prec = wptheta theta = ctx.siegeltheta(w) ctx.prec = wpR xrz, yrz = Rzeta_simul(ctx,s,k) pta = 0.25 + 0.5j*w ptb = 0.25 - 0.5j*w if k > 0: ps1 = 0.25*(ctx.psi(0,pta)+ctx.psi(0,ptb)) - ctx.ln(ctx.pi)/2 if k > 1: ps2 = (1j/8)*(ctx.psi(1,pta)-ctx.psi(1,ptb)) if k > 2: ps3 = (-1./16)*(ctx.psi(2,pta)+ctx.psi(2,ptb)) if k > 3: ps4 = (-1j/32)*(ctx.psi(3,pta)-ctx.psi(3,ptb)) ctx.prec = wpbasic exptheta = ctx.expj(theta) if k == 0: zv = exptheta*xrz[0]+yrz[0]/exptheta j = ctx.j if k == 1: zv = j*exptheta*(xrz[1]+xrz[0]*ps1)-j*(yrz[1]+yrz[0]*ps1)/exptheta if k == 2: zv = exptheta*(-2*xrz[1]*ps1-xrz[0]*ps1**2-xrz[2]+j*xrz[0]*ps2) zv =zv + (-2*yrz[1]*ps1-yrz[0]*ps1**2-yrz[2]-j*yrz[0]*ps2)/exptheta if k == 3: zv1 = -3*xrz[1]*ps1**2-xrz[0]*ps1**3-3*xrz[2]*ps1+j*3*xrz[1]*ps2 zv1 = (zv1+ 3j*xrz[0]*ps1*ps2-xrz[3]+xrz[0]*ps3)*j*exptheta zv2 = 3*yrz[1]*ps1**2+yrz[0]*ps1**3+3*yrz[2]*ps1+j*3*yrz[1]*ps2 zv2 = j*(zv2 + 3j*yrz[0]*ps1*ps2+ yrz[3]-yrz[0]*ps3)/exptheta zv = zv1+zv2 if k == 4: zv1 = 4*xrz[1]*ps1**3+xrz[0]*ps1**4 + 6*xrz[2]*ps1**2 zv1 = zv1-12j*xrz[1]*ps1*ps2-6j*xrz[0]*ps1**2*ps2-6j*xrz[2]*ps2 zv1 = zv1-3*xrz[0]*ps2*ps2+4*xrz[3]*ps1-4*xrz[1]*ps3-4*xrz[0]*ps1*ps3 zv1 = zv1+xrz[4]+j*xrz[0]*ps4 zv2 = 4*yrz[1]*ps1**3+yrz[0]*ps1**4 + 6*yrz[2]*ps1**2 zv2 = zv2+12j*yrz[1]*ps1*ps2+6j*yrz[0]*ps1**2*ps2+6j*yrz[2]*ps2 zv2 = zv2-3*yrz[0]*ps2*ps2+4*yrz[3]*ps1-4*yrz[1]*ps3-4*yrz[0]*ps1*ps3 zv2 = zv2+yrz[4]-j*yrz[0]*ps4 zv = exptheta*zv1+zv2/exptheta ctx.prec = wpinitial return zv @defun def rs_zeta(ctx, s, derivative=0, **kwargs): if derivative > 4: raise NotImplementedError s = ctx.convert(s) re = ctx._re(s) im = ctx._im(s) if im < 0: z = ctx.conj(ctx.rs_zeta(ctx.conj(s), derivative)) return z critical_line = (re == 0.5) if critical_line: return zeta_half(ctx, s, derivative) else: return zeta_offline(ctx, s, derivative) @defun def rs_z(ctx, w, derivative=0): w = ctx.convert(w) re = ctx._re(w) im = ctx._im(w) if re < 0: return rs_z(ctx, -w, derivative) critical_line = (im == 0) if critical_line : return z_half(ctx, w, derivative) else: return z_offline(ctx, w, derivative) sympy-0.7.4.1/sympy/mpmath/functions/zeta.py0000644000175000017500000010621712253362407021247 0ustar georgeskgeorgeskfrom ..libmp.backend import xrange from .functions import defun, defun_wrapped, defun_static @defun def stieltjes(ctx, n, a=1): n = ctx.convert(n) a = ctx.convert(a) if n < 0: return ctx.bad_domain("Stieltjes constants defined for n >= 0") if hasattr(ctx, "stieltjes_cache"): stieltjes_cache = ctx.stieltjes_cache else: stieltjes_cache = ctx.stieltjes_cache = {} if a == 1: if n == 0: return +ctx.euler if n in stieltjes_cache: prec, s = stieltjes_cache[n] if prec >= ctx.prec: return +s mag = 1 def f(x): xa = x/a v = (xa-ctx.j)*ctx.ln(a-ctx.j*x)**n/(1+xa**2)/(ctx.exp(2*ctx.pi*x)-1) return ctx._re(v) / mag orig = ctx.prec try: # Normalize integrand by approx. magnitude to # speed up quadrature (which uses absolute error) if n > 50: ctx.prec = 20 mag = ctx.quad(f, [0,ctx.inf], maxdegree=3) ctx.prec = orig + 10 + int(n**0.5) s = ctx.quad(f, [0,ctx.inf], maxdegree=20) v = ctx.ln(a)**n/(2*a) - ctx.ln(a)**(n+1)/(n+1) + 2*s/a*mag finally: ctx.prec = orig if a == 1 and ctx.isint(n): stieltjes_cache[n] = (ctx.prec, v) return +v @defun_wrapped def siegeltheta(ctx, t, derivative=0): d = int(derivative) if (t == ctx.inf or t == ctx.ninf): if d < 2: if t == ctx.ninf and d == 0: return ctx.ninf return ctx.inf else: return ctx.zero if d == 0: if ctx._im(t): # XXX: cancellation occurs a = ctx.loggamma(0.25+0.5j*t) b = ctx.loggamma(0.25-0.5j*t) return -ctx.ln(ctx.pi)/2*t - 0.5j*(a-b) else: if ctx.isinf(t): return t return ctx._im(ctx.loggamma(0.25+0.5j*t)) - ctx.ln(ctx.pi)/2*t if d > 0: a = (-0.5j)**(d-1)*ctx.polygamma(d-1, 0.25-0.5j*t) b = (0.5j)**(d-1)*ctx.polygamma(d-1, 0.25+0.5j*t) if ctx._im(t): if d == 1: return -0.5*ctx.log(ctx.pi)+0.25*(a+b) else: return 0.25*(a+b) else: if d == 1: return ctx._re(-0.5*ctx.log(ctx.pi)+0.25*(a+b)) else: return ctx._re(0.25*(a+b)) @defun_wrapped def grampoint(ctx, n): # asymptotic expansion, from # http://mathworld.wolfram.com/GramPoint.html g = 2*ctx.pi*ctx.exp(1+ctx.lambertw((8*n+1)/(8*ctx.e))) return ctx.findroot(lambda t: ctx.siegeltheta(t)-ctx.pi*n, g) @defun_wrapped def siegelz(ctx, t, **kwargs): d = int(kwargs.get("derivative", 0)) t = ctx.convert(t) t1 = ctx._re(t) t2 = ctx._im(t) prec = ctx.prec try: if abs(t1) > 500*prec and t2**2 < t1: v = ctx.rs_z(t, d) if ctx._is_real_type(t): return ctx._re(v) return v except NotImplementedError: pass ctx.prec += 21 e1 = ctx.expj(ctx.siegeltheta(t)) z = ctx.zeta(0.5+ctx.j*t) if d == 0: v = e1*z ctx.prec=prec if ctx._is_real_type(t): return ctx._re(v) return +v z1 = ctx.zeta(0.5+ctx.j*t, derivative=1) theta1 = ctx.siegeltheta(t, derivative=1) if d == 1: v = ctx.j*e1*(z1+z*theta1) ctx.prec=prec if ctx._is_real_type(t): return ctx._re(v) return +v z2 = ctx.zeta(0.5+ctx.j*t, derivative=2) theta2 = ctx.siegeltheta(t, derivative=2) comb1 = theta1**2-ctx.j*theta2 if d == 2: def terms(): return [2*z1*theta1, z2, z*comb1] v = ctx.sum_accurately(terms, 1) v = -e1*v ctx.prec = prec if ctx._is_real_type(t): return ctx._re(v) return +v ctx.prec += 10 z3 = ctx.zeta(0.5+ctx.j*t, derivative=3) theta3 = ctx.siegeltheta(t, derivative=3) comb2 = theta1**3-3*ctx.j*theta1*theta2-theta3 if d == 3: def terms(): return [3*theta1*z2, 3*z1*comb1, z3+z*comb2] v = ctx.sum_accurately(terms, 1) v = -ctx.j*e1*v ctx.prec = prec if ctx._is_real_type(t): return ctx._re(v) return +v z4 = ctx.zeta(0.5+ctx.j*t, derivative=4) theta4 = ctx.siegeltheta(t, derivative=4) def terms(): return [theta1**4, -6*ctx.j*theta1**2*theta2, -3*theta2**2, -4*theta1*theta3, ctx.j*theta4] comb3 = ctx.sum_accurately(terms, 1) if d == 4: def terms(): return [6*theta1**2*z2, -6*ctx.j*z2*theta2, 4*theta1*z3, 4*z1*comb2, z4, z*comb3] v = ctx.sum_accurately(terms, 1) v = e1*v ctx.prec = prec if ctx._is_real_type(t): return ctx._re(v) return +v if d > 4: h = lambda x: ctx.siegelz(x, derivative=4) return ctx.diff(h, t, n=d-4) _zeta_zeros = [ 14.134725142,21.022039639,25.010857580,30.424876126,32.935061588, 37.586178159,40.918719012,43.327073281,48.005150881,49.773832478, 52.970321478,56.446247697,59.347044003,60.831778525,65.112544048, 67.079810529,69.546401711,72.067157674,75.704690699,77.144840069, 79.337375020,82.910380854,84.735492981,87.425274613,88.809111208, 92.491899271,94.651344041,95.870634228,98.831194218,101.317851006, 103.725538040,105.446623052,107.168611184,111.029535543,111.874659177, 114.320220915,116.226680321,118.790782866,121.370125002,122.946829294, 124.256818554,127.516683880,129.578704200,131.087688531,133.497737203, 134.756509753,138.116042055,139.736208952,141.123707404,143.111845808, 146.000982487,147.422765343,150.053520421,150.925257612,153.024693811, 156.112909294,157.597591818,158.849988171,161.188964138,163.030709687, 165.537069188,167.184439978,169.094515416,169.911976479,173.411536520, 174.754191523,176.441434298,178.377407776,179.916484020,182.207078484, 184.874467848,185.598783678,187.228922584,189.416158656,192.026656361, 193.079726604,195.265396680,196.876481841,198.015309676,201.264751944, 202.493594514,204.189671803,205.394697202,207.906258888,209.576509717, 211.690862595,213.347919360,214.547044783,216.169538508,219.067596349, 220.714918839,221.430705555,224.007000255,224.983324670,227.421444280, 229.337413306,231.250188700,231.987235253,233.693404179,236.524229666, ] def _load_zeta_zeros(url): import urllib d = urllib.urlopen(url) L = [float(x) for x in d.readlines()] # Sanity check assert round(L[0]) == 14 _zeta_zeros[:] = L @defun def oldzetazero(ctx, n, url='http://www.dtc.umn.edu/~odlyzko/zeta_tables/zeros1'): n = int(n) if n < 0: return ctx.zetazero(-n).conjugate() if n == 0: raise ValueError("n must be nonzero") if n > len(_zeta_zeros) and n <= 100000: _load_zeta_zeros(url) if n > len(_zeta_zeros): raise NotImplementedError("n too large for zetazeros") return ctx.mpc(0.5, ctx.findroot(ctx.siegelz, _zeta_zeros[n-1])) @defun_wrapped def riemannr(ctx, x): if x == 0: return ctx.zero # Check if a simple asymptotic estimate is accurate enough if abs(x) > 1000: a = ctx.li(x) b = 0.5*ctx.li(ctx.sqrt(x)) if abs(b) < abs(a)*ctx.eps: return a if abs(x) < 0.01: # XXX ctx.prec += int(-ctx.log(abs(x),2)) # Sum Gram's series s = t = ctx.one u = ctx.ln(x) k = 1 while abs(t) > abs(s)*ctx.eps: t = t * u / k s += t / (k * ctx._zeta_int(k+1)) k += 1 return s @defun_static def primepi(ctx, x): x = int(x) if x < 2: return 0 return len(ctx.list_primes(x)) # TODO: fix the interface wrt contexts @defun_wrapped def primepi2(ctx, x): x = int(x) if x < 2: return ctx._iv.zero if x < 2657: return ctx._iv.mpf(ctx.primepi(x)) mid = ctx.li(x) # Schoenfeld's estimate for x >= 2657, assuming RH err = ctx.sqrt(x,rounding='u')*ctx.ln(x,rounding='u')/8/ctx.pi(rounding='d') a = ctx.floor((ctx._iv.mpf(mid)-err).a, rounding='d') b = ctx.ceil((ctx._iv.mpf(mid)+err).b, rounding='u') return ctx._iv.mpf([a,b]) @defun_wrapped def primezeta(ctx, s): if ctx.isnan(s): return s if ctx.re(s) <= 0: raise ValueError("prime zeta function defined only for re(s) > 0") if s == 1: return ctx.inf if s == 0.5: return ctx.mpc(ctx.ninf, ctx.pi) r = ctx.re(s) if r > ctx.prec: return 0.5**s else: wp = ctx.prec + int(r) def terms(): orig = ctx.prec # zeta ~ 1+eps; need to set precision # to get logarithm accurately k = 0 while 1: k += 1 u = ctx.moebius(k) if not u: continue ctx.prec = wp t = u*ctx.ln(ctx.zeta(k*s))/k if not t: return #print ctx.prec, ctx.nstr(t) ctx.prec = orig yield t return ctx.sum_accurately(terms) # TODO: for bernpoly and eulerpoly, ensure that all exact zeros are covered @defun_wrapped def bernpoly(ctx, n, z): # Slow implementation: #return sum(ctx.binomial(n,k)*ctx.bernoulli(k)*z**(n-k) for k in xrange(0,n+1)) n = int(n) if n < 0: raise ValueError("Bernoulli polynomials only defined for n >= 0") if z == 0 or (z == 1 and n > 1): return ctx.bernoulli(n) if z == 0.5: return (ctx.ldexp(1,1-n)-1)*ctx.bernoulli(n) if n <= 3: if n == 0: return z ** 0 if n == 1: return z - 0.5 if n == 2: return (6*z*(z-1)+1)/6 if n == 3: return z*(z*(z-1.5)+0.5) if ctx.isinf(z): return z ** n if ctx.isnan(z): return z if abs(z) > 2: def terms(): t = ctx.one yield t r = ctx.one/z k = 1 while k <= n: t = t*(n+1-k)/k*r if not (k > 2 and k & 1): yield t*ctx.bernoulli(k) k += 1 return ctx.sum_accurately(terms) * z**n else: def terms(): yield ctx.bernoulli(n) t = ctx.one k = 1 while k <= n: t = t*(n+1-k)/k * z m = n-k if not (m > 2 and m & 1): yield t*ctx.bernoulli(m) k += 1 return ctx.sum_accurately(terms) @defun_wrapped def eulerpoly(ctx, n, z): n = int(n) if n < 0: raise ValueError("Euler polynomials only defined for n >= 0") if n <= 2: if n == 0: return z ** 0 if n == 1: return z - 0.5 if n == 2: return z*(z-1) if ctx.isinf(z): return z**n if ctx.isnan(z): return z m = n+1 if z == 0: return -2*(ctx.ldexp(1,m)-1)*ctx.bernoulli(m)/m * z**0 if z == 1: return 2*(ctx.ldexp(1,m)-1)*ctx.bernoulli(m)/m * z**0 if z == 0.5: if n % 2: return ctx.zero # Use exact code for Euler numbers if n < 100 or n*ctx.mag(0.46839865*n) < ctx.prec*0.25: return ctx.ldexp(ctx._eulernum(n), -n) # http://functions.wolfram.com/Polynomials/EulerE2/06/01/02/01/0002/ def terms(): t = ctx.one k = 0 w = ctx.ldexp(1,n+2) while 1: v = n-k+1 if not (v > 2 and v & 1): yield (2-w)*ctx.bernoulli(v)*t k += 1 if k > n: break t = t*z*(n-k+2)/k w *= 0.5 return ctx.sum_accurately(terms) / m @defun def eulernum(ctx, n, exact=False): n = int(n) if exact: return int(ctx._eulernum(n)) if n < 100: return ctx.mpf(ctx._eulernum(n)) if n % 2: return ctx.zero return ctx.ldexp(ctx.eulerpoly(n,0.5), n) # TODO: this should be implemented low-level def polylog_series(ctx, s, z): tol = +ctx.eps l = ctx.zero k = 1 zk = z while 1: term = zk / k**s l += term if abs(term) < tol: break zk *= z k += 1 return l def polylog_continuation(ctx, n, z): if n < 0: return z*0 twopij = 2j * ctx.pi a = -twopij**n/ctx.fac(n) * ctx.bernpoly(n, ctx.ln(z)/twopij) if ctx._is_real_type(z) and z < 0: a = ctx._re(a) if ctx._im(z) < 0 or (ctx._im(z) == 0 and ctx._re(z) >= 1): a -= twopij*ctx.ln(z)**(n-1)/ctx.fac(n-1) return a def polylog_unitcircle(ctx, n, z): tol = +ctx.eps if n > 1: l = ctx.zero logz = ctx.ln(z) logmz = ctx.one m = 0 while 1: if (n-m) != 1: term = ctx.zeta(n-m) * logmz / ctx.fac(m) if term and abs(term) < tol: break l += term logmz *= logz m += 1 l += ctx.ln(z)**(n-1)/ctx.fac(n-1)*(ctx.harmonic(n-1)-ctx.ln(-ctx.ln(z))) elif n < 1: # else l = ctx.fac(-n)*(-ctx.ln(z))**(n-1) logz = ctx.ln(z) logkz = ctx.one k = 0 while 1: b = ctx.bernoulli(k-n+1) if b: term = b*logkz/(ctx.fac(k)*(k-n+1)) if abs(term) < tol: break l -= term logkz *= logz k += 1 else: raise ValueError if ctx._is_real_type(z) and z < 0: l = ctx._re(l) return l def polylog_general(ctx, s, z): v = ctx.zero u = ctx.ln(z) if not abs(u) < 5: # theoretically |u| < 2*pi raise NotImplementedError("polylog for arbitrary s and z") t = 1 k = 0 while 1: term = ctx.zeta(s-k) * t if abs(term) < ctx.eps: break v += term k += 1 t *= u t /= k return ctx.gamma(1-s)*(-u)**(s-1) + v @defun_wrapped def polylog(ctx, s, z): s = ctx.convert(s) z = ctx.convert(z) if z == 1: return ctx.zeta(s) if z == -1: return -ctx.altzeta(s) if s == 0: return z/(1-z) if s == 1: return -ctx.ln(1-z) if s == -1: return z/(1-z)**2 if abs(z) <= 0.75 or (not ctx.isint(s) and abs(z) < 0.9): return polylog_series(ctx, s, z) if abs(z) >= 1.4 and ctx.isint(s): return (-1)**(s+1)*polylog_series(ctx, s, 1/z) + polylog_continuation(ctx, s, z) if ctx.isint(s): return polylog_unitcircle(ctx, int(s), z) return polylog_general(ctx, s, z) #raise NotImplementedError("polylog for arbitrary s and z") # This could perhaps be used in some cases #from quadrature import quad #return quad(lambda t: t**(s-1)/(exp(t)/z-1),[0,inf])/gamma(s) @defun_wrapped def clsin(ctx, s, z, pi=False): if ctx.isint(s) and s < 0 and int(s) % 2 == 1: return z*0 if pi: a = ctx.expjpi(z) else: a = ctx.expj(z) if ctx._is_real_type(z) and ctx._is_real_type(s): return ctx.im(ctx.polylog(s,a)) b = 1/a return (-0.5j)*(ctx.polylog(s,a) - ctx.polylog(s,b)) @defun_wrapped def clcos(ctx, s, z, pi=False): if ctx.isint(s) and s < 0 and int(s) % 2 == 0: return z*0 if pi: a = ctx.expjpi(z) else: a = ctx.expj(z) if ctx._is_real_type(z) and ctx._is_real_type(s): return ctx.re(ctx.polylog(s,a)) b = 1/a return 0.5*(ctx.polylog(s,a) + ctx.polylog(s,b)) @defun def altzeta(ctx, s, **kwargs): try: return ctx._altzeta(s, **kwargs) except NotImplementedError: return ctx._altzeta_generic(s) @defun_wrapped def _altzeta_generic(ctx, s): if s == 1: return ctx.ln2 + 0*s return -ctx.powm1(2, 1-s) * ctx.zeta(s) @defun def zeta(ctx, s, a=1, derivative=0, method=None, **kwargs): d = int(derivative) if a == 1 and not (d or method): try: return ctx._zeta(s, **kwargs) except NotImplementedError: pass s = ctx.convert(s) prec = ctx.prec method = kwargs.get('method') verbose = kwargs.get('verbose') if a == 1 and method != 'euler-maclaurin': im = abs(ctx._im(s)) re = abs(ctx._re(s)) #if (im < prec or method == 'borwein') and not derivative: # try: # if verbose: # print "zeta: Attempting to use the Borwein algorithm" # return ctx._zeta(s, **kwargs) # except NotImplementedError: # if verbose: # print "zeta: Could not use the Borwein algorithm" # pass if abs(im) > 500*prec and 10*re < prec and derivative <= 4 or \ method == 'riemann-siegel': try: # py2.4 compatible try block try: if verbose: print("zeta: Attempting to use the Riemann-Siegel algorithm") return ctx.rs_zeta(s, derivative, **kwargs) except NotImplementedError: if verbose: print("zeta: Could not use the Riemann-Siegel algorithm") pass finally: ctx.prec = prec if s == 1: return ctx.inf abss = abs(s) if abss == ctx.inf: if ctx.re(s) == ctx.inf: if d == 0: return ctx.one return ctx.zero return s*0 elif ctx.isnan(abss): return 1/s if ctx.re(s) > 2*ctx.prec and a == 1 and not derivative: return ctx.one + ctx.power(2, -s) return +ctx._hurwitz(s, a, d, **kwargs) @defun def _hurwitz(ctx, s, a=1, d=0, **kwargs): prec = ctx.prec verbose = kwargs.get('verbose') try: extraprec = 10 ctx.prec += extraprec # We strongly want to special-case rational a a, atype = ctx._convert_param(a) if ctx.re(s) < 0: if verbose: print("zeta: Attempting reflection formula") try: return _hurwitz_reflection(ctx, s, a, d, atype) except NotImplementedError: pass if verbose: print("zeta: Reflection formula failed") if verbose: print("zeta: Using the Euler-Maclaurin algorithm") while 1: ctx.prec = prec + extraprec T1, T2 = _hurwitz_em(ctx, s, a, d, prec+10, verbose) cancellation = ctx.mag(T1) - ctx.mag(T1+T2) if verbose: print("Term 1:", T1) print("Term 2:", T2) print("Cancellation:", cancellation, "bits") if cancellation < extraprec: return T1 + T2 else: extraprec = max(2*extraprec, min(cancellation + 5, 100*prec)) if extraprec > kwargs.get('maxprec', 100*prec): raise ctx.NoConvergence("zeta: too much cancellation") finally: ctx.prec = prec def _hurwitz_reflection(ctx, s, a, d, atype): # TODO: implement for derivatives if d != 0: raise NotImplementedError res = ctx.re(s) negs = -s # Integer reflection formula if ctx.isnpint(s): n = int(res) if n <= 0: return ctx.bernpoly(1-n, a) / (n-1) t = 1-s # We now require a to be standardized v = 0 shift = 0 b = a while ctx.re(b) > 1: b -= 1 v -= b**negs shift -= 1 while ctx.re(b) <= 0: v += b**negs b += 1 shift += 1 # Rational reflection formula if atype == 'Q' or atype == 'Z': try: p, q = a._mpq_ except: assert a == int(a) p = int(a) q = 1 p += shift*q assert 1 <= p <= q g = ctx.fsum(ctx.cospi(t/2-2*k*b)*ctx._hurwitz(t,(k,q)) \ for k in range(1,q+1)) g *= 2*ctx.gamma(t)/(2*ctx.pi*q)**t v += g return v # General reflection formula # Note: clcos/clsin can raise NotImplementedError else: C1, C2 = ctx.cospi_sinpi(0.5*t) # Clausen functions; could maybe use polylog directly if C1: C1 *= ctx.clcos(t, 2*a, pi=True) if C2: C2 *= ctx.clsin(t, 2*a, pi=True) v += 2*ctx.gamma(t)/(2*ctx.pi)**t*(C1+C2) return v def _hurwitz_em(ctx, s, a, d, prec, verbose): # May not be converted at this point a = ctx.convert(a) tol = -prec # Estimate number of terms for Euler-Maclaurin summation; could be improved M1 = 0 M2 = prec // 3 N = M2 lsum = 0 # This speeds up the recurrence for derivatives if ctx.isint(s): s = int(ctx._re(s)) s1 = s-1 while 1: # Truncated L-series l = ctx._zetasum(s, M1+a, M2-M1-1, [d])[0][0] #if d: # l = ctx.fsum((-ctx.ln(n+a))**d * (n+a)**negs for n in range(M1,M2)) #else: # l = ctx.fsum((n+a)**negs for n in range(M1,M2)) lsum += l M2a = M2+a logM2a = ctx.ln(M2a) logM2ad = logM2a**d logs = [logM2ad] logr = 1/logM2a rM2a = 1/M2a M2as = rM2a**s if d: tailsum = ctx.gammainc(d+1, s1*logM2a) / s1**(d+1) else: tailsum = 1/((s1)*(M2a)**s1) tailsum += 0.5 * logM2ad * M2as U = [1] r = M2as fact = 2 for j in range(1, N+1): # TODO: the following could perhaps be tidied a bit j2 = 2*j if j == 1: upds = [1] else: upds = [j2-2, j2-1] for m in upds: D = min(m,d+1) if m <= d: logs.append(logs[-1] * logr) Un = [0]*(D+1) for i in xrange(D): Un[i] = (1-m-s)*U[i] for i in xrange(1,D+1): Un[i] += (d-(i-1))*U[i-1] U = Un r *= rM2a t = ctx.fdot(U, logs) * r * ctx.bernoulli(j2)/(-fact) tailsum += t if ctx.mag(t) < tol: return lsum, (-1)**d * tailsum fact *= (j2+1)*(j2+2) if verbose: print("Sum range:", M1, M2, "term magnitude", ctx.mag(t), "tolerance", tol) M1, M2 = M2, M2*2 if ctx.re(s) < 0: N += N//2 @defun def _zetasum(ctx, s, a, n, derivatives=[0], reflect=False): """ Returns [xd0,xd1,...,xdr], [yd0,yd1,...ydr] where xdk = D^k ( 1/a^s + 1/(a+1)^s + ... + 1/(a+n)^s ) ydk = D^k conj( 1/a^(1-s) + 1/(a+1)^(1-s) + ... + 1/(a+n)^(1-s) ) D^k = kth derivative with respect to s, k ranges over the given list of derivatives (which should consist of either a single element or a range 0,1,...r). If reflect=False, the ydks are not computed. """ #print "zetasum", s, a, n try: return ctx._zetasum_fast(s, a, n, derivatives, reflect) except NotImplementedError: pass negs = ctx.fneg(s, exact=True) have_derivatives = derivatives != [0] have_one_derivative = len(derivatives) == 1 if not reflect: if not have_derivatives: return [ctx.fsum((a+k)**negs for k in xrange(n+1))], [] if have_one_derivative: d = derivatives[0] x = ctx.fsum(ctx.ln(a+k)**d * (a+k)**negs for k in xrange(n+1)) return [(-1)**d * x], [] maxd = max(derivatives) if not have_one_derivative: derivatives = range(maxd+1) xs = [ctx.zero for d in derivatives] if reflect: ys = [ctx.zero for d in derivatives] else: ys = [] for k in xrange(n+1): w = a + k xterm = w ** negs if reflect: yterm = ctx.conj(ctx.one / (w * xterm)) if have_derivatives: logw = -ctx.ln(w) if have_one_derivative: logw = logw ** maxd xs[0] += xterm * logw if reflect: ys[0] += yterm * logw else: t = ctx.one for d in derivatives: xs[d] += xterm * t if reflect: ys[d] += yterm * t t *= logw else: xs[0] += xterm if reflect: ys[0] += yterm return xs, ys @defun def dirichlet(ctx, s, chi=[1], derivative=0): s = ctx.convert(s) q = len(chi) d = int(derivative) if d > 2: raise NotImplementedError("arbitrary order derivatives") prec = ctx.prec try: ctx.prec += 10 if s == 1: have_pole = True for x in chi: if x and x != 1: have_pole = False h = +ctx.eps ctx.prec *= 2*(d+1) s += h if have_pole: return +ctx.inf z = ctx.zero for p in range(1,q+1): if chi[p%q]: if d == 1: z += chi[p%q] * (ctx.zeta(s, (p,q), 1) - \ ctx.zeta(s, (p,q))*ctx.log(q)) else: z += chi[p%q] * ctx.zeta(s, (p,q)) z /= q**s finally: ctx.prec = prec return +z def secondzeta_main_term(ctx, s, a, **kwargs): tol = ctx.eps f = lambda n: ctx.gammainc(0.5*s, a*gamm**2, regularized=True)*gamm**(-s) totsum = term = ctx.zero mg = ctx.inf n = 0 while mg > tol: totsum += term n += 1 gamm = ctx.im(ctx.zetazero_memoized(n)) term = f(n) mg = abs(term) err = 0 if kwargs.get("error"): sg = ctx.re(s) err = 0.5*ctx.pi**(-1)*max(1,sg)*a**(sg-0.5)*ctx.log(gamm/(2*ctx.pi))*\ ctx.gammainc(-0.5, a*gamm**2)/abs(ctx.gamma(s/2)) err = abs(err) return +totsum, err, n def secondzeta_prime_term(ctx, s, a, **kwargs): tol = ctx.eps f = lambda n: ctx.gammainc(0.5*(1-s),0.25*ctx.log(n)**2 * a**(-1))*\ ((0.5*ctx.log(n))**(s-1))*ctx.mangoldt(n)/ctx.sqrt(n)/\ (2*ctx.gamma(0.5*s)*ctx.sqrt(ctx.pi)) totsum = term = ctx.zero mg = ctx.inf n = 1 while mg > tol or n < 9: totsum += term n += 1 term = f(n) if term == 0: mg = ctx.inf else: mg = abs(term) if kwargs.get("error"): err = mg return +totsum, err, n def secondzeta_exp_term(ctx, s, a): if ctx.isint(s) and ctx.re(s) <= 0: m = int(round(ctx.re(s))) if not m & 1: return ctx.mpf('-0.25')**(-m//2) tol = ctx.eps f = lambda n: (0.25*a)**n/((n+0.5*s)*ctx.fac(n)) totsum = ctx.zero term = f(0) mg = ctx.inf n = 0 while mg > tol: totsum += term n += 1 term = f(n) mg = abs(term) v = a**(0.5*s)*totsum/ctx.gamma(0.5*s) return v def secondzeta_singular_term(ctx, s, a, **kwargs): factor = a**(0.5*(s-1))/(4*ctx.sqrt(ctx.pi)*ctx.gamma(0.5*s)) extraprec = ctx.mag(factor) ctx.prec += extraprec factor = a**(0.5*(s-1))/(4*ctx.sqrt(ctx.pi)*ctx.gamma(0.5*s)) tol = ctx.eps f = lambda n: ctx.bernpoly(n,0.75)*(4*ctx.sqrt(a))**n*\ ctx.gamma(0.5*n)/((s+n-1)*ctx.fac(n)) totsum = ctx.zero mg1 = ctx.inf n = 1 term = f(n) mg2 = abs(term) while mg2 > tol and mg2 <= mg1: totsum += term n += 1 term = f(n) totsum += term n +=1 term = f(n) mg1 = mg2 mg2 = abs(term) totsum += term pole = -2*(s-1)**(-2)+(ctx.euler+ctx.log(16*ctx.pi**2*a))*(s-1)**(-1) st = factor*(pole+totsum) err = 0 if kwargs.get("error"): if not ((mg2 > tol) and (mg2 <= mg1)): if mg2 <= tol: err = ctx.mpf(10)**int(ctx.log(abs(factor*tol),10)) if mg2 > mg1: err = ctx.mpf(10)**int(ctx.log(abs(factor*mg1),10)) err = max(err, ctx.eps*1.) ctx.prec -= extraprec return +st, err @defun def secondzeta(ctx, s, a = 0.015, **kwargs): r""" Evaluates the secondary zeta function `Z(s)`, defined for `\mathrm{Re}(s)>1` by .. math :: Z(s) = \sum_{n=1}^{\infty} \frac{1}{\tau_n^s} where `\frac12+i\tau_n` runs through the zeros of `\zeta(s)` with imaginary part positive. `Z(s)` extends to a meromorphic function on `\mathbb{C}` with a double pole at `s=1` and simple poles at the points `-2n` for `n=0`, 1, 2, ... **Examples** >>> from mpmath import * >>> mp.pretty = True; mp.dps = 15 >>> secondzeta(2) 0.023104993115419 >>> xi = lambda s: 0.5*s*(s-1)*pi**(-0.5*s)*gamma(0.5*s)*zeta(s) >>> Xi = lambda t: xi(0.5+t*j) >>> -0.5*diff(Xi,0,n=2)/Xi(0) (0.023104993115419 + 0.0j) We may ask for an approximate error value:: >>> secondzeta(0.5+100j, error=True) ((-0.216272011276718 - 0.844952708937228j), 2.22044604925031e-16) The function has poles at the negative odd integers, and dyadic rational values at the negative even integers:: >>> mp.dps = 30 >>> secondzeta(-8) -0.67236328125 >>> secondzeta(-7) +inf **Implementation notes** The function is computed as sum of four terms `Z(s)=A(s)-P(s)+E(s)-S(s)` respectively main, prime, exponential and singular terms. The main term `A(s)` is computed from the zeros of zeta. The prime term depends on the von Mangoldt function. The singular term is responsible for the poles of the function. The four terms depends on a small parameter `a`. We may change the value of `a`. Theoretically this has no effect on the sum of the four terms, but in practice may be important. A smaller value of the parameter `a` makes `A(s)` depend on a smaller number of zeros of zeta, but `P(s)` uses more values of von Mangoldt function. We may also add a verbose option to obtain data about the values of the four terms. >>> mp.dps = 10 >>> secondzeta(0.5 + 40j, error=True, verbose=True) main term = (-30190318549.138656312556 - 13964804384.624622876523j) computed using 19 zeros of zeta prime term = (132717176.89212754625045 + 188980555.17563978290601j) computed using 9 values of the von Mangoldt function exponential term = (542447428666.07179812536 + 362434922978.80192435203j) singular term = (512124392939.98154322355 + 348281138038.65531023921j) ((0.059471043 + 0.3463514534j), 1.455191523e-11) >>> secondzeta(0.5 + 40j, a=0.04, error=True, verbose=True) main term = (-151962888.19606243907725 - 217930683.90210294051982j) computed using 9 zeros of zeta prime term = (2476659342.3038722372461 + 28711581821.921627163136j) computed using 37 values of the von Mangoldt function exponential term = (178506047114.7838188264 + 819674143244.45677330576j) singular term = (175877424884.22441310708 + 790744630738.28669174871j) ((0.059471043 + 0.3463514534j), 1.455191523e-11) Notice the great cancellation between the four terms. Changing `a`, the four terms are very different numbers but the cancellation gives the good value of Z(s). **References** A. Voros, Zeta functions for the Riemann zeros, Ann. Institute Fourier, 53, (2003) 665--699. A. Voros, Zeta functions over Zeros of Zeta Functions, Lecture Notes of the Unione Matematica Italiana, Springer, 2009. """ s = ctx.convert(s) a = ctx.convert(a) tol = ctx.eps if ctx.isint(s) and ctx.re(s) <= 1: if abs(s-1) < tol*1000: return ctx.inf m = int(round(ctx.re(s))) if m & 1: return ctx.inf else: return ((-1)**(-m//2)*\ ctx.fraction(8-ctx.eulernum(-m,exact=True),2**(-m+3))) prec = ctx.prec try: t3 = secondzeta_exp_term(ctx, s, a) extraprec = max(ctx.mag(t3),0) ctx.prec += extraprec + 3 t1, r1, gt = secondzeta_main_term(ctx,s,a,error='True', verbose='True') t2, r2, pt = secondzeta_prime_term(ctx,s,a,error='True', verbose='True') t4, r4 = secondzeta_singular_term(ctx,s,a,error='True') t3 = secondzeta_exp_term(ctx, s, a) err = r1+r2+r4 t = t1-t2+t3-t4 if kwargs.get("verbose"): print('main term =', t1) print(' computed using', gt, 'zeros of zeta') print('prime term =', t2) print(' computed using', pt, 'values of the von Mangoldt function') print('exponential term =', t3) print('singular term =', t4) finally: ctx.prec = prec if kwargs.get("error"): w = max(ctx.mag(abs(t)),0) err = max(err*2**w, ctx.eps*1.*2**w) return +t, err return +t @defun_wrapped def lerchphi(ctx, z, s, a): r""" Gives the Lerch transcendent, defined for `|z| < 1` and `\Re{a} > 0` by .. math :: \Phi(z,s,a) = \sum_{k=0}^{\infty} \frac{z^k}{(a+k)^s} and generally by the recurrence `\Phi(z,s,a) = z \Phi(z,s,a+1) + a^{-s}` along with the integral representation valid for `\Re{a} > 0` .. math :: \Phi(z,s,a) = \frac{1}{2 a^s} + \int_0^{\infty} \frac{z^t}{(a+t)^s} dt - 2 \int_0^{\infty} \frac{\sin(t \log z - s \operatorname{arctan}(t/a)}{(a^2 + t^2)^{s/2} (e^{2 \pi t}-1)} dt. The Lerch transcendent generalizes the Hurwitz zeta function :func:`zeta` (`z = 1`) and the polylogarithm :func:`polylog` (`a = 1`). **Examples** Several evaluations in terms of simpler functions:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> lerchphi(-1,2,0.5); 4*catalan 3.663862376708876060218414 3.663862376708876060218414 >>> diff(lerchphi, (-1,-2,1), (0,1,0)); 7*zeta(3)/(4*pi**2) 0.2131391994087528954617607 0.2131391994087528954617607 >>> lerchphi(-4,1,1); log(5)/4 0.4023594781085250936501898 0.4023594781085250936501898 >>> lerchphi(-3+2j,1,0.5); 2*atanh(sqrt(-3+2j))/sqrt(-3+2j) (1.142423447120257137774002 + 0.2118232380980201350495795j) (1.142423447120257137774002 + 0.2118232380980201350495795j) Evaluation works for complex arguments and `|z| \ge 1`:: >>> lerchphi(1+2j, 3-j, 4+2j) (0.002025009957009908600539469 + 0.003327897536813558807438089j) >>> lerchphi(-2,2,-2.5) -12.28676272353094275265944 >>> lerchphi(10,10,10) (-4.462130727102185701817349e-11 + 1.575172198981096218823481e-12j) >>> lerchphi(10,10,-10.5) (112658784011940.5605789002 + 498113185.5756221777743631j) Some degenerate cases:: >>> lerchphi(0,1,2) 0.5 >>> lerchphi(0,1,-2) -0.5 **References** 1. [DLMF]_ section 25.14 """ if z == 0: return a ** (-s) """ # Faster, but these cases are useful for testing right now if z == 1: return ctx.zeta(s, a) if a == 1: return z * ctx.polylog(s, z) """ if ctx.re(a) < 1: if ctx.isnpint(a): raise ValueError("Lerch transcendent complex infinity") m = int(ctx.ceil(1-ctx.re(a))) v = ctx.zero zpow = ctx.one for n in xrange(m): v += zpow / (a+n)**s zpow *= z return zpow * ctx.lerchphi(z,s, a+m) + v g = ctx.ln(z) v = 1/(2*a**s) + ctx.gammainc(1-s, -a*g) * (-g)**(s-1) / z**a h = s / 2 r = 2*ctx.pi f = lambda t: ctx.sin(s*ctx.atan(t/a)-t*g) / \ ((a**2+t**2)**h * ctx.expm1(r*t)) v += 2*ctx.quad(f, [0, ctx.inf]) if not ctx.im(z) and not ctx.im(s) and not ctx.im(a) and ctx.re(z) < 1: v = ctx.chop(v) return v sympy-0.7.4.1/sympy/mpmath/functions/hypergeometric.py0000644000175000017500000014465712253362407023344 0ustar georgeskgeorgeskfrom ..libmp.backend import xrange from .functions import defun, defun_wrapped def _check_need_perturb(ctx, terms, prec, discard_known_zeros): perturb = recompute = False extraprec = 0 discard = [] for term_index, term in enumerate(terms): w_s, c_s, alpha_s, beta_s, a_s, b_s, z = term have_singular_nongamma_weight = False # Avoid division by zero in leading factors (TODO: # also check for near division by zero?) for k, w in enumerate(w_s): if not w: if ctx.re(c_s[k]) <= 0 and c_s[k]: perturb = recompute = True have_singular_nongamma_weight = True pole_count = [0, 0, 0] # Check for gamma and series poles and near-poles for data_index, data in enumerate([alpha_s, beta_s, b_s]): for i, x in enumerate(data): n, d = ctx.nint_distance(x) # Poles if n > 0: continue if d == ctx.ninf: # OK if we have a polynomial # ------------------------------ ok = False if data_index == 2: for u in a_s: if ctx.isnpint(u) and u >= int(n): ok = True break if ok: continue pole_count[data_index] += 1 # ------------------------------ #perturb = recompute = True #return perturb, recompute, extraprec elif d < -4: extraprec += -d recompute = True if discard_known_zeros and pole_count[1] > pole_count[0] + pole_count[2] \ and not have_singular_nongamma_weight: discard.append(term_index) elif sum(pole_count): perturb = recompute = True return perturb, recompute, extraprec, discard _hypercomb_msg = """ hypercomb() failed to converge to the requested %i bits of accuracy using a working precision of %i bits. The function value may be zero or infinite; try passing zeroprec=N or infprec=M to bound finite values between 2^(-N) and 2^M. Otherwise try a higher maxprec or maxterms. """ @defun def hypercomb(ctx, function, params=[], discard_known_zeros=True, **kwargs): orig = ctx.prec sumvalue = ctx.zero dist = ctx.nint_distance ninf = ctx.ninf orig_params = params[:] verbose = kwargs.get('verbose', False) maxprec = kwargs.get('maxprec', ctx._default_hyper_maxprec(orig)) kwargs['maxprec'] = maxprec # For calls to hypsum zeroprec = kwargs.get('zeroprec') infprec = kwargs.get('infprec') perturbed_reference_value = None hextra = 0 try: while 1: ctx.prec += 10 if ctx.prec > maxprec: raise ValueError(_hypercomb_msg % (orig, ctx.prec)) orig2 = ctx.prec params = orig_params[:] terms = function(*params) if verbose: print() print("ENTERING hypercomb main loop") print("prec =", ctx.prec) print("hextra", hextra) perturb, recompute, extraprec, discard = \ _check_need_perturb(ctx, terms, orig, discard_known_zeros) ctx.prec += extraprec if perturb: if "hmag" in kwargs: hmag = kwargs["hmag"] elif ctx._fixed_precision: hmag = int(ctx.prec*0.3) else: hmag = orig + 10 + hextra h = ctx.ldexp(ctx.one, -hmag) ctx.prec = orig2 + 10 + hmag + 10 for k in range(len(params)): params[k] += h # Heuristically ensure that the perturbations # are "independent" so that two perturbations # don't accidentally cancel each other out # in a subtraction. h += h/(k+1) if recompute: terms = function(*params) if discard_known_zeros: terms = [term for (i, term) in enumerate(terms) if i not in discard] if not terms: return ctx.zero evaluated_terms = [] for term_index, term_data in enumerate(terms): w_s, c_s, alpha_s, beta_s, a_s, b_s, z = term_data if verbose: print() print(" Evaluating term %i/%i : %iF%i" % \ (term_index+1, len(terms), len(a_s), len(b_s))) print(" powers", ctx.nstr(w_s), ctx.nstr(c_s)) print(" gamma", ctx.nstr(alpha_s), ctx.nstr(beta_s)) print(" hyper", ctx.nstr(a_s), ctx.nstr(b_s)) print(" z", ctx.nstr(z)) #v = ctx.hyper(a_s, b_s, z, **kwargs) #for a in alpha_s: v *= ctx.gamma(a) #for b in beta_s: v *= ctx.rgamma(b) #for w, c in zip(w_s, c_s): v *= ctx.power(w, c) v = ctx.fprod([ctx.hyper(a_s, b_s, z, **kwargs)] + \ [ctx.gamma(a) for a in alpha_s] + \ [ctx.rgamma(b) for b in beta_s] + \ [ctx.power(w,c) for (w,c) in zip(w_s,c_s)]) if verbose: print(" Value:", v) evaluated_terms.append(v) if len(terms) == 1 and (not perturb): sumvalue = evaluated_terms[0] break if ctx._fixed_precision: sumvalue = ctx.fsum(evaluated_terms) break sumvalue = ctx.fsum(evaluated_terms) term_magnitudes = [ctx.mag(x) for x in evaluated_terms] max_magnitude = max(term_magnitudes) sum_magnitude = ctx.mag(sumvalue) cancellation = max_magnitude - sum_magnitude if verbose: print() print(" Cancellation:", cancellation, "bits") print(" Increased precision:", ctx.prec - orig, "bits") precision_ok = cancellation < ctx.prec - orig if zeroprec is None: zero_ok = False else: zero_ok = max_magnitude - ctx.prec < -zeroprec if infprec is None: inf_ok = False else: inf_ok = max_magnitude > infprec if precision_ok and (not perturb) or ctx.isnan(cancellation): break elif precision_ok: if perturbed_reference_value is None: hextra += 20 perturbed_reference_value = sumvalue continue elif ctx.mag(sumvalue - perturbed_reference_value) <= \ ctx.mag(sumvalue) - orig: break elif zero_ok: sumvalue = ctx.zero break elif inf_ok: sumvalue = ctx.inf break elif 'hmag' in kwargs: break else: hextra *= 2 perturbed_reference_value = sumvalue # Increase precision else: increment = min(max(cancellation, orig//2), max(extraprec,orig)) ctx.prec += increment if verbose: print(" Must start over with increased precision") continue finally: ctx.prec = orig return +sumvalue @defun def hyper(ctx, a_s, b_s, z, **kwargs): """ Hypergeometric function, general case. """ z = ctx.convert(z) p = len(a_s) q = len(b_s) a_s = [ctx._convert_param(a) for a in a_s] b_s = [ctx._convert_param(b) for b in b_s] # Reduce degree by eliminating common parameters if kwargs.get('eliminate', True): i = 0 while i < q and a_s: b = b_s[i] if b in a_s: a_s.remove(b) b_s.remove(b) p -= 1 q -= 1 else: i += 1 # Handle special cases if p == 0: if q == 1: return ctx._hyp0f1(b_s, z, **kwargs) elif q == 0: return ctx.exp(z) elif p == 1: if q == 1: return ctx._hyp1f1(a_s, b_s, z, **kwargs) elif q == 2: return ctx._hyp1f2(a_s, b_s, z, **kwargs) elif q == 0: return ctx._hyp1f0(a_s[0][0], z) elif p == 2: if q == 1: return ctx._hyp2f1(a_s, b_s, z, **kwargs) elif q == 2: return ctx._hyp2f2(a_s, b_s, z, **kwargs) elif q == 3: return ctx._hyp2f3(a_s, b_s, z, **kwargs) elif q == 0: return ctx._hyp2f0(a_s, b_s, z, **kwargs) elif p == q+1: return ctx._hypq1fq(p, q, a_s, b_s, z, **kwargs) elif p > q+1 and not kwargs.get('force_series'): return ctx._hyp_borel(p, q, a_s, b_s, z, **kwargs) coeffs, types = zip(*(a_s+b_s)) return ctx.hypsum(p, q, types, coeffs, z, **kwargs) @defun def hyp0f1(ctx,b,z,**kwargs): return ctx.hyper([],[b],z,**kwargs) @defun def hyp1f1(ctx,a,b,z,**kwargs): return ctx.hyper([a],[b],z,**kwargs) @defun def hyp1f2(ctx,a1,b1,b2,z,**kwargs): return ctx.hyper([a1],[b1,b2],z,**kwargs) @defun def hyp2f1(ctx,a,b,c,z,**kwargs): return ctx.hyper([a,b],[c],z,**kwargs) @defun def hyp2f2(ctx,a1,a2,b1,b2,z,**kwargs): return ctx.hyper([a1,a2],[b1,b2],z,**kwargs) @defun def hyp2f3(ctx,a1,a2,b1,b2,b3,z,**kwargs): return ctx.hyper([a1,a2],[b1,b2,b3],z,**kwargs) @defun def hyp2f0(ctx,a,b,z,**kwargs): return ctx.hyper([a,b],[],z,**kwargs) @defun def hyp3f2(ctx,a1,a2,a3,b1,b2,z,**kwargs): return ctx.hyper([a1,a2,a3],[b1,b2],z,**kwargs) @defun_wrapped def _hyp1f0(ctx, a, z): return (1-z) ** (-a) @defun def _hyp0f1(ctx, b_s, z, **kwargs): (b, btype), = b_s if z: magz = ctx.mag(z) else: magz = 0 if magz >= 8 and not kwargs.get('force_series'): try: # http://functions.wolfram.com/HypergeometricFunctions/ # Hypergeometric0F1/06/02/03/0004/ # We don't need hypercomb because the only possible singularity # occurs when the value is undefined. However, we should perhaps # still check for cancellation... # TODO: handle the all-real case more efficiently! # TODO: figure out how much precision is needed (exponential growth) orig = ctx.prec try: ctx.prec += 12 + magz//2 w = ctx.sqrt(-z) jw = ctx.j*w u = 1/(4*jw) c = ctx.mpq_1_2 - b E = ctx.exp(2*jw) H1 = (-jw)**c/E*ctx.hyp2f0(b-ctx.mpq_1_2, ctx.mpq_3_2-b, -u, force_series=True) H2 = (jw)**c*E*ctx.hyp2f0(b-ctx.mpq_1_2, ctx.mpq_3_2-b, u, force_series=True) v = ctx.gamma(b)/(2*ctx.sqrt(ctx.pi))*(H1 + H2) finally: ctx.prec = orig if ctx._is_real_type(b) and ctx._is_real_type(z): v = ctx._re(v) return +v except ctx.NoConvergence: pass return ctx.hypsum(0, 1, (btype,), [b], z, **kwargs) @defun def _hyp1f1(ctx, a_s, b_s, z, **kwargs): (a, atype), = a_s (b, btype), = b_s if not z: return ctx.one+z magz = ctx.mag(z) if magz >= 7 and not (ctx.isint(a) and ctx.re(a) <= 0): if ctx.isinf(z): if ctx.sign(a) == ctx.sign(b) == ctx.sign(z) == 1: return ctx.inf return ctx.nan * z try: try: ctx.prec += magz sector = ctx._im(z) < 0 def h(a,b): if sector: E = ctx.expjpi(ctx.fneg(a, exact=True)) else: E = ctx.expjpi(a) rz = 1/z T1 = ([E,z], [1,-a], [b], [b-a], [a, 1+a-b], [], -rz) T2 = ([ctx.exp(z),z], [1,a-b], [b], [a], [b-a, 1-a], [], rz) return T1, T2 v = ctx.hypercomb(h, [a,b], force_series=True) if ctx._is_real_type(a) and ctx._is_real_type(b) and ctx._is_real_type(z): v = ctx._re(v) return +v except ctx.NoConvergence: pass finally: ctx.prec -= magz v = ctx.hypsum(1, 1, (atype, btype), [a, b], z, **kwargs) return v def _hyp2f1_gosper(ctx,a,b,c,z,**kwargs): # Use Gosper's recurrence # See http://www.math.utexas.edu/pipermail/maxima/2006/000126.html _a,_b,_c,_z = a, b, c, z orig = ctx.prec maxprec = kwargs.get('maxprec', 100*orig) extra = 10 while 1: ctx.prec = orig + extra #a = ctx.convert(_a) #b = ctx.convert(_b) #c = ctx.convert(_c) z = ctx.convert(_z) d = ctx.mpf(0) e = ctx.mpf(1) f = ctx.mpf(0) k = 0 # Common subexpression elimination, unfortunately making # things a bit unreadable. The formula is quite messy to begin # with, though... abz = a*b*z ch = c * ctx.mpq_1_2 c1h = (c+1) * ctx.mpq_1_2 nz = 1-z g = z/nz abg = a*b*g cba = c-b-a z2 = z-2 tol = -ctx.prec - 10 nstr = ctx.nstr nprint = ctx.nprint mag = ctx.mag maxmag = ctx.ninf while 1: kch = k+ch kakbz = (k+a)*(k+b)*z / (4*(k+1)*kch*(k+c1h)) d1 = kakbz*(e-(k+cba)*d*g) e1 = kakbz*(d*abg+(k+c)*e) ft = d*(k*(cba*z+k*z2-c)-abz)/(2*kch*nz) f1 = f + e - ft maxmag = max(maxmag, mag(f1)) if mag(f1-f) < tol: break d, e, f = d1, e1, f1 k += 1 cancellation = maxmag - mag(f1) if cancellation < extra: break else: extra += cancellation if extra > maxprec: raise ctx.NoConvergence return f1 @defun def _hyp2f1(ctx, a_s, b_s, z, **kwargs): (a, atype), (b, btype) = a_s (c, ctype), = b_s if z == 1: # TODO: the following logic can be simplified convergent = ctx.re(c-a-b) > 0 finite = (ctx.isint(a) and a <= 0) or (ctx.isint(b) and b <= 0) zerodiv = ctx.isint(c) and c <= 0 and not \ ((ctx.isint(a) and c <= a <= 0) or (ctx.isint(b) and c <= b <= 0)) #print "bz", a, b, c, z, convergent, finite, zerodiv # Gauss's theorem gives the value if convergent if (convergent or finite) and not zerodiv: return ctx.gammaprod([c, c-a-b], [c-a, c-b], _infsign=True) # Otherwise, there is a pole and we take the # sign to be that when approaching from below # XXX: this evaluation is not necessarily correct in all cases return ctx.hyp2f1(a,b,c,1-ctx.eps*2) * ctx.inf # Equal to 1 (first term), unless there is a subsequent # division by zero if not z: # Division by zero but power of z is higher than # first order so cancels if c or a == 0 or b == 0: return 1+z # Indeterminate return ctx.nan # Hit zero denominator unless numerator goes to 0 first if ctx.isint(c) and c <= 0: if (ctx.isint(a) and c <= a <= 0) or \ (ctx.isint(b) and c <= b <= 0): pass else: # Pole in series return ctx.inf absz = abs(z) # Fast case: standard series converges rapidly, # possibly in finitely many terms if absz <= 0.8 or (ctx.isint(a) and a <= 0 and a >= -1000) or \ (ctx.isint(b) and b <= 0 and b >= -1000): return ctx.hypsum(2, 1, (atype, btype, ctype), [a, b, c], z, **kwargs) orig = ctx.prec try: ctx.prec += 10 # Use 1/z transformation if absz >= 1.3: def h(a,b): t = ctx.mpq_1-c ab = a-b rz = 1/z T1 = ([-z],[-a], [c,-ab],[b,c-a], [a,t+a],[ctx.mpq_1+ab], rz) T2 = ([-z],[-b], [c,ab],[a,c-b], [b,t+b],[ctx.mpq_1-ab], rz) return T1, T2 v = ctx.hypercomb(h, [a,b], **kwargs) # Use 1-z transformation elif abs(1-z) <= 0.75: def h(a,b): t = c-a-b ca = c-a cb = c-b rz = 1-z T1 = [], [], [c,t], [ca,cb], [a,b], [1-t], rz T2 = [rz], [t], [c,a+b-c], [a,b], [ca,cb], [1+t], rz return T1, T2 v = ctx.hypercomb(h, [a,b], **kwargs) # Use z/(z-1) transformation elif abs(z/(z-1)) <= 0.75: v = ctx.hyp2f1(a, c-b, c, z/(z-1)) / (1-z)**a # Remaining part of unit circle else: v = _hyp2f1_gosper(ctx,a,b,c,z,**kwargs) finally: ctx.prec = orig return +v @defun def _hypq1fq(ctx, p, q, a_s, b_s, z, **kwargs): r""" Evaluates 3F2, 4F3, 5F4, ... """ a_s, a_types = zip(*a_s) b_s, b_types = zip(*b_s) a_s = list(a_s) b_s = list(b_s) absz = abs(z) ispoly = False for a in a_s: if ctx.isint(a) and a <= 0: ispoly = True break # Direct summation if absz < 1 or ispoly: try: return ctx.hypsum(p, q, a_types+b_types, a_s+b_s, z, **kwargs) except ctx.NoConvergence: if absz > 1.1 or ispoly: raise # Use expansion at |z-1| -> 0. # Reference: Wolfgang Buhring, "Generalized Hypergeometric Functions at # Unit Argument", Proc. Amer. Math. Soc., Vol. 114, No. 1 (Jan. 1992), # pp.145-153 # The current implementation has several problems: # 1. We only implement it for 3F2. The expansion coefficients are # given by extremely messy nested sums in the higher degree cases # (see reference). Is efficient sequential generation of the coefficients # possible in the > 3F2 case? # 2. Although the series converges, it may do so slowly, so we need # convergence acceleration. The acceleration implemented by # nsum does not always help, so results returned are sometimes # inaccurate! Can we do better? # 3. We should check conditions for convergence, and possibly # do a better job of cancelling out gamma poles if possible. if z == 1: # XXX: should also check for division by zero in the # denominator of the series (cf. hyp2f1) S = ctx.re(sum(b_s)-sum(a_s)) if S <= 0: #return ctx.hyper(a_s, b_s, 1-ctx.eps*2, **kwargs) * ctx.inf return ctx.hyper(a_s, b_s, 0.9, **kwargs) * ctx.inf if (p,q) == (3,2) and abs(z-1) < 0.05: # and kwargs.get('sum1') #print "Using alternate summation (experimental)" a1,a2,a3 = a_s b1,b2 = b_s u = b1+b2-a3 initial = ctx.gammaprod([b2-a3,b1-a3,a1,a2],[b2-a3,b1-a3,1,u]) def term(k, _cache={0:initial}): u = b1+b2-a3+k if k in _cache: t = _cache[k] else: t = _cache[k-1] t *= (b1+k-a3-1)*(b2+k-a3-1) t /= k*(u-1) _cache[k] = t return t * ctx.hyp2f1(a1,a2,u,z) try: S = ctx.nsum(term, [0,ctx.inf], verbose=kwargs.get('verbose'), strict=kwargs.get('strict', True)) return S * ctx.gammaprod([b1,b2],[a1,a2,a3]) except ctx.NoConvergence: pass # Try to use convergence acceleration on and close to the unit circle. # Problem: the convergence acceleration degenerates as |z-1| -> 0, # except for special cases. Everywhere else, the Shanks transformation # is very efficient. if absz < 1.1 and ctx._re(z) <= 1: def term(kk, _cache={0:ctx.one}): k = int(kk) if k != kk: t = z ** ctx.mpf(kk) / ctx.fac(kk) for a in a_s: t *= ctx.rf(a,kk) for b in b_s: t /= ctx.rf(b,kk) return t if k in _cache: return _cache[k] t = term(k-1) m = k-1 for j in xrange(p): t *= (a_s[j]+m) for j in xrange(q): t /= (b_s[j]+m) t *= z t /= k _cache[k] = t return t sum_method = kwargs.get('sum_method', 'r+s+e') try: return ctx.nsum(term, [0,ctx.inf], verbose=kwargs.get('verbose'), strict=kwargs.get('strict', True), method=sum_method.replace('e','')) except ctx.NoConvergence: if 'e' not in sum_method: raise pass if kwargs.get('verbose'): print("Attempting Euler-Maclaurin summation") """ Somewhat slower version (one diffs_exp for each factor). However, this would be faster with fast direct derivatives of the gamma function. def power_diffs(k0): r = 0 l = ctx.log(z) while 1: yield z**ctx.mpf(k0) * l**r r += 1 def loggamma_diffs(x, reciprocal=False): sign = (-1) ** reciprocal yield sign * ctx.loggamma(x) i = 0 while 1: yield sign * ctx.psi(i,x) i += 1 def hyper_diffs(k0): b2 = b_s + [1] A = [ctx.diffs_exp(loggamma_diffs(a+k0)) for a in a_s] B = [ctx.diffs_exp(loggamma_diffs(b+k0,True)) for b in b2] Z = [power_diffs(k0)] C = ctx.gammaprod([b for b in b2], [a for a in a_s]) for d in ctx.diffs_prod(A + B + Z): v = C * d yield v """ def log_diffs(k0): b2 = b_s + [1] yield sum(ctx.loggamma(a+k0) for a in a_s) - \ sum(ctx.loggamma(b+k0) for b in b2) + k0*ctx.log(z) i = 0 while 1: v = sum(ctx.psi(i,a+k0) for a in a_s) - \ sum(ctx.psi(i,b+k0) for b in b2) if i == 0: v += ctx.log(z) yield v i += 1 def hyper_diffs(k0): C = ctx.gammaprod([b for b in b_s], [a for a in a_s]) for d in ctx.diffs_exp(log_diffs(k0)): v = C * d yield v tol = ctx.eps / 1024 prec = ctx.prec try: trunc = 50 * ctx.dps ctx.prec += 20 for i in xrange(5): head = ctx.fsum(term(k) for k in xrange(trunc)) tail, err = ctx.sumem(term, [trunc, ctx.inf], tol=tol, adiffs=hyper_diffs(trunc), verbose=kwargs.get('verbose'), error=True, _fast_abort=True) if err < tol: v = head + tail break trunc *= 2 # Need to increase precision because calculation of # derivatives may be inaccurate ctx.prec += ctx.prec//2 if i == 4: raise ctx.NoConvergence(\ "Euler-Maclaurin summation did not converge") finally: ctx.prec = prec return +v # Use 1/z transformation # http://functions.wolfram.com/HypergeometricFunctions/ # HypergeometricPFQ/06/01/05/02/0004/ def h(*args): a_s = list(args[:p]) b_s = list(args[p:]) Ts = [] recz = ctx.one/z negz = ctx.fneg(z, exact=True) for k in range(q+1): ak = a_s[k] C = [negz] Cp = [-ak] Gn = b_s + [ak] + [a_s[j]-ak for j in range(q+1) if j != k] Gd = a_s + [b_s[j]-ak for j in range(q)] Fn = [ak] + [ak-b_s[j]+1 for j in range(q)] Fd = [1-a_s[j]+ak for j in range(q+1) if j != k] Ts.append((C, Cp, Gn, Gd, Fn, Fd, recz)) return Ts return ctx.hypercomb(h, a_s+b_s, **kwargs) @defun def _hyp_borel(ctx, p, q, a_s, b_s, z, **kwargs): if a_s: a_s, a_types = zip(*a_s) a_s = list(a_s) else: a_s, a_types = [], () if b_s: b_s, b_types = zip(*b_s) b_s = list(b_s) else: b_s, b_types = [], () kwargs['maxterms'] = kwargs.get('maxterms', ctx.prec) try: return ctx.hypsum(p, q, a_types+b_types, a_s+b_s, z, **kwargs) except ctx.NoConvergence: pass prec = ctx.prec try: tol = kwargs.get('asymp_tol', ctx.eps/4) ctx.prec += 10 # hypsum is has a conservative tolerance. So we try again: def term(k, cache={0:ctx.one}): if k in cache: return cache[k] t = term(k-1) for a in a_s: t *= (a+(k-1)) for b in b_s: t /= (b+(k-1)) t *= z t /= k cache[k] = t return t s = ctx.one for k in xrange(1, ctx.prec): t = term(k) s += t if abs(t) <= tol: return s finally: ctx.prec = prec if p <= q+3: contour = kwargs.get('contour') if not contour: if ctx.arg(z) < 0.25: u = z / max(1, abs(z)) if ctx.arg(z) >= 0: contour = [0, 2j, (2j+2)/u, 2/u, ctx.inf] else: contour = [0, -2j, (-2j+2)/u, 2/u, ctx.inf] #contour = [0, 2j/z, 2/z, ctx.inf] #contour = [0, 2j, 2/z, ctx.inf] #contour = [0, 2j, ctx.inf] else: contour = [0, ctx.inf] quad_kwargs = kwargs.get('quad_kwargs', {}) def g(t): return ctx.exp(-t)*ctx.hyper(a_s, b_s+[1], t*z) I, err = ctx.quad(g, contour, error=True, **quad_kwargs) if err <= abs(I)*ctx.eps*8: return I raise ctx.NoConvergence @defun def _hyp2f2(ctx, a_s, b_s, z, **kwargs): (a1, a1type), (a2, a2type) = a_s (b1, b1type), (b2, b2type) = b_s absz = abs(z) magz = ctx.mag(z) orig = ctx.prec # Asymptotic expansion is ~ exp(z) asymp_extraprec = magz # Asymptotic series is in terms of 3F1 can_use_asymptotic = (not kwargs.get('force_series')) and \ (ctx.mag(absz) > 3) # TODO: much of the following could be shared with 2F3 instead of # copypasted if can_use_asymptotic: #print "using asymp" try: try: ctx.prec += asymp_extraprec # http://functions.wolfram.com/HypergeometricFunctions/ # Hypergeometric2F2/06/02/02/0002/ def h(a1,a2,b1,b2): X = a1+a2-b1-b2 A2 = a1+a2 B2 = b1+b2 c = {} c[0] = ctx.one c[1] = (A2-1)*X+b1*b2-a1*a2 s1 = 0 k = 0 tprev = 0 while 1: if k not in c: uu1 = 1-B2+2*a1+a1**2+2*a2+a2**2-A2*B2+a1*a2+b1*b2+(2*B2-3*(A2+1))*k+2*k**2 uu2 = (k-A2+b1-1)*(k-A2+b2-1)*(k-X-2) c[k] = ctx.one/k * (uu1*c[k-1]-uu2*c[k-2]) t1 = c[k] * z**(-k) if abs(t1) < 0.1*ctx.eps: #print "Convergence :)" break # Quit if the series doesn't converge quickly enough if k > 5 and abs(tprev) / abs(t1) < 1.5: #print "No convergence :(" raise ctx.NoConvergence s1 += t1 tprev = t1 k += 1 S = ctx.exp(z)*s1 T1 = [z,S], [X,1], [b1,b2],[a1,a2],[],[],0 T2 = [-z],[-a1],[b1,b2,a2-a1],[a2,b1-a1,b2-a1],[a1,a1-b1+1,a1-b2+1],[a1-a2+1],-1/z T3 = [-z],[-a2],[b1,b2,a1-a2],[a1,b1-a2,b2-a2],[a2,a2-b1+1,a2-b2+1],[-a1+a2+1],-1/z return T1, T2, T3 v = ctx.hypercomb(h, [a1,a2,b1,b2], force_series=True, maxterms=4*ctx.prec) if sum(ctx._is_real_type(u) for u in [a1,a2,b1,b2,z]) == 5: v = ctx.re(v) return v except ctx.NoConvergence: pass finally: ctx.prec = orig return ctx.hypsum(2, 2, (a1type, a2type, b1type, b2type), [a1, a2, b1, b2], z, **kwargs) @defun def _hyp1f2(ctx, a_s, b_s, z, **kwargs): (a1, a1type), = a_s (b1, b1type), (b2, b2type) = b_s absz = abs(z) magz = ctx.mag(z) orig = ctx.prec # Asymptotic expansion is ~ exp(sqrt(z)) asymp_extraprec = z and magz//2 # Asymptotic series is in terms of 3F0 can_use_asymptotic = (not kwargs.get('force_series')) and \ (ctx.mag(absz) > 19) and \ (ctx.sqrt(absz) > 1.5*orig) #and \ #ctx._hyp_check_convergence([a1, a1-b1+1, a1-b2+1], [], # 1/absz, orig+40+asymp_extraprec) # TODO: much of the following could be shared with 2F3 instead of # copypasted if can_use_asymptotic: #print "using asymp" try: try: ctx.prec += asymp_extraprec # http://functions.wolfram.com/HypergeometricFunctions/ # Hypergeometric1F2/06/02/03/ def h(a1,b1,b2): X = ctx.mpq_1_2*(a1-b1-b2+ctx.mpq_1_2) c = {} c[0] = ctx.one c[1] = 2*(ctx.mpq_1_4*(3*a1+b1+b2-2)*(a1-b1-b2)+b1*b2-ctx.mpq_3_16) c[2] = 2*(b1*b2+ctx.mpq_1_4*(a1-b1-b2)*(3*a1+b1+b2-2)-ctx.mpq_3_16)**2+\ ctx.mpq_1_16*(-16*(2*a1-3)*b1*b2 + \ 4*(a1-b1-b2)*(-8*a1**2+11*a1+b1+b2-2)-3) s1 = 0 s2 = 0 k = 0 tprev = 0 while 1: if k not in c: uu1 = (3*k**2+(-6*a1+2*b1+2*b2-4)*k + 3*a1**2 - \ (b1-b2)**2 - 2*a1*(b1+b2-2) + ctx.mpq_1_4) uu2 = (k-a1+b1-b2-ctx.mpq_1_2)*(k-a1-b1+b2-ctx.mpq_1_2)*\ (k-a1+b1+b2-ctx.mpq_5_2) c[k] = ctx.one/(2*k)*(uu1*c[k-1]-uu2*c[k-2]) w = c[k] * (-z)**(-0.5*k) t1 = (-ctx.j)**k * ctx.mpf(2)**(-k) * w t2 = ctx.j**k * ctx.mpf(2)**(-k) * w if abs(t1) < 0.1*ctx.eps: #print "Convergence :)" break # Quit if the series doesn't converge quickly enough if k > 5 and abs(tprev) / abs(t1) < 1.5: #print "No convergence :(" raise ctx.NoConvergence s1 += t1 s2 += t2 tprev = t1 k += 1 S = ctx.expj(ctx.pi*X+2*ctx.sqrt(-z))*s1 + \ ctx.expj(-(ctx.pi*X+2*ctx.sqrt(-z)))*s2 T1 = [0.5*S, ctx.pi, -z], [1, -0.5, X], [b1, b2], [a1],\ [], [], 0 T2 = [-z], [-a1], [b1,b2],[b1-a1,b2-a1], \ [a1,a1-b1+1,a1-b2+1], [], 1/z return T1, T2 v = ctx.hypercomb(h, [a1,b1,b2], force_series=True, maxterms=4*ctx.prec) if sum(ctx._is_real_type(u) for u in [a1,b1,b2,z]) == 4: v = ctx.re(v) return v except ctx.NoConvergence: pass finally: ctx.prec = orig #print "not using asymp" return ctx.hypsum(1, 2, (a1type, b1type, b2type), [a1, b1, b2], z, **kwargs) @defun def _hyp2f3(ctx, a_s, b_s, z, **kwargs): (a1, a1type), (a2, a2type) = a_s (b1, b1type), (b2, b2type), (b3, b3type) = b_s absz = abs(z) magz = ctx.mag(z) # Asymptotic expansion is ~ exp(sqrt(z)) asymp_extraprec = z and magz//2 orig = ctx.prec # Asymptotic series is in terms of 4F1 # The square root below empirically provides a plausible criterion # for the leading series to converge can_use_asymptotic = (not kwargs.get('force_series')) and \ (ctx.mag(absz) > 19) and (ctx.sqrt(absz) > 1.5*orig) if can_use_asymptotic: #print "using asymp" try: try: ctx.prec += asymp_extraprec # http://functions.wolfram.com/HypergeometricFunctions/ # Hypergeometric2F3/06/02/03/01/0002/ def h(a1,a2,b1,b2,b3): X = ctx.mpq_1_2*(a1+a2-b1-b2-b3+ctx.mpq_1_2) A2 = a1+a2 B3 = b1+b2+b3 A = a1*a2 B = b1*b2+b3*b2+b1*b3 R = b1*b2*b3 c = {} c[0] = ctx.one c[1] = 2*(B - A + ctx.mpq_1_4*(3*A2+B3-2)*(A2-B3) - ctx.mpq_3_16) c[2] = ctx.mpq_1_2*c[1]**2 + ctx.mpq_1_16*(-16*(2*A2-3)*(B-A) + 32*R +\ 4*(-8*A2**2 + 11*A2 + 8*A + B3 - 2)*(A2-B3)-3) s1 = 0 s2 = 0 k = 0 tprev = 0 while 1: if k not in c: uu1 = (k-2*X-3)*(k-2*X-2*b1-1)*(k-2*X-2*b2-1)*\ (k-2*X-2*b3-1) uu2 = (4*(k-1)**3 - 6*(4*X+B3)*(k-1)**2 + \ 2*(24*X**2+12*B3*X+4*B+B3-1)*(k-1) - 32*X**3 - \ 24*B3*X**2 - 4*B - 8*R - 4*(4*B+B3-1)*X + 2*B3-1) uu3 = (5*(k-1)**2+2*(-10*X+A2-3*B3+3)*(k-1)+2*c[1]) c[k] = ctx.one/(2*k)*(uu1*c[k-3]-uu2*c[k-2]+uu3*c[k-1]) w = c[k] * ctx.power(-z, -0.5*k) t1 = (-ctx.j)**k * ctx.mpf(2)**(-k) * w t2 = ctx.j**k * ctx.mpf(2)**(-k) * w if abs(t1) < 0.1*ctx.eps: break # Quit if the series doesn't converge quickly enough if k > 5 and abs(tprev) / abs(t1) < 1.5: raise ctx.NoConvergence s1 += t1 s2 += t2 tprev = t1 k += 1 S = ctx.expj(ctx.pi*X+2*ctx.sqrt(-z))*s1 + \ ctx.expj(-(ctx.pi*X+2*ctx.sqrt(-z)))*s2 T1 = [0.5*S, ctx.pi, -z], [1, -0.5, X], [b1, b2, b3], [a1, a2],\ [], [], 0 T2 = [-z], [-a1], [b1,b2,b3,a2-a1],[a2,b1-a1,b2-a1,b3-a1], \ [a1,a1-b1+1,a1-b2+1,a1-b3+1], [a1-a2+1], 1/z T3 = [-z], [-a2], [b1,b2,b3,a1-a2],[a1,b1-a2,b2-a2,b3-a2], \ [a2,a2-b1+1,a2-b2+1,a2-b3+1],[-a1+a2+1], 1/z return T1, T2, T3 v = ctx.hypercomb(h, [a1,a2,b1,b2,b3], force_series=True, maxterms=4*ctx.prec) if sum(ctx._is_real_type(u) for u in [a1,a2,b1,b2,b3,z]) == 6: v = ctx.re(v) return v except ctx.NoConvergence: pass finally: ctx.prec = orig return ctx.hypsum(2, 3, (a1type, a2type, b1type, b2type, b3type), [a1, a2, b1, b2, b3], z, **kwargs) @defun def _hyp2f0(ctx, a_s, b_s, z, **kwargs): (a, atype), (b, btype) = a_s # We want to try aggressively to use the asymptotic expansion, # and fall back only when absolutely necessary try: kwargsb = kwargs.copy() kwargsb['maxterms'] = kwargsb.get('maxterms', ctx.prec) return ctx.hypsum(2, 0, (atype,btype), [a,b], z, **kwargsb) except ctx.NoConvergence: if kwargs.get('force_series'): raise pass def h(a, b): w = ctx.sinpi(b) rz = -1/z T1 = ([ctx.pi,w,rz],[1,-1,a],[],[a-b+1,b],[a],[b],rz) T2 = ([-ctx.pi,w,rz],[1,-1,1+a-b],[],[a,2-b],[a-b+1],[2-b],rz) return T1, T2 return ctx.hypercomb(h, [a, 1+a-b], **kwargs) @defun def meijerg(ctx, a_s, b_s, z, r=1, series=None, **kwargs): an, ap = a_s bm, bq = b_s n = len(an) p = n + len(ap) m = len(bm) q = m + len(bq) a = an+ap b = bm+bq a = [ctx.convert(_) for _ in a] b = [ctx.convert(_) for _ in b] z = ctx.convert(z) if series is None: if p < q: series = 1 if p > q: series = 2 if p == q: if m+n == p and abs(z) > 1: series = 2 else: series = 1 if kwargs.get('verbose'): print("Meijer G m,n,p,q,series =", m,n,p,q,series) if series == 1: def h(*args): a = args[:p] b = args[p:] terms = [] for k in range(m): bases = [z] expts = [b[k]/r] gn = [b[j]-b[k] for j in range(m) if j != k] gn += [1-a[j]+b[k] for j in range(n)] gd = [a[j]-b[k] for j in range(n,p)] gd += [1-b[j]+b[k] for j in range(m,q)] hn = [1-a[j]+b[k] for j in range(p)] hd = [1-b[j]+b[k] for j in range(q) if j != k] hz = (-ctx.one)**(p-m-n) * z**(ctx.one/r) terms.append((bases, expts, gn, gd, hn, hd, hz)) return terms else: def h(*args): a = args[:p] b = args[p:] terms = [] for k in range(n): bases = [z] if r == 1: expts = [a[k]-1] else: expts = [(a[k]-1)/ctx.convert(r)] gn = [a[k]-a[j] for j in range(n) if j != k] gn += [1-a[k]+b[j] for j in range(m)] gd = [a[k]-b[j] for j in range(m,q)] gd += [1-a[k]+a[j] for j in range(n,p)] hn = [1-a[k]+b[j] for j in range(q)] hd = [1+a[j]-a[k] for j in range(p) if j != k] hz = (-ctx.one)**(q-m-n) / z**(ctx.one/r) terms.append((bases, expts, gn, gd, hn, hd, hz)) return terms return ctx.hypercomb(h, a+b, **kwargs) @defun_wrapped def appellf1(ctx,a,b1,b2,c,x,y,**kwargs): # Assume x smaller # We will use x for the outer loop if abs(x) > abs(y): x, y = y, x b1, b2 = b2, b1 def ok(x): return abs(x) < 0.99 # Finite cases if ctx.isnpint(a): pass elif ctx.isnpint(b1): pass elif ctx.isnpint(b2): x, y, b1, b2 = y, x, b2, b1 else: #print x, y # Note: ok if |y| > 1, because # 2F1 implements analytic continuation if not ok(x): u1 = (x-y)/(x-1) if not ok(u1): raise ValueError("Analytic continuation not implemented") #print "Using analytic continuation" return (1-x)**(-b1)*(1-y)**(c-a-b2)*\ ctx.appellf1(c-a,b1,c-b1-b2,c,u1,y,**kwargs) return ctx.hyper2d({'m+n':[a],'m':[b1],'n':[b2]}, {'m+n':[c]}, x,y, **kwargs) @defun def appellf2(ctx,a,b1,b2,c1,c2,x,y,**kwargs): # TODO: continuation return ctx.hyper2d({'m+n':[a],'m':[b1],'n':[b2]}, {'m':[c1],'n':[c2]}, x,y, **kwargs) @defun def appellf3(ctx,a1,a2,b1,b2,c,x,y,**kwargs): outer_polynomial = ctx.isnpint(a1) or ctx.isnpint(b1) inner_polynomial = ctx.isnpint(a2) or ctx.isnpint(b2) if not outer_polynomial: if inner_polynomial or abs(x) > abs(y): x, y = y, x a1,a2,b1,b2 = a2,a1,b2,b1 return ctx.hyper2d({'m':[a1,b1],'n':[a2,b2]}, {'m+n':[c]},x,y,**kwargs) @defun def appellf4(ctx,a,b,c1,c2,x,y,**kwargs): # TODO: continuation return ctx.hyper2d({'m+n':[a,b]}, {'m':[c1],'n':[c2]},x,y,**kwargs) @defun def hyper2d(ctx, a, b, x, y, **kwargs): r""" Sums the generalized 2D hypergeometric series .. math :: \sum_{m=0}^{\infty} \sum_{n=0}^{\infty} \frac{P((a),m,n)}{Q((b),m,n)} \frac{x^m y^n} {m! n!} where `(a) = (a_1,\ldots,a_r)`, `(b) = (b_1,\ldots,b_s)` and where `P` and `Q` are products of rising factorials such as `(a_j)_n` or `(a_j)_{m+n}`. `P` and `Q` are specified in the form of dicts, with the `m` and `n` dependence as keys and parameter lists as values. The supported rising factorials are given in the following table (note that only a few are supported in `Q`): +------------+-------------------+--------+ | Key | Rising factorial | `Q` | +============+===================+========+ | ``'m'`` | `(a_j)_m` | Yes | +------------+-------------------+--------+ | ``'n'`` | `(a_j)_n` | Yes | +------------+-------------------+--------+ | ``'m+n'`` | `(a_j)_{m+n}` | Yes | +------------+-------------------+--------+ | ``'m-n'`` | `(a_j)_{m-n}` | No | +------------+-------------------+--------+ | ``'n-m'`` | `(a_j)_{n-m}` | No | +------------+-------------------+--------+ | ``'2m+n'`` | `(a_j)_{2m+n}` | No | +------------+-------------------+--------+ | ``'2m-n'`` | `(a_j)_{2m-n}` | No | +------------+-------------------+--------+ | ``'2n-m'`` | `(a_j)_{2n-m}` | No | +------------+-------------------+--------+ For example, the Appell F1 and F4 functions .. math :: F_1 = \sum_{m=0}^{\infty} \sum_{n=0}^{\infty} \frac{(a)_{m+n} (b)_m (c)_n}{(d)_{m+n}} \frac{x^m y^n}{m! n!} F_4 = \sum_{m=0}^{\infty} \sum_{n=0}^{\infty} \frac{(a)_{m+n} (b)_{m+n}}{(c)_m (d)_{n}} \frac{x^m y^n}{m! n!} can be represented respectively as ``hyper2d({'m+n':[a], 'm':[b], 'n':[c]}, {'m+n':[d]}, x, y)`` ``hyper2d({'m+n':[a,b]}, {'m':[c], 'n':[d]}, x, y)`` More generally, :func:`~mpmath.hyper2d` can evaluate any of the 34 distinct convergent second-order (generalized Gaussian) hypergeometric series enumerated by Horn, as well as the Kampe de Feriet function. The series is computed by rewriting it so that the inner series (i.e. the series containing `n` and `y`) has the form of an ordinary generalized hypergeometric series and thereby can be evaluated efficiently using :func:`~mpmath.hyper`. If possible, manually swapping `x` and `y` and the corresponding parameters can sometimes give better results. **Examples** Two separable cases: a product of two geometric series, and a product of two Gaussian hypergeometric functions:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> x, y = mpf(0.25), mpf(0.5) >>> hyper2d({'m':1,'n':1}, {}, x,y) 2.666666666666666666666667 >>> 1/(1-x)/(1-y) 2.666666666666666666666667 >>> hyper2d({'m':[1,2],'n':[3,4]}, {'m':[5],'n':[6]}, x,y) 4.164358531238938319669856 >>> hyp2f1(1,2,5,x)*hyp2f1(3,4,6,y) 4.164358531238938319669856 Some more series that can be done in closed form:: >>> hyper2d({'m':1,'n':1},{'m+n':1},x,y) 2.013417124712514809623881 >>> (exp(x)*x-exp(y)*y)/(x-y) 2.013417124712514809623881 Six of the 34 Horn functions, G1-G3 and H1-H3:: >>> from mpmath import * >>> mp.dps = 10; mp.pretty = True >>> x, y = 0.0625, 0.125 >>> a1,a2,b1,b2,c1,c2,d = 1.1,-1.2,-1.3,-1.4,1.5,-1.6,1.7 >>> hyper2d({'m+n':a1,'n-m':b1,'m-n':b2},{},x,y) # G1 1.139090746 >>> nsum(lambda m,n: rf(a1,m+n)*rf(b1,n-m)*rf(b2,m-n)*\ ... x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf]) 1.139090746 >>> hyper2d({'m':a1,'n':a2,'n-m':b1,'m-n':b2},{},x,y) # G2 0.9503682696 >>> nsum(lambda m,n: rf(a1,m)*rf(a2,n)*rf(b1,n-m)*rf(b2,m-n)*\ ... x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf]) 0.9503682696 >>> hyper2d({'2n-m':a1,'2m-n':a2},{},x,y) # G3 1.029372029 >>> nsum(lambda m,n: rf(a1,2*n-m)*rf(a2,2*m-n)*\ ... x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf]) 1.029372029 >>> hyper2d({'m-n':a1,'m+n':b1,'n':c1},{'m':d},x,y) # H1 -1.605331256 >>> nsum(lambda m,n: rf(a1,m-n)*rf(b1,m+n)*rf(c1,n)/rf(d,m)*\ ... x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf]) -1.605331256 >>> hyper2d({'m-n':a1,'m':b1,'n':[c1,c2]},{'m':d},x,y) # H2 -2.35405404 >>> nsum(lambda m,n: rf(a1,m-n)*rf(b1,m)*rf(c1,n)*rf(c2,n)/rf(d,m)*\ ... x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf]) -2.35405404 >>> hyper2d({'2m+n':a1,'n':b1},{'m+n':c1},x,y) # H3 0.974479074 >>> nsum(lambda m,n: rf(a1,2*m+n)*rf(b1,n)/rf(c1,m+n)*\ ... x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf]) 0.974479074 **References** 1. [SrivastavaKarlsson]_ 2. [Weisstein]_ http://mathworld.wolfram.com/HornFunction.html 3. [Weisstein]_ http://mathworld.wolfram.com/AppellHypergeometricFunction.html """ x = ctx.convert(x) y = ctx.convert(y) def parse(dct, key): args = dct.pop(key, []) try: args = list(args) except TypeError: args = [args] return [ctx.convert(arg) for arg in args] a_s = dict(a) b_s = dict(b) a_m = parse(a, 'm') a_n = parse(a, 'n') a_m_add_n = parse(a, 'm+n') a_m_sub_n = parse(a, 'm-n') a_n_sub_m = parse(a, 'n-m') a_2m_add_n = parse(a, '2m+n') a_2m_sub_n = parse(a, '2m-n') a_2n_sub_m = parse(a, '2n-m') b_m = parse(b, 'm') b_n = parse(b, 'n') b_m_add_n = parse(b, 'm+n') if a: raise ValueError("unsupported key: %r" % a.keys()[0]) if b: raise ValueError("unsupported key: %r" % b.keys()[0]) s = 0 outer = ctx.one m = ctx.mpf(0) ok_count = 0 prec = ctx.prec maxterms = kwargs.get('maxterms', 20*prec) try: ctx.prec += 10 tol = +ctx.eps while 1: inner_sign = 1 outer_sign = 1 inner_a = list(a_n) inner_b = list(b_n) outer_a = [a+m for a in a_m] outer_b = [b+m for b in b_m] # (a)_{m+n} = (a)_m (a+m)_n for a in a_m_add_n: a = a+m inner_a.append(a) outer_a.append(a) # (b)_{m+n} = (b)_m (b+m)_n for b in b_m_add_n: b = b+m inner_b.append(b) outer_b.append(b) # (a)_{n-m} = (a-m)_n / (a-m)_m for a in a_n_sub_m: inner_a.append(a-m) outer_b.append(a-m-1) # (a)_{m-n} = (-1)^(m+n) (1-a-m)_m / (1-a-m)_n for a in a_m_sub_n: inner_sign *= (-1) outer_sign *= (-1)**(m) inner_b.append(1-a-m) outer_a.append(-a-m) # (a)_{2m+n} = (a)_{2m} (a+2m)_n for a in a_2m_add_n: inner_a.append(a+2*m) outer_a.append((a+2*m)*(1+a+2*m)) # (a)_{2m-n} = (-1)^(2m+n) (1-a-2m)_{2m} / (1-a-2m)_n for a in a_2m_sub_n: inner_sign *= (-1) inner_b.append(1-a-2*m) outer_a.append((a+2*m)*(1+a+2*m)) # (a)_{2n-m} = 4^n ((a-m)/2)_n ((a-m+1)/2)_n / (a-m)_m for a in a_2n_sub_m: inner_sign *= 4 inner_a.append(0.5*(a-m)) inner_a.append(0.5*(a-m+1)) outer_b.append(a-m-1) inner = ctx.hyper(inner_a, inner_b, inner_sign*y, zeroprec=ctx.prec, **kwargs) term = outer * inner * outer_sign if abs(term) < tol: ok_count += 1 else: ok_count = 0 if ok_count >= 3 or not outer: break s += term for a in outer_a: outer *= a for b in outer_b: outer /= b m += 1 outer = outer * x / m if m > maxterms: raise ctx.NoConvergence("maxterms exceeded in hyper2d") finally: ctx.prec = prec return +s """ @defun def kampe_de_feriet(ctx,a,b,c,d,e,f,x,y,**kwargs): return ctx.hyper2d({'m+n':a,'m':b,'n':c}, {'m+n':d,'m':e,'n':f}, x,y, **kwargs) """ @defun def bihyper(ctx, a_s, b_s, z, **kwargs): r""" Evaluates the bilateral hypergeometric series .. math :: \,_AH_B(a_1, \ldots, a_k; b_1, \ldots, b_B; z) = \sum_{n=-\infty}^{\infty} \frac{(a_1)_n \ldots (a_A)_n} {(b_1)_n \ldots (b_B)_n} \, z^n where, for direct convergence, `A = B` and `|z| = 1`, although a regularized sum exists more generally by considering the bilateral series as a sum of two ordinary hypergeometric functions. In order for the series to make sense, none of the parameters may be integers. **Examples** The value of `\,_2H_2` at `z = 1` is given by Dougall's formula:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> a,b,c,d = 0.5, 1.5, 2.25, 3.25 >>> bihyper([a,b],[c,d],1) -14.49118026212345786148847 >>> gammaprod([c,d,1-a,1-b,c+d-a-b-1],[c-a,d-a,c-b,d-b]) -14.49118026212345786148847 The regularized function `\,_1H_0` can be expressed as the sum of one `\,_2F_0` function and one `\,_1F_1` function:: >>> a = mpf(0.25) >>> z = mpf(0.75) >>> bihyper([a], [], z) (0.2454393389657273841385582 + 0.2454393389657273841385582j) >>> hyper([a,1],[],z) + (hyper([1],[1-a],-1/z)-1) (0.2454393389657273841385582 + 0.2454393389657273841385582j) >>> hyper([a,1],[],z) + hyper([1],[2-a],-1/z)/z/(a-1) (0.2454393389657273841385582 + 0.2454393389657273841385582j) **References** 1. [Slater]_ (chapter 6: "Bilateral Series", pp. 180-189) 2. [Wikipedia]_ http://en.wikipedia.org/wiki/Bilateral_hypergeometric_series """ z = ctx.convert(z) c_s = a_s + b_s p = len(a_s) q = len(b_s) if (p, q) == (0,0) or (p, q) == (1,1): return ctx.zero * z neg = (p-q) % 2 def h(*c_s): a_s = list(c_s[:p]) b_s = list(c_s[p:]) aa_s = [2-b for b in b_s] bb_s = [2-a for a in a_s] rp = [(-1)**neg * z] + [1-b for b in b_s] + [1-a for a in a_s] rc = [-1] + [1]*len(b_s) + [-1]*len(a_s) T1 = [], [], [], [], a_s + [1], b_s, z T2 = rp, rc, [], [], aa_s + [1], bb_s, (-1)**neg / z return T1, T2 return ctx.hypercomb(h, c_s, **kwargs) sympy-0.7.4.1/sympy/mpmath/functions/bessel.py0000644000175000017500000011200112253362407021545 0ustar georgeskgeorgeskfrom .functions import defun, defun_wrapped @defun def j0(ctx, x): """Computes the Bessel function `J_0(x)`. See :func:`~mpmath.besselj`.""" return ctx.besselj(0, x) @defun def j1(ctx, x): """Computes the Bessel function `J_1(x)`. See :func:`~mpmath.besselj`.""" return ctx.besselj(1, x) @defun def besselj(ctx, n, z, derivative=0, **kwargs): if type(n) is int: n_isint = True else: n = ctx.convert(n) n_isint = ctx.isint(n) if n_isint: n = int(ctx._re(n)) if n_isint and n < 0: return (-1)**n * ctx.besselj(-n, z, derivative, **kwargs) z = ctx.convert(z) M = ctx.mag(z) if derivative: d = ctx.convert(derivative) # TODO: the integer special-casing shouldn't be necessary. # However, the hypergeometric series gets inaccurate for large d # because of inaccurate pole cancellation at a pole far from # zero (needs to be fixed in hypercomb or hypsum) if ctx.isint(d) and d >= 0: d = int(d) orig = ctx.prec try: ctx.prec += 15 v = ctx.fsum((-1)**k * ctx.binomial(d,k) * ctx.besselj(2*k+n-d,z) for k in range(d+1)) finally: ctx.prec = orig v *= ctx.mpf(2)**(-d) else: def h(n,d): r = ctx.fmul(ctx.fmul(z, z, prec=ctx.prec+M), -0.25, exact=True) B = [0.5*(n-d+1), 0.5*(n-d+2)] T = [([2,ctx.pi,z],[d-2*n,0.5,n-d],[],B,[(n+1)*0.5,(n+2)*0.5],B+[n+1],r)] return T v = ctx.hypercomb(h, [n,d], **kwargs) else: # Fast case: J_n(x), n int, appropriate magnitude for fixed-point calculation if (not derivative) and n_isint and abs(M) < 10 and abs(n) < 20: try: return ctx._besselj(n, z) except NotImplementedError: pass if not z: if not n: v = ctx.one + n+z elif ctx.re(n) > 0: v = n*z else: v = ctx.inf + z + n else: #v = 0 orig = ctx.prec try: # XXX: workaround for accuracy in low level hypergeometric series # when alternating, large arguments ctx.prec += min(3*abs(M), ctx.prec) w = ctx.fmul(z, 0.5, exact=True) def h(n): r = ctx.fneg(ctx.fmul(w, w, prec=max(0,ctx.prec+M)), exact=True) return [([w], [n], [], [n+1], [], [n+1], r)] v = ctx.hypercomb(h, [n], **kwargs) finally: ctx.prec = orig v = +v return v @defun def besseli(ctx, n, z, derivative=0, **kwargs): n = ctx.convert(n) z = ctx.convert(z) if not z: if derivative: raise ValueError if not n: # I(0,0) = 1 return 1+n+z if ctx.isint(n): return 0*(n+z) r = ctx.re(n) if r == 0: return ctx.nan*(n+z) elif r > 0: return 0*(n+z) else: return ctx.inf+(n+z) M = ctx.mag(z) if derivative: d = ctx.convert(derivative) def h(n,d): r = ctx.fmul(ctx.fmul(z, z, prec=ctx.prec+M), 0.25, exact=True) B = [0.5*(n-d+1), 0.5*(n-d+2), n+1] T = [([2,ctx.pi,z],[d-2*n,0.5,n-d],[n+1],B,[(n+1)*0.5,(n+2)*0.5],B,r)] return T v = ctx.hypercomb(h, [n,d], **kwargs) else: def h(n): w = ctx.fmul(z, 0.5, exact=True) r = ctx.fmul(w, w, prec=max(0,ctx.prec+M)) return [([w], [n], [], [n+1], [], [n+1], r)] v = ctx.hypercomb(h, [n], **kwargs) return v @defun_wrapped def bessely(ctx, n, z, derivative=0, **kwargs): if not z: if derivative: # Not implemented raise ValueError if not n: # ~ log(z/2) return -ctx.inf + (n+z) if ctx.im(n): return nan * (n+z) r = ctx.re(n) q = n+0.5 if ctx.isint(q): if n > 0: return -ctx.inf + (n+z) else: return 0 * (n+z) if r < 0 and int(ctx.floor(q)) % 2: return ctx.inf + (n+z) else: return ctx.ninf + (n+z) # XXX: use hypercomb ctx.prec += 10 m, d = ctx.nint_distance(n) if d < -ctx.prec: h = +ctx.eps ctx.prec *= 2 n += h elif d < 0: ctx.prec -= d # TODO: avoid cancellation for imaginary arguments cos, sin = ctx.cospi_sinpi(n) return (ctx.besselj(n,z,derivative,**kwargs)*cos - \ ctx.besselj(-n,z,derivative,**kwargs))/sin @defun_wrapped def besselk(ctx, n, z, **kwargs): if not z: return ctx.inf M = ctx.mag(z) if M < 1: # Represent as limit definition def h(n): r = (z/2)**2 T1 = [z, 2], [-n, n-1], [n], [], [], [1-n], r T2 = [z, 2], [n, -n-1], [-n], [], [], [1+n], r return T1, T2 # We could use the limit definition always, but it leads # to very bad cancellation (of exponentially large terms) # for large real z # Instead represent in terms of 2F0 else: ctx.prec += M def h(n): return [([ctx.pi/2, z, ctx.exp(-z)], [0.5,-0.5,1], [], [], \ [n+0.5, 0.5-n], [], -1/(2*z))] return ctx.hypercomb(h, [n], **kwargs) @defun_wrapped def hankel1(ctx,n,x,**kwargs): return ctx.besselj(n,x,**kwargs) + ctx.j*ctx.bessely(n,x,**kwargs) @defun_wrapped def hankel2(ctx,n,x,**kwargs): return ctx.besselj(n,x,**kwargs) - ctx.j*ctx.bessely(n,x,**kwargs) @defun_wrapped def whitm(ctx,k,m,z,**kwargs): if z == 0: # M(k,m,z) = 0^(1/2+m) if ctx.re(m) > -0.5: return z elif ctx.re(m) < -0.5: return ctx.inf + z else: return ctx.nan * z x = ctx.fmul(-0.5, z, exact=True) y = 0.5+m return ctx.exp(x) * z**y * ctx.hyp1f1(y-k, 1+2*m, z, **kwargs) @defun_wrapped def whitw(ctx,k,m,z,**kwargs): if z == 0: g = abs(ctx.re(m)) if g < 0.5: return z elif g > 0.5: return ctx.inf + z else: return ctx.nan * z x = ctx.fmul(-0.5, z, exact=True) y = 0.5+m return ctx.exp(x) * z**y * ctx.hyperu(y-k, 1+2*m, z, **kwargs) @defun def hyperu(ctx, a, b, z, **kwargs): a, atype = ctx._convert_param(a) b, btype = ctx._convert_param(b) z = ctx.convert(z) if not z: if ctx.re(b) <= 1: return ctx.gammaprod([1-b],[a-b+1]) else: return ctx.inf + z bb = 1+a-b bb, bbtype = ctx._convert_param(bb) try: orig = ctx.prec try: ctx.prec += 10 v = ctx.hypsum(2, 0, (atype, bbtype), [a, bb], -1/z, maxterms=ctx.prec) return v / z**a finally: ctx.prec = orig except ctx.NoConvergence: pass def h(a,b): w = ctx.sinpi(b) T1 = ([ctx.pi,w],[1,-1],[],[a-b+1,b],[a],[b],z) T2 = ([-ctx.pi,w,z],[1,-1,1-b],[],[a,2-b],[a-b+1],[2-b],z) return T1, T2 return ctx.hypercomb(h, [a,b], **kwargs) @defun def struveh(ctx,n,z, **kwargs): n = ctx.convert(n) z = ctx.convert(z) # http://functions.wolfram.com/Bessel-TypeFunctions/StruveH/26/01/02/ def h(n): return [([z/2, 0.5*ctx.sqrt(ctx.pi)], [n+1, -1], [], [n+1.5], [1], [1.5, n+1.5], -(z/2)**2)] return ctx.hypercomb(h, [n], **kwargs) @defun def struvel(ctx,n,z, **kwargs): n = ctx.convert(n) z = ctx.convert(z) # http://functions.wolfram.com/Bessel-TypeFunctions/StruveL/26/01/02/ def h(n): return [([z/2, 0.5*ctx.sqrt(ctx.pi)], [n+1, -1], [], [n+1.5], [1], [1.5, n+1.5], (z/2)**2)] return ctx.hypercomb(h, [n], **kwargs) def _anger(ctx,which,v,z,**kwargs): v = ctx._convert_param(v)[0] z = ctx.convert(z) def h(v): b = ctx.mpq_1_2 u = v*b m = b*3 a1,a2,b1,b2 = m-u, m+u, 1-u, 1+u c, s = ctx.cospi_sinpi(u) if which == 0: A, B = [b*z, s], [c] if which == 1: A, B = [b*z, -c], [s] w = ctx.square_exp_arg(z, mult=-0.25) T1 = A, [1, 1], [], [a1,a2], [1], [a1,a2], w T2 = B, [1], [], [b1,b2], [1], [b1,b2], w return T1, T2 return ctx.hypercomb(h, [v], **kwargs) @defun def angerj(ctx, v, z, **kwargs): return _anger(ctx, 0, v, z, **kwargs) @defun def webere(ctx, v, z, **kwargs): return _anger(ctx, 1, v, z, **kwargs) @defun def lommels1(ctx, u, v, z, **kwargs): u = ctx._convert_param(u)[0] v = ctx._convert_param(v)[0] z = ctx.convert(z) def h(u,v): b = ctx.mpq_1_2 w = ctx.square_exp_arg(z, mult=-0.25) return ([u-v+1, u+v+1, z], [-1, -1, u+1], [], [], [1], \ [b*(u-v+3),b*(u+v+3)], w), return ctx.hypercomb(h, [u,v], **kwargs) @defun def lommels2(ctx, u, v, z, **kwargs): u = ctx._convert_param(u)[0] v = ctx._convert_param(v)[0] z = ctx.convert(z) # Asymptotic expansion (GR p. 947) -- need to be careful # not to use for small arguments # def h(u,v): # b = ctx.mpq_1_2 # w = -(z/2)**(-2) # return ([z], [u-1], [], [], [b*(1-u+v)], [b*(1-u-v)], w), def h(u,v): b = ctx.mpq_1_2 w = ctx.square_exp_arg(z, mult=-0.25) T1 = [u-v+1, u+v+1, z], [-1, -1, u+1], [], [], [1], [b*(u-v+3),b*(u+v+3)], w T2 = [2, z], [u+v-1, -v], [v, b*(u+v+1)], [b*(v-u+1)], [], [1-v], w T3 = [2, z], [u-v-1, v], [-v, b*(u-v+1)], [b*(1-u-v)], [], [1+v], w #c1 = ctx.cospi((u-v)*b) #c2 = ctx.cospi((u+v)*b) #s = ctx.sinpi(v) #r1 = (u-v+1)*b #r2 = (u+v+1)*b #T2 = [c1, s, z, 2], [1, -1, -v, v], [], [-v+1], [], [-v+1], w #T3 = [-c2, s, z, 2], [1, -1, v, -v], [], [v+1], [], [v+1], w #T2 = [c1, s, z, 2], [1, -1, -v, v+u-1], [r1, r2], [-v+1], [], [-v+1], w #T3 = [-c2, s, z, 2], [1, -1, v, -v+u-1], [r1, r2], [v+1], [], [v+1], w return T1, T2, T3 return ctx.hypercomb(h, [u,v], **kwargs) @defun def ber(ctx, n, z, **kwargs): n = ctx.convert(n) z = ctx.convert(z) # http://functions.wolfram.com/Bessel-TypeFunctions/KelvinBer2/26/01/02/0001/ def h(n): r = -(z/4)**4 cos, sin = ctx.cospi_sinpi(-0.75*n) T1 = [cos, z/2], [1, n], [], [n+1], [], [0.5, 0.5*(n+1), 0.5*n+1], r T2 = [sin, z/2], [1, n+2], [], [n+2], [], [1.5, 0.5*(n+3), 0.5*n+1], r return T1, T2 return ctx.hypercomb(h, [n], **kwargs) @defun def bei(ctx, n, z, **kwargs): n = ctx.convert(n) z = ctx.convert(z) # http://functions.wolfram.com/Bessel-TypeFunctions/KelvinBei2/26/01/02/0001/ def h(n): r = -(z/4)**4 cos, sin = ctx.cospi_sinpi(0.75*n) T1 = [cos, z/2], [1, n+2], [], [n+2], [], [1.5, 0.5*(n+3), 0.5*n+1], r T2 = [sin, z/2], [1, n], [], [n+1], [], [0.5, 0.5*(n+1), 0.5*n+1], r return T1, T2 return ctx.hypercomb(h, [n], **kwargs) @defun def ker(ctx, n, z, **kwargs): n = ctx.convert(n) z = ctx.convert(z) # http://functions.wolfram.com/Bessel-TypeFunctions/KelvinKer2/26/01/02/0001/ def h(n): r = -(z/4)**4 cos1, sin1 = ctx.cospi_sinpi(0.25*n) cos2, sin2 = ctx.cospi_sinpi(0.75*n) T1 = [2, z, 4*cos1], [-n-3, n, 1], [-n], [], [], [0.5, 0.5*(1+n), 0.5*(n+2)], r T2 = [2, z, -sin1], [-n-3, 2+n, 1], [-n-1], [], [], [1.5, 0.5*(3+n), 0.5*(n+2)], r T3 = [2, z, 4*cos2], [n-3, -n, 1], [n], [], [], [0.5, 0.5*(1-n), 1-0.5*n], r T4 = [2, z, -sin2], [n-3, 2-n, 1], [n-1], [], [], [1.5, 0.5*(3-n), 1-0.5*n], r return T1, T2, T3, T4 return ctx.hypercomb(h, [n], **kwargs) @defun def kei(ctx, n, z, **kwargs): n = ctx.convert(n) z = ctx.convert(z) # http://functions.wolfram.com/Bessel-TypeFunctions/KelvinKei2/26/01/02/0001/ def h(n): r = -(z/4)**4 cos1, sin1 = ctx.cospi_sinpi(0.75*n) cos2, sin2 = ctx.cospi_sinpi(0.25*n) T1 = [-cos1, 2, z], [1, n-3, 2-n], [n-1], [], [], [1.5, 0.5*(3-n), 1-0.5*n], r T2 = [-sin1, 2, z], [1, n-1, -n], [n], [], [], [0.5, 0.5*(1-n), 1-0.5*n], r T3 = [-sin2, 2, z], [1, -n-1, n], [-n], [], [], [0.5, 0.5*(n+1), 0.5*(n+2)], r T4 = [-cos2, 2, z], [1, -n-3, n+2], [-n-1], [], [], [1.5, 0.5*(n+3), 0.5*(n+2)], r return T1, T2, T3, T4 return ctx.hypercomb(h, [n], **kwargs) # TODO: do this more generically? def c_memo(f): name = f.__name__ def f_wrapped(ctx): cache = ctx._misc_const_cache prec = ctx.prec p,v = cache.get(name, (-1,0)) if p >= prec: return +v else: cache[name] = (prec, f(ctx)) return cache[name][1] return f_wrapped @c_memo def _airyai_C1(ctx): return 1 / (ctx.cbrt(9) * ctx.gamma(ctx.mpf(2)/3)) @c_memo def _airyai_C2(ctx): return -1 / (ctx.cbrt(3) * ctx.gamma(ctx.mpf(1)/3)) @c_memo def _airybi_C1(ctx): return 1 / (ctx.nthroot(3,6) * ctx.gamma(ctx.mpf(2)/3)) @c_memo def _airybi_C2(ctx): return ctx.nthroot(3,6) / ctx.gamma(ctx.mpf(1)/3) def _airybi_n2_inf(ctx): prec = ctx.prec try: v = ctx.power(3,'2/3')*ctx.gamma('2/3')/(2*ctx.pi) finally: ctx.prec = prec return +v # Derivatives at z = 0 # TODO: could be expressed more elegantly using triple factorials def _airyderiv_0(ctx, z, n, ntype, which): if ntype == 'Z': if n < 0: return z r = ctx.mpq_1_3 prec = ctx.prec try: ctx.prec += 10 v = ctx.gamma((n+1)*r) * ctx.power(3,n*r) / ctx.pi if which == 0: v *= ctx.sinpi(2*(n+1)*r) v /= ctx.power(3,'2/3') else: v *= abs(ctx.sinpi(2*(n+1)*r)) v /= ctx.power(3,'1/6') finally: ctx.prec = prec return +v + z else: # singular (does the limit exist?) raise NotImplementedError @defun def airyai(ctx, z, derivative=0, **kwargs): z = ctx.convert(z) if derivative: n, ntype = ctx._convert_param(derivative) else: n = 0 # Values at infinities if not ctx.isnormal(z) and z: if n and ntype == 'Z': if n == -1: if z == ctx.inf: return ctx.mpf(1)/3 + 1/z if z == ctx.ninf: return ctx.mpf(-2)/3 + 1/z if n < -1: if z == ctx.inf: return z if z == ctx.ninf: return (-1)**n * (-z) if (not n) and z == ctx.inf or z == ctx.ninf: return 1/z # TODO: limits raise ValueError("essential singularity of Ai(z)") # Account for exponential scaling if z: extraprec = max(0, int(1.5*ctx.mag(z))) else: extraprec = 0 if n: if n == 1: def h(): # http://functions.wolfram.com/03.07.06.0005.01 if ctx._re(z) > 4: ctx.prec += extraprec w = z**1.5 r = -0.75/w u = -2*w/3 ctx.prec -= extraprec C = -ctx.exp(u)/(2*ctx.sqrt(ctx.pi))*ctx.nthroot(z,4) return ([C],[1],[],[],[(-1,6),(7,6)],[],r), # http://functions.wolfram.com/03.07.26.0001.01 else: ctx.prec += extraprec w = z**3 / 9 ctx.prec -= extraprec C1 = _airyai_C1(ctx) * 0.5 C2 = _airyai_C2(ctx) T1 = [C1,z],[1,2],[],[],[],[ctx.mpq_5_3],w T2 = [C2],[1],[],[],[],[ctx.mpq_1_3],w return T1, T2 return ctx.hypercomb(h, [], **kwargs) else: if z == 0: return _airyderiv_0(ctx, z, n, ntype, 0) # http://functions.wolfram.com/03.05.20.0004.01 def h(n): ctx.prec += extraprec w = z**3/9 ctx.prec -= extraprec q13,q23,q43 = ctx.mpq_1_3, ctx.mpq_2_3, ctx.mpq_4_3 a1=q13 a2=1 b1=(1-n)*q13 b2=(2-n)*q13 b3=1-n*q13 T1 = [3, z], [n-q23, -n], [a1], [b1,b2,b3], \ [a1,a2], [b1,b2,b3], w a1=q23 b1=(2-n)*q13 b2=1-n*q13 b3=(4-n)*q13 T2 = [3, z, -z], [n-q43, -n, 1], [a1], [b1,b2,b3], \ [a1,a2], [b1,b2,b3], w return T1, T2 v = ctx.hypercomb(h, [n], **kwargs) if ctx._is_real_type(z) and ctx.isint(n): v = ctx._re(v) return v else: def h(): if ctx._re(z) > 4: # We could use 1F1, but it results in huge cancellation; # the following expansion is better. # TODO: asymptotic series for derivatives ctx.prec += extraprec w = z**1.5 r = -0.75/w u = -2*w/3 ctx.prec -= extraprec C = ctx.exp(u)/(2*ctx.sqrt(ctx.pi)*ctx.nthroot(z,4)) return ([C],[1],[],[],[(1,6),(5,6)],[],r), else: ctx.prec += extraprec w = z**3 / 9 ctx.prec -= extraprec C1 = _airyai_C1(ctx) C2 = _airyai_C2(ctx) T1 = [C1],[1],[],[],[],[ctx.mpq_2_3],w T2 = [z*C2],[1],[],[],[],[ctx.mpq_4_3],w return T1, T2 return ctx.hypercomb(h, [], **kwargs) @defun def airybi(ctx, z, derivative=0, **kwargs): z = ctx.convert(z) if derivative: n, ntype = ctx._convert_param(derivative) else: n = 0 # Values at infinities if not ctx.isnormal(z) and z: if n and ntype == 'Z': if z == ctx.inf: return z if z == ctx.ninf: if n == -1: return 1/z if n == -2: return _airybi_n2_inf(ctx) if n < -2: return (-1)**n * (-z) if not n: if z == ctx.inf: return z if z == ctx.ninf: return 1/z # TODO: limits raise ValueError("essential singularity of Bi(z)") if z: extraprec = max(0, int(1.5*ctx.mag(z))) else: extraprec = 0 if n: if n == 1: # http://functions.wolfram.com/03.08.26.0001.01 def h(): ctx.prec += extraprec w = z**3 / 9 ctx.prec -= extraprec C1 = _airybi_C1(ctx)*0.5 C2 = _airybi_C2(ctx) T1 = [C1,z],[1,2],[],[],[],[ctx.mpq_5_3],w T2 = [C2],[1],[],[],[],[ctx.mpq_1_3],w return T1, T2 return ctx.hypercomb(h, [], **kwargs) else: if z == 0: return _airyderiv_0(ctx, z, n, ntype, 1) def h(n): ctx.prec += extraprec w = z**3/9 ctx.prec -= extraprec q13,q23,q43 = ctx.mpq_1_3, ctx.mpq_2_3, ctx.mpq_4_3 q16 = ctx.mpq_1_6 q56 = ctx.mpq_5_6 a1=q13 a2=1 b1=(1-n)*q13 b2=(2-n)*q13 b3=1-n*q13 T1 = [3, z], [n-q16, -n], [a1], [b1,b2,b3], \ [a1,a2], [b1,b2,b3], w a1=q23 b1=(2-n)*q13 b2=1-n*q13 b3=(4-n)*q13 T2 = [3, z], [n-q56, 1-n], [a1], [b1,b2,b3], \ [a1,a2], [b1,b2,b3], w return T1, T2 v = ctx.hypercomb(h, [n], **kwargs) if ctx._is_real_type(z) and ctx.isint(n): v = ctx._re(v) return v else: def h(): ctx.prec += extraprec w = z**3 / 9 ctx.prec -= extraprec C1 = _airybi_C1(ctx) C2 = _airybi_C2(ctx) T1 = [C1],[1],[],[],[],[ctx.mpq_2_3],w T2 = [z*C2],[1],[],[],[],[ctx.mpq_4_3],w return T1, T2 return ctx.hypercomb(h, [], **kwargs) def _airy_zero(ctx, which, k, derivative, complex=False): # Asymptotic formulas are given in DLMF section 9.9 def U(t): return t**(2/3.)*(1-7/(t**2*48)) def T(t): return t**(2/3.)*(1+5/(t**2*48)) k = int(k) assert k >= 1 assert derivative in (0,1) if which == 0: if derivative: return ctx.findroot(lambda z: ctx.airyai(z,1), -U(3*ctx.pi*(4*k-3)/8)) return ctx.findroot(ctx.airyai, -T(3*ctx.pi*(4*k-1)/8)) if which == 1 and complex == False: if derivative: return ctx.findroot(lambda z: ctx.airybi(z,1), -U(3*ctx.pi*(4*k-1)/8)) return ctx.findroot(ctx.airybi, -T(3*ctx.pi*(4*k-3)/8)) if which == 1 and complex == True: if derivative: t = 3*ctx.pi*(4*k-3)/8 + 0.75j*ctx.ln2 s = ctx.expjpi(ctx.mpf(1)/3) * T(t) return ctx.findroot(lambda z: ctx.airybi(z,1), s) t = 3*ctx.pi*(4*k-1)/8 + 0.75j*ctx.ln2 s = ctx.expjpi(ctx.mpf(1)/3) * U(t) return ctx.findroot(ctx.airybi, s) @defun def airyaizero(ctx, k, derivative=0): return _airy_zero(ctx, 0, k, derivative, False) @defun def airybizero(ctx, k, derivative=0, complex=False): return _airy_zero(ctx, 1, k, derivative, complex) def _scorer(ctx, z, which, kwargs): z = ctx.convert(z) if ctx.isinf(z): if z == ctx.inf: if which == 0: return 1/z if which == 1: return z if z == ctx.ninf: return 1/z raise ValueError("essential singularity") if z: extraprec = max(0, int(1.5*ctx.mag(z))) else: extraprec = 0 if kwargs.get('derivative'): raise NotImplementedError # Direct asymptotic expansions, to avoid # exponentially large cancellation try: if ctx.mag(z) > 3: if which == 0 and abs(ctx.arg(z)) < ctx.pi/3 * 0.999: def h(): return (([ctx.pi,z],[-1,-1],[],[],[(1,3),(2,3),1],[],9/z**3),) return ctx.hypercomb(h, [], maxterms=ctx.prec, force_series=True) if which == 1 and abs(ctx.arg(-z)) < 2*ctx.pi/3 * 0.999: def h(): return (([-ctx.pi,z],[-1,-1],[],[],[(1,3),(2,3),1],[],9/z**3),) return ctx.hypercomb(h, [], maxterms=ctx.prec, force_series=True) except ctx.NoConvergence: pass def h(): A = ctx.airybi(z, **kwargs)/3 B = -2*ctx.pi if which == 1: A *= 2 B *= -1 ctx.prec += extraprec w = z**3/9 ctx.prec -= extraprec T1 = [A], [1], [], [], [], [], 0 T2 = [B,z], [-1,2], [], [], [1], [ctx.mpq_4_3,ctx.mpq_5_3], w return T1, T2 return ctx.hypercomb(h, [], **kwargs) @defun def scorergi(ctx, z, **kwargs): return _scorer(ctx, z, 0, kwargs) @defun def scorerhi(ctx, z, **kwargs): return _scorer(ctx, z, 1, kwargs) @defun_wrapped def coulombc(ctx, l, eta, _cache={}): if (l, eta) in _cache and _cache[l,eta][0] >= ctx.prec: return +_cache[l,eta][1] G3 = ctx.loggamma(2*l+2) G1 = ctx.loggamma(1+l+ctx.j*eta) G2 = ctx.loggamma(1+l-ctx.j*eta) v = 2**l * ctx.exp((-ctx.pi*eta+G1+G2)/2 - G3) if not (ctx.im(l) or ctx.im(eta)): v = ctx.re(v) _cache[l,eta] = (ctx.prec, v) return v @defun_wrapped def coulombf(ctx, l, eta, z, w=1, chop=True, **kwargs): # Regular Coulomb wave function # Note: w can be either 1 or -1; the other may be better in some cases # TODO: check that chop=True chops when and only when it should #ctx.prec += 10 def h(l, eta): try: jw = ctx.j*w jwz = ctx.fmul(jw, z, exact=True) jwz2 = ctx.fmul(jwz, -2, exact=True) C = ctx.coulombc(l, eta) T1 = [C, z, ctx.exp(jwz)], [1, l+1, 1], [], [], [1+l+jw*eta], \ [2*l+2], jwz2 except ValueError: T1 = [0], [-1], [], [], [], [], 0 return (T1,) v = ctx.hypercomb(h, [l,eta], **kwargs) if chop and (not ctx.im(l)) and (not ctx.im(eta)) and (not ctx.im(z)) and \ (ctx.re(z) >= 0): v = ctx.re(v) return v @defun_wrapped def _coulomb_chi(ctx, l, eta, _cache={}): if (l, eta) in _cache and _cache[l,eta][0] >= ctx.prec: return _cache[l,eta][1] def terms(): l2 = -l-1 jeta = ctx.j*eta return [ctx.loggamma(1+l+jeta) * (-0.5j), ctx.loggamma(1+l-jeta) * (0.5j), ctx.loggamma(1+l2+jeta) * (0.5j), ctx.loggamma(1+l2-jeta) * (-0.5j), -(l+0.5)*ctx.pi] v = ctx.sum_accurately(terms, 1) _cache[l,eta] = (ctx.prec, v) return v @defun_wrapped def coulombg(ctx, l, eta, z, w=1, chop=True, **kwargs): # Irregular Coulomb wave function # Note: w can be either 1 or -1; the other may be better in some cases # TODO: check that chop=True chops when and only when it should if not ctx._im(l): l = ctx._re(l) # XXX: for isint def h(l, eta): # Force perturbation for integers and half-integers if ctx.isint(l*2): T1 = [0], [-1], [], [], [], [], 0 return (T1,) l2 = -l-1 try: chi = ctx._coulomb_chi(l, eta) jw = ctx.j*w s = ctx.sin(chi) c = ctx.cos(chi) C1 = ctx.coulombc(l,eta) C2 = ctx.coulombc(l2,eta) u = ctx.exp(jw*z) x = -2*jw*z T1 = [s, C1, z, u, c], [-1, 1, l+1, 1, 1], [], [], \ [1+l+jw*eta], [2*l+2], x T2 = [-s, C2, z, u], [-1, 1, l2+1, 1], [], [], \ [1+l2+jw*eta], [2*l2+2], x return T1, T2 except ValueError: T1 = [0], [-1], [], [], [], [], 0 return (T1,) v = ctx.hypercomb(h, [l,eta], **kwargs) if chop and (not ctx._im(l)) and (not ctx._im(eta)) and (not ctx._im(z)) and \ (ctx._re(z) >= 0): v = ctx._re(v) return v def mcmahon(ctx,kind,prime,v,m): """ Computes an estimate for the location of the Bessel function zero j_{v,m}, y_{v,m}, j'_{v,m} or y'_{v,m} using McMahon's asymptotic expansion (Abramowitz & Stegun 9.5.12-13, DLMF 20.21(vi)). Returns (r,err) where r is the estimated location of the root and err is a positive number estimating the error of the asymptotic expansion. """ u = 4*v**2 if kind == 1 and not prime: b = (4*m+2*v-1)*ctx.pi/4 if kind == 2 and not prime: b = (4*m+2*v-3)*ctx.pi/4 if kind == 1 and prime: b = (4*m+2*v-3)*ctx.pi/4 if kind == 2 and prime: b = (4*m+2*v-1)*ctx.pi/4 if not prime: s1 = b s2 = -(u-1)/(8*b) s3 = -4*(u-1)*(7*u-31)/(3*(8*b)**3) s4 = -32*(u-1)*(83*u**2-982*u+3779)/(15*(8*b)**5) s5 = -64*(u-1)*(6949*u**3-153855*u**2+1585743*u-6277237)/(105*(8*b)**7) if prime: s1 = b s2 = -(u+3)/(8*b) s3 = -4*(7*u**2+82*u-9)/(3*(8*b)**3) s4 = -32*(83*u**3+2075*u**2-3039*u+3537)/(15*(8*b)**5) s5 = -64*(6949*u**4+296492*u**3-1248002*u**2+7414380*u-5853627)/(105*(8*b)**7) terms = [s1,s2,s3,s4,s5] s = s1 err = 0.0 for i in range(1,len(terms)): if abs(terms[i]) < abs(terms[i-1]): s += terms[i] else: err = abs(terms[i]) if i == len(terms)-1: err = abs(terms[-1]) return s, err def generalized_bisection(ctx,f,a,b,n): """ Given f known to have exactly n simple roots within [a,b], return a list of n intervals isolating the roots and having opposite signs at the endpoints. TODO: this can be optimized, e.g. by reusing evaluation points. """ assert n >= 1 N = n+1 points = [] signs = [] while 1: points = ctx.linspace(a,b,N) signs = [ctx.sign(f(x)) for x in points] ok_intervals = [(points[i],points[i+1]) for i in range(N-1) \ if signs[i]*signs[i+1] == -1] if len(ok_intervals) == n: return ok_intervals N = N*2 def find_in_interval(ctx, f, ab): return ctx.findroot(f, ab, solver='illinois', verify=False) def bessel_zero(ctx, kind, prime, v, m, isoltol=0.01, _interval_cache={}): prec = ctx.prec workprec = max(prec, ctx.mag(v), ctx.mag(m))+10 try: ctx.prec = workprec v = ctx.mpf(v) m = int(m) prime = int(prime) assert v >= 0 assert m >= 1 assert prime in (0,1) if kind == 1: if prime: f = lambda x: ctx.besselj(v,x,derivative=1) else: f = lambda x: ctx.besselj(v,x) if kind == 2: if prime: f = lambda x: ctx.bessely(v,x,derivative=1) else: f = lambda x: ctx.bessely(v,x) # The first root of J' is very close to 0 for small # orders, and this needs to be special-cased if kind == 1 and prime and m == 1: if v == 0: return ctx.zero if v <= 1: # TODO: use v <= j'_{v,1} < y_{v,1}? r = 2*ctx.sqrt(v*(1+v)/(v+2)) return find_in_interval(ctx, f, (r/10, 2*r)) if (kind,prime,v,m) in _interval_cache: return find_in_interval(ctx, f, _interval_cache[kind,prime,v,m]) r, err = mcmahon(ctx, kind, prime, v, m) if err < isoltol: return find_in_interval(ctx, f, (r-isoltol, r+isoltol)) # An x such that 0 < x < r_{v,1} if kind == 1 and not prime: low = 2.4 if kind == 1 and prime: low = 1.8 if kind == 2 and not prime: low = 0.8 if kind == 2 and prime: low = 2.0 n = m+1 while 1: r1, err = mcmahon(ctx, kind, prime, v, n) if err < isoltol: r2, err2 = mcmahon(ctx, kind, prime, v, n+1) intervals = generalized_bisection(ctx, f, low, 0.5*(r1+r2), n) for k, ab in enumerate(intervals): _interval_cache[kind,prime,v,k+1] = ab return find_in_interval(ctx, f, intervals[m-1]) else: n = n*2 finally: ctx.prec = prec @defun def besseljzero(ctx, v, m, derivative=0): r""" For a real order `\nu \ge 0` and a positive integer `m`, returns `j_{\nu,m}`, the `m`-th positive zero of the Bessel function of the first kind `J_{\nu}(z)` (see :func:`~mpmath.besselj`). Alternatively, with *derivative=1*, gives the first nonnegative simple zero `j'_{\nu,m}` of `J'_{\nu}(z)`. The indexing convention is that used by Abramowitz & Stegun and the DLMF. Note the special case `j'_{0,1} = 0`, while all other zeros are positive. In effect, only simple zeros are counted (all zeros of Bessel functions are simple except possibly `z = 0`) and `j_{\nu,m}` becomes a monotonic function of both `\nu` and `m`. The zeros are interlaced according to the inequalities .. math :: j'_{\nu,k} < j_{\nu,k} < j'_{\nu,k+1} j_{\nu,1} < j_{\nu+1,2} < j_{\nu,2} < j_{\nu+1,2} < j_{\nu,3} < \cdots **Examples** Initial zeros of the Bessel functions `J_0(z), J_1(z), J_2(z)`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> besseljzero(0,1); besseljzero(0,2); besseljzero(0,3) 2.404825557695772768621632 5.520078110286310649596604 8.653727912911012216954199 >>> besseljzero(1,1); besseljzero(1,2); besseljzero(1,3) 3.831705970207512315614436 7.01558666981561875353705 10.17346813506272207718571 >>> besseljzero(2,1); besseljzero(2,2); besseljzero(2,3) 5.135622301840682556301402 8.417244140399864857783614 11.61984117214905942709415 Initial zeros of `J'_0(z), J'_1(z), J'_2(z)`:: 0.0 3.831705970207512315614436 7.01558666981561875353705 >>> besseljzero(1,1,1); besseljzero(1,2,1); besseljzero(1,3,1) 1.84118378134065930264363 5.331442773525032636884016 8.536316366346285834358961 >>> besseljzero(2,1,1); besseljzero(2,2,1); besseljzero(2,3,1) 3.054236928227140322755932 6.706133194158459146634394 9.969467823087595793179143 Zeros with large index:: >>> besseljzero(0,100); besseljzero(0,1000); besseljzero(0,10000) 313.3742660775278447196902 3140.807295225078628895545 31415.14114171350798533666 >>> besseljzero(5,100); besseljzero(5,1000); besseljzero(5,10000) 321.1893195676003157339222 3148.657306813047523500494 31422.9947255486291798943 >>> besseljzero(0,100,1); besseljzero(0,1000,1); besseljzero(0,10000,1) 311.8018681873704508125112 3139.236339643802482833973 31413.57032947022399485808 Zeros of functions with large order:: >>> besseljzero(50,1) 57.11689916011917411936228 >>> besseljzero(50,2) 62.80769876483536093435393 >>> besseljzero(50,100) 388.6936600656058834640981 >>> besseljzero(50,1,1) 52.99764038731665010944037 >>> besseljzero(50,2,1) 60.02631933279942589882363 >>> besseljzero(50,100,1) 387.1083151608726181086283 Zeros of functions with fractional order:: >>> besseljzero(0.5,1); besseljzero(1.5,1); besseljzero(2.25,4) 3.141592653589793238462643 4.493409457909064175307881 15.15657692957458622921634 Both `J_{\nu}(z)` and `J'_{\nu}(z)` can be expressed as infinite products over their zeros:: >>> v,z = 2, mpf(1) >>> (z/2)**v/gamma(v+1) * \ ... nprod(lambda k: 1-(z/besseljzero(v,k))**2, [1,inf]) ... 0.1149034849319004804696469 >>> besselj(v,z) 0.1149034849319004804696469 >>> (z/2)**(v-1)/2/gamma(v) * \ ... nprod(lambda k: 1-(z/besseljzero(v,k,1))**2, [1,inf]) ... 0.2102436158811325550203884 >>> besselj(v,z,1) 0.2102436158811325550203884 """ return +bessel_zero(ctx, 1, derivative, v, m) @defun def besselyzero(ctx, v, m, derivative=0): r""" For a real order `\nu \ge 0` and a positive integer `m`, returns `y_{\nu,m}`, the `m`-th positive zero of the Bessel function of the second kind `Y_{\nu}(z)` (see :func:`~mpmath.bessely`). Alternatively, with *derivative=1*, gives the first positive zero `y'_{\nu,m}` of `Y'_{\nu}(z)`. The zeros are interlaced according to the inequalities .. math :: y_{\nu,k} < y'_{\nu,k} < y_{\nu,k+1} y_{\nu,1} < y_{\nu+1,2} < y_{\nu,2} < y_{\nu+1,2} < y_{\nu,3} < \cdots **Examples** Initial zeros of the Bessel functions `Y_0(z), Y_1(z), Y_2(z)`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> besselyzero(0,1); besselyzero(0,2); besselyzero(0,3) 0.8935769662791675215848871 3.957678419314857868375677 7.086051060301772697623625 >>> besselyzero(1,1); besselyzero(1,2); besselyzero(1,3) 2.197141326031017035149034 5.429681040794135132772005 8.596005868331168926429606 >>> besselyzero(2,1); besselyzero(2,2); besselyzero(2,3) 3.384241767149593472701426 6.793807513268267538291167 10.02347797936003797850539 Initial zeros of `Y'_0(z), Y'_1(z), Y'_2(z)`:: >>> besselyzero(0,1,1); besselyzero(0,2,1); besselyzero(0,3,1) 2.197141326031017035149034 5.429681040794135132772005 8.596005868331168926429606 >>> besselyzero(1,1,1); besselyzero(1,2,1); besselyzero(1,3,1) 3.683022856585177699898967 6.941499953654175655751944 10.12340465543661307978775 >>> besselyzero(2,1,1); besselyzero(2,2,1); besselyzero(2,3,1) 5.002582931446063945200176 8.350724701413079526349714 11.57419546521764654624265 Zeros with large index:: >>> besselyzero(0,100); besselyzero(0,1000); besselyzero(0,10000) 311.8034717601871549333419 3139.236498918198006794026 31413.57034538691205229188 >>> besselyzero(5,100); besselyzero(5,1000); besselyzero(5,10000) 319.6183338562782156235062 3147.086508524556404473186 31421.42392920214673402828 >>> besselyzero(0,100,1); besselyzero(0,1000,1); besselyzero(0,10000,1) 313.3726705426359345050449 3140.807136030340213610065 31415.14112579761578220175 Zeros of functions with large order:: >>> besselyzero(50,1) 53.50285882040036394680237 >>> besselyzero(50,2) 60.11244442774058114686022 >>> besselyzero(50,100) 387.1096509824943957706835 >>> besselyzero(50,1,1) 56.96290427516751320063605 >>> besselyzero(50,2,1) 62.74888166945933944036623 >>> besselyzero(50,100,1) 388.6923300548309258355475 Zeros of functions with fractional order:: >>> besselyzero(0.5,1); besselyzero(1.5,1); besselyzero(2.25,4) 1.570796326794896619231322 2.798386045783887136720249 13.56721208770735123376018 """ return +bessel_zero(ctx, 2, derivative, v, m) sympy-0.7.4.1/sympy/mpmath/functions/zetazeros.py0000644000175000017500000007322312253362407022332 0ustar georgeskgeorgesk""" The function zetazero(n) computes the n-th nontrivial zero of zeta(s). The general strategy is to locate a block of Gram intervals B where we know exactly the number of zeros contained and which of those zeros is that which we search. If n <= 400 000 000 we know exactly the Rosser exceptions, contained in a list in this file. Hence for n<=400 000 000 we simply look at these list of exceptions. If our zero is implicated in one of these exceptions we have our block B. In other case we simply locate the good Rosser block containing our zero. For n > 400 000 000 we apply the method of Turing, as complemented by Lehman, Brent and Trudgian to find a suitable B. """ from .functions import defun, defun_wrapped def find_rosser_block_zero(ctx, n): """for n<400 000 000 determines a block were one find our zero""" for k in range(len(_ROSSER_EXCEPTIONS)//2): a=_ROSSER_EXCEPTIONS[2*k][0] b=_ROSSER_EXCEPTIONS[2*k][1] if ((a<= n-2) and (n-1 <= b)): t0 = ctx.grampoint(a) t1 = ctx.grampoint(b) v0 = ctx._fp.siegelz(t0) v1 = ctx._fp.siegelz(t1) my_zero_number = n-a-1 zero_number_block = b-a pattern = _ROSSER_EXCEPTIONS[2*k+1] return (my_zero_number, [a,b], [t0,t1], [v0,v1]) k = n-2 t,v,b = compute_triple_tvb(ctx, k) T = [t] V = [v] while b < 0: k -= 1 t,v,b = compute_triple_tvb(ctx, k) T.insert(0,t) V.insert(0,v) my_zero_number = n-k-1 m = n-1 t,v,b = compute_triple_tvb(ctx, m) T.append(t) V.append(v) while b < 0: m += 1 t,v,b = compute_triple_tvb(ctx, m) T.append(t) V.append(v) return (my_zero_number, [k,m], T, V) def wpzeros(t): """Precision needed to compute higher zeros""" wp = 53 if t > 3*10**8: wp = 63 if t > 10**11: wp = 70 if t > 10**14: wp = 83 return wp def separate_zeros_in_block(ctx, zero_number_block, T, V, limitloop=None, fp_tolerance=None): """Separate the zeros contained in the block T, limitloop determines how long one must search""" if limitloop is None: limitloop = ctx.inf loopnumber = 0 variations = count_variations(V) while ((variations < zero_number_block) and (loopnumber 0): alpha = ctx.sqrt(u/v) b= (alpha*a+b2)/(alpha+1) else: b = (a+b2)/2 if fp_tolerance < 10: w = ctx._fp.siegelz(b) if abs(w)ITERATION_LIMIT)and(loopnumber>2)and(variations+2==zero_number_block): dtMax=0 dtSec=0 kMax = 0 for k1 in range(1,len(T)): dt = T[k1]-T[k1-1] if dt > dtMax: kMax=k1 dtSec = dtMax dtMax = dt elif (dtdtSec): dtSec = dt if dtMax>3*dtSec: f = lambda x: ctx.rs_z(x,derivative=1) t0=T[kMax-1] t1 = T[kMax] t=ctx.findroot(f, (t0,t1), solver ='illinois',verify=False, verbose=False) v = ctx.siegelz(t) if (t0400 000 000""" sb = sure_number_block(ctx, n) number_goodblocks = 0 m2 = n-1 t, v, b = compute_triple_tvb(ctx, m2) Tf = [t] Vf = [v] while b < 0: m2 += 1 t,v,b = compute_triple_tvb(ctx, m2) Tf.append(t) Vf.append(v) goodpoints = [m2] T = [t] V = [v] while number_goodblocks < 2*sb: m2 += 1 t, v, b = compute_triple_tvb(ctx, m2) T.append(t) V.append(v) while b < 0: m2 += 1 t,v,b = compute_triple_tvb(ctx, m2) T.append(t) V.append(v) goodpoints.append(m2) zn = len(T)-1 A, B, separated =\ separate_zeros_in_block(ctx, zn, T, V, limitloop=ITERATION_LIMIT, fp_tolerance=fp_tolerance) Tf.pop() Tf.extend(A) Vf.pop() Vf.extend(B) if separated: number_goodblocks += 1 else: number_goodblocks = 0 T = [t] V = [v] # Now the same procedure to the left number_goodblocks = 0 m2 = n-2 t, v, b = compute_triple_tvb(ctx, m2) Tf.insert(0,t) Vf.insert(0,v) while b < 0: m2 -= 1 t,v,b = compute_triple_tvb(ctx, m2) Tf.insert(0,t) Vf.insert(0,v) goodpoints.insert(0,m2) T = [t] V = [v] while number_goodblocks < 2*sb: m2 -= 1 t, v, b = compute_triple_tvb(ctx, m2) T.insert(0,t) V.insert(0,v) while b < 0: m2 -= 1 t,v,b = compute_triple_tvb(ctx, m2) T.insert(0,t) V.insert(0,v) goodpoints.insert(0,m2) zn = len(T)-1 A, B, separated =\ separate_zeros_in_block(ctx, zn, T, V, limitloop=ITERATION_LIMIT, fp_tolerance=fp_tolerance) A.pop() Tf = A+Tf B.pop() Vf = B+Vf if separated: number_goodblocks += 1 else: number_goodblocks = 0 T = [t] V = [v] r = goodpoints[2*sb] lg = len(goodpoints) s = goodpoints[lg-2*sb-1] tr, vr, br = compute_triple_tvb(ctx, r) ar = Tf.index(tr) ts, vs, bs = compute_triple_tvb(ctx, s) as1 = Tf.index(ts) T = Tf[ar:as1+1] V = Vf[ar:as1+1] zn = s-r A, B, separated =\ separate_zeros_in_block(ctx, zn,T,V,limitloop=ITERATION_LIMIT, fp_tolerance=fp_tolerance) if separated: return (n-r-1,[r,s],A,B) q = goodpoints[sb] lg = len(goodpoints) t = goodpoints[lg-sb-1] tq, vq, bq = compute_triple_tvb(ctx, q) aq = Tf.index(tq) tt, vt, bt = compute_triple_tvb(ctx, t) at = Tf.index(tt) T = Tf[aq:at+1] V = Vf[aq:at+1] return (n-q-1,[q,t],T,V) def count_variations(V): count = 0 vold = V[0] for n in range(1, len(V)): vnew = V[n] if vold*vnew < 0: count +=1 vold = vnew return count def pattern_construct(ctx, block, T, V): pattern = '(' a = block[0] b = block[1] t0,v0,b0 = compute_triple_tvb(ctx, a) k = 0 k0 = 0 for n in range(a+1,b+1): t1,v1,b1 = compute_triple_tvb(ctx, n) lgT =len(T) while (k < lgT) and (T[k] <= t1): k += 1 L = V[k0:k] L.append(v1) L.insert(0,v0) count = count_variations(L) pattern = pattern + ("%s" % count) if b1 > 0: pattern = pattern + ')(' k0 = k t0,v0,b0 = t1,v1,b1 pattern = pattern[:-1] return pattern @defun def zetazero(ctx, n, info=False, round=True): r""" Computes the `n`-th nontrivial zero of `\zeta(s)` on the critical line, i.e. returns an approximation of the `n`-th largest complex number `s = \frac{1}{2} + ti` for which `\zeta(s) = 0`. Equivalently, the imaginary part `t` is a zero of the Z-function (:func:`~mpmath.siegelz`). **Examples** The first few zeros:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> zetazero(1) (0.5 + 14.13472514173469379045725j) >>> zetazero(2) (0.5 + 21.02203963877155499262848j) >>> zetazero(20) (0.5 + 77.14484006887480537268266j) Verifying that the values are zeros:: >>> for n in range(1,5): ... s = zetazero(n) ... chop(zeta(s)), chop(siegelz(s.imag)) ... (0.0, 0.0) (0.0, 0.0) (0.0, 0.0) (0.0, 0.0) Negative indices give the conjugate zeros (`n = 0` is undefined):: >>> zetazero(-1) (0.5 - 14.13472514173469379045725j) :func:`~mpmath.zetazero` supports arbitrarily large `n` and arbitrary precision:: >>> mp.dps = 15 >>> zetazero(1234567) (0.5 + 727690.906948208j) >>> mp.dps = 50 >>> zetazero(1234567) (0.5 + 727690.9069482075392389420041147142092708393819935j) >>> chop(zeta(_)/_) 0.0 with *info=True*, :func:`~mpmath.zetazero` gives additional information:: >>> mp.dps = 15 >>> zetazero(542964976,info=True) ((0.5 + 209039046.578535j), [542964969, 542964978], 6, '(013111110)') This means that the zero is between Gram points 542964969 and 542964978; it is the 6-th zero between them. Finally (01311110) is the pattern of zeros in this interval. The numbers indicate the number of zeros in each Gram interval (Rosser blocks between parenthesis). In this case there is only one Rosser block of length nine. """ n = int(n) if n < 0: return ctx.zetazero(-n).conjugate() if n == 0: raise ValueError("n must be nonzero") wpinitial = ctx.prec try: wpz, fp_tolerance = comp_fp_tolerance(ctx, n) ctx.prec = wpz if n < 400000000: my_zero_number, block, T, V =\ find_rosser_block_zero(ctx, n) else: my_zero_number, block, T, V =\ search_supergood_block(ctx, n, fp_tolerance) zero_number_block = block[1]-block[0] T, V, separated = separate_zeros_in_block(ctx, zero_number_block, T, V, limitloop=ctx.inf, fp_tolerance=fp_tolerance) if info: pattern = pattern_construct(ctx,block,T,V) prec = max(wpinitial, wpz) t = separate_my_zero(ctx, my_zero_number, zero_number_block,T,V,prec) v = ctx.mpc(0.5,t) finally: ctx.prec = wpinitial if round: v =+v if info: return (v,block,my_zero_number,pattern) else: return v def gram_index(ctx, t): if t > 10**13: wp = 3*ctx.log(t, 10) else: wp = 0 prec = ctx.prec try: ctx.prec += wp x0 = (t/(2*ctx.pi))*ctx.log(t/(2*ctx.pi)) h = ctx.findroot(lambda x:ctx.siegeltheta(t)-ctx.pi*x, x0) h = int(h) finally: ctx.prec = prec return(h) def count_to(ctx, t, T, V): count = 0 vold = V[0] told = T[0] tnew = T[1] k = 1 while tnew < t: vnew = V[k] if vold*vnew < 0: count += 1 vold = vnew k += 1 tnew = T[k] a = ctx.siegelz(t) if a*vold < 0: count += 1 return count def comp_fp_tolerance(ctx, n): wpz = wpzeros(n*ctx.log(n)) if n < 15*10**8: fp_tolerance = 0.0005 elif n <= 10**14: fp_tolerance = 0.1 else: fp_tolerance = 100 return wpz, fp_tolerance @defun def nzeros(ctx, t): r""" Computes the number of zeros of the Riemann zeta function in `(0,1) \times (0,t]`, usually denoted by `N(t)`. **Examples** The first zero has imaginary part between 14 and 15:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> nzeros(14) 0 >>> nzeros(15) 1 >>> zetazero(1) (0.5 + 14.1347251417347j) Some closely spaced zeros:: >>> nzeros(10**7) 21136125 >>> zetazero(21136125) (0.5 + 9999999.32718175j) >>> zetazero(21136126) (0.5 + 10000000.2400236j) >>> nzeros(545439823.215) 1500000001 >>> zetazero(1500000001) (0.5 + 545439823.201985j) >>> zetazero(1500000002) (0.5 + 545439823.325697j) This confirms the data given by J. van de Lune, H. J. J. te Riele and D. T. Winter in 1986. """ if t < 14.1347251417347: return 0 x = gram_index(ctx, t) k = int(ctx.floor(x)) wpinitial = ctx.prec wpz, fp_tolerance = comp_fp_tolerance(ctx, k) ctx.prec = wpz a = ctx.siegelz(t) if k == -1 and a < 0: return 0 elif k == -1 and a > 0: return 1 if k+2 < 400000000: Rblock = find_rosser_block_zero(ctx, k+2) else: Rblock = search_supergood_block(ctx, k+2, fp_tolerance) n1, n2 = Rblock[1] if n2-n1 == 1: b = Rblock[3][0] if a*b > 0: ctx.prec = wpinitial return k+1 else: ctx.prec = wpinitial return k+2 my_zero_number,block, T, V = Rblock zero_number_block = n2-n1 T, V, separated = separate_zeros_in_block(ctx,\ zero_number_block, T, V,\ limitloop=ctx.inf,\ fp_tolerance=fp_tolerance) n = count_to(ctx, t, T, V) ctx.prec = wpinitial return n+n1+1 @defun_wrapped def backlunds(ctx, t): r""" Computes the function `S(t) = \operatorname{arg} \zeta(\frac{1}{2} + it) / \pi`. See Titchmarsh Section 9.3 for details of the definition. **Examples** >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> backlunds(217.3) 0.16302205431184 Generally, the value is a small number. At Gram points it is an integer, frequently equal to 0:: >>> chop(backlunds(grampoint(200))) 0.0 >>> backlunds(extraprec(10)(grampoint)(211)) 1.0 >>> backlunds(extraprec(10)(grampoint)(232)) -1.0 The number of zeros of the Riemann zeta function up to height `t` satisfies `N(t) = \theta(t)/\pi + 1 + S(t)` (see :func:nzeros` and :func:`siegeltheta`):: >>> t = 1234.55 >>> nzeros(t) 842 >>> siegeltheta(t)/pi+1+backlunds(t) 842.0 """ return ctx.nzeros(t)-1-ctx.siegeltheta(t)/ctx.pi """ _ROSSER_EXCEPTIONS is a list of all exceptions to Rosser's rule for n <= 400 000 000. Alternately the entry is of type [n,m], or a string. The string is the zero pattern of the Block and the relevant adjacent. For example (010)3 corresponds to a block composed of three Gram intervals, the first ant third without a zero and the intermediate with a zero. The next Gram interval contain three zeros. So that in total we have 4 zeros in 4 Gram blocks. n and m are the indices of the Gram points of this interval of four Gram intervals. The Rosser exception is therefore formed by the three Gram intervals that are signaled between parenthesis. We have included also some Rosser's exceptions beyond n=400 000 000 that are noted in the literature by some reason. The list is composed from the data published in the references: R. P. Brent, J. van de Lune, H. J. J. te Riele, D. T. Winter, 'On the Zeros of the Riemann Zeta Function in the Critical Strip. II', Math. Comp. 39 (1982) 681--688. See also Corrigenda in Math. Comp. 46 (1986) 771. J. van de Lune, H. J. J. te Riele, 'On the Zeros of the Riemann Zeta Function in the Critical Strip. III', Math. Comp. 41 (1983) 759--767. See also Corrigenda in Math. Comp. 46 (1986) 771. J. van de Lune, 'Sums of Equal Powers of Positive Integers', Dissertation, Vrije Universiteit te Amsterdam, Centrum voor Wiskunde en Informatica, Amsterdam, 1984. Thanks to the authors all this papers and those others that have contributed to make this possible. """ _ROSSER_EXCEPTIONS = \ [[13999525, 13999528], '(00)3', [30783329, 30783332], '(00)3', [30930926, 30930929], '3(00)', [37592215, 37592218], '(00)3', [40870156, 40870159], '(00)3', [43628107, 43628110], '(00)3', [46082042, 46082045], '(00)3', [46875667, 46875670], '(00)3', [49624540, 49624543], '3(00)', [50799238, 50799241], '(00)3', [55221453, 55221456], '3(00)', [56948779, 56948782], '3(00)', [60515663, 60515666], '(00)3', [61331766, 61331770], '(00)40', [69784843, 69784846], '3(00)', [75052114, 75052117], '(00)3', [79545240, 79545243], '3(00)', [79652247, 79652250], '3(00)', [83088043, 83088046], '(00)3', [83689522, 83689525], '3(00)', [85348958, 85348961], '(00)3', [86513820, 86513823], '(00)3', [87947596, 87947599], '3(00)', [88600095, 88600098], '(00)3', [93681183, 93681186], '(00)3', [100316551, 100316554], '3(00)', [100788444, 100788447], '(00)3', [106236172, 106236175], '(00)3', [106941327, 106941330], '3(00)', [107287955, 107287958], '(00)3', [107532016, 107532019], '3(00)', [110571044, 110571047], '(00)3', [111885253, 111885256], '3(00)', [113239783, 113239786], '(00)3', [120159903, 120159906], '(00)3', [121424391, 121424394], '3(00)', [121692931, 121692934], '3(00)', [121934170, 121934173], '3(00)', [122612848, 122612851], '3(00)', [126116567, 126116570], '(00)3', [127936513, 127936516], '(00)3', [128710277, 128710280], '3(00)', [129398902, 129398905], '3(00)', [130461096, 130461099], '3(00)', [131331947, 131331950], '3(00)', [137334071, 137334074], '3(00)', [137832603, 137832606], '(00)3', [138799471, 138799474], '3(00)', [139027791, 139027794], '(00)3', [141617806, 141617809], '(00)3', [144454931, 144454934], '(00)3', [145402379, 145402382], '3(00)', [146130245, 146130248], '3(00)', [147059770, 147059773], '(00)3', [147896099, 147896102], '3(00)', [151097113, 151097116], '(00)3', [152539438, 152539441], '(00)3', [152863168, 152863171], '3(00)', [153522726, 153522729], '3(00)', [155171524, 155171527], '3(00)', [155366607, 155366610], '(00)3', [157260686, 157260689], '3(00)', [157269224, 157269227], '(00)3', [157755123, 157755126], '(00)3', [158298484, 158298487], '3(00)', [160369050, 160369053], '3(00)', [162962787, 162962790], '(00)3', [163724709, 163724712], '(00)3', [164198113, 164198116], '3(00)', [164689301, 164689305], '(00)40', [164880228, 164880231], '3(00)', [166201932, 166201935], '(00)3', [168573836, 168573839], '(00)3', [169750763, 169750766], '(00)3', [170375507, 170375510], '(00)3', [170704879, 170704882], '3(00)', [172000992, 172000995], '3(00)', [173289941, 173289944], '(00)3', [173737613, 173737616], '3(00)', [174102513, 174102516], '(00)3', [174284990, 174284993], '(00)3', [174500513, 174500516], '(00)3', [175710609, 175710612], '(00)3', [176870843, 176870846], '3(00)', [177332732, 177332735], '3(00)', [177902861, 177902864], '3(00)', [179979095, 179979098], '(00)3', [181233726, 181233729], '3(00)', [181625435, 181625438], '(00)3', [182105255, 182105259], '22(00)', [182223559, 182223562], '3(00)', [191116404, 191116407], '3(00)', [191165599, 191165602], '3(00)', [191297535, 191297539], '(00)22', [192485616, 192485619], '(00)3', [193264634, 193264638], '22(00)', [194696968, 194696971], '(00)3', [195876805, 195876808], '(00)3', [195916548, 195916551], '3(00)', [196395160, 196395163], '3(00)', [196676303, 196676306], '(00)3', [197889882, 197889885], '3(00)', [198014122, 198014125], '(00)3', [199235289, 199235292], '(00)3', [201007375, 201007378], '(00)3', [201030605, 201030608], '3(00)', [201184290, 201184293], '3(00)', [201685414, 201685418], '(00)22', [202762875, 202762878], '3(00)', [202860957, 202860960], '3(00)', [203832577, 203832580], '3(00)', [205880544, 205880547], '(00)3', [206357111, 206357114], '(00)3', [207159767, 207159770], '3(00)', [207167343, 207167346], '3(00)', [207482539, 207482543], '3(010)', [207669540, 207669543], '3(00)', [208053426, 208053429], '(00)3', [208110027, 208110030], '3(00)', [209513826, 209513829], '3(00)', [212623522, 212623525], '(00)3', [213841715, 213841718], '(00)3', [214012333, 214012336], '(00)3', [214073567, 214073570], '(00)3', [215170600, 215170603], '3(00)', [215881039, 215881042], '3(00)', [216274604, 216274607], '3(00)', [216957120, 216957123], '3(00)', [217323208, 217323211], '(00)3', [218799264, 218799267], '(00)3', [218803557, 218803560], '3(00)', [219735146, 219735149], '(00)3', [219830062, 219830065], '3(00)', [219897904, 219897907], '(00)3', [221205545, 221205548], '(00)3', [223601929, 223601932], '(00)3', [223907076, 223907079], '3(00)', [223970397, 223970400], '(00)3', [224874044, 224874048], '22(00)', [225291157, 225291160], '(00)3', [227481734, 227481737], '(00)3', [228006442, 228006445], '3(00)', [228357900, 228357903], '(00)3', [228386399, 228386402], '(00)3', [228907446, 228907449], '(00)3', [228984552, 228984555], '3(00)', [229140285, 229140288], '3(00)', [231810024, 231810027], '(00)3', [232838062, 232838065], '3(00)', [234389088, 234389091], '3(00)', [235588194, 235588197], '(00)3', [236645695, 236645698], '(00)3', [236962876, 236962879], '3(00)', [237516723, 237516727], '04(00)', [240004911, 240004914], '(00)3', [240221306, 240221309], '3(00)', [241389213, 241389217], '(010)3', [241549003, 241549006], '(00)3', [241729717, 241729720], '(00)3', [241743684, 241743687], '3(00)', [243780200, 243780203], '3(00)', [243801317, 243801320], '(00)3', [244122072, 244122075], '(00)3', [244691224, 244691227], '3(00)', [244841577, 244841580], '(00)3', [245813461, 245813464], '(00)3', [246299475, 246299478], '(00)3', [246450176, 246450179], '3(00)', [249069349, 249069352], '(00)3', [250076378, 250076381], '(00)3', [252442157, 252442160], '3(00)', [252904231, 252904234], '3(00)', [255145220, 255145223], '(00)3', [255285971, 255285974], '3(00)', [256713230, 256713233], '(00)3', [257992082, 257992085], '(00)3', [258447955, 258447959], '22(00)', [259298045, 259298048], '3(00)', [262141503, 262141506], '(00)3', [263681743, 263681746], '3(00)', [266527881, 266527885], '(010)3', [266617122, 266617125], '(00)3', [266628044, 266628047], '3(00)', [267305763, 267305766], '(00)3', [267388404, 267388407], '3(00)', [267441672, 267441675], '3(00)', [267464886, 267464889], '(00)3', [267554907, 267554910], '3(00)', [269787480, 269787483], '(00)3', [270881434, 270881437], '(00)3', [270997583, 270997586], '3(00)', [272096378, 272096381], '3(00)', [272583009, 272583012], '(00)3', [274190881, 274190884], '3(00)', [274268747, 274268750], '(00)3', [275297429, 275297432], '3(00)', [275545476, 275545479], '3(00)', [275898479, 275898482], '3(00)', [275953000, 275953003], '(00)3', [277117197, 277117201], '(00)22', [277447310, 277447313], '3(00)', [279059657, 279059660], '3(00)', [279259144, 279259147], '3(00)', [279513636, 279513639], '3(00)', [279849069, 279849072], '3(00)', [280291419, 280291422], '(00)3', [281449425, 281449428], '3(00)', [281507953, 281507956], '3(00)', [281825600, 281825603], '(00)3', [282547093, 282547096], '3(00)', [283120963, 283120966], '3(00)', [283323493, 283323496], '(00)3', [284764535, 284764538], '3(00)', [286172639, 286172642], '3(00)', [286688824, 286688827], '(00)3', [287222172, 287222175], '3(00)', [287235534, 287235537], '3(00)', [287304861, 287304864], '3(00)', [287433571, 287433574], '(00)3', [287823551, 287823554], '(00)3', [287872422, 287872425], '3(00)', [288766615, 288766618], '3(00)', [290122963, 290122966], '3(00)', [290450849, 290450853], '(00)22', [291426141, 291426144], '3(00)', [292810353, 292810356], '3(00)', [293109861, 293109864], '3(00)', [293398054, 293398057], '3(00)', [294134426, 294134429], '3(00)', [294216438, 294216441], '(00)3', [295367141, 295367144], '3(00)', [297834111, 297834114], '3(00)', [299099969, 299099972], '3(00)', [300746958, 300746961], '3(00)', [301097423, 301097426], '(00)3', [301834209, 301834212], '(00)3', [302554791, 302554794], '(00)3', [303497445, 303497448], '3(00)', [304165344, 304165347], '3(00)', [304790218, 304790222], '3(010)', [305302352, 305302355], '(00)3', [306785996, 306785999], '3(00)', [307051443, 307051446], '3(00)', [307481539, 307481542], '3(00)', [308605569, 308605572], '3(00)', [309237610, 309237613], '3(00)', [310509287, 310509290], '(00)3', [310554057, 310554060], '3(00)', [310646345, 310646348], '3(00)', [311274896, 311274899], '(00)3', [311894272, 311894275], '3(00)', [312269470, 312269473], '(00)3', [312306601, 312306605], '(00)40', [312683193, 312683196], '3(00)', [314499804, 314499807], '3(00)', [314636802, 314636805], '(00)3', [314689897, 314689900], '3(00)', [314721319, 314721322], '3(00)', [316132890, 316132893], '3(00)', [316217470, 316217474], '(010)3', [316465705, 316465708], '3(00)', [316542790, 316542793], '(00)3', [320822347, 320822350], '3(00)', [321733242, 321733245], '3(00)', [324413970, 324413973], '(00)3', [325950140, 325950143], '(00)3', [326675884, 326675887], '(00)3', [326704208, 326704211], '3(00)', [327596247, 327596250], '3(00)', [328123172, 328123175], '3(00)', [328182212, 328182215], '(00)3', [328257498, 328257501], '3(00)', [328315836, 328315839], '(00)3', [328800974, 328800977], '(00)3', [328998509, 328998512], '3(00)', [329725370, 329725373], '(00)3', [332080601, 332080604], '(00)3', [332221246, 332221249], '(00)3', [332299899, 332299902], '(00)3', [332532822, 332532825], '(00)3', [333334544, 333334548], '(00)22', [333881266, 333881269], '3(00)', [334703267, 334703270], '3(00)', [334875138, 334875141], '3(00)', [336531451, 336531454], '3(00)', [336825907, 336825910], '(00)3', [336993167, 336993170], '(00)3', [337493998, 337494001], '3(00)', [337861034, 337861037], '3(00)', [337899191, 337899194], '(00)3', [337958123, 337958126], '(00)3', [342331982, 342331985], '3(00)', [342676068, 342676071], '3(00)', [347063781, 347063784], '3(00)', [347697348, 347697351], '3(00)', [347954319, 347954322], '3(00)', [348162775, 348162778], '3(00)', [349210702, 349210705], '(00)3', [349212913, 349212916], '3(00)', [349248650, 349248653], '(00)3', [349913500, 349913503], '3(00)', [350891529, 350891532], '3(00)', [351089323, 351089326], '3(00)', [351826158, 351826161], '3(00)', [352228580, 352228583], '(00)3', [352376244, 352376247], '3(00)', [352853758, 352853761], '(00)3', [355110439, 355110442], '(00)3', [355808090, 355808094], '(00)40', [355941556, 355941559], '3(00)', [356360231, 356360234], '(00)3', [356586657, 356586660], '3(00)', [356892926, 356892929], '(00)3', [356908232, 356908235], '3(00)', [357912730, 357912733], '3(00)', [358120344, 358120347], '3(00)', [359044096, 359044099], '(00)3', [360819357, 360819360], '3(00)', [361399662, 361399666], '(010)3', [362361315, 362361318], '(00)3', [363610112, 363610115], '(00)3', [363964804, 363964807], '3(00)', [364527375, 364527378], '(00)3', [365090327, 365090330], '(00)3', [365414539, 365414542], '3(00)', [366738474, 366738477], '3(00)', [368714778, 368714783], '04(010)', [368831545, 368831548], '(00)3', [368902387, 368902390], '(00)3', [370109769, 370109772], '3(00)', [370963333, 370963336], '3(00)', [372541136, 372541140], '3(010)', [372681562, 372681565], '(00)3', [373009410, 373009413], '(00)3', [373458970, 373458973], '3(00)', [375648658, 375648661], '3(00)', [376834728, 376834731], '3(00)', [377119945, 377119948], '(00)3', [377335703, 377335706], '(00)3', [378091745, 378091748], '3(00)', [379139522, 379139525], '3(00)', [380279160, 380279163], '(00)3', [380619442, 380619445], '3(00)', [381244231, 381244234], '3(00)', [382327446, 382327450], '(010)3', [382357073, 382357076], '3(00)', [383545479, 383545482], '3(00)', [384363766, 384363769], '(00)3', [384401786, 384401790], '22(00)', [385198212, 385198215], '3(00)', [385824476, 385824479], '(00)3', [385908194, 385908197], '3(00)', [386946806, 386946809], '3(00)', [387592175, 387592179], '22(00)', [388329293, 388329296], '(00)3', [388679566, 388679569], '3(00)', [388832142, 388832145], '3(00)', [390087103, 390087106], '(00)3', [390190926, 390190930], '(00)22', [390331207, 390331210], '3(00)', [391674495, 391674498], '3(00)', [391937831, 391937834], '3(00)', [391951632, 391951636], '(00)22', [392963986, 392963989], '(00)3', [393007921, 393007924], '3(00)', [393373210, 393373213], '3(00)', [393759572, 393759575], '(00)3', [394036662, 394036665], '(00)3', [395813866, 395813869], '(00)3', [395956690, 395956693], '3(00)', [396031670, 396031673], '3(00)', [397076433, 397076436], '3(00)', [397470601, 397470604], '3(00)', [398289458, 398289461], '3(00)', # [368714778, 368714783], '04(010)', [437953499, 437953504], '04(010)', [526196233, 526196238], '032(00)', [744719566, 744719571], '(010)40', [750375857, 750375862], '032(00)', [958241932, 958241937], '04(010)', [983377342, 983377347], '(00)410', [1003780080, 1003780085], '04(010)', [1070232754, 1070232759], '(00)230', [1209834865, 1209834870], '032(00)', [1257209100, 1257209105], '(00)410', [1368002233, 1368002238], '(00)230' ] sympy-0.7.4.1/sympy/mpmath/functions/orthogonal.py0000644000175000017500000003737712253362407022472 0ustar georgeskgeorgeskfrom .functions import defun, defun_wrapped def _hermite_param(ctx, n, z, parabolic_cylinder): """ Combined calculation of the Hermite polynomial H_n(z) (and its generalization to complex n) and the parabolic cylinder function D. """ n, ntyp = ctx._convert_param(n) z = ctx.convert(z) q = -ctx.mpq_1_2 # For re(z) > 0, 2F0 -- http://functions.wolfram.com/ # HypergeometricFunctions/HermiteHGeneral/06/02/0009/ # Otherwise, there is a reflection formula # 2F0 + http://functions.wolfram.com/HypergeometricFunctions/ # HermiteHGeneral/16/01/01/0006/ # # TODO: # An alternative would be to use # http://functions.wolfram.com/HypergeometricFunctions/ # HermiteHGeneral/06/02/0006/ # # Also, the 1F1 expansion # http://functions.wolfram.com/HypergeometricFunctions/ # HermiteHGeneral/26/01/02/0001/ # should probably be used for tiny z if not z: T1 = [2, ctx.pi], [n, 0.5], [], [q*(n-1)], [], [], 0 if parabolic_cylinder: T1[1][0] += q*n return T1, can_use_2f0 = ctx.isnpint(-n) or ctx.re(z) > 0 or \ (ctx.re(z) == 0 and ctx.im(z) > 0) expprec = ctx.prec*4 + 20 if parabolic_cylinder: u = ctx.fmul(ctx.fmul(z,z,prec=expprec), -0.25, exact=True) w = ctx.fmul(z, ctx.sqrt(0.5,prec=expprec), prec=expprec) else: w = z w2 = ctx.fmul(w, w, prec=expprec) rw2 = ctx.fdiv(1, w2, prec=expprec) nrw2 = ctx.fneg(rw2, exact=True) nw = ctx.fneg(w, exact=True) if can_use_2f0: T1 = [2, w], [n, n], [], [], [q*n, q*(n-1)], [], nrw2 terms = [T1] else: T1 = [2, nw], [n, n], [], [], [q*n, q*(n-1)], [], nrw2 T2 = [2, ctx.pi, nw], [n+2, 0.5, 1], [], [q*n], [q*(n-1)], [1-q], w2 terms = [T1,T2] # Multiply by prefactor for D_n if parabolic_cylinder: expu = ctx.exp(u) for i in range(len(terms)): terms[i][1][0] += q*n terms[i][0].append(expu) terms[i][1].append(1) return tuple(terms) @defun def hermite(ctx, n, z, **kwargs): return ctx.hypercomb(lambda: _hermite_param(ctx, n, z, 0), [], **kwargs) @defun def pcfd(ctx, n, z, **kwargs): r""" Gives the parabolic cylinder function in Whittaker's notation `D_n(z) = U(-n-1/2, z)` (see :func:`~mpmath.pcfu`). It solves the differential equation .. math :: y'' + \left(n + \frac{1}{2} - \frac{1}{4} z^2\right) y = 0. and can be represented in terms of Hermite polynomials (see :func:`~mpmath.hermite`) as .. math :: D_n(z) = 2^{-n/2} e^{-z^2/4} H_n\left(\frac{z}{\sqrt{2}}\right). **Plots** .. literalinclude :: /modules/mpmath/plots/pcfd.py .. image :: /modules/mpmath/plots/pcfd.png **Examples** >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> pcfd(0,0); pcfd(1,0); pcfd(2,0); pcfd(3,0) 1.0 0.0 -1.0 0.0 >>> pcfd(4,0); pcfd(-3,0) 3.0 0.6266570686577501256039413 >>> pcfd('1/2', 2+3j) (-5.363331161232920734849056 - 3.858877821790010714163487j) >>> pcfd(2, -10) 1.374906442631438038871515e-9 Verifying the differential equation:: >>> n = mpf(2.5) >>> y = lambda z: pcfd(n,z) >>> z = 1.75 >>> chop(diff(y,z,2) + (n+0.5-0.25*z**2)*y(z)) 0.0 Rational Taylor series expansion when `n` is an integer:: >>> taylor(lambda z: pcfd(5,z), 0, 7) [0.0, 15.0, 0.0, -13.75, 0.0, 3.96875, 0.0, -0.6015625] """ return ctx.hypercomb(lambda: _hermite_param(ctx, n, z, 1), [], **kwargs) @defun def pcfu(ctx, a, z, **kwargs): r""" Gives the parabolic cylinder function `U(a,z)`, which may be defined for `\Re(z) > 0` in terms of the confluent U-function (see :func:`~mpmath.hyperu`) by .. math :: U(a,z) = 2^{-\frac{1}{4}-\frac{a}{2}} e^{-\frac{1}{4} z^2} U\left(\frac{a}{2}+\frac{1}{4}, \frac{1}{2}, \frac{1}{2}z^2\right) or, for arbitrary `z`, .. math :: e^{-\frac{1}{4}z^2} U(a,z) = U(a,0) \,_1F_1\left(-\tfrac{a}{2}+\tfrac{1}{4}; \tfrac{1}{2}; -\tfrac{1}{2}z^2\right) + U'(a,0) z \,_1F_1\left(-\tfrac{a}{2}+\tfrac{3}{4}; \tfrac{3}{2}; -\tfrac{1}{2}z^2\right). **Examples** Connection to other functions:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> z = mpf(3) >>> pcfu(0.5,z) 0.03210358129311151450551963 >>> sqrt(pi/2)*exp(z**2/4)*erfc(z/sqrt(2)) 0.03210358129311151450551963 >>> pcfu(0.5,-z) 23.75012332835297233711255 >>> sqrt(pi/2)*exp(z**2/4)*erfc(-z/sqrt(2)) 23.75012332835297233711255 >>> pcfu(0.5,-z) 23.75012332835297233711255 >>> sqrt(pi/2)*exp(z**2/4)*erfc(-z/sqrt(2)) 23.75012332835297233711255 """ n, _ = ctx._convert_param(a) return ctx.pcfd(-n-ctx.mpq_1_2, z) @defun def pcfv(ctx, a, z, **kwargs): r""" Gives the parabolic cylinder function `V(a,z)`, which can be represented in terms of :func:`~mpmath.pcfu` as .. math :: V(a,z) = \frac{\Gamma(a+\tfrac{1}{2}) (U(a,-z)-\sin(\pi a) U(a,z)}{\pi}. **Examples** Wronskian relation between `U` and `V`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> a, z = 2, 3 >>> pcfu(a,z)*diff(pcfv,(a,z),(0,1))-diff(pcfu,(a,z),(0,1))*pcfv(a,z) 0.7978845608028653558798921 >>> sqrt(2/pi) 0.7978845608028653558798921 >>> a, z = 2.5, 3 >>> pcfu(a,z)*diff(pcfv,(a,z),(0,1))-diff(pcfu,(a,z),(0,1))*pcfv(a,z) 0.7978845608028653558798921 >>> a, z = 0.25, -1 >>> pcfu(a,z)*diff(pcfv,(a,z),(0,1))-diff(pcfu,(a,z),(0,1))*pcfv(a,z) 0.7978845608028653558798921 >>> a, z = 2+1j, 2+3j >>> chop(pcfu(a,z)*diff(pcfv,(a,z),(0,1))-diff(pcfu,(a,z),(0,1))*pcfv(a,z)) 0.7978845608028653558798921 """ n, ntype = ctx._convert_param(a) z = ctx.convert(z) q = ctx.mpq_1_2 r = ctx.mpq_1_4 if ntype == 'Q' and ctx.isint(n*2): # Faster for half-integers def h(): jz = ctx.fmul(z, -1j, exact=True) T1terms = _hermite_param(ctx, -n-q, z, 1) T2terms = _hermite_param(ctx, n-q, jz, 1) for T in T1terms: T[0].append(1j) T[1].append(1) T[3].append(q-n) u = ctx.expjpi((q*n-r)) * ctx.sqrt(2/ctx.pi) for T in T2terms: T[0].append(u) T[1].append(1) return T1terms + T2terms v = ctx.hypercomb(h, [], **kwargs) if ctx._is_real_type(n) and ctx._is_real_type(z): v = ctx._re(v) return v else: def h(n): w = ctx.square_exp_arg(z, -0.25) u = ctx.square_exp_arg(z, 0.5) e = ctx.exp(w) l = [ctx.pi, q, ctx.exp(w)] Y1 = l, [-q, n*q+r, 1], [r-q*n], [], [q*n+r], [q], u Y2 = l + [z], [-q, n*q-r, 1, 1], [1-r-q*n], [], [q*n+1-r], [1+q], u c, s = ctx.cospi_sinpi(r+q*n) Y1[0].append(s) Y2[0].append(c) for Y in (Y1, Y2): Y[1].append(1) Y[3].append(q-n) return Y1, Y2 return ctx.hypercomb(h, [n], **kwargs) @defun def pcfw(ctx, a, z, **kwargs): r""" Gives the parabolic cylinder function `W(a,z)` defined in (DLMF 12.14). **Examples** Value at the origin:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> a = mpf(0.25) >>> pcfw(a,0) 0.9722833245718180765617104 >>> power(2,-0.75)*sqrt(abs(gamma(0.25+0.5j*a)/gamma(0.75+0.5j*a))) 0.9722833245718180765617104 >>> diff(pcfw,(a,0),(0,1)) -0.5142533944210078966003624 >>> -power(2,-0.25)*sqrt(abs(gamma(0.75+0.5j*a)/gamma(0.25+0.5j*a))) -0.5142533944210078966003624 """ n, _ = ctx._convert_param(a) z = ctx.convert(z) def terms(): phi2 = ctx.arg(ctx.gamma(0.5 + ctx.j*n)) phi2 = (ctx.loggamma(0.5+ctx.j*n) - ctx.loggamma(0.5-ctx.j*n))/2j rho = ctx.pi/8 + 0.5*phi2 # XXX: cancellation computing k k = ctx.sqrt(1 + ctx.exp(2*ctx.pi*n)) - ctx.exp(ctx.pi*n) C = ctx.sqrt(k/2) * ctx.exp(0.25*ctx.pi*n) yield C * ctx.expj(rho) * ctx.pcfu(ctx.j*n, z*ctx.expjpi(-0.25)) yield C * ctx.expj(-rho) * ctx.pcfu(-ctx.j*n, z*ctx.expjpi(0.25)) v = ctx.sum_accurately(terms) if ctx._is_real_type(n) and ctx._is_real_type(z): v = ctx._re(v) return v """ Even/odd PCFs. Useful? @defun def pcfy1(ctx, a, z, **kwargs): a, _ = ctx._convert_param(n) z = ctx.convert(z) def h(): w = ctx.square_exp_arg(z) w1 = ctx.fmul(w, -0.25, exact=True) w2 = ctx.fmul(w, 0.5, exact=True) e = ctx.exp(w1) return [e], [1], [], [], [ctx.mpq_1_2*a+ctx.mpq_1_4], [ctx.mpq_1_2], w2 return ctx.hypercomb(h, [], **kwargs) @defun def pcfy2(ctx, a, z, **kwargs): a, _ = ctx._convert_param(n) z = ctx.convert(z) def h(): w = ctx.square_exp_arg(z) w1 = ctx.fmul(w, -0.25, exact=True) w2 = ctx.fmul(w, 0.5, exact=True) e = ctx.exp(w1) return [e, z], [1, 1], [], [], [ctx.mpq_1_2*a+ctx.mpq_3_4], \ [ctx.mpq_3_2], w2 return ctx.hypercomb(h, [], **kwargs) """ @defun_wrapped def gegenbauer(ctx, n, a, z, **kwargs): # Special cases: a+0.5, a*2 poles if ctx.isnpint(a): return 0*(z+n) if ctx.isnpint(a+0.5): # TODO: something else is required here # E.g.: gegenbauer(-2, -0.5, 3) == -12 if ctx.isnpint(n+1): raise NotImplementedError("Gegenbauer function with two limits") def h(a): a2 = 2*a T = [], [], [n+a2], [n+1, a2], [-n, n+a2], [a+0.5], 0.5*(1-z) return [T] return ctx.hypercomb(h, [a], **kwargs) def h(n): a2 = 2*a T = [], [], [n+a2], [n+1, a2], [-n, n+a2], [a+0.5], 0.5*(1-z) return [T] return ctx.hypercomb(h, [n], **kwargs) @defun_wrapped def jacobi(ctx, n, a, b, x, **kwargs): if not ctx.isnpint(a): def h(n): return (([], [], [a+n+1], [n+1, a+1], [-n, a+b+n+1], [a+1], (1-x)*0.5),) return ctx.hypercomb(h, [n], **kwargs) if not ctx.isint(b): def h(n, a): return (([], [], [-b], [n+1, -b-n], [-n, a+b+n+1], [b+1], (x+1)*0.5),) return ctx.hypercomb(h, [n, a], **kwargs) # XXX: determine appropriate limit return ctx.binomial(n+a,n) * ctx.hyp2f1(-n,1+n+a+b,a+1,(1-x)/2, **kwargs) @defun_wrapped def laguerre(ctx, n, a, z, **kwargs): # XXX: limits, poles #if ctx.isnpint(n): # return 0*(a+z) def h(a): return (([], [], [a+n+1], [a+1, n+1], [-n], [a+1], z),) return ctx.hypercomb(h, [a], **kwargs) @defun_wrapped def legendre(ctx, n, x, **kwargs): if ctx.isint(n): n = int(n) # Accuracy near zeros if (n + (n < 0)) & 1: if not x: return x mag = ctx.mag(x) if mag < -2*ctx.prec-10: return x if mag < -5: ctx.prec += -mag return ctx.hyp2f1(-n,n+1,1,(1-x)/2, **kwargs) @defun def legenp(ctx, n, m, z, type=2, **kwargs): # Legendre function, 1st kind n = ctx.convert(n) m = ctx.convert(m) # Faster if not m: return ctx.legendre(n, z, **kwargs) # TODO: correct evaluation at singularities if type == 2: def h(n,m): g = m*0.5 T = [1+z, 1-z], [g, -g], [], [1-m], [-n, n+1], [1-m], 0.5*(1-z) return (T,) return ctx.hypercomb(h, [n,m], **kwargs) if type == 3: def h(n,m): g = m*0.5 T = [z+1, z-1], [g, -g], [], [1-m], [-n, n+1], [1-m], 0.5*(1-z) return (T,) return ctx.hypercomb(h, [n,m], **kwargs) raise ValueError("requires type=2 or type=3") @defun def legenq(ctx, n, m, z, type=2, **kwargs): # Legendre function, 2nd kind n = ctx.convert(n) m = ctx.convert(m) z = ctx.convert(z) if z in (1, -1): #if ctx.isint(m): # return ctx.nan #return ctx.inf # unsigned return ctx.nan if type == 2: def h(n, m): cos, sin = ctx.cospi_sinpi(m) s = 2 * sin / ctx.pi c = cos a = 1+z b = 1-z u = m/2 w = (1-z)/2 T1 = [s, c, a, b], [-1, 1, u, -u], [], [1-m], \ [-n, n+1], [1-m], w T2 = [-s, a, b], [-1, -u, u], [n+m+1], [n-m+1, m+1], \ [-n, n+1], [m+1], w return T1, T2 return ctx.hypercomb(h, [n, m], **kwargs) if type == 3: # The following is faster when there only is a single series # Note: not valid for -1 < z < 0 (?) if abs(z) > 1: def h(n, m): T1 = [ctx.expjpi(m), 2, ctx.pi, z, z-1, z+1], \ [1, -n-1, 0.5, -n-m-1, 0.5*m, 0.5*m], \ [n+m+1], [n+1.5], \ [0.5*(2+n+m), 0.5*(1+n+m)], [n+1.5], z**(-2) return [T1] return ctx.hypercomb(h, [n, m], **kwargs) else: # not valid for 1 < z < inf ? def h(n, m): s = 2 * ctx.sinpi(m) / ctx.pi c = ctx.expjpi(m) a = 1+z b = z-1 u = m/2 w = (1-z)/2 T1 = [s, c, a, b], [-1, 1, u, -u], [], [1-m], \ [-n, n+1], [1-m], w T2 = [-s, c, a, b], [-1, 1, -u, u], [n+m+1], [n-m+1, m+1], \ [-n, n+1], [m+1], w return T1, T2 return ctx.hypercomb(h, [n, m], **kwargs) raise ValueError("requires type=2 or type=3") @defun_wrapped def chebyt(ctx, n, x, **kwargs): if (not x) and ctx.isint(n) and int(ctx._re(n)) % 2 == 1: return x * 0 return ctx.hyp2f1(-n,n,(1,2),(1-x)/2, **kwargs) @defun_wrapped def chebyu(ctx, n, x, **kwargs): if (not x) and ctx.isint(n) and int(ctx._re(n)) % 2 == 1: return x * 0 return (n+1) * ctx.hyp2f1(-n, n+2, (3,2), (1-x)/2, **kwargs) @defun def spherharm(ctx, l, m, theta, phi, **kwargs): l = ctx.convert(l) m = ctx.convert(m) theta = ctx.convert(theta) phi = ctx.convert(phi) l_isint = ctx.isint(l) l_natural = l_isint and l >= 0 m_isint = ctx.isint(m) if l_isint and l < 0 and m_isint: return ctx.spherharm(-(l+1), m, theta, phi, **kwargs) if theta == 0 and m_isint and m < 0: return ctx.zero * 1j if l_natural and m_isint: if abs(m) > l: return ctx.zero * 1j # http://functions.wolfram.com/Polynomials/ # SphericalHarmonicY/26/01/02/0004/ def h(l,m): absm = abs(m) C = [-1, ctx.expj(m*phi), (2*l+1)*ctx.fac(l+absm)/ctx.pi/ctx.fac(l-absm), ctx.sin(theta)**2, ctx.fac(absm), 2] P = [0.5*m*(ctx.sign(m)+1), 1, 0.5, 0.5*absm, -1, -absm-1] return ((C, P, [], [], [absm-l, l+absm+1], [absm+1], ctx.sin(0.5*theta)**2),) else: # http://functions.wolfram.com/HypergeometricFunctions/ # SphericalHarmonicYGeneral/26/01/02/0001/ def h(l,m): if ctx.isnpint(l-m+1) or ctx.isnpint(l+m+1) or ctx.isnpint(1-m): return (([0], [-1], [], [], [], [], 0),) cos, sin = ctx.cos_sin(0.5*theta) C = [0.5*ctx.expj(m*phi), (2*l+1)/ctx.pi, ctx.gamma(l-m+1), ctx.gamma(l+m+1), cos**2, sin**2] P = [1, 0.5, 0.5, -0.5, 0.5*m, -0.5*m] return ((C, P, [], [1-m], [-l,l+1], [1-m], sin**2),) return ctx.hypercomb(h, [l,m], **kwargs) sympy-0.7.4.1/sympy/mpmath/functions/expintegrals.py0000644000175000017500000002627412253362407023015 0ustar georgeskgeorgeskfrom .functions import defun, defun_wrapped @defun_wrapped def _erf_complex(ctx, z): z2 = ctx.square_exp_arg(z, -1) #z2 = -z**2 v = (2/ctx.sqrt(ctx.pi))*z * ctx.hyp1f1((1,2),(3,2), z2) if not ctx._re(z): v = ctx._im(v)*ctx.j return v @defun_wrapped def _erfc_complex(ctx, z): if ctx.re(z) > 2: z2 = ctx.square_exp_arg(z) nz2 = ctx.fneg(z2, exact=True) v = ctx.exp(nz2)/ctx.sqrt(ctx.pi) * ctx.hyperu((1,2),(1,2), z2) else: v = 1 - ctx._erf_complex(z) if not ctx._re(z): v = 1+ctx._im(v)*ctx.j return v @defun def erf(ctx, z): z = ctx.convert(z) if ctx._is_real_type(z): try: return ctx._erf(z) except NotImplementedError: pass if ctx._is_complex_type(z) and not z.imag: try: return type(z)(ctx._erf(z.real)) except NotImplementedError: pass return ctx._erf_complex(z) @defun def erfc(ctx, z): z = ctx.convert(z) if ctx._is_real_type(z): try: return ctx._erfc(z) except NotImplementedError: pass if ctx._is_complex_type(z) and not z.imag: try: return type(z)(ctx._erfc(z.real)) except NotImplementedError: pass return ctx._erfc_complex(z) @defun def square_exp_arg(ctx, z, mult=1, reciprocal=False): prec = ctx.prec*4+20 if reciprocal: z2 = ctx.fmul(z, z, prec=prec) z2 = ctx.fdiv(ctx.one, z2, prec=prec) else: z2 = ctx.fmul(z, z, prec=prec) if mult != 1: z2 = ctx.fmul(z2, mult, exact=True) return z2 @defun_wrapped def erfi(ctx, z): if not z: return z z2 = ctx.square_exp_arg(z) v = (2/ctx.sqrt(ctx.pi)*z) * ctx.hyp1f1((1,2), (3,2), z2) if not ctx._re(z): v = ctx._im(v)*ctx.j return v @defun_wrapped def erfinv(ctx, x): xre = ctx._re(x) if (xre != x) or (xre < -1) or (xre > 1): return ctx.bad_domain("erfinv(x) is defined only for -1 <= x <= 1") x = xre #if ctx.isnan(x): return x if not x: return x if x == 1: return ctx.inf if x == -1: return ctx.ninf if abs(x) < 0.9: a = 0.53728*x**3 + 0.813198*x else: # An asymptotic formula u = ctx.ln(2/ctx.pi/(abs(x)-1)**2) a = ctx.sign(x) * ctx.sqrt(u - ctx.ln(u))/ctx.sqrt(2) ctx.prec += 10 return ctx.findroot(lambda t: ctx.erf(t)-x, a) @defun_wrapped def npdf(ctx, x, mu=0, sigma=1): sigma = ctx.convert(sigma) return ctx.exp(-(x-mu)**2/(2*sigma**2)) / (sigma*ctx.sqrt(2*ctx.pi)) @defun_wrapped def ncdf(ctx, x, mu=0, sigma=1): a = (x-mu)/(sigma*ctx.sqrt(2)) if a < 0: return ctx.erfc(-a)/2 else: return (1+ctx.erf(a))/2 @defun_wrapped def betainc(ctx, a, b, x1=0, x2=1, regularized=False): if x1 == x2: v = 0 elif not x1: if x1 == 0 and x2 == 1: v = ctx.beta(a, b) else: v = x2**a * ctx.hyp2f1(a, 1-b, a+1, x2) / a else: m, d = ctx.nint_distance(a) if m <= 0: if d < -ctx.prec: h = +ctx.eps ctx.prec *= 2 a += h elif d < -4: ctx.prec -= d s1 = x2**a * ctx.hyp2f1(a,1-b,a+1,x2) s2 = x1**a * ctx.hyp2f1(a,1-b,a+1,x1) v = (s1 - s2) / a if regularized: v /= ctx.beta(a,b) return v @defun def gammainc(ctx, z, a=0, b=None, regularized=False): regularized = bool(regularized) z = ctx.convert(z) if a is None: a = ctx.zero lower_modified = False else: a = ctx.convert(a) lower_modified = a != ctx.zero if b is None: b = ctx.inf upper_modified = False else: b = ctx.convert(b) upper_modified = b != ctx.inf # Complete gamma function if not (upper_modified or lower_modified): if regularized: if ctx.re(z) < 0: return ctx.inf elif ctx.re(z) > 0: return ctx.one else: return ctx.nan return ctx.gamma(z) if a == b: return ctx.zero # Standardize if ctx.re(a) > ctx.re(b): return -ctx.gammainc(z, b, a, regularized) # Generalized gamma if upper_modified and lower_modified: return +ctx._gamma3(z, a, b, regularized) # Upper gamma elif lower_modified: return ctx._upper_gamma(z, a, regularized) # Lower gamma elif upper_modified: return ctx._lower_gamma(z, b, regularized) @defun def _lower_gamma(ctx, z, b, regularized=False): # Pole if ctx.isnpint(z): return type(z)(ctx.inf) G = [z] * regularized negb = ctx.fneg(b, exact=True) def h(z): T1 = [ctx.exp(negb), b, z], [1, z, -1], [], G, [1], [1+z], b return (T1,) return ctx.hypercomb(h, [z]) @defun def _upper_gamma(ctx, z, a, regularized=False): # Fast integer case, when available if ctx.isint(z): try: if regularized: # Gamma pole if ctx.isnpint(z): return type(z)(ctx.zero) orig = ctx.prec try: ctx.prec += 10 return ctx._gamma_upper_int(z, a) / ctx.gamma(z) finally: ctx.prec = orig else: return ctx._gamma_upper_int(z, a) except NotImplementedError: pass nega = ctx.fneg(a, exact=True) G = [z] * regularized # Use 2F0 series when possible; fall back to lower gamma representation try: def h(z): r = z-1 return [([ctx.exp(nega), a], [1, r], [], G, [1, -r], [], 1/nega)] return ctx.hypercomb(h, [z], force_series=True) except ctx.NoConvergence: def h(z): T1 = [], [1, z-1], [z], G, [], [], 0 T2 = [-ctx.exp(nega), a, z], [1, z, -1], [], G, [1], [1+z], a return T1, T2 return ctx.hypercomb(h, [z]) @defun def _gamma3(ctx, z, a, b, regularized=False): pole = ctx.isnpint(z) if regularized and pole: return ctx.zero try: ctx.prec += 15 # We don't know in advance whether it's better to write as a difference # of lower or upper gamma functions, so try both T1 = ctx.gammainc(z, a, regularized=regularized) T2 = ctx.gammainc(z, b, regularized=regularized) R = T1 - T2 if ctx.mag(R) - max(ctx.mag(T1), ctx.mag(T2)) > -10: return R if not pole: T1 = ctx.gammainc(z, 0, b, regularized=regularized) T2 = ctx.gammainc(z, 0, a, regularized=regularized) R = T1 - T2 # May be ok, but should probably at least print a warning # about possible cancellation if 1: #ctx.mag(R) - max(ctx.mag(T1), ctx.mag(T2)) > -10: return R finally: ctx.prec -= 15 raise NotImplementedError @defun_wrapped def expint(ctx, n, z): if ctx.isint(n) and ctx._is_real_type(z): try: return ctx._expint_int(n, z) except NotImplementedError: pass if ctx.isnan(n) or ctx.isnan(z): return z*n if z == ctx.inf: return 1/z if z == 0: # integral from 1 to infinity of t^n if ctx.re(n) <= 1: # TODO: reasonable sign of infinity return type(z)(ctx.inf) else: return ctx.one/(n-1) if n == 0: return ctx.exp(-z)/z if n == -1: return ctx.exp(-z)*(z+1)/z**2 return z**(n-1) * ctx.gammainc(1-n, z) @defun_wrapped def li(ctx, z, offset=False): if offset: if z == 2: return ctx.zero return ctx.ei(ctx.ln(z)) - ctx.ei(ctx.ln2) if not z: return z if z == 1: return ctx.ninf return ctx.ei(ctx.ln(z)) @defun def ei(ctx, z): try: return ctx._ei(z) except NotImplementedError: return ctx._ei_generic(z) @defun_wrapped def _ei_generic(ctx, z): # Note: the following is currently untested because mp and fp # both use special-case ei code if z == ctx.inf: return z if z == ctx.ninf: return ctx.zero if ctx.mag(z) > 1: try: r = ctx.one/z v = ctx.exp(z)*ctx.hyper([1,1],[],r, maxterms=ctx.prec, force_series=True)/z im = ctx._im(z) if im > 0: v += ctx.pi*ctx.j if im < 0: v -= ctx.pi*ctx.j return v except ctx.NoConvergence: pass v = z*ctx.hyp2f2(1,1,2,2,z) + ctx.euler if ctx._im(z): v += 0.5*(ctx.log(z) - ctx.log(ctx.one/z)) else: v += ctx.log(abs(z)) return v @defun def e1(ctx, z): try: return ctx._e1(z) except NotImplementedError: return ctx.expint(1, z) @defun def ci(ctx, z): try: return ctx._ci(z) except NotImplementedError: return ctx._ci_generic(z) @defun_wrapped def _ci_generic(ctx, z): if ctx.isinf(z): if z == ctx.inf: return ctx.zero if z == ctx.ninf: return ctx.pi*1j jz = ctx.fmul(ctx.j,z,exact=True) njz = ctx.fneg(jz,exact=True) v = 0.5*(ctx.ei(jz) + ctx.ei(njz)) zreal = ctx._re(z) zimag = ctx._im(z) if zreal == 0: if zimag > 0: v += ctx.pi*0.5j if zimag < 0: v -= ctx.pi*0.5j if zreal < 0: if zimag >= 0: v += ctx.pi*1j if zimag < 0: v -= ctx.pi*1j if ctx._is_real_type(z) and zreal > 0: v = ctx._re(v) return v @defun def si(ctx, z): try: return ctx._si(z) except NotImplementedError: return ctx._si_generic(z) @defun_wrapped def _si_generic(ctx, z): if ctx.isinf(z): if z == ctx.inf: return 0.5*ctx.pi if z == ctx.ninf: return -0.5*ctx.pi # Suffers from cancellation near 0 if ctx.mag(z) >= -1: jz = ctx.fmul(ctx.j,z,exact=True) njz = ctx.fneg(jz,exact=True) v = (-0.5j)*(ctx.ei(jz) - ctx.ei(njz)) zreal = ctx._re(z) if zreal > 0: v -= 0.5*ctx.pi if zreal < 0: v += 0.5*ctx.pi if ctx._is_real_type(z): v = ctx._re(v) return v else: return z*ctx.hyp1f2((1,2),(3,2),(3,2),-0.25*z*z) @defun_wrapped def chi(ctx, z): nz = ctx.fneg(z, exact=True) v = 0.5*(ctx.ei(z) + ctx.ei(nz)) zreal = ctx._re(z) zimag = ctx._im(z) if zimag > 0: v += ctx.pi*0.5j elif zimag < 0: v -= ctx.pi*0.5j elif zreal < 0: v += ctx.pi*1j return v @defun_wrapped def shi(ctx, z): # Suffers from cancellation near 0 if ctx.mag(z) >= -1: nz = ctx.fneg(z, exact=True) v = 0.5*(ctx.ei(z) - ctx.ei(nz)) zimag = ctx._im(z) if zimag > 0: v -= 0.5j*ctx.pi if zimag < 0: v += 0.5j*ctx.pi return v else: return z * ctx.hyp1f2((1,2),(3,2),(3,2),0.25*z*z) @defun_wrapped def fresnels(ctx, z): if z == ctx.inf: return ctx.mpf(0.5) if z == ctx.ninf: return ctx.mpf(-0.5) return ctx.pi*z**3/6*ctx.hyp1f2((3,4),(3,2),(7,4),-ctx.pi**2*z**4/16) @defun_wrapped def fresnelc(ctx, z): if z == ctx.inf: return ctx.mpf(0.5) if z == ctx.ninf: return ctx.mpf(-0.5) return z*ctx.hyp1f2((1,4),(1,2),(5,4),-ctx.pi**2*z**4/16) sympy-0.7.4.1/sympy/mpmath/functions/elliptic.py0000644000175000017500000011324412253362407022107 0ustar georgeskgeorgeskr""" Elliptic functions historically comprise the elliptic integrals and their inverses, and originate from the problem of computing the arc length of an ellipse. From a more modern point of view, an elliptic function is defined as a doubly periodic function, i.e. a function which satisfies .. math :: f(z + 2 \omega_1) = f(z + 2 \omega_2) = f(z) for some half-periods `\omega_1, \omega_2` with `\mathrm{Im}[\omega_1 / \omega_2] > 0`. The canonical elliptic functions are the Jacobi elliptic functions. More broadly, this section includes quasi-doubly periodic functions (such as the Jacobi theta functions) and other functions useful in the study of elliptic functions. Many different conventions for the arguments of elliptic functions are in use. It is even standard to use different parameterizations for different functions in the same text or software (and mpmath is no exception). The usual parameters are the elliptic nome `q`, which usually must satisfy `|q| < 1`; the elliptic parameter `m` (an arbitrary complex number); the elliptic modulus `k` (an arbitrary complex number); and the half-period ratio `\tau`, which usually must satisfy `\mathrm{Im}[\tau] > 0`. These quantities can be expressed in terms of each other using the following relations: .. math :: m = k^2 .. math :: \tau = -i \frac{K(1-m)}{K(m)} .. math :: q = e^{i \pi \tau} .. math :: k = \frac{\vartheta_2^4(q)}{\vartheta_3^4(q)} In addition, an alternative definition is used for the nome in number theory, which we here denote by q-bar: .. math :: \bar{q} = q^2 = e^{2 i \pi \tau} For convenience, mpmath provides functions to convert between the various parameters (:func:`~mpmath.qfrom`, :func:`~mpmath.mfrom`, :func:`~mpmath.kfrom`, :func:`~mpmath.taufrom`, :func:`~mpmath.qbarfrom`). **References** 1. [AbramowitzStegun]_ 2. [WhittakerWatson]_ """ from .functions import defun, defun_wrapped def nome(ctx, m): m = ctx.convert(m) if not m: return m if m == ctx.one: return m if ctx.isnan(m): return m if ctx.isinf(m): if m == ctx.ninf: return type(m)(-1) else: return ctx.mpc(-1) a = ctx.ellipk(ctx.one-m) b = ctx.ellipk(m) v = ctx.exp(-ctx.pi*a/b) if not ctx._im(m) and ctx._re(m) < 1: if ctx._is_real_type(m): return v.real else: return v.real + 0j elif m == 2: v = ctx.mpc(0, v.imag) return v @defun_wrapped def qfrom(ctx, q=None, m=None, k=None, tau=None, qbar=None): r""" Returns the elliptic nome `q`, given any of `q, m, k, \tau, \bar{q}`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> qfrom(q=0.25) 0.25 >>> qfrom(m=mfrom(q=0.25)) 0.25 >>> qfrom(k=kfrom(q=0.25)) 0.25 >>> qfrom(tau=taufrom(q=0.25)) (0.25 + 0.0j) >>> qfrom(qbar=qbarfrom(q=0.25)) 0.25 """ if q is not None: return ctx.convert(q) if m is not None: return nome(ctx, m) if k is not None: return nome(ctx, ctx.convert(k)**2) if tau is not None: return ctx.expjpi(tau) if qbar is not None: return ctx.sqrt(qbar) @defun_wrapped def qbarfrom(ctx, q=None, m=None, k=None, tau=None, qbar=None): r""" Returns the number-theoretic nome `\bar q`, given any of `q, m, k, \tau, \bar{q}`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> qbarfrom(qbar=0.25) 0.25 >>> qbarfrom(q=qfrom(qbar=0.25)) 0.25 >>> qbarfrom(m=extraprec(20)(mfrom)(qbar=0.25)) # ill-conditioned 0.25 >>> qbarfrom(k=extraprec(20)(kfrom)(qbar=0.25)) # ill-conditioned 0.25 >>> qbarfrom(tau=taufrom(qbar=0.25)) (0.25 + 0.0j) """ if qbar is not None: return ctx.convert(qbar) if q is not None: return ctx.convert(q) ** 2 if m is not None: return nome(ctx, m) ** 2 if k is not None: return nome(ctx, ctx.convert(k)**2) ** 2 if tau is not None: return ctx.expjpi(2*tau) @defun_wrapped def taufrom(ctx, q=None, m=None, k=None, tau=None, qbar=None): r""" Returns the elliptic half-period ratio `\tau`, given any of `q, m, k, \tau, \bar{q}`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> taufrom(tau=0.5j) (0.0 + 0.5j) >>> taufrom(q=qfrom(tau=0.5j)) (0.0 + 0.5j) >>> taufrom(m=mfrom(tau=0.5j)) (0.0 + 0.5j) >>> taufrom(k=kfrom(tau=0.5j)) (0.0 + 0.5j) >>> taufrom(qbar=qbarfrom(tau=0.5j)) (0.0 + 0.5j) """ if tau is not None: return ctx.convert(tau) if m is not None: m = ctx.convert(m) return ctx.j*ctx.ellipk(1-m)/ctx.ellipk(m) if k is not None: k = ctx.convert(k) return ctx.j*ctx.ellipk(1-k**2)/ctx.ellipk(k**2) if q is not None: return ctx.log(q) / (ctx.pi*ctx.j) if qbar is not None: qbar = ctx.convert(qbar) return ctx.log(qbar) / (2*ctx.pi*ctx.j) @defun_wrapped def kfrom(ctx, q=None, m=None, k=None, tau=None, qbar=None): r""" Returns the elliptic modulus `k`, given any of `q, m, k, \tau, \bar{q}`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> kfrom(k=0.25) 0.25 >>> kfrom(m=mfrom(k=0.25)) 0.25 >>> kfrom(q=qfrom(k=0.25)) 0.25 >>> kfrom(tau=taufrom(k=0.25)) (0.25 + 0.0j) >>> kfrom(qbar=qbarfrom(k=0.25)) 0.25 As `q \to 1` and `q \to -1`, `k` rapidly approaches `1` and `i \infty` respectively:: >>> kfrom(q=0.75) 0.9999999999999899166471767 >>> kfrom(q=-0.75) (0.0 + 7041781.096692038332790615j) >>> kfrom(q=1) 1 >>> kfrom(q=-1) (0.0 + +infj) """ if k is not None: return ctx.convert(k) if m is not None: return ctx.sqrt(m) if tau is not None: q = ctx.expjpi(tau) if qbar is not None: q = ctx.sqrt(qbar) if q == 1: return q if q == -1: return ctx.mpc(0,'inf') return (ctx.jtheta(2,0,q)/ctx.jtheta(3,0,q))**2 @defun_wrapped def mfrom(ctx, q=None, m=None, k=None, tau=None, qbar=None): r""" Returns the elliptic parameter `m`, given any of `q, m, k, \tau, \bar{q}`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> mfrom(m=0.25) 0.25 >>> mfrom(q=qfrom(m=0.25)) 0.25 >>> mfrom(k=kfrom(m=0.25)) 0.25 >>> mfrom(tau=taufrom(m=0.25)) (0.25 + 0.0j) >>> mfrom(qbar=qbarfrom(m=0.25)) 0.25 As `q \to 1` and `q \to -1`, `m` rapidly approaches `1` and `-\infty` respectively:: >>> mfrom(q=0.75) 0.9999999999999798332943533 >>> mfrom(q=-0.75) -49586681013729.32611558353 >>> mfrom(q=1) 1.0 >>> mfrom(q=-1) -inf The inverse nome as a function of `q` has an integer Taylor series expansion:: >>> taylor(lambda q: mfrom(q), 0, 7) [0.0, 16.0, -128.0, 704.0, -3072.0, 11488.0, -38400.0, 117632.0] """ if m is not None: return m if k is not None: return k**2 if tau is not None: q = ctx.expjpi(tau) if qbar is not None: q = ctx.sqrt(qbar) if q == 1: return ctx.convert(q) if q == -1: return q*ctx.inf v = (ctx.jtheta(2,0,q)/ctx.jtheta(3,0,q))**4 if ctx._is_real_type(q) and q < 0: v = v.real return v jacobi_spec = { 'sn' : ([3],[2],[1],[4], 'sin', 'tanh'), 'cn' : ([4],[2],[2],[4], 'cos', 'sech'), 'dn' : ([4],[3],[3],[4], '1', 'sech'), 'ns' : ([2],[3],[4],[1], 'csc', 'coth'), 'nc' : ([2],[4],[4],[2], 'sec', 'cosh'), 'nd' : ([3],[4],[4],[3], '1', 'cosh'), 'sc' : ([3],[4],[1],[2], 'tan', 'sinh'), 'sd' : ([3,3],[2,4],[1],[3], 'sin', 'sinh'), 'cd' : ([3],[2],[2],[3], 'cos', '1'), 'cs' : ([4],[3],[2],[1], 'cot', 'csch'), 'dc' : ([2],[3],[3],[2], 'sec', '1'), 'ds' : ([2,4],[3,3],[3],[1], 'csc', 'csch'), 'cc' : None, 'ss' : None, 'nn' : None, 'dd' : None } @defun def ellipfun(ctx, kind, u=None, m=None, q=None, k=None, tau=None): try: S = jacobi_spec[kind] except KeyError: raise ValueError("First argument must be a two-character string " "containing 's', 'c', 'd' or 'n', e.g.: 'sn'") if u is None: def f(*args, **kwargs): return ctx.ellipfun(kind, *args, **kwargs) f.__name__ = kind return f prec = ctx.prec try: ctx.prec += 10 u = ctx.convert(u) q = ctx.qfrom(m=m, q=q, k=k, tau=tau) if S is None: v = ctx.one + 0*q*u elif q == ctx.zero: if S[4] == '1': v = ctx.one else: v = getattr(ctx, S[4])(u) v += 0*q*u elif q == ctx.one: if S[5] == '1': v = ctx.one else: v = getattr(ctx, S[5])(u) v += 0*q*u else: t = u / ctx.jtheta(3, 0, q)**2 v = ctx.one for a in S[0]: v *= ctx.jtheta(a, 0, q) for b in S[1]: v /= ctx.jtheta(b, 0, q) for c in S[2]: v *= ctx.jtheta(c, t, q) for d in S[3]: v /= ctx.jtheta(d, t, q) finally: ctx.prec = prec return +v @defun_wrapped def kleinj(ctx, tau=None, **kwargs): r""" Evaluates the Klein j-invariant, which is a modular function defined for `\tau` in the upper half-plane as .. math :: J(\tau) = \frac{g_2^3(\tau)}{g_2^3(\tau) - 27 g_3^2(\tau)} where `g_2` and `g_3` are the modular invariants of the Weierstrass elliptic function, .. math :: g_2(\tau) = 60 \sum_{(m,n) \in \mathbb{Z}^2 \setminus (0,0)} (m \tau+n)^{-4} g_3(\tau) = 140 \sum_{(m,n) \in \mathbb{Z}^2 \setminus (0,0)} (m \tau+n)^{-6}. An alternative, common notation is that of the j-function `j(\tau) = 1728 J(\tau)`. **Plots** .. literalinclude :: /modules/mpmath/plots/kleinj.py .. image :: /modules/mpmath/plots/kleinj.png .. literalinclude :: /modules/mpmath/plots/kleinj2.py .. image :: /modules/mpmath/plots/kleinj2.png **Examples** Verifying the functional equation `J(\tau) = J(\tau+1) = J(-\tau^{-1})`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> tau = 0.625+0.75*j >>> tau = 0.625+0.75*j >>> kleinj(tau) (-0.1507492166511182267125242 + 0.07595948379084571927228948j) >>> kleinj(tau+1) (-0.1507492166511182267125242 + 0.07595948379084571927228948j) >>> kleinj(-1/tau) (-0.1507492166511182267125242 + 0.07595948379084571927228946j) The j-function has a famous Laurent series expansion in terms of the nome `\bar{q}`, `j(\tau) = \bar{q}^{-1} + 744 + 196884\bar{q} + \ldots`:: >>> mp.dps = 15 >>> taylor(lambda q: 1728*q*kleinj(qbar=q), 0, 5, singular=True) [1.0, 744.0, 196884.0, 21493760.0, 864299970.0, 20245856256.0] The j-function admits exact evaluation at special algebraic points related to the Heegner numbers 1, 2, 3, 7, 11, 19, 43, 67, 163:: >>> @extraprec(10) ... def h(n): ... v = (1+sqrt(n)*j) ... if n > 2: ... v *= 0.5 ... return v ... >>> mp.dps = 25 >>> for n in [1,2,3,7,11,19,43,67,163]: ... n, chop(1728*kleinj(h(n))) ... (1, 1728.0) (2, 8000.0) (3, 0.0) (7, -3375.0) (11, -32768.0) (19, -884736.0) (43, -884736000.0) (67, -147197952000.0) (163, -262537412640768000.0) Also at other special points, the j-function assumes explicit algebraic values, e.g.:: >>> chop(1728*kleinj(j*sqrt(5))) 1264538.909475140509320227 >>> identify(cbrt(_)) # note: not simplified '((100+sqrt(13520))/2)' >>> (50+26*sqrt(5))**3 1264538.909475140509320227 """ q = ctx.qfrom(tau=tau, **kwargs) t2 = ctx.jtheta(2,0,q) t3 = ctx.jtheta(3,0,q) t4 = ctx.jtheta(4,0,q) P = (t2**8 + t3**8 + t4**8)**3 Q = 54*(t2*t3*t4)**8 return P/Q def RF_calc(ctx, x, y, z, r): if y == z: return RC_calc(ctx, x, y, r) if x == z: return RC_calc(ctx, y, x, r) if x == y: return RC_calc(ctx, z, x, r) if not (ctx.isnormal(x) and ctx.isnormal(y) and ctx.isnormal(z)): if ctx.isnan(x) or ctx.isnan(y) or ctx.isnan(z): return x*y*z if ctx.isinf(x) or ctx.isinf(y) or ctx.isinf(z): return ctx.zero xm,ym,zm = x,y,z A0 = Am = (x+y+z)/3 Q = ctx.root(3*r, -6) * max(abs(A0-x),abs(A0-y),abs(A0-z)) g = ctx.mpf(0.25) pow4 = ctx.one m = 0 while 1: xs = ctx.sqrt(xm) ys = ctx.sqrt(ym) zs = ctx.sqrt(zm) lm = xs*ys + xs*zs + ys*zs Am1 = (Am+lm)*g xm, ym, zm = (xm+lm)*g, (ym+lm)*g, (zm+lm)*g if pow4 * Q < abs(Am): break Am = Am1 m += 1 pow4 *= g t = pow4/Am X = (A0-x)*t Y = (A0-y)*t Z = -X-Y E2 = X*Y-Z**2 E3 = X*Y*Z return ctx.power(Am,-0.5) * (9240-924*E2+385*E2**2+660*E3-630*E2*E3)/9240 def RC_calc(ctx, x, y, r, pv=True): if not (ctx.isnormal(x) and ctx.isnormal(y)): if ctx.isinf(x) or ctx.isinf(y): return 1/(x*y) if y == 0: return ctx.inf if x == 0: return ctx.pi / ctx.sqrt(y) / 2 raise ValueError # Cauchy principal value if pv and ctx._im(y) == 0 and ctx._re(y) < 0: return ctx.sqrt(x/(x-y)) * RC_calc(ctx, x-y, -y, r) if x == y: return 1/ctx.sqrt(x) extraprec = 2*max(0,-ctx.mag(x-y)+ctx.mag(x)) ctx.prec += extraprec if ctx._is_real_type(x) and ctx._is_real_type(y): x = ctx._re(x) y = ctx._re(y) a = ctx.sqrt(x/y) if x < y: b = ctx.sqrt(y-x) v = ctx.acos(a)/b else: b = ctx.sqrt(x-y) v = ctx.acosh(a)/b else: sx = ctx.sqrt(x) sy = ctx.sqrt(y) v = ctx.acos(sx/sy)/(ctx.sqrt((1-x/y))*sy) ctx.prec -= extraprec return v def RJ_calc(ctx, x, y, z, p, r): if not (ctx.isnormal(x) and ctx.isnormal(y) and \ ctx.isnormal(z) and ctx.isnormal(p)): if ctx.isnan(x) or ctx.isnan(y) or ctx.isnan(z) or ctx.isnan(p): return x*y*z if ctx.isinf(x) or ctx.isinf(y) or ctx.isinf(z) or ctx.isinf(p): return ctx.zero if not p: return ctx.inf xm,ym,zm,pm = x,y,z,p A0 = Am = (x + y + z + 2*p)/5 delta = (p-x)*(p-y)*(p-z) Q = ctx.root(0.25*r, -6) * max(abs(A0-x),abs(A0-y),abs(A0-z),abs(A0-p)) m = 0 g = ctx.mpf(0.25) pow4 = ctx.one S = 0 while 1: sx = ctx.sqrt(xm) sy = ctx.sqrt(ym) sz = ctx.sqrt(zm) sp = ctx.sqrt(pm) lm = sx*sy + sx*sz + sy*sz Am1 = (Am+lm)*g xm = (xm+lm)*g ym = (ym+lm)*g zm = (zm+lm)*g pm = (pm+lm)*g dm = (sp+sx) * (sp+sy) * (sp+sz) em = delta * ctx.power(4, -3*m) / dm**2 if pow4 * Q < abs(Am): break T = RC_calc(ctx, ctx.one, ctx.one+em, r) * pow4 / dm S += T pow4 *= g m += 1 Am = Am1 t = ctx.ldexp(1,-2*m) / Am X = (A0-x)*t Y = (A0-y)*t Z = (A0-z)*t P = (-X-Y-Z)/2 E2 = X*Y + X*Z + Y*Z - 3*P**2 E3 = X*Y*Z + 2*E2*P + 4*P**3 E4 = (2*X*Y*Z + E2*P + 3*P**3)*P E5 = X*Y*Z*P**2 P = 24024 - 5148*E2 + 2457*E2**2 + 4004*E3 - 4158*E2*E3 - 3276*E4 + 2772*E5 Q = 24024 v1 = g**m * ctx.power(Am, -1.5) * P/Q v2 = 6*S return v1 + v2 @defun def elliprf(ctx, x, y, z): r""" Evaluates the Carlson symmetric elliptic integral of the first kind .. math :: R_F(x,y,z) = \frac{1}{2} \int_0^{\infty} \frac{dt}{\sqrt{(t+x)(t+y)(t+z)}} which is defined for `x,y,z \notin (-\infty,0)`, and with at most one of `x,y,z` being zero. For real `x,y,z \ge 0`, the principal square root is taken in the integrand. For complex `x,y,z`, the principal square root is taken as `t \to \infty` and as `t \to 0` non-principal branches are chosen as necessary so as to make the integrand continuous. **Examples** Some basic values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> elliprf(0,1,1); pi/2 1.570796326794896619231322 1.570796326794896619231322 >>> elliprf(0,1,inf) 0.0 >>> elliprf(1,1,1) 1.0 >>> elliprf(2,2,2)**2 0.5 >>> elliprf(1,0,0); elliprf(0,0,1); elliprf(0,1,0); elliprf(0,0,0) +inf +inf +inf +inf Representing complete elliptic integrals in terms of `R_F`:: >>> m = mpf(0.75) >>> ellipk(m); elliprf(0,1-m,1) 2.156515647499643235438675 2.156515647499643235438675 >>> ellipe(m); elliprf(0,1-m,1)-m*elliprd(0,1-m,1)/3 1.211056027568459524803563 1.211056027568459524803563 Some symmetries and argument transformations:: >>> x,y,z = 2,3,4 >>> elliprf(x,y,z); elliprf(y,x,z); elliprf(z,y,x) 0.5840828416771517066928492 0.5840828416771517066928492 0.5840828416771517066928492 >>> k = mpf(100000) >>> elliprf(k*x,k*y,k*z); k**(-0.5) * elliprf(x,y,z) 0.001847032121923321253219284 0.001847032121923321253219284 >>> l = sqrt(x*y) + sqrt(y*z) + sqrt(z*x) >>> elliprf(x,y,z); 2*elliprf(x+l,y+l,z+l) 0.5840828416771517066928492 0.5840828416771517066928492 >>> elliprf((x+l)/4,(y+l)/4,(z+l)/4) 0.5840828416771517066928492 Comparing with numerical integration:: >>> x,y,z = 2,3,4 >>> elliprf(x,y,z) 0.5840828416771517066928492 >>> f = lambda t: 0.5*((t+x)*(t+y)*(t+z))**(-0.5) >>> q = extradps(25)(quad) >>> q(f, [0,inf]) 0.5840828416771517066928492 With the following arguments, the square root in the integrand becomes discontinuous at `t = 1/2` if the principal branch is used. To obtain the right value, `-\sqrt{r}` must be taken instead of `\sqrt{r}` on `t \in (0, 1/2)`:: >>> x,y,z = j-1,j,0 >>> elliprf(x,y,z) (0.7961258658423391329305694 - 1.213856669836495986430094j) >>> -q(f, [0,0.5]) + q(f, [0.5,inf]) (0.7961258658423391329305694 - 1.213856669836495986430094j) The so-called *first lemniscate constant*, a transcendental number:: >>> elliprf(0,1,2) 1.31102877714605990523242 >>> extradps(25)(quad)(lambda t: 1/sqrt(1-t**4), [0,1]) 1.31102877714605990523242 >>> gamma('1/4')**2/(4*sqrt(2*pi)) 1.31102877714605990523242 **References** 1. [Carlson]_ 2. [DLMF]_ Chapter 19. Elliptic Integrals """ x = ctx.convert(x) y = ctx.convert(y) z = ctx.convert(z) prec = ctx.prec try: ctx.prec += 20 tol = ctx.eps * 2**10 v = RF_calc(ctx, x, y, z, tol) finally: ctx.prec = prec return +v @defun def elliprc(ctx, x, y, pv=True): r""" Evaluates the degenerate Carlson symmetric elliptic integral of the first kind .. math :: R_C(x,y) = R_F(x,y,y) = \frac{1}{2} \int_0^{\infty} \frac{dt}{(t+y) \sqrt{(t+x)}}. If `y \in (-\infty,0)`, either a value defined by continuity, or with *pv=True* the Cauchy principal value, can be computed. If `x \ge 0, y > 0`, the value can be expressed in terms of elementary functions as .. math :: R_C(x,y) = \begin{cases} \dfrac{1}{\sqrt{y-x}} \cos^{-1}\left(\sqrt{\dfrac{x}{y}}\right), & x < y \\ \dfrac{1}{\sqrt{y}}, & x = y \\ \dfrac{1}{\sqrt{x-y}} \cosh^{-1}\left(\sqrt{\dfrac{x}{y}}\right), & x > y \\ \end{cases}. **Examples** Some special values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> elliprc(1,2)*4; elliprc(0,1)*2; +pi 3.141592653589793238462643 3.141592653589793238462643 3.141592653589793238462643 >>> elliprc(1,0) +inf >>> elliprc(5,5)**2 0.2 >>> elliprc(1,inf); elliprc(inf,1); elliprc(inf,inf) 0.0 0.0 0.0 Comparing with the elementary closed-form solution:: >>> elliprc('1/3', '1/5'); sqrt(7.5)*acosh(sqrt('5/3')) 2.041630778983498390751238 2.041630778983498390751238 >>> elliprc('1/5', '1/3'); sqrt(7.5)*acos(sqrt('3/5')) 1.875180765206547065111085 1.875180765206547065111085 Comparing with numerical integration:: >>> q = extradps(25)(quad) >>> elliprc(2, -3, pv=True) 0.3333969101113672670749334 >>> elliprc(2, -3, pv=False) (0.3333969101113672670749334 + 0.7024814731040726393156375j) >>> 0.5*q(lambda t: 1/(sqrt(t+2)*(t-3)), [0,3-j,6,inf]) (0.3333969101113672670749334 + 0.7024814731040726393156375j) """ x = ctx.convert(x) y = ctx.convert(y) prec = ctx.prec try: ctx.prec += 20 tol = ctx.eps * 2**10 v = RC_calc(ctx, x, y, tol, pv) finally: ctx.prec = prec return +v @defun def elliprj(ctx, x, y, z, p): r""" Evaluates the Carlson symmetric elliptic integral of the third kind .. math :: R_J(x,y,z,p) = \frac{3}{2} \int_0^{\infty} \frac{dt}{(t+p)\sqrt{(t+x)(t+y)(t+z)}}. Like :func:`~mpmath.elliprf`, the branch of the square root in the integrand is defined so as to be continuous along the path of integration for complex values of the arguments. **Examples** Some values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> elliprj(1,1,1,1) 1.0 >>> elliprj(2,2,2,2); 1/(2*sqrt(2)) 0.3535533905932737622004222 0.3535533905932737622004222 >>> elliprj(0,1,2,2) 1.067937989667395702268688 >>> 3*(2*gamma('5/4')**2-pi**2/gamma('1/4')**2)/(sqrt(2*pi)) 1.067937989667395702268688 >>> elliprj(0,1,1,2); 3*pi*(2-sqrt(2))/4 1.380226776765915172432054 1.380226776765915172432054 >>> elliprj(1,3,2,0); elliprj(0,1,1,0); elliprj(0,0,0,0) +inf +inf +inf >>> elliprj(1,inf,1,0); elliprj(1,1,1,inf) 0.0 0.0 >>> chop(elliprj(1+j, 1-j, 1, 1)) 0.8505007163686739432927844 Scale transformation:: >>> x,y,z,p = 2,3,4,5 >>> k = mpf(100000) >>> elliprj(k*x,k*y,k*z,k*p); k**(-1.5)*elliprj(x,y,z,p) 4.521291677592745527851168e-9 4.521291677592745527851168e-9 Comparing with numerical integration:: >>> elliprj(1,2,3,4) 0.2398480997495677621758617 >>> f = lambda t: 1/((t+4)*sqrt((t+1)*(t+2)*(t+3))) >>> 1.5*quad(f, [0,inf]) 0.2398480997495677621758617 >>> elliprj(1,2+1j,3,4-2j) (0.216888906014633498739952 + 0.04081912627366673332369512j) >>> f = lambda t: 1/((t+4-2j)*sqrt((t+1)*(t+2+1j)*(t+3))) >>> 1.5*quad(f, [0,inf]) (0.216888906014633498739952 + 0.04081912627366673332369511j) """ x = ctx.convert(x) y = ctx.convert(y) z = ctx.convert(z) p = ctx.convert(p) prec = ctx.prec try: ctx.prec += 20 tol = ctx.eps * 2**10 v = RJ_calc(ctx, x, y, z, p, tol) finally: ctx.prec = prec return +v @defun def elliprd(ctx, x, y, z): r""" Evaluates the degenerate Carlson symmetric elliptic integral of the third kind or Carlson elliptic integral of the second kind `R_D(x,y,z) = R_J(x,y,z,z)`. See :func:`~mpmath.elliprj` for additional information. **Examples** >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> elliprd(1,2,3) 0.2904602810289906442326534 >>> elliprj(1,2,3,3) 0.2904602810289906442326534 The so-called *second lemniscate constant*, a transcendental number:: >>> elliprd(0,2,1)/3 0.5990701173677961037199612 >>> extradps(25)(quad)(lambda t: t**2/sqrt(1-t**4), [0,1]) 0.5990701173677961037199612 >>> gamma('3/4')**2/sqrt(2*pi) 0.5990701173677961037199612 """ return ctx.elliprj(x,y,z,z) @defun def elliprg(ctx, x, y, z): r""" Evaluates the Carlson completely symmetric elliptic integral of the second kind .. math :: R_G(x,y,z) = \frac{1}{4} \int_0^{\infty} \frac{t}{\sqrt{(t+x)(t+y)(t+z)}} \left( \frac{x}{t+x} + \frac{y}{t+y} + \frac{z}{t+z}\right) dt. **Examples** Evaluation for real and complex arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> elliprg(0,1,1)*4; +pi 3.141592653589793238462643 3.141592653589793238462643 >>> elliprg(0,0.5,1) 0.6753219405238377512600874 >>> chop(elliprg(1+j, 1-j, 2)) 1.172431327676416604532822 A double integral that can be evaluated in terms of `R_G`:: >>> x,y,z = 2,3,4 >>> def f(t,u): ... st = fp.sin(t); ct = fp.cos(t) ... su = fp.sin(u); cu = fp.cos(u) ... return (x*(st*cu)**2 + y*(st*su)**2 + z*ct**2)**0.5 * st ... >>> nprint(mpf(fp.quad(f, [0,fp.pi], [0,2*fp.pi])/(4*fp.pi)), 13) 1.725503028069 >>> nprint(elliprg(x,y,z), 13) 1.725503028069 """ x = ctx.convert(x) y = ctx.convert(y) z = ctx.convert(z) if not z: x, z = z, x if not z: y, z = x, y if not z: return ctx.inf def terms(): T1 = 0.5*z*ctx.elliprf(x,y,z) T2 = -0.5*(x-z)*(y-z)*ctx.elliprd(x,y,z)/3 T3 = 0.5*ctx.sqrt(x*y/z) return T1,T2,T3 return ctx.sum_accurately(terms) @defun_wrapped def ellipf(ctx, phi, m): r""" Evaluates the Legendre incomplete elliptic integral of the first kind .. math :: F(\phi,m) = \int_0^{\phi} \frac{dt}{\sqrt{1-m \sin^2 t}} or equivalently .. math :: F(\phi,m) = \int_0^{\sin z} \frac{dt}{\left(\sqrt{1-t^2}\right)\left(\sqrt{1-mt^2}\right)}. The function reduces to a complete elliptic integral of the first kind (see :func:`~mpmath.ellipk`) when `\phi = \frac{\pi}{2}`; that is, .. math :: F\left(\frac{\pi}{2}, m\right) = K(m). In the defining integral, it is assumed that the principal branch of the square root is taken and that the path of integration avoids crossing any branch cuts. Outside `-\pi/2 \le \Re(z) \le \pi/2`, the function extends quasi-periodically as .. math :: F(\phi + n \pi, m) = 2 n K(m) + F(\phi,m), n \in \mathbb{Z}. **Plots** .. literalinclude :: /modules/mpmath/plots/ellipf.py .. image :: /modules/mpmath/plots/ellipf.png **Examples** Basic values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> ellipf(0,1) 0.0 >>> ellipf(0,0) 0.0 >>> ellipf(1,0); ellipf(2+3j,0) 1.0 (2.0 + 3.0j) >>> ellipf(1,1); log(sec(1)+tan(1)) 1.226191170883517070813061 1.226191170883517070813061 >>> ellipf(pi/2, -0.5); ellipk(-0.5) 1.415737208425956198892166 1.415737208425956198892166 >>> ellipf(pi/2+eps, 1); ellipf(-pi/2-eps, 1) +inf +inf >>> ellipf(1.5, 1) 3.340677542798311003320813 Comparing with numerical integration:: >>> z,m = 0.5, 1.25 >>> ellipf(z,m) 0.5287219202206327872978255 >>> quad(lambda t: (1-m*sin(t)**2)**(-0.5), [0,z]) 0.5287219202206327872978255 The arguments may be complex numbers:: >>> ellipf(3j, 0.5) (0.0 + 1.713602407841590234804143j) >>> ellipf(3+4j, 5-6j) (1.269131241950351323305741 - 0.3561052815014558335412538j) >>> z,m = 2+3j, 1.25 >>> k = 1011 >>> ellipf(z+pi*k,m); ellipf(z,m) + 2*k*ellipk(m) (4086.184383622179764082821 - 3003.003538923749396546871j) (4086.184383622179764082821 - 3003.003538923749396546871j) For `|\Re(z)| < \pi/2`, the function can be expressed as a hypergeometric series of two variables (see :func:`~mpmath.appellf1`):: >>> z,m = 0.5, 0.25 >>> ellipf(z,m) 0.5050887275786480788831083 >>> sin(z)*appellf1(0.5,0.5,0.5,1.5,sin(z)**2,m*sin(z)**2) 0.5050887275786480788831083 """ z = phi if not (ctx.isnormal(z) and ctx.isnormal(m)): if m == 0: return z + m if z == 0: return z * m if m == ctx.inf or m == ctx.ninf: return z/m raise ValueError x = z.real ctx.prec += max(0, ctx.mag(x)) pi = +ctx.pi away = abs(x) > pi/2 if m == 1: if away: return ctx.inf if away: d = ctx.nint(x/pi) z = z-pi*d P = 2*d*ctx.ellipk(m) else: P = 0 c, s = ctx.cos_sin(z) return s * ctx.elliprf(c**2, 1-m*s**2, 1) + P @defun_wrapped def ellipe(ctx, *args): r""" Called with a single argument `m`, evaluates the Legendre complete elliptic integral of the second kind, `E(m)`, defined by .. math :: E(m) = \int_0^{\pi/2} \sqrt{1-m \sin^2 t} \, dt \,=\, \frac{\pi}{2} \,_2F_1\left(\frac{1}{2}, -\frac{1}{2}, 1, m\right). Called with two arguments `\phi, m`, evaluates the incomplete elliptic integral of the second kind .. math :: E(\phi,m) = \int_0^{\phi} \sqrt{1-m \sin^2 t} \, dt = \int_0^{\sin z} \frac{\sqrt{1-mt^2}}{\sqrt{1-t^2}} \, dt. The incomplete integral reduces to a complete integral when `\phi = \frac{\pi}{2}`; that is, .. math :: E\left(\frac{\pi}{2}, m\right) = E(m). In the defining integral, it is assumed that the principal branch of the square root is taken and that the path of integration avoids crossing any branch cuts. Outside `-\pi/2 \le \Re(z) \le \pi/2`, the function extends quasi-periodically as .. math :: E(\phi + n \pi, m) = 2 n E(m) + F(\phi,m), n \in \mathbb{Z}. **Plots** .. literalinclude :: /modules/mpmath/plots/ellipe.py .. image :: /modules/mpmath/plots/ellipe.png **Examples for the complete integral** Basic values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> ellipe(0) 1.570796326794896619231322 >>> ellipe(1) 1.0 >>> ellipe(-1) 1.910098894513856008952381 >>> ellipe(2) (0.5990701173677961037199612 + 0.5990701173677961037199612j) >>> ellipe(inf) (0.0 + +infj) >>> ellipe(-inf) +inf Verifying the defining integral and hypergeometric representation:: >>> ellipe(0.5) 1.350643881047675502520175 >>> quad(lambda t: sqrt(1-0.5*sin(t)**2), [0, pi/2]) 1.350643881047675502520175 >>> pi/2*hyp2f1(0.5,-0.5,1,0.5) 1.350643881047675502520175 Evaluation is supported for arbitrary complex `m`:: >>> ellipe(0.5+0.25j) (1.360868682163129682716687 - 0.1238733442561786843557315j) >>> ellipe(3+4j) (1.499553520933346954333612 - 1.577879007912758274533309j) A definite integral:: >>> quad(ellipe, [0,1]) 1.333333333333333333333333 **Examples for the incomplete integral** Basic values and limits:: >>> ellipe(0,1) 0.0 >>> ellipe(0,0) 0.0 >>> ellipe(1,0) 1.0 >>> ellipe(2+3j,0) (2.0 + 3.0j) >>> ellipe(1,1); sin(1) 0.8414709848078965066525023 0.8414709848078965066525023 >>> ellipe(pi/2, -0.5); ellipe(-0.5) 1.751771275694817862026502 1.751771275694817862026502 >>> ellipe(pi/2, 1); ellipe(-pi/2, 1) 1.0 -1.0 >>> ellipe(1.5, 1) 0.9974949866040544309417234 Comparing with numerical integration:: >>> z,m = 0.5, 1.25 >>> ellipe(z,m) 0.4740152182652628394264449 >>> quad(lambda t: sqrt(1-m*sin(t)**2), [0,z]) 0.4740152182652628394264449 The arguments may be complex numbers:: >>> ellipe(3j, 0.5) (0.0 + 7.551991234890371873502105j) >>> ellipe(3+4j, 5-6j) (24.15299022574220502424466 + 75.2503670480325997418156j) >>> k = 35 >>> z,m = 2+3j, 1.25 >>> ellipe(z+pi*k,m); ellipe(z,m) + 2*k*ellipe(m) (48.30138799412005235090766 + 17.47255216721987688224357j) (48.30138799412005235090766 + 17.47255216721987688224357j) For `|\Re(z)| < \pi/2`, the function can be expressed as a hypergeometric series of two variables (see :func:`~mpmath.appellf1`):: >>> z,m = 0.5, 0.25 >>> ellipe(z,m) 0.4950017030164151928870375 >>> sin(z)*appellf1(0.5,0.5,-0.5,1.5,sin(z)**2,m*sin(z)**2) 0.4950017030164151928870376 """ if len(args) == 1: return ctx._ellipe(args[0]) else: phi, m = args z = phi if not (ctx.isnormal(z) and ctx.isnormal(m)): if m == 0: return z + m if z == 0: return z * m if m == ctx.inf or m == ctx.ninf: return ctx.inf raise ValueError x = z.real ctx.prec += max(0, ctx.mag(x)) pi = +ctx.pi away = abs(x) > pi/2 if away: d = ctx.nint(x/pi) z = z-pi*d P = 2*d*ctx.ellipe(m) else: P = 0 def terms(): c, s = ctx.cos_sin(z) x = c**2 y = 1-m*s**2 RF = ctx.elliprf(x, y, 1) RD = ctx.elliprd(x, y, 1) return s*RF, -m*s**3*RD/3 return ctx.sum_accurately(terms) + P @defun_wrapped def ellippi(ctx, *args): r""" Called with three arguments `n, \phi, m`, evaluates the Legendre incomplete elliptic integral of the third kind .. math :: \Pi(n; \phi, m) = \int_0^{\phi} \frac{dt}{(1-n \sin^2 t) \sqrt{1-m \sin^2 t}} = \int_0^{\sin \phi} \frac{dt}{(1-nt^2) \sqrt{1-t^2} \sqrt{1-mt^2}}. Called with two arguments `n, m`, evaluates the complete elliptic integral of the third kind `\Pi(n,m) = \Pi(n; \frac{\pi}{2},m)`. In the defining integral, it is assumed that the principal branch of the square root is taken and that the path of integration avoids crossing any branch cuts. Outside `-\pi/2 \le \Re(z) \le \pi/2`, the function extends quasi-periodically as .. math :: \Pi(n,\phi+k\pi,m) = 2k\Pi(n,m) + \Pi(n,\phi,m), k \in \mathbb{Z}. **Plots** .. literalinclude :: /modules/mpmath/plots/ellippi.py .. image :: /modules/mpmath/plots/ellippi.png **Examples for the complete integral** Some basic values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> ellippi(0,-5); ellipk(-5) 0.9555039270640439337379334 0.9555039270640439337379334 >>> ellippi(inf,2) 0.0 >>> ellippi(2,inf) 0.0 >>> abs(ellippi(1,5)) +inf >>> abs(ellippi(0.25,1)) +inf Evaluation in terms of simpler functions:: >>> ellippi(0.25,0.25); ellipe(0.25)/(1-0.25) 1.956616279119236207279727 1.956616279119236207279727 >>> ellippi(3,0); pi/(2*sqrt(-2)) (0.0 - 1.11072073453959156175397j) (0.0 - 1.11072073453959156175397j) >>> ellippi(-3,0); pi/(2*sqrt(4)) 0.7853981633974483096156609 0.7853981633974483096156609 **Examples for the incomplete integral** Basic values and limits:: >>> ellippi(0.25,-0.5); ellippi(0.25,pi/2,-0.5) 1.622944760954741603710555 1.622944760954741603710555 >>> ellippi(1,0,1) 0.0 >>> ellippi(inf,0,1) 0.0 >>> ellippi(0,0.25,0.5); ellipf(0.25,0.5) 0.2513040086544925794134591 0.2513040086544925794134591 >>> ellippi(1,1,1); (log(sec(1)+tan(1))+sec(1)*tan(1))/2 2.054332933256248668692452 2.054332933256248668692452 >>> ellippi(0.25, 53*pi/2, 0.75); 53*ellippi(0.25,0.75) 135.240868757890840755058 135.240868757890840755058 >>> ellippi(0.5,pi/4,0.5); 2*ellipe(pi/4,0.5)-1/sqrt(3) 0.9190227391656969903987269 0.9190227391656969903987269 Complex arguments are supported:: >>> ellippi(0.5, 5+6j-2*pi, -7-8j) (-0.3612856620076747660410167 + 0.5217735339984807829755815j) """ if len(args) == 2: n, m = args complete = True z = phi = ctx.pi/2 else: n, phi, m = args complete = False z = phi if not (ctx.isnormal(n) and ctx.isnormal(z) and ctx.isnormal(m)): if ctx.isnan(n) or ctx.isnan(z) or ctx.isnan(m): raise ValueError if complete: if m == 0: return ctx.pi/(2*ctx.sqrt(1-n)) if n == 0: return ctx.ellipk(m) if ctx.isinf(n) or ctx.isinf(m): return ctx.zero else: if z == 0: return z if ctx.isinf(n): return ctx.zero if ctx.isinf(m): return ctx.zero if ctx.isinf(n) or ctx.isinf(z) or ctx.isinf(m): raise ValueError if complete: if m == 1: return -ctx.inf/ctx.sign(n-1) away = False else: x = z.real ctx.prec += max(0, ctx.mag(x)) pi = +ctx.pi away = abs(x) > pi/2 if away: d = ctx.nint(x/pi) z = z-pi*d P = 2*d*ctx.ellippi(n,m) else: P = 0 def terms(): if complete: c, s = ctx.zero, ctx.one else: c, s = ctx.cos_sin(z) x = c**2 y = 1-m*s**2 RF = ctx.elliprf(x, y, 1) RJ = ctx.elliprj(x, y, 1, 1-n*s**2) return s*RF, n*s**3*RJ/3 return ctx.sum_accurately(terms) + P sympy-0.7.4.1/sympy/mpmath/functions/factorials.py0000644000175000017500000001243412253362407022430 0ustar georgeskgeorgeskfrom ..libmp.backend import xrange from .functions import defun, defun_wrapped @defun def gammaprod(ctx, a, b, _infsign=False): a = [ctx.convert(x) for x in a] b = [ctx.convert(x) for x in b] poles_num = [] poles_den = [] regular_num = [] regular_den = [] for x in a: [regular_num, poles_num][ctx.isnpint(x)].append(x) for x in b: [regular_den, poles_den][ctx.isnpint(x)].append(x) # One more pole in numerator or denominator gives 0 or inf if len(poles_num) < len(poles_den): return ctx.zero if len(poles_num) > len(poles_den): # Get correct sign of infinity for x+h, h -> 0 from above # XXX: hack, this should be done properly if _infsign: a = [x and x*(1+ctx.eps) or x+ctx.eps for x in poles_num] b = [x and x*(1+ctx.eps) or x+ctx.eps for x in poles_den] return ctx.sign(ctx.gammaprod(a+regular_num,b+regular_den)) * ctx.inf else: return ctx.inf # All poles cancel # lim G(i)/G(j) = (-1)**(i+j) * gamma(1-j) / gamma(1-i) p = ctx.one orig = ctx.prec try: ctx.prec = orig + 15 while poles_num: i = poles_num.pop() j = poles_den.pop() p *= (-1)**(i+j) * ctx.gamma(1-j) / ctx.gamma(1-i) for x in regular_num: p *= ctx.gamma(x) for x in regular_den: p /= ctx.gamma(x) finally: ctx.prec = orig return +p @defun def beta(ctx, x, y): x = ctx.convert(x) y = ctx.convert(y) if ctx.isinf(y): x, y = y, x if ctx.isinf(x): if x == ctx.inf and not ctx._im(y): if y == ctx.ninf: return ctx.nan if y > 0: return ctx.zero if ctx.isint(y): return ctx.nan if y < 0: return ctx.sign(ctx.gamma(y)) * ctx.inf return ctx.nan return ctx.gammaprod([x, y], [x+y]) @defun def binomial(ctx, n, k): return ctx.gammaprod([n+1], [k+1, n-k+1]) @defun def rf(ctx, x, n): return ctx.gammaprod([x+n], [x]) @defun def ff(ctx, x, n): return ctx.gammaprod([x+1], [x-n+1]) @defun_wrapped def fac2(ctx, x): if ctx.isinf(x): if x == ctx.inf: return x return ctx.nan return 2**(x/2)*(ctx.pi/2)**((ctx.cospi(x)-1)/4)*ctx.gamma(x/2+1) @defun_wrapped def barnesg(ctx, z): if ctx.isinf(z): if z == ctx.inf: return z return ctx.nan if ctx.isnan(z): return z if (not ctx._im(z)) and ctx._re(z) <= 0 and ctx.isint(ctx._re(z)): return z*0 # Account for size (would not be needed if computing log(G)) if abs(z) > 5: ctx.dps += 2*ctx.log(abs(z),2) # Reflection formula if ctx.re(z) < -ctx.dps: w = 1-z pi2 = 2*ctx.pi u = ctx.expjpi(2*w) v = ctx.j*ctx.pi/12 - ctx.j*ctx.pi*w**2/2 + w*ctx.ln(1-u) - \ ctx.j*ctx.polylog(2, u)/pi2 v = ctx.barnesg(2-z)*ctx.exp(v)/pi2**w if ctx._is_real_type(z): v = ctx._re(v) return v # Estimate terms for asymptotic expansion # TODO: fixme, obviously N = ctx.dps // 2 + 5 G = 1 while abs(z) < N or ctx.re(z) < 1: G /= ctx.gamma(z) z += 1 z -= 1 s = ctx.mpf(1)/12 s -= ctx.log(ctx.glaisher) s += z*ctx.log(2*ctx.pi)/2 s += (z**2/2-ctx.mpf(1)/12)*ctx.log(z) s -= 3*z**2/4 z2k = z2 = z**2 for k in xrange(1, N+1): t = ctx.bernoulli(2*k+2) / (4*k*(k+1)*z2k) if abs(t) < ctx.eps: #print k, N # check how many terms were needed break z2k *= z2 s += t #if k == N: # print "warning: series for barnesg failed to converge", ctx.dps return G*ctx.exp(s) @defun def superfac(ctx, z): return ctx.barnesg(z+2) @defun_wrapped def hyperfac(ctx, z): # XXX: estimate needed extra bits accurately if z == ctx.inf: return z if abs(z) > 5: extra = 4*int(ctx.log(abs(z),2)) else: extra = 0 ctx.prec += extra if not ctx._im(z) and ctx._re(z) < 0 and ctx.isint(ctx._re(z)): n = int(ctx.re(z)) h = ctx.hyperfac(-n-1) if ((n+1)//2) & 1: h = -h if ctx._is_complex_type(z): return h + 0j return h zp1 = z+1 # Wrong branch cut #v = ctx.gamma(zp1)**z #ctx.prec -= extra #return v / ctx.barnesg(zp1) v = ctx.exp(z*ctx.loggamma(zp1)) ctx.prec -= extra return v / ctx.barnesg(zp1) @defun_wrapped def loggamma_old(ctx, z): a = ctx._re(z) b = ctx._im(z) if not b and a > 0: return ctx.ln(ctx.gamma_old(z)) u = ctx.arg(z) w = ctx.ln(ctx.gamma_old(z)) if b: gi = -b - u/2 + a*u + b*ctx.ln(abs(z)) n = ctx.floor((gi-ctx._im(w))/(2*ctx.pi)+0.5) * (2*ctx.pi) return w + n*ctx.j elif a < 0: n = int(ctx.floor(a)) w += (n-(n%2))*ctx.pi*ctx.j return w ''' @defun def psi0(ctx, z): """Shortcut for psi(0,z) (the digamma function)""" return ctx.psi(0, z) @defun def psi1(ctx, z): """Shortcut for psi(1,z) (the trigamma function)""" return ctx.psi(1, z) @defun def psi2(ctx, z): """Shortcut for psi(2,z) (the tetragamma function)""" return ctx.psi(2, z) @defun def psi3(ctx, z): """Shortcut for psi(3,z) (the pentagamma function)""" return ctx.psi(3, z) ''' sympy-0.7.4.1/sympy/mpmath/functions/__init__.py0000644000175000017500000000046412253362407022040 0ustar georgeskgeorgeskfrom . import functions # Hack to update methods from . import factorials from . import hypergeometric from . import expintegrals from . import bessel from . import orthogonal from . import theta from . import elliptic from . import zeta from . import rszeta from . import zetazeros from . import qfunctions sympy-0.7.4.1/sympy/mpmath/ctx_iv.py0000644000175000017500000003724112253362407017570 0ustar georgeskgeorgeskimport operator from . import libmp from .libmp.backend import basestring from .libmp import ( int_types, MPZ_ONE, prec_to_dps, dps_to_prec, repr_dps, round_floor, round_ceiling, fzero, finf, fninf, fnan, mpf_le, mpf_neg, from_int, from_float, from_str, from_rational, mpi_mid, mpi_delta, mpi_str, mpi_abs, mpi_pos, mpi_neg, mpi_add, mpi_sub, mpi_mul, mpi_div, mpi_pow_int, mpi_pow, mpi_from_str, mpci_pos, mpci_neg, mpci_add, mpci_sub, mpci_mul, mpci_div, mpci_pow, mpci_abs, mpci_pow, mpci_exp, mpci_log, ComplexResult) mpi_zero = (fzero, fzero) from .ctx_base import StandardBaseContext new = object.__new__ def convert_mpf_(x, prec, rounding): if hasattr(x, "_mpf_"): return x._mpf_ if isinstance(x, int_types): return from_int(x, prec, rounding) if isinstance(x, float): return from_float(x, prec, rounding) if isinstance(x, basestring): return from_str(x, prec, rounding) class ivmpf(object): """ Interval arithmetic class. Precision is controlled by iv.prec. """ def __new__(cls, x=0): return cls.ctx.convert(x) def __int__(self): a, b = self._mpi_ if a == b: return int(libmp.to_int(a)) raise ValueError @property def real(self): return self @property def imag(self): return self.ctx.zero def conjugate(self): return self @property def a(self): a, b = self._mpi_ return self.ctx.make_mpf((a, a)) @property def b(self): a, b = self._mpi_ return self.ctx.make_mpf((b, b)) @property def mid(self): ctx = self.ctx v = mpi_mid(self._mpi_, ctx.prec) return ctx.make_mpf((v, v)) @property def delta(self): ctx = self.ctx v = mpi_delta(self._mpi_, ctx.prec) return ctx.make_mpf((v,v)) @property def _mpci_(self): return self._mpi_, mpi_zero def _compare(*args): raise TypeError("no ordering relation is defined for intervals") __gt__ = _compare __le__ = _compare __gt__ = _compare __ge__ = _compare def __contains__(self, t): t = self.ctx.mpf(t) return (self.a <= t.a) and (t.b <= self.b) def __str__(self): return mpi_str(self._mpi_, self.ctx.prec) def __repr__(self): if self.ctx.pretty: return str(self) a, b = self._mpi_ n = repr_dps(self.ctx.prec) a = libmp.to_str(a, n) b = libmp.to_str(b, n) return "mpi(%r, %r)" % (a, b) def _compare(s, t, cmpfun): if not hasattr(t, "_mpi_"): try: t = s.ctx.convert(t) except: return NotImplemented return cmpfun(s._mpi_, t._mpi_) def __eq__(s, t): return s._compare(t, libmp.mpi_eq) def __ne__(s, t): return s._compare(t, libmp.mpi_ne) def __lt__(s, t): return s._compare(t, libmp.mpi_lt) def __le__(s, t): return s._compare(t, libmp.mpi_le) def __gt__(s, t): return s._compare(t, libmp.mpi_gt) def __ge__(s, t): return s._compare(t, libmp.mpi_ge) def __abs__(self): return self.ctx.make_mpf(mpi_abs(self._mpi_, self.ctx.prec)) def __pos__(self): return self.ctx.make_mpf(mpi_pos(self._mpi_, self.ctx.prec)) def __neg__(self): return self.ctx.make_mpf(mpi_neg(self._mpi_, self.ctx.prec)) def ae(s, t, rel_eps=None, abs_eps=None): return s.ctx.almosteq(s, t, rel_eps, abs_eps) class ivmpc(object): def __new__(cls, re=0, im=0): re = cls.ctx.convert(re) im = cls.ctx.convert(im) y = new(cls) y._mpci_ = re._mpi_, im._mpi_ return y def __repr__(s): if s.ctx.pretty: return str(s) return "iv.mpc(%s, %s)" % (repr(s.real), repr(s.imag)) def __str__(s): return "(%s + %s*j)" % (str(s.real), str(s.imag)) @property def a(self): (a, b), (c,d) = self._mpci_ return self.ctx.make_mpf((a, a)) @property def b(self): (a, b), (c,d) = self._mpci_ return self.ctx.make_mpf((b, b)) @property def c(self): (a, b), (c,d) = self._mpci_ return self.ctx.make_mpf((c, c)) @property def d(self): (a, b), (c,d) = self._mpci_ return self.ctx.make_mpf((d, d)) @property def real(s): return s.ctx.make_mpf(s._mpci_[0]) @property def imag(s): return s.ctx.make_mpf(s._mpci_[1]) def conjugate(s): a, b = s._mpci_ return s.ctx.make_mpc((a, mpf_neg(b))) def overlap(s, t): t = s.ctx.convert(t) real_overlap = (s.a <= t.a <= s.b) or (s.a <= t.b <= s.b) or (t.a <= s.a <= t.b) or (t.a <= s.b <= t.b) imag_overlap = (s.c <= t.c <= s.d) or (s.c <= t.d <= s.d) or (t.c <= s.c <= t.d) or (t.c <= s.d <= t.d) return real_overlap and imag_overlap def __contains__(s, t): t = s.ctx.convert(t) return t.real in s.real and t.imag in s.imag def _compare(s, t, ne=False): if not isinstance(t, s.ctx._types): try: t = s.ctx.convert(t) except: return NotImplemented if hasattr(t, '_mpi_'): tval = t._mpi_, mpi_zero elif hasattr(t, '_mpci_'): tval = t._mpci_ if ne: return s._mpci_ != tval return s._mpci_ == tval def __eq__(s, t): return s._compare(t) def __ne__(s, t): return s._compare(t, True) def __lt__(s, t): raise TypeError("complex intervals cannot be ordered") __le__ = __gt__ = __ge__ = __lt__ def __neg__(s): return s.ctx.make_mpc(mpci_neg(s._mpci_, s.ctx.prec)) def __pos__(s): return s.ctx.make_mpc(mpci_pos(s._mpci_, s.ctx.prec)) def __abs__(s): return s.ctx.make_mpf(mpci_abs(s._mpci_, s.ctx.prec)) def ae(s, t, rel_eps=None, abs_eps=None): return s.ctx.almosteq(s, t, rel_eps, abs_eps) def _binary_op(f_real, f_complex): def g_complex(ctx, sval, tval): return ctx.make_mpc(f_complex(sval, tval, ctx.prec)) def g_real(ctx, sval, tval): try: return ctx.make_mpf(f_real(sval, tval, ctx.prec)) except ComplexResult: sval = (sval, mpi_zero) tval = (tval, mpi_zero) return g_complex(ctx, sval, tval) def lop_real(s, t): ctx = s.ctx if not isinstance(t, ctx._types): t = ctx.convert(t) if hasattr(t, "_mpi_"): return g_real(ctx, s._mpi_, t._mpi_) if hasattr(t, "_mpci_"): return g_complex(ctx, (s._mpi_, mpi_zero), t._mpci_) return NotImplemented def rop_real(s, t): ctx = s.ctx if not isinstance(t, ctx._types): t = ctx.convert(t) if hasattr(t, "_mpi_"): return g_real(ctx, t._mpi_, s._mpi_) if hasattr(t, "_mpci_"): return g_complex(ctx, t._mpci_, (s._mpi_, mpi_zero)) return NotImplemented def lop_complex(s, t): ctx = s.ctx if not isinstance(t, s.ctx._types): try: t = s.ctx.convert(t) except (ValueError, TypeError): return NotImplemented return g_complex(ctx, s._mpci_, t._mpci_) def rop_complex(s, t): ctx = s.ctx if not isinstance(t, s.ctx._types): t = s.ctx.convert(t) return g_complex(ctx, t._mpci_, s._mpci_) return lop_real, rop_real, lop_complex, rop_complex ivmpf.__add__, ivmpf.__radd__, ivmpc.__add__, ivmpc.__radd__ = _binary_op(mpi_add, mpci_add) ivmpf.__sub__, ivmpf.__rsub__, ivmpc.__sub__, ivmpc.__rsub__ = _binary_op(mpi_sub, mpci_sub) ivmpf.__mul__, ivmpf.__rmul__, ivmpc.__mul__, ivmpc.__rmul__ = _binary_op(mpi_mul, mpci_mul) ivmpf.__div__, ivmpf.__rdiv__, ivmpc.__div__, ivmpc.__rdiv__ = _binary_op(mpi_div, mpci_div) ivmpf.__pow__, ivmpf.__rpow__, ivmpc.__pow__, ivmpc.__rpow__ = _binary_op(mpi_pow, mpci_pow) ivmpf.__truediv__ = ivmpf.__div__ ivmpf.__rtruediv__ = ivmpf.__rdiv__ ivmpc.__truediv__ = ivmpc.__div__ ivmpc.__rtruediv__ = ivmpc.__rdiv__ class ivmpf_constant(ivmpf): def __new__(cls, f): self = new(cls) self._f = f return self def _get_mpi_(self): prec = self.ctx._prec[0] a = self._f(prec, round_floor) b = self._f(prec, round_ceiling) return a, b _mpi_ = property(_get_mpi_) class MPIntervalContext(StandardBaseContext): def __init__(ctx): ctx.mpf = type('ivmpf', (ivmpf,), {}) ctx.mpc = type('ivmpc', (ivmpc,), {}) ctx._types = (ctx.mpf, ctx.mpc) ctx._constant = type('ivmpf_constant', (ivmpf_constant,), {}) ctx._prec = [53] ctx._set_prec(53) ctx._constant._ctxdata = ctx.mpf._ctxdata = ctx.mpc._ctxdata = [ctx.mpf, new, ctx._prec] ctx._constant.ctx = ctx.mpf.ctx = ctx.mpc.ctx = ctx ctx.pretty = False StandardBaseContext.__init__(ctx) ctx._init_builtins() def _mpi(ctx, a, b=None): if b is None: return ctx.mpf(a) return ctx.mpf((a,b)) def _init_builtins(ctx): ctx.one = ctx.mpf(1) ctx.zero = ctx.mpf(0) ctx.inf = ctx.mpf('inf') ctx.ninf = -ctx.inf ctx.nan = ctx.mpf('nan') ctx.j = ctx.mpc(0,1) ctx.exp = ctx._wrap_mpi_function(libmp.mpi_exp, libmp.mpci_exp) ctx.sqrt = ctx._wrap_mpi_function(libmp.mpi_sqrt) ctx.ln = ctx._wrap_mpi_function(libmp.mpi_log, libmp.mpci_log) ctx.cos = ctx._wrap_mpi_function(libmp.mpi_cos, libmp.mpci_cos) ctx.sin = ctx._wrap_mpi_function(libmp.mpi_sin, libmp.mpci_sin) ctx.tan = ctx._wrap_mpi_function(libmp.mpi_tan) ctx.gamma = ctx._wrap_mpi_function(libmp.mpi_gamma, libmp.mpci_gamma) ctx.loggamma = ctx._wrap_mpi_function(libmp.mpi_loggamma, libmp.mpci_loggamma) ctx.rgamma = ctx._wrap_mpi_function(libmp.mpi_rgamma, libmp.mpci_rgamma) ctx.factorial = ctx._wrap_mpi_function(libmp.mpi_factorial, libmp.mpci_factorial) ctx.fac = ctx.factorial ctx.eps = ctx._constant(lambda prec, rnd: (0, MPZ_ONE, 1-prec, 1)) ctx.pi = ctx._constant(libmp.mpf_pi) ctx.e = ctx._constant(libmp.mpf_e) ctx.ln2 = ctx._constant(libmp.mpf_ln2) ctx.ln10 = ctx._constant(libmp.mpf_ln10) ctx.phi = ctx._constant(libmp.mpf_phi) ctx.euler = ctx._constant(libmp.mpf_euler) ctx.catalan = ctx._constant(libmp.mpf_catalan) ctx.glaisher = ctx._constant(libmp.mpf_glaisher) ctx.khinchin = ctx._constant(libmp.mpf_khinchin) ctx.twinprime = ctx._constant(libmp.mpf_twinprime) def _wrap_mpi_function(ctx, f_real, f_complex=None): def g(x, **kwargs): if kwargs: prec = kwargs.get('prec', ctx._prec[0]) else: prec = ctx._prec[0] x = ctx.convert(x) if hasattr(x, "_mpi_"): return ctx.make_mpf(f_real(x._mpi_, prec)) if hasattr(x, "_mpci_"): return ctx.make_mpc(f_complex(x._mpci_, prec)) raise ValueError return g @classmethod def _wrap_specfun(cls, name, f, wrap): if wrap: def f_wrapped(ctx, *args, **kwargs): convert = ctx.convert args = [convert(a) for a in args] prec = ctx.prec try: ctx.prec += 10 retval = f(ctx, *args, **kwargs) finally: ctx.prec = prec return +retval else: f_wrapped = f setattr(cls, name, f_wrapped) def _set_prec(ctx, n): ctx._prec[0] = max(1, int(n)) ctx._dps = prec_to_dps(n) def _set_dps(ctx, n): ctx._prec[0] = dps_to_prec(n) ctx._dps = max(1, int(n)) prec = property(lambda ctx: ctx._prec[0], _set_prec) dps = property(lambda ctx: ctx._dps, _set_dps) def make_mpf(ctx, v): a = new(ctx.mpf) a._mpi_ = v return a def make_mpc(ctx, v): a = new(ctx.mpc) a._mpci_ = v return a def _mpq(ctx, pq): p, q = pq a = libmp.from_rational(p, q, ctx.prec, round_floor) b = libmp.from_rational(p, q, ctx.prec, round_ceiling) return ctx.make_mpf((a, b)) def convert(ctx, x): if isinstance(x, (ctx.mpf, ctx.mpc)): return x if isinstance(x, ctx._constant): return +x if isinstance(x, complex) or hasattr(x, "_mpc_"): re = ctx.convert(x.real) im = ctx.convert(x.imag) return ctx.mpc(re,im) if isinstance(x, basestring): v = mpi_from_str(x, ctx.prec) return ctx.make_mpf(v) if hasattr(x, "_mpi_"): a, b = x._mpi_ else: try: a, b = x except (TypeError, ValueError): a = b = x if hasattr(a, "_mpi_"): a = a._mpi_[0] else: a = convert_mpf_(a, ctx.prec, round_floor) if hasattr(b, "_mpi_"): b = b._mpi_[1] else: b = convert_mpf_(b, ctx.prec, round_ceiling) if a == fnan or b == fnan: a = fninf b = finf assert mpf_le(a, b), "endpoints must be properly ordered" return ctx.make_mpf((a, b)) def nstr(ctx, x, n=5, **kwargs): x = ctx.convert(x) if hasattr(x, "_mpi_"): return libmp.mpi_to_str(x._mpi_, n, **kwargs) if hasattr(x, "_mpci_"): re = libmp.mpi_to_str(x._mpci_[0], n, **kwargs) im = libmp.mpi_to_str(x._mpci_[1], n, **kwargs) return "(%s + %s*j)" % (re, im) def mag(ctx, x): x = ctx.convert(x) if isinstance(x, ctx.mpc): return max(ctx.mag(x.real), ctx.mag(x.imag)) + 1 a, b = libmp.mpi_abs(x._mpi_) sign, man, exp, bc = b if man: return exp+bc if b == fzero: return ctx.ninf if b == fnan: return ctx.nan return ctx.inf def isnan(ctx, x): return False def isinf(ctx, x): return x == ctx.inf def isint(ctx, x): x = ctx.convert(x) a, b = x._mpi_ if a == b: sign, man, exp, bc = a if man: return exp >= 0 return a == fzero return None def ldexp(ctx, x, n): a, b = ctx.convert(x)._mpi_ a = libmp.mpf_shift(a, n) b = libmp.mpf_shift(b, n) return ctx.make_mpf((a,b)) def absmin(ctx, x): return abs(ctx.convert(x)).a def absmax(ctx, x): return abs(ctx.convert(x)).b def atan2(ctx, y, x): y = ctx.convert(y)._mpi_ x = ctx.convert(x)._mpi_ return ctx.make_mpf(libmp.mpi_atan2(y,x,ctx.prec)) def _convert_param(ctx, x): if isinstance(x, libmp.int_types): return x, 'Z' if isinstance(x, tuple): p, q = x return (ctx.mpf(p) / ctx.mpf(q), 'R') x = ctx.convert(x) if isinstance(x, ctx.mpf): return x, 'R' if isinstance(x, ctx.mpc): return x, 'C' raise ValueError def _is_real_type(ctx, z): return isinstance(z, ctx.mpf) or isinstance(z, int_types) def _is_complex_type(ctx, z): return isinstance(z, ctx.mpc) def hypsum(ctx, p, q, types, coeffs, z, maxterms=6000, **kwargs): coeffs = list(coeffs) num = range(p) den = range(p,p+q) #tol = ctx.eps s = t = ctx.one k = 0 while 1: for i in num: t *= (coeffs[i]+k) for i in den: t /= (coeffs[i]+k) k += 1 t /= k t *= z s += t if t == 0: return s #if abs(t) < tol: # return s if k > maxterms: raise ctx.NoConvergence sympy-0.7.4.1/sympy/mpmath/usertools.py0000644000175000017500000000604512253362407020331 0ustar georgeskgeorgesk def monitor(f, input='print', output='print'): """ Returns a wrapped copy of *f* that monitors evaluation by calling *input* with every input (*args*, *kwargs*) passed to *f* and *output* with every value returned from *f*. The default action (specify using the special string value ``'print'``) is to print inputs and outputs to stdout, along with the total evaluation count:: >>> from mpmath import * >>> mp.dps = 5; mp.pretty = False >>> diff(monitor(exp), 1) # diff will eval f(x-h) and f(x+h) in 0 (mpf('0.99999999906867742538452148'),) {} out 0 mpf('2.7182818259274480055282064') in 1 (mpf('1.0000000009313225746154785'),) {} out 1 mpf('2.7182818309906424675501024') mpf('2.7182808') To disable either the input or the output handler, you may pass *None* as argument. Custom input and output handlers may be used e.g. to store results for later analysis:: >>> mp.dps = 15 >>> input = [] >>> output = [] >>> findroot(monitor(sin, input.append, output.append), 3.0) mpf('3.1415926535897932') >>> len(input) # Count number of evaluations 9 >>> print(input[3]); print(output[3]) ((mpf('3.1415076583334066'),), {}) 8.49952562843408e-5 >>> print(input[4]); print(output[4]) ((mpf('3.1415928201669122'),), {}) -1.66577118985331e-7 """ if not input: input = lambda v: None elif input == 'print': incount = [0] def input(value): args, kwargs = value print("in %s %r %r" % (incount[0], args, kwargs)) incount[0] += 1 if not output: output = lambda v: None elif output == 'print': outcount = [0] def output(value): print("out %s %r" % (outcount[0], value)) outcount[0] += 1 def f_monitored(*args, **kwargs): input((args, kwargs)) v = f(*args, **kwargs) output(v) return v return f_monitored def timing(f, *args, **kwargs): """ Returns time elapsed for evaluating ``f()``. Optionally arguments may be passed to time the execution of ``f(*args, **kwargs)``. If the first call is very quick, ``f`` is called repeatedly and the best time is returned. """ once = kwargs.get('once') if 'once' in kwargs: del kwargs['once'] if args or kwargs: if len(args) == 1 and not kwargs: arg = args[0] g = lambda: f(arg) else: g = lambda: f(*args, **kwargs) else: g = f from timeit import default_timer as clock t1=clock() v=g() t2=clock() t=t2-t1 if t > 0.05 or once: return t for i in range(3): t1=clock() # Evaluate multiple times because the timer function # has a significant overhead g() g() g() g() g() g() g() g() g() g() t2=clock() t=min(t,(t2-t1)/10) return t sympy-0.7.4.1/sympy/mpmath/function_docs.py0000644000175000017500000103455212253362407021134 0ustar georgeskgeorgesk""" Extended docstrings for functions.py """ pi = r""" `\pi`, roughly equal to 3.141592654, represents the area of the unit circle, the half-period of trigonometric functions, and many other things in mathematics. Mpmath can evaluate `\pi` to arbitrary precision:: >>> from mpmath import * >>> mp.dps = 50; mp.pretty = True >>> +pi 3.1415926535897932384626433832795028841971693993751 This shows digits 99991-100000 of `\pi`:: >>> mp.dps = 100000 >>> str(pi)[-10:] '5549362464' **Possible issues** :data:`pi` always rounds to the nearest floating-point number when used. This means that exact mathematical identities involving `\pi` will generally not be preserved in floating-point arithmetic. In particular, multiples of :data:`pi` (except for the trivial case ``0*pi``) are *not* the exact roots of :func:`~mpmath.sin`, but differ roughly by the current epsilon:: >>> mp.dps = 15 >>> sin(pi) 1.22464679914735e-16 One solution is to use the :func:`~mpmath.sinpi` function instead:: >>> sinpi(1) 0.0 See the documentation of trigonometric functions for additional details. """ degree = r""" Represents one degree of angle, `1^{\circ} = \pi/180`, or about 0.01745329. This constant may be evaluated to arbitrary precision:: >>> from mpmath import * >>> mp.dps = 50; mp.pretty = True >>> +degree 0.017453292519943295769236907684886127134428718885417 The :data:`degree` object is convenient for conversion to radians:: >>> sin(30 * degree) 0.5 >>> asin(0.5) / degree 30.0 """ e = r""" The transcendental number `e` = 2.718281828... is the base of the natural logarithm (:func:`~mpmath.ln`) and of the exponential function (:func:`~mpmath.exp`). Mpmath can be evaluate `e` to arbitrary precision:: >>> from mpmath import * >>> mp.dps = 50; mp.pretty = True >>> +e 2.7182818284590452353602874713526624977572470937 This shows digits 99991-100000 of `e`:: >>> mp.dps = 100000 >>> str(e)[-10:] '2100427165' **Possible issues** :data:`e` always rounds to the nearest floating-point number when used, and mathematical identities involving `e` may not hold in floating-point arithmetic. For example, ``ln(e)`` might not evaluate exactly to 1. In particular, don't use ``e**x`` to compute the exponential function. Use ``exp(x)`` instead; this is both faster and more accurate. """ phi = r""" Represents the golden ratio `\phi = (1+\sqrt 5)/2`, approximately equal to 1.6180339887. To high precision, its value is:: >>> from mpmath import * >>> mp.dps = 50; mp.pretty = True >>> +phi 1.6180339887498948482045868343656381177203091798058 Formulas for the golden ratio include the following:: >>> (1+sqrt(5))/2 1.6180339887498948482045868343656381177203091798058 >>> findroot(lambda x: x**2-x-1, 1) 1.6180339887498948482045868343656381177203091798058 >>> limit(lambda n: fib(n+1)/fib(n), inf) 1.6180339887498948482045868343656381177203091798058 """ euler = r""" Euler's constant or the Euler-Mascheroni constant `\gamma` = 0.57721566... is a number of central importance to number theory and special functions. It is defined as the limit .. math :: \gamma = \lim_{n\to\infty} H_n - \log n where `H_n = 1 + \frac{1}{2} + \ldots + \frac{1}{n}` is a harmonic number (see :func:`~mpmath.harmonic`). Evaluation of `\gamma` is supported at arbitrary precision:: >>> from mpmath import * >>> mp.dps = 50; mp.pretty = True >>> +euler 0.57721566490153286060651209008240243104215933593992 We can also compute `\gamma` directly from the definition, although this is less efficient:: >>> limit(lambda n: harmonic(n)-log(n), inf) 0.57721566490153286060651209008240243104215933593992 This shows digits 9991-10000 of `\gamma`:: >>> mp.dps = 10000 >>> str(euler)[-10:] '4679858165' Integrals, series, and representations for `\gamma` in terms of special functions include the following (there are many others):: >>> mp.dps = 25 >>> -quad(lambda x: exp(-x)*log(x), [0,inf]) 0.5772156649015328606065121 >>> quad(lambda x,y: (x-1)/(1-x*y)/log(x*y), [0,1], [0,1]) 0.5772156649015328606065121 >>> nsum(lambda k: 1/k-log(1+1/k), [1,inf]) 0.5772156649015328606065121 >>> nsum(lambda k: (-1)**k*zeta(k)/k, [2,inf]) 0.5772156649015328606065121 >>> -diff(gamma, 1) 0.5772156649015328606065121 >>> limit(lambda x: 1/x-gamma(x), 0) 0.5772156649015328606065121 >>> limit(lambda x: zeta(x)-1/(x-1), 1) 0.5772156649015328606065121 >>> (log(2*pi*nprod(lambda n: ... exp(-2+2/n)*(1+2/n)**n, [1,inf]))-3)/2 0.5772156649015328606065121 For generalizations of the identities `\gamma = -\Gamma'(1)` and `\gamma = \lim_{x\to1} \zeta(x)-1/(x-1)`, see :func:`~mpmath.psi` and :func:`~mpmath.stieltjes` respectively. """ catalan = r""" Catalan's constant `K` = 0.91596559... is given by the infinite series .. math :: K = \sum_{k=0}^{\infty} \frac{(-1)^k}{(2k+1)^2}. Mpmath can evaluate it to arbitrary precision:: >>> from mpmath import * >>> mp.dps = 50; mp.pretty = True >>> +catalan 0.91596559417721901505460351493238411077414937428167 One can also compute `K` directly from the definition, although this is significantly less efficient:: >>> nsum(lambda k: (-1)**k/(2*k+1)**2, [0, inf]) 0.91596559417721901505460351493238411077414937428167 This shows digits 9991-10000 of `K`:: >>> mp.dps = 10000 >>> str(catalan)[-10:] '9537871503' Catalan's constant has numerous integral representations:: >>> mp.dps = 50 >>> quad(lambda x: -log(x)/(1+x**2), [0, 1]) 0.91596559417721901505460351493238411077414937428167 >>> quad(lambda x: atan(x)/x, [0, 1]) 0.91596559417721901505460351493238411077414937428167 >>> quad(lambda x: ellipk(x**2)/2, [0, 1]) 0.91596559417721901505460351493238411077414937428167 >>> quad(lambda x,y: 1/(1+(x*y)**2), [0, 1], [0, 1]) 0.91596559417721901505460351493238411077414937428167 As well as series representations:: >>> pi*log(sqrt(3)+2)/8 + 3*nsum(lambda n: ... (fac(n)/(2*n+1))**2/fac(2*n), [0, inf])/8 0.91596559417721901505460351493238411077414937428167 >>> 1-nsum(lambda n: n*zeta(2*n+1)/16**n, [1,inf]) 0.91596559417721901505460351493238411077414937428167 """ khinchin = r""" Khinchin's constant `K` = 2.68542... is a number that appears in the theory of continued fractions. Mpmath can evaluate it to arbitrary precision:: >>> from mpmath import * >>> mp.dps = 50; mp.pretty = True >>> +khinchin 2.6854520010653064453097148354817956938203822939945 An integral representation is:: >>> I = quad(lambda x: log((1-x**2)/sincpi(x))/x/(1+x), [0, 1]) >>> 2*exp(1/log(2)*I) 2.6854520010653064453097148354817956938203822939945 The computation of ``khinchin`` is based on an efficient implementation of the following series:: >>> f = lambda n: (zeta(2*n)-1)/n*sum((-1)**(k+1)/mpf(k) ... for k in range(1,2*int(n))) >>> exp(nsum(f, [1,inf])/log(2)) 2.6854520010653064453097148354817956938203822939945 """ glaisher = r""" Glaisher's constant `A`, also known as the Glaisher-Kinkelin constant, is a number approximately equal to 1.282427129 that sometimes appears in formulas related to gamma and zeta functions. It is also related to the Barnes G-function (see :func:`~mpmath.barnesg`). The constant is defined as `A = \exp(1/12-\zeta'(-1))` where `\zeta'(s)` denotes the derivative of the Riemann zeta function (see :func:`~mpmath.zeta`). Mpmath can evaluate Glaisher's constant to arbitrary precision: >>> from mpmath import * >>> mp.dps = 50; mp.pretty = True >>> +glaisher 1.282427129100622636875342568869791727767688927325 We can verify that the value computed by :data:`glaisher` is correct using mpmath's facilities for numerical differentiation and arbitrary evaluation of the zeta function: >>> exp(mpf(1)/12 - diff(zeta, -1)) 1.282427129100622636875342568869791727767688927325 Here is an example of an integral that can be evaluated in terms of Glaisher's constant: >>> mp.dps = 15 >>> quad(lambda x: log(gamma(x)), [1, 1.5]) -0.0428537406502909 >>> -0.5 - 7*log(2)/24 + log(pi)/4 + 3*log(glaisher)/2 -0.042853740650291 Mpmath computes Glaisher's constant by applying Euler-Maclaurin summation to a slowly convergent series. The implementation is reasonably efficient up to about 10,000 digits. See the source code for additional details. References: http://mathworld.wolfram.com/Glaisher-KinkelinConstant.html """ apery = r""" Represents Apery's constant, which is the irrational number approximately equal to 1.2020569 given by .. math :: \zeta(3) = \sum_{k=1}^\infty\frac{1}{k^3}. The calculation is based on an efficient hypergeometric series. To 50 decimal places, the value is given by:: >>> from mpmath import * >>> mp.dps = 50; mp.pretty = True >>> +apery 1.2020569031595942853997381615114499907649862923405 Other ways to evaluate Apery's constant using mpmath include:: >>> zeta(3) 1.2020569031595942853997381615114499907649862923405 >>> -psi(2,1)/2 1.2020569031595942853997381615114499907649862923405 >>> 8*nsum(lambda k: 1/(2*k+1)**3, [0,inf])/7 1.2020569031595942853997381615114499907649862923405 >>> f = lambda k: 2/k**3/(exp(2*pi*k)-1) >>> 7*pi**3/180 - nsum(f, [1,inf]) 1.2020569031595942853997381615114499907649862923405 This shows digits 9991-10000 of Apery's constant:: >>> mp.dps = 10000 >>> str(apery)[-10:] '3189504235' """ mertens = r""" Represents the Mertens or Meissel-Mertens constant, which is the prime number analog of Euler's constant: .. math :: B_1 = \lim_{N\to\infty} \left(\sum_{p_k \le N} \frac{1}{p_k} - \log \log N \right) Here `p_k` denotes the `k`-th prime number. Other names for this constant include the Hadamard-de la Vallee-Poussin constant or the prime reciprocal constant. The following gives the Mertens constant to 50 digits:: >>> from mpmath import * >>> mp.dps = 50; mp.pretty = True >>> +mertens 0.2614972128476427837554268386086958590515666482612 References: http://mathworld.wolfram.com/MertensConstant.html """ twinprime = r""" Represents the twin prime constant, which is the factor `C_2` featuring in the Hardy-Littlewood conjecture for the growth of the twin prime counting function, .. math :: \pi_2(n) \sim 2 C_2 \frac{n}{\log^2 n}. It is given by the product over primes .. math :: C_2 = \prod_{p\ge3} \frac{p(p-2)}{(p-1)^2} \approx 0.66016 Computing `C_2` to 50 digits:: >>> from mpmath import * >>> mp.dps = 50; mp.pretty = True >>> +twinprime 0.66016181584686957392781211001455577843262336028473 References: http://mathworld.wolfram.com/TwinPrimesConstant.html """ ln = r""" Computes the natural logarithm of `x`, `\ln x`. See :func:`~mpmath.log` for additional documentation.""" sqrt = r""" ``sqrt(x)`` gives the principal square root of `x`, `\sqrt x`. For positive real numbers, the principal root is simply the positive square root. For arbitrary complex numbers, the principal square root is defined to satisfy `\sqrt x = \exp(\log(x)/2)`. The function thus has a branch cut along the negative half real axis. For all mpmath numbers ``x``, calling ``sqrt(x)`` is equivalent to performing ``x**0.5``. **Examples** Basic examples and limits:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> sqrt(10) 3.16227766016838 >>> sqrt(100) 10.0 >>> sqrt(-4) (0.0 + 2.0j) >>> sqrt(1+1j) (1.09868411346781 + 0.455089860562227j) >>> sqrt(inf) +inf Square root evaluation is fast at huge precision:: >>> mp.dps = 50000 >>> a = sqrt(3) >>> str(a)[-10:] '9329332814' :func:`mpmath.iv.sqrt` supports interval arguments:: >>> iv.dps = 15; iv.pretty = True >>> iv.sqrt([16,100]) [4.0, 10.0] >>> iv.sqrt(2) [1.4142135623730949234, 1.4142135623730951455] >>> iv.sqrt(2) ** 2 [1.9999999999999995559, 2.0000000000000004441] """ cbrt = r""" ``cbrt(x)`` computes the cube root of `x`, `x^{1/3}`. This function is faster and more accurate than raising to a floating-point fraction:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> 125**(mpf(1)/3) mpf('4.9999999999999991') >>> cbrt(125) mpf('5.0') Every nonzero complex number has three cube roots. This function returns the cube root defined by `\exp(\log(x)/3)` where the principal branch of the natural logarithm is used. Note that this does not give a real cube root for negative real numbers:: >>> mp.pretty = True >>> cbrt(-1) (0.5 + 0.866025403784439j) """ exp = r""" Computes the exponential function, .. math :: \exp(x) = e^x = \sum_{k=0}^{\infty} \frac{x^k}{k!}. For complex numbers, the exponential function also satisfies .. math :: \exp(x+yi) = e^x (\cos y + i \sin y). **Basic examples** Some values of the exponential function:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> exp(0) 1.0 >>> exp(1) 2.718281828459045235360287 >>> exp(-1) 0.3678794411714423215955238 >>> exp(inf) +inf >>> exp(-inf) 0.0 Arguments can be arbitrarily large:: >>> exp(10000) 8.806818225662921587261496e+4342 >>> exp(-10000) 1.135483865314736098540939e-4343 Evaluation is supported for interval arguments via :func:`mpmath.iv.exp`:: >>> iv.dps = 25; iv.pretty = True >>> iv.exp([-inf,0]) [0.0, 1.0] >>> iv.exp([0,1]) [1.0, 2.71828182845904523536028749558] The exponential function can be evaluated efficiently to arbitrary precision:: >>> mp.dps = 10000 >>> exp(pi) #doctest: +ELLIPSIS 23.140692632779269005729...8984304016040616 **Functional properties** Numerical verification of Euler's identity for the complex exponential function:: >>> mp.dps = 15 >>> exp(j*pi)+1 (0.0 + 1.22464679914735e-16j) >>> chop(exp(j*pi)+1) 0.0 This recovers the coefficients (reciprocal factorials) in the Maclaurin series expansion of exp:: >>> nprint(taylor(exp, 0, 5)) [1.0, 1.0, 0.5, 0.166667, 0.0416667, 0.00833333] The exponential function is its own derivative and antiderivative:: >>> exp(pi) 23.1406926327793 >>> diff(exp, pi) 23.1406926327793 >>> quad(exp, [-inf, pi]) 23.1406926327793 The exponential function can be evaluated using various methods, including direct summation of the series, limits, and solving the defining differential equation:: >>> nsum(lambda k: pi**k/fac(k), [0,inf]) 23.1406926327793 >>> limit(lambda k: (1+pi/k)**k, inf) 23.1406926327793 >>> odefun(lambda t, x: x, 0, 1)(pi) 23.1406926327793 """ cosh = r""" Computes the hyperbolic cosine of `x`, `\cosh(x) = (e^x + e^{-x})/2`. Values and limits include:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> cosh(0) 1.0 >>> cosh(1) 1.543080634815243778477906 >>> cosh(-inf), cosh(+inf) (+inf, +inf) The hyperbolic cosine is an even, convex function with a global minimum at `x = 0`, having a Maclaurin series that starts:: >>> nprint(chop(taylor(cosh, 0, 5))) [1.0, 0.0, 0.5, 0.0, 0.0416667, 0.0] Generalized to complex numbers, the hyperbolic cosine is equivalent to a cosine with the argument rotated in the imaginary direction, or `\cosh x = \cos ix`:: >>> cosh(2+3j) (-3.724545504915322565473971 + 0.5118225699873846088344638j) >>> cos(3-2j) (-3.724545504915322565473971 + 0.5118225699873846088344638j) """ sinh = r""" Computes the hyperbolic sine of `x`, `\sinh(x) = (e^x - e^{-x})/2`. Values and limits include:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> sinh(0) 0.0 >>> sinh(1) 1.175201193643801456882382 >>> sinh(-inf), sinh(+inf) (-inf, +inf) The hyperbolic sine is an odd function, with a Maclaurin series that starts:: >>> nprint(chop(taylor(sinh, 0, 5))) [0.0, 1.0, 0.0, 0.166667, 0.0, 0.00833333] Generalized to complex numbers, the hyperbolic sine is essentially a sine with a rotation `i` applied to the argument; more precisely, `\sinh x = -i \sin ix`:: >>> sinh(2+3j) (-3.590564589985779952012565 + 0.5309210862485198052670401j) >>> j*sin(3-2j) (-3.590564589985779952012565 + 0.5309210862485198052670401j) """ tanh = r""" Computes the hyperbolic tangent of `x`, `\tanh(x) = \sinh(x)/\cosh(x)`. Values and limits include:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> tanh(0) 0.0 >>> tanh(1) 0.7615941559557648881194583 >>> tanh(-inf), tanh(inf) (-1.0, 1.0) The hyperbolic tangent is an odd, sigmoidal function, similar to the inverse tangent and error function. Its Maclaurin series is:: >>> nprint(chop(taylor(tanh, 0, 5))) [0.0, 1.0, 0.0, -0.333333, 0.0, 0.133333] Generalized to complex numbers, the hyperbolic tangent is essentially a tangent with a rotation `i` applied to the argument; more precisely, `\tanh x = -i \tan ix`:: >>> tanh(2+3j) (0.9653858790221331242784803 - 0.009884375038322493720314034j) >>> j*tan(3-2j) (0.9653858790221331242784803 - 0.009884375038322493720314034j) """ cos = r""" Computes the cosine of `x`, `\cos(x)`. >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> cos(pi/3) 0.5 >>> cos(100000001) -0.9802850113244713353133243 >>> cos(2+3j) (-4.189625690968807230132555 - 9.109227893755336597979197j) >>> cos(inf) nan >>> nprint(chop(taylor(cos, 0, 6))) [1.0, 0.0, -0.5, 0.0, 0.0416667, 0.0, -0.00138889] Intervals are supported via :func:`mpmath.iv.cos`:: >>> iv.dps = 25; iv.pretty = True >>> iv.cos([0,1]) [0.540302305868139717400936602301, 1.0] >>> iv.cos([0,2]) [-0.41614683654714238699756823214, 1.0] """ sin = r""" Computes the sine of `x`, `\sin(x)`. >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> sin(pi/3) 0.8660254037844386467637232 >>> sin(100000001) 0.1975887055794968911438743 >>> sin(2+3j) (9.1544991469114295734673 - 4.168906959966564350754813j) >>> sin(inf) nan >>> nprint(chop(taylor(sin, 0, 6))) [0.0, 1.0, 0.0, -0.166667, 0.0, 0.00833333, 0.0] Intervals are supported via :func:`mpmath.iv.sin`:: >>> iv.dps = 25; iv.pretty = True >>> iv.sin([0,1]) [0.0, 0.841470984807896506652502331201] >>> iv.sin([0,2]) [0.0, 1.0] """ tan = r""" Computes the tangent of `x`, `\tan(x) = \frac{\sin(x)}{\cos(x)}`. The tangent function is singular at `x = (n+1/2)\pi`, but ``tan(x)`` always returns a finite result since `(n+1/2)\pi` cannot be represented exactly using floating-point arithmetic. >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> tan(pi/3) 1.732050807568877293527446 >>> tan(100000001) -0.2015625081449864533091058 >>> tan(2+3j) (-0.003764025641504248292751221 + 1.003238627353609801446359j) >>> tan(inf) nan >>> nprint(chop(taylor(tan, 0, 6))) [0.0, 1.0, 0.0, 0.333333, 0.0, 0.133333, 0.0] Intervals are supported via :func:`mpmath.iv.tan`:: >>> iv.dps = 25; iv.pretty = True >>> iv.tan([0,1]) [0.0, 1.55740772465490223050697482944] >>> iv.tan([0,2]) # Interval includes a singularity [-inf, +inf] """ sec = r""" Computes the secant of `x`, `\mathrm{sec}(x) = \frac{1}{\cos(x)}`. The secant function is singular at `x = (n+1/2)\pi`, but ``sec(x)`` always returns a finite result since `(n+1/2)\pi` cannot be represented exactly using floating-point arithmetic. >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> sec(pi/3) 2.0 >>> sec(10000001) -1.184723164360392819100265 >>> sec(2+3j) (-0.04167496441114427004834991 + 0.0906111371962375965296612j) >>> sec(inf) nan >>> nprint(chop(taylor(sec, 0, 6))) [1.0, 0.0, 0.5, 0.0, 0.208333, 0.0, 0.0847222] Intervals are supported via :func:`mpmath.iv.sec`:: >>> iv.dps = 25; iv.pretty = True >>> iv.sec([0,1]) [1.0, 1.85081571768092561791175326276] >>> iv.sec([0,2]) # Interval includes a singularity [-inf, +inf] """ csc = r""" Computes the cosecant of `x`, `\mathrm{csc}(x) = \frac{1}{\sin(x)}`. This cosecant function is singular at `x = n \pi`, but with the exception of the point `x = 0`, ``csc(x)`` returns a finite result since `n \pi` cannot be represented exactly using floating-point arithmetic. >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> csc(pi/3) 1.154700538379251529018298 >>> csc(10000001) -1.864910497503629858938891 >>> csc(2+3j) (0.09047320975320743980579048 + 0.04120098628857412646300981j) >>> csc(inf) nan Intervals are supported via :func:`mpmath.iv.csc`:: >>> iv.dps = 25; iv.pretty = True >>> iv.csc([0,1]) # Interval includes a singularity [1.18839510577812121626159943988, +inf] >>> iv.csc([0,2]) [1.0, +inf] """ cot = r""" Computes the cotangent of `x`, `\mathrm{cot}(x) = \frac{1}{\tan(x)} = \frac{\cos(x)}{\sin(x)}`. This cotangent function is singular at `x = n \pi`, but with the exception of the point `x = 0`, ``cot(x)`` returns a finite result since `n \pi` cannot be represented exactly using floating-point arithmetic. >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> cot(pi/3) 0.5773502691896257645091488 >>> cot(10000001) 1.574131876209625656003562 >>> cot(2+3j) (-0.003739710376336956660117409 - 0.9967577965693583104609688j) >>> cot(inf) nan Intervals are supported via :func:`mpmath.iv.cot`:: >>> iv.dps = 25; iv.pretty = True >>> iv.cot([0,1]) # Interval includes a singularity [0.642092615934330703006419974862, +inf] >>> iv.cot([1,2]) [-inf, +inf] """ acos = r""" Computes the inverse cosine or arccosine of `x`, `\cos^{-1}(x)`. Since `-1 \le \cos(x) \le 1` for real `x`, the inverse cosine is real-valued only for `-1 \le x \le 1`. On this interval, :func:`~mpmath.acos` is defined to be a monotonically decreasing function assuming values between `+\pi` and `0`. Basic values are:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> acos(-1) 3.141592653589793238462643 >>> acos(0) 1.570796326794896619231322 >>> acos(1) 0.0 >>> nprint(chop(taylor(acos, 0, 6))) [1.5708, -1.0, 0.0, -0.166667, 0.0, -0.075, 0.0] :func:`~mpmath.acos` is defined so as to be a proper inverse function of `\cos(\theta)` for `0 \le \theta < \pi`. We have `\cos(\cos^{-1}(x)) = x` for all `x`, but `\cos^{-1}(\cos(x)) = x` only for `0 \le \Re[x] < \pi`:: >>> for x in [1, 10, -1, 2+3j, 10+3j]: ... print("%s %s" % (cos(acos(x)), acos(cos(x)))) ... 1.0 1.0 (10.0 + 0.0j) 2.566370614359172953850574 -1.0 1.0 (2.0 + 3.0j) (2.0 + 3.0j) (10.0 + 3.0j) (2.566370614359172953850574 - 3.0j) The inverse cosine has two branch points: `x = \pm 1`. :func:`~mpmath.acos` places the branch cuts along the line segments `(-\infty, -1)` and `(+1, +\infty)`. In general, .. math :: \cos^{-1}(x) = \frac{\pi}{2} + i \log\left(ix + \sqrt{1-x^2} \right) where the principal-branch log and square root are implied. """ asin = r""" Computes the inverse sine or arcsine of `x`, `\sin^{-1}(x)`. Since `-1 \le \sin(x) \le 1` for real `x`, the inverse sine is real-valued only for `-1 \le x \le 1`. On this interval, it is defined to be a monotonically increasing function assuming values between `-\pi/2` and `\pi/2`. Basic values are:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> asin(-1) -1.570796326794896619231322 >>> asin(0) 0.0 >>> asin(1) 1.570796326794896619231322 >>> nprint(chop(taylor(asin, 0, 6))) [0.0, 1.0, 0.0, 0.166667, 0.0, 0.075, 0.0] :func:`~mpmath.asin` is defined so as to be a proper inverse function of `\sin(\theta)` for `-\pi/2 < \theta < \pi/2`. We have `\sin(\sin^{-1}(x)) = x` for all `x`, but `\sin^{-1}(\sin(x)) = x` only for `-\pi/2 < \Re[x] < \pi/2`:: >>> for x in [1, 10, -1, 1+3j, -2+3j]: ... print("%s %s" % (chop(sin(asin(x))), asin(sin(x)))) ... 1.0 1.0 10.0 -0.5752220392306202846120698 -1.0 -1.0 (1.0 + 3.0j) (1.0 + 3.0j) (-2.0 + 3.0j) (-1.141592653589793238462643 - 3.0j) The inverse sine has two branch points: `x = \pm 1`. :func:`~mpmath.asin` places the branch cuts along the line segments `(-\infty, -1)` and `(+1, +\infty)`. In general, .. math :: \sin^{-1}(x) = -i \log\left(ix + \sqrt{1-x^2} \right) where the principal-branch log and square root are implied. """ atan = r""" Computes the inverse tangent or arctangent of `x`, `\tan^{-1}(x)`. This is a real-valued function for all real `x`, with range `(-\pi/2, \pi/2)`. Basic values are:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> atan(-inf) -1.570796326794896619231322 >>> atan(-1) -0.7853981633974483096156609 >>> atan(0) 0.0 >>> atan(1) 0.7853981633974483096156609 >>> atan(inf) 1.570796326794896619231322 >>> nprint(chop(taylor(atan, 0, 6))) [0.0, 1.0, 0.0, -0.333333, 0.0, 0.2, 0.0] The inverse tangent is often used to compute angles. However, the atan2 function is often better for this as it preserves sign (see :func:`~mpmath.atan2`). :func:`~mpmath.atan` is defined so as to be a proper inverse function of `\tan(\theta)` for `-\pi/2 < \theta < \pi/2`. We have `\tan(\tan^{-1}(x)) = x` for all `x`, but `\tan^{-1}(\tan(x)) = x` only for `-\pi/2 < \Re[x] < \pi/2`:: >>> mp.dps = 25 >>> for x in [1, 10, -1, 1+3j, -2+3j]: ... print("%s %s" % (tan(atan(x)), atan(tan(x)))) ... 1.0 1.0 10.0 0.5752220392306202846120698 -1.0 -1.0 (1.0 + 3.0j) (1.000000000000000000000001 + 3.0j) (-2.0 + 3.0j) (1.141592653589793238462644 + 3.0j) The inverse tangent has two branch points: `x = \pm i`. :func:`~mpmath.atan` places the branch cuts along the line segments `(-i \infty, -i)` and `(+i, +i \infty)`. In general, .. math :: \tan^{-1}(x) = \frac{i}{2}\left(\log(1-ix)-\log(1+ix)\right) where the principal-branch log is implied. """ acot = r"""Computes the inverse cotangent of `x`, `\mathrm{cot}^{-1}(x) = \tan^{-1}(1/x)`.""" asec = r"""Computes the inverse secant of `x`, `\mathrm{sec}^{-1}(x) = \cos^{-1}(1/x)`.""" acsc = r"""Computes the inverse cosecant of `x`, `\mathrm{csc}^{-1}(x) = \sin^{-1}(1/x)`.""" coth = r"""Computes the hyperbolic cotangent of `x`, `\mathrm{coth}(x) = \frac{\cosh(x)}{\sinh(x)}`. """ sech = r"""Computes the hyperbolic secant of `x`, `\mathrm{sech}(x) = \frac{1}{\cosh(x)}`. """ csch = r"""Computes the hyperbolic cosecant of `x`, `\mathrm{csch}(x) = \frac{1}{\sinh(x)}`. """ acosh = r"""Computes the inverse hyperbolic cosine of `x`, `\mathrm{cosh}^{-1}(x) = \log(x+\sqrt{x+1}\sqrt{x-1})`. """ asinh = r"""Computes the inverse hyperbolic sine of `x`, `\mathrm{sinh}^{-1}(x) = \log(x+\sqrt{1+x^2})`. """ atanh = r"""Computes the inverse hyperbolic tangent of `x`, `\mathrm{tanh}^{-1}(x) = \frac{1}{2}\left(\log(1+x)-\log(1-x)\right)`. """ acoth = r"""Computes the inverse hyperbolic cotangent of `x`, `\mathrm{coth}^{-1}(x) = \tanh^{-1}(1/x)`.""" asech = r"""Computes the inverse hyperbolic secant of `x`, `\mathrm{sech}^{-1}(x) = \cosh^{-1}(1/x)`.""" acsch = r"""Computes the inverse hyperbolic cosecant of `x`, `\mathrm{csch}^{-1}(x) = \sinh^{-1}(1/x)`.""" sinpi = r""" Computes `\sin(\pi x)`, more accurately than the expression ``sin(pi*x)``:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> sinpi(10**10), sin(pi*(10**10)) (0.0, -2.23936276195592e-6) >>> sinpi(10**10+0.5), sin(pi*(10**10+0.5)) (1.0, 0.999999999998721) """ cospi = r""" Computes `\cos(\pi x)`, more accurately than the expression ``cos(pi*x)``:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> cospi(10**10), cos(pi*(10**10)) (1.0, 0.999999999997493) >>> cospi(10**10+0.5), cos(pi*(10**10+0.5)) (0.0, 1.59960492420134e-6) """ sinc = r""" ``sinc(x)`` computes the unnormalized sinc function, defined as .. math :: \mathrm{sinc}(x) = \begin{cases} \sin(x)/x, & \mbox{if } x \ne 0 \\ 1, & \mbox{if } x = 0. \end{cases} See :func:`~mpmath.sincpi` for the normalized sinc function. Simple values and limits include:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> sinc(0) 1.0 >>> sinc(1) 0.841470984807897 >>> sinc(inf) 0.0 The integral of the sinc function is the sine integral Si:: >>> quad(sinc, [0, 1]) 0.946083070367183 >>> si(1) 0.946083070367183 """ sincpi = r""" ``sincpi(x)`` computes the normalized sinc function, defined as .. math :: \mathrm{sinc}_{\pi}(x) = \begin{cases} \sin(\pi x)/(\pi x), & \mbox{if } x \ne 0 \\ 1, & \mbox{if } x = 0. \end{cases} Equivalently, we have `\mathrm{sinc}_{\pi}(x) = \mathrm{sinc}(\pi x)`. The normalization entails that the function integrates to unity over the entire real line:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> quadosc(sincpi, [-inf, inf], period=2.0) 1.0 Like, :func:`~mpmath.sinpi`, :func:`~mpmath.sincpi` is evaluated accurately at its roots:: >>> sincpi(10) 0.0 """ expj = r""" Convenience function for computing `e^{ix}`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> expj(0) (1.0 + 0.0j) >>> expj(-1) (0.5403023058681397174009366 - 0.8414709848078965066525023j) >>> expj(j) (0.3678794411714423215955238 + 0.0j) >>> expj(1+j) (0.1987661103464129406288032 + 0.3095598756531121984439128j) """ expjpi = r""" Convenience function for computing `e^{i \pi x}`. Evaluation is accurate near zeros (see also :func:`~mpmath.cospi`, :func:`~mpmath.sinpi`):: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> expjpi(0) (1.0 + 0.0j) >>> expjpi(1) (-1.0 + 0.0j) >>> expjpi(0.5) (0.0 + 1.0j) >>> expjpi(-1) (-1.0 + 0.0j) >>> expjpi(j) (0.04321391826377224977441774 + 0.0j) >>> expjpi(1+j) (-0.04321391826377224977441774 + 0.0j) """ floor = r""" Computes the floor of `x`, `\lfloor x \rfloor`, defined as the largest integer less than or equal to `x`:: >>> from mpmath import * >>> mp.pretty = False >>> floor(3.5) mpf('3.0') .. note :: :func:`~mpmath.floor`, :func:`~mpmath.ceil` and :func:`~mpmath.nint` return a floating-point number, not a Python ``int``. If `\lfloor x \rfloor` is too large to be represented exactly at the present working precision, the result will be rounded, not necessarily in the direction implied by the mathematical definition of the function. To avoid rounding, use *prec=0*:: >>> mp.dps = 15 >>> print(int(floor(10**30+1))) 1000000000000000019884624838656 >>> print(int(floor(10**30+1, prec=0))) 1000000000000000000000000000001 The floor function is defined for complex numbers and acts on the real and imaginary parts separately:: >>> floor(3.25+4.75j) mpc(real='3.0', imag='4.0') """ ceil = r""" Computes the ceiling of `x`, `\lceil x \rceil`, defined as the smallest integer greater than or equal to `x`:: >>> from mpmath import * >>> mp.pretty = False >>> ceil(3.5) mpf('4.0') The ceiling function is defined for complex numbers and acts on the real and imaginary parts separately:: >>> ceil(3.25+4.75j) mpc(real='4.0', imag='5.0') See notes about rounding for :func:`~mpmath.floor`. """ nint = r""" Evaluates the nearest integer function, `\mathrm{nint}(x)`. This gives the nearest integer to `x`; on a tie, it gives the nearest even integer:: >>> from mpmath import * >>> mp.pretty = False >>> nint(3.2) mpf('3.0') >>> nint(3.8) mpf('4.0') >>> nint(3.5) mpf('4.0') >>> nint(4.5) mpf('4.0') The nearest integer function is defined for complex numbers and acts on the real and imaginary parts separately:: >>> nint(3.25+4.75j) mpc(real='3.0', imag='5.0') See notes about rounding for :func:`~mpmath.floor`. """ frac = r""" Gives the fractional part of `x`, defined as `\mathrm{frac}(x) = x - \lfloor x \rfloor` (see :func:`~mpmath.floor`). In effect, this computes `x` modulo 1, or `x+n` where `n \in \mathbb{Z}` is such that `x+n \in [0,1)`:: >>> from mpmath import * >>> mp.pretty = False >>> frac(1.25) mpf('0.25') >>> frac(3) mpf('0.0') >>> frac(-1.25) mpf('0.75') For a complex number, the fractional part function applies to the real and imaginary parts separately:: >>> frac(2.25+3.75j) mpc(real='0.25', imag='0.75') Plotted, the fractional part function gives a sawtooth wave. The Fourier series coefficients have a simple form:: >>> mp.dps = 15 >>> nprint(fourier(lambda x: frac(x)-0.5, [0,1], 4)) ([0.0, 0.0, 0.0, 0.0, 0.0], [0.0, -0.31831, -0.159155, -0.106103, -0.0795775]) >>> nprint([-1/(pi*k) for k in range(1,5)]) [-0.31831, -0.159155, -0.106103, -0.0795775] .. note:: The fractional part is sometimes defined as a symmetric function, i.e. returning `-\mathrm{frac}(-x)` if `x < 0`. This convention is used, for instance, by Mathematica's ``FractionalPart``. """ sign = r""" Returns the sign of `x`, defined as `\mathrm{sign}(x) = x / |x|` (with the special case `\mathrm{sign}(0) = 0`):: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> sign(10) mpf('1.0') >>> sign(-10) mpf('-1.0') >>> sign(0) mpf('0.0') Note that the sign function is also defined for complex numbers, for which it gives the projection onto the unit circle:: >>> mp.dps = 15; mp.pretty = True >>> sign(1+j) (0.707106781186547 + 0.707106781186547j) """ arg = r""" Computes the complex argument (phase) of `x`, defined as the signed angle between the positive real axis and `x` in the complex plane:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> arg(3) 0.0 >>> arg(3+3j) 0.785398163397448 >>> arg(3j) 1.5707963267949 >>> arg(-3) 3.14159265358979 >>> arg(-3j) -1.5707963267949 The angle is defined to satisfy `-\pi < \arg(x) \le \pi` and with the sign convention that a nonnegative imaginary part results in a nonnegative argument. The value returned by :func:`~mpmath.arg` is an ``mpf`` instance. """ fabs = r""" Returns the absolute value of `x`, `|x|`. Unlike :func:`abs`, :func:`~mpmath.fabs` converts non-mpmath numbers (such as ``int``) into mpmath numbers:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> fabs(3) mpf('3.0') >>> fabs(-3) mpf('3.0') >>> fabs(3+4j) mpf('5.0') """ re = r""" Returns the real part of `x`, `\Re(x)`. Unlike ``x.real``, :func:`~mpmath.re` converts `x` to a mpmath number:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> re(3) mpf('3.0') >>> re(-1+4j) mpf('-1.0') """ im = r""" Returns the imaginary part of `x`, `\Im(x)`. Unlike ``x.imag``, :func:`~mpmath.im` converts `x` to a mpmath number:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> im(3) mpf('0.0') >>> im(-1+4j) mpf('4.0') """ conj = r""" Returns the complex conjugate of `x`, `\overline{x}`. Unlike ``x.conjugate()``, :func:`~mpmath.im` converts `x` to a mpmath number:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> conj(3) mpf('3.0') >>> conj(-1+4j) mpc(real='-1.0', imag='-4.0') """ polar = r""" Returns the polar representation of the complex number `z` as a pair `(r, \phi)` such that `z = r e^{i \phi}`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> polar(-2) (2.0, 3.14159265358979) >>> polar(3-4j) (5.0, -0.927295218001612) """ rect = r""" Returns the complex number represented by polar coordinates `(r, \phi)`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> chop(rect(2, pi)) -2.0 >>> rect(sqrt(2), -pi/4) (1.0 - 1.0j) """ expm1 = r""" Computes `e^x - 1`, accurately for small `x`. Unlike the expression ``exp(x) - 1``, ``expm1(x)`` does not suffer from potentially catastrophic cancellation:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> exp(1e-10)-1; print(expm1(1e-10)) 1.00000008274037e-10 1.00000000005e-10 >>> exp(1e-20)-1; print(expm1(1e-20)) 0.0 1.0e-20 >>> 1/(exp(1e-20)-1) Traceback (most recent call last): ... ZeroDivisionError >>> 1/expm1(1e-20) 1.0e+20 Evaluation works for extremely tiny values:: >>> expm1(0) 0.0 >>> expm1('1e-10000000') 1.0e-10000000 """ powm1 = r""" Computes `x^y - 1`, accurately when `x^y` is very close to 1. This avoids potentially catastrophic cancellation:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> power(0.99999995, 1e-10) - 1 0.0 >>> powm1(0.99999995, 1e-10) -5.00000012791934e-18 Powers exactly equal to 1, and only those powers, yield 0 exactly:: >>> powm1(-j, 4) (0.0 + 0.0j) >>> powm1(3, 0) 0.0 >>> powm1(fadd(-1, 1e-100, exact=True), 4) -4.0e-100 Evaluation works for extremely tiny `y`:: >>> powm1(2, '1e-100000') 6.93147180559945e-100001 >>> powm1(j, '1e-1000') (-1.23370055013617e-2000 + 1.5707963267949e-1000j) """ root = r""" ``root(z, n, k=0)`` computes an `n`-th root of `z`, i.e. returns a number `r` that (up to possible approximation error) satisfies `r^n = z`. (``nthroot`` is available as an alias for ``root``.) Every complex number `z \ne 0` has `n` distinct `n`-th roots, which are equidistant points on a circle with radius `|z|^{1/n}`, centered around the origin. A specific root may be selected using the optional index `k`. The roots are indexed counterclockwise, starting with `k = 0` for the root closest to the positive real half-axis. The `k = 0` root is the so-called principal `n`-th root, often denoted by `\sqrt[n]{z}` or `z^{1/n}`, and also given by `\exp(\log(z) / n)`. If `z` is a positive real number, the principal root is just the unique positive `n`-th root of `z`. Under some circumstances, non-principal real roots exist: for positive real `z`, `n` even, there is a negative root given by `k = n/2`; for negative real `z`, `n` odd, there is a negative root given by `k = (n-1)/2`. To obtain all roots with a simple expression, use ``[root(z,n,k) for k in range(n)]``. An important special case, ``root(1, n, k)`` returns the `k`-th `n`-th root of unity, `\zeta_k = e^{2 \pi i k / n}`. Alternatively, :func:`~mpmath.unitroots` provides a slightly more convenient way to obtain the roots of unity, including the option to compute only the primitive roots of unity. Both `k` and `n` should be integers; `k` outside of ``range(n)`` will be reduced modulo `n`. If `n` is negative, `x^{-1/n} = 1/x^{1/n}` (or the equivalent reciprocal for a non-principal root with `k \ne 0`) is computed. :func:`~mpmath.root` is implemented to use Newton's method for small `n`. At high precision, this makes `x^{1/n}` not much more expensive than the regular exponentiation, `x^n`. For very large `n`, :func:`~mpmath.nthroot` falls back to use the exponential function. **Examples** :func:`~mpmath.nthroot`/:func:`~mpmath.root` is faster and more accurate than raising to a floating-point fraction:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> 16807 ** (mpf(1)/5) mpf('7.0000000000000009') >>> root(16807, 5) mpf('7.0') >>> nthroot(16807, 5) # Alias mpf('7.0') A high-precision root:: >>> mp.dps = 50; mp.pretty = True >>> nthroot(10, 5) 1.584893192461113485202101373391507013269442133825 >>> nthroot(10, 5) ** 5 10.0 Computing principal and non-principal square and cube roots:: >>> mp.dps = 15 >>> root(10, 2) 3.16227766016838 >>> root(10, 2, 1) -3.16227766016838 >>> root(-10, 3) (1.07721734501594 + 1.86579517236206j) >>> root(-10, 3, 1) -2.15443469003188 >>> root(-10, 3, 2) (1.07721734501594 - 1.86579517236206j) All the 7th roots of a complex number:: >>> for r in [root(3+4j, 7, k) for k in range(7)]: ... print("%s %s" % (r, r**7)) ... (1.24747270589553 + 0.166227124177353j) (3.0 + 4.0j) (0.647824911301003 + 1.07895435170559j) (3.0 + 4.0j) (-0.439648254723098 + 1.17920694574172j) (3.0 + 4.0j) (-1.19605731775069 + 0.391492658196305j) (3.0 + 4.0j) (-1.05181082538903 - 0.691023585965793j) (3.0 + 4.0j) (-0.115529328478668 - 1.25318497558335j) (3.0 + 4.0j) (0.907748109144957 - 0.871672518271819j) (3.0 + 4.0j) Cube roots of unity:: >>> for k in range(3): print(root(1, 3, k)) ... 1.0 (-0.5 + 0.866025403784439j) (-0.5 - 0.866025403784439j) Some exact high order roots:: >>> root(75**210, 105) 5625.0 >>> root(1, 128, 96) (0.0 - 1.0j) >>> root(4**128, 128, 96) (0.0 - 4.0j) """ unitroots = r""" ``unitroots(n)`` returns `\zeta_0, \zeta_1, \ldots, \zeta_{n-1}`, all the distinct `n`-th roots of unity, as a list. If the option *primitive=True* is passed, only the primitive roots are returned. Every `n`-th root of unity satisfies `(\zeta_k)^n = 1`. There are `n` distinct roots for each `n` (`\zeta_k` and `\zeta_j` are the same when `k = j \pmod n`), which form a regular polygon with vertices on the unit circle. They are ordered counterclockwise with increasing `k`, starting with `\zeta_0 = 1`. **Examples** The roots of unity up to `n = 4`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> nprint(unitroots(1)) [1.0] >>> nprint(unitroots(2)) [1.0, -1.0] >>> nprint(unitroots(3)) [1.0, (-0.5 + 0.866025j), (-0.5 - 0.866025j)] >>> nprint(unitroots(4)) [1.0, (0.0 + 1.0j), -1.0, (0.0 - 1.0j)] Roots of unity form a geometric series that sums to 0:: >>> mp.dps = 50 >>> chop(fsum(unitroots(25))) 0.0 Primitive roots up to `n = 4`:: >>> mp.dps = 15 >>> nprint(unitroots(1, primitive=True)) [1.0] >>> nprint(unitroots(2, primitive=True)) [-1.0] >>> nprint(unitroots(3, primitive=True)) [(-0.5 + 0.866025j), (-0.5 - 0.866025j)] >>> nprint(unitroots(4, primitive=True)) [(0.0 + 1.0j), (0.0 - 1.0j)] There are only four primitive 12th roots:: >>> nprint(unitroots(12, primitive=True)) [(0.866025 + 0.5j), (-0.866025 + 0.5j), (-0.866025 - 0.5j), (0.866025 - 0.5j)] The `n`-th roots of unity form a group, the cyclic group of order `n`. Any primitive root `r` is a generator for this group, meaning that `r^0, r^1, \ldots, r^{n-1}` gives the whole set of unit roots (in some permuted order):: >>> for r in unitroots(6): print(r) ... 1.0 (0.5 + 0.866025403784439j) (-0.5 + 0.866025403784439j) -1.0 (-0.5 - 0.866025403784439j) (0.5 - 0.866025403784439j) >>> r = unitroots(6, primitive=True)[1] >>> for k in range(6): print(chop(r**k)) ... 1.0 (0.5 - 0.866025403784439j) (-0.5 - 0.866025403784439j) -1.0 (-0.5 + 0.866025403784438j) (0.5 + 0.866025403784438j) The number of primitive roots equals the Euler totient function `\phi(n)`:: >>> [len(unitroots(n, primitive=True)) for n in range(1,20)] [1, 1, 2, 2, 4, 2, 6, 4, 6, 4, 10, 4, 12, 6, 8, 8, 16, 6, 18] """ log = r""" Computes the base-`b` logarithm of `x`, `\log_b(x)`. If `b` is unspecified, :func:`~mpmath.log` computes the natural (base `e`) logarithm and is equivalent to :func:`~mpmath.ln`. In general, the base `b` logarithm is defined in terms of the natural logarithm as `\log_b(x) = \ln(x)/\ln(b)`. By convention, we take `\log(0) = -\infty`. The natural logarithm is real if `x > 0` and complex if `x < 0` or if `x` is complex. The principal branch of the complex logarithm is used, meaning that `\Im(\ln(x)) = -\pi < \arg(x) \le \pi`. **Examples** Some basic values and limits:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> log(1) 0.0 >>> log(2) 0.693147180559945 >>> log(1000,10) 3.0 >>> log(4, 16) 0.5 >>> log(j) (0.0 + 1.5707963267949j) >>> log(-1) (0.0 + 3.14159265358979j) >>> log(0) -inf >>> log(inf) +inf The natural logarithm is the antiderivative of `1/x`:: >>> quad(lambda x: 1/x, [1, 5]) 1.6094379124341 >>> log(5) 1.6094379124341 >>> diff(log, 10) 0.1 The Taylor series expansion of the natural logarithm around `x = 1` has coefficients `(-1)^{n+1}/n`:: >>> nprint(taylor(log, 1, 7)) [0.0, 1.0, -0.5, 0.333333, -0.25, 0.2, -0.166667, 0.142857] :func:`~mpmath.log` supports arbitrary precision evaluation:: >>> mp.dps = 50 >>> log(pi) 1.1447298858494001741434273513530587116472948129153 >>> log(pi, pi**3) 0.33333333333333333333333333333333333333333333333333 >>> mp.dps = 25 >>> log(3+4j) (1.609437912434100374600759 + 0.9272952180016122324285125j) """ log10 = r""" Computes the base-10 logarithm of `x`, `\log_{10}(x)`. ``log10(x)`` is equivalent to ``log(x, 10)``. """ fmod = r""" Converts `x` and `y` to mpmath numbers and returns `x \mod y`. For mpmath numbers, this is equivalent to ``x % y``. >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> fmod(100, pi) 2.61062773871641 You can use :func:`~mpmath.fmod` to compute fractional parts of numbers:: >>> fmod(10.25, 1) 0.25 """ radians = r""" Converts the degree angle `x` to radians:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> radians(60) 1.0471975511966 """ degrees = r""" Converts the radian angle `x` to a degree angle:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> degrees(pi/3) 60.0 """ atan2 = r""" Computes the two-argument arctangent, `\mathrm{atan2}(y, x)`, giving the signed angle between the positive `x`-axis and the point `(x, y)` in the 2D plane. This function is defined for real `x` and `y` only. The two-argument arctangent essentially computes `\mathrm{atan}(y/x)`, but accounts for the signs of both `x` and `y` to give the angle for the correct quadrant. The following examples illustrate the difference:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> atan2(1,1), atan(1/1.) (0.785398163397448, 0.785398163397448) >>> atan2(1,-1), atan(1/-1.) (2.35619449019234, -0.785398163397448) >>> atan2(-1,1), atan(-1/1.) (-0.785398163397448, -0.785398163397448) >>> atan2(-1,-1), atan(-1/-1.) (-2.35619449019234, 0.785398163397448) The angle convention is the same as that used for the complex argument; see :func:`~mpmath.arg`. """ fibonacci = r""" ``fibonacci(n)`` computes the `n`-th Fibonacci number, `F(n)`. The Fibonacci numbers are defined by the recurrence `F(n) = F(n-1) + F(n-2)` with the initial values `F(0) = 0`, `F(1) = 1`. :func:`~mpmath.fibonacci` extends this definition to arbitrary real and complex arguments using the formula .. math :: F(z) = \frac{\phi^z - \cos(\pi z) \phi^{-z}}{\sqrt 5} where `\phi` is the golden ratio. :func:`~mpmath.fibonacci` also uses this continuous formula to compute `F(n)` for extremely large `n`, where calculating the exact integer would be wasteful. For convenience, :func:`~mpmath.fib` is available as an alias for :func:`~mpmath.fibonacci`. **Basic examples** Some small Fibonacci numbers are:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for i in range(10): ... print(fibonacci(i)) ... 0.0 1.0 1.0 2.0 3.0 5.0 8.0 13.0 21.0 34.0 >>> fibonacci(50) 12586269025.0 The recurrence for `F(n)` extends backwards to negative `n`:: >>> for i in range(10): ... print(fibonacci(-i)) ... 0.0 1.0 -1.0 2.0 -3.0 5.0 -8.0 13.0 -21.0 34.0 Large Fibonacci numbers will be computed approximately unless the precision is set high enough:: >>> fib(200) 2.8057117299251e+41 >>> mp.dps = 45 >>> fib(200) 280571172992510140037611932413038677189525.0 :func:`~mpmath.fibonacci` can compute approximate Fibonacci numbers of stupendous size:: >>> mp.dps = 15 >>> fibonacci(10**25) 3.49052338550226e+2089876402499787337692720 **Real and complex arguments** The extended Fibonacci function is an analytic function. The property `F(z) = F(z-1) + F(z-2)` holds for arbitrary `z`:: >>> mp.dps = 15 >>> fib(pi) 2.1170270579161 >>> fib(pi-1) + fib(pi-2) 2.1170270579161 >>> fib(3+4j) (-5248.51130728372 - 14195.962288353j) >>> fib(2+4j) + fib(1+4j) (-5248.51130728372 - 14195.962288353j) The Fibonacci function has infinitely many roots on the negative half-real axis. The first root is at 0, the second is close to -0.18, and then there are infinitely many roots that asymptotically approach `-n+1/2`:: >>> findroot(fib, -0.2) -0.183802359692956 >>> findroot(fib, -2) -1.57077646820395 >>> findroot(fib, -17) -16.4999999596115 >>> findroot(fib, -24) -23.5000000000479 **Mathematical relationships** For large `n`, `F(n+1)/F(n)` approaches the golden ratio:: >>> mp.dps = 50 >>> fibonacci(101)/fibonacci(100) 1.6180339887498948482045868343656381177203127439638 >>> +phi 1.6180339887498948482045868343656381177203091798058 The sum of reciprocal Fibonacci numbers converges to an irrational number for which no closed form expression is known:: >>> mp.dps = 15 >>> nsum(lambda n: 1/fib(n), [1, inf]) 3.35988566624318 Amazingly, however, the sum of odd-index reciprocal Fibonacci numbers can be expressed in terms of a Jacobi theta function:: >>> nsum(lambda n: 1/fib(2*n+1), [0, inf]) 1.82451515740692 >>> sqrt(5)*jtheta(2,0,(3-sqrt(5))/2)**2/4 1.82451515740692 Some related sums can be done in closed form:: >>> nsum(lambda k: 1/(1+fib(2*k+1)), [0, inf]) 1.11803398874989 >>> phi - 0.5 1.11803398874989 >>> f = lambda k:(-1)**(k+1) / sum(fib(n)**2 for n in range(1,int(k+1))) >>> nsum(f, [1, inf]) 0.618033988749895 >>> phi-1 0.618033988749895 **References** 1. http://mathworld.wolfram.com/FibonacciNumber.html """ altzeta = r""" Gives the Dirichlet eta function, `\eta(s)`, also known as the alternating zeta function. This function is defined in analogy with the Riemann zeta function as providing the sum of the alternating series .. math :: \eta(s) = \sum_{k=0}^{\infty} \frac{(-1)^k}{k^s} = 1-\frac{1}{2^s}+\frac{1}{3^s}-\frac{1}{4^s}+\ldots The eta function, unlike the Riemann zeta function, is an entire function, having a finite value for all complex `s`. The special case `\eta(1) = \log(2)` gives the value of the alternating harmonic series. The alternating zeta function may expressed using the Riemann zeta function as `\eta(s) = (1 - 2^{1-s}) \zeta(s)`. It can also be expressed in terms of the Hurwitz zeta function, for example using :func:`~mpmath.dirichlet` (see documentation for that function). **Examples** Some special values are:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> altzeta(1) 0.693147180559945 >>> altzeta(0) 0.5 >>> altzeta(-1) 0.25 >>> altzeta(-2) 0.0 An example of a sum that can be computed more accurately and efficiently via :func:`~mpmath.altzeta` than via numerical summation:: >>> sum(-(-1)**n / n**2.5 for n in range(1, 100)) 0.86720495150398402 >>> altzeta(2.5) 0.867199889012184 At positive even integers, the Dirichlet eta function evaluates to a rational multiple of a power of `\pi`:: >>> altzeta(2) 0.822467033424113 >>> pi**2/12 0.822467033424113 Like the Riemann zeta function, `\eta(s)`, approaches 1 as `s` approaches positive infinity, although it does so from below rather than from above:: >>> altzeta(30) 0.999999999068682 >>> altzeta(inf) 1.0 >>> mp.pretty = False >>> altzeta(1000, rounding='d') mpf('0.99999999999999989') >>> altzeta(1000, rounding='u') mpf('1.0') **References** 1. http://mathworld.wolfram.com/DirichletEtaFunction.html 2. http://en.wikipedia.org/wiki/Dirichlet_eta_function """ factorial = r""" Computes the factorial, `x!`. For integers `n \ge 0`, we have `n! = 1 \cdot 2 \cdots (n-1) \cdot n` and more generally the factorial is defined for real or complex `x` by `x! = \Gamma(x+1)`. **Examples** Basic values and limits:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for k in range(6): ... print("%s %s" % (k, fac(k))) ... 0 1.0 1 1.0 2 2.0 3 6.0 4 24.0 5 120.0 >>> fac(inf) +inf >>> fac(0.5), sqrt(pi)/2 (0.886226925452758, 0.886226925452758) For large positive `x`, `x!` can be approximated by Stirling's formula:: >>> x = 10**10 >>> fac(x) 2.32579620567308e+95657055186 >>> sqrt(2*pi*x)*(x/e)**x 2.32579597597705e+95657055186 :func:`~mpmath.fac` supports evaluation for astronomically large values:: >>> fac(10**30) 6.22311232304258e+29565705518096748172348871081098 Reciprocal factorials appear in the Taylor series of the exponential function (among many other contexts):: >>> nsum(lambda k: 1/fac(k), [0, inf]), exp(1) (2.71828182845905, 2.71828182845905) >>> nsum(lambda k: pi**k/fac(k), [0, inf]), exp(pi) (23.1406926327793, 23.1406926327793) """ gamma = r""" Computes the gamma function, `\Gamma(x)`. The gamma function is a shifted version of the ordinary factorial, satisfying `\Gamma(n) = (n-1)!` for integers `n > 0`. More generally, it is defined by .. math :: \Gamma(x) = \int_0^{\infty} t^{x-1} e^{-t}\, dt for any real or complex `x` with `\Re(x) > 0` and for `\Re(x) < 0` by analytic continuation. **Examples** Basic values and limits:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for k in range(1, 6): ... print("%s %s" % (k, gamma(k))) ... 1 1.0 2 1.0 3 2.0 4 6.0 5 24.0 >>> gamma(inf) +inf >>> gamma(0) Traceback (most recent call last): ... ValueError: gamma function pole The gamma function of a half-integer is a rational multiple of `\sqrt{\pi}`:: >>> gamma(0.5), sqrt(pi) (1.77245385090552, 1.77245385090552) >>> gamma(1.5), sqrt(pi)/2 (0.886226925452758, 0.886226925452758) We can check the integral definition:: >>> gamma(3.5) 3.32335097044784 >>> quad(lambda t: t**2.5*exp(-t), [0,inf]) 3.32335097044784 :func:`~mpmath.gamma` supports arbitrary-precision evaluation and complex arguments:: >>> mp.dps = 50 >>> gamma(sqrt(3)) 0.91510229697308632046045539308226554038315280564184 >>> mp.dps = 25 >>> gamma(2j) (0.009902440080927490985955066 - 0.07595200133501806872408048j) Arguments can also be large. Note that the gamma function grows very quickly:: >>> mp.dps = 15 >>> gamma(10**20) 1.9328495143101e+1956570551809674817225 """ psi = r""" Gives the polygamma function of order `m` of `z`, `\psi^{(m)}(z)`. Special cases are known as the *digamma function* (`\psi^{(0)}(z)`), the *trigamma function* (`\psi^{(1)}(z)`), etc. The polygamma functions are defined as the logarithmic derivatives of the gamma function: .. math :: \psi^{(m)}(z) = \left(\frac{d}{dz}\right)^{m+1} \log \Gamma(z) In particular, `\psi^{(0)}(z) = \Gamma'(z)/\Gamma(z)`. In the present implementation of :func:`~mpmath.psi`, the order `m` must be a nonnegative integer, while the argument `z` may be an arbitrary complex number (with exception for the polygamma function's poles at `z = 0, -1, -2, \ldots`). **Examples** For various rational arguments, the polygamma function reduces to a combination of standard mathematical constants:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> psi(0, 1), -euler (-0.5772156649015328606065121, -0.5772156649015328606065121) >>> psi(1, '1/4'), pi**2+8*catalan (17.19732915450711073927132, 17.19732915450711073927132) >>> psi(2, '1/2'), -14*apery (-16.82879664423431999559633, -16.82879664423431999559633) The polygamma functions are derivatives of each other:: >>> diff(lambda x: psi(3, x), pi), psi(4, pi) (-0.1105749312578862734526952, -0.1105749312578862734526952) >>> quad(lambda x: psi(4, x), [2, 3]), psi(3,3)-psi(3,2) (-0.375, -0.375) The digamma function diverges logarithmically as `z \to \infty`, while higher orders tend to zero:: >>> psi(0,inf), psi(1,inf), psi(2,inf) (+inf, 0.0, 0.0) Evaluation for a complex argument:: >>> psi(2, -1-2j) (0.03902435405364952654838445 + 0.1574325240413029954685366j) Evaluation is supported for large orders `m` and/or large arguments `z`:: >>> psi(3, 10**100) 2.0e-300 >>> psi(250, 10**30+10**20*j) (-1.293142504363642687204865e-7010 + 3.232856260909107391513108e-7018j) **Application to infinite series** Any infinite series where the summand is a rational function of the index `k` can be evaluated in closed form in terms of polygamma functions of the roots and poles of the summand:: >>> a = sqrt(2) >>> b = sqrt(3) >>> nsum(lambda k: 1/((k+a)**2*(k+b)), [0, inf]) 0.4049668927517857061917531 >>> (psi(0,a)-psi(0,b)-a*psi(1,a)+b*psi(1,a))/(a-b)**2 0.4049668927517857061917531 This follows from the series representation (`m > 0`) .. math :: \psi^{(m)}(z) = (-1)^{m+1} m! \sum_{k=0}^{\infty} \frac{1}{(z+k)^{m+1}}. Since the roots of a polynomial may be complex, it is sometimes necessary to use the complex polygamma function to evaluate an entirely real-valued sum:: >>> nsum(lambda k: 1/(k**2-2*k+3), [0, inf]) 1.694361433907061256154665 >>> nprint(polyroots([1,-2,3])) [(1.0 - 1.41421j), (1.0 + 1.41421j)] >>> r1 = 1-sqrt(2)*j >>> r2 = r1.conjugate() >>> (psi(0,-r2)-psi(0,-r1))/(r1-r2) (1.694361433907061256154665 + 0.0j) """ digamma = r""" Shortcut for ``psi(0,z)``. """ harmonic = r""" If `n` is an integer, ``harmonic(n)`` gives a floating-point approximation of the `n`-th harmonic number `H(n)`, defined as .. math :: H(n) = 1 + \frac{1}{2} + \frac{1}{3} + \ldots + \frac{1}{n} The first few harmonic numbers are:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for n in range(8): ... print("%s %s" % (n, harmonic(n))) ... 0 0.0 1 1.0 2 1.5 3 1.83333333333333 4 2.08333333333333 5 2.28333333333333 6 2.45 7 2.59285714285714 The infinite harmonic series `1 + 1/2 + 1/3 + \ldots` diverges:: >>> harmonic(inf) +inf :func:`~mpmath.harmonic` is evaluated using the digamma function rather than by summing the harmonic series term by term. It can therefore be computed quickly for arbitrarily large `n`, and even for nonintegral arguments:: >>> harmonic(10**100) 230.835724964306 >>> harmonic(0.5) 0.613705638880109 >>> harmonic(3+4j) (2.24757548223494 + 0.850502209186044j) :func:`~mpmath.harmonic` supports arbitrary precision evaluation:: >>> mp.dps = 50 >>> harmonic(11) 3.0198773448773448773448773448773448773448773448773 >>> harmonic(pi) 1.8727388590273302654363491032336134987519132374152 The harmonic series diverges, but at a glacial pace. It is possible to calculate the exact number of terms required before the sum exceeds a given amount, say 100:: >>> mp.dps = 50 >>> v = 10**findroot(lambda x: harmonic(10**x) - 100, 10) >>> v 15092688622113788323693563264538101449859496.864101 >>> v = int(ceil(v)) >>> print(v) 15092688622113788323693563264538101449859497 >>> harmonic(v-1) 99.999999999999999999999999999999999999999999942747 >>> harmonic(v) 100.000000000000000000000000000000000000000000009 """ bernoulli = r""" Computes the nth Bernoulli number, `B_n`, for any integer `n \ge 0`. The Bernoulli numbers are rational numbers, but this function returns a floating-point approximation. To obtain an exact fraction, use :func:`~mpmath.bernfrac` instead. **Examples** Numerical values of the first few Bernoulli numbers:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for n in range(15): ... print("%s %s" % (n, bernoulli(n))) ... 0 1.0 1 -0.5 2 0.166666666666667 3 0.0 4 -0.0333333333333333 5 0.0 6 0.0238095238095238 7 0.0 8 -0.0333333333333333 9 0.0 10 0.0757575757575758 11 0.0 12 -0.253113553113553 13 0.0 14 1.16666666666667 Bernoulli numbers can be approximated with arbitrary precision:: >>> mp.dps = 50 >>> bernoulli(100) -2.8382249570693706959264156336481764738284680928013e+78 Arbitrarily large `n` are supported:: >>> mp.dps = 15 >>> bernoulli(10**20 + 2) 3.09136296657021e+1876752564973863312327 The Bernoulli numbers are related to the Riemann zeta function at integer arguments:: >>> -bernoulli(8) * (2*pi)**8 / (2*fac(8)) 1.00407735619794 >>> zeta(8) 1.00407735619794 **Algorithm** For small `n` (`n < 3000`) :func:`~mpmath.bernoulli` uses a recurrence formula due to Ramanujan. All results in this range are cached, so sequential computation of small Bernoulli numbers is guaranteed to be fast. For larger `n`, `B_n` is evaluated in terms of the Riemann zeta function. """ stieltjes = r""" For a nonnegative integer `n`, ``stieltjes(n)`` computes the `n`-th Stieltjes constant `\gamma_n`, defined as the `n`-th coefficient in the Laurent series expansion of the Riemann zeta function around the pole at `s = 1`. That is, we have: .. math :: \zeta(s) = \frac{1}{s-1} \sum_{n=0}^{\infty} \frac{(-1)^n}{n!} \gamma_n (s-1)^n More generally, ``stieltjes(n, a)`` gives the corresponding coefficient `\gamma_n(a)` for the Hurwitz zeta function `\zeta(s,a)` (with `\gamma_n = \gamma_n(1)`). **Examples** The zeroth Stieltjes constant is just Euler's constant `\gamma`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> stieltjes(0) 0.577215664901533 Some more values are:: >>> stieltjes(1) -0.0728158454836767 >>> stieltjes(10) 0.000205332814909065 >>> stieltjes(30) 0.00355772885557316 >>> stieltjes(1000) -1.57095384420474e+486 >>> stieltjes(2000) 2.680424678918e+1109 >>> stieltjes(1, 2.5) -0.23747539175716 An alternative way to compute `\gamma_1`:: >>> diff(extradps(15)(lambda x: 1/(x-1) - zeta(x)), 1) -0.0728158454836767 :func:`~mpmath.stieltjes` supports arbitrary precision evaluation:: >>> mp.dps = 50 >>> stieltjes(2) -0.0096903631928723184845303860352125293590658061013408 **Algorithm** :func:`~mpmath.stieltjes` numerically evaluates the integral in the following representation due to Ainsworth, Howell and Coffey [1], [2]: .. math :: \gamma_n(a) = \frac{\log^n a}{2a} - \frac{\log^{n+1}(a)}{n+1} + \frac{2}{a} \Re \int_0^{\infty} \frac{(x/a-i)\log^n(a-ix)}{(1+x^2/a^2)(e^{2\pi x}-1)} dx. For some reference values with `a = 1`, see e.g. [4]. **References** 1. O. R. Ainsworth & L. W. Howell, "An integral representation of the generalized Euler-Mascheroni constants", NASA Technical Paper 2456 (1985), http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19850014994_1985014994.pdf 2. M. W. Coffey, "The Stieltjes constants, their relation to the `\eta_j` coefficients, and representation of the Hurwitz zeta function", arXiv:0706.0343v1 http://arxiv.org/abs/0706.0343 3. http://mathworld.wolfram.com/StieltjesConstants.html 4. http://pi.lacim.uqam.ca/piDATA/stieltjesgamma.txt """ gammaprod = r""" Given iterables `a` and `b`, ``gammaprod(a, b)`` computes the product / quotient of gamma functions: .. math :: \frac{\Gamma(a_0) \Gamma(a_1) \cdots \Gamma(a_p)} {\Gamma(b_0) \Gamma(b_1) \cdots \Gamma(b_q)} Unlike direct calls to :func:`~mpmath.gamma`, :func:`~mpmath.gammaprod` considers the entire product as a limit and evaluates this limit properly if any of the numerator or denominator arguments are nonpositive integers such that poles of the gamma function are encountered. That is, :func:`~mpmath.gammaprod` evaluates .. math :: \lim_{\epsilon \to 0} \frac{\Gamma(a_0+\epsilon) \Gamma(a_1+\epsilon) \cdots \Gamma(a_p+\epsilon)} {\Gamma(b_0+\epsilon) \Gamma(b_1+\epsilon) \cdots \Gamma(b_q+\epsilon)} In particular: * If there are equally many poles in the numerator and the denominator, the limit is a rational number times the remaining, regular part of the product. * If there are more poles in the numerator, :func:`~mpmath.gammaprod` returns ``+inf``. * If there are more poles in the denominator, :func:`~mpmath.gammaprod` returns 0. **Examples** The reciprocal gamma function `1/\Gamma(x)` evaluated at `x = 0`:: >>> from mpmath import * >>> mp.dps = 15 >>> gammaprod([], [0]) 0.0 A limit:: >>> gammaprod([-4], [-3]) -0.25 >>> limit(lambda x: gamma(x-1)/gamma(x), -3, direction=1) -0.25 >>> limit(lambda x: gamma(x-1)/gamma(x), -3, direction=-1) -0.25 """ beta = r""" Computes the beta function, `B(x,y) = \Gamma(x) \Gamma(y) / \Gamma(x+y)`. The beta function is also commonly defined by the integral representation .. math :: B(x,y) = \int_0^1 t^{x-1} (1-t)^{y-1} \, dt **Examples** For integer and half-integer arguments where all three gamma functions are finite, the beta function becomes either rational number or a rational multiple of `\pi`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> beta(5, 2) 0.0333333333333333 >>> beta(1.5, 2) 0.266666666666667 >>> 16*beta(2.5, 1.5) 3.14159265358979 Where appropriate, :func:`~mpmath.beta` evaluates limits. A pole of the beta function is taken to result in ``+inf``:: >>> beta(-0.5, 0.5) 0.0 >>> beta(-3, 3) -0.333333333333333 >>> beta(-2, 3) +inf >>> beta(inf, 1) 0.0 >>> beta(inf, 0) nan :func:`~mpmath.beta` supports complex numbers and arbitrary precision evaluation:: >>> beta(1, 2+j) (0.4 - 0.2j) >>> mp.dps = 25 >>> beta(j,0.5) (1.079424249270925780135675 - 1.410032405664160838288752j) >>> mp.dps = 50 >>> beta(pi, e) 0.037890298781212201348153837138927165984170287886464 Various integrals can be computed by means of the beta function:: >>> mp.dps = 15 >>> quad(lambda t: t**2.5*(1-t)**2, [0, 1]) 0.0230880230880231 >>> beta(3.5, 3) 0.0230880230880231 >>> quad(lambda t: sin(t)**4 * sqrt(cos(t)), [0, pi/2]) 0.319504062596158 >>> beta(2.5, 0.75)/2 0.319504062596158 """ betainc = r""" ``betainc(a, b, x1=0, x2=1, regularized=False)`` gives the generalized incomplete beta function, .. math :: I_{x_1}^{x_2}(a,b) = \int_{x_1}^{x_2} t^{a-1} (1-t)^{b-1} dt. When `x_1 = 0, x_2 = 1`, this reduces to the ordinary (complete) beta function `B(a,b)`; see :func:`~mpmath.beta`. With the keyword argument ``regularized=True``, :func:`~mpmath.betainc` computes the regularized incomplete beta function `I_{x_1}^{x_2}(a,b) / B(a,b)`. This is the cumulative distribution of the beta distribution with parameters `a`, `b`. .. note : Implementations of the incomplete beta function in some other software uses a different argument order. For example, Mathematica uses the reversed argument order ``Beta[x1,x2,a,b]``. For the equivalent of SciPy's three-argument incomplete beta integral (implicitly with `x1 = 0`), use ``betainc(a,b,0,x2,regularized=True)``. **Examples** Verifying that :func:`~mpmath.betainc` computes the integral in the definition:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> x,y,a,b = 3, 4, 0, 6 >>> betainc(x, y, a, b) -4010.4 >>> quad(lambda t: t**(x-1) * (1-t)**(y-1), [a, b]) -4010.4 The arguments may be arbitrary complex numbers:: >>> betainc(0.75, 1-4j, 0, 2+3j) (0.2241657956955709603655887 + 0.3619619242700451992411724j) With regularization:: >>> betainc(1, 2, 0, 0.25, regularized=True) 0.4375 >>> betainc(pi, e, 0, 1, regularized=True) # Complete 1.0 The beta integral satisfies some simple argument transformation symmetries:: >>> mp.dps = 15 >>> betainc(2,3,4,5), -betainc(2,3,5,4), betainc(3,2,1-5,1-4) (56.0833333333333, 56.0833333333333, 56.0833333333333) The beta integral can often be evaluated analytically. For integer and rational arguments, the incomplete beta function typically reduces to a simple algebraic-logarithmic expression:: >>> mp.dps = 25 >>> identify(chop(betainc(0, 0, 3, 4))) '-(log((9/8)))' >>> identify(betainc(2, 3, 4, 5)) '(673/12)' >>> identify(betainc(1.5, 1, 1, 2)) '((-12+sqrt(1152))/18)' """ binomial = r""" Computes the binomial coefficient .. math :: {n \choose k} = \frac{n!}{k!(n-k)!}. The binomial coefficient gives the number of ways that `k` items can be chosen from a set of `n` items. More generally, the binomial coefficient is a well-defined function of arbitrary real or complex `n` and `k`, via the gamma function. **Examples** Generate Pascal's triangle:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for n in range(5): ... nprint([binomial(n,k) for k in range(n+1)]) ... [1.0] [1.0, 1.0] [1.0, 2.0, 1.0] [1.0, 3.0, 3.0, 1.0] [1.0, 4.0, 6.0, 4.0, 1.0] There is 1 way to select 0 items from the empty set, and 0 ways to select 1 item from the empty set:: >>> binomial(0, 0) 1.0 >>> binomial(0, 1) 0.0 :func:`~mpmath.binomial` supports large arguments:: >>> binomial(10**20, 10**20-5) 8.33333333333333e+97 >>> binomial(10**20, 10**10) 2.60784095465201e+104342944813 Nonintegral binomial coefficients find use in series expansions:: >>> nprint(taylor(lambda x: (1+x)**0.25, 0, 4)) [1.0, 0.25, -0.09375, 0.0546875, -0.0375977] >>> nprint([binomial(0.25, k) for k in range(5)]) [1.0, 0.25, -0.09375, 0.0546875, -0.0375977] An integral representation:: >>> n, k = 5, 3 >>> f = lambda t: exp(-j*k*t)*(1+exp(j*t))**n >>> chop(quad(f, [-pi,pi])/(2*pi)) 10.0 >>> binomial(n,k) 10.0 """ rf = r""" Computes the rising factorial or Pochhammer symbol, .. math :: x^{(n)} = x (x+1) \cdots (x+n-1) = \frac{\Gamma(x+n)}{\Gamma(x)} where the rightmost expression is valid for nonintegral `n`. **Examples** For integral `n`, the rising factorial is a polynomial:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for n in range(5): ... nprint(taylor(lambda x: rf(x,n), 0, n)) ... [1.0] [0.0, 1.0] [0.0, 1.0, 1.0] [0.0, 2.0, 3.0, 1.0] [0.0, 6.0, 11.0, 6.0, 1.0] Evaluation is supported for arbitrary arguments:: >>> rf(2+3j, 5.5) (-7202.03920483347 - 3777.58810701527j) """ ff = r""" Computes the falling factorial, .. math :: (x)_n = x (x-1) \cdots (x-n+1) = \frac{\Gamma(x+1)}{\Gamma(x-n+1)} where the rightmost expression is valid for nonintegral `n`. **Examples** For integral `n`, the falling factorial is a polynomial:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for n in range(5): ... nprint(taylor(lambda x: ff(x,n), 0, n)) ... [1.0] [0.0, 1.0] [0.0, -1.0, 1.0] [0.0, 2.0, -3.0, 1.0] [0.0, -6.0, 11.0, -6.0, 1.0] Evaluation is supported for arbitrary arguments:: >>> ff(2+3j, 5.5) (-720.41085888203 + 316.101124983878j) """ fac2 = r""" Computes the double factorial `x!!`, defined for integers `x > 0` by .. math :: x!! = \begin{cases} 1 \cdot 3 \cdots (x-2) \cdot x & x \;\mathrm{odd} \\ 2 \cdot 4 \cdots (x-2) \cdot x & x \;\mathrm{even} \end{cases} and more generally by [1] .. math :: x!! = 2^{x/2} \left(\frac{\pi}{2}\right)^{(\cos(\pi x)-1)/4} \Gamma\left(\frac{x}{2}+1\right). **Examples** The integer sequence of double factorials begins:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> nprint([fac2(n) for n in range(10)]) [1.0, 1.0, 2.0, 3.0, 8.0, 15.0, 48.0, 105.0, 384.0, 945.0] For large `x`, double factorials follow a Stirling-like asymptotic approximation:: >>> x = mpf(10000) >>> fac2(x) 5.97272691416282e+17830 >>> sqrt(pi)*x**((x+1)/2)*exp(-x/2) 5.97262736954392e+17830 The recurrence formula `x!! = x (x-2)!!` can be reversed to define the double factorial of negative odd integers (but not negative even integers):: >>> fac2(-1), fac2(-3), fac2(-5), fac2(-7) (1.0, -1.0, 0.333333333333333, -0.0666666666666667) >>> fac2(-2) Traceback (most recent call last): ... ValueError: gamma function pole With the exception of the poles at negative even integers, :func:`~mpmath.fac2` supports evaluation for arbitrary complex arguments. The recurrence formula is valid generally:: >>> fac2(pi+2j) (-1.3697207890154e-12 + 3.93665300979176e-12j) >>> (pi+2j)*fac2(pi-2+2j) (-1.3697207890154e-12 + 3.93665300979176e-12j) Double factorials should not be confused with nested factorials, which are immensely larger:: >>> fac(fac(20)) 5.13805976125208e+43675043585825292774 >>> fac2(20) 3715891200.0 Double factorials appear, among other things, in series expansions of Gaussian functions and the error function. Infinite series include:: >>> nsum(lambda k: 1/fac2(k), [0, inf]) 3.05940740534258 >>> sqrt(e)*(1+sqrt(pi/2)*erf(sqrt(2)/2)) 3.05940740534258 >>> nsum(lambda k: 2**k/fac2(2*k-1), [1, inf]) 4.06015693855741 >>> e * erf(1) * sqrt(pi) 4.06015693855741 A beautiful Ramanujan sum:: >>> nsum(lambda k: (-1)**k*(fac2(2*k-1)/fac2(2*k))**3, [0,inf]) 0.90917279454693 >>> (gamma('9/8')/gamma('5/4')/gamma('7/8'))**2 0.90917279454693 **References** 1. http://functions.wolfram.com/GammaBetaErf/Factorial2/27/01/0002/ 2. http://mathworld.wolfram.com/DoubleFactorial.html """ hyper = r""" Evaluates the generalized hypergeometric function .. math :: \,_pF_q(a_1,\ldots,a_p; b_1,\ldots,b_q; z) = \sum_{n=0}^\infty \frac{(a_1)_n (a_2)_n \ldots (a_p)_n} {(b_1)_n(b_2)_n\ldots(b_q)_n} \frac{z^n}{n!} where `(x)_n` denotes the rising factorial (see :func:`~mpmath.rf`). The parameters lists ``a_s`` and ``b_s`` may contain integers, real numbers, complex numbers, as well as exact fractions given in the form of tuples `(p, q)`. :func:`~mpmath.hyper` is optimized to handle integers and fractions more efficiently than arbitrary floating-point parameters (since rational parameters are by far the most common). **Examples** Verifying that :func:`~mpmath.hyper` gives the sum in the definition, by comparison with :func:`~mpmath.nsum`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> a,b,c,d = 2,3,4,5 >>> x = 0.25 >>> hyper([a,b],[c,d],x) 1.078903941164934876086237 >>> fn = lambda n: rf(a,n)*rf(b,n)/rf(c,n)/rf(d,n)*x**n/fac(n) >>> nsum(fn, [0, inf]) 1.078903941164934876086237 The parameters can be any combination of integers, fractions, floats and complex numbers:: >>> a, b, c, d, e = 1, (-1,2), pi, 3+4j, (2,3) >>> x = 0.2j >>> hyper([a,b],[c,d,e],x) (0.9923571616434024810831887 - 0.005753848733883879742993122j) >>> b, e = -0.5, mpf(2)/3 >>> fn = lambda n: rf(a,n)*rf(b,n)/rf(c,n)/rf(d,n)/rf(e,n)*x**n/fac(n) >>> nsum(fn, [0, inf]) (0.9923571616434024810831887 - 0.005753848733883879742993122j) The `\,_0F_0` and `\,_1F_0` series are just elementary functions:: >>> a, z = sqrt(2), +pi >>> hyper([],[],z) 23.14069263277926900572909 >>> exp(z) 23.14069263277926900572909 >>> hyper([a],[],z) (-0.09069132879922920160334114 + 0.3283224323946162083579656j) >>> (1-z)**(-a) (-0.09069132879922920160334114 + 0.3283224323946162083579656j) If any `a_k` coefficient is a nonpositive integer, the series terminates into a finite polynomial:: >>> hyper([1,1,1,-3],[2,5],1) 0.7904761904761904761904762 >>> identify(_) '(83/105)' If any `b_k` is a nonpositive integer, the function is undefined (unless the series terminates before the division by zero occurs):: >>> hyper([1,1,1,-3],[-2,5],1) Traceback (most recent call last): ... ZeroDivisionError: pole in hypergeometric series >>> hyper([1,1,1,-1],[-2,5],1) 1.1 Except for polynomial cases, the radius of convergence `R` of the hypergeometric series is either `R = \infty` (if `p \le q`), `R = 1` (if `p = q+1`), or `R = 0` (if `p > q+1`). The analytic continuations of the functions with `p = q+1`, i.e. `\,_2F_1`, `\,_3F_2`, `\,_4F_3`, etc, are all implemented and therefore these functions can be evaluated for `|z| \ge 1`. The shortcuts :func:`~mpmath.hyp2f1`, :func:`~mpmath.hyp3f2` are available to handle the most common cases (see their documentation), but functions of higher degree are also supported via :func:`~mpmath.hyper`:: >>> hyper([1,2,3,4], [5,6,7], 1) # 4F3 at finite-valued branch point 1.141783505526870731311423 >>> hyper([4,5,6,7], [1,2,3], 1) # 4F3 at pole +inf >>> hyper([1,2,3,4,5], [6,7,8,9], 10) # 5F4 (1.543998916527972259717257 - 0.5876309929580408028816365j) >>> hyper([1,2,3,4,5,6], [7,8,9,10,11], 1j) # 6F5 (0.9996565821853579063502466 + 0.0129721075905630604445669j) Near `z = 1` with noninteger parameters:: >>> hyper(['1/3',1,'3/2',2], ['1/5','11/6','41/8'], 1) 2.219433352235586121250027 >>> hyper(['1/3',1,'3/2',2], ['1/5','11/6','5/4'], 1) +inf >>> eps1 = extradps(6)(lambda: 1 - mpf('1e-6'))() >>> hyper(['1/3',1,'3/2',2], ['1/5','11/6','5/4'], eps1) 2923978034.412973409330956 Please note that, as currently implemented, evaluation of `\,_pF_{p-1}` with `p \ge 3` may be slow or inaccurate when `|z-1|` is small, for some parameter values. When `p > q+1`, ``hyper`` computes the (iterated) Borel sum of the divergent series. For `\,_2F_0` the Borel sum has an analytic solution and can be computed efficiently (see :func:`~mpmath.hyp2f0`). For higher degrees, the functions is evaluated first by attempting to sum it directly as an asymptotic series (this only works for tiny `|z|`), and then by evaluating the Borel regularized sum using numerical integration. Except for special parameter combinations, this can be extremely slow. >>> hyper([1,1], [], 0.5) # regularization of 2F0 (1.340965419580146562086448 + 0.8503366631752726568782447j) >>> hyper([1,1,1,1], [1], 0.5) # regularization of 4F1 (1.108287213689475145830699 + 0.5327107430640678181200491j) With the following magnitude of argument, the asymptotic series for `\,_3F_1` gives only a few digits. Using Borel summation, ``hyper`` can produce a value with full accuracy:: >>> mp.dps = 15 >>> hyper([2,0.5,4], [5.25], '0.08', force_series=True) Traceback (most recent call last): ... NoConvergence: Hypergeometric series converges too slowly. Try increasing maxterms. >>> hyper([2,0.5,4], [5.25], '0.08', asymp_tol=1e-4) 1.0725535790737 >>> hyper([2,0.5,4], [5.25], '0.08') (1.07269542893559 + 5.54668863216891e-5j) >>> hyper([2,0.5,4], [5.25], '-0.08', asymp_tol=1e-4) 0.946344925484879 >>> hyper([2,0.5,4], [5.25], '-0.08') 0.946312503737771 >>> mp.dps = 25 >>> hyper([2,0.5,4], [5.25], '-0.08') 0.9463125037377662296700858 Note that with the positive `z` value, there is a complex part in the correct result, which falls below the tolerance of the asymptotic series. """ hypercomb = r""" Computes a weighted combination of hypergeometric functions .. math :: \sum_{r=1}^N \left[ \prod_{k=1}^{l_r} {w_{r,k}}^{c_{r,k}} \frac{\prod_{k=1}^{m_r} \Gamma(\alpha_{r,k})}{\prod_{k=1}^{n_r} \Gamma(\beta_{r,k})} \,_{p_r}F_{q_r}(a_{r,1},\ldots,a_{r,p}; b_{r,1}, \ldots, b_{r,q}; z_r)\right]. Typically the parameters are linear combinations of a small set of base parameters; :func:`~mpmath.hypercomb` permits computing a correct value in the case that some of the `\alpha`, `\beta`, `b` turn out to be nonpositive integers, or if division by zero occurs for some `w^c`, assuming that there are opposing singularities that cancel out. The limit is computed by evaluating the function with the base parameters perturbed, at a higher working precision. The first argument should be a function that takes the perturbable base parameters ``params`` as input and returns `N` tuples ``(w, c, alpha, beta, a, b, z)``, where the coefficients ``w``, ``c``, gamma factors ``alpha``, ``beta``, and hypergeometric coefficients ``a``, ``b`` each should be lists of numbers, and ``z`` should be a single number. **Examples** The following evaluates .. math :: (a-1) \frac{\Gamma(a-3)}{\Gamma(a-4)} \,_1F_1(a,a-1,z) = e^z(a-4)(a+z-1) with `a=1, z=3`. There is a zero factor, two gamma function poles, and the 1F1 function is singular; all singularities cancel out to give a finite value:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> hypercomb(lambda a: [([a-1],[1],[a-3],[a-4],[a],[a-1],3)], [1]) -180.769832308689 >>> -9*exp(3) -180.769832308689 """ hyp0f1 = r""" Gives the hypergeometric function `\,_0F_1`, sometimes known as the confluent limit function, defined as .. math :: \,_0F_1(a,z) = \sum_{k=0}^{\infty} \frac{1}{(a)_k} \frac{z^k}{k!}. This function satisfies the differential equation `z f''(z) + a f'(z) = f(z)`, and is related to the Bessel function of the first kind (see :func:`~mpmath.besselj`). ``hyp0f1(a,z)`` is equivalent to ``hyper([],[a],z)``; see documentation for :func:`~mpmath.hyper` for more information. **Examples** Evaluation for arbitrary arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> hyp0f1(2, 0.25) 1.130318207984970054415392 >>> hyp0f1((1,2), 1234567) 6.27287187546220705604627e+964 >>> hyp0f1(3+4j, 1000000j) (3.905169561300910030267132e+606 + 3.807708544441684513934213e+606j) Evaluation is supported for arbitrarily large values of `z`, using asymptotic expansions:: >>> hyp0f1(1, 10**50) 2.131705322874965310390701e+8685889638065036553022565 >>> hyp0f1(1, -10**50) 1.115945364792025420300208e-13 Verifying the differential equation:: >>> a = 2.5 >>> f = lambda z: hyp0f1(a,z) >>> for z in [0, 10, 3+4j]: ... chop(z*diff(f,z,2) + a*diff(f,z) - f(z)) ... 0.0 0.0 0.0 """ hyp1f1 = r""" Gives the confluent hypergeometric function of the first kind, .. math :: \,_1F_1(a,b,z) = \sum_{k=0}^{\infty} \frac{(a)_k}{(b)_k} \frac{z^k}{k!}, also known as Kummer's function and sometimes denoted by `M(a,b,z)`. This function gives one solution to the confluent (Kummer's) differential equation .. math :: z f''(z) + (b-z) f'(z) - af(z) = 0. A second solution is given by the `U` function; see :func:`~mpmath.hyperu`. Solutions are also given in an alternate form by the Whittaker functions (:func:`~mpmath.whitm`, :func:`~mpmath.whitw`). ``hyp1f1(a,b,z)`` is equivalent to ``hyper([a],[b],z)``; see documentation for :func:`~mpmath.hyper` for more information. **Examples** Evaluation for real and complex values of the argument `z`, with fixed parameters `a = 2, b = -1/3`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> hyp1f1(2, (-1,3), 3.25) -2815.956856924817275640248 >>> hyp1f1(2, (-1,3), -3.25) -1.145036502407444445553107 >>> hyp1f1(2, (-1,3), 1000) -8.021799872770764149793693e+441 >>> hyp1f1(2, (-1,3), -1000) 0.000003131987633006813594535331 >>> hyp1f1(2, (-1,3), 100+100j) (-3.189190365227034385898282e+48 - 1.106169926814270418999315e+49j) Parameters may be complex:: >>> hyp1f1(2+3j, -1+j, 10j) (261.8977905181045142673351 + 160.8930312845682213562172j) Arbitrarily large values of `z` are supported:: >>> hyp1f1(3, 4, 10**20) 3.890569218254486878220752e+43429448190325182745 >>> hyp1f1(3, 4, -10**20) 6.0e-60 >>> hyp1f1(3, 4, 10**20*j) (-1.935753855797342532571597e-20 - 2.291911213325184901239155e-20j) Verifying the differential equation:: >>> a, b = 1.5, 2 >>> f = lambda z: hyp1f1(a,b,z) >>> for z in [0, -10, 3, 3+4j]: ... chop(z*diff(f,z,2) + (b-z)*diff(f,z) - a*f(z)) ... 0.0 0.0 0.0 0.0 An integral representation:: >>> a, b = 1.5, 3 >>> z = 1.5 >>> hyp1f1(a,b,z) 2.269381460919952778587441 >>> g = lambda t: exp(z*t)*t**(a-1)*(1-t)**(b-a-1) >>> gammaprod([b],[a,b-a])*quad(g, [0,1]) 2.269381460919952778587441 """ hyp1f2 = r""" Gives the hypergeometric function `\,_1F_2(a_1,a_2;b_1,b_2; z)`. The call ``hyp1f2(a1,b1,b2,z)`` is equivalent to ``hyper([a1],[b1,b2],z)``. Evaluation works for complex and arbitrarily large arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> a, b, c = 1.5, (-1,3), 2.25 >>> hyp1f2(a, b, c, 10**20) -1.159388148811981535941434e+8685889639 >>> hyp1f2(a, b, c, -10**20) -12.60262607892655945795907 >>> hyp1f2(a, b, c, 10**20*j) (4.237220401382240876065501e+6141851464 - 2.950930337531768015892987e+6141851464j) >>> hyp1f2(2+3j, -2j, 0.5j, 10-20j) (135881.9905586966432662004 - 86681.95885418079535738828j) """ hyp2f2 = r""" Gives the hypergeometric function `\,_2F_2(a_1,a_2;b_1,b_2; z)`. The call ``hyp2f2(a1,a2,b1,b2,z)`` is equivalent to ``hyper([a1,a2],[b1,b2],z)``. Evaluation works for complex and arbitrarily large arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> a, b, c, d = 1.5, (-1,3), 2.25, 4 >>> hyp2f2(a, b, c, d, 10**20) -5.275758229007902299823821e+43429448190325182663 >>> hyp2f2(a, b, c, d, -10**20) 2561445.079983207701073448 >>> hyp2f2(a, b, c, d, 10**20*j) (2218276.509664121194836667 - 1280722.539991603850462856j) >>> hyp2f2(2+3j, -2j, 0.5j, 4j, 10-20j) (80500.68321405666957342788 - 20346.82752982813540993502j) """ hyp2f3 = r""" Gives the hypergeometric function `\,_2F_3(a_1,a_2;b_1,b_2,b_3; z)`. The call ``hyp2f3(a1,a2,b1,b2,b3,z)`` is equivalent to ``hyper([a1,a2],[b1,b2,b3],z)``. Evaluation works for arbitrarily large arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> a1,a2,b1,b2,b3 = 1.5, (-1,3), 2.25, 4, (1,5) >>> hyp2f3(a1,a2,b1,b2,b3,10**20) -4.169178177065714963568963e+8685889590 >>> hyp2f3(a1,a2,b1,b2,b3,-10**20) 7064472.587757755088178629 >>> hyp2f3(a1,a2,b1,b2,b3,10**20*j) (-5.163368465314934589818543e+6141851415 + 1.783578125755972803440364e+6141851416j) >>> hyp2f3(2+3j, -2j, 0.5j, 4j, -1-j, 10-20j) (-2280.938956687033150740228 + 13620.97336609573659199632j) >>> hyp2f3(2+3j, -2j, 0.5j, 4j, -1-j, 10000000-20000000j) (4.849835186175096516193e+3504 - 3.365981529122220091353633e+3504j) """ hyp2f1 = r""" Gives the Gauss hypergeometric function `\,_2F_1` (often simply referred to as *the* hypergeometric function), defined for `|z| < 1` as .. math :: \,_2F_1(a,b,c,z) = \sum_{k=0}^{\infty} \frac{(a)_k (b)_k}{(c)_k} \frac{z^k}{k!}. and for `|z| \ge 1` by analytic continuation, with a branch cut on `(1, \infty)` when necessary. Special cases of this function include many of the orthogonal polynomials as well as the incomplete beta function and other functions. Properties of the Gauss hypergeometric function are documented comprehensively in many references, for example Abramowitz & Stegun, section 15. The implementation supports the analytic continuation as well as evaluation close to the unit circle where `|z| \approx 1`. The syntax ``hyp2f1(a,b,c,z)`` is equivalent to ``hyper([a,b],[c],z)``. **Examples** Evaluation with `z` inside, outside and on the unit circle, for fixed parameters:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> hyp2f1(2, (1,2), 4, 0.75) 1.303703703703703703703704 >>> hyp2f1(2, (1,2), 4, -1.75) 0.7431290566046919177853916 >>> hyp2f1(2, (1,2), 4, 1.75) (1.418075801749271137026239 - 1.114976146679907015775102j) >>> hyp2f1(2, (1,2), 4, 1) 1.6 >>> hyp2f1(2, (1,2), 4, -1) 0.8235498012182875315037882 >>> hyp2f1(2, (1,2), 4, j) (0.9144026291433065674259078 + 0.2050415770437884900574923j) >>> hyp2f1(2, (1,2), 4, 2+j) (0.9274013540258103029011549 + 0.7455257875808100868984496j) >>> hyp2f1(2, (1,2), 4, 0.25j) (0.9931169055799728251931672 + 0.06154836525312066938147793j) Evaluation with complex parameter values:: >>> hyp2f1(1+j, 0.75, 10j, 1+5j) (0.8834833319713479923389638 + 0.7053886880648105068343509j) Evaluation with `z = 1`:: >>> hyp2f1(-2.5, 3.5, 1.5, 1) 0.0 >>> hyp2f1(-2.5, 3, 4, 1) 0.06926406926406926406926407 >>> hyp2f1(2, 3, 4, 1) +inf Evaluation for huge arguments:: >>> hyp2f1((-1,3), 1.75, 4, '1e100') (7.883714220959876246415651e+32 + 1.365499358305579597618785e+33j) >>> hyp2f1((-1,3), 1.75, 4, '1e1000000') (7.883714220959876246415651e+333332 + 1.365499358305579597618785e+333333j) >>> hyp2f1((-1,3), 1.75, 4, '1e1000000j') (1.365499358305579597618785e+333333 - 7.883714220959876246415651e+333332j) An integral representation:: >>> a,b,c,z = -0.5, 1, 2.5, 0.25 >>> g = lambda t: t**(b-1) * (1-t)**(c-b-1) * (1-t*z)**(-a) >>> gammaprod([c],[b,c-b]) * quad(g, [0,1]) 0.9480458814362824478852618 >>> hyp2f1(a,b,c,z) 0.9480458814362824478852618 Verifying the hypergeometric differential equation:: >>> f = lambda z: hyp2f1(a,b,c,z) >>> chop(z*(1-z)*diff(f,z,2) + (c-(a+b+1)*z)*diff(f,z) - a*b*f(z)) 0.0 """ hyp3f2 = r""" Gives the generalized hypergeometric function `\,_3F_2`, defined for `|z| < 1` as .. math :: \,_3F_2(a_1,a_2,a_3,b_1,b_2,z) = \sum_{k=0}^{\infty} \frac{(a_1)_k (a_2)_k (a_3)_k}{(b_1)_k (b_2)_k} \frac{z^k}{k!}. and for `|z| \ge 1` by analytic continuation. The analytic structure of this function is similar to that of `\,_2F_1`, generally with a singularity at `z = 1` and a branch cut on `(1, \infty)`. Evaluation is supported inside, on, and outside the circle of convergence `|z| = 1`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> hyp3f2(1,2,3,4,5,0.25) 1.083533123380934241548707 >>> hyp3f2(1,2+2j,3,4,5,-10+10j) (0.1574651066006004632914361 - 0.03194209021885226400892963j) >>> hyp3f2(1,2,3,4,5,-10) 0.3071141169208772603266489 >>> hyp3f2(1,2,3,4,5,10) (-0.4857045320523947050581423 - 0.5988311440454888436888028j) >>> hyp3f2(0.25,1,1,2,1.5,1) 1.157370995096772047567631 >>> (8-pi-2*ln2)/3 1.157370995096772047567631 >>> hyp3f2(1+j,0.5j,2,1,-2j,-1) (1.74518490615029486475959 + 0.1454701525056682297614029j) >>> hyp3f2(1+j,0.5j,2,1,-2j,sqrt(j)) (0.9829816481834277511138055 - 0.4059040020276937085081127j) >>> hyp3f2(-3,2,1,-5,4,1) 1.41 >>> hyp3f2(-3,2,1,-5,4,2) 2.12 Evaluation very close to the unit circle:: >>> hyp3f2(1,2,3,4,5,'1.0001') (1.564877796743282766872279 - 3.76821518787438186031973e-11j) >>> hyp3f2(1,2,3,4,5,'1+0.0001j') (1.564747153061671573212831 + 0.0001305757570366084557648482j) >>> hyp3f2(1,2,3,4,5,'0.9999') 1.564616644881686134983664 >>> hyp3f2(1,2,3,4,5,'-0.9999') 0.7823896253461678060196207 .. note :: Evaluation for `|z-1|` small can currently be inaccurate or slow for some parameter combinations. For various parameter combinations, `\,_3F_2` admits representation in terms of hypergeometric functions of lower degree, or in terms of simpler functions:: >>> for a, b, z in [(1,2,-1), (2,0.5,1)]: ... hyp2f1(a,b,a+b+0.5,z)**2 ... hyp3f2(2*a,a+b,2*b,a+b+0.5,2*a+2*b,z) ... 0.4246104461966439006086308 0.4246104461966439006086308 7.111111111111111111111111 7.111111111111111111111111 >>> z = 2+3j >>> hyp3f2(0.5,1,1.5,2,2,z) (0.7621440939243342419729144 + 0.4249117735058037649915723j) >>> 4*(pi-2*ellipe(z))/(pi*z) (0.7621440939243342419729144 + 0.4249117735058037649915723j) """ hyperu = r""" Gives the Tricomi confluent hypergeometric function `U`, also known as the Kummer or confluent hypergeometric function of the second kind. This function gives a second linearly independent solution to the confluent hypergeometric differential equation (the first is provided by `\,_1F_1` -- see :func:`~mpmath.hyp1f1`). **Examples** Evaluation for arbitrary complex arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> hyperu(2,3,4) 0.0625 >>> hyperu(0.25, 5, 1000) 0.1779949416140579573763523 >>> hyperu(0.25, 5, -1000) (0.1256256609322773150118907 - 0.1256256609322773150118907j) The `U` function may be singular at `z = 0`:: >>> hyperu(1.5, 2, 0) +inf >>> hyperu(1.5, -2, 0) 0.1719434921288400112603671 Verifying the differential equation:: >>> a, b = 1.5, 2 >>> f = lambda z: hyperu(a,b,z) >>> for z in [-10, 3, 3+4j]: ... chop(z*diff(f,z,2) + (b-z)*diff(f,z) - a*f(z)) ... 0.0 0.0 0.0 An integral representation:: >>> a,b,z = 2, 3.5, 4.25 >>> hyperu(a,b,z) 0.06674960718150520648014567 >>> quad(lambda t: exp(-z*t)*t**(a-1)*(1+t)**(b-a-1),[0,inf]) / gamma(a) 0.06674960718150520648014567 [1] http://people.math.sfu.ca/~cbm/aands/page_504.htm """ hyp2f0 = r""" Gives the hypergeometric function `\,_2F_0`, defined formally by the series .. math :: \,_2F_0(a,b;;z) = \sum_{n=0}^{\infty} (a)_n (b)_n \frac{z^n}{n!}. This series usually does not converge. For small enough `z`, it can be viewed as an asymptotic series that may be summed directly with an appropriate truncation. When this is not the case, :func:`~mpmath.hyp2f0` gives a regularized sum, or equivalently, it uses a representation in terms of the hypergeometric U function [1]. The series also converges when either `a` or `b` is a nonpositive integer, as it then terminates into a polynomial after `-a` or `-b` terms. **Examples** Evaluation is supported for arbitrary complex arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> hyp2f0((2,3), 1.25, -100) 0.07095851870980052763312791 >>> hyp2f0((2,3), 1.25, 100) (-0.03254379032170590665041131 + 0.07269254613282301012735797j) >>> hyp2f0(-0.75, 1-j, 4j) (-0.3579987031082732264862155 - 3.052951783922142735255881j) Even with real arguments, the regularized value of 2F0 is often complex-valued, but the imaginary part decreases exponentially as `z \to 0`. In the following example, the first call uses complex evaluation while the second has a small enough `z` to evaluate using the direct series and thus the returned value is strictly real (this should be taken to indicate that the imaginary part is less than ``eps``):: >>> mp.dps = 15 >>> hyp2f0(1.5, 0.5, 0.05) (1.04166637647907 + 8.34584913683906e-8j) >>> hyp2f0(1.5, 0.5, 0.0005) 1.00037535207621 The imaginary part can be retrieved by increasing the working precision:: >>> mp.dps = 80 >>> nprint(hyp2f0(1.5, 0.5, 0.009).imag) 1.23828e-46 In the polynomial case (the series terminating), 2F0 can evaluate exactly:: >>> mp.dps = 15 >>> hyp2f0(-6,-6,2) 291793.0 >>> identify(hyp2f0(-2,1,0.25)) '(5/8)' The coefficients of the polynomials can be recovered using Taylor expansion:: >>> nprint(taylor(lambda x: hyp2f0(-3,0.5,x), 0, 10)) [1.0, -1.5, 2.25, -1.875, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] >>> nprint(taylor(lambda x: hyp2f0(-4,0.5,x), 0, 10)) [1.0, -2.0, 4.5, -7.5, 6.5625, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] [1] http://people.math.sfu.ca/~cbm/aands/page_504.htm """ gammainc = r""" ``gammainc(z, a=0, b=inf)`` computes the (generalized) incomplete gamma function with integration limits `[a, b]`: .. math :: \Gamma(z,a,b) = \int_a^b t^{z-1} e^{-t} \, dt The generalized incomplete gamma function reduces to the following special cases when one or both endpoints are fixed: * `\Gamma(z,0,\infty)` is the standard ("complete") gamma function, `\Gamma(z)` (available directly as the mpmath function :func:`~mpmath.gamma`) * `\Gamma(z,a,\infty)` is the "upper" incomplete gamma function, `\Gamma(z,a)` * `\Gamma(z,0,b)` is the "lower" incomplete gamma function, `\gamma(z,b)`. Of course, we have `\Gamma(z,0,x) + \Gamma(z,x,\infty) = \Gamma(z)` for all `z` and `x`. Note however that some authors reverse the order of the arguments when defining the lower and upper incomplete gamma function, so one should be careful to get the correct definition. If also given the keyword argument ``regularized=True``, :func:`~mpmath.gammainc` computes the "regularized" incomplete gamma function .. math :: P(z,a,b) = \frac{\Gamma(z,a,b)}{\Gamma(z)}. **Examples** We can compare with numerical quadrature to verify that :func:`~mpmath.gammainc` computes the integral in the definition:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> gammainc(2+3j, 4, 10) (0.00977212668627705160602312 - 0.0770637306312989892451977j) >>> quad(lambda t: t**(2+3j-1) * exp(-t), [4, 10]) (0.00977212668627705160602312 - 0.0770637306312989892451977j) Argument symmetries follow directly from the integral definition:: >>> gammainc(3, 4, 5) + gammainc(3, 5, 4) 0.0 >>> gammainc(3,0,2) + gammainc(3,2,4); gammainc(3,0,4) 1.523793388892911312363331 1.523793388892911312363331 >>> findroot(lambda z: gammainc(2,z,3), 1) 3.0 Evaluation for arbitrarily large arguments:: >>> gammainc(10, 100) 4.083660630910611272288592e-26 >>> gammainc(10, 10000000000000000) 5.290402449901174752972486e-4342944819032375 >>> gammainc(3+4j, 1000000+1000000j) (-1.257913707524362408877881e-434284 + 2.556691003883483531962095e-434284j) Evaluation of a generalized incomplete gamma function automatically chooses the representation that gives a more accurate result, depending on which parameter is larger:: >>> gammainc(10000000, 3) - gammainc(10000000, 2) # Bad 0.0 >>> gammainc(10000000, 2, 3) # Good 1.755146243738946045873491e+4771204 >>> gammainc(2, 0, 100000001) - gammainc(2, 0, 100000000) # Bad 0.0 >>> gammainc(2, 100000000, 100000001) # Good 4.078258353474186729184421e-43429441 The incomplete gamma functions satisfy simple recurrence relations:: >>> mp.dps = 25 >>> z, a = mpf(3.5), mpf(2) >>> gammainc(z+1, a); z*gammainc(z,a) + a**z*exp(-a) 10.60130296933533459267329 10.60130296933533459267329 >>> gammainc(z+1,0,a); z*gammainc(z,0,a) - a**z*exp(-a) 1.030425427232114336470932 1.030425427232114336470932 Evaluation at integers and poles:: >>> gammainc(-3, -4, -5) (-0.2214577048967798566234192 + 0.0j) >>> gammainc(-3, 0, 5) +inf If `z` is an integer, the recurrence reduces the incomplete gamma function to `P(a) \exp(-a) + Q(b) \exp(-b)` where `P` and `Q` are polynomials:: >>> gammainc(1, 2); exp(-2) 0.1353352832366126918939995 0.1353352832366126918939995 >>> mp.dps = 50 >>> identify(gammainc(6, 1, 2), ['exp(-1)', 'exp(-2)']) '(326*exp(-1) + (-872)*exp(-2))' The incomplete gamma functions reduce to functions such as the exponential integral Ei and the error function for special arguments:: >>> mp.dps = 25 >>> gammainc(0, 4); -ei(-4) 0.00377935240984890647887486 0.00377935240984890647887486 >>> gammainc(0.5, 0, 2); sqrt(pi)*erf(sqrt(2)) 1.691806732945198336509541 1.691806732945198336509541 """ erf = r""" Computes the error function, `\mathrm{erf}(x)`. The error function is the normalized antiderivative of the Gaussian function `\exp(-t^2)`. More precisely, .. math:: \mathrm{erf}(x) = \frac{2}{\sqrt \pi} \int_0^x \exp(-t^2) \,dt **Basic examples** Simple values and limits include:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> erf(0) 0.0 >>> erf(1) 0.842700792949715 >>> erf(-1) -0.842700792949715 >>> erf(inf) 1.0 >>> erf(-inf) -1.0 For large real `x`, `\mathrm{erf}(x)` approaches 1 very rapidly:: >>> erf(3) 0.999977909503001 >>> erf(5) 0.999999999998463 The error function is an odd function:: >>> nprint(chop(taylor(erf, 0, 5))) [0.0, 1.12838, 0.0, -0.376126, 0.0, 0.112838] :func:`~mpmath.erf` implements arbitrary-precision evaluation and supports complex numbers:: >>> mp.dps = 50 >>> erf(0.5) 0.52049987781304653768274665389196452873645157575796 >>> mp.dps = 25 >>> erf(1+j) (1.316151281697947644880271 + 0.1904534692378346862841089j) Evaluation is supported for large arguments:: >>> mp.dps = 25 >>> erf('1e1000') 1.0 >>> erf('-1e1000') -1.0 >>> erf('1e-1000') 1.128379167095512573896159e-1000 >>> erf('1e7j') (0.0 + 8.593897639029319267398803e+43429448190317j) >>> erf('1e7+1e7j') (0.9999999858172446172631323 + 3.728805278735270407053139e-8j) **Related functions** See also :func:`~mpmath.erfc`, which is more accurate for large `x`, and :func:`~mpmath.erfi` which gives the antiderivative of `\exp(t^2)`. The Fresnel integrals :func:`~mpmath.fresnels` and :func:`~mpmath.fresnelc` are also related to the error function. """ erfc = r""" Computes the complementary error function, `\mathrm{erfc}(x) = 1-\mathrm{erf}(x)`. This function avoids cancellation that occurs when naively computing the complementary error function as ``1-erf(x)``:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> 1 - erf(10) 0.0 >>> erfc(10) 2.08848758376254e-45 :func:`~mpmath.erfc` works accurately even for ludicrously large arguments:: >>> erfc(10**10) 4.3504398860243e-43429448190325182776 Complex arguments are supported:: >>> erfc(500+50j) (1.19739830969552e-107492 + 1.46072418957528e-107491j) """ erfi = r""" Computes the imaginary error function, `\mathrm{erfi}(x)`. The imaginary error function is defined in analogy with the error function, but with a positive sign in the integrand: .. math :: \mathrm{erfi}(x) = \frac{2}{\sqrt \pi} \int_0^x \exp(t^2) \,dt Whereas the error function rapidly converges to 1 as `x` grows, the imaginary error function rapidly diverges to infinity. The functions are related as `\mathrm{erfi}(x) = -i\,\mathrm{erf}(ix)` for all complex numbers `x`. **Examples** Basic values and limits:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> erfi(0) 0.0 >>> erfi(1) 1.65042575879754 >>> erfi(-1) -1.65042575879754 >>> erfi(inf) +inf >>> erfi(-inf) -inf Note the symmetry between erf and erfi:: >>> erfi(3j) (0.0 + 0.999977909503001j) >>> erf(3) 0.999977909503001 >>> erf(1+2j) (-0.536643565778565 - 5.04914370344703j) >>> erfi(2+1j) (-5.04914370344703 - 0.536643565778565j) Large arguments are supported:: >>> erfi(1000) 1.71130938718796e+434291 >>> erfi(10**10) 7.3167287567024e+43429448190325182754 >>> erfi(-10**10) -7.3167287567024e+43429448190325182754 >>> erfi(1000-500j) (2.49895233563961e+325717 + 2.6846779342253e+325717j) >>> erfi(100000j) (0.0 + 1.0j) >>> erfi(-100000j) (0.0 - 1.0j) """ erfinv = r""" Computes the inverse error function, satisfying .. math :: \mathrm{erf}(\mathrm{erfinv}(x)) = \mathrm{erfinv}(\mathrm{erf}(x)) = x. This function is defined only for `-1 \le x \le 1`. **Examples** Special values include:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> erfinv(0) 0.0 >>> erfinv(1) +inf >>> erfinv(-1) -inf The domain is limited to the standard interval:: >>> erfinv(2) Traceback (most recent call last): ... ValueError: erfinv(x) is defined only for -1 <= x <= 1 It is simple to check that :func:`~mpmath.erfinv` computes inverse values of :func:`~mpmath.erf` as promised:: >>> erf(erfinv(0.75)) 0.75 >>> erf(erfinv(-0.995)) -0.995 :func:`~mpmath.erfinv` supports arbitrary-precision evaluation:: >>> mp.dps = 50 >>> x = erf(2) >>> x 0.99532226501895273416206925636725292861089179704006 >>> erfinv(x) 2.0 A definite integral involving the inverse error function:: >>> mp.dps = 15 >>> quad(erfinv, [0, 1]) 0.564189583547756 >>> 1/sqrt(pi) 0.564189583547756 The inverse error function can be used to generate random numbers with a Gaussian distribution (although this is a relatively inefficient algorithm):: >>> nprint([erfinv(2*rand()-1) for n in range(6)]) # doctest: +SKIP [-0.586747, 1.10233, -0.376796, 0.926037, -0.708142, -0.732012] """ npdf = r""" ``npdf(x, mu=0, sigma=1)`` evaluates the probability density function of a normal distribution with mean value `\mu` and variance `\sigma^2`. Elementary properties of the probability distribution can be verified using numerical integration:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> quad(npdf, [-inf, inf]) 1.0 >>> quad(lambda x: npdf(x, 3), [3, inf]) 0.5 >>> quad(lambda x: npdf(x, 3, 2), [3, inf]) 0.5 See also :func:`~mpmath.ncdf`, which gives the cumulative distribution. """ ncdf = r""" ``ncdf(x, mu=0, sigma=1)`` evaluates the cumulative distribution function of a normal distribution with mean value `\mu` and variance `\sigma^2`. See also :func:`~mpmath.npdf`, which gives the probability density. Elementary properties include:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> ncdf(pi, mu=pi) 0.5 >>> ncdf(-inf) 0.0 >>> ncdf(+inf) 1.0 The cumulative distribution is the integral of the density function having identical mu and sigma:: >>> mp.dps = 15 >>> diff(ncdf, 2) 0.053990966513188 >>> npdf(2) 0.053990966513188 >>> diff(lambda x: ncdf(x, 1, 0.5), 0) 0.107981933026376 >>> npdf(0, 1, 0.5) 0.107981933026376 """ expint = r""" :func:`~mpmath.expint(n,z)` gives the generalized exponential integral or En-function, .. math :: \mathrm{E}_n(z) = \int_1^{\infty} \frac{e^{-zt}}{t^n} dt, where `n` and `z` may both be complex numbers. The case with `n = 1` is also given by :func:`~mpmath.e1`. **Examples** Evaluation at real and complex arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> expint(1, 6.25) 0.0002704758872637179088496194 >>> expint(-3, 2+3j) (0.00299658467335472929656159 + 0.06100816202125885450319632j) >>> expint(2+3j, 4-5j) (0.001803529474663565056945248 - 0.002235061547756185403349091j) At negative integer values of `n`, `E_n(z)` reduces to a rational-exponential function:: >>> f = lambda n, z: fac(n)*sum(z**k/fac(k-1) for k in range(1,n+2))/\ ... exp(z)/z**(n+2) >>> n = 3 >>> z = 1/pi >>> expint(-n,z) 584.2604820613019908668219 >>> f(n,z) 584.2604820613019908668219 >>> n = 5 >>> expint(-n,z) 115366.5762594725451811138 >>> f(n,z) 115366.5762594725451811138 """ e1 = r""" Computes the exponential integral `\mathrm{E}_1(z)`, given by .. math :: \mathrm{E}_1(z) = \int_z^{\infty} \frac{e^{-t}}{t} dt. This is equivalent to :func:`~mpmath.expint` with `n = 1`. **Examples** Two ways to evaluate this function:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> e1(6.25) 0.0002704758872637179088496194 >>> expint(1,6.25) 0.0002704758872637179088496194 The E1-function is essentially the same as the Ei-function (:func:`~mpmath.ei`) with negated argument, except for an imaginary branch cut term:: >>> e1(2.5) 0.02491491787026973549562801 >>> -ei(-2.5) 0.02491491787026973549562801 >>> e1(-2.5) (-7.073765894578600711923552 - 3.141592653589793238462643j) >>> -ei(2.5) -7.073765894578600711923552 """ ei = r""" Computes the exponential integral or Ei-function, `\mathrm{Ei}(x)`. The exponential integral is defined as .. math :: \mathrm{Ei}(x) = \int_{-\infty\,}^x \frac{e^t}{t} \, dt. When the integration range includes `t = 0`, the exponential integral is interpreted as providing the Cauchy principal value. For real `x`, the Ei-function behaves roughly like `\mathrm{Ei}(x) \approx \exp(x) + \log(|x|)`. The Ei-function is related to the more general family of exponential integral functions denoted by `E_n`, which are available as :func:`~mpmath.expint`. **Basic examples** Some basic values and limits are:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> ei(0) -inf >>> ei(1) 1.89511781635594 >>> ei(inf) +inf >>> ei(-inf) 0.0 For `x < 0`, the defining integral can be evaluated numerically as a reference:: >>> ei(-4) -0.00377935240984891 >>> quad(lambda t: exp(t)/t, [-inf, -4]) -0.00377935240984891 :func:`~mpmath.ei` supports complex arguments and arbitrary precision evaluation:: >>> mp.dps = 50 >>> ei(pi) 10.928374389331410348638445906907535171566338835056 >>> mp.dps = 25 >>> ei(3+4j) (-4.154091651642689822535359 + 4.294418620024357476985535j) **Related functions** The exponential integral is closely related to the logarithmic integral. See :func:`~mpmath.li` for additional information. The exponential integral is related to the hyperbolic and trigonometric integrals (see :func:`~mpmath.chi`, :func:`~mpmath.shi`, :func:`~mpmath.ci`, :func:`~mpmath.si`) similarly to how the ordinary exponential function is related to the hyperbolic and trigonometric functions:: >>> mp.dps = 15 >>> ei(3) 9.93383257062542 >>> chi(3) + shi(3) 9.93383257062542 >>> chop(ci(3j) - j*si(3j) - pi*j/2) 9.93383257062542 Beware that logarithmic corrections, as in the last example above, are required to obtain the correct branch in general. For details, see [1]. The exponential integral is also a special case of the hypergeometric function `\,_2F_2`:: >>> z = 0.6 >>> z*hyper([1,1],[2,2],z) + (ln(z)-ln(1/z))/2 + euler 0.769881289937359 >>> ei(z) 0.769881289937359 **References** 1. Relations between Ei and other functions: http://functions.wolfram.com/GammaBetaErf/ExpIntegralEi/27/01/ 2. Abramowitz & Stegun, section 5: http://www.math.sfu.ca/~cbm/aands/page_228.htm 3. Asymptotic expansion for Ei: http://mathworld.wolfram.com/En-Function.html """ li = r""" Computes the logarithmic integral or li-function `\mathrm{li}(x)`, defined by .. math :: \mathrm{li}(x) = \int_0^x \frac{1}{\log t} \, dt The logarithmic integral has a singularity at `x = 1`. Alternatively, ``li(x, offset=True)`` computes the offset logarithmic integral (used in number theory) .. math :: \mathrm{Li}(x) = \int_2^x \frac{1}{\log t} \, dt. These two functions are related via the simple identity `\mathrm{Li}(x) = \mathrm{li}(x) - \mathrm{li}(2)`. The logarithmic integral should also not be confused with the polylogarithm (also denoted by Li), which is implemented as :func:`~mpmath.polylog`. **Examples** Some basic values and limits:: >>> from mpmath import * >>> mp.dps = 30; mp.pretty = True >>> li(0) 0.0 >>> li(1) -inf >>> li(1) -inf >>> li(2) 1.04516378011749278484458888919 >>> findroot(li, 2) 1.45136923488338105028396848589 >>> li(inf) +inf >>> li(2, offset=True) 0.0 >>> li(1, offset=True) -inf >>> li(0, offset=True) -1.04516378011749278484458888919 >>> li(10, offset=True) 5.12043572466980515267839286347 The logarithmic integral can be evaluated for arbitrary complex arguments:: >>> mp.dps = 20 >>> li(3+4j) (3.1343755504645775265 + 2.6769247817778742392j) The logarithmic integral is related to the exponential integral:: >>> ei(log(3)) 2.1635885946671919729 >>> li(3) 2.1635885946671919729 The logarithmic integral grows like `O(x/\log(x))`:: >>> mp.dps = 15 >>> x = 10**100 >>> x/log(x) 4.34294481903252e+97 >>> li(x) 4.3619719871407e+97 The prime number theorem states that the number of primes less than `x` is asymptotic to `\mathrm{Li}(x)` (equivalently `\mathrm{li}(x)`). For example, it is known that there are exactly 1,925,320,391,606,803,968,923 prime numbers less than `10^{23}` [1]. The logarithmic integral provides a very accurate estimate:: >>> li(10**23, offset=True) 1.92532039161405e+21 A definite integral is:: >>> quad(li, [0, 1]) -0.693147180559945 >>> -ln(2) -0.693147180559945 **References** 1. http://mathworld.wolfram.com/PrimeCountingFunction.html 2. http://mathworld.wolfram.com/LogarithmicIntegral.html """ ci = r""" Computes the cosine integral, .. math :: \mathrm{Ci}(x) = -\int_x^{\infty} \frac{\cos t}{t}\,dt = \gamma + \log x + \int_0^x \frac{\cos t - 1}{t}\,dt **Examples** Some values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> ci(0) -inf >>> ci(1) 0.3374039229009681346626462 >>> ci(pi) 0.07366791204642548599010096 >>> ci(inf) 0.0 >>> ci(-inf) (0.0 + 3.141592653589793238462643j) >>> ci(2+3j) (1.408292501520849518759125 - 2.983617742029605093121118j) The cosine integral behaves roughly like the sinc function (see :func:`~mpmath.sinc`) for large real `x`:: >>> ci(10**10) -4.875060251748226537857298e-11 >>> sinc(10**10) -4.875060250875106915277943e-11 >>> chop(limit(ci, inf)) 0.0 It has infinitely many roots on the positive real axis:: >>> findroot(ci, 1) 0.6165054856207162337971104 >>> findroot(ci, 2) 3.384180422551186426397851 Evaluation is supported for `z` anywhere in the complex plane:: >>> ci(10**6*(1+j)) (4.449410587611035724984376e+434287 + 9.75744874290013526417059e+434287j) We can evaluate the defining integral as a reference:: >>> mp.dps = 15 >>> -quadosc(lambda t: cos(t)/t, [5, inf], omega=1) -0.190029749656644 >>> ci(5) -0.190029749656644 Some infinite series can be evaluated using the cosine integral:: >>> nsum(lambda k: (-1)**k/(fac(2*k)*(2*k)), [1,inf]) -0.239811742000565 >>> ci(1) - euler -0.239811742000565 """ si = r""" Computes the sine integral, .. math :: \mathrm{Si}(x) = \int_0^x \frac{\sin t}{t}\,dt. The sine integral is thus the antiderivative of the sinc function (see :func:`~mpmath.sinc`). **Examples** Some values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> si(0) 0.0 >>> si(1) 0.9460830703671830149413533 >>> si(-1) -0.9460830703671830149413533 >>> si(pi) 1.851937051982466170361053 >>> si(inf) 1.570796326794896619231322 >>> si(-inf) -1.570796326794896619231322 >>> si(2+3j) (4.547513889562289219853204 + 1.399196580646054789459839j) The sine integral approaches `\pi/2` for large real `x`:: >>> si(10**10) 1.570796326707584656968511 >>> pi/2 1.570796326794896619231322 Evaluation is supported for `z` anywhere in the complex plane:: >>> si(10**6*(1+j)) (-9.75744874290013526417059e+434287 + 4.449410587611035724984376e+434287j) We can evaluate the defining integral as a reference:: >>> mp.dps = 15 >>> quad(sinc, [0, 5]) 1.54993124494467 >>> si(5) 1.54993124494467 Some infinite series can be evaluated using the sine integral:: >>> nsum(lambda k: (-1)**k/(fac(2*k+1)*(2*k+1)), [0,inf]) 0.946083070367183 >>> si(1) 0.946083070367183 """ chi = r""" Computes the hyperbolic cosine integral, defined in analogy with the cosine integral (see :func:`~mpmath.ci`) as .. math :: \mathrm{Chi}(x) = -\int_x^{\infty} \frac{\cosh t}{t}\,dt = \gamma + \log x + \int_0^x \frac{\cosh t - 1}{t}\,dt Some values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> chi(0) -inf >>> chi(1) 0.8378669409802082408946786 >>> chi(inf) +inf >>> findroot(chi, 0.5) 0.5238225713898644064509583 >>> chi(2+3j) (-0.1683628683277204662429321 + 2.625115880451325002151688j) Evaluation is supported for `z` anywhere in the complex plane:: >>> chi(10**6*(1+j)) (4.449410587611035724984376e+434287 - 9.75744874290013526417059e+434287j) """ shi = r""" Computes the hyperbolic sine integral, defined in analogy with the sine integral (see :func:`~mpmath.si`) as .. math :: \mathrm{Shi}(x) = \int_0^x \frac{\sinh t}{t}\,dt. Some values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> shi(0) 0.0 >>> shi(1) 1.057250875375728514571842 >>> shi(-1) -1.057250875375728514571842 >>> shi(inf) +inf >>> shi(2+3j) (-0.1931890762719198291678095 + 2.645432555362369624818525j) Evaluation is supported for `z` anywhere in the complex plane:: >>> shi(10**6*(1+j)) (4.449410587611035724984376e+434287 - 9.75744874290013526417059e+434287j) """ fresnels = r""" Computes the Fresnel sine integral .. math :: S(x) = \int_0^x \sin\left(\frac{\pi t^2}{2}\right) \,dt Note that some sources define this function without the normalization factor `\pi/2`. **Examples** Some basic values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> fresnels(0) 0.0 >>> fresnels(inf) 0.5 >>> fresnels(-inf) -0.5 >>> fresnels(1) 0.4382591473903547660767567 >>> fresnels(1+2j) (36.72546488399143842838788 + 15.58775110440458732748279j) Comparing with the definition:: >>> fresnels(3) 0.4963129989673750360976123 >>> quad(lambda t: sin(pi*t**2/2), [0,3]) 0.4963129989673750360976123 """ fresnelc = r""" Computes the Fresnel cosine integral .. math :: C(x) = \int_0^x \cos\left(\frac{\pi t^2}{2}\right) \,dt Note that some sources define this function without the normalization factor `\pi/2`. **Examples** Some basic values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> fresnelc(0) 0.0 >>> fresnelc(inf) 0.5 >>> fresnelc(-inf) -0.5 >>> fresnelc(1) 0.7798934003768228294742064 >>> fresnelc(1+2j) (16.08787137412548041729489 - 36.22568799288165021578758j) Comparing with the definition:: >>> fresnelc(3) 0.6057207892976856295561611 >>> quad(lambda t: cos(pi*t**2/2), [0,3]) 0.6057207892976856295561611 """ airyai = r""" Computes the Airy function `\operatorname{Ai}(z)`, which is the solution of the Airy differential equation `f''(z) - z f(z) = 0` with initial conditions .. math :: \operatorname{Ai}(0) = \frac{1}{3^{2/3}\Gamma\left(\frac{2}{3}\right)} \operatorname{Ai}'(0) = -\frac{1}{3^{1/3}\Gamma\left(\frac{1}{3}\right)}. Other common ways of defining the Ai-function include integrals such as .. math :: \operatorname{Ai}(x) = \frac{1}{\pi} \int_0^{\infty} \cos\left(\frac{1}{3}t^3+xt\right) dt \qquad x \in \mathbb{R} \operatorname{Ai}(z) = \frac{\sqrt{3}}{2\pi} \int_0^{\infty} \exp\left(-\frac{t^3}{3}-\frac{z^3}{3t^3}\right) dt. The Ai-function is an entire function with a turning point, behaving roughly like a slowly decaying sine wave for `z < 0` and like a rapidly decreasing exponential for `z > 0`. A second solution of the Airy differential equation is given by `\operatorname{Bi}(z)` (see :func:`~mpmath.airybi`). Optionally, with *derivative=alpha*, :func:`airyai` can compute the `\alpha`-th order fractional derivative with respect to `z`. For `\alpha = n = 1,2,3,\ldots` this gives the derivative `\operatorname{Ai}^{(n)}(z)`, and for `\alpha = -n = -1,-2,-3,\ldots` this gives the `n`-fold iterated integral .. math :: f_0(z) = \operatorname{Ai}(z) f_n(z) = \int_0^z f_{n-1}(t) dt. The Ai-function has infinitely many zeros, all located along the negative half of the real axis. They can be computed with :func:`~mpmath.airyaizero`. **Plots** .. literalinclude :: /modules/mpmath/plots/ai.py .. image :: /modules/mpmath/plots/ai.png .. literalinclude :: /modules/mpmath/plots/ai_c.py .. image :: /modules/mpmath/plots/ai_c.png **Basic examples** Limits and values include:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> airyai(0); 1/(power(3,'2/3')*gamma('2/3')) 0.3550280538878172392600632 0.3550280538878172392600632 >>> airyai(1) 0.1352924163128814155241474 >>> airyai(-1) 0.5355608832923521187995166 >>> airyai(inf); airyai(-inf) 0.0 0.0 Evaluation is supported for large magnitudes of the argument:: >>> airyai(-100) 0.1767533932395528780908311 >>> airyai(100) 2.634482152088184489550553e-291 >>> airyai(50+50j) (-5.31790195707456404099817e-68 - 1.163588003770709748720107e-67j) >>> airyai(-50+50j) (1.041242537363167632587245e+158 + 3.347525544923600321838281e+157j) Huge arguments are also fine:: >>> airyai(10**10) 1.162235978298741779953693e-289529654602171 >>> airyai(-10**10) 0.0001736206448152818510510181 >>> w = airyai(10**10*(1+j)) >>> w.real 5.711508683721355528322567e-186339621747698 >>> w.imag 1.867245506962312577848166e-186339621747697 The first root of the Ai-function is:: >>> findroot(airyai, -2) -2.338107410459767038489197 >>> airyaizero(1) -2.338107410459767038489197 **Properties and relations** Verifying the Airy differential equation:: >>> for z in [-3.4, 0, 2.5, 1+2j]: ... chop(airyai(z,2) - z*airyai(z)) ... 0.0 0.0 0.0 0.0 The first few terms of the Taylor series expansion around `z = 0` (every third term is zero):: >>> nprint(taylor(airyai, 0, 5)) [0.355028, -0.258819, 0.0, 0.0591713, -0.0215683, 0.0] The Airy functions satisfy the Wronskian relation `\operatorname{Ai}(z) \operatorname{Bi}'(z) - \operatorname{Ai}'(z) \operatorname{Bi}(z) = 1/\pi`:: >>> z = -0.5 >>> airyai(z)*airybi(z,1) - airyai(z,1)*airybi(z) 0.3183098861837906715377675 >>> 1/pi 0.3183098861837906715377675 The Airy functions can be expressed in terms of Bessel functions of order `\pm 1/3`. For `\Re[z] \le 0`, we have:: >>> z = -3 >>> airyai(z) -0.3788142936776580743472439 >>> y = 2*power(-z,'3/2')/3 >>> (sqrt(-z) * (besselj('1/3',y) + besselj('-1/3',y)))/3 -0.3788142936776580743472439 **Derivatives and integrals** Derivatives of the Ai-function (directly and using :func:`~mpmath.diff`):: >>> airyai(-3,1); diff(airyai,-3) 0.3145837692165988136507873 0.3145837692165988136507873 >>> airyai(-3,2); diff(airyai,-3,2) 1.136442881032974223041732 1.136442881032974223041732 >>> airyai(1000,1); diff(airyai,1000) -2.943133917910336090459748e-9156 -2.943133917910336090459748e-9156 Several derivatives at `z = 0`:: >>> airyai(0,0); airyai(0,1); airyai(0,2) 0.3550280538878172392600632 -0.2588194037928067984051836 0.0 >>> airyai(0,3); airyai(0,4); airyai(0,5) 0.3550280538878172392600632 -0.5176388075856135968103671 0.0 >>> airyai(0,15); airyai(0,16); airyai(0,17) 1292.30211615165475090663 -3188.655054727379756351861 0.0 The integral of the Ai-function:: >>> airyai(3,-1); quad(airyai, [0,3]) 0.3299203760070217725002701 0.3299203760070217725002701 >>> airyai(-10,-1); quad(airyai, [0,-10]) -0.765698403134212917425148 -0.765698403134212917425148 Integrals of high or fractional order:: >>> airyai(-2,0.5); differint(airyai,-2,0.5,0) (0.0 + 0.2453596101351438273844725j) (0.0 + 0.2453596101351438273844725j) >>> airyai(-2,-4); differint(airyai,-2,-4,0) 0.2939176441636809580339365 0.2939176441636809580339365 >>> airyai(0,-1); airyai(0,-2); airyai(0,-3) 0.0 0.0 0.0 Integrals of the Ai-function can be evaluated at limit points:: >>> airyai(-1000000,-1); airyai(-inf,-1) -0.6666843728311539978751512 -0.6666666666666666666666667 >>> airyai(10,-1); airyai(+inf,-1) 0.3333333332991690159427932 0.3333333333333333333333333 >>> airyai(+inf,-2); airyai(+inf,-3) +inf +inf >>> airyai(-1000000,-2); airyai(-inf,-2) 666666.4078472650651209742 +inf >>> airyai(-1000000,-3); airyai(-inf,-3) -333333074513.7520264995733 -inf **References** 1. [DLMF]_ Chapter 9: Airy and Related Functions 2. [WolframFunctions]_ section: Bessel-Type Functions """ airybi = r""" Computes the Airy function `\operatorname{Bi}(z)`, which is the solution of the Airy differential equation `f''(z) - z f(z) = 0` with initial conditions .. math :: \operatorname{Bi}(0) = \frac{1}{3^{1/6}\Gamma\left(\frac{2}{3}\right)} \operatorname{Bi}'(0) = \frac{3^{1/6}}{\Gamma\left(\frac{1}{3}\right)}. Like the Ai-function (see :func:`~mpmath.airyai`), the Bi-function is oscillatory for `z < 0`, but it grows rather than decreases for `z > 0`. Optionally, as for :func:`~mpmath.airyai`, derivatives, integrals and fractional derivatives can be computed with the *derivative* parameter. The Bi-function has infinitely many zeros along the negative half-axis, as well as complex zeros, which can all be computed with :func:`~mpmath.airybizero`. **Plots** .. literalinclude :: /modules/mpmath/plots/bi.py .. image :: /modules/mpmath/plots/bi.png .. literalinclude :: /modules/mpmath/plots/bi_c.py .. image :: /modules/mpmath/plots/bi_c.png **Basic examples** Limits and values include:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> airybi(0); 1/(power(3,'1/6')*gamma('2/3')) 0.6149266274460007351509224 0.6149266274460007351509224 >>> airybi(1) 1.207423594952871259436379 >>> airybi(-1) 0.10399738949694461188869 >>> airybi(inf); airybi(-inf) +inf 0.0 Evaluation is supported for large magnitudes of the argument:: >>> airybi(-100) 0.02427388768016013160566747 >>> airybi(100) 6.041223996670201399005265e+288 >>> airybi(50+50j) (-5.322076267321435669290334e+63 + 1.478450291165243789749427e+65j) >>> airybi(-50+50j) (-3.347525544923600321838281e+157 + 1.041242537363167632587245e+158j) Huge arguments:: >>> airybi(10**10) 1.369385787943539818688433e+289529654602165 >>> airybi(-10**10) 0.001775656141692932747610973 >>> w = airybi(10**10*(1+j)) >>> w.real -6.559955931096196875845858e+186339621747689 >>> w.imag -6.822462726981357180929024e+186339621747690 The first real root of the Bi-function is:: >>> findroot(airybi, -1); airybizero(1) -1.17371322270912792491998 -1.17371322270912792491998 **Properties and relations** Verifying the Airy differential equation:: >>> for z in [-3.4, 0, 2.5, 1+2j]: ... chop(airybi(z,2) - z*airybi(z)) ... 0.0 0.0 0.0 0.0 The first few terms of the Taylor series expansion around `z = 0` (every third term is zero):: >>> nprint(taylor(airybi, 0, 5)) [0.614927, 0.448288, 0.0, 0.102488, 0.0373574, 0.0] The Airy functions can be expressed in terms of Bessel functions of order `\pm 1/3`. For `\Re[z] \le 0`, we have:: >>> z = -3 >>> airybi(z) -0.1982896263749265432206449 >>> p = 2*power(-z,'3/2')/3 >>> sqrt(-mpf(z)/3)*(besselj('-1/3',p) - besselj('1/3',p)) -0.1982896263749265432206449 **Derivatives and integrals** Derivatives of the Bi-function (directly and using :func:`~mpmath.diff`):: >>> airybi(-3,1); diff(airybi,-3) -0.675611222685258537668032 -0.675611222685258537668032 >>> airybi(-3,2); diff(airybi,-3,2) 0.5948688791247796296619346 0.5948688791247796296619346 >>> airybi(1000,1); diff(airybi,1000) 1.710055114624614989262335e+9156 1.710055114624614989262335e+9156 Several derivatives at `z = 0`:: >>> airybi(0,0); airybi(0,1); airybi(0,2) 0.6149266274460007351509224 0.4482883573538263579148237 0.0 >>> airybi(0,3); airybi(0,4); airybi(0,5) 0.6149266274460007351509224 0.8965767147076527158296474 0.0 >>> airybi(0,15); airybi(0,16); airybi(0,17) 2238.332923903442675949357 5522.912562599140729510628 0.0 The integral of the Bi-function:: >>> airybi(3,-1); quad(airybi, [0,3]) 10.06200303130620056316655 10.06200303130620056316655 >>> airybi(-10,-1); quad(airybi, [0,-10]) -0.01504042480614002045135483 -0.01504042480614002045135483 Integrals of high or fractional order:: >>> airybi(-2,0.5); differint(airybi, -2, 0.5, 0) (0.0 + 0.5019859055341699223453257j) (0.0 + 0.5019859055341699223453257j) >>> airybi(-2,-4); differint(airybi,-2,-4,0) 0.2809314599922447252139092 0.2809314599922447252139092 >>> airybi(0,-1); airybi(0,-2); airybi(0,-3) 0.0 0.0 0.0 Integrals of the Bi-function can be evaluated at limit points:: >>> airybi(-1000000,-1); airybi(-inf,-1) 0.000002191261128063434047966873 0.0 >>> airybi(10,-1); airybi(+inf,-1) 147809803.1074067161675853 +inf >>> airybi(+inf,-2); airybi(+inf,-3) +inf +inf >>> airybi(-1000000,-2); airybi(-inf,-2) 0.4482883750599908479851085 0.4482883573538263579148237 >>> gamma('2/3')*power(3,'2/3')/(2*pi) 0.4482883573538263579148237 >>> airybi(-100000,-3); airybi(-inf,-3) -44828.52827206932872493133 -inf >>> airybi(-100000,-4); airybi(-inf,-4) 2241411040.437759489540248 +inf """ airyaizero = r""" Gives the `k`-th zero of the Airy Ai-function, i.e. the `k`-th number `a_k` ordered by magnitude for which `\operatorname{Ai}(a_k) = 0`. Optionally, with *derivative=1*, the corresponding zero `a'_k` of the derivative function, i.e. `\operatorname{Ai}'(a'_k) = 0`, is computed. **Examples** Some values of `a_k`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> airyaizero(1) -2.338107410459767038489197 >>> airyaizero(2) -4.087949444130970616636989 >>> airyaizero(3) -5.520559828095551059129856 >>> airyaizero(1000) -281.0315196125215528353364 Some values of `a'_k`:: >>> airyaizero(1,1) -1.018792971647471089017325 >>> airyaizero(2,1) -3.248197582179836537875424 >>> airyaizero(3,1) -4.820099211178735639400616 >>> airyaizero(1000,1) -280.9378080358935070607097 Verification:: >>> chop(airyai(airyaizero(1))) 0.0 >>> chop(airyai(airyaizero(1,1),1)) 0.0 """ airybizero = r""" With *complex=False*, gives the `k`-th real zero of the Airy Bi-function, i.e. the `k`-th number `b_k` ordered by magnitude for which `\operatorname{Bi}(b_k) = 0`. With *complex=True*, gives the `k`-th complex zero in the upper half plane `\beta_k`. Also the conjugate `\overline{\beta_k}` is a zero. Optionally, with *derivative=1*, the corresponding zero `b'_k` or `\beta'_k` of the derivative function, i.e. `\operatorname{Bi}'(b'_k) = 0` or `\operatorname{Bi}'(\beta'_k) = 0`, is computed. **Examples** Some values of `b_k`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> airybizero(1) -1.17371322270912792491998 >>> airybizero(2) -3.271093302836352715680228 >>> airybizero(3) -4.830737841662015932667709 >>> airybizero(1000) -280.9378112034152401578834 Some values of `b_k`:: >>> airybizero(1,1) -2.294439682614123246622459 >>> airybizero(2,1) -4.073155089071828215552369 >>> airybizero(3,1) -5.512395729663599496259593 >>> airybizero(1000,1) -281.0315164471118527161362 Some values of `\beta_k`:: >>> airybizero(1,complex=True) (0.9775448867316206859469927 + 2.141290706038744575749139j) >>> airybizero(2,complex=True) (1.896775013895336346627217 + 3.627291764358919410440499j) >>> airybizero(3,complex=True) (2.633157739354946595708019 + 4.855468179979844983174628j) >>> airybizero(1000,complex=True) (140.4978560578493018899793 + 243.3907724215792121244867j) Some values of `\beta'_k`:: >>> airybizero(1,1,complex=True) (0.2149470745374305676088329 + 1.100600143302797880647194j) >>> airybizero(2,1,complex=True) (1.458168309223507392028211 + 2.912249367458445419235083j) >>> airybizero(3,1,complex=True) (2.273760763013482299792362 + 4.254528549217097862167015j) >>> airybizero(1000,1,complex=True) (140.4509972835270559730423 + 243.3096175398562811896208j) Verification:: >>> chop(airybi(airybizero(1))) 0.0 >>> chop(airybi(airybizero(1,1),1)) 0.0 >>> u = airybizero(1,complex=True) >>> chop(airybi(u)) 0.0 >>> chop(airybi(conj(u))) 0.0 The complex zeros (in the upper and lower half-planes respectively) asymptotically approach the rays `z = R \exp(\pm i \pi /3)`:: >>> arg(airybizero(1,complex=True)) 1.142532510286334022305364 >>> arg(airybizero(1000,complex=True)) 1.047271114786212061583917 >>> arg(airybizero(1000000,complex=True)) 1.047197624741816183341355 >>> pi/3 1.047197551196597746154214 """ ellipk = r""" Evaluates the complete elliptic integral of the first kind, `K(m)`, defined by .. math :: K(m) = \int_0^{\pi/2} \frac{dt}{\sqrt{1-m \sin^2 t}} \, = \, \frac{\pi}{2} \,_2F_1\left(\frac{1}{2}, \frac{1}{2}, 1, m\right). Note that the argument is the parameter `m = k^2`, not the modulus `k` which is sometimes used. **Plots** .. literalinclude :: /modules/mpmath/plots/ellipk.py .. image :: /modules/mpmath/plots/ellipk.png **Examples** Values and limits include:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> ellipk(0) 1.570796326794896619231322 >>> ellipk(inf) (0.0 + 0.0j) >>> ellipk(-inf) 0.0 >>> ellipk(1) +inf >>> ellipk(-1) 1.31102877714605990523242 >>> ellipk(2) (1.31102877714605990523242 - 1.31102877714605990523242j) Verifying the defining integral and hypergeometric representation:: >>> ellipk(0.5) 1.85407467730137191843385 >>> quad(lambda t: (1-0.5*sin(t)**2)**-0.5, [0, pi/2]) 1.85407467730137191843385 >>> pi/2*hyp2f1(0.5,0.5,1,0.5) 1.85407467730137191843385 Evaluation is supported for arbitrary complex `m`:: >>> ellipk(3+4j) (0.9111955638049650086562171 + 0.6313342832413452438845091j) A definite integral:: >>> quad(ellipk, [0, 1]) 2.0 """ agm = r""" ``agm(a, b)`` computes the arithmetic-geometric mean of `a` and `b`, defined as the limit of the following iteration: .. math :: a_0 = a b_0 = b a_{n+1} = \frac{a_n+b_n}{2} b_{n+1} = \sqrt{a_n b_n} This function can be called with a single argument, computing `\mathrm{agm}(a,1) = \mathrm{agm}(1,a)`. **Examples** It is a well-known theorem that the geometric mean of two distinct positive numbers is less than the arithmetic mean. It follows that the arithmetic-geometric mean lies between the two means:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> a = mpf(3) >>> b = mpf(4) >>> sqrt(a*b) 3.46410161513775 >>> agm(a,b) 3.48202767635957 >>> (a+b)/2 3.5 The arithmetic-geometric mean is scale-invariant:: >>> agm(10*e, 10*pi) 29.261085515723 >>> 10*agm(e, pi) 29.261085515723 As an order-of-magnitude estimate, `\mathrm{agm}(1,x) \approx x` for large `x`:: >>> agm(10**10) 643448704.760133 >>> agm(10**50) 1.34814309345871e+48 For tiny `x`, `\mathrm{agm}(1,x) \approx -\pi/(2 \log(x/4))`:: >>> agm('0.01') 0.262166887202249 >>> -pi/2/log('0.0025') 0.262172347753122 The arithmetic-geometric mean can also be computed for complex numbers:: >>> agm(3, 2+j) (2.51055133276184 + 0.547394054060638j) The AGM iteration converges very quickly (each step doubles the number of correct digits), so :func:`~mpmath.agm` supports efficient high-precision evaluation:: >>> mp.dps = 10000 >>> a = agm(1,2) >>> str(a)[-10:] '1679581912' **Mathematical relations** The arithmetic-geometric mean may be used to evaluate the following two parametric definite integrals: .. math :: I_1 = \int_0^{\infty} \frac{1}{\sqrt{(x^2+a^2)(x^2+b^2)}} \,dx I_2 = \int_0^{\pi/2} \frac{1}{\sqrt{a^2 \cos^2(x) + b^2 \sin^2(x)}} \,dx We have:: >>> mp.dps = 15 >>> a = 3 >>> b = 4 >>> f1 = lambda x: ((x**2+a**2)*(x**2+b**2))**-0.5 >>> f2 = lambda x: ((a*cos(x))**2 + (b*sin(x))**2)**-0.5 >>> quad(f1, [0, inf]) 0.451115405388492 >>> quad(f2, [0, pi/2]) 0.451115405388492 >>> pi/(2*agm(a,b)) 0.451115405388492 A formula for `\Gamma(1/4)`:: >>> gamma(0.25) 3.62560990822191 >>> sqrt(2*sqrt(2*pi**3)/agm(1,sqrt(2))) 3.62560990822191 **Possible issues** The branch cut chosen for complex `a` and `b` is somewhat arbitrary. """ gegenbauer = r""" Evaluates the Gegenbauer polynomial, or ultraspherical polynomial, .. math :: C_n^{(a)}(z) = {n+2a-1 \choose n} \,_2F_1\left(-n, n+2a; a+\frac{1}{2}; \frac{1}{2}(1-z)\right). When `n` is a nonnegative integer, this formula gives a polynomial in `z` of degree `n`, but all parameters are permitted to be complex numbers. With `a = 1/2`, the Gegenbauer polynomial reduces to a Legendre polynomial. **Examples** Evaluation for arbitrary arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> gegenbauer(3, 0.5, -10) -2485.0 >>> gegenbauer(1000, 10, 100) 3.012757178975667428359374e+2322 >>> gegenbauer(2+3j, -0.75, -1000j) (-5038991.358609026523401901 + 9414549.285447104177860806j) Evaluation at negative integer orders:: >>> gegenbauer(-4, 2, 1.75) -1.0 >>> gegenbauer(-4, 3, 1.75) 0.0 >>> gegenbauer(-4, 2j, 1.75) 0.0 >>> gegenbauer(-7, 0.5, 3) 8989.0 The Gegenbauer polynomials solve the differential equation:: >>> n, a = 4.5, 1+2j >>> f = lambda z: gegenbauer(n, a, z) >>> for z in [0, 0.75, -0.5j]: ... chop((1-z**2)*diff(f,z,2) - (2*a+1)*z*diff(f,z) + n*(n+2*a)*f(z)) ... 0.0 0.0 0.0 The Gegenbauer polynomials have generating function `(1-2zt+t^2)^{-a}`:: >>> a, z = 2.5, 1 >>> taylor(lambda t: (1-2*z*t+t**2)**(-a), 0, 3) [1.0, 5.0, 15.0, 35.0] >>> [gegenbauer(n,a,z) for n in range(4)] [1.0, 5.0, 15.0, 35.0] The Gegenbauer polynomials are orthogonal on `[-1, 1]` with respect to the weight `(1-z^2)^{a-\frac{1}{2}}`:: >>> a, n, m = 2.5, 4, 5 >>> Cn = lambda z: gegenbauer(n, a, z, zeroprec=1000) >>> Cm = lambda z: gegenbauer(m, a, z, zeroprec=1000) >>> chop(quad(lambda z: Cn(z)*Cm(z)*(1-z**2)*(a-0.5), [-1, 1])) 0.0 """ laguerre = r""" Gives the generalized (associated) Laguerre polynomial, defined by .. math :: L_n^a(z) = \frac{\Gamma(n+b+1)}{\Gamma(b+1) \Gamma(n+1)} \,_1F_1(-n, a+1, z). With `a = 0` and `n` a nonnegative integer, this reduces to an ordinary Laguerre polynomial, the sequence of which begins `L_0(z) = 1, L_1(z) = 1-z, L_2(z) = z^2-2z+1, \ldots`. The Laguerre polynomials are orthogonal with respect to the weight `z^a e^{-z}` on `[0, \infty)`. **Plots** .. literalinclude :: /modules/mpmath/plots/laguerre.py .. image :: /modules/mpmath/plots/laguerre.png **Examples** Evaluation for arbitrary arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> laguerre(5, 0, 0.25) 0.03726399739583333333333333 >>> laguerre(1+j, 0.5, 2+3j) (4.474921610704496808379097 - 11.02058050372068958069241j) >>> laguerre(2, 0, 10000) 49980001.0 >>> laguerre(2.5, 0, 10000) -9.327764910194842158583189e+4328 The first few Laguerre polynomials, normalized to have integer coefficients:: >>> for n in range(7): ... chop(taylor(lambda z: fac(n)*laguerre(n, 0, z), 0, n)) ... [1.0] [1.0, -1.0] [2.0, -4.0, 1.0] [6.0, -18.0, 9.0, -1.0] [24.0, -96.0, 72.0, -16.0, 1.0] [120.0, -600.0, 600.0, -200.0, 25.0, -1.0] [720.0, -4320.0, 5400.0, -2400.0, 450.0, -36.0, 1.0] Verifying orthogonality:: >>> Lm = lambda t: laguerre(m,a,t) >>> Ln = lambda t: laguerre(n,a,t) >>> a, n, m = 2.5, 2, 3 >>> chop(quad(lambda t: exp(-t)*t**a*Lm(t)*Ln(t), [0,inf])) 0.0 """ hermite = r""" Evaluates the Hermite polynomial `H_n(z)`, which may be defined using the recurrence .. math :: H_0(z) = 1 H_1(z) = 2z H_{n+1} = 2z H_n(z) - 2n H_{n-1}(z). The Hermite polynomials are orthogonal on `(-\infty, \infty)` with respect to the weight `e^{-z^2}`. More generally, allowing arbitrary complex values of `n`, the Hermite function `H_n(z)` is defined as .. math :: H_n(z) = (2z)^n \,_2F_0\left(-\frac{n}{2}, \frac{1-n}{2}, -\frac{1}{z^2}\right) for `\Re{z} > 0`, or generally .. math :: H_n(z) = 2^n \sqrt{\pi} \left( \frac{1}{\Gamma\left(\frac{1-n}{2}\right)} \,_1F_1\left(-\frac{n}{2}, \frac{1}{2}, z^2\right) - \frac{2z}{\Gamma\left(-\frac{n}{2}\right)} \,_1F_1\left(\frac{1-n}{2}, \frac{3}{2}, z^2\right) \right). **Plots** .. literalinclude :: /modules/mpmath/plots/hermite.py .. image :: /modules/mpmath/plots/hermite.png **Examples** Evaluation for arbitrary arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> hermite(0, 10) 1.0 >>> hermite(1, 10); hermite(2, 10) 20.0 398.0 >>> hermite(10000, 2) 4.950440066552087387515653e+19334 >>> hermite(3, -10**8) -7999999999999998800000000.0 >>> hermite(-3, -10**8) 1.675159751729877682920301e+4342944819032534 >>> hermite(2+3j, -1+2j) (-0.07652130602993513389421901 - 0.1084662449961914580276007j) Coefficients of the first few Hermite polynomials are:: >>> for n in range(7): ... chop(taylor(lambda z: hermite(n, z), 0, n)) ... [1.0] [0.0, 2.0] [-2.0, 0.0, 4.0] [0.0, -12.0, 0.0, 8.0] [12.0, 0.0, -48.0, 0.0, 16.0] [0.0, 120.0, 0.0, -160.0, 0.0, 32.0] [-120.0, 0.0, 720.0, 0.0, -480.0, 0.0, 64.0] Values at `z = 0`:: >>> for n in range(-5, 9): ... hermite(n, 0) ... 0.02769459142039868792653387 0.08333333333333333333333333 0.2215567313631895034122709 0.5 0.8862269254527580136490837 1.0 0.0 -2.0 0.0 12.0 0.0 -120.0 0.0 1680.0 Hermite functions satisfy the differential equation:: >>> n = 4 >>> f = lambda z: hermite(n, z) >>> z = 1.5 >>> chop(diff(f,z,2) - 2*z*diff(f,z) + 2*n*f(z)) 0.0 Verifying orthogonality:: >>> chop(quad(lambda t: hermite(2,t)*hermite(4,t)*exp(-t**2), [-inf,inf])) 0.0 """ jacobi = r""" ``jacobi(n, a, b, x)`` evaluates the Jacobi polynomial `P_n^{(a,b)}(x)`. The Jacobi polynomials are a special case of the hypergeometric function `\,_2F_1` given by: .. math :: P_n^{(a,b)}(x) = {n+a \choose n} \,_2F_1\left(-n,1+a+b+n,a+1,\frac{1-x}{2}\right). Note that this definition generalizes to nonintegral values of `n`. When `n` is an integer, the hypergeometric series terminates after a finite number of terms, giving a polynomial in `x`. **Evaluation of Jacobi polynomials** A special evaluation is `P_n^{(a,b)}(1) = {n+a \choose n}`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> jacobi(4, 0.5, 0.25, 1) 2.4609375 >>> binomial(4+0.5, 4) 2.4609375 A Jacobi polynomial of degree `n` is equal to its Taylor polynomial of degree `n`. The explicit coefficients of Jacobi polynomials can therefore be recovered easily using :func:`~mpmath.taylor`:: >>> for n in range(5): ... nprint(taylor(lambda x: jacobi(n,1,2,x), 0, n)) ... [1.0] [-0.5, 2.5] [-0.75, -1.5, 5.25] [0.5, -3.5, -3.5, 10.5] [0.625, 2.5, -11.25, -7.5, 20.625] For nonintegral `n`, the Jacobi "polynomial" is no longer a polynomial:: >>> nprint(taylor(lambda x: jacobi(0.5,1,2,x), 0, 4)) [0.309983, 1.84119, -1.26933, 1.26699, -1.34808] **Orthogonality** The Jacobi polynomials are orthogonal on the interval `[-1, 1]` with respect to the weight function `w(x) = (1-x)^a (1+x)^b`. That is, `w(x) P_n^{(a,b)}(x) P_m^{(a,b)}(x)` integrates to zero if `m \ne n` and to a nonzero number if `m = n`. The orthogonality is easy to verify using numerical quadrature:: >>> P = jacobi >>> f = lambda x: (1-x)**a * (1+x)**b * P(m,a,b,x) * P(n,a,b,x) >>> a = 2 >>> b = 3 >>> m, n = 3, 4 >>> chop(quad(f, [-1, 1]), 1) 0.0 >>> m, n = 4, 4 >>> quad(f, [-1, 1]) 1.9047619047619 **Differential equation** The Jacobi polynomials are solutions of the differential equation .. math :: (1-x^2) y'' + (b-a-(a+b+2)x) y' + n (n+a+b+1) y = 0. We can verify that :func:`~mpmath.jacobi` approximately satisfies this equation:: >>> from mpmath import * >>> mp.dps = 15 >>> a = 2.5 >>> b = 4 >>> n = 3 >>> y = lambda x: jacobi(n,a,b,x) >>> x = pi >>> A0 = n*(n+a+b+1)*y(x) >>> A1 = (b-a-(a+b+2)*x)*diff(y,x) >>> A2 = (1-x**2)*diff(y,x,2) >>> nprint(A2 + A1 + A0, 1) 4.0e-12 The difference of order `10^{-12}` is as close to zero as it could be at 15-digit working precision, since the terms are large:: >>> A0, A1, A2 (26560.2328981879, -21503.7641037294, -5056.46879445852) """ legendre = r""" ``legendre(n, x)`` evaluates the Legendre polynomial `P_n(x)`. The Legendre polynomials are given by the formula .. math :: P_n(x) = \frac{1}{2^n n!} \frac{d^n}{dx^n} (x^2 -1)^n. Alternatively, they can be computed recursively using .. math :: P_0(x) = 1 P_1(x) = x (n+1) P_{n+1}(x) = (2n+1) x P_n(x) - n P_{n-1}(x). A third definition is in terms of the hypergeometric function `\,_2F_1`, whereby they can be generalized to arbitrary `n`: .. math :: P_n(x) = \,_2F_1\left(-n, n+1, 1, \frac{1-x}{2}\right) **Plots** .. literalinclude :: /modules/mpmath/plots/legendre.py .. image :: /modules/mpmath/plots/legendre.png **Basic evaluation** The Legendre polynomials assume fixed values at the points `x = -1` and `x = 1`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> nprint([legendre(n, 1) for n in range(6)]) [1.0, 1.0, 1.0, 1.0, 1.0, 1.0] >>> nprint([legendre(n, -1) for n in range(6)]) [1.0, -1.0, 1.0, -1.0, 1.0, -1.0] The coefficients of Legendre polynomials can be recovered using degree-`n` Taylor expansion:: >>> for n in range(5): ... nprint(chop(taylor(lambda x: legendre(n, x), 0, n))) ... [1.0] [0.0, 1.0] [-0.5, 0.0, 1.5] [0.0, -1.5, 0.0, 2.5] [0.375, 0.0, -3.75, 0.0, 4.375] The roots of Legendre polynomials are located symmetrically on the interval `[-1, 1]`:: >>> for n in range(5): ... nprint(polyroots(taylor(lambda x: legendre(n, x), 0, n)[::-1])) ... [] [0.0] [-0.57735, 0.57735] [-0.774597, 0.0, 0.774597] [-0.861136, -0.339981, 0.339981, 0.861136] An example of an evaluation for arbitrary `n`:: >>> legendre(0.75, 2+4j) (1.94952805264875 + 2.1071073099422j) **Orthogonality** The Legendre polynomials are orthogonal on `[-1, 1]` with respect to the trivial weight `w(x) = 1`. That is, `P_m(x) P_n(x)` integrates to zero if `m \ne n` and to `2/(2n+1)` if `m = n`:: >>> m, n = 3, 4 >>> quad(lambda x: legendre(m,x)*legendre(n,x), [-1, 1]) 0.0 >>> m, n = 4, 4 >>> quad(lambda x: legendre(m,x)*legendre(n,x), [-1, 1]) 0.222222222222222 **Differential equation** The Legendre polynomials satisfy the differential equation .. math :: ((1-x^2) y')' + n(n+1) y' = 0. We can verify this numerically:: >>> n = 3.6 >>> x = 0.73 >>> P = legendre >>> A = diff(lambda t: (1-t**2)*diff(lambda u: P(n,u), t), x) >>> B = n*(n+1)*P(n,x) >>> nprint(A+B,1) 9.0e-16 """ legenp = r""" Calculates the (associated) Legendre function of the first kind of degree *n* and order *m*, `P_n^m(z)`. Taking `m = 0` gives the ordinary Legendre function of the first kind, `P_n(z)`. The parameters may be complex numbers. In terms of the Gauss hypergeometric function, the (associated) Legendre function is defined as .. math :: P_n^m(z) = \frac{1}{\Gamma(1-m)} \frac{(1+z)^{m/2}}{(1-z)^{m/2}} \,_2F_1\left(-n, n+1, 1-m, \frac{1-z}{2}\right). With *type=3* instead of *type=2*, the alternative definition .. math :: \hat{P}_n^m(z) = \frac{1}{\Gamma(1-m)} \frac{(z+1)^{m/2}}{(z-1)^{m/2}} \,_2F_1\left(-n, n+1, 1-m, \frac{1-z}{2}\right). is used. These functions correspond respectively to ``LegendreP[n,m,2,z]`` and ``LegendreP[n,m,3,z]`` in Mathematica. The general solution of the (associated) Legendre differential equation .. math :: (1-z^2) f''(z) - 2zf'(z) + \left(n(n+1)-\frac{m^2}{1-z^2}\right)f(z) = 0 is given by `C_1 P_n^m(z) + C_2 Q_n^m(z)` for arbitrary constants `C_1`, `C_2`, where `Q_n^m(z)` is a Legendre function of the second kind as implemented by :func:`~mpmath.legenq`. **Examples** Evaluation for arbitrary parameters and arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> legenp(2, 0, 10); legendre(2, 10) 149.5 149.5 >>> legenp(-2, 0.5, 2.5) (1.972260393822275434196053 - 1.972260393822275434196053j) >>> legenp(2+3j, 1-j, -0.5+4j) (-3.335677248386698208736542 - 5.663270217461022307645625j) >>> chop(legenp(3, 2, -1.5, type=2)) 28.125 >>> chop(legenp(3, 2, -1.5, type=3)) -28.125 Verifying the associated Legendre differential equation:: >>> n, m = 2, -0.5 >>> C1, C2 = 1, -3 >>> f = lambda z: C1*legenp(n,m,z) + C2*legenq(n,m,z) >>> deq = lambda z: (1-z**2)*diff(f,z,2) - 2*z*diff(f,z) + \ ... (n*(n+1)-m**2/(1-z**2))*f(z) >>> for z in [0, 2, -1.5, 0.5+2j]: ... chop(deq(mpmathify(z))) ... 0.0 0.0 0.0 0.0 """ legenq = r""" Calculates the (associated) Legendre function of the second kind of degree *n* and order *m*, `Q_n^m(z)`. Taking `m = 0` gives the ordinary Legendre function of the second kind, `Q_n(z)`. The parameters may complex numbers. The Legendre functions of the second kind give a second set of solutions to the (associated) Legendre differential equation. (See :func:`~mpmath.legenp`.) Unlike the Legendre functions of the first kind, they are not polynomials of `z` for integer `n`, `m` but rational or logarithmic functions with poles at `z = \pm 1`. There are various ways to define Legendre functions of the second kind, giving rise to different complex structure. A version can be selected using the *type* keyword argument. The *type=2* and *type=3* functions are given respectively by .. math :: Q_n^m(z) = \frac{\pi}{2 \sin(\pi m)} \left( \cos(\pi m) P_n^m(z) - \frac{\Gamma(1+m+n)}{\Gamma(1-m+n)} P_n^{-m}(z)\right) \hat{Q}_n^m(z) = \frac{\pi}{2 \sin(\pi m)} e^{\pi i m} \left( \hat{P}_n^m(z) - \frac{\Gamma(1+m+n)}{\Gamma(1-m+n)} \hat{P}_n^{-m}(z)\right) where `P` and `\hat{P}` are the *type=2* and *type=3* Legendre functions of the first kind. The formulas above should be understood as limits when `m` is an integer. These functions correspond to ``LegendreQ[n,m,2,z]`` (or ``LegendreQ[n,m,z]``) and ``LegendreQ[n,m,3,z]`` in Mathematica. The *type=3* function is essentially the same as the function defined in Abramowitz & Stegun (eq. 8.1.3) but with `(z+1)^{m/2}(z-1)^{m/2}` instead of `(z^2-1)^{m/2}`, giving slightly different branches. **Examples** Evaluation for arbitrary parameters and arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> legenq(2, 0, 0.5) -0.8186632680417568557122028 >>> legenq(-1.5, -2, 2.5) (0.6655964618250228714288277 + 0.3937692045497259717762649j) >>> legenq(2-j, 3+4j, -6+5j) (-10001.95256487468541686564 - 6011.691337610097577791134j) Different versions of the function:: >>> legenq(2, 1, 0.5) 0.7298060598018049369381857 >>> legenq(2, 1, 1.5) (-7.902916572420817192300921 + 0.1998650072605976600724502j) >>> legenq(2, 1, 0.5, type=3) (2.040524284763495081918338 - 0.7298060598018049369381857j) >>> chop(legenq(2, 1, 1.5, type=3)) -0.1998650072605976600724502 """ chebyt = r""" ``chebyt(n, x)`` evaluates the Chebyshev polynomial of the first kind `T_n(x)`, defined by the identity .. math :: T_n(\cos x) = \cos(n x). The Chebyshev polynomials of the first kind are a special case of the Jacobi polynomials, and by extension of the hypergeometric function `\,_2F_1`. They can thus also be evaluated for nonintegral `n`. **Plots** .. literalinclude :: /modules/mpmath/plots/chebyt.py .. image :: /modules/mpmath/plots/chebyt.png **Basic evaluation** The coefficients of the `n`-th polynomial can be recovered using using degree-`n` Taylor expansion:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for n in range(5): ... nprint(chop(taylor(lambda x: chebyt(n, x), 0, n))) ... [1.0] [0.0, 1.0] [-1.0, 0.0, 2.0] [0.0, -3.0, 0.0, 4.0] [1.0, 0.0, -8.0, 0.0, 8.0] **Orthogonality** The Chebyshev polynomials of the first kind are orthogonal on the interval `[-1, 1]` with respect to the weight function `w(x) = 1/\sqrt{1-x^2}`:: >>> f = lambda x: chebyt(m,x)*chebyt(n,x)/sqrt(1-x**2) >>> m, n = 3, 4 >>> nprint(quad(f, [-1, 1]),1) 0.0 >>> m, n = 4, 4 >>> quad(f, [-1, 1]) 1.57079632596448 """ chebyu = r""" ``chebyu(n, x)`` evaluates the Chebyshev polynomial of the second kind `U_n(x)`, defined by the identity .. math :: U_n(\cos x) = \frac{\sin((n+1)x)}{\sin(x)}. The Chebyshev polynomials of the second kind are a special case of the Jacobi polynomials, and by extension of the hypergeometric function `\,_2F_1`. They can thus also be evaluated for nonintegral `n`. **Plots** .. literalinclude :: /modules/mpmath/plots/chebyu.py .. image :: /modules/mpmath/plots/chebyu.png **Basic evaluation** The coefficients of the `n`-th polynomial can be recovered using using degree-`n` Taylor expansion:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for n in range(5): ... nprint(chop(taylor(lambda x: chebyu(n, x), 0, n))) ... [1.0] [0.0, 2.0] [-1.0, 0.0, 4.0] [0.0, -4.0, 0.0, 8.0] [1.0, 0.0, -12.0, 0.0, 16.0] **Orthogonality** The Chebyshev polynomials of the second kind are orthogonal on the interval `[-1, 1]` with respect to the weight function `w(x) = \sqrt{1-x^2}`:: >>> f = lambda x: chebyu(m,x)*chebyu(n,x)*sqrt(1-x**2) >>> m, n = 3, 4 >>> quad(f, [-1, 1]) 0.0 >>> m, n = 4, 4 >>> quad(f, [-1, 1]) 1.5707963267949 """ besselj = r""" ``besselj(n, x, derivative=0)`` gives the Bessel function of the first kind `J_n(x)`. Bessel functions of the first kind are defined as solutions of the differential equation .. math :: x^2 y'' + x y' + (x^2 - n^2) y = 0 which appears, among other things, when solving the radial part of Laplace's equation in cylindrical coordinates. This equation has two solutions for given `n`, where the `J_n`-function is the solution that is nonsingular at `x = 0`. For positive integer `n`, `J_n(x)` behaves roughly like a sine (odd `n`) or cosine (even `n`) multiplied by a magnitude factor that decays slowly as `x \to \pm\infty`. Generally, `J_n` is a special case of the hypergeometric function `\,_0F_1`: .. math :: J_n(x) = \frac{x^n}{2^n \Gamma(n+1)} \,_0F_1\left(n+1,-\frac{x^2}{4}\right) With *derivative* = `m \ne 0`, the `m`-th derivative .. math :: \frac{d^m}{dx^m} J_n(x) is computed. **Plots** .. literalinclude :: /modules/mpmath/plots/besselj.py .. image :: /modules/mpmath/plots/besselj.png .. literalinclude :: /modules/mpmath/plots/besselj_c.py .. image :: /modules/mpmath/plots/besselj_c.png **Examples** Evaluation is supported for arbitrary arguments, and at arbitrary precision:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> besselj(2, 1000) -0.024777229528606 >>> besselj(4, 0.75) 0.000801070086542314 >>> besselj(2, 1000j) (-2.48071721019185e+432 + 6.41567059811949e-437j) >>> mp.dps = 25 >>> besselj(0.75j, 3+4j) (-2.778118364828153309919653 - 1.5863603889018621585533j) >>> mp.dps = 50 >>> besselj(1, pi) 0.28461534317975275734531059968613140570981118184947 Arguments may be large:: >>> mp.dps = 25 >>> besselj(0, 10000) -0.007096160353388801477265164 >>> besselj(0, 10**10) 0.000002175591750246891726859055 >>> besselj(2, 10**100) 7.337048736538615712436929e-51 >>> besselj(2, 10**5*j) (-3.540725411970948860173735e+43426 + 4.4949812409615803110051e-43433j) The Bessel functions of the first kind satisfy simple symmetries around `x = 0`:: >>> mp.dps = 15 >>> nprint([besselj(n,0) for n in range(5)]) [1.0, 0.0, 0.0, 0.0, 0.0] >>> nprint([besselj(n,pi) for n in range(5)]) [-0.304242, 0.284615, 0.485434, 0.333458, 0.151425] >>> nprint([besselj(n,-pi) for n in range(5)]) [-0.304242, -0.284615, 0.485434, -0.333458, 0.151425] Roots of Bessel functions are often used:: >>> nprint([findroot(j0, k) for k in [2, 5, 8, 11, 14]]) [2.40483, 5.52008, 8.65373, 11.7915, 14.9309] >>> nprint([findroot(j1, k) for k in [3, 7, 10, 13, 16]]) [3.83171, 7.01559, 10.1735, 13.3237, 16.4706] The roots are not periodic, but the distance between successive roots asymptotically approaches `2 \pi`. Bessel functions of the first kind have the following normalization:: >>> quadosc(j0, [0, inf], period=2*pi) 1.0 >>> quadosc(j1, [0, inf], period=2*pi) 1.0 For `n = 1/2` or `n = -1/2`, the Bessel function reduces to a trigonometric function:: >>> x = 10 >>> besselj(0.5, x), sqrt(2/(pi*x))*sin(x) (-0.13726373575505, -0.13726373575505) >>> besselj(-0.5, x), sqrt(2/(pi*x))*cos(x) (-0.211708866331398, -0.211708866331398) Derivatives of any order can be computed (negative orders correspond to integration):: >>> mp.dps = 25 >>> besselj(0, 7.5, 1) -0.1352484275797055051822405 >>> diff(lambda x: besselj(0,x), 7.5) -0.1352484275797055051822405 >>> besselj(0, 7.5, 10) -0.1377811164763244890135677 >>> diff(lambda x: besselj(0,x), 7.5, 10) -0.1377811164763244890135677 >>> besselj(0,7.5,-1) - besselj(0,3.5,-1) -0.1241343240399987693521378 >>> quad(j0, [3.5, 7.5]) -0.1241343240399987693521378 Differentiation with a noninteger order gives the fractional derivative in the sense of the Riemann-Liouville differintegral, as computed by :func:`~mpmath.differint`:: >>> mp.dps = 15 >>> besselj(1, 3.5, 0.75) -0.385977722939384 >>> differint(lambda x: besselj(1, x), 3.5, 0.75) -0.385977722939384 """ besseli = r""" ``besseli(n, x, derivative=0)`` gives the modified Bessel function of the first kind, .. math :: I_n(x) = i^{-n} J_n(ix). With *derivative* = `m \ne 0`, the `m`-th derivative .. math :: \frac{d^m}{dx^m} I_n(x) is computed. **Plots** .. literalinclude :: /modules/mpmath/plots/besseli.py .. image :: /modules/mpmath/plots/besseli.png .. literalinclude :: /modules/mpmath/plots/besseli_c.py .. image :: /modules/mpmath/plots/besseli_c.png **Examples** Some values of `I_n(x)`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> besseli(0,0) 1.0 >>> besseli(1,0) 0.0 >>> besseli(0,1) 1.266065877752008335598245 >>> besseli(3.5, 2+3j) (-0.2904369752642538144289025 - 0.4469098397654815837307006j) Arguments may be large:: >>> besseli(2, 1000) 2.480717210191852440616782e+432 >>> besseli(2, 10**10) 4.299602851624027900335391e+4342944813 >>> besseli(2, 6000+10000j) (-2.114650753239580827144204e+2603 + 4.385040221241629041351886e+2602j) For integers `n`, the following integral representation holds:: >>> mp.dps = 15 >>> n = 3 >>> x = 2.3 >>> quad(lambda t: exp(x*cos(t))*cos(n*t), [0,pi])/pi 0.349223221159309 >>> besseli(n,x) 0.349223221159309 Derivatives and antiderivatives of any order can be computed:: >>> mp.dps = 25 >>> besseli(2, 7.5, 1) 195.8229038931399062565883 >>> diff(lambda x: besseli(2,x), 7.5) 195.8229038931399062565883 >>> besseli(2, 7.5, 10) 153.3296508971734525525176 >>> diff(lambda x: besseli(2,x), 7.5, 10) 153.3296508971734525525176 >>> besseli(2,7.5,-1) - besseli(2,3.5,-1) 202.5043900051930141956876 >>> quad(lambda x: besseli(2,x), [3.5, 7.5]) 202.5043900051930141956876 """ bessely = r""" ``bessely(n, x, derivative=0)`` gives the Bessel function of the second kind, .. math :: Y_n(x) = \frac{J_n(x) \cos(\pi n) - J_{-n}(x)}{\sin(\pi n)}. For `n` an integer, this formula should be understood as a limit. With *derivative* = `m \ne 0`, the `m`-th derivative .. math :: \frac{d^m}{dx^m} Y_n(x) is computed. **Plots** .. literalinclude :: /modules/mpmath/plots/bessely.py .. image :: /modules/mpmath/plots/bessely.png .. literalinclude :: /modules/mpmath/plots/bessely_c.py .. image :: /modules/mpmath/plots/bessely_c.png **Examples** Some values of `Y_n(x)`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> bessely(0,0), bessely(1,0), bessely(2,0) (-inf, -inf, -inf) >>> bessely(1, pi) 0.3588729167767189594679827 >>> bessely(0.5, 3+4j) (9.242861436961450520325216 - 3.085042824915332562522402j) Arguments may be large:: >>> bessely(0, 10000) 0.00364780555898660588668872 >>> bessely(2.5, 10**50) -4.8952500412050989295774e-26 >>> bessely(2.5, -10**50) (0.0 + 4.8952500412050989295774e-26j) Derivatives and antiderivatives of any order can be computed:: >>> bessely(2, 3.5, 1) 0.3842618820422660066089231 >>> diff(lambda x: bessely(2, x), 3.5) 0.3842618820422660066089231 >>> bessely(0.5, 3.5, 1) -0.2066598304156764337900417 >>> diff(lambda x: bessely(0.5, x), 3.5) -0.2066598304156764337900417 >>> diff(lambda x: bessely(2, x), 0.5, 10) -208173867409.5547350101511 >>> bessely(2, 0.5, 10) -208173867409.5547350101511 >>> bessely(2, 100.5, 100) 0.02668487547301372334849043 >>> quad(lambda x: bessely(2,x), [1,3]) -1.377046859093181969213262 >>> bessely(2,3,-1) - bessely(2,1,-1) -1.377046859093181969213262 """ besselk = r""" ``besselk(n, x)`` gives the modified Bessel function of the second kind, .. math :: K_n(x) = \frac{\pi}{2} \frac{I_{-n}(x)-I_{n}(x)}{\sin(\pi n)} For `n` an integer, this formula should be understood as a limit. **Plots** .. literalinclude :: /modules/mpmath/plots/besselk.py .. image :: /modules/mpmath/plots/besselk.png .. literalinclude :: /modules/mpmath/plots/besselk_c.py .. image :: /modules/mpmath/plots/besselk_c.png **Examples** Evaluation is supported for arbitrary complex arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> besselk(0,1) 0.4210244382407083333356274 >>> besselk(0, -1) (0.4210244382407083333356274 - 3.97746326050642263725661j) >>> besselk(3.5, 2+3j) (-0.02090732889633760668464128 + 0.2464022641351420167819697j) >>> besselk(2+3j, 0.5) (0.9615816021726349402626083 + 0.1918250181801757416908224j) Arguments may be large:: >>> besselk(0, 100) 4.656628229175902018939005e-45 >>> besselk(1, 10**6) 4.131967049321725588398296e-434298 >>> besselk(1, 10**6*j) (0.001140348428252385844876706 - 0.0005200017201681152909000961j) >>> besselk(4.5, fmul(10**50, j, exact=True)) (1.561034538142413947789221e-26 + 1.243554598118700063281496e-25j) The point `x = 0` is a singularity (logarithmic if `n = 0`):: >>> besselk(0,0) +inf >>> besselk(1,0) +inf >>> for n in range(-4, 5): ... print(besselk(n, '1e-1000')) ... 4.8e+4001 8.0e+3000 2.0e+2000 1.0e+1000 2302.701024509704096466802 1.0e+1000 2.0e+2000 8.0e+3000 4.8e+4001 """ hankel1 = r""" ``hankel1(n,x)`` computes the Hankel function of the first kind, which is the complex combination of Bessel functions given by .. math :: H_n^{(1)}(x) = J_n(x) + i Y_n(x). **Plots** .. literalinclude :: /modules/mpmath/plots/hankel1.py .. image :: /modules/mpmath/plots/hankel1.png .. literalinclude :: /modules/mpmath/plots/hankel1_c.py .. image :: /modules/mpmath/plots/hankel1_c.png **Examples** The Hankel function is generally complex-valued:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> hankel1(2, pi) (0.4854339326315091097054957 - 0.0999007139290278787734903j) >>> hankel1(3.5, pi) (0.2340002029630507922628888 - 0.6419643823412927142424049j) """ hankel2 = r""" ``hankel2(n,x)`` computes the Hankel function of the second kind, which is the complex combination of Bessel functions given by .. math :: H_n^{(2)}(x) = J_n(x) - i Y_n(x). **Plots** .. literalinclude :: /modules/mpmath/plots/hankel2.py .. image :: /modules/mpmath/plots/hankel2.png .. literalinclude :: /modules/mpmath/plots/hankel2_c.py .. image :: /modules/mpmath/plots/hankel2_c.png **Examples** The Hankel function is generally complex-valued:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> hankel2(2, pi) (0.4854339326315091097054957 + 0.0999007139290278787734903j) >>> hankel2(3.5, pi) (0.2340002029630507922628888 + 0.6419643823412927142424049j) """ lambertw = r""" The Lambert W function `W(z)` is defined as the inverse function of `w \exp(w)`. In other words, the value of `W(z)` is such that `z = W(z) \exp(W(z))` for any complex number `z`. The Lambert W function is a multivalued function with infinitely many branches `W_k(z)`, indexed by `k \in \mathbb{Z}`. Each branch gives a different solution `w` of the equation `z = w \exp(w)`. All branches are supported by :func:`~mpmath.lambertw`: * ``lambertw(z)`` gives the principal solution (branch 0) * ``lambertw(z, k)`` gives the solution on branch `k` The Lambert W function has two partially real branches: the principal branch (`k = 0`) is real for real `z > -1/e`, and the `k = -1` branch is real for `-1/e < z < 0`. All branches except `k = 0` have a logarithmic singularity at `z = 0`. The definition, implementation and choice of branches is based on [Corless]_. **Plots** .. literalinclude :: /modules/mpmath/plots/lambertw.py .. image :: /modules/mpmath/plots/lambertw.png .. literalinclude :: /modules/mpmath/plots/lambertw_c.py .. image :: /modules/mpmath/plots/lambertw_c.png **Basic examples** The Lambert W function is the inverse of `w \exp(w)`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> w = lambertw(1) >>> w 0.5671432904097838729999687 >>> w*exp(w) 1.0 Any branch gives a valid inverse:: >>> w = lambertw(1, k=3) >>> w (-2.853581755409037807206819 + 17.11353553941214591260783j) >>> w = lambertw(1, k=25) >>> w (-5.047020464221569709378686 + 155.4763860949415867162066j) >>> chop(w*exp(w)) 1.0 **Applications to equation-solving** The Lambert W function may be used to solve various kinds of equations, such as finding the value of the infinite power tower `z^{z^{z^{\ldots}}}`:: >>> def tower(z, n): ... if n == 0: ... return z ... return z ** tower(z, n-1) ... >>> tower(mpf(0.5), 100) 0.6411857445049859844862005 >>> -lambertw(-log(0.5))/log(0.5) 0.6411857445049859844862005 **Properties** The Lambert W function grows roughly like the natural logarithm for large arguments:: >>> lambertw(1000); log(1000) 5.249602852401596227126056 6.907755278982137052053974 >>> lambertw(10**100); log(10**100) 224.8431064451185015393731 230.2585092994045684017991 The principal branch of the Lambert W function has a rational Taylor series expansion around `z = 0`:: >>> nprint(taylor(lambertw, 0, 6), 10) [0.0, 1.0, -1.0, 1.5, -2.666666667, 5.208333333, -10.8] Some special values and limits are:: >>> lambertw(0) 0.0 >>> lambertw(1) 0.5671432904097838729999687 >>> lambertw(e) 1.0 >>> lambertw(inf) +inf >>> lambertw(0, k=-1) -inf >>> lambertw(0, k=3) -inf >>> lambertw(inf, k=2) (+inf + 12.56637061435917295385057j) >>> lambertw(inf, k=3) (+inf + 18.84955592153875943077586j) >>> lambertw(-inf, k=3) (+inf + 21.9911485751285526692385j) The `k = 0` and `k = -1` branches join at `z = -1/e` where `W(z) = -1` for both branches. Since `-1/e` can only be represented approximately with binary floating-point numbers, evaluating the Lambert W function at this point only gives `-1` approximately:: >>> lambertw(-1/e, 0) -0.9999999999998371330228251 >>> lambertw(-1/e, -1) -1.000000000000162866977175 If `-1/e` happens to round in the negative direction, there might be a small imaginary part:: >>> mp.dps = 15 >>> lambertw(-1/e) (-1.0 + 8.22007971483662e-9j) >>> lambertw(-1/e+eps) -0.999999966242188 **References** 1. [Corless]_ """ barnesg = r""" Evaluates the Barnes G-function, which generalizes the superfactorial (:func:`~mpmath.superfac`) and by extension also the hyperfactorial (:func:`~mpmath.hyperfac`) to the complex numbers in an analogous way to how the gamma function generalizes the ordinary factorial. The Barnes G-function may be defined in terms of a Weierstrass product: .. math :: G(z+1) = (2\pi)^{z/2} e^{-[z(z+1)+\gamma z^2]/2} \prod_{n=1}^\infty \left[\left(1+\frac{z}{n}\right)^ne^{-z+z^2/(2n)}\right] For positive integers `n`, we have have relation to superfactorials `G(n) = \mathrm{sf}(n-2) = 0! \cdot 1! \cdots (n-2)!`. **Examples** Some elementary values and limits of the Barnes G-function:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> barnesg(1), barnesg(2), barnesg(3) (1.0, 1.0, 1.0) >>> barnesg(4) 2.0 >>> barnesg(5) 12.0 >>> barnesg(6) 288.0 >>> barnesg(7) 34560.0 >>> barnesg(8) 24883200.0 >>> barnesg(inf) +inf >>> barnesg(0), barnesg(-1), barnesg(-2) (0.0, 0.0, 0.0) Closed-form values are known for some rational arguments:: >>> barnesg('1/2') 0.603244281209446 >>> sqrt(exp(0.25+log(2)/12)/sqrt(pi)/glaisher**3) 0.603244281209446 >>> barnesg('1/4') 0.29375596533861 >>> nthroot(exp('3/8')/exp(catalan/pi)/ ... gamma(0.25)**3/sqrt(glaisher)**9, 4) 0.29375596533861 The Barnes G-function satisfies the functional equation `G(z+1) = \Gamma(z) G(z)`:: >>> z = pi >>> barnesg(z+1) 2.39292119327948 >>> gamma(z)*barnesg(z) 2.39292119327948 The asymptotic growth rate of the Barnes G-function is related to the Glaisher-Kinkelin constant:: >>> limit(lambda n: barnesg(n+1)/(n**(n**2/2-mpf(1)/12)* ... (2*pi)**(n/2)*exp(-3*n**2/4)), inf) 0.847536694177301 >>> exp('1/12')/glaisher 0.847536694177301 The Barnes G-function can be differentiated in closed form:: >>> z = 3 >>> diff(barnesg, z) 0.264507203401607 >>> barnesg(z)*((z-1)*psi(0,z)-z+(log(2*pi)+1)/2) 0.264507203401607 Evaluation is supported for arbitrary arguments and at arbitrary precision:: >>> barnesg(6.5) 2548.7457695685 >>> barnesg(-pi) 0.00535976768353037 >>> barnesg(3+4j) (-0.000676375932234244 - 4.42236140124728e-5j) >>> mp.dps = 50 >>> barnesg(1/sqrt(2)) 0.81305501090451340843586085064413533788206204124732 >>> q = barnesg(10j) >>> q.real 0.000000000021852360840356557241543036724799812371995850552234 >>> q.imag -0.00000000000070035335320062304849020654215545839053210041457588 >>> mp.dps = 15 >>> barnesg(100) 3.10361006263698e+6626 >>> barnesg(-101) 0.0 >>> barnesg(-10.5) 5.94463017605008e+25 >>> barnesg(-10000.5) -6.14322868174828e+167480422 >>> barnesg(1000j) (5.21133054865546e-1173597 + 4.27461836811016e-1173597j) >>> barnesg(-1000+1000j) (2.43114569750291e+1026623 + 2.24851410674842e+1026623j) **References** 1. Whittaker & Watson, *A Course of Modern Analysis*, Cambridge University Press, 4th edition (1927), p.264 2. http://en.wikipedia.org/wiki/Barnes_G-function 3. http://mathworld.wolfram.com/BarnesG-Function.html """ superfac = r""" Computes the superfactorial, defined as the product of consecutive factorials .. math :: \mathrm{sf}(n) = \prod_{k=1}^n k! For general complex `z`, `\mathrm{sf}(z)` is defined in terms of the Barnes G-function (see :func:`~mpmath.barnesg`). **Examples** The first few superfactorials are (OEIS A000178):: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for n in range(10): ... print("%s %s" % (n, superfac(n))) ... 0 1.0 1 1.0 2 2.0 3 12.0 4 288.0 5 34560.0 6 24883200.0 7 125411328000.0 8 5.05658474496e+15 9 1.83493347225108e+21 Superfactorials grow very rapidly:: >>> superfac(1000) 3.24570818422368e+1177245 >>> superfac(10**10) 2.61398543581249e+467427913956904067453 Evaluation is supported for arbitrary arguments:: >>> mp.dps = 25 >>> superfac(pi) 17.20051550121297985285333 >>> superfac(2+3j) (-0.005915485633199789627466468 + 0.008156449464604044948738263j) >>> diff(superfac, 1) 0.2645072034016070205673056 **References** 1. http://www.research.att.com/~njas/sequences/A000178 """ hyperfac = r""" Computes the hyperfactorial, defined for integers as the product .. math :: H(n) = \prod_{k=1}^n k^k. The hyperfactorial satisfies the recurrence formula `H(z) = z^z H(z-1)`. It can be defined more generally in terms of the Barnes G-function (see :func:`~mpmath.barnesg`) and the gamma function by the formula .. math :: H(z) = \frac{\Gamma(z+1)^z}{G(z)}. The extension to complex numbers can also be done via the integral representation .. math :: H(z) = (2\pi)^{-z/2} \exp \left[ {z+1 \choose 2} + \int_0^z \log(t!)\,dt \right]. **Examples** The rapidly-growing sequence of hyperfactorials begins (OEIS A002109):: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for n in range(10): ... print("%s %s" % (n, hyperfac(n))) ... 0 1.0 1 1.0 2 4.0 3 108.0 4 27648.0 5 86400000.0 6 4031078400000.0 7 3.3197663987712e+18 8 5.56964379417266e+25 9 2.15779412229419e+34 Some even larger hyperfactorials are:: >>> hyperfac(1000) 5.46458120882585e+1392926 >>> hyperfac(10**10) 4.60408207642219e+489142638002418704309 The hyperfactorial can be evaluated for arbitrary arguments:: >>> hyperfac(0.5) 0.880449235173423 >>> diff(hyperfac, 1) 0.581061466795327 >>> hyperfac(pi) 205.211134637462 >>> hyperfac(-10+1j) (3.01144471378225e+46 - 2.45285242480185e+46j) The recurrence property of the hyperfactorial holds generally:: >>> z = 3-4*j >>> hyperfac(z) (-4.49795891462086e-7 - 6.33262283196162e-7j) >>> z**z * hyperfac(z-1) (-4.49795891462086e-7 - 6.33262283196162e-7j) >>> z = mpf(-0.6) >>> chop(z**z * hyperfac(z-1)) 1.28170142849352 >>> hyperfac(z) 1.28170142849352 The hyperfactorial may also be computed using the integral definition:: >>> z = 2.5 >>> hyperfac(z) 15.9842119922237 >>> (2*pi)**(-z/2)*exp(binomial(z+1,2) + ... quad(lambda t: loggamma(t+1), [0, z])) 15.9842119922237 :func:`~mpmath.hyperfac` supports arbitrary-precision evaluation:: >>> mp.dps = 50 >>> hyperfac(10) 215779412229418562091680268288000000000000000.0 >>> hyperfac(1/sqrt(2)) 0.89404818005227001975423476035729076375705084390942 **References** 1. http://www.research.att.com/~njas/sequences/A002109 2. http://mathworld.wolfram.com/Hyperfactorial.html """ rgamma = r""" Computes the reciprocal of the gamma function, `1/\Gamma(z)`. This function evaluates to zero at the poles of the gamma function, `z = 0, -1, -2, \ldots`. **Examples** Basic examples:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> rgamma(1) 1.0 >>> rgamma(4) 0.1666666666666666666666667 >>> rgamma(0); rgamma(-1) 0.0 0.0 >>> rgamma(1000) 2.485168143266784862783596e-2565 >>> rgamma(inf) 0.0 A definite integral that can be evaluated in terms of elementary integrals:: >>> quad(rgamma, [0,inf]) 2.807770242028519365221501 >>> e + quad(lambda t: exp(-t)/(pi**2+log(t)**2), [0,inf]) 2.807770242028519365221501 """ loggamma = r""" Computes the principal branch of the log-gamma function, `\ln \Gamma(z)`. Unlike `\ln(\Gamma(z))`, which has infinitely many complex branch cuts, the principal log-gamma function only has a single branch cut along the negative half-axis. The principal branch continuously matches the asymptotic Stirling expansion .. math :: \ln \Gamma(z) \sim \frac{\ln(2 \pi)}{2} + \left(z-\frac{1}{2}\right) \ln(z) - z + O(z^{-1}). The real parts of both functions agree, but their imaginary parts generally differ by `2 n \pi` for some `n \in \mathbb{Z}`. They coincide for `z \in \mathbb{R}, z > 0`. Computationally, it is advantageous to use :func:`~mpmath.loggamma` instead of :func:`~mpmath.gamma` for extremely large arguments. **Examples** Comparing with `\ln(\Gamma(z))`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> loggamma('13.2'); log(gamma('13.2')) 20.49400419456603678498394 20.49400419456603678498394 >>> loggamma(3+4j) (-1.756626784603784110530604 + 4.742664438034657928194889j) >>> log(gamma(3+4j)) (-1.756626784603784110530604 - 1.540520869144928548730397j) >>> log(gamma(3+4j)) + 2*pi*j (-1.756626784603784110530604 + 4.742664438034657928194889j) Note the imaginary parts for negative arguments:: >>> loggamma(-0.5); loggamma(-1.5); loggamma(-2.5) (1.265512123484645396488946 - 3.141592653589793238462643j) (0.8600470153764810145109327 - 6.283185307179586476925287j) (-0.05624371649767405067259453 - 9.42477796076937971538793j) Some special values:: >>> loggamma(1); loggamma(2) 0.0 0.0 >>> loggamma(3); +ln2 0.6931471805599453094172321 0.6931471805599453094172321 >>> loggamma(3.5); log(15*sqrt(pi)/8) 1.200973602347074224816022 1.200973602347074224816022 >>> loggamma(inf) +inf Huge arguments are permitted:: >>> loggamma('1e30') 6.807755278982137052053974e+31 >>> loggamma('1e300') 6.897755278982137052053974e+302 >>> loggamma('1e3000') 6.906755278982137052053974e+3003 >>> loggamma('1e100000000000000000000') 2.302585092994045684007991e+100000000000000000020 >>> loggamma('1e30j') (-1.570796326794896619231322e+30 + 6.807755278982137052053974e+31j) >>> loggamma('1e300j') (-1.570796326794896619231322e+300 + 6.897755278982137052053974e+302j) >>> loggamma('1e3000j') (-1.570796326794896619231322e+3000 + 6.906755278982137052053974e+3003j) The log-gamma function can be integrated analytically on any interval of unit length:: >>> z = 0 >>> quad(loggamma, [z,z+1]); log(2*pi)/2 0.9189385332046727417803297 0.9189385332046727417803297 >>> z = 3+4j >>> quad(loggamma, [z,z+1]); (log(z)-1)*z + log(2*pi)/2 (-0.9619286014994750641314421 + 5.219637303741238195688575j) (-0.9619286014994750641314421 + 5.219637303741238195688575j) The derivatives of the log-gamma function are given by the polygamma function (:func:`~mpmath.psi`):: >>> diff(loggamma, -4+3j); psi(0, -4+3j) (1.688493531222971393607153 + 2.554898911356806978892748j) (1.688493531222971393607153 + 2.554898911356806978892748j) >>> diff(loggamma, -4+3j, 2); psi(1, -4+3j) (-0.1539414829219882371561038 - 0.1020485197430267719746479j) (-0.1539414829219882371561038 - 0.1020485197430267719746479j) The log-gamma function satisfies an additive form of the recurrence relation for the ordinary gamma function:: >>> z = 2+3j >>> loggamma(z); loggamma(z+1) - log(z) (-2.092851753092733349564189 + 2.302396543466867626153708j) (-2.092851753092733349564189 + 2.302396543466867626153708j) """ siegeltheta = r""" Computes the Riemann-Siegel theta function, .. math :: \theta(t) = \frac{ \log\Gamma\left(\frac{1+2it}{4}\right) - \log\Gamma\left(\frac{1-2it}{4}\right) }{2i} - \frac{\log \pi}{2} t. The Riemann-Siegel theta function is important in providing the phase factor for the Z-function (see :func:`~mpmath.siegelz`). Evaluation is supported for real and complex arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> siegeltheta(0) 0.0 >>> siegeltheta(inf) +inf >>> siegeltheta(-inf) -inf >>> siegeltheta(1) -1.767547952812290388302216 >>> siegeltheta(10+0.25j) (-3.068638039426838572528867 + 0.05804937947429712998395177j) Arbitrary derivatives may be computed with derivative = k >>> siegeltheta(1234, derivative=2) 0.0004051864079114053109473741 >>> diff(siegeltheta, 1234, n=2) 0.0004051864079114053109473741 The Riemann-Siegel theta function has odd symmetry around `t = 0`, two local extreme points and three real roots including 0 (located symmetrically):: >>> nprint(chop(taylor(siegeltheta, 0, 5))) [0.0, -2.68609, 0.0, 2.69433, 0.0, -6.40218] >>> findroot(diffun(siegeltheta), 7) 6.28983598883690277966509 >>> findroot(siegeltheta, 20) 17.84559954041086081682634 For large `t`, there is a famous asymptotic formula for `\theta(t)`, to first order given by:: >>> t = mpf(10**6) >>> siegeltheta(t) 5488816.353078403444882823 >>> -t*log(2*pi/t)/2-t/2 5488816.745777464310273645 """ grampoint = r""" Gives the `n`-th Gram point `g_n`, defined as the solution to the equation `\theta(g_n) = \pi n` where `\theta(t)` is the Riemann-Siegel theta function (:func:`~mpmath.siegeltheta`). The first few Gram points are:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> grampoint(0) 17.84559954041086081682634 >>> grampoint(1) 23.17028270124630927899664 >>> grampoint(2) 27.67018221781633796093849 >>> grampoint(3) 31.71797995476405317955149 Checking the definition:: >>> siegeltheta(grampoint(3)) 9.42477796076937971538793 >>> 3*pi 9.42477796076937971538793 A large Gram point:: >>> grampoint(10**10) 3293531632.728335454561153 Gram points are useful when studying the Z-function (:func:`~mpmath.siegelz`). See the documentation of that function for additional examples. :func:`~mpmath.grampoint` can solve the defining equation for nonintegral `n`. There is a fixed point where `g(x) = x`:: >>> findroot(lambda x: grampoint(x) - x, 10000) 9146.698193171459265866198 **References** 1. http://mathworld.wolfram.com/GramPoint.html """ siegelz = r""" Computes the Z-function, also known as the Riemann-Siegel Z function, .. math :: Z(t) = e^{i \theta(t)} \zeta(1/2+it) where `\zeta(s)` is the Riemann zeta function (:func:`~mpmath.zeta`) and where `\theta(t)` denotes the Riemann-Siegel theta function (see :func:`~mpmath.siegeltheta`). Evaluation is supported for real and complex arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> siegelz(1) -0.7363054628673177346778998 >>> siegelz(3+4j) (-0.1852895764366314976003936 - 0.2773099198055652246992479j) The first four derivatives are supported, using the optional *derivative* keyword argument:: >>> siegelz(1234567, derivative=3) 56.89689348495089294249178 >>> diff(siegelz, 1234567, n=3) 56.89689348495089294249178 The Z-function has a Maclaurin expansion:: >>> nprint(chop(taylor(siegelz, 0, 4))) [-1.46035, 0.0, 2.73588, 0.0, -8.39357] The Z-function `Z(t)` is equal to `\pm |\zeta(s)|` on the critical line `s = 1/2+it` (i.e. for real arguments `t` to `Z`). Its zeros coincide with those of the Riemann zeta function:: >>> findroot(siegelz, 14) 14.13472514173469379045725 >>> findroot(siegelz, 20) 21.02203963877155499262848 >>> findroot(zeta, 0.5+14j) (0.5 + 14.13472514173469379045725j) >>> findroot(zeta, 0.5+20j) (0.5 + 21.02203963877155499262848j) Since the Z-function is real-valued on the critical line (and unlike `|\zeta(s)|` analytic), it is useful for investigating the zeros of the Riemann zeta function. For example, one can use a root-finding algorithm based on sign changes:: >>> findroot(siegelz, [100, 200], solver='bisect') 176.4414342977104188888926 To locate roots, Gram points `g_n` which can be computed by :func:`~mpmath.grampoint` are useful. If `(-1)^n Z(g_n)` is positive for two consecutive `n`, then `Z(t)` must have a zero between those points:: >>> g10 = grampoint(10) >>> g11 = grampoint(11) >>> (-1)**10 * siegelz(g10) > 0 True >>> (-1)**11 * siegelz(g11) > 0 True >>> findroot(siegelz, [g10, g11], solver='bisect') 56.44624769706339480436776 >>> g10, g11 (54.67523744685325626632663, 57.54516517954725443703014) """ riemannr = r""" Evaluates the Riemann R function, a smooth approximation of the prime counting function `\pi(x)` (see :func:`~mpmath.primepi`). The Riemann R function gives a fast numerical approximation useful e.g. to roughly estimate the number of primes in a given interval. The Riemann R function is computed using the rapidly convergent Gram series, .. math :: R(x) = 1 + \sum_{k=1}^{\infty} \frac{\log^k x}{k k! \zeta(k+1)}. From the Gram series, one sees that the Riemann R function is a well-defined analytic function (except for a branch cut along the negative real half-axis); it can be evaluated for arbitrary real or complex arguments. The Riemann R function gives a very accurate approximation of the prime counting function. For example, it is wrong by at most 2 for `x < 1000`, and for `x = 10^9` differs from the exact value of `\pi(x)` by 79, or less than two parts in a million. It is about 10 times more accurate than the logarithmic integral estimate (see :func:`~mpmath.li`), which however is even faster to evaluate. It is orders of magnitude more accurate than the extremely fast `x/\log x` estimate. **Examples** For small arguments, the Riemann R function almost exactly gives the prime counting function if rounded to the nearest integer:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> primepi(50), riemannr(50) (15, 14.9757023241462) >>> max(abs(primepi(n)-int(round(riemannr(n)))) for n in range(100)) 1 >>> max(abs(primepi(n)-int(round(riemannr(n)))) for n in range(300)) 2 The Riemann R function can be evaluated for arguments far too large for exact determination of `\pi(x)` to be computationally feasible with any presently known algorithm:: >>> riemannr(10**30) 1.46923988977204e+28 >>> riemannr(10**100) 4.3619719871407e+97 >>> riemannr(10**1000) 4.3448325764012e+996 A comparison of the Riemann R function and logarithmic integral estimates for `\pi(x)` using exact values of `\pi(10^n)` up to `n = 9`. The fractional error is shown in parentheses:: >>> exact = [4,25,168,1229,9592,78498,664579,5761455,50847534] >>> for n, p in enumerate(exact): ... n += 1 ... r, l = riemannr(10**n), li(10**n) ... rerr, lerr = nstr((r-p)/p,3), nstr((l-p)/p,3) ... print("%i %i %s(%s) %s(%s)" % (n, p, r, rerr, l, lerr)) ... 1 4 4.56458314100509(0.141) 6.1655995047873(0.541) 2 25 25.6616332669242(0.0265) 30.1261415840796(0.205) 3 168 168.359446281167(0.00214) 177.609657990152(0.0572) 4 1229 1226.93121834343(-0.00168) 1246.13721589939(0.0139) 5 9592 9587.43173884197(-0.000476) 9629.8090010508(0.00394) 6 78498 78527.3994291277(0.000375) 78627.5491594622(0.00165) 7 664579 664667.447564748(0.000133) 664918.405048569(0.000511) 8 5761455 5761551.86732017(1.68e-5) 5762209.37544803(0.000131) 9 50847534 50847455.4277214(-1.55e-6) 50849234.9570018(3.35e-5) The derivative of the Riemann R function gives the approximate probability for a number of magnitude `x` to be prime:: >>> diff(riemannr, 1000) 0.141903028110784 >>> mpf(primepi(1050) - primepi(950)) / 100 0.15 Evaluation is supported for arbitrary arguments and at arbitrary precision:: >>> mp.dps = 30 >>> riemannr(7.5) 3.72934743264966261918857135136 >>> riemannr(-4+2j) (-0.551002208155486427591793957644 + 2.16966398138119450043195899746j) """ primepi = r""" Evaluates the prime counting function, `\pi(x)`, which gives the number of primes less than or equal to `x`. The argument `x` may be fractional. The prime counting function is very expensive to evaluate precisely for large `x`, and the present implementation is not optimized in any way. For numerical approximation of the prime counting function, it is better to use :func:`~mpmath.primepi2` or :func:`~mpmath.riemannr`. Some values of the prime counting function:: >>> from mpmath import * >>> [primepi(k) for k in range(20)] [0, 0, 1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 8] >>> primepi(3.5) 2 >>> primepi(100000) 9592 """ primepi2 = r""" Returns an interval (as an ``mpi`` instance) providing bounds for the value of the prime counting function `\pi(x)`. For small `x`, :func:`~mpmath.primepi2` returns an exact interval based on the output of :func:`~mpmath.primepi`. For `x > 2656`, a loose interval based on Schoenfeld's inequality .. math :: |\pi(x) - \mathrm{li}(x)| < \frac{\sqrt x \log x}{8 \pi} is returned. This estimate is rigorous assuming the truth of the Riemann hypothesis, and can be computed very quickly. **Examples** Exact values of the prime counting function for small `x`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> iv.dps = 15; iv.pretty = True >>> primepi2(10) [4.0, 4.0] >>> primepi2(100) [25.0, 25.0] >>> primepi2(1000) [168.0, 168.0] Loose intervals are generated for moderately large `x`: >>> primepi2(10000), primepi(10000) ([1209.0, 1283.0], 1229) >>> primepi2(50000), primepi(50000) ([5070.0, 5263.0], 5133) As `x` increases, the absolute error gets worse while the relative error improves. The exact value of `\pi(10^{23})` is 1925320391606803968923, and :func:`~mpmath.primepi2` gives 9 significant digits:: >>> p = primepi2(10**23) >>> p [1.9253203909477020467e+21, 1.925320392280406229e+21] >>> mpf(p.delta) / mpf(p.a) 6.9219865355293e-10 A more precise, nonrigorous estimate for `\pi(x)` can be obtained using the Riemann R function (:func:`~mpmath.riemannr`). For large enough `x`, the value returned by :func:`~mpmath.primepi2` essentially amounts to a small perturbation of the value returned by :func:`~mpmath.riemannr`:: >>> primepi2(10**100) [4.3619719871407024816e+97, 4.3619719871407032404e+97] >>> riemannr(10**100) 4.3619719871407e+97 """ primezeta = r""" Computes the prime zeta function, which is defined in analogy with the Riemann zeta function (:func:`~mpmath.zeta`) as .. math :: P(s) = \sum_p \frac{1}{p^s} where the sum is taken over all prime numbers `p`. Although this sum only converges for `\mathrm{Re}(s) > 1`, the function is defined by analytic continuation in the half-plane `\mathrm{Re}(s) > 0`. **Examples** Arbitrary-precision evaluation for real and complex arguments is supported:: >>> from mpmath import * >>> mp.dps = 30; mp.pretty = True >>> primezeta(2) 0.452247420041065498506543364832 >>> primezeta(pi) 0.15483752698840284272036497397 >>> mp.dps = 50 >>> primezeta(3) 0.17476263929944353642311331466570670097541212192615 >>> mp.dps = 20 >>> primezeta(3+4j) (-0.12085382601645763295 - 0.013370403397787023602j) The prime zeta function has a logarithmic pole at `s = 1`, with residue equal to the difference of the Mertens and Euler constants:: >>> primezeta(1) +inf >>> extradps(25)(lambda x: primezeta(1+x)+log(x))(+eps) -0.31571845205389007685 >>> mertens-euler -0.31571845205389007685 The analytic continuation to `0 < \mathrm{Re}(s) \le 1` is implemented. In this strip the function exhibits very complex behavior; on the unit interval, it has poles at `1/n` for every squarefree integer `n`:: >>> primezeta(0.5) # Pole at s = 1/2 (-inf + 3.1415926535897932385j) >>> primezeta(0.25) (-1.0416106801757269036 + 0.52359877559829887308j) >>> primezeta(0.5+10j) (0.54892423556409790529 + 0.45626803423487934264j) Although evaluation works in principle for any `\mathrm{Re}(s) > 0`, it should be noted that the evaluation time increases exponentially as `s` approaches the imaginary axis. For large `\mathrm{Re}(s)`, `P(s)` is asymptotic to `2^{-s}`:: >>> primezeta(inf) 0.0 >>> primezeta(10), mpf(2)**-10 (0.00099360357443698021786, 0.0009765625) >>> primezeta(1000) 9.3326361850321887899e-302 >>> primezeta(1000+1000j) (-3.8565440833654995949e-302 - 8.4985390447553234305e-302j) **References** Carl-Erik Froberg, "On the prime zeta function", BIT 8 (1968), pp. 187-202. """ bernpoly = r""" Evaluates the Bernoulli polynomial `B_n(z)`. The first few Bernoulli polynomials are:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for n in range(6): ... nprint(chop(taylor(lambda x: bernpoly(n,x), 0, n))) ... [1.0] [-0.5, 1.0] [0.166667, -1.0, 1.0] [0.0, 0.5, -1.5, 1.0] [-0.0333333, 0.0, 1.0, -2.0, 1.0] [0.0, -0.166667, 0.0, 1.66667, -2.5, 1.0] At `z = 0`, the Bernoulli polynomial evaluates to a Bernoulli number (see :func:`~mpmath.bernoulli`):: >>> bernpoly(12, 0), bernoulli(12) (-0.253113553113553, -0.253113553113553) >>> bernpoly(13, 0), bernoulli(13) (0.0, 0.0) Evaluation is accurate for large `n` and small `z`:: >>> mp.dps = 25 >>> bernpoly(100, 0.5) 2.838224957069370695926416e+78 >>> bernpoly(1000, 10.5) 5.318704469415522036482914e+1769 """ polylog = r""" Computes the polylogarithm, defined by the sum .. math :: \mathrm{Li}_s(z) = \sum_{k=1}^{\infty} \frac{z^k}{k^s}. This series is convergent only for `|z| < 1`, so elsewhere the analytic continuation is implied. The polylogarithm should not be confused with the logarithmic integral (also denoted by Li or li), which is implemented as :func:`~mpmath.li`. **Examples** The polylogarithm satisfies a huge number of functional identities. A sample of polylogarithm evaluations is shown below:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> polylog(1,0.5), log(2) (0.693147180559945, 0.693147180559945) >>> polylog(2,0.5), (pi**2-6*log(2)**2)/12 (0.582240526465012, 0.582240526465012) >>> polylog(2,-phi), -log(phi)**2-pi**2/10 (-1.21852526068613, -1.21852526068613) >>> polylog(3,0.5), 7*zeta(3)/8-pi**2*log(2)/12+log(2)**3/6 (0.53721319360804, 0.53721319360804) :func:`~mpmath.polylog` can evaluate the analytic continuation of the polylogarithm when `s` is an integer:: >>> polylog(2, 10) (0.536301287357863 - 7.23378441241546j) >>> polylog(2, -10) -4.1982778868581 >>> polylog(2, 10j) (-3.05968879432873 + 3.71678149306807j) >>> polylog(-2, 10) -0.150891632373114 >>> polylog(-2, -10) 0.067618332081142 >>> polylog(-2, 10j) (0.0384353698579347 + 0.0912451798066779j) Some more examples, with arguments on the unit circle (note that the series definition cannot be used for computation here):: >>> polylog(2,j) (-0.205616758356028 + 0.915965594177219j) >>> j*catalan-pi**2/48 (-0.205616758356028 + 0.915965594177219j) >>> polylog(3,exp(2*pi*j/3)) (-0.534247512515375 + 0.765587078525922j) >>> -4*zeta(3)/9 + 2*j*pi**3/81 (-0.534247512515375 + 0.765587078525921j) Polylogarithms of different order are related by integration and differentiation:: >>> s, z = 3, 0.5 >>> polylog(s+1, z) 0.517479061673899 >>> quad(lambda t: polylog(s,t)/t, [0, z]) 0.517479061673899 >>> z*diff(lambda t: polylog(s+2,t), z) 0.517479061673899 Taylor series expansions around `z = 0` are:: >>> for n in range(-3, 4): ... nprint(taylor(lambda x: polylog(n,x), 0, 5)) ... [0.0, 1.0, 8.0, 27.0, 64.0, 125.0] [0.0, 1.0, 4.0, 9.0, 16.0, 25.0] [0.0, 1.0, 2.0, 3.0, 4.0, 5.0] [0.0, 1.0, 1.0, 1.0, 1.0, 1.0] [0.0, 1.0, 0.5, 0.333333, 0.25, 0.2] [0.0, 1.0, 0.25, 0.111111, 0.0625, 0.04] [0.0, 1.0, 0.125, 0.037037, 0.015625, 0.008] The series defining the polylogarithm is simultaneously a Taylor series and an L-series. For certain values of `z`, the polylogarithm reduces to a pure zeta function:: >>> polylog(pi, 1), zeta(pi) (1.17624173838258, 1.17624173838258) >>> polylog(pi, -1), -altzeta(pi) (-0.909670702980385, -0.909670702980385) Evaluation for arbitrary, nonintegral `s` is supported for `z` within the unit circle: >>> polylog(3+4j, 0.25) (0.24258605789446 - 0.00222938275488344j) >>> nsum(lambda k: 0.25**k / k**(3+4j), [1,inf]) (0.24258605789446 - 0.00222938275488344j) It is also currently supported outside of the unit circle for `z` not too large in magnitude:: >>> polylog(1+j, 20+40j) (-7.1421172179728 - 3.92726697721369j) >>> polylog(1+j, 200+400j) Traceback (most recent call last): ... NotImplementedError: polylog for arbitrary s and z **References** 1. Richard Crandall, "Note on fast polylogarithm computation" http://people.reed.edu/~crandall/papers/Polylog.pdf 2. http://en.wikipedia.org/wiki/Polylogarithm 3. http://mathworld.wolfram.com/Polylogarithm.html """ bell = r""" For `n` a nonnegative integer, ``bell(n,x)`` evaluates the Bell polynomial `B_n(x)`, the first few of which are .. math :: B_0(x) = 1 B_1(x) = x B_2(x) = x^2+x B_3(x) = x^3+3x^2+x If `x = 1` or :func:`~mpmath.bell` is called with only one argument, it gives the `n`-th Bell number `B_n`, which is the number of partitions of a set with `n` elements. By setting the precision to at least `\log_{10} B_n` digits, :func:`~mpmath.bell` provides fast calculation of exact Bell numbers. In general, :func:`~mpmath.bell` computes .. math :: B_n(x) = e^{-x} \left(\mathrm{sinc}(\pi n) + E_n(x)\right) where `E_n(x)` is the generalized exponential function implemented by :func:`~mpmath.polyexp`. This is an extension of Dobinski's formula [1], where the modification is the sinc term ensuring that `B_n(x)` is continuous in `n`; :func:`~mpmath.bell` can thus be evaluated, differentiated, etc for arbitrary complex arguments. **Examples** Simple evaluations:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> bell(0, 2.5) 1.0 >>> bell(1, 2.5) 2.5 >>> bell(2, 2.5) 8.75 Evaluation for arbitrary complex arguments:: >>> bell(5.75+1j, 2-3j) (-10767.71345136587098445143 - 15449.55065599872579097221j) The first few Bell polynomials:: >>> for k in range(7): ... nprint(taylor(lambda x: bell(k,x), 0, k)) ... [1.0] [0.0, 1.0] [0.0, 1.0, 1.0] [0.0, 1.0, 3.0, 1.0] [0.0, 1.0, 7.0, 6.0, 1.0] [0.0, 1.0, 15.0, 25.0, 10.0, 1.0] [0.0, 1.0, 31.0, 90.0, 65.0, 15.0, 1.0] The first few Bell numbers and complementary Bell numbers:: >>> [int(bell(k)) for k in range(10)] [1, 1, 2, 5, 15, 52, 203, 877, 4140, 21147] >>> [int(bell(k,-1)) for k in range(10)] [1, -1, 0, 1, 1, -2, -9, -9, 50, 267] Large Bell numbers:: >>> mp.dps = 50 >>> bell(50) 185724268771078270438257767181908917499221852770.0 >>> bell(50,-1) -29113173035759403920216141265491160286912.0 Some even larger values:: >>> mp.dps = 25 >>> bell(1000,-1) -1.237132026969293954162816e+1869 >>> bell(1000) 2.989901335682408421480422e+1927 >>> bell(1000,2) 6.591553486811969380442171e+1987 >>> bell(1000,100.5) 9.101014101401543575679639e+2529 A determinant identity satisfied by Bell numbers:: >>> mp.dps = 15 >>> N = 8 >>> det([[bell(k+j) for j in range(N)] for k in range(N)]) 125411328000.0 >>> superfac(N-1) 125411328000.0 **References** 1. http://mathworld.wolfram.com/DobinskisFormula.html """ polyexp = r""" Evaluates the polyexponential function, defined for arbitrary complex `s`, `z` by the series .. math :: E_s(z) = \sum_{k=1}^{\infty} \frac{k^s}{k!} z^k. `E_s(z)` is constructed from the exponential function analogously to how the polylogarithm is constructed from the ordinary logarithm; as a function of `s` (with `z` fixed), `E_s` is an L-series It is an entire function of both `s` and `z`. The polyexponential function provides a generalization of the Bell polynomials `B_n(x)` (see :func:`~mpmath.bell`) to noninteger orders `n`. In terms of the Bell polynomials, .. math :: E_s(z) = e^z B_s(z) - \mathrm{sinc}(\pi s). Note that `B_n(x)` and `e^{-x} E_n(x)` are identical if `n` is a nonzero integer, but not otherwise. In particular, they differ at `n = 0`. **Examples** Evaluating a series:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> nsum(lambda k: sqrt(k)/fac(k), [1,inf]) 2.101755547733791780315904 >>> polyexp(0.5,1) 2.101755547733791780315904 Evaluation for arbitrary arguments:: >>> polyexp(-3-4j, 2.5+2j) (2.351660261190434618268706 + 1.202966666673054671364215j) Evaluation is accurate for tiny function values:: >>> polyexp(4, -100) 3.499471750566824369520223e-36 If `n` is a nonpositive integer, `E_n` reduces to a special instance of the hypergeometric function `\,_pF_q`:: >>> n = 3 >>> x = pi >>> polyexp(-n,x) 4.042192318847986561771779 >>> x*hyper([1]*(n+1), [2]*(n+1), x) 4.042192318847986561771779 """ cyclotomic = r""" Evaluates the cyclotomic polynomial `\Phi_n(x)`, defined by .. math :: \Phi_n(x) = \prod_{\zeta} (x - \zeta) where `\zeta` ranges over all primitive `n`-th roots of unity (see :func:`~mpmath.unitroots`). An equivalent representation, used for computation, is .. math :: \Phi_n(x) = \prod_{d\mid n}(x^d-1)^{\mu(n/d)} = \Phi_n(x) where `\mu(m)` denotes the Moebius function. The cyclotomic polynomials are integer polynomials, the first of which can be written explicitly as .. math :: \Phi_0(x) = 1 \Phi_1(x) = x - 1 \Phi_2(x) = x + 1 \Phi_3(x) = x^3 + x^2 + 1 \Phi_4(x) = x^2 + 1 \Phi_5(x) = x^4 + x^3 + x^2 + x + 1 \Phi_6(x) = x^2 - x + 1 **Examples** The coefficients of low-order cyclotomic polynomials can be recovered using Taylor expansion:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for n in range(9): ... p = chop(taylor(lambda x: cyclotomic(n,x), 0, 10)) ... print("%s %s" % (n, nstr(p[:10+1-p[::-1].index(1)]))) ... 0 [1.0] 1 [-1.0, 1.0] 2 [1.0, 1.0] 3 [1.0, 1.0, 1.0] 4 [1.0, 0.0, 1.0] 5 [1.0, 1.0, 1.0, 1.0, 1.0] 6 [1.0, -1.0, 1.0] 7 [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] 8 [1.0, 0.0, 0.0, 0.0, 1.0] The definition as a product over primitive roots may be checked by computing the product explicitly (for a real argument, this method will generally introduce numerical noise in the imaginary part):: >>> mp.dps = 25 >>> z = 3+4j >>> cyclotomic(10, z) (-419.0 - 360.0j) >>> fprod(z-r for r in unitroots(10, primitive=True)) (-419.0 - 360.0j) >>> z = 3 >>> cyclotomic(10, z) 61.0 >>> fprod(z-r for r in unitroots(10, primitive=True)) (61.0 - 3.146045605088568607055454e-25j) Up to permutation, the roots of a given cyclotomic polynomial can be checked to agree with the list of primitive roots:: >>> p = taylor(lambda x: cyclotomic(6,x), 0, 6)[:3] >>> for r in polyroots(p[::-1]): ... print(r) ... (0.5 - 0.8660254037844386467637232j) (0.5 + 0.8660254037844386467637232j) >>> >>> for r in unitroots(6, primitive=True): ... print(r) ... (0.5 + 0.8660254037844386467637232j) (0.5 - 0.8660254037844386467637232j) """ meijerg = r""" Evaluates the Meijer G-function, defined as .. math :: G^{m,n}_{p,q} \left( \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}\; \right| \; z ; r \right) = \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=n+1}^{p}\Gamma(a_j+s) \prod_{j=m+1}^q \Gamma(1-b_j-s)} z^{-s/r} ds for an appropriate choice of the contour `L` (see references). There are `p` elements `a_j`. The argument *a_s* should be a pair of lists, the first containing the `n` elements `a_1, \ldots, a_n` and the second containing the `p-n` elements `a_{n+1}, \ldots a_p`. There are `q` elements `b_j`. The argument *b_s* should be a pair of lists, the first containing the `m` elements `b_1, \ldots, b_m` and the second containing the `q-m` elements `b_{m+1}, \ldots b_q`. The implicit tuple `(m, n, p, q)` constitutes the order or degree of the Meijer G-function, and is determined by the lengths of the coefficient vectors. Confusingly, the indices in this tuple appear in a different order from the coefficients, but this notation is standard. The many examples given below should hopefully clear up any potential confusion. **Algorithm** The Meijer G-function is evaluated as a combination of hypergeometric series. There are two versions of the function, which can be selected with the optional *series* argument. *series=1* uses a sum of `m` `\,_pF_{q-1}` functions of `z` *series=2* uses a sum of `n` `\,_qF_{p-1}` functions of `1/z` The default series is chosen based on the degree and `|z|` in order to be consistent with Mathematica's. This definition of the Meijer G-function has a discontinuity at `|z| = 1` for some orders, which can be avoided by explicitly specifying a series. Keyword arguments are forwarded to :func:`~mpmath.hypercomb`. **Examples** Many standard functions are special cases of the Meijer G-function (possibly rescaled and/or with branch cut corrections). We define some test parameters:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> a = mpf(0.75) >>> b = mpf(1.5) >>> z = mpf(2.25) The exponential function: `e^z = G^{1,0}_{0,1} \left( \left. \begin{matrix} - \\ 0 \end{matrix} \; \right| \; -z \right)` >>> meijerg([[],[]], [[0],[]], -z) 9.487735836358525720550369 >>> exp(z) 9.487735836358525720550369 The natural logarithm: `\log(1+z) = G^{1,2}_{2,2} \left( \left. \begin{matrix} 1, 1 \\ 1, 0 \end{matrix} \; \right| \; -z \right)` >>> meijerg([[1,1],[]], [[1],[0]], z) 1.178654996341646117219023 >>> log(1+z) 1.178654996341646117219023 A rational function: `\frac{z}{z+1} = G^{1,2}_{2,2} \left( \left. \begin{matrix} 1, 1 \\ 1, 1 \end{matrix} \; \right| \; z \right)` >>> meijerg([[1,1],[]], [[1],[1]], z) 0.6923076923076923076923077 >>> z/(z+1) 0.6923076923076923076923077 The sine and cosine functions: `\frac{1}{\sqrt \pi} \sin(2 \sqrt z) = G^{1,0}_{0,2} \left( \left. \begin{matrix} - \\ \frac{1}{2}, 0 \end{matrix} \; \right| \; z \right)` `\frac{1}{\sqrt \pi} \cos(2 \sqrt z) = G^{1,0}_{0,2} \left( \left. \begin{matrix} - \\ 0, \frac{1}{2} \end{matrix} \; \right| \; z \right)` >>> meijerg([[],[]], [[0.5],[0]], (z/2)**2) 0.4389807929218676682296453 >>> sin(z)/sqrt(pi) 0.4389807929218676682296453 >>> meijerg([[],[]], [[0],[0.5]], (z/2)**2) -0.3544090145996275423331762 >>> cos(z)/sqrt(pi) -0.3544090145996275423331762 Bessel functions: `J_a(2 \sqrt z) = G^{1,0}_{0,2} \left( \left. \begin{matrix} - \\ \frac{a}{2}, -\frac{a}{2} \end{matrix} \; \right| \; z \right)` `Y_a(2 \sqrt z) = G^{2,0}_{1,3} \left( \left. \begin{matrix} \frac{-a-1}{2} \\ \frac{a}{2}, -\frac{a}{2}, \frac{-a-1}{2} \end{matrix} \; \right| \; z \right)` `(-z)^{a/2} z^{-a/2} I_a(2 \sqrt z) = G^{1,0}_{0,2} \left( \left. \begin{matrix} - \\ \frac{a}{2}, -\frac{a}{2} \end{matrix} \; \right| \; -z \right)` `2 K_a(2 \sqrt z) = G^{2,0}_{0,2} \left( \left. \begin{matrix} - \\ \frac{a}{2}, -\frac{a}{2} \end{matrix} \; \right| \; z \right)` As the example with the Bessel *I* function shows, a branch factor is required for some arguments when inverting the square root. >>> meijerg([[],[]], [[a/2],[-a/2]], (z/2)**2) 0.5059425789597154858527264 >>> besselj(a,z) 0.5059425789597154858527264 >>> meijerg([[],[(-a-1)/2]], [[a/2,-a/2],[(-a-1)/2]], (z/2)**2) 0.1853868950066556941442559 >>> bessely(a, z) 0.1853868950066556941442559 >>> meijerg([[],[]], [[a/2],[-a/2]], -(z/2)**2) (0.8685913322427653875717476 + 2.096964974460199200551738j) >>> (-z)**(a/2) / z**(a/2) * besseli(a, z) (0.8685913322427653875717476 + 2.096964974460199200551738j) >>> 0.5*meijerg([[],[]], [[a/2,-a/2],[]], (z/2)**2) 0.09334163695597828403796071 >>> besselk(a,z) 0.09334163695597828403796071 Error functions: `\sqrt{\pi} z^{2(a-1)} \mathrm{erfc}(z) = G^{2,0}_{1,2} \left( \left. \begin{matrix} a \\ a-1, a-\frac{1}{2} \end{matrix} \; \right| \; z, \frac{1}{2} \right)` >>> meijerg([[],[a]], [[a-1,a-0.5],[]], z, 0.5) 0.00172839843123091957468712 >>> sqrt(pi) * z**(2*a-2) * erfc(z) 0.00172839843123091957468712 A Meijer G-function of higher degree, (1,1,2,3): >>> meijerg([[a],[b]], [[a],[b,a-1]], z) 1.55984467443050210115617 >>> sin((b-a)*pi)/pi*(exp(z)-1)*z**(a-1) 1.55984467443050210115617 A Meijer G-function of still higher degree, (4,1,2,4), that can be expanded as a messy combination of exponential integrals: >>> meijerg([[a],[2*b-a]], [[b,a,b-0.5,-1-a+2*b],[]], z) 0.3323667133658557271898061 >>> chop(4**(a-b+1)*sqrt(pi)*gamma(2*b-2*a)*z**a*\ ... expint(2*b-2*a, -2*sqrt(-z))*expint(2*b-2*a, 2*sqrt(-z))) 0.3323667133658557271898061 In the following case, different series give different values:: >>> chop(meijerg([[1],[0.25]],[[3],[0.5]],-2)) -0.06417628097442437076207337 >>> meijerg([[1],[0.25]],[[3],[0.5]],-2,series=1) 0.1428699426155117511873047 >>> chop(meijerg([[1],[0.25]],[[3],[0.5]],-2,series=2)) -0.06417628097442437076207337 **References** 1. http://en.wikipedia.org/wiki/Meijer_G-function 2. http://mathworld.wolfram.com/MeijerG-Function.html 3. http://functions.wolfram.com/HypergeometricFunctions/MeijerG/ 4. http://functions.wolfram.com/HypergeometricFunctions/MeijerG1/ """ clsin = r""" Computes the Clausen sine function, defined formally by the series .. math :: \mathrm{Cl}_s(z) = \sum_{k=1}^{\infty} \frac{\sin(kz)}{k^s}. The special case `\mathrm{Cl}_2(z)` (i.e. ``clsin(2,z)``) is the classical "Clausen function". More generally, the Clausen function is defined for complex `s` and `z`, even when the series does not converge. The Clausen function is related to the polylogarithm (:func:`~mpmath.polylog`) as .. math :: \mathrm{Cl}_s(z) = \frac{1}{2i}\left(\mathrm{Li}_s\left(e^{iz}\right) - \mathrm{Li}_s\left(e^{-iz}\right)\right) = \mathrm{Im}\left[\mathrm{Li}_s(e^{iz})\right] \quad (s, z \in \mathbb{R}), and this representation can be taken to provide the analytic continuation of the series. The complementary function :func:`~mpmath.clcos` gives the corresponding cosine sum. **Examples** Evaluation for arbitrarily chosen `s` and `z`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> s, z = 3, 4 >>> clsin(s, z); nsum(lambda k: sin(z*k)/k**s, [1,inf]) -0.6533010136329338746275795 -0.6533010136329338746275795 Using `z + \pi` instead of `z` gives an alternating series:: >>> clsin(s, z+pi) 0.8860032351260589402871624 >>> nsum(lambda k: (-1)**k*sin(z*k)/k**s, [1,inf]) 0.8860032351260589402871624 With `s = 1`, the sum can be expressed in closed form using elementary functions:: >>> z = 1 + sqrt(3) >>> clsin(1, z) 0.2047709230104579724675985 >>> chop((log(1-exp(-j*z)) - log(1-exp(j*z)))/(2*j)) 0.2047709230104579724675985 >>> nsum(lambda k: sin(k*z)/k, [1,inf]) 0.2047709230104579724675985 The classical Clausen function `\mathrm{Cl}_2(\theta)` gives the value of the integral `\int_0^{\theta} -\ln(2\sin(x/2)) dx` for `0 < \theta < 2 \pi`:: >>> cl2 = lambda t: clsin(2, t) >>> cl2(3.5) -0.2465045302347694216534255 >>> -quad(lambda x: ln(2*sin(0.5*x)), [0, 3.5]) -0.2465045302347694216534255 This function is symmetric about `\theta = \pi` with zeros and extreme points:: >>> cl2(0); cl2(pi/3); chop(cl2(pi)); cl2(5*pi/3); chop(cl2(2*pi)) 0.0 1.014941606409653625021203 0.0 -1.014941606409653625021203 0.0 Catalan's constant is a special value:: >>> cl2(pi/2) 0.9159655941772190150546035 >>> +catalan 0.9159655941772190150546035 The Clausen sine function can be expressed in closed form when `s` is an odd integer (becoming zero when `s` < 0):: >>> z = 1 + sqrt(2) >>> clsin(1, z); (pi-z)/2 0.3636895456083490948304773 0.3636895456083490948304773 >>> clsin(3, z); pi**2/6*z - pi*z**2/4 + z**3/12 0.5661751584451144991707161 0.5661751584451144991707161 >>> clsin(-1, z) 0.0 >>> clsin(-3, z) 0.0 It can also be expressed in closed form for even integer `s \le 0`, providing a finite sum for series such as `\sin(z) + \sin(2z) + \sin(3z) + \ldots`:: >>> z = 1 + sqrt(2) >>> clsin(0, z) 0.1903105029507513881275865 >>> cot(z/2)/2 0.1903105029507513881275865 >>> clsin(-2, z) -0.1089406163841548817581392 >>> -cot(z/2)*csc(z/2)**2/4 -0.1089406163841548817581392 Call with ``pi=True`` to multiply `z` by `\pi` exactly:: >>> clsin(3, 3*pi) -8.892316224968072424732898e-26 >>> clsin(3, 3, pi=True) 0.0 Evaluation for complex `s`, `z` in a nonconvergent case:: >>> s, z = -1-j, 1+2j >>> clsin(s, z) (-0.593079480117379002516034 + 0.9038644233367868273362446j) >>> extraprec(20)(nsum)(lambda k: sin(k*z)/k**s, [1,inf]) (-0.593079480117379002516034 + 0.9038644233367868273362446j) """ clcos = r""" Computes the Clausen cosine function, defined formally by the series .. math :: \mathrm{\widetilde{Cl}}_s(z) = \sum_{k=1}^{\infty} \frac{\cos(kz)}{k^s}. This function is complementary to the Clausen sine function :func:`~mpmath.clsin`. In terms of the polylogarithm, .. math :: \mathrm{\widetilde{Cl}}_s(z) = \frac{1}{2}\left(\mathrm{Li}_s\left(e^{iz}\right) + \mathrm{Li}_s\left(e^{-iz}\right)\right) = \mathrm{Re}\left[\mathrm{Li}_s(e^{iz})\right] \quad (s, z \in \mathbb{R}). **Examples** Evaluation for arbitrarily chosen `s` and `z`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> s, z = 3, 4 >>> clcos(s, z); nsum(lambda k: cos(z*k)/k**s, [1,inf]) -0.6518926267198991308332759 -0.6518926267198991308332759 Using `z + \pi` instead of `z` gives an alternating series:: >>> s, z = 3, 0.5 >>> clcos(s, z+pi) -0.8155530586502260817855618 >>> nsum(lambda k: (-1)**k*cos(z*k)/k**s, [1,inf]) -0.8155530586502260817855618 With `s = 1`, the sum can be expressed in closed form using elementary functions:: >>> z = 1 + sqrt(3) >>> clcos(1, z) -0.6720334373369714849797918 >>> chop(-0.5*(log(1-exp(j*z))+log(1-exp(-j*z)))) -0.6720334373369714849797918 >>> -log(abs(2*sin(0.5*z))) # Equivalent to above when z is real -0.6720334373369714849797918 >>> nsum(lambda k: cos(k*z)/k, [1,inf]) -0.6720334373369714849797918 It can also be expressed in closed form when `s` is an even integer. For example, >>> clcos(2,z) -0.7805359025135583118863007 >>> pi**2/6 - pi*z/2 + z**2/4 -0.7805359025135583118863007 The case `s = 0` gives the renormalized sum of `\cos(z) + \cos(2z) + \cos(3z) + \ldots` (which happens to be the same for any value of `z`):: >>> clcos(0, z) -0.5 >>> nsum(lambda k: cos(k*z), [1,inf]) -0.5 Also the sums .. math :: \cos(z) + 2\cos(2z) + 3\cos(3z) + \ldots and .. math :: \cos(z) + 2^n \cos(2z) + 3^n \cos(3z) + \ldots for higher integer powers `n = -s` can be done in closed form. They are zero when `n` is positive and even (`s` negative and even):: >>> clcos(-1, z); 1/(2*cos(z)-2) -0.2607829375240542480694126 -0.2607829375240542480694126 >>> clcos(-3, z); (2+cos(z))*csc(z/2)**4/8 0.1472635054979944390848006 0.1472635054979944390848006 >>> clcos(-2, z); clcos(-4, z); clcos(-6, z) 0.0 0.0 0.0 With `z = \pi`, the series reduces to that of the Riemann zeta function (more generally, if `z = p \pi/q`, it is a finite sum over Hurwitz zeta function values):: >>> clcos(2.5, 0); zeta(2.5) 1.34148725725091717975677 1.34148725725091717975677 >>> clcos(2.5, pi); -altzeta(2.5) -0.8671998890121841381913472 -0.8671998890121841381913472 Call with ``pi=True`` to multiply `z` by `\pi` exactly:: >>> clcos(-3, 2*pi) 2.997921055881167659267063e+102 >>> clcos(-3, 2, pi=True) 0.008333333333333333333333333 Evaluation for complex `s`, `z` in a nonconvergent case:: >>> s, z = -1-j, 1+2j >>> clcos(s, z) (0.9407430121562251476136807 + 0.715826296033590204557054j) >>> extraprec(20)(nsum)(lambda k: cos(k*z)/k**s, [1,inf]) (0.9407430121562251476136807 + 0.715826296033590204557054j) """ whitm = r""" Evaluates the Whittaker function `M(k,m,z)`, which gives a solution to the Whittaker differential equation .. math :: \frac{d^2f}{dz^2} + \left(-\frac{1}{4}+\frac{k}{z}+ \frac{(\frac{1}{4}-m^2)}{z^2}\right) f = 0. A second solution is given by :func:`~mpmath.whitw`. The Whittaker functions are defined in Abramowitz & Stegun, section 13.1. They are alternate forms of the confluent hypergeometric functions `\,_1F_1` and `U`: .. math :: M(k,m,z) = e^{-\frac{1}{2}z} z^{\frac{1}{2}+m} \,_1F_1(\tfrac{1}{2}+m-k, 1+2m, z) W(k,m,z) = e^{-\frac{1}{2}z} z^{\frac{1}{2}+m} U(\tfrac{1}{2}+m-k, 1+2m, z). **Examples** Evaluation for arbitrary real and complex arguments is supported:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> whitm(1, 1, 1) 0.7302596799460411820509668 >>> whitm(1, 1, -1) (0.0 - 1.417977827655098025684246j) >>> whitm(j, j/2, 2+3j) (3.245477713363581112736478 - 0.822879187542699127327782j) >>> whitm(2, 3, 100000) 4.303985255686378497193063e+21707 Evaluation at zero:: >>> whitm(1,-1,0); whitm(1,-0.5,0); whitm(1,0,0) +inf nan 0.0 We can verify that :func:`~mpmath.whitm` numerically satisfies the differential equation for arbitrarily chosen values:: >>> k = mpf(0.25) >>> m = mpf(1.5) >>> f = lambda z: whitm(k,m,z) >>> for z in [-1, 2.5, 3, 1+2j]: ... chop(diff(f,z,2) + (-0.25 + k/z + (0.25-m**2)/z**2)*f(z)) ... 0.0 0.0 0.0 0.0 An integral involving both :func:`~mpmath.whitm` and :func:`~mpmath.whitw`, verifying evaluation along the real axis:: >>> quad(lambda x: exp(-x)*whitm(3,2,x)*whitw(1,-2,x), [0,inf]) 3.438869842576800225207341 >>> 128/(21*sqrt(pi)) 3.438869842576800225207341 """ whitw = r""" Evaluates the Whittaker function `W(k,m,z)`, which gives a second solution to the Whittaker differential equation. (See :func:`~mpmath.whitm`.) **Examples** Evaluation for arbitrary real and complex arguments is supported:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> whitw(1, 1, 1) 1.19532063107581155661012 >>> whitw(1, 1, -1) (-0.9424875979222187313924639 - 0.2607738054097702293308689j) >>> whitw(j, j/2, 2+3j) (0.1782899315111033879430369 - 0.01609578360403649340169406j) >>> whitw(2, 3, 100000) 1.887705114889527446891274e-21705 >>> whitw(-1, -1, 100) 1.905250692824046162462058e-24 Evaluation at zero:: >>> for m in [-1, -0.5, 0, 0.5, 1]: ... whitw(1, m, 0) ... +inf nan 0.0 nan +inf We can verify that :func:`~mpmath.whitw` numerically satisfies the differential equation for arbitrarily chosen values:: >>> k = mpf(0.25) >>> m = mpf(1.5) >>> f = lambda z: whitw(k,m,z) >>> for z in [-1, 2.5, 3, 1+2j]: ... chop(diff(f,z,2) + (-0.25 + k/z + (0.25-m**2)/z**2)*f(z)) ... 0.0 0.0 0.0 0.0 """ ber = r""" Computes the Kelvin function ber, which for real arguments gives the real part of the Bessel J function of a rotated argument .. math :: J_n\left(x e^{3\pi i/4}\right) = \mathrm{ber}_n(x) + i \mathrm{bei}_n(x). The imaginary part is given by :func:`~mpmath.bei`. **Plots** .. literalinclude :: /modules/mpmath/plots/ber.py .. image :: /modules/mpmath/plots/ber.png **Examples** Verifying the defining relation:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> n, x = 2, 3.5 >>> ber(n,x) 1.442338852571888752631129 >>> bei(n,x) -0.948359035324558320217678 >>> besselj(n, x*root(1,8,3)) (1.442338852571888752631129 - 0.948359035324558320217678j) The ber and bei functions are also defined by analytic continuation for complex arguments:: >>> ber(1+j, 2+3j) (4.675445984756614424069563 - 15.84901771719130765656316j) >>> bei(1+j, 2+3j) (15.83886679193707699364398 + 4.684053288183046528703611j) """ bei = r""" Computes the Kelvin function bei, which for real arguments gives the imaginary part of the Bessel J function of a rotated argument. See :func:`~mpmath.ber`. """ ker = r""" Computes the Kelvin function ker, which for real arguments gives the real part of the (rescaled) Bessel K function of a rotated argument .. math :: e^{-\pi i/2} K_n\left(x e^{3\pi i/4}\right) = \mathrm{ker}_n(x) + i \mathrm{kei}_n(x). The imaginary part is given by :func:`~mpmath.kei`. **Plots** .. literalinclude :: /modules/mpmath/plots/ker.py .. image :: /modules/mpmath/plots/ker.png **Examples** Verifying the defining relation:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> n, x = 2, 4.5 >>> ker(n,x) 0.02542895201906369640249801 >>> kei(n,x) -0.02074960467222823237055351 >>> exp(-n*pi*j/2) * besselk(n, x*root(1,8,1)) (0.02542895201906369640249801 - 0.02074960467222823237055351j) The ker and kei functions are also defined by analytic continuation for complex arguments:: >>> ker(1+j, 3+4j) (1.586084268115490421090533 - 2.939717517906339193598719j) >>> kei(1+j, 3+4j) (-2.940403256319453402690132 - 1.585621643835618941044855j) """ kei = r""" Computes the Kelvin function kei, which for real arguments gives the imaginary part of the (rescaled) Bessel K function of a rotated argument. See :func:`~mpmath.ker`. """ struveh = r""" Gives the Struve function .. math :: \,\mathbf{H}_n(z) = \sum_{k=0}^\infty \frac{(-1)^k}{\Gamma(k+\frac{3}{2}) \Gamma(k+n+\frac{3}{2})} {\left({\frac{z}{2}}\right)}^{2k+n+1} which is a solution to the Struve differential equation .. math :: z^2 f''(z) + z f'(z) + (z^2-n^2) f(z) = \frac{2 z^{n+1}}{\pi (2n-1)!!}. **Examples** Evaluation for arbitrary real and complex arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> struveh(0, 3.5) 0.3608207733778295024977797 >>> struveh(-1, 10) -0.255212719726956768034732 >>> struveh(1, -100.5) 0.5819566816797362287502246 >>> struveh(2.5, 10000000000000) 3153915652525200060.308937 >>> struveh(2.5, -10000000000000) (0.0 - 3153915652525200060.308937j) >>> struveh(1+j, 1000000+4000000j) (-3.066421087689197632388731e+1737173 - 1.596619701076529803290973e+1737173j) A Struve function of half-integer order is elementary; for example: >>> z = 3 >>> struveh(0.5, 3) 0.9167076867564138178671595 >>> sqrt(2/(pi*z))*(1-cos(z)) 0.9167076867564138178671595 Numerically verifying the differential equation:: >>> z = mpf(4.5) >>> n = 3 >>> f = lambda z: struveh(n,z) >>> lhs = z**2*diff(f,z,2) + z*diff(f,z) + (z**2-n**2)*f(z) >>> rhs = 2*z**(n+1)/fac2(2*n-1)/pi >>> lhs 17.40359302709875496632744 >>> rhs 17.40359302709875496632744 """ struvel = r""" Gives the modified Struve function .. math :: \,\mathbf{L}_n(z) = -i e^{-n\pi i/2} \mathbf{H}_n(i z) which solves to the modified Struve differential equation .. math :: z^2 f''(z) + z f'(z) - (z^2+n^2) f(z) = \frac{2 z^{n+1}}{\pi (2n-1)!!}. **Examples** Evaluation for arbitrary real and complex arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> struvel(0, 3.5) 7.180846515103737996249972 >>> struvel(-1, 10) 2670.994904980850550721511 >>> struvel(1, -100.5) 1.757089288053346261497686e+42 >>> struvel(2.5, 10000000000000) 4.160893281017115450519948e+4342944819025 >>> struvel(2.5, -10000000000000) (0.0 - 4.160893281017115450519948e+4342944819025j) >>> struvel(1+j, 700j) (-0.1721150049480079451246076 + 0.1240770953126831093464055j) >>> struvel(1+j, 1000000+4000000j) (-2.973341637511505389128708e+434290 - 5.164633059729968297147448e+434290j) Numerically verifying the differential equation:: >>> z = mpf(3.5) >>> n = 3 >>> f = lambda z: struvel(n,z) >>> lhs = z**2*diff(f,z,2) + z*diff(f,z) - (z**2+n**2)*f(z) >>> rhs = 2*z**(n+1)/fac2(2*n-1)/pi >>> lhs 6.368850306060678353018165 >>> rhs 6.368850306060678353018165 """ appellf1 = r""" Gives the Appell F1 hypergeometric function of two variables, .. math :: F_1(a,b_1,b_2,c,x,y) = \sum_{m=0}^{\infty} \sum_{n=0}^{\infty} \frac{(a)_{m+n} (b_1)_m (b_2)_n}{(c)_{m+n}} \frac{x^m y^n}{m! n!}. This series is only generally convergent when `|x| < 1` and `|y| < 1`, although :func:`~mpmath.appellf1` can evaluate an analytic continuation with respecto to either variable, and sometimes both. **Examples** Evaluation is supported for real and complex parameters:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> appellf1(1,0,0.5,1,0.5,0.25) 1.154700538379251529018298 >>> appellf1(1,1+j,0.5,1,0.5,0.5j) (1.138403860350148085179415 + 1.510544741058517621110615j) For some integer parameters, the F1 series reduces to a polynomial:: >>> appellf1(2,-4,-3,1,2,5) -816.0 >>> appellf1(-5,1,2,1,4,5) -20528.0 The analytic continuation with respect to either `x` or `y`, and sometimes with respect to both, can be evaluated:: >>> appellf1(2,3,4,5,100,0.5) (0.0006231042714165329279738662 + 0.0000005769149277148425774499857j) >>> appellf1('1.1', '0.3', '0.2+2j', '0.4', '0.2', 1.5+3j) (-0.1782604566893954897128702 + 0.002472407104546216117161499j) >>> appellf1(1,2,3,4,10,12) -0.07122993830066776374929313 For certain arguments, F1 reduces to an ordinary hypergeometric function:: >>> appellf1(1,2,3,5,0.5,0.25) 1.547902270302684019335555 >>> 4*hyp2f1(1,2,5,'1/3')/3 1.547902270302684019335555 >>> appellf1(1,2,3,4,0,1.5) (-1.717202506168937502740238 - 2.792526803190927323077905j) >>> hyp2f1(1,3,4,1.5) (-1.717202506168937502740238 - 2.792526803190927323077905j) The F1 function satisfies a system of partial differential equations:: >>> a,b1,b2,c,x,y = map(mpf, [1,0.5,0.25,1.125,0.25,-0.25]) >>> F = lambda x,y: appellf1(a,b1,b2,c,x,y) >>> chop(x*(1-x)*diff(F,(x,y),(2,0)) + ... y*(1-x)*diff(F,(x,y),(1,1)) + ... (c-(a+b1+1)*x)*diff(F,(x,y),(1,0)) - ... b1*y*diff(F,(x,y),(0,1)) - ... a*b1*F(x,y)) 0.0 >>> >>> chop(y*(1-y)*diff(F,(x,y),(0,2)) + ... x*(1-y)*diff(F,(x,y),(1,1)) + ... (c-(a+b2+1)*y)*diff(F,(x,y),(0,1)) - ... b2*x*diff(F,(x,y),(1,0)) - ... a*b2*F(x,y)) 0.0 The Appell F1 function allows for closed-form evaluation of various integrals, such as any integral of the form `\int x^r (x+a)^p (x+b)^q dx`:: >>> def integral(a,b,p,q,r,x1,x2): ... a,b,p,q,r,x1,x2 = map(mpmathify, [a,b,p,q,r,x1,x2]) ... f = lambda x: x**r * (x+a)**p * (x+b)**q ... def F(x): ... v = x**(r+1)/(r+1) * (a+x)**p * (b+x)**q ... v *= (1+x/a)**(-p) ... v *= (1+x/b)**(-q) ... v *= appellf1(r+1,-p,-q,2+r,-x/a,-x/b) ... return v ... print("Num. quad: %s" % quad(f, [x1,x2])) ... print("Appell F1: %s" % (F(x2)-F(x1))) ... >>> integral('1/5','4/3','-2','3','1/2',0,1) Num. quad: 9.073335358785776206576981 Appell F1: 9.073335358785776206576981 >>> integral('3/2','4/3','-2','3','1/2',0,1) Num. quad: 1.092829171999626454344678 Appell F1: 1.092829171999626454344678 >>> integral('3/2','4/3','-2','3','1/2',12,25) Num. quad: 1106.323225040235116498927 Appell F1: 1106.323225040235116498927 Also incomplete elliptic integrals fall into this category [1]:: >>> def E(z, m): ... if (pi/2).ae(z): ... return ellipe(m) ... return 2*round(re(z)/pi)*ellipe(m) + mpf(-1)**round(re(z)/pi)*\ ... sin(z)*appellf1(0.5,0.5,-0.5,1.5,sin(z)**2,m*sin(z)**2) ... >>> z, m = 1, 0.5 >>> E(z,m); quad(lambda t: sqrt(1-m*sin(t)**2), [0,pi/4,3*pi/4,z]) 0.9273298836244400669659042 0.9273298836244400669659042 >>> z, m = 3, 2 >>> E(z,m); quad(lambda t: sqrt(1-m*sin(t)**2), [0,pi/4,3*pi/4,z]) (1.057495752337234229715836 + 1.198140234735592207439922j) (1.057495752337234229715836 + 1.198140234735592207439922j) **References** 1. [WolframFunctions]_ http://functions.wolfram.com/EllipticIntegrals/EllipticE2/26/01/ 2. [SrivastavaKarlsson]_ 3. [CabralRosetti]_ 4. [Vidunas]_ 5. [Slater]_ """ angerj = r""" Gives the Anger function .. math :: \mathbf{J}_{\nu}(z) = \frac{1}{\pi} \int_0^{\pi} \cos(\nu t - z \sin t) dt which is an entire function of both the parameter `\nu` and the argument `z`. It solves the inhomogeneous Bessel differential equation .. math :: f''(z) + \frac{1}{z}f'(z) + \left(1-\frac{\nu^2}{z^2}\right) f(z) = \frac{(z-\nu)}{\pi z^2} \sin(\pi \nu). **Examples** Evaluation for real and complex parameter and argument:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> angerj(2,3) 0.4860912605858910769078311 >>> angerj(-3+4j, 2+5j) (-5033.358320403384472395612 + 585.8011892476145118551756j) >>> angerj(3.25, 1e6j) (4.630743639715893346570743e+434290 - 1.117960409887505906848456e+434291j) >>> angerj(-1.5, 1e6) 0.0002795719747073879393087011 The Anger function coincides with the Bessel J-function when `\nu` is an integer:: >>> angerj(1,3); besselj(1,3) 0.3390589585259364589255146 0.3390589585259364589255146 >>> angerj(1.5,3); besselj(1.5,3) 0.4088969848691080859328847 0.4777182150870917715515015 Verifying the differential equation:: >>> v,z = mpf(2.25), 0.75 >>> f = lambda z: angerj(v,z) >>> diff(f,z,2) + diff(f,z)/z + (1-(v/z)**2)*f(z) -0.6002108774380707130367995 >>> (z-v)/(pi*z**2) * sinpi(v) -0.6002108774380707130367995 Verifying the integral representation:: >>> angerj(v,z) 0.1145380759919333180900501 >>> quad(lambda t: cos(v*t-z*sin(t))/pi, [0,pi]) 0.1145380759919333180900501 **References** 1. [DLMF]_ section 11.10: Anger-Weber Functions """ webere = r""" Gives the Weber function .. math :: \mathbf{E}_{\nu}(z) = \frac{1}{\pi} \int_0^{\pi} \sin(\nu t - z \sin t) dt which is an entire function of both the parameter `\nu` and the argument `z`. It solves the inhomogeneous Bessel differential equation .. math :: f''(z) + \frac{1}{z}f'(z) + \left(1-\frac{\nu^2}{z^2}\right) f(z) = -\frac{1}{\pi z^2} (z+\nu+(z-\nu)\cos(\pi \nu)). **Examples** Evaluation for real and complex parameter and argument:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> webere(2,3) -0.1057668973099018425662646 >>> webere(-3+4j, 2+5j) (-585.8081418209852019290498 - 5033.314488899926921597203j) >>> webere(3.25, 1e6j) (-1.117960409887505906848456e+434291 - 4.630743639715893346570743e+434290j) >>> webere(3.25, 1e6) -0.00002812518265894315604914453 Up to addition of a rational function of `z`, the Weber function coincides with the Struve H-function when `\nu` is an integer:: >>> webere(1,3); 2/pi-struveh(1,3) -0.3834897968188690177372881 -0.3834897968188690177372881 >>> webere(5,3); 26/(35*pi)-struveh(5,3) 0.2009680659308154011878075 0.2009680659308154011878075 Verifying the differential equation:: >>> v,z = mpf(2.25), 0.75 >>> f = lambda z: webere(v,z) >>> diff(f,z,2) + diff(f,z)/z + (1-(v/z)**2)*f(z) -1.097441848875479535164627 >>> -(z+v+(z-v)*cospi(v))/(pi*z**2) -1.097441848875479535164627 Verifying the integral representation:: >>> webere(v,z) 0.1486507351534283744485421 >>> quad(lambda t: sin(v*t-z*sin(t))/pi, [0,pi]) 0.1486507351534283744485421 **References** 1. [DLMF]_ section 11.10: Anger-Weber Functions """ lommels1 = r""" Gives the Lommel function `s_{\mu,\nu}` or `s^{(1)}_{\mu,\nu}` .. math :: s_{\mu,\nu}(z) = \frac{z^{\mu+1}}{(\mu-\nu+1)(\mu+\nu+1)} \,_1F_2\left(1; \frac{\mu-\nu+3}{2}, \frac{\mu+\nu+3}{2}; -\frac{z^2}{4} \right) which solves the inhomogeneous Bessel equation .. math :: z^2 f''(z) + z f'(z) + (z^2-\nu^2) f(z) = z^{\mu+1}. A second solution is given by :func:`~mpmath.lommels2`. **Plots** .. literalinclude :: /modules/mpmath/plots/lommels1.py .. image :: /modules/mpmath/plots/lommels1.png **Examples** An integral representation:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> u,v,z = 0.25, 0.125, mpf(0.75) >>> lommels1(u,v,z) 0.4276243877565150372999126 >>> (bessely(v,z)*quad(lambda t: t**u*besselj(v,t), [0,z]) - \ ... besselj(v,z)*quad(lambda t: t**u*bessely(v,t), [0,z]))*(pi/2) 0.4276243877565150372999126 A special value:: >>> lommels1(v,v,z) 0.5461221367746048054932553 >>> gamma(v+0.5)*sqrt(pi)*power(2,v-1)*struveh(v,z) 0.5461221367746048054932553 Verifying the differential equation:: >>> f = lambda z: lommels1(u,v,z) >>> z**2*diff(f,z,2) + z*diff(f,z) + (z**2-v**2)*f(z) 0.6979536443265746992059141 >>> z**(u+1) 0.6979536443265746992059141 **References** 1. [GradshteynRyzhik]_ 2. [Weisstein]_ http://mathworld.wolfram.com/LommelFunction.html """ lommels2 = r""" Gives the second Lommel function `S_{\mu,\nu}` or `s^{(2)}_{\mu,\nu}` .. math :: S_{\mu,\nu}(z) = s_{\mu,\nu}(z) + 2^{\mu-1} \Gamma\left(\tfrac{1}{2}(\mu-\nu+1)\right) \Gamma\left(\tfrac{1}{2}(\mu+\nu+1)\right) \times \left[\sin(\tfrac{1}{2}(\mu-\nu)\pi) J_{\nu}(z) - \cos(\tfrac{1}{2}(\mu-\nu)\pi) Y_{\nu}(z) \right] which solves the same differential equation as :func:`~mpmath.lommels1`. **Plots** .. literalinclude :: /modules/mpmath/plots/lommels2.py .. image :: /modules/mpmath/plots/lommels2.png **Examples** For large `|z|`, `S_{\mu,\nu} \sim z^{\mu-1}`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> lommels2(10,2,30000) 1.968299831601008419949804e+40 >>> power(30000,9) 1.9683e+40 A special value:: >>> u,v,z = 0.5, 0.125, mpf(0.75) >>> lommels2(v,v,z) 0.9589683199624672099969765 >>> (struveh(v,z)-bessely(v,z))*power(2,v-1)*sqrt(pi)*gamma(v+0.5) 0.9589683199624672099969765 Verifying the differential equation:: >>> f = lambda z: lommels2(u,v,z) >>> z**2*diff(f,z,2) + z*diff(f,z) + (z**2-v**2)*f(z) 0.6495190528383289850727924 >>> z**(u+1) 0.6495190528383289850727924 **References** 1. [GradshteynRyzhik]_ 2. [Weisstein]_ http://mathworld.wolfram.com/LommelFunction.html """ appellf2 = r""" Gives the Appell F2 hypergeometric function of two variables .. math :: F_2(a,b_1,b_2,c_1,c_2,x,y) = \sum_{m=0}^{\infty} \sum_{n=0}^{\infty} \frac{(a)_{m+n} (b_1)_m (b_2)_n}{(c_1)_m (c_2)_n} \frac{x^m y^n}{m! n!}. The series is generally absolutely convergent for `|x| + |y| < 1`. **Examples** Evaluation for real and complex arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> appellf2(1,2,3,4,5,0.25,0.125) 1.257417193533135344785602 >>> appellf2(1,-3,-4,2,3,2,3) -42.8 >>> appellf2(0.5,0.25,-0.25,2,3,0.25j,0.25) (0.9880539519421899867041719 + 0.01497616165031102661476978j) >>> chop(appellf2(1,1+j,1-j,3j,-3j,0.25,0.25)) 1.201311219287411337955192 >>> appellf2(1,1,1,4,6,0.125,16) (-0.09455532250274744282125152 - 0.7647282253046207836769297j) A transformation formula:: >>> a,b1,b2,c1,c2,x,y = map(mpf, [1,2,0.5,0.25,1.625,-0.125,0.125]) >>> appellf2(a,b1,b2,c1,c2,x,y) 0.2299211717841180783309688 >>> (1-x)**(-a)*appellf2(a,c1-b1,b2,c1,c2,x/(x-1),y/(1-x)) 0.2299211717841180783309688 A system of partial differential equations satisfied by F2:: >>> a,b1,b2,c1,c2,x,y = map(mpf, [1,0.5,0.25,1.125,1.5,0.0625,-0.0625]) >>> F = lambda x,y: appellf2(a,b1,b2,c1,c2,x,y) >>> chop(x*(1-x)*diff(F,(x,y),(2,0)) - ... x*y*diff(F,(x,y),(1,1)) + ... (c1-(a+b1+1)*x)*diff(F,(x,y),(1,0)) - ... b1*y*diff(F,(x,y),(0,1)) - ... a*b1*F(x,y)) 0.0 >>> chop(y*(1-y)*diff(F,(x,y),(0,2)) - ... x*y*diff(F,(x,y),(1,1)) + ... (c2-(a+b2+1)*y)*diff(F,(x,y),(0,1)) - ... b2*x*diff(F,(x,y),(1,0)) - ... a*b2*F(x,y)) 0.0 **References** See references for :func:`~mpmath.appellf1`. """ appellf3 = r""" Gives the Appell F3 hypergeometric function of two variables .. math :: F_3(a_1,a_2,b_1,b_2,c,x,y) = \sum_{m=0}^{\infty} \sum_{n=0}^{\infty} \frac{(a_1)_m (a_2)_n (b_1)_m (b_2)_n}{(c)_{m+n}} \frac{x^m y^n}{m! n!}. The series is generally absolutely convergent for `|x| < 1, |y| < 1`. **Examples** Evaluation for various parameters and variables:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> appellf3(1,2,3,4,5,0.5,0.25) 2.221557778107438938158705 >>> appellf3(1,2,3,4,5,6,0); hyp2f1(1,3,5,6) (-0.5189554589089861284537389 - 0.1454441043328607980769742j) (-0.5189554589089861284537389 - 0.1454441043328607980769742j) >>> appellf3(1,-2,-3,1,1,4,6) -17.4 >>> appellf3(1,2,-3,1,1,4,6) (17.7876136773677356641825 + 19.54768762233649126154534j) >>> appellf3(1,2,-3,1,1,6,4) (85.02054175067929402953645 + 148.4402528821177305173599j) >>> chop(appellf3(1+j,2,1-j,2,3,0.25,0.25)) 1.719992169545200286696007 Many transformations and evaluations for special combinations of the parameters are possible, e.g.: >>> a,b,c,x,y = map(mpf, [0.5,0.25,0.125,0.125,-0.125]) >>> appellf3(a,c-a,b,c-b,c,x,y) 1.093432340896087107444363 >>> (1-y)**(a+b-c)*hyp2f1(a,b,c,x+y-x*y) 1.093432340896087107444363 >>> x**2*appellf3(1,1,1,1,3,x,-x) 0.01568646277445385390945083 >>> polylog(2,x**2) 0.01568646277445385390945083 >>> a1,a2,b1,b2,c,x = map(mpf, [0.5,0.25,0.125,0.5,4.25,0.125]) >>> appellf3(a1,a2,b1,b2,c,x,1) 1.03947361709111140096947 >>> gammaprod([c,c-a2-b2],[c-a2,c-b2])*hyp3f2(a1,b1,c-a2-b2,c-a2,c-b2,x) 1.03947361709111140096947 The Appell F3 function satisfies a pair of partial differential equations:: >>> a1,a2,b1,b2,c,x,y = map(mpf, [0.5,0.25,0.125,0.5,0.625,0.0625,-0.0625]) >>> F = lambda x,y: appellf3(a1,a2,b1,b2,c,x,y) >>> chop(x*(1-x)*diff(F,(x,y),(2,0)) + ... y*diff(F,(x,y),(1,1)) + ... (c-(a1+b1+1)*x)*diff(F,(x,y),(1,0)) - ... a1*b1*F(x,y)) 0.0 >>> chop(y*(1-y)*diff(F,(x,y),(0,2)) + ... x*diff(F,(x,y),(1,1)) + ... (c-(a2+b2+1)*y)*diff(F,(x,y),(0,1)) - ... a2*b2*F(x,y)) 0.0 **References** See references for :func:`~mpmath.appellf1`. """ appellf4 = r""" Gives the Appell F4 hypergeometric function of two variables .. math :: F_4(a,b,c_1,c_2,x,y) = \sum_{m=0}^{\infty} \sum_{n=0}^{\infty} \frac{(a)_{m+n} (b)_{m+n}}{(c_1)_m (c_2)_n} \frac{x^m y^n}{m! n!}. The series is generally absolutely convergent for `\sqrt{|x|} + \sqrt{|y|} < 1`. **Examples** Evaluation for various parameters and arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> appellf4(1,1,2,2,0.25,0.125) 1.286182069079718313546608 >>> appellf4(-2,-3,4,5,4,5) 34.8 >>> appellf4(5,4,2,3,0.25j,-0.125j) (-0.2585967215437846642163352 + 2.436102233553582711818743j) Reduction to `\,_2F_1` in a special case:: >>> a,b,c,x,y = map(mpf, [0.5,0.25,0.125,0.125,-0.125]) >>> appellf4(a,b,c,a+b-c+1,x*(1-y),y*(1-x)) 1.129143488466850868248364 >>> hyp2f1(a,b,c,x)*hyp2f1(a,b,a+b-c+1,y) 1.129143488466850868248364 A system of partial differential equations satisfied by F4:: >>> a,b,c1,c2,x,y = map(mpf, [1,0.5,0.25,1.125,0.0625,-0.0625]) >>> F = lambda x,y: appellf4(a,b,c1,c2,x,y) >>> chop(x*(1-x)*diff(F,(x,y),(2,0)) - ... y**2*diff(F,(x,y),(0,2)) - ... 2*x*y*diff(F,(x,y),(1,1)) + ... (c1-(a+b+1)*x)*diff(F,(x,y),(1,0)) - ... ((a+b+1)*y)*diff(F,(x,y),(0,1)) - ... a*b*F(x,y)) 0.0 >>> chop(y*(1-y)*diff(F,(x,y),(0,2)) - ... x**2*diff(F,(x,y),(2,0)) - ... 2*x*y*diff(F,(x,y),(1,1)) + ... (c2-(a+b+1)*y)*diff(F,(x,y),(0,1)) - ... ((a+b+1)*x)*diff(F,(x,y),(1,0)) - ... a*b*F(x,y)) 0.0 **References** See references for :func:`~mpmath.appellf1`. """ zeta = r""" Computes the Riemann zeta function .. math :: \zeta(s) = 1+\frac{1}{2^s}+\frac{1}{3^s}+\frac{1}{4^s}+\ldots or, with `a \ne 1`, the more general Hurwitz zeta function .. math :: \zeta(s,a) = \sum_{k=0}^\infty \frac{1}{(a+k)^s}. Optionally, ``zeta(s, a, n)`` computes the `n`-th derivative with respect to `s`, .. math :: \zeta^{(n)}(s,a) = (-1)^n \sum_{k=0}^\infty \frac{\log^n(a+k)}{(a+k)^s}. Although these series only converge for `\Re(s) > 1`, the Riemann and Hurwitz zeta functions are defined through analytic continuation for arbitrary complex `s \ne 1` (`s = 1` is a pole). The implementation uses three algorithms: the Borwein algorithm for the Riemann zeta function when `s` is close to the real line; the Riemann-Siegel formula for the Riemann zeta function when `s` is large imaginary, and Euler-Maclaurin summation in all other cases. The reflection formula for `\Re(s) < 0` is implemented in some cases. The algorithm can be chosen with ``method = 'borwein'``, ``method='riemann-siegel'`` or ``method = 'euler-maclaurin'``. The parameter `a` is usually a rational number `a = p/q`, and may be specified as such by passing an integer tuple `(p, q)`. Evaluation is supported for arbitrary complex `a`, but may be slow and/or inaccurate when `\Re(s) < 0` for nonrational `a` or when computing derivatives. **Examples** Some values of the Riemann zeta function:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> zeta(2); pi**2 / 6 1.644934066848226436472415 1.644934066848226436472415 >>> zeta(0) -0.5 >>> zeta(-1) -0.08333333333333333333333333 >>> zeta(-2) 0.0 For large positive `s`, `\zeta(s)` rapidly approaches 1:: >>> zeta(50) 1.000000000000000888178421 >>> zeta(100) 1.0 >>> zeta(inf) 1.0 >>> 1-sum((zeta(k)-1)/k for k in range(2,85)); +euler 0.5772156649015328606065121 0.5772156649015328606065121 >>> nsum(lambda k: zeta(k)-1, [2, inf]) 1.0 Evaluation is supported for complex `s` and `a`: >>> zeta(-3+4j) (-0.03373057338827757067584698 + 0.2774499251557093745297677j) >>> zeta(2+3j, -1+j) (389.6841230140842816370741 + 295.2674610150305334025962j) The Riemann zeta function has so-called nontrivial zeros on the critical line `s = 1/2 + it`:: >>> findroot(zeta, 0.5+14j); zetazero(1) (0.5 + 14.13472514173469379045725j) (0.5 + 14.13472514173469379045725j) >>> findroot(zeta, 0.5+21j); zetazero(2) (0.5 + 21.02203963877155499262848j) (0.5 + 21.02203963877155499262848j) >>> findroot(zeta, 0.5+25j); zetazero(3) (0.5 + 25.01085758014568876321379j) (0.5 + 25.01085758014568876321379j) >>> chop(zeta(zetazero(10))) 0.0 Evaluation on and near the critical line is supported for large heights `t` by means of the Riemann-Siegel formula (currently for `a = 1`, `n \le 4`):: >>> zeta(0.5+100000j) (1.073032014857753132114076 + 5.780848544363503984261041j) >>> zeta(0.75+1000000j) (0.9535316058375145020351559 + 0.9525945894834273060175651j) >>> zeta(0.5+10000000j) (11.45804061057709254500227 - 8.643437226836021723818215j) >>> zeta(0.5+100000000j, derivative=1) (51.12433106710194942681869 + 43.87221167872304520599418j) >>> zeta(0.5+100000000j, derivative=2) (-444.2760822795430400549229 - 896.3789978119185981665403j) >>> zeta(0.5+100000000j, derivative=3) (3230.72682687670422215339 + 14374.36950073615897616781j) >>> zeta(0.5+100000000j, derivative=4) (-11967.35573095046402130602 - 218945.7817789262839266148j) >>> zeta(1+10000000j) # off the line (2.859846483332530337008882 + 0.491808047480981808903986j) >>> zeta(1+10000000j, derivative=1) (-4.333835494679647915673205 - 0.08405337962602933636096103j) >>> zeta(1+10000000j, derivative=4) (453.2764822702057701894278 - 581.963625832768189140995j) For investigation of the zeta function zeros, the Riemann-Siegel Z-function is often more convenient than working with the Riemann zeta function directly (see :func:`~mpmath.siegelz`). Some values of the Hurwitz zeta function:: >>> zeta(2, 3); -5./4 + pi**2/6 0.3949340668482264364724152 0.3949340668482264364724152 >>> zeta(2, (3,4)); pi**2 - 8*catalan 2.541879647671606498397663 2.541879647671606498397663 For positive integer values of `s`, the Hurwitz zeta function is equivalent to a polygamma function (except for a normalizing factor):: >>> zeta(4, (1,5)); psi(3, '1/5')/6 625.5408324774542966919938 625.5408324774542966919938 Evaluation of derivatives:: >>> zeta(0, 3+4j, 1); loggamma(3+4j) - ln(2*pi)/2 (-2.675565317808456852310934 + 4.742664438034657928194889j) (-2.675565317808456852310934 + 4.742664438034657928194889j) >>> zeta(2, 1, 20) 2432902008176640000.000242 >>> zeta(3+4j, 5.5+2j, 4) (-0.140075548947797130681075 - 0.3109263360275413251313634j) >>> zeta(0.5+100000j, 1, 4) (-10407.16081931495861539236 + 13777.78669862804508537384j) >>> zeta(-100+0.5j, (1,3), derivative=4) (4.007180821099823942702249e+79 + 4.916117957092593868321778e+78j) Generating a Taylor series at `s = 2` using derivatives:: >>> for k in range(11): print("%s * (s-2)^%i" % (zeta(2,1,k)/fac(k), k)) ... 1.644934066848226436472415 * (s-2)^0 -0.9375482543158437537025741 * (s-2)^1 0.9946401171494505117104293 * (s-2)^2 -1.000024300473840810940657 * (s-2)^3 1.000061933072352565457512 * (s-2)^4 -1.000006869443931806408941 * (s-2)^5 1.000000173233769531820592 * (s-2)^6 -0.9999999569989868493432399 * (s-2)^7 0.9999999937218844508684206 * (s-2)^8 -0.9999999996355013916608284 * (s-2)^9 1.000000000004610645020747 * (s-2)^10 Evaluation at zero and for negative integer `s`:: >>> zeta(0, 10) -9.5 >>> zeta(-2, (2,3)); mpf(1)/81 0.01234567901234567901234568 0.01234567901234567901234568 >>> zeta(-3+4j, (5,4)) (0.2899236037682695182085988 + 0.06561206166091757973112783j) >>> zeta(-3.25, 1/pi) -0.0005117269627574430494396877 >>> zeta(-3.5, pi, 1) 11.156360390440003294709 >>> zeta(-100.5, (8,3)) -4.68162300487989766727122e+77 >>> zeta(-10.5, (-8,3)) (-0.01521913704446246609237979 + 29907.72510874248161608216j) >>> zeta(-1000.5, (-8,3)) (1.031911949062334538202567e+1770 + 1.519555750556794218804724e+426j) >>> zeta(-1+j, 3+4j) (-16.32988355630802510888631 - 22.17706465801374033261383j) >>> zeta(-1+j, 3+4j, 2) (32.48985276392056641594055 - 51.11604466157397267043655j) >>> diff(lambda s: zeta(s, 3+4j), -1+j, 2) (32.48985276392056641594055 - 51.11604466157397267043655j) **References** 1. http://mathworld.wolfram.com/RiemannZetaFunction.html 2. http://mathworld.wolfram.com/HurwitzZetaFunction.html 3. http://www.cecm.sfu.ca/personal/pborwein/PAPERS/P155.pdf """ dirichlet = r""" Evaluates the Dirichlet L-function .. math :: L(s,\chi) = \sum_{k=1}^\infty \frac{\chi(k)}{k^s}. where `\chi` is a periodic sequence of length `q` which should be supplied in the form of a list `[\chi(0), \chi(1), \ldots, \chi(q-1)]`. Strictly, `\chi` should be a Dirichlet character, but any periodic sequence will work. For example, ``dirichlet(s, [1])`` gives the ordinary Riemann zeta function and ``dirichlet(s, [-1,1])`` gives the alternating zeta function (Dirichlet eta function). Also the derivative with respect to `s` (currently only a first derivative) can be evaluated. **Examples** The ordinary Riemann zeta function:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> dirichlet(3, [1]); zeta(3) 1.202056903159594285399738 1.202056903159594285399738 >>> dirichlet(1, [1]) +inf The alternating zeta function:: >>> dirichlet(1, [-1,1]); ln(2) 0.6931471805599453094172321 0.6931471805599453094172321 The following defines the Dirichlet beta function `\beta(s) = \sum_{k=0}^\infty \frac{(-1)^k}{(2k+1)^s}` and verifies several values of this function:: >>> B = lambda s, d=0: dirichlet(s, [0, 1, 0, -1], d) >>> B(0); 1./2 0.5 0.5 >>> B(1); pi/4 0.7853981633974483096156609 0.7853981633974483096156609 >>> B(2); +catalan 0.9159655941772190150546035 0.9159655941772190150546035 >>> B(2,1); diff(B, 2) 0.08158073611659279510291217 0.08158073611659279510291217 >>> B(-1,1); 2*catalan/pi 0.5831218080616375602767689 0.5831218080616375602767689 >>> B(0,1); log(gamma(0.25)**2/(2*pi*sqrt(2))) 0.3915943927068367764719453 0.3915943927068367764719454 >>> B(1,1); 0.25*pi*(euler+2*ln2+3*ln(pi)-4*ln(gamma(0.25))) 0.1929013167969124293631898 0.1929013167969124293631898 A custom L-series of period 3:: >>> dirichlet(2, [2,0,1]) 0.7059715047839078092146831 >>> 2*nsum(lambda k: (3*k)**-2, [1,inf]) + \ ... nsum(lambda k: (3*k+2)**-2, [0,inf]) 0.7059715047839078092146831 """ coulombf = r""" Calculates the regular Coulomb wave function .. math :: F_l(\eta,z) = C_l(\eta) z^{l+1} e^{-iz} \,_1F_1(l+1-i\eta, 2l+2, 2iz) where the normalization constant `C_l(\eta)` is as calculated by :func:`~mpmath.coulombc`. This function solves the differential equation .. math :: f''(z) + \left(1-\frac{2\eta}{z}-\frac{l(l+1)}{z^2}\right) f(z) = 0. A second linearly independent solution is given by the irregular Coulomb wave function `G_l(\eta,z)` (see :func:`~mpmath.coulombg`) and thus the general solution is `f(z) = C_1 F_l(\eta,z) + C_2 G_l(\eta,z)` for arbitrary constants `C_1`, `C_2`. Physically, the Coulomb wave functions give the radial solution to the Schrodinger equation for a point particle in a `1/z` potential; `z` is then the radius and `l`, `\eta` are quantum numbers. The Coulomb wave functions with real parameters are defined in Abramowitz & Stegun, section 14. However, all parameters are permitted to be complex in this implementation (see references). **Plots** .. literalinclude :: /modules/mpmath/plots/coulombf.py .. image :: /modules/mpmath/plots/coulombf.png .. literalinclude :: /modules/mpmath/plots/coulombf_c.py .. image :: /modules/mpmath/plots/coulombf_c.png **Examples** Evaluation is supported for arbitrary magnitudes of `z`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> coulombf(2, 1.5, 3.5) 0.4080998961088761187426445 >>> coulombf(-2, 1.5, 3.5) 0.7103040849492536747533465 >>> coulombf(2, 1.5, '1e-10') 4.143324917492256448770769e-33 >>> coulombf(2, 1.5, 1000) 0.4482623140325567050716179 >>> coulombf(2, 1.5, 10**10) -0.066804196437694360046619 Verifying the differential equation:: >>> l, eta, z = 2, 3, mpf(2.75) >>> A, B = 1, 2 >>> f = lambda z: A*coulombf(l,eta,z) + B*coulombg(l,eta,z) >>> chop(diff(f,z,2) + (1-2*eta/z - l*(l+1)/z**2)*f(z)) 0.0 A Wronskian relation satisfied by the Coulomb wave functions:: >>> l = 2 >>> eta = 1.5 >>> F = lambda z: coulombf(l,eta,z) >>> G = lambda z: coulombg(l,eta,z) >>> for z in [3.5, -1, 2+3j]: ... chop(diff(F,z)*G(z) - F(z)*diff(G,z)) ... 1.0 1.0 1.0 Another Wronskian relation:: >>> F = coulombf >>> G = coulombg >>> for z in [3.5, -1, 2+3j]: ... chop(F(l-1,eta,z)*G(l,eta,z)-F(l,eta,z)*G(l-1,eta,z) - l/sqrt(l**2+eta**2)) ... 0.0 0.0 0.0 An integral identity connecting the regular and irregular wave functions:: >>> l, eta, z = 4+j, 2-j, 5+2j >>> coulombf(l,eta,z) + j*coulombg(l,eta,z) (0.7997977752284033239714479 + 0.9294486669502295512503127j) >>> g = lambda t: exp(-t)*t**(l-j*eta)*(t+2*j*z)**(l+j*eta) >>> j*exp(-j*z)*z**(-l)/fac(2*l+1)/coulombc(l,eta)*quad(g, [0,inf]) (0.7997977752284033239714479 + 0.9294486669502295512503127j) Some test case with complex parameters, taken from Michel [2]:: >>> mp.dps = 15 >>> coulombf(1+0.1j, 50+50j, 100.156) (-1.02107292320897e+15 - 2.83675545731519e+15j) >>> coulombg(1+0.1j, 50+50j, 100.156) (2.83675545731519e+15 - 1.02107292320897e+15j) >>> coulombf(1e-5j, 10+1e-5j, 0.1+1e-6j) (4.30566371247811e-14 - 9.03347835361657e-19j) >>> coulombg(1e-5j, 10+1e-5j, 0.1+1e-6j) (778709182061.134 + 18418936.2660553j) The following reproduces a table in Abramowitz & Stegun, at twice the precision:: >>> mp.dps = 10 >>> eta = 2; z = 5 >>> for l in [5, 4, 3, 2, 1, 0]: ... print("%s %s %s" % (l, coulombf(l,eta,z), ... diff(lambda z: coulombf(l,eta,z), z))) ... 5 0.09079533488 0.1042553261 4 0.2148205331 0.2029591779 3 0.4313159311 0.320534053 2 0.7212774133 0.3952408216 1 0.9935056752 0.3708676452 0 1.143337392 0.2937960375 **References** 1. I.J. Thompson & A.R. Barnett, "Coulomb and Bessel Functions of Complex Arguments and Order", J. Comp. Phys., vol 64, no. 2, June 1986. 2. N. Michel, "Precise Coulomb wave functions for a wide range of complex `l`, `\eta` and `z`", http://arxiv.org/abs/physics/0702051v1 """ coulombg = r""" Calculates the irregular Coulomb wave function .. math :: G_l(\eta,z) = \frac{F_l(\eta,z) \cos(\chi) - F_{-l-1}(\eta,z)}{\sin(\chi)} where `\chi = \sigma_l - \sigma_{-l-1} - (l+1/2) \pi` and `\sigma_l(\eta) = (\ln \Gamma(1+l+i\eta)-\ln \Gamma(1+l-i\eta))/(2i)`. See :func:`~mpmath.coulombf` for additional information. **Plots** .. literalinclude :: /modules/mpmath/plots/coulombg.py .. image :: /modules/mpmath/plots/coulombg.png .. literalinclude :: /modules/mpmath/plots/coulombg_c.py .. image :: /modules/mpmath/plots/coulombg_c.png **Examples** Evaluation is supported for arbitrary magnitudes of `z`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> coulombg(-2, 1.5, 3.5) 1.380011900612186346255524 >>> coulombg(2, 1.5, 3.5) 1.919153700722748795245926 >>> coulombg(-2, 1.5, '1e-10') 201126715824.7329115106793 >>> coulombg(-2, 1.5, 1000) 0.1802071520691149410425512 >>> coulombg(-2, 1.5, 10**10) 0.652103020061678070929794 The following reproduces a table in Abramowitz & Stegun, at twice the precision:: >>> mp.dps = 10 >>> eta = 2; z = 5 >>> for l in [1, 2, 3, 4, 5]: ... print("%s %s %s" % (l, coulombg(l,eta,z), ... -diff(lambda z: coulombg(l,eta,z), z))) ... 1 1.08148276 0.6028279961 2 1.496877075 0.5661803178 3 2.048694714 0.7959909551 4 3.09408669 1.731802374 5 5.629840456 4.549343289 Evaluation close to the singularity at `z = 0`:: >>> mp.dps = 15 >>> coulombg(0,10,1) 3088184933.67358 >>> coulombg(0,10,'1e-10') 5554866000719.8 >>> coulombg(0,10,'1e-100') 5554866221524.1 Evaluation with a half-integer value for `l`:: >>> coulombg(1.5, 1, 10) 0.852320038297334 """ coulombc = r""" Gives the normalizing Gamow constant for Coulomb wave functions, .. math :: C_l(\eta) = 2^l \exp\left(-\pi \eta/2 + [\ln \Gamma(1+l+i\eta) + \ln \Gamma(1+l-i\eta)]/2 - \ln \Gamma(2l+2)\right), where the log gamma function with continuous imaginary part away from the negative half axis (see :func:`~mpmath.loggamma`) is implied. This function is used internally for the calculation of Coulomb wave functions, and automatically cached to make multiple evaluations with fixed `l`, `\eta` fast. """ ellipfun = r""" Computes any of the Jacobi elliptic functions, defined in terms of Jacobi theta functions as .. math :: \mathrm{sn}(u,m) = \frac{\vartheta_3(0,q)}{\vartheta_2(0,q)} \frac{\vartheta_1(t,q)}{\vartheta_4(t,q)} \mathrm{cn}(u,m) = \frac{\vartheta_4(0,q)}{\vartheta_2(0,q)} \frac{\vartheta_2(t,q)}{\vartheta_4(t,q)} \mathrm{dn}(u,m) = \frac{\vartheta_4(0,q)}{\vartheta_3(0,q)} \frac{\vartheta_3(t,q)}{\vartheta_4(t,q)}, or more generally computes a ratio of two such functions. Here `t = u/\vartheta_3(0,q)^2`, and `q = q(m)` denotes the nome (see :func:`~mpmath.nome`). Optionally, you can specify the nome directly instead of `m` by passing ``q=``, or you can directly specify the elliptic parameter `k` with ``k=``. The first argument should be a two-character string specifying the function using any combination of ``'s'``, ``'c'``, ``'d'``, ``'n'``. These letters respectively denote the basic functions `\mathrm{sn}(u,m)`, `\mathrm{cn}(u,m)`, `\mathrm{dn}(u,m)`, and `1`. The identifier specifies the ratio of two such functions. For example, ``'ns'`` identifies the function .. math :: \mathrm{ns}(u,m) = \frac{1}{\mathrm{sn}(u,m)} and ``'cd'`` identifies the function .. math :: \mathrm{cd}(u,m) = \frac{\mathrm{cn}(u,m)}{\mathrm{dn}(u,m)}. If called with only the first argument, a function object evaluating the chosen function for given arguments is returned. **Examples** Basic evaluation:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> ellipfun('cd', 3.5, 0.5) -0.9891101840595543931308394 >>> ellipfun('cd', 3.5, q=0.25) 0.07111979240214668158441418 The sn-function is doubly periodic in the complex plane with periods `4 K(m)` and `2 i K(1-m)` (see :func:`~mpmath.ellipk`):: >>> sn = ellipfun('sn') >>> sn(2, 0.25) 0.9628981775982774425751399 >>> sn(2+4*ellipk(0.25), 0.25) 0.9628981775982774425751399 >>> chop(sn(2+2*j*ellipk(1-0.25), 0.25)) 0.9628981775982774425751399 The cn-function is doubly periodic with periods `4 K(m)` and `4 i K(1-m)`:: >>> cn = ellipfun('cn') >>> cn(2, 0.25) -0.2698649654510865792581416 >>> cn(2+4*ellipk(0.25), 0.25) -0.2698649654510865792581416 >>> chop(cn(2+4*j*ellipk(1-0.25), 0.25)) -0.2698649654510865792581416 The dn-function is doubly periodic with periods `2 K(m)` and `4 i K(1-m)`:: >>> dn = ellipfun('dn') >>> dn(2, 0.25) 0.8764740583123262286931578 >>> dn(2+2*ellipk(0.25), 0.25) 0.8764740583123262286931578 >>> chop(dn(2+4*j*ellipk(1-0.25), 0.25)) 0.8764740583123262286931578 """ jtheta = r""" Computes the Jacobi theta function `\vartheta_n(z, q)`, where `n = 1, 2, 3, 4`, defined by the infinite series: .. math :: \vartheta_1(z,q) = 2 q^{1/4} \sum_{n=0}^{\infty} (-1)^n q^{n^2+n\,} \sin((2n+1)z) \vartheta_2(z,q) = 2 q^{1/4} \sum_{n=0}^{\infty} q^{n^{2\,} + n} \cos((2n+1)z) \vartheta_3(z,q) = 1 + 2 \sum_{n=1}^{\infty} q^{n^2\,} \cos(2 n z) \vartheta_4(z,q) = 1 + 2 \sum_{n=1}^{\infty} (-q)^{n^2\,} \cos(2 n z) The theta functions are functions of two variables: * `z` is the *argument*, an arbitrary real or complex number * `q` is the *nome*, which must be a real or complex number in the unit disk (i.e. `|q| < 1`). For `|q| \ll 1`, the series converge very quickly, so the Jacobi theta functions can efficiently be evaluated to high precision. The compact notations `\vartheta_n(q) = \vartheta_n(0,q)` and `\vartheta_n = \vartheta_n(0,q)` are also frequently encountered. Finally, Jacobi theta functions are frequently considered as functions of the half-period ratio `\tau` and then usually denoted by `\vartheta_n(z|\tau)`. Optionally, ``jtheta(n, z, q, derivative=d)`` with `d > 0` computes a `d`-th derivative with respect to `z`. **Examples and basic properties** Considered as functions of `z`, the Jacobi theta functions may be viewed as generalizations of the ordinary trigonometric functions cos and sin. They are periodic functions:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> jtheta(1, 0.25, '0.2') 0.2945120798627300045053104 >>> jtheta(1, 0.25 + 2*pi, '0.2') 0.2945120798627300045053104 Indeed, the series defining the theta functions are essentially trigonometric Fourier series. The coefficients can be retrieved using :func:`~mpmath.fourier`:: >>> mp.dps = 10 >>> nprint(fourier(lambda x: jtheta(2, x, 0.5), [-pi, pi], 4)) ([0.0, 1.68179, 0.0, 0.420448, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0]) The Jacobi theta functions are also so-called quasiperiodic functions of `z` and `\tau`, meaning that for fixed `\tau`, `\vartheta_n(z, q)` and `\vartheta_n(z+\pi \tau, q)` are the same except for an exponential factor:: >>> mp.dps = 25 >>> tau = 3*j/10 >>> q = exp(pi*j*tau) >>> z = 10 >>> jtheta(4, z+tau*pi, q) (-0.682420280786034687520568 + 1.526683999721399103332021j) >>> -exp(-2*j*z)/q * jtheta(4, z, q) (-0.682420280786034687520568 + 1.526683999721399103332021j) The Jacobi theta functions satisfy a huge number of other functional equations, such as the following identity (valid for any `q`):: >>> q = mpf(3)/10 >>> jtheta(3,0,q)**4 6.823744089352763305137427 >>> jtheta(2,0,q)**4 + jtheta(4,0,q)**4 6.823744089352763305137427 Extensive listings of identities satisfied by the Jacobi theta functions can be found in standard reference works. The Jacobi theta functions are related to the gamma function for special arguments:: >>> jtheta(3, 0, exp(-pi)) 1.086434811213308014575316 >>> pi**(1/4.) / gamma(3/4.) 1.086434811213308014575316 :func:`~mpmath.jtheta` supports arbitrary precision evaluation and complex arguments:: >>> mp.dps = 50 >>> jtheta(4, sqrt(2), 0.5) 2.0549510717571539127004115835148878097035750653737 >>> mp.dps = 25 >>> jtheta(4, 1+2j, (1+j)/5) (7.180331760146805926356634 - 1.634292858119162417301683j) Evaluation of derivatives:: >>> mp.dps = 25 >>> jtheta(1, 7, 0.25, 1); diff(lambda z: jtheta(1, z, 0.25), 7) 1.209857192844475388637236 1.209857192844475388637236 >>> jtheta(1, 7, 0.25, 2); diff(lambda z: jtheta(1, z, 0.25), 7, 2) -0.2598718791650217206533052 -0.2598718791650217206533052 >>> jtheta(2, 7, 0.25, 1); diff(lambda z: jtheta(2, z, 0.25), 7) -1.150231437070259644461474 -1.150231437070259644461474 >>> jtheta(2, 7, 0.25, 2); diff(lambda z: jtheta(2, z, 0.25), 7, 2) -0.6226636990043777445898114 -0.6226636990043777445898114 >>> jtheta(3, 7, 0.25, 1); diff(lambda z: jtheta(3, z, 0.25), 7) -0.9990312046096634316587882 -0.9990312046096634316587882 >>> jtheta(3, 7, 0.25, 2); diff(lambda z: jtheta(3, z, 0.25), 7, 2) -0.1530388693066334936151174 -0.1530388693066334936151174 >>> jtheta(4, 7, 0.25, 1); diff(lambda z: jtheta(4, z, 0.25), 7) 0.9820995967262793943571139 0.9820995967262793943571139 >>> jtheta(4, 7, 0.25, 2); diff(lambda z: jtheta(4, z, 0.25), 7, 2) 0.3936902850291437081667755 0.3936902850291437081667755 **Possible issues** For `|q| \ge 1` or `\Im(\tau) \le 0`, :func:`~mpmath.jtheta` raises ``ValueError``. This exception is also raised for `|q|` extremely close to 1 (or equivalently `\tau` very close to 0), since the series would converge too slowly:: >>> jtheta(1, 10, 0.99999999 * exp(0.5*j)) Traceback (most recent call last): ... ValueError: abs(q) > THETA_Q_LIM = 1.000000 """ eulernum = r""" Gives the `n`-th Euler number, defined as the `n`-th derivative of `\mathrm{sech}(t) = 1/\cosh(t)` evaluated at `t = 0`. Equivalently, the Euler numbers give the coefficients of the Taylor series .. math :: \mathrm{sech}(t) = \sum_{n=0}^{\infty} \frac{E_n}{n!} t^n. The Euler numbers are closely related to Bernoulli numbers and Bernoulli polynomials. They can also be evaluated in terms of Euler polynomials (see :func:`~mpmath.eulerpoly`) as `E_n = 2^n E_n(1/2)`. **Examples** Computing the first few Euler numbers and verifying that they agree with the Taylor series:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> [eulernum(n) for n in range(11)] [1.0, 0.0, -1.0, 0.0, 5.0, 0.0, -61.0, 0.0, 1385.0, 0.0, -50521.0] >>> chop(diffs(sech, 0, 10)) [1.0, 0.0, -1.0, 0.0, 5.0, 0.0, -61.0, 0.0, 1385.0, 0.0, -50521.0] Euler numbers grow very rapidly. :func:`~mpmath.eulernum` efficiently computes numerical approximations for large indices:: >>> eulernum(50) -6.053285248188621896314384e+54 >>> eulernum(1000) 3.887561841253070615257336e+2371 >>> eulernum(10**20) 4.346791453661149089338186e+1936958564106659551331 Comparing with an asymptotic formula for the Euler numbers:: >>> n = 10**5 >>> (-1)**(n//2) * 8 * sqrt(n/(2*pi)) * (2*n/(pi*e))**n 3.69919063017432362805663e+436961 >>> eulernum(n) 3.699193712834466537941283e+436961 Pass ``exact=True`` to obtain exact values of Euler numbers as integers:: >>> print(eulernum(50, exact=True)) -6053285248188621896314383785111649088103498225146815121 >>> print(eulernum(200, exact=True) % 10**10) 1925859625 >>> eulernum(1001, exact=True) 0 """ eulerpoly = r""" Evaluates the Euler polynomial `E_n(z)`, defined by the generating function representation .. math :: \frac{2e^{zt}}{e^t+1} = \sum_{n=0}^\infty E_n(z) \frac{t^n}{n!}. The Euler polynomials may also be represented in terms of Bernoulli polynomials (see :func:`~mpmath.bernpoly`) using various formulas, for example .. math :: E_n(z) = \frac{2}{n+1} \left( B_n(z)-2^{n+1}B_n\left(\frac{z}{2}\right) \right). Special values include the Euler numbers `E_n = 2^n E_n(1/2)` (see :func:`~mpmath.eulernum`). **Examples** Computing the coefficients of the first few Euler polynomials:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> for n in range(6): ... chop(taylor(lambda z: eulerpoly(n,z), 0, n)) ... [1.0] [-0.5, 1.0] [0.0, -1.0, 1.0] [0.25, 0.0, -1.5, 1.0] [0.0, 1.0, 0.0, -2.0, 1.0] [-0.5, 0.0, 2.5, 0.0, -2.5, 1.0] Evaluation for arbitrary `z`:: >>> eulerpoly(2,3) 6.0 >>> eulerpoly(5,4) 423.5 >>> eulerpoly(35, 11111111112) 3.994957561486776072734601e+351 >>> eulerpoly(4, 10+20j) (-47990.0 - 235980.0j) >>> eulerpoly(2, '-3.5e-5') 0.000035001225 >>> eulerpoly(3, 0.5) 0.0 >>> eulerpoly(55, -10**80) -1.0e+4400 >>> eulerpoly(5, -inf) -inf >>> eulerpoly(6, -inf) +inf Computing Euler numbers:: >>> 2**26 * eulerpoly(26,0.5) -4087072509293123892361.0 >>> eulernum(26) -4087072509293123892361.0 Evaluation is accurate for large `n` and small `z`:: >>> eulerpoly(100, 0.5) 2.29047999988194114177943e+108 >>> eulerpoly(1000, 10.5) 3.628120031122876847764566e+2070 >>> eulerpoly(10000, 10.5) 1.149364285543783412210773e+30688 """ spherharm = r""" Evaluates the spherical harmonic `Y_l^m(\theta,\phi)`, .. math :: Y_l^m(\theta,\phi) = \sqrt{\frac{2l+1}{4\pi}\frac{(l-m)!}{(l+m)!}} P_l^m(\cos \theta) e^{i m \phi} where `P_l^m` is an associated Legendre function (see :func:`~mpmath.legenp`). Here `\theta \in [0, \pi]` denotes the polar coordinate (ranging from the north pole to the south pole) and `\phi \in [0, 2 \pi]` denotes the azimuthal coordinate on a sphere. Care should be used since many different conventions for spherical coordinate variables are used. Usually spherical harmonics are considered for `l \in \mathbb{N}`, `m \in \mathbb{Z}`, `|m| \le l`. More generally, `l,m,\theta,\phi` are permitted to be complex numbers. .. note :: :func:`~mpmath.spherharm` returns a complex number, even the value is purely real. **Plots** .. literalinclude :: /modules/mpmath/plots/spherharm40.py `Y_{4,0}`: .. image :: /modules/mpmath/plots/spherharm40.png `Y_{4,1}`: .. image :: /modules/mpmath/plots/spherharm41.png `Y_{4,2}`: .. image :: /modules/mpmath/plots/spherharm42.png `Y_{4,3}`: .. image :: /modules/mpmath/plots/spherharm43.png `Y_{4,4}`: .. image :: /modules/mpmath/plots/spherharm44.png **Examples** Some low-order spherical harmonics with reference values:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> theta = pi/4 >>> phi = pi/3 >>> spherharm(0,0,theta,phi); 0.5*sqrt(1/pi)*expj(0) (0.2820947917738781434740397 + 0.0j) (0.2820947917738781434740397 + 0.0j) >>> spherharm(1,-1,theta,phi); 0.5*sqrt(3/(2*pi))*expj(-phi)*sin(theta) (0.1221506279757299803965962 - 0.2115710938304086076055298j) (0.1221506279757299803965962 - 0.2115710938304086076055298j) >>> spherharm(1,0,theta,phi); 0.5*sqrt(3/pi)*cos(theta)*expj(0) (0.3454941494713354792652446 + 0.0j) (0.3454941494713354792652446 + 0.0j) >>> spherharm(1,1,theta,phi); -0.5*sqrt(3/(2*pi))*expj(phi)*sin(theta) (-0.1221506279757299803965962 - 0.2115710938304086076055298j) (-0.1221506279757299803965962 - 0.2115710938304086076055298j) With the normalization convention used, the spherical harmonics are orthonormal on the unit sphere:: >>> sphere = [0,pi], [0,2*pi] >>> dS = lambda t,p: fp.sin(t) # differential element >>> Y1 = lambda t,p: fp.spherharm(l1,m1,t,p) >>> Y2 = lambda t,p: fp.conj(fp.spherharm(l2,m2,t,p)) >>> l1 = l2 = 3; m1 = m2 = 2 >>> print(fp.quad(lambda t,p: Y1(t,p)*Y2(t,p)*dS(t,p), *sphere)) (1+0j) >>> m2 = 1 # m1 != m2 >>> print(fp.chop(fp.quad(lambda t,p: Y1(t,p)*Y2(t,p)*dS(t,p), *sphere))) 0.0 Evaluation is accurate for large orders:: >>> spherharm(1000,750,0.5,0.25) (3.776445785304252879026585e-102 - 5.82441278771834794493484e-102j) Evaluation works with complex parameter values:: >>> spherharm(1+j, 2j, 2+3j, -0.5j) (64.44922331113759992154992 + 1981.693919841408089681743j) """ scorergi = r""" Evaluates the Scorer function .. math :: \operatorname{Gi}(z) = \operatorname{Ai}(z) \int_0^z \operatorname{Bi}(t) dt + \operatorname{Bi}(z) \int_z^{\infty} \operatorname{Ai}(t) dt which gives a particular solution to the inhomogeneous Airy differential equation `f''(z) - z f(z) = 1/\pi`. Another particular solution is given by the Scorer Hi-function (:func:`~mpmath.scorerhi`). The two functions are related as `\operatorname{Gi}(z) + \operatorname{Hi}(z) = \operatorname{Bi}(z)`. **Plots** .. literalinclude :: /modules/mpmath/plots/gi.py .. image :: /modules/mpmath/plots/gi.png .. literalinclude :: /modules/mpmath/plots/gi_c.py .. image :: /modules/mpmath/plots/gi_c.png **Examples** Some values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> scorergi(0); 1/(power(3,'7/6')*gamma('2/3')) 0.2049755424820002450503075 0.2049755424820002450503075 >>> diff(scorergi, 0); 1/(power(3,'5/6')*gamma('1/3')) 0.1494294524512754526382746 0.1494294524512754526382746 >>> scorergi(+inf); scorergi(-inf) 0.0 0.0 >>> scorergi(1) 0.2352184398104379375986902 >>> scorergi(-1) -0.1166722172960152826494198 Evaluation for large arguments:: >>> scorergi(10) 0.03189600510067958798062034 >>> scorergi(100) 0.003183105228162961476590531 >>> scorergi(1000000) 0.0000003183098861837906721743873 >>> 1/(pi*1000000) 0.0000003183098861837906715377675 >>> scorergi(-1000) -0.08358288400262780392338014 >>> scorergi(-100000) 0.02886866118619660226809581 >>> scorergi(50+10j) (0.0061214102799778578790984 - 0.001224335676457532180747917j) >>> scorergi(-50-10j) (5.236047850352252236372551e+29 - 3.08254224233701381482228e+29j) >>> scorergi(100000j) (-8.806659285336231052679025e+6474077 + 8.684731303500835514850962e+6474077j) Verifying the connection between Gi and Hi:: >>> z = 0.25 >>> scorergi(z) + scorerhi(z) 0.7287469039362150078694543 >>> airybi(z) 0.7287469039362150078694543 Verifying the differential equation:: >>> for z in [-3.4, 0, 2.5, 1+2j]: ... chop(diff(scorergi,z,2) - z*scorergi(z)) ... -0.3183098861837906715377675 -0.3183098861837906715377675 -0.3183098861837906715377675 -0.3183098861837906715377675 Verifying the integral representation:: >>> z = 0.5 >>> scorergi(z) 0.2447210432765581976910539 >>> Ai,Bi = airyai,airybi >>> Bi(z)*(Ai(inf,-1)-Ai(z,-1)) + Ai(z)*(Bi(z,-1)-Bi(0,-1)) 0.2447210432765581976910539 **References** 1. [DLMF]_ section 9.12: Scorer Functions """ scorerhi = r""" Evaluates the second Scorer function .. math :: \operatorname{Hi}(z) = \operatorname{Bi}(z) \int_{-\infty}^z \operatorname{Ai}(t) dt - \operatorname{Ai}(z) \int_{-\infty}^z \operatorname{Bi}(t) dt which gives a particular solution to the inhomogeneous Airy differential equation `f''(z) - z f(z) = 1/\pi`. See also :func:`~mpmath.scorergi`. **Plots** .. literalinclude :: /modules/mpmath/plots/hi.py .. image :: /modules/mpmath/plots/hi.png .. literalinclude :: /modules/mpmath/plots/hi_c.py .. image :: /modules/mpmath/plots/hi_c.png **Examples** Some values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> scorerhi(0); 2/(power(3,'7/6')*gamma('2/3')) 0.4099510849640004901006149 0.4099510849640004901006149 >>> diff(scorerhi,0); 2/(power(3,'5/6')*gamma('1/3')) 0.2988589049025509052765491 0.2988589049025509052765491 >>> scorerhi(+inf); scorerhi(-inf) +inf 0.0 >>> scorerhi(1) 0.9722051551424333218376886 >>> scorerhi(-1) 0.2206696067929598945381098 Evaluation for large arguments:: >>> scorerhi(10) 455641153.5163291358991077 >>> scorerhi(100) 6.041223996670201399005265e+288 >>> scorerhi(1000000) 7.138269638197858094311122e+289529652 >>> scorerhi(-10) 0.0317685352825022727415011 >>> scorerhi(-100) 0.003183092495767499864680483 >>> scorerhi(100j) (-6.366197716545672122983857e-9 + 0.003183098861710582761688475j) >>> scorerhi(50+50j) (-5.322076267321435669290334e+63 + 1.478450291165243789749427e+65j) >>> scorerhi(-1000-1000j) (0.0001591549432510502796565538 - 0.000159154943091895334973109j) Verifying the differential equation:: >>> for z in [-3.4, 0, 2, 1+2j]: ... chop(diff(scorerhi,z,2) - z*scorerhi(z)) ... 0.3183098861837906715377675 0.3183098861837906715377675 0.3183098861837906715377675 0.3183098861837906715377675 Verifying the integral representation:: >>> z = 0.5 >>> scorerhi(z) 0.6095559998265972956089949 >>> Ai,Bi = airyai,airybi >>> Bi(z)*(Ai(z,-1)-Ai(-inf,-1)) - Ai(z)*(Bi(z,-1)-Bi(-inf,-1)) 0.6095559998265972956089949 """ sympy-0.7.4.1/sympy/mpmath/tests/0000755000175000017500000000000012253362407017055 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/mpmath/tests/test_basic_ops.py0000644000175000017500000003544712253362407022445 0ustar georgeskgeorgeskimport sympy.mpmath from sympy.mpmath import * from sympy.mpmath.libmp import * import random import sys try: long = long except NameError: long = int def test_type_compare(): assert mpf(2) == mpc(2,0) assert mpf(0) == mpc(0) assert mpf(2) != mpc(2, 0.00001) assert mpf(2) == 2.0 assert mpf(2) != 3.0 assert mpf(2) == 2 assert mpf(2) != '2.0' assert mpc(2) != '2.0' def test_add(): assert mpf(2.5) + mpf(3) == 5.5 assert mpf(2.5) + 3 == 5.5 assert mpf(2.5) + 3.0 == 5.5 assert 3 + mpf(2.5) == 5.5 assert 3.0 + mpf(2.5) == 5.5 assert (3+0j) + mpf(2.5) == 5.5 assert mpc(2.5) + mpf(3) == 5.5 assert mpc(2.5) + 3 == 5.5 assert mpc(2.5) + 3.0 == 5.5 assert mpc(2.5) + (3+0j) == 5.5 assert 3 + mpc(2.5) == 5.5 assert 3.0 + mpc(2.5) == 5.5 assert (3+0j) + mpc(2.5) == 5.5 def test_sub(): assert mpf(2.5) - mpf(3) == -0.5 assert mpf(2.5) - 3 == -0.5 assert mpf(2.5) - 3.0 == -0.5 assert 3 - mpf(2.5) == 0.5 assert 3.0 - mpf(2.5) == 0.5 assert (3+0j) - mpf(2.5) == 0.5 assert mpc(2.5) - mpf(3) == -0.5 assert mpc(2.5) - 3 == -0.5 assert mpc(2.5) - 3.0 == -0.5 assert mpc(2.5) - (3+0j) == -0.5 assert 3 - mpc(2.5) == 0.5 assert 3.0 - mpc(2.5) == 0.5 assert (3+0j) - mpc(2.5) == 0.5 def test_mul(): assert mpf(2.5) * mpf(3) == 7.5 assert mpf(2.5) * 3 == 7.5 assert mpf(2.5) * 3.0 == 7.5 assert 3 * mpf(2.5) == 7.5 assert 3.0 * mpf(2.5) == 7.5 assert (3+0j) * mpf(2.5) == 7.5 assert mpc(2.5) * mpf(3) == 7.5 assert mpc(2.5) * 3 == 7.5 assert mpc(2.5) * 3.0 == 7.5 assert mpc(2.5) * (3+0j) == 7.5 assert 3 * mpc(2.5) == 7.5 assert 3.0 * mpc(2.5) == 7.5 assert (3+0j) * mpc(2.5) == 7.5 def test_div(): assert mpf(6) / mpf(3) == 2.0 assert mpf(6) / 3 == 2.0 assert mpf(6) / 3.0 == 2.0 assert 6 / mpf(3) == 2.0 assert 6.0 / mpf(3) == 2.0 assert (6+0j) / mpf(3.0) == 2.0 assert mpc(6) / mpf(3) == 2.0 assert mpc(6) / 3 == 2.0 assert mpc(6) / 3.0 == 2.0 assert mpc(6) / (3+0j) == 2.0 assert 6 / mpc(3) == 2.0 assert 6.0 / mpc(3) == 2.0 assert (6+0j) / mpc(3) == 2.0 def test_pow(): assert mpf(6) ** mpf(3) == 216.0 assert mpf(6) ** 3 == 216.0 assert mpf(6) ** 3.0 == 216.0 assert 6 ** mpf(3) == 216.0 assert 6.0 ** mpf(3) == 216.0 assert (6+0j) ** mpf(3.0) == 216.0 assert mpc(6) ** mpf(3) == 216.0 assert mpc(6) ** 3 == 216.0 assert mpc(6) ** 3.0 == 216.0 assert mpc(6) ** (3+0j) == 216.0 assert 6 ** mpc(3) == 216.0 assert 6.0 ** mpc(3) == 216.0 assert (6+0j) ** mpc(3) == 216.0 def test_mixed_misc(): assert 1 + mpf(3) == mpf(3) + 1 == 4 assert 1 - mpf(3) == -(mpf(3) - 1) == -2 assert 3 * mpf(2) == mpf(2) * 3 == 6 assert 6 / mpf(2) == mpf(6) / 2 == 3 assert 1.0 + mpf(3) == mpf(3) + 1.0 == 4 assert 1.0 - mpf(3) == -(mpf(3) - 1.0) == -2 assert 3.0 * mpf(2) == mpf(2) * 3.0 == 6 assert 6.0 / mpf(2) == mpf(6) / 2.0 == 3 def test_add_misc(): mp.dps = 15 assert mpf(4) + mpf(-70) == -66 assert mpf(1) + mpf(1.1)/80 == 1 + 1.1/80 assert mpf((1, 10000000000)) + mpf(3) == mpf((1, 10000000000)) assert mpf(3) + mpf((1, 10000000000)) == mpf((1, 10000000000)) assert mpf((1, -10000000000)) + mpf(3) == mpf(3) assert mpf(3) + mpf((1, -10000000000)) == mpf(3) assert mpf(1) + 1e-15 != 1 assert mpf(1) + 1e-20 == 1 assert mpf(1.07e-22) + 0 == mpf(1.07e-22) assert mpf(0) + mpf(1.07e-22) == mpf(1.07e-22) def test_complex_misc(): # many more tests needed assert 1 + mpc(2) == 3 assert not mpc(2).ae(2 + 1e-13) assert mpc(2+1e-15j).ae(2) def test_complex_zeros(): for a in [0,2]: for b in [0,3]: for c in [0,4]: for d in [0,5]: assert mpc(a,b)*mpc(c,d) == complex(a,b)*complex(c,d) def test_hash(): for i in range(-256, 256): assert hash(mpf(i)) == hash(i) assert hash(mpf(0.5)) == hash(0.5) assert hash(mpc(2,3)) == hash(2+3j) # Check that this doesn't fail assert hash(inf) # Check that overflow doesn't assign equal hashes to large numbers assert hash(mpf('1e1000')) != hash('1e10000') assert hash(mpc(100,'1e1000')) != hash(mpc(200,'1e1000')) from sympy.mpmath.rational import mpq assert hash(mp.mpq(1,3)) assert hash(mp.mpq(0,1)) == 0 assert hash(mp.mpq(-1,1)) == hash(-1) assert hash(mp.mpq(1,1)) == hash(1) assert hash(mp.mpq(5,1)) == hash(5) assert hash(mp.mpq(1,2)) == hash(0.5) if sys.version >= "3.2": assert hash(mpf(1)*2**2000) == hash(2**2000) assert hash(mpf(1)/2**2000) == hash(mpq(1,2**2000)) # Advanced rounding test def test_add_rounding(): mp.dps = 15 a = from_float(1e-50) assert mpf_sub(mpf_add(fone, a, 53, round_up), fone, 53, round_up) == from_float(2.2204460492503131e-16) assert mpf_sub(fone, a, 53, round_up) == fone assert mpf_sub(fone, mpf_sub(fone, a, 53, round_down), 53, round_down) == from_float(1.1102230246251565e-16) assert mpf_add(fone, a, 53, round_down) == fone def test_almost_equal(): assert mpf(1.2).ae(mpf(1.20000001), 1e-7) assert not mpf(1.2).ae(mpf(1.20000001), 1e-9) assert not mpf(-0.7818314824680298).ae(mpf(-0.774695868667929)) def test_arithmetic_functions(): import operator ops = [(operator.add, fadd), (operator.sub, fsub), (operator.mul, fmul), (operator.truediv, fdiv)] a = mpf(0.27) b = mpf(1.13) c = mpc(0.51+2.16j) d = mpc(1.08-0.99j) for x in [a,b,c,d]: for y in [a,b,c,d]: for op, fop in ops: if fop is not fdiv: mp.prec = 200 z0 = op(x,y) mp.prec = 60 z1 = op(x,y) mp.prec = 53 z2 = op(x,y) assert fop(x, y, prec=60) == z1 assert fop(x, y) == z2 if fop is not fdiv: assert fop(x, y, prec=inf) == z0 assert fop(x, y, dps=inf) == z0 assert fop(x, y, exact=True) == z0 assert fneg(fneg(z1, exact=True), prec=inf) == z1 assert fneg(z1) == -(+z1) mp.dps = 15 def test_exact_integer_arithmetic(): # XXX: re-fix this so that all operations are tested with all rounding modes random.seed(0) for prec in [6, 10, 25, 40, 100, 250, 725]: for rounding in ['d', 'u', 'f', 'c', 'n']: mp.dps = prec M = 10**(prec-2) M2 = 10**(prec//2-2) for i in range(10): a = random.randint(-M, M) b = random.randint(-M, M) assert mpf(a, rounding=rounding) == a assert int(mpf(a, rounding=rounding)) == a assert int(mpf(str(a), rounding=rounding)) == a assert mpf(a) + mpf(b) == a + b assert mpf(a) - mpf(b) == a - b assert -mpf(a) == -a a = random.randint(-M2, M2) b = random.randint(-M2, M2) assert mpf(a) * mpf(b) == a*b assert mpf_mul(from_int(a), from_int(b), mp.prec, rounding) == from_int(a*b) mp.dps = 15 def test_odd_int_bug(): assert to_int(from_int(3), round_nearest) == 3 def test_str_1000_digits(): mp.dps = 1001 # last digit may be wrong assert str(mpf(2)**0.5)[-10:-1] == '9518488472'[:9] assert str(pi)[-10:-1] == '2164201989'[:9] mp.dps = 15 def test_str_10000_digits(): mp.dps = 10001 # last digit may be wrong assert str(mpf(2)**0.5)[-10:-1] == '5873258351'[:9] assert str(pi)[-10:-1] == '5256375678'[:9] mp.dps = 15 def test_monitor(): f = lambda x: x**2 a = [] b = [] g = monitor(f, a.append, b.append) assert g(3) == 9 assert g(4) == 16 assert a[0] == ((3,), {}) assert b[0] == 9 def test_nint_distance(): assert nint_distance(mpf(-3)) == (-3, -inf) assert nint_distance(mpc(-3)) == (-3, -inf) assert nint_distance(mpf(-3.1)) == (-3, -3) assert nint_distance(mpf(-3.01)) == (-3, -6) assert nint_distance(mpf(-3.001)) == (-3, -9) assert nint_distance(mpf(-3.0001)) == (-3, -13) assert nint_distance(mpf(-2.9)) == (-3, -3) assert nint_distance(mpf(-2.99)) == (-3, -6) assert nint_distance(mpf(-2.999)) == (-3, -9) assert nint_distance(mpf(-2.9999)) == (-3, -13) assert nint_distance(mpc(-3+0.1j)) == (-3, -3) assert nint_distance(mpc(-3+0.01j)) == (-3, -6) assert nint_distance(mpc(-3.1+0.1j)) == (-3, -3) assert nint_distance(mpc(-3.01+0.01j)) == (-3, -6) assert nint_distance(mpc(-3.001+0.001j)) == (-3, -9) assert nint_distance(mpf(0)) == (0, -inf) assert nint_distance(mpf(0.01)) == (0, -6) assert nint_distance(mpf('1e-100')) == (0, -332) def test_floor_ceil_nint_frac(): mp.dps = 15 for n in range(-10,10): assert floor(n) == n assert floor(n+0.5) == n assert ceil(n) == n assert ceil(n+0.5) == n+1 assert nint(n) == n # nint rounds to even if n % 2 == 1: assert nint(n+0.5) == n+1 else: assert nint(n+0.5) == n assert floor(inf) == inf assert floor(ninf) == ninf assert isnan(floor(nan)) assert ceil(inf) == inf assert ceil(ninf) == ninf assert isnan(ceil(nan)) assert nint(inf) == inf assert nint(ninf) == ninf assert isnan(nint(nan)) assert floor(0.1) == 0 assert floor(0.9) == 0 assert floor(-0.1) == -1 assert floor(-0.9) == -1 assert floor(10000000000.1) == 10000000000 assert floor(10000000000.9) == 10000000000 assert floor(-10000000000.1) == -10000000000-1 assert floor(-10000000000.9) == -10000000000-1 assert floor(1e-100) == 0 assert floor(-1e-100) == -1 assert floor(1e100) == 1e100 assert floor(-1e100) == -1e100 assert ceil(0.1) == 1 assert ceil(0.9) == 1 assert ceil(-0.1) == 0 assert ceil(-0.9) == 0 assert ceil(10000000000.1) == 10000000000+1 assert ceil(10000000000.9) == 10000000000+1 assert ceil(-10000000000.1) == -10000000000 assert ceil(-10000000000.9) == -10000000000 assert ceil(1e-100) == 1 assert ceil(-1e-100) == 0 assert ceil(1e100) == 1e100 assert ceil(-1e100) == -1e100 assert nint(0.1) == 0 assert nint(0.9) == 1 assert nint(-0.1) == 0 assert nint(-0.9) == -1 assert nint(10000000000.1) == 10000000000 assert nint(10000000000.9) == 10000000000+1 assert nint(-10000000000.1) == -10000000000 assert nint(-10000000000.9) == -10000000000-1 assert nint(1e-100) == 0 assert nint(-1e-100) == 0 assert nint(1e100) == 1e100 assert nint(-1e100) == -1e100 assert floor(3.2+4.6j) == 3+4j assert ceil(3.2+4.6j) == 4+5j assert nint(3.2+4.6j) == 3+5j for n in range(-10,10): assert frac(n) == 0 assert frac(0.25) == 0.25 assert frac(1.25) == 0.25 assert frac(2.25) == 0.25 assert frac(-0.25) == 0.75 assert frac(-1.25) == 0.75 assert frac(-2.25) == 0.75 assert frac('1e100000000000000') == 0 u = mpf('1e-100000000000000') assert frac(u) == u assert frac(-u) == 1 # rounding! u = mpf('1e-400') assert frac(-u, prec=0) == fsub(1, u, exact=True) assert frac(3.25+4.75j) == 0.25+0.75j def test_isnan_etc(): from sympy.mpmath.rational import mpq assert isnan(nan) == True assert isnan(3) == False assert isnan(mpf(3)) == False assert isnan(inf) == False assert isnan(mpc(2,nan)) == True assert isnan(mpc(2,nan)) == True assert isnan(mpc(nan,nan)) == True assert isnan(mpc(2,2)) == False assert isnan(mpc(nan,inf)) == True assert isnan(mpc(inf,inf)) == False assert isnan(mpq((3,2))) == False assert isnan(mpq((0,1))) == False assert isinf(inf) == True assert isinf(-inf) == True assert isinf(3) == False assert isinf(nan) == False assert isinf(3+4j) == False assert isinf(mpc(inf)) == True assert isinf(mpc(3,inf)) == True assert isinf(mpc(inf,3)) == True assert isinf(mpc(inf,inf)) == True assert isinf(mpc(nan,inf)) == True assert isinf(mpc(inf,nan)) == True assert isinf(mpc(nan,nan)) == False assert isinf(mpq((3,2))) == False assert isinf(mpq((0,1))) == False assert isnormal(3) == True assert isnormal(3.5) == True assert isnormal(mpf(3.5)) == True assert isnormal(0) == False assert isnormal(mpf(0)) == False assert isnormal(0.0) == False assert isnormal(inf) == False assert isnormal(-inf) == False assert isnormal(nan) == False assert isnormal(float(inf)) == False assert isnormal(mpc(0,0)) == False assert isnormal(mpc(3,0)) == True assert isnormal(mpc(0,3)) == True assert isnormal(mpc(3,3)) == True assert isnormal(mpc(0,nan)) == False assert isnormal(mpc(0,inf)) == False assert isnormal(mpc(3,nan)) == False assert isnormal(mpc(3,inf)) == False assert isnormal(mpc(3,-inf)) == False assert isnormal(mpc(nan,0)) == False assert isnormal(mpc(inf,0)) == False assert isnormal(mpc(nan,3)) == False assert isnormal(mpc(inf,3)) == False assert isnormal(mpc(inf,nan)) == False assert isnormal(mpc(nan,inf)) == False assert isnormal(mpc(nan,nan)) == False assert isnormal(mpc(inf,inf)) == False assert isnormal(mpq((3,2))) == True assert isnormal(mpq((0,1))) == False assert isint(3) == True assert isint(0) == True assert isint(long(3)) == True assert isint(long(0)) == True assert isint(mpf(3)) == True assert isint(mpf(0)) == True assert isint(mpf(-3)) == True assert isint(mpf(3.2)) == False assert isint(3.2) == False assert isint(nan) == False assert isint(inf) == False assert isint(-inf) == False assert isint(mpc(0)) == True assert isint(mpc(3)) == True assert isint(mpc(3.2)) == False assert isint(mpc(3,inf)) == False assert isint(mpc(inf)) == False assert isint(mpc(3,2)) == False assert isint(mpc(0,2)) == False assert isint(mpc(3,2),gaussian=True) == True assert isint(mpc(3,0),gaussian=True) == True assert isint(mpc(0,3),gaussian=True) == True assert isint(3+4j) == False assert isint(3+4j, gaussian=True) == True assert isint(3+0j) == True assert isint(mpq((3,2))) == False assert isint(mpq((3,9))) == False assert isint(mpq((9,3))) == True assert isint(mpq((0,4))) == True assert isint(mpq((1,1))) == True assert isint(mpq((-1,1))) == True assert mp.isnpint(0) == True assert mp.isnpint(1) == False assert mp.isnpint(-1) == True assert mp.isnpint(-1.1) == False assert mp.isnpint(-1.0) == True assert mp.isnpint(mp.mpq(1,2)) == False assert mp.isnpint(mp.mpq(-1,2)) == False assert mp.isnpint(mp.mpq(-3,1)) == True assert mp.isnpint(mp.mpq(0,1)) == True assert mp.isnpint(mp.mpq(1,1)) == False assert mp.isnpint(0+0j) == True assert mp.isnpint(-1+0j) == True assert mp.isnpint(-1.1+0j) == False assert mp.isnpint(-1+0.1j) == False assert mp.isnpint(0+0.1j) == False sympy-0.7.4.1/sympy/mpmath/tests/test_special.py0000644000175000017500000000544612253362407022117 0ustar georgeskgeorgeskfrom sympy.mpmath import * def test_special(): assert inf == inf assert inf != -inf assert -inf == -inf assert inf != nan assert nan != nan assert isnan(nan) assert --inf == inf assert abs(inf) == inf assert abs(-inf) == inf assert abs(nan) != abs(nan) assert isnan(inf - inf) assert isnan(inf + (-inf)) assert isnan(-inf - (-inf)) assert isnan(inf + nan) assert isnan(-inf + nan) assert mpf(2) + inf == inf assert 2 + inf == inf assert mpf(2) - inf == -inf assert 2 - inf == -inf assert inf > 3 assert 3 < inf assert 3 > -inf assert -inf < 3 assert inf > mpf(3) assert mpf(3) < inf assert mpf(3) > -inf assert -inf < mpf(3) assert not (nan < 3) assert not (nan > 3) assert isnan(inf * 0) assert isnan(-inf * 0) assert inf * 3 == inf assert inf * -3 == -inf assert -inf * 3 == -inf assert -inf * -3 == inf assert inf * inf == inf assert -inf * -inf == inf assert isnan(nan / 3) assert inf / -3 == -inf assert inf / 3 == inf assert 3 / inf == 0 assert -3 / inf == 0 assert 0 / inf == 0 assert isnan(inf / inf) assert isnan(inf / -inf) assert isnan(inf / nan) assert mpf('inf') == mpf('+inf') == inf assert mpf('-inf') == -inf assert isnan(mpf('nan')) assert isinf(inf) assert isinf(-inf) assert not isinf(mpf(0)) assert not isinf(nan) def test_special_powers(): assert inf**3 == inf assert isnan(inf**0) assert inf**-3 == 0 assert (-inf)**2 == inf assert (-inf)**3 == -inf assert isnan((-inf)**0) assert (-inf)**-2 == 0 assert (-inf)**-3 == 0 assert isnan(nan**5) assert isnan(nan**0) def test_functions_special(): assert exp(inf) == inf assert exp(-inf) == 0 assert isnan(exp(nan)) assert log(inf) == inf assert isnan(log(nan)) assert isnan(sin(inf)) assert isnan(sin(nan)) assert atan(inf).ae(pi/2) assert atan(-inf).ae(-pi/2) assert isnan(sqrt(nan)) assert sqrt(inf) == inf def test_convert_special(): float_inf = 1e300 * 1e300 float_ninf = -float_inf float_nan = float_inf/float_ninf assert mpf(3) * float_inf == inf assert mpf(3) * float_ninf == -inf assert isnan(mpf(3) * float_nan) assert not (mpf(3) < float_nan) assert not (mpf(3) > float_nan) assert not (mpf(3) <= float_nan) assert not (mpf(3) >= float_nan) assert float(mpf('1e1000')) == float_inf assert float(mpf('-1e1000')) == float_ninf assert float(mpf('1e100000000000000000')) == float_inf assert float(mpf('-1e100000000000000000')) == float_ninf assert float(mpf('1e-100000000000000000')) == 0.0 def test_div_bug(): assert isnan(nan/1) assert isnan(nan/2) assert inf/2 == inf assert (-inf)/2 == -inf sympy-0.7.4.1/sympy/mpmath/tests/test_hp.py0000644000175000017500000002432012253362407021076 0ustar georgeskgeorgesk""" Check that the output from irrational functions is accurate for high-precision input, from 5 to 200 digits. The reference values were verified with Mathematica. """ import time from sympy.mpmath import * precs = [5, 15, 28, 35, 57, 80, 100, 150, 200] # sqrt(3) + pi/2 a = \ "3.302847134363773912758768033145623809041389953497933538543279275605"\ "841220051904536395163599428307109666700184672047856353516867399774243594"\ "67433521615861420725323528325327484262075464241255915238845599752675" # e + 1/euler**2 b = \ "5.719681166601007617111261398629939965860873957353320734275716220045750"\ "31474116300529519620938123730851145473473708966080207482581266469342214"\ "824842256999042984813905047895479210702109260221361437411947323431" # sqrt(a) sqrt_a = \ "1.817373691447021556327498239690365674922395036495564333152483422755"\ "144321726165582817927383239308173567921345318453306994746434073691275094"\ "484777905906961689902608644112196725896908619756404253109722911487" # sqrt(a+b*i).real sqrt_abi_real = \ "2.225720098415113027729407777066107959851146508557282707197601407276"\ "89160998185797504198062911768240808839104987021515555650875977724230130"\ "3584116233925658621288393930286871862273400475179312570274423840384" # sqrt(a+b*i).imag sqrt_abi_imag = \ "1.2849057639084690902371581529110949983261182430040898147672052833653668"\ "0629534491275114877090834296831373498336559849050755848611854282001250"\ "1924311019152914021365263161630765255610885489295778894976075186" # log(a) log_a = \ "1.194784864491089550288313512105715261520511949410072046160598707069"\ "4336653155025770546309137440687056366757650909754708302115204338077595203"\ "83005773986664564927027147084436553262269459110211221152925732612" # log(a+b*i).real log_abi_real = \ "1.8877985921697018111624077550443297276844736840853590212962006811663"\ "04949387789489704203167470111267581371396245317618589339274243008242708"\ "014251531496104028712866224020066439049377679709216784954509456421" # log(a+b*i).imag log_abi_imag = \ "1.0471204952840802663567714297078763189256357109769672185219334169734948"\ "4265809854092437285294686651806426649541504240470168212723133326542181"\ "8300136462287639956713914482701017346851009323172531601894918640" # exp(a) exp_a = \ "27.18994224087168661137253262213293847994194869430518354305430976149"\ "382792035050358791398632888885200049857986258414049540376323785711941636"\ "100358982497583832083513086941635049329804685212200507288797531143" # exp(a+b*i).real exp_abi_real = \ "22.98606617170543596386921087657586890620262522816912505151109385026"\ "40160179326569526152851983847133513990281518417211964710397233157168852"\ "4963130831190142571659948419307628119985383887599493378056639916701" # exp(a+b*i).imag exp_abi_imag = \ "-14.523557450291489727214750571590272774669907424478129280902375851196283"\ "3377162379031724734050088565710975758824441845278120105728824497308303"\ "6065619788140201636218705414429933685889542661364184694108251449" # a**b pow_a_b = \ "928.7025342285568142947391505837660251004990092821305668257284426997"\ "361966028275685583421197860603126498884545336686124793155581311527995550"\ "580229264427202446131740932666832138634013168125809402143796691154" # (a**(a+b*i)).real pow_a_abi_real = \ "44.09156071394489511956058111704382592976814280267142206420038656267"\ "67707916510652790502399193109819563864568986234654864462095231138500505"\ "8197456514795059492120303477512711977915544927440682508821426093455" # (a**(a+b*i)).imag pow_a_abi_imag = \ "27.069371511573224750478105146737852141664955461266218367212527612279886"\ "9322304536553254659049205414427707675802193810711302947536332040474573"\ "8166261217563960235014674118610092944307893857862518964990092301" # ((a+b*i)**(a+b*i)).real pow_abi_abi_real = \ "-0.15171310677859590091001057734676423076527145052787388589334350524"\ "8084195882019497779202452975350579073716811284169068082670778986235179"\ "0813026562962084477640470612184016755250592698408112493759742219150452"\ # ((a+b*i)**(a+b*i)).imag pow_abi_abi_imag = \ "1.2697592504953448936553147870155987153192995316950583150964099070426"\ "4736837932577176947632535475040521749162383347758827307504526525647759"\ "97547638617201824468382194146854367480471892602963428122896045019902" # sin(a) sin_a = \ "-0.16055653857469062740274792907968048154164433772938156243509084009"\ "38437090841460493108570147191289893388608611542655654723437248152535114"\ "528368009465836614227575701220612124204622383149391870684288862269631" # sin(1000*a) sin_1000a = \ "-0.85897040577443833776358106803777589664322997794126153477060795801"\ "09151695416961724733492511852267067419573754315098042850381158563024337"\ "216458577140500488715469780315833217177634490142748614625281171216863" # sin(a+b*i) sin_abi_real = \ "-24.4696999681556977743346798696005278716053366404081910969773939630"\ "7149215135459794473448465734589287491880563183624997435193637389884206"\ "02151395451271809790360963144464736839412254746645151672423256977064" sin_abi_imag = \ "-150.42505378241784671801405965872972765595073690984080160750785565810981"\ "8314482499135443827055399655645954830931316357243750839088113122816583"\ "7169201254329464271121058839499197583056427233866320456505060735" # cos cos_a = \ "-0.98702664499035378399332439243967038895709261414476495730788864004"\ "05406821549361039745258003422386169330787395654908532996287293003581554"\ "257037193284199198069707141161341820684198547572456183525659969145501" cos_1000a = \ "-0.51202523570982001856195696460663971099692261342827540426136215533"\ "52686662667660613179619804463250686852463876088694806607652218586060613"\ "951310588158830695735537073667299449753951774916401887657320950496820" # tan tan_a = \ "0.162666873675188117341401059858835168007137819495998960250142156848"\ "639654718809412181543343168174807985559916643549174530459883826451064966"\ "7996119428949951351938178809444268785629011625179962457123195557310" tan_abi_real = \ "6.822696615947538488826586186310162599974827139564433912601918442911"\ "1026830824380070400102213741875804368044342309515353631134074491271890"\ "467615882710035471686578162073677173148647065131872116479947620E-6" tan_abi_imag = \ "0.9999795833048243692245661011298447587046967777739649018690797625964167"\ "1446419978852235960862841608081413169601038230073129482874832053357571"\ "62702259309150715669026865777947502665936317953101462202542168429" def test_hp(): for dps in precs: mp.dps = dps + 8 aa = mpf(a) bb = mpf(b) a1000 = 1000*mpf(a) abi = mpc(aa, bb) mp.dps = dps assert (sqrt(3) + pi/2).ae(aa) assert (e + 1/euler**2).ae(bb) assert sqrt(aa).ae(mpf(sqrt_a)) assert sqrt(abi).ae(mpc(sqrt_abi_real, sqrt_abi_imag)) assert log(aa).ae(mpf(log_a)) assert log(abi).ae(mpc(log_abi_real, log_abi_imag)) assert exp(aa).ae(mpf(exp_a)) assert exp(abi).ae(mpc(exp_abi_real, exp_abi_imag)) assert (aa**bb).ae(mpf(pow_a_b)) assert (aa**abi).ae(mpc(pow_a_abi_real, pow_a_abi_imag)) assert (abi**abi).ae(mpc(pow_abi_abi_real, pow_abi_abi_imag)) assert sin(a).ae(mpf(sin_a)) assert sin(a1000).ae(mpf(sin_1000a)) assert sin(abi).ae(mpc(sin_abi_real, sin_abi_imag)) assert cos(a).ae(mpf(cos_a)) assert cos(a1000).ae(mpf(cos_1000a)) assert tan(a).ae(mpf(tan_a)) assert tan(abi).ae(mpc(tan_abi_real, tan_abi_imag)) # check that complex cancellation is avoided so that both # real and imaginary parts have high relative accuracy. # abs_eps should be 0, but has to be set to 1e-205 to pass the # 200-digit case, probably due to slight inaccuracy in the # precomputed input assert (tan(abi).real).ae(mpf(tan_abi_real), abs_eps=1e-205) assert (tan(abi).imag).ae(mpf(tan_abi_imag), abs_eps=1e-205) mp.dps = 460 assert str(log(3))[-20:] == '02166121184001409826' mp.dps = 15 # Since str(a) can differ in the last digit from rounded a, and I want # to compare the last digits of big numbers with the results in Mathematica, # I made this hack to get the last 20 digits of rounded a def last_digits(a): r = repr(a) s = str(a) #dps = mp.dps #mp.dps += 3 m = 10 r = r.replace(s[:-m],'') r = r.replace("mpf('",'').replace("')",'') num0 = 0 for c in r: if c == '0': num0 += 1 else: break b = float(int(r))/10**(len(r) - m) if b >= 10**m - 0.5: raise NotImplementedError n = int(round(b)) sn = str(n) s = s[:-m] + '0'*num0 + sn return s[-20:] # values checked with Mathematica def test_log_hp(): mp.dps = 2000 a = mpf(10)**15000/3 r = log(a) res = last_digits(r) # Mathematica N[Log[10^15000/3], 2000] # ...7443804441768333470331 assert res == '44380444176833347033' # see issue 105 r = log(mpf(3)/2) # Mathematica N[Log[3/2], 2000] # ...69653749808140753263288 res = last_digits(r) assert res == '53749808140753263288' mp.dps = 10000 r = log(2) res = last_digits(r) # Mathematica N[Log[2], 10000] # ...695615913401856601359655561 assert res == '91340185660135965556' r = log(mpf(10)**10/3) res = last_digits(r) # Mathematica N[Log[10^10/3], 10000] # ...587087654020631943060007154 assert res == '54020631943060007154', res r = log(mpf(10)**100/3) res = last_digits(r) # Mathematica N[Log[10^100/3], 10000] # ,,,59246336539088351652334666 assert res == '36539088351652334666', res mp.dps += 10 a = 1 - mpf(1)/10**10 mp.dps -= 10 r = log(a) res = last_digits(r) # ...3310334360482956137216724048322957404 # 372167240483229574038733026370 # Mathematica N[Log[1 - 10^-10]*10^10, 10000] # ...60482956137216724048322957404 assert res == '37216724048322957404', res mp.dps = 10000 mp.dps += 100 a = 1 + mpf(1)/10**100 mp.dps -= 100 r = log(a) res = last_digits(+r) # Mathematica N[Log[1 + 10^-100]*10^10, 10030] # ...3994733877377412241546890854692521568292338268273 10^-91 assert res == '39947338773774122415', res mp.dps = 15 def test_exp_hp(): mp.dps = 4000 r = exp(mpf(1)/10) # IntegerPart[N[Exp[1/10] * 10^4000, 4000]] # ...92167105162069688129 assert int(r * 10**mp.dps) % 10**20 == 92167105162069688129 sympy-0.7.4.1/sympy/mpmath/tests/test_division.py0000644000175000017500000001236612253362407022322 0ustar georgeskgeorgeskfrom sympy.mpmath.libmp import * from sympy.mpmath import mpf, mp from random import randint, choice, seed all_modes = [round_floor, round_ceiling, round_down, round_up, round_nearest] fb = from_bstr fi = from_int ff = from_float def test_div_1_3(): a = fi(1) b = fi(3) c = fi(-1) # floor rounds down, ceiling rounds up assert mpf_div(a, b, 7, round_floor) == fb('0.01010101') assert mpf_div(a, b, 7, round_ceiling) == fb('0.01010110') assert mpf_div(a, b, 7, round_down) == fb('0.01010101') assert mpf_div(a, b, 7, round_up) == fb('0.01010110') assert mpf_div(a, b, 7, round_nearest) == fb('0.01010101') # floor rounds up, ceiling rounds down assert mpf_div(c, b, 7, round_floor) == fb('-0.01010110') assert mpf_div(c, b, 7, round_ceiling) == fb('-0.01010101') assert mpf_div(c, b, 7, round_down) == fb('-0.01010101') assert mpf_div(c, b, 7, round_up) == fb('-0.01010110') assert mpf_div(c, b, 7, round_nearest) == fb('-0.01010101') def test_mpf_divi_1_3(): a = 1 b = fi(3) c = -1 assert mpf_rdiv_int(a, b, 7, round_floor) == fb('0.01010101') assert mpf_rdiv_int(a, b, 7, round_ceiling) == fb('0.01010110') assert mpf_rdiv_int(a, b, 7, round_down) == fb('0.01010101') assert mpf_rdiv_int(a, b, 7, round_up) == fb('0.01010110') assert mpf_rdiv_int(a, b, 7, round_nearest) == fb('0.01010101') assert mpf_rdiv_int(c, b, 7, round_floor) == fb('-0.01010110') assert mpf_rdiv_int(c, b, 7, round_ceiling) == fb('-0.01010101') assert mpf_rdiv_int(c, b, 7, round_down) == fb('-0.01010101') assert mpf_rdiv_int(c, b, 7, round_up) == fb('-0.01010110') assert mpf_rdiv_int(c, b, 7, round_nearest) == fb('-0.01010101') def test_div_300(): q = fi(1000000) a = fi(300499999) # a/q is a little less than a half-integer b = fi(300500000) # b/q exactly a half-integer c = fi(300500001) # c/q is a little more than a half-integer # Check nearest integer rounding (prec=9 as 2**8 < 300 < 2**9) assert mpf_div(a, q, 9, round_down) == fi(300) assert mpf_div(b, q, 9, round_down) == fi(300) assert mpf_div(c, q, 9, round_down) == fi(300) assert mpf_div(a, q, 9, round_up) == fi(301) assert mpf_div(b, q, 9, round_up) == fi(301) assert mpf_div(c, q, 9, round_up) == fi(301) # Nearest even integer is down assert mpf_div(a, q, 9, round_nearest) == fi(300) assert mpf_div(b, q, 9, round_nearest) == fi(300) assert mpf_div(c, q, 9, round_nearest) == fi(301) # Nearest even integer is up a = fi(301499999) b = fi(301500000) c = fi(301500001) assert mpf_div(a, q, 9, round_nearest) == fi(301) assert mpf_div(b, q, 9, round_nearest) == fi(302) assert mpf_div(c, q, 9, round_nearest) == fi(302) def test_tight_integer_division(): # Test that integer division at tightest possible precision is exact N = 100 seed(1) for i in range(N): a = choice([1, -1]) * randint(1, 1< 10000.0 assert from_man_exp(0xf9, -4, 4, round_nearest)[:3] == (0, 1, 4) # 1111.1001 -> 10000.0 assert from_man_exp(0xe8, -4, 4, round_nearest)[:3] == (0, 7, 1) # 1110.1000 -> 1110.0 assert from_man_exp(0xe9, -4, 4, round_nearest)[:3] == (0, 15, 0) # 1110.1001 -> 1111.0 assert from_man_exp(-0xf0, -4, 4, round_nearest)[:3] == (1, 15, 0) assert from_man_exp(-0xf7, -4, 4, round_nearest)[:3] == (1, 15, 0) assert from_man_exp(-0xf8, -4, 4, round_nearest)[:3] == (1, 1, 4) assert from_man_exp(-0xf9, -4, 4, round_nearest)[:3] == (1, 1, 4) assert from_man_exp(-0xe8, -4, 4, round_nearest)[:3] == (1, 7, 1) assert from_man_exp(-0xe9, -4, 4, round_nearest)[:3] == (1, 15, 0) def test_rounding_bugs(): # 1 less than power-of-two cases assert from_man_exp(72057594037927935, -56, 53, round_up) == (0, 1, 0, 1) assert from_man_exp(73786976294838205979, -65, 53, round_nearest) == (0, 1, 1, 1) assert from_man_exp(31, 0, 4, round_up) == (0, 1, 5, 1) assert from_man_exp(-31, 0, 4, round_floor) == (1, 1, 5, 1) assert from_man_exp(255, 0, 7, round_up) == (0, 1, 8, 1) assert from_man_exp(-255, 0, 7, round_floor) == (1, 1, 8, 1) def test_rounding_issue160(): a = from_man_exp(9867,-100) b = from_man_exp(9867,-200) c = from_man_exp(-1,0) z = (1, 1023, -10, 10) assert mpf_add(a, c, 10, 'd') == z assert mpf_add(b, c, 10, 'd') == z assert mpf_add(c, a, 10, 'd') == z assert mpf_add(c, b, 10, 'd') == z def test_perturb(): a = fone b = from_float(0.99999999999999989) c = from_float(1.0000000000000002) assert mpf_perturb(a, 0, 53, round_nearest) == a assert mpf_perturb(a, 1, 53, round_nearest) == a assert mpf_perturb(a, 0, 53, round_up) == c assert mpf_perturb(a, 0, 53, round_ceiling) == c assert mpf_perturb(a, 0, 53, round_down) == a assert mpf_perturb(a, 0, 53, round_floor) == a assert mpf_perturb(a, 1, 53, round_up) == a assert mpf_perturb(a, 1, 53, round_ceiling) == a assert mpf_perturb(a, 1, 53, round_down) == b assert mpf_perturb(a, 1, 53, round_floor) == b a = mpf_neg(a) b = mpf_neg(b) c = mpf_neg(c) assert mpf_perturb(a, 0, 53, round_nearest) == a assert mpf_perturb(a, 1, 53, round_nearest) == a assert mpf_perturb(a, 0, 53, round_up) == a assert mpf_perturb(a, 0, 53, round_floor) == a assert mpf_perturb(a, 0, 53, round_down) == b assert mpf_perturb(a, 0, 53, round_ceiling) == b assert mpf_perturb(a, 1, 53, round_up) == c assert mpf_perturb(a, 1, 53, round_floor) == c assert mpf_perturb(a, 1, 53, round_down) == a assert mpf_perturb(a, 1, 53, round_ceiling) == a def test_add_exact(): ff = from_float assert mpf_add(ff(3.0), ff(2.5)) == ff(5.5) assert mpf_add(ff(3.0), ff(-2.5)) == ff(0.5) assert mpf_add(ff(-3.0), ff(2.5)) == ff(-0.5) assert mpf_add(ff(-3.0), ff(-2.5)) == ff(-5.5) assert mpf_sub(mpf_add(fone, ff(1e-100)), fone) == ff(1e-100) assert mpf_sub(mpf_add(ff(1e-100), fone), fone) == ff(1e-100) assert mpf_sub(mpf_add(fone, ff(-1e-100)), fone) == ff(-1e-100) assert mpf_sub(mpf_add(ff(-1e-100), fone), fone) == ff(-1e-100) assert mpf_add(fone, fzero) == fone assert mpf_add(fzero, fone) == fone assert mpf_add(fzero, fzero) == fzero def test_long_exponent_shifts(): mp.dps = 15 # Check for possible bugs due to exponent arithmetic overflow # in a C implementation x = mpf(1) for p in [32, 64]: a = ldexp(1,2**(p-1)) b = ldexp(1,2**p) c = ldexp(1,2**(p+1)) d = ldexp(1,-2**(p-1)) e = ldexp(1,-2**p) f = ldexp(1,-2**(p+1)) assert (x+a) == a assert (x+b) == b assert (x+c) == c assert (x+d) == x assert (x+e) == x assert (x+f) == x assert (a+x) == a assert (b+x) == b assert (c+x) == c assert (d+x) == x assert (e+x) == x assert (f+x) == x assert (x-a) == -a assert (x-b) == -b assert (x-c) == -c assert (x-d) == x assert (x-e) == x assert (x-f) == x assert (a-x) == a assert (b-x) == b assert (c-x) == c assert (d-x) == -x assert (e-x) == -x assert (f-x) == -x sympy-0.7.4.1/sympy/mpmath/tests/test_pickle.py0000644000175000017500000000067112253362407021741 0ustar georgeskgeorgeskfrom __future__ import with_statement import os import tempfile import pickle from sympy.mpmath import * def pickler(obj): fn = tempfile.mktemp() with open(fn, 'wb') as f: pickle.dump(obj, f) with open(fn, 'rb') as f: obj2 = pickle.load(f) os.remove(fn) return obj2 def test_pickle(): obj = mpf('0.5') assert obj == pickler(obj) obj = mpc('0.5','0.2') assert obj == pickler(obj) sympy-0.7.4.1/sympy/mpmath/tests/extratest_bessel.py0000644000175000017500000005042612253362407023016 0ustar georgeskgeorgesk# Extra stress testing for Bessel functions # Reference zeros generated with the aid of scipy.special # jn_zero, jnp_zero, yn_zero, ynp_zero from mpmath import * V = 15 M = 15 jn_small_zeros = \ [[2.4048255576957728, 5.5200781102863106, 8.6537279129110122, 11.791534439014282, 14.930917708487786, 18.071063967910923, 21.211636629879259, 24.352471530749303, 27.493479132040255, 30.634606468431975, 33.775820213573569, 36.917098353664044, 40.058425764628239, 43.19979171317673, 46.341188371661814], [3.8317059702075123, 7.0155866698156188, 10.173468135062722, 13.323691936314223, 16.470630050877633, 19.615858510468242, 22.760084380592772, 25.903672087618383, 29.046828534916855, 32.189679910974404, 35.332307550083865, 38.474766234771615, 41.617094212814451, 44.759318997652822, 47.901460887185447], [5.1356223018406826, 8.4172441403998649, 11.619841172149059, 14.795951782351261, 17.959819494987826, 21.116997053021846, 24.270112313573103, 27.420573549984557, 30.569204495516397, 33.7165195092227, 36.86285651128381, 40.008446733478192, 43.153453778371463, 46.297996677236919, 49.442164110416873], [6.3801618959239835, 9.7610231299816697, 13.015200721698434, 16.223466160318768, 19.409415226435012, 22.582729593104442, 25.748166699294978, 28.908350780921758, 32.064852407097709, 35.218670738610115, 38.370472434756944, 41.520719670406776, 44.669743116617253, 47.817785691533302, 50.965029906205183], [7.5883424345038044, 11.064709488501185, 14.37253667161759, 17.615966049804833, 20.826932956962388, 24.01901952477111, 27.199087765981251, 30.371007667117247, 33.537137711819223, 36.699001128744649, 39.857627302180889, 43.01373772335443, 46.167853512924375, 49.320360686390272, 52.471551398458023], [8.771483815959954, 12.338604197466944, 15.700174079711671, 18.980133875179921, 22.217799896561268, 25.430341154222704, 28.626618307291138, 31.811716724047763, 34.988781294559295, 38.159868561967132, 41.326383254047406, 44.489319123219673, 47.649399806697054, 50.80716520300633, 53.963026558378149], [9.9361095242176849, 13.589290170541217, 17.003819667816014, 20.320789213566506, 23.58608443558139, 26.820151983411405, 30.033722386570469, 33.233041762847123, 36.422019668258457, 39.603239416075404, 42.778481613199507, 45.949015998042603, 49.11577372476426, 52.279453903601052, 55.440592068853149], [11.086370019245084, 14.821268727013171, 18.287582832481726, 21.641541019848401, 24.934927887673022, 28.191188459483199, 31.42279419226558, 34.637089352069324, 37.838717382853611, 41.030773691585537, 44.21540850526126, 47.394165755570512, 50.568184679795566, 53.738325371963291, 56.905249991978781], [12.225092264004655, 16.037774190887709, 19.554536430997055, 22.94517313187462, 26.266814641176644, 29.54565967099855, 32.795800037341462, 36.025615063869571, 39.240447995178135, 42.443887743273558, 45.638444182199141, 48.825930381553857, 52.007691456686903, 55.184747939289049, 58.357889025269694], [13.354300477435331, 17.241220382489128, 20.807047789264107, 24.233885257750552, 27.583748963573006, 30.885378967696675, 34.154377923855096, 37.400099977156589, 40.628553718964528, 43.843801420337347, 47.048700737654032, 50.245326955305383, 53.435227157042058, 56.619580266508436, 59.799301630960228], [14.475500686554541, 18.433463666966583, 22.046985364697802, 25.509450554182826, 28.887375063530457, 32.211856199712731, 35.499909205373851, 38.761807017881651, 42.004190236671805, 45.231574103535045, 48.447151387269394, 51.653251668165858, 54.851619075963349, 58.043587928232478, 61.230197977292681], [15.589847884455485, 19.61596690396692, 23.275853726263409, 26.773322545509539, 30.17906117878486, 33.526364075588624, 36.833571341894905, 40.111823270954241, 43.368360947521711, 46.608132676274944, 49.834653510396724, 53.050498959135054, 56.257604715114484, 59.457456908388002, 62.651217388202912], [16.698249933848246, 20.789906360078443, 24.494885043881354, 28.026709949973129, 31.45996003531804, 34.829986990290238, 38.156377504681354, 41.451092307939681, 44.721943543191147, 47.974293531269048, 51.211967004101068, 54.437776928325074, 57.653844811906946, 60.8618046824805, 64.062937824850136], [17.801435153282442, 21.95624406783631, 25.705103053924724, 29.270630441874802, 32.731053310978403, 36.123657666448762, 39.469206825243883, 42.780439265447158, 46.06571091157561, 49.330780096443524, 52.579769064383396, 55.815719876305778, 59.040934037249271, 62.257189393731728, 65.465883797232125], [18.899997953174024, 23.115778347252756, 26.907368976182104, 30.505950163896036, 33.993184984781542, 37.408185128639695, 40.772827853501868, 44.100590565798301, 47.400347780543231, 50.678236946479898, 53.93866620912693, 57.184898598119301, 60.419409852130297, 63.644117508962281, 66.860533012260103]] jnp_small_zeros = \ [[0.0, 3.8317059702075123, 7.0155866698156188, 10.173468135062722, 13.323691936314223, 16.470630050877633, 19.615858510468242, 22.760084380592772, 25.903672087618383, 29.046828534916855, 32.189679910974404, 35.332307550083865, 38.474766234771615, 41.617094212814451, 44.759318997652822], [1.8411837813406593, 5.3314427735250326, 8.5363163663462858, 11.706004902592064, 14.863588633909033, 18.015527862681804, 21.16436985918879, 24.311326857210776, 27.457050571059246, 30.601922972669094, 33.746182898667383, 36.889987409236811, 40.033444053350675, 43.176628965448822, 46.319597561173912], [3.0542369282271403, 6.7061331941584591, 9.9694678230875958, 13.170370856016123, 16.347522318321783, 19.512912782488205, 22.671581772477426, 25.826037141785263, 28.977672772993679, 32.127327020443474, 35.275535050674691, 38.422654817555906, 41.568934936074314, 44.714553532819734, 47.859641607992093], [4.2011889412105285, 8.0152365983759522, 11.345924310743006, 14.585848286167028, 17.78874786606647, 20.9724769365377, 24.144897432909265, 27.310057930204349, 30.470268806290424, 33.626949182796679, 36.781020675464386, 39.933108623659488, 43.083652662375079, 46.232971081836478, 49.381300092370349], [5.3175531260839944, 9.2823962852416123, 12.681908442638891, 15.964107037731551, 19.196028800048905, 22.401032267689004, 25.589759681386733, 28.767836217666503, 31.938539340972783, 35.103916677346764, 38.265316987088158, 41.423666498500732, 44.579623137359257, 47.733667523865744, 50.886159153182682], [6.4156163757002403, 10.519860873772308, 13.9871886301403, 17.312842487884625, 20.575514521386888, 23.803581476593863, 27.01030789777772, 30.20284907898166, 33.385443901010121, 36.560777686880356, 39.730640230067416, 42.896273163494417, 46.058566273567043, 49.218174614666636, 52.375591529563596], [7.501266144684147, 11.734935953042708, 15.268181461097873, 18.637443009666202, 21.931715017802236, 25.183925599499626, 28.409776362510085, 31.617875716105035, 34.81339298429743, 37.999640897715301, 41.178849474321413, 44.352579199070217, 47.521956905768113, 50.687817781723741, 53.85079463676896], [8.5778364897140741, 12.932386237089576, 16.529365884366944, 19.941853366527342, 23.268052926457571, 26.545032061823576, 29.790748583196614, 33.015178641375142, 36.224380548787162, 39.422274578939259, 42.611522172286684, 45.793999658055002, 48.971070951900596, 52.143752969301988, 55.312820330403446], [9.6474216519972168, 14.115518907894618, 17.774012366915256, 21.229062622853124, 24.587197486317681, 27.889269427955092, 31.155326556188325, 34.39662855427218, 37.620078044197086, 40.830178681822041, 44.030010337966153, 47.221758471887113, 50.407020967034367, 53.586995435398319, 56.762598475105272], [10.711433970699945, 15.28673766733295, 19.004593537946053, 22.501398726777283, 25.891277276839136, 29.218563499936081, 32.505247352375523, 35.763792928808799, 39.001902811514218, 42.224638430753279, 45.435483097475542, 48.636922645305525, 51.830783925834728, 55.01844255063594, 58.200955824859509], [11.770876674955582, 16.447852748486498, 20.223031412681701, 23.760715860327448, 27.182021527190532, 30.534504754007074, 33.841965775135715, 37.118000423665604, 40.371068905333891, 43.606764901379516, 46.828959446564562, 50.040428970943456, 53.243223214220535, 56.438892058982552, 59.628631306921512], [12.826491228033465, 17.600266557468326, 21.430854238060294, 25.008518704644261, 28.460857279654847, 31.838424458616998, 35.166714427392629, 38.460388720328256, 41.728625562624312, 44.977526250903469, 48.211333836373288, 51.433105171422278, 54.645106240447105, 57.849056857839799, 61.046288512821078], [13.878843069697276, 18.745090916814406, 22.629300302835503, 26.246047773946584, 29.72897816891134, 33.131449953571661, 36.480548302231658, 39.791940718940855, 43.075486800191012, 46.337772104541405, 49.583396417633095, 52.815686826850452, 56.037118687012179, 59.249577075517968, 62.454525995970462], [14.928374492964716, 19.88322436109951, 23.81938909003628, 27.474339750968247, 30.987394331665278, 34.414545662167183, 37.784378506209499, 41.113512376883377, 44.412454519229281, 47.688252845993366, 50.945849245830813, 54.188831071035124, 57.419876154678179, 60.641030026538746, 63.853885828967512], [15.975438807484321, 21.015404934568315, 25.001971500138194, 28.694271223110755, 32.236969407878118, 35.688544091185301, 39.078998185245057, 42.425854432866141, 45.740236776624833, 49.029635055514276, 52.299319390331728, 55.553127779547459, 58.793933759028134, 62.02393848337554, 65.244860767043859]] yn_small_zeros = \ [[0.89357696627916752, 3.9576784193148579, 7.0860510603017727, 10.222345043496417, 13.361097473872763, 16.500922441528091, 19.64130970088794, 22.782028047291559, 25.922957653180923, 29.064030252728398, 32.205204116493281, 35.346452305214321, 38.487756653081537, 41.629104466213808, 44.770486607221993], [2.197141326031017, 5.4296810407941351, 8.5960058683311689, 11.749154830839881, 14.897442128336725, 18.043402276727856, 21.188068934142213, 24.331942571356912, 27.475294980449224, 30.618286491641115, 33.761017796109326, 36.90355531614295, 40.045944640266876, 43.188218097393211, 46.330399250701687], [3.3842417671495935, 6.7938075132682675, 10.023477979360038, 13.209986710206416, 16.378966558947457, 19.539039990286384, 22.69395593890929, 25.845613720902269, 28.995080395650151, 32.143002257627551, 35.289793869635804, 38.435733485446343, 41.581014867297885, 44.725777117640461, 47.870122696676504], [4.5270246611496439, 8.0975537628604907, 11.396466739595867, 14.623077742393873, 17.81845523294552, 20.997284754187761, 24.166235758581828, 27.328799850405162, 30.486989604098659, 33.642049384702463, 36.794791029185579, 39.945767226378749, 43.095367507846703, 46.2438744334407, 49.391498015725107], [5.6451478942208959, 9.3616206152445429, 12.730144474090465, 15.999627085382479, 19.22442895931681, 22.424810599698521, 25.610267054939328, 28.785893657666548, 31.954686680031668, 35.118529525584828, 38.278668089521758, 41.435960629910073, 44.591018225353424, 47.744288086361052, 50.896105199722123], [6.7471838248710219, 10.597176726782031, 14.033804104911233, 17.347086393228382, 20.602899017175335, 23.826536030287532, 27.030134937138834, 30.220335654231385, 33.401105611047908, 36.574972486670962, 39.743627733020277, 42.908248189569535, 46.069679073215439, 49.228543693445843, 52.385312123112282], [7.8377378223268716, 11.811037107609447, 15.313615118517857, 18.670704965906724, 21.958290897126571, 25.206207715021249, 28.429037095235496, 31.634879502950644, 34.828638524084437, 38.013473399691765, 41.19151880917741, 44.364272633271975, 47.53281875312084, 50.697961822183806, 53.860312300118388], [8.919605734873789, 13.007711435388313, 16.573915129085334, 19.974342312352426, 23.293972585596648, 26.5667563757203, 29.809531451608321, 33.031769327150685, 36.239265816598239, 39.435790312675323, 42.623910919472727, 45.805442883111651, 48.981708325514764, 52.153694518185572, 55.322154420959698], [9.9946283820824834, 14.190361295800141, 17.817887841179873, 21.26093227125945, 24.612576377421522, 27.910524883974868, 31.173701563441602, 34.412862242025045, 37.634648706110989, 40.843415321050884, 44.04214994542435, 47.232978012841169, 50.417456447370186, 53.596753874948731, 56.771765754432457], [11.064090256031013, 15.361301343575925, 19.047949646361388, 22.532765416313869, 25.91620496332662, 29.2394205079349, 32.523270869465881, 35.779715464475261, 39.016196664616095, 42.237627509803703, 45.4474001519274, 48.647941127433196, 51.841036928216499, 55.028034667184916, 58.209970905250097], [12.128927704415439, 16.522284394784426, 20.265984501212254, 23.791669719454272, 27.206568881574774, 30.555020011020762, 33.859683872746356, 37.133649760307504, 40.385117593813002, 43.619533085646856, 46.840676630553575, 50.051265851897857, 53.253310556711732, 56.448332488918971, 59.637507005589829], [13.189846995683845, 17.674674253171487, 21.473493977824902, 25.03913093040942, 28.485081336558058, 31.858644293774859, 35.184165245422787, 38.475796636190897, 41.742455848758449, 44.990096293791186, 48.222870660068338, 51.443777308699826, 54.655042589416311, 57.858358441436511, 61.055036135780528], [14.247395665073945, 18.819555894710682, 22.671697117872794, 26.276375544903892, 29.752925495549038, 33.151412708998983, 36.497763772987645, 39.807134090704376, 43.089121522203808, 46.350163579538652, 49.594769786270069, 52.82620892320143, 56.046916910756961, 59.258751140598783, 62.463155567737854], [15.30200785858925, 19.957808654258601, 23.861599172945054, 27.504429642227545, 31.011103429019229, 34.434283425782942, 37.801385632318459, 41.128514139788358, 44.425913324440663, 47.700482714581842, 50.957073905278458, 54.199216028087261, 57.429547607017405, 60.65008661807661, 63.862406280068586], [16.354034360047551, 21.090156519983806, 25.044040298785627, 28.724161640881914, 32.260472459522644, 35.708083982611664, 39.095820003878235, 42.440684315990936, 45.75353669045622, 49.041718113283529, 52.310408280968073, 55.56338698149062, 58.803488508906895, 62.032886550960831, 65.253280088312461]] ynp_small_zeros = \ [[2.197141326031017, 5.4296810407941351, 8.5960058683311689, 11.749154830839881, 14.897442128336725, 18.043402276727856, 21.188068934142213, 24.331942571356912, 27.475294980449224, 30.618286491641115, 33.761017796109326, 36.90355531614295, 40.045944640266876, 43.188218097393211, 46.330399250701687], [3.6830228565851777, 6.9414999536541757, 10.123404655436613, 13.285758156782854, 16.440058007293282, 19.590241756629495, 22.738034717396327, 25.884314618788867, 29.029575819372535, 32.174118233366201, 35.318134458192094, 38.461753870997549, 41.605066618873108, 44.74813744908079, 47.891014070791065], [5.0025829314460639, 8.3507247014130795, 11.574195465217647, 14.760909306207676, 17.931285939466855, 21.092894504412739, 24.249231678519058, 27.402145837145258, 30.552708880564553, 33.70158627151572, 36.849213419846257, 39.995887376143356, 43.141817835750686, 46.287157097544201, 49.432018469138281], [6.2536332084598136, 9.6987879841487711, 12.972409052292216, 16.19044719506921, 19.38238844973613, 22.559791857764261, 25.728213194724094, 28.890678419054777, 32.048984005266337, 35.204266606440635, 38.357281675961019, 41.508551443818436, 44.658448731963676, 47.807246956681162, 50.95515126455207], [7.4649217367571329, 11.005169149809189, 14.3317235192331, 17.58443601710272, 20.801062338411128, 23.997004122902644, 27.179886689853435, 30.353960608554323, 33.521797098666792, 36.685048382072301, 39.844826969405863, 43.001910515625288, 46.15685955107263, 49.310088614282257, 52.461911043685864], [8.6495562436971983, 12.280868725807848, 15.660799304540377, 18.949739756016503, 22.192841809428241, 25.409072788867674, 28.608039283077593, 31.795195353138159, 34.973890634255288, 38.14630522169358, 41.313923188794905, 44.477791768537617, 47.638672065035628, 50.797131066967842, 53.953600129601663], [9.8147970120105779, 13.532811875789828, 16.965526446046053, 20.291285512443867, 23.56186260680065, 26.799499736027237, 30.015665481543419, 33.216968050039509, 36.407516858984748, 39.590015243560459, 42.766320595957378, 45.937754257017323, 49.105283450953203, 52.269633324547373, 55.431358715604255], [10.965152105242974, 14.765687379508912, 18.250123150217555, 21.612750053384621, 24.911310600813573, 28.171051927637585, 31.40518108895689, 34.621401012564177, 37.824552065973114, 41.017847386464902, 44.203512240871601, 47.3831408366063, 50.557907466622796, 53.728697478957026, 56.896191727313342], [12.103641941939539, 15.982840905145284, 19.517731005559611, 22.916962141504605, 26.243700855690533, 29.525960140695407, 32.778568197561124, 36.010261572392516, 39.226578757802172, 42.43122493258747, 45.626783824134354, 48.815117837929515, 51.997606404328863, 55.175294723956816, 58.348990221754937], [13.232403808592215, 17.186756572616758, 20.770762917490496, 24.206152448722253, 27.561059462697153, 30.866053571250639, 34.137476603379774, 37.385039772270268, 40.614946085165892, 43.831373184731238, 47.037251786726299, 50.234705848765229, 53.425316228549359, 56.610286079882087, 59.790548623216652], [14.35301374369987, 18.379337301642568, 22.011118775283494, 25.482116178696707, 28.865046588695164, 32.192853922166294, 35.483296655830277, 38.747005493021857, 41.990815194320955, 45.219355876831731, 48.435892856078888, 51.642803925173029, 54.84186659475857, 58.034439083840155, 61.221578745109862], [15.466672066554263, 19.562077985759503, 23.240325531101082, 26.746322986645901, 30.157042415639891, 33.507642948240263, 36.817212798512775, 40.097251300178642, 43.355193847719752, 46.596103410173672, 49.823567279972794, 53.040208868780832, 56.247996968470062, 59.448441365714251, 62.642721301357187], [16.574317035530872, 20.73617763753932, 24.459631728238804, 27.999993668839644, 31.438208790267783, 34.811512070805535, 38.140243708611251, 41.436725143893739, 44.708963264433333, 47.962435051891027, 51.201037321915983, 54.427630745992975, 57.644369734615238, 60.852911791989989, 64.054555435720397], [17.676697936439624, 21.9026148697762, 25.670073356263225, 29.244155124266438, 32.709534477396028, 36.105399554497548, 39.453272918267025, 42.766255701958017, 46.052899215578358, 49.319076602061401, 52.568982147952547, 55.805705507386287, 59.031580956740466, 62.248409689597653, 65.457606670836759], [18.774423978290318, 23.06220035979272, 26.872520985976736, 30.479680663499762, 33.971869047372436, 37.390118854896324, 40.757072537673599, 44.086572292170345, 47.387688809191869, 50.66667461073936, 53.928009929563275, 57.175005343085052, 60.410169281219877, 63.635442539153021, 66.85235358587768]] def test_bessel_zeros(): mp.dps = 15 for v in range(V): for m in range(1,M+1): print(v, m, "of", V, M) # Twice to test cache (if used) assert besseljzero(v,m).ae(jn_small_zeros[v][m-1]) assert besseljzero(v,m).ae(jn_small_zeros[v][m-1]) assert besseljzero(v,m,1).ae(jnp_small_zeros[v][m-1]) assert besseljzero(v,m,1).ae(jnp_small_zeros[v][m-1]) assert besselyzero(v,m).ae(yn_small_zeros[v][m-1]) assert besselyzero(v,m).ae(yn_small_zeros[v][m-1]) assert besselyzero(v,m,1).ae(ynp_small_zeros[v][m-1]) assert besselyzero(v,m,1).ae(ynp_small_zeros[v][m-1]) if __name__ == "__main__": test_bessel_zeros() sympy-0.7.4.1/sympy/mpmath/tests/test_visualization.py0000644000175000017500000000221012253362407023362 0ustar georgeskgeorgesk""" Limited tests of the visualization module. Right now it just makes sure that passing custom Axes works. """ # This test either prints something to the terminal or displays a plot, # depending on whether matplotlib is installed or not. Neither is ideal # for a test, so let's just skip this entirely. disabled = True from sympy.mpmath import mp, fp def test_axes(): try: import matplotlib version = matplotlib.__version__.split("-")[0] version = version.split(".")[:2] if [int(_) for _ in version] < [0,99]: raise ImportError import pylab except ImportError: print("\nSkipping test (pylab not available or too old version)\n") return fig = pylab.figure() axes = fig.add_subplot(111) for ctx in [mp, fp]: ctx.plot(lambda x: x**2, [0, 3], axes=axes) assert axes.get_xlabel() == 'x' assert axes.get_ylabel() == 'f(x)' fig = pylab.figure() axes = fig.add_subplot(111) for ctx in [mp, fp]: ctx.cplot(lambda z: z, [-2, 2], [-10, 10], axes=axes) assert axes.get_xlabel() == 'Re(z)' assert axes.get_ylabel() == 'Im(z)' sympy-0.7.4.1/sympy/mpmath/tests/runtests.py0000644000175000017500000001157212253362407021324 0ustar georgeskgeorgesk#!/usr/bin/env python """ python runtests.py -py Use py.test to run tests (more useful for debugging) python runtests.py -psyco Enable psyco to make tests run about 50% faster python runtests.py -coverage Generate test coverage report. Statistics are written to /tmp python runtests.py -profile Generate profile stats (this is much slower) python runtests.py -nogmpy Run tests without using GMPY even if it exists python runtests.py -strict Enforce extra tests in normalize() python runtests.py -local Insert '../..' at the beginning of sys.path to use local mpmath Additional arguments are used to filter the tests to run. Only files that have one of the arguments in their name are executed. """ import sys, os, traceback if "-psyco" in sys.argv: sys.argv.remove('-psyco') import psyco psyco.full() profile = False if "-profile" in sys.argv: sys.argv.remove('-profile') profile = True coverage = False if "-coverage" in sys.argv: sys.argv.remove('-coverage') coverage = True if "-nogmpy" in sys.argv: sys.argv.remove('-nogmpy') os.environ['MPMATH_NOGMPY'] = 'Y' if "-strict" in sys.argv: sys.argv.remove('-strict') os.environ['MPMATH_STRICT'] = 'Y' if "-local" in sys.argv: sys.argv.remove('-local') importdir = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../..')) else: importdir = '' # TODO: add a flag for this testdir = '' def testit(importdir='', testdir=''): """Run all tests in testdir while importing from importdir.""" if importdir: sys.path.insert(1, importdir) if testdir: sys.path.insert(1, testdir) import os.path import mpmath print("mpmath imported from %s" % os.path.dirname(mpmath.__file__)) print("mpmath backend: %s" % mpmath.libmp.backend.BACKEND) print("mpmath mp class: %s" % repr(mpmath.mp)) print("mpmath version: %s" % mpmath.__version__) print("Python version: %s" % sys.version) print("") if "-py" in sys.argv: sys.argv.remove('-py') import py py.test.cmdline.main() else: import glob from timeit import default_timer as clock modules = [] args = sys.argv[1:] # search for tests in directory of this file if not otherwise specified if not testdir: pattern = os.path.dirname(sys.argv[0]) else: pattern = testdir if pattern: pattern += '/' pattern += 'test*.py' # look for tests (respecting specified filter) for f in glob.glob(pattern): name = os.path.splitext(os.path.basename(f))[0] # If run as a script, only run tests given as args, if any are given if args and __name__ == "__main__": ok = False for arg in args: if arg in name: ok = True break if not ok: continue module = __import__(name) priority = module.__dict__.get('priority', 100) if priority == 666: modules = [[priority, name, module]] break modules.append([priority, name, module]) # execute tests modules.sort() tstart = clock() for priority, name, module in modules: print(name) for f in sorted(module.__dict__.keys()): if f.startswith('test_'): if coverage and ('numpy' in f): continue sys.stdout.write(" " + f[5:].ljust(25) + " ") t1 = clock() try: module.__dict__[f]() except: etype, evalue, trb = sys.exc_info() if etype in (KeyboardInterrupt, SystemExit): raise print("") print("TEST FAILED!") print("") traceback.print_exc() t2 = clock() print("ok " + " " + ("%.7f" % (t2-t1)) + " s") tend = clock() print("") print("finished tests in " + ("%.2f" % (tend-tstart)) + " seconds") # clean sys.path if importdir: sys.path.remove(importdir) if testdir: sys.path.remove(testdir) if __name__ == '__main__': if profile: import cProfile cProfile.run("testit('%s', '%s')" % (importdir, testdir), sort=1) elif coverage: import trace tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix], trace=0, count=1) tracer.run('testit(importdir, testdir)') r = tracer.results() r.write_results(show_missing=True, summary=True, coverdir="/tmp") else: testit(importdir, testdir) sympy-0.7.4.1/sympy/mpmath/tests/test_fp.py0000644000175000017500000026045412253362407021106 0ustar georgeskgeorgesk""" Easy-to-use test-generating code: cases = ''' exp 2.25 log 2.25 ''' from sympy.mpmath import * mp.dps = 20 for test in cases.splitlines(): if not test: continue words = test.split() fname = words[0] args = words[1:] argstr = ", ".join(args) testline = "%s(%s)" % (fname, argstr) ans = str(eval(testline)) print " assert ae(fp.%s, %s)" % (testline, ans) """ from sympy.mpmath import fp def ae(x, y, tol=1e-12): if x == y: return True return abs(x-y) <= tol*abs(y) def test_conj(): assert fp.conj(4) == 4 assert fp.conj(3+4j) == 3-4j assert fp.fdot([1,2],[3,2+1j], conjugate=True) == 7-2j def test_fp_number_parts(): assert ae(fp.arg(3), 0.0) assert ae(fp.arg(-3), 3.1415926535897932385) assert ae(fp.arg(3j), 1.5707963267948966192) assert ae(fp.arg(-3j), -1.5707963267948966192) assert ae(fp.arg(2+3j), 0.98279372324732906799) assert ae(fp.arg(-1-1j), -2.3561944901923449288) assert ae(fp.re(2.5), 2.5) assert ae(fp.re(2.5+3j), 2.5) assert ae(fp.im(2.5), 0.0) assert ae(fp.im(2.5+3j), 3.0) assert ae(fp.floor(2.5), 2.0) assert ae(fp.floor(2), 2.0) assert ae(fp.floor(2.0+0j), (2.0 + 0.0j)) assert ae(fp.floor(-1.5-0.5j), (-2.0 - 1.0j)) assert ae(fp.ceil(2.5), 3.0) assert ae(fp.ceil(2), 2.0) assert ae(fp.ceil(2.0+0j), (2.0 + 0.0j)) assert ae(fp.ceil(-1.5-0.5j), (-1.0 + 0.0j)) def test_fp_cospi_sinpi(): assert ae(fp.sinpi(0), 0.0) assert ae(fp.sinpi(0.25), 0.7071067811865475244) assert ae(fp.sinpi(0.5), 1.0) assert ae(fp.sinpi(0.75), 0.7071067811865475244) assert ae(fp.sinpi(1), 0.0) assert ae(fp.sinpi(1.25), -0.7071067811865475244) assert ae(fp.sinpi(1.5), -1.0) assert ae(fp.sinpi(1.75), -0.7071067811865475244) assert ae(fp.sinpi(2), 0.0) assert ae(fp.sinpi(2.25), 0.7071067811865475244) assert ae(fp.sinpi(0+3j), (0.0 + 6195.8238636085899556j)) assert ae(fp.sinpi(0.25+3j), (4381.1091260582448033 + 4381.1090689950686908j)) assert ae(fp.sinpi(0.5+3j), (6195.8239443081075259 + 0.0j)) assert ae(fp.sinpi(0.75+3j), (4381.1091260582448033 - 4381.1090689950686908j)) assert ae(fp.sinpi(1+3j), (0.0 - 6195.8238636085899556j)) assert ae(fp.sinpi(1.25+3j), (-4381.1091260582448033 - 4381.1090689950686908j)) assert ae(fp.sinpi(1.5+3j), (-6195.8239443081075259 + 0.0j)) assert ae(fp.sinpi(1.75+3j), (-4381.1091260582448033 + 4381.1090689950686908j)) assert ae(fp.sinpi(2+3j), (0.0 + 6195.8238636085899556j)) assert ae(fp.sinpi(2.25+3j), (4381.1091260582448033 + 4381.1090689950686908j)) assert ae(fp.sinpi(-0.75), -0.7071067811865475244) assert ae(fp.sinpi(-1e-10), -3.1415926535897933529e-10) assert ae(fp.sinpi(1e-10), 3.1415926535897933529e-10) assert ae(fp.sinpi(1e-10+1e-10j), (3.141592653589793353e-10 + 3.1415926535897933528e-10j)) assert ae(fp.sinpi(1e-10-1e-10j), (3.141592653589793353e-10 - 3.1415926535897933528e-10j)) assert ae(fp.sinpi(-1e-10+1e-10j), (-3.141592653589793353e-10 + 3.1415926535897933528e-10j)) assert ae(fp.sinpi(-1e-10-1e-10j), (-3.141592653589793353e-10 - 3.1415926535897933528e-10j)) assert ae(fp.cospi(0), 1.0) assert ae(fp.cospi(0.25), 0.7071067811865475244) assert ae(fp.cospi(0.5), 0.0) assert ae(fp.cospi(0.75), -0.7071067811865475244) assert ae(fp.cospi(1), -1.0) assert ae(fp.cospi(1.25), -0.7071067811865475244) assert ae(fp.cospi(1.5), 0.0) assert ae(fp.cospi(1.75), 0.7071067811865475244) assert ae(fp.cospi(2), 1.0) assert ae(fp.cospi(2.25), 0.7071067811865475244) assert ae(fp.cospi(0+3j), (6195.8239443081075259 + 0.0j)) assert ae(fp.cospi(0.25+3j), (4381.1091260582448033 - 4381.1090689950686908j)) assert ae(fp.cospi(0.5+3j), (0.0 - 6195.8238636085899556j)) assert ae(fp.cospi(0.75+3j), (-4381.1091260582448033 - 4381.1090689950686908j)) assert ae(fp.cospi(1+3j), (-6195.8239443081075259 + 0.0j)) assert ae(fp.cospi(1.25+3j), (-4381.1091260582448033 + 4381.1090689950686908j)) assert ae(fp.cospi(1.5+3j), (0.0 + 6195.8238636085899556j)) assert ae(fp.cospi(1.75+3j), (4381.1091260582448033 + 4381.1090689950686908j)) assert ae(fp.cospi(2+3j), (6195.8239443081075259 + 0.0j)) assert ae(fp.cospi(2.25+3j), (4381.1091260582448033 - 4381.1090689950686908j)) assert ae(fp.cospi(-0.75), -0.7071067811865475244) assert ae(fp.sinpi(-0.7), -0.80901699437494750611) assert ae(fp.cospi(-0.7), -0.5877852522924730163) assert ae(fp.cospi(-3+2j), (-267.74676148374822225 + 0.0j)) assert ae(fp.sinpi(-3+2j), (0.0 - 267.74489404101651426j)) assert ae(fp.sinpi(-0.7+2j), (-216.6116802292079471 - 157.37650009392034693j)) assert ae(fp.cospi(-0.7+2j), (-157.37759774921754565 + 216.61016943630197336j)) def test_fp_expj(): assert ae(fp.expj(0), (1.0 + 0.0j)) assert ae(fp.expj(1), (0.5403023058681397174 + 0.84147098480789650665j)) assert ae(fp.expj(2), (-0.416146836547142387 + 0.9092974268256816954j)) assert ae(fp.expj(0.75), (0.73168886887382088631 + 0.68163876002333416673j)) assert ae(fp.expj(2+3j), (-0.020718731002242879378 + 0.045271253156092975488j)) assert ae(fp.expjpi(0), (1.0 + 0.0j)) assert ae(fp.expjpi(1), (-1.0 + 0.0j)) assert ae(fp.expjpi(2), (1.0 + 0.0j)) assert ae(fp.expjpi(0.75), (-0.7071067811865475244 + 0.7071067811865475244j)) assert ae(fp.expjpi(2+3j), (0.000080699517570304599239 + 0.0j)) def test_fp_bernoulli(): assert ae(fp.bernoulli(0), 1.0) assert ae(fp.bernoulli(1), -0.5) assert ae(fp.bernoulli(2), 0.16666666666666666667) assert ae(fp.bernoulli(10), 0.075757575757575757576) assert ae(fp.bernoulli(11), 0.0) def test_fp_gamma(): assert ae(fp.gamma(1), 1.0) assert ae(fp.gamma(1.5), 0.88622692545275801365) assert ae(fp.gamma(10), 362880.0) assert ae(fp.gamma(-0.5), -3.5449077018110320546) assert ae(fp.gamma(-7.1), 0.0016478244570263333622) assert ae(fp.gamma(12.3), 83385367.899970000963) assert ae(fp.gamma(2+0j), (1.0 + 0.0j)) assert ae(fp.gamma(-2.5+0j), (-0.94530872048294188123 + 0.0j)) assert ae(fp.gamma(3+4j), (0.0052255384713692141947 - 0.17254707929430018772j)) assert ae(fp.gamma(-3-4j), (0.00001460997305874775607 - 0.000020760733311509070396j)) assert ae(fp.fac(0), 1.0) assert ae(fp.fac(1), 1.0) assert ae(fp.fac(20), 2432902008176640000.0) assert ae(fp.fac(-3.5), -0.94530872048294188123) assert ae(fp.fac(2+3j), (-0.44011340763700171113 - 0.06363724312631702183j)) assert ae(fp.loggamma(1.0), 0.0) assert ae(fp.loggamma(2.0), 0.0) assert ae(fp.loggamma(3.0), 0.69314718055994530942) assert ae(fp.loggamma(7.25), 7.0521854507385394449) assert ae(fp.loggamma(1000.0), 5905.2204232091812118) assert ae(fp.loggamma(1e50), 1.1412925464970229298e+52) assert ae(fp.loggamma(1e25+1e25j), (5.6125802751733671621e+26 + 5.7696599078528568383e+26j)) assert ae(fp.loggamma(3+4j), (-1.7566267846037841105 + 4.7426644380346579282j)) assert ae(fp.loggamma(-0.5), (1.2655121234846453965 - 3.1415926535897932385j)) assert ae(fp.loggamma(-1.25), (1.3664317612369762346 - 6.2831853071795864769j)) assert ae(fp.loggamma(-2.75), (0.0044878975359557733115 - 9.4247779607693797154j)) assert ae(fp.loggamma(-3.5), (-1.3090066849930420464 - 12.566370614359172954j)) assert ae(fp.loggamma(-4.5), (-2.8130840817693161197 - 15.707963267948966192j)) assert ae(fp.loggamma(-2+3j), (-6.776523813485657093 - 4.568791367260286402j)) assert ae(fp.loggamma(-1000.3), (-5912.8440347785205041 - 3144.7342462433830317j)) assert ae(fp.loggamma(-100-100j), (-632.35117666833135562 - 158.37641469650352462j)) assert ae(fp.loggamma(1e-10), 23.025850929882735237) assert ae(fp.loggamma(-1e-10), (23.02585092999817837 - 3.1415926535897932385j)) assert ae(fp.loggamma(1e-10j), (23.025850929940456804 - 1.5707963268526181857j)) assert ae(fp.loggamma(1e-10j-1e-10), (22.679277339718205716 - 2.3561944902500664954j)) def test_fp_psi(): assert ae(fp.psi(0, 3.7), 1.1671535393615114409) assert ae(fp.psi(0, 0.5), -1.9635100260214234794) assert ae(fp.psi(0, 1), -0.57721566490153286061) assert ae(fp.psi(0, -2.5), 1.1031566406452431872) assert ae(fp.psi(0, 12.9), 2.5179671503279156347) assert ae(fp.psi(0, 100), 4.6001618527380874002) assert ae(fp.psi(0, 2500.3), 7.8239660143238547877) assert ae(fp.psi(0, 1e40), 92.103403719761827391) assert ae(fp.psi(0, 1e200), 460.51701859880913677) assert ae(fp.psi(0, 3.7+0j), (1.1671535393615114409 + 0.0j)) assert ae(fp.psi(1, 3), 0.39493406684822643647) assert ae(fp.psi(3, 2+3j), (-0.05383196209159972116 + 0.0076890935247364805218j)) assert ae(fp.psi(4, -0.5+1j), (1.2719531355492328195 - 18.211833410936276774j)) assert ae(fp.harmonic(0), 0.0) assert ae(fp.harmonic(1), 1.0) assert ae(fp.harmonic(2), 1.5) assert ae(fp.harmonic(100), 5.1873775176396202608) assert ae(fp.harmonic(-2.5), 1.2803723055467760478) assert ae(fp.harmonic(2+3j), (1.9390425294578375875 + 0.87336044981834544043j)) assert ae(fp.harmonic(-5-4j), (2.3725754822349437733 - 2.4160904444801621j)) def test_fp_zeta(): assert ae(fp.zeta(1e100), 1.0) assert ae(fp.zeta(3), 1.2020569031595942854) assert ae(fp.zeta(2+0j), (1.6449340668482264365 + 0.0j)) assert ae(fp.zeta(0.93), -13.713619351638164784) assert ae(fp.zeta(1.74), 1.9796863545771774095) assert ae(fp.zeta(0.0), -0.5) assert ae(fp.zeta(-1.0), -0.083333333333333333333) assert ae(fp.zeta(-2.0), 0.0) assert ae(fp.zeta(-3.0), 0.0083333333333333333333) assert ae(fp.zeta(-500.0), 0.0) assert ae(fp.zeta(-7.4), 0.0036537321227995882447) assert ae(fp.zeta(2.1), 1.5602165335033620158) assert ae(fp.zeta(26.9), 1.0000000079854809935) assert ae(fp.zeta(26), 1.0000000149015548284) assert ae(fp.zeta(27), 1.0000000074507117898) assert ae(fp.zeta(28), 1.0000000037253340248) assert ae(fp.zeta(27.1), 1.000000006951755045) assert ae(fp.zeta(32.7), 1.0000000001433243232) assert ae(fp.zeta(100), 1.0) assert ae(fp.altzeta(3.5), 0.92755357777394803511) assert ae(fp.altzeta(1), 0.69314718055994530942) assert ae(fp.altzeta(2), 0.82246703342411321824) assert ae(fp.altzeta(0), 0.5) assert ae(fp.zeta(-2+3j, 1), (0.13297115587929864827 + 0.12305330040458776494j)) assert ae(fp.zeta(-2+3j, 5), (18.384866151867576927 - 11.377015110597711009j)) assert ae(fp.zeta(1.0000000001), 9999999173.1735741337) assert ae(fp.zeta(0.9999999999), -9999999172.0191428039) assert ae(fp.zeta(1+0.000000001j), (0.57721566490153286061 - 999999999.99999993765j)) assert ae(fp.primezeta(2.5+4j), (-0.16922458243438033385 - 0.010847965298387727811j)) assert ae(fp.primezeta(4), 0.076993139764246844943) assert ae(fp.riemannr(3.7), 2.3034079839110855717) assert ae(fp.riemannr(8), 3.9011860449341499474) assert ae(fp.riemannr(3+4j), (2.2369653314259991796 + 1.6339943856990281694j)) def test_fp_hyp2f1(): assert ae(fp.hyp2f1(1, (3,2), 3.25, 5.0), (-0.46600275923108143059 - 0.74393667908854842325j)) assert ae(fp.hyp2f1(1+1j, (3,2), 3.25, 5.0), (-5.9208875603806515987 - 2.3813557707889590686j)) assert ae(fp.hyp2f1(1+1j, (3,2), 3.25, 2+3j), (0.17174552030925080445 + 0.19589781970539389999j)) def test_fp_erf(): assert fp.erf(2) == fp.erf(2.0) == fp.erf(2.0+0.0j) assert fp.erf(fp.inf) == 1.0 assert fp.erf(fp.ninf) == -1.0 assert ae(fp.erf(0), 0.0) assert ae(fp.erf(-0), -0.0) assert ae(fp.erf(0.3), 0.32862675945912741619) assert ae(fp.erf(-0.3), -0.32862675945912741619) assert ae(fp.erf(0.9), 0.79690821242283213966) assert ae(fp.erf(-0.9), -0.79690821242283213966) assert ae(fp.erf(1.0), 0.84270079294971486934) assert ae(fp.erf(-1.0), -0.84270079294971486934) assert ae(fp.erf(1.1), 0.88020506957408172966) assert ae(fp.erf(-1.1), -0.88020506957408172966) assert ae(fp.erf(8.5), 1.0) assert ae(fp.erf(-8.5), -1.0) assert ae(fp.erf(9.1), 1.0) assert ae(fp.erf(-9.1), -1.0) assert ae(fp.erf(20.0), 1.0) assert ae(fp.erf(-20.0), -1.0) assert ae(fp.erf(10000.0), 1.0) assert ae(fp.erf(-10000.0), -1.0) assert ae(fp.erf(1e+50), 1.0) assert ae(fp.erf(-1e+50), -1.0) assert ae(fp.erf(1j), 1.650425758797542876j) assert ae(fp.erf(-1j), -1.650425758797542876j) assert ae(fp.erf((2+3j)), (-20.829461427614568389 + 8.6873182714701631444j)) assert ae(fp.erf(-(2+3j)), -(-20.829461427614568389 + 8.6873182714701631444j)) assert ae(fp.erf((8+9j)), (-1072004.2525062051158 + 364149.91954310255423j)) assert ae(fp.erf(-(8+9j)), -(-1072004.2525062051158 + 364149.91954310255423j)) assert fp.erfc(fp.inf) == 0.0 assert fp.erfc(fp.ninf) == 2.0 assert fp.erfc(0) == 1 assert fp.erfc(-0.0) == 1 assert fp.erfc(0+0j) == 1 assert ae(fp.erfc(0.3), 0.67137324054087258381) assert ae(fp.erfc(-0.3), 1.3286267594591274162) assert ae(fp.erfc(0.9), 0.20309178757716786034) assert ae(fp.erfc(-0.9), 1.7969082124228321397) assert ae(fp.erfc(1.0), 0.15729920705028513066) assert ae(fp.erfc(-1.0), 1.8427007929497148693) assert ae(fp.erfc(1.1), 0.11979493042591827034) assert ae(fp.erfc(-1.1), 1.8802050695740817297) assert ae(fp.erfc(8.5), 2.7623240713337714461e-33) assert ae(fp.erfc(-8.5), 2.0) assert ae(fp.erfc(9.1), 6.6969004279886077452e-38) assert ae(fp.erfc(-9.1), 2.0) assert ae(fp.erfc(20.0), 5.3958656116079009289e-176) assert ae(fp.erfc(-20.0), 2.0) assert ae(fp.erfc(10000.0), 0.0) assert ae(fp.erfc(-10000.0), 2.0) assert ae(fp.erfc(1e+50), 0.0) assert ae(fp.erfc(-1e+50), 2.0) assert ae(fp.erfc(1j), (1.0 - 1.650425758797542876j)) assert ae(fp.erfc(-1j), (1.0 + 1.650425758797542876j)) assert ae(fp.erfc((2+3j)), (21.829461427614568389 - 8.6873182714701631444j), 1e-13) assert ae(fp.erfc(-(2+3j)), (-19.829461427614568389 + 8.6873182714701631444j), 1e-13) assert ae(fp.erfc((8+9j)), (1072005.2525062051158 - 364149.91954310255423j)) assert ae(fp.erfc(-(8+9j)), (-1072003.2525062051158 + 364149.91954310255423j)) assert ae(fp.erfc(20+0j), (5.3958656116079009289e-176 + 0.0j)) def test_fp_lambertw(): assert ae(fp.lambertw(0.0), 0.0) assert ae(fp.lambertw(1.0), 0.567143290409783873) assert ae(fp.lambertw(7.5), 1.5662309537823875394) assert ae(fp.lambertw(-0.25), -0.35740295618138890307) assert ae(fp.lambertw(-10.0), (1.3699809685212708156 + 2.140194527074713196j)) assert ae(fp.lambertw(0+0j), (0.0 + 0.0j)) assert ae(fp.lambertw(4+0j), (1.2021678731970429392 + 0.0j)) assert ae(fp.lambertw(1000.5), 5.2500227450408980127) assert ae(fp.lambertw(1e100), 224.84310644511850156) assert ae(fp.lambertw(-1000.0), (5.1501630246362515223 + 2.6641981432905204596j)) assert ae(fp.lambertw(1e-10), 9.9999999990000003645e-11) assert ae(fp.lambertw(1e-10j), (1.0000000000000000728e-20 + 1.0000000000000000364e-10j)) assert ae(fp.lambertw(3+4j), (1.2815618061237758782 + 0.53309522202097107131j)) assert ae(fp.lambertw(-3-4j), (1.0750730665692549276 - 1.3251023817343588823j)) assert ae(fp.lambertw(10000+1000j), (7.2361526563371602186 + 0.087567810943839352034j)) assert ae(fp.lambertw(0.0, -1), -fp.inf) assert ae(fp.lambertw(1.0, -1), (-1.5339133197935745079 - 4.3751851530618983855j)) assert ae(fp.lambertw(7.5, -1), (0.44125668415098614999 - 4.8039842008452390179j)) assert ae(fp.lambertw(-0.25, -1), -2.1532923641103496492) assert ae(fp.lambertw(-10.0, -1), (1.3699809685212708156 - 2.140194527074713196j)) assert ae(fp.lambertw(0+0j, -1), -fp.inf) assert ae(fp.lambertw(4+0j, -1), (-0.15730793189620765317 - 4.6787800704666656212j)) assert ae(fp.lambertw(1000.5, -1), (4.9153765415404024736 - 5.4465682700815159569j)) assert ae(fp.lambertw(1e100, -1), (224.84272130101601052 - 6.2553713838167244141j)) assert ae(fp.lambertw(-1000.0, -1), (5.1501630246362515223 - 2.6641981432905204596j)) assert ae(fp.lambertw(1e-10, -1), (-26.303186778379041521 - 3.2650939117038283975j)) assert ae(fp.lambertw(1e-10j, -1), (-26.297238779529035028 - 1.6328071613455765135j)) assert ae(fp.lambertw(3+4j, -1), (0.25856740686699741676 - 3.8521166861614355895j)) assert ae(fp.lambertw(-3-4j, -1), (-0.32028750204310768396 - 6.8801677192091972343j)) assert ae(fp.lambertw(10000+1000j, -1), (7.0255308742285435567 - 5.5177506835734067601j)) assert ae(fp.lambertw(0.0, 2), -fp.inf) assert ae(fp.lambertw(1.0, 2), (-2.4015851048680028842 + 10.776299516115070898j)) assert ae(fp.lambertw(7.5, 2), (-0.38003357962843791529 + 10.960916473368746184j)) assert ae(fp.lambertw(-0.25, 2), (-4.0558735269061511898 + 13.852334658567271386j)) assert ae(fp.lambertw(-10.0, 2), (-0.34479123764318858696 + 14.112740596763592363j)) assert ae(fp.lambertw(0+0j, 2), -fp.inf) assert ae(fp.lambertw(4+0j, 2), (-1.0070343323804262788 + 10.903476551861683082j)) assert ae(fp.lambertw(1000.5, 2), (4.4076185165459395295 + 11.365524591091402177j)) assert ae(fp.lambertw(1e100, 2), (224.84156762724875878 + 12.510785262632255672j)) assert ae(fp.lambertw(-1000.0, 2), (4.1984245610246530756 + 14.420478573754313845j)) assert ae(fp.lambertw(1e-10, 2), (-26.362258095445866488 + 9.7800247407031482519j)) assert ae(fp.lambertw(1e-10j, 2), (-26.384250801683084252 + 11.403535950607739763j)) assert ae(fp.lambertw(3+4j, 2), (-0.86554679943333993562 + 11.849956798331992027j)) assert ae(fp.lambertw(-3-4j, 2), (-0.55792273874679112639 + 8.7173627024159324811j)) assert ae(fp.lambertw(10000+1000j, 2), (6.6223802254585662734 + 11.61348646825020766j)) # This function has to be split because it is too long for Jython. # See http://grinder.sourceforge.net/faq.html#faq-N10544. def test_fp_stress_ei_e1_1(): # Can be tightened on recent Pythons with more accurate math/cmath ATOL = 1e-13 PTOL = 1e-12 v = fp.e1(1.1641532182693481445e-10) assert ae(v, 22.296641293693077672, tol=ATOL) assert type(v) is float v = fp.e1(0.25) assert ae(v, 1.0442826344437381945, tol=ATOL) assert type(v) is float v = fp.e1(1.0) assert ae(v, 0.21938393439552027368, tol=ATOL) assert type(v) is float v = fp.e1(2.0) assert ae(v, 0.048900510708061119567, tol=ATOL) assert type(v) is float v = fp.e1(5.0) assert ae(v, 0.0011482955912753257973, tol=ATOL) assert type(v) is float v = fp.e1(20.0) assert ae(v, 9.8355252906498816904e-11, tol=ATOL) assert type(v) is float v = fp.e1(30.0) assert ae(v, 3.0215520106888125448e-15, tol=ATOL) assert type(v) is float v = fp.e1(40.0) assert ae(v, 1.0367732614516569722e-19, tol=ATOL) assert type(v) is float v = fp.e1(50.0) assert ae(v, 3.7832640295504590187e-24, tol=ATOL) assert type(v) is float v = fp.e1(80.0) assert ae(v, 2.2285432586884729112e-37, tol=ATOL) assert type(v) is float v = fp.e1((1.1641532182693481445e-10 + 0.0j)) assert ae(v, (22.296641293693077672 + 0.0j), tol=ATOL) assert ae(v.real, 22.296641293693077672, tol=PTOL) assert v.imag == 0 v = fp.e1((0.25 + 0.0j)) assert ae(v, (1.0442826344437381945 + 0.0j), tol=ATOL) assert ae(v.real, 1.0442826344437381945, tol=PTOL) assert v.imag == 0 v = fp.e1((1.0 + 0.0j)) assert ae(v, (0.21938393439552027368 + 0.0j), tol=ATOL) assert ae(v.real, 0.21938393439552027368, tol=PTOL) assert v.imag == 0 v = fp.e1((2.0 + 0.0j)) assert ae(v, (0.048900510708061119567 + 0.0j), tol=ATOL) assert ae(v.real, 0.048900510708061119567, tol=PTOL) assert v.imag == 0 v = fp.e1((5.0 + 0.0j)) assert ae(v, (0.0011482955912753257973 + 0.0j), tol=ATOL) assert ae(v.real, 0.0011482955912753257973, tol=PTOL) assert v.imag == 0 v = fp.e1((20.0 + 0.0j)) assert ae(v, (9.8355252906498816904e-11 + 0.0j), tol=ATOL) assert ae(v.real, 9.8355252906498816904e-11, tol=PTOL) assert v.imag == 0 v = fp.e1((30.0 + 0.0j)) assert ae(v, (3.0215520106888125448e-15 + 0.0j), tol=ATOL) assert ae(v.real, 3.0215520106888125448e-15, tol=PTOL) assert v.imag == 0 v = fp.e1((40.0 + 0.0j)) assert ae(v, (1.0367732614516569722e-19 + 0.0j), tol=ATOL) assert ae(v.real, 1.0367732614516569722e-19, tol=PTOL) assert v.imag == 0 v = fp.e1((50.0 + 0.0j)) assert ae(v, (3.7832640295504590187e-24 + 0.0j), tol=ATOL) assert ae(v.real, 3.7832640295504590187e-24, tol=PTOL) assert v.imag == 0 v = fp.e1((80.0 + 0.0j)) assert ae(v, (2.2285432586884729112e-37 + 0.0j), tol=ATOL) assert ae(v.real, 2.2285432586884729112e-37, tol=PTOL) assert v.imag == 0 v = fp.e1((4.6566128730773925781e-10 + 1.1641532182693481445e-10j)) assert ae(v, (20.880034622014215597 - 0.24497866301044883237j), tol=ATOL) assert ae(v.real, 20.880034622014215597, tol=PTOL) assert ae(v.imag, -0.24497866301044883237, tol=PTOL) v = fp.e1((1.0 + 0.25j)) assert ae(v, (0.19731063945004229095 - 0.087366045774299963672j), tol=ATOL) assert ae(v.real, 0.19731063945004229095, tol=PTOL) assert ae(v.imag, -0.087366045774299963672, tol=PTOL) v = fp.e1((4.0 + 1.0j)) assert ae(v, (0.0013106173980145506944 - 0.0034542480199350626699j), tol=ATOL) assert ae(v.real, 0.0013106173980145506944, tol=PTOL) assert ae(v.imag, -0.0034542480199350626699, tol=PTOL) v = fp.e1((8.0 + 2.0j)) assert ae(v, (-0.000022278049065270225945 - 0.000029191940456521555288j), tol=ATOL) assert ae(v.real, -0.000022278049065270225945, tol=PTOL) assert ae(v.imag, -0.000029191940456521555288, tol=PTOL) v = fp.e1((20.0 + 5.0j)) assert ae(v, (4.7711374515765346894e-11 + 8.2902652405126947359e-11j), tol=ATOL) assert ae(v.real, 4.7711374515765346894e-11, tol=PTOL) assert ae(v.imag, 8.2902652405126947359e-11, tol=PTOL) v = fp.e1((80.0 + 20.0j)) assert ae(v, (3.8353473865788235787e-38 - 2.129247592349605139e-37j), tol=ATOL) assert ae(v.real, 3.8353473865788235787e-38, tol=PTOL) assert ae(v.imag, -2.129247592349605139e-37, tol=PTOL) v = fp.e1((120.0 + 30.0j)) assert ae(v, (2.3836002337480334716e-55 + 5.6704043587126198306e-55j), tol=ATOL) assert ae(v.real, 2.3836002337480334716e-55, tol=PTOL) assert ae(v.imag, 5.6704043587126198306e-55, tol=PTOL) v = fp.e1((160.0 + 40.0j)) assert ae(v, (-1.6238022898654510661e-72 - 1.104172355572287367e-72j), tol=ATOL) assert ae(v.real, -1.6238022898654510661e-72, tol=PTOL) assert ae(v.imag, -1.104172355572287367e-72, tol=PTOL) v = fp.e1((200.0 + 50.0j)) assert ae(v, (6.6800061461666228487e-90 + 1.4473816083541016115e-91j), tol=ATOL) assert ae(v.real, 6.6800061461666228487e-90, tol=PTOL) assert ae(v.imag, 1.4473816083541016115e-91, tol=PTOL) v = fp.e1((320.0 + 80.0j)) assert ae(v, (4.2737871527778786157e-143 + 3.1789935525785660314e-142j), tol=ATOL) assert ae(v.real, 4.2737871527778786157e-143, tol=PTOL) assert ae(v.imag, 3.1789935525785660314e-142, tol=PTOL) v = fp.e1((1.1641532182693481445e-10 + 1.1641532182693481445e-10j)) assert ae(v, (21.950067703413105017 - 0.7853981632810329878j), tol=ATOL) assert ae(v.real, 21.950067703413105017, tol=PTOL) assert ae(v.imag, -0.7853981632810329878, tol=PTOL) v = fp.e1((0.25 + 0.25j)) assert ae(v, (0.71092525792923287894 - 0.56491812441304194711j), tol=ATOL) assert ae(v.real, 0.71092525792923287894, tol=PTOL) assert ae(v.imag, -0.56491812441304194711, tol=PTOL) v = fp.e1((1.0 + 1.0j)) assert ae(v, (0.00028162445198141832551 - 0.17932453503935894015j), tol=ATOL) assert ae(v.real, 0.00028162445198141832551, tol=PTOL) assert ae(v.imag, -0.17932453503935894015, tol=PTOL) v = fp.e1((2.0 + 2.0j)) assert ae(v, (-0.033767089606562004246 - 0.018599414169750541925j), tol=ATOL) assert ae(v.real, -0.033767089606562004246, tol=PTOL) assert ae(v.imag, -0.018599414169750541925, tol=PTOL) v = fp.e1((5.0 + 5.0j)) assert ae(v, (0.0007266506660356393891 + 0.00047102780163522245054j), tol=ATOL) assert ae(v.real, 0.0007266506660356393891, tol=PTOL) assert ae(v.imag, 0.00047102780163522245054, tol=PTOL) v = fp.e1((20.0 + 20.0j)) assert ae(v, (-2.3824537449367396579e-11 - 6.6969873156525615158e-11j), tol=ATOL) assert ae(v.real, -2.3824537449367396579e-11, tol=PTOL) assert ae(v.imag, -6.6969873156525615158e-11, tol=PTOL) v = fp.e1((30.0 + 30.0j)) assert ae(v, (1.7316045841744061617e-15 + 1.3065678019487308689e-15j), tol=ATOL) assert ae(v.real, 1.7316045841744061617e-15, tol=PTOL) assert ae(v.imag, 1.3065678019487308689e-15, tol=PTOL) v = fp.e1((40.0 + 40.0j)) assert ae(v, (-7.4001043002899232182e-20 - 4.991847855336816304e-21j), tol=ATOL) assert ae(v.real, -7.4001043002899232182e-20, tol=PTOL) assert ae(v.imag, -4.991847855336816304e-21, tol=PTOL) v = fp.e1((50.0 + 50.0j)) assert ae(v, (2.3566128324644641219e-24 - 1.3188326726201614778e-24j), tol=ATOL) assert ae(v.real, 2.3566128324644641219e-24, tol=PTOL) assert ae(v.imag, -1.3188326726201614778e-24, tol=PTOL) v = fp.e1((80.0 + 80.0j)) assert ae(v, (9.8279750572186526673e-38 + 1.243952841288868831e-37j), tol=ATOL) assert ae(v.real, 9.8279750572186526673e-38, tol=PTOL) assert ae(v.imag, 1.243952841288868831e-37, tol=PTOL) v = fp.e1((1.1641532182693481445e-10 + 4.6566128730773925781e-10j)) assert ae(v, (20.880034621664969632 - 1.3258176632023711778j), tol=ATOL) assert ae(v.real, 20.880034621664969632, tol=PTOL) assert ae(v.imag, -1.3258176632023711778, tol=PTOL) v = fp.e1((0.25 + 1.0j)) assert ae(v, (-0.16868306393667788761 - 0.4858011885947426971j), tol=ATOL) assert ae(v.real, -0.16868306393667788761, tol=PTOL) assert ae(v.imag, -0.4858011885947426971, tol=PTOL) v = fp.e1((1.0 + 4.0j)) assert ae(v, (0.03373591813926547318 + 0.073523452241083821877j), tol=ATOL) assert ae(v.real, 0.03373591813926547318, tol=PTOL) assert ae(v.imag, 0.073523452241083821877, tol=PTOL) v = fp.e1((2.0 + 8.0j)) assert ae(v, (-0.015392833434733785143 - 0.0031747121557605415914j), tol=ATOL) assert ae(v.real, -0.015392833434733785143, tol=PTOL) assert ae(v.imag, -0.0031747121557605415914, tol=PTOL) v = fp.e1((5.0 + 20.0j)) assert ae(v, (-0.00024419662286542966525 - 0.00021008322966152755674j), tol=ATOL) assert ae(v.real, -0.00024419662286542966525, tol=PTOL) assert ae(v.imag, -0.00021008322966152755674, tol=PTOL) v = fp.e1((20.0 + 80.0j)) assert ae(v, (2.3255552781051330088e-11 + 8.9463918891349438007e-12j), tol=ATOL) assert ae(v.real, 2.3255552781051330088e-11, tol=PTOL) assert ae(v.imag, 8.9463918891349438007e-12, tol=PTOL) v = fp.e1((30.0 + 120.0j)) assert ae(v, (-2.7068919097124652332e-16 - 7.0477762411705130239e-16j), tol=ATOL) assert ae(v.real, -2.7068919097124652332e-16, tol=PTOL) assert ae(v.imag, -7.0477762411705130239e-16, tol=PTOL) v = fp.e1((40.0 + 160.0j)) assert ae(v, (-1.1695597827678024687e-20 + 2.2907401455645736661e-20j), tol=ATOL) assert ae(v.real, -1.1695597827678024687e-20, tol=PTOL) assert ae(v.imag, 2.2907401455645736661e-20, tol=PTOL) v = fp.e1((50.0 + 200.0j)) assert ae(v, (9.0323746914410162531e-25 - 2.3950601790033530935e-25j), tol=ATOL) assert ae(v.real, 9.0323746914410162531e-25, tol=PTOL) assert ae(v.imag, -2.3950601790033530935e-25, tol=PTOL) v = fp.e1((80.0 + 320.0j)) assert ae(v, (3.4819106748728063576e-38 - 4.215653005615772724e-38j), tol=ATOL) assert ae(v.real, 3.4819106748728063576e-38, tol=PTOL) assert ae(v.imag, -4.215653005615772724e-38, tol=PTOL) v = fp.e1((0.0 + 1.1641532182693481445e-10j)) assert ae(v, (22.29664129357666235 - 1.5707963266784812974j), tol=ATOL) assert ae(v.real, 22.29664129357666235, tol=PTOL) assert ae(v.imag, -1.5707963266784812974, tol=PTOL) v = fp.e1((0.0 + 0.25j)) assert ae(v, (0.82466306258094565309 - 1.3216627564751394551j), tol=ATOL) assert ae(v.real, 0.82466306258094565309, tol=PTOL) assert ae(v.imag, -1.3216627564751394551, tol=PTOL) v = fp.e1((0.0 + 1.0j)) assert ae(v, (-0.33740392290096813466 - 0.62471325642771360429j), tol=ATOL) assert ae(v.real, -0.33740392290096813466, tol=PTOL) assert ae(v.imag, -0.62471325642771360429, tol=PTOL) v = fp.e1((0.0 + 2.0j)) assert ae(v, (-0.4229808287748649957 + 0.034616650007798229345j), tol=ATOL) assert ae(v.real, -0.4229808287748649957, tol=PTOL) assert ae(v.imag, 0.034616650007798229345, tol=PTOL) v = fp.e1((0.0 + 5.0j)) assert ae(v, (0.19002974965664387862 - 0.020865081850222481957j), tol=ATOL) assert ae(v.real, 0.19002974965664387862, tol=PTOL) assert ae(v.imag, -0.020865081850222481957, tol=PTOL) v = fp.e1((0.0 + 20.0j)) assert ae(v, (-0.04441982084535331654 - 0.022554625751456779068j), tol=ATOL) assert ae(v.real, -0.04441982084535331654, tol=PTOL) assert ae(v.imag, -0.022554625751456779068, tol=PTOL) v = fp.e1((0.0 + 30.0j)) assert ae(v, (0.033032417282071143779 - 0.0040397867645455082476j), tol=ATOL) assert ae(v.real, 0.033032417282071143779, tol=PTOL) assert ae(v.imag, -0.0040397867645455082476, tol=PTOL) v = fp.e1((0.0 + 40.0j)) assert ae(v, (-0.019020007896208766962 + 0.016188792559887887544j), tol=ATOL) assert ae(v.real, -0.019020007896208766962, tol=PTOL) assert ae(v.imag, 0.016188792559887887544, tol=PTOL) v = fp.e1((0.0 + 50.0j)) assert ae(v, (0.0056283863241163054402 - 0.019179254308960724503j), tol=ATOL) assert ae(v.real, 0.0056283863241163054402, tol=PTOL) assert ae(v.imag, -0.019179254308960724503, tol=PTOL) v = fp.e1((0.0 + 80.0j)) assert ae(v, (0.012402501155070958192 + 0.0015345601175906961199j), tol=ATOL) assert ae(v.real, 0.012402501155070958192, tol=PTOL) assert ae(v.imag, 0.0015345601175906961199, tol=PTOL) v = fp.e1((-1.1641532182693481445e-10 + 4.6566128730773925781e-10j)) assert ae(v, (20.880034621432138988 - 1.8157749894560994861j), tol=ATOL) assert ae(v.real, 20.880034621432138988, tol=PTOL) assert ae(v.imag, -1.8157749894560994861, tol=PTOL) v = fp.e1((-0.25 + 1.0j)) assert ae(v, (-0.59066621214766308594 - 0.74474454765205036972j), tol=ATOL) assert ae(v.real, -0.59066621214766308594, tol=PTOL) assert ae(v.imag, -0.74474454765205036972, tol=PTOL) v = fp.e1((-1.0 + 4.0j)) assert ae(v, (0.49739047283060471093 + 0.41543605404038863174j), tol=ATOL) assert ae(v.real, 0.49739047283060471093, tol=PTOL) assert ae(v.imag, 0.41543605404038863174, tol=PTOL) v = fp.e1((-2.0 + 8.0j)) assert ae(v, (-0.8705211147733730969 + 0.24099328498605539667j), tol=ATOL) assert ae(v.real, -0.8705211147733730969, tol=PTOL) assert ae(v.imag, 0.24099328498605539667, tol=PTOL) v = fp.e1((-5.0 + 20.0j)) assert ae(v, (-7.0789514293925893007 - 1.6102177171960790536j), tol=ATOL) assert ae(v.real, -7.0789514293925893007, tol=PTOL) assert ae(v.imag, -1.6102177171960790536, tol=PTOL) v = fp.e1((-20.0 + 80.0j)) assert ae(v, (5855431.4907298084434 - 720920.93315409165707j), tol=ATOL) assert ae(v.real, 5855431.4907298084434, tol=PTOL) assert ae(v.imag, -720920.93315409165707, tol=PTOL) def test_fp_stress_ei_e1_2(): # Can be tightened on recent Pythons with more accurate math/cmath ATOL = 1e-13 PTOL = 1e-12 v = fp.e1((-30.0 + 120.0j)) assert ae(v, (-65402491644.703470747 - 56697658399.657460294j), tol=ATOL) assert ae(v.real, -65402491644.703470747, tol=PTOL) assert ae(v.imag, -56697658399.657460294, tol=PTOL) v = fp.e1((-40.0 + 160.0j)) assert ae(v, (25504929379604.776769 + 1429035198630573.2463j), tol=ATOL) assert ae(v.real, 25504929379604.776769, tol=PTOL) assert ae(v.imag, 1429035198630573.2463, tol=PTOL) v = fp.e1((-50.0 + 200.0j)) assert ae(v, (18437746526988116954.0 - 17146362239046152345.0j), tol=ATOL) assert ae(v.real, 18437746526988116954.0, tol=PTOL) assert ae(v.imag, -17146362239046152345.0, tol=PTOL) v = fp.e1((-80.0 + 320.0j)) assert ae(v, (3.3464697299634526706e+31 - 1.6473152633843023919e+32j), tol=ATOL) assert ae(v.real, 3.3464697299634526706e+31, tol=PTOL) assert ae(v.imag, -1.6473152633843023919e+32, tol=PTOL) v = fp.e1((-4.6566128730773925781e-10 + 1.1641532182693481445e-10j)) assert ae(v, (20.880034621082893023 - 2.8966139903465137624j), tol=ATOL) assert ae(v.real, 20.880034621082893023, tol=PTOL) assert ae(v.imag, -2.8966139903465137624, tol=PTOL) v = fp.e1((-1.0 + 0.25j)) assert ae(v, (-1.8942716983721074932 - 2.4689102827070540799j), tol=ATOL) assert ae(v.real, -1.8942716983721074932, tol=PTOL) assert ae(v.imag, -2.4689102827070540799, tol=PTOL) v = fp.e1((-4.0 + 1.0j)) assert ae(v, (-14.806699492675420438 + 9.1384225230837893776j), tol=ATOL) assert ae(v.real, -14.806699492675420438, tol=PTOL) assert ae(v.imag, 9.1384225230837893776, tol=PTOL) v = fp.e1((-8.0 + 2.0j)) assert ae(v, (54.633252667426386294 + 413.20318163814670688j), tol=ATOL) assert ae(v.real, 54.633252667426386294, tol=PTOL) assert ae(v.imag, 413.20318163814670688, tol=PTOL) v = fp.e1((-20.0 + 5.0j)) assert ae(v, (-711836.97165402624643 - 24745250.939695900956j), tol=ATOL) assert ae(v.real, -711836.97165402624643, tol=PTOL) assert ae(v.imag, -24745250.939695900956, tol=PTOL) v = fp.e1((-80.0 + 20.0j)) assert ae(v, (-4.2139911108612653091e+32 + 5.3367124741918251637e+32j), tol=ATOL) assert ae(v.real, -4.2139911108612653091e+32, tol=PTOL) assert ae(v.imag, 5.3367124741918251637e+32, tol=PTOL) v = fp.e1((-120.0 + 30.0j)) assert ae(v, (9.7760616203707508892e+48 - 1.058257682317195792e+50j), tol=ATOL) assert ae(v.real, 9.7760616203707508892e+48, tol=PTOL) assert ae(v.imag, -1.058257682317195792e+50, tol=PTOL) v = fp.e1((-160.0 + 40.0j)) assert ae(v, (8.7065541466623638861e+66 + 1.6577106725141739889e+67j), tol=ATOL) assert ae(v.real, 8.7065541466623638861e+66, tol=PTOL) assert ae(v.imag, 1.6577106725141739889e+67, tol=PTOL) v = fp.e1((-200.0 + 50.0j)) assert ae(v, (-3.070744996327018106e+84 - 1.7243244846769415903e+84j), tol=ATOL) assert ae(v.real, -3.070744996327018106e+84, tol=PTOL) assert ae(v.imag, -1.7243244846769415903e+84, tol=PTOL) v = fp.e1((-320.0 + 80.0j)) assert ae(v, (9.9960598637998647276e+135 - 2.6855081527595608863e+136j), tol=ATOL) assert ae(v.real, 9.9960598637998647276e+135, tol=PTOL) assert ae(v.imag, -2.6855081527595608863e+136, tol=PTOL) v = fp.e1(-1.1641532182693481445e-10) assert ae(v, (22.296641293460247028 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, 22.296641293460247028, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1(-0.25) assert ae(v, (0.54254326466191372953 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, 0.54254326466191372953, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1(-1.0) assert ae(v, (-1.8951178163559367555 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -1.8951178163559367555, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1(-2.0) assert ae(v, (-4.9542343560018901634 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -4.9542343560018901634, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1(-5.0) assert ae(v, (-40.185275355803177455 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -40.185275355803177455, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1(-20.0) assert ae(v, (-25615652.66405658882 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -25615652.66405658882, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1(-30.0) assert ae(v, (-368973209407.27419706 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -368973209407.27419706, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1(-40.0) assert ae(v, (-6039718263611241.5784 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -6039718263611241.5784, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1(-50.0) assert ae(v, (-1.0585636897131690963e+20 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -1.0585636897131690963e+20, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1(-80.0) assert ae(v, (-7.0146000049047999696e+32 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -7.0146000049047999696e+32, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1((-1.1641532182693481445e-10 + 0.0j)) assert ae(v, (22.296641293460247028 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, 22.296641293460247028, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1((-0.25 + 0.0j)) assert ae(v, (0.54254326466191372953 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, 0.54254326466191372953, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1((-1.0 + 0.0j)) assert ae(v, (-1.8951178163559367555 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -1.8951178163559367555, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1((-2.0 + 0.0j)) assert ae(v, (-4.9542343560018901634 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -4.9542343560018901634, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1((-5.0 + 0.0j)) assert ae(v, (-40.185275355803177455 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -40.185275355803177455, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1((-20.0 + 0.0j)) assert ae(v, (-25615652.66405658882 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -25615652.66405658882, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1((-30.0 + 0.0j)) assert ae(v, (-368973209407.27419706 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -368973209407.27419706, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1((-40.0 + 0.0j)) assert ae(v, (-6039718263611241.5784 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -6039718263611241.5784, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1((-50.0 + 0.0j)) assert ae(v, (-1.0585636897131690963e+20 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -1.0585636897131690963e+20, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1((-80.0 + 0.0j)) assert ae(v, (-7.0146000049047999696e+32 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -7.0146000049047999696e+32, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1((-4.6566128730773925781e-10 - 1.1641532182693481445e-10j)) assert ae(v, (20.880034621082893023 + 2.8966139903465137624j), tol=ATOL) assert ae(v.real, 20.880034621082893023, tol=PTOL) assert ae(v.imag, 2.8966139903465137624, tol=PTOL) v = fp.e1((-1.0 - 0.25j)) assert ae(v, (-1.8942716983721074932 + 2.4689102827070540799j), tol=ATOL) assert ae(v.real, -1.8942716983721074932, tol=PTOL) assert ae(v.imag, 2.4689102827070540799, tol=PTOL) v = fp.e1((-4.0 - 1.0j)) assert ae(v, (-14.806699492675420438 - 9.1384225230837893776j), tol=ATOL) assert ae(v.real, -14.806699492675420438, tol=PTOL) assert ae(v.imag, -9.1384225230837893776, tol=PTOL) v = fp.e1((-8.0 - 2.0j)) assert ae(v, (54.633252667426386294 - 413.20318163814670688j), tol=ATOL) assert ae(v.real, 54.633252667426386294, tol=PTOL) assert ae(v.imag, -413.20318163814670688, tol=PTOL) v = fp.e1((-20.0 - 5.0j)) assert ae(v, (-711836.97165402624643 + 24745250.939695900956j), tol=ATOL) assert ae(v.real, -711836.97165402624643, tol=PTOL) assert ae(v.imag, 24745250.939695900956, tol=PTOL) v = fp.e1((-80.0 - 20.0j)) assert ae(v, (-4.2139911108612653091e+32 - 5.3367124741918251637e+32j), tol=ATOL) assert ae(v.real, -4.2139911108612653091e+32, tol=PTOL) assert ae(v.imag, -5.3367124741918251637e+32, tol=PTOL) v = fp.e1((-120.0 - 30.0j)) assert ae(v, (9.7760616203707508892e+48 + 1.058257682317195792e+50j), tol=ATOL) assert ae(v.real, 9.7760616203707508892e+48, tol=PTOL) assert ae(v.imag, 1.058257682317195792e+50, tol=PTOL) v = fp.e1((-160.0 - 40.0j)) assert ae(v, (8.7065541466623638861e+66 - 1.6577106725141739889e+67j), tol=ATOL) assert ae(v.real, 8.7065541466623638861e+66, tol=PTOL) assert ae(v.imag, -1.6577106725141739889e+67, tol=PTOL) v = fp.e1((-200.0 - 50.0j)) assert ae(v, (-3.070744996327018106e+84 + 1.7243244846769415903e+84j), tol=ATOL) assert ae(v.real, -3.070744996327018106e+84, tol=PTOL) assert ae(v.imag, 1.7243244846769415903e+84, tol=PTOL) v = fp.e1((-320.0 - 80.0j)) assert ae(v, (9.9960598637998647276e+135 + 2.6855081527595608863e+136j), tol=ATOL) assert ae(v.real, 9.9960598637998647276e+135, tol=PTOL) assert ae(v.imag, 2.6855081527595608863e+136, tol=PTOL) v = fp.e1((-1.1641532182693481445e-10 - 1.1641532182693481445e-10j)) assert ae(v, (21.950067703180274374 + 2.356194490075929607j), tol=ATOL) assert ae(v.real, 21.950067703180274374, tol=PTOL) assert ae(v.imag, 2.356194490075929607, tol=PTOL) v = fp.e1((-0.25 - 0.25j)) assert ae(v, (0.21441047326710323254 + 2.0732153554307936389j), tol=ATOL) assert ae(v.real, 0.21441047326710323254, tol=PTOL) assert ae(v.imag, 2.0732153554307936389, tol=PTOL) v = fp.e1((-1.0 - 1.0j)) assert ae(v, (-1.7646259855638540684 + 0.7538228020792708192j), tol=ATOL) assert ae(v.real, -1.7646259855638540684, tol=PTOL) assert ae(v.imag, 0.7538228020792708192, tol=PTOL) v = fp.e1((-2.0 - 2.0j)) assert ae(v, (-1.8920781621855474089 - 2.1753697842428647236j), tol=ATOL) assert ae(v.real, -1.8920781621855474089, tol=PTOL) assert ae(v.imag, -2.1753697842428647236, tol=PTOL) v = fp.e1((-5.0 - 5.0j)) assert ae(v, (13.470936071475245856 + 18.464085049321024206j), tol=ATOL) assert ae(v.real, 13.470936071475245856, tol=PTOL) assert ae(v.imag, 18.464085049321024206, tol=PTOL) v = fp.e1((-20.0 - 20.0j)) assert ae(v, (-16589317.398788971896 - 5831702.3296441771206j), tol=ATOL) assert ae(v.real, -16589317.398788971896, tol=PTOL) assert ae(v.imag, -5831702.3296441771206, tol=PTOL) v = fp.e1((-30.0 - 30.0j)) assert ae(v, (154596484273.69322527 + 204179357837.41389696j), tol=ATOL) assert ae(v.real, 154596484273.69322527, tol=PTOL) assert ae(v.imag, 204179357837.41389696, tol=PTOL) v = fp.e1((-40.0 - 40.0j)) assert ae(v, (-287512180321448.45408 - 4203502407932314.974j), tol=ATOL) assert ae(v.real, -287512180321448.45408, tol=PTOL) assert ae(v.imag, -4203502407932314.974, tol=PTOL) v = fp.e1((-50.0 - 50.0j)) assert ae(v, (-36128528616649268826.0 + 64648801861338741963.0j), tol=ATOL) assert ae(v.real, -36128528616649268826.0, tol=PTOL) assert ae(v.imag, 64648801861338741963.0, tol=PTOL) v = fp.e1((-80.0 - 80.0j)) assert ae(v, (3.8674816337930010217e+32 + 3.0540709639658071041e+32j), tol=ATOL) assert ae(v.real, 3.8674816337930010217e+32, tol=PTOL) assert ae(v.imag, 3.0540709639658071041e+32, tol=PTOL) v = fp.e1((-1.1641532182693481445e-10 - 4.6566128730773925781e-10j)) assert ae(v, (20.880034621432138988 + 1.8157749894560994861j), tol=ATOL) assert ae(v.real, 20.880034621432138988, tol=PTOL) assert ae(v.imag, 1.8157749894560994861, tol=PTOL) v = fp.e1((-0.25 - 1.0j)) assert ae(v, (-0.59066621214766308594 + 0.74474454765205036972j), tol=ATOL) assert ae(v.real, -0.59066621214766308594, tol=PTOL) assert ae(v.imag, 0.74474454765205036972, tol=PTOL) v = fp.e1((-1.0 - 4.0j)) assert ae(v, (0.49739047283060471093 - 0.41543605404038863174j), tol=ATOL) assert ae(v.real, 0.49739047283060471093, tol=PTOL) assert ae(v.imag, -0.41543605404038863174, tol=PTOL) v = fp.e1((-2.0 - 8.0j)) assert ae(v, (-0.8705211147733730969 - 0.24099328498605539667j), tol=ATOL) assert ae(v.real, -0.8705211147733730969, tol=PTOL) assert ae(v.imag, -0.24099328498605539667, tol=PTOL) v = fp.e1((-5.0 - 20.0j)) assert ae(v, (-7.0789514293925893007 + 1.6102177171960790536j), tol=ATOL) assert ae(v.real, -7.0789514293925893007, tol=PTOL) assert ae(v.imag, 1.6102177171960790536, tol=PTOL) v = fp.e1((-20.0 - 80.0j)) assert ae(v, (5855431.4907298084434 + 720920.93315409165707j), tol=ATOL) assert ae(v.real, 5855431.4907298084434, tol=PTOL) assert ae(v.imag, 720920.93315409165707, tol=PTOL) v = fp.e1((-30.0 - 120.0j)) assert ae(v, (-65402491644.703470747 + 56697658399.657460294j), tol=ATOL) assert ae(v.real, -65402491644.703470747, tol=PTOL) assert ae(v.imag, 56697658399.657460294, tol=PTOL) v = fp.e1((-40.0 - 160.0j)) assert ae(v, (25504929379604.776769 - 1429035198630573.2463j), tol=ATOL) assert ae(v.real, 25504929379604.776769, tol=PTOL) assert ae(v.imag, -1429035198630573.2463, tol=PTOL) v = fp.e1((-50.0 - 200.0j)) assert ae(v, (18437746526988116954.0 + 17146362239046152345.0j), tol=ATOL) assert ae(v.real, 18437746526988116954.0, tol=PTOL) assert ae(v.imag, 17146362239046152345.0, tol=PTOL) v = fp.e1((-80.0 - 320.0j)) assert ae(v, (3.3464697299634526706e+31 + 1.6473152633843023919e+32j), tol=ATOL) assert ae(v.real, 3.3464697299634526706e+31, tol=PTOL) assert ae(v.imag, 1.6473152633843023919e+32, tol=PTOL) v = fp.e1((0.0 - 1.1641532182693481445e-10j)) assert ae(v, (22.29664129357666235 + 1.5707963266784812974j), tol=ATOL) assert ae(v.real, 22.29664129357666235, tol=PTOL) assert ae(v.imag, 1.5707963266784812974, tol=PTOL) v = fp.e1((0.0 - 0.25j)) assert ae(v, (0.82466306258094565309 + 1.3216627564751394551j), tol=ATOL) assert ae(v.real, 0.82466306258094565309, tol=PTOL) assert ae(v.imag, 1.3216627564751394551, tol=PTOL) v = fp.e1((0.0 - 1.0j)) assert ae(v, (-0.33740392290096813466 + 0.62471325642771360429j), tol=ATOL) assert ae(v.real, -0.33740392290096813466, tol=PTOL) assert ae(v.imag, 0.62471325642771360429, tol=PTOL) v = fp.e1((0.0 - 2.0j)) assert ae(v, (-0.4229808287748649957 - 0.034616650007798229345j), tol=ATOL) assert ae(v.real, -0.4229808287748649957, tol=PTOL) assert ae(v.imag, -0.034616650007798229345, tol=PTOL) v = fp.e1((0.0 - 5.0j)) assert ae(v, (0.19002974965664387862 + 0.020865081850222481957j), tol=ATOL) assert ae(v.real, 0.19002974965664387862, tol=PTOL) assert ae(v.imag, 0.020865081850222481957, tol=PTOL) v = fp.e1((0.0 - 20.0j)) assert ae(v, (-0.04441982084535331654 + 0.022554625751456779068j), tol=ATOL) assert ae(v.real, -0.04441982084535331654, tol=PTOL) assert ae(v.imag, 0.022554625751456779068, tol=PTOL) v = fp.e1((0.0 - 30.0j)) assert ae(v, (0.033032417282071143779 + 0.0040397867645455082476j), tol=ATOL) assert ae(v.real, 0.033032417282071143779, tol=PTOL) assert ae(v.imag, 0.0040397867645455082476, tol=PTOL) v = fp.e1((0.0 - 40.0j)) assert ae(v, (-0.019020007896208766962 - 0.016188792559887887544j), tol=ATOL) assert ae(v.real, -0.019020007896208766962, tol=PTOL) assert ae(v.imag, -0.016188792559887887544, tol=PTOL) v = fp.e1((0.0 - 50.0j)) assert ae(v, (0.0056283863241163054402 + 0.019179254308960724503j), tol=ATOL) assert ae(v.real, 0.0056283863241163054402, tol=PTOL) assert ae(v.imag, 0.019179254308960724503, tol=PTOL) v = fp.e1((0.0 - 80.0j)) assert ae(v, (0.012402501155070958192 - 0.0015345601175906961199j), tol=ATOL) assert ae(v.real, 0.012402501155070958192, tol=PTOL) assert ae(v.imag, -0.0015345601175906961199, tol=PTOL) v = fp.e1((1.1641532182693481445e-10 - 4.6566128730773925781e-10j)) assert ae(v, (20.880034621664969632 + 1.3258176632023711778j), tol=ATOL) assert ae(v.real, 20.880034621664969632, tol=PTOL) assert ae(v.imag, 1.3258176632023711778, tol=PTOL) v = fp.e1((0.25 - 1.0j)) assert ae(v, (-0.16868306393667788761 + 0.4858011885947426971j), tol=ATOL) assert ae(v.real, -0.16868306393667788761, tol=PTOL) assert ae(v.imag, 0.4858011885947426971, tol=PTOL) v = fp.e1((1.0 - 4.0j)) assert ae(v, (0.03373591813926547318 - 0.073523452241083821877j), tol=ATOL) assert ae(v.real, 0.03373591813926547318, tol=PTOL) assert ae(v.imag, -0.073523452241083821877, tol=PTOL) v = fp.e1((2.0 - 8.0j)) assert ae(v, (-0.015392833434733785143 + 0.0031747121557605415914j), tol=ATOL) assert ae(v.real, -0.015392833434733785143, tol=PTOL) assert ae(v.imag, 0.0031747121557605415914, tol=PTOL) v = fp.e1((5.0 - 20.0j)) assert ae(v, (-0.00024419662286542966525 + 0.00021008322966152755674j), tol=ATOL) assert ae(v.real, -0.00024419662286542966525, tol=PTOL) assert ae(v.imag, 0.00021008322966152755674, tol=PTOL) v = fp.e1((20.0 - 80.0j)) assert ae(v, (2.3255552781051330088e-11 - 8.9463918891349438007e-12j), tol=ATOL) assert ae(v.real, 2.3255552781051330088e-11, tol=PTOL) assert ae(v.imag, -8.9463918891349438007e-12, tol=PTOL) v = fp.e1((30.0 - 120.0j)) assert ae(v, (-2.7068919097124652332e-16 + 7.0477762411705130239e-16j), tol=ATOL) assert ae(v.real, -2.7068919097124652332e-16, tol=PTOL) assert ae(v.imag, 7.0477762411705130239e-16, tol=PTOL) v = fp.e1((40.0 - 160.0j)) assert ae(v, (-1.1695597827678024687e-20 - 2.2907401455645736661e-20j), tol=ATOL) assert ae(v.real, -1.1695597827678024687e-20, tol=PTOL) assert ae(v.imag, -2.2907401455645736661e-20, tol=PTOL) v = fp.e1((50.0 - 200.0j)) assert ae(v, (9.0323746914410162531e-25 + 2.3950601790033530935e-25j), tol=ATOL) assert ae(v.real, 9.0323746914410162531e-25, tol=PTOL) assert ae(v.imag, 2.3950601790033530935e-25, tol=PTOL) v = fp.e1((80.0 - 320.0j)) assert ae(v, (3.4819106748728063576e-38 + 4.215653005615772724e-38j), tol=ATOL) assert ae(v.real, 3.4819106748728063576e-38, tol=PTOL) assert ae(v.imag, 4.215653005615772724e-38, tol=PTOL) v = fp.e1((1.1641532182693481445e-10 - 1.1641532182693481445e-10j)) assert ae(v, (21.950067703413105017 + 0.7853981632810329878j), tol=ATOL) assert ae(v.real, 21.950067703413105017, tol=PTOL) assert ae(v.imag, 0.7853981632810329878, tol=PTOL) v = fp.e1((0.25 - 0.25j)) assert ae(v, (0.71092525792923287894 + 0.56491812441304194711j), tol=ATOL) assert ae(v.real, 0.71092525792923287894, tol=PTOL) assert ae(v.imag, 0.56491812441304194711, tol=PTOL) v = fp.e1((1.0 - 1.0j)) assert ae(v, (0.00028162445198141832551 + 0.17932453503935894015j), tol=ATOL) assert ae(v.real, 0.00028162445198141832551, tol=PTOL) assert ae(v.imag, 0.17932453503935894015, tol=PTOL) v = fp.e1((2.0 - 2.0j)) assert ae(v, (-0.033767089606562004246 + 0.018599414169750541925j), tol=ATOL) assert ae(v.real, -0.033767089606562004246, tol=PTOL) assert ae(v.imag, 0.018599414169750541925, tol=PTOL) v = fp.e1((5.0 - 5.0j)) assert ae(v, (0.0007266506660356393891 - 0.00047102780163522245054j), tol=ATOL) assert ae(v.real, 0.0007266506660356393891, tol=PTOL) assert ae(v.imag, -0.00047102780163522245054, tol=PTOL) v = fp.e1((20.0 - 20.0j)) assert ae(v, (-2.3824537449367396579e-11 + 6.6969873156525615158e-11j), tol=ATOL) assert ae(v.real, -2.3824537449367396579e-11, tol=PTOL) assert ae(v.imag, 6.6969873156525615158e-11, tol=PTOL) v = fp.e1((30.0 - 30.0j)) assert ae(v, (1.7316045841744061617e-15 - 1.3065678019487308689e-15j), tol=ATOL) assert ae(v.real, 1.7316045841744061617e-15, tol=PTOL) assert ae(v.imag, -1.3065678019487308689e-15, tol=PTOL) v = fp.e1((40.0 - 40.0j)) assert ae(v, (-7.4001043002899232182e-20 + 4.991847855336816304e-21j), tol=ATOL) assert ae(v.real, -7.4001043002899232182e-20, tol=PTOL) assert ae(v.imag, 4.991847855336816304e-21, tol=PTOL) v = fp.e1((50.0 - 50.0j)) assert ae(v, (2.3566128324644641219e-24 + 1.3188326726201614778e-24j), tol=ATOL) assert ae(v.real, 2.3566128324644641219e-24, tol=PTOL) assert ae(v.imag, 1.3188326726201614778e-24, tol=PTOL) v = fp.e1((80.0 - 80.0j)) assert ae(v, (9.8279750572186526673e-38 - 1.243952841288868831e-37j), tol=ATOL) assert ae(v.real, 9.8279750572186526673e-38, tol=PTOL) assert ae(v.imag, -1.243952841288868831e-37, tol=PTOL) v = fp.e1((4.6566128730773925781e-10 - 1.1641532182693481445e-10j)) assert ae(v, (20.880034622014215597 + 0.24497866301044883237j), tol=ATOL) assert ae(v.real, 20.880034622014215597, tol=PTOL) assert ae(v.imag, 0.24497866301044883237, tol=PTOL) v = fp.e1((1.0 - 0.25j)) assert ae(v, (0.19731063945004229095 + 0.087366045774299963672j), tol=ATOL) assert ae(v.real, 0.19731063945004229095, tol=PTOL) assert ae(v.imag, 0.087366045774299963672, tol=PTOL) v = fp.e1((4.0 - 1.0j)) assert ae(v, (0.0013106173980145506944 + 0.0034542480199350626699j), tol=ATOL) assert ae(v.real, 0.0013106173980145506944, tol=PTOL) assert ae(v.imag, 0.0034542480199350626699, tol=PTOL) v = fp.e1((8.0 - 2.0j)) assert ae(v, (-0.000022278049065270225945 + 0.000029191940456521555288j), tol=ATOL) assert ae(v.real, -0.000022278049065270225945, tol=PTOL) assert ae(v.imag, 0.000029191940456521555288, tol=PTOL) v = fp.e1((20.0 - 5.0j)) assert ae(v, (4.7711374515765346894e-11 - 8.2902652405126947359e-11j), tol=ATOL) assert ae(v.real, 4.7711374515765346894e-11, tol=PTOL) assert ae(v.imag, -8.2902652405126947359e-11, tol=PTOL) v = fp.e1((80.0 - 20.0j)) assert ae(v, (3.8353473865788235787e-38 + 2.129247592349605139e-37j), tol=ATOL) assert ae(v.real, 3.8353473865788235787e-38, tol=PTOL) assert ae(v.imag, 2.129247592349605139e-37, tol=PTOL) v = fp.e1((120.0 - 30.0j)) assert ae(v, (2.3836002337480334716e-55 - 5.6704043587126198306e-55j), tol=ATOL) assert ae(v.real, 2.3836002337480334716e-55, tol=PTOL) assert ae(v.imag, -5.6704043587126198306e-55, tol=PTOL) v = fp.e1((160.0 - 40.0j)) assert ae(v, (-1.6238022898654510661e-72 + 1.104172355572287367e-72j), tol=ATOL) assert ae(v.real, -1.6238022898654510661e-72, tol=PTOL) assert ae(v.imag, 1.104172355572287367e-72, tol=PTOL) v = fp.e1((200.0 - 50.0j)) assert ae(v, (6.6800061461666228487e-90 - 1.4473816083541016115e-91j), tol=ATOL) assert ae(v.real, 6.6800061461666228487e-90, tol=PTOL) assert ae(v.imag, -1.4473816083541016115e-91, tol=PTOL) v = fp.e1((320.0 - 80.0j)) assert ae(v, (4.2737871527778786157e-143 - 3.1789935525785660314e-142j), tol=ATOL) assert ae(v.real, 4.2737871527778786157e-143, tol=PTOL) assert ae(v.imag, -3.1789935525785660314e-142, tol=PTOL) v = fp.ei(1.1641532182693481445e-10) assert ae(v, -22.296641293460247028, tol=ATOL) assert type(v) is float def test_fp_stress_ei_e1_3(): # Can be tightened on recent Pythons with more accurate math/cmath ATOL = 1e-13 PTOL = 1e-12 v = fp.ei(0.25) assert ae(v, -0.54254326466191372953, tol=ATOL) assert type(v) is float v = fp.ei(1.0) assert ae(v, 1.8951178163559367555, tol=ATOL) assert type(v) is float v = fp.ei(2.0) assert ae(v, 4.9542343560018901634, tol=ATOL) assert type(v) is float v = fp.ei(5.0) assert ae(v, 40.185275355803177455, tol=ATOL) assert type(v) is float v = fp.ei(20.0) assert ae(v, 25615652.66405658882, tol=ATOL) assert type(v) is float v = fp.ei(30.0) assert ae(v, 368973209407.27419706, tol=ATOL) assert type(v) is float v = fp.ei(40.0) assert ae(v, 6039718263611241.5784, tol=ATOL) assert type(v) is float v = fp.ei(50.0) assert ae(v, 1.0585636897131690963e+20, tol=ATOL) assert type(v) is float v = fp.ei(80.0) assert ae(v, 7.0146000049047999696e+32, tol=ATOL) assert type(v) is float v = fp.ei((1.1641532182693481445e-10 + 0.0j)) assert ae(v, (-22.296641293460247028 + 0.0j), tol=ATOL) assert ae(v.real, -22.296641293460247028, tol=PTOL) assert v.imag == 0 v = fp.ei((0.25 + 0.0j)) assert ae(v, (-0.54254326466191372953 + 0.0j), tol=ATOL) assert ae(v.real, -0.54254326466191372953, tol=PTOL) assert v.imag == 0 v = fp.ei((1.0 + 0.0j)) assert ae(v, (1.8951178163559367555 + 0.0j), tol=ATOL) assert ae(v.real, 1.8951178163559367555, tol=PTOL) assert v.imag == 0 v = fp.ei((2.0 + 0.0j)) assert ae(v, (4.9542343560018901634 + 0.0j), tol=ATOL) assert ae(v.real, 4.9542343560018901634, tol=PTOL) assert v.imag == 0 v = fp.ei((5.0 + 0.0j)) assert ae(v, (40.185275355803177455 + 0.0j), tol=ATOL) assert ae(v.real, 40.185275355803177455, tol=PTOL) assert v.imag == 0 v = fp.ei((20.0 + 0.0j)) assert ae(v, (25615652.66405658882 + 0.0j), tol=ATOL) assert ae(v.real, 25615652.66405658882, tol=PTOL) assert v.imag == 0 v = fp.ei((30.0 + 0.0j)) assert ae(v, (368973209407.27419706 + 0.0j), tol=ATOL) assert ae(v.real, 368973209407.27419706, tol=PTOL) assert v.imag == 0 v = fp.ei((40.0 + 0.0j)) assert ae(v, (6039718263611241.5784 + 0.0j), tol=ATOL) assert ae(v.real, 6039718263611241.5784, tol=PTOL) assert v.imag == 0 v = fp.ei((50.0 + 0.0j)) assert ae(v, (1.0585636897131690963e+20 + 0.0j), tol=ATOL) assert ae(v.real, 1.0585636897131690963e+20, tol=PTOL) assert v.imag == 0 v = fp.ei((80.0 + 0.0j)) assert ae(v, (7.0146000049047999696e+32 + 0.0j), tol=ATOL) assert ae(v.real, 7.0146000049047999696e+32, tol=PTOL) assert v.imag == 0 v = fp.ei((4.6566128730773925781e-10 + 1.1641532182693481445e-10j)) assert ae(v, (-20.880034621082893023 + 0.24497866324327947603j), tol=ATOL) assert ae(v.real, -20.880034621082893023, tol=PTOL) assert ae(v.imag, 0.24497866324327947603, tol=PTOL) v = fp.ei((1.0 + 0.25j)) assert ae(v, (1.8942716983721074932 + 0.67268237088273915854j), tol=ATOL) assert ae(v.real, 1.8942716983721074932, tol=PTOL) assert ae(v.imag, 0.67268237088273915854, tol=PTOL) v = fp.ei((4.0 + 1.0j)) assert ae(v, (14.806699492675420438 + 12.280015176673582616j), tol=ATOL) assert ae(v.real, 14.806699492675420438, tol=PTOL) assert ae(v.imag, 12.280015176673582616, tol=PTOL) v = fp.ei((8.0 + 2.0j)) assert ae(v, (-54.633252667426386294 + 416.34477429173650012j), tol=ATOL) assert ae(v.real, -54.633252667426386294, tol=PTOL) assert ae(v.imag, 416.34477429173650012, tol=PTOL) v = fp.ei((20.0 + 5.0j)) assert ae(v, (711836.97165402624643 - 24745247.798103247366j), tol=ATOL) assert ae(v.real, 711836.97165402624643, tol=PTOL) assert ae(v.imag, -24745247.798103247366, tol=PTOL) v = fp.ei((80.0 + 20.0j)) assert ae(v, (4.2139911108612653091e+32 + 5.3367124741918251637e+32j), tol=ATOL) assert ae(v.real, 4.2139911108612653091e+32, tol=PTOL) assert ae(v.imag, 5.3367124741918251637e+32, tol=PTOL) v = fp.ei((120.0 + 30.0j)) assert ae(v, (-9.7760616203707508892e+48 - 1.058257682317195792e+50j), tol=ATOL) assert ae(v.real, -9.7760616203707508892e+48, tol=PTOL) assert ae(v.imag, -1.058257682317195792e+50, tol=PTOL) v = fp.ei((160.0 + 40.0j)) assert ae(v, (-8.7065541466623638861e+66 + 1.6577106725141739889e+67j), tol=ATOL) assert ae(v.real, -8.7065541466623638861e+66, tol=PTOL) assert ae(v.imag, 1.6577106725141739889e+67, tol=PTOL) v = fp.ei((200.0 + 50.0j)) assert ae(v, (3.070744996327018106e+84 - 1.7243244846769415903e+84j), tol=ATOL) assert ae(v.real, 3.070744996327018106e+84, tol=PTOL) assert ae(v.imag, -1.7243244846769415903e+84, tol=PTOL) v = fp.ei((320.0 + 80.0j)) assert ae(v, (-9.9960598637998647276e+135 - 2.6855081527595608863e+136j), tol=ATOL) assert ae(v.real, -9.9960598637998647276e+135, tol=PTOL) assert ae(v.imag, -2.6855081527595608863e+136, tol=PTOL) v = fp.ei((1.1641532182693481445e-10 + 1.1641532182693481445e-10j)) assert ae(v, (-21.950067703180274374 + 0.78539816351386363145j), tol=ATOL) assert ae(v.real, -21.950067703180274374, tol=PTOL) assert ae(v.imag, 0.78539816351386363145, tol=PTOL) v = fp.ei((0.25 + 0.25j)) assert ae(v, (-0.21441047326710323254 + 1.0683772981589995996j), tol=ATOL) assert ae(v.real, -0.21441047326710323254, tol=PTOL) assert ae(v.imag, 1.0683772981589995996, tol=PTOL) v = fp.ei((1.0 + 1.0j)) assert ae(v, (1.7646259855638540684 + 2.3877698515105224193j), tol=ATOL) assert ae(v.real, 1.7646259855638540684, tol=PTOL) assert ae(v.imag, 2.3877698515105224193, tol=PTOL) v = fp.ei((2.0 + 2.0j)) assert ae(v, (1.8920781621855474089 + 5.3169624378326579621j), tol=ATOL) assert ae(v.real, 1.8920781621855474089, tol=PTOL) assert ae(v.imag, 5.3169624378326579621, tol=PTOL) v = fp.ei((5.0 + 5.0j)) assert ae(v, (-13.470936071475245856 - 15.322492395731230968j), tol=ATOL) assert ae(v.real, -13.470936071475245856, tol=PTOL) assert ae(v.imag, -15.322492395731230968, tol=PTOL) v = fp.ei((20.0 + 20.0j)) assert ae(v, (16589317.398788971896 + 5831705.4712368307104j), tol=ATOL) assert ae(v.real, 16589317.398788971896, tol=PTOL) assert ae(v.imag, 5831705.4712368307104, tol=PTOL) v = fp.ei((30.0 + 30.0j)) assert ae(v, (-154596484273.69322527 - 204179357834.2723043j), tol=ATOL) assert ae(v.real, -154596484273.69322527, tol=PTOL) assert ae(v.imag, -204179357834.2723043, tol=PTOL) v = fp.ei((40.0 + 40.0j)) assert ae(v, (287512180321448.45408 + 4203502407932318.1156j), tol=ATOL) assert ae(v.real, 287512180321448.45408, tol=PTOL) assert ae(v.imag, 4203502407932318.1156, tol=PTOL) v = fp.ei((50.0 + 50.0j)) assert ae(v, (36128528616649268826.0 - 64648801861338741960.0j), tol=ATOL) assert ae(v.real, 36128528616649268826.0, tol=PTOL) assert ae(v.imag, -64648801861338741960.0, tol=PTOL) v = fp.ei((80.0 + 80.0j)) assert ae(v, (-3.8674816337930010217e+32 - 3.0540709639658071041e+32j), tol=ATOL) assert ae(v.real, -3.8674816337930010217e+32, tol=PTOL) assert ae(v.imag, -3.0540709639658071041e+32, tol=PTOL) v = fp.ei((1.1641532182693481445e-10 + 4.6566128730773925781e-10j)) assert ae(v, (-20.880034621432138988 + 1.3258176641336937524j), tol=ATOL) assert ae(v.real, -20.880034621432138988, tol=PTOL) assert ae(v.imag, 1.3258176641336937524, tol=PTOL) v = fp.ei((0.25 + 1.0j)) assert ae(v, (0.59066621214766308594 + 2.3968481059377428687j), tol=ATOL) assert ae(v.real, 0.59066621214766308594, tol=PTOL) assert ae(v.imag, 2.3968481059377428687, tol=PTOL) v = fp.ei((1.0 + 4.0j)) assert ae(v, (-0.49739047283060471093 + 3.5570287076301818702j), tol=ATOL) assert ae(v.real, -0.49739047283060471093, tol=PTOL) assert ae(v.imag, 3.5570287076301818702, tol=PTOL) v = fp.ei((2.0 + 8.0j)) assert ae(v, (0.8705211147733730969 + 3.3825859385758486351j), tol=ATOL) assert ae(v.real, 0.8705211147733730969, tol=PTOL) assert ae(v.imag, 3.3825859385758486351, tol=PTOL) v = fp.ei((5.0 + 20.0j)) assert ae(v, (7.0789514293925893007 + 1.5313749363937141849j), tol=ATOL) assert ae(v.real, 7.0789514293925893007, tol=PTOL) assert ae(v.imag, 1.5313749363937141849, tol=PTOL) v = fp.ei((20.0 + 80.0j)) assert ae(v, (-5855431.4907298084434 - 720917.79156143806727j), tol=ATOL) assert ae(v.real, -5855431.4907298084434, tol=PTOL) assert ae(v.imag, -720917.79156143806727, tol=PTOL) v = fp.ei((30.0 + 120.0j)) assert ae(v, (65402491644.703470747 - 56697658396.51586764j), tol=ATOL) assert ae(v.real, 65402491644.703470747, tol=PTOL) assert ae(v.imag, -56697658396.51586764, tol=PTOL) v = fp.ei((40.0 + 160.0j)) assert ae(v, (-25504929379604.776769 + 1429035198630576.3879j), tol=ATOL) assert ae(v.real, -25504929379604.776769, tol=PTOL) assert ae(v.imag, 1429035198630576.3879, tol=PTOL) v = fp.ei((50.0 + 200.0j)) assert ae(v, (-18437746526988116954.0 - 17146362239046152342.0j), tol=ATOL) assert ae(v.real, -18437746526988116954.0, tol=PTOL) assert ae(v.imag, -17146362239046152342.0, tol=PTOL) v = fp.ei((80.0 + 320.0j)) assert ae(v, (-3.3464697299634526706e+31 - 1.6473152633843023919e+32j), tol=ATOL) assert ae(v.real, -3.3464697299634526706e+31, tol=PTOL) assert ae(v.imag, -1.6473152633843023919e+32, tol=PTOL) v = fp.ei((0.0 + 1.1641532182693481445e-10j)) assert ae(v, (-22.29664129357666235 + 1.5707963269113119411j), tol=ATOL) assert ae(v.real, -22.29664129357666235, tol=PTOL) assert ae(v.imag, 1.5707963269113119411, tol=PTOL) v = fp.ei((0.0 + 0.25j)) assert ae(v, (-0.82466306258094565309 + 1.8199298971146537833j), tol=ATOL) assert ae(v.real, -0.82466306258094565309, tol=PTOL) assert ae(v.imag, 1.8199298971146537833, tol=PTOL) v = fp.ei((0.0 + 1.0j)) assert ae(v, (0.33740392290096813466 + 2.5168793971620796342j), tol=ATOL) assert ae(v.real, 0.33740392290096813466, tol=PTOL) assert ae(v.imag, 2.5168793971620796342, tol=PTOL) v = fp.ei((0.0 + 2.0j)) assert ae(v, (0.4229808287748649957 + 3.1762093035975914678j), tol=ATOL) assert ae(v.real, 0.4229808287748649957, tol=PTOL) assert ae(v.imag, 3.1762093035975914678, tol=PTOL) v = fp.ei((0.0 + 5.0j)) assert ae(v, (-0.19002974965664387862 + 3.1207275717395707565j), tol=ATOL) assert ae(v.real, -0.19002974965664387862, tol=PTOL) assert ae(v.imag, 3.1207275717395707565, tol=PTOL) v = fp.ei((0.0 + 20.0j)) assert ae(v, (0.04441982084535331654 + 3.1190380278383364594j), tol=ATOL) assert ae(v.real, 0.04441982084535331654, tol=PTOL) assert ae(v.imag, 3.1190380278383364594, tol=PTOL) v = fp.ei((0.0 + 30.0j)) assert ae(v, (-0.033032417282071143779 + 3.1375528668252477302j), tol=ATOL) assert ae(v.real, -0.033032417282071143779, tol=PTOL) assert ae(v.imag, 3.1375528668252477302, tol=PTOL) v = fp.ei((0.0 + 40.0j)) assert ae(v, (0.019020007896208766962 + 3.157781446149681126j), tol=ATOL) assert ae(v.real, 0.019020007896208766962, tol=PTOL) assert ae(v.imag, 3.157781446149681126, tol=PTOL) v = fp.ei((0.0 + 50.0j)) assert ae(v, (-0.0056283863241163054402 + 3.122413399280832514j), tol=ATOL) assert ae(v.real, -0.0056283863241163054402, tol=PTOL) assert ae(v.imag, 3.122413399280832514, tol=PTOL) v = fp.ei((0.0 + 80.0j)) assert ae(v, (-0.012402501155070958192 + 3.1431272137073839346j), tol=ATOL) assert ae(v.real, -0.012402501155070958192, tol=PTOL) assert ae(v.imag, 3.1431272137073839346, tol=PTOL) v = fp.ei((-1.1641532182693481445e-10 + 4.6566128730773925781e-10j)) assert ae(v, (-20.880034621664969632 + 1.8157749903874220607j), tol=ATOL) assert ae(v.real, -20.880034621664969632, tol=PTOL) assert ae(v.imag, 1.8157749903874220607, tol=PTOL) v = fp.ei((-0.25 + 1.0j)) assert ae(v, (0.16868306393667788761 + 2.6557914649950505414j), tol=ATOL) assert ae(v.real, 0.16868306393667788761, tol=PTOL) assert ae(v.imag, 2.6557914649950505414, tol=PTOL) v = fp.ei((-1.0 + 4.0j)) assert ae(v, (-0.03373591813926547318 + 3.2151161058308770603j), tol=ATOL) assert ae(v.real, -0.03373591813926547318, tol=PTOL) assert ae(v.imag, 3.2151161058308770603, tol=PTOL) v = fp.ei((-2.0 + 8.0j)) assert ae(v, (0.015392833434733785143 + 3.1384179414340326969j), tol=ATOL) assert ae(v.real, 0.015392833434733785143, tol=PTOL) assert ae(v.imag, 3.1384179414340326969, tol=PTOL) v = fp.ei((-5.0 + 20.0j)) assert ae(v, (0.00024419662286542966525 + 3.1413825703601317109j), tol=ATOL) assert ae(v.real, 0.00024419662286542966525, tol=PTOL) assert ae(v.imag, 3.1413825703601317109, tol=PTOL) v = fp.ei((-20.0 + 80.0j)) assert ae(v, (-2.3255552781051330088e-11 + 3.1415926535987396304j), tol=ATOL) assert ae(v.real, -2.3255552781051330088e-11, tol=PTOL) assert ae(v.imag, 3.1415926535987396304, tol=PTOL) v = fp.ei((-30.0 + 120.0j)) assert ae(v, (2.7068919097124652332e-16 + 3.1415926535897925337j), tol=ATOL) assert ae(v.real, 2.7068919097124652332e-16, tol=PTOL) assert ae(v.imag, 3.1415926535897925337, tol=PTOL) v = fp.ei((-40.0 + 160.0j)) assert ae(v, (1.1695597827678024687e-20 + 3.1415926535897932385j), tol=ATOL) assert ae(v.real, 1.1695597827678024687e-20, tol=PTOL) assert ae(v.imag, 3.1415926535897932385, tol=PTOL) v = fp.ei((-50.0 + 200.0j)) assert ae(v, (-9.0323746914410162531e-25 + 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -9.0323746914410162531e-25, tol=PTOL) assert ae(v.imag, 3.1415926535897932385, tol=PTOL) v = fp.ei((-80.0 + 320.0j)) assert ae(v, (-3.4819106748728063576e-38 + 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -3.4819106748728063576e-38, tol=PTOL) assert ae(v.imag, 3.1415926535897932385, tol=PTOL) v = fp.ei((-4.6566128730773925781e-10 + 1.1641532182693481445e-10j)) assert ae(v, (-20.880034622014215597 + 2.8966139905793444061j), tol=ATOL) assert ae(v.real, -20.880034622014215597, tol=PTOL) assert ae(v.imag, 2.8966139905793444061, tol=PTOL) v = fp.ei((-1.0 + 0.25j)) assert ae(v, (-0.19731063945004229095 + 3.0542266078154932748j), tol=ATOL) assert ae(v.real, -0.19731063945004229095, tol=PTOL) assert ae(v.imag, 3.0542266078154932748, tol=PTOL) v = fp.ei((-4.0 + 1.0j)) assert ae(v, (-0.0013106173980145506944 + 3.1381384055698581758j), tol=ATOL) assert ae(v.real, -0.0013106173980145506944, tol=PTOL) assert ae(v.imag, 3.1381384055698581758, tol=PTOL) v = fp.ei((-8.0 + 2.0j)) assert ae(v, (0.000022278049065270225945 + 3.1415634616493367169j), tol=ATOL) assert ae(v.real, 0.000022278049065270225945, tol=PTOL) assert ae(v.imag, 3.1415634616493367169, tol=PTOL) v = fp.ei((-20.0 + 5.0j)) assert ae(v, (-4.7711374515765346894e-11 + 3.1415926536726958909j), tol=ATOL) assert ae(v.real, -4.7711374515765346894e-11, tol=PTOL) assert ae(v.imag, 3.1415926536726958909, tol=PTOL) v = fp.ei((-80.0 + 20.0j)) assert ae(v, (-3.8353473865788235787e-38 + 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -3.8353473865788235787e-38, tol=PTOL) assert ae(v.imag, 3.1415926535897932385, tol=PTOL) v = fp.ei((-120.0 + 30.0j)) assert ae(v, (-2.3836002337480334716e-55 + 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -2.3836002337480334716e-55, tol=PTOL) assert ae(v.imag, 3.1415926535897932385, tol=PTOL) v = fp.ei((-160.0 + 40.0j)) assert ae(v, (1.6238022898654510661e-72 + 3.1415926535897932385j), tol=ATOL) assert ae(v.real, 1.6238022898654510661e-72, tol=PTOL) assert ae(v.imag, 3.1415926535897932385, tol=PTOL) v = fp.ei((-200.0 + 50.0j)) assert ae(v, (-6.6800061461666228487e-90 + 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -6.6800061461666228487e-90, tol=PTOL) assert ae(v.imag, 3.1415926535897932385, tol=PTOL) v = fp.ei((-320.0 + 80.0j)) assert ae(v, (-4.2737871527778786157e-143 + 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -4.2737871527778786157e-143, tol=PTOL) assert ae(v.imag, 3.1415926535897932385, tol=PTOL) v = fp.ei(-1.1641532182693481445e-10) assert ae(v, -22.296641293693077672, tol=ATOL) assert type(v) is float v = fp.ei(-0.25) assert ae(v, -1.0442826344437381945, tol=ATOL) assert type(v) is float v = fp.ei(-1.0) assert ae(v, -0.21938393439552027368, tol=ATOL) assert type(v) is float v = fp.ei(-2.0) assert ae(v, -0.048900510708061119567, tol=ATOL) assert type(v) is float v = fp.ei(-5.0) assert ae(v, -0.0011482955912753257973, tol=ATOL) assert type(v) is float v = fp.ei(-20.0) assert ae(v, -9.8355252906498816904e-11, tol=ATOL) assert type(v) is float v = fp.ei(-30.0) assert ae(v, -3.0215520106888125448e-15, tol=ATOL) assert type(v) is float v = fp.ei(-40.0) assert ae(v, -1.0367732614516569722e-19, tol=ATOL) assert type(v) is float v = fp.ei(-50.0) assert ae(v, -3.7832640295504590187e-24, tol=ATOL) assert type(v) is float v = fp.ei(-80.0) assert ae(v, -2.2285432586884729112e-37, tol=ATOL) assert type(v) is float v = fp.ei((-1.1641532182693481445e-10 + 0.0j)) assert ae(v, (-22.296641293693077672 + 0.0j), tol=ATOL) assert ae(v.real, -22.296641293693077672, tol=PTOL) assert v.imag == 0 v = fp.ei((-0.25 + 0.0j)) assert ae(v, (-1.0442826344437381945 + 0.0j), tol=ATOL) assert ae(v.real, -1.0442826344437381945, tol=PTOL) assert v.imag == 0 v = fp.ei((-1.0 + 0.0j)) assert ae(v, (-0.21938393439552027368 + 0.0j), tol=ATOL) assert ae(v.real, -0.21938393439552027368, tol=PTOL) assert v.imag == 0 v = fp.ei((-2.0 + 0.0j)) assert ae(v, (-0.048900510708061119567 + 0.0j), tol=ATOL) assert ae(v.real, -0.048900510708061119567, tol=PTOL) assert v.imag == 0 v = fp.ei((-5.0 + 0.0j)) assert ae(v, (-0.0011482955912753257973 + 0.0j), tol=ATOL) assert ae(v.real, -0.0011482955912753257973, tol=PTOL) assert v.imag == 0 v = fp.ei((-20.0 + 0.0j)) assert ae(v, (-9.8355252906498816904e-11 + 0.0j), tol=ATOL) assert ae(v.real, -9.8355252906498816904e-11, tol=PTOL) assert v.imag == 0 v = fp.ei((-30.0 + 0.0j)) assert ae(v, (-3.0215520106888125448e-15 + 0.0j), tol=ATOL) assert ae(v.real, -3.0215520106888125448e-15, tol=PTOL) assert v.imag == 0 v = fp.ei((-40.0 + 0.0j)) assert ae(v, (-1.0367732614516569722e-19 + 0.0j), tol=ATOL) assert ae(v.real, -1.0367732614516569722e-19, tol=PTOL) assert v.imag == 0 v = fp.ei((-50.0 + 0.0j)) assert ae(v, (-3.7832640295504590187e-24 + 0.0j), tol=ATOL) assert ae(v.real, -3.7832640295504590187e-24, tol=PTOL) assert v.imag == 0 v = fp.ei((-80.0 + 0.0j)) assert ae(v, (-2.2285432586884729112e-37 + 0.0j), tol=ATOL) assert ae(v.real, -2.2285432586884729112e-37, tol=PTOL) assert v.imag == 0 v = fp.ei((-4.6566128730773925781e-10 - 1.1641532182693481445e-10j)) assert ae(v, (-20.880034622014215597 - 2.8966139905793444061j), tol=ATOL) assert ae(v.real, -20.880034622014215597, tol=PTOL) assert ae(v.imag, -2.8966139905793444061, tol=PTOL) v = fp.ei((-1.0 - 0.25j)) assert ae(v, (-0.19731063945004229095 - 3.0542266078154932748j), tol=ATOL) assert ae(v.real, -0.19731063945004229095, tol=PTOL) assert ae(v.imag, -3.0542266078154932748, tol=PTOL) v = fp.ei((-4.0 - 1.0j)) assert ae(v, (-0.0013106173980145506944 - 3.1381384055698581758j), tol=ATOL) assert ae(v.real, -0.0013106173980145506944, tol=PTOL) assert ae(v.imag, -3.1381384055698581758, tol=PTOL) v = fp.ei((-8.0 - 2.0j)) assert ae(v, (0.000022278049065270225945 - 3.1415634616493367169j), tol=ATOL) assert ae(v.real, 0.000022278049065270225945, tol=PTOL) assert ae(v.imag, -3.1415634616493367169, tol=PTOL) v = fp.ei((-20.0 - 5.0j)) assert ae(v, (-4.7711374515765346894e-11 - 3.1415926536726958909j), tol=ATOL) assert ae(v.real, -4.7711374515765346894e-11, tol=PTOL) assert ae(v.imag, -3.1415926536726958909, tol=PTOL) v = fp.ei((-80.0 - 20.0j)) assert ae(v, (-3.8353473865788235787e-38 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -3.8353473865788235787e-38, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.ei((-120.0 - 30.0j)) assert ae(v, (-2.3836002337480334716e-55 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -2.3836002337480334716e-55, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.ei((-160.0 - 40.0j)) assert ae(v, (1.6238022898654510661e-72 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, 1.6238022898654510661e-72, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.ei((-200.0 - 50.0j)) assert ae(v, (-6.6800061461666228487e-90 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -6.6800061461666228487e-90, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.ei((-320.0 - 80.0j)) assert ae(v, (-4.2737871527778786157e-143 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -4.2737871527778786157e-143, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.ei((-1.1641532182693481445e-10 - 1.1641532182693481445e-10j)) assert ae(v, (-21.950067703413105017 - 2.3561944903087602507j), tol=ATOL) assert ae(v.real, -21.950067703413105017, tol=PTOL) assert ae(v.imag, -2.3561944903087602507, tol=PTOL) v = fp.ei((-0.25 - 0.25j)) assert ae(v, (-0.71092525792923287894 - 2.5766745291767512913j), tol=ATOL) assert ae(v.real, -0.71092525792923287894, tol=PTOL) assert ae(v.imag, -2.5766745291767512913, tol=PTOL) v = fp.ei((-1.0 - 1.0j)) assert ae(v, (-0.00028162445198141832551 - 2.9622681185504342983j), tol=ATOL) assert ae(v.real, -0.00028162445198141832551, tol=PTOL) assert ae(v.imag, -2.9622681185504342983, tol=PTOL) v = fp.ei((-2.0 - 2.0j)) assert ae(v, (0.033767089606562004246 - 3.1229932394200426965j), tol=ATOL) assert ae(v.real, 0.033767089606562004246, tol=PTOL) assert ae(v.imag, -3.1229932394200426965, tol=PTOL) v = fp.ei((-5.0 - 5.0j)) assert ae(v, (-0.0007266506660356393891 - 3.1420636813914284609j), tol=ATOL) assert ae(v.real, -0.0007266506660356393891, tol=PTOL) assert ae(v.imag, -3.1420636813914284609, tol=PTOL) v = fp.ei((-20.0 - 20.0j)) assert ae(v, (2.3824537449367396579e-11 - 3.1415926535228233653j), tol=ATOL) assert ae(v.real, 2.3824537449367396579e-11, tol=PTOL) assert ae(v.imag, -3.1415926535228233653, tol=PTOL) v = fp.ei((-30.0 - 30.0j)) assert ae(v, (-1.7316045841744061617e-15 - 3.141592653589794545j), tol=ATOL) assert ae(v.real, -1.7316045841744061617e-15, tol=PTOL) assert ae(v.imag, -3.141592653589794545, tol=PTOL) v = fp.ei((-40.0 - 40.0j)) assert ae(v, (7.4001043002899232182e-20 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, 7.4001043002899232182e-20, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.ei((-50.0 - 50.0j)) assert ae(v, (-2.3566128324644641219e-24 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -2.3566128324644641219e-24, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.ei((-80.0 - 80.0j)) assert ae(v, (-9.8279750572186526673e-38 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -9.8279750572186526673e-38, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.ei((-1.1641532182693481445e-10 - 4.6566128730773925781e-10j)) assert ae(v, (-20.880034621664969632 - 1.8157749903874220607j), tol=ATOL) assert ae(v.real, -20.880034621664969632, tol=PTOL) assert ae(v.imag, -1.8157749903874220607, tol=PTOL) v = fp.ei((-0.25 - 1.0j)) assert ae(v, (0.16868306393667788761 - 2.6557914649950505414j), tol=ATOL) assert ae(v.real, 0.16868306393667788761, tol=PTOL) assert ae(v.imag, -2.6557914649950505414, tol=PTOL) v = fp.ei((-1.0 - 4.0j)) assert ae(v, (-0.03373591813926547318 - 3.2151161058308770603j), tol=ATOL) assert ae(v.real, -0.03373591813926547318, tol=PTOL) assert ae(v.imag, -3.2151161058308770603, tol=PTOL) v = fp.ei((-2.0 - 8.0j)) assert ae(v, (0.015392833434733785143 - 3.1384179414340326969j), tol=ATOL) assert ae(v.real, 0.015392833434733785143, tol=PTOL) assert ae(v.imag, -3.1384179414340326969, tol=PTOL) v = fp.ei((-5.0 - 20.0j)) assert ae(v, (0.00024419662286542966525 - 3.1413825703601317109j), tol=ATOL) assert ae(v.real, 0.00024419662286542966525, tol=PTOL) assert ae(v.imag, -3.1413825703601317109, tol=PTOL) v = fp.ei((-20.0 - 80.0j)) assert ae(v, (-2.3255552781051330088e-11 - 3.1415926535987396304j), tol=ATOL) assert ae(v.real, -2.3255552781051330088e-11, tol=PTOL) assert ae(v.imag, -3.1415926535987396304, tol=PTOL) v = fp.ei((-30.0 - 120.0j)) assert ae(v, (2.7068919097124652332e-16 - 3.1415926535897925337j), tol=ATOL) assert ae(v.real, 2.7068919097124652332e-16, tol=PTOL) assert ae(v.imag, -3.1415926535897925337, tol=PTOL) v = fp.ei((-40.0 - 160.0j)) assert ae(v, (1.1695597827678024687e-20 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, 1.1695597827678024687e-20, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.ei((-50.0 - 200.0j)) assert ae(v, (-9.0323746914410162531e-25 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -9.0323746914410162531e-25, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.ei((-80.0 - 320.0j)) assert ae(v, (-3.4819106748728063576e-38 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -3.4819106748728063576e-38, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.ei((0.0 - 1.1641532182693481445e-10j)) assert ae(v, (-22.29664129357666235 - 1.5707963269113119411j), tol=ATOL) assert ae(v.real, -22.29664129357666235, tol=PTOL) assert ae(v.imag, -1.5707963269113119411, tol=PTOL) v = fp.ei((0.0 - 0.25j)) assert ae(v, (-0.82466306258094565309 - 1.8199298971146537833j), tol=ATOL) assert ae(v.real, -0.82466306258094565309, tol=PTOL) assert ae(v.imag, -1.8199298971146537833, tol=PTOL) v = fp.ei((0.0 - 1.0j)) assert ae(v, (0.33740392290096813466 - 2.5168793971620796342j), tol=ATOL) assert ae(v.real, 0.33740392290096813466, tol=PTOL) assert ae(v.imag, -2.5168793971620796342, tol=PTOL) v = fp.ei((0.0 - 2.0j)) assert ae(v, (0.4229808287748649957 - 3.1762093035975914678j), tol=ATOL) assert ae(v.real, 0.4229808287748649957, tol=PTOL) assert ae(v.imag, -3.1762093035975914678, tol=PTOL) v = fp.ei((0.0 - 5.0j)) assert ae(v, (-0.19002974965664387862 - 3.1207275717395707565j), tol=ATOL) assert ae(v.real, -0.19002974965664387862, tol=PTOL) assert ae(v.imag, -3.1207275717395707565, tol=PTOL) v = fp.ei((0.0 - 20.0j)) assert ae(v, (0.04441982084535331654 - 3.1190380278383364594j), tol=ATOL) assert ae(v.real, 0.04441982084535331654, tol=PTOL) assert ae(v.imag, -3.1190380278383364594, tol=PTOL) v = fp.ei((0.0 - 30.0j)) assert ae(v, (-0.033032417282071143779 - 3.1375528668252477302j), tol=ATOL) assert ae(v.real, -0.033032417282071143779, tol=PTOL) assert ae(v.imag, -3.1375528668252477302, tol=PTOL) v = fp.ei((0.0 - 40.0j)) assert ae(v, (0.019020007896208766962 - 3.157781446149681126j), tol=ATOL) assert ae(v.real, 0.019020007896208766962, tol=PTOL) assert ae(v.imag, -3.157781446149681126, tol=PTOL) v = fp.ei((0.0 - 50.0j)) assert ae(v, (-0.0056283863241163054402 - 3.122413399280832514j), tol=ATOL) assert ae(v.real, -0.0056283863241163054402, tol=PTOL) assert ae(v.imag, -3.122413399280832514, tol=PTOL) v = fp.ei((0.0 - 80.0j)) assert ae(v, (-0.012402501155070958192 - 3.1431272137073839346j), tol=ATOL) assert ae(v.real, -0.012402501155070958192, tol=PTOL) assert ae(v.imag, -3.1431272137073839346, tol=PTOL) v = fp.ei((1.1641532182693481445e-10 - 4.6566128730773925781e-10j)) assert ae(v, (-20.880034621432138988 - 1.3258176641336937524j), tol=ATOL) assert ae(v.real, -20.880034621432138988, tol=PTOL) assert ae(v.imag, -1.3258176641336937524, tol=PTOL) v = fp.ei((0.25 - 1.0j)) assert ae(v, (0.59066621214766308594 - 2.3968481059377428687j), tol=ATOL) assert ae(v.real, 0.59066621214766308594, tol=PTOL) assert ae(v.imag, -2.3968481059377428687, tol=PTOL) v = fp.ei((1.0 - 4.0j)) assert ae(v, (-0.49739047283060471093 - 3.5570287076301818702j), tol=ATOL) assert ae(v.real, -0.49739047283060471093, tol=PTOL) assert ae(v.imag, -3.5570287076301818702, tol=PTOL) v = fp.ei((2.0 - 8.0j)) assert ae(v, (0.8705211147733730969 - 3.3825859385758486351j), tol=ATOL) assert ae(v.real, 0.8705211147733730969, tol=PTOL) assert ae(v.imag, -3.3825859385758486351, tol=PTOL) v = fp.ei((5.0 - 20.0j)) assert ae(v, (7.0789514293925893007 - 1.5313749363937141849j), tol=ATOL) assert ae(v.real, 7.0789514293925893007, tol=PTOL) assert ae(v.imag, -1.5313749363937141849, tol=PTOL) v = fp.ei((20.0 - 80.0j)) assert ae(v, (-5855431.4907298084434 + 720917.79156143806727j), tol=ATOL) assert ae(v.real, -5855431.4907298084434, tol=PTOL) assert ae(v.imag, 720917.79156143806727, tol=PTOL) v = fp.ei((30.0 - 120.0j)) assert ae(v, (65402491644.703470747 + 56697658396.51586764j), tol=ATOL) assert ae(v.real, 65402491644.703470747, tol=PTOL) assert ae(v.imag, 56697658396.51586764, tol=PTOL) v = fp.ei((40.0 - 160.0j)) assert ae(v, (-25504929379604.776769 - 1429035198630576.3879j), tol=ATOL) assert ae(v.real, -25504929379604.776769, tol=PTOL) assert ae(v.imag, -1429035198630576.3879, tol=PTOL) v = fp.ei((50.0 - 200.0j)) assert ae(v, (-18437746526988116954.0 + 17146362239046152342.0j), tol=ATOL) assert ae(v.real, -18437746526988116954.0, tol=PTOL) assert ae(v.imag, 17146362239046152342.0, tol=PTOL) v = fp.ei((80.0 - 320.0j)) assert ae(v, (-3.3464697299634526706e+31 + 1.6473152633843023919e+32j), tol=ATOL) assert ae(v.real, -3.3464697299634526706e+31, tol=PTOL) assert ae(v.imag, 1.6473152633843023919e+32, tol=PTOL) v = fp.ei((1.1641532182693481445e-10 - 1.1641532182693481445e-10j)) assert ae(v, (-21.950067703180274374 - 0.78539816351386363145j), tol=ATOL) assert ae(v.real, -21.950067703180274374, tol=PTOL) assert ae(v.imag, -0.78539816351386363145, tol=PTOL) v = fp.ei((0.25 - 0.25j)) assert ae(v, (-0.21441047326710323254 - 1.0683772981589995996j), tol=ATOL) assert ae(v.real, -0.21441047326710323254, tol=PTOL) assert ae(v.imag, -1.0683772981589995996, tol=PTOL) v = fp.ei((1.0 - 1.0j)) assert ae(v, (1.7646259855638540684 - 2.3877698515105224193j), tol=ATOL) assert ae(v.real, 1.7646259855638540684, tol=PTOL) assert ae(v.imag, -2.3877698515105224193, tol=PTOL) v = fp.ei((2.0 - 2.0j)) assert ae(v, (1.8920781621855474089 - 5.3169624378326579621j), tol=ATOL) assert ae(v.real, 1.8920781621855474089, tol=PTOL) assert ae(v.imag, -5.3169624378326579621, tol=PTOL) v = fp.ei((5.0 - 5.0j)) assert ae(v, (-13.470936071475245856 + 15.322492395731230968j), tol=ATOL) assert ae(v.real, -13.470936071475245856, tol=PTOL) assert ae(v.imag, 15.322492395731230968, tol=PTOL) v = fp.ei((20.0 - 20.0j)) assert ae(v, (16589317.398788971896 - 5831705.4712368307104j), tol=ATOL) assert ae(v.real, 16589317.398788971896, tol=PTOL) assert ae(v.imag, -5831705.4712368307104, tol=PTOL) v = fp.ei((30.0 - 30.0j)) assert ae(v, (-154596484273.69322527 + 204179357834.2723043j), tol=ATOL) assert ae(v.real, -154596484273.69322527, tol=PTOL) assert ae(v.imag, 204179357834.2723043, tol=PTOL) v = fp.ei((40.0 - 40.0j)) assert ae(v, (287512180321448.45408 - 4203502407932318.1156j), tol=ATOL) assert ae(v.real, 287512180321448.45408, tol=PTOL) assert ae(v.imag, -4203502407932318.1156, tol=PTOL) v = fp.ei((50.0 - 50.0j)) assert ae(v, (36128528616649268826.0 + 64648801861338741960.0j), tol=ATOL) assert ae(v.real, 36128528616649268826.0, tol=PTOL) assert ae(v.imag, 64648801861338741960.0, tol=PTOL) v = fp.ei((80.0 - 80.0j)) assert ae(v, (-3.8674816337930010217e+32 + 3.0540709639658071041e+32j), tol=ATOL) assert ae(v.real, -3.8674816337930010217e+32, tol=PTOL) assert ae(v.imag, 3.0540709639658071041e+32, tol=PTOL) v = fp.ei((4.6566128730773925781e-10 - 1.1641532182693481445e-10j)) assert ae(v, (-20.880034621082893023 - 0.24497866324327947603j), tol=ATOL) assert ae(v.real, -20.880034621082893023, tol=PTOL) assert ae(v.imag, -0.24497866324327947603, tol=PTOL) v = fp.ei((1.0 - 0.25j)) assert ae(v, (1.8942716983721074932 - 0.67268237088273915854j), tol=ATOL) assert ae(v.real, 1.8942716983721074932, tol=PTOL) assert ae(v.imag, -0.67268237088273915854, tol=PTOL) v = fp.ei((4.0 - 1.0j)) assert ae(v, (14.806699492675420438 - 12.280015176673582616j), tol=ATOL) assert ae(v.real, 14.806699492675420438, tol=PTOL) assert ae(v.imag, -12.280015176673582616, tol=PTOL) v = fp.ei((8.0 - 2.0j)) assert ae(v, (-54.633252667426386294 - 416.34477429173650012j), tol=ATOL) assert ae(v.real, -54.633252667426386294, tol=PTOL) assert ae(v.imag, -416.34477429173650012, tol=PTOL) v = fp.ei((20.0 - 5.0j)) assert ae(v, (711836.97165402624643 + 24745247.798103247366j), tol=ATOL) assert ae(v.real, 711836.97165402624643, tol=PTOL) assert ae(v.imag, 24745247.798103247366, tol=PTOL) v = fp.ei((80.0 - 20.0j)) assert ae(v, (4.2139911108612653091e+32 - 5.3367124741918251637e+32j), tol=ATOL) assert ae(v.real, 4.2139911108612653091e+32, tol=PTOL) assert ae(v.imag, -5.3367124741918251637e+32, tol=PTOL) v = fp.ei((120.0 - 30.0j)) assert ae(v, (-9.7760616203707508892e+48 + 1.058257682317195792e+50j), tol=ATOL) assert ae(v.real, -9.7760616203707508892e+48, tol=PTOL) assert ae(v.imag, 1.058257682317195792e+50, tol=PTOL) v = fp.ei((160.0 - 40.0j)) assert ae(v, (-8.7065541466623638861e+66 - 1.6577106725141739889e+67j), tol=ATOL) assert ae(v.real, -8.7065541466623638861e+66, tol=PTOL) assert ae(v.imag, -1.6577106725141739889e+67, tol=PTOL) v = fp.ei((200.0 - 50.0j)) assert ae(v, (3.070744996327018106e+84 + 1.7243244846769415903e+84j), tol=ATOL) assert ae(v.real, 3.070744996327018106e+84, tol=PTOL) assert ae(v.imag, 1.7243244846769415903e+84, tol=PTOL) v = fp.ei((320.0 - 80.0j)) assert ae(v, (-9.9960598637998647276e+135 + 2.6855081527595608863e+136j), tol=ATOL) assert ae(v.real, -9.9960598637998647276e+135, tol=PTOL) assert ae(v.imag, 2.6855081527595608863e+136, tol=PTOL) sympy-0.7.4.1/sympy/mpmath/tests/extratest_zeta.py0000644000175000017500000000210312253362407022471 0ustar georgeskgeorgeskfrom mpmath import zetazero from timeit import default_timer as clock def test_zetazero(): cases = [\ (399999999, 156762524.6750591511), (241389216, 97490234.2276711795), (526196239, 202950727.691229534), (542964976, 209039046.578535272), (1048449112, 388858885.231056486), (1048449113, 388858885.384337406), (1048449114, 388858886.002285122), (1048449115, 388858886.00239369), (1048449116, 388858886.690745053) ] for n, v in cases: print(n, v) t1 = clock() ok = zetazero(n).ae(complex(0.5,v)) t2 = clock() print("ok =", ok, ("(time = %s)" % round(t2-t1,3))) print("Now computing two huge zeros (this may take hours)") print("Computing zetazero(8637740722917)") ok = zetazero(8637740722917).ae(complex(0.5,2124447368584.39296466152)) print("ok =", ok) ok = zetazero(8637740722918).ae(complex(0.5,2124447368584.39298170604)) print("ok =", ok) if __name__ == "__main__": try: import psyco psyco.full() except ImportError: pass test_zetazero() sympy-0.7.4.1/sympy/mpmath/tests/test_functions.py0000644000175000017500000007375112253362407022513 0ustar georgeskgeorgeskfrom sympy.mpmath.libmp import * from sympy.mpmath import * import random import time import math import cmath def mpc_ae(a, b, eps=eps): res = True res = res and a.real.ae(b.real, eps) res = res and a.imag.ae(b.imag, eps) return res #---------------------------------------------------------------------------- # Constants and functions # tpi = "3.1415926535897932384626433832795028841971693993751058209749445923078\ 1640628620899862803482534211706798" te = "2.71828182845904523536028747135266249775724709369995957496696762772407\ 663035354759457138217852516642743" tdegree = "0.017453292519943295769236907684886127134428718885417254560971914\ 4017100911460344944368224156963450948221" teuler = "0.5772156649015328606065120900824024310421593359399235988057672348\ 84867726777664670936947063291746749516" tln2 = "0.693147180559945309417232121458176568075500134360255254120680009493\ 393621969694715605863326996418687542" tln10 = "2.30258509299404568401799145468436420760110148862877297603332790096\ 757260967735248023599720508959829834" tcatalan = "0.91596559417721901505460351493238411077414937428167213426649811\ 9621763019776254769479356512926115106249" tkhinchin = "2.6854520010653064453097148354817956938203822939944629530511523\ 4555721885953715200280114117493184769800" tglaisher = "1.2824271291006226368753425688697917277676889273250011920637400\ 2174040630885882646112973649195820237439420646" tapery = "1.2020569031595942853997381615114499907649862923404988817922715553\ 4183820578631309018645587360933525815" tphi = "1.618033988749894848204586834365638117720309179805762862135448622705\ 26046281890244970720720418939113748475" tmertens = "0.26149721284764278375542683860869585905156664826119920619206421\ 3924924510897368209714142631434246651052" ttwinprime = "0.660161815846869573927812110014555778432623360284733413319448\ 423335405642304495277143760031413839867912" def test_constants(): for prec in [3, 7, 10, 15, 20, 37, 80, 100, 29]: mp.dps = prec assert pi == mpf(tpi) assert e == mpf(te) assert degree == mpf(tdegree) assert euler == mpf(teuler) assert ln2 == mpf(tln2) assert ln10 == mpf(tln10) assert catalan == mpf(tcatalan) assert khinchin == mpf(tkhinchin) assert glaisher == mpf(tglaisher) assert phi == mpf(tphi) if prec < 50: assert mertens == mpf(tmertens) assert twinprime == mpf(ttwinprime) mp.dps = 15 assert pi >= -1 assert pi > 2 assert pi > 3 assert pi < 4 def test_exact_sqrts(): for i in range(20000): assert sqrt(mpf(i*i)) == i random.seed(1) for prec in [100, 300, 1000, 10000]: mp.dps = prec for i in range(20): A = random.randint(10**(prec//2-2), 10**(prec//2-1)) assert sqrt(mpf(A*A)) == A mp.dps = 15 for i in range(100): for a in [1, 8, 25, 112307]: assert sqrt(mpf((a*a, 2*i))) == mpf((a, i)) assert sqrt(mpf((a*a, -2*i))) == mpf((a, -i)) def test_sqrt_rounding(): for i in [2, 3, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15]: i = from_int(i) for dps in [7, 15, 83, 106, 2000]: mp.dps = dps a = mpf_pow_int(mpf_sqrt(i, mp.prec, round_down), 2, mp.prec, round_down) b = mpf_pow_int(mpf_sqrt(i, mp.prec, round_up), 2, mp.prec, round_up) assert mpf_lt(a, i) assert mpf_gt(b, i) random.seed(1234) prec = 100 for rnd in [round_down, round_nearest, round_ceiling]: for i in range(100): a = mpf_rand(prec) b = mpf_mul(a, a) assert mpf_sqrt(b, prec, rnd) == a # Test some extreme cases mp.dps = 100 a = mpf(9) + 1e-90 b = mpf(9) - 1e-90 mp.dps = 15 assert sqrt(a, rounding='d') == 3 assert sqrt(a, rounding='n') == 3 assert sqrt(a, rounding='u') > 3 assert sqrt(b, rounding='d') < 3 assert sqrt(b, rounding='n') == 3 assert sqrt(b, rounding='u') == 3 # A worst case, from the MPFR test suite assert sqrt(mpf('7.0503726185518891')) == mpf('2.655253776675949') def test_float_sqrt(): mp.dps = 15 # These should round identically for x in [0, 1e-7, 0.1, 0.5, 1, 2, 3, 4, 5, 0.333, 76.19]: assert sqrt(mpf(x)) == float(x)**0.5 assert sqrt(-1) == 1j assert sqrt(-2).ae(cmath.sqrt(-2)) assert sqrt(-3).ae(cmath.sqrt(-3)) assert sqrt(-100).ae(cmath.sqrt(-100)) assert sqrt(1j).ae(cmath.sqrt(1j)) assert sqrt(-1j).ae(cmath.sqrt(-1j)) assert sqrt(math.pi + math.e*1j).ae(cmath.sqrt(math.pi + math.e*1j)) assert sqrt(math.pi - math.e*1j).ae(cmath.sqrt(math.pi - math.e*1j)) def test_hypot(): assert hypot(0, 0) == 0 assert hypot(0, 0.33) == mpf(0.33) assert hypot(0.33, 0) == mpf(0.33) assert hypot(-0.33, 0) == mpf(0.33) assert hypot(3, 4) == mpf(5) def test_exact_cbrt(): for i in range(0, 20000, 200): assert cbrt(mpf(i*i*i)) == i random.seed(1) for prec in [100, 300, 1000, 10000]: mp.dps = prec A = random.randint(10**(prec//2-2), 10**(prec//2-1)) assert cbrt(mpf(A*A*A)) == A mp.dps = 15 def test_exp(): assert exp(0) == 1 assert exp(10000).ae(mpf('8.8068182256629215873e4342')) assert exp(-10000).ae(mpf('1.1354838653147360985e-4343')) a = exp(mpf((1, 8198646019315405, -53, 53))) assert(a.bc == bitcount(a.man)) mp.prec = 67 a = exp(mpf((1, 1781864658064754565, -60, 61))) assert(a.bc == bitcount(a.man)) mp.prec = 53 assert exp(ln2 * 10).ae(1024) assert exp(2+2j).ae(cmath.exp(2+2j)) def test_issue_33(): mp.dps = 512 a = exp(-1) b = exp(1) mp.dps = 15 assert (+a).ae(0.36787944117144233) assert (+b).ae(2.7182818284590451) def test_log(): mp.dps = 15 assert log(1) == 0 for x in [0.5, 1.5, 2.0, 3.0, 100, 10**50, 1e-50]: assert log(x).ae(math.log(x)) assert log(x, x) == 1 assert log(1024, 2) == 10 assert log(10**1234, 10) == 1234 assert log(2+2j).ae(cmath.log(2+2j)) # Accuracy near 1 assert (log(0.6+0.8j).real*10**17).ae(2.2204460492503131) assert (log(0.6-0.8j).real*10**17).ae(2.2204460492503131) assert (log(0.8-0.6j).real*10**17).ae(2.2204460492503131) assert (log(1+1e-8j).real*10**16).ae(0.5) assert (log(1-1e-8j).real*10**16).ae(0.5) assert (log(-1+1e-8j).real*10**16).ae(0.5) assert (log(-1-1e-8j).real*10**16).ae(0.5) assert (log(1j+1e-8).real*10**16).ae(0.5) assert (log(1j-1e-8).real*10**16).ae(0.5) assert (log(-1j+1e-8).real*10**16).ae(0.5) assert (log(-1j-1e-8).real*10**16).ae(0.5) assert (log(1+1e-40j).real*10**80).ae(0.5) assert (log(1j+1e-40).real*10**80).ae(0.5) # Huge assert log(ldexp(1.234,10**20)).ae(log(2)*1e20) assert log(ldexp(1.234,10**200)).ae(log(2)*1e200) # Some special values assert log(mpc(0,0)) == mpc(-inf,0) assert isnan(log(mpc(nan,0)).real) assert isnan(log(mpc(nan,0)).imag) assert isnan(log(mpc(0,nan)).real) assert isnan(log(mpc(0,nan)).imag) assert isnan(log(mpc(nan,1)).real) assert isnan(log(mpc(nan,1)).imag) assert isnan(log(mpc(1,nan)).real) assert isnan(log(mpc(1,nan)).imag) def test_trig_hyperb_basic(): for x in (list(range(100)) + list(range(-100,0))): t = x / 4.1 assert cos(mpf(t)).ae(math.cos(t)) assert sin(mpf(t)).ae(math.sin(t)) assert tan(mpf(t)).ae(math.tan(t)) assert cosh(mpf(t)).ae(math.cosh(t)) assert sinh(mpf(t)).ae(math.sinh(t)) assert tanh(mpf(t)).ae(math.tanh(t)) assert sin(1+1j).ae(cmath.sin(1+1j)) assert sin(-4-3.6j).ae(cmath.sin(-4-3.6j)) assert cos(1+1j).ae(cmath.cos(1+1j)) assert cos(-4-3.6j).ae(cmath.cos(-4-3.6j)) def test_degrees(): assert cos(0*degree) == 1 assert cos(90*degree).ae(0) assert cos(180*degree).ae(-1) assert cos(270*degree).ae(0) assert cos(360*degree).ae(1) assert sin(0*degree) == 0 assert sin(90*degree).ae(1) assert sin(180*degree).ae(0) assert sin(270*degree).ae(-1) assert sin(360*degree).ae(0) def random_complexes(N): random.seed(1) a = [] for i in range(N): x1 = random.uniform(-10, 10) y1 = random.uniform(-10, 10) x2 = random.uniform(-10, 10) y2 = random.uniform(-10, 10) z1 = complex(x1, y1) z2 = complex(x2, y2) a.append((z1, z2)) return a def test_complex_powers(): for dps in [15, 30, 100]: # Check accuracy for complex square root mp.dps = dps a = mpc(1j)**0.5 assert a.real == a.imag == mpf(2)**0.5 / 2 mp.dps = 15 random.seed(1) for (z1, z2) in random_complexes(100): assert (mpc(z1)**mpc(z2)).ae(z1**z2, 1e-12) assert (e**(-pi*1j)).ae(-1) mp.dps = 50 assert (e**(-pi*1j)).ae(-1) mp.dps = 15 def test_complex_sqrt_accuracy(): def test_mpc_sqrt(lst): for a, b in lst: z = mpc(a + j*b) assert mpc_ae(sqrt(z*z), z) z = mpc(-a + j*b) assert mpc_ae(sqrt(z*z), -z) z = mpc(a - j*b) assert mpc_ae(sqrt(z*z), z) z = mpc(-a - j*b) assert mpc_ae(sqrt(z*z), -z) random.seed(2) N = 10 mp.dps = 30 dps = mp.dps test_mpc_sqrt([(random.uniform(0, 10),random.uniform(0, 10)) for i in range(N)]) test_mpc_sqrt([(i + 0.1, (i + 0.2)*10**i) for i in range(N)]) mp.dps = 15 def test_atan(): mp.dps = 15 assert atan(-2.3).ae(math.atan(-2.3)) assert atan(1e-50) == 1e-50 assert atan(1e50).ae(pi/2) assert atan(-1e-50) == -1e-50 assert atan(-1e50).ae(-pi/2) assert atan(10**1000).ae(pi/2) for dps in [25, 70, 100, 300, 1000]: mp.dps = dps assert (4*atan(1)).ae(pi) mp.dps = 15 pi2 = pi/2 assert atan(mpc(inf,-1)).ae(pi2) assert atan(mpc(inf,0)).ae(pi2) assert atan(mpc(inf,1)).ae(pi2) assert atan(mpc(1,inf)).ae(pi2) assert atan(mpc(0,inf)).ae(pi2) assert atan(mpc(-1,inf)).ae(-pi2) assert atan(mpc(-inf,1)).ae(-pi2) assert atan(mpc(-inf,0)).ae(-pi2) assert atan(mpc(-inf,-1)).ae(-pi2) assert atan(mpc(-1,-inf)).ae(-pi2) assert atan(mpc(0,-inf)).ae(-pi2) assert atan(mpc(1,-inf)).ae(pi2) def test_atan2(): mp.dps = 15 assert atan2(1,1).ae(pi/4) assert atan2(1,-1).ae(3*pi/4) assert atan2(-1,-1).ae(-3*pi/4) assert atan2(-1,1).ae(-pi/4) assert atan2(-1,0).ae(-pi/2) assert atan2(1,0).ae(pi/2) assert atan2(0,0) == 0 assert atan2(inf,0).ae(pi/2) assert atan2(-inf,0).ae(-pi/2) assert isnan(atan2(inf,inf)) assert isnan(atan2(-inf,inf)) assert isnan(atan2(inf,-inf)) assert isnan(atan2(3,nan)) assert isnan(atan2(nan,3)) assert isnan(atan2(0,nan)) assert isnan(atan2(nan,0)) assert atan2(0,inf) == 0 assert atan2(0,-inf).ae(pi) assert atan2(10,inf) == 0 assert atan2(-10,inf) == 0 assert atan2(-10,-inf).ae(-pi) assert atan2(10,-inf).ae(pi) assert atan2(inf,10).ae(pi/2) assert atan2(inf,-10).ae(pi/2) assert atan2(-inf,10).ae(-pi/2) assert atan2(-inf,-10).ae(-pi/2) def test_areal_inverses(): assert asin(mpf(0)) == 0 assert asinh(mpf(0)) == 0 assert acosh(mpf(1)) == 0 assert isinstance(asin(mpf(0.5)), mpf) assert isinstance(asin(mpf(2.0)), mpc) assert isinstance(acos(mpf(0.5)), mpf) assert isinstance(acos(mpf(2.0)), mpc) assert isinstance(atanh(mpf(0.1)), mpf) assert isinstance(atanh(mpf(1.1)), mpc) random.seed(1) for i in range(50): x = random.uniform(0, 1) assert asin(mpf(x)).ae(math.asin(x)) assert acos(mpf(x)).ae(math.acos(x)) x = random.uniform(-10, 10) assert asinh(mpf(x)).ae(cmath.asinh(x).real) assert isinstance(asinh(mpf(x)), mpf) x = random.uniform(1, 10) assert acosh(mpf(x)).ae(cmath.acosh(x).real) assert isinstance(acosh(mpf(x)), mpf) x = random.uniform(-10, 0.999) assert isinstance(acosh(mpf(x)), mpc) x = random.uniform(-1, 1) assert atanh(mpf(x)).ae(cmath.atanh(x).real) assert isinstance(atanh(mpf(x)), mpf) dps = mp.dps mp.dps = 300 assert isinstance(asin(0.5), mpf) mp.dps = 1000 assert asin(1).ae(pi/2) assert asin(-1).ae(-pi/2) mp.dps = dps def test_invhyperb_inaccuracy(): mp.dps = 15 assert (asinh(1e-5)*10**5).ae(0.99999999998333333) assert (asinh(1e-10)*10**10).ae(1) assert (asinh(1e-50)*10**50).ae(1) assert (asinh(-1e-5)*10**5).ae(-0.99999999998333333) assert (asinh(-1e-10)*10**10).ae(-1) assert (asinh(-1e-50)*10**50).ae(-1) assert asinh(10**20).ae(46.744849040440862) assert asinh(-10**20).ae(-46.744849040440862) assert (tanh(1e-10)*10**10).ae(1) assert (tanh(-1e-10)*10**10).ae(-1) assert (atanh(1e-10)*10**10).ae(1) assert (atanh(-1e-10)*10**10).ae(-1) def test_complex_functions(): for x in (list(range(10)) + list(range(-10,0))): for y in (list(range(10)) + list(range(-10,0))): z = complex(x, y)/4.3 + 0.01j assert exp(mpc(z)).ae(cmath.exp(z)) assert log(mpc(z)).ae(cmath.log(z)) assert cos(mpc(z)).ae(cmath.cos(z)) assert sin(mpc(z)).ae(cmath.sin(z)) assert tan(mpc(z)).ae(cmath.tan(z)) assert sinh(mpc(z)).ae(cmath.sinh(z)) assert cosh(mpc(z)).ae(cmath.cosh(z)) assert tanh(mpc(z)).ae(cmath.tanh(z)) def test_complex_inverse_functions(): mp.dps = 15 iv.dps = 15 for (z1, z2) in random_complexes(30): # apparently cmath uses a different branch, so we # can't use it for comparison assert sinh(asinh(z1)).ae(z1) # assert acosh(z1).ae(cmath.acosh(z1)) assert atanh(z1).ae(cmath.atanh(z1)) assert atan(z1).ae(cmath.atan(z1)) # the reason we set a big eps here is that the cmath # functions are inaccurate assert asin(z1).ae(cmath.asin(z1), rel_eps=1e-12) assert acos(z1).ae(cmath.acos(z1), rel_eps=1e-12) one = mpf(1) for i in range(-9, 10, 3): for k in range(-9, 10, 3): a = 0.9*j*10**k + 0.8*one*10**i b = cos(acos(a)) assert b.ae(a) b = sin(asin(a)) assert b.ae(a) one = mpf(1) err = 2*10**-15 for i in range(-9, 9, 3): for k in range(-9, 9, 3): a = -0.9*10**k + j*0.8*one*10**i b = cosh(acosh(a)) assert b.ae(a, err) b = sinh(asinh(a)) assert b.ae(a, err) def test_reciprocal_functions(): assert sec(3).ae(-1.01010866590799375) assert csc(3).ae(7.08616739573718592) assert cot(3).ae(-7.01525255143453347) assert sech(3).ae(0.0993279274194332078) assert csch(3).ae(0.0998215696688227329) assert coth(3).ae(1.00496982331368917) assert asec(3).ae(1.23095941734077468) assert acsc(3).ae(0.339836909454121937) assert acot(3).ae(0.321750554396642193) assert asech(0.5).ae(1.31695789692481671) assert acsch(3).ae(0.327450150237258443) assert acoth(3).ae(0.346573590279972655) def test_ldexp(): mp.dps = 15 assert ldexp(mpf(2.5), 0) == 2.5 assert ldexp(mpf(2.5), -1) == 1.25 assert ldexp(mpf(2.5), 2) == 10 assert ldexp(mpf('inf'), 3) == mpf('inf') def test_frexp(): mp.dps = 15 assert frexp(0) == (0.0, 0) assert frexp(9) == (0.5625, 4) assert frexp(1) == (0.5, 1) assert frexp(0.2) == (0.8, -2) assert frexp(1000) == (0.9765625, 10) def test_aliases(): assert ln(7) == log(7) assert log10(3.75) == log(3.75,10) assert degrees(5.6) == 5.6 / degree assert radians(5.6) == 5.6 * degree assert power(-1,0.5) == j assert fmod(25,7) == 4.0 and isinstance(fmod(25,7), mpf) def test_arg_sign(): assert arg(3) == 0 assert arg(-3).ae(pi) assert arg(j).ae(pi/2) assert arg(-j).ae(-pi/2) assert arg(0) == 0 assert isnan(atan2(3,nan)) assert isnan(atan2(nan,3)) assert isnan(atan2(0,nan)) assert isnan(atan2(nan,0)) assert isnan(atan2(nan,nan)) assert arg(inf) == 0 assert arg(-inf).ae(pi) assert isnan(arg(nan)) assert arg(inf*j).ae(pi/2) assert sign(0) == 0 assert sign(3) == 1 assert sign(-3) == -1 assert sign(inf) == 1 assert sign(-inf) == -1 assert isnan(sign(nan)) assert sign(j) == j assert sign(-3*j) == -j assert sign(1+j).ae((1+j)/sqrt(2)) def test_misc_bugs(): # test that this doesn't raise an exception mp.dps = 1000 log(1302) mp.dps = 15 def test_arange(): assert arange(10) == [mpf('0.0'), mpf('1.0'), mpf('2.0'), mpf('3.0'), mpf('4.0'), mpf('5.0'), mpf('6.0'), mpf('7.0'), mpf('8.0'), mpf('9.0')] assert arange(-5, 5) == [mpf('-5.0'), mpf('-4.0'), mpf('-3.0'), mpf('-2.0'), mpf('-1.0'), mpf('0.0'), mpf('1.0'), mpf('2.0'), mpf('3.0'), mpf('4.0')] assert arange(0, 1, 0.1) == [mpf('0.0'), mpf('0.10000000000000001'), mpf('0.20000000000000001'), mpf('0.30000000000000004'), mpf('0.40000000000000002'), mpf('0.5'), mpf('0.60000000000000009'), mpf('0.70000000000000007'), mpf('0.80000000000000004'), mpf('0.90000000000000002')] assert arange(17, -9, -3) == [mpf('17.0'), mpf('14.0'), mpf('11.0'), mpf('8.0'), mpf('5.0'), mpf('2.0'), mpf('-1.0'), mpf('-4.0'), mpf('-7.0')] assert arange(0.2, 0.1, -0.1) == [mpf('0.20000000000000001')] assert arange(0) == [] assert arange(1000, -1) == [] assert arange(-1.23, 3.21, -0.0000001) == [] def test_linspace(): assert linspace(2, 9, 7) == [mpf('2.0'), mpf('3.166666666666667'), mpf('4.3333333333333339'), mpf('5.5'), mpf('6.666666666666667'), mpf('7.8333333333333339'), mpf('9.0')] assert linspace(2, 9, 7, endpoint=0) == [mpf('2.0'), mpf('3.0'), mpf('4.0'), mpf('5.0'), mpf('6.0'), mpf('7.0'), mpf('8.0')] assert linspace(2, 7, 1) == [mpf(2)] def test_float_cbrt(): mp.dps = 30 for a in arange(0,10,0.1): assert cbrt(a*a*a).ae(a, eps) assert cbrt(-1).ae(0.5 + j*sqrt(3)/2) one_third = mpf(1)/3 for a in arange(0,10,2.7) + [0.1 + 10**5]: a = mpc(a + 1.1j) r1 = cbrt(a) mp.dps += 10 r2 = pow(a, one_third) mp.dps -= 10 assert r1.ae(r2, eps) mp.dps = 100 for n in range(100, 301, 100): w = 10**n + j*10**-3 z = w*w*w r = cbrt(z) assert mpc_ae(r, w, eps) mp.dps = 15 def test_root(): mp.dps = 30 random.seed(1) a = random.randint(0, 10000) p = a*a*a r = nthroot(mpf(p), 3) assert r == a for n in range(4, 10): p = p*a assert nthroot(mpf(p), n) == a mp.dps = 40 for n in range(10, 5000, 100): for a in [random.random()*10000, random.random()*10**100]: r = nthroot(a, n) r1 = pow(a, mpf(1)/n) assert r.ae(r1) r = nthroot(a, -n) r1 = pow(a, -mpf(1)/n) assert r.ae(r1) # XXX: this is broken right now # tests for nthroot rounding for rnd in ['nearest', 'up', 'down']: mp.rounding = rnd for n in [-5, -3, 3, 5]: prec = 50 for i in range(10): mp.prec = prec a = rand() mp.prec = 2*prec b = a**n mp.prec = prec r = nthroot(b, n) assert r == a mp.dps = 30 for n in range(3, 21): a = (random.random() + j*random.random()) assert nthroot(a, n).ae(pow(a, mpf(1)/n)) assert mpc_ae(nthroot(a, n), pow(a, mpf(1)/n)) a = (random.random()*10**100 + j*random.random()) r = nthroot(a, n) mp.dps += 4 r1 = pow(a, mpf(1)/n) mp.dps -= 4 assert r.ae(r1) assert mpc_ae(r, r1, eps) r = nthroot(a, -n) mp.dps += 4 r1 = pow(a, -mpf(1)/n) mp.dps -= 4 assert r.ae(r1) assert mpc_ae(r, r1, eps) mp.dps = 15 assert nthroot(4, 1) == 4 assert nthroot(4, 0) == 1 assert nthroot(4, -1) == 0.25 assert nthroot(inf, 1) == inf assert nthroot(inf, 2) == inf assert nthroot(inf, 3) == inf assert nthroot(inf, -1) == 0 assert nthroot(inf, -2) == 0 assert nthroot(inf, -3) == 0 assert nthroot(j, 1) == j assert nthroot(j, 0) == 1 assert nthroot(j, -1) == -j assert isnan(nthroot(nan, 1)) assert isnan(nthroot(nan, 0)) assert isnan(nthroot(nan, -1)) assert isnan(nthroot(inf, 0)) assert root(2,3) == nthroot(2,3) assert root(16,4,0) == 2 assert root(16,4,1) == 2j assert root(16,4,2) == -2 assert root(16,4,3) == -2j assert root(16,4,4) == 2 assert root(-125,3,1) == -5 def test_issue_96(): for dps in [20, 80]: mp.dps = dps r = nthroot(mpf('-1e-20'), 4) assert r.ae(mpf(10)**(-5) * (1 + j) * mpf(2)**(-0.5)) mp.dps = 80 assert nthroot('-1e-3', 4).ae(mpf(10)**(-3./4) * (1 + j)/sqrt(2)) assert nthroot('-1e-6', 4).ae((1 + j)/(10 * sqrt(20))) # Check that this doesn't take eternity to compute mp.dps = 20 assert nthroot('-1e100000000', 4).ae((1+j)*mpf('1e25000000')/sqrt(2)) mp.dps = 15 def test_mpcfun_real_imag(): mp.dps = 15 x = mpf(0.3) y = mpf(0.4) assert exp(mpc(x,0)) == exp(x) assert exp(mpc(0,y)) == mpc(cos(y),sin(y)) assert cos(mpc(x,0)) == cos(x) assert sin(mpc(x,0)) == sin(x) assert cos(mpc(0,y)) == cosh(y) assert sin(mpc(0,y)) == mpc(0,sinh(y)) assert cospi(mpc(x,0)) == cospi(x) assert sinpi(mpc(x,0)) == sinpi(x) assert cospi(mpc(0,y)).ae(cosh(pi*y)) assert sinpi(mpc(0,y)).ae(mpc(0,sinh(pi*y))) c, s = cospi_sinpi(mpc(x,0)) assert c == cospi(x) assert s == sinpi(x) c, s = cospi_sinpi(mpc(0,y)) assert c.ae(cosh(pi*y)) assert s.ae(mpc(0,sinh(pi*y))) c, s = cos_sin(mpc(x,0)) assert c == cos(x) assert s == sin(x) c, s = cos_sin(mpc(0,y)) assert c == cosh(y) assert s == mpc(0,sinh(y)) def test_perturbation_rounding(): mp.dps = 100 a = pi/10**50 b = -pi/10**50 c = 1 + a d = 1 + b mp.dps = 15 assert exp(a) == 1 assert exp(a, rounding='c') > 1 assert exp(b, rounding='c') == 1 assert exp(a, rounding='f') == 1 assert exp(b, rounding='f') < 1 assert cos(a) == 1 assert cos(a, rounding='c') == 1 assert cos(b, rounding='c') == 1 assert cos(a, rounding='f') < 1 assert cos(b, rounding='f') < 1 for f in [sin, atan, asinh, tanh]: assert f(a) == +a assert f(a, rounding='c') > a assert f(a, rounding='f') < a assert f(b) == +b assert f(b, rounding='c') > b assert f(b, rounding='f') < b for f in [asin, tan, sinh, atanh]: assert f(a) == +a assert f(b) == +b assert f(a, rounding='c') > a assert f(b, rounding='c') > b assert f(a, rounding='f') < a assert f(b, rounding='f') < b assert ln(c) == +a assert ln(d) == +b assert ln(c, rounding='c') > a assert ln(c, rounding='f') < a assert ln(d, rounding='c') > b assert ln(d, rounding='f') < b assert cosh(a) == 1 assert cosh(b) == 1 assert cosh(a, rounding='c') > 1 assert cosh(b, rounding='c') > 1 assert cosh(a, rounding='f') == 1 assert cosh(b, rounding='f') == 1 def test_integer_parts(): assert floor(3.2) == 3 assert ceil(3.2) == 4 assert floor(3.2+5j) == 3+5j assert ceil(3.2+5j) == 4+5j def test_complex_parts(): assert fabs('3') == 3 assert fabs(3+4j) == 5 assert re(3) == 3 assert re(1+4j) == 1 assert im(3) == 0 assert im(1+4j) == 4 assert conj(3) == 3 assert conj(3+4j) == 3-4j assert mpf(3).conjugate() == 3 def test_cospi_sinpi(): assert sinpi(0) == 0 assert sinpi(0.5) == 1 assert sinpi(1) == 0 assert sinpi(1.5) == -1 assert sinpi(2) == 0 assert sinpi(2.5) == 1 assert sinpi(-0.5) == -1 assert cospi(0) == 1 assert cospi(0.5) == 0 assert cospi(1) == -1 assert cospi(1.5) == 0 assert cospi(2) == 1 assert cospi(2.5) == 0 assert cospi(-0.5) == 0 assert cospi(100000000000.25).ae(sqrt(2)/2) a = cospi(2+3j) assert a.real.ae(cos((2+3j)*pi).real) assert a.imag == 0 b = sinpi(2+3j) assert b.imag.ae(sin((2+3j)*pi).imag) assert b.real == 0 mp.dps = 35 x1 = mpf(10000) - mpf('1e-15') x2 = mpf(10000) + mpf('1e-15') x3 = mpf(10000.5) - mpf('1e-15') x4 = mpf(10000.5) + mpf('1e-15') x5 = mpf(10001) - mpf('1e-15') x6 = mpf(10001) + mpf('1e-15') x7 = mpf(10001.5) - mpf('1e-15') x8 = mpf(10001.5) + mpf('1e-15') mp.dps = 15 M = 10**15 assert (sinpi(x1)*M).ae(-pi) assert (sinpi(x2)*M).ae(pi) assert (cospi(x3)*M).ae(pi) assert (cospi(x4)*M).ae(-pi) assert (sinpi(x5)*M).ae(pi) assert (sinpi(x6)*M).ae(-pi) assert (cospi(x7)*M).ae(-pi) assert (cospi(x8)*M).ae(pi) assert 0.999 < cospi(x1, rounding='d') < 1 assert 0.999 < cospi(x2, rounding='d') < 1 assert 0.999 < sinpi(x3, rounding='d') < 1 assert 0.999 < sinpi(x4, rounding='d') < 1 assert -1 < cospi(x5, rounding='d') < -0.999 assert -1 < cospi(x6, rounding='d') < -0.999 assert -1 < sinpi(x7, rounding='d') < -0.999 assert -1 < sinpi(x8, rounding='d') < -0.999 assert (sinpi(1e-15)*M).ae(pi) assert (sinpi(-1e-15)*M).ae(-pi) assert cospi(1e-15) == 1 assert cospi(1e-15, rounding='d') < 1 def test_expj(): assert expj(0) == 1 assert expj(1).ae(exp(j)) assert expj(j).ae(exp(-1)) assert expj(1+j).ae(exp(j*(1+j))) assert expjpi(0) == 1 assert expjpi(1).ae(exp(j*pi)) assert expjpi(j).ae(exp(-pi)) assert expjpi(1+j).ae(exp(j*pi*(1+j))) assert expjpi(-10**15 * j).ae('2.22579818340535731e+1364376353841841') def test_sinc(): assert sinc(0) == sincpi(0) == 1 assert sinc(inf) == sincpi(inf) == 0 assert sinc(-inf) == sincpi(-inf) == 0 assert sinc(2).ae(0.45464871341284084770) assert sinc(2+3j).ae(0.4463290318402435457-2.7539470277436474940j) assert sincpi(2) == 0 assert sincpi(1.5).ae(-0.212206590789193781) def test_fibonacci(): mp.dps = 15 assert [fibonacci(n) for n in range(-5, 10)] == \ [5, -3, 2, -1, 1, 0, 1, 1, 2, 3, 5, 8, 13, 21, 34] assert fib(2.5).ae(1.4893065462657091) assert fib(3+4j).ae(-5248.51130728372 - 14195.962288353j) assert fib(1000).ae(4.3466557686937455e+208) assert str(fib(10**100)) == '6.24499112864607e+2089876402499787337692720892375554168224592399182109535392875613974104853496745963277658556235103534' mp.dps = 2100 a = fib(10000) assert a % 10**10 == 9947366875 mp.dps = 15 assert fibonacci(inf) == inf assert fib(3+0j) == 2 def test_call_with_dps(): mp.dps = 15 assert abs(exp(1, dps=30)-e(dps=35)) < 1e-29 def test_tanh(): mp.dps = 15 assert tanh(0) == 0 assert tanh(inf) == 1 assert tanh(-inf) == -1 assert isnan(tanh(nan)) assert tanh(mpc('inf', '0')) == 1 def test_atanh(): mp.dps = 15 assert atanh(0) == 0 assert atanh(0.5).ae(0.54930614433405484570) assert atanh(-0.5).ae(-0.54930614433405484570) assert atanh(1) == inf assert atanh(-1) == -inf assert isnan(atanh(nan)) assert isinstance(atanh(1), mpf) assert isinstance(atanh(-1), mpf) # Limits at infinity jpi2 = j*pi/2 assert atanh(inf).ae(-jpi2) assert atanh(-inf).ae(jpi2) assert atanh(mpc(inf,-1)).ae(-jpi2) assert atanh(mpc(inf,0)).ae(-jpi2) assert atanh(mpc(inf,1)).ae(jpi2) assert atanh(mpc(1,inf)).ae(jpi2) assert atanh(mpc(0,inf)).ae(jpi2) assert atanh(mpc(-1,inf)).ae(jpi2) assert atanh(mpc(-inf,1)).ae(jpi2) assert atanh(mpc(-inf,0)).ae(jpi2) assert atanh(mpc(-inf,-1)).ae(-jpi2) assert atanh(mpc(-1,-inf)).ae(-jpi2) assert atanh(mpc(0,-inf)).ae(-jpi2) assert atanh(mpc(1,-inf)).ae(-jpi2) def test_expm1(): mp.dps = 15 assert expm1(0) == 0 assert expm1(3).ae(exp(3)-1) assert expm1(inf) == inf assert expm1(1e-10)*1e10 assert expm1(1e-50).ae(1e-50) assert (expm1(1e-10)*1e10).ae(1.00000000005) def test_powm1(): mp.dps = 15 assert powm1(2,3) == 7 assert powm1(-1,2) == 0 assert powm1(-1,0) == 0 assert powm1(-2,0) == 0 assert powm1(3+4j,0) == 0 assert powm1(0,1) == -1 assert powm1(0,0) == 0 assert powm1(1,0) == 0 assert powm1(1,2) == 0 assert powm1(1,3+4j) == 0 assert powm1(1,5) == 0 assert powm1(j,4) == 0 assert powm1(-j,4) == 0 assert (powm1(2,1e-100)*1e100).ae(ln2) assert powm1(2,'1e-100000000000') != 0 assert (powm1(fadd(1,1e-100,exact=True), 5)*1e100).ae(5) def test_unitroots(): assert unitroots(1) == [1] assert unitroots(2) == [1, -1] a, b, c = unitroots(3) assert a == 1 assert b.ae(-0.5 + 0.86602540378443864676j) assert c.ae(-0.5 - 0.86602540378443864676j) assert unitroots(1, primitive=True) == [1] assert unitroots(2, primitive=True) == [-1] assert unitroots(3, primitive=True) == unitroots(3)[1:] assert unitroots(4, primitive=True) == [j, -j] assert len(unitroots(17, primitive=True)) == 16 assert len(unitroots(16, primitive=True)) == 8 def test_cyclotomic(): mp.dps = 15 assert [cyclotomic(n,1) for n in range(31)] == [1,0,2,3,2,5,1,7,2,3,1,11,1,13,1,1,2,17,1,19,1,1,1,23,1,5,1,3,1,29,1] assert [cyclotomic(n,-1) for n in range(31)] == [1,-2,0,1,2,1,3,1,2,1,5,1,1,1,7,1,2,1,3,1,1,1,11,1,1,1,13,1,1,1,1] assert [cyclotomic(n,j) for n in range(21)] == [1,-1+j,1+j,j,0,1,-j,j,2,-j,1,j,3,1,-j,1,2,1,j,j,5] assert [cyclotomic(n,-j) for n in range(21)] == [1,-1-j,1-j,-j,0,1,j,-j,2,j,1,-j,3,1,j,1,2,1,-j,-j,5] assert cyclotomic(1624,j) == 1 assert cyclotomic(33600,j) == 1 u = sqrt(j, prec=500) assert cyclotomic(8, u).ae(0) assert cyclotomic(30, u).ae(5.8284271247461900976) assert cyclotomic(2040, u).ae(1) assert cyclotomic(0,2.5) == 1 assert cyclotomic(1,2.5) == 2.5-1 assert cyclotomic(2,2.5) == 2.5+1 assert cyclotomic(3,2.5) == 2.5**2 + 2.5 + 1 assert cyclotomic(7,2.5) == 406.234375 sympy-0.7.4.1/sympy/mpmath/tests/test_interval.py0000644000175000017500000004071512253362407022321 0ustar georgeskgeorgeskfrom sympy.mpmath import * def test_interval_identity(): iv.dps = 15 assert mpi(2) == mpi(2, 2) assert mpi(2) != mpi(-2, 2) assert not (mpi(2) != mpi(2, 2)) assert mpi(-1, 1) == mpi(-1, 1) assert str(mpi('0.1')) == "[0.099999999999999991673, 0.10000000000000000555]" assert repr(mpi('0.1')) == "mpi('0.099999999999999992', '0.10000000000000001')" u = mpi(-1, 3) assert -1 in u assert 2 in u assert 3 in u assert -1.1 not in u assert 3.1 not in u assert mpi(-1, 3) in u assert mpi(0, 1) in u assert mpi(-1.1, 2) not in u assert mpi(2.5, 3.1) not in u w = mpi(-inf, inf) assert mpi(-5, 5) in w assert mpi(2, inf) in w assert mpi(0, 2) in mpi(0, 10) assert not (3 in mpi(-inf, 0)) def test_interval_arithmetic(): iv.dps = 15 assert mpi(2) + mpi(3,4) == mpi(5,6) assert mpi(1, 2)**2 == mpi(1, 4) assert mpi(1) + mpi(0, 1e-50) == mpi(1, mpf('1.0000000000000002')) x = 1 / (1 / mpi(3)) assert x.a < 3 < x.b x = mpi(2) ** mpi(0.5) iv.dps += 5 sq = iv.sqrt(2) iv.dps -= 5 assert x.a < sq < x.b assert mpi(1) / mpi(1, inf) assert mpi(2, 3) / inf == mpi(0, 0) assert mpi(0) / inf == 0 assert mpi(0) / 0 == mpi(-inf, inf) assert mpi(inf) / 0 == mpi(-inf, inf) assert mpi(0) * inf == mpi(-inf, inf) assert 1 / mpi(2, inf) == mpi(0, 0.5) assert str((mpi(50, 50) * mpi(-10, -10)) / 3) == \ '[-166.66666666666668561, -166.66666666666665719]' assert mpi(0, 4) ** 3 == mpi(0, 64) assert mpi(2,4).mid == 3 iv.dps = 30 a = mpi(iv.pi) iv.dps = 15 b = +a assert b.a < a.a assert b.b > a.b a = mpi(iv.pi) assert a == +a assert abs(mpi(-1,2)) == mpi(0,2) assert abs(mpi(0.5,2)) == mpi(0.5,2) assert abs(mpi(-3,2)) == mpi(0,3) assert abs(mpi(-3,-0.5)) == mpi(0.5,3) assert mpi(0) * mpi(2,3) == mpi(0) assert mpi(2,3) * mpi(0) == mpi(0) assert mpi(1,3).delta == 2 assert mpi(1,2) - mpi(3,4) == mpi(-3,-1) assert mpi(-inf,0) - mpi(0,inf) == mpi(-inf,0) assert mpi(-inf,0) - mpi(-inf,inf) == mpi(-inf,inf) assert mpi(0,inf) - mpi(-inf,1) == mpi(-1,inf) def test_interval_mul(): assert mpi(-1, 0) * inf == mpi(-inf, 0) assert mpi(-1, 0) * -inf == mpi(0, inf) assert mpi(0, 1) * inf == mpi(0, inf) assert mpi(0, 1) * mpi(0, inf) == mpi(0, inf) assert mpi(-1, 1) * inf == mpi(-inf, inf) assert mpi(-1, 1) * mpi(0, inf) == mpi(-inf, inf) assert mpi(-1, 1) * mpi(-inf, inf) == mpi(-inf, inf) assert mpi(-inf, 0) * mpi(0, 1) == mpi(-inf, 0) assert mpi(-inf, 0) * mpi(0, 0) * mpi(-inf, 0) assert mpi(-inf, 0) * mpi(-inf, inf) == mpi(-inf, inf) assert mpi(-5,0)*mpi(-32,28) == mpi(-140,160) assert mpi(2,3) * mpi(-1,2) == mpi(-3,6) # Should be undefined? assert mpi(inf, inf) * 0 == mpi(-inf, inf) assert mpi(-inf, -inf) * 0 == mpi(-inf, inf) assert mpi(0) * mpi(-inf,2) == mpi(-inf,inf) assert mpi(0) * mpi(-2,inf) == mpi(-inf,inf) assert mpi(-2,inf) * mpi(0) == mpi(-inf,inf) assert mpi(-inf,2) * mpi(0) == mpi(-inf,inf) def test_interval_pow(): assert mpi(3)**2 == mpi(9, 9) assert mpi(-3)**2 == mpi(9, 9) assert mpi(-3, 1)**2 == mpi(0, 9) assert mpi(-3, -1)**2 == mpi(1, 9) assert mpi(-3, -1)**3 == mpi(-27, -1) assert mpi(-3, 1)**3 == mpi(-27, 1) assert mpi(-2, 3)**2 == mpi(0, 9) assert mpi(-3, 2)**2 == mpi(0, 9) assert mpi(4) ** -1 == mpi(0.25, 0.25) assert mpi(-4) ** -1 == mpi(-0.25, -0.25) assert mpi(4) ** -2 == mpi(0.0625, 0.0625) assert mpi(-4) ** -2 == mpi(0.0625, 0.0625) assert mpi(0, 1) ** inf == mpi(0, 1) assert mpi(0, 1) ** -inf == mpi(1, inf) assert mpi(0, inf) ** inf == mpi(0, inf) assert mpi(0, inf) ** -inf == mpi(0, inf) assert mpi(1, inf) ** inf == mpi(1, inf) assert mpi(1, inf) ** -inf == mpi(0, 1) assert mpi(2, 3) ** 1 == mpi(2, 3) assert mpi(2, 3) ** 0 == 1 assert mpi(1,3) ** mpi(2) == mpi(1,9) def test_interval_sqrt(): assert mpi(4) ** 0.5 == mpi(2) def test_interval_div(): assert mpi(0.5, 1) / mpi(-1, 0) == mpi(-inf, -0.5) assert mpi(0, 1) / mpi(0, 1) == mpi(0, inf) assert mpi(inf, inf) / mpi(inf, inf) == mpi(0, inf) assert mpi(inf, inf) / mpi(2, inf) == mpi(0, inf) assert mpi(inf, inf) / mpi(2, 2) == mpi(inf, inf) assert mpi(0, inf) / mpi(2, inf) == mpi(0, inf) assert mpi(0, inf) / mpi(2, 2) == mpi(0, inf) assert mpi(2, inf) / mpi(2, 2) == mpi(1, inf) assert mpi(2, inf) / mpi(2, inf) == mpi(0, inf) assert mpi(-4, 8) / mpi(1, inf) == mpi(-4, 8) assert mpi(-4, 8) / mpi(0.5, inf) == mpi(-8, 16) assert mpi(-inf, 8) / mpi(0.5, inf) == mpi(-inf, 16) assert mpi(-inf, inf) / mpi(0.5, inf) == mpi(-inf, inf) assert mpi(8, inf) / mpi(0.5, inf) == mpi(0, inf) assert mpi(-8, inf) / mpi(0.5, inf) == mpi(-16, inf) assert mpi(-4, 8) / mpi(inf, inf) == mpi(0, 0) assert mpi(0, 8) / mpi(inf, inf) == mpi(0, 0) assert mpi(0, 0) / mpi(inf, inf) == mpi(0, 0) assert mpi(-inf, 0) / mpi(inf, inf) == mpi(-inf, 0) assert mpi(-inf, 8) / mpi(inf, inf) == mpi(-inf, 0) assert mpi(-inf, inf) / mpi(inf, inf) == mpi(-inf, inf) assert mpi(-8, inf) / mpi(inf, inf) == mpi(0, inf) assert mpi(0, inf) / mpi(inf, inf) == mpi(0, inf) assert mpi(8, inf) / mpi(inf, inf) == mpi(0, inf) assert mpi(inf, inf) / mpi(inf, inf) == mpi(0, inf) assert mpi(-1, 2) / mpi(0, 1) == mpi(-inf, +inf) assert mpi(0, 1) / mpi(0, 1) == mpi(0.0, +inf) assert mpi(-1, 0) / mpi(0, 1) == mpi(-inf, 0.0) assert mpi(-0.5, -0.25) / mpi(0, 1) == mpi(-inf, -0.25) assert mpi(0.5, 1) / mpi(0, 1) == mpi(0.5, +inf) assert mpi(0.5, 4) / mpi(0, 1) == mpi(0.5, +inf) assert mpi(-1, -0.5) / mpi(0, 1) == mpi(-inf, -0.5) assert mpi(-4, -0.5) / mpi(0, 1) == mpi(-inf, -0.5) assert mpi(-1, 2) / mpi(-2, 0.5) == mpi(-inf, +inf) assert mpi(0, 1) / mpi(-2, 0.5) == mpi(-inf, +inf) assert mpi(-1, 0) / mpi(-2, 0.5) == mpi(-inf, +inf) assert mpi(-0.5, -0.25) / mpi(-2, 0.5) == mpi(-inf, +inf) assert mpi(0.5, 1) / mpi(-2, 0.5) == mpi(-inf, +inf) assert mpi(0.5, 4) / mpi(-2, 0.5) == mpi(-inf, +inf) assert mpi(-1, -0.5) / mpi(-2, 0.5) == mpi(-inf, +inf) assert mpi(-4, -0.5) / mpi(-2, 0.5) == mpi(-inf, +inf) assert mpi(-1, 2) / mpi(-1, 0) == mpi(-inf, +inf) assert mpi(0, 1) / mpi(-1, 0) == mpi(-inf, 0.0) assert mpi(-1, 0) / mpi(-1, 0) == mpi(0.0, +inf) assert mpi(-0.5, -0.25) / mpi(-1, 0) == mpi(0.25, +inf) assert mpi(0.5, 1) / mpi(-1, 0) == mpi(-inf, -0.5) assert mpi(0.5, 4) / mpi(-1, 0) == mpi(-inf, -0.5) assert mpi(-1, -0.5) / mpi(-1, 0) == mpi(0.5, +inf) assert mpi(-4, -0.5) / mpi(-1, 0) == mpi(0.5, +inf) assert mpi(-1, 2) / mpi(0.5, 1) == mpi(-2.0, 4.0) assert mpi(0, 1) / mpi(0.5, 1) == mpi(0.0, 2.0) assert mpi(-1, 0) / mpi(0.5, 1) == mpi(-2.0, 0.0) assert mpi(-0.5, -0.25) / mpi(0.5, 1) == mpi(-1.0, -0.25) assert mpi(0.5, 1) / mpi(0.5, 1) == mpi(0.5, 2.0) assert mpi(0.5, 4) / mpi(0.5, 1) == mpi(0.5, 8.0) assert mpi(-1, -0.5) / mpi(0.5, 1) == mpi(-2.0, -0.5) assert mpi(-4, -0.5) / mpi(0.5, 1) == mpi(-8.0, -0.5) assert mpi(-1, 2) / mpi(-2, -0.5) == mpi(-4.0, 2.0) assert mpi(0, 1) / mpi(-2, -0.5) == mpi(-2.0, 0.0) assert mpi(-1, 0) / mpi(-2, -0.5) == mpi(0.0, 2.0) assert mpi(-0.5, -0.25) / mpi(-2, -0.5) == mpi(0.125, 1.0) assert mpi(0.5, 1) / mpi(-2, -0.5) == mpi(-2.0, -0.25) assert mpi(0.5, 4) / mpi(-2, -0.5) == mpi(-8.0, -0.25) assert mpi(-1, -0.5) / mpi(-2, -0.5) == mpi(0.25, 2.0) assert mpi(-4, -0.5) / mpi(-2, -0.5) == mpi(0.25, 8.0) # Should be undefined? assert mpi(0, 0) / mpi(0, 0) == mpi(-inf, inf) assert mpi(0, 0) / mpi(0, 1) == mpi(-inf, inf) def test_interval_cos_sin(): iv.dps = 15 cos = iv.cos sin = iv.sin tan = iv.tan pi = iv.pi # Around 0 assert cos(mpi(0)) == 1 assert sin(mpi(0)) == 0 assert cos(mpi(0,1)) == mpi(0.54030230586813965399, 1.0) assert sin(mpi(0,1)) == mpi(0, 0.8414709848078966159) assert cos(mpi(1,2)) == mpi(-0.4161468365471424069, 0.54030230586813976501) assert sin(mpi(1,2)) == mpi(0.84147098480789650488, 1.0) assert sin(mpi(1,2.5)) == mpi(0.59847214410395643824, 1.0) assert cos(mpi(-1, 1)) == mpi(0.54030230586813965399, 1.0) assert cos(mpi(-1, 0.5)) == mpi(0.54030230586813965399, 1.0) assert cos(mpi(-1, 1.5)) == mpi(0.070737201667702906405, 1.0) assert sin(mpi(-1,1)) == mpi(-0.8414709848078966159, 0.8414709848078966159) assert sin(mpi(-1,0.5)) == mpi(-0.8414709848078966159, 0.47942553860420300538) assert mpi(-0.8414709848078966159, 1.00000000000000002e-100) in sin(mpi(-1,1e-100)) assert mpi(-2.00000000000000004e-100, 1.00000000000000002e-100) in sin(mpi(-2e-100,1e-100)) # Same interval assert cos(mpi(2, 2.5)) assert cos(mpi(3.5, 4)) == mpi(-0.93645668729079634129, -0.65364362086361182946) assert cos(mpi(5, 5.5)) == mpi(0.28366218546322624627, 0.70866977429126010168) assert mpi(0.59847214410395654927, 0.90929742682568170942) in sin(mpi(2, 2.5)) assert sin(mpi(3.5, 4)) == mpi(-0.75680249530792831347, -0.35078322768961983646) assert sin(mpi(5, 5.5)) == mpi(-0.95892427466313856499, -0.70554032557039181306) # Higher roots iv.dps = 55 w = 4*10**50 + mpi(0.5) for p in [15, 40, 80]: iv.dps = p assert 0 in sin(4*mpi(pi)) assert 0 in sin(4*10**50*mpi(pi)) assert 0 in cos((4+0.5)*mpi(pi)) assert 0 in cos(w*mpi(pi)) assert 1 in cos(4*mpi(pi)) assert 1 in cos(4*10**50*mpi(pi)) iv.dps = 15 assert cos(mpi(2,inf)) == mpi(-1,1) assert sin(mpi(2,inf)) == mpi(-1,1) assert cos(mpi(-inf,2)) == mpi(-1,1) assert sin(mpi(-inf,2)) == mpi(-1,1) u = tan(mpi(0.5,1)) assert mpf(u.a).ae(mp.tan(0.5)) assert mpf(u.b).ae(mp.tan(1)) v = iv.cot(mpi(0.5,1)) assert mpf(v.a).ae(mp.cot(1)) assert mpf(v.b).ae(mp.cot(0.5)) # Sanity check of evaluation at n*pi and (n+1/2)*pi for n in range(-5,7,2): x = iv.cos(n*iv.pi) assert -1 in x assert x >= -1 assert x != -1 x = iv.sin((n+0.5)*iv.pi) assert -1 in x assert x >= -1 assert x != -1 for n in range(-6,8,2): x = iv.cos(n*iv.pi) assert 1 in x assert x <= 1 if n: assert x != 1 x = iv.sin((n+0.5)*iv.pi) assert 1 in x assert x <= 1 assert x != 1 for n in range(-6,7): x = iv.cos((n+0.5)*iv.pi) assert x.a < 0 < x.b x = iv.sin(n*iv.pi) if n: assert x.a < 0 < x.b def test_interval_complex(): # TODO: many more tests iv.dps = 15 mp.dps = 15 assert iv.mpc(2,3) == 2+3j assert iv.mpc(2,3) != 2+4j assert iv.mpc(2,3) != 1+3j assert 1+3j in iv.mpc([1,2],[3,4]) assert 2+5j not in iv.mpc([1,2],[3,4]) assert iv.mpc(1,2) + 1j == 1+3j assert iv.mpc([1,2],[2,3]) + 2+3j == iv.mpc([3,4],[5,6]) assert iv.mpc([2,4],[4,8]) / 2 == iv.mpc([1,2],[2,4]) assert iv.mpc([1,2],[2,4]) * 2j == iv.mpc([-8,-4],[2,4]) assert iv.mpc([2,4],[4,8]) / 2j == iv.mpc([2,4],[-2,-1]) assert iv.exp(2+3j).ae(mp.exp(2+3j)) assert iv.log(2+3j).ae(mp.log(2+3j)) assert (iv.mpc(2,3) ** iv.mpc(0.5,2)).ae(mp.mpc(2,3) ** mp.mpc(0.5,2)) assert 1j in (iv.mpf(-1) ** 0.5) assert 1j in (iv.mpc(-1) ** 0.5) assert abs(iv.mpc(0)) == 0 assert abs(iv.mpc(inf)) == inf assert abs(iv.mpc(3,4)) == 5 assert abs(iv.mpc(4)) == 4 assert abs(iv.mpc(0,4)) == 4 assert abs(iv.mpc(0,[2,3])) == iv.mpf([2,3]) assert abs(iv.mpc(0,[-3,2])) == iv.mpf([0,3]) assert abs(iv.mpc([3,5],[4,12])) == iv.mpf([5,13]) assert abs(iv.mpc([3,5],[-4,12])) == iv.mpf([3,13]) assert iv.mpc(2,3) ** 0 == 1 assert iv.mpc(2,3) ** 1 == (2+3j) assert iv.mpc(2,3) ** 2 == (2+3j)**2 assert iv.mpc(2,3) ** 3 == (2+3j)**3 assert iv.mpc(2,3) ** 4 == (2+3j)**4 assert iv.mpc(2,3) ** 5 == (2+3j)**5 assert iv.mpc(2,2) ** (-1) == (2+2j) ** (-1) assert iv.mpc(2,2) ** (-2) == (2+2j) ** (-2) assert iv.cos(2).ae(mp.cos(2)) assert iv.sin(2).ae(mp.sin(2)) assert iv.cos(2+3j).ae(mp.cos(2+3j)) assert iv.sin(2+3j).ae(mp.sin(2+3j)) def test_interval_complex_arg(): mp.dps = 15 iv.dps = 15 assert iv.arg(3) == 0 assert iv.arg(0) == 0 assert iv.arg([0,3]) == 0 assert iv.arg(-3).ae(pi) assert iv.arg(2+3j).ae(iv.arg(2+3j)) z = iv.mpc([-2,-1],[3,4]) t = iv.arg(z) assert t.a.ae(mp.arg(-1+4j)) assert t.b.ae(mp.arg(-2+3j)) z = iv.mpc([-2,1],[3,4]) t = iv.arg(z) assert t.a.ae(mp.arg(1+3j)) assert t.b.ae(mp.arg(-2+3j)) z = iv.mpc([1,2],[3,4]) t = iv.arg(z) assert t.a.ae(mp.arg(2+3j)) assert t.b.ae(mp.arg(1+4j)) z = iv.mpc([1,2],[-2,3]) t = iv.arg(z) assert t.a.ae(mp.arg(1-2j)) assert t.b.ae(mp.arg(1+3j)) z = iv.mpc([1,2],[-4,-3]) t = iv.arg(z) assert t.a.ae(mp.arg(1-4j)) assert t.b.ae(mp.arg(2-3j)) z = iv.mpc([-1,2],[-4,-3]) t = iv.arg(z) assert t.a.ae(mp.arg(-1-3j)) assert t.b.ae(mp.arg(2-3j)) z = iv.mpc([-2,-1],[-4,-3]) t = iv.arg(z) assert t.a.ae(mp.arg(-2-3j)) assert t.b.ae(mp.arg(-1-4j)) z = iv.mpc([-2,-1],[-3,3]) t = iv.arg(z) assert t.a.ae(-mp.pi) assert t.b.ae(mp.pi) z = iv.mpc([-2,2],[-3,3]) t = iv.arg(z) assert t.a.ae(-mp.pi) assert t.b.ae(mp.pi) def test_interval_ae(): iv.dps = 15 x = iv.mpf([1,2]) assert x.ae(1) is None assert x.ae(1.5) is None assert x.ae(2) is None assert x.ae(2.01) is False assert x.ae(0.99) is False x = iv.mpf(3.5) assert x.ae(3.5) is True assert x.ae(3.5+1e-15) is True assert x.ae(3.5-1e-15) is True assert x.ae(3.501) is False assert x.ae(3.499) is False assert x.ae(iv.mpf([3.5,3.501])) is None assert x.ae(iv.mpf([3.5,4.5+1e-15])) is None def test_interval_nstr(): iv.dps = n = 30 x = mpi(1, 2) # FIXME: error_dps should not be necessary assert iv.nstr(x, n, mode='plusminus', error_dps=6) == '1.5 +- 0.5' assert iv.nstr(x, n, mode='plusminus', use_spaces=False, error_dps=6) == '1.5+-0.5' assert iv.nstr(x, n, mode='percent') == '1.5 (33.33%)' assert iv.nstr(x, n, mode='brackets', use_spaces=False) == '[1.0,2.0]' assert iv.nstr(x, n, mode='brackets' , brackets=('<', '>')) == '<1.0, 2.0>' x = mpi('5.2582327113062393041', '5.2582327113062749951') assert iv.nstr(x, n, mode='diff') == '5.2582327113062[393041, 749951]' assert iv.nstr(iv.cos(mpi(1)), n, mode='diff', use_spaces=False) == '0.54030230586813971740093660744[2955,3053]' assert iv.nstr(mpi('1e123', '1e129'), n, mode='diff') == '[1.0e+123, 1.0e+129]' exp = iv.exp assert iv.nstr(iv.exp(mpi('5000.1')), n, mode='diff') == '3.2797365856787867069110487[0926, 1191]e+2171' iv.dps = 15 def test_mpi_from_str(): iv.dps = 15 assert iv.convert('1.5 +- 0.5') == mpi(mpf('1.0'), mpf('2.0')) assert mpi(1, 2) in iv.convert('1.5 (33.33333333333333333333333333333%)') assert iv.convert('[1, 2]') == mpi(1, 2) assert iv.convert('1[2, 3]') == mpi(12, 13) assert iv.convert('1.[23,46]e-8') == mpi('1.23e-8', '1.46e-8') assert iv.convert('12[3.4,5.9]e4') == mpi('123.4e+4', '125.9e4') def test_interval_gamma(): mp.dps = 15 iv.dps = 15 # TODO: need many more tests assert iv.rgamma(0) == 0 assert iv.fac(0) == 1 assert iv.fac(1) == 1 assert iv.fac(2) == 2 assert iv.fac(3) == 6 assert iv.gamma(0) == [-inf,inf] assert iv.gamma(1) == 1 assert iv.gamma(2) == 1 assert iv.gamma(3) == 2 assert -3.5449077018110320546 in iv.gamma(-0.5) assert iv.loggamma(1) == 0 assert iv.loggamma(2) == 0 assert 0.69314718055994530942 in iv.loggamma(3) # Test tight log-gamma endpoints based on monotonicity xs = [iv.mpc([2,3],[1,4]), iv.mpc([2,3],[-4,-1]), iv.mpc([2,3],[-1,4]), iv.mpc([2,3],[-4,1]), iv.mpc([2,3],[-4,4]), iv.mpc([-3,-2],[2,4]), iv.mpc([-3,-2],[-4,-2])] for x in xs: ys = [mp.loggamma(mp.mpc(x.a,x.c)), mp.loggamma(mp.mpc(x.b,x.c)), mp.loggamma(mp.mpc(x.a,x.d)), mp.loggamma(mp.mpc(x.b,x.d))] if 0 in x.imag: ys += [mp.loggamma(x.a), mp.loggamma(x.b)] min_real = min([y.real for y in ys]) max_real = max([y.real for y in ys]) min_imag = min([y.imag for y in ys]) max_imag = max([y.imag for y in ys]) z = iv.loggamma(x) assert z.a.ae(min_real) assert z.b.ae(max_real) assert z.c.ae(min_imag) assert z.d.ae(max_imag) sympy-0.7.4.1/sympy/mpmath/tests/test_elliptic.py0000644000175000017500000005661412253362407022307 0ustar georgeskgeorgesk""" Limited tests of the elliptic functions module. A full suite of extensive testing can be found in elliptic_torture_tests.py Author of the first version: M.T. Taschuk References: [1] Abramowitz & Stegun. 'Handbook of Mathematical Functions, 9th Ed.', (Dover duplicate of 1972 edition) [2] Whittaker 'A Course of Modern Analysis, 4th Ed.', 1946, Cambridge University Press """ import sympy.mpmath import random from sympy.mpmath import * def mpc_ae(a, b, eps=eps): res = True res = res and a.real.ae(b.real, eps) res = res and a.imag.ae(b.imag, eps) return res zero = mpf(0) one = mpf(1) jsn = ellipfun('sn') jcn = ellipfun('cn') jdn = ellipfun('dn') calculate_nome = lambda k: qfrom(k=k) def test_ellipfun(): mp.dps = 15 assert ellipfun('ss', 0, 0) == 1 assert ellipfun('cc', 0, 0) == 1 assert ellipfun('dd', 0, 0) == 1 assert ellipfun('nn', 0, 0) == 1 assert ellipfun('sn', 0.25, 0).ae(sin(0.25)) assert ellipfun('cn', 0.25, 0).ae(cos(0.25)) assert ellipfun('dn', 0.25, 0).ae(1) assert ellipfun('ns', 0.25, 0).ae(csc(0.25)) assert ellipfun('nc', 0.25, 0).ae(sec(0.25)) assert ellipfun('nd', 0.25, 0).ae(1) assert ellipfun('sc', 0.25, 0).ae(tan(0.25)) assert ellipfun('sd', 0.25, 0).ae(sin(0.25)) assert ellipfun('cd', 0.25, 0).ae(cos(0.25)) assert ellipfun('cs', 0.25, 0).ae(cot(0.25)) assert ellipfun('dc', 0.25, 0).ae(sec(0.25)) assert ellipfun('ds', 0.25, 0).ae(csc(0.25)) assert ellipfun('sn', 0.25, 1).ae(tanh(0.25)) assert ellipfun('cn', 0.25, 1).ae(sech(0.25)) assert ellipfun('dn', 0.25, 1).ae(sech(0.25)) assert ellipfun('ns', 0.25, 1).ae(coth(0.25)) assert ellipfun('nc', 0.25, 1).ae(cosh(0.25)) assert ellipfun('nd', 0.25, 1).ae(cosh(0.25)) assert ellipfun('sc', 0.25, 1).ae(sinh(0.25)) assert ellipfun('sd', 0.25, 1).ae(sinh(0.25)) assert ellipfun('cd', 0.25, 1).ae(1) assert ellipfun('cs', 0.25, 1).ae(csch(0.25)) assert ellipfun('dc', 0.25, 1).ae(1) assert ellipfun('ds', 0.25, 1).ae(csch(0.25)) assert ellipfun('sn', 0.25, 0.5).ae(0.24615967096986145833) assert ellipfun('cn', 0.25, 0.5).ae(0.96922928989378439337) assert ellipfun('dn', 0.25, 0.5).ae(0.98473484156599474563) assert ellipfun('ns', 0.25, 0.5).ae(4.0624038700573130369) assert ellipfun('nc', 0.25, 0.5).ae(1.0317476065024692949) assert ellipfun('nd', 0.25, 0.5).ae(1.0155017958029488665) assert ellipfun('sc', 0.25, 0.5).ae(0.25397465134058993408) assert ellipfun('sd', 0.25, 0.5).ae(0.24997558792415733063) assert ellipfun('cd', 0.25, 0.5).ae(0.98425408443195497052) assert ellipfun('cs', 0.25, 0.5).ae(3.9374008182374110826) assert ellipfun('dc', 0.25, 0.5).ae(1.0159978158253033913) assert ellipfun('ds', 0.25, 0.5).ae(4.0003906313579720593) def test_calculate_nome(): mp.dps = 100 q = calculate_nome(zero) assert(q == zero) mp.dps = 25 # used Mathematica's EllipticNomeQ[m] math1 = [(mpf(1)/10, mpf('0.006584651553858370274473060')), (mpf(2)/10, mpf('0.01394285727531826872146409')), (mpf(3)/10, mpf('0.02227743615715350822901627')), (mpf(4)/10, mpf('0.03188334731336317755064299')), (mpf(5)/10, mpf('0.04321391826377224977441774')), (mpf(6)/10, mpf('0.05702025781460967637754953')), (mpf(7)/10, mpf('0.07468994353717944761143751')), (mpf(8)/10, mpf('0.09927369733882489703607378')), (mpf(9)/10, mpf('0.1401731269542615524091055')), (mpf(9)/10, mpf('0.1401731269542615524091055'))] for i in math1: m = i[0] q = calculate_nome(sqrt(m)) assert q.ae(i[1]) mp.dps = 15 def test_jtheta(): mp.dps = 25 z = q = zero for n in range(1,5): value = jtheta(n, z, q) assert(value == (n-1)//2) for q in [one, mpf(2)]: for n in range(1,5): raised = True try: r = jtheta(n, z, q) except: pass else: raised = False assert(raised) z = one/10 q = one/11 # Mathematical N[EllipticTheta[1, 1/10, 1/11], 25] res = mpf('0.1069552990104042681962096') result = jtheta(1, z, q) assert(result.ae(res)) # Mathematica N[EllipticTheta[2, 1/10, 1/11], 25] res = mpf('1.101385760258855791140606') result = jtheta(2, z, q) assert(result.ae(res)) # Mathematica N[EllipticTheta[3, 1/10, 1/11], 25] res = mpf('1.178319743354331061795905') result = jtheta(3, z, q) assert(result.ae(res)) # Mathematica N[EllipticTheta[4, 1/10, 1/11], 25] res = mpf('0.8219318954665153577314573') result = jtheta(4, z, q) assert(result.ae(res)) # test for sin zeros for jtheta(1, z, q) # test for cos zeros for jtheta(2, z, q) z1 = pi z2 = pi/2 for i in range(10): qstring = str(random.random()) q = mpf(qstring) result = jtheta(1, z1, q) assert(result.ae(0)) result = jtheta(2, z2, q) assert(result.ae(0)) mp.dps = 15 def test_jtheta_issue39(): # near the circle of covergence |q| = 1 the convergence slows # down; for |q| > Q_LIM the theta functions raise ValueError mp.dps = 30 mp.dps += 30 q = mpf(6)/10 - one/10**6 - mpf(8)/10 * j mp.dps -= 30 # Mathematica run first # N[EllipticTheta[3, 1, 6/10 - 10^-6 - 8/10*I], 2000] # then it works: # N[EllipticTheta[3, 1, 6/10 - 10^-6 - 8/10*I], 30] res = mpf('32.0031009628901652627099524264') + \ mpf('16.6153027998236087899308935624') * j result = jtheta(3, 1, q) # check that for abs(q) > Q_LIM a ValueError exception is raised mp.dps += 30 q = mpf(6)/10 - one/10**7 - mpf(8)/10 * j mp.dps -= 30 try: result = jtheta(3, 1, q) except ValueError: pass else: assert(False) # bug reported in issue39 mp.dps = 100 z = (1+j)/3 q = mpf(368983957219251)/10**15 + mpf(636363636363636)/10**15 * j # Mathematica N[EllipticTheta[1, z, q], 35] res = mpf('2.4439389177990737589761828991467471') + \ mpf('0.5446453005688226915290954851851490') *j mp.dps = 30 result = jtheta(1, z, q) assert(result.ae(res)) mp.dps = 80 z = 3 + 4*j q = 0.5 + 0.5*j r1 = jtheta(1, z, q) mp.dps = 15 r2 = jtheta(1, z, q) assert r1.ae(r2) mp.dps = 80 z = 3 + j q1 = exp(j*3) # longer test # for n in range(1, 6) for n in range(1, 2): mp.dps = 80 q = q1*(1 - mpf(1)/10**n) r1 = jtheta(1, z, q) mp.dps = 15 r2 = jtheta(1, z, q) assert r1.ae(r2) mp.dps = 15 # issue 39 about high derivatives assert jtheta(3, 4.5, 0.25, 9).ae(1359.04892680683) assert jtheta(3, 4.5, 0.25, 50).ae(-6.14832772630905e+33) mp.dps = 50 r = jtheta(3, 4.5, 0.25, 9) assert r.ae('1359.048926806828939547859396600218966947753213803') r = jtheta(3, 4.5, 0.25, 50) assert r.ae('-6148327726309051673317975084654262.4119215720343656') def test_jtheta_identities(): """ Tests the some of the jacobi identidies found in Abramowitz, Sec. 16.28, Pg. 576. The identities are tested to 1 part in 10^98. """ mp.dps = 110 eps1 = ldexp(eps, 30) for i in range(10): qstring = str(random.random()) q = mpf(qstring) zstring = str(10*random.random()) z = mpf(zstring) # Abramowitz 16.28.1 # v_1(z, q)**2 * v_4(0, q)**2 = v_3(z, q)**2 * v_2(0, q)**2 # - v_2(z, q)**2 * v_3(0, q)**2 term1 = (jtheta(1, z, q)**2) * (jtheta(4, zero, q)**2) term2 = (jtheta(3, z, q)**2) * (jtheta(2, zero, q)**2) term3 = (jtheta(2, z, q)**2) * (jtheta(3, zero, q)**2) equality = term1 - term2 + term3 assert(equality.ae(0, eps1)) zstring = str(100*random.random()) z = mpf(zstring) # Abramowitz 16.28.2 # v_2(z, q)**2 * v_4(0, q)**2 = v_4(z, q)**2 * v_2(0, q)**2 # - v_1(z, q)**2 * v_3(0, q)**2 term1 = (jtheta(2, z, q)**2) * (jtheta(4, zero, q)**2) term2 = (jtheta(4, z, q)**2) * (jtheta(2, zero, q)**2) term3 = (jtheta(1, z, q)**2) * (jtheta(3, zero, q)**2) equality = term1 - term2 + term3 assert(equality.ae(0, eps1)) # Abramowitz 16.28.3 # v_3(z, q)**2 * v_4(0, q)**2 = v_4(z, q)**2 * v_3(0, q)**2 # - v_1(z, q)**2 * v_2(0, q)**2 term1 = (jtheta(3, z, q)**2) * (jtheta(4, zero, q)**2) term2 = (jtheta(4, z, q)**2) * (jtheta(3, zero, q)**2) term3 = (jtheta(1, z, q)**2) * (jtheta(2, zero, q)**2) equality = term1 - term2 + term3 assert(equality.ae(0, eps1)) # Abramowitz 16.28.4 # v_4(z, q)**2 * v_4(0, q)**2 = v_3(z, q)**2 * v_3(0, q)**2 # - v_2(z, q)**2 * v_2(0, q)**2 term1 = (jtheta(4, z, q)**2) * (jtheta(4, zero, q)**2) term2 = (jtheta(3, z, q)**2) * (jtheta(3, zero, q)**2) term3 = (jtheta(2, z, q)**2) * (jtheta(2, zero, q)**2) equality = term1 - term2 + term3 assert(equality.ae(0, eps1)) # Abramowitz 16.28.5 # v_2(0, q)**4 + v_4(0, q)**4 == v_3(0, q)**4 term1 = (jtheta(2, zero, q))**4 term2 = (jtheta(4, zero, q))**4 term3 = (jtheta(3, zero, q))**4 equality = term1 + term2 - term3 assert(equality.ae(0, eps1)) mp.dps = 15 def test_jtheta_complex(): mp.dps = 30 z = mpf(1)/4 + j/8 q = mpf(1)/3 + j/7 # Mathematica N[EllipticTheta[1, 1/4 + I/8, 1/3 + I/7], 35] res = mpf('0.31618034835986160705729105731678285') + \ mpf('0.07542013825835103435142515194358975') * j r = jtheta(1, z, q) assert(mpc_ae(r, res)) # Mathematica N[EllipticTheta[2, 1/4 + I/8, 1/3 + I/7], 35] res = mpf('1.6530986428239765928634711417951828') + \ mpf('0.2015344864707197230526742145361455') * j r = jtheta(2, z, q) assert(mpc_ae(r, res)) # Mathematica N[EllipticTheta[3, 1/4 + I/8, 1/3 + I/7], 35] res = mpf('1.6520564411784228184326012700348340') + \ mpf('0.1998129119671271328684690067401823') * j r = jtheta(3, z, q) assert(mpc_ae(r, res)) # Mathematica N[EllipticTheta[4, 1/4 + I/8, 1/3 + I/7], 35] res = mpf('0.37619082382228348252047624089973824') - \ mpf('0.15623022130983652972686227200681074') * j r = jtheta(4, z, q) assert(mpc_ae(r, res)) # check some theta function identities mp.dos = 100 z = mpf(1)/4 + j/8 q = mpf(1)/3 + j/7 mp.dps += 10 a = [0,0, jtheta(2, 0, q), jtheta(3, 0, q), jtheta(4, 0, q)] t = [0, jtheta(1, z, q), jtheta(2, z, q), jtheta(3, z, q), jtheta(4, z, q)] r = [(t[2]*a[4])**2 - (t[4]*a[2])**2 + (t[1] *a[3])**2, (t[3]*a[4])**2 - (t[4]*a[3])**2 + (t[1] *a[2])**2, (t[1]*a[4])**2 - (t[3]*a[2])**2 + (t[2] *a[3])**2, (t[4]*a[4])**2 - (t[3]*a[3])**2 + (t[2] *a[2])**2, a[2]**4 + a[4]**4 - a[3]**4] mp.dps -= 10 for x in r: assert(mpc_ae(x, mpc(0))) mp.dps = 15 def test_djtheta(): mp.dps = 30 z = one/7 + j/3 q = one/8 + j/5 # Mathematica N[EllipticThetaPrime[1, 1/7 + I/3, 1/8 + I/5], 35] res = mpf('1.5555195883277196036090928995803201') - \ mpf('0.02439761276895463494054149673076275') * j result = jtheta(1, z, q, 1) assert(mpc_ae(result, res)) # Mathematica N[EllipticThetaPrime[2, 1/7 + I/3, 1/8 + I/5], 35] res = mpf('0.19825296689470982332701283509685662') - \ mpf('0.46038135182282106983251742935250009') * j result = jtheta(2, z, q, 1) assert(mpc_ae(result, res)) # Mathematica N[EllipticThetaPrime[3, 1/7 + I/3, 1/8 + I/5], 35] res = mpf('0.36492498415476212680896699407390026') - \ mpf('0.57743812698666990209897034525640369') * j result = jtheta(3, z, q, 1) assert(mpc_ae(result, res)) # Mathematica N[EllipticThetaPrime[4, 1/7 + I/3, 1/8 + I/5], 35] res = mpf('-0.38936892528126996010818803742007352') + \ mpf('0.66549886179739128256269617407313625') * j result = jtheta(4, z, q, 1) assert(mpc_ae(result, res)) for i in range(10): q = (one*random.random() + j*random.random())/2 # identity in Wittaker, Watson &21.41 a = jtheta(1, 0, q, 1) b = jtheta(2, 0, q)*jtheta(3, 0, q)*jtheta(4, 0, q) assert(a.ae(b)) # test higher derivatives mp.dps = 20 for q,z in [(one/3, one/5), (one/3 + j/8, one/5), (one/3, one/5 + j/8), (one/3 + j/7, one/5 + j/8)]: for n in [1, 2, 3, 4]: r = jtheta(n, z, q, 2) r1 = diff(lambda zz: jtheta(n, zz, q), z, n=2) assert r.ae(r1) r = jtheta(n, z, q, 3) r1 = diff(lambda zz: jtheta(n, zz, q), z, n=3) assert r.ae(r1) # identity in Wittaker, Watson &21.41 q = one/3 z = zero a = [0]*5 a[1] = jtheta(1, z, q, 3)/jtheta(1, z, q, 1) for n in [2,3,4]: a[n] = jtheta(n, z, q, 2)/jtheta(n, z, q) equality = a[2] + a[3] + a[4] - a[1] assert(equality.ae(0)) mp.dps = 15 def test_jsn(): """ Test some special cases of the sn(z, q) function. """ mp.dps = 100 # trival case result = jsn(zero, zero) assert(result == zero) # Abramowitz Table 16.5 # # sn(0, m) = 0 for i in range(10): qstring = str(random.random()) q = mpf(qstring) equality = jsn(zero, q) assert(equality.ae(0)) # Abramowitz Table 16.6.1 # # sn(z, 0) = sin(z), m == 0 # # sn(z, 1) = tanh(z), m == 1 # # It would be nice to test these, but I find that they run # in to numerical trouble. I'm currently treating as a boundary # case for sn function. mp.dps = 25 arg = one/10 #N[JacobiSN[1/10, 2^-100], 25] res = mpf('0.09983341664682815230681420') m = ldexp(one, -100) result = jsn(arg, m) assert(result.ae(res)) # N[JacobiSN[1/10, 1/10], 25] res = mpf('0.09981686718599080096451168') result = jsn(arg, arg) assert(result.ae(res)) mp.dps = 15 def test_jcn(): """ Test some special cases of the cn(z, q) function. """ mp.dps = 100 # Abramowitz Table 16.5 # cn(0, q) = 1 qstring = str(random.random()) q = mpf(qstring) cn = jcn(zero, q) assert(cn.ae(one)) # Abramowitz Table 16.6.2 # # cn(u, 0) = cos(u), m == 0 # # cn(u, 1) = sech(z), m == 1 # # It would be nice to test these, but I find that they run # in to numerical trouble. I'm currently treating as a boundary # case for cn function. mp.dps = 25 arg = one/10 m = ldexp(one, -100) #N[JacobiCN[1/10, 2^-100], 25] res = mpf('0.9950041652780257660955620') result = jcn(arg, m) assert(result.ae(res)) # N[JacobiCN[1/10, 1/10], 25] res = mpf('0.9950058256237368748520459') result = jcn(arg, arg) assert(result.ae(res)) mp.dps = 15 def test_jdn(): """ Test some special cases of the dn(z, q) function. """ mp.dps = 100 # Abramowitz Table 16.5 # dn(0, q) = 1 mstring = str(random.random()) m = mpf(mstring) dn = jdn(zero, m) assert(dn.ae(one)) mp.dps = 25 # N[JacobiDN[1/10, 1/10], 25] res = mpf('0.9995017055025556219713297') arg = one/10 result = jdn(arg, arg) assert(result.ae(res)) mp.dps = 15 def test_sn_cn_dn_identities(): """ Tests the some of the jacobi elliptic function identities found on Mathworld. Haven't found in Abramowitz. """ mp.dps = 100 N = 5 for i in range(N): qstring = str(random.random()) q = mpf(qstring) zstring = str(100*random.random()) z = mpf(zstring) # MathWorld # sn(z, q)**2 + cn(z, q)**2 == 1 term1 = jsn(z, q)**2 term2 = jcn(z, q)**2 equality = one - term1 - term2 assert(equality.ae(0)) # MathWorld # k**2 * sn(z, m)**2 + dn(z, m)**2 == 1 for i in range(N): mstring = str(random.random()) m = mpf(qstring) k = m.sqrt() zstring = str(10*random.random()) z = mpf(zstring) term1 = k**2 * jsn(z, m)**2 term2 = jdn(z, m)**2 equality = one - term1 - term2 assert(equality.ae(0)) for i in range(N): mstring = str(random.random()) m = mpf(mstring) k = m.sqrt() zstring = str(random.random()) z = mpf(zstring) # MathWorld # k**2 * cn(z, m)**2 + (1 - k**2) = dn(z, m)**2 term1 = k**2 * jcn(z, m)**2 term2 = 1 - k**2 term3 = jdn(z, m)**2 equality = term3 - term1 - term2 assert(equality.ae(0)) K = ellipk(k**2) # Abramowitz Table 16.5 # sn(K, m) = 1; K is K(k), first complete elliptic integral r = jsn(K, m) assert(r.ae(one)) # Abramowitz Table 16.5 # cn(K, q) = 0; K is K(k), first complete elliptic integral equality = jcn(K, m) assert(equality.ae(0)) # Abramowitz Table 16.6.3 # dn(z, 0) = 1, m == 0 z = m value = jdn(z, zero) assert(value.ae(one)) mp.dps = 15 def test_sn_cn_dn_complex(): mp.dps = 30 # N[JacobiSN[1/4 + I/8, 1/3 + I/7], 35] in Mathematica res = mpf('0.2495674401066275492326652143537') + \ mpf('0.12017344422863833381301051702823') * j u = mpf(1)/4 + j/8 m = mpf(1)/3 + j/7 r = jsn(u, m) assert(mpc_ae(r, res)) #N[JacobiCN[1/4 + I/8, 1/3 + I/7], 35] res = mpf('0.9762691700944007312693721148331') - \ mpf('0.0307203994181623243583169154824')*j r = jcn(u, m) assert r.real.ae(res.real) assert r.imag.ae(res.imag) assert(mpc_ae(r, res)) #N[JacobiDN[1/4 + I/8, 1/3 + I/7], 35] res = mpf('0.99639490163039577560547478589753039') - \ mpf('0.01346296520008176393432491077244994')*j r = jdn(u, m) assert(mpc_ae(r, res)) mp.dps = 15 def test_elliptic_integrals(): # Test cases from Carlson's paper mp.dps = 15 assert elliprd(0,2,1).ae(1.7972103521033883112) assert elliprd(2,3,4).ae(0.16510527294261053349) assert elliprd(j,-j,2).ae(0.65933854154219768919) assert elliprd(0,j,-j).ae(1.2708196271909686299 + 2.7811120159520578777j) assert elliprd(0,j-1,j).ae(-1.8577235439239060056 - 0.96193450888838559989j) assert elliprd(-2-j,-j,-1+j).ae(1.8249027393703805305 - 1.2218475784827035855j) for n in [5, 15, 30, 60, 100]: mp.dps = n assert elliprf(1,2,0).ae('1.3110287771460599052324197949455597068413774757158115814084108519003952935352071251151477664807145467230678763') assert elliprf(0.5,1,0).ae('1.854074677301371918433850347195260046217598823521766905585928045056021776838119978357271861650371897277771871') assert elliprf(j,-j,0).ae('1.854074677301371918433850347195260046217598823521766905585928045056021776838119978357271861650371897277771871') assert elliprf(j-1,j,0).ae(mpc('0.79612586584233913293056938229563057846592264089185680214929401744498956943287031832657642790719940442165621412', '-1.2138566698364959864300942567386038975419875860741507618279563735753073152507112254567291141460317931258599889')) assert elliprf(2,3,4).ae('0.58408284167715170669284916892566789240351359699303216166309375305508295130412919665541330837704050454472379308') assert elliprf(j,-j,2).ae('1.0441445654064360931078658361850779139591660747973017593275012615517220315993723776182276555339288363064476126') assert elliprf(j-1,j,1-j).ae(mpc('0.93912050218619371196624617169781141161485651998254431830645241993282941057500174238125105410055253623847335313', '-0.53296252018635269264859303449447908970360344322834582313172115220559316331271520508208025270300138589669326136')) assert elliprc(0,0.25).ae(+pi) assert elliprc(2.25,2).ae(+ln2) assert elliprc(0,j).ae(mpc('1.1107207345395915617539702475151734246536554223439225557713489017391086982748684776438317336911913093408525532', '-1.1107207345395915617539702475151734246536554223439225557713489017391086982748684776438317336911913093408525532')) assert elliprc(-j,j).ae(mpc('1.2260849569072198222319655083097718755633725139745941606203839524036426936825652935738621522906572884239069297', '-0.34471136988767679699935618332997956653521218571295874986708834375026550946053920574015526038040124556716711353')) assert elliprc(0.25,-2).ae(ln2/3) assert elliprc(j,-1).ae(mpc('0.77778596920447389875196055840799837589537035343923012237628610795937014001905822029050288316217145443865649819', '0.1983248499342877364755170948292130095921681309577950696116251029742793455964385947473103628983664877025779304')) assert elliprj(0,1,2,3).ae('0.77688623778582332014190282640545501102298064276022952731669118325952563819813258230708177398475643634103990878') assert elliprj(2,3,4,5).ae('0.14297579667156753833233879421985774801466647854232626336218889885463800128817976132826443904216546421431528308') assert elliprj(2,3,4,-1+j).ae(mpc('0.13613945827770535203521374457913768360237593025944342652613569368333226052158214183059386307242563164036672709', '-0.38207561624427164249600936454845112611060375760094156571007648297226090050927156176977091273224510621553615189')) assert elliprj(j,-j,0,2).ae('1.6490011662710884518243257224860232300246792717163891216346170272567376981346412066066050103935109581019055806') assert elliprj(-1+j,-1-j,1,2).ae('0.94148358841220238083044612133767270187474673547917988681610772381758628963408843935027667916713866133196845063') assert elliprj(j,-j,0,1-j).ae(mpc('1.8260115229009316249372594065790946657011067182850435297162034335356430755397401849070610280860044610878657501', '1.2290661908643471500163617732957042849283739403009556715926326841959667290840290081010472716420690899886276961')) assert elliprj(-1+j,-1-j,1,-3+j).ae(mpc('-0.61127970812028172123588152373622636829986597243716610650831553882054127570542477508023027578037045504958619422', '-1.0684038390006807880182112972232562745485871763154040245065581157751693730095703406209466903752930797510491155')) assert elliprj(-1+j,-2-j,-j,-1+j).ae(mpc('1.8249027393703805304622013339009022294368078659619988943515764258335975852685224202567854526307030593012768954', '-1.2218475784827035854568450371590419833166777535029296025352291308244564398645467465067845461070602841312456831')) assert elliprg(0,16,16).ae(+pi) assert elliprg(2,3,4).ae('1.7255030280692277601061148835701141842692457170470456590515892070736643637303053506944907685301315299153040991') assert elliprg(0,j,-j).ae('0.42360654239698954330324956174109581824072295516347109253028968632986700241706737986160014699730561497106114281') assert elliprg(j-1,j,0).ae(mpc('0.44660591677018372656731970402124510811555212083508861036067729944477855594654762496407405328607219895053798354', '0.70768352357515390073102719507612395221369717586839400605901402910893345301718731499237159587077682267374159282')) assert elliprg(-j,j-1,j).ae(mpc('0.36023392184473309033675652092928695596803358846377334894215349632203382573844427952830064383286995172598964266', '0.40348623401722113740956336997761033878615232917480045914551915169013722542827052849476969199578321834819903921')) assert elliprg(0, mpf('0.0796'), 4).ae('1.0284758090288040009838871385180217366569777284430590125081211090574701293154645750017813190805144572673802094') mp.dps = 15 def test_issue198(): assert isnan(qfrom(m=nan)) sympy-0.7.4.1/sympy/mpmath/tests/test_quad.py0000644000175000017500000000723112253362407021423 0ustar georgeskgeorgeskfrom sympy.mpmath import * def ae(a, b): return abs(a-b) < 10**(-mp.dps+5) def test_basic_integrals(): for prec in [15, 30, 100]: mp.dps = prec assert ae(quadts(lambda x: x**3 - 3*x**2, [-2, 4]), -12) assert ae(quadgl(lambda x: x**3 - 3*x**2, [-2, 4]), -12) assert ae(quadts(sin, [0, pi]), 2) assert ae(quadts(sin, [0, 2*pi]), 0) assert ae(quadts(exp, [-inf, -1]), 1/e) assert ae(quadts(lambda x: exp(-x), [0, inf]), 1) assert ae(quadts(lambda x: exp(-x*x), [-inf, inf]), sqrt(pi)) assert ae(quadts(lambda x: 1/(1+x*x), [-1, 1]), pi/2) assert ae(quadts(lambda x: 1/(1+x*x), [-inf, inf]), pi) assert ae(quadts(lambda x: 2*sqrt(1-x*x), [-1, 1]), pi) mp.dps = 15 def test_quad_symmetry(): assert quadts(sin, [-1, 1]) == 0 assert quadgl(sin, [-1, 1]) == 0 def test_quad_infinite_mirror(): # Check mirrored infinite interval assert ae(quad(lambda x: exp(-x*x), [inf,-inf]), -sqrt(pi)) assert ae(quad(lambda x: exp(x), [0,-inf]), -1) def test_quadgl_linear(): assert quadgl(lambda x: x, [0, 1], maxdegree=1).ae(0.5) def test_complex_integration(): assert quadts(lambda x: x, [0, 1+j]).ae(j) def test_quadosc(): mp.dps = 15 assert quadosc(lambda x: sin(x)/x, [0, inf], period=2*pi).ae(pi/2) # Double integrals def test_double_trivial(): assert ae(quadts(lambda x, y: x, [0, 1], [0, 1]), 0.5) assert ae(quadts(lambda x, y: x, [-1, 1], [-1, 1]), 0.0) def test_double_1(): assert ae(quadts(lambda x, y: cos(x+y/2), [-pi/2, pi/2], [0, pi]), 4) def test_double_2(): assert ae(quadts(lambda x, y: (x-1)/((1-x*y)*log(x*y)), [0, 1], [0, 1]), euler) def test_double_3(): assert ae(quadts(lambda x, y: 1/sqrt(1+x*x+y*y), [-1, 1], [-1, 1]), 4*log(2+sqrt(3))-2*pi/3) def test_double_4(): assert ae(quadts(lambda x, y: 1/(1-x*x * y*y), [0, 1], [0, 1]), pi**2 / 8) def test_double_5(): assert ae(quadts(lambda x, y: 1/(1-x*y), [0, 1], [0, 1]), pi**2 / 6) def test_double_6(): assert ae(quadts(lambda x, y: exp(-(x+y)), [0, inf], [0, inf]), 1) # fails def xtest_double_7(): assert ae(quadts(lambda x, y: exp(-x*x-y*y), [-inf, inf], [-inf, inf]), pi) # Test integrals from "Experimentation in Mathematics" by Borwein, # Bailey & Girgensohn def test_expmath_integrals(): for prec in [15, 30, 50]: mp.dps = prec assert ae(quadts(lambda x: x/sinh(x), [0, inf]), pi**2 / 4) assert ae(quadts(lambda x: log(x)**2 / (1+x**2), [0, inf]), pi**3 / 8) assert ae(quadts(lambda x: (1+x**2)/(1+x**4), [0, inf]), pi/sqrt(2)) assert ae(quadts(lambda x: log(x)/cosh(x)**2, [0, inf]), log(pi)-2*log(2)-euler) assert ae(quadts(lambda x: log(1+x**3)/(1-x+x**2), [0, inf]), 2*pi*log(3)/sqrt(3)) assert ae(quadts(lambda x: log(x)**2 / (x**2+x+1), [0, 1]), 8*pi**3 / (81*sqrt(3))) assert ae(quadts(lambda x: log(cos(x))**2, [0, pi/2]), pi/2 * (log(2)**2+pi**2/12)) assert ae(quadts(lambda x: x**2 / sin(x)**2, [0, pi/2]), pi*log(2)) assert ae(quadts(lambda x: x**2/sqrt(exp(x)-1), [0, inf]), 4*pi*(log(2)**2 + pi**2/12)) assert ae(quadts(lambda x: x*exp(-x)*sqrt(1-exp(-2*x)), [0, inf]), pi*(1+2*log(2))/8) mp.dps = 15 # Do not reach full accuracy def xtest_expmath_fail(): assert ae(quadts(lambda x: sqrt(tan(x)), [0, pi/2]), pi*sqrt(2)/2) assert ae(quadts(lambda x: atan(x)/(x*sqrt(1-x**2)), [0, 1]), pi*log(1+sqrt(2))/2) assert ae(quadts(lambda x: log(1+x**2)/x**2, [0, 1]), pi/2-log(2)) assert ae(quadts(lambda x: x**2/((1+x**4)*sqrt(1-x**4)), [0, 1]), pi/8) sympy-0.7.4.1/sympy/mpmath/tests/test_trig.py0000644000175000017500000001153012253362407021433 0ustar georgeskgeorgeskfrom sympy.mpmath import * from sympy.mpmath.libmp import * def test_trig_misc_hard(): mp.prec = 53 # Worst-case input for an IEEE double, from a paper by Kahan x = ldexp(6381956970095103,797) assert cos(x) == mpf('-4.6871659242546277e-19') assert sin(x) == 1 mp.prec = 150 a = mpf(10**50) mp.prec = 53 assert sin(a).ae(-0.7896724934293100827) assert cos(a).ae(-0.6135286082336635622) # Check relative accuracy close to x = zero assert sin(1e-100) == 1e-100 # when rounding to nearest assert sin(1e-6).ae(9.999999999998333e-007, rel_eps=2e-15, abs_eps=0) assert sin(1e-6j).ae(1.0000000000001666e-006j, rel_eps=2e-15, abs_eps=0) assert sin(-1e-6j).ae(-1.0000000000001666e-006j, rel_eps=2e-15, abs_eps=0) assert cos(1e-100) == 1 assert cos(1e-6).ae(0.9999999999995) assert cos(-1e-6j).ae(1.0000000000005) assert tan(1e-100) == 1e-100 assert tan(1e-6).ae(1.0000000000003335e-006, rel_eps=2e-15, abs_eps=0) assert tan(1e-6j).ae(9.9999999999966644e-007j, rel_eps=2e-15, abs_eps=0) assert tan(-1e-6j).ae(-9.9999999999966644e-007j, rel_eps=2e-15, abs_eps=0) def test_trig_near_zero(): mp.dps = 15 for r in [round_nearest, round_down, round_up, round_floor, round_ceiling]: assert sin(0, rounding=r) == 0 assert cos(0, rounding=r) == 1 a = mpf('1e-100') b = mpf('-1e-100') assert sin(a, rounding=round_nearest) == a assert sin(a, rounding=round_down) < a assert sin(a, rounding=round_floor) < a assert sin(a, rounding=round_up) >= a assert sin(a, rounding=round_ceiling) >= a assert sin(b, rounding=round_nearest) == b assert sin(b, rounding=round_down) > b assert sin(b, rounding=round_floor) <= b assert sin(b, rounding=round_up) <= b assert sin(b, rounding=round_ceiling) > b assert cos(a, rounding=round_nearest) == 1 assert cos(a, rounding=round_down) < 1 assert cos(a, rounding=round_floor) < 1 assert cos(a, rounding=round_up) == 1 assert cos(a, rounding=round_ceiling) == 1 assert cos(b, rounding=round_nearest) == 1 assert cos(b, rounding=round_down) < 1 assert cos(b, rounding=round_floor) < 1 assert cos(b, rounding=round_up) == 1 assert cos(b, rounding=round_ceiling) == 1 def test_trig_near_n_pi(): mp.dps = 15 a = [n*pi for n in [1, 2, 6, 11, 100, 1001, 10000, 100001]] mp.dps = 135 a.append(10**100 * pi) mp.dps = 15 assert sin(a[0]) == mpf('1.2246467991473531772e-16') assert sin(a[1]) == mpf('-2.4492935982947063545e-16') assert sin(a[2]) == mpf('-7.3478807948841190634e-16') assert sin(a[3]) == mpf('4.8998251578625894243e-15') assert sin(a[4]) == mpf('1.9643867237284719452e-15') assert sin(a[5]) == mpf('-8.8632615209684813458e-15') assert sin(a[6]) == mpf('-4.8568235395684898392e-13') assert sin(a[7]) == mpf('3.9087342299491231029e-11') assert sin(a[8]) == mpf('-1.369235466754566993528e-36') r = round_nearest assert cos(a[0], rounding=r) == -1 assert cos(a[1], rounding=r) == 1 assert cos(a[2], rounding=r) == 1 assert cos(a[3], rounding=r) == -1 assert cos(a[4], rounding=r) == 1 assert cos(a[5], rounding=r) == -1 assert cos(a[6], rounding=r) == 1 assert cos(a[7], rounding=r) == -1 assert cos(a[8], rounding=r) == 1 r = round_up assert cos(a[0], rounding=r) == -1 assert cos(a[1], rounding=r) == 1 assert cos(a[2], rounding=r) == 1 assert cos(a[3], rounding=r) == -1 assert cos(a[4], rounding=r) == 1 assert cos(a[5], rounding=r) == -1 assert cos(a[6], rounding=r) == 1 assert cos(a[7], rounding=r) == -1 assert cos(a[8], rounding=r) == 1 r = round_down assert cos(a[0], rounding=r) > -1 assert cos(a[1], rounding=r) < 1 assert cos(a[2], rounding=r) < 1 assert cos(a[3], rounding=r) > -1 assert cos(a[4], rounding=r) < 1 assert cos(a[5], rounding=r) > -1 assert cos(a[6], rounding=r) < 1 assert cos(a[7], rounding=r) > -1 assert cos(a[8], rounding=r) < 1 r = round_floor assert cos(a[0], rounding=r) == -1 assert cos(a[1], rounding=r) < 1 assert cos(a[2], rounding=r) < 1 assert cos(a[3], rounding=r) == -1 assert cos(a[4], rounding=r) < 1 assert cos(a[5], rounding=r) == -1 assert cos(a[6], rounding=r) < 1 assert cos(a[7], rounding=r) == -1 assert cos(a[8], rounding=r) < 1 r = round_ceiling assert cos(a[0], rounding=r) > -1 assert cos(a[1], rounding=r) == 1 assert cos(a[2], rounding=r) == 1 assert cos(a[3], rounding=r) > -1 assert cos(a[4], rounding=r) == 1 assert cos(a[5], rounding=r) > -1 assert cos(a[6], rounding=r) == 1 assert cos(a[7], rounding=r) > -1 assert cos(a[8], rounding=r) == 1 mp.dps = 15 if __name__ == '__main__': for f in globals().keys(): if f.startswith("test_"): print(f) globals()[f]() sympy-0.7.4.1/sympy/mpmath/tests/test_matrices.py0000644000175000017500000001301312253362407022273 0ustar georgeskgeorgeskfrom sympy.mpmath import * def test_matrix_basic(): A1 = matrix(3) for i in range(3): A1[i,i] = 1 assert A1 == eye(3) assert A1 == matrix(A1) A2 = matrix(3, 2) assert not A2._matrix__data A3 = matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) assert list(A3) == list(range(1, 10)) A3[1,1] = 0 assert not (1, 1) in A3._matrix__data A4 = matrix([[1, 2, 3], [4, 5, 6]]) A5 = matrix([[6, -1], [3, 2], [0, -3]]) assert A4 * A5 == matrix([[12, -6], [39, -12]]) assert A1 * A3 == A3 * A1 == A3 try: A2 * A2 assert False except ValueError: pass l = [[10, 20, 30], [40, 0, 60], [70, 80, 90]] A6 = matrix(l) assert A6.tolist() == l assert A6 == eval(repr(A6)) A6 = matrix(A6, force_type=float) assert A6 == eval(repr(A6)) assert A6*1j == eval(repr(A6*1j)) assert A3 * 10 == 10 * A3 == A6 assert A2.rows == 3 assert A2.cols == 2 A3.rows = 2 A3.cols = 2 assert len(A3._matrix__data) == 3 assert A4 + A4 == 2*A4 try: A4 + A2 except ValueError: pass assert sum(A1 - A1) == 0 A7 = matrix([[1, 2], [3, 4], [5, 6], [7, 8]]) x = matrix([10, -10]) assert A7*x == matrix([-10, -10, -10, -10]) A8 = ones(5) assert sum((A8 + 1) - (2 - zeros(5))) == 0 assert (1 + ones(4)) / 2 - 1 == zeros(4) assert eye(3)**10 == eye(3) try: A7**2 assert False except ValueError: pass A9 = randmatrix(3) A10 = matrix(A9) A9[0,0] = -100 assert A9 != A10 assert nstr(A9) def test_matrix_slices(): A = matrix([ [1, 2, 3], [4, 5 ,6], [7, 8 ,9]]) V = matrix([1,2,3,4,5]) # Get slice assert A[:,:] == A assert A[:,1] == matrix([[2],[5],[8]]) assert A[2,:] == matrix([[7, 8 ,9]]) assert A[1:3,1:3] == matrix([[5,6],[8,9]]) assert V[2:4] == matrix([3,4]) try: A6 = A[:,1:6] assert False except IndexError: pass # Assign slice with matrix A1 = matrix(3) A1[:,:] = A assert A1[:,:] == matrix([[1, 2, 3], [4, 5 ,6], [7, 8 ,9]]) A1[0,:] = matrix([[10, 11, 12]]) assert A1 == matrix([ [10, 11, 12], [4, 5 ,6], [7, 8 ,9]]) A1[:,2] = matrix([[13], [14], [15]]) assert A1 == matrix([ [10, 11, 13], [4, 5 ,14], [7, 8 ,15]]) A1[:2,:2] = matrix([[16, 17], [18 , 19]]) assert A1 == matrix([ [16, 17, 13], [18, 19 ,14], [7, 8 ,15]]) V[1:3] = 10 assert V == matrix([1,10,10,4,5]) try: A1[2,:] = A[:,1] assert False except ValueError: pass try: A1[2,1:20] = A[:,:] assert False except IndexError: pass # Assign slice with scalar A1[:,2] = 10 assert A1 == matrix([ [16, 17, 10], [18, 19 ,10], [7, 8 ,10]]) A1[:,:] = 40 for x in A1: assert x == 40 def test_matrix_power(): A = matrix([[1, 2], [3, 4]]) assert A**2 == A*A assert A**3 == A*A*A assert A**-1 == inverse(A) assert A**-2 == inverse(A*A) def test_matrix_transform(): A = matrix([[1, 2], [3, 4], [5, 6]]) assert A.T == A.transpose() == matrix([[1, 3, 5], [2, 4, 6]]) swap_row(A, 1, 2) assert A == matrix([[1, 2], [5, 6], [3, 4]]) l = [1, 2] swap_row(l, 0, 1) assert l == [2, 1] assert extend(eye(3), [1,2,3]) == matrix([[1,0,0,1],[0,1,0,2],[0,0,1,3]]) def test_matrix_conjugate(): A = matrix([[1 + j, 0], [2, j]]) assert A.conjugate() == matrix([[mpc(1, -1), 0], [2, mpc(0, -1)]]) assert A.transpose_conj() == A.H == matrix([[mpc(1, -1), 2], [0, mpc(0, -1)]]) def test_matrix_creation(): assert diag([1, 2, 3]) == matrix([[1, 0, 0], [0, 2, 0], [0, 0, 3]]) A1 = ones(2, 3) assert A1.rows == 2 and A1.cols == 3 for a in A1: assert a == 1 A2 = zeros(3, 2) assert A2.rows == 3 and A2.cols == 2 for a in A2: assert a == 0 assert randmatrix(10) != randmatrix(10) one = mpf(1) assert hilbert(3) == matrix([[one, one/2, one/3], [one/2, one/3, one/4], [one/3, one/4, one/5]]) def test_norms(): # matrix norms A = matrix([[1, -2], [-3, -1], [2, 1]]) assert mnorm(A,1) == 6 assert mnorm(A,inf) == 4 assert mnorm(A,'F') == sqrt(20) # vector norms assert norm(-3) == 3 x = [1, -2, 7, -12] assert norm(x, 1) == 22 assert round(norm(x, 2), 10) == 14.0712472795 assert round(norm(x, 10), 10) == 12.0054633727 assert norm(x, inf) == 12 def test_vector(): x = matrix([0, 1, 2, 3, 4]) assert x == matrix([[0], [1], [2], [3], [4]]) assert x[3] == 3 assert len(x._matrix__data) == 4 assert list(x) == list(range(5)) x[0] = -10 x[4] = 0 assert x[0] == -10 assert len(x) == len(x.T) == 5 assert x.T*x == matrix([[114]]) def test_matrix_copy(): A = ones(6) B = A.copy() assert A == B B[0,0] = 0 assert A != B def test_matrix_numpy(): from sympy.external import import_module numpy = import_module('numpy') if not numpy: return l = [[1, 2], [3, 4], [5, 6]] a = numpy.matrix(l) assert matrix(l) == matrix(a) sympy-0.7.4.1/sympy/mpmath/tests/test_summation.py0000644000175000017500000000351112253362407022502 0ustar georgeskgeorgeskfrom sympy.mpmath import * def test_sumem(): mp.dps = 15 assert sumem(lambda k: 1/k**2.5, [50, 100]).ae(0.0012524505324784962) assert sumem(lambda k: k**4 + 3*k + 1, [10, 100]).ae(2050333103) def test_nsum(): mp.dps = 15 assert nsum(lambda x: x**2, [1, 3]) == 14 assert nsum(lambda k: 1/factorial(k), [0, inf]).ae(e) assert nsum(lambda k: (-1)**(k+1) / k, [1, inf]).ae(log(2)) assert nsum(lambda k: (-1)**(k+1) / k**2, [1, inf]).ae(pi**2 / 12) assert nsum(lambda k: (-1)**k / log(k), [2, inf]).ae(0.9242998972229388) assert nsum(lambda k: 1/k**2, [1, inf]).ae(pi**2 / 6) assert nsum(lambda k: 2**k/fac(k), [0, inf]).ae(exp(2)) assert nsum(lambda k: 1/k**2, [4, inf], method='e').ae(0.2838229557371153) def test_nprod(): mp.dps = 15 assert nprod(lambda k: exp(1/k**2), [1,inf], method='r').ae(exp(pi**2/6)) assert nprod(lambda x: x**2, [1, 3]) == 36 def test_fsum(): mp.dps = 15 assert fsum([]) == 0 assert fsum([-4]) == -4 assert fsum([2,3]) == 5 assert fsum([1e-100,1]) == 1 assert fsum([1,1e-100]) == 1 assert fsum([1e100,1]) == 1e100 assert fsum([1,1e100]) == 1e100 assert fsum([1e-100,0]) == 1e-100 assert fsum([1e-100,1e100,1e-100]) == 1e100 assert fsum([2,1+1j,1]) == 4+1j assert fsum([2,inf,3]) == inf assert fsum([2,-1], absolute=1) == 3 assert fsum([2,-1], squared=1) == 5 assert fsum([1,1+j], squared=1) == 1+2j assert fsum([1,3+4j], absolute=1) == 6 assert fsum([1,2+3j], absolute=1, squared=1) == 14 assert isnan(fsum([inf,-inf])) assert fsum([inf,-inf], absolute=1) == inf assert fsum([inf,-inf], squared=1) == inf assert fsum([inf,-inf], absolute=1, squared=1) == inf assert iv.fsum([1,mpi(2,3)]) == mpi(3,4) def test_fprod(): mp.dps = 15 assert fprod([]) == 1 assert fprod([2,3]) == 6 sympy-0.7.4.1/sympy/mpmath/tests/extratest_gamma.py0000644000175000017500000001612412253362407022620 0ustar georgeskgeorgeskfrom __future__ import print_function from mpmath import * from mpmath.libmp import ifac import sys if "-dps" in sys.argv: maxdps = int(sys.argv[sys.argv.index("-dps")+1]) else: maxdps = 1000 raise_ = "-raise" in sys.argv errcount = 0 def check(name, func, z, y): global errcount try: x = func(z) except: errcount += 1 if raise_: raise print() print(name) print("EXCEPTION") import traceback traceback.print_tb(sys.exc_info()[2]) print() return xre = x.real xim = x.imag yre = y.real yim = y.imag tol = eps*8 err = 0 if abs(xre-yre) > abs(yre)*tol: err = 1 print() print("Error! %s (re = %s, wanted %s, err=%s)" % (name, nstr(xre,10), nstr(yre,10), nstr(abs(xre-yre)))) errcount += 1 if raise_: raise SystemExit if abs(xim-yim) > abs(yim)*tol: err = 1 print() print("Error! %s (im = %s, wanted %s, err=%s)" % (name, nstr(xim,10), nstr(yim,10), nstr(abs(xim-yim)))) errcount += 1 if raise_: raise SystemExit if not err: print("%s ok;" % name, end=' ') def testcase(case): z, result = case print("Testing z =", z) mp.dps = 1010 z = eval(z) mp.dps = maxdps + 50 if result is None: gamma_val = gamma(z) loggamma_val = loggamma(z) factorial_val = factorial(z) rgamma_val = rgamma(z) else: loggamma_val = eval(result) gamma_val = exp(loggamma_val) factorial_val = z * gamma_val rgamma_val = 1/gamma_val for dps in [5, 10, 15, 25, 40, 60, 90, 120, 250, 600, 1000, 1800, 3600]: if dps > maxdps: break mp.dps = dps print("dps = %s" % dps) check("gamma", gamma, z, gamma_val) check("rgamma", rgamma, z, rgamma_val) check("loggamma", loggamma, z, loggamma_val) check("factorial", factorial, z, factorial_val) print() mp.dps = 15 testcases = [] # Basic values for n in range(1,200) + range(201,2000,17): testcases.append(["%s" % n, None]) for n in range(-200,200): testcases.append(["%s+0.5" % n, None]) testcases.append(["%s+0.37" % n, None]) testcases += [\ ["(0.1+1j)", None], ["(-0.1+1j)", None], ["(0.1-1j)", None], ["(-0.1-1j)", None], ["10j", None], ["-10j", None], ["100j", None], ["10000j", None], ["-10000000j", None], ["(10**100)*j", None], ["125+(10**100)*j", None], ["-125+(10**100)*j", None], ["(10**10)*(1+j)", None], ["(10**10)*(-1+j)", None], ["(10**100)*(1+j)", None], ["(10**100)*(-1+j)", None], ["(1.5-1j)", None], ["(6+4j)", None], ["(4+1j)", None], ["(3.5+2j)", None], ["(1.5-1j)", None], ["(-6-4j)", None], ["(-2-3j)", None], ["(-2.5-2j)", None], ["(4+1j)", None], ["(3+3j)", None], ["(2-2j)", None], ["1", "0"], ["2", "0"], ["3", "log(2)"], ["4", "log(6)"], ["5", "log(24)"], ["0.5", "log(pi)/2"], ["1.5", "log(sqrt(pi)/2)"], ["2.5", "log(3*sqrt(pi)/4)"], ["mpf('0.37')", None], ["0.25", "log(sqrt(2*sqrt(2*pi**3)/agm(1,sqrt(2))))"], ["-0.4", None], ["mpf('-1.9')", None], ["mpf('12.8')", None], ["mpf('33.7')", None], ["mpf('95.2')", None], ["mpf('160.3')", None], ["mpf('2057.8')", None], ["25", "log(ifac(24))"], ["80", "log(ifac(79))"], ["500", "log(ifac(500-1))"], ["8000", "log(ifac(8000-1))"], ["8000.5", None], ["mpf('8000.1')", None], ["mpf('1.37e10')", None], ["mpf('1.37e10')*(1+j)", None], ["mpf('1.37e10')*(-1+j)", None], ["mpf('1.37e10')*(-1-j)", None], ["mpf('1.37e10')*(-1+j)", None], ["mpf('1.37e100')", None], ["mpf('1.37e100')*(1+j)", None], ["mpf('1.37e100')*(-1+j)", None], ["mpf('1.37e100')*(-1-j)", None], ["mpf('1.37e100')*(-1+j)", None], ["3+4j", "mpc('" "-1.7566267846037841105306041816232757851567066070613445016197619371316057169" "4723618263960834804618463052988607348289672535780644470689771115236512106002" "5970873471563240537307638968509556191696167970488390423963867031934333890838" "8009531786948197210025029725361069435208930363494971027388382086721660805397" "9163230643216054580167976201709951509519218635460317367338612500626714783631" "7498317478048447525674016344322545858832610325861086336204591943822302971823" "5161814175530618223688296232894588415495615809337292518431903058265147109853" "1710568942184987827643886816200452860853873815413367529829631430146227470517" "6579967222200868632179482214312673161276976117132204633283806161971389519137" "1243359764435612951384238091232760634271570950240717650166551484551654327989" "9360285030081716934130446150245110557038117075172576825490035434069388648124" "6678152254554001586736120762641422590778766100376515737713938521275749049949" "1284143906816424244705094759339932733567910991920631339597278805393743140853" "391550313363278558195609260225928','" "4.74266443803465792819488940755002274088830335171164611359052405215840070271" "5906813009373171139767051863542508136875688550817670379002790304870822775498" "2809996675877564504192565392367259119610438951593128982646945990372179860613" "4294436498090428077839141927485901735557543641049637962003652638924845391650" "9546290137755550107224907606529385248390667634297183361902055842228798984200" "9591180450211798341715874477629099687609819466457990642030707080894518168924" "6805549314043258530272479246115112769957368212585759640878745385160943755234" "9398036774908108204370323896757543121853650025529763655312360354244898913463" "7115955702828838923393113618205074162812089732064414530813087483533203244056" "0546577484241423134079056537777170351934430586103623577814746004431994179990" "5318522939077992613855205801498201930221975721246498720895122345420698451980" "0051215797310305885845964334761831751370672996984756815410977750799748813563" "8784405288158432214886648743541773208808731479748217023665577802702269468013" "673719173759245720489020315779001')"], ] for z in [4, 14, 34, 64]: testcases.append(["(2+j)*%s/3" % z, None]) testcases.append(["(-2+j)*%s/3" % z, None]) testcases.append(["(1+2*j)*%s/3" % z, None]) testcases.append(["(2-j)*%s/3" % z, None]) testcases.append(["(20+j)*%s/3" % z, None]) testcases.append(["(-20+j)*%s/3" % z, None]) testcases.append(["(1+20*j)*%s/3" % z, None]) testcases.append(["(20-j)*%s/3" % z, None]) testcases.append(["(200+j)*%s/3" % z, None]) testcases.append(["(-200+j)*%s/3" % z, None]) testcases.append(["(1+200*j)*%s/3" % z, None]) testcases.append(["(200-j)*%s/3" % z, None]) # Poles for n in [0,1,2,3,4,25,-1,-2,-3,-4,-20,-21,-50,-51,-200,-201,-20000,-20001]: for t in ['1e-5', '1e-20', '1e-100', '1e-10000']: testcases.append(["fadd(%s,'%s',exact=True)" % (n, t), None]) testcases.append(["fsub(%s,'%s',exact=True)" % (n, t), None]) testcases.append(["fadd(%s,'%sj',exact=True)" % (n, t), None]) testcases.append(["fsub(%s,'%sj',exact=True)" % (n, t), None]) if __name__ == "__main__": from timeit import default_timer as clock tot_time = 0.0 for case in testcases: t1 = clock() testcase(case) t2 = clock() print("Test time:", t2-t1) print() tot_time += (t2-t1) print("Total time:", tot_time) print("Errors:", errcount) sympy-0.7.4.1/sympy/mpmath/tests/test_gammazeta.py0000644000175000017500000006501212253362407022440 0ustar georgeskgeorgeskfrom sympy.mpmath import * from sympy.mpmath.libmp import round_up, from_float, mpf_zeta_int from sympy.utilities.pytest import XFAIL def test_zeta_int_bug(): assert mpf_zeta_int(0, 10) == from_float(-0.5) def test_bernoulli(): assert bernfrac(0) == (1,1) assert bernfrac(1) == (-1,2) assert bernfrac(2) == (1,6) assert bernfrac(3) == (0,1) assert bernfrac(4) == (-1,30) assert bernfrac(5) == (0,1) assert bernfrac(6) == (1,42) assert bernfrac(8) == (-1,30) assert bernfrac(10) == (5,66) assert bernfrac(12) == (-691,2730) assert bernfrac(18) == (43867,798) p, q = bernfrac(228) assert p % 10**10 == 164918161 assert q == 625170 p, q = bernfrac(1000) assert p % 10**10 == 7950421099 assert q == 342999030 mp.dps = 15 assert bernoulli(0) == 1 assert bernoulli(1) == -0.5 assert bernoulli(2).ae(1./6) assert bernoulli(3) == 0 assert bernoulli(4).ae(-1./30) assert bernoulli(5) == 0 assert bernoulli(6).ae(1./42) assert str(bernoulli(10)) == '0.0757575757575758' assert str(bernoulli(234)) == '7.62772793964344e+267' assert str(bernoulli(10**5)) == '-5.82229431461335e+376755' assert str(bernoulli(10**8+2)) == '1.19570355039953e+676752584' mp.dps = 50 assert str(bernoulli(10)) == '0.075757575757575757575757575757575757575757575757576' assert str(bernoulli(234)) == '7.6277279396434392486994969020496121553385863373331e+267' assert str(bernoulli(10**5)) == '-5.8222943146133508236497045360612887555320691004308e+376755' assert str(bernoulli(10**8+2)) == '1.1957035503995297272263047884604346914602088317782e+676752584' mp.dps = 1000 assert bernoulli(10).ae(mpf(5)/66) mp.dps = 50000 assert bernoulli(10).ae(mpf(5)/66) mp.dps = 15 def test_bernpoly_eulerpoly(): mp.dps = 15 assert bernpoly(0,-1).ae(1) assert bernpoly(0,0).ae(1) assert bernpoly(0,'1/2').ae(1) assert bernpoly(0,'3/4').ae(1) assert bernpoly(0,1).ae(1) assert bernpoly(0,2).ae(1) assert bernpoly(1,-1).ae('-3/2') assert bernpoly(1,0).ae('-1/2') assert bernpoly(1,'1/2').ae(0) assert bernpoly(1,'3/4').ae('1/4') assert bernpoly(1,1).ae('1/2') assert bernpoly(1,2).ae('3/2') assert bernpoly(2,-1).ae('13/6') assert bernpoly(2,0).ae('1/6') assert bernpoly(2,'1/2').ae('-1/12') assert bernpoly(2,'3/4').ae('-1/48') assert bernpoly(2,1).ae('1/6') assert bernpoly(2,2).ae('13/6') assert bernpoly(3,-1).ae(-3) assert bernpoly(3,0).ae(0) assert bernpoly(3,'1/2').ae(0) assert bernpoly(3,'3/4').ae('-3/64') assert bernpoly(3,1).ae(0) assert bernpoly(3,2).ae(3) assert bernpoly(4,-1).ae('119/30') assert bernpoly(4,0).ae('-1/30') assert bernpoly(4,'1/2').ae('7/240') assert bernpoly(4,'3/4').ae('7/3840') assert bernpoly(4,1).ae('-1/30') assert bernpoly(4,2).ae('119/30') assert bernpoly(5,-1).ae(-5) assert bernpoly(5,0).ae(0) assert bernpoly(5,'1/2').ae(0) assert bernpoly(5,'3/4').ae('25/1024') assert bernpoly(5,1).ae(0) assert bernpoly(5,2).ae(5) assert bernpoly(10,-1).ae('665/66') assert bernpoly(10,0).ae('5/66') assert bernpoly(10,'1/2').ae('-2555/33792') assert bernpoly(10,'3/4').ae('-2555/34603008') assert bernpoly(10,1).ae('5/66') assert bernpoly(10,2).ae('665/66') assert bernpoly(11,-1).ae(-11) assert bernpoly(11,0).ae(0) assert bernpoly(11,'1/2').ae(0) assert bernpoly(11,'3/4').ae('-555731/4194304') assert bernpoly(11,1).ae(0) assert bernpoly(11,2).ae(11) assert eulerpoly(0,-1).ae(1) assert eulerpoly(0,0).ae(1) assert eulerpoly(0,'1/2').ae(1) assert eulerpoly(0,'3/4').ae(1) assert eulerpoly(0,1).ae(1) assert eulerpoly(0,2).ae(1) assert eulerpoly(1,-1).ae('-3/2') assert eulerpoly(1,0).ae('-1/2') assert eulerpoly(1,'1/2').ae(0) assert eulerpoly(1,'3/4').ae('1/4') assert eulerpoly(1,1).ae('1/2') assert eulerpoly(1,2).ae('3/2') assert eulerpoly(2,-1).ae(2) assert eulerpoly(2,0).ae(0) assert eulerpoly(2,'1/2').ae('-1/4') assert eulerpoly(2,'3/4').ae('-3/16') assert eulerpoly(2,1).ae(0) assert eulerpoly(2,2).ae(2) assert eulerpoly(3,-1).ae('-9/4') assert eulerpoly(3,0).ae('1/4') assert eulerpoly(3,'1/2').ae(0) assert eulerpoly(3,'3/4').ae('-11/64') assert eulerpoly(3,1).ae('-1/4') assert eulerpoly(3,2).ae('9/4') assert eulerpoly(4,-1).ae(2) assert eulerpoly(4,0).ae(0) assert eulerpoly(4,'1/2').ae('5/16') assert eulerpoly(4,'3/4').ae('57/256') assert eulerpoly(4,1).ae(0) assert eulerpoly(4,2).ae(2) assert eulerpoly(5,-1).ae('-3/2') assert eulerpoly(5,0).ae('-1/2') assert eulerpoly(5,'1/2').ae(0) assert eulerpoly(5,'3/4').ae('361/1024') assert eulerpoly(5,1).ae('1/2') assert eulerpoly(5,2).ae('3/2') assert eulerpoly(10,-1).ae(2) assert eulerpoly(10,0).ae(0) assert eulerpoly(10,'1/2').ae('-50521/1024') assert eulerpoly(10,'3/4').ae('-36581523/1048576') assert eulerpoly(10,1).ae(0) assert eulerpoly(10,2).ae(2) assert eulerpoly(11,-1).ae('-699/4') assert eulerpoly(11,0).ae('691/4') assert eulerpoly(11,'1/2').ae(0) assert eulerpoly(11,'3/4').ae('-512343611/4194304') assert eulerpoly(11,1).ae('-691/4') assert eulerpoly(11,2).ae('699/4') # Potential accuracy issues assert bernpoly(10000,10000).ae('5.8196915936323387117e+39999') assert bernpoly(200,17.5).ae(3.8048418524583064909e244) assert eulerpoly(200,17.5).ae(-3.7309911582655785929e275) def test_gamma(): mp.dps = 15 assert gamma(0.25).ae(3.6256099082219083119) assert gamma(0.0001).ae(9999.4228832316241908) assert gamma(300).ae('1.0201917073881354535e612') assert gamma(-0.5).ae(-3.5449077018110320546) assert gamma(-7.43).ae(0.00026524416464197007186) assert gamma(1+1j).ae(0.49801566811835604271 - 0.15494982830181068512j) assert gamma(-1+0.01j).ae(-0.422733904013474115 + 99.985883082635367436j) assert gamma(20+30j).ae(-1453876687.5534810 + 1163777777.8031573j) # Should always give exact factorials when they can # be represented as mpfs under the current working precision fact = 1 for i in range(1, 18): assert gamma(i) == fact fact *= i for dps in [170, 600]: fact = 1 mp.dps = dps for i in range(1, 105): assert gamma(i) == fact fact *= i mp.dps = 100 assert gamma(0.5).ae(sqrt(pi)) mp.dps = 15 assert factorial(0) == fac(0) == 1 assert factorial(3) == 6 assert isnan(gamma(nan)) assert gamma(1100).ae('4.8579168073569433667e2866') assert rgamma(0) == 0 assert rgamma(-1) == 0 assert rgamma(2) == 1.0 assert rgamma(3) == 0.5 assert loggamma(2+8j).ae(-8.5205176753667636926 + 10.8569497125597429366j) assert loggamma('1e10000').ae('2.302485092994045684017991e10004') assert loggamma('1e10000j').ae(mpc('-1.570796326794896619231322e10000','2.302485092994045684017991e10004')) @XFAIL def test_gamma_failing(): assert gamma(Rational(1,2)) == gamma(0.5) assert gamma(Rational(-7,3)).ae(gamma(mpf(-7)/3)) def test_fac2(): mp.dps = 15 assert [fac2(n) for n in range(10)] == [1,1,2,3,8,15,48,105,384,945] assert fac2(-5).ae(1./3) assert fac2(-11).ae(-1./945) assert fac2(50).ae(5.20469842636666623e32) assert fac2(0.5+0.75j).ae(0.81546769394688069176-0.34901016085573266889j) assert fac2(inf) == inf assert isnan(fac2(-inf)) def test_gamma_quotients(): mp.dps = 15 h = 1e-8 ep = 1e-4 G = gamma assert gammaprod([-1],[-3,-4]) == 0 assert gammaprod([-1,0],[-5]) == inf assert abs(gammaprod([-1],[-2]) - G(-1+h)/G(-2+h)) < 1e-4 assert abs(gammaprod([-4,-3],[-2,0]) - G(-4+h)*G(-3+h)/G(-2+h)/G(0+h)) < 1e-4 assert rf(3,0) == 1 assert rf(2.5,1) == 2.5 assert rf(-5,2) == 20 assert rf(j,j).ae(gamma(2*j)/gamma(j)) assert ff(-2,0) == 1 assert ff(-2,1) == -2 assert ff(4,3) == 24 assert ff(3,4) == 0 assert binomial(0,0) == 1 assert binomial(1,0) == 1 assert binomial(0,-1) == 0 assert binomial(3,2) == 3 assert binomial(5,2) == 10 assert binomial(5,3) == 10 assert binomial(5,5) == 1 assert binomial(-1,0) == 1 assert binomial(-2,-4) == 3 assert binomial(4.5, 1.5) == 6.5625 assert binomial(1100,1) == 1100 assert binomial(1100,2) == 604450 assert beta(1,1) == 1 assert beta(0,0) == inf assert beta(3,0) == inf assert beta(-1,-1) == inf assert beta(1.5,1).ae(2/3.) assert beta(1.5,2.5).ae(pi/16) assert (10**15*beta(10,100)).ae(2.3455339739604649879) assert beta(inf,inf) == 0 assert isnan(beta(-inf,inf)) assert isnan(beta(-3,inf)) assert isnan(beta(0,inf)) assert beta(inf,0.5) == beta(0.5,inf) == 0 assert beta(inf,-1.5) == inf assert beta(inf,-0.5) == -inf assert beta(1+2j,-1-j/2).ae(1.16396542451069943086+0.08511695947832914640j) assert beta(-0.5,0.5) == 0 assert beta(-3,3).ae(-1/3.) def test_zeta(): mp.dps = 15 assert zeta(2).ae(pi**2 / 6) assert zeta(2.0).ae(pi**2 / 6) assert zeta(mpc(2)).ae(pi**2 / 6) assert zeta(100).ae(1) assert zeta(0).ae(-0.5) assert zeta(0.5).ae(-1.46035450880958681) assert zeta(-1).ae(-mpf(1)/12) assert zeta(-2) == 0 assert zeta(-3).ae(mpf(1)/120) assert zeta(-4) == 0 assert zeta(-100) == 0 assert isnan(zeta(nan)) # Zeros in the critical strip assert zeta(mpc(0.5, 14.1347251417346937904)).ae(0) assert zeta(mpc(0.5, 21.0220396387715549926)).ae(0) assert zeta(mpc(0.5, 25.0108575801456887632)).ae(0) mp.dps = 50 im = '236.5242296658162058024755079556629786895294952121891237' assert zeta(mpc(0.5, im)).ae(0, 1e-46) mp.dps = 15 # Complex reflection formula assert (zeta(-60+3j) / 10**34).ae(8.6270183987866146+15.337398548226238j) def test_altzeta(): mp.dps = 15 assert altzeta(-2) == 0 assert altzeta(-4) == 0 assert altzeta(-100) == 0 assert altzeta(0) == 0.5 assert altzeta(-1) == 0.25 assert altzeta(-3) == -0.125 assert altzeta(-5) == 0.25 assert altzeta(-21) == 1180529130.25 assert altzeta(1).ae(log(2)) assert altzeta(2).ae(pi**2/12) assert altzeta(10).ae(73*pi**10/6842880) assert altzeta(50) < 1 assert altzeta(60, rounding='d') < 1 assert altzeta(60, rounding='u') == 1 assert altzeta(10000, rounding='d') < 1 assert altzeta(10000, rounding='u') == 1 assert altzeta(3+0j) == altzeta(3) s = 3+4j assert altzeta(s).ae((1-2**(1-s))*zeta(s)) s = -3+4j assert altzeta(s).ae((1-2**(1-s))*zeta(s)) assert altzeta(-100.5).ae(4.58595480083585913e+108) assert altzeta(1.3).ae(0.73821404216623045) def test_zeta_huge(): mp.dps = 15 assert zeta(inf) == 1 mp.dps = 50 assert zeta(100).ae('1.0000000000000000000000000000007888609052210118073522') assert zeta(40*pi).ae('1.0000000000000000000000000000000000000148407238666182') mp.dps = 10000 v = zeta(33000) mp.dps = 15 assert str(v-1) == '1.02363019598118e-9934' assert zeta(pi*1000, rounding=round_up) > 1 assert zeta(3000, rounding=round_up) > 1 assert zeta(pi*1000) == 1 assert zeta(3000) == 1 def test_zeta_negative(): mp.dps = 150 a = -pi*10**40 mp.dps = 15 assert str(zeta(a)) == '2.55880492708712e+1233536161668617575553892558646631323374078' mp.dps = 50 assert str(zeta(a)) == '2.5588049270871154960875033337384432038436330847333e+1233536161668617575553892558646631323374078' mp.dps = 15 def test_polygamma(): mp.dps = 15 psi0 = lambda z: psi(0,z) psi1 = lambda z: psi(1,z) assert psi0(3) == psi(0,3) == digamma(3) # Not sure what this guy was doing, only place psi2, psi3, tetragamma used # assert psi2(3) == psi(2,3) == tetragamma(3) # assert psi3(3) == psi(3,3) == pentagamma(3) assert psi0(pi).ae(0.97721330794200673) assert psi0(-pi).ae(7.8859523853854902) assert psi0(-pi+1).ae(7.5676424992016996) assert psi0(pi+j).ae(1.04224048313859376 + 0.35853686544063749j) assert psi0(-pi-j).ae(1.3404026194821986 - 2.8824392476809402j) assert findroot(psi0, 1).ae(1.4616321449683622) assert psi0(inf) == inf assert psi1(inf) == 0 assert psi(2,inf) == 0 assert psi1(pi).ae(0.37424376965420049) assert psi1(-pi).ae(53.030438740085385) assert psi1(pi+j).ae(0.32935710377142464 - 0.12222163911221135j) assert psi1(-pi-j).ae(-0.30065008356019703 + 0.01149892486928227j) assert (10**6*psi(4,1+10*pi*j)).ae(-6.1491803479004446 - 0.3921316371664063j) assert psi0(1+10*pi*j).ae(3.4473994217222650 + 1.5548808324857071j) assert isnan(psi0(nan)) assert isnan(psi0(-inf)) assert psi0(-100.5).ae(4.615124601338064) assert psi0(3+0j).ae(psi0(3)) assert psi0(-100+3j).ae(4.6106071768714086321+3.1117510556817394626j) assert isnan(psi(2,mpc(0,inf))) assert isnan(psi(2,mpc(0,nan))) assert isnan(psi(2,mpc(0,-inf))) assert isnan(psi(2,mpc(1,inf))) assert isnan(psi(2,mpc(1,nan))) assert isnan(psi(2,mpc(1,-inf))) assert isnan(psi(2,mpc(inf,inf))) assert isnan(psi(2,mpc(nan,nan))) assert isnan(psi(2,mpc(-inf,-inf))) def test_polygamma_high_prec(): mp.dps = 100 assert str(psi(0,pi)) == "0.9772133079420067332920694864061823436408346099943256380095232865318105924777141317302075654362928734" assert str(psi(10,pi)) == "-12.98876181434889529310283769414222588307175962213707170773803550518307617769657562747174101900659238" def test_polygamma_identities(): mp.dps = 15 psi0 = lambda z: psi(0,z) psi1 = lambda z: psi(1,z) psi2 = lambda z: psi(2,z) assert psi0(0.5).ae(-euler-2*log(2)) assert psi0(1).ae(-euler) assert psi1(0.5).ae(0.5*pi**2) assert psi1(1).ae(pi**2/6) assert psi1(0.25).ae(pi**2 + 8*catalan) assert psi2(1).ae(-2*apery) mp.dps = 20 u = -182*apery+4*sqrt(3)*pi**3 mp.dps = 15 assert psi(2,5/6.).ae(u) assert psi(3,0.5).ae(pi**4) def test_foxtrot_identity(): # A test of the complex digamma function. # See http://mathworld.wolfram.com/FoxTrotSeries.html and # http://mathworld.wolfram.com/DigammaFunction.html psi0 = lambda z: psi(0,z) mp.dps = 50 a = (-1)**fraction(1,3) b = (-1)**fraction(2,3) x = -psi0(0.5*a) - psi0(-0.5*b) + psi0(0.5*(1+a)) + psi0(0.5*(1-b)) y = 2*pi*sech(0.5*sqrt(3)*pi) assert x.ae(y) mp.dps = 15 def test_polygamma_high_order(): mp.dps = 100 assert str(psi(50, pi)) == "-1344100348958402765749252447726432491812.641985273160531055707095989227897753035823152397679626136483" assert str(psi(50, pi + 14*e)) == "-0.00000000000000000189793739550804321623512073101895801993019919886375952881053090844591920308111549337295143780341396" assert str(psi(50, pi + 14*e*j)) == ("(-0.0000000000000000522516941152169248975225472155683565752375889510631513244785" "9377385233700094871256507814151956624433 - 0.00000000000000001813157041407010184" "702414110218205348527862196327980417757665282244728963891298080199341480881811613j)") mp.dps = 15 assert str(psi(50, pi)) == "-1.34410034895841e+39" assert str(psi(50, pi + 14*e)) == "-1.89793739550804e-18" assert str(psi(50, pi + 14*e*j)) == "(-5.2251694115217e-17 - 1.81315704140701e-17j)" def test_harmonic(): mp.dps = 15 assert harmonic(0) == 0 assert harmonic(1) == 1 assert harmonic(2) == 1.5 assert harmonic(3).ae(1. + 1./2 + 1./3) assert harmonic(10**10).ae(23.603066594891989701) assert harmonic(10**1000).ae(2303.162308658947) assert harmonic(0.5).ae(2-2*log(2)) assert harmonic(inf) == inf assert harmonic(2+0j) == 1.5+0j assert harmonic(1+2j).ae(1.4918071802755104+0.92080728264223022j) def test_gamma_huge_1(): mp.dps = 500 x = mpf(10**10) / 7 mp.dps = 15 assert str(gamma(x)) == "6.26075321389519e+12458010678" mp.dps = 50 assert str(gamma(x)) == "6.2607532138951929201303779291707455874010420783933e+12458010678" mp.dps = 15 def test_gamma_huge_2(): mp.dps = 500 x = mpf(10**100) / 19 mp.dps = 15 assert str(gamma(x)) == (\ "1.82341134776679e+5172997469323364168990133558175077136829182824042201886051511" "9656908623426021308685461258226190190661") mp.dps = 50 assert str(gamma(x)) == (\ "1.82341134776678875374414910350027596939980412984e+5172997469323364168990133558" "1750771368291828240422018860515119656908623426021308685461258226190190661") def test_gamma_huge_3(): mp.dps = 500 x = 10**80 // 3 + 10**70*j / 7 mp.dps = 15 y = gamma(x) assert str(y.real) == (\ "-6.82925203918106e+2636286142112569524501781477865238132302397236429627932441916" "056964386399485392600") assert str(y.imag) == (\ "8.54647143678418e+26362861421125695245017814778652381323023972364296279324419160" "56964386399485392600") mp.dps = 50 y = gamma(x) assert str(y.real) == (\ "-6.8292520391810548460682736226799637356016538421817e+26362861421125695245017814" "77865238132302397236429627932441916056964386399485392600") assert str(y.imag) == (\ "8.5464714367841748507479306948130687511711420234015e+263628614211256952450178147" "7865238132302397236429627932441916056964386399485392600") def test_gamma_huge_4(): x = 3200+11500j mp.dps = 15 assert str(gamma(x)) == \ "(8.95783268539713e+5164 - 1.94678798329735e+5164j)" mp.dps = 50 assert str(gamma(x)) == (\ "(8.9578326853971339570292952697675570822206567327092e+5164" " - 1.9467879832973509568895402139429643650329524144794e+51" "64j)") mp.dps = 15 def test_gamma_huge_5(): mp.dps = 500 x = 10**60 * j / 3 mp.dps = 15 y = gamma(x) assert str(y.real) == "-3.27753899634941e-227396058973640224580963937571892628368354580620654233316839" assert str(y.imag) == "-7.1519888950416e-227396058973640224580963937571892628368354580620654233316841" mp.dps = 50 y = gamma(x) assert str(y.real) == (\ "-3.2775389963494132168950056995974690946983219123935e-22739605897364022458096393" "7571892628368354580620654233316839") assert str(y.imag) == (\ "-7.1519888950415979749736749222530209713136588885897e-22739605897364022458096393" "7571892628368354580620654233316841") mp.dps = 15 def test_gamma_huge_6(): return mp.dps = 500 x = -10**10 + mpf(10)**(-175)*j mp.dps = 15 assert str(gamma(x)) == \ "(1.86729378905343e-95657055178 - 4.29960285282433e-95657055002j)" mp.dps = 50 assert str(gamma(x)) == (\ "(1.8672937890534298925763143275474177736153484820662e-9565705517" "8 - 4.2996028528243336966001185406200082244961757496106e-9565705" "5002j)") mp.dps = 15 def test_gamma_huge_7(): mp.dps = 100 a = 3 + j/mpf(10)**1000 mp.dps = 15 y = gamma(a) assert str(y.real) == "2.0" assert str(y.imag) == "1.84556867019693e-1000" mp.dps = 50 y = gamma(a) assert str(y.real) == "2.0" assert str(y.imag) == "1.8455686701969342787869758198351951379156813281202e-1000" @XFAIL def test_gamma_huge_7_failing(): assert str(y.imag) == "2.16735365342606e-1000" assert str(y.imag) == "2.1673536534260596065418805612488708028522563689298e-1000" def test_stieltjes(): mp.dps = 15 assert stieltjes(0).ae(+euler) mp.dps = 25 assert stieltjes(1).ae('-0.07281584548367672486058637587') assert stieltjes(2).ae('-0.009690363192872318484530386035') assert stieltjes(3).ae('0.002053834420303345866160046543') assert stieltjes(4).ae('0.002325370065467300057468170178') mp.dps = 15 assert stieltjes(1).ae(-0.07281584548367672486058637587) assert stieltjes(2).ae(-0.009690363192872318484530386035) assert stieltjes(3).ae(0.002053834420303345866160046543) assert stieltjes(4).ae(0.0023253700654673000574681701775) def test_barnesg(): mp.dps = 15 assert barnesg(0) == barnesg(-1) == 0 assert [superfac(i) for i in range(8)] == [1, 1, 2, 12, 288, 34560, 24883200, 125411328000] assert str(superfac(1000)) == '3.24570818422368e+1177245' assert isnan(barnesg(nan)) assert isnan(superfac(nan)) assert isnan(hyperfac(nan)) assert barnesg(inf) == inf assert superfac(inf) == inf assert hyperfac(inf) == inf assert isnan(superfac(-inf)) assert barnesg(0.7).ae(0.8068722730141471) assert barnesg(2+3j).ae(-0.17810213864082169+0.04504542715447838j) assert [hyperfac(n) for n in range(7)] == [1, 1, 4, 108, 27648, 86400000, 4031078400000] assert [hyperfac(n) for n in range(0,-7,-1)] == [1,1,-1,-4,108,27648,-86400000] a = barnesg(-3+0j) assert a == 0 and isinstance(a, mpc) a = hyperfac(-3+0j) assert a == -4 and isinstance(a, mpc) def test_polylog(): mp.dps = 15 zs = [mpmathify(z) for z in [0, 0.5, 0.99, 4, -0.5, -4, 1j, 3+4j]] for z in zs: assert polylog(1, z).ae(-log(1-z)) for z in zs: assert polylog(0, z).ae(z/(1-z)) for z in zs: assert polylog(-1, z).ae(z/(1-z)**2) for z in zs: assert polylog(-2, z).ae(z*(1+z)/(1-z)**3) for z in zs: assert polylog(-3, z).ae(z*(1+4*z+z**2)/(1-z)**4) assert polylog(3, 7).ae(5.3192579921456754382-5.9479244480803301023j) assert polylog(3, -7).ae(-4.5693548977219423182) assert polylog(2, 0.9).ae(1.2997147230049587252) assert polylog(2, -0.9).ae(-0.75216317921726162037) assert polylog(2, 0.9j).ae(-0.17177943786580149299+0.83598828572550503226j) assert polylog(2, 1.1).ae(1.9619991013055685931-0.2994257606855892575j) assert polylog(2, -1.1).ae(-0.89083809026228260587) assert polylog(2, 1.1*sqrt(j)).ae(0.58841571107611387722+1.09962542118827026011j) assert polylog(-2, 0.9).ae(1710) assert polylog(-2, -0.9).ae(-90/6859.) assert polylog(3, 0.9).ae(1.0496589501864398696) assert polylog(-3, 0.9).ae(48690) assert polylog(-3, -4).ae(-0.0064) assert polylog(0.5+j/3, 0.5+j/2).ae(0.31739144796565650535 + 0.99255390416556261437j) assert polylog(3+4j,1).ae(zeta(3+4j)) assert polylog(3+4j,-1).ae(-altzeta(3+4j)) def test_bell_polyexp(): mp.dps = 15 # TODO: more tests for polyexp assert (polyexp(0,1e-10)*10**10).ae(1.00000000005) assert (polyexp(1,1e-10)*10**10).ae(1.0000000001) assert polyexp(5,3j).ae(-607.7044517476176454+519.962786482001476087j) assert polyexp(-1,3.5).ae(12.09537536175543444) # bell(0,x) = 1 assert bell(0,0) == 1 assert bell(0,1) == 1 assert bell(0,2) == 1 assert bell(0,inf) == 1 assert bell(0,-inf) == 1 assert isnan(bell(0,nan)) # bell(1,x) = x assert bell(1,4) == 4 assert bell(1,0) == 0 assert bell(1,inf) == inf assert bell(1,-inf) == -inf assert isnan(bell(1,nan)) # bell(2,x) = x*(1+x) assert bell(2,-1) == 0 assert bell(2,0) == 0 # large orders / arguments assert bell(10) == 115975 assert bell(10,1) == 115975 assert bell(10, -8) == 11054008 assert bell(5,-50) == -253087550 assert bell(50,-50).ae('3.4746902914629720259e74') mp.dps = 80 assert bell(50,-50) == 347469029146297202586097646631767227177164818163463279814268368579055777450 assert bell(40,50) == 5575520134721105844739265207408344706846955281965031698187656176321717550 assert bell(74) == 5006908024247925379707076470957722220463116781409659160159536981161298714301202 mp.dps = 15 assert bell(10,20j) == 7504528595600+15649605360020j # continuity of the generalization assert bell(0.5,0).ae(sinc(pi*0.5)) def test_primezeta(): mp.dps = 15 assert primezeta(0.9).ae(1.8388316154446882243 + 3.1415926535897932385j) assert primezeta(4).ae(0.076993139764246844943) assert primezeta(1) == inf assert primezeta(inf) == 0 assert isnan(primezeta(nan)) def test_rs_zeta(): mp.dps = 15 assert zeta(0.5+100000j).ae(1.0730320148577531321 + 5.7808485443635039843j) assert zeta(0.75+100000j).ae(1.837852337251873704 + 1.9988492668661145358j) assert zeta(0.5+1000000j, derivative=3).ae(1647.7744105852674733 - 1423.1270943036622097j) assert zeta(1+1000000j, derivative=3).ae(3.4085866124523582894 - 18.179184721525947301j) assert zeta(1+1000000j, derivative=1).ae(-0.10423479366985452134 - 0.74728992803359056244j) assert zeta(0.5-1000000j, derivative=1).ae(11.636804066002521459 + 17.127254072212996004j) # Additional sanity tests using fp arithmetic. # Some more high-precision tests are found in the docstrings def ae(x, y, tol=1e-6): return abs(x-y) < tol*abs(y) assert ae(fp.zeta(0.5-100000j), 1.0730320148577531321 - 5.7808485443635039843j) assert ae(fp.zeta(0.75-100000j), 1.837852337251873704 - 1.9988492668661145358j) assert ae(fp.zeta(0.5+1e6j), 0.076089069738227100006 + 2.8051021010192989554j) assert ae(fp.zeta(0.5+1e6j, derivative=1), 11.636804066002521459 - 17.127254072212996004j) assert ae(fp.zeta(1+1e6j), 0.94738726251047891048 + 0.59421999312091832833j) assert ae(fp.zeta(1+1e6j, derivative=1), -0.10423479366985452134 - 0.74728992803359056244j) assert ae(fp.zeta(0.5+100000j, derivative=1), 10.766962036817482375 - 30.92705282105996714j) assert ae(fp.zeta(0.5+100000j, derivative=2), -119.40515625740538429 + 217.14780631141830251j) assert ae(fp.zeta(0.5+100000j, derivative=3), 1129.7550282628460881 - 1685.4736895169690346j) assert ae(fp.zeta(0.5+100000j, derivative=4), -10407.160819314958615 + 13777.786698628045085j) assert ae(fp.zeta(0.75+100000j, derivative=1), -0.41742276699594321475 - 6.4453816275049955949j) assert ae(fp.zeta(0.75+100000j, derivative=2), -9.214314279161977266 + 35.07290795337967899j) assert ae(fp.zeta(0.75+100000j, derivative=3), 110.61331857820103469 - 236.87847130518129926j) assert ae(fp.zeta(0.75+100000j, derivative=4), -1054.334275898559401 + 1769.9177890161596383j) def test_siegelz(): mp.dps = 15 assert siegelz(100000).ae(5.87959246868176504171) assert siegelz(100000, derivative=2).ae(-54.1172711010126452832) assert siegelz(100000, derivative=3).ae(-278.930831343966552538) assert siegelz(100000+j,derivative=1).ae(678.214511857070283307-379.742160779916375413j) def test_zeta_near_1(): # Test for a former bug in mpf_zeta and mpc_zeta mp.dps = 15 s1 = fadd(1, '1e-10', exact=True) s2 = fadd(1, '-1e-10', exact=True) s3 = fadd(1, '1e-10j', exact=True) assert zeta(s1).ae(1.000000000057721566490881444e10) assert zeta(s2).ae(-9.99999999942278433510574872e9) z = zeta(s3) assert z.real.ae(0.57721566490153286060) assert z.imag.ae(-9.9999999999999999999927184e9) mp.dps = 30 s1 = fadd(1, '1e-50', exact=True) s2 = fadd(1, '-1e-50', exact=True) s3 = fadd(1, '1e-50j', exact=True) assert zeta(s1).ae('1e50') assert zeta(s2).ae('-1e50') z = zeta(s3) assert z.real.ae('0.57721566490153286060651209008240243104215933593992') assert z.imag.ae('-1e50') sympy-0.7.4.1/sympy/mpmath/tests/test_functions2.py0000644000175000017500000022366012253362407022571 0ustar georgeskgeorgeskimport math from sympy.mpmath import * from sympy.utilities.pytest import XFAIL def test_bessel(): mp.dps = 15 assert j0(1).ae(0.765197686557966551) assert j0(pi).ae(-0.304242177644093864) assert j0(1000).ae(0.0247866861524201746) assert j0(-25).ae(0.0962667832759581162) assert j1(1).ae(0.440050585744933516) assert j1(pi).ae(0.284615343179752757) assert j1(1000).ae(0.00472831190708952392) assert j1(-25).ae(0.125350249580289905) assert besselj(5,1).ae(0.000249757730211234431) assert besselj(5+0j,1).ae(0.000249757730211234431) assert besselj(5,pi).ae(0.0521411843671184747) assert besselj(5,1000).ae(0.00502540694523318607) assert besselj(5,-25).ae(0.0660079953984229934) assert besselj(-3,2).ae(-0.128943249474402051) assert besselj(-4,2).ae(0.0339957198075684341) assert besselj(3,3+2j).ae(0.424718794929639595942 + 0.625665327745785804812j) assert besselj(0.25,4).ae(-0.374760630804249715) assert besselj(1+2j,3+4j).ae(0.319247428741872131 - 0.669557748880365678j) assert (besselj(3, 10**10) * 10**5).ae(0.76765081748139204023) assert bessely(-0.5, 0) == 0 assert bessely(0.5, 0) == -inf assert bessely(1.5, 0) == -inf assert bessely(0,0) == -inf assert bessely(-0.4, 0) == -inf assert bessely(-0.6, 0) == inf assert bessely(-1, 0) == inf assert bessely(-1.4, 0) == inf assert bessely(-1.6, 0) == -inf assert bessely(-1, 0) == inf assert bessely(-2, 0) == -inf assert bessely(-3, 0) == inf assert bessely(0.5, 0) == -inf assert bessely(1, 0) == -inf assert bessely(1.5, 0) == -inf assert bessely(2, 0) == -inf assert bessely(2.5, 0) == -inf assert bessely(3, 0) == -inf assert bessely(0,0.5).ae(-0.44451873350670655715) assert bessely(1,0.5).ae(-1.4714723926702430692) assert bessely(-1,0.5).ae(1.4714723926702430692) assert bessely(3.5,0.5).ae(-138.86400867242488443) assert bessely(0,3+4j).ae(4.6047596915010138655-8.8110771408232264208j) assert bessely(0,j).ae(-0.26803248203398854876+1.26606587775200833560j) assert (bessely(3, 10**10) * 10**5).ae(0.21755917537013204058) assert besseli(0,0) == 1 assert besseli(1,0) == 0 assert besseli(2,0) == 0 assert besseli(-1,0) == 0 assert besseli(-2,0) == 0 assert besseli(0,0.5).ae(1.0634833707413235193) assert besseli(1,0.5).ae(0.25789430539089631636) assert besseli(-1,0.5).ae(0.25789430539089631636) assert besseli(3.5,0.5).ae(0.00068103597085793815863) assert besseli(0,3+4j).ae(-3.3924877882755196097-1.3239458916287264815j) assert besseli(0,j).ae(besselj(0,1)) assert (besseli(3, 10**10) * mpf(10)**(-4342944813)).ae(4.2996028505491271875) assert besselk(0,0) == inf assert besselk(1,0) == inf assert besselk(2,0) == inf assert besselk(-1,0) == inf assert besselk(-2,0) == inf assert besselk(0,0.5).ae(0.92441907122766586178) assert besselk(1,0.5).ae(1.6564411200033008937) assert besselk(-1,0.5).ae(1.6564411200033008937) assert besselk(3.5,0.5).ae(207.48418747548460607) assert besselk(0,3+4j).ae(-0.007239051213570155013+0.026510418350267677215j) assert besselk(0,j).ae(-0.13863371520405399968-1.20196971531720649914j) assert (besselk(3, 10**10) * mpf(10)**4342944824).ae(1.1628981033356187851) def test_bessel_zeros(): mp.dps = 15 assert besseljzero(0,1).ae(2.40482555769577276869) assert besseljzero(2,1).ae(5.1356223018406825563) assert besseljzero(1,50).ae(157.86265540193029781) assert besseljzero(10,1).ae(14.475500686554541220) assert besseljzero(0.5,3).ae(9.4247779607693797153) assert besseljzero(2,1,1).ae(3.0542369282271403228) assert besselyzero(0,1).ae(0.89357696627916752158) assert besselyzero(2,1).ae(3.3842417671495934727) assert besselyzero(1,50).ae(156.29183520147840108) assert besselyzero(10,1).ae(12.128927704415439387) assert besselyzero(0.5,3).ae(7.8539816339744830962) assert besselyzero(2,1,1).ae(5.0025829314460639452) def test_hankel(): mp.dps = 15 assert hankel1(0,0.5).ae(0.93846980724081290423-0.44451873350670655715j) assert hankel1(1,0.5).ae(0.2422684576748738864-1.4714723926702430692j) assert hankel1(-1,0.5).ae(-0.2422684576748738864+1.4714723926702430692j) assert hankel1(1.5,0.5).ae(0.0917016996256513026-2.5214655504213378514j) assert hankel1(1.5,3+4j).ae(0.0066806866476728165382-0.0036684231610839127106j) assert hankel2(0,0.5).ae(0.93846980724081290423+0.44451873350670655715j) assert hankel2(1,0.5).ae(0.2422684576748738864+1.4714723926702430692j) assert hankel2(-1,0.5).ae(-0.2422684576748738864-1.4714723926702430692j) assert hankel2(1.5,0.5).ae(0.0917016996256513026+2.5214655504213378514j) assert hankel2(1.5,3+4j).ae(14.783528526098567526-7.397390270853446512j) def test_struve(): mp.dps = 15 assert struveh(2,3).ae(0.74238666967748318564) assert struveh(-2.5,3).ae(0.41271003220971599344) assert struvel(2,3).ae(1.7476573277362782744) assert struvel(-2.5,3).ae(1.5153394466819651377) def test_whittaker(): mp.dps = 15 assert whitm(2,3,4).ae(49.753745589025246591) assert whitw(2,3,4).ae(14.111656223052932215) def test_kelvin(): mp.dps = 15 assert ber(2,3).ae(0.80836846563726819091) assert ber(3,4).ae(-0.28262680167242600233) assert ber(-3,2).ae(-0.085611448496796363669) assert bei(2,3).ae(-0.89102236377977331571) assert bei(-3,2).ae(-0.14420994155731828415) assert ker(2,3).ae(0.12839126695733458928) assert ker(-3,2).ae(-0.29802153400559142783) assert ker(0.5,3).ae(-0.085662378535217097524) assert kei(2,3).ae(0.036804426134164634000) assert kei(-3,2).ae(0.88682069845786731114) assert kei(0.5,3).ae(0.013633041571314302948) def test_hyper_misc(): mp.dps = 15 assert hyp0f1(1,0) == 1 assert hyp1f1(1,2,0) == 1 assert hyp1f2(1,2,3,0) == 1 assert hyp2f1(1,2,3,0) == 1 assert hyp2f2(1,2,3,4,0) == 1 assert hyp2f3(1,2,3,4,5,0) == 1 # Degenerate case: 0F0 assert hyper([],[],0) == 1 assert hyper([],[],-2).ae(exp(-2)) # Degenerate case: 1F0 assert hyper([2],[],1.5) == 4 # assert hyp2f1((1,3),(2,3),(5,6),mpf(27)/32).ae(1.6) assert hyp2f1((1,4),(1,2),(3,4),mpf(80)/81).ae(1.8) assert hyp2f1((2,3),(1,1),(3,2),(2+j)/3).ae(1.327531603558679093+0.439585080092769253j) mp.dps = 25 v = mpc('1.2282306665029814734863026', '-0.1225033830118305184672133') assert hyper([(3,4),2+j,1],[1,5,j/3],mpf(1)/5+j/8).ae(v) mp.dps = 15 def test_elliptic_integrals(): mp.dps = 15 assert ellipk(0).ae(pi/2) assert ellipk(0.5).ae(gamma(0.25)**2/(4*sqrt(pi))) assert ellipk(1) == inf assert ellipk(1+0j) == inf assert ellipk(-1).ae('1.3110287771460599052') assert ellipk(-2).ae('1.1714200841467698589') assert isinstance(ellipk(-2), mpf) assert isinstance(ellipe(-2), mpf) assert ellipk(-50).ae('0.47103424540873331679') mp.dps = 30 n1 = +fraction(99999,100000) n2 = +fraction(100001,100000) mp.dps = 15 assert ellipk(n1).ae('7.1427724505817781901') assert ellipk(n2).ae(mpc('7.1427417367963090109', '-1.5707923998261688019')) assert ellipe(n1).ae('1.0000332138990829170') v = ellipe(n2) assert v.real.ae('0.999966786328145474069137') assert (v.imag*10**6).ae('7.853952181727432') assert ellipk(2).ae(mpc('1.3110287771460599052', '-1.3110287771460599052')) assert ellipk(50).ae(mpc('0.22326753950210985451', '-0.47434723226254522087')) assert ellipk(3+4j).ae(mpc('0.91119556380496500866', '0.63133428324134524388')) assert ellipk(3-4j).ae(mpc('0.91119556380496500866', '-0.63133428324134524388')) assert ellipk(-3+4j).ae(mpc('0.95357894880405122483', '0.23093044503746114444')) assert ellipk(-3-4j).ae(mpc('0.95357894880405122483', '-0.23093044503746114444')) assert isnan(ellipk(nan)) assert isnan(ellipe(nan)) assert ellipk(inf) == 0 assert isinstance(ellipk(inf), mpc) assert ellipk(-inf) == 0 assert ellipk(1+0j) == inf assert ellipe(0).ae(pi/2) assert ellipe(0.5).ae(pi**(mpf(3)/2)/gamma(0.25)**2 +gamma(0.25)**2/(8*sqrt(pi))) assert ellipe(1) == 1 assert ellipe(1+0j) == 1 assert ellipe(inf) == mpc(0,inf) assert ellipe(-inf) == inf assert ellipe(3+4j).ae(1.4995535209333469543-1.5778790079127582745j) assert ellipe(3-4j).ae(1.4995535209333469543+1.5778790079127582745j) assert ellipe(-3+4j).ae(2.5804237855343377803-0.8306096791000413778j) assert ellipe(-3-4j).ae(2.5804237855343377803+0.8306096791000413778j) assert ellipe(2).ae(0.59907011736779610372+0.59907011736779610372j) assert ellipe('1e-1000000000').ae(pi/2) assert ellipk('1e-1000000000').ae(pi/2) assert ellipe(-pi).ae(2.4535865983838923) mp.dps = 50 assert ellipk(1/pi).ae('1.724756270009501831744438120951614673874904182624739673') assert ellipe(1/pi).ae('1.437129808135123030101542922290970050337425479058225712') assert ellipk(-10*pi).ae('0.5519067523886233967683646782286965823151896970015484512') assert ellipe(-10*pi).ae('5.926192483740483797854383268707108012328213431657645509') v = ellipk(pi) assert v.real.ae('0.973089521698042334840454592642137667227167622330325225') assert v.imag.ae('-1.156151296372835303836814390793087600271609993858798016') v = ellipe(pi) assert v.real.ae('0.4632848917264710404078033487934663562998345622611263332') assert v.imag.ae('1.0637961621753130852473300451583414489944099504180510966') mp.dps = 15 def test_exp_integrals(): mp.dps = 15 x = +e z = e + sqrt(3)*j assert ei(x).ae(8.21168165538361560) assert li(x).ae(1.89511781635593676) assert si(x).ae(1.82104026914756705) assert ci(x).ae(0.213958001340379779) assert shi(x).ae(4.11520706247846193) assert chi(x).ae(4.09647459290515367) assert fresnels(x).ae(0.437189718149787643) assert fresnelc(x).ae(0.401777759590243012) assert airyai(x).ae(0.0108502401568586681) assert airybi(x).ae(8.98245748585468627) assert ei(z).ae(3.72597969491314951 + 7.34213212314224421j) assert li(z).ae(2.28662658112562502 + 1.50427225297269364j) assert si(z).ae(2.48122029237669054 + 0.12684703275254834j) assert ci(z).ae(0.169255590269456633 - 0.892020751420780353j) assert shi(z).ae(1.85810366559344468 + 3.66435842914920263j) assert chi(z).ae(1.86787602931970484 + 3.67777369399304159j) assert fresnels(z/3).ae(0.034534397197008182 + 0.754859844188218737j) assert fresnelc(z/3).ae(1.261581645990027372 + 0.417949198775061893j) assert airyai(z).ae(-0.0162552579839056062 - 0.0018045715700210556j) assert airybi(z).ae(-4.98856113282883371 + 2.08558537872180623j) assert li(0) == 0.0 assert li(1) == -inf assert li(inf) == inf assert isinstance(li(0.7), mpf) assert si(inf).ae(pi/2) assert si(-inf).ae(-pi/2) assert ci(inf) == 0 assert ci(0) == -inf assert isinstance(ei(-0.7), mpf) assert airyai(inf) == 0 assert airybi(inf) == inf assert airyai(-inf) == 0 assert airybi(-inf) == 0 assert fresnels(inf) == 0.5 assert fresnelc(inf) == 0.5 assert fresnels(-inf) == -0.5 assert fresnelc(-inf) == -0.5 assert shi(0) == 0 assert shi(inf) == inf assert shi(-inf) == -inf assert chi(0) == -inf assert chi(inf) == inf def test_ei(): mp.dps = 15 assert ei(0) == -inf assert ei(inf) == inf assert ei(-inf) == -0.0 assert ei(20+70j).ae(6.1041351911152984397e6 - 2.7324109310519928872e6j) # tests for the asymptotic expansion # values checked with Mathematica ExpIntegralEi mp.dps = 50 r = ei(20000) s = '3.8781962825045010930273870085501819470698476975019e+8681' assert str(r) == s r = ei(-200) s = '-6.8852261063076355977108174824557929738368086933303e-90' assert str(r) == s r =ei(20000 + 10*j) sre = '-3.255138234032069402493850638874410725961401274106e+8681' sim = '-2.1081929993474403520785942429469187647767369645423e+8681' assert str(r.real) == sre and str(r.imag) == sim mp.dps = 15 # More asymptotic expansions assert chi(-10**6+100j).ae('1.3077239389562548386e+434288 + 7.6808956999707408158e+434287j') assert shi(-10**6+100j).ae('-1.3077239389562548386e+434288 - 7.6808956999707408158e+434287j') mp.dps = 15 assert ei(10j).ae(-0.0454564330044553726+3.2291439210137706686j) assert ei(100j).ae(-0.0051488251426104921+3.1330217936839529126j) u = ei(fmul(10**20, j, exact=True)) assert u.real.ae(-6.4525128526578084421345e-21, abs_eps=0, rel_eps=8*eps) assert u.imag.ae(pi) assert ei(-10j).ae(-0.0454564330044553726-3.2291439210137706686j) assert ei(-100j).ae(-0.0051488251426104921-3.1330217936839529126j) u = ei(fmul(-10**20, j, exact=True)) assert u.real.ae(-6.4525128526578084421345e-21, abs_eps=0, rel_eps=8*eps) assert u.imag.ae(-pi) assert ei(10+10j).ae(-1576.1504265768517448+436.9192317011328140j) u = ei(-10+10j) assert u.real.ae(7.6698978415553488362543e-7, abs_eps=0, rel_eps=8*eps) assert u.imag.ae(3.141595611735621062025) def test_e1(): mp.dps = 15 assert e1(0) == inf assert e1(inf) == 0 assert e1(-inf) == mpc(-inf, -pi) assert e1(10j).ae(0.045456433004455372635 + 0.087551267423977430100j) assert e1(100j).ae(0.0051488251426104921444 - 0.0085708599058403258790j) assert e1(fmul(10**20, j, exact=True)).ae(6.4525128526578084421e-21 - 7.6397040444172830039e-21j, abs_eps=0, rel_eps=8*eps) assert e1(-10j).ae(0.045456433004455372635 - 0.087551267423977430100j) assert e1(-100j).ae(0.0051488251426104921444 + 0.0085708599058403258790j) assert e1(fmul(-10**20, j, exact=True)).ae(6.4525128526578084421e-21 + 7.6397040444172830039e-21j, abs_eps=0, rel_eps=8*eps) def test_expint(): mp.dps = 15 assert expint(0,0) == inf assert expint(0,1).ae(1/e) assert expint(0,1.5).ae(2/exp(1.5)/3) assert expint(1,1).ae(-ei(-1)) assert expint(2,0).ae(1) assert expint(3,0).ae(1/2.) assert expint(4,0).ae(1/3.) assert expint(-2, 0.5).ae(26/sqrt(e)) assert expint(-1,-1) == 0 assert expint(-2,-1).ae(-e) assert expint(5.5, 0).ae(2/9.) assert expint(2.00000001,0).ae(100000000./100000001) assert expint(2+3j,4-j).ae(0.0023461179581675065414+0.0020395540604713669262j) assert expint('1.01', '1e-1000').ae(99.9999999899412802) assert expint('1.000000000001', 3.5).ae(0.00697013985754701819446) assert expint(2,3).ae(3*ei(-3)+exp(-3)) assert (expint(10,20)*10**10).ae(0.694439055541231353) assert expint(3,inf) == 0 assert expint(3.2,inf) == 0 assert expint(3.2+2j,inf) == 0 assert expint(1,3j).ae(-0.11962978600800032763 + 0.27785620120457163717j) assert expint(1,3).ae(0.013048381094197037413) assert expint(1,-3).ae(-ei(3)-pi*j) # Not sure what the writer of this test was trying to do not xfailing or # putting in its own test because it's probably just written incorrectly # assert expint(3) == expint(1,3) assert expint(1,-20).ae(-25615652.66405658882 - 3.1415926535897932385j) assert expint(1000000,0).ae(1./999999) assert expint(0,2+3j).ae(-0.025019798357114678171 + 0.027980439405104419040j) assert expint(-1,2+3j).ae(-0.022411973626262070419 + 0.038058922011377716932j) assert expint(-1.5,0) == inf def test_trig_integrals(): mp.dps = 30 assert si(mpf(1)/1000000).ae('0.000000999999999999944444444444446111') assert ci(mpf(1)/1000000).ae('-13.2382948930629912435014366276') assert si(10**10).ae('1.5707963267075846569685111517747537') assert ci(10**10).ae('-4.87506025174822653785729773959e-11') assert si(10**100).ae(pi/2) assert (ci(10**100)*10**100).ae('-0.372376123661276688262086695553') assert si(-3) == -si(3) assert ci(-3).ae(ci(3) + pi*j) # Test complex structure mp.dps = 15 assert mp.ci(50).ae(-0.0056283863241163054402) assert mp.ci(50+2j).ae(-0.018378282946133067149+0.070352808023688336193j) assert mp.ci(20j).ae(1.28078263320282943611e7+1.5707963267949j) assert mp.ci(-2+20j).ae(-4.050116856873293505e6+1.207476188206989909e7j) assert mp.ci(-50+2j).ae(-0.0183782829461330671+3.0712398455661049023j) assert mp.ci(-50).ae(-0.0056283863241163054+3.1415926535897932385j) assert mp.ci(-50-2j).ae(-0.0183782829461330671-3.0712398455661049023j) assert mp.ci(-2-20j).ae(-4.050116856873293505e6-1.207476188206989909e7j) assert mp.ci(-20j).ae(1.28078263320282943611e7-1.5707963267949j) assert mp.ci(50-2j).ae(-0.018378282946133067149-0.070352808023688336193j) assert mp.si(50).ae(1.5516170724859358947) assert mp.si(50+2j).ae(1.497884414277228461-0.017515007378437448j) assert mp.si(20j).ae(1.2807826332028294459e7j) assert mp.si(-2+20j).ae(-1.20747603112735722103e7-4.050116856873293554e6j) assert mp.si(-50+2j).ae(-1.497884414277228461-0.017515007378437448j) assert mp.si(-50).ae(-1.5516170724859358947) assert mp.si(-50-2j).ae(-1.497884414277228461+0.017515007378437448j) assert mp.si(-2-20j).ae(-1.20747603112735722103e7+4.050116856873293554e6j) assert mp.si(-20j).ae(-1.2807826332028294459e7j) assert mp.si(50-2j).ae(1.497884414277228461+0.017515007378437448j) assert mp.chi(50j).ae(-0.0056283863241163054+1.5707963267948966192j) assert mp.chi(-2+50j).ae(-0.0183782829461330671+1.6411491348185849554j) assert mp.chi(-20).ae(1.28078263320282943611e7+3.1415926535898j) assert mp.chi(-20-2j).ae(-4.050116856873293505e6+1.20747571696809187053e7j) assert mp.chi(-2-50j).ae(-0.0183782829461330671-1.6411491348185849554j) assert mp.chi(-50j).ae(-0.0056283863241163054-1.5707963267948966192j) assert mp.chi(2-50j).ae(-0.0183782829461330671-1.500443518771208283j) assert mp.chi(20-2j).ae(-4.050116856873293505e6-1.20747603112735722951e7j) assert mp.chi(20).ae(1.2807826332028294361e7) assert mp.chi(2+50j).ae(-0.0183782829461330671+1.500443518771208283j) assert mp.shi(50j).ae(1.5516170724859358947j) assert mp.shi(-2+50j).ae(0.017515007378437448+1.497884414277228461j) assert mp.shi(-20).ae(-1.2807826332028294459e7) assert mp.shi(-20-2j).ae(4.050116856873293554e6-1.20747603112735722103e7j) assert mp.shi(-2-50j).ae(0.017515007378437448-1.497884414277228461j) assert mp.shi(-50j).ae(-1.5516170724859358947j) assert mp.shi(2-50j).ae(-0.017515007378437448-1.497884414277228461j) assert mp.shi(20-2j).ae(-4.050116856873293554e6-1.20747603112735722103e7j) assert mp.shi(20).ae(1.2807826332028294459e7) assert mp.shi(2+50j).ae(-0.017515007378437448+1.497884414277228461j) def ae(x,y,tol=1e-12): return abs(x-y) <= abs(y)*tol assert fp.ci(fp.inf) == 0 assert ae(fp.ci(fp.ninf), fp.pi*1j) assert ae(fp.si(fp.inf), fp.pi/2) assert ae(fp.si(fp.ninf), -fp.pi/2) assert fp.si(0) == 0 assert ae(fp.ci(50), -0.0056283863241163054402) assert ae(fp.ci(50+2j), -0.018378282946133067149+0.070352808023688336193j) assert ae(fp.ci(20j), 1.28078263320282943611e7+1.5707963267949j) assert ae(fp.ci(-2+20j), -4.050116856873293505e6+1.207476188206989909e7j) assert ae(fp.ci(-50+2j), -0.0183782829461330671+3.0712398455661049023j) assert ae(fp.ci(-50), -0.0056283863241163054+3.1415926535897932385j) assert ae(fp.ci(-50-2j), -0.0183782829461330671-3.0712398455661049023j) assert ae(fp.ci(-2-20j), -4.050116856873293505e6-1.207476188206989909e7j) assert ae(fp.ci(-20j), 1.28078263320282943611e7-1.5707963267949j) assert ae(fp.ci(50-2j), -0.018378282946133067149-0.070352808023688336193j) assert ae(fp.si(50), 1.5516170724859358947) assert ae(fp.si(50+2j), 1.497884414277228461-0.017515007378437448j) assert ae(fp.si(20j), 1.2807826332028294459e7j) assert ae(fp.si(-2+20j), -1.20747603112735722103e7-4.050116856873293554e6j) assert ae(fp.si(-50+2j), -1.497884414277228461-0.017515007378437448j) assert ae(fp.si(-50), -1.5516170724859358947) assert ae(fp.si(-50-2j), -1.497884414277228461+0.017515007378437448j) assert ae(fp.si(-2-20j), -1.20747603112735722103e7+4.050116856873293554e6j) assert ae(fp.si(-20j), -1.2807826332028294459e7j) assert ae(fp.si(50-2j), 1.497884414277228461+0.017515007378437448j) assert ae(fp.chi(50j), -0.0056283863241163054+1.5707963267948966192j) assert ae(fp.chi(-2+50j), -0.0183782829461330671+1.6411491348185849554j) assert ae(fp.chi(-20), 1.28078263320282943611e7+3.1415926535898j) assert ae(fp.chi(-20-2j), -4.050116856873293505e6+1.20747571696809187053e7j) assert ae(fp.chi(-2-50j), -0.0183782829461330671-1.6411491348185849554j) assert ae(fp.chi(-50j), -0.0056283863241163054-1.5707963267948966192j) assert ae(fp.chi(2-50j), -0.0183782829461330671-1.500443518771208283j) assert ae(fp.chi(20-2j), -4.050116856873293505e6-1.20747603112735722951e7j) assert ae(fp.chi(20), 1.2807826332028294361e7) assert ae(fp.chi(2+50j), -0.0183782829461330671+1.500443518771208283j) assert ae(fp.shi(50j), 1.5516170724859358947j) assert ae(fp.shi(-2+50j), 0.017515007378437448+1.497884414277228461j) assert ae(fp.shi(-20), -1.2807826332028294459e7) assert ae(fp.shi(-20-2j), 4.050116856873293554e6-1.20747603112735722103e7j) assert ae(fp.shi(-2-50j), 0.017515007378437448-1.497884414277228461j) assert ae(fp.shi(-50j), -1.5516170724859358947j) assert ae(fp.shi(2-50j), -0.017515007378437448-1.497884414277228461j) assert ae(fp.shi(20-2j), -4.050116856873293554e6-1.20747603112735722103e7j) assert ae(fp.shi(20), 1.2807826332028294459e7) assert ae(fp.shi(2+50j), -0.017515007378437448+1.497884414277228461j) def test_airy(): mp.dps = 15 assert (airyai(10)*10**10).ae(1.1047532552898687) assert (airybi(10)/10**9).ae(0.45564115354822515) assert (airyai(1000)*10**9158).ae(9.306933063179556004) assert (airybi(1000)/10**9154).ae(5.4077118391949465477) assert airyai(-1000).ae(0.055971895773019918842) assert airybi(-1000).ae(-0.083264574117080633012) assert (airyai(100+100j)*10**188).ae(2.9099582462207032076 + 2.353013591706178756j) assert (airybi(100+100j)/10**185).ae(1.7086751714463652039 - 3.1416590020830804578j) def test_hyper_0f1(): mp.dps = 15 v = 8.63911136507950465 assert hyper([],[(1,3)],1.5).ae(v) assert hyper([],[1/3.],1.5).ae(v) assert hyp0f1(1/3.,1.5).ae(v) assert hyp0f1((1,3),1.5).ae(v) # Asymptotic expansion assert hyp0f1(3,1e9).ae('4.9679055380347771271e+27455') assert hyp0f1(3,1e9j).ae('-2.1222788784457702157e+19410 + 5.0840597555401854116e+19410j') def test_hyper_1f1(): mp.dps = 15 v = 1.2917526488617656673 assert hyper([(1,2)],[(3,2)],0.7).ae(v) assert hyper([(1,2)],[(3,2)],0.7+0j).ae(v) assert hyper([0.5],[(3,2)],0.7).ae(v) assert hyper([0.5],[1.5],0.7).ae(v) assert hyper([0.5],[(3,2)],0.7+0j).ae(v) assert hyper([0.5],[1.5],0.7+0j).ae(v) assert hyper([(1,2)],[1.5+0j],0.7).ae(v) assert hyper([0.5+0j],[1.5],0.7).ae(v) assert hyper([0.5+0j],[1.5+0j],0.7+0j).ae(v) assert hyp1f1(0.5,1.5,0.7).ae(v) assert hyp1f1((1,2),1.5,0.7).ae(v) # Asymptotic expansion assert hyp1f1(2,3,1e10).ae('2.1555012157015796988e+4342944809') assert (hyp1f1(2,3,1e10j)*10**10).ae(-0.97501205020039745852 - 1.7462392454512132074j) # Shouldn't use asymptotic expansion assert hyp1f1(-2, 1, 10000).ae(49980001) # Bug assert hyp1f1(1j,fraction(1,3),0.415-69.739j).ae(25.857588206024346592 + 15.738060264515292063j) def test_hyper_2f1(): mp.dps = 15 v = 1.0652207633823291032 assert hyper([(1,2), (3,4)], [2], 0.3).ae(v) assert hyper([(1,2), 0.75], [2], 0.3).ae(v) assert hyper([0.5, 0.75], [2.0], 0.3).ae(v) assert hyper([0.5, 0.75], [2.0], 0.3+0j).ae(v) assert hyper([0.5+0j, (3,4)], [2.0], 0.3+0j).ae(v) assert hyper([0.5+0j, (3,4)], [2.0], 0.3).ae(v) assert hyper([0.5, (3,4)], [2.0+0j], 0.3).ae(v) assert hyper([0.5+0j, 0.75+0j], [2.0+0j], 0.3+0j).ae(v) v = 1.09234681096223231717 + 0.18104859169479360380j assert hyper([(1,2),0.75+j], [2], 0.5).ae(v) assert hyper([0.5,0.75+j], [2.0], 0.5).ae(v) assert hyper([0.5,0.75+j], [2.0], 0.5+0j).ae(v) assert hyper([0.5,0.75+j], [2.0+0j], 0.5+0j).ae(v) v = 0.9625 - 0.125j assert hyper([(3,2),-1],[4], 0.1+j/3).ae(v) assert hyper([1.5,-1.0],[4], 0.1+j/3).ae(v) assert hyper([1.5,-1.0],[4+0j], 0.1+j/3).ae(v) assert hyper([1.5+0j,-1.0+0j],[4+0j], 0.1+j/3).ae(v) v = 1.02111069501693445001 - 0.50402252613466859521j assert hyper([(2,10),(3,10)],[(4,10)],1.5).ae(v) assert hyper([0.2,(3,10)],[0.4+0j],1.5).ae(v) assert hyper([0.2,(3,10)],[0.4+0j],1.5+0j).ae(v) v = 0.76922501362865848528 + 0.32640579593235886194j assert hyper([(2,10),(3,10)],[(4,10)],4+2j).ae(v) assert hyper([0.2,(3,10)],[0.4+0j],4+2j).ae(v) assert hyper([0.2,(3,10)],[(4,10)],4+2j).ae(v) def test_hyper_2f1_hard(): mp.dps = 15 # Singular cases assert hyp2f1(2,-1,-1,3).ae(0.25) assert hyp2f1(2,-2,-2,3).ae(0.25) assert hyp2f1(2,-1,-1,3,eliminate=False) == 7 assert hyp2f1(2,-2,-2,3,eliminate=False) == 34 assert hyp2f1(2,-2,-3,3) == 14 assert hyp2f1(2,-3,-2,3) == inf assert hyp2f1(2,-1.5,-1.5,3) == 0.25 assert hyp2f1(1,2,3,0) == 1 assert hyp2f1(0,1,0,0) == 1 assert hyp2f1(0,0,0,0) == 1 assert isnan(hyp2f1(1,1,0,0)) assert hyp2f1(2,-1,-5, 0.25+0.25j).ae(1.1+0.1j) assert hyp2f1(2,-5,-5, 0.25+0.25j, eliminate=False).ae(163./128 + 125./128*j) assert hyp2f1(0.7235, -1, -5, 0.3).ae(1.04341) assert hyp2f1(0.7235, -5, -5, 0.3, eliminate=False).ae(1.2939225017815903812) assert hyp2f1(-1,-2,4,1) == 1.5 assert hyp2f1(1,2,-3,1) == inf assert hyp2f1(-2,-2,1,1) == 6 assert hyp2f1(1,-2,-4,1).ae(5./3) assert hyp2f1(0,-6,-4,1) == 1 assert hyp2f1(0,-3,-4,1) == 1 assert hyp2f1(0,0,0,1) == 1 assert hyp2f1(1,0,0,1,eliminate=False) == 1 assert hyp2f1(1,1,0,1) == inf assert hyp2f1(1,-6,-4,1) == inf assert hyp2f1(-7.2,-0.5,-4.5,1) == 0 assert hyp2f1(-7.2,-1,-2,1).ae(-2.6) assert hyp2f1(1,-0.5,-4.5, 1) == inf assert hyp2f1(1,0.5,-4.5, 1) == -inf # Check evaluation on / close to unit circle z = exp(j*pi/3) w = (nthroot(2,3)+1)*exp(j*pi/12)/nthroot(3,4)**3 assert hyp2f1('1/2','1/6','1/3', z).ae(w) assert hyp2f1('1/2','1/6','1/3', z.conjugate()).ae(w.conjugate()) assert hyp2f1(0.25, (1,3), 2, '0.999').ae(1.06826449496030635) assert hyp2f1(0.25, (1,3), 2, '1.001').ae(1.06867299254830309446-0.00001446586793975874j) assert hyp2f1(0.25, (1,3), 2, -1).ae(0.96656584492524351673) assert hyp2f1(0.25, (1,3), 2, j).ae(0.99041766248982072266+0.03777135604180735522j) assert hyp2f1(2,3,5,'0.99').ae(27.699347904322690602) assert hyp2f1((3,2),-0.5,3,'0.99').ae(0.68403036843911661388) assert hyp2f1(2,3,5,1j).ae(0.37290667145974386127+0.59210004902748285917j) assert fsum([hyp2f1((7,10),(2,3),(-1,2), 0.95*exp(j*k)) for k in range(1,15)]).ae(52.851400204289452922+6.244285013912953225j) assert fsum([hyp2f1((7,10),(2,3),(-1,2), 1.05*exp(j*k)) for k in range(1,15)]).ae(54.506013786220655330-3.000118813413217097j) assert fsum([hyp2f1((7,10),(2,3),(-1,2), exp(j*k)) for k in range(1,15)]).ae(55.792077935955314887+1.731986485778500241j) assert hyp2f1(2,2.5,-3.25,0.999).ae(218373932801217082543180041.33) # Branches assert hyp2f1(1,1,2,1.01).ae(4.5595744415723676911-3.1104877758314784539j) assert hyp2f1(1,1,2,1.01+0.1j).ae(2.4149427480552782484+1.4148224796836938829j) assert hyp2f1(1,1,2,3+4j).ae(0.14576709331407297807+0.48379185417980360773j) assert hyp2f1(1,1,2,4).ae(-0.27465307216702742285 - 0.78539816339744830962j) assert hyp2f1(1,1,2,-4).ae(0.40235947810852509365) # Other: # Cancellation with a large parameter involved (bug reported on sage-devel) assert hyp2f1(112, (51,10), (-9,10), -0.99999).ae(-1.6241361047970862961e-24, abs_eps=0, rel_eps=eps*16) def test_hyper_3f2_etc(): assert hyper([1,2,3],[1.5,8],-1).ae(0.67108992351533333030) assert hyper([1,2,3,4],[5,6,7], -1).ae(0.90232988035425506008) assert hyper([1,2,3],[1.25,5], 1).ae(28.924181329701905701) assert hyper([1,2,3,4],[5,6,7],5).ae(1.5192307344006649499-1.1529845225075537461j) assert hyper([1,2,3,4,5],[6,7,8,9],-1).ae(0.96288759462882357253) assert hyper([1,2,3,4,5],[6,7,8,9],1).ae(1.0428697385885855841) assert hyper([1,2,3,4,5],[6,7,8,9],5).ae(1.33980653631074769423-0.07143405251029226699j) assert hyper([1,2.79,3.08,4.37],[5.2,6.1,7.3],5).ae(1.0996321464692607231-1.7748052293979985001j) assert hyper([1,1,1],[1,2],1) == inf assert hyper([1,1,1],[2,(101,100)],1).ae(100.01621213528313220) # slow -- covered by doctests #assert hyper([1,1,1],[2,3],0.9999).ae(1.2897972005319693905) def test_hyper_u(): mp.dps = 15 assert hyperu(2,-3,0).ae(0.05) assert hyperu(2,-3.5,0).ae(4./99) assert hyperu(2,0,0) == 0.5 assert hyperu(-5,1,0) == -120 assert hyperu(-5,2,0) == inf assert hyperu(-5,-2,0) == 0 assert hyperu(7,7,3).ae(0.00014681269365593503986) #exp(3)*gammainc(-6,3) assert hyperu(2,-3,4).ae(0.011836478100271995559) assert hyperu(3,4,5).ae(1./125) assert hyperu(2,3,0.0625) == 256 assert hyperu(-1,2,0.25+0.5j) == -1.75+0.5j assert hyperu(0.5,1.5,7.25).ae(2/sqrt(29)) assert hyperu(2,6,pi).ae(0.55804439825913399130) assert (hyperu((3,2),8,100+201j)*10**4).ae(-0.3797318333856738798 - 2.9974928453561707782j) assert (hyperu((5,2),(-1,2),-5000)*10**10).ae(-5.6681877926881664678j) assert (hyperu((5,2),(-1,2),-500)*10**7).ae(-1.82526906001593252847j) def test_hyper_2f0(): mp.dps = 15 assert hyper([1,2],[],3) == hyp2f0(1,2,3) assert hyp2f0(2,3,7).ae(0.0116108068639728714668 - 0.0073727413865865802130j) assert hyp2f0(2,3,0) == 1 assert hyp2f0(0,0,0) == 1 assert hyp2f0(-1,-1,1).ae(2) assert hyp2f0(-4,1,1.5).ae(62.5) assert hyp2f0(-4,1,50).ae(147029801) assert hyp2f0(-4,1,0.0001).ae(0.99960011997600240000) assert hyp2f0(0.5,0.25,0.001).ae(1.0001251174078538115) assert hyp2f0(0.5,0.25,3+4j).ae(0.85548875824755163518 + 0.21636041283392292973j) # Important: cancellation check assert hyp2f0((1,6),(5,6),-0.02371708245126284498).ae(0.996785723120804309) # Should be exact; polynomial case assert hyp2f0(-2,1,0.5+0.5j,zeroprec=200) == 0 assert hyp2f0(1,-2,0.5+0.5j,zeroprec=200) == 0 # There used to be a bug in thresholds that made one of the following hang for d in [15, 50, 80]: mp.dps = d assert hyp2f0(1.5, 0.5, 0.009).ae('1.006867007239309717945323585695344927904000945829843527398772456281301440034218290443367270629519483 + 1.238277162240704919639384945859073461954721356062919829456053965502443570466701567100438048602352623e-46j') def test_hyper_1f2(): mp.dps = 15 assert hyper([1],[2,3],4) == hyp1f2(1,2,3,4) a1,b1,b2 = (1,10),(2,3),1./16 assert hyp1f2(a1,b1,b2,10).ae(298.7482725554557568) assert hyp1f2(a1,b1,b2,100).ae(224128961.48602947604) assert hyp1f2(a1,b1,b2,1000).ae(1.1669528298622675109e+27) assert hyp1f2(a1,b1,b2,10000).ae(2.4780514622487212192e+86) assert hyp1f2(a1,b1,b2,100000).ae(1.3885391458871523997e+274) assert hyp1f2(a1,b1,b2,1000000).ae('9.8851796978960318255e+867') assert hyp1f2(a1,b1,b2,10**7).ae('1.1505659189516303646e+2746') assert hyp1f2(a1,b1,b2,10**8).ae('1.4672005404314334081e+8685') assert hyp1f2(a1,b1,b2,10**20).ae('3.6888217332150976493e+8685889636') assert hyp1f2(a1,b1,b2,10*j).ae(-16.163252524618572878 - 44.321567896480184312j) assert hyp1f2(a1,b1,b2,100*j).ae(61938.155294517848171 + 637349.45215942348739j) assert hyp1f2(a1,b1,b2,1000*j).ae(8455057657257695958.7 + 6261969266997571510.6j) assert hyp1f2(a1,b1,b2,10000*j).ae(-8.9771211184008593089e+60 + 4.6550528111731631456e+59j) assert hyp1f2(a1,b1,b2,100000*j).ae(2.6398091437239324225e+193 + 4.1658080666870618332e+193j) assert hyp1f2(a1,b1,b2,1000000*j).ae('3.5999042951925965458e+613 + 1.5026014707128947992e+613j') assert hyp1f2(a1,b1,b2,10**7*j).ae('-8.3208715051623234801e+1939 - 3.6752883490851869429e+1941j') assert hyp1f2(a1,b1,b2,10**8*j).ae('2.0724195707891484454e+6140 - 1.3276619482724266387e+6141j') assert hyp1f2(a1,b1,b2,10**20*j).ae('-1.1734497974795488504e+6141851462 + 1.1498106965385471542e+6141851462j') def test_hyper_2f3(): mp.dps = 15 assert hyper([1,2],[3,4,5],6) == hyp2f3(1,2,3,4,5,6) a1,a2,b1,b2,b3 = (1,10),(2,3),(3,10), 2, 1./16 # Check asymptotic expansion assert hyp2f3(a1,a2,b1,b2,b3,10).ae(128.98207160698659976) assert hyp2f3(a1,a2,b1,b2,b3,1000).ae(6.6309632883131273141e25) assert hyp2f3(a1,a2,b1,b2,b3,10000).ae(4.6863639362713340539e84) assert hyp2f3(a1,a2,b1,b2,b3,100000).ae(8.6632451236103084119e271) assert hyp2f3(a1,a2,b1,b2,b3,10**6).ae('2.0291718386574980641e865') assert hyp2f3(a1,a2,b1,b2,b3,10**7).ae('7.7639836665710030977e2742') assert hyp2f3(a1,a2,b1,b2,b3,10**8).ae('3.2537462584071268759e8681') assert hyp2f3(a1,a2,b1,b2,b3,10**20).ae('1.2966030542911614163e+8685889627') assert hyp2f3(a1,a2,b1,b2,b3,10*j).ae(-18.551602185587547854 - 13.348031097874113552j) assert hyp2f3(a1,a2,b1,b2,b3,100*j).ae(78634.359124504488695 + 74459.535945281973996j) assert hyp2f3(a1,a2,b1,b2,b3,1000*j).ae(597682550276527901.59 - 65136194809352613.078j) assert hyp2f3(a1,a2,b1,b2,b3,10000*j).ae(-1.1779696326238582496e+59 + 1.2297607505213133872e+59j) assert hyp2f3(a1,a2,b1,b2,b3,100000*j).ae(2.9844228969804380301e+191 + 7.5587163231490273296e+190j) assert hyp2f3(a1,a2,b1,b2,b3,1000000*j).ae('7.4859161049322370311e+610 - 2.8467477015940090189e+610j') assert hyp2f3(a1,a2,b1,b2,b3,10**7*j).ae('-1.7477645579418800826e+1938 - 1.7606522995808116405e+1938j') assert hyp2f3(a1,a2,b1,b2,b3,10**8*j).ae('-1.6932731942958401784e+6137 - 2.4521909113114629368e+6137j') assert hyp2f3(a1,a2,b1,b2,b3,10**20*j).ae('-2.0988815677627225449e+6141851451 + 5.7708223542739208681e+6141851452j') def test_hyper_2f2(): mp.dps = 15 assert hyper([1,2],[3,4],5) == hyp2f2(1,2,3,4,5) a1,a2,b1,b2 = (3,10),4,(1,2),1./16 assert hyp2f2(a1,a2,b1,b2,10).ae(448225936.3377556696) assert hyp2f2(a1,a2,b1,b2,10000).ae('1.2012553712966636711e+4358') assert hyp2f2(a1,a2,b1,b2,-20000).ae(-0.04182343755661214626) assert hyp2f2(a1,a2,b1,b2,10**20).ae('1.1148680024303263661e+43429448190325182840') def test_orthpoly(): mp.dps = 15 assert jacobi(-4,2,3,0.7).ae(22800./4913) assert jacobi(3,2,4,5.5) == 4133.125 assert jacobi(1.5,5/6.,4,0).ae(-1.0851951434075508417) assert jacobi(-2, 1, 2, 4).ae(-0.16) assert jacobi(2, -1, 2.5, 4).ae(34.59375) assert legendre(5, 7) == 129367 assert legendre(0.5,0).ae(0.53935260118837935667) assert legendre(-1,-1) == 1 assert legendre(0,-1) == 1 assert legendre(0, 1) == 1 assert legendre(1, -1) == -1 assert legendre(7, 1) == 1 assert legendre(7, -1) == -1 assert legendre(8,1.5).ae(15457523./32768) assert legendre(j,-j).ae(2.4448182735671431011 + 0.6928881737669934843j) assert chebyu(5,1) == 6 assert chebyt(3,2) == 26 assert legendre(3.5,-1) == inf assert legendre(4.5,-1) == -inf assert legendre(3.5+1j,-1) == mpc(inf,inf) assert legendre(4.5+1j,-1) == mpc(-inf,-inf) assert laguerre(4, -2, 3).ae(-1.125) assert laguerre(3, 1+j, 0.5).ae(0.2291666666666666667 + 2.5416666666666666667j) @XFAIL def test_orthpoly_bug(): assert jacobi(2, -1, 2, 4) == 28.5 def test_hermite(): mp.dps = 15 assert hermite(-2, 0).ae(0.5) assert hermite(-1, 0).ae(0.88622692545275801365) assert hermite(0, 0).ae(1) assert hermite(1, 0) == 0 assert hermite(2, 0).ae(-2) assert hermite(0, 2).ae(1) assert hermite(1, 2).ae(4) assert hermite(1, -2).ae(-4) assert hermite(2, -2).ae(14) assert hermite(0.5, 0).ae(0.69136733903629335053) assert hermite(9, 0) == 0 assert hermite(4,4).ae(3340) assert hermite(3,4).ae(464) assert hermite(-4,4).ae(0.00018623860287512396181) assert hermite(-3,4).ae(0.0016540169879668766270) assert hermite(9, 2.5j).ae(13638725j) assert hermite(9, -2.5j).ae(-13638725j) assert hermite(9, 100).ae(511078883759363024000) assert hermite(9, -100).ae(-511078883759363024000) assert hermite(9, 100j).ae(512922083920643024000j) assert hermite(9, -100j).ae(-512922083920643024000j) assert hermite(-9.5, 2.5j).ae(-2.9004951258126778174e-6 + 1.7601372934039951100e-6j) assert hermite(-9.5, -2.5j).ae(-2.9004951258126778174e-6 - 1.7601372934039951100e-6j) assert hermite(-9.5, 100).ae(1.3776300722767084162e-22, abs_eps=0, rel_eps=eps) assert hermite(-9.5, -100).ae('1.3106082028470671626e4355') assert hermite(-9.5, 100j).ae(-9.7900218581864768430e-23 - 9.7900218581864768430e-23j, abs_eps=0, rel_eps=eps) assert hermite(-9.5, -100j).ae(-9.7900218581864768430e-23 + 9.7900218581864768430e-23j, abs_eps=0, rel_eps=eps) assert hermite(2+3j, -1-j).ae(851.3677063883687676 - 1496.4373467871007997j) def test_gegenbauer(): mp.dps = 15 assert gegenbauer(1,2,3).ae(12) assert gegenbauer(2,3,4).ae(381) assert gegenbauer(0,0,0) == 0 assert gegenbauer(2,-1,3) == 0 assert gegenbauer(-7, 0.5, 3).ae(8989) assert gegenbauer(1, -0.5, 3).ae(-3) assert gegenbauer(1, -1.5, 3).ae(-9) assert gegenbauer(-0.5, -0.5, 3).ae(-2.6383553159023906245) assert gegenbauer(2+3j, 1-j, 3+4j).ae(14.880536623203696780 + 20.022029711598032898j) @XFAIL def test_gegenbauer_unimplemented(): assert gegenbauer(1, -0.5, 3).ae(-3) assert gegenbauer(-2, -0.5, 3).ae(-12) def test_legenp(): mp.dps = 15 assert legenp(2,0,4) == legendre(2,4) assert legenp(-2, -1, 0.5).ae(0.43301270189221932338) assert legenp(-2, -1, 0.5, type=3).ae(0.43301270189221932338j) assert legenp(-2, 1, 0.5).ae(-0.86602540378443864676) assert legenp(2+j, 3+4j, -j).ae(134742.98773236786148 + 429782.72924463851745j) assert legenp(2+j, 3+4j, -j, type=3).ae(802.59463394152268507 - 251.62481308942906447j) assert legenp(2,4,3).ae(0) assert legenp(2,4,3,type=3).ae(0) assert legenp(2,1,0.5).ae(-1.2990381056766579701) assert legenp(2,1,0.5,type=3).ae(1.2990381056766579701j) assert legenp(3,2,3).ae(-360) assert legenp(3,3,3).ae(240j*2**0.5) assert legenp(3,4,3).ae(0) assert legenp(0,0.5,2).ae(0.52503756790433198939 - 0.52503756790433198939j) assert legenp(-1,-0.5,2).ae(0.60626116232846498110 + 0.60626116232846498110j) assert legenp(-2,0.5,2).ae(1.5751127037129959682 - 1.5751127037129959682j) assert legenp(-2,0.5,-0.5).ae(-0.85738275810499171286) def test_legenq(): mp.dps = 15 f = legenq # Evaluation at poles assert isnan(f(3,2,1)) assert isnan(f(3,2,-1)) assert isnan(f(3,2,1,type=3)) assert isnan(f(3,2,-1,type=3)) # Evaluation at 0 assert f(0,1,0,type=2).ae(-1) assert f(-2,2,0,type=2,zeroprec=200).ae(0) assert f(1.5,3,0,type=2).ae(-2.2239343475841951023) assert f(0,1,0,type=3).ae(j) assert f(-2,2,0,type=3,zeroprec=200).ae(0) assert f(1.5,3,0,type=3).ae(2.2239343475841951022*(1-1j)) # Standard case, degree 0 assert f(0,0,-1.5).ae(-0.8047189562170501873 + 1.5707963267948966192j) assert f(0,0,-0.5).ae(-0.54930614433405484570) assert f(0,0,0,zeroprec=200).ae(0) assert f(0,0,0.5).ae(0.54930614433405484570) assert f(0,0,1.5).ae(0.8047189562170501873 - 1.5707963267948966192j) assert f(0,0,-1.5,type=3).ae(-0.80471895621705018730) assert f(0,0,-0.5,type=3).ae(-0.5493061443340548457 - 1.5707963267948966192j) assert f(0,0,0,type=3).ae(-1.5707963267948966192j) assert f(0,0,0.5,type=3).ae(0.5493061443340548457 - 1.5707963267948966192j) assert f(0,0,1.5,type=3).ae(0.80471895621705018730) # Standard case, degree 1 assert f(1,0,-1.5).ae(0.2070784343255752810 - 2.3561944901923449288j) assert f(1,0,-0.5).ae(-0.72534692783297257715) assert f(1,0,0).ae(-1) assert f(1,0,0.5).ae(-0.72534692783297257715) assert f(1,0,1.5).ae(0.2070784343255752810 - 2.3561944901923449288j) # Standard case, degree 2 assert f(2,0,-1.5).ae(-0.0635669991240192885 + 4.5160394395353277803j) assert f(2,0,-0.5).ae(0.81866326804175685571) assert f(2,0,0,zeroprec=200).ae(0) assert f(2,0,0.5).ae(-0.81866326804175685571) assert f(2,0,1.5).ae(0.0635669991240192885 - 4.5160394395353277803j) # Misc orders and degrees assert f(2,3,1.5,type=2).ae(-5.7243340223994616228j) assert f(2,3,1.5,type=3).ae(-5.7243340223994616228) assert f(2,3,0.5,type=2).ae(-12.316805742712016310) assert f(2,3,0.5,type=3).ae(-12.316805742712016310j) assert f(2,3,-1.5,type=2).ae(-5.7243340223994616228j) assert f(2,3,-1.5,type=3).ae(5.7243340223994616228) assert f(2,3,-0.5,type=2).ae(-12.316805742712016310) assert f(2,3,-0.5,type=3).ae(-12.316805742712016310j) assert f(2+3j, 3+4j, 0.5, type=3).ae(0.0016119404873235186807 - 0.0005885900510718119836j) assert f(2+3j, 3+4j, -1.5, type=3).ae(0.008451400254138808670 + 0.020645193304593235298j) assert f(-2.5,1,-1.5).ae(3.9553395527435335749j) assert f(-2.5,1,-0.5).ae(1.9290561746445456908) assert f(-2.5,1,0).ae(1.2708196271909686299) assert f(-2.5,1,0.5).ae(-0.31584812990742202869) assert f(-2.5,1,1.5).ae(-3.9553395527435335742 + 0.2993235655044701706j) assert f(-2.5,1,-1.5,type=3).ae(0.29932356550447017254j) assert f(-2.5,1,-0.5,type=3).ae(-0.3158481299074220287 - 1.9290561746445456908j) assert f(-2.5,1,0,type=3).ae(1.2708196271909686292 - 1.2708196271909686299j) assert f(-2.5,1,0.5,type=3).ae(1.9290561746445456907 + 0.3158481299074220287j) assert f(-2.5,1,1.5,type=3).ae(-0.29932356550447017254) def test_agm(): mp.dps = 15 assert agm(0,0) == 0 assert agm(0,1) == 0 assert agm(1,1) == 1 assert agm(7,7) == 7 assert agm(j,j) == j assert (1/agm(1,sqrt(2))).ae(0.834626841674073186) assert agm(1,2).ae(1.4567910310469068692) assert agm(1,3).ae(1.8636167832448965424) assert agm(1,j).ae(0.599070117367796104+0.599070117367796104j) assert agm(2) == agm(1,2) assert agm(-3,4).ae(0.63468509766550907+1.3443087080896272j) def test_gammainc(): mp.dps = 15 assert gammainc(2,5).ae(6*exp(-5)) assert gammainc(2,0,5).ae(1-6*exp(-5)) assert gammainc(2,3,5).ae(-6*exp(-5)+4*exp(-3)) assert gammainc(-2.5,-0.5).ae(-0.9453087204829418812-5.3164237738936178621j) assert gammainc(0,2,4).ae(0.045121158298212213088) assert gammainc(0,3).ae(0.013048381094197037413) assert gammainc(0,2+j,1-j).ae(0.00910653685850304839-0.22378752918074432574j) assert gammainc(0,1-j).ae(0.00028162445198141833+0.17932453503935894015j) assert gammainc(3,4,5,True).ae(0.11345128607046320253) assert gammainc(3.5,0,inf).ae(gamma(3.5)) assert gammainc(-150.5,500).ae('6.9825435345798951153e-627') assert gammainc(-150.5,800).ae('4.6885137549474089431e-788') assert gammainc(-3.5, -20.5).ae(0.27008820585226911 - 1310.31447140574997636j) assert gammainc(-3.5, -200.5).ae(0.27008820585226911 - 5.3264597096208368435e76j) # XXX real part assert gammainc(0,0,2) == inf assert gammainc(1,b=1).ae(0.6321205588285576784) assert gammainc(3,2,2) == 0 assert gammainc(2,3+j,3-j).ae(-0.28135485191849314194j) assert gammainc(4+0j,1).ae(5.8860710587430771455) # Regularized upper gamma assert isnan(gammainc(0, 0, regularized=True)) assert gammainc(-1, 0, regularized=True) == inf assert gammainc(1, 0, regularized=True) == 1 assert gammainc(0, 5, regularized=True) == 0 assert gammainc(0, 2+3j, regularized=True) == 0 assert gammainc(0, 5000, regularized=True) == 0 assert gammainc(0, 10**30, regularized=True) == 0 assert gammainc(-1, 5, regularized=True) == 0 assert gammainc(-1, 5000, regularized=True) == 0 assert gammainc(-1, 10**30, regularized=True) == 0 assert gammainc(-1, -5, regularized=True) == 0 assert gammainc(-1, -5000, regularized=True) == 0 assert gammainc(-1, -10**30, regularized=True) == 0 assert gammainc(-1, 3+4j, regularized=True) == 0 assert gammainc(1, 5, regularized=True).ae(exp(-5)) assert gammainc(1, 5000, regularized=True).ae(exp(-5000)) assert gammainc(1, 10**30, regularized=True).ae(exp(-10**30)) assert gammainc(1, 3+4j, regularized=True).ae(exp(-3-4j)) assert gammainc(-1000000,2).ae('1.3669297209397347754e-301037', abs_eps=0, rel_eps=8*eps) assert gammainc(-1000000,2,regularized=True) == 0 assert gammainc(-1000000,3+4j).ae('-1.322575609404222361e-698979 - 4.9274570591854533273e-698978j', abs_eps=0, rel_eps=8*eps) assert gammainc(-1000000,3+4j,regularized=True) == 0 assert gammainc(2+3j, 4+5j, regularized=True).ae(0.085422013530993285774-0.052595379150390078503j) assert gammainc(1000j, 1000j, regularized=True).ae(0.49702647628921131761 + 0.00297355675013575341j) # Generalized assert gammainc(3,4,2) == -gammainc(3,2,4) assert gammainc(4, 2, 3).ae(1.2593494302978947396) assert gammainc(4, 2, 3, regularized=True).ae(0.20989157171631578993) assert gammainc(0, 2, 3).ae(0.035852129613864082155) assert gammainc(0, 2, 3, regularized=True) == 0 assert gammainc(-1, 2, 3).ae(0.015219822548487616132) assert gammainc(-1, 2, 3, regularized=True) == 0 assert gammainc(0, 2, 3).ae(0.035852129613864082155) assert gammainc(0, 2, 3, regularized=True) == 0 # Should use upper gammas assert gammainc(5, 10000, 12000).ae('1.1359381951461801687e-4327', abs_eps=0, rel_eps=8*eps) # Should use lower gammas assert gammainc(10000, 2, 3).ae('8.1244514125995785934e4765') def test_gammainc_expint_n(): # These tests are intended to check all cases of the low-level code # for upper gamma and expint with small integer index. # Need to cover positive/negative arguments; small/large/huge arguments # for both positive and negative indices, as well as indices 0 and 1 # which may be special-cased mp.dps = 15 assert expint(-3,3.5).ae(0.021456366563296693987) assert expint(-2,3.5).ae(0.014966633183073309405) assert expint(-1,3.5).ae(0.011092916359219041088) assert expint(0,3.5).ae(0.0086278238349481430685) assert expint(1,3.5).ae(0.0069701398575483929193) assert expint(2,3.5).ae(0.0058018939208991255223) assert expint(3,3.5).ae(0.0049453773495857807058) assert expint(-3,-3.5).ae(-4.6618170604073311319) assert expint(-2,-3.5).ae(-5.5996974157555515963) assert expint(-1,-3.5).ae(-6.7582555017739415818) assert expint(0,-3.5).ae(-9.4615577024835182145) assert expint(1,-3.5).ae(-13.925353995152335292 - 3.1415926535897932385j) assert expint(2,-3.5).ae(-15.62328702434085977 - 10.995574287564276335j) assert expint(3,-3.5).ae(-10.783026313250347722 - 19.242255003237483586j) assert expint(-3,350).ae(2.8614825451252838069e-155, abs_eps=0, rel_eps=8*eps) assert expint(-2,350).ae(2.8532837224504675901e-155, abs_eps=0, rel_eps=8*eps) assert expint(-1,350).ae(2.8451316155828634555e-155, abs_eps=0, rel_eps=8*eps) assert expint(0,350).ae(2.8370258275042797989e-155, abs_eps=0, rel_eps=8*eps) assert expint(1,350).ae(2.8289659656701459404e-155, abs_eps=0, rel_eps=8*eps) assert expint(2,350).ae(2.8209516419468505006e-155, abs_eps=0, rel_eps=8*eps) assert expint(3,350).ae(2.8129824725501272171e-155, abs_eps=0, rel_eps=8*eps) assert expint(-3,-350).ae(-2.8528796154044839443e+149) assert expint(-2,-350).ae(-2.8610072121701264351e+149) assert expint(-1,-350).ae(-2.8691813842677537647e+149) assert expint(0,-350).ae(-2.8774025343659421709e+149) u = expint(1,-350) assert u.ae(-2.8856710698020863568e+149) assert u.imag.ae(-3.1415926535897932385) u = expint(2,-350) assert u.ae(-2.8939874026504650534e+149) assert u.imag.ae(-1099.5574287564276335) u = expint(3,-350) assert u.ae(-2.9023519497915044349e+149) assert u.imag.ae(-192422.55003237483586) assert expint(-3,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) assert expint(-2,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) assert expint(-1,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) assert expint(0,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) assert expint(1,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) assert expint(2,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) assert expint(3,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) assert expint(-3,-350000000000000000000000).ae('-3.7805306852415755699e+152003068666138139677871') assert expint(-2,-350000000000000000000000).ae('-3.7805306852415755699e+152003068666138139677871') assert expint(-1,-350000000000000000000000).ae('-3.7805306852415755699e+152003068666138139677871') assert expint(0,-350000000000000000000000).ae('-3.7805306852415755699e+152003068666138139677871') u = expint(1,-350000000000000000000000) assert u.ae('-3.7805306852415755699e+152003068666138139677871') assert u.imag.ae(-3.1415926535897932385) u = expint(2,-350000000000000000000000) assert u.imag.ae(-1.0995574287564276335e+24) assert u.ae('-3.7805306852415755699e+152003068666138139677871') u = expint(3,-350000000000000000000000) assert u.imag.ae(-1.9242255003237483586e+47) assert u.ae('-3.7805306852415755699e+152003068666138139677871') # Small case; no branch cut assert gammainc(-3,3.5).ae(0.00010020262545203707109) assert gammainc(-2,3.5).ae(0.00040370427343557393517) assert gammainc(-1,3.5).ae(0.0016576839773997501492) assert gammainc(0,3.5).ae(0.0069701398575483929193) assert gammainc(1,3.5).ae(0.03019738342231850074) assert gammainc(2,3.5).ae(0.13588822540043325333) assert gammainc(3,3.5).ae(0.64169439772426814072) # Small case; with branch cut assert gammainc(-3,-3.5).ae(0.03595832954467563286 - 0.52359877559829887308j) assert gammainc(-2,-3.5).ae(-0.88024704597962022221 - 1.5707963267948966192j) assert gammainc(-1,-3.5).ae(4.4637962926688170771 - 3.1415926535897932385j) assert gammainc(0,-3.5).ae(-13.925353995152335292 - 3.1415926535897932385j) assert gammainc(1,-3.5).ae(33.115451958692313751) assert gammainc(2,-3.5).ae(-82.788629896730784377) assert gammainc(3,-3.5).ae(240.08702670051927469) # Asymptotic case; no branch cut assert gammainc(-3,350).ae(6.5424095113340358813e-163, abs_eps=0, rel_eps=8*eps) assert gammainc(-2,350).ae(2.296312222489899769e-160, abs_eps=0, rel_eps=8*eps) assert gammainc(-1,350).ae(8.059861834133858573e-158, abs_eps=0, rel_eps=8*eps) assert gammainc(0,350).ae(2.8289659656701459404e-155, abs_eps=0, rel_eps=8*eps) assert gammainc(1,350).ae(9.9295903962649792963e-153, abs_eps=0, rel_eps=8*eps) assert gammainc(2,350).ae(3.485286229089007733e-150, abs_eps=0, rel_eps=8*eps) assert gammainc(3,350).ae(1.2233453960006379793e-147, abs_eps=0, rel_eps=8*eps) # Asymptotic case; branch cut u = gammainc(-3,-350) assert u.ae(6.7889565783842895085e+141) assert u.imag.ae(-0.52359877559829887308) u = gammainc(-2,-350) assert u.ae(-2.3692668977889832121e+144) assert u.imag.ae(-1.5707963267948966192) u = gammainc(-1,-350) assert u.ae(8.2685354361441858669e+146) assert u.imag.ae(-3.1415926535897932385) u = gammainc(0,-350) assert u.ae(-2.8856710698020863568e+149) assert u.imag.ae(-3.1415926535897932385) u = gammainc(1,-350) assert u.ae(1.0070908870280797598e+152) assert u.imag == 0 u = gammainc(2,-350) assert u.ae(-3.5147471957279983618e+154) assert u.imag == 0 u = gammainc(3,-350) assert u.ae(1.2266568422179417091e+157) assert u.imag == 0 # Extreme asymptotic case assert gammainc(-3,350000000000000000000000).ae('5.0362468738874738859e-152003068666138139677990', abs_eps=0, rel_eps=8*eps) assert gammainc(-2,350000000000000000000000).ae('1.7626864058606158601e-152003068666138139677966', abs_eps=0, rel_eps=8*eps) assert gammainc(-1,350000000000000000000000).ae('6.1694024205121555102e-152003068666138139677943', abs_eps=0, rel_eps=8*eps) assert gammainc(0,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) assert gammainc(1,350000000000000000000000).ae('7.5575179651273905e-152003068666138139677896', abs_eps=0, rel_eps=8*eps) assert gammainc(2,350000000000000000000000).ae('2.645131287794586675e-152003068666138139677872', abs_eps=0, rel_eps=8*eps) assert gammainc(3,350000000000000000000000).ae('9.2579595072810533625e-152003068666138139677849', abs_eps=0, rel_eps=8*eps) u = gammainc(-3,-350000000000000000000000) assert u.ae('8.8175642804468234866e+152003068666138139677800') assert u.imag.ae(-0.52359877559829887308) u = gammainc(-2,-350000000000000000000000) assert u.ae('-3.0861474981563882203e+152003068666138139677824') assert u.imag.ae(-1.5707963267948966192) u = gammainc(-1,-350000000000000000000000) assert u.ae('1.0801516243547358771e+152003068666138139677848') assert u.imag.ae(-3.1415926535897932385) u = gammainc(0,-350000000000000000000000) assert u.ae('-3.7805306852415755699e+152003068666138139677871') assert u.imag.ae(-3.1415926535897932385) assert gammainc(1,-350000000000000000000000).ae('1.3231857398345514495e+152003068666138139677895') assert gammainc(2,-350000000000000000000000).ae('-4.6311500894209300731e+152003068666138139677918') assert gammainc(3,-350000000000000000000000).ae('1.6209025312973255256e+152003068666138139677942') def test_incomplete_beta(): mp.dps = 15 assert betainc(-2,-3,0.5,0.75).ae(63.4305673311255413583969) assert betainc(4.5,0.5+2j,2.5,6).ae(0.2628801146130621387903065 + 0.5162565234467020592855378j) assert betainc(4,5,0,6).ae(90747.77142857142857142857) def test_erf(): mp.dps = 15 assert erf(0) == 0 assert erf(1).ae(0.84270079294971486934) assert erf(3+4j).ae(-120.186991395079444098 - 27.750337293623902498j) assert erf(-4-3j).ae(-0.99991066178539168236 + 0.00004972026054496604j) assert erf(pi).ae(0.99999112385363235839) assert erf(1j).ae(1.6504257587975428760j) assert erf(-1j).ae(-1.6504257587975428760j) assert isinstance(erf(1), mpf) assert isinstance(erf(-1), mpf) assert isinstance(erf(0), mpf) assert isinstance(erf(0j), mpc) assert erf(inf) == 1 assert erf(-inf) == -1 assert erfi(0) == 0 assert erfi(1/pi).ae(0.371682698493894314) assert erfi(inf) == inf assert erfi(-inf) == -inf assert erf(1+0j) == erf(1) assert erfc(1+0j) == erfc(1) assert erf(0.2+0.5j).ae(1 - erfc(0.2+0.5j)) assert erfc(0) == 1 assert erfc(1).ae(1-erf(1)) assert erfc(-1).ae(1-erf(-1)) assert erfc(1/pi).ae(1-erf(1/pi)) assert erfc(-10) == 2 assert erfc(-1000000) == 2 assert erfc(-inf) == 2 assert erfc(inf) == 0 assert isnan(erfc(nan)) assert (erfc(10**4)*mpf(10)**43429453).ae('3.63998738656420') assert erf(8+9j).ae(-1072004.2525062051158 + 364149.91954310255423j) assert erfc(8+9j).ae(1072005.2525062051158 - 364149.91954310255423j) assert erfc(-8-9j).ae(-1072003.2525062051158 + 364149.91954310255423j) mp.dps = 50 # This one does not use the asymptotic series assert (erfc(10)*10**45).ae('2.0884875837625447570007862949577886115608181193212') # This one does assert (erfc(50)*10**1088).ae('2.0709207788416560484484478751657887929322509209954') mp.dps = 15 assert str(erfc(10**50)) == '3.66744826532555e-4342944819032518276511289189166050822943970058036665661144537831658646492088707747292249493384317534' assert erfinv(0) == 0 assert erfinv(0.5).ae(0.47693627620446987338) assert erfinv(-0.5).ae(-0.47693627620446987338) assert erfinv(1) == inf assert erfinv(-1) == -inf assert erf(erfinv(0.95)).ae(0.95) assert erf(erfinv(0.999999999995)).ae(0.999999999995) assert erf(erfinv(-0.999999999995)).ae(-0.999999999995) mp.dps = 50 assert erf(erfinv('0.99999999999999999999999999999995')).ae('0.99999999999999999999999999999995') assert erf(erfinv('0.999999999999999999999999999999995')).ae('0.999999999999999999999999999999995') assert erf(erfinv('-0.999999999999999999999999999999995')).ae('-0.999999999999999999999999999999995') mp.dps = 15 # Complex asymptotic expansions v = erfc(50j) assert v.real == 1 assert v.imag.ae('-6.1481820666053078736e+1083') assert erfc(-100+5j).ae(2) assert (erfc(100+5j)*10**4335).ae(2.3973567853824133572 - 3.9339259530609420597j) assert erfc(100+100j).ae(0.00065234366376857698698 - 0.0039357263629214118437j) def test_pdf(): mp.dps = 15 assert npdf(-inf) == 0 assert npdf(inf) == 0 assert npdf(5,0,2).ae(npdf(5+4,4,2)) assert quadts(lambda x: npdf(x,-0.5,0.8), [-inf, inf]) == 1 assert ncdf(0) == 0.5 assert ncdf(3,3) == 0.5 assert ncdf(-inf) == 0 assert ncdf(inf) == 1 assert ncdf(10) == 1 # Verify that this is computed accurately assert (ncdf(-10)*10**24).ae(7.619853024160526) def test_lambertw(): mp.dps = 15 assert lambertw(0) == 0 assert lambertw(0+0j) == 0 assert lambertw(inf) == inf assert isnan(lambertw(nan)) assert lambertw(inf,1).real == inf assert lambertw(inf,1).imag.ae(2*pi) assert lambertw(-inf,1).real == inf assert lambertw(-inf,1).imag.ae(3*pi) assert lambertw(0,-1) == -inf assert lambertw(0,1) == -inf assert lambertw(0,3) == -inf assert lambertw(e).ae(1) assert lambertw(1).ae(0.567143290409783873) assert lambertw(-pi/2).ae(j*pi/2) assert lambertw(-log(2)/2).ae(-log(2)) assert lambertw(0.25).ae(0.203888354702240164) assert lambertw(-0.25).ae(-0.357402956181388903) assert lambertw(-1./10000,0).ae(-0.000100010001500266719) assert lambertw(-0.25,-1).ae(-2.15329236411034965) assert lambertw(0.25,-1).ae(-3.00899800997004620-4.07652978899159763j) assert lambertw(-0.25,-1).ae(-2.15329236411034965) assert lambertw(0.25,1).ae(-3.00899800997004620+4.07652978899159763j) assert lambertw(-0.25,1).ae(-3.48973228422959210+7.41405453009603664j) assert lambertw(-4).ae(0.67881197132094523+1.91195078174339937j) assert lambertw(-4,1).ae(-0.66743107129800988+7.76827456802783084j) assert lambertw(-4,-1).ae(0.67881197132094523-1.91195078174339937j) assert lambertw(1000).ae(5.24960285240159623) assert lambertw(1000,1).ae(4.91492239981054535+5.44652615979447070j) assert lambertw(1000,-1).ae(4.91492239981054535-5.44652615979447070j) assert lambertw(1000,5).ae(3.5010625305312892+29.9614548941181328j) assert lambertw(3+4j).ae(1.281561806123775878+0.533095222020971071j) assert lambertw(-0.4+0.4j).ae(-0.10396515323290657+0.61899273315171632j) assert lambertw(3+4j,1).ae(-0.11691092896595324+5.61888039871282334j) assert lambertw(3+4j,-1).ae(0.25856740686699742-3.85211668616143559j) assert lambertw(-0.5,-1).ae(-0.794023632344689368-0.770111750510379110j) assert lambertw(-1./10000,1).ae(-11.82350837248724344+6.80546081842002101j) assert lambertw(-1./10000,-1).ae(-11.6671145325663544) assert lambertw(-1./10000,-2).ae(-11.82350837248724344-6.80546081842002101j) assert lambertw(-1./100000,4).ae(-14.9186890769540539+26.1856750178782046j) assert lambertw(-1./100000,5).ae(-15.0931437726379218666+32.5525721210262290086j) assert lambertw((2+j)/10).ae(0.173704503762911669+0.071781336752835511j) assert lambertw((2+j)/10,1).ae(-3.21746028349820063+4.56175438896292539j) assert lambertw((2+j)/10,-1).ae(-3.03781405002993088-3.53946629633505737j) assert lambertw((2+j)/10,4).ae(-4.6878509692773249+23.8313630697683291j) assert lambertw(-(2+j)/10).ae(-0.226933772515757933-0.164986470020154580j) assert lambertw(-(2+j)/10,1).ae(-2.43569517046110001+0.76974067544756289j) assert lambertw(-(2+j)/10,-1).ae(-3.54858738151989450-6.91627921869943589j) assert lambertw(-(2+j)/10,4).ae(-4.5500846928118151+20.6672982215434637j) mp.dps = 50 assert lambertw(pi).ae('1.073658194796149172092178407024821347547745350410314531') mp.dps = 15 # Former bug in generated branch assert lambertw(-0.5+0.002j).ae(-0.78917138132659918344 + 0.76743539379990327749j) assert lambertw(-0.5-0.002j).ae(-0.78917138132659918344 - 0.76743539379990327749j) assert lambertw(-0.448+0.4j).ae(-0.11855133765652382241 + 0.66570534313583423116j) assert lambertw(-0.448-0.4j).ae(-0.11855133765652382241 - 0.66570534313583423116j) assert lambertw(-0.65475+0.0001j).ae(-0.61053421111385310898+1.0396534993944097723803j) # Huge branch index w = lambertw(1,10**20) assert w.real.ae(-47.889578926290259164) assert w.imag.ae(6.2831853071795864769e+20) def test_lambertw_hard(): def check(x,y): y = convert(y) type_ok = True if isinstance(y, mpf): type_ok = isinstance(x, mpf) real_ok = abs(x.real-y.real) <= abs(y.real)*8*eps imag_ok = abs(x.imag-y.imag) <= abs(y.imag)*8*eps #print x, y, abs(x.real-y.real), abs(x.imag-y.imag) return real_ok and imag_ok # Evaluation near 0 mp.dps = 15 assert check(lambertw(1e-10), 9.999999999000000000e-11) assert check(lambertw(-1e-10), -1.000000000100000000e-10) assert check(lambertw(1e-10j), 9.999999999999999999733e-21 + 9.99999999999999999985e-11j) assert check(lambertw(-1e-10j), 9.999999999999999999733e-21 - 9.99999999999999999985e-11j) assert check(lambertw(1e-10,1), -26.303186778379041559 + 3.265093911703828397j) assert check(lambertw(-1e-10,1), -26.326236166739163892 + 6.526183280686333315j) assert check(lambertw(1e-10j,1), -26.312931726911421551 + 4.896366881798013421j) assert check(lambertw(-1e-10j,1), -26.297238779529035066 + 1.632807161345576513j) assert check(lambertw(1e-10,-1), -26.303186778379041559 - 3.265093911703828397j) assert check(lambertw(-1e-10,-1), -26.295238819246925694) assert check(lambertw(1e-10j,-1), -26.297238779529035028 - 1.6328071613455765135j) assert check(lambertw(-1e-10j,-1), -26.312931726911421551 - 4.896366881798013421j) # Test evaluation very close to the branch point -1/e # on the -1, 0, and 1 branches add = lambda x, y: fadd(x,y,exact=True) sub = lambda x, y: fsub(x,y,exact=True) addj = lambda x, y: fadd(x,fmul(y,1j,exact=True),exact=True) subj = lambda x, y: fadd(x,fmul(y,-1j,exact=True),exact=True) mp.dps = 1500 a = -1/e + 10*eps d3 = mpf('1e-3') d10 = mpf('1e-10') d20 = mpf('1e-20') d40 = mpf('1e-40') d80 = mpf('1e-80') d300 = mpf('1e-300') d1000 = mpf('1e-1000') mp.dps = 15 # ---- Branch 0 ---- # -1/e + eps assert check(lambertw(add(a,d3)), -0.92802015005456704876) assert check(lambertw(add(a,d10)), -0.99997668374140088071) assert check(lambertw(add(a,d20)), -0.99999999976683560186) assert lambertw(add(a,d40)) == -1 assert lambertw(add(a,d80)) == -1 assert lambertw(add(a,d300)) == -1 assert lambertw(add(a,d1000)) == -1 # -1/e - eps assert check(lambertw(sub(a,d3)), -0.99819016149860989001+0.07367191188934638577j) assert check(lambertw(sub(a,d10)), -0.9999999998187812114595992+0.0000233164398140346109194j) assert check(lambertw(sub(a,d20)), -0.99999999999999999998187+2.331643981597124203344e-10j) assert check(lambertw(sub(a,d40)), -1.0+2.33164398159712420336e-20j) assert check(lambertw(sub(a,d80)), -1.0+2.33164398159712420336e-40j) assert check(lambertw(sub(a,d300)), -1.0+2.33164398159712420336e-150j) assert check(lambertw(sub(a,d1000)), mpc(-1,'2.33164398159712420336e-500')) # -1/e + eps*j assert check(lambertw(addj(a,d3)), -0.94790387486938526634+0.05036819639190132490j) assert check(lambertw(addj(a,d10)), -0.9999835127872943680999899+0.0000164870314895821225256j) assert check(lambertw(addj(a,d20)), -0.999999999835127872929987+1.64872127051890935830e-10j) assert check(lambertw(addj(a,d40)), -0.9999999999999999999835+1.6487212707001281468305e-20j) assert check(lambertw(addj(a,d80)), -1.0 + 1.64872127070012814684865e-40j) assert check(lambertw(addj(a,d300)), -1.0 + 1.64872127070012814684865e-150j) assert check(lambertw(addj(a,d1000)), mpc(-1.0,'1.64872127070012814684865e-500')) # -1/e - eps*j assert check(lambertw(subj(a,d3)), -0.94790387486938526634-0.05036819639190132490j) assert check(lambertw(subj(a,d10)), -0.9999835127872943680999899-0.0000164870314895821225256j) assert check(lambertw(subj(a,d20)), -0.999999999835127872929987-1.64872127051890935830e-10j) assert check(lambertw(subj(a,d40)), -0.9999999999999999999835-1.6487212707001281468305e-20j) assert check(lambertw(subj(a,d80)), -1.0 - 1.64872127070012814684865e-40j) assert check(lambertw(subj(a,d300)), -1.0 - 1.64872127070012814684865e-150j) assert check(lambertw(subj(a,d1000)), mpc(-1.0,'-1.64872127070012814684865e-500')) # ---- Branch 1 ---- assert check(lambertw(addj(a,d3),1), -3.088501303219933378005990 + 7.458676867597474813950098j) assert check(lambertw(addj(a,d80),1), -3.088843015613043855957087 + 7.461489285654254556906117j) assert check(lambertw(addj(a,d300),1), -3.088843015613043855957087 + 7.461489285654254556906117j) assert check(lambertw(addj(a,d1000),1), -3.088843015613043855957087 + 7.461489285654254556906117j) assert check(lambertw(subj(a,d3),1), -1.0520914180450129534365906 + 0.0539925638125450525673175j) assert check(lambertw(subj(a,d10),1), -1.0000164872127056318529390 + 0.000016487393927159250398333077j) assert check(lambertw(subj(a,d20),1), -1.0000000001648721270700128 + 1.64872127088134693542628e-10j) assert check(lambertw(subj(a,d40),1), -1.000000000000000000016487 + 1.64872127070012814686677e-20j) assert check(lambertw(subj(a,d80),1), -1.0 + 1.64872127070012814684865e-40j) assert check(lambertw(subj(a,d300),1), -1.0 + 1.64872127070012814684865e-150j) assert check(lambertw(subj(a,d1000),1), mpc(-1.0, '1.64872127070012814684865e-500')) # ---- Branch -1 ---- # -1/e + eps assert check(lambertw(add(a,d3),-1), -1.075608941186624989414945) assert check(lambertw(add(a,d10),-1), -1.000023316621036696460620) assert check(lambertw(add(a,d20),-1), -1.000000000233164398177834) assert lambertw(add(a,d40),-1) == -1 assert lambertw(add(a,d80),-1) == -1 assert lambertw(add(a,d300),-1) == -1 assert lambertw(add(a,d1000),-1) == -1 # -1/e - eps assert check(lambertw(sub(a,d3),-1), -0.99819016149860989001-0.07367191188934638577j) assert check(lambertw(sub(a,d10),-1), -0.9999999998187812114595992-0.0000233164398140346109194j) assert check(lambertw(sub(a,d20),-1), -0.99999999999999999998187-2.331643981597124203344e-10j) assert check(lambertw(sub(a,d40),-1), -1.0-2.33164398159712420336e-20j) assert check(lambertw(sub(a,d80),-1), -1.0-2.33164398159712420336e-40j) assert check(lambertw(sub(a,d300),-1), -1.0-2.33164398159712420336e-150j) assert check(lambertw(sub(a,d1000),-1), mpc(-1,'-2.33164398159712420336e-500')) # -1/e + eps*j assert check(lambertw(addj(a,d3),-1), -1.0520914180450129534365906 - 0.0539925638125450525673175j) assert check(lambertw(addj(a,d10),-1), -1.0000164872127056318529390 - 0.0000164873939271592503983j) assert check(lambertw(addj(a,d20),-1), -1.0000000001648721270700 - 1.64872127088134693542628e-10j) assert check(lambertw(addj(a,d40),-1), -1.00000000000000000001648 - 1.6487212707001281468667726e-20j) assert check(lambertw(addj(a,d80),-1), -1.0 - 1.64872127070012814684865e-40j) assert check(lambertw(addj(a,d300),-1), -1.0 - 1.64872127070012814684865e-150j) assert check(lambertw(addj(a,d1000),-1), mpc(-1.0,'-1.64872127070012814684865e-500')) # -1/e - eps*j assert check(lambertw(subj(a,d3),-1), -3.088501303219933378005990-7.458676867597474813950098j) assert check(lambertw(subj(a,d10),-1), -3.088843015579260686911033-7.461489285372968780020716j) assert check(lambertw(subj(a,d20),-1), -3.088843015613043855953708-7.461489285654254556877988j) assert check(lambertw(subj(a,d40),-1), -3.088843015613043855957087-7.461489285654254556906117j) assert check(lambertw(subj(a,d80),-1), -3.088843015613043855957087 - 7.461489285654254556906117j) assert check(lambertw(subj(a,d300),-1), -3.088843015613043855957087 - 7.461489285654254556906117j) assert check(lambertw(subj(a,d1000),-1), -3.088843015613043855957087 - 7.461489285654254556906117j) # One more case, testing higher precision mp.dps = 500 x = -1/e + mpf('1e-13') ans = "-0.99999926266961377166355784455394913638782494543377383"\ "744978844374498153493943725364881490261187530235150668593869563"\ "168276697689459394902153960200361935311512317183678882" mp.dps = 15 assert lambertw(x).ae(ans) mp.dps = 50 assert lambertw(x).ae(ans) mp.dps = 150 assert lambertw(x).ae(ans) def test_meijerg(): mp.dps = 15 assert meijerg([[2,3],[1]],[[0.5,2],[3,4]], 2.5).ae(4.2181028074787439386) assert meijerg([[],[1+j]],[[1],[1]], 3+4j).ae(271.46290321152464592 - 703.03330399954820169j) assert meijerg([[0.25],[1]],[[0.5],[2]],0) == 0 assert meijerg([[0],[]],[[0,0,'1/3','2/3'], []], '2/27').ae(2.2019391389653314120) # Verify 1/z series being used assert meijerg([[-3],[-0.5]], [[-1],[-2.5]], -0.5).ae(-1.338096165935754898687431) assert meijerg([[1-(-1)],[1-(-2.5)]], [[1-(-3)],[1-(-0.5)]], -2.0).ae(-1.338096165935754898687431) assert meijerg([[-3],[-0.5]], [[-1],[-2.5]], -1).ae(-(pi+4)/(4*pi)) a = 2.5 b = 1.25 for z in [mpf(0.25), mpf(2)]: x1 = hyp1f1(a,b,z) x2 = gamma(b)/gamma(a)*meijerg([[1-a],[]],[[0],[1-b]],-z) x3 = gamma(b)/gamma(a)*meijerg([[1-0],[1-(1-b)]],[[1-(1-a)],[]],-1/z) assert x1.ae(x2) assert x1.ae(x3) def test_appellf1(): mp.dps = 15 assert appellf1(2,-2,1,1,2,3).ae(-1.75) assert appellf1(2,1,-2,1,2,3).ae(-8) assert appellf1(2,1,-2,1,0.5,0.25).ae(1.5) assert appellf1(-2,1,3,2,3,3).ae(19) assert appellf1(1,2,3,4,0.5,0.125).ae( 1.53843285792549786518) def test_coulomb(): # Note: most tests are doctests # Test for a bug: mp.dps = 15 assert coulombg(mpc(-5,0),2,3).ae(20.087729487721430394) def test_hyper_param_accuracy(): mp.dps = 15 As = [n+1e-10 for n in range(-5,-1)] Bs = [n+1e-10 for n in range(-12,-5)] assert hyper(As,Bs,10).ae(-381757055858.652671927) assert legenp(0.5, 100, 0.25).ae(-2.4124576567211311755e+144) assert (hyp1f1(1000,1,-100)*10**24).ae(5.2589445437370169113) assert (hyp2f1(10, -900, 10.5, 0.99)*10**24).ae(1.9185370579660768203) assert (hyp2f1(1000,1.5,-3.5,-1.5)*10**385).ae(-2.7367529051334000764) assert hyp2f1(-5, 10, 3, 0.5, zeroprec=500) == 0 assert (hyp1f1(-10000, 1000, 100)*10**424).ae(-3.1046080515824859974) assert (hyp2f1(1000,1.5,-3.5,-0.75,maxterms=100000)*10**231).ae(-4.0534790813913998643) assert legenp(2, 3, 0.25) == 0 try: hypercomb(lambda a: [([],[],[],[],[a],[-a],0.5)], [3]) assert 0 except ValueError: pass assert hypercomb(lambda a: [([],[],[],[],[a],[-a],0.5)], [3], infprec=200) == inf assert meijerg([[],[]],[[0,0,0,0],[]],0.1).ae(1.5680822343832351418) assert (besselk(400,400)*10**94).ae(1.4387057277018550583) mp.dps = 5 (hyp1f1(-5000.5, 1500, 100)*10**185).ae(8.5185229673381935522) (hyp1f1(-5000, 1500, 100)*10**185).ae(9.1501213424563944311) mp.dps = 15 (hyp1f1(-5000.5, 1500, 100)*10**185).ae(8.5185229673381935522) (hyp1f1(-5000, 1500, 100)*10**185).ae(9.1501213424563944311) assert hyp0f1(fadd(-20,'1e-100',exact=True), 0.25).ae(1.85014429040102783e+49) assert hyp0f1((-20*10**100+1, 10**100), 0.25).ae(1.85014429040102783e+49) def test_hypercomb_zero_pow(): # check that 0^0 = 1 assert hypercomb(lambda a: (([0],[a],[],[],[],[],0),), [0]) == 1 assert meijerg([[-1.5],[]],[[0],[-0.75]],0).ae(1.4464090846320771425) def test_spherharm(): mp.dps = 15 t = 0.5 r = 0.25 assert spherharm(0,0,t,r).ae(0.28209479177387814347) assert spherharm(1,-1,t,r).ae(0.16048941205971996369 - 0.04097967481096344271j) assert spherharm(1,0,t,r).ae(0.42878904414183579379) assert spherharm(1,1,t,r).ae(-0.16048941205971996369 - 0.04097967481096344271j) assert spherharm(2,-2,t,r).ae(0.077915886919031181734 - 0.042565643022253962264j) assert spherharm(2,-1,t,r).ae(0.31493387233497459884 - 0.08041582001959297689j) assert spherharm(2,0,t,r).ae(0.41330596756220761898) assert spherharm(2,1,t,r).ae(-0.31493387233497459884 - 0.08041582001959297689j) assert spherharm(2,2,t,r).ae(0.077915886919031181734 + 0.042565643022253962264j) assert spherharm(3,-3,t,r).ae(0.033640236589690881646 - 0.031339125318637082197j) assert spherharm(3,-2,t,r).ae(0.18091018743101461963 - 0.09883168583167010241j) assert spherharm(3,-1,t,r).ae(0.42796713930907320351 - 0.10927795157064962317j) assert spherharm(3,0,t,r).ae(0.27861659336351639787) assert spherharm(3,1,t,r).ae(-0.42796713930907320351 - 0.10927795157064962317j) assert spherharm(3,2,t,r).ae(0.18091018743101461963 + 0.09883168583167010241j) assert spherharm(3,3,t,r).ae(-0.033640236589690881646 - 0.031339125318637082197j) assert spherharm(0,-1,t,r) == 0 assert spherharm(0,-2,t,r) == 0 assert spherharm(0,1,t,r) == 0 assert spherharm(0,2,t,r) == 0 assert spherharm(1,2,t,r) == 0 assert spherharm(1,3,t,r) == 0 assert spherharm(1,-2,t,r) == 0 assert spherharm(1,-3,t,r) == 0 assert spherharm(2,3,t,r) == 0 assert spherharm(2,4,t,r) == 0 assert spherharm(2,-3,t,r) == 0 assert spherharm(2,-4,t,r) == 0 assert spherharm(3,4.5,0.5,0.25).ae(-22.831053442240790148 + 10.910526059510013757j) assert spherharm(2+3j, 1-j, 1+j, 3+4j).ae(-2.6582752037810116935 - 1.0909214905642160211j) assert spherharm(-6,2.5,t,r).ae(0.39383644983851448178 + 0.28414687085358299021j) assert spherharm(-3.5, 3, 0.5, 0.25).ae(0.014516852987544698924 - 0.015582769591477628495j) assert spherharm(-3, 3, 0.5, 0.25) == 0 assert spherharm(-6, 3, 0.5, 0.25).ae(-0.16544349818782275459 - 0.15412657723253924562j) assert spherharm(-6, 1.5, 0.5, 0.25).ae(0.032208193499767402477 + 0.012678000924063664921j) assert spherharm(3,0,0,1).ae(0.74635266518023078283) assert spherharm(3,-2,0,1) == 0 assert spherharm(3,-2,1,1).ae(-0.16270707338254028971 - 0.35552144137546777097j) def test_qfunctions(): mp.dps = 15 assert qp(2,3,100).ae('2.7291482267247332183e2391') def test_issue199(): mp.prec = 150 x = ldexp(2476979795053773,-52) assert betainc(206, 385, 0, 0.55, 1).ae('0.99999999999999999999996570910644857895771110649954') mp.dps = 15 try: u = hyp2f1(-5,5,0.5,0.5) raise AssertionError("hyp2f1(-5,5,0.5,0.5) (failed zero detection)") except (mp.NoConvergence, ValueError): pass sympy-0.7.4.1/sympy/mpmath/tests/test_power.py0000644000175000017500000001175712253362407021635 0ustar georgeskgeorgeskfrom sympy.mpmath import * from sympy.mpmath.libmp import * import random def test_fractional_pow(): mp.dps = 15 assert mpf(16) ** 2.5 == 1024 assert mpf(64) ** 0.5 == 8 assert mpf(64) ** -0.5 == 0.125 assert mpf(16) ** -2.5 == 0.0009765625 assert (mpf(10) ** 0.5).ae(3.1622776601683791) assert (mpf(10) ** 2.5).ae(316.2277660168379) assert (mpf(10) ** -0.5).ae(0.31622776601683794) assert (mpf(10) ** -2.5).ae(0.0031622776601683794) assert (mpf(10) ** 0.3).ae(1.9952623149688795) assert (mpf(10) ** -0.3).ae(0.50118723362727224) def test_pow_integer_direction(): """ Test that inexact integer powers are rounded in the right direction. """ random.seed(1234) for prec in [10, 53, 200]: for i in range(50): a = random.randint(1<<(prec-1), 1< ab def test_pow_epsilon_rounding(): """ Stress test directed rounding for powers with integer exponents. Basically, we look at the following cases: >>> 1.0001 ** -5 0.99950014996500702 >>> 0.9999 ** -5 1.000500150035007 >>> (-1.0001) ** -5 -0.99950014996500702 >>> (-0.9999) ** -5 -1.000500150035007 >>> 1.0001 ** -6 0.99940020994401269 >>> 0.9999 ** -6 1.0006002100560125 >>> (-1.0001) ** -6 0.99940020994401269 >>> (-0.9999) ** -6 1.0006002100560125 etc. We run the tests with values a very small epsilon away from 1: small enough that the result is indistinguishable from 1 when rounded to nearest at the output precision. We check that the result is not erroneously rounded to 1 in cases where the rounding should be done strictly away from 1. """ def powr(x, n, r): return make_mpf(mpf_pow_int(x._mpf_, n, mp.prec, r)) for (inprec, outprec) in [(100, 20), (5000, 3000)]: mp.prec = inprec pos10001 = mpf(1) + mpf(2)**(-inprec+5) pos09999 = mpf(1) - mpf(2)**(-inprec+5) neg10001 = -pos10001 neg09999 = -pos09999 mp.prec = outprec r = round_up assert powr(pos10001, 5, r) > 1 assert powr(pos09999, 5, r) == 1 assert powr(neg10001, 5, r) < -1 assert powr(neg09999, 5, r) == -1 assert powr(pos10001, 6, r) > 1 assert powr(pos09999, 6, r) == 1 assert powr(neg10001, 6, r) > 1 assert powr(neg09999, 6, r) == 1 assert powr(pos10001, -5, r) == 1 assert powr(pos09999, -5, r) > 1 assert powr(neg10001, -5, r) == -1 assert powr(neg09999, -5, r) < -1 assert powr(pos10001, -6, r) == 1 assert powr(pos09999, -6, r) > 1 assert powr(neg10001, -6, r) == 1 assert powr(neg09999, -6, r) > 1 r = round_down assert powr(pos10001, 5, r) == 1 assert powr(pos09999, 5, r) < 1 assert powr(neg10001, 5, r) == -1 assert powr(neg09999, 5, r) > -1 assert powr(pos10001, 6, r) == 1 assert powr(pos09999, 6, r) < 1 assert powr(neg10001, 6, r) == 1 assert powr(neg09999, 6, r) < 1 assert powr(pos10001, -5, r) < 1 assert powr(pos09999, -5, r) == 1 assert powr(neg10001, -5, r) > -1 assert powr(neg09999, -5, r) == -1 assert powr(pos10001, -6, r) < 1 assert powr(pos09999, -6, r) == 1 assert powr(neg10001, -6, r) < 1 assert powr(neg09999, -6, r) == 1 r = round_ceiling assert powr(pos10001, 5, r) > 1 assert powr(pos09999, 5, r) == 1 assert powr(neg10001, 5, r) == -1 assert powr(neg09999, 5, r) > -1 assert powr(pos10001, 6, r) > 1 assert powr(pos09999, 6, r) == 1 assert powr(neg10001, 6, r) > 1 assert powr(neg09999, 6, r) == 1 assert powr(pos10001, -5, r) == 1 assert powr(pos09999, -5, r) > 1 assert powr(neg10001, -5, r) > -1 assert powr(neg09999, -5, r) == -1 assert powr(pos10001, -6, r) == 1 assert powr(pos09999, -6, r) > 1 assert powr(neg10001, -6, r) == 1 assert powr(neg09999, -6, r) > 1 r = round_floor assert powr(pos10001, 5, r) == 1 assert powr(pos09999, 5, r) < 1 assert powr(neg10001, 5, r) < -1 assert powr(neg09999, 5, r) == -1 assert powr(pos10001, 6, r) == 1 assert powr(pos09999, 6, r) < 1 assert powr(neg10001, 6, r) == 1 assert powr(neg09999, 6, r) < 1 assert powr(pos10001, -5, r) < 1 assert powr(pos09999, -5, r) == 1 assert powr(neg10001, -5, r) == -1 assert powr(neg09999, -5, r) < -1 assert powr(pos10001, -6, r) < 1 assert powr(pos09999, -6, r) == 1 assert powr(neg10001, -6, r) < 1 assert powr(neg09999, -6, r) == 1 mp.dps = 15 sympy-0.7.4.1/sympy/mpmath/tests/test_calculus.py0000644000175000017500000000344112253362407022303 0ustar georgeskgeorgeskfrom sympy.mpmath import * def test_approximation(): mp.dps = 15 f = lambda x: cos(2-2*x)/x p, err = chebyfit(f, [2, 4], 8, error=True) assert err < 1e-5 for i in range(10): x = 2 + i/5. assert abs(polyval(p, x) - f(x)) < err def test_limits(): mp.dps = 15 assert limit(lambda x: (x-sin(x))/x**3, 0).ae(mpf(1)/6) assert limit(lambda n: (1+1/n)**n, inf).ae(e) def test_polyval(): assert polyval([], 3) == 0 assert polyval([0], 3) == 0 assert polyval([5], 3) == 5 # 4x^3 - 2x + 5 p = [4, 0, -2, 5] assert polyval(p,4) == 253 assert polyval(p,4,derivative=True) == (253, 190) def test_polyroots(): p = polyroots([1,-4]) assert p[0].ae(4) p, q = polyroots([1,2,3]) assert p.ae(-1 - sqrt(2)*j) assert q.ae(-1 + sqrt(2)*j) #this is not a real test, it only tests a specific case assert polyroots([1]) == [] try: polyroots([0]) assert False except ValueError: pass def test_pade(): one = mpf(1) mp.dps = 20 N = 10 a = [one] k = 1 for i in range(1, N+1): k *= i a.append(one/k) p, q = pade(a, N//2, N//2) for x in arange(0, 1, 0.1): r = polyval(p[::-1], x)/polyval(q[::-1], x) assert(r.ae(exp(x), 1.0e-10)) mp.dps = 15 def test_fourier(): mp.dps = 15 c, s = fourier(lambda x: x+1, [-1, 2], 2) #plot([lambda x: x+1, lambda x: fourierval((c, s), [-1, 2], x)], [-1, 2]) assert c[0].ae(1.5) assert c[1].ae(-3*sqrt(3)/(2*pi)) assert c[2].ae(3*sqrt(3)/(4*pi)) assert s[0] == 0 assert s[1].ae(3/(2*pi)) assert s[2].ae(3/(4*pi)) assert fourierval((c, s), [-1, 2], 1).ae(1.9134966715663442) def test_differint(): mp.dps = 15 assert differint(lambda t: t, 2, -0.5).ae(8*sqrt(2/pi)/3) sympy-0.7.4.1/sympy/mpmath/tests/test_str.py0000644000175000017500000000104712253362407021300 0ustar georgeskgeorgeskfrom sympy.mpmath import nstr, matrix, inf def test_nstr(): m = matrix([[0.75, 0.190940654, -0.0299195971], [0.190940654, 0.65625, 0.205663228], [-0.0299195971, 0.205663228, 0.64453125e-20]]) assert nstr(m, 4, min_fixed=-inf) == \ '''[ 0.75 0.1909 -0.02992] [ 0.1909 0.6563 0.2057] [-0.02992 0.2057 0.000000000000000000006445]''' assert nstr(m, 4) == \ '''[ 0.75 0.1909 -0.02992] [ 0.1909 0.6563 0.2057] [-0.02992 0.2057 6.445e-21]''' sympy-0.7.4.1/sympy/mpmath/tests/test_ode.py0000644000175000017500000000345212253362407021241 0ustar georgeskgeorgesk#from sympy.mpmath.calculus import ODE_step_euler, ODE_step_rk4, odeint, arange from sympy.mpmath import odefun, cos, sin, mpf, sinc, mp ''' solvers = [ODE_step_euler, ODE_step_rk4] def test_ode1(): """ Let's solve: x'' + w**2 * x = 0 i.e. x1 = x, x2 = x1': x1' = x2 x2' = -x1 """ def derivs((x1, x2), t): return x2, -x1 for solver in solvers: t = arange(0, 3.1415926, 0.005) sol = odeint(derivs, (0., 1.), t, solver) x1 = [a[0] for a in sol] x2 = [a[1] for a in sol] # the result is x1 = sin(t), x2 = cos(t) # let's just check the end points for t = pi assert abs(x1[-1]) < 1e-2 assert abs(x2[-1] - (-1)) < 1e-2 def test_ode2(): """ Let's solve: x' - x = 0 i.e. x = exp(x) """ def derivs((x), t): return x for solver in solvers: t = arange(0, 1, 1e-3) sol = odeint(derivs, (1.,), t, solver) x = [a[0] for a in sol] # the result is x = exp(t) # let's just check the end point for t = 1, i.e. x = e assert abs(x[-1] - 2.718281828) < 1e-2 ''' def test_odefun_rational(): mp.dps = 15 # A rational function f = lambda t: 1/(1+mpf(t)**2) g = odefun(lambda x, y: [-2*x*y[0]**2], 0, [f(0)]) assert f(2).ae(g(2)[0]) def test_odefun_sinc_large(): mp.dps = 15 # Sinc function; test for large x f = sinc g = odefun(lambda x, y: [(cos(x)-y[0])/x], 1, [f(1)], tol=0.01, degree=5) assert abs(f(100) - g(100)[0])/f(100) < 0.01 def test_odefun_harmonic(): mp.dps = 15 # Harmonic oscillator f = odefun(lambda x, y: [-y[1], y[0]], 0, [1, 0]) for x in [0, 1, 2.5, 8, 3.7]: # we go back to 3.7 to check caching c, s = f(x) assert c.ae(cos(x)) assert s.ae(sin(x)) sympy-0.7.4.1/sympy/mpmath/tests/test_convert.py0000644000175000017500000001667212253362407022162 0ustar georgeskgeorgeskimport random from sympy.mpmath import * from sympy.mpmath.libmp import * def test_basic_string(): """ Test basic string conversion """ mp.dps = 15 assert mpf('3') == mpf('3.0') == mpf('0003.') == mpf('0.03e2') == mpf(3.0) assert mpf('30') == mpf('30.0') == mpf('00030.') == mpf(30.0) for i in range(10): for j in range(10): assert mpf('%ie%i' % (i,j)) == i * 10**j assert str(mpf('25000.0')) == '25000.0' assert str(mpf('2500.0')) == '2500.0' assert str(mpf('250.0')) == '250.0' assert str(mpf('25.0')) == '25.0' assert str(mpf('2.5')) == '2.5' assert str(mpf('0.25')) == '0.25' assert str(mpf('0.025')) == '0.025' assert str(mpf('0.0025')) == '0.0025' assert str(mpf('0.00025')) == '0.00025' assert str(mpf('0.000025')) == '2.5e-5' assert str(mpf(0)) == '0.0' assert str(mpf('2.5e1000000000000000000000')) == '2.5e+1000000000000000000000' assert str(mpf('2.6e-1000000000000000000000')) == '2.6e-1000000000000000000000' assert str(mpf(1.23402834e-15)) == '1.23402834e-15' assert str(mpf(-1.23402834e-15)) == '-1.23402834e-15' assert str(mpf(-1.2344e-15)) == '-1.2344e-15' assert repr(mpf(-1.2344e-15)) == "mpf('-1.2343999999999999e-15')" def test_pretty(): mp.pretty = True assert repr(mpf(2.5)) == '2.5' assert repr(mpc(2.5,3.5)) == '(2.5 + 3.5j)' mp.pretty = False iv.pretty = True assert repr(mpi(2.5,3.5)) == '[2.5, 3.5]' iv.pretty = False def test_str_whitespace(): assert mpf('1.26 ') == 1.26 def test_unicode(): mp.dps = 15 try: unicode = unicode except NameError: unicode = str assert mpf(unicode('2.76')) == 2.76 assert mpf(unicode('inf')) == inf def test_str_format(): assert to_str(from_float(0.1),15,strip_zeros=False) == '0.100000000000000' assert to_str(from_float(0.0),15,show_zero_exponent=True) == '0.0e+0' assert to_str(from_float(0.0),0,show_zero_exponent=True) == '.0e+0' assert to_str(from_float(0.0),0,show_zero_exponent=False) == '.0' assert to_str(from_float(0.0),1,show_zero_exponent=True) == '0.0e+0' assert to_str(from_float(0.0),1,show_zero_exponent=False) == '0.0' assert to_str(from_float(1.23),3,show_zero_exponent=True) == '1.23e+0' assert to_str(from_float(1.23456789000000e-2),15,strip_zeros=False,min_fixed=0,max_fixed=0) == '1.23456789000000e-2' assert to_str(from_float(1.23456789000000e+2),15,strip_zeros=False,min_fixed=0,max_fixed=0) == '1.23456789000000e+2' assert to_str(from_float(2.1287e14), 15, max_fixed=1000) == '212870000000000.0' assert to_str(from_float(2.1287e15), 15, max_fixed=1000) == '2128700000000000.0' assert to_str(from_float(2.1287e16), 15, max_fixed=1000) == '21287000000000000.0' assert to_str(from_float(2.1287e30), 15, max_fixed=1000) == '2128700000000000000000000000000.0' def test_tight_string_conversion(): mp.dps = 15 # In an old version, '0.5' wasn't recognized as representing # an exact binary number and was erroneously rounded up or down assert from_str('0.5', 10, round_floor) == fhalf assert from_str('0.5', 10, round_ceiling) == fhalf def test_eval_repr_invariant(): """Test that eval(repr(x)) == x""" random.seed(123) for dps in [10, 15, 20, 50, 100]: mp.dps = dps for i in range(1000): a = mpf(random.random())**0.5 * 10**random.randint(-100, 100) assert eval(repr(a)) == a mp.dps = 15 def test_str_bugs(): mp.dps = 15 # Decimal rounding used to give the wrong exponent in some cases assert str(mpf('1e600')) == '1.0e+600' assert str(mpf('1e10000')) == '1.0e+10000' def test_str_prec0(): assert to_str(from_float(1.234), 0) == '.0e+0' assert to_str(from_float(1e-15), 0) == '.0e-15' assert to_str(from_float(1e+15), 0) == '.0e+15' assert to_str(from_float(-1e-15), 0) == '-.0e-15' assert to_str(from_float(-1e+15), 0) == '-.0e+15' def test_convert_rational(): mp.dps = 15 assert from_rational(30, 5, 53, round_nearest) == (0, 3, 1, 2) assert from_rational(-7, 4, 53, round_nearest) == (1, 7, -2, 3) assert to_rational((0, 1, -1, 1)) == (1, 2) def test_custom_class(): class mympf: @property def _mpf_(self): return mpf(3.5)._mpf_ class mympc: @property def _mpc_(self): return mpf(3.5)._mpf_, mpf(2.5)._mpf_ assert mpf(2) + mympf() == 5.5 assert mympf() + mpf(2) == 5.5 assert mpf(mympf()) == 3.5 assert mympc() + mpc(2) == mpc(5.5, 2.5) assert mpc(2) + mympc() == mpc(5.5, 2.5) assert mpc(mympc()) == (3.5+2.5j) def test_conversion_methods(): class SomethingRandom: pass class SomethingReal: def _mpmath_(self, prec, rounding): return mp.make_mpf(from_str('1.3', prec, rounding)) class SomethingComplex: def _mpmath_(self, prec, rounding): return mp.make_mpc((from_str('1.3', prec, rounding), \ from_str('1.7', prec, rounding))) x = mpf(3) z = mpc(3) a = SomethingRandom() y = SomethingReal() w = SomethingComplex() for d in [15, 45]: mp.dps = d assert (x+y).ae(mpf('4.3')) assert (y+x).ae(mpf('4.3')) assert (x+w).ae(mpc('4.3', '1.7')) assert (w+x).ae(mpc('4.3', '1.7')) assert (z+y).ae(mpc('4.3')) assert (y+z).ae(mpc('4.3')) assert (z+w).ae(mpc('4.3', '1.7')) assert (w+z).ae(mpc('4.3', '1.7')) x-y y-x x-w w-x z-y y-z z-w w-z x*y y*x x*w w*x z*y y*z z*w w*z x/y y/x x/w w/x z/y y/z z/w w/z x**y y**x x**w w**x z**y y**z z**w w**z x==y y==x x==w w==x z==y y==z z==w w==z mp.dps = 15 assert x.__add__(a) is NotImplemented assert x.__radd__(a) is NotImplemented assert x.__lt__(a) is NotImplemented assert x.__gt__(a) is NotImplemented assert x.__le__(a) is NotImplemented assert x.__ge__(a) is NotImplemented assert x.__eq__(a) is NotImplemented assert x.__ne__(a) is NotImplemented # implementation detail if hasattr(x, "__cmp__"): assert x.__cmp__(a) is NotImplemented assert x.__sub__(a) is NotImplemented assert x.__rsub__(a) is NotImplemented assert x.__mul__(a) is NotImplemented assert x.__rmul__(a) is NotImplemented assert x.__div__(a) is NotImplemented assert x.__rdiv__(a) is NotImplemented assert x.__mod__(a) is NotImplemented assert x.__rmod__(a) is NotImplemented assert x.__pow__(a) is NotImplemented assert x.__rpow__(a) is NotImplemented assert z.__add__(a) is NotImplemented assert z.__radd__(a) is NotImplemented assert z.__eq__(a) is NotImplemented assert z.__ne__(a) is NotImplemented assert z.__sub__(a) is NotImplemented assert z.__rsub__(a) is NotImplemented assert z.__mul__(a) is NotImplemented assert z.__rmul__(a) is NotImplemented assert z.__div__(a) is NotImplemented assert z.__rdiv__(a) is NotImplemented assert z.__pow__(a) is NotImplemented assert z.__rpow__(a) is NotImplemented def test_mpmathify(): assert mpmathify('1/2') == 0.5 assert mpmathify('(1.0+1.0j)') == mpc(1, 1) assert mpmathify('(1.2e-10 - 3.4e5j)') == mpc('1.2e-10', '-3.4e5') assert mpmathify('1j') == mpc(1j) sympy-0.7.4.1/sympy/mpmath/tests/test_compatibility.py0000644000175000017500000000441012253362407023336 0ustar georgeskgeorgeskfrom sympy.mpmath import * from random import seed, randint, random import math # Test compatibility with Python floats, which are # IEEE doubles (53-bit) N = 5000 seed(1) # Choosing exponents between roughly -140, 140 ensures that # the Python floats don't overflow or underflow xs = [(random()-1) * 10**randint(-140, 140) for x in range(N)] ys = [(random()-1) * 10**randint(-140, 140) for x in range(N)] # include some equal values ys[int(N*0.8):] = xs[int(N*0.8):] # Detect whether Python is compiled to use 80-bit floating-point # instructions, in which case the double compatibility test breaks uses_x87 = -4.1974624032366689e+117 / -8.4657370748010221e-47 \ == 4.9581771393902231e+163 def test_double_compatibility(): mp.prec = 53 for x, y in zip(xs, ys): mpx = mpf(x) mpy = mpf(y) assert mpf(x) == x assert (mpx < mpy) == (x < y) assert (mpx > mpy) == (x > y) assert (mpx == mpy) == (x == y) assert (mpx != mpy) == (x != y) assert (mpx <= mpy) == (x <= y) assert (mpx >= mpy) == (x >= y) assert mpx == mpx if uses_x87: mp.prec = 64 a = mpx + mpy b = mpx * mpy c = mpx / mpy d = mpx % mpy mp.prec = 53 assert +a == x + y assert +b == x * y assert +c == x / y assert +d == x % y else: assert mpx + mpy == x + y assert mpx * mpy == x * y assert mpx / mpy == x / y assert mpx % mpy == x % y assert abs(mpx) == abs(x) assert mpf(repr(x)) == x assert ceil(mpx) == math.ceil(x) assert floor(mpx) == math.floor(x) def test_sqrt(): # this fails quite often. it appers to be float # that rounds the wrong way, not mpf fail = 0 mp.prec = 53 for x in xs: x = abs(x) mp.prec = 100 mp_high = mpf(x)**0.5 mp.prec = 53 mp_low = mpf(x)**0.5 fp = x**0.5 assert abs(mp_low-mp_high) <= abs(fp-mp_high) fail += mp_low != fp assert fail < N/10 def test_bugs(): # particular bugs assert mpf(4.4408920985006262E-16) < mpf(1.7763568394002505E-15) assert mpf(-4.4408920985006262E-16) > mpf(-1.7763568394002505E-15) sympy-0.7.4.1/sympy/mpmath/tests/test_linalg.py0000644000175000017500000001672412253362407021746 0ustar georgeskgeorgesk# TODO: don't use round from __future__ import division from sympy.mpmath import * # XXX: these shouldn't be visible(?) LU_decomp = mp.LU_decomp L_solve = mp.L_solve U_solve = mp.U_solve householder = mp.householder improve_solution = mp.improve_solution A1 = matrix([[3, 1, 6], [2, 1, 3], [1, 1, 1]]) b1 = [2, 7, 4] A2 = matrix([[ 2, -1, -1, 2], [ 6, -2, 3, -1], [-4, 2, 3, -2], [ 2, 0, 4, -3]]) b2 = [3, -3, -2, -1] A3 = matrix([[ 1, 0, -1, -1, 0], [ 0, 1, 1, 0, -1], [ 4, -5, 2, 0, 0], [ 0, 0, -2, 9,-12], [ 0, 5, 0, 0, 12]]) b3 = [0, 0, 0, 0, 50] A4 = matrix([[10.235, -4.56, 0., -0.035, 5.67], [-2.463, 1.27, 3.97, -8.63, 1.08], [-6.58, 0.86, -0.257, 9.32, -43.6 ], [ 9.83, 7.39, -17.25, 0.036, 24.86], [-9.31, 34.9, 78.56, 1.07, 65.8 ]]) b4 = [8.95, 20.54, 7.42, 5.60, 58.43] A5 = matrix([[ 1, 2, -4], [-2, -3, 5], [ 3, 5, -8]]) A6 = matrix([[ 1.377360, 2.481400, 5.359190], [ 2.679280, -1.229560, 25.560210], [-1.225280+1.e6, 9.910180, -35.049900-1.e6]]) b6 = [23.500000, -15.760000, 2.340000] A7 = matrix([[1, -0.5], [2, 1], [-2, 6]]) b7 = [3, 2, -4] A8 = matrix([[1, 2, 3], [-1, 0, 1], [-1, -2, -1], [1, 0, -1]]) b8 = [1, 2, 3, 4] A9 = matrix([[ 4, 2, -2], [ 2, 5, -4], [-2, -4, 5.5]]) b9 = [10, 16, -15.5] A10 = matrix([[1.0 + 1.0j, 2.0, 2.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]) b10 = [1.0, 1.0 + 1.0j, 1.0] def test_LU_decomp(): A = A3.copy() b = b3 A, p = LU_decomp(A) y = L_solve(A, b, p) x = U_solve(A, y) assert p == [2, 1, 2, 3] assert [round(i, 14) for i in x] == [3.78953107960742, 2.9989094874591098, -0.081788440567070006, 3.8713195201744801, 2.9171210468920399] A = A4.copy() b = b4 A, p = LU_decomp(A) y = L_solve(A, b, p) x = U_solve(A, y) assert p == [0, 3, 4, 3] assert [round(i, 14) for i in x] == [2.6383625899619201, 2.6643834462368399, 0.79208015947958998, -2.5088376454101899, -1.0567657691375001] A = randmatrix(3) bak = A.copy() LU_decomp(A, overwrite=1) assert A != bak def test_inverse(): for A in [A1, A2, A5]: inv = inverse(A) assert mnorm(A*inv - eye(A.rows), 1) < 1.e-14 def test_householder(): mp.dps = 15 A, b = A8, b8 H, p, x, r = householder(extend(A, b)) assert H == matrix( [[mpf('3.0'), mpf('-2.0'), mpf('-1.0'), 0], [-1.0,mpf('3.333333333333333'),mpf('-2.9999999999999991'),mpf('2.0')], [-1.0, mpf('-0.66666666666666674'),mpf('2.8142135623730948'), mpf('-2.8284271247461898')], [1.0, mpf('-1.3333333333333333'),mpf('-0.20000000000000018'), mpf('4.2426406871192857')]]) assert p == [-2, -2, mpf('-1.4142135623730949')] assert round(norm(r, 2), 10) == 4.2426406870999998 y = [102.102, 58.344, 36.463, 24.310, 17.017, 12.376, 9.282, 7.140, 5.610, 4.488, 3.6465, 3.003] def coeff(n): # similiar to Hilbert matrix A = [] for i in range(1, 13): A.append([1. / (i + j - 1) for j in range(1, n + 1)]) return matrix(A) residuals = [] refres = [] for n in range(2, 7): A = coeff(n) H, p, x, r = householder(extend(A, y)) x = matrix(x) y = matrix(y) residuals.append(norm(r, 2)) refres.append(norm(residual(A, x, y), 2)) assert [round(res, 10) for res in residuals] == [15.1733888877, 0.82378073210000002, 0.302645887, 0.0260109244, 0.00058653999999999998] assert norm(matrix(residuals) - matrix(refres), inf) < 1.e-13 def test_factorization(): A = randmatrix(5) P, L, U = lu(A) assert mnorm(P*A - L*U, 1) < 1.e-15 def test_solve(): assert norm(residual(A6, lu_solve(A6, b6), b6), inf) < 1.e-10 assert norm(residual(A7, lu_solve(A7, b7), b7), inf) < 1.5 assert norm(residual(A8, lu_solve(A8, b8), b8), inf) <= 3 + 1.e-10 assert norm(residual(A6, qr_solve(A6, b6)[0], b6), inf) < 1.e-10 assert norm(residual(A7, qr_solve(A7, b7)[0], b7), inf) < 1.5 assert norm(residual(A8, qr_solve(A8, b8)[0], b8), 2) <= 4.3 assert norm(residual(A10, lu_solve(A10, b10), b10), 2) < 1.e-10 assert norm(residual(A10, qr_solve(A10, b10)[0], b10), 2) < 1.e-10 def test_solve_overdet_complex(): A = matrix([[1, 2j], [3, 4j], [5, 6]]) b = matrix([1 + j, 2, -j]) assert norm(residual(A, lu_solve(A, b), b)) < 1.0208 def test_singular(): mp.dps = 15 A = [[5.6, 1.2], [7./15, .1]] B = repr(zeros(2)) b = [1, 2] def _assert_ZeroDivisionError(statement): try: eval(statement) assert False except (ZeroDivisionError, ValueError): pass for i in ['lu_solve(%s, %s)' % (A, b), 'lu_solve(%s, %s)' % (B, b), 'qr_solve(%s, %s)' % (A, b), 'qr_solve(%s, %s)' % (B, b)]: _assert_ZeroDivisionError(i) def test_cholesky(): assert fp.cholesky(fp.matrix(A9)) == fp.matrix([[2, 0, 0], [1, 2, 0], [-1, -3/2, 3/2]]) x = fp.cholesky_solve(A9, b9) assert fp.norm(fp.residual(A9, x, b9), fp.inf) == 0 def test_det(): assert det(A1) == 1 assert round(det(A2), 14) == 8 assert round(det(A3)) == 1834 assert round(det(A4)) == 4443376 assert det(A5) == 1 assert round(det(A6)) == 78356463 assert det(zeros(3)) == 0 def test_cond(): mp.dps = 15 A = matrix([[1.2969, 0.8648], [0.2161, 0.1441]]) assert cond(A, lambda x: mnorm(x,1)) == mpf('327065209.73817754') assert cond(A, lambda x: mnorm(x,inf)) == mpf('327065209.73817754') assert cond(A, lambda x: mnorm(x,'F')) == mpf('249729266.80008656') @extradps(50) def test_precision(): A = randmatrix(10, 10) assert mnorm(inverse(inverse(A)) - A, 1) < 1.e-45 def test_interval_matrix(): mp.dps = 15 iv.dps = 15 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) assert c[0].delta < 1e-13 assert c[1].delta < 1e-13 assert c[2].delta < 1e-13 assert 5.25823271130625686059275 in c[0] assert -13.155049396267837541163 in c[1] assert 7.42069154774972557628979 in c[2] def test_LU_cache(): A = randmatrix(3) LU = LU_decomp(A) assert A._LU == LU_decomp(A) A[0,0] = -1000 assert A._LU is None def test_improve_solution(): A = randmatrix(5, min=1e-20, max=1e20) b = randmatrix(5, 1, min=-1000, max=1000) x1 = lu_solve(A, b) + randmatrix(5, 1, min=-1e-5, max=1.e-5) x2 = improve_solution(A, x1, b) assert norm(residual(A, x2, b), 2) < norm(residual(A, x1, b), 2) def test_exp_pade(): for i in range(3): dps = 15 extra = 15 mp.dps = dps + extra dm = 0 N = 3 dg = range(1,N+1) a = diag(dg) expa = diag([exp(x) for x in dg]) # choose a random matrix not close to be singular # to avoid adding too much extra precision in computing # m**-1 * M * m while abs(dm) < 0.01: m = randmatrix(N) dm = det(m) m = m/dm a1 = m**-1 * a * m e2 = m**-1 * expa * m mp.dps = dps e1 = expm(a1, method='pade') mp.dps = dps + extra d = e2 - e1 #print d mp.dps = dps assert norm(d, inf).ae(0) mp.dps = 15 sympy-0.7.4.1/sympy/mpmath/tests/test_identify.py0000644000175000017500000000127212253362407022303 0ustar georgeskgeorgeskfrom sympy.mpmath import * def test_pslq(): mp.dps = 15 assert pslq([3*pi+4*e/7, pi, e, log(2)]) == [7, -21, -4, 0] assert pslq([4.9999999999999991, 1]) == [1, -5] assert pslq([2,1]) == [1, -2] def test_identify(): mp.dps = 20 assert identify(zeta(4), ['log(2)', 'pi**4']) == '((1/90)*pi**4)' mp.dps = 15 assert identify(exp(5)) == 'exp(5)' assert identify(exp(4)) == 'exp(4)' assert identify(log(5)) == 'log(5)' assert identify(exp(3*pi), ['pi']) == 'exp((3*pi))' assert identify(3, full=True) == ['3', '3', '1/(1/3)', 'sqrt(9)', '1/sqrt((1/9))', '(sqrt(12)/2)**2', '1/(sqrt(12)/6)**2'] assert identify(pi+1, {'a':+pi}) == '(1 + 1*a)' sympy-0.7.4.1/sympy/mpmath/tests/test_diff.py0000644000175000017500000000417512253362407021405 0ustar georgeskgeorgeskfrom sympy.mpmath import * def test_diff(): mp.dps = 15 assert diff(log, 2.0, n=0).ae(log(2)) assert diff(cos, 1.0).ae(-sin(1)) assert diff(abs, 0.0) == 0 assert diff(abs, 0.0, direction=1) == 1 assert diff(abs, 0.0, direction=-1) == -1 assert diff(exp, 1.0).ae(e) assert diff(exp, 1.0, n=5).ae(e) assert diff(exp, 2.0, n=5, direction=3*j).ae(e**2) assert diff(lambda x: x**2, 3.0, method='quad').ae(6) assert diff(lambda x: 3+x**5, 3.0, n=2, method='quad').ae(540) assert diff(lambda x: 3+x**5, 3.0, n=2, method='step').ae(540) assert diffun(sin)(2).ae(cos(2)) assert diffun(sin, n=2)(2).ae(-sin(2)) def test_taylor(): mp.dps = 15 # Easy to test since the coefficients are exact in floating-point assert taylor(sqrt, 1, 4) == [1, 0.5, -0.125, 0.0625, -0.0390625] def test_diff_partial(): mp.dps = 15 x,y,z = xyz = 2,3,7 f = lambda x,y,z: 3*x**2 * (y+2)**3 * z**5 assert diff(f, xyz, (0,0,0)).ae(25210500) assert diff(f, xyz, (0,0,1)).ae(18007500) assert diff(f, xyz, (0,0,2)).ae(10290000) assert diff(f, xyz, (0,1,0)).ae(15126300) assert diff(f, xyz, (0,1,1)).ae(10804500) assert diff(f, xyz, (0,1,2)).ae(6174000) assert diff(f, xyz, (0,2,0)).ae(6050520) assert diff(f, xyz, (0,2,1)).ae(4321800) assert diff(f, xyz, (0,2,2)).ae(2469600) assert diff(f, xyz, (1,0,0)).ae(25210500) assert diff(f, xyz, (1,0,1)).ae(18007500) assert diff(f, xyz, (1,0,2)).ae(10290000) assert diff(f, xyz, (1,1,0)).ae(15126300) assert diff(f, xyz, (1,1,1)).ae(10804500) assert diff(f, xyz, (1,1,2)).ae(6174000) assert diff(f, xyz, (1,2,0)).ae(6050520) assert diff(f, xyz, (1,2,1)).ae(4321800) assert diff(f, xyz, (1,2,2)).ae(2469600) assert diff(f, xyz, (2,0,0)).ae(12605250) assert diff(f, xyz, (2,0,1)).ae(9003750) assert diff(f, xyz, (2,0,2)).ae(5145000) assert diff(f, xyz, (2,1,0)).ae(7563150) assert diff(f, xyz, (2,1,1)).ae(5402250) assert diff(f, xyz, (2,1,2)).ae(3087000) assert diff(f, xyz, (2,2,0)).ae(3025260) assert diff(f, xyz, (2,2,1)).ae(2160900) assert diff(f, xyz, (2,2,2)).ae(1234800) sympy-0.7.4.1/sympy/mpmath/tests/test_mpmath.py0000644000175000017500000000011212253362407021746 0ustar georgeskgeorgeskfrom sympy.mpmath.libmp import * from sympy.mpmath import * import random sympy-0.7.4.1/sympy/mpmath/tests/__init__.py0000644000175000017500000000000112253362407021155 0ustar georgeskgeorgesk sympy-0.7.4.1/sympy/mpmath/__init__.py0000644000175000017500000002004512253362407020025 0ustar georgeskgeorgesk__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() sympy-0.7.4.1/sympy/series/0000755000175000017500000000000012253362407015717 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/series/acceleration.py0000644000175000017500000000630712253362407020730 0ustar georgeskgeorgesk""" 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 __future__ import print_function, division from sympy import factorial, Integer, S 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 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] sympy-0.7.4.1/sympy/series/residues.py0000644000175000017500000000454612253362407020125 0ustar georgeskgeorgesk""" This module implements the Residue function and related tools for working with residues. """ from __future__ import print_function, division from sympy import Wild, sympify, Integer, Add from sympy.utilities.timeutils import timethis @timethis('residue') 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 sympy-0.7.4.1/sympy/series/limits.py0000644000175000017500000001107312253362407017574 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import S, Symbol, Add, sympify, Expr, PoleError, Mul, oo, C from sympy.core.compatibility import string_types from sympy.functions import tan, cot, factorial, gamma from .gruntz import gruntz 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). """ e = sympify(e) z = sympify(z) z0 = sympify(z0) if e == z: return z0 if not e.has(z): return e # gruntz fails on factorials but works with the gamma function # If no factorial term is present, e should remain unchanged. # factorial is defined to be zero for negative inputs (which # differs from gamma) so only rewrite for positive z0. if z0.is_positive: e = e.rewrite(factorial, gamma) if e.is_Mul: if abs(z0) is S.Infinity: # 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 e.as_numer_denom()): u = C.Dummy(positive=(z0 is S.Infinity)) inve = e.subs(z, 1/u) return limit(inve.as_leading_term(u), u, S.Zero, "+" if z0 is S.Infinity else "-") if e.is_Add: if e.is_rational_function(z): rval = Add(*[limit(term, z, z0, dir) for term in e.args]) if rval != S.NaN: return rval if e.is_Order: return C.Order(limit(e.expr, z, z0), *e.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.NaN, None) if e.is_Mul or e.is_Add or e.is_Pow or e.is_Function: r = [] for a in e.args: try: r.append(limit(a, z, z0, dir)) except PoleError: break if r[-1] in bad: break else: if r: rv = e.func(*r) 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 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, string_types): 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 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)) sympy-0.7.4.1/sympy/series/gruntz.py0000644000175000017500000005005312253362407017625 0ustar georgeskgeorgesk""" 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 e0oo, 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 __future__ import print_function, division 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 sympy.utilities.timeutils import timethis timeit = timethis('gruntz') from sympy.utilities.misc import debug_decorator as debug def compare(a, b, x): """Returns "<" if a" 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 "=" 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 def meets(self, s2): """Tell whether or not self and s2 have non-empty intersection""" return set(self.keys()).intersection(list(s2.keys())) != set() 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 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) 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 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 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 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 e.has(Order): e = e.expand().removeO() 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 def calculate_series(e, x, 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. """ from sympy.polys import cancel for t in e.lseries(x, logx=logx): t = cancel(t) if t: break return t @debug @timeit @cacheit 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) return series.leadterm(w) 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 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 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) # make sure we know the sign of each exp() term; after the loop, # g is going to be the "w" - the simplest one in the mrv set for g, _ in Omega: sig = sign(g.args[0], x) if sig != 1 and sig != -1: raise NotImplementedError('Result depends on the sign of %s' % sig) if sig == 1: wsym = 1/wsym # if g goes to oo, substitute 1/w #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 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) sympy-0.7.4.1/sympy/series/order.py0000644000175000017500000003256012253362407017412 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import Basic, S, sympify, Expr, Rational, Symbol from sympy.core import Add, Mul, expand_power_base, expand_log from sympy.core.cache import cacheit from sympy.core.compatibility import default_sort_key, is_sequence from sympy.core.containers import Tuple from sympy.utilities.iterables import uniq class Order(Expr): r""" 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 all limit points to be 0 or positive infinity 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} \sup |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, oo >>> from sympy.abc import x, y >>> O(x + x**2) O(x) >>> O(x + x**2, (x, 0)) O(x) >>> O(x + x**2, (x, oo)) O(x**2, (x, oo)) >>> O(1 + x*y) O(1, x, y) >>> O(1 + x*y, (x, 0), (y, 0)) O(1, x, y) >>> O(1 + x*y, (x, oo), (y, oo)) O(x*y, (x, oo), (y, oo)) >>> O(1) in O(1, x) True >>> O(1, x) in O(1) False >>> O(x) in O(1, x) True >>> O(x**2) in O(x) True >>> O(x)*x O(x**2) >>> O(x) - O(x) O(x) References ========== .. [1] `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, *args, **kwargs): expr = sympify(expr) if not args: if expr.is_Order: variables = expr.variables point = expr.point else: variables = list(expr.free_symbols) point = [S.Zero]*len(variables) else: args = list(args if is_sequence(args) else [args]) variables, point = [], [] if is_sequence(args[0]): for a in args: v, p = list(map(sympify, a)) variables.append(v) point.append(p) else: variables = list(map(sympify, args)) point = [S.Zero]*len(variables) if not all(isinstance(v, Symbol) for v in variables): raise TypeError('Variables are not symbols, got %s' % variables) if len(list(uniq(variables))) != len(variables): raise ValueError('Variables are supposed to be unique symbols, got %s' % variables) if expr.is_Order: expr_vp = dict(expr.args[1:]) new_vp = dict(expr_vp) vp = dict(zip(variables, point)) for v, p in vp.items(): if v in new_vp.keys(): if p != new_vp[v]: raise NotImplementedError( "Mixing Order at different points is not supported.") else: new_vp[v] = p if set(expr_vp.keys()) == set(new_vp.keys()): return expr else: variables = list(new_vp.keys()) point = [new_vp[v] for v in variables] if expr is S.NaN: return S.NaN if not all(p is S.Zero for p in point) and \ not all(p is S.Infinity for p in point): raise NotImplementedError('Order at points other than 0 ' 'or oo not supported, got %s as a point.' % point) if variables: if len(variables) > 1: # XXX: better way? We need this expand() to # workaround e.g: expr = x*(x + y). # (x*(x + y)).as_leading_term(x, y) currently returns # x*y (wrong order term!). That's why we want to deal with # expand()'ed expr (handled in "if expr.is_Add" branch below). expr = expr.expand() if expr.is_Add: lst = expr.extract_leading_order(variables, point) expr = Add(*[f.expr for (e, f) in lst]) elif expr: if point[0] == S.Zero: expr = expr.as_leading_term(*variables) expr = expr.as_independent(*variables, as_Add=False)[1] expr = expand_power_base(expr) expr = expand_log(expr) if len(variables) == 1: # The definition of O(f(x)) symbol explicitly stated that # the argument of f(x) is irrelevant. That's why we can # combine some power exponents (only "on top" of the # expression tree for f(x)), e.g.: # x**p * (-x)**q -> x**(p+q) for real p, q. x = variables[0] margs = list(Mul.make_args( expr.as_independent(x, as_Add=False)[1])) for i, t in enumerate(margs): if t.is_Pow: b, q = t.args if b in (x, -x) and q.is_real and not q.has(x): margs[i] = x**q elif b.is_Pow and not b.exp.has(x): b, r = b.args if b in (x, -x) and r.is_real: margs[i] = x**(r*q) elif b.is_Mul and b.args[0] is S.NegativeOne: b = -b if b.is_Pow and not b.exp.has(x): b, r = b.args if b in (x, -x) and r.is_real: margs[i] = x**(r*q) expr = Mul(*margs) if expr is S.Zero: return expr if expr.is_Order: expr = expr.expr if not expr.has(*variables): expr = S.One # create Order instance: variables.sort(key=default_sort_key) args = (expr,) + Tuple(*zip(variables, point)) obj = Expr.__new__(cls, *args) 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): if self.args[1:]: return tuple(x[0] for x in self.args[1:]) else: return () @property def point(self): if self.args[1:]: return tuple(x[1] for x in self.args[1:]) else: return () @property def free_symbols(self): return self.expr.free_symbols | set(self.variables) def _eval_power(b, e): if e.is_Number and e.is_nonnegative: return b.func(b.expr ** e, *b.args[1:]) return def as_expr_variables(self, order_symbols): if order_symbols is None: order_symbols = self.args[1:] else: if not all(o[1] == order_symbols[0][1] for o in order_symbols) and \ not all(p == self.point[0] for p in self.point): raise NotImplementedError('Order at points other than 0 ' 'or oo not supported, got %s as a point.' % point) if order_symbols[0][1] != self.point[0]: raise NotImplementedError( "Multiplying Order at different points is not supported.") order_symbols = dict(order_symbols) for s, p in dict(self.args[1:]).items(): if s not in order_symbols.keys(): order_symbols[s] = p order_symbols = sorted(order_symbols.items(), key=lambda x: default_sort_key(x[0])) return self.expr, tuple(order_symbols) def removeO(self): return S.Zero def getO(self): return self @cacheit 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). """ from sympy import powsimp, limit if expr is S.Zero: return True if expr is S.NaN: return False if expr.is_Order: if not all(p == expr.point[0] for p in expr.point) and \ not all(p == self.point[0] for p in self.point): raise NotImplementedError('Order at points other than 0 ' 'or oo not supported, got %s as a point.' % point) else: # self and/or expr is O(1): if any(not p for p in [expr.point, self.point]): point = self.point + expr.point if point: point = point[0] else: point = S.Zero else: point = self.point[0] if expr.expr == self.expr: # O(1) + O(1), O(1) + O(1, x), etc. return all([x in self.args[1:] for x in expr.args[1:]]) if expr.expr.is_Add: return all([self.contains(x) for x in expr.expr.args]) if self.expr.is_Add: return any([self.func(x, *self.args[1:]).contains(expr) for x in self.expr.args]) 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: return None r = None ratio = self.expr/expr.expr ratio = powsimp(ratio, deep=True, combine='exp') for s in common_symbols: l = limit(ratio, s, point) != 0 if r is None: r = l else: if r != l: return return r obj = self.func(expr, *self.args[1:]) return self.contains(obj) def __contains__(self, other): result = self.contains(other) if result is None: raise TypeError('contains did not evaluate to a bool') return result def _eval_subs(self, old, new): if old.is_Symbol and old in self.variables: i = self.variables.index(old) newexpr = self.expr._subs(old, new) if isinstance(new, Symbol): newvars = list(self.variables) newvars[i] = new newpt = self.point else: newvars = tuple(newexpr.free_symbols) + \ self.variables[:i] + self.variables[i + 1:] newpt = self.point[0]**(new.as_numer_denom()[1].is_number*2 - 1) newpt = [newpt]*len(newvars) return Order(newexpr, *zip(newvars, newpt)) return Order(self.expr._subs(old, new), *self.args[1:]) def _eval_conjugate(self): expr = self.expr._eval_conjugate() if expr is not None: return self.func(expr, *self.args[1:]) def _eval_derivative(self, x): return self.func(self.expr.diff(x), *self.args[1:]) or self def _eval_transpose(self): expr = self.expr._eval_transpose() if expr is not None: return self.func(expr, *self.args[1:]) def _sage_(self): #XXX: SAGE doesn't have Order yet. Let's return 0 instead. return Rational(0)._sage_() O = Order sympy-0.7.4.1/sympy/series/benchmarks/0000755000175000017500000000000012253362407020034 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/series/benchmarks/bench_limit.py0000644000175000017500000000022512253362407022662 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import Symbol, limit, oo x = Symbol('x') def timeit_limit_1x(): limit(1/x, x, oo) sympy-0.7.4.1/sympy/series/kauers.py0000644000175000017500000000352412253362407017567 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import expand from sympy import diff from sympy import Sum def finite_diff(expression, variable, increment=1): """ Takes as input a polynomial expression and the variable used to construct it and returns the difference between function's value when the input is incremented to 1 and the original function value. If you want an increment other than one supply it as a third argument. Examples ========= >>> from sympy.abc import x, y, z, k, n >>> from sympy.series.kauers import finite_diff >>> from sympy import Sum >>> finite_diff(x**2, x) 2*x + 1 >>> finite_diff(y**3 + 2*y**2 + 3*y + 4, y) 3*y**2 + 7*y + 6 >>> finite_diff(x**2 + 3*x + 8, x, 2) 4*x + 10 >>> finite_diff(z**3 + 8*z, z, 3) 9*z**2 + 27*z + 51 """ expression = expression.expand() expression2 = expression.subs(variable, variable + increment) expression2 = expression2.expand() return expression2 - expression def finite_diff_kauers(sum): """ Takes as input a Sum instance and returns the difference between the sum with the upper index incremented by 1 and the original sum. For example, if S(n) is a sum, then finite_diff_kauers will return S(n + 1) - S(n). Examples ======== >>> from sympy.series.kauers import finite_diff_kauers >>> from sympy import Sum >>> from sympy.abc import x, y, m, n, k >>> finite_diff_kauers(Sum(k, (k, 1, n))) n + 1 >>> finite_diff_kauers(Sum(1/k, (k, 1, n))) 1/(n + 1) >>> finite_diff_kauers(Sum((x*y**2), (x, 1, n), (y, 1, m))) (m + 1)**2*(n + 1) >>> finite_diff_kauers(Sum((x*y), (x, 1, m), (y, 1, n))) (m + 1)*(n + 1) """ function = sum.function for l in sum.limits: function = function.subs(l[0], l[- 1] + 1) return function sympy-0.7.4.1/sympy/series/series.py0000644000175000017500000000052312253362407017563 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core.sympify import sympify 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) sympy-0.7.4.1/sympy/series/tests/0000755000175000017500000000000012253362407017061 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/series/tests/test_limits.py0000644000175000017500000003277712253362407022013 0ustar georgeskgeorgeskfrom itertools import product as cartes from sympy import (limit, exp, oo, log, sqrt, Limit, sin, floor, cos, ceiling, atan, gamma, Symbol, S, pi, Integral, cot, Rational, I, zoo, tan, cot, integrate, Sum, sign) from sympy.series.limits import heuristics from sympy.series.order import Order from sympy.abc import x, y, z from sympy.utilities.pytest import XFAIL, raises def test_basic1(): assert limit(x, x, oo) == oo assert limit(x, x, -oo) == -oo assert limit(-x, x, oo) == -oo assert limit(x**2, x, -oo) == oo assert limit(-x**2, x, oo) == -oo assert limit(x*log(x), x, 0, dir="+") == 0 assert limit(1/x, x, oo) == 0 assert limit(exp(x), x, oo) == oo assert limit(-exp(x), x, oo) == -oo assert limit(exp(x)/x, x, oo) == oo assert limit(1/x - exp(-x), x, oo) == 0 assert limit(x + 1/x, x, oo) == oo assert limit(x - x**2, x, oo) == -oo assert limit((1 + x)**(1 + sqrt(2)), x, 0) == 1 assert limit((1 + x)**oo, x, 0) == oo assert limit((1 + x)**oo, x, 0, dir='-') == 0 assert limit((1 + x + y)**oo, x, 0, dir='-') == (1 + y)**(oo) assert limit(y/x/log(x), x, 0) == -oo*sign(y) assert limit(cos(x + y)/x, x, 0) == sign(cos(y))*oo raises(NotImplementedError, lambda: limit(Sum(1/x, (x, 1, y)) - log(y), y, oo)) raises(NotImplementedError, lambda: limit(Sum(1/x, (x, 1, y)) - 1/y, y, oo)) assert limit(gamma(1/x + 3), x, oo) == 2 assert limit(S.NaN, x, -oo) == S.NaN assert limit(Order(2)*x, x, S.NaN) == S.NaN assert limit(gamma(1/x + 3), x, oo) == 2 assert limit(S.NaN, x, -oo) == S.NaN assert limit(Order(2)*x, x, S.NaN) == S.NaN assert limit(1/(x - 1), x, 1, dir="+") == oo assert limit(1/(x - 1), x, 1, dir="-") == -oo assert limit(1/(5 - x)**3, x, 5, dir="+") == -oo assert limit(1/(5 - x)**3, x, 5, dir="-") == oo assert limit(1/sin(x), x, pi, dir="+") == -oo assert limit(1/sin(x), x, pi, dir="-") == oo assert limit(1/cos(x), x, pi/2, dir="+") == -oo assert limit(1/cos(x), x, pi/2, dir="-") == oo assert limit(1/tan(x**3), x, (2*pi)**(S(1)/3), dir="+") == oo assert limit(1/tan(x**3), x, (2*pi)**(S(1)/3), dir="-") == -oo assert limit(1/cot(x)**3, x, (3*pi/2), dir="+") == -oo assert limit(1/cot(x)**3, x, (3*pi/2), dir="-") == oo # approaching 0 # from dir="+" assert limit(1 + 1/x, x, 0) == oo # from dir='-' # Add assert limit(1 + 1/x, x, 0, dir='-') == -oo # Pow assert limit(x**(-2), x, 0, dir='-') == oo assert limit(x**(-3), x, 0, dir='-') == -oo assert limit(1/sqrt(x), x, 0, dir='-') == (-oo)*I assert limit(x**2, x, 0, dir='-') == 0 assert limit(sqrt(x), x, 0, dir='-') == 0 assert limit((1 + cos(x))**oo, x, 0) == oo @XFAIL def test_basic1_xfail(): assert limit(x**-pi, x, 0, dir='-') == zoo def test_basic2(): assert limit(x**x, x, 0, dir="+") == 1 assert limit((exp(x) - 1)/x, x, 0) == 1 assert limit(1 + 1/x, x, oo) == 1 assert limit(-exp(1/x), x, oo) == -1 assert limit(x + exp(-x), x, oo) == oo assert limit(x + exp(-x**2), x, oo) == oo assert limit(x + exp(-exp(x)), x, oo) == oo assert limit(13 + 1/x - exp(-x), x, oo) == 13 def test_basic3(): assert limit(1/x, x, 0, dir="+") == oo assert limit(1/x, x, 0, dir="-") == -oo def test_basic4(): assert limit(2*x + y*x, x, 0) == 0 assert limit(2*x + y*x, x, 1) == 2 + y assert limit(2*x**8 + y*x**(-3), x, -2) == 512 - y/8 assert limit(sqrt(x + 1) - sqrt(x), x, oo) == 0 assert integrate(1/(x**3 + 1), (x, 0, oo)) == 2*pi*sqrt(3)/9 def test_issue786(): assert limit(x*y + x*z, z, 2) == x*y + 2*x def test_Limit(): assert Limit(sin(x)/x, x, 0) != 1 assert Limit(sin(x)/x, x, 0).doit() == 1 def test_floor(): assert limit(floor(x), x, -2, "+") == -2 assert limit(floor(x), x, -2, "-") == -3 assert limit(floor(x), x, -1, "+") == -1 assert limit(floor(x), x, -1, "-") == -2 assert limit(floor(x), x, 0, "+") == 0 assert limit(floor(x), x, 0, "-") == -1 assert limit(floor(x), x, 1, "+") == 1 assert limit(floor(x), x, 1, "-") == 0 assert limit(floor(x), x, 2, "+") == 2 assert limit(floor(x), x, 2, "-") == 1 assert limit(floor(x), x, 248, "+") == 248 assert limit(floor(x), x, 248, "-") == 247 def test_floor_requires_robust_assumptions(): assert limit(floor(sin(x)), x, 0, "+") == 0 assert limit(floor(sin(x)), x, 0, "-") == -1 assert limit(floor(cos(x)), x, 0, "+") == 0 assert limit(floor(cos(x)), x, 0, "-") == 0 assert limit(floor(5 + sin(x)), x, 0, "+") == 5 assert limit(floor(5 + sin(x)), x, 0, "-") == 4 assert limit(floor(5 + cos(x)), x, 0, "+") == 5 assert limit(floor(5 + cos(x)), x, 0, "-") == 5 def test_ceiling(): assert limit(ceiling(x), x, -2, "+") == -1 assert limit(ceiling(x), x, -2, "-") == -2 assert limit(ceiling(x), x, -1, "+") == 0 assert limit(ceiling(x), x, -1, "-") == -1 assert limit(ceiling(x), x, 0, "+") == 1 assert limit(ceiling(x), x, 0, "-") == 0 assert limit(ceiling(x), x, 1, "+") == 2 assert limit(ceiling(x), x, 1, "-") == 1 assert limit(ceiling(x), x, 2, "+") == 3 assert limit(ceiling(x), x, 2, "-") == 2 assert limit(ceiling(x), x, 248, "+") == 249 assert limit(ceiling(x), x, 248, "-") == 248 def test_ceiling_requires_robust_assumptions(): assert limit(ceiling(sin(x)), x, 0, "+") == 1 assert limit(ceiling(sin(x)), x, 0, "-") == 0 assert limit(ceiling(cos(x)), x, 0, "+") == 1 assert limit(ceiling(cos(x)), x, 0, "-") == 1 assert limit(ceiling(5 + sin(x)), x, 0, "+") == 6 assert limit(ceiling(5 + sin(x)), x, 0, "-") == 5 assert limit(ceiling(5 + cos(x)), x, 0, "+") == 6 assert limit(ceiling(5 + cos(x)), x, 0, "-") == 6 def test_atan(): x = Symbol("x", real=True) assert limit(atan(x)*sin(1/x), x, 0) == 0 assert limit(atan(x) + sqrt(x + 1) - sqrt(x), x, oo) == pi/2 def test_abs(): assert limit(abs(x), x, 0) == 0 assert limit(abs(sin(x)), x, 0) == 0 assert limit(abs(cos(x)), x, 0) == 1 assert limit(abs(sin(x + 1)), x, 0) == sin(1) def test_heuristic(): x = Symbol("x", real=True) assert heuristics(sin(1/x) + atan(x), x, 0, '+') == sin(oo) assert limit(log(2 + sqrt(atan(x))*sqrt(sin(1/x))), x, 0) == log(2) def test_issue772(): z = Symbol("z", positive=True) f = -1/z*exp(-z*x) assert limit(f, x, oo) == 0 assert f.limit(x, oo) == 0 def test_exponential(): n = Symbol('n') x = Symbol('x', real=True) assert limit((1 + x/n)**n, n, oo) == exp(x) assert limit((1 + x/(2*n))**n, n, oo) == exp(x/2) assert limit((1 + x/(2*n + 1))**n, n, oo) == exp(x/2) assert limit(((x - 1)/(x + 1))**x, x, oo) == exp(-2) assert limit(1 + (1 + 1/x)**x, x, oo) == 1 + S.Exp1 @XFAIL def test_exponential2(): n = Symbol('n') assert limit((1 + x/(n + sin(n)))**n, n, oo) == exp(x) def test_doit(): f = Integral(2 * x, x) l = Limit(f, x, oo) assert l.doit() == oo @XFAIL def test_doit2(): f = Integral(2 * x, x) l = Limit(f, x, oo) # limit() breaks on the contained Integral. assert l.doit(deep=False) == l def test_bug693a(): assert sin(sin(x + 1) + 1).limit(x, 0) == sin(sin(1) + 1) def test_issue693(): assert limit( (1 - cos(x))/x**2, x, S(1)/2) == 4 - 4*cos(S(1)/2) assert limit(sin(sin(x + 1) + 1), x, 0) == sin(1 + sin(1)) assert limit(abs(sin(x + 1) + 1), x, 0) == 1 + sin(1) def test_issue991(): assert limit(1/(x + 3), x, 2) == S(1)/5 assert limit(1/(x + pi), x, 2) == S(1)/(2 + pi) assert limit(log(x)/(x**2 + 3), x, 2) == log(2)/7 assert limit(log(x)/(x**2 + pi), x, 2) == log(2)/(4 + pi) def test_issue1448(): assert limit(cot(x), x, 0, dir='+') == oo assert limit(cot(x), x, pi/2, dir='+') == 0 def test_issue2065(): assert limit(x**0.5, x, oo) == oo**0.5 == oo assert limit(x**0.5, x, 16) == S(16)**0.5 assert limit(x**0.5, x, 0) == 0 assert limit(x**(-0.5), x, oo) == 0 assert limit(x**(-0.5), x, 4) == S(4)**(-0.5) def test_issue2084(): # using list(...) so py.test can recalculate values tests = list(cartes([x, -x], [-1, 1], [2, 3, Rational(1, 2), Rational(2, 3)], ['-', '+'])) results = (oo, oo, -oo, oo, -oo*I, oo, -oo*(-1)**Rational(1, 3), oo, 0, 0, 0, 0, 0, 0, 0, 0, oo, oo, oo, -oo, oo, -oo*I, oo, -oo*(-1)**Rational(1, 3), 0, 0, 0, 0, 0, 0, 0, 0) assert len(tests) == len(results) for i, (args, res) in enumerate(zip(tests, results)): y, s, e, d = args eq = y**(s*e) try: assert limit(eq, x, 0, dir=d) == res except AssertionError: if 0: # change to 1 if you want to see the failing tests print() print(i, res, eq, d, limit(eq, x, 0, dir=d)) else: assert None def test_issue2085(): assert limit(sin(x)/x, x, oo) == 0 assert limit(atan(x), x, oo) == pi/2 assert limit(gamma(x), x, oo) == oo assert limit(cos(x)/x, x, oo) == 0 assert limit(gamma(x), x, Rational(1, 2)) == sqrt(pi) r = Symbol('r', real=True, bounded=True) assert limit(r*sin(1/r), r, 0) == 0 def test_issue2130(): assert limit((1 + y)**(1/y) - S.Exp1, y, 0) == 0 def test_issue1447(): # using list(...) so py.test can recalculate values tests = list(cartes([cot, tan], [-pi/2, 0, pi/2, pi, 3*pi/2], ['-', '+'])) results = (0, 0, -oo, oo, 0, 0, -oo, oo, 0, 0, oo, -oo, 0, 0, oo, -oo, 0, 0, oo, -oo) assert len(tests) == len(results) for i, (args, res) in enumerate(zip(tests, results)): f, l, d = args eq = f(x) try: assert limit(eq, x, l, dir=d) == res except AssertionError: if 0: # change to 1 if you want to see the failing tests print() print(i, res, eq, l, d, limit(eq, x, l, dir=d)) else: assert None def test_issue835(): assert limit((1 + x**log(3))**(1/x), x, 0) == 1 assert limit((5**(1/x) + 3**(1/x))**x, x, 0) == 5 def test_calculate_series(): # needs gruntz calculate_series to go to n = 32 assert limit(x**(S(77)/3)/(1 + x**(S(77)/3)), x, oo) == 1 # needs gruntz calculate_series to go to n = 128 assert limit(x**101.1/(1 + x**101.1), x, oo) == 1 def test_issue2856(): assert limit((x**16)/(1 + x**16), x, oo) == 1 assert limit((x**100)/(1 + x**100), x, oo) == 1 assert limit((x**1885)/(1 + x**1885), x, oo) == 1 assert limit((x**1000/((x + 1)**1000 + exp(-x))), x, oo) == 1 def test_newissue(): assert limit(exp(1/sin(x))/exp(cot(x)), x, 0) == 1 def test_extended_real_line(): assert limit(x - oo, x, oo) == -oo assert limit(oo - x, x, -oo) == oo assert limit(x**2/(x - 5) - oo, x, oo) == -oo assert limit(1/(x + sin(x)) - oo, x, 0) == -oo assert limit(oo/x, x, oo) == oo assert limit(x - oo + 1/x, x, oo) == -oo assert limit(x - oo + 1/x, x, 0) == -oo @XFAIL def test_order_oo(): from sympy import C x = Symbol('x', positive=True, bounded=True) assert C.Order(x)*oo != C.Order(1, x) assert limit(oo/(x**2 - 4), x, oo) == oo def test_issue2337(): raises(NotImplementedError, lambda: limit(exp(x*y), x, oo)) raises(NotImplementedError, lambda: limit(exp(-x*y), x, oo)) def test_Limit_dir(): raises(TypeError, lambda: Limit(x, x, 0, dir=0)) raises(ValueError, lambda: Limit(x, x, 0, dir='0')) def test_polynomial(): assert limit((x + 1)**1000/((x + 1)**1000 + 1), x, oo) == 1 assert limit((x + 1)**1000/((x + 1)**1000 + 1), x, -oo) == 1 def test_rational(): assert limit(1/y - ( 1/(y+x) + x/(y+x)/y )/z,x,oo) == 1/y - 1/(y*z) assert limit(1/y - ( 1/(y+x) + x/(y+x)/y )/z,x,-oo) == 1/y - 1/(y*z) def test_issue_2641(): assert limit(log(x)/z - log(2*x)/z, x, 0) == -log(2)/z def test_issue_3267(): n = Symbol('n', integer=True, positive=True) r = (n + 1)*x**(n + 1)/(x**(n + 1) - 1) - x/(x - 1) assert limit(r, x, 1).simplify() == n/2 def test_factorial(): from sympy import factorial, E f = factorial(x) assert limit(f, x, oo) == oo assert limit(x/f, x, oo) == 0 # see Stirling's approximation: # http://en.wikipedia.org/wiki/Stirling's_approximation assert limit(f/(sqrt(2*pi*x)*(x/E)**x), x, oo) == 1 assert limit(f, x, -oo) == factorial(-oo) assert limit(f, x, x**2) == factorial(x**2) assert limit(f, x, -x**2) == factorial(-x**2) def test_issue_3461(): e = 5*x**3/4 - 3*x/4 + (y*(3*x**2/2 - S(1)/2) + \ 35*x**4/8 - 15*x**2/4 + S(3)/8)/(2*(y + 1)) assert limit(e, y, oo) == (5*x**3 + 3*x**2 - 3*x - 1)/4 def test_issue_2641(): assert limit(log(x)*z - log(2*x)*y, x, 0) == oo*sign(y - z) def test_issue_2073(): n = Symbol('n') r = Symbol('r', positive=True) c = Symbol('c') p = Symbol('p', positive=True) m = Symbol('m', negative=True) expr = ((2*n*(n - r + 1)/(n + r*(n - r + 1)))**c + \ (r - 1)*(n*(n - r + 2)/(n + r*(n - r + 1)))**c - n)/(n**c - n) expr = expr.subs(c, c + 1) raises(NotImplementedError, lambda: limit(expr, n, oo)) assert limit(expr.subs(c, m), n, oo) == 1 assert limit(expr.subs(c, p), n, oo).simplify() == \ (2**(p + 1) + r - 1)/(r + 1)**(p + 1) def test_issue_3989(): a = Symbol('a') assert limit(sqrt(x/(x + a)), x, oo) == 1 def test_issue_3265(): a = Symbol('a') e = z/(1 - sqrt(1 + z)*sin(a)**2 - sqrt(1 - z)*cos(a)**2) assert limit(e, z, 0).simplify() == 2/cos(2*a) sympy-0.7.4.1/sympy/series/tests/test_residues.py0000644000175000017500000000422512253362407022320 0ustar georgeskgeorgeskfrom sympy import residue, Symbol, Function, sin, S, I, pi, exp, log, pi, factorial from sympy.utilities.pytest import XFAIL, raises from sympy.abc import x, y, z, a, s def test_basic1(): assert residue(1/x, x, 0) == 1 assert residue(-2/x, x, 0) == -2 assert residue(81/x, x, 0) == 81 assert residue(1/x**2, x, 0) == 0 assert residue(0, x, 0) == 0 assert residue(5, x, 0) == 0 assert residue(x, x, 0) == 0 assert residue(x**2, x, 0) == 0 def test_basic2(): assert residue(1/x, x, 1) == 0 assert residue(-2/x, x, 1) == 0 assert residue(81/x, x, -1) == 0 assert residue(1/x**2, x, 1) == 0 assert residue(0, x, 1) == 0 assert residue(5, x, 1) == 0 assert residue(x, x, 1) == 0 assert residue(x**2, x, 5) == 0 def _test_f(): # FIXME: we get infinite recursion here: f = Function("f") assert residue(f(x)/x**5, x, 0) == f.diff(x, 4)/24 def test_functions(): assert residue(1/sin(x), x, 0) == 1 assert residue(2/sin(x), x, 0) == 2 assert residue(1/sin(x)**2, x, 0) == 0 assert residue(1/sin(x)**5, x, 0) == S(3)/8 def test_expressions(): assert residue(1/(x + 1), x, 0) == 0 assert residue(1/(x + 1), x, -1) == 1 assert residue(1/(x**2 + 1), x, -1) == 0 assert residue(1/(x**2 + 1), x, I) == -I/2 assert residue(1/(x**2 + 1), x, -I) == I/2 assert residue(1/(x**4 + 1), x, 0) == 0 @XFAIL def test_expressions_failing(): assert residue(1/(x**4 + 1), x, exp(I*pi/4)) == -(S(1)/4 + I/4)/sqrt(2) n = Symbol('n', integer=True, positive=True) assert residue(exp(z)/(z - pi*I/4*a)**n, z, I*pi*a) == \ exp(I*pi*a/4)/factorial(n - 1) assert residue(1/(x**2 + a**2)**2, x, a*I) == -I/4/a**3 def test_NotImplemented(): raises(NotImplementedError, lambda: residue(exp(1/z), z, 0)) def test_bug(): assert residue(2**(z)*(s + z)*(1 - s - z)/z**2, z, 0) == \ 1 + s*log(2) - s**2*log(2) - 2*s def test_issue_2555(): assert residue(1/(x**2 + a**2)**2, x, a*I) == -I/(4*a**3) def test_issue_3400(): assert residue(1/(exp(z) - 1), z, 0) == 1 # github issue 2519: assert residue((z**3 + 5)/((z**4 - 1)*(z + 1)), z, -1) == -S(9)/4 sympy-0.7.4.1/sympy/series/tests/test_gruntz.py0000644000175000017500000003616712253362407022040 0ustar georgeskgeorgeskfrom sympy import Symbol, exp, log, oo, Rational, I, sin, gamma, loggamma, S, \ atan, acot, pi, cancel, E, erf, sqrt, zeta, cos, digamma, Integer, Ei, EulerGamma from sympy.functions.elementary.hyperbolic import cosh, coth, sinh, tanh from sympy.series.gruntz import compare, mrv, rewrite, mrv_leadterm, gruntz, \ sign from sympy.utilities.pytest import XFAIL, skip """ This test suite is testing the limit algorithm using the bottom up approach. See the documentation in limits2.py. The algorithm itself is highly recursive by nature, so "compare" is logically the lowest part of the algorithm, yet in some sense it's the most complex part, because it needs to calculate a limit to return the result. Nevertheless the rest of the algorithm depends on compare that it works correctly. """ x = Symbol('x', real=True) m = Symbol('m', real=True) runslow = False def _sskip(): if not runslow: skip("slow") def test_gruntz_evaluation(): # Gruntz' thesis pp. 122 to 123 # 8.1 assert gruntz(exp(x)*(exp(1/x - exp(-x)) - exp(1/x)), x, oo) == -1 # 8.2 assert gruntz(exp(x)*(exp(1/x + exp(-x) + exp(-x**2)) - exp(1/x - exp(-exp(x)))), x, oo) == 1 # 8.3 assert gruntz(exp(exp(x - exp(-x))/(1 - 1/x)) - exp(exp(x)), x, oo) == oo # 8.5 assert gruntz(exp(exp(exp(x + exp(-x)))) / exp(exp(exp(x))), x, oo) == oo # 8.6 assert gruntz(exp(exp(exp(x))) / exp(exp(exp(x - exp(-exp(x))))), x, oo) == oo # 8.7 assert gruntz(exp(exp(exp(x))) / exp(exp(exp(x - exp(-exp(exp(x)))))), x, oo) == 1 # 8.8 assert gruntz(exp(exp(x)) / exp(exp(x - exp(-exp(exp(x))))), x, oo) == 1 # 8.9 assert gruntz(log(x)**2 * exp(sqrt(log(x))*(log(log(x)))**2 * exp(sqrt(log(log(x))) * (log(log(log(x))))**3)) / sqrt(x), x, oo) == 0 # 8.10 assert gruntz((x*log(x)*(log(x*exp(x) - x**2))**2) / (log(log(x**2 + 2*exp(exp(3*x**3*log(x)))))), x, oo) == S(1)/3 # 8.11 assert gruntz((exp(x*exp(-x)/(exp(-x) + exp(-2*x**2/(x + 1)))) - exp(x))/x, x, oo) == -exp(2) # 8.12 assert gruntz((3**x + 5**x)**(1/x), x, oo) == 5 # 8.13 assert gruntz(x/log(x**(log(x**(log(2)/log(x))))), x, oo) == oo # 8.14 assert gruntz(exp(exp(2*log(x**5 + x)*log(log(x)))) / exp(exp(10*log(x)*log(log(x)))), x, oo) == oo # 8.15 assert gruntz(exp(exp(S(5)/2*x**(-S(5)/7) + S(21)/8*x**(S(6)/11) + 2*x**(-8) + S(54)/17*x**(S(49)/45) ))**8 / log(log(-log(S(4)/3*x**(-S(5)/14))))**(S(7)/6), x, oo) == oo # 8.16 assert gruntz((exp(4*x*exp(-x)/(1/exp(x) + 1/exp(2*x**2/(x + 1)))) - exp(x)) / exp(x)**4, x, oo) == 1 # 8.17 assert gruntz(exp(x*exp(-x)/(exp(-x) + exp(-2*x**2/(x + 1))))/exp(x), x, oo) \ == 1 # 8.19 assert gruntz(log(x)*(log(log(x) + log(log(x))) - log(log(x))) / (log(log(x) + log(log(log(x))))), x, oo) == 1 # 8.20 assert gruntz(exp((log(log(x + exp(log(x)*log(log(x)))))) / (log(log(log(exp(x) + x + log(x)))))), x, oo) == E # Another assert gruntz(exp(exp(exp(x + exp(-x)))) / exp(exp(x)), x, oo) == oo def test_gruntz_evaluation_slow(): _sskip() # 8.4 assert gruntz(exp(exp(exp(x)/(1 - 1/x))) - exp(exp(exp(x)/(1 - 1/x - log(x)**(-log(x))))), x, oo) == -oo # 8.18 assert gruntz((exp(exp(-x/(1 + exp(-x))))*exp(-x/(1 + exp(-x/(1 + exp(-x))))) *exp(exp(-x + exp(-x/(1 + exp(-x)))))) / (exp(-x/(1 + exp(-x))))**2 - exp(x) + x, x, oo) == 2 def test_gruntz_eval_special(): # Gruntz, p. 126 assert gruntz(exp(x)*(sin(1/x + exp(-x)) - sin(1/x + exp(-x**2))), x, oo) == 1 assert gruntz((erf(x - exp(-exp(x))) - erf(x)) * exp(exp(x)) * exp(x**2), x, oo) == -2/sqrt(pi) assert gruntz(exp(exp(x)) * (exp(sin(1/x + exp(-exp(x)))) - exp(sin(1/x))), x, oo) == 1 assert gruntz(exp(x)*(gamma(x + exp(-x)) - gamma(x)), x, oo) == oo assert gruntz(exp(exp(digamma(digamma(x))))/x, x, oo) == exp(-S(1)/2) assert gruntz(exp(exp(digamma(log(x))))/x, x, oo) == exp(-S(1)/2) assert gruntz(digamma(digamma(digamma(x))), x, oo) == oo assert gruntz(loggamma(loggamma(x)), x, oo) == oo assert gruntz(((gamma(x + 1/gamma(x)) - gamma(x))/log(x) - cos(1/x)) * x*log(x), x, oo) == -S(1)/2 assert gruntz(x * (gamma(x - 1/gamma(x)) - gamma(x) + log(x)), x, oo) \ == S(1)/2 assert gruntz((gamma(x + 1/gamma(x)) - gamma(x)) / log(x), x, oo) == 1 def test_gruntz_eval_special_slow(): _sskip() assert gruntz(gamma(x + 1)/sqrt(2*pi) - exp(-x)*(x**(x + S(1)/2) + x**(x - S(1)/2)/12), x, oo) == oo assert gruntz(exp(exp(exp(digamma(digamma(digamma(x))))))/x, x, oo) == 0 @XFAIL def test_grunts_eval_special_slow_sometimes_fail(): _sskip() # XXX This sometimes fails!!! assert gruntz(exp(gamma(x - exp(-x))*exp(1/x)) - exp(gamma(x)), x, oo) == oo @XFAIL def test_gruntz_eval_special_fail(): # TODO exponential integral Ei assert gruntz( (Ei(x - exp(-exp(x))) - Ei(x)) *exp(-x)*exp(exp(x))*x, x, oo) == -1 # TODO zeta function series assert gruntz( exp((log(2) + 1)*x) * (zeta(x + exp(-x)) - zeta(x)), x, oo) == -log(2) # TODO 8.35 - 8.37 (bessel, max-min) def test_gruntz_hyperbolic(): assert gruntz(cosh(x), x, oo) == oo assert gruntz(cosh(x), x, -oo) == oo assert gruntz(sinh(x), x, oo) == oo assert gruntz(sinh(x), x, -oo) == -oo assert gruntz(2*cosh(x)*exp(x), x, oo) == oo assert gruntz(2*cosh(x)*exp(x), x, -oo) == 1 assert gruntz(2*sinh(x)*exp(x), x, oo) == oo assert gruntz(2*sinh(x)*exp(x), x, -oo) == -1 assert gruntz(tanh(x), x, oo) == 1 assert gruntz(tanh(x), x, -oo) == -1 assert gruntz(coth(x), x, oo) == 1 assert gruntz(coth(x), x, -oo) == -1 def test_compare1(): assert compare(2, x, x) == "<" assert compare(x, exp(x), x) == "<" assert compare(exp(x), exp(x**2), x) == "<" assert compare(exp(x**2), exp(exp(x)), x) == "<" assert compare(1, exp(exp(x)), x) == "<" assert compare(x, 2, x) == ">" assert compare(exp(x), x, x) == ">" assert compare(exp(x**2), exp(x), x) == ">" assert compare(exp(exp(x)), exp(x**2), x) == ">" assert compare(exp(exp(x)), 1, x) == ">" assert compare(2, 3, x) == "=" assert compare(3, -5, x) == "=" assert compare(2, -5, x) == "=" assert compare(x, x**2, x) == "=" assert compare(x**2, x**3, x) == "=" assert compare(x**3, 1/x, x) == "=" assert compare(1/x, x**m, x) == "=" assert compare(x**m, -x, x) == "=" assert compare(exp(x), exp(-x), x) == "=" assert compare(exp(-x), exp(2*x), x) == "=" assert compare(exp(2*x), exp(x)**2, x) == "=" assert compare(exp(x)**2, exp(x + exp(-x)), x) == "=" assert compare(exp(x), exp(x + exp(-x)), x) == "=" assert compare(exp(x**2), 1/exp(x**2), x) == "=" def test_compare2(): assert compare(exp(x), x**5, x) == ">" assert compare(exp(x**2), exp(x)**2, x) == ">" assert compare(exp(x), exp(x + exp(-x)), x) == "=" assert compare(exp(x + exp(-x)), exp(x), x) == "=" assert compare(exp(x + exp(-x)), exp(-x), x) == "=" assert compare(exp(-x), x, x) == ">" assert compare(x, exp(-x), x) == "<" assert compare(exp(x + 1/x), x, x) == ">" assert compare(exp(-exp(x)), exp(x), x) == ">" assert compare(exp(exp(-exp(x)) + x), exp(-exp(x)), x) == "<" def test_compare3(): assert compare(exp(exp(x)), exp(x + exp(-exp(x))), x) == ">" def test_sign1(): assert sign(Rational(0), x) == 0 assert sign(Rational(3), x) == 1 assert sign(Rational(-5), x) == -1 assert sign(log(x), x) == 1 assert sign(exp(-x), x) == 1 assert sign(exp(x), x) == 1 assert sign(-exp(x), x) == -1 assert sign(3 - 1/x, x) == 1 assert sign(-3 - 1/x, x) == -1 assert sign(sin(1/x), x) == 1 assert sign((x**Integer(2)), x) == 1 def test_sign2(): assert sign(x, x) == 1 assert sign(-x, x) == -1 y = Symbol("y", positive=True) assert sign(y, x) == 1 assert sign(-y, x) == -1 assert sign(y*x, x) == 1 assert sign(-y*x, x) == -1 def mmrv(a, b): return set(mrv(a, b)[0].keys()) def test_mrv1(): assert mmrv(x, x) == set([x]) assert mmrv(x + 1/x, x) == set([x]) assert mmrv(x**2, x) == set([x]) assert mmrv(log(x), x) == set([x]) assert mmrv(exp(x), x) == set([exp(x)]) assert mmrv(exp(-x), x) == set([exp(-x)]) assert mmrv(exp(x**2), x) == set([exp(x**2)]) assert mmrv(-exp(1/x), x) == set([x]) assert mmrv(exp(x + 1/x), x) == set([exp(x + 1/x)]) def test_mrv2a(): assert mmrv(exp(x + exp(-exp(x))), x) == set([exp(-exp(x))]) assert mmrv(exp(x + exp(-x)), x) == set([exp(x + exp(-x)), exp(-x)]) assert mmrv(exp(1/x + exp(-x)), x) == set([exp(-x)]) #sometimes infinite recursion due to log(exp(x**2)) not simplifying def test_mrv2b(): assert mmrv(exp(x + exp(-x**2)), x) == set([exp(-x**2)]) #sometimes infinite recursion due to log(exp(x**2)) not simplifying def test_mrv2c(): assert mmrv( exp(-x + 1/x**2) - exp(x + 1/x), x) == set([exp(x + 1/x), exp(1/x**2 - x)]) #sometimes infinite recursion due to log(exp(x**2)) not simplifying def test_mrv3(): assert mmrv(exp(x**2) + x*exp(x) + log(x)**x/x, x) == set([exp(x**2)]) assert mmrv( exp(x)*(exp(1/x + exp(-x)) - exp(1/x)), x) == set([exp(x), exp(-x)]) assert mmrv(log( x**2 + 2*exp(exp(3*x**3*log(x)))), x) == set([exp(exp(3*x**3*log(x)))]) assert mmrv(log(x - log(x))/log(x), x) == set([x]) assert mmrv( (exp(1/x - exp(-x)) - exp(1/x))*exp(x), x) == set([exp(x), exp(-x)]) assert mmrv( 1/exp(-x + exp(-x)) - exp(x), x) == set([exp(x), exp(-x), exp(x - exp(-x))]) assert mmrv(log(log(x*exp(x*exp(x)) + 1)), x) == set([exp(x*exp(x))]) assert mmrv(exp(exp(log(log(x) + 1/x))), x) == set([x]) def test_mrv4(): ln = log assert mmrv((ln(ln(x) + ln(ln(x))) - ln(ln(x)))/ln(ln(x) + ln(ln(ln(x))))*ln(x), x) == set([x]) assert mmrv(log(log(x*exp(x*exp(x)) + 1)) - exp(exp(log(log(x) + 1/x))), x) == \ set([exp(x*exp(x))]) def mrewrite(a, b, c): return rewrite(a[1], a[0], b, c) def test_rewrite1(): e = exp(x) assert mrewrite(mrv(e, x), x, m) == (1/m, -x) e = exp(x**2) assert mrewrite(mrv(e, x), x, m) == (1/m, -x**2) e = exp(x + 1/x) assert mrewrite(mrv(e, x), x, m) == (1/m, -x - 1/x) e = 1/exp(-x + exp(-x)) - exp(x) assert mrewrite(mrv(e, x), x, m) == (1/(m*exp(m)) - 1/m, -x) def test_rewrite2(): e = exp(x)*log(log(exp(x))) assert mmrv(e, x) == set([exp(x)]) assert mrewrite(mrv(e, x), x, m) == (1/m*log(x), -x) #sometimes infinite recursion due to log(exp(x**2)) not simplifying def test_rewrite3(): e = exp(-x + 1/x**2) - exp(x + 1/x) #both of these are correct and should be equivalent: assert mrewrite(mrv(e, x), x, m) in [(-1/m + m*exp( 1/x + 1/x**2), -x - 1/x), (m - 1/m*exp(1/x + x**(-2)), x**(-2) - x)] def test_mrv_leadterm1(): assert mrv_leadterm(-exp(1/x), x) == (-1, 0) assert mrv_leadterm(1/exp(-x + exp(-x)) - exp(x), x) == (-1, 0) assert mrv_leadterm( (exp(1/x - exp(-x)) - exp(1/x))*exp(x), x) == (-exp(1/x), 0) def test_mrv_leadterm2(): #Gruntz: p51, 3.25 assert mrv_leadterm((log(exp(x) + x) - x)/log(exp(x) + log(x))*exp(x), x) == \ (1, 0) def test_mrv_leadterm3(): #Gruntz: p56, 3.27 assert mmrv(exp(-x + exp(-x)*exp(-x*log(x))), x) == set([exp(-x - x*log(x))]) assert mrv_leadterm(exp(-x + exp(-x)*exp(-x*log(x))), x) == (exp(-x), 0) def test_limit1(): assert gruntz(x, x, oo) == oo assert gruntz(x, x, -oo) == -oo assert gruntz(-x, x, oo) == -oo assert gruntz(x**2, x, -oo) == oo assert gruntz(-x**2, x, oo) == -oo assert gruntz(x*log(x), x, 0, dir="+") == 0 assert gruntz(1/x, x, oo) == 0 assert gruntz(exp(x), x, oo) == oo assert gruntz(-exp(x), x, oo) == -oo assert gruntz(exp(x)/x, x, oo) == oo assert gruntz(1/x - exp(-x), x, oo) == 0 assert gruntz(x + 1/x, x, oo) == oo def test_limit2(): assert gruntz(x**x, x, 0, dir="+") == 1 assert gruntz((exp(x) - 1)/x, x, 0) == 1 assert gruntz(1 + 1/x, x, oo) == 1 assert gruntz(-exp(1/x), x, oo) == -1 assert gruntz(x + exp(-x), x, oo) == oo assert gruntz(x + exp(-x**2), x, oo) == oo assert gruntz(x + exp(-exp(x)), x, oo) == oo assert gruntz(13 + 1/x - exp(-x), x, oo) == 13 def test_limit3(): a = Symbol('a') assert gruntz(x - log(1 + exp(x)), x, oo) == 0 assert gruntz(x - log(a + exp(x)), x, oo) == 0 assert gruntz(exp(x)/(1 + exp(x)), x, oo) == 1 assert gruntz(exp(x)/(a + exp(x)), x, oo) == 1 def test_limit4(): #issue 364 assert gruntz((3**x + 5**x)**(1/x), x, oo) == 5 #issue 364 assert gruntz((3**(1/x) + 5**(1/x))**x, x, 0) == 5 @XFAIL def test_MrvTestCase_page47_ex3_21(): h = exp(-x/(1 + exp(-x))) expr = exp(h)*exp(-x/(1 + h))*exp(exp(-x + h))/h**2 - exp(x) + x expected = set([1/h, exp(x), exp(x - h), exp(x/(1 + h))]) # XXX Incorrect result assert mrv(expr, x).difference(expected) == set() def test_I(): from sympy.functions import sign as sgn y = Symbol("y") assert gruntz(I*x, x, oo) == I*oo assert gruntz(y*I*x, x, oo) == y*I*oo assert gruntz(y*3*I*x, x, oo) == y*I*oo assert gruntz(y*3*sin(I)*x, x, oo).simplify() == sgn(y)*I*oo def test_issue1715(): assert gruntz((x + 1)**(1/log(x + 1)), x, oo) == E def test_intractable(): assert gruntz(1/gamma(x), x, oo) == 0 assert gruntz(1/loggamma(x), x, oo) == 0 assert gruntz(gamma(x)/loggamma(x), x, oo) == oo assert gruntz(exp(gamma(x))/gamma(x), x, oo) == oo assert gruntz(gamma(x), x, 3) == 2 assert gruntz(gamma(S(1)/7 + 1/x), x, oo) == gamma(S(1)/7) assert gruntz(log(x**x)/log(gamma(x)), x, oo) == 1 assert gruntz(log(gamma(gamma(x)))/exp(x), x, oo) == oo def test_aseries_trig(): assert cancel(gruntz(1/log(atan(x)), x, oo) - 1/(log(pi) + log(S(1)/2))) == 0 assert gruntz(1/acot(x), x, -oo) == -oo def test_exp_log_series(): assert gruntz(x/log(log(x*exp(x))), x, oo) == oo def test_issue545(): assert gruntz(((x**7 + x + 1)/(2**x + x**2))**(-1/x), x, oo) == 2 def test_issue3744(): n = Symbol('n', integer=True, positive=True) r = (n + 1)*x**(n + 1)/(x**(n + 1) - 1) - x/(x - 1) assert gruntz(r, x, 1).simplify() == n/2 def test_issue1091(): assert gruntz(x - gamma(1/x), x, oo) == S.EulerGamma @XFAIL def test_issue_2073(): n = Symbol('n') r = Symbol('r', positive=True) c = Symbol('c') p = Symbol('p', positive=True) m = Symbol('m', negative=True) expr = ((2*n*(n - r + 1)/(n + r*(n - r + 1)))**c + \ (r - 1)*(n*(n - r + 2)/(n + r*(n - r + 1)))**c - n)/(n**c - n) expr = expr.subs(c, c + 1) assert gruntz(expr.subs(c, m), n, oo) == 1 # fail: assert gruntz(expr.subs(c, p), n, oo).simplify() == \ (2**(p + 1) + r - 1)/(r + 1)**(p + 1) def test_issue_1010(): assert gruntz(1/gamma(x), x, 0) == 0 assert gruntz(x*gamma(x), x, 0) == 1 def test_issue_3583(): assert gruntz(exp(2*Ei(-x))/x**2, x, 0) == exp(2*EulerGamma) sympy-0.7.4.1/sympy/series/tests/test_lseries.py0000644000175000017500000000320612253362407022141 0ustar georgeskgeorgeskfrom sympy import sin, cos, exp, tanh, E, S, Order from sympy.abc import x, y def test_sin(): e = sin(x).lseries(x) assert next(e) == x assert next(e) == -x**3/6 assert next(e) == x**5/120 def test_cos(): e = cos(x).lseries(x) assert next(e) == 1 assert next(e) == -x**2/2 assert next(e) == x**4/24 def test_exp(): e = exp(x).lseries(x) assert next(e) == 1 assert next(e) == x assert next(e) == x**2/2 assert next(e) == x**3/6 def test_exp2(): e = exp(cos(x)).lseries(x) assert next(e) == E assert next(e) == -E*x**2/2 assert next(e) == E*x**4/6 assert next(e) == -31*E*x**6/720 def test_simple(): assert [t for t in x.lseries()] == [x] assert [t for t in S.One.lseries(x)] == [1] assert not next((x/(x + y)).lseries(y)).has(Order) def test_issue_2084(): s = (x + 1/x).lseries() assert [si for si in s] == [1/x, x] assert next((x + x**2).lseries()) == x assert next(((1 + x)**7).lseries(x)) == 1 assert next((sin(x + y)).series(x, n=3).lseries(y)) == x # it would be nice if all terms were grouped, but in the # following case that would mean that all the terms would have # to be known since, for example, every term has a constant in it. s = ((1 + x)**7).series(x, 1, n=None) assert [next(s) for i in range(2)] == [128, -448 + 448*x] def test_issue_3900(): s = tanh(x).lseries(x, 1) assert next(s) == tanh(1) assert next(s) == x - (x - 1)*tanh(1)**2 - 1 assert next(s) == -(x - 1)**2*tanh(1) + (x - 1)**2*tanh(1)**3 assert next(s) == -(x - 1)**3*tanh(1)**4 - (x - 1)**3/3 + \ 4*(x - 1)**3*tanh(1)**2/3 sympy-0.7.4.1/sympy/series/tests/test_nseries.py0000644000175000017500000003737412253362407022160 0ustar georgeskgeorgeskfrom sympy import (Symbol, Rational, ln, exp, log, sqrt, E, O, pi, I, sinh, sin, cosh, cos, tanh, coth, asinh, acosh, atanh, acoth, tan, cot, Integer, PoleError, floor, ceiling, asin, symbols, limit, Piecewise, Eq, sign, Derivative) from sympy.abc import x, y, z from sympy.utilities.pytest import raises, XFAIL def test_simple_1(): assert x.nseries(x, n=5) == x assert y.nseries(x, n=5) == y assert (1/(x*y)).nseries(y, n=5) == 1/(x*y) assert Rational(3, 4).nseries(x, n=5) == Rational(3, 4) assert x.nseries() == x def test_mul_0(): assert (x*ln(x)).nseries(x, n=5) == x*ln(x) def test_mul_1(): assert (x*ln(2 + x)).nseries(x, n=5) == x*log(2) + x**2/2 - x**3/8 + \ x**4/24 + O(x**5) assert (x*ln(1 + x)).nseries( x, n=5) == x**2 - x**3/2 + x**4/3 + O(x**5) def test_pow_0(): assert (x**2).nseries(x, n=5) == x**2 assert (1/x).nseries(x, n=5) == 1/x assert (1/x**2).nseries(x, n=5) == 1/x**2 assert (x**Rational(2, 3)).nseries(x, n=5) == (x**Rational(2, 3)) assert (sqrt(x)**3).nseries(x, n=5) == (sqrt(x)**3) def test_pow_1(): assert ((1 + x)**2).nseries(x, n=5) == 1 + 2*x + x**2 def test_geometric_1(): assert (1/(1 - x)).nseries(x, n=5) == 1 + x + x**2 + x**3 + x**4 + O(x**5) assert (x/(1 - x)).nseries(x, n=6) == x + x**2 + x**3 + x**4 + x**5 + O(x**6) assert (x**3/(1 - x)).nseries(x, n=8) == x**3 + x**4 + x**5 + x**6 + \ x**7 + O(x**8) def test_sqrt_1(): assert sqrt(1 + x).nseries(x, n=5) == 1 + x/2 - x**2/8 + x**3/16 - 5*x**4/128 + O(x**5) def test_exp_1(): assert exp(x).nseries(x, n=5) == 1 + x + x**2/2 + x**3/6 + x**4/24 + O(x**5) assert exp(x).nseries(x, n=12) == 1 + x + x**2/2 + x**3/6 + x**4/24 + x**5/120 + \ x**6/720 + x**7/5040 + x**8/40320 + x**9/362880 + x**10/3628800 + \ x**11/39916800 + O(x**12) assert exp(1/x).nseries(x, n=5) == exp(1/x) assert exp(1/(1 + x)).nseries(x, n=4) == \ (E*(1 - x - 13*x**3/6 + 3*x**2/2)).expand() + O(x**4) assert exp(2 + x).nseries(x, n=5) == \ (exp(2)*(1 + x + x**2/2 + x**3/6 + x**4/24)).expand() + O(x**5) def test_exp_sqrt_1(): assert exp(1 + sqrt(x)).nseries(x, n=3) == \ (exp(1)*(1 + sqrt(x) + x/2 + sqrt(x)*x/6)).expand() + O(sqrt(x)**3) def test_power_x_x1(): assert (exp(x*ln(x))).nseries(x, n=4) == \ 1 + x*log(x) + x**2*log(x)**2/2 + x**3*log(x)**3/6 + O(x**4*log(x)**4) def test_power_x_x2(): assert (x**x).nseries(x, n=4) == \ 1 + x*log(x) + x**2*log(x)**2/2 + x**3*log(x)**3/6 + O(x**4*log(x)**4) def test_log_singular1(): assert log(1 + 1/x).nseries(x, n=5) == x - log(x) - x**2/2 + x**3/3 - \ x**4/4 + O(x**5) def test_log_power1(): e = 1 / (1/x + x ** (log(3)/log(2))) assert e.nseries(x, n=5) == x - x**(2 + log(3)/log(2)) + O(x**5) def test_log_series(): l = Symbol('l') e = 1/(1 - log(x)) assert e.nseries(x, n=5, logx=l) == 1/(1 - l) def test_log2(): e = log(-1/x) assert e.nseries(x, n=5) == -log(x) + log(-1) def test_log3(): l = Symbol('l') e = 1/log(-1/x) assert e.nseries(x, n=4, logx=l) == 1/(-l + log(-1)) def test_series1(): e = sin(x) assert e.nseries(x, 0, 0) != 0 assert e.nseries(x, 0, 0) == O(1, x) assert e.nseries(x, 0, 1) == O(x, x) assert e.nseries(x, 0, 2) == x + O(x**2, x) assert e.nseries(x, 0, 3) == x + O(x**3, x) assert e.nseries(x, 0, 4) == x - x**3/6 + O(x**4, x) e = (exp(x) - 1)/x assert e.nseries(x, 0, 3) == 1 + x/2 + O(x**2, x) assert x.nseries(x, 0, 2) == x @XFAIL def test_series1_failing(): assert x.nseries(x, 0, 0) == O(1, x) assert x.nseries(x, 0, 1) == O(x, x) def test_seriesbug1(): assert (1/x).nseries(x, 0, 3) == 1/x assert (x + 1/x).nseries(x, 0, 3) == x + 1/x def test_series2x(): assert ((x + 1)**(-2)).nseries(x, 0, 4) == 1 - 2*x + 3*x**2 - 4*x**3 + O(x**4, x) assert ((x + 1)**(-1)).nseries(x, 0, 4) == 1 - x + x**2 - x**3 + O(x**4, x) assert ((x + 1)**0).nseries(x, 0, 3) == 1 assert ((x + 1)**1).nseries(x, 0, 3) == 1 + x assert ((x + 1)**2).nseries(x, 0, 3) == 1 + 2*x + x**2 assert ((x + 1)**3).nseries( x, 0, 3) == 1 + 3*x + 3*x**2 + x**3 # 1+3*x+3*x**2+O(x**3) assert (1/(1 + x)).nseries(x, 0, 4) == 1 - x + x**2 - x**3 + O(x**4, x) assert (x + 3/(1 + 2*x)).nseries(x, 0, 4) == 3 - 5*x + 12*x**2 - 24*x**3 + O(x**4, x) assert ((1/x + 1)**3).nseries(x, 0, 3) == 1 + x**(-3) + 3*x**(-2) + 3/x assert (1/(1 + 1/x)).nseries(x, 0, 4) == x - x**2 + x**3 - O(x**4, x) assert (1/(1 + 1/x**2)).nseries(x, 0, 6) == x**2 - x**4 + O(x**6, x) def test_bug2(): # 1/log(0) * log(0) problem w = Symbol("w") e = (w**(-1) + w**( -log(3)*log(2)**(-1)))**(-1)*(3*w**(-log(3)*log(2)**(-1)) + 2*w**(-1)) e = e.expand() assert e.nseries(w, 0, 4).subs(w, 0) == 3 def test_exp(): e = (1 + x)**(1/x) assert e.nseries(x, n=3) == exp(1) - x*exp(1)/2 + O(x**2, x) def test_exp2(): w = Symbol("w") e = w**(1 - log(x)/(log(2) + log(x))) logw = Symbol("logw") assert e.nseries( w, 0, 1, logx=logw) == exp(logw - logw*log(x)/(log(2) + log(x))) def test_bug3(): e = (2/x + 3/x**2)/(1/x + 1/x**2) assert e.nseries(x, n=3) == 3 + O(x) def test_generalexponent(): p = 2 e = (2/x + 3/x**p)/(1/x + 1/x**p) assert e.nseries(x, 0, 3) == 3 + O(x) p = Rational(1, 2) e = (2/x + 3/x**p)/(1/x + 1/x**p) assert e.nseries(x, 0, 2) == 2 + sqrt(x) + O(x) e = 1 + sqrt(x) assert e.nseries(x, 0, 4) == 1 + sqrt(x) # more complicated example def test_genexp_x(): e = 1/(1 + sqrt(x)) assert e.nseries(x, 0, 2) == \ 1 + x - sqrt(x) - sqrt(x)**3 + O(x**2, x) # more complicated example def test_genexp_x2(): p = Rational(3, 2) e = (2/x + 3/x**p)/(1/x + 1/x**p) assert e.nseries(x, 0, 3) == 3 - sqrt(x) + x + O(sqrt(x)**3) def test_seriesbug2(): w = Symbol("w") #simple case (1): e = ((2*w)/w)**(1 + w) assert e.nseries(w, 0, 1) == 2 + O(w, w) assert e.nseries(w, 0, 1).subs(w, 0) == 2 def test_seriesbug2b(): w = Symbol("w") #test sin e = sin(2*w)/w assert e.nseries(w, 0, 3) == 2 + O(w**2, w) def test_seriesbug2d(): w = Symbol("w", real=True) e = log(sin(2*w)/w) assert e.series(w, n=5) == log(2) - 2*w**2/3 - 4*w**4/45 + O(w**5) def test_seriesbug2c(): w = Symbol("w", real=True) #more complicated case, but sin(x)~x, so the result is the same as in (1) e = (sin(2*w)/w)**(1 + w) assert e.series(w, 0, 1) == 2 + O(w) assert e.series(w, 0, 3) == 2 + 2*w*log(2) + \ w**2*(-Rational(4, 3) + log(2)**2) + O(w**3) assert e.series(w, 0, 2).subs(w, 0) == 2 def test_expbug4(): x = Symbol("x", real=True) assert (log( sin(2*x)/x)*(1 + x)).series(x, 0, 2) == log(2) + x*log(2) + O(x**2, x) assert exp( log(sin(2*x)/x)*(1 + x)).series(x, 0, 2) == 2 + 2*x*log(2) + O(x**2) assert exp(log(2) + O(x)).nseries(x, 0, 2) == 2 + O(x) assert ((2 + O(x))**(1 + x)).nseries(x, 0, 2) == 2 + O(x) def test_logbug4(): assert log(2 + O(x)).nseries(x, 0, 2) == log(2) + O(x, x) def test_expbug5(): assert exp(log(1 + x)/x).nseries(x, n=3) == exp(1) + -exp(1)*x/2 + O(x**2) assert exp(O(x)).nseries(x, 0, 2) == 1 + O(x) def test_sinsinbug(): assert sin(sin(x)).nseries(x, 0, 8) == x - x**3/3 + x**5/10 - 8*x**7/315 + O(x**8) def test_issue159(): a = x/(exp(x) - 1) assert a.nseries(x, 0, 5) == 1 - x/2 - x**4/720 + x**2/12 + O(x**5) def test_issue105(): x = Symbol("x", nonnegative=True) f = sin(x**3)**Rational(1, 3) assert f.nseries(x, 0, 17) == x - x**7/18 - x**13/3240 + O(x**17) def test_issue125(): f = sqrt(1 - sqrt(y)) assert f.nseries(y, 0, 2) == 1 - sqrt(y)/2 - y/8 - sqrt(y)**3/16 + O(y**2) def test_issue364(): from sympy import summation, symbols w, i = symbols('w,i') r = log(5)/log(3) p = w**(-1 + r) e = 1/x*(-log(w**(1 + r)) + log(w + w**r)) e_ser = -r*log(w)/x + p/x - p**2/(2*x) + O(p**3) assert e.nseries(w, n=3) == e_ser def test_sin(): assert sin(8*x).nseries(x, n=4) == 8*x - 256*x**3/3 + O(x**4) assert sin(x + y).nseries(x, n=1) == sin(y) + O(x) assert sin(x + y).nseries(x, n=2) == sin(y) + cos(y)*x + O(x**2) assert sin(x + y).nseries(x, n=5) == sin(y) + cos(y)*x - sin(y)*x**2/2 - \ cos(y)*x**3/6 + sin(y)*x**4/24 + O(x**5) def test_issue416(): e = sin(8*x)/x assert e.nseries(x, n=6) == 8 - 256*x**2/3 + 4096*x**4/15 + O(x**5) def test_issue406(): e = sin(x)**(-4)*(sqrt(cos(x))*sin(x)**2 - cos(x)**Rational(1, 3)*sin(x)**2) assert e.nseries(x, n=9) == -Rational(1)/12 - 7*x**2/288 - \ 43*x**4/10368 + O(x**5) def test_issue402(): a = Symbol("a") e = x**(-2)*(x*sin(a + x) - x*sin(a)) assert e.nseries(x, n=6) == cos(a) - sin(a)*x/2 - cos(a)*x**2/6 + \ sin(a)*x**3/24 + O(x**4) e = x**(-2)*(x*cos(a + x) - x*cos(a)) assert e.nseries(x, n=6) == -sin(a) - cos(a)*x/2 + sin(a)*x**2/6 + \ cos(a)*x**3/24 + O(x**4) def test_issue403(): e = sin(5*x)/sin(2*x) assert e.nseries(x, n=2) == Rational(5, 2) + O(x) assert e.nseries(x, n=6) == \ Rational(5, 2) - 35*x**2/4 + 329*x**4/48 + O(x**5) def test_issue404(): e = sin(2 + x)/(2 + x) assert e.nseries(x, n=2) == sin(2)/2 + x*cos(2)/2 - x*sin(2)/4 + O(x**2) def test_issue407(): e = (x + sin(3*x))**(-2)*(x*(x + sin(3*x)) - (x + sin(3*x))*sin(2*x)) assert e.nseries(x, n=7) == \ -Rational(1, 4) + 5*x**2/96 + 91*x**4/768 + O(x**5) def test_issue409(): x = Symbol("x", real=True) assert log(sin(x)).series(x, n=5) == log(x) - x**2/6 - x**4/180 + O(x**5) e = -log(x) + x*(-log(x) + log(sin(2*x))) + log(sin(2*x)) assert e.series(x, n=5) == \ log(2) + log(2)*x - 2*x**2/3 - 2*x**3/3 - 4*x**4/45 + O(x**5) def test_issue408(): e = x**(-4)*(x**2 - x**2*sqrt(cos(x))) assert e.nseries(x, n=9) == \ Rational(1, 4) + x**2/96 + 19*x**4/5760 + O(x**5) def test_issue540(): assert sin(cos(x)).nseries(x, n=5) == \ sin(1) - x**2*cos(1)/2 - x**4*sin(1)/8 + x**4*cos(1)/24 + O(x**5) def test_hyperbolic(): assert sinh(x).nseries(x, n=6) == x + x**3/6 + x**5/120 + O(x**6) assert cosh(x).nseries(x, n=5) == 1 + x**2/2 + x**4/24 + O(x**5) assert tanh(x).nseries(x, n=6) == x - x**3/3 + 2*x**5/15 + O(x**6) assert coth(x).nseries(x, n=6) == \ 1/x - x**3/45 + x/3 + 2*x**5/945 + O(x**6) assert asinh(x).nseries(x, n=6) == x - x**3/6 + 3*x**5/40 + O(x**6) assert acosh(x).nseries(x, n=6) == \ pi*I/2 - I*x - 3*I*x**5/40 - I*x**3/6 + O(x**6) assert atanh(x).nseries(x, n=6) == x + x**3/3 + x**5/5 + O(x**6) assert acoth(x).nseries(x, n=6) == x + x**3/3 + x**5/5 + pi*I/2 + O(x**6) def test_series2(): w = Symbol("w", real=True) x = Symbol("x", real=True) e = w**(-2)*(w*exp(1/x - w) - w*exp(1/x)) assert e.nseries(w, n=4) == -exp(1/x) + w * exp(1/x) / 2 + O(w**2) def test_series3(): w = Symbol("w", real=True) x = Symbol("x", real=True) e = w**(-6)*(w**3*tan(w) - w**3*sin(w)) assert e.nseries(w, n=8) == Integer(1)/2 + O(w**2) def test_bug4(): w = Symbol("w") e = x/(w**4 + x**2*w**4 + 2*x*w**4)*w**4 assert e.nseries(w, n=2) in [x/(1 + 2*x + x**2), 1/(1 + x/2 + 1/x/2)/2, 1/x/(1 + 2/x + x**(-2))] def test_bug5(): w = Symbol("w") l = Symbol('l') e = (-log(w) + log(1 + w*log(x)))**(-2)*w**(-2)*((-log(w) + log(1 + x*w))*(-log(w) + log(1 + w*log(x)))*w - x*(-log(w) + log(1 + w*log(x)))*w) assert e.nseries(w, n=2, logx=l) == x/w/l + 1/w + O(1, w) assert e.nseries(w, n=3, logx=l) == x/w/l + 1/w - x/l + 1/l*log(x) \ + x*log(x)/l**2 + O(w) def test_issue1016(): assert (sin(x)/(1 - cos(x))).nseries(x, n=1) == O(1/x) assert (sin(x)**2/(1 - cos(x))).nseries(x, n=1) == O(1, x) def test_pole(): raises(PoleError, lambda: sin(1/x).series(x, 0, 5)) raises(PoleError, lambda: sin(1 + 1/x).series(x, 0, 5)) raises(PoleError, lambda: (x*sin(1/x)).series(x, 0, 5)) def test_expsinbug(): assert exp(sin(x)).series(x, 0, 0) == O(1, x) assert exp(sin(x)).series(x, 0, 1) == 1 + O(x) assert exp(sin(x)).series(x, 0, 2) == 1 + x + O(x**2) assert exp(sin(x)).series(x, 0, 3) == 1 + x + x**2/2 + O(x**3) assert exp(sin(x)).series(x, 0, 4) == 1 + x + x**2/2 + O(x**4) assert exp(sin(x)).series(x, 0, 5) == 1 + x + x**2/2 - x**4/8 + O(x**5) def test_floor(): x = Symbol('x') assert floor(x).series(x) == 0 assert floor(-x).series(x) == -1 assert floor(sin(x)).series(x) == 0 assert floor(sin(-x)).series(x) == -1 assert floor(x**3).series(x) == 0 assert floor(-x**3).series(x) == -1 assert floor(cos(x)).series(x) == 0 assert floor(cos(-x)).series(x) == 0 assert floor(5 + sin(x)).series(x) == 5 assert floor(5 + sin(-x)).series(x) == 4 assert floor(x).series(x, 2) == 2 assert floor(-x).series(x, 2) == -3 x = Symbol('x', negative=True) assert floor(x + 1.5).series(x) == 1 def test_ceiling(): assert ceiling(x).series(x) == 1 assert ceiling(-x).series(x) == 0 assert ceiling(sin(x)).series(x) == 1 assert ceiling(sin(-x)).series(x) == 0 assert ceiling(1 - cos(x)).series(x) == 1 assert ceiling(1 - cos(-x)).series(x) == 1 assert ceiling(x).series(x, 2) == 3 assert ceiling(-x).series(x, 2) == -2 def test_abs(): a = Symbol('a') assert abs(x).nseries(x, n=4) == x assert abs(-x).nseries(x, n=4) == x assert abs(x + 1).nseries(x, n=4) == x + 1 assert abs(sin(x)).nseries(x, n=4) == x - Rational(1, 6)*x**3 + O(x**4) assert abs(sin(-x)).nseries(x, n=4) == x - Rational(1, 6)*x**3 + O(x**4) assert abs(x - a).nseries(x, 1) == Piecewise((x - 1, Eq(1 - a, 0)), ((x - a)*sign(1 - a), True)) def test_dir(): assert abs(x).series(x, 0, dir="+") == x assert abs(x).series(x, 0, dir="-") == -x assert floor(x + 2).series(x, 0, dir='+') == 2 assert floor(x + 2).series(x, 0, dir='-') == 1 assert floor(x + 2.2).series(x, 0, dir='-') == 2 assert ceiling(x + 2.2).series(x, 0, dir='-') == 3 assert sin(x + y).series(x, 0, dir='-') == sin(x + y).series(x, 0, dir='+') def test_issue405(): a = Symbol("a") e = asin(a*x)/x assert e.series(x, 4, n=2).removeO().subs(x, x - 4) == \ (x - 4)*(a/(4*sqrt(-16*a**2 + 1)) - asin(4*a)/16) + asin(4*a)/4 def test_issue1342(): a, b = symbols('a,b') f = 1/(1 + a*x) assert f.series(x, 0, 5) == 1 - a*x + a**2*x**2 - a**3*x**3 + \ a**4*x**4 + O(x**5) f = 1/(1 + (a + b)*x) assert f.series(x, 0, 3) == 1 + x*(-a - b) + \ x**2*(a**2 + 2*a*b + b**2) + O(x**3) def test_issue1230(): assert tan(x).series(x, pi/2, n=3).removeO().subs(x, x - pi/2) == \ -pi/6 + x/3 - 1/(x - pi/2) assert cot(x).series(x, pi, n=3).removeO().subs(x, x - pi) == \ -x/3 + pi/3 + 1/(x - pi) assert limit(tan(x)**tan(2*x), x, pi/4) == exp(-1) def test_issue2084(): assert abs(x + x**2).series(n=1) == O(x) assert abs(x + x**2).series(n=2) == x + O(x**2) assert ((1 + x)**2).series(x, n=6) == 1 + 2*x + x**2 assert (1 + 1/x).series() == 1 + 1/x assert Derivative(exp(x).series(), x).doit() == \ 1 + x + x**2/2 + x**3/6 + x**4/24 + O(x**5) def test_issue_2555(): a = Symbol('a') assert (1/(x**2+a**2)**2).nseries(x, x0=I*a, n=0) == \ -I/(4*a**3*x) - 1/(4*a**2*x**2) + O(1, x) assert (1/(x**2+a**2)**2).nseries(x, x0=I*a, n=1) == \ 3/(16*a**4) - I/(4*a**3*x) - 1/(4*a**2*x**2) + O(x) def test_issue_2826(): sx = sqrt(x + z).series(z, 0, 1) sxy = sqrt(x + y + z).series(z, 0, 1) s1, s2 = sx.subs(x, x + y), sxy assert (s1 - s2).expand().removeO().simplify() == 0 sx = sqrt(x + z).series(z, 0, 1) sxy = sqrt(x + y + z).series(z, 0, 1) assert sxy.subs({x:1, y:2}) == sx.subs(x, 3) sympy-0.7.4.1/sympy/series/tests/test_demidovich.py0000644000175000017500000001110212253362407022600 0ustar georgeskgeorgeskfrom sympy import limit, Symbol, oo, sqrt, Rational, log, exp, cos, sin, tan, \ pi, asin, together, root # Numbers listed with the tests refer to problem numbers in the book # "Anti-demidovich, problemas resueltos, Ed. URSS" x = Symbol("x") def test_leadterm(): assert (3 + 2*x**(log(3)/log(2) - 1)).leadterm(x) == (3, 0) def root3(x): return root(x, 3) def root4(x): return root(x, 4) def test_Limits_simple_0(): assert limit((2**(x + 1) + 3**(x + 1))/(2**x + 3**x), x, oo) == 3 # 175 def test_Limits_simple_1(): assert limit((x + 1)*(x + 2)*(x + 3)/x**3, x, oo) == 1 # 172 assert limit(sqrt(x + 1) - sqrt(x), x, oo) == 0 # 179 assert limit((2*x - 3)*(3*x + 5)*(4*x - 6)/(3*x**3 + x - 1), x, oo) == 8 # Primjer 1 assert limit(x/root3(x**3 + 10), x, oo) == 1 # Primjer 2 assert limit((x + 1)**2/(x**2 + 1), x, oo) == 1 # 181 def test_Limits_simple_2(): assert limit(1000*x/(x**2 - 1), x, oo) == 0 # 182 assert limit((x**2 - 5*x + 1)/(3*x + 7), x, oo) == oo # 183 assert limit((2*x**2 - x + 3)/(x**3 - 8*x + 5), x, oo) == 0 # 184 assert limit((2*x**2 - 3*x - 4)/sqrt(x**4 + 1), x, oo) == 2 # 186 assert limit((2*x + 3)/(x + root3(x)), x, oo) == 2 # 187 assert limit(x**2/(10 + x*sqrt(x)), x, oo) == oo # 188 assert limit(root3(x**2 + 1)/(x + 1), x, oo) == 0 # 189 assert limit(sqrt(x)/sqrt(x + sqrt(x + sqrt(x))), x, oo) == 1 # 190 def test_Limits_simple_3a(): a = Symbol('a') #issue 414 assert together(limit((x**2 - (a + 1)*x + a)/(x**3 - a**3), x, a)) == \ (a - 1)/(3*a**2) # 196 def test_Limits_simple_3b(): h = Symbol("h") assert limit(((x + h)**3 - x**3)/h, h, 0) == 3*x**2 # 197 assert limit((1/(1 - x) - 3/(1 - x**3)), x, 1) == -1 # 198 assert limit((sqrt(1 + x) - 1)/(root3(1 + x) - 1), x, 0) == Rational(3)/2 # Primer 4 assert limit((sqrt(x) - 1)/(x - 1), x, 1) == Rational(1)/2 # 199 assert limit((sqrt(x) - 8)/(root3(x) - 4), x, 64) == 3 # 200 assert limit((root3(x) - 1)/(root4(x) - 1), x, 1) == Rational(4)/3 # 201 assert limit( (root3(x**2) - 2*root3(x) + 1)/(x - 1)**2, x, 1) == Rational(1)/9 # 202 def test_Limits_simple_4a(): a = Symbol('a') assert limit((sqrt(x) - sqrt(a))/(x - a), x, a) == 1/(2*sqrt(a)) # Primer 5 assert limit((sqrt(x) - 1)/(root3(x) - 1), x, 1) == Rational(3)/2 # 205 assert limit((sqrt(1 + x) - sqrt(1 - x))/x, x, 0) == 1 # 207 assert limit(sqrt(x**2 - 5*x + 6) - x, x, oo) == -Rational(5)/2 # 213 def test_limits_simple_4aa(): assert limit(x*(sqrt(x**2 + 1) - x), x, oo) == Rational(1)/2 # 214 def test_Limits_simple_4b(): #issue 412 assert limit(x - root3(x**3 - 1), x, oo) == 0 # 215 def test_Limits_simple_4c(): assert limit(log(1 + exp(x))/x, x, -oo) == 0 # 267a assert limit(log(1 + exp(x))/x, x, oo) == 1 # 267b def test_bounded(): assert limit(sin(x)/x, x, oo) == 0 # 216b assert limit(x*sin(1/x), x, 0) == 0 # 227a def test_f1a(): h = Symbol("h") #issue 409: assert limit((sin(2*x)/x)**(1 + x), x, 0) == 2 # Primer 7 def test_f1a2(): #issue 410: assert limit(((x - 1)/(x + 1))**x, x, oo) == exp(-2) # Primer 9 def test_f1b(): m = Symbol("m") n = Symbol("n") h = Symbol("h") a = Symbol("a") assert limit(sin(x)/x, x, 2) == sin(2)/2 # 216a assert limit(sin(3*x)/x, x, 0) == 3 # 217 assert limit(sin(5*x)/sin(2*x), x, 0) == Rational(5)/2 # 218 assert limit(sin(pi*x)/sin(3*pi*x), x, 0) == Rational(1)/3 # 219 assert limit(x*sin(pi/x), x, oo) == pi # 220 assert limit((1 - cos(x))/x**2, x, 0) == Rational(1, 2) # 221 assert limit(x*sin(1/x), x, oo) == 1 # 227b assert limit((cos(m*x) - cos(n*x))/x**2, x, 0) == ((n**2 - m**2)/2) # 232 assert limit((tan(x) - sin(x))/x**3, x, 0) == Rational(1, 2) # 233 assert limit((x - sin(2*x))/(x + sin(3*x)), x, 0) == -Rational(1, 4) # 237 assert limit((1 - sqrt(cos(x)))/x**2, x, 0) == Rational(1, 4) # 239 assert limit((sqrt(1 + sin(x)) - sqrt(1 - sin(x)))/x, x, 0) == 1 # 240 assert limit((1 + h/x)**x, x, oo) == exp(h) # Primer 9 assert limit((sin(x) - sin(a))/(x - a), x, a) == cos(a) # 222, *176 assert limit((cos(x) - cos(a))/(x - a), x, a) == -sin(a) # 223 assert limit((sin(x + h) - sin(x))/h, h, 0) == cos(x) # 225 def test_f2a(): assert limit(((x + 1)/(2*x + 1))**(x**2), x, oo) == 0 # Primer 8 def test_f2(): assert limit((sqrt( cos(x)) - root3(cos(x)))/(sin(x)**2), x, 0) == -Rational(1, 12) # *184 def test_f3(): a = Symbol('a') #issue 405 assert limit(asin(a*x)/x, x, 0) == a sympy-0.7.4.1/sympy/series/tests/test_series.py0000644000175000017500000001051012253362407021761 0ustar georgeskgeorgeskfrom sympy import sin, cos, exp, E, series, oo, S, Derivative, O, Integral, \ Function, log, sqrt, Symbol, Subs from sympy.abc import x, y, n, k from sympy.utilities.pytest import raises from sympy.series.gruntz import calculate_series def test_sin(): e1 = sin(x).series(x, 0) e2 = series(sin(x), x, 0) assert e1 == e2 def test_cos(): e1 = cos(x).series(x, 0) e2 = series(cos(x), x, 0) assert e1 == e2 def test_exp(): e1 = exp(x).series(x, 0) e2 = series(exp(x), x, 0) assert e1 == e2 def test_exp2(): e1 = exp(cos(x)).series(x, 0) e2 = series(exp(cos(x)), x, 0) assert e1 == e2 def test_2124(): assert series(1, x) == 1 assert next(S(0).lseries(x)) == 0 assert cos(x).series() == cos(x).series(x) raises(ValueError, lambda: cos(x + y).series()) raises(ValueError, lambda: x.series(dir="")) assert (cos(x).series(x, 1).removeO().subs(x, x - 1) - cos(x + 1).series(x).removeO().subs(x, x - 1)).expand() == 0 e = cos(x).series(x, 1, n=None) assert [next(e) for i in range(2)] == [cos(1), -((x - 1)*sin(1))] e = cos(x).series(x, 1, n=None, dir='-') assert [next(e) for i in range(2)] == [cos(1), (1 - x)*sin(1)] # the following test is exact so no need for x -> x - 1 replacement assert abs(x).series(x, 1, dir='-') == x assert exp(x).series(x, 1, dir='-', n=3).removeO().subs(x, x - 1) == \ E + E*(x - 1) + E*(x - 1)**2/2 D = Derivative assert D(x**2 + x**3*y**2, x, 2, y, 1).series(x).doit() == 12*x*y assert next(D(cos(x), x).lseries()) == D(1, x) assert D( exp(x), x).series(n=3) == D(1, x) + D(x, x) + D(x**2/2, x) + O(x**3) assert Integral(x, (x, 1, 3), (y, 1, x)).series(x) == -4 + 4*x assert (1 + x + O(x**2)).getn() == 2 assert (1 + x).getn() is None assert ((1/sin(x))**oo).series() == oo logx = Symbol('logx') assert ((sin(x))**y).nseries(x, n=1, logx=logx) == \ exp(y*logx) + O(x*exp(y*logx), x) assert sin(1/x).series(x, oo, n=5) == 1/x - 1/(6*x**3) + O(x**(-5), (x, oo)) assert abs(x).series(x, oo, n=5, dir='+') == x assert abs(x).series(x, -oo, n=5, dir='-') == -x assert abs(-x).series(x, oo, n=5, dir='+') == x assert abs(-x).series(x, -oo, n=5, dir='-') == -x assert exp(x*log(x)).series(n=3) == \ 1 + x*log(x) + x**2*log(x)**2/2 + O(x**3*log(x)**3) # XXX is this right? If not, fix "ngot > n" handling in expr. p = Symbol('p', positive=True) assert exp(sqrt(p)**3*log(p)).series(n=3) == \ 1 + p**S('3/2')*log(p) + O(p**3*log(p)**3) assert exp(sin(x)*log(x)).series(n=2) == 1 + x*log(x) + O(x**2*log(x)**2) def test_879(): f = Function('f') assert f(x).series(x, 0, 3, dir='-') == \ f(0) + x*Subs(Derivative(f(x), x), (x,), (0,)) + \ x**2*Subs(Derivative(f(x), x, x), (x,), (0,))/2 + O(x**3) assert f(x).series(x, 0, 3) == \ f(0) + x*Subs(Derivative(f(x), x), (x,), (0,)) + \ x**2*Subs(Derivative(f(x), x, x), (x,), (0,))/2 + O(x**3) assert f(x**2).series(x, 0, 3) == \ f(0) + x**2*Subs(Derivative(f(x), x), (x,), (0,)) + O(x**3) assert f(x**2+1).series(x, 0, 3) == \ f(1) + x**2*Subs(Derivative(f(x), x), (x,), (1,)) + O(x**3) class TestF(Function): pass assert TestF(x).series(x, 0, 3) == TestF(0) + \ x*Subs(Derivative(TestF(x), x), (x,), (0,)) + \ x**2*Subs(Derivative(TestF(x), x, x), (x,), (0,))/2 + O(x**3) from sympy.series.acceleration import richardson, shanks from sympy import Sum, Integer def test_acceleration(): e = (1 + 1/n)**n assert round(richardson(e, n, 10, 20).evalf(), 10) == round(E.evalf(), 10) A = Sum(Integer(-1)**(k + 1) / k, (k, 1, n)) assert round(shanks(A, n, 25).evalf(), 4) == round(log(2).evalf(), 4) assert round(shanks(A, n, 25, 5).evalf(), 10) == round(log(2).evalf(), 10) def test_1484(): assert cos(1 + x + x**2).series(x, 0, 5) == cos(1) - x*sin(1) + \ x**2*(-sin(1) - cos(1)/2) + x**3*(-cos(1) + sin(1)/6) + \ x**4*(-11*cos(1)/24 + sin(1)/2) + O(x**5) def test_issue_3219(): eq = (1/x)**(S(2)/3) assert (eq + 1).as_leading_term(x) == eq def test_x_is_base_detection(): eq = (x**2)**(S(2)/3) assert eq.series() == x**(S(4)/3) def test_sin_power(): e = sin(x)**1.2 assert calculate_series(e, x) == x**1.2 sympy-0.7.4.1/sympy/series/tests/test_kauers.py0000644000175000017500000000201312253362407021760 0ustar georgeskgeorgeskfrom sympy.series.kauers import finite_diff from sympy.series.kauers import finite_diff_kauers from sympy.abc import x, y, z, m, n, k, w from sympy import sin, cos from sympy import pi from sympy import Sum def test_finite_diff(): assert finite_diff(x**2 + 2*x + 1, x) == 2*x + 3 assert finite_diff(y**3 + 2*y**2 + 3*y + 5, y) == 3*y**2 + 7*y + 6 assert finite_diff(z**2 - 2*z + 3, z) == 2*z - 1 assert finite_diff(w**2 + 3*w - 2, w) == 2*w + 4 assert finite_diff(sin(x), x, pi/6) == -sin(x) + sin(x + pi/6) assert finite_diff(cos(y), y, pi/3) == -cos(y) + cos(y + pi/3) assert finite_diff(x**2 - 2*x + 3, x, 2) == 4*x assert finite_diff(n**2 - 2*n + 3, n, 3) == 6*n + 3 def test_finite_diff_kauers(): assert finite_diff_kauers(Sum(x**2, (x, 1, n))) == (n + 1)**2 assert finite_diff_kauers(Sum(y, (y, 1, m))) == (m + 1) assert finite_diff_kauers(Sum((x*y), (x, 1, m), (y, 1, n))) == (m + 1)*(n + 1) assert finite_diff_kauers(Sum((x*y**2), (x, 1, m), (y, 1, n))) == (n + 1)**2*(m + 1) sympy-0.7.4.1/sympy/series/tests/test_order.py0000644000175000017500000003223212253362407021607 0ustar georgeskgeorgeskfrom sympy import (Symbol, Rational, Order, exp, ln, log, nan, oo, O, pi, I, S, Integral, sin, sqrt, conjugate, expand, transpose, symbols, Function) from sympy.utilities.pytest import XFAIL, raises from sympy.abc import w, x, y, z def test_caching_bug(): #needs to be a first test, so that all caches are clean #cache it e = O(w) #and test that this won't raise an exception O(w**(-1/x/log(3)*log(5)), w) def test_free_symbols(): assert Order(1).free_symbols == set() assert Order(x).free_symbols == set([x]) assert Order(1, x).free_symbols == set([x]) assert Order(x*y).free_symbols == set([x, y]) assert Order(x, x, y).free_symbols == set([x, y]) def test_simple_1(): o = Rational(0) assert Order(2*x) == Order(x) assert Order(x)*3 == Order(x) assert -28*Order(x) == Order(x) assert Order(Order(x)) == Order(x) assert Order(Order(x), y) == Order(Order(x), x, y) assert Order(-23) == Order(1) assert Order(exp(x)) == Order(1, x) assert Order(exp(1/x)).expr == exp(1/x) assert Order(x*exp(1/x)).expr == x*exp(1/x) assert Order(x**(o/3)).expr == x**(o/3) assert Order(x**(5*o/3)).expr == x**(5*o/3) assert Order(x**2 + x + y, x) == O(1, x) assert Order(x**2 + x + y, y) == O(1, y) raises(ValueError, lambda: Order(exp(x), x, x)) raises(TypeError, lambda: Order(x, 2 - x)) def test_simple_2(): assert Order(2*x)*x == Order(x**2) assert Order(2*x)/x == Order(1, x) assert Order(2*x)*x*exp(1/x) == Order(x**2*exp(1/x)) assert (Order(2*x)*x*exp(1/x)/ln(x)**3).expr == x**2*exp(1/x)*ln(x)**-3 def test_simple_3(): assert Order(x) + x == Order(x) assert Order(x) + 2 == 2 + Order(x) assert Order(x) + x**2 == Order(x) assert Order(x) + 1/x == 1/x + Order(x) assert Order(1/x) + 1/x**2 == 1/x**2 + Order(1/x) assert Order(x) + exp(1/x) == Order(x) + exp(1/x) def test_simple_4(): assert Order(x)**2 == Order(x**2) def test_simple_5(): assert Order(x) + Order(x**2) == Order(x) assert Order(x) + Order(x**-2) == Order(x**-2) assert Order(x) + Order(1/x) == Order(1/x) def test_simple_6(): assert Order(x) - Order(x) == Order(x) assert Order(x) + Order(1) == Order(1) assert Order(x) + Order(x**2) == Order(x) assert Order(1/x) + Order(1) == Order(1/x) assert Order(x) + Order(exp(1/x)) == Order(exp(1/x)) assert Order(x**3) + Order(exp(2/x)) == Order(exp(2/x)) assert Order(x**-3) + Order(exp(2/x)) == Order(exp(2/x)) def test_simple_7(): assert 1 + O(1) == O(1) assert 2 + O(1) == O(1) assert x + O(1) == O(1) assert 1/x + O(1) == 1/x + O(1) def test_simple_8(): assert O(sqrt(-x)) == O(sqrt(x)) assert O(x**2*sqrt(x)) == O(x**(S(5)/2)) assert O(x**3*sqrt(-(-x)**3)) == O(x**(S(9)/2)) assert O(x**(S(3)/2)*sqrt((-x)**3)) == O(x**3) assert O(x*(-2*x)**(I/2)) == O(x*(-x)**(I/2)) def test_as_expr_variables(): assert Order(x).as_expr_variables(None) == (x, ((x, 0),)) assert Order(x).as_expr_variables((((x, 0),))) == (x, ((x, 0),)) assert Order(y).as_expr_variables(((x, 0),)) == (y, ((x, 0), (y, 0))) assert Order(y).as_expr_variables(((x, 0), (y, 0))) == (y, ((x, 0), (y, 0))) def test_contains_0(): assert Order(1, x).contains(Order(1, x)) assert Order(1, x).contains(Order(1)) assert Order(1).contains(Order(1, x)) is False def test_contains_1(): assert Order(x).contains(Order(x)) assert Order(x).contains(Order(x**2)) assert not Order(x**2).contains(Order(x)) assert not Order(x).contains(Order(1/x)) assert not Order(1/x).contains(Order(exp(1/x))) assert not Order(x).contains(Order(exp(1/x))) assert Order(1/x).contains(Order(x)) assert Order(exp(1/x)).contains(Order(x)) assert Order(exp(1/x)).contains(Order(1/x)) assert Order(exp(1/x)).contains(Order(exp(1/x))) assert Order(exp(2/x)).contains(Order(exp(1/x))) assert not Order(exp(1/x)).contains(Order(exp(2/x))) def test_contains_2(): assert Order(x).contains(Order(y)) is None assert Order(x).contains(Order(y*x)) assert Order(y*x).contains(Order(x)) assert Order(y).contains(Order(x*y)) assert Order(x).contains(Order(y**2*x)) def test_contains_3(): assert Order(x*y**2).contains(Order(x**2*y)) is None assert Order(x**2*y).contains(Order(x*y**2)) is None def test_contains(): assert Order(1, x) not in Order(1) assert Order(1) in Order(1, x) raises(TypeError, lambda: Order(x*y**2) in Order(x**2*y)) def test_add_1(): assert Order(x + x) == Order(x) assert Order(3*x - 2*x**2) == Order(x) assert Order(1 + x) == Order(1, x) assert Order(1 + 1/x) == Order(1/x) assert Order(ln(x) + 1/ln(x)) == Order(ln(x)) assert Order(exp(1/x) + x) == Order(exp(1/x)) assert Order(exp(1/x) + 1/x**20) == Order(exp(1/x)) def test_ln_args(): assert O(log(x)) + O(log(2*x)) == O(log(x)) assert O(log(x)) + O(log(x**3)) == O(log(x)) assert O(log(x*y)) + O(log(x) + log(y)) == O(log(x*y)) def test_multivar_0(): assert Order(x*y).expr == x*y assert Order(x*y**2).expr == x*y**2 assert Order(x*y, x).expr == x assert Order(x*y**2, y).expr == y**2 assert Order(x*y*z).expr == x*y*z assert Order(x/y).expr == x/y assert Order(x*exp(1/y)).expr == x*exp(1/y) assert Order(exp(x)*exp(1/y)).expr == exp(1/y) def test_multivar_0a(): assert Order(exp(1/x)*exp(1/y)).expr == exp(1/x + 1/y) def test_multivar_1(): assert Order(x + y).expr == x + y assert Order(x + 2*y).expr == x + y assert (Order(x + y) + x).expr == (x + y) assert (Order(x + y) + x**2) == Order(x + y) assert (Order(x + y) + 1/x) == 1/x + Order(x + y) assert Order(x**2 + y*x).expr == x**2 + y*x def test_multivar_2(): assert Order(x**2*y + y**2*x, x, y).expr == x**2*y + y**2*x def test_multivar_mul_1(): assert Order(x + y)*x == Order(x**2 + y*x, x, y) def test_multivar_3(): assert (Order(x) + Order(y)).args in [ (Order(x), Order(y)), (Order(y), Order(x))] assert Order(x) + Order(y) + Order(x + y) == Order(x + y) assert (Order(x**2*y) + Order(y**2*x)).args in [ (Order(x*y**2), Order(y*x**2)), (Order(y*x**2), Order(x*y**2))] assert (Order(x**2*y) + Order(y*x)) == Order(x*y) def test_issue369(): y = Symbol('y', negative=True) z = Symbol('z', complex=True) # check that Order does not modify assumptions about symbols Order(x) Order(y) Order(z) assert x.is_positive is None assert y.is_positive is False assert z.is_positive is None assert x.is_infinitesimal is None assert y.is_infinitesimal is None assert z.is_infinitesimal is None def test_leading_order(): assert (x + 1 + 1/x**5).extract_leading_order(x) == ((1/x**5, O(1/x**5)),) assert (1 + 1/x).extract_leading_order(x) == ((1/x, O(1/x)),) assert (1 + x).extract_leading_order(x) == ((1, O(1, x)),) assert (1 + x**2).extract_leading_order(x) == ((1, O(1, x)),) assert (2 + x**2).extract_leading_order(x) == ((2, O(1, x)),) assert (x + x**2).extract_leading_order(x) == ((x, O(x)),) def test_leading_order2(): assert set((2 + pi + x**2).extract_leading_order(x)) == set(((pi, O(1, x)), (S(2), O(1, x)))) assert set((2*x + pi*x + x**2).extract_leading_order(x)) == set(((2*x, O(x)), (x*pi, O(x)))) def test_order_leadterm(): assert O(x**2)._eval_as_leading_term(x) == O(x**2) def test_order_symbols(): e = x*y*sin(x)*Integral(x, (x, 1, 2)) assert O(e) == O(x**2*y, x, y) assert O(e, x) == O(x**2) def test_nan(): assert O(nan) == nan assert not O(x).contains(nan) def test_O1(): assert O(1, x) * x == O(x) assert O(1, y) * x == O(1, y) def test_getn(): # other lines are tested incidentally by the suite assert O(x).getn() == 1 assert O(x/log(x)).getn() == 1 assert O(x**2/log(x)**2).getn() == 2 assert O(x*log(x)).getn() == 1 raises(NotImplementedError, lambda: (O(x) + O(y)).getn()) def test_diff(): assert O(x**2).diff(x) == O(x) def test_getO(): assert (x).getO() is None assert (x).removeO() == x assert (O(x)).getO() == O(x) assert (O(x)).removeO() == 0 assert (z + O(x) + O(y)).getO() == O(x) + O(y) assert (z + O(x) + O(y)).removeO() == z raises(NotImplementedError, lambda: (O(x) + O(y)).getn()) def test_leading_term(): from sympy import digamma assert O(1/digamma(1/x)) == O(1/log(x)) def test_eval(): assert Order(x).subs(Order(x), 1) == 1 assert Order(x).subs(x, y) == Order(y) assert Order(x).subs(y, x) == Order(x) assert Order(x).subs(x, x + y) == Order(x + y) assert (O(1)**x).is_Pow def test_oseries(): assert Order(x).oseries(x) == Order(x) def test_issue_1180(): a, b = symbols('a b') assert O(a, a, b) + O(1, a, b) == O(1, a, b) assert O(b, a, b) + O(1, a, b) == O(1, a, b) assert O(a + b, a, b) + O(1, a, b) == O(1, a, b) assert O(1, a, b) + O(a, a, b) == O(1, a, b) assert O(1, a, b) + O(b, a, b) == O(1, a, b) assert O(1, a, b) + O(a + b, a, b) == O(1, a, b) def test_issue_1756(): assert 1/O(1) != O(1) assert 1/O(x) != O(1/x) assert 1/O(x, (x, oo)) != O(1/x, (x, oo)) f = Function('f') assert 1/O(f(x)) != O(1/x) def test_order_conjugate_transpose(): x = Symbol('x', real=True) y = Symbol('y', imaginary=True) assert conjugate(Order(x)) == Order(conjugate(x)) assert conjugate(Order(y)) == Order(conjugate(y)) assert conjugate(Order(x**2)) == Order(conjugate(x)**2) assert conjugate(Order(y**2)) == Order(conjugate(y)**2) assert transpose(Order(x)) == Order(transpose(x)) assert transpose(Order(y)) == Order(transpose(y)) assert transpose(Order(x**2)) == Order(transpose(x)**2) assert transpose(Order(y**2)) == Order(transpose(y)**2) def test_order_noncommutative(): A = Symbol('A', commutative=False) assert Order(A + A*x, x) == Order(1, x) assert (A + A*x)*Order(x) == Order(x) assert (A*x)*Order(x) == Order(x**2, x) assert expand((1 + Order(x))*A*A*x) == A*A*x + Order(x**2, x) assert expand((A*A + Order(x))*x) == A*A*x + Order(x**2, x) assert expand((A + Order(x))*A*x) == A*A*x + Order(x**2, x) def test_issue_3654(): assert (1 + x**2)**10000*O(x) == O(x) def test_order_at_infinity(): assert Order(1 + x, (x, oo)) == Order(x, (x, oo)) assert Order(3*x, (x, oo)) == Order(x, (x, oo)) assert Order(x, (x, oo))*3 == Order(x, (x, oo)) assert -28*Order(x, (x, oo)) == Order(x, (x, oo)) assert Order(Order(x, (x, oo)), (x, oo)) == Order(x, (x, oo)) assert Order(Order(x, (x, oo)), (y, oo)) == Order(x, (x, oo), (y, oo)) assert Order(3, (x, oo)) == Order(1, (x, oo)) assert Order(x**2 + x + y, (x, oo)) == O(x**2, (x, oo)) assert Order(x**2 + x + y, (y, oo)) == O(y, (y, oo)) assert Order(2*x, (x, oo))*x == Order(x**2, (x, oo)) assert Order(2*x, (x, oo))/x == Order(1, (x, oo)) assert Order(2*x, (x, oo))*x*exp(1/x) == Order(x**2*exp(1/x), (x, oo)) assert Order(2*x, (x, oo))*x*exp(1/x)/ln(x)**3 == Order(x**2*exp(1/x)*ln(x)**-3, (x, oo)) assert Order(x, (x, oo)) + 1/x == 1/x + Order(x, (x, oo)) == Order(x, (x, oo)) assert Order(x, (x, oo)) + 1 == 1 + Order(x, (x, oo)) == Order(x, (x, oo)) assert Order(x, (x, oo)) + x == x + Order(x, (x, oo)) == Order(x, (x, oo)) assert Order(x, (x, oo)) + x**2 == x**2 + Order(x, (x, oo)) assert Order(1/x, (x, oo)) + 1/x**2 == 1/x**2 + Order(1/x, (x, oo)) == Order(1/x, (x, oo)) assert Order(x, (x, oo)) + exp(1/x) == exp(1/x) + Order(x, (x, oo)) assert Order(x, (x, oo))**2 == Order(x**2, (x, oo)) assert Order(x, (x, oo)) + Order(x**2, (x, oo)) == Order(x**2, (x, oo)) assert Order(x, (x, oo)) + Order(x**-2, (x, oo)) == Order(x, (x, oo)) assert Order(x, (x, oo)) + Order(1/x, (x, oo)) == Order(x, (x, oo)) assert Order(x, (x, oo)) - Order(x, (x, oo)) == Order(x, (x, oo)) assert Order(x, (x, oo)) + Order(1, (x, oo)) == Order(x, (x, oo)) assert Order(x, (x, oo)) + Order(x**2, (x, oo)) == Order(x**2, (x, oo)) assert Order(1/x, (x, oo)) + Order(1, (x, oo)) == Order(1, (x, oo)) assert Order(x, (x, oo)) + Order(exp(1/x), (x, oo)) == Order(x, (x, oo)) assert Order(x**3, (x, oo)) + Order(exp(2/x), (x, oo)) == Order(x**3, (x, oo)) assert Order(x**-3, (x, oo)) + Order(exp(2/x), (x, oo)) == Order(exp(2/x), (x, oo)) # issue 4108 assert Order(exp(x), (x, oo)).expr == Order(2*exp(x), (x, oo)).expr == exp(x) assert Order(y**x, (x, oo)).expr == Order(2*y**x, (x, oo)).expr == y**x def test_mixing_order_at_zero_and_infinity(): assert (Order(x, (x, 0)) + Order(x, (x, oo))).is_Add assert Order(x, (x, 0)) + Order(x, (x, oo)) == Order(x, (x, oo)) + Order(x, (x, 0)) assert Order(Order(x, (x, oo))) == Order(x, (x, oo)) # not supported (yet) raises(NotImplementedError, lambda: Order(x, (x, 0))*Order(x, (x, oo))) raises(NotImplementedError, lambda: Order(x, (x, oo))*Order(x, (x, 0))) raises(NotImplementedError, lambda: Order(Order(x, (x, oo)), y)) raises(NotImplementedError, lambda: Order(Order(x), (x, oo))) def test_order_subs_limits(): # issue 234 assert (1 + Order(x)).subs(x, 1/x) == 1 + Order(1/x, (x, oo)) assert (1 + Order(x)).limit(x, 0) == 1 # issue 2670 assert ((x + Order(x**2))/x).limit(x, 0) == 1 sympy-0.7.4.1/sympy/series/tests/__init__.py0000644000175000017500000000000012253362407021160 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/series/__init__.py0000644000175000017500000000045512253362407020034 0ustar georgeskgeorgesk"""A module that handles series: find a limit, order the series etc. """ from .order import Order from .limits import limit, Limit from .gruntz import gruntz from .series import series from .residues import residue O = Order __all__ = ['gruntz', 'limit', 'series', 'O', 'Order', 'Limit', "residue"] sympy-0.7.4.1/sympy/sets/0000755000175000017500000000000012253362407015403 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/sets/fancysets.py0000644000175000017500000001755312253362407017767 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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, with_metaclass from sympy.core.sets import Set, Interval, FiniteSet, Intersection from sympy.core.singleton import Singleton, S from sympy.core.decorators import deprecated from sympy.solvers import solve oo = S.Infinity class Naturals(with_metaclass(Singleton, Set)): """ 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 _inf = S.One _sup = oo def _intersect(self, other): if other.is_Interval: return Intersection(S.Integers, other, Interval(self._inf, 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 = self._inf while True: yield i i = i + 1 class Naturals0(Naturals): """ The Natural Numbers starting at 0 See also: S.Naturals - starts at 1 """ _inf = S.Zero def _contains(self, other): if ask(Q.negative(other)) == False and ask(Q.integer(other)): return True return False class Integers(with_metaclass(Singleton, Set)): """ 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(with_metaclass(Singleton, Interval)): def __new__(cls): return Interval.__new__(cls, -oo, oo) class ImageSet(Set): """ Image of a set under a mathematical function Examples -------- >>> from sympy import Symbol, S, ImageSet, FiniteSet, Lambda >>> x = Symbol('x') >>> N = S.Naturals >>> squares = ImageSet(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 @deprecated(useinstead="ImageSet", issue=3958, deprecated_since_version="0.7.4") def TransformationSet(*args, **kwargs): """Deprecated alias for the ImageSet constructor.""" return ImageSet(*args, **kwargs) 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 ImageSets 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 sympy-0.7.4.1/sympy/sets/tests/0000755000175000017500000000000012253362407016545 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/sets/tests/test_fancysets.py0000644000175000017500000001141112253362407022153 0ustar georgeskgeorgeskfrom sympy.sets.fancysets import ImageSet, Range from sympy.core.sets import FiniteSet, Interval, imageset from sympy import (S, Symbol, Lambda, symbols, cos, sin, pi, oo, Basic, Rational, sqrt) from sympy.utilities.pytest import XFAIL import itertools x = Symbol('x') def test_naturals(): N = S.Naturals assert 5 in N assert -5 not in N assert 5.5 not in N ni = iter(N) a, b, c, d = next(ni), next(ni), next(ni), next(ni) assert (a, b, c, d) == (1, 2, 3, 4) assert isinstance(a, Basic) assert N.intersect(Interval(-5, 5)) == Range(1, 6) assert N.intersect(Interval(-5, 5, True, True)) == Range(1, 5) assert N.inf == 1 assert N.sup == oo def test_naturals0(): N = S.Naturals0 assert 0 in N assert next(iter(N)) == 0 def test_integers(): Z = S.Integers assert 5 in Z assert -5 in Z assert 5.5 not in Z zi = iter(Z) a, b, c, d = next(zi), next(zi), next(zi), next(zi) assert (a, b, c, d) == (0, 1, -1, 2) assert isinstance(a, Basic) assert Z.intersect(Interval(-5, 5)) == Range(-5, 6) assert Z.intersect(Interval(-5, 5, True, True)) == Range(-4, 5) assert Z.inf == -oo assert Z.sup == oo def test_ImageSet(): squares = ImageSet(Lambda(x, x**2), S.Naturals) assert 4 in squares assert 5 not in squares assert FiniteSet(range(10)).intersect(squares) == FiniteSet(1, 4, 9) assert 16 not in squares.intersect(Interval(0, 10)) si = iter(squares) a, b, c, d = next(si), next(si), next(si), next(si) assert (a, b, c, d) == (1, 4, 9, 16) harmonics = ImageSet(Lambda(x, 1/x), S.Naturals) assert Rational(1, 5) in harmonics assert .25 in harmonics assert .3 not in harmonics assert harmonics.is_iterable def test_image_is_ImageSet(): assert isinstance(imageset(x, sqrt(sin(x)), Range(5)), ImageSet) @XFAIL def test_halfcircle(): # This test sometimes works and sometimes doesn't. # It may be an issue with solve? Maybe with using Lambdas/dummys? # I believe the code within fancysets is correct r, th = symbols('r, theta', real=True) L = Lambda((r, th), (r*cos(th), r*sin(th))) halfcircle = ImageSet(L, Interval(0, 1)*Interval(0, pi)) assert (1, 0) in halfcircle assert (0, -1) not in halfcircle assert (0, 0) in halfcircle assert not halfcircle.is_iterable def test_ImageSet_iterator_not_injetive(): L = Lambda(x, x - x % 2) # produces 0, 2, 2, 4, 4, 6, 6, ... evens = ImageSet(L, S.Naturals) i = iter(evens) # No repeats here assert (next(i), next(i), next(i), next(i)) == (0, 2, 4, 6) def test_Range(): assert Range(5) == Range(0, 5) == Range(0, 5, 1) r = Range(10, 20, 2) assert 12 in r assert 8 not in r assert 11 not in r assert 30 not in r assert list(Range(0, 5)) == list(range(5)) assert list(Range(5, 0, -1)) == list(range(1, 6)) assert Range(5, 15).sup == 14 assert Range(5, 15).inf == 5 assert Range(15, 5, -1).sup == 15 assert Range(15, 5, -1).inf == 6 assert Range(10, 67, 10).sup == 60 assert Range(60, 7, -10).inf == 10 assert len(Range(10, 38, 10)) == 3 assert Range(0, 0, 5) == S.EmptySet def test_range_interval_intersection(): # Intersection with intervals assert FiniteSet(Range(0, 10, 1).intersect(Interval(2, 6))) == \ FiniteSet(2, 3, 4, 5, 6) # Open Intervals are removed assert (FiniteSet(Range(0, 10, 1).intersect(Interval(2, 6, True, True))) == FiniteSet(3, 4, 5)) # Try this with large steps assert (FiniteSet(Range(0, 100, 10).intersect(Interval(15, 55))) == FiniteSet(20, 30, 40, 50)) # Going backwards assert FiniteSet(Range(10, -9, -3).intersect(Interval(-5, 6))) == \ FiniteSet(-5, -2, 1, 4) assert FiniteSet(Range(10, -9, -3).intersect(Interval(-5, 6, True))) == \ FiniteSet(-2, 1, 4) def test_fun(): assert (FiniteSet(ImageSet(Lambda(x, sin(pi*x/4)), Range(-10, 11))) == FiniteSet(-1, -sqrt(2)/2, 0, sqrt(2)/2, 1)) def test_reals(): assert 5 in S.Reals assert S.Pi in S.Reals assert -sqrt(2) in S.Reals assert (2, 5) not in S.Reals @XFAIL # this is because contains is now very strict def test_reals_fail(): assert sqrt(-1) not in S.Reals def take(n, iterable): "Return first n items of the iterable as a list" return list(itertools.islice(iterable, n)) def test_intersections(): assert 5 in S.Integers.intersect(S.Reals) assert 5 in S.Integers.intersect(S.Reals) assert -5 not in S.Naturals.intersect(S.Reals) assert 5.5 not in S.Integers.intersect(S.Reals) assert 5 in S.Integers.intersect(Interval(3, oo)) assert -5 in S.Integers.intersect(Interval(-oo, 3)) assert all(x.is_Integer for x in take(10, S.Integers.intersect(Interval(3, oo)) )) sympy-0.7.4.1/sympy/sets/tests/__init__.py0000644000175000017500000000000012253362407020644 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/sets/__init__.py0000644000175000017500000000007212253362407017513 0ustar georgeskgeorgeskfrom .fancysets import TransformationSet, ImageSet, Range sympy-0.7.4.1/sympy/unify/0000755000175000017500000000000012253362407015557 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/unify/rewrite.py0000644000175000017500000000347212253362407017620 0ustar georgeskgeorgesk""" Functions to support rewriting of SymPy expressions """ from __future__ import print_function, division from sympy.unify.usympy import unify from sympy.unify.usympy import rebuild from sympy.strategies.tools import subs from sympy import Expr from sympy.assumptions import Q, ask def rewriterule(source, target, variables=(), condition=None, assume=None): """ Rewrite rule Transform expressions that match source into expressions that match target treating all `variables` as wilds. >>> from sympy.abc import w, x, y, z >>> from sympy.unify.rewrite import rewriterule >>> from sympy.utilities import default_sort_key >>> rl = rewriterule(x + y, x**y, [x, y]) >>> sorted(rl(z + 3), key=default_sort_key) [3**z, z**3] Use ``condition`` to specify additional requirements. Inputs are taken in the same order as is found in variables. >>> rl = rewriterule(x + y, x**y, [x, y], lambda x, y: x.is_integer) >>> list(rl(z + 3)) [3**z] Use ``assume`` to specify additional requirements using new assumptions. >>> from sympy.assumptions import Q >>> rl = rewriterule(x + y, x**y, [x, y], assume=Q.integer(x)) >>> list(rl(z + 3)) [3**z] Assumptions for the local context are provided at rule runtime >>> list(rl(w + z, Q.integer(z))) [z**w] """ def rewrite_rl(expr, assumptions=True): for match in unify(source, expr, {}, variables=variables): if (condition and not condition(*[match.get(var, var) for var in variables])): continue if (assume and not ask(assume.xreplace(match), assumptions)): continue expr2 = subs(match)(target) if isinstance(expr2, Expr): expr2 = rebuild(expr2) yield expr2 return rewrite_rl sympy-0.7.4.1/sympy/unify/usympy.py0000644000175000017500000000770712253362407017512 0ustar georgeskgeorgesk""" SymPy interface to Unification engine See sympy.unify for module level docstring See sympy.unify.core for algorithmic docstring """ from __future__ import print_function, division from sympy.core import Basic, Expr, Tuple, Add, Mul, Pow, FiniteSet from sympy.matrices import MatAdd, MatMul, MatrixExpr from sympy.core.sets import Union, Intersection, FiniteSet from sympy.core.operations import AssocOp, LatticeOp from sympy.unify.core import Compound, Variable, CondVariable from sympy.unify import core basic_new_legal = [MatrixExpr] eval_false_legal = [AssocOp, Pow, FiniteSet] illegal = [LatticeOp] def sympy_associative(op): assoc_ops = (AssocOp, MatAdd, MatMul, Union, Intersection, FiniteSet) return any(issubclass(op, aop) for aop in assoc_ops) def sympy_commutative(op): comm_ops = (Add, MatAdd, Union, Intersection, FiniteSet) return any(issubclass(op, cop) for cop in comm_ops) def is_associative(x): return isinstance(x, Compound) and sympy_associative(x.op) def is_commutative(x): if not isinstance(x, Compound): return False if sympy_commutative(x.op): return True if issubclass(x.op, Mul): return all(construct(arg).is_commutative for arg in x.args) def mk_matchtype(typ): def matchtype(x): return (isinstance(x, typ) or isinstance(x, Compound) and issubclass(x.op, typ)) return matchtype def deconstruct(s, variables=()): """ Turn a SymPy object into a Compound """ if s in variables: return Variable(s) if isinstance(s, (Variable, CondVariable)): return s if not isinstance(s, Basic) or s.is_Atom: return s return Compound(s.__class__, tuple(deconstruct(arg, variables) for arg in s.args)) def construct(t): """ Turn a Compound into a SymPy object """ if isinstance(t, (Variable, CondVariable)): return t.arg if not isinstance(t, Compound): return t if any(issubclass(t.op, cls) for cls in eval_false_legal): return t.op(*map(construct, t.args), evaluate=False) elif any(issubclass(t.op, cls) for cls in basic_new_legal): return Basic.__new__(t.op, *map(construct, t.args)) else: return t.op(*map(construct, t.args)) def rebuild(s): """ Rebuild a SymPy expression This removes harm caused by Expr-Rules interactions """ return construct(deconstruct(s)) def unify(x, y, s=None, variables=(), **kwargs): """ Structural unification of two expressions/patterns Examples ======== >>> from sympy.unify.usympy import unify >>> from sympy import Basic, cos >>> from sympy.abc import x, y, z, p, q >>> next(unify(Basic(1, 2), Basic(1, x), variables=[x])) {x: 2} >>> expr = 2*x + y + z >>> pattern = 2*p + q >>> next(unify(expr, pattern, {}, variables=(p, q))) {p: x, q: y + z} Unification supports commutative and associative matching >>> expr = x + y + z >>> pattern = p + q >>> len(list(unify(expr, pattern, {}, variables=(p, q)))) 12 Symbols not indicated to be variables are treated as literal, else they are wild-like and match anything in a sub-expression. >>> expr = x*y*z + 3 >>> pattern = x*y + 3 >>> next(unify(expr, pattern, {}, variables=[x, y])) {x: y, y: x*z} The x and y of the pattern above were in a Mul and matched factors in the Mul of expr. Here, a single symbol matches an entire term: >>> expr = x*y + 3 >>> pattern = p + 3 >>> next(unify(expr, pattern, {}, variables=[p])) {p: x*y} """ decons = lambda x: deconstruct(x, variables) s = s or {} s = dict((decons(k), decons(v)) for k, v in s.items()) ds = core.unify(decons(x), decons(y), s, is_associative=is_associative, is_commutative=is_commutative, **kwargs) for d in ds: yield dict((construct(k), construct(v)) for k, v in d.items()) sympy-0.7.4.1/sympy/unify/core.py0000644000175000017500000001574212253362407017072 0ustar georgeskgeorgesk""" Generic Unification algorithm for expression trees with lists of children This implementation is a direct translation of Artificial Intelligence: A Modern Approach by Stuart Russel and Peter Norvig Second edition, section 9.2, page 276 It is modified in the following ways: 1. We allow associative and commutative Compound expressions. This results in combinatorial blowup. 2. We explore the tree lazily. 3. We provide generic interfaces to symbolic algebra libraries in Python. A more traditional version can be found here http://aima.cs.berkeley.edu/python/logic.html """ from __future__ import print_function, division from sympy.utilities.iterables import kbins class Compound(object): """ A little class to represent an interior node in the tree This is analagous to SymPy.Basic for non-Atoms """ def __init__(self, op, args): self.op = op self.args = args def __eq__(self, other): return (type(self) == type(other) and self.op == other.op and self.args == other.args) def __hash__(self): return hash((type(self), self.op, self.args)) def __str__(self): return "%s[%s]" % (str(self.op), ', '.join(map(str, self.args))) class Variable(object): """ A Wild token """ def __init__(self, arg): self.arg = arg def __eq__(self, other): return type(self) == type(other) and self.arg == other.arg def __hash__(self): return hash((type(self), self.arg)) def __str__(self): return "Variable(%s)" % str(self.arg) class CondVariable(object): """ A wild token that matches conditionally arg - a wild token valid - an additional constraining function on a match """ def __init__(self, arg, valid): self.arg = arg self.valid = valid def __eq__(self, other): return (type(self) == type(other) and self.arg == other.arg and self.valid == other.valid) def __hash__(self): return hash((type(self), self.arg, self.valid)) def __str__(self): return "CondVariable(%s)" % str(self.arg) def unify(x, y, s=None, **fns): """ Unify two expressions inputs: x, y - expression trees containing leaves, Compounds and Variables s - a mapping of variables to subtrees outputs: lazy sequence of mappings {Variable: subtree} Example ======= >>> from sympy.unify.core import unify, Compound, Variable >>> expr = Compound("Add", ("x", "y")) >>> pattern = Compound("Add", ("x", Variable("a"))) >>> next(unify(expr, pattern, {})) {Variable(a): 'y'} """ s = s or {} if x == y: yield s elif isinstance(x, (Variable, CondVariable)): for match in unify_var(x, y, s, **fns): yield match elif isinstance(y, (Variable, CondVariable)): for match in unify_var(y, x, s, **fns): yield match elif isinstance(x, Compound) and isinstance(y, Compound): is_commutative = fns.get('is_commutative', lambda x: False) is_associative = fns.get('is_associative', lambda x: False) for sop in unify(x.op, y.op, s, **fns): if is_associative(x) and is_associative(y): a, b = (x, y) if len(x.args) < len(y.args) else (y, x) if is_commutative(x) and is_commutative(y): combs = allcombinations(a.args, b.args, 'commutative') else: combs = allcombinations(a.args, b.args, 'associative') for aaargs, bbargs in combs: aa = [unpack(Compound(a.op, arg)) for arg in aaargs] bb = [unpack(Compound(b.op, arg)) for arg in bbargs] for match in unify(aa, bb, sop, **fns): yield match elif len(x.args) == len(y.args): for match in unify(x.args, y.args, sop, **fns): yield match elif is_args(x) and is_args(y) and len(x) == len(y): if len(x) == 0: yield s else: for shead in unify(x[0], y[0], s, **fns): for match in unify(x[1:], y[1:], shead, **fns): yield match def unify_var(var, x, s, **fns): if var in s: for match in unify(s[var], x, s, **fns): yield match elif occur_check(var, x): pass elif isinstance(var, CondVariable) and var.valid(x): yield assoc(s, var, x) elif isinstance(var, Variable): yield assoc(s, var, x) def occur_check(var, x): """ var occurs in subtree owned by x? """ if var == x: return True elif isinstance(x, Compound): return occur_check(var, x.args) elif is_args(x): if any(occur_check(var, xi) for xi in x): return True return False def assoc(d, key, val): """ Return copy of d with key associated to val """ d = d.copy() d[key] = val return d def is_args(x): """ Is x a traditional iterable? """ return type(x) in (tuple, list, set) def unpack(x): if isinstance(x, Compound) and len(x.args) == 1: return x.args[0] else: return x def allcombinations(A, B, ordered): """ Restructure A and B to have the same number of elements ordered must be either 'commutative' or 'associative' A and B can be rearranged so that the larger of the two lists is reorganized into smaller sublists. >>> from sympy.unify.core import allcombinations >>> for x in allcombinations((1, 2, 3), (5, 6), 'associative'): print(x) (((1,), (2, 3)), ((5,), (6,))) (((1, 2), (3,)), ((5,), (6,))) >>> for x in allcombinations((1, 2, 3), (5, 6), 'commutative'): print(x) (((1,), (2, 3)), ((5,), (6,))) (((1, 2), (3,)), ((5,), (6,))) (((1,), (3, 2)), ((5,), (6,))) (((1, 3), (2,)), ((5,), (6,))) (((2,), (1, 3)), ((5,), (6,))) (((2, 1), (3,)), ((5,), (6,))) (((2,), (3, 1)), ((5,), (6,))) (((2, 3), (1,)), ((5,), (6,))) (((3,), (1, 2)), ((5,), (6,))) (((3, 1), (2,)), ((5,), (6,))) (((3,), (2, 1)), ((5,), (6,))) (((3, 2), (1,)), ((5,), (6,))) """ if ordered == "commutative": ordered = 11 if ordered == "associative": ordered = None sm, bg = (A, B) if len(A) < len(B) else (B, A) for part in kbins(range(len(bg)), len(sm), ordered=ordered): if bg == B: yield tuple((a,) for a in A), partition(B, part) else: yield partition(A, part), tuple((b,) for b in B) def partition(it, part): """ Partition a tuple/list into pieces defined by indices >>> from sympy.unify.core import partition >>> partition((10, 20, 30, 40), [[0, 1, 2], [3]]) ((10, 20, 30), (40,)) """ return type(it)([index(it, ind) for ind in part]) def index(it, ind): """ Fancy indexing into an indexable iterable (tuple, list) >>> from sympy.unify.core import index >>> index([10, 20, 30], (1, 2, 0)) [20, 30, 10] """ return type(it)([it[i] for i in ind]) sympy-0.7.4.1/sympy/unify/tests/0000755000175000017500000000000012253362407016721 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/unify/tests/test_sympy.py0000644000175000017500000001254012253362407021515 0ustar georgeskgeorgeskfrom sympy import Add, Basic, symbols, Mul, And, Symbol from sympy.unify.core import Compound, Variable from sympy.unify.usympy import (deconstruct, construct, unify, is_associative, is_commutative) from sympy.abc import w, x, y, z, n, m, k from sympy.utilities.pytest import XFAIL def test_deconstruct(): expr = Basic(1, 2, 3) expected = Compound(Basic, (1, 2, 3)) assert deconstruct(expr) == expected assert deconstruct(1) == 1 assert deconstruct(x) == x assert deconstruct(x, variables=(x,)) == Variable(x) assert deconstruct(Add(1, x, evaluate=False)) == Compound(Add, (1, x)) assert deconstruct(Add(1, x, evaluate=False), variables=(x,)) == \ Compound(Add, (1, Variable(x))) def test_construct(): expr = Compound(Basic, (1, 2, 3)) expected = Basic(1, 2, 3) assert construct(expr) == expected def test_nested(): expr = Basic(1, Basic(2), 3) cmpd = Compound(Basic, (1, Compound(Basic, (2,)), 3)) assert deconstruct(expr) == cmpd assert construct(cmpd) == expr def test_unify(): expr = Basic(1, 2, 3) a, b, c = map(Symbol, 'abc') pattern = Basic(a, b, c) assert list(unify(expr, pattern, {}, (a, b, c))) == [{a: 1, b: 2, c: 3}] assert list(unify(expr, pattern, variables=(a, b, c))) == \ [{a: 1, b: 2, c: 3}] def test_unify_variables(): assert list(unify(Basic(1, 2), Basic(1, x), {}, variables=(x,))) == [{x: 2}] def test_s_input(): expr = Basic(1, 2) a, b = map(Symbol, 'ab') pattern = Basic(a, b) assert list(unify(expr, pattern, {}, (a, b))) == [{a: 1, b: 2}] assert list(unify(expr, pattern, {a: 5}, (a, b))) == [] def iterdicteq(a, b): a = tuple(a) b = tuple(b) return len(a) == len(b) and all(x in b for x in a) def test_unify_commutative(): expr = Add(1, 2, 3, evaluate=False) a, b, c = map(Symbol, 'abc') pattern = Add(a, b, c, evaluate=False) result = tuple(unify(expr, pattern, {}, (a, b, c))) expected = ({a: 1, b: 2, c: 3}, {a: 1, b: 3, c: 2}, {a: 2, b: 1, c: 3}, {a: 2, b: 3, c: 1}, {a: 3, b: 1, c: 2}, {a: 3, b: 2, c: 1}) assert iterdicteq(result, expected) def test_unify_iter(): expr = Add(1, 2, 3, evaluate=False) a, b, c = map(Symbol, 'abc') pattern = Add(a, c, evaluate=False) assert is_associative(deconstruct(pattern)) assert is_commutative(deconstruct(pattern)) result = list(unify(expr, pattern, {}, (a, c))) expected = [{a: 1, c: Add(2, 3, evaluate=False)}, {a: 1, c: Add(3, 2, evaluate=False)}, {a: 2, c: Add(1, 3, evaluate=False)}, {a: 2, c: Add(3, 1, evaluate=False)}, {a: 3, c: Add(1, 2, evaluate=False)}, {a: 3, c: Add(2, 1, evaluate=False)}, {a: Add(1, 2, evaluate=False), c: 3}, {a: Add(2, 1, evaluate=False), c: 3}, {a: Add(1, 3, evaluate=False), c: 2}, {a: Add(3, 1, evaluate=False), c: 2}, {a: Add(2, 3, evaluate=False), c: 1}, {a: Add(3, 2, evaluate=False), c: 1}] assert iterdicteq(result, expected) def test_hard_match(): from sympy import sin, cos expr = sin(x) + cos(x)**2 p, q = map(Symbol, 'pq') pattern = sin(p) + cos(p)**2 assert list(unify(expr, pattern, {}, (p, q))) == [{p: x}] def test_matrix(): from sympy import MatrixSymbol X = MatrixSymbol('X', n, n) Y = MatrixSymbol('Y', 2, 2) Z = MatrixSymbol('Z', 2, 3) assert list(unify(X, Y, {}, variables=[n, 'X'])) == [{'X': 'Y', n: 2}] assert list(unify(X, Z, {}, variables=[n, 'X'])) == [] def test_non_frankenAdds(): # the is_commutative property used to fail because of Basic.__new__ # This caused is_commutative and str calls to fail expr = x+y*2 rebuilt = construct(deconstruct(expr)) # Ensure that we can run these commands without causing an error str(rebuilt) rebuilt.is_commutative def test_FiniteSet_commutivity(): from sympy import FiniteSet a, b, c, x, y = symbols('a,b,c,x,y') s = FiniteSet(a, b, c) t = FiniteSet(x, y) variables = (x, y) assert {x: FiniteSet(a, c), y: b} in tuple(unify(s, t, variables=variables)) def test_FiniteSet_complex(): from sympy import FiniteSet a, b, c, x, y, z = symbols('a,b,c,x,y,z') expr = FiniteSet(Basic(1, x), y, Basic(x, z)) pattern = FiniteSet(a, Basic(x, b)) variables = a, b expected = tuple([{b: 1, a: FiniteSet(y, Basic(x, z))}, {b: z, a: FiniteSet(y, Basic(1, x))}]) assert iterdicteq(unify(expr, pattern, variables=variables), expected) @XFAIL def test_and(): variables = x, y str(list(unify((x>0) & (z<3), pattern, variables=variables))) def test_Union(): from sympy import Interval assert list(unify(Interval(0, 1) + Interval(10, 11), Interval(0, 1) + Interval(12, 13), variables=(Interval(12, 13),))) def test_is_commutative(): assert is_commutative(deconstruct(x+y)) assert is_commutative(deconstruct(x*y)) assert not is_commutative(deconstruct(x**y)) def test_commutative_in_commutative(): from sympy.abc import a,b,c,d from sympy import sin, cos eq = sin(3)*sin(4)*sin(5) + 4*cos(3)*cos(4) pat = a*cos(b)*cos(c) + d*sin(b)*sin(c) assert next(unify(eq, pat, variables=(a,b,c,d))) sympy-0.7.4.1/sympy/unify/tests/test_unify.py0000644000175000017500000000600112253362407021461 0ustar georgeskgeorgeskfrom sympy.unify.core import Compound, Variable, CondVariable, allcombinations from sympy.unify import core a,b,c = 'abc' w,x,y,z = map(Variable, 'wxyz') C = Compound def is_associative(x): return isinstance(x, Compound) and (x.op in ('Add', 'Mul', 'CAdd', 'CMul')) def is_commutative(x): return isinstance(x, Compound) and (x.op in ('CAdd', 'CMul')) def unify(a, b, s={}): return core.unify(a, b, s=s, is_associative=is_associative, is_commutative=is_commutative) def test_basic(): assert list(unify(a, x, {})) == [{x: a}] assert list(unify(a, x, {x: 10})) == [] assert list(unify(1, x, {})) == [{x: 1}] assert list(unify(a, a, {})) == [{}] assert list(unify((w, x), (y, z), {})) == [{w: y, x: z}] assert list(unify(x, (a, b), {})) == [{x: (a, b)}] assert list(unify((a, b), (x, x), {})) == [] assert list(unify((y, z), (x, x), {}))!= [] assert list(unify((a, (b, c)), (a, (x, y)), {})) == [{x: b, y: c}] def test_ops(): assert list(unify(C('Add', (a,b,c)), C('Add', (a,x,y)), {})) == \ [{x:b, y:c}] assert list(unify(C('Add', (C('Mul', (1,2)), b,c)), C('Add', (x,y,c)), {})) == \ [{x: C('Mul', (1,2)), y:b}] def test_associative(): c1 = C('Add', (1,2,3)) c2 = C('Add', (x,y)) result = list(unify(c1, c2, {})) assert tuple(unify(c1, c2, {})) == ({x: 1, y: C('Add', (2, 3))}, {x: C('Add', (1, 2)), y: 3}) def test_commutative(): c1 = C('CAdd', (1,2,3)) c2 = C('CAdd', (x,y)) result = list(unify(c1, c2, {})) assert {x: 1, y: C('CAdd', (2, 3))} in result assert ({x: 2, y: C('CAdd', (1, 3))} in result or {x: 2, y: C('CAdd', (3, 1))} in result) def _test_combinations_assoc(): assert set(allcombinations((1,2,3), (a,b), True)) == \ set(((((1, 2), (3,)), (a, b)), (((1,), (2, 3)), (a, b)))) def _test_combinations_comm(): assert set(allcombinations((1,2,3), (a,b), None)) == \ set(((((1,), (2, 3)), ('a', 'b')), (((2,), (3, 1)), ('a', 'b')), (((3,), (1, 2)), ('a', 'b')), (((1, 2), (3,)), ('a', 'b')), (((2, 3), (1,)), ('a', 'b')), (((3, 1), (2,)), ('a', 'b')))) def test_allcombinations(): assert set(allcombinations((1,2), (1,2), 'commutative')) ==\ set(((((1,),(2,)), ((1,),(2,))), (((1,),(2,)), ((2,),(1,))))) def test_commutativity(): c1 = Compound('CAdd', (a, b)) c2 = Compound('CAdd', (x, y)) assert is_commutative(c1) and is_commutative(c2) assert len(list(unify(c1, c2, {}))) == 2 def test_CondVariable(): expr = C('CAdd', (1, 2)) x = Variable('x') y = CondVariable('y', lambda a: a % 2 == 0) z = CondVariable('z', lambda a: a > 3) pattern = C('CAdd', (x, y)) assert list(unify(expr, pattern, {})) == \ [{x: 1, y: 2}] z = CondVariable('z', lambda a: a > 3) pattern = C('CAdd', (z, y)) assert list(unify(expr, pattern, {})) == [] def test_defaultdict(): assert next(unify(Variable('x'), 'foo')) == {Variable('x'): 'foo'} sympy-0.7.4.1/sympy/unify/tests/test_rewrite.py0000644000175000017500000000347712253362407022026 0ustar georgeskgeorgeskfrom sympy.unify.rewrite import rewriterule from sympy import sin, cos, Basic, Symbol, S from sympy.abc import x, y, z from sympy.strategies.rl import rebuild from sympy.assumptions import Q p, q = Symbol('p'), Symbol('q') def test_simple(): rl = rewriterule(Basic(p, 1), Basic(p, 2), variables=(p,)) assert list(rl(Basic(3, 1))) == [Basic(3, 2)] p1 = p**2 p2 = p**3 rl = rewriterule(p1, p2, variables=(p,)) expr = x**2 assert list(rl(expr)) == [x**3] def test_simple_variables(): rl = rewriterule(Basic(x, 1), Basic(x, 2), variables=(x,)) assert list(rl(Basic(3, 1))) == [Basic(3, 2)] rl = rewriterule(x**2, x**3, variables=(x,)) assert list(rl(y**2)) == [y**3] def test_moderate(): p1 = p**2 + q**3 p2 = (p*q)**4 rl = rewriterule(p1, p2, (p, q)) expr = x**2 + y**3 assert list(rl(expr)) == [(x*y)**4] def test_sincos(): p1 = sin(p)**2 + sin(p)**2 p2 = 1 rl = rewriterule(p1, p2, (p, q)) assert list(rl(sin(x)**2 + sin(x)**2)) == [1] assert list(rl(sin(y)**2 + sin(y)**2)) == [1] def test_Exprs_ok(): rl = rewriterule(p+q, q+p, (p, q)) next(rl(x+y)).is_commutative str(next(rl(x+y))) def test_condition_simple(): rl = rewriterule(x, x+1, [x], lambda x: x < 10) assert not list(rl(S(15))) assert rebuild(next(rl(S(5)))) == 6 def test_condition_multiple(): rl = rewriterule(x + y, x**y, [x,y], lambda x, y: x.is_integer) a = Symbol('a') b = Symbol('b', integer=True) expr = a + b assert list(rl(expr)) == [b**a] c = Symbol('c', integer=True) d = Symbol('d', integer=True) assert set(rl(c + d)) == set([c**d, d**c]) def test_assumptions(): rl = rewriterule(x + y, x**y, [x, y], assume=Q.integer(x)) a, b = map(Symbol, 'ab') expr = a + b assert list(rl(expr, Q.integer(b))) == [b**a] sympy-0.7.4.1/sympy/unify/tests/__init__.py0000644000175000017500000000000012253362407021020 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/unify/__init__.py0000644000175000017500000000035212253362407017670 0ustar georgeskgeorgesk""" Unification in SymPy See sympy.unify.core docstring for algorithmic details See http://matthewrocklin.com/blog/work/2012/11/01/Unification/ for discussion """ from .usympy import unify, rebuild from .rewrite import rewriterule sympy-0.7.4.1/sympy/stats/0000755000175000017500000000000012253362407015563 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/stats/rv_interface.py0000644000175000017500000001207412253362407020610 0ustar georgeskgeorgeskfrom __future__ import print_function, division from .rv import (probability, expectation, density, where, given, pspace, cdf, sample, sample_iter, random_symbols, independent, dependent) from sympy import sqrt, simplify __all__ = ['P', 'E', 'density', 'where', 'given', 'sample', 'cdf', 'pspace', 'sample_iter', 'variance', 'std', 'skewness', 'covariance', 'dependent', 'independent', 'random_symbols', 'correlation', 'moment', 'cmoment'] def moment(X, n, c=0, condition=None, **kwargs): """ Return the nth moment of a random expression about c i.e. E((X-c)**n) Default value of c is 0. Examples ======== >>> from sympy.stats import Die, moment, E >>> X = Die('X', 6) >>> moment(X, 1, 6) -5/2 >>> moment(X, 2) 91/6 >>> moment(X, 1) == E(X) True """ return expectation((X - c)**n, condition, **kwargs) def variance(X, condition=None, **kwargs): """ Variance of a random expression Expectation of (X-E(X))**2 Examples ======== >>> from sympy.stats import Die, E, Bernoulli, variance >>> from sympy import simplify, Symbol >>> X = Die('X', 6) >>> p = Symbol('p') >>> B = Bernoulli('B', p, 1, 0) >>> variance(2*X) 35/3 >>> simplify(variance(B)) p*(-p + 1) """ return cmoment(X, 2, condition, **kwargs) def standard_deviation(X, condition=None, **kwargs): """ Standard Deviation of a random expression Square root of the Expectation of (X-E(X))**2 Examples ======== >>> from sympy.stats import Bernoulli, std >>> from sympy import Symbol, simplify >>> p = Symbol('p') >>> B = Bernoulli('B', p, 1, 0) >>> simplify(std(B)) sqrt(p*(-p + 1)) """ return sqrt(variance(X, condition, **kwargs)) std = standard_deviation def covariance(X, Y, condition=None, **kwargs): """ Covariance of two random expressions The expectation that the two variables will rise and fall together Covariance(X,Y) = E( (X-E(X)) * (Y-E(Y)) ) Examples ======== >>> from sympy.stats import Exponential, covariance >>> from sympy import Symbol >>> rate = Symbol('lambda', positive=True, real=True, bounded = True) >>> X = Exponential('X', rate) >>> Y = Exponential('Y', rate) >>> covariance(X, X) lambda**(-2) >>> covariance(X, Y) 0 >>> covariance(X, Y + rate*X) 1/lambda """ return expectation( (X - expectation(X, condition, **kwargs)) * (Y - expectation(Y, condition, **kwargs)), condition, **kwargs) def correlation(X, Y, condition=None, **kwargs): """ Correlation of two random expressions, also known as correlation coefficient or Pearson's correlation The normalized expectation that the two variables will rise and fall together Correlation(X,Y) = E( (X-E(X)) * (Y-E(Y)) / (sigma(X) * sigma(Y)) ) Examples ======== >>> from sympy.stats import Exponential, correlation >>> from sympy import Symbol >>> rate = Symbol('lambda', positive=True, real=True, bounded = True) >>> X = Exponential('X', rate) >>> Y = Exponential('Y', rate) >>> correlation(X, X) 1 >>> correlation(X, Y) 0 >>> correlation(X, Y + rate*X) 1/sqrt(1 + lambda**(-2)) """ return covariance(X, Y, condition, **kwargs)/(std(X, condition, **kwargs) * std(Y, condition, **kwargs)) def cmoment(X, n, condition=None, **kwargs): """ Return the nth central moment of a random expression about its mean i.e. E((X - E(X))**n) Examples ======== >>> from sympy.stats import Die, cmoment, variance >>> X = Die('X', 6) >>> cmoment(X, 3) 0 >>> cmoment(X, 2) 35/12 >>> cmoment(X, 2) == variance(X) True """ mu = expectation(X, condition, **kwargs) return moment(X, n, mu, condition, **kwargs) def smoment(X, n, condition=None, **kwargs): """ Return the nth Standardized moment of a random expression i.e. E( ((X - mu)/sigma(X))**n ) Examples ======== >>> from sympy.stats import skewness, Exponential, smoment >>> from sympy import Symbol >>> rate = Symbol('lambda', positive=True, real=True, bounded = True) >>> Y = Exponential('Y', rate) >>> smoment(Y, 4) 9 >>> smoment(Y, 4) == smoment(3*Y, 4) True >>> smoment(Y, 3) == skewness(Y) True """ sigma = std(X, condition, **kwargs) return (1/sigma)**n*cmoment(X, n, condition, **kwargs) def skewness(X, condition=None, **kwargs): """ Measure of the asymmetry of the probability distribution Positive skew indicates that most of the values lie to the right of the mean skewness(X) = E( ((X - E(X))/sigma)**3 ) Examples ======== >>> from sympy.stats import skewness, Exponential, Normal >>> from sympy import Symbol >>> X = Normal('X', 0, 1) >>> skewness(X) 0 >>> rate = Symbol('lambda', positive=True, real=True, bounded = True) >>> Y = Exponential('Y', rate) >>> skewness(Y) 2 """ return smoment(X, 3, condition, **kwargs) P = probability E = expectation sympy-0.7.4.1/sympy/stats/crv_types.py0000644000175000017500000016664312253362407020173 0ustar georgeskgeorgesk""" 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 __future__ import print_function, division from sympy import (exp, log, sqrt, pi, S, Dummy, Interval, S, sympify, gamma, Piecewise, And, Eq, binomial, factorial, Sum, floor, Abs, Symbol, log, besseli, Lambda, Basic) from sympy import beta as beta_fn from sympy import cos, exp, besseli from sympy.stats.crv import (SingleContinuousPSpace, SingleContinuousDistribution, ContinuousDistributionHandmade) from sympy.stats.rv import _value_check 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 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 """ pdf = Lambda(symbol, density) dist = ContinuousDistributionHandmade(pdf, set) return SingleContinuousPSpace(symbol, dist).value def rv(symbol, cls, args): args = list(map(sympify, args)) dist = cls(*args) dist.check(*args) return SingleContinuousPSpace(symbol, dist).value ######################################## # Continuous Probability Distributions # ######################################## #------------------------------------------------------------------------------- # Arcsin distribution ---------------------------------------------------------- class ArcsinDistribution(SingleContinuousDistribution): _argnames = ('a', 'b') def pdf(self, x): return 1/(pi*sqrt((x - self.a)*(self.b - x))) 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) >>> z = Symbol("z") >>> X = Arcsin("x", a, b) >>> density(X)(z) 1/(pi*sqrt((-a + z)*(b - z))) References ========== .. [1] http://en.wikipedia.org/wiki/Arcsine_distribution """ return rv(name, ArcsinDistribution, (a, b)) #------------------------------------------------------------------------------- # Benini distribution ---------------------------------------------------------- class BeniniDistribution(SingleContinuousDistribution): _argnames = ('alpha', 'beta', 'sigma') @property def set(self): return Interval(self.sigma, oo) def pdf(self, x): alpha, beta, sigma = self.alpha, self.beta, self.sigma return (exp(-alpha*log(x/sigma) - beta*log(x/sigma)**2) *(alpha/x + 2*beta*log(x/sigma)/x)) 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^2\left[{\frac{x}{\sigma}}\right]} \left(\frac{\alpha}{x}+\frac{2\beta\log{\frac{x}{\sigma}}}{x}\right) This is a heavy-tailed distrubtion and is also known as the log-Rayleigh distribution. 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) >>> z = Symbol("z") >>> X = Benini("x", alpha, beta, sigma) >>> D = density(X)(z) >>> pprint(D, use_unicode=False) / / z \\ / z \ 2/ z \ | 2*beta*log|-----|| - alpha*log|-----| - beta*log |-----| |alpha \sigma/| \sigma/ \sigma/ |----- + -----------------|*e \ z z / References ========== .. [1] http://en.wikipedia.org/wiki/Benini_distribution .. [2] http://reference.wolfram.com/legacy/v8/ref/BeniniDistribution.html """ return rv(name, BeniniDistribution, (alpha, beta, sigma)) #------------------------------------------------------------------------------- # Beta distribution ------------------------------------------------------------ class BetaDistribution(SingleContinuousDistribution): _argnames = ('alpha', 'beta') set = Interval(0, 1) @staticmethod def check(alpha, beta): _value_check(alpha > 0, "Alpha must be positive") _value_check(beta > 0, "Beta must be positive") def pdf(self, x): alpha, beta = self.alpha, self.beta return x**(alpha - 1) * (1 - x)**(beta - 1) / beta_fn(alpha, beta) def sample(self): return 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) >>> z = Symbol("z") >>> X = Beta("x", alpha, beta) >>> D = density(X)(z) >>> pprint(D, use_unicode=False) alpha - 1 beta - 1 z *(-z + 1) *gamma(alpha + beta) ----------------------------------------------- gamma(alpha)*gamma(beta) >>> simplify(E(X, meijerg=True)) alpha/(alpha + beta) >>> simplify(variance(X, meijerg=True)) #doctest: +SKIP 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 rv(name, BetaDistribution, (alpha, beta)) #------------------------------------------------------------------------------- # Beta prime distribution ------------------------------------------------------ class BetaPrimeDistribution(SingleContinuousDistribution): _argnames = ('alpha', 'beta') set = Interval(0, oo) def pdf(self, x): alpha, beta = self.alpha, self.beta return x**(alpha - 1)*(1 + x)**(-alpha - beta)/beta_fn(alpha, beta) 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) >>> z = Symbol("z") >>> X = BetaPrime("x", alpha, beta) >>> D = density(X)(z) >>> pprint(D, use_unicode=False) alpha - 1 -alpha - beta z *(z + 1) *gamma(alpha + beta) --------------------------------------------------- gamma(alpha)*gamma(beta) References ========== .. [1] http://en.wikipedia.org/wiki/Beta_prime_distribution .. [2] http://mathworld.wolfram.com/BetaPrimeDistribution.html """ return rv(name, BetaPrimeDistribution, (alpha, beta)) #------------------------------------------------------------------------------- # Cauchy distribution ---------------------------------------------------------- class CauchyDistribution(SingleContinuousDistribution): _argnames = ('x0', 'gamma') def pdf(self, x): return 1/(pi*self.gamma*(1 + ((x - self.x0)/self.gamma)**2)) 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) >>> z = Symbol("z") >>> X = Cauchy("x", x0, gamma) >>> density(X)(z) 1/(pi*gamma*(1 + (-x0 + z)**2/gamma**2)) References ========== .. [1] http://en.wikipedia.org/wiki/Cauchy_distribution .. [2] http://mathworld.wolfram.com/CauchyDistribution.html """ return rv(name, CauchyDistribution, (x0, gamma)) #------------------------------------------------------------------------------- # Chi distribution ------------------------------------------------------------- class ChiDistribution(SingleContinuousDistribution): _argnames = ('k',) set = Interval(0, oo) def pdf(self, x): return 2**(1 - self.k/2)*x**(self.k - 1)*exp(-x**2/2)/gamma(self.k/2) 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 : A positive 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) >>> z = Symbol("z") >>> X = Chi("x", k) >>> density(X)(z) 2**(-k/2 + 1)*z**(k - 1)*exp(-z**2/2)/gamma(k/2) References ========== .. [1] http://en.wikipedia.org/wiki/Chi_distribution .. [2] http://mathworld.wolfram.com/ChiDistribution.html """ return rv(name, ChiDistribution, (k,)) #------------------------------------------------------------------------------- # Non-central Chi distribution ------------------------------------------------- class ChiNoncentralDistribution(SingleContinuousDistribution): _argnames = ('k', 'l') set = Interval(0, oo) def pdf(self, x): k, l = self.k, self.l return exp(-(x**2+l**2)/2)*x**k*l / (l*x)**(k/2) * besseli(k/2-1, l*x) 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 `x \geq 0`. Here, `I_\nu (x)` is the :ref:`modified Bessel function of the first kind `. Parameters ========== k : A positive Integer, `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") >>> z = Symbol("z") >>> X = ChiNoncentral("x", k, l) >>> density(X)(z) l*z**k*(l*z)**(-k/2)*exp(-l**2/2 - z**2/2)*besseli(k/2 - 1, l*z) References ========== .. [1] http://en.wikipedia.org/wiki/Noncentral_chi_distribution """ return rv(name, ChiNoncentralDistribution, (k, l)) #------------------------------------------------------------------------------- # Chi squared distribution ----------------------------------------------------- class ChiSquaredDistribution(SingleContinuousDistribution): _argnames = ('k',) set = Interval(0, oo) def pdf(self, x): k = self.k return 1/(2**(k/2)*gamma(k/2))*x**(k/2 - 1)*exp(-x/2) 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 : A positive 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) >>> z = Symbol("z") >>> X = ChiSquared("x", k) >>> density(X)(z) 2**(-k/2)*z**(k/2 - 1)*exp(-z/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 rv(name, ChiSquaredDistribution, (k, )) #------------------------------------------------------------------------------- # Dagum distribution ----------------------------------------------------------- class DagumDistribution(SingleContinuousDistribution): _argnames = ('p', 'a', 'b') def pdf(self, x): p, a, b = self.p, self.a, self.b return a*p/x*((x/b)**(a*p)/(((x/b)**a + 1)**(p + 1))) 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{\left(\tfrac{x}{b}\right)^{a p}} {\left(\left(\tfrac{x}{b}\right)^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) >>> z = Symbol("z") >>> X = Dagum("x", p, a, b) >>> density(X)(z) a*p*(z/b)**(a*p)*((z/b)**a + 1)**(-p - 1)/z References ========== .. [1] http://en.wikipedia.org/wiki/Dagum_distribution """ return rv(name, DagumDistribution, (p, a, b)) #------------------------------------------------------------------------------- # 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, `\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) >>> z = Symbol("z") >>> X = Erlang("x", k, l) >>> D = density(X)(z) >>> pprint(D, use_unicode=False) k k - 1 -l*z l *z *e --------------- gamma(k) >>> C = cdf(X, meijerg=True)(z) >>> pprint(C, use_unicode=False) / k*lowergamma(k, 0) k*lowergamma(k, l*z) |- ------------------ + -------------------- for z >= 0 < gamma(k + 1) gamma(k + 1) | \ 0 otherwise >>> simplify(E(X)) k/l >>> simplify(variance(X)) k/l**2 References ========== .. [1] http://en.wikipedia.org/wiki/Erlang_distribution .. [2] http://mathworld.wolfram.com/ErlangDistribution.html """ return rv(name, GammaDistribution, (k, 1/l)) #------------------------------------------------------------------------------- # Exponential distribution ----------------------------------------------------- class ExponentialDistribution(SingleContinuousDistribution): _argnames = ('rate',) set = Interval(0, oo) @staticmethod def check(rate): _value_check(rate > 0, "Rate must be positive.") def pdf(self, x): return self.rate * exp(-self.rate*x) def sample(self): return 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 `x > 0`. Note that the expected value is `1/\lambda`. Parameters ========== rate : A positive Real number, `\lambda > 0`, the rate (or inverse scale/inverse mean) 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) >>> z = Symbol("z") >>> X = Exponential("x", l) >>> density(X)(z) lambda*exp(-lambda*z) >>> cdf(X)(z) Piecewise((1 - exp(-lambda*z), z >= 0), (0, True)) >>> E(X) 1/lambda >>> variance(X) lambda**(-2) >>> skewness(X) 2 >>> X = Exponential('x', 10) >>> density(X)(z) 10*exp(-10*z) >>> 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 rv(name, ExponentialDistribution, (rate, )) #------------------------------------------------------------------------------- # F distribution --------------------------------------------------------------- class FDistributionDistribution(SingleContinuousDistribution): _argnames = ('d1', 'd2') set = Interval(0, oo) def pdf(self, x): d1, d2 = self.d1, self.d2 return (sqrt((d1*x)**d1*d2**d2 / (d1*x+d2)**(d1+d2)) / (x * beta_fn(d1/2, d2/2))) 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`. .. TODO - What do these parameters mean? Parameters ========== d1 : `d_1 > 0` a parameter d2 : `d_2 > 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) >>> z = Symbol("z") >>> X = FDistribution("x", d1, d2) >>> D = density(X)(z) >>> pprint(D, use_unicode=False) d2 -- ______________________________ 2 / d1 -d1 - d2 /d1 d2\ d2 *\/ (d1*z) *(d1*z + d2) *gamma|-- + --| \2 2 / ----------------------------------------------------- /d1\ /d2\ z*gamma|--|*gamma|--| \2 / \2 / References ========== .. [1] http://en.wikipedia.org/wiki/F-distribution .. [2] http://mathworld.wolfram.com/F-Distribution.html """ return rv(name, FDistributionDistribution, (d1, d2)) #------------------------------------------------------------------------------- # Fisher Z distribution -------------------------------------------------------- class FisherZDistribution(SingleContinuousDistribution): _argnames = ('d1', 'd2') def pdf(self, x): d1, d2 = self.d1, self.d2 return (2*d1**(d1/2)*d2**(d2/2) / beta_fn(d1/2, d2/2) * exp(d1*x) / (d1*exp(2*x)+d2)**((d1+d2)/2)) 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}} .. TODO - What is the difference between these degrees of freedom? Parameters ========== d1 : `d_1 > 0`, degree of freedom d2 : `d_2 > 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) >>> z = Symbol("z") >>> X = FisherZ("x", d1, d2) >>> D = density(X)(z) >>> pprint(D, use_unicode=False) d1 d2 d1 d2 - -- - -- -- -- 2 2 2 2 / 2*z \ d1*z /d1 d2\ 2*d1 *d2 *\d1*e + d2/ *e *gamma|-- + --| \2 2 / -------------------------------------------------------- /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 rv(name, FisherZDistribution, (d1, d2)) #------------------------------------------------------------------------------- # Frechet distribution --------------------------------------------------------- class FrechetDistribution(SingleContinuousDistribution): _argnames = ('a', 's', 'm') set = Interval(0, oo) def __new__(cls, a, s=1, m=0): a, s, m = list(map(sympify, (a, s, m))) return Basic.__new__(cls, a, s, m) def pdf(self, x): a, s, m = self.a, self.s, self.m return a/s * ((x-m)/s)**(-1-a) * exp(-((x-m)/s)**(-a)) 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) >>> z = Symbol("z") >>> X = Frechet("x", a, s, m) >>> density(X)(z) a*((-m + z)/s)**(-a - 1)*exp(-((-m + z)/s)**(-a))/s References ========== .. [1] http://en.wikipedia.org/wiki/Fr%C3%A9chet_distribution """ return rv(name, FrechetDistribution, (a, s, m)) #------------------------------------------------------------------------------- # Gamma distribution ----------------------------------------------------------- class GammaDistribution(SingleContinuousDistribution): _argnames = ('k', 'theta') set = Interval(0, oo) @staticmethod def check(k, theta): _value_check(k > 0, "k must be positive") _value_check(theta > 0, "Theta must be positive") def pdf(self, x): k, theta = self.k, self.theta return x**(k - 1) * exp(-x/theta) / (gamma(k)*theta**k) def sample(self): return 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, simplify >>> k = Symbol("k", positive=True) >>> theta = Symbol("theta", positive=True) >>> z = Symbol("z") >>> X = Gamma("x", k, theta) >>> D = density(X)(z) >>> pprint(D, use_unicode=False) -z ----- -k k - 1 theta theta *z *e --------------------- gamma(k) >>> C = cdf(X, meijerg=True)(z) >>> pprint(C, use_unicode=False) / / z \ | k*lowergamma|k, -----| | k*lowergamma(k, 0) \ theta/ <- ------------------ + ---------------------- for z >= 0 | gamma(k + 1) gamma(k + 1) | \ 0 otherwise >>> E(X) theta*gamma(k + 1)/gamma(k) >>> V = simplify(variance(X)) >>> pprint(V, use_unicode=False) 2 k*theta References ========== .. [1] http://en.wikipedia.org/wiki/Gamma_distribution .. [2] http://mathworld.wolfram.com/GammaDistribution.html """ return rv(name, GammaDistribution, (k, theta)) #------------------------------------------------------------------------------- # Inverse Gamma distribution --------------------------------------------------- class GammaInverseDistribution(SingleContinuousDistribution): _argnames = ('a', 'b') set = Interval(0, oo) @staticmethod def check(a, b): _value_check(a > 0, "alpha must be positive") _value_check(b > 0, "beta must be positive") def pdf(self, x): a, b = self.a, self.b return b**a/gamma(a) * x**(-a-1) * exp(-b/x) 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) >>> z = Symbol("z") >>> X = GammaInverse("x", a, b) >>> D = density(X)(z) >>> pprint(D, use_unicode=False) -b --- a -a - 1 z b *z *e --------------- gamma(a) References ========== .. [1] http://en.wikipedia.org/wiki/Inverse-gamma_distribution """ return rv(name, GammaInverseDistribution, (a, b)) #------------------------------------------------------------------------------- # Kumaraswamy distribution ----------------------------------------------------- class KumaraswamyDistribution(SingleContinuousDistribution): _argnames = ('a', 'b') set = Interval(0, oo) @staticmethod def check(a, b): _value_check(a > 0, "a must be positive") _value_check(b > 0, "b must be positive") def pdf(self, x): a, b = self.a, self.b return a * b * x**(a-1) * (1-x**a)**(b-1) 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) >>> z = Symbol("z") >>> X = Kumaraswamy("x", a, b) >>> D = density(X)(z) >>> pprint(D, use_unicode=False) b - 1 a - 1 / a \ a*b*z *\- z + 1/ References ========== .. [1] http://en.wikipedia.org/wiki/Kumaraswamy_distribution """ return rv(name, KumaraswamyDistribution, (a, b)) #------------------------------------------------------------------------------- # Laplace distribution --------------------------------------------------------- class LaplaceDistribution(SingleContinuousDistribution): _argnames = ('mu', 'b') def pdf(self, x): mu, b = self.mu, self.b return 1/(2*b)*exp(-Abs(x - mu)/b) 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 (mean) 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) >>> z = Symbol("z") >>> X = Laplace("x", mu, b) >>> density(X)(z) exp(-Abs(-mu + z)/b)/(2*b) References ========== .. [1] http://en.wikipedia.org/wiki/Laplace_distribution .. [2] http://mathworld.wolfram.com/LaplaceDistribution.html """ return rv(name, LaplaceDistribution, (mu, b)) #------------------------------------------------------------------------------- # Logistic distribution -------------------------------------------------------- class LogisticDistribution(SingleContinuousDistribution): _argnames = ('mu', 's') def pdf(self, x): mu, s = self.mu, self.s return exp(-(x - mu)/s)/(s*(1 + exp(-(x - mu)/s))**2) 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 (mean) 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) >>> z = Symbol("z") >>> X = Logistic("x", mu, s) >>> density(X)(z) exp((mu - z)/s)/(s*(exp((mu - z)/s) + 1)**2) References ========== .. [1] http://en.wikipedia.org/wiki/Logistic_distribution .. [2] http://mathworld.wolfram.com/LogisticDistribution.html """ return rv(name, LogisticDistribution, (mu, s)) #------------------------------------------------------------------------------- # Log Normal distribution ------------------------------------------------------ class LogNormalDistribution(SingleContinuousDistribution): _argnames = ('mean', 'std') set = Interval(0, oo) def pdf(self, x): mean, std = self.mean, self.std return exp(-(log(x) - mean)**2 / (2*std**2)) / (x*sqrt(2*pi)*std) def sample(self): return 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) >>> z = Symbol("z") >>> X = LogNormal("x", mu, sigma) >>> D = density(X)(z) >>> pprint(D, use_unicode=False) 2 -(-mu + log(z)) ----------------- 2 ___ 2*sigma \/ 2 *e ------------------------ ____ 2*\/ pi *sigma*z >>> X = LogNormal('x', 0, 1) # Mean 0, standard deviation 1 >>> density(X)(z) sqrt(2)*exp(-log(z)**2/2)/(2*sqrt(pi)*z) References ========== .. [1] http://en.wikipedia.org/wiki/Lognormal .. [2] http://mathworld.wolfram.com/LogNormalDistribution.html """ return rv(name, LogNormalDistribution, (mean, std)) #------------------------------------------------------------------------------- # Maxwell distribution --------------------------------------------------------- class MaxwellDistribution(SingleContinuousDistribution): _argnames = ('a',) set = Interval(0, oo) def pdf(self, x): a = self.a return sqrt(2/pi)*x**2*exp(-x**2/(2*a**2))/a**3 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`. .. TODO - what does the parameter mean? 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) >>> z = Symbol("z") >>> X = Maxwell("x", a) >>> density(X)(z) sqrt(2)*z**2*exp(-z**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 rv(name, MaxwellDistribution, (a, )) #------------------------------------------------------------------------------- # Nakagami distribution -------------------------------------------------------- class NakagamiDistribution(SingleContinuousDistribution): _argnames = ('mu', 'omega') set = Interval(0, oo) def pdf(self, x): mu, omega = self.mu, self.omega return 2*mu**mu/(gamma(mu)*omega**mu)*x**(2*mu - 1)*exp(-mu/omega*x**2) 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, `\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) >>> z = Symbol("z") >>> X = Nakagami("x", mu, omega) >>> D = density(X)(z) >>> pprint(D, use_unicode=False) 2 -mu*z ------- mu -mu 2*mu - 1 omega 2*mu *omega *z *e ---------------------------------- 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 + 1/2) omega - ----------------------- gamma(mu)*gamma(mu + 1) References ========== .. [1] http://en.wikipedia.org/wiki/Nakagami_distribution """ return rv(name, NakagamiDistribution, (mu, omega)) #------------------------------------------------------------------------------- # Normal distribution ---------------------------------------------------------- class NormalDistribution(SingleContinuousDistribution): _argnames = ('mean', 'std') @staticmethod def check(mean, std): _value_check(std > 0, "Standard deviation must be positive") def pdf(self, x): return exp(-(x - self.mean)**2 / (2*self.std**2)) / (sqrt(2*pi)*self.std) def sample(self): return 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, factor_terms >>> mu = Symbol("mu") >>> sigma = Symbol("sigma", positive=True) >>> z = Symbol("z") >>> X = Normal("x", mu, sigma) >>> density(X)(z) sqrt(2)*exp(-(-mu + z)**2/(2*sigma**2))/(2*sqrt(pi)*sigma) >>> C = simplify(cdf(X))(z) # it needs a little more help... >>> pprint(C, use_unicode=False) / ___ \ |\/ 2 *(-mu + z)| erf|---------------| \ 2*sigma / 1 -------------------- + - 2 2 >>> simplify(skewness(X)) 0 >>> X = Normal("x", 0, 1) # Mean 0, standard deviation 1 >>> density(X)(z) sqrt(2)*exp(-z**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 rv(name, NormalDistribution, (mean, std)) #------------------------------------------------------------------------------- # Pareto distribution ---------------------------------------------------------- class ParetoDistribution(SingleContinuousDistribution): _argnames = ('xm', 'alpha') @property def set(self): return Interval(self.xm, oo) @staticmethod def check(xm, alpha): _value_check(xm > 0, "Xm must be positive") _value_check(alpha > 0, "Alpha must be positive") def pdf(self, x): xm, alpha = self.xm, self.alpha return alpha * xm**alpha / x**(alpha + 1) def sample(self): return 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_m^\alpha}{x^{\alpha+1}} with :math:`x \in [x_m,\infty]`. Parameters ========== xm : Real number, `x_m > 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) >>> z = Symbol("z") >>> X = Pareto("x", xm, beta) >>> density(X)(z) beta*xm**beta*z**(-beta - 1) References ========== .. [1] http://en.wikipedia.org/wiki/Pareto_distribution .. [2] http://mathworld.wolfram.com/ParetoDistribution.html """ return rv(name, ParetoDistribution, (xm, alpha)) #------------------------------------------------------------------------------- # QuadraticU distribution ------------------------------------------------------ class QuadraticUDistribution(SingleContinuousDistribution): _argnames = ('a', 'b') @property def set(self): return Interval(self.a, self.b) def pdf(self, x): a, b = self.a, self.b alpha = 12 / (b-a)**3 beta = (a+b) / 2 return Piecewise( (alpha * (x-beta)**2, And(a<=x, x<=b)), (S.Zero, True)) 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) >>> z = Symbol("z") >>> X = QuadraticU("x", a, b) >>> D = density(X)(z) >>> pprint(D, use_unicode=False) / 2 | / a b \ |12*|- - - - + z| | \ 2 2 / <----------------- for And(a <= z, z <= b) | 3 | (-a + b) | \ 0 otherwise References ========== .. [1] http://en.wikipedia.org/wiki/U-quadratic_distribution """ return rv(name, QuadraticUDistribution, (a, b)) #------------------------------------------------------------------------------- # RaisedCosine distribution ---------------------------------------------------- class RaisedCosineDistribution(SingleContinuousDistribution): _argnames = ('mu', 's') @property def set(self): return Interval(self.mu - self.s, self.mu + self.s) @staticmethod def check(mu, s): _value_check(s > 0, "s must be positive") def pdf(self, x): mu, s = self.mu, self.s return Piecewise( ((1+cos(pi*(x-mu)/s)) / (2*s), And(mu-s<=x, x<=mu+s)), (S.Zero, True)) 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) >>> z = Symbol("z") >>> X = RaisedCosine("x", mu, s) >>> D = density(X)(z) >>> pprint(D, use_unicode=False) / /pi*(-mu + z)\ |cos|------------| + 1 | \ s / <--------------------- for And(z <= mu + s, mu - s <= z) | 2*s | \ 0 otherwise References ========== .. [1] http://en.wikipedia.org/wiki/Raised_cosine_distribution """ return rv(name, RaisedCosineDistribution, (mu, s)) #------------------------------------------------------------------------------- # Rayleigh distribution -------------------------------------------------------- class RayleighDistribution(SingleContinuousDistribution): _argnames = ('sigma',) set = Interval(0, oo) def pdf(self, x): sigma = self.sigma return x/sigma**2*exp(-x**2/(2*sigma**2)) 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) >>> z = Symbol("z") >>> X = Rayleigh("x", sigma) >>> density(X)(z) z*exp(-z**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 rv(name, RayleighDistribution, (sigma, )) #------------------------------------------------------------------------------- # StudentT distribution -------------------------------------------------------- class StudentTDistribution(SingleContinuousDistribution): _argnames = ('nu',) def pdf(self, x): nu = self.nu return 1/(sqrt(nu)*beta_fn(S(1)/2, nu/2))*(1 + x**2/nu)**(-(nu + 1)/2) 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) >>> z = Symbol("z") >>> X = StudentT("x", nu) >>> D = density(X)(z) >>> pprint(D, use_unicode=False) nu 1 - -- - - 2 2 / 2\ | z | /nu 1\ |1 + --| *gamma|-- + -| \ nu/ \2 2/ ------------------------------ ____ ____ /nu\ \/ pi *\/ nu *gamma|--| \2 / References ========== .. [1] http://en.wikipedia.org/wiki/Student_t-distribution .. [2] http://mathworld.wolfram.com/Studentst-Distribution.html """ return rv(name, StudentTDistribution, (nu, )) #------------------------------------------------------------------------------- # Triangular distribution ------------------------------------------------------ class TriangularDistribution(SingleContinuousDistribution): _argnames = ('a', 'b', 'c') def pdf(self, x): a, b, c = self.a, self.b, self.c return 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)) 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, pprint >>> a = Symbol("a") >>> b = Symbol("b") >>> c = Symbol("c") >>> z = Symbol("z") >>> X = Triangular("x", a,b,c) >>> pprint(density(X)(z), use_unicode=False) / -2*a + 2*z |----------------- for And(a <= z, z < c) |(-a + b)*(-a + c) | | 2 | ------ for z = c < -a + b | | 2*b - 2*z |---------------- for And(z <= b, c < z) |(-a + b)*(b - c) | \ 0 otherwise References ========== .. [1] http://en.wikipedia.org/wiki/Triangular_distribution .. [2] http://mathworld.wolfram.com/TriangularDistribution.html """ return rv(name, TriangularDistribution, (a, b, c)) #------------------------------------------------------------------------------- # Uniform distribution --------------------------------------------------------- class UniformDistribution(SingleContinuousDistribution): _argnames = ('left', 'right') def pdf(self, x): left, right = self.left, self.right return Piecewise( (S.One/(right - left), And(left <= x, x <= right)), (S.Zero, True)) def compute_cdf(self, **kwargs): from sympy import Lambda, Min z = Dummy('z', real=True, bounded=True) result = SingleContinuousDistribution.compute_cdf(self, **kwargs) result = result(z).subs({Min(z, self.right): z, Min(z, self.left, self.right): self.left}) return Lambda(z, result) def expectation(self, expr, var, **kwargs): from sympy import Max, Min kwargs['evaluate'] = True result = SingleContinuousDistribution.expectation(self, expr, var, **kwargs) result = result.subs({Max(self.left, self.right): self.right, Min(self.left, self.right): self.left}) return result def sample(self): return 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", negative=True) >>> b = Symbol("b", positive=True) >>> z = Symbol("z") >>> X = Uniform("x", a, b) >>> density(X)(z) Piecewise((1/(-a + b), And(a <= z, z <= b)), (0, True)) >>> cdf(X)(z) -a/(-a + b) + z/(-a + b) >>> simplify(E(X)) a/2 + b/2 >>> simplify(variance(X)) a**2/12 - a*b/6 + b**2/12 References ========== .. [1] http://en.wikipedia.org/wiki/Uniform_distribution_%28continuous%29 .. [2] http://mathworld.wolfram.com/UniformDistribution.html """ return rv(name, UniformDistribution, (left, right)) #------------------------------------------------------------------------------- # UniformSum distribution ------------------------------------------------------ class UniformSumDistribution(SingleContinuousDistribution): _argnames = ('n',) @property def set(self): return Interval(0, self.n) def pdf(self, x): n = self.n k = Dummy("k") return 1/factorial( n - 1)*Sum((-1)**k*binomial(n, k)*(x - k)**(n - 1), (k, 0, floor(x))) 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 : A positive Integer, `n > 0` Returns ======= A RandomSymbol. Examples ======== >>> from sympy.stats import UniformSum, density >>> from sympy import Symbol, pprint >>> n = Symbol("n", integer=True) >>> z = Symbol("z") >>> X = UniformSum("x", n) >>> D = density(X)(z) >>> pprint(D, use_unicode=False) floor(z) ___ \ ` \ k n - 1 /n\ ) (-1) *(-k + z) *| | / \k/ /__, k = 0 -------------------------------- (n - 1)! References ========== .. [1] http://en.wikipedia.org/wiki/Uniform_sum_distribution .. [2] http://mathworld.wolfram.com/UniformSumDistribution.html """ return rv(name, UniformSumDistribution, (n, )) #------------------------------------------------------------------------------- # VonMises distribution -------------------------------------------------------- class VonMisesDistribution(SingleContinuousDistribution): _argnames = ('mu', 'k') set = Interval(0, 2*pi) @staticmethod def check(mu, k): _value_check(k > 0, "k must be positive") def pdf(self, x): mu, k = self.mu, self.k return exp(k*cos(x-mu)) / (2*pi*besseli(0, k)) 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) >>> z = Symbol("z") >>> X = VonMises("x", mu, k) >>> D = density(X)(z) >>> pprint(D, use_unicode=False) k*cos(mu - z) e ------------------ 2*pi*besseli(0, k) References ========== .. [1] http://en.wikipedia.org/wiki/Von_Mises_distribution .. [2] http://mathworld.wolfram.com/vonMisesDistribution.html """ return rv(name, VonMisesDistribution, (mu, k)) #------------------------------------------------------------------------------- # Weibull distribution --------------------------------------------------------- class WeibullDistribution(SingleContinuousDistribution): _argnames = ('alpha', 'beta') set = Interval(0, oo) @staticmethod def check(alpha, beta): _value_check(alpha > 0, "Alpha must be positive") _value_check(beta > 0, "Beta must be positive") def pdf(self, x): alpha, beta = self.alpha, self.beta return beta * (x/alpha)**(beta - 1) * exp(-(x/alpha)**beta) / alpha def sample(self): return 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) >>> z = Symbol("z") >>> X = Weibull("x", l, k) >>> density(X)(z) k*(z/lambda)**(k - 1)*exp(-(z/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 rv(name, WeibullDistribution, (alpha, beta)) #------------------------------------------------------------------------------- # Wigner semicircle distribution ----------------------------------------------- class WignerSemicircleDistribution(SingleContinuousDistribution): _argnames = ('R',) @property def set(self): return Interval(-self.R, self.R) def pdf(self, x): R = self.R return 2/(pi*R**2)*sqrt(R**2 - x**2) 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) >>> z = Symbol("z") >>> X = WignerSemicircle("x", R) >>> density(X)(z) 2*sqrt(R**2 - z**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 rv(name, WignerSemicircleDistribution, (R,)) sympy-0.7.4.1/sympy/stats/frv.py0000644000175000017500000002372712253362407016745 0ustar georgeskgeorgesk""" Finite Discrete Random Variables Module See Also ======== sympy.stats.frv_types sympy.stats.rv sympy.stats.crv """ from __future__ import print_function, division from itertools import product from sympy import (And, Eq, Basic, S, Expr, Symbol, cacheit, sympify, Mul, Add, And, Or, Tuple, Piecewise, Eq, Lambda) from sympy.core.sets import FiniteSet from sympy.stats.rv import (RandomDomain, ProductDomain, ConditionalDomain, PSpace, ProductPSpace, SinglePSpace, random_symbols, sumsets, rv_subs, NamedArgsMixin) from sympy.core.containers import Dict import random class FiniteDensity(dict): def __call__(self, item): item = sympify(item) if item in self: return self[item] else: return 0 @property def dict(self): return dict(self) class FiniteDomain(RandomDomain): """ A domain with discrete finite support Represented using a FiniteSet. """ is_Finite = True @property def symbols(self): return FiniteSet(sym for sym, val in self.elements) @property def elements(self): return self.args[0] @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): if not isinstance(set, FiniteSet): set = FiniteSet(*set) return Basic.__new__(cls, symbol, set) @property def symbol(self): return self.args[0] return tuple(self.symbols)[0] @property def symbols(self): return FiniteSet(self.symbol) @property def set(self): return self.args[1] @property def elements(self): return FiniteSet(frozenset(((self.symbol, elem), )) for elem in self.set) 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): if condition is True: return domain 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 Basic.__new__(cls, domain, cond) def _test(self, elem): val = self.condition.xreplace(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) class SingleFiniteDistribution(Basic, NamedArgsMixin): def __new__(cls, *args): args = list(map(sympify, args)) return Basic.__new__(cls, *args) @property @cacheit def dict(self): return dict((k, self.pdf(k)) for k in self.set) @property def pdf(self): x = Symbol('x') return Lambda(x, Piecewise(*( [(v, Eq(k, x)) for k, v in self.dict.items()] + [(0, True)]))) @property def set(self): return list(self.dict.keys()) values = property(lambda self: self.dict.values) items = property(lambda self: self.dict.items) __iter__ = property(lambda self: self.dict.__iter__) __getitem__ = property(lambda self: self.dict.__getitem__) __call__ = pdf def __contains__(self, other): return other in self.set #============================================= #========= Probability Space =============== #============================================= class FinitePSpace(PSpace): """ A Finite Probability Space Represents the probabilities of a finite number of events. """ is_Finite = True @property def domain(self): return self.args[0] @property def density(self): return self.args[0] def __new__(cls, domain, density): density = dict((sympify(key), sympify(val)) for key, val in 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.xreplace(dict(((rs, rs.symbol) for rs in self.values))) d = FiniteDensity() for elem in self.domain: val = expr.xreplace(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_cumprob: val_cumprob[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.xreplace(dict((rs, rs.symbol) for rs in rvs)) return sum([expr.xreplace(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 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(SinglePSpace, FinitePSpace): """ 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.... """ @property def domain(self): return SingleFiniteDomain(self.symbol, self.distribution.set) @property @cacheit def _density(self): return dict((frozenset(((self.symbol, val),)), prob) for val, prob in self.distribution.dict.items()) 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) sympy-0.7.4.1/sympy/stats/crv.py0000644000175000017500000003431612253362407016736 0ustar georgeskgeorgesk""" Continuous Random Variables Module See Also ======== sympy.stats.crv_types sympy.stats.rv sympy.stats.frv """ from __future__ import print_function, division from sympy.stats.rv import (RandomDomain, SingleDomain, ConditionalDomain, ProductDomain, PSpace, SinglePSpace, random_symbols, ProductPSpace, NamedArgsMixin) 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, Basic) from sympy.solvers.inequalities import reduce_rational_inequalities from sympy.polys.polyerrors import PolynomialError import random 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 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 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) # 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_rational_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) 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_rational_inequalities_wrap( self.condition, tuple(self.symbols)[0])) else: raise NotImplementedError( "Set of Conditional Domain not Implemented") class ContinuousDistribution(Basic): def __call__(self, *args): return self.pdf(*args) class SingleContinuousDistribution(ContinuousDistribution, NamedArgsMixin): """ Continuous distribution of a single variable Serves as superclass for Normal/Exponential/UniformDistribution etc.... Represented by parameters for each of the specific classes. E.g NormalDistribution is represented by a mean and standard deviation. Provides methods for pdf, cdf, and sampling See Also: sympy.stats.crv_types.* """ set = Interval(-oo, oo) def __new__(cls, *args): args = list(map(sympify, args)) return Basic.__new__(cls, *args) @staticmethod def check(*args): pass def sample(self): """ A random realization from the distribution """ icdf = self._inverse_cdf_expression() return icdf(random.uniform(0, 1)) @cacheit def _inverse_cdf_expression(self): """ Inverse of the CDF Used by sample """ x, z = symbols('x, z', real=True, positive=True, cls=Dummy) # Invert CDF try: inverse_cdf = solve(self.cdf(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]) @cacheit def compute_cdf(self, **kwargs): """ Compute the CDF from the PDF Returns a Lambda """ x, z = symbols('x, z', real=True, bounded=True, cls=Dummy) left_bound = self.set.start # CDF is integral of PDF from left bound to z pdf = self.pdf(x) cdf = integrate(pdf, (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 cdf(self, x, **kwargs): """ Cumulative density function """ return self.compute_cdf(**kwargs)(x) def expectation(self, expr, var, evaluate=True, **kwargs): """ Expectation of expression over distribution """ integral = Integral(expr * self.pdf(var), (var, self.set), **kwargs) return integral.doit() if evaluate else integral class ContinuousDistributionHandmade(SingleContinuousDistribution): _argnames = ('pdf',) @property def set(self): return self.args[1] def __new__(cls, pdf, set=Interval(-oo, oo)): return Basic.__new__(cls, pdf, set) class ContinuousPSpace(PSpace): """ Continuous Probability Space Represents the likelihood of an event space defined over a continuum. Represented with a ContinuousDomain and a PDF (Lambda-Like) """ is_Continuous = True is_real = True @property def domain(self): return self.args[0] @property def density(self): return self.args[1] @property def pdf(self): return self.density(*self.domain.symbols) def integrate(self, expr, rvs=None, **kwargs): if rvs is None: rvs = self.values else: rvs = frozenset(rvs) expr = expr.xreplace(dict((rv, rv.symbol) for rv in rvs)) domain_symbols = frozenset(rv.symbol for rv in rvs) return self.domain.integrate(self.pdf * 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 randomsymbols = tuple(set(self.values) - frozenset([expr])) symbols = tuple(rs.symbol for rs in randomsymbols) pdf = self.domain.integrate(self.pdf, symbols, **kwargs) return Lambda(expr.symbol, pdf) 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 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: from sympy.stats.rv import density expr = condition.lhs - condition.rhs dens = density(expr, **kwargs) if not isinstance(dens, ContinuousDistribution): dens = ContinuousDistributionHandmade(dens) # Turn problem into univariate case space = SingleContinuousPSpace(z, dens) 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_rational_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.xreplace(dict((rv, rv.symbol) for rv in self.values)) domain = ConditionalContinuousDomain(self.domain, condition) if normalize: pdf = self.pdf / domain.integrate(self.pdf, **kwargs) density = Lambda(domain.symbols, pdf) return ContinuousPSpace(domain, density) class SingleContinuousPSpace(ContinuousPSpace, SinglePSpace): """ A continuous probability space over a single univariate variable These consist of a Symbol and a SingleContinuousDistribution This class is normally accessed through the various random variable functions, Normal, Exponential, Uniform, etc.... """ @property def set(self): return self.distribution.set @property def domain(self): return SingleContinuousDomain(sympify(self.symbol), self.set) def sample(self): """ Internal sample method Returns dictionary mapping RandomSymbol to realization value. """ return {self.value: self.distribution.sample()} def integrate(self, expr, rvs=None, **kwargs): rvs = rvs or (self.value,) if self.value not in rvs: return expr expr = expr.xreplace(dict((rv, rv.symbol) for rv in rvs)) x = self.value.symbol try: return self.distribution.expectation(expr, x, evaluate=False, **kwargs) except: return Integral(expr * self.pdf, (x, self.set), **kwargs) def compute_cdf(self, expr, **kwargs): if expr == self.value: return self.distribution.compute_cdf(**kwargs) else: return ContinuousPSpace.compute_cdf(self, expr, **kwargs) def compute_density(self, expr, **kwargs): # http://en.wikipedia.org/wiki/Random_variable#Functions_of_random_variables if expr == self.value: return self.density y = Dummy('y') gs = solve(expr - y, self.value) if not gs: raise ValueError("Can not solve %s for %s"%(expr, self.value)) fx = self.compute_density(self.value) fy = sum(fx(g) * abs(g.diff(y)) for g in gs) return Lambda(y, fy) class ProductContinuousPSpace(ProductPSpace, ContinuousPSpace): """ A collection of independent continuous probability spaces """ @property def pdf(self): p = Mul(*[space.pdf for space in self.spaces]) return p.subs(dict((rv, rv.symbol) for rv in self.values)) def _reduce_inequalities(conditions, var, **kwargs): try: return reduce_rational_inequalities(conditions, var, **kwargs) except PolynomialError: raise ValueError("Reduction of condition failed %s\n" % conditions[0]) def reduce_rational_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 sympy-0.7.4.1/sympy/stats/drv_types.py0000644000175000017500000000527712253362407020167 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.stats.drv import SingleDiscreteDistribution, SingleDiscretePSpace from sympy import factorial, exp, Basic, Range, S, oo, sympify from sympy.stats.rv import _value_check __all__ = ['Geometric', 'Poisson'] def rv(symbol, cls, *args): args = list(map(sympify, args)) dist = cls(*args) dist.check(*args) return SingleDiscretePSpace(symbol, dist).value class PoissonDistribution(SingleDiscreteDistribution): _argnames = ('lamda',) set = S.Naturals0 @staticmethod def check(lamda): _value_check(lamda > 0, "Lambda must be positive") def pdf(self, k): return self.lamda**k / factorial(k) * exp(-self.lamda) def Poisson(name, lamda): r""" Create a discrete random variable with a Poisson distribution. The density of the Poisson distribution is given by .. math:: f(k) := \frac{\lambda^{k} e^{- \lambda}}{k!} Parameters ========== lamda: Positive number, a rate Returns ======= A RandomSymbol. Examples ======== >>> from sympy.stats import Poisson, density, E, variance >>> from sympy import Symbol, simplify >>> rate = Symbol("lambda", positive=True) >>> z = Symbol("z") >>> X = Poisson("x", rate) >>> density(X)(z) lambda**z*exp(-lambda)/factorial(z) >>> E(X) lambda >>> simplify(variance(X)) lambda References ========== [1] http://en.wikipedia.org/wiki/Poisson_distribution [2] http://mathworld.wolfram.com/PoissonDistribution.html """ return rv(name, PoissonDistribution, lamda) class GeometricDistribution(SingleDiscreteDistribution): _argnames = ('p',) set = S.Naturals @staticmethod def check(p): # _value_check(0 < p <= 1, "p must be between 0 and 1") pass def pdf(self, k): return (1 - self.p)**(k - 1) * self.p def Geometric(name, p): r""" Create a discrete random variable with a Geometric distribution. The density of the Poisson distribution is given by .. math:: f(k) := p (1 - p)^{k - 1} Parameters ========== p: A probability between 0 and 1 Returns ======= A RandomSymbol. Examples ======== >>> from sympy.stats import Geometric, density, E, variance >>> from sympy import Symbol, S >>> p = S.One / 5 >>> z = Symbol("z") >>> X = Geometric("x", p) >>> density(X)(z) (4/5)**(z - 1)/5 >>> E(X) 5 >>> variance(X) 20 References ========== [1] http://en.wikipedia.org/wiki/Geometric_distribution [2] http://mathworld.wolfram.com/GeometricDistribution.html """ return rv(name, GeometricDistribution, p) sympy-0.7.4.1/sympy/stats/frv_types.py0000644000175000017500000001376112253362407020166 0ustar georgeskgeorgesk""" Finite Discrete Random Variables - Prebuilt variable types Contains ======== FiniteRV DiscreteUniform Die Bernoulli Coin Binomial Hypergeometric """ from __future__ import print_function, division from sympy.stats.frv import (SingleFinitePSpace, SingleFiniteDistribution) from sympy import (S, sympify, Rational, binomial, cacheit, Symbol, Integer, Dict, Basic) __all__ = ['FiniteRV', 'DiscreteUniform', 'Die', 'Bernoulli', 'Coin', 'Binomial', 'Hypergeometric'] def rv(name, cls, *args): density = cls(*args) return SingleFinitePSpace(name, density).value class FiniteDistributionHandmade(SingleFiniteDistribution): @property def dict(self): return self.args[0] def __new__(cls, density): density = Dict(density) return Basic.__new__(cls, density) 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 rv(name, FiniteDistributionHandmade, density) class DiscreteUniformDistribution(SingleFiniteDistribution): @property def p(self): return Rational(1, len(self.args)) @property @cacheit def dict(self): return dict((k, self.p) for k in self.set) @property def set(self): return self.args def pdf(self, x): if x in self.args: return self.p else: return S.Zero 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).dict {a: 1/3, b: 1/3, c: 1/3} >>> Y = DiscreteUniform('Y', list(range(5))) # distribution over a range >>> density(Y).dict {0: 1/5, 1: 1/5, 2: 1/5, 3: 1/5, 4: 1/5} """ return rv(name, DiscreteUniformDistribution, *items) class DieDistribution(SingleFiniteDistribution): _argnames = ('sides',) @property def set(self): return list(map(Integer, list(range(1, self.sides+1)))) def pdf(self, x): x = sympify(x) if x.is_Integer and x >= 1 and x <= self.sides: return Rational(1, self.sides) else: return 0 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).dict {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).dict {1: 1/4, 2: 1/4, 3: 1/4, 4: 1/4} """ return rv(name, DieDistribution, sides) class BernoulliDistribution(SingleFiniteDistribution): _argnames = ('p', 'succ', 'fail') @property @cacheit def dict(self): return {self.succ: self.p, self.fail: 1 - self.p} 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).dict {0: 1/4, 1: 3/4} >>> X = Bernoulli('X', S.Half, 'Heads', 'Tails') # A fair coin toss >>> density(X).dict {Heads: 1/2, Tails: 1/2} """ return rv(name, BernoulliDistribution, p, succ, fail) 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).dict {H: 1/2, T: 1/2} >>> C2 = Coin('C2', Rational(3, 5)) # An unfair coin >>> density(C2).dict {H: 3/5, T: 2/5} """ return rv(name, BernoulliDistribution, p, 'H', 'T') class BinomialDistribution(SingleFiniteDistribution): _argnames = ('n', 'p', 'succ', 'fail') @property @cacheit def dict(self): n, p, succ, fail = self.n, self.p, self.succ, self.fail return dict((k*succ + (n - k)*fail, binomial(n, k) * p**k * (1 - p)**(n - k)) for k in range(0, n + 1)) 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).dict {0: 1/16, 1: 1/4, 2: 3/8, 3: 1/4, 4: 1/16} """ return rv(name, BinomialDistribution, n, p, succ, fail) class HypergeometricDistribution(SingleFiniteDistribution): _argnames = ('N', 'm', 'n') @property @cacheit def dict(self): N, m, n = self.N, self.m, self.n N, m, n = list(map(sympify, (N, m, n))) density = dict((sympify(k), Rational(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 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).dict {0: 1/12, 1: 5/12, 2: 5/12, 3: 1/12} """ return rv(name, HypergeometricDistribution, N, m, n) sympy-0.7.4.1/sympy/stats/drv.py0000644000175000017500000000763612253362407016744 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import (Basic, sympify, symbols, Dummy, Lambda, summation, Piecewise, S, cacheit, solve, Sum) from sympy.stats.rv import NamedArgsMixin, SinglePSpace, SingleDomain import random class SingleDiscreteDistribution(Basic, NamedArgsMixin): """ Discrete distribution of a single variable Serves as superclass for PoissonDistribution etc.... Provides methods for pdf, cdf, and sampling See Also: sympy.stats.crv_types.* """ set = S.Integers def __new__(cls, *args): args = list(map(sympify, args)) return Basic.__new__(cls, *args) @staticmethod def check(*args): pass def sample(self): """ A random realization from the distribution """ icdf = self._inverse_cdf_expression() return floor(icdf(random.uniform(0, 1))) @cacheit def _inverse_cdf_expression(self): """ Inverse of the CDF Used by sample """ x, z = symbols('x, z', real=True, positive=True, cls=Dummy) # Invert CDF try: inverse_cdf = solve(self.cdf(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]) @cacheit def compute_cdf(self, **kwargs): """ Compute the CDF from the PDF Returns a Lambda """ x, z = symbols('x, z', integer=True, bounded=True, cls=Dummy) left_bound = self.set.inf # CDF is integral of PDF from left bound to z pdf = self.pdf(x) cdf = summation(pdf, (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 cdf(self, x, **kwargs): """ Cumulative density function """ return self.compute_cdf(**kwargs)(x) def expectation(self, expr, var, evaluate=True, **kwargs): """ Expectation of expression over distribution """ # return summation(expr * self.pdf(var), (var, self.set), **kwargs) # TODO: support discrete sets with non integer stepsizes if evaluate: return summation(expr * self.pdf(var), (var, self.set.inf, self.set.sup), **kwargs) else: return Sum(expr * self.pdf(var), (var, self.set.inf, self.set.sup), **kwargs) def __call__(self, *args): return self.pdf(*args) class SingleDiscreteDomain(SingleDomain): pass class SingleDiscretePSpace(SinglePSpace): """ Discrete probability space over a single univariate variable """ is_real = True @property def set(self): return self.distribution.set @property def domain(self): return SingleDiscreteDomain(self.symbol, self.set) def sample(self): """ Internal sample method Returns dictionary mapping RandomSymbol to realization value. """ return {self.value: self.distribution.sample()} def integrate(self, expr, rvs=None, **kwargs): rvs = rvs or (self.value,) if self.value not in rvs: return expr expr = expr.xreplace(dict((rv, rv.symbol) for rv in rvs)) x = self.value.symbol try: return self.distribution.expectation(expr, x, evaluate=False, **kwargs) except: return Sum(expr * self.pdf, (x, self.set.inf, self.set.sup), **kwargs) def compute_cdf(self, expr, **kwargs): if expr == self.value: return self.distribution.compute_cdf(**kwargs) else: raise NotImplementedError() def compute_density(self, expr, **kwargs): if expr == self.value: return self.distribution raise NotImplementedError() sympy-0.7.4.1/sympy/stats/rv.py0000644000175000017500000006774612253362407016610 0ustar georgeskgeorgesk""" 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 __future__ import print_function, division from sympy import (Basic, S, Expr, Symbol, Tuple, And, Add, Eq, lambdify, sympify, Equality, solve, Lambda, DiracDelta) from sympy.core.compatibility import reduce from sympy.core.sets import FiniteSet, ProductSet from sympy.abc import x 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() 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 return Basic.__new__(cls, symbol, set) @property def symbol(self): return self.args[0] @property def symbols(self): return FiniteSet(self.symbol) def __contains__(self, other): if len(other) != 1: return False sym, val = tuple(other)[0] return self.symbol == sym and val in self.set 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.xreplace(dict((rs, rs.symbol) for rs in random_symbols(condition))) return Basic.__new__(cls, fulldomain, condition) @property def symbols(self): return self.fulldomain.symbols @property def fulldomain(self): return self.args[0] @property def condition(self): return self.args[1] @property def set(self): raise NotImplementedError("Set of Conditional Domain not Implemented") def as_boolean(self): return And(self.fulldomain.as_boolean(), self.condition) 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 is_real = 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() class SinglePSpace(PSpace): """ Represents the probabilities of a set of random events that can be attributed to a single variable/symbol. """ def __new__(cls, s, distribution): if isinstance(s, str): s = Symbol(s) if not isinstance(s, Symbol): raise TypeError("s should have been string or Symbol") return Basic.__new__(cls, s, distribution) @property def value(self): return RandomSymbol(self, self.symbol) @property def symbol(self): return self.args[0] @property def distribution(self): return self.args[1] @property def pdf(self): return self.distribution.pdf(self.symbol) class RandomSymbol(Expr): """ 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.... """ def __new__(cls, pspace, symbol): assert isinstance(symbol, Symbol) assert isinstance(pspace, PSpace) return Basic.__new__(cls, pspace, symbol) is_bounded = True is_finite = True is_Symbol = True is_Atom = True _diff_wrt = True pspace = property(lambda self: self.args[0]) symbol = property(lambda self: self.args[1]) name = property(lambda self: self.symbol.name) is_positive = property(lambda self: self.symbol.is_positive) is_integer = property(lambda self: self.symbol.is_integer) is_real = property(lambda self: self.symbol.is_real or self.pspace.is_real) @property def is_commutative(self): return self.symbol.is_commutative def _hashable_content(self): return self.pspace, self.symbol @property def free_symbols(self): return set([self]) 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 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, *FiniteSet(*spaces)) return obj @property def rs_space_dict(self): d = {} for space in self.spaces: for value in space.values: d[value] = space return d @property def symbols(self): return FiniteSet(val.symbol for val in self.rs_space_dict.keys()) @property def spaces(self): return FiniteSet(*self.args) @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 space.sample().items()]) 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) 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 return Basic.__new__(cls, *domains2) @property def sym_domain_dict(self): return dict((symbol, domain) for domain in self.domains for symbol in domain.symbols) @property def symbols(self): return FiniteSet(sym for domain in self.domains for sym in domain.symbols) @property def domains(self): return self.args @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]) def random_symbols(expr): """ Returns all RandomSymbols within a SymPy Expression. """ try: return list(expr.atoms(RandomSymbol)) except AttributeError: return [] 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()) 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): """ Conditional Random Expression 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).dict {4: 1/3, 5: 1/3, 6: 1/3} Following convention, if the condition is a random symbol then that symbol is considered fixed. >>> from sympy.stats import Normal >>> from sympy import pprint >>> from sympy.abc import z >>> X = Normal('X', 0, 1) >>> Y = Normal('Y', 0, 1) >>> pprint(density(X + Y, Y)(z), use_unicode=False) 2 -(-Y + z) ----------- ___ 2 \/ 2 *e ------------------ ____ 2*\/ pi """ if not random_symbols(condition) or pspace_independent(expr, condition): return expr if isinstance(condition, RandomSymbol): condition = Eq(condition, condition.symbol) condsymbols = random_symbols(condition) if (isinstance(condition, Equality) and len(condsymbols) == 1 and not isinstance(pspace(expr).domain, ConditionalDomain)): rv = tuple(condsymbols)[0] results = solve(condition, rv) return sum(expr.subs(rv, res) for res in results) # 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.xreplace(swapdict) return expr def expectation(expr, condition=None, numsamples=None, evaluate=True, **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) # Create new expr and recompute E if condition is not None: # If there is a condition return expectation(given(expr, condition), evaluate=evaluate) # A few known statements for efficiency if expr.is_Add: # We know that E is Linear return Add(*[expectation(arg, evaluate=evaluate) for arg in expr.args]) # Otherwise case is simple, pass work off to the ProbabilitySpace result = pspace(expr).integrate(expr) if evaluate and hasattr(result, 'doit'): return result.doit(**kwargs) else: return result def probability(condition, given_condition=None, numsamples=None, evaluate=True, **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 result = pspace(condition).probability(condition, **kwargs) if evaluate and hasattr(result, 'doit'): return result.doit() else: return result class Density(Basic): expr = property(lambda self: self.args[0]) @property def condition(self): if len(self.args) > 1: return self.args[1] else: return None def doit(self, evaluate=True, **kwargs): expr, condition = self.expr, self.condition if condition is not None: # Recompute on new conditional expr expr = given(expr, condition, **kwargs) if not random_symbols(expr): return Lambda(x, DiracDelta(x-expr)) if (isinstance(expr, RandomSymbol) and hasattr(expr.pspace, 'distribution') and isinstance(pspace(expr), SinglePSpace)): return expr.pspace.distribution result = pspace(expr).compute_density(expr, **kwargs) if evaluate and hasattr(result, 'doit'): return result.doit() else: return result def density(expr, condition=None, evaluate=True, **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 >>> x = Symbol('x') >>> D = Die('D', 6) >>> X = Normal(x, 0, 1) >>> density(D).dict {1: 1/6, 2: 1/6, 3: 1/6, 4: 1/6, 5: 1/6, 6: 1/6} >>> density(2*D).dict {2: 1/6, 4: 1/6, 6: 1/6, 8: 1/6, 10: 1/6, 12: 1/6} >>> density(X)(x) sqrt(2)*exp(-x**2/2)/(2*sqrt(pi)) """ return Density(expr, condition).doit(evaluate=evaluate, **kwargs) def cdf(expr, condition=None, evaluate=True, **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).dict {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 result = pspace(expr).compute_cdf(expr, **kwargs) if evaluate and hasattr(result, 'doit'): return result.doit() else: return result 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.xreplace(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.xreplace(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) if not symbols: return expr swapdict = dict([(rv, rv.symbol) for rv in symbols]) return expr.xreplace(swapdict) class NamedArgsMixin(object): _argnames = () def __getattr__(self, attr): try: return self.args[self._argnames.index(attr)] except ValueError: raise AttributeError("'%s' object has not attribute '%s'" % ( type(self).__name__, attr)) 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) sympy-0.7.4.1/sympy/stats/tests/0000755000175000017500000000000012253362407016725 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/stats/tests/test_mix.py0000644000175000017500000000065012253362407021134 0ustar georgeskgeorgeskfrom sympy.stats import Poisson, Beta from sympy.stats.rv import pspace, ProductPSpace, density from sympy.stats.drv_types import PoissonDistribution from sympy import Symbol, Eq def test_density(): x = Symbol('x') l = Symbol('l', positive=True) rate = Beta(l, 2, 3) X = Poisson(x, rate) assert isinstance(pspace(X), ProductPSpace) assert density(X, Eq(rate, rate.symbol)) == PoissonDistribution(l) sympy-0.7.4.1/sympy/stats/tests/test_continuous_rv.py0000644000175000017500000004566512253362407023273 0ustar georgeskgeorgeskfrom sympy.stats import (P, E, where, density, variance, covariance, skewness, given, pspace, cdf, ContinuousRV, sample, Arcsin, Benini, Beta, BetaPrime, Cauchy, Chi, ChiSquared, ChiNoncentral, 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, correlation, moment, cmoment, smoment) from sympy import (Symbol, Dummy, Abs, exp, S, N, pi, simplify, Interval, erf, Eq, log, lowergamma, Sum, symbols, sqrt, And, gamma, beta, Eq, log, lowergamma, Sum, symbols, sqrt, And, gamma, beta, Piecewise, Integral, sin, cos, besseli, factorial, binomial, floor) from sympy.stats.crv_types import NormalDistribution from sympy.stats.rv import ProductPSpace from sympy.utilities.pytest import raises, XFAIL, slow from sympy.core.compatibility import xrange oo = S.Infinity x, y, z = map(Symbol, 'xyz') def test_single_normal(): mu = Symbol('mu', real=True, bounded=True) sigma = Symbol('sigma', real=True, positive=True, bounded=True) X = Normal('x', 0, 1) Y = X*sigma + mu assert simplify(E(Y)) == mu assert simplify(variance(Y)) == sigma**2 pdf = density(Y) x = Symbol('x') assert (pdf(x) == 2**S.Half*exp(-(x - mu)**2/(2*sigma**2))/(2*pi**S.Half*sigma)) assert P(X**2 < 1) == erf(2**S.Half/2) assert E(X, Eq(X, mu)) == mu @XFAIL def test_conditional_1d(): X = Normal('x', 0, 1) Y = given(X, X >= 0) assert density(Y) == 2 * density(X) assert Y.pspace.domain.set == Interval(0, oo) assert E(Y) == sqrt(2) / sqrt(pi) assert E(X**2) == E(Y**2) def test_ContinuousDomain(): X = Normal('x', 0, 1) assert where(X**2 <= 1).set == Interval(-1, 1) assert where(X**2 <= 1).symbol == X.symbol where(And(X**2 <= 1, X >= 0)).set == Interval(0, 1) raises(ValueError, lambda: where(sin(X) > 1)) Y = given(X, X >= 0) assert Y.pspace.domain.set == Interval(0, oo) def test_multiple_normal(): X, Y = Normal('x', 0, 1), Normal('y', 0, 1) assert E(X + Y) == 0 assert variance(X + Y) == 2 assert variance(X + X) == 4 assert covariance(X, Y) == 0 assert covariance(2*X + Y, -X) == -2*variance(X) assert skewness(X) == 0 assert skewness(X + Y) == 0 assert correlation(X, Y) == 0 assert correlation(X, X + Y) == correlation(X, X - Y) assert moment(X, 2) == 1 assert cmoment(X, 3) == 0 assert moment(X + Y, 4) == 12 assert cmoment(X, 2) == variance(X) assert smoment(X*X, 2) == 1 assert smoment(X + Y, 3) == skewness(X + Y) assert E(X, Eq(X + Y, 0)) == 0 assert variance(X, Eq(X + Y, 0)) == S.Half @slow def test_symbolic(): mu1, mu2 = symbols('mu1 mu2', real=True, bounded=True) s1, s2 = symbols('sigma1 sigma2', real=True, bounded=True, positive=True) rate = Symbol('lambda', real=True, positive=True, bounded=True) X = Normal('x', mu1, s1) Y = Normal('y', mu2, s2) Z = Exponential('z', rate) a, b, c = symbols('a b c', real=True, bounded=True) assert E(X) == mu1 assert E(X + Y) == mu1 + mu2 assert E(a*X + b) == a*E(X) + b assert variance(X) == s1**2 assert simplify(variance(X + a*Y + b)) == variance(X) + a**2*variance(Y) assert E(Z) == 1/rate assert E(a*Z + b) == a*E(Z) + b assert E(X + a*Z + b) == mu1 + a/rate + b def test_cdf(): X = Normal('x', 0, 1) d = cdf(X) assert P(X < 1) == d(1) assert d(0) == S.Half d = cdf(X, X > 0) # given X>0 assert d(0) == 0 Y = Exponential('y', 10) d = cdf(Y) assert d(-5) == 0 assert P(Y > 3) == 1 - d(3) raises(ValueError, lambda: cdf(X + Y)) Z = Exponential('z', 1) f = cdf(Z) z = Symbol('z') assert f(z) == Piecewise((1 - exp(-z), z >= 0), (0, True)) def test_sample(): z = Symbol('z') Z = ContinuousRV(z, exp(-z), set=Interval(0, oo)) assert sample(Z) in Z.pspace.domain.set sym, val = list(Z.pspace.sample().items())[0] assert sym == Z and val in Interval(0, oo) def test_ContinuousRV(): x = Symbol('x') pdf = sqrt(2)*exp(-x**2/2)/(2*sqrt(pi)) # Normal distribution # X and Y should be equivalent X = ContinuousRV(x, pdf) Y = Normal('y', 0, 1) assert variance(X) == variance(Y) assert P(X > 0) == P(Y > 0) def test_arcsin(): a = Symbol("a", real=True) b = Symbol("b", real=True) X = Arcsin('x', a, b) assert density(X)(x) == 1/(pi*sqrt((-x + b)*(x - a))) def test_benini(): alpha = Symbol("alpha", positive=True) b = Symbol("beta", positive=True) sigma = Symbol("sigma", positive=True) X = Benini('x', alpha, b, sigma) assert density(X)(x) == ((alpha/x + 2*b*log(x/sigma)/x) *exp(-alpha*log(x/sigma) - b*log(x/sigma)**2)) def test_beta(): a, b = symbols('alpha beta', positive=True) B = Beta('x', a, b) assert pspace(B).domain.set == Interval(0, 1) dens = density(B) x = Symbol('x') assert dens(x) == x**(a - 1)*(1 - x)**(b - 1) / beta(a, b) # This is too slow # assert E(B) == a / (a + b) # assert variance(B) == (a*b) / ((a+b)**2 * (a+b+1)) # Full symbolic solution is too much, test with numeric version a, b = 1, 2 B = Beta('x', a, b) assert E(B) == a / S(a + b) assert variance(B) == (a*b) / S((a + b)**2 * (a + b + 1)) def test_betaprime(): alpha = Symbol("alpha", positive=True) beta = Symbol("beta", positive=True) X = BetaPrime('x', alpha, beta) assert density(X)(x) == (x**(alpha - 1)*(x + 1)**(-alpha - beta) *gamma(alpha + beta)/(gamma(alpha)*gamma(beta))) def test_cauchy(): x0 = Symbol("x0") gamma = Symbol("gamma", positive=True) X = Cauchy('x', x0, gamma) assert density(X)(x) == 1/(pi*gamma*(1 + (x - x0)**2/gamma**2)) def test_chi(): k = Symbol("k", integer=True) X = Chi('x', k) assert density(X)(x) == 2**(-k/2 + 1)*x**(k - 1)*exp(-x**2/2)/gamma(k/2) def test_chi_noncentral(): k = Symbol("k", integer=True) l = Symbol("l") X = ChiNoncentral("x", k, l) assert density(X)(x) == (x**k*l*(x*l)**(-k/2)* exp(-x**2/2 - l**2/2)*besseli(k/2 - 1, x*l)) def test_chi_squared(): k = Symbol("k", integer=True) X = ChiSquared('x', k) assert density(X)(x) == 2**(-k/2)*x**(k/2 - 1)*exp(-x/2)/gamma(k/2) def test_dagum(): p = Symbol("p", positive=True) b = Symbol("b", positive=True) a = Symbol("a", positive=True) X = Dagum('x', p, a, b) assert density(X)(x) == a*p*(x/b)**(a*p)*((x/b)**a + 1)**(-p - 1)/x def test_erlang(): k = Symbol("k", integer=True, positive=True) l = Symbol("l", positive=True) X = Erlang("x", k, l) assert density(X)(x) == x**(k - 1)*l**k*exp(-x*l)/gamma(k) def test_exponential(): rate = Symbol('lambda', positive=True, real=True, bounded=True) X = Exponential('x', rate) assert E(X) == 1/rate assert variance(X) == 1/rate**2 assert skewness(X) == 2 assert skewness(X) == smoment(X, 3) assert smoment(2*X, 4) == smoment(X, 4) assert moment(X, 3) == 3*2*1/rate**3 assert P(X > 0) == S(1) assert P(X > 1) == exp(-rate) assert P(X > 10) == exp(-10*rate) assert where(X <= 1).set == Interval(0, 1) def test_f_distribution(): d1 = Symbol("d1", positive=True) d2 = Symbol("d2", positive=True) X = FDistribution("x", d1, d2) assert density(X)(x) == (d2**(d2/2)*sqrt((x*d1)**d1 * (x*d1 + d2)**(-d1 - d2))*gamma(d1/2 + d2/2)/(x*gamma(d1/2)*gamma(d2/2))) def test_fisher_z(): d1 = Symbol("d1", positive=True) d2 = Symbol("d2", positive=True) X = FisherZ("x", d1, d2) assert density(X)(x) == (2*d1**(d1/2)*d2**(d2/2)* (d1*exp(2*x) + d2)**(-d1/2 - d2/2)* exp(x*d1)*gamma(d1/2 + d2/2)/(gamma(d1/2)*gamma(d2/2))) def test_frechet(): a = Symbol("a", positive=True) s = Symbol("s", positive=True) m = Symbol("m", real=True) X = Frechet("x", a, s=s, m=m) assert density(X)(x) == a*((x - m)/s)**(-a - 1)*exp(-((x - m)/s)**(-a))/s def test_gamma(): k = Symbol("k", positive=True) theta = Symbol("theta", positive=True) X = Gamma('x', k, theta) assert density(X)(x) == x**(k - 1)*theta**(-k)*exp(-x/theta)/gamma(k) assert cdf(X, meijerg=True)(z) == Piecewise( (-k*lowergamma(k, 0)/gamma(k + 1) + k*lowergamma(k, z/theta)/gamma(k + 1), z >= 0), (0, True)) # assert simplify(variance(X)) == k*theta**2 # handled numerically below assert E(X) == moment(X, 1) k, theta = symbols('k theta', real=True, bounded=True, positive=True) X = Gamma('x', k, theta) assert simplify(E(X)) == k*theta # can't get things to simplify on this one so we use subs assert variance(X).subs(k, 5) == (k*theta**2).subs(k, 5) # The following is too slow # assert simplify(skewness(X)).subs(k, 5) == (2/sqrt(k)).subs(k, 5) def test_gamma_inverse(): a = Symbol("a", positive=True) b = Symbol("b", positive=True) X = GammaInverse("x", a, b) assert density(X)(x) == x**(-a - 1)*b**a*exp(-b/x)/gamma(a) def test_kumaraswamy(): a = Symbol("a", positive=True) b = Symbol("b", positive=True) X = Kumaraswamy("x", a, b) assert density(X)(x) == x**(a - 1)*a*b*(-x**a + 1)**(b - 1) def test_laplace(): mu = Symbol("mu") b = Symbol("b", positive=True) X = Laplace('x', mu, b) assert density(X)(x) == exp(-Abs(x - mu)/b)/(2*b) def test_logistic(): mu = Symbol("mu", real=True) s = Symbol("s", positive=True) X = Logistic('x', mu, s) assert density(X)(x) == exp((-x + mu)/s)/(s*(exp((-x + mu)/s) + 1)**2) def test_lognormal(): mean = Symbol('mu', real=True, bounded=True) std = Symbol('sigma', positive=True, real=True, bounded=True) X = LogNormal('x', mean, std) # The sympy integrator can't do this too well #assert E(X) == exp(mean+std**2/2) #assert variance(X) == (exp(std**2)-1) * exp(2*mean + std**2) # Right now, only density function and sampling works # Test sampling: Only e^mean in sample std of 0 for i in range(3): X = LogNormal('x', i, 0) assert S(sample(X)) == N(exp(i)) # The sympy integrator can't do this too well #assert E(X) == mu = Symbol("mu", real=True) sigma = Symbol("sigma", positive=True) X = LogNormal('x', mu, sigma) assert density(X)(x) == (sqrt(2)*exp(-(-mu + log(x))**2 /(2*sigma**2))/(2*x*sqrt(pi)*sigma)) X = LogNormal('x', 0, 1) # Mean 0, standard deviation 1 assert density(X)(x) == sqrt(2)*exp(-log(x)**2/2)/(2*x*sqrt(pi)) def test_maxwell(): a = Symbol("a", positive=True) X = Maxwell('x', a) assert density(X)(x) == (sqrt(2)*x**2*exp(-x**2/(2*a**2))/ (sqrt(pi)*a**3)) assert E(X) == 2*sqrt(2)*a/sqrt(pi) assert simplify(variance(X)) == a**2*(-8 + 3*pi)/pi def test_nakagami(): mu = Symbol("mu", positive=True) omega = Symbol("omega", positive=True) X = Nakagami('x', mu, omega) assert density(X)(x) == (2*x**(2*mu - 1)*mu**mu*omega**(-mu) *exp(-x**2*mu/omega)/gamma(mu)) assert simplify(E(X, meijerg=True)) == (sqrt(mu)*sqrt(omega) *gamma(mu + S.Half)/gamma(mu + 1)) assert simplify(variance(X, meijerg=True)) == ( omega - omega*gamma(mu + S(1)/2)**2/(gamma(mu)*gamma(mu + 1))) def test_pareto(): xm, beta = symbols('xm beta', positive=True, bounded=True) alpha = beta + 5 X = Pareto('x', xm, alpha) dens = density(X) x = Symbol('x') assert dens(x) == x**(-(alpha + 1))*xm**(alpha)*(alpha) # These fail because SymPy can not deduce that 1/xm != 0 # assert simplify(E(X)) == alpha*xm/(alpha-1) # assert simplify(variance(X)) == xm**2*alpha / ((alpha-1)**2*(alpha-2)) def test_pareto_numeric(): xm, beta = 3, 2 alpha = beta + 5 X = Pareto('x', xm, alpha) assert E(X) == alpha*xm/S(alpha - 1) assert variance(X) == xm**2*alpha / S(((alpha - 1)**2*(alpha - 2))) # Skewness tests too slow. Try shortcutting function? def test_raised_cosine(): mu = Symbol("mu", real=True) s = Symbol("s", positive=True) X = RaisedCosine("x", mu, s) assert density(X)(x) == (Piecewise(((cos(pi*(x - mu)/s) + 1)/(2*s), And(x <= mu + s, mu - s <= x)), (0, True))) def test_rayleigh(): sigma = Symbol("sigma", positive=True) X = Rayleigh('x', sigma) assert density(X)(x) == x*exp(-x**2/(2*sigma**2))/sigma**2 assert E(X) == sqrt(2)*sqrt(pi)*sigma/2 assert variance(X) == -pi*sigma**2/2 + 2*sigma**2 def test_studentt(): nu = Symbol("nu", positive=True) X = StudentT('x', nu) assert density(X)(x) == ((x**2/nu + 1)**(-nu/2 - S.Half) *gamma(nu/2 + S.Half)/(sqrt(pi)*sqrt(nu)*gamma(nu/2))) @XFAIL def test_triangular(): a = Symbol("a") b = Symbol("b") c = Symbol("c") X = Triangular('x', a, b, c) assert density(X)(x) == Piecewise( ((2*x - 2*a)/((-a + b)*(-a + c)), And(a <= x, x < c)), (2/(-a + b), x == c), ((-2*x + 2*b)/((-a + b)*(b - c)), And(x <= b, c < x)), (0, True)) def test_quadratic_u(): a = Symbol("a", real=True) b = Symbol("b", real=True) X = QuadraticU("x", a, b) assert density(X)(x) == (Piecewise((12*(x - a/2 - b/2)**2/(-a + b)**3, And(x <= b, a <= x)), (0, True))) def test_uniform(): l = Symbol('l', real=True, bounded=True) w = Symbol('w', positive=True, bounded=True) X = Uniform('x', l, l + w) assert simplify(E(X)) == l + w/2 assert simplify(variance(X)) == w**2/12 # With numbers all is well X = Uniform('x', 3, 5) assert P(X < 3) == 0 and P(X > 5) == 0 assert P(X < 4) == P(X > 4) == S.Half @XFAIL def test_uniform_P(): """ This stopped working because SingleContinuousPSpace.compute_density no longer calls integrate on a DiracDelta but rather just solves directly. integrate used to call UniformDistribution.expectation which special-cased subsed out the Min and Max terms that Uniform produces I decided to regress on this class for general cleanliness (and I suspect speed) of the algorithm. """ l = Symbol('l', real=True, bounded=True) w = Symbol('w', positive=True, bounded=True) X = Uniform('x', l, l + w) assert P(X < l) == 0 and P(X > l + w) == 0 @XFAIL def test_uniformsum(): n = Symbol("n", integer=True) _k = Symbol("k") X = UniformSum('x', n) assert density(X)(x) == (Sum((-1)**_k*(-_k + x)**(n - 1) *binomial(n, _k), (_k, 0, floor(x)))/factorial(n - 1)) def test_von_mises(): mu = Symbol("mu") k = Symbol("k", positive=True) X = VonMises("x", mu, k) assert density(X)(x) == exp(k*cos(x - mu))/(2*pi*besseli(0, k)) def test_weibull(): a, b = symbols('a b', positive=True) X = Weibull('x', a, b) assert simplify(E(X)) == simplify(a * gamma(1 + 1/b)) assert simplify(variance(X)) == simplify(a**2 * gamma(1 + 2/b) - E(X)**2) # Skewness tests too slow. Try shortcutting function? def test_weibull_numeric(): # Test for integers and rationals a = 1 bvals = [S.Half, 1, S(3)/2, 5] for b in bvals: X = Weibull('x', a, b) assert simplify(E(X)) == simplify(a * gamma(1 + 1/S(b))) assert simplify(variance(X)) == simplify( a**2 * gamma(1 + 2/S(b)) - E(X)**2) # Not testing Skew... it's slow with int/frac values > 3/2 def test_wignersemicircle(): R = Symbol("R", positive=True) X = WignerSemicircle('x', R) assert density(X)(x) == 2*sqrt(-x**2 + R**2)/(pi*R**2) assert E(X) == 0 def test_prefab_sampling(): N = Normal('X', 0, 1) L = LogNormal('L', 0, 1) E = Exponential('Ex', 1) P = Pareto('P', 1, 3) W = Weibull('W', 1, 1) U = Uniform('U', 0, 1) B = Beta('B', 2, 5) G = Gamma('G', 1, 3) variables = [N, L, E, P, W, U, B, G] niter = 10 for var in variables: for i in xrange(niter): assert sample(var) in var.pspace.domain.set def test_input_value_assertions(): a, b = symbols('a b') p, q = symbols('p q', positive=True) raises(ValueError, lambda: Normal('x', 3, 0)) raises(ValueError, lambda: Normal('x', a, b)) Normal('X', a, p) # No error raised raises(ValueError, lambda: Exponential('x', a)) Exponential('Ex', p) # No error raised for fn in [Pareto, Weibull, Beta, Gamma]: raises(ValueError, lambda: fn('x', a, p)) raises(ValueError, lambda: fn('x', p, a)) fn('x', p, q) # No error raised @XFAIL def test_unevaluated(): X = Normal('x', 0, 1) assert E(X, evaluate=False) == ( Integral(sqrt(2)*x*exp(-x**2/2)/(2*sqrt(pi)), (x, -oo, oo))) assert E(X + 1, evaluate=False) == ( Integral(sqrt(2)*x*exp(-x**2/2)/(2*sqrt(pi)), (x, -oo, oo)) + 1) assert P(X > 0, evaluate=False) == ( Integral(sqrt(2)*exp(-x**2/2)/(2*sqrt(pi)), (x, 0, oo))) assert P(X > 0, X**2 < 1, evaluate=False) == ( Integral(sqrt(2)*exp(-x**2/2)/(2*sqrt(pi)* Integral(sqrt(2)*exp(-x**2/2)/(2*sqrt(pi)), (x, -1, 1))), (x, 0, 1))) def test_probability_unevaluated(): T = Normal('T', 30, 3) assert type(P(T > 33, evaluate=False)) == Integral def test_density_unevaluated(): X = Normal('X', 0, 1) Y = Normal('Y', 0, 2) assert isinstance(density(X+Y, evaluate=False)(z), Integral) def test_NormalDistribution(): nd = NormalDistribution(0, 1) x = Symbol('x') assert nd.cdf(x) == erf(sqrt(2)*x/2)/2 + S.One/2 assert isinstance(nd.sample(), float) or nd.sample().is_Number assert nd.expectation(1, x) == 1 assert nd.expectation(x, x) == 0 assert nd.expectation(x**2, x) == 1 def test_random_parameters(): mu = Normal('mu', 2, 3) meas = Normal('T', mu, 1) assert density(meas, evaluate=False)(z) assert isinstance(pspace(meas), ProductPSpace) #assert density(meas, evaluate=False)(z) == Integral(mu.pspace.pdf * # meas.pspace.pdf, (mu.symbol, -oo, oo)).subs(meas.symbol, z) def test_random_parameters_given(): mu = Normal('mu', 2, 3) meas = Normal('T', mu, 1) assert given(meas, Eq(mu, 5)) == Normal('T', 5, 1) def test_conjugate_priors(): mu = Normal('mu', 2, 3) x = Normal('x', mu, 1) assert isinstance(simplify(density(mu, Eq(x, y), evaluate=False)(z)), Integral) def test_difficult_univariate(): """ Since using solve in place of deltaintegrate we're able to perform substantially more complex density computations on single continuous random variables """ x = Normal('x', 0, 1) assert density(x**3) assert density(exp(x**2)) assert density(log(x)) sympy-0.7.4.1/sympy/stats/tests/test_rv.py0000644000175000017500000001134112253362407020765 0ustar georgeskgeorgeskfrom sympy import (EmptySet, FiniteSet, S, Symbol, Interval, exp, erf, sqrt, symbols, simplify, Eq, cos, And, Tuple, integrate, oo, sin, Sum, Basic, DiracDelta) from sympy.stats import (Die, Normal, Exponential, P, E, variance, covariance, skewness, density, given, independent, dependent, where, pspace, random_symbols, sample) from sympy.stats.rv import ProductPSpace, rs_swap, Density, NamedArgsMixin from sympy.utilities.pytest import raises, XFAIL def test_where(): X, Y = Die('X'), Die('Y') Z = Normal('Z', 0, 1) assert where(Z**2 <= 1).set == Interval(-1, 1) assert where( Z**2 <= 1).as_boolean() == Interval(-1, 1).as_relational(Z.symbol) assert where(And(X > Y, Y > 4)).as_boolean() == And( Eq(X.symbol, 6), Eq(Y.symbol, 5)) assert len(where(X < 3).set) == 2 assert 1 in where(X < 3).set X, Y = Normal('X', 0, 1), Normal('Y', 0, 1) assert where(And(X**2 <= 1, X >= 0)).set == Interval(0, 1) XX = given(X, And(X**2 <= 1, X >= 0)) assert XX.pspace.domain.set == Interval(0, 1) assert XX.pspace.domain.as_boolean() == \ And(0 <= X.symbol, X.symbol**2 <= 1) with raises(TypeError): XX = given(X, X + 3) def test_random_symbols(): X, Y = Normal('X', 0, 1), Normal('Y', 0, 1) assert set(random_symbols(2*X + 1)) == set((X,)) assert set(random_symbols(2*X + Y)) == set((X, Y)) assert set(random_symbols(2*X + Y.symbol)) == set((X,)) assert set(random_symbols(2)) == set() def test_pspace(): X, Y = Normal('X', 0, 1), Normal('Y', 0, 1) assert not pspace(5 + 3) assert pspace(X) == X.pspace assert pspace(2*X + 1) == X.pspace assert pspace(2*X + Y) == ProductPSpace(Y.pspace, X.pspace) def test_rs_swap(): X = Normal('x', 0, 1) Y = Exponential('y', 1) XX = Normal('x', 0, 2) YY = Normal('y', 0, 3) expr = 2*X + Y assert expr.subs(rs_swap((X, Y), (YY, XX))) == 2*XX + YY def test_RandomSymbol(): X = Normal('x', 0, 1) Y = Normal('x', 0, 2) assert X.symbol == Y.symbol assert X != Y assert X.name == X.symbol.name X = Normal('lambda', 0, 1) # make sure we can use protected terms X = Normal('Lambda', 0, 1) # make sure we can use SymPy terms def test_RandomSymbol_diff(): X = Normal('x', 0, 1) assert (2*X).diff(X) def test_overlap(): X = Normal('x', 0, 1) Y = Normal('x', 0, 2) raises(ValueError, lambda: P(X > Y)) def test_ProductPSpace(): X = Normal('X', 0, 1) Y = Normal('Y', 0, 1) px = X.pspace py = Y.pspace assert pspace(X + Y) == ProductPSpace(px, py) assert pspace(X + Y) == ProductPSpace(py, px) def test_E(): assert E(5) == 5 def test_Sample(): X = Die('X', 6) Y = Normal('Y', 0, 1) z = Symbol('z') assert sample(X) in [1, 2, 3, 4, 5, 6] assert sample(X + Y).is_Float P(X + Y > 0, Y < 0, numsamples=10).is_number assert E(X + Y, numsamples=10).is_number assert variance(X + Y, numsamples=10).is_number raises(ValueError, lambda: P(Y > z, numsamples=5)) assert P(sin(Y) <= 1, numsamples=10) == 1 assert P(sin(Y) <= 1, cos(Y) < 1, numsamples=10) == 1 # Make sure this doesn't raise an error E(Sum(1/z**Y, (z, 1, oo)), Y > 2, numsamples=3) def test_given(): X = Normal('X', 0, 1) Y = Normal('Y', 0, 1) A = given(X, True) B = given(X, Y > 2) assert X == A == B def test_dependence(): X, Y = Die('X'), Die('Y') assert independent(X, 2*Y) assert not dependent(X, 2*Y) X, Y = Normal('X', 0, 1), Normal('Y', 0, 1) assert independent(X, Y) assert dependent(X, 2*X) # Create a dependency XX, YY = given(Tuple(X, Y), Eq(X + Y, 3)) assert dependent(XX, YY) @XFAIL def test_dependent_finite(): X, Y = Die('X'), Die('Y') # Dependence testing requires symbolic conditions which currently break # finite random variables assert dependent(X, Y + X) XX, YY = given(Tuple(X, Y), X + Y > 5) # Create a dependency assert dependent(XX, YY) def test_normality(): X, Y = Normal('X', 0, 1), Normal('Y', 0, 1) x, z = symbols('x, z', real=True) dens = density(X - Y, Eq(X + Y, z)) assert integrate(dens(x), (x, -oo, oo)) == 1 def test_Density(): X = Die('X', 6) d = Density(X) assert d.doit() == density(X) def test_NamedArgsMixin(): class Foo(Basic, NamedArgsMixin): _argnames = 'foo', 'bar' a = Foo(1, 2) assert a.foo == 1 assert a.bar == 2 raises(AttributeError, lambda: a.baz) class Bar(Basic, NamedArgsMixin): pass raises(AttributeError, lambda: Bar(1, 2).foo) def test_density_constant(): assert density(3)(2) == 0 assert density(3)(3) == DiracDelta(0) def test_real(): x = Normal('x', 0, 1) assert x.is_real sympy-0.7.4.1/sympy/stats/tests/test_finite_rv.py0000644000175000017500000001643612253362407022335 0ustar georgeskgeorgeskfrom sympy import (EmptySet, FiniteSet, S, Symbol, Interval, exp, erf, sqrt, symbols, simplify, Eq, cos, And, Tuple, Or, Dict, sympify, binomial, factor, cancel) from sympy.stats import (DiscreteUniform, Die, Bernoulli, Coin, Binomial, Hypergeometric, P, E, variance, covariance, skewness, sample, density, given, independent, dependent, where, FiniteRV, pspace, cdf, correlation, moment, cmoment, smoment) from sympy.utilities.pytest import raises, slow from sympy.abc import p oo = S.Infinity def BayesTest(A, B): assert P(A, B) == P(And(A, B)) / P(B) assert P(A, B) == P(B, A) * P(A) / P(B) def test_discreteuniform(): # Symbolic a, b, c = symbols('a b c') X = DiscreteUniform('X', [a, b, c]) assert E(X) == (a + b + c)/3 assert simplify(variance(X) - ((a**2 + b**2 + c**2)/3 - (a/3 + b/3 + c/3)**2)) == 0 assert P(Eq(X, a)) == P(Eq(X, b)) == P(Eq(X, c)) == S('1/3') Y = DiscreteUniform('Y', range(-5, 5)) # Numeric assert E(Y) == S('-1/2') assert variance(Y) == S('33/4') for x in range(-5, 5): assert P(Eq(Y, x)) == S('1/10') assert P(Y <= x) == S(x + 6)/10 assert P(Y >= x) == S(5 - x)/10 assert dict(density(Die('D', 6)).items()) == \ dict(density(DiscreteUniform('U', range(1, 7))).items()) def test_dice(): # TODO: Make iid method! X, Y, Z = Die('X', 6), Die('Y', 6), Die('Z', 6) a, b = symbols('a b') assert E(X) == 3 + S.Half assert variance(X) == S(35)/12 assert E(X + Y) == 7 assert E(X + X) == 7 assert E(a*X + b) == a*E(X) + b assert variance(X + Y) == variance(X) + variance(Y) == cmoment(X + Y, 2) assert variance(X + X) == 4 * variance(X) == cmoment(X + X, 2) assert cmoment(X, 0) == 1 assert cmoment(4*X, 3) == 64*cmoment(X, 3) assert covariance(X, Y) == S.Zero assert covariance(X, X + Y) == variance(X) assert density(Eq(cos(X*S.Pi), 1))[True] == S.Half assert correlation(X, Y) == 0 assert correlation(X, Y) == correlation(Y, X) assert smoment(X + Y, 3) == skewness(X + Y) assert smoment(X, 0) == 1 assert P(X > 3) == S.Half assert P(2*X > 6) == S.Half assert P(X > Y) == S(5)/12 assert P(Eq(X, Y)) == P(Eq(X, 1)) assert E(X, X > 3) == 5 == moment(X, 1, 0, X > 3) assert E(X, Y > 3) == E(X) == moment(X, 1, 0, Y > 3) assert E(X + Y, Eq(X, Y)) == E(2*X) assert moment(X, 0) == 1 assert moment(5*X, 2) == 25*moment(X, 2) assert P(X > 3, X > 3) == S.One assert P(X > Y, Eq(Y, 6)) == S.Zero assert P(Eq(X + Y, 12)) == S.One/36 assert P(Eq(X + Y, 12), Eq(X, 6)) == S.One/6 assert density(X + Y) == density(Y + Z) != density(X + X) d = density(2*X + Y**Z) assert d[S(22)] == S.One/108 and d[S(4100)] == S.One/216 and S(3130) not in d assert pspace(X).domain.as_boolean() == Or( *[Eq(X.symbol, i) for i in [1, 2, 3, 4, 5, 6]]) assert where(X > 3).set == FiniteSet(4, 5, 6) def test_given(): X = Die('X', 6) assert density(X, X > 5) == {S(6): S(1)} assert where(X > 2, X > 5).as_boolean() == Eq(X.symbol, 6) assert sample(X, X > 5) == 6 def test_domains(): X, Y = Die('x', 6), Die('y', 6) x, y = X.symbol, Y.symbol # Domains d = where(X > Y) assert d.condition == (x > y) d = where(And(X > Y, Y > 3)) assert d.as_boolean() == Or(And(Eq(x, 5), Eq(y, 4)), And(Eq(x, 6), Eq(y, 5)), And(Eq(x, 6), Eq(y, 4))) assert len(d.elements) == 3 assert len(pspace(X + Y).domain.elements) == 36 Z = Die('x', 4) raises(ValueError, lambda: P(X > Z)) # Two domains with same internal symbol pspace(X + Y).domain.set == FiniteSet(1, 2, 3, 4, 5, 6)**2 assert where(X > 3).set == FiniteSet(4, 5, 6) assert X.pspace.domain.dict == FiniteSet( Dict({X.symbol: i}) for i in range(1, 7)) assert where(X > Y).dict == FiniteSet(Dict({X.symbol: i, Y.symbol: j}) for i in range(1, 7) for j in range(1, 7) if i > j) def test_dice_bayes(): X, Y, Z = Die('X', 6), Die('Y', 6), Die('Z', 6) BayesTest(X > 3, X + Y < 5) BayesTest(Eq(X - Y, Z), Z > Y) BayesTest(X > 3, X > 2) def test_bernoulli(): p, a, b = symbols('p a b') X = Bernoulli('B', p, a, b) assert E(X) == a*p + b*(-p + 1) assert density(X)[a] == p assert density(X)[b] == 1 - p X = Bernoulli('B', p, 1, 0) assert E(X) == p assert simplify(variance(X)) == p*(1 - p) E(a*X + b) == a*E(X) + b variance(a*X + b) == a**2 * variance(X) def test_cdf(): D = Die('D', 6) o = S.One assert cdf( D) == sympify({1: o/6, 2: o/3, 3: o/2, 4: 2*o/3, 5: 5*o/6, 6: o}) def test_coins(): C, D = Coin('C'), Coin('D') H, T = symbols('H, T') assert P(Eq(C, D)) == S.Half assert density(Tuple(C, D)) == {(H, H): S.One/4, (H, T): S.One/4, (T, H): S.One/4, (T, T): S.One/4} assert dict(density(C).items()) == {H: S.Half, T: S.Half} F = Coin('F', S.One/10) assert P(Eq(F, H)) == S(1)/10 d = pspace(C).domain assert d.as_boolean() == Or(Eq(C.symbol, H), Eq(C.symbol, T)) raises(ValueError, lambda: P(C > D)) # Can't intelligently compare H to T def test_binomial_numeric(): nvals = range(5) pvals = [0, S(1)/4, S.Half, S(3)/4, 1] for n in nvals: for p in pvals: X = Binomial('X', n, p) assert Eq(E(X), n*p) assert Eq(variance(X), n*p*(1 - p)) if n > 0 and 0 < p < 1: assert Eq(skewness(X), (1 - 2*p)/sqrt(n*p*(1 - p))) for k in range(n + 1): assert Eq(P(Eq(X, k)), binomial(n, k)*p**k*(1 - p)**(n - k)) @slow def test_binomial_symbolic(): n = 10 # Because we're using for loops, can't do symbolic n p = symbols('p', positive=True) X = Binomial('X', n, p) assert simplify(E(X)) == n*p == simplify(moment(X, 1)) assert simplify(variance(X)) == n*p*(1 - p) == simplify(cmoment(X, 2)) assert cancel((skewness(X) - (1-2*p)/sqrt(n*p*(1-p)))) == 0 # Test ability to change success/failure winnings H, T = symbols('H T') Y = Binomial('Y', n, p, succ=H, fail=T) assert simplify(E(Y) - (n*(H*p + T*(1 - p)))) == 0 def test_hypergeometric_numeric(): for N in range(1, 5): for m in range(0, N + 1): for n in range(1, N + 1): X = Hypergeometric('X', N, m, n) N, m, n = map(sympify, (N, m, n)) assert sum(density(X).values()) == 1 assert E(X) == n * m / N if N > 1: assert variance(X) == n*(m/N)*(N - m)/N*(N - n)/(N - 1) # Only test for skewness when defined if N > 2 and 0 < m < N and n < N: assert Eq(skewness(X), simplify((N - 2*m)*sqrt(N - 1)*(N - 2*n) / (sqrt(n*m*(N - m)*(N - n))*(N - 2)))) def test_FiniteRV(): F = FiniteRV('F', {1: S.Half, 2: S.One/4, 3: S.One/4}) assert dict(density(F).items()) == {S(1): S.Half, S(2): S.One/4, S(3): S.One/4} assert P(F >= 2) == S.Half assert pspace(F).domain.as_boolean() == Or( *[Eq(F.symbol, i) for i in [1, 2, 3]]) def test_density_call(): x = Bernoulli('x', p) d = density(x) assert d(0) == 1 - p assert d(S.Zero) == 1 - p assert d(5) == 0 assert 0 in d assert 5 not in d assert d(S(0)) == d[S(0)] sympy-0.7.4.1/sympy/stats/tests/test_discrete_rv.py0000644000175000017500000000161212253362407022647 0ustar georgeskgeorgeskfrom sympy.stats.drv_types import (PoissonDistribution, GeometricDistribution, Poisson) from sympy.abc import x from sympy import S, Sum from sympy.stats import E, variance, density def test_PoissonDistribution(): l = 3 p = PoissonDistribution(l) assert abs(p.cdf(10).evalf() - 1) < .001 assert p.expectation(x, x) == l assert p.expectation(x**2, x) - p.expectation(x, x)**2 == l def test_Poisson(): l = 3 x = Poisson('x', l) assert E(x) == l assert variance(x) == l assert density(x) == PoissonDistribution(l) assert isinstance(E(x, evaluate=False), Sum) assert isinstance(E(2*x, evaluate=False), Sum) def test_GeometricDistribution(): p = S.One / 5 d = GeometricDistribution(p) assert d.expectation(x, x) == 1/p assert d.expectation(x**2, x) - d.expectation(x, x)**2 == (1-p)/p**2 assert abs(d.cdf(20000).evalf() - 1) < .001 sympy-0.7.4.1/sympy/stats/tests/__init__.py0000644000175000017500000000000012253362407021024 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/stats/__init__.py0000644000175000017500000000443112253362407017676 0ustar georgeskgeorgesk""" 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)`` Expected 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, correlation, moment, cmoment, smoment, ) __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__) from . import drv_types from .drv_types import (Geometric, Poisson) __all__.extend(drv_types.__all__) sympy-0.7.4.1/sympy/geometry/0000755000175000017500000000000012253362407016260 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/geometry/exceptions.py0000644000175000017500000000026512253362407021016 0ustar georgeskgeorgesk"""Geometry Errors.""" from __future__ import print_function, division class GeometryError(ValueError): """An exception raised by classes in the geometry module.""" pass sympy-0.7.4.1/sympy/geometry/point.py0000644000175000017500000003571512253362407017776 0ustar georgeskgeorgesk"""Geometrical Points. Contains ======== Point """ from __future__ import print_function, division from sympy.core import S, sympify from sympy.core.compatibility import iterable from sympy.core.containers import Tuple from sympy.simplify import simplify, nsimplify from sympy.geometry.exceptions import GeometryError from sympy.functions.elementary.miscellaneous import sqrt from .entity import GeometryEntity from sympy.matrices import Matrix from sympy.core.numbers import Float 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 = [simplify(nsimplify(c, rational=True)) 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 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 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 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 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 """ # Coincident points are irrelevant and can confuse this algorithm. # Use only unique points. points = list(set(points)) 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 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 # """ 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)])) 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)]) 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, evaluate=False) n = evalf 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) 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 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) 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) 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]) 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) sympy-0.7.4.1/sympy/geometry/polygon.py0000644000175000017500000016672512253362407020342 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import Expr, S, sympify, oo, pi, Symbol, zoo from sympy.core.compatibility import as_int, xrange from sympy.functions.elementary.complexes import sign from sympy.functions.elementary.piecewise import Piecewise from sympy.functions.elementary.trigonometric import cos, sin, tan, sqrt, atan from sympy.geometry.exceptions import GeometryError from sympy.logic import And from sympy.matrices import Matrix from sympy.simplify import simplify from sympy.solvers import solve from sympy.utilities import default_sort_key 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 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]], key=default_sort_key) 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 = list(filter(lambda x: x is not None, nodup)) 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 if any side intersects with any preceding side 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 xrange(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: raise GeometryError( "Polygon has intersecting sides.") return rv @property 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 = 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 xrange(len(args)): x1, y1 = args[i - 1].args x2, y2 = args[i].args area += x1*y2 - x2*y1 return simplify(area) / 2 @staticmethod def _isright(a, b, c): ba = b - a ca = c - a t_area = simplify(ba.x*ca.y - ca.x*ba.y) res = t_area.is_nonpositive if res is None: raise ValueError("Can't determine orientation") return res @property 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 = 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) """ # Determine orientation of points args = self.vertices cw = self._isright(args[-1], args[0], args[1]) ret = {} for i in xrange(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 ^ self._isright(a, b, c): ret[b] = 2*S.Pi - ang else: ret[b] = ang return ret @property 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 = 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 xrange(len(args)): p += args[i - 1].distance(args[i]) return simplify(p) @property 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 = 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 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 = 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 xrange(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 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 = 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 xrange(-len(args), 0): res.append(Segment(args[i], args[i + 1])) return res 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 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)]) >>> poly = Polygon(p1, p2, p3, p4) >>> poly.is_convex() True """ # Determine orientation of points args = self.vertices cw = self._isright(args[-2], args[-1], args[0]) for i in xrange(1, len(args)): if cw ^ self._isright(args[i - 2], args[i - 1], args[i]): return False return True 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 = 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 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, (And(perim_fraction_start <= t, t < perim_fraction_end)))) perim_fraction_start = perim_fraction_end return Piecewise(*sides) 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] 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 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)]) >>> poly1 = Polygon(p1, p2, p3, p4) >>> p5, p6, p7 = 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 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 = 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) is True: 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) is True: 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 xrange(n): if args[i0] == o0: if all(args[(i0 + i) % n] == oargs[i] for i in xrange(1, n)): return True if all(args[(i0 - i) % n] == oargs[i] for i in xrange(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 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 = 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 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 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 sign(r)*n*self.length**2/(4*tan(pi/n)) @property 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 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 centroid = center @property 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 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 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 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 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 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 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 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 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 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 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 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) 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 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) 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) def reflect(self, line): """Override GeometryEntity.reflect since this is not made of only points. >>> from sympy import RegularPolygon, Line >>> RegularPolygon((0, 0), 1, 4).reflect(Line((0, 1), slope=-2)) RegularPolygon(Point(4/5, 2/5), -1, 4, acos(3/5)) """ c, r, n, rot = self.args cc = c.reflect(line) v = self.vertices[0] vv = v.reflect(line) # see how much it must get spun at the new center ang = Segment(cc, vv).angle_between(Segment(c, v)) rot = (rot + ang + pi) % (2*pi/n) return self.func(cc, -r, n, rot) @property 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 = abs(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 xrange(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__() 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(*[simplify(a) for a in kwargs['sss']]) if 'asa' in kwargs: return _asa(*[simplify(a) for a in kwargs['asa']]) if 'sas' in kwargs: return _sas(*[simplify(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]], key=default_sort_key) if Point.is_collinear(a, b, c): nodup[i] = a nodup[i + 1] = None nodup.pop(i + 1) i += 1 vertices = list(filter(lambda x: x is not None, nodup)) if len(vertices) == 3: return GeometryEntity.__new__(cls, *vertices, **kwargs) elif len(vertices) == 2: return Segment(*vertices, **kwargs) else: return Point(*vertices, **kwargs) @property 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 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) 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) 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) 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) 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 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 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 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 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 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) 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 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 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 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 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(v[0], s[1].midpoint), v[1]: Segment(v[1], s[2].midpoint), v[2]: Segment(v[2], s[0].midpoint)} @property 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) sympy-0.7.4.1/sympy/geometry/util.py0000644000175000017500000002415612253362407017617 0ustar georgeskgeorgesk"""Utility functions for geometrical entities. Contains ======== intersection convex_hull are_similar """ from __future__ import print_function, division from sympy import Symbol, Function, solve from sympy.core.compatibility import string_types 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, string_types): 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') 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 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) 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)) 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 sympy-0.7.4.1/sympy/geometry/ellipse.py0000644000175000017500000010642512253362407020277 0ustar georgeskgeorgesk"""Elliptical geometrical entities. Contains * Ellipse * Circle """ from __future__ import print_function, division from sympy.core import S, C, sympify, pi, Dummy from sympy.core.logic import fuzzy_bool from sympy.core.numbers import oo 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 from sympy.utilities.decorator import doctest_depends_on 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.plotting.pygletplot import PygletPlot as Plot >>> from sympy import Circle, 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()) >>> 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(list(filter(None, (hradius, vradius, eccentricity)))) != 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 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 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 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 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 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 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 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 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 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 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 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 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)) 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) 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) def reflect(self, line): """Override GeometryEntity.reflect since the radius is not a GeometryEntity. Examples ======== >>> from sympy import Circle, Line >>> Circle((0, 1), 1).reflect(Line((0, 0), (1, 1))) Circle(Point(1, 0), -1) """ if line.slope in (0, oo): c = self.center c = c.reflect(line) return self.func(c, -self.hradius, self.vradius) raise NotImplementedError('reflection line not horizontal | vertical.') 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) @doctest_depends_on(modules=('pyglet',)) 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.plotting.pygletplot import PygletPlot as Plot >>> from sympy import Point, Ellipse >>> e = Ellipse(Point(0,0), 3, 2) >>> t = e.tangent_lines(e.random_point()) >>> p = Plot() >>> 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])] 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") 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)) 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] 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 sin, cos, Rational 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? # simplify this now or else the Float will turn s into a Float c = 2*Rational(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.') 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] 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 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 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 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. It is always a positive value in order that the Circle, like Polygons, will have an area that can be positive or negative as determined by the sign of the hradius. Examples ======== >>> from sympy import Point, Circle >>> c1 = Circle(Point(3, 4), 6) >>> c1.vradius 6 """ return abs(self.radius) @property 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 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 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) 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) x, y = [abs(i) for i in (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) def reflect(self, line): """Override GeometryEntity.reflect since the radius is not a GeometryEntity. Examples ======== >>> from sympy import Circle, Line >>> Circle((0, 1), 1).reflect(Line((0, 0), (1, 1))) Circle(Point(1, 0), -1) """ c = self.center c = c.reflect(line) return self.func(c, -self.radius) from .polygon import Polygon sympy-0.7.4.1/sympy/geometry/line.py0000644000175000017500000012440412253362407017566 0ustar georgeskgeorgesk"""Line-like geometrical entities. Contains ======== LinearEntity Line Ray Segment """ from __future__ import print_function, division 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.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 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 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 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 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)]) 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 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 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 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))) 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) 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) 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 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 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 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) 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] 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) 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'. When this parameter is 0, the first point used to define the line will be returned, and when it is 1 the second point will be returned. 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) 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 = (-c - a*x) / b return Point(x, y) 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)) 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__() 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 a slope, enter it 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) def plot_interval(self, parameter='t'): """The plot interval for the default geometric plot of line. Gives values that will produce a line that is +/- 5 units long (where a unit is the distance between the two points that define the 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] 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) 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__() 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 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 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 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 def plot_interval(self, parameter='t'): """The plot interval for the default geometric plot of the Ray. Gives values that will produce a ray that is 10 units long (where a unit is the distance between the two points that define 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, 10] """ t = _symbol(parameter) return [t, 0, 10] 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__() 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 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) is True: p1, p2 = p2, p1 elif (p1.x == p2.x) is (p1.y > p2.y) is True: p1, p2 = p2, p1 return LinearEntity.__new__(cls, p1, p2, **kwargs) def plot_interval(self, parameter='t'): """The plot interval for the default geometric plot of the Segment. Gives values that will produce the full segment in a plot. 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] 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 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 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) 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__() 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 sympy-0.7.4.1/sympy/geometry/entity.py0000644000175000017500000002573012253362407020155 0ustar georgeskgeorgesk"""The definition of the base geometrical entity with attributes common to all derived geometrical entities. Contains ======== GeometryEntity """ from __future__ import print_function, division from sympy.core.compatibility import 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" ] 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 = map(sympify, args) return Basic.__new__(cls, *args) def _sympy_(self): return self def __getnewargs__(self): return tuple(self.args) 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() 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) 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 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 self.func(*newargs) def reflect(self, line): from sympy import atan, Line, Point, Dummy, oo g = self l = line o = Point(0, 0) if l.slope == 0: y = l.args[0].y if not y: # x-axis return g.scale(y=-1) reps = [(p, p.translate(y=2*(y - p.y))) for p in g.atoms(Point)] elif l.slope == oo: x = l.args[0].x if not x: # y-axis return g.scale(x=-1) reps = [(p, p.translate(x=2*(x - p.x))) for p in g.atoms(Point)] else: if not hasattr(g, 'reflect') and not all( isinstance(arg, Point) for arg in g.args): raise NotImplementedError( 'reflect undefined or non-Point args in %s' % g) a = atan(l.slope) c = l.coefficients d = -c[-1]/c[1] # y-intercept # apply the transform to a single point x, y = Dummy(), Dummy() xf = Point(x, y) xf = xf.translate(y=-d).rotate(-a, o).scale(y=-1 ).rotate(a, o).translate(y=d) # replace every point using that transform reps = [(p, xf.xreplace({x: p.x, y: p.y})) for p in g.atoms(Point)] return g.xreplace(dict(reps)) 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() 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 = (n1 > n2) - (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 (i1 > i2) - (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 sympy-0.7.4.1/sympy/geometry/curve.py0000644000175000017500000002027612253362407017765 0ustar georgeskgeorgesk"""Curves in 2-dimensional Euclidean space. Contains ======== Curve """ from __future__ import print_function, division 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 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 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 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 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 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] 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 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) 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) 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]) 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:]) sympy-0.7.4.1/sympy/geometry/tests/0000755000175000017500000000000012253362407017422 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/geometry/tests/test_geometry.py0000644000175000017500000012045612253362407022676 0ustar georgeskgeorgeskimport warnings from sympy import (Abs, C, Dummy, Rational, Float, S, Symbol, cos, oo, pi, simplify, sin, sqrt, symbols, tan) from sympy.geometry import (Circle, Curve, Ellipse, GeometryError, Line, Point, Polygon, Ray, RegularPolygon, Segment, Triangle, are_similar, convex_hull, intersection, centroid) from sympy.geometry.line import Undecidable from sympy.geometry.entity import rotate, scale, translate from sympy.geometry.polygon import _asa as asa, rad, deg from sympy.utilities.randtest import test_numerically from sympy.utilities.pytest import raises x = Symbol('x', real=True) y = Symbol('y', real=True) t = Symbol('t', real=True) x1 = Symbol('x1', real=True) x2 = Symbol('x2', real=True) y1 = Symbol('y1', real=True) y2 = Symbol('y2', real=True) half = Rational(1, 2) def feq(a, b): """Test if two floating point values are 'equal'.""" t = Float("1.0E-10") return -t < a - b < t def test_curve(): s = Symbol('s') z = Symbol('z') # this curve is independent of the indicated parameter c = Curve([2*s, s**2], (z, 0, 2)) assert c.parameter == z assert c.functions == (2*s, s**2) assert c.arbitrary_point() == Point(2*s, s**2) assert c.arbitrary_point(z) == Point(2*s, s**2) # this is how it is normally used c = Curve([2*s, s**2], (s, 0, 2)) assert c.parameter == s assert c.functions == (2*s, s**2) t = Symbol('t') # the t returned as assumptions assert c.arbitrary_point() != Point(2*t, t**2) t = Symbol('t', real=True) # now t has the same assumptions so the test passes assert c.arbitrary_point() == Point(2*t, t**2) assert c.arbitrary_point(z) == Point(2*z, z**2) assert c.arbitrary_point(c.parameter) == Point(2*s, s**2) assert c.arbitrary_point(None) == Point(2*s, s**2) assert c.plot_interval() == [t, 0, 2] assert c.plot_interval(z) == [z, 0, 2] assert Curve([x, x], (x, 0, 1)).rotate(pi/2, (1, 2)).scale(2, 3).translate( 1, 3).arbitrary_point(s) == \ Line((0, 0), (1, 1)).rotate(pi/2, (1, 2)).scale(2, 3).translate( 1, 3).arbitrary_point(s) == \ Point(-2*s + 7, 3*s + 6) raises(ValueError, lambda: Curve((s), (s, 1, 2))) raises(ValueError, lambda: Curve((x, x * 2), (1, x))) raises(ValueError, lambda: Curve((s, s + t), (s, 1, 2)).arbitrary_point()) raises(ValueError, lambda: Curve((s, s + t), (t, 1, 2)).arbitrary_point(s)) def test_point(): p1 = Point(x1, x2) p2 = Point(y1, y2) p3 = Point(0, 0) p4 = Point(1, 1) p5 = Point(0, 1) assert p1 in p1 assert p1 not in p2 assert p2.y == y2 assert (p3 + p4) == p4 assert (p2 - p1) == Point(y1 - x1, y2 - x2) assert p4*5 == Point(5, 5) assert -p2 == Point(-y1, -y2) assert Point.midpoint(p3, p4) == Point(half, half) assert Point.midpoint(p1, p4) == Point(half + half*x1, half + half*x2) assert Point.midpoint(p2, p2) == p2 assert p2.midpoint(p2) == p2 assert Point.distance(p3, p4) == sqrt(2) assert Point.distance(p1, p1) == 0 assert Point.distance(p3, p2) == sqrt(p2.x**2 + p2.y**2) p1_1 = Point(x1, x1) p1_2 = Point(y2, y2) p1_3 = Point(x1 + 1, x1) assert Point.is_collinear(p3) assert Point.is_collinear(p3, p4) assert Point.is_collinear(p3, p4, p1_1, p1_2) assert Point.is_collinear(p3, p4, p1_1, p1_3) is False assert Point.is_collinear(p3, p3, p4, p5) is False assert p3.intersection(Point(0, 0)) == [p3] assert p3.intersection(p4) == [] x_pos = Symbol('x', real=True, positive=True) p2_1 = Point(x_pos, 0) p2_2 = Point(0, x_pos) p2_3 = Point(-x_pos, 0) p2_4 = Point(0, -x_pos) p2_5 = Point(x_pos, 5) assert Point.is_concyclic(p2_1) assert Point.is_concyclic(p2_1, p2_2) assert Point.is_concyclic(p2_1, p2_2, p2_3, p2_4) assert Point.is_concyclic(p2_1, p2_2, p2_3, p2_5) is False assert Point.is_concyclic(p4, p4 * 2, p4 * 3) is False assert p4.scale(2, 3) == Point(2, 3) assert p3.scale(2, 3) == p3 assert p4.rotate(pi, Point(0.5, 0.5)) == p3 assert p1.__radd__(p2) == p1.midpoint(p2).scale(2, 2) assert (-p3).__rsub__(p4) == p3.midpoint(p4).scale(2, 2) assert p4 * 5 == Point(5, 5) assert p4 / 5 == Point(0.2, 0.2) raises(ValueError, lambda: Point(0, 0) + 10) # Point differences should be simplified assert Point(x*(x - 1), y) - Point(x**2 - x, y + 1) == Point(0, -1) a, b = Rational(1, 2), Rational(1, 3) assert Point(a, b).evalf(2) == \ Point(a.n(2), b.n(2)) raises(ValueError, lambda: Point(1, 2) + 1) # test transformations p = Point(1, 0) assert p.rotate(pi/2) == Point(0, 1) assert p.rotate(pi/2, p) == p p = Point(1, 1) assert p.scale(2, 3) == Point(2, 3) assert p.translate(1, 2) == Point(2, 3) assert p.translate(1) == Point(2, 1) assert p.translate(y=1) == Point(1, 2) assert p.translate(*p.args) == Point(2, 2) def test_line(): p1 = Point(0, 0) p2 = Point(1, 1) p3 = Point(x1, x1) p4 = Point(y1, y1) p5 = Point(x1, 1 + x1) p6 = Point(1, 0) p7 = Point(0, 1) p8 = Point(2, 0) p9 = Point(2, 1) l1 = Line(p1, p2) l2 = Line(p3, p4) l3 = Line(p3, p5) l4 = Line(p1, p6) l5 = Line(p1, p7) l6 = Line(p8, p9) l7 = Line(p2, p9) raises(ValueError, lambda: Line(Point(0, 0), Point(0, 0))) # Basic stuff assert Line((1, 1), slope=1) == Line((1, 1), (2, 2)) assert Line((1, 1), slope=oo) == Line((1, 1), (1, 2)) assert Line((1, 1), slope=-oo) == Line((1, 1), (1, 2)) raises(ValueError, lambda: Line((1, 1), 1)) assert Line(p1, p2) == Line(p2, p1) assert l1 == l2 assert l1 != l3 assert l1.slope == 1 assert l1.length == oo assert l3.slope == oo assert l4.slope == 0 assert l4.coefficients == (0, 1, 0) assert l4.equation(x=x, y=y) == y assert l5.slope == oo assert l5.coefficients == (1, 0, 0) assert l5.equation() == x assert l6.equation() == x - 2 assert l7.equation() == y - 1 assert p1 in l1 # is p1 on the line l1? assert p1 not in l3 assert Line((-x, x), (-x + 1, x - 1)).coefficients == (1, 1, 0) assert simplify(l1.equation()) in (x - y, y - x) assert simplify(l3.equation()) in (x - x1, x1 - x) assert Line(p1, p2).scale(2, 1) == Line(p1, p9) assert l2.arbitrary_point() in l2 for ind in range(0, 5): assert l3.random_point() in l3 # Orthogonality p1_1 = Point(-x1, x1) l1_1 = Line(p1, p1_1) assert l1.perpendicular_line(p1) == l1_1 assert Line.is_perpendicular(l1, l1_1) assert Line.is_perpendicular(l1, l2) is False p = l1.random_point() assert l1.perpendicular_segment(p) == p # Parallelity p2_1 = Point(-2*x1, 0) l2_1 = Line(p3, p5) assert l2.parallel_line(p1_1) == Line(p2_1, p1_1) assert l2_1.parallel_line(p1) == Line(p1, Point(0, 2)) assert Line.is_parallel(l1, l2) assert Line.is_parallel(l2, l3) is False assert Line.is_parallel(l2, l2.parallel_line(p1_1)) assert Line.is_parallel(l2_1, l2_1.parallel_line(p1)) # Intersection assert intersection(l1, p1) == [p1] assert intersection(l1, p5) == [] assert intersection(l1, l2) in [[l1], [l2]] assert intersection(l1, l1.parallel_line(p5)) == [] # Concurrency l3_1 = Line(Point(5, x1), Point(-Rational(3, 5), x1)) assert Line.is_concurrent(l1) is False assert Line.is_concurrent(l1, l3) assert Line.is_concurrent(l1, l3, l3_1) assert Line.is_concurrent(l1, l1_1, l3) is False # Projection assert l2.projection(p4) == p4 assert l1.projection(p1_1) == p1 assert l3.projection(p2) == Point(x1, 1) raises(GeometryError, lambda: Line(Point(0, 0), Point(1, 0)) .projection(Circle(Point(0, 0), 1))) # Finding angles l1_1 = Line(p1, Point(5, 0)) assert feq(Line.angle_between(l1, l1_1).evalf(), pi.evalf()/4) # Testing Rays and Segments (very similar to Lines) assert Ray((1, 1), angle=pi/4) == Ray((1, 1), (2, 2)) assert Ray((1, 1), angle=pi/2) == Ray((1, 1), (1, 2)) assert Ray((1, 1), angle=-pi/2) == Ray((1, 1), (1, 0)) assert Ray((1, 1), angle=-3*pi/2) == Ray((1, 1), (1, 2)) assert Ray((1, 1), angle=5*pi/2) == Ray((1, 1), (1, 2)) assert Ray((1, 1), angle=5.0*pi/2) == Ray((1, 1), (1, 2)) assert Ray((1, 1), angle=pi) == Ray((1, 1), (0, 1)) assert Ray((1, 1), angle=3.0*pi) == Ray((1, 1), (0, 1)) assert Ray((1, 1), angle=4.0*pi) == Ray((1, 1), (2, 1)) assert Ray((1, 1), angle=0) == Ray((1, 1), (2, 1)) assert Ray((1, 1), angle=4.05*pi) == Ray(Point(1, 1), Point(2, 1 + C.tan(4.05*pi))) assert Ray((1, 1), angle=5) == Ray((1, 1), (2, 1 + C.tan(5))) raises(ValueError, lambda: Ray((1, 1), 1)) r1 = Ray(p1, Point(-1, 5)) r2 = Ray(p1, Point(-1, 1)) r3 = Ray(p3, p5) r4 = Ray(p1, p2) r5 = Ray(p2, p1) r6 = Ray(Point(0, 1), Point(1, 2)) r7 = Ray(Point(0.5, 0.5), Point(1, 1)) assert l1.projection(r1) == Ray(p1, p2) assert l1.projection(r2) == p1 assert r3 != r1 t = Symbol('t', real=True) assert Ray((1, 1), angle=pi/4).arbitrary_point() == \ Point(t + 1, t + 1) s1 = Segment(p1, p2) s2 = Segment(p1, p1_1) assert s1.midpoint == Point(Rational(1, 2), Rational(1, 2)) assert s2.length == sqrt( 2*(x1**2) ) assert s1.perpendicular_bisector() == Line(Point(0, 1), Point(1, 0)) assert Segment((1, 1), (2, 3)).arbitrary_point() == Point(1 + t, 1 + 2*t) # intersections assert s1.intersection(Line(p6, p9)) == [] s3 = Segment(Point(0.25, 0.25), Point(0.5, 0.5)) assert s1.intersection(s3) == [s1] assert s3.intersection(s1) == [s3] assert r4.intersection(s3) == [s3] assert r4.intersection(Segment(Point(2, 3), Point(3, 4))) == [] assert r4.intersection(Segment(Point(-1, -1), Point(0.5, 0.5))) == \ [Segment(p1, Point(0.5, 0.5))] s3 = Segment(Point(1, 1), Point(2, 2)) assert s1.intersection(s3) == [Point(1, 1)] s3 = Segment(Point(0.5, 0.5), Point(1.5, 1.5)) assert s1.intersection(s3) == [Segment(Point(0.5, 0.5), p2)] assert s1.intersection(Segment(Point(4, 4), Point(5, 5))) == [] assert s1.intersection(Segment(Point(-1, -1), p1)) == [p1] assert s1.intersection(Segment(Point(-1, -1), Point(0.5, 0.5))) == \ [Segment(p1, Point(0.5, 0.5))] assert r4.intersection(r5) == [s1] assert r5.intersection(r6) == [] assert r4.intersection(r7) == r7.intersection(r4) == [r7] # Segment contains a, b = symbols('a,b') s = Segment((0, a), (0, b)) assert Point(0, (a + b)/2) in s s = Segment((a, 0), (b, 0)) assert Point((a + b)/2, 0) in s raises(Undecidable, lambda: Point(2*a, 0) in s) # Testing distance from a Segment to an object s1 = Segment(Point(0, 0), Point(1, 1)) s2 = Segment(Point(half, half), Point(1, 0)) pt1 = Point(0, 0) pt2 = Point(Rational(3)/2, Rational(3)/2) assert s1.distance(pt1) == 0 assert s2.distance(pt1) == 2**(half)/2 assert s2.distance(pt2) == 2**(half) # Special cases of projection and intersection r1 = Ray(Point(1, 1), Point(2, 2)) r2 = Ray(Point(2, 2), Point(0, 0)) r3 = Ray(Point(1, 1), Point(-1, -1)) r4 = Ray(Point(0, 4), Point(-1, -5)) r5 = Ray(Point(2, 2), Point(3, 3)) assert intersection(r1, r2) == [Segment(Point(1, 1), Point(2, 2))] assert intersection(r1, r3) == [Point(1, 1)] assert r1.projection(r3) == Point(1, 1) assert r1.projection(r4) == Segment(Point(1, 1), Point(2, 2)) r5 = Ray(Point(0, 0), Point(0, 1)) r6 = Ray(Point(0, 0), Point(0, 2)) assert r5 in r6 assert r6 in r5 s1 = Segment(Point(0, 0), Point(2, 2)) s2 = Segment(Point(-1, 5), Point(-5, -10)) s3 = Segment(Point(0, 4), Point(-2, 2)) assert intersection(r1, s1) == [Segment(Point(1, 1), Point(2, 2))] assert r1.projection(s2) == Segment(Point(1, 1), Point(2, 2)) assert s3.projection(r1) == Segment(Point(0, 4), Point(-1, 3)) l1 = Line(Point(0, 0), Point(3, 4)) r1 = Ray(Point(0, 0), Point(3, 4)) s1 = Segment(Point(0, 0), Point(3, 4)) assert intersection(l1, l1) == [l1] assert intersection(l1, r1) == [r1] assert intersection(l1, s1) == [s1] assert intersection(r1, l1) == [r1] assert intersection(s1, l1) == [s1] entity1 = Segment(Point(-10, 10), Point(10, 10)) entity2 = Segment(Point(-5, -5), Point(-5, 5)) assert intersection(entity1, entity2) == [] r1 = Ray(p1, Point(0, 1)) r2 = Ray(Point(0, 1), p1) r3 = Ray(p1, p2) r4 = Ray(p2, p1) s1 = Segment(p1, Point(0, 1)) assert Line(r1.source, r1.random_point()).slope == r1.slope assert Line(r2.source, r2.random_point()).slope == r2.slope assert Segment(Point(0, -1), s1.random_point()).slope == s1.slope p_r3 = r3.random_point() p_r4 = r4.random_point() assert p_r3.x >= p1.x and p_r3.y >= p1.y assert p_r4.x <= p2.x and p_r4.y <= p2.y p10 = Point(2000, 2000) s1 = Segment(p1, p10) p_s1 = s1.random_point() assert p1.x <= p_s1.x and p_s1.x <= p10.x and p1.y <= p_s1.y and p_s1.y <= p10.y s2 = Segment(p10, p1) assert hash(s1) == hash(s2) p11 = p10.scale(2, 2) assert s1.is_similar(Segment(p10, p11)) assert s1.is_similar(r1) is False assert (r1 in s1) is False assert Segment(p1, p2) in s1 assert s1.plot_interval() == [t, 0, 1] assert s1 in Line(p1, p10) assert Line(p1, p10) == Line(p10, p1) assert Line(p1, p10) != p1 assert Line(p1, p10).plot_interval() == [t, -5, 5] assert Ray((0, 0), angle=pi/4).plot_interval() == \ [t, 0, 10] def test_ellipse(): p1 = Point(0, 0) p2 = Point(1, 1) p4 = Point(0, 1) e1 = Ellipse(p1, 1, 1) e2 = Ellipse(p2, half, 1) e3 = Ellipse(p1, y1, y1) c1 = Circle(p1, 1) c2 = Circle(p2, 1) c3 = Circle(Point(sqrt(2), sqrt(2)), 1) # Test creation with three points cen, rad = Point(3*half, 2), 5*half assert Circle(Point(0, 0), Point(3, 0), Point(0, 4)) == Circle(cen, rad) raises( GeometryError, lambda: Circle(Point(0, 0), Point(1, 1), Point(2, 2))) raises(ValueError, lambda: Ellipse(None, None, None, 1)) raises(GeometryError, lambda: Circle(Point(0, 0))) # Basic Stuff assert Ellipse(None, 1, 1).center == Point(0, 0) assert e1 == c1 assert e1 != e2 assert p4 in e1 assert p2 not in e2 assert e1.area == pi assert e2.area == pi/2 assert e3.area == pi*y1*abs(y1) assert c1.area == e1.area assert c1.circumference == e1.circumference assert e3.circumference == 2*pi*y1 assert e1.plot_interval() == e2.plot_interval() == [t, -pi, pi] assert e1.plot_interval(x) == e2.plot_interval(x) == [x, -pi, pi] assert Ellipse(None, 1, None, 1).circumference == 2*pi assert c1.minor == 1 assert c1.major == 1 assert c1.hradius == 1 assert c1.vradius == 1 # Private Functions assert hash(c1) == hash(Circle(Point(1, 0), Point(0, 1), Point(0, -1))) assert c1 in e1 assert (Line(p1, p2) in e1) is False assert e1.__cmp__(e1) == 0 assert e1.__cmp__(Point(0, 0)) > 0 # Encloses assert e1.encloses(Segment(Point(-0.5, -0.5), Point(0.5, 0.5))) is True assert e1.encloses(Line(p1, p2)) is False assert e1.encloses(Ray(p1, p2)) is False assert e1.encloses(e1) is False assert e1.encloses( Polygon(Point(-0.5, -0.5), Point(-0.5, 0.5), Point(0.5, 0.5))) is True assert e1.encloses(RegularPolygon(p1, 0.5, 3)) is True assert e1.encloses(RegularPolygon(p1, 5, 3)) is False assert e1.encloses(RegularPolygon(p2, 5, 3)) is False # with generic symbols, the hradius is assumed to contain the major radius M = Symbol('M') m = Symbol('m') c = Ellipse(p1, M, m).circumference _x = c.atoms(Dummy).pop() assert c == 4*M*C.Integral( sqrt((1 - _x**2*(M**2 - m**2)/M**2)/(1 - _x**2)), (_x, 0, 1)) assert e2.arbitrary_point() in e2 # Foci f1, f2 = Point(sqrt(12), 0), Point(-sqrt(12), 0) ef = Ellipse(Point(0, 0), 4, 2) assert ef.foci in [(f1, f2), (f2, f1)] # Tangents v = sqrt(2) / 2 p1_1 = Point(v, v) p1_2 = p2 + Point(half, 0) p1_3 = p2 + Point(0, 1) assert e1.tangent_lines(p4) == c1.tangent_lines(p4) assert e2.tangent_lines(p1_2) == [Line(p1_2, p2 + Point(half, 1))] assert e2.tangent_lines(p1_3) == [Line(p1_3, p2 + Point(half, 1))] assert c1.tangent_lines(p1_1) == [Line(p1_1, Point(0, sqrt(2)))] assert c1.tangent_lines(p1) == [] assert e2.is_tangent(Line(p1_2, p2 + Point(half, 1))) assert e2.is_tangent(Line(p1_3, p2 + Point(half, 1))) assert c1.is_tangent(Line(p1_1, Point(0, sqrt(2)))) assert e1.is_tangent(Line(Point(0, 0), Point(1, 1))) is False assert c1.is_tangent(e1) is False assert c1.is_tangent(Ellipse(Point(2, 0), 1, 1)) is True assert c1.is_tangent( Polygon(Point(1, 1), Point(1, -1), Point(2, 0))) is True assert c1.is_tangent( Polygon(Point(1, 1), Point(1, 0), Point(2, 0))) is False assert Ellipse(Point(5, 5), 2, 1).tangent_lines(Point(0, 0)) == \ [Line(Point(0, 0), Point(S(77)/25, S(132)/25)), Line(Point(0, 0), Point(S(33)/5, S(22)/5))] assert Ellipse(Point(5, 5), 2, 1).tangent_lines(Point(3, 4)) == \ [Line(Point(3, 4), Point(3, 5)), Line(Point(3, 4), Point(5, 4))] assert Circle(Point(5, 5), 2).tangent_lines(Point(3, 3)) == \ [Line(Point(3, 3), Point(3, 5)), Line(Point(3, 3), Point(5, 3))] assert Circle(Point(5, 5), 2).tangent_lines(Point(5 - 2*sqrt(2), 5)) == \ [Line(Point(5 - 2*sqrt(2), 5), Point(5 - sqrt(2), 5 - sqrt(2))), Line(Point(5 - 2*sqrt(2), 5), Point(5 - sqrt(2), 5 + sqrt(2))), ] # Properties major = 3 minor = 1 e4 = Ellipse(p2, minor, major) assert e4.focus_distance == sqrt(major**2 - minor**2) ecc = e4.focus_distance / major assert e4.eccentricity == ecc assert e4.periapsis == major*(1 - ecc) assert e4.apoapsis == major*(1 + ecc) # independent of orientation e4 = Ellipse(p2, major, minor) assert e4.focus_distance == sqrt(major**2 - minor**2) ecc = e4.focus_distance / major assert e4.eccentricity == ecc assert e4.periapsis == major*(1 - ecc) assert e4.apoapsis == major*(1 + ecc) # Intersection l1 = Line(Point(1, -5), Point(1, 5)) l2 = Line(Point(-5, -1), Point(5, -1)) l3 = Line(Point(-1, -1), Point(1, 1)) l4 = Line(Point(-10, 0), Point(0, 10)) pts_c1_l3 = [Point(sqrt(2)/2, sqrt(2)/2), Point(-sqrt(2)/2, -sqrt(2)/2)] assert intersection(e2, l4) == [] assert intersection(c1, Point(1, 0)) == [Point(1, 0)] assert intersection(c1, l1) == [Point(1, 0)] assert intersection(c1, l2) == [Point(0, -1)] assert intersection(c1, l3) in [pts_c1_l3, [pts_c1_l3[1], pts_c1_l3[0]]] assert intersection(c1, c2) == [Point(0, 1), Point(1, 0)] assert intersection(c1, c3) == [Point(sqrt(2)/2, sqrt(2)/2)] assert e1.intersection(l1) == [Point(1, 0)] assert e2.intersection(l4) == [] assert e1.intersection(Circle(Point(0, 2), 1)) == [Point(0, 1)] assert e1.intersection(Circle(Point(5, 0), 1)) == [] assert e1.intersection(Ellipse(Point(2, 0), 1, 1)) == [Point(1, 0)] assert e1.intersection(Ellipse(Point(5, 0), 1, 1,)) == [] assert e1.intersection(Point(2, 0)) == [] assert e1.intersection(e1) == e1 # some special case intersections csmall = Circle(p1, 3) cbig = Circle(p1, 5) cout = Circle(Point(5, 5), 1) # one circle inside of another assert csmall.intersection(cbig) == [] # separate circles assert csmall.intersection(cout) == [] # coincident circles assert csmall.intersection(csmall) == csmall v = sqrt(2) t1 = Triangle(Point(0, v), Point(0, -v), Point(v, 0)) points = intersection(t1, c1) assert len(points) == 4 assert Point(0, 1) in points assert Point(0, -1) in points assert Point(v/2, v/2) in points assert Point(v/2, -v/2) in points circ = Circle(Point(0, 0), 5) elip = Ellipse(Point(0, 0), 5, 20) assert intersection(circ, elip) in \ [[Point(5, 0), Point(-5, 0)], [Point(-5, 0), Point(5, 0)]] assert elip.tangent_lines(Point(0, 0)) == [] elip = Ellipse(Point(0, 0), 3, 2) assert elip.tangent_lines(Point(3, 0)) == \ [Line(Point(3, 0), Point(3, -12))] e1 = Ellipse(Point(0, 0), 5, 10) e2 = Ellipse(Point(2, 1), 4, 8) a = S(53)/17 c = 2*sqrt(3991)/17 ans = [Point(a - c/8, a/2 + c), Point(a + c/8, a/2 - c)] assert e1.intersection(e2) == ans e2 = Ellipse(Point(x, y), 4, 8) c = sqrt(3991) ans = [Point(c/68 + a, -2*c/17 + a/2), Point(-c/68 + a, 2*c/17 + a/2)] assert [p.subs({x: 2, y:1}) for p in e1.intersection(e2)] == ans # Combinations of above assert e3.is_tangent(e3.tangent_lines(p1 + Point(y1, 0))[0]) e = Ellipse((1, 2), 3, 2) assert e.tangent_lines(Point(10, 0)) == \ [Line(Point(10, 0), Point(1, 0)), Line(Point(10, 0), Point(S(14)/5, S(18)/5))] # encloses_point e = Ellipse((0, 0), 1, 2) assert e.encloses_point(e.center) assert e.encloses_point(e.center + Point(0, e.vradius - Rational(1, 10))) assert e.encloses_point(e.center + Point(e.hradius - Rational(1, 10), 0)) assert e.encloses_point(e.center + Point(e.hradius, 0)) is False assert e.encloses_point( e.center + Point(e.hradius + Rational(1, 10), 0)) is False e = Ellipse((0, 0), 2, 1) assert e.encloses_point(e.center) assert e.encloses_point(e.center + Point(0, e.vradius - Rational(1, 10))) assert e.encloses_point(e.center + Point(e.hradius - Rational(1, 10), 0)) assert e.encloses_point(e.center + Point(e.hradius, 0)) is False assert e.encloses_point( e.center + Point(e.hradius + Rational(1, 10), 0)) is False assert c1.encloses_point(Point(1, 0)) is False assert c1.encloses_point(Point(0.3, 0.4)) is True assert e.scale(2, 3) == Ellipse((0, 0), 4, 3) assert e.scale(3, 6) == Ellipse((0, 0), 6, 6) assert e.rotate(pi/3) == e assert e.rotate(pi/3, (1, 2)) == \ Ellipse(Point(S(1)/2 + sqrt(3), -sqrt(3)/2 + 1), 2, 1) # transformations c = Circle((1, 1), 2) assert c.scale(-1) == Circle((-1, 1), 2) assert c.scale(y=-1) == Circle((1, -1), 2) assert c.scale(2) == Ellipse((2, 1), 4, 2) def test_ellipse_random_point(): e3 = Ellipse(Point(0, 0), y1, y1) rx, ry = Symbol('rx'), Symbol('ry') for ind in range(0, 5): r = e3.random_point() # substitution should give zero*y1**2 assert e3.equation(rx, ry).subs(zip((rx, ry), r.args)).equals(0) def test_polygon(): t = Triangle(Point(0, 0), Point(2, 0), Point(3, 3)) assert Polygon(Point(0, 0), Point(1, 0), Point(2, 0), Point(3, 3)) == t assert Polygon(Point(1, 0), Point(2, 0), Point(3, 3), Point(0, 0)) == t assert Polygon(Point(2, 0), Point(3, 3), Point(0, 0), Point(1, 0)) == t p1 = Polygon( Point(0, 0), Point(3, -1), Point(6, 0), Point(4, 5), Point(2, 3), Point(0, 3)) p2 = Polygon( Point(6, 0), Point(3, -1), Point(0, 0), Point(0, 3), Point(2, 3), Point(4, 5)) p3 = Polygon( Point(0, 0), Point(3, 0), Point(5, 2), Point(4, 4)) p4 = Polygon( Point(0, 0), Point(4, 4), Point(5, 2), Point(3, 0)) p5 = Polygon( Point(0, 0), Point(4, 4), Point(0, 4)) # # General polygon # assert p1 == p2 assert len(p1.args) == 6 assert len(p1.sides) == 6 assert p1.perimeter == 5 + 2*sqrt(10) + sqrt(29) + sqrt(8) assert p1.area == 22 assert not p1.is_convex() # ensure convex for both CW and CCW point specification assert p3.is_convex() assert p4.is_convex() dict5 = p5.angles assert dict5[Point(0, 0)] == pi / 4 assert dict5[Point(0, 4)] == pi / 2 assert p5.encloses_point(Point(x, y)) is None assert p5.encloses_point(Point(1, 3)) assert p5.encloses_point(Point(0, 0)) is False assert p5.encloses_point(Point(4, 0)) is False p5.plot_interval('x') == [x, 0, 1] assert p5.distance( Polygon(Point(10, 10), Point(14, 14), Point(10, 14))) == 6 * sqrt(2) assert p5.distance( Polygon(Point(1, 8), Point(5, 8), Point(8, 12), Point(1, 12))) == 4 warnings.filterwarnings( "error", message="Polygons may intersect producing erroneous output") raises(UserWarning, lambda: Polygon(Point(0, 0), Point(1, 0), Point(1, 1)).distance(Polygon(Point(0, 0), Point(0, 1), Point(1, 1)))) warnings.filterwarnings( "ignore", message="Polygons may intersect producing erroneous output") assert hash(p5) == hash(Polygon(Point(0, 0), Point(4, 4), Point(0, 4))) assert p5 == Polygon(Point(4, 4), Point(0, 4), Point(0, 0)) assert Polygon(Point(4, 4), Point(0, 4), Point(0, 0)) in p5 assert p5 != Point(0, 4) assert Point(0, 1) in p5 assert p5.arbitrary_point('t').subs(Symbol('t', real=True), 0) == \ Point(0, 0) raises(ValueError, lambda: Polygon( Point(x, 0), Point(0, y), Point(x, y)).arbitrary_point('x')) # # Regular polygon # p1 = RegularPolygon(Point(0, 0), 10, 5) p2 = RegularPolygon(Point(0, 0), 5, 5) raises(GeometryError, lambda: RegularPolygon(Point(0, 0), Point(0, 1), Point(1, 1))) raises(GeometryError, lambda: RegularPolygon(Point(0, 0), 1, 2)) raises(ValueError, lambda: RegularPolygon(Point(0, 0), 1, 2.5)) assert p1 != p2 assert p1.interior_angle == 3*pi/5 assert p1.exterior_angle == 2*pi/5 assert p2.apothem == 5*cos(pi/5) assert p2.circumcenter == p1.circumcenter == Point(0, 0) assert p1.circumradius == p1.radius == 10 assert p2.circumcircle == Circle(Point(0, 0), 5) assert p2.incircle == Circle(Point(0, 0), p2.apothem) assert p2.inradius == p2.apothem == (5 * (1 + sqrt(5)) / 4) p2.spin(pi / 10) dict1 = p2.angles assert dict1[Point(0, 5)] == 3 * pi / 5 assert p1.is_convex() assert p1.rotation == 0 assert p1.encloses_point(Point(0, 0)) assert p1.encloses_point(Point(11, 0)) is False assert p2.encloses_point(Point(0, 4.9)) p1.spin(pi/3) assert p1.rotation == pi/3 assert p1.vertices[0] == Point(5, 5*sqrt(3)) for var in p1.args: if isinstance(var, Point): assert var == Point(0, 0) else: assert var == 5 or var == 10 or var == pi / 3 assert p1 != Point(0, 0) assert p1 != p5 # while spin works in place (notice that rotation is 2pi/3 below) # rotate returns a new object p1_old = p1 assert p1.rotate(pi/3) == RegularPolygon(Point(0, 0), 10, 5, 2*pi/3) assert p1 == p1_old assert p1.area == (-250*sqrt(5) + 1250)/(4*tan(pi/5)) assert p1.length == 20*sqrt(-sqrt(5)/8 + S(5)/8) assert p1.scale(2, 2) == \ RegularPolygon(p1.center, p1.radius*2, p1._n, p1.rotation) assert RegularPolygon((0, 0), 1, 4).scale(2, 3) == \ Polygon(Point(2, 0), Point(0, 3), Point(-2, 0), Point(0, -3)) assert repr(p1) == str(p1) # # Angles # angles = p4.angles assert feq(angles[Point(0, 0)].evalf(), Float("0.7853981633974483")) assert feq(angles[Point(4, 4)].evalf(), Float("1.2490457723982544")) assert feq(angles[Point(5, 2)].evalf(), Float("1.8925468811915388")) assert feq(angles[Point(3, 0)].evalf(), Float("2.3561944901923449")) angles = p3.angles assert feq(angles[Point(0, 0)].evalf(), Float("0.7853981633974483")) assert feq(angles[Point(4, 4)].evalf(), Float("1.2490457723982544")) assert feq(angles[Point(5, 2)].evalf(), Float("1.8925468811915388")) assert feq(angles[Point(3, 0)].evalf(), Float("2.3561944901923449")) # # Triangle # p1 = Point(0, 0) p2 = Point(5, 0) p3 = Point(0, 5) t1 = Triangle(p1, p2, p3) t2 = Triangle(p1, p2, Point(Rational(5, 2), sqrt(Rational(75, 4)))) t3 = Triangle(p1, Point(x1, 0), Point(0, x1)) s1 = t1.sides assert Triangle(p1, p2, p1) == Polygon(p1, p2, p1) == Segment(p1, p2) raises(GeometryError, lambda: Triangle(Point(0, 0))) # Basic stuff assert Triangle(p1, p1, p1) == p1 assert Triangle(p2, p2*2, p2*3) == Segment(p2, p2*3) assert t1.area == Rational(25, 2) assert t1.is_right() assert t2.is_right() is False assert t3.is_right() assert p1 in t1 assert t1.sides[0] in t1 assert Segment((0, 0), (1, 0)) in t1 assert Point(5, 5) not in t2 assert t1.is_convex() assert feq(t1.angles[p1].evalf(), pi.evalf()/2) assert t1.is_equilateral() is False assert t2.is_equilateral() assert t3.is_equilateral() is False assert are_similar(t1, t2) is False assert are_similar(t1, t3) assert are_similar(t2, t3) is False assert t1.is_similar(Point(0, 0)) is False # Bisectors bisectors = t1.bisectors() assert bisectors[p1] == Segment(p1, Point(Rational(5, 2), Rational(5, 2))) ic = (250 - 125*sqrt(2)) / 50 assert t1.incenter == Point(ic, ic) # Inradius assert t1.inradius == t1.incircle.radius == 5 - 5*sqrt(2)/2 assert t2.inradius == t2.incircle.radius == 5*sqrt(3)/6 assert t3.inradius == t3.incircle.radius == x1**2/((2 + sqrt(2))*Abs(x1)) # Circumcircle assert t1.circumcircle.center == Point(2.5, 2.5) # Medians + Centroid m = t1.medians assert t1.centroid == Point(Rational(5, 3), Rational(5, 3)) assert m[p1] == Segment(p1, Point(Rational(5, 2), Rational(5, 2))) assert t3.medians[p1] == Segment(p1, Point(x1/2, x1/2)) assert intersection(m[p1], m[p2], m[p3]) == [t1.centroid] assert t1.medial == Triangle(Point(2.5, 0), Point(0, 2.5), Point(2.5, 2.5)) # Perpendicular altitudes = t1.altitudes assert altitudes[p1] == Segment(p1, Point(Rational(5, 2), Rational(5, 2))) assert altitudes[p2] == s1[0] assert altitudes[p3] == s1[2] assert t1.orthocenter == p1 t = S('''Triangle( Point(100080156402737/5000000000000, 79782624633431/500000000000), Point(39223884078253/2000000000000, 156345163124289/1000000000000), Point(31241359188437/1250000000000, 338338270939941/1000000000000000))''') assert t.orthocenter == S('''Point(-780660869050599840216997''' '''79471538701955848721853/80368430960602242240789074233100000000000000,''' '''20151573611150265741278060334545897615974257/16073686192120448448157''' '''8148466200000000000)''') # Ensure assert len(intersection(*bisectors.values())) == 1 assert len(intersection(*altitudes.values())) == 1 assert len(intersection(*m.values())) == 1 # Distance p1 = Polygon( Point(0, 0), Point(1, 0), Point(1, 1), Point(0, 1)) p2 = Polygon( Point(0, Rational(5)/4), Point(1, Rational(5)/4), Point(1, Rational(9)/4), Point(0, Rational(9)/4)) p3 = Polygon( Point(1, 2), Point(2, 2), Point(2, 1)) p4 = Polygon( Point(1, 1), Point(Rational(6)/5, 1), Point(1, Rational(6)/5)) pt1 = Point(half, half) pt2 = Point(1, 1) '''Polygon to Point''' assert p1.distance(pt1) == half assert p1.distance(pt2) == 0 assert p2.distance(pt1) == Rational(3)/4 assert p3.distance(pt2) == sqrt(2)/2 '''Polygon to Polygon''' # p1.distance(p2) emits a warning # First, test the warning warnings.filterwarnings("error", message="Polygons may intersect producing erroneous output") raises(UserWarning, lambda: p1.distance(p2)) # now test the actual output warnings.filterwarnings("ignore", message="Polygons may intersect producing erroneous output") assert p1.distance(p2) == half/2 assert p1.distance(p3) == sqrt(2)/2 assert p3.distance(p4) == (sqrt(2)/2 - sqrt(Rational(2)/25)/2) def test_convex_hull(): p = [Point(-5, -1), Point(-2, 1), Point(-2, -1), Point(-1, -3), Point(0, 0), Point(1, 1), Point(2, 2), Point(2, -1), Point(3, 1), Point(4, -1), Point(6, 2)] ch = Polygon(p[0], p[3], p[9], p[10], p[6], p[1]) #test handling of duplicate points p.append(p[3]) #more than 3 collinear points another_p = [Point(-45, -85), Point(-45, 85), Point(-45, 26), Point(-45, -24)] ch2 = Segment(another_p[0], another_p[1]) assert convex_hull(*another_p) == ch2 assert convex_hull(*p) == ch assert convex_hull(p[0]) == p[0] assert convex_hull(p[0], p[1]) == Segment(p[0], p[1]) # no unique points assert convex_hull(*[p[-1]]*3) == p[-1] # collection of items assert convex_hull(*[Point(0, 0), Segment(Point(1, 0), Point(1, 1)), RegularPolygon(Point(2, 0), 2, 4)]) == \ Polygon(Point(0, 0), Point(2, -2), Point(4, 0), Point(2, 2)) def test_concyclic_doctest_bug(): p1, p2 = Point(-1, 0), Point(1, 0) p3, p4 = Point(0, 1), Point(-1, 2) assert Point.is_concyclic(p1, p2, p3) assert not Point.is_concyclic(p1, p2, p3, p4) def test_subs(): p = Point(x, 2) q = Point(1, 1) r = Point(3, 4) for o in [p, Segment(p, q), Ray(p, q), Line(p, q), Triangle(p, q, r), RegularPolygon(p, 3, 6), Polygon(p, q, r, Point(5, 4)), Circle(p, 3), Ellipse(p, 3, 4)]: assert 'y' in str(o.subs(x, y)) assert p.subs({x: 1}) == Point(1, 2) assert Point(1, 2).subs(Point(1, 2), Point(3, 4)) == Point(3, 4) assert Point(1, 2).subs((1, 2), Point(3, 4)) == Point(3, 4) assert Point(1, 2).subs(Point(1, 2), Point(3, 4)) == Point(3, 4) assert Point(1, 2).subs(set([(1, 2)])) == Point(2, 2) raises(ValueError, lambda: Point(1, 2).subs(1)) raises(ValueError, lambda: Point(1, 1).subs((Point(1, 1), Point(1, 2)), 1, 2)) def test_encloses(): # square with a dimpled left side s = Polygon(Point(0, 0), Point(1, 0), Point(1, 1), Point(0, 1), Point(S.Half, S.Half)) # the following will be True if the polygon isn't treated as closing on itself assert s.encloses(Point(0, S.Half)) is False assert s.encloses(Point(S.Half, S.Half)) is False # it's a vertex assert s.encloses(Point(Rational(3, 4), S.Half)) is True def test_free_symbols(): a, b, c, d, e, f, s = symbols('a:f,s') assert Point(a, b).free_symbols == set([a, b]) assert Line((a, b), (c, d)).free_symbols == set([a, b, c, d]) assert Ray((a, b), (c, d)).free_symbols == set([a, b, c, d]) assert Ray((a, b), angle=c).free_symbols == set([a, b, c]) assert Segment((a, b), (c, d)).free_symbols == set([a, b, c, d]) assert Line((a, b), slope=c).free_symbols == set([a, b, c]) assert Curve((a*s, b*s), (s, c, d)).free_symbols == set([a, b, c, d]) assert Ellipse((a, b), c, d).free_symbols == set([a, b, c, d]) assert Ellipse((a, b), c, eccentricity=d).free_symbols == \ set([a, b, c, d]) assert Ellipse((a, b), vradius=c, eccentricity=d).free_symbols == \ set([a, b, c, d]) assert Circle((a, b), c).free_symbols == set([a, b, c]) assert Circle((a, b), (c, d), (e, f)).free_symbols == \ set([e, d, c, b, f, a]) assert Polygon((a, b), (c, d), (e, f)).free_symbols == \ set([e, b, d, f, a, c]) assert RegularPolygon((a, b), c, d, e).free_symbols == set([e, a, b, c, d]) def test_util_centroid(): p = Polygon((0, 0), (10, 0), (10, 10)) q = p.translate(0, 20) assert centroid(p, q) == Point(20, 40)/3 p = Segment((0, 0), (2, 0)) q = Segment((0, 0), (2, 2)) assert centroid(p, q) == Point(1, 2*sqrt(2)/(2 + 2*sqrt(2))) assert centroid(Point(0, 0), Point(2, 0)) == Point(2, 0)/2 assert centroid(Point(0, 0), Point(0, 0), Point(2, 0)) == Point(2, 0)/3 def test_util(): # coverage for some leftover functions in sympy.geometry.util assert intersection(Point(0, 0)) == [] raises(ValueError, lambda: intersection(Point(0, 0), 3)) raises(ValueError, lambda: convex_hull(Point(0, 0), 3)) def test_repr(): assert repr(Circle((0, 1), 2)) == 'Circle(Point(0, 1), 2)' def test_transform(): p = Point(1, 1) assert p.transform(rotate(pi/2)) == Point(-1, 1) assert p.transform(scale(3, 2)) == Point(3, 2) assert p.transform(translate(1, 2)) == Point(2, 3) def test_line_intersection(): assert asa(120, 8, 52) == \ Triangle( Point(0, 0), Point(8, 0), Point(-4*cos(19*pi/90)/sin(2*pi/45), 4*sqrt(3)*cos(19*pi/90)/sin(2*pi/45))) assert Line((0, 0), (1, 1)).intersection(Ray((1, 0), (1, 2))) == \ [Point(1, 1)] assert Line((0, 0), (1, 1)).intersection(Segment((1, 0), (1, 2))) == \ [Point(1, 1)] assert Ray((0, 0), (1, 1)).intersection(Ray((1, 0), (1, 2))) == \ [Point(1, 1)] assert Ray((0, 0), (1, 1)).intersection(Segment((1, 0), (1, 2))) == \ [Point(1, 1)] assert Ray((0, 0), (10, 10)).contains(Segment((1, 1), (2, 2))) is True assert Segment((1, 1), (2, 2)) in Line((0, 0), (10, 10)) x = 8*tan(13*pi/45)/(tan(13*pi/45) + sqrt(3)) y = (-8*sqrt(3)*tan(13*pi/45)**2 + 24*tan(13*pi/45))/ \ (-3 + tan(13*pi/45)**2) assert Line(Point(0, 0), Point(1, -sqrt(3))).contains(Point(x, y)) is True def test_triangle_kwargs(): assert Triangle(sss=(3, 4, 5)) == \ Triangle(Point(0, 0), Point(3, 0), Point(3, 4)) assert Triangle(asa=(30, 2, 30)) == \ Triangle(Point(0, 0), Point(2, 0), Point(1, sqrt(3)/3)) assert Triangle(sas=(1, 45, 2)) == \ Triangle(Point(0, 0), Point(2, 0), Point(sqrt(2)/2, sqrt(2)/2)) assert Triangle(sss=(1, 2, 5)) is None assert deg(rad(180)) == 180 def test_geometry_transforms(): from sympy import Tuple c = Curve((x, x**2), (x, 0, 1)) pts = [Point(0, 0), Point(S(1)/2, S(1)/4), Point(1, 1)] cout = Curve((2*x - 4, 3*x**2 - 10), (x, 0, 1)) pts_out = [Point(-4, -10), Point(-3, -S(37)/4), Point(-2, -7)] assert c.scale(2, 3, (4, 5)) == cout assert [c.subs(x, xi/2) for xi in Tuple(0, 1, 2)] == pts assert [cout.subs(x, xi/2) for xi in Tuple(0, 1, 2)] == pts_out assert Triangle(*pts).scale(2, 3, (4, 5)) == Triangle(*pts_out) assert Ellipse((0, 0), 2, 3).scale(2, 3, (4, 5)) == \ Ellipse(Point(-4, -10), 4, 9) assert Circle((0, 0), 2).scale(2, 3, (4, 5)) == \ Ellipse(Point(-4, -10), 4, 6) assert Ellipse((0, 0), 2, 3).scale(3, 3, (4, 5)) == \ Ellipse(Point(-8, -10), 6, 9) assert Circle((0, 0), 2).scale(3, 3, (4, 5)) == \ Circle(Point(-8, -10), 6) assert Circle(Point(-8, -10), 6).scale(S(1)/3, S(1)/3, (4, 5)) == \ Circle((0, 0), 2) assert Curve((x + y, 3*x), (x, 0, 1)).subs(y, S.Half) == \ Curve((x + S(1)/2, 3*x), (x, 0, 1)) assert Curve((x, 3*x), (x, 0, 1)).translate(4, 5) == \ Curve((x + 4, 3*x + 5), (x, 0, 1)) assert Circle((0, 0), 2).translate(4, 5) == \ Circle((4, 5), 2) assert Circle((0, 0), 2).scale(3, 3) == \ Circle((0, 0), 6) assert Point(1, 1).scale(2, 3, (4, 5)) == \ Point(-2, -7) assert Point(1, 1).translate(4, 5) == \ Point(5, 6) assert scale(1, 2, (3, 4)).tolist() == \ [[1, 0, 0], [0, 2, 0], [0, -4, 1]] assert RegularPolygon((0, 0), 1, 4).scale(2, 3, (4, 5)) == \ Polygon(Point(-2, -10), Point(-4, -7), Point(-6, -10), Point(-4, -13)) def test_reflect(): b = Symbol('b') m = Symbol('m') l = Line((0, b), slope=m) p = Point(x, y) r = p.reflect(l) dp = l.perpendicular_segment(p).length dr = l.perpendicular_segment(r).length assert test_numerically(dp, dr) t = Triangle((0, 0), (1, 0), (2, 3)) assert t.area == -t.reflect(l).area e = Ellipse((1, 0), 1, 2) assert e.area == -e.reflect(Line((1, 0), slope=0)).area assert e.area == -e.reflect(Line((1, 0), slope=oo)).area raises(NotImplementedError, lambda: e.reflect(Line((1,0), slope=m))) assert Polygon((1, 0), (2, 0), (2, 2)).reflect(Line((3, 0), slope=oo)) \ == Triangle(Point(5, 0), Point(4, 0), Point(4, 2)) assert Polygon((1, 0), (2, 0), (2, 2)).reflect(Line((0, 3), slope=oo)) \ == Triangle(Point(-1, 0), Point(-2, 0), Point(-2, 2)) assert Polygon((1, 0), (2, 0), (2, 2)).reflect(Line((0, 3), slope=0)) \ == Triangle(Point(1, 6), Point(2, 6), Point(2, 4)) assert Polygon((1, 0), (2, 0), (2, 2)).reflect(Line((3, 0), slope=0)) \ == Triangle(Point(1, 0), Point(2, 0), Point(2, -2)) # test entity overrides c = Circle((x, y), 3) cr = c.reflect(l) assert cr == Circle(r, -3) assert c.area == -cr.area pent = RegularPolygon((1, 2), 1, 5) l = Line((0, pi), slope=sqrt(2)) rpent = pent.reflect(l) poly_pent = Polygon(*pent.vertices) assert rpent.center == pent.center.reflect(l) assert str([w.n(3) for w in rpent.vertices]) == ( '[Point(-0.586, 4.27), Point(-1.69, 4.66), ' 'Point(-2.41, 3.73), Point(-1.74, 2.76), ' 'Point(-0.616, 3.10)]') assert pent.area.equals(-rpent.area) sympy-0.7.4.1/sympy/geometry/tests/__init__.py0000644000175000017500000000000012253362407021521 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/geometry/__init__.py0000644000175000017500000000134212253362407020371 0ustar georgeskgeorgesk""" A geometry module for the SymPy library. This module contains all of the entities and functions needed to construct basic geometrical data and to perform simple informational queries. Usage: ====== Notes: ====== Currently the geometry module is restricted to the 2-dimensional Euclidean space. Examples ======== """ from sympy.geometry.point import Point from sympy.geometry.line import Line, Ray, Segment from sympy.geometry.ellipse import Ellipse, Circle from sympy.geometry.polygon import Polygon, RegularPolygon, Triangle, rad, deg from sympy.geometry.util import are_similar, centroid, convex_hull, idiff, \ intersection from sympy.geometry.exceptions import GeometryError from sympy.geometry.curve import Curve sympy-0.7.4.1/sympy/interactive/0000755000175000017500000000000012253362407016742 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/interactive/session.py0000644000175000017500000003714512253362407021011 0ustar georgeskgeorgesk"""Tools for setting up interactive sessions. """ from __future__ import print_function, division from sympy.external import import_module from sympy.interactive.printing import init_printing preexec_source = """\ from __future__ import division 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) """ verbose_message = """\ These commands were executed: %(source)s Documentation can be found at http://www.sympy.org """ no_ipython = """\ Couldn't locate IPython. Having IPython installed is greatly recommended. See http://ipython.scipy.org for more details. If you use Debian/Ubuntu, just install the 'ipython' package and start isympy again. """ def _make_message(ipython=True, quiet=False, source=None): """Create a banner for an interactive session. """ from sympy import __version__ as sympy_version from sympy.polys.domains import GROUND_TYPES from sympy.utilities.misc import ARCH from sympy import SYMPY_DEBUG import sys import os python_version = "%d.%d.%d" % sys.version_info[:3] if ipython: shell_name = "IPython" else: shell_name = "Python" info = ['ground types: %s' % GROUND_TYPES] cache = os.getenv('SYMPY_USE_CACHE') if cache is not None and cache.lower() == 'no': info.append('cache: off') if SYMPY_DEBUG: info.append('debugging: on') args = shell_name, sympy_version, python_version, ARCH, ', '.join(info) message = "%s console for SymPy %s (Python %s-%s) (%s)\n" % args if not quiet: if source is None: source = preexec_source _source = "" for line in source.split('\n')[:-1]: if not line: _source += '\n' else: _source += '>>> ' + line + '\n' message += '\n' + verbose_message % {'source': _source} return message def int_to_Integer(s): """ Wrap integer literals with Integer. This is based on the decistmt example from http://docs.python.org/library/tokenize.html. Only integer literals are converted. Float literals are left alone. Example ======= >>> from __future__ import division >>> from sympy.interactive.session import int_to_Integer >>> from sympy import Integer >>> s = '1.2 + 1/2 - 0x12 + a1' >>> int_to_Integer(s) '1.2 +Integer (1 )/Integer (2 )-Integer (0x12 )+a1 ' >>> s = 'print (1/2)' >>> int_to_Integer(s) 'print (Integer (1 )/Integer (2 ))' >>> exec(s) 0.5 >>> exec(int_to_Integer(s)) 1/2 """ from tokenize import generate_tokens, untokenize, NUMBER, NAME, OP from sympy.core.compatibility import StringIO def _is_int(num): """ Returns true if string value num (with token NUMBER) represents an integer. """ # XXX: Is there something in the standard library that will do this? if '.' in num or 'j' in num.lower() or 'e' in num.lower(): return False return True result = [] g = generate_tokens(StringIO(s).readline) # tokenize the string for toknum, tokval, _, _, _ in g: if toknum == NUMBER and _is_int(tokval): # replace NUMBER tokens result.extend([ (NAME, 'Integer'), (OP, '('), (NUMBER, tokval), (OP, ')') ]) else: result.append((toknum, tokval)) return untokenize(result) # XXX: Something like this might be used, but it only works on single line # inputs. See # http://mail.scipy.org/pipermail/ipython-user/2012-August/010846.html and # https://github.com/ipython/ipython/issues/1491. So instead we are forced to # just monkey-patch run_cell until IPython builds a better API. # # class IntTransformer(object): # """ # IPython command line transformer that recognizes and replaces int # literals. # # Based on # https://bitbucket.org/birkenfeld/ipython-physics/src/71b2d850da00/physics.py. # # """ # priority = 99 # enabled = True # def transform(self, line, continue_prompt): # import re # from tokenize import TokenError # leading_space = re.compile(' *') # spaces = re.match(leading_space, line).span()[1] # try: # return ' '*spaces + int_to_Integer(line) # except TokenError: # return line # # int_transformer = IntTransformer() # # def enable_automatic_int_sympification(app): # """ # Allow IPython to automatically convert integer literals to Integer. # # This lets things like 1/2 be executed as (essentially) Rational(1, 2). # """ # app.shell.prefilter_manager.register_transformer(int_transformer) def enable_automatic_int_sympification(app): """ Allow IPython to automatically convert integer literals to Integer. """ hasshell = hasattr(app, 'shell') import ast if hasshell: old_run_cell = app.shell.run_cell else: old_run_cell = app.run_cell def my_run_cell(cell, *args, **kwargs): try: # Check the cell for syntax errors. This way, the syntax error # will show the original input, not the transformed input. The # downside here is that IPython magic like %timeit will not work # with transformed input (but on the other hand, IPython magic # that doesn't expect transformed input will continue to work). ast.parse(cell) except SyntaxError: pass else: cell = int_to_Integer(cell) old_run_cell(cell, *args, **kwargs) if hasshell: app.shell.run_cell = my_run_cell else: app.run_cell = my_run_cell def enable_automatic_symbols(app): """Allow IPython to automatially create symbols (``isympy -a``). """ # XXX: This should perhaps use tokenize, like int_to_Integer() above. # This would avoid re-executing the code, which can lead to subtle # issues. For example: # # In [1]: a = 1 # # In [2]: for i in range(10): # ...: a += 1 # ...: # # In [3]: a # Out[3]: 11 # # In [4]: a = 1 # # In [5]: for i in range(10): # ...: a += 1 # ...: print b # ...: # b # b # b # b # b # b # b # b # b # b # # In [6]: a # Out[6]: 12 # # Note how the for loop is executed again because `b` was not defined, but `a` # was already incremented once, so the result is that it is incremented # multiple times. import re re_nameerror = re.compile( "name '(?P[A-Za-z_][A-Za-z0-9_]*)' is not defined") def _handler(self, etype, value, tb, tb_offset=None): """Handle :exc:`NameError` exception and allow injection of missing symbols. """ if etype is NameError and tb.tb_next and not tb.tb_next.tb_next: match = re_nameerror.match(str(value)) if match is not None: # XXX: Make sure Symbol is in scope. Otherwise you'll get infinite recursion. self.run_cell("%(symbol)s = Symbol('%(symbol)s')" % {'symbol': match.group("symbol")}, store_history=False) try: code = self.user_ns['In'][-1] except (KeyError, IndexError): pass else: self.run_cell(code, store_history=False) return None finally: self.run_cell("del %s" % match.group("symbol"), store_history=False) stb = self.InteractiveTB.structured_traceback( etype, value, tb, tb_offset=tb_offset) self._showtraceback(etype, value, stb) if hasattr(app, 'shell'): app.shell.set_custom_exc((NameError,), _handler) else: # This was restructured in IPython 0.13 app.set_custom_exc((NameError,), _handler) def init_ipython_session(argv=[], auto_symbols=False, auto_int_to_Integer=False): """Construct new IPython session. """ import IPython if IPython.__version__ >= '0.11': # use an app to parse the command line, and init config # IPython 1.0 deprecates the frontend module, so we import directly # from the terminal module to prevent a deprecation message from being # shown. if IPython.__version__ >= '1.0': from IPython.terminal import ipapp else: from IPython.frontend.terminal import ipapp app = ipapp.TerminalIPythonApp() # don't draw IPython banner during initialization: app.display_banner = False app.initialize(argv) if auto_symbols: readline = import_module("readline") if readline: enable_automatic_symbols(app) if auto_int_to_Integer: enable_automatic_int_sympification(app) return app.shell else: from IPython.Shell import make_IPython return make_IPython(argv) def init_python_session(): """Construct new Python session. """ from code import InteractiveConsole class SymPyConsole(InteractiveConsole): """An interactive console with readline support. """ def __init__(self): InteractiveConsole.__init__(self) try: import readline except ImportError: pass else: import os import atexit readline.parse_and_bind('tab: complete') if hasattr(readline, 'read_history_file'): history = os.path.expanduser('~/.sympy-history') try: readline.read_history_file(history) except IOError: pass atexit.register(readline.write_history_file, history) return SymPyConsole() def init_session(ipython=None, pretty_print=True, order=None, use_unicode=None, use_latex=None, quiet=False, auto_symbols=False, auto_int_to_Integer=False, argv=[]): """ Initialize an embedded IPython or Python session. The IPython session is initiated with the --pylab option, without the numpy imports, so that matplotlib plotting can be interactive. Parameters ========== pretty_print: boolean If True, use pretty_print to stringify; if False, use sstrrepr to stringify. order: string or None There are a few different settings for this parameter: lex (default), which is lexographic order; grlex, which is graded lexographic order; grevlex, which is reversed graded lexographic order; old, which is used for compatibility reasons and for long expressions; None, which sets it to lex. use_unicode: boolean or None If True, use unicode characters; if False, do not use unicode characters. use_latex: boolean or None If True, use latex rendering if IPython GUI's; if False, do not use latex rendering. quiet: boolean If True, init_session will not print messages regarding its status; if False, init_session will print messages regarding its status. auto_symbols: boolean If True, IPython will automatically create symbols for you. If False, it will not. The default is False. auto_int_to_Integer: boolean If True, IPython will automatically wrap int literals with Integer, so that things like 1/2 give Rational(1, 2). If False, it will not. The default is False. ipython: boolean or None If True, printing will initialize for an IPython console; if False, printing will initialize for a normal console; The default is None, which automatically determines whether we are in an ipython instance or not. argv: list of arguments for IPython See sympy.bin.isympy for options that can be used to initialize IPython. See Also ======== sympy.interactive.printing.init_printing: for examples and the rest of the parameters. Examples ======== >>> from sympy import init_session, Symbol, sin, sqrt >>> sin(x) #doctest: +SKIP NameError: name 'x' is not defined >>> init_session() #doctest: +SKIP >>> sin(x) #doctest: +SKIP sin(x) >>> sqrt(5) #doctest: +SKIP ___ \/ 5 >>> init_session(pretty_print=False) #doctest: +SKIP >>> sqrt(5) #doctest: +SKIP sqrt(5) >>> y + x + y**2 + x**2 #doctest: +SKIP x**2 + x + y**2 + y >>> init_session(order='grlex') #doctest: +SKIP >>> y + x + y**2 + x**2 #doctest: +SKIP x**2 + y**2 + x + y >>> init_session(order='grevlex') #doctest: +SKIP >>> y * x**2 + x * y**2 #doctest: +SKIP x**2*y + x*y**2 >>> init_session(order='old') #doctest: +SKIP >>> x**2 + y**2 + x + y #doctest: +SKIP x + y + x**2 + y**2 >>> theta = Symbol('theta') #doctest: +SKIP >>> theta #doctest: +SKIP theta >>> init_session(use_unicode=True) #doctest: +SKIP >>> theta # doctest: +SKIP \u03b8 """ import sys in_ipython = False if ipython is not False: try: import IPython except ImportError: if ipython is True: raise RuntimeError("IPython is not available on this system") ip = None else: if IPython.__version__ >= '0.11': try: ip = get_ipython() except NameError: ip = None else: ip = IPython.ipapi.get() if ip: ip = ip.IP in_ipython = bool(ip) if ipython is None: ipython = in_ipython if ipython is False: ip = init_python_session() mainloop = ip.interact else: if ip is None: ip = init_ipython_session(argv=argv, auto_symbols=auto_symbols, auto_int_to_Integer=auto_int_to_Integer) if IPython.__version__ >= '0.11': # runsource is gone, use run_cell instead, which doesn't # take a symbol arg. The second arg is `store_history`, # and False means don't add the line to IPython's history. ip.runsource = lambda src, symbol='exec': ip.run_cell(src, False) #Enable interactive plotting using pylab. try: ip.enable_pylab(import_all=False) except Exception: # Causes an import error if matplotlib is not installed. # Causes other errors (depending on the backend) if there # is no display, or if there is some problem in the # backend, so we have a bare "except Exception" here pass if not in_ipython: mainloop = ip.mainloop readline = import_module("readline") if auto_symbols and (not ipython or IPython.__version__ < '0.11' or not readline): raise RuntimeError("automatic construction of symbols is possible only in IPython 0.11 or above with readline support") if auto_int_to_Integer and (not ipython or IPython.__version__ < '0.11'): raise RuntimeError("automatic int to Integer transformation is possible only in IPython 0.11 or above") _preexec_source = preexec_source ip.runsource(_preexec_source, symbol='exec') init_printing(pretty_print=pretty_print, order=order, use_unicode=use_unicode, use_latex=use_latex, ip=ip) message = _make_message(ipython, quiet, _preexec_source) if not in_ipython: mainloop(message) sys.exit('Exiting ...') else: ip.write(message) ip.set_hook('shutdown_hook', lambda ip: ip.write("Exiting ...\n")) sympy-0.7.4.1/sympy/interactive/ipythonprinting.py0000644000175000017500000000337112253362407022565 0ustar georgeskgeorgesk""" A print function that pretty prints SymPy objects. :moduleauthor: Brian Granger Usage ===== To use this extension, execute: %load_ext sympy.interactive.ipythonprinting Once the extension is loaded, SymPy Basic objects are automatically pretty-printed in the terminal and rendered in LaTeX in the Qt console and notebook. """ #----------------------------------------------------------------------------- # Copyright (C) 2008 The IPython Development Team # # Distributed under the terms of the BSD License. The full license is in # the file COPYING, distributed as part of this software. #----------------------------------------------------------------------------- #----------------------------------------------------------------------------- # Imports #----------------------------------------------------------------------------- from __future__ import print_function, division import warnings from sympy.interactive.printing import init_printing from sympy.utilities.exceptions import SymPyDeprecationWarning #----------------------------------------------------------------------------- # Definitions of special display functions for use with IPython #----------------------------------------------------------------------------- def load_ipython_extension(ip): """Load the extension in IPython.""" # Since Python filters deprecation warnings by default, # we add a filter to make sure this message will be shown. warnings.simplefilter("once", SymPyDeprecationWarning) SymPyDeprecationWarning( feature="using %load_ext sympy.interactive.ipythonprinting", useinstead="from sympy import init_printing ; init_printing()", deprecated_since_version="0.7.3", issue=3914 ).warn() init_printing(ip=ip) sympy-0.7.4.1/sympy/interactive/printing.py0000644000175000017500000003040612253362407021151 0ustar georgeskgeorgesk"""Tools for setting up printing in interactive sessions. """ from __future__ import print_function, division from io import BytesIO from sympy import latex from sympy import preview from sympy.core.compatibility import integer_types, string_types from sympy.utilities.misc import debug def _init_python_printing(stringify_func): """Setup printing in Python interactive session. """ import sys from sympy.core.compatibility import builtins def _displayhook(arg): """Python's pretty-printer display hook. This function was adapted from: http://www.python.org/dev/peps/pep-0217/ """ if arg is not None: builtins._ = None print(stringify_func(arg)) builtins._ = arg sys.displayhook = _displayhook def _init_ipython_printing(ip, stringify_func, use_latex, euler, forecolor, backcolor, fontsize, latex_mode): """Setup printing in IPython interactive session. """ try: from IPython.lib.latextools import latex_to_png except ImportError: pass preamble = "\\documentclass[%s]{article}\n" \ "\\pagestyle{empty}\n" \ "\\usepackage{amsmath,amsfonts}%s\\begin{document}" if euler: addpackages = '\\usepackage{euler}' else: addpackages = '' preamble = preamble % (fontsize, addpackages) imagesize = 'tight' offset = "0cm,0cm" resolution = 150 dvi = r"-T %s -D %d -bg %s -fg %s -O %s" % ( imagesize, resolution, backcolor, forecolor, offset) dvioptions = dvi.split() debug("init_printing: DVIOPTIONS:", dvioptions) debug("init_printing: PREAMBLE:", preamble) def _print_plain(arg, p, cycle): """caller for pretty, for use in IPython 0.11""" if _can_print_latex(arg): p.text(stringify_func(arg)) else: p.text(IPython.lib.pretty.pretty(arg)) def _preview_wrapper(o): exprbuffer = BytesIO() try: preview(o, output='png', viewer='BytesIO', outputbuffer=exprbuffer, preamble=preamble, dvioptions=dvioptions) except Exception as e: # IPython swallows exceptions debug("png printing:", "_preview_wrapper exception raised:", repr(e)) raise return exprbuffer.getvalue() def _matplotlib_wrapper(o): # mathtext does not understand centain latex flags, so we try to # replace them with suitable subs o = o.replace(r'\operatorname', '') o = o.replace(r'\overline', r'\bar') return latex_to_png(o) def _can_print_latex(o): """Return True if type o can be printed with LaTeX. If o is a container type, this is True if and only if every element of o can be printed with LaTeX. """ import sympy if isinstance(o, (list, tuple, set, frozenset)): return all(_can_print_latex(i) for i in o) elif isinstance(o, dict): return all((isinstance(i, string_types) or _can_print_latex(i)) and _can_print_latex(o[i]) for i in o) elif isinstance(o, bool): return False elif isinstance(o, (sympy.Basic, sympy.matrices.MatrixBase, float, integer_types)): return True return False def _print_latex_png(o): """ A function that returns a png rendered by an external latex distribution, falling back to matplotlib rendering """ if _can_print_latex(o): s = latex(o, mode=latex_mode) try: return _preview_wrapper(s) except RuntimeError: if latex_mode != 'inline': s = latex(o, mode='inline') return _matplotlib_wrapper(s) def _print_latex_matplotlib(o): """ A function that returns a png rendered by mathtext """ if _can_print_latex(o): s = latex(o, mode='inline') return _matplotlib_wrapper(s) def _print_latex_text(o): """ A function to generate the latex representation of sympy expressions. """ if _can_print_latex(o): s = latex(o, mode='plain') s = s.replace(r'\dag', r'\dagger') s = s.strip('$') return '$$%s$$' % s def _result_display(self, arg): """IPython's pretty-printer display hook, for use in IPython 0.10 This function was adapted from: ipython/IPython/hooks.py:155 """ if self.rc.pprint: out = stringify_func(arg) if '\n' in out: print print(out) else: print(repr(arg)) import IPython if IPython.__version__ >= '0.11': from sympy.core.basic import Basic from sympy.matrices.matrices import MatrixBase printable_types = [Basic, MatrixBase, float, tuple, list, set, frozenset, dict] + list(integer_types) plaintext_formatter = ip.display_formatter.formatters['text/plain'] for cls in printable_types: plaintext_formatter.for_type(cls, _print_plain) png_formatter = ip.display_formatter.formatters['image/png'] if use_latex in (True, 'png'): debug("init_printing: using png formatter") for cls in printable_types: png_formatter.for_type(cls, _print_latex_png) elif use_latex == 'matplotlib': debug("init_printing: using matplotlib formatter") for cls in printable_types: png_formatter.for_type(cls, _print_latex_matplotlib) else: debug("init_printing: not using any png formatter") for cls in printable_types: # Better way to set this, but currently does not work in IPython #png_formatter.for_type(cls, None) if cls in png_formatter.type_printers: png_formatter.type_printers.pop(cls) latex_formatter = ip.display_formatter.formatters['text/latex'] if use_latex in (True, 'mathjax'): debug("init_printing: using mathjax formatter") for cls in printable_types: latex_formatter.for_type(cls, _print_latex_text) else: debug("init_printing: not using text/latex formatter") for cls in printable_types: # Better way to set this, but currently does not work in IPython #latex_formatter.for_type(cls, None) if cls in latex_formatter.type_printers: latex_formatter.type_printers.pop(cls) else: ip.set_hook('result_display', _result_display) def init_printing(pretty_print=True, order=None, use_unicode=None, use_latex=None, wrap_line=None, num_columns=None, no_global=False, ip=None, euler=False, forecolor='Black', backcolor='Transparent', fontsize='10pt', latex_mode='equation*'): """ Initializes pretty-printer depending on the environment. Parameters ========== pretty_print: boolean If True, use pretty_print to stringify; if False, use sstrrepr to stringify. order: string or None There are a few different settings for this parameter: lex (default), which is lexographic order; grlex, which is graded lexographic order; grevlex, which is reversed graded lexographic order; old, which is used for compatibility reasons and for long expressions; None, which sets it to lex. use_unicode: boolean or None If True, use unicode characters; if False, do not use unicode characters. use_latex: string, boolean, or None If True, use default latex rendering in GUI interfaces (png and mathjax); if False, do not use latex rendering; if 'png', enable latex rendering with an external latex compiler, falling back to matplotlib if external compilation fails; if 'matplotlib', enable latex rendering with matplotlib; if 'mathjax', enable latex text generation, for example MathJax rendering in IPython notebook or text rendering in LaTeX documents wrap_line: boolean If True, lines will wrap at the end; if False, they will not wrap but continue as one line. num_columns: int or None If int, number of columns before wrapping is set to num_columns; if None, number of columns before wrapping is set to terminal width. no_global: boolean If True, the settings become system wide; if False, use just for this console/session. ip: An interactive console This can either be an instance of IPython, or a class that derives from code.InteractiveConsole. Examples ======== >>> from sympy.interactive import init_printing >>> from sympy import Symbol, sqrt >>> from sympy.abc import x, y >>> sqrt(5) sqrt(5) >>> init_printing(pretty_print=True) # doctest: +SKIP >>> sqrt(5) # doctest: +SKIP ___ \/ 5 >>> theta = Symbol('theta') # doctest: +SKIP >>> init_printing(use_unicode=True) # doctest: +SKIP >>> theta # doctest: +SKIP \u03b8 >>> init_printing(use_unicode=False) # doctest: +SKIP >>> theta # doctest: +SKIP theta >>> init_printing(order='lex') # doctest: +SKIP >>> str(y + x + y**2 + x**2) # doctest: +SKIP x**2 + x + y**2 + y >>> init_printing(order='grlex') # doctest: +SKIP >>> str(y + x + y**2 + x**2) # doctest: +SKIP x**2 + x + y**2 + y >>> init_printing(order='grevlex') # doctest: +SKIP >>> str(y * x**2 + x * y**2) # doctest: +SKIP x**2*y + x*y**2 >>> init_printing(order='old') # doctest: +SKIP >>> str(x**2 + y**2 + x + y) # doctest: +SKIP x**2 + x + y**2 + y >>> init_printing(num_columns=10) # doctest: +SKIP >>> x**2 + x + y**2 + y # doctest: +SKIP x + y + x**2 + y**2 """ import sys from sympy.printing.printer import Printer if pretty_print: from sympy.printing import pretty as stringify_func else: from sympy.printing import sstrrepr as stringify_func # Even if ip is not passed, double check that not in IPython shell if ip is None: try: ip = get_ipython() except NameError: pass if ip and pretty_print: try: import IPython # IPython 1.0 deprecates the frontend module, so we import directly # from the terminal module to prevent a deprecation message from being # shown. if IPython.__version__ >= '1.0': from IPython.terminal.interactiveshell import TerminalInteractiveShell else: from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell from code import InteractiveConsole except ImportError: pass else: # This will be True if we are in the qtconsole or notebook if not isinstance(ip, (InteractiveConsole, TerminalInteractiveShell)) \ and 'ipython-console' not in ''.join(sys.argv): if use_unicode is None: debug("init_printing: Setting use_unicode to True") use_unicode = True if use_latex is None: debug("init_printing: Setting use_latex to True") use_latex = True if not no_global: Printer.set_global_settings(order=order, use_unicode=use_unicode, wrap_line=wrap_line, num_columns=num_columns) else: _stringify_func = stringify_func if pretty_print: stringify_func = lambda expr: \ _stringify_func(expr, order=order, use_unicode=use_unicode, wrap_line=wrap_line, num_columns=num_columns) else: stringify_func = lambda expr: _stringify_func(expr, order=order) if ip is not None and ip.__module__.startswith('IPython'): _init_ipython_printing(ip, stringify_func, use_latex, euler, forecolor, backcolor, fontsize, latex_mode) else: _init_python_printing(stringify_func) sympy-0.7.4.1/sympy/interactive/tests/0000755000175000017500000000000012253362407020104 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/interactive/tests/test_interactive.py0000644000175000017500000000111112253362407024024 0ustar georgeskgeorgeskimport sys from sympy.interactive.session import int_to_Integer def test_int_to_Integer(): assert int_to_Integer("1 + 2.2 + 0x3 + 40") == \ 'Integer (1 )+2.2 +Integer (0x3 )+Integer (40 )' if sys.version_info[0] == 2: assert int_to_Integer("1l") == 'Integer (1l )' assert int_to_Integer("0b101") == 'Integer (0b101 )' assert int_to_Integer("ab1 + 1 + '1 + 2'") == "ab1 +Integer (1 )+'1 + 2'" assert int_to_Integer("(2 + \n3)") == '(Integer (2 )+\nInteger (3 ))' assert int_to_Integer("2 + 2.0 + 2j + 2e-10") == 'Integer (2 )+2.0 +2j +2e-10 ' sympy-0.7.4.1/sympy/interactive/tests/test_ipython.py0000644000175000017500000000472212253362407023214 0ustar georgeskgeorgesk"""Tests of tools for setting up interactive IPython sessions. """ from sympy.interactive.session import (init_ipython_session, enable_automatic_symbols, enable_automatic_int_sympification) from sympy.core import Symbol, Rational, Integer from sympy.external import import_module from sympy.utilities.pytest import raises # TODO: The code below could be made more granular with something like: # # @requires('IPython', version=">=0.11") # def test_automatic_symbols(ipython): # run_cell was added in IPython 0.11 ipython = import_module("IPython", min_module_version="0.11") readline = import_module("readline") if not ipython: #bin/test will not execute any tests now disabled = True def test_automatic_symbols(): # this implicitly requires readline if not readline: return None # NOTE: Because of the way the hook works, you have to use run_cell(code, # True). This means that the code must have no Out, or it will be printed # during the tests. app = init_ipython_session() app.run_cell("from sympy import *") enable_automatic_symbols(app) symbol = "verylongsymbolname" assert symbol not in app.user_ns app.run_cell("a = %s" % symbol, True) assert symbol not in app.user_ns app.run_cell("a = type(%s)" % symbol, True) assert app.user_ns['a'] == Symbol app.run_cell("%s = Symbol('%s')" % (symbol, symbol), True) assert symbol in app.user_ns # Check that built-in names aren't overridden app.run_cell("a = all == __builtin__.all", True) assert "all" not in app.user_ns assert app.user_ns['a'] is True # Check that sympy names aren't overridden app.run_cell("import sympy") app.run_cell("a = factorial == sympy.factorial", True) assert app.user_ns['a'] is True def test_int_to_Integer(): # XXX: Warning, don't test with == here. 0.5 == Rational(1, 2) is True! app = init_ipython_session() app.run_cell("from sympy import Integer") app.run_cell("a = 1") assert isinstance(app.user_ns['a'], int) enable_automatic_int_sympification(app) app.run_cell("a = 1/2") assert isinstance(app.user_ns['a'], Rational) app.run_cell("a = 1") assert isinstance(app.user_ns['a'], Integer) app.run_cell("a = int(1)") assert isinstance(app.user_ns['a'], int) app.run_cell("a = (1/\n2)") assert app.user_ns['a'] == Rational(1, 2) # TODO: How can we test that the output of a SyntaxError is the original # input, not the transformed input? sympy-0.7.4.1/sympy/interactive/tests/test_ipythonprinting.py0000644000175000017500000000343712253362407024771 0ustar georgeskgeorgesk"""Tests that the IPython printing module is properly loaded. """ from sympy.core.compatibility import u from sympy.interactive.session import init_ipython_session from sympy.external import import_module # run_cell was added in IPython 0.11 ipython = import_module("IPython", min_module_version="0.11") # disable tests if ipython is not present if not ipython: disabled = True def test_ipythonprinting(): # Initialize and setup IPython session app = init_ipython_session() app.run_cell("ip = get_ipython()") app.run_cell("inst = ip.instance()") app.run_cell("format = inst.display_formatter.format") app.run_cell("from sympy import Symbol") # Printing without printing extension app.run_cell("a = format(Symbol('pi'))") app.run_cell("a2 = format(Symbol('pi')**2)") # Deal with API change starting at IPython 1.0 if int(ipython.__version__.split(".")[0]) < 1: assert app.user_ns['a']['text/plain'] == "pi" assert app.user_ns['a2']['text/plain'] == "pi**2" else: assert app.user_ns['a'][0]['text/plain'] == "pi" assert app.user_ns['a2'][0]['text/plain'] == "pi**2" # Load printing extension app.run_cell("from sympy import init_printing") app.run_cell("init_printing()") # Printing with printing extension app.run_cell("a = format(Symbol('pi'))") app.run_cell("a2 = format(Symbol('pi')**2)") # Deal with API change starting at IPython 1.0 if int(ipython.__version__.split(".")[0]) < 1: assert app.user_ns['a']['text/plain'] in (u('\u03c0'), 'pi') assert app.user_ns['a2']['text/plain'] in (u(' 2\n\u03c0 '), ' 2\npi ') else: assert app.user_ns['a'][0]['text/plain'] in (u('\u03c0'), 'pi') assert app.user_ns['a2'][0]['text/plain'] in (u(' 2\n\u03c0 '), ' 2\npi ') sympy-0.7.4.1/sympy/interactive/tests/__init__.py0000644000175000017500000000000012253362407022203 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/interactive/__init__.py0000644000175000017500000000020712253362407021052 0ustar georgeskgeorgesk"""Helper module for setting up interactive SymPy sessions. """ from .printing import init_printing from .session import init_session sympy-0.7.4.1/sympy/polys/0000755000175000017500000000000012253362407015573 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/polys/polyclasses.py0000644000175000017500000014571712253362407020525 0ustar georgeskgeorgesk"""OO layer for several polynomial representations. """ from __future__ import print_function, division from sympy.core.sympify import CantSympify from sympy.polys.polyutils import PicklableWithSlots from sympy.polys.polyerrors import CoercionFailed, NotReversible from sympy import oo 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) class DMP(PicklableWithSlots, CantSympify): """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)) 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 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 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 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) 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) 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 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 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) def to_ring(f): """Make the ground domain a ring. """ return f.convert(f.dom.get_ring()) def to_field(f): """Make the ground domain a field. """ return f.convert(f.dom.get_field()) def to_exact(f): """Make the ground domain exact. """ return f.convert(f.dom.get_exact()) 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) 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)) 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) ] 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) ] 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) 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') 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') 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') def lift(f): """Convert algebraic coefficients to rationals. """ return f.per(dmp_lift(f.rep, f.lev, f.dom), dom=f.dom.dom) 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) 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) 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.symbols)) 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) 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)) 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) 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)) 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)) 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)) 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)) 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)) def abs(f): """Make all coefficients in ``f`` positive. """ return f.per(dmp_abs(f.rep, f.lev, f.dom)) def neg(f): """Negate all coefficients in ``f``. """ return f.per(dmp_neg(f.rep, f.lev, f.dom)) 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)) 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)) 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)) def sqr(f): """Square a multivariate polynomial ``f``. """ return f.per(dmp_sqr(f.rep, f.lev, f.dom)) 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)) 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) 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)) 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)) 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)) 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) 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)) 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)) 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 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)) def degree_list(f): """Returns a list of degrees of ``f``. """ return dmp_degree_list(f.rep, f.lev) def total_degree(f): """Returns the total degree of ``f``. """ return max(sum(m) for m in f.monoms()) def homogenize(f, s): """Return homogeneous polynomial of ``f``""" td = f.total_degree() result = {} new_symbol = (s == len(f.terms()[0][0])) for term in f.terms(): d = sum(term[0]) if d < td: i = td - d else: i = 0 if new_symbol: result[term[0] + (i,)] = term[1] else: l = list(term[0]) l[s] += i result[tuple(l)] = term[1] return DMP(result, f.dom, f.lev + int(new_symbol), f.ring) def homogeneous_order(f): """Returns the homogeneous order of ``f``. """ if f.is_zero: return -oo monoms = f.monoms() tdeg = sum(monoms[0]) for monom in monoms: _tdeg = sum(monom) if _tdeg != tdeg: return None return tdeg def LC(f): """Returns the leading coefficient of ``f``. """ return dmp_ground_LC(f.rep, f.lev, f.dom) def TC(f): """Returns the trailing coefficient of ``f``. """ return dmp_ground_TC(f.rep, f.lev, f.dom) 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") def max_norm(f): """Returns maximum norm of ``f``. """ return dmp_max_norm(f.rep, f.lev, f.dom) def l1_norm(f): """Returns l1 norm of ``f``. """ return dmp_l1_norm(f.rep, f.lev, f.dom) 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) 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)) 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)) 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) 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') 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') 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') 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') 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)) 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) def discriminant(f): """Computes discriminant of ``f``. """ return f.per(dmp_discriminant(f.rep, f.lev, f.dom), kill=True) 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) 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)) 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)) 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 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)) def monic(f): """Divides all coefficients by ``LC(f)``. """ return f.per(dmp_ground_monic(f.rep, f.lev, f.dom)) def content(f): """Returns GCD of polynomial coefficients. """ return dmp_ground_content(f.rep, f.lev, f.dom) 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) 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)) 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') 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') 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') 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') 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) def sqf_part(f): """Computes square-free part of ``f``. """ return f.per(dmp_sqf_part(f.rep, f.lev, f.dom)) 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 ] 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 ] 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 ] 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 ] 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") 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") 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) 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 def is_zero(f): """Returns ``True`` if ``f`` is a zero polynomial. """ return dmp_zero_p(f.rep, f.lev) @property def is_one(f): """Returns ``True`` if ``f`` is a unit polynomial. """ return dmp_one_p(f.rep, f.lev, f.dom) @property 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 def is_sqf(f): """Returns ``True`` if ``f`` is a square-free polynomial. """ return dmp_sqf_p(f.rep, f.lev, f.dom) @property 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 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 def is_linear(f): """Returns ``True`` if ``f`` is linear in all its variables. """ return all(sum(monom) <= 1 for monom in dmp_to_dict(f.rep, f.lev, f.dom).keys()) @property def is_quadratic(f): """Returns ``True`` if ``f`` is quadratic in all its variables. """ return all(sum(monom) <= 2 for monom in dmp_to_dict(f.rep, f.lev, f.dom).keys()) @property def is_monomial(f): """Returns ``True`` if ``f`` is zero or has only one term. """ return len(f.to_dict()) <= 1 @property def is_homogeneous(f): """Returns ``True`` if ``f`` is a homogeneous polynomial. """ return f.homogeneous_order() is not None @property 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 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 __nonzero__(f): return not dmp_zero_p(f.rep, f.lev) __bool__ = __nonzero__ def init_normal_DMF(num, den, lev, dom): return DMF(dmp_normal(num, lev, dom), dmp_normal(den, lev, dom), dom, lev) class DMF(PicklableWithSlots, CantSympify): """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)) 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 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 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) 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) def numer(f): """Returns the numerator of ``f``. """ return f.half_per(f.num) def denom(f): """Returns the denominator of ``f``. """ return f.half_per(f.den) def cancel(f): """Remove common factors from ``f.num`` and ``f.den``. """ return f.per(f.num, f.den) def neg(f): """Negate all coefficients in ``f``. """ return f.per(dmp_neg(f.num, f.lev, f.dom), f.den, cancel=False) 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) 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) 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) 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)) 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 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 def is_zero(f): """Returns ``True`` if ``f`` is a zero fraction. """ return dmp_zero_p(f.num, f.lev) @property 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 __nonzero__(f): return not dmp_zero_p(f.num, f.lev) __bool__ = __nonzero__ def init_normal_ANP(rep, mod, dom): return ANP(dup_normal(rep, dom), dup_normal(mod, dom), dom) class ANP(PicklableWithSlots, CantSympify): """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)) 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) def to_dict(f): """Convert ``f`` to a dict representation with native coefficients. """ return dmp_to_dict(f.rep, 0, f.dom) 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 def to_list(f): """Convert ``f`` to a list representation with native coefficients. """ return f.rep 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 ] 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)) 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 def LC(f): """Returns the leading coefficient of ``f``. """ return dup_LC(f.rep, f.dom) def TC(f): """Returns the trailing coefficient of ``f``. """ return dup_TC(f.rep, f.dom) @property def is_zero(f): """Returns ``True`` if ``f`` is a zero algebraic number. """ return not f @property def is_one(f): """Returns ``True`` if ``f`` is a unit algebraic number. """ return f.rep == [f.dom.one] @property 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 (CoercionFailed, 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 (CoercionFailed, 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 (CoercionFailed, 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 (CoercionFailed, 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 __nonzero__(f): return bool(f.rep) __bool__ = __nonzero__ sympy-0.7.4.1/sympy/polys/densearith.py0000644000175000017500000010102712253362407020274 0ustar georgeskgeorgesk"""Arithmetics for dense recursive polynomials in ``K[x]`` or ``K[X]``. """ from __future__ import print_function, division from sympy.polys.densebasic import ( dup_slice, 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, PolynomialDivisionFailed) from sympy.core.compatibility import xrange def dup_add_term(f, c, i, K): """ Add ``c*x**i`` to ``f`` in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_add_term(x**2 - 1, ZZ(2), 4) 2*x**4 + x**2 - 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:] 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 import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_add_term(x*y + 1, 2, 2) 2*x**2 + x*y + 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:] def dup_sub_term(f, c, i, K): """ Subtract ``c*x**i`` from ``f`` in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_sub_term(2*x**4 + x**2 - 1, ZZ(2), 4) x**2 - 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:] 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 import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_sub_term(2*x**2 + x*y + 1, 2, 2) x*y + 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:] def dup_mul_term(f, c, i, K): """ Multiply ``f`` by ``c*x**i`` in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_mul_term(x**2 - 1, ZZ(3), 2) 3*x**4 - 3*x**2 """ if not c or not f: return [] else: return [ cf * c for cf in f ] + [K.zero]*i 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 import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_mul_term(x**2*y + x, 3*y, 2) 3*x**4*y**2 + 3*x**3*y """ 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_add_ground(x**3 + 2*x**2 + 3*x + 4, ZZ(4)) x**3 + 2*x**2 + 3*x + 8 """ return dup_add_term(f, c, 0, K) def dmp_add_ground(f, c, u, K): """ Add an element of the ground domain to ``f``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_add_ground(x**3 + 2*x**2 + 3*x + 4, ZZ(4)) x**3 + 2*x**2 + 3*x + 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_sub_ground(x**3 + 2*x**2 + 3*x + 4, ZZ(4)) x**3 + 2*x**2 + 3*x """ return dup_sub_term(f, c, 0, K) def dmp_sub_ground(f, c, u, K): """ Subtract an element of the ground domain from ``f``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_sub_ground(x**3 + 2*x**2 + 3*x + 4, ZZ(4)) x**3 + 2*x**2 + 3*x """ 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_mul_ground(x**2 + 2*x - 1, ZZ(3)) 3*x**2 + 6*x - 3 """ if not c or not f: return [] else: return [ cf * c for cf in f ] def dmp_mul_ground(f, c, u, K): """ Multiply ``f`` by a constant value in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_mul_ground(2*x + 2*y, ZZ(3)) 6*x + 6*y """ 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 import ring, ZZ, QQ >>> R, x = ring("x", ZZ) >>> R.dup_quo_ground(3*x**2 + 2, ZZ(2)) x**2 + 1 >>> R, x = ring("x", QQ) >>> R.dup_quo_ground(3*x**2 + 2, QQ(2)) 3/2*x**2 + 1 """ if not c: raise ZeroDivisionError('polynomial division') if not f: return f if K.has_Field: return [ K.quo(cf, c) for cf in f ] else: return [ cf // c for cf in f ] def dmp_quo_ground(f, c, u, K): """ Quotient by a constant in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ, QQ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_quo_ground(2*x**2*y + 3*x, ZZ(2)) x**2*y + x >>> R, x,y = ring("x,y", QQ) >>> R.dmp_quo_ground(2*x**2*y + 3*x, QQ(2)) x**2*y + 3/2*x """ 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 import ring, QQ >>> R, x = ring("x", QQ) >>> R.dup_exquo_ground(x**2 + 2, QQ(2)) 1/2*x**2 + 1 """ if not c: raise ZeroDivisionError('polynomial division') if not f: return f return [ K.exquo(cf, c) for cf in f ] def dmp_exquo_ground(f, c, u, K): """ Exact quotient by a constant in ``K[X]``. Examples ======== >>> from sympy.polys import ring, QQ >>> R, x,y = ring("x,y", QQ) >>> R.dmp_exquo_ground(x**2*y + 2*x, QQ(2)) 1/2*x**2*y + x """ 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 ] def dup_lshift(f, n, K): """ Efficiently multiply ``f`` by ``x**n`` in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_lshift(x**2 + 1, 2) x**4 + x**2 """ if not f: return f else: return f + [K.zero]*n def dup_rshift(f, n, K): """ Efficiently divide ``f`` by ``x**n`` in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_rshift(x**4 + x**2, 2) x**2 + 1 >>> R.dup_rshift(x**4 + x**2 + 2, 2) x**2 + 1 """ return f[:-n] def dup_abs(f, K): """ Make all coefficients positive in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_abs(x**2 - 1) x**2 + 1 """ return [ K.abs(coeff) for coeff in f ] def dmp_abs(f, u, K): """ Make all coefficients positive in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_abs(x**2*y - x) x**2*y + x """ 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_neg(x**2 - 1) -x**2 + 1 """ return [ -coeff for coeff in f ] def dmp_neg(f, u, K): """ Negate a polynomial in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_neg(x**2*y - x) -x**2*y + x """ if not u: return dup_neg(f, K) v = u - 1 return [ dmp_neg(cf, v, K) for cf in f ] def dup_add(f, g, K): """ Add dense polynomials in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_add(x**2 - 1, x - 2) x**2 + x - 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) ] def dmp_add(f, g, u, K): """ Add dense polynomials in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_add(x**2 + y, x**2*y + x) x**2*y + x**2 + x + y """ 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) ] def dup_sub(f, g, K): """ Subtract dense polynomials in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_sub(x**2 - 1, x - 2) x**2 - x + 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) ] def dmp_sub(f, g, u, K): """ Subtract dense polynomials in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_sub(x**2 + y, x**2*y + x) -x**2*y + x**2 - x + y """ 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_add_mul(x**2 - 1, x - 2, x + 2) 2*x**2 - 5 """ return dup_add(f, dup_mul(g, h, K), K) 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 import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_add_mul(x**2 + y, x, x + 2) 2*x**2 + 2*x + y """ 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_sub_mul(x**2 - 1, x - 2, x + 2) 3 """ return dup_sub(f, dup_mul(g, h, K), K) 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 import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_sub_mul(x**2 + y, x, x + 2) -2*x + y """ return dmp_sub(f, dmp_mul(g, h, u, K), u, K) def dup_mul(f, g, K): """ Multiply dense polynomials in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_mul(x - 2, x + 2) x**2 - 4 """ if f == g: return dup_sqr(f, K) if not (f and g): return [] df = dup_degree(f) dg = dup_degree(g) n = max(df, dg) + 1 if n < 100: h = [] for i in xrange(0, df + dg + 1): coeff = K.zero for j in xrange(max(0, i - dg), min(df, i) + 1): coeff += f[j]*g[i - j] h.append(coeff) return dup_strip(h) else: # Use Karatsuba's algorithm (divide and conquer), see e.g.: # Joris van der Hoeven, Relax But Don't Be Too Lazy, # J. Symbolic Computation, 11 (2002), section 3.1.1. n2 = n//2 fl, gl = dup_slice(f, 0, n2, K), dup_slice(g, 0, n2, K) fh = dup_rshift(dup_slice(f, n2, n, K), n2, K) gh = dup_rshift(dup_slice(g, n2, n, K), n2, K) lo, hi = dup_mul(fl, gl, K), dup_mul(fh, gh, K) mid = dup_mul(dup_add(fl, fh, K), dup_add(gl, gh, K), K) mid = dup_sub(mid, dup_add(lo, hi, K), K) return dup_add(dup_add(lo, dup_lshift(mid, n2, K), K), dup_lshift(hi, 2*n2, K), K) def dmp_mul(f, g, u, K): """ Multiply dense polynomials in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_mul(x*y + 1, x) x**2*y + x """ 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 xrange(0, df + dg + 1): coeff = dmp_zero(v) for j in xrange(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) def dup_sqr(f, K): """ Square dense polynomials in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_sqr(x**2 + 1) x**4 + 2*x**2 + 1 """ df, h = len(f) - 1, [] for i in xrange(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 xrange(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) def dmp_sqr(f, u, K): """ Square dense polynomials in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_sqr(x**2 + x*y + y**2) x**4 + 2*x**3*y + 3*x**2*y**2 + 2*x*y**3 + y**4 """ 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 xrange(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 xrange(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) def dup_pow(f, n, K): """ Raise ``f`` to the ``n``-th power in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_pow(x - 2, 3) x**3 - 6*x**2 + 12*x - 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 def dmp_pow(f, n, u, K): """ Raise ``f`` to the ``n``-th power in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_pow(x*y + 1, 3) x**3*y**3 + 3*x**2*y**2 + 3*x*y + 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 def dup_pdiv(f, g, K): """ Polynomial pseudo-division in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_pdiv(x**2 + 1, 2*x - 4) (2*x + 4, 20) """ df = dup_degree(f) dg = dup_degree(g) q, r, dr = [], f, df 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: 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) _dr, dr = dr, dup_degree(r) if dr < dg: break elif not (dr < _dr): raise PolynomialDivisionFailed(f, g, K) c = lc_g**N q = dup_mul_ground(q, c, K) r = dup_mul_ground(r, c, K) return q, r def dup_prem(f, g, K): """ Polynomial pseudo-remainder in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_prem(x**2 + 1, 2*x - 4) 20 """ df = dup_degree(f) dg = dup_degree(g) r, dr = f, df if not g: raise ZeroDivisionError("polynomial division") elif df < dg: return r N = df - dg + 1 lc_g = dup_LC(g, K) while True: 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) _dr, dr = dr, dup_degree(r) if dr < dg: break elif not (dr < _dr): raise PolynomialDivisionFailed(f, 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_pquo(x**2 - 1, 2*x - 2) 2*x + 2 >>> R.dup_pquo(x**2 + 1, 2*x - 4) 2*x + 4 """ return dup_pdiv(f, g, K)[0] def dup_pexquo(f, g, K): """ Polynomial pseudo-quotient in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_pexquo(x**2 - 1, 2*x - 2) 2*x + 2 >>> R.dup_pexquo(x**2 + 1, 2*x - 4) 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) def dmp_pdiv(f, g, u, K): """ Polynomial pseudo-division in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_pdiv(x**2 + x*y, 2*x + 2) (2*x + 2*y - 2, -4*y + 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, dr = dmp_zero(u), f, df if df < dg: return q, r N = df - dg + 1 lc_g = dmp_LC(g, K) while True: 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) _dr, dr = dr, dmp_degree(r, u) if dr < dg: break elif not (dr < _dr): raise PolynomialDivisionFailed(f, g, 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 def dmp_prem(f, g, u, K): """ Polynomial pseudo-remainder in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_prem(x**2 + x*y, 2*x + 2) -4*y + 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, dr = f, df if df < dg: return r N = df - dg + 1 lc_g = dmp_LC(g, K) while True: 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) _dr, dr = dr, dmp_degree(r, u) if dr < dg: break elif not (dr < _dr): raise PolynomialDivisionFailed(f, g, K) c = dmp_pow(lc_g, N, u - 1, K) return dmp_mul_term(r, c, 0, u, K) def dmp_pquo(f, g, u, K): """ Polynomial exact pseudo-quotient in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> f = x**2 + x*y >>> g = 2*x + 2*y >>> h = 2*x + 2 >>> R.dmp_pquo(f, g) 2*x >>> R.dmp_pquo(f, h) 2*x + 2*y - 2 """ return dmp_pdiv(f, g, u, K)[0] def dmp_pexquo(f, g, u, K): """ Polynomial pseudo-quotient in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> f = x**2 + x*y >>> g = 2*x + 2*y >>> h = 2*x + 2 >>> R.dmp_pexquo(f, g) 2*x >>> R.dmp_pexquo(f, h) 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) def dup_rr_div(f, g, K): """ Univariate division with remainder over a ring. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_rr_div(x**2 + 1, 2*x - 4) (0, x**2 + 1) """ df = dup_degree(f) dg = dup_degree(g) q, r, dr = [], f, df if not g: raise ZeroDivisionError("polynomial division") elif df < dg: return q, r lc_g = dup_LC(g, K) while True: 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) _dr, dr = dr, dup_degree(r) if dr < dg: break elif not (dr < _dr): raise PolynomialDivisionFailed(f, g, K) return q, r def dmp_rr_div(f, g, u, K): """ Multivariate division with remainder over a ring. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_rr_div(x**2 + x*y, 2*x + 2) (0, x**2 + x*y) """ 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, dr = dmp_zero(u), f, df if df < dg: return q, r lc_g, v = dmp_LC(g, K), u - 1 while True: 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) _dr, dr = dr, dmp_degree(r, u) if dr < dg: break elif not (dr < _dr): raise PolynomialDivisionFailed(f, g, K) return q, r def dup_ff_div(f, g, K): """ Polynomial division with remainder over a field. Examples ======== >>> from sympy.polys import ring, QQ >>> R, x = ring("x", QQ) >>> R.dup_ff_div(x**2 + 1, 2*x - 4) (1/2*x + 1, 5) """ df = dup_degree(f) dg = dup_degree(g) q, r, dr = [], f, df if not g: raise ZeroDivisionError("polynomial division") elif df < dg: return q, r lc_g = dup_LC(g, K) while True: 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) _dr, dr = dr, dup_degree(r) if dr < dg: break elif not (dr < _dr): raise PolynomialDivisionFailed(f, g, K) return q, r def dmp_ff_div(f, g, u, K): """ Polynomial division with remainder over a field. Examples ======== >>> from sympy.polys import ring, QQ >>> R, x,y = ring("x,y", QQ) >>> R.dmp_ff_div(x**2 + x*y, 2*x + 2) (1/2*x + 1/2*y - 1/2, -y + 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, dr = dmp_zero(u), f, df if df < dg: return q, r lc_g, v = dmp_LC(g, K), u - 1 while True: 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) _dr, dr = dr, dmp_degree(r, u) if dr < dg: break elif not (dr < _dr): raise PolynomialDivisionFailed(f, g, K) return q, r def dup_div(f, g, K): """ Polynomial division with remainder in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ, QQ >>> R, x = ring("x", ZZ) >>> R.dup_div(x**2 + 1, 2*x - 4) (0, x**2 + 1) >>> R, x = ring("x", QQ) >>> R.dup_div(x**2 + 1, 2*x - 4) (1/2*x + 1, 5) """ if K.has_Field: 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 import ring, ZZ, QQ >>> R, x = ring("x", ZZ) >>> R.dup_rem(x**2 + 1, 2*x - 4) x**2 + 1 >>> R, x = ring("x", QQ) >>> R.dup_rem(x**2 + 1, 2*x - 4) 5 """ return dup_div(f, g, K)[1] def dup_quo(f, g, K): """ Returns exact polynomial quotient in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ, QQ >>> R, x = ring("x", ZZ) >>> R.dup_quo(x**2 + 1, 2*x - 4) 0 >>> R, x = ring("x", QQ) >>> R.dup_quo(x**2 + 1, 2*x - 4) 1/2*x + 1 """ return dup_div(f, g, K)[0] def dup_exquo(f, g, K): """ Returns polynomial quotient in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_exquo(x**2 - 1, x - 1) x + 1 >>> R.dup_exquo(x**2 + 1, 2*x - 4) 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) def dmp_div(f, g, u, K): """ Polynomial division with remainder in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ, QQ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_div(x**2 + x*y, 2*x + 2) (0, x**2 + x*y) >>> R, x,y = ring("x,y", QQ) >>> R.dmp_div(x**2 + x*y, 2*x + 2) (1/2*x + 1/2*y - 1/2, -y + 1) """ if K.has_Field: return dmp_ff_div(f, g, u, K) else: return dmp_rr_div(f, g, u, K) def dmp_rem(f, g, u, K): """ Returns polynomial remainder in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ, QQ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_rem(x**2 + x*y, 2*x + 2) x**2 + x*y >>> R, x,y = ring("x,y", QQ) >>> R.dmp_rem(x**2 + x*y, 2*x + 2) -y + 1 """ return dmp_div(f, g, u, K)[1] def dmp_quo(f, g, u, K): """ Returns exact polynomial quotient in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ, QQ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_quo(x**2 + x*y, 2*x + 2) 0 >>> R, x,y = ring("x,y", QQ) >>> R.dmp_quo(x**2 + x*y, 2*x + 2) 1/2*x + 1/2*y - 1/2 """ return dmp_div(f, g, u, K)[0] def dmp_exquo(f, g, u, K): """ Returns polynomial quotient in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> f = x**2 + x*y >>> g = x + y >>> h = 2*x + 2 >>> R.dmp_exquo(f, g) x >>> R.dmp_exquo(f, h) 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_max_norm(-x**2 + 2*x - 3) 3 """ if not f: return K.zero else: return max(dup_abs(f, K)) def dmp_max_norm(f, u, K): """ Returns maximum norm of a polynomial in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_max_norm(2*x*y - x - 3) 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_l1_norm(2*x**3 - 3*x**2 + 1) 6 """ if not f: return K.zero else: return sum(dup_abs(f, K)) def dmp_l1_norm(f, u, K): """ Returns l1 norm of a polynomial in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_l1_norm(2*x*y - x - 3) 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_expand([x**2 - 1, x, 2]) 2*x**3 - 2*x """ if not polys: return [K.one] f = polys[0] for g in polys[1:]: f = dup_mul(f, g, K) return f def dmp_expand(polys, u, K): """ Multiply together several polynomials in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_expand([x**2 + y**2, x + 1]) x**3 + x**2 + x*y**2 + y**2 """ 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 sympy-0.7.4.1/sympy/polys/sqfreetools.py0000644000175000017500000002556712253362407020532 0ustar georgeskgeorgesk"""Square-free decomposition algorithms and related tools. """ from __future__ import print_function, division from sympy.polys.densebasic import ( dup_strip, dup_LC, dmp_ground_LC, dmp_zero_p, dmp_ground, dup_degree, dmp_degree, dmp_raise, dmp_inject, dup_convert) from sympy.polys.densearith import ( dup_neg, dmp_neg, dup_sub, dmp_sub, dup_mul, dup_quo, dmp_quo, dup_mul_ground, dmp_mul_ground) from sympy.polys.densetools import ( dup_diff, dmp_diff, dup_shift, dmp_compose, dup_monic, dmp_ground_monic, dup_primitive, dmp_ground_primitive) from sympy.polys.euclidtools import ( dup_inner_gcd, dmp_inner_gcd, dup_gcd, dmp_gcd, dmp_resultant) from sympy.polys.galoistools import ( gf_sqf_list, gf_sqf_part) from sympy.polys.polyerrors import ( MultivariatePolynomialError, DomainError) def dup_sqf_p(f, K): """ Return ``True`` if ``f`` is a square-free polynomial in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_sqf_p(x**2 - 2*x + 1) False >>> R.dup_sqf_p(x**2 - 1) True """ if not f: return True else: return not dup_degree(dup_gcd(f, dup_diff(f, 1, K), K)) def dmp_sqf_p(f, u, K): """ Return ``True`` if ``f`` is a square-free polynomial in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_sqf_p(x**2 + 2*x*y + y**2) False >>> R.dmp_sqf_p(x**2 + y**2) True """ if dmp_zero_p(f, u): return True else: return not dmp_degree(dmp_gcd(f, dmp_diff(f, 1, u, K), u, K), u) def dup_sqf_norm(f, K): """ Square-free norm of ``f`` in ``K[x]``, useful over algebraic domains. 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 ``K``. Examples ======== >>> from sympy.polys import ring, QQ >>> from sympy import sqrt >>> K = QQ.algebraic_field(sqrt(3)) >>> R, x = ring("x", K) >>> _, X = ring("x", QQ) >>> s, f, r = R.dup_sqf_norm(x**2 - 2) >>> s == 1 True >>> f == x**2 + K([QQ(-2), QQ(0)])*x + 1 True >>> r == X**4 - 10*X**2 + 1 True """ if not K.is_Algebraic: raise DomainError("ground domain must be algebraic") s, g = 0, dmp_raise(K.mod.rep, 1, 0, K.dom) while True: h, _ = dmp_inject(f, 0, K, front=True) r = dmp_resultant(g, h, 1, K.dom) if dup_sqf_p(r, K.dom): break else: f, s = dup_shift(f, -K.unit, K), s + 1 return s, f, r def dmp_sqf_norm(f, u, K): """ Square-free norm of ``f`` in ``K[X]``, useful over algebraic domains. 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 ``K``. Examples ======== >>> from sympy.polys import ring, QQ >>> from sympy import I >>> K = QQ.algebraic_field(I) >>> R, x, y = ring("x,y", K) >>> _, X, Y = ring("x,y", QQ) >>> s, f, r = R.dmp_sqf_norm(x*y + y**2) >>> s == 1 True >>> f == x*y + y**2 + K([QQ(-1), QQ(0)])*y True >>> r == X**2*Y**2 + 2*X*Y**3 + Y**4 + Y**2 True """ if not u: return dup_sqf_norm(f, K) if not K.is_Algebraic: raise DomainError("ground domain must be algebraic") g = dmp_raise(K.mod.rep, u + 1, 0, K.dom) F = dmp_raise([K.one, -K.unit], u, 0, K) s = 0 while True: h, _ = dmp_inject(f, u, K, front=True) r = dmp_resultant(g, h, u + 1, K.dom) if dmp_sqf_p(r, u, K.dom): break else: f, s = dmp_compose(f, F, u, K), s + 1 return s, f, r def dup_gf_sqf_part(f, K): """Compute square-free part of ``f`` in ``GF(p)[x]``. """ f = dup_convert(f, K, K.dom) g = gf_sqf_part(f, K.mod, K.dom) return dup_convert(g, K.dom, K) def dmp_gf_sqf_part(f, K): """Compute square-free part of ``f`` in ``GF(p)[X]``. """ raise NotImplementedError('multivariate polynomials over finite fields') def dup_sqf_part(f, K): """ Returns square-free part of a polynomial in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_sqf_part(x**3 - 3*x - 2) x**2 - x - 2 """ if K.is_FiniteField: return dup_gf_sqf_part(f, K) if not f: return f if K.is_negative(dup_LC(f, K)): f = dup_neg(f, K) gcd = dup_gcd(f, dup_diff(f, 1, K), K) sqf = dup_quo(f, gcd, K) if K.has_Field: return dup_monic(sqf, K) else: return dup_primitive(sqf, K)[1] def dmp_sqf_part(f, u, K): """ Returns square-free part of a polynomial in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_sqf_part(x**3 + 2*x**2*y + x*y**2) x**2 + x*y """ if not u: return dup_sqf_part(f, K) if K.is_FiniteField: return dmp_gf_sqf_part(f, u, K) if dmp_zero_p(f, u): return f if K.is_negative(dmp_ground_LC(f, u, K)): f = dmp_neg(f, u, K) gcd = dmp_gcd(f, dmp_diff(f, 1, u, K), u, K) sqf = dmp_quo(f, gcd, u, K) if K.has_Field: return dmp_ground_monic(sqf, u, K) else: return dmp_ground_primitive(sqf, u, K)[1] def dup_gf_sqf_list(f, K, all=False): """Compute square-free decomposition of ``f`` in ``GF(p)[x]``. """ f = dup_convert(f, K, K.dom) coeff, factors = gf_sqf_list(f, K.mod, K.dom, all=all) 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_sqf_list(f, u, K, all=False): """Compute square-free decomposition of ``f`` in ``GF(p)[X]``. """ raise NotImplementedError('multivariate polynomials over finite fields') def dup_sqf_list(f, K, all=False): """ Return square-free decomposition of a polynomial in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> f = 2*x**5 + 16*x**4 + 50*x**3 + 76*x**2 + 56*x + 16 >>> R.dup_sqf_list(f) (2, [(x + 1, 2), (x + 2, 3)]) >>> R.dup_sqf_list(f, all=True) (2, [(1, 1), (x + 1, 2), (x + 2, 3)]) """ if K.is_FiniteField: return dup_gf_sqf_list(f, K, all=all) if K.has_Field: coeff = dup_LC(f, K) f = dup_monic(f, K) else: coeff, f = dup_primitive(f, K) if K.is_negative(dup_LC(f, K)): f = dup_neg(f, K) coeff = -coeff if dup_degree(f) <= 0: return coeff, [] result, i = [], 1 h = dup_diff(f, 1, K) g, p, q = dup_inner_gcd(f, h, K) while True: d = dup_diff(p, 1, K) h = dup_sub(q, d, K) if not h: result.append((p, i)) break g, p, q = dup_inner_gcd(p, h, K) if all or dup_degree(g) > 0: result.append((g, i)) i += 1 return coeff, result def dup_sqf_list_include(f, K, all=False): """ Return square-free decomposition of a polynomial in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> f = 2*x**5 + 16*x**4 + 50*x**3 + 76*x**2 + 56*x + 16 >>> R.dup_sqf_list_include(f) [(2, 1), (x + 1, 2), (x + 2, 3)] >>> R.dup_sqf_list_include(f, all=True) [(2, 1), (x + 1, 2), (x + 2, 3)] """ coeff, factors = dup_sqf_list(f, K, all=all) if factors and factors[0][1] == 1: g = dup_mul_ground(factors[0][0], coeff, K) return [(g, 1)] + factors[1:] else: g = dup_strip([coeff]) return [(g, 1)] + factors def dmp_sqf_list(f, u, K, all=False): """ Return square-free decomposition of a polynomial in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> f = x**5 + 2*x**4*y + x**3*y**2 >>> R.dmp_sqf_list(f) (1, [(x + y, 2), (x, 3)]) >>> R.dmp_sqf_list(f, all=True) (1, [(1, 1), (x + y, 2), (x, 3)]) """ if not u: return dup_sqf_list(f, K, all=all) if K.is_FiniteField: return dmp_gf_sqf_list(f, u, K, all=all) if K.has_Field: coeff = dmp_ground_LC(f, u, K) f = dmp_ground_monic(f, u, K) else: coeff, f = dmp_ground_primitive(f, u, K) if K.is_negative(dmp_ground_LC(f, u, K)): f = dmp_neg(f, u, K) coeff = -coeff if dmp_degree(f, u) <= 0: return coeff, [] result, i = [], 1 h = dmp_diff(f, 1, u, K) g, p, q = dmp_inner_gcd(f, h, u, K) while True: d = dmp_diff(p, 1, u, K) h = dmp_sub(q, d, u, K) if dmp_zero_p(h, u): result.append((p, i)) break g, p, q = dmp_inner_gcd(p, h, u, K) if all or dmp_degree(g, u) > 0: result.append((g, i)) i += 1 return coeff, result def dmp_sqf_list_include(f, u, K, all=False): """ Return square-free decomposition of a polynomial in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> f = x**5 + 2*x**4*y + x**3*y**2 >>> R.dmp_sqf_list_include(f) [(1, 1), (x + y, 2), (x, 3)] >>> R.dmp_sqf_list_include(f, all=True) [(1, 1), (x + y, 2), (x, 3)] """ if not u: return dup_sqf_list_include(f, K, all=all) coeff, factors = dmp_sqf_list(f, u, K, all=all) if factors and factors[0][1] == 1: g = dmp_mul_ground(factors[0][0], coeff, u, K) return [(g, 1)] + factors[1:] else: g = dmp_ground(coeff, u) return [(g, 1)] + factors def dup_gff_list(f, K): """ Compute greatest factorial factorization of ``f`` in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_gff_list(x**5 + 2*x**4 - x**3 - 2*x**2) [(x, 1), (x + 2, 4)] """ if not f: raise ValueError("greatest factorial factorization doesn't exist for a zero polynomial") f = dup_monic(f, K) if not dup_degree(f): return [] else: g = dup_gcd(f, dup_shift(f, K.one, K), K) H = dup_gff_list(g, K) for i, (h, k) in enumerate(H): g = dup_mul(g, dup_shift(h, -K(k), K), K) H[i] = (h, k + 1) f = dup_quo(f, g, K) if not dup_degree(f): return H else: return [(f, 1)] + H def dmp_gff_list(f, u, K): """ Compute greatest factorial factorization of ``f`` in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) """ if not u: return dup_gff_list(f, K) else: raise MultivariatePolynomialError(f) sympy-0.7.4.1/sympy/polys/rationaltools.py0000644000175000017500000000537512253362407021051 0ustar georgeskgeorgesk"""Tools for manipulation of rational expressions. """ from __future__ import print_function, division from sympy.core import Basic, Add, sympify from sympy.core.exprtools import gcd_terms from sympy.utilities import public @public 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)) sympy-0.7.4.1/sympy/polys/heuristicgcd.py0000644000175000017500000000730112253362407020623 0ustar georgeskgeorgesk"""Heuristic polynomial GCD algorithm (HEUGCD). """ from __future__ import print_function, division from sympy.core.compatibility import xrange HEU_GCD_MAX = 6 def heugcd(f, g): """ 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.heuristicgcd import heugcd >>> from sympy.polys import ring, ZZ >>> R, x,y, = ring("x,y", ZZ) >>> f = x**2 + 2*x*y + y**2 >>> g = x**2 + x*y >>> h, cff, cfg = heugcd(f, g) >>> h, cff, cfg (x + y, x + y, x) >>> cff*h == f True >>> cfg*h == g True References ========== 1. [Liao95]_ """ assert f.ring == g.ring and f.ring.domain.is_ZZ ring = f.ring x0 = ring.gens[0] domain = ring.domain gcd, f, g = f.extract_ground(g) f_norm = f.max_norm() g_norm = g.max_norm() B = domain(2*min(f_norm, g_norm) + 29) x = max(min(B, 99*domain.sqrt(B)), 2*min(f_norm // abs(f.LC), g_norm // abs(g.LC)) + 2) for i in xrange(0, HEU_GCD_MAX): ff = f.evaluate(x0, x) gg = g.evaluate(x0, x) if ff and gg: if ring.ngens == 1: h, cff, cfg = domain.cofactors(ff, gg) else: h, cff, cfg = heugcd(ff, gg) h = _gcd_interpolate(h, x, ring) h = h.primitive()[1] cff_, r = f.div(h) if not r: cfg_, r = g.div(h) if not r: h = h.mul_ground(gcd) return h, cff_, cfg_ cff = _gcd_interpolate(cff, x, ring) h, r = f.div(cff) if not r: cfg_, r = g.div(h) if not r: h = h.mul_ground(gcd) return h, cff, cfg_ cfg = _gcd_interpolate(cfg, x, ring) h, r = g.div(cfg) if not r: cff_, r = f.div(h) if not r: h = h.mul_ground(gcd) return h, cff_, cfg x = 73794*x * domain.sqrt(domain.sqrt(x)) // 27011 raise HeuristicGCDFailed('no luck') def _gcd_interpolate(h, x, ring): """Interpolate polynomial GCD from integer GCD. """ f, i = ring.zero, 0 # TODO: don't expose poly repr implementation details if ring.ngens == 1: while h: g = h % x if g > x // 2: g -= x h = (h - g) // x # f += X**i*g if g: f[(i,)] = g i += 1 else: while h: g = h.trunc_ground(x) h = (h - g).quo_ground(x) # f += X**i*g if g: for monom, coeff in g.iterterms(): f[(i,) + monom] = coeff i += 1 if f.LC < 0: return -f else: return f sympy-0.7.4.1/sympy/polys/polyerrors.py0000644000175000017500000001072712253362407020374 0ustar georgeskgeorgesk"""Definitions of common exceptions for `polys` module. """ from __future__ import print_function, division from sympy.utilities import public @public class BasePolynomialError(Exception): """Base class for polynomial related exceptions. """ def new(self, *args): raise NotImplementedError("abstract base class") @public 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) @public class PolynomialDivisionFailed(BasePolynomialError): def __init__(self, f, g, domain): self.f = f self.g = g self.domain = domain def __str__(self): if self.domain.is_EX: msg = "You may want to use a different simplification algorithm. Note " \ "that in general it's not possible to guarantee to detect zero " \ "in this domain." elif not self.domain.is_Exact: msg = "Your working precision or tolerance of computations may be set " \ "improperly. Adjust those parameters of the coefficient domain " \ "and try again." else: msg = "Zero detection is guaranteed in this coefficient domain. This " \ "may indicate a bug in SymPy or the domain is user defined and " \ "doesn't implement zero detection properly." return "couldn't reduce degree in a polynomial division algorithm when " \ "dividing %s by %s. This can happen when it's not possible to " \ "detect zero in the coefficient domain. The domain of computation " \ "is %s. %s" % (self.f, self.g, self.domain, msg) @public 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__) @public class HeuristicGCDFailed(BasePolynomialError): pass class ModularGCDFailed(BasePolynomialError): pass @public class HomomorphismFailed(BasePolynomialError): pass @public class IsomorphismFailed(BasePolynomialError): pass @public class ExtraneousFactors(BasePolynomialError): pass @public class EvaluationFailed(BasePolynomialError): pass @public class RefinementFailed(BasePolynomialError): pass @public class CoercionFailed(BasePolynomialError): pass @public class NotInvertible(BasePolynomialError): pass @public class NotReversible(BasePolynomialError): pass @public class NotAlgebraic(BasePolynomialError): pass @public class DomainError(BasePolynomialError): pass @public class PolynomialError(BasePolynomialError): pass @public class UnificationFailed(BasePolynomialError): pass @public class GeneratorsError(BasePolynomialError): pass @public class GeneratorsNeeded(GeneratorsError): pass @public 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]))) @public class UnivariatePolynomialError(PolynomialError): pass @public class MultivariatePolynomialError(PolynomialError): pass @public 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)) @public class OptionError(BasePolynomialError): pass @public class FlagError(OptionError): pass sympy-0.7.4.1/sympy/polys/polyutils.py0000644000175000017500000002763212253362407020223 0ustar georgeskgeorgesk"""Useful utilities for higher level polynomial classes. """ from __future__ import print_function, division from sympy.polys.polyerrors import PolynomialError, GeneratorsNeeded, GeneratorsError from sympy.polys.polyoptions import build_options from sympy.core.exprtools import decompose_power from sympy.core import S, Add, Mul, Pow, expand_mul, expand_multinomial from sympy.assumptions import ask, Q from sympy.core.compatibility import xrange import re _gens_order = { 'a': 301, 'b': 302, 'c': 303, 'd': 304, 'e': 305, 'f': 306, 'g': 307, 'h': 308, 'i': 309, 'j': 310, 'k': 311, 'l': 312, 'm': 313, 'n': 314, 'o': 315, 'p': 216, 'q': 217, 'r': 218, 's': 219, 't': 220, 'u': 221, 'v': 222, 'w': 223, 'x': 124, 'y': 125, 'z': 126, } _max_order = 1000 _re_gen = re.compile(r"^(.+?)(\d*)$") def _sort_gens(gens, **args): """Sort generators in a reasonably intelligent way. """ opt = build_options(args) gens_order, wrt = {}, None if opt is not None: gens_order, wrt = {}, opt.wrt for i, gen in enumerate(opt.sort): gens_order[gen] = i + 1 def order_key(gen): gen = str(gen) if wrt is not None: try: return (-len(wrt) + wrt.index(gen), gen, 0) except ValueError: pass name, index = _re_gen.match(gen).groups() if index: index = int(index) else: index = 0 try: return ( gens_order[name], name, index) except KeyError: pass try: return (_gens_order[name], name, index) except KeyError: pass return (_max_order, name, index) try: gens = sorted(gens, key=order_key) except TypeError: # pragma: no cover pass return tuple(gens) def _unify_gens(f_gens, g_gens): """Unify generators in a reasonably intelligent way. """ f_gens = list(f_gens) g_gens = list(g_gens) if f_gens == g_gens: return tuple(f_gens) gens, common, k = [], [], 0 for gen in f_gens: if gen in g_gens: common.append(gen) for i, gen in enumerate(g_gens): if gen in common: g_gens[i], k = common[k], k + 1 for gen in common: i = f_gens.index(gen) gens.extend(f_gens[:i]) f_gens = f_gens[i + 1:] i = g_gens.index(gen) gens.extend(g_gens[:i]) g_gens = g_gens[i + 1:] gens.append(gen) gens.extend(f_gens) gens.extend(g_gens) return tuple(gens) def _analyze_gens(gens): """Support for passing generators as `*gens` and `[gens]`. """ if len(gens) == 1 and hasattr(gens[0], '__iter__'): return tuple(gens[0]) else: return tuple(gens) def _sort_factors(factors, **args): """Sort low-level factors in increasing 'complexity' order. """ def order_if_multiple_key(factor): (f, n) = factor return (len(f), n, f) def order_no_multiple_key(f): return (len(f), f) if args.get('multiple', True): return sorted(factors, key=order_if_multiple_key) else: return sorted(factors, key=order_no_multiple_key) def _not_a_coeff(expr): """Do not treat NaN and infinities as valid polynomial coefficients. """ return expr in [S.NaN, S.Infinity, S.NegativeInfinity, S.ComplexInfinity] def _parallel_dict_from_expr_if_gens(exprs, opt): """Transform expressions into a multinomial form given generators. """ k, indices = len(opt.gens), {} for i, g in enumerate(opt.gens): indices[g] = i polys = [] for expr in exprs: poly = {} if expr.is_Equality: expr = expr.lhs - expr.rhs for term in Add.make_args(expr): coeff, monom = [], [0]*k for factor in Mul.make_args(term): if not _not_a_coeff(factor) and factor.is_Number: coeff.append(factor) else: try: base, exp = decompose_power(factor) if exp < 0: exp, base = -exp, Pow(base, -S.One) monom[indices[base]] = exp except KeyError: if not factor.free_symbols.intersection(opt.gens): coeff.append(factor) else: raise PolynomialError("%s contains an element of the generators set" % factor) monom = tuple(monom) if monom in poly: poly[monom] += Mul(*coeff) else: poly[monom] = Mul(*coeff) polys.append(poly) return polys, opt.gens def _parallel_dict_from_expr_no_gens(exprs, opt): """Transform expressions into a multinomial form and figure out generators. """ if opt.domain is not None: def _is_coeff(factor): return factor in opt.domain elif opt.extension is True: def _is_coeff(factor): return ask(Q.algebraic(factor)) elif opt.greedy is not False: def _is_coeff(factor): return False else: def _is_coeff(factor): return factor.is_number gens, reprs = set([]), [] for expr in exprs: terms = [] if expr.is_Equality: expr = expr.lhs - expr.rhs for term in Add.make_args(expr): coeff, elements = [], {} for factor in Mul.make_args(term): if not _not_a_coeff(factor) and (factor.is_Number or _is_coeff(factor)): coeff.append(factor) else: base, exp = decompose_power(factor) if exp < 0: exp, base = -exp, Pow(base, -S.One) elements[base] = exp gens.add(base) terms.append((coeff, elements)) reprs.append(terms) if not gens: if len(exprs) == 1: arg = exprs[0] else: arg = (exprs,) raise GeneratorsNeeded("specify generators to give %s a meaning" % arg) gens = _sort_gens(gens, opt=opt) k, indices = len(gens), {} for i, g in enumerate(gens): indices[g] = i polys = [] for terms in reprs: poly = {} for coeff, term in terms: monom = [0]*k for base, exp in term.items(): monom[indices[base]] = exp monom = tuple(monom) if monom in poly: poly[monom] += Mul(*coeff) else: poly[monom] = Mul(*coeff) polys.append(poly) return polys, tuple(gens) def _dict_from_expr_if_gens(expr, opt): """Transform an expression into a multinomial form given generators. """ (poly,), gens = _parallel_dict_from_expr_if_gens((expr,), opt) return poly, gens def _dict_from_expr_no_gens(expr, opt): """Transform an expression into a multinomial form and figure out generators. """ (poly,), gens = _parallel_dict_from_expr_no_gens((expr,), opt) return poly, gens def parallel_dict_from_expr(exprs, **args): """Transform expressions into a multinomial form. """ reps, opt = _parallel_dict_from_expr(exprs, build_options(args)) return reps, opt.gens def _parallel_dict_from_expr(exprs, opt): """Transform expressions into a multinomial form. """ if opt.expand is not False: exprs = [ expr.expand() for expr in exprs ] if any(expr.is_commutative is False for expr in exprs): raise PolynomialError('non-commutative expressions are not supported') if opt.gens: reps, gens = _parallel_dict_from_expr_if_gens(exprs, opt) else: reps, gens = _parallel_dict_from_expr_no_gens(exprs, opt) return reps, opt.clone({'gens': gens}) def dict_from_expr(expr, **args): """Transform an expression into a multinomial form. """ rep, opt = _dict_from_expr(expr, build_options(args)) return rep, opt.gens def _dict_from_expr(expr, opt): """Transform an expression into a multinomial form. """ if expr.is_commutative is False: raise PolynomialError('non-commutative expressions are not supported') def _is_expandable_pow(expr): return (expr.is_Pow and expr.exp.is_positive and expr.exp.is_Integer and expr.base.is_Add) if opt.expand is not False: expr = expr.expand() # TODO: Integrate this into expand() itself while any(_is_expandable_pow(i) or i.is_Mul and any(_is_expandable_pow(j) for j in i.args) for i in Add.make_args(expr)): expr = expand_multinomial(expr) while any(i.is_Mul and any(j.is_Add for j in i.args) for i in Add.make_args(expr)): expr = expand_mul(expr) if opt.gens: rep, gens = _dict_from_expr_if_gens(expr, opt) else: rep, gens = _dict_from_expr_no_gens(expr, opt) return rep, opt.clone({'gens': gens}) def expr_from_dict(rep, *gens): """Convert a multinomial form into an expression. """ result = [] for monom, coeff in rep.items(): term = [coeff] for g, m in zip(gens, monom): if m: term.append(Pow(g, m)) result.append(Mul(*term)) return Add(*result) parallel_dict_from_basic = parallel_dict_from_expr dict_from_basic = dict_from_expr basic_from_dict = expr_from_dict def _dict_reorder(rep, gens, new_gens): """Reorder levels using dict representation. """ gens = list(gens) monoms = rep.keys() coeffs = rep.values() new_monoms = [ [] for _ in xrange(len(rep)) ] used_indices = set() for gen in new_gens: try: j = gens.index(gen) used_indices.add(j) for M, new_M in zip(monoms, new_monoms): new_M.append(M[j]) except ValueError: for new_M in new_monoms: new_M.append(0) for i, _ in enumerate(gens): if i not in used_indices: for monom in monoms: if monom[i]: raise GeneratorsError("unable to drop generators") return map(tuple, new_monoms), coeffs class PicklableWithSlots(object): """ Mixin class that allows to pickle objects with ``__slots__``. Examples -------- First define a class that mixes :class:`PicklableWithSlots` in:: >>> from sympy.polys.polyutils import PicklableWithSlots >>> class Some(PicklableWithSlots): ... __slots__ = ['foo', 'bar'] ... ... def __init__(self, foo, bar): ... self.foo = foo ... self.bar = bar To make :mod:`pickle` happy in doctest we have to use this hack:: >>> from sympy.core.compatibility import builtins >>> builtins.Some = Some Next lets see if we can create an instance, pickle it and unpickle:: >>> some = Some('abc', 10) >>> some.foo, some.bar ('abc', 10) >>> from pickle import dumps, loads >>> some2 = loads(dumps(some)) >>> some2.foo, some2.bar ('abc', 10) """ __slots__ = [] def __getstate__(self, cls=None): if cls is None: # This is the case for the instance that gets pickled cls = self.__class__ d = {} # Get all data that should be stored from super classes for c in cls.__bases__: if hasattr(c, "__getstate__"): d.update(c.__getstate__(self, c)) # Get all information that should be stored from cls and return the dict for name in cls.__slots__: if hasattr(self, name): d[name] = getattr(self, name) return d def __setstate__(self, d): # All values that were pickled are now assigned to a fresh instance for name, value in d.items(): try: setattr(self, name, value) except AttributeError: # This is needed in cases like Rational :> Half pass sympy-0.7.4.1/sympy/polys/polytools.py0000644000175000017500000050335212253362407020221 0ustar georgeskgeorgesk"""User-friendly public interface to polynomial functions. """ from __future__ import print_function, division from sympy.core import ( S, Basic, Expr, I, Integer, Add, Mul, Dummy, Tuple ) from sympy.core.mul import _keep_coeff from sympy.core.symbol import Symbol from sympy.core.basic import preorder_traversal from sympy.core.relational import Relational from sympy.core.sympify import sympify from sympy.core.decorators import _sympifyit from sympy.logic.boolalg import BooleanAtom 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.groebnertools import groebner as _groebner from sympy.polys.fglmtools import matrix_fglm from sympy.polys.monomials import Monomial from sympy.polys.orderings import monomial_key from sympy.polys.polyerrors import ( OperationNotSupported, DomainError, CoercionFailed, UnificationFailed, GeneratorsNeeded, PolynomialError, MultivariatePolynomialError, ExactQuotientFailed, PolificationFailed, ComputationFailed, GeneratorsError, ) from sympy.utilities import group, sift, public 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 @public 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 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 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 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 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 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 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 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.symbols: symbols |= gen.free_symbols elif domain.is_EX: for coeff in self.coeffs(): symbols |= coeff.free_symbols return symbols @property 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 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 def domain(self): """Get the ground domain of ``self``. """ return self.get_domain() @property 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 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 def unit(self): """Return unit polynomial with ``self``'s properties. """ return self.new(self.rep.unit(self.rep.lev, self.rep.dom), *self.gens) def unify(f, g): """ Make ``f`` and ``g`` belong to the same domain. Examples ======== >>> from sympy import Poly >>> from sympy.abc import x >>> 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 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) 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)) def get_domain(f): """Get the ground domain of ``f``. """ return f.rep.dom 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)) 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 domain.is_FiniteField: 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) 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) 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.symbols: 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)) 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) 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) 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 """ 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 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) 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) 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) def retract(f, field=None): """ Recalculate the ground domain of a polynomial. Examples ======== >>> from sympy import Poly >>> from sympy.abc import x >>> 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, composite=f.domain.is_Composite or None) return f.from_dict(rep, f.gens, domain=dom) 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) 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] See Also ======== all_coeffs coeff_monomial nth """ return [f.rep.dom.to_sympy(c) for c in f.rep.coeffs(order=order)] 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)] See Also ======== all_monoms """ return f.rep.monoms(order=order) 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)] See Also ======== all_terms """ return [(m, f.rep.dom.to_sympy(c)) for m, c in f.rep.terms(order=order)] 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()] 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,)] See Also ======== all_terms """ return f.rep.all_monoms() 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()] 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(k, coeff): ... k = k[0] ... 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) 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()) 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) 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() def as_expr(f, *gens): """ Convert a Poly instance to an Expr instance. 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) 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) 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) 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.symbols + f.gens else: gens = f.gens + dom.symbols return f.new(result, *gens) 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[k:], True elif f.gens[-k:] == gens: _gens, front = f.gens[:-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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) def rem(f, g, auto=True): """ Computes the polynomial remainder of ``f`` by ``g``. Examples ======== >>> from sympy import Poly >>> 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) 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) 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 f.gens.index(sympify(gen)) except ValueError: raise PolynomialError( "a valid generator expected, got %s" % gen) def degree(f, gen=0): """ Returns degree of ``f`` in ``x_j``. The degree of 0 is negative infinity. 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 >>> Poly(0, x).degree() -oo """ j = f._gen_to_level(gen) if hasattr(f.rep, 'degree'): return f.rep.degree(j) else: # pragma: no cover raise OperationNotSupported(f, 'degree') 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') 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') def homogenize(f, s): """ Returns the homogeneous polynomial of ``f``. A homogeneous polynomial is a polynomial whose all monomials with non-zero coefficients have the same total degree. If you only want to check if a polynomial is homogeneous, then use :func:`Poly.is_homogeneous`. 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, z >>> f = Poly(x**5 + 2*x**2*y**2 + 9*x*y**3) >>> f.homogenize(z) Poly(x**5 + 2*x**2*y**2*z + 9*x*y**3*z, x, y, z, domain='ZZ') """ if not isinstance(s, Symbol): raise TypeError("``Symbol`` expected, got %s" % type(s)) if s in f.gens: i = f.gens.index(s) gens = f.gens else: i = len(f.gens) gens = f.gens + (s,) if hasattr(f.rep, 'homogenize'): return f.per(f.rep.homogenize(i), gens=gens) raise OperationNotSupported(f, 'homogeneous_order') 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') 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) 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) 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') def coeff_monomial(f, monom): """ Returns the coefficient of ``monom`` in ``f`` if there, else None. Examples ======== >>> from sympy import Poly, exp >>> from sympy.abc import x, y >>> p = Poly(24*x*y*exp(8) + 23*x, x, y) >>> p.coeff_monomial(x) 23 >>> p.coeff_monomial(y) 0 >>> p.coeff_monomial(x*y) 24*exp(8) Note that ``Expr.coeff()`` behaves differently, collecting terms if possible; the Poly must be converted to an Expr to use that method, however: >>> p.as_expr().coeff(x) 24*y*exp(8) + 23 >>> p.as_expr().coeff(y) 24*x*exp(8) >>> p.as_expr().coeff(x*y) 24*exp(8) See Also ======== nth: more efficient query using exponents of the monomial's generators """ return f.nth(*Monomial(monom, f.gens).exponents) def nth(f, *N): """ Returns the ``n``-th coefficient of ``f`` where ``N`` are the exponents of the generators in the term of interest. Examples ======== >>> from sympy import Poly, sqrt >>> 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 >>> Poly(4*sqrt(x)*y) Poly(4*y*sqrt(x), y, sqrt(x), domain='ZZ') >>> _.nth(1, 1) 4 See Also ======== coeff_monomial """ 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) def coeff(f, x, n=1, right=False): # the semantics of coeff_monomial and Expr.coeff are different; # if someone is working with a Poly, they should be aware of the # differences and chose the method best suited for the query. # Alternatively, a pure-polys method could be written here but # at this time the ``right`` keyword would be ignored because Poly # doesn't work with non-commutatives. raise NotImplementedError( 'Either convert to Expr with `as_expr` method ' 'to use Expr\'s coeff method or else use the ' '`coeff_monomial` method of Polys.') 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) 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) 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 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 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) 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) 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 or not dom.has_assoc_Ring: return coeff, f else: return coeff, f.to_ring() 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 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') 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') 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: a_domain, [a] = construct_domain([a]) new_domain = f.get_domain().unify_with_symbols(a_domain, f.gens) f = f.set_domain(new_domain) a = new_domain.convert(a, a_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) 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) 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) 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) 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) 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)) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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)) 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) 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)) 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] 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') >>> 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) 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) 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] 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] 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] 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] def intervals(f, all=False, eps=None, inf=None, sup=None, fast=False, sqf=False): """ Compute isolating intervals for roots of ``f``. For real roots the Vincent-Akritas-Strzebonski (VAS) continued fractions method is used. References: =========== 1. Alkiviadis G. Akritas and Adam W. Strzebonski: A Comparative Study of Two Real Root Isolation Methods . Nonlinear Analysis: Modelling and Control, Vol. 10, No. 4, 297-304, 2005. 2. Alkiviadis G. Akritas, Adam W. Strzebonski and Panagiotis S. Vigklas: Improving the Performance of the Continued Fractions Method Using new Bounds of Positive Roots. Nonlinear Analysis: Modelling and Control, Vol. 13, No. 3, 265-279, 2008. 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)) 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) 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) 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) 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) 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) 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 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 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) 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 __nonzero__(f): return not f.is_zero __bool__ = __nonzero__ 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) @public 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 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 @public 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: opt.domain, coeffs = construct_domain(coeffs, opt=opt) else: coeffs = list(map(domain.from_sympy, coeffs)) rep = dict(list(zip(monoms, coeffs))) poly = Poly._from_dict(rep, opt) if opt.polys is None: opt.polys = False return poly, opt @public 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) for k in opt.gens: if isinstance(k, Piecewise): raise PolynomialError("Piecewise generators do not make sense") 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: opt.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 = [] for monoms, coeffs in zip(all_monoms, all_coeffs): rep = dict(list(zip(monoms, coeffs))) poly = Poly._from_dict(rep, opt) polys.append(poly) 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 @public def degree(f, *gens, **args): """ Return the degree of ``f`` in the given variable. The degree of 0 is negative infinity. 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 >>> degree(0, x) -oo """ 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 sympify(F.degree(opt.gen)) @public 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)) @public 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) @public 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() @public 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() @public 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 @public 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 @public 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 @public 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 @public 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 @public 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 @public 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 @public 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 @public 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 @public 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 @public 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 @public 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 @public 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 @public 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 @public 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 @public 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 @public 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 @public 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 @public 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 @public 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 >>> 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) @public 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 @public 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 @public 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() @public 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 >>> 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 @public 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 @public 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 @public 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 @public 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 @public def gff(f, *gens, **args): """Compute greatest factorial factorization of ``f``. """ raise NotImplementedError('symbolic falling factorial') @public 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 @public 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: if hasattr(expr,'_eval_factor'): return expr._eval_factor() 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) def to_rational_coeffs(f): """ try to transform a polynomial to have rational coefficients try to find a transformation ``x = alpha*y`` ``f(x) = lc*alpha**n * g(y)`` where ``g`` is a polynomial with rational coefficients, ``lc`` the leading coefficient. If this fails, try ``x = y + beta`` ``f(x) = g(y)`` Returns ``None`` if ``g`` not found; ``(lc, alpha, None, g)`` in case of rescaling ``(None, None, beta, g)`` in case of translation Notes ===== Currently it transforms only polynomials without roots larger than 2. Examples ======== >>> from sympy import sqrt, Poly, simplify >>> from sympy.polys.polytools import to_rational_coeffs >>> from sympy.abc import x >>> p = Poly(((x**2-1)*(x-2)).subs({x:x*(1 + sqrt(2))}), x, domain='EX') >>> lc, r, _, g = to_rational_coeffs(p) >>> lc, r (7 + 5*sqrt(2), -2*sqrt(2) + 2) >>> g Poly(x**3 + x**2 - 1/4*x - 1/4, x, domain='QQ') >>> r1 = simplify(1/r) >>> Poly(lc*r**3*(g.as_expr()).subs({x:x*r1}), x, domain='EX') == p True """ from sympy.simplify.simplify import simplify def _try_rescale(f): """ try rescaling ``x -> alpha*x`` to convert f to a polynomial with rational coefficients. Returns ``alpha, f``; if the rescaling is successful, ``alpha`` is the rescaling factor, and ``f`` is the rescaled polynomial; else ``alpha`` is ``None``. """ from sympy.core.add import Add if not len(f.gens) == 1 or not (f.gens[0]).is_Atom: return None, f n = f.degree() lc = f.LC() coeffs = f.monic().all_coeffs()[1:] coeffs = [simplify(coeffx) for coeffx in coeffs] if coeffs[-2] and not all(coeffx.is_rational for coeffx in coeffs): rescale1_x = simplify(coeffs[-2]/coeffs[-1]) coeffs1 = [] for i in range(len(coeffs)): coeffx = simplify(coeffs[i]*rescale1_x**(i + 1)) if not coeffx.is_rational: break coeffs1.append(coeffx) else: rescale_x = simplify(1/rescale1_x) x = f.gens[0] v = [x**n] for i in range(1, n + 1): v.append(coeffs1[i - 1]*x**(n - i)) f = Add(*v) f = Poly(f) return lc, rescale_x, f return None def _try_translate(f): """ try translating ``x -> x + alpha`` to convert f to a polynomial with rational coefficients. Returns ``alpha, f``; if the translating is successful, ``alpha`` is the translating factor, and ``f`` is the shifted polynomial; else ``alpha`` is ``None``. """ from sympy.core.add import Add if not len(f.gens) == 1 or not (f.gens[0]).is_Atom: return None, f n = f.degree() f1 = f.monic() coeffs = f1.all_coeffs()[1:] c = simplify(coeffs[0]) if c and not c.is_rational: func = Add if c.is_Add: args = c.args func = c.func else: args = [c] sifted = sift(args, lambda z: z.is_rational) c1, c2 = sifted[True], sifted[False] alpha = -func(*c2)/n f2 = f1.shift(alpha) return alpha, f2 return None def _has_square_roots(p): """ Return True if ``f`` is a sum with square roots but no other root """ from sympy.core.exprtools import Factors coeffs = p.coeffs() has_sq = False for y in coeffs: for x in Add.make_args(y): f = Factors(x).factors r = [wx.q for wx in f.values() if wx.is_Rational and wx.q >= 2] if not r: continue if min(r) == 2: has_sq = True if max(r) > 2: return False return has_sq if f.get_domain().is_EX and _has_square_roots(f): r = _try_rescale(f) if r: return r[0], r[1], None, r[2] else: r = _try_translate(f) if r: return None, None, r[0], r[1] return None def _torational_factor_list(p, x): """ helper function to factor polynomial using to_rational_coeffs Examples ======== >>> from sympy.polys.polytools import _torational_factor_list >>> from sympy.abc import x >>> from sympy import sqrt, expand, Mul >>> p = expand(((x**2-1)*(x-2)).subs({x:x*(1 + sqrt(2))})) >>> factors = _torational_factor_list(p, x); factors (-2, [(-x*(1 + sqrt(2))/2 + 1, 1), (-x*(1 + sqrt(2)) - 1, 1), (-x*(1 + sqrt(2)) + 1, 1)]) >>> expand(factors[0]*Mul(*[z[0] for z in factors[1]])) == p True >>> p = expand(((x**2-1)*(x-2)).subs({x:x + sqrt(2)})) >>> factors = _torational_factor_list(p, x); factors (1, [(x - 2 + sqrt(2), 1), (x - 1 + sqrt(2), 1), (x + 1 + sqrt(2), 1)]) >>> expand(factors[0]*Mul(*[z[0] for z in factors[1]])) == p True """ from sympy.simplify.simplify import simplify p1 = Poly(p, x, domain='EX') n = p1.degree() res = to_rational_coeffs(p1) if not res: return None lc, r, t, g = res factors = factor_list(g.as_expr()) if lc: c = simplify(factors[0]*lc*r**n) r1 = simplify(1/r) a = [] for z in factors[1:][0]: a.append((simplify(z[0].subs({x: x*r1})), z[1])) else: c = factors[0] a = [] for z in factors[1:][0]: a.append((z[0].subs({x: x - t}), z[1])) return (c, a) @public 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') @public 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') @public 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') @public def factor(f, *gens, **args): """ Compute the factorization of expression, ``f``, into irreducibles. (To factor an integer into primes, use ``factorint``.) 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) By default, factor deals with an expression as a whole: >>> eq = 2**(x**2 + 2*x + 1) >>> factor(eq) 2**(x**2 + 2*x + 1) If the ``deep`` flag is True then subexpressions will be factored: >>> factor(eq, deep=True) 2**((x + 1)**2) See Also ======== sympy.ntheory.factor_.factorint """ f = sympify(f) if args.pop('deep', False): partials = {} muladd = f.atoms(Mul, Add) for p in muladd: fac = factor(p, *gens, **args) if (fac.is_Mul or fac.is_Pow) and fac != p: partials[p] = fac return f.xreplace(partials) 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) @public 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 @public 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) @public 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) @public 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) @public 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) @public 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() @public 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 = 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 @public def cancel(f, *gens, **args): """ Cancel common factors in a rational function ``f``. Examples ======== >>> from sympy import cancel, sqrt, Symbol >>> from sympy.abc import x >>> A = Symbol('A', commutative=False) >>> cancel((2*x**2 - 2)/(x**2 - 2*x + 1)) (2*x + 2)/(x - 1) >>> cancel((sqrt(3) + sqrt(15)*A)/(sqrt(2) + sqrt(10)*A)) sqrt(6)/2 """ from sympy.core.exprtools import factor_terms options.allowed_flags(args, ['polys']) f = sympify(f) if not isinstance(f, (tuple, Tuple)): if f.is_Number or isinstance(f, Relational) or not isinstance(f, Expr): return f f = factor_terms(f, radical=True) p, q = f.as_numer_denom() elif len(f) == 2: p, q = f elif isinstance(f, Tuple): return factor_terms(f) else: raise ValueError('unexpected argument: %s' % 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 except PolynomialError as msg: if f.is_commutative and not f.has(Piecewise): raise PolynomialError(msg) # Handling of noncommutative and/or piecewise expressions if f.is_Add or f.is_Mul: sifted = sift(f.args, lambda x: x.is_commutative and not x.has(Piecewise)) c, nc = sifted[True], sifted[False] nc = [cancel(i) for i in nc] return f.func(cancel(f.func._from_args(c)), *nc) else: reps = [] pot = preorder_traversal(f) next(pot) for e in pot: # XXX: This should really skip anything that's not Expr. if isinstance(e, (tuple, Tuple, BooleanAtom)): continue try: reps.append((e, cancel(e))) pot.skip() # this was handled successfully except NotImplementedError: pass return f.xreplace(dict(reps)) 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 @public 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 from sympy.polys.rings import xring _ring, _ = xring(opt.gens, opt.domain, opt.order) for i, poly in enumerate(polys): poly = poly.set_domain(opt.domain).rep.to_dict() polys[i] = _ring.from_dict(poly) Q, r = polys[0].div(polys[1:]) 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 @public 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) @public 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 @public class GroebnerBasis(Basic): """Represents a reduced Groebner basis. """ 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) from sympy.polys.rings import PolyRing ring = PolyRing(opt.gens, opt.domain, opt.order) for i, poly in enumerate(polys): polys[i] = ring.from_dict(poly.rep.to_dict()) G = _groebner(polys, ring, method=opt.method) G = [Poly._from_dict(g, opt) for g in G] 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 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) 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, )) from sympy.polys.rings import xring _ring, _ = xring(opt.gens, opt.domain, src_order) for i, poly in enumerate(polys): poly = poly.set_domain(opt.domain).rep.to_dict() polys[i] = _ring.from_dict(poly) G = matrix_fglm(polys, _ring, dst_order) 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) 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 from sympy.polys.rings import xring _ring, _ = xring(opt.gens, opt.domain, opt.order) for i, poly in enumerate(polys): poly = poly.set_domain(opt.domain).rep.to_dict() polys[i] = _ring.from_dict(poly) Q, r = polys[0].div(polys[1:]) 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 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 @public 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) from sympy.functions import Piecewise sympy-0.7.4.1/sympy/polys/monomials.py0000644000175000017500000003406512253362407020153 0ustar georgeskgeorgesk"""Tools and arithmetics for monomials of distributed polynomials. """ from __future__ import print_function, division from textwrap import dedent from sympy.core import S, C, Symbol, Mul, Tuple, Expr, sympify from sympy.core.compatibility import exec_, iterable, xrange from sympy.polys.polyutils import PicklableWithSlots, dict_from_expr from sympy.polys.polyerrors import ExactQuotientFailed from sympy.utilities import public @public def itermonomials(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.polys.monomials import itermonomials >>> from sympy.polys.orderings import monomial_key >>> from sympy.abc import x, y >>> sorted(itermonomials([x, y], 2), key=monomial_key('grlex', [y, x])) [1, x, y, x**2, x*y, y**2] >>> sorted(itermonomials([x, y], 3), key=monomial_key('grlex', [y, x])) [1, x, y, x**2, x*y, y**2, x**3, x**2*y, x*y**2, y**3] """ if not variables: return set([S.One]) else: x, tail = variables[0], variables[1:] monoms = itermonomials(tail, degree) for i in range(1, degree + 1): monoms |= set([ x**i * m for m in itermonomials(tail, degree - i) ]) return monoms 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.polys.monomials import itermonomials, monomial_count >>> from sympy.polys.orderings import monomial_key >>> from sympy.abc import x, y >>> monomial_count(2, 2) 6 >>> M = itermonomials([x, y], 2) >>> sorted(M, key=monomial_key('grlex', [y, x])) [1, x, y, x**2, x*y, y**2] >>> len(M) 6 """ return C.factorial(V + N) / C.factorial(V) / C.factorial(N) def monomial_mul(A, B): """ Multiplication of tuples representing monomials. Lets multiply `x**3*y**4*z` with `x*y**2`:: >>> from sympy.polys.monomials 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) ]) def monomial_div(A, B): """ Division of tuples representing monomials. Lets divide `x**3*y**4*z` by `x*y**2`:: >>> from sympy.polys.monomials 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 = monomial_ldiv(A, B) if all(c >= 0 for c in C): return tuple(C) else: return None def monomial_ldiv(A, B): """ Division of tuples representing monomials. Lets divide `x**3*y**4*z` by `x*y**2`:: >>> from sympy.polys.monomials import monomial_ldiv >>> monomial_ldiv((3, 4, 1), (1, 2, 0)) (2, 2, 1) which gives `x**2*y**2*z`. >>> monomial_ldiv((3, 4, 1), (1, 2, 2)) (2, 2, -1) which gives `x**2*y**2*z**-1`. """ return tuple([ a - b for a, b in zip(A, B) ]) def monomial_pow(A, n): """Return the n-th pow of the monomial. """ return tuple([ a*n for a in A ]) 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.monomials 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) ]) 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.monomials 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) ]) def monomial_divides(A, B): """ Does there exist a monomial X such that XA == B? >>> from sympy.polys.monomials 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)) 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.monomials 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) 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.monomials 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.monomials import monomial_deg >>> monomial_deg((1, 2)) 3 """ return sum(M) def term_div(a, b, domain): """Division of two terms in over a ring/field. """ a_lm, a_lc = a b_lm, b_lc = b monom = monomial_div(a_lm, b_lm) if domain.has_Field: if monom is not None: return monom, domain.quo(a_lc, b_lc) else: return None else: if not (monom is None or a_lc % b_lc): return monom, domain.quo(a_lc, b_lc) else: return None class MonomialOps(object): """Code generator of fast monomial arithmetic functions. """ def __init__(self, ngens): self.ngens = ngens def _build(self, code, name): ns = {} exec_(code, ns) return ns[name] def _vars(self, name): return [ "%s%s" % (name, i) for i in xrange(self.ngens) ] def mul(self): name = "monomial_mul" template = dedent("""\ def %(name)s(A, B): (%(A)s,) = A (%(B)s,) = B return (%(AB)s,) """) A = self._vars("a") B = self._vars("b") AB = [ "%s + %s" % (a, b) for a, b in zip(A, B) ] code = template % dict(name=name, A=", ".join(A), B=", ".join(B), AB=", ".join(AB)) return self._build(code, name) def pow(self): name = "monomial_pow" template = dedent("""\ def %(name)s(A, k): (%(A)s,) = A return (%(Ak)s,) """) A = self._vars("a") Ak = [ "%s*k" % a for a in A ] code = template % dict(name=name, A=", ".join(A), Ak=", ".join(Ak)) return self._build(code, name) def mulpow(self): name = "monomial_mulpow" template = dedent("""\ def %(name)s(A, B, k): (%(A)s,) = A (%(B)s,) = B return (%(ABk)s,) """) A = self._vars("a") B = self._vars("b") ABk = [ "%s + %s*k" % (a, b) for a, b in zip(A, B) ] code = template % dict(name=name, A=", ".join(A), B=", ".join(B), ABk=", ".join(ABk)) return self._build(code, name) def ldiv(self): name = "monomial_ldiv" template = dedent("""\ def %(name)s(A, B): (%(A)s,) = A (%(B)s,) = B return (%(AB)s,) """) A = self._vars("a") B = self._vars("b") AB = [ "%s - %s" % (a, b) for a, b in zip(A, B) ] code = template % dict(name=name, A=", ".join(A), B=", ".join(B), AB=", ".join(AB)) return self._build(code, name) def div(self): name = "monomial_div" template = dedent("""\ def %(name)s(A, B): (%(A)s,) = A (%(B)s,) = B %(RAB)s return (%(R)s,) """) A = self._vars("a") B = self._vars("b") RAB = [ "r%(i)s = a%(i)s - b%(i)s\n if r%(i)s < 0: return None" % dict(i=i) for i in xrange(self.ngens) ] R = self._vars("r") code = template % dict(name=name, A=", ".join(A), B=", ".join(B), RAB="\n ".join(RAB), R=", ".join(R)) return self._build(code, name) def lcm(self): name = "monomial_lcm" template = dedent("""\ def %(name)s(A, B): (%(A)s,) = A (%(B)s,) = B return (%(AB)s,) """) A = self._vars("a") B = self._vars("b") AB = [ "%s if %s >= %s else %s" % (a, a, b, b) for a, b in zip(A, B) ] code = template % dict(name=name, A=", ".join(A), B=", ".join(B), AB=", ".join(AB)) return self._build(code, name) def gcd(self): name = "monomial_gcd" template = dedent("""\ def %(name)s(A, B): (%(A)s,) = A (%(B)s,) = B return (%(AB)s,) """) A = self._vars("a") B = self._vars("b") AB = [ "%s if %s <= %s else %s" % (a, a, b, b) for a, b in zip(A, B) ] code = template % dict(name=name, A=", ".join(A), B=", ".join(B), AB=", ".join(AB)) return self._build(code, name) @public class Monomial(PicklableWithSlots): """Class representing a monomial, i.e. a product of powers. """ __slots__ = ['exponents', 'gens'] def __init__(self, monom, gens=None): if not iterable(monom): rep, gens = dict_from_expr(sympify(monom), gens=gens) if len(rep) == 1 and list(rep.values())[0] == 1: monom = list(rep.keys())[0] else: raise ValueError("Expected a monomial got %s" % monom) self.exponents = tuple(map(int, monom)) 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 xrange(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)) sympy-0.7.4.1/sympy/polys/specialpolys.py0000644000175000017500000002312212253362407020654 0ustar georgeskgeorgesk"""Functions for generating interesting polynomials, e.g. for benchmarking. """ from __future__ import print_function, division 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 subsets, public from sympy.core.compatibility import xrange @public def swinnerton_dyer_poly(n, x=None, **args): """Generates n-th Swinnerton-Dyer polynomial in `x`. """ from .numberfields import minimal_polynomial if n <= 0: raise ValueError( "can't generate Swinnerton-Dyer polynomial of order %s" % n) if x is not None: sympify(x) else: x = Dummy('x') if n > 3: p = 2 a = [sqrt(2)] for i in xrange(2, n + 1): p = nextprime(p) a.append(sqrt(p)) return minimal_polynomial(Add(*a), x, polys=args.get('polys', False)) if n == 1: ex = x**2 - 2 elif n == 2: ex = x**4 - 10*x**2 + 1 elif n == 3: ex = x**8 - 40*x**6 + 352*x**4 - 960*x**2 + 576 if not args.get('polys', False): return ex else: return PurePoly(ex, x) @public 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 @public 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) @public 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 @public 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 xrange(0, n): numer = [] denom = [] for j in xrange(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) ]) def fateman_poly_F_1(n): """Fateman's GCD benchmark: trivial GCD """ Y = [ Symbol('y_' + str(i)) for i in xrange(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 def dmp_fateman_poly_F_1(n, K): """Fateman's GCD benchmark: trivial GCD """ u = [K(1), K(0)] for i in xrange(0, n): u = [dmp_one(i, K), u] v = [K(1), K(0), K(0)] for i in xrange(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 def fateman_poly_F_2(n): """Fateman's GCD benchmark: linearly dense quartic inputs """ Y = [ Symbol('y_' + str(i)) for i in xrange(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 def dmp_fateman_poly_F_2(n, K): """Fateman's GCD benchmark: linearly dense quartic inputs """ u = [K(1), K(0)] for i in xrange(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 def fateman_poly_F_3(n): """Fateman's GCD benchmark: sparse inputs (deg f ~ vars f) """ Y = [ Symbol('y_' + str(i)) for i in xrange(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 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 xrange(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). from sympy.polys.rings import ring def _f_0(): R, x, y, z = ring("x,y,z", ZZ) return x**2*y*z**2 + 2*x**2*y*z + 3*x**2*y + 2*x**2 + 3*x + 4*y**2*z**2 + 5*y**2*z + 6*y**2 + y*z**2 + 2*y*z + y + 1 def _f_1(): R, x, y, z = ring("x,y,z", ZZ) return x**3*y*z + x**2*y**2*z**2 + x**2*y**2 + 20*x**2*y*z + 30*x**2*y + x**2*z**2 + 10*x**2*z + x*y**3*z + 30*x*y**2*z + 20*x*y**2 + x*y*z**3 + 10*x*y*z**2 + x*y*z + 610*x*y + 20*x*z**2 + 230*x*z + 300*x + y**2*z**2 + 10*y**2*z + 30*y*z**2 + 320*y*z + 200*y + 600*z + 6000 def _f_2(): R, x, y, z = ring("x,y,z", ZZ) return x**5*y**3 + x**5*y**2*z + x**5*y*z**2 + x**5*z**3 + x**3*y**2 + x**3*y*z + 90*x**3*y + 90*x**3*z + x**2*y**2*z - 11*x**2*y**2 + x**2*z**3 - 11*x**2*z**2 + y*z - 11*y + 90*z - 990 def _f_3(): R, x, y, z = ring("x,y,z", ZZ) return x**5*y**2 + x**4*z**4 + x**4 + x**3*y**3*z + x**3*z + x**2*y**4 + x**2*y**3*z**3 + x**2*y*z**5 + x**2*y*z + x*y**2*z**4 + x*y**2 + x*y*z**7 + x*y*z**3 + x*y*z**2 + y**2*z + y*z**4 def _f_4(): R, x, y, z = ring("x,y,z", ZZ) return -x**9*y**8*z - x**8*y**5*z**3 - x**7*y**12*z**2 - 5*x**7*y**8 - x**6*y**9*z**4 + x**6*y**7*z**3 + 3*x**6*y**7*z - 5*x**6*y**5*z**2 - x**6*y**4*z**3 + x**5*y**4*z**5 + 3*x**5*y**4*z**3 - x**5*y*z**5 + x**4*y**11*z**4 + 3*x**4*y**11*z**2 - x**4*y**8*z**4 + 5*x**4*y**7*z**2 + 15*x**4*y**7 - 5*x**4*y**4*z**2 + x**3*y**8*z**6 + 3*x**3*y**8*z**4 - x**3*y**5*z**6 + 5*x**3*y**4*z**4 + 15*x**3*y**4*z**2 + x**3*y**3*z**5 + 3*x**3*y**3*z**3 - 5*x**3*y*z**4 + x**2*z**7 + 3*x**2*z**5 + x*y**7*z**6 + 3*x*y**7*z**4 + 5*x*y**3*z**4 + 15*x*y**3*z**2 + y**4*z**8 + 3*y**4*z**6 + 5*z**6 + 15*z**4 def _f_5(): R, x, y, z = ring("x,y,z", ZZ) return -x**3 - 3*x**2*y + 3*x**2*z - 3*x*y**2 + 6*x*y*z - 3*x*z**2 - y**3 + 3*y**2*z - 3*y*z**2 + z**3 def _f_6(): R, x, y, z, t = ring("x,y,z,t", ZZ) return 2115*x**4*y + 45*x**3*z**3*t**2 - 45*x**3*t**2 - 423*x*y**4 - 47*x*y**3 + 141*x*y*z**3 + 94*x*y*z*t - 9*y**3*z**3*t**2 + 9*y**3*t**2 - y**2*z**3*t**2 + y**2*t**2 + 3*z**6*t**2 + 2*z**4*t**3 - 3*z**3*t**2 - 2*z*t**3 def _w_1(): R, x, y, z = ring("x,y,z", ZZ) return 4*x**6*y**4*z**2 + 4*x**6*y**3*z**3 - 4*x**6*y**2*z**4 - 4*x**6*y*z**5 + x**5*y**4*z**3 + 12*x**5*y**3*z - x**5*y**2*z**5 + 12*x**5*y**2*z**2 - 12*x**5*y*z**3 - 12*x**5*z**4 + 8*x**4*y**4 + 6*x**4*y**3*z**2 + 8*x**4*y**3*z - 4*x**4*y**2*z**4 + 4*x**4*y**2*z**3 - 8*x**4*y**2*z**2 - 4*x**4*y*z**5 - 2*x**4*y*z**4 - 8*x**4*y*z**3 + 2*x**3*y**4*z + x**3*y**3*z**3 - x**3*y**2*z**5 - 2*x**3*y**2*z**3 + 9*x**3*y**2*z - 12*x**3*y*z**3 + 12*x**3*y*z**2 - 12*x**3*z**4 + 3*x**3*z**3 + 6*x**2*y**3 - 6*x**2*y**2*z**2 + 8*x**2*y**2*z - 2*x**2*y*z**4 - 8*x**2*y*z**3 + 2*x**2*y*z**2 + 2*x*y**3*z - 2*x*y**2*z**3 - 3*x*y*z + 3*x*z**3 - 2*y**2 + 2*y*z**2 def _w_2(): R, x, y = ring("x,y", ZZ) return 24*x**8*y**3 + 48*x**8*y**2 + 24*x**7*y**5 - 72*x**7*y**2 + 25*x**6*y**4 + 2*x**6*y**3 + 4*x**6*y + 8*x**6 + x**5*y**6 + x**5*y**3 - 12*x**5 + x**4*y**5 - x**4*y**4 - 2*x**4*y**3 + 292*x**4*y**2 - x**3*y**6 + 3*x**3*y**3 - x**2*y**5 + 12*x**2*y**3 + 48*x**2 - 12*y**3 def f_polys(): return _f_0(), _f_1(), _f_2(), _f_3(), _f_4(), _f_5(), _f_6() def w_polys(): return _w_1(), _w_2() sympy-0.7.4.1/sympy/polys/polycontext.py0000644000175000017500000000362412253362407020542 0ustar georgeskgeorgesk"""Tools for managing evaluation contexts. """ from __future__ import print_function, division from sympy.utilities.iterables import dict_merge from sympy.polys.polyutils import PicklableWithSlots __known_options__ = set(['frac', 'gens', 'wrt', 'sort', 'order', 'domain', 'modulus', 'gaussian', 'extension', 'field', 'greedy', 'symmetric']) __global_options__ = [] __template__ = """\ def %(option)s(_%(option)s): return Context(%(option)s=_%(option)s) """ for option in __known_options__: exec(__template__ % { 'option': option }) class Context(PicklableWithSlots): __slots__ = ['__options__'] def __init__(self, dict=None, **options): if dict is not None: self.__options__ = dict_merge(dict, options) else: self.__options__ = options def __getattribute__(self, name): if name in __known_options__: try: return object.__getattribute__(self, '__options__')[name] except KeyError: return None else: return object.__getattribute__(self, name) def __str__(self): return 'Context(%s)' % ', '.join( [ '%s=%r' % (key, value) for key, value in self.__options__.items() ]) def __and__(self, other): if isinstance(other, Context): return Context(**dict_merge(self.__options__, other.__options__)) else: raise TypeError("a context manager expected, got %s" % other) def __enter__(self): raise NotImplementedError('global context') def __exit__(self, exc_type, exc_val, exc_tb): raise NotImplementedError('global context') def register_context(func): def wrapper(self, *args, **kwargs): return func(*args, **dict_merge(self.__options__, kwargs)) wrapper.__doc__ = func.__doc__ wrapper.__name__ = func.__name__ setattr(Context, func.__name__, wrapper) return func sympy-0.7.4.1/sympy/polys/galoistools.py0000644000175000017500000014535512253362407020521 0ustar georgeskgeorgesk"""Dense univariate polynomials with coefficients in Galois fields. """ from __future__ import print_function, division from random import uniform from math import ceil as _ceil, sqrt as _sqrt from sympy.core.compatibility import SYMPY_INTS, xrange 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.ntheory import factorint 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 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 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 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 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 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] 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] 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:] 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 ]) 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) 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 isinstance(n, SYMPY_INTS): for k in xrange(n, -1, -1): h.append(f.get(k, K.zero) % p) else: (n,) = n for k in xrange(n, -1, -1): h.append(f.get((k,), K.zero) % p) return gf_trunc(h, p) 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 xrange(0, n + 1): if symmetric: a = gf_int(f[n - k], p) else: a = f[n - k] if a: result[k] = a return result 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) 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 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 ] 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] 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] 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 ] 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) 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) ] 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) ] 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 xrange(0, dh + 1): coeff = K.zero for j in xrange(max(0, i - dg), min(i, df) + 1): coeff += f[j]*g[i - j] h[i] = coeff % p return gf_strip(h) 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 xrange(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 xrange(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) 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) 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) 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 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 xrange(0, df + 1): coeff = h[i] for j in xrange(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:]) 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] 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 xrange(0, dq + 1): coeff = h[i] for j in xrange(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] 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) 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 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:] 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 def gf_frobenius_monomial_base(g, p, K): """ return the list of ``x**(i*p) mod g in Z_p`` for ``i = 0, .., n - 1`` where ``n = gf_degree(g)`` Examples ======== >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_frobenius_monomial_base >>> g = ZZ.map([1, 0, 2, 1]) >>> gf_frobenius_monomial_base(g, 5, ZZ) [[1], [4, 4, 2], [1, 2]] """ n = gf_degree(g) if n == 0: return [] b = [0]*n b[0] = [1] if p < n: for i in range(1, n): mon = gf_lshift(b[i - 1], p, K) b[i] = gf_rem(mon, g, p, K) elif n > 1: b[1] = gf_pow_mod([K.one, K.zero], p, g, p, K) for i in range(2, n): b[i] = gf_mul(b[i - 1], b[1], p, K) b[i] = gf_rem(b[i], g, p, K) return b def gf_frobenius_map(f, g, b, p, K): """ compute gf_pow_mod(f, p, g, p, K) using the Frobenius map Parameters ========== f, g : polynomials in ``GF(p)[x]`` b : frobenius monomial base p : prime number K : domain Examples ======== >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_frobenius_monomial_base, gf_frobenius_map >>> f = ZZ.map([2, 1 , 0, 1]) >>> g = ZZ.map([1, 0, 2, 1]) >>> p = 5 >>> b = gf_frobenius_monomial_base(g, p, ZZ) >>> r = gf_frobenius_map(f, g, b, p, ZZ) >>> gf_frobenius_map(f, g, b, p, ZZ) [4, 0, 3] """ m = gf_degree(g) if gf_degree(f) >= m: f = gf_rem(f, g, p, K) if not f: return [] n = gf_degree(f) sf = [f[-1]] for i in range(1, n + 1): v = gf_mul_ground(b[i], f[n - i], p, K) sf = gf_add(sf, v, p, K) return sf def _gf_pow_pnm1d2(f, n, g, b, p, K): """ utility function for ``gf_edf_zassenhaus`` Compute ``f**((p**n - 1) // 2)`` in ``GF(p)[x]/(g)`` ``f**((p**n - 1) // 2) = (f*f**p*...*f**(p**n - 1))**((p - 1) // 2)`` """ f = gf_rem(f, g, p, K) h = f r = f for i in range(1, n): h = gf_frobenius_map(h, g, b, p, K) r = gf_mul(r, h, p, K) r = gf_rem(r, g, p, K) res = gf_pow_mod(r, (p - 1)//2, g, p, K) return res 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 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] 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] 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)) 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 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) 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) 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 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 ] 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 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 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 def _gf_trace_map(f, n, g, b, p, K): """ utility for ``gf_edf_shoup`` """ f = gf_rem(f, g, p, K) h = f r = f for i in range(1, n): h = gf_frobenius_map(h, g, b, p, K) r = gf_add(r, h, p, K) r = gf_rem(r, g, p, K) return r 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 xrange(0, n) ] 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 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) if n < 5: H = h = gf_pow_mod([K.one, K.zero], p, f, p, K) for i in xrange(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 else: b = gf_frobenius_monomial_base(f, p, K) H = h = gf_frobenius_map([K.one, K.zero], f, b, p, K) for i in xrange(0, n//2): g = gf_sub(h, [K.one, K.zero], p, K) if gf_gcd(f, g, p, K) == [K.one]: h = gf_frobenius_map(h, f, b, p, K) else: return False return True 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] indices = set([ n//d for d in factorint(n) ]) b = gf_frobenius_monomial_base(f, p, K) h = b[1] for i in xrange(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_frobenius_map(h, f, b, p, K) return h == x _irred_methods = { 'ben-or': gf_irred_p_ben_or, 'rabin': gf_irred_p_rabin, } 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 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] 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 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 xrange(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 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 xrange(1, (n - 1)*r + 1): qq, c = [(-q[-1]*f[-1]) % p], q[-1] for j in xrange(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 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 xrange(0, n): Q[k][k] = (Q[k][k] - K.one) % p for k in xrange(0, n): for i in xrange(k, n): if Q[k][i]: break else: continue inv = K.invert(Q[k][i], p) for j in xrange(0, n): Q[j][i] = (Q[j][i]*inv) % p for j in xrange(0, n): t = Q[j][k] Q[j][k] = Q[j][i] Q[j][i] = t for i in xrange(0, n): if i != k: q = Q[k][i] for j in xrange(0, n): Q[j][i] = (Q[j][i] - Q[j][k]*q) % p for i in xrange(0, n): for j in xrange(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 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 xrange(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) 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], [] b = gf_frobenius_monomial_base(f, p, K) while 2*i <= gf_degree(f): g = gf_frobenius_map(g, f, b, 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) b = gf_frobenius_monomial_base(f, p, K) i += 1 if f != [K.one]: return factors + [(f, gf_degree(f))] else: return factors 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 if p != 2: b = gf_frobenius_monomial_base(f, p, K) while len(factors) < N: r = gf_random(2*n - 1, p, K) if p == 2: h = r for i in xrange(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_pnm1d2(r, n, f, b, 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) 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))) b = gf_frobenius_monomial_base(f, p, K) h = gf_frobenius_map([K.one, K.zero], f, b, p, K) # U[i] = x**(p**i) U = [[K.one, K.zero], h] + [K.zero]*(k - 1) for i in xrange(2, k + 1): U[i] = gf_frobenius_map(U[i-1], f, b, p, K) h, U = U[k], U[:k] # V[i] = x**(p**(k*(i+1))) V = [h] + [K.zero]*(k - 1) for i in xrange(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 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) if p == 2: h = gf_pow_mod(x, q, f, p, K) H = gf_trace_map(r, h, x, n - 1, f, p, K)[1] 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: b = gf_frobenius_monomial_base(f, p, K) H = _gf_trace_map(r, n, f, b, p, K) 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) 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) 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 } 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 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) 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) 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]) sympy-0.7.4.1/sympy/polys/densebasic.py0000644000175000017500000010630112253362407020246 0ustar georgeskgeorgesk"""Basic tools for dense recursive polynomials in ``K[x]`` or ``K[X]``. """ from __future__ import print_function, division from sympy.core import igcd from sympy import oo from sympy.polys.monomials import monomial_min, monomial_div from sympy.polys.orderings import monomial_key from sympy.core.compatibility import xrange 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 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) 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) 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]``. Note that the degree of 0 is negative infinity (the SymPy object -oo). 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 """ if not f: return -oo return len(f) - 1 def dmp_degree(f, u): """ Return the leading degree of ``f`` in ``x_0`` in ``K[X]``. Note that the degree of 0 is negative infinity (the SymPy object -oo). Examples ======== >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_degree >>> dmp_degree([[[]]], 2) -oo >>> f = ZZ.map([[2], [1, 2, 3]]) >>> dmp_degree(f, 1) 1 """ if dmp_zero_p(f, u): return -oo else: return len(f) - 1 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 ]) 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) 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) 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 = [-oo]*(u + 1) _rec_degree_list(f, u, 0, degs) return tuple(degs) 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:] 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:] 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 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) 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") 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) 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) 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 ]) 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.rings import ring >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dup_convert >>> R, x = ring("x", ZZ) >>> dup_convert([R(1), R(2)], R.to_domain(), ZZ) [1, 2] >>> dup_convert([ZZ(1), ZZ(2)], ZZ, R.to_domain()) [1, 2] """ if K0 is not None and K0 == K1: return f else: return dup_strip([ K1.convert(c, K0) for c in f ]) def dmp_convert(f, u, K0, K1): """ Convert the ground domain of ``f`` from ``K0`` to ``K1``. Examples ======== >>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_convert >>> R, x = ring("x", ZZ) >>> dmp_convert([[R(1)], [R(2)]], 1, R.to_domain(), ZZ) [[1], [2]] >>> dmp_convert([[ZZ(1)], [ZZ(2)]], 1, ZZ, R.to_domain()) [[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 ]) 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) 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] 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] 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: d = dmp_degree(f, v) if d == -oo: d = -1 f, v = f[d - n], v - 1 return f 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 def dmp_zero(u): """ Return a multivariate zero. Examples ======== >>> from sympy.polys.densebasic import dmp_zero >>> dmp_zero(4) [[[[[]]]]] """ r = [] for i in xrange(u): r = [r] return r 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) 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) 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] 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 xrange(u + 1): c = [c] return c 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 xrange(n) ] 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 xrange(n) ] 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)) 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)) 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 xrange(n, -1, -1): h.append(f.get(k, K.zero)) else: (n,) = n for k in xrange(n, -1, -1): h.append(f.get((k,), K.zero)) return dup_strip(h) 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 xrange(n, -1, -1): h.append(f.get(k, K.zero)) return dup_strip(h) 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 xrange(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) 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 = len(f) - 1, {} for k in xrange(0, n + 1): if f[n - k]: result[(k,)] = f[n - k] return result 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 = len(f) - 1, {} for k in xrange(0, n + 1): if f[n - k]: result[k] = f[n - k] return result 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, {} if n == -oo: n = -1 for k in xrange(0, n + 1): h = dmp_to_dict(f[n - k], v) for exp, coeff in h.items(): result[(k,) + exp] = coeff return result 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) 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) 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 xrange(l): f = [f] return f 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 ] 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 xrange(len(f)): if not f[-i - 1]: continue g = igcd(g, i) if g == 1: return 1, f return g, f[::g] 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) 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 xrange(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 ]) 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) 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 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 xrange(1, M[i]): result.append(dmp_zero(w)) result.append(coeff) return result 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) 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 xrange(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 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) def dmp_inject(f, u, K, front=False): """ Convert ``f`` from ``K[X][Y]`` to ``K[X,Y]``. Examples ======== >>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_inject >>> R, x,y = ring("x,y", ZZ) >>> dmp_inject([R(1), x + 2], 0, R.to_domain()) ([[[1]], [[1], [2]]], 2) >>> dmp_inject([R(1), x + 2], 0, R.to_domain(), front=True) ([[[1]], [[1, 2]]], 2) """ f, h = dmp_to_dict(f, u), {} v = K.ngens - 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 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 >>> dmp_eject([[[1]], [[1], [2]]], 2, ZZ['x', 'y']) [1, x + 2] """ f, h = dmp_to_dict(f, u), {} n = K.ngens v = u - K.ngens + 1 for monom, c in f.items(): if front: g_monom, f_monom = monom[:n], monom[n:] else: g_monom, f_monom = monom[-n:], monom[:-n] 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) 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] 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) 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 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)] """ def sort(terms, O): return sorted(terms, key=lambda term: O(term[0]), reverse=True) terms = _rec_list_terms(f, u, ()) if not terms: return [((0,)*(u + 1), K.zero)] if order is None: return terms else: return sort(terms, monomial_key(order)) 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) 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) 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 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) 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) 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 xrange(0, n + 1) ] while not f[0]: f[0] = K.convert(random.randint(a, b)) return f sympy-0.7.4.1/sympy/polys/orderings.py0000644000175000017500000002046212253362407020145 0ustar georgeskgeorgesk"""Definitions of monomial orderings. """ from __future__ import print_function, division __all__ = ["lex", "grlex", "grevlex", "ilex", "igrlex", "igrevlex"] from sympy.core import Symbol from sympy.core.compatibility import iterable class MonomialOrder(object): """Base class for monomial orderings. """ alias = None is_global = None is_default = False def __repr__(self): return self.__class__.__name__ + "()" def __str__(self): return self.alias def __call__(self, monomial): raise NotImplementedError def __eq__(self, other): return self.__class__ == other.__class__ def __hash__(self): return hash(self.__class__) def __ne__(self, other): return not (self == other) class LexOrder(MonomialOrder): """Lexicographic order of monomials. """ alias = 'lex' is_global = True is_default = True def __call__(self, monomial): return monomial class GradedLexOrder(MonomialOrder): """Graded lexicographic order of monomials. """ alias = 'grlex' is_global = True def __call__(self, monomial): return (sum(monomial), monomial) class ReversedGradedLexOrder(MonomialOrder): """Reversed graded lexicographic order of monomials. """ alias = 'grevlex' is_global = True def __call__(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.orderings 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 __call__(self, monomial): return tuple(O(lamda(monomial)) for (O, lamda) in self.args) def __repr__(self): from sympy.core import Tuple return self.__class__.__name__ + repr(Tuple(*[x[0] for x in self.args])) def __str__(self): from sympy.core import Tuple return self.__class__.__name__ + 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.orderings 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 __call__(self, monomial): def inv(l): if iterable(l): return tuple(inv(x) for x in l) return -l return inv(self.O(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): 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, gens=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 ``order`` 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 the ``gens`` input argument contains a list of generators, the resulting key function can be used to sort SymPy ``Expr`` objects. """ if order is None: order = lex if isinstance(order, Symbol): order = str(order) if isinstance(order, str): try: order = _monomial_key[order] except KeyError: raise ValueError("supported monomial orderings are 'lex', 'grlex' and 'grevlex', got %r" % order) if hasattr(order, '__call__'): if gens is not None: def _order(expr): return order(expr.as_poly(*gens).degree_list()) return _order 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.orderings 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) sympy-0.7.4.1/sympy/polys/polyconfig.py0000644000175000017500000000315612253362407020323 0ustar georgeskgeorgesk"""Configuration utilities for polynomial manipulation algorithms. """ from __future__ import print_function, division from contextlib import contextmanager _default_config = { 'USE_COLLINS_RESULTANT': False, 'USE_SIMPLIFY_GCD': True, 'USE_HEU_GCD': True, 'USE_IRREDUCIBLE_IN_FACTOR': False, 'USE_CYCLOTOMIC_FACTOR': True, 'EEZ_RESTART_IF_NEEDED': True, 'EEZ_NUMBER_OF_CONFIGS': 3, 'EEZ_NUMBER_OF_TRIES': 5, 'EEZ_MODULUS_STEP': 2, 'GF_IRRED_METHOD': 'rabin', 'GF_FACTOR_METHOD': 'zassenhaus', 'GROEBNER': 'buchberger', } _current_config = {} @contextmanager def using(**kwargs): for k, v in kwargs.items(): setup(k, v) yield for k in kwargs.keys(): setup(k) def setup(key, value=None): """Assign a value to (or reset) a configuration item. """ key = key.upper() if value is not None: _current_config[key] = value else: _current_config[key] = _default_config[key] def query(key): """Ask for a value of the given configuration item. """ return _current_config.get(key.upper(), None) def configure(): """Initialized configuration of polys module. """ from os import getenv for key, default in _default_config.items(): value = getenv('SYMPY_' + key) if value is not None: try: _current_config[key] = eval(value) except NameError: _current_config[key] = value else: _current_config[key] = default configure() sympy-0.7.4.1/sympy/polys/rootoftools.py0000644000175000017500000005375512253362407020555 0ustar georgeskgeorgesk"""Implementation of RootOf class and related tools. """ from __future__ import print_function, division from sympy.core import (S, Expr, Integer, Float, I, Add, Lambda, symbols, sympify, Rational) 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, public from sympy.core.compatibility import xrange _reals_cache = {} _complexes_cache = {} @public 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): # RootOf currently only works with univariate expressions and although # the poly attribute is often a PurePoly, sometimes it is a Poly. In # either case no free symbols should be reported. return set() 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 xrange(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 xrange(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 xrange(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() if not self.is_real: # For complex intervals, we need to keep refining until the # imaginary interval is disjunct with other roots, that is, # until both ends get refined. ay = interval.ay by = interval.by while interval.ay == ay or interval.by == by: interval = interval.refine() while True: if self.is_real: x0 = mpf(str(interval.center)) else: x0 = mpc(*map(str, interval.center)) try: root = findroot(func, x0) # If the (real or complex) root is not in the 'interval', # then keep refining the interval. This happens if findroot # accidentally finds a different root outside of this # interval because our initial estimate 'x0' was not close # enough. if self.is_real: a = mpf(str(interval.a)) b = mpf(str(interval.b)) # This is needed due to the bug #3364: a, b = min(a, b), max(a, b) if not (a < root < b): raise ValueError("Root not in the interval.") else: ax = mpf(str(interval.ax)) bx = mpf(str(interval.bx)) ay = mpf(str(interval.ay)) by = mpf(str(interval.by)) # This is needed due to the bug #3364: ax, bx = min(ax, bx), max(ax, bx) ay, by = min(ay, by), max(ay, by) if not (ax < root.real < bx and ay < root.imag < by): raise ValueError("Root not in the interval.") except ValueError: interval = interval.refine() continue else: break finally: mp.prec = _prec return Float._new(root.real._mpf_, prec) + I*Float._new(root.imag._mpf_, prec) def eval_rational(self, tol): """ Returns a Rational approximation to ``self`` with the tolerance ``tol``. This method uses bisection, which is very robust and it will always converge. The returned Rational instance will be at most 'tol' from the exact root. The following example first obtains Rational approximation to 1e-7 accuracy for all roots of the 4-th order Legendre polynomial, and then evaluates it to 5 decimal digits (so all digits will be correct including rounding): >>> from sympy import S, legendre_poly, Symbol >>> x = Symbol("x") >>> p = legendre_poly(4, x, polys=True) >>> roots = [r.eval_rational(S(1)/10**7) for r in p.real_roots()] >>> roots = [str(r.n(5)) for r in roots] >>> roots ['-0.86114', '-0.33998', '0.33998', '0.86114'] """ if not self.is_real: raise NotImplementedError("eval_rational() only works for real polynomials so far") func = lambdify(self.poly.gen, self.expr) interval = self._get_interval() a = Rational(str(interval.a)) b = Rational(str(interval.b)) # This is needed due to the bug #3364: a, b = min(a, b), max(a, b) return bisect(func, a, b, tol) @public 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 = zip(*p.terms()) try: q = Poly(q, domain=domain, expand=False) except GeneratorsNeeded: q, q_coeff = None, (q,) else: q_monom, q_coeff = 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(zip(p_monom, p_coeff)), *p.gens).as_expr() else: (p,) = p_coeff if q is not None: q = Poly(dict(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) def bisect(f, a, b, tol): """ Implements bisection. This function is used in RootOf.eval_rational() and it needs to be robust. Examples ======== >>> from sympy import S >>> from sympy.polys.rootoftools import bisect >>> bisect(lambda x: x**2-1, -10, 0, S(1)/10**2) -1025/1024 >>> bisect(lambda x: x**2-1, -10, 0, S(1)/10**4) -131075/131072 """ a = sympify(a) b = sympify(b) fa = f(a) fb = f(b) if fa * fb >= 0: raise ValueError("bisect: f(a) and f(b) must have opposite signs") while (b-a > tol): c = (a+b)/2 fc = f(c) if (fc == 0): return c # We need to make sure f(c) is not zero below if (fa * fc < 0): b = c fb = fc else: a = c fa = fc return (a+b)/2 sympy-0.7.4.1/sympy/polys/polyroots.py0000644000175000017500000006543312253362407020232 0ustar georgeskgeorgesk"""Algorithms for computing symbolic roots of polynomials. """ from __future__ import print_function, division import math from sympy.core.symbol import Dummy, Symbol, symbols from sympy.core import S, I, pi from sympy.core.mul import expand_2arg 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, re, im, Abs, cos, sin from sympy.polys.polytools import Poly, cancel, factor, gcd_list, discriminant from sympy.polys.specialpolys import cyclotomic_poly from sympy.polys.polyerrors import PolynomialError, GeneratorsNeeded, DomainError from sympy.polys.polyquinticconst import PolyQuintic from sympy.polys.rationaltools import together from sympy.simplify import simplify, powsimp from sympy.utilities import default_sort_key, public from sympy.core.compatibility import reduce, xrange 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([expand_2arg(i) for i in (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: if q.is_real: if (q > 0) is True: u1 = -q**Rational(1, 3) else: u1 = (-q)**Rational(1, 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]] elif q.is_real and q < 0: u1 = -(-q/2 + sqrt(q**2/4 + pon3**3))**Rational(1, 3) 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) if p is S.Zero: return [u1 - aon3, u2 - aon3, u3 - aon3] soln = [ -u1 + pon3/u1 - aon3, -u2 + pon3/u2 - aon3, -u3 + pon3/u3 - aon3 ] return soln def _roots_quartic_euler(p, q, r, a): """ Descartes-Euler solution of the quartic equation Parameters ========== p, q, r: coefficients of ``x**4 + p*x**2 + q*x + r`` a: shift of the roots Notes ===== This is a helper function for ``roots_quartic``. Look for solutions of the form :: ``x1 = sqrt(R) - sqrt(A + B*sqrt(R))`` ``x2 = -sqrt(R) - sqrt(A - B*sqrt(R))`` ``x3 = -sqrt(R) + sqrt(A - B*sqrt(R))`` ``x4 = sqrt(R) + sqrt(A + B*sqrt(R))`` To satisfy the quartic equation one must have ``p = -2*(R + A); q = -4*B*R; r = (R - A)**2 - B**2*R`` so that ``R`` must satisfy the Descartes-Euler resolvent equation ``64*R**3 + 32*p*R**2 + (4*p**2 - 16*r)*R - q**2 = 0`` If the resolvent does not have a rational solution, return None; in that case it is likely that the Ferrari method gives a simpler solution. Examples ======== >>> from sympy import S >>> from sympy.polys.polyroots import _roots_quartic_euler >>> p, q, r = -S(64)/5, -S(512)/125, -S(1024)/3125 >>> _roots_quartic_euler(p, q, r, S(0))[0] -sqrt(32*sqrt(5)/125 + 16/5) + 4*sqrt(5)/5 """ from sympy.solvers import solve # solve the resolvent equation x = Symbol('x') eq = 64*x**3 + 32*p*x**2 + (4*p**2 - 16*r)*x - q**2 xsols = list(roots(Poly(eq, x), cubics=False).keys()) xsols = [sol for sol in xsols if sol.is_rational] if not xsols: return None R = max(xsols) c1 = sqrt(R) B = -q*c1/(4*R) A = -R - p/2 c2 = sqrt(A + B) c3 = sqrt(A - B) return [c1 - c2 - a, -c1 - c3 - a, -c1 + c3 - a, c1 + c2 - a] 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 7. eqworld.ipmnet.ru/en/solutions/ae/ae0108.pdf """ _, 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 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: # Descartes-Euler method, see [7] sols = _roots_quartic_euler(e, f, g, aon4) if sols: return sols # Ferrari method, see [1, 2] 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) p = -e**2/12 - g q = -e**3/108 + e*g/3 - f**2/8 TH = Rational(1, 3) if p.is_zero: y = -5*e/6 - q**TH elif p.is_number and p.is_comparable: # 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 else: raise PolynomialError('cannot return general quartic solution') w = sqrt(e + 2*y) arg1 = 3*e + 2*y arg2 = 2*f/w ans = [] 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 xrange(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 xrange(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 xrange(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_quintic(f): """ Calulate exact roots of a solvable quintic """ result = [] coeff_5, coeff_4, p, q, r, s = f.all_coeffs() # Eqn must be of the form x^5 + px^3 + qx^2 + rx + s if coeff_4: return result if coeff_5 != 1: l = [p/coeff_5, q/coeff_5, r/coeff_5, s/coeff_5] if not all(coeff.is_Rational for coeff in l): return result f = Poly(f/coeff_5) quintic = PolyQuintic(f) # Eqn standardised. Algo for solving starts here if not f.is_irreducible: return result f20 = quintic.f20 # Check if f20 has linear factors over domain Z if f20.is_irreducible: return result # Now, we know that f is solvable for _factor in f20.factor_list()[1]: if _factor[0].is_linear: theta = _factor[0].root(0) break d = discriminant(f) delta = sqrt(d) # zeta = a fifth root of unity zeta1, zeta2, zeta3, zeta4 = quintic.zeta T = quintic.T(theta, d) tol = S(1e-10) alpha = T[1] + T[2]*delta alpha_bar = T[1] - T[2]*delta beta = T[3] + T[4]*delta beta_bar = T[3] - T[4]*delta disc = alpha**2 - 4*beta disc_bar = alpha_bar**2 - 4*beta_bar l0 = quintic.l0(theta) l1 = _quintic_simplify((-alpha + sqrt(disc)) / S(2)) l4 = _quintic_simplify((-alpha - sqrt(disc)) / S(2)) l2 = _quintic_simplify((-alpha_bar + sqrt(disc_bar)) / S(2)) l3 = _quintic_simplify((-alpha_bar - sqrt(disc_bar)) / S(2)) order = quintic.order(theta, d) test = (order*delta.n()) - ( (l1.n() - l4.n())*(l2.n() - l3.n()) ) # Comparing floats # Problems importing on top from sympy.utilities.randtest import comp if not comp(test, 0, tol): l2, l3 = l3, l2 # Now we have correct order of l's R1 = l0 + l1*zeta1 + l2*zeta2 + l3*zeta3 + l4*zeta4 R2 = l0 + l3*zeta1 + l1*zeta2 + l4*zeta3 + l2*zeta4 R3 = l0 + l2*zeta1 + l4*zeta2 + l1*zeta3 + l3*zeta4 R4 = l0 + l4*zeta1 + l3*zeta2 + l2*zeta3 + l1*zeta4 Res = [None, [None]*5, [None]*5, [None]*5, [None]*5] Res_n = [None, [None]*5, [None]*5, [None]*5, [None]*5] sol = Symbol('sol') # Simplifying improves performace a lot for exact expressions R1 = _quintic_simplify(R1) R2 = _quintic_simplify(R2) R3 = _quintic_simplify(R3) R4 = _quintic_simplify(R4) # Solve imported here. Causing problems if imported as 'solve' # and hence the changed name from sympy.solvers.solvers import solve as _solve a, b = symbols('a b', cls=Dummy) _sol = _solve( sol**5 - a - I*b, sol) for i in range(5): _sol[i] = factor(_sol[i]) R1 = R1.as_real_imag() R2 = R2.as_real_imag() R3 = R3.as_real_imag() R4 = R4.as_real_imag() for i, root in enumerate(_sol): Res[1][i] = _quintic_simplify(root.subs({ a: R1[0], b: R1[1] })) Res[2][i] = _quintic_simplify(root.subs({ a: R2[0], b: R2[1] })) Res[3][i] = _quintic_simplify(root.subs({ a: R3[0], b: R3[1] })) Res[4][i] = _quintic_simplify(root.subs({ a: R4[0], b: R4[1] })) for i in range(1, 5): for j in range(5): Res_n[i][j] = Res[i][j].n() Res[i][j] = _quintic_simplify(Res[i][j]) r1 = Res[1][0] r1_n = Res_n[1][0] for i in range(5): if comp(im(r1_n*Res_n[4][i]), 0, tol): r4 = Res[4][i] break u, v = quintic.uv(theta, d) sqrt5 = math.sqrt(5) # Now we have various Res values. Each will be a list of five # values. We have to pick one r value from those five for each Res u, v = quintic.uv(theta, d) testplus = (u + v*delta*sqrt(5)).n() testminus = (u - v*delta*sqrt(5)).n() # Evaluated numbers suffixed with _n # We will use evaluated numbers for calculation. Much faster. r4_n = r4.n() r2 = r3 = None for i in range(5): r2temp_n = Res_n[2][i] for j in range(5): # Again storing away the exact number and using # evaluated numbers in computations r3temp_n = Res_n[3][j] if( comp( r1_n*r2temp_n**2 + r4_n*r3temp_n**2 - testplus, 0, tol) and comp( r3temp_n*r1_n**2 + r2temp_n*r4_n**2 - testminus, 0, tol ) ): r2 = Res[2][i] r3 = Res[3][j] break if r2: break # Now, we have r's so we can get roots x1 = (r1 + r2 + r3 + r4)/5 x2 = (r1*zeta4 + r2*zeta3 + r3*zeta2 + r4*zeta1)/5 x3 = (r1*zeta3 + r2*zeta1 + r3*zeta4 + r4*zeta2)/5 x4 = (r1*zeta2 + r2*zeta4 + r3*zeta1 + r4*zeta3)/5 x5 = (r1*zeta1 + r2*zeta2 + r3*zeta3 + r4*zeta4)/5 result = [x1, x2, x3, x4, x5] # Now check if solutions are distinct result_n = [] for root in result: result_n.append(root.n(5)) result_n = sorted(result_n, key=default_sort_key) prev_entry = None for r in result_n: if r == prev_entry: # Roots are identical. Abort. Return [] # and fall back to usual solve return [] prev_entry = r return result def _quintic_simplify(expr): expr = powsimp(expr) expr = cancel(expr) return together(expr) def _integer_basis(poly): """Compute coefficient basis for a polynomial over integers. Returns the integer ``div`` such that substituting ``x = div*y`` ``p(x) = m*q(y)`` where the coefficients of ``q`` are smaller than those of ``p``. For example ``x**5 + 512*x + 1024 = 0`` with ``div = 4`` becomes ``y**5 + 2*y + 1 = 0`` Returns the integer ``div`` or ``None`` if there is no possible scaling. Examples ======== >>> from sympy.polys import Poly >>> from sympy.abc import x >>> from sympy.polys.polyroots import _integer_basis >>> p = Poly(x**5 + 512*x + 1024, x, domain='ZZ') >>> _integer_basis(p) 4 """ monoms, coeffs = list(zip(*poly.terms())) monoms, = list(zip(*monoms)) coeffs = list(map(abs, coeffs)) if coeffs[0] < coeffs[-1]: coeffs = list(reversed(coeffs)) n = monoms[0] monoms = [n - i for i in reversed(monoms)] 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() # TODO: This is fragile. Figure out how to make this independent of construct_domain(). if poly.get_domain().is_Poly and all(c.is_term 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 @public 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} """ from sympy.polys.polytools import to_rational_coeffs flags = dict(flags) auto = flags.pop('auto', True) cubics = flags.pop('cubics', True) quartics = flags.pop('quartics', True) quintics = flags.pop('quintics', False) 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) elif n == 5 and quintics: result += roots_quintic(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() rescale_x = None translate_x = None 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: if f.get_domain().is_EX: res = to_rational_coeffs(f) if res: if res[0] is None: translate_x, f = res[2:] else: rescale_x, f = res[1], res[-1] result = roots(f) if not result: for root in _try_decompose(f): _update_dict(result, root, 1) else: 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 rescale_x: result1 = {} for k, v in result.items(): result1[k*rescale_x] = v result = result1 if translate_x: result1 = {} for k, v in result.items(): result1[k + translate_x] = v result = result1 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) sympy-0.7.4.1/sympy/polys/distributedmodules.py0000644000175000017500000005250212253362407022064 0ustar georgeskgeorgeskr""" Sparse distributed elements of free modules over multivariate (generalized) polynomial rings. This code and its data structures are very much like the distributed polynomials, 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". """ from __future__ import print_function, division from itertools import permutations from sympy.polys.monomials import ( monomial_mul, monomial_lcm, monomial_div, monomial_deg, monomial_divides ) from sympy.polys.polytools import Poly from sympy.polys.polyutils import parallel_dict_from_expr from sympy import S, sympify # Additional monomial tools. 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:]) 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:]) 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. def sdm_LC(f, K): """Returns the leading coeffcient of ``f``. """ if not f: return K.zero else: return f[0][1] def sdm_to_dict(f): """Make a dictionary from a distributed polynomial. """ return dict(f) 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, 0, 0), 2)] """ return sdm_strip(sdm_sort(list(d.items()), O)) def sdm_sort(f, O): """Sort terms in ``f`` using the given monomial order ``O``. """ return sorted(f, key=lambda term: O(term[0]), reverse=True) def sdm_strip(f): """Remove terms with zero coefficients from ``f`` in ``K[X]``. """ return [ (monom, coeff) for monom, coeff in f if coeff ] 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)] `(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)] `(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, 0, 1), 1)] """ 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 sdm_from_dict(h, O) 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] 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) """ return f[0] 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)] `(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, 2, 1), 6)] """ 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 ] def sdm_zero(): """Return the zero module element.""" return [] 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 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), ((0, 2, 0, 0), 1), ((0, 0, 2, 0), 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) 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. 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)] """ 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 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)) 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 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 sympy-0.7.4.1/sympy/polys/polyquinticconst.py0000644000175000017500000027361712253362407021614 0ustar georgeskgeorgesk""" Solving solvable quintics - An implementation of DS Dummit's paper Paper : http://www.ams.org/journals/mcom/1991-57-195/S0025-5718-1991-1079014-X/S0025-5718-1991-1079014-X.pdf Mathematica notebook: http://www.emba.uvm.edu/~ddummit/quintics/quintics.nb """ from __future__ import print_function, division from sympy.core import S, Symbol from sympy.core.numbers import I from sympy.polys.polytools import Poly from sympy.core.evalf import N from sympy.functions import sqrt from sympy.utilities import public x = Symbol('x') @public class PolyQuintic(object): """Special functions for solvable quintics""" def __init__(self, poly): _, _, self.p, self.q, self.r, self.s = poly.all_coeffs() self.zeta1 = S(-1)/4 + (sqrt(5)/4) + I*sqrt((sqrt(5)/8) + S(5)/8) self.zeta2 = (-sqrt(5)/4) - S(1)/4 + I*sqrt((-sqrt(5)/8) + S(5)/8) self.zeta3 = (-sqrt(5)/4) - S(1)/4 - I*sqrt((-sqrt(5)/8) + S(5)/8) self.zeta4 = S(-1)/4 + (sqrt(5)/4) - I*sqrt((sqrt(5)/8) + S(5)/8) @property def f20(self): p, q, r, s = self.p, self.q, self.r, self.s f20 = q**8 - 13*p*q**6*r + p**5*q**2*r**2 + 65*p**2*q**4*r**2 - 4*p**6*r**3 - 128*p**3*q**2*r**3 + 17*q**4*r**3 + 48*p**4*r**4 - 16*p*q**2*r**4 - 192*p**2*r**5 + 256*r**6 - 4*p**5*q**3*s - 12*p**2*q**5*s + 18*p**6*q*r*s + 12*p**3*q**3*r*s - 124*q**5*r*s + 196*p**4*q*r**2*s + 590*p*q**3*r**2*s - 160*p**2*q*r**3*s - 1600*q*r**4*s - 27*p**7*s**2 - 150*p**4*q**2*s**2 - 125*p*q**4*s**2 - 99*p**5*r*s**2 - 725*p**2*q**2*r*s**2 + 1200*p**3*r**2*s**2 + 3250*q**2*r**2*s**2 - 2000*p*r**3*s**2 - 1250*p*q*r*s**3 + 3125*p**2*s**4 - 9375*r*s**4-(2*p*q**6 - 19*p**2*q**4*r + 51*p**3*q**2*r**2 - 3*q**4*r**2 - 32*p**4*r**3 - 76*p*q**2*r**3 + 256*p**2*r**4 - 512*r**5 + 31*p**3*q**3*s + 58*q**5*s - 117*p**4*q*r*s - 105*p*q**3*r*s - 260*p**2*q*r**2*s + 2400*q*r**3*s + 108*p**5*s**2 + 325*p**2*q**2*s**2 - 525*p**3*r*s**2 - 2750*q**2*r*s**2 + 500*p*r**2*s**2 - 625*p*q*s**3 + 3125*s**4)*x+(p**2*q**4 - 6*p**3*q**2*r - 8*q**4*r + 9*p**4*r**2 + 76*p*q**2*r**2 - 136*p**2*r**3 + 400*r**4 - 50*p*q**3*s + 90*p**2*q*r*s - 1400*q*r**2*s + 625*q**2*s**2 + 500*p*r*s**2)*x**2-(2*q**4 - 21*p*q**2*r + 40*p**2*r**2 - 160*r**3 + 15*p**2*q*s + 400*q*r*s - 125*p*s**2)*x**3+(2*p*q**2 - 6*p**2*r + 40*r**2 - 50*q*s)*x**4 + 8*r*x**5 + x**6 return Poly(f20, x) @property def b(self): p, q, r, s = self.p, self.q, self.r, self.s b = ( [], [0,0,0,0,0,0], [0,0,0,0,0,0], [0,0,0,0,0,0], [0,0,0,0,0,0],) b[1][5] = 100*p**7*q**7 + 2175*p**4*q**9 + 10500*p*q**11 - 1100*p**8*q**5*r - 27975*p**5*q**7*r - 152950*p**2*q**9*r + 4125*p**9*q**3*r**2 + 128875*p**6*q**5*r**2 + 830525*p**3*q**7*r**2 - 59450*q**9*r**2 - 5400*p**10*q*r**3 - 243800*p**7*q**3*r**3 - 2082650*p**4*q**5*r**3 + 333925*p*q**7*r**3 + 139200*p**8*q*r**4 + 2406000*p**5*q**3*r**4 + 122600*p**2*q**5*r**4 - 1254400*p**6*q*r**5 - 3776000*p**3*q**3*r**5 - 1832000*q**5*r**5 + 4736000*p**4*q*r**6 + 6720000*p*q**3*r**6 - 6400000*p**2*q*r**7 + 900*p**9*q**4*s + 37400*p**6*q**6*s + 281625*p**3*q**8*s + 435000*q**10*s - 6750*p**10*q**2*r*s - 322300*p**7*q**4*r*s - 2718575*p**4*q**6*r*s - 4214250*p*q**8*r*s + 16200*p**11*r**2*s + 859275*p**8*q**2*r**2*s + 8925475*p**5*q**4*r**2*s + 14427875*p**2*q**6*r**2*s - 453600*p**9*r**3*s - 10038400*p**6*q**2*r**3*s - 17397500*p**3*q**4*r**3*s + 11333125*q**6*r**3*s + 4451200*p**7*r**4*s + 15850000*p**4*q**2*r**4*s - 34000000*p*q**4*r**4*s - 17984000*p**5*r**5*s + 10000000*p**2*q**2*r**5*s + 25600000*p**3*r**6*s + 8000000*q**2*r**6*s - 6075*p**11*q*s**2 + 83250*p**8*q**3*s**2 + 1282500*p**5*q**5*s**2 + 2862500*p**2*q**7*s**2 - 724275*p**9*q*r*s**2 - 9807250*p**6*q**3*r*s**2 - 28374375*p**3*q**5*r*s**2 - 22212500*q**7*r*s**2 + 8982000*p**7*q*r**2*s**2 + 39600000*p**4*q**3*r**2*s**2 + 61746875*p*q**5*r**2*s**2 + 1010000*p**5*q*r**3*s**2 + 1000000*p**2*q**3*r**3*s**2 - 78000000*p**3*q*r**4*s**2 - 30000000*q**3*r**4*s**2 - 80000000*p*q*r**5*s**2 + 759375*p**10*s**3 + 9787500*p**7*q**2*s**3 + 39062500*p**4*q**4*s**3 + 52343750*p*q**6*s**3 - 12301875*p**8*r*s**3 - 98175000*p**5*q**2*r*s**3 - 225078125*p**2*q**4*r*s**3 + 54900000*p**6*r**2*s**3 + 310000000*p**3*q**2*r**2*s**3 + 7890625*q**4*r**2*s**3 - 51250000*p**4*r**3*s**3 + 420000000*p*q**2*r**3*s**3 - 110000000*p**2*r**4*s**3 + 200000000*r**5*s**3 - 2109375*p**6*q*s**4 + 21093750*p**3*q**3*s**4 + 89843750*q**5*s**4 - 182343750*p**4*q*r*s**4 - 733203125*p*q**3*r*s**4 + 196875000*p**2*q*r**2*s**4 - 1125000000*q*r**3*s**4 + 158203125*p**5*s**5 + 566406250*p**2*q**2*s**5 - 101562500*p**3*r*s**5 + 1669921875*q**2*r*s**5 - 1250000000*p*r**2*s**5 + 1220703125*p*q*s**6 - 6103515625*s**7 b[1][4] = -1000*p**5*q**7 - 7250*p**2*q**9 + 10800*p**6*q**5*r + 96900*p**3*q**7*r + 52500*q**9*r - 37400*p**7*q**3*r**2 - 470850*p**4*q**5*r**2 - 640600*p*q**7*r**2 + 39600*p**8*q*r**3 + 983600*p**5*q**3*r**3 + 2848100*p**2*q**5*r**3 - 814400*p**6*q*r**4 - 6076000*p**3*q**3*r**4 - 2308000*q**5*r**4 + 5024000*p**4*q*r**5 + 9680000*p*q**3*r**5 - 9600000*p**2*q*r**6 - 13800*p**7*q**4*s - 94650*p**4*q**6*s + 26500*p*q**8*s + 86400*p**8*q**2*r*s + 816500*p**5*q**4*r*s + 257500*p**2*q**6*r*s - 91800*p**9*r**2*s - 1853700*p**6*q**2*r**2*s - 630000*p**3*q**4*r**2*s + 8971250*q**6*r**2*s + 2071200*p**7*r**3*s + 7240000*p**4*q**2*r**3*s - 29375000*p*q**4*r**3*s - 14416000*p**5*r**4*s + 5200000*p**2*q**2*r**4*s + 30400000*p**3*r**5*s + 12000000*q**2*r**5*s - 64800*p**9*q*s**2 - 567000*p**6*q**3*s**2 - 1655000*p**3*q**5*s**2 - 6987500*q**7*s**2 - 337500*p**7*q*r*s**2 - 8462500*p**4*q**3*r*s**2 + 5812500*p*q**5*r*s**2 + 24930000*p**5*q*r**2*s**2 + 69125000*p**2*q**3*r**2*s**2 - 103500000*p**3*q*r**3*s**2 - 30000000*q**3*r**3*s**2 - 90000000*p*q*r**4*s**2 + 708750*p**8*s**3 + 5400000*p**5*q**2*s**3 - 8906250*p**2*q**4*s**3 - 18562500*p**6*r*s**3 + 625000*p**3*q**2*r*s**3 - 29687500*q**4*r*s**3 + 75000000*p**4*r**2*s**3 + 416250000*p*q**2*r**2*s**3 - 60000000*p**2*r**3*s**3 + 300000000*r**4*s**3 - 71718750*p**4*q*s**4 - 189062500*p*q**3*s**4 - 210937500*p**2*q*r*s**4 - 1187500000*q*r**2*s**4 + 187500000*p**3*s**5 + 800781250*q**2*s**5 + 390625000*p*r*s**5 b[1][3] = 500*p**6*q**5 + 6350*p**3*q**7 + 19800*q**9 - 3750*p**7*q**3*r - 65100*p**4*q**5*r - 264950*p*q**7*r + 6750*p**8*q*r**2 + 209050*p**5*q**3*r**2 + 1217250*p**2*q**5*r**2 - 219000*p**6*q*r**3 - 2510000*p**3*q**3*r**3 - 1098500*q**5*r**3 + 2068000*p**4*q*r**4 + 5060000*p*q**3*r**4 - 5200000*p**2*q*r**5 + 6750*p**8*q**2*s + 96350*p**5*q**4*s + 346000*p**2*q**6*s - 20250*p**9*r*s - 459900*p**6*q**2*r*s - 1828750*p**3*q**4*r*s + 2930000*q**6*r*s + 594000*p**7*r**2*s + 4301250*p**4*q**2*r**2*s - 10906250*p*q**4*r**2*s - 5252000*p**5*r**3*s + 1450000*p**2*q**2*r**3*s + 12800000*p**3*r**4*s + 6500000*q**2*r**4*s - 74250*p**7*q*s**2 - 1418750*p**4*q**3*s**2 - 5956250*p*q**5*s**2 + 4297500*p**5*q*r*s**2 + 29906250*p**2*q**3*r*s**2 - 31500000*p**3*q*r**2*s**2 - 12500000*q**3*r**2*s**2 - 35000000*p*q*r**3*s**2 - 1350000*p**6*s**3 - 6093750*p**3*q**2*s**3 - 17500000*q**4*s**3 + 7031250*p**4*r*s**3 + 127812500*p*q**2*r*s**3 - 18750000*p**2*r**2*s**3 + 162500000*r**3*s**3 - 107812500*p**2*q*s**4 - 460937500*q*r*s**4 + 214843750*p*s**5 b[1][2] = -1950*p**4*q**5 - 14100*p*q**7 + 14350*p**5*q**3*r + 125600*p**2*q**5*r - 27900*p**6*q*r**2 - 402250*p**3*q**3*r**2 - 288250*q**5*r**2 + 436000*p**4*q*r**3 + 1345000*p*q**3*r**3 - 1400000*p**2*q*r**4 - 9450*p**6*q**2*s + 1250*p**3*q**4*s + 465000*q**6*s + 49950*p**7*r*s + 302500*p**4*q**2*r*s - 1718750*p*q**4*r*s - 834000*p**5*r**2*s - 437500*p**2*q**2*r**2*s + 3100000*p**3*r**3*s + 1750000*q**2*r**3*s + 292500*p**5*q*s**2 + 1937500*p**2*q**3*s**2 - 3343750*p**3*q*r*s**2 - 1875000*q**3*r*s**2 - 8125000*p*q*r**2*s**2 + 1406250*p**4*s**3 + 12343750*p*q**2*s**3 - 5312500*p**2*r*s**3 + 43750000*r**2*s**3 - 74218750*q*s**4 b[1][1] = 300*p**5*q**3 + 2150*p**2*q**5 - 1350*p**6*q*r - 21500*p**3*q**3*r - 61500*q**5*r + 42000*p**4*q*r**2 + 290000*p*q**3*r**2 - 300000*p**2*q*r**3 + 4050*p**7*s + 45000*p**4*q**2*s + 125000*p*q**4*s - 108000*p**5*r*s - 643750*p**2*q**2*r*s + 700000*p**3*r**2*s + 375000*q**2*r**2*s + 93750*p**3*q*s**2 + 312500*q**3*s**2 - 1875000*p*q*r*s**2 + 1406250*p**2*s**3 + 9375000*r*s**3 b[1][0] = -1250*p**3*q**3 - 9000*q**5 + 4500*p**4*q*r + 46250*p*q**3*r - 50000*p**2*q*r**2 - 6750*p**5*s - 43750*p**2*q**2*s + 75000*p**3*r*s + 62500*q**2*r*s - 156250*p*q*s**2 + 1562500*s**3 b[2][5] = 200*p**6*q**11 - 250*p**3*q**13 - 10800*q**15 - 3900*p**7*q**9*r - 3325*p**4*q**11*r + 181800*p*q**13*r + 26950*p**8*q**7*r**2 + 69625*p**5*q**9*r**2 - 1214450*p**2*q**11*r**2 - 78725*p**9*q**5*r**3 - 368675*p**6*q**7*r**3 + 4166325*p**3*q**9*r**3 + 1131100*q**11*r**3 + 73400*p**10*q**3*r**4 + 661950*p**7*q**5*r**4 - 9151950*p**4*q**7*r**4 - 16633075*p*q**9*r**4 + 36000*p**11*q*r**5 + 135600*p**8*q**3*r**5 + 17321400*p**5*q**5*r**5 + 85338300*p**2*q**7*r**5 - 832000*p**9*q*r**6 - 21379200*p**6*q**3*r**6 - 176044000*p**3*q**5*r**6 - 1410000*q**7*r**6 + 6528000*p**7*q*r**7 + 129664000*p**4*q**3*r**7 + 47344000*p*q**5*r**7 - 21504000*p**5*q*r**8 - 115200000*p**2*q**3*r**8 + 25600000*p**3*q*r**9 + 64000000*q**3*r**9 + 15700*p**8*q**8*s + 120525*p**5*q**10*s + 113250*p**2*q**12*s - 196900*p**9*q**6*r*s - 1776925*p**6*q**8*r*s - 3062475*p**3*q**10*r*s - 4153500*q**12*r*s + 857925*p**10*q**4*r**2*s + 10562775*p**7*q**6*r**2*s + 34866250*p**4*q**8*r**2*s + 73486750*p*q**10*r**2*s - 1333800*p**11*q**2*r**3*s - 29212625*p**8*q**4*r**3*s - 168729675*p**5*q**6*r**3*s - 427230750*p**2*q**8*r**3*s + 108000*p**12*r**4*s + 30384200*p**9*q**2*r**4*s + 324535100*p**6*q**4*r**4*s + 952666750*p**3*q**6*r**4*s - 38076875*q**8*r**4*s - 4296000*p**10*r**5*s - 213606400*p**7*q**2*r**5*s - 842060000*p**4*q**4*r**5*s - 95285000*p*q**6*r**5*s + 61184000*p**8*r**6*s + 567520000*p**5*q**2*r**6*s + 547000000*p**2*q**4*r**6*s - 390912000*p**6*r**7*s - 812800000*p**3*q**2*r**7*s - 924000000*q**4*r**7*s + 1152000000*p**4*r**8*s + 800000000*p*q**2*r**8*s - 1280000000*p**2*r**9*s + 141750*p**10*q**5*s**2 - 31500*p**7*q**7*s**2 - 11325000*p**4*q**9*s**2 - 31687500*p*q**11*s**2 - 1293975*p**11*q**3*r*s**2 - 4803800*p**8*q**5*r*s**2 + 71398250*p**5*q**7*r*s**2 + 227625000*p**2*q**9*r*s**2 + 3256200*p**12*q*r**2*s**2 + 43870125*p**9*q**3*r**2*s**2 + 64581500*p**6*q**5*r**2*s**2 + 56090625*p**3*q**7*r**2*s**2 + 260218750*q**9*r**2*s**2 - 74610000*p**10*q*r**3*s**2 - 662186500*p**7*q**3*r**3*s**2 - 1987747500*p**4*q**5*r**3*s**2 - 811928125*p*q**7*r**3*s**2 + 471286000*p**8*q*r**4*s**2 + 2106040000*p**5*q**3*r**4*s**2 + 792687500*p**2*q**5*r**4*s**2 - 135120000*p**6*q*r**5*s**2 + 2479000000*p**3*q**3*r**5*s**2 + 5242250000*q**5*r**5*s**2 - 6400000000*p**4*q*r**6*s**2 - 8620000000*p*q**3*r**6*s**2 + 13280000000*p**2*q*r**7*s**2 + 1600000000*q*r**8*s**2 + 273375*p**12*q**2*s**3 - 13612500*p**9*q**4*s**3 - 177250000*p**6*q**6*s**3 - 511015625*p**3*q**8*s**3 - 320937500*q**10*s**3 - 2770200*p**13*r*s**3 + 12595500*p**10*q**2*r*s**3 + 543950000*p**7*q**4*r*s**3 + 1612281250*p**4*q**6*r*s**3 + 968125000*p*q**8*r*s**3 + 77031000*p**11*r**2*s**3 + 373218750*p**8*q**2*r**2*s**3 + 1839765625*p**5*q**4*r**2*s**3 + 1818515625*p**2*q**6*r**2*s**3 - 776745000*p**9*r**3*s**3 - 6861075000*p**6*q**2*r**3*s**3 - 20014531250*p**3*q**4*r**3*s**3 - 13747812500*q**6*r**3*s**3 + 3768000000*p**7*r**4*s**3 + 35365000000*p**4*q**2*r**4*s**3 + 34441875000*p*q**4*r**4*s**3 - 9628000000*p**5*r**5*s**3 - 63230000000*p**2*q**2*r**5*s**3 + 13600000000*p**3*r**6*s**3 - 15000000000*q**2*r**6*s**3 - 10400000000*p*r**7*s**3 - 45562500*p**11*q*s**4 - 525937500*p**8*q**3*s**4 - 1364218750*p**5*q**5*s**4 - 1382812500*p**2*q**7*s**4 + 572062500*p**9*q*r*s**4 + 2473515625*p**6*q**3*r*s**4 + 13192187500*p**3*q**5*r*s**4 + 12703125000*q**7*r*s**4 - 451406250*p**7*q*r**2*s**4 - 18153906250*p**4*q**3*r**2*s**4 - 36908203125*p*q**5*r**2*s**4 - 9069375000*p**5*q*r**3*s**4 + 79957812500*p**2*q**3*r**3*s**4 + 5512500000*p**3*q*r**4*s**4 + 50656250000*q**3*r**4*s**4 + 74750000000*p*q*r**5*s**4 + 56953125*p**10*s**5 + 1381640625*p**7*q**2*s**5 - 781250000*p**4*q**4*s**5 + 878906250*p*q**6*s**5 - 2655703125*p**8*r*s**5 - 3223046875*p**5*q**2*r*s**5 - 35117187500*p**2*q**4*r*s**5 + 26573437500*p**6*r**2*s**5 + 14785156250*p**3*q**2*r**2*s**5 - 52050781250*q**4*r**2*s**5 - 103062500000*p**4*r**3*s**5 - 281796875000*p*q**2*r**3*s**5 + 146875000000*p**2*r**4*s**5 - 37500000000*r**5*s**5 - 8789062500*p**6*q*s**6 - 3906250000*p**3*q**3*s**6 + 1464843750*q**5*s**6 + 102929687500*p**4*q*r*s**6 + 297119140625*p*q**3*r*s**6 - 217773437500*p**2*q*r**2*s**6 + 167968750000*q*r**3*s**6 + 10986328125*p**5*s**7 + 98876953125*p**2*q**2*s**7 - 188964843750*p**3*r*s**7 - 278320312500*q**2*r*s**7 + 517578125000*p*r**2*s**7 - 610351562500*p*q*s**8 + 762939453125*s**9 b[2][4] = -200*p**7*q**9 + 1850*p**4*q**11 + 21600*p*q**13 + 3200*p**8*q**7*r - 19200*p**5*q**9*r - 316350*p**2*q**11*r - 19050*p**9*q**5*r**2 + 37400*p**6*q**7*r**2 + 1759250*p**3*q**9*r**2 + 440100*q**11*r**2 + 48750*p**10*q**3*r**3 + 190200*p**7*q**5*r**3 - 4604200*p**4*q**7*r**3 - 6072800*p*q**9*r**3 - 43200*p**11*q*r**4 - 834500*p**8*q**3*r**4 + 4916000*p**5*q**5*r**4 + 27926850*p**2*q**7*r**4 + 969600*p**9*q*r**5 + 2467200*p**6*q**3*r**5 - 45393200*p**3*q**5*r**5 - 5399500*q**7*r**5 - 7283200*p**7*q*r**6 + 10536000*p**4*q**3*r**6 + 41656000*p*q**5*r**6 + 22784000*p**5*q*r**7 - 35200000*p**2*q**3*r**7 - 25600000*p**3*q*r**8 + 96000000*q**3*r**8 - 3000*p**9*q**6*s + 40400*p**6*q**8*s + 136550*p**3*q**10*s - 1647000*q**12*s + 40500*p**10*q**4*r*s - 173600*p**7*q**6*r*s - 126500*p**4*q**8*r*s + 23969250*p*q**10*r*s - 153900*p**11*q**2*r**2*s - 486150*p**8*q**4*r**2*s - 4115800*p**5*q**6*r**2*s - 112653250*p**2*q**8*r**2*s + 129600*p**12*r**3*s + 2683350*p**9*q**2*r**3*s + 10906650*p**6*q**4*r**3*s + 187289500*p**3*q**6*r**3*s + 44098750*q**8*r**3*s - 4384800*p**10*r**4*s - 35660800*p**7*q**2*r**4*s - 175420000*p**4*q**4*r**4*s - 426538750*p*q**6*r**4*s + 60857600*p**8*r**5*s + 349436000*p**5*q**2*r**5*s + 900600000*p**2*q**4*r**5*s - 429568000*p**6*r**6*s - 1511200000*p**3*q**2*r**6*s - 1286000000*q**4*r**6*s + 1472000000*p**4*r**7*s + 1440000000*p*q**2*r**7*s - 1920000000*p**2*r**8*s - 36450*p**11*q**3*s**2 - 188100*p**8*q**5*s**2 - 5504750*p**5*q**7*s**2 - 37968750*p**2*q**9*s**2 + 255150*p**12*q*r*s**2 + 2754000*p**9*q**3*r*s**2 + 49196500*p**6*q**5*r*s**2 + 323587500*p**3*q**7*r*s**2 - 83250000*q**9*r*s**2 - 465750*p**10*q*r**2*s**2 - 31881500*p**7*q**3*r**2*s**2 - 415585000*p**4*q**5*r**2*s**2 + 1054775000*p*q**7*r**2*s**2 - 96823500*p**8*q*r**3*s**2 - 701490000*p**5*q**3*r**3*s**2 - 2953531250*p**2*q**5*r**3*s**2 + 1454560000*p**6*q*r**4*s**2 + 7670500000*p**3*q**3*r**4*s**2 + 5661062500*q**5*r**4*s**2 - 7785000000*p**4*q*r**5*s**2 - 9450000000*p*q**3*r**5*s**2 + 14000000000*p**2*q*r**6*s**2 + 2400000000*q*r**7*s**2 - 437400*p**13*s**3 - 10145250*p**10*q**2*s**3 - 121912500*p**7*q**4*s**3 - 576531250*p**4*q**6*s**3 - 528593750*p*q**8*s**3 + 12939750*p**11*r*s**3 + 313368750*p**8*q**2*r*s**3 + 2171812500*p**5*q**4*r*s**3 + 2381718750*p**2*q**6*r*s**3 - 124638750*p**9*r**2*s**3 - 3001575000*p**6*q**2*r**2*s**3 - 12259375000*p**3*q**4*r**2*s**3 - 9985312500*q**6*r**2*s**3 + 384000000*p**7*r**3*s**3 + 13997500000*p**4*q**2*r**3*s**3 + 20749531250*p*q**4*r**3*s**3 - 553500000*p**5*r**4*s**3 - 41835000000*p**2*q**2*r**4*s**3 + 5420000000*p**3*r**5*s**3 - 16300000000*q**2*r**5*s**3 - 17600000000*p*r**6*s**3 - 7593750*p**9*q*s**4 + 289218750*p**6*q**3*s**4 + 3591406250*p**3*q**5*s**4 + 5992187500*q**7*s**4 + 658125000*p**7*q*r*s**4 - 269531250*p**4*q**3*r*s**4 - 15882812500*p*q**5*r*s**4 - 4785000000*p**5*q*r**2*s**4 + 54375781250*p**2*q**3*r**2*s**4 - 5668750000*p**3*q*r**3*s**4 + 35867187500*q**3*r**3*s**4 + 113875000000*p*q*r**4*s**4 - 544218750*p**8*s**5 - 5407031250*p**5*q**2*s**5 - 14277343750*p**2*q**4*s**5 + 5421093750*p**6*r*s**5 - 24941406250*p**3*q**2*r*s**5 - 25488281250*q**4*r*s**5 - 11500000000*p**4*r**2*s**5 - 231894531250*p*q**2*r**2*s**5 - 6250000000*p**2*r**3*s**5 - 43750000000*r**4*s**5 + 35449218750*p**4*q*s**6 + 137695312500*p*q**3*s**6 + 34667968750*p**2*q*r*s**6 + 202148437500*q*r**2*s**6 - 33691406250*p**3*s**7 - 214843750000*q**2*s**7 - 31738281250*p*r*s**7 b[2][3] = -800*p**5*q**9 - 5400*p**2*q**11 + 5800*p**6*q**7*r + 48750*p**3*q**9*r + 16200*q**11*r - 3000*p**7*q**5*r**2 - 108350*p**4*q**7*r**2 - 263250*p*q**9*r**2 - 60700*p**8*q**3*r**3 - 386250*p**5*q**5*r**3 + 253100*p**2*q**7*r**3 + 127800*p**9*q*r**4 + 2326700*p**6*q**3*r**4 + 6565550*p**3*q**5*r**4 - 705750*q**7*r**4 - 2903200*p**7*q*r**5 - 21218000*p**4*q**3*r**5 + 1057000*p*q**5*r**5 + 20368000*p**5*q*r**6 + 33000000*p**2*q**3*r**6 - 43200000*p**3*q*r**7 + 52000000*q**3*r**7 + 6200*p**7*q**6*s + 188250*p**4*q**8*s + 931500*p*q**10*s - 73800*p**8*q**4*r*s - 1466850*p**5*q**6*r*s - 6894000*p**2*q**8*r*s + 315900*p**9*q**2*r**2*s + 4547000*p**6*q**4*r**2*s + 20362500*p**3*q**6*r**2*s + 15018750*q**8*r**2*s - 653400*p**10*r**3*s - 13897550*p**7*q**2*r**3*s - 76757500*p**4*q**4*r**3*s - 124207500*p*q**6*r**3*s + 18567600*p**8*r**4*s + 175911000*p**5*q**2*r**4*s + 253787500*p**2*q**4*r**4*s - 183816000*p**6*r**5*s - 706900000*p**3*q**2*r**5*s - 665750000*q**4*r**5*s + 740000000*p**4*r**6*s + 890000000*p*q**2*r**6*s - 1040000000*p**2*r**7*s - 763000*p**6*q**5*s**2 - 12375000*p**3*q**7*s**2 - 40500000*q**9*s**2 + 364500*p**10*q*r*s**2 + 15537000*p**7*q**3*r*s**2 + 154392500*p**4*q**5*r*s**2 + 372206250*p*q**7*r*s**2 - 25481250*p**8*q*r**2*s**2 - 386300000*p**5*q**3*r**2*s**2 - 996343750*p**2*q**5*r**2*s**2 + 459872500*p**6*q*r**3*s**2 + 2943937500*p**3*q**3*r**3*s**2 + 2437781250*q**5*r**3*s**2 - 2883750000*p**4*q*r**4*s**2 - 4343750000*p*q**3*r**4*s**2 + 5495000000*p**2*q*r**5*s**2 + 1300000000*q*r**6*s**2 - 364500*p**11*s**3 - 13668750*p**8*q**2*s**3 - 113406250*p**5*q**4*s**3 - 159062500*p**2*q**6*s**3 + 13972500*p**9*r*s**3 + 61537500*p**6*q**2*r*s**3 - 1622656250*p**3*q**4*r*s**3 - 2720625000*q**6*r*s**3 - 201656250*p**7*r**2*s**3 + 1949687500*p**4*q**2*r**2*s**3 + 4979687500*p*q**4*r**2*s**3 + 497125000*p**5*r**3*s**3 - 11150625000*p**2*q**2*r**3*s**3 + 2982500000*p**3*r**4*s**3 - 6612500000*q**2*r**4*s**3 - 10450000000*p*r**5*s**3 + 126562500*p**7*q*s**4 + 1443750000*p**4*q**3*s**4 + 281250000*p*q**5*s**4 - 1648125000*p**5*q*r*s**4 + 11271093750*p**2*q**3*r*s**4 - 4785156250*p**3*q*r**2*s**4 + 8808593750*q**3*r**2*s**4 + 52390625000*p*q*r**3*s**4 - 611718750*p**6*s**5 - 13027343750*p**3*q**2*s**5 - 1464843750*q**4*s**5 + 6492187500*p**4*r*s**5 - 65351562500*p*q**2*r*s**5 - 13476562500*p**2*r**2*s**5 - 24218750000*r**3*s**5 + 41992187500*p**2*q*s**6 + 69824218750*q*r*s**6 - 34179687500*p*s**7 b[2][2] = -1000*p**6*q**7 - 5150*p**3*q**9 + 10800*q**11 + 11000*p**7*q**5*r + 66450*p**4*q**7*r - 127800*p*q**9*r - 41250*p**8*q**3*r**2 - 368400*p**5*q**5*r**2 + 204200*p**2*q**7*r**2 + 54000*p**9*q*r**3 + 1040950*p**6*q**3*r**3 + 2096500*p**3*q**5*r**3 + 200000*q**7*r**3 - 1140000*p**7*q*r**4 - 7691000*p**4*q**3*r**4 - 2281000*p*q**5*r**4 + 7296000*p**5*q*r**5 + 13300000*p**2*q**3*r**5 - 14400000*p**3*q*r**6 + 14000000*q**3*r**6 - 9000*p**8*q**4*s + 52100*p**5*q**6*s + 710250*p**2*q**8*s + 67500*p**9*q**2*r*s - 256100*p**6*q**4*r*s - 5753000*p**3*q**6*r*s + 292500*q**8*r*s - 162000*p**10*r**2*s - 1432350*p**7*q**2*r**2*s + 5410000*p**4*q**4*r**2*s - 7408750*p*q**6*r**2*s + 4401000*p**8*r**3*s + 24185000*p**5*q**2*r**3*s + 20781250*p**2*q**4*r**3*s - 43012000*p**6*r**4*s - 146300000*p**3*q**2*r**4*s - 165875000*q**4*r**4*s + 182000000*p**4*r**5*s + 250000000*p*q**2*r**5*s - 280000000*p**2*r**6*s + 60750*p**10*q*s**2 + 2414250*p**7*q**3*s**2 + 15770000*p**4*q**5*s**2 + 15825000*p*q**7*s**2 - 6021000*p**8*q*r*s**2 - 62252500*p**5*q**3*r*s**2 - 74718750*p**2*q**5*r*s**2 + 90888750*p**6*q*r**2*s**2 + 471312500*p**3*q**3*r**2*s**2 + 525875000*q**5*r**2*s**2 - 539375000*p**4*q*r**3*s**2 - 1030000000*p*q**3*r**3*s**2 + 1142500000*p**2*q*r**4*s**2 + 350000000*q*r**5*s**2 - 303750*p**9*s**3 - 35943750*p**6*q**2*s**3 - 331875000*p**3*q**4*s**3 - 505937500*q**6*s**3 + 8437500*p**7*r*s**3 + 530781250*p**4*q**2*r*s**3 + 1150312500*p*q**4*r*s**3 - 154500000*p**5*r**2*s**3 - 2059062500*p**2*q**2*r**2*s**3 + 1150000000*p**3*r**3*s**3 - 1343750000*q**2*r**3*s**3 - 2900000000*p*r**4*s**3 + 30937500*p**5*q*s**4 + 1166406250*p**2*q**3*s**4 - 1496875000*p**3*q*r*s**4 + 1296875000*q**3*r*s**4 + 10640625000*p*q*r**2*s**4 - 281250000*p**4*s**5 - 9746093750*p*q**2*s**5 + 1269531250*p**2*r*s**5 - 7421875000*r**2*s**5 + 15625000000*q*s**6 b[2][1] = -1600*p**4*q**7 - 10800*p*q**9 + 9800*p**5*q**5*r + 80550*p**2*q**7*r - 4600*p**6*q**3*r**2 - 112700*p**3*q**5*r**2 + 40500*q**7*r**2 - 34200*p**7*q*r**3 - 279500*p**4*q**3*r**3 - 665750*p*q**5*r**3 + 632000*p**5*q*r**4 + 3200000*p**2*q**3*r**4 - 2800000*p**3*q*r**5 + 3000000*q**3*r**5 - 18600*p**6*q**4*s - 51750*p**3*q**6*s + 405000*q**8*s + 21600*p**7*q**2*r*s - 122500*p**4*q**4*r*s - 2891250*p*q**6*r*s + 156600*p**8*r**2*s + 1569750*p**5*q**2*r**2*s + 6943750*p**2*q**4*r**2*s - 3774000*p**6*r**3*s - 27100000*p**3*q**2*r**3*s - 30187500*q**4*r**3*s + 28000000*p**4*r**4*s + 52500000*p*q**2*r**4*s - 60000000*p**2*r**5*s - 81000*p**8*q*s**2 - 240000*p**5*q**3*s**2 + 937500*p**2*q**5*s**2 + 3273750*p**6*q*r*s**2 + 30406250*p**3*q**3*r*s**2 + 55687500*q**5*r*s**2 - 42187500*p**4*q*r**2*s**2 - 112812500*p*q**3*r**2*s**2 + 152500000*p**2*q*r**3*s**2 + 75000000*q*r**4*s**2 - 4218750*p**4*q**2*s**3 + 15156250*p*q**4*s**3 + 5906250*p**5*r*s**3 - 206562500*p**2*q**2*r*s**3 + 107500000*p**3*r**2*s**3 - 159375000*q**2*r**2*s**3 - 612500000*p*r**3*s**3 + 135937500*p**3*q*s**4 + 46875000*q**3*s**4 + 1175781250*p*q*r*s**4 - 292968750*p**2*s**5 - 1367187500*r*s**5 b[2][0] = -800*p**5*q**5 - 5400*p**2*q**7 + 6000*p**6*q**3*r + 51700*p**3*q**5*r + 27000*q**7*r - 10800*p**7*q*r**2 - 163250*p**4*q**3*r**2 - 285750*p*q**5*r**2 + 192000*p**5*q*r**3 + 1000000*p**2*q**3*r**3 - 800000*p**3*q*r**4 + 500000*q**3*r**4 - 10800*p**7*q**2*s - 57500*p**4*q**4*s + 67500*p*q**6*s + 32400*p**8*r*s + 279000*p**5*q**2*r*s - 131250*p**2*q**4*r*s - 729000*p**6*r**2*s - 4100000*p**3*q**2*r**2*s - 5343750*q**4*r**2*s + 5000000*p**4*r**3*s + 10000000*p*q**2*r**3*s - 10000000*p**2*r**4*s + 641250*p**6*q*s**2 + 5812500*p**3*q**3*s**2 + 10125000*q**5*s**2 - 7031250*p**4*q*r*s**2 - 20625000*p*q**3*r*s**2 + 17500000*p**2*q*r**2*s**2 + 12500000*q*r**3*s**2 - 843750*p**5*s**3 - 19375000*p**2*q**2*s**3 + 30000000*p**3*r*s**3 - 20312500*q**2*r*s**3 - 112500000*p*r**2*s**3 + 183593750*p*q*s**4 - 292968750*s**5 b[3][5] = 500*p**11*q**6 + 9875*p**8*q**8 + 42625*p**5*q**10 - 35000*p**2*q**12 - 4500*p**12*q**4*r - 108375*p**9*q**6*r - 516750*p**6*q**8*r + 1110500*p**3*q**10*r + 2730000*q**12*r + 10125*p**13*q**2*r**2 + 358250*p**10*q**4*r**2 + 1908625*p**7*q**6*r**2 - 11744250*p**4*q**8*r**2 - 43383250*p*q**10*r**2 - 313875*p**11*q**2*r**3 - 2074875*p**8*q**4*r**3 + 52094750*p**5*q**6*r**3 + 264567500*p**2*q**8*r**3 + 796125*p**9*q**2*r**4 - 92486250*p**6*q**4*r**4 - 757957500*p**3*q**6*r**4 - 29354375*q**8*r**4 + 60970000*p**7*q**2*r**5 + 1112462500*p**4*q**4*r**5 + 571094375*p*q**6*r**5 - 685290000*p**5*q**2*r**6 - 2037800000*p**2*q**4*r**6 + 2279600000*p**3*q**2*r**7 + 849000000*q**4*r**7 - 1480000000*p*q**2*r**8 + 13500*p**13*q**3*s + 363000*p**10*q**5*s + 2861250*p**7*q**7*s + 8493750*p**4*q**9*s + 17031250*p*q**11*s - 60750*p**14*q*r*s - 2319750*p**11*q**3*r*s - 22674250*p**8*q**5*r*s - 74368750*p**5*q**7*r*s - 170578125*p**2*q**9*r*s + 2760750*p**12*q*r**2*s + 46719000*p**9*q**3*r**2*s + 163356375*p**6*q**5*r**2*s + 360295625*p**3*q**7*r**2*s - 195990625*q**9*r**2*s - 37341750*p**10*q*r**3*s - 194739375*p**7*q**3*r**3*s - 105463125*p**4*q**5*r**3*s - 415825000*p*q**7*r**3*s + 90180000*p**8*q*r**4*s - 990552500*p**5*q**3*r**4*s + 3519212500*p**2*q**5*r**4*s + 1112220000*p**6*q*r**5*s - 4508750000*p**3*q**3*r**5*s - 8159500000*q**5*r**5*s - 4356000000*p**4*q*r**6*s + 14615000000*p*q**3*r**6*s - 2160000000*p**2*q*r**7*s + 91125*p**15*s**2 + 3290625*p**12*q**2*s**2 + 35100000*p**9*q**4*s**2 + 175406250*p**6*q**6*s**2 + 629062500*p**3*q**8*s**2 + 910937500*q**10*s**2 - 5710500*p**13*r*s**2 - 100423125*p**10*q**2*r*s**2 - 604743750*p**7*q**4*r*s**2 - 2954843750*p**4*q**6*r*s**2 - 4587578125*p*q**8*r*s**2 + 116194500*p**11*r**2*s**2 + 1280716250*p**8*q**2*r**2*s**2 + 7401190625*p**5*q**4*r**2*s**2 + 11619937500*p**2*q**6*r**2*s**2 - 952173125*p**9*r**3*s**2 - 6519712500*p**6*q**2*r**3*s**2 - 10238593750*p**3*q**4*r**3*s**2 + 29984609375*q**6*r**3*s**2 + 2558300000*p**7*r**4*s**2 + 16225000000*p**4*q**2*r**4*s**2 - 64994140625*p*q**4*r**4*s**2 + 4202250000*p**5*r**5*s**2 + 46925000000*p**2*q**2*r**5*s**2 - 28950000000*p**3*r**6*s**2 - 1000000000*q**2*r**6*s**2 + 37000000000*p*r**7*s**2 - 48093750*p**11*q*s**3 - 673359375*p**8*q**3*s**3 - 2170312500*p**5*q**5*s**3 - 2466796875*p**2*q**7*s**3 + 647578125*p**9*q*r*s**3 + 597031250*p**6*q**3*r*s**3 - 7542578125*p**3*q**5*r*s**3 - 41125000000*q**7*r*s**3 - 2175828125*p**7*q*r**2*s**3 - 7101562500*p**4*q**3*r**2*s**3 + 100596875000*p*q**5*r**2*s**3 - 8984687500*p**5*q*r**3*s**3 - 120070312500*p**2*q**3*r**3*s**3 + 57343750000*p**3*q*r**4*s**3 + 9500000000*q**3*r**4*s**3 - 342875000000*p*q*r**5*s**3 + 400781250*p**10*s**4 + 8531250000*p**7*q**2*s**4 + 34033203125*p**4*q**4*s**4 + 42724609375*p*q**6*s**4 - 6289453125*p**8*r*s**4 - 24037109375*p**5*q**2*r*s**4 - 62626953125*p**2*q**4*r*s**4 + 17299218750*p**6*r**2*s**4 + 108357421875*p**3*q**2*r**2*s**4 - 55380859375*q**4*r**2*s**4 + 105648437500*p**4*r**3*s**4 + 1204228515625*p*q**2*r**3*s**4 - 365000000000*p**2*r**4*s**4 + 184375000000*r**5*s**4 - 32080078125*p**6*q*s**5 - 98144531250*p**3*q**3*s**5 + 93994140625*q**5*s**5 - 178955078125*p**4*q*r*s**5 - 1299804687500*p*q**3*r*s**5 + 332421875000*p**2*q*r**2*s**5 - 1195312500000*q*r**3*s**5 + 72021484375*p**5*s**6 + 323486328125*p**2*q**2*s**6 + 682373046875*p**3*r*s**6 + 2447509765625*q**2*r*s**6 - 3011474609375*p*r**2*s**6 + 3051757812500*p*q*s**7 - 7629394531250*s**8 b[3][4] = 1500*p**9*q**6 + 69625*p**6*q**8 + 590375*p**3*q**10 + 1035000*q**12 - 13500*p**10*q**4*r - 760625*p**7*q**6*r - 7904500*p**4*q**8*r - 18169250*p*q**10*r + 30375*p**11*q**2*r**2 + 2628625*p**8*q**4*r**2 + 37879000*p**5*q**6*r**2 + 121367500*p**2*q**8*r**2 - 2699250*p**9*q**2*r**3 - 76776875*p**6*q**4*r**3 - 403583125*p**3*q**6*r**3 - 78865625*q**8*r**3 + 60907500*p**7*q**2*r**4 + 735291250*p**4*q**4*r**4 + 781142500*p*q**6*r**4 - 558270000*p**5*q**2*r**5 - 2150725000*p**2*q**4*r**5 + 2015400000*p**3*q**2*r**6 + 1181000000*q**4*r**6 - 2220000000*p*q**2*r**7 + 40500*p**11*q**3*s + 1376500*p**8*q**5*s + 9953125*p**5*q**7*s + 9765625*p**2*q**9*s - 182250*p**12*q*r*s - 8859000*p**9*q**3*r*s - 82854500*p**6*q**5*r*s - 71511250*p**3*q**7*r*s + 273631250*q**9*r*s + 10233000*p**10*q*r**2*s + 179627500*p**7*q**3*r**2*s + 25164375*p**4*q**5*r**2*s - 2927290625*p*q**7*r**2*s - 171305000*p**8*q*r**3*s - 544768750*p**5*q**3*r**3*s + 7583437500*p**2*q**5*r**3*s + 1139860000*p**6*q*r**4*s - 6489375000*p**3*q**3*r**4*s - 9625375000*q**5*r**4*s - 1838000000*p**4*q*r**5*s + 19835000000*p*q**3*r**5*s - 3240000000*p**2*q*r**6*s + 273375*p**13*s**2 + 9753750*p**10*q**2*s**2 + 82575000*p**7*q**4*s**2 + 202265625*p**4*q**6*s**2 + 556093750*p*q**8*s**2 - 11552625*p**11*r*s**2 - 115813125*p**8*q**2*r*s**2 + 630590625*p**5*q**4*r*s**2 + 1347015625*p**2*q**6*r*s**2 + 157578750*p**9*r**2*s**2 - 689206250*p**6*q**2*r**2*s**2 - 4299609375*p**3*q**4*r**2*s**2 + 23896171875*q**6*r**2*s**2 - 1022437500*p**7*r**3*s**2 + 6648125000*p**4*q**2*r**3*s**2 - 52895312500*p*q**4*r**3*s**2 + 4401750000*p**5*r**4*s**2 + 26500000000*p**2*q**2*r**4*s**2 - 22125000000*p**3*r**5*s**2 - 1500000000*q**2*r**5*s**2 + 55500000000*p*r**6*s**2 - 137109375*p**9*q*s**3 - 1955937500*p**6*q**3*s**3 - 6790234375*p**3*q**5*s**3 - 16996093750*q**7*s**3 + 2146218750*p**7*q*r*s**3 + 6570312500*p**4*q**3*r*s**3 + 39918750000*p*q**5*r*s**3 - 7673281250*p**5*q*r**2*s**3 - 52000000000*p**2*q**3*r**2*s**3 + 50796875000*p**3*q*r**3*s**3 + 18750000000*q**3*r**3*s**3 - 399875000000*p*q*r**4*s**3 + 780468750*p**8*s**4 + 14455078125*p**5*q**2*s**4 + 10048828125*p**2*q**4*s**4 - 15113671875*p**6*r*s**4 + 39298828125*p**3*q**2*r*s**4 - 52138671875*q**4*r*s**4 + 45964843750*p**4*r**2*s**4 + 914414062500*p*q**2*r**2*s**4 + 1953125000*p**2*r**3*s**4 + 334375000000*r**4*s**4 - 149169921875*p**4*q*s**5 - 459716796875*p*q**3*s**5 - 325585937500*p**2*q*r*s**5 - 1462890625000*q*r**2*s**5 + 296630859375*p**3*s**6 + 1324462890625*q**2*s**6 + 307617187500*p*r*s**6 b[3][3] = -20750*p**7*q**6 - 290125*p**4*q**8 - 993000*p*q**10 + 146125*p**8*q**4*r + 2721500*p**5*q**6*r + 11833750*p**2*q**8*r - 237375*p**9*q**2*r**2 - 8167500*p**6*q**4*r**2 - 54605625*p**3*q**6*r**2 - 23802500*q**8*r**2 + 8927500*p**7*q**2*r**3 + 131184375*p**4*q**4*r**3 + 254695000*p*q**6*r**3 - 121561250*p**5*q**2*r**4 - 728003125*p**2*q**4*r**4 + 702550000*p**3*q**2*r**5 + 597312500*q**4*r**5 - 1202500000*p*q**2*r**6 - 194625*p**9*q**3*s - 1568875*p**6*q**5*s + 9685625*p**3*q**7*s + 74662500*q**9*s + 327375*p**10*q*r*s + 1280000*p**7*q**3*r*s - 123703750*p**4*q**5*r*s - 850121875*p*q**7*r*s - 7436250*p**8*q*r**2*s + 164820000*p**5*q**3*r**2*s + 2336659375*p**2*q**5*r**2*s + 32202500*p**6*q*r**3*s - 2429765625*p**3*q**3*r**3*s - 4318609375*q**5*r**3*s + 148000000*p**4*q*r**4*s + 9902812500*p*q**3*r**4*s - 1755000000*p**2*q*r**5*s + 1154250*p**11*s**2 + 36821250*p**8*q**2*s**2 + 372825000*p**5*q**4*s**2 + 1170921875*p**2*q**6*s**2 - 38913750*p**9*r*s**2 - 797071875*p**6*q**2*r*s**2 - 2848984375*p**3*q**4*r*s**2 + 7651406250*q**6*r*s**2 + 415068750*p**7*r**2*s**2 + 3151328125*p**4*q**2*r**2*s**2 - 17696875000*p*q**4*r**2*s**2 - 725968750*p**5*r**3*s**2 + 5295312500*p**2*q**2*r**3*s**2 - 8581250000*p**3*r**4*s**2 - 812500000*q**2*r**4*s**2 + 30062500000*p*r**5*s**2 - 110109375*p**7*q*s**3 - 1976562500*p**4*q**3*s**3 - 6329296875*p*q**5*s**3 + 2256328125*p**5*q*r*s**3 + 8554687500*p**2*q**3*r*s**3 + 12947265625*p**3*q*r**2*s**3 + 7984375000*q**3*r**2*s**3 - 167039062500*p*q*r**3*s**3 + 1181250000*p**6*s**4 + 17873046875*p**3*q**2*s**4 - 20449218750*q**4*s**4 - 16265625000*p**4*r*s**4 + 260869140625*p*q**2*r*s**4 + 21025390625*p**2*r**2*s**4 + 207617187500*r**3*s**4 - 207177734375*p**2*q*s**5 - 615478515625*q*r*s**5 + 301513671875*p*s**6 b[3][2] = 53125*p**5*q**6 + 425000*p**2*q**8 - 394375*p**6*q**4*r - 4301875*p**3*q**6*r - 3225000*q**8*r + 851250*p**7*q**2*r**2 + 16910625*p**4*q**4*r**2 + 44210000*p*q**6*r**2 - 20474375*p**5*q**2*r**3 - 147190625*p**2*q**4*r**3 + 163975000*p**3*q**2*r**4 + 156812500*q**4*r**4 - 323750000*p*q**2*r**5 - 99375*p**7*q**3*s - 6395000*p**4*q**5*s - 49243750*p*q**7*s - 1164375*p**8*q*r*s + 4465625*p**5*q**3*r*s + 205546875*p**2*q**5*r*s + 12163750*p**6*q*r**2*s - 315546875*p**3*q**3*r**2*s - 946453125*q**5*r**2*s - 23500000*p**4*q*r**3*s + 2313437500*p*q**3*r**3*s - 472500000*p**2*q*r**4*s + 1316250*p**9*s**2 + 22715625*p**6*q**2*s**2 + 206953125*p**3*q**4*s**2 + 1220000000*q**6*s**2 - 20953125*p**7*r*s**2 - 277656250*p**4*q**2*r*s**2 - 3317187500*p*q**4*r*s**2 + 293734375*p**5*r**2*s**2 + 1351562500*p**2*q**2*r**2*s**2 - 2278125000*p**3*r**3*s**2 - 218750000*q**2*r**3*s**2 + 8093750000*p*r**4*s**2 - 9609375*p**5*q*s**3 + 240234375*p**2*q**3*s**3 + 2310546875*p**3*q*r*s**3 + 1171875000*q**3*r*s**3 - 33460937500*p*q*r**2*s**3 + 2185546875*p**4*s**4 + 32578125000*p*q**2*s**4 - 8544921875*p**2*r*s**4 + 58398437500*r**2*s**4 - 114013671875*q*s**5 b[3][1] = -16250*p**6*q**4 - 191875*p**3*q**6 - 495000*q**8 + 73125*p**7*q**2*r + 1437500*p**4*q**4*r + 5866250*p*q**6*r - 2043125*p**5*q**2*r**2 - 17218750*p**2*q**4*r**2 + 19106250*p**3*q**2*r**3 + 34015625*q**4*r**3 - 69375000*p*q**2*r**4 - 219375*p**8*q*s - 2846250*p**5*q**3*s - 8021875*p**2*q**5*s + 3420000*p**6*q*r*s - 1640625*p**3*q**3*r*s - 152468750*q**5*r*s + 3062500*p**4*q*r**2*s + 381171875*p*q**3*r**2*s - 101250000*p**2*q*r**3*s + 2784375*p**7*s**2 + 43515625*p**4*q**2*s**2 + 115625000*p*q**4*s**2 - 48140625*p**5*r*s**2 - 307421875*p**2*q**2*r*s**2 - 25781250*p**3*r**2*s**2 - 46875000*q**2*r**2*s**2 + 1734375000*p*r**3*s**2 - 128906250*p**3*q*s**3 + 339843750*q**3*s**3 - 4583984375*p*q*r*s**3 + 2236328125*p**2*s**4 + 12255859375*r*s**4 b[3][0] = 31875*p**4*q**4 + 255000*p*q**6 - 82500*p**5*q**2*r - 1106250*p**2*q**4*r + 1653125*p**3*q**2*r**2 + 5187500*q**4*r**2 - 11562500*p*q**2*r**3 - 118125*p**6*q*s - 3593750*p**3*q**3*s - 23812500*q**5*s + 4656250*p**4*q*r*s + 67109375*p*q**3*r*s - 16875000*p**2*q*r**2*s - 984375*p**5*s**2 - 19531250*p**2*q**2*s**2 - 37890625*p**3*r*s**2 - 7812500*q**2*r*s**2 + 289062500*p*r**2*s**2 - 529296875*p*q*s**3 + 2343750000*s**4 b[4][5] = 600*p**10*q**10 + 13850*p**7*q**12 + 106150*p**4*q**14 + 270000*p*q**16 - 9300*p**11*q**8*r - 234075*p**8*q**10*r - 1942825*p**5*q**12*r - 5319900*p**2*q**14*r + 52050*p**12*q**6*r**2 + 1481025*p**9*q**8*r**2 + 13594450*p**6*q**10*r**2 + 40062750*p**3*q**12*r**2 - 3569400*q**14*r**2 - 122175*p**13*q**4*r**3 - 4260350*p**10*q**6*r**3 - 45052375*p**7*q**8*r**3 - 142634900*p**4*q**10*r**3 + 54186350*p*q**12*r**3 + 97200*p**14*q**2*r**4 + 5284225*p**11*q**4*r**4 + 70389525*p**8*q**6*r**4 + 232732850*p**5*q**8*r**4 - 318849400*p**2*q**10*r**4 - 2046000*p**12*q**2*r**5 - 43874125*p**9*q**4*r**5 - 107411850*p**6*q**6*r**5 + 948310700*p**3*q**8*r**5 - 34763575*q**10*r**5 + 5915600*p**10*q**2*r**6 - 115887800*p**7*q**4*r**6 - 1649542400*p**4*q**6*r**6 + 224468875*p*q**8*r**6 + 120252800*p**8*q**2*r**7 + 1779902000*p**5*q**4*r**7 - 288250000*p**2*q**6*r**7 - 915200000*p**6*q**2*r**8 - 1164000000*p**3*q**4*r**8 - 444200000*q**6*r**8 + 2502400000*p**4*q**2*r**9 + 1984000000*p*q**4*r**9 - 2880000000*p**2*q**2*r**10 + 20700*p**12*q**7*s + 551475*p**9*q**9*s + 5194875*p**6*q**11*s + 18985000*p**3*q**13*s + 16875000*q**15*s - 218700*p**13*q**5*r*s - 6606475*p**10*q**7*r*s - 69770850*p**7*q**9*r*s - 285325500*p**4*q**11*r*s - 292005000*p*q**13*r*s + 694575*p**14*q**3*r**2*s + 26187750*p**11*q**5*r**2*s + 328992825*p**8*q**7*r**2*s + 1573292400*p**5*q**9*r**2*s + 1930043875*p**2*q**11*r**2*s - 583200*p**15*q*r**3*s - 37263225*p**12*q**3*r**3*s - 638579425*p**9*q**5*r**3*s - 3920212225*p**6*q**7*r**3*s - 6327336875*p**3*q**9*r**3*s + 440969375*q**11*r**3*s + 13446000*p**13*q*r**4*s + 462330325*p**10*q**3*r**4*s + 4509088275*p**7*q**5*r**4*s + 11709795625*p**4*q**7*r**4*s - 3579565625*p*q**9*r**4*s - 85033600*p**11*q*r**5*s - 2136801600*p**8*q**3*r**5*s - 12221575800*p**5*q**5*r**5*s + 9431044375*p**2*q**7*r**5*s + 10643200*p**9*q*r**6*s + 4565594000*p**6*q**3*r**6*s - 1778590000*p**3*q**5*r**6*s + 4842175000*q**7*r**6*s + 712320000*p**7*q*r**7*s - 16182000000*p**4*q**3*r**7*s - 21918000000*p*q**5*r**7*s - 742400000*p**5*q*r**8*s + 31040000000*p**2*q**3*r**8*s + 1280000000*p**3*q*r**9*s + 4800000000*q**3*r**9*s + 230850*p**14*q**4*s**2 + 7373250*p**11*q**6*s**2 + 85045625*p**8*q**8*s**2 + 399140625*p**5*q**10*s**2 + 565031250*p**2*q**12*s**2 - 1257525*p**15*q**2*r*s**2 - 52728975*p**12*q**4*r*s**2 - 743466375*p**9*q**6*r*s**2 - 4144915000*p**6*q**8*r*s**2 - 7102690625*p**3*q**10*r*s**2 - 1389937500*q**12*r*s**2 + 874800*p**16*r**2*s**2 + 89851275*p**13*q**2*r**2*s**2 + 1897236775*p**10*q**4*r**2*s**2 + 14144163000*p**7*q**6*r**2*s**2 + 31942921875*p**4*q**8*r**2*s**2 + 13305118750*p*q**10*r**2*s**2 - 23004000*p**14*r**3*s**2 - 1450715475*p**11*q**2*r**3*s**2 - 19427105000*p**8*q**4*r**3*s**2 - 70634028750*p**5*q**6*r**3*s**2 - 47854218750*p**2*q**8*r**3*s**2 + 204710400*p**12*r**4*s**2 + 10875135000*p**9*q**2*r**4*s**2 + 83618806250*p**6*q**4*r**4*s**2 + 62744500000*p**3*q**6*r**4*s**2 - 19806718750*q**8*r**4*s**2 - 757094800*p**10*r**5*s**2 - 37718030000*p**7*q**2*r**5*s**2 - 22479500000*p**4*q**4*r**5*s**2 + 91556093750*p*q**6*r**5*s**2 + 2306320000*p**8*r**6*s**2 + 55539600000*p**5*q**2*r**6*s**2 - 112851250000*p**2*q**4*r**6*s**2 - 10720000000*p**6*r**7*s**2 - 64720000000*p**3*q**2*r**7*s**2 - 59925000000*q**4*r**7*s**2 + 28000000000*p**4*r**8*s**2 + 28000000000*p*q**2*r**8*s**2 - 24000000000*p**2*r**9*s**2 + 820125*p**16*q*s**3 + 36804375*p**13*q**3*s**3 + 552225000*p**10*q**5*s**3 + 3357593750*p**7*q**7*s**3 + 7146562500*p**4*q**9*s**3 + 3851562500*p*q**11*s**3 - 92400750*p**14*q*r*s**3 - 2350175625*p**11*q**3*r*s**3 - 19470640625*p**8*q**5*r*s**3 - 52820593750*p**5*q**7*r*s**3 - 45447734375*p**2*q**9*r*s**3 + 1824363000*p**12*q*r**2*s**3 + 31435234375*p**9*q**3*r**2*s**3 + 141717537500*p**6*q**5*r**2*s**3 + 228370781250*p**3*q**7*r**2*s**3 + 34610078125*q**9*r**2*s**3 - 17591825625*p**10*q*r**3*s**3 - 188927187500*p**7*q**3*r**3*s**3 - 502088984375*p**4*q**5*r**3*s**3 - 187849296875*p*q**7*r**3*s**3 + 75577750000*p**8*q*r**4*s**3 + 342800000000*p**5*q**3*r**4*s**3 + 295384296875*p**2*q**5*r**4*s**3 - 107681250000*p**6*q*r**5*s**3 + 53330000000*p**3*q**3*r**5*s**3 + 271586875000*q**5*r**5*s**3 - 26410000000*p**4*q*r**6*s**3 - 188200000000*p*q**3*r**6*s**3 + 92000000000*p**2*q*r**7*s**3 + 120000000000*q*r**8*s**3 + 47840625*p**15*s**4 + 1150453125*p**12*q**2*s**4 + 9229453125*p**9*q**4*s**4 + 24954687500*p**6*q**6*s**4 + 22978515625*p**3*q**8*s**4 + 1367187500*q**10*s**4 - 1193737500*p**13*r*s**4 - 20817843750*p**10*q**2*r*s**4 - 98640000000*p**7*q**4*r*s**4 - 225767187500*p**4*q**6*r*s**4 - 74707031250*p*q**8*r*s**4 + 13431318750*p**11*r**2*s**4 + 188709843750*p**8*q**2*r**2*s**4 + 875157656250*p**5*q**4*r**2*s**4 + 593812890625*p**2*q**6*r**2*s**4 - 69869296875*p**9*r**3*s**4 - 854811093750*p**6*q**2*r**3*s**4 - 1730658203125*p**3*q**4*r**3*s**4 - 570867187500*q**6*r**3*s**4 + 162075625000*p**7*r**4*s**4 + 1536375000000*p**4*q**2*r**4*s**4 + 765156250000*p*q**4*r**4*s**4 - 165988750000*p**5*r**5*s**4 - 728968750000*p**2*q**2*r**5*s**4 + 121500000000*p**3*r**6*s**4 - 1039375000000*q**2*r**6*s**4 - 100000000000*p*r**7*s**4 - 379687500*p**11*q*s**5 - 11607421875*p**8*q**3*s**5 - 20830078125*p**5*q**5*s**5 - 33691406250*p**2*q**7*s**5 - 41491406250*p**9*q*r*s**5 - 419054687500*p**6*q**3*r*s**5 - 129511718750*p**3*q**5*r*s**5 + 311767578125*q**7*r*s**5 + 620116015625*p**7*q*r**2*s**5 + 1154687500000*p**4*q**3*r**2*s**5 + 36455078125*p*q**5*r**2*s**5 - 2265953125000*p**5*q*r**3*s**5 - 1509521484375*p**2*q**3*r**3*s**5 + 2530468750000*p**3*q*r**4*s**5 + 3259765625000*q**3*r**4*s**5 + 93750000000*p*q*r**5*s**5 + 23730468750*p**10*s**6 + 243603515625*p**7*q**2*s**6 + 341552734375*p**4*q**4*s**6 - 12207031250*p*q**6*s**6 - 357099609375*p**8*r*s**6 - 298193359375*p**5*q**2*r*s**6 + 406738281250*p**2*q**4*r*s**6 + 1615683593750*p**6*r**2*s**6 + 558593750000*p**3*q**2*r**2*s**6 - 2811035156250*q**4*r**2*s**6 - 2960937500000*p**4*r**3*s**6 - 3802246093750*p*q**2*r**3*s**6 + 2347656250000*p**2*r**4*s**6 - 671875000000*r**5*s**6 - 651855468750*p**6*q*s**7 - 1458740234375*p**3*q**3*s**7 - 152587890625*q**5*s**7 + 1628417968750*p**4*q*r*s**7 + 3948974609375*p*q**3*r*s**7 - 916748046875*p**2*q*r**2*s**7 + 1611328125000*q*r**3*s**7 + 640869140625*p**5*s**8 + 1068115234375*p**2*q**2*s**8 - 2044677734375*p**3*r*s**8 - 3204345703125*q**2*r*s**8 + 1739501953125*p*r**2*s**8 b[4][4] = -600*p**11*q**8 - 14050*p**8*q**10 - 109100*p**5*q**12 - 280800*p**2*q**14 + 7200*p**12*q**6*r + 188700*p**9*q**8*r + 1621725*p**6*q**10*r + 4577075*p**3*q**12*r + 5400*q**14*r - 28350*p**13*q**4*r**2 - 910600*p**10*q**6*r**2 - 9237975*p**7*q**8*r**2 - 30718900*p**4*q**10*r**2 - 5575950*p*q**12*r**2 + 36450*p**14*q**2*r**3 + 1848125*p**11*q**4*r**3 + 25137775*p**8*q**6*r**3 + 109591450*p**5*q**8*r**3 + 70627650*p**2*q**10*r**3 - 1317150*p**12*q**2*r**4 - 32857100*p**9*q**4*r**4 - 219125575*p**6*q**6*r**4 - 327565875*p**3*q**8*r**4 - 13011875*q**10*r**4 + 16484150*p**10*q**2*r**5 + 222242250*p**7*q**4*r**5 + 642173750*p**4*q**6*r**5 + 101263750*p*q**8*r**5 - 79345000*p**8*q**2*r**6 - 433180000*p**5*q**4*r**6 - 93731250*p**2*q**6*r**6 - 74300000*p**6*q**2*r**7 - 1057900000*p**3*q**4*r**7 - 591175000*q**6*r**7 + 1891600000*p**4*q**2*r**8 + 2796000000*p*q**4*r**8 - 4320000000*p**2*q**2*r**9 - 16200*p**13*q**5*s - 359500*p**10*q**7*s - 2603825*p**7*q**9*s - 4590375*p**4*q**11*s + 12352500*p*q**13*s + 121500*p**14*q**3*r*s + 3227400*p**11*q**5*r*s + 27301725*p**8*q**7*r*s + 59480975*p**5*q**9*r*s - 137308875*p**2*q**11*r*s - 218700*p**15*q*r**2*s - 8903925*p**12*q**3*r**2*s - 100918225*p**9*q**5*r**2*s - 325291300*p**6*q**7*r**2*s + 365705000*p**3*q**9*r**2*s + 94342500*q**11*r**2*s + 7632900*p**13*q*r**3*s + 162995400*p**10*q**3*r**3*s + 974558975*p**7*q**5*r**3*s + 930991250*p**4*q**7*r**3*s - 495368750*p*q**9*r**3*s - 97344900*p**11*q*r**4*s - 1406739250*p**8*q**3*r**4*s - 5572526250*p**5*q**5*r**4*s - 1903987500*p**2*q**7*r**4*s + 678550000*p**9*q*r**5*s + 8176215000*p**6*q**3*r**5*s + 18082050000*p**3*q**5*r**5*s + 5435843750*q**7*r**5*s - 2979800000*p**7*q*r**6*s - 29163500000*p**4*q**3*r**6*s - 27417500000*p*q**5*r**6*s + 6282400000*p**5*q*r**7*s + 48690000000*p**2*q**3*r**7*s - 2880000000*p**3*q*r**8*s + 7200000000*q**3*r**8*s - 109350*p**15*q**2*s**2 - 2405700*p**12*q**4*s**2 - 16125250*p**9*q**6*s**2 - 4930000*p**6*q**8*s**2 + 201150000*p**3*q**10*s**2 - 243000000*q**12*s**2 + 328050*p**16*r*s**2 + 10552275*p**13*q**2*r*s**2 + 88019100*p**10*q**4*r*s**2 - 4208625*p**7*q**6*r*s**2 - 1920390625*p**4*q**8*r*s**2 + 1759537500*p*q**10*r*s**2 - 11955600*p**14*r**2*s**2 - 196375050*p**11*q**2*r**2*s**2 - 555196250*p**8*q**4*r**2*s**2 + 4213270000*p**5*q**6*r**2*s**2 - 157468750*p**2*q**8*r**2*s**2 + 162656100*p**12*r**3*s**2 + 1880870000*p**9*q**2*r**3*s**2 + 753684375*p**6*q**4*r**3*s**2 - 25423062500*p**3*q**6*r**3*s**2 - 14142031250*q**8*r**3*s**2 - 1251948750*p**10*r**4*s**2 - 12524475000*p**7*q**2*r**4*s**2 + 18067656250*p**4*q**4*r**4*s**2 + 60531875000*p*q**6*r**4*s**2 + 6827725000*p**8*r**5*s**2 + 57157000000*p**5*q**2*r**5*s**2 - 75844531250*p**2*q**4*r**5*s**2 - 24452500000*p**6*r**6*s**2 - 144950000000*p**3*q**2*r**6*s**2 - 82109375000*q**4*r**6*s**2 + 46950000000*p**4*r**7*s**2 + 60000000000*p*q**2*r**7*s**2 - 36000000000*p**2*r**8*s**2 + 1549125*p**14*q*s**3 + 51873750*p**11*q**3*s**3 + 599781250*p**8*q**5*s**3 + 2421156250*p**5*q**7*s**3 - 1693515625*p**2*q**9*s**3 - 104884875*p**12*q*r*s**3 - 1937437500*p**9*q**3*r*s**3 - 11461053125*p**6*q**5*r*s**3 + 10299375000*p**3*q**7*r*s**3 + 10551250000*q**9*r*s**3 + 1336263750*p**10*q*r**2*s**3 + 23737250000*p**7*q**3*r**2*s**3 + 57136718750*p**4*q**5*r**2*s**3 - 8288906250*p*q**7*r**2*s**3 - 10907218750*p**8*q*r**3*s**3 - 160615000000*p**5*q**3*r**3*s**3 - 111134687500*p**2*q**5*r**3*s**3 + 46743125000*p**6*q*r**4*s**3 + 570509375000*p**3*q**3*r**4*s**3 + 274839843750*q**5*r**4*s**3 - 73312500000*p**4*q*r**5*s**3 - 145437500000*p*q**3*r**5*s**3 + 8750000000*p**2*q*r**6*s**3 + 180000000000*q*r**7*s**3 + 15946875*p**13*s**4 + 1265625*p**10*q**2*s**4 - 3282343750*p**7*q**4*s**4 - 38241406250*p**4*q**6*s**4 - 40136718750*p*q**8*s**4 - 113146875*p**11*r*s**4 - 2302734375*p**8*q**2*r*s**4 + 68450156250*p**5*q**4*r*s**4 + 177376562500*p**2*q**6*r*s**4 + 3164062500*p**9*r**2*s**4 + 14392890625*p**6*q**2*r**2*s**4 - 543781250000*p**3*q**4*r**2*s**4 - 319769531250*q**6*r**2*s**4 - 21048281250*p**7*r**3*s**4 - 240687500000*p**4*q**2*r**3*s**4 - 228164062500*p*q**4*r**3*s**4 + 23062500000*p**5*r**4*s**4 + 300410156250*p**2*q**2*r**4*s**4 + 93437500000*p**3*r**5*s**4 - 1141015625000*q**2*r**5*s**4 - 187500000000*p*r**6*s**4 + 1761328125*p**9*q*s**5 - 3177734375*p**6*q**3*s**5 + 60019531250*p**3*q**5*s**5 + 108398437500*q**7*s**5 + 24106640625*p**7*q*r*s**5 + 429589843750*p**4*q**3*r*s**5 + 410371093750*p*q**5*r*s**5 - 23582031250*p**5*q*r**2*s**5 + 202441406250*p**2*q**3*r**2*s**5 - 383203125000*p**3*q*r**3*s**5 + 2232910156250*q**3*r**3*s**5 + 1500000000000*p*q*r**4*s**5 - 13710937500*p**8*s**6 - 202832031250*p**5*q**2*s**6 - 531738281250*p**2*q**4*s**6 + 73330078125*p**6*r*s**6 - 3906250000*p**3*q**2*r*s**6 - 1275878906250*q**4*r*s**6 - 121093750000*p**4*r**2*s**6 - 3308593750000*p*q**2*r**2*s**6 + 18066406250*p**2*r**3*s**6 - 244140625000*r**4*s**6 + 327148437500*p**4*q*s**7 + 1672363281250*p*q**3*s**7 + 446777343750*p**2*q*r*s**7 + 1232910156250*q*r**2*s**7 - 274658203125*p**3*s**8 - 1068115234375*q**2*s**8 - 61035156250*p*r*s**8 b[4][3] = 200*p**9*q**8 + 7550*p**6*q**10 + 78650*p**3*q**12 + 248400*q**14 - 4800*p**10*q**6*r - 164300*p**7*q**8*r - 1709575*p**4*q**10*r - 5566500*p*q**12*r + 31050*p**11*q**4*r**2 + 1116175*p**8*q**6*r**2 + 12674650*p**5*q**8*r**2 + 45333850*p**2*q**10*r**2 - 60750*p**12*q**2*r**3 - 2872725*p**9*q**4*r**3 - 40403050*p**6*q**6*r**3 - 173564375*p**3*q**8*r**3 - 11242250*q**10*r**3 + 2174100*p**10*q**2*r**4 + 54010000*p**7*q**4*r**4 + 331074875*p**4*q**6*r**4 + 114173750*p*q**8*r**4 - 24858500*p**8*q**2*r**5 - 300875000*p**5*q**4*r**5 - 319430625*p**2*q**6*r**5 + 69810000*p**6*q**2*r**6 - 23900000*p**3*q**4*r**6 - 294662500*q**6*r**6 + 524200000*p**4*q**2*r**7 + 1432000000*p*q**4*r**7 - 2340000000*p**2*q**2*r**8 + 5400*p**11*q**5*s + 310400*p**8*q**7*s + 3591725*p**5*q**9*s + 11556750*p**2*q**11*s - 105300*p**12*q**3*r*s - 4234650*p**9*q**5*r*s - 49928875*p**6*q**7*r*s - 174078125*p**3*q**9*r*s + 18000000*q**11*r*s + 364500*p**13*q*r**2*s + 15763050*p**10*q**3*r**2*s + 220187400*p**7*q**5*r**2*s + 929609375*p**4*q**7*r**2*s - 43653125*p*q**9*r**2*s - 13427100*p**11*q*r**3*s - 346066250*p**8*q**3*r**3*s - 2287673375*p**5*q**5*r**3*s - 1403903125*p**2*q**7*r**3*s + 184586000*p**9*q*r**4*s + 2983460000*p**6*q**3*r**4*s + 8725818750*p**3*q**5*r**4*s + 2527734375*q**7*r**4*s - 1284480000*p**7*q*r**5*s - 13138250000*p**4*q**3*r**5*s - 14001625000*p*q**5*r**5*s + 4224800000*p**5*q*r**6*s + 27460000000*p**2*q**3*r**6*s - 3760000000*p**3*q*r**7*s + 3900000000*q**3*r**7*s + 36450*p**13*q**2*s**2 + 2765475*p**10*q**4*s**2 + 34027625*p**7*q**6*s**2 + 97375000*p**4*q**8*s**2 - 88275000*p*q**10*s**2 - 546750*p**14*r*s**2 - 21961125*p**11*q**2*r*s**2 - 273059375*p**8*q**4*r*s**2 - 761562500*p**5*q**6*r*s**2 + 1869656250*p**2*q**8*r*s**2 + 20545650*p**12*r**2*s**2 + 473934375*p**9*q**2*r**2*s**2 + 1758053125*p**6*q**4*r**2*s**2 - 8743359375*p**3*q**6*r**2*s**2 - 4154375000*q**8*r**2*s**2 - 296559000*p**10*r**3*s**2 - 4065056250*p**7*q**2*r**3*s**2 - 186328125*p**4*q**4*r**3*s**2 + 19419453125*p*q**6*r**3*s**2 + 2326262500*p**8*r**4*s**2 + 21189375000*p**5*q**2*r**4*s**2 - 26301953125*p**2*q**4*r**4*s**2 - 10513250000*p**6*r**5*s**2 - 69937500000*p**3*q**2*r**5*s**2 - 42257812500*q**4*r**5*s**2 + 23375000000*p**4*r**6*s**2 + 40750000000*p*q**2*r**6*s**2 - 19500000000*p**2*r**7*s**2 + 4009500*p**12*q*s**3 + 36140625*p**9*q**3*s**3 - 335459375*p**6*q**5*s**3 - 2695312500*p**3*q**7*s**3 - 1486250000*q**9*s**3 + 102515625*p**10*q*r*s**3 + 4006812500*p**7*q**3*r*s**3 + 27589609375*p**4*q**5*r*s**3 + 20195312500*p*q**7*r*s**3 - 2792812500*p**8*q*r**2*s**3 - 44115156250*p**5*q**3*r**2*s**3 - 72609453125*p**2*q**5*r**2*s**3 + 18752500000*p**6*q*r**3*s**3 + 218140625000*p**3*q**3*r**3*s**3 + 109940234375*q**5*r**3*s**3 - 21893750000*p**4*q*r**4*s**3 - 65187500000*p*q**3*r**4*s**3 - 31000000000*p**2*q*r**5*s**3 + 97500000000*q*r**6*s**3 - 86568750*p**11*s**4 - 1955390625*p**8*q**2*s**4 - 8960781250*p**5*q**4*s**4 - 1357812500*p**2*q**6*s**4 + 1657968750*p**9*r*s**4 + 10467187500*p**6*q**2*r*s**4 - 55292968750*p**3*q**4*r*s**4 - 60683593750*q**6*r*s**4 - 11473593750*p**7*r**2*s**4 - 123281250000*p**4*q**2*r**2*s**4 - 164912109375*p*q**4*r**2*s**4 + 13150000000*p**5*r**3*s**4 + 190751953125*p**2*q**2*r**3*s**4 + 61875000000*p**3*r**4*s**4 - 467773437500*q**2*r**4*s**4 - 118750000000*p*r**5*s**4 + 7583203125*p**7*q*s**5 + 54638671875*p**4*q**3*s**5 + 39423828125*p*q**5*s**5 + 32392578125*p**5*q*r*s**5 + 278515625000*p**2*q**3*r*s**5 - 298339843750*p**3*q*r**2*s**5 + 560791015625*q**3*r**2*s**5 + 720703125000*p*q*r**3*s**5 - 19687500000*p**6*s**6 - 159667968750*p**3*q**2*s**6 - 72265625000*q**4*s**6 + 116699218750*p**4*r*s**6 - 924072265625*p*q**2*r*s**6 - 156005859375*p**2*r**2*s**6 - 112304687500*r**3*s**6 + 349121093750*p**2*q*s**7 + 396728515625*q*r*s**7 - 213623046875*p*s**8 b[4][2] = -600*p**10*q**6 - 18450*p**7*q**8 - 174000*p**4*q**10 - 518400*p*q**12 + 5400*p**11*q**4*r + 197550*p**8*q**6*r + 2147775*p**5*q**8*r + 7219800*p**2*q**10*r - 12150*p**12*q**2*r**2 - 662200*p**9*q**4*r**2 - 9274775*p**6*q**6*r**2 - 38330625*p**3*q**8*r**2 - 5508000*q**10*r**2 + 656550*p**10*q**2*r**3 + 16233750*p**7*q**4*r**3 + 97335875*p**4*q**6*r**3 + 58271250*p*q**8*r**3 - 9845500*p**8*q**2*r**4 - 119464375*p**5*q**4*r**4 - 194431875*p**2*q**6*r**4 + 49465000*p**6*q**2*r**5 + 166000000*p**3*q**4*r**5 - 80793750*q**6*r**5 + 54400000*p**4*q**2*r**6 + 377750000*p*q**4*r**6 - 630000000*p**2*q**2*r**7 - 16200*p**12*q**3*s - 459300*p**9*q**5*s - 4207225*p**6*q**7*s - 10827500*p**3*q**9*s + 13635000*q**11*s + 72900*p**13*q*r*s + 2877300*p**10*q**3*r*s + 33239700*p**7*q**5*r*s + 107080625*p**4*q**7*r*s - 114975000*p*q**9*r*s - 3601800*p**11*q*r**2*s - 75214375*p**8*q**3*r**2*s - 387073250*p**5*q**5*r**2*s + 55540625*p**2*q**7*r**2*s + 53793000*p**9*q*r**3*s + 687176875*p**6*q**3*r**3*s + 1670018750*p**3*q**5*r**3*s + 665234375*q**7*r**3*s - 391570000*p**7*q*r**4*s - 3420125000*p**4*q**3*r**4*s - 3609625000*p*q**5*r**4*s + 1365600000*p**5*q*r**5*s + 7236250000*p**2*q**3*r**5*s - 1220000000*p**3*q*r**6*s + 1050000000*q**3*r**6*s - 109350*p**14*s**2 - 3065850*p**11*q**2*s**2 - 26908125*p**8*q**4*s**2 - 44606875*p**5*q**6*s**2 + 269812500*p**2*q**8*s**2 + 5200200*p**12*r*s**2 + 81826875*p**9*q**2*r*s**2 + 155378125*p**6*q**4*r*s**2 - 1936203125*p**3*q**6*r*s**2 - 998437500*q**8*r*s**2 - 77145750*p**10*r**2*s**2 - 745528125*p**7*q**2*r**2*s**2 + 683437500*p**4*q**4*r**2*s**2 + 4083359375*p*q**6*r**2*s**2 + 593287500*p**8*r**3*s**2 + 4799375000*p**5*q**2*r**3*s**2 - 4167578125*p**2*q**4*r**3*s**2 - 2731125000*p**6*r**4*s**2 - 18668750000*p**3*q**2*r**4*s**2 - 10480468750*q**4*r**4*s**2 + 6200000000*p**4*r**5*s**2 + 11750000000*p*q**2*r**5*s**2 - 5250000000*p**2*r**6*s**2 + 26527500*p**10*q*s**3 + 526031250*p**7*q**3*s**3 + 3160703125*p**4*q**5*s**3 + 2650312500*p*q**7*s**3 - 448031250*p**8*q*r*s**3 - 6682968750*p**5*q**3*r*s**3 - 11642812500*p**2*q**5*r*s**3 + 2553203125*p**6*q*r**2*s**3 + 37234375000*p**3*q**3*r**2*s**3 + 21871484375*q**5*r**2*s**3 + 2803125000*p**4*q*r**3*s**3 - 10796875000*p*q**3*r**3*s**3 - 16656250000*p**2*q*r**4*s**3 + 26250000000*q*r**5*s**3 - 75937500*p**9*s**4 - 704062500*p**6*q**2*s**4 - 8363281250*p**3*q**4*s**4 - 10398437500*q**6*s**4 + 197578125*p**7*r*s**4 - 16441406250*p**4*q**2*r*s**4 - 24277343750*p*q**4*r*s**4 - 5716015625*p**5*r**2*s**4 + 31728515625*p**2*q**2*r**2*s**4 + 27031250000*p**3*r**3*s**4 - 92285156250*q**2*r**3*s**4 - 33593750000*p*r**4*s**4 + 10394531250*p**5*q*s**5 + 38037109375*p**2*q**3*s**5 - 48144531250*p**3*q*r*s**5 + 74462890625*q**3*r*s**5 + 121093750000*p*q*r**2*s**5 - 2197265625*p**4*s**6 - 92529296875*p*q**2*s**6 + 15380859375*p**2*r*s**6 - 31738281250*r**2*s**6 + 54931640625*q*s**7 b[4][1] = 200*p**8*q**6 + 2950*p**5*q**8 + 10800*p**2*q**10 - 1800*p**9*q**4*r - 49650*p**6*q**6*r - 403375*p**3*q**8*r - 999000*q**10*r + 4050*p**10*q**2*r**2 + 236625*p**7*q**4*r**2 + 3109500*p**4*q**6*r**2 + 11463750*p*q**8*r**2 - 331500*p**8*q**2*r**3 - 7818125*p**5*q**4*r**3 - 41411250*p**2*q**6*r**3 + 4782500*p**6*q**2*r**4 + 47475000*p**3*q**4*r**4 - 16728125*q**6*r**4 - 8700000*p**4*q**2*r**5 + 81750000*p*q**4*r**5 - 135000000*p**2*q**2*r**6 + 5400*p**10*q**3*s + 144200*p**7*q**5*s + 939375*p**4*q**7*s + 1012500*p*q**9*s - 24300*p**11*q*r*s - 1169250*p**8*q**3*r*s - 14027250*p**5*q**5*r*s - 44446875*p**2*q**7*r*s + 2011500*p**9*q*r**2*s + 49330625*p**6*q**3*r**2*s + 272009375*p**3*q**5*r**2*s + 104062500*q**7*r**2*s - 34660000*p**7*q*r**3*s - 455062500*p**4*q**3*r**3*s - 625906250*p*q**5*r**3*s + 210200000*p**5*q*r**4*s + 1298750000*p**2*q**3*r**4*s - 240000000*p**3*q*r**5*s + 225000000*q**3*r**5*s + 36450*p**12*s**2 + 1231875*p**9*q**2*s**2 + 10712500*p**6*q**4*s**2 + 21718750*p**3*q**6*s**2 + 16875000*q**8*s**2 - 2814750*p**10*r*s**2 - 67612500*p**7*q**2*r*s**2 - 345156250*p**4*q**4*r*s**2 - 283125000*p*q**6*r*s**2 + 51300000*p**8*r**2*s**2 + 734531250*p**5*q**2*r**2*s**2 + 1267187500*p**2*q**4*r**2*s**2 - 384312500*p**6*r**3*s**2 - 3912500000*p**3*q**2*r**3*s**2 - 1822265625*q**4*r**3*s**2 + 1112500000*p**4*r**4*s**2 + 2437500000*p*q**2*r**4*s**2 - 1125000000*p**2*r**5*s**2 - 72578125*p**5*q**3*s**3 - 189296875*p**2*q**5*s**3 + 127265625*p**6*q*r*s**3 + 1415625000*p**3*q**3*r*s**3 + 1229687500*q**5*r*s**3 + 1448437500*p**4*q*r**2*s**3 + 2218750000*p*q**3*r**2*s**3 - 4031250000*p**2*q*r**3*s**3 + 5625000000*q*r**4*s**3 - 132890625*p**7*s**4 - 529296875*p**4*q**2*s**4 - 175781250*p*q**4*s**4 - 401953125*p**5*r*s**4 - 4482421875*p**2*q**2*r*s**4 + 4140625000*p**3*r**2*s**4 - 10498046875*q**2*r**2*s**4 - 7031250000*p*r**3*s**4 + 1220703125*p**3*q*s**5 + 1953125000*q**3*s**5 + 14160156250*p*q*r*s**5 - 1708984375*p**2*s**6 - 3662109375*r*s**6 b[4][0] = -4600*p**6*q**6 - 67850*p**3*q**8 - 248400*q**10 + 38900*p**7*q**4*r + 679575*p**4*q**6*r + 2866500*p*q**8*r - 81900*p**8*q**2*r**2 - 2009750*p**5*q**4*r**2 - 10783750*p**2*q**6*r**2 + 1478750*p**6*q**2*r**3 + 14165625*p**3*q**4*r**3 - 2743750*q**6*r**3 - 5450000*p**4*q**2*r**4 + 12687500*p*q**4*r**4 - 22500000*p**2*q**2*r**5 - 101700*p**8*q**3*s - 1700975*p**5*q**5*s - 7061250*p**2*q**7*s + 423900*p**9*q*r*s + 9292375*p**6*q**3*r*s + 50438750*p**3*q**5*r*s + 20475000*q**7*r*s - 7852500*p**7*q*r**2*s - 87765625*p**4*q**3*r**2*s - 121609375*p*q**5*r**2*s + 47700000*p**5*q*r**3*s + 264687500*p**2*q**3*r**3*s - 65000000*p**3*q*r**4*s + 37500000*q**3*r**4*s - 534600*p**10*s**2 - 10344375*p**7*q**2*s**2 - 54859375*p**4*q**4*s**2 - 40312500*p*q**6*s**2 + 10158750*p**8*r*s**2 + 117778125*p**5*q**2*r*s**2 + 192421875*p**2*q**4*r*s**2 - 70593750*p**6*r**2*s**2 - 685312500*p**3*q**2*r**2*s**2 - 334375000*q**4*r**2*s**2 + 193750000*p**4*r**3*s**2 + 500000000*p*q**2*r**3*s**2 - 187500000*p**2*r**4*s**2 + 8437500*p**6*q*s**3 + 159218750*p**3*q**3*s**3 + 220625000*q**5*s**3 + 353828125*p**4*q*r*s**3 + 412500000*p*q**3*r*s**3 - 1023437500*p**2*q*r**2*s**3 + 937500000*q*r**3*s**3 - 206015625*p**5*s**4 - 701171875*p**2*q**2*s**4 + 998046875*p**3*r*s**4 - 1308593750*q**2*r*s**4 - 1367187500*p*r**2*s**4 + 1708984375*p*q*s**5 - 976562500*s**6 return b @property def o(self): p, q, r, s = self.p, self.q, self.r, self.s o = [0]*6 o[5] = -1600*p**10*q**10 - 23600*p**7*q**12 - 86400*p**4*q**14 + 24800*p**11*q**8*r + 419200*p**8*q**10*r + 1850450*p**5*q**12*r + 896400*p**2*q**14*r - 138800*p**12*q**6*r**2 - 2921900*p**9*q**8*r**2 - 17295200*p**6*q**10*r**2 - 27127750*p**3*q**12*r**2 - 26076600*q**14*r**2 + 325800*p**13*q**4*r**3 + 9993850*p**10*q**6*r**3 + 88010500*p**7*q**8*r**3 + 274047650*p**4*q**10*r**3 + 410171400*p*q**12*r**3 - 259200*p**14*q**2*r**4 - 17147100*p**11*q**4*r**4 - 254289150*p**8*q**6*r**4 - 1318548225*p**5*q**8*r**4 - 2633598475*p**2*q**10*r**4 + 12636000*p**12*q**2*r**5 + 388911000*p**9*q**4*r**5 + 3269704725*p**6*q**6*r**5 + 8791192300*p**3*q**8*r**5 + 93560575*q**10*r**5 - 228361600*p**10*q**2*r**6 - 3951199200*p**7*q**4*r**6 - 16276981100*p**4*q**6*r**6 - 1597227000*p*q**8*r**6 + 1947899200*p**8*q**2*r**7 + 17037648000*p**5*q**4*r**7 + 8919740000*p**2*q**6*r**7 - 7672160000*p**6*q**2*r**8 - 15496000000*p**3*q**4*r**8 + 4224000000*q**6*r**8 + 9968000000*p**4*q**2*r**9 - 8640000000*p*q**4*r**9 + 4800000000*p**2*q**2*r**10 - 55200*p**12*q**7*s - 685600*p**9*q**9*s + 1028250*p**6*q**11*s + 37650000*p**3*q**13*s + 111375000*q**15*s + 583200*p**13*q**5*r*s + 9075600*p**10*q**7*r*s - 883150*p**7*q**9*r*s - 506830750*p**4*q**11*r*s - 1793137500*p*q**13*r*s - 1852200*p**14*q**3*r**2*s - 41435250*p**11*q**5*r**2*s - 80566700*p**8*q**7*r**2*s + 2485673600*p**5*q**9*r**2*s + 11442286125*p**2*q**11*r**2*s + 1555200*p**15*q*r**3*s + 80846100*p**12*q**3*r**3*s + 564906800*p**9*q**5*r**3*s - 4493012400*p**6*q**7*r**3*s - 35492391250*p**3*q**9*r**3*s - 789931875*q**11*r**3*s - 71766000*p**13*q*r**4*s - 1551149200*p**10*q**3*r**4*s - 1773437900*p**7*q**5*r**4*s + 51957593125*p**4*q**7*r**4*s + 14964765625*p*q**9*r**4*s + 1231569600*p**11*q*r**5*s + 12042977600*p**8*q**3*r**5*s - 27151011200*p**5*q**5*r**5*s - 88080610000*p**2*q**7*r**5*s - 9912995200*p**9*q*r**6*s - 29448104000*p**6*q**3*r**6*s + 144954840000*p**3*q**5*r**6*s - 44601300000*q**7*r**6*s + 35453760000*p**7*q*r**7*s - 63264000000*p**4*q**3*r**7*s + 60544000000*p*q**5*r**7*s - 30048000000*p**5*q*r**8*s + 37040000000*p**2*q**3*r**8*s - 60800000000*p**3*q*r**9*s - 48000000000*q**3*r**9*s - 615600*p**14*q**4*s**2 - 10524500*p**11*q**6*s**2 - 33831250*p**8*q**8*s**2 + 222806250*p**5*q**10*s**2 + 1099687500*p**2*q**12*s**2 + 3353400*p**15*q**2*r*s**2 + 74269350*p**12*q**4*r*s**2 + 276445750*p**9*q**6*r*s**2 - 2618600000*p**6*q**8*r*s**2 - 14473243750*p**3*q**10*r*s**2 + 1383750000*q**12*r*s**2 - 2332800*p**16*r**2*s**2 - 132750900*p**13*q**2*r**2*s**2 - 900775150*p**10*q**4*r**2*s**2 + 8249244500*p**7*q**6*r**2*s**2 + 59525796875*p**4*q**8*r**2*s**2 - 40292868750*p*q**10*r**2*s**2 + 128304000*p**14*r**3*s**2 + 3160232100*p**11*q**2*r**3*s**2 + 8329580000*p**8*q**4*r**3*s**2 - 45558458750*p**5*q**6*r**3*s**2 + 297252890625*p**2*q**8*r**3*s**2 - 2769854400*p**12*r**4*s**2 - 37065970000*p**9*q**2*r**4*s**2 - 90812546875*p**6*q**4*r**4*s**2 - 627902000000*p**3*q**6*r**4*s**2 + 181347421875*q**8*r**4*s**2 + 30946932800*p**10*r**5*s**2 + 249954680000*p**7*q**2*r**5*s**2 + 802954812500*p**4*q**4*r**5*s**2 - 80900000000*p*q**6*r**5*s**2 - 192137320000*p**8*r**6*s**2 - 932641600000*p**5*q**2*r**6*s**2 - 943242500000*p**2*q**4*r**6*s**2 + 658412000000*p**6*r**7*s**2 + 1930720000000*p**3*q**2*r**7*s**2 + 593800000000*q**4*r**7*s**2 - 1162800000000*p**4*r**8*s**2 - 280000000000*p*q**2*r**8*s**2 + 840000000000*p**2*r**9*s**2 - 2187000*p**16*q*s**3 - 47418750*p**13*q**3*s**3 - 180618750*p**10*q**5*s**3 + 2231250000*p**7*q**7*s**3 + 17857734375*p**4*q**9*s**3 + 29882812500*p*q**11*s**3 + 24664500*p**14*q*r*s**3 - 853368750*p**11*q**3*r*s**3 - 25939693750*p**8*q**5*r*s**3 - 177541562500*p**5*q**7*r*s**3 - 297978828125*p**2*q**9*r*s**3 - 153468000*p**12*q*r**2*s**3 + 30188125000*p**9*q**3*r**2*s**3 + 344049821875*p**6*q**5*r**2*s**3 + 534026875000*p**3*q**7*r**2*s**3 - 340726484375*q**9*r**2*s**3 - 9056190000*p**10*q*r**3*s**3 - 322314687500*p**7*q**3*r**3*s**3 - 769632109375*p**4*q**5*r**3*s**3 - 83276875000*p*q**7*r**3*s**3 + 164061000000*p**8*q*r**4*s**3 + 1381358750000*p**5*q**3*r**4*s**3 + 3088020000000*p**2*q**5*r**4*s**3 - 1267655000000*p**6*q*r**5*s**3 - 7642630000000*p**3*q**3*r**5*s**3 - 2759877500000*q**5*r**5*s**3 + 4597760000000*p**4*q*r**6*s**3 + 1846200000000*p*q**3*r**6*s**3 - 7006000000000*p**2*q*r**7*s**3 - 1200000000000*q*r**8*s**3 + 18225000*p**15*s**4 + 1328906250*p**12*q**2*s**4 + 24729140625*p**9*q**4*s**4 + 169467187500*p**6*q**6*s**4 + 413281250000*p**3*q**8*s**4 + 223828125000*q**10*s**4 + 710775000*p**13*r*s**4 - 18611015625*p**10*q**2*r*s**4 - 314344375000*p**7*q**4*r*s**4 - 828439843750*p**4*q**6*r*s**4 + 460937500000*p*q**8*r*s**4 - 25674975000*p**11*r**2*s**4 - 52223515625*p**8*q**2*r**2*s**4 - 387160000000*p**5*q**4*r**2*s**4 - 4733680078125*p**2*q**6*r**2*s**4 + 343911875000*p**9*r**3*s**4 + 3328658359375*p**6*q**2*r**3*s**4 + 16532406250000*p**3*q**4*r**3*s**4 + 5980613281250*q**6*r**3*s**4 - 2295497500000*p**7*r**4*s**4 - 14809820312500*p**4*q**2*r**4*s**4 - 6491406250000*p*q**4*r**4*s**4 + 7768470000000*p**5*r**5*s**4 + 34192562500000*p**2*q**2*r**5*s**4 - 11859000000000*p**3*r**6*s**4 + 10530000000000*q**2*r**6*s**4 + 6000000000000*p*r**7*s**4 + 11453906250*p**11*q*s**5 + 149765625000*p**8*q**3*s**5 + 545537109375*p**5*q**5*s**5 + 527343750000*p**2*q**7*s**5 - 371313281250*p**9*q*r*s**5 - 3461455078125*p**6*q**3*r*s**5 - 7920878906250*p**3*q**5*r*s**5 - 4747314453125*q**7*r*s**5 + 2417815625000*p**7*q*r**2*s**5 + 5465576171875*p**4*q**3*r**2*s**5 + 5937128906250*p*q**5*r**2*s**5 - 10661156250000*p**5*q*r**3*s**5 - 63574218750000*p**2*q**3*r**3*s**5 + 24059375000000*p**3*q*r**4*s**5 - 33023437500000*q**3*r**4*s**5 - 43125000000000*p*q*r**5*s**5 + 94394531250*p**10*s**6 + 1097167968750*p**7*q**2*s**6 + 2829833984375*p**4*q**4*s**6 - 1525878906250*p*q**6*s**6 + 2724609375*p**8*r*s**6 + 13998535156250*p**5*q**2*r*s**6 + 57094482421875*p**2*q**4*r*s**6 - 8512509765625*p**6*r**2*s**6 - 37941406250000*p**3*q**2*r**2*s**6 + 33191894531250*q**4*r**2*s**6 + 50534179687500*p**4*r**3*s**6 + 156656250000000*p*q**2*r**3*s**6 - 85023437500000*p**2*r**4*s**6 + 10125000000000*r**5*s**6 - 2717285156250*p**6*q*s**7 - 11352539062500*p**3*q**3*s**7 - 2593994140625*q**5*s**7 - 47154541015625*p**4*q*r*s**7 - 160644531250000*p*q**3*r*s**7 + 142500000000000*p**2*q*r**2*s**7 - 26757812500000*q*r**3*s**7 - 4364013671875*p**5*s**8 - 94604492187500*p**2*q**2*s**8 + 114379882812500*p**3*r*s**8 + 51116943359375*q**2*r*s**8 - 346435546875000*p*r**2*s**8 + 476837158203125*p*q*s**9 - 476837158203125*s**10 o[4] = 1600*p**11*q**8 + 20800*p**8*q**10 + 45100*p**5*q**12 - 151200*p**2*q**14 - 19200*p**12*q**6*r - 293200*p**9*q**8*r - 794600*p**6*q**10*r + 2634675*p**3*q**12*r + 2640600*q**14*r + 75600*p**13*q**4*r**2 + 1529100*p**10*q**6*r**2 + 6233350*p**7*q**8*r**2 - 12013350*p**4*q**10*r**2 - 29069550*p*q**12*r**2 - 97200*p**14*q**2*r**3 - 3562500*p**11*q**4*r**3 - 26984900*p**8*q**6*r**3 - 15900325*p**5*q**8*r**3 + 76267100*p**2*q**10*r**3 + 3272400*p**12*q**2*r**4 + 59486850*p**9*q**4*r**4 + 221270075*p**6*q**6*r**4 + 74065250*p**3*q**8*r**4 - 300564375*q**10*r**4 - 45569400*p**10*q**2*r**5 - 438666000*p**7*q**4*r**5 - 444821250*p**4*q**6*r**5 + 2448256250*p*q**8*r**5 + 290640000*p**8*q**2*r**6 + 855850000*p**5*q**4*r**6 - 5741875000*p**2*q**6*r**6 - 644000000*p**6*q**2*r**7 + 5574000000*p**3*q**4*r**7 + 4643000000*q**6*r**7 - 1696000000*p**4*q**2*r**8 - 12660000000*p*q**4*r**8 + 7200000000*p**2*q**2*r**9 + 43200*p**13*q**5*s + 572000*p**10*q**7*s - 59800*p**7*q**9*s - 24174625*p**4*q**11*s - 74587500*p*q**13*s - 324000*p**14*q**3*r*s - 5531400*p**11*q**5*r*s - 3712100*p**8*q**7*r*s + 293009275*p**5*q**9*r*s + 1115548875*p**2*q**11*r*s + 583200*p**15*q*r**2*s + 18343800*p**12*q**3*r**2*s + 77911100*p**9*q**5*r**2*s - 957488825*p**6*q**7*r**2*s - 5449661250*p**3*q**9*r**2*s + 960120000*q**11*r**2*s - 23684400*p**13*q*r**3*s - 373761900*p**10*q**3*r**3*s - 27944975*p**7*q**5*r**3*s + 10375740625*p**4*q**7*r**3*s - 4649093750*p*q**9*r**3*s + 395816400*p**11*q*r**4*s + 2910968000*p**8*q**3*r**4*s - 9126162500*p**5*q**5*r**4*s - 11696118750*p**2*q**7*r**4*s - 3028640000*p**9*q*r**5*s - 3251550000*p**6*q**3*r**5*s + 47914250000*p**3*q**5*r**5*s - 30255625000*q**7*r**5*s + 9304000000*p**7*q*r**6*s - 42970000000*p**4*q**3*r**6*s + 31475000000*p*q**5*r**6*s + 2176000000*p**5*q*r**7*s + 62100000000*p**2*q**3*r**7*s - 43200000000*p**3*q*r**8*s - 72000000000*q**3*r**8*s + 291600*p**15*q**2*s**2 + 2702700*p**12*q**4*s**2 - 38692250*p**9*q**6*s**2 - 538903125*p**6*q**8*s**2 - 1613112500*p**3*q**10*s**2 + 320625000*q**12*s**2 - 874800*p**16*r*s**2 - 14166900*p**13*q**2*r*s**2 + 193284900*p**10*q**4*r*s**2 + 3688520500*p**7*q**6*r*s**2 + 11613390625*p**4*q**8*r*s**2 - 15609881250*p*q**10*r*s**2 + 44031600*p**14*r**2*s**2 + 482345550*p**11*q**2*r**2*s**2 - 2020881875*p**8*q**4*r**2*s**2 - 7407026250*p**5*q**6*r**2*s**2 + 136175750000*p**2*q**8*r**2*s**2 - 1000884600*p**12*r**3*s**2 - 8888950000*p**9*q**2*r**3*s**2 - 30101703125*p**6*q**4*r**3*s**2 - 319761000000*p**3*q**6*r**3*s**2 + 51519218750*q**8*r**3*s**2 + 12622395000*p**10*r**4*s**2 + 97032450000*p**7*q**2*r**4*s**2 + 469929218750*p**4*q**4*r**4*s**2 + 291342187500*p*q**6*r**4*s**2 - 96382000000*p**8*r**5*s**2 - 598070000000*p**5*q**2*r**5*s**2 - 1165021875000*p**2*q**4*r**5*s**2 + 446500000000*p**6*r**6*s**2 + 1651500000000*p**3*q**2*r**6*s**2 + 789375000000*q**4*r**6*s**2 - 1152000000000*p**4*r**7*s**2 - 600000000000*p*q**2*r**7*s**2 + 1260000000000*p**2*r**8*s**2 - 24786000*p**14*q*s**3 - 660487500*p**11*q**3*s**3 - 5886356250*p**8*q**5*s**3 - 18137187500*p**5*q**7*s**3 - 5120546875*p**2*q**9*s**3 + 827658000*p**12*q*r*s**3 + 13343062500*p**9*q**3*r*s**3 + 39782068750*p**6*q**5*r*s**3 - 111288437500*p**3*q**7*r*s**3 - 15438750000*q**9*r*s**3 - 14540782500*p**10*q*r**2*s**3 - 135889750000*p**7*q**3*r**2*s**3 - 176892578125*p**4*q**5*r**2*s**3 - 934462656250*p*q**7*r**2*s**3 + 171669250000*p**8*q*r**3*s**3 + 1164538125000*p**5*q**3*r**3*s**3 + 3192346406250*p**2*q**5*r**3*s**3 - 1295476250000*p**6*q*r**4*s**3 - 6540712500000*p**3*q**3*r**4*s**3 - 2957828125000*q**5*r**4*s**3 + 5366750000000*p**4*q*r**5*s**3 + 3165000000000*p*q**3*r**5*s**3 - 8862500000000*p**2*q*r**6*s**3 - 1800000000000*q*r**7*s**3 + 236925000*p**13*s**4 + 8895234375*p**10*q**2*s**4 + 106180781250*p**7*q**4*s**4 + 474221875000*p**4*q**6*s**4 + 616210937500*p*q**8*s**4 - 6995868750*p**11*r*s**4 - 184190625000*p**8*q**2*r*s**4 - 1299254453125*p**5*q**4*r*s**4 - 2475458593750*p**2*q**6*r*s**4 + 63049218750*p**9*r**2*s**4 + 1646791484375*p**6*q**2*r**2*s**4 + 9086886718750*p**3*q**4*r**2*s**4 + 4673421875000*q**6*r**2*s**4 - 215665000000*p**7*r**3*s**4 - 7864589843750*p**4*q**2*r**3*s**4 - 5987890625000*p*q**4*r**3*s**4 + 594843750000*p**5*r**4*s**4 + 27791171875000*p**2*q**2*r**4*s**4 - 3881250000000*p**3*r**5*s**4 + 12203125000000*q**2*r**5*s**4 + 10312500000000*p*r**6*s**4 - 34720312500*p**9*q*s**5 - 545126953125*p**6*q**3*s**5 - 2176425781250*p**3*q**5*s**5 - 2792968750000*q**7*s**5 - 1395703125*p**7*q*r*s**5 - 1957568359375*p**4*q**3*r*s**5 + 5122636718750*p*q**5*r*s**5 + 858210937500*p**5*q*r**2*s**5 - 42050097656250*p**2*q**3*r**2*s**5 + 7088281250000*p**3*q*r**3*s**5 - 25974609375000*q**3*r**3*s**5 - 69296875000000*p*q*r**4*s**5 + 384697265625*p**8*s**6 + 6403320312500*p**5*q**2*s**6 + 16742675781250*p**2*q**4*s**6 - 3467080078125*p**6*r*s**6 + 11009765625000*p**3*q**2*r*s**6 + 16451660156250*q**4*r*s**6 + 6979003906250*p**4*r**2*s**6 + 145403320312500*p*q**2*r**2*s**6 + 4076171875000*p**2*r**3*s**6 + 22265625000000*r**4*s**6 - 21915283203125*p**4*q*s**7 - 86608886718750*p*q**3*s**7 - 22785644531250*p**2*q*r*s**7 - 103466796875000*q*r**2*s**7 + 18798828125000*p**3*s**8 + 106048583984375*q**2*s**8 + 17761230468750*p*r*s**8 o[3] = 2800*p**9*q**8 + 55700*p**6*q**10 + 363600*p**3*q**12 + 777600*q**14 - 27200*p**10*q**6*r - 700200*p**7*q**8*r - 5726550*p**4*q**10*r - 15066000*p*q**12*r + 74700*p**11*q**4*r**2 + 2859575*p**8*q**6*r**2 + 31175725*p**5*q**8*r**2 + 103147650*p**2*q**10*r**2 - 40500*p**12*q**2*r**3 - 4274400*p**9*q**4*r**3 - 76065825*p**6*q**6*r**3 - 365623750*p**3*q**8*r**3 - 132264000*q**10*r**3 + 2192400*p**10*q**2*r**4 + 92562500*p**7*q**4*r**4 + 799193875*p**4*q**6*r**4 + 1188193125*p*q**8*r**4 - 41231500*p**8*q**2*r**5 - 914210000*p**5*q**4*r**5 - 3318853125*p**2*q**6*r**5 + 398850000*p**6*q**2*r**6 + 3944000000*p**3*q**4*r**6 + 2211312500*q**6*r**6 - 1817000000*p**4*q**2*r**7 - 6720000000*p*q**4*r**7 + 3900000000*p**2*q**2*r**8 + 75600*p**11*q**5*s + 1823100*p**8*q**7*s + 14534150*p**5*q**9*s + 38265750*p**2*q**11*s - 394200*p**12*q**3*r*s - 11453850*p**9*q**5*r*s - 101213000*p**6*q**7*r*s - 223565625*p**3*q**9*r*s + 415125000*q**11*r*s + 243000*p**13*q*r**2*s + 13654575*p**10*q**3*r**2*s + 163811725*p**7*q**5*r**2*s + 173461250*p**4*q**7*r**2*s - 3008671875*p*q**9*r**2*s - 2016900*p**11*q*r**3*s - 86576250*p**8*q**3*r**3*s - 324146625*p**5*q**5*r**3*s + 3378506250*p**2*q**7*r**3*s - 89211000*p**9*q*r**4*s - 55207500*p**6*q**3*r**4*s + 1493950000*p**3*q**5*r**4*s - 12573609375*q**7*r**4*s + 1140100000*p**7*q*r**5*s + 42500000*p**4*q**3*r**5*s + 21511250000*p*q**5*r**5*s - 4058000000*p**5*q*r**6*s + 6725000000*p**2*q**3*r**6*s - 1400000000*p**3*q*r**7*s - 39000000000*q**3*r**7*s + 510300*p**13*q**2*s**2 + 4814775*p**10*q**4*s**2 - 70265125*p**7*q**6*s**2 - 1016484375*p**4*q**8*s**2 - 3221100000*p*q**10*s**2 - 364500*p**14*r*s**2 + 30314250*p**11*q**2*r*s**2 + 1106765625*p**8*q**4*r*s**2 + 10984203125*p**5*q**6*r*s**2 + 33905812500*p**2*q**8*r*s**2 - 37980900*p**12*r**2*s**2 - 2142905625*p**9*q**2*r**2*s**2 - 26896125000*p**6*q**4*r**2*s**2 - 95551328125*p**3*q**6*r**2*s**2 + 11320312500*q**8*r**2*s**2 + 1743781500*p**10*r**3*s**2 + 35432262500*p**7*q**2*r**3*s**2 + 177855859375*p**4*q**4*r**3*s**2 + 121260546875*p*q**6*r**3*s**2 - 25943162500*p**8*r**4*s**2 - 249165500000*p**5*q**2*r**4*s**2 - 461739453125*p**2*q**4*r**4*s**2 + 177823750000*p**6*r**5*s**2 + 726225000000*p**3*q**2*r**5*s**2 + 404195312500*q**4*r**5*s**2 - 565875000000*p**4*r**6*s**2 - 407500000000*p*q**2*r**6*s**2 + 682500000000*p**2*r**7*s**2 - 59140125*p**12*q*s**3 - 1290515625*p**9*q**3*s**3 - 8785071875*p**6*q**5*s**3 - 15588281250*p**3*q**7*s**3 + 17505000000*q**9*s**3 + 896062500*p**10*q*r*s**3 + 2589750000*p**7*q**3*r*s**3 - 82700156250*p**4*q**5*r*s**3 - 347683593750*p*q**7*r*s**3 + 17022656250*p**8*q*r**2*s**3 + 320923593750*p**5*q**3*r**2*s**3 + 1042116875000*p**2*q**5*r**2*s**3 - 353262812500*p**6*q*r**3*s**3 - 2212664062500*p**3*q**3*r**3*s**3 - 1252408984375*q**5*r**3*s**3 + 1967362500000*p**4*q*r**4*s**3 + 1583343750000*p*q**3*r**4*s**3 - 3560625000000*p**2*q*r**5*s**3 - 975000000000*q*r**6*s**3 + 462459375*p**11*s**4 + 14210859375*p**8*q**2*s**4 + 99521718750*p**5*q**4*s**4 + 114955468750*p**2*q**6*s**4 - 17720859375*p**9*r*s**4 - 100320703125*p**6*q**2*r*s**4 + 1021943359375*p**3*q**4*r*s**4 + 1193203125000*q**6*r*s**4 + 171371250000*p**7*r**2*s**4 - 1113390625000*p**4*q**2*r**2*s**4 - 1211474609375*p*q**4*r**2*s**4 - 274056250000*p**5*r**3*s**4 + 8285166015625*p**2*q**2*r**3*s**4 - 2079375000000*p**3*r**4*s**4 + 5137304687500*q**2*r**4*s**4 + 6187500000000*p*r**5*s**4 - 135675000000*p**7*q*s**5 - 1275244140625*p**4*q**3*s**5 - 28388671875*p*q**5*s**5 + 1015166015625*p**5*q*r*s**5 - 10584423828125*p**2*q**3*r*s**5 + 3559570312500*p**3*q*r**2*s**5 - 6929931640625*q**3*r**2*s**5 - 32304687500000*p*q*r**3*s**5 + 430576171875*p**6*s**6 + 9397949218750*p**3*q**2*s**6 + 575195312500*q**4*s**6 - 4086425781250*p**4*r*s**6 + 42183837890625*p*q**2*r*s**6 + 8156494140625*p**2*r**2*s**6 + 12612304687500*r**3*s**6 - 25513916015625*p**2*q*s**7 - 37017822265625*q*r*s**7 + 18981933593750*p*s**8 o[2] = 1600*p**10*q**6 + 9200*p**7*q**8 - 126000*p**4*q**10 - 777600*p*q**12 - 14400*p**11*q**4*r - 119300*p**8*q**6*r + 1203225*p**5*q**8*r + 9412200*p**2*q**10*r + 32400*p**12*q**2*r**2 + 417950*p**9*q**4*r**2 - 4543725*p**6*q**6*r**2 - 49008125*p**3*q**8*r**2 - 24192000*q**10*r**2 - 292050*p**10*q**2*r**3 + 8760000*p**7*q**4*r**3 + 137506625*p**4*q**6*r**3 + 225438750*p*q**8*r**3 - 4213250*p**8*q**2*r**4 - 173595625*p**5*q**4*r**4 - 653003125*p**2*q**6*r**4 + 82575000*p**6*q**2*r**5 + 838125000*p**3*q**4*r**5 + 578562500*q**6*r**5 - 421500000*p**4*q**2*r**6 - 1796250000*p*q**4*r**6 + 1050000000*p**2*q**2*r**7 + 43200*p**12*q**3*s + 807300*p**9*q**5*s + 5328225*p**6*q**7*s + 16946250*p**3*q**9*s + 29565000*q**11*s - 194400*p**13*q*r*s - 5505300*p**10*q**3*r*s - 49886700*p**7*q**5*r*s - 178821875*p**4*q**7*r*s - 222750000*p*q**9*r*s + 6814800*p**11*q*r**2*s + 120525625*p**8*q**3*r**2*s + 526694500*p**5*q**5*r**2*s + 84065625*p**2*q**7*r**2*s - 123670500*p**9*q*r**3*s - 1106731875*p**6*q**3*r**3*s - 669556250*p**3*q**5*r**3*s - 2869265625*q**7*r**3*s + 1004350000*p**7*q*r**4*s + 3384375000*p**4*q**3*r**4*s + 5665625000*p*q**5*r**4*s - 3411000000*p**5*q*r**5*s - 418750000*p**2*q**3*r**5*s + 1700000000*p**3*q*r**6*s - 10500000000*q**3*r**6*s + 291600*p**14*s**2 + 9829350*p**11*q**2*s**2 + 114151875*p**8*q**4*s**2 + 522169375*p**5*q**6*s**2 + 716906250*p**2*q**8*s**2 - 18625950*p**12*r*s**2 - 387703125*p**9*q**2*r*s**2 - 2056109375*p**6*q**4*r*s**2 - 760203125*p**3*q**6*r*s**2 + 3071250000*q**8*r*s**2 + 512419500*p**10*r**2*s**2 + 5859053125*p**7*q**2*r**2*s**2 + 12154062500*p**4*q**4*r**2*s**2 + 15931640625*p*q**6*r**2*s**2 - 6598393750*p**8*r**3*s**2 - 43549625000*p**5*q**2*r**3*s**2 - 82011328125*p**2*q**4*r**3*s**2 + 43538125000*p**6*r**4*s**2 + 160831250000*p**3*q**2*r**4*s**2 + 99070312500*q**4*r**4*s**2 - 141812500000*p**4*r**5*s**2 - 117500000000*p*q**2*r**5*s**2 + 183750000000*p**2*r**6*s**2 - 154608750*p**10*q*s**3 - 3309468750*p**7*q**3*s**3 - 20834140625*p**4*q**5*s**3 - 34731562500*p*q**7*s**3 + 5970375000*p**8*q*r*s**3 + 68533281250*p**5*q**3*r*s**3 + 142698281250*p**2*q**5*r*s**3 - 74509140625*p**6*q*r**2*s**3 - 389148437500*p**3*q**3*r**2*s**3 - 270937890625*q**5*r**2*s**3 + 366696875000*p**4*q*r**3*s**3 + 400031250000*p*q**3*r**3*s**3 - 735156250000*p**2*q*r**4*s**3 - 262500000000*q*r**5*s**3 + 371250000*p**9*s**4 + 21315000000*p**6*q**2*s**4 + 179515625000*p**3*q**4*s**4 + 238406250000*q**6*s**4 - 9071015625*p**7*r*s**4 - 268945312500*p**4*q**2*r*s**4 - 379785156250*p*q**4*r*s**4 + 140262890625*p**5*r**2*s**4 + 1486259765625*p**2*q**2*r**2*s**4 - 806484375000*p**3*r**3*s**4 + 1066210937500*q**2*r**3*s**4 + 1722656250000*p*r**4*s**4 - 125648437500*p**5*q*s**5 - 1236279296875*p**2*q**3*s**5 + 1267871093750*p**3*q*r*s**5 - 1044677734375*q**3*r*s**5 - 6630859375000*p*q*r**2*s**5 + 160888671875*p**4*s**6 + 6352294921875*p*q**2*s**6 - 708740234375*p**2*r*s**6 + 3901367187500*r**2*s**6 - 8050537109375*q*s**7 o[1] = 2800*p**8*q**6 + 41300*p**5*q**8 + 151200*p**2*q**10 - 25200*p**9*q**4*r - 542600*p**6*q**6*r - 3397875*p**3*q**8*r - 5751000*q**10*r + 56700*p**10*q**2*r**2 + 1972125*p**7*q**4*r**2 + 18624250*p**4*q**6*r**2 + 50253750*p*q**8*r**2 - 1701000*p**8*q**2*r**3 - 32630625*p**5*q**4*r**3 - 139868750*p**2*q**6*r**3 + 18162500*p**6*q**2*r**4 + 177125000*p**3*q**4*r**4 + 121734375*q**6*r**4 - 100500000*p**4*q**2*r**5 - 386250000*p*q**4*r**5 + 225000000*p**2*q**2*r**6 + 75600*p**10*q**3*s + 1708800*p**7*q**5*s + 12836875*p**4*q**7*s + 32062500*p*q**9*s - 340200*p**11*q*r*s - 10185750*p**8*q**3*r*s - 97502750*p**5*q**5*r*s - 301640625*p**2*q**7*r*s + 7168500*p**9*q*r**2*s + 135960625*p**6*q**3*r**2*s + 587471875*p**3*q**5*r**2*s - 384750000*q**7*r**2*s - 29325000*p**7*q*r**3*s - 320625000*p**4*q**3*r**3*s + 523437500*p*q**5*r**3*s - 42000000*p**5*q*r**4*s + 343750000*p**2*q**3*r**4*s + 150000000*p**3*q*r**5*s - 2250000000*q**3*r**5*s + 510300*p**12*s**2 + 12808125*p**9*q**2*s**2 + 107062500*p**6*q**4*s**2 + 270312500*p**3*q**6*s**2 - 168750000*q**8*s**2 - 2551500*p**10*r*s**2 - 5062500*p**7*q**2*r*s**2 + 712343750*p**4*q**4*r*s**2 + 4788281250*p*q**6*r*s**2 - 256837500*p**8*r**2*s**2 - 3574812500*p**5*q**2*r**2*s**2 - 14967968750*p**2*q**4*r**2*s**2 + 4040937500*p**6*r**3*s**2 + 26400000000*p**3*q**2*r**3*s**2 + 17083984375*q**4*r**3*s**2 - 21812500000*p**4*r**4*s**2 - 24375000000*p*q**2*r**4*s**2 + 39375000000*p**2*r**5*s**2 - 127265625*p**5*q**3*s**3 - 680234375*p**2*q**5*s**3 - 2048203125*p**6*q*r*s**3 - 18794531250*p**3*q**3*r*s**3 - 25050000000*q**5*r*s**3 + 26621875000*p**4*q*r**2*s**3 + 37007812500*p*q**3*r**2*s**3 - 105468750000*p**2*q*r**3*s**3 - 56250000000*q*r**4*s**3 + 1124296875*p**7*s**4 + 9251953125*p**4*q**2*s**4 - 8007812500*p*q**4*s**4 - 4004296875*p**5*r*s**4 + 179931640625*p**2*q**2*r*s**4 - 75703125000*p**3*r**2*s**4 + 133447265625*q**2*r**2*s**4 + 363281250000*p*r**3*s**4 - 91552734375*p**3*q*s**5 - 19531250000*q**3*s**5 - 751953125000*p*q*r*s**5 + 157958984375*p**2*s**6 + 748291015625*r*s**6 o[0] = -14400*p**6*q**6 - 212400*p**3*q**8 - 777600*q**10 + 92100*p**7*q**4*r + 1689675*p**4*q**6*r + 7371000*p*q**8*r - 122850*p**8*q**2*r**2 - 3735250*p**5*q**4*r**2 - 22432500*p**2*q**6*r**2 + 2298750*p**6*q**2*r**3 + 29390625*p**3*q**4*r**3 + 18000000*q**6*r**3 - 17750000*p**4*q**2*r**4 - 62812500*p*q**4*r**4 + 37500000*p**2*q**2*r**5 - 51300*p**8*q**3*s - 768025*p**5*q**5*s - 2801250*p**2*q**7*s - 275400*p**9*q*r*s - 5479875*p**6*q**3*r*s - 35538750*p**3*q**5*r*s - 68850000*q**7*r*s + 12757500*p**7*q*r**2*s + 133640625*p**4*q**3*r**2*s + 222609375*p*q**5*r**2*s - 108500000*p**5*q*r**3*s - 290312500*p**2*q**3*r**3*s + 275000000*p**3*q*r**4*s - 375000000*q**3*r**4*s + 1931850*p**10*s**2 + 40213125*p**7*q**2*s**2 + 253921875*p**4*q**4*s**2 + 464062500*p*q**6*s**2 - 71077500*p**8*r*s**2 - 818746875*p**5*q**2*r*s**2 - 1882265625*p**2*q**4*r*s**2 + 826031250*p**6*r**2*s**2 + 4369687500*p**3*q**2*r**2*s**2 + 3107812500*q**4*r**2*s**2 - 3943750000*p**4*r**3*s**2 - 5000000000*p*q**2*r**3*s**2 + 6562500000*p**2*r**4*s**2 - 295312500*p**6*q*s**3 - 2938906250*p**3*q**3*s**3 - 4848750000*q**5*s**3 + 3791484375*p**4*q*r*s**3 + 7556250000*p*q**3*r*s**3 - 11960937500*p**2*q*r**2*s**3 - 9375000000*q*r**3*s**3 + 1668515625*p**5*s**4 + 20447265625*p**2*q**2*s**4 - 21955078125*p**3*r*s**4 + 18984375000*q**2*r*s**4 + 67382812500*p*r**2*s**4 - 120849609375*p*q*s**5 + 157226562500*s**6 return o @property def a(self): p, q, r, s = self.p, self.q, self.r, self.s a = [0]*6 a[5] = -100*p**7*q**7 - 2175*p**4*q**9 - 10500*p*q**11 + 1100*p**8*q**5*r + 27975*p**5*q**7*r + 152950*p**2*q**9*r - 4125*p**9*q**3*r**2 - 128875*p**6*q**5*r**2 - 830525*p**3*q**7*r**2 + 59450*q**9*r**2 + 5400*p**10*q*r**3 + 243800*p**7*q**3*r**3 + 2082650*p**4*q**5*r**3 - 333925*p*q**7*r**3 - 139200*p**8*q*r**4 - 2406000*p**5*q**3*r**4 - 122600*p**2*q**5*r**4 + 1254400*p**6*q*r**5 + 3776000*p**3*q**3*r**5 + 1832000*q**5*r**5 - 4736000*p**4*q*r**6 - 6720000*p*q**3*r**6 + 6400000*p**2*q*r**7 - 900*p**9*q**4*s - 37400*p**6*q**6*s - 281625*p**3*q**8*s - 435000*q**10*s + 6750*p**10*q**2*r*s + 322300*p**7*q**4*r*s + 2718575*p**4*q**6*r*s + 4214250*p*q**8*r*s - 16200*p**11*r**2*s - 859275*p**8*q**2*r**2*s - 8925475*p**5*q**4*r**2*s - 14427875*p**2*q**6*r**2*s + 453600*p**9*r**3*s + 10038400*p**6*q**2*r**3*s + 17397500*p**3*q**4*r**3*s - 11333125*q**6*r**3*s - 4451200*p**7*r**4*s - 15850000*p**4*q**2*r**4*s + 34000000*p*q**4*r**4*s + 17984000*p**5*r**5*s - 10000000*p**2*q**2*r**5*s - 25600000*p**3*r**6*s - 8000000*q**2*r**6*s + 6075*p**11*q*s**2 - 83250*p**8*q**3*s**2 - 1282500*p**5*q**5*s**2 - 2862500*p**2*q**7*s**2 + 724275*p**9*q*r*s**2 + 9807250*p**6*q**3*r*s**2 + 28374375*p**3*q**5*r*s**2 + 22212500*q**7*r*s**2 - 8982000*p**7*q*r**2*s**2 - 39600000*p**4*q**3*r**2*s**2 - 61746875*p*q**5*r**2*s**2 - 1010000*p**5*q*r**3*s**2 - 1000000*p**2*q**3*r**3*s**2 + 78000000*p**3*q*r**4*s**2 + 30000000*q**3*r**4*s**2 + 80000000*p*q*r**5*s**2 - 759375*p**10*s**3 - 9787500*p**7*q**2*s**3 - 39062500*p**4*q**4*s**3 - 52343750*p*q**6*s**3 + 12301875*p**8*r*s**3 + 98175000*p**5*q**2*r*s**3 + 225078125*p**2*q**4*r*s**3 - 54900000*p**6*r**2*s**3 - 310000000*p**3*q**2*r**2*s**3 - 7890625*q**4*r**2*s**3 + 51250000*p**4*r**3*s**3 - 420000000*p*q**2*r**3*s**3 + 110000000*p**2*r**4*s**3 - 200000000*r**5*s**3 + 2109375*p**6*q*s**4 - 21093750*p**3*q**3*s**4 - 89843750*q**5*s**4 + 182343750*p**4*q*r*s**4 + 733203125*p*q**3*r*s**4 - 196875000*p**2*q*r**2*s**4 + 1125000000*q*r**3*s**4 - 158203125*p**5*s**5 - 566406250*p**2*q**2*s**5 + 101562500*p**3*r*s**5 - 1669921875*q**2*r*s**5 + 1250000000*p*r**2*s**5 - 1220703125*p*q*s**6 + 6103515625*s**7 a[4] = 1000*p**5*q**7 + 7250*p**2*q**9 - 10800*p**6*q**5*r - 96900*p**3*q**7*r - 52500*q**9*r + 37400*p**7*q**3*r**2 + 470850*p**4*q**5*r**2 + 640600*p*q**7*r**2 - 39600*p**8*q*r**3 - 983600*p**5*q**3*r**3 - 2848100*p**2*q**5*r**3 + 814400*p**6*q*r**4 + 6076000*p**3*q**3*r**4 + 2308000*q**5*r**4 - 5024000*p**4*q*r**5 - 9680000*p*q**3*r**5 + 9600000*p**2*q*r**6 + 13800*p**7*q**4*s + 94650*p**4*q**6*s - 26500*p*q**8*s - 86400*p**8*q**2*r*s - 816500*p**5*q**4*r*s - 257500*p**2*q**6*r*s + 91800*p**9*r**2*s + 1853700*p**6*q**2*r**2*s + 630000*p**3*q**4*r**2*s - 8971250*q**6*r**2*s - 2071200*p**7*r**3*s - 7240000*p**4*q**2*r**3*s + 29375000*p*q**4*r**3*s + 14416000*p**5*r**4*s - 5200000*p**2*q**2*r**4*s - 30400000*p**3*r**5*s - 12000000*q**2*r**5*s + 64800*p**9*q*s**2 + 567000*p**6*q**3*s**2 + 1655000*p**3*q**5*s**2 + 6987500*q**7*s**2 + 337500*p**7*q*r*s**2 + 8462500*p**4*q**3*r*s**2 - 5812500*p*q**5*r*s**2 - 24930000*p**5*q*r**2*s**2 - 69125000*p**2*q**3*r**2*s**2 + 103500000*p**3*q*r**3*s**2 + 30000000*q**3*r**3*s**2 + 90000000*p*q*r**4*s**2 - 708750*p**8*s**3 - 5400000*p**5*q**2*s**3 + 8906250*p**2*q**4*s**3 + 18562500*p**6*r*s**3 - 625000*p**3*q**2*r*s**3 + 29687500*q**4*r*s**3 - 75000000*p**4*r**2*s**3 - 416250000*p*q**2*r**2*s**3 + 60000000*p**2*r**3*s**3 - 300000000*r**4*s**3 + 71718750*p**4*q*s**4 + 189062500*p*q**3*s**4 + 210937500*p**2*q*r*s**4 + 1187500000*q*r**2*s**4 - 187500000*p**3*s**5 - 800781250*q**2*s**5 - 390625000*p*r*s**5 a[3] = -500*p**6*q**5 - 6350*p**3*q**7 - 19800*q**9 + 3750*p**7*q**3*r + 65100*p**4*q**5*r + 264950*p*q**7*r - 6750*p**8*q*r**2 - 209050*p**5*q**3*r**2 - 1217250*p**2*q**5*r**2 + 219000*p**6*q*r**3 + 2510000*p**3*q**3*r**3 + 1098500*q**5*r**3 - 2068000*p**4*q*r**4 - 5060000*p*q**3*r**4 + 5200000*p**2*q*r**5 - 6750*p**8*q**2*s - 96350*p**5*q**4*s - 346000*p**2*q**6*s + 20250*p**9*r*s + 459900*p**6*q**2*r*s + 1828750*p**3*q**4*r*s - 2930000*q**6*r*s - 594000*p**7*r**2*s - 4301250*p**4*q**2*r**2*s + 10906250*p*q**4*r**2*s + 5252000*p**5*r**3*s - 1450000*p**2*q**2*r**3*s - 12800000*p**3*r**4*s - 6500000*q**2*r**4*s + 74250*p**7*q*s**2 + 1418750*p**4*q**3*s**2 + 5956250*p*q**5*s**2 - 4297500*p**5*q*r*s**2 - 29906250*p**2*q**3*r*s**2 + 31500000*p**3*q*r**2*s**2 + 12500000*q**3*r**2*s**2 + 35000000*p*q*r**3*s**2 + 1350000*p**6*s**3 + 6093750*p**3*q**2*s**3 + 17500000*q**4*s**3 - 7031250*p**4*r*s**3 - 127812500*p*q**2*r*s**3 + 18750000*p**2*r**2*s**3 - 162500000*r**3*s**3 + 107812500*p**2*q*s**4 + 460937500*q*r*s**4 - 214843750*p*s**5 a[2] = 1950*p**4*q**5 + 14100*p*q**7 - 14350*p**5*q**3*r - 125600*p**2*q**5*r + 27900*p**6*q*r**2 + 402250*p**3*q**3*r**2 + 288250*q**5*r**2 - 436000*p**4*q*r**3 - 1345000*p*q**3*r**3 + 1400000*p**2*q*r**4 + 9450*p**6*q**2*s - 1250*p**3*q**4*s - 465000*q**6*s - 49950*p**7*r*s - 302500*p**4*q**2*r*s + 1718750*p*q**4*r*s + 834000*p**5*r**2*s + 437500*p**2*q**2*r**2*s - 3100000*p**3*r**3*s - 1750000*q**2*r**3*s - 292500*p**5*q*s**2 - 1937500*p**2*q**3*s**2 + 3343750*p**3*q*r*s**2 + 1875000*q**3*r*s**2 + 8125000*p*q*r**2*s**2 - 1406250*p**4*s**3 - 12343750*p*q**2*s**3 + 5312500*p**2*r*s**3 - 43750000*r**2*s**3 + 74218750*q*s**4 a[1] = -300*p**5*q**3 - 2150*p**2*q**5 + 1350*p**6*q*r + 21500*p**3*q**3*r + 61500*q**5*r - 42000*p**4*q*r**2 - 290000*p*q**3*r**2 + 300000*p**2*q*r**3 - 4050*p**7*s - 45000*p**4*q**2*s - 125000*p*q**4*s + 108000*p**5*r*s + 643750*p**2*q**2*r*s - 700000*p**3*r**2*s - 375000*q**2*r**2*s - 93750*p**3*q*s**2 - 312500*q**3*s**2 + 1875000*p*q*r*s**2 - 1406250*p**2*s**3 - 9375000*r*s**3 a[0] = 1250*p**3*q**3 + 9000*q**5 - 4500*p**4*q*r - 46250*p*q**3*r + 50000*p**2*q*r**2 + 6750*p**5*s + 43750*p**2*q**2*s - 75000*p**3*r*s - 62500*q**2*r*s + 156250*p*q*s**2 - 1562500*s**3 return a @property def c(self): p, q, r, s = self.p, self.q, self.r, self.s c = [0]*6 c[5] = -40*p**5*q**11 - 270*p**2*q**13 + 700*p**6*q**9*r + 5165*p**3*q**11*r + 540*q**13*r - 4230*p**7*q**7*r**2 - 31845*p**4*q**9*r**2 + 20880*p*q**11*r**2 + 9645*p**8*q**5*r**3 + 57615*p**5*q**7*r**3 - 358255*p**2*q**9*r**3 - 1880*p**9*q**3*r**4 + 114020*p**6*q**5*r**4 + 2012190*p**3*q**7*r**4 - 26855*q**9*r**4 - 14400*p**10*q*r**5 - 470400*p**7*q**3*r**5 - 5088640*p**4*q**5*r**5 + 920*p*q**7*r**5 + 332800*p**8*q*r**6 + 5797120*p**5*q**3*r**6 + 1608000*p**2*q**5*r**6 - 2611200*p**6*q*r**7 - 7424000*p**3*q**3*r**7 - 2323200*q**5*r**7 + 8601600*p**4*q*r**8 + 9472000*p*q**3*r**8 - 10240000*p**2*q*r**9 - 3060*p**7*q**8*s - 39085*p**4*q**10*s - 132300*p*q**12*s + 36580*p**8*q**6*r*s + 520185*p**5*q**8*r*s + 1969860*p**2*q**10*r*s - 144045*p**9*q**4*r**2*s - 2438425*p**6*q**6*r**2*s - 10809475*p**3*q**8*r**2*s + 518850*q**10*r**2*s + 182520*p**10*q**2*r**3*s + 4533930*p**7*q**4*r**3*s + 26196770*p**4*q**6*r**3*s - 4542325*p*q**8*r**3*s + 21600*p**11*r**4*s - 2208080*p**8*q**2*r**4*s - 24787960*p**5*q**4*r**4*s + 10813900*p**2*q**6*r**4*s - 499200*p**9*r**5*s + 3827840*p**6*q**2*r**5*s + 9596000*p**3*q**4*r**5*s + 22662000*q**6*r**5*s + 3916800*p**7*r**6*s - 29952000*p**4*q**2*r**6*s - 90800000*p*q**4*r**6*s - 12902400*p**5*r**7*s + 87040000*p**2*q**2*r**7*s + 15360000*p**3*r**8*s + 12800000*q**2*r**8*s - 38070*p**9*q**5*s**2 - 566700*p**6*q**7*s**2 - 2574375*p**3*q**9*s**2 - 1822500*q**11*s**2 + 292815*p**10*q**3*r*s**2 + 5170280*p**7*q**5*r*s**2 + 27918125*p**4*q**7*r*s**2 + 21997500*p*q**9*r*s**2 - 573480*p**11*q*r**2*s**2 - 14566350*p**8*q**3*r**2*s**2 - 104851575*p**5*q**5*r**2*s**2 - 96448750*p**2*q**7*r**2*s**2 + 11001240*p**9*q*r**3*s**2 + 147798600*p**6*q**3*r**3*s**2 + 158632750*p**3*q**5*r**3*s**2 - 78222500*q**7*r**3*s**2 - 62819200*p**7*q*r**4*s**2 - 136160000*p**4*q**3*r**4*s**2 + 317555000*p*q**5*r**4*s**2 + 160224000*p**5*q*r**5*s**2 - 267600000*p**2*q**3*r**5*s**2 - 153600000*p**3*q*r**6*s**2 - 120000000*q**3*r**6*s**2 - 32000000*p*q*r**7*s**2 - 127575*p**11*q**2*s**3 - 2148750*p**8*q**4*s**3 - 13652500*p**5*q**6*s**3 - 19531250*p**2*q**8*s**3 + 495720*p**12*r*s**3 + 11856375*p**9*q**2*r*s**3 + 107807500*p**6*q**4*r*s**3 + 222334375*p**3*q**6*r*s**3 + 105062500*q**8*r*s**3 - 11566800*p**10*r**2*s**3 - 216787500*p**7*q**2*r**2*s**3 - 633437500*p**4*q**4*r**2*s**3 - 504484375*p*q**6*r**2*s**3 + 90918000*p**8*r**3*s**3 + 567080000*p**5*q**2*r**3*s**3 + 692937500*p**2*q**4*r**3*s**3 - 326640000*p**6*r**4*s**3 - 339000000*p**3*q**2*r**4*s**3 + 369250000*q**4*r**4*s**3 + 560000000*p**4*r**5*s**3 + 508000000*p*q**2*r**5*s**3 - 480000000*p**2*r**6*s**3 + 320000000*r**7*s**3 - 455625*p**10*q*s**4 - 27562500*p**7*q**3*s**4 - 120593750*p**4*q**5*s**4 - 60312500*p*q**7*s**4 + 110615625*p**8*q*r*s**4 + 662984375*p**5*q**3*r*s**4 + 528515625*p**2*q**5*r*s**4 - 541687500*p**6*q*r**2*s**4 - 1262343750*p**3*q**3*r**2*s**4 - 466406250*q**5*r**2*s**4 + 633000000*p**4*q*r**3*s**4 - 1264375000*p*q**3*r**3*s**4 + 1085000000*p**2*q*r**4*s**4 - 2700000000*q*r**5*s**4 - 68343750*p**9*s**5 - 478828125*p**6*q**2*s**5 - 355468750*p**3*q**4*s**5 - 11718750*q**6*s**5 + 718031250*p**7*r*s**5 + 1658593750*p**4*q**2*r*s**5 + 2212890625*p*q**4*r*s**5 - 2855625000*p**5*r**2*s**5 - 4273437500*p**2*q**2*r**2*s**5 + 4537500000*p**3*r**3*s**5 + 8031250000*q**2*r**3*s**5 - 1750000000*p*r**4*s**5 + 1353515625*p**5*q*s**6 + 1562500000*p**2*q**3*s**6 - 3964843750*p**3*q*r*s**6 - 7226562500*q**3*r*s**6 + 1953125000*p*q*r**2*s**6 - 1757812500*p**4*s**7 - 3173828125*p*q**2*s**7 + 6445312500*p**2*r*s**7 - 3906250000*r**2*s**7 + 6103515625*q*s**8 c[4] = 40*p**6*q**9 + 110*p**3*q**11 - 1080*q**13 - 560*p**7*q**7*r - 1780*p**4*q**9*r + 17370*p*q**11*r + 2850*p**8*q**5*r**2 + 10520*p**5*q**7*r**2 - 115910*p**2*q**9*r**2 - 6090*p**9*q**3*r**3 - 25330*p**6*q**5*r**3 + 448740*p**3*q**7*r**3 + 128230*q**9*r**3 + 4320*p**10*q*r**4 + 16960*p**7*q**3*r**4 - 1143600*p**4*q**5*r**4 - 1410310*p*q**7*r**4 + 3840*p**8*q*r**5 + 1744480*p**5*q**3*r**5 + 5619520*p**2*q**5*r**5 - 1198080*p**6*q*r**6 - 10579200*p**3*q**3*r**6 - 2940800*q**5*r**6 + 8294400*p**4*q*r**7 + 13568000*p*q**3*r**7 - 15360000*p**2*q*r**8 + 840*p**8*q**6*s + 7580*p**5*q**8*s + 24420*p**2*q**10*s - 8100*p**9*q**4*r*s - 94100*p**6*q**6*r*s - 473000*p**3*q**8*r*s - 473400*q**10*r*s + 22680*p**10*q**2*r**2*s + 374370*p**7*q**4*r**2*s + 2888020*p**4*q**6*r**2*s + 5561050*p*q**8*r**2*s - 12960*p**11*r**3*s - 485820*p**8*q**2*r**3*s - 6723440*p**5*q**4*r**3*s - 23561400*p**2*q**6*r**3*s + 190080*p**9*r**4*s + 5894880*p**6*q**2*r**4*s + 50882000*p**3*q**4*r**4*s + 22411500*q**6*r**4*s - 258560*p**7*r**5*s - 46248000*p**4*q**2*r**5*s - 103800000*p*q**4*r**5*s - 3737600*p**5*r**6*s + 119680000*p**2*q**2*r**6*s + 10240000*p**3*r**7*s + 19200000*q**2*r**7*s + 7290*p**10*q**3*s**2 + 117360*p**7*q**5*s**2 + 691250*p**4*q**7*s**2 - 198750*p*q**9*s**2 - 36450*p**11*q*r*s**2 - 854550*p**8*q**3*r*s**2 - 7340700*p**5*q**5*r*s**2 - 2028750*p**2*q**7*r*s**2 + 995490*p**9*q*r**2*s**2 + 18896600*p**6*q**3*r**2*s**2 + 5026500*p**3*q**5*r**2*s**2 - 52272500*q**7*r**2*s**2 - 16636800*p**7*q*r**3*s**2 - 43200000*p**4*q**3*r**3*s**2 + 223426250*p*q**5*r**3*s**2 + 112068000*p**5*q*r**4*s**2 - 177000000*p**2*q**3*r**4*s**2 - 244000000*p**3*q*r**5*s**2 - 156000000*q**3*r**5*s**2 + 43740*p**12*s**3 + 1032750*p**9*q**2*s**3 + 8602500*p**6*q**4*s**3 + 15606250*p**3*q**6*s**3 + 39625000*q**8*s**3 - 1603800*p**10*r*s**3 - 26932500*p**7*q**2*r*s**3 - 19562500*p**4*q**4*r*s**3 - 152000000*p*q**6*r*s**3 + 25555500*p**8*r**2*s**3 + 16230000*p**5*q**2*r**2*s**3 + 42187500*p**2*q**4*r**2*s**3 - 165660000*p**6*r**3*s**3 + 373500000*p**3*q**2*r**3*s**3 + 332937500*q**4*r**3*s**3 + 465000000*p**4*r**4*s**3 + 586000000*p*q**2*r**4*s**3 - 592000000*p**2*r**5*s**3 + 480000000*r**6*s**3 - 1518750*p**8*q*s**4 - 62531250*p**5*q**3*s**4 + 7656250*p**2*q**5*s**4 + 184781250*p**6*q*r*s**4 - 15781250*p**3*q**3*r*s**4 - 135156250*q**5*r*s**4 - 1148250000*p**4*q*r**2*s**4 - 2121406250*p*q**3*r**2*s**4 + 1990000000*p**2*q*r**3*s**4 - 3150000000*q*r**4*s**4 - 2531250*p**7*s**5 + 660937500*p**4*q**2*s**5 + 1339843750*p*q**4*s**5 - 33750000*p**5*r*s**5 - 679687500*p**2*q**2*r*s**5 + 6250000*p**3*r**2*s**5 + 6195312500*q**2*r**2*s**5 + 1125000000*p*r**3*s**5 - 996093750*p**3*q*s**6 - 3125000000*q**3*s**6 - 3222656250*p*q*r*s**6 + 1171875000*p**2*s**7 + 976562500*r*s**7 c[3] = 80*p**4*q**9 + 540*p*q**11 - 600*p**5*q**7*r - 4770*p**2*q**9*r + 1230*p**6*q**5*r**2 + 20900*p**3*q**7*r**2 + 47250*q**9*r**2 - 710*p**7*q**3*r**3 - 84950*p**4*q**5*r**3 - 526310*p*q**7*r**3 + 720*p**8*q*r**4 + 216280*p**5*q**3*r**4 + 2068020*p**2*q**5*r**4 - 198080*p**6*q*r**5 - 3703200*p**3*q**3*r**5 - 1423600*q**5*r**5 + 2860800*p**4*q*r**6 + 7056000*p*q**3*r**6 - 8320000*p**2*q*r**7 - 2720*p**6*q**6*s - 46350*p**3*q**8*s - 178200*q**10*s + 25740*p**7*q**4*r*s + 489490*p**4*q**6*r*s + 2152350*p*q**8*r*s - 61560*p**8*q**2*r**2*s - 1568150*p**5*q**4*r**2*s - 9060500*p**2*q**6*r**2*s + 24840*p**9*r**3*s + 1692380*p**6*q**2*r**3*s + 18098250*p**3*q**4*r**3*s + 9387750*q**6*r**3*s - 382560*p**7*r**4*s - 16818000*p**4*q**2*r**4*s - 49325000*p*q**4*r**4*s + 1212800*p**5*r**5*s + 64840000*p**2*q**2*r**5*s - 320000*p**3*r**6*s + 10400000*q**2*r**6*s - 36450*p**8*q**3*s**2 - 588350*p**5*q**5*s**2 - 2156250*p**2*q**7*s**2 + 123930*p**9*q*r*s**2 + 2879700*p**6*q**3*r*s**2 + 12548000*p**3*q**5*r*s**2 - 14445000*q**7*r*s**2 - 3233250*p**7*q*r**2*s**2 - 28485000*p**4*q**3*r**2*s**2 + 72231250*p*q**5*r**2*s**2 + 32093000*p**5*q*r**3*s**2 - 61275000*p**2*q**3*r**3*s**2 - 107500000*p**3*q*r**4*s**2 - 78500000*q**3*r**4*s**2 + 22000000*p*q*r**5*s**2 - 72900*p**10*s**3 - 1215000*p**7*q**2*s**3 - 2937500*p**4*q**4*s**3 + 9156250*p*q**6*s**3 + 2612250*p**8*r*s**3 + 16560000*p**5*q**2*r*s**3 - 75468750*p**2*q**4*r*s**3 - 32737500*p**6*r**2*s**3 + 169062500*p**3*q**2*r**2*s**3 + 121718750*q**4*r**2*s**3 + 160250000*p**4*r**3*s**3 + 219750000*p*q**2*r**3*s**3 - 317000000*p**2*r**4*s**3 + 260000000*r**5*s**3 + 2531250*p**6*q*s**4 + 22500000*p**3*q**3*s**4 + 39843750*q**5*s**4 - 266343750*p**4*q*r*s**4 - 776406250*p*q**3*r*s**4 + 789062500*p**2*q*r**2*s**4 - 1368750000*q*r**3*s**4 + 67500000*p**5*s**5 + 441406250*p**2*q**2*s**5 - 311718750*p**3*r*s**5 + 1785156250*q**2*r*s**5 + 546875000*p*r**2*s**5 - 1269531250*p*q*s**6 + 488281250*s**7 c[2] = 120*p**5*q**7 + 810*p**2*q**9 - 1280*p**6*q**5*r - 9160*p**3*q**7*r + 3780*q**9*r + 4530*p**7*q**3*r**2 + 36640*p**4*q**5*r**2 - 45270*p*q**7*r**2 - 5400*p**8*q*r**3 - 60920*p**5*q**3*r**3 + 200050*p**2*q**5*r**3 + 31200*p**6*q*r**4 - 476000*p**3*q**3*r**4 - 378200*q**5*r**4 + 521600*p**4*q*r**5 + 1872000*p*q**3*r**5 - 2240000*p**2*q*r**6 + 1440*p**7*q**4*s + 15310*p**4*q**6*s + 59400*p*q**8*s - 9180*p**8*q**2*r*s - 115240*p**5*q**4*r*s - 589650*p**2*q**6*r*s + 16200*p**9*r**2*s + 316710*p**6*q**2*r**2*s + 2547750*p**3*q**4*r**2*s + 2178000*q**6*r**2*s - 259200*p**7*r**3*s - 4123000*p**4*q**2*r**3*s - 11700000*p*q**4*r**3*s + 937600*p**5*r**4*s + 16340000*p**2*q**2*r**4*s - 640000*p**3*r**5*s + 2800000*q**2*r**5*s - 2430*p**9*q*s**2 - 54450*p**6*q**3*s**2 - 285500*p**3*q**5*s**2 - 2767500*q**7*s**2 + 43200*p**7*q*r*s**2 - 916250*p**4*q**3*r*s**2 + 14482500*p*q**5*r*s**2 + 4806000*p**5*q*r**2*s**2 - 13212500*p**2*q**3*r**2*s**2 - 25400000*p**3*q*r**3*s**2 - 18750000*q**3*r**3*s**2 + 8000000*p*q*r**4*s**2 + 121500*p**8*s**3 + 2058750*p**5*q**2*s**3 - 6656250*p**2*q**4*s**3 - 6716250*p**6*r*s**3 + 24125000*p**3*q**2*r*s**3 + 23875000*q**4*r*s**3 + 43125000*p**4*r**2*s**3 + 45750000*p*q**2*r**2*s**3 - 87500000*p**2*r**3*s**3 + 70000000*r**4*s**3 - 44437500*p**4*q*s**4 - 107968750*p*q**3*s**4 + 159531250*p**2*q*r*s**4 - 284375000*q*r**2*s**4 + 7031250*p**3*s**5 + 265625000*q**2*s**5 + 31250000*p*r*s**5 c[1] = 160*p**3*q**7 + 1080*q**9 - 1080*p**4*q**5*r - 8730*p*q**7*r + 1510*p**5*q**3*r**2 + 20420*p**2*q**5*r**2 + 720*p**6*q*r**3 - 23200*p**3*q**3*r**3 - 79900*q**5*r**3 + 35200*p**4*q*r**4 + 404000*p*q**3*r**4 - 480000*p**2*q*r**5 + 960*p**5*q**4*s + 2850*p**2*q**6*s + 540*p**6*q**2*r*s + 63500*p**3*q**4*r*s + 319500*q**6*r*s - 7560*p**7*r**2*s - 253500*p**4*q**2*r**2*s - 1806250*p*q**4*r**2*s + 91200*p**5*r**3*s + 2600000*p**2*q**2*r**3*s - 80000*p**3*r**4*s + 600000*q**2*r**4*s - 4050*p**7*q*s**2 - 120000*p**4*q**3*s**2 - 273750*p*q**5*s**2 + 425250*p**5*q*r*s**2 + 2325000*p**2*q**3*r*s**2 - 5400000*p**3*q*r**2*s**2 - 2875000*q**3*r**2*s**2 + 1500000*p*q*r**3*s**2 - 303750*p**6*s**3 - 843750*p**3*q**2*s**3 - 812500*q**4*s**3 + 5062500*p**4*r*s**3 + 13312500*p*q**2*r*s**3 - 14500000*p**2*r**2*s**3 + 15000000*r**3*s**3 - 3750000*p**2*q*s**4 - 35937500*q*r*s**4 + 11718750*p*s**5 c[0] = 80*p**4*q**5 + 540*p*q**7 - 600*p**5*q**3*r - 4770*p**2*q**5*r + 1080*p**6*q*r**2 + 11200*p**3*q**3*r**2 - 12150*q**5*r**2 - 4800*p**4*q*r**3 + 64000*p*q**3*r**3 - 80000*p**2*q*r**4 + 1080*p**6*q**2*s + 13250*p**3*q**4*s + 54000*q**6*s - 3240*p**7*r*s - 56250*p**4*q**2*r*s - 337500*p*q**4*r*s + 43200*p**5*r**2*s + 560000*p**2*q**2*r**2*s - 80000*p**3*r**3*s + 100000*q**2*r**3*s + 6750*p**5*q*s**2 + 225000*p**2*q**3*s**2 - 900000*p**3*q*r*s**2 - 562500*q**3*r*s**2 + 500000*p*q*r**2*s**2 + 843750*p**4*s**3 + 1937500*p*q**2*s**3 - 3000000*p**2*r*s**3 + 2500000*r**2*s**3 - 5468750*q*s**4 return c @property def F(self): p, q, r, s = self.p, self.q, self.r, self.s F = 4*p**6*q**6 + 59*p**3*q**8 + 216*q**10 - 36*p**7*q**4*r - 623*p**4*q**6*r - 2610*p*q**8*r + 81*p**8*q**2*r**2 + 2015*p**5*q**4*r**2 + 10825*p**2*q**6*r**2 - 1800*p**6*q**2*r**3 - 17500*p**3*q**4*r**3 + 625*q**6*r**3 + 10000*p**4*q**2*r**4 + 108*p**8*q**3*s + 1584*p**5*q**5*s + 5700*p**2*q**7*s - 486*p**9*q*r*s - 9720*p**6*q**3*r*s - 45050*p**3*q**5*r*s - 9000*q**7*r*s + 10800*p**7*q*r**2*s + 92500*p**4*q**3*r**2*s + 32500*p*q**5*r**2*s - 60000*p**5*q*r**3*s - 50000*p**2*q**3*r**3*s + 729*p**10*s**2 + 12150*p**7*q**2*s**2 + 60000*p**4*q**4*s**2 + 93750*p*q**6*s**2 - 18225*p**8*r*s**2 - 175500*p**5*q**2*r*s**2 - 478125*p**2*q**4*r*s**2 + 135000*p**6*r**2*s**2 + 850000*p**3*q**2*r**2*s**2 + 15625*q**4*r**2*s**2 - 250000*p**4*r**3*s**2 + 225000*p**3*q**3*s**3 + 175000*q**5*s**3 - 1012500*p**4*q*r*s**3 - 1187500*p*q**3*r*s**3 + 1250000*p**2*q*r**2*s**3 + 928125*p**5*s**4 + 1875000*p**2*q**2*s**4 - 2812500*p**3*r*s**4 - 390625*q**2*r*s**4 - 9765625*s**6 return F def l0(self, theta): p, q, r, s, F = self.p, self.q, self.r, self.s, self.F a = self.a l0 = Poly(a, x).eval(theta)/F return l0 def T(self, theta, d): p, q, r, s, F = self.p, self.q, self.r, self.s, self.F T = [0]*5 b = self.b # Note that the order of sublists of the b's has been reversed compared to the paper T[1] = -Poly(b[1], x).eval(theta)/(2*F) T[2] = Poly(b[2], x).eval(theta)/(2*d*F) T[3] = Poly(b[3], x).eval(theta)/(2*F) T[4] = Poly(b[4], x).eval(theta)/(2*d*F) return T def order(self, theta, d): p, q, r, s, F = self.p, self.q, self.r, self.s, self.F o = self.o order = Poly(o, x).eval(theta)/(d*F) return N(order) def uv(self, theta, d): c = self.c u = S(-25*self.q/2) v = Poly(c, x).eval(theta)/(2*d*self.F) return N(u), N(v) @property def zeta(self): return [self.zeta1, self.zeta2, self.zeta3, self.zeta4] sympy-0.7.4.1/sympy/polys/numberfields.py0000644000175000017500000010333512253362407020631 0ustar georgeskgeorgesk"""Computational algebraic field theory. """ from __future__ import print_function, division from sympy import ( S, C, Expr, Rational, Symbol, Add, Mul, sympify, Q, ask, Dummy, Tuple, expand_mul, I, pi ) from sympy.polys.polytools import ( Poly, PurePoly, sqf_norm, invert, factor_list, groebner, resultant, degree, poly_from_expr, parallel_poly_from_expr, lcm ) from sympy.polys.polyclasses import ( ANP, DMP, ) from sympy.polys.polyerrors import ( IsomorphismFailed, CoercionFailed, NotAlgebraic, GeneratorsError, ) from sympy.polys.rootoftools import RootOf from sympy.polys.specialpolys import cyclotomic_poly from sympy.polys.polyutils import dict_from_expr, expr_from_dict from sympy.polys.domains import ZZ, QQ from sympy.polys.orthopolys import dup_chebyshevt from sympy.printing.lambdarepr import LambdaPrinter from sympy.utilities import ( numbered_symbols, variations, lambdify, public, sift ) from sympy.core.exprtools import Factors from sympy.simplify.simplify import _mexpand, _is_sum_surds from sympy.ntheory import sieve from sympy.ntheory.factor_ import divisors from sympy.mpmath import pslq, mp from sympy.core.compatibility import reduce from sympy.core.compatibility import xrange def _choose_factor(factors, x, v, dom=QQ, prec=200, bound=5): """ Return a factor having root ``v`` It is assumed that one of the factors has root ``v``. """ if isinstance(factors[0], tuple): factors = [f[0] for f in factors] if len(factors) == 1: return factors[0] points = {x:v} symbols = dom.symbols if hasattr(dom, 'symbols') else [] t = QQ(1, 10) for n in range(bound**len(symbols)): prec1 = 10 n_temp = n for s in symbols: points[s] = n_temp % bound n_temp = n_temp // bound while True: candidates = [] eps = t**(prec1 // 2) for f in factors: if abs(f.as_expr().evalf(prec1, points)) < eps: candidates.append(f) if candidates: factors = candidates if len(factors) == 1: return factors[0] if prec1 > prec: break prec1 *= 2 raise NotImplementedError("multiple candidates for the minimal polynomial of %s" % v) def _separate_sq(p): """ helper function for ``_minimal_polynomial_sq`` It selects a rational ``g`` such that the polynomial ``p`` consists of a sum of terms whose surds squared have gcd equal to ``g`` and a sum of terms with surds squared prime with ``g``; then it takes the field norm to eliminate ``sqrt(g)`` See simplify.simplify.split_surds and polytools.sqf_norm. Examples ======== >>> from sympy import sqrt >>> from sympy.abc import x >>> from sympy.polys.numberfields import _separate_sq >>> p= -x + sqrt(2) + sqrt(3) + sqrt(7) >>> p = _separate_sq(p); p -x**2 + 2*sqrt(3)*x + 2*sqrt(7)*x - 2*sqrt(21) - 8 >>> p = _separate_sq(p); p -x**4 + 4*sqrt(7)*x**3 - 32*x**2 + 8*sqrt(7)*x + 20 >>> p = _separate_sq(p); p -x**8 + 48*x**6 - 536*x**4 + 1728*x**2 - 400 """ from sympy.simplify.simplify import _split_gcd, _mexpand from sympy.utilities.iterables import sift def is_sqrt(expr): return expr.is_Pow and expr.exp is S.Half # p = c1*sqrt(q1) + ... + cn*sqrt(qn) -> a = [(c1, q1), .., (cn, qn)] a = [] for y in p.args: if not y.is_Mul: if is_sqrt(y): a.append((S.One, y**2)) elif y.is_Atom: a.append((y, S.One)) elif y.is_Pow and y.exp.is_integer: a.append((y, S.One)) else: raise NotImplementedError continue sifted = sift(y.args, is_sqrt) a.append((Mul(*sifted[False]), Mul(*sifted[True])**2)) a.sort(key=lambda z: z[1]) if a[-1][1] is S.One: # there are no surds return p surds = [z for y, z in a] for i in range(len(surds)): if surds[i] != 1: break g, b1, b2 = _split_gcd(*surds[i:]) a1 = [] a2 = [] for y, z in a: if z in b1: a1.append(y*z**S.Half) else: a2.append(y*z**S.Half) p1 = Add(*a1) p2 = Add(*a2) p = _mexpand(p1**2) - _mexpand(p2**2) return p def _minimal_polynomial_sq(p, n, x): """ Returns the minimal polynomial for the ``nth-root`` of a sum of surds or ``None`` if it fails. Parameters ========== p : sum of surds n : positive integer x : variable of the returned polynomial Examples ======== >>> from sympy.polys.numberfields import _minimal_polynomial_sq >>> from sympy import sqrt >>> from sympy.abc import x >>> q = 1 + sqrt(2) + sqrt(3) >>> _minimal_polynomial_sq(q, 3, x) x**12 - 4*x**9 - 4*x**6 + 16*x**3 - 8 """ from sympy.simplify.simplify import _is_sum_surds p = sympify(p) n = sympify(n) r = _is_sum_surds(p) if not n.is_Integer or not n > 0 or not _is_sum_surds(p): return None pn = p**Rational(1, n) # eliminate the square roots p -= x while 1: p1 = _separate_sq(p) if p1 is p: p = p1.subs({x:x**n}) break else: p = p1 # _separate_sq eliminates field extensions in a minimal way, so that # if n = 1 then `p = constant*(minimal_polynomial(p))` # if n > 1 it contains the minimal polynomial as a factor. if n == 1: p1 = Poly(p) if p.coeff(x**p1.degree(x)) < 0: p = -p p = p.primitive()[1] return p # by construction `p` has root `pn` # the minimal polynomial is the factor vanishing in x = pn factors = factor_list(p)[1] result = _choose_factor(factors, x, pn) return result def _minpoly_op_algebraic_element(op, ex1, ex2, x, dom, mp1=None, mp2=None): """ return the minimal polynomial for ``op(ex1, ex2)`` Parameters ========== op : operation ``Add`` or ``Mul`` ex1, ex2 : expressions for the algebraic elements x : indeterminate of the polynomials dom: ground domain mp1, mp2 : minimal polynomials for ``ex1`` and ``ex2`` or None Examples ======== >>> from sympy import sqrt, Add, Mul, QQ >>> from sympy.polys.numberfields import _minpoly_op_algebraic_element >>> from sympy.abc import x, y >>> p1 = sqrt(sqrt(2) + 1) >>> p2 = sqrt(sqrt(2) - 1) >>> _minpoly_op_algebraic_element(Mul, p1, p2, x, QQ) x - 1 >>> q1 = sqrt(y) >>> q2 = 1 / y >>> _minpoly_op_algebraic_element(Add, q1, q2, x, QQ.frac_field(y)) x**2*y**2 - 2*x*y - y**3 + 1 References ========== [1] http://en.wikipedia.org/wiki/Resultant [2] I.M. Isaacs, Proc. Amer. Math. Soc. 25 (1970), 638 "Degrees of sums in a separable field extension". """ from sympy import gcd y = Dummy(str(x)) if mp1 is None: mp1 = _minpoly_compose(ex1, x, dom) if mp2 is None: mp2 = _minpoly_compose(ex2, y, dom) else: mp2 = mp2.subs({x: y}) if op is Add: # mp1a = mp1.subs({x: x - y}) (p1, p2), _ = parallel_poly_from_expr((mp1, x - y), x, y) r = p1.compose(p2) mp1a = r.as_expr() elif op is Mul: mp1a = _muly(mp1, x, y) else: raise NotImplementedError('option not available') r = resultant(mp1a, mp2, gens=[y, x]) deg1 = degree(mp1, x) deg2 = degree(mp2, y) if op is Add and gcd(deg1, deg2) == 1: # `r` is irreducible, see [2] return r if op is Mul and deg1 == 1 or deg2 == 1: # if deg1 = 1, then mp1 = x - a; mp1a = x - y - a; # r = mp2(x - a), so that `r` is irreducible return r r = Poly(r, x, domain=dom) _, factors = r.factor_list() res = _choose_factor(factors, x, op(ex1, ex2), dom) return res.as_expr() def _invertx(p, x): """ Returns ``expand_mul(x**degree(p, x)*p.subs(x, 1/x))`` """ p1 = poly_from_expr(p, x)[0] n = degree(p1) a = [c * x**(n - i) for (i,), c in p1.terms()] return Add(*a) def _muly(p, x, y): """ Returns ``_mexpand(y**deg*p.subs({x:x / y}))`` """ p1 = poly_from_expr(p, x)[0] n = degree(p1) a = [c * x**i * y**(n - i) for (i,), c in p1.terms()] return Add(*a) def _minpoly_pow(ex, pw, x, dom, mp=None): """ Returns ``minpoly(ex**pw, x)`` Parameters ========== ex : algebraic element pw : rational number x : indeterminate of the polynomial dom: ground domain mp : minimal polynomial of ``p`` Examples ======== >>> from sympy import sqrt, QQ, Rational >>> from sympy.polys.numberfields import _minpoly_pow, minpoly >>> from sympy.abc import x, y >>> p = sqrt(1 + sqrt(2)) >>> _minpoly_pow(p, 2, x, QQ) x**2 - 2*x - 1 >>> minpoly(p**2, x) x**2 - 2*x - 1 >>> _minpoly_pow(y, Rational(1, 3), x, QQ.frac_field(y)) x**3 - y >>> minpoly(y**Rational(1, 3), x) x**3 - y """ pw = sympify(pw) if not mp: mp = _minpoly_compose(ex, x, dom) if not pw.is_rational: raise NotAlgebraic("%s doesn't seem to be an algebraic element" % ex) if pw < 0: if mp == x: raise ZeroDivisionError('%s is zero' % ex) mp = _invertx(mp, x) if pw == -1: return mp pw = -pw ex = 1/ex y = Dummy(str(x)) mp = mp.subs({x: y}) n, d = pw.as_numer_denom() res = Poly(resultant(mp, x**d - y**n, gens=[y]), x, domain=dom) _, factors = res.factor_list() res = _choose_factor(factors, x, ex**pw, dom) return res.as_expr() def _minpoly_add(x, dom, *a): """ returns ``minpoly(Add(*a), dom, x)`` """ mp = _minpoly_op_algebraic_element(Add, a[0], a[1], x, dom) p = a[0] + a[1] for px in a[2:]: mp = _minpoly_op_algebraic_element(Add, p, px, x, dom, mp1=mp) p = p + px return mp def _minpoly_mul(x, dom, *a): """ returns ``minpoly(Mul(*a), dom, x)`` """ mp = _minpoly_op_algebraic_element(Mul, a[0], a[1], x, dom) p = a[0] * a[1] for px in a[2:]: mp = _minpoly_op_algebraic_element(Mul, p, px, x, dom, mp1=mp) p = p * px return mp def _minpoly_sin(ex, x): """ Returns the minimal polynomial of ``sin(ex)`` see http://mathworld.wolfram.com/TrigonometryAngles.html """ from sympy.functions.combinatorial.factorials import binomial c, a = ex.args[0].as_coeff_Mul() if a is pi: if c.is_rational: n = c.q q = sympify(n) if q.is_prime: # for a = pi*p/q with q odd prime, using chebyshevt # write sin(q*a) = mp(sin(a))*sin(a); # the roots of mp(x) are sin(pi*p/q) for p = 1,..., q - 1 a = dup_chebyshevt(n, ZZ) return Add(*[x**(n - i - 1)*a[i] for i in range(n)]) if c.p == 1: if q == 9: return 64*x**6 - 96*x**4 + 36*x**2 - 3 if n % 2 == 1: # for a = pi*p/q with q odd, use # sin(q*a) = 0 to see that the minimal polynomial must be # a factor of dup_chebyshevt(n, ZZ) a = dup_chebyshevt(n, ZZ) a = [x**(n - i)*a[i] for i in range(n + 1)] r = Add(*a) _, factors = factor_list(r) res = _choose_factor(factors, x, ex) return res expr = ((1 - C.cos(2*c*pi))/2)**S.Half res = _minpoly_compose(expr, x, QQ) return res raise NotAlgebraic("%s doesn't seem to be an algebraic element" % ex) def _minpoly_cos(ex, x): """ Returns the minimal polynomial of ``cos(ex)`` see http://mathworld.wolfram.com/TrigonometryAngles.html """ from sympy import sqrt c, a = ex.args[0].as_coeff_Mul() if a is pi: if c.is_rational: if c.p == 1: if c.q == 7: return 8*x**3 - 4*x**2 - 4*x + 1 if c.q == 9: return 8*x**3 - 6*x + 1 elif c.p == 2: q = sympify(c.q) if q.is_prime: s = _minpoly_sin(ex, x) return _mexpand(s.subs({x:sqrt((1 - x)/2)})) # for a = pi*p/q, cos(q*a) =T_q(cos(a)) = (-1)**p n = int(c.q) a = dup_chebyshevt(n, ZZ) a = [x**(n - i)*a[i] for i in range(n + 1)] r = Add(*a) - (-1)**c.p _, factors = factor_list(r) res = _choose_factor(factors, x, ex) return res raise NotAlgebraic("%s doesn't seem to be an algebraic element" % ex) def _minpoly_exp(ex, x): """ Returns the minimal polynomial of ``exp(ex)`` """ c, a = ex.args[0].as_coeff_Mul() p = sympify(c.p) q = sympify(c.q) if a == I*pi: if c.is_rational: if c.p == 1 or c.p == -1: if q == 3: return x**2 - x + 1 if q == 4: return x**4 + 1 if q == 6: return x**4 - x**2 + 1 if q == 8: return x**8 + 1 if q == 9: return x**6 - x**3 + 1 if q == 10: return x**8 - x**6 + x**4 - x**2 + 1 if q.is_prime: s = 0 for i in range(q): s += (-x)**i return s # x**(2*q) = product(factors) factors = [cyclotomic_poly(i, x) for i in divisors(2*q)] mp = _choose_factor(factors, x, ex) return mp else: raise NotAlgebraic("%s doesn't seem to be an algebraic element" % ex) raise NotAlgebraic("%s doesn't seem to be an algebraic element" % ex) def _minpoly_rootof(ex, x): """ Returns the minimal polynomial of a ``RootOf`` object. """ p = ex.expr p = p.subs({ex.poly.gens[0]:x}) _, factors = factor_list(p, x) result = _choose_factor(factors, x, ex) return result def _minpoly_compose(ex, x, dom): """ Computes the minimal polynomial of an algebraic element using operations on minimal polynomials Examples ======== >>> from sympy import minimal_polynomial, sqrt, Rational >>> from sympy.abc import x, y >>> minimal_polynomial(sqrt(2) + 3*Rational(1, 3), x, compose=True) x**2 - 2*x - 1 >>> minimal_polynomial(sqrt(y) + 1/y, x, compose=True) x**2*y**2 - 2*x*y - y**3 + 1 """ if ex.is_Rational: return ex.q*x - ex.p if ex is I: return x**2 + 1 if hasattr(dom, 'symbols') and ex in dom.symbols: return x - ex if dom.is_QQ and _is_sum_surds(ex): # eliminate the square roots ex -= x while 1: ex1 = _separate_sq(ex) if ex1 is ex: return ex else: ex = ex1 if ex.is_Add: res = _minpoly_add(x, dom, *ex.args) elif ex.is_Mul: f = Factors(ex).factors r = sift(f.items(), lambda itx: itx[0].is_rational and itx[1].is_rational) if r[True] and dom == QQ: ex1 = Mul(*[bx**ex for bx, ex in r[False] + r[None]]) r1 = r[True] dens = [y.q for _, y in r1] lcmdens = reduce(lcm, dens, 1) nums = [base**(y.p*lcmdens // y.q) for base, y in r1] ex2 = Mul(*nums) mp1 = minimal_polynomial(ex1, x) # use the fact that in SymPy canonicalization products of integers # raised to rational powers are organized in relatively prime # bases, and that in ``base**(n/d)`` a perfect power is # simplified with the root mp2 = ex2.q*x**lcmdens - ex2.p ex2 = ex2**Rational(1, lcmdens) res = _minpoly_op_algebraic_element(Mul, ex1, ex2, x, dom, mp1=mp1, mp2=mp2) else: res = _minpoly_mul(x, dom, *ex.args) elif ex.is_Pow: res = _minpoly_pow(ex.base, ex.exp, x, dom) elif ex.__class__ is C.sin: res = _minpoly_sin(ex, x) elif ex.__class__ is C.cos: res = _minpoly_cos(ex, x) elif ex.__class__ is C.exp: res = _minpoly_exp(ex, x) elif ex.__class__ is RootOf: res = _minpoly_rootof(ex, x) else: raise NotAlgebraic("%s doesn't seem to be an algebraic element" % ex) return res @public def minimal_polynomial(ex, x=None, **args): """ Computes the minimal polynomial of an algebraic element. Parameters ========== ex : algebraic element expression x : independent variable of the minimal polynomial Options ======= compose : if ``True`` ``_minpoly_compose`` is used, if ``False`` the ``groebner`` algorithm polys : if ``True`` returns a ``Poly`` object domain : ground domain Notes ===== By default ``compose=True``, the minimal polynomial of the subexpressions of ``ex`` are computed, then the arithmetic operations on them are performed using the resultant and factorization. If ``compose=False``, a bottom-up algorithm is used with ``groebner``. The default algorithm stalls less frequently. If no ground domain is given, it will be generated automatically from the expression. Examples ======== >>> from sympy import minimal_polynomial, sqrt, solve, QQ >>> from sympy.abc import x, y >>> minimal_polynomial(sqrt(2), x) x**2 - 2 >>> minimal_polynomial(sqrt(2), x, domain=QQ.algebraic_field(sqrt(2))) x - sqrt(2) >>> minimal_polynomial(sqrt(2) + sqrt(3), x) x**4 - 10*x**2 + 1 >>> minimal_polynomial(solve(x**3 + x + 3)[0], x) x**3 + x + 3 >>> minimal_polynomial(sqrt(y), x) x**2 - y """ from sympy.polys.polytools import degree from sympy.polys.domains import FractionField from sympy.core.basic import preorder_traversal compose = args.get('compose', True) polys = args.get('polys', False) dom = args.get('domain', None) ex = sympify(ex) for expr in preorder_traversal(ex): if expr.is_AlgebraicNumber: compose = False break if x is not None: x, cls = sympify(x), Poly else: x, cls = Dummy('x'), PurePoly if not dom: dom = FractionField(QQ, list(ex.free_symbols)) if ex.free_symbols else QQ if hasattr(dom, 'symbols') and x in dom.symbols: raise GeneratorsError("the variable %s is an element of the ground domain %s" % (x, dom)) if compose: result = _minpoly_compose(ex, x, dom) result = result.primitive()[1] c = result.coeff(x**degree(result, x)) if c.is_negative: result = expand_mul(-result) return cls(result, x, field=True) if polys else result.collect(x) if not dom.is_QQ: raise NotImplementedError("groebner method only works for QQ") result = _minpoly_groebner(ex, x, cls) return cls(result, x, field=True) if polys else result.collect(x) def _minpoly_groebner(ex, x, cls): """ Computes the minimal polynomial of an algebraic number using Groebner bases Examples ======== >>> from sympy import minimal_polynomial, sqrt, Rational >>> from sympy.abc import x >>> minimal_polynomial(sqrt(2) + 3*Rational(1, 3), x, compose=False) x**2 - 2*x - 1 """ from sympy.polys.polytools import degree from sympy.core.function import expand_multinomial generator = numbered_symbols('a', cls=Dummy) mapping, symbols, replace = {}, {}, [] 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) def simpler_inverse(ex): """ Returns True if it is more likely that the minimal polynomial algorithm works better with the inverse """ if ex.is_Pow: if (1/ex.exp).is_integer and ex.exp < 0: if ex.base.is_Add: return True if ex.is_Mul: hit = True a = [] for p in ex.args: if p.is_Add: return False if p.is_Pow: if p.base.is_Add and p.exp > 0: return False if hit: return True return False inverted = False ex = expand_multinomial(ex) if ex.is_AlgebraicNumber: return ex.minpoly.as_expr(x) elif ex.is_Rational: result = ex.q*x - ex.p else: inverted = simpler_inverse(ex) if inverted: ex = ex**-1 res = None if ex.is_Pow and (1/ex.exp).is_Integer: n = 1/ex.exp res = _minimal_polynomial_sq(ex.base, n, x) elif _is_sum_surds(ex): res = _minimal_polynomial_sq(ex, S.One, x) if res is not None: result = res if res is None: bus = bottom_up_scan(ex) F = [x - bus] + list(mapping.values()) G = groebner(F, list(symbols.values()) + [x], order='lex') _, factors = factor_list(G[-1]) # by construction G[-1] has root `ex` result = _choose_factor(factors, x, ex) if inverted: result = _invertx(result, x) if result.coeff(x**degree(result, x)) < 0: result = expand_mul(-result) return result minpoly = minimal_polynomial __all__.append('minpoly') def _coeffs_generator(n): """Generate coefficients for `primitive_element()`. """ for coeffs in variations([1, -1], n, repetition=True): yield list(coeffs) @public 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(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 xrange(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 @public 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) @public 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)) @public 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 def is_aliased(self): """Returns ``True`` if ``alias`` was set. """ return self.alias is not None 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')) def as_expr(self, x=None): """Create a Basic expression from ``self``. """ return self.as_poly(x or self.root).as_expr().expand() 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() ] def native_coeffs(self): """Returns all native coefficients of an algebraic number. """ return self.rep.all_coeffs() 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) @public 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) sympy-0.7.4.1/sympy/polys/fglmtools.py0000644000175000017500000001050512253362407020154 0ustar georgeskgeorgesk"""Implementation of matrix FGLM Groebner basis conversion algorithm. """ from __future__ import print_function, division from sympy.polys.monomials import monomial_mul, monomial_div from sympy.core.compatibility import xrange def matrix_fglm(F, ring, O_to): """ 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 """ domain = ring.domain ngens = ring.ngens ring_to = ring.clone(order=O_to) old_basis = _basis(F, ring) M = _representing_matrices(old_basis, F, ring) # V contains the normalforms (wrt O_from) of S S = [ring.zero_monom] V = [[domain.one] + [domain.zero] * (len(old_basis) - 1)] G = [] L = [(i, 0) for i in xrange(ngens)] # (i, j) corresponds to x_i * S[j] L.sort(key=lambda k_l: O_to(_incr_k(S[k_l[1]], k_l[0])), reverse=True) t = L.pop() P = _identity_matrix(len(old_basis), domain) while True: s = len(S) v = _matrix_mul(M[t[0]], V[t[1]]) _lambda = _matrix_mul(P, v) if all(_lambda[i] == domain.zero for i in xrange(s, len(old_basis))): # there is a linear combination of v by V lt = ring.term_new(_incr_k(S[t[1]], t[0]), domain.one) rest = ring.from_dict(dict([ (S[i], _lambda[i]) for i in xrange(s) ])) g = (lt - rest).set_ring(ring_to) if g: G.append(g) else: # v is linearly independant from V P = _update(s, _lambda, P) S.append(_incr_k(S[t[1]], t[0])) V.append(v) L.extend([(i, s) for i in xrange(ngens)]) 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), g.LM) is None for g in G)] if not L: G = [ g.monic() for g in G ] return sorted(G, key=lambda g: O_to(g.LM), 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, domain): M = [[domain.zero]*n for _ in xrange(n)] for i in xrange(n): M[i][i] = domain.one return M def _matrix_mul(M, v): return [sum([row[i] * v[i] for i in xrange(len(v))]) for row in M] def _update(s, _lambda, P): """ Update ``P`` such that for the updated `P'` `P' v = e_{s}`. """ k = min([j for j in xrange(s, len(_lambda)) if _lambda[j] != 0]) for r in xrange(len(_lambda)): if r != k: P[r] = [P[r][j] - (P[k][j] * _lambda[r]) / _lambda[k] for j in xrange(len(P[r]))] P[k] = [P[k][j] / _lambda[k] for j in xrange(len(P[k]))] P[k], P[s] = P[s], P[k] return P def _representing_matrices(basis, G, ring): """ Compute the matrices corresponding to the linear maps `m \mapsto x_i m` for all variables `x_i`. """ domain = ring.domain u = ring.ngens-1 def var(i): return tuple([0] * i + [1] + [0] * (u - i)) def representing_matrix(m): M = [[domain.zero] * len(basis) for _ in xrange(len(basis))] for i, v in enumerate(basis): r = ring.term_new(monomial_mul(m, v), domain.one).rem(G) for monom, coeff in r.terms(): j = basis.index(monom) M[j][i] = coeff return M return [representing_matrix(var(i)) for i in xrange(u + 1)] def _basis(G, ring): """ 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)`. """ order = ring.order leading_monomials = [g.LM for g in G] candidates = [ring.zero_monom] basis = [] while candidates: t = candidates.pop() basis.append(t) new_candidates = [_incr_k(t, k) for k in xrange(ring.ngens) 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: order(m), reverse=True) basis = list(set(basis)) return sorted(basis, key=lambda m: order(m)) sympy-0.7.4.1/sympy/polys/factortools.py0000644000175000017500000010220712253362407020506 0ustar georgeskgeorgesk"""Polynomial factorization routines in characteristic zero. """ from __future__ import print_function, division 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, dup_quo_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 from math import ceil as _ceil, log as _log from sympy.core.compatibility import xrange 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) 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 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 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 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) def _test_pl(fc, q, pl): if q > pl // 2: q = q - pl if not q: return True return fc % q == 0 def dup_zz_zassenhaus(f, K): """Factor primitive square-free polynomials in `Z[x]`. """ n = dup_degree(f) if n == 1: return [f] fc = f[-1] 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)) a = [] # choose a prime number `p` such that `f` be square free in Z_p # if there are many factors in Z_p, choose among a few different `p` # the one with fewer factors for px in xrange(3, bound + 1): if not isprime(px) or b % px == 0: continue px = K.convert(px) F = gf_from_int_poly(f, px) if not gf_sqf_p(F, px, K): continue fsqfx = gf_factor_sqf(F, px, K)[1] a.append((px, fsqfx)) if len(fsqfx) < 15 or len(a) > 4: break p, fsqf = min(a, key=lambda x: len(x[1])) l = int(_ceil(_log(2*B + 1, p))) modular = [gf_to_int_poly(ff, p) for ff in fsqf] g = dup_zz_hensel_lift(p, f, modular, l, K) sorted_T = range(len(g)) T = set(sorted_T) factors, s = [], 1 pl = p**l while 2*s <= len(T): for S in subsets(sorted_T, s): # lift the constant coefficient of the product `G` of the factors # in the subset `S`; if it is does not divide `fc`, `G` does # not divide the input polynomial if b == 1: q = 1 for i in S: q = q*g[i][-1] q = q % pl if not _test_pl(fc, q, pl): continue else: G = [b] for i in S: G = dup_mul(G, g[i], K) G = dup_trunc(G, pl, K) G1 = dup_primitive(G, K)[1] q = G1[-1] if q and fc % q != 0: continue H = [b] S = set(S) T_S = T - S if b == 1: G = [b] for i in S: G = dup_mul(G, g[i], K) G = dup_trunc(G, pl, K) for i in T_S: H = dup_mul(H, g[i], K) H = dup_trunc(H, pl, 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] 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 def dup_cyclotomic_p(f, K, irreducible=False): """ Efficiently test if ``f`` is a cyclotomic polnomial. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> f = x**16 + x**14 - x**10 + x**8 - x**6 + x**2 + 1 >>> R.dup_cyclotomic_p(f) False >>> g = x**16 + x**14 - x**10 - x**8 - x**6 + x**2 + 1 >>> R.dup_cyclotomic_p(g) 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 xrange(n, -1, -2): g.insert(0, f[i]) for i in xrange(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 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 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 xrange(1, k): Q = [ dup_inflate(q, p, K) for q in Q ] H.extend(Q) return H 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 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] if query('USE_IRREDUCIBLE_IN_FACTOR'): if dup_zz_irreducible_p(g, K): return cont, [g] 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) 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_zz_factor(2*x**4 - 2) (2, [(x - 1, 1), (x + 1, 1), (x**2 + 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 = None if query('USE_CYCLOTOMIC_FACTOR'): H = dup_zz_cyclotomic_factor(g, K) if H is None: H = dup_zz_zassenhaus(g, K) factors = dup_trial_division(f, H, K) return cont, factors 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:] 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') 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(xrange(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 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 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(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(xrange(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(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 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(xrange(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(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(xrange(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(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 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 xrange(eez_num_tries): A = [ K(randint(-mod, mod)) for _ in xrange(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 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 import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_zz_factor(2*x**2 - 2*y**2) (2, [(x - y, 1), (x + y, 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) factors = dmp_trial_division(f, H, u, 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 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) 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 NotImplementedError('multivariate polynomials over finite fields') def dup_factor_list(f, K0): """Factor polynomials into irreducibles in `K[x]`. """ j, f = dup_terms_gcd(f, K0) cont, f = dup_primitive(f, K0) if K0.is_FiniteField: 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) if K0_inexact is None: coeff = coeff/denom else: for i, (f, k) in enumerate(factors): f = dup_quo_ground(f, denom, K0) f = dup_convert(f, K0, K0_inexact) factors[i] = (f, k) coeff = K0_inexact.convert(coeff, K0) K0 = K0_inexact if j: factors.insert(0, ([K0.one, K0.zero], j)) return coeff*cont, _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:] 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) cont, f = dmp_ground_primitive(f, u, K0) if K0.is_FiniteField: # 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) if K0_inexact is None: coeff = coeff/denom else: for i, (f, k) in enumerate(factors): f = dmp_quo_ground(f, denom, u, K0) f = dmp_convert(f, u, K0, K0_inexact) factors[i] = (f, k) coeff = K0_inexact.convert(coeff, K0) K0 = K0_inexact 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*cont, _sort_factors(factors) 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) 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 sympy-0.7.4.1/sympy/polys/benchmarks/0000755000175000017500000000000012253362407017710 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/polys/benchmarks/bench_groebnertools.py0000644000175000017500000000152312253362407024306 0ustar georgeskgeorgesk"""Benchmark of the Groebner bases algorithms. """ from __future__ import print_function, division from sympy.polys.rings import ring from sympy.polys.domains import QQ from sympy.polys.groebnertools import groebner R, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12 = ring("x1:13", QQ) V = R.gens E = [(x1, x2), (x2, x3), (x1, x4), (x1, x6), (x1, x12), (x2, x5), (x2, x7), (x3, x8), (x3, x10), (x4, x11), (x4, x9), (x5, x6), (x6, x7), (x7, x8), (x8, x9), (x9, x10), (x10, x11), (x11, x12), (x5, x12), (x5, x9), (x6, x10), (x7, x11), (x8, x12)] F3 = [ x**3 - 1 for x in V ] Fg = [ x**2 + x*y + y**2 for x, y in E ] F_1 = F3 + Fg F_2 = F3 + Fg + [x3**2 + x3*x4 + x4**2] def time_vertex_color_12_vertices_23_edges(): assert groebner(F_1, R) != [1] def time_vertex_color_12_vertices_24_edges(): assert groebner(F_2, R) == [1] sympy-0.7.4.1/sympy/polys/benchmarks/bench_solvers.py0000644000175000017500000154774512253362407023145 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.polys.rings import ring from sympy.polys.fields import field from sympy.polys.domains import ZZ, QQ from sympy.polys.solvers import solve_lin_sys # Expected times on 3.4 GHz i7: # In [1]: %timeit time_solve_lin_sys_189x49() # 1 loops, best of 3: 864 ms per loop # In [2]: %timeit time_solve_lin_sys_165x165() # 1 loops, best of 3: 1.83 s per loop # In [3]: %timeit time_solve_lin_sys_10x8() # 1 loops, best of 3: 2.31 s per loop # Benchmark R_165: shows how fast are arithmetics in QQ. R_165, uk_0, uk_1, uk_2, uk_3, uk_4, uk_5, uk_6, uk_7, uk_8, uk_9, uk_10, uk_11, uk_12, uk_13, uk_14, uk_15, uk_16, uk_17, uk_18, uk_19, uk_20, uk_21, uk_22, uk_23, uk_24, uk_25, uk_26, uk_27, uk_28, uk_29, uk_30, uk_31, uk_32, uk_33, uk_34, uk_35, uk_36, uk_37, uk_38, uk_39, uk_40, uk_41, uk_42, uk_43, uk_44, uk_45, uk_46, uk_47, uk_48, uk_49, uk_50, uk_51, uk_52, uk_53, uk_54, uk_55, uk_56, uk_57, uk_58, uk_59, uk_60, uk_61, uk_62, uk_63, uk_64, uk_65, uk_66, uk_67, uk_68, uk_69, uk_70, uk_71, uk_72, uk_73, uk_74, uk_75, uk_76, uk_77, uk_78, uk_79, uk_80, uk_81, uk_82, uk_83, uk_84, uk_85, uk_86, uk_87, uk_88, uk_89, uk_90, uk_91, uk_92, uk_93, uk_94, uk_95, uk_96, uk_97, uk_98, uk_99, uk_100, uk_101, uk_102, uk_103, uk_104, uk_105, uk_106, uk_107, uk_108, uk_109, uk_110, uk_111, uk_112, uk_113, uk_114, uk_115, uk_116, uk_117, uk_118, uk_119, uk_120, uk_121, uk_122, uk_123, uk_124, uk_125, uk_126, uk_127, uk_128, uk_129, uk_130, uk_131, uk_132, uk_133, uk_134, uk_135, uk_136, uk_137, uk_138, uk_139, uk_140, uk_141, uk_142, uk_143, uk_144, uk_145, uk_146, uk_147, uk_148, uk_149, uk_150, uk_151, uk_152, uk_153, uk_154, uk_155, uk_156, uk_157, uk_158, uk_159, uk_160, uk_161, uk_162, uk_163, uk_164 = ring("uk_:165", QQ) def eqs_165x165(): return [ uk_0 + 50719*uk_1 + 2789545*uk_10 + 411400*uk_100 + 1683000*uk_101 + 166375*uk_103 + 680625*uk_104 + 2784375*uk_106 + 729*uk_109 + 456471*uk_11 + 4131*uk_110 + 11016*uk_111 + 4455*uk_112 + 18225*uk_113 + 23409*uk_115 + 62424*uk_116 + 25245*uk_117 + 103275*uk_118 + 2586669*uk_12 + 166464*uk_120 + 67320*uk_121 + 275400*uk_122 + 27225*uk_124 + 111375*uk_125 + 455625*uk_127 + 6897784*uk_13 + 132651*uk_130 + 353736*uk_131 + 143055*uk_132 + 585225*uk_133 + 943296*uk_135 + 381480*uk_136 + 1560600*uk_137 + 154275*uk_139 + 2789545*uk_14 + 631125*uk_140 + 2581875*uk_142 + 2515456*uk_145 + 1017280*uk_146 + 4161600*uk_147 + 411400*uk_149 + 11411775*uk_15 + 1683000*uk_150 + 6885000*uk_152 + 166375*uk_155 + 680625*uk_156 + 2784375*uk_158 + 11390625*uk_161 + 3025*uk_17 + 495*uk_18 + 2805*uk_19 + 55*uk_2 + 7480*uk_20 + 3025*uk_21 + 12375*uk_22 + 81*uk_24 + 459*uk_25 + 1224*uk_26 + 495*uk_27 + 2025*uk_28 + 9*uk_3 + 2601*uk_30 + 6936*uk_31 + 2805*uk_32 + 11475*uk_33 + 18496*uk_35 + 7480*uk_36 + 30600*uk_37 + 3025*uk_39 + 51*uk_4 + 12375*uk_40 + 50625*uk_42 + 130470415844959*uk_45 + 141482932855*uk_46 + 23151752649*uk_47 + 131193265011*uk_48 + 349848706696*uk_49 + 136*uk_5 + 141482932855*uk_50 + 578793816225*uk_51 + 153424975*uk_53 + 25105905*uk_54 + 142266795*uk_55 + 379378120*uk_56 + 153424975*uk_57 + 627647625*uk_58 + 55*uk_6 + 4108239*uk_60 + 23280021*uk_61 + 62080056*uk_62 + 25105905*uk_63 + 102705975*uk_64 + 131920119*uk_66 + 351786984*uk_67 + 142266795*uk_68 + 582000525*uk_69 + 225*uk_7 + 938098624*uk_71 + 379378120*uk_72 + 1552001400*uk_73 + 153424975*uk_75 + 627647625*uk_76 + 2567649375*uk_78 + 166375*uk_81 + 27225*uk_82 + 154275*uk_83 + 411400*uk_84 + 166375*uk_85 + 680625*uk_86 + 4455*uk_88 + 25245*uk_89 + 2572416961*uk_9 + 67320*uk_90 + 27225*uk_91 + 111375*uk_92 + 143055*uk_94 + 381480*uk_95 + 154275*uk_96 + 631125*uk_97 + 1017280*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 413820*uk_100 + 1633500*uk_101 + 65340*uk_102 + 178695*uk_103 + 705375*uk_104 + 28215*uk_105 + 2784375*uk_106 + 111375*uk_107 + 4455*uk_108 + 97336*uk_109 + 2333074*uk_11 + 19044*uk_110 + 279312*uk_111 + 120612*uk_112 + 476100*uk_113 + 19044*uk_114 + 3726*uk_115 + 54648*uk_116 + 23598*uk_117 + 93150*uk_118 + 3726*uk_119 + 456471*uk_12 + 801504*uk_120 + 346104*uk_121 + 1366200*uk_122 + 54648*uk_123 + 149454*uk_124 + 589950*uk_125 + 23598*uk_126 + 2328750*uk_127 + 93150*uk_128 + 3726*uk_129 + 6694908*uk_13 + 729*uk_130 + 10692*uk_131 + 4617*uk_132 + 18225*uk_133 + 729*uk_134 + 156816*uk_135 + 67716*uk_136 + 267300*uk_137 + 10692*uk_138 + 29241*uk_139 + 2890983*uk_14 + 115425*uk_140 + 4617*uk_141 + 455625*uk_142 + 18225*uk_143 + 729*uk_144 + 2299968*uk_145 + 993168*uk_146 + 3920400*uk_147 + 156816*uk_148 + 428868*uk_149 + 11411775*uk_15 + 1692900*uk_150 + 67716*uk_151 + 6682500*uk_152 + 267300*uk_153 + 10692*uk_154 + 185193*uk_155 + 731025*uk_156 + 29241*uk_157 + 2885625*uk_158 + 115425*uk_159 + 456471*uk_16 + 4617*uk_160 + 11390625*uk_161 + 455625*uk_162 + 18225*uk_163 + 729*uk_164 + 3025*uk_17 + 2530*uk_18 + 495*uk_19 + 55*uk_2 + 7260*uk_20 + 3135*uk_21 + 12375*uk_22 + 495*uk_23 + 2116*uk_24 + 414*uk_25 + 6072*uk_26 + 2622*uk_27 + 10350*uk_28 + 414*uk_29 + 46*uk_3 + 81*uk_30 + 1188*uk_31 + 513*uk_32 + 2025*uk_33 + 81*uk_34 + 17424*uk_35 + 7524*uk_36 + 29700*uk_37 + 1188*uk_38 + 3249*uk_39 + 9*uk_4 + 12825*uk_40 + 513*uk_41 + 50625*uk_42 + 2025*uk_43 + 81*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 118331180206*uk_47 + 23151752649*uk_48 + 339559038852*uk_49 + 132*uk_5 + 146627766777*uk_50 + 578793816225*uk_51 + 23151752649*uk_52 + 153424975*uk_53 + 128319070*uk_54 + 25105905*uk_55 + 368219940*uk_56 + 159004065*uk_57 + 627647625*uk_58 + 25105905*uk_59 + 57*uk_6 + 107321404*uk_60 + 20997666*uk_61 + 307965768*uk_62 + 132985218*uk_63 + 524941650*uk_64 + 20997666*uk_65 + 4108239*uk_66 + 60254172*uk_67 + 26018847*uk_68 + 102705975*uk_69 + 225*uk_7 + 4108239*uk_70 + 883727856*uk_71 + 381609756*uk_72 + 1506354300*uk_73 + 60254172*uk_74 + 164786031*uk_75 + 650471175*uk_76 + 26018847*uk_77 + 2567649375*uk_78 + 102705975*uk_79 + 9*uk_8 + 4108239*uk_80 + 166375*uk_81 + 139150*uk_82 + 27225*uk_83 + 399300*uk_84 + 172425*uk_85 + 680625*uk_86 + 27225*uk_87 + 116380*uk_88 + 22770*uk_89 + 2572416961*uk_9 + 333960*uk_90 + 144210*uk_91 + 569250*uk_92 + 22770*uk_93 + 4455*uk_94 + 65340*uk_95 + 28215*uk_96 + 111375*uk_97 + 4455*uk_98 + 958320*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 402380*uk_100 + 1534500*uk_101 + 313720*uk_102 + 191455*uk_103 + 730125*uk_104 + 149270*uk_105 + 2784375*uk_106 + 569250*uk_107 + 116380*uk_108 + 912673*uk_109 + 4919743*uk_11 + 432814*uk_110 + 1166716*uk_111 + 555131*uk_112 + 2117025*uk_113 + 432814*uk_114 + 205252*uk_115 + 553288*uk_116 + 263258*uk_117 + 1003950*uk_118 + 205252*uk_119 + 2333074*uk_12 + 1491472*uk_120 + 709652*uk_121 + 2706300*uk_122 + 553288*uk_123 + 337657*uk_124 + 1287675*uk_125 + 263258*uk_126 + 4910625*uk_127 + 1003950*uk_128 + 205252*uk_129 + 6289156*uk_13 + 97336*uk_130 + 262384*uk_131 + 124844*uk_132 + 476100*uk_133 + 97336*uk_134 + 707296*uk_135 + 336536*uk_136 + 1283400*uk_137 + 262384*uk_138 + 160126*uk_139 + 2992421*uk_14 + 610650*uk_140 + 124844*uk_141 + 2328750*uk_142 + 476100*uk_143 + 97336*uk_144 + 1906624*uk_145 + 907184*uk_146 + 3459600*uk_147 + 707296*uk_148 + 431644*uk_149 + 11411775*uk_15 + 1646100*uk_150 + 336536*uk_151 + 6277500*uk_152 + 1283400*uk_153 + 262384*uk_154 + 205379*uk_155 + 783225*uk_156 + 160126*uk_157 + 2986875*uk_158 + 610650*uk_159 + 2333074*uk_16 + 124844*uk_160 + 11390625*uk_161 + 2328750*uk_162 + 476100*uk_163 + 97336*uk_164 + 3025*uk_17 + 5335*uk_18 + 2530*uk_19 + 55*uk_2 + 6820*uk_20 + 3245*uk_21 + 12375*uk_22 + 2530*uk_23 + 9409*uk_24 + 4462*uk_25 + 12028*uk_26 + 5723*uk_27 + 21825*uk_28 + 4462*uk_29 + 97*uk_3 + 2116*uk_30 + 5704*uk_31 + 2714*uk_32 + 10350*uk_33 + 2116*uk_34 + 15376*uk_35 + 7316*uk_36 + 27900*uk_37 + 5704*uk_38 + 3481*uk_39 + 46*uk_4 + 13275*uk_40 + 2714*uk_41 + 50625*uk_42 + 10350*uk_43 + 2116*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 249524445217*uk_47 + 118331180206*uk_48 + 318979703164*uk_49 + 124*uk_5 + 151772600699*uk_50 + 578793816225*uk_51 + 118331180206*uk_52 + 153424975*uk_53 + 270585865*uk_54 + 128319070*uk_55 + 345903580*uk_56 + 164583155*uk_57 + 627647625*uk_58 + 128319070*uk_59 + 59*uk_6 + 477215071*uk_60 + 226308178*uk_61 + 610048132*uk_62 + 290264837*uk_63 + 1106942175*uk_64 + 226308178*uk_65 + 107321404*uk_66 + 289301176*uk_67 + 137651366*uk_68 + 524941650*uk_69 + 225*uk_7 + 107321404*uk_70 + 779855344*uk_71 + 371060204*uk_72 + 1415060100*uk_73 + 289301176*uk_74 + 176552839*uk_75 + 673294725*uk_76 + 137651366*uk_77 + 2567649375*uk_78 + 524941650*uk_79 + 46*uk_8 + 107321404*uk_80 + 166375*uk_81 + 293425*uk_82 + 139150*uk_83 + 375100*uk_84 + 178475*uk_85 + 680625*uk_86 + 139150*uk_87 + 517495*uk_88 + 245410*uk_89 + 2572416961*uk_9 + 661540*uk_90 + 314765*uk_91 + 1200375*uk_92 + 245410*uk_93 + 116380*uk_94 + 313720*uk_95 + 149270*uk_96 + 569250*uk_97 + 116380*uk_98 + 845680*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 389180*uk_100 + 1435500*uk_101 + 618860*uk_102 + 204655*uk_103 + 754875*uk_104 + 325435*uk_105 + 2784375*uk_106 + 1200375*uk_107 + 517495*uk_108 + 3375000*uk_109 + 7607850*uk_11 + 2182500*uk_110 + 2610000*uk_111 + 1372500*uk_112 + 5062500*uk_113 + 2182500*uk_114 + 1411350*uk_115 + 1687800*uk_116 + 887550*uk_117 + 3273750*uk_118 + 1411350*uk_119 + 4919743*uk_12 + 2018400*uk_120 + 1061400*uk_121 + 3915000*uk_122 + 1687800*uk_123 + 558150*uk_124 + 2058750*uk_125 + 887550*uk_126 + 7593750*uk_127 + 3273750*uk_128 + 1411350*uk_129 + 5883404*uk_13 + 912673*uk_130 + 1091444*uk_131 + 573949*uk_132 + 2117025*uk_133 + 912673*uk_134 + 1305232*uk_135 + 686372*uk_136 + 2531700*uk_137 + 1091444*uk_138 + 360937*uk_139 + 3093859*uk_14 + 1331325*uk_140 + 573949*uk_141 + 4910625*uk_142 + 2117025*uk_143 + 912673*uk_144 + 1560896*uk_145 + 820816*uk_146 + 3027600*uk_147 + 1305232*uk_148 + 431636*uk_149 + 11411775*uk_15 + 1592100*uk_150 + 686372*uk_151 + 5872500*uk_152 + 2531700*uk_153 + 1091444*uk_154 + 226981*uk_155 + 837225*uk_156 + 360937*uk_157 + 3088125*uk_158 + 1331325*uk_159 + 4919743*uk_16 + 573949*uk_160 + 11390625*uk_161 + 4910625*uk_162 + 2117025*uk_163 + 912673*uk_164 + 3025*uk_17 + 8250*uk_18 + 5335*uk_19 + 55*uk_2 + 6380*uk_20 + 3355*uk_21 + 12375*uk_22 + 5335*uk_23 + 22500*uk_24 + 14550*uk_25 + 17400*uk_26 + 9150*uk_27 + 33750*uk_28 + 14550*uk_29 + 150*uk_3 + 9409*uk_30 + 11252*uk_31 + 5917*uk_32 + 21825*uk_33 + 9409*uk_34 + 13456*uk_35 + 7076*uk_36 + 26100*uk_37 + 11252*uk_38 + 3721*uk_39 + 97*uk_4 + 13725*uk_40 + 5917*uk_41 + 50625*uk_42 + 21825*uk_43 + 9409*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 385862544150*uk_47 + 249524445217*uk_48 + 298400367476*uk_49 + 116*uk_5 + 156917434621*uk_50 + 578793816225*uk_51 + 249524445217*uk_52 + 153424975*uk_53 + 418431750*uk_54 + 270585865*uk_55 + 323587220*uk_56 + 170162245*uk_57 + 627647625*uk_58 + 270585865*uk_59 + 61*uk_6 + 1141177500*uk_60 + 737961450*uk_61 + 882510600*uk_62 + 464078850*uk_63 + 1711766250*uk_64 + 737961450*uk_65 + 477215071*uk_66 + 570690188*uk_67 + 300104323*uk_68 + 1106942175*uk_69 + 225*uk_7 + 477215071*uk_70 + 682474864*uk_71 + 358887644*uk_72 + 1323765900*uk_73 + 570690188*uk_74 + 188725399*uk_75 + 696118275*uk_76 + 300104323*uk_77 + 2567649375*uk_78 + 1106942175*uk_79 + 97*uk_8 + 477215071*uk_80 + 166375*uk_81 + 453750*uk_82 + 293425*uk_83 + 350900*uk_84 + 184525*uk_85 + 680625*uk_86 + 293425*uk_87 + 1237500*uk_88 + 800250*uk_89 + 2572416961*uk_9 + 957000*uk_90 + 503250*uk_91 + 1856250*uk_92 + 800250*uk_93 + 517495*uk_94 + 618860*uk_95 + 325435*uk_96 + 1200375*uk_97 + 517495*uk_98 + 740080*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 374220*uk_100 + 1336500*uk_101 + 891000*uk_102 + 218295*uk_103 + 779625*uk_104 + 519750*uk_105 + 2784375*uk_106 + 1856250*uk_107 + 1237500*uk_108 + 7189057*uk_109 + 9788767*uk_11 + 5587350*uk_110 + 4022892*uk_111 + 2346687*uk_112 + 8381025*uk_113 + 5587350*uk_114 + 4342500*uk_115 + 3126600*uk_116 + 1823850*uk_117 + 6513750*uk_118 + 4342500*uk_119 + 7607850*uk_12 + 2251152*uk_120 + 1313172*uk_121 + 4689900*uk_122 + 3126600*uk_123 + 766017*uk_124 + 2735775*uk_125 + 1823850*uk_126 + 9770625*uk_127 + 6513750*uk_128 + 4342500*uk_129 + 5477652*uk_13 + 3375000*uk_130 + 2430000*uk_131 + 1417500*uk_132 + 5062500*uk_133 + 3375000*uk_134 + 1749600*uk_135 + 1020600*uk_136 + 3645000*uk_137 + 2430000*uk_138 + 595350*uk_139 + 3195297*uk_14 + 2126250*uk_140 + 1417500*uk_141 + 7593750*uk_142 + 5062500*uk_143 + 3375000*uk_144 + 1259712*uk_145 + 734832*uk_146 + 2624400*uk_147 + 1749600*uk_148 + 428652*uk_149 + 11411775*uk_15 + 1530900*uk_150 + 1020600*uk_151 + 5467500*uk_152 + 3645000*uk_153 + 2430000*uk_154 + 250047*uk_155 + 893025*uk_156 + 595350*uk_157 + 3189375*uk_158 + 2126250*uk_159 + 7607850*uk_16 + 1417500*uk_160 + 11390625*uk_161 + 7593750*uk_162 + 5062500*uk_163 + 3375000*uk_164 + 3025*uk_17 + 10615*uk_18 + 8250*uk_19 + 55*uk_2 + 5940*uk_20 + 3465*uk_21 + 12375*uk_22 + 8250*uk_23 + 37249*uk_24 + 28950*uk_25 + 20844*uk_26 + 12159*uk_27 + 43425*uk_28 + 28950*uk_29 + 193*uk_3 + 22500*uk_30 + 16200*uk_31 + 9450*uk_32 + 33750*uk_33 + 22500*uk_34 + 11664*uk_35 + 6804*uk_36 + 24300*uk_37 + 16200*uk_38 + 3969*uk_39 + 150*uk_4 + 14175*uk_40 + 9450*uk_41 + 50625*uk_42 + 33750*uk_43 + 22500*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 496476473473*uk_47 + 385862544150*uk_48 + 277821031788*uk_49 + 108*uk_5 + 162062268543*uk_50 + 578793816225*uk_51 + 385862544150*uk_52 + 153424975*uk_53 + 538382185*uk_54 + 418431750*uk_55 + 301270860*uk_56 + 175741335*uk_57 + 627647625*uk_58 + 418431750*uk_59 + 63*uk_6 + 1889232031*uk_60 + 1468315050*uk_61 + 1057186836*uk_62 + 616692321*uk_63 + 2202472575*uk_64 + 1468315050*uk_65 + 1141177500*uk_66 + 821647800*uk_67 + 479294550*uk_68 + 1711766250*uk_69 + 225*uk_7 + 1141177500*uk_70 + 591586416*uk_71 + 345092076*uk_72 + 1232471700*uk_73 + 821647800*uk_74 + 201303711*uk_75 + 718941825*uk_76 + 479294550*uk_77 + 2567649375*uk_78 + 1711766250*uk_79 + 150*uk_8 + 1141177500*uk_80 + 166375*uk_81 + 583825*uk_82 + 453750*uk_83 + 326700*uk_84 + 190575*uk_85 + 680625*uk_86 + 453750*uk_87 + 2048695*uk_88 + 1592250*uk_89 + 2572416961*uk_9 + 1146420*uk_90 + 668745*uk_91 + 2388375*uk_92 + 1592250*uk_93 + 1237500*uk_94 + 891000*uk_95 + 519750*uk_96 + 1856250*uk_97 + 1237500*uk_98 + 641520*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 357500*uk_100 + 1237500*uk_101 + 1061500*uk_102 + 232375*uk_103 + 804375*uk_104 + 689975*uk_105 + 2784375*uk_106 + 2388375*uk_107 + 2048695*uk_108 + 9800344*uk_109 + 10853866*uk_11 + 8838628*uk_110 + 4579600*uk_111 + 2976740*uk_112 + 10304100*uk_113 + 8838628*uk_114 + 7971286*uk_115 + 4130200*uk_116 + 2684630*uk_117 + 9292950*uk_118 + 7971286*uk_119 + 9788767*uk_12 + 2140000*uk_120 + 1391000*uk_121 + 4815000*uk_122 + 4130200*uk_123 + 904150*uk_124 + 3129750*uk_125 + 2684630*uk_126 + 10833750*uk_127 + 9292950*uk_128 + 7971286*uk_129 + 5071900*uk_13 + 7189057*uk_130 + 3724900*uk_131 + 2421185*uk_132 + 8381025*uk_133 + 7189057*uk_134 + 1930000*uk_135 + 1254500*uk_136 + 4342500*uk_137 + 3724900*uk_138 + 815425*uk_139 + 3296735*uk_14 + 2822625*uk_140 + 2421185*uk_141 + 9770625*uk_142 + 8381025*uk_143 + 7189057*uk_144 + 1000000*uk_145 + 650000*uk_146 + 2250000*uk_147 + 1930000*uk_148 + 422500*uk_149 + 11411775*uk_15 + 1462500*uk_150 + 1254500*uk_151 + 5062500*uk_152 + 4342500*uk_153 + 3724900*uk_154 + 274625*uk_155 + 950625*uk_156 + 815425*uk_157 + 3290625*uk_158 + 2822625*uk_159 + 9788767*uk_16 + 2421185*uk_160 + 11390625*uk_161 + 9770625*uk_162 + 8381025*uk_163 + 7189057*uk_164 + 3025*uk_17 + 11770*uk_18 + 10615*uk_19 + 55*uk_2 + 5500*uk_20 + 3575*uk_21 + 12375*uk_22 + 10615*uk_23 + 45796*uk_24 + 41302*uk_25 + 21400*uk_26 + 13910*uk_27 + 48150*uk_28 + 41302*uk_29 + 214*uk_3 + 37249*uk_30 + 19300*uk_31 + 12545*uk_32 + 43425*uk_33 + 37249*uk_34 + 10000*uk_35 + 6500*uk_36 + 22500*uk_37 + 19300*uk_38 + 4225*uk_39 + 193*uk_4 + 14625*uk_40 + 12545*uk_41 + 50625*uk_42 + 43425*uk_43 + 37249*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 550497229654*uk_47 + 496476473473*uk_48 + 257241696100*uk_49 + 100*uk_5 + 167207102465*uk_50 + 578793816225*uk_51 + 496476473473*uk_52 + 153424975*uk_53 + 596962630*uk_54 + 538382185*uk_55 + 278954500*uk_56 + 181320425*uk_57 + 627647625*uk_58 + 538382185*uk_59 + 65*uk_6 + 2322727324*uk_60 + 2094796138*uk_61 + 1085386600*uk_62 + 705501290*uk_63 + 2442119850*uk_64 + 2094796138*uk_65 + 1889232031*uk_66 + 978876700*uk_67 + 636269855*uk_68 + 2202472575*uk_69 + 225*uk_7 + 1889232031*uk_70 + 507190000*uk_71 + 329673500*uk_72 + 1141177500*uk_73 + 978876700*uk_74 + 214287775*uk_75 + 741765375*uk_76 + 636269855*uk_77 + 2567649375*uk_78 + 2202472575*uk_79 + 193*uk_8 + 1889232031*uk_80 + 166375*uk_81 + 647350*uk_82 + 583825*uk_83 + 302500*uk_84 + 196625*uk_85 + 680625*uk_86 + 583825*uk_87 + 2518780*uk_88 + 2271610*uk_89 + 2572416961*uk_9 + 1177000*uk_90 + 765050*uk_91 + 2648250*uk_92 + 2271610*uk_93 + 2048695*uk_94 + 1061500*uk_95 + 689975*uk_96 + 2388375*uk_97 + 2048695*uk_98 + 550000*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 339020*uk_100 + 1138500*uk_101 + 1082840*uk_102 + 246895*uk_103 + 829125*uk_104 + 788590*uk_105 + 2784375*uk_106 + 2648250*uk_107 + 2518780*uk_108 + 8120601*uk_109 + 10194519*uk_11 + 8645814*uk_110 + 3716892*uk_111 + 2706867*uk_112 + 9090225*uk_113 + 8645814*uk_114 + 9204996*uk_115 + 3957288*uk_116 + 2881938*uk_117 + 9678150*uk_118 + 9204996*uk_119 + 10853866*uk_12 + 1701264*uk_120 + 1238964*uk_121 + 4160700*uk_122 + 3957288*uk_123 + 902289*uk_124 + 3030075*uk_125 + 2881938*uk_126 + 10175625*uk_127 + 9678150*uk_128 + 9204996*uk_129 + 4666148*uk_13 + 9800344*uk_130 + 4213232*uk_131 + 3068332*uk_132 + 10304100*uk_133 + 9800344*uk_134 + 1811296*uk_135 + 1319096*uk_136 + 4429800*uk_137 + 4213232*uk_138 + 960646*uk_139 + 3398173*uk_14 + 3226050*uk_140 + 3068332*uk_141 + 10833750*uk_142 + 10304100*uk_143 + 9800344*uk_144 + 778688*uk_145 + 567088*uk_146 + 1904400*uk_147 + 1811296*uk_148 + 412988*uk_149 + 11411775*uk_15 + 1386900*uk_150 + 1319096*uk_151 + 4657500*uk_152 + 4429800*uk_153 + 4213232*uk_154 + 300763*uk_155 + 1010025*uk_156 + 960646*uk_157 + 3391875*uk_158 + 3226050*uk_159 + 10853866*uk_16 + 3068332*uk_160 + 11390625*uk_161 + 10833750*uk_162 + 10304100*uk_163 + 9800344*uk_164 + 3025*uk_17 + 11055*uk_18 + 11770*uk_19 + 55*uk_2 + 5060*uk_20 + 3685*uk_21 + 12375*uk_22 + 11770*uk_23 + 40401*uk_24 + 43014*uk_25 + 18492*uk_26 + 13467*uk_27 + 45225*uk_28 + 43014*uk_29 + 201*uk_3 + 45796*uk_30 + 19688*uk_31 + 14338*uk_32 + 48150*uk_33 + 45796*uk_34 + 8464*uk_35 + 6164*uk_36 + 20700*uk_37 + 19688*uk_38 + 4489*uk_39 + 214*uk_4 + 15075*uk_40 + 14338*uk_41 + 50625*uk_42 + 48150*uk_43 + 45796*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 517055809161*uk_47 + 550497229654*uk_48 + 236662360412*uk_49 + 92*uk_5 + 172351936387*uk_50 + 578793816225*uk_51 + 550497229654*uk_52 + 153424975*uk_53 + 560698545*uk_54 + 596962630*uk_55 + 256638140*uk_56 + 186899515*uk_57 + 627647625*uk_58 + 596962630*uk_59 + 67*uk_6 + 2049098319*uk_60 + 2181627066*uk_61 + 937895748*uk_62 + 683032773*uk_63 + 2293766775*uk_64 + 2181627066*uk_65 + 2322727324*uk_66 + 998555672*uk_67 + 727209022*uk_68 + 2442119850*uk_69 + 225*uk_7 + 2322727324*uk_70 + 429285616*uk_71 + 312631916*uk_72 + 1049883300*uk_73 + 998555672*uk_74 + 227677591*uk_75 + 764588925*uk_76 + 727209022*uk_77 + 2567649375*uk_78 + 2442119850*uk_79 + 214*uk_8 + 2322727324*uk_80 + 166375*uk_81 + 608025*uk_82 + 647350*uk_83 + 278300*uk_84 + 202675*uk_85 + 680625*uk_86 + 647350*uk_87 + 2222055*uk_88 + 2365770*uk_89 + 2572416961*uk_9 + 1017060*uk_90 + 740685*uk_91 + 2487375*uk_92 + 2365770*uk_93 + 2518780*uk_94 + 1082840*uk_95 + 788590*uk_96 + 2648250*uk_97 + 2518780*uk_98 + 465520*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 318780*uk_100 + 1039500*uk_101 + 928620*uk_102 + 261855*uk_103 + 853875*uk_104 + 762795*uk_105 + 2784375*uk_106 + 2487375*uk_107 + 2222055*uk_108 + 2863288*uk_109 + 7202098*uk_11 + 4052964*uk_110 + 1693776*uk_111 + 1391316*uk_112 + 4536900*uk_113 + 4052964*uk_114 + 5736942*uk_115 + 2397528*uk_116 + 1969398*uk_117 + 6421950*uk_118 + 5736942*uk_119 + 10194519*uk_12 + 1001952*uk_120 + 823032*uk_121 + 2683800*uk_122 + 2397528*uk_123 + 676062*uk_124 + 2204550*uk_125 + 1969398*uk_126 + 7188750*uk_127 + 6421950*uk_128 + 5736942*uk_129 + 4260396*uk_13 + 8120601*uk_130 + 3393684*uk_131 + 2787669*uk_132 + 9090225*uk_133 + 8120601*uk_134 + 1418256*uk_135 + 1164996*uk_136 + 3798900*uk_137 + 3393684*uk_138 + 956961*uk_139 + 3499611*uk_14 + 3120525*uk_140 + 2787669*uk_141 + 10175625*uk_142 + 9090225*uk_143 + 8120601*uk_144 + 592704*uk_145 + 486864*uk_146 + 1587600*uk_147 + 1418256*uk_148 + 399924*uk_149 + 11411775*uk_15 + 1304100*uk_150 + 1164996*uk_151 + 4252500*uk_152 + 3798900*uk_153 + 3393684*uk_154 + 328509*uk_155 + 1071225*uk_156 + 956961*uk_157 + 3493125*uk_158 + 3120525*uk_159 + 10194519*uk_16 + 2787669*uk_160 + 11390625*uk_161 + 10175625*uk_162 + 9090225*uk_163 + 8120601*uk_164 + 3025*uk_17 + 7810*uk_18 + 11055*uk_19 + 55*uk_2 + 4620*uk_20 + 3795*uk_21 + 12375*uk_22 + 11055*uk_23 + 20164*uk_24 + 28542*uk_25 + 11928*uk_26 + 9798*uk_27 + 31950*uk_28 + 28542*uk_29 + 142*uk_3 + 40401*uk_30 + 16884*uk_31 + 13869*uk_32 + 45225*uk_33 + 40401*uk_34 + 7056*uk_35 + 5796*uk_36 + 18900*uk_37 + 16884*uk_38 + 4761*uk_39 + 201*uk_4 + 15525*uk_40 + 13869*uk_41 + 50625*uk_42 + 45225*uk_43 + 40401*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 365283208462*uk_47 + 517055809161*uk_48 + 216083024724*uk_49 + 84*uk_5 + 177496770309*uk_50 + 578793816225*uk_51 + 517055809161*uk_52 + 153424975*uk_53 + 396115390*uk_54 + 560698545*uk_55 + 234321780*uk_56 + 192478605*uk_57 + 627647625*uk_58 + 560698545*uk_59 + 69*uk_6 + 1022697916*uk_60 + 1447621698*uk_61 + 604976232*uk_62 + 496944762*uk_63 + 1620472050*uk_64 + 1447621698*uk_65 + 2049098319*uk_66 + 856339596*uk_67 + 703421811*uk_68 + 2293766775*uk_69 + 225*uk_7 + 2049098319*uk_70 + 357873264*uk_71 + 293967324*uk_72 + 958589100*uk_73 + 856339596*uk_74 + 241473159*uk_75 + 787412475*uk_76 + 703421811*uk_77 + 2567649375*uk_78 + 2293766775*uk_79 + 201*uk_8 + 2049098319*uk_80 + 166375*uk_81 + 429550*uk_82 + 608025*uk_83 + 254100*uk_84 + 208725*uk_85 + 680625*uk_86 + 608025*uk_87 + 1109020*uk_88 + 1569810*uk_89 + 2572416961*uk_9 + 656040*uk_90 + 538890*uk_91 + 1757250*uk_92 + 1569810*uk_93 + 2222055*uk_94 + 928620*uk_95 + 762795*uk_96 + 2487375*uk_97 + 2222055*uk_98 + 388080*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 296780*uk_100 + 940500*uk_101 + 593560*uk_102 + 277255*uk_103 + 878625*uk_104 + 554510*uk_105 + 2784375*uk_106 + 1757250*uk_107 + 1109020*uk_108 + 15625*uk_109 + 1267975*uk_11 + 88750*uk_110 + 47500*uk_111 + 44375*uk_112 + 140625*uk_113 + 88750*uk_114 + 504100*uk_115 + 269800*uk_116 + 252050*uk_117 + 798750*uk_118 + 504100*uk_119 + 7202098*uk_12 + 144400*uk_120 + 134900*uk_121 + 427500*uk_122 + 269800*uk_123 + 126025*uk_124 + 399375*uk_125 + 252050*uk_126 + 1265625*uk_127 + 798750*uk_128 + 504100*uk_129 + 3854644*uk_13 + 2863288*uk_130 + 1532464*uk_131 + 1431644*uk_132 + 4536900*uk_133 + 2863288*uk_134 + 820192*uk_135 + 766232*uk_136 + 2428200*uk_137 + 1532464*uk_138 + 715822*uk_139 + 3601049*uk_14 + 2268450*uk_140 + 1431644*uk_141 + 7188750*uk_142 + 4536900*uk_143 + 2863288*uk_144 + 438976*uk_145 + 410096*uk_146 + 1299600*uk_147 + 820192*uk_148 + 383116*uk_149 + 11411775*uk_15 + 1214100*uk_150 + 766232*uk_151 + 3847500*uk_152 + 2428200*uk_153 + 1532464*uk_154 + 357911*uk_155 + 1134225*uk_156 + 715822*uk_157 + 3594375*uk_158 + 2268450*uk_159 + 7202098*uk_16 + 1431644*uk_160 + 11390625*uk_161 + 7188750*uk_162 + 4536900*uk_163 + 2863288*uk_164 + 3025*uk_17 + 1375*uk_18 + 7810*uk_19 + 55*uk_2 + 4180*uk_20 + 3905*uk_21 + 12375*uk_22 + 7810*uk_23 + 625*uk_24 + 3550*uk_25 + 1900*uk_26 + 1775*uk_27 + 5625*uk_28 + 3550*uk_29 + 25*uk_3 + 20164*uk_30 + 10792*uk_31 + 10082*uk_32 + 31950*uk_33 + 20164*uk_34 + 5776*uk_35 + 5396*uk_36 + 17100*uk_37 + 10792*uk_38 + 5041*uk_39 + 142*uk_4 + 15975*uk_40 + 10082*uk_41 + 50625*uk_42 + 31950*uk_43 + 20164*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 64310424025*uk_47 + 365283208462*uk_48 + 195503689036*uk_49 + 76*uk_5 + 182641604231*uk_50 + 578793816225*uk_51 + 365283208462*uk_52 + 153424975*uk_53 + 69738625*uk_54 + 396115390*uk_55 + 212005420*uk_56 + 198057695*uk_57 + 627647625*uk_58 + 396115390*uk_59 + 71*uk_6 + 31699375*uk_60 + 180052450*uk_61 + 96366100*uk_62 + 90026225*uk_63 + 285294375*uk_64 + 180052450*uk_65 + 1022697916*uk_66 + 547359448*uk_67 + 511348958*uk_68 + 1620472050*uk_69 + 225*uk_7 + 1022697916*uk_70 + 292952944*uk_71 + 273679724*uk_72 + 867294900*uk_73 + 547359448*uk_74 + 255674479*uk_75 + 810236025*uk_76 + 511348958*uk_77 + 2567649375*uk_78 + 1620472050*uk_79 + 142*uk_8 + 1022697916*uk_80 + 166375*uk_81 + 75625*uk_82 + 429550*uk_83 + 229900*uk_84 + 214775*uk_85 + 680625*uk_86 + 429550*uk_87 + 34375*uk_88 + 195250*uk_89 + 2572416961*uk_9 + 104500*uk_90 + 97625*uk_91 + 309375*uk_92 + 195250*uk_93 + 1109020*uk_94 + 593560*uk_95 + 554510*uk_96 + 1757250*uk_97 + 1109020*uk_98 + 317680*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 321200*uk_100 + 990000*uk_101 + 110000*uk_102 + 293095*uk_103 + 903375*uk_104 + 100375*uk_105 + 2784375*uk_106 + 309375*uk_107 + 34375*uk_108 + 185193*uk_109 + 2890983*uk_11 + 81225*uk_110 + 259920*uk_111 + 237177*uk_112 + 731025*uk_113 + 81225*uk_114 + 35625*uk_115 + 114000*uk_116 + 104025*uk_117 + 320625*uk_118 + 35625*uk_119 + 1267975*uk_12 + 364800*uk_120 + 332880*uk_121 + 1026000*uk_122 + 114000*uk_123 + 303753*uk_124 + 936225*uk_125 + 104025*uk_126 + 2885625*uk_127 + 320625*uk_128 + 35625*uk_129 + 4057520*uk_13 + 15625*uk_130 + 50000*uk_131 + 45625*uk_132 + 140625*uk_133 + 15625*uk_134 + 160000*uk_135 + 146000*uk_136 + 450000*uk_137 + 50000*uk_138 + 133225*uk_139 + 3702487*uk_14 + 410625*uk_140 + 45625*uk_141 + 1265625*uk_142 + 140625*uk_143 + 15625*uk_144 + 512000*uk_145 + 467200*uk_146 + 1440000*uk_147 + 160000*uk_148 + 426320*uk_149 + 11411775*uk_15 + 1314000*uk_150 + 146000*uk_151 + 4050000*uk_152 + 450000*uk_153 + 50000*uk_154 + 389017*uk_155 + 1199025*uk_156 + 133225*uk_157 + 3695625*uk_158 + 410625*uk_159 + 1267975*uk_16 + 45625*uk_160 + 11390625*uk_161 + 1265625*uk_162 + 140625*uk_163 + 15625*uk_164 + 3025*uk_17 + 3135*uk_18 + 1375*uk_19 + 55*uk_2 + 4400*uk_20 + 4015*uk_21 + 12375*uk_22 + 1375*uk_23 + 3249*uk_24 + 1425*uk_25 + 4560*uk_26 + 4161*uk_27 + 12825*uk_28 + 1425*uk_29 + 57*uk_3 + 625*uk_30 + 2000*uk_31 + 1825*uk_32 + 5625*uk_33 + 625*uk_34 + 6400*uk_35 + 5840*uk_36 + 18000*uk_37 + 2000*uk_38 + 5329*uk_39 + 25*uk_4 + 16425*uk_40 + 1825*uk_41 + 50625*uk_42 + 5625*uk_43 + 625*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 146627766777*uk_47 + 64310424025*uk_48 + 205793356880*uk_49 + 80*uk_5 + 187786438153*uk_50 + 578793816225*uk_51 + 64310424025*uk_52 + 153424975*uk_53 + 159004065*uk_54 + 69738625*uk_55 + 223163600*uk_56 + 203636785*uk_57 + 627647625*uk_58 + 69738625*uk_59 + 73*uk_6 + 164786031*uk_60 + 72274575*uk_61 + 231278640*uk_62 + 211041759*uk_63 + 650471175*uk_64 + 72274575*uk_65 + 31699375*uk_66 + 101438000*uk_67 + 92562175*uk_68 + 285294375*uk_69 + 225*uk_7 + 31699375*uk_70 + 324601600*uk_71 + 296198960*uk_72 + 912942000*uk_73 + 101438000*uk_74 + 270281551*uk_75 + 833059575*uk_76 + 92562175*uk_77 + 2567649375*uk_78 + 285294375*uk_79 + 25*uk_8 + 31699375*uk_80 + 166375*uk_81 + 172425*uk_82 + 75625*uk_83 + 242000*uk_84 + 220825*uk_85 + 680625*uk_86 + 75625*uk_87 + 178695*uk_88 + 78375*uk_89 + 2572416961*uk_9 + 250800*uk_90 + 228855*uk_91 + 705375*uk_92 + 78375*uk_93 + 34375*uk_94 + 110000*uk_95 + 100375*uk_96 + 309375*uk_97 + 34375*uk_98 + 352000*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 297000*uk_100 + 891000*uk_101 + 225720*uk_102 + 309375*uk_103 + 928125*uk_104 + 235125*uk_105 + 2784375*uk_106 + 705375*uk_107 + 178695*uk_108 + 6859*uk_109 + 963661*uk_11 + 20577*uk_110 + 25992*uk_111 + 27075*uk_112 + 81225*uk_113 + 20577*uk_114 + 61731*uk_115 + 77976*uk_116 + 81225*uk_117 + 243675*uk_118 + 61731*uk_119 + 2890983*uk_12 + 98496*uk_120 + 102600*uk_121 + 307800*uk_122 + 77976*uk_123 + 106875*uk_124 + 320625*uk_125 + 81225*uk_126 + 961875*uk_127 + 243675*uk_128 + 61731*uk_129 + 3651768*uk_13 + 185193*uk_130 + 233928*uk_131 + 243675*uk_132 + 731025*uk_133 + 185193*uk_134 + 295488*uk_135 + 307800*uk_136 + 923400*uk_137 + 233928*uk_138 + 320625*uk_139 + 3803925*uk_14 + 961875*uk_140 + 243675*uk_141 + 2885625*uk_142 + 731025*uk_143 + 185193*uk_144 + 373248*uk_145 + 388800*uk_146 + 1166400*uk_147 + 295488*uk_148 + 405000*uk_149 + 11411775*uk_15 + 1215000*uk_150 + 307800*uk_151 + 3645000*uk_152 + 923400*uk_153 + 233928*uk_154 + 421875*uk_155 + 1265625*uk_156 + 320625*uk_157 + 3796875*uk_158 + 961875*uk_159 + 2890983*uk_16 + 243675*uk_160 + 11390625*uk_161 + 2885625*uk_162 + 731025*uk_163 + 185193*uk_164 + 3025*uk_17 + 1045*uk_18 + 3135*uk_19 + 55*uk_2 + 3960*uk_20 + 4125*uk_21 + 12375*uk_22 + 3135*uk_23 + 361*uk_24 + 1083*uk_25 + 1368*uk_26 + 1425*uk_27 + 4275*uk_28 + 1083*uk_29 + 19*uk_3 + 3249*uk_30 + 4104*uk_31 + 4275*uk_32 + 12825*uk_33 + 3249*uk_34 + 5184*uk_35 + 5400*uk_36 + 16200*uk_37 + 4104*uk_38 + 5625*uk_39 + 57*uk_4 + 16875*uk_40 + 4275*uk_41 + 50625*uk_42 + 12825*uk_43 + 3249*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 48875922259*uk_47 + 146627766777*uk_48 + 185214021192*uk_49 + 72*uk_5 + 192931272075*uk_50 + 578793816225*uk_51 + 146627766777*uk_52 + 153424975*uk_53 + 53001355*uk_54 + 159004065*uk_55 + 200847240*uk_56 + 209215875*uk_57 + 627647625*uk_58 + 159004065*uk_59 + 75*uk_6 + 18309559*uk_60 + 54928677*uk_61 + 69383592*uk_62 + 72274575*uk_63 + 216823725*uk_64 + 54928677*uk_65 + 164786031*uk_66 + 208150776*uk_67 + 216823725*uk_68 + 650471175*uk_69 + 225*uk_7 + 164786031*uk_70 + 262927296*uk_71 + 273882600*uk_72 + 821647800*uk_73 + 208150776*uk_74 + 285294375*uk_75 + 855883125*uk_76 + 216823725*uk_77 + 2567649375*uk_78 + 650471175*uk_79 + 57*uk_8 + 164786031*uk_80 + 166375*uk_81 + 57475*uk_82 + 172425*uk_83 + 217800*uk_84 + 226875*uk_85 + 680625*uk_86 + 172425*uk_87 + 19855*uk_88 + 59565*uk_89 + 2572416961*uk_9 + 75240*uk_90 + 78375*uk_91 + 235125*uk_92 + 59565*uk_93 + 178695*uk_94 + 225720*uk_95 + 235125*uk_96 + 705375*uk_97 + 178695*uk_98 + 285120*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 304920*uk_100 + 891000*uk_101 + 75240*uk_102 + 326095*uk_103 + 952875*uk_104 + 80465*uk_105 + 2784375*uk_106 + 235125*uk_107 + 19855*uk_108 + 148877*uk_109 + 2688107*uk_11 + 53371*uk_110 + 202248*uk_111 + 216293*uk_112 + 632025*uk_113 + 53371*uk_114 + 19133*uk_115 + 72504*uk_116 + 77539*uk_117 + 226575*uk_118 + 19133*uk_119 + 963661*uk_12 + 274752*uk_120 + 293832*uk_121 + 858600*uk_122 + 72504*uk_123 + 314237*uk_124 + 918225*uk_125 + 77539*uk_126 + 2683125*uk_127 + 226575*uk_128 + 19133*uk_129 + 3651768*uk_13 + 6859*uk_130 + 25992*uk_131 + 27797*uk_132 + 81225*uk_133 + 6859*uk_134 + 98496*uk_135 + 105336*uk_136 + 307800*uk_137 + 25992*uk_138 + 112651*uk_139 + 3905363*uk_14 + 329175*uk_140 + 27797*uk_141 + 961875*uk_142 + 81225*uk_143 + 6859*uk_144 + 373248*uk_145 + 399168*uk_146 + 1166400*uk_147 + 98496*uk_148 + 426888*uk_149 + 11411775*uk_15 + 1247400*uk_150 + 105336*uk_151 + 3645000*uk_152 + 307800*uk_153 + 25992*uk_154 + 456533*uk_155 + 1334025*uk_156 + 112651*uk_157 + 3898125*uk_158 + 329175*uk_159 + 963661*uk_16 + 27797*uk_160 + 11390625*uk_161 + 961875*uk_162 + 81225*uk_163 + 6859*uk_164 + 3025*uk_17 + 2915*uk_18 + 1045*uk_19 + 55*uk_2 + 3960*uk_20 + 4235*uk_21 + 12375*uk_22 + 1045*uk_23 + 2809*uk_24 + 1007*uk_25 + 3816*uk_26 + 4081*uk_27 + 11925*uk_28 + 1007*uk_29 + 53*uk_3 + 361*uk_30 + 1368*uk_31 + 1463*uk_32 + 4275*uk_33 + 361*uk_34 + 5184*uk_35 + 5544*uk_36 + 16200*uk_37 + 1368*uk_38 + 5929*uk_39 + 19*uk_4 + 17325*uk_40 + 1463*uk_41 + 50625*uk_42 + 4275*uk_43 + 361*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 136338098933*uk_47 + 48875922259*uk_48 + 185214021192*uk_49 + 72*uk_5 + 198076105997*uk_50 + 578793816225*uk_51 + 48875922259*uk_52 + 153424975*uk_53 + 147845885*uk_54 + 53001355*uk_55 + 200847240*uk_56 + 214794965*uk_57 + 627647625*uk_58 + 53001355*uk_59 + 77*uk_6 + 142469671*uk_60 + 51074033*uk_61 + 193543704*uk_62 + 206984239*uk_63 + 604824075*uk_64 + 51074033*uk_65 + 18309559*uk_66 + 69383592*uk_67 + 74201897*uk_68 + 216823725*uk_69 + 225*uk_7 + 18309559*uk_70 + 262927296*uk_71 + 281186136*uk_72 + 821647800*uk_73 + 69383592*uk_74 + 300712951*uk_75 + 878706675*uk_76 + 74201897*uk_77 + 2567649375*uk_78 + 216823725*uk_79 + 19*uk_8 + 18309559*uk_80 + 166375*uk_81 + 160325*uk_82 + 57475*uk_83 + 217800*uk_84 + 232925*uk_85 + 680625*uk_86 + 57475*uk_87 + 154495*uk_88 + 55385*uk_89 + 2572416961*uk_9 + 209880*uk_90 + 224455*uk_91 + 655875*uk_92 + 55385*uk_93 + 19855*uk_94 + 75240*uk_95 + 80465*uk_96 + 235125*uk_97 + 19855*uk_98 + 285120*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 278080*uk_100 + 792000*uk_101 + 186560*uk_102 + 343255*uk_103 + 977625*uk_104 + 230285*uk_105 + 2784375*uk_106 + 655875*uk_107 + 154495*uk_108 + uk_109 + 50719*uk_11 + 53*uk_110 + 64*uk_111 + 79*uk_112 + 225*uk_113 + 53*uk_114 + 2809*uk_115 + 3392*uk_116 + 4187*uk_117 + 11925*uk_118 + 2809*uk_119 + 2688107*uk_12 + 4096*uk_120 + 5056*uk_121 + 14400*uk_122 + 3392*uk_123 + 6241*uk_124 + 17775*uk_125 + 4187*uk_126 + 50625*uk_127 + 11925*uk_128 + 2809*uk_129 + 3246016*uk_13 + 148877*uk_130 + 179776*uk_131 + 221911*uk_132 + 632025*uk_133 + 148877*uk_134 + 217088*uk_135 + 267968*uk_136 + 763200*uk_137 + 179776*uk_138 + 330773*uk_139 + 4006801*uk_14 + 942075*uk_140 + 221911*uk_141 + 2683125*uk_142 + 632025*uk_143 + 148877*uk_144 + 262144*uk_145 + 323584*uk_146 + 921600*uk_147 + 217088*uk_148 + 399424*uk_149 + 11411775*uk_15 + 1137600*uk_150 + 267968*uk_151 + 3240000*uk_152 + 763200*uk_153 + 179776*uk_154 + 493039*uk_155 + 1404225*uk_156 + 330773*uk_157 + 3999375*uk_158 + 942075*uk_159 + 2688107*uk_16 + 221911*uk_160 + 11390625*uk_161 + 2683125*uk_162 + 632025*uk_163 + 148877*uk_164 + 3025*uk_17 + 55*uk_18 + 2915*uk_19 + 55*uk_2 + 3520*uk_20 + 4345*uk_21 + 12375*uk_22 + 2915*uk_23 + uk_24 + 53*uk_25 + 64*uk_26 + 79*uk_27 + 225*uk_28 + 53*uk_29 + uk_3 + 2809*uk_30 + 3392*uk_31 + 4187*uk_32 + 11925*uk_33 + 2809*uk_34 + 4096*uk_35 + 5056*uk_36 + 14400*uk_37 + 3392*uk_38 + 6241*uk_39 + 53*uk_4 + 17775*uk_40 + 4187*uk_41 + 50625*uk_42 + 11925*uk_43 + 2809*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 2572416961*uk_47 + 136338098933*uk_48 + 164634685504*uk_49 + 64*uk_5 + 203220939919*uk_50 + 578793816225*uk_51 + 136338098933*uk_52 + 153424975*uk_53 + 2789545*uk_54 + 147845885*uk_55 + 178530880*uk_56 + 220374055*uk_57 + 627647625*uk_58 + 147845885*uk_59 + 79*uk_6 + 50719*uk_60 + 2688107*uk_61 + 3246016*uk_62 + 4006801*uk_63 + 11411775*uk_64 + 2688107*uk_65 + 142469671*uk_66 + 172038848*uk_67 + 212360453*uk_68 + 604824075*uk_69 + 225*uk_7 + 142469671*uk_70 + 207745024*uk_71 + 256435264*uk_72 + 730353600*uk_73 + 172038848*uk_74 + 316537279*uk_75 + 901530225*uk_76 + 212360453*uk_77 + 2567649375*uk_78 + 604824075*uk_79 + 53*uk_8 + 142469671*uk_80 + 166375*uk_81 + 3025*uk_82 + 160325*uk_83 + 193600*uk_84 + 238975*uk_85 + 680625*uk_86 + 160325*uk_87 + 55*uk_88 + 2915*uk_89 + 2572416961*uk_9 + 3520*uk_90 + 4345*uk_91 + 12375*uk_92 + 2915*uk_93 + 154495*uk_94 + 186560*uk_95 + 230285*uk_96 + 655875*uk_97 + 154495*uk_98 + 225280*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 285120*uk_100 + 792000*uk_101 + 3520*uk_102 + 360855*uk_103 + 1002375*uk_104 + 4455*uk_105 + 2784375*uk_106 + 12375*uk_107 + 55*uk_108 + 2197*uk_109 + 659347*uk_11 + 169*uk_110 + 10816*uk_111 + 13689*uk_112 + 38025*uk_113 + 169*uk_114 + 13*uk_115 + 832*uk_116 + 1053*uk_117 + 2925*uk_118 + 13*uk_119 + 50719*uk_12 + 53248*uk_120 + 67392*uk_121 + 187200*uk_122 + 832*uk_123 + 85293*uk_124 + 236925*uk_125 + 1053*uk_126 + 658125*uk_127 + 2925*uk_128 + 13*uk_129 + 3246016*uk_13 + uk_130 + 64*uk_131 + 81*uk_132 + 225*uk_133 + uk_134 + 4096*uk_135 + 5184*uk_136 + 14400*uk_137 + 64*uk_138 + 6561*uk_139 + 4108239*uk_14 + 18225*uk_140 + 81*uk_141 + 50625*uk_142 + 225*uk_143 + uk_144 + 262144*uk_145 + 331776*uk_146 + 921600*uk_147 + 4096*uk_148 + 419904*uk_149 + 11411775*uk_15 + 1166400*uk_150 + 5184*uk_151 + 3240000*uk_152 + 14400*uk_153 + 64*uk_154 + 531441*uk_155 + 1476225*uk_156 + 6561*uk_157 + 4100625*uk_158 + 18225*uk_159 + 50719*uk_16 + 81*uk_160 + 11390625*uk_161 + 50625*uk_162 + 225*uk_163 + uk_164 + 3025*uk_17 + 715*uk_18 + 55*uk_19 + 55*uk_2 + 3520*uk_20 + 4455*uk_21 + 12375*uk_22 + 55*uk_23 + 169*uk_24 + 13*uk_25 + 832*uk_26 + 1053*uk_27 + 2925*uk_28 + 13*uk_29 + 13*uk_3 + uk_30 + 64*uk_31 + 81*uk_32 + 225*uk_33 + uk_34 + 4096*uk_35 + 5184*uk_36 + 14400*uk_37 + 64*uk_38 + 6561*uk_39 + uk_4 + 18225*uk_40 + 81*uk_41 + 50625*uk_42 + 225*uk_43 + uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 33441420493*uk_47 + 2572416961*uk_48 + 164634685504*uk_49 + 64*uk_5 + 208365773841*uk_50 + 578793816225*uk_51 + 2572416961*uk_52 + 153424975*uk_53 + 36264085*uk_54 + 2789545*uk_55 + 178530880*uk_56 + 225953145*uk_57 + 627647625*uk_58 + 2789545*uk_59 + 81*uk_6 + 8571511*uk_60 + 659347*uk_61 + 42198208*uk_62 + 53407107*uk_63 + 148353075*uk_64 + 659347*uk_65 + 50719*uk_66 + 3246016*uk_67 + 4108239*uk_68 + 11411775*uk_69 + 225*uk_7 + 50719*uk_70 + 207745024*uk_71 + 262927296*uk_72 + 730353600*uk_73 + 3246016*uk_74 + 332767359*uk_75 + 924353775*uk_76 + 4108239*uk_77 + 2567649375*uk_78 + 11411775*uk_79 + uk_8 + 50719*uk_80 + 166375*uk_81 + 39325*uk_82 + 3025*uk_83 + 193600*uk_84 + 245025*uk_85 + 680625*uk_86 + 3025*uk_87 + 9295*uk_88 + 715*uk_89 + 2572416961*uk_9 + 45760*uk_90 + 57915*uk_91 + 160875*uk_92 + 715*uk_93 + 55*uk_94 + 3520*uk_95 + 4455*uk_96 + 12375*uk_97 + 55*uk_98 + 225280*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 273900*uk_100 + 742500*uk_101 + 42900*uk_102 + 378895*uk_103 + 1027125*uk_104 + 59345*uk_105 + 2784375*uk_106 + 160875*uk_107 + 9295*uk_108 + 216*uk_109 + 304314*uk_11 + 468*uk_110 + 2160*uk_111 + 2988*uk_112 + 8100*uk_113 + 468*uk_114 + 1014*uk_115 + 4680*uk_116 + 6474*uk_117 + 17550*uk_118 + 1014*uk_119 + 659347*uk_12 + 21600*uk_120 + 29880*uk_121 + 81000*uk_122 + 4680*uk_123 + 41334*uk_124 + 112050*uk_125 + 6474*uk_126 + 303750*uk_127 + 17550*uk_128 + 1014*uk_129 + 3043140*uk_13 + 2197*uk_130 + 10140*uk_131 + 14027*uk_132 + 38025*uk_133 + 2197*uk_134 + 46800*uk_135 + 64740*uk_136 + 175500*uk_137 + 10140*uk_138 + 89557*uk_139 + 4209677*uk_14 + 242775*uk_140 + 14027*uk_141 + 658125*uk_142 + 38025*uk_143 + 2197*uk_144 + 216000*uk_145 + 298800*uk_146 + 810000*uk_147 + 46800*uk_148 + 413340*uk_149 + 11411775*uk_15 + 1120500*uk_150 + 64740*uk_151 + 3037500*uk_152 + 175500*uk_153 + 10140*uk_154 + 571787*uk_155 + 1550025*uk_156 + 89557*uk_157 + 4201875*uk_158 + 242775*uk_159 + 659347*uk_16 + 14027*uk_160 + 11390625*uk_161 + 658125*uk_162 + 38025*uk_163 + 2197*uk_164 + 3025*uk_17 + 330*uk_18 + 715*uk_19 + 55*uk_2 + 3300*uk_20 + 4565*uk_21 + 12375*uk_22 + 715*uk_23 + 36*uk_24 + 78*uk_25 + 360*uk_26 + 498*uk_27 + 1350*uk_28 + 78*uk_29 + 6*uk_3 + 169*uk_30 + 780*uk_31 + 1079*uk_32 + 2925*uk_33 + 169*uk_34 + 3600*uk_35 + 4980*uk_36 + 13500*uk_37 + 780*uk_38 + 6889*uk_39 + 13*uk_4 + 18675*uk_40 + 1079*uk_41 + 50625*uk_42 + 2925*uk_43 + 169*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 15434501766*uk_47 + 33441420493*uk_48 + 154345017660*uk_49 + 60*uk_5 + 213510607763*uk_50 + 578793816225*uk_51 + 33441420493*uk_52 + 153424975*uk_53 + 16737270*uk_54 + 36264085*uk_55 + 167372700*uk_56 + 231532235*uk_57 + 627647625*uk_58 + 36264085*uk_59 + 83*uk_6 + 1825884*uk_60 + 3956082*uk_61 + 18258840*uk_62 + 25258062*uk_63 + 68470650*uk_64 + 3956082*uk_65 + 8571511*uk_66 + 39560820*uk_67 + 54725801*uk_68 + 148353075*uk_69 + 225*uk_7 + 8571511*uk_70 + 182588400*uk_71 + 252580620*uk_72 + 684706500*uk_73 + 39560820*uk_74 + 349403191*uk_75 + 947177325*uk_76 + 54725801*uk_77 + 2567649375*uk_78 + 148353075*uk_79 + 13*uk_8 + 8571511*uk_80 + 166375*uk_81 + 18150*uk_82 + 39325*uk_83 + 181500*uk_84 + 251075*uk_85 + 680625*uk_86 + 39325*uk_87 + 1980*uk_88 + 4290*uk_89 + 2572416961*uk_9 + 19800*uk_90 + 27390*uk_91 + 74250*uk_92 + 4290*uk_93 + 9295*uk_94 + 42900*uk_95 + 59345*uk_96 + 160875*uk_97 + 9295*uk_98 + 198000*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 280500*uk_100 + 742500*uk_101 + 19800*uk_102 + 397375*uk_103 + 1051875*uk_104 + 28050*uk_105 + 2784375*uk_106 + 74250*uk_107 + 1980*uk_108 + 205379*uk_109 + 2992421*uk_11 + 20886*uk_110 + 208860*uk_111 + 295885*uk_112 + 783225*uk_113 + 20886*uk_114 + 2124*uk_115 + 21240*uk_116 + 30090*uk_117 + 79650*uk_118 + 2124*uk_119 + 304314*uk_12 + 212400*uk_120 + 300900*uk_121 + 796500*uk_122 + 21240*uk_123 + 426275*uk_124 + 1128375*uk_125 + 30090*uk_126 + 2986875*uk_127 + 79650*uk_128 + 2124*uk_129 + 3043140*uk_13 + 216*uk_130 + 2160*uk_131 + 3060*uk_132 + 8100*uk_133 + 216*uk_134 + 21600*uk_135 + 30600*uk_136 + 81000*uk_137 + 2160*uk_138 + 43350*uk_139 + 4311115*uk_14 + 114750*uk_140 + 3060*uk_141 + 303750*uk_142 + 8100*uk_143 + 216*uk_144 + 216000*uk_145 + 306000*uk_146 + 810000*uk_147 + 21600*uk_148 + 433500*uk_149 + 11411775*uk_15 + 1147500*uk_150 + 30600*uk_151 + 3037500*uk_152 + 81000*uk_153 + 2160*uk_154 + 614125*uk_155 + 1625625*uk_156 + 43350*uk_157 + 4303125*uk_158 + 114750*uk_159 + 304314*uk_16 + 3060*uk_160 + 11390625*uk_161 + 303750*uk_162 + 8100*uk_163 + 216*uk_164 + 3025*uk_17 + 3245*uk_18 + 330*uk_19 + 55*uk_2 + 3300*uk_20 + 4675*uk_21 + 12375*uk_22 + 330*uk_23 + 3481*uk_24 + 354*uk_25 + 3540*uk_26 + 5015*uk_27 + 13275*uk_28 + 354*uk_29 + 59*uk_3 + 36*uk_30 + 360*uk_31 + 510*uk_32 + 1350*uk_33 + 36*uk_34 + 3600*uk_35 + 5100*uk_36 + 13500*uk_37 + 360*uk_38 + 7225*uk_39 + 6*uk_4 + 19125*uk_40 + 510*uk_41 + 50625*uk_42 + 1350*uk_43 + 36*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 151772600699*uk_47 + 15434501766*uk_48 + 154345017660*uk_49 + 60*uk_5 + 218655441685*uk_50 + 578793816225*uk_51 + 15434501766*uk_52 + 153424975*uk_53 + 164583155*uk_54 + 16737270*uk_55 + 167372700*uk_56 + 237111325*uk_57 + 627647625*uk_58 + 16737270*uk_59 + 85*uk_6 + 176552839*uk_60 + 17954526*uk_61 + 179545260*uk_62 + 254355785*uk_63 + 673294725*uk_64 + 17954526*uk_65 + 1825884*uk_66 + 18258840*uk_67 + 25866690*uk_68 + 68470650*uk_69 + 225*uk_7 + 1825884*uk_70 + 182588400*uk_71 + 258666900*uk_72 + 684706500*uk_73 + 18258840*uk_74 + 366444775*uk_75 + 970000875*uk_76 + 25866690*uk_77 + 2567649375*uk_78 + 68470650*uk_79 + 6*uk_8 + 1825884*uk_80 + 166375*uk_81 + 178475*uk_82 + 18150*uk_83 + 181500*uk_84 + 257125*uk_85 + 680625*uk_86 + 18150*uk_87 + 191455*uk_88 + 19470*uk_89 + 2572416961*uk_9 + 194700*uk_90 + 275825*uk_91 + 730125*uk_92 + 19470*uk_93 + 1980*uk_94 + 19800*uk_95 + 28050*uk_96 + 74250*uk_97 + 1980*uk_98 + 198000*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 267960*uk_100 + 693000*uk_101 + 181720*uk_102 + 416295*uk_103 + 1076625*uk_104 + 282315*uk_105 + 2784375*uk_106 + 730125*uk_107 + 191455*uk_108 + 614125*uk_109 + 4311115*uk_11 + 426275*uk_110 + 404600*uk_111 + 628575*uk_112 + 1625625*uk_113 + 426275*uk_114 + 295885*uk_115 + 280840*uk_116 + 436305*uk_117 + 1128375*uk_118 + 295885*uk_119 + 2992421*uk_12 + 266560*uk_120 + 414120*uk_121 + 1071000*uk_122 + 280840*uk_123 + 643365*uk_124 + 1663875*uk_125 + 436305*uk_126 + 4303125*uk_127 + 1128375*uk_128 + 295885*uk_129 + 2840264*uk_13 + 205379*uk_130 + 194936*uk_131 + 302847*uk_132 + 783225*uk_133 + 205379*uk_134 + 185024*uk_135 + 287448*uk_136 + 743400*uk_137 + 194936*uk_138 + 446571*uk_139 + 4412553*uk_14 + 1154925*uk_140 + 302847*uk_141 + 2986875*uk_142 + 783225*uk_143 + 205379*uk_144 + 175616*uk_145 + 272832*uk_146 + 705600*uk_147 + 185024*uk_148 + 423864*uk_149 + 11411775*uk_15 + 1096200*uk_150 + 287448*uk_151 + 2835000*uk_152 + 743400*uk_153 + 194936*uk_154 + 658503*uk_155 + 1703025*uk_156 + 446571*uk_157 + 4404375*uk_158 + 1154925*uk_159 + 2992421*uk_16 + 302847*uk_160 + 11390625*uk_161 + 2986875*uk_162 + 783225*uk_163 + 205379*uk_164 + 3025*uk_17 + 4675*uk_18 + 3245*uk_19 + 55*uk_2 + 3080*uk_20 + 4785*uk_21 + 12375*uk_22 + 3245*uk_23 + 7225*uk_24 + 5015*uk_25 + 4760*uk_26 + 7395*uk_27 + 19125*uk_28 + 5015*uk_29 + 85*uk_3 + 3481*uk_30 + 3304*uk_31 + 5133*uk_32 + 13275*uk_33 + 3481*uk_34 + 3136*uk_35 + 4872*uk_36 + 12600*uk_37 + 3304*uk_38 + 7569*uk_39 + 59*uk_4 + 19575*uk_40 + 5133*uk_41 + 50625*uk_42 + 13275*uk_43 + 3481*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 218655441685*uk_47 + 151772600699*uk_48 + 144055349816*uk_49 + 56*uk_5 + 223800275607*uk_50 + 578793816225*uk_51 + 151772600699*uk_52 + 153424975*uk_53 + 237111325*uk_54 + 164583155*uk_55 + 156214520*uk_56 + 242690415*uk_57 + 627647625*uk_58 + 164583155*uk_59 + 87*uk_6 + 366444775*uk_60 + 254355785*uk_61 + 241422440*uk_62 + 375067005*uk_63 + 970000875*uk_64 + 254355785*uk_65 + 176552839*uk_66 + 167575576*uk_67 + 260340627*uk_68 + 673294725*uk_69 + 225*uk_7 + 176552839*uk_70 + 159054784*uk_71 + 247102968*uk_72 + 639059400*uk_73 + 167575576*uk_74 + 383892111*uk_75 + 992824425*uk_76 + 260340627*uk_77 + 2567649375*uk_78 + 673294725*uk_79 + 59*uk_8 + 176552839*uk_80 + 166375*uk_81 + 257125*uk_82 + 178475*uk_83 + 169400*uk_84 + 263175*uk_85 + 680625*uk_86 + 178475*uk_87 + 397375*uk_88 + 275825*uk_89 + 2572416961*uk_9 + 261800*uk_90 + 406725*uk_91 + 1051875*uk_92 + 275825*uk_93 + 191455*uk_94 + 181720*uk_95 + 282315*uk_96 + 730125*uk_97 + 191455*uk_98 + 172480*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 254540*uk_100 + 643500*uk_101 + 243100*uk_102 + 435655*uk_103 + 1101375*uk_104 + 416075*uk_105 + 2784375*uk_106 + 1051875*uk_107 + 397375*uk_108 + 474552*uk_109 + 3956082*uk_11 + 517140*uk_110 + 316368*uk_111 + 541476*uk_112 + 1368900*uk_113 + 517140*uk_114 + 563550*uk_115 + 344760*uk_116 + 590070*uk_117 + 1491750*uk_118 + 563550*uk_119 + 4311115*uk_12 + 210912*uk_120 + 360984*uk_121 + 912600*uk_122 + 344760*uk_123 + 617838*uk_124 + 1561950*uk_125 + 590070*uk_126 + 3948750*uk_127 + 1491750*uk_128 + 563550*uk_129 + 2637388*uk_13 + 614125*uk_130 + 375700*uk_131 + 643025*uk_132 + 1625625*uk_133 + 614125*uk_134 + 229840*uk_135 + 393380*uk_136 + 994500*uk_137 + 375700*uk_138 + 673285*uk_139 + 4513991*uk_14 + 1702125*uk_140 + 643025*uk_141 + 4303125*uk_142 + 1625625*uk_143 + 614125*uk_144 + 140608*uk_145 + 240656*uk_146 + 608400*uk_147 + 229840*uk_148 + 411892*uk_149 + 11411775*uk_15 + 1041300*uk_150 + 393380*uk_151 + 2632500*uk_152 + 994500*uk_153 + 375700*uk_154 + 704969*uk_155 + 1782225*uk_156 + 673285*uk_157 + 4505625*uk_158 + 1702125*uk_159 + 4311115*uk_16 + 643025*uk_160 + 11390625*uk_161 + 4303125*uk_162 + 1625625*uk_163 + 614125*uk_164 + 3025*uk_17 + 4290*uk_18 + 4675*uk_19 + 55*uk_2 + 2860*uk_20 + 4895*uk_21 + 12375*uk_22 + 4675*uk_23 + 6084*uk_24 + 6630*uk_25 + 4056*uk_26 + 6942*uk_27 + 17550*uk_28 + 6630*uk_29 + 78*uk_3 + 7225*uk_30 + 4420*uk_31 + 7565*uk_32 + 19125*uk_33 + 7225*uk_34 + 2704*uk_35 + 4628*uk_36 + 11700*uk_37 + 4420*uk_38 + 7921*uk_39 + 85*uk_4 + 20025*uk_40 + 7565*uk_41 + 50625*uk_42 + 19125*uk_43 + 7225*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 200648522958*uk_47 + 218655441685*uk_48 + 133765681972*uk_49 + 52*uk_5 + 228945109529*uk_50 + 578793816225*uk_51 + 218655441685*uk_52 + 153424975*uk_53 + 217584510*uk_54 + 237111325*uk_55 + 145056340*uk_56 + 248269505*uk_57 + 627647625*uk_58 + 237111325*uk_59 + 89*uk_6 + 308574396*uk_60 + 336266970*uk_61 + 205716264*uk_62 + 352091298*uk_63 + 890118450*uk_64 + 336266970*uk_65 + 366444775*uk_66 + 224177980*uk_67 + 383689235*uk_68 + 970000875*uk_69 + 225*uk_7 + 366444775*uk_70 + 137144176*uk_71 + 234727532*uk_72 + 593412300*uk_73 + 224177980*uk_74 + 401745199*uk_75 + 1015647975*uk_76 + 383689235*uk_77 + 2567649375*uk_78 + 970000875*uk_79 + 85*uk_8 + 366444775*uk_80 + 166375*uk_81 + 235950*uk_82 + 257125*uk_83 + 157300*uk_84 + 269225*uk_85 + 680625*uk_86 + 257125*uk_87 + 334620*uk_88 + 364650*uk_89 + 2572416961*uk_9 + 223080*uk_90 + 381810*uk_91 + 965250*uk_92 + 364650*uk_93 + 397375*uk_94 + 243100*uk_95 + 416075*uk_96 + 1051875*uk_97 + 397375*uk_98 + 148720*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 240240*uk_100 + 594000*uk_101 + 205920*uk_102 + 455455*uk_103 + 1126125*uk_104 + 390390*uk_105 + 2784375*uk_106 + 965250*uk_107 + 334620*uk_108 + 32768*uk_109 + 1623008*uk_11 + 79872*uk_110 + 49152*uk_111 + 93184*uk_112 + 230400*uk_113 + 79872*uk_114 + 194688*uk_115 + 119808*uk_116 + 227136*uk_117 + 561600*uk_118 + 194688*uk_119 + 3956082*uk_12 + 73728*uk_120 + 139776*uk_121 + 345600*uk_122 + 119808*uk_123 + 264992*uk_124 + 655200*uk_125 + 227136*uk_126 + 1620000*uk_127 + 561600*uk_128 + 194688*uk_129 + 2434512*uk_13 + 474552*uk_130 + 292032*uk_131 + 553644*uk_132 + 1368900*uk_133 + 474552*uk_134 + 179712*uk_135 + 340704*uk_136 + 842400*uk_137 + 292032*uk_138 + 645918*uk_139 + 4615429*uk_14 + 1597050*uk_140 + 553644*uk_141 + 3948750*uk_142 + 1368900*uk_143 + 474552*uk_144 + 110592*uk_145 + 209664*uk_146 + 518400*uk_147 + 179712*uk_148 + 397488*uk_149 + 11411775*uk_15 + 982800*uk_150 + 340704*uk_151 + 2430000*uk_152 + 842400*uk_153 + 292032*uk_154 + 753571*uk_155 + 1863225*uk_156 + 645918*uk_157 + 4606875*uk_158 + 1597050*uk_159 + 3956082*uk_16 + 553644*uk_160 + 11390625*uk_161 + 3948750*uk_162 + 1368900*uk_163 + 474552*uk_164 + 3025*uk_17 + 1760*uk_18 + 4290*uk_19 + 55*uk_2 + 2640*uk_20 + 5005*uk_21 + 12375*uk_22 + 4290*uk_23 + 1024*uk_24 + 2496*uk_25 + 1536*uk_26 + 2912*uk_27 + 7200*uk_28 + 2496*uk_29 + 32*uk_3 + 6084*uk_30 + 3744*uk_31 + 7098*uk_32 + 17550*uk_33 + 6084*uk_34 + 2304*uk_35 + 4368*uk_36 + 10800*uk_37 + 3744*uk_38 + 8281*uk_39 + 78*uk_4 + 20475*uk_40 + 7098*uk_41 + 50625*uk_42 + 17550*uk_43 + 6084*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 82317342752*uk_47 + 200648522958*uk_48 + 123476014128*uk_49 + 48*uk_5 + 234089943451*uk_50 + 578793816225*uk_51 + 200648522958*uk_52 + 153424975*uk_53 + 89265440*uk_54 + 217584510*uk_55 + 133898160*uk_56 + 253848595*uk_57 + 627647625*uk_58 + 217584510*uk_59 + 91*uk_6 + 51936256*uk_60 + 126594624*uk_61 + 77904384*uk_62 + 147693728*uk_63 + 365176800*uk_64 + 126594624*uk_65 + 308574396*uk_66 + 189891936*uk_67 + 360003462*uk_68 + 890118450*uk_69 + 225*uk_7 + 308574396*uk_70 + 116856576*uk_71 + 221540592*uk_72 + 547765200*uk_73 + 189891936*uk_74 + 420004039*uk_75 + 1038471525*uk_76 + 360003462*uk_77 + 2567649375*uk_78 + 890118450*uk_79 + 78*uk_8 + 308574396*uk_80 + 166375*uk_81 + 96800*uk_82 + 235950*uk_83 + 145200*uk_84 + 275275*uk_85 + 680625*uk_86 + 235950*uk_87 + 56320*uk_88 + 137280*uk_89 + 2572416961*uk_9 + 84480*uk_90 + 160160*uk_91 + 396000*uk_92 + 137280*uk_93 + 334620*uk_94 + 205920*uk_95 + 390390*uk_96 + 965250*uk_97 + 334620*uk_98 + 126720*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 245520*uk_100 + 594000*uk_101 + 84480*uk_102 + 475695*uk_103 + 1150875*uk_104 + 163680*uk_105 + 2784375*uk_106 + 396000*uk_107 + 56320*uk_108 + 39304*uk_109 + 1724446*uk_11 + 36992*uk_110 + 55488*uk_111 + 107508*uk_112 + 260100*uk_113 + 36992*uk_114 + 34816*uk_115 + 52224*uk_116 + 101184*uk_117 + 244800*uk_118 + 34816*uk_119 + 1623008*uk_12 + 78336*uk_120 + 151776*uk_121 + 367200*uk_122 + 52224*uk_123 + 294066*uk_124 + 711450*uk_125 + 101184*uk_126 + 1721250*uk_127 + 244800*uk_128 + 34816*uk_129 + 2434512*uk_13 + 32768*uk_130 + 49152*uk_131 + 95232*uk_132 + 230400*uk_133 + 32768*uk_134 + 73728*uk_135 + 142848*uk_136 + 345600*uk_137 + 49152*uk_138 + 276768*uk_139 + 4716867*uk_14 + 669600*uk_140 + 95232*uk_141 + 1620000*uk_142 + 230400*uk_143 + 32768*uk_144 + 110592*uk_145 + 214272*uk_146 + 518400*uk_147 + 73728*uk_148 + 415152*uk_149 + 11411775*uk_15 + 1004400*uk_150 + 142848*uk_151 + 2430000*uk_152 + 345600*uk_153 + 49152*uk_154 + 804357*uk_155 + 1946025*uk_156 + 276768*uk_157 + 4708125*uk_158 + 669600*uk_159 + 1623008*uk_16 + 95232*uk_160 + 11390625*uk_161 + 1620000*uk_162 + 230400*uk_163 + 32768*uk_164 + 3025*uk_17 + 1870*uk_18 + 1760*uk_19 + 55*uk_2 + 2640*uk_20 + 5115*uk_21 + 12375*uk_22 + 1760*uk_23 + 1156*uk_24 + 1088*uk_25 + 1632*uk_26 + 3162*uk_27 + 7650*uk_28 + 1088*uk_29 + 34*uk_3 + 1024*uk_30 + 1536*uk_31 + 2976*uk_32 + 7200*uk_33 + 1024*uk_34 + 2304*uk_35 + 4464*uk_36 + 10800*uk_37 + 1536*uk_38 + 8649*uk_39 + 32*uk_4 + 20925*uk_40 + 2976*uk_41 + 50625*uk_42 + 7200*uk_43 + 1024*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 87462176674*uk_47 + 82317342752*uk_48 + 123476014128*uk_49 + 48*uk_5 + 239234777373*uk_50 + 578793816225*uk_51 + 82317342752*uk_52 + 153424975*uk_53 + 94844530*uk_54 + 89265440*uk_55 + 133898160*uk_56 + 259427685*uk_57 + 627647625*uk_58 + 89265440*uk_59 + 93*uk_6 + 58631164*uk_60 + 55182272*uk_61 + 82773408*uk_62 + 160373478*uk_63 + 388000350*uk_64 + 55182272*uk_65 + 51936256*uk_66 + 77904384*uk_67 + 150939744*uk_68 + 365176800*uk_69 + 225*uk_7 + 51936256*uk_70 + 116856576*uk_71 + 226409616*uk_72 + 547765200*uk_73 + 77904384*uk_74 + 438668631*uk_75 + 1061295075*uk_76 + 150939744*uk_77 + 2567649375*uk_78 + 365176800*uk_79 + 32*uk_8 + 51936256*uk_80 + 166375*uk_81 + 102850*uk_82 + 96800*uk_83 + 145200*uk_84 + 281325*uk_85 + 680625*uk_86 + 96800*uk_87 + 63580*uk_88 + 59840*uk_89 + 2572416961*uk_9 + 89760*uk_90 + 173910*uk_91 + 420750*uk_92 + 59840*uk_93 + 56320*uk_94 + 84480*uk_95 + 163680*uk_96 + 396000*uk_97 + 56320*uk_98 + 126720*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 250800*uk_100 + 594000*uk_101 + 89760*uk_102 + 496375*uk_103 + 1175625*uk_104 + 177650*uk_105 + 2784375*uk_106 + 420750*uk_107 + 63580*uk_108 + 592704*uk_109 + 4260396*uk_11 + 239904*uk_110 + 338688*uk_111 + 670320*uk_112 + 1587600*uk_113 + 239904*uk_114 + 97104*uk_115 + 137088*uk_116 + 271320*uk_117 + 642600*uk_118 + 97104*uk_119 + 1724446*uk_12 + 193536*uk_120 + 383040*uk_121 + 907200*uk_122 + 137088*uk_123 + 758100*uk_124 + 1795500*uk_125 + 271320*uk_126 + 4252500*uk_127 + 642600*uk_128 + 97104*uk_129 + 2434512*uk_13 + 39304*uk_130 + 55488*uk_131 + 109820*uk_132 + 260100*uk_133 + 39304*uk_134 + 78336*uk_135 + 155040*uk_136 + 367200*uk_137 + 55488*uk_138 + 306850*uk_139 + 4818305*uk_14 + 726750*uk_140 + 109820*uk_141 + 1721250*uk_142 + 260100*uk_143 + 39304*uk_144 + 110592*uk_145 + 218880*uk_146 + 518400*uk_147 + 78336*uk_148 + 433200*uk_149 + 11411775*uk_15 + 1026000*uk_150 + 155040*uk_151 + 2430000*uk_152 + 367200*uk_153 + 55488*uk_154 + 857375*uk_155 + 2030625*uk_156 + 306850*uk_157 + 4809375*uk_158 + 726750*uk_159 + 1724446*uk_16 + 109820*uk_160 + 11390625*uk_161 + 1721250*uk_162 + 260100*uk_163 + 39304*uk_164 + 3025*uk_17 + 4620*uk_18 + 1870*uk_19 + 55*uk_2 + 2640*uk_20 + 5225*uk_21 + 12375*uk_22 + 1870*uk_23 + 7056*uk_24 + 2856*uk_25 + 4032*uk_26 + 7980*uk_27 + 18900*uk_28 + 2856*uk_29 + 84*uk_3 + 1156*uk_30 + 1632*uk_31 + 3230*uk_32 + 7650*uk_33 + 1156*uk_34 + 2304*uk_35 + 4560*uk_36 + 10800*uk_37 + 1632*uk_38 + 9025*uk_39 + 34*uk_4 + 21375*uk_40 + 3230*uk_41 + 50625*uk_42 + 7650*uk_43 + 1156*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 216083024724*uk_47 + 87462176674*uk_48 + 123476014128*uk_49 + 48*uk_5 + 244379611295*uk_50 + 578793816225*uk_51 + 87462176674*uk_52 + 153424975*uk_53 + 234321780*uk_54 + 94844530*uk_55 + 133898160*uk_56 + 265006775*uk_57 + 627647625*uk_58 + 94844530*uk_59 + 95*uk_6 + 357873264*uk_60 + 144853464*uk_61 + 204499008*uk_62 + 404737620*uk_63 + 958589100*uk_64 + 144853464*uk_65 + 58631164*uk_66 + 82773408*uk_67 + 163822370*uk_68 + 388000350*uk_69 + 225*uk_7 + 58631164*uk_70 + 116856576*uk_71 + 231278640*uk_72 + 547765200*uk_73 + 82773408*uk_74 + 457738975*uk_75 + 1084118625*uk_76 + 163822370*uk_77 + 2567649375*uk_78 + 388000350*uk_79 + 34*uk_8 + 58631164*uk_80 + 166375*uk_81 + 254100*uk_82 + 102850*uk_83 + 145200*uk_84 + 287375*uk_85 + 680625*uk_86 + 102850*uk_87 + 388080*uk_88 + 157080*uk_89 + 2572416961*uk_9 + 221760*uk_90 + 438900*uk_91 + 1039500*uk_92 + 157080*uk_93 + 63580*uk_94 + 89760*uk_95 + 177650*uk_96 + 420750*uk_97 + 63580*uk_98 + 126720*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 234740*uk_100 + 544500*uk_101 + 203280*uk_102 + 517495*uk_103 + 1200375*uk_104 + 448140*uk_105 + 2784375*uk_106 + 1039500*uk_107 + 388080*uk_108 + 614125*uk_109 + 4311115*uk_11 + 606900*uk_110 + 317900*uk_111 + 700825*uk_112 + 1625625*uk_113 + 606900*uk_114 + 599760*uk_115 + 314160*uk_116 + 692580*uk_117 + 1606500*uk_118 + 599760*uk_119 + 4260396*uk_12 + 164560*uk_120 + 362780*uk_121 + 841500*uk_122 + 314160*uk_123 + 799765*uk_124 + 1855125*uk_125 + 692580*uk_126 + 4303125*uk_127 + 1606500*uk_128 + 599760*uk_129 + 2231636*uk_13 + 592704*uk_130 + 310464*uk_131 + 684432*uk_132 + 1587600*uk_133 + 592704*uk_134 + 162624*uk_135 + 358512*uk_136 + 831600*uk_137 + 310464*uk_138 + 790356*uk_139 + 4919743*uk_14 + 1833300*uk_140 + 684432*uk_141 + 4252500*uk_142 + 1587600*uk_143 + 592704*uk_144 + 85184*uk_145 + 187792*uk_146 + 435600*uk_147 + 162624*uk_148 + 413996*uk_149 + 11411775*uk_15 + 960300*uk_150 + 358512*uk_151 + 2227500*uk_152 + 831600*uk_153 + 310464*uk_154 + 912673*uk_155 + 2117025*uk_156 + 790356*uk_157 + 4910625*uk_158 + 1833300*uk_159 + 4260396*uk_16 + 684432*uk_160 + 11390625*uk_161 + 4252500*uk_162 + 1587600*uk_163 + 592704*uk_164 + 3025*uk_17 + 4675*uk_18 + 4620*uk_19 + 55*uk_2 + 2420*uk_20 + 5335*uk_21 + 12375*uk_22 + 4620*uk_23 + 7225*uk_24 + 7140*uk_25 + 3740*uk_26 + 8245*uk_27 + 19125*uk_28 + 7140*uk_29 + 85*uk_3 + 7056*uk_30 + 3696*uk_31 + 8148*uk_32 + 18900*uk_33 + 7056*uk_34 + 1936*uk_35 + 4268*uk_36 + 9900*uk_37 + 3696*uk_38 + 9409*uk_39 + 84*uk_4 + 21825*uk_40 + 8148*uk_41 + 50625*uk_42 + 18900*uk_43 + 7056*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 218655441685*uk_47 + 216083024724*uk_48 + 113186346284*uk_49 + 44*uk_5 + 249524445217*uk_50 + 578793816225*uk_51 + 216083024724*uk_52 + 153424975*uk_53 + 237111325*uk_54 + 234321780*uk_55 + 122739980*uk_56 + 270585865*uk_57 + 627647625*uk_58 + 234321780*uk_59 + 97*uk_6 + 366444775*uk_60 + 362133660*uk_61 + 189689060*uk_62 + 418178155*uk_63 + 970000875*uk_64 + 362133660*uk_65 + 357873264*uk_66 + 187457424*uk_67 + 413258412*uk_68 + 958589100*uk_69 + 225*uk_7 + 357873264*uk_70 + 98191984*uk_71 + 216468692*uk_72 + 502118100*uk_73 + 187457424*uk_74 + 477215071*uk_75 + 1106942175*uk_76 + 413258412*uk_77 + 2567649375*uk_78 + 958589100*uk_79 + 84*uk_8 + 357873264*uk_80 + 166375*uk_81 + 257125*uk_82 + 254100*uk_83 + 133100*uk_84 + 293425*uk_85 + 680625*uk_86 + 254100*uk_87 + 397375*uk_88 + 392700*uk_89 + 2572416961*uk_9 + 205700*uk_90 + 453475*uk_91 + 1051875*uk_92 + 392700*uk_93 + 388080*uk_94 + 203280*uk_95 + 448140*uk_96 + 1039500*uk_97 + 388080*uk_98 + 106480*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 217800*uk_100 + 495000*uk_101 + 187000*uk_102 + 539055*uk_103 + 1225125*uk_104 + 462825*uk_105 + 2784375*uk_106 + 1051875*uk_107 + 397375*uk_108 + 29791*uk_109 + 1572289*uk_11 + 81685*uk_110 + 38440*uk_111 + 95139*uk_112 + 216225*uk_113 + 81685*uk_114 + 223975*uk_115 + 105400*uk_116 + 260865*uk_117 + 592875*uk_118 + 223975*uk_119 + 4311115*uk_12 + 49600*uk_120 + 122760*uk_121 + 279000*uk_122 + 105400*uk_123 + 303831*uk_124 + 690525*uk_125 + 260865*uk_126 + 1569375*uk_127 + 592875*uk_128 + 223975*uk_129 + 2028760*uk_13 + 614125*uk_130 + 289000*uk_131 + 715275*uk_132 + 1625625*uk_133 + 614125*uk_134 + 136000*uk_135 + 336600*uk_136 + 765000*uk_137 + 289000*uk_138 + 833085*uk_139 + 5021181*uk_14 + 1893375*uk_140 + 715275*uk_141 + 4303125*uk_142 + 1625625*uk_143 + 614125*uk_144 + 64000*uk_145 + 158400*uk_146 + 360000*uk_147 + 136000*uk_148 + 392040*uk_149 + 11411775*uk_15 + 891000*uk_150 + 336600*uk_151 + 2025000*uk_152 + 765000*uk_153 + 289000*uk_154 + 970299*uk_155 + 2205225*uk_156 + 833085*uk_157 + 5011875*uk_158 + 1893375*uk_159 + 4311115*uk_16 + 715275*uk_160 + 11390625*uk_161 + 4303125*uk_162 + 1625625*uk_163 + 614125*uk_164 + 3025*uk_17 + 1705*uk_18 + 4675*uk_19 + 55*uk_2 + 2200*uk_20 + 5445*uk_21 + 12375*uk_22 + 4675*uk_23 + 961*uk_24 + 2635*uk_25 + 1240*uk_26 + 3069*uk_27 + 6975*uk_28 + 2635*uk_29 + 31*uk_3 + 7225*uk_30 + 3400*uk_31 + 8415*uk_32 + 19125*uk_33 + 7225*uk_34 + 1600*uk_35 + 3960*uk_36 + 9000*uk_37 + 3400*uk_38 + 9801*uk_39 + 85*uk_4 + 22275*uk_40 + 8415*uk_41 + 50625*uk_42 + 19125*uk_43 + 7225*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 79744925791*uk_47 + 218655441685*uk_48 + 102896678440*uk_49 + 40*uk_5 + 254669279139*uk_50 + 578793816225*uk_51 + 218655441685*uk_52 + 153424975*uk_53 + 86475895*uk_54 + 237111325*uk_55 + 111581800*uk_56 + 276164955*uk_57 + 627647625*uk_58 + 237111325*uk_59 + 99*uk_6 + 48740959*uk_60 + 133644565*uk_61 + 62891560*uk_62 + 155656611*uk_63 + 353765025*uk_64 + 133644565*uk_65 + 366444775*uk_66 + 172444600*uk_67 + 426800385*uk_68 + 970000875*uk_69 + 225*uk_7 + 366444775*uk_70 + 81150400*uk_71 + 200847240*uk_72 + 456471000*uk_73 + 172444600*uk_74 + 497096919*uk_75 + 1129765725*uk_76 + 426800385*uk_77 + 2567649375*uk_78 + 970000875*uk_79 + 85*uk_8 + 366444775*uk_80 + 166375*uk_81 + 93775*uk_82 + 257125*uk_83 + 121000*uk_84 + 299475*uk_85 + 680625*uk_86 + 257125*uk_87 + 52855*uk_88 + 144925*uk_89 + 2572416961*uk_9 + 68200*uk_90 + 168795*uk_91 + 383625*uk_92 + 144925*uk_93 + 397375*uk_94 + 187000*uk_95 + 462825*uk_96 + 1051875*uk_97 + 397375*uk_98 + 88000*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 222200*uk_100 + 495000*uk_101 + 68200*uk_102 + 561055*uk_103 + 1249875*uk_104 + 172205*uk_105 + 2784375*uk_106 + 383625*uk_107 + 52855*uk_108 + 4913*uk_109 + 862223*uk_11 + 8959*uk_110 + 11560*uk_111 + 29189*uk_112 + 65025*uk_113 + 8959*uk_114 + 16337*uk_115 + 21080*uk_116 + 53227*uk_117 + 118575*uk_118 + 16337*uk_119 + 1572289*uk_12 + 27200*uk_120 + 68680*uk_121 + 153000*uk_122 + 21080*uk_123 + 173417*uk_124 + 386325*uk_125 + 53227*uk_126 + 860625*uk_127 + 118575*uk_128 + 16337*uk_129 + 2028760*uk_13 + 29791*uk_130 + 38440*uk_131 + 97061*uk_132 + 216225*uk_133 + 29791*uk_134 + 49600*uk_135 + 125240*uk_136 + 279000*uk_137 + 38440*uk_138 + 316231*uk_139 + 5122619*uk_14 + 704475*uk_140 + 97061*uk_141 + 1569375*uk_142 + 216225*uk_143 + 29791*uk_144 + 64000*uk_145 + 161600*uk_146 + 360000*uk_147 + 49600*uk_148 + 408040*uk_149 + 11411775*uk_15 + 909000*uk_150 + 125240*uk_151 + 2025000*uk_152 + 279000*uk_153 + 38440*uk_154 + 1030301*uk_155 + 2295225*uk_156 + 316231*uk_157 + 5113125*uk_158 + 704475*uk_159 + 1572289*uk_16 + 97061*uk_160 + 11390625*uk_161 + 1569375*uk_162 + 216225*uk_163 + 29791*uk_164 + 3025*uk_17 + 935*uk_18 + 1705*uk_19 + 55*uk_2 + 2200*uk_20 + 5555*uk_21 + 12375*uk_22 + 1705*uk_23 + 289*uk_24 + 527*uk_25 + 680*uk_26 + 1717*uk_27 + 3825*uk_28 + 527*uk_29 + 17*uk_3 + 961*uk_30 + 1240*uk_31 + 3131*uk_32 + 6975*uk_33 + 961*uk_34 + 1600*uk_35 + 4040*uk_36 + 9000*uk_37 + 1240*uk_38 + 10201*uk_39 + 31*uk_4 + 22725*uk_40 + 3131*uk_41 + 50625*uk_42 + 6975*uk_43 + 961*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 43731088337*uk_47 + 79744925791*uk_48 + 102896678440*uk_49 + 40*uk_5 + 259814113061*uk_50 + 578793816225*uk_51 + 79744925791*uk_52 + 153424975*uk_53 + 47422265*uk_54 + 86475895*uk_55 + 111581800*uk_56 + 281744045*uk_57 + 627647625*uk_58 + 86475895*uk_59 + 101*uk_6 + 14657791*uk_60 + 26728913*uk_61 + 34488920*uk_62 + 87084523*uk_63 + 194000175*uk_64 + 26728913*uk_65 + 48740959*uk_66 + 62891560*uk_67 + 158801189*uk_68 + 353765025*uk_69 + 225*uk_7 + 48740959*uk_70 + 81150400*uk_71 + 204904760*uk_72 + 456471000*uk_73 + 62891560*uk_74 + 517384519*uk_75 + 1152589275*uk_76 + 158801189*uk_77 + 2567649375*uk_78 + 353765025*uk_79 + 31*uk_8 + 48740959*uk_80 + 166375*uk_81 + 51425*uk_82 + 93775*uk_83 + 121000*uk_84 + 305525*uk_85 + 680625*uk_86 + 93775*uk_87 + 15895*uk_88 + 28985*uk_89 + 2572416961*uk_9 + 37400*uk_90 + 94435*uk_91 + 210375*uk_92 + 28985*uk_93 + 52855*uk_94 + 68200*uk_95 + 172205*uk_96 + 383625*uk_97 + 52855*uk_98 + 88000*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 226600*uk_100 + 495000*uk_101 + 37400*uk_102 + 583495*uk_103 + 1274625*uk_104 + 96305*uk_105 + 2784375*uk_106 + 210375*uk_107 + 15895*uk_108 + 79507*uk_109 + 2180917*uk_11 + 31433*uk_110 + 73960*uk_111 + 190447*uk_112 + 416025*uk_113 + 31433*uk_114 + 12427*uk_115 + 29240*uk_116 + 75293*uk_117 + 164475*uk_118 + 12427*uk_119 + 862223*uk_12 + 68800*uk_120 + 177160*uk_121 + 387000*uk_122 + 29240*uk_123 + 456187*uk_124 + 996525*uk_125 + 75293*uk_126 + 2176875*uk_127 + 164475*uk_128 + 12427*uk_129 + 2028760*uk_13 + 4913*uk_130 + 11560*uk_131 + 29767*uk_132 + 65025*uk_133 + 4913*uk_134 + 27200*uk_135 + 70040*uk_136 + 153000*uk_137 + 11560*uk_138 + 180353*uk_139 + 5224057*uk_14 + 393975*uk_140 + 29767*uk_141 + 860625*uk_142 + 65025*uk_143 + 4913*uk_144 + 64000*uk_145 + 164800*uk_146 + 360000*uk_147 + 27200*uk_148 + 424360*uk_149 + 11411775*uk_15 + 927000*uk_150 + 70040*uk_151 + 2025000*uk_152 + 153000*uk_153 + 11560*uk_154 + 1092727*uk_155 + 2387025*uk_156 + 180353*uk_157 + 5214375*uk_158 + 393975*uk_159 + 862223*uk_16 + 29767*uk_160 + 11390625*uk_161 + 860625*uk_162 + 65025*uk_163 + 4913*uk_164 + 3025*uk_17 + 2365*uk_18 + 935*uk_19 + 55*uk_2 + 2200*uk_20 + 5665*uk_21 + 12375*uk_22 + 935*uk_23 + 1849*uk_24 + 731*uk_25 + 1720*uk_26 + 4429*uk_27 + 9675*uk_28 + 731*uk_29 + 43*uk_3 + 289*uk_30 + 680*uk_31 + 1751*uk_32 + 3825*uk_33 + 289*uk_34 + 1600*uk_35 + 4120*uk_36 + 9000*uk_37 + 680*uk_38 + 10609*uk_39 + 17*uk_4 + 23175*uk_40 + 1751*uk_41 + 50625*uk_42 + 3825*uk_43 + 289*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 110613929323*uk_47 + 43731088337*uk_48 + 102896678440*uk_49 + 40*uk_5 + 264958946983*uk_50 + 578793816225*uk_51 + 43731088337*uk_52 + 153424975*uk_53 + 119950435*uk_54 + 47422265*uk_55 + 111581800*uk_56 + 287323135*uk_57 + 627647625*uk_58 + 47422265*uk_59 + 103*uk_6 + 93779431*uk_60 + 37075589*uk_61 + 87236680*uk_62 + 224634451*uk_63 + 490706325*uk_64 + 37075589*uk_65 + 14657791*uk_66 + 34488920*uk_67 + 88808969*uk_68 + 194000175*uk_69 + 225*uk_7 + 14657791*uk_70 + 81150400*uk_71 + 208962280*uk_72 + 456471000*uk_73 + 34488920*uk_74 + 538077871*uk_75 + 1175412825*uk_76 + 88808969*uk_77 + 2567649375*uk_78 + 194000175*uk_79 + 17*uk_8 + 14657791*uk_80 + 166375*uk_81 + 130075*uk_82 + 51425*uk_83 + 121000*uk_84 + 311575*uk_85 + 680625*uk_86 + 51425*uk_87 + 101695*uk_88 + 40205*uk_89 + 2572416961*uk_9 + 94600*uk_90 + 243595*uk_91 + 532125*uk_92 + 40205*uk_93 + 15895*uk_94 + 37400*uk_95 + 96305*uk_96 + 210375*uk_97 + 15895*uk_98 + 88000*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 207900*uk_100 + 445500*uk_101 + 85140*uk_102 + 606375*uk_103 + 1299375*uk_104 + 248325*uk_105 + 2784375*uk_106 + 532125*uk_107 + 101695*uk_108 + 64*uk_109 + 202876*uk_11 + 688*uk_110 + 576*uk_111 + 1680*uk_112 + 3600*uk_113 + 688*uk_114 + 7396*uk_115 + 6192*uk_116 + 18060*uk_117 + 38700*uk_118 + 7396*uk_119 + 2180917*uk_12 + 5184*uk_120 + 15120*uk_121 + 32400*uk_122 + 6192*uk_123 + 44100*uk_124 + 94500*uk_125 + 18060*uk_126 + 202500*uk_127 + 38700*uk_128 + 7396*uk_129 + 1825884*uk_13 + 79507*uk_130 + 66564*uk_131 + 194145*uk_132 + 416025*uk_133 + 79507*uk_134 + 55728*uk_135 + 162540*uk_136 + 348300*uk_137 + 66564*uk_138 + 474075*uk_139 + 5325495*uk_14 + 1015875*uk_140 + 194145*uk_141 + 2176875*uk_142 + 416025*uk_143 + 79507*uk_144 + 46656*uk_145 + 136080*uk_146 + 291600*uk_147 + 55728*uk_148 + 396900*uk_149 + 11411775*uk_15 + 850500*uk_150 + 162540*uk_151 + 1822500*uk_152 + 348300*uk_153 + 66564*uk_154 + 1157625*uk_155 + 2480625*uk_156 + 474075*uk_157 + 5315625*uk_158 + 1015875*uk_159 + 2180917*uk_16 + 194145*uk_160 + 11390625*uk_161 + 2176875*uk_162 + 416025*uk_163 + 79507*uk_164 + 3025*uk_17 + 220*uk_18 + 2365*uk_19 + 55*uk_2 + 1980*uk_20 + 5775*uk_21 + 12375*uk_22 + 2365*uk_23 + 16*uk_24 + 172*uk_25 + 144*uk_26 + 420*uk_27 + 900*uk_28 + 172*uk_29 + 4*uk_3 + 1849*uk_30 + 1548*uk_31 + 4515*uk_32 + 9675*uk_33 + 1849*uk_34 + 1296*uk_35 + 3780*uk_36 + 8100*uk_37 + 1548*uk_38 + 11025*uk_39 + 43*uk_4 + 23625*uk_40 + 4515*uk_41 + 50625*uk_42 + 9675*uk_43 + 1849*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 10289667844*uk_47 + 110613929323*uk_48 + 92607010596*uk_49 + 36*uk_5 + 270103780905*uk_50 + 578793816225*uk_51 + 110613929323*uk_52 + 153424975*uk_53 + 11158180*uk_54 + 119950435*uk_55 + 100423620*uk_56 + 292902225*uk_57 + 627647625*uk_58 + 119950435*uk_59 + 105*uk_6 + 811504*uk_60 + 8723668*uk_61 + 7303536*uk_62 + 21301980*uk_63 + 45647100*uk_64 + 8723668*uk_65 + 93779431*uk_66 + 78513012*uk_67 + 228996285*uk_68 + 490706325*uk_69 + 225*uk_7 + 93779431*uk_70 + 65731824*uk_71 + 191717820*uk_72 + 410823900*uk_73 + 78513012*uk_74 + 559176975*uk_75 + 1198236375*uk_76 + 228996285*uk_77 + 2567649375*uk_78 + 490706325*uk_79 + 43*uk_8 + 93779431*uk_80 + 166375*uk_81 + 12100*uk_82 + 130075*uk_83 + 108900*uk_84 + 317625*uk_85 + 680625*uk_86 + 130075*uk_87 + 880*uk_88 + 9460*uk_89 + 2572416961*uk_9 + 7920*uk_90 + 23100*uk_91 + 49500*uk_92 + 9460*uk_93 + 101695*uk_94 + 85140*uk_95 + 248325*uk_96 + 532125*uk_97 + 101695*uk_98 + 71280*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 211860*uk_100 + 445500*uk_101 + 7920*uk_102 + 629695*uk_103 + 1324125*uk_104 + 23540*uk_105 + 2784375*uk_106 + 49500*uk_107 + 880*uk_108 + uk_109 + 50719*uk_11 + 4*uk_110 + 36*uk_111 + 107*uk_112 + 225*uk_113 + 4*uk_114 + 16*uk_115 + 144*uk_116 + 428*uk_117 + 900*uk_118 + 16*uk_119 + 202876*uk_12 + 1296*uk_120 + 3852*uk_121 + 8100*uk_122 + 144*uk_123 + 11449*uk_124 + 24075*uk_125 + 428*uk_126 + 50625*uk_127 + 900*uk_128 + 16*uk_129 + 1825884*uk_13 + 64*uk_130 + 576*uk_131 + 1712*uk_132 + 3600*uk_133 + 64*uk_134 + 5184*uk_135 + 15408*uk_136 + 32400*uk_137 + 576*uk_138 + 45796*uk_139 + 5426933*uk_14 + 96300*uk_140 + 1712*uk_141 + 202500*uk_142 + 3600*uk_143 + 64*uk_144 + 46656*uk_145 + 138672*uk_146 + 291600*uk_147 + 5184*uk_148 + 412164*uk_149 + 11411775*uk_15 + 866700*uk_150 + 15408*uk_151 + 1822500*uk_152 + 32400*uk_153 + 576*uk_154 + 1225043*uk_155 + 2576025*uk_156 + 45796*uk_157 + 5416875*uk_158 + 96300*uk_159 + 202876*uk_16 + 1712*uk_160 + 11390625*uk_161 + 202500*uk_162 + 3600*uk_163 + 64*uk_164 + 3025*uk_17 + 55*uk_18 + 220*uk_19 + 55*uk_2 + 1980*uk_20 + 5885*uk_21 + 12375*uk_22 + 220*uk_23 + uk_24 + 4*uk_25 + 36*uk_26 + 107*uk_27 + 225*uk_28 + 4*uk_29 + uk_3 + 16*uk_30 + 144*uk_31 + 428*uk_32 + 900*uk_33 + 16*uk_34 + 1296*uk_35 + 3852*uk_36 + 8100*uk_37 + 144*uk_38 + 11449*uk_39 + 4*uk_4 + 24075*uk_40 + 428*uk_41 + 50625*uk_42 + 900*uk_43 + 16*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 2572416961*uk_47 + 10289667844*uk_48 + 92607010596*uk_49 + 36*uk_5 + 275248614827*uk_50 + 578793816225*uk_51 + 10289667844*uk_52 + 153424975*uk_53 + 2789545*uk_54 + 11158180*uk_55 + 100423620*uk_56 + 298481315*uk_57 + 627647625*uk_58 + 11158180*uk_59 + 107*uk_6 + 50719*uk_60 + 202876*uk_61 + 1825884*uk_62 + 5426933*uk_63 + 11411775*uk_64 + 202876*uk_65 + 811504*uk_66 + 7303536*uk_67 + 21707732*uk_68 + 45647100*uk_69 + 225*uk_7 + 811504*uk_70 + 65731824*uk_71 + 195369588*uk_72 + 410823900*uk_73 + 7303536*uk_74 + 580681831*uk_75 + 1221059925*uk_76 + 21707732*uk_77 + 2567649375*uk_78 + 45647100*uk_79 + 4*uk_8 + 811504*uk_80 + 166375*uk_81 + 3025*uk_82 + 12100*uk_83 + 108900*uk_84 + 323675*uk_85 + 680625*uk_86 + 12100*uk_87 + 55*uk_88 + 220*uk_89 + 2572416961*uk_9 + 1980*uk_90 + 5885*uk_91 + 12375*uk_92 + 220*uk_93 + 880*uk_94 + 7920*uk_95 + 23540*uk_96 + 49500*uk_97 + 880*uk_98 + 71280*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 215820*uk_100 + 445500*uk_101 + 1980*uk_102 + 653455*uk_103 + 1348875*uk_104 + 5995*uk_105 + 2784375*uk_106 + 12375*uk_107 + 55*uk_108 + 39304*uk_109 + 1724446*uk_11 + 1156*uk_110 + 41616*uk_111 + 126004*uk_112 + 260100*uk_113 + 1156*uk_114 + 34*uk_115 + 1224*uk_116 + 3706*uk_117 + 7650*uk_118 + 34*uk_119 + 50719*uk_12 + 44064*uk_120 + 133416*uk_121 + 275400*uk_122 + 1224*uk_123 + 403954*uk_124 + 833850*uk_125 + 3706*uk_126 + 1721250*uk_127 + 7650*uk_128 + 34*uk_129 + 1825884*uk_13 + uk_130 + 36*uk_131 + 109*uk_132 + 225*uk_133 + uk_134 + 1296*uk_135 + 3924*uk_136 + 8100*uk_137 + 36*uk_138 + 11881*uk_139 + 5528371*uk_14 + 24525*uk_140 + 109*uk_141 + 50625*uk_142 + 225*uk_143 + uk_144 + 46656*uk_145 + 141264*uk_146 + 291600*uk_147 + 1296*uk_148 + 427716*uk_149 + 11411775*uk_15 + 882900*uk_150 + 3924*uk_151 + 1822500*uk_152 + 8100*uk_153 + 36*uk_154 + 1295029*uk_155 + 2673225*uk_156 + 11881*uk_157 + 5518125*uk_158 + 24525*uk_159 + 50719*uk_16 + 109*uk_160 + 11390625*uk_161 + 50625*uk_162 + 225*uk_163 + uk_164 + 3025*uk_17 + 1870*uk_18 + 55*uk_19 + 55*uk_2 + 1980*uk_20 + 5995*uk_21 + 12375*uk_22 + 55*uk_23 + 1156*uk_24 + 34*uk_25 + 1224*uk_26 + 3706*uk_27 + 7650*uk_28 + 34*uk_29 + 34*uk_3 + uk_30 + 36*uk_31 + 109*uk_32 + 225*uk_33 + uk_34 + 1296*uk_35 + 3924*uk_36 + 8100*uk_37 + 36*uk_38 + 11881*uk_39 + uk_4 + 24525*uk_40 + 109*uk_41 + 50625*uk_42 + 225*uk_43 + uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 87462176674*uk_47 + 2572416961*uk_48 + 92607010596*uk_49 + 36*uk_5 + 280393448749*uk_50 + 578793816225*uk_51 + 2572416961*uk_52 + 153424975*uk_53 + 94844530*uk_54 + 2789545*uk_55 + 100423620*uk_56 + 304060405*uk_57 + 627647625*uk_58 + 2789545*uk_59 + 109*uk_6 + 58631164*uk_60 + 1724446*uk_61 + 62080056*uk_62 + 187964614*uk_63 + 388000350*uk_64 + 1724446*uk_65 + 50719*uk_66 + 1825884*uk_67 + 5528371*uk_68 + 11411775*uk_69 + 225*uk_7 + 50719*uk_70 + 65731824*uk_71 + 199021356*uk_72 + 410823900*uk_73 + 1825884*uk_74 + 602592439*uk_75 + 1243883475*uk_76 + 5528371*uk_77 + 2567649375*uk_78 + 11411775*uk_79 + uk_8 + 50719*uk_80 + 166375*uk_81 + 102850*uk_82 + 3025*uk_83 + 108900*uk_84 + 329725*uk_85 + 680625*uk_86 + 3025*uk_87 + 63580*uk_88 + 1870*uk_89 + 2572416961*uk_9 + 67320*uk_90 + 203830*uk_91 + 420750*uk_92 + 1870*uk_93 + 55*uk_94 + 1980*uk_95 + 5995*uk_96 + 12375*uk_97 + 55*uk_98 + 71280*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 219780*uk_100 + 445500*uk_101 + 67320*uk_102 + 677655*uk_103 + 1373625*uk_104 + 207570*uk_105 + 2784375*uk_106 + 420750*uk_107 + 63580*uk_108 + 1092727*uk_109 + 5224057*uk_11 + 360706*uk_110 + 381924*uk_111 + 1177599*uk_112 + 2387025*uk_113 + 360706*uk_114 + 119068*uk_115 + 126072*uk_116 + 388722*uk_117 + 787950*uk_118 + 119068*uk_119 + 1724446*uk_12 + 133488*uk_120 + 411588*uk_121 + 834300*uk_122 + 126072*uk_123 + 1269063*uk_124 + 2572425*uk_125 + 388722*uk_126 + 5214375*uk_127 + 787950*uk_128 + 119068*uk_129 + 1825884*uk_13 + 39304*uk_130 + 41616*uk_131 + 128316*uk_132 + 260100*uk_133 + 39304*uk_134 + 44064*uk_135 + 135864*uk_136 + 275400*uk_137 + 41616*uk_138 + 418914*uk_139 + 5629809*uk_14 + 849150*uk_140 + 128316*uk_141 + 1721250*uk_142 + 260100*uk_143 + 39304*uk_144 + 46656*uk_145 + 143856*uk_146 + 291600*uk_147 + 44064*uk_148 + 443556*uk_149 + 11411775*uk_15 + 899100*uk_150 + 135864*uk_151 + 1822500*uk_152 + 275400*uk_153 + 41616*uk_154 + 1367631*uk_155 + 2772225*uk_156 + 418914*uk_157 + 5619375*uk_158 + 849150*uk_159 + 1724446*uk_16 + 128316*uk_160 + 11390625*uk_161 + 1721250*uk_162 + 260100*uk_163 + 39304*uk_164 + 3025*uk_17 + 5665*uk_18 + 1870*uk_19 + 55*uk_2 + 1980*uk_20 + 6105*uk_21 + 12375*uk_22 + 1870*uk_23 + 10609*uk_24 + 3502*uk_25 + 3708*uk_26 + 11433*uk_27 + 23175*uk_28 + 3502*uk_29 + 103*uk_3 + 1156*uk_30 + 1224*uk_31 + 3774*uk_32 + 7650*uk_33 + 1156*uk_34 + 1296*uk_35 + 3996*uk_36 + 8100*uk_37 + 1224*uk_38 + 12321*uk_39 + 34*uk_4 + 24975*uk_40 + 3774*uk_41 + 50625*uk_42 + 7650*uk_43 + 1156*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 264958946983*uk_47 + 87462176674*uk_48 + 92607010596*uk_49 + 36*uk_5 + 285538282671*uk_50 + 578793816225*uk_51 + 87462176674*uk_52 + 153424975*uk_53 + 287323135*uk_54 + 94844530*uk_55 + 100423620*uk_56 + 309639495*uk_57 + 627647625*uk_58 + 94844530*uk_59 + 111*uk_6 + 538077871*uk_60 + 177617938*uk_61 + 188066052*uk_62 + 579870327*uk_63 + 1175412825*uk_64 + 177617938*uk_65 + 58631164*uk_66 + 62080056*uk_67 + 191413506*uk_68 + 388000350*uk_69 + 225*uk_7 + 58631164*uk_70 + 65731824*uk_71 + 202673124*uk_72 + 410823900*uk_73 + 62080056*uk_74 + 624908799*uk_75 + 1266707025*uk_76 + 191413506*uk_77 + 2567649375*uk_78 + 388000350*uk_79 + 34*uk_8 + 58631164*uk_80 + 166375*uk_81 + 311575*uk_82 + 102850*uk_83 + 108900*uk_84 + 335775*uk_85 + 680625*uk_86 + 102850*uk_87 + 583495*uk_88 + 192610*uk_89 + 2572416961*uk_9 + 203940*uk_90 + 628815*uk_91 + 1274625*uk_92 + 192610*uk_93 + 63580*uk_94 + 67320*uk_95 + 207570*uk_96 + 420750*uk_97 + 63580*uk_98 + 71280*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 198880*uk_100 + 396000*uk_101 + 181280*uk_102 + 702295*uk_103 + 1398375*uk_104 + 640145*uk_105 + 2784375*uk_106 + 1274625*uk_107 + 583495*uk_108 + 857375*uk_109 + 4818305*uk_11 + 929575*uk_110 + 288800*uk_111 + 1019825*uk_112 + 2030625*uk_113 + 929575*uk_114 + 1007855*uk_115 + 313120*uk_116 + 1105705*uk_117 + 2201625*uk_118 + 1007855*uk_119 + 5224057*uk_12 + 97280*uk_120 + 343520*uk_121 + 684000*uk_122 + 313120*uk_123 + 1213055*uk_124 + 2415375*uk_125 + 1105705*uk_126 + 4809375*uk_127 + 2201625*uk_128 + 1007855*uk_129 + 1623008*uk_13 + 1092727*uk_130 + 339488*uk_131 + 1198817*uk_132 + 2387025*uk_133 + 1092727*uk_134 + 105472*uk_135 + 372448*uk_136 + 741600*uk_137 + 339488*uk_138 + 1315207*uk_139 + 5731247*uk_14 + 2618775*uk_140 + 1198817*uk_141 + 5214375*uk_142 + 2387025*uk_143 + 1092727*uk_144 + 32768*uk_145 + 115712*uk_146 + 230400*uk_147 + 105472*uk_148 + 408608*uk_149 + 11411775*uk_15 + 813600*uk_150 + 372448*uk_151 + 1620000*uk_152 + 741600*uk_153 + 339488*uk_154 + 1442897*uk_155 + 2873025*uk_156 + 1315207*uk_157 + 5720625*uk_158 + 2618775*uk_159 + 5224057*uk_16 + 1198817*uk_160 + 11390625*uk_161 + 5214375*uk_162 + 2387025*uk_163 + 1092727*uk_164 + 3025*uk_17 + 5225*uk_18 + 5665*uk_19 + 55*uk_2 + 1760*uk_20 + 6215*uk_21 + 12375*uk_22 + 5665*uk_23 + 9025*uk_24 + 9785*uk_25 + 3040*uk_26 + 10735*uk_27 + 21375*uk_28 + 9785*uk_29 + 95*uk_3 + 10609*uk_30 + 3296*uk_31 + 11639*uk_32 + 23175*uk_33 + 10609*uk_34 + 1024*uk_35 + 3616*uk_36 + 7200*uk_37 + 3296*uk_38 + 12769*uk_39 + 103*uk_4 + 25425*uk_40 + 11639*uk_41 + 50625*uk_42 + 23175*uk_43 + 10609*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 244379611295*uk_47 + 264958946983*uk_48 + 82317342752*uk_49 + 32*uk_5 + 290683116593*uk_50 + 578793816225*uk_51 + 264958946983*uk_52 + 153424975*uk_53 + 265006775*uk_54 + 287323135*uk_55 + 89265440*uk_56 + 315218585*uk_57 + 627647625*uk_58 + 287323135*uk_59 + 113*uk_6 + 457738975*uk_60 + 496285415*uk_61 + 154185760*uk_62 + 544468465*uk_63 + 1084118625*uk_64 + 496285415*uk_65 + 538077871*uk_66 + 167169824*uk_67 + 590318441*uk_68 + 1175412825*uk_69 + 225*uk_7 + 538077871*uk_70 + 51936256*uk_71 + 183399904*uk_72 + 365176800*uk_73 + 167169824*uk_74 + 647630911*uk_75 + 1289530575*uk_76 + 590318441*uk_77 + 2567649375*uk_78 + 1175412825*uk_79 + 103*uk_8 + 538077871*uk_80 + 166375*uk_81 + 287375*uk_82 + 311575*uk_83 + 96800*uk_84 + 341825*uk_85 + 680625*uk_86 + 311575*uk_87 + 496375*uk_88 + 538175*uk_89 + 2572416961*uk_9 + 167200*uk_90 + 590425*uk_91 + 1175625*uk_92 + 538175*uk_93 + 583495*uk_94 + 181280*uk_95 + 640145*uk_96 + 1274625*uk_97 + 583495*uk_98 + 56320*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 177100*uk_100 + 346500*uk_101 + 146300*uk_102 + 727375*uk_103 + 1423125*uk_104 + 600875*uk_105 + 2784375*uk_106 + 1175625*uk_107 + 496375*uk_108 + 64*uk_109 + 202876*uk_11 + 1520*uk_110 + 448*uk_111 + 1840*uk_112 + 3600*uk_113 + 1520*uk_114 + 36100*uk_115 + 10640*uk_116 + 43700*uk_117 + 85500*uk_118 + 36100*uk_119 + 4818305*uk_12 + 3136*uk_120 + 12880*uk_121 + 25200*uk_122 + 10640*uk_123 + 52900*uk_124 + 103500*uk_125 + 43700*uk_126 + 202500*uk_127 + 85500*uk_128 + 36100*uk_129 + 1420132*uk_13 + 857375*uk_130 + 252700*uk_131 + 1037875*uk_132 + 2030625*uk_133 + 857375*uk_134 + 74480*uk_135 + 305900*uk_136 + 598500*uk_137 + 252700*uk_138 + 1256375*uk_139 + 5832685*uk_14 + 2458125*uk_140 + 1037875*uk_141 + 4809375*uk_142 + 2030625*uk_143 + 857375*uk_144 + 21952*uk_145 + 90160*uk_146 + 176400*uk_147 + 74480*uk_148 + 370300*uk_149 + 11411775*uk_15 + 724500*uk_150 + 305900*uk_151 + 1417500*uk_152 + 598500*uk_153 + 252700*uk_154 + 1520875*uk_155 + 2975625*uk_156 + 1256375*uk_157 + 5821875*uk_158 + 2458125*uk_159 + 4818305*uk_16 + 1037875*uk_160 + 11390625*uk_161 + 4809375*uk_162 + 2030625*uk_163 + 857375*uk_164 + 3025*uk_17 + 220*uk_18 + 5225*uk_19 + 55*uk_2 + 1540*uk_20 + 6325*uk_21 + 12375*uk_22 + 5225*uk_23 + 16*uk_24 + 380*uk_25 + 112*uk_26 + 460*uk_27 + 900*uk_28 + 380*uk_29 + 4*uk_3 + 9025*uk_30 + 2660*uk_31 + 10925*uk_32 + 21375*uk_33 + 9025*uk_34 + 784*uk_35 + 3220*uk_36 + 6300*uk_37 + 2660*uk_38 + 13225*uk_39 + 95*uk_4 + 25875*uk_40 + 10925*uk_41 + 50625*uk_42 + 21375*uk_43 + 9025*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 10289667844*uk_47 + 244379611295*uk_48 + 72027674908*uk_49 + 28*uk_5 + 295827950515*uk_50 + 578793816225*uk_51 + 244379611295*uk_52 + 153424975*uk_53 + 11158180*uk_54 + 265006775*uk_55 + 78107260*uk_56 + 320797675*uk_57 + 627647625*uk_58 + 265006775*uk_59 + 115*uk_6 + 811504*uk_60 + 19273220*uk_61 + 5680528*uk_62 + 23330740*uk_63 + 45647100*uk_64 + 19273220*uk_65 + 457738975*uk_66 + 134912540*uk_67 + 554105075*uk_68 + 1084118625*uk_69 + 225*uk_7 + 457738975*uk_70 + 39763696*uk_71 + 163315180*uk_72 + 319529700*uk_73 + 134912540*uk_74 + 670758775*uk_75 + 1312354125*uk_76 + 554105075*uk_77 + 2567649375*uk_78 + 1084118625*uk_79 + 95*uk_8 + 457738975*uk_80 + 166375*uk_81 + 12100*uk_82 + 287375*uk_83 + 84700*uk_84 + 347875*uk_85 + 680625*uk_86 + 287375*uk_87 + 880*uk_88 + 20900*uk_89 + 2572416961*uk_9 + 6160*uk_90 + 25300*uk_91 + 49500*uk_92 + 20900*uk_93 + 496375*uk_94 + 146300*uk_95 + 600875*uk_96 + 1175625*uk_97 + 496375*uk_98 + 43120*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 205920*uk_100 + 396000*uk_101 + 7040*uk_102 + 752895*uk_103 + 1447875*uk_104 + 25740*uk_105 + 2784375*uk_106 + 49500*uk_107 + 880*uk_108 + 195112*uk_109 + 2941702*uk_11 + 13456*uk_110 + 107648*uk_111 + 393588*uk_112 + 756900*uk_113 + 13456*uk_114 + 928*uk_115 + 7424*uk_116 + 27144*uk_117 + 52200*uk_118 + 928*uk_119 + 202876*uk_12 + 59392*uk_120 + 217152*uk_121 + 417600*uk_122 + 7424*uk_123 + 793962*uk_124 + 1526850*uk_125 + 27144*uk_126 + 2936250*uk_127 + 52200*uk_128 + 928*uk_129 + 1623008*uk_13 + 64*uk_130 + 512*uk_131 + 1872*uk_132 + 3600*uk_133 + 64*uk_134 + 4096*uk_135 + 14976*uk_136 + 28800*uk_137 + 512*uk_138 + 54756*uk_139 + 5934123*uk_14 + 105300*uk_140 + 1872*uk_141 + 202500*uk_142 + 3600*uk_143 + 64*uk_144 + 32768*uk_145 + 119808*uk_146 + 230400*uk_147 + 4096*uk_148 + 438048*uk_149 + 11411775*uk_15 + 842400*uk_150 + 14976*uk_151 + 1620000*uk_152 + 28800*uk_153 + 512*uk_154 + 1601613*uk_155 + 3080025*uk_156 + 54756*uk_157 + 5923125*uk_158 + 105300*uk_159 + 202876*uk_16 + 1872*uk_160 + 11390625*uk_161 + 202500*uk_162 + 3600*uk_163 + 64*uk_164 + 3025*uk_17 + 3190*uk_18 + 220*uk_19 + 55*uk_2 + 1760*uk_20 + 6435*uk_21 + 12375*uk_22 + 220*uk_23 + 3364*uk_24 + 232*uk_25 + 1856*uk_26 + 6786*uk_27 + 13050*uk_28 + 232*uk_29 + 58*uk_3 + 16*uk_30 + 128*uk_31 + 468*uk_32 + 900*uk_33 + 16*uk_34 + 1024*uk_35 + 3744*uk_36 + 7200*uk_37 + 128*uk_38 + 13689*uk_39 + 4*uk_4 + 26325*uk_40 + 468*uk_41 + 50625*uk_42 + 900*uk_43 + 16*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 149200183738*uk_47 + 10289667844*uk_48 + 82317342752*uk_49 + 32*uk_5 + 300972784437*uk_50 + 578793816225*uk_51 + 10289667844*uk_52 + 153424975*uk_53 + 161793610*uk_54 + 11158180*uk_55 + 89265440*uk_56 + 326376765*uk_57 + 627647625*uk_58 + 11158180*uk_59 + 117*uk_6 + 170618716*uk_60 + 11766808*uk_61 + 94134464*uk_62 + 344179134*uk_63 + 661882950*uk_64 + 11766808*uk_65 + 811504*uk_66 + 6492032*uk_67 + 23736492*uk_68 + 45647100*uk_69 + 225*uk_7 + 811504*uk_70 + 51936256*uk_71 + 189891936*uk_72 + 365176800*uk_73 + 6492032*uk_74 + 694292391*uk_75 + 1335177675*uk_76 + 23736492*uk_77 + 2567649375*uk_78 + 45647100*uk_79 + 4*uk_8 + 811504*uk_80 + 166375*uk_81 + 175450*uk_82 + 12100*uk_83 + 96800*uk_84 + 353925*uk_85 + 680625*uk_86 + 12100*uk_87 + 185020*uk_88 + 12760*uk_89 + 2572416961*uk_9 + 102080*uk_90 + 373230*uk_91 + 717750*uk_92 + 12760*uk_93 + 880*uk_94 + 7040*uk_95 + 25740*uk_96 + 49500*uk_97 + 880*uk_98 + 56320*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 183260*uk_100 + 346500*uk_101 + 89320*uk_102 + 778855*uk_103 + 1472625*uk_104 + 379610*uk_105 + 2784375*uk_106 + 717750*uk_107 + 185020*uk_108 + 15625*uk_109 + 1267975*uk_11 + 36250*uk_110 + 17500*uk_111 + 74375*uk_112 + 140625*uk_113 + 36250*uk_114 + 84100*uk_115 + 40600*uk_116 + 172550*uk_117 + 326250*uk_118 + 84100*uk_119 + 2941702*uk_12 + 19600*uk_120 + 83300*uk_121 + 157500*uk_122 + 40600*uk_123 + 354025*uk_124 + 669375*uk_125 + 172550*uk_126 + 1265625*uk_127 + 326250*uk_128 + 84100*uk_129 + 1420132*uk_13 + 195112*uk_130 + 94192*uk_131 + 400316*uk_132 + 756900*uk_133 + 195112*uk_134 + 45472*uk_135 + 193256*uk_136 + 365400*uk_137 + 94192*uk_138 + 821338*uk_139 + 6035561*uk_14 + 1552950*uk_140 + 400316*uk_141 + 2936250*uk_142 + 756900*uk_143 + 195112*uk_144 + 21952*uk_145 + 93296*uk_146 + 176400*uk_147 + 45472*uk_148 + 396508*uk_149 + 11411775*uk_15 + 749700*uk_150 + 193256*uk_151 + 1417500*uk_152 + 365400*uk_153 + 94192*uk_154 + 1685159*uk_155 + 3186225*uk_156 + 821338*uk_157 + 6024375*uk_158 + 1552950*uk_159 + 2941702*uk_16 + 400316*uk_160 + 11390625*uk_161 + 2936250*uk_162 + 756900*uk_163 + 195112*uk_164 + 3025*uk_17 + 1375*uk_18 + 3190*uk_19 + 55*uk_2 + 1540*uk_20 + 6545*uk_21 + 12375*uk_22 + 3190*uk_23 + 625*uk_24 + 1450*uk_25 + 700*uk_26 + 2975*uk_27 + 5625*uk_28 + 1450*uk_29 + 25*uk_3 + 3364*uk_30 + 1624*uk_31 + 6902*uk_32 + 13050*uk_33 + 3364*uk_34 + 784*uk_35 + 3332*uk_36 + 6300*uk_37 + 1624*uk_38 + 14161*uk_39 + 58*uk_4 + 26775*uk_40 + 6902*uk_41 + 50625*uk_42 + 13050*uk_43 + 3364*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 64310424025*uk_47 + 149200183738*uk_48 + 72027674908*uk_49 + 28*uk_5 + 306117618359*uk_50 + 578793816225*uk_51 + 149200183738*uk_52 + 153424975*uk_53 + 69738625*uk_54 + 161793610*uk_55 + 78107260*uk_56 + 331955855*uk_57 + 627647625*uk_58 + 161793610*uk_59 + 119*uk_6 + 31699375*uk_60 + 73542550*uk_61 + 35503300*uk_62 + 150889025*uk_63 + 285294375*uk_64 + 73542550*uk_65 + 170618716*uk_66 + 82367656*uk_67 + 350062538*uk_68 + 661882950*uk_69 + 225*uk_7 + 170618716*uk_70 + 39763696*uk_71 + 168995708*uk_72 + 319529700*uk_73 + 82367656*uk_74 + 718231759*uk_75 + 1358001225*uk_76 + 350062538*uk_77 + 2567649375*uk_78 + 661882950*uk_79 + 58*uk_8 + 170618716*uk_80 + 166375*uk_81 + 75625*uk_82 + 175450*uk_83 + 84700*uk_84 + 359975*uk_85 + 680625*uk_86 + 175450*uk_87 + 34375*uk_88 + 79750*uk_89 + 2572416961*uk_9 + 38500*uk_90 + 163625*uk_91 + 309375*uk_92 + 79750*uk_93 + 185020*uk_94 + 89320*uk_95 + 379610*uk_96 + 717750*uk_97 + 185020*uk_98 + 43120*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 186340*uk_100 + 346500*uk_101 + 38500*uk_102 + 805255*uk_103 + 1497375*uk_104 + 166375*uk_105 + 2784375*uk_106 + 309375*uk_107 + 34375*uk_108 + 8000*uk_109 + 1014380*uk_11 + 10000*uk_110 + 11200*uk_111 + 48400*uk_112 + 90000*uk_113 + 10000*uk_114 + 12500*uk_115 + 14000*uk_116 + 60500*uk_117 + 112500*uk_118 + 12500*uk_119 + 1267975*uk_12 + 15680*uk_120 + 67760*uk_121 + 126000*uk_122 + 14000*uk_123 + 292820*uk_124 + 544500*uk_125 + 60500*uk_126 + 1012500*uk_127 + 112500*uk_128 + 12500*uk_129 + 1420132*uk_13 + 15625*uk_130 + 17500*uk_131 + 75625*uk_132 + 140625*uk_133 + 15625*uk_134 + 19600*uk_135 + 84700*uk_136 + 157500*uk_137 + 17500*uk_138 + 366025*uk_139 + 6136999*uk_14 + 680625*uk_140 + 75625*uk_141 + 1265625*uk_142 + 140625*uk_143 + 15625*uk_144 + 21952*uk_145 + 94864*uk_146 + 176400*uk_147 + 19600*uk_148 + 409948*uk_149 + 11411775*uk_15 + 762300*uk_150 + 84700*uk_151 + 1417500*uk_152 + 157500*uk_153 + 17500*uk_154 + 1771561*uk_155 + 3294225*uk_156 + 366025*uk_157 + 6125625*uk_158 + 680625*uk_159 + 1267975*uk_16 + 75625*uk_160 + 11390625*uk_161 + 1265625*uk_162 + 140625*uk_163 + 15625*uk_164 + 3025*uk_17 + 1100*uk_18 + 1375*uk_19 + 55*uk_2 + 1540*uk_20 + 6655*uk_21 + 12375*uk_22 + 1375*uk_23 + 400*uk_24 + 500*uk_25 + 560*uk_26 + 2420*uk_27 + 4500*uk_28 + 500*uk_29 + 20*uk_3 + 625*uk_30 + 700*uk_31 + 3025*uk_32 + 5625*uk_33 + 625*uk_34 + 784*uk_35 + 3388*uk_36 + 6300*uk_37 + 700*uk_38 + 14641*uk_39 + 25*uk_4 + 27225*uk_40 + 3025*uk_41 + 50625*uk_42 + 5625*uk_43 + 625*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 51448339220*uk_47 + 64310424025*uk_48 + 72027674908*uk_49 + 28*uk_5 + 311262452281*uk_50 + 578793816225*uk_51 + 64310424025*uk_52 + 153424975*uk_53 + 55790900*uk_54 + 69738625*uk_55 + 78107260*uk_56 + 337534945*uk_57 + 627647625*uk_58 + 69738625*uk_59 + 121*uk_6 + 20287600*uk_60 + 25359500*uk_61 + 28402640*uk_62 + 122739980*uk_63 + 228235500*uk_64 + 25359500*uk_65 + 31699375*uk_66 + 35503300*uk_67 + 153424975*uk_68 + 285294375*uk_69 + 225*uk_7 + 31699375*uk_70 + 39763696*uk_71 + 171835972*uk_72 + 319529700*uk_73 + 35503300*uk_74 + 742576879*uk_75 + 1380824775*uk_76 + 153424975*uk_77 + 2567649375*uk_78 + 285294375*uk_79 + 25*uk_8 + 31699375*uk_80 + 166375*uk_81 + 60500*uk_82 + 75625*uk_83 + 84700*uk_84 + 366025*uk_85 + 680625*uk_86 + 75625*uk_87 + 22000*uk_88 + 27500*uk_89 + 2572416961*uk_9 + 30800*uk_90 + 133100*uk_91 + 247500*uk_92 + 27500*uk_93 + 34375*uk_94 + 38500*uk_95 + 166375*uk_96 + 309375*uk_97 + 34375*uk_98 + 43120*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 189420*uk_100 + 346500*uk_101 + 30800*uk_102 + 832095*uk_103 + 1522125*uk_104 + 135300*uk_105 + 2784375*uk_106 + 247500*uk_107 + 22000*uk_108 + 79507*uk_109 + 2180917*uk_11 + 36980*uk_110 + 51772*uk_111 + 227427*uk_112 + 416025*uk_113 + 36980*uk_114 + 17200*uk_115 + 24080*uk_116 + 105780*uk_117 + 193500*uk_118 + 17200*uk_119 + 1014380*uk_12 + 33712*uk_120 + 148092*uk_121 + 270900*uk_122 + 24080*uk_123 + 650547*uk_124 + 1190025*uk_125 + 105780*uk_126 + 2176875*uk_127 + 193500*uk_128 + 17200*uk_129 + 1420132*uk_13 + 8000*uk_130 + 11200*uk_131 + 49200*uk_132 + 90000*uk_133 + 8000*uk_134 + 15680*uk_135 + 68880*uk_136 + 126000*uk_137 + 11200*uk_138 + 302580*uk_139 + 6238437*uk_14 + 553500*uk_140 + 49200*uk_141 + 1012500*uk_142 + 90000*uk_143 + 8000*uk_144 + 21952*uk_145 + 96432*uk_146 + 176400*uk_147 + 15680*uk_148 + 423612*uk_149 + 11411775*uk_15 + 774900*uk_150 + 68880*uk_151 + 1417500*uk_152 + 126000*uk_153 + 11200*uk_154 + 1860867*uk_155 + 3404025*uk_156 + 302580*uk_157 + 6226875*uk_158 + 553500*uk_159 + 1014380*uk_16 + 49200*uk_160 + 11390625*uk_161 + 1012500*uk_162 + 90000*uk_163 + 8000*uk_164 + 3025*uk_17 + 2365*uk_18 + 1100*uk_19 + 55*uk_2 + 1540*uk_20 + 6765*uk_21 + 12375*uk_22 + 1100*uk_23 + 1849*uk_24 + 860*uk_25 + 1204*uk_26 + 5289*uk_27 + 9675*uk_28 + 860*uk_29 + 43*uk_3 + 400*uk_30 + 560*uk_31 + 2460*uk_32 + 4500*uk_33 + 400*uk_34 + 784*uk_35 + 3444*uk_36 + 6300*uk_37 + 560*uk_38 + 15129*uk_39 + 20*uk_4 + 27675*uk_40 + 2460*uk_41 + 50625*uk_42 + 4500*uk_43 + 400*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 110613929323*uk_47 + 51448339220*uk_48 + 72027674908*uk_49 + 28*uk_5 + 316407286203*uk_50 + 578793816225*uk_51 + 51448339220*uk_52 + 153424975*uk_53 + 119950435*uk_54 + 55790900*uk_55 + 78107260*uk_56 + 343114035*uk_57 + 627647625*uk_58 + 55790900*uk_59 + 123*uk_6 + 93779431*uk_60 + 43618340*uk_61 + 61065676*uk_62 + 268252791*uk_63 + 490706325*uk_64 + 43618340*uk_65 + 20287600*uk_66 + 28402640*uk_67 + 124768740*uk_68 + 228235500*uk_69 + 225*uk_7 + 20287600*uk_70 + 39763696*uk_71 + 174676236*uk_72 + 319529700*uk_73 + 28402640*uk_74 + 767327751*uk_75 + 1403648325*uk_76 + 124768740*uk_77 + 2567649375*uk_78 + 228235500*uk_79 + 20*uk_8 + 20287600*uk_80 + 166375*uk_81 + 130075*uk_82 + 60500*uk_83 + 84700*uk_84 + 372075*uk_85 + 680625*uk_86 + 60500*uk_87 + 101695*uk_88 + 47300*uk_89 + 2572416961*uk_9 + 66220*uk_90 + 290895*uk_91 + 532125*uk_92 + 47300*uk_93 + 22000*uk_94 + 30800*uk_95 + 135300*uk_96 + 247500*uk_97 + 22000*uk_98 + 43120*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 192500*uk_100 + 346500*uk_101 + 66220*uk_102 + 859375*uk_103 + 1546875*uk_104 + 295625*uk_105 + 2784375*uk_106 + 532125*uk_107 + 101695*uk_108 + 830584*uk_109 + 4767586*uk_11 + 379948*uk_110 + 247408*uk_111 + 1104500*uk_112 + 1988100*uk_113 + 379948*uk_114 + 173806*uk_115 + 113176*uk_116 + 505250*uk_117 + 909450*uk_118 + 173806*uk_119 + 2180917*uk_12 + 73696*uk_120 + 329000*uk_121 + 592200*uk_122 + 113176*uk_123 + 1468750*uk_124 + 2643750*uk_125 + 505250*uk_126 + 4758750*uk_127 + 909450*uk_128 + 173806*uk_129 + 1420132*uk_13 + 79507*uk_130 + 51772*uk_131 + 231125*uk_132 + 416025*uk_133 + 79507*uk_134 + 33712*uk_135 + 150500*uk_136 + 270900*uk_137 + 51772*uk_138 + 671875*uk_139 + 6339875*uk_14 + 1209375*uk_140 + 231125*uk_141 + 2176875*uk_142 + 416025*uk_143 + 79507*uk_144 + 21952*uk_145 + 98000*uk_146 + 176400*uk_147 + 33712*uk_148 + 437500*uk_149 + 11411775*uk_15 + 787500*uk_150 + 150500*uk_151 + 1417500*uk_152 + 270900*uk_153 + 51772*uk_154 + 1953125*uk_155 + 3515625*uk_156 + 671875*uk_157 + 6328125*uk_158 + 1209375*uk_159 + 2180917*uk_16 + 231125*uk_160 + 11390625*uk_161 + 2176875*uk_162 + 416025*uk_163 + 79507*uk_164 + 3025*uk_17 + 5170*uk_18 + 2365*uk_19 + 55*uk_2 + 1540*uk_20 + 6875*uk_21 + 12375*uk_22 + 2365*uk_23 + 8836*uk_24 + 4042*uk_25 + 2632*uk_26 + 11750*uk_27 + 21150*uk_28 + 4042*uk_29 + 94*uk_3 + 1849*uk_30 + 1204*uk_31 + 5375*uk_32 + 9675*uk_33 + 1849*uk_34 + 784*uk_35 + 3500*uk_36 + 6300*uk_37 + 1204*uk_38 + 15625*uk_39 + 43*uk_4 + 28125*uk_40 + 5375*uk_41 + 50625*uk_42 + 9675*uk_43 + 1849*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 241807194334*uk_47 + 110613929323*uk_48 + 72027674908*uk_49 + 28*uk_5 + 321552120125*uk_50 + 578793816225*uk_51 + 110613929323*uk_52 + 153424975*uk_53 + 262217230*uk_54 + 119950435*uk_55 + 78107260*uk_56 + 348693125*uk_57 + 627647625*uk_58 + 119950435*uk_59 + 125*uk_6 + 448153084*uk_60 + 205006198*uk_61 + 133492408*uk_62 + 595948250*uk_63 + 1072706850*uk_64 + 205006198*uk_65 + 93779431*uk_66 + 61065676*uk_67 + 272614625*uk_68 + 490706325*uk_69 + 225*uk_7 + 93779431*uk_70 + 39763696*uk_71 + 177516500*uk_72 + 319529700*uk_73 + 61065676*uk_74 + 792484375*uk_75 + 1426471875*uk_76 + 272614625*uk_77 + 2567649375*uk_78 + 490706325*uk_79 + 43*uk_8 + 93779431*uk_80 + 166375*uk_81 + 284350*uk_82 + 130075*uk_83 + 84700*uk_84 + 378125*uk_85 + 680625*uk_86 + 130075*uk_87 + 485980*uk_88 + 222310*uk_89 + 2572416961*uk_9 + 144760*uk_90 + 646250*uk_91 + 1163250*uk_92 + 222310*uk_93 + 101695*uk_94 + 66220*uk_95 + 295625*uk_96 + 532125*uk_97 + 101695*uk_98 + 43120*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 167640*uk_100 + 297000*uk_101 + 124080*uk_102 + 887095*uk_103 + 1571625*uk_104 + 656590*uk_105 + 2784375*uk_106 + 1163250*uk_107 + 485980*uk_108 + 97336*uk_109 + 2333074*uk_11 + 198904*uk_110 + 50784*uk_111 + 268732*uk_112 + 476100*uk_113 + 198904*uk_114 + 406456*uk_115 + 103776*uk_116 + 549148*uk_117 + 972900*uk_118 + 406456*uk_119 + 4767586*uk_12 + 26496*uk_120 + 140208*uk_121 + 248400*uk_122 + 103776*uk_123 + 741934*uk_124 + 1314450*uk_125 + 549148*uk_126 + 2328750*uk_127 + 972900*uk_128 + 406456*uk_129 + 1217256*uk_13 + 830584*uk_130 + 212064*uk_131 + 1122172*uk_132 + 1988100*uk_133 + 830584*uk_134 + 54144*uk_135 + 286512*uk_136 + 507600*uk_137 + 212064*uk_138 + 1516126*uk_139 + 6441313*uk_14 + 2686050*uk_140 + 1122172*uk_141 + 4758750*uk_142 + 1988100*uk_143 + 830584*uk_144 + 13824*uk_145 + 73152*uk_146 + 129600*uk_147 + 54144*uk_148 + 387096*uk_149 + 11411775*uk_15 + 685800*uk_150 + 286512*uk_151 + 1215000*uk_152 + 507600*uk_153 + 212064*uk_154 + 2048383*uk_155 + 3629025*uk_156 + 1516126*uk_157 + 6429375*uk_158 + 2686050*uk_159 + 4767586*uk_16 + 1122172*uk_160 + 11390625*uk_161 + 4758750*uk_162 + 1988100*uk_163 + 830584*uk_164 + 3025*uk_17 + 2530*uk_18 + 5170*uk_19 + 55*uk_2 + 1320*uk_20 + 6985*uk_21 + 12375*uk_22 + 5170*uk_23 + 2116*uk_24 + 4324*uk_25 + 1104*uk_26 + 5842*uk_27 + 10350*uk_28 + 4324*uk_29 + 46*uk_3 + 8836*uk_30 + 2256*uk_31 + 11938*uk_32 + 21150*uk_33 + 8836*uk_34 + 576*uk_35 + 3048*uk_36 + 5400*uk_37 + 2256*uk_38 + 16129*uk_39 + 94*uk_4 + 28575*uk_40 + 11938*uk_41 + 50625*uk_42 + 21150*uk_43 + 8836*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 118331180206*uk_47 + 241807194334*uk_48 + 61738007064*uk_49 + 24*uk_5 + 326696954047*uk_50 + 578793816225*uk_51 + 241807194334*uk_52 + 153424975*uk_53 + 128319070*uk_54 + 262217230*uk_55 + 66949080*uk_56 + 354272215*uk_57 + 627647625*uk_58 + 262217230*uk_59 + 127*uk_6 + 107321404*uk_60 + 219308956*uk_61 + 55993776*uk_62 + 296300398*uk_63 + 524941650*uk_64 + 219308956*uk_65 + 448153084*uk_66 + 114422064*uk_67 + 605483422*uk_68 + 1072706850*uk_69 + 225*uk_7 + 448153084*uk_70 + 29214144*uk_71 + 154591512*uk_72 + 273882600*uk_73 + 114422064*uk_74 + 818046751*uk_75 + 1449295425*uk_76 + 605483422*uk_77 + 2567649375*uk_78 + 1072706850*uk_79 + 94*uk_8 + 448153084*uk_80 + 166375*uk_81 + 139150*uk_82 + 284350*uk_83 + 72600*uk_84 + 384175*uk_85 + 680625*uk_86 + 284350*uk_87 + 116380*uk_88 + 237820*uk_89 + 2572416961*uk_9 + 60720*uk_90 + 321310*uk_91 + 569250*uk_92 + 237820*uk_93 + 485980*uk_94 + 124080*uk_95 + 656590*uk_96 + 1163250*uk_97 + 485980*uk_98 + 31680*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 170280*uk_100 + 297000*uk_101 + 60720*uk_102 + 915255*uk_103 + 1596375*uk_104 + 326370*uk_105 + 2784375*uk_106 + 569250*uk_107 + 116380*uk_108 + 10648*uk_109 + 1115818*uk_11 + 22264*uk_110 + 11616*uk_111 + 62436*uk_112 + 108900*uk_113 + 22264*uk_114 + 46552*uk_115 + 24288*uk_116 + 130548*uk_117 + 227700*uk_118 + 46552*uk_119 + 2333074*uk_12 + 12672*uk_120 + 68112*uk_121 + 118800*uk_122 + 24288*uk_123 + 366102*uk_124 + 638550*uk_125 + 130548*uk_126 + 1113750*uk_127 + 227700*uk_128 + 46552*uk_129 + 1217256*uk_13 + 97336*uk_130 + 50784*uk_131 + 272964*uk_132 + 476100*uk_133 + 97336*uk_134 + 26496*uk_135 + 142416*uk_136 + 248400*uk_137 + 50784*uk_138 + 765486*uk_139 + 6542751*uk_14 + 1335150*uk_140 + 272964*uk_141 + 2328750*uk_142 + 476100*uk_143 + 97336*uk_144 + 13824*uk_145 + 74304*uk_146 + 129600*uk_147 + 26496*uk_148 + 399384*uk_149 + 11411775*uk_15 + 696600*uk_150 + 142416*uk_151 + 1215000*uk_152 + 248400*uk_153 + 50784*uk_154 + 2146689*uk_155 + 3744225*uk_156 + 765486*uk_157 + 6530625*uk_158 + 1335150*uk_159 + 2333074*uk_16 + 272964*uk_160 + 11390625*uk_161 + 2328750*uk_162 + 476100*uk_163 + 97336*uk_164 + 3025*uk_17 + 1210*uk_18 + 2530*uk_19 + 55*uk_2 + 1320*uk_20 + 7095*uk_21 + 12375*uk_22 + 2530*uk_23 + 484*uk_24 + 1012*uk_25 + 528*uk_26 + 2838*uk_27 + 4950*uk_28 + 1012*uk_29 + 22*uk_3 + 2116*uk_30 + 1104*uk_31 + 5934*uk_32 + 10350*uk_33 + 2116*uk_34 + 576*uk_35 + 3096*uk_36 + 5400*uk_37 + 1104*uk_38 + 16641*uk_39 + 46*uk_4 + 29025*uk_40 + 5934*uk_41 + 50625*uk_42 + 10350*uk_43 + 2116*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 56593173142*uk_47 + 118331180206*uk_48 + 61738007064*uk_49 + 24*uk_5 + 331841787969*uk_50 + 578793816225*uk_51 + 118331180206*uk_52 + 153424975*uk_53 + 61369990*uk_54 + 128319070*uk_55 + 66949080*uk_56 + 359851305*uk_57 + 627647625*uk_58 + 128319070*uk_59 + 129*uk_6 + 24547996*uk_60 + 51327628*uk_61 + 26779632*uk_62 + 143940522*uk_63 + 251059050*uk_64 + 51327628*uk_65 + 107321404*uk_66 + 55993776*uk_67 + 300966546*uk_68 + 524941650*uk_69 + 225*uk_7 + 107321404*uk_70 + 29214144*uk_71 + 157026024*uk_72 + 273882600*uk_73 + 55993776*uk_74 + 844014879*uk_75 + 1472118975*uk_76 + 300966546*uk_77 + 2567649375*uk_78 + 524941650*uk_79 + 46*uk_8 + 107321404*uk_80 + 166375*uk_81 + 66550*uk_82 + 139150*uk_83 + 72600*uk_84 + 390225*uk_85 + 680625*uk_86 + 139150*uk_87 + 26620*uk_88 + 55660*uk_89 + 2572416961*uk_9 + 29040*uk_90 + 156090*uk_91 + 272250*uk_92 + 55660*uk_93 + 116380*uk_94 + 60720*uk_95 + 326370*uk_96 + 569250*uk_97 + 116380*uk_98 + 31680*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 172920*uk_100 + 297000*uk_101 + 29040*uk_102 + 943855*uk_103 + 1621125*uk_104 + 158510*uk_105 + 2784375*uk_106 + 272250*uk_107 + 26620*uk_108 + 10648*uk_109 + 1115818*uk_11 + 10648*uk_110 + 11616*uk_111 + 63404*uk_112 + 108900*uk_113 + 10648*uk_114 + 10648*uk_115 + 11616*uk_116 + 63404*uk_117 + 108900*uk_118 + 10648*uk_119 + 1115818*uk_12 + 12672*uk_120 + 69168*uk_121 + 118800*uk_122 + 11616*uk_123 + 377542*uk_124 + 648450*uk_125 + 63404*uk_126 + 1113750*uk_127 + 108900*uk_128 + 10648*uk_129 + 1217256*uk_13 + 10648*uk_130 + 11616*uk_131 + 63404*uk_132 + 108900*uk_133 + 10648*uk_134 + 12672*uk_135 + 69168*uk_136 + 118800*uk_137 + 11616*uk_138 + 377542*uk_139 + 6644189*uk_14 + 648450*uk_140 + 63404*uk_141 + 1113750*uk_142 + 108900*uk_143 + 10648*uk_144 + 13824*uk_145 + 75456*uk_146 + 129600*uk_147 + 12672*uk_148 + 411864*uk_149 + 11411775*uk_15 + 707400*uk_150 + 69168*uk_151 + 1215000*uk_152 + 118800*uk_153 + 11616*uk_154 + 2248091*uk_155 + 3861225*uk_156 + 377542*uk_157 + 6631875*uk_158 + 648450*uk_159 + 1115818*uk_16 + 63404*uk_160 + 11390625*uk_161 + 1113750*uk_162 + 108900*uk_163 + 10648*uk_164 + 3025*uk_17 + 1210*uk_18 + 1210*uk_19 + 55*uk_2 + 1320*uk_20 + 7205*uk_21 + 12375*uk_22 + 1210*uk_23 + 484*uk_24 + 484*uk_25 + 528*uk_26 + 2882*uk_27 + 4950*uk_28 + 484*uk_29 + 22*uk_3 + 484*uk_30 + 528*uk_31 + 2882*uk_32 + 4950*uk_33 + 484*uk_34 + 576*uk_35 + 3144*uk_36 + 5400*uk_37 + 528*uk_38 + 17161*uk_39 + 22*uk_4 + 29475*uk_40 + 2882*uk_41 + 50625*uk_42 + 4950*uk_43 + 484*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 56593173142*uk_47 + 56593173142*uk_48 + 61738007064*uk_49 + 24*uk_5 + 336986621891*uk_50 + 578793816225*uk_51 + 56593173142*uk_52 + 153424975*uk_53 + 61369990*uk_54 + 61369990*uk_55 + 66949080*uk_56 + 365430395*uk_57 + 627647625*uk_58 + 61369990*uk_59 + 131*uk_6 + 24547996*uk_60 + 24547996*uk_61 + 26779632*uk_62 + 146172158*uk_63 + 251059050*uk_64 + 24547996*uk_65 + 24547996*uk_66 + 26779632*uk_67 + 146172158*uk_68 + 251059050*uk_69 + 225*uk_7 + 24547996*uk_70 + 29214144*uk_71 + 159460536*uk_72 + 273882600*uk_73 + 26779632*uk_74 + 870388759*uk_75 + 1494942525*uk_76 + 146172158*uk_77 + 2567649375*uk_78 + 251059050*uk_79 + 22*uk_8 + 24547996*uk_80 + 166375*uk_81 + 66550*uk_82 + 66550*uk_83 + 72600*uk_84 + 396275*uk_85 + 680625*uk_86 + 66550*uk_87 + 26620*uk_88 + 26620*uk_89 + 2572416961*uk_9 + 29040*uk_90 + 158510*uk_91 + 272250*uk_92 + 26620*uk_93 + 26620*uk_94 + 29040*uk_95 + 158510*uk_96 + 272250*uk_97 + 26620*uk_98 + 31680*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 175560*uk_100 + 297000*uk_101 + 29040*uk_102 + 972895*uk_103 + 1645875*uk_104 + 160930*uk_105 + 2784375*uk_106 + 272250*uk_107 + 26620*uk_108 + 97336*uk_109 + 2333074*uk_11 + 46552*uk_110 + 50784*uk_111 + 281428*uk_112 + 476100*uk_113 + 46552*uk_114 + 22264*uk_115 + 24288*uk_116 + 134596*uk_117 + 227700*uk_118 + 22264*uk_119 + 1115818*uk_12 + 26496*uk_120 + 146832*uk_121 + 248400*uk_122 + 24288*uk_123 + 813694*uk_124 + 1376550*uk_125 + 134596*uk_126 + 2328750*uk_127 + 227700*uk_128 + 22264*uk_129 + 1217256*uk_13 + 10648*uk_130 + 11616*uk_131 + 64372*uk_132 + 108900*uk_133 + 10648*uk_134 + 12672*uk_135 + 70224*uk_136 + 118800*uk_137 + 11616*uk_138 + 389158*uk_139 + 6745627*uk_14 + 658350*uk_140 + 64372*uk_141 + 1113750*uk_142 + 108900*uk_143 + 10648*uk_144 + 13824*uk_145 + 76608*uk_146 + 129600*uk_147 + 12672*uk_148 + 424536*uk_149 + 11411775*uk_15 + 718200*uk_150 + 70224*uk_151 + 1215000*uk_152 + 118800*uk_153 + 11616*uk_154 + 2352637*uk_155 + 3980025*uk_156 + 389158*uk_157 + 6733125*uk_158 + 658350*uk_159 + 1115818*uk_16 + 64372*uk_160 + 11390625*uk_161 + 1113750*uk_162 + 108900*uk_163 + 10648*uk_164 + 3025*uk_17 + 2530*uk_18 + 1210*uk_19 + 55*uk_2 + 1320*uk_20 + 7315*uk_21 + 12375*uk_22 + 1210*uk_23 + 2116*uk_24 + 1012*uk_25 + 1104*uk_26 + 6118*uk_27 + 10350*uk_28 + 1012*uk_29 + 46*uk_3 + 484*uk_30 + 528*uk_31 + 2926*uk_32 + 4950*uk_33 + 484*uk_34 + 576*uk_35 + 3192*uk_36 + 5400*uk_37 + 528*uk_38 + 17689*uk_39 + 22*uk_4 + 29925*uk_40 + 2926*uk_41 + 50625*uk_42 + 4950*uk_43 + 484*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 118331180206*uk_47 + 56593173142*uk_48 + 61738007064*uk_49 + 24*uk_5 + 342131455813*uk_50 + 578793816225*uk_51 + 56593173142*uk_52 + 153424975*uk_53 + 128319070*uk_54 + 61369990*uk_55 + 66949080*uk_56 + 371009485*uk_57 + 627647625*uk_58 + 61369990*uk_59 + 133*uk_6 + 107321404*uk_60 + 51327628*uk_61 + 55993776*uk_62 + 310298842*uk_63 + 524941650*uk_64 + 51327628*uk_65 + 24547996*uk_66 + 26779632*uk_67 + 148403794*uk_68 + 251059050*uk_69 + 225*uk_7 + 24547996*uk_70 + 29214144*uk_71 + 161895048*uk_72 + 273882600*uk_73 + 26779632*uk_74 + 897168391*uk_75 + 1517766075*uk_76 + 148403794*uk_77 + 2567649375*uk_78 + 251059050*uk_79 + 22*uk_8 + 24547996*uk_80 + 166375*uk_81 + 139150*uk_82 + 66550*uk_83 + 72600*uk_84 + 402325*uk_85 + 680625*uk_86 + 66550*uk_87 + 116380*uk_88 + 55660*uk_89 + 2572416961*uk_9 + 60720*uk_90 + 336490*uk_91 + 569250*uk_92 + 55660*uk_93 + 26620*uk_94 + 29040*uk_95 + 160930*uk_96 + 272250*uk_97 + 26620*uk_98 + 31680*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 178200*uk_100 + 297000*uk_101 + 60720*uk_102 + 1002375*uk_103 + 1670625*uk_104 + 341550*uk_105 + 2784375*uk_106 + 569250*uk_107 + 116380*uk_108 + 830584*uk_109 + 4767586*uk_11 + 406456*uk_110 + 212064*uk_111 + 1192860*uk_112 + 1988100*uk_113 + 406456*uk_114 + 198904*uk_115 + 103776*uk_116 + 583740*uk_117 + 972900*uk_118 + 198904*uk_119 + 2333074*uk_12 + 54144*uk_120 + 304560*uk_121 + 507600*uk_122 + 103776*uk_123 + 1713150*uk_124 + 2855250*uk_125 + 583740*uk_126 + 4758750*uk_127 + 972900*uk_128 + 198904*uk_129 + 1217256*uk_13 + 97336*uk_130 + 50784*uk_131 + 285660*uk_132 + 476100*uk_133 + 97336*uk_134 + 26496*uk_135 + 149040*uk_136 + 248400*uk_137 + 50784*uk_138 + 838350*uk_139 + 6847065*uk_14 + 1397250*uk_140 + 285660*uk_141 + 2328750*uk_142 + 476100*uk_143 + 97336*uk_144 + 13824*uk_145 + 77760*uk_146 + 129600*uk_147 + 26496*uk_148 + 437400*uk_149 + 11411775*uk_15 + 729000*uk_150 + 149040*uk_151 + 1215000*uk_152 + 248400*uk_153 + 50784*uk_154 + 2460375*uk_155 + 4100625*uk_156 + 838350*uk_157 + 6834375*uk_158 + 1397250*uk_159 + 2333074*uk_16 + 285660*uk_160 + 11390625*uk_161 + 2328750*uk_162 + 476100*uk_163 + 97336*uk_164 + 3025*uk_17 + 5170*uk_18 + 2530*uk_19 + 55*uk_2 + 1320*uk_20 + 7425*uk_21 + 12375*uk_22 + 2530*uk_23 + 8836*uk_24 + 4324*uk_25 + 2256*uk_26 + 12690*uk_27 + 21150*uk_28 + 4324*uk_29 + 94*uk_3 + 2116*uk_30 + 1104*uk_31 + 6210*uk_32 + 10350*uk_33 + 2116*uk_34 + 576*uk_35 + 3240*uk_36 + 5400*uk_37 + 1104*uk_38 + 18225*uk_39 + 46*uk_4 + 30375*uk_40 + 6210*uk_41 + 50625*uk_42 + 10350*uk_43 + 2116*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 241807194334*uk_47 + 118331180206*uk_48 + 61738007064*uk_49 + 24*uk_5 + 347276289735*uk_50 + 578793816225*uk_51 + 118331180206*uk_52 + 153424975*uk_53 + 262217230*uk_54 + 128319070*uk_55 + 66949080*uk_56 + 376588575*uk_57 + 627647625*uk_58 + 128319070*uk_59 + 135*uk_6 + 448153084*uk_60 + 219308956*uk_61 + 114422064*uk_62 + 643624110*uk_63 + 1072706850*uk_64 + 219308956*uk_65 + 107321404*uk_66 + 55993776*uk_67 + 314964990*uk_68 + 524941650*uk_69 + 225*uk_7 + 107321404*uk_70 + 29214144*uk_71 + 164329560*uk_72 + 273882600*uk_73 + 55993776*uk_74 + 924353775*uk_75 + 1540589625*uk_76 + 314964990*uk_77 + 2567649375*uk_78 + 524941650*uk_79 + 46*uk_8 + 107321404*uk_80 + 166375*uk_81 + 284350*uk_82 + 139150*uk_83 + 72600*uk_84 + 408375*uk_85 + 680625*uk_86 + 139150*uk_87 + 485980*uk_88 + 237820*uk_89 + 2572416961*uk_9 + 124080*uk_90 + 697950*uk_91 + 1163250*uk_92 + 237820*uk_93 + 116380*uk_94 + 60720*uk_95 + 341550*uk_96 + 569250*uk_97 + 116380*uk_98 + 31680*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 150700*uk_100 + 247500*uk_101 + 103400*uk_102 + 1032295*uk_103 + 1695375*uk_104 + 708290*uk_105 + 2784375*uk_106 + 1163250*uk_107 + 485980*uk_108 + 24389*uk_109 + 1470851*uk_11 + 79054*uk_110 + 16820*uk_111 + 115217*uk_112 + 189225*uk_113 + 79054*uk_114 + 256244*uk_115 + 54520*uk_116 + 373462*uk_117 + 613350*uk_118 + 256244*uk_119 + 4767586*uk_12 + 11600*uk_120 + 79460*uk_121 + 130500*uk_122 + 54520*uk_123 + 544301*uk_124 + 893925*uk_125 + 373462*uk_126 + 1468125*uk_127 + 613350*uk_128 + 256244*uk_129 + 1014380*uk_13 + 830584*uk_130 + 176720*uk_131 + 1210532*uk_132 + 1988100*uk_133 + 830584*uk_134 + 37600*uk_135 + 257560*uk_136 + 423000*uk_137 + 176720*uk_138 + 1764286*uk_139 + 6948503*uk_14 + 2897550*uk_140 + 1210532*uk_141 + 4758750*uk_142 + 1988100*uk_143 + 830584*uk_144 + 8000*uk_145 + 54800*uk_146 + 90000*uk_147 + 37600*uk_148 + 375380*uk_149 + 11411775*uk_15 + 616500*uk_150 + 257560*uk_151 + 1012500*uk_152 + 423000*uk_153 + 176720*uk_154 + 2571353*uk_155 + 4223025*uk_156 + 1764286*uk_157 + 6935625*uk_158 + 2897550*uk_159 + 4767586*uk_16 + 1210532*uk_160 + 11390625*uk_161 + 4758750*uk_162 + 1988100*uk_163 + 830584*uk_164 + 3025*uk_17 + 1595*uk_18 + 5170*uk_19 + 55*uk_2 + 1100*uk_20 + 7535*uk_21 + 12375*uk_22 + 5170*uk_23 + 841*uk_24 + 2726*uk_25 + 580*uk_26 + 3973*uk_27 + 6525*uk_28 + 2726*uk_29 + 29*uk_3 + 8836*uk_30 + 1880*uk_31 + 12878*uk_32 + 21150*uk_33 + 8836*uk_34 + 400*uk_35 + 2740*uk_36 + 4500*uk_37 + 1880*uk_38 + 18769*uk_39 + 94*uk_4 + 30825*uk_40 + 12878*uk_41 + 50625*uk_42 + 21150*uk_43 + 8836*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 74600091869*uk_47 + 241807194334*uk_48 + 51448339220*uk_49 + 20*uk_5 + 352421123657*uk_50 + 578793816225*uk_51 + 241807194334*uk_52 + 153424975*uk_53 + 80896805*uk_54 + 262217230*uk_55 + 55790900*uk_56 + 382167665*uk_57 + 627647625*uk_58 + 262217230*uk_59 + 137*uk_6 + 42654679*uk_60 + 138259994*uk_61 + 29417020*uk_62 + 201506587*uk_63 + 330941475*uk_64 + 138259994*uk_65 + 448153084*uk_66 + 95351720*uk_67 + 653159282*uk_68 + 1072706850*uk_69 + 225*uk_7 + 448153084*uk_70 + 20287600*uk_71 + 138970060*uk_72 + 228235500*uk_73 + 95351720*uk_74 + 951944911*uk_75 + 1563413175*uk_76 + 653159282*uk_77 + 2567649375*uk_78 + 1072706850*uk_79 + 94*uk_8 + 448153084*uk_80 + 166375*uk_81 + 87725*uk_82 + 284350*uk_83 + 60500*uk_84 + 414425*uk_85 + 680625*uk_86 + 284350*uk_87 + 46255*uk_88 + 149930*uk_89 + 2572416961*uk_9 + 31900*uk_90 + 218515*uk_91 + 358875*uk_92 + 149930*uk_93 + 485980*uk_94 + 103400*uk_95 + 708290*uk_96 + 1163250*uk_97 + 485980*uk_98 + 22000*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 183480*uk_100 + 297000*uk_101 + 38280*uk_102 + 1062655*uk_103 + 1720125*uk_104 + 221705*uk_105 + 2784375*uk_106 + 358875*uk_107 + 46255*uk_108 + 1860867*uk_109 + 6238437*uk_11 + 438741*uk_110 + 363096*uk_111 + 2102931*uk_112 + 3404025*uk_113 + 438741*uk_114 + 103443*uk_115 + 85608*uk_116 + 495813*uk_117 + 802575*uk_118 + 103443*uk_119 + 1470851*uk_12 + 70848*uk_120 + 410328*uk_121 + 664200*uk_122 + 85608*uk_123 + 2376483*uk_124 + 3846825*uk_125 + 495813*uk_126 + 6226875*uk_127 + 802575*uk_128 + 103443*uk_129 + 1217256*uk_13 + 24389*uk_130 + 20184*uk_131 + 116899*uk_132 + 189225*uk_133 + 24389*uk_134 + 16704*uk_135 + 96744*uk_136 + 156600*uk_137 + 20184*uk_138 + 560309*uk_139 + 7049941*uk_14 + 906975*uk_140 + 116899*uk_141 + 1468125*uk_142 + 189225*uk_143 + 24389*uk_144 + 13824*uk_145 + 80064*uk_146 + 129600*uk_147 + 16704*uk_148 + 463704*uk_149 + 11411775*uk_15 + 750600*uk_150 + 96744*uk_151 + 1215000*uk_152 + 156600*uk_153 + 20184*uk_154 + 2685619*uk_155 + 4347225*uk_156 + 560309*uk_157 + 7036875*uk_158 + 906975*uk_159 + 1470851*uk_16 + 116899*uk_160 + 11390625*uk_161 + 1468125*uk_162 + 189225*uk_163 + 24389*uk_164 + 3025*uk_17 + 6765*uk_18 + 1595*uk_19 + 55*uk_2 + 1320*uk_20 + 7645*uk_21 + 12375*uk_22 + 1595*uk_23 + 15129*uk_24 + 3567*uk_25 + 2952*uk_26 + 17097*uk_27 + 27675*uk_28 + 3567*uk_29 + 123*uk_3 + 841*uk_30 + 696*uk_31 + 4031*uk_32 + 6525*uk_33 + 841*uk_34 + 576*uk_35 + 3336*uk_36 + 5400*uk_37 + 696*uk_38 + 19321*uk_39 + 29*uk_4 + 31275*uk_40 + 4031*uk_41 + 50625*uk_42 + 6525*uk_43 + 841*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 316407286203*uk_47 + 74600091869*uk_48 + 61738007064*uk_49 + 24*uk_5 + 357565957579*uk_50 + 578793816225*uk_51 + 74600091869*uk_52 + 153424975*uk_53 + 343114035*uk_54 + 80896805*uk_55 + 66949080*uk_56 + 387746755*uk_57 + 627647625*uk_58 + 80896805*uk_59 + 139*uk_6 + 767327751*uk_60 + 180914673*uk_61 + 149722488*uk_62 + 867142743*uk_63 + 1403648325*uk_64 + 180914673*uk_65 + 42654679*uk_66 + 35300424*uk_67 + 204448289*uk_68 + 330941475*uk_69 + 225*uk_7 + 42654679*uk_70 + 29214144*uk_71 + 169198584*uk_72 + 273882600*uk_73 + 35300424*uk_74 + 979941799*uk_75 + 1586236725*uk_76 + 204448289*uk_77 + 2567649375*uk_78 + 330941475*uk_79 + 29*uk_8 + 42654679*uk_80 + 166375*uk_81 + 372075*uk_82 + 87725*uk_83 + 72600*uk_84 + 420475*uk_85 + 680625*uk_86 + 87725*uk_87 + 832095*uk_88 + 196185*uk_89 + 2572416961*uk_9 + 162360*uk_90 + 940335*uk_91 + 1522125*uk_92 + 196185*uk_93 + 46255*uk_94 + 38280*uk_95 + 221705*uk_96 + 358875*uk_97 + 46255*uk_98 + 31680*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 155100*uk_100 + 247500*uk_101 + 135300*uk_102 + 1093455*uk_103 + 1744875*uk_104 + 953865*uk_105 + 2784375*uk_106 + 1522125*uk_107 + 832095*uk_108 + 1000000*uk_109 + 5071900*uk_11 + 1230000*uk_110 + 200000*uk_111 + 1410000*uk_112 + 2250000*uk_113 + 1230000*uk_114 + 1512900*uk_115 + 246000*uk_116 + 1734300*uk_117 + 2767500*uk_118 + 1512900*uk_119 + 6238437*uk_12 + 40000*uk_120 + 282000*uk_121 + 450000*uk_122 + 246000*uk_123 + 1988100*uk_124 + 3172500*uk_125 + 1734300*uk_126 + 5062500*uk_127 + 2767500*uk_128 + 1512900*uk_129 + 1014380*uk_13 + 1860867*uk_130 + 302580*uk_131 + 2133189*uk_132 + 3404025*uk_133 + 1860867*uk_134 + 49200*uk_135 + 346860*uk_136 + 553500*uk_137 + 302580*uk_138 + 2445363*uk_139 + 7151379*uk_14 + 3902175*uk_140 + 2133189*uk_141 + 6226875*uk_142 + 3404025*uk_143 + 1860867*uk_144 + 8000*uk_145 + 56400*uk_146 + 90000*uk_147 + 49200*uk_148 + 397620*uk_149 + 11411775*uk_15 + 634500*uk_150 + 346860*uk_151 + 1012500*uk_152 + 553500*uk_153 + 302580*uk_154 + 2803221*uk_155 + 4473225*uk_156 + 2445363*uk_157 + 7138125*uk_158 + 3902175*uk_159 + 6238437*uk_16 + 2133189*uk_160 + 11390625*uk_161 + 6226875*uk_162 + 3404025*uk_163 + 1860867*uk_164 + 3025*uk_17 + 5500*uk_18 + 6765*uk_19 + 55*uk_2 + 1100*uk_20 + 7755*uk_21 + 12375*uk_22 + 6765*uk_23 + 10000*uk_24 + 12300*uk_25 + 2000*uk_26 + 14100*uk_27 + 22500*uk_28 + 12300*uk_29 + 100*uk_3 + 15129*uk_30 + 2460*uk_31 + 17343*uk_32 + 27675*uk_33 + 15129*uk_34 + 400*uk_35 + 2820*uk_36 + 4500*uk_37 + 2460*uk_38 + 19881*uk_39 + 123*uk_4 + 31725*uk_40 + 17343*uk_41 + 50625*uk_42 + 27675*uk_43 + 15129*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 257241696100*uk_47 + 316407286203*uk_48 + 51448339220*uk_49 + 20*uk_5 + 362710791501*uk_50 + 578793816225*uk_51 + 316407286203*uk_52 + 153424975*uk_53 + 278954500*uk_54 + 343114035*uk_55 + 55790900*uk_56 + 393325845*uk_57 + 627647625*uk_58 + 343114035*uk_59 + 141*uk_6 + 507190000*uk_60 + 623843700*uk_61 + 101438000*uk_62 + 715137900*uk_63 + 1141177500*uk_64 + 623843700*uk_65 + 767327751*uk_66 + 124768740*uk_67 + 879619617*uk_68 + 1403648325*uk_69 + 225*uk_7 + 767327751*uk_70 + 20287600*uk_71 + 143027580*uk_72 + 228235500*uk_73 + 124768740*uk_74 + 1008344439*uk_75 + 1609060275*uk_76 + 879619617*uk_77 + 2567649375*uk_78 + 1403648325*uk_79 + 123*uk_8 + 767327751*uk_80 + 166375*uk_81 + 302500*uk_82 + 372075*uk_83 + 60500*uk_84 + 426525*uk_85 + 680625*uk_86 + 372075*uk_87 + 550000*uk_88 + 676500*uk_89 + 2572416961*uk_9 + 110000*uk_90 + 775500*uk_91 + 1237500*uk_92 + 676500*uk_93 + 832095*uk_94 + 135300*uk_95 + 953865*uk_96 + 1522125*uk_97 + 832095*uk_98 + 22000*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 157300*uk_100 + 247500*uk_101 + 110000*uk_102 + 1124695*uk_103 + 1769625*uk_104 + 786500*uk_105 + 2784375*uk_106 + 1237500*uk_107 + 550000*uk_108 + 912673*uk_109 + 4919743*uk_11 + 940900*uk_110 + 188180*uk_111 + 1345487*uk_112 + 2117025*uk_113 + 940900*uk_114 + 970000*uk_115 + 194000*uk_116 + 1387100*uk_117 + 2182500*uk_118 + 970000*uk_119 + 5071900*uk_12 + 38800*uk_120 + 277420*uk_121 + 436500*uk_122 + 194000*uk_123 + 1983553*uk_124 + 3120975*uk_125 + 1387100*uk_126 + 4910625*uk_127 + 2182500*uk_128 + 970000*uk_129 + 1014380*uk_13 + 1000000*uk_130 + 200000*uk_131 + 1430000*uk_132 + 2250000*uk_133 + 1000000*uk_134 + 40000*uk_135 + 286000*uk_136 + 450000*uk_137 + 200000*uk_138 + 2044900*uk_139 + 7252817*uk_14 + 3217500*uk_140 + 1430000*uk_141 + 5062500*uk_142 + 2250000*uk_143 + 1000000*uk_144 + 8000*uk_145 + 57200*uk_146 + 90000*uk_147 + 40000*uk_148 + 408980*uk_149 + 11411775*uk_15 + 643500*uk_150 + 286000*uk_151 + 1012500*uk_152 + 450000*uk_153 + 200000*uk_154 + 2924207*uk_155 + 4601025*uk_156 + 2044900*uk_157 + 7239375*uk_158 + 3217500*uk_159 + 5071900*uk_16 + 1430000*uk_160 + 11390625*uk_161 + 5062500*uk_162 + 2250000*uk_163 + 1000000*uk_164 + 3025*uk_17 + 5335*uk_18 + 5500*uk_19 + 55*uk_2 + 1100*uk_20 + 7865*uk_21 + 12375*uk_22 + 5500*uk_23 + 9409*uk_24 + 9700*uk_25 + 1940*uk_26 + 13871*uk_27 + 21825*uk_28 + 9700*uk_29 + 97*uk_3 + 10000*uk_30 + 2000*uk_31 + 14300*uk_32 + 22500*uk_33 + 10000*uk_34 + 400*uk_35 + 2860*uk_36 + 4500*uk_37 + 2000*uk_38 + 20449*uk_39 + 100*uk_4 + 32175*uk_40 + 14300*uk_41 + 50625*uk_42 + 22500*uk_43 + 10000*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 249524445217*uk_47 + 257241696100*uk_48 + 51448339220*uk_49 + 20*uk_5 + 367855625423*uk_50 + 578793816225*uk_51 + 257241696100*uk_52 + 153424975*uk_53 + 270585865*uk_54 + 278954500*uk_55 + 55790900*uk_56 + 398904935*uk_57 + 627647625*uk_58 + 278954500*uk_59 + 143*uk_6 + 477215071*uk_60 + 491974300*uk_61 + 98394860*uk_62 + 703523249*uk_63 + 1106942175*uk_64 + 491974300*uk_65 + 507190000*uk_66 + 101438000*uk_67 + 725281700*uk_68 + 1141177500*uk_69 + 225*uk_7 + 507190000*uk_70 + 20287600*uk_71 + 145056340*uk_72 + 228235500*uk_73 + 101438000*uk_74 + 1037152831*uk_75 + 1631883825*uk_76 + 725281700*uk_77 + 2567649375*uk_78 + 1141177500*uk_79 + 100*uk_8 + 507190000*uk_80 + 166375*uk_81 + 293425*uk_82 + 302500*uk_83 + 60500*uk_84 + 432575*uk_85 + 680625*uk_86 + 302500*uk_87 + 517495*uk_88 + 533500*uk_89 + 2572416961*uk_9 + 106700*uk_90 + 762905*uk_91 + 1200375*uk_92 + 533500*uk_93 + 550000*uk_94 + 110000*uk_95 + 786500*uk_96 + 1237500*uk_97 + 550000*uk_98 + 22000*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 159500*uk_100 + 247500*uk_101 + 106700*uk_102 + 1156375*uk_103 + 1794375*uk_104 + 773575*uk_105 + 2784375*uk_106 + 1200375*uk_107 + 517495*uk_108 + 1481544*uk_109 + 5781966*uk_11 + 1260612*uk_110 + 259920*uk_111 + 1884420*uk_112 + 2924100*uk_113 + 1260612*uk_114 + 1072626*uk_115 + 221160*uk_116 + 1603410*uk_117 + 2488050*uk_118 + 1072626*uk_119 + 4919743*uk_12 + 45600*uk_120 + 330600*uk_121 + 513000*uk_122 + 221160*uk_123 + 2396850*uk_124 + 3719250*uk_125 + 1603410*uk_126 + 5771250*uk_127 + 2488050*uk_128 + 1072626*uk_129 + 1014380*uk_13 + 912673*uk_130 + 188180*uk_131 + 1364305*uk_132 + 2117025*uk_133 + 912673*uk_134 + 38800*uk_135 + 281300*uk_136 + 436500*uk_137 + 188180*uk_138 + 2039425*uk_139 + 7354255*uk_14 + 3164625*uk_140 + 1364305*uk_141 + 4910625*uk_142 + 2117025*uk_143 + 912673*uk_144 + 8000*uk_145 + 58000*uk_146 + 90000*uk_147 + 38800*uk_148 + 420500*uk_149 + 11411775*uk_15 + 652500*uk_150 + 281300*uk_151 + 1012500*uk_152 + 436500*uk_153 + 188180*uk_154 + 3048625*uk_155 + 4730625*uk_156 + 2039425*uk_157 + 7340625*uk_158 + 3164625*uk_159 + 4919743*uk_16 + 1364305*uk_160 + 11390625*uk_161 + 4910625*uk_162 + 2117025*uk_163 + 912673*uk_164 + 3025*uk_17 + 6270*uk_18 + 5335*uk_19 + 55*uk_2 + 1100*uk_20 + 7975*uk_21 + 12375*uk_22 + 5335*uk_23 + 12996*uk_24 + 11058*uk_25 + 2280*uk_26 + 16530*uk_27 + 25650*uk_28 + 11058*uk_29 + 114*uk_3 + 9409*uk_30 + 1940*uk_31 + 14065*uk_32 + 21825*uk_33 + 9409*uk_34 + 400*uk_35 + 2900*uk_36 + 4500*uk_37 + 1940*uk_38 + 21025*uk_39 + 97*uk_4 + 32625*uk_40 + 14065*uk_41 + 50625*uk_42 + 21825*uk_43 + 9409*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 293255533554*uk_47 + 249524445217*uk_48 + 51448339220*uk_49 + 20*uk_5 + 373000459345*uk_50 + 578793816225*uk_51 + 249524445217*uk_52 + 153424975*uk_53 + 318008130*uk_54 + 270585865*uk_55 + 55790900*uk_56 + 404484025*uk_57 + 627647625*uk_58 + 270585865*uk_59 + 145*uk_6 + 659144124*uk_60 + 560850702*uk_61 + 115639320*uk_62 + 838385070*uk_63 + 1300942350*uk_64 + 560850702*uk_65 + 477215071*uk_66 + 98394860*uk_67 + 713362735*uk_68 + 1106942175*uk_69 + 225*uk_7 + 477215071*uk_70 + 20287600*uk_71 + 147085100*uk_72 + 228235500*uk_73 + 98394860*uk_74 + 1066366975*uk_75 + 1654707375*uk_76 + 713362735*uk_77 + 2567649375*uk_78 + 1106942175*uk_79 + 97*uk_8 + 477215071*uk_80 + 166375*uk_81 + 344850*uk_82 + 293425*uk_83 + 60500*uk_84 + 438625*uk_85 + 680625*uk_86 + 293425*uk_87 + 714780*uk_88 + 608190*uk_89 + 2572416961*uk_9 + 125400*uk_90 + 909150*uk_91 + 1410750*uk_92 + 608190*uk_93 + 517495*uk_94 + 106700*uk_95 + 773575*uk_96 + 1200375*uk_97 + 517495*uk_98 + 22000*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 129360*uk_100 + 198000*uk_101 + 100320*uk_102 + 1188495*uk_103 + 1819125*uk_104 + 921690*uk_105 + 2784375*uk_106 + 1410750*uk_107 + 714780*uk_108 + 64*uk_109 + 202876*uk_11 + 1824*uk_110 + 256*uk_111 + 2352*uk_112 + 3600*uk_113 + 1824*uk_114 + 51984*uk_115 + 7296*uk_116 + 67032*uk_117 + 102600*uk_118 + 51984*uk_119 + 5781966*uk_12 + 1024*uk_120 + 9408*uk_121 + 14400*uk_122 + 7296*uk_123 + 86436*uk_124 + 132300*uk_125 + 67032*uk_126 + 202500*uk_127 + 102600*uk_128 + 51984*uk_129 + 811504*uk_13 + 1481544*uk_130 + 207936*uk_131 + 1910412*uk_132 + 2924100*uk_133 + 1481544*uk_134 + 29184*uk_135 + 268128*uk_136 + 410400*uk_137 + 207936*uk_138 + 2463426*uk_139 + 7455693*uk_14 + 3770550*uk_140 + 1910412*uk_141 + 5771250*uk_142 + 2924100*uk_143 + 1481544*uk_144 + 4096*uk_145 + 37632*uk_146 + 57600*uk_147 + 29184*uk_148 + 345744*uk_149 + 11411775*uk_15 + 529200*uk_150 + 268128*uk_151 + 810000*uk_152 + 410400*uk_153 + 207936*uk_154 + 3176523*uk_155 + 4862025*uk_156 + 2463426*uk_157 + 7441875*uk_158 + 3770550*uk_159 + 5781966*uk_16 + 1910412*uk_160 + 11390625*uk_161 + 5771250*uk_162 + 2924100*uk_163 + 1481544*uk_164 + 3025*uk_17 + 220*uk_18 + 6270*uk_19 + 55*uk_2 + 880*uk_20 + 8085*uk_21 + 12375*uk_22 + 6270*uk_23 + 16*uk_24 + 456*uk_25 + 64*uk_26 + 588*uk_27 + 900*uk_28 + 456*uk_29 + 4*uk_3 + 12996*uk_30 + 1824*uk_31 + 16758*uk_32 + 25650*uk_33 + 12996*uk_34 + 256*uk_35 + 2352*uk_36 + 3600*uk_37 + 1824*uk_38 + 21609*uk_39 + 114*uk_4 + 33075*uk_40 + 16758*uk_41 + 50625*uk_42 + 25650*uk_43 + 12996*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 10289667844*uk_47 + 293255533554*uk_48 + 41158671376*uk_49 + 16*uk_5 + 378145293267*uk_50 + 578793816225*uk_51 + 293255533554*uk_52 + 153424975*uk_53 + 11158180*uk_54 + 318008130*uk_55 + 44632720*uk_56 + 410063115*uk_57 + 627647625*uk_58 + 318008130*uk_59 + 147*uk_6 + 811504*uk_60 + 23127864*uk_61 + 3246016*uk_62 + 29822772*uk_63 + 45647100*uk_64 + 23127864*uk_65 + 659144124*uk_66 + 92511456*uk_67 + 849949002*uk_68 + 1300942350*uk_69 + 225*uk_7 + 659144124*uk_70 + 12984064*uk_71 + 119291088*uk_72 + 182588400*uk_73 + 92511456*uk_74 + 1095986871*uk_75 + 1677530925*uk_76 + 849949002*uk_77 + 2567649375*uk_78 + 1300942350*uk_79 + 114*uk_8 + 659144124*uk_80 + 166375*uk_81 + 12100*uk_82 + 344850*uk_83 + 48400*uk_84 + 444675*uk_85 + 680625*uk_86 + 344850*uk_87 + 880*uk_88 + 25080*uk_89 + 2572416961*uk_9 + 3520*uk_90 + 32340*uk_91 + 49500*uk_92 + 25080*uk_93 + 714780*uk_94 + 100320*uk_95 + 921690*uk_96 + 1410750*uk_97 + 714780*uk_98 + 14080*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 163900*uk_100 + 247500*uk_101 + 4400*uk_102 + 1221055*uk_103 + 1843875*uk_104 + 32780*uk_105 + 2784375*uk_106 + 49500*uk_107 + 880*uk_108 + 205379*uk_109 + 2992421*uk_11 + 13924*uk_110 + 69620*uk_111 + 518669*uk_112 + 783225*uk_113 + 13924*uk_114 + 944*uk_115 + 4720*uk_116 + 35164*uk_117 + 53100*uk_118 + 944*uk_119 + 202876*uk_12 + 23600*uk_120 + 175820*uk_121 + 265500*uk_122 + 4720*uk_123 + 1309859*uk_124 + 1977975*uk_125 + 35164*uk_126 + 2986875*uk_127 + 53100*uk_128 + 944*uk_129 + 1014380*uk_13 + 64*uk_130 + 320*uk_131 + 2384*uk_132 + 3600*uk_133 + 64*uk_134 + 1600*uk_135 + 11920*uk_136 + 18000*uk_137 + 320*uk_138 + 88804*uk_139 + 7557131*uk_14 + 134100*uk_140 + 2384*uk_141 + 202500*uk_142 + 3600*uk_143 + 64*uk_144 + 8000*uk_145 + 59600*uk_146 + 90000*uk_147 + 1600*uk_148 + 444020*uk_149 + 11411775*uk_15 + 670500*uk_150 + 11920*uk_151 + 1012500*uk_152 + 18000*uk_153 + 320*uk_154 + 3307949*uk_155 + 4995225*uk_156 + 88804*uk_157 + 7543125*uk_158 + 134100*uk_159 + 202876*uk_16 + 2384*uk_160 + 11390625*uk_161 + 202500*uk_162 + 3600*uk_163 + 64*uk_164 + 3025*uk_17 + 3245*uk_18 + 220*uk_19 + 55*uk_2 + 1100*uk_20 + 8195*uk_21 + 12375*uk_22 + 220*uk_23 + 3481*uk_24 + 236*uk_25 + 1180*uk_26 + 8791*uk_27 + 13275*uk_28 + 236*uk_29 + 59*uk_3 + 16*uk_30 + 80*uk_31 + 596*uk_32 + 900*uk_33 + 16*uk_34 + 400*uk_35 + 2980*uk_36 + 4500*uk_37 + 80*uk_38 + 22201*uk_39 + 4*uk_4 + 33525*uk_40 + 596*uk_41 + 50625*uk_42 + 900*uk_43 + 16*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 151772600699*uk_47 + 10289667844*uk_48 + 51448339220*uk_49 + 20*uk_5 + 383290127189*uk_50 + 578793816225*uk_51 + 10289667844*uk_52 + 153424975*uk_53 + 164583155*uk_54 + 11158180*uk_55 + 55790900*uk_56 + 415642205*uk_57 + 627647625*uk_58 + 11158180*uk_59 + 149*uk_6 + 176552839*uk_60 + 11969684*uk_61 + 59848420*uk_62 + 445870729*uk_63 + 673294725*uk_64 + 11969684*uk_65 + 811504*uk_66 + 4057520*uk_67 + 30228524*uk_68 + 45647100*uk_69 + 225*uk_7 + 811504*uk_70 + 20287600*uk_71 + 151142620*uk_72 + 228235500*uk_73 + 4057520*uk_74 + 1126012519*uk_75 + 1700354475*uk_76 + 30228524*uk_77 + 2567649375*uk_78 + 45647100*uk_79 + 4*uk_8 + 811504*uk_80 + 166375*uk_81 + 178475*uk_82 + 12100*uk_83 + 60500*uk_84 + 450725*uk_85 + 680625*uk_86 + 12100*uk_87 + 191455*uk_88 + 12980*uk_89 + 2572416961*uk_9 + 64900*uk_90 + 483505*uk_91 + 730125*uk_92 + 12980*uk_93 + 880*uk_94 + 4400*uk_95 + 32780*uk_96 + 49500*uk_97 + 880*uk_98 + 22000*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 166100*uk_100 + 247500*uk_101 + 64900*uk_102 + 1254055*uk_103 + 1868625*uk_104 + 489995*uk_105 + 2784375*uk_106 + 730125*uk_107 + 191455*uk_108 + 2406104*uk_109 + 6796346*uk_11 + 1059404*uk_110 + 359120*uk_111 + 2711356*uk_112 + 4040100*uk_113 + 1059404*uk_114 + 466454*uk_115 + 158120*uk_116 + 1193806*uk_117 + 1778850*uk_118 + 466454*uk_119 + 2992421*uk_12 + 53600*uk_120 + 404680*uk_121 + 603000*uk_122 + 158120*uk_123 + 3055334*uk_124 + 4552650*uk_125 + 1193806*uk_126 + 6783750*uk_127 + 1778850*uk_128 + 466454*uk_129 + 1014380*uk_13 + 205379*uk_130 + 69620*uk_131 + 525631*uk_132 + 783225*uk_133 + 205379*uk_134 + 23600*uk_135 + 178180*uk_136 + 265500*uk_137 + 69620*uk_138 + 1345259*uk_139 + 7658569*uk_14 + 2004525*uk_140 + 525631*uk_141 + 2986875*uk_142 + 783225*uk_143 + 205379*uk_144 + 8000*uk_145 + 60400*uk_146 + 90000*uk_147 + 23600*uk_148 + 456020*uk_149 + 11411775*uk_15 + 679500*uk_150 + 178180*uk_151 + 1012500*uk_152 + 265500*uk_153 + 69620*uk_154 + 3442951*uk_155 + 5130225*uk_156 + 1345259*uk_157 + 7644375*uk_158 + 2004525*uk_159 + 2992421*uk_16 + 525631*uk_160 + 11390625*uk_161 + 2986875*uk_162 + 783225*uk_163 + 205379*uk_164 + 3025*uk_17 + 7370*uk_18 + 3245*uk_19 + 55*uk_2 + 1100*uk_20 + 8305*uk_21 + 12375*uk_22 + 3245*uk_23 + 17956*uk_24 + 7906*uk_25 + 2680*uk_26 + 20234*uk_27 + 30150*uk_28 + 7906*uk_29 + 134*uk_3 + 3481*uk_30 + 1180*uk_31 + 8909*uk_32 + 13275*uk_33 + 3481*uk_34 + 400*uk_35 + 3020*uk_36 + 4500*uk_37 + 1180*uk_38 + 22801*uk_39 + 59*uk_4 + 33975*uk_40 + 8909*uk_41 + 50625*uk_42 + 13275*uk_43 + 3481*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 344703872774*uk_47 + 151772600699*uk_48 + 51448339220*uk_49 + 20*uk_5 + 388434961111*uk_50 + 578793816225*uk_51 + 151772600699*uk_52 + 153424975*uk_53 + 373799030*uk_54 + 164583155*uk_55 + 55790900*uk_56 + 421221295*uk_57 + 627647625*uk_58 + 164583155*uk_59 + 151*uk_6 + 910710364*uk_60 + 400984414*uk_61 + 135926920*uk_62 + 1026248246*uk_63 + 1529177850*uk_64 + 400984414*uk_65 + 176552839*uk_66 + 59848420*uk_67 + 451855571*uk_68 + 673294725*uk_69 + 225*uk_7 + 176552839*uk_70 + 20287600*uk_71 + 153171380*uk_72 + 228235500*uk_73 + 59848420*uk_74 + 1156443919*uk_75 + 1723178025*uk_76 + 451855571*uk_77 + 2567649375*uk_78 + 673294725*uk_79 + 59*uk_8 + 176552839*uk_80 + 166375*uk_81 + 405350*uk_82 + 178475*uk_83 + 60500*uk_84 + 456775*uk_85 + 680625*uk_86 + 178475*uk_87 + 987580*uk_88 + 434830*uk_89 + 2572416961*uk_9 + 147400*uk_90 + 1112870*uk_91 + 1658250*uk_92 + 434830*uk_93 + 191455*uk_94 + 64900*uk_95 + 489995*uk_96 + 730125*uk_97 + 191455*uk_98 + 22000*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 134640*uk_100 + 198000*uk_101 + 117920*uk_102 + 1287495*uk_103 + 1893375*uk_104 + 1127610*uk_105 + 2784375*uk_106 + 1658250*uk_107 + 987580*uk_108 + 438976*uk_109 + 3854644*uk_11 + 773984*uk_110 + 92416*uk_111 + 883728*uk_112 + 1299600*uk_113 + 773984*uk_114 + 1364656*uk_115 + 162944*uk_116 + 1558152*uk_117 + 2291400*uk_118 + 1364656*uk_119 + 6796346*uk_12 + 19456*uk_120 + 186048*uk_121 + 273600*uk_122 + 162944*uk_123 + 1779084*uk_124 + 2616300*uk_125 + 1558152*uk_126 + 3847500*uk_127 + 2291400*uk_128 + 1364656*uk_129 + 811504*uk_13 + 2406104*uk_130 + 287296*uk_131 + 2747268*uk_132 + 4040100*uk_133 + 2406104*uk_134 + 34304*uk_135 + 328032*uk_136 + 482400*uk_137 + 287296*uk_138 + 3136806*uk_139 + 7760007*uk_14 + 4612950*uk_140 + 2747268*uk_141 + 6783750*uk_142 + 4040100*uk_143 + 2406104*uk_144 + 4096*uk_145 + 39168*uk_146 + 57600*uk_147 + 34304*uk_148 + 374544*uk_149 + 11411775*uk_15 + 550800*uk_150 + 328032*uk_151 + 810000*uk_152 + 482400*uk_153 + 287296*uk_154 + 3581577*uk_155 + 5267025*uk_156 + 3136806*uk_157 + 7745625*uk_158 + 4612950*uk_159 + 6796346*uk_16 + 2747268*uk_160 + 11390625*uk_161 + 6783750*uk_162 + 4040100*uk_163 + 2406104*uk_164 + 3025*uk_17 + 4180*uk_18 + 7370*uk_19 + 55*uk_2 + 880*uk_20 + 8415*uk_21 + 12375*uk_22 + 7370*uk_23 + 5776*uk_24 + 10184*uk_25 + 1216*uk_26 + 11628*uk_27 + 17100*uk_28 + 10184*uk_29 + 76*uk_3 + 17956*uk_30 + 2144*uk_31 + 20502*uk_32 + 30150*uk_33 + 17956*uk_34 + 256*uk_35 + 2448*uk_36 + 3600*uk_37 + 2144*uk_38 + 23409*uk_39 + 134*uk_4 + 34425*uk_40 + 20502*uk_41 + 50625*uk_42 + 30150*uk_43 + 17956*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 195503689036*uk_47 + 344703872774*uk_48 + 41158671376*uk_49 + 16*uk_5 + 393579795033*uk_50 + 578793816225*uk_51 + 344703872774*uk_52 + 153424975*uk_53 + 212005420*uk_54 + 373799030*uk_55 + 44632720*uk_56 + 426800385*uk_57 + 627647625*uk_58 + 373799030*uk_59 + 153*uk_6 + 292952944*uk_60 + 516522296*uk_61 + 61674304*uk_62 + 589760532*uk_63 + 867294900*uk_64 + 516522296*uk_65 + 910710364*uk_66 + 108741536*uk_67 + 1039840938*uk_68 + 1529177850*uk_69 + 225*uk_7 + 910710364*uk_70 + 12984064*uk_71 + 124160112*uk_72 + 182588400*uk_73 + 108741536*uk_74 + 1187281071*uk_75 + 1746001575*uk_76 + 1039840938*uk_77 + 2567649375*uk_78 + 1529177850*uk_79 + 134*uk_8 + 910710364*uk_80 + 166375*uk_81 + 229900*uk_82 + 405350*uk_83 + 48400*uk_84 + 462825*uk_85 + 680625*uk_86 + 405350*uk_87 + 317680*uk_88 + 560120*uk_89 + 2572416961*uk_9 + 66880*uk_90 + 639540*uk_91 + 940500*uk_92 + 560120*uk_93 + 987580*uk_94 + 117920*uk_95 + 1127610*uk_96 + 1658250*uk_97 + 987580*uk_98 + 14080*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 136400*uk_100 + 198000*uk_101 + 66880*uk_102 + 1321375*uk_103 + 1918125*uk_104 + 647900*uk_105 + 2784375*uk_106 + 940500*uk_107 + 317680*uk_108 + 39304*uk_109 + 1724446*uk_11 + 87856*uk_110 + 18496*uk_111 + 179180*uk_112 + 260100*uk_113 + 87856*uk_114 + 196384*uk_115 + 41344*uk_116 + 400520*uk_117 + 581400*uk_118 + 196384*uk_119 + 3854644*uk_12 + 8704*uk_120 + 84320*uk_121 + 122400*uk_122 + 41344*uk_123 + 816850*uk_124 + 1185750*uk_125 + 400520*uk_126 + 1721250*uk_127 + 581400*uk_128 + 196384*uk_129 + 811504*uk_13 + 438976*uk_130 + 92416*uk_131 + 895280*uk_132 + 1299600*uk_133 + 438976*uk_134 + 19456*uk_135 + 188480*uk_136 + 273600*uk_137 + 92416*uk_138 + 1825900*uk_139 + 7861445*uk_14 + 2650500*uk_140 + 895280*uk_141 + 3847500*uk_142 + 1299600*uk_143 + 438976*uk_144 + 4096*uk_145 + 39680*uk_146 + 57600*uk_147 + 19456*uk_148 + 384400*uk_149 + 11411775*uk_15 + 558000*uk_150 + 188480*uk_151 + 810000*uk_152 + 273600*uk_153 + 92416*uk_154 + 3723875*uk_155 + 5405625*uk_156 + 1825900*uk_157 + 7846875*uk_158 + 2650500*uk_159 + 3854644*uk_16 + 895280*uk_160 + 11390625*uk_161 + 3847500*uk_162 + 1299600*uk_163 + 438976*uk_164 + 3025*uk_17 + 1870*uk_18 + 4180*uk_19 + 55*uk_2 + 880*uk_20 + 8525*uk_21 + 12375*uk_22 + 4180*uk_23 + 1156*uk_24 + 2584*uk_25 + 544*uk_26 + 5270*uk_27 + 7650*uk_28 + 2584*uk_29 + 34*uk_3 + 5776*uk_30 + 1216*uk_31 + 11780*uk_32 + 17100*uk_33 + 5776*uk_34 + 256*uk_35 + 2480*uk_36 + 3600*uk_37 + 1216*uk_38 + 24025*uk_39 + 76*uk_4 + 34875*uk_40 + 11780*uk_41 + 50625*uk_42 + 17100*uk_43 + 5776*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 87462176674*uk_47 + 195503689036*uk_48 + 41158671376*uk_49 + 16*uk_5 + 398724628955*uk_50 + 578793816225*uk_51 + 195503689036*uk_52 + 153424975*uk_53 + 94844530*uk_54 + 212005420*uk_55 + 44632720*uk_56 + 432379475*uk_57 + 627647625*uk_58 + 212005420*uk_59 + 155*uk_6 + 58631164*uk_60 + 131057896*uk_61 + 27591136*uk_62 + 267289130*uk_63 + 388000350*uk_64 + 131057896*uk_65 + 292952944*uk_66 + 61674304*uk_67 + 597469820*uk_68 + 867294900*uk_69 + 225*uk_7 + 292952944*uk_70 + 12984064*uk_71 + 125783120*uk_72 + 182588400*uk_73 + 61674304*uk_74 + 1218523975*uk_75 + 1768825125*uk_76 + 597469820*uk_77 + 2567649375*uk_78 + 867294900*uk_79 + 76*uk_8 + 292952944*uk_80 + 166375*uk_81 + 102850*uk_82 + 229900*uk_83 + 48400*uk_84 + 468875*uk_85 + 680625*uk_86 + 229900*uk_87 + 63580*uk_88 + 142120*uk_89 + 2572416961*uk_9 + 29920*uk_90 + 289850*uk_91 + 420750*uk_92 + 142120*uk_93 + 317680*uk_94 + 66880*uk_95 + 647900*uk_96 + 940500*uk_97 + 317680*uk_98 + 14080*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 138160*uk_100 + 198000*uk_101 + 29920*uk_102 + 1355695*uk_103 + 1942875*uk_104 + 293590*uk_105 + 2784375*uk_106 + 420750*uk_107 + 63580*uk_108 + 512*uk_109 + 405752*uk_11 + 2176*uk_110 + 1024*uk_111 + 10048*uk_112 + 14400*uk_113 + 2176*uk_114 + 9248*uk_115 + 4352*uk_116 + 42704*uk_117 + 61200*uk_118 + 9248*uk_119 + 1724446*uk_12 + 2048*uk_120 + 20096*uk_121 + 28800*uk_122 + 4352*uk_123 + 197192*uk_124 + 282600*uk_125 + 42704*uk_126 + 405000*uk_127 + 61200*uk_128 + 9248*uk_129 + 811504*uk_13 + 39304*uk_130 + 18496*uk_131 + 181492*uk_132 + 260100*uk_133 + 39304*uk_134 + 8704*uk_135 + 85408*uk_136 + 122400*uk_137 + 18496*uk_138 + 838066*uk_139 + 7962883*uk_14 + 1201050*uk_140 + 181492*uk_141 + 1721250*uk_142 + 260100*uk_143 + 39304*uk_144 + 4096*uk_145 + 40192*uk_146 + 57600*uk_147 + 8704*uk_148 + 394384*uk_149 + 11411775*uk_15 + 565200*uk_150 + 85408*uk_151 + 810000*uk_152 + 122400*uk_153 + 18496*uk_154 + 3869893*uk_155 + 5546025*uk_156 + 838066*uk_157 + 7948125*uk_158 + 1201050*uk_159 + 1724446*uk_16 + 181492*uk_160 + 11390625*uk_161 + 1721250*uk_162 + 260100*uk_163 + 39304*uk_164 + 3025*uk_17 + 440*uk_18 + 1870*uk_19 + 55*uk_2 + 880*uk_20 + 8635*uk_21 + 12375*uk_22 + 1870*uk_23 + 64*uk_24 + 272*uk_25 + 128*uk_26 + 1256*uk_27 + 1800*uk_28 + 272*uk_29 + 8*uk_3 + 1156*uk_30 + 544*uk_31 + 5338*uk_32 + 7650*uk_33 + 1156*uk_34 + 256*uk_35 + 2512*uk_36 + 3600*uk_37 + 544*uk_38 + 24649*uk_39 + 34*uk_4 + 35325*uk_40 + 5338*uk_41 + 50625*uk_42 + 7650*uk_43 + 1156*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 20579335688*uk_47 + 87462176674*uk_48 + 41158671376*uk_49 + 16*uk_5 + 403869462877*uk_50 + 578793816225*uk_51 + 87462176674*uk_52 + 153424975*uk_53 + 22316360*uk_54 + 94844530*uk_55 + 44632720*uk_56 + 437958565*uk_57 + 627647625*uk_58 + 94844530*uk_59 + 157*uk_6 + 3246016*uk_60 + 13795568*uk_61 + 6492032*uk_62 + 63703064*uk_63 + 91294200*uk_64 + 13795568*uk_65 + 58631164*uk_66 + 27591136*uk_67 + 270738022*uk_68 + 388000350*uk_69 + 225*uk_7 + 58631164*uk_70 + 12984064*uk_71 + 127406128*uk_72 + 182588400*uk_73 + 27591136*uk_74 + 1250172631*uk_75 + 1791648675*uk_76 + 270738022*uk_77 + 2567649375*uk_78 + 388000350*uk_79 + 34*uk_8 + 58631164*uk_80 + 166375*uk_81 + 24200*uk_82 + 102850*uk_83 + 48400*uk_84 + 474925*uk_85 + 680625*uk_86 + 102850*uk_87 + 3520*uk_88 + 14960*uk_89 + 2572416961*uk_9 + 7040*uk_90 + 69080*uk_91 + 99000*uk_92 + 14960*uk_93 + 63580*uk_94 + 29920*uk_95 + 293590*uk_96 + 420750*uk_97 + 63580*uk_98 + 14080*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 174900*uk_100 + 247500*uk_101 + 8800*uk_102 + 1390455*uk_103 + 1967625*uk_104 + 69960*uk_105 + 2784375*uk_106 + 99000*uk_107 + 3520*uk_108 + 3869893*uk_109 + 7962883*uk_11 + 197192*uk_110 + 492980*uk_111 + 3919191*uk_112 + 5546025*uk_113 + 197192*uk_114 + 10048*uk_115 + 25120*uk_116 + 199704*uk_117 + 282600*uk_118 + 10048*uk_119 + 405752*uk_12 + 62800*uk_120 + 499260*uk_121 + 706500*uk_122 + 25120*uk_123 + 3969117*uk_124 + 5616675*uk_125 + 199704*uk_126 + 7948125*uk_127 + 282600*uk_128 + 10048*uk_129 + 1014380*uk_13 + 512*uk_130 + 1280*uk_131 + 10176*uk_132 + 14400*uk_133 + 512*uk_134 + 3200*uk_135 + 25440*uk_136 + 36000*uk_137 + 1280*uk_138 + 202248*uk_139 + 8064321*uk_14 + 286200*uk_140 + 10176*uk_141 + 405000*uk_142 + 14400*uk_143 + 512*uk_144 + 8000*uk_145 + 63600*uk_146 + 90000*uk_147 + 3200*uk_148 + 505620*uk_149 + 11411775*uk_15 + 715500*uk_150 + 25440*uk_151 + 1012500*uk_152 + 36000*uk_153 + 1280*uk_154 + 4019679*uk_155 + 5688225*uk_156 + 202248*uk_157 + 8049375*uk_158 + 286200*uk_159 + 405752*uk_16 + 10176*uk_160 + 11390625*uk_161 + 405000*uk_162 + 14400*uk_163 + 512*uk_164 + 3025*uk_17 + 8635*uk_18 + 440*uk_19 + 55*uk_2 + 1100*uk_20 + 8745*uk_21 + 12375*uk_22 + 440*uk_23 + 24649*uk_24 + 1256*uk_25 + 3140*uk_26 + 24963*uk_27 + 35325*uk_28 + 1256*uk_29 + 157*uk_3 + 64*uk_30 + 160*uk_31 + 1272*uk_32 + 1800*uk_33 + 64*uk_34 + 400*uk_35 + 3180*uk_36 + 4500*uk_37 + 160*uk_38 + 25281*uk_39 + 8*uk_4 + 35775*uk_40 + 1272*uk_41 + 50625*uk_42 + 1800*uk_43 + 64*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 403869462877*uk_47 + 20579335688*uk_48 + 51448339220*uk_49 + 20*uk_5 + 409014296799*uk_50 + 578793816225*uk_51 + 20579335688*uk_52 + 153424975*uk_53 + 437958565*uk_54 + 22316360*uk_55 + 55790900*uk_56 + 443537655*uk_57 + 627647625*uk_58 + 22316360*uk_59 + 159*uk_6 + 1250172631*uk_60 + 63703064*uk_61 + 159257660*uk_62 + 1266098397*uk_63 + 1791648675*uk_64 + 63703064*uk_65 + 3246016*uk_66 + 8115040*uk_67 + 64514568*uk_68 + 91294200*uk_69 + 225*uk_7 + 3246016*uk_70 + 20287600*uk_71 + 161286420*uk_72 + 228235500*uk_73 + 8115040*uk_74 + 1282227039*uk_75 + 1814472225*uk_76 + 64514568*uk_77 + 2567649375*uk_78 + 91294200*uk_79 + 8*uk_8 + 3246016*uk_80 + 166375*uk_81 + 474925*uk_82 + 24200*uk_83 + 60500*uk_84 + 480975*uk_85 + 680625*uk_86 + 24200*uk_87 + 1355695*uk_88 + 69080*uk_89 + 2572416961*uk_9 + 172700*uk_90 + 1372965*uk_91 + 1942875*uk_92 + 69080*uk_93 + 3520*uk_94 + 8800*uk_95 + 69960*uk_96 + 99000*uk_97 + 3520*uk_98 + 22000*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 106260*uk_100 + 148500*uk_101 + 103620*uk_102 + 1425655*uk_103 + 1992375*uk_104 + 1390235*uk_105 + 2784375*uk_106 + 1942875*uk_107 + 1355695*uk_108 + 64*uk_109 + 202876*uk_11 + 2512*uk_110 + 192*uk_111 + 2576*uk_112 + 3600*uk_113 + 2512*uk_114 + 98596*uk_115 + 7536*uk_116 + 101108*uk_117 + 141300*uk_118 + 98596*uk_119 + 7962883*uk_12 + 576*uk_120 + 7728*uk_121 + 10800*uk_122 + 7536*uk_123 + 103684*uk_124 + 144900*uk_125 + 101108*uk_126 + 202500*uk_127 + 141300*uk_128 + 98596*uk_129 + 608628*uk_13 + 3869893*uk_130 + 295788*uk_131 + 3968489*uk_132 + 5546025*uk_133 + 3869893*uk_134 + 22608*uk_135 + 303324*uk_136 + 423900*uk_137 + 295788*uk_138 + 4069597*uk_139 + 8165759*uk_14 + 5687325*uk_140 + 3968489*uk_141 + 7948125*uk_142 + 5546025*uk_143 + 3869893*uk_144 + 1728*uk_145 + 23184*uk_146 + 32400*uk_147 + 22608*uk_148 + 311052*uk_149 + 11411775*uk_15 + 434700*uk_150 + 303324*uk_151 + 607500*uk_152 + 423900*uk_153 + 295788*uk_154 + 4173281*uk_155 + 5832225*uk_156 + 4069597*uk_157 + 8150625*uk_158 + 5687325*uk_159 + 7962883*uk_16 + 3968489*uk_160 + 11390625*uk_161 + 7948125*uk_162 + 5546025*uk_163 + 3869893*uk_164 + 3025*uk_17 + 220*uk_18 + 8635*uk_19 + 55*uk_2 + 660*uk_20 + 8855*uk_21 + 12375*uk_22 + 8635*uk_23 + 16*uk_24 + 628*uk_25 + 48*uk_26 + 644*uk_27 + 900*uk_28 + 628*uk_29 + 4*uk_3 + 24649*uk_30 + 1884*uk_31 + 25277*uk_32 + 35325*uk_33 + 24649*uk_34 + 144*uk_35 + 1932*uk_36 + 2700*uk_37 + 1884*uk_38 + 25921*uk_39 + 157*uk_4 + 36225*uk_40 + 25277*uk_41 + 50625*uk_42 + 35325*uk_43 + 24649*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 10289667844*uk_47 + 403869462877*uk_48 + 30869003532*uk_49 + 12*uk_5 + 414159130721*uk_50 + 578793816225*uk_51 + 403869462877*uk_52 + 153424975*uk_53 + 11158180*uk_54 + 437958565*uk_55 + 33474540*uk_56 + 449116745*uk_57 + 627647625*uk_58 + 437958565*uk_59 + 161*uk_6 + 811504*uk_60 + 31851532*uk_61 + 2434512*uk_62 + 32663036*uk_63 + 45647100*uk_64 + 31851532*uk_65 + 1250172631*uk_66 + 95554596*uk_67 + 1282024163*uk_68 + 1791648675*uk_69 + 225*uk_7 + 1250172631*uk_70 + 7303536*uk_71 + 97989108*uk_72 + 136941300*uk_73 + 95554596*uk_74 + 1314687199*uk_75 + 1837295775*uk_76 + 1282024163*uk_77 + 2567649375*uk_78 + 1791648675*uk_79 + 157*uk_8 + 1250172631*uk_80 + 166375*uk_81 + 12100*uk_82 + 474925*uk_83 + 36300*uk_84 + 487025*uk_85 + 680625*uk_86 + 474925*uk_87 + 880*uk_88 + 34540*uk_89 + 2572416961*uk_9 + 2640*uk_90 + 35420*uk_91 + 49500*uk_92 + 34540*uk_93 + 1355695*uk_94 + 103620*uk_95 + 1390235*uk_96 + 1942875*uk_97 + 1355695*uk_98 + 7920*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 143440*uk_100 + 198000*uk_101 + 3520*uk_102 + 1461295*uk_103 + 2017125*uk_104 + 35860*uk_105 + 2784375*uk_106 + 49500*uk_107 + 880*uk_108 + 17576*uk_109 + 1318694*uk_11 + 2704*uk_110 + 10816*uk_111 + 110188*uk_112 + 152100*uk_113 + 2704*uk_114 + 416*uk_115 + 1664*uk_116 + 16952*uk_117 + 23400*uk_118 + 416*uk_119 + 202876*uk_12 + 6656*uk_120 + 67808*uk_121 + 93600*uk_122 + 1664*uk_123 + 690794*uk_124 + 953550*uk_125 + 16952*uk_126 + 1316250*uk_127 + 23400*uk_128 + 416*uk_129 + 811504*uk_13 + 64*uk_130 + 256*uk_131 + 2608*uk_132 + 3600*uk_133 + 64*uk_134 + 1024*uk_135 + 10432*uk_136 + 14400*uk_137 + 256*uk_138 + 106276*uk_139 + 8267197*uk_14 + 146700*uk_140 + 2608*uk_141 + 202500*uk_142 + 3600*uk_143 + 64*uk_144 + 4096*uk_145 + 41728*uk_146 + 57600*uk_147 + 1024*uk_148 + 425104*uk_149 + 11411775*uk_15 + 586800*uk_150 + 10432*uk_151 + 810000*uk_152 + 14400*uk_153 + 256*uk_154 + 4330747*uk_155 + 5978025*uk_156 + 106276*uk_157 + 8251875*uk_158 + 146700*uk_159 + 202876*uk_16 + 2608*uk_160 + 11390625*uk_161 + 202500*uk_162 + 3600*uk_163 + 64*uk_164 + 3025*uk_17 + 1430*uk_18 + 220*uk_19 + 55*uk_2 + 880*uk_20 + 8965*uk_21 + 12375*uk_22 + 220*uk_23 + 676*uk_24 + 104*uk_25 + 416*uk_26 + 4238*uk_27 + 5850*uk_28 + 104*uk_29 + 26*uk_3 + 16*uk_30 + 64*uk_31 + 652*uk_32 + 900*uk_33 + 16*uk_34 + 256*uk_35 + 2608*uk_36 + 3600*uk_37 + 64*uk_38 + 26569*uk_39 + 4*uk_4 + 36675*uk_40 + 652*uk_41 + 50625*uk_42 + 900*uk_43 + 16*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 66882840986*uk_47 + 10289667844*uk_48 + 41158671376*uk_49 + 16*uk_5 + 419303964643*uk_50 + 578793816225*uk_51 + 10289667844*uk_52 + 153424975*uk_53 + 72528170*uk_54 + 11158180*uk_55 + 44632720*uk_56 + 454695835*uk_57 + 627647625*uk_58 + 11158180*uk_59 + 163*uk_6 + 34286044*uk_60 + 5274776*uk_61 + 21099104*uk_62 + 214947122*uk_63 + 296706150*uk_64 + 5274776*uk_65 + 811504*uk_66 + 3246016*uk_67 + 33068788*uk_68 + 45647100*uk_69 + 225*uk_7 + 811504*uk_70 + 12984064*uk_71 + 132275152*uk_72 + 182588400*uk_73 + 3246016*uk_74 + 1347553111*uk_75 + 1860119325*uk_76 + 33068788*uk_77 + 2567649375*uk_78 + 45647100*uk_79 + 4*uk_8 + 811504*uk_80 + 166375*uk_81 + 78650*uk_82 + 12100*uk_83 + 48400*uk_84 + 493075*uk_85 + 680625*uk_86 + 12100*uk_87 + 37180*uk_88 + 5720*uk_89 + 2572416961*uk_9 + 22880*uk_90 + 233090*uk_91 + 321750*uk_92 + 5720*uk_93 + 880*uk_94 + 3520*uk_95 + 35860*uk_96 + 49500*uk_97 + 880*uk_98 + 14080*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 145200*uk_100 + 198000*uk_101 + 22880*uk_102 + 1497375*uk_103 + 2041875*uk_104 + 235950*uk_105 + 2784375*uk_106 + 321750*uk_107 + 37180*uk_108 + 262144*uk_109 + 3246016*uk_11 + 106496*uk_110 + 65536*uk_111 + 675840*uk_112 + 921600*uk_113 + 106496*uk_114 + 43264*uk_115 + 26624*uk_116 + 274560*uk_117 + 374400*uk_118 + 43264*uk_119 + 1318694*uk_12 + 16384*uk_120 + 168960*uk_121 + 230400*uk_122 + 26624*uk_123 + 1742400*uk_124 + 2376000*uk_125 + 274560*uk_126 + 3240000*uk_127 + 374400*uk_128 + 43264*uk_129 + 811504*uk_13 + 17576*uk_130 + 10816*uk_131 + 111540*uk_132 + 152100*uk_133 + 17576*uk_134 + 6656*uk_135 + 68640*uk_136 + 93600*uk_137 + 10816*uk_138 + 707850*uk_139 + 8368635*uk_14 + 965250*uk_140 + 111540*uk_141 + 1316250*uk_142 + 152100*uk_143 + 17576*uk_144 + 4096*uk_145 + 42240*uk_146 + 57600*uk_147 + 6656*uk_148 + 435600*uk_149 + 11411775*uk_15 + 594000*uk_150 + 68640*uk_151 + 810000*uk_152 + 93600*uk_153 + 10816*uk_154 + 4492125*uk_155 + 6125625*uk_156 + 707850*uk_157 + 8353125*uk_158 + 965250*uk_159 + 1318694*uk_16 + 111540*uk_160 + 11390625*uk_161 + 1316250*uk_162 + 152100*uk_163 + 17576*uk_164 + 3025*uk_17 + 3520*uk_18 + 1430*uk_19 + 55*uk_2 + 880*uk_20 + 9075*uk_21 + 12375*uk_22 + 1430*uk_23 + 4096*uk_24 + 1664*uk_25 + 1024*uk_26 + 10560*uk_27 + 14400*uk_28 + 1664*uk_29 + 64*uk_3 + 676*uk_30 + 416*uk_31 + 4290*uk_32 + 5850*uk_33 + 676*uk_34 + 256*uk_35 + 2640*uk_36 + 3600*uk_37 + 416*uk_38 + 27225*uk_39 + 26*uk_4 + 37125*uk_40 + 4290*uk_41 + 50625*uk_42 + 5850*uk_43 + 676*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 164634685504*uk_47 + 66882840986*uk_48 + 41158671376*uk_49 + 16*uk_5 + 424448798565*uk_50 + 578793816225*uk_51 + 66882840986*uk_52 + 153424975*uk_53 + 178530880*uk_54 + 72528170*uk_55 + 44632720*uk_56 + 460274925*uk_57 + 627647625*uk_58 + 72528170*uk_59 + 165*uk_6 + 207745024*uk_60 + 84396416*uk_61 + 51936256*uk_62 + 535592640*uk_63 + 730353600*uk_64 + 84396416*uk_65 + 34286044*uk_66 + 21099104*uk_67 + 217584510*uk_68 + 296706150*uk_69 + 225*uk_7 + 34286044*uk_70 + 12984064*uk_71 + 133898160*uk_72 + 182588400*uk_73 + 21099104*uk_74 + 1380824775*uk_75 + 1882942875*uk_76 + 217584510*uk_77 + 2567649375*uk_78 + 296706150*uk_79 + 26*uk_8 + 34286044*uk_80 + 166375*uk_81 + 193600*uk_82 + 78650*uk_83 + 48400*uk_84 + 499125*uk_85 + 680625*uk_86 + 78650*uk_87 + 225280*uk_88 + 91520*uk_89 + 2572416961*uk_9 + 56320*uk_90 + 580800*uk_91 + 792000*uk_92 + 91520*uk_93 + 37180*uk_94 + 22880*uk_95 + 235950*uk_96 + 321750*uk_97 + 37180*uk_98 + 14080*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 146960*uk_100 + 198000*uk_101 + 56320*uk_102 + 1533895*uk_103 + 2066625*uk_104 + 587840*uk_105 + 2784375*uk_106 + 792000*uk_107 + 225280*uk_108 + 1643032*uk_109 + 5984842*uk_11 + 891136*uk_110 + 222784*uk_111 + 2325308*uk_112 + 3132900*uk_113 + 891136*uk_114 + 483328*uk_115 + 120832*uk_116 + 1261184*uk_117 + 1699200*uk_118 + 483328*uk_119 + 3246016*uk_12 + 30208*uk_120 + 315296*uk_121 + 424800*uk_122 + 120832*uk_123 + 3290902*uk_124 + 4433850*uk_125 + 1261184*uk_126 + 5973750*uk_127 + 1699200*uk_128 + 483328*uk_129 + 811504*uk_13 + 262144*uk_130 + 65536*uk_131 + 684032*uk_132 + 921600*uk_133 + 262144*uk_134 + 16384*uk_135 + 171008*uk_136 + 230400*uk_137 + 65536*uk_138 + 1784896*uk_139 + 8470073*uk_14 + 2404800*uk_140 + 684032*uk_141 + 3240000*uk_142 + 921600*uk_143 + 262144*uk_144 + 4096*uk_145 + 42752*uk_146 + 57600*uk_147 + 16384*uk_148 + 446224*uk_149 + 11411775*uk_15 + 601200*uk_150 + 171008*uk_151 + 810000*uk_152 + 230400*uk_153 + 65536*uk_154 + 4657463*uk_155 + 6275025*uk_156 + 1784896*uk_157 + 8454375*uk_158 + 2404800*uk_159 + 3246016*uk_16 + 684032*uk_160 + 11390625*uk_161 + 3240000*uk_162 + 921600*uk_163 + 262144*uk_164 + 3025*uk_17 + 6490*uk_18 + 3520*uk_19 + 55*uk_2 + 880*uk_20 + 9185*uk_21 + 12375*uk_22 + 3520*uk_23 + 13924*uk_24 + 7552*uk_25 + 1888*uk_26 + 19706*uk_27 + 26550*uk_28 + 7552*uk_29 + 118*uk_3 + 4096*uk_30 + 1024*uk_31 + 10688*uk_32 + 14400*uk_33 + 4096*uk_34 + 256*uk_35 + 2672*uk_36 + 3600*uk_37 + 1024*uk_38 + 27889*uk_39 + 64*uk_4 + 37575*uk_40 + 10688*uk_41 + 50625*uk_42 + 14400*uk_43 + 4096*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 303545201398*uk_47 + 164634685504*uk_48 + 41158671376*uk_49 + 16*uk_5 + 429593632487*uk_50 + 578793816225*uk_51 + 164634685504*uk_52 + 153424975*uk_53 + 329166310*uk_54 + 178530880*uk_55 + 44632720*uk_56 + 465854015*uk_57 + 627647625*uk_58 + 178530880*uk_59 + 167*uk_6 + 706211356*uk_60 + 383029888*uk_61 + 95757472*uk_62 + 999468614*uk_63 + 1346589450*uk_64 + 383029888*uk_65 + 207745024*uk_66 + 51936256*uk_67 + 542084672*uk_68 + 730353600*uk_69 + 225*uk_7 + 207745024*uk_70 + 12984064*uk_71 + 135521168*uk_72 + 182588400*uk_73 + 51936256*uk_74 + 1414502191*uk_75 + 1905766425*uk_76 + 542084672*uk_77 + 2567649375*uk_78 + 730353600*uk_79 + 64*uk_8 + 207745024*uk_80 + 166375*uk_81 + 356950*uk_82 + 193600*uk_83 + 48400*uk_84 + 505175*uk_85 + 680625*uk_86 + 193600*uk_87 + 765820*uk_88 + 415360*uk_89 + 2572416961*uk_9 + 103840*uk_90 + 1083830*uk_91 + 1460250*uk_92 + 415360*uk_93 + 225280*uk_94 + 56320*uk_95 + 587840*uk_96 + 792000*uk_97 + 225280*uk_98 + 14080*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 111540*uk_100 + 148500*uk_101 + 77880*uk_102 + 1570855*uk_103 + 2091375*uk_104 + 1096810*uk_105 + 2784375*uk_106 + 1460250*uk_107 + 765820*uk_108 + 6859*uk_109 + 963661*uk_11 + 42598*uk_110 + 4332*uk_111 + 61009*uk_112 + 81225*uk_113 + 42598*uk_114 + 264556*uk_115 + 26904*uk_116 + 378898*uk_117 + 504450*uk_118 + 264556*uk_119 + 5984842*uk_12 + 2736*uk_120 + 38532*uk_121 + 51300*uk_122 + 26904*uk_123 + 542659*uk_124 + 722475*uk_125 + 378898*uk_126 + 961875*uk_127 + 504450*uk_128 + 264556*uk_129 + 608628*uk_13 + 1643032*uk_130 + 167088*uk_131 + 2353156*uk_132 + 3132900*uk_133 + 1643032*uk_134 + 16992*uk_135 + 239304*uk_136 + 318600*uk_137 + 167088*uk_138 + 3370198*uk_139 + 8571511*uk_14 + 4486950*uk_140 + 2353156*uk_141 + 5973750*uk_142 + 3132900*uk_143 + 1643032*uk_144 + 1728*uk_145 + 24336*uk_146 + 32400*uk_147 + 16992*uk_148 + 342732*uk_149 + 11411775*uk_15 + 456300*uk_150 + 239304*uk_151 + 607500*uk_152 + 318600*uk_153 + 167088*uk_154 + 4826809*uk_155 + 6426225*uk_156 + 3370198*uk_157 + 8555625*uk_158 + 4486950*uk_159 + 5984842*uk_16 + 2353156*uk_160 + 11390625*uk_161 + 5973750*uk_162 + 3132900*uk_163 + 1643032*uk_164 + 3025*uk_17 + 1045*uk_18 + 6490*uk_19 + 55*uk_2 + 660*uk_20 + 9295*uk_21 + 12375*uk_22 + 6490*uk_23 + 361*uk_24 + 2242*uk_25 + 228*uk_26 + 3211*uk_27 + 4275*uk_28 + 2242*uk_29 + 19*uk_3 + 13924*uk_30 + 1416*uk_31 + 19942*uk_32 + 26550*uk_33 + 13924*uk_34 + 144*uk_35 + 2028*uk_36 + 2700*uk_37 + 1416*uk_38 + 28561*uk_39 + 118*uk_4 + 38025*uk_40 + 19942*uk_41 + 50625*uk_42 + 26550*uk_43 + 13924*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 48875922259*uk_47 + 303545201398*uk_48 + 30869003532*uk_49 + 12*uk_5 + 434738466409*uk_50 + 578793816225*uk_51 + 303545201398*uk_52 + 153424975*uk_53 + 53001355*uk_54 + 329166310*uk_55 + 33474540*uk_56 + 471433105*uk_57 + 627647625*uk_58 + 329166310*uk_59 + 169*uk_6 + 18309559*uk_60 + 113711998*uk_61 + 11563932*uk_62 + 162858709*uk_63 + 216823725*uk_64 + 113711998*uk_65 + 706211356*uk_66 + 71818104*uk_67 + 1011438298*uk_68 + 1346589450*uk_69 + 225*uk_7 + 706211356*uk_70 + 7303536*uk_71 + 102858132*uk_72 + 136941300*uk_73 + 71818104*uk_74 + 1448585359*uk_75 + 1928589975*uk_76 + 1011438298*uk_77 + 2567649375*uk_78 + 1346589450*uk_79 + 118*uk_8 + 706211356*uk_80 + 166375*uk_81 + 57475*uk_82 + 356950*uk_83 + 36300*uk_84 + 511225*uk_85 + 680625*uk_86 + 356950*uk_87 + 19855*uk_88 + 123310*uk_89 + 2572416961*uk_9 + 12540*uk_90 + 176605*uk_91 + 235125*uk_92 + 123310*uk_93 + 765820*uk_94 + 77880*uk_95 + 1096810*uk_96 + 1460250*uk_97 + 765820*uk_98 + 7920*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 150480*uk_100 + 198000*uk_101 + 16720*uk_102 + 1608255*uk_103 + 2116125*uk_104 + 178695*uk_105 + 2784375*uk_106 + 235125*uk_107 + 19855*uk_108 + 1092727*uk_109 + 5224057*uk_11 + 201571*uk_110 + 169744*uk_111 + 1814139*uk_112 + 2387025*uk_113 + 201571*uk_114 + 37183*uk_115 + 31312*uk_116 + 334647*uk_117 + 440325*uk_118 + 37183*uk_119 + 963661*uk_12 + 26368*uk_120 + 281808*uk_121 + 370800*uk_122 + 31312*uk_123 + 3011823*uk_124 + 3962925*uk_125 + 334647*uk_126 + 5214375*uk_127 + 440325*uk_128 + 37183*uk_129 + 811504*uk_13 + 6859*uk_130 + 5776*uk_131 + 61731*uk_132 + 81225*uk_133 + 6859*uk_134 + 4864*uk_135 + 51984*uk_136 + 68400*uk_137 + 5776*uk_138 + 555579*uk_139 + 8672949*uk_14 + 731025*uk_140 + 61731*uk_141 + 961875*uk_142 + 81225*uk_143 + 6859*uk_144 + 4096*uk_145 + 43776*uk_146 + 57600*uk_147 + 4864*uk_148 + 467856*uk_149 + 11411775*uk_15 + 615600*uk_150 + 51984*uk_151 + 810000*uk_152 + 68400*uk_153 + 5776*uk_154 + 5000211*uk_155 + 6579225*uk_156 + 555579*uk_157 + 8656875*uk_158 + 731025*uk_159 + 963661*uk_16 + 61731*uk_160 + 11390625*uk_161 + 961875*uk_162 + 81225*uk_163 + 6859*uk_164 + 3025*uk_17 + 5665*uk_18 + 1045*uk_19 + 55*uk_2 + 880*uk_20 + 9405*uk_21 + 12375*uk_22 + 1045*uk_23 + 10609*uk_24 + 1957*uk_25 + 1648*uk_26 + 17613*uk_27 + 23175*uk_28 + 1957*uk_29 + 103*uk_3 + 361*uk_30 + 304*uk_31 + 3249*uk_32 + 4275*uk_33 + 361*uk_34 + 256*uk_35 + 2736*uk_36 + 3600*uk_37 + 304*uk_38 + 29241*uk_39 + 19*uk_4 + 38475*uk_40 + 3249*uk_41 + 50625*uk_42 + 4275*uk_43 + 361*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 264958946983*uk_47 + 48875922259*uk_48 + 41158671376*uk_49 + 16*uk_5 + 439883300331*uk_50 + 578793816225*uk_51 + 48875922259*uk_52 + 153424975*uk_53 + 287323135*uk_54 + 53001355*uk_55 + 44632720*uk_56 + 477012195*uk_57 + 627647625*uk_58 + 53001355*uk_59 + 171*uk_6 + 538077871*uk_60 + 99257083*uk_61 + 83584912*uk_62 + 893313747*uk_63 + 1175412825*uk_64 + 99257083*uk_65 + 18309559*uk_66 + 15418576*uk_67 + 164786031*uk_68 + 216823725*uk_69 + 225*uk_7 + 18309559*uk_70 + 12984064*uk_71 + 138767184*uk_72 + 182588400*uk_73 + 15418576*uk_74 + 1483074279*uk_75 + 1951413525*uk_76 + 164786031*uk_77 + 2567649375*uk_78 + 216823725*uk_79 + 19*uk_8 + 18309559*uk_80 + 166375*uk_81 + 311575*uk_82 + 57475*uk_83 + 48400*uk_84 + 517275*uk_85 + 680625*uk_86 + 57475*uk_87 + 583495*uk_88 + 107635*uk_89 + 2572416961*uk_9 + 90640*uk_90 + 968715*uk_91 + 1274625*uk_92 + 107635*uk_93 + 19855*uk_94 + 16720*uk_95 + 178695*uk_96 + 235125*uk_97 + 19855*uk_98 + 14080*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 114180*uk_100 + 148500*uk_101 + 67980*uk_102 + 1646095*uk_103 + 2140875*uk_104 + 980045*uk_105 + 2784375*uk_106 + 1274625*uk_107 + 583495*uk_108 + 27000*uk_109 + 1521570*uk_11 + 92700*uk_110 + 10800*uk_111 + 155700*uk_112 + 202500*uk_113 + 92700*uk_114 + 318270*uk_115 + 37080*uk_116 + 534570*uk_117 + 695250*uk_118 + 318270*uk_119 + 5224057*uk_12 + 4320*uk_120 + 62280*uk_121 + 81000*uk_122 + 37080*uk_123 + 897870*uk_124 + 1167750*uk_125 + 534570*uk_126 + 1518750*uk_127 + 695250*uk_128 + 318270*uk_129 + 608628*uk_13 + 1092727*uk_130 + 127308*uk_131 + 1835357*uk_132 + 2387025*uk_133 + 1092727*uk_134 + 14832*uk_135 + 213828*uk_136 + 278100*uk_137 + 127308*uk_138 + 3082687*uk_139 + 8774387*uk_14 + 4009275*uk_140 + 1835357*uk_141 + 5214375*uk_142 + 2387025*uk_143 + 1092727*uk_144 + 1728*uk_145 + 24912*uk_146 + 32400*uk_147 + 14832*uk_148 + 359148*uk_149 + 11411775*uk_15 + 467100*uk_150 + 213828*uk_151 + 607500*uk_152 + 278100*uk_153 + 127308*uk_154 + 5177717*uk_155 + 6734025*uk_156 + 3082687*uk_157 + 8758125*uk_158 + 4009275*uk_159 + 5224057*uk_16 + 1835357*uk_160 + 11390625*uk_161 + 5214375*uk_162 + 2387025*uk_163 + 1092727*uk_164 + 3025*uk_17 + 1650*uk_18 + 5665*uk_19 + 55*uk_2 + 660*uk_20 + 9515*uk_21 + 12375*uk_22 + 5665*uk_23 + 900*uk_24 + 3090*uk_25 + 360*uk_26 + 5190*uk_27 + 6750*uk_28 + 3090*uk_29 + 30*uk_3 + 10609*uk_30 + 1236*uk_31 + 17819*uk_32 + 23175*uk_33 + 10609*uk_34 + 144*uk_35 + 2076*uk_36 + 2700*uk_37 + 1236*uk_38 + 29929*uk_39 + 103*uk_4 + 38925*uk_40 + 17819*uk_41 + 50625*uk_42 + 23175*uk_43 + 10609*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 77172508830*uk_47 + 264958946983*uk_48 + 30869003532*uk_49 + 12*uk_5 + 445028134253*uk_50 + 578793816225*uk_51 + 264958946983*uk_52 + 153424975*uk_53 + 83686350*uk_54 + 287323135*uk_55 + 33474540*uk_56 + 482591285*uk_57 + 627647625*uk_58 + 287323135*uk_59 + 173*uk_6 + 45647100*uk_60 + 156721710*uk_61 + 18258840*uk_62 + 263231610*uk_63 + 342353250*uk_64 + 156721710*uk_65 + 538077871*uk_66 + 62688684*uk_67 + 903761861*uk_68 + 1175412825*uk_69 + 225*uk_7 + 538077871*uk_70 + 7303536*uk_71 + 105292644*uk_72 + 136941300*uk_73 + 62688684*uk_74 + 1517968951*uk_75 + 1974237075*uk_76 + 903761861*uk_77 + 2567649375*uk_78 + 1175412825*uk_79 + 103*uk_8 + 538077871*uk_80 + 166375*uk_81 + 90750*uk_82 + 311575*uk_83 + 36300*uk_84 + 523325*uk_85 + 680625*uk_86 + 311575*uk_87 + 49500*uk_88 + 169950*uk_89 + 2572416961*uk_9 + 19800*uk_90 + 285450*uk_91 + 371250*uk_92 + 169950*uk_93 + 583495*uk_94 + 67980*uk_95 + 980045*uk_96 + 1274625*uk_97 + 583495*uk_98 + 7920*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 154000*uk_100 + 198000*uk_101 + 26400*uk_102 + 1684375*uk_103 + 2165625*uk_104 + 288750*uk_105 + 2784375*uk_106 + 371250*uk_107 + 49500*uk_108 + 2985984*uk_109 + 7303536*uk_11 + 622080*uk_110 + 331776*uk_111 + 3628800*uk_112 + 4665600*uk_113 + 622080*uk_114 + 129600*uk_115 + 69120*uk_116 + 756000*uk_117 + 972000*uk_118 + 129600*uk_119 + 1521570*uk_12 + 36864*uk_120 + 403200*uk_121 + 518400*uk_122 + 69120*uk_123 + 4410000*uk_124 + 5670000*uk_125 + 756000*uk_126 + 7290000*uk_127 + 972000*uk_128 + 129600*uk_129 + 811504*uk_13 + 27000*uk_130 + 14400*uk_131 + 157500*uk_132 + 202500*uk_133 + 27000*uk_134 + 7680*uk_135 + 84000*uk_136 + 108000*uk_137 + 14400*uk_138 + 918750*uk_139 + 8875825*uk_14 + 1181250*uk_140 + 157500*uk_141 + 1518750*uk_142 + 202500*uk_143 + 27000*uk_144 + 4096*uk_145 + 44800*uk_146 + 57600*uk_147 + 7680*uk_148 + 490000*uk_149 + 11411775*uk_15 + 630000*uk_150 + 84000*uk_151 + 810000*uk_152 + 108000*uk_153 + 14400*uk_154 + 5359375*uk_155 + 6890625*uk_156 + 918750*uk_157 + 8859375*uk_158 + 1181250*uk_159 + 1521570*uk_16 + 157500*uk_160 + 11390625*uk_161 + 1518750*uk_162 + 202500*uk_163 + 27000*uk_164 + 3025*uk_17 + 7920*uk_18 + 1650*uk_19 + 55*uk_2 + 880*uk_20 + 9625*uk_21 + 12375*uk_22 + 1650*uk_23 + 20736*uk_24 + 4320*uk_25 + 2304*uk_26 + 25200*uk_27 + 32400*uk_28 + 4320*uk_29 + 144*uk_3 + 900*uk_30 + 480*uk_31 + 5250*uk_32 + 6750*uk_33 + 900*uk_34 + 256*uk_35 + 2800*uk_36 + 3600*uk_37 + 480*uk_38 + 30625*uk_39 + 30*uk_4 + 39375*uk_40 + 5250*uk_41 + 50625*uk_42 + 6750*uk_43 + 900*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 370428042384*uk_47 + 77172508830*uk_48 + 41158671376*uk_49 + 16*uk_5 + 450172968175*uk_50 + 578793816225*uk_51 + 77172508830*uk_52 + 153424975*uk_53 + 401694480*uk_54 + 83686350*uk_55 + 44632720*uk_56 + 488170375*uk_57 + 627647625*uk_58 + 83686350*uk_59 + 175*uk_6 + 1051709184*uk_60 + 219106080*uk_61 + 116856576*uk_62 + 1278118800*uk_63 + 1643295600*uk_64 + 219106080*uk_65 + 45647100*uk_66 + 24345120*uk_67 + 266274750*uk_68 + 342353250*uk_69 + 225*uk_7 + 45647100*uk_70 + 12984064*uk_71 + 142013200*uk_72 + 182588400*uk_73 + 24345120*uk_74 + 1553269375*uk_75 + 1997060625*uk_76 + 266274750*uk_77 + 2567649375*uk_78 + 342353250*uk_79 + 30*uk_8 + 45647100*uk_80 + 166375*uk_81 + 435600*uk_82 + 90750*uk_83 + 48400*uk_84 + 529375*uk_85 + 680625*uk_86 + 90750*uk_87 + 1140480*uk_88 + 237600*uk_89 + 2572416961*uk_9 + 126720*uk_90 + 1386000*uk_91 + 1782000*uk_92 + 237600*uk_93 + 49500*uk_94 + 26400*uk_95 + 288750*uk_96 + 371250*uk_97 + 49500*uk_98 + 14080*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 116820*uk_100 + 148500*uk_101 + 95040*uk_102 + 1723095*uk_103 + 2190375*uk_104 + 1401840*uk_105 + 2784375*uk_106 + 1782000*uk_107 + 1140480*uk_108 + 912673*uk_109 + 4919743*uk_11 + 1354896*uk_110 + 112908*uk_111 + 1665393*uk_112 + 2117025*uk_113 + 1354896*uk_114 + 2011392*uk_115 + 167616*uk_116 + 2472336*uk_117 + 3142800*uk_118 + 2011392*uk_119 + 7303536*uk_12 + 13968*uk_120 + 206028*uk_121 + 261900*uk_122 + 167616*uk_123 + 3038913*uk_124 + 3863025*uk_125 + 2472336*uk_126 + 4910625*uk_127 + 3142800*uk_128 + 2011392*uk_129 + 608628*uk_13 + 2985984*uk_130 + 248832*uk_131 + 3670272*uk_132 + 4665600*uk_133 + 2985984*uk_134 + 20736*uk_135 + 305856*uk_136 + 388800*uk_137 + 248832*uk_138 + 4511376*uk_139 + 8977263*uk_14 + 5734800*uk_140 + 3670272*uk_141 + 7290000*uk_142 + 4665600*uk_143 + 2985984*uk_144 + 1728*uk_145 + 25488*uk_146 + 32400*uk_147 + 20736*uk_148 + 375948*uk_149 + 11411775*uk_15 + 477900*uk_150 + 305856*uk_151 + 607500*uk_152 + 388800*uk_153 + 248832*uk_154 + 5545233*uk_155 + 7049025*uk_156 + 4511376*uk_157 + 8960625*uk_158 + 5734800*uk_159 + 7303536*uk_16 + 3670272*uk_160 + 11390625*uk_161 + 7290000*uk_162 + 4665600*uk_163 + 2985984*uk_164 + 3025*uk_17 + 5335*uk_18 + 7920*uk_19 + 55*uk_2 + 660*uk_20 + 9735*uk_21 + 12375*uk_22 + 7920*uk_23 + 9409*uk_24 + 13968*uk_25 + 1164*uk_26 + 17169*uk_27 + 21825*uk_28 + 13968*uk_29 + 97*uk_3 + 20736*uk_30 + 1728*uk_31 + 25488*uk_32 + 32400*uk_33 + 20736*uk_34 + 144*uk_35 + 2124*uk_36 + 2700*uk_37 + 1728*uk_38 + 31329*uk_39 + 144*uk_4 + 39825*uk_40 + 25488*uk_41 + 50625*uk_42 + 32400*uk_43 + 20736*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 249524445217*uk_47 + 370428042384*uk_48 + 30869003532*uk_49 + 12*uk_5 + 455317802097*uk_50 + 578793816225*uk_51 + 370428042384*uk_52 + 153424975*uk_53 + 270585865*uk_54 + 401694480*uk_55 + 33474540*uk_56 + 493749465*uk_57 + 627647625*uk_58 + 401694480*uk_59 + 177*uk_6 + 477215071*uk_60 + 708442992*uk_61 + 59036916*uk_62 + 870794511*uk_63 + 1106942175*uk_64 + 708442992*uk_65 + 1051709184*uk_66 + 87642432*uk_67 + 1292725872*uk_68 + 1643295600*uk_69 + 225*uk_7 + 1051709184*uk_70 + 7303536*uk_71 + 107727156*uk_72 + 136941300*uk_73 + 87642432*uk_74 + 1588975551*uk_75 + 2019884175*uk_76 + 1292725872*uk_77 + 2567649375*uk_78 + 1643295600*uk_79 + 144*uk_8 + 1051709184*uk_80 + 166375*uk_81 + 293425*uk_82 + 435600*uk_83 + 36300*uk_84 + 535425*uk_85 + 680625*uk_86 + 435600*uk_87 + 517495*uk_88 + 768240*uk_89 + 2572416961*uk_9 + 64020*uk_90 + 944295*uk_91 + 1200375*uk_92 + 768240*uk_93 + 1140480*uk_94 + 95040*uk_95 + 1401840*uk_96 + 1782000*uk_97 + 1140480*uk_98 + 7920*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 118140*uk_100 + 148500*uk_101 + 64020*uk_102 + 1762255*uk_103 + 2215125*uk_104 + 954965*uk_105 + 2784375*uk_106 + 1200375*uk_107 + 517495*uk_108 + 238328*uk_109 + 3144578*uk_11 + 372868*uk_110 + 46128*uk_111 + 688076*uk_112 + 864900*uk_113 + 372868*uk_114 + 583358*uk_115 + 72168*uk_116 + 1076506*uk_117 + 1353150*uk_118 + 583358*uk_119 + 4919743*uk_12 + 8928*uk_120 + 133176*uk_121 + 167400*uk_122 + 72168*uk_123 + 1986542*uk_124 + 2497050*uk_125 + 1076506*uk_126 + 3138750*uk_127 + 1353150*uk_128 + 583358*uk_129 + 608628*uk_13 + 912673*uk_130 + 112908*uk_131 + 1684211*uk_132 + 2117025*uk_133 + 912673*uk_134 + 13968*uk_135 + 208356*uk_136 + 261900*uk_137 + 112908*uk_138 + 3107977*uk_139 + 9078701*uk_14 + 3906675*uk_140 + 1684211*uk_141 + 4910625*uk_142 + 2117025*uk_143 + 912673*uk_144 + 1728*uk_145 + 25776*uk_146 + 32400*uk_147 + 13968*uk_148 + 384492*uk_149 + 11411775*uk_15 + 483300*uk_150 + 208356*uk_151 + 607500*uk_152 + 261900*uk_153 + 112908*uk_154 + 5735339*uk_155 + 7209225*uk_156 + 3107977*uk_157 + 9061875*uk_158 + 3906675*uk_159 + 4919743*uk_16 + 1684211*uk_160 + 11390625*uk_161 + 4910625*uk_162 + 2117025*uk_163 + 912673*uk_164 + 3025*uk_17 + 3410*uk_18 + 5335*uk_19 + 55*uk_2 + 660*uk_20 + 9845*uk_21 + 12375*uk_22 + 5335*uk_23 + 3844*uk_24 + 6014*uk_25 + 744*uk_26 + 11098*uk_27 + 13950*uk_28 + 6014*uk_29 + 62*uk_3 + 9409*uk_30 + 1164*uk_31 + 17363*uk_32 + 21825*uk_33 + 9409*uk_34 + 144*uk_35 + 2148*uk_36 + 2700*uk_37 + 1164*uk_38 + 32041*uk_39 + 97*uk_4 + 40275*uk_40 + 17363*uk_41 + 50625*uk_42 + 21825*uk_43 + 9409*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 159489851582*uk_47 + 249524445217*uk_48 + 30869003532*uk_49 + 12*uk_5 + 460462636019*uk_50 + 578793816225*uk_51 + 249524445217*uk_52 + 153424975*uk_53 + 172951790*uk_54 + 270585865*uk_55 + 33474540*uk_56 + 499328555*uk_57 + 627647625*uk_58 + 270585865*uk_59 + 179*uk_6 + 194963836*uk_60 + 305024066*uk_61 + 37734936*uk_62 + 562879462*uk_63 + 707530050*uk_64 + 305024066*uk_65 + 477215071*uk_66 + 59036916*uk_67 + 880633997*uk_68 + 1106942175*uk_69 + 225*uk_7 + 477215071*uk_70 + 7303536*uk_71 + 108944412*uk_72 + 136941300*uk_73 + 59036916*uk_74 + 1625087479*uk_75 + 2042707725*uk_76 + 880633997*uk_77 + 2567649375*uk_78 + 1106942175*uk_79 + 97*uk_8 + 477215071*uk_80 + 166375*uk_81 + 187550*uk_82 + 293425*uk_83 + 36300*uk_84 + 541475*uk_85 + 680625*uk_86 + 293425*uk_87 + 211420*uk_88 + 330770*uk_89 + 2572416961*uk_9 + 40920*uk_90 + 610390*uk_91 + 767250*uk_92 + 330770*uk_93 + 517495*uk_94 + 64020*uk_95 + 954965*uk_96 + 1200375*uk_97 + 517495*uk_98 + 7920*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 119460*uk_100 + 148500*uk_101 + 40920*uk_102 + 1801855*uk_103 + 2239875*uk_104 + 617210*uk_105 + 2784375*uk_106 + 767250*uk_107 + 211420*uk_108 + 59319*uk_109 + 1978041*uk_11 + 94302*uk_110 + 18252*uk_111 + 275301*uk_112 + 342225*uk_113 + 94302*uk_114 + 149916*uk_115 + 29016*uk_116 + 437658*uk_117 + 544050*uk_118 + 149916*uk_119 + 3144578*uk_12 + 5616*uk_120 + 84708*uk_121 + 105300*uk_122 + 29016*uk_123 + 1277679*uk_124 + 1588275*uk_125 + 437658*uk_126 + 1974375*uk_127 + 544050*uk_128 + 149916*uk_129 + 608628*uk_13 + 238328*uk_130 + 46128*uk_131 + 695764*uk_132 + 864900*uk_133 + 238328*uk_134 + 8928*uk_135 + 134664*uk_136 + 167400*uk_137 + 46128*uk_138 + 2031182*uk_139 + 9180139*uk_14 + 2524950*uk_140 + 695764*uk_141 + 3138750*uk_142 + 864900*uk_143 + 238328*uk_144 + 1728*uk_145 + 26064*uk_146 + 32400*uk_147 + 8928*uk_148 + 393132*uk_149 + 11411775*uk_15 + 488700*uk_150 + 134664*uk_151 + 607500*uk_152 + 167400*uk_153 + 46128*uk_154 + 5929741*uk_155 + 7371225*uk_156 + 2031182*uk_157 + 9163125*uk_158 + 2524950*uk_159 + 3144578*uk_16 + 695764*uk_160 + 11390625*uk_161 + 3138750*uk_162 + 864900*uk_163 + 238328*uk_164 + 3025*uk_17 + 2145*uk_18 + 3410*uk_19 + 55*uk_2 + 660*uk_20 + 9955*uk_21 + 12375*uk_22 + 3410*uk_23 + 1521*uk_24 + 2418*uk_25 + 468*uk_26 + 7059*uk_27 + 8775*uk_28 + 2418*uk_29 + 39*uk_3 + 3844*uk_30 + 744*uk_31 + 11222*uk_32 + 13950*uk_33 + 3844*uk_34 + 144*uk_35 + 2172*uk_36 + 2700*uk_37 + 744*uk_38 + 32761*uk_39 + 62*uk_4 + 40725*uk_40 + 11222*uk_41 + 50625*uk_42 + 13950*uk_43 + 3844*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 100324261479*uk_47 + 159489851582*uk_48 + 30869003532*uk_49 + 12*uk_5 + 465607469941*uk_50 + 578793816225*uk_51 + 159489851582*uk_52 + 153424975*uk_53 + 108792255*uk_54 + 172951790*uk_55 + 33474540*uk_56 + 504907645*uk_57 + 627647625*uk_58 + 172951790*uk_59 + 181*uk_6 + 77143599*uk_60 + 122638542*uk_61 + 23736492*uk_62 + 358025421*uk_63 + 445059225*uk_64 + 122638542*uk_65 + 194963836*uk_66 + 37734936*uk_67 + 569168618*uk_68 + 707530050*uk_69 + 225*uk_7 + 194963836*uk_70 + 7303536*uk_71 + 110161668*uk_72 + 136941300*uk_73 + 37734936*uk_74 + 1661605159*uk_75 + 2065531275*uk_76 + 569168618*uk_77 + 2567649375*uk_78 + 707530050*uk_79 + 62*uk_8 + 194963836*uk_80 + 166375*uk_81 + 117975*uk_82 + 187550*uk_83 + 36300*uk_84 + 547525*uk_85 + 680625*uk_86 + 187550*uk_87 + 83655*uk_88 + 132990*uk_89 + 2572416961*uk_9 + 25740*uk_90 + 388245*uk_91 + 482625*uk_92 + 132990*uk_93 + 211420*uk_94 + 40920*uk_95 + 617210*uk_96 + 767250*uk_97 + 211420*uk_98 + 7920*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 120780*uk_100 + 148500*uk_101 + 25740*uk_102 + 1841895*uk_103 + 2264625*uk_104 + 392535*uk_105 + 2784375*uk_106 + 482625*uk_107 + 83655*uk_108 + 21952*uk_109 + 1420132*uk_11 + 30576*uk_110 + 9408*uk_111 + 143472*uk_112 + 176400*uk_113 + 30576*uk_114 + 42588*uk_115 + 13104*uk_116 + 199836*uk_117 + 245700*uk_118 + 42588*uk_119 + 1978041*uk_12 + 4032*uk_120 + 61488*uk_121 + 75600*uk_122 + 13104*uk_123 + 937692*uk_124 + 1152900*uk_125 + 199836*uk_126 + 1417500*uk_127 + 245700*uk_128 + 42588*uk_129 + 608628*uk_13 + 59319*uk_130 + 18252*uk_131 + 278343*uk_132 + 342225*uk_133 + 59319*uk_134 + 5616*uk_135 + 85644*uk_136 + 105300*uk_137 + 18252*uk_138 + 1306071*uk_139 + 9281577*uk_14 + 1605825*uk_140 + 278343*uk_141 + 1974375*uk_142 + 342225*uk_143 + 59319*uk_144 + 1728*uk_145 + 26352*uk_146 + 32400*uk_147 + 5616*uk_148 + 401868*uk_149 + 11411775*uk_15 + 494100*uk_150 + 85644*uk_151 + 607500*uk_152 + 105300*uk_153 + 18252*uk_154 + 6128487*uk_155 + 7535025*uk_156 + 1306071*uk_157 + 9264375*uk_158 + 1605825*uk_159 + 1978041*uk_16 + 278343*uk_160 + 11390625*uk_161 + 1974375*uk_162 + 342225*uk_163 + 59319*uk_164 + 3025*uk_17 + 1540*uk_18 + 2145*uk_19 + 55*uk_2 + 660*uk_20 + 10065*uk_21 + 12375*uk_22 + 2145*uk_23 + 784*uk_24 + 1092*uk_25 + 336*uk_26 + 5124*uk_27 + 6300*uk_28 + 1092*uk_29 + 28*uk_3 + 1521*uk_30 + 468*uk_31 + 7137*uk_32 + 8775*uk_33 + 1521*uk_34 + 144*uk_35 + 2196*uk_36 + 2700*uk_37 + 468*uk_38 + 33489*uk_39 + 39*uk_4 + 41175*uk_40 + 7137*uk_41 + 50625*uk_42 + 8775*uk_43 + 1521*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 72027674908*uk_47 + 100324261479*uk_48 + 30869003532*uk_49 + 12*uk_5 + 470752303863*uk_50 + 578793816225*uk_51 + 100324261479*uk_52 + 153424975*uk_53 + 78107260*uk_54 + 108792255*uk_55 + 33474540*uk_56 + 510486735*uk_57 + 627647625*uk_58 + 108792255*uk_59 + 183*uk_6 + 39763696*uk_60 + 55385148*uk_61 + 17041584*uk_62 + 259884156*uk_63 + 319529700*uk_64 + 55385148*uk_65 + 77143599*uk_66 + 23736492*uk_67 + 361981503*uk_68 + 445059225*uk_69 + 225*uk_7 + 77143599*uk_70 + 7303536*uk_71 + 111378924*uk_72 + 136941300*uk_73 + 23736492*uk_74 + 1698528591*uk_75 + 2088354825*uk_76 + 361981503*uk_77 + 2567649375*uk_78 + 445059225*uk_79 + 39*uk_8 + 77143599*uk_80 + 166375*uk_81 + 84700*uk_82 + 117975*uk_83 + 36300*uk_84 + 553575*uk_85 + 680625*uk_86 + 117975*uk_87 + 43120*uk_88 + 60060*uk_89 + 2572416961*uk_9 + 18480*uk_90 + 281820*uk_91 + 346500*uk_92 + 60060*uk_93 + 83655*uk_94 + 25740*uk_95 + 392535*uk_96 + 482625*uk_97 + 83655*uk_98 + 7920*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 122100*uk_100 + 148500*uk_101 + 18480*uk_102 + 1882375*uk_103 + 2289375*uk_104 + 284900*uk_105 + 2784375*uk_106 + 346500*uk_107 + 43120*uk_108 + 24389*uk_109 + 1470851*uk_11 + 23548*uk_110 + 10092*uk_111 + 155585*uk_112 + 189225*uk_113 + 23548*uk_114 + 22736*uk_115 + 9744*uk_116 + 150220*uk_117 + 182700*uk_118 + 22736*uk_119 + 1420132*uk_12 + 4176*uk_120 + 64380*uk_121 + 78300*uk_122 + 9744*uk_123 + 992525*uk_124 + 1207125*uk_125 + 150220*uk_126 + 1468125*uk_127 + 182700*uk_128 + 22736*uk_129 + 608628*uk_13 + 21952*uk_130 + 9408*uk_131 + 145040*uk_132 + 176400*uk_133 + 21952*uk_134 + 4032*uk_135 + 62160*uk_136 + 75600*uk_137 + 9408*uk_138 + 958300*uk_139 + 9383015*uk_14 + 1165500*uk_140 + 145040*uk_141 + 1417500*uk_142 + 176400*uk_143 + 21952*uk_144 + 1728*uk_145 + 26640*uk_146 + 32400*uk_147 + 4032*uk_148 + 410700*uk_149 + 11411775*uk_15 + 499500*uk_150 + 62160*uk_151 + 607500*uk_152 + 75600*uk_153 + 9408*uk_154 + 6331625*uk_155 + 7700625*uk_156 + 958300*uk_157 + 9365625*uk_158 + 1165500*uk_159 + 1420132*uk_16 + 145040*uk_160 + 11390625*uk_161 + 1417500*uk_162 + 176400*uk_163 + 21952*uk_164 + 3025*uk_17 + 1595*uk_18 + 1540*uk_19 + 55*uk_2 + 660*uk_20 + 10175*uk_21 + 12375*uk_22 + 1540*uk_23 + 841*uk_24 + 812*uk_25 + 348*uk_26 + 5365*uk_27 + 6525*uk_28 + 812*uk_29 + 29*uk_3 + 784*uk_30 + 336*uk_31 + 5180*uk_32 + 6300*uk_33 + 784*uk_34 + 144*uk_35 + 2220*uk_36 + 2700*uk_37 + 336*uk_38 + 34225*uk_39 + 28*uk_4 + 41625*uk_40 + 5180*uk_41 + 50625*uk_42 + 6300*uk_43 + 784*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 74600091869*uk_47 + 72027674908*uk_48 + 30869003532*uk_49 + 12*uk_5 + 475897137785*uk_50 + 578793816225*uk_51 + 72027674908*uk_52 + 153424975*uk_53 + 80896805*uk_54 + 78107260*uk_55 + 33474540*uk_56 + 516065825*uk_57 + 627647625*uk_58 + 78107260*uk_59 + 185*uk_6 + 42654679*uk_60 + 41183828*uk_61 + 17650212*uk_62 + 272107435*uk_63 + 330941475*uk_64 + 41183828*uk_65 + 39763696*uk_66 + 17041584*uk_67 + 262724420*uk_68 + 319529700*uk_69 + 225*uk_7 + 39763696*uk_70 + 7303536*uk_71 + 112596180*uk_72 + 136941300*uk_73 + 17041584*uk_74 + 1735857775*uk_75 + 2111178375*uk_76 + 262724420*uk_77 + 2567649375*uk_78 + 319529700*uk_79 + 28*uk_8 + 39763696*uk_80 + 166375*uk_81 + 87725*uk_82 + 84700*uk_83 + 36300*uk_84 + 559625*uk_85 + 680625*uk_86 + 84700*uk_87 + 46255*uk_88 + 44660*uk_89 + 2572416961*uk_9 + 19140*uk_90 + 295075*uk_91 + 358875*uk_92 + 44660*uk_93 + 43120*uk_94 + 18480*uk_95 + 284900*uk_96 + 346500*uk_97 + 43120*uk_98 + 7920*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 123420*uk_100 + 148500*uk_101 + 19140*uk_102 + 1923295*uk_103 + 2314125*uk_104 + 298265*uk_105 + 2784375*uk_106 + 358875*uk_107 + 46255*uk_108 + 74088*uk_109 + 2130198*uk_11 + 51156*uk_110 + 21168*uk_111 + 329868*uk_112 + 396900*uk_113 + 51156*uk_114 + 35322*uk_115 + 14616*uk_116 + 227766*uk_117 + 274050*uk_118 + 35322*uk_119 + 1470851*uk_12 + 6048*uk_120 + 94248*uk_121 + 113400*uk_122 + 14616*uk_123 + 1468698*uk_124 + 1767150*uk_125 + 227766*uk_126 + 2126250*uk_127 + 274050*uk_128 + 35322*uk_129 + 608628*uk_13 + 24389*uk_130 + 10092*uk_131 + 157267*uk_132 + 189225*uk_133 + 24389*uk_134 + 4176*uk_135 + 65076*uk_136 + 78300*uk_137 + 10092*uk_138 + 1014101*uk_139 + 9484453*uk_14 + 1220175*uk_140 + 157267*uk_141 + 1468125*uk_142 + 189225*uk_143 + 24389*uk_144 + 1728*uk_145 + 26928*uk_146 + 32400*uk_147 + 4176*uk_148 + 419628*uk_149 + 11411775*uk_15 + 504900*uk_150 + 65076*uk_151 + 607500*uk_152 + 78300*uk_153 + 10092*uk_154 + 6539203*uk_155 + 7868025*uk_156 + 1014101*uk_157 + 9466875*uk_158 + 1220175*uk_159 + 1470851*uk_16 + 157267*uk_160 + 11390625*uk_161 + 1468125*uk_162 + 189225*uk_163 + 24389*uk_164 + 3025*uk_17 + 2310*uk_18 + 1595*uk_19 + 55*uk_2 + 660*uk_20 + 10285*uk_21 + 12375*uk_22 + 1595*uk_23 + 1764*uk_24 + 1218*uk_25 + 504*uk_26 + 7854*uk_27 + 9450*uk_28 + 1218*uk_29 + 42*uk_3 + 841*uk_30 + 348*uk_31 + 5423*uk_32 + 6525*uk_33 + 841*uk_34 + 144*uk_35 + 2244*uk_36 + 2700*uk_37 + 348*uk_38 + 34969*uk_39 + 29*uk_4 + 42075*uk_40 + 5423*uk_41 + 50625*uk_42 + 6525*uk_43 + 841*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 108041512362*uk_47 + 74600091869*uk_48 + 30869003532*uk_49 + 12*uk_5 + 481041971707*uk_50 + 578793816225*uk_51 + 74600091869*uk_52 + 153424975*uk_53 + 117160890*uk_54 + 80896805*uk_55 + 33474540*uk_56 + 521644915*uk_57 + 627647625*uk_58 + 80896805*uk_59 + 187*uk_6 + 89468316*uk_60 + 61775742*uk_61 + 25562376*uk_62 + 398347026*uk_63 + 479294550*uk_64 + 61775742*uk_65 + 42654679*uk_66 + 17650212*uk_67 + 275049137*uk_68 + 330941475*uk_69 + 225*uk_7 + 42654679*uk_70 + 7303536*uk_71 + 113813436*uk_72 + 136941300*uk_73 + 17650212*uk_74 + 1773592711*uk_75 + 2134001925*uk_76 + 275049137*uk_77 + 2567649375*uk_78 + 330941475*uk_79 + 29*uk_8 + 42654679*uk_80 + 166375*uk_81 + 127050*uk_82 + 87725*uk_83 + 36300*uk_84 + 565675*uk_85 + 680625*uk_86 + 87725*uk_87 + 97020*uk_88 + 66990*uk_89 + 2572416961*uk_9 + 27720*uk_90 + 431970*uk_91 + 519750*uk_92 + 66990*uk_93 + 46255*uk_94 + 19140*uk_95 + 298265*uk_96 + 358875*uk_97 + 46255*uk_98 + 7920*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 124740*uk_100 + 148500*uk_101 + 27720*uk_102 + 1964655*uk_103 + 2338875*uk_104 + 436590*uk_105 + 2784375*uk_106 + 519750*uk_107 + 97020*uk_108 + 300763*uk_109 + 3398173*uk_11 + 188538*uk_110 + 53868*uk_111 + 848421*uk_112 + 1010025*uk_113 + 188538*uk_114 + 118188*uk_115 + 33768*uk_116 + 531846*uk_117 + 633150*uk_118 + 118188*uk_119 + 2130198*uk_12 + 9648*uk_120 + 151956*uk_121 + 180900*uk_122 + 33768*uk_123 + 2393307*uk_124 + 2849175*uk_125 + 531846*uk_126 + 3391875*uk_127 + 633150*uk_128 + 118188*uk_129 + 608628*uk_13 + 74088*uk_130 + 21168*uk_131 + 333396*uk_132 + 396900*uk_133 + 74088*uk_134 + 6048*uk_135 + 95256*uk_136 + 113400*uk_137 + 21168*uk_138 + 1500282*uk_139 + 9585891*uk_14 + 1786050*uk_140 + 333396*uk_141 + 2126250*uk_142 + 396900*uk_143 + 74088*uk_144 + 1728*uk_145 + 27216*uk_146 + 32400*uk_147 + 6048*uk_148 + 428652*uk_149 + 11411775*uk_15 + 510300*uk_150 + 95256*uk_151 + 607500*uk_152 + 113400*uk_153 + 21168*uk_154 + 6751269*uk_155 + 8037225*uk_156 + 1500282*uk_157 + 9568125*uk_158 + 1786050*uk_159 + 2130198*uk_16 + 333396*uk_160 + 11390625*uk_161 + 2126250*uk_162 + 396900*uk_163 + 74088*uk_164 + 3025*uk_17 + 3685*uk_18 + 2310*uk_19 + 55*uk_2 + 660*uk_20 + 10395*uk_21 + 12375*uk_22 + 2310*uk_23 + 4489*uk_24 + 2814*uk_25 + 804*uk_26 + 12663*uk_27 + 15075*uk_28 + 2814*uk_29 + 67*uk_3 + 1764*uk_30 + 504*uk_31 + 7938*uk_32 + 9450*uk_33 + 1764*uk_34 + 144*uk_35 + 2268*uk_36 + 2700*uk_37 + 504*uk_38 + 35721*uk_39 + 42*uk_4 + 42525*uk_40 + 7938*uk_41 + 50625*uk_42 + 9450*uk_43 + 1764*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 172351936387*uk_47 + 108041512362*uk_48 + 30869003532*uk_49 + 12*uk_5 + 486186805629*uk_50 + 578793816225*uk_51 + 108041512362*uk_52 + 153424975*uk_53 + 186899515*uk_54 + 117160890*uk_55 + 33474540*uk_56 + 527224005*uk_57 + 627647625*uk_58 + 117160890*uk_59 + 189*uk_6 + 227677591*uk_60 + 142723266*uk_61 + 40778076*uk_62 + 642254697*uk_63 + 764588925*uk_64 + 142723266*uk_65 + 89468316*uk_66 + 25562376*uk_67 + 402607422*uk_68 + 479294550*uk_69 + 225*uk_7 + 89468316*uk_70 + 7303536*uk_71 + 115030692*uk_72 + 136941300*uk_73 + 25562376*uk_74 + 1811733399*uk_75 + 2156825475*uk_76 + 402607422*uk_77 + 2567649375*uk_78 + 479294550*uk_79 + 42*uk_8 + 89468316*uk_80 + 166375*uk_81 + 202675*uk_82 + 127050*uk_83 + 36300*uk_84 + 571725*uk_85 + 680625*uk_86 + 127050*uk_87 + 246895*uk_88 + 154770*uk_89 + 2572416961*uk_9 + 44220*uk_90 + 696465*uk_91 + 829125*uk_92 + 154770*uk_93 + 97020*uk_94 + 27720*uk_95 + 436590*uk_96 + 519750*uk_97 + 97020*uk_98 + 7920*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 126060*uk_100 + 148500*uk_101 + 44220*uk_102 + 2006455*uk_103 + 2363625*uk_104 + 703835*uk_105 + 2784375*uk_106 + 829125*uk_107 + 246895*uk_108 + 1124864*uk_109 + 5274776*uk_11 + 724672*uk_110 + 129792*uk_111 + 2065856*uk_112 + 2433600*uk_113 + 724672*uk_114 + 466856*uk_115 + 83616*uk_116 + 1330888*uk_117 + 1567800*uk_118 + 466856*uk_119 + 3398173*uk_12 + 14976*uk_120 + 238368*uk_121 + 280800*uk_122 + 83616*uk_123 + 3794024*uk_124 + 4469400*uk_125 + 1330888*uk_126 + 5265000*uk_127 + 1567800*uk_128 + 466856*uk_129 + 608628*uk_13 + 300763*uk_130 + 53868*uk_131 + 857399*uk_132 + 1010025*uk_133 + 300763*uk_134 + 9648*uk_135 + 153564*uk_136 + 180900*uk_137 + 53868*uk_138 + 2444227*uk_139 + 9687329*uk_14 + 2879325*uk_140 + 857399*uk_141 + 3391875*uk_142 + 1010025*uk_143 + 300763*uk_144 + 1728*uk_145 + 27504*uk_146 + 32400*uk_147 + 9648*uk_148 + 437772*uk_149 + 11411775*uk_15 + 515700*uk_150 + 153564*uk_151 + 607500*uk_152 + 180900*uk_153 + 53868*uk_154 + 6967871*uk_155 + 8208225*uk_156 + 2444227*uk_157 + 9669375*uk_158 + 2879325*uk_159 + 3398173*uk_16 + 857399*uk_160 + 11390625*uk_161 + 3391875*uk_162 + 1010025*uk_163 + 300763*uk_164 + 3025*uk_17 + 5720*uk_18 + 3685*uk_19 + 55*uk_2 + 660*uk_20 + 10505*uk_21 + 12375*uk_22 + 3685*uk_23 + 10816*uk_24 + 6968*uk_25 + 1248*uk_26 + 19864*uk_27 + 23400*uk_28 + 6968*uk_29 + 104*uk_3 + 4489*uk_30 + 804*uk_31 + 12797*uk_32 + 15075*uk_33 + 4489*uk_34 + 144*uk_35 + 2292*uk_36 + 2700*uk_37 + 804*uk_38 + 36481*uk_39 + 67*uk_4 + 42975*uk_40 + 12797*uk_41 + 50625*uk_42 + 15075*uk_43 + 4489*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 267531363944*uk_47 + 172351936387*uk_48 + 30869003532*uk_49 + 12*uk_5 + 491331639551*uk_50 + 578793816225*uk_51 + 172351936387*uk_52 + 153424975*uk_53 + 290112680*uk_54 + 186899515*uk_55 + 33474540*uk_56 + 532803095*uk_57 + 627647625*uk_58 + 186899515*uk_59 + 191*uk_6 + 548576704*uk_60 + 353409992*uk_61 + 63297312*uk_62 + 1007482216*uk_63 + 1186824600*uk_64 + 353409992*uk_65 + 227677591*uk_66 + 40778076*uk_67 + 649051043*uk_68 + 764588925*uk_69 + 225*uk_7 + 227677591*uk_70 + 7303536*uk_71 + 116247948*uk_72 + 136941300*uk_73 + 40778076*uk_74 + 1850279839*uk_75 + 2179649025*uk_76 + 649051043*uk_77 + 2567649375*uk_78 + 764588925*uk_79 + 67*uk_8 + 227677591*uk_80 + 166375*uk_81 + 314600*uk_82 + 202675*uk_83 + 36300*uk_84 + 577775*uk_85 + 680625*uk_86 + 202675*uk_87 + 594880*uk_88 + 383240*uk_89 + 2572416961*uk_9 + 68640*uk_90 + 1092520*uk_91 + 1287000*uk_92 + 383240*uk_93 + 246895*uk_94 + 44220*uk_95 + 703835*uk_96 + 829125*uk_97 + 246895*uk_98 + 7920*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 127380*uk_100 + 148500*uk_101 + 68640*uk_102 + 2048695*uk_103 + 2388375*uk_104 + 1103960*uk_105 + 2784375*uk_106 + 1287000*uk_107 + 594880*uk_108 + 3581577*uk_109 + 7760007*uk_11 + 2434536*uk_110 + 280908*uk_111 + 4517937*uk_112 + 5267025*uk_113 + 2434536*uk_114 + 1654848*uk_115 + 190944*uk_116 + 3071016*uk_117 + 3580200*uk_118 + 1654848*uk_119 + 5274776*uk_12 + 22032*uk_120 + 354348*uk_121 + 413100*uk_122 + 190944*uk_123 + 5699097*uk_124 + 6644025*uk_125 + 3071016*uk_126 + 7745625*uk_127 + 3580200*uk_128 + 1654848*uk_129 + 608628*uk_13 + 1124864*uk_130 + 129792*uk_131 + 2087488*uk_132 + 2433600*uk_133 + 1124864*uk_134 + 14976*uk_135 + 240864*uk_136 + 280800*uk_137 + 129792*uk_138 + 3873896*uk_139 + 9788767*uk_14 + 4516200*uk_140 + 2087488*uk_141 + 5265000*uk_142 + 2433600*uk_143 + 1124864*uk_144 + 1728*uk_145 + 27792*uk_146 + 32400*uk_147 + 14976*uk_148 + 446988*uk_149 + 11411775*uk_15 + 521100*uk_150 + 240864*uk_151 + 607500*uk_152 + 280800*uk_153 + 129792*uk_154 + 7189057*uk_155 + 8381025*uk_156 + 3873896*uk_157 + 9770625*uk_158 + 4516200*uk_159 + 5274776*uk_16 + 2087488*uk_160 + 11390625*uk_161 + 5265000*uk_162 + 2433600*uk_163 + 1124864*uk_164 + 3025*uk_17 + 8415*uk_18 + 5720*uk_19 + 55*uk_2 + 660*uk_20 + 10615*uk_21 + 12375*uk_22 + 5720*uk_23 + 23409*uk_24 + 15912*uk_25 + 1836*uk_26 + 29529*uk_27 + 34425*uk_28 + 15912*uk_29 + 153*uk_3 + 10816*uk_30 + 1248*uk_31 + 20072*uk_32 + 23400*uk_33 + 10816*uk_34 + 144*uk_35 + 2316*uk_36 + 2700*uk_37 + 1248*uk_38 + 37249*uk_39 + 104*uk_4 + 43425*uk_40 + 20072*uk_41 + 50625*uk_42 + 23400*uk_43 + 10816*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 393579795033*uk_47 + 267531363944*uk_48 + 30869003532*uk_49 + 12*uk_5 + 496476473473*uk_50 + 578793816225*uk_51 + 267531363944*uk_52 + 153424975*uk_53 + 426800385*uk_54 + 290112680*uk_55 + 33474540*uk_56 + 538382185*uk_57 + 627647625*uk_58 + 290112680*uk_59 + 193*uk_6 + 1187281071*uk_60 + 807040728*uk_61 + 93120084*uk_62 + 1497681351*uk_63 + 1746001575*uk_64 + 807040728*uk_65 + 548576704*uk_66 + 63297312*uk_67 + 1018031768*uk_68 + 1186824600*uk_69 + 225*uk_7 + 548576704*uk_70 + 7303536*uk_71 + 117465204*uk_72 + 136941300*uk_73 + 63297312*uk_74 + 1889232031*uk_75 + 2202472575*uk_76 + 1018031768*uk_77 + 2567649375*uk_78 + 1186824600*uk_79 + 104*uk_8 + 548576704*uk_80 + 166375*uk_81 + 462825*uk_82 + 314600*uk_83 + 36300*uk_84 + 583825*uk_85 + 680625*uk_86 + 314600*uk_87 + 1287495*uk_88 + 875160*uk_89 + 2572416961*uk_9 + 100980*uk_90 + 1624095*uk_91 + 1893375*uk_92 + 875160*uk_93 + 594880*uk_94 + 68640*uk_95 + 1103960*uk_96 + 1287000*uk_97 + 594880*uk_98 + 7920*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 85800*uk_100 + 99000*uk_101 + 67320*uk_102 + 2091375*uk_103 + 2413125*uk_104 + 1640925*uk_105 + 2784375*uk_106 + 1893375*uk_107 + 1287495*uk_108 + 6859*uk_109 + 963661*uk_11 + 55233*uk_110 + 2888*uk_111 + 70395*uk_112 + 81225*uk_113 + 55233*uk_114 + 444771*uk_115 + 23256*uk_116 + 566865*uk_117 + 654075*uk_118 + 444771*uk_119 + 7760007*uk_12 + 1216*uk_120 + 29640*uk_121 + 34200*uk_122 + 23256*uk_123 + 722475*uk_124 + 833625*uk_125 + 566865*uk_126 + 961875*uk_127 + 654075*uk_128 + 444771*uk_129 + 405752*uk_13 + 3581577*uk_130 + 187272*uk_131 + 4564755*uk_132 + 5267025*uk_133 + 3581577*uk_134 + 9792*uk_135 + 238680*uk_136 + 275400*uk_137 + 187272*uk_138 + 5817825*uk_139 + 9890205*uk_14 + 6712875*uk_140 + 4564755*uk_141 + 7745625*uk_142 + 5267025*uk_143 + 3581577*uk_144 + 512*uk_145 + 12480*uk_146 + 14400*uk_147 + 9792*uk_148 + 304200*uk_149 + 11411775*uk_15 + 351000*uk_150 + 238680*uk_151 + 405000*uk_152 + 275400*uk_153 + 187272*uk_154 + 7414875*uk_155 + 8555625*uk_156 + 5817825*uk_157 + 9871875*uk_158 + 6712875*uk_159 + 7760007*uk_16 + 4564755*uk_160 + 11390625*uk_161 + 7745625*uk_162 + 5267025*uk_163 + 3581577*uk_164 + 3025*uk_17 + 1045*uk_18 + 8415*uk_19 + 55*uk_2 + 440*uk_20 + 10725*uk_21 + 12375*uk_22 + 8415*uk_23 + 361*uk_24 + 2907*uk_25 + 152*uk_26 + 3705*uk_27 + 4275*uk_28 + 2907*uk_29 + 19*uk_3 + 23409*uk_30 + 1224*uk_31 + 29835*uk_32 + 34425*uk_33 + 23409*uk_34 + 64*uk_35 + 1560*uk_36 + 1800*uk_37 + 1224*uk_38 + 38025*uk_39 + 153*uk_4 + 43875*uk_40 + 29835*uk_41 + 50625*uk_42 + 34425*uk_43 + 23409*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 48875922259*uk_47 + 393579795033*uk_48 + 20579335688*uk_49 + 8*uk_5 + 501621307395*uk_50 + 578793816225*uk_51 + 393579795033*uk_52 + 153424975*uk_53 + 53001355*uk_54 + 426800385*uk_55 + 22316360*uk_56 + 543961275*uk_57 + 627647625*uk_58 + 426800385*uk_59 + 195*uk_6 + 18309559*uk_60 + 147440133*uk_61 + 7709288*uk_62 + 187913895*uk_63 + 216823725*uk_64 + 147440133*uk_65 + 1187281071*uk_66 + 62080056*uk_67 + 1513201365*uk_68 + 1746001575*uk_69 + 225*uk_7 + 1187281071*uk_70 + 3246016*uk_71 + 79121640*uk_72 + 91294200*uk_73 + 62080056*uk_74 + 1928589975*uk_75 + 2225296125*uk_76 + 1513201365*uk_77 + 2567649375*uk_78 + 1746001575*uk_79 + 153*uk_8 + 1187281071*uk_80 + 166375*uk_81 + 57475*uk_82 + 462825*uk_83 + 24200*uk_84 + 589875*uk_85 + 680625*uk_86 + 462825*uk_87 + 19855*uk_88 + 159885*uk_89 + 2572416961*uk_9 + 8360*uk_90 + 203775*uk_91 + 235125*uk_92 + 159885*uk_93 + 1287495*uk_94 + 67320*uk_95 + 1640925*uk_96 + 1893375*uk_97 + 1287495*uk_98 + 3520*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 130020*uk_100 + 148500*uk_101 + 12540*uk_102 + 2134495*uk_103 + 2437875*uk_104 + 205865*uk_105 + 2784375*uk_106 + 235125*uk_107 + 19855*uk_108 + 729000*uk_109 + 4564710*uk_11 + 153900*uk_110 + 97200*uk_111 + 1595700*uk_112 + 1822500*uk_113 + 153900*uk_114 + 32490*uk_115 + 20520*uk_116 + 336870*uk_117 + 384750*uk_118 + 32490*uk_119 + 963661*uk_12 + 12960*uk_120 + 212760*uk_121 + 243000*uk_122 + 20520*uk_123 + 3492810*uk_124 + 3989250*uk_125 + 336870*uk_126 + 4556250*uk_127 + 384750*uk_128 + 32490*uk_129 + 608628*uk_13 + 6859*uk_130 + 4332*uk_131 + 71117*uk_132 + 81225*uk_133 + 6859*uk_134 + 2736*uk_135 + 44916*uk_136 + 51300*uk_137 + 4332*uk_138 + 737371*uk_139 + 9991643*uk_14 + 842175*uk_140 + 71117*uk_141 + 961875*uk_142 + 81225*uk_143 + 6859*uk_144 + 1728*uk_145 + 28368*uk_146 + 32400*uk_147 + 2736*uk_148 + 465708*uk_149 + 11411775*uk_15 + 531900*uk_150 + 44916*uk_151 + 607500*uk_152 + 51300*uk_153 + 4332*uk_154 + 7645373*uk_155 + 8732025*uk_156 + 737371*uk_157 + 9973125*uk_158 + 842175*uk_159 + 963661*uk_16 + 71117*uk_160 + 11390625*uk_161 + 961875*uk_162 + 81225*uk_163 + 6859*uk_164 + 3025*uk_17 + 4950*uk_18 + 1045*uk_19 + 55*uk_2 + 660*uk_20 + 10835*uk_21 + 12375*uk_22 + 1045*uk_23 + 8100*uk_24 + 1710*uk_25 + 1080*uk_26 + 17730*uk_27 + 20250*uk_28 + 1710*uk_29 + 90*uk_3 + 361*uk_30 + 228*uk_31 + 3743*uk_32 + 4275*uk_33 + 361*uk_34 + 144*uk_35 + 2364*uk_36 + 2700*uk_37 + 228*uk_38 + 38809*uk_39 + 19*uk_4 + 44325*uk_40 + 3743*uk_41 + 50625*uk_42 + 4275*uk_43 + 361*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 231517526490*uk_47 + 48875922259*uk_48 + 30869003532*uk_49 + 12*uk_5 + 506766141317*uk_50 + 578793816225*uk_51 + 48875922259*uk_52 + 153424975*uk_53 + 251059050*uk_54 + 53001355*uk_55 + 33474540*uk_56 + 549540365*uk_57 + 627647625*uk_58 + 53001355*uk_59 + 197*uk_6 + 410823900*uk_60 + 86729490*uk_61 + 54776520*uk_62 + 899247870*uk_63 + 1027059750*uk_64 + 86729490*uk_65 + 18309559*uk_66 + 11563932*uk_67 + 189841217*uk_68 + 216823725*uk_69 + 225*uk_7 + 18309559*uk_70 + 7303536*uk_71 + 119899716*uk_72 + 136941300*uk_73 + 11563932*uk_74 + 1968353671*uk_75 + 2248119675*uk_76 + 189841217*uk_77 + 2567649375*uk_78 + 216823725*uk_79 + 19*uk_8 + 18309559*uk_80 + 166375*uk_81 + 272250*uk_82 + 57475*uk_83 + 36300*uk_84 + 595925*uk_85 + 680625*uk_86 + 57475*uk_87 + 445500*uk_88 + 94050*uk_89 + 2572416961*uk_9 + 59400*uk_90 + 975150*uk_91 + 1113750*uk_92 + 94050*uk_93 + 19855*uk_94 + 12540*uk_95 + 205865*uk_96 + 235125*uk_97 + 19855*uk_98 + 7920*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 131340*uk_100 + 148500*uk_101 + 59400*uk_102 + 2178055*uk_103 + 2462625*uk_104 + 985050*uk_105 + 2784375*uk_106 + 1113750*uk_107 + 445500*uk_108 + 5177717*uk_109 + 8774387*uk_11 + 2693610*uk_110 + 359148*uk_111 + 5955871*uk_112 + 6734025*uk_113 + 2693610*uk_114 + 1401300*uk_115 + 186840*uk_116 + 3098430*uk_117 + 3503250*uk_118 + 1401300*uk_119 + 4564710*uk_12 + 24912*uk_120 + 413124*uk_121 + 467100*uk_122 + 186840*uk_123 + 6850973*uk_124 + 7746075*uk_125 + 3098430*uk_126 + 8758125*uk_127 + 3503250*uk_128 + 1401300*uk_129 + 608628*uk_13 + 729000*uk_130 + 97200*uk_131 + 1611900*uk_132 + 1822500*uk_133 + 729000*uk_134 + 12960*uk_135 + 214920*uk_136 + 243000*uk_137 + 97200*uk_138 + 3564090*uk_139 + 10093081*uk_14 + 4029750*uk_140 + 1611900*uk_141 + 4556250*uk_142 + 1822500*uk_143 + 729000*uk_144 + 1728*uk_145 + 28656*uk_146 + 32400*uk_147 + 12960*uk_148 + 475212*uk_149 + 11411775*uk_15 + 537300*uk_150 + 214920*uk_151 + 607500*uk_152 + 243000*uk_153 + 97200*uk_154 + 7880599*uk_155 + 8910225*uk_156 + 3564090*uk_157 + 10074375*uk_158 + 4029750*uk_159 + 4564710*uk_16 + 1611900*uk_160 + 11390625*uk_161 + 4556250*uk_162 + 1822500*uk_163 + 729000*uk_164 + 3025*uk_17 + 9515*uk_18 + 4950*uk_19 + 55*uk_2 + 660*uk_20 + 10945*uk_21 + 12375*uk_22 + 4950*uk_23 + 29929*uk_24 + 15570*uk_25 + 2076*uk_26 + 34427*uk_27 + 38925*uk_28 + 15570*uk_29 + 173*uk_3 + 8100*uk_30 + 1080*uk_31 + 17910*uk_32 + 20250*uk_33 + 8100*uk_34 + 144*uk_35 + 2388*uk_36 + 2700*uk_37 + 1080*uk_38 + 39601*uk_39 + 90*uk_4 + 44775*uk_40 + 17910*uk_41 + 50625*uk_42 + 20250*uk_43 + 8100*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 445028134253*uk_47 + 231517526490*uk_48 + 30869003532*uk_49 + 12*uk_5 + 511910975239*uk_50 + 578793816225*uk_51 + 231517526490*uk_52 + 153424975*uk_53 + 482591285*uk_54 + 251059050*uk_55 + 33474540*uk_56 + 555119455*uk_57 + 627647625*uk_58 + 251059050*uk_59 + 199*uk_6 + 1517968951*uk_60 + 789694830*uk_61 + 105292644*uk_62 + 1746103013*uk_63 + 1974237075*uk_64 + 789694830*uk_65 + 410823900*uk_66 + 54776520*uk_67 + 908377290*uk_68 + 1027059750*uk_69 + 225*uk_7 + 410823900*uk_70 + 7303536*uk_71 + 121116972*uk_72 + 136941300*uk_73 + 54776520*uk_74 + 2008523119*uk_75 + 2270943225*uk_76 + 908377290*uk_77 + 2567649375*uk_78 + 1027059750*uk_79 + 90*uk_8 + 410823900*uk_80 + 166375*uk_81 + 523325*uk_82 + 272250*uk_83 + 36300*uk_84 + 601975*uk_85 + 680625*uk_86 + 272250*uk_87 + 1646095*uk_88 + 856350*uk_89 + 2572416961*uk_9 + 114180*uk_90 + 1893485*uk_91 + 2140875*uk_92 + 856350*uk_93 + 445500*uk_94 + 59400*uk_95 + 985050*uk_96 + 1113750*uk_97 + 445500*uk_98 + 7920*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 88440*uk_100 + 99000*uk_101 + 76120*uk_102 + 2222055*uk_103 + 2487375*uk_104 + 1912515*uk_105 + 2784375*uk_106 + 2140875*uk_107 + 1646095*uk_108 + 300763*uk_109 + 3398173*uk_11 + 776597*uk_110 + 35912*uk_111 + 902289*uk_112 + 1010025*uk_113 + 776597*uk_114 + 2005243*uk_115 + 92728*uk_116 + 2329791*uk_117 + 2607975*uk_118 + 2005243*uk_119 + 8774387*uk_12 + 4288*uk_120 + 107736*uk_121 + 120600*uk_122 + 92728*uk_123 + 2706867*uk_124 + 3030075*uk_125 + 2329791*uk_126 + 3391875*uk_127 + 2607975*uk_128 + 2005243*uk_129 + 405752*uk_13 + 5177717*uk_130 + 239432*uk_131 + 6015729*uk_132 + 6734025*uk_133 + 5177717*uk_134 + 11072*uk_135 + 278184*uk_136 + 311400*uk_137 + 239432*uk_138 + 6989373*uk_139 + 10194519*uk_14 + 7823925*uk_140 + 6015729*uk_141 + 8758125*uk_142 + 6734025*uk_143 + 5177717*uk_144 + 512*uk_145 + 12864*uk_146 + 14400*uk_147 + 11072*uk_148 + 323208*uk_149 + 11411775*uk_15 + 361800*uk_150 + 278184*uk_151 + 405000*uk_152 + 311400*uk_153 + 239432*uk_154 + 8120601*uk_155 + 9090225*uk_156 + 6989373*uk_157 + 10175625*uk_158 + 7823925*uk_159 + 8774387*uk_16 + 6015729*uk_160 + 11390625*uk_161 + 8758125*uk_162 + 6734025*uk_163 + 5177717*uk_164 + 3025*uk_17 + 3685*uk_18 + 9515*uk_19 + 55*uk_2 + 440*uk_20 + 11055*uk_21 + 12375*uk_22 + 9515*uk_23 + 4489*uk_24 + 11591*uk_25 + 536*uk_26 + 13467*uk_27 + 15075*uk_28 + 11591*uk_29 + 67*uk_3 + 29929*uk_30 + 1384*uk_31 + 34773*uk_32 + 38925*uk_33 + 29929*uk_34 + 64*uk_35 + 1608*uk_36 + 1800*uk_37 + 1384*uk_38 + 40401*uk_39 + 173*uk_4 + 45225*uk_40 + 34773*uk_41 + 50625*uk_42 + 38925*uk_43 + 29929*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 172351936387*uk_47 + 445028134253*uk_48 + 20579335688*uk_49 + 8*uk_5 + 517055809161*uk_50 + 578793816225*uk_51 + 445028134253*uk_52 + 153424975*uk_53 + 186899515*uk_54 + 482591285*uk_55 + 22316360*uk_56 + 560698545*uk_57 + 627647625*uk_58 + 482591285*uk_59 + 201*uk_6 + 227677591*uk_60 + 587883929*uk_61 + 27185384*uk_62 + 683032773*uk_63 + 764588925*uk_64 + 587883929*uk_65 + 1517968951*uk_66 + 70195096*uk_67 + 1763651787*uk_68 + 1974237075*uk_69 + 225*uk_7 + 1517968951*uk_70 + 3246016*uk_71 + 81556152*uk_72 + 91294200*uk_73 + 70195096*uk_74 + 2049098319*uk_75 + 2293766775*uk_76 + 1763651787*uk_77 + 2567649375*uk_78 + 1974237075*uk_79 + 173*uk_8 + 1517968951*uk_80 + 166375*uk_81 + 202675*uk_82 + 523325*uk_83 + 24200*uk_84 + 608025*uk_85 + 680625*uk_86 + 523325*uk_87 + 246895*uk_88 + 637505*uk_89 + 2572416961*uk_9 + 29480*uk_90 + 740685*uk_91 + 829125*uk_92 + 637505*uk_93 + 1646095*uk_94 + 76120*uk_95 + 1912515*uk_96 + 2140875*uk_97 + 1646095*uk_98 + 3520*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 133980*uk_100 + 148500*uk_101 + 44220*uk_102 + 2266495*uk_103 + 2512125*uk_104 + 748055*uk_105 + 2784375*uk_106 + 829125*uk_107 + 246895*uk_108 + 5088448*uk_109 + 8723668*uk_11 + 1982128*uk_110 + 355008*uk_111 + 6005552*uk_112 + 6656400*uk_113 + 1982128*uk_114 + 772108*uk_115 + 138288*uk_116 + 2339372*uk_117 + 2592900*uk_118 + 772108*uk_119 + 3398173*uk_12 + 24768*uk_120 + 418992*uk_121 + 464400*uk_122 + 138288*uk_123 + 7087948*uk_124 + 7856100*uk_125 + 2339372*uk_126 + 8707500*uk_127 + 2592900*uk_128 + 772108*uk_129 + 608628*uk_13 + 300763*uk_130 + 53868*uk_131 + 911267*uk_132 + 1010025*uk_133 + 300763*uk_134 + 9648*uk_135 + 163212*uk_136 + 180900*uk_137 + 53868*uk_138 + 2761003*uk_139 + 10295957*uk_14 + 3060225*uk_140 + 911267*uk_141 + 3391875*uk_142 + 1010025*uk_143 + 300763*uk_144 + 1728*uk_145 + 29232*uk_146 + 32400*uk_147 + 9648*uk_148 + 494508*uk_149 + 11411775*uk_15 + 548100*uk_150 + 163212*uk_151 + 607500*uk_152 + 180900*uk_153 + 53868*uk_154 + 8365427*uk_155 + 9272025*uk_156 + 2761003*uk_157 + 10276875*uk_158 + 3060225*uk_159 + 3398173*uk_16 + 911267*uk_160 + 11390625*uk_161 + 3391875*uk_162 + 1010025*uk_163 + 300763*uk_164 + 3025*uk_17 + 9460*uk_18 + 3685*uk_19 + 55*uk_2 + 660*uk_20 + 11165*uk_21 + 12375*uk_22 + 3685*uk_23 + 29584*uk_24 + 11524*uk_25 + 2064*uk_26 + 34916*uk_27 + 38700*uk_28 + 11524*uk_29 + 172*uk_3 + 4489*uk_30 + 804*uk_31 + 13601*uk_32 + 15075*uk_33 + 4489*uk_34 + 144*uk_35 + 2436*uk_36 + 2700*uk_37 + 804*uk_38 + 41209*uk_39 + 67*uk_4 + 45675*uk_40 + 13601*uk_41 + 50625*uk_42 + 15075*uk_43 + 4489*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 442455717292*uk_47 + 172351936387*uk_48 + 30869003532*uk_49 + 12*uk_5 + 522200643083*uk_50 + 578793816225*uk_51 + 172351936387*uk_52 + 153424975*uk_53 + 479801740*uk_54 + 186899515*uk_55 + 33474540*uk_56 + 566277635*uk_57 + 627647625*uk_58 + 186899515*uk_59 + 203*uk_6 + 1500470896*uk_60 + 584485756*uk_61 + 104684016*uk_62 + 1770904604*uk_63 + 1962825300*uk_64 + 584485756*uk_65 + 227677591*uk_66 + 40778076*uk_67 + 689829119*uk_68 + 764588925*uk_69 + 225*uk_7 + 227677591*uk_70 + 7303536*uk_71 + 123551484*uk_72 + 136941300*uk_73 + 40778076*uk_74 + 2090079271*uk_75 + 2316590325*uk_76 + 689829119*uk_77 + 2567649375*uk_78 + 764588925*uk_79 + 67*uk_8 + 227677591*uk_80 + 166375*uk_81 + 520300*uk_82 + 202675*uk_83 + 36300*uk_84 + 614075*uk_85 + 680625*uk_86 + 202675*uk_87 + 1627120*uk_88 + 633820*uk_89 + 2572416961*uk_9 + 113520*uk_90 + 1920380*uk_91 + 2128500*uk_92 + 633820*uk_93 + 246895*uk_94 + 44220*uk_95 + 748055*uk_96 + 829125*uk_97 + 246895*uk_98 + 7920*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 90200*uk_100 + 99000*uk_101 + 75680*uk_102 + 2311375*uk_103 + 2536875*uk_104 + 1939300*uk_105 + 2784375*uk_106 + 2128500*uk_107 + 1627120*uk_108 + 592704*uk_109 + 4260396*uk_11 + 1213632*uk_110 + 56448*uk_111 + 1446480*uk_112 + 1587600*uk_113 + 1213632*uk_114 + 2485056*uk_115 + 115584*uk_116 + 2961840*uk_117 + 3250800*uk_118 + 2485056*uk_119 + 8723668*uk_12 + 5376*uk_120 + 137760*uk_121 + 151200*uk_122 + 115584*uk_123 + 3530100*uk_124 + 3874500*uk_125 + 2961840*uk_126 + 4252500*uk_127 + 3250800*uk_128 + 2485056*uk_129 + 405752*uk_13 + 5088448*uk_130 + 236672*uk_131 + 6064720*uk_132 + 6656400*uk_133 + 5088448*uk_134 + 11008*uk_135 + 282080*uk_136 + 309600*uk_137 + 236672*uk_138 + 7228300*uk_139 + 10397395*uk_14 + 7933500*uk_140 + 6064720*uk_141 + 8707500*uk_142 + 6656400*uk_143 + 5088448*uk_144 + 512*uk_145 + 13120*uk_146 + 14400*uk_147 + 11008*uk_148 + 336200*uk_149 + 11411775*uk_15 + 369000*uk_150 + 282080*uk_151 + 405000*uk_152 + 309600*uk_153 + 236672*uk_154 + 8615125*uk_155 + 9455625*uk_156 + 7228300*uk_157 + 10378125*uk_158 + 7933500*uk_159 + 8723668*uk_16 + 6064720*uk_160 + 11390625*uk_161 + 8707500*uk_162 + 6656400*uk_163 + 5088448*uk_164 + 3025*uk_17 + 4620*uk_18 + 9460*uk_19 + 55*uk_2 + 440*uk_20 + 11275*uk_21 + 12375*uk_22 + 9460*uk_23 + 7056*uk_24 + 14448*uk_25 + 672*uk_26 + 17220*uk_27 + 18900*uk_28 + 14448*uk_29 + 84*uk_3 + 29584*uk_30 + 1376*uk_31 + 35260*uk_32 + 38700*uk_33 + 29584*uk_34 + 64*uk_35 + 1640*uk_36 + 1800*uk_37 + 1376*uk_38 + 42025*uk_39 + 172*uk_4 + 46125*uk_40 + 35260*uk_41 + 50625*uk_42 + 38700*uk_43 + 29584*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 216083024724*uk_47 + 442455717292*uk_48 + 20579335688*uk_49 + 8*uk_5 + 527345477005*uk_50 + 578793816225*uk_51 + 442455717292*uk_52 + 153424975*uk_53 + 234321780*uk_54 + 479801740*uk_55 + 22316360*uk_56 + 571856725*uk_57 + 627647625*uk_58 + 479801740*uk_59 + 205*uk_6 + 357873264*uk_60 + 732788112*uk_61 + 34083168*uk_62 + 873381180*uk_63 + 958589100*uk_64 + 732788112*uk_65 + 1500470896*uk_66 + 69789344*uk_67 + 1788351940*uk_68 + 1962825300*uk_69 + 225*uk_7 + 1500470896*uk_70 + 3246016*uk_71 + 83179160*uk_72 + 91294200*uk_73 + 69789344*uk_74 + 2131465975*uk_75 + 2339413875*uk_76 + 1788351940*uk_77 + 2567649375*uk_78 + 1962825300*uk_79 + 172*uk_8 + 1500470896*uk_80 + 166375*uk_81 + 254100*uk_82 + 520300*uk_83 + 24200*uk_84 + 620125*uk_85 + 680625*uk_86 + 520300*uk_87 + 388080*uk_88 + 794640*uk_89 + 2572416961*uk_9 + 36960*uk_90 + 947100*uk_91 + 1039500*uk_92 + 794640*uk_93 + 1627120*uk_94 + 75680*uk_95 + 1939300*uk_96 + 2128500*uk_97 + 1627120*uk_98 + 3520*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 91080*uk_100 + 99000*uk_101 + 36960*uk_102 + 2356695*uk_103 + 2561625*uk_104 + 956340*uk_105 + 2784375*uk_106 + 1039500*uk_107 + 388080*uk_108 + 64*uk_109 + 202876*uk_11 + 1344*uk_110 + 128*uk_111 + 3312*uk_112 + 3600*uk_113 + 1344*uk_114 + 28224*uk_115 + 2688*uk_116 + 69552*uk_117 + 75600*uk_118 + 28224*uk_119 + 4260396*uk_12 + 256*uk_120 + 6624*uk_121 + 7200*uk_122 + 2688*uk_123 + 171396*uk_124 + 186300*uk_125 + 69552*uk_126 + 202500*uk_127 + 75600*uk_128 + 28224*uk_129 + 405752*uk_13 + 592704*uk_130 + 56448*uk_131 + 1460592*uk_132 + 1587600*uk_133 + 592704*uk_134 + 5376*uk_135 + 139104*uk_136 + 151200*uk_137 + 56448*uk_138 + 3599316*uk_139 + 10498833*uk_14 + 3912300*uk_140 + 1460592*uk_141 + 4252500*uk_142 + 1587600*uk_143 + 592704*uk_144 + 512*uk_145 + 13248*uk_146 + 14400*uk_147 + 5376*uk_148 + 342792*uk_149 + 11411775*uk_15 + 372600*uk_150 + 139104*uk_151 + 405000*uk_152 + 151200*uk_153 + 56448*uk_154 + 8869743*uk_155 + 9641025*uk_156 + 3599316*uk_157 + 10479375*uk_158 + 3912300*uk_159 + 4260396*uk_16 + 1460592*uk_160 + 11390625*uk_161 + 4252500*uk_162 + 1587600*uk_163 + 592704*uk_164 + 3025*uk_17 + 220*uk_18 + 4620*uk_19 + 55*uk_2 + 440*uk_20 + 11385*uk_21 + 12375*uk_22 + 4620*uk_23 + 16*uk_24 + 336*uk_25 + 32*uk_26 + 828*uk_27 + 900*uk_28 + 336*uk_29 + 4*uk_3 + 7056*uk_30 + 672*uk_31 + 17388*uk_32 + 18900*uk_33 + 7056*uk_34 + 64*uk_35 + 1656*uk_36 + 1800*uk_37 + 672*uk_38 + 42849*uk_39 + 84*uk_4 + 46575*uk_40 + 17388*uk_41 + 50625*uk_42 + 18900*uk_43 + 7056*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 10289667844*uk_47 + 216083024724*uk_48 + 20579335688*uk_49 + 8*uk_5 + 532490310927*uk_50 + 578793816225*uk_51 + 216083024724*uk_52 + 153424975*uk_53 + 11158180*uk_54 + 234321780*uk_55 + 22316360*uk_56 + 577435815*uk_57 + 627647625*uk_58 + 234321780*uk_59 + 207*uk_6 + 811504*uk_60 + 17041584*uk_61 + 1623008*uk_62 + 41995332*uk_63 + 45647100*uk_64 + 17041584*uk_65 + 357873264*uk_66 + 34083168*uk_67 + 881901972*uk_68 + 958589100*uk_69 + 225*uk_7 + 357873264*uk_70 + 3246016*uk_71 + 83990664*uk_72 + 91294200*uk_73 + 34083168*uk_74 + 2173258431*uk_75 + 2362237425*uk_76 + 881901972*uk_77 + 2567649375*uk_78 + 958589100*uk_79 + 84*uk_8 + 357873264*uk_80 + 166375*uk_81 + 12100*uk_82 + 254100*uk_83 + 24200*uk_84 + 626175*uk_85 + 680625*uk_86 + 254100*uk_87 + 880*uk_88 + 18480*uk_89 + 2572416961*uk_9 + 1760*uk_90 + 45540*uk_91 + 49500*uk_92 + 18480*uk_93 + 388080*uk_94 + 36960*uk_95 + 956340*uk_96 + 1039500*uk_97 + 388080*uk_98 + 3520*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 137940*uk_100 + 148500*uk_101 + 2640*uk_102 + 2402455*uk_103 + 2586375*uk_104 + 45980*uk_105 + 2784375*uk_106 + 49500*uk_107 + 880*uk_108 + 2803221*uk_109 + 7151379*uk_11 + 79524*uk_110 + 238572*uk_111 + 4155129*uk_112 + 4473225*uk_113 + 79524*uk_114 + 2256*uk_115 + 6768*uk_116 + 117876*uk_117 + 126900*uk_118 + 2256*uk_119 + 202876*uk_12 + 20304*uk_120 + 353628*uk_121 + 380700*uk_122 + 6768*uk_123 + 6159021*uk_124 + 6630525*uk_125 + 117876*uk_126 + 7138125*uk_127 + 126900*uk_128 + 2256*uk_129 + 608628*uk_13 + 64*uk_130 + 192*uk_131 + 3344*uk_132 + 3600*uk_133 + 64*uk_134 + 576*uk_135 + 10032*uk_136 + 10800*uk_137 + 192*uk_138 + 174724*uk_139 + 10600271*uk_14 + 188100*uk_140 + 3344*uk_141 + 202500*uk_142 + 3600*uk_143 + 64*uk_144 + 1728*uk_145 + 30096*uk_146 + 32400*uk_147 + 576*uk_148 + 524172*uk_149 + 11411775*uk_15 + 564300*uk_150 + 10032*uk_151 + 607500*uk_152 + 10800*uk_153 + 192*uk_154 + 9129329*uk_155 + 9828225*uk_156 + 174724*uk_157 + 10580625*uk_158 + 188100*uk_159 + 202876*uk_16 + 3344*uk_160 + 11390625*uk_161 + 202500*uk_162 + 3600*uk_163 + 64*uk_164 + 3025*uk_17 + 7755*uk_18 + 220*uk_19 + 55*uk_2 + 660*uk_20 + 11495*uk_21 + 12375*uk_22 + 220*uk_23 + 19881*uk_24 + 564*uk_25 + 1692*uk_26 + 29469*uk_27 + 31725*uk_28 + 564*uk_29 + 141*uk_3 + 16*uk_30 + 48*uk_31 + 836*uk_32 + 900*uk_33 + 16*uk_34 + 144*uk_35 + 2508*uk_36 + 2700*uk_37 + 48*uk_38 + 43681*uk_39 + 4*uk_4 + 47025*uk_40 + 836*uk_41 + 50625*uk_42 + 900*uk_43 + 16*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 362710791501*uk_47 + 10289667844*uk_48 + 30869003532*uk_49 + 12*uk_5 + 537635144849*uk_50 + 578793816225*uk_51 + 10289667844*uk_52 + 153424975*uk_53 + 393325845*uk_54 + 11158180*uk_55 + 33474540*uk_56 + 583014905*uk_57 + 627647625*uk_58 + 11158180*uk_59 + 209*uk_6 + 1008344439*uk_60 + 28605516*uk_61 + 85816548*uk_62 + 1494638211*uk_63 + 1609060275*uk_64 + 28605516*uk_65 + 811504*uk_66 + 2434512*uk_67 + 42401084*uk_68 + 45647100*uk_69 + 225*uk_7 + 811504*uk_70 + 7303536*uk_71 + 127203252*uk_72 + 136941300*uk_73 + 2434512*uk_74 + 2215456639*uk_75 + 2385060975*uk_76 + 42401084*uk_77 + 2567649375*uk_78 + 45647100*uk_79 + 4*uk_8 + 811504*uk_80 + 166375*uk_81 + 426525*uk_82 + 12100*uk_83 + 36300*uk_84 + 632225*uk_85 + 680625*uk_86 + 12100*uk_87 + 1093455*uk_88 + 31020*uk_89 + 2572416961*uk_9 + 93060*uk_90 + 1620795*uk_91 + 1744875*uk_92 + 31020*uk_93 + 880*uk_94 + 2640*uk_95 + 45980*uk_96 + 49500*uk_97 + 880*uk_98 + 7920*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 92840*uk_100 + 99000*uk_101 + 62040*uk_102 + 2448655*uk_103 + 2611125*uk_104 + 1636305*uk_105 + 2784375*uk_106 + 1744875*uk_107 + 1093455*uk_108 + 493039*uk_109 + 4006801*uk_11 + 879981*uk_110 + 49928*uk_111 + 1316851*uk_112 + 1404225*uk_113 + 879981*uk_114 + 1570599*uk_115 + 89112*uk_116 + 2350329*uk_117 + 2506275*uk_118 + 1570599*uk_119 + 7151379*uk_12 + 5056*uk_120 + 133352*uk_121 + 142200*uk_122 + 89112*uk_123 + 3517159*uk_124 + 3750525*uk_125 + 2350329*uk_126 + 3999375*uk_127 + 2506275*uk_128 + 1570599*uk_129 + 405752*uk_13 + 2803221*uk_130 + 159048*uk_131 + 4194891*uk_132 + 4473225*uk_133 + 2803221*uk_134 + 9024*uk_135 + 238008*uk_136 + 253800*uk_137 + 159048*uk_138 + 6277461*uk_139 + 10701709*uk_14 + 6693975*uk_140 + 4194891*uk_141 + 7138125*uk_142 + 4473225*uk_143 + 2803221*uk_144 + 512*uk_145 + 13504*uk_146 + 14400*uk_147 + 9024*uk_148 + 356168*uk_149 + 11411775*uk_15 + 379800*uk_150 + 238008*uk_151 + 405000*uk_152 + 253800*uk_153 + 159048*uk_154 + 9393931*uk_155 + 10017225*uk_156 + 6277461*uk_157 + 10681875*uk_158 + 6693975*uk_159 + 7151379*uk_16 + 4194891*uk_160 + 11390625*uk_161 + 7138125*uk_162 + 4473225*uk_163 + 2803221*uk_164 + 3025*uk_17 + 4345*uk_18 + 7755*uk_19 + 55*uk_2 + 440*uk_20 + 11605*uk_21 + 12375*uk_22 + 7755*uk_23 + 6241*uk_24 + 11139*uk_25 + 632*uk_26 + 16669*uk_27 + 17775*uk_28 + 11139*uk_29 + 79*uk_3 + 19881*uk_30 + 1128*uk_31 + 29751*uk_32 + 31725*uk_33 + 19881*uk_34 + 64*uk_35 + 1688*uk_36 + 1800*uk_37 + 1128*uk_38 + 44521*uk_39 + 141*uk_4 + 47475*uk_40 + 29751*uk_41 + 50625*uk_42 + 31725*uk_43 + 19881*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 203220939919*uk_47 + 362710791501*uk_48 + 20579335688*uk_49 + 8*uk_5 + 542779978771*uk_50 + 578793816225*uk_51 + 362710791501*uk_52 + 153424975*uk_53 + 220374055*uk_54 + 393325845*uk_55 + 22316360*uk_56 + 588593995*uk_57 + 627647625*uk_58 + 393325845*uk_59 + 211*uk_6 + 316537279*uk_60 + 564958941*uk_61 + 32054408*uk_62 + 845435011*uk_63 + 901530225*uk_64 + 564958941*uk_65 + 1008344439*uk_66 + 57211032*uk_67 + 1508940969*uk_68 + 1609060275*uk_69 + 225*uk_7 + 1008344439*uk_70 + 3246016*uk_71 + 85613672*uk_72 + 91294200*uk_73 + 57211032*uk_74 + 2258060599*uk_75 + 2407884525*uk_76 + 1508940969*uk_77 + 2567649375*uk_78 + 1609060275*uk_79 + 141*uk_8 + 1008344439*uk_80 + 166375*uk_81 + 238975*uk_82 + 426525*uk_83 + 24200*uk_84 + 638275*uk_85 + 680625*uk_86 + 426525*uk_87 + 343255*uk_88 + 612645*uk_89 + 2572416961*uk_9 + 34760*uk_90 + 916795*uk_91 + 977625*uk_92 + 612645*uk_93 + 1093455*uk_94 + 62040*uk_95 + 1636305*uk_96 + 1744875*uk_97 + 1093455*uk_98 + 3520*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 93720*uk_100 + 99000*uk_101 + 34760*uk_102 + 2495295*uk_103 + 2635875*uk_104 + 925485*uk_105 + 2784375*uk_106 + 977625*uk_107 + 343255*uk_108 + 15625*uk_109 + 1267975*uk_11 + 49375*uk_110 + 5000*uk_111 + 133125*uk_112 + 140625*uk_113 + 49375*uk_114 + 156025*uk_115 + 15800*uk_116 + 420675*uk_117 + 444375*uk_118 + 156025*uk_119 + 4006801*uk_12 + 1600*uk_120 + 42600*uk_121 + 45000*uk_122 + 15800*uk_123 + 1134225*uk_124 + 1198125*uk_125 + 420675*uk_126 + 1265625*uk_127 + 444375*uk_128 + 156025*uk_129 + 405752*uk_13 + 493039*uk_130 + 49928*uk_131 + 1329333*uk_132 + 1404225*uk_133 + 493039*uk_134 + 5056*uk_135 + 134616*uk_136 + 142200*uk_137 + 49928*uk_138 + 3584151*uk_139 + 10803147*uk_14 + 3786075*uk_140 + 1329333*uk_141 + 3999375*uk_142 + 1404225*uk_143 + 493039*uk_144 + 512*uk_145 + 13632*uk_146 + 14400*uk_147 + 5056*uk_148 + 362952*uk_149 + 11411775*uk_15 + 383400*uk_150 + 134616*uk_151 + 405000*uk_152 + 142200*uk_153 + 49928*uk_154 + 9663597*uk_155 + 10208025*uk_156 + 3584151*uk_157 + 10783125*uk_158 + 3786075*uk_159 + 4006801*uk_16 + 1329333*uk_160 + 11390625*uk_161 + 3999375*uk_162 + 1404225*uk_163 + 493039*uk_164 + 3025*uk_17 + 1375*uk_18 + 4345*uk_19 + 55*uk_2 + 440*uk_20 + 11715*uk_21 + 12375*uk_22 + 4345*uk_23 + 625*uk_24 + 1975*uk_25 + 200*uk_26 + 5325*uk_27 + 5625*uk_28 + 1975*uk_29 + 25*uk_3 + 6241*uk_30 + 632*uk_31 + 16827*uk_32 + 17775*uk_33 + 6241*uk_34 + 64*uk_35 + 1704*uk_36 + 1800*uk_37 + 632*uk_38 + 45369*uk_39 + 79*uk_4 + 47925*uk_40 + 16827*uk_41 + 50625*uk_42 + 17775*uk_43 + 6241*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 64310424025*uk_47 + 203220939919*uk_48 + 20579335688*uk_49 + 8*uk_5 + 547924812693*uk_50 + 578793816225*uk_51 + 203220939919*uk_52 + 153424975*uk_53 + 69738625*uk_54 + 220374055*uk_55 + 22316360*uk_56 + 594173085*uk_57 + 627647625*uk_58 + 220374055*uk_59 + 213*uk_6 + 31699375*uk_60 + 100170025*uk_61 + 10143800*uk_62 + 270078675*uk_63 + 285294375*uk_64 + 100170025*uk_65 + 316537279*uk_66 + 32054408*uk_67 + 853448613*uk_68 + 901530225*uk_69 + 225*uk_7 + 316537279*uk_70 + 3246016*uk_71 + 86425176*uk_72 + 91294200*uk_73 + 32054408*uk_74 + 2301070311*uk_75 + 2430708075*uk_76 + 853448613*uk_77 + 2567649375*uk_78 + 901530225*uk_79 + 79*uk_8 + 316537279*uk_80 + 166375*uk_81 + 75625*uk_82 + 238975*uk_83 + 24200*uk_84 + 644325*uk_85 + 680625*uk_86 + 238975*uk_87 + 34375*uk_88 + 108625*uk_89 + 2572416961*uk_9 + 11000*uk_90 + 292875*uk_91 + 309375*uk_92 + 108625*uk_93 + 343255*uk_94 + 34760*uk_95 + 925485*uk_96 + 977625*uk_97 + 343255*uk_98 + 3520*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 141900*uk_100 + 148500*uk_101 + 16500*uk_102 + 2542375*uk_103 + 2660625*uk_104 + 295625*uk_105 + 2784375*uk_106 + 309375*uk_107 + 34375*uk_108 + 7301384*uk_109 + 9839486*uk_11 + 940900*uk_110 + 451632*uk_111 + 8091740*uk_112 + 8468100*uk_113 + 940900*uk_114 + 121250*uk_115 + 58200*uk_116 + 1042750*uk_117 + 1091250*uk_118 + 121250*uk_119 + 1267975*uk_12 + 27936*uk_120 + 500520*uk_121 + 523800*uk_122 + 58200*uk_123 + 8967650*uk_124 + 9384750*uk_125 + 1042750*uk_126 + 9821250*uk_127 + 1091250*uk_128 + 121250*uk_129 + 608628*uk_13 + 15625*uk_130 + 7500*uk_131 + 134375*uk_132 + 140625*uk_133 + 15625*uk_134 + 3600*uk_135 + 64500*uk_136 + 67500*uk_137 + 7500*uk_138 + 1155625*uk_139 + 10904585*uk_14 + 1209375*uk_140 + 134375*uk_141 + 1265625*uk_142 + 140625*uk_143 + 15625*uk_144 + 1728*uk_145 + 30960*uk_146 + 32400*uk_147 + 3600*uk_148 + 554700*uk_149 + 11411775*uk_15 + 580500*uk_150 + 64500*uk_151 + 607500*uk_152 + 67500*uk_153 + 7500*uk_154 + 9938375*uk_155 + 10400625*uk_156 + 1155625*uk_157 + 10884375*uk_158 + 1209375*uk_159 + 1267975*uk_16 + 134375*uk_160 + 11390625*uk_161 + 1265625*uk_162 + 140625*uk_163 + 15625*uk_164 + 3025*uk_17 + 10670*uk_18 + 1375*uk_19 + 55*uk_2 + 660*uk_20 + 11825*uk_21 + 12375*uk_22 + 1375*uk_23 + 37636*uk_24 + 4850*uk_25 + 2328*uk_26 + 41710*uk_27 + 43650*uk_28 + 4850*uk_29 + 194*uk_3 + 625*uk_30 + 300*uk_31 + 5375*uk_32 + 5625*uk_33 + 625*uk_34 + 144*uk_35 + 2580*uk_36 + 2700*uk_37 + 300*uk_38 + 46225*uk_39 + 25*uk_4 + 48375*uk_40 + 5375*uk_41 + 50625*uk_42 + 5625*uk_43 + 625*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 499048890434*uk_47 + 64310424025*uk_48 + 30869003532*uk_49 + 12*uk_5 + 553069646615*uk_50 + 578793816225*uk_51 + 64310424025*uk_52 + 153424975*uk_53 + 541171730*uk_54 + 69738625*uk_55 + 33474540*uk_56 + 599752175*uk_57 + 627647625*uk_58 + 69738625*uk_59 + 215*uk_6 + 1908860284*uk_60 + 245987150*uk_61 + 118073832*uk_62 + 2115489490*uk_63 + 2213884350*uk_64 + 245987150*uk_65 + 31699375*uk_66 + 15215700*uk_67 + 272614625*uk_68 + 285294375*uk_69 + 225*uk_7 + 31699375*uk_70 + 7303536*uk_71 + 130855020*uk_72 + 136941300*uk_73 + 15215700*uk_74 + 2344485775*uk_75 + 2453531625*uk_76 + 272614625*uk_77 + 2567649375*uk_78 + 285294375*uk_79 + 25*uk_8 + 31699375*uk_80 + 166375*uk_81 + 586850*uk_82 + 75625*uk_83 + 36300*uk_84 + 650375*uk_85 + 680625*uk_86 + 75625*uk_87 + 2069980*uk_88 + 266750*uk_89 + 2572416961*uk_9 + 128040*uk_90 + 2294050*uk_91 + 2400750*uk_92 + 266750*uk_93 + 34375*uk_94 + 16500*uk_95 + 295625*uk_96 + 309375*uk_97 + 34375*uk_98 + 7920*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 95480*uk_100 + 99000*uk_101 + 85360*uk_102 + 2589895*uk_103 + 2685375*uk_104 + 2315390*uk_105 + 2784375*uk_106 + 2400750*uk_107 + 2069980*uk_108 + 3944312*uk_109 + 8013602*uk_11 + 4843016*uk_110 + 199712*uk_111 + 5417188*uk_112 + 5616900*uk_113 + 4843016*uk_114 + 5946488*uk_115 + 245216*uk_116 + 6651484*uk_117 + 6896700*uk_118 + 5946488*uk_119 + 9839486*uk_12 + 10112*uk_120 + 274288*uk_121 + 284400*uk_122 + 245216*uk_123 + 7440062*uk_124 + 7714350*uk_125 + 6651484*uk_126 + 7998750*uk_127 + 6896700*uk_128 + 5946488*uk_129 + 405752*uk_13 + 7301384*uk_130 + 301088*uk_131 + 8167012*uk_132 + 8468100*uk_133 + 7301384*uk_134 + 12416*uk_135 + 336784*uk_136 + 349200*uk_137 + 301088*uk_138 + 9135266*uk_139 + 11006023*uk_14 + 9472050*uk_140 + 8167012*uk_141 + 9821250*uk_142 + 8468100*uk_143 + 7301384*uk_144 + 512*uk_145 + 13888*uk_146 + 14400*uk_147 + 12416*uk_148 + 376712*uk_149 + 11411775*uk_15 + 390600*uk_150 + 336784*uk_151 + 405000*uk_152 + 349200*uk_153 + 301088*uk_154 + 10218313*uk_155 + 10595025*uk_156 + 9135266*uk_157 + 10985625*uk_158 + 9472050*uk_159 + 9839486*uk_16 + 8167012*uk_160 + 11390625*uk_161 + 9821250*uk_162 + 8468100*uk_163 + 7301384*uk_164 + 3025*uk_17 + 8690*uk_18 + 10670*uk_19 + 55*uk_2 + 440*uk_20 + 11935*uk_21 + 12375*uk_22 + 10670*uk_23 + 24964*uk_24 + 30652*uk_25 + 1264*uk_26 + 34286*uk_27 + 35550*uk_28 + 30652*uk_29 + 158*uk_3 + 37636*uk_30 + 1552*uk_31 + 42098*uk_32 + 43650*uk_33 + 37636*uk_34 + 64*uk_35 + 1736*uk_36 + 1800*uk_37 + 1552*uk_38 + 47089*uk_39 + 194*uk_4 + 48825*uk_40 + 42098*uk_41 + 50625*uk_42 + 43650*uk_43 + 37636*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 406441879838*uk_47 + 499048890434*uk_48 + 20579335688*uk_49 + 8*uk_5 + 558214480537*uk_50 + 578793816225*uk_51 + 499048890434*uk_52 + 153424975*uk_53 + 440748110*uk_54 + 541171730*uk_55 + 22316360*uk_56 + 605331265*uk_57 + 627647625*uk_58 + 541171730*uk_59 + 217*uk_6 + 1266149116*uk_60 + 1554638788*uk_61 + 64108816*uk_62 + 1738951634*uk_63 + 1803060450*uk_64 + 1554638788*uk_65 + 1908860284*uk_66 + 78715888*uk_67 + 2135168462*uk_68 + 2213884350*uk_69 + 225*uk_7 + 1908860284*uk_70 + 3246016*uk_71 + 88048184*uk_72 + 91294200*uk_73 + 78715888*uk_74 + 2388306991*uk_75 + 2476355175*uk_76 + 2135168462*uk_77 + 2567649375*uk_78 + 2213884350*uk_79 + 194*uk_8 + 1908860284*uk_80 + 166375*uk_81 + 477950*uk_82 + 586850*uk_83 + 24200*uk_84 + 656425*uk_85 + 680625*uk_86 + 586850*uk_87 + 1373020*uk_88 + 1685860*uk_89 + 2572416961*uk_9 + 69520*uk_90 + 1885730*uk_91 + 1955250*uk_92 + 1685860*uk_93 + 2069980*uk_94 + 85360*uk_95 + 2315390*uk_96 + 2400750*uk_97 + 2069980*uk_98 + 3520*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 96360*uk_100 + 99000*uk_101 + 69520*uk_102 + 2637855*uk_103 + 2710125*uk_104 + 1903110*uk_105 + 2784375*uk_106 + 1955250*uk_107 + 1373020*uk_108 + 2197000*uk_109 + 6593470*uk_11 + 2670200*uk_110 + 135200*uk_111 + 3701100*uk_112 + 3802500*uk_113 + 2670200*uk_114 + 3245320*uk_115 + 164320*uk_116 + 4498260*uk_117 + 4621500*uk_118 + 3245320*uk_119 + 8013602*uk_12 + 8320*uk_120 + 227760*uk_121 + 234000*uk_122 + 164320*uk_123 + 6234930*uk_124 + 6405750*uk_125 + 4498260*uk_126 + 6581250*uk_127 + 4621500*uk_128 + 3245320*uk_129 + 405752*uk_13 + 3944312*uk_130 + 199712*uk_131 + 5467116*uk_132 + 5616900*uk_133 + 3944312*uk_134 + 10112*uk_135 + 276816*uk_136 + 284400*uk_137 + 199712*uk_138 + 7577838*uk_139 + 11107461*uk_14 + 7785450*uk_140 + 5467116*uk_141 + 7998750*uk_142 + 5616900*uk_143 + 3944312*uk_144 + 512*uk_145 + 14016*uk_146 + 14400*uk_147 + 10112*uk_148 + 383688*uk_149 + 11411775*uk_15 + 394200*uk_150 + 276816*uk_151 + 405000*uk_152 + 284400*uk_153 + 199712*uk_154 + 10503459*uk_155 + 10791225*uk_156 + 7577838*uk_157 + 11086875*uk_158 + 7785450*uk_159 + 8013602*uk_16 + 5467116*uk_160 + 11390625*uk_161 + 7998750*uk_162 + 5616900*uk_163 + 3944312*uk_164 + 3025*uk_17 + 7150*uk_18 + 8690*uk_19 + 55*uk_2 + 440*uk_20 + 12045*uk_21 + 12375*uk_22 + 8690*uk_23 + 16900*uk_24 + 20540*uk_25 + 1040*uk_26 + 28470*uk_27 + 29250*uk_28 + 20540*uk_29 + 130*uk_3 + 24964*uk_30 + 1264*uk_31 + 34602*uk_32 + 35550*uk_33 + 24964*uk_34 + 64*uk_35 + 1752*uk_36 + 1800*uk_37 + 1264*uk_38 + 47961*uk_39 + 158*uk_4 + 49275*uk_40 + 34602*uk_41 + 50625*uk_42 + 35550*uk_43 + 24964*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 334414204930*uk_47 + 406441879838*uk_48 + 20579335688*uk_49 + 8*uk_5 + 563359314459*uk_50 + 578793816225*uk_51 + 406441879838*uk_52 + 153424975*uk_53 + 362640850*uk_54 + 440748110*uk_55 + 22316360*uk_56 + 610910355*uk_57 + 627647625*uk_58 + 440748110*uk_59 + 219*uk_6 + 857151100*uk_60 + 1041768260*uk_61 + 52747760*uk_62 + 1443969930*uk_63 + 1483530750*uk_64 + 1041768260*uk_65 + 1266149116*uk_66 + 64108816*uk_67 + 1754978838*uk_68 + 1803060450*uk_69 + 225*uk_7 + 1266149116*uk_70 + 3246016*uk_71 + 88859688*uk_72 + 91294200*uk_73 + 64108816*uk_74 + 2432533959*uk_75 + 2499178725*uk_76 + 1754978838*uk_77 + 2567649375*uk_78 + 1803060450*uk_79 + 158*uk_8 + 1266149116*uk_80 + 166375*uk_81 + 393250*uk_82 + 477950*uk_83 + 24200*uk_84 + 662475*uk_85 + 680625*uk_86 + 477950*uk_87 + 929500*uk_88 + 1129700*uk_89 + 2572416961*uk_9 + 57200*uk_90 + 1565850*uk_91 + 1608750*uk_92 + 1129700*uk_93 + 1373020*uk_94 + 69520*uk_95 + 1903110*uk_96 + 1955250*uk_97 + 1373020*uk_98 + 3520*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 97240*uk_100 + 99000*uk_101 + 57200*uk_102 + 2686255*uk_103 + 2734875*uk_104 + 1580150*uk_105 + 2784375*uk_106 + 1608750*uk_107 + 929500*uk_108 + 1331000*uk_109 + 5579090*uk_11 + 1573000*uk_110 + 96800*uk_111 + 2674100*uk_112 + 2722500*uk_113 + 1573000*uk_114 + 1859000*uk_115 + 114400*uk_116 + 3160300*uk_117 + 3217500*uk_118 + 1859000*uk_119 + 6593470*uk_12 + 7040*uk_120 + 194480*uk_121 + 198000*uk_122 + 114400*uk_123 + 5372510*uk_124 + 5469750*uk_125 + 3160300*uk_126 + 5568750*uk_127 + 3217500*uk_128 + 1859000*uk_129 + 405752*uk_13 + 2197000*uk_130 + 135200*uk_131 + 3734900*uk_132 + 3802500*uk_133 + 2197000*uk_134 + 8320*uk_135 + 229840*uk_136 + 234000*uk_137 + 135200*uk_138 + 6349330*uk_139 + 11208899*uk_14 + 6464250*uk_140 + 3734900*uk_141 + 6581250*uk_142 + 3802500*uk_143 + 2197000*uk_144 + 512*uk_145 + 14144*uk_146 + 14400*uk_147 + 8320*uk_148 + 390728*uk_149 + 11411775*uk_15 + 397800*uk_150 + 229840*uk_151 + 405000*uk_152 + 234000*uk_153 + 135200*uk_154 + 10793861*uk_155 + 10989225*uk_156 + 6349330*uk_157 + 11188125*uk_158 + 6464250*uk_159 + 6593470*uk_16 + 3734900*uk_160 + 11390625*uk_161 + 6581250*uk_162 + 3802500*uk_163 + 2197000*uk_164 + 3025*uk_17 + 6050*uk_18 + 7150*uk_19 + 55*uk_2 + 440*uk_20 + 12155*uk_21 + 12375*uk_22 + 7150*uk_23 + 12100*uk_24 + 14300*uk_25 + 880*uk_26 + 24310*uk_27 + 24750*uk_28 + 14300*uk_29 + 110*uk_3 + 16900*uk_30 + 1040*uk_31 + 28730*uk_32 + 29250*uk_33 + 16900*uk_34 + 64*uk_35 + 1768*uk_36 + 1800*uk_37 + 1040*uk_38 + 48841*uk_39 + 130*uk_4 + 49725*uk_40 + 28730*uk_41 + 50625*uk_42 + 29250*uk_43 + 16900*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 282965865710*uk_47 + 334414204930*uk_48 + 20579335688*uk_49 + 8*uk_5 + 568504148381*uk_50 + 578793816225*uk_51 + 334414204930*uk_52 + 153424975*uk_53 + 306849950*uk_54 + 362640850*uk_55 + 22316360*uk_56 + 616489445*uk_57 + 627647625*uk_58 + 362640850*uk_59 + 221*uk_6 + 613699900*uk_60 + 725281700*uk_61 + 44632720*uk_62 + 1232978890*uk_63 + 1255295250*uk_64 + 725281700*uk_65 + 857151100*uk_66 + 52747760*uk_67 + 1457156870*uk_68 + 1483530750*uk_69 + 225*uk_7 + 857151100*uk_70 + 3246016*uk_71 + 89671192*uk_72 + 91294200*uk_73 + 52747760*uk_74 + 2477166679*uk_75 + 2522002275*uk_76 + 1457156870*uk_77 + 2567649375*uk_78 + 1483530750*uk_79 + 130*uk_8 + 857151100*uk_80 + 166375*uk_81 + 332750*uk_82 + 393250*uk_83 + 24200*uk_84 + 668525*uk_85 + 680625*uk_86 + 393250*uk_87 + 665500*uk_88 + 786500*uk_89 + 2572416961*uk_9 + 48400*uk_90 + 1337050*uk_91 + 1361250*uk_92 + 786500*uk_93 + 929500*uk_94 + 57200*uk_95 + 1580150*uk_96 + 1608750*uk_97 + 929500*uk_98 + 3520*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 98120*uk_100 + 99000*uk_101 + 48400*uk_102 + 2735095*uk_103 + 2759625*uk_104 + 1349150*uk_105 + 2784375*uk_106 + 1361250*uk_107 + 665500*uk_108 + 941192*uk_109 + 4970462*uk_11 + 1056440*uk_110 + 76832*uk_111 + 2141692*uk_112 + 2160900*uk_113 + 1056440*uk_114 + 1185800*uk_115 + 86240*uk_116 + 2403940*uk_117 + 2425500*uk_118 + 1185800*uk_119 + 5579090*uk_12 + 6272*uk_120 + 174832*uk_121 + 176400*uk_122 + 86240*uk_123 + 4873442*uk_124 + 4917150*uk_125 + 2403940*uk_126 + 4961250*uk_127 + 2425500*uk_128 + 1185800*uk_129 + 405752*uk_13 + 1331000*uk_130 + 96800*uk_131 + 2698300*uk_132 + 2722500*uk_133 + 1331000*uk_134 + 7040*uk_135 + 196240*uk_136 + 198000*uk_137 + 96800*uk_138 + 5470190*uk_139 + 11310337*uk_14 + 5519250*uk_140 + 2698300*uk_141 + 5568750*uk_142 + 2722500*uk_143 + 1331000*uk_144 + 512*uk_145 + 14272*uk_146 + 14400*uk_147 + 7040*uk_148 + 397832*uk_149 + 11411775*uk_15 + 401400*uk_150 + 196240*uk_151 + 405000*uk_152 + 198000*uk_153 + 96800*uk_154 + 11089567*uk_155 + 11189025*uk_156 + 5470190*uk_157 + 11289375*uk_158 + 5519250*uk_159 + 5579090*uk_16 + 2698300*uk_160 + 11390625*uk_161 + 5568750*uk_162 + 2722500*uk_163 + 1331000*uk_164 + 3025*uk_17 + 5390*uk_18 + 6050*uk_19 + 55*uk_2 + 440*uk_20 + 12265*uk_21 + 12375*uk_22 + 6050*uk_23 + 9604*uk_24 + 10780*uk_25 + 784*uk_26 + 21854*uk_27 + 22050*uk_28 + 10780*uk_29 + 98*uk_3 + 12100*uk_30 + 880*uk_31 + 24530*uk_32 + 24750*uk_33 + 12100*uk_34 + 64*uk_35 + 1784*uk_36 + 1800*uk_37 + 880*uk_38 + 49729*uk_39 + 110*uk_4 + 50175*uk_40 + 24530*uk_41 + 50625*uk_42 + 24750*uk_43 + 12100*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 252096862178*uk_47 + 282965865710*uk_48 + 20579335688*uk_49 + 8*uk_5 + 573648982303*uk_50 + 578793816225*uk_51 + 282965865710*uk_52 + 153424975*uk_53 + 273375410*uk_54 + 306849950*uk_55 + 22316360*uk_56 + 622068535*uk_57 + 627647625*uk_58 + 306849950*uk_59 + 223*uk_6 + 487105276*uk_60 + 546750820*uk_61 + 39763696*uk_62 + 1108413026*uk_63 + 1118353950*uk_64 + 546750820*uk_65 + 613699900*uk_66 + 44632720*uk_67 + 1244137070*uk_68 + 1255295250*uk_69 + 225*uk_7 + 613699900*uk_70 + 3246016*uk_71 + 90482696*uk_72 + 91294200*uk_73 + 44632720*uk_74 + 2522205151*uk_75 + 2544825825*uk_76 + 1244137070*uk_77 + 2567649375*uk_78 + 1255295250*uk_79 + 110*uk_8 + 613699900*uk_80 + 166375*uk_81 + 296450*uk_82 + 332750*uk_83 + 24200*uk_84 + 674575*uk_85 + 680625*uk_86 + 332750*uk_87 + 528220*uk_88 + 592900*uk_89 + 2572416961*uk_9 + 43120*uk_90 + 1201970*uk_91 + 1212750*uk_92 + 592900*uk_93 + 665500*uk_94 + 48400*uk_95 + 1349150*uk_96 + 1361250*uk_97 + 665500*uk_98 + 3520*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 99000*uk_100 + 99000*uk_101 + 43120*uk_102 + 2784375*uk_103 + 2784375*uk_104 + 1212750*uk_105 + 2784375*uk_106 + 1212750*uk_107 + 528220*uk_108 + 830584*uk_109 + 4767586*uk_11 + 865928*uk_110 + 70688*uk_111 + 1988100*uk_112 + 1988100*uk_113 + 865928*uk_114 + 902776*uk_115 + 73696*uk_116 + 2072700*uk_117 + 2072700*uk_118 + 902776*uk_119 + 4970462*uk_12 + 6016*uk_120 + 169200*uk_121 + 169200*uk_122 + 73696*uk_123 + 4758750*uk_124 + 4758750*uk_125 + 2072700*uk_126 + 4758750*uk_127 + 2072700*uk_128 + 902776*uk_129 + 405752*uk_13 + 941192*uk_130 + 76832*uk_131 + 2160900*uk_132 + 2160900*uk_133 + 941192*uk_134 + 6272*uk_135 + 176400*uk_136 + 176400*uk_137 + 76832*uk_138 + 4961250*uk_139 + 11411775*uk_14 + 4961250*uk_140 + 2160900*uk_141 + 4961250*uk_142 + 2160900*uk_143 + 941192*uk_144 + 512*uk_145 + 14400*uk_146 + 14400*uk_147 + 6272*uk_148 + 405000*uk_149 + 11411775*uk_15 + 405000*uk_150 + 176400*uk_151 + 405000*uk_152 + 176400*uk_153 + 76832*uk_154 + 11390625*uk_155 + 11390625*uk_156 + 4961250*uk_157 + 11390625*uk_158 + 4961250*uk_159 + 4970462*uk_16 + 2160900*uk_160 + 11390625*uk_161 + 4961250*uk_162 + 2160900*uk_163 + 941192*uk_164 + 3025*uk_17 + 5170*uk_18 + 5390*uk_19 + 55*uk_2 + 440*uk_20 + 12375*uk_21 + 12375*uk_22 + 5390*uk_23 + 8836*uk_24 + 9212*uk_25 + 752*uk_26 + 21150*uk_27 + 21150*uk_28 + 9212*uk_29 + 94*uk_3 + 9604*uk_30 + 784*uk_31 + 22050*uk_32 + 22050*uk_33 + 9604*uk_34 + 64*uk_35 + 1800*uk_36 + 1800*uk_37 + 784*uk_38 + 50625*uk_39 + 98*uk_4 + 50625*uk_40 + 22050*uk_41 + 50625*uk_42 + 22050*uk_43 + 9604*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 241807194334*uk_47 + 252096862178*uk_48 + 20579335688*uk_49 + 8*uk_5 + 578793816225*uk_50 + 578793816225*uk_51 + 252096862178*uk_52 + 153424975*uk_53 + 262217230*uk_54 + 273375410*uk_55 + 22316360*uk_56 + 627647625*uk_57 + 627647625*uk_58 + 273375410*uk_59 + 225*uk_6 + 448153084*uk_60 + 467223428*uk_61 + 38140688*uk_62 + 1072706850*uk_63 + 1072706850*uk_64 + 467223428*uk_65 + 487105276*uk_66 + 39763696*uk_67 + 1118353950*uk_68 + 1118353950*uk_69 + 225*uk_7 + 487105276*uk_70 + 3246016*uk_71 + 91294200*uk_72 + 91294200*uk_73 + 39763696*uk_74 + 2567649375*uk_75 + 2567649375*uk_76 + 1118353950*uk_77 + 2567649375*uk_78 + 1118353950*uk_79 + 98*uk_8 + 487105276*uk_80 + 166375*uk_81 + 284350*uk_82 + 296450*uk_83 + 24200*uk_84 + 680625*uk_85 + 680625*uk_86 + 296450*uk_87 + 485980*uk_88 + 506660*uk_89 + 2572416961*uk_9 + 41360*uk_90 + 1163250*uk_91 + 1163250*uk_92 + 506660*uk_93 + 528220*uk_94 + 43120*uk_95 + 1212750*uk_96 + 1212750*uk_97 + 528220*uk_98 + 3520*uk_99, uk_0 + 50719*uk_1 + 2789545*uk_10 + 99880*uk_100 + 99000*uk_101 + 41360*uk_102 + 2834095*uk_103 + 2809125*uk_104 + 1173590*uk_105 + 2784375*uk_106 + 1163250*uk_107 + 485980*uk_108 + 941192*uk_109 + 4970462*uk_11 + 902776*uk_110 + 76832*uk_111 + 2180108*uk_112 + 2160900*uk_113 + 902776*uk_114 + 865928*uk_115 + 73696*uk_116 + 2091124*uk_117 + 2072700*uk_118 + 865928*uk_119 + 4767586*uk_12 + 6272*uk_120 + 177968*uk_121 + 176400*uk_122 + 73696*uk_123 + 5049842*uk_124 + 5005350*uk_125 + 2091124*uk_126 + 4961250*uk_127 + 2072700*uk_128 + 865928*uk_129 + 405752*uk_13 + 830584*uk_130 + 70688*uk_131 + 2005772*uk_132 + 1988100*uk_133 + 830584*uk_134 + 6016*uk_135 + 170704*uk_136 + 169200*uk_137 + 70688*uk_138 + 4843726*uk_139 + 11513213*uk_14 + 4801050*uk_140 + 2005772*uk_141 + 4758750*uk_142 + 1988100*uk_143 + 830584*uk_144 + 512*uk_145 + 14528*uk_146 + 14400*uk_147 + 6016*uk_148 + 412232*uk_149 + 11411775*uk_15 + 408600*uk_150 + 170704*uk_151 + 405000*uk_152 + 169200*uk_153 + 70688*uk_154 + 11697083*uk_155 + 11594025*uk_156 + 4843726*uk_157 + 11491875*uk_158 + 4801050*uk_159 + 4767586*uk_16 + 2005772*uk_160 + 11390625*uk_161 + 4758750*uk_162 + 1988100*uk_163 + 830584*uk_164 + 3025*uk_17 + 5390*uk_18 + 5170*uk_19 + 55*uk_2 + 440*uk_20 + 12485*uk_21 + 12375*uk_22 + 5170*uk_23 + 9604*uk_24 + 9212*uk_25 + 784*uk_26 + 22246*uk_27 + 22050*uk_28 + 9212*uk_29 + 98*uk_3 + 8836*uk_30 + 752*uk_31 + 21338*uk_32 + 21150*uk_33 + 8836*uk_34 + 64*uk_35 + 1816*uk_36 + 1800*uk_37 + 752*uk_38 + 51529*uk_39 + 94*uk_4 + 51075*uk_40 + 21338*uk_41 + 50625*uk_42 + 21150*uk_43 + 8836*uk_44 + 130470415844959*uk_45 + 141482932855*uk_46 + 252096862178*uk_47 + 241807194334*uk_48 + 20579335688*uk_49 + 8*uk_5 + 583938650147*uk_50 + 578793816225*uk_51 + 241807194334*uk_52 + 153424975*uk_53 + 273375410*uk_54 + 262217230*uk_55 + 22316360*uk_56 + 633226715*uk_57 + 627647625*uk_58 + 262217230*uk_59 + 227*uk_6 + 487105276*uk_60 + 467223428*uk_61 + 39763696*uk_62 + 1128294874*uk_63 + 1118353950*uk_64 + 467223428*uk_65 + 448153084*uk_66 + 38140688*uk_67 + 1082242022*uk_68 + 1072706850*uk_69 + 225*uk_7 + 448153084*uk_70 + 3246016*uk_71 + 92105704*uk_72 + 91294200*uk_73 + 38140688*uk_74 + 2613499351*uk_75 + 2590472925*uk_76 + 1082242022*uk_77 + 2567649375*uk_78 + 1072706850*uk_79 + 94*uk_8 + 448153084*uk_80 + 166375*uk_81 + 296450*uk_82 + 284350*uk_83 + 24200*uk_84 + 686675*uk_85 + 680625*uk_86 + 284350*uk_87 + 528220*uk_88 + 506660*uk_89 + 2572416961*uk_9 + 43120*uk_90 + 1223530*uk_91 + 1212750*uk_92 + 506660*uk_93 + 485980*uk_94 + 41360*uk_95 + 1173590*uk_96 + 1163250*uk_97 + 485980*uk_98 + 3520*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 396900*uk_100 + 1367100*uk_101 + 250047*uk_103 + 861273*uk_104 + 2966607*uk_106 + 64000*uk_109 + 1894120*uk_11 + 27200*uk_110 + 160000*uk_111 + 100800*uk_112 + 347200*uk_113 + 11560*uk_115 + 68000*uk_116 + 42840*uk_117 + 147560*uk_118 + 805001*uk_12 + 400000*uk_120 + 252000*uk_121 + 868000*uk_122 + 158760*uk_124 + 546840*uk_125 + 1883560*uk_127 + 4735300*uk_13 + 4913*uk_130 + 28900*uk_131 + 18207*uk_132 + 62713*uk_133 + 170000*uk_135 + 107100*uk_136 + 368900*uk_137 + 67473*uk_139 + 2983239*uk_14 + 232407*uk_140 + 800513*uk_142 + 1000000*uk_145 + 630000*uk_146 + 2170000*uk_147 + 396900*uk_149 + 10275601*uk_15 + 1367100*uk_150 + 4708900*uk_152 + 250047*uk_155 + 861273*uk_156 + 2966607*uk_158 + 10218313*uk_161 + 3969*uk_17 + 2520*uk_18 + 1071*uk_19 + 63*uk_2 + 6300*uk_20 + 3969*uk_21 + 13671*uk_22 + 1600*uk_24 + 680*uk_25 + 4000*uk_26 + 2520*uk_27 + 8680*uk_28 + 40*uk_3 + 289*uk_30 + 1700*uk_31 + 1071*uk_32 + 3689*uk_33 + 10000*uk_35 + 6300*uk_36 + 21700*uk_37 + 3969*uk_39 + 17*uk_4 + 13671*uk_40 + 47089*uk_42 + 106179944855977*uk_45 + 141265316367*uk_46 + 89692264360*uk_47 + 38119212353*uk_48 + 224230660900*uk_49 + 100*uk_5 + 141265316367*uk_50 + 486580534153*uk_51 + 187944057*uk_53 + 119329560*uk_54 + 50715063*uk_55 + 298323900*uk_56 + 187944057*uk_57 + 647362863*uk_58 + 63*uk_6 + 75764800*uk_60 + 32200040*uk_61 + 189412000*uk_62 + 119329560*uk_63 + 411024040*uk_64 + 13685017*uk_66 + 80500100*uk_67 + 50715063*uk_68 + 174685217*uk_69 + 217*uk_7 + 473530000*uk_71 + 298323900*uk_72 + 1027560100*uk_73 + 187944057*uk_75 + 647362863*uk_76 + 2229805417*uk_78 + 250047*uk_81 + 158760*uk_82 + 67473*uk_83 + 396900*uk_84 + 250047*uk_85 + 861273*uk_86 + 100800*uk_88 + 42840*uk_89 + 2242306609*uk_9 + 252000*uk_90 + 158760*uk_91 + 546840*uk_92 + 18207*uk_94 + 107100*uk_95 + 67473*uk_96 + 232407*uk_97 + 630000*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 376740*uk_100 + 1257732*uk_101 + 231840*uk_102 + 266175*uk_103 + 888615*uk_104 + 163800*uk_105 + 2966607*uk_106 + 546840*uk_107 + 100800*uk_108 + 35937*uk_109 + 1562649*uk_11 + 43560*uk_110 + 100188*uk_111 + 70785*uk_112 + 236313*uk_113 + 43560*uk_114 + 52800*uk_115 + 121440*uk_116 + 85800*uk_117 + 286440*uk_118 + 52800*uk_119 + 1894120*uk_12 + 279312*uk_120 + 197340*uk_121 + 658812*uk_122 + 121440*uk_123 + 139425*uk_124 + 465465*uk_125 + 85800*uk_126 + 1553937*uk_127 + 286440*uk_128 + 52800*uk_129 + 4356476*uk_13 + 64000*uk_130 + 147200*uk_131 + 104000*uk_132 + 347200*uk_133 + 64000*uk_134 + 338560*uk_135 + 239200*uk_136 + 798560*uk_137 + 147200*uk_138 + 169000*uk_139 + 3077945*uk_14 + 564200*uk_140 + 104000*uk_141 + 1883560*uk_142 + 347200*uk_143 + 64000*uk_144 + 778688*uk_145 + 550160*uk_146 + 1836688*uk_147 + 338560*uk_148 + 388700*uk_149 + 10275601*uk_15 + 1297660*uk_150 + 239200*uk_151 + 4332188*uk_152 + 798560*uk_153 + 147200*uk_154 + 274625*uk_155 + 916825*uk_156 + 169000*uk_157 + 3060785*uk_158 + 564200*uk_159 + 1894120*uk_16 + 104000*uk_160 + 10218313*uk_161 + 1883560*uk_162 + 347200*uk_163 + 64000*uk_164 + 3969*uk_17 + 2079*uk_18 + 2520*uk_19 + 63*uk_2 + 5796*uk_20 + 4095*uk_21 + 13671*uk_22 + 2520*uk_23 + 1089*uk_24 + 1320*uk_25 + 3036*uk_26 + 2145*uk_27 + 7161*uk_28 + 1320*uk_29 + 33*uk_3 + 1600*uk_30 + 3680*uk_31 + 2600*uk_32 + 8680*uk_33 + 1600*uk_34 + 8464*uk_35 + 5980*uk_36 + 19964*uk_37 + 3680*uk_38 + 4225*uk_39 + 40*uk_4 + 14105*uk_40 + 2600*uk_41 + 47089*uk_42 + 8680*uk_43 + 1600*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 73996118097*uk_47 + 89692264360*uk_48 + 206292208028*uk_49 + 92*uk_5 + 145749929585*uk_50 + 486580534153*uk_51 + 89692264360*uk_52 + 187944057*uk_53 + 98446887*uk_54 + 119329560*uk_55 + 274457988*uk_56 + 193910535*uk_57 + 647362863*uk_58 + 119329560*uk_59 + 65*uk_6 + 51567417*uk_60 + 62505960*uk_61 + 143763708*uk_62 + 101572185*uk_63 + 339094833*uk_64 + 62505960*uk_65 + 75764800*uk_66 + 174259040*uk_67 + 123117800*uk_68 + 411024040*uk_69 + 217*uk_7 + 75764800*uk_70 + 400795792*uk_71 + 283170940*uk_72 + 945355292*uk_73 + 174259040*uk_74 + 200066425*uk_75 + 667914065*uk_76 + 123117800*uk_77 + 2229805417*uk_78 + 411024040*uk_79 + 40*uk_8 + 75764800*uk_80 + 250047*uk_81 + 130977*uk_82 + 158760*uk_83 + 365148*uk_84 + 257985*uk_85 + 861273*uk_86 + 158760*uk_87 + 68607*uk_88 + 83160*uk_89 + 2242306609*uk_9 + 191268*uk_90 + 135135*uk_91 + 451143*uk_92 + 83160*uk_93 + 100800*uk_94 + 231840*uk_95 + 163800*uk_96 + 546840*uk_97 + 100800*uk_98 + 533232*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 371448*uk_100 + 1203048*uk_101 + 182952*uk_102 + 282807*uk_103 + 915957*uk_104 + 139293*uk_105 + 2966607*uk_106 + 451143*uk_107 + 68607*uk_108 + 132651*uk_109 + 2415003*uk_11 + 85833*uk_110 + 228888*uk_111 + 174267*uk_112 + 564417*uk_113 + 85833*uk_114 + 55539*uk_115 + 148104*uk_116 + 112761*uk_117 + 365211*uk_118 + 55539*uk_119 + 1562649*uk_12 + 394944*uk_120 + 300696*uk_121 + 973896*uk_122 + 148104*uk_123 + 228939*uk_124 + 741489*uk_125 + 112761*uk_126 + 2401539*uk_127 + 365211*uk_128 + 55539*uk_129 + 4167064*uk_13 + 35937*uk_130 + 95832*uk_131 + 72963*uk_132 + 236313*uk_133 + 35937*uk_134 + 255552*uk_135 + 194568*uk_136 + 630168*uk_137 + 95832*uk_138 + 148137*uk_139 + 3172651*uk_14 + 479787*uk_140 + 72963*uk_141 + 1553937*uk_142 + 236313*uk_143 + 35937*uk_144 + 681472*uk_145 + 518848*uk_146 + 1680448*uk_147 + 255552*uk_148 + 395032*uk_149 + 10275601*uk_15 + 1279432*uk_150 + 194568*uk_151 + 4143832*uk_152 + 630168*uk_153 + 95832*uk_154 + 300763*uk_155 + 974113*uk_156 + 148137*uk_157 + 3154963*uk_158 + 479787*uk_159 + 1562649*uk_16 + 72963*uk_160 + 10218313*uk_161 + 1553937*uk_162 + 236313*uk_163 + 35937*uk_164 + 3969*uk_17 + 3213*uk_18 + 2079*uk_19 + 63*uk_2 + 5544*uk_20 + 4221*uk_21 + 13671*uk_22 + 2079*uk_23 + 2601*uk_24 + 1683*uk_25 + 4488*uk_26 + 3417*uk_27 + 11067*uk_28 + 1683*uk_29 + 51*uk_3 + 1089*uk_30 + 2904*uk_31 + 2211*uk_32 + 7161*uk_33 + 1089*uk_34 + 7744*uk_35 + 5896*uk_36 + 19096*uk_37 + 2904*uk_38 + 4489*uk_39 + 33*uk_4 + 14539*uk_40 + 2211*uk_41 + 47089*uk_42 + 7161*uk_43 + 1089*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 114357637059*uk_47 + 73996118097*uk_48 + 197322981592*uk_49 + 88*uk_5 + 150234542803*uk_50 + 486580534153*uk_51 + 73996118097*uk_52 + 187944057*uk_53 + 152145189*uk_54 + 98446887*uk_55 + 262525032*uk_56 + 199877013*uk_57 + 647362863*uk_58 + 98446887*uk_59 + 67*uk_6 + 123165153*uk_60 + 79695099*uk_61 + 212520264*uk_62 + 161805201*uk_63 + 524055651*uk_64 + 79695099*uk_65 + 51567417*uk_66 + 137513112*uk_67 + 104697483*uk_68 + 339094833*uk_69 + 217*uk_7 + 51567417*uk_70 + 366701632*uk_71 + 279193288*uk_72 + 904252888*uk_73 + 137513112*uk_74 + 212567617*uk_75 + 688465267*uk_76 + 104697483*uk_77 + 2229805417*uk_78 + 339094833*uk_79 + 33*uk_8 + 51567417*uk_80 + 250047*uk_81 + 202419*uk_82 + 130977*uk_83 + 349272*uk_84 + 265923*uk_85 + 861273*uk_86 + 130977*uk_87 + 163863*uk_88 + 106029*uk_89 + 2242306609*uk_9 + 282744*uk_90 + 215271*uk_91 + 697221*uk_92 + 106029*uk_93 + 68607*uk_94 + 182952*uk_95 + 139293*uk_96 + 451143*uk_97 + 68607*uk_98 + 487872*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 347760*uk_100 + 1093680*uk_101 + 257040*uk_102 + 299943*uk_103 + 943299*uk_104 + 221697*uk_105 + 2966607*uk_106 + 697221*uk_107 + 163863*uk_108 + 6859*uk_109 + 899707*uk_11 + 18411*uk_110 + 28880*uk_111 + 24909*uk_112 + 78337*uk_113 + 18411*uk_114 + 49419*uk_115 + 77520*uk_116 + 66861*uk_117 + 210273*uk_118 + 49419*uk_119 + 2415003*uk_12 + 121600*uk_120 + 104880*uk_121 + 329840*uk_122 + 77520*uk_123 + 90459*uk_124 + 284487*uk_125 + 66861*uk_126 + 894691*uk_127 + 210273*uk_128 + 49419*uk_129 + 3788240*uk_13 + 132651*uk_130 + 208080*uk_131 + 179469*uk_132 + 564417*uk_133 + 132651*uk_134 + 326400*uk_135 + 281520*uk_136 + 885360*uk_137 + 208080*uk_138 + 242811*uk_139 + 3267357*uk_14 + 763623*uk_140 + 179469*uk_141 + 2401539*uk_142 + 564417*uk_143 + 132651*uk_144 + 512000*uk_145 + 441600*uk_146 + 1388800*uk_147 + 326400*uk_148 + 380880*uk_149 + 10275601*uk_15 + 1197840*uk_150 + 281520*uk_151 + 3767120*uk_152 + 885360*uk_153 + 208080*uk_154 + 328509*uk_155 + 1033137*uk_156 + 242811*uk_157 + 3249141*uk_158 + 763623*uk_159 + 2415003*uk_16 + 179469*uk_160 + 10218313*uk_161 + 2401539*uk_162 + 564417*uk_163 + 132651*uk_164 + 3969*uk_17 + 1197*uk_18 + 3213*uk_19 + 63*uk_2 + 5040*uk_20 + 4347*uk_21 + 13671*uk_22 + 3213*uk_23 + 361*uk_24 + 969*uk_25 + 1520*uk_26 + 1311*uk_27 + 4123*uk_28 + 969*uk_29 + 19*uk_3 + 2601*uk_30 + 4080*uk_31 + 3519*uk_32 + 11067*uk_33 + 2601*uk_34 + 6400*uk_35 + 5520*uk_36 + 17360*uk_37 + 4080*uk_38 + 4761*uk_39 + 51*uk_4 + 14973*uk_40 + 3519*uk_41 + 47089*uk_42 + 11067*uk_43 + 2601*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 42603825571*uk_47 + 114357637059*uk_48 + 179384528720*uk_49 + 80*uk_5 + 154719156021*uk_50 + 486580534153*uk_51 + 114357637059*uk_52 + 187944057*uk_53 + 56681541*uk_54 + 152145189*uk_55 + 238659120*uk_56 + 205843491*uk_57 + 647362863*uk_58 + 152145189*uk_59 + 69*uk_6 + 17094433*uk_60 + 45885057*uk_61 + 71976560*uk_62 + 62079783*uk_63 + 195236419*uk_64 + 45885057*uk_65 + 123165153*uk_66 + 193200240*uk_67 + 166635207*uk_68 + 524055651*uk_69 + 217*uk_7 + 123165153*uk_70 + 303059200*uk_71 + 261388560*uk_72 + 822048080*uk_73 + 193200240*uk_74 + 225447633*uk_75 + 709016469*uk_76 + 166635207*uk_77 + 2229805417*uk_78 + 524055651*uk_79 + 51*uk_8 + 123165153*uk_80 + 250047*uk_81 + 75411*uk_82 + 202419*uk_83 + 317520*uk_84 + 273861*uk_85 + 861273*uk_86 + 202419*uk_87 + 22743*uk_88 + 61047*uk_89 + 2242306609*uk_9 + 95760*uk_90 + 82593*uk_91 + 259749*uk_92 + 61047*uk_93 + 163863*uk_94 + 257040*uk_95 + 221697*uk_96 + 697221*uk_97 + 163863*uk_98 + 403200*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 357840*uk_100 + 1093680*uk_101 + 95760*uk_102 + 317583*uk_103 + 970641*uk_104 + 84987*uk_105 + 2966607*uk_106 + 259749*uk_107 + 22743*uk_108 + 300763*uk_109 + 3172651*uk_11 + 85291*uk_110 + 359120*uk_111 + 318719*uk_112 + 974113*uk_113 + 85291*uk_114 + 24187*uk_115 + 101840*uk_116 + 90383*uk_117 + 276241*uk_118 + 24187*uk_119 + 899707*uk_12 + 428800*uk_120 + 380560*uk_121 + 1163120*uk_122 + 101840*uk_123 + 337747*uk_124 + 1032269*uk_125 + 90383*uk_126 + 3154963*uk_127 + 276241*uk_128 + 24187*uk_129 + 3788240*uk_13 + 6859*uk_130 + 28880*uk_131 + 25631*uk_132 + 78337*uk_133 + 6859*uk_134 + 121600*uk_135 + 107920*uk_136 + 329840*uk_137 + 28880*uk_138 + 95779*uk_139 + 3362063*uk_14 + 292733*uk_140 + 25631*uk_141 + 894691*uk_142 + 78337*uk_143 + 6859*uk_144 + 512000*uk_145 + 454400*uk_146 + 1388800*uk_147 + 121600*uk_148 + 403280*uk_149 + 10275601*uk_15 + 1232560*uk_150 + 107920*uk_151 + 3767120*uk_152 + 329840*uk_153 + 28880*uk_154 + 357911*uk_155 + 1093897*uk_156 + 95779*uk_157 + 3343319*uk_158 + 292733*uk_159 + 899707*uk_16 + 25631*uk_160 + 10218313*uk_161 + 894691*uk_162 + 78337*uk_163 + 6859*uk_164 + 3969*uk_17 + 4221*uk_18 + 1197*uk_19 + 63*uk_2 + 5040*uk_20 + 4473*uk_21 + 13671*uk_22 + 1197*uk_23 + 4489*uk_24 + 1273*uk_25 + 5360*uk_26 + 4757*uk_27 + 14539*uk_28 + 1273*uk_29 + 67*uk_3 + 361*uk_30 + 1520*uk_31 + 1349*uk_32 + 4123*uk_33 + 361*uk_34 + 6400*uk_35 + 5680*uk_36 + 17360*uk_37 + 1520*uk_38 + 5041*uk_39 + 19*uk_4 + 15407*uk_40 + 1349*uk_41 + 47089*uk_42 + 4123*uk_43 + 361*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 150234542803*uk_47 + 42603825571*uk_48 + 179384528720*uk_49 + 80*uk_5 + 159203769239*uk_50 + 486580534153*uk_51 + 42603825571*uk_52 + 187944057*uk_53 + 199877013*uk_54 + 56681541*uk_55 + 238659120*uk_56 + 211809969*uk_57 + 647362863*uk_58 + 56681541*uk_59 + 71*uk_6 + 212567617*uk_60 + 60280369*uk_61 + 253812080*uk_62 + 225258221*uk_63 + 688465267*uk_64 + 60280369*uk_65 + 17094433*uk_66 + 71976560*uk_67 + 63879197*uk_68 + 195236419*uk_69 + 217*uk_7 + 17094433*uk_70 + 303059200*uk_71 + 268965040*uk_72 + 822048080*uk_73 + 71976560*uk_74 + 238706473*uk_75 + 729567671*uk_76 + 63879197*uk_77 + 2229805417*uk_78 + 195236419*uk_79 + 19*uk_8 + 17094433*uk_80 + 250047*uk_81 + 265923*uk_82 + 75411*uk_83 + 317520*uk_84 + 281799*uk_85 + 861273*uk_86 + 75411*uk_87 + 282807*uk_88 + 80199*uk_89 + 2242306609*uk_9 + 337680*uk_90 + 299691*uk_91 + 915957*uk_92 + 80199*uk_93 + 22743*uk_94 + 95760*uk_95 + 84987*uk_96 + 259749*uk_97 + 22743*uk_98 + 403200*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 331128*uk_100 + 984312*uk_101 + 303912*uk_102 + 335727*uk_103 + 997983*uk_104 + 308133*uk_105 + 2966607*uk_106 + 915957*uk_107 + 282807*uk_108 + 117649*uk_109 + 2320297*uk_11 + 160867*uk_110 + 172872*uk_111 + 175273*uk_112 + 521017*uk_113 + 160867*uk_114 + 219961*uk_115 + 236376*uk_116 + 239659*uk_117 + 712411*uk_118 + 219961*uk_119 + 3172651*uk_12 + 254016*uk_120 + 257544*uk_121 + 765576*uk_122 + 236376*uk_123 + 261121*uk_124 + 776209*uk_125 + 239659*uk_126 + 2307361*uk_127 + 712411*uk_128 + 219961*uk_129 + 3409416*uk_13 + 300763*uk_130 + 323208*uk_131 + 327697*uk_132 + 974113*uk_133 + 300763*uk_134 + 347328*uk_135 + 352152*uk_136 + 1046808*uk_137 + 323208*uk_138 + 357043*uk_139 + 3456769*uk_14 + 1061347*uk_140 + 327697*uk_141 + 3154963*uk_142 + 974113*uk_143 + 300763*uk_144 + 373248*uk_145 + 378432*uk_146 + 1124928*uk_147 + 347328*uk_148 + 383688*uk_149 + 10275601*uk_15 + 1140552*uk_150 + 352152*uk_151 + 3390408*uk_152 + 1046808*uk_153 + 323208*uk_154 + 389017*uk_155 + 1156393*uk_156 + 357043*uk_157 + 3437497*uk_158 + 1061347*uk_159 + 3172651*uk_16 + 327697*uk_160 + 10218313*uk_161 + 3154963*uk_162 + 974113*uk_163 + 300763*uk_164 + 3969*uk_17 + 3087*uk_18 + 4221*uk_19 + 63*uk_2 + 4536*uk_20 + 4599*uk_21 + 13671*uk_22 + 4221*uk_23 + 2401*uk_24 + 3283*uk_25 + 3528*uk_26 + 3577*uk_27 + 10633*uk_28 + 3283*uk_29 + 49*uk_3 + 4489*uk_30 + 4824*uk_31 + 4891*uk_32 + 14539*uk_33 + 4489*uk_34 + 5184*uk_35 + 5256*uk_36 + 15624*uk_37 + 4824*uk_38 + 5329*uk_39 + 67*uk_4 + 15841*uk_40 + 4891*uk_41 + 47089*uk_42 + 14539*uk_43 + 4489*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 109873023841*uk_47 + 150234542803*uk_48 + 161446075848*uk_49 + 72*uk_5 + 163688382457*uk_50 + 486580534153*uk_51 + 150234542803*uk_52 + 187944057*uk_53 + 146178711*uk_54 + 199877013*uk_55 + 214793208*uk_56 + 217776447*uk_57 + 647362863*uk_58 + 199877013*uk_59 + 73*uk_6 + 113694553*uk_60 + 155459899*uk_61 + 167061384*uk_62 + 169381681*uk_63 + 503504449*uk_64 + 155459899*uk_65 + 212567617*uk_66 + 228430872*uk_67 + 231603523*uk_68 + 688465267*uk_69 + 217*uk_7 + 212567617*uk_70 + 245477952*uk_71 + 248887368*uk_72 + 739843272*uk_73 + 228430872*uk_74 + 252344137*uk_75 + 750118873*uk_76 + 231603523*uk_77 + 2229805417*uk_78 + 688465267*uk_79 + 67*uk_8 + 212567617*uk_80 + 250047*uk_81 + 194481*uk_82 + 265923*uk_83 + 285768*uk_84 + 289737*uk_85 + 861273*uk_86 + 265923*uk_87 + 151263*uk_88 + 206829*uk_89 + 2242306609*uk_9 + 222264*uk_90 + 225351*uk_91 + 669879*uk_92 + 206829*uk_93 + 282807*uk_94 + 303912*uk_95 + 308133*uk_96 + 915957*uk_97 + 282807*uk_98 + 326592*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 321300*uk_100 + 929628*uk_101 + 209916*uk_102 + 354375*uk_103 + 1025325*uk_104 + 231525*uk_105 + 2966607*uk_106 + 669879*uk_107 + 151263*uk_108 + 21952*uk_109 + 1325884*uk_11 + 38416*uk_110 + 53312*uk_111 + 58800*uk_112 + 170128*uk_113 + 38416*uk_114 + 67228*uk_115 + 93296*uk_116 + 102900*uk_117 + 297724*uk_118 + 67228*uk_119 + 2320297*uk_12 + 129472*uk_120 + 142800*uk_121 + 413168*uk_122 + 93296*uk_123 + 157500*uk_124 + 455700*uk_125 + 102900*uk_126 + 1318492*uk_127 + 297724*uk_128 + 67228*uk_129 + 3220004*uk_13 + 117649*uk_130 + 163268*uk_131 + 180075*uk_132 + 521017*uk_133 + 117649*uk_134 + 226576*uk_135 + 249900*uk_136 + 723044*uk_137 + 163268*uk_138 + 275625*uk_139 + 3551475*uk_14 + 797475*uk_140 + 180075*uk_141 + 2307361*uk_142 + 521017*uk_143 + 117649*uk_144 + 314432*uk_145 + 346800*uk_146 + 1003408*uk_147 + 226576*uk_148 + 382500*uk_149 + 10275601*uk_15 + 1106700*uk_150 + 249900*uk_151 + 3202052*uk_152 + 723044*uk_153 + 163268*uk_154 + 421875*uk_155 + 1220625*uk_156 + 275625*uk_157 + 3531675*uk_158 + 797475*uk_159 + 2320297*uk_16 + 180075*uk_160 + 10218313*uk_161 + 2307361*uk_162 + 521017*uk_163 + 117649*uk_164 + 3969*uk_17 + 1764*uk_18 + 3087*uk_19 + 63*uk_2 + 4284*uk_20 + 4725*uk_21 + 13671*uk_22 + 3087*uk_23 + 784*uk_24 + 1372*uk_25 + 1904*uk_26 + 2100*uk_27 + 6076*uk_28 + 1372*uk_29 + 28*uk_3 + 2401*uk_30 + 3332*uk_31 + 3675*uk_32 + 10633*uk_33 + 2401*uk_34 + 4624*uk_35 + 5100*uk_36 + 14756*uk_37 + 3332*uk_38 + 5625*uk_39 + 49*uk_4 + 16275*uk_40 + 3675*uk_41 + 47089*uk_42 + 10633*uk_43 + 2401*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 62784585052*uk_47 + 109873023841*uk_48 + 152476849412*uk_49 + 68*uk_5 + 168172995675*uk_50 + 486580534153*uk_51 + 109873023841*uk_52 + 187944057*uk_53 + 83530692*uk_54 + 146178711*uk_55 + 202860252*uk_56 + 223742925*uk_57 + 647362863*uk_58 + 146178711*uk_59 + 75*uk_6 + 37124752*uk_60 + 64968316*uk_61 + 90160112*uk_62 + 99441300*uk_63 + 287716828*uk_64 + 64968316*uk_65 + 113694553*uk_66 + 157780196*uk_67 + 174022275*uk_68 + 503504449*uk_69 + 217*uk_7 + 113694553*uk_70 + 218960272*uk_71 + 241500300*uk_72 + 698740868*uk_73 + 157780196*uk_74 + 266360625*uk_75 + 770670075*uk_76 + 174022275*uk_77 + 2229805417*uk_78 + 503504449*uk_79 + 49*uk_8 + 113694553*uk_80 + 250047*uk_81 + 111132*uk_82 + 194481*uk_83 + 269892*uk_84 + 297675*uk_85 + 861273*uk_86 + 194481*uk_87 + 49392*uk_88 + 86436*uk_89 + 2242306609*uk_9 + 119952*uk_90 + 132300*uk_91 + 382788*uk_92 + 86436*uk_93 + 151263*uk_94 + 209916*uk_95 + 231525*uk_96 + 669879*uk_97 + 151263*uk_98 + 291312*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 329868*uk_100 + 929628*uk_101 + 119952*uk_102 + 373527*uk_103 + 1052667*uk_104 + 135828*uk_105 + 2966607*uk_106 + 382788*uk_107 + 49392*uk_108 + 421875*uk_109 + 3551475*uk_11 + 157500*uk_110 + 382500*uk_111 + 433125*uk_112 + 1220625*uk_113 + 157500*uk_114 + 58800*uk_115 + 142800*uk_116 + 161700*uk_117 + 455700*uk_118 + 58800*uk_119 + 1325884*uk_12 + 346800*uk_120 + 392700*uk_121 + 1106700*uk_122 + 142800*uk_123 + 444675*uk_124 + 1253175*uk_125 + 161700*uk_126 + 3531675*uk_127 + 455700*uk_128 + 58800*uk_129 + 3220004*uk_13 + 21952*uk_130 + 53312*uk_131 + 60368*uk_132 + 170128*uk_133 + 21952*uk_134 + 129472*uk_135 + 146608*uk_136 + 413168*uk_137 + 53312*uk_138 + 166012*uk_139 + 3646181*uk_14 + 467852*uk_140 + 60368*uk_141 + 1318492*uk_142 + 170128*uk_143 + 21952*uk_144 + 314432*uk_145 + 356048*uk_146 + 1003408*uk_147 + 129472*uk_148 + 403172*uk_149 + 10275601*uk_15 + 1136212*uk_150 + 146608*uk_151 + 3202052*uk_152 + 413168*uk_153 + 53312*uk_154 + 456533*uk_155 + 1286593*uk_156 + 166012*uk_157 + 3625853*uk_158 + 467852*uk_159 + 1325884*uk_16 + 60368*uk_160 + 10218313*uk_161 + 1318492*uk_162 + 170128*uk_163 + 21952*uk_164 + 3969*uk_17 + 4725*uk_18 + 1764*uk_19 + 63*uk_2 + 4284*uk_20 + 4851*uk_21 + 13671*uk_22 + 1764*uk_23 + 5625*uk_24 + 2100*uk_25 + 5100*uk_26 + 5775*uk_27 + 16275*uk_28 + 2100*uk_29 + 75*uk_3 + 784*uk_30 + 1904*uk_31 + 2156*uk_32 + 6076*uk_33 + 784*uk_34 + 4624*uk_35 + 5236*uk_36 + 14756*uk_37 + 1904*uk_38 + 5929*uk_39 + 28*uk_4 + 16709*uk_40 + 2156*uk_41 + 47089*uk_42 + 6076*uk_43 + 784*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 168172995675*uk_47 + 62784585052*uk_48 + 152476849412*uk_49 + 68*uk_5 + 172657608893*uk_50 + 486580534153*uk_51 + 62784585052*uk_52 + 187944057*uk_53 + 223742925*uk_54 + 83530692*uk_55 + 202860252*uk_56 + 229709403*uk_57 + 647362863*uk_58 + 83530692*uk_59 + 77*uk_6 + 266360625*uk_60 + 99441300*uk_61 + 241500300*uk_62 + 273463575*uk_63 + 770670075*uk_64 + 99441300*uk_65 + 37124752*uk_66 + 90160112*uk_67 + 102093068*uk_68 + 287716828*uk_69 + 217*uk_7 + 37124752*uk_70 + 218960272*uk_71 + 247940308*uk_72 + 698740868*uk_73 + 90160112*uk_74 + 280755937*uk_75 + 791221277*uk_76 + 102093068*uk_77 + 2229805417*uk_78 + 287716828*uk_79 + 28*uk_8 + 37124752*uk_80 + 250047*uk_81 + 297675*uk_82 + 111132*uk_83 + 269892*uk_84 + 305613*uk_85 + 861273*uk_86 + 111132*uk_87 + 354375*uk_88 + 132300*uk_89 + 2242306609*uk_9 + 321300*uk_90 + 363825*uk_91 + 1025325*uk_92 + 132300*uk_93 + 49392*uk_94 + 119952*uk_95 + 135828*uk_96 + 382788*uk_97 + 49392*uk_98 + 291312*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 298620*uk_100 + 820260*uk_101 + 283500*uk_102 + 393183*uk_103 + 1080009*uk_104 + 373275*uk_105 + 2966607*uk_106 + 1025325*uk_107 + 354375*uk_108 + 32768*uk_109 + 1515296*uk_11 + 76800*uk_110 + 61440*uk_111 + 80896*uk_112 + 222208*uk_113 + 76800*uk_114 + 180000*uk_115 + 144000*uk_116 + 189600*uk_117 + 520800*uk_118 + 180000*uk_119 + 3551475*uk_12 + 115200*uk_120 + 151680*uk_121 + 416640*uk_122 + 144000*uk_123 + 199712*uk_124 + 548576*uk_125 + 189600*uk_126 + 1506848*uk_127 + 520800*uk_128 + 180000*uk_129 + 2841180*uk_13 + 421875*uk_130 + 337500*uk_131 + 444375*uk_132 + 1220625*uk_133 + 421875*uk_134 + 270000*uk_135 + 355500*uk_136 + 976500*uk_137 + 337500*uk_138 + 468075*uk_139 + 3740887*uk_14 + 1285725*uk_140 + 444375*uk_141 + 3531675*uk_142 + 1220625*uk_143 + 421875*uk_144 + 216000*uk_145 + 284400*uk_146 + 781200*uk_147 + 270000*uk_148 + 374460*uk_149 + 10275601*uk_15 + 1028580*uk_150 + 355500*uk_151 + 2825340*uk_152 + 976500*uk_153 + 337500*uk_154 + 493039*uk_155 + 1354297*uk_156 + 468075*uk_157 + 3720031*uk_158 + 1285725*uk_159 + 3551475*uk_16 + 444375*uk_160 + 10218313*uk_161 + 3531675*uk_162 + 1220625*uk_163 + 421875*uk_164 + 3969*uk_17 + 2016*uk_18 + 4725*uk_19 + 63*uk_2 + 3780*uk_20 + 4977*uk_21 + 13671*uk_22 + 4725*uk_23 + 1024*uk_24 + 2400*uk_25 + 1920*uk_26 + 2528*uk_27 + 6944*uk_28 + 2400*uk_29 + 32*uk_3 + 5625*uk_30 + 4500*uk_31 + 5925*uk_32 + 16275*uk_33 + 5625*uk_34 + 3600*uk_35 + 4740*uk_36 + 13020*uk_37 + 4500*uk_38 + 6241*uk_39 + 75*uk_4 + 17143*uk_40 + 5925*uk_41 + 47089*uk_42 + 16275*uk_43 + 5625*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 71753811488*uk_47 + 168172995675*uk_48 + 134538396540*uk_49 + 60*uk_5 + 177142222111*uk_50 + 486580534153*uk_51 + 168172995675*uk_52 + 187944057*uk_53 + 95463648*uk_54 + 223742925*uk_55 + 178994340*uk_56 + 235675881*uk_57 + 647362863*uk_58 + 223742925*uk_59 + 79*uk_6 + 48489472*uk_60 + 113647200*uk_61 + 90917760*uk_62 + 119708384*uk_63 + 328819232*uk_64 + 113647200*uk_65 + 266360625*uk_66 + 213088500*uk_67 + 280566525*uk_68 + 770670075*uk_69 + 217*uk_7 + 266360625*uk_70 + 170470800*uk_71 + 224453220*uk_72 + 616536060*uk_73 + 213088500*uk_74 + 295530073*uk_75 + 811772479*uk_76 + 280566525*uk_77 + 2229805417*uk_78 + 770670075*uk_79 + 75*uk_8 + 266360625*uk_80 + 250047*uk_81 + 127008*uk_82 + 297675*uk_83 + 238140*uk_84 + 313551*uk_85 + 861273*uk_86 + 297675*uk_87 + 64512*uk_88 + 151200*uk_89 + 2242306609*uk_9 + 120960*uk_90 + 159264*uk_91 + 437472*uk_92 + 151200*uk_93 + 354375*uk_94 + 283500*uk_95 + 373275*uk_96 + 1025325*uk_97 + 354375*uk_98 + 226800*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 306180*uk_100 + 820260*uk_101 + 120960*uk_102 + 413343*uk_103 + 1107351*uk_104 + 163296*uk_105 + 2966607*uk_106 + 437472*uk_107 + 64512*uk_108 + 117649*uk_109 + 2320297*uk_11 + 76832*uk_110 + 144060*uk_111 + 194481*uk_112 + 521017*uk_113 + 76832*uk_114 + 50176*uk_115 + 94080*uk_116 + 127008*uk_117 + 340256*uk_118 + 50176*uk_119 + 1515296*uk_12 + 176400*uk_120 + 238140*uk_121 + 637980*uk_122 + 94080*uk_123 + 321489*uk_124 + 861273*uk_125 + 127008*uk_126 + 2307361*uk_127 + 340256*uk_128 + 50176*uk_129 + 2841180*uk_13 + 32768*uk_130 + 61440*uk_131 + 82944*uk_132 + 222208*uk_133 + 32768*uk_134 + 115200*uk_135 + 155520*uk_136 + 416640*uk_137 + 61440*uk_138 + 209952*uk_139 + 3835593*uk_14 + 562464*uk_140 + 82944*uk_141 + 1506848*uk_142 + 222208*uk_143 + 32768*uk_144 + 216000*uk_145 + 291600*uk_146 + 781200*uk_147 + 115200*uk_148 + 393660*uk_149 + 10275601*uk_15 + 1054620*uk_150 + 155520*uk_151 + 2825340*uk_152 + 416640*uk_153 + 61440*uk_154 + 531441*uk_155 + 1423737*uk_156 + 209952*uk_157 + 3814209*uk_158 + 562464*uk_159 + 1515296*uk_16 + 82944*uk_160 + 10218313*uk_161 + 1506848*uk_162 + 222208*uk_163 + 32768*uk_164 + 3969*uk_17 + 3087*uk_18 + 2016*uk_19 + 63*uk_2 + 3780*uk_20 + 5103*uk_21 + 13671*uk_22 + 2016*uk_23 + 2401*uk_24 + 1568*uk_25 + 2940*uk_26 + 3969*uk_27 + 10633*uk_28 + 1568*uk_29 + 49*uk_3 + 1024*uk_30 + 1920*uk_31 + 2592*uk_32 + 6944*uk_33 + 1024*uk_34 + 3600*uk_35 + 4860*uk_36 + 13020*uk_37 + 1920*uk_38 + 6561*uk_39 + 32*uk_4 + 17577*uk_40 + 2592*uk_41 + 47089*uk_42 + 6944*uk_43 + 1024*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 109873023841*uk_47 + 71753811488*uk_48 + 134538396540*uk_49 + 60*uk_5 + 181626835329*uk_50 + 486580534153*uk_51 + 71753811488*uk_52 + 187944057*uk_53 + 146178711*uk_54 + 95463648*uk_55 + 178994340*uk_56 + 241642359*uk_57 + 647362863*uk_58 + 95463648*uk_59 + 81*uk_6 + 113694553*uk_60 + 74249504*uk_61 + 139217820*uk_62 + 187944057*uk_63 + 503504449*uk_64 + 74249504*uk_65 + 48489472*uk_66 + 90917760*uk_67 + 122738976*uk_68 + 328819232*uk_69 + 217*uk_7 + 48489472*uk_70 + 170470800*uk_71 + 230135580*uk_72 + 616536060*uk_73 + 90917760*uk_74 + 310683033*uk_75 + 832323681*uk_76 + 122738976*uk_77 + 2229805417*uk_78 + 328819232*uk_79 + 32*uk_8 + 48489472*uk_80 + 250047*uk_81 + 194481*uk_82 + 127008*uk_83 + 238140*uk_84 + 321489*uk_85 + 861273*uk_86 + 127008*uk_87 + 151263*uk_88 + 98784*uk_89 + 2242306609*uk_9 + 185220*uk_90 + 250047*uk_91 + 669879*uk_92 + 98784*uk_93 + 64512*uk_94 + 120960*uk_95 + 163296*uk_96 + 437472*uk_97 + 64512*uk_98 + 226800*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 292824*uk_100 + 765576*uk_101 + 172872*uk_102 + 434007*uk_103 + 1134693*uk_104 + 256221*uk_105 + 2966607*uk_106 + 669879*uk_107 + 151263*uk_108 + 79507*uk_109 + 2036179*uk_11 + 90601*uk_110 + 103544*uk_111 + 153467*uk_112 + 401233*uk_113 + 90601*uk_114 + 103243*uk_115 + 117992*uk_116 + 174881*uk_117 + 457219*uk_118 + 103243*uk_119 + 2320297*uk_12 + 134848*uk_120 + 199864*uk_121 + 522536*uk_122 + 117992*uk_123 + 296227*uk_124 + 774473*uk_125 + 174881*uk_126 + 2024827*uk_127 + 457219*uk_128 + 103243*uk_129 + 2651768*uk_13 + 117649*uk_130 + 134456*uk_131 + 199283*uk_132 + 521017*uk_133 + 117649*uk_134 + 153664*uk_135 + 227752*uk_136 + 595448*uk_137 + 134456*uk_138 + 337561*uk_139 + 3930299*uk_14 + 882539*uk_140 + 199283*uk_141 + 2307361*uk_142 + 521017*uk_143 + 117649*uk_144 + 175616*uk_145 + 260288*uk_146 + 680512*uk_147 + 153664*uk_148 + 385784*uk_149 + 10275601*uk_15 + 1008616*uk_150 + 227752*uk_151 + 2636984*uk_152 + 595448*uk_153 + 134456*uk_154 + 571787*uk_155 + 1494913*uk_156 + 337561*uk_157 + 3908387*uk_158 + 882539*uk_159 + 2320297*uk_16 + 199283*uk_160 + 10218313*uk_161 + 2307361*uk_162 + 521017*uk_163 + 117649*uk_164 + 3969*uk_17 + 2709*uk_18 + 3087*uk_19 + 63*uk_2 + 3528*uk_20 + 5229*uk_21 + 13671*uk_22 + 3087*uk_23 + 1849*uk_24 + 2107*uk_25 + 2408*uk_26 + 3569*uk_27 + 9331*uk_28 + 2107*uk_29 + 43*uk_3 + 2401*uk_30 + 2744*uk_31 + 4067*uk_32 + 10633*uk_33 + 2401*uk_34 + 3136*uk_35 + 4648*uk_36 + 12152*uk_37 + 2744*uk_38 + 6889*uk_39 + 49*uk_4 + 18011*uk_40 + 4067*uk_41 + 47089*uk_42 + 10633*uk_43 + 2401*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 96419184187*uk_47 + 109873023841*uk_48 + 125569170104*uk_49 + 56*uk_5 + 186111448547*uk_50 + 486580534153*uk_51 + 109873023841*uk_52 + 187944057*uk_53 + 128279277*uk_54 + 146178711*uk_55 + 167061384*uk_56 + 247608837*uk_57 + 647362863*uk_58 + 146178711*uk_59 + 83*uk_6 + 87555697*uk_60 + 99772771*uk_61 + 114026024*uk_62 + 169002857*uk_63 + 441850843*uk_64 + 99772771*uk_65 + 113694553*uk_66 + 129936632*uk_67 + 192584651*uk_68 + 503504449*uk_69 + 217*uk_7 + 113694553*uk_70 + 148499008*uk_71 + 220096744*uk_72 + 575433656*uk_73 + 129936632*uk_74 + 326214817*uk_75 + 852874883*uk_76 + 192584651*uk_77 + 2229805417*uk_78 + 503504449*uk_79 + 49*uk_8 + 113694553*uk_80 + 250047*uk_81 + 170667*uk_82 + 194481*uk_83 + 222264*uk_84 + 329427*uk_85 + 861273*uk_86 + 194481*uk_87 + 116487*uk_88 + 132741*uk_89 + 2242306609*uk_9 + 151704*uk_90 + 224847*uk_91 + 587853*uk_92 + 132741*uk_93 + 151263*uk_94 + 172872*uk_95 + 256221*uk_96 + 669879*uk_97 + 151263*uk_98 + 197568*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 278460*uk_100 + 710892*uk_101 + 140868*uk_102 + 455175*uk_103 + 1162035*uk_104 + 230265*uk_105 + 2966607*uk_106 + 587853*uk_107 + 116487*uk_108 + 512*uk_109 + 378824*uk_11 + 2752*uk_110 + 3328*uk_111 + 5440*uk_112 + 13888*uk_113 + 2752*uk_114 + 14792*uk_115 + 17888*uk_116 + 29240*uk_117 + 74648*uk_118 + 14792*uk_119 + 2036179*uk_12 + 21632*uk_120 + 35360*uk_121 + 90272*uk_122 + 17888*uk_123 + 57800*uk_124 + 147560*uk_125 + 29240*uk_126 + 376712*uk_127 + 74648*uk_128 + 14792*uk_129 + 2462356*uk_13 + 79507*uk_130 + 96148*uk_131 + 157165*uk_132 + 401233*uk_133 + 79507*uk_134 + 116272*uk_135 + 190060*uk_136 + 485212*uk_137 + 96148*uk_138 + 310675*uk_139 + 4025005*uk_14 + 793135*uk_140 + 157165*uk_141 + 2024827*uk_142 + 401233*uk_143 + 79507*uk_144 + 140608*uk_145 + 229840*uk_146 + 586768*uk_147 + 116272*uk_148 + 375700*uk_149 + 10275601*uk_15 + 959140*uk_150 + 190060*uk_151 + 2448628*uk_152 + 485212*uk_153 + 96148*uk_154 + 614125*uk_155 + 1567825*uk_156 + 310675*uk_157 + 4002565*uk_158 + 793135*uk_159 + 2036179*uk_16 + 157165*uk_160 + 10218313*uk_161 + 2024827*uk_162 + 401233*uk_163 + 79507*uk_164 + 3969*uk_17 + 504*uk_18 + 2709*uk_19 + 63*uk_2 + 3276*uk_20 + 5355*uk_21 + 13671*uk_22 + 2709*uk_23 + 64*uk_24 + 344*uk_25 + 416*uk_26 + 680*uk_27 + 1736*uk_28 + 344*uk_29 + 8*uk_3 + 1849*uk_30 + 2236*uk_31 + 3655*uk_32 + 9331*uk_33 + 1849*uk_34 + 2704*uk_35 + 4420*uk_36 + 11284*uk_37 + 2236*uk_38 + 7225*uk_39 + 43*uk_4 + 18445*uk_40 + 3655*uk_41 + 47089*uk_42 + 9331*uk_43 + 1849*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 17938452872*uk_47 + 96419184187*uk_48 + 116599943668*uk_49 + 52*uk_5 + 190596061765*uk_50 + 486580534153*uk_51 + 96419184187*uk_52 + 187944057*uk_53 + 23865912*uk_54 + 128279277*uk_55 + 155128428*uk_56 + 253575315*uk_57 + 647362863*uk_58 + 128279277*uk_59 + 85*uk_6 + 3030592*uk_60 + 16289432*uk_61 + 19698848*uk_62 + 32200040*uk_63 + 82204808*uk_64 + 16289432*uk_65 + 87555697*uk_66 + 105881308*uk_67 + 173075215*uk_68 + 441850843*uk_69 + 217*uk_7 + 87555697*uk_70 + 128042512*uk_71 + 209300260*uk_72 + 534331252*uk_73 + 105881308*uk_74 + 342125425*uk_75 + 873426085*uk_76 + 173075215*uk_77 + 2229805417*uk_78 + 441850843*uk_79 + 43*uk_8 + 87555697*uk_80 + 250047*uk_81 + 31752*uk_82 + 170667*uk_83 + 206388*uk_84 + 337365*uk_85 + 861273*uk_86 + 170667*uk_87 + 4032*uk_88 + 21672*uk_89 + 2242306609*uk_9 + 26208*uk_90 + 42840*uk_91 + 109368*uk_92 + 21672*uk_93 + 116487*uk_94 + 140868*uk_95 + 230265*uk_96 + 587853*uk_97 + 116487*uk_98 + 170352*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 285012*uk_100 + 710892*uk_101 + 26208*uk_102 + 476847*uk_103 + 1189377*uk_104 + 43848*uk_105 + 2966607*uk_106 + 109368*uk_107 + 4032*uk_108 + 15625*uk_109 + 1183825*uk_11 + 5000*uk_110 + 32500*uk_111 + 54375*uk_112 + 135625*uk_113 + 5000*uk_114 + 1600*uk_115 + 10400*uk_116 + 17400*uk_117 + 43400*uk_118 + 1600*uk_119 + 378824*uk_12 + 67600*uk_120 + 113100*uk_121 + 282100*uk_122 + 10400*uk_123 + 189225*uk_124 + 471975*uk_125 + 17400*uk_126 + 1177225*uk_127 + 43400*uk_128 + 1600*uk_129 + 2462356*uk_13 + 512*uk_130 + 3328*uk_131 + 5568*uk_132 + 13888*uk_133 + 512*uk_134 + 21632*uk_135 + 36192*uk_136 + 90272*uk_137 + 3328*uk_138 + 60552*uk_139 + 4119711*uk_14 + 151032*uk_140 + 5568*uk_141 + 376712*uk_142 + 13888*uk_143 + 512*uk_144 + 140608*uk_145 + 235248*uk_146 + 586768*uk_147 + 21632*uk_148 + 393588*uk_149 + 10275601*uk_15 + 981708*uk_150 + 36192*uk_151 + 2448628*uk_152 + 90272*uk_153 + 3328*uk_154 + 658503*uk_155 + 1642473*uk_156 + 60552*uk_157 + 4096743*uk_158 + 151032*uk_159 + 378824*uk_16 + 5568*uk_160 + 10218313*uk_161 + 376712*uk_162 + 13888*uk_163 + 512*uk_164 + 3969*uk_17 + 1575*uk_18 + 504*uk_19 + 63*uk_2 + 3276*uk_20 + 5481*uk_21 + 13671*uk_22 + 504*uk_23 + 625*uk_24 + 200*uk_25 + 1300*uk_26 + 2175*uk_27 + 5425*uk_28 + 200*uk_29 + 25*uk_3 + 64*uk_30 + 416*uk_31 + 696*uk_32 + 1736*uk_33 + 64*uk_34 + 2704*uk_35 + 4524*uk_36 + 11284*uk_37 + 416*uk_38 + 7569*uk_39 + 8*uk_4 + 18879*uk_40 + 696*uk_41 + 47089*uk_42 + 1736*uk_43 + 64*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 56057665225*uk_47 + 17938452872*uk_48 + 116599943668*uk_49 + 52*uk_5 + 195080674983*uk_50 + 486580534153*uk_51 + 17938452872*uk_52 + 187944057*uk_53 + 74580975*uk_54 + 23865912*uk_55 + 155128428*uk_56 + 259541793*uk_57 + 647362863*uk_58 + 23865912*uk_59 + 87*uk_6 + 29595625*uk_60 + 9470600*uk_61 + 61558900*uk_62 + 102992775*uk_63 + 256890025*uk_64 + 9470600*uk_65 + 3030592*uk_66 + 19698848*uk_67 + 32957688*uk_68 + 82204808*uk_69 + 217*uk_7 + 3030592*uk_70 + 128042512*uk_71 + 214224972*uk_72 + 534331252*uk_73 + 19698848*uk_74 + 358414857*uk_75 + 893977287*uk_76 + 32957688*uk_77 + 2229805417*uk_78 + 82204808*uk_79 + 8*uk_8 + 3030592*uk_80 + 250047*uk_81 + 99225*uk_82 + 31752*uk_83 + 206388*uk_84 + 345303*uk_85 + 861273*uk_86 + 31752*uk_87 + 39375*uk_88 + 12600*uk_89 + 2242306609*uk_9 + 81900*uk_90 + 137025*uk_91 + 341775*uk_92 + 12600*uk_93 + 4032*uk_94 + 26208*uk_95 + 43848*uk_96 + 109368*uk_97 + 4032*uk_98 + 170352*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 269136*uk_100 + 656208*uk_101 + 75600*uk_102 + 499023*uk_103 + 1216719*uk_104 + 140175*uk_105 + 2966607*uk_106 + 341775*uk_107 + 39375*uk_108 + 125*uk_109 + 236765*uk_11 + 625*uk_110 + 1200*uk_111 + 2225*uk_112 + 5425*uk_113 + 625*uk_114 + 3125*uk_115 + 6000*uk_116 + 11125*uk_117 + 27125*uk_118 + 3125*uk_119 + 1183825*uk_12 + 11520*uk_120 + 21360*uk_121 + 52080*uk_122 + 6000*uk_123 + 39605*uk_124 + 96565*uk_125 + 11125*uk_126 + 235445*uk_127 + 27125*uk_128 + 3125*uk_129 + 2272944*uk_13 + 15625*uk_130 + 30000*uk_131 + 55625*uk_132 + 135625*uk_133 + 15625*uk_134 + 57600*uk_135 + 106800*uk_136 + 260400*uk_137 + 30000*uk_138 + 198025*uk_139 + 4214417*uk_14 + 482825*uk_140 + 55625*uk_141 + 1177225*uk_142 + 135625*uk_143 + 15625*uk_144 + 110592*uk_145 + 205056*uk_146 + 499968*uk_147 + 57600*uk_148 + 380208*uk_149 + 10275601*uk_15 + 927024*uk_150 + 106800*uk_151 + 2260272*uk_152 + 260400*uk_153 + 30000*uk_154 + 704969*uk_155 + 1718857*uk_156 + 198025*uk_157 + 4190921*uk_158 + 482825*uk_159 + 1183825*uk_16 + 55625*uk_160 + 10218313*uk_161 + 1177225*uk_162 + 135625*uk_163 + 15625*uk_164 + 3969*uk_17 + 315*uk_18 + 1575*uk_19 + 63*uk_2 + 3024*uk_20 + 5607*uk_21 + 13671*uk_22 + 1575*uk_23 + 25*uk_24 + 125*uk_25 + 240*uk_26 + 445*uk_27 + 1085*uk_28 + 125*uk_29 + 5*uk_3 + 625*uk_30 + 1200*uk_31 + 2225*uk_32 + 5425*uk_33 + 625*uk_34 + 2304*uk_35 + 4272*uk_36 + 10416*uk_37 + 1200*uk_38 + 7921*uk_39 + 25*uk_4 + 19313*uk_40 + 2225*uk_41 + 47089*uk_42 + 5425*uk_43 + 625*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 11211533045*uk_47 + 56057665225*uk_48 + 107630717232*uk_49 + 48*uk_5 + 199565288201*uk_50 + 486580534153*uk_51 + 56057665225*uk_52 + 187944057*uk_53 + 14916195*uk_54 + 74580975*uk_55 + 143195472*uk_56 + 265508271*uk_57 + 647362863*uk_58 + 74580975*uk_59 + 89*uk_6 + 1183825*uk_60 + 5919125*uk_61 + 11364720*uk_62 + 21072085*uk_63 + 51378005*uk_64 + 5919125*uk_65 + 29595625*uk_66 + 56823600*uk_67 + 105360425*uk_68 + 256890025*uk_69 + 217*uk_7 + 29595625*uk_70 + 109101312*uk_71 + 202292016*uk_72 + 493228848*uk_73 + 56823600*uk_74 + 375083113*uk_75 + 914528489*uk_76 + 105360425*uk_77 + 2229805417*uk_78 + 256890025*uk_79 + 25*uk_8 + 29595625*uk_80 + 250047*uk_81 + 19845*uk_82 + 99225*uk_83 + 190512*uk_84 + 353241*uk_85 + 861273*uk_86 + 99225*uk_87 + 1575*uk_88 + 7875*uk_89 + 2242306609*uk_9 + 15120*uk_90 + 28035*uk_91 + 68355*uk_92 + 7875*uk_93 + 39375*uk_94 + 75600*uk_95 + 140175*uk_96 + 341775*uk_97 + 39375*uk_98 + 145152*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 275184*uk_100 + 656208*uk_101 + 15120*uk_102 + 521703*uk_103 + 1244061*uk_104 + 28665*uk_105 + 2966607*uk_106 + 68355*uk_107 + 1575*uk_108 + 35937*uk_109 + 1562649*uk_11 + 5445*uk_110 + 52272*uk_111 + 99099*uk_112 + 236313*uk_113 + 5445*uk_114 + 825*uk_115 + 7920*uk_116 + 15015*uk_117 + 35805*uk_118 + 825*uk_119 + 236765*uk_12 + 76032*uk_120 + 144144*uk_121 + 343728*uk_122 + 7920*uk_123 + 273273*uk_124 + 651651*uk_125 + 15015*uk_126 + 1553937*uk_127 + 35805*uk_128 + 825*uk_129 + 2272944*uk_13 + 125*uk_130 + 1200*uk_131 + 2275*uk_132 + 5425*uk_133 + 125*uk_134 + 11520*uk_135 + 21840*uk_136 + 52080*uk_137 + 1200*uk_138 + 41405*uk_139 + 4309123*uk_14 + 98735*uk_140 + 2275*uk_141 + 235445*uk_142 + 5425*uk_143 + 125*uk_144 + 110592*uk_145 + 209664*uk_146 + 499968*uk_147 + 11520*uk_148 + 397488*uk_149 + 10275601*uk_15 + 947856*uk_150 + 21840*uk_151 + 2260272*uk_152 + 52080*uk_153 + 1200*uk_154 + 753571*uk_155 + 1796977*uk_156 + 41405*uk_157 + 4285099*uk_158 + 98735*uk_159 + 236765*uk_16 + 2275*uk_160 + 10218313*uk_161 + 235445*uk_162 + 5425*uk_163 + 125*uk_164 + 3969*uk_17 + 2079*uk_18 + 315*uk_19 + 63*uk_2 + 3024*uk_20 + 5733*uk_21 + 13671*uk_22 + 315*uk_23 + 1089*uk_24 + 165*uk_25 + 1584*uk_26 + 3003*uk_27 + 7161*uk_28 + 165*uk_29 + 33*uk_3 + 25*uk_30 + 240*uk_31 + 455*uk_32 + 1085*uk_33 + 25*uk_34 + 2304*uk_35 + 4368*uk_36 + 10416*uk_37 + 240*uk_38 + 8281*uk_39 + 5*uk_4 + 19747*uk_40 + 455*uk_41 + 47089*uk_42 + 1085*uk_43 + 25*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 73996118097*uk_47 + 11211533045*uk_48 + 107630717232*uk_49 + 48*uk_5 + 204049901419*uk_50 + 486580534153*uk_51 + 11211533045*uk_52 + 187944057*uk_53 + 98446887*uk_54 + 14916195*uk_55 + 143195472*uk_56 + 271474749*uk_57 + 647362863*uk_58 + 14916195*uk_59 + 91*uk_6 + 51567417*uk_60 + 7813245*uk_61 + 75007152*uk_62 + 142201059*uk_63 + 339094833*uk_64 + 7813245*uk_65 + 1183825*uk_66 + 11364720*uk_67 + 21545615*uk_68 + 51378005*uk_69 + 217*uk_7 + 1183825*uk_70 + 109101312*uk_71 + 206837904*uk_72 + 493228848*uk_73 + 11364720*uk_74 + 392130193*uk_75 + 935079691*uk_76 + 21545615*uk_77 + 2229805417*uk_78 + 51378005*uk_79 + 5*uk_8 + 1183825*uk_80 + 250047*uk_81 + 130977*uk_82 + 19845*uk_83 + 190512*uk_84 + 361179*uk_85 + 861273*uk_86 + 19845*uk_87 + 68607*uk_88 + 10395*uk_89 + 2242306609*uk_9 + 99792*uk_90 + 189189*uk_91 + 451143*uk_92 + 10395*uk_93 + 1575*uk_94 + 15120*uk_95 + 28665*uk_96 + 68355*uk_97 + 1575*uk_98 + 145152*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 257796*uk_100 + 601524*uk_101 + 91476*uk_102 + 544887*uk_103 + 1271403*uk_104 + 193347*uk_105 + 2966607*uk_106 + 451143*uk_107 + 68607*uk_108 + 4096*uk_109 + 757648*uk_11 + 8448*uk_110 + 11264*uk_111 + 23808*uk_112 + 55552*uk_113 + 8448*uk_114 + 17424*uk_115 + 23232*uk_116 + 49104*uk_117 + 114576*uk_118 + 17424*uk_119 + 1562649*uk_12 + 30976*uk_120 + 65472*uk_121 + 152768*uk_122 + 23232*uk_123 + 138384*uk_124 + 322896*uk_125 + 49104*uk_126 + 753424*uk_127 + 114576*uk_128 + 17424*uk_129 + 2083532*uk_13 + 35937*uk_130 + 47916*uk_131 + 101277*uk_132 + 236313*uk_133 + 35937*uk_134 + 63888*uk_135 + 135036*uk_136 + 315084*uk_137 + 47916*uk_138 + 285417*uk_139 + 4403829*uk_14 + 665973*uk_140 + 101277*uk_141 + 1553937*uk_142 + 236313*uk_143 + 35937*uk_144 + 85184*uk_145 + 180048*uk_146 + 420112*uk_147 + 63888*uk_148 + 380556*uk_149 + 10275601*uk_15 + 887964*uk_150 + 135036*uk_151 + 2071916*uk_152 + 315084*uk_153 + 47916*uk_154 + 804357*uk_155 + 1876833*uk_156 + 285417*uk_157 + 4379277*uk_158 + 665973*uk_159 + 1562649*uk_16 + 101277*uk_160 + 10218313*uk_161 + 1553937*uk_162 + 236313*uk_163 + 35937*uk_164 + 3969*uk_17 + 1008*uk_18 + 2079*uk_19 + 63*uk_2 + 2772*uk_20 + 5859*uk_21 + 13671*uk_22 + 2079*uk_23 + 256*uk_24 + 528*uk_25 + 704*uk_26 + 1488*uk_27 + 3472*uk_28 + 528*uk_29 + 16*uk_3 + 1089*uk_30 + 1452*uk_31 + 3069*uk_32 + 7161*uk_33 + 1089*uk_34 + 1936*uk_35 + 4092*uk_36 + 9548*uk_37 + 1452*uk_38 + 8649*uk_39 + 33*uk_4 + 20181*uk_40 + 3069*uk_41 + 47089*uk_42 + 7161*uk_43 + 1089*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 35876905744*uk_47 + 73996118097*uk_48 + 98661490796*uk_49 + 44*uk_5 + 208534514637*uk_50 + 486580534153*uk_51 + 73996118097*uk_52 + 187944057*uk_53 + 47731824*uk_54 + 98446887*uk_55 + 131262516*uk_56 + 277441227*uk_57 + 647362863*uk_58 + 98446887*uk_59 + 93*uk_6 + 12122368*uk_60 + 25002384*uk_61 + 33336512*uk_62 + 70461264*uk_63 + 164409616*uk_64 + 25002384*uk_65 + 51567417*uk_66 + 68756556*uk_67 + 145326357*uk_68 + 339094833*uk_69 + 217*uk_7 + 51567417*uk_70 + 91675408*uk_71 + 193768476*uk_72 + 452126444*uk_73 + 68756556*uk_74 + 409556097*uk_75 + 955630893*uk_76 + 145326357*uk_77 + 2229805417*uk_78 + 339094833*uk_79 + 33*uk_8 + 51567417*uk_80 + 250047*uk_81 + 63504*uk_82 + 130977*uk_83 + 174636*uk_84 + 369117*uk_85 + 861273*uk_86 + 130977*uk_87 + 16128*uk_88 + 33264*uk_89 + 2242306609*uk_9 + 44352*uk_90 + 93744*uk_91 + 218736*uk_92 + 33264*uk_93 + 68607*uk_94 + 91476*uk_95 + 193347*uk_96 + 451143*uk_97 + 68607*uk_98 + 121968*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 263340*uk_100 + 601524*uk_101 + 44352*uk_102 + 568575*uk_103 + 1298745*uk_104 + 95760*uk_105 + 2966607*uk_106 + 218736*uk_107 + 16128*uk_108 + 79507*uk_109 + 2036179*uk_11 + 29584*uk_110 + 81356*uk_111 + 175655*uk_112 + 401233*uk_113 + 29584*uk_114 + 11008*uk_115 + 30272*uk_116 + 65360*uk_117 + 149296*uk_118 + 11008*uk_119 + 757648*uk_12 + 83248*uk_120 + 179740*uk_121 + 410564*uk_122 + 30272*uk_123 + 388075*uk_124 + 886445*uk_125 + 65360*uk_126 + 2024827*uk_127 + 149296*uk_128 + 11008*uk_129 + 2083532*uk_13 + 4096*uk_130 + 11264*uk_131 + 24320*uk_132 + 55552*uk_133 + 4096*uk_134 + 30976*uk_135 + 66880*uk_136 + 152768*uk_137 + 11264*uk_138 + 144400*uk_139 + 4498535*uk_14 + 329840*uk_140 + 24320*uk_141 + 753424*uk_142 + 55552*uk_143 + 4096*uk_144 + 85184*uk_145 + 183920*uk_146 + 420112*uk_147 + 30976*uk_148 + 397100*uk_149 + 10275601*uk_15 + 907060*uk_150 + 66880*uk_151 + 2071916*uk_152 + 152768*uk_153 + 11264*uk_154 + 857375*uk_155 + 1958425*uk_156 + 144400*uk_157 + 4473455*uk_158 + 329840*uk_159 + 757648*uk_16 + 24320*uk_160 + 10218313*uk_161 + 753424*uk_162 + 55552*uk_163 + 4096*uk_164 + 3969*uk_17 + 2709*uk_18 + 1008*uk_19 + 63*uk_2 + 2772*uk_20 + 5985*uk_21 + 13671*uk_22 + 1008*uk_23 + 1849*uk_24 + 688*uk_25 + 1892*uk_26 + 4085*uk_27 + 9331*uk_28 + 688*uk_29 + 43*uk_3 + 256*uk_30 + 704*uk_31 + 1520*uk_32 + 3472*uk_33 + 256*uk_34 + 1936*uk_35 + 4180*uk_36 + 9548*uk_37 + 704*uk_38 + 9025*uk_39 + 16*uk_4 + 20615*uk_40 + 1520*uk_41 + 47089*uk_42 + 3472*uk_43 + 256*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 96419184187*uk_47 + 35876905744*uk_48 + 98661490796*uk_49 + 44*uk_5 + 213019127855*uk_50 + 486580534153*uk_51 + 35876905744*uk_52 + 187944057*uk_53 + 128279277*uk_54 + 47731824*uk_55 + 131262516*uk_56 + 283407705*uk_57 + 647362863*uk_58 + 47731824*uk_59 + 95*uk_6 + 87555697*uk_60 + 32578864*uk_61 + 89591876*uk_62 + 193437005*uk_63 + 441850843*uk_64 + 32578864*uk_65 + 12122368*uk_66 + 33336512*uk_67 + 71976560*uk_68 + 164409616*uk_69 + 217*uk_7 + 12122368*uk_70 + 91675408*uk_71 + 197935540*uk_72 + 452126444*uk_73 + 33336512*uk_74 + 427360825*uk_75 + 976182095*uk_76 + 71976560*uk_77 + 2229805417*uk_78 + 164409616*uk_79 + 16*uk_8 + 12122368*uk_80 + 250047*uk_81 + 170667*uk_82 + 63504*uk_83 + 174636*uk_84 + 377055*uk_85 + 861273*uk_86 + 63504*uk_87 + 116487*uk_88 + 43344*uk_89 + 2242306609*uk_9 + 119196*uk_90 + 257355*uk_91 + 587853*uk_92 + 43344*uk_93 + 16128*uk_94 + 44352*uk_95 + 95760*uk_96 + 218736*uk_97 + 16128*uk_98 + 121968*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 244440*uk_100 + 546840*uk_101 + 108360*uk_102 + 592767*uk_103 + 1326087*uk_104 + 262773*uk_105 + 2966607*uk_106 + 587853*uk_107 + 116487*uk_108 + 4913*uk_109 + 805001*uk_11 + 12427*uk_110 + 11560*uk_111 + 28033*uk_112 + 62713*uk_113 + 12427*uk_114 + 31433*uk_115 + 29240*uk_116 + 70907*uk_117 + 158627*uk_118 + 31433*uk_119 + 2036179*uk_12 + 27200*uk_120 + 65960*uk_121 + 147560*uk_122 + 29240*uk_123 + 159953*uk_124 + 357833*uk_125 + 70907*uk_126 + 800513*uk_127 + 158627*uk_128 + 31433*uk_129 + 1894120*uk_13 + 79507*uk_130 + 73960*uk_131 + 179353*uk_132 + 401233*uk_133 + 79507*uk_134 + 68800*uk_135 + 166840*uk_136 + 373240*uk_137 + 73960*uk_138 + 404587*uk_139 + 4593241*uk_14 + 905107*uk_140 + 179353*uk_141 + 2024827*uk_142 + 401233*uk_143 + 79507*uk_144 + 64000*uk_145 + 155200*uk_146 + 347200*uk_147 + 68800*uk_148 + 376360*uk_149 + 10275601*uk_15 + 841960*uk_150 + 166840*uk_151 + 1883560*uk_152 + 373240*uk_153 + 73960*uk_154 + 912673*uk_155 + 2041753*uk_156 + 404587*uk_157 + 4567633*uk_158 + 905107*uk_159 + 2036179*uk_16 + 179353*uk_160 + 10218313*uk_161 + 2024827*uk_162 + 401233*uk_163 + 79507*uk_164 + 3969*uk_17 + 1071*uk_18 + 2709*uk_19 + 63*uk_2 + 2520*uk_20 + 6111*uk_21 + 13671*uk_22 + 2709*uk_23 + 289*uk_24 + 731*uk_25 + 680*uk_26 + 1649*uk_27 + 3689*uk_28 + 731*uk_29 + 17*uk_3 + 1849*uk_30 + 1720*uk_31 + 4171*uk_32 + 9331*uk_33 + 1849*uk_34 + 1600*uk_35 + 3880*uk_36 + 8680*uk_37 + 1720*uk_38 + 9409*uk_39 + 43*uk_4 + 21049*uk_40 + 4171*uk_41 + 47089*uk_42 + 9331*uk_43 + 1849*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 38119212353*uk_47 + 96419184187*uk_48 + 89692264360*uk_49 + 40*uk_5 + 217503741073*uk_50 + 486580534153*uk_51 + 96419184187*uk_52 + 187944057*uk_53 + 50715063*uk_54 + 128279277*uk_55 + 119329560*uk_56 + 289374183*uk_57 + 647362863*uk_58 + 128279277*uk_59 + 97*uk_6 + 13685017*uk_60 + 34615043*uk_61 + 32200040*uk_62 + 78085097*uk_63 + 174685217*uk_64 + 34615043*uk_65 + 87555697*uk_66 + 81447160*uk_67 + 197509363*uk_68 + 441850843*uk_69 + 217*uk_7 + 87555697*uk_70 + 75764800*uk_71 + 183729640*uk_72 + 411024040*uk_73 + 81447160*uk_74 + 445544377*uk_75 + 996733297*uk_76 + 197509363*uk_77 + 2229805417*uk_78 + 441850843*uk_79 + 43*uk_8 + 87555697*uk_80 + 250047*uk_81 + 67473*uk_82 + 170667*uk_83 + 158760*uk_84 + 384993*uk_85 + 861273*uk_86 + 170667*uk_87 + 18207*uk_88 + 46053*uk_89 + 2242306609*uk_9 + 42840*uk_90 + 103887*uk_91 + 232407*uk_92 + 46053*uk_93 + 116487*uk_94 + 108360*uk_95 + 262773*uk_96 + 587853*uk_97 + 116487*uk_98 + 100800*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 249480*uk_100 + 546840*uk_101 + 42840*uk_102 + 617463*uk_103 + 1353429*uk_104 + 106029*uk_105 + 2966607*uk_106 + 232407*uk_107 + 18207*uk_108 + 29791*uk_109 + 1467943*uk_11 + 16337*uk_110 + 38440*uk_111 + 95139*uk_112 + 208537*uk_113 + 16337*uk_114 + 8959*uk_115 + 21080*uk_116 + 52173*uk_117 + 114359*uk_118 + 8959*uk_119 + 805001*uk_12 + 49600*uk_120 + 122760*uk_121 + 269080*uk_122 + 21080*uk_123 + 303831*uk_124 + 665973*uk_125 + 52173*uk_126 + 1459759*uk_127 + 114359*uk_128 + 8959*uk_129 + 1894120*uk_13 + 4913*uk_130 + 11560*uk_131 + 28611*uk_132 + 62713*uk_133 + 4913*uk_134 + 27200*uk_135 + 67320*uk_136 + 147560*uk_137 + 11560*uk_138 + 166617*uk_139 + 4687947*uk_14 + 365211*uk_140 + 28611*uk_141 + 800513*uk_142 + 62713*uk_143 + 4913*uk_144 + 64000*uk_145 + 158400*uk_146 + 347200*uk_147 + 27200*uk_148 + 392040*uk_149 + 10275601*uk_15 + 859320*uk_150 + 67320*uk_151 + 1883560*uk_152 + 147560*uk_153 + 11560*uk_154 + 970299*uk_155 + 2126817*uk_156 + 166617*uk_157 + 4661811*uk_158 + 365211*uk_159 + 805001*uk_16 + 28611*uk_160 + 10218313*uk_161 + 800513*uk_162 + 62713*uk_163 + 4913*uk_164 + 3969*uk_17 + 1953*uk_18 + 1071*uk_19 + 63*uk_2 + 2520*uk_20 + 6237*uk_21 + 13671*uk_22 + 1071*uk_23 + 961*uk_24 + 527*uk_25 + 1240*uk_26 + 3069*uk_27 + 6727*uk_28 + 527*uk_29 + 31*uk_3 + 289*uk_30 + 680*uk_31 + 1683*uk_32 + 3689*uk_33 + 289*uk_34 + 1600*uk_35 + 3960*uk_36 + 8680*uk_37 + 680*uk_38 + 9801*uk_39 + 17*uk_4 + 21483*uk_40 + 1683*uk_41 + 47089*uk_42 + 3689*uk_43 + 289*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 69511504879*uk_47 + 38119212353*uk_48 + 89692264360*uk_49 + 40*uk_5 + 221988354291*uk_50 + 486580534153*uk_51 + 38119212353*uk_52 + 187944057*uk_53 + 92480409*uk_54 + 50715063*uk_55 + 119329560*uk_56 + 295340661*uk_57 + 647362863*uk_58 + 50715063*uk_59 + 99*uk_6 + 45506233*uk_60 + 24955031*uk_61 + 58717720*uk_62 + 145326357*uk_63 + 318543631*uk_64 + 24955031*uk_65 + 13685017*uk_66 + 32200040*uk_67 + 79695099*uk_68 + 174685217*uk_69 + 217*uk_7 + 13685017*uk_70 + 75764800*uk_71 + 187517880*uk_72 + 411024040*uk_73 + 32200040*uk_74 + 464106753*uk_75 + 1017284499*uk_76 + 79695099*uk_77 + 2229805417*uk_78 + 174685217*uk_79 + 17*uk_8 + 13685017*uk_80 + 250047*uk_81 + 123039*uk_82 + 67473*uk_83 + 158760*uk_84 + 392931*uk_85 + 861273*uk_86 + 67473*uk_87 + 60543*uk_88 + 33201*uk_89 + 2242306609*uk_9 + 78120*uk_90 + 193347*uk_91 + 423801*uk_92 + 33201*uk_93 + 18207*uk_94 + 42840*uk_95 + 106029*uk_96 + 232407*uk_97 + 18207*uk_98 + 100800*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 254520*uk_100 + 546840*uk_101 + 78120*uk_102 + 642663*uk_103 + 1380771*uk_104 + 197253*uk_105 + 2966607*uk_106 + 423801*uk_107 + 60543*uk_108 + 614125*uk_109 + 4025005*uk_11 + 223975*uk_110 + 289000*uk_111 + 729725*uk_112 + 1567825*uk_113 + 223975*uk_114 + 81685*uk_115 + 105400*uk_116 + 266135*uk_117 + 571795*uk_118 + 81685*uk_119 + 1467943*uk_12 + 136000*uk_120 + 343400*uk_121 + 737800*uk_122 + 105400*uk_123 + 867085*uk_124 + 1862945*uk_125 + 266135*uk_126 + 4002565*uk_127 + 571795*uk_128 + 81685*uk_129 + 1894120*uk_13 + 29791*uk_130 + 38440*uk_131 + 97061*uk_132 + 208537*uk_133 + 29791*uk_134 + 49600*uk_135 + 125240*uk_136 + 269080*uk_137 + 38440*uk_138 + 316231*uk_139 + 4782653*uk_14 + 679427*uk_140 + 97061*uk_141 + 1459759*uk_142 + 208537*uk_143 + 29791*uk_144 + 64000*uk_145 + 161600*uk_146 + 347200*uk_147 + 49600*uk_148 + 408040*uk_149 + 10275601*uk_15 + 876680*uk_150 + 125240*uk_151 + 1883560*uk_152 + 269080*uk_153 + 38440*uk_154 + 1030301*uk_155 + 2213617*uk_156 + 316231*uk_157 + 4755989*uk_158 + 679427*uk_159 + 1467943*uk_16 + 97061*uk_160 + 10218313*uk_161 + 1459759*uk_162 + 208537*uk_163 + 29791*uk_164 + 3969*uk_17 + 5355*uk_18 + 1953*uk_19 + 63*uk_2 + 2520*uk_20 + 6363*uk_21 + 13671*uk_22 + 1953*uk_23 + 7225*uk_24 + 2635*uk_25 + 3400*uk_26 + 8585*uk_27 + 18445*uk_28 + 2635*uk_29 + 85*uk_3 + 961*uk_30 + 1240*uk_31 + 3131*uk_32 + 6727*uk_33 + 961*uk_34 + 1600*uk_35 + 4040*uk_36 + 8680*uk_37 + 1240*uk_38 + 10201*uk_39 + 31*uk_4 + 21917*uk_40 + 3131*uk_41 + 47089*uk_42 + 6727*uk_43 + 961*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 190596061765*uk_47 + 69511504879*uk_48 + 89692264360*uk_49 + 40*uk_5 + 226472967509*uk_50 + 486580534153*uk_51 + 69511504879*uk_52 + 187944057*uk_53 + 253575315*uk_54 + 92480409*uk_55 + 119329560*uk_56 + 301307139*uk_57 + 647362863*uk_58 + 92480409*uk_59 + 101*uk_6 + 342125425*uk_60 + 124775155*uk_61 + 161000200*uk_62 + 406525505*uk_63 + 873426085*uk_64 + 124775155*uk_65 + 45506233*uk_66 + 58717720*uk_67 + 148262243*uk_68 + 318543631*uk_69 + 217*uk_7 + 45506233*uk_70 + 75764800*uk_71 + 191306120*uk_72 + 411024040*uk_73 + 58717720*uk_74 + 483047953*uk_75 + 1037835701*uk_76 + 148262243*uk_77 + 2229805417*uk_78 + 318543631*uk_79 + 31*uk_8 + 45506233*uk_80 + 250047*uk_81 + 337365*uk_82 + 123039*uk_83 + 158760*uk_84 + 400869*uk_85 + 861273*uk_86 + 123039*uk_87 + 455175*uk_88 + 166005*uk_89 + 2242306609*uk_9 + 214200*uk_90 + 540855*uk_91 + 1162035*uk_92 + 166005*uk_93 + 60543*uk_94 + 78120*uk_95 + 197253*uk_96 + 423801*uk_97 + 60543*uk_98 + 100800*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 233604*uk_100 + 492156*uk_101 + 192780*uk_102 + 668367*uk_103 + 1408113*uk_104 + 551565*uk_105 + 2966607*uk_106 + 1162035*uk_107 + 455175*uk_108 + 438976*uk_109 + 3598828*uk_11 + 490960*uk_110 + 207936*uk_111 + 594928*uk_112 + 1253392*uk_113 + 490960*uk_114 + 549100*uk_115 + 232560*uk_116 + 665380*uk_117 + 1401820*uk_118 + 549100*uk_119 + 4025005*uk_12 + 98496*uk_120 + 281808*uk_121 + 593712*uk_122 + 232560*uk_123 + 806284*uk_124 + 1698676*uk_125 + 665380*uk_126 + 3578764*uk_127 + 1401820*uk_128 + 549100*uk_129 + 1704708*uk_13 + 614125*uk_130 + 260100*uk_131 + 744175*uk_132 + 1567825*uk_133 + 614125*uk_134 + 110160*uk_135 + 315180*uk_136 + 664020*uk_137 + 260100*uk_138 + 901765*uk_139 + 4877359*uk_14 + 1899835*uk_140 + 744175*uk_141 + 4002565*uk_142 + 1567825*uk_143 + 614125*uk_144 + 46656*uk_145 + 133488*uk_146 + 281232*uk_147 + 110160*uk_148 + 381924*uk_149 + 10275601*uk_15 + 804636*uk_150 + 315180*uk_151 + 1695204*uk_152 + 664020*uk_153 + 260100*uk_154 + 1092727*uk_155 + 2302153*uk_156 + 901765*uk_157 + 4850167*uk_158 + 1899835*uk_159 + 4025005*uk_16 + 744175*uk_160 + 10218313*uk_161 + 4002565*uk_162 + 1567825*uk_163 + 614125*uk_164 + 3969*uk_17 + 4788*uk_18 + 5355*uk_19 + 63*uk_2 + 2268*uk_20 + 6489*uk_21 + 13671*uk_22 + 5355*uk_23 + 5776*uk_24 + 6460*uk_25 + 2736*uk_26 + 7828*uk_27 + 16492*uk_28 + 6460*uk_29 + 76*uk_3 + 7225*uk_30 + 3060*uk_31 + 8755*uk_32 + 18445*uk_33 + 7225*uk_34 + 1296*uk_35 + 3708*uk_36 + 7812*uk_37 + 3060*uk_38 + 10609*uk_39 + 85*uk_4 + 22351*uk_40 + 8755*uk_41 + 47089*uk_42 + 18445*uk_43 + 7225*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 170415302284*uk_47 + 190596061765*uk_48 + 80723037924*uk_49 + 36*uk_5 + 230957580727*uk_50 + 486580534153*uk_51 + 190596061765*uk_52 + 187944057*uk_53 + 226726164*uk_54 + 253575315*uk_55 + 107396604*uk_56 + 307273617*uk_57 + 647362863*uk_58 + 253575315*uk_59 + 103*uk_6 + 273510928*uk_60 + 305900380*uk_61 + 129557808*uk_62 + 370679284*uk_63 + 780945676*uk_64 + 305900380*uk_65 + 342125425*uk_66 + 144900180*uk_67 + 414575515*uk_68 + 873426085*uk_69 + 217*uk_7 + 342125425*uk_70 + 61369488*uk_71 + 175584924*uk_72 + 369921636*uk_73 + 144900180*uk_74 + 502367977*uk_75 + 1058386903*uk_76 + 414575515*uk_77 + 2229805417*uk_78 + 873426085*uk_79 + 85*uk_8 + 342125425*uk_80 + 250047*uk_81 + 301644*uk_82 + 337365*uk_83 + 142884*uk_84 + 408807*uk_85 + 861273*uk_86 + 337365*uk_87 + 363888*uk_88 + 406980*uk_89 + 2242306609*uk_9 + 172368*uk_90 + 493164*uk_91 + 1038996*uk_92 + 406980*uk_93 + 455175*uk_94 + 192780*uk_95 + 551565*uk_96 + 1162035*uk_97 + 455175*uk_98 + 81648*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 238140*uk_100 + 492156*uk_101 + 172368*uk_102 + 694575*uk_103 + 1435455*uk_104 + 502740*uk_105 + 2966607*uk_106 + 1038996*uk_107 + 363888*uk_108 + 1092727*uk_109 + 4877359*uk_11 + 806284*uk_110 + 381924*uk_111 + 1113945*uk_112 + 2302153*uk_113 + 806284*uk_114 + 594928*uk_115 + 281808*uk_116 + 821940*uk_117 + 1698676*uk_118 + 594928*uk_119 + 3598828*uk_12 + 133488*uk_120 + 389340*uk_121 + 804636*uk_122 + 281808*uk_123 + 1135575*uk_124 + 2346855*uk_125 + 821940*uk_126 + 4850167*uk_127 + 1698676*uk_128 + 594928*uk_129 + 1704708*uk_13 + 438976*uk_130 + 207936*uk_131 + 606480*uk_132 + 1253392*uk_133 + 438976*uk_134 + 98496*uk_135 + 287280*uk_136 + 593712*uk_137 + 207936*uk_138 + 837900*uk_139 + 4972065*uk_14 + 1731660*uk_140 + 606480*uk_141 + 3578764*uk_142 + 1253392*uk_143 + 438976*uk_144 + 46656*uk_145 + 136080*uk_146 + 281232*uk_147 + 98496*uk_148 + 396900*uk_149 + 10275601*uk_15 + 820260*uk_150 + 287280*uk_151 + 1695204*uk_152 + 593712*uk_153 + 207936*uk_154 + 1157625*uk_155 + 2392425*uk_156 + 837900*uk_157 + 4944345*uk_158 + 1731660*uk_159 + 3598828*uk_16 + 606480*uk_160 + 10218313*uk_161 + 3578764*uk_162 + 1253392*uk_163 + 438976*uk_164 + 3969*uk_17 + 6489*uk_18 + 4788*uk_19 + 63*uk_2 + 2268*uk_20 + 6615*uk_21 + 13671*uk_22 + 4788*uk_23 + 10609*uk_24 + 7828*uk_25 + 3708*uk_26 + 10815*uk_27 + 22351*uk_28 + 7828*uk_29 + 103*uk_3 + 5776*uk_30 + 2736*uk_31 + 7980*uk_32 + 16492*uk_33 + 5776*uk_34 + 1296*uk_35 + 3780*uk_36 + 7812*uk_37 + 2736*uk_38 + 11025*uk_39 + 76*uk_4 + 22785*uk_40 + 7980*uk_41 + 47089*uk_42 + 16492*uk_43 + 5776*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 230957580727*uk_47 + 170415302284*uk_48 + 80723037924*uk_49 + 36*uk_5 + 235442193945*uk_50 + 486580534153*uk_51 + 170415302284*uk_52 + 187944057*uk_53 + 307273617*uk_54 + 226726164*uk_55 + 107396604*uk_56 + 313240095*uk_57 + 647362863*uk_58 + 226726164*uk_59 + 105*uk_6 + 502367977*uk_60 + 370679284*uk_61 + 175584924*uk_62 + 512122695*uk_63 + 1058386903*uk_64 + 370679284*uk_65 + 273510928*uk_66 + 129557808*uk_67 + 377876940*uk_68 + 780945676*uk_69 + 217*uk_7 + 273510928*uk_70 + 61369488*uk_71 + 178994340*uk_72 + 369921636*uk_73 + 129557808*uk_74 + 522066825*uk_75 + 1078938105*uk_76 + 377876940*uk_77 + 2229805417*uk_78 + 780945676*uk_79 + 76*uk_8 + 273510928*uk_80 + 250047*uk_81 + 408807*uk_82 + 301644*uk_83 + 142884*uk_84 + 416745*uk_85 + 861273*uk_86 + 301644*uk_87 + 668367*uk_88 + 493164*uk_89 + 2242306609*uk_9 + 233604*uk_90 + 681345*uk_91 + 1408113*uk_92 + 493164*uk_93 + 363888*uk_94 + 172368*uk_95 + 502740*uk_96 + 1038996*uk_97 + 363888*uk_98 + 81648*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 215712*uk_100 + 437472*uk_101 + 207648*uk_102 + 721287*uk_103 + 1462797*uk_104 + 694323*uk_105 + 2966607*uk_106 + 1408113*uk_107 + 668367*uk_108 + 205379*uk_109 + 2793827*uk_11 + 358543*uk_110 + 111392*uk_111 + 372467*uk_112 + 755377*uk_113 + 358543*uk_114 + 625931*uk_115 + 194464*uk_116 + 650239*uk_117 + 1318709*uk_118 + 625931*uk_119 + 4877359*uk_12 + 60416*uk_120 + 202016*uk_121 + 409696*uk_122 + 194464*uk_123 + 675491*uk_124 + 1369921*uk_125 + 650239*uk_126 + 2778251*uk_127 + 1318709*uk_128 + 625931*uk_129 + 1515296*uk_13 + 1092727*uk_130 + 339488*uk_131 + 1135163*uk_132 + 2302153*uk_133 + 1092727*uk_134 + 105472*uk_135 + 352672*uk_136 + 715232*uk_137 + 339488*uk_138 + 1179247*uk_139 + 5066771*uk_14 + 2391557*uk_140 + 1135163*uk_141 + 4850167*uk_142 + 2302153*uk_143 + 1092727*uk_144 + 32768*uk_145 + 109568*uk_146 + 222208*uk_147 + 105472*uk_148 + 366368*uk_149 + 10275601*uk_15 + 743008*uk_150 + 352672*uk_151 + 1506848*uk_152 + 715232*uk_153 + 339488*uk_154 + 1225043*uk_155 + 2484433*uk_156 + 1179247*uk_157 + 5038523*uk_158 + 2391557*uk_159 + 4877359*uk_16 + 1135163*uk_160 + 10218313*uk_161 + 4850167*uk_162 + 2302153*uk_163 + 1092727*uk_164 + 3969*uk_17 + 3717*uk_18 + 6489*uk_19 + 63*uk_2 + 2016*uk_20 + 6741*uk_21 + 13671*uk_22 + 6489*uk_23 + 3481*uk_24 + 6077*uk_25 + 1888*uk_26 + 6313*uk_27 + 12803*uk_28 + 6077*uk_29 + 59*uk_3 + 10609*uk_30 + 3296*uk_31 + 11021*uk_32 + 22351*uk_33 + 10609*uk_34 + 1024*uk_35 + 3424*uk_36 + 6944*uk_37 + 3296*uk_38 + 11449*uk_39 + 103*uk_4 + 23219*uk_40 + 11021*uk_41 + 47089*uk_42 + 22351*uk_43 + 10609*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 132296089931*uk_47 + 230957580727*uk_48 + 71753811488*uk_49 + 32*uk_5 + 239926807163*uk_50 + 486580534153*uk_51 + 230957580727*uk_52 + 187944057*uk_53 + 176011101*uk_54 + 307273617*uk_55 + 95463648*uk_56 + 319206573*uk_57 + 647362863*uk_58 + 307273617*uk_59 + 107*uk_6 + 164835793*uk_60 + 287764181*uk_61 + 89402464*uk_62 + 298939489*uk_63 + 606260459*uk_64 + 287764181*uk_65 + 502367977*uk_66 + 156075488*uk_67 + 521877413*uk_68 + 1058386903*uk_69 + 217*uk_7 + 502367977*uk_70 + 48489472*uk_71 + 162136672*uk_72 + 328819232*uk_73 + 156075488*uk_74 + 542144497*uk_75 + 1099489307*uk_76 + 521877413*uk_77 + 2229805417*uk_78 + 1058386903*uk_79 + 103*uk_8 + 502367977*uk_80 + 250047*uk_81 + 234171*uk_82 + 408807*uk_83 + 127008*uk_84 + 424683*uk_85 + 861273*uk_86 + 408807*uk_87 + 219303*uk_88 + 382851*uk_89 + 2242306609*uk_9 + 118944*uk_90 + 397719*uk_91 + 806589*uk_92 + 382851*uk_93 + 668367*uk_94 + 207648*uk_95 + 694323*uk_96 + 1408113*uk_97 + 668367*uk_98 + 64512*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 219744*uk_100 + 437472*uk_101 + 118944*uk_102 + 748503*uk_103 + 1490139*uk_104 + 405153*uk_105 + 2966607*uk_106 + 806589*uk_107 + 219303*uk_108 + 103823*uk_109 + 2225591*uk_11 + 130331*uk_110 + 70688*uk_111 + 240781*uk_112 + 479353*uk_113 + 130331*uk_114 + 163607*uk_115 + 88736*uk_116 + 302257*uk_117 + 601741*uk_118 + 163607*uk_119 + 2793827*uk_12 + 48128*uk_120 + 163936*uk_121 + 326368*uk_122 + 88736*uk_123 + 558407*uk_124 + 1111691*uk_125 + 302257*uk_126 + 2213183*uk_127 + 601741*uk_128 + 163607*uk_129 + 1515296*uk_13 + 205379*uk_130 + 111392*uk_131 + 379429*uk_132 + 755377*uk_133 + 205379*uk_134 + 60416*uk_135 + 205792*uk_136 + 409696*uk_137 + 111392*uk_138 + 700979*uk_139 + 5161477*uk_14 + 1395527*uk_140 + 379429*uk_141 + 2778251*uk_142 + 755377*uk_143 + 205379*uk_144 + 32768*uk_145 + 111616*uk_146 + 222208*uk_147 + 60416*uk_148 + 380192*uk_149 + 10275601*uk_15 + 756896*uk_150 + 205792*uk_151 + 1506848*uk_152 + 409696*uk_153 + 111392*uk_154 + 1295029*uk_155 + 2578177*uk_156 + 700979*uk_157 + 5132701*uk_158 + 1395527*uk_159 + 2793827*uk_16 + 379429*uk_160 + 10218313*uk_161 + 2778251*uk_162 + 755377*uk_163 + 205379*uk_164 + 3969*uk_17 + 2961*uk_18 + 3717*uk_19 + 63*uk_2 + 2016*uk_20 + 6867*uk_21 + 13671*uk_22 + 3717*uk_23 + 2209*uk_24 + 2773*uk_25 + 1504*uk_26 + 5123*uk_27 + 10199*uk_28 + 2773*uk_29 + 47*uk_3 + 3481*uk_30 + 1888*uk_31 + 6431*uk_32 + 12803*uk_33 + 3481*uk_34 + 1024*uk_35 + 3488*uk_36 + 6944*uk_37 + 1888*uk_38 + 11881*uk_39 + 59*uk_4 + 23653*uk_40 + 6431*uk_41 + 47089*uk_42 + 12803*uk_43 + 3481*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 105388410623*uk_47 + 132296089931*uk_48 + 71753811488*uk_49 + 32*uk_5 + 244411420381*uk_50 + 486580534153*uk_51 + 132296089931*uk_52 + 187944057*uk_53 + 140212233*uk_54 + 176011101*uk_55 + 95463648*uk_56 + 325173051*uk_57 + 647362863*uk_58 + 176011101*uk_59 + 109*uk_6 + 104602777*uk_60 + 131309869*uk_61 + 71218912*uk_62 + 242589419*uk_63 + 482953247*uk_64 + 131309869*uk_65 + 164835793*uk_66 + 89402464*uk_67 + 304527143*uk_68 + 606260459*uk_69 + 217*uk_7 + 164835793*uk_70 + 48489472*uk_71 + 165167264*uk_72 + 328819232*uk_73 + 89402464*uk_74 + 562600993*uk_75 + 1120040509*uk_76 + 304527143*uk_77 + 2229805417*uk_78 + 606260459*uk_79 + 59*uk_8 + 164835793*uk_80 + 250047*uk_81 + 186543*uk_82 + 234171*uk_83 + 127008*uk_84 + 432621*uk_85 + 861273*uk_86 + 234171*uk_87 + 139167*uk_88 + 174699*uk_89 + 2242306609*uk_9 + 94752*uk_90 + 322749*uk_91 + 642537*uk_92 + 174699*uk_93 + 219303*uk_94 + 118944*uk_95 + 405153*uk_96 + 806589*uk_97 + 219303*uk_98 + 64512*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 223776*uk_100 + 437472*uk_101 + 94752*uk_102 + 776223*uk_103 + 1517481*uk_104 + 328671*uk_105 + 2966607*uk_106 + 642537*uk_107 + 139167*uk_108 + 300763*uk_109 + 3172651*uk_11 + 210983*uk_110 + 143648*uk_111 + 498279*uk_112 + 974113*uk_113 + 210983*uk_114 + 148003*uk_115 + 100768*uk_116 + 349539*uk_117 + 683333*uk_118 + 148003*uk_119 + 2225591*uk_12 + 68608*uk_120 + 237984*uk_121 + 465248*uk_122 + 100768*uk_123 + 825507*uk_124 + 1613829*uk_125 + 349539*uk_126 + 3154963*uk_127 + 683333*uk_128 + 148003*uk_129 + 1515296*uk_13 + 103823*uk_130 + 70688*uk_131 + 245199*uk_132 + 479353*uk_133 + 103823*uk_134 + 48128*uk_135 + 166944*uk_136 + 326368*uk_137 + 70688*uk_138 + 579087*uk_139 + 5256183*uk_14 + 1132089*uk_140 + 245199*uk_141 + 2213183*uk_142 + 479353*uk_143 + 103823*uk_144 + 32768*uk_145 + 113664*uk_146 + 222208*uk_147 + 48128*uk_148 + 394272*uk_149 + 10275601*uk_15 + 770784*uk_150 + 166944*uk_151 + 1506848*uk_152 + 326368*uk_153 + 70688*uk_154 + 1367631*uk_155 + 2673657*uk_156 + 579087*uk_157 + 5226879*uk_158 + 1132089*uk_159 + 2225591*uk_16 + 245199*uk_160 + 10218313*uk_161 + 2213183*uk_162 + 479353*uk_163 + 103823*uk_164 + 3969*uk_17 + 4221*uk_18 + 2961*uk_19 + 63*uk_2 + 2016*uk_20 + 6993*uk_21 + 13671*uk_22 + 2961*uk_23 + 4489*uk_24 + 3149*uk_25 + 2144*uk_26 + 7437*uk_27 + 14539*uk_28 + 3149*uk_29 + 67*uk_3 + 2209*uk_30 + 1504*uk_31 + 5217*uk_32 + 10199*uk_33 + 2209*uk_34 + 1024*uk_35 + 3552*uk_36 + 6944*uk_37 + 1504*uk_38 + 12321*uk_39 + 47*uk_4 + 24087*uk_40 + 5217*uk_41 + 47089*uk_42 + 10199*uk_43 + 2209*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 150234542803*uk_47 + 105388410623*uk_48 + 71753811488*uk_49 + 32*uk_5 + 248896033599*uk_50 + 486580534153*uk_51 + 105388410623*uk_52 + 187944057*uk_53 + 199877013*uk_54 + 140212233*uk_55 + 95463648*uk_56 + 331139529*uk_57 + 647362863*uk_58 + 140212233*uk_59 + 111*uk_6 + 212567617*uk_60 + 149114597*uk_61 + 101524832*uk_62 + 352164261*uk_63 + 688465267*uk_64 + 149114597*uk_65 + 104602777*uk_66 + 71218912*uk_67 + 247040601*uk_68 + 482953247*uk_69 + 217*uk_7 + 104602777*uk_70 + 48489472*uk_71 + 168197856*uk_72 + 328819232*uk_73 + 71218912*uk_74 + 583436313*uk_75 + 1140591711*uk_76 + 247040601*uk_77 + 2229805417*uk_78 + 482953247*uk_79 + 47*uk_8 + 104602777*uk_80 + 250047*uk_81 + 265923*uk_82 + 186543*uk_83 + 127008*uk_84 + 440559*uk_85 + 861273*uk_86 + 186543*uk_87 + 282807*uk_88 + 198387*uk_89 + 2242306609*uk_9 + 135072*uk_90 + 468531*uk_91 + 915957*uk_92 + 198387*uk_93 + 139167*uk_94 + 94752*uk_95 + 328671*uk_96 + 642537*uk_97 + 139167*uk_98 + 64512*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 199332*uk_100 + 382788*uk_101 + 118188*uk_102 + 804447*uk_103 + 1544823*uk_104 + 476973*uk_105 + 2966607*uk_106 + 915957*uk_107 + 282807*uk_108 + 216*uk_109 + 284118*uk_11 + 2412*uk_110 + 1008*uk_111 + 4068*uk_112 + 7812*uk_113 + 2412*uk_114 + 26934*uk_115 + 11256*uk_116 + 45426*uk_117 + 87234*uk_118 + 26934*uk_119 + 3172651*uk_12 + 4704*uk_120 + 18984*uk_121 + 36456*uk_122 + 11256*uk_123 + 76614*uk_124 + 147126*uk_125 + 45426*uk_126 + 282534*uk_127 + 87234*uk_128 + 26934*uk_129 + 1325884*uk_13 + 300763*uk_130 + 125692*uk_131 + 507257*uk_132 + 974113*uk_133 + 300763*uk_134 + 52528*uk_135 + 211988*uk_136 + 407092*uk_137 + 125692*uk_138 + 855523*uk_139 + 5350889*uk_14 + 1642907*uk_140 + 507257*uk_141 + 3154963*uk_142 + 974113*uk_143 + 300763*uk_144 + 21952*uk_145 + 88592*uk_146 + 170128*uk_147 + 52528*uk_148 + 357532*uk_149 + 10275601*uk_15 + 686588*uk_150 + 211988*uk_151 + 1318492*uk_152 + 407092*uk_153 + 125692*uk_154 + 1442897*uk_155 + 2770873*uk_156 + 855523*uk_157 + 5321057*uk_158 + 1642907*uk_159 + 3172651*uk_16 + 507257*uk_160 + 10218313*uk_161 + 3154963*uk_162 + 974113*uk_163 + 300763*uk_164 + 3969*uk_17 + 378*uk_18 + 4221*uk_19 + 63*uk_2 + 1764*uk_20 + 7119*uk_21 + 13671*uk_22 + 4221*uk_23 + 36*uk_24 + 402*uk_25 + 168*uk_26 + 678*uk_27 + 1302*uk_28 + 402*uk_29 + 6*uk_3 + 4489*uk_30 + 1876*uk_31 + 7571*uk_32 + 14539*uk_33 + 4489*uk_34 + 784*uk_35 + 3164*uk_36 + 6076*uk_37 + 1876*uk_38 + 12769*uk_39 + 67*uk_4 + 24521*uk_40 + 7571*uk_41 + 47089*uk_42 + 14539*uk_43 + 4489*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 13453839654*uk_47 + 150234542803*uk_48 + 62784585052*uk_49 + 28*uk_5 + 253380646817*uk_50 + 486580534153*uk_51 + 150234542803*uk_52 + 187944057*uk_53 + 17899434*uk_54 + 199877013*uk_55 + 83530692*uk_56 + 337106007*uk_57 + 647362863*uk_58 + 199877013*uk_59 + 113*uk_6 + 1704708*uk_60 + 19035906*uk_61 + 7955304*uk_62 + 32105334*uk_63 + 61653606*uk_64 + 19035906*uk_65 + 212567617*uk_66 + 88834228*uk_67 + 358509563*uk_68 + 688465267*uk_69 + 217*uk_7 + 212567617*uk_70 + 37124752*uk_71 + 149824892*uk_72 + 287716828*uk_73 + 88834228*uk_74 + 604650457*uk_75 + 1161142913*uk_76 + 358509563*uk_77 + 2229805417*uk_78 + 688465267*uk_79 + 67*uk_8 + 212567617*uk_80 + 250047*uk_81 + 23814*uk_82 + 265923*uk_83 + 111132*uk_84 + 448497*uk_85 + 861273*uk_86 + 265923*uk_87 + 2268*uk_88 + 25326*uk_89 + 2242306609*uk_9 + 10584*uk_90 + 42714*uk_91 + 82026*uk_92 + 25326*uk_93 + 282807*uk_94 + 118188*uk_95 + 476973*uk_96 + 915957*uk_97 + 282807*uk_98 + 49392*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 231840*uk_100 + 437472*uk_101 + 12096*uk_102 + 833175*uk_103 + 1572165*uk_104 + 43470*uk_105 + 2966607*uk_106 + 82026*uk_107 + 2268*uk_108 + 681472*uk_109 + 4167064*uk_11 + 46464*uk_110 + 247808*uk_111 + 890560*uk_112 + 1680448*uk_113 + 46464*uk_114 + 3168*uk_115 + 16896*uk_116 + 60720*uk_117 + 114576*uk_118 + 3168*uk_119 + 284118*uk_12 + 90112*uk_120 + 323840*uk_121 + 611072*uk_122 + 16896*uk_123 + 1163800*uk_124 + 2196040*uk_125 + 60720*uk_126 + 4143832*uk_127 + 114576*uk_128 + 3168*uk_129 + 1515296*uk_13 + 216*uk_130 + 1152*uk_131 + 4140*uk_132 + 7812*uk_133 + 216*uk_134 + 6144*uk_135 + 22080*uk_136 + 41664*uk_137 + 1152*uk_138 + 79350*uk_139 + 5445595*uk_14 + 149730*uk_140 + 4140*uk_141 + 282534*uk_142 + 7812*uk_143 + 216*uk_144 + 32768*uk_145 + 117760*uk_146 + 222208*uk_147 + 6144*uk_148 + 423200*uk_149 + 10275601*uk_15 + 798560*uk_150 + 22080*uk_151 + 1506848*uk_152 + 41664*uk_153 + 1152*uk_154 + 1520875*uk_155 + 2869825*uk_156 + 79350*uk_157 + 5415235*uk_158 + 149730*uk_159 + 284118*uk_16 + 4140*uk_160 + 10218313*uk_161 + 282534*uk_162 + 7812*uk_163 + 216*uk_164 + 3969*uk_17 + 5544*uk_18 + 378*uk_19 + 63*uk_2 + 2016*uk_20 + 7245*uk_21 + 13671*uk_22 + 378*uk_23 + 7744*uk_24 + 528*uk_25 + 2816*uk_26 + 10120*uk_27 + 19096*uk_28 + 528*uk_29 + 88*uk_3 + 36*uk_30 + 192*uk_31 + 690*uk_32 + 1302*uk_33 + 36*uk_34 + 1024*uk_35 + 3680*uk_36 + 6944*uk_37 + 192*uk_38 + 13225*uk_39 + 6*uk_4 + 24955*uk_40 + 690*uk_41 + 47089*uk_42 + 1302*uk_43 + 36*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 197322981592*uk_47 + 13453839654*uk_48 + 71753811488*uk_49 + 32*uk_5 + 257865260035*uk_50 + 486580534153*uk_51 + 13453839654*uk_52 + 187944057*uk_53 + 262525032*uk_54 + 17899434*uk_55 + 95463648*uk_56 + 343072485*uk_57 + 647362863*uk_58 + 17899434*uk_59 + 115*uk_6 + 366701632*uk_60 + 25002384*uk_61 + 133346048*uk_62 + 479212360*uk_63 + 904252888*uk_64 + 25002384*uk_65 + 1704708*uk_66 + 9091776*uk_67 + 32673570*uk_68 + 61653606*uk_69 + 217*uk_7 + 1704708*uk_70 + 48489472*uk_71 + 174259040*uk_72 + 328819232*uk_73 + 9091776*uk_74 + 626243425*uk_75 + 1181694115*uk_76 + 32673570*uk_77 + 2229805417*uk_78 + 61653606*uk_79 + 6*uk_8 + 1704708*uk_80 + 250047*uk_81 + 349272*uk_82 + 23814*uk_83 + 127008*uk_84 + 456435*uk_85 + 861273*uk_86 + 23814*uk_87 + 487872*uk_88 + 33264*uk_89 + 2242306609*uk_9 + 177408*uk_90 + 637560*uk_91 + 1203048*uk_92 + 33264*uk_93 + 2268*uk_94 + 12096*uk_95 + 43470*uk_96 + 82026*uk_97 + 2268*uk_98 + 64512*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 206388*uk_100 + 382788*uk_101 + 155232*uk_102 + 862407*uk_103 + 1599507*uk_104 + 648648*uk_105 + 2966607*uk_106 + 1203048*uk_107 + 487872*uk_108 + 614125*uk_109 + 4025005*uk_11 + 635800*uk_110 + 202300*uk_111 + 845325*uk_112 + 1567825*uk_113 + 635800*uk_114 + 658240*uk_115 + 209440*uk_116 + 875160*uk_117 + 1623160*uk_118 + 658240*uk_119 + 4167064*uk_12 + 66640*uk_120 + 278460*uk_121 + 516460*uk_122 + 209440*uk_123 + 1163565*uk_124 + 2158065*uk_125 + 875160*uk_126 + 4002565*uk_127 + 1623160*uk_128 + 658240*uk_129 + 1325884*uk_13 + 681472*uk_130 + 216832*uk_131 + 906048*uk_132 + 1680448*uk_133 + 681472*uk_134 + 68992*uk_135 + 288288*uk_136 + 534688*uk_137 + 216832*uk_138 + 1204632*uk_139 + 5540301*uk_14 + 2234232*uk_140 + 906048*uk_141 + 4143832*uk_142 + 1680448*uk_143 + 681472*uk_144 + 21952*uk_145 + 91728*uk_146 + 170128*uk_147 + 68992*uk_148 + 383292*uk_149 + 10275601*uk_15 + 710892*uk_150 + 288288*uk_151 + 1318492*uk_152 + 534688*uk_153 + 216832*uk_154 + 1601613*uk_155 + 2970513*uk_156 + 1204632*uk_157 + 5509413*uk_158 + 2234232*uk_159 + 4167064*uk_16 + 906048*uk_160 + 10218313*uk_161 + 4143832*uk_162 + 1680448*uk_163 + 681472*uk_164 + 3969*uk_17 + 5355*uk_18 + 5544*uk_19 + 63*uk_2 + 1764*uk_20 + 7371*uk_21 + 13671*uk_22 + 5544*uk_23 + 7225*uk_24 + 7480*uk_25 + 2380*uk_26 + 9945*uk_27 + 18445*uk_28 + 7480*uk_29 + 85*uk_3 + 7744*uk_30 + 2464*uk_31 + 10296*uk_32 + 19096*uk_33 + 7744*uk_34 + 784*uk_35 + 3276*uk_36 + 6076*uk_37 + 2464*uk_38 + 13689*uk_39 + 88*uk_4 + 25389*uk_40 + 10296*uk_41 + 47089*uk_42 + 19096*uk_43 + 7744*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 190596061765*uk_47 + 197322981592*uk_48 + 62784585052*uk_49 + 28*uk_5 + 262349873253*uk_50 + 486580534153*uk_51 + 197322981592*uk_52 + 187944057*uk_53 + 253575315*uk_54 + 262525032*uk_55 + 83530692*uk_56 + 349038963*uk_57 + 647362863*uk_58 + 262525032*uk_59 + 117*uk_6 + 342125425*uk_60 + 354200440*uk_61 + 112700140*uk_62 + 470925585*uk_63 + 873426085*uk_64 + 354200440*uk_65 + 366701632*uk_66 + 116677792*uk_67 + 487546488*uk_68 + 904252888*uk_69 + 217*uk_7 + 366701632*uk_70 + 37124752*uk_71 + 155128428*uk_72 + 287716828*uk_73 + 116677792*uk_74 + 648215217*uk_75 + 1202245317*uk_76 + 487546488*uk_77 + 2229805417*uk_78 + 904252888*uk_79 + 88*uk_8 + 366701632*uk_80 + 250047*uk_81 + 337365*uk_82 + 349272*uk_83 + 111132*uk_84 + 464373*uk_85 + 861273*uk_86 + 349272*uk_87 + 455175*uk_88 + 471240*uk_89 + 2242306609*uk_9 + 149940*uk_90 + 626535*uk_91 + 1162035*uk_92 + 471240*uk_93 + 487872*uk_94 + 155232*uk_95 + 648648*uk_96 + 1203048*uk_97 + 487872*uk_98 + 49392*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 209916*uk_100 + 382788*uk_101 + 149940*uk_102 + 892143*uk_103 + 1626849*uk_104 + 637245*uk_105 + 2966607*uk_106 + 1162035*uk_107 + 455175*uk_108 + 1331000*uk_109 + 5208830*uk_11 + 1028500*uk_110 + 338800*uk_111 + 1439900*uk_112 + 2625700*uk_113 + 1028500*uk_114 + 794750*uk_115 + 261800*uk_116 + 1112650*uk_117 + 2028950*uk_118 + 794750*uk_119 + 4025005*uk_12 + 86240*uk_120 + 366520*uk_121 + 668360*uk_122 + 261800*uk_123 + 1557710*uk_124 + 2840530*uk_125 + 1112650*uk_126 + 5179790*uk_127 + 2028950*uk_128 + 794750*uk_129 + 1325884*uk_13 + 614125*uk_130 + 202300*uk_131 + 859775*uk_132 + 1567825*uk_133 + 614125*uk_134 + 66640*uk_135 + 283220*uk_136 + 516460*uk_137 + 202300*uk_138 + 1203685*uk_139 + 5635007*uk_14 + 2194955*uk_140 + 859775*uk_141 + 4002565*uk_142 + 1567825*uk_143 + 614125*uk_144 + 21952*uk_145 + 93296*uk_146 + 170128*uk_147 + 66640*uk_148 + 396508*uk_149 + 10275601*uk_15 + 723044*uk_150 + 283220*uk_151 + 1318492*uk_152 + 516460*uk_153 + 202300*uk_154 + 1685159*uk_155 + 3072937*uk_156 + 1203685*uk_157 + 5603591*uk_158 + 2194955*uk_159 + 4025005*uk_16 + 859775*uk_160 + 10218313*uk_161 + 4002565*uk_162 + 1567825*uk_163 + 614125*uk_164 + 3969*uk_17 + 6930*uk_18 + 5355*uk_19 + 63*uk_2 + 1764*uk_20 + 7497*uk_21 + 13671*uk_22 + 5355*uk_23 + 12100*uk_24 + 9350*uk_25 + 3080*uk_26 + 13090*uk_27 + 23870*uk_28 + 9350*uk_29 + 110*uk_3 + 7225*uk_30 + 2380*uk_31 + 10115*uk_32 + 18445*uk_33 + 7225*uk_34 + 784*uk_35 + 3332*uk_36 + 6076*uk_37 + 2380*uk_38 + 14161*uk_39 + 85*uk_4 + 25823*uk_40 + 10115*uk_41 + 47089*uk_42 + 18445*uk_43 + 7225*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 246653726990*uk_47 + 190596061765*uk_48 + 62784585052*uk_49 + 28*uk_5 + 266834486471*uk_50 + 486580534153*uk_51 + 190596061765*uk_52 + 187944057*uk_53 + 328156290*uk_54 + 253575315*uk_55 + 83530692*uk_56 + 355005441*uk_57 + 647362863*uk_58 + 253575315*uk_59 + 119*uk_6 + 572971300*uk_60 + 442750550*uk_61 + 145847240*uk_62 + 619850770*uk_63 + 1130316110*uk_64 + 442750550*uk_65 + 342125425*uk_66 + 112700140*uk_67 + 478975595*uk_68 + 873426085*uk_69 + 217*uk_7 + 342125425*uk_70 + 37124752*uk_71 + 157780196*uk_72 + 287716828*uk_73 + 112700140*uk_74 + 670565833*uk_75 + 1222796519*uk_76 + 478975595*uk_77 + 2229805417*uk_78 + 873426085*uk_79 + 85*uk_8 + 342125425*uk_80 + 250047*uk_81 + 436590*uk_82 + 337365*uk_83 + 111132*uk_84 + 472311*uk_85 + 861273*uk_86 + 337365*uk_87 + 762300*uk_88 + 589050*uk_89 + 2242306609*uk_9 + 194040*uk_90 + 824670*uk_91 + 1503810*uk_92 + 589050*uk_93 + 455175*uk_94 + 149940*uk_95 + 637245*uk_96 + 1162035*uk_97 + 455175*uk_98 + 49392*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 182952*uk_100 + 328104*uk_101 + 166320*uk_102 + 922383*uk_103 + 1654191*uk_104 + 838530*uk_105 + 2966607*uk_106 + 1503810*uk_107 + 762300*uk_108 + 74088*uk_109 + 1988826*uk_11 + 194040*uk_110 + 42336*uk_111 + 213444*uk_112 + 382788*uk_113 + 194040*uk_114 + 508200*uk_115 + 110880*uk_116 + 559020*uk_117 + 1002540*uk_118 + 508200*uk_119 + 5208830*uk_12 + 24192*uk_120 + 121968*uk_121 + 218736*uk_122 + 110880*uk_123 + 614922*uk_124 + 1102794*uk_125 + 559020*uk_126 + 1977738*uk_127 + 1002540*uk_128 + 508200*uk_129 + 1136472*uk_13 + 1331000*uk_130 + 290400*uk_131 + 1464100*uk_132 + 2625700*uk_133 + 1331000*uk_134 + 63360*uk_135 + 319440*uk_136 + 572880*uk_137 + 290400*uk_138 + 1610510*uk_139 + 5729713*uk_14 + 2888270*uk_140 + 1464100*uk_141 + 5179790*uk_142 + 2625700*uk_143 + 1331000*uk_144 + 13824*uk_145 + 69696*uk_146 + 124992*uk_147 + 63360*uk_148 + 351384*uk_149 + 10275601*uk_15 + 630168*uk_150 + 319440*uk_151 + 1130136*uk_152 + 572880*uk_153 + 290400*uk_154 + 1771561*uk_155 + 3177097*uk_156 + 1610510*uk_157 + 5697769*uk_158 + 2888270*uk_159 + 5208830*uk_16 + 1464100*uk_160 + 10218313*uk_161 + 5179790*uk_162 + 2625700*uk_163 + 1331000*uk_164 + 3969*uk_17 + 2646*uk_18 + 6930*uk_19 + 63*uk_2 + 1512*uk_20 + 7623*uk_21 + 13671*uk_22 + 6930*uk_23 + 1764*uk_24 + 4620*uk_25 + 1008*uk_26 + 5082*uk_27 + 9114*uk_28 + 4620*uk_29 + 42*uk_3 + 12100*uk_30 + 2640*uk_31 + 13310*uk_32 + 23870*uk_33 + 12100*uk_34 + 576*uk_35 + 2904*uk_36 + 5208*uk_37 + 2640*uk_38 + 14641*uk_39 + 110*uk_4 + 26257*uk_40 + 13310*uk_41 + 47089*uk_42 + 23870*uk_43 + 12100*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 94176877578*uk_47 + 246653726990*uk_48 + 53815358616*uk_49 + 24*uk_5 + 271319099689*uk_50 + 486580534153*uk_51 + 246653726990*uk_52 + 187944057*uk_53 + 125296038*uk_54 + 328156290*uk_55 + 71597736*uk_56 + 360971919*uk_57 + 647362863*uk_58 + 328156290*uk_59 + 121*uk_6 + 83530692*uk_60 + 218770860*uk_61 + 47731824*uk_62 + 240647946*uk_63 + 431575242*uk_64 + 218770860*uk_65 + 572971300*uk_66 + 125011920*uk_67 + 630268430*uk_68 + 1130316110*uk_69 + 217*uk_7 + 572971300*uk_70 + 27275328*uk_71 + 137513112*uk_72 + 246614424*uk_73 + 125011920*uk_74 + 693295273*uk_75 + 1243347721*uk_76 + 630268430*uk_77 + 2229805417*uk_78 + 1130316110*uk_79 + 110*uk_8 + 572971300*uk_80 + 250047*uk_81 + 166698*uk_82 + 436590*uk_83 + 95256*uk_84 + 480249*uk_85 + 861273*uk_86 + 436590*uk_87 + 111132*uk_88 + 291060*uk_89 + 2242306609*uk_9 + 63504*uk_90 + 320166*uk_91 + 574182*uk_92 + 291060*uk_93 + 762300*uk_94 + 166320*uk_95 + 838530*uk_96 + 1503810*uk_97 + 762300*uk_98 + 36288*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 216972*uk_100 + 382788*uk_101 + 74088*uk_102 + 953127*uk_103 + 1681533*uk_104 + 325458*uk_105 + 2966607*uk_106 + 574182*uk_107 + 111132*uk_108 + 1771561*uk_109 + 5729713*uk_11 + 614922*uk_110 + 409948*uk_111 + 1800843*uk_112 + 3177097*uk_113 + 614922*uk_114 + 213444*uk_115 + 142296*uk_116 + 625086*uk_117 + 1102794*uk_118 + 213444*uk_119 + 1988826*uk_12 + 94864*uk_120 + 416724*uk_121 + 735196*uk_122 + 142296*uk_123 + 1830609*uk_124 + 3229611*uk_125 + 625086*uk_126 + 5697769*uk_127 + 1102794*uk_128 + 213444*uk_129 + 1325884*uk_13 + 74088*uk_130 + 49392*uk_131 + 216972*uk_132 + 382788*uk_133 + 74088*uk_134 + 32928*uk_135 + 144648*uk_136 + 255192*uk_137 + 49392*uk_138 + 635418*uk_139 + 5824419*uk_14 + 1121022*uk_140 + 216972*uk_141 + 1977738*uk_142 + 382788*uk_143 + 74088*uk_144 + 21952*uk_145 + 96432*uk_146 + 170128*uk_147 + 32928*uk_148 + 423612*uk_149 + 10275601*uk_15 + 747348*uk_150 + 144648*uk_151 + 1318492*uk_152 + 255192*uk_153 + 49392*uk_154 + 1860867*uk_155 + 3282993*uk_156 + 635418*uk_157 + 5791947*uk_158 + 1121022*uk_159 + 1988826*uk_16 + 216972*uk_160 + 10218313*uk_161 + 1977738*uk_162 + 382788*uk_163 + 74088*uk_164 + 3969*uk_17 + 7623*uk_18 + 2646*uk_19 + 63*uk_2 + 1764*uk_20 + 7749*uk_21 + 13671*uk_22 + 2646*uk_23 + 14641*uk_24 + 5082*uk_25 + 3388*uk_26 + 14883*uk_27 + 26257*uk_28 + 5082*uk_29 + 121*uk_3 + 1764*uk_30 + 1176*uk_31 + 5166*uk_32 + 9114*uk_33 + 1764*uk_34 + 784*uk_35 + 3444*uk_36 + 6076*uk_37 + 1176*uk_38 + 15129*uk_39 + 42*uk_4 + 26691*uk_40 + 5166*uk_41 + 47089*uk_42 + 9114*uk_43 + 1764*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 271319099689*uk_47 + 94176877578*uk_48 + 62784585052*uk_49 + 28*uk_5 + 275803712907*uk_50 + 486580534153*uk_51 + 94176877578*uk_52 + 187944057*uk_53 + 360971919*uk_54 + 125296038*uk_55 + 83530692*uk_56 + 366938397*uk_57 + 647362863*uk_58 + 125296038*uk_59 + 123*uk_6 + 693295273*uk_60 + 240647946*uk_61 + 160431964*uk_62 + 704754699*uk_63 + 1243347721*uk_64 + 240647946*uk_65 + 83530692*uk_66 + 55687128*uk_67 + 244625598*uk_68 + 431575242*uk_69 + 217*uk_7 + 83530692*uk_70 + 37124752*uk_71 + 163083732*uk_72 + 287716828*uk_73 + 55687128*uk_74 + 716403537*uk_75 + 1263898923*uk_76 + 244625598*uk_77 + 2229805417*uk_78 + 431575242*uk_79 + 42*uk_8 + 83530692*uk_80 + 250047*uk_81 + 480249*uk_82 + 166698*uk_83 + 111132*uk_84 + 488187*uk_85 + 861273*uk_86 + 166698*uk_87 + 922383*uk_88 + 320166*uk_89 + 2242306609*uk_9 + 213444*uk_90 + 937629*uk_91 + 1654191*uk_92 + 320166*uk_93 + 111132*uk_94 + 74088*uk_95 + 325458*uk_96 + 574182*uk_97 + 111132*uk_98 + 49392*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 189000*uk_100 + 328104*uk_101 + 182952*uk_102 + 984375*uk_103 + 1708875*uk_104 + 952875*uk_105 + 2966607*uk_106 + 1654191*uk_107 + 922383*uk_108 + 1092727*uk_109 + 4877359*uk_11 + 1283689*uk_110 + 254616*uk_111 + 1326125*uk_112 + 2302153*uk_113 + 1283689*uk_114 + 1508023*uk_115 + 299112*uk_116 + 1557875*uk_117 + 2704471*uk_118 + 1508023*uk_119 + 5729713*uk_12 + 59328*uk_120 + 309000*uk_121 + 536424*uk_122 + 299112*uk_123 + 1609375*uk_124 + 2793875*uk_125 + 1557875*uk_126 + 4850167*uk_127 + 2704471*uk_128 + 1508023*uk_129 + 1136472*uk_13 + 1771561*uk_130 + 351384*uk_131 + 1830125*uk_132 + 3177097*uk_133 + 1771561*uk_134 + 69696*uk_135 + 363000*uk_136 + 630168*uk_137 + 351384*uk_138 + 1890625*uk_139 + 5919125*uk_14 + 3282125*uk_140 + 1830125*uk_141 + 5697769*uk_142 + 3177097*uk_143 + 1771561*uk_144 + 13824*uk_145 + 72000*uk_146 + 124992*uk_147 + 69696*uk_148 + 375000*uk_149 + 10275601*uk_15 + 651000*uk_150 + 363000*uk_151 + 1130136*uk_152 + 630168*uk_153 + 351384*uk_154 + 1953125*uk_155 + 3390625*uk_156 + 1890625*uk_157 + 5886125*uk_158 + 3282125*uk_159 + 5729713*uk_16 + 1830125*uk_160 + 10218313*uk_161 + 5697769*uk_162 + 3177097*uk_163 + 1771561*uk_164 + 3969*uk_17 + 6489*uk_18 + 7623*uk_19 + 63*uk_2 + 1512*uk_20 + 7875*uk_21 + 13671*uk_22 + 7623*uk_23 + 10609*uk_24 + 12463*uk_25 + 2472*uk_26 + 12875*uk_27 + 22351*uk_28 + 12463*uk_29 + 103*uk_3 + 14641*uk_30 + 2904*uk_31 + 15125*uk_32 + 26257*uk_33 + 14641*uk_34 + 576*uk_35 + 3000*uk_36 + 5208*uk_37 + 2904*uk_38 + 15625*uk_39 + 121*uk_4 + 27125*uk_40 + 15125*uk_41 + 47089*uk_42 + 26257*uk_43 + 14641*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 230957580727*uk_47 + 271319099689*uk_48 + 53815358616*uk_49 + 24*uk_5 + 280288326125*uk_50 + 486580534153*uk_51 + 271319099689*uk_52 + 187944057*uk_53 + 307273617*uk_54 + 360971919*uk_55 + 71597736*uk_56 + 372904875*uk_57 + 647362863*uk_58 + 360971919*uk_59 + 125*uk_6 + 502367977*uk_60 + 590160439*uk_61 + 117056616*uk_62 + 609669875*uk_63 + 1058386903*uk_64 + 590160439*uk_65 + 693295273*uk_66 + 137513112*uk_67 + 716214125*uk_68 + 1243347721*uk_69 + 217*uk_7 + 693295273*uk_70 + 27275328*uk_71 + 142059000*uk_72 + 246614424*uk_73 + 137513112*uk_74 + 739890625*uk_75 + 1284450125*uk_76 + 716214125*uk_77 + 2229805417*uk_78 + 1243347721*uk_79 + 121*uk_8 + 693295273*uk_80 + 250047*uk_81 + 408807*uk_82 + 480249*uk_83 + 95256*uk_84 + 496125*uk_85 + 861273*uk_86 + 480249*uk_87 + 668367*uk_88 + 785169*uk_89 + 2242306609*uk_9 + 155736*uk_90 + 811125*uk_91 + 1408113*uk_92 + 785169*uk_93 + 922383*uk_94 + 182952*uk_95 + 952875*uk_96 + 1654191*uk_97 + 922383*uk_98 + 36288*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 192024*uk_100 + 328104*uk_101 + 155736*uk_102 + 1016127*uk_103 + 1736217*uk_104 + 824103*uk_105 + 2966607*uk_106 + 1408113*uk_107 + 668367*uk_108 + 1295029*uk_109 + 5161477*uk_11 + 1223743*uk_110 + 285144*uk_111 + 1508887*uk_112 + 2578177*uk_113 + 1223743*uk_114 + 1156381*uk_115 + 269448*uk_116 + 1425829*uk_117 + 2436259*uk_118 + 1156381*uk_119 + 4877359*uk_12 + 62784*uk_120 + 332232*uk_121 + 567672*uk_122 + 269448*uk_123 + 1758061*uk_124 + 3003931*uk_125 + 1425829*uk_126 + 5132701*uk_127 + 2436259*uk_128 + 1156381*uk_129 + 1136472*uk_13 + 1092727*uk_130 + 254616*uk_131 + 1347343*uk_132 + 2302153*uk_133 + 1092727*uk_134 + 59328*uk_135 + 313944*uk_136 + 536424*uk_137 + 254616*uk_138 + 1661287*uk_139 + 6013831*uk_14 + 2838577*uk_140 + 1347343*uk_141 + 4850167*uk_142 + 2302153*uk_143 + 1092727*uk_144 + 13824*uk_145 + 73152*uk_146 + 124992*uk_147 + 59328*uk_148 + 387096*uk_149 + 10275601*uk_15 + 661416*uk_150 + 313944*uk_151 + 1130136*uk_152 + 536424*uk_153 + 254616*uk_154 + 2048383*uk_155 + 3499993*uk_156 + 1661287*uk_157 + 5980303*uk_158 + 2838577*uk_159 + 4877359*uk_16 + 1347343*uk_160 + 10218313*uk_161 + 4850167*uk_162 + 2302153*uk_163 + 1092727*uk_164 + 3969*uk_17 + 6867*uk_18 + 6489*uk_19 + 63*uk_2 + 1512*uk_20 + 8001*uk_21 + 13671*uk_22 + 6489*uk_23 + 11881*uk_24 + 11227*uk_25 + 2616*uk_26 + 13843*uk_27 + 23653*uk_28 + 11227*uk_29 + 109*uk_3 + 10609*uk_30 + 2472*uk_31 + 13081*uk_32 + 22351*uk_33 + 10609*uk_34 + 576*uk_35 + 3048*uk_36 + 5208*uk_37 + 2472*uk_38 + 16129*uk_39 + 103*uk_4 + 27559*uk_40 + 13081*uk_41 + 47089*uk_42 + 22351*uk_43 + 10609*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 244411420381*uk_47 + 230957580727*uk_48 + 53815358616*uk_49 + 24*uk_5 + 284772939343*uk_50 + 486580534153*uk_51 + 230957580727*uk_52 + 187944057*uk_53 + 325173051*uk_54 + 307273617*uk_55 + 71597736*uk_56 + 378871353*uk_57 + 647362863*uk_58 + 307273617*uk_59 + 127*uk_6 + 562600993*uk_60 + 531632131*uk_61 + 123875448*uk_62 + 655507579*uk_63 + 1120040509*uk_64 + 531632131*uk_65 + 502367977*uk_66 + 117056616*uk_67 + 619424593*uk_68 + 1058386903*uk_69 + 217*uk_7 + 502367977*uk_70 + 27275328*uk_71 + 144331944*uk_72 + 246614424*uk_73 + 117056616*uk_74 + 763756537*uk_75 + 1305001327*uk_76 + 619424593*uk_77 + 2229805417*uk_78 + 1058386903*uk_79 + 103*uk_8 + 502367977*uk_80 + 250047*uk_81 + 432621*uk_82 + 408807*uk_83 + 95256*uk_84 + 504063*uk_85 + 861273*uk_86 + 408807*uk_87 + 748503*uk_88 + 707301*uk_89 + 2242306609*uk_9 + 164808*uk_90 + 872109*uk_91 + 1490139*uk_92 + 707301*uk_93 + 668367*uk_94 + 155736*uk_95 + 824103*uk_96 + 1408113*uk_97 + 668367*uk_98 + 36288*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 162540*uk_100 + 273420*uk_101 + 137340*uk_102 + 1048383*uk_103 + 1763559*uk_104 + 885843*uk_105 + 2966607*uk_106 + 1490139*uk_107 + 748503*uk_108 + 1000*uk_109 + 473530*uk_11 + 10900*uk_110 + 2000*uk_111 + 12900*uk_112 + 21700*uk_113 + 10900*uk_114 + 118810*uk_115 + 21800*uk_116 + 140610*uk_117 + 236530*uk_118 + 118810*uk_119 + 5161477*uk_12 + 4000*uk_120 + 25800*uk_121 + 43400*uk_122 + 21800*uk_123 + 166410*uk_124 + 279930*uk_125 + 140610*uk_126 + 470890*uk_127 + 236530*uk_128 + 118810*uk_129 + 947060*uk_13 + 1295029*uk_130 + 237620*uk_131 + 1532649*uk_132 + 2578177*uk_133 + 1295029*uk_134 + 43600*uk_135 + 281220*uk_136 + 473060*uk_137 + 237620*uk_138 + 1813869*uk_139 + 6108537*uk_14 + 3051237*uk_140 + 1532649*uk_141 + 5132701*uk_142 + 2578177*uk_143 + 1295029*uk_144 + 8000*uk_145 + 51600*uk_146 + 86800*uk_147 + 43600*uk_148 + 332820*uk_149 + 10275601*uk_15 + 559860*uk_150 + 281220*uk_151 + 941780*uk_152 + 473060*uk_153 + 237620*uk_154 + 2146689*uk_155 + 3611097*uk_156 + 1813869*uk_157 + 6074481*uk_158 + 3051237*uk_159 + 5161477*uk_16 + 1532649*uk_160 + 10218313*uk_161 + 5132701*uk_162 + 2578177*uk_163 + 1295029*uk_164 + 3969*uk_17 + 630*uk_18 + 6867*uk_19 + 63*uk_2 + 1260*uk_20 + 8127*uk_21 + 13671*uk_22 + 6867*uk_23 + 100*uk_24 + 1090*uk_25 + 200*uk_26 + 1290*uk_27 + 2170*uk_28 + 1090*uk_29 + 10*uk_3 + 11881*uk_30 + 2180*uk_31 + 14061*uk_32 + 23653*uk_33 + 11881*uk_34 + 400*uk_35 + 2580*uk_36 + 4340*uk_37 + 2180*uk_38 + 16641*uk_39 + 109*uk_4 + 27993*uk_40 + 14061*uk_41 + 47089*uk_42 + 23653*uk_43 + 11881*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 22423066090*uk_47 + 244411420381*uk_48 + 44846132180*uk_49 + 20*uk_5 + 289257552561*uk_50 + 486580534153*uk_51 + 244411420381*uk_52 + 187944057*uk_53 + 29832390*uk_54 + 325173051*uk_55 + 59664780*uk_56 + 384837831*uk_57 + 647362863*uk_58 + 325173051*uk_59 + 129*uk_6 + 4735300*uk_60 + 51614770*uk_61 + 9470600*uk_62 + 61085370*uk_63 + 102756010*uk_64 + 51614770*uk_65 + 562600993*uk_66 + 103229540*uk_67 + 665830533*uk_68 + 1120040509*uk_69 + 217*uk_7 + 562600993*uk_70 + 18941200*uk_71 + 122170740*uk_72 + 205512020*uk_73 + 103229540*uk_74 + 788001273*uk_75 + 1325552529*uk_76 + 665830533*uk_77 + 2229805417*uk_78 + 1120040509*uk_79 + 109*uk_8 + 562600993*uk_80 + 250047*uk_81 + 39690*uk_82 + 432621*uk_83 + 79380*uk_84 + 512001*uk_85 + 861273*uk_86 + 432621*uk_87 + 6300*uk_88 + 68670*uk_89 + 2242306609*uk_9 + 12600*uk_90 + 81270*uk_91 + 136710*uk_92 + 68670*uk_93 + 748503*uk_94 + 137340*uk_95 + 885843*uk_96 + 1490139*uk_97 + 748503*uk_98 + 25200*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 198072*uk_100 + 328104*uk_101 + 15120*uk_102 + 1081143*uk_103 + 1790901*uk_104 + 82530*uk_105 + 2966607*uk_106 + 136710*uk_107 + 6300*uk_108 + 238328*uk_109 + 2935886*uk_11 + 38440*uk_110 + 92256*uk_111 + 503564*uk_112 + 834148*uk_113 + 38440*uk_114 + 6200*uk_115 + 14880*uk_116 + 81220*uk_117 + 134540*uk_118 + 6200*uk_119 + 473530*uk_12 + 35712*uk_120 + 194928*uk_121 + 322896*uk_122 + 14880*uk_123 + 1063982*uk_124 + 1762474*uk_125 + 81220*uk_126 + 2919518*uk_127 + 134540*uk_128 + 6200*uk_129 + 1136472*uk_13 + 1000*uk_130 + 2400*uk_131 + 13100*uk_132 + 21700*uk_133 + 1000*uk_134 + 5760*uk_135 + 31440*uk_136 + 52080*uk_137 + 2400*uk_138 + 171610*uk_139 + 6203243*uk_14 + 284270*uk_140 + 13100*uk_141 + 470890*uk_142 + 21700*uk_143 + 1000*uk_144 + 13824*uk_145 + 75456*uk_146 + 124992*uk_147 + 5760*uk_148 + 411864*uk_149 + 10275601*uk_15 + 682248*uk_150 + 31440*uk_151 + 1130136*uk_152 + 52080*uk_153 + 2400*uk_154 + 2248091*uk_155 + 3723937*uk_156 + 171610*uk_157 + 6168659*uk_158 + 284270*uk_159 + 473530*uk_16 + 13100*uk_160 + 10218313*uk_161 + 470890*uk_162 + 21700*uk_163 + 1000*uk_164 + 3969*uk_17 + 3906*uk_18 + 630*uk_19 + 63*uk_2 + 1512*uk_20 + 8253*uk_21 + 13671*uk_22 + 630*uk_23 + 3844*uk_24 + 620*uk_25 + 1488*uk_26 + 8122*uk_27 + 13454*uk_28 + 620*uk_29 + 62*uk_3 + 100*uk_30 + 240*uk_31 + 1310*uk_32 + 2170*uk_33 + 100*uk_34 + 576*uk_35 + 3144*uk_36 + 5208*uk_37 + 240*uk_38 + 17161*uk_39 + 10*uk_4 + 28427*uk_40 + 1310*uk_41 + 47089*uk_42 + 2170*uk_43 + 100*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 139023009758*uk_47 + 22423066090*uk_48 + 53815358616*uk_49 + 24*uk_5 + 293742165779*uk_50 + 486580534153*uk_51 + 22423066090*uk_52 + 187944057*uk_53 + 184960818*uk_54 + 29832390*uk_55 + 71597736*uk_56 + 390804309*uk_57 + 647362863*uk_58 + 29832390*uk_59 + 131*uk_6 + 182024932*uk_60 + 29358860*uk_61 + 70461264*uk_62 + 384601066*uk_63 + 637087262*uk_64 + 29358860*uk_65 + 4735300*uk_66 + 11364720*uk_67 + 62032430*uk_68 + 102756010*uk_69 + 217*uk_7 + 4735300*uk_70 + 27275328*uk_71 + 148877832*uk_72 + 246614424*uk_73 + 11364720*uk_74 + 812624833*uk_75 + 1346103731*uk_76 + 62032430*uk_77 + 2229805417*uk_78 + 102756010*uk_79 + 10*uk_8 + 4735300*uk_80 + 250047*uk_81 + 246078*uk_82 + 39690*uk_83 + 95256*uk_84 + 519939*uk_85 + 861273*uk_86 + 39690*uk_87 + 242172*uk_88 + 39060*uk_89 + 2242306609*uk_9 + 93744*uk_90 + 511686*uk_91 + 847602*uk_92 + 39060*uk_93 + 6300*uk_94 + 15120*uk_95 + 82530*uk_96 + 136710*uk_97 + 6300*uk_98 + 36288*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 167580*uk_100 + 273420*uk_101 + 78120*uk_102 + 1114407*uk_103 + 1818243*uk_104 + 519498*uk_105 + 2966607*uk_106 + 847602*uk_107 + 242172*uk_108 + 125*uk_109 + 236765*uk_11 + 1550*uk_110 + 500*uk_111 + 3325*uk_112 + 5425*uk_113 + 1550*uk_114 + 19220*uk_115 + 6200*uk_116 + 41230*uk_117 + 67270*uk_118 + 19220*uk_119 + 2935886*uk_12 + 2000*uk_120 + 13300*uk_121 + 21700*uk_122 + 6200*uk_123 + 88445*uk_124 + 144305*uk_125 + 41230*uk_126 + 235445*uk_127 + 67270*uk_128 + 19220*uk_129 + 947060*uk_13 + 238328*uk_130 + 76880*uk_131 + 511252*uk_132 + 834148*uk_133 + 238328*uk_134 + 24800*uk_135 + 164920*uk_136 + 269080*uk_137 + 76880*uk_138 + 1096718*uk_139 + 6297949*uk_14 + 1789382*uk_140 + 511252*uk_141 + 2919518*uk_142 + 834148*uk_143 + 238328*uk_144 + 8000*uk_145 + 53200*uk_146 + 86800*uk_147 + 24800*uk_148 + 353780*uk_149 + 10275601*uk_15 + 577220*uk_150 + 164920*uk_151 + 941780*uk_152 + 269080*uk_153 + 76880*uk_154 + 2352637*uk_155 + 3838513*uk_156 + 1096718*uk_157 + 6262837*uk_158 + 1789382*uk_159 + 2935886*uk_16 + 511252*uk_160 + 10218313*uk_161 + 2919518*uk_162 + 834148*uk_163 + 238328*uk_164 + 3969*uk_17 + 315*uk_18 + 3906*uk_19 + 63*uk_2 + 1260*uk_20 + 8379*uk_21 + 13671*uk_22 + 3906*uk_23 + 25*uk_24 + 310*uk_25 + 100*uk_26 + 665*uk_27 + 1085*uk_28 + 310*uk_29 + 5*uk_3 + 3844*uk_30 + 1240*uk_31 + 8246*uk_32 + 13454*uk_33 + 3844*uk_34 + 400*uk_35 + 2660*uk_36 + 4340*uk_37 + 1240*uk_38 + 17689*uk_39 + 62*uk_4 + 28861*uk_40 + 8246*uk_41 + 47089*uk_42 + 13454*uk_43 + 3844*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 11211533045*uk_47 + 139023009758*uk_48 + 44846132180*uk_49 + 20*uk_5 + 298226778997*uk_50 + 486580534153*uk_51 + 139023009758*uk_52 + 187944057*uk_53 + 14916195*uk_54 + 184960818*uk_55 + 59664780*uk_56 + 396770787*uk_57 + 647362863*uk_58 + 184960818*uk_59 + 133*uk_6 + 1183825*uk_60 + 14679430*uk_61 + 4735300*uk_62 + 31489745*uk_63 + 51378005*uk_64 + 14679430*uk_65 + 182024932*uk_66 + 58717720*uk_67 + 390472838*uk_68 + 637087262*uk_69 + 217*uk_7 + 182024932*uk_70 + 18941200*uk_71 + 125958980*uk_72 + 205512020*uk_73 + 58717720*uk_74 + 837627217*uk_75 + 1366654933*uk_76 + 390472838*uk_77 + 2229805417*uk_78 + 637087262*uk_79 + 62*uk_8 + 182024932*uk_80 + 250047*uk_81 + 19845*uk_82 + 246078*uk_83 + 79380*uk_84 + 527877*uk_85 + 861273*uk_86 + 246078*uk_87 + 1575*uk_88 + 19530*uk_89 + 2242306609*uk_9 + 6300*uk_90 + 41895*uk_91 + 68355*uk_92 + 19530*uk_93 + 242172*uk_94 + 78120*uk_95 + 519498*uk_96 + 847602*uk_97 + 242172*uk_98 + 25200*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 204120*uk_100 + 328104*uk_101 + 7560*uk_102 + 1148175*uk_103 + 1845585*uk_104 + 42525*uk_105 + 2966607*uk_106 + 68355*uk_107 + 1575*uk_108 + 1092727*uk_109 + 4877359*uk_11 + 53045*uk_110 + 254616*uk_111 + 1432215*uk_112 + 2302153*uk_113 + 53045*uk_114 + 2575*uk_115 + 12360*uk_116 + 69525*uk_117 + 111755*uk_118 + 2575*uk_119 + 236765*uk_12 + 59328*uk_120 + 333720*uk_121 + 536424*uk_122 + 12360*uk_123 + 1877175*uk_124 + 3017385*uk_125 + 69525*uk_126 + 4850167*uk_127 + 111755*uk_128 + 2575*uk_129 + 1136472*uk_13 + 125*uk_130 + 600*uk_131 + 3375*uk_132 + 5425*uk_133 + 125*uk_134 + 2880*uk_135 + 16200*uk_136 + 26040*uk_137 + 600*uk_138 + 91125*uk_139 + 6392655*uk_14 + 146475*uk_140 + 3375*uk_141 + 235445*uk_142 + 5425*uk_143 + 125*uk_144 + 13824*uk_145 + 77760*uk_146 + 124992*uk_147 + 2880*uk_148 + 437400*uk_149 + 10275601*uk_15 + 703080*uk_150 + 16200*uk_151 + 1130136*uk_152 + 26040*uk_153 + 600*uk_154 + 2460375*uk_155 + 3954825*uk_156 + 91125*uk_157 + 6357015*uk_158 + 146475*uk_159 + 236765*uk_16 + 3375*uk_160 + 10218313*uk_161 + 235445*uk_162 + 5425*uk_163 + 125*uk_164 + 3969*uk_17 + 6489*uk_18 + 315*uk_19 + 63*uk_2 + 1512*uk_20 + 8505*uk_21 + 13671*uk_22 + 315*uk_23 + 10609*uk_24 + 515*uk_25 + 2472*uk_26 + 13905*uk_27 + 22351*uk_28 + 515*uk_29 + 103*uk_3 + 25*uk_30 + 120*uk_31 + 675*uk_32 + 1085*uk_33 + 25*uk_34 + 576*uk_35 + 3240*uk_36 + 5208*uk_37 + 120*uk_38 + 18225*uk_39 + 5*uk_4 + 29295*uk_40 + 675*uk_41 + 47089*uk_42 + 1085*uk_43 + 25*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 230957580727*uk_47 + 11211533045*uk_48 + 53815358616*uk_49 + 24*uk_5 + 302711392215*uk_50 + 486580534153*uk_51 + 11211533045*uk_52 + 187944057*uk_53 + 307273617*uk_54 + 14916195*uk_55 + 71597736*uk_56 + 402737265*uk_57 + 647362863*uk_58 + 14916195*uk_59 + 135*uk_6 + 502367977*uk_60 + 24386795*uk_61 + 117056616*uk_62 + 658443465*uk_63 + 1058386903*uk_64 + 24386795*uk_65 + 1183825*uk_66 + 5682360*uk_67 + 31963275*uk_68 + 51378005*uk_69 + 217*uk_7 + 1183825*uk_70 + 27275328*uk_71 + 153423720*uk_72 + 246614424*uk_73 + 5682360*uk_74 + 863008425*uk_75 + 1387206135*uk_76 + 31963275*uk_77 + 2229805417*uk_78 + 51378005*uk_79 + 5*uk_8 + 1183825*uk_80 + 250047*uk_81 + 408807*uk_82 + 19845*uk_83 + 95256*uk_84 + 535815*uk_85 + 861273*uk_86 + 19845*uk_87 + 668367*uk_88 + 32445*uk_89 + 2242306609*uk_9 + 155736*uk_90 + 876015*uk_91 + 1408113*uk_92 + 32445*uk_93 + 1575*uk_94 + 7560*uk_95 + 42525*uk_96 + 68355*uk_97 + 1575*uk_98 + 36288*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 172620*uk_100 + 273420*uk_101 + 129780*uk_102 + 1182447*uk_103 + 1872927*uk_104 + 888993*uk_105 + 2966607*uk_106 + 1408113*uk_107 + 668367*uk_108 + 681472*uk_109 + 4167064*uk_11 + 797632*uk_110 + 154880*uk_111 + 1060928*uk_112 + 1680448*uk_113 + 797632*uk_114 + 933592*uk_115 + 181280*uk_116 + 1241768*uk_117 + 1966888*uk_118 + 933592*uk_119 + 4877359*uk_12 + 35200*uk_120 + 241120*uk_121 + 381920*uk_122 + 181280*uk_123 + 1651672*uk_124 + 2616152*uk_125 + 1241768*uk_126 + 4143832*uk_127 + 1966888*uk_128 + 933592*uk_129 + 947060*uk_13 + 1092727*uk_130 + 212180*uk_131 + 1453433*uk_132 + 2302153*uk_133 + 1092727*uk_134 + 41200*uk_135 + 282220*uk_136 + 447020*uk_137 + 212180*uk_138 + 1933207*uk_139 + 6487361*uk_14 + 3062087*uk_140 + 1453433*uk_141 + 4850167*uk_142 + 2302153*uk_143 + 1092727*uk_144 + 8000*uk_145 + 54800*uk_146 + 86800*uk_147 + 41200*uk_148 + 375380*uk_149 + 10275601*uk_15 + 594580*uk_150 + 282220*uk_151 + 941780*uk_152 + 447020*uk_153 + 212180*uk_154 + 2571353*uk_155 + 4072873*uk_156 + 1933207*uk_157 + 6451193*uk_158 + 3062087*uk_159 + 4877359*uk_16 + 1453433*uk_160 + 10218313*uk_161 + 4850167*uk_162 + 2302153*uk_163 + 1092727*uk_164 + 3969*uk_17 + 5544*uk_18 + 6489*uk_19 + 63*uk_2 + 1260*uk_20 + 8631*uk_21 + 13671*uk_22 + 6489*uk_23 + 7744*uk_24 + 9064*uk_25 + 1760*uk_26 + 12056*uk_27 + 19096*uk_28 + 9064*uk_29 + 88*uk_3 + 10609*uk_30 + 2060*uk_31 + 14111*uk_32 + 22351*uk_33 + 10609*uk_34 + 400*uk_35 + 2740*uk_36 + 4340*uk_37 + 2060*uk_38 + 18769*uk_39 + 103*uk_4 + 29729*uk_40 + 14111*uk_41 + 47089*uk_42 + 22351*uk_43 + 10609*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 197322981592*uk_47 + 230957580727*uk_48 + 44846132180*uk_49 + 20*uk_5 + 307196005433*uk_50 + 486580534153*uk_51 + 230957580727*uk_52 + 187944057*uk_53 + 262525032*uk_54 + 307273617*uk_55 + 59664780*uk_56 + 408703743*uk_57 + 647362863*uk_58 + 307273617*uk_59 + 137*uk_6 + 366701632*uk_60 + 429207592*uk_61 + 83341280*uk_62 + 570887768*uk_63 + 904252888*uk_64 + 429207592*uk_65 + 502367977*uk_66 + 97547180*uk_67 + 668198183*uk_68 + 1058386903*uk_69 + 217*uk_7 + 502367977*uk_70 + 18941200*uk_71 + 129747220*uk_72 + 205512020*uk_73 + 97547180*uk_74 + 888768457*uk_75 + 1407757337*uk_76 + 668198183*uk_77 + 2229805417*uk_78 + 1058386903*uk_79 + 103*uk_8 + 502367977*uk_80 + 250047*uk_81 + 349272*uk_82 + 408807*uk_83 + 79380*uk_84 + 543753*uk_85 + 861273*uk_86 + 408807*uk_87 + 487872*uk_88 + 571032*uk_89 + 2242306609*uk_9 + 110880*uk_90 + 759528*uk_91 + 1203048*uk_92 + 571032*uk_93 + 668367*uk_94 + 129780*uk_95 + 888993*uk_96 + 1408113*uk_97 + 668367*uk_98 + 25200*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 175140*uk_100 + 273420*uk_101 + 110880*uk_102 + 1217223*uk_103 + 1900269*uk_104 + 770616*uk_105 + 2966607*uk_106 + 1203048*uk_107 + 487872*uk_108 + 804357*uk_109 + 4403829*uk_11 + 761112*uk_110 + 172980*uk_111 + 1202211*uk_112 + 1876833*uk_113 + 761112*uk_114 + 720192*uk_115 + 163680*uk_116 + 1137576*uk_117 + 1775928*uk_118 + 720192*uk_119 + 4167064*uk_12 + 37200*uk_120 + 258540*uk_121 + 403620*uk_122 + 163680*uk_123 + 1796853*uk_124 + 2805159*uk_125 + 1137576*uk_126 + 4379277*uk_127 + 1775928*uk_128 + 720192*uk_129 + 947060*uk_13 + 681472*uk_130 + 154880*uk_131 + 1076416*uk_132 + 1680448*uk_133 + 681472*uk_134 + 35200*uk_135 + 244640*uk_136 + 381920*uk_137 + 154880*uk_138 + 1700248*uk_139 + 6582067*uk_14 + 2654344*uk_140 + 1076416*uk_141 + 4143832*uk_142 + 1680448*uk_143 + 681472*uk_144 + 8000*uk_145 + 55600*uk_146 + 86800*uk_147 + 35200*uk_148 + 386420*uk_149 + 10275601*uk_15 + 603260*uk_150 + 244640*uk_151 + 941780*uk_152 + 381920*uk_153 + 154880*uk_154 + 2685619*uk_155 + 4192657*uk_156 + 1700248*uk_157 + 6545371*uk_158 + 2654344*uk_159 + 4167064*uk_16 + 1076416*uk_160 + 10218313*uk_161 + 4143832*uk_162 + 1680448*uk_163 + 681472*uk_164 + 3969*uk_17 + 5859*uk_18 + 5544*uk_19 + 63*uk_2 + 1260*uk_20 + 8757*uk_21 + 13671*uk_22 + 5544*uk_23 + 8649*uk_24 + 8184*uk_25 + 1860*uk_26 + 12927*uk_27 + 20181*uk_28 + 8184*uk_29 + 93*uk_3 + 7744*uk_30 + 1760*uk_31 + 12232*uk_32 + 19096*uk_33 + 7744*uk_34 + 400*uk_35 + 2780*uk_36 + 4340*uk_37 + 1760*uk_38 + 19321*uk_39 + 88*uk_4 + 30163*uk_40 + 12232*uk_41 + 47089*uk_42 + 19096*uk_43 + 7744*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 208534514637*uk_47 + 197322981592*uk_48 + 44846132180*uk_49 + 20*uk_5 + 311680618651*uk_50 + 486580534153*uk_51 + 197322981592*uk_52 + 187944057*uk_53 + 277441227*uk_54 + 262525032*uk_55 + 59664780*uk_56 + 414670221*uk_57 + 647362863*uk_58 + 262525032*uk_59 + 139*uk_6 + 409556097*uk_60 + 387536952*uk_61 + 88076580*uk_62 + 612132231*uk_63 + 955630893*uk_64 + 387536952*uk_65 + 366701632*uk_66 + 83341280*uk_67 + 579221896*uk_68 + 904252888*uk_69 + 217*uk_7 + 366701632*uk_70 + 18941200*uk_71 + 131641340*uk_72 + 205512020*uk_73 + 83341280*uk_74 + 914907313*uk_75 + 1428308539*uk_76 + 579221896*uk_77 + 2229805417*uk_78 + 904252888*uk_79 + 88*uk_8 + 366701632*uk_80 + 250047*uk_81 + 369117*uk_82 + 349272*uk_83 + 79380*uk_84 + 551691*uk_85 + 861273*uk_86 + 349272*uk_87 + 544887*uk_88 + 515592*uk_89 + 2242306609*uk_9 + 117180*uk_90 + 814401*uk_91 + 1271403*uk_92 + 515592*uk_93 + 487872*uk_94 + 110880*uk_95 + 770616*uk_96 + 1203048*uk_97 + 487872*uk_98 + 25200*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 177660*uk_100 + 273420*uk_101 + 117180*uk_102 + 1252503*uk_103 + 1927611*uk_104 + 826119*uk_105 + 2966607*uk_106 + 1271403*uk_107 + 544887*uk_108 + 1643032*uk_109 + 5587654*uk_11 + 1294932*uk_110 + 278480*uk_111 + 1963284*uk_112 + 3021508*uk_113 + 1294932*uk_114 + 1020582*uk_115 + 219480*uk_116 + 1547334*uk_117 + 2381358*uk_118 + 1020582*uk_119 + 4403829*uk_12 + 47200*uk_120 + 332760*uk_121 + 512120*uk_122 + 219480*uk_123 + 2345958*uk_124 + 3610446*uk_125 + 1547334*uk_126 + 5556502*uk_127 + 2381358*uk_128 + 1020582*uk_129 + 947060*uk_13 + 804357*uk_130 + 172980*uk_131 + 1219509*uk_132 + 1876833*uk_133 + 804357*uk_134 + 37200*uk_135 + 262260*uk_136 + 403620*uk_137 + 172980*uk_138 + 1848933*uk_139 + 6676773*uk_14 + 2845521*uk_140 + 1219509*uk_141 + 4379277*uk_142 + 1876833*uk_143 + 804357*uk_144 + 8000*uk_145 + 56400*uk_146 + 86800*uk_147 + 37200*uk_148 + 397620*uk_149 + 10275601*uk_15 + 611940*uk_150 + 262260*uk_151 + 941780*uk_152 + 403620*uk_153 + 172980*uk_154 + 2803221*uk_155 + 4314177*uk_156 + 1848933*uk_157 + 6639549*uk_158 + 2845521*uk_159 + 4403829*uk_16 + 1219509*uk_160 + 10218313*uk_161 + 4379277*uk_162 + 1876833*uk_163 + 804357*uk_164 + 3969*uk_17 + 7434*uk_18 + 5859*uk_19 + 63*uk_2 + 1260*uk_20 + 8883*uk_21 + 13671*uk_22 + 5859*uk_23 + 13924*uk_24 + 10974*uk_25 + 2360*uk_26 + 16638*uk_27 + 25606*uk_28 + 10974*uk_29 + 118*uk_3 + 8649*uk_30 + 1860*uk_31 + 13113*uk_32 + 20181*uk_33 + 8649*uk_34 + 400*uk_35 + 2820*uk_36 + 4340*uk_37 + 1860*uk_38 + 19881*uk_39 + 93*uk_4 + 30597*uk_40 + 13113*uk_41 + 47089*uk_42 + 20181*uk_43 + 8649*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 264592179862*uk_47 + 208534514637*uk_48 + 44846132180*uk_49 + 20*uk_5 + 316165231869*uk_50 + 486580534153*uk_51 + 208534514637*uk_52 + 187944057*uk_53 + 352022202*uk_54 + 277441227*uk_55 + 59664780*uk_56 + 420636699*uk_57 + 647362863*uk_58 + 277441227*uk_59 + 141*uk_6 + 659343172*uk_60 + 519651822*uk_61 + 111753080*uk_62 + 787859214*uk_63 + 1212520918*uk_64 + 519651822*uk_65 + 409556097*uk_66 + 88076580*uk_67 + 620939889*uk_68 + 955630893*uk_69 + 217*uk_7 + 409556097*uk_70 + 18941200*uk_71 + 133535460*uk_72 + 205512020*uk_73 + 88076580*uk_74 + 941424993*uk_75 + 1448859741*uk_76 + 620939889*uk_77 + 2229805417*uk_78 + 955630893*uk_79 + 93*uk_8 + 409556097*uk_80 + 250047*uk_81 + 468342*uk_82 + 369117*uk_83 + 79380*uk_84 + 559629*uk_85 + 861273*uk_86 + 369117*uk_87 + 877212*uk_88 + 691362*uk_89 + 2242306609*uk_9 + 148680*uk_90 + 1048194*uk_91 + 1613178*uk_92 + 691362*uk_93 + 544887*uk_94 + 117180*uk_95 + 826119*uk_96 + 1271403*uk_97 + 544887*uk_98 + 25200*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 144144*uk_100 + 218736*uk_101 + 118944*uk_102 + 1288287*uk_103 + 1954953*uk_104 + 1063062*uk_105 + 2966607*uk_106 + 1613178*uk_107 + 877212*uk_108 + 8000*uk_109 + 947060*uk_11 + 47200*uk_110 + 6400*uk_111 + 57200*uk_112 + 86800*uk_113 + 47200*uk_114 + 278480*uk_115 + 37760*uk_116 + 337480*uk_117 + 512120*uk_118 + 278480*uk_119 + 5587654*uk_12 + 5120*uk_120 + 45760*uk_121 + 69440*uk_122 + 37760*uk_123 + 408980*uk_124 + 620620*uk_125 + 337480*uk_126 + 941780*uk_127 + 512120*uk_128 + 278480*uk_129 + 757648*uk_13 + 1643032*uk_130 + 222784*uk_131 + 1991132*uk_132 + 3021508*uk_133 + 1643032*uk_134 + 30208*uk_135 + 269984*uk_136 + 409696*uk_137 + 222784*uk_138 + 2412982*uk_139 + 6771479*uk_14 + 3661658*uk_140 + 1991132*uk_141 + 5556502*uk_142 + 3021508*uk_143 + 1643032*uk_144 + 4096*uk_145 + 36608*uk_146 + 55552*uk_147 + 30208*uk_148 + 327184*uk_149 + 10275601*uk_15 + 496496*uk_150 + 269984*uk_151 + 753424*uk_152 + 409696*uk_153 + 222784*uk_154 + 2924207*uk_155 + 4437433*uk_156 + 2412982*uk_157 + 6733727*uk_158 + 3661658*uk_159 + 5587654*uk_16 + 1991132*uk_160 + 10218313*uk_161 + 5556502*uk_162 + 3021508*uk_163 + 1643032*uk_164 + 3969*uk_17 + 1260*uk_18 + 7434*uk_19 + 63*uk_2 + 1008*uk_20 + 9009*uk_21 + 13671*uk_22 + 7434*uk_23 + 400*uk_24 + 2360*uk_25 + 320*uk_26 + 2860*uk_27 + 4340*uk_28 + 2360*uk_29 + 20*uk_3 + 13924*uk_30 + 1888*uk_31 + 16874*uk_32 + 25606*uk_33 + 13924*uk_34 + 256*uk_35 + 2288*uk_36 + 3472*uk_37 + 1888*uk_38 + 20449*uk_39 + 118*uk_4 + 31031*uk_40 + 16874*uk_41 + 47089*uk_42 + 25606*uk_43 + 13924*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 44846132180*uk_47 + 264592179862*uk_48 + 35876905744*uk_49 + 16*uk_5 + 320649845087*uk_50 + 486580534153*uk_51 + 264592179862*uk_52 + 187944057*uk_53 + 59664780*uk_54 + 352022202*uk_55 + 47731824*uk_56 + 426603177*uk_57 + 647362863*uk_58 + 352022202*uk_59 + 143*uk_6 + 18941200*uk_60 + 111753080*uk_61 + 15152960*uk_62 + 135429580*uk_63 + 205512020*uk_64 + 111753080*uk_65 + 659343172*uk_66 + 89402464*uk_67 + 799034522*uk_68 + 1212520918*uk_69 + 217*uk_7 + 659343172*uk_70 + 12122368*uk_71 + 108343664*uk_72 + 164409616*uk_73 + 89402464*uk_74 + 968321497*uk_75 + 1469410943*uk_76 + 799034522*uk_77 + 2229805417*uk_78 + 1212520918*uk_79 + 118*uk_8 + 659343172*uk_80 + 250047*uk_81 + 79380*uk_82 + 468342*uk_83 + 63504*uk_84 + 567567*uk_85 + 861273*uk_86 + 468342*uk_87 + 25200*uk_88 + 148680*uk_89 + 2242306609*uk_9 + 20160*uk_90 + 180180*uk_91 + 273420*uk_92 + 148680*uk_93 + 877212*uk_94 + 118944*uk_95 + 1063062*uk_96 + 1613178*uk_97 + 877212*uk_98 + 16128*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 182700*uk_100 + 273420*uk_101 + 25200*uk_102 + 1324575*uk_103 + 1982295*uk_104 + 182700*uk_105 + 2966607*uk_106 + 273420*uk_107 + 25200*uk_108 + 571787*uk_109 + 3930299*uk_11 + 137780*uk_110 + 137780*uk_111 + 998905*uk_112 + 1494913*uk_113 + 137780*uk_114 + 33200*uk_115 + 33200*uk_116 + 240700*uk_117 + 360220*uk_118 + 33200*uk_119 + 947060*uk_12 + 33200*uk_120 + 240700*uk_121 + 360220*uk_122 + 33200*uk_123 + 1745075*uk_124 + 2611595*uk_125 + 240700*uk_126 + 3908387*uk_127 + 360220*uk_128 + 33200*uk_129 + 947060*uk_13 + 8000*uk_130 + 8000*uk_131 + 58000*uk_132 + 86800*uk_133 + 8000*uk_134 + 8000*uk_135 + 58000*uk_136 + 86800*uk_137 + 8000*uk_138 + 420500*uk_139 + 6866185*uk_14 + 629300*uk_140 + 58000*uk_141 + 941780*uk_142 + 86800*uk_143 + 8000*uk_144 + 8000*uk_145 + 58000*uk_146 + 86800*uk_147 + 8000*uk_148 + 420500*uk_149 + 10275601*uk_15 + 629300*uk_150 + 58000*uk_151 + 941780*uk_152 + 86800*uk_153 + 8000*uk_154 + 3048625*uk_155 + 4562425*uk_156 + 420500*uk_157 + 6827905*uk_158 + 629300*uk_159 + 947060*uk_16 + 58000*uk_160 + 10218313*uk_161 + 941780*uk_162 + 86800*uk_163 + 8000*uk_164 + 3969*uk_17 + 5229*uk_18 + 1260*uk_19 + 63*uk_2 + 1260*uk_20 + 9135*uk_21 + 13671*uk_22 + 1260*uk_23 + 6889*uk_24 + 1660*uk_25 + 1660*uk_26 + 12035*uk_27 + 18011*uk_28 + 1660*uk_29 + 83*uk_3 + 400*uk_30 + 400*uk_31 + 2900*uk_32 + 4340*uk_33 + 400*uk_34 + 400*uk_35 + 2900*uk_36 + 4340*uk_37 + 400*uk_38 + 21025*uk_39 + 20*uk_4 + 31465*uk_40 + 2900*uk_41 + 47089*uk_42 + 4340*uk_43 + 400*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 186111448547*uk_47 + 44846132180*uk_48 + 44846132180*uk_49 + 20*uk_5 + 325134458305*uk_50 + 486580534153*uk_51 + 44846132180*uk_52 + 187944057*uk_53 + 247608837*uk_54 + 59664780*uk_55 + 59664780*uk_56 + 432569655*uk_57 + 647362863*uk_58 + 59664780*uk_59 + 145*uk_6 + 326214817*uk_60 + 78605980*uk_61 + 78605980*uk_62 + 569893355*uk_63 + 852874883*uk_64 + 78605980*uk_65 + 18941200*uk_66 + 18941200*uk_67 + 137323700*uk_68 + 205512020*uk_69 + 217*uk_7 + 18941200*uk_70 + 18941200*uk_71 + 137323700*uk_72 + 205512020*uk_73 + 18941200*uk_74 + 995596825*uk_75 + 1489962145*uk_76 + 137323700*uk_77 + 2229805417*uk_78 + 205512020*uk_79 + 20*uk_8 + 18941200*uk_80 + 250047*uk_81 + 329427*uk_82 + 79380*uk_83 + 79380*uk_84 + 575505*uk_85 + 861273*uk_86 + 79380*uk_87 + 434007*uk_88 + 104580*uk_89 + 2242306609*uk_9 + 104580*uk_90 + 758205*uk_91 + 1134693*uk_92 + 104580*uk_93 + 25200*uk_94 + 25200*uk_95 + 182700*uk_96 + 273420*uk_97 + 25200*uk_98 + 25200*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 148176*uk_100 + 218736*uk_101 + 83664*uk_102 + 1361367*uk_103 + 2009637*uk_104 + 768663*uk_105 + 2966607*uk_106 + 1134693*uk_107 + 434007*uk_108 + 6859*uk_109 + 899707*uk_11 + 29963*uk_110 + 5776*uk_111 + 53067*uk_112 + 78337*uk_113 + 29963*uk_114 + 130891*uk_115 + 25232*uk_116 + 231819*uk_117 + 342209*uk_118 + 130891*uk_119 + 3930299*uk_12 + 4864*uk_120 + 44688*uk_121 + 65968*uk_122 + 25232*uk_123 + 410571*uk_124 + 606081*uk_125 + 231819*uk_126 + 894691*uk_127 + 342209*uk_128 + 130891*uk_129 + 757648*uk_13 + 571787*uk_130 + 110224*uk_131 + 1012683*uk_132 + 1494913*uk_133 + 571787*uk_134 + 21248*uk_135 + 195216*uk_136 + 288176*uk_137 + 110224*uk_138 + 1793547*uk_139 + 6960891*uk_14 + 2647617*uk_140 + 1012683*uk_141 + 3908387*uk_142 + 1494913*uk_143 + 571787*uk_144 + 4096*uk_145 + 37632*uk_146 + 55552*uk_147 + 21248*uk_148 + 345744*uk_149 + 10275601*uk_15 + 510384*uk_150 + 195216*uk_151 + 753424*uk_152 + 288176*uk_153 + 110224*uk_154 + 3176523*uk_155 + 4689153*uk_156 + 1793547*uk_157 + 6922083*uk_158 + 2647617*uk_159 + 3930299*uk_16 + 1012683*uk_160 + 10218313*uk_161 + 3908387*uk_162 + 1494913*uk_163 + 571787*uk_164 + 3969*uk_17 + 1197*uk_18 + 5229*uk_19 + 63*uk_2 + 1008*uk_20 + 9261*uk_21 + 13671*uk_22 + 5229*uk_23 + 361*uk_24 + 1577*uk_25 + 304*uk_26 + 2793*uk_27 + 4123*uk_28 + 1577*uk_29 + 19*uk_3 + 6889*uk_30 + 1328*uk_31 + 12201*uk_32 + 18011*uk_33 + 6889*uk_34 + 256*uk_35 + 2352*uk_36 + 3472*uk_37 + 1328*uk_38 + 21609*uk_39 + 83*uk_4 + 31899*uk_40 + 12201*uk_41 + 47089*uk_42 + 18011*uk_43 + 6889*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 42603825571*uk_47 + 186111448547*uk_48 + 35876905744*uk_49 + 16*uk_5 + 329619071523*uk_50 + 486580534153*uk_51 + 186111448547*uk_52 + 187944057*uk_53 + 56681541*uk_54 + 247608837*uk_55 + 47731824*uk_56 + 438536133*uk_57 + 647362863*uk_58 + 247608837*uk_59 + 147*uk_6 + 17094433*uk_60 + 74675681*uk_61 + 14395312*uk_62 + 132256929*uk_63 + 195236419*uk_64 + 74675681*uk_65 + 326214817*uk_66 + 62884784*uk_67 + 577753953*uk_68 + 852874883*uk_69 + 217*uk_7 + 326214817*uk_70 + 12122368*uk_71 + 111374256*uk_72 + 164409616*uk_73 + 62884784*uk_74 + 1023250977*uk_75 + 1510513347*uk_76 + 577753953*uk_77 + 2229805417*uk_78 + 852874883*uk_79 + 83*uk_8 + 326214817*uk_80 + 250047*uk_81 + 75411*uk_82 + 329427*uk_83 + 63504*uk_84 + 583443*uk_85 + 861273*uk_86 + 329427*uk_87 + 22743*uk_88 + 99351*uk_89 + 2242306609*uk_9 + 19152*uk_90 + 175959*uk_91 + 259749*uk_92 + 99351*uk_93 + 434007*uk_94 + 83664*uk_95 + 768663*uk_96 + 1134693*uk_97 + 434007*uk_98 + 16128*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 187740*uk_100 + 273420*uk_101 + 23940*uk_102 + 1398663*uk_103 + 2036979*uk_104 + 178353*uk_105 + 2966607*uk_106 + 259749*uk_107 + 22743*uk_108 + 1728000*uk_109 + 5682360*uk_11 + 273600*uk_110 + 288000*uk_111 + 2145600*uk_112 + 3124800*uk_113 + 273600*uk_114 + 43320*uk_115 + 45600*uk_116 + 339720*uk_117 + 494760*uk_118 + 43320*uk_119 + 899707*uk_12 + 48000*uk_120 + 357600*uk_121 + 520800*uk_122 + 45600*uk_123 + 2664120*uk_124 + 3879960*uk_125 + 339720*uk_126 + 5650680*uk_127 + 494760*uk_128 + 43320*uk_129 + 947060*uk_13 + 6859*uk_130 + 7220*uk_131 + 53789*uk_132 + 78337*uk_133 + 6859*uk_134 + 7600*uk_135 + 56620*uk_136 + 82460*uk_137 + 7220*uk_138 + 421819*uk_139 + 7055597*uk_14 + 614327*uk_140 + 53789*uk_141 + 894691*uk_142 + 78337*uk_143 + 6859*uk_144 + 8000*uk_145 + 59600*uk_146 + 86800*uk_147 + 7600*uk_148 + 444020*uk_149 + 10275601*uk_15 + 646660*uk_150 + 56620*uk_151 + 941780*uk_152 + 82460*uk_153 + 7220*uk_154 + 3307949*uk_155 + 4817617*uk_156 + 421819*uk_157 + 7016261*uk_158 + 614327*uk_159 + 899707*uk_16 + 53789*uk_160 + 10218313*uk_161 + 894691*uk_162 + 78337*uk_163 + 6859*uk_164 + 3969*uk_17 + 7560*uk_18 + 1197*uk_19 + 63*uk_2 + 1260*uk_20 + 9387*uk_21 + 13671*uk_22 + 1197*uk_23 + 14400*uk_24 + 2280*uk_25 + 2400*uk_26 + 17880*uk_27 + 26040*uk_28 + 2280*uk_29 + 120*uk_3 + 361*uk_30 + 380*uk_31 + 2831*uk_32 + 4123*uk_33 + 361*uk_34 + 400*uk_35 + 2980*uk_36 + 4340*uk_37 + 380*uk_38 + 22201*uk_39 + 19*uk_4 + 32333*uk_40 + 2831*uk_41 + 47089*uk_42 + 4123*uk_43 + 361*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 269076793080*uk_47 + 42603825571*uk_48 + 44846132180*uk_49 + 20*uk_5 + 334103684741*uk_50 + 486580534153*uk_51 + 42603825571*uk_52 + 187944057*uk_53 + 357988680*uk_54 + 56681541*uk_55 + 59664780*uk_56 + 444502611*uk_57 + 647362863*uk_58 + 56681541*uk_59 + 149*uk_6 + 681883200*uk_60 + 107964840*uk_61 + 113647200*uk_62 + 846671640*uk_63 + 1233072120*uk_64 + 107964840*uk_65 + 17094433*uk_66 + 17994140*uk_67 + 134056343*uk_68 + 195236419*uk_69 + 217*uk_7 + 17094433*uk_70 + 18941200*uk_71 + 141111940*uk_72 + 205512020*uk_73 + 17994140*uk_74 + 1051283953*uk_75 + 1531064549*uk_76 + 134056343*uk_77 + 2229805417*uk_78 + 195236419*uk_79 + 19*uk_8 + 17094433*uk_80 + 250047*uk_81 + 476280*uk_82 + 75411*uk_83 + 79380*uk_84 + 591381*uk_85 + 861273*uk_86 + 75411*uk_87 + 907200*uk_88 + 143640*uk_89 + 2242306609*uk_9 + 151200*uk_90 + 1126440*uk_91 + 1640520*uk_92 + 143640*uk_93 + 22743*uk_94 + 23940*uk_95 + 178353*uk_96 + 259749*uk_97 + 22743*uk_98 + 25200*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 152208*uk_100 + 218736*uk_101 + 120960*uk_102 + 1436463*uk_103 + 2064321*uk_104 + 1141560*uk_105 + 2966607*uk_106 + 1640520*uk_107 + 907200*uk_108 + 729000*uk_109 + 4261770*uk_11 + 972000*uk_110 + 129600*uk_111 + 1223100*uk_112 + 1757700*uk_113 + 972000*uk_114 + 1296000*uk_115 + 172800*uk_116 + 1630800*uk_117 + 2343600*uk_118 + 1296000*uk_119 + 5682360*uk_12 + 23040*uk_120 + 217440*uk_121 + 312480*uk_122 + 172800*uk_123 + 2052090*uk_124 + 2949030*uk_125 + 1630800*uk_126 + 4238010*uk_127 + 2343600*uk_128 + 1296000*uk_129 + 757648*uk_13 + 1728000*uk_130 + 230400*uk_131 + 2174400*uk_132 + 3124800*uk_133 + 1728000*uk_134 + 30720*uk_135 + 289920*uk_136 + 416640*uk_137 + 230400*uk_138 + 2736120*uk_139 + 7150303*uk_14 + 3932040*uk_140 + 2174400*uk_141 + 5650680*uk_142 + 3124800*uk_143 + 1728000*uk_144 + 4096*uk_145 + 38656*uk_146 + 55552*uk_147 + 30720*uk_148 + 364816*uk_149 + 10275601*uk_15 + 524272*uk_150 + 289920*uk_151 + 753424*uk_152 + 416640*uk_153 + 230400*uk_154 + 3442951*uk_155 + 4947817*uk_156 + 2736120*uk_157 + 7110439*uk_158 + 3932040*uk_159 + 5682360*uk_16 + 2174400*uk_160 + 10218313*uk_161 + 5650680*uk_162 + 3124800*uk_163 + 1728000*uk_164 + 3969*uk_17 + 5670*uk_18 + 7560*uk_19 + 63*uk_2 + 1008*uk_20 + 9513*uk_21 + 13671*uk_22 + 7560*uk_23 + 8100*uk_24 + 10800*uk_25 + 1440*uk_26 + 13590*uk_27 + 19530*uk_28 + 10800*uk_29 + 90*uk_3 + 14400*uk_30 + 1920*uk_31 + 18120*uk_32 + 26040*uk_33 + 14400*uk_34 + 256*uk_35 + 2416*uk_36 + 3472*uk_37 + 1920*uk_38 + 22801*uk_39 + 120*uk_4 + 32767*uk_40 + 18120*uk_41 + 47089*uk_42 + 26040*uk_43 + 14400*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 201807594810*uk_47 + 269076793080*uk_48 + 35876905744*uk_49 + 16*uk_5 + 338588297959*uk_50 + 486580534153*uk_51 + 269076793080*uk_52 + 187944057*uk_53 + 268491510*uk_54 + 357988680*uk_55 + 47731824*uk_56 + 450469089*uk_57 + 647362863*uk_58 + 357988680*uk_59 + 151*uk_6 + 383559300*uk_60 + 511412400*uk_61 + 68188320*uk_62 + 643527270*uk_63 + 924804090*uk_64 + 511412400*uk_65 + 681883200*uk_66 + 90917760*uk_67 + 858036360*uk_68 + 1233072120*uk_69 + 217*uk_7 + 681883200*uk_70 + 12122368*uk_71 + 114404848*uk_72 + 164409616*uk_73 + 90917760*uk_74 + 1079695753*uk_75 + 1551615751*uk_76 + 858036360*uk_77 + 2229805417*uk_78 + 1233072120*uk_79 + 120*uk_8 + 681883200*uk_80 + 250047*uk_81 + 357210*uk_82 + 476280*uk_83 + 63504*uk_84 + 599319*uk_85 + 861273*uk_86 + 476280*uk_87 + 510300*uk_88 + 680400*uk_89 + 2242306609*uk_9 + 90720*uk_90 + 856170*uk_91 + 1230390*uk_92 + 680400*uk_93 + 907200*uk_94 + 120960*uk_95 + 1141560*uk_96 + 1640520*uk_97 + 907200*uk_98 + 16128*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 154224*uk_100 + 218736*uk_101 + 90720*uk_102 + 1474767*uk_103 + 2091663*uk_104 + 867510*uk_105 + 2966607*uk_106 + 1230390*uk_107 + 510300*uk_108 + 438976*uk_109 + 3598828*uk_11 + 519840*uk_110 + 92416*uk_111 + 883728*uk_112 + 1253392*uk_113 + 519840*uk_114 + 615600*uk_115 + 109440*uk_116 + 1046520*uk_117 + 1484280*uk_118 + 615600*uk_119 + 4261770*uk_12 + 19456*uk_120 + 186048*uk_121 + 263872*uk_122 + 109440*uk_123 + 1779084*uk_124 + 2523276*uk_125 + 1046520*uk_126 + 3578764*uk_127 + 1484280*uk_128 + 615600*uk_129 + 757648*uk_13 + 729000*uk_130 + 129600*uk_131 + 1239300*uk_132 + 1757700*uk_133 + 729000*uk_134 + 23040*uk_135 + 220320*uk_136 + 312480*uk_137 + 129600*uk_138 + 2106810*uk_139 + 7245009*uk_14 + 2988090*uk_140 + 1239300*uk_141 + 4238010*uk_142 + 1757700*uk_143 + 729000*uk_144 + 4096*uk_145 + 39168*uk_146 + 55552*uk_147 + 23040*uk_148 + 374544*uk_149 + 10275601*uk_15 + 531216*uk_150 + 220320*uk_151 + 753424*uk_152 + 312480*uk_153 + 129600*uk_154 + 3581577*uk_155 + 5079753*uk_156 + 2106810*uk_157 + 7204617*uk_158 + 2988090*uk_159 + 4261770*uk_16 + 1239300*uk_160 + 10218313*uk_161 + 4238010*uk_162 + 1757700*uk_163 + 729000*uk_164 + 3969*uk_17 + 4788*uk_18 + 5670*uk_19 + 63*uk_2 + 1008*uk_20 + 9639*uk_21 + 13671*uk_22 + 5670*uk_23 + 5776*uk_24 + 6840*uk_25 + 1216*uk_26 + 11628*uk_27 + 16492*uk_28 + 6840*uk_29 + 76*uk_3 + 8100*uk_30 + 1440*uk_31 + 13770*uk_32 + 19530*uk_33 + 8100*uk_34 + 256*uk_35 + 2448*uk_36 + 3472*uk_37 + 1440*uk_38 + 23409*uk_39 + 90*uk_4 + 33201*uk_40 + 13770*uk_41 + 47089*uk_42 + 19530*uk_43 + 8100*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 170415302284*uk_47 + 201807594810*uk_48 + 35876905744*uk_49 + 16*uk_5 + 343072911177*uk_50 + 486580534153*uk_51 + 201807594810*uk_52 + 187944057*uk_53 + 226726164*uk_54 + 268491510*uk_55 + 47731824*uk_56 + 456435567*uk_57 + 647362863*uk_58 + 268491510*uk_59 + 153*uk_6 + 273510928*uk_60 + 323894520*uk_61 + 57581248*uk_62 + 550620684*uk_63 + 780945676*uk_64 + 323894520*uk_65 + 383559300*uk_66 + 68188320*uk_67 + 652050810*uk_68 + 924804090*uk_69 + 217*uk_7 + 383559300*uk_70 + 12122368*uk_71 + 115920144*uk_72 + 164409616*uk_73 + 68188320*uk_74 + 1108486377*uk_75 + 1572166953*uk_76 + 652050810*uk_77 + 2229805417*uk_78 + 924804090*uk_79 + 90*uk_8 + 383559300*uk_80 + 250047*uk_81 + 301644*uk_82 + 357210*uk_83 + 63504*uk_84 + 607257*uk_85 + 861273*uk_86 + 357210*uk_87 + 363888*uk_88 + 430920*uk_89 + 2242306609*uk_9 + 76608*uk_90 + 732564*uk_91 + 1038996*uk_92 + 430920*uk_93 + 510300*uk_94 + 90720*uk_95 + 867510*uk_96 + 1230390*uk_97 + 510300*uk_98 + 16128*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 156240*uk_100 + 218736*uk_101 + 76608*uk_102 + 1513575*uk_103 + 2119005*uk_104 + 742140*uk_105 + 2966607*uk_106 + 1038996*uk_107 + 363888*uk_108 + 474552*uk_109 + 3693534*uk_11 + 462384*uk_110 + 97344*uk_111 + 943020*uk_112 + 1320228*uk_113 + 462384*uk_114 + 450528*uk_115 + 94848*uk_116 + 918840*uk_117 + 1286376*uk_118 + 450528*uk_119 + 3598828*uk_12 + 19968*uk_120 + 193440*uk_121 + 270816*uk_122 + 94848*uk_123 + 1873950*uk_124 + 2623530*uk_125 + 918840*uk_126 + 3672942*uk_127 + 1286376*uk_128 + 450528*uk_129 + 757648*uk_13 + 438976*uk_130 + 92416*uk_131 + 895280*uk_132 + 1253392*uk_133 + 438976*uk_134 + 19456*uk_135 + 188480*uk_136 + 263872*uk_137 + 92416*uk_138 + 1825900*uk_139 + 7339715*uk_14 + 2556260*uk_140 + 895280*uk_141 + 3578764*uk_142 + 1253392*uk_143 + 438976*uk_144 + 4096*uk_145 + 39680*uk_146 + 55552*uk_147 + 19456*uk_148 + 384400*uk_149 + 10275601*uk_15 + 538160*uk_150 + 188480*uk_151 + 753424*uk_152 + 263872*uk_153 + 92416*uk_154 + 3723875*uk_155 + 5213425*uk_156 + 1825900*uk_157 + 7298795*uk_158 + 2556260*uk_159 + 3598828*uk_16 + 895280*uk_160 + 10218313*uk_161 + 3578764*uk_162 + 1253392*uk_163 + 438976*uk_164 + 3969*uk_17 + 4914*uk_18 + 4788*uk_19 + 63*uk_2 + 1008*uk_20 + 9765*uk_21 + 13671*uk_22 + 4788*uk_23 + 6084*uk_24 + 5928*uk_25 + 1248*uk_26 + 12090*uk_27 + 16926*uk_28 + 5928*uk_29 + 78*uk_3 + 5776*uk_30 + 1216*uk_31 + 11780*uk_32 + 16492*uk_33 + 5776*uk_34 + 256*uk_35 + 2480*uk_36 + 3472*uk_37 + 1216*uk_38 + 24025*uk_39 + 76*uk_4 + 33635*uk_40 + 11780*uk_41 + 47089*uk_42 + 16492*uk_43 + 5776*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 174899915502*uk_47 + 170415302284*uk_48 + 35876905744*uk_49 + 16*uk_5 + 347557524395*uk_50 + 486580534153*uk_51 + 170415302284*uk_52 + 187944057*uk_53 + 232692642*uk_54 + 226726164*uk_55 + 47731824*uk_56 + 462402045*uk_57 + 647362863*uk_58 + 226726164*uk_59 + 155*uk_6 + 288095652*uk_60 + 280708584*uk_61 + 59096544*uk_62 + 572497770*uk_63 + 801496878*uk_64 + 280708584*uk_65 + 273510928*uk_66 + 57581248*uk_67 + 557818340*uk_68 + 780945676*uk_69 + 217*uk_7 + 273510928*uk_70 + 12122368*uk_71 + 117435440*uk_72 + 164409616*uk_73 + 57581248*uk_74 + 1137655825*uk_75 + 1592718155*uk_76 + 557818340*uk_77 + 2229805417*uk_78 + 780945676*uk_79 + 76*uk_8 + 273510928*uk_80 + 250047*uk_81 + 309582*uk_82 + 301644*uk_83 + 63504*uk_84 + 615195*uk_85 + 861273*uk_86 + 301644*uk_87 + 383292*uk_88 + 373464*uk_89 + 2242306609*uk_9 + 78624*uk_90 + 761670*uk_91 + 1066338*uk_92 + 373464*uk_93 + 363888*uk_94 + 76608*uk_95 + 742140*uk_96 + 1038996*uk_97 + 363888*uk_98 + 16128*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 158256*uk_100 + 218736*uk_101 + 78624*uk_102 + 1552887*uk_103 + 2146347*uk_104 + 771498*uk_105 + 2966607*uk_106 + 1066338*uk_107 + 383292*uk_108 + 884736*uk_109 + 4545888*uk_11 + 718848*uk_110 + 147456*uk_111 + 1446912*uk_112 + 1999872*uk_113 + 718848*uk_114 + 584064*uk_115 + 119808*uk_116 + 1175616*uk_117 + 1624896*uk_118 + 584064*uk_119 + 3693534*uk_12 + 24576*uk_120 + 241152*uk_121 + 333312*uk_122 + 119808*uk_123 + 2366304*uk_124 + 3270624*uk_125 + 1175616*uk_126 + 4520544*uk_127 + 1624896*uk_128 + 584064*uk_129 + 757648*uk_13 + 474552*uk_130 + 97344*uk_131 + 955188*uk_132 + 1320228*uk_133 + 474552*uk_134 + 19968*uk_135 + 195936*uk_136 + 270816*uk_137 + 97344*uk_138 + 1922622*uk_139 + 7434421*uk_14 + 2657382*uk_140 + 955188*uk_141 + 3672942*uk_142 + 1320228*uk_143 + 474552*uk_144 + 4096*uk_145 + 40192*uk_146 + 55552*uk_147 + 19968*uk_148 + 394384*uk_149 + 10275601*uk_15 + 545104*uk_150 + 195936*uk_151 + 753424*uk_152 + 270816*uk_153 + 97344*uk_154 + 3869893*uk_155 + 5348833*uk_156 + 1922622*uk_157 + 7392973*uk_158 + 2657382*uk_159 + 3693534*uk_16 + 955188*uk_160 + 10218313*uk_161 + 3672942*uk_162 + 1320228*uk_163 + 474552*uk_164 + 3969*uk_17 + 6048*uk_18 + 4914*uk_19 + 63*uk_2 + 1008*uk_20 + 9891*uk_21 + 13671*uk_22 + 4914*uk_23 + 9216*uk_24 + 7488*uk_25 + 1536*uk_26 + 15072*uk_27 + 20832*uk_28 + 7488*uk_29 + 96*uk_3 + 6084*uk_30 + 1248*uk_31 + 12246*uk_32 + 16926*uk_33 + 6084*uk_34 + 256*uk_35 + 2512*uk_36 + 3472*uk_37 + 1248*uk_38 + 24649*uk_39 + 78*uk_4 + 34069*uk_40 + 12246*uk_41 + 47089*uk_42 + 16926*uk_43 + 6084*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 215261434464*uk_47 + 174899915502*uk_48 + 35876905744*uk_49 + 16*uk_5 + 352042137613*uk_50 + 486580534153*uk_51 + 174899915502*uk_52 + 187944057*uk_53 + 286390944*uk_54 + 232692642*uk_55 + 47731824*uk_56 + 468368523*uk_57 + 647362863*uk_58 + 232692642*uk_59 + 157*uk_6 + 436405248*uk_60 + 354579264*uk_61 + 72734208*uk_62 + 713704416*uk_63 + 986457696*uk_64 + 354579264*uk_65 + 288095652*uk_66 + 59096544*uk_67 + 579884838*uk_68 + 801496878*uk_69 + 217*uk_7 + 288095652*uk_70 + 12122368*uk_71 + 118950736*uk_72 + 164409616*uk_73 + 59096544*uk_74 + 1167204097*uk_75 + 1613269357*uk_76 + 579884838*uk_77 + 2229805417*uk_78 + 801496878*uk_79 + 78*uk_8 + 288095652*uk_80 + 250047*uk_81 + 381024*uk_82 + 309582*uk_83 + 63504*uk_84 + 623133*uk_85 + 861273*uk_86 + 309582*uk_87 + 580608*uk_88 + 471744*uk_89 + 2242306609*uk_9 + 96768*uk_90 + 949536*uk_91 + 1312416*uk_92 + 471744*uk_93 + 383292*uk_94 + 78624*uk_95 + 771498*uk_96 + 1066338*uk_97 + 383292*uk_98 + 16128*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 160272*uk_100 + 218736*uk_101 + 96768*uk_102 + 1592703*uk_103 + 2173689*uk_104 + 961632*uk_105 + 2966607*uk_106 + 1312416*uk_107 + 580608*uk_108 + 2197000*uk_109 + 6155890*uk_11 + 1622400*uk_110 + 270400*uk_111 + 2687100*uk_112 + 3667300*uk_113 + 1622400*uk_114 + 1198080*uk_115 + 199680*uk_116 + 1984320*uk_117 + 2708160*uk_118 + 1198080*uk_119 + 4545888*uk_12 + 33280*uk_120 + 330720*uk_121 + 451360*uk_122 + 199680*uk_123 + 3286530*uk_124 + 4485390*uk_125 + 1984320*uk_126 + 6121570*uk_127 + 2708160*uk_128 + 1198080*uk_129 + 757648*uk_13 + 884736*uk_130 + 147456*uk_131 + 1465344*uk_132 + 1999872*uk_133 + 884736*uk_134 + 24576*uk_135 + 244224*uk_136 + 333312*uk_137 + 147456*uk_138 + 2426976*uk_139 + 7529127*uk_14 + 3312288*uk_140 + 1465344*uk_141 + 4520544*uk_142 + 1999872*uk_143 + 884736*uk_144 + 4096*uk_145 + 40704*uk_146 + 55552*uk_147 + 24576*uk_148 + 404496*uk_149 + 10275601*uk_15 + 552048*uk_150 + 244224*uk_151 + 753424*uk_152 + 333312*uk_153 + 147456*uk_154 + 4019679*uk_155 + 5485977*uk_156 + 2426976*uk_157 + 7487151*uk_158 + 3312288*uk_159 + 4545888*uk_16 + 1465344*uk_160 + 10218313*uk_161 + 4520544*uk_162 + 1999872*uk_163 + 884736*uk_164 + 3969*uk_17 + 8190*uk_18 + 6048*uk_19 + 63*uk_2 + 1008*uk_20 + 10017*uk_21 + 13671*uk_22 + 6048*uk_23 + 16900*uk_24 + 12480*uk_25 + 2080*uk_26 + 20670*uk_27 + 28210*uk_28 + 12480*uk_29 + 130*uk_3 + 9216*uk_30 + 1536*uk_31 + 15264*uk_32 + 20832*uk_33 + 9216*uk_34 + 256*uk_35 + 2544*uk_36 + 3472*uk_37 + 1536*uk_38 + 25281*uk_39 + 96*uk_4 + 34503*uk_40 + 15264*uk_41 + 47089*uk_42 + 20832*uk_43 + 9216*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 291499859170*uk_47 + 215261434464*uk_48 + 35876905744*uk_49 + 16*uk_5 + 356526750831*uk_50 + 486580534153*uk_51 + 215261434464*uk_52 + 187944057*uk_53 + 387821070*uk_54 + 286390944*uk_55 + 47731824*uk_56 + 474335001*uk_57 + 647362863*uk_58 + 286390944*uk_59 + 159*uk_6 + 800265700*uk_60 + 590965440*uk_61 + 98494240*uk_62 + 978786510*uk_63 + 1335828130*uk_64 + 590965440*uk_65 + 436405248*uk_66 + 72734208*uk_67 + 722796192*uk_68 + 986457696*uk_69 + 217*uk_7 + 436405248*uk_70 + 12122368*uk_71 + 120466032*uk_72 + 164409616*uk_73 + 72734208*uk_74 + 1197131193*uk_75 + 1633820559*uk_76 + 722796192*uk_77 + 2229805417*uk_78 + 986457696*uk_79 + 96*uk_8 + 436405248*uk_80 + 250047*uk_81 + 515970*uk_82 + 381024*uk_83 + 63504*uk_84 + 631071*uk_85 + 861273*uk_86 + 381024*uk_87 + 1064700*uk_88 + 786240*uk_89 + 2242306609*uk_9 + 131040*uk_90 + 1302210*uk_91 + 1777230*uk_92 + 786240*uk_93 + 580608*uk_94 + 96768*uk_95 + 961632*uk_96 + 1312416*uk_97 + 580608*uk_98 + 16128*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 121716*uk_100 + 164052*uk_101 + 98280*uk_102 + 1633023*uk_103 + 2201031*uk_104 + 1318590*uk_105 + 2966607*uk_106 + 1777230*uk_107 + 1064700*uk_108 + 6859*uk_109 + 899707*uk_11 + 46930*uk_110 + 4332*uk_111 + 58121*uk_112 + 78337*uk_113 + 46930*uk_114 + 321100*uk_115 + 29640*uk_116 + 397670*uk_117 + 535990*uk_118 + 321100*uk_119 + 6155890*uk_12 + 2736*uk_120 + 36708*uk_121 + 49476*uk_122 + 29640*uk_123 + 492499*uk_124 + 663803*uk_125 + 397670*uk_126 + 894691*uk_127 + 535990*uk_128 + 321100*uk_129 + 568236*uk_13 + 2197000*uk_130 + 202800*uk_131 + 2720900*uk_132 + 3667300*uk_133 + 2197000*uk_134 + 18720*uk_135 + 251160*uk_136 + 338520*uk_137 + 202800*uk_138 + 3369730*uk_139 + 7623833*uk_14 + 4541810*uk_140 + 2720900*uk_141 + 6121570*uk_142 + 3667300*uk_143 + 2197000*uk_144 + 1728*uk_145 + 23184*uk_146 + 31248*uk_147 + 18720*uk_148 + 311052*uk_149 + 10275601*uk_15 + 419244*uk_150 + 251160*uk_151 + 565068*uk_152 + 338520*uk_153 + 202800*uk_154 + 4173281*uk_155 + 5624857*uk_156 + 3369730*uk_157 + 7581329*uk_158 + 4541810*uk_159 + 6155890*uk_16 + 2720900*uk_160 + 10218313*uk_161 + 6121570*uk_162 + 3667300*uk_163 + 2197000*uk_164 + 3969*uk_17 + 1197*uk_18 + 8190*uk_19 + 63*uk_2 + 756*uk_20 + 10143*uk_21 + 13671*uk_22 + 8190*uk_23 + 361*uk_24 + 2470*uk_25 + 228*uk_26 + 3059*uk_27 + 4123*uk_28 + 2470*uk_29 + 19*uk_3 + 16900*uk_30 + 1560*uk_31 + 20930*uk_32 + 28210*uk_33 + 16900*uk_34 + 144*uk_35 + 1932*uk_36 + 2604*uk_37 + 1560*uk_38 + 25921*uk_39 + 130*uk_4 + 34937*uk_40 + 20930*uk_41 + 47089*uk_42 + 28210*uk_43 + 16900*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 42603825571*uk_47 + 291499859170*uk_48 + 26907679308*uk_49 + 12*uk_5 + 361011364049*uk_50 + 486580534153*uk_51 + 291499859170*uk_52 + 187944057*uk_53 + 56681541*uk_54 + 387821070*uk_55 + 35798868*uk_56 + 480301479*uk_57 + 647362863*uk_58 + 387821070*uk_59 + 161*uk_6 + 17094433*uk_60 + 116961910*uk_61 + 10796484*uk_62 + 144852827*uk_63 + 195236419*uk_64 + 116961910*uk_65 + 800265700*uk_66 + 73870680*uk_67 + 991098290*uk_68 + 1335828130*uk_69 + 217*uk_7 + 800265700*uk_70 + 6818832*uk_71 + 91485996*uk_72 + 123307212*uk_73 + 73870680*uk_74 + 1227437113*uk_75 + 1654371761*uk_76 + 991098290*uk_77 + 2229805417*uk_78 + 1335828130*uk_79 + 130*uk_8 + 800265700*uk_80 + 250047*uk_81 + 75411*uk_82 + 515970*uk_83 + 47628*uk_84 + 639009*uk_85 + 861273*uk_86 + 515970*uk_87 + 22743*uk_88 + 155610*uk_89 + 2242306609*uk_9 + 14364*uk_90 + 192717*uk_91 + 259749*uk_92 + 155610*uk_93 + 1064700*uk_94 + 98280*uk_95 + 1318590*uk_96 + 1777230*uk_97 + 1064700*uk_98 + 9072*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 164304*uk_100 + 218736*uk_101 + 19152*uk_102 + 1673847*uk_103 + 2228373*uk_104 + 195111*uk_105 + 2966607*uk_106 + 259749*uk_107 + 22743*uk_108 + 571787*uk_109 + 3930299*uk_11 + 130891*uk_110 + 110224*uk_111 + 1122907*uk_112 + 1494913*uk_113 + 130891*uk_114 + 29963*uk_115 + 25232*uk_116 + 257051*uk_117 + 342209*uk_118 + 29963*uk_119 + 899707*uk_12 + 21248*uk_120 + 216464*uk_121 + 288176*uk_122 + 25232*uk_123 + 2205227*uk_124 + 2935793*uk_125 + 257051*uk_126 + 3908387*uk_127 + 342209*uk_128 + 29963*uk_129 + 757648*uk_13 + 6859*uk_130 + 5776*uk_131 + 58843*uk_132 + 78337*uk_133 + 6859*uk_134 + 4864*uk_135 + 49552*uk_136 + 65968*uk_137 + 5776*uk_138 + 504811*uk_139 + 7718539*uk_14 + 672049*uk_140 + 58843*uk_141 + 894691*uk_142 + 78337*uk_143 + 6859*uk_144 + 4096*uk_145 + 41728*uk_146 + 55552*uk_147 + 4864*uk_148 + 425104*uk_149 + 10275601*uk_15 + 565936*uk_150 + 49552*uk_151 + 753424*uk_152 + 65968*uk_153 + 5776*uk_154 + 4330747*uk_155 + 5765473*uk_156 + 504811*uk_157 + 7675507*uk_158 + 672049*uk_159 + 899707*uk_16 + 58843*uk_160 + 10218313*uk_161 + 894691*uk_162 + 78337*uk_163 + 6859*uk_164 + 3969*uk_17 + 5229*uk_18 + 1197*uk_19 + 63*uk_2 + 1008*uk_20 + 10269*uk_21 + 13671*uk_22 + 1197*uk_23 + 6889*uk_24 + 1577*uk_25 + 1328*uk_26 + 13529*uk_27 + 18011*uk_28 + 1577*uk_29 + 83*uk_3 + 361*uk_30 + 304*uk_31 + 3097*uk_32 + 4123*uk_33 + 361*uk_34 + 256*uk_35 + 2608*uk_36 + 3472*uk_37 + 304*uk_38 + 26569*uk_39 + 19*uk_4 + 35371*uk_40 + 3097*uk_41 + 47089*uk_42 + 4123*uk_43 + 361*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 186111448547*uk_47 + 42603825571*uk_48 + 35876905744*uk_49 + 16*uk_5 + 365495977267*uk_50 + 486580534153*uk_51 + 42603825571*uk_52 + 187944057*uk_53 + 247608837*uk_54 + 56681541*uk_55 + 47731824*uk_56 + 486267957*uk_57 + 647362863*uk_58 + 56681541*uk_59 + 163*uk_6 + 326214817*uk_60 + 74675681*uk_61 + 62884784*uk_62 + 640638737*uk_63 + 852874883*uk_64 + 74675681*uk_65 + 17094433*uk_66 + 14395312*uk_67 + 146652241*uk_68 + 195236419*uk_69 + 217*uk_7 + 17094433*uk_70 + 12122368*uk_71 + 123496624*uk_72 + 164409616*uk_73 + 14395312*uk_74 + 1258121857*uk_75 + 1674922963*uk_76 + 146652241*uk_77 + 2229805417*uk_78 + 195236419*uk_79 + 19*uk_8 + 17094433*uk_80 + 250047*uk_81 + 329427*uk_82 + 75411*uk_83 + 63504*uk_84 + 646947*uk_85 + 861273*uk_86 + 75411*uk_87 + 434007*uk_88 + 99351*uk_89 + 2242306609*uk_9 + 83664*uk_90 + 852327*uk_91 + 1134693*uk_92 + 99351*uk_93 + 22743*uk_94 + 19152*uk_95 + 195111*uk_96 + 259749*uk_97 + 22743*uk_98 + 16128*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 166320*uk_100 + 218736*uk_101 + 83664*uk_102 + 1715175*uk_103 + 2255715*uk_104 + 862785*uk_105 + 2966607*uk_106 + 1134693*uk_107 + 434007*uk_108 + 4330747*uk_109 + 7718539*uk_11 + 2205227*uk_110 + 425104*uk_111 + 4383885*uk_112 + 5765473*uk_113 + 2205227*uk_114 + 1122907*uk_115 + 216464*uk_116 + 2232285*uk_117 + 2935793*uk_118 + 1122907*uk_119 + 3930299*uk_12 + 41728*uk_120 + 430320*uk_121 + 565936*uk_122 + 216464*uk_123 + 4437675*uk_124 + 5836215*uk_125 + 2232285*uk_126 + 7675507*uk_127 + 2935793*uk_128 + 1122907*uk_129 + 757648*uk_13 + 571787*uk_130 + 110224*uk_131 + 1136685*uk_132 + 1494913*uk_133 + 571787*uk_134 + 21248*uk_135 + 219120*uk_136 + 288176*uk_137 + 110224*uk_138 + 2259675*uk_139 + 7813245*uk_14 + 2971815*uk_140 + 1136685*uk_141 + 3908387*uk_142 + 1494913*uk_143 + 571787*uk_144 + 4096*uk_145 + 42240*uk_146 + 55552*uk_147 + 21248*uk_148 + 435600*uk_149 + 10275601*uk_15 + 572880*uk_150 + 219120*uk_151 + 753424*uk_152 + 288176*uk_153 + 110224*uk_154 + 4492125*uk_155 + 5907825*uk_156 + 2259675*uk_157 + 7769685*uk_158 + 2971815*uk_159 + 3930299*uk_16 + 1136685*uk_160 + 10218313*uk_161 + 3908387*uk_162 + 1494913*uk_163 + 571787*uk_164 + 3969*uk_17 + 10269*uk_18 + 5229*uk_19 + 63*uk_2 + 1008*uk_20 + 10395*uk_21 + 13671*uk_22 + 5229*uk_23 + 26569*uk_24 + 13529*uk_25 + 2608*uk_26 + 26895*uk_27 + 35371*uk_28 + 13529*uk_29 + 163*uk_3 + 6889*uk_30 + 1328*uk_31 + 13695*uk_32 + 18011*uk_33 + 6889*uk_34 + 256*uk_35 + 2640*uk_36 + 3472*uk_37 + 1328*uk_38 + 27225*uk_39 + 83*uk_4 + 35805*uk_40 + 13695*uk_41 + 47089*uk_42 + 18011*uk_43 + 6889*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 365495977267*uk_47 + 186111448547*uk_48 + 35876905744*uk_49 + 16*uk_5 + 369980590485*uk_50 + 486580534153*uk_51 + 186111448547*uk_52 + 187944057*uk_53 + 486267957*uk_54 + 247608837*uk_55 + 47731824*uk_56 + 492234435*uk_57 + 647362863*uk_58 + 247608837*uk_59 + 165*uk_6 + 1258121857*uk_60 + 640638737*uk_61 + 123496624*uk_62 + 1273558935*uk_63 + 1674922963*uk_64 + 640638737*uk_65 + 326214817*uk_66 + 62884784*uk_67 + 648499335*uk_68 + 852874883*uk_69 + 217*uk_7 + 326214817*uk_70 + 12122368*uk_71 + 125011920*uk_72 + 164409616*uk_73 + 62884784*uk_74 + 1289185425*uk_75 + 1695474165*uk_76 + 648499335*uk_77 + 2229805417*uk_78 + 852874883*uk_79 + 83*uk_8 + 326214817*uk_80 + 250047*uk_81 + 646947*uk_82 + 329427*uk_83 + 63504*uk_84 + 654885*uk_85 + 861273*uk_86 + 329427*uk_87 + 1673847*uk_88 + 852327*uk_89 + 2242306609*uk_9 + 164304*uk_90 + 1694385*uk_91 + 2228373*uk_92 + 852327*uk_93 + 434007*uk_94 + 83664*uk_95 + 862785*uk_96 + 1134693*uk_97 + 434007*uk_98 + 16128*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 126252*uk_100 + 164052*uk_101 + 123228*uk_102 + 1757007*uk_103 + 2283057*uk_104 + 1714923*uk_105 + 2966607*uk_106 + 2228373*uk_107 + 1673847*uk_108 + 778688*uk_109 + 4356476*uk_11 + 1379632*uk_110 + 101568*uk_111 + 1413488*uk_112 + 1836688*uk_113 + 1379632*uk_114 + 2444348*uk_115 + 179952*uk_116 + 2504332*uk_117 + 3254132*uk_118 + 2444348*uk_119 + 7718539*uk_12 + 13248*uk_120 + 184368*uk_121 + 239568*uk_122 + 179952*uk_123 + 2565788*uk_124 + 3333988*uk_125 + 2504332*uk_126 + 4332188*uk_127 + 3254132*uk_128 + 2444348*uk_129 + 568236*uk_13 + 4330747*uk_130 + 318828*uk_131 + 4437023*uk_132 + 5765473*uk_133 + 4330747*uk_134 + 23472*uk_135 + 326652*uk_136 + 424452*uk_137 + 318828*uk_138 + 4545907*uk_139 + 7907951*uk_14 + 5906957*uk_140 + 4437023*uk_141 + 7675507*uk_142 + 5765473*uk_143 + 4330747*uk_144 + 1728*uk_145 + 24048*uk_146 + 31248*uk_147 + 23472*uk_148 + 334668*uk_149 + 10275601*uk_15 + 434868*uk_150 + 326652*uk_151 + 565068*uk_152 + 424452*uk_153 + 318828*uk_154 + 4657463*uk_155 + 6051913*uk_156 + 4545907*uk_157 + 7863863*uk_158 + 5906957*uk_159 + 7718539*uk_16 + 4437023*uk_160 + 10218313*uk_161 + 7675507*uk_162 + 5765473*uk_163 + 4330747*uk_164 + 3969*uk_17 + 5796*uk_18 + 10269*uk_19 + 63*uk_2 + 756*uk_20 + 10521*uk_21 + 13671*uk_22 + 10269*uk_23 + 8464*uk_24 + 14996*uk_25 + 1104*uk_26 + 15364*uk_27 + 19964*uk_28 + 14996*uk_29 + 92*uk_3 + 26569*uk_30 + 1956*uk_31 + 27221*uk_32 + 35371*uk_33 + 26569*uk_34 + 144*uk_35 + 2004*uk_36 + 2604*uk_37 + 1956*uk_38 + 27889*uk_39 + 163*uk_4 + 36239*uk_40 + 27221*uk_41 + 47089*uk_42 + 35371*uk_43 + 26569*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 206292208028*uk_47 + 365495977267*uk_48 + 26907679308*uk_49 + 12*uk_5 + 374465203703*uk_50 + 486580534153*uk_51 + 365495977267*uk_52 + 187944057*uk_53 + 274457988*uk_54 + 486267957*uk_55 + 35798868*uk_56 + 498200913*uk_57 + 647362863*uk_58 + 486267957*uk_59 + 167*uk_6 + 400795792*uk_60 + 710105588*uk_61 + 52277712*uk_62 + 727531492*uk_63 + 945355292*uk_64 + 710105588*uk_65 + 1258121857*uk_66 + 92622468*uk_67 + 1288996013*uk_68 + 1674922963*uk_69 + 217*uk_7 + 1258121857*uk_70 + 6818832*uk_71 + 94895412*uk_72 + 123307212*uk_73 + 92622468*uk_74 + 1320627817*uk_75 + 1716025367*uk_76 + 1288996013*uk_77 + 2229805417*uk_78 + 1674922963*uk_79 + 163*uk_8 + 1258121857*uk_80 + 250047*uk_81 + 365148*uk_82 + 646947*uk_83 + 47628*uk_84 + 662823*uk_85 + 861273*uk_86 + 646947*uk_87 + 533232*uk_88 + 944748*uk_89 + 2242306609*uk_9 + 69552*uk_90 + 967932*uk_91 + 1257732*uk_92 + 944748*uk_93 + 1673847*uk_94 + 123228*uk_95 + 1714923*uk_96 + 2228373*uk_97 + 1673847*uk_98 + 9072*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 127764*uk_100 + 164052*uk_101 + 69552*uk_102 + 1799343*uk_103 + 2310399*uk_104 + 979524*uk_105 + 2966607*uk_106 + 1257732*uk_107 + 533232*uk_108 + 35937*uk_109 + 1562649*uk_11 + 100188*uk_110 + 13068*uk_111 + 184041*uk_112 + 236313*uk_113 + 100188*uk_114 + 279312*uk_115 + 36432*uk_116 + 513084*uk_117 + 658812*uk_118 + 279312*uk_119 + 4356476*uk_12 + 4752*uk_120 + 66924*uk_121 + 85932*uk_122 + 36432*uk_123 + 942513*uk_124 + 1210209*uk_125 + 513084*uk_126 + 1553937*uk_127 + 658812*uk_128 + 279312*uk_129 + 568236*uk_13 + 778688*uk_130 + 101568*uk_131 + 1430416*uk_132 + 1836688*uk_133 + 778688*uk_134 + 13248*uk_135 + 186576*uk_136 + 239568*uk_137 + 101568*uk_138 + 2627612*uk_139 + 8002657*uk_14 + 3373916*uk_140 + 1430416*uk_141 + 4332188*uk_142 + 1836688*uk_143 + 778688*uk_144 + 1728*uk_145 + 24336*uk_146 + 31248*uk_147 + 13248*uk_148 + 342732*uk_149 + 10275601*uk_15 + 440076*uk_150 + 186576*uk_151 + 565068*uk_152 + 239568*uk_153 + 101568*uk_154 + 4826809*uk_155 + 6197737*uk_156 + 2627612*uk_157 + 7958041*uk_158 + 3373916*uk_159 + 4356476*uk_16 + 1430416*uk_160 + 10218313*uk_161 + 4332188*uk_162 + 1836688*uk_163 + 778688*uk_164 + 3969*uk_17 + 2079*uk_18 + 5796*uk_19 + 63*uk_2 + 756*uk_20 + 10647*uk_21 + 13671*uk_22 + 5796*uk_23 + 1089*uk_24 + 3036*uk_25 + 396*uk_26 + 5577*uk_27 + 7161*uk_28 + 3036*uk_29 + 33*uk_3 + 8464*uk_30 + 1104*uk_31 + 15548*uk_32 + 19964*uk_33 + 8464*uk_34 + 144*uk_35 + 2028*uk_36 + 2604*uk_37 + 1104*uk_38 + 28561*uk_39 + 92*uk_4 + 36673*uk_40 + 15548*uk_41 + 47089*uk_42 + 19964*uk_43 + 8464*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 73996118097*uk_47 + 206292208028*uk_48 + 26907679308*uk_49 + 12*uk_5 + 378949816921*uk_50 + 486580534153*uk_51 + 206292208028*uk_52 + 187944057*uk_53 + 98446887*uk_54 + 274457988*uk_55 + 35798868*uk_56 + 504167391*uk_57 + 647362863*uk_58 + 274457988*uk_59 + 169*uk_6 + 51567417*uk_60 + 143763708*uk_61 + 18751788*uk_62 + 264087681*uk_63 + 339094833*uk_64 + 143763708*uk_65 + 400795792*uk_66 + 52277712*uk_67 + 736244444*uk_68 + 945355292*uk_69 + 217*uk_7 + 400795792*uk_70 + 6818832*uk_71 + 96031884*uk_72 + 123307212*uk_73 + 52277712*uk_74 + 1352449033*uk_75 + 1736576569*uk_76 + 736244444*uk_77 + 2229805417*uk_78 + 945355292*uk_79 + 92*uk_8 + 400795792*uk_80 + 250047*uk_81 + 130977*uk_82 + 365148*uk_83 + 47628*uk_84 + 670761*uk_85 + 861273*uk_86 + 365148*uk_87 + 68607*uk_88 + 191268*uk_89 + 2242306609*uk_9 + 24948*uk_90 + 351351*uk_91 + 451143*uk_92 + 191268*uk_93 + 533232*uk_94 + 69552*uk_95 + 979524*uk_96 + 1257732*uk_97 + 533232*uk_98 + 9072*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 172368*uk_100 + 218736*uk_101 + 33264*uk_102 + 1842183*uk_103 + 2337741*uk_104 + 355509*uk_105 + 2966607*uk_106 + 451143*uk_107 + 68607*uk_108 + 3869893*uk_109 + 7434421*uk_11 + 813417*uk_110 + 394384*uk_111 + 4214979*uk_112 + 5348833*uk_113 + 813417*uk_114 + 170973*uk_115 + 82896*uk_116 + 885951*uk_117 + 1124277*uk_118 + 170973*uk_119 + 1562649*uk_12 + 40192*uk_120 + 429552*uk_121 + 545104*uk_122 + 82896*uk_123 + 4590837*uk_124 + 5825799*uk_125 + 885951*uk_126 + 7392973*uk_127 + 1124277*uk_128 + 170973*uk_129 + 757648*uk_13 + 35937*uk_130 + 17424*uk_131 + 186219*uk_132 + 236313*uk_133 + 35937*uk_134 + 8448*uk_135 + 90288*uk_136 + 114576*uk_137 + 17424*uk_138 + 964953*uk_139 + 8097363*uk_14 + 1224531*uk_140 + 186219*uk_141 + 1553937*uk_142 + 236313*uk_143 + 35937*uk_144 + 4096*uk_145 + 43776*uk_146 + 55552*uk_147 + 8448*uk_148 + 467856*uk_149 + 10275601*uk_15 + 593712*uk_150 + 90288*uk_151 + 753424*uk_152 + 114576*uk_153 + 17424*uk_154 + 5000211*uk_155 + 6345297*uk_156 + 964953*uk_157 + 8052219*uk_158 + 1224531*uk_159 + 1562649*uk_16 + 186219*uk_160 + 10218313*uk_161 + 1553937*uk_162 + 236313*uk_163 + 35937*uk_164 + 3969*uk_17 + 9891*uk_18 + 2079*uk_19 + 63*uk_2 + 1008*uk_20 + 10773*uk_21 + 13671*uk_22 + 2079*uk_23 + 24649*uk_24 + 5181*uk_25 + 2512*uk_26 + 26847*uk_27 + 34069*uk_28 + 5181*uk_29 + 157*uk_3 + 1089*uk_30 + 528*uk_31 + 5643*uk_32 + 7161*uk_33 + 1089*uk_34 + 256*uk_35 + 2736*uk_36 + 3472*uk_37 + 528*uk_38 + 29241*uk_39 + 33*uk_4 + 37107*uk_40 + 5643*uk_41 + 47089*uk_42 + 7161*uk_43 + 1089*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 352042137613*uk_47 + 73996118097*uk_48 + 35876905744*uk_49 + 16*uk_5 + 383434430139*uk_50 + 486580534153*uk_51 + 73996118097*uk_52 + 187944057*uk_53 + 468368523*uk_54 + 98446887*uk_55 + 47731824*uk_56 + 510133869*uk_57 + 647362863*uk_58 + 98446887*uk_59 + 171*uk_6 + 1167204097*uk_60 + 245335893*uk_61 + 118950736*uk_62 + 1271285991*uk_63 + 1613269357*uk_64 + 245335893*uk_65 + 51567417*uk_66 + 25002384*uk_67 + 267212979*uk_68 + 339094833*uk_69 + 217*uk_7 + 51567417*uk_70 + 12122368*uk_71 + 129557808*uk_72 + 164409616*uk_73 + 25002384*uk_74 + 1384649073*uk_75 + 1757127771*uk_76 + 267212979*uk_77 + 2229805417*uk_78 + 339094833*uk_79 + 33*uk_8 + 51567417*uk_80 + 250047*uk_81 + 623133*uk_82 + 130977*uk_83 + 63504*uk_84 + 678699*uk_85 + 861273*uk_86 + 130977*uk_87 + 1552887*uk_88 + 326403*uk_89 + 2242306609*uk_9 + 158256*uk_90 + 1691361*uk_91 + 2146347*uk_92 + 326403*uk_93 + 68607*uk_94 + 33264*uk_95 + 355509*uk_96 + 451143*uk_97 + 68607*uk_98 + 16128*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 130788*uk_100 + 164052*uk_101 + 118692*uk_102 + 1885527*uk_103 + 2365083*uk_104 + 1711143*uk_105 + 2966607*uk_106 + 2146347*uk_107 + 1552887*uk_108 + 1906624*uk_109 + 5871772*uk_11 + 2414032*uk_110 + 184512*uk_111 + 2660048*uk_112 + 3336592*uk_113 + 2414032*uk_114 + 3056476*uk_115 + 233616*uk_116 + 3367964*uk_117 + 4224556*uk_118 + 3056476*uk_119 + 7434421*uk_12 + 17856*uk_120 + 257424*uk_121 + 322896*uk_122 + 233616*uk_123 + 3711196*uk_124 + 4655084*uk_125 + 3367964*uk_126 + 5839036*uk_127 + 4224556*uk_128 + 3056476*uk_129 + 568236*uk_13 + 3869893*uk_130 + 295788*uk_131 + 4264277*uk_132 + 5348833*uk_133 + 3869893*uk_134 + 22608*uk_135 + 325932*uk_136 + 408828*uk_137 + 295788*uk_138 + 4698853*uk_139 + 8192069*uk_14 + 5893937*uk_140 + 4264277*uk_141 + 7392973*uk_142 + 5348833*uk_143 + 3869893*uk_144 + 1728*uk_145 + 24912*uk_146 + 31248*uk_147 + 22608*uk_148 + 359148*uk_149 + 10275601*uk_15 + 450492*uk_150 + 325932*uk_151 + 565068*uk_152 + 408828*uk_153 + 295788*uk_154 + 5177717*uk_155 + 6494593*uk_156 + 4698853*uk_157 + 8146397*uk_158 + 5893937*uk_159 + 7434421*uk_16 + 4264277*uk_160 + 10218313*uk_161 + 7392973*uk_162 + 5348833*uk_163 + 3869893*uk_164 + 3969*uk_17 + 7812*uk_18 + 9891*uk_19 + 63*uk_2 + 756*uk_20 + 10899*uk_21 + 13671*uk_22 + 9891*uk_23 + 15376*uk_24 + 19468*uk_25 + 1488*uk_26 + 21452*uk_27 + 26908*uk_28 + 19468*uk_29 + 124*uk_3 + 24649*uk_30 + 1884*uk_31 + 27161*uk_32 + 34069*uk_33 + 24649*uk_34 + 144*uk_35 + 2076*uk_36 + 2604*uk_37 + 1884*uk_38 + 29929*uk_39 + 157*uk_4 + 37541*uk_40 + 27161*uk_41 + 47089*uk_42 + 34069*uk_43 + 24649*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 278046019516*uk_47 + 352042137613*uk_48 + 26907679308*uk_49 + 12*uk_5 + 387919043357*uk_50 + 486580534153*uk_51 + 352042137613*uk_52 + 187944057*uk_53 + 369921636*uk_54 + 468368523*uk_55 + 35798868*uk_56 + 516100347*uk_57 + 647362863*uk_58 + 468368523*uk_59 + 173*uk_6 + 728099728*uk_60 + 921868204*uk_61 + 70461264*uk_62 + 1015816556*uk_63 + 1274174524*uk_64 + 921868204*uk_65 + 1167204097*uk_66 + 89213052*uk_67 + 1286154833*uk_68 + 1613269357*uk_69 + 217*uk_7 + 1167204097*uk_70 + 6818832*uk_71 + 98304828*uk_72 + 123307212*uk_73 + 89213052*uk_74 + 1417227937*uk_75 + 1777678973*uk_76 + 1286154833*uk_77 + 2229805417*uk_78 + 1613269357*uk_79 + 157*uk_8 + 1167204097*uk_80 + 250047*uk_81 + 492156*uk_82 + 623133*uk_83 + 47628*uk_84 + 686637*uk_85 + 861273*uk_86 + 623133*uk_87 + 968688*uk_88 + 1226484*uk_89 + 2242306609*uk_9 + 93744*uk_90 + 1351476*uk_91 + 1695204*uk_92 + 1226484*uk_93 + 1552887*uk_94 + 118692*uk_95 + 1711143*uk_96 + 2146347*uk_97 + 1552887*uk_98 + 9072*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 132300*uk_100 + 164052*uk_101 + 93744*uk_102 + 1929375*uk_103 + 2392425*uk_104 + 1367100*uk_105 + 2966607*uk_106 + 1695204*uk_107 + 968688*uk_108 + 1092727*uk_109 + 4877359*uk_11 + 1315516*uk_110 + 127308*uk_111 + 1856575*uk_112 + 2302153*uk_113 + 1315516*uk_114 + 1583728*uk_115 + 153264*uk_116 + 2235100*uk_117 + 2771524*uk_118 + 1583728*uk_119 + 5871772*uk_12 + 14832*uk_120 + 216300*uk_121 + 268212*uk_122 + 153264*uk_123 + 3154375*uk_124 + 3911425*uk_125 + 2235100*uk_126 + 4850167*uk_127 + 2771524*uk_128 + 1583728*uk_129 + 568236*uk_13 + 1906624*uk_130 + 184512*uk_131 + 2690800*uk_132 + 3336592*uk_133 + 1906624*uk_134 + 17856*uk_135 + 260400*uk_136 + 322896*uk_137 + 184512*uk_138 + 3797500*uk_139 + 8286775*uk_14 + 4708900*uk_140 + 2690800*uk_141 + 5839036*uk_142 + 3336592*uk_143 + 1906624*uk_144 + 1728*uk_145 + 25200*uk_146 + 31248*uk_147 + 17856*uk_148 + 367500*uk_149 + 10275601*uk_15 + 455700*uk_150 + 260400*uk_151 + 565068*uk_152 + 322896*uk_153 + 184512*uk_154 + 5359375*uk_155 + 6645625*uk_156 + 3797500*uk_157 + 8240575*uk_158 + 4708900*uk_159 + 5871772*uk_16 + 2690800*uk_160 + 10218313*uk_161 + 5839036*uk_162 + 3336592*uk_163 + 1906624*uk_164 + 3969*uk_17 + 6489*uk_18 + 7812*uk_19 + 63*uk_2 + 756*uk_20 + 11025*uk_21 + 13671*uk_22 + 7812*uk_23 + 10609*uk_24 + 12772*uk_25 + 1236*uk_26 + 18025*uk_27 + 22351*uk_28 + 12772*uk_29 + 103*uk_3 + 15376*uk_30 + 1488*uk_31 + 21700*uk_32 + 26908*uk_33 + 15376*uk_34 + 144*uk_35 + 2100*uk_36 + 2604*uk_37 + 1488*uk_38 + 30625*uk_39 + 124*uk_4 + 37975*uk_40 + 21700*uk_41 + 47089*uk_42 + 26908*uk_43 + 15376*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 230957580727*uk_47 + 278046019516*uk_48 + 26907679308*uk_49 + 12*uk_5 + 392403656575*uk_50 + 486580534153*uk_51 + 278046019516*uk_52 + 187944057*uk_53 + 307273617*uk_54 + 369921636*uk_55 + 35798868*uk_56 + 522066825*uk_57 + 647362863*uk_58 + 369921636*uk_59 + 175*uk_6 + 502367977*uk_60 + 604792516*uk_61 + 58528308*uk_62 + 853537825*uk_63 + 1058386903*uk_64 + 604792516*uk_65 + 728099728*uk_66 + 70461264*uk_67 + 1027560100*uk_68 + 1274174524*uk_69 + 217*uk_7 + 728099728*uk_70 + 6818832*uk_71 + 99441300*uk_72 + 123307212*uk_73 + 70461264*uk_74 + 1450185625*uk_75 + 1798230175*uk_76 + 1027560100*uk_77 + 2229805417*uk_78 + 1274174524*uk_79 + 124*uk_8 + 728099728*uk_80 + 250047*uk_81 + 408807*uk_82 + 492156*uk_83 + 47628*uk_84 + 694575*uk_85 + 861273*uk_86 + 492156*uk_87 + 668367*uk_88 + 804636*uk_89 + 2242306609*uk_9 + 77868*uk_90 + 1135575*uk_91 + 1408113*uk_92 + 804636*uk_93 + 968688*uk_94 + 93744*uk_95 + 1367100*uk_96 + 1695204*uk_97 + 968688*uk_98 + 9072*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 133812*uk_100 + 164052*uk_101 + 77868*uk_102 + 1973727*uk_103 + 2419767*uk_104 + 1148553*uk_105 + 2966607*uk_106 + 1408113*uk_107 + 668367*uk_108 + 830584*uk_109 + 4451182*uk_11 + 910108*uk_110 + 106032*uk_111 + 1563972*uk_112 + 1917412*uk_113 + 910108*uk_114 + 997246*uk_115 + 116184*uk_116 + 1713714*uk_117 + 2100994*uk_118 + 997246*uk_119 + 4877359*uk_12 + 13536*uk_120 + 199656*uk_121 + 244776*uk_122 + 116184*uk_123 + 2944926*uk_124 + 3610446*uk_125 + 1713714*uk_126 + 4426366*uk_127 + 2100994*uk_128 + 997246*uk_129 + 568236*uk_13 + 1092727*uk_130 + 127308*uk_131 + 1877793*uk_132 + 2302153*uk_133 + 1092727*uk_134 + 14832*uk_135 + 218772*uk_136 + 268212*uk_137 + 127308*uk_138 + 3226887*uk_139 + 8381481*uk_14 + 3956127*uk_140 + 1877793*uk_141 + 4850167*uk_142 + 2302153*uk_143 + 1092727*uk_144 + 1728*uk_145 + 25488*uk_146 + 31248*uk_147 + 14832*uk_148 + 375948*uk_149 + 10275601*uk_15 + 460908*uk_150 + 218772*uk_151 + 565068*uk_152 + 268212*uk_153 + 127308*uk_154 + 5545233*uk_155 + 6798393*uk_156 + 3226887*uk_157 + 8334753*uk_158 + 3956127*uk_159 + 4877359*uk_16 + 1877793*uk_160 + 10218313*uk_161 + 4850167*uk_162 + 2302153*uk_163 + 1092727*uk_164 + 3969*uk_17 + 5922*uk_18 + 6489*uk_19 + 63*uk_2 + 756*uk_20 + 11151*uk_21 + 13671*uk_22 + 6489*uk_23 + 8836*uk_24 + 9682*uk_25 + 1128*uk_26 + 16638*uk_27 + 20398*uk_28 + 9682*uk_29 + 94*uk_3 + 10609*uk_30 + 1236*uk_31 + 18231*uk_32 + 22351*uk_33 + 10609*uk_34 + 144*uk_35 + 2124*uk_36 + 2604*uk_37 + 1236*uk_38 + 31329*uk_39 + 103*uk_4 + 38409*uk_40 + 18231*uk_41 + 47089*uk_42 + 22351*uk_43 + 10609*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 210776821246*uk_47 + 230957580727*uk_48 + 26907679308*uk_49 + 12*uk_5 + 396888269793*uk_50 + 486580534153*uk_51 + 230957580727*uk_52 + 187944057*uk_53 + 280424466*uk_54 + 307273617*uk_55 + 35798868*uk_56 + 528033303*uk_57 + 647362863*uk_58 + 307273617*uk_59 + 177*uk_6 + 418411108*uk_60 + 458471746*uk_61 + 53414184*uk_62 + 787859214*uk_63 + 965906494*uk_64 + 458471746*uk_65 + 502367977*uk_66 + 58528308*uk_67 + 863292543*uk_68 + 1058386903*uk_69 + 217*uk_7 + 502367977*uk_70 + 6818832*uk_71 + 100577772*uk_72 + 123307212*uk_73 + 58528308*uk_74 + 1483522137*uk_75 + 1818781377*uk_76 + 863292543*uk_77 + 2229805417*uk_78 + 1058386903*uk_79 + 103*uk_8 + 502367977*uk_80 + 250047*uk_81 + 373086*uk_82 + 408807*uk_83 + 47628*uk_84 + 702513*uk_85 + 861273*uk_86 + 408807*uk_87 + 556668*uk_88 + 609966*uk_89 + 2242306609*uk_9 + 71064*uk_90 + 1048194*uk_91 + 1285074*uk_92 + 609966*uk_93 + 668367*uk_94 + 77868*uk_95 + 1148553*uk_96 + 1408113*uk_97 + 668367*uk_98 + 9072*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 135324*uk_100 + 164052*uk_101 + 71064*uk_102 + 2018583*uk_103 + 2447109*uk_104 + 1060038*uk_105 + 2966607*uk_106 + 1285074*uk_107 + 556668*uk_108 + 912673*uk_109 + 4593241*uk_11 + 884446*uk_110 + 112908*uk_111 + 1684211*uk_112 + 2041753*uk_113 + 884446*uk_114 + 857092*uk_115 + 109416*uk_116 + 1632122*uk_117 + 1978606*uk_118 + 857092*uk_119 + 4451182*uk_12 + 13968*uk_120 + 208356*uk_121 + 252588*uk_122 + 109416*uk_123 + 3107977*uk_124 + 3767771*uk_125 + 1632122*uk_126 + 4567633*uk_127 + 1978606*uk_128 + 857092*uk_129 + 568236*uk_13 + 830584*uk_130 + 106032*uk_131 + 1581644*uk_132 + 1917412*uk_133 + 830584*uk_134 + 13536*uk_135 + 201912*uk_136 + 244776*uk_137 + 106032*uk_138 + 3011854*uk_139 + 8476187*uk_14 + 3651242*uk_140 + 1581644*uk_141 + 4426366*uk_142 + 1917412*uk_143 + 830584*uk_144 + 1728*uk_145 + 25776*uk_146 + 31248*uk_147 + 13536*uk_148 + 384492*uk_149 + 10275601*uk_15 + 466116*uk_150 + 201912*uk_151 + 565068*uk_152 + 244776*uk_153 + 106032*uk_154 + 5735339*uk_155 + 6952897*uk_156 + 3011854*uk_157 + 8428931*uk_158 + 3651242*uk_159 + 4451182*uk_16 + 1581644*uk_160 + 10218313*uk_161 + 4426366*uk_162 + 1917412*uk_163 + 830584*uk_164 + 3969*uk_17 + 6111*uk_18 + 5922*uk_19 + 63*uk_2 + 756*uk_20 + 11277*uk_21 + 13671*uk_22 + 5922*uk_23 + 9409*uk_24 + 9118*uk_25 + 1164*uk_26 + 17363*uk_27 + 21049*uk_28 + 9118*uk_29 + 97*uk_3 + 8836*uk_30 + 1128*uk_31 + 16826*uk_32 + 20398*uk_33 + 8836*uk_34 + 144*uk_35 + 2148*uk_36 + 2604*uk_37 + 1128*uk_38 + 32041*uk_39 + 94*uk_4 + 38843*uk_40 + 16826*uk_41 + 47089*uk_42 + 20398*uk_43 + 8836*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 217503741073*uk_47 + 210776821246*uk_48 + 26907679308*uk_49 + 12*uk_5 + 401372883011*uk_50 + 486580534153*uk_51 + 210776821246*uk_52 + 187944057*uk_53 + 289374183*uk_54 + 280424466*uk_55 + 35798868*uk_56 + 533999781*uk_57 + 647362863*uk_58 + 280424466*uk_59 + 179*uk_6 + 445544377*uk_60 + 431764654*uk_61 + 55118892*uk_62 + 822190139*uk_63 + 996733297*uk_64 + 431764654*uk_65 + 418411108*uk_66 + 53414184*uk_67 + 796761578*uk_68 + 965906494*uk_69 + 217*uk_7 + 418411108*uk_70 + 6818832*uk_71 + 101714244*uk_72 + 123307212*uk_73 + 53414184*uk_74 + 1517237473*uk_75 + 1839332579*uk_76 + 796761578*uk_77 + 2229805417*uk_78 + 965906494*uk_79 + 94*uk_8 + 418411108*uk_80 + 250047*uk_81 + 384993*uk_82 + 373086*uk_83 + 47628*uk_84 + 710451*uk_85 + 861273*uk_86 + 373086*uk_87 + 592767*uk_88 + 574434*uk_89 + 2242306609*uk_9 + 73332*uk_90 + 1093869*uk_91 + 1326087*uk_92 + 574434*uk_93 + 556668*uk_94 + 71064*uk_95 + 1060038*uk_96 + 1285074*uk_97 + 556668*uk_98 + 9072*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 136836*uk_100 + 164052*uk_101 + 73332*uk_102 + 2063943*uk_103 + 2474451*uk_104 + 1106091*uk_105 + 2966607*uk_106 + 1326087*uk_107 + 592767*uk_108 + 1404928*uk_109 + 5303536*uk_11 + 1216768*uk_110 + 150528*uk_111 + 2270464*uk_112 + 2722048*uk_113 + 1216768*uk_114 + 1053808*uk_115 + 130368*uk_116 + 1966384*uk_117 + 2357488*uk_118 + 1053808*uk_119 + 4593241*uk_12 + 16128*uk_120 + 243264*uk_121 + 291648*uk_122 + 130368*uk_123 + 3669232*uk_124 + 4399024*uk_125 + 1966384*uk_126 + 5273968*uk_127 + 2357488*uk_128 + 1053808*uk_129 + 568236*uk_13 + 912673*uk_130 + 112908*uk_131 + 1703029*uk_132 + 2041753*uk_133 + 912673*uk_134 + 13968*uk_135 + 210684*uk_136 + 252588*uk_137 + 112908*uk_138 + 3177817*uk_139 + 8570893*uk_14 + 3809869*uk_140 + 1703029*uk_141 + 4567633*uk_142 + 2041753*uk_143 + 912673*uk_144 + 1728*uk_145 + 26064*uk_146 + 31248*uk_147 + 13968*uk_148 + 393132*uk_149 + 10275601*uk_15 + 471324*uk_150 + 210684*uk_151 + 565068*uk_152 + 252588*uk_153 + 112908*uk_154 + 5929741*uk_155 + 7109137*uk_156 + 3177817*uk_157 + 8523109*uk_158 + 3809869*uk_159 + 4593241*uk_16 + 1703029*uk_160 + 10218313*uk_161 + 4567633*uk_162 + 2041753*uk_163 + 912673*uk_164 + 3969*uk_17 + 7056*uk_18 + 6111*uk_19 + 63*uk_2 + 756*uk_20 + 11403*uk_21 + 13671*uk_22 + 6111*uk_23 + 12544*uk_24 + 10864*uk_25 + 1344*uk_26 + 20272*uk_27 + 24304*uk_28 + 10864*uk_29 + 112*uk_3 + 9409*uk_30 + 1164*uk_31 + 17557*uk_32 + 21049*uk_33 + 9409*uk_34 + 144*uk_35 + 2172*uk_36 + 2604*uk_37 + 1164*uk_38 + 32761*uk_39 + 97*uk_4 + 39277*uk_40 + 17557*uk_41 + 47089*uk_42 + 21049*uk_43 + 9409*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 251138340208*uk_47 + 217503741073*uk_48 + 26907679308*uk_49 + 12*uk_5 + 405857496229*uk_50 + 486580534153*uk_51 + 217503741073*uk_52 + 187944057*uk_53 + 334122768*uk_54 + 289374183*uk_55 + 35798868*uk_56 + 539966259*uk_57 + 647362863*uk_58 + 289374183*uk_59 + 181*uk_6 + 593996032*uk_60 + 514442992*uk_61 + 63642432*uk_62 + 959940016*uk_63 + 1150867312*uk_64 + 514442992*uk_65 + 445544377*uk_66 + 55118892*uk_67 + 831376621*uk_68 + 996733297*uk_69 + 217*uk_7 + 445544377*uk_70 + 6818832*uk_71 + 102850716*uk_72 + 123307212*uk_73 + 55118892*uk_74 + 1551331633*uk_75 + 1859883781*uk_76 + 831376621*uk_77 + 2229805417*uk_78 + 996733297*uk_79 + 97*uk_8 + 445544377*uk_80 + 250047*uk_81 + 444528*uk_82 + 384993*uk_83 + 47628*uk_84 + 718389*uk_85 + 861273*uk_86 + 384993*uk_87 + 790272*uk_88 + 684432*uk_89 + 2242306609*uk_9 + 84672*uk_90 + 1277136*uk_91 + 1531152*uk_92 + 684432*uk_93 + 592767*uk_94 + 73332*uk_95 + 1106091*uk_96 + 1326087*uk_97 + 592767*uk_98 + 9072*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 138348*uk_100 + 164052*uk_101 + 84672*uk_102 + 2109807*uk_103 + 2501793*uk_104 + 1291248*uk_105 + 2966607*uk_106 + 1531152*uk_107 + 790272*uk_108 + 2685619*uk_109 + 6582067*uk_11 + 2163952*uk_110 + 231852*uk_111 + 3535743*uk_112 + 4192657*uk_113 + 2163952*uk_114 + 1743616*uk_115 + 186816*uk_116 + 2848944*uk_117 + 3378256*uk_118 + 1743616*uk_119 + 5303536*uk_12 + 20016*uk_120 + 305244*uk_121 + 361956*uk_122 + 186816*uk_123 + 4654971*uk_124 + 5519829*uk_125 + 2848944*uk_126 + 6545371*uk_127 + 3378256*uk_128 + 1743616*uk_129 + 568236*uk_13 + 1404928*uk_130 + 150528*uk_131 + 2295552*uk_132 + 2722048*uk_133 + 1404928*uk_134 + 16128*uk_135 + 245952*uk_136 + 291648*uk_137 + 150528*uk_138 + 3750768*uk_139 + 8665599*uk_14 + 4447632*uk_140 + 2295552*uk_141 + 5273968*uk_142 + 2722048*uk_143 + 1404928*uk_144 + 1728*uk_145 + 26352*uk_146 + 31248*uk_147 + 16128*uk_148 + 401868*uk_149 + 10275601*uk_15 + 476532*uk_150 + 245952*uk_151 + 565068*uk_152 + 291648*uk_153 + 150528*uk_154 + 6128487*uk_155 + 7267113*uk_156 + 3750768*uk_157 + 8617287*uk_158 + 4447632*uk_159 + 5303536*uk_16 + 2295552*uk_160 + 10218313*uk_161 + 5273968*uk_162 + 2722048*uk_163 + 1404928*uk_164 + 3969*uk_17 + 8757*uk_18 + 7056*uk_19 + 63*uk_2 + 756*uk_20 + 11529*uk_21 + 13671*uk_22 + 7056*uk_23 + 19321*uk_24 + 15568*uk_25 + 1668*uk_26 + 25437*uk_27 + 30163*uk_28 + 15568*uk_29 + 139*uk_3 + 12544*uk_30 + 1344*uk_31 + 20496*uk_32 + 24304*uk_33 + 12544*uk_34 + 144*uk_35 + 2196*uk_36 + 2604*uk_37 + 1344*uk_38 + 33489*uk_39 + 112*uk_4 + 39711*uk_40 + 20496*uk_41 + 47089*uk_42 + 24304*uk_43 + 12544*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 311680618651*uk_47 + 251138340208*uk_48 + 26907679308*uk_49 + 12*uk_5 + 410342109447*uk_50 + 486580534153*uk_51 + 251138340208*uk_52 + 187944057*uk_53 + 414670221*uk_54 + 334122768*uk_55 + 35798868*uk_56 + 545932737*uk_57 + 647362863*uk_58 + 334122768*uk_59 + 183*uk_6 + 914907313*uk_60 + 737191504*uk_61 + 78984804*uk_62 + 1204518261*uk_63 + 1428308539*uk_64 + 737191504*uk_65 + 593996032*uk_66 + 63642432*uk_67 + 970547088*uk_68 + 1150867312*uk_69 + 217*uk_7 + 593996032*uk_70 + 6818832*uk_71 + 103987188*uk_72 + 123307212*uk_73 + 63642432*uk_74 + 1585804617*uk_75 + 1880434983*uk_76 + 970547088*uk_77 + 2229805417*uk_78 + 1150867312*uk_79 + 112*uk_8 + 593996032*uk_80 + 250047*uk_81 + 551691*uk_82 + 444528*uk_83 + 47628*uk_84 + 726327*uk_85 + 861273*uk_86 + 444528*uk_87 + 1217223*uk_88 + 980784*uk_89 + 2242306609*uk_9 + 105084*uk_90 + 1602531*uk_91 + 1900269*uk_92 + 980784*uk_93 + 790272*uk_94 + 84672*uk_95 + 1291248*uk_96 + 1531152*uk_97 + 790272*uk_98 + 9072*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 139860*uk_100 + 164052*uk_101 + 105084*uk_102 + 2156175*uk_103 + 2529135*uk_104 + 1620045*uk_105 + 2966607*uk_106 + 1900269*uk_107 + 1217223*uk_108 + 5639752*uk_109 + 8428834*uk_11 + 4404076*uk_110 + 380208*uk_111 + 5861540*uk_112 + 6875428*uk_113 + 4404076*uk_114 + 3439138*uk_115 + 296904*uk_116 + 4577270*uk_117 + 5369014*uk_118 + 3439138*uk_119 + 6582067*uk_12 + 25632*uk_120 + 395160*uk_121 + 463512*uk_122 + 296904*uk_123 + 6092050*uk_124 + 7145810*uk_125 + 4577270*uk_126 + 8381842*uk_127 + 5369014*uk_128 + 3439138*uk_129 + 568236*uk_13 + 2685619*uk_130 + 231852*uk_131 + 3574385*uk_132 + 4192657*uk_133 + 2685619*uk_134 + 20016*uk_135 + 308580*uk_136 + 361956*uk_137 + 231852*uk_138 + 4757275*uk_139 + 8760305*uk_14 + 5580155*uk_140 + 3574385*uk_141 + 6545371*uk_142 + 4192657*uk_143 + 2685619*uk_144 + 1728*uk_145 + 26640*uk_146 + 31248*uk_147 + 20016*uk_148 + 410700*uk_149 + 10275601*uk_15 + 481740*uk_150 + 308580*uk_151 + 565068*uk_152 + 361956*uk_153 + 231852*uk_154 + 6331625*uk_155 + 7426825*uk_156 + 4757275*uk_157 + 8711465*uk_158 + 5580155*uk_159 + 6582067*uk_16 + 3574385*uk_160 + 10218313*uk_161 + 6545371*uk_162 + 4192657*uk_163 + 2685619*uk_164 + 3969*uk_17 + 11214*uk_18 + 8757*uk_19 + 63*uk_2 + 756*uk_20 + 11655*uk_21 + 13671*uk_22 + 8757*uk_23 + 31684*uk_24 + 24742*uk_25 + 2136*uk_26 + 32930*uk_27 + 38626*uk_28 + 24742*uk_29 + 178*uk_3 + 19321*uk_30 + 1668*uk_31 + 25715*uk_32 + 30163*uk_33 + 19321*uk_34 + 144*uk_35 + 2220*uk_36 + 2604*uk_37 + 1668*uk_38 + 34225*uk_39 + 139*uk_4 + 40145*uk_40 + 25715*uk_41 + 47089*uk_42 + 30163*uk_43 + 19321*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 399130576402*uk_47 + 311680618651*uk_48 + 26907679308*uk_49 + 12*uk_5 + 414826722665*uk_50 + 486580534153*uk_51 + 311680618651*uk_52 + 187944057*uk_53 + 531016542*uk_54 + 414670221*uk_55 + 35798868*uk_56 + 551899215*uk_57 + 647362863*uk_58 + 414670221*uk_59 + 185*uk_6 + 1500332452*uk_60 + 1171607926*uk_61 + 101146008*uk_62 + 1559334290*uk_63 + 1829056978*uk_64 + 1171607926*uk_65 + 914907313*uk_66 + 78984804*uk_67 + 1217682395*uk_68 + 1428308539*uk_69 + 217*uk_7 + 914907313*uk_70 + 6818832*uk_71 + 105123660*uk_72 + 123307212*uk_73 + 78984804*uk_74 + 1620656425*uk_75 + 1900986185*uk_76 + 1217682395*uk_77 + 2229805417*uk_78 + 1428308539*uk_79 + 139*uk_8 + 914907313*uk_80 + 250047*uk_81 + 706482*uk_82 + 551691*uk_83 + 47628*uk_84 + 734265*uk_85 + 861273*uk_86 + 551691*uk_87 + 1996092*uk_88 + 1558746*uk_89 + 2242306609*uk_9 + 134568*uk_90 + 2074590*uk_91 + 2433438*uk_92 + 1558746*uk_93 + 1217223*uk_94 + 105084*uk_95 + 1620045*uk_96 + 1900269*uk_97 + 1217223*uk_98 + 9072*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 94248*uk_100 + 109368*uk_101 + 89712*uk_102 + 2203047*uk_103 + 2556477*uk_104 + 2097018*uk_105 + 2966607*uk_106 + 2433438*uk_107 + 1996092*uk_108 + 74088*uk_109 + 1988826*uk_11 + 313992*uk_110 + 14112*uk_111 + 329868*uk_112 + 382788*uk_113 + 313992*uk_114 + 1330728*uk_115 + 59808*uk_116 + 1398012*uk_117 + 1622292*uk_118 + 1330728*uk_119 + 8428834*uk_12 + 2688*uk_120 + 62832*uk_121 + 72912*uk_122 + 59808*uk_123 + 1468698*uk_124 + 1704318*uk_125 + 1398012*uk_126 + 1977738*uk_127 + 1622292*uk_128 + 1330728*uk_129 + 378824*uk_13 + 5639752*uk_130 + 253472*uk_131 + 5924908*uk_132 + 6875428*uk_133 + 5639752*uk_134 + 11392*uk_135 + 266288*uk_136 + 309008*uk_137 + 253472*uk_138 + 6224482*uk_139 + 8855011*uk_14 + 7223062*uk_140 + 5924908*uk_141 + 8381842*uk_142 + 6875428*uk_143 + 5639752*uk_144 + 512*uk_145 + 11968*uk_146 + 13888*uk_147 + 11392*uk_148 + 279752*uk_149 + 10275601*uk_15 + 324632*uk_150 + 266288*uk_151 + 376712*uk_152 + 309008*uk_153 + 253472*uk_154 + 6539203*uk_155 + 7588273*uk_156 + 6224482*uk_157 + 8805643*uk_158 + 7223062*uk_159 + 8428834*uk_16 + 5924908*uk_160 + 10218313*uk_161 + 8381842*uk_162 + 6875428*uk_163 + 5639752*uk_164 + 3969*uk_17 + 2646*uk_18 + 11214*uk_19 + 63*uk_2 + 504*uk_20 + 11781*uk_21 + 13671*uk_22 + 11214*uk_23 + 1764*uk_24 + 7476*uk_25 + 336*uk_26 + 7854*uk_27 + 9114*uk_28 + 7476*uk_29 + 42*uk_3 + 31684*uk_30 + 1424*uk_31 + 33286*uk_32 + 38626*uk_33 + 31684*uk_34 + 64*uk_35 + 1496*uk_36 + 1736*uk_37 + 1424*uk_38 + 34969*uk_39 + 178*uk_4 + 40579*uk_40 + 33286*uk_41 + 47089*uk_42 + 38626*uk_43 + 31684*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 94176877578*uk_47 + 399130576402*uk_48 + 17938452872*uk_49 + 8*uk_5 + 419311335883*uk_50 + 486580534153*uk_51 + 399130576402*uk_52 + 187944057*uk_53 + 125296038*uk_54 + 531016542*uk_55 + 23865912*uk_56 + 557865693*uk_57 + 647362863*uk_58 + 531016542*uk_59 + 187*uk_6 + 83530692*uk_60 + 354011028*uk_61 + 15910608*uk_62 + 371910462*uk_63 + 431575242*uk_64 + 354011028*uk_65 + 1500332452*uk_66 + 67430672*uk_67 + 1576191958*uk_68 + 1829056978*uk_69 + 217*uk_7 + 1500332452*uk_70 + 3030592*uk_71 + 70840088*uk_72 + 82204808*uk_73 + 67430672*uk_74 + 1655887057*uk_75 + 1921537387*uk_76 + 1576191958*uk_77 + 2229805417*uk_78 + 1829056978*uk_79 + 178*uk_8 + 1500332452*uk_80 + 250047*uk_81 + 166698*uk_82 + 706482*uk_83 + 31752*uk_84 + 742203*uk_85 + 861273*uk_86 + 706482*uk_87 + 111132*uk_88 + 470988*uk_89 + 2242306609*uk_9 + 21168*uk_90 + 494802*uk_91 + 574182*uk_92 + 470988*uk_93 + 1996092*uk_94 + 89712*uk_95 + 2097018*uk_96 + 2433438*uk_97 + 1996092*uk_98 + 4032*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 142884*uk_100 + 164052*uk_101 + 31752*uk_102 + 2250423*uk_103 + 2583819*uk_104 + 500094*uk_105 + 2966607*uk_106 + 574182*uk_107 + 111132*uk_108 + 1092727*uk_109 + 4877359*uk_11 + 445578*uk_110 + 127308*uk_111 + 2005101*uk_112 + 2302153*uk_113 + 445578*uk_114 + 181692*uk_115 + 51912*uk_116 + 817614*uk_117 + 938742*uk_118 + 181692*uk_119 + 1988826*uk_12 + 14832*uk_120 + 233604*uk_121 + 268212*uk_122 + 51912*uk_123 + 3679263*uk_124 + 4224339*uk_125 + 817614*uk_126 + 4850167*uk_127 + 938742*uk_128 + 181692*uk_129 + 568236*uk_13 + 74088*uk_130 + 21168*uk_131 + 333396*uk_132 + 382788*uk_133 + 74088*uk_134 + 6048*uk_135 + 95256*uk_136 + 109368*uk_137 + 21168*uk_138 + 1500282*uk_139 + 8949717*uk_14 + 1722546*uk_140 + 333396*uk_141 + 1977738*uk_142 + 382788*uk_143 + 74088*uk_144 + 1728*uk_145 + 27216*uk_146 + 31248*uk_147 + 6048*uk_148 + 428652*uk_149 + 10275601*uk_15 + 492156*uk_150 + 95256*uk_151 + 565068*uk_152 + 109368*uk_153 + 21168*uk_154 + 6751269*uk_155 + 7751457*uk_156 + 1500282*uk_157 + 8899821*uk_158 + 1722546*uk_159 + 1988826*uk_16 + 333396*uk_160 + 10218313*uk_161 + 1977738*uk_162 + 382788*uk_163 + 74088*uk_164 + 3969*uk_17 + 6489*uk_18 + 2646*uk_19 + 63*uk_2 + 756*uk_20 + 11907*uk_21 + 13671*uk_22 + 2646*uk_23 + 10609*uk_24 + 4326*uk_25 + 1236*uk_26 + 19467*uk_27 + 22351*uk_28 + 4326*uk_29 + 103*uk_3 + 1764*uk_30 + 504*uk_31 + 7938*uk_32 + 9114*uk_33 + 1764*uk_34 + 144*uk_35 + 2268*uk_36 + 2604*uk_37 + 504*uk_38 + 35721*uk_39 + 42*uk_4 + 41013*uk_40 + 7938*uk_41 + 47089*uk_42 + 9114*uk_43 + 1764*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 230957580727*uk_47 + 94176877578*uk_48 + 26907679308*uk_49 + 12*uk_5 + 423795949101*uk_50 + 486580534153*uk_51 + 94176877578*uk_52 + 187944057*uk_53 + 307273617*uk_54 + 125296038*uk_55 + 35798868*uk_56 + 563832171*uk_57 + 647362863*uk_58 + 125296038*uk_59 + 189*uk_6 + 502367977*uk_60 + 204849078*uk_61 + 58528308*uk_62 + 921820851*uk_63 + 1058386903*uk_64 + 204849078*uk_65 + 83530692*uk_66 + 23865912*uk_67 + 375888114*uk_68 + 431575242*uk_69 + 217*uk_7 + 83530692*uk_70 + 6818832*uk_71 + 107396604*uk_72 + 123307212*uk_73 + 23865912*uk_74 + 1691496513*uk_75 + 1942088589*uk_76 + 375888114*uk_77 + 2229805417*uk_78 + 431575242*uk_79 + 42*uk_8 + 83530692*uk_80 + 250047*uk_81 + 408807*uk_82 + 166698*uk_83 + 47628*uk_84 + 750141*uk_85 + 861273*uk_86 + 166698*uk_87 + 668367*uk_88 + 272538*uk_89 + 2242306609*uk_9 + 77868*uk_90 + 1226421*uk_91 + 1408113*uk_92 + 272538*uk_93 + 111132*uk_94 + 31752*uk_95 + 500094*uk_96 + 574182*uk_97 + 111132*uk_98 + 9072*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 144396*uk_100 + 164052*uk_101 + 77868*uk_102 + 2298303*uk_103 + 2611161*uk_104 + 1239399*uk_105 + 2966607*uk_106 + 1408113*uk_107 + 668367*uk_108 + 5451776*uk_109 + 8334128*uk_11 + 3190528*uk_110 + 371712*uk_111 + 5916416*uk_112 + 6721792*uk_113 + 3190528*uk_114 + 1867184*uk_115 + 217536*uk_116 + 3462448*uk_117 + 3933776*uk_118 + 1867184*uk_119 + 4877359*uk_12 + 25344*uk_120 + 403392*uk_121 + 458304*uk_122 + 217536*uk_123 + 6420656*uk_124 + 7294672*uk_125 + 3462448*uk_126 + 8287664*uk_127 + 3933776*uk_128 + 1867184*uk_129 + 568236*uk_13 + 1092727*uk_130 + 127308*uk_131 + 2026319*uk_132 + 2302153*uk_133 + 1092727*uk_134 + 14832*uk_135 + 236076*uk_136 + 268212*uk_137 + 127308*uk_138 + 3757543*uk_139 + 9044423*uk_14 + 4269041*uk_140 + 2026319*uk_141 + 4850167*uk_142 + 2302153*uk_143 + 1092727*uk_144 + 1728*uk_145 + 27504*uk_146 + 31248*uk_147 + 14832*uk_148 + 437772*uk_149 + 10275601*uk_15 + 497364*uk_150 + 236076*uk_151 + 565068*uk_152 + 268212*uk_153 + 127308*uk_154 + 6967871*uk_155 + 7916377*uk_156 + 3757543*uk_157 + 8993999*uk_158 + 4269041*uk_159 + 4877359*uk_16 + 2026319*uk_160 + 10218313*uk_161 + 4850167*uk_162 + 2302153*uk_163 + 1092727*uk_164 + 3969*uk_17 + 11088*uk_18 + 6489*uk_19 + 63*uk_2 + 756*uk_20 + 12033*uk_21 + 13671*uk_22 + 6489*uk_23 + 30976*uk_24 + 18128*uk_25 + 2112*uk_26 + 33616*uk_27 + 38192*uk_28 + 18128*uk_29 + 176*uk_3 + 10609*uk_30 + 1236*uk_31 + 19673*uk_32 + 22351*uk_33 + 10609*uk_34 + 144*uk_35 + 2292*uk_36 + 2604*uk_37 + 1236*uk_38 + 36481*uk_39 + 103*uk_4 + 41447*uk_40 + 19673*uk_41 + 47089*uk_42 + 22351*uk_43 + 10609*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 394645963184*uk_47 + 230957580727*uk_48 + 26907679308*uk_49 + 12*uk_5 + 428280562319*uk_50 + 486580534153*uk_51 + 230957580727*uk_52 + 187944057*uk_53 + 525050064*uk_54 + 307273617*uk_55 + 35798868*uk_56 + 569798649*uk_57 + 647362863*uk_58 + 307273617*uk_59 + 191*uk_6 + 1466806528*uk_60 + 858415184*uk_61 + 100009536*uk_62 + 1591818448*uk_63 + 1808505776*uk_64 + 858415184*uk_65 + 502367977*uk_66 + 58528308*uk_67 + 931575569*uk_68 + 1058386903*uk_69 + 217*uk_7 + 502367977*uk_70 + 6818832*uk_71 + 108533076*uk_72 + 123307212*uk_73 + 58528308*uk_74 + 1727484793*uk_75 + 1962639791*uk_76 + 931575569*uk_77 + 2229805417*uk_78 + 1058386903*uk_79 + 103*uk_8 + 502367977*uk_80 + 250047*uk_81 + 698544*uk_82 + 408807*uk_83 + 47628*uk_84 + 758079*uk_85 + 861273*uk_86 + 408807*uk_87 + 1951488*uk_88 + 1142064*uk_89 + 2242306609*uk_9 + 133056*uk_90 + 2117808*uk_91 + 2406096*uk_92 + 1142064*uk_93 + 668367*uk_94 + 77868*uk_95 + 1239399*uk_96 + 1408113*uk_97 + 668367*uk_98 + 9072*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 97272*uk_100 + 109368*uk_101 + 88704*uk_102 + 2346687*uk_103 + 2638503*uk_104 + 2139984*uk_105 + 2966607*uk_106 + 2406096*uk_107 + 1951488*uk_108 + 314432*uk_109 + 3220004*uk_11 + 813824*uk_110 + 36992*uk_111 + 892432*uk_112 + 1003408*uk_113 + 813824*uk_114 + 2106368*uk_115 + 95744*uk_116 + 2309824*uk_117 + 2597056*uk_118 + 2106368*uk_119 + 8334128*uk_12 + 4352*uk_120 + 104992*uk_121 + 118048*uk_122 + 95744*uk_123 + 2532932*uk_124 + 2847908*uk_125 + 2309824*uk_126 + 3202052*uk_127 + 2597056*uk_128 + 2106368*uk_129 + 378824*uk_13 + 5451776*uk_130 + 247808*uk_131 + 5978368*uk_132 + 6721792*uk_133 + 5451776*uk_134 + 11264*uk_135 + 271744*uk_136 + 305536*uk_137 + 247808*uk_138 + 6555824*uk_139 + 9139129*uk_14 + 7371056*uk_140 + 5978368*uk_141 + 8287664*uk_142 + 6721792*uk_143 + 5451776*uk_144 + 512*uk_145 + 12352*uk_146 + 13888*uk_147 + 11264*uk_148 + 297992*uk_149 + 10275601*uk_15 + 335048*uk_150 + 271744*uk_151 + 376712*uk_152 + 305536*uk_153 + 247808*uk_154 + 7189057*uk_155 + 8083033*uk_156 + 6555824*uk_157 + 9088177*uk_158 + 7371056*uk_159 + 8334128*uk_16 + 5978368*uk_160 + 10218313*uk_161 + 8287664*uk_162 + 6721792*uk_163 + 5451776*uk_164 + 3969*uk_17 + 4284*uk_18 + 11088*uk_19 + 63*uk_2 + 504*uk_20 + 12159*uk_21 + 13671*uk_22 + 11088*uk_23 + 4624*uk_24 + 11968*uk_25 + 544*uk_26 + 13124*uk_27 + 14756*uk_28 + 11968*uk_29 + 68*uk_3 + 30976*uk_30 + 1408*uk_31 + 33968*uk_32 + 38192*uk_33 + 30976*uk_34 + 64*uk_35 + 1544*uk_36 + 1736*uk_37 + 1408*uk_38 + 37249*uk_39 + 176*uk_4 + 41881*uk_40 + 33968*uk_41 + 47089*uk_42 + 38192*uk_43 + 30976*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 152476849412*uk_47 + 394645963184*uk_48 + 17938452872*uk_49 + 8*uk_5 + 432765175537*uk_50 + 486580534153*uk_51 + 394645963184*uk_52 + 187944057*uk_53 + 202860252*uk_54 + 525050064*uk_55 + 23865912*uk_56 + 575765127*uk_57 + 647362863*uk_58 + 525050064*uk_59 + 193*uk_6 + 218960272*uk_60 + 566720704*uk_61 + 25760032*uk_62 + 621460772*uk_63 + 698740868*uk_64 + 566720704*uk_65 + 1466806528*uk_66 + 66673024*uk_67 + 1608486704*uk_68 + 1808505776*uk_69 + 217*uk_7 + 1466806528*uk_70 + 3030592*uk_71 + 73113032*uk_72 + 82204808*uk_73 + 66673024*uk_74 + 1763851897*uk_75 + 1983190993*uk_76 + 1608486704*uk_77 + 2229805417*uk_78 + 1808505776*uk_79 + 176*uk_8 + 1466806528*uk_80 + 250047*uk_81 + 269892*uk_82 + 698544*uk_83 + 31752*uk_84 + 766017*uk_85 + 861273*uk_86 + 698544*uk_87 + 291312*uk_88 + 753984*uk_89 + 2242306609*uk_9 + 34272*uk_90 + 826812*uk_91 + 929628*uk_92 + 753984*uk_93 + 1951488*uk_94 + 88704*uk_95 + 2139984*uk_96 + 2406096*uk_97 + 1951488*uk_98 + 4032*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 147420*uk_100 + 164052*uk_101 + 51408*uk_102 + 2395575*uk_103 + 2665845*uk_104 + 835380*uk_105 + 2966607*uk_106 + 929628*uk_107 + 291312*uk_108 + 4330747*uk_109 + 7718539*uk_11 + 1806692*uk_110 + 318828*uk_111 + 5180955*uk_112 + 5765473*uk_113 + 1806692*uk_114 + 753712*uk_115 + 133008*uk_116 + 2161380*uk_117 + 2405228*uk_118 + 753712*uk_119 + 3220004*uk_12 + 23472*uk_120 + 381420*uk_121 + 424452*uk_122 + 133008*uk_123 + 6198075*uk_124 + 6897345*uk_125 + 2161380*uk_126 + 7675507*uk_127 + 2405228*uk_128 + 753712*uk_129 + 568236*uk_13 + 314432*uk_130 + 55488*uk_131 + 901680*uk_132 + 1003408*uk_133 + 314432*uk_134 + 9792*uk_135 + 159120*uk_136 + 177072*uk_137 + 55488*uk_138 + 2585700*uk_139 + 9233835*uk_14 + 2877420*uk_140 + 901680*uk_141 + 3202052*uk_142 + 1003408*uk_143 + 314432*uk_144 + 1728*uk_145 + 28080*uk_146 + 31248*uk_147 + 9792*uk_148 + 456300*uk_149 + 10275601*uk_15 + 507780*uk_150 + 159120*uk_151 + 565068*uk_152 + 177072*uk_153 + 55488*uk_154 + 7414875*uk_155 + 8251425*uk_156 + 2585700*uk_157 + 9182355*uk_158 + 2877420*uk_159 + 3220004*uk_16 + 901680*uk_160 + 10218313*uk_161 + 3202052*uk_162 + 1003408*uk_163 + 314432*uk_164 + 3969*uk_17 + 10269*uk_18 + 4284*uk_19 + 63*uk_2 + 756*uk_20 + 12285*uk_21 + 13671*uk_22 + 4284*uk_23 + 26569*uk_24 + 11084*uk_25 + 1956*uk_26 + 31785*uk_27 + 35371*uk_28 + 11084*uk_29 + 163*uk_3 + 4624*uk_30 + 816*uk_31 + 13260*uk_32 + 14756*uk_33 + 4624*uk_34 + 144*uk_35 + 2340*uk_36 + 2604*uk_37 + 816*uk_38 + 38025*uk_39 + 68*uk_4 + 42315*uk_40 + 13260*uk_41 + 47089*uk_42 + 14756*uk_43 + 4624*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 365495977267*uk_47 + 152476849412*uk_48 + 26907679308*uk_49 + 12*uk_5 + 437249788755*uk_50 + 486580534153*uk_51 + 152476849412*uk_52 + 187944057*uk_53 + 486267957*uk_54 + 202860252*uk_55 + 35798868*uk_56 + 581731605*uk_57 + 647362863*uk_58 + 202860252*uk_59 + 195*uk_6 + 1258121857*uk_60 + 524860652*uk_61 + 92622468*uk_62 + 1505115105*uk_63 + 1674922963*uk_64 + 524860652*uk_65 + 218960272*uk_66 + 38640048*uk_67 + 627900780*uk_68 + 698740868*uk_69 + 217*uk_7 + 218960272*uk_70 + 6818832*uk_71 + 110806020*uk_72 + 123307212*uk_73 + 38640048*uk_74 + 1800597825*uk_75 + 2003742195*uk_76 + 627900780*uk_77 + 2229805417*uk_78 + 698740868*uk_79 + 68*uk_8 + 218960272*uk_80 + 250047*uk_81 + 646947*uk_82 + 269892*uk_83 + 47628*uk_84 + 773955*uk_85 + 861273*uk_86 + 269892*uk_87 + 1673847*uk_88 + 698292*uk_89 + 2242306609*uk_9 + 123228*uk_90 + 2002455*uk_91 + 2228373*uk_92 + 698292*uk_93 + 291312*uk_94 + 51408*uk_95 + 835380*uk_96 + 929628*uk_97 + 291312*uk_98 + 9072*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 99288*uk_100 + 109368*uk_101 + 82152*uk_102 + 2444967*uk_103 + 2693187*uk_104 + 2022993*uk_105 + 2966607*uk_106 + 2228373*uk_107 + 1673847*uk_108 + 389017*uk_109 + 3456769*uk_11 + 868627*uk_110 + 42632*uk_111 + 1049813*uk_112 + 1156393*uk_113 + 868627*uk_114 + 1939537*uk_115 + 95192*uk_116 + 2344103*uk_117 + 2582083*uk_118 + 1939537*uk_119 + 7718539*uk_12 + 4672*uk_120 + 115048*uk_121 + 126728*uk_122 + 95192*uk_123 + 2833057*uk_124 + 3120677*uk_125 + 2344103*uk_126 + 3437497*uk_127 + 2582083*uk_128 + 1939537*uk_129 + 378824*uk_13 + 4330747*uk_130 + 212552*uk_131 + 5234093*uk_132 + 5765473*uk_133 + 4330747*uk_134 + 10432*uk_135 + 256888*uk_136 + 282968*uk_137 + 212552*uk_138 + 6325867*uk_139 + 9328541*uk_14 + 6968087*uk_140 + 5234093*uk_141 + 7675507*uk_142 + 5765473*uk_143 + 4330747*uk_144 + 512*uk_145 + 12608*uk_146 + 13888*uk_147 + 10432*uk_148 + 310472*uk_149 + 10275601*uk_15 + 341992*uk_150 + 256888*uk_151 + 376712*uk_152 + 282968*uk_153 + 212552*uk_154 + 7645373*uk_155 + 8421553*uk_156 + 6325867*uk_157 + 9276533*uk_158 + 6968087*uk_159 + 7718539*uk_16 + 5234093*uk_160 + 10218313*uk_161 + 7675507*uk_162 + 5765473*uk_163 + 4330747*uk_164 + 3969*uk_17 + 4599*uk_18 + 10269*uk_19 + 63*uk_2 + 504*uk_20 + 12411*uk_21 + 13671*uk_22 + 10269*uk_23 + 5329*uk_24 + 11899*uk_25 + 584*uk_26 + 14381*uk_27 + 15841*uk_28 + 11899*uk_29 + 73*uk_3 + 26569*uk_30 + 1304*uk_31 + 32111*uk_32 + 35371*uk_33 + 26569*uk_34 + 64*uk_35 + 1576*uk_36 + 1736*uk_37 + 1304*uk_38 + 38809*uk_39 + 163*uk_4 + 42749*uk_40 + 32111*uk_41 + 47089*uk_42 + 35371*uk_43 + 26569*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 163688382457*uk_47 + 365495977267*uk_48 + 17938452872*uk_49 + 8*uk_5 + 441734401973*uk_50 + 486580534153*uk_51 + 365495977267*uk_52 + 187944057*uk_53 + 217776447*uk_54 + 486267957*uk_55 + 23865912*uk_56 + 587698083*uk_57 + 647362863*uk_58 + 486267957*uk_59 + 197*uk_6 + 252344137*uk_60 + 563453347*uk_61 + 27654152*uk_62 + 680983493*uk_63 + 750118873*uk_64 + 563453347*uk_65 + 1258121857*uk_66 + 61748312*uk_67 + 1520552183*uk_68 + 1674922963*uk_69 + 217*uk_7 + 1258121857*uk_70 + 3030592*uk_71 + 74628328*uk_72 + 82204808*uk_73 + 61748312*uk_74 + 1837722577*uk_75 + 2024293397*uk_76 + 1520552183*uk_77 + 2229805417*uk_78 + 1674922963*uk_79 + 163*uk_8 + 1258121857*uk_80 + 250047*uk_81 + 289737*uk_82 + 646947*uk_83 + 31752*uk_84 + 781893*uk_85 + 861273*uk_86 + 646947*uk_87 + 335727*uk_88 + 749637*uk_89 + 2242306609*uk_9 + 36792*uk_90 + 906003*uk_91 + 997983*uk_92 + 749637*uk_93 + 1673847*uk_94 + 82152*uk_95 + 2022993*uk_96 + 2228373*uk_97 + 1673847*uk_98 + 4032*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 150444*uk_100 + 164052*uk_101 + 55188*uk_102 + 2494863*uk_103 + 2720529*uk_104 + 915201*uk_105 + 2966607*uk_106 + 997983*uk_107 + 335727*uk_108 + 6859000*uk_109 + 8997070*uk_11 + 2635300*uk_110 + 433200*uk_111 + 7183900*uk_112 + 7833700*uk_113 + 2635300*uk_114 + 1012510*uk_115 + 166440*uk_116 + 2760130*uk_117 + 3009790*uk_118 + 1012510*uk_119 + 3456769*uk_12 + 27360*uk_120 + 453720*uk_121 + 494760*uk_122 + 166440*uk_123 + 7524190*uk_124 + 8204770*uk_125 + 2760130*uk_126 + 8946910*uk_127 + 3009790*uk_128 + 1012510*uk_129 + 568236*uk_13 + 389017*uk_130 + 63948*uk_131 + 1060471*uk_132 + 1156393*uk_133 + 389017*uk_134 + 10512*uk_135 + 174324*uk_136 + 190092*uk_137 + 63948*uk_138 + 2890873*uk_139 + 9423247*uk_14 + 3152359*uk_140 + 1060471*uk_141 + 3437497*uk_142 + 1156393*uk_143 + 389017*uk_144 + 1728*uk_145 + 28656*uk_146 + 31248*uk_147 + 10512*uk_148 + 475212*uk_149 + 10275601*uk_15 + 518196*uk_150 + 174324*uk_151 + 565068*uk_152 + 190092*uk_153 + 63948*uk_154 + 7880599*uk_155 + 8593417*uk_156 + 2890873*uk_157 + 9370711*uk_158 + 3152359*uk_159 + 3456769*uk_16 + 1060471*uk_160 + 10218313*uk_161 + 3437497*uk_162 + 1156393*uk_163 + 389017*uk_164 + 3969*uk_17 + 11970*uk_18 + 4599*uk_19 + 63*uk_2 + 756*uk_20 + 12537*uk_21 + 13671*uk_22 + 4599*uk_23 + 36100*uk_24 + 13870*uk_25 + 2280*uk_26 + 37810*uk_27 + 41230*uk_28 + 13870*uk_29 + 190*uk_3 + 5329*uk_30 + 876*uk_31 + 14527*uk_32 + 15841*uk_33 + 5329*uk_34 + 144*uk_35 + 2388*uk_36 + 2604*uk_37 + 876*uk_38 + 39601*uk_39 + 73*uk_4 + 43183*uk_40 + 14527*uk_41 + 47089*uk_42 + 15841*uk_43 + 5329*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 426038255710*uk_47 + 163688382457*uk_48 + 26907679308*uk_49 + 12*uk_5 + 446219015191*uk_50 + 486580534153*uk_51 + 163688382457*uk_52 + 187944057*uk_53 + 566815410*uk_54 + 217776447*uk_55 + 35798868*uk_56 + 593664561*uk_57 + 647362863*uk_58 + 217776447*uk_59 + 199*uk_6 + 1709443300*uk_60 + 656786110*uk_61 + 107964840*uk_62 + 1790416930*uk_63 + 1952364190*uk_64 + 656786110*uk_65 + 252344137*uk_66 + 41481228*uk_67 + 687897031*uk_68 + 750118873*uk_69 + 217*uk_7 + 252344137*uk_70 + 6818832*uk_71 + 113078964*uk_72 + 123307212*uk_73 + 41481228*uk_74 + 1875226153*uk_75 + 2044844599*uk_76 + 687897031*uk_77 + 2229805417*uk_78 + 750118873*uk_79 + 73*uk_8 + 252344137*uk_80 + 250047*uk_81 + 754110*uk_82 + 289737*uk_83 + 47628*uk_84 + 789831*uk_85 + 861273*uk_86 + 289737*uk_87 + 2274300*uk_88 + 873810*uk_89 + 2242306609*uk_9 + 143640*uk_90 + 2382030*uk_91 + 2597490*uk_92 + 873810*uk_93 + 335727*uk_94 + 55188*uk_95 + 915201*uk_96 + 997983*uk_97 + 335727*uk_98 + 9072*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 101304*uk_100 + 109368*uk_101 + 95760*uk_102 + 2545263*uk_103 + 2747871*uk_104 + 2405970*uk_105 + 2966607*uk_106 + 2597490*uk_107 + 2274300*uk_108 + 1643032*uk_109 + 5587654*uk_11 + 2645560*uk_110 + 111392*uk_111 + 2798724*uk_112 + 3021508*uk_113 + 2645560*uk_114 + 4259800*uk_115 + 179360*uk_116 + 4506420*uk_117 + 4865140*uk_118 + 4259800*uk_119 + 8997070*uk_12 + 7552*uk_120 + 189744*uk_121 + 204848*uk_122 + 179360*uk_123 + 4767318*uk_124 + 5146806*uk_125 + 4506420*uk_126 + 5556502*uk_127 + 4865140*uk_128 + 4259800*uk_129 + 378824*uk_13 + 6859000*uk_130 + 288800*uk_131 + 7256100*uk_132 + 7833700*uk_133 + 6859000*uk_134 + 12160*uk_135 + 305520*uk_136 + 329840*uk_137 + 288800*uk_138 + 7676190*uk_139 + 9517953*uk_14 + 8287230*uk_140 + 7256100*uk_141 + 8946910*uk_142 + 7833700*uk_143 + 6859000*uk_144 + 512*uk_145 + 12864*uk_146 + 13888*uk_147 + 12160*uk_148 + 323208*uk_149 + 10275601*uk_15 + 348936*uk_150 + 305520*uk_151 + 376712*uk_152 + 329840*uk_153 + 288800*uk_154 + 8120601*uk_155 + 8767017*uk_156 + 7676190*uk_157 + 9464889*uk_158 + 8287230*uk_159 + 8997070*uk_16 + 7256100*uk_160 + 10218313*uk_161 + 8946910*uk_162 + 7833700*uk_163 + 6859000*uk_164 + 3969*uk_17 + 7434*uk_18 + 11970*uk_19 + 63*uk_2 + 504*uk_20 + 12663*uk_21 + 13671*uk_22 + 11970*uk_23 + 13924*uk_24 + 22420*uk_25 + 944*uk_26 + 23718*uk_27 + 25606*uk_28 + 22420*uk_29 + 118*uk_3 + 36100*uk_30 + 1520*uk_31 + 38190*uk_32 + 41230*uk_33 + 36100*uk_34 + 64*uk_35 + 1608*uk_36 + 1736*uk_37 + 1520*uk_38 + 40401*uk_39 + 190*uk_4 + 43617*uk_40 + 38190*uk_41 + 47089*uk_42 + 41230*uk_43 + 36100*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 264592179862*uk_47 + 426038255710*uk_48 + 17938452872*uk_49 + 8*uk_5 + 450703628409*uk_50 + 486580534153*uk_51 + 426038255710*uk_52 + 187944057*uk_53 + 352022202*uk_54 + 566815410*uk_55 + 23865912*uk_56 + 599631039*uk_57 + 647362863*uk_58 + 566815410*uk_59 + 201*uk_6 + 659343172*uk_60 + 1061654260*uk_61 + 44701232*uk_62 + 1123118454*uk_63 + 1212520918*uk_64 + 1061654260*uk_65 + 1709443300*uk_66 + 71976560*uk_67 + 1808411070*uk_68 + 1952364190*uk_69 + 217*uk_7 + 1709443300*uk_70 + 3030592*uk_71 + 76143624*uk_72 + 82204808*uk_73 + 71976560*uk_74 + 1913108553*uk_75 + 2065395801*uk_76 + 1808411070*uk_77 + 2229805417*uk_78 + 1952364190*uk_79 + 190*uk_8 + 1709443300*uk_80 + 250047*uk_81 + 468342*uk_82 + 754110*uk_83 + 31752*uk_84 + 797769*uk_85 + 861273*uk_86 + 754110*uk_87 + 877212*uk_88 + 1412460*uk_89 + 2242306609*uk_9 + 59472*uk_90 + 1494234*uk_91 + 1613178*uk_92 + 1412460*uk_93 + 2274300*uk_94 + 95760*uk_95 + 2405970*uk_96 + 2597490*uk_97 + 2274300*uk_98 + 4032*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 102312*uk_100 + 109368*uk_101 + 59472*uk_102 + 2596167*uk_103 + 2775213*uk_104 + 1509102*uk_105 + 2966607*uk_106 + 1613178*uk_107 + 877212*uk_108 + 157464*uk_109 + 2557062*uk_11 + 344088*uk_110 + 23328*uk_111 + 591948*uk_112 + 632772*uk_113 + 344088*uk_114 + 751896*uk_115 + 50976*uk_116 + 1293516*uk_117 + 1382724*uk_118 + 751896*uk_119 + 5587654*uk_12 + 3456*uk_120 + 87696*uk_121 + 93744*uk_122 + 50976*uk_123 + 2225286*uk_124 + 2378754*uk_125 + 1293516*uk_126 + 2542806*uk_127 + 1382724*uk_128 + 751896*uk_129 + 378824*uk_13 + 1643032*uk_130 + 111392*uk_131 + 2826572*uk_132 + 3021508*uk_133 + 1643032*uk_134 + 7552*uk_135 + 191632*uk_136 + 204848*uk_137 + 111392*uk_138 + 4862662*uk_139 + 9612659*uk_14 + 5198018*uk_140 + 2826572*uk_141 + 5556502*uk_142 + 3021508*uk_143 + 1643032*uk_144 + 512*uk_145 + 12992*uk_146 + 13888*uk_147 + 7552*uk_148 + 329672*uk_149 + 10275601*uk_15 + 352408*uk_150 + 191632*uk_151 + 376712*uk_152 + 204848*uk_153 + 111392*uk_154 + 8365427*uk_155 + 8942353*uk_156 + 4862662*uk_157 + 9559067*uk_158 + 5198018*uk_159 + 5587654*uk_16 + 2826572*uk_160 + 10218313*uk_161 + 5556502*uk_162 + 3021508*uk_163 + 1643032*uk_164 + 3969*uk_17 + 3402*uk_18 + 7434*uk_19 + 63*uk_2 + 504*uk_20 + 12789*uk_21 + 13671*uk_22 + 7434*uk_23 + 2916*uk_24 + 6372*uk_25 + 432*uk_26 + 10962*uk_27 + 11718*uk_28 + 6372*uk_29 + 54*uk_3 + 13924*uk_30 + 944*uk_31 + 23954*uk_32 + 25606*uk_33 + 13924*uk_34 + 64*uk_35 + 1624*uk_36 + 1736*uk_37 + 944*uk_38 + 41209*uk_39 + 118*uk_4 + 44051*uk_40 + 23954*uk_41 + 47089*uk_42 + 25606*uk_43 + 13924*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 121084556886*uk_47 + 264592179862*uk_48 + 17938452872*uk_49 + 8*uk_5 + 455188241627*uk_50 + 486580534153*uk_51 + 264592179862*uk_52 + 187944057*uk_53 + 161094906*uk_54 + 352022202*uk_55 + 23865912*uk_56 + 605597517*uk_57 + 647362863*uk_58 + 352022202*uk_59 + 203*uk_6 + 138081348*uk_60 + 301733316*uk_61 + 20456496*uk_62 + 519083586*uk_63 + 554882454*uk_64 + 301733316*uk_65 + 659343172*uk_66 + 44701232*uk_67 + 1134293762*uk_68 + 1212520918*uk_69 + 217*uk_7 + 659343172*uk_70 + 3030592*uk_71 + 76901272*uk_72 + 82204808*uk_73 + 44701232*uk_74 + 1951369777*uk_75 + 2085947003*uk_76 + 1134293762*uk_77 + 2229805417*uk_78 + 1212520918*uk_79 + 118*uk_8 + 659343172*uk_80 + 250047*uk_81 + 214326*uk_82 + 468342*uk_83 + 31752*uk_84 + 805707*uk_85 + 861273*uk_86 + 468342*uk_87 + 183708*uk_88 + 401436*uk_89 + 2242306609*uk_9 + 27216*uk_90 + 690606*uk_91 + 738234*uk_92 + 401436*uk_93 + 877212*uk_94 + 59472*uk_95 + 1509102*uk_96 + 1613178*uk_97 + 877212*uk_98 + 4032*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 154980*uk_100 + 164052*uk_101 + 40824*uk_102 + 2647575*uk_103 + 2802555*uk_104 + 697410*uk_105 + 2966607*uk_106 + 738234*uk_107 + 183708*uk_108 + 8365427*uk_109 + 9612659*uk_11 + 2225286*uk_110 + 494508*uk_111 + 8447845*uk_112 + 8942353*uk_113 + 2225286*uk_114 + 591948*uk_115 + 131544*uk_116 + 2247210*uk_117 + 2378754*uk_118 + 591948*uk_119 + 2557062*uk_12 + 29232*uk_120 + 499380*uk_121 + 528612*uk_122 + 131544*uk_123 + 8531075*uk_124 + 9030455*uk_125 + 2247210*uk_126 + 9559067*uk_127 + 2378754*uk_128 + 591948*uk_129 + 568236*uk_13 + 157464*uk_130 + 34992*uk_131 + 597780*uk_132 + 632772*uk_133 + 157464*uk_134 + 7776*uk_135 + 132840*uk_136 + 140616*uk_137 + 34992*uk_138 + 2269350*uk_139 + 9707365*uk_14 + 2402190*uk_140 + 597780*uk_141 + 2542806*uk_142 + 632772*uk_143 + 157464*uk_144 + 1728*uk_145 + 29520*uk_146 + 31248*uk_147 + 7776*uk_148 + 504300*uk_149 + 10275601*uk_15 + 533820*uk_150 + 132840*uk_151 + 565068*uk_152 + 140616*uk_153 + 34992*uk_154 + 8615125*uk_155 + 9119425*uk_156 + 2269350*uk_157 + 9653245*uk_158 + 2402190*uk_159 + 2557062*uk_16 + 597780*uk_160 + 10218313*uk_161 + 2542806*uk_162 + 632772*uk_163 + 157464*uk_164 + 3969*uk_17 + 12789*uk_18 + 3402*uk_19 + 63*uk_2 + 756*uk_20 + 12915*uk_21 + 13671*uk_22 + 3402*uk_23 + 41209*uk_24 + 10962*uk_25 + 2436*uk_26 + 41615*uk_27 + 44051*uk_28 + 10962*uk_29 + 203*uk_3 + 2916*uk_30 + 648*uk_31 + 11070*uk_32 + 11718*uk_33 + 2916*uk_34 + 144*uk_35 + 2460*uk_36 + 2604*uk_37 + 648*uk_38 + 42025*uk_39 + 54*uk_4 + 44485*uk_40 + 11070*uk_41 + 47089*uk_42 + 11718*uk_43 + 2916*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 455188241627*uk_47 + 121084556886*uk_48 + 26907679308*uk_49 + 12*uk_5 + 459672854845*uk_50 + 486580534153*uk_51 + 121084556886*uk_52 + 187944057*uk_53 + 605597517*uk_54 + 161094906*uk_55 + 35798868*uk_56 + 611563995*uk_57 + 647362863*uk_58 + 161094906*uk_59 + 205*uk_6 + 1951369777*uk_60 + 519083586*uk_61 + 115351908*uk_62 + 1970595095*uk_63 + 2085947003*uk_64 + 519083586*uk_65 + 138081348*uk_66 + 30684744*uk_67 + 524197710*uk_68 + 554882454*uk_69 + 217*uk_7 + 138081348*uk_70 + 6818832*uk_71 + 116488380*uk_72 + 123307212*uk_73 + 30684744*uk_74 + 1990009825*uk_75 + 2106498205*uk_76 + 524197710*uk_77 + 2229805417*uk_78 + 554882454*uk_79 + 54*uk_8 + 138081348*uk_80 + 250047*uk_81 + 805707*uk_82 + 214326*uk_83 + 47628*uk_84 + 813645*uk_85 + 861273*uk_86 + 214326*uk_87 + 2596167*uk_88 + 690606*uk_89 + 2242306609*uk_9 + 153468*uk_90 + 2621745*uk_91 + 2775213*uk_92 + 690606*uk_93 + 183708*uk_94 + 40824*uk_95 + 697410*uk_96 + 738234*uk_97 + 183708*uk_98 + 9072*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 104328*uk_100 + 109368*uk_101 + 102312*uk_102 + 2699487*uk_103 + 2829897*uk_104 + 2647323*uk_105 + 2966607*uk_106 + 2775213*uk_107 + 2596167*uk_108 + 3869893*uk_109 + 7434421*uk_11 + 5003747*uk_110 + 197192*uk_111 + 5102343*uk_112 + 5348833*uk_113 + 5003747*uk_114 + 6469813*uk_115 + 254968*uk_116 + 6597297*uk_117 + 6916007*uk_118 + 6469813*uk_119 + 9612659*uk_12 + 10048*uk_120 + 259992*uk_121 + 272552*uk_122 + 254968*uk_123 + 6727293*uk_124 + 7052283*uk_125 + 6597297*uk_126 + 7392973*uk_127 + 6916007*uk_128 + 6469813*uk_129 + 378824*uk_13 + 8365427*uk_130 + 329672*uk_131 + 8530263*uk_132 + 8942353*uk_133 + 8365427*uk_134 + 12992*uk_135 + 336168*uk_136 + 352408*uk_137 + 329672*uk_138 + 8698347*uk_139 + 9802071*uk_14 + 9118557*uk_140 + 8530263*uk_141 + 9559067*uk_142 + 8942353*uk_143 + 8365427*uk_144 + 512*uk_145 + 13248*uk_146 + 13888*uk_147 + 12992*uk_148 + 342792*uk_149 + 10275601*uk_15 + 359352*uk_150 + 336168*uk_151 + 376712*uk_152 + 352408*uk_153 + 329672*uk_154 + 8869743*uk_155 + 9298233*uk_156 + 8698347*uk_157 + 9747423*uk_158 + 9118557*uk_159 + 9612659*uk_16 + 8530263*uk_160 + 10218313*uk_161 + 9559067*uk_162 + 8942353*uk_163 + 8365427*uk_164 + 3969*uk_17 + 9891*uk_18 + 12789*uk_19 + 63*uk_2 + 504*uk_20 + 13041*uk_21 + 13671*uk_22 + 12789*uk_23 + 24649*uk_24 + 31871*uk_25 + 1256*uk_26 + 32499*uk_27 + 34069*uk_28 + 31871*uk_29 + 157*uk_3 + 41209*uk_30 + 1624*uk_31 + 42021*uk_32 + 44051*uk_33 + 41209*uk_34 + 64*uk_35 + 1656*uk_36 + 1736*uk_37 + 1624*uk_38 + 42849*uk_39 + 203*uk_4 + 44919*uk_40 + 42021*uk_41 + 47089*uk_42 + 44051*uk_43 + 41209*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 352042137613*uk_47 + 455188241627*uk_48 + 17938452872*uk_49 + 8*uk_5 + 464157468063*uk_50 + 486580534153*uk_51 + 455188241627*uk_52 + 187944057*uk_53 + 468368523*uk_54 + 605597517*uk_55 + 23865912*uk_56 + 617530473*uk_57 + 647362863*uk_58 + 605597517*uk_59 + 207*uk_6 + 1167204097*uk_60 + 1509187463*uk_61 + 59475368*uk_62 + 1538925147*uk_63 + 1613269357*uk_64 + 1509187463*uk_65 + 1951369777*uk_66 + 76901272*uk_67 + 1989820413*uk_68 + 2085947003*uk_69 + 217*uk_7 + 1951369777*uk_70 + 3030592*uk_71 + 78416568*uk_72 + 82204808*uk_73 + 76901272*uk_74 + 2029028697*uk_75 + 2127049407*uk_76 + 1989820413*uk_77 + 2229805417*uk_78 + 2085947003*uk_79 + 203*uk_8 + 1951369777*uk_80 + 250047*uk_81 + 623133*uk_82 + 805707*uk_83 + 31752*uk_84 + 821583*uk_85 + 861273*uk_86 + 805707*uk_87 + 1552887*uk_88 + 2007873*uk_89 + 2242306609*uk_9 + 79128*uk_90 + 2047437*uk_91 + 2146347*uk_92 + 2007873*uk_93 + 2596167*uk_94 + 102312*uk_95 + 2647323*uk_96 + 2775213*uk_97 + 2596167*uk_98 + 4032*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 105336*uk_100 + 109368*uk_101 + 79128*uk_102 + 2751903*uk_103 + 2857239*uk_104 + 2067219*uk_105 + 2966607*uk_106 + 2146347*uk_107 + 1552887*uk_108 + 1685159*uk_109 + 5635007*uk_11 + 2223277*uk_110 + 113288*uk_111 + 2959649*uk_112 + 3072937*uk_113 + 2223277*uk_114 + 2933231*uk_115 + 149464*uk_116 + 3904747*uk_117 + 4054211*uk_118 + 2933231*uk_119 + 7434421*uk_12 + 7616*uk_120 + 198968*uk_121 + 206584*uk_122 + 149464*uk_123 + 5198039*uk_124 + 5397007*uk_125 + 3904747*uk_126 + 5603591*uk_127 + 4054211*uk_128 + 2933231*uk_129 + 378824*uk_13 + 3869893*uk_130 + 197192*uk_131 + 5151641*uk_132 + 5348833*uk_133 + 3869893*uk_134 + 10048*uk_135 + 262504*uk_136 + 272552*uk_137 + 197192*uk_138 + 6857917*uk_139 + 9896777*uk_14 + 7120421*uk_140 + 5151641*uk_141 + 7392973*uk_142 + 5348833*uk_143 + 3869893*uk_144 + 512*uk_145 + 13376*uk_146 + 13888*uk_147 + 10048*uk_148 + 349448*uk_149 + 10275601*uk_15 + 362824*uk_150 + 262504*uk_151 + 376712*uk_152 + 272552*uk_153 + 197192*uk_154 + 9129329*uk_155 + 9478777*uk_156 + 6857917*uk_157 + 9841601*uk_158 + 7120421*uk_159 + 7434421*uk_16 + 5151641*uk_160 + 10218313*uk_161 + 7392973*uk_162 + 5348833*uk_163 + 3869893*uk_164 + 3969*uk_17 + 7497*uk_18 + 9891*uk_19 + 63*uk_2 + 504*uk_20 + 13167*uk_21 + 13671*uk_22 + 9891*uk_23 + 14161*uk_24 + 18683*uk_25 + 952*uk_26 + 24871*uk_27 + 25823*uk_28 + 18683*uk_29 + 119*uk_3 + 24649*uk_30 + 1256*uk_31 + 32813*uk_32 + 34069*uk_33 + 24649*uk_34 + 64*uk_35 + 1672*uk_36 + 1736*uk_37 + 1256*uk_38 + 43681*uk_39 + 157*uk_4 + 45353*uk_40 + 32813*uk_41 + 47089*uk_42 + 34069*uk_43 + 24649*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 266834486471*uk_47 + 352042137613*uk_48 + 17938452872*uk_49 + 8*uk_5 + 468642081281*uk_50 + 486580534153*uk_51 + 352042137613*uk_52 + 187944057*uk_53 + 355005441*uk_54 + 468368523*uk_55 + 23865912*uk_56 + 623496951*uk_57 + 647362863*uk_58 + 468368523*uk_59 + 209*uk_6 + 670565833*uk_60 + 884696099*uk_61 + 45080056*uk_62 + 1177716463*uk_63 + 1222796519*uk_64 + 884696099*uk_65 + 1167204097*uk_66 + 59475368*uk_67 + 1553793989*uk_68 + 1613269357*uk_69 + 217*uk_7 + 1167204097*uk_70 + 3030592*uk_71 + 79174216*uk_72 + 82204808*uk_73 + 59475368*uk_74 + 2068426393*uk_75 + 2147600609*uk_76 + 1553793989*uk_77 + 2229805417*uk_78 + 1613269357*uk_79 + 157*uk_8 + 1167204097*uk_80 + 250047*uk_81 + 472311*uk_82 + 623133*uk_83 + 31752*uk_84 + 829521*uk_85 + 861273*uk_86 + 623133*uk_87 + 892143*uk_88 + 1177029*uk_89 + 2242306609*uk_9 + 59976*uk_90 + 1566873*uk_91 + 1626849*uk_92 + 1177029*uk_93 + 1552887*uk_94 + 79128*uk_95 + 2067219*uk_96 + 2146347*uk_97 + 1552887*uk_98 + 4032*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 106344*uk_100 + 109368*uk_101 + 59976*uk_102 + 2804823*uk_103 + 2884581*uk_104 + 1581867*uk_105 + 2966607*uk_106 + 1626849*uk_107 + 892143*uk_108 + 704969*uk_109 + 4214417*uk_11 + 942599*uk_110 + 63368*uk_111 + 1671331*uk_112 + 1718857*uk_113 + 942599*uk_114 + 1260329*uk_115 + 84728*uk_116 + 2234701*uk_117 + 2298247*uk_118 + 1260329*uk_119 + 5635007*uk_12 + 5696*uk_120 + 150232*uk_121 + 154504*uk_122 + 84728*uk_123 + 3962369*uk_124 + 4075043*uk_125 + 2234701*uk_126 + 4190921*uk_127 + 2298247*uk_128 + 1260329*uk_129 + 378824*uk_13 + 1685159*uk_130 + 113288*uk_131 + 2987971*uk_132 + 3072937*uk_133 + 1685159*uk_134 + 7616*uk_135 + 200872*uk_136 + 206584*uk_137 + 113288*uk_138 + 5297999*uk_139 + 9991483*uk_14 + 5448653*uk_140 + 2987971*uk_141 + 5603591*uk_142 + 3072937*uk_143 + 1685159*uk_144 + 512*uk_145 + 13504*uk_146 + 13888*uk_147 + 7616*uk_148 + 356168*uk_149 + 10275601*uk_15 + 366296*uk_150 + 200872*uk_151 + 376712*uk_152 + 206584*uk_153 + 113288*uk_154 + 9393931*uk_155 + 9661057*uk_156 + 5297999*uk_157 + 9935779*uk_158 + 5448653*uk_159 + 5635007*uk_16 + 2987971*uk_160 + 10218313*uk_161 + 5603591*uk_162 + 3072937*uk_163 + 1685159*uk_164 + 3969*uk_17 + 5607*uk_18 + 7497*uk_19 + 63*uk_2 + 504*uk_20 + 13293*uk_21 + 13671*uk_22 + 7497*uk_23 + 7921*uk_24 + 10591*uk_25 + 712*uk_26 + 18779*uk_27 + 19313*uk_28 + 10591*uk_29 + 89*uk_3 + 14161*uk_30 + 952*uk_31 + 25109*uk_32 + 25823*uk_33 + 14161*uk_34 + 64*uk_35 + 1688*uk_36 + 1736*uk_37 + 952*uk_38 + 44521*uk_39 + 119*uk_4 + 45787*uk_40 + 25109*uk_41 + 47089*uk_42 + 25823*uk_43 + 14161*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 199565288201*uk_47 + 266834486471*uk_48 + 17938452872*uk_49 + 8*uk_5 + 473126694499*uk_50 + 486580534153*uk_51 + 266834486471*uk_52 + 187944057*uk_53 + 265508271*uk_54 + 355005441*uk_55 + 23865912*uk_56 + 629463429*uk_57 + 647362863*uk_58 + 355005441*uk_59 + 211*uk_6 + 375083113*uk_60 + 501515623*uk_61 + 33715336*uk_62 + 889241987*uk_63 + 914528489*uk_64 + 501515623*uk_65 + 670565833*uk_66 + 45080056*uk_67 + 1188986477*uk_68 + 1222796519*uk_69 + 217*uk_7 + 670565833*uk_70 + 3030592*uk_71 + 79931864*uk_72 + 82204808*uk_73 + 45080056*uk_74 + 2108202913*uk_75 + 2168151811*uk_76 + 1188986477*uk_77 + 2229805417*uk_78 + 1222796519*uk_79 + 119*uk_8 + 670565833*uk_80 + 250047*uk_81 + 353241*uk_82 + 472311*uk_83 + 31752*uk_84 + 837459*uk_85 + 861273*uk_86 + 472311*uk_87 + 499023*uk_88 + 667233*uk_89 + 2242306609*uk_9 + 44856*uk_90 + 1183077*uk_91 + 1216719*uk_92 + 667233*uk_93 + 892143*uk_94 + 59976*uk_95 + 1581867*uk_96 + 1626849*uk_97 + 892143*uk_98 + 4032*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 107352*uk_100 + 109368*uk_101 + 44856*uk_102 + 2858247*uk_103 + 2911923*uk_104 + 1194291*uk_105 + 2966607*uk_106 + 1216719*uk_107 + 499023*uk_108 + 300763*uk_109 + 3172651*uk_11 + 399521*uk_110 + 35912*uk_111 + 956157*uk_112 + 974113*uk_113 + 399521*uk_114 + 530707*uk_115 + 47704*uk_116 + 1270119*uk_117 + 1293971*uk_118 + 530707*uk_119 + 4214417*uk_12 + 4288*uk_120 + 114168*uk_121 + 116312*uk_122 + 47704*uk_123 + 3039723*uk_124 + 3096807*uk_125 + 1270119*uk_126 + 3154963*uk_127 + 1293971*uk_128 + 530707*uk_129 + 378824*uk_13 + 704969*uk_130 + 63368*uk_131 + 1687173*uk_132 + 1718857*uk_133 + 704969*uk_134 + 5696*uk_135 + 151656*uk_136 + 154504*uk_137 + 63368*uk_138 + 4037841*uk_139 + 10086189*uk_14 + 4113669*uk_140 + 1687173*uk_141 + 4190921*uk_142 + 1718857*uk_143 + 704969*uk_144 + 512*uk_145 + 13632*uk_146 + 13888*uk_147 + 5696*uk_148 + 362952*uk_149 + 10275601*uk_15 + 369768*uk_150 + 151656*uk_151 + 376712*uk_152 + 154504*uk_153 + 63368*uk_154 + 9663597*uk_155 + 9845073*uk_156 + 4037841*uk_157 + 10029957*uk_158 + 4113669*uk_159 + 4214417*uk_16 + 1687173*uk_160 + 10218313*uk_161 + 4190921*uk_162 + 1718857*uk_163 + 704969*uk_164 + 3969*uk_17 + 4221*uk_18 + 5607*uk_19 + 63*uk_2 + 504*uk_20 + 13419*uk_21 + 13671*uk_22 + 5607*uk_23 + 4489*uk_24 + 5963*uk_25 + 536*uk_26 + 14271*uk_27 + 14539*uk_28 + 5963*uk_29 + 67*uk_3 + 7921*uk_30 + 712*uk_31 + 18957*uk_32 + 19313*uk_33 + 7921*uk_34 + 64*uk_35 + 1704*uk_36 + 1736*uk_37 + 712*uk_38 + 45369*uk_39 + 89*uk_4 + 46221*uk_40 + 18957*uk_41 + 47089*uk_42 + 19313*uk_43 + 7921*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 150234542803*uk_47 + 199565288201*uk_48 + 17938452872*uk_49 + 8*uk_5 + 477611307717*uk_50 + 486580534153*uk_51 + 199565288201*uk_52 + 187944057*uk_53 + 199877013*uk_54 + 265508271*uk_55 + 23865912*uk_56 + 635429907*uk_57 + 647362863*uk_58 + 265508271*uk_59 + 213*uk_6 + 212567617*uk_60 + 282365939*uk_61 + 25381208*uk_62 + 675774663*uk_63 + 688465267*uk_64 + 282365939*uk_65 + 375083113*uk_66 + 33715336*uk_67 + 897670821*uk_68 + 914528489*uk_69 + 217*uk_7 + 375083113*uk_70 + 3030592*uk_71 + 80689512*uk_72 + 82204808*uk_73 + 33715336*uk_74 + 2148358257*uk_75 + 2188703013*uk_76 + 897670821*uk_77 + 2229805417*uk_78 + 914528489*uk_79 + 89*uk_8 + 375083113*uk_80 + 250047*uk_81 + 265923*uk_82 + 353241*uk_83 + 31752*uk_84 + 845397*uk_85 + 861273*uk_86 + 353241*uk_87 + 282807*uk_88 + 375669*uk_89 + 2242306609*uk_9 + 33768*uk_90 + 899073*uk_91 + 915957*uk_92 + 375669*uk_93 + 499023*uk_94 + 44856*uk_95 + 1194291*uk_96 + 1216719*uk_97 + 499023*uk_98 + 4032*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 108360*uk_100 + 109368*uk_101 + 33768*uk_102 + 2912175*uk_103 + 2939265*uk_104 + 907515*uk_105 + 2966607*uk_106 + 915957*uk_107 + 282807*uk_108 + 148877*uk_109 + 2509709*uk_11 + 188203*uk_110 + 22472*uk_111 + 603935*uk_112 + 609553*uk_113 + 188203*uk_114 + 237917*uk_115 + 28408*uk_116 + 763465*uk_117 + 770567*uk_118 + 237917*uk_119 + 3172651*uk_12 + 3392*uk_120 + 91160*uk_121 + 92008*uk_122 + 28408*uk_123 + 2449925*uk_124 + 2472715*uk_125 + 763465*uk_126 + 2495717*uk_127 + 770567*uk_128 + 237917*uk_129 + 378824*uk_13 + 300763*uk_130 + 35912*uk_131 + 965135*uk_132 + 974113*uk_133 + 300763*uk_134 + 4288*uk_135 + 115240*uk_136 + 116312*uk_137 + 35912*uk_138 + 3097075*uk_139 + 10180895*uk_14 + 3125885*uk_140 + 965135*uk_141 + 3154963*uk_142 + 974113*uk_143 + 300763*uk_144 + 512*uk_145 + 13760*uk_146 + 13888*uk_147 + 4288*uk_148 + 369800*uk_149 + 10275601*uk_15 + 373240*uk_150 + 115240*uk_151 + 376712*uk_152 + 116312*uk_153 + 35912*uk_154 + 9938375*uk_155 + 10030825*uk_156 + 3097075*uk_157 + 10124135*uk_158 + 3125885*uk_159 + 3172651*uk_16 + 965135*uk_160 + 10218313*uk_161 + 3154963*uk_162 + 974113*uk_163 + 300763*uk_164 + 3969*uk_17 + 3339*uk_18 + 4221*uk_19 + 63*uk_2 + 504*uk_20 + 13545*uk_21 + 13671*uk_22 + 4221*uk_23 + 2809*uk_24 + 3551*uk_25 + 424*uk_26 + 11395*uk_27 + 11501*uk_28 + 3551*uk_29 + 53*uk_3 + 4489*uk_30 + 536*uk_31 + 14405*uk_32 + 14539*uk_33 + 4489*uk_34 + 64*uk_35 + 1720*uk_36 + 1736*uk_37 + 536*uk_38 + 46225*uk_39 + 67*uk_4 + 46655*uk_40 + 14405*uk_41 + 47089*uk_42 + 14539*uk_43 + 4489*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 118842250277*uk_47 + 150234542803*uk_48 + 17938452872*uk_49 + 8*uk_5 + 482095920935*uk_50 + 486580534153*uk_51 + 150234542803*uk_52 + 187944057*uk_53 + 158111667*uk_54 + 199877013*uk_55 + 23865912*uk_56 + 641396385*uk_57 + 647362863*uk_58 + 199877013*uk_59 + 215*uk_6 + 133014577*uk_60 + 168150503*uk_61 + 20077672*uk_62 + 539587435*uk_63 + 544606853*uk_64 + 168150503*uk_65 + 212567617*uk_66 + 25381208*uk_67 + 682119965*uk_68 + 688465267*uk_69 + 217*uk_7 + 212567617*uk_70 + 3030592*uk_71 + 81447160*uk_72 + 82204808*uk_73 + 25381208*uk_74 + 2188892425*uk_75 + 2209254215*uk_76 + 682119965*uk_77 + 2229805417*uk_78 + 688465267*uk_79 + 67*uk_8 + 212567617*uk_80 + 250047*uk_81 + 210357*uk_82 + 265923*uk_83 + 31752*uk_84 + 853335*uk_85 + 861273*uk_86 + 265923*uk_87 + 176967*uk_88 + 223713*uk_89 + 2242306609*uk_9 + 26712*uk_90 + 717885*uk_91 + 724563*uk_92 + 223713*uk_93 + 282807*uk_94 + 33768*uk_95 + 907515*uk_96 + 915957*uk_97 + 282807*uk_98 + 4032*uk_99, uk_0 + 47353*uk_1 + 2983239*uk_10 + 109368*uk_100 + 109368*uk_101 + 26712*uk_102 + 2966607*uk_103 + 2966607*uk_104 + 724563*uk_105 + 2966607*uk_106 + 724563*uk_107 + 176967*uk_108 + 103823*uk_109 + 2225591*uk_11 + 117077*uk_110 + 17672*uk_111 + 479353*uk_112 + 479353*uk_113 + 117077*uk_114 + 132023*uk_115 + 19928*uk_116 + 540547*uk_117 + 540547*uk_118 + 132023*uk_119 + 2509709*uk_12 + 3008*uk_120 + 81592*uk_121 + 81592*uk_122 + 19928*uk_123 + 2213183*uk_124 + 2213183*uk_125 + 540547*uk_126 + 2213183*uk_127 + 540547*uk_128 + 132023*uk_129 + 378824*uk_13 + 148877*uk_130 + 22472*uk_131 + 609553*uk_132 + 609553*uk_133 + 148877*uk_134 + 3392*uk_135 + 92008*uk_136 + 92008*uk_137 + 22472*uk_138 + 2495717*uk_139 + 10275601*uk_14 + 2495717*uk_140 + 609553*uk_141 + 2495717*uk_142 + 609553*uk_143 + 148877*uk_144 + 512*uk_145 + 13888*uk_146 + 13888*uk_147 + 3392*uk_148 + 376712*uk_149 + 10275601*uk_15 + 376712*uk_150 + 92008*uk_151 + 376712*uk_152 + 92008*uk_153 + 22472*uk_154 + 10218313*uk_155 + 10218313*uk_156 + 2495717*uk_157 + 10218313*uk_158 + 2495717*uk_159 + 2509709*uk_16 + 609553*uk_160 + 10218313*uk_161 + 2495717*uk_162 + 609553*uk_163 + 148877*uk_164 + 3969*uk_17 + 2961*uk_18 + 3339*uk_19 + 63*uk_2 + 504*uk_20 + 13671*uk_21 + 13671*uk_22 + 3339*uk_23 + 2209*uk_24 + 2491*uk_25 + 376*uk_26 + 10199*uk_27 + 10199*uk_28 + 2491*uk_29 + 47*uk_3 + 2809*uk_30 + 424*uk_31 + 11501*uk_32 + 11501*uk_33 + 2809*uk_34 + 64*uk_35 + 1736*uk_36 + 1736*uk_37 + 424*uk_38 + 47089*uk_39 + 53*uk_4 + 47089*uk_40 + 11501*uk_41 + 47089*uk_42 + 11501*uk_43 + 2809*uk_44 + 106179944855977*uk_45 + 141265316367*uk_46 + 105388410623*uk_47 + 118842250277*uk_48 + 17938452872*uk_49 + 8*uk_5 + 486580534153*uk_50 + 486580534153*uk_51 + 118842250277*uk_52 + 187944057*uk_53 + 140212233*uk_54 + 158111667*uk_55 + 23865912*uk_56 + 647362863*uk_57 + 647362863*uk_58 + 158111667*uk_59 + 217*uk_6 + 104602777*uk_60 + 117956323*uk_61 + 17804728*uk_62 + 482953247*uk_63 + 482953247*uk_64 + 117956323*uk_65 + 133014577*uk_66 + 20077672*uk_67 + 544606853*uk_68 + 544606853*uk_69 + 217*uk_7 + 133014577*uk_70 + 3030592*uk_71 + 82204808*uk_72 + 82204808*uk_73 + 20077672*uk_74 + 2229805417*uk_75 + 2229805417*uk_76 + 544606853*uk_77 + 2229805417*uk_78 + 544606853*uk_79 + 53*uk_8 + 133014577*uk_80 + 250047*uk_81 + 186543*uk_82 + 210357*uk_83 + 31752*uk_84 + 861273*uk_85 + 861273*uk_86 + 210357*uk_87 + 139167*uk_88 + 156933*uk_89 + 2242306609*uk_9 + 23688*uk_90 + 642537*uk_91 + 642537*uk_92 + 156933*uk_93 + 176967*uk_94 + 26712*uk_95 + 724563*uk_96 + 724563*uk_97 + 176967*uk_98 + 4032*uk_99, ] def sol_165x165(): return { uk_0: -QQ(295441,1683)*uk_2 - QQ(175799,1683)*uk_7 + QQ(2401696807,1)*uk_9 - QQ(9606787228,1683)*uk_10 + QQ(9606787228,1683)*uk_15 - QQ(29030443,1683)*uk_17 - QQ(5965893,187)*uk_22 + QQ(262901,99)*uk_42 + QQ(235539209256104,1)*uk_45 - QQ(232597130667529,1683)*uk_46 + QQ(1364372733998209,1683)*uk_51 - QQ(1133600892904,1683)*uk_53 - QQ(172922170104,187)*uk_58 + QQ(249776467928,99)*uk_78 - QQ(2401889209,1683)*uk_81 - QQ(636292759,187)*uk_86 - QQ(1034157281,187)*uk_106 + QQ(10558824289,1683)*uk_161, uk_1: QQ(4,1683)*uk_2 - QQ(4,1683)*uk_7 - QQ(98072,1)*uk_9 + QQ(96847,1683)*uk_10 - QQ(568087,1683)*uk_15 + QQ(472,1683)*uk_17 + QQ(72,187)*uk_22 - QQ(104,99)*uk_42 - QQ(7216420377,1)*uk_45 - QQ(108808244,1683)*uk_46 - QQ(46106641036,1683)*uk_51 + QQ(17259541,1683)*uk_53 + QQ(1095291,187)*uk_58 - QQ(9936587,99)*uk_78 + QQ(41836,1683)*uk_81 + QQ(10036,187)*uk_86 + QQ(10124,187)*uk_106 - QQ(8,1)*uk_149 - QQ(586156,1683)*uk_161, uk_3: -QQ(295441,1683)*uk_18 - QQ(175799,1683)*uk_28 + QQ(2401696807,1)*uk_47 - QQ(9606787228,1683)*uk_54 + QQ(9606787228,1683)*uk_64 - QQ(29030443,1683)*uk_82 - QQ(5965893,187)*uk_92 + QQ(262901,99)*uk_127 + QQ(8,1)*uk_149, uk_4: -QQ(295441,1683)*uk_19 + QQ(1602583,3366)*uk_29 - QQ(175799,1683)*uk_33 - QQ(45670,99)*uk_34 - QQ(76006,187)*uk_38 + QQ(295441,1683)*uk_41 - QQ(45670,99)*uk_44 + QQ(2401696807,1)*uk_48 - QQ(9606787228,1683)*uk_55 + QQ(74452601017,3366)*uk_65 + QQ(9606787228,1683)*uk_69 - QQ(2401696807,99)*uk_70 - QQ(4803393614,187)*uk_74 + QQ(9606787228,1683)*uk_77 - QQ(2401696807,99)*uk_80 - QQ(29030443,1683)*uk_83 + QQ(11596905,374)*uk_93 - QQ(5965893,187)*uk_97 - QQ(769658,33)*uk_98 - QQ(17335370,1683)*uk_102 + QQ(29030443,1683)*uk_105 - QQ(769658,33)*uk_108 + QQ(77314807,3366)*uk_114 + QQ(750229,198)*uk_119 + QQ(72457964,1683)*uk_123 + QQ(11596905,374)*uk_126 + QQ(31304645,306)*uk_128 + QQ(750229,198)*uk_129 - QQ(3191393,99)*uk_134 - QQ(647642,9)*uk_138 - QQ(769658,33)*uk_141 + QQ(262901,99)*uk_142 - QQ(10478626,99)*uk_143 - QQ(3191393,99)*uk_144 - QQ(20480616,187)*uk_148 - QQ(17335370,1683)*uk_151 - QQ(174199750,1683)*uk_153 - QQ(647642,9)*uk_154 + QQ(29030443,1683)*uk_157 + QQ(5965893,187)*uk_159 - QQ(769658,33)*uk_160 - QQ(10478626,99)*uk_163 - QQ(3191393,99)*uk_164, uk_5: -QQ(295441,1683)*uk_20 - QQ(175799,1683)*uk_37 + QQ(2401696807,1)*uk_49 - QQ(9606787228,1683)*uk_56 + QQ(9606787228,1683)*uk_73 - QQ(29030443,1683)*uk_84 - QQ(5965893,187)*uk_101 + QQ(262901,99)*uk_152, uk_6: -QQ(295441,1683)*uk_21 - QQ(175799,1683)*uk_40 + QQ(2401696807,1)*uk_50 - QQ(9606787228,1683)*uk_57 + QQ(9606787228,1683)*uk_76 - QQ(29030443,1683)*uk_85 - QQ(5965893,187)*uk_104 + QQ(262901,99)*uk_158, uk_8: -QQ(295441,1683)*uk_23 - QQ(1602583,3366)*uk_29 + QQ(45670,99)*uk_34 + QQ(76006,187)*uk_38 - QQ(295441,1683)*uk_41 - QQ(175799,1683)*uk_43 + QQ(45670,99)*uk_44 + QQ(2401696807,1)*uk_52 - QQ(9606787228,1683)*uk_59 - QQ(74452601017,3366)*uk_65 + QQ(2401696807,99)*uk_70 + QQ(4803393614,187)*uk_74 - QQ(9606787228,1683)*uk_77 + QQ(9606787228,1683)*uk_79 + QQ(2401696807,99)*uk_80 - QQ(29030443,1683)*uk_87 - QQ(11596905,374)*uk_93 + QQ(769658,33)*uk_98 + QQ(17335370,1683)*uk_102 - QQ(29030443,1683)*uk_105 - QQ(5965893,187)*uk_107 + QQ(769658,33)*uk_108 - QQ(77314807,3366)*uk_114 - QQ(750229,198)*uk_119 - QQ(72457964,1683)*uk_123 - QQ(11596905,374)*uk_126 - QQ(31304645,306)*uk_128 - QQ(750229,198)*uk_129 + QQ(3191393,99)*uk_134 + QQ(647642,9)*uk_138 + QQ(769658,33)*uk_141 + QQ(10478626,99)*uk_143 + QQ(3191393,99)*uk_144 + QQ(20480616,187)*uk_148 + QQ(17335370,1683)*uk_151 + QQ(174199750,1683)*uk_153 + QQ(647642,9)*uk_154 - QQ(29030443,1683)*uk_157 - QQ(5965893,187)*uk_159 + QQ(769658,33)*uk_160 + QQ(262901,99)*uk_162 + QQ(10478626,99)*uk_163 + QQ(3191393,99)*uk_164, uk_11: QQ(4,1683)*uk_18 - QQ(4,1683)*uk_28 - QQ(98072,1)*uk_47 + QQ(96847,1683)*uk_54 - QQ(568087,1683)*uk_64 + QQ(472,1683)*uk_82 + QQ(72,187)*uk_92 - QQ(104,99)*uk_127, uk_12: QQ(4,1683)*uk_19 - QQ(31,3366)*uk_29 - QQ(4,1683)*uk_33 + QQ(1,99)*uk_34 + QQ(2,187)*uk_38 - QQ(4,1683)*uk_41 + QQ(1,99)*uk_44 - QQ(98072,1)*uk_48 + QQ(96847,1683)*uk_55 - QQ(1437649,3366)*uk_65 - QQ(568087,1683)*uk_69 + QQ(52402,99)*uk_70 + QQ(120138,187)*uk_74 - QQ(96847,1683)*uk_77 + QQ(52402,99)*uk_80 + QQ(472,1683)*uk_83 - QQ(225,374)*uk_93 + QQ(72,187)*uk_97 + QQ(17,33)*uk_98 + QQ(590,1683)*uk_102 - QQ(472,1683)*uk_105 + QQ(17,33)*uk_108 - QQ(1519,3366)*uk_114 - QQ(13,198)*uk_119 - QQ(1388,1683)*uk_123 - QQ(225,374)*uk_126 - QQ(605,306)*uk_128 - QQ(13,198)*uk_129 + QQ(68,99)*uk_134 + QQ(14,9)*uk_138 + QQ(17,33)*uk_141 - QQ(104,99)*uk_142 + QQ(229,99)*uk_143 + QQ(68,99)*uk_144 + QQ(472,187)*uk_148 + QQ(590,1683)*uk_151 + QQ(4450,1683)*uk_153 + QQ(14,9)*uk_154 - QQ(472,1683)*uk_157 - QQ(72,187)*uk_159 + QQ(17,33)*uk_160 + QQ(229,99)*uk_163 + QQ(68,99)*uk_164, uk_13: QQ(4,1683)*uk_20 - QQ(4,1683)*uk_37 - QQ(98072,1)*uk_49 + QQ(96847,1683)*uk_56 - QQ(568087,1683)*uk_73 + QQ(472,1683)*uk_84 + QQ(72,187)*uk_101 - QQ(104,99)*uk_152, uk_14: QQ(4,1683)*uk_21 - QQ(4,1683)*uk_40 - QQ(98072,1)*uk_50 + QQ(96847,1683)*uk_57 - QQ(568087,1683)*uk_76 + QQ(472,1683)*uk_85 + QQ(72,187)*uk_104 - QQ(104,99)*uk_158, uk_16: QQ(4,1683)*uk_23 + QQ(31,3366)*uk_29 - QQ(1,99)*uk_34 - QQ(2,187)*uk_38 + QQ(4,1683)*uk_41 - QQ(4,1683)*uk_43 - QQ(1,99)*uk_44 - QQ(98072,1)*uk_52 + QQ(96847,1683)*uk_59 + QQ(1437649,3366)*uk_65 - QQ(52402,99)*uk_70 - QQ(120138,187)*uk_74 + QQ(96847,1683)*uk_77 - QQ(568087,1683)*uk_79 - QQ(52402,99)*uk_80 + QQ(472,1683)*uk_87 + QQ(225,374)*uk_93 - QQ(17,33)*uk_98 - QQ(590,1683)*uk_102 + QQ(472,1683)*uk_105 + QQ(72,187)*uk_107 - QQ(17,33)*uk_108 + QQ(1519,3366)*uk_114 + QQ(13,198)*uk_119 + QQ(1388,1683)*uk_123 + QQ(225,374)*uk_126 + QQ(605,306)*uk_128 + QQ(13,198)*uk_129 - QQ(68,99)*uk_134 - QQ(14,9)*uk_138 - QQ(17,33)*uk_141 - QQ(229,99)*uk_143 - QQ(68,99)*uk_144 - QQ(472,187)*uk_148 - QQ(590,1683)*uk_151 - QQ(4450,1683)*uk_153 - QQ(14,9)*uk_154 + QQ(472,1683)*uk_157 + QQ(72,187)*uk_159 - QQ(17,33)*uk_160 - QQ(104,99)*uk_162 - QQ(229,99)*uk_163 - QQ(68,99)*uk_164, uk_24: -QQ(295441,1683)*uk_88 - QQ(175799,1683)*uk_113, uk_26: -QQ(295441,1683)*uk_90 - QQ(175799,1683)*uk_122, uk_25: -uk_29 - QQ(295441,1683)*uk_89 - QQ(295441,1683)*uk_93 - QQ(175799,1683)*uk_118 - QQ(175799,1683)*uk_128, uk_27: -QQ(295441,1683)*uk_91 - QQ(175799,1683)*uk_125 - QQ(4,1)*uk_149, uk_30: -uk_34 - uk_44 - QQ(295441,1683)*uk_94 - QQ(295441,1683)*uk_98 - QQ(295441,1683)*uk_108 - QQ(175799,1683)*uk_133 - QQ(175799,1683)*uk_143 - QQ(175799,1683)*uk_163, uk_31: -uk_38 - QQ(295441,1683)*uk_95 - QQ(295441,1683)*uk_102 - QQ(175799,1683)*uk_137 - QQ(175799,1683)*uk_153, uk_32: -uk_41 - QQ(295441,1683)*uk_96 - QQ(295441,1683)*uk_105 - QQ(175799,1683)*uk_140 + QQ(4,1)*uk_149 - QQ(175799,1683)*uk_159, uk_35: -QQ(295441,1683)*uk_99 - QQ(175799,1683)*uk_147, uk_36: -QQ(295441,1683)*uk_100 - QQ(2,1)*uk_149 - QQ(175799,1683)*uk_150, uk_39: -QQ(295441,1683)*uk_103 - QQ(175799,1683)*uk_156, uk_60: QQ(4,1683)*uk_88 - QQ(4,1683)*uk_113, uk_61: -uk_65 + QQ(4,1683)*uk_89 + QQ(4,1683)*uk_93 - QQ(4,1683)*uk_118 - QQ(4,1683)*uk_128, uk_62: QQ(4,1683)*uk_90 - QQ(4,1683)*uk_122, uk_63: QQ(4,1683)*uk_91 - QQ(4,1683)*uk_125, uk_66: -uk_70 - uk_80 + QQ(4,1683)*uk_94 + QQ(4,1683)*uk_98 + QQ(4,1683)*uk_108 - QQ(4,1683)*uk_133 - QQ(4,1683)*uk_143 - QQ(4,1683)*uk_163, uk_67: -uk_74 + QQ(4,1683)*uk_95 + QQ(4,1683)*uk_102 - QQ(4,1683)*uk_137 - QQ(4,1683)*uk_153, uk_68: -uk_77 + QQ(4,1683)*uk_96 + QQ(4,1683)*uk_105 - QQ(4,1683)*uk_140 - QQ(4,1683)*uk_159, uk_71: QQ(4,1683)*uk_99 - QQ(4,1683)*uk_147, uk_72: QQ(4,1683)*uk_100 - QQ(4,1683)*uk_150, uk_75: QQ(4,1683)*uk_103 - QQ(4,1683)*uk_156, uk_109: 0, uk_110: -uk_114, uk_111: 0, uk_112: 0, uk_115: -uk_119 - uk_129, uk_116: -uk_123, uk_117: -uk_126, uk_120: 0, uk_121: 0, uk_124: 0, uk_130: -uk_134 - uk_144 - uk_164, uk_131: -uk_138 - uk_154, uk_132: -uk_141 - uk_160, uk_135: -uk_148, uk_136: -uk_151, uk_139: -uk_157, uk_145: 0, uk_146: 0, uk_155: 0, } def time_eqs_165x165(): assert len(eqs_165x165()) == 165 def time_solve_lin_sys_165x165(): eqs = eqs_165x165() sol = solve_lin_sys(eqs, R_165) assert sol == sol_165x165() def time_verify_sol_165x165(): eqs = eqs_165x165() sol = sol_165x165() zeros = [ eq.compose(sol) for eq in eqs ] assert all([ zero == 0 for zero in zeros ]) def time_to_expr_eqs_165x165(): eqs = eqs_165x165() assert [ R_165.from_expr(eq.as_expr()) for eq in eqs ] == eqs # Benchmark R_49: shows how fast are arithmetics in rational function fields. F_abc, a, b, c = field("a,b,c", ZZ) R_49, k1, k2, k3, k4, k5, k6, k7, k8, k9, k10, k11, k12, k13, k14, k15, k16, k17, k18, k19, k20, k21, k22, k23, k24, k25, k26, k27, k28, k29, k30, k31, k32, k33, k34, k35, k36, k37, k38, k39, k40, k41, k42, k43, k44, k45, k46, k47, k48, k49 = ring("k1:50", F_abc) def eqs_189x49(): return [ -b*k8/a+c*k8/a, -b*k11/a+c*k11/a, -b*k10/a+c*k10/a+k2, -k3-b*k9/a+c*k9/a, -b*k14/a+c*k14/a, -b*k15/a+c*k15/a, -b*k18/a+c*k18/a-k2, -b*k17/a+c*k17/a, -b*k16/a+c*k16/a+k4, -b*k13/a+c*k13/a-b*k21/a+c*k21/a+b*k5/a-c*k5/a, b*k44/a-c*k44/a, -b*k45/a+c*k45/a, -b*k20/a+c*k20/a, -b*k44/a+c*k44/a, b*k46/a-c*k46/a, b**2*k47/a**2-2*b*c*k47/a**2+c**2*k47/a**2, k3, -k4, -b*k12/a+c*k12/a-a*k6/b+c*k6/b, -b*k19/a+c*k19/a+a*k7/c-b*k7/c, b*k45/a-c*k45/a, -b*k46/a+c*k46/a, -k48+c*k48/a+c*k48/b-c**2*k48/(a*b), -k49+b*k49/a+b*k49/c-b**2*k49/(a*c), a*k1/b-c*k1/b, a*k4/b-c*k4/b, a*k3/b-c*k3/b+k9, -k10+a*k2/b-c*k2/b, a*k7/b-c*k7/b, -k9, k11, b*k12/a-c*k12/a+a*k6/b-c*k6/b, a*k15/b-c*k15/b, k10+a*k18/b-c*k18/b, -k11+a*k17/b-c*k17/b, a*k16/b-c*k16/b, -a*k13/b+c*k13/b+a*k21/b-c*k21/b+a*k5/b-c*k5/b, -a*k44/b+c*k44/b, a*k45/b-c*k45/b, a*k14/c-b*k14/c+a*k20/b-c*k20/b, a*k44/b-c*k44/b, -a*k46/b+c*k46/b, -k47+c*k47/a+c*k47/b-c**2*k47/(a*b), a*k19/b-c*k19/b, -a*k45/b+c*k45/b, a*k46/b-c*k46/b, a**2*k48/b**2-2*a*c*k48/b**2+c**2*k48/b**2, -k49+a*k49/b+a*k49/c-a**2*k49/(b*c), k16, -k17, -a*k1/c+b*k1/c, -k16-a*k4/c+b*k4/c, -a*k3/c+b*k3/c, k18-a*k2/c+b*k2/c, b*k19/a-c*k19/a-a*k7/c+b*k7/c, -a*k6/c+b*k6/c, -a*k8/c+b*k8/c, -a*k11/c+b*k11/c+k17, -a*k10/c+b*k10/c-k18, -a*k9/c+b*k9/c, -a*k14/c+b*k14/c-a*k20/b+c*k20/b, -a*k13/c+b*k13/c+a*k21/c-b*k21/c-a*k5/c+b*k5/c, a*k44/c-b*k44/c, -a*k45/c+b*k45/c, -a*k44/c+b*k44/c, a*k46/c-b*k46/c, -k47+b*k47/a+b*k47/c-b**2*k47/(a*c), -a*k12/c+b*k12/c, a*k45/c-b*k45/c, -a*k46/c+b*k46/c, -k48+a*k48/b+a*k48/c-a**2*k48/(b*c), a**2*k49/c**2-2*a*b*k49/c**2+b**2*k49/c**2, k8, k11, -k15, k10-k18, -k17, k9, -k16, -k29, k14-k32, -k21+k23-k31, -k24-k30, -k35, k44, -k45, k36, k13-k23+k39, -k20+k38, k25+k37, b*k26/a-c*k26/a-k34+k42, -2*k44, k45, k46, b*k47/a-c*k47/a, k41, k44, -k46, -b*k47/a+c*k47/a, k12+k24, -k19-k25, -a*k27/b+c*k27/b-k33, k45, -k46, -a*k48/b+c*k48/b, a*k28/c-b*k28/c+k40, -k45, k46, a*k48/b-c*k48/b, a*k49/c-b*k49/c, -a*k49/c+b*k49/c, -k1, -k4, -k3, k15, k18-k2, k17, k16, k22, k25-k7, k24+k30, k21+k23-k31, k28, -k44, k45, -k30-k6, k20+k32, k27+b*k33/a-c*k33/a, k44, -k46, -b*k47/a+c*k47/a, -k36, k31-k39-k5, -k32-k38, k19-k37, k26-a*k34/b+c*k34/b-k42, k44, -2*k45, k46, a*k48/b-c*k48/b, a*k35/c-b*k35/c-k41, -k44, k46, b*k47/a-c*k47/a, -a*k49/c+b*k49/c, -k40, k45, -k46, -a*k48/b+c*k48/b, a*k49/c-b*k49/c, k1, k4, k3, -k8, -k11, -k10+k2, -k9, k37+k7, -k14-k38, -k22, -k25-k37, -k24+k6, -k13-k23+k39, -k28+b*k40/a-c*k40/a, k44, -k45, -k27, -k44, k46, b*k47/a-c*k47/a, k29, k32+k38, k31-k39+k5, -k12+k30, k35-a*k41/b+c*k41/b, -k44, k45, -k26+k34+a*k42/c-b*k42/c, k44, k45, -2*k46, -b*k47/a+c*k47/a, -a*k48/b+c*k48/b, a*k49/c-b*k49/c, k33, -k45, k46, a*k48/b-c*k48/b, -a*k49/c+b*k49/c, ] def sol_189x49(): return { k49: 0, k48: 0, k47: 0, k46: 0, k45: 0, k44: 0, k41: 0, k40: 0, k38: 0, k37: 0, k36: 0, k35: 0, k33: 0, k32: 0, k30: 0, k29: 0, k28: 0, k27: 0, k25: 0, k24: 0, k22: 0, k21: 0, k20: 0, k19: 0, k18: 0, k17: 0, k16: 0, k15: 0, k14: 0, k13: 0, k12: 0, k11: 0, k10: 0, k9: 0, k8: 0, k7: 0, k6: 0, k5: 0, k4: 0, k3: 0, k2: 0, k1: 0, k34: b/c*k42, k31: k39, k26: a/c*k42, k23: k39, } def time_eqs_189x49(): assert len(eqs_189x49()) == 189 def time_solve_lin_sys_189x49(): eqs = eqs_189x49() sol = solve_lin_sys(eqs, R_49) assert sol == sol_189x49() def time_verify_sol_189x49(): eqs = eqs_189x49() sol = sol_189x49() zeros = [ eq.compose(sol) for eq in eqs ] assert all([ zero == 0 for zero in zeros ]) def time_to_expr_eqs_189x49(): eqs = eqs_189x49() assert [ R_49.from_expr(eq.as_expr()) for eq in eqs ] == eqs # Benchmark R_8: shows how fast polynomial GCDs are computed. F_a5_5, a_11, a_12, a_13, a_14, a_21, a_22, a_23, a_24, a_31, a_32, a_33, a_34, a_41, a_42, a_43, a_44 = field("a_(1:5)(1:5)", ZZ) R_8, x0, x1, x2, x3, x4, x5, x6, x7 = ring("x:8", F_a5_5) def eqs_10x8(): return [ (a_33*a_34 + a_33*a_44 + a_43*a_44)*x3 + (a_33*a_34 + a_33*a_44 + a_43*a_44)*x4 + (a_12*a_34 + a_12*a_44 + a_22*a_34 + a_22*a_44)*x5 + (a_12*a_44 + a_22*a_44)*x6 + (a_12*a_33 + a_22*a_33)*x7 - a_12*a_33 - a_12*a_43 - a_22*a_33 - a_22*a_43, (a_33 + a_34 + a_43 + a_44)*x3 + (a_33 + a_34 + a_43 + a_44)*x4 + (a_12 + a_22 + a_34 + a_44)*x5 + (a_12 + a_22 + a_44)*x6 + (a_12 + a_22 + a_33)*x7 - a_12 - a_22 - a_33 - a_43, x3 + x4 + x5 + x6 + x7 - 1, (a_12*a_33*a_34 + a_12*a_33*a_44 + a_12*a_43*a_44 + a_22*a_33*a_34 + a_22*a_33*a_44 + a_22*a_43*a_44)*x0 + (a_22*a_33*a_34 + a_22*a_33*a_44 + a_22*a_43*a_44)*x1 + (a_12*a_33*a_34 + a_12*a_33*a_44 + a_12*a_43*a_44 + a_22*a_33*a_34 + a_22*a_33*a_44 + a_22*a_43*a_44)*x2 + (a_11*a_33*a_34 + a_11*a_33*a_44 + a_11*a_43*a_44 + a_31*a_33*a_34 + a_31*a_33*a_44 + a_31*a_43*a_44)*x3 + (a_11*a_33*a_34 + a_11*a_33*a_44 + a_11*a_43*a_44 + a_21*a_33*a_34 + a_21*a_33*a_44 + a_21*a_43*a_44 + a_31*a_33*a_34 + a_31*a_33*a_44 + a_31*a_43*a_44)*x4 + (a_11*a_12*a_34 + a_11*a_12*a_44 + a_11*a_22*a_34 + a_11*a_22*a_44 + a_12*a_31*a_34 + a_12*a_31*a_44 + a_21*a_22*a_34 + a_21*a_22*a_44 + a_22*a_31*a_34 + a_22*a_31*a_44)*x5 + (a_11*a_12*a_44 + a_11*a_22*a_44 + a_12*a_31*a_44 + a_21*a_22*a_44 + a_22*a_31*a_44)*x6 + (a_11*a_12*a_33 + a_11*a_22*a_33 + a_12*a_31*a_33 + a_21*a_22*a_33 + a_22*a_31*a_33)*x7 - a_11*a_12*a_33 - a_11*a_12*a_43 - a_11*a_22*a_33 - a_11*a_22*a_43 - a_12*a_31*a_33 - a_12*a_31*a_43 - a_21*a_22*a_33 - a_21*a_22*a_43 - a_22*a_31*a_33 - a_22*a_31*a_43, (a_12*a_33 + a_12*a_34 + a_12*a_43 + a_12*a_44 + a_22*a_33 + a_22*a_34 + a_22*a_43 + a_22*a_44 + a_33*a_34 + a_33*a_44 + a_43*a_44)*x0 + (a_22*a_33 + a_22*a_34 + a_22*a_43 + a_22*a_44 + a_33*a_34 + a_33*a_44 + a_43*a_44)*x1 + (a_12*a_33 + a_12*a_34 + a_12*a_43 + a_12*a_44 + a_22*a_33 + a_22*a_34 + a_22*a_43 + a_22*a_44 + a_33*a_34 + a_33*a_44 + a_43*a_44)*x2 + (a_11*a_33 + a_11*a_34 + a_11*a_43 + a_11*a_44 + a_31*a_33 + a_31*a_34 + a_31*a_43 + a_31*a_44 + a_33*a_34 + a_33*a_44 + a_43*a_44)*x3 + (a_11*a_33 + a_11*a_34 + a_11*a_43 + a_11*a_44 + a_21*a_33 + a_21*a_34 + a_21*a_43 + a_21*a_44 + a_31*a_33 + a_31*a_34 + a_31*a_43 + a_31*a_44 + a_33*a_34 + a_33*a_44 + a_43*a_44)*x4 + (a_11*a_12 + a_11*a_22 + a_11*a_34 + a_11*a_44 + a_12*a_31 + a_12*a_34 + a_12*a_44 + a_21*a_22 + a_21*a_34 + a_21*a_44 + a_22*a_31 + a_22*a_34 + a_22*a_44 + a_31*a_34 + a_31*a_44)*x5 + (a_11*a_12 + a_11*a_22 + a_11*a_44 + a_12*a_31 + a_12*a_44 + a_21*a_22 + a_21*a_44 + a_22*a_31 + a_22*a_44 + a_31*a_44)*x6 + (a_11*a_12 + a_11*a_22 + a_11*a_33 + a_12*a_31 + a_12*a_33 + a_21*a_22 + a_21*a_33 + a_22*a_31 + a_22*a_33 + a_31*a_33)*x7 - a_11*a_12 - a_11*a_22 - a_11*a_33 - a_11*a_43 - a_12*a_31 - a_12*a_33 - a_12*a_43 - a_21*a_22 - a_21*a_33 - a_21*a_43 - a_22*a_31 - a_22*a_33 - a_22*a_43 - a_31*a_33 - a_31*a_43, (a_12 + a_22 + a_33 + a_34 + a_43 + a_44)*x0 + (a_22 + a_33 + a_34 + a_43 + a_44)*x1 + (a_12 + a_22 + a_33 + a_34 + a_43 + a_44)*x2 + (a_11 + a_31 + a_33 + a_34 + a_43 + a_44)*x3 + (a_11 + a_21 + a_31 + a_33 + a_34 + a_43 + a_44)*x4 + (a_11 + a_12 + a_21 + a_22 + a_31 + a_34 + a_44)*x5 + (a_11 + a_12 + a_21 + a_22 + a_31 + a_44)*x6 + (a_11 + a_12 + a_21 + a_22 + a_31 + a_33)*x7 - a_11 - a_12 - a_21 - a_22 - a_31 - a_33 - a_43, x0 + x1 + x2 + x3 + x4 + x5 + x6 + x7 - 1, (a_12*a_34 + a_12*a_44 + a_22*a_34 + a_22*a_44)*x2 + (a_31*a_34 + a_31*a_44)*x3 + (a_31*a_34 + a_31*a_44)*x4 + (a_12*a_31 + a_22*a_31)*x7 - a_12*a_31 - a_22*a_31, (a_12 + a_22 + a_34 + a_44)*x2 + a_31*x3 + a_31*x4 + a_31*x7 - a_31, x2, ] def sol_10x8(): return { x0: -a_21/a_12*x4, x1: a_21/a_12*x4, x2: 0, x3: -x4, x5: a_43/a_34, x6: -a_43/a_34, x7: 1, } def time_eqs_10x8(): assert len(eqs_10x8()) == 10 def time_solve_lin_sys_10x8(): eqs = eqs_10x8() sol = solve_lin_sys(eqs, R_8) assert sol == sol_10x8() def time_verify_sol_10x8(): eqs = eqs_10x8() sol = sol_10x8() zeros = [ eq.compose(sol) for eq in eqs ] assert all([ zero == 0 for zero in zeros ]) def time_to_expr_eqs_10x8(): eqs = eqs_10x8() assert [ R_8.from_expr(eq.as_expr()) for eq in eqs ] == eqs sympy-0.7.4.1/sympy/polys/benchmarks/bench_galoispolys.py0000644000175000017500000000302712253362407023770 0ustar georgeskgeorgesk"""Benchmarks for polynomials over Galois fields. """ from __future__ import print_function, division from sympy.polys.galoistools import gf_from_dict, gf_factor, gf_factor_sqf from sympy.polys.domains import ZZ from sympy import pi, nextprime from sympy.core.compatibility import xrange def gathen_poly(n, p, K): return gf_from_dict({n: K.one, 1: K.one, 0: K.one}, p, K) def shoup_poly(n, p, K): f = [K.one] * (n + 1) for i in xrange(1, n + 1): f[i] = (f[i - 1]**2 + K.one) % p return f def genprime(n, K): return K(nextprime(int((2**n * pi).evalf()))) p_10 = genprime(10, ZZ) f_10 = gathen_poly(10, p_10, ZZ) p_20 = genprime(20, ZZ) f_20 = gathen_poly(20, p_20, ZZ) def timeit_gathen_poly_f10_zassenhaus(): gf_factor_sqf(f_10, p_10, ZZ, method='zassenhaus') def timeit_gathen_poly_f10_shoup(): gf_factor_sqf(f_10, p_10, ZZ, method='shoup') def timeit_gathen_poly_f20_zassenhaus(): gf_factor_sqf(f_20, p_20, ZZ, method='zassenhaus') def timeit_gathen_poly_f20_shoup(): gf_factor_sqf(f_20, p_20, ZZ, method='shoup') P_08 = genprime(8, ZZ) F_10 = shoup_poly(10, P_08, ZZ) P_18 = genprime(18, ZZ) F_20 = shoup_poly(20, P_18, ZZ) def timeit_shoup_poly_F10_zassenhaus(): gf_factor_sqf(F_10, P_08, ZZ, method='zassenhaus') def timeit_shoup_poly_F10_shoup(): gf_factor_sqf(F_10, P_08, ZZ, method='shoup') def timeit_shoup_poly_F20_zassenhaus(): gf_factor_sqf(F_20, P_18, ZZ, method='zassenhaus') def timeit_shoup_poly_F20_shoup(): gf_factor_sqf(F_20, P_18, ZZ, method='shoup') sympy-0.7.4.1/sympy/polys/benchmarks/__init__.py0000644000175000017500000000000012253362407022007 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/polys/rootisolation.py0000644000175000017500000015301112253362407021053 0ustar georgeskgeorgesk"""Real and complex root isolation and refinement algorithms. """ from __future__ import print_function, division from sympy.polys.densebasic import ( dup_LC, dup_TC, dup_degree, dup_strip, dup_reverse, dup_convert, dup_terms_gcd) from sympy.polys.densearith import ( dup_neg, dup_rshift, dup_rem) from sympy.polys.densetools import ( dup_clear_denoms, dup_mirror, dup_scale, dup_shift, dup_transform, dup_diff, dup_eval, dmp_eval_in, dup_sign_variations, dup_real_imag) from sympy.polys.sqfreetools import ( dup_sqf_part, dup_sqf_list) from sympy.polys.factortools import ( dup_factor_list) from sympy.polys.polyerrors import ( RefinementFailed, DomainError) from sympy.core.compatibility import xrange def dup_sturm(f, K): """ Computes the Sturm sequence of ``f`` in ``F[x]``. Given a univariate, square-free polynomial ``f(x)`` returns the associated Sturm sequence ``f_0(x), ..., f_n(x)`` defined by:: f_0(x), f_1(x) = f(x), f'(x) f_n = -rem(f_{n-2}(x), f_{n-1}(x)) Examples ======== >>> from sympy.polys import ring, QQ >>> R, x = ring("x", QQ) >>> R.dup_sturm(x**3 - 2*x**2 + x - 3) [x**3 - 2*x**2 + x - 3, 3*x**2 - 4*x + 1, 2/9*x + 25/9, -2079/4] References ========== 1. [Davenport88]_ """ if not K.has_Field: raise DomainError("can't compute Sturm sequence over %s" % K) f = dup_sqf_part(f, K) sturm = [f, dup_diff(f, 1, K)] while sturm[-1]: s = dup_rem(sturm[-2], sturm[-1], K) sturm.append(dup_neg(s, K)) return sturm[:-1] def dup_root_upper_bound(f, K): """Compute the LMQ upper bound for `f`'s positive roots, which was developed by Akritas-Strzebonski-Vigklas. Reference: ========== Alkiviadis G. Akritas: "Linear and Quadratic Complexity Bounds on the Values of the Positive Roots of Polynomials" Journal of Universal Computer Science, Vol. 15, No. 3, 523-537, 2009. """ n, t, P = len(f), K.one, [] if dup_LC(f, K) < 0: f = dup_neg(f, K) f = list(reversed(f)) for i in xrange(0, n): if f[i] >= 0: continue a, Q = K.log(-f[i], 2), [] for j in xrange(i + 1, n): if f[j] <= 0: continue q = t + a - K.log(f[j], 2) Q.append(q // (j - i)) t += 1 if not Q: continue P.append(min(Q)) if not P: return None else: return K.get_field()(2)**(max(P) + 1) def dup_root_lower_bound(f, K): """Compute LMQ lower bound for `f`'s positive roots. Reference: ========== Alkiviadis G. Akritas: "Linear and Quadratic Complexity Bounds on the Values of the Positive Roots of Polynomials" Journal of Universal Computer Science, Vol. 15, No. 3, 523-537, 2009. """ bound = dup_root_upper_bound(dup_reverse(f), K) if bound is not None: return 1/bound else: return None def _mobius_from_interval(I, field): """Convert an open interval to a Mobius transform. """ s, t = I a, c = field.numer(s), field.denom(s) b, d = field.numer(t), field.denom(t) return a, b, c, d def _mobius_to_interval(M, field): """Convert a Mobius transform to an open interval. """ a, b, c, d = M s, t = field(a, c), field(b, d) if s <= t: return (s, t) else: return (t, s) def dup_step_refine_real_root(f, M, K, fast=False): """One step of positive real root refinement algorithm. """ a, b, c, d = M if a == b and c == d: return f, (a, b, c, d) A = dup_root_lower_bound(f, K) if A is not None: A = K(int(A)) else: A = K.zero if fast and A > 16: f = dup_scale(f, A, K) a, c, A = A*a, A*c, K.one if A >= K.one: f = dup_shift(f, A, K) b, d = A*a + b, A*c + d if not dup_eval(f, K.zero, K): return f, (b, b, d, d) f, g = dup_shift(f, K.one, K), f a1, b1, c1, d1 = a, a + b, c, c + d if not dup_eval(f, K.zero, K): return f, (b1, b1, d1, d1) k = dup_sign_variations(f, K) if k == 1: a, b, c, d = a1, b1, c1, d1 else: f = dup_shift(dup_reverse(g), K.one, K) if not dup_eval(f, K.zero, K): f = dup_rshift(f, 1, K) a, b, c, d = b, a + b, d, c + d return f, (a, b, c, d) def dup_inner_refine_real_root(f, M, K, eps=None, steps=None, disjoint=None, fast=False, mobius=False): """Refine a positive root of `f` given a Mobius transform or an interval. """ F = K.get_field() if len(M) == 2: a, b, c, d = _mobius_from_interval(M, F) else: a, b, c, d = M while not c: f, (a, b, c, d) = dup_step_refine_real_root(f, (a, b, c, d), K, fast=fast) if eps is not None and steps is not None: for i in xrange(0, steps): if abs(F(a, c) - F(b, d)) >= eps: f, (a, b, c, d) = dup_step_refine_real_root(f, (a, b, c, d), K, fast=fast) else: break else: if eps is not None: while abs(F(a, c) - F(b, d)) >= eps: f, (a, b, c, d) = dup_step_refine_real_root(f, (a, b, c, d), K, fast=fast) if steps is not None: for i in xrange(0, steps): f, (a, b, c, d) = dup_step_refine_real_root(f, (a, b, c, d), K, fast=fast) if disjoint is not None: while True: u, v = _mobius_to_interval((a, b, c, d), F) if v <= disjoint or disjoint <= u: break else: f, (a, b, c, d) = dup_step_refine_real_root(f, (a, b, c, d), K, fast=fast) if not mobius: return _mobius_to_interval((a, b, c, d), F) else: return f, (a, b, c, d) def dup_outer_refine_real_root(f, s, t, K, eps=None, steps=None, disjoint=None, fast=False): """Refine a positive root of `f` given an interval `(s, t)`. """ a, b, c, d = _mobius_from_interval((s, t), K.get_field()) f = dup_transform(f, dup_strip([a, b]), dup_strip([c, d]), K) if dup_sign_variations(f, K) != 1: raise RefinementFailed("there should be exactly one root in (%s, %s) interval" % (s, t)) return dup_inner_refine_real_root(f, (a, b, c, d), K, eps=eps, steps=steps, disjoint=disjoint, fast=fast) def dup_refine_real_root(f, s, t, K, eps=None, steps=None, disjoint=None, fast=False): """Refine real root's approximating interval to the given precision. """ if K.is_QQ: (_, f), K = dup_clear_denoms(f, K, convert=True), K.get_ring() elif not K.is_ZZ: raise DomainError("real root refinement not supported over %s" % K) if s == t: return (s, t) if s > t: s, t = t, s negative = False if s < 0: if t <= 0: f, s, t, negative = dup_mirror(f, K), -t, -s, True else: raise ValueError("can't refine a real root in (%s, %s)" % (s, t)) if negative and disjoint is not None: if disjoint < 0: disjoint = -disjoint else: disjoint = None s, t = dup_outer_refine_real_root( f, s, t, K, eps=eps, steps=steps, disjoint=disjoint, fast=fast) if negative: return (-t, -s) else: return ( s, t) def dup_inner_isolate_real_roots(f, K, eps=None, fast=False): """Internal function for isolation positive roots up to given precision. References: =========== 1. Alkiviadis G. Akritas and Adam W. Strzebonski: A Comparative Study of Two Real Root Isolation Methods . Nonlinear Analysis: Modelling and Control, Vol. 10, No. 4, 297-304, 2005. 2. Alkiviadis G. Akritas, Adam W. Strzebonski and Panagiotis S. Vigklas: Improving the Performance of the Continued Fractions Method Using new Bounds of Positive Roots. Nonlinear Analysis: Modelling and Control, Vol. 13, No. 3, 265-279, 2008. """ a, b, c, d = K.one, K.zero, K.zero, K.one k = dup_sign_variations(f, K) if k == 0: return [] if k == 1: roots = [dup_inner_refine_real_root( f, (a, b, c, d), K, eps=eps, fast=fast, mobius=True)] else: roots, stack = [], [(a, b, c, d, f, k)] while stack: a, b, c, d, f, k = stack.pop() A = dup_root_lower_bound(f, K) if A is not None: A = K(int(A)) else: A = K.zero if fast and A > 16: f = dup_scale(f, A, K) a, c, A = A*a, A*c, K.one if A >= K.one: f = dup_shift(f, A, K) b, d = A*a + b, A*c + d if not dup_TC(f, K): roots.append((f, (b, b, d, d))) f = dup_rshift(f, 1, K) k = dup_sign_variations(f, K) if k == 0: continue if k == 1: roots.append(dup_inner_refine_real_root( f, (a, b, c, d), K, eps=eps, fast=fast, mobius=True)) continue f1 = dup_shift(f, K.one, K) a1, b1, c1, d1, r = a, a + b, c, c + d, 0 if not dup_TC(f1, K): roots.append((f1, (b1, b1, d1, d1))) f1, r = dup_rshift(f1, 1, K), 1 k1 = dup_sign_variations(f1, K) k2 = k - k1 - r a2, b2, c2, d2 = b, a + b, d, c + d if k2 > 1: f2 = dup_shift(dup_reverse(f), K.one, K) if not dup_TC(f2, K): f2 = dup_rshift(f2, 1, K) k2 = dup_sign_variations(f2, K) else: f2 = None if k1 < k2: a1, a2, b1, b2 = a2, a1, b2, b1 c1, c2, d1, d2 = c2, c1, d2, d1 f1, f2, k1, k2 = f2, f1, k2, k1 if not k1: continue if f1 is None: f1 = dup_shift(dup_reverse(f), K.one, K) if not dup_TC(f1, K): f1 = dup_rshift(f1, 1, K) if k1 == 1: roots.append(dup_inner_refine_real_root( f1, (a1, b1, c1, d1), K, eps=eps, fast=fast, mobius=True)) else: stack.append((a1, b1, c1, d1, f1, k1)) if not k2: continue if f2 is None: f2 = dup_shift(dup_reverse(f), K.one, K) if not dup_TC(f2, K): f2 = dup_rshift(f2, 1, K) if k2 == 1: roots.append(dup_inner_refine_real_root( f2, (a2, b2, c2, d2), K, eps=eps, fast=fast, mobius=True)) else: stack.append((a2, b2, c2, d2, f2, k2)) return roots def _discard_if_outside_interval(f, M, inf, sup, K, negative, fast, mobius): """Discard an isolating interval if outside ``(inf, sup)``. """ F = K.get_field() while True: u, v = _mobius_to_interval(M, F) if negative: u, v = -v, -u if (inf is None or u >= inf) and (sup is None or v <= sup): if not mobius: return u, v else: return f, M elif (sup is not None and u > sup) or (inf is not None and v < inf): return None else: f, M = dup_step_refine_real_root(f, M, K, fast=fast) def dup_inner_isolate_positive_roots(f, K, eps=None, inf=None, sup=None, fast=False, mobius=False): """Iteratively compute disjoint positive root isolation intervals. """ if sup is not None and sup < 0: return [] roots = dup_inner_isolate_real_roots(f, K, eps=eps, fast=fast) F, results = K.get_field(), [] if inf is not None or sup is not None: for f, M in roots: result = _discard_if_outside_interval(f, M, inf, sup, K, False, fast, mobius) if result is not None: results.append(result) elif not mobius: for f, M in roots: u, v = _mobius_to_interval(M, F) results.append((u, v)) else: results = roots return results def dup_inner_isolate_negative_roots(f, K, inf=None, sup=None, eps=None, fast=False, mobius=False): """Iteratively compute disjoint negative root isolation intervals. """ if inf is not None and inf >= 0: return [] roots = dup_inner_isolate_real_roots(dup_mirror(f, K), K, eps=eps, fast=fast) F, results = K.get_field(), [] if inf is not None or sup is not None: for f, M in roots: result = _discard_if_outside_interval(f, M, inf, sup, K, True, fast, mobius) if result is not None: results.append(result) elif not mobius: for f, M in roots: u, v = _mobius_to_interval(M, F) results.append((-v, -u)) else: results = roots return results def _isolate_zero(f, K, inf, sup, basis=False, sqf=False): """Handle special case of CF algorithm when ``f`` is homogeneous. """ j, f = dup_terms_gcd(f, K) if j > 0: F = K.get_field() if (inf is None or inf <= 0) and (sup is None or 0 <= sup): if not sqf: if not basis: return [((F.zero, F.zero), j)], f else: return [((F.zero, F.zero), j, [K.one, K.zero])], f else: return [(F.zero, F.zero)], f return [], f def dup_isolate_real_roots_sqf(f, K, eps=None, inf=None, sup=None, fast=False, blackbox=False): """Isolate real roots of a square-free polynomial using the Vincent-Akritas-Strzebonski (VAS) CF approach. References: =========== 1. Alkiviadis G. Akritas and Adam W. Strzebonski: A Comparative Study of Two Real Root Isolation Methods. Nonlinear Analysis: Modelling and Control, Vol. 10, No. 4, 297-304, 2005. 2. Alkiviadis G. Akritas, Adam W. Strzebonski and Panagiotis S. Vigklas: Improving the Performance of the Continued Fractions Method Using New Bounds of Positive Roots. Nonlinear Analysis: Modelling and Control, Vol. 13, No. 3, 265-279, 2008. """ if K.is_QQ: (_, f), K = dup_clear_denoms(f, K, convert=True), K.get_ring() elif not K.is_ZZ: raise DomainError("isolation of real roots not supported over %s" % K) if dup_degree(f) <= 0: return [] I_zero, f = _isolate_zero(f, K, inf, sup, basis=False, sqf=True) I_neg = dup_inner_isolate_negative_roots(f, K, eps=eps, inf=inf, sup=sup, fast=fast) I_pos = dup_inner_isolate_positive_roots(f, K, eps=eps, inf=inf, sup=sup, fast=fast) roots = sorted(I_neg + I_zero + I_pos) if not blackbox: return roots else: return [ RealInterval((a, b), f, K) for (a, b) in roots ] def dup_isolate_real_roots(f, K, eps=None, inf=None, sup=None, basis=False, fast=False): """Isolate real roots using Vincent-Akritas-Strzebonski (VAS) continued fractions approach. References: =========== 1. Alkiviadis G. Akritas and Adam W. Strzebonski: A Comparative Study of Two Real Root Isolation Methods. Nonlinear Analysis: Modelling and Control, Vol. 10, No. 4, 297-304, 2005. 2. Alkiviadis G. Akritas, Adam W. Strzebonski and Panagiotis S. Vigklas: Improving the Performance of the Continued Fractions Method Using New Bounds of Positive Roots. Nonlinear Analysis: Modelling and Control, Vol. 13, No. 3, 265-279, 2008. """ if K.is_QQ: (_, f), K = dup_clear_denoms(f, K, convert=True), K.get_ring() elif not K.is_ZZ: raise DomainError("isolation of real roots not supported over %s" % K) if dup_degree(f) <= 0: return [] I_zero, f = _isolate_zero(f, K, inf, sup, basis=basis, sqf=False) _, factors = dup_sqf_list(f, K) if len(factors) == 1: ((f, k),) = factors I_neg = dup_inner_isolate_negative_roots(f, K, eps=eps, inf=inf, sup=sup, fast=fast) I_pos = dup_inner_isolate_positive_roots(f, K, eps=eps, inf=inf, sup=sup, fast=fast) I_neg = [ ((u, v), k) for u, v in I_neg ] I_pos = [ ((u, v), k) for u, v in I_pos ] else: I_neg, I_pos = _real_isolate_and_disjoin(factors, K, eps=eps, inf=inf, sup=sup, basis=basis, fast=fast) return sorted(I_neg + I_zero + I_pos) def dup_isolate_real_roots_list(polys, K, eps=None, inf=None, sup=None, strict=False, basis=False, fast=False): """Isolate real roots of a list of square-free polynomial using Vincent-Akritas-Strzebonski (VAS) CF approach. References: =========== 1. Alkiviadis G. Akritas and Adam W. Strzebonski: A Comparative Study of Two Real Root Isolation Methods. Nonlinear Analysis: Modelling and Control, Vol. 10, No. 4, 297-304, 2005. 2. Alkiviadis G. Akritas, Adam W. Strzebonski and Panagiotis S. Vigklas: Improving the Performance of the Continued Fractions Method Using New Bounds of Positive Roots. Nonlinear Analysis: Modelling and Control, Vol. 13, No. 3, 265-279, 2008. """ if K.is_QQ: K, F, polys = K.get_ring(), K, polys[:] for i, p in enumerate(polys): polys[i] = dup_clear_denoms(p, F, K, convert=True)[1] elif not K.is_ZZ: raise DomainError("isolation of real roots not supported over %s" % K) zeros, factors_dict = False, {} if (inf is None or inf <= 0) and (sup is None or 0 <= sup): zeros, zero_indices = True, {} for i, p in enumerate(polys): j, p = dup_terms_gcd(p, K) if zeros and j > 0: zero_indices[i] = j for f, k in dup_factor_list(p, K)[1]: f = tuple(f) if f not in factors_dict: factors_dict[f] = {i: k} else: factors_dict[f][i] = k factors_list = [] for f, indices in factors_dict.items(): factors_list.append((list(f), indices)) I_neg, I_pos = _real_isolate_and_disjoin(factors_list, K, eps=eps, inf=inf, sup=sup, strict=strict, basis=basis, fast=fast) F = K.get_field() if not zeros or not zero_indices: I_zero = [] else: if not basis: I_zero = [((F.zero, F.zero), zero_indices)] else: I_zero = [((F.zero, F.zero), zero_indices, [K.one, K.zero])] return sorted(I_neg + I_zero + I_pos) def _disjoint_p(M, N, strict=False): """Check if Mobius transforms define disjoint intervals. """ a1, b1, c1, d1 = M a2, b2, c2, d2 = N a1d1, b1c1 = a1*d1, b1*c1 a2d2, b2c2 = a2*d2, b2*c2 if a1d1 == b1c1 and a2d2 == b2c2: return True if a1d1 > b1c1: a1, c1, b1, d1 = b1, d1, a1, c1 if a2d2 > b2c2: a2, c2, b2, d2 = b2, d2, a2, c2 if not strict: return a2*d1 >= c2*b1 or b2*c1 <= d2*a1 else: return a2*d1 > c2*b1 or b2*c1 < d2*a1 def _real_isolate_and_disjoin(factors, K, eps=None, inf=None, sup=None, strict=False, basis=False, fast=False): """Isolate real roots of a list of polynomials and disjoin intervals. """ I_pos, I_neg = [], [] for i, (f, k) in enumerate(factors): for F, M in dup_inner_isolate_positive_roots(f, K, eps=eps, inf=inf, sup=sup, fast=fast, mobius=True): I_pos.append((F, M, k, f)) for G, N in dup_inner_isolate_negative_roots(f, K, eps=eps, inf=inf, sup=sup, fast=fast, mobius=True): I_neg.append((G, N, k, f)) for i, (f, M, k, F) in enumerate(I_pos): for j, (g, N, m, G) in enumerate(I_pos[i + 1:]): while not _disjoint_p(M, N, strict=strict): f, M = dup_inner_refine_real_root(f, M, K, steps=1, fast=fast, mobius=True) g, N = dup_inner_refine_real_root(g, N, K, steps=1, fast=fast, mobius=True) I_pos[i + j + 1] = (g, N, m, G) I_pos[i] = (f, M, k, F) for i, (f, M, k, F) in enumerate(I_neg): for j, (g, N, m, G) in enumerate(I_neg[i + 1:]): while not _disjoint_p(M, N, strict=strict): f, M = dup_inner_refine_real_root(f, M, K, steps=1, fast=fast, mobius=True) g, N = dup_inner_refine_real_root(g, N, K, steps=1, fast=fast, mobius=True) I_neg[i + j + 1] = (g, N, m, G) I_neg[i] = (f, M, k, F) if strict: for i, (f, M, k, F) in enumerate(I_neg): if not M[0]: while not M[0]: f, M = dup_inner_refine_real_root(f, M, K, steps=1, fast=fast, mobius=True) I_neg[i] = (f, M, k, F) break for j, (g, N, m, G) in enumerate(I_pos): if not N[0]: while not N[0]: g, N = dup_inner_refine_real_root(g, N, K, steps=1, fast=fast, mobius=True) I_pos[j] = (g, N, m, G) break field = K.get_field() I_neg = [ (_mobius_to_interval(M, field), k, f) for (_, M, k, f) in I_neg ] I_pos = [ (_mobius_to_interval(M, field), k, f) for (_, M, k, f) in I_pos ] if not basis: I_neg = [ ((-v, -u), k) for ((u, v), k, _) in I_neg ] I_pos = [ (( u, v), k) for ((u, v), k, _) in I_pos ] else: I_neg = [ ((-v, -u), k, f) for ((u, v), k, f) in I_neg ] I_pos = [ (( u, v), k, f) for ((u, v), k, f) in I_pos ] return I_neg, I_pos def dup_count_real_roots(f, K, inf=None, sup=None): """Returns the number of distinct real roots of ``f`` in ``[inf, sup]``. """ if dup_degree(f) <= 0: return 0 if not K.has_Field: R, K = K, K.get_field() f = dup_convert(f, R, K) sturm = dup_sturm(f, K) if inf is None: signs_inf = dup_sign_variations([ dup_LC(s, K)*(-1)**dup_degree(s) for s in sturm ], K) else: signs_inf = dup_sign_variations([ dup_eval(s, inf, K) for s in sturm ], K) if sup is None: signs_sup = dup_sign_variations([ dup_LC(s, K) for s in sturm ], K) else: signs_sup = dup_sign_variations([ dup_eval(s, sup, K) for s in sturm ], K) count = abs(signs_inf - signs_sup) if inf is not None and not dup_eval(f, inf, K): count += 1 return count OO = 'OO' # Origin of (re, im) coordinate system Q1 = 'Q1' # Quadrant #1 (++): re > 0 and im > 0 Q2 = 'Q2' # Quadrant #2 (-+): re < 0 and im > 0 Q3 = 'Q3' # Quadrant #3 (--): re < 0 and im < 0 Q4 = 'Q4' # Quadrant #4 (+-): re > 0 and im < 0 A1 = 'A1' # Axis #1 (+0): re > 0 and im = 0 A2 = 'A2' # Axis #2 (0+): re = 0 and im > 0 A3 = 'A3' # Axis #3 (-0): re < 0 and im = 0 A4 = 'A4' # Axis #4 (0-): re = 0 and im < 0 _rules_simple = { # A -- CCW --> Q => +1/4 (CCW) (A1, Q1): 1, (A2, Q2): 1, (A3, Q3): 1, (A4, Q4): 1, # A -- CW --> Q => -1/4 (CCW) (A1, Q4): 2, (A2, Q1): 2, (A3, Q2): 2, (A4, Q3): 2, # Q -- CCW --> A => +1/4 (CCW) (Q1, A2): 3, (Q2, A3): 3, (Q3, A4): 3, (Q4, A1): 3, # Q -- CW --> A => -1/4 (CCW) (Q1, A1): 4, (Q2, A2): 4, (Q3, A3): 4, (Q4, A4): 4, # Q -- CCW --> Q => +1/2 (CCW) (Q1, Q2): +5, (Q2, Q3): +5, (Q3, Q4): +5, (Q4, Q1): +5, # Q -- CW --> Q => -1/2 (CW) (Q1, Q4): -5, (Q2, Q1): -5, (Q3, Q2): -5, (Q4, Q3): -5, } _rules_ambiguous = { # A -- CCW --> Q => { +1/4 (CCW), -9/4 (CW) } (A1, OO, Q1): -1, (A2, OO, Q2): -1, (A3, OO, Q3): -1, (A4, OO, Q4): -1, # A -- CW --> Q => { -1/4 (CCW), +7/4 (CW) } (A1, OO, Q4): -2, (A2, OO, Q1): -2, (A3, OO, Q2): -2, (A4, OO, Q3): -2, # Q -- CCW --> A => { +1/4 (CCW), -9/4 (CW) } (Q1, OO, A2): -3, (Q2, OO, A3): -3, (Q3, OO, A4): -3, (Q4, OO, A1): -3, # Q -- CW --> A => { -1/4 (CCW), +7/4 (CW) } (Q1, OO, A1): -4, (Q2, OO, A2): -4, (Q3, OO, A3): -4, (Q4, OO, A4): -4, # A -- OO --> A => { +1 (CCW), -1 (CW) } (A1, A3): 7, (A2, A4): 7, (A3, A1): 7, (A4, A2): 7, (A1, OO, A3): 7, (A2, OO, A4): 7, (A3, OO, A1): 7, (A4, OO, A2): 7, # Q -- DIA --> Q => { +1 (CCW), -1 (CW) } (Q1, Q3): 8, (Q2, Q4): 8, (Q3, Q1): 8, (Q4, Q2): 8, (Q1, OO, Q3): 8, (Q2, OO, Q4): 8, (Q3, OO, Q1): 8, (Q4, OO, Q2): 8, # A --- R ---> A => { +1/2 (CCW), -3/2 (CW) } (A1, A2): 9, (A2, A3): 9, (A3, A4): 9, (A4, A1): 9, (A1, OO, A2): 9, (A2, OO, A3): 9, (A3, OO, A4): 9, (A4, OO, A1): 9, # A --- L ---> A => { +3/2 (CCW), -1/2 (CW) } (A1, A4): 10, (A2, A1): 10, (A3, A2): 10, (A4, A3): 10, (A1, OO, A4): 10, (A2, OO, A1): 10, (A3, OO, A2): 10, (A4, OO, A3): 10, # Q --- 1 ---> A => { +3/4 (CCW), -5/4 (CW) } (Q1, A3): 11, (Q2, A4): 11, (Q3, A1): 11, (Q4, A2): 11, (Q1, OO, A3): 11, (Q2, OO, A4): 11, (Q3, OO, A1): 11, (Q4, OO, A2): 11, # Q --- 2 ---> A => { +5/4 (CCW), -3/4 (CW) } (Q1, A4): 12, (Q2, A1): 12, (Q3, A2): 12, (Q4, A3): 12, (Q1, OO, A4): 12, (Q2, OO, A1): 12, (Q3, OO, A2): 12, (Q4, OO, A3): 12, # A --- 1 ---> Q => { +5/4 (CCW), -3/4 (CW) } (A1, Q3): 13, (A2, Q4): 13, (A3, Q1): 13, (A4, Q2): 13, (A1, OO, Q3): 13, (A2, OO, Q4): 13, (A3, OO, Q1): 13, (A4, OO, Q2): 13, # A --- 2 ---> Q => { +3/4 (CCW), -5/4 (CW) } (A1, Q2): 14, (A2, Q3): 14, (A3, Q4): 14, (A4, Q1): 14, (A1, OO, Q2): 14, (A2, OO, Q3): 14, (A3, OO, Q4): 14, (A4, OO, Q1): 14, # Q --> OO --> Q => { +1/2 (CCW), -3/2 (CW) } (Q1, OO, Q2): 15, (Q2, OO, Q3): 15, (Q3, OO, Q4): 15, (Q4, OO, Q1): 15, # Q --> OO --> Q => { +3/2 (CCW), -1/2 (CW) } (Q1, OO, Q4): 16, (Q2, OO, Q1): 16, (Q3, OO, Q2): 16, (Q4, OO, Q3): 16, # A --> OO --> A => { +2 (CCW), 0 (CW) } (A1, OO, A1): 17, (A2, OO, A2): 17, (A3, OO, A3): 17, (A4, OO, A4): 17, # Q --> OO --> Q => { +2 (CCW), 0 (CW) } (Q1, OO, Q1): 18, (Q2, OO, Q2): 18, (Q3, OO, Q3): 18, (Q4, OO, Q4): 18, } _values = { 1: [(+1, 4)], 2: [(-1, 4)], 3: [(+1, 4)], 4: [(-1, 4)], -1: [(+9, 4), (+1, 4)], -2: [(+7, 4), (-1, 4)], -3: [(+9, 4), (+1, 4)], -4: [(+7, 4), (-1, 4)], +5: [(+1, 2)], -5: [(-1, 2)], 7: [(+1, 1), (-1, 1)], 8: [(+1, 1), (-1, 1)], 9: [(+1, 2), (-3, 2)], 10: [(+3, 2), (-1, 2)], 11: [(+3, 4), (-5, 4)], 12: [(+5, 4), (-3, 4)], 13: [(+5, 4), (-3, 4)], 14: [(+3, 4), (-5, 4)], 15: [(+1, 2), (-3, 2)], 16: [(+3, 2), (-1, 2)], 17: [(+2, 1), ( 0, 1)], 18: [(+2, 1), ( 0, 1)], } def _classify_point(re, im): """Return the half-axis (or origin) on which (re, im) point is located. """ if not re and not im: return OO if not re: if im > 0: return A2 else: return A4 elif not im: if re > 0: return A1 else: return A3 def _intervals_to_quadrants(intervals, f1, f2, s, t, F): """Generate a sequence of extended quadrants from a list of critical points. """ if not intervals: return [] Q = [] if not f1: (a, b), _, _ = intervals[0] if a == b == s: if len(intervals) == 1: if dup_eval(f2, t, F) > 0: return [OO, A2] else: return [OO, A4] else: (a, _), _, _ = intervals[1] if dup_eval(f2, (s + a)/2, F) > 0: Q.extend([OO, A2]) f2_sgn = +1 else: Q.extend([OO, A4]) f2_sgn = -1 intervals = intervals[1:] else: if dup_eval(f2, s, F) > 0: Q.append(A2) f2_sgn = +1 else: Q.append(A4) f2_sgn = -1 for (a, _), indices, _ in intervals: Q.append(OO) if indices[1] % 2 == 1: f2_sgn = -f2_sgn if a != t: if f2_sgn > 0: Q.append(A2) else: Q.append(A4) return Q if not f2: (a, b), _, _ = intervals[0] if a == b == s: if len(intervals) == 1: if dup_eval(f1, t, F) > 0: return [OO, A1] else: return [OO, A3] else: (a, _), _, _ = intervals[1] if dup_eval(f1, (s + a)/2, F) > 0: Q.extend([OO, A1]) f1_sgn = +1 else: Q.extend([OO, A3]) f1_sgn = -1 intervals = intervals[1:] else: if dup_eval(f1, s, F) > 0: Q.append(A1) f1_sgn = +1 else: Q.append(A3) f1_sgn = -1 for (a, _), indices, _ in intervals: Q.append(OO) if indices[0] % 2 == 1: f1_sgn = -f1_sgn if a != t: if f1_sgn > 0: Q.append(A1) else: Q.append(A3) return Q re = dup_eval(f1, s, F) im = dup_eval(f2, s, F) if not re or not im: Q.append(_classify_point(re, im)) if len(intervals) == 1: re = dup_eval(f1, t, F) im = dup_eval(f2, t, F) else: (a, _), _, _ = intervals[1] re = dup_eval(f1, (s + a)/2, F) im = dup_eval(f2, (s + a)/2, F) intervals = intervals[1:] if re > 0: f1_sgn = +1 else: f1_sgn = -1 if im > 0: f2_sgn = +1 else: f2_sgn = -1 sgn = { (+1, +1): Q1, (-1, +1): Q2, (-1, -1): Q3, (+1, -1): Q4, } Q.append(sgn[(f1_sgn, f2_sgn)]) for (a, b), indices, _ in intervals: if a == b: re = dup_eval(f1, a, F) im = dup_eval(f2, a, F) cls = _classify_point(re, im) if cls is not None: Q.append(cls) if 0 in indices: if indices[0] % 2 == 1: f1_sgn = -f1_sgn if 1 in indices: if indices[1] % 2 == 1: f2_sgn = -f2_sgn if not (a == b and b == t): Q.append(sgn[(f1_sgn, f2_sgn)]) return Q def _traverse_quadrants(Q_L1, Q_L2, Q_L3, Q_L4, exclude=None): """Transform sequences of quadrants to a sequence of rules. """ if exclude is True: edges = [1, 1, 0, 0] corners = { (0, 1): 1, (1, 2): 1, (2, 3): 0, (3, 0): 1, } else: edges = [0, 0, 0, 0] corners = { (0, 1): 0, (1, 2): 0, (2, 3): 0, (3, 0): 0, } if exclude is not None and exclude is not True: exclude = set(exclude) for i, edge in enumerate(['S', 'E', 'N', 'W']): if edge in exclude: edges[i] = 1 for i, corner in enumerate(['SW', 'SE', 'NE', 'NW']): if corner in exclude: corners[((i - 1) % 4, i)] = 1 QQ, rules = [Q_L1, Q_L2, Q_L3, Q_L4], [] for i, Q in enumerate(QQ): if not Q: continue if Q[-1] == OO: Q = Q[:-1] if Q[0] == OO: j, Q = (i - 1) % 4, Q[1:] qq = (QQ[j][-2], OO, Q[0]) if qq in _rules_ambiguous: rules.append((_rules_ambiguous[qq], corners[(j, i)])) else: raise NotImplementedError("3 element rule (corner): " + str(qq)) q1, k = Q[0], 1 while k < len(Q): q2, k = Q[k], k + 1 if q2 != OO: qq = (q1, q2) if qq in _rules_simple: rules.append((_rules_simple[qq], 0)) elif qq in _rules_ambiguous: rules.append((_rules_ambiguous[qq], edges[i])) else: raise NotImplementedError("2 element rule (inside): " + str(qq)) else: qq, k = (q1, q2, Q[k]), k + 1 if qq in _rules_ambiguous: rules.append((_rules_ambiguous[qq], edges[i])) else: raise NotImplementedError("3 element rule (edge): " + str(qq)) q1 = qq[-1] return rules def _reverse_intervals(intervals): """Reverse intervals for traversal from right to left and from top to bottom. """ return [ ((b, a), indices, f) for (a, b), indices, f in reversed(intervals) ] def _winding_number(T, field): """Compute the winding number of the input polynomial, i.e. the number of roots. """ return int(sum([ field(*_values[t][i]) for t, i in T ]) / field(2)) def dup_count_complex_roots(f, K, inf=None, sup=None, exclude=None): """Count all roots in [u + v*I, s + t*I] rectangle using Collins-Krandick algorithm. """ if not K.is_ZZ and not K.is_QQ: raise DomainError("complex root counting is not supported over %s" % K) if K.is_ZZ: R, F = K, K.get_field() else: R, F = K.get_ring(), K f = dup_convert(f, K, F) if inf is None or sup is None: n, lc = dup_degree(f), abs(dup_LC(f, F)) B = 2*max([ F.quo(abs(c), lc) for c in f ]) if inf is None: (u, v) = (-B, -B) else: (u, v) = inf if sup is None: (s, t) = (+B, +B) else: (s, t) = sup f1, f2 = dup_real_imag(f, F) f1L1F = dmp_eval_in(f1, v, 1, 1, F) f2L1F = dmp_eval_in(f2, v, 1, 1, F) _, f1L1R = dup_clear_denoms(f1L1F, F, R, convert=True) _, f2L1R = dup_clear_denoms(f2L1F, F, R, convert=True) f1L2F = dmp_eval_in(f1, s, 0, 1, F) f2L2F = dmp_eval_in(f2, s, 0, 1, F) _, f1L2R = dup_clear_denoms(f1L2F, F, R, convert=True) _, f2L2R = dup_clear_denoms(f2L2F, F, R, convert=True) f1L3F = dmp_eval_in(f1, t, 1, 1, F) f2L3F = dmp_eval_in(f2, t, 1, 1, F) _, f1L3R = dup_clear_denoms(f1L3F, F, R, convert=True) _, f2L3R = dup_clear_denoms(f2L3F, F, R, convert=True) f1L4F = dmp_eval_in(f1, u, 0, 1, F) f2L4F = dmp_eval_in(f2, u, 0, 1, F) _, f1L4R = dup_clear_denoms(f1L4F, F, R, convert=True) _, f2L4R = dup_clear_denoms(f2L4F, F, R, convert=True) S_L1 = [f1L1R, f2L1R] S_L2 = [f1L2R, f2L2R] S_L3 = [f1L3R, f2L3R] S_L4 = [f1L4R, f2L4R] I_L1 = dup_isolate_real_roots_list(S_L1, R, inf=u, sup=s, fast=True, basis=True, strict=True) I_L2 = dup_isolate_real_roots_list(S_L2, R, inf=v, sup=t, fast=True, basis=True, strict=True) I_L3 = dup_isolate_real_roots_list(S_L3, R, inf=u, sup=s, fast=True, basis=True, strict=True) I_L4 = dup_isolate_real_roots_list(S_L4, R, inf=v, sup=t, fast=True, basis=True, strict=True) I_L3 = _reverse_intervals(I_L3) I_L4 = _reverse_intervals(I_L4) Q_L1 = _intervals_to_quadrants(I_L1, f1L1F, f2L1F, u, s, F) Q_L2 = _intervals_to_quadrants(I_L2, f1L2F, f2L2F, v, t, F) Q_L3 = _intervals_to_quadrants(I_L3, f1L3F, f2L3F, s, u, F) Q_L4 = _intervals_to_quadrants(I_L4, f1L4F, f2L4F, t, v, F) T = _traverse_quadrants(Q_L1, Q_L2, Q_L3, Q_L4, exclude=exclude) return _winding_number(T, F) def _vertical_bisection(N, a, b, I, Q, F1, F2, f1, f2, F): """Vertical bisection step in Collins-Krandick root isolation algorithm. """ (u, v), (s, t) = a, b I_L1, I_L2, I_L3, I_L4 = I Q_L1, Q_L2, Q_L3, Q_L4 = Q f1L1F, f1L2F, f1L3F, f1L4F = F1 f2L1F, f2L2F, f2L3F, f2L4F = F2 x = (u + s) / 2 f1V = dmp_eval_in(f1, x, 0, 1, F) f2V = dmp_eval_in(f2, x, 0, 1, F) I_V = dup_isolate_real_roots_list([f1V, f2V], F, inf=v, sup=t, fast=True, strict=True, basis=True) I_L1_L, I_L1_R = [], [] I_L2_L, I_L2_R = I_V, I_L2 I_L3_L, I_L3_R = [], [] I_L4_L, I_L4_R = I_L4, _reverse_intervals(I_V) for I in I_L1: (a, b), indices, h = I if a == b: if a == x: I_L1_L.append(I) I_L1_R.append(I) elif a < x: I_L1_L.append(I) else: I_L1_R.append(I) else: if b <= x: I_L1_L.append(I) elif a >= x: I_L1_R.append(I) else: a, b = dup_refine_real_root(h, a, b, F.get_ring(), disjoint=x, fast=True) if b <= x: I_L1_L.append(((a, b), indices, h)) else: I_L1_R.append(((a, b), indices, h)) for I in I_L3: (b, a), indices, h = I if a == b: if a == x: I_L3_L.append(I) I_L3_R.append(I) elif a < x: I_L3_L.append(I) else: I_L3_R.append(I) else: if b <= x: I_L3_L.append(I) elif a >= x: I_L3_R.append(I) else: a, b = dup_refine_real_root(h, a, b, F.get_ring(), disjoint=x, fast=True) if b <= x: I_L3_L.append(((b, a), indices, h)) else: I_L3_R.append(((b, a), indices, h)) Q_L1_L = _intervals_to_quadrants(I_L1_L, f1L1F, f2L1F, u, x, F) Q_L2_L = _intervals_to_quadrants(I_L2_L, f1V, f2V, v, t, F) Q_L3_L = _intervals_to_quadrants(I_L3_L, f1L3F, f2L3F, x, u, F) Q_L4_L = Q_L4 Q_L1_R = _intervals_to_quadrants(I_L1_R, f1L1F, f2L1F, x, s, F) Q_L2_R = Q_L2 Q_L3_R = _intervals_to_quadrants(I_L3_R, f1L3F, f2L3F, s, x, F) Q_L4_R = _intervals_to_quadrants(I_L4_R, f1V, f2V, t, v, F) T_L = _traverse_quadrants(Q_L1_L, Q_L2_L, Q_L3_L, Q_L4_L, exclude=True) T_R = _traverse_quadrants(Q_L1_R, Q_L2_R, Q_L3_R, Q_L4_R, exclude=True) N_L = _winding_number(T_L, F) N_R = _winding_number(T_R, F) I_L = (I_L1_L, I_L2_L, I_L3_L, I_L4_L) Q_L = (Q_L1_L, Q_L2_L, Q_L3_L, Q_L4_L) I_R = (I_L1_R, I_L2_R, I_L3_R, I_L4_R) Q_R = (Q_L1_R, Q_L2_R, Q_L3_R, Q_L4_R) F1_L = (f1L1F, f1V, f1L3F, f1L4F) F2_L = (f2L1F, f2V, f2L3F, f2L4F) F1_R = (f1L1F, f1L2F, f1L3F, f1V) F2_R = (f2L1F, f2L2F, f2L3F, f2V) a, b = (u, v), (x, t) c, d = (x, v), (s, t) D_L = (N_L, a, b, I_L, Q_L, F1_L, F2_L) D_R = (N_R, c, d, I_R, Q_R, F1_R, F2_R) return D_L, D_R def _horizontal_bisection(N, a, b, I, Q, F1, F2, f1, f2, F): """Horizontal bisection step in Collins-Krandick root isolation algorithm. """ (u, v), (s, t) = a, b I_L1, I_L2, I_L3, I_L4 = I Q_L1, Q_L2, Q_L3, Q_L4 = Q f1L1F, f1L2F, f1L3F, f1L4F = F1 f2L1F, f2L2F, f2L3F, f2L4F = F2 y = (v + t) / 2 f1H = dmp_eval_in(f1, y, 1, 1, F) f2H = dmp_eval_in(f2, y, 1, 1, F) I_H = dup_isolate_real_roots_list([f1H, f2H], F, inf=u, sup=s, fast=True, strict=True, basis=True) I_L1_B, I_L1_U = I_L1, I_H I_L2_B, I_L2_U = [], [] I_L3_B, I_L3_U = _reverse_intervals(I_H), I_L3 I_L4_B, I_L4_U = [], [] for I in I_L2: (a, b), indices, h = I if a == b: if a == y: I_L2_B.append(I) I_L2_U.append(I) elif a < y: I_L2_B.append(I) else: I_L2_U.append(I) else: if b <= y: I_L2_B.append(I) elif a >= y: I_L2_U.append(I) else: a, b = dup_refine_real_root(h, a, b, F.get_ring(), disjoint=y, fast=True) if b <= y: I_L2_B.append(((a, b), indices, h)) else: I_L2_U.append(((a, b), indices, h)) for I in I_L4: (b, a), indices, h = I if a == b: if a == y: I_L4_B.append(I) I_L4_U.append(I) elif a < y: I_L4_B.append(I) else: I_L4_U.append(I) else: if b <= y: I_L4_B.append(I) elif a >= y: I_L4_U.append(I) else: a, b = dup_refine_real_root(h, a, b, F.get_ring(), disjoint=y, fast=True) if b <= y: I_L4_B.append(((b, a), indices, h)) else: I_L4_U.append(((b, a), indices, h)) Q_L1_B = Q_L1 Q_L2_B = _intervals_to_quadrants(I_L2_B, f1L2F, f2L2F, v, y, F) Q_L3_B = _intervals_to_quadrants(I_L3_B, f1H, f2H, s, u, F) Q_L4_B = _intervals_to_quadrants(I_L4_B, f1L4F, f2L4F, y, v, F) Q_L1_U = _intervals_to_quadrants(I_L1_U, f1H, f2H, u, s, F) Q_L2_U = _intervals_to_quadrants(I_L2_U, f1L2F, f2L2F, y, t, F) Q_L3_U = Q_L3 Q_L4_U = _intervals_to_quadrants(I_L4_U, f1L4F, f2L4F, t, y, F) T_B = _traverse_quadrants(Q_L1_B, Q_L2_B, Q_L3_B, Q_L4_B, exclude=True) T_U = _traverse_quadrants(Q_L1_U, Q_L2_U, Q_L3_U, Q_L4_U, exclude=True) N_B = _winding_number(T_B, F) N_U = _winding_number(T_U, F) I_B = (I_L1_B, I_L2_B, I_L3_B, I_L4_B) Q_B = (Q_L1_B, Q_L2_B, Q_L3_B, Q_L4_B) I_U = (I_L1_U, I_L2_U, I_L3_U, I_L4_U) Q_U = (Q_L1_U, Q_L2_U, Q_L3_U, Q_L4_U) F1_B = (f1L1F, f1L2F, f1H, f1L4F) F2_B = (f2L1F, f2L2F, f2H, f2L4F) F1_U = (f1H, f1L2F, f1L3F, f1L4F) F2_U = (f2H, f2L2F, f2L3F, f2L4F) a, b = (u, v), (s, y) c, d = (u, y), (s, t) D_B = (N_B, a, b, I_B, Q_B, F1_B, F2_B) D_U = (N_U, c, d, I_U, Q_U, F1_U, F2_U) return D_B, D_U def _depth_first_select(rectangles): """Find a rectangle of minimum area for bisection. """ min_area, j = None, None for i, (_, (u, v), (s, t), _, _, _, _) in enumerate(rectangles): area = (s - u)*(t - v) if min_area is None or area < min_area: min_area, j = area, i return rectangles.pop(j) def _rectangle_small_p(a, b, eps): """Return ``True`` if the given rectangle is small enough. """ (u, v), (s, t) = a, b if eps is not None: return s - u < eps and t - v < eps else: return True def dup_isolate_complex_roots_sqf(f, K, eps=None, inf=None, sup=None, blackbox=False): """Isolate complex roots of a square-free polynomial using Collins-Krandick algorithm. """ if not K.is_ZZ and not K.is_QQ: raise DomainError("isolation of complex roots is not supported over %s" % K) if dup_degree(f) <= 0: return [] if K.is_ZZ: R, F = K, K.get_field() else: R, F = K.get_ring(), K f = dup_convert(f, K, F) n, lc = dup_degree(f), abs(dup_LC(f, F)) B = 2*max([ F.quo(abs(c), lc) for c in f ]) (u, v), (s, t) = (-B, F.zero), (B, B) if inf is not None: u = inf if sup is not None: s = sup if v < 0 or t <= v or s <= u: raise ValueError("not a valid complex isolation rectangle") f1, f2 = dup_real_imag(f, F) f1L1 = dmp_eval_in(f1, v, 1, 1, F) f2L1 = dmp_eval_in(f2, v, 1, 1, F) f1L2 = dmp_eval_in(f1, s, 0, 1, F) f2L2 = dmp_eval_in(f2, s, 0, 1, F) f1L3 = dmp_eval_in(f1, t, 1, 1, F) f2L3 = dmp_eval_in(f2, t, 1, 1, F) f1L4 = dmp_eval_in(f1, u, 0, 1, F) f2L4 = dmp_eval_in(f2, u, 0, 1, F) S_L1 = [f1L1, f2L1] S_L2 = [f1L2, f2L2] S_L3 = [f1L3, f2L3] S_L4 = [f1L4, f2L4] I_L1 = dup_isolate_real_roots_list(S_L1, F, inf=u, sup=s, fast=True, strict=True, basis=True) I_L2 = dup_isolate_real_roots_list(S_L2, F, inf=v, sup=t, fast=True, strict=True, basis=True) I_L3 = dup_isolate_real_roots_list(S_L3, F, inf=u, sup=s, fast=True, strict=True, basis=True) I_L4 = dup_isolate_real_roots_list(S_L4, F, inf=v, sup=t, fast=True, strict=True, basis=True) I_L3 = _reverse_intervals(I_L3) I_L4 = _reverse_intervals(I_L4) Q_L1 = _intervals_to_quadrants(I_L1, f1L1, f2L1, u, s, F) Q_L2 = _intervals_to_quadrants(I_L2, f1L2, f2L2, v, t, F) Q_L3 = _intervals_to_quadrants(I_L3, f1L3, f2L3, s, u, F) Q_L4 = _intervals_to_quadrants(I_L4, f1L4, f2L4, t, v, F) T = _traverse_quadrants(Q_L1, Q_L2, Q_L3, Q_L4) N = _winding_number(T, F) if not N: return [] I = (I_L1, I_L2, I_L3, I_L4) Q = (Q_L1, Q_L2, Q_L3, Q_L4) F1 = (f1L1, f1L2, f1L3, f1L4) F2 = (f2L1, f2L2, f2L3, f2L4) rectangles, roots = [(N, (u, v), (s, t), I, Q, F1, F2)], [] while rectangles: N, (u, v), (s, t), I, Q, F1, F2 = _depth_first_select(rectangles) if s - u > t - v: D_L, D_R = _vertical_bisection(N, (u, v), (s, t), I, Q, F1, F2, f1, f2, F) N_L, a, b, I_L, Q_L, F1_L, F2_L = D_L N_R, c, d, I_R, Q_R, F1_R, F2_R = D_R if N_L >= 1: if N_L == 1 and _rectangle_small_p(a, b, eps): roots.append(ComplexInterval(a, b, I_L, Q_L, F1_L, F2_L, f1, f2, F)) else: rectangles.append(D_L) if N_R >= 1: if N_R == 1 and _rectangle_small_p(c, d, eps): roots.append(ComplexInterval(c, d, I_R, Q_R, F1_R, F2_R, f1, f2, F)) else: rectangles.append(D_R) else: D_B, D_U = _horizontal_bisection(N, (u, v), (s, t), I, Q, F1, F2, f1, f2, F) N_B, a, b, I_B, Q_B, F1_B, F2_B = D_B N_U, c, d, I_U, Q_U, F1_U, F2_U = D_U if N_B >= 1: if N_B == 1 and _rectangle_small_p(a, b, eps): roots.append(ComplexInterval( a, b, I_B, Q_B, F1_B, F2_B, f1, f2, F)) else: rectangles.append(D_B) if N_U >= 1: if N_U == 1 and _rectangle_small_p(c, d, eps): roots.append(ComplexInterval( c, d, I_U, Q_U, F1_U, F2_U, f1, f2, F)) else: rectangles.append(D_U) _roots, roots = sorted(roots, key=lambda r: (r.ax, r.ay)), [] for root in _roots: roots.extend([root.conjugate(), root]) if blackbox: return roots else: return [ r.as_tuple() for r in roots ] def dup_isolate_all_roots_sqf(f, K, eps=None, inf=None, sup=None, fast=False, blackbox=False): """Isolate real and complex roots of a square-free polynomial ``f``. """ return ( dup_isolate_real_roots_sqf( f, K, eps=eps, inf=inf, sup=sup, fast=fast, blackbox=blackbox), dup_isolate_complex_roots_sqf(f, K, eps=eps, inf=inf, sup=sup, blackbox=blackbox)) def dup_isolate_all_roots(f, K, eps=None, inf=None, sup=None, fast=False): """Isolate real and complex roots of a non-square-free polynomial ``f``. """ if not K.is_ZZ and not K.is_QQ: raise DomainError("isolation of real and complex roots is not supported over %s" % K) _, factors = dup_sqf_list(f, K) if len(factors) == 1: ((f, k),) = factors real_part, complex_part = dup_isolate_all_roots_sqf( f, K, eps=eps, inf=inf, sup=sup, fast=fast) real_part = [ ((a, b), k) for (a, b) in real_part ] complex_part = [ ((a, b), k) for (a, b) in complex_part ] return real_part, complex_part else: raise NotImplementedError( "only trivial square-free polynomials are supported") class RealInterval(object): """A fully qualified representation of a real isolation interval. """ def __init__(self, data, f, dom): """Initialize new real interval with complete information. """ if len(data) == 2: s, t = data self.neg = False if s < 0: if t <= 0: f, s, t, self.neg = dup_mirror(f, dom), -t, -s, True else: raise ValueError("can't refine a real root in (%s, %s)" % (s, t)) a, b, c, d = _mobius_from_interval((s, t), dom.get_field()) f = dup_transform(f, dup_strip([a, b]), dup_strip([c, d]), dom) self.mobius = a, b, c, d else: self.mobius = data[:-1] self.neg = data[-1] self.f, self.dom = f, dom @property def a(self): """Return the position of the left end. """ field = self.dom.get_field() a, b, c, d = self.mobius if not self.neg: return field(a, c) else: return -field(b, d) @property def b(self): """Return the position of the right end. """ field = self.dom.get_field() a, b, c, d = self.mobius if not self.neg: return field(b, d) else: return -field(a, c) @property def dx(self): """Return width of the real isolating interval. """ return self.b - self.a @property def center(self): """Return the center of the real isolating interval. """ return (self.a + self.b)/2 def as_tuple(self): """Return tuple representation of real isolating interval. """ return (self.a, self.b) def __repr__(self): return "(%s, %s)" % (self.a, self.b) def is_disjoint(self, other): """Return ``True`` if two isolation intervals are disjoint. """ return (self.b <= other.a or other.b <= self.a) def _inner_refine(self): """Internal one step real root refinement procedure. """ if self.mobius is None: return self f, mobius = dup_inner_refine_real_root( self.f, self.mobius, self.dom, steps=1, mobius=True) return RealInterval(mobius + (self.neg,), f, self.dom) def refine_disjoint(self, other): """Refine an isolating interval until it is disjoint with another one. """ while not self.is_disjoint(other): self, other = self._inner_refine(), other._inner_refine() return self, other def refine_size(self, dx): """Refine an isolating interval until it is of sufficiently small size. """ while not (self.dx < dx): self = self._inner_refine() return self def refine_step(self, steps=1): """Perform several steps of real root refinement algorithm. """ for _ in xrange(steps): self = self._inner_refine() return self def refine(self): """Perform one step of real root refinement algorithm. """ return self._inner_refine() class ComplexInterval(object): """A fully qualified representation of a complex isolation interval. """ def __init__(self, a, b, I, Q, F1, F2, f1, f2, dom, conj=False): """Initialize new complex interval with complete information. """ self.a, self.b = a, b self.I, self.Q = I, Q self.f1, self.F1 = f1, F1 self.f2, self.F2 = f2, F2 self.dom = dom self.conj = conj @property def ax(self): """Return ``x`` coordinate of south-western corner. """ return self.a[0] @property def ay(self): """Return ``y`` coordinate of south-western corner. """ if not self.conj: return self.a[1] else: return -self.b[1] @property def bx(self): """Return ``x`` coordinate of north-eastern corner. """ return self.b[0] @property def by(self): """Return ``y`` coordinate of north-eastern corner. """ if not self.conj: return self.b[1] else: return -self.a[1] @property def dx(self): """Return width of the complex isolating interval. """ return self.b[0] - self.a[0] @property def dy(self): """Return height of the complex isolating interval. """ return self.b[1] - self.a[1] @property def center(self): """Return the center of the complex isolating interval. """ return ((self.ax + self.bx)/2, (self.ay + self.by)/2) def as_tuple(self): """Return tuple representation of complex isolating interval. """ return ((self.ax, self.ay), (self.bx, self.by)) def __repr__(self): return "(%s, %s) x (%s, %s)" % (self.ax, self.bx, self.ay, self.by) def conjugate(self): """This complex interval really is located in lower half-plane. """ return ComplexInterval(self.a, self.b, self.I, self.Q, self.F1, self.F2, self.f1, self.f2, self.dom, conj=True) def is_disjoint(self, other): """Return ``True`` if two isolation intervals are disjoint. """ return ((self.conj != other.conj) or ((self.bx <= other.ax or other.bx <= self.ax) or (self.by <= other.ay or other.by <= self.ay))) def _inner_refine(self): """Internal one step complex root refinement procedure. """ (u, v), (s, t) = self.a, self.b I, Q = self.I, self.Q f1, F1 = self.f1, self.F1 f2, F2 = self.f2, self.F2 dom = self.dom if s - u > t - v: D_L, D_R = _vertical_bisection(1, (u, v), (s, t), I, Q, F1, F2, f1, f2, dom) if D_L[0] == 1: _, a, b, I, Q, F1, F2 = D_L else: _, a, b, I, Q, F1, F2 = D_R else: D_B, D_U = _horizontal_bisection(1, (u, v), (s, t), I, Q, F1, F2, f1, f2, dom) if D_B[0] == 1: _, a, b, I, Q, F1, F2 = D_B else: _, a, b, I, Q, F1, F2 = D_U return ComplexInterval(a, b, I, Q, F1, F2, f1, f2, dom, self.conj) def refine_disjoint(self, other): """Refine an isolating interval until it is disjoint with another one. """ while not self.is_disjoint(other): self, other = self._inner_refine(), other._inner_refine() return self, other def refine_size(self, dx, dy=None): """Refine an isolating interval until it is of sufficiently small size. """ if dy is None: dy = dx while not (self.dx < dx and self.dy < dy): self = self._inner_refine() return self def refine_step(self, steps=1): """Perform several steps of complex root refinement algorithm. """ for _ in xrange(steps): self = self._inner_refine() return self def refine(self): """Perform one step of complex root refinement algorithm. """ return self._inner_refine() sympy-0.7.4.1/sympy/polys/orthopolys.py0000644000175000017500000002031012253362407020363 0ustar georgeskgeorgesk"""Efficient functions for generating orthogonal polynomials. """ from __future__ import print_function, division from sympy import Dummy from sympy.utilities import public 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 from sympy.core.compatibility import xrange 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 xrange(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] @public 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 def dup_gegenbauer(n, a, K): """Low-level implementation of Gegenbauer polynomials. """ seq = [[K.one], [K(2)*a, K.zero]] for i in xrange(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] 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 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 xrange(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] @public 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 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 xrange(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] @public 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 def dup_hermite(n, K): """Low-level implementation of Hermite polynomials. """ seq = [[K.one], [K(2), K.zero]] for i in xrange(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] @public 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 def dup_legendre(n, K): """Low-level implementation of Legendre polynomials. """ seq = [[K.one], [K.one, K.zero]] for i in xrange(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] @public 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 def dup_laguerre(n, alpha, K): """Low-level implementation of Laguerre polynomials. """ seq = [[K.zero], [K.one]] for i in xrange(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] @public 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 def dup_spherical_bessel_fn(n, K): """ Low-level implementation of fn(n, x) """ seq = [[K.one], [K.one, K.zero]] for i in xrange(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) 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 xrange(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 sympy-0.7.4.1/sympy/polys/polyfuncs.py0000644000175000017500000001722112253362407020172 0ustar georgeskgeorgesk"""High-level polynomials manipulation functions. """ from __future__ import print_function, division 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, public from sympy.core import S, Basic, Add, Mul from sympy.core.compatibility import xrange @public 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 xrange(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,) @public 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 @public 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(*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() @public 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 sympy-0.7.4.1/sympy/polys/domains/0000755000175000017500000000000012253362407017225 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/polys/domains/groundtypes.py0000644000175000017500000000322512253362407022164 0ustar georgeskgeorgesk"""Ground types for various mathematical domains in SymPy. """ from __future__ import print_function, division __all__ = [] from sympy.core.compatibility import builtins, HAS_GMPY PythonInteger = builtins.int PythonReal = builtins.float PythonComplex = builtins.complex from .pythonrational import PythonRational from sympy.core.numbers import ( igcdex as python_gcdex, igcd as python_gcd, ilcm as python_lcm, ) from sympy import ( Float as SymPyReal, Integer as SymPyInteger, Rational as SymPyRational, ) if HAS_GMPY == 1: from gmpy import ( mpz as GMPYInteger, mpq as GMPYRational, fac as gmpy_factorial, numer as gmpy_numer, denom as gmpy_denom, gcdext as gmpy_gcdex, gcd as gmpy_gcd, lcm as gmpy_lcm, sqrt as gmpy_sqrt, qdiv as gmpy_qdiv, ) elif HAS_GMPY == 2: from gmpy2 import ( mpz as GMPYInteger, mpq as GMPYRational, fac as gmpy_factorial, numer as gmpy_numer, denom as gmpy_denom, gcdext as gmpy_gcdex, gcd as gmpy_gcd, lcm as gmpy_lcm, isqrt as gmpy_sqrt, qdiv as gmpy_qdiv, ) else: class GMPYInteger(object): def __init__(self, obj): pass class GMPYRational(object): def __init__(self, obj): pass gmpy_factorial = None gmpy_numer = None gmpy_denom = None gmpy_gcdex = None gmpy_gcd = None gmpy_lcm = None gmpy_sqrt = None gmpy_qdiv = None import sympy.mpmath.libmp as mlib def python_sqrt(n): return int(mlib.isqrt(n)) def python_factorial(n): return int(mlib.ifac(n)) sympy-0.7.4.1/sympy/polys/domains/rationalfield.py0000644000175000017500000000166712253362407022426 0ustar georgeskgeorgesk"""Implementation of :class:`RationalField` class. """ from __future__ import print_function, division from sympy.polys.domains.field import Field from sympy.polys.domains.simpledomain import SimpleDomain from sympy.polys.domains.characteristiczero import CharacteristicZero from sympy.utilities import public @public class RationalField(Field, CharacteristicZero, SimpleDomain): """General class for rational fields. """ rep = 'QQ' is_RationalField = is_QQ = True is_Numerical = True has_assoc_Ring = True has_assoc_Field = True def algebraic_field(self, *extension): r"""Returns an algebraic field, i.e. `\mathbb{Q}(\alpha, \dots)`. """ from sympy.polys.domains import AlgebraicField return AlgebraicField(self, *extension) def from_AlgebraicField(K1, a, K0): """Convert a ``ANP`` object to ``dtype``. """ if a.is_ground: return K1.convert(a.LC(), K0.dom) sympy-0.7.4.1/sympy/polys/domains/quotientring.py0000644000175000017500000001320312253362407022326 0ustar georgeskgeorgesk"""Implementation of :class:`QuotientRing` class.""" from __future__ import print_function, division from sympy.polys.domains.ring import Ring from sympy.polys.polyerrors import NotReversible, CoercionFailed from sympy.polys.agca.modules import FreeModuleQuotientRing from sympy.utilities import public # TODO # - successive quotients (when quotient ideals are implemented) # - poly rings over quotients? # - division by non-units in integral domains? @public class QuotientRingElement(object): """ Class representing elements of (commutative) quotient rings. Attributes: - ring - containing ring - data - element of ring.ring (i.e. base ring) representing self """ def __init__(self, ring, data): self.ring = ring self.data = data def __str__(self): from sympy import sstr return sstr(self.data) + " + " + str(self.ring.base_ideal) def __add__(self, om): if not isinstance(om, self.__class__) or om.ring != self.ring: try: om = self.ring.convert(om) except (NotImplementedError, CoercionFailed): return NotImplemented return self.ring(self.data + om.data) __radd__ = __add__ def __neg__(self): return self.ring(self.data*self.ring.ring.convert(-1)) def __sub__(self, om): return self.__add__(-om) def __rsub__(self, om): return (-self).__add__(om) def __mul__(self, o): if not isinstance(o, self.__class__): try: o = self.ring.convert(o) except (NotImplementedError, CoercionFailed): return NotImplemented return self.ring(self.data*o.data) __rmul__ = __mul__ def __rdiv__(self, o): return self.ring.revert(self)*o __rtruediv__ = __rdiv__ def __div__(self, o): if not isinstance(o, self.__class__): try: o = self.ring.convert(o) except (NotImplementedError, CoercionFailed): return NotImplemented return self.ring.revert(o)*self __truediv__ = __div__ def __pow__(self, oth): return self.ring(self.data**oth) def __eq__(self, om): if not isinstance(om, self.__class__) or om.ring != self.ring: return False return self.ring.is_zero(self - om) def __ne__(self, om): return not self.__eq__(om) class QuotientRing(Ring): """ Class representing (commutative) quotient rings. You should not usually instantiate this by hand, instead use the constructor from the base ring in the construction. >>> from sympy.abc import x >>> from sympy import QQ >>> I = QQ.old_poly_ring(x).ideal(x**3 + 1) >>> QQ.old_poly_ring(x).quotient_ring(I) QQ[x]/ Shorter versions are possible: >>> QQ.old_poly_ring(x)/I QQ[x]/ >>> QQ.old_poly_ring(x)/[x**3 + 1] QQ[x]/ Attributes: - ring - the base ring - base_ideal - the ideal used to form the quotient """ has_assoc_Ring = True has_assoc_Field = False dtype = QuotientRingElement def __init__(self, ring, ideal): if not ideal.ring == ring: raise ValueError('Ideal must belong to %s, got %s' % (ring, ideal)) self.ring = ring self.base_ideal = ideal self.zero = self(self.ring.zero) self.one = self(self.ring.one) def __str__(self): return str(self.ring) + "/" + str(self.base_ideal) def __hash__(self): return hash((self.__class__.__name__, self.dtype, self.ring, self.base_ideal)) def new(self, a): """Construct an element of `self` domain from `a`. """ if not isinstance(a, self.ring.dtype): a = self.ring(a) # TODO optionally disable reduction? return self.dtype(self, self.base_ideal.reduce_element(a)) def __eq__(self, other): """Returns `True` if two domains are equivalent. """ return isinstance(other, QuotientRing) and \ self.ring == other.ring and self.base_ideal == other.base_ideal def from_ZZ_python(K1, a, K0): """Convert a Python `int` object to `dtype`. """ return K1(K1.ring.convert(a, K0)) from_QQ_python = from_ZZ_python from_ZZ_gmpy = from_ZZ_python from_QQ_gmpy = from_ZZ_python from_RealField = from_ZZ_python from_GlobalPolynomialRing = from_ZZ_python from_FractionField = from_ZZ_python def from_sympy(self, a): return self(self.ring.from_sympy(a)) def to_sympy(self, a): return self.ring.to_sympy(a.data) def from_QuotientRing(self, a, K0): if K0 == self: return a def poly_ring(self, *gens): """Returns a polynomial ring, i.e. `K[X]`. """ raise NotImplementedError('nested domains not allowed') def frac_field(self, *gens): """Returns a fraction field, i.e. `K(X)`. """ raise NotImplementedError('nested domains not allowed') def revert(self, a): """ Compute a**(-1), if possible. """ I = self.ring.ideal(a.data) + self.base_ideal try: return self(I.in_terms_of_generators(1)[0]) except ValueError: # 1 not in I raise NotReversible('%s not a unit in %r' % (a, self)) def is_zero(self, a): return self.base_ideal.contains(a.data) def free_module(self, rank): """ Generate a free module of rank ``rank`` over ``self``. >>> from sympy.abc import x >>> from sympy import QQ >>> (QQ.old_poly_ring(x)/[x**2 + 1]).free_module(2) (QQ[x]/)**2 """ return FreeModuleQuotientRing(self, rank) sympy-0.7.4.1/sympy/polys/domains/pythonintegerring.py0000644000175000017500000000513212253362407023357 0ustar georgeskgeorgesk"""Implementaton of :class:`PythonIntegerRing` class. """ from __future__ import print_function, division from sympy.polys.domains.integerring import IntegerRing from sympy.polys.domains.groundtypes import ( PythonInteger, SymPyInteger, python_sqrt, python_factorial, python_gcdex, python_gcd, python_lcm, ) from sympy.polys.polyerrors import CoercionFailed from sympy.utilities import public @public class PythonIntegerRing(IntegerRing): """Integer ring based on Python's ``int`` type. """ dtype = PythonInteger zero = dtype(0) one = dtype(1) alias = 'ZZ_python' def __init__(self): """Allow instantiation of this domain. """ def to_sympy(self, a): """Convert ``a`` to a SymPy object. """ return SymPyInteger(a) def from_sympy(self, a): """Convert SymPy's Integer to ``dtype``. """ if a.is_Integer: return PythonInteger(a.p) elif a.is_Float and int(a) == a: return PythonInteger(int(a)) else: raise CoercionFailed("expected an integer, got %s" % a) def from_FF_python(K1, a, K0): """Convert ``ModularInteger(int)`` to Python's ``int``. """ return a.to_int() def from_ZZ_python(K1, a, K0): """Convert Python's ``int`` to Python's ``int``. """ return a def from_QQ_python(K1, a, K0): """Convert Python's ``Fraction`` to Python's ``int``. """ if a.denominator == 1: return a.numerator def from_FF_gmpy(K1, a, K0): """Convert ``ModularInteger(mpz)`` to Python's ``int``. """ return PythonInteger(a.to_int()) def from_ZZ_gmpy(K1, a, K0): """Convert GMPY's ``mpz`` to Python's ``int``. """ return PythonInteger(a) def from_QQ_gmpy(K1, a, K0): """Convert GMPY's ``mpq`` to Python's ``int``. """ if a.denom() == 1: return PythonInteger(a.numer()) def from_RealField(K1, a, K0): """Convert mpmath's ``mpf`` to Python's ``int``. """ p, q = K0.to_rational(a) if q == 1: return PythonInteger(p) def gcdex(self, a, b): """Compute extended GCD of ``a`` and ``b``. """ return python_gcdex(a, b) def gcd(self, a, b): """Compute GCD of ``a`` and ``b``. """ return python_gcd(a, b) def lcm(self, a, b): """Compute LCM of ``a`` and ``b``. """ return python_lcm(a, b) def sqrt(self, a): """Compute square root of ``a``. """ return python_sqrt(a) def factorial(self, a): """Compute factorial of ``a``. """ return python_factorial(a) sympy-0.7.4.1/sympy/polys/domains/domainelement.py0000644000175000017500000000067312253362407022426 0ustar georgeskgeorgesk"""Trait for implementing domain elements. """ from __future__ import print_function, division from sympy.utilities import public @public class DomainElement(object): """ Represents an element of a domain. Mix in this trait into a class which instances should be recognized as elements of a domain. Method ``parent()`` gives that domain. """ def parent(self): raise NotImplementedError("abstract method") sympy-0.7.4.1/sympy/polys/domains/gmpyfinitefield.py0000644000175000017500000000100112253362407022746 0ustar georgeskgeorgesk"""Implementation of :class:`GMPYFiniteField` class. """ from __future__ import print_function, division from sympy.polys.domains.finitefield import FiniteField from sympy.polys.domains.gmpyintegerring import GMPYIntegerRing from sympy.utilities import public @public class GMPYFiniteField(FiniteField): """Finite field based on GMPY integers. """ alias = 'FF_gmpy' def __init__(self, mod, symmetric=True): return super(GMPYFiniteField, self).__init__(mod, GMPYIntegerRing(), symmetric) sympy-0.7.4.1/sympy/polys/domains/domain.py0000644000175000017500000003765112253362407021062 0ustar georgeskgeorgesk"""Implementation of :class:`Domain` class. """ from __future__ import print_function, division from sympy.polys.domains.domainelement import DomainElement from sympy.core import Basic, sympify from sympy.core.compatibility import SYMPY_INTS, HAS_GMPY, integer_types, is_sequence from sympy.polys.polyerrors import UnificationFailed, CoercionFailed, DomainError from sympy.polys.orderings import lex from sympy.polys.polyutils import _unify_gens from sympy.utilities import default_sort_key, public @public class Domain(object): """Represents an abstract domain. """ dtype = None zero = None one = None has_Ring = False has_Field = False has_assoc_Ring = False has_assoc_Field = False is_FiniteField = is_FF = False is_IntegerRing = is_ZZ = False is_RationalField = is_QQ = False is_RealField = is_RR = False is_ComplexField = is_CC = False is_AlgebraicField = is_Algebraic = False is_PolynomialRing = is_Poly = False is_FractionField = is_Frac = False is_SymbolicDomain = is_EX = False is_Exact = True is_Numerical = False is_Simple = False is_Composite = False has_CharacteristicZero = False rep = None alias = None def __init__(self): raise NotImplementedError def __str__(self): return self.rep def __repr__(self): return str(self) def __hash__(self): return hash((self.__class__.__name__, self.dtype)) def new(self, *args): return self.dtype(*args) @property def tp(self): return self.dtype def __call__(self, *args): """Construct an element of ``self`` domain from ``args``. """ return self.new(*args) def normal(self, *args): return self.dtype(*args) def convert_from(self, element, base): """Convert ``element`` to ``self.dtype`` given the base domain. """ if base.alias is not None: method = "from_" + base.alias else: method = "from_" + base.__class__.__name__ _convert = getattr(self, method) if _convert is not None: result = _convert(element, base) if result is not None: return result raise CoercionFailed("can't convert %s of type %s from %s to %s" % (element, type(element), base, self)) def convert(self, element, base=None): """Convert ``element`` to ``self.dtype``. """ if base is not None: return self.convert_from(element, base) if self.of_type(element): return element from sympy.polys.domains import PythonIntegerRing, GMPYIntegerRing, GMPYRationalField, RealField, ComplexField if isinstance(element, integer_types): return self.convert_from(element, PythonIntegerRing()) if HAS_GMPY: integers = GMPYIntegerRing() if isinstance(element, integers.tp): return self.convert_from(element, integers) rationals = GMPYRationalField() if isinstance(element, rationals.tp): return self.convert_from(element, rationals) if isinstance(element, float): parent = RealField(tol=False) return self.convert_from(parent(element), parent) if isinstance(element, complex): parent = ComplexField(tol=False) return self.convert_from(parent(element), parent) if isinstance(element, DomainElement): return self.convert_from(element, element.parent()) # TODO: implement this in from_ methods if self.is_Numerical and getattr(element, 'is_ground', False): return self.convert(element.LC()) if isinstance(element, Basic): try: return self.from_sympy(element) except (TypeError, ValueError): pass else: # TODO: remove this branch if not is_sequence(element): try: element = sympify(element) if isinstance(element, Basic): return self.from_sympy(element) except (TypeError, ValueError): pass raise CoercionFailed("can't convert %s of type %s to %s" % (element, type(element), self)) def of_type(self, element): """Check if ``a`` is of type ``dtype``. """ return isinstance(element, self.tp) # XXX: this isn't correct, e.g. PolyElement def __contains__(self, a): """Check if ``a`` belongs to this domain. """ try: self.convert(a) except CoercionFailed: return False return True def to_sympy(self, a): """Convert ``a`` to a SymPy object. """ raise NotImplementedError def from_sympy(self, a): """Convert a SymPy object to ``dtype``. """ raise NotImplementedError def from_FF_python(K1, a, K0): """Convert ``ModularInteger(int)`` to ``dtype``. """ return None def from_ZZ_python(K1, a, K0): """Convert a Python ``int`` object to ``dtype``. """ return None def from_QQ_python(K1, a, K0): """Convert a Python ``Fraction`` object to ``dtype``. """ return None def from_FF_gmpy(K1, a, K0): """Convert ``ModularInteger(mpz)`` to ``dtype``. """ return None def from_ZZ_gmpy(K1, a, K0): """Convert a GMPY ``mpz`` object to ``dtype``. """ return None def from_QQ_gmpy(K1, a, K0): """Convert a GMPY ``mpq`` object to ``dtype``. """ return None def from_RealField(K1, a, K0): """Convert a real element object to ``dtype``. """ return None def from_ComplexField(K1, a, K0): """Convert a complex element to ``dtype``. """ return None def from_AlgebraicField(K1, a, K0): """Convert an algebraic number to ``dtype``. """ return None def from_PolynomialRing(K1, a, K0): """Convert a polynomial to ``dtype``. """ if a.is_ground: return K1.convert(a.LC, K0.dom) def from_FractionField(K1, a, K0): """Convert a rational function to ``dtype``. """ return None def from_ExpressionDomain(K1, a, K0): """Convert a ``EX`` object to ``dtype``. """ return K1.from_sympy(a.ex) def from_GlobalPolynomialRing(K1, a, K0): """Convert a polynomial to ``dtype``. """ if a.degree() <= 0: return K1.convert(a.LC(), K0.dom) def from_GeneralizedPolynomialRing(K1, a, K0): return K1.from_FractionField(a, K0) def unify_with_symbols(K0, K1, symbols): if (K0.is_Composite and (set(K0.symbols) & set(symbols))) or (K1.is_Composite and (set(K1.symbols) & set(symbols))): raise UnificationFailed("can't unify %s with %s, given %s generators" % (K0, K1, tuple(symbols))) return K0.unify(K1) def unify(K0, K1, symbols=None): """ Construct a minimal domain that contains elements of ``K0`` and ``K1``. Known domains (from smallest to largest): - ``GF(p)`` - ``ZZ`` - ``QQ`` - ``RR(prec, tol)`` - ``CC(prec, tol)`` - ``ALG(a, b, c)`` - ``K[x, y, z]`` - ``K(x, y, z)`` - ``EX`` """ if symbols is not None: return K0.unify_with_symbols(K1, symbols) if K0 == K1: return K0 if K0.is_EX: return K0 if K1.is_EX: return K1 if K0.is_Composite or K1.is_Composite: K0_ground = K0.dom if K0.is_Composite else K0 K1_ground = K1.dom if K1.is_Composite else K1 K0_symbols = K0.symbols if K0.is_Composite else () K1_symbols = K1.symbols if K1.is_Composite else () domain = K0_ground.unify(K1_ground) symbols = _unify_gens(K0_symbols, K1_symbols) order = K0.order if K0.is_Composite else K1.order if ((K0.is_FractionField and K1.is_PolynomialRing or K1.is_FractionField and K0.is_PolynomialRing) and (not K0_ground.has_Field or not K1_ground.has_Field) and domain.has_Field): domain = domain.get_ring() if K0.is_Composite and (not K1.is_Composite or K0.is_FractionField or K1.is_PolynomialRing): cls = K0.__class__ else: cls = K1.__class__ return cls(domain, symbols, order) def mkinexact(cls, K0, K1): prec = max(K0.precision, K1.precision) tol = max(K0.tolerance, K1.tolerance) return cls(prec=prec, tol=tol) if K0.is_ComplexField and K1.is_ComplexField: return mkinexact(K0.__class__, K0, K1) if K0.is_ComplexField and K1.is_RealField: return mkinexact(K0.__class__, K0, K1) if K0.is_RealField and K1.is_ComplexField: return mkinexact(K1.__class__, K1, K0) if K0.is_RealField and K1.is_RealField: return mkinexact(K0.__class__, K0, K1) if K0.is_ComplexField or K0.is_RealField: return K0 if K1.is_ComplexField or K1.is_RealField: return K1 if K0.is_AlgebraicField and K1.is_AlgebraicField: return K0.__class__(K0.dom.unify(K1.dom), *_unify_gens(K0.orig_ext, K1.orig_ext)) elif K0.is_AlgebraicField: return K0 elif K1.is_AlgebraicField: return K1 if K0.is_RationalField: return K0 if K1.is_RationalField: return K1 if K0.is_IntegerRing: return K0 if K1.is_IntegerRing: return K1 if K0.is_FiniteField and K1.is_FiniteField: return K0.__class__(max(K0.mod, K1.mod, key=default_sort_key)) from sympy.polys.domains import EX return EX def __eq__(self, other): """Returns ``True`` if two domains are equivalent. """ return isinstance(other, Domain) and self.dtype == other.dtype def __ne__(self, other): """Returns ``False`` if two domains are equivalent. """ return not self.__eq__(other) def map(self, seq): """Rersively apply ``self`` to all elements of ``seq``. """ result = [] for elt in seq: if isinstance(elt, list): result.append(self.map(elt)) else: result.append(self(elt)) return result def get_ring(self): """Returns a ring associated with ``self``. """ raise DomainError('there is no ring associated with %s' % self) def get_field(self): """Returns a field associated with ``self``. """ raise DomainError('there is no field associated with %s' % self) def get_exact(self): """Returns an exact domain associated with ``self``. """ return self def __getitem__(self, symbols): """The mathematical way to make a polynomial ring. """ if hasattr(symbols, '__iter__'): return self.poly_ring(*symbols) else: return self.poly_ring(symbols) def poly_ring(self, *symbols, **kwargs): """Returns a polynomial ring, i.e. `K[X]`. """ from sympy.polys.domains.polynomialring import PolynomialRing return PolynomialRing(self, symbols, kwargs.get("order", lex)) def frac_field(self, *symbols, **kwargs): """Returns a fraction field, i.e. `K(X)`. """ from sympy.polys.domains.fractionfield import FractionField return FractionField(self, symbols, kwargs.get("order", lex)) def old_poly_ring(self, *symbols, **kwargs): """Returns a polynomial ring, i.e. `K[X]`. """ from sympy.polys.domains.old_polynomialring import PolynomialRing return PolynomialRing(self, *symbols, **kwargs) def old_frac_field(self, *symbols, **kwargs): """Returns a fraction field, i.e. `K(X)`. """ from sympy.polys.domains.old_fractionfield import FractionField return FractionField(self, *symbols, **kwargs) def algebraic_field(self, *extension): """Returns an algebraic field, i.e. `K(\\alpha, \dots)`. """ raise DomainError("can't create algebraic field over %s" % self) def inject(self, *symbols): """Inject generators into this domain. """ raise NotImplementedError def is_zero(self, a): """Returns True if ``a`` is zero. """ return not a def is_one(self, a): """Returns True if ``a`` is one. """ return a == self.one def is_positive(self, a): """Returns True if ``a`` is positive. """ return a > 0 def is_negative(self, a): """Returns True if ``a`` is negative. """ return a < 0 def is_nonpositive(self, a): """Returns True if ``a`` is non-positive. """ return a <= 0 def is_nonnegative(self, a): """Returns True if ``a`` is non-negative. """ return a >= 0 def abs(self, a): """Absolute value of ``a``, implies ``__abs__``. """ return abs(a) def neg(self, a): """Returns ``a`` negated, implies ``__neg__``. """ return -a def pos(self, a): """Returns ``a`` positive, implies ``__pos__``. """ return +a def add(self, a, b): """Sum of ``a`` and ``b``, implies ``__add__``. """ return a + b def sub(self, a, b): """Difference of ``a`` and ``b``, implies ``__sub__``. """ return a - b def mul(self, a, b): """Product of ``a`` and ``b``, implies ``__mul__``. """ return a * b def pow(self, a, b): """Raise ``a`` to power ``b``, implies ``__pow__``. """ return a ** b def exquo(self, a, b): """Exact quotient of ``a`` and ``b``, implies something. """ raise NotImplementedError def quo(self, a, b): """Quotient of ``a`` and ``b``, implies something. """ raise NotImplementedError def rem(self, a, b): """Remainder of ``a`` and ``b``, implies ``__mod__``. """ raise NotImplementedError def div(self, a, b): """Division of ``a`` and ``b``, implies something. """ raise NotImplementedError def invert(self, a, b): """Returns inversion of ``a mod b``, implies something. """ raise NotImplementedError def revert(self, a): """Returns ``a**(-1)`` if possible. """ raise NotImplementedError def numer(self, a): """Returns numerator of ``a``. """ raise NotImplementedError def denom(self, a): """Returns denominator of ``a``. """ raise NotImplementedError def half_gcdex(self, a, b): """Half extended GCD of ``a`` and ``b``. """ s, t, h = self.gcdex(a, b) return s, h def gcdex(self, a, b): """Extended GCD of ``a`` and ``b``. """ raise NotImplementedError def cofactors(self, a, b): """Returns GCD and cofactors of ``a`` and ``b``. """ gcd = self.gcd(a, b) cfa = self.quo(a, gcd) cfb = self.quo(b, gcd) return gcd, cfa, cfb def gcd(self, a, b): """Returns GCD of ``a`` and ``b``. """ raise NotImplementedError def lcm(self, a, b): """Returns LCM of ``a`` and ``b``. """ raise NotImplementedError def log(self, a, b): """Returns b-base logarithm of ``a``. """ raise NotImplementedError def sqrt(self, a): """Returns square root of ``a``. """ raise NotImplementedError def evalf(self, a, prec=None, **args): """Returns numerical approximation of ``a``. """ if prec is None: return self.to_sympy(a).evalf(**args) else: return self.to_sympy(a).evalf(prec, **args) n = evalf def real(self, a): return a def imag(self, a): return self.zero def almosteq(self, a, b, tolerance=None): """Check if ``a`` and ``b`` are almost equal. """ return a == b def characteristic(self): """Return the characteristic of this domain. """ raise NotImplementedError('characteristic()') sympy-0.7.4.1/sympy/polys/domains/algebraicfield.py0000644000175000017500000001014212253362407022512 0ustar georgeskgeorgesk"""Implementation of :class:`AlgebraicField` class. """ from __future__ import print_function, division from sympy.polys.domains.field import Field from sympy.polys.domains.simpledomain import SimpleDomain from sympy.polys.domains.characteristiczero import CharacteristicZero from sympy.polys.polyclasses import ANP from sympy.polys.polyerrors import CoercionFailed, DomainError, NotAlgebraic, IsomorphismFailed from sympy.utilities import public @public class AlgebraicField(Field, CharacteristicZero, SimpleDomain): """A class for representing algebraic number fields. """ dtype = ANP is_AlgebraicField = is_Algebraic = True is_Numerical = True has_assoc_Ring = False has_assoc_Field = True def __init__(self, dom, *ext): if not dom.is_QQ: raise DomainError("ground domain must be a rational field") from sympy.polys.numberfields import to_number_field self.orig_ext = ext self.ext = to_number_field(ext) self.mod = self.ext.minpoly.rep self.domain = self.dom = dom self.ngens = 1 self.symbols = self.gens = (self.ext,) self.unit = self([dom(1), dom(0)]) self.zero = self.dtype.zero(self.mod.rep, dom) self.one = self.dtype.one(self.mod.rep, dom) def new(self, element): return self.dtype(element, self.mod.rep, self.dom) def __str__(self): return str(self.dom) + '<' + str(self.ext) + '>' def __hash__(self): return hash((self.__class__.__name__, self.dtype, self.dom, self.ext)) def __eq__(self, other): """Returns ``True`` if two domains are equivalent. """ return isinstance(other, AlgebraicField) and \ self.dtype == other.dtype and self.ext == other.ext def algebraic_field(self, *extension): r"""Returns an algebraic field, i.e. `\mathbb{Q}(\alpha, \dots)`. """ return AlgebraicField(self.dom, *((self.ext,) + extension)) def to_sympy(self, a): """Convert ``a`` to a SymPy object. """ from sympy.polys.numberfields import AlgebraicNumber return AlgebraicNumber(self.ext, a).as_expr() def from_sympy(self, a): """Convert SymPy's expression to ``dtype``. """ try: return self([self.dom.from_sympy(a)]) except CoercionFailed: pass from sympy.polys.numberfields import to_number_field try: return self(to_number_field(a, self.ext).native_coeffs()) except (NotAlgebraic, IsomorphismFailed): raise CoercionFailed( "%s is not a valid algebraic number in %s" % (a, self)) def from_ZZ_python(K1, a, K0): """Convert a Python ``int`` object to ``dtype``. """ return K1(K1.dom.convert(a, K0)) def from_QQ_python(K1, a, K0): """Convert a Python ``Fraction`` object to ``dtype``. """ return K1(K1.dom.convert(a, K0)) def from_ZZ_gmpy(K1, a, K0): """Convert a GMPY ``mpz`` object to ``dtype``. """ return K1(K1.dom.convert(a, K0)) def from_QQ_gmpy(K1, a, K0): """Convert a GMPY ``mpq`` object to ``dtype``. """ return K1(K1.dom.convert(a, K0)) def from_RealField(K1, a, K0): """Convert a mpmath ``mpf`` object to ``dtype``. """ return K1(K1.dom.convert(a, K0)) def get_ring(self): """Returns a ring associated with ``self``. """ raise DomainError('there is no ring associated with %s' % self) def is_positive(self, a): """Returns True if ``a`` is positive. """ return self.dom.is_positive(a.LC()) def is_negative(self, a): """Returns True if ``a`` is negative. """ return self.dom.is_negative(a.LC()) def is_nonpositive(self, a): """Returns True if ``a`` is non-positive. """ return self.dom.is_nonpositive(a.LC()) def is_nonnegative(self, a): """Returns True if ``a`` is non-negative. """ return self.dom.is_nonnegative(a.LC()) def numer(self, a): """Returns numerator of ``a``. """ return a def denom(self, a): """Returns denominator of ``a``. """ return self.one sympy-0.7.4.1/sympy/polys/domains/complexfield.py0000644000175000017500000000666212253362407022264 0ustar georgeskgeorgesk"""Implementation of :class:`ComplexField` class. """ from __future__ import print_function, division from sympy.core.numbers import Float, I from sympy.utilities import public from sympy.polys.domains.field import Field from sympy.polys.domains.simpledomain import SimpleDomain from sympy.polys.domains.characteristiczero import CharacteristicZero from sympy.polys.domains.mpelements import MPContext from sympy.polys.polyerrors import DomainError, CoercionFailed import math @public class ComplexField(Field, CharacteristicZero, SimpleDomain): """Complex numbers up to the given precision. """ rep = 'CC' is_ComplexField = is_CC = True is_Exact = False is_Numerical = True has_assoc_Ring = False has_assoc_Field = True _default_precision = 53 @property def has_default_precision(self): return self.precision == self._default_precision @property def precision(self): return self._context.prec @property def dps(self): return self._context.dps @property def tolerance(self): return self._context.tolerance def __init__(self, prec=_default_precision, dps=None, tol=False): context = MPContext(prec, dps, tol) context._parent = self self._context = context self.dtype = context.mpc self.zero = self.dtype(0) self.one = self.dtype(1) def __eq__(self, other): return (isinstance(other, ComplexField) and self.precision == other.precision and self.tolerance == other.tolerance) def __hash__(self): return hash((self.__class__.__name__, self.dtype, self.precision, self.tolerance)) def to_sympy(self, element): """Convert ``element`` to SymPy number. """ return Float(element.real, self.dps) + I*Float(element.imag, self.dps) def from_sympy(self, expr): """Convert SymPy's number to ``dtype``. """ number = expr.evalf(n=self.dps) real, imag = number.as_real_imag() if real.is_Number and imag.is_Number: return self.dtype(real, imag) else: raise CoercionFailed("expected complex number, got %s" % expr) def from_ZZ_python(self, element, base): return self.dtype(element) def from_QQ_python(self, element, base): return self.dtype(element.numerator) / element.denominator def from_ZZ_gmpy(self, element, base): return self.dtype(int(element)) def from_QQ_gmpy(self, element, base): return self.dtype(int(element.numerator)) / int(element.denominator) def from_RealField(self, element, base): return self.dtype(element) def from_ComplexField(self, element, base): if self == base: return element else: return self.dtype(element) def get_ring(self): """Returns a ring associated with ``self``. """ raise DomainError("there is no ring associated with %s" % self) def get_exact(self): """Returns an exact domain associated with ``self``. """ raise DomainError("there is no exact domain associated with %s" % self) def gcd(self, a, b): """Returns GCD of ``a`` and ``b``. """ return self.one def lcm(self, a, b): """Returns LCM of ``a`` and ``b``. """ return a*b def almosteq(self, a, b, tolerance=None): """Check if ``a`` and ``b`` are almost equal. """ return self._context.almosteq(a, b, tolerance) sympy-0.7.4.1/sympy/polys/domains/pythonfinitefield.py0000644000175000017500000000102312253362407023317 0ustar georgeskgeorgesk"""Implementation of :class:`PythonFiniteField` class. """ from __future__ import print_function, division from sympy.polys.domains.finitefield import FiniteField from sympy.polys.domains.pythonintegerring import PythonIntegerRing from sympy.utilities import public @public class PythonFiniteField(FiniteField): """Finite field based on Python's integers. """ alias = 'FF_python' def __init__(self, mod, symmetric=True): return super(PythonFiniteField, self).__init__(mod, PythonIntegerRing(), symmetric) sympy-0.7.4.1/sympy/polys/domains/gmpyintegerring.py0000644000175000017500000000513212253362407023012 0ustar georgeskgeorgesk"""Implementaton of :class:`GMPYIntegerRing` class. """ from __future__ import print_function, division from sympy.polys.domains.integerring import IntegerRing from sympy.polys.domains.groundtypes import ( GMPYInteger, SymPyInteger, gmpy_factorial, gmpy_gcdex, gmpy_gcd, gmpy_lcm, gmpy_sqrt, ) from sympy.polys.polyerrors import CoercionFailed from sympy.utilities import public @public class GMPYIntegerRing(IntegerRing): """Integer ring based on GMPY's ``mpz`` type. """ dtype = GMPYInteger zero = dtype(0) one = dtype(1) tp = type(one) alias = 'ZZ_gmpy' def __init__(self): """Allow instantiation of this domain. """ def to_sympy(self, a): """Convert ``a`` to a SymPy object. """ return SymPyInteger(int(a)) def from_sympy(self, a): """Convert SymPy's Integer to ``dtype``. """ if a.is_Integer: return GMPYInteger(a.p) elif a.is_Float and int(a) == a: return GMPYInteger(int(a)) else: raise CoercionFailed("expected an integer, got %s" % a) def from_FF_python(K1, a, K0): """Convert ``ModularInteger(int)`` to GMPY's ``mpz``. """ return GMPYInteger(a.to_int()) def from_ZZ_python(K1, a, K0): """Convert Python's ``int`` to GMPY's ``mpz``. """ return GMPYInteger(a) def from_QQ_python(K1, a, K0): """Convert Python's ``Fraction`` to GMPY's ``mpz``. """ if a.denominator == 1: return GMPYInteger(a.numerator) def from_FF_gmpy(K1, a, K0): """Convert ``ModularInteger(mpz)`` to GMPY's ``mpz``. """ return a.to_int() def from_ZZ_gmpy(K1, a, K0): """Convert GMPY's ``mpz`` to GMPY's ``mpz``. """ return a def from_QQ_gmpy(K1, a, K0): """Convert GMPY ``mpq`` to GMPY's ``mpz``. """ if a.denominator == 1: return a.numerator def from_RealField(K1, a, K0): """Convert mpmath's ``mpf`` to GMPY's ``mpz``. """ p, q = K0.to_rational(a) if q == 1: return GMPYInteger(p) def gcdex(self, a, b): """Compute extended GCD of ``a`` and ``b``. """ h, s, t = gmpy_gcdex(a, b) return s, t, h def gcd(self, a, b): """Compute GCD of ``a`` and ``b``. """ return gmpy_gcd(a, b) def lcm(self, a, b): """Compute LCM of ``a`` and ``b``. """ return gmpy_lcm(a, b) def sqrt(self, a): """Compute square root of ``a``. """ return gmpy_sqrt(a) def factorial(self, a): """Compute factorial of ``a``. """ return gmpy_factorial(a) sympy-0.7.4.1/sympy/polys/domains/old_fractionfield.py0000644000175000017500000001372112253362407023252 0ustar georgeskgeorgesk"""Implementation of :class:`FractionField` class. """ from __future__ import print_function, division from sympy.polys.domains.field import Field from sympy.polys.domains.compositedomain import CompositeDomain from sympy.polys.domains.characteristiczero import CharacteristicZero from sympy.polys.polyclasses import DMF from sympy.polys.polyerrors import GeneratorsNeeded from sympy.polys.polyutils import dict_from_basic, basic_from_dict, _dict_reorder from sympy.utilities import public @public class FractionField(Field, CharacteristicZero, CompositeDomain): """A class for representing rational function fields. """ dtype = DMF is_FractionField = is_Frac = True has_assoc_Ring = True has_assoc_Field = True def __init__(self, dom, *gens): if not gens: raise GeneratorsNeeded("generators not specified") lev = len(gens) - 1 self.ngens = len(gens) self.zero = self.dtype.zero(lev, dom, ring=self) self.one = self.dtype.one(lev, dom, ring=self) self.domain = self.dom = dom self.symbols = self.gens = gens def new(self, element): return self.dtype(element, self.dom, len(self.gens) - 1, ring=self) def __str__(self): return str(self.dom) + '(' + ','.join(map(str, self.gens)) + ')' def __hash__(self): return hash((self.__class__.__name__, self.dtype, self.dom, self.gens)) def __eq__(self, other): """Returns ``True`` if two domains are equivalent. """ return isinstance(other, FractionField) and \ self.dtype == other.dtype and self.dom == other.dom and self.gens == other.gens def to_sympy(self, a): """Convert ``a`` to a SymPy object. """ return (basic_from_dict(a.numer().to_sympy_dict(), *self.gens) / basic_from_dict(a.denom().to_sympy_dict(), *self.gens)) def from_sympy(self, a): """Convert SymPy's expression to ``dtype``. """ p, q = a.as_numer_denom() num, _ = dict_from_basic(p, gens=self.gens) den, _ = dict_from_basic(q, gens=self.gens) for k, v in num.items(): num[k] = self.dom.from_sympy(v) for k, v in den.items(): den[k] = self.dom.from_sympy(v) return self((num, den)).cancel() def from_ZZ_python(K1, a, K0): """Convert a Python ``int`` object to ``dtype``. """ return K1(K1.dom.convert(a, K0)) def from_QQ_python(K1, a, K0): """Convert a Python ``Fraction`` object to ``dtype``. """ return K1(K1.dom.convert(a, K0)) def from_ZZ_gmpy(K1, a, K0): """Convert a GMPY ``mpz`` object to ``dtype``. """ return K1(K1.dom.convert(a, K0)) def from_QQ_gmpy(K1, a, K0): """Convert a GMPY ``mpq`` object to ``dtype``. """ return K1(K1.dom.convert(a, K0)) def from_RealField(K1, a, K0): """Convert a mpmath ``mpf`` object to ``dtype``. """ return K1(K1.dom.convert(a, K0)) def from_GlobalPolynomialRing(K1, a, K0): """Convert a ``DMF`` object to ``dtype``. """ if K1.gens == K0.gens: if K1.dom == K0.dom: return K1(a.rep) else: return K1(a.convert(K1.dom).rep) else: monoms, coeffs = _dict_reorder(a.to_dict(), K0.gens, K1.gens) if K1.dom != K0.dom: coeffs = [ K1.dom.convert(c, K0.dom) for c in coeffs ] return K1(dict(zip(monoms, coeffs))) def from_FractionField(K1, a, K0): """ Convert a fraction field element to another fraction field. Examples ======== >>> from sympy.polys.polyclasses import DMF >>> from sympy.polys.domains import ZZ, QQ >>> from sympy.abc import x >>> f = DMF(([ZZ(1), ZZ(2)], [ZZ(1), ZZ(1)]), ZZ) >>> QQx = QQ.old_frac_field(x) >>> ZZx = ZZ.old_frac_field(x) >>> QQx.from_FractionField(f, ZZx) (x + 2)/(x + 1) """ if K1.gens == K0.gens: if K1.dom == K0.dom: return a else: return K1((a.numer().convert(K1.dom).rep, a.denom().convert(K1.dom).rep)) elif set(K0.gens).issubset(K1.gens): nmonoms, ncoeffs = _dict_reorder( a.numer().to_dict(), K0.gens, K1.gens) dmonoms, dcoeffs = _dict_reorder( a.denom().to_dict(), K0.gens, K1.gens) if K1.dom != K0.dom: ncoeffs = [ K1.dom.convert(c, K0.dom) for c in ncoeffs ] dcoeffs = [ K1.dom.convert(c, K0.dom) for c in dcoeffs ] return K1((dict(zip(nmonoms, ncoeffs)), dict(zip(dmonoms, dcoeffs)))) def get_ring(self): """Returns a ring associated with ``self``. """ from sympy.polys.domains import PolynomialRing return PolynomialRing(self.dom, *self.gens) def poly_ring(self, *gens): """Returns a polynomial ring, i.e. `K[X]`. """ raise NotImplementedError('nested domains not allowed') def frac_field(self, *gens): """Returns a fraction field, i.e. `K(X)`. """ raise NotImplementedError('nested domains not allowed') def is_positive(self, a): """Returns True if ``a`` is positive. """ return self.dom.is_positive(a.numer().LC()) def is_negative(self, a): """Returns True if ``a`` is negative. """ return self.dom.is_negative(a.numer().LC()) def is_nonpositive(self, a): """Returns True if ``a`` is non-positive. """ return self.dom.is_nonpositive(a.numer().LC()) def is_nonnegative(self, a): """Returns True if ``a`` is non-negative. """ return self.dom.is_nonnegative(a.numer().LC()) def numer(self, a): """Returns numerator of ``a``. """ return a.numer() def denom(self, a): """Returns denominator of ``a``. """ return a.denom() def factorial(self, a): """Returns factorial of ``a``. """ return self.dtype(self.dom.factorial(a)) sympy-0.7.4.1/sympy/polys/domains/pythonrationalfield.py0000644000175000017500000000427212253362407023663 0ustar georgeskgeorgesk"""Implementation of :class:`PythonRationalField` class. """ from __future__ import print_function, division from sympy.polys.domains.rationalfield import RationalField from sympy.polys.domains.groundtypes import PythonInteger, PythonRational, SymPyRational from sympy.polys.polyerrors import CoercionFailed from sympy.utilities import public @public class PythonRationalField(RationalField): """Rational field based on Python rational number type. """ dtype = PythonRational zero = dtype(0) one = dtype(1) alias = 'QQ_python' def __init__(self): pass def get_ring(self): """Returns ring associated with ``self``. """ from sympy.polys.domains import PythonIntegerRing return PythonIntegerRing() def to_sympy(self, a): """Convert `a` to a SymPy object. """ return SymPyRational(a.numerator, a.denominator) def from_sympy(self, a): """Convert SymPy's Rational to `dtype`. """ if a.is_Rational: return PythonRational(a.p, a.q) elif a.is_Float: from sympy.polys.domains import RR p, q = RR.to_rational(a) return PythonRational(int(p), int(q)) else: raise CoercionFailed("expected `Rational` object, got %s" % a) def from_ZZ_python(K1, a, K0): """Convert a Python `int` object to `dtype`. """ return PythonRational(a) def from_QQ_python(K1, a, K0): """Convert a Python `Fraction` object to `dtype`. """ return a def from_ZZ_gmpy(K1, a, K0): """Convert a GMPY `mpz` object to `dtype`. """ return PythonRational(PythonInteger(a)) def from_QQ_gmpy(K1, a, K0): """Convert a GMPY `mpq` object to `dtype`. """ return PythonRational(PythonInteger(a.numer()), PythonInteger(a.denom())) def from_RealField(K1, a, K0): """Convert a mpmath `mpf` object to `dtype`. """ p, q = K0.to_rational(a) return PythonRational(int(p), int(q)) def numer(self, a): """Returns numerator of `a`. """ return a.numerator def denom(self, a): """Returns denominator of `a`. """ return a.denominator sympy-0.7.4.1/sympy/polys/domains/ring.py0000644000175000017500000000633012253362407020540 0ustar georgeskgeorgesk"""Implementation of :class:`Ring` class. """ from __future__ import print_function, division from sympy.polys.domains.domain import Domain from sympy.polys.polyerrors import ExactQuotientFailed, NotInvertible, NotReversible from sympy.utilities import public @public class Ring(Domain): """Represents a ring domain. """ has_Ring = True def get_ring(self): """Returns a ring associated with ``self``. """ return self def exquo(self, a, b): """Exact quotient of ``a`` and ``b``, implies ``__floordiv__``. """ if a % b: raise ExactQuotientFailed(a, b, self) else: return a // b def quo(self, a, b): """Quotient of ``a`` and ``b``, implies ``__floordiv__``. """ return a // b def rem(self, a, b): """Remainder of ``a`` and ``b``, implies ``__mod__``. """ return a % b def div(self, a, b): """Division of ``a`` and ``b``, implies ``__divmod__``. """ return divmod(a, b) def invert(self, a, b): """Returns inversion of ``a mod b``. """ s, t, h = self.gcdex(a, b) if self.is_one(h): return s % b else: raise NotInvertible("zero divisor") def revert(self, a): """Returns ``a**(-1)`` if possible. """ if self.is_one(a): return a else: raise NotReversible('only unity is reversible in a ring') def is_unit(self, a): try: self.revert(a) return True except NotReversible: return False def numer(self, a): """Returns numerator of ``a``. """ return a def denom(self, a): """Returns denominator of `a`. """ return self.one def free_module(self, rank): """ Generate a free module of rank ``rank`` over self. >>> from sympy.abc import x >>> from sympy import QQ >>> QQ.old_poly_ring(x).free_module(2) QQ[x]**2 """ raise NotImplementedError def ideal(self, *gens): """ Generate an ideal of ``self``. >>> from sympy.abc import x >>> from sympy import QQ >>> QQ.old_poly_ring(x).ideal(x**2) """ from sympy.polys.agca.ideals import ModuleImplementedIdeal return ModuleImplementedIdeal(self, self.free_module(1).submodule( *[[x] for x in gens])) def quotient_ring(self, e): """ Form a quotient ring of ``self``. Here ``e`` can be an ideal or an iterable. >>> from sympy.abc import x >>> from sympy import QQ >>> QQ.old_poly_ring(x).quotient_ring(QQ.old_poly_ring(x).ideal(x**2)) QQ[x]/ >>> QQ.old_poly_ring(x).quotient_ring([x**2]) QQ[x]/ The division operator has been overloaded for this: >>> QQ.old_poly_ring(x)/[x**2] QQ[x]/ """ from sympy.polys.agca.ideals import Ideal from sympy.polys.domains.quotientring import QuotientRing if not isinstance(e, Ideal): e = self.ideal(*e) return QuotientRing(self, e) def __div__(self, e): return self.quotient_ring(e) __truediv__ = __div__ sympy-0.7.4.1/sympy/polys/domains/old_polynomialring.py0000644000175000017500000003303112253362407023500 0ustar georgeskgeorgesk"""Implementation of :class:`PolynomialRing` class. """ from __future__ import print_function, division from sympy.polys.domains.ring import Ring from sympy.polys.domains.compositedomain import CompositeDomain from sympy.polys.domains.characteristiczero import CharacteristicZero from sympy.polys.domains.old_fractionfield import FractionField from sympy.polys.polyclasses import DMP, DMF from sympy.polys.polyerrors import (GeneratorsNeeded, PolynomialError, CoercionFailed, ExactQuotientFailed, NotReversible) from sympy.polys.polyutils import dict_from_basic, basic_from_dict, _dict_reorder from sympy.polys.orderings import monomial_key, build_product_order from sympy.polys.agca.modules import FreeModulePolyRing from sympy.core.compatibility import iterable from sympy.utilities import public # XXX why does this derive from CharacteristicZero??? @public class PolynomialRingBase(Ring, CharacteristicZero, CompositeDomain): """ Base class for generalized polynomial rings. This base class should be used for uniform access to generalized polynomial rings. Subclasses only supply information about the element storage etc. Do not instantiate. """ has_assoc_Ring = True has_assoc_Field = True default_order = "grevlex" def __init__(self, dom, *gens, **opts): if not gens: raise GeneratorsNeeded("generators not specified") lev = len(gens) - 1 self.ngens = len(gens) self.zero = self.dtype.zero(lev, dom, ring=self) self.one = self.dtype.one(lev, dom, ring=self) self.domain = self.dom = dom self.symbols = self.gens = gens # NOTE 'order' may not be set if inject was called through CompositeDomain self.order = opts.get('order', monomial_key(self.default_order)) def new(self, element): return self.dtype(element, self.dom, len(self.gens) - 1, ring=self) def __str__(self): s_order = str(self.order) orderstr = ( " order=" + s_order) if s_order != self.default_order else "" return str(self.dom) + '[' + ','.join(map(str, self.gens)) + orderstr + ']' def __hash__(self): return hash((self.__class__.__name__, self.dtype, self.dom, self.gens, self.order)) def __eq__(self, other): """Returns `True` if two domains are equivalent. """ return isinstance(other, PolynomialRingBase) and \ self.dtype == other.dtype and self.dom == other.dom and \ self.gens == other.gens and self.order == other.order def from_ZZ_python(K1, a, K0): """Convert a Python `int` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_QQ_python(K1, a, K0): """Convert a Python `Fraction` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_ZZ_gmpy(K1, a, K0): """Convert a GMPY `mpz` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_QQ_gmpy(K1, a, K0): """Convert a GMPY `mpq` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_RealField(K1, a, K0): """Convert a mpmath `mpf` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_AlgebraicField(K1, a, K0): """Convert a `ANP` object to `dtype`. """ if K1.dom == K0: return K1(a) def from_GlobalPolynomialRing(K1, a, K0): """Convert a `DMP` object to `dtype`. """ if K1.gens == K0.gens: if K1.dom == K0.dom: return K1(a.rep) # set the correct ring else: return K1(a.convert(K1.dom).rep) else: monoms, coeffs = _dict_reorder(a.to_dict(), K0.gens, K1.gens) if K1.dom != K0.dom: coeffs = [ K1.dom.convert(c, K0.dom) for c in coeffs ] return K1(dict(zip(monoms, coeffs))) def get_field(self): """Returns a field associated with `self`. """ return FractionField(self.dom, *self.gens) def poly_ring(self, *gens): """Returns a polynomial ring, i.e. `K[X]`. """ raise NotImplementedError('nested domains not allowed') def frac_field(self, *gens): """Returns a fraction field, i.e. `K(X)`. """ raise NotImplementedError('nested domains not allowed') def revert(self, a): try: return 1/a except (ExactQuotientFailed, ZeroDivisionError): raise NotReversible('%s is not a unit' % a) def gcdex(self, a, b): """Extended GCD of `a` and `b`. """ return a.gcdex(b) def gcd(self, a, b): """Returns GCD of `a` and `b`. """ return a.gcd(b) def lcm(self, a, b): """Returns LCM of `a` and `b`. """ return a.lcm(b) def factorial(self, a): """Returns factorial of `a`. """ return self.dtype(self.dom.factorial(a)) def _vector_to_sdm(self, v, order): """ For internal use by the modules class. Convert an iterable of elements of this ring into a sparse distributed module element. """ raise NotImplementedError def _sdm_to_dics(self, s, n): """Helper for _sdm_to_vector.""" from sympy.polys.distributedmodules import sdm_to_dict dic = sdm_to_dict(s) res = [{} for _ in range(n)] for k, v in dic.items(): res[k[0]][k[1:]] = v return res def _sdm_to_vector(self, s, n): """ For internal use by the modules class. Convert a sparse distributed module into a list of length ``n``. >>> from sympy import QQ, ilex >>> from sympy.abc import x, y >>> R = QQ.old_poly_ring(x, y, order=ilex) >>> L = [((1, 1, 1), QQ(1)), ((0, 1, 0), QQ(1)), ((0, 0, 1), QQ(2))] >>> R._sdm_to_vector(L, 2) [x + 2*y, x*y] """ dics = self._sdm_to_dics(s, n) # NOTE this works for global and local rings! return [self(x) for x in dics] def free_module(self, rank): """ Generate a free module of rank ``rank`` over ``self``. >>> from sympy.abc import x >>> from sympy import QQ >>> QQ.old_poly_ring(x).free_module(2) QQ[x]**2 """ return FreeModulePolyRing(self, rank) def _vector_to_sdm_helper(v, order): """Helper method for common code in Global and Local poly rings.""" from sympy.polys.distributedmodules import sdm_from_dict d = {} for i, e in enumerate(v): for key, value in e.to_dict().items(): d[(i,) + key] = value return sdm_from_dict(d, order) @public class GlobalPolynomialRing(PolynomialRingBase): """A true polynomial ring, with objects DMP. """ is_PolynomialRing = is_Poly = True dtype = DMP def from_FractionField(K1, a, K0): """ Convert a ``DMF`` object to ``DMP``. Examples ======== >>> from sympy.polys.polyclasses import DMP, DMF >>> from sympy.polys.domains import ZZ >>> from sympy.abc import x >>> f = DMF(([ZZ(1), ZZ(1)], [ZZ(1)]), ZZ) >>> K = ZZ.old_frac_field(x) >>> F = ZZ.old_poly_ring(x).from_FractionField(f, K) >>> F == DMP([ZZ(1), ZZ(1)], ZZ) True >>> type(F) """ if a.denom().is_one: return K1.from_GlobalPolynomialRing(a.numer(), K0) def to_sympy(self, a): """Convert `a` to a SymPy object. """ return basic_from_dict(a.to_sympy_dict(), *self.gens) def from_sympy(self, a): """Convert SymPy's expression to `dtype`. """ try: rep, _ = dict_from_basic(a, gens=self.gens) except PolynomialError: raise CoercionFailed("can't convert %s to type %s" % (a, self)) for k, v in rep.items(): rep[k] = self.dom.from_sympy(v) return self(rep) def is_positive(self, a): """Returns True if `LC(a)` is positive. """ return self.dom.is_positive(a.LC()) def is_negative(self, a): """Returns True if `LC(a)` is negative. """ return self.dom.is_negative(a.LC()) def is_nonpositive(self, a): """Returns True if `LC(a)` is non-positive. """ return self.dom.is_nonpositive(a.LC()) def is_nonnegative(self, a): """Returns True if `LC(a)` is non-negative. """ return self.dom.is_nonnegative(a.LC()) def _vector_to_sdm(self, v, order): """ >>> from sympy import lex, QQ >>> from sympy.abc import x, y >>> R = QQ.old_poly_ring(x, y) >>> f = R.convert(x + 2*y) >>> g = R.convert(x * y) >>> R._vector_to_sdm([f, g], lex) [((1, 1, 1), 1), ((0, 1, 0), 1), ((0, 0, 1), 2)] """ return _vector_to_sdm_helper(v, order) class GeneralizedPolynomialRing(PolynomialRingBase): """A generalized polynomial ring, with objects DMF. """ dtype = DMF def new(self, a): """Construct an element of `self` domain from `a`. """ res = self.dtype(a, self.dom, len(self.gens) - 1, ring=self) # make sure res is actually in our ring if res.denom().terms(order=self.order)[0][0] != (0,)*len(self.gens): from sympy.printing.str import sstr raise CoercionFailed("denominator %s not allowed in %s" % (sstr(res), self)) return res def __contains__(self, a): try: a = self.convert(a) except CoercionFailed: return False return a.denom().terms(order=self.order)[0][0] == (0,)*len(self.gens) def from_FractionField(K1, a, K0): dmf = K1.get_field().from_FractionField(a, K0) return K1((dmf.num, dmf.den)) def to_sympy(self, a): """Convert `a` to a SymPy object. """ return (basic_from_dict(a.numer().to_sympy_dict(), *self.gens) / basic_from_dict(a.denom().to_sympy_dict(), *self.gens)) def from_sympy(self, a): """Convert SymPy's expression to `dtype`. """ p, q = a.as_numer_denom() num, _ = dict_from_basic(p, gens=self.gens) den, _ = dict_from_basic(q, gens=self.gens) for k, v in num.items(): num[k] = self.dom.from_sympy(v) for k, v in den.items(): den[k] = self.dom.from_sympy(v) return self((num, den)).cancel() def _vector_to_sdm(self, v, order): """ Turn an iterable into a sparse distributed module. Note that the vector is multiplied by a unit first to make all entries polynomials. >>> from sympy import ilex, QQ >>> from sympy.abc import x, y >>> R = QQ.old_poly_ring(x, y, order=ilex) >>> f = R.convert((x + 2*y) / (1 + x)) >>> g = R.convert(x * y) >>> R._vector_to_sdm([f, g], ilex) [((0, 0, 1), 2), ((0, 1, 0), 1), ((1, 1, 1), 1), ((1, 2, 1), 1)] """ # NOTE this is quite inefficient... u = self.one.numer() for x in v: u *= x.denom() return _vector_to_sdm_helper([x.numer()*u/x.denom() for x in v], order) @public def PolynomialRing(dom, *gens, **opts): r""" Create a generalized multivariate polynomial ring. A generalized polynomial ring is defined by a ground field `K`, a set of generators (typically `x_1, \dots, x_n`) and a monomial order `<`. The monomial order can be global, local or mixed. In any case it induces a total ordering on the monomials, and there exists for every (non-zero) polynomial `f \in K[x_1, \dots, x_n]` a well-defined "leading monomial" `LM(f) = LM(f, >)`. One can then define a multiplicative subset `S = S_> = \{f \in K[x_1, \dots, x_n] | LM(f) = 1\}`. The generalized polynomial ring corresponding to the monomial order is `R = S^{-1}K[x_1, \dots, x_n]`. If `>` is a so-called global order, that is `1` is the smallest monomial, then we just have `S = K` and `R = K[x_1, \dots, x_n]`. Examples ======== A few examples may make this clearer. >>> from sympy.abc import x, y >>> from sympy import QQ Our first ring uses global lexicographic order. >>> R1 = QQ.old_poly_ring(x, y, order=(("lex", x, y),)) The second ring uses local lexicographic order. Note that when using a single (non-product) order, you can just specify the name and omit the variables: >>> R2 = QQ.old_poly_ring(x, y, order="ilex") The third and fourth rings use a mixed orders: >>> o1 = (("ilex", x), ("lex", y)) >>> o2 = (("lex", x), ("ilex", y)) >>> R3 = QQ.old_poly_ring(x, y, order=o1) >>> R4 = QQ.old_poly_ring(x, y, order=o2) We will investigate what elements of `K(x, y)` are contained in the various rings. >>> L = [x, 1/x, y/(1 + x), 1/(1 + y), 1/(1 + x*y)] >>> test = lambda R: [f in R for f in L] The first ring is just `K[x, y]`: >>> test(R1) [True, False, False, False, False] The second ring is R1 localised at the maximal ideal (x, y): >>> test(R2) [True, False, True, True, True] The third ring is R1 localised at the prime ideal (x): >>> test(R3) [True, False, True, False, True] Finally the fourth ring is R1 localised at `S = K[x, y] \setminus yK[y]`: >>> test(R4) [True, False, False, True, False] """ order = opts.get("order", GeneralizedPolynomialRing.default_order) if iterable(order): order = build_product_order(order, gens) order = monomial_key(order) opts['order'] = order if order.is_global: return GlobalPolynomialRing(dom, *gens, **opts) else: return GeneralizedPolynomialRing(dom, *gens, **opts) sympy-0.7.4.1/sympy/polys/domains/finitefield.py0000644000175000017500000000635212253362407022067 0ustar georgeskgeorgesk"""Implementation of :class:`FiniteField` class. """ from __future__ import print_function, division from sympy.polys.domains.field import Field from sympy.polys.domains.simpledomain import SimpleDomain from sympy.polys.domains.groundtypes import SymPyInteger from sympy.polys.domains.modularinteger import ModularIntegerFactory from sympy.polys.polyerrors import CoercionFailed from sympy.utilities import public @public class FiniteField(Field, SimpleDomain): """General class for finite fields. """ rep = 'FF' is_FiniteField = is_FF = True is_Numerical = True has_assoc_Ring = False has_assoc_Field = True dom = None mod = None def __init__(self, mod, dom=None, symmetric=True): if mod <= 0: raise ValueError('modulus must be a positive integer, got %s' % mod) if dom is None: from sympy.polys.domains import ZZ dom = ZZ self.dtype = ModularIntegerFactory(mod, dom, symmetric, self) self.zero = self.dtype(0) self.one = self.dtype(1) self.dom = dom self.mod = mod def __str__(self): return 'GF(%s)' % self.mod def __hash__(self): return hash((self.__class__.__name__, self.dtype, self.mod, self.dom)) def __eq__(self, other): """Returns ``True`` if two domains are equivalent. """ return isinstance(other, FiniteField) and \ self.mod == other.mod and self.dom == other.dom def characteristic(self): """Return the characteristic of this domain. """ return self.mod def get_field(self): """Returns a field associated with ``self``. """ return self def to_sympy(self, a): """Convert ``a`` to a SymPy object. """ return SymPyInteger(int(a)) def from_sympy(self, a): """Convert SymPy's Integer to SymPy's ``Integer``. """ if a.is_Integer: return self.dtype(self.dom.dtype(int(a))) elif a.is_Float and int(a) == a: return self.dtype(self.dom.dtype(int(a))) else: raise CoercionFailed("expected an integer, got %s" % a) def from_FF_python(K1, a, K0=None): """Convert ``ModularInteger(int)`` to ``dtype``. """ return K1.dtype(K1.dom.from_ZZ_python(a.val, K0.dom)) def from_ZZ_python(K1, a, K0=None): """Convert Python's ``int`` to ``dtype``. """ return K1.dtype(K1.dom.from_ZZ_python(a, K0)) def from_QQ_python(K1, a, K0=None): """Convert Python's ``Fraction`` to ``dtype``. """ if a.denominator == 1: return K1.from_ZZ_python(a.numerator) def from_FF_gmpy(K1, a, K0=None): """Convert ``ModularInteger(mpz)`` to ``dtype``. """ return K1.dtype(K1.dom.from_ZZ_gmpy(a.val, K0.dom)) def from_ZZ_gmpy(K1, a, K0=None): """Convert GMPY's ``mpz`` to ``dtype``. """ return K1.dtype(K1.dom.from_ZZ_gmpy(a, K0)) def from_QQ_gmpy(K1, a, K0=None): """Convert GMPY's ``mpq`` to ``dtype``. """ if a.denominator == 1: return K1.from_ZZ_gmpy(a.numerator) def from_RealField(K1, a, K0): """Convert mpmath's ``mpf`` to ``dtype``. """ p, q = K0.to_rational(a) if q == 1: return K1.dtype(self.dom.dtype(p)) sympy-0.7.4.1/sympy/polys/domains/mpelements.py0000644000175000017500000001231012253362407021745 0ustar georgeskgeorgesk"""Real and complex elements with built-in truncation. """ from __future__ import print_function, division from sympy.polys.domains.domainelement import DomainElement from sympy.mpmath.ctx_mp_python import PythonMPContext, _mpf, _mpc, _constant from sympy.mpmath.libmp import (MPZ_ONE, fzero, fone, finf, fninf, fnan, round_nearest, mpf_mul, mpf_abs, mpf_lt, mpc_abs, repr_dps, int_types, from_int, from_float, from_str, to_rational) from sympy.mpmath.rational import mpq from sympy.utilities import public @public class RealElement(_mpf, DomainElement): """An element of a real domain. """ __slots__ = ['__mpf__'] def _set_mpf(self, val): prec, rounding = self.context._prec_rounding tol = self.context.tol if mpf_lt(mpf_abs(val, prec, rounding), tol): self.__mpf__ = fzero else: self.__mpf__ = val _mpf_ = property(lambda self: self.__mpf__, _set_mpf) def parent(self): return self.context._parent @public class ComplexElement(_mpc, DomainElement): """An element of a complex domain. """ __slots__ = ['__mpc__'] def _set_mpc(self, val): prec, rounding = self.context._prec_rounding tol = self.context.tol # norm = mpc_abs(val, prec, rounding) # tol = mpf_max(tol, mpf_mul(norm, tol)) re, im = val if mpf_lt(mpf_abs(re, prec, rounding), tol): re = fzero if mpf_lt(mpf_abs(im, prec, rounding), tol): im = fzero self.__mpc__ = (re, im) _mpc_ = property(lambda self: self.__mpc__, _set_mpc) def parent(self): return self.context._parent new = object.__new__ @public class MPContext(PythonMPContext): def __init__(ctx, prec=53, dps=None, tol=None): ctx._prec_rounding = [prec, round_nearest] if dps is None: ctx._set_prec(prec) else: ctx._set_dps(dps) ctx.mpf = type('RealElement', (RealElement,), {}) ctx.mpc = type('ComplexElement', (ComplexElement,), {}) ctx.mpf._ctxdata = [ctx.mpf, new, ctx._prec_rounding] ctx.mpc._ctxdata = [ctx.mpc, new, ctx._prec_rounding] ctx.mpf.context = ctx ctx.mpc.context = ctx ctx.constant = type('constant', (_constant,), {}) ctx.constant._ctxdata = [ctx.mpf, new, ctx._prec_rounding] ctx.constant.context = ctx ctx.types = [ctx.mpf, ctx.mpc, ctx.constant] ctx.trap_complex = True ctx.pretty = True if tol is None: ctx.tol = ctx._make_tol() elif tol is False: ctx.tol = fzero else: ctx.tol = ctx._convert_tol(tol) ctx.tolerance = ctx.make_mpf(ctx.tol) if not ctx.tolerance: ctx.max_denom = 1000000 else: ctx.max_denom = int(1/ctx.tolerance) ctx.zero = ctx.make_mpf(fzero) ctx.one = ctx.make_mpf(fone) ctx.j = ctx.make_mpc((fzero, fone)) ctx.inf = ctx.make_mpf(finf) ctx.ninf = ctx.make_mpf(fninf) ctx.nan = ctx.make_mpf(fnan) def _make_tol(ctx): hundred = (0, 25, 2, 5) eps = (0, MPZ_ONE, 1-ctx.prec, 1) return mpf_mul(hundred, eps) def make_tol(ctx): return ctx.make_mpf(ctx._make_tol()) def _convert_tol(ctx, tol): if isinstance(tol, int_types): return from_int(tol) if isinstance(tol, float): return from_float(tol) if hasattr(tol, "_mpf_"): return tol._mpf_ prec, rounding = ctx._prec_rounding if isinstance(tol, basestring): return from_str(tol, prec, rounding) raise ValueError("expected a real number, got %s" % tol) def _convert_fallback(ctx, x, strings): raise TypeError("cannot create mpf from " + repr(x)) @property def _repr_digits(ctx): return repr_dps(ctx._prec) @property def _str_digits(ctx): return ctx._dps def to_rational(ctx, s, limit=True): p, q = to_rational(s._mpf_) if not limit or q <= ctx.max_denom: return p, q p0, q0, p1, q1 = 0, 1, 1, 0 n, d = p, q while True: a = n//d q2 = q0 + a*q1 if q2 > ctx.max_denom: break p0, q0, p1, q1 = p1, q1, p0 + a*p1, q2 n, d = d, n - a*d k = (ctx.max_denom - q0)//q1 number = mpq(p, q) bound1 = mpq(p0 + k*p1, q0 + k*q1) bound2 = mpq(p1, q1) if not bound2 or not bound1: return p, q elif abs(bound2 - number) <= abs(bound1 - number): return bound2._mpq_ else: return bound1._mpq_ def almosteq(ctx, s, t, rel_eps=None, abs_eps=None): t = ctx.convert(t) if abs_eps is None and rel_eps is None: rel_eps = abs_eps = ctx.tolerance or ctx.make_tol() if abs_eps is None: abs_eps = ctx.convert(rel_eps) elif rel_eps is None: rel_eps = ctx.convert(abs_eps) diff = abs(s-t) if diff <= abs_eps: return True abss = abs(s) abst = abs(t) if abss < abst: err = diff/abst else: err = diff/abss return err <= rel_eps sympy-0.7.4.1/sympy/polys/domains/modularinteger.py0000644000175000017500000001173712253362407022631 0ustar georgeskgeorgesk"""Implementation of :class:`ModularInteger` class. """ from __future__ import print_function, division import operator from sympy.polys.polyutils import PicklableWithSlots from sympy.polys.polyerrors import CoercionFailed from sympy.polys.domains.domainelement import DomainElement from sympy.utilities import public @public class ModularInteger(PicklableWithSlots, DomainElement): """A class representing a modular integer. """ mod, dom, sym, _parent = None, None, None, None __slots__ = ['val'] def parent(self): return self._parent def __init__(self, val): if isinstance(val, self.__class__): self.val = val.val % self.mod else: self.val = self.dom.convert(val) % self.mod def __hash__(self): return hash((self.val, self.mod)) def __repr__(self): return "%s(%s)" % (self.__class__.__name__, self.val) def __str__(self): return "%s mod %s" % (self.val, self.mod) def __int__(self): return int(self.to_int()) def to_int(self): if self.sym: if self.val <= self.mod // 2: return self.val else: return self.val - self.mod else: return self.val def __pos__(self): return self def __neg__(self): return self.__class__(-self.val) @classmethod def _get_val(cls, other): if isinstance(other, cls): return other.val else: try: return cls.dom.convert(other) except CoercionFailed: return None def __add__(self, other): val = self._get_val(other) if val is not None: return self.__class__(self.val + val) else: return NotImplemented def __radd__(self, other): return self.__add__(other) def __sub__(self, other): val = self._get_val(other) if val is not None: return self.__class__(self.val - val) else: return NotImplemented def __rsub__(self, other): return (-self).__add__(other) def __mul__(self, other): val = self._get_val(other) if val is not None: return self.__class__(self.val * val) else: return NotImplemented def __rmul__(self, other): return self.__mul__(other) def __div__(self, other): val = self._get_val(other) if val is not None: return self.__class__(self.val * self._invert(val)) else: return NotImplemented def __rdiv__(self, other): return self.invert().__mul__(other) __truediv__ = __div__ __rtruediv__ = __rdiv__ def __mod__(self, other): val = self._get_val(other) if val is not None: return self.__class__(self.val % val) else: return NotImplemented def __rmod__(self, other): val = self._get_val(other) if val is not None: return self.__class__(val % self.val) else: return NotImplemented def __pow__(self, exp): if not exp: return self.__class__(self.dom.one) if exp < 0: val, exp = self.invert(), -exp else: val = self.val return self.__class__(val**exp) def _compare(self, other, op): val = self._get_val(other) if val is not None: return op(self.val, val % self.mod) else: return NotImplemented def __eq__(self, other): return self._compare(other, operator.eq) def __ne__(self, other): return self._compare(other, operator.ne) def __lt__(self, other): return self._compare(other, operator.lt) def __le__(self, other): return self._compare(other, operator.le) def __gt__(self, other): return self._compare(other, operator.gt) def __ge__(self, other): return self._compare(other, operator.ge) def __nonzero__(self): return bool(self.val) __bool__ = __nonzero__ @classmethod def _invert(cls, value): return cls.dom.invert(value, cls.mod) def invert(self): return self.__class__(self._invert(self.val)) _modular_integer_cache = {} def ModularIntegerFactory(_mod, _dom, _sym, parent): """Create custom class for specific integer modulus.""" try: _mod = _dom.convert(_mod) except CoercionFailed: ok = False else: ok = True if not ok or _mod < 1: raise ValueError("modulus must be a positive integer, got %s" % _mod) key = _mod, _dom, _sym try: cls = _modular_integer_cache[key] except KeyError: class cls(ModularInteger): mod, dom, sym = _mod, _dom, _sym _parent = parent if _sym: cls.__name__ = "SymmetricModularIntegerMod%s" % _mod else: cls.__name__ = "ModularIntegerMod%s" % _mod _modular_integer_cache[key] = cls return cls sympy-0.7.4.1/sympy/polys/domains/fractionfield.py0000644000175000017500000001031212253362407022405 0ustar georgeskgeorgesk"""Implementation of :class:`FractionField` class. """ from __future__ import print_function, division from sympy.polys.domains.field import Field from sympy.polys.domains.compositedomain import CompositeDomain from sympy.polys.polyerrors import CoercionFailed, GeneratorsError from sympy.utilities import public @public class FractionField(Field, CompositeDomain): """A class for representing multivariate rational function fields. """ is_FractionField = is_Frac = True has_assoc_Ring = True has_assoc_Field = True def __init__(self, domain_or_field, symbols=None, order=None): from sympy.polys.fields import FracField if isinstance(domain_or_field, FracField) and symbols is None and order is None: field = domain_or_field else: field = FracField(symbols, domain_or_field, order) self.field = field self.dtype = field.dtype self.gens = field.gens self.ngens = field.ngens self.symbols = field.symbols self.domain = field.domain # TODO: remove this self.dom = self.domain def new(self, element): return self.field.field_new(element) @property def zero(self): return self.field.zero @property def one(self): return self.field.one @property def order(self): return self.field.order def __str__(self): return str(self.domain) + '(' + ','.join(map(str, self.symbols)) + ')' def __hash__(self): return hash((self.__class__.__name__, self.dtype, self.domain, self.symbols)) def __eq__(self, other): """Returns `True` if two domains are equivalent. """ return isinstance(other, FractionField) and \ self.dtype == other.dtype and self.field == other.field def to_sympy(self, a): """Convert `a` to a SymPy object. """ return a.as_expr() def from_sympy(self, a): """Convert SymPy's expression to `dtype`. """ return self.field.from_expr(a) def from_ZZ_python(K1, a, K0): """Convert a Python `int` object to `dtype`. """ return K1(K1.domain.convert(a, K0)) def from_QQ_python(K1, a, K0): """Convert a Python `Fraction` object to `dtype`. """ return K1(K1.domain.convert(a, K0)) def from_ZZ_gmpy(K1, a, K0): """Convert a GMPY `mpz` object to `dtype`. """ return K1(K1.domain.convert(a, K0)) def from_QQ_gmpy(K1, a, K0): """Convert a GMPY `mpq` object to `dtype`. """ return K1(K1.domain.convert(a, K0)) def from_RealField(K1, a, K0): """Convert a mpmath `mpf` object to `dtype`. """ return K1(K1.domain.convert(a, K0)) def from_AlgebraicField(K1, a, K0): """Convert an algebraic number to ``dtype``. """ if K1.domain == K0: return K1.new(a) def from_PolynomialRing(K1, a, K0): """Convert a polynomial to ``dtype``. """ try: return K1.new(a) except (CoercionFailed, GeneratorsError): return None def from_FractionField(K1, a, K0): """Convert a rational function to ``dtype``. """ try: return a.set_field(K1.field) except (CoercionFailed, GeneratorsError): return None def get_ring(self): """Returns a field associated with `self`. """ return self.field.to_ring().to_domain() def is_positive(self, a): """Returns True if `LC(a)` is positive. """ return self.domain.is_positive(a.numer.LC) def is_negative(self, a): """Returns True if `LC(a)` is negative. """ return self.domain.is_negative(a.numer.LC) def is_nonpositive(self, a): """Returns True if `LC(a)` is non-positive. """ return self.domain.is_nonpositive(a.numer.LC) def is_nonnegative(self, a): """Returns True if `LC(a)` is non-negative. """ return self.domain.is_nonnegative(a.numer.LC) def numer(self, a): """Returns numerator of ``a``. """ return a.numer def denom(self, a): """Returns denominator of ``a``. """ return a.denom def factorial(self, a): """Returns factorial of `a`. """ return self.dtype(self.domain.factorial(a)) sympy-0.7.4.1/sympy/polys/domains/polynomialring.py0000644000175000017500000001047112253362407022645 0ustar georgeskgeorgesk"""Implementation of :class:`PolynomialRing` class. """ from __future__ import print_function, division from sympy.polys.domains.ring import Ring from sympy.polys.domains.compositedomain import CompositeDomain from sympy.polys.polyerrors import CoercionFailed, GeneratorsError from sympy.utilities import public @public class PolynomialRing(Ring, CompositeDomain): """A class for representing multivariate polynomial rings. """ is_PolynomialRing = is_Poly = True has_assoc_Ring = True has_assoc_Field = True def __init__(self, domain_or_ring, symbols=None, order=None): from sympy.polys.rings import PolyRing if isinstance(domain_or_ring, PolyRing) and symbols is None and order is None: ring = domain_or_ring else: ring = PolyRing(symbols, domain_or_ring, order) self.ring = ring self.dtype = ring.dtype self.gens = ring.gens self.ngens = ring.ngens self.symbols = ring.symbols self.domain = ring.domain # TODO: remove this self.dom = self.domain def new(self, element): return self.ring.ring_new(element) @property def zero(self): return self.ring.zero @property def one(self): return self.ring.one @property def order(self): return self.ring.order def __str__(self): return str(self.domain) + '[' + ','.join(map(str, self.symbols)) + ']' def __hash__(self): return hash((self.__class__.__name__, self.dtype, self.domain, self.symbols)) def __eq__(self, other): """Returns `True` if two domains are equivalent. """ return isinstance(other, PolynomialRing) and \ self.dtype == other.dtype and self.ring == other.ring def to_sympy(self, a): """Convert `a` to a SymPy object. """ return a.as_expr() def from_sympy(self, a): """Convert SymPy's expression to `dtype`. """ return self.ring.from_expr(a) def from_ZZ_python(K1, a, K0): """Convert a Python `int` object to `dtype`. """ return K1(K1.domain.convert(a, K0)) def from_QQ_python(K1, a, K0): """Convert a Python `Fraction` object to `dtype`. """ return K1(K1.domain.convert(a, K0)) def from_ZZ_gmpy(K1, a, K0): """Convert a GMPY `mpz` object to `dtype`. """ return K1(K1.domain.convert(a, K0)) def from_QQ_gmpy(K1, a, K0): """Convert a GMPY `mpq` object to `dtype`. """ return K1(K1.domain.convert(a, K0)) def from_RealField(K1, a, K0): """Convert a mpmath `mpf` object to `dtype`. """ return K1(K1.domain.convert(a, K0)) def from_AlgebraicField(K1, a, K0): """Convert an algebraic number to ``dtype``. """ if K1.domain == K0: return K1.new(a) def from_PolynomialRing(K1, a, K0): """Convert a polynomial to ``dtype``. """ try: return a.set_ring(K1.ring) except (CoercionFailed, GeneratorsError): return None def from_FractionField(K1, a, K0): """Convert a rational function to ``dtype``. """ denom = K0.denom(a) if denom.is_ground: return K1.from_PolynomialRing(K0.numer(a)/denom, K0.field.ring.to_domain()) else: return None def get_field(self): """Returns a field associated with `self`. """ return self.ring.to_field().to_domain() def is_positive(self, a): """Returns True if `LC(a)` is positive. """ return self.domain.is_positive(a.LC) def is_negative(self, a): """Returns True if `LC(a)` is negative. """ return self.domain.is_negative(a.LC) def is_nonpositive(self, a): """Returns True if `LC(a)` is non-positive. """ return self.domain.is_nonpositive(a.LC) def is_nonnegative(self, a): """Returns True if `LC(a)` is non-negative. """ return self.domain.is_nonnegative(a.LC) def gcdex(self, a, b): """Extended GCD of `a` and `b`. """ return a.gcdex(b) def gcd(self, a, b): """Returns GCD of `a` and `b`. """ return a.gcd(b) def lcm(self, a, b): """Returns LCM of `a` and `b`. """ return a.lcm(b) def factorial(self, a): """Returns factorial of `a`. """ return self.dtype(self.domain.factorial(a)) sympy-0.7.4.1/sympy/polys/domains/characteristiczero.py0000644000175000017500000000065512253362407023475 0ustar georgeskgeorgesk"""Implementaton of :class:`CharacteristicZero` class. """ from __future__ import print_function, division from sympy.polys.domains.domain import Domain from sympy.utilities import public @public class CharacteristicZero(Domain): """Domain that has infinite number of elements. """ has_CharacteristicZero = True def characteristic(self): """Return the characteristic of this domain. """ return 0 sympy-0.7.4.1/sympy/polys/domains/pythonrational.py0000644000175000017500000001330412253362407022653 0ustar georgeskgeorgesk"""Rational number type based on Python integers. """ from __future__ import print_function, division import operator from sympy.polys.domains.domainelement import DomainElement from sympy.polys.polyutils import PicklableWithSlots from sympy.polys.domains.domainelement import DomainElement from sympy.core.compatibility import integer_types from sympy.printing.defaults import DefaultPrinting from sympy.utilities import public @public class PythonRational(DefaultPrinting, PicklableWithSlots, DomainElement): """ Rational number type based on Python integers. This was supposed to be needed for compatibility with older Python versions which don't support Fraction. However, Fraction is very slow so we don't use it anyway. Examples ======== >>> from sympy.polys.domains import PythonRational >>> PythonRational(1) 1 >>> PythonRational(2, 3) 2/3 >>> PythonRational(14, 10) 7/5 """ __slots__ = ['p', 'q'] def parent(self): from sympy.polys.domains import PythonRationalField return PythonRationalField() def __init__(self, p, q=1): if not q: raise ZeroDivisionError('rational number') elif q < 0: p, q = -p, -q if not p: self.p = 0 self.q = 1 elif p == 1 or q == 1: self.p = p self.q = q else: x, y = p, q while y: x, y = y, x % y if x != 1: p //= x q //= x self.p = p self.q = q @classmethod def new(cls, p, q): obj = object.__new__(cls) obj.p = p obj.q = q return obj def __hash__(self): if self.q == 1: return hash(self.p) else: return hash((self.p, self.q)) def __int__(self): p, q = self.p, self.q if p < 0: return -(-p//q) return p//q def __float__(self): return float(self.p)/self.q def __abs__(self): return self.new(abs(self.p), self.q) def __pos__(self): return self.new(+self.p, self.q) def __neg__(self): return self.new(-self.p, self.q) def __add__(self, other): if isinstance(other, PythonRational): p = self.p*other.q + self.q*other.p q = self.q*other.q elif isinstance(other, integer_types): p = self.p + self.q*other q = self.q else: return NotImplemented return self.__class__(p, q) def __radd__(self, other): if not isinstance(other, integer_types): return NotImplemented p = self.p + self.q*other q = self.q return self.__class__(p, q) def __sub__(self, other): if isinstance(other, PythonRational): p = self.p*other.q - self.q*other.p q = self.q*other.q elif isinstance(other, integer_types): p = self.p - self.q*other q = self.q else: return NotImplemented return self.__class__(p, q) def __rsub__(self, other): if not isinstance(other, integer_types): return NotImplemented p = self.q*other - self.p q = self.q return self.__class__(p, q) def __mul__(self, other): if isinstance(other, PythonRational): p = self.p*other.p q = self.q*other.q elif isinstance(other, integer_types): p = self.p*other q = self.q else: return NotImplemented return self.__class__(p, q) def __rmul__(self, other): if not isinstance(other, integer_types): return NotImplemented p = self.p*other q = self.q return self.__class__(p, q) def __div__(self, other): if isinstance(other, PythonRational): p = self.p*other.q q = self.q*other.p elif isinstance(other, integer_types): p = self.p q = self.q*other else: return NotImplemented return self.__class__(p, q) __truediv__ = __div__ def __rdiv__(self, other): if not isinstance(other, integer_types): return NotImplemented p = self.q*other q = self.p return self.__class__(p, q) __rtruediv__ = __rdiv__ def __mod__(self, other): return self.__class__(0) def __divmod__(self, other): return (self//other, self % other) def __pow__(self, exp): p, q = self.p, self.q if exp < 0: p, q, exp = q, p, -exp return self.new(p**exp, q**exp) def __nonzero__(self): return self.p != 0 __bool__ = __nonzero__ def __eq__(self, other): if isinstance(other, PythonRational): return self.q == other.q and self.p == other.p elif isinstance(other, integer_types): return self.q == 1 and self.p == other else: return False def __ne__(self, other): return not self.__eq__(other) def _cmp(self, other, op): try: diff = self - other except TypeError: return NotImplemented else: return op(diff.p, 0) def __lt__(self, other): return self._cmp(other, operator.lt) def __le__(self, other): return self._cmp(other, operator.le) def __gt__(self, other): return self._cmp(other, operator.gt) def __ge__(self, other): return self._cmp(other, operator.ge) @property def numer(self): return self.p @property def denom(self): return self.q numerator = numer denominator = denom sympy-0.7.4.1/sympy/polys/domains/compositedomain.py0000644000175000017500000000141612253362407022773 0ustar georgeskgeorgesk"""Implementation of :class:`CompositeDomain` class. """ from __future__ import print_function, division from sympy.polys.domains.domain import Domain from sympy.polys.polyerrors import GeneratorsError from sympy.utilities import public from sympy.utilities.magic import pollute @public class CompositeDomain(Domain): """Base class for composite domains, e.g. ZZ[x], ZZ(X). """ is_Composite = True gens, ngens, symbols, domain = [None]*4 def inject(self, *symbols): """Inject generators into this domain. """ if not (set(self.symbols) & set(symbols)): return self.__class__(self.domain, self.symbols + symbols, self.order) else: raise GeneratorsError("common generators in %s and %s" % (self.symbols, symbols)) sympy-0.7.4.1/sympy/polys/domains/expressiondomain.py0000644000175000017500000001361112253362407023170 0ustar georgeskgeorgesk"""Implementation of :class:`ExpressionDomain` class. """ from __future__ import print_function, division from sympy.polys.domains.field import Field from sympy.polys.domains.simpledomain import SimpleDomain from sympy.polys.domains.characteristiczero import CharacteristicZero from sympy.core import sympify, SympifyError from sympy.utilities import public from sympy.polys.polyutils import PicklableWithSlots from sympy.polys.polyerrors import DomainError @public class ExpressionDomain(Field, CharacteristicZero, SimpleDomain): """A class for arbitrary expressions. """ is_SymbolicDomain = is_EX = True class Expression(PicklableWithSlots): """An arbitrary expression. """ __slots__ = ['ex'] def __init__(self, ex): if not isinstance(ex, self.__class__): self.ex = sympify(ex) else: self.ex = ex.ex def __repr__(f): return 'EX(%s)' % repr(f.ex) def __str__(f): return 'EX(%s)' % str(f.ex) def __hash__(self): return hash((self.__class__.__name__, self.ex)) def as_expr(f): return f.ex def numer(f): return f.__class__(f.ex.as_numer_denom()[0]) def denom(f): return f.__class__(f.ex.as_numer_denom()[1]) def simplify(f, ex): return f.__class__(ex.cancel()) def __abs__(f): return f.__class__(abs(f.ex)) def __neg__(f): return f.__class__(-f.ex) def _to_ex(f, g): try: return f.__class__(g) except SympifyError: return None def __add__(f, g): g = f._to_ex(g) if g is not None: return f.simplify(f.ex + g.ex) else: return NotImplemented def __radd__(f, g): return f.simplify(f.__class__(g).ex + f.ex) def __sub__(f, g): g = f._to_ex(g) if g is not None: return f.simplify(f.ex - g.ex) else: return NotImplemented def __rsub__(f, g): return f.simplify(f.__class__(g).ex - f.ex) def __mul__(f, g): g = f._to_ex(g) if g is not None: return f.simplify(f.ex*g.ex) else: return NotImplemented def __rmul__(f, g): return f.simplify(f.__class__(g).ex*f.ex) def __pow__(f, n): n = f._to_ex(n) if n is not None: return f.simplify(f.ex**n.ex) else: return NotImplemented def __truediv__(f, g): g = f._to_ex(g) if g is not None: return f.simplify(f.ex/g.ex) else: return NotImplemented def __rtruediv__(f, g): return f.simplify(f.__class__(g).ex/f.ex) __div__ = __truediv__ __rdiv__ = __rtruediv__ def __eq__(f, g): return f.ex == f.__class__(g).ex def __ne__(f, g): return not f.__eq__(g) def __nonzero__(f): return f.ex != 0 __bool__ = __nonzero__ def gcd(f, g): from sympy.polys import gcd return f.__class__(gcd(f.ex, f.__class__(g).ex)) def lcm(f, g): from sympy.polys import lcm return f.__class__(lcm(f.ex, f.__class__(g).ex)) dtype = Expression zero = Expression(0) one = Expression(1) rep = 'EX' has_assoc_Ring = False has_assoc_Field = True def __init__(self): pass def to_sympy(self, a): """Convert ``a`` to a SymPy object. """ return a.as_expr() def from_sympy(self, a): """Convert SymPy's expression to ``dtype``. """ return self.dtype(a) def from_ZZ_python(K1, a, K0): """Convert a Python ``int`` object to ``dtype``. """ return K1(K0.to_sympy(a)) def from_QQ_python(K1, a, K0): """Convert a Python ``Fraction`` object to ``dtype``. """ return K1(K0.to_sympy(a)) def from_ZZ_gmpy(K1, a, K0): """Convert a GMPY ``mpz`` object to ``dtype``. """ return K1(K0.to_sympy(a)) def from_QQ_gmpy(K1, a, K0): """Convert a GMPY ``mpq`` object to ``dtype``. """ return K1(K0.to_sympy(a)) def from_RealField(K1, a, K0): """Convert a mpmath ``mpf`` object to ``dtype``. """ return K1(K0.to_sympy(a)) def from_PolynomialRing(K1, a, K0): """Convert a ``DMP`` object to ``dtype``. """ return K1(K0.to_sympy(a)) def from_FractionField(K1, a, K0): """Convert a ``DMF`` object to ``dtype``. """ return K1(K0.to_sympy(a)) def from_ExpressionDomain(K1, a, K0): """Convert a ``EX`` object to ``dtype``. """ return a def get_ring(self): """Returns a ring associated with ``self``. """ return self # XXX: EX is not a ring but we don't have much choice here. def get_field(self): """Returns a field associated with ``self``. """ return self def is_positive(self, a): """Returns True if ``a`` is positive. """ return a.ex.as_coeff_mul()[0].is_positive def is_negative(self, a): """Returns True if ``a`` is negative. """ return a.ex.as_coeff_mul()[0].is_negative def is_nonpositive(self, a): """Returns True if ``a`` is non-positive. """ return a.ex.as_coeff_mul()[0].is_nonpositive def is_nonnegative(self, a): """Returns True if ``a`` is non-negative. """ return a.ex.as_coeff_mul()[0].is_nonnegative def numer(self, a): """Returns numerator of ``a``. """ return a.numer() def denom(self, a): """Returns denominator of ``a``. """ return a.denom() def gcd(self, a, b): return a.gcd(b) def lcm(self, a, b): return a.lcm(b) sympy-0.7.4.1/sympy/polys/domains/gmpyrationalfield.py0000644000175000017500000000537512253362407023323 0ustar georgeskgeorgesk"""Implementaton of :class:`GMPYRationalField` class. """ from __future__ import print_function, division from sympy.polys.domains.rationalfield import RationalField from sympy.polys.domains.groundtypes import ( GMPYRational, SymPyRational, gmpy_numer, gmpy_denom, gmpy_factorial, gmpy_qdiv, ) from sympy.polys.polyerrors import CoercionFailed from sympy.utilities import public @public class GMPYRationalField(RationalField): """Rational field based on GMPY mpq class. """ dtype = GMPYRational zero = dtype(0) one = dtype(1) tp = type(one) alias = 'QQ_gmpy' def __init__(self): pass def get_ring(self): """Returns ring associated with ``self``. """ from sympy.polys.domains import GMPYIntegerRing return GMPYIntegerRing() def to_sympy(self, a): """Convert `a` to a SymPy object. """ return SymPyRational(int(gmpy_numer(a)), int(gmpy_denom(a))) def from_sympy(self, a): """Convert SymPy's Integer to `dtype`. """ if a.is_Rational: return GMPYRational(a.p, a.q) elif a.is_Float: from sympy.polys.domains import RR return GMPYRational(*RR.to_rational(a)) else: raise CoercionFailed("expected `Rational` object, got %s" % a) def from_ZZ_python(K1, a, K0): """Convert a Python `int` object to `dtype`. """ return GMPYRational(a) def from_QQ_python(K1, a, K0): """Convert a Python `Fraction` object to `dtype`. """ return GMPYRational(a.numerator, a.denominator) def from_ZZ_gmpy(K1, a, K0): """Convert a GMPY `mpz` object to `dtype`. """ return GMPYRational(a) def from_QQ_gmpy(K1, a, K0): """Convert a GMPY `mpq` object to `dtype`. """ return a def from_RealField(K1, a, K0): """Convert a mpmath `mpf` object to `dtype`. """ return GMPYRational(*K0.to_rational(a)) def exquo(self, a, b): """Exact quotient of `a` and `b`, implies `__div__`. """ return GMPYRational(gmpy_qdiv(a, b)) def quo(self, a, b): """Quotient of `a` and `b`, implies `__div__`. """ return GMPYRational(gmpy_qdiv(a, b)) def rem(self, a, b): """Remainder of `a` and `b`, implies nothing. """ return self.zero def div(self, a, b): """Division of `a` and `b`, implies `__div__`. """ return GMPYRational(gmpy_qdiv(a, b)), self.zero def numer(self, a): """Returns numerator of `a`. """ return a.numerator def denom(self, a): """Returns denominator of `a`. """ return a.denominator def factorial(self, a): """Returns factorial of `a`. """ return GMPYRational(gmpy_factorial(int(a))) sympy-0.7.4.1/sympy/polys/domains/field.py0000644000175000017500000000465212253362407020671 0ustar georgeskgeorgesk"""Implementation of :class:`Field` class. """ from __future__ import print_function, division from sympy.polys.domains.ring import Ring from sympy.polys.polyerrors import NotReversible, DomainError from sympy.utilities import public @public class Field(Ring): """Represents a field domain. """ has_Field = True def get_ring(self): """Returns a ring associated with ``self``. """ raise DomainError('there is no ring associated with %s' % self) def get_field(self): """Returns a field associated with ``self``. """ return self def exquo(self, a, b): """Exact quotient of ``a`` and ``b``, implies ``__div__``. """ return a / b def quo(self, a, b): """Quotient of ``a`` and ``b``, implies ``__div__``. """ return a / b def rem(self, a, b): """Remainder of ``a`` and ``b``, implies nothing. """ return self.zero def div(self, a, b): """Division of ``a`` and ``b``, implies ``__div__``. """ return a / b, self.zero def gcd(self, a, b): """ Returns GCD of ``a`` and ``b``. This definition of GCD over fields allows to clear denominators in `primitive()`. >>> from sympy.polys.domains import QQ >>> from sympy import S, gcd, primitive >>> from sympy.abc import x >>> QQ.gcd(QQ(2, 3), QQ(4, 9)) 2/9 >>> gcd(S(2)/3, S(4)/9) 2/9 >>> primitive(2*x/3 + S(4)/9) (2/9, 3*x + 2) """ try: ring = self.get_ring() except DomainError: return self.one p = ring.gcd(self.numer(a), self.numer(b)) q = ring.lcm(self.denom(a), self.denom(b)) return self.convert(p, ring)/q def lcm(self, a, b): """ Returns LCM of ``a`` and ``b``. >>> from sympy.polys.domains import QQ >>> from sympy import S, lcm >>> QQ.lcm(QQ(2, 3), QQ(4, 9)) 4/3 >>> lcm(S(2)/3, S(4)/9) 4/3 """ try: ring = self.get_ring() except DomainError: return a*b p = ring.lcm(self.numer(a), self.numer(b)) q = ring.gcd(self.denom(a), self.denom(b)) return self.convert(p, ring)/q def revert(self, a): """Returns ``a**(-1)`` if possible. """ if a: return 1/a else: raise NotReversible('zero is not reversible') sympy-0.7.4.1/sympy/polys/domains/simpledomain.py0000644000175000017500000000064112253362407022261 0ustar georgeskgeorgesk"""Implementation of :class:`SimpleDomain` class. """ from __future__ import print_function, division from sympy.polys.domains.domain import Domain from sympy.utilities import public @public class SimpleDomain(Domain): """Base class for simple domains, e.g. ZZ, QQ. """ is_Simple = True def inject(self, *gens): """Inject generators into this domain. """ return self.poly_ring(*gens) sympy-0.7.4.1/sympy/polys/domains/realfield.py0000644000175000017500000000675412253362407021542 0ustar georgeskgeorgesk"""Implementation of :class:`RealField` class. """ from __future__ import print_function, division import math from sympy.polys.domains.field import Field from sympy.polys.domains.simpledomain import SimpleDomain from sympy.polys.domains.characteristiczero import CharacteristicZero from sympy.polys.domains.mpelements import MPContext from sympy.polys.polyerrors import DomainError, CoercionFailed from sympy.core.numbers import Float from sympy.utilities import public @public class RealField(Field, CharacteristicZero, SimpleDomain): """Real numbers up to the given precision. """ rep = 'RR' is_RealField = is_RR = True is_Exact = False is_Numerical = True has_assoc_Ring = False has_assoc_Field = True _default_precision = 53 @property def has_default_precision(self): return self.precision == self._default_precision @property def precision(self): return self._context.prec @property def dps(self): return self._context.dps @property def tolerance(self): return self._context.tolerance def __init__(self, prec=_default_precision, dps=None, tol=False): context = MPContext(prec, dps, tol) context._parent = self self._context = context self.dtype = context.mpf self.zero = self.dtype(0) self.one = self.dtype(1) def __eq__(self, other): return (isinstance(other, RealField) and self.precision == other.precision and self.tolerance == other.tolerance) def __hash__(self): return hash((self.__class__.__name__, self.dtype, self.precision, self.tolerance)) def to_sympy(self, element): """Convert ``element`` to SymPy number. """ return Float(element, self.dps) def from_sympy(self, expr): """Convert SymPy's number to ``dtype``. """ number = expr.evalf(n=self.dps) if number.is_Number: return self.dtype(number) else: raise CoercionFailed("expected real number, got %s" % expr) def from_ZZ_python(self, element, base): return self.dtype(element) def from_QQ_python(self, element, base): return self.dtype(element.numerator) / element.denominator def from_ZZ_gmpy(self, element, base): return self.dtype(int(element)) def from_QQ_gmpy(self, element, base): return self.dtype(int(element.numerator)) / int(element.denominator) def from_RealField(self, element, base): if self == base: return element else: return self.dtype(element) def from_ComplexField(self, element, base): if not element.imag: return self.dtype(element.real) def to_rational(self, element, limit=True): """Convert a real number to rational number. """ return self._context.to_rational(element, limit) def get_ring(self): """Returns a ring associated with ``self``. """ raise DomainError('there is no ring associated with %s' % self) def get_exact(self): """Returns an exact domain associated with ``self``. """ from sympy.polys.domains import QQ return QQ def gcd(self, a, b): """Returns GCD of ``a`` and ``b``. """ return self.one def lcm(self, a, b): """Returns LCM of ``a`` and ``b``. """ return a*b def almosteq(self, a, b, tolerance=None): """Check if ``a`` and ``b`` are almost equal. """ return self._context.almosteq(a, b, tolerance) sympy-0.7.4.1/sympy/polys/domains/integerring.py0000644000175000017500000000223112253362407022112 0ustar georgeskgeorgesk"""Implementation of :class:`IntegerRing` class. """ from __future__ import print_function, division from sympy.polys.domains.ring import Ring from sympy.polys.domains.simpledomain import SimpleDomain from sympy.polys.domains.characteristiczero import CharacteristicZero from sympy.utilities import public import math @public class IntegerRing(Ring, CharacteristicZero, SimpleDomain): """General class for integer rings. """ rep = 'ZZ' is_IntegerRing = is_ZZ = True is_Numerical = True has_assoc_Ring = True has_assoc_Field = True def get_field(self): """Returns a field associated with ``self``. """ from sympy.polys.domains import QQ return QQ def algebraic_field(self, *extension): r"""Returns an algebraic field, i.e. `\mathbb{Q}(\alpha, \dots)`. """ return self.get_field().algebraic_field(*extension) def from_AlgebraicField(K1, a, K0): """Convert a ``ANP`` object to ``dtype``. """ if a.is_ground: return K1.convert(a.LC(), K0.dom) def log(self, a, b): """Returns b-base logarithm of ``a``. """ return self.dtype(math.log(int(a), b)) sympy-0.7.4.1/sympy/polys/domains/tests/0000755000175000017500000000000012253362407020367 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/polys/domains/tests/test_domains.py0000644000175000017500000006331712253362407023444 0ustar georgeskgeorgesk"""Tests for classes defining properties of ground domains, e.g. ZZ, QQ, ZZ[x] ... """ from sympy import S, sqrt, sin, oo, nan, Poly, Integer, Rational from sympy.abc import x, y, z from sympy.polys.domains import (ZZ, QQ, RR, CC, FF, GF, PolynomialRing, FractionField, EX) from sympy.polys.rings import ring from sympy.polys.fields import field from sympy.polys.domains.modularinteger import ModularIntegerFactory from sympy.polys.polyerrors import ( UnificationFailed, GeneratorsNeeded, GeneratorsError, CoercionFailed, NotInvertible, DomainError) from sympy.utilities.pytest import raises, XFAIL ALG = QQ.algebraic_field(sqrt(2), sqrt(3)) def unify(K0, K1): return K0.unify(K1) def test_Domain_unify(): F3 = GF(3) assert unify(F3, F3) == F3 assert unify(F3, ZZ) == ZZ assert unify(F3, QQ) == QQ assert unify(F3, ALG) == ALG assert unify(F3, RR) == RR assert unify(F3, CC) == CC assert unify(F3, ZZ[x]) == ZZ[x] assert unify(F3, ZZ.frac_field(x)) == ZZ.frac_field(x) assert unify(F3, EX) == EX assert unify(ZZ, F3) == ZZ assert unify(ZZ, ZZ) == ZZ assert unify(ZZ, QQ) == QQ assert unify(ZZ, ALG) == ALG assert unify(ZZ, RR) == RR assert unify(ZZ, CC) == CC assert unify(ZZ, ZZ[x]) == ZZ[x] assert unify(ZZ, ZZ.frac_field(x)) == ZZ.frac_field(x) assert unify(ZZ, EX) == EX assert unify(QQ, F3) == QQ assert unify(QQ, ZZ) == QQ assert unify(QQ, QQ) == QQ assert unify(QQ, ALG) == ALG assert unify(QQ, RR) == RR assert unify(QQ, CC) == CC assert unify(QQ, ZZ[x]) == QQ[x] assert unify(QQ, ZZ.frac_field(x)) == QQ.frac_field(x) assert unify(QQ, EX) == EX assert unify(RR, F3) == RR assert unify(RR, ZZ) == RR assert unify(RR, QQ) == RR assert unify(RR, ALG) == RR assert unify(RR, RR) == RR assert unify(RR, CC) == CC assert unify(RR, ZZ[x]) == RR[x] assert unify(RR, ZZ.frac_field(x)) == RR.frac_field(x) assert unify(RR, EX) == EX assert unify(CC, F3) == CC assert unify(CC, ZZ) == CC assert unify(CC, QQ) == CC assert unify(CC, ALG) == CC assert unify(CC, RR) == CC assert unify(CC, CC) == CC assert unify(CC, ZZ[x]) == CC[x] assert unify(CC, ZZ.frac_field(x)) == CC.frac_field(x) assert unify(CC, EX) == EX assert unify(ZZ[x], F3) == ZZ[x] assert unify(ZZ[x], ZZ) == ZZ[x] assert unify(ZZ[x], QQ) == QQ[x] assert unify(ZZ[x], ALG) == ALG[x] assert unify(ZZ[x], RR) == RR[x] assert unify(ZZ[x], CC) == CC[x] assert unify(ZZ[x], ZZ[x]) == ZZ[x] assert unify(ZZ[x], ZZ.frac_field(x)) == ZZ.frac_field(x) assert unify(ZZ[x], EX) == EX assert unify(ZZ.frac_field(x), F3) == ZZ.frac_field(x) assert unify(ZZ.frac_field(x), ZZ) == ZZ.frac_field(x) assert unify(ZZ.frac_field(x), QQ) == QQ.frac_field(x) assert unify(ZZ.frac_field(x), ALG) == ALG.frac_field(x) assert unify(ZZ.frac_field(x), RR) == RR.frac_field(x) assert unify(ZZ.frac_field(x), CC) == CC.frac_field(x) assert unify(ZZ.frac_field(x), ZZ[x]) == ZZ.frac_field(x) assert unify(ZZ.frac_field(x), ZZ.frac_field(x)) == ZZ.frac_field(x) assert unify(ZZ.frac_field(x), EX) == EX assert unify(EX, F3) == EX assert unify(EX, ZZ) == EX assert unify(EX, QQ) == EX assert unify(EX, ALG) == EX assert unify(EX, RR) == EX assert unify(EX, CC) == EX assert unify(EX, ZZ[x]) == EX assert unify(EX, ZZ.frac_field(x)) == EX assert unify(EX, EX) == EX def test_Domain_unify_composite(): assert unify(ZZ.poly_ring(x), ZZ) == ZZ.poly_ring(x) assert unify(ZZ.poly_ring(x), QQ) == QQ.poly_ring(x) assert unify(QQ.poly_ring(x), ZZ) == QQ.poly_ring(x) assert unify(QQ.poly_ring(x), QQ) == QQ.poly_ring(x) assert unify(ZZ, ZZ.poly_ring(x)) == ZZ.poly_ring(x) assert unify(QQ, ZZ.poly_ring(x)) == QQ.poly_ring(x) assert unify(ZZ, QQ.poly_ring(x)) == QQ.poly_ring(x) assert unify(QQ, QQ.poly_ring(x)) == QQ.poly_ring(x) assert unify(ZZ.poly_ring(x, y), ZZ) == ZZ.poly_ring(x, y) assert unify(ZZ.poly_ring(x, y), QQ) == QQ.poly_ring(x, y) assert unify(QQ.poly_ring(x, y), ZZ) == QQ.poly_ring(x, y) assert unify(QQ.poly_ring(x, y), QQ) == QQ.poly_ring(x, y) assert unify(ZZ, ZZ.poly_ring(x, y)) == ZZ.poly_ring(x, y) assert unify(QQ, ZZ.poly_ring(x, y)) == QQ.poly_ring(x, y) assert unify(ZZ, QQ.poly_ring(x, y)) == QQ.poly_ring(x, y) assert unify(QQ, QQ.poly_ring(x, y)) == QQ.poly_ring(x, y) assert unify(ZZ.frac_field(x), ZZ) == ZZ.frac_field(x) assert unify(ZZ.frac_field(x), QQ) == QQ.frac_field(x) assert unify(QQ.frac_field(x), ZZ) == QQ.frac_field(x) assert unify(QQ.frac_field(x), QQ) == QQ.frac_field(x) assert unify(ZZ, ZZ.frac_field(x)) == ZZ.frac_field(x) assert unify(QQ, ZZ.frac_field(x)) == QQ.frac_field(x) assert unify(ZZ, QQ.frac_field(x)) == QQ.frac_field(x) assert unify(QQ, QQ.frac_field(x)) == QQ.frac_field(x) assert unify(ZZ.frac_field(x, y), ZZ) == ZZ.frac_field(x, y) assert unify(ZZ.frac_field(x, y), QQ) == QQ.frac_field(x, y) assert unify(QQ.frac_field(x, y), ZZ) == QQ.frac_field(x, y) assert unify(QQ.frac_field(x, y), QQ) == QQ.frac_field(x, y) assert unify(ZZ, ZZ.frac_field(x, y)) == ZZ.frac_field(x, y) assert unify(QQ, ZZ.frac_field(x, y)) == QQ.frac_field(x, y) assert unify(ZZ, QQ.frac_field(x, y)) == QQ.frac_field(x, y) assert unify(QQ, QQ.frac_field(x, y)) == QQ.frac_field(x, y) assert unify(ZZ.poly_ring(x), ZZ.poly_ring(x)) == ZZ.poly_ring(x) assert unify(ZZ.poly_ring(x), QQ.poly_ring(x)) == QQ.poly_ring(x) assert unify(QQ.poly_ring(x), ZZ.poly_ring(x)) == QQ.poly_ring(x) assert unify(QQ.poly_ring(x), QQ.poly_ring(x)) == QQ.poly_ring(x) assert unify(ZZ.poly_ring(x, y), ZZ.poly_ring(x)) == ZZ.poly_ring(x, y) assert unify(ZZ.poly_ring(x, y), QQ.poly_ring(x)) == QQ.poly_ring(x, y) assert unify(QQ.poly_ring(x, y), ZZ.poly_ring(x)) == QQ.poly_ring(x, y) assert unify(QQ.poly_ring(x, y), QQ.poly_ring(x)) == QQ.poly_ring(x, y) assert unify(ZZ.poly_ring(x), ZZ.poly_ring(x, y)) == ZZ.poly_ring(x, y) assert unify(ZZ.poly_ring(x), QQ.poly_ring(x, y)) == QQ.poly_ring(x, y) assert unify(QQ.poly_ring(x), ZZ.poly_ring(x, y)) == QQ.poly_ring(x, y) assert unify(QQ.poly_ring(x), QQ.poly_ring(x, y)) == QQ.poly_ring(x, y) assert unify(ZZ.poly_ring(x, y), ZZ.poly_ring(x, z)) == ZZ.poly_ring(x, y, z) assert unify(ZZ.poly_ring(x, y), QQ.poly_ring(x, z)) == QQ.poly_ring(x, y, z) assert unify(QQ.poly_ring(x, y), ZZ.poly_ring(x, z)) == QQ.poly_ring(x, y, z) assert unify(QQ.poly_ring(x, y), QQ.poly_ring(x, z)) == QQ.poly_ring(x, y, z) assert unify(ZZ.frac_field(x), ZZ.frac_field(x)) == ZZ.frac_field(x) assert unify(ZZ.frac_field(x), QQ.frac_field(x)) == QQ.frac_field(x) assert unify(QQ.frac_field(x), ZZ.frac_field(x)) == QQ.frac_field(x) assert unify(QQ.frac_field(x), QQ.frac_field(x)) == QQ.frac_field(x) assert unify(ZZ.frac_field(x, y), ZZ.frac_field(x)) == ZZ.frac_field(x, y) assert unify(ZZ.frac_field(x, y), QQ.frac_field(x)) == QQ.frac_field(x, y) assert unify(QQ.frac_field(x, y), ZZ.frac_field(x)) == QQ.frac_field(x, y) assert unify(QQ.frac_field(x, y), QQ.frac_field(x)) == QQ.frac_field(x, y) assert unify(ZZ.frac_field(x), ZZ.frac_field(x, y)) == ZZ.frac_field(x, y) assert unify(ZZ.frac_field(x), QQ.frac_field(x, y)) == QQ.frac_field(x, y) assert unify(QQ.frac_field(x), ZZ.frac_field(x, y)) == QQ.frac_field(x, y) assert unify(QQ.frac_field(x), QQ.frac_field(x, y)) == QQ.frac_field(x, y) assert unify(ZZ.frac_field(x, y), ZZ.frac_field(x, z)) == ZZ.frac_field(x, y, z) assert unify(ZZ.frac_field(x, y), QQ.frac_field(x, z)) == QQ.frac_field(x, y, z) assert unify(QQ.frac_field(x, y), ZZ.frac_field(x, z)) == QQ.frac_field(x, y, z) assert unify(QQ.frac_field(x, y), QQ.frac_field(x, z)) == QQ.frac_field(x, y, z) assert unify(ZZ.poly_ring(x), ZZ.frac_field(x)) == ZZ.frac_field(x) assert unify(ZZ.poly_ring(x), QQ.frac_field(x)) == ZZ.frac_field(x) assert unify(QQ.poly_ring(x), ZZ.frac_field(x)) == ZZ.frac_field(x) assert unify(QQ.poly_ring(x), QQ.frac_field(x)) == QQ.frac_field(x) assert unify(ZZ.poly_ring(x, y), ZZ.frac_field(x)) == ZZ.frac_field(x, y) assert unify(ZZ.poly_ring(x, y), QQ.frac_field(x)) == ZZ.frac_field(x, y) assert unify(QQ.poly_ring(x, y), ZZ.frac_field(x)) == ZZ.frac_field(x, y) assert unify(QQ.poly_ring(x, y), QQ.frac_field(x)) == QQ.frac_field(x, y) assert unify(ZZ.poly_ring(x), ZZ.frac_field(x, y)) == ZZ.frac_field(x, y) assert unify(ZZ.poly_ring(x), QQ.frac_field(x, y)) == ZZ.frac_field(x, y) assert unify(QQ.poly_ring(x), ZZ.frac_field(x, y)) == ZZ.frac_field(x, y) assert unify(QQ.poly_ring(x), QQ.frac_field(x, y)) == QQ.frac_field(x, y) assert unify(ZZ.poly_ring(x, y), ZZ.frac_field(x, z)) == ZZ.frac_field(x, y, z) assert unify(ZZ.poly_ring(x, y), QQ.frac_field(x, z)) == ZZ.frac_field(x, y, z) assert unify(QQ.poly_ring(x, y), ZZ.frac_field(x, z)) == ZZ.frac_field(x, y, z) assert unify(QQ.poly_ring(x, y), QQ.frac_field(x, z)) == QQ.frac_field(x, y, z) assert unify(ZZ.frac_field(x), ZZ.poly_ring(x)) == ZZ.frac_field(x) assert unify(ZZ.frac_field(x), QQ.poly_ring(x)) == ZZ.frac_field(x) assert unify(QQ.frac_field(x), ZZ.poly_ring(x)) == ZZ.frac_field(x) assert unify(QQ.frac_field(x), QQ.poly_ring(x)) == QQ.frac_field(x) assert unify(ZZ.frac_field(x, y), ZZ.poly_ring(x)) == ZZ.frac_field(x, y) assert unify(ZZ.frac_field(x, y), QQ.poly_ring(x)) == ZZ.frac_field(x, y) assert unify(QQ.frac_field(x, y), ZZ.poly_ring(x)) == ZZ.frac_field(x, y) assert unify(QQ.frac_field(x, y), QQ.poly_ring(x)) == QQ.frac_field(x, y) assert unify(ZZ.frac_field(x), ZZ.poly_ring(x, y)) == ZZ.frac_field(x, y) assert unify(ZZ.frac_field(x), QQ.poly_ring(x, y)) == ZZ.frac_field(x, y) assert unify(QQ.frac_field(x), ZZ.poly_ring(x, y)) == ZZ.frac_field(x, y) assert unify(QQ.frac_field(x), QQ.poly_ring(x, y)) == QQ.frac_field(x, y) assert unify(ZZ.frac_field(x, y), ZZ.poly_ring(x, z)) == ZZ.frac_field(x, y, z) assert unify(ZZ.frac_field(x, y), QQ.poly_ring(x, z)) == ZZ.frac_field(x, y, z) assert unify(QQ.frac_field(x, y), ZZ.poly_ring(x, z)) == ZZ.frac_field(x, y, z) assert unify(QQ.frac_field(x, y), QQ.poly_ring(x, z)) == QQ.frac_field(x, y, z) def test_Domain_unify_algebraic(): sqrt5 = QQ.algebraic_field(sqrt(5)) sqrt7 = QQ.algebraic_field(sqrt(7)) sqrt57 = QQ.algebraic_field(sqrt(5), sqrt(7)) assert sqrt5.unify(sqrt7) == sqrt57 assert sqrt5.unify(sqrt5[x, y]) == sqrt5[x, y] assert sqrt5[x, y].unify(sqrt5) == sqrt5[x, y] assert sqrt5.unify(sqrt5.frac_field(x, y)) == sqrt5.frac_field(x, y) assert sqrt5.frac_field(x, y).unify(sqrt5) == sqrt5.frac_field(x, y) assert sqrt5.unify(sqrt7[x, y]) == sqrt57[x, y] assert sqrt5[x, y].unify(sqrt7) == sqrt57[x, y] assert sqrt5.unify(sqrt7.frac_field(x, y)) == sqrt57.frac_field(x, y) assert sqrt5.frac_field(x, y).unify(sqrt7) == sqrt57.frac_field(x, y) def test_Domain_unify_with_symbols(): raises(UnificationFailed, lambda: ZZ[x, y].unify_with_symbols(ZZ, (y, z))) raises(UnificationFailed, lambda: ZZ.unify_with_symbols(ZZ[x, y], (y, z))) def test_Domain__contains__(): assert (0 in EX) is True assert (0 in ZZ) is True assert (0 in QQ) is True assert (0 in RR) is True assert (0 in CC) is True assert (0 in ALG) is True assert (0 in ZZ[x, y]) is True assert (0 in QQ[x, y]) is True assert (0 in RR[x, y]) is True assert (-7 in EX) is True assert (-7 in ZZ) is True assert (-7 in QQ) is True assert (-7 in RR) is True assert (-7 in CC) is True assert (-7 in ALG) is True assert (-7 in ZZ[x, y]) is True assert (-7 in QQ[x, y]) is True assert (-7 in RR[x, y]) is True assert (17 in EX) is True assert (17 in ZZ) is True assert (17 in QQ) is True assert (17 in RR) is True assert (17 in CC) is True assert (17 in ALG) is True assert (17 in ZZ[x, y]) is True assert (17 in QQ[x, y]) is True assert (17 in RR[x, y]) is True assert (-S(1)/7 in EX) is True assert (-S(1)/7 in ZZ) is False assert (-S(1)/7 in QQ) is True assert (-S(1)/7 in RR) is True assert (-S(1)/7 in CC) is True assert (-S(1)/7 in ALG) is True assert (-S(1)/7 in ZZ[x, y]) is False assert (-S(1)/7 in QQ[x, y]) is True assert (-S(1)/7 in RR[x, y]) is True assert (S(3)/5 in EX) is True assert (S(3)/5 in ZZ) is False assert (S(3)/5 in QQ) is True assert (S(3)/5 in RR) is True assert (S(3)/5 in CC) is True assert (S(3)/5 in ALG) is True assert (S(3)/5 in ZZ[x, y]) is False assert (S(3)/5 in QQ[x, y]) is True assert (S(3)/5 in RR[x, y]) is True assert (3.0 in EX) is True assert (3.0 in ZZ) is True assert (3.0 in QQ) is True assert (3.0 in RR) is True assert (3.0 in CC) is True assert (3.0 in ALG) is True assert (3.0 in ZZ[x, y]) is True assert (3.0 in QQ[x, y]) is True assert (3.0 in RR[x, y]) is True assert (3.14 in EX) is True assert (3.14 in ZZ) is False assert (3.14 in QQ) is True assert (3.14 in RR) is True assert (3.14 in CC) is True assert (3.14 in ALG) is True assert (3.14 in ZZ[x, y]) is False assert (3.14 in QQ[x, y]) is True assert (3.14 in RR[x, y]) is True assert (oo in EX) is True assert (oo in ZZ) is False assert (oo in QQ) is False assert (oo in RR) is True assert (oo in CC) is True assert (oo in ALG) is False assert (oo in ZZ[x, y]) is False assert (oo in QQ[x, y]) is False assert (oo in RR[x, y]) is True assert (-oo in EX) is True assert (-oo in ZZ) is False assert (-oo in QQ) is False assert (-oo in RR) is True assert (-oo in CC) is True assert (-oo in ALG) is False assert (-oo in ZZ[x, y]) is False assert (-oo in QQ[x, y]) is False assert (-oo in RR[x, y]) is True assert (sqrt(7) in EX) is True assert (sqrt(7) in ZZ) is False assert (sqrt(7) in QQ) is False assert (sqrt(7) in RR) is True assert (sqrt(7) in CC) is True assert (sqrt(7) in ALG) is False assert (sqrt(7) in ZZ[x, y]) is False assert (sqrt(7) in QQ[x, y]) is False assert (sqrt(7) in RR[x, y]) is True assert (2*sqrt(3) + 1 in EX) is True assert (2*sqrt(3) + 1 in ZZ) is False assert (2*sqrt(3) + 1 in QQ) is False assert (2*sqrt(3) + 1 in RR) is True assert (2*sqrt(3) + 1 in CC) is True assert (2*sqrt(3) + 1 in ALG) is True assert (2*sqrt(3) + 1 in ZZ[x, y]) is False assert (2*sqrt(3) + 1 in QQ[x, y]) is False assert (2*sqrt(3) + 1 in RR[x, y]) is True assert (sin(1) in EX) is True assert (sin(1) in ZZ) is False assert (sin(1) in QQ) is False assert (sin(1) in RR) is True assert (sin(1) in CC) is True assert (sin(1) in ALG) is False assert (sin(1) in ZZ[x, y]) is False assert (sin(1) in QQ[x, y]) is False assert (sin(1) in RR[x, y]) is True assert (x**2 + 1 in EX) is True assert (x**2 + 1 in ZZ) is False assert (x**2 + 1 in QQ) is False assert (x**2 + 1 in RR) is False assert (x**2 + 1 in CC) is False assert (x**2 + 1 in ALG) is False assert (x**2 + 1 in ZZ[x]) is True assert (x**2 + 1 in QQ[x]) is True assert (x**2 + 1 in RR[x]) is True assert (x**2 + 1 in ZZ[x, y]) is True assert (x**2 + 1 in QQ[x, y]) is True assert (x**2 + 1 in RR[x, y]) is True assert (x**2 + y**2 in EX) is True assert (x**2 + y**2 in ZZ) is False assert (x**2 + y**2 in QQ) is False assert (x**2 + y**2 in RR) is False assert (x**2 + y**2 in CC) is False assert (x**2 + y**2 in ALG) is False assert (x**2 + y**2 in ZZ[x]) is False assert (x**2 + y**2 in QQ[x]) is False assert (x**2 + y**2 in RR[x]) is False assert (x**2 + y**2 in ZZ[x, y]) is True assert (x**2 + y**2 in QQ[x, y]) is True assert (x**2 + y**2 in RR[x, y]) is True assert (S(3)/2*x/(y + 1) - z in QQ[x, y, z]) is False def test_Domain_get_ring(): assert ZZ.has_assoc_Ring is True assert QQ.has_assoc_Ring is True assert ZZ[x].has_assoc_Ring is True assert QQ[x].has_assoc_Ring is True assert ZZ[x, y].has_assoc_Ring is True assert QQ[x, y].has_assoc_Ring is True assert ZZ.frac_field(x).has_assoc_Ring is True assert QQ.frac_field(x).has_assoc_Ring is True assert ZZ.frac_field(x, y).has_assoc_Ring is True assert QQ.frac_field(x, y).has_assoc_Ring is True assert EX.has_assoc_Ring is False assert RR.has_assoc_Ring is False assert ALG.has_assoc_Ring is False assert ZZ.get_ring() == ZZ assert QQ.get_ring() == ZZ assert ZZ[x].get_ring() == ZZ[x] assert QQ[x].get_ring() == QQ[x] assert ZZ[x, y].get_ring() == ZZ[x, y] assert QQ[x, y].get_ring() == QQ[x, y] assert ZZ.frac_field(x).get_ring() == ZZ[x] assert QQ.frac_field(x).get_ring() == QQ[x] assert ZZ.frac_field(x, y).get_ring() == ZZ[x, y] assert QQ.frac_field(x, y).get_ring() == QQ[x, y] assert EX.get_ring() == EX raises(DomainError, lambda: RR.get_ring()) raises(DomainError, lambda: ALG.get_ring()) def test_Domain_get_field(): assert EX.has_assoc_Field is True assert ZZ.has_assoc_Field is True assert QQ.has_assoc_Field is True assert RR.has_assoc_Field is True assert ALG.has_assoc_Field is True assert ZZ[x].has_assoc_Field is True assert QQ[x].has_assoc_Field is True assert ZZ[x, y].has_assoc_Field is True assert QQ[x, y].has_assoc_Field is True assert EX.get_field() == EX assert ZZ.get_field() == QQ assert QQ.get_field() == QQ assert RR.get_field() == RR assert ALG.get_field() == ALG assert ZZ[x].get_field() == ZZ.frac_field(x) assert QQ[x].get_field() == QQ.frac_field(x) assert ZZ[x, y].get_field() == ZZ.frac_field(x, y) assert QQ[x, y].get_field() == QQ.frac_field(x, y) def test_Domain_get_exact(): assert EX.get_exact() == EX assert ZZ.get_exact() == ZZ assert QQ.get_exact() == QQ assert RR.get_exact() == QQ assert ALG.get_exact() == ALG assert ZZ[x].get_exact() == ZZ[x] assert QQ[x].get_exact() == QQ[x] assert ZZ[x, y].get_exact() == ZZ[x, y] assert QQ[x, y].get_exact() == QQ[x, y] assert ZZ.frac_field(x).get_exact() == ZZ.frac_field(x) assert QQ.frac_field(x).get_exact() == QQ.frac_field(x) assert ZZ.frac_field(x, y).get_exact() == ZZ.frac_field(x, y) assert QQ.frac_field(x, y).get_exact() == QQ.frac_field(x, y) def test_Domain_convert(): assert QQ.convert(10e-52) == QQ(1684996666696915, 1684996666696914987166688442938726917102321526408785780068975640576) R, x = ring("x", ZZ) assert ZZ.convert(x - x) == 0 assert ZZ.convert(x - x, R.to_domain()) == 0 def test_PolynomialRing__init(): raises(GeneratorsNeeded, lambda: ZZ.poly_ring()) def test_FractionField__init(): raises(GeneratorsNeeded, lambda: ZZ.frac_field()) def test_inject(): assert ZZ.inject(x, y, z) == ZZ[x, y, z] assert ZZ[x].inject(y, z) == ZZ[x, y, z] assert ZZ.frac_field(x).inject(y, z) == ZZ.frac_field(x, y, z) raises(GeneratorsError, lambda: ZZ[x].inject(x)) def test_Domain_map(): seq = ZZ.map([1, 2, 3, 4]) assert all(ZZ.of_type(elt) for elt in seq) seq = ZZ.map([[1, 2, 3, 4]]) assert all(ZZ.of_type(elt) for elt in seq[0]) and len(seq) == 1 def test_Domain___eq__(): assert (ZZ[x, y] == ZZ[x, y]) is True assert (QQ[x, y] == QQ[x, y]) is True assert (ZZ[x, y] == QQ[x, y]) is False assert (QQ[x, y] == ZZ[x, y]) is False assert (ZZ.frac_field(x, y) == ZZ.frac_field(x, y)) is True assert (QQ.frac_field(x, y) == QQ.frac_field(x, y)) is True assert (ZZ.frac_field(x, y) == QQ.frac_field(x, y)) is False assert (QQ.frac_field(x, y) == ZZ.frac_field(x, y)) is False def test_Domain__algebraic_field(): alg = ZZ.algebraic_field(sqrt(2)) assert alg.ext.minpoly == Poly(x**2 - 2) assert alg.dom == QQ alg = QQ.algebraic_field(sqrt(2)) assert alg.ext.minpoly == Poly(x**2 - 2) assert alg.dom == QQ alg = alg.algebraic_field(sqrt(3)) assert alg.ext.minpoly == Poly(x**4 - 10*x**2 + 1) assert alg.dom == QQ def test_PolynomialRing_from_FractionField(): F, x,y = field("x,y", ZZ) R, X,Y = ring("x,y", ZZ) f = (x**2 + y**2)/(x + 1) g = (x**2 + y**2)/4 h = x**2 + y**2 assert R.to_domain().from_FractionField(f, F.to_domain()) is None assert R.to_domain().from_FractionField(g, F.to_domain()) == X**2/4 + Y**2/4 assert R.to_domain().from_FractionField(h, F.to_domain()) == X**2 + Y**2 F, x,y = field("x,y", QQ) R, X,Y = ring("x,y", QQ) f = (x**2 + y**2)/(x + 1) g = (x**2 + y**2)/4 h = x**2 + y**2 assert R.to_domain().from_FractionField(f, F.to_domain()) is None assert R.to_domain().from_FractionField(g, F.to_domain()) == X**2/4 + Y**2/4 assert R.to_domain().from_FractionField(h, F.to_domain()) == X**2 + Y**2 def test_FractionField_from_PolynomialRing(): R, x,y = ring("x,y", QQ) F, X,Y = field("x,y", ZZ) f = 3*x**2 + 5*y**2 g = x**2/3 + y**2/5 assert F.to_domain().from_PolynomialRing(f, R.to_domain()) == 3*X**2 + 5*Y**2 assert F.to_domain().from_PolynomialRing(g, R.to_domain()) == (5*X**2 + 3*Y**2)/15 def test_FF_of_type(): assert FF(3).of_type(FF(3)(1)) is True assert FF(5).of_type(FF(5)(3)) is True assert FF(5).of_type(FF(7)(3)) is False def test___eq__(): assert not QQ[x] == ZZ[x] assert not QQ.frac_field(x) == ZZ.frac_field(x) def test_RealField_from_sympy(): assert RR.convert(S(0)) == RR.dtype(0) assert RR.convert(S(0.0)) == RR.dtype(0.0) assert RR.convert(S(1)) == RR.dtype(1) assert RR.convert(S(1.0)) == RR.dtype(1.0) assert RR.convert(sin(1)) == RR.dtype(sin(1).evalf()) assert RR.convert(oo) == RR("+inf") assert RR.convert(-oo) == RR("-inf") raises(CoercionFailed, lambda: RR.convert(x)) def test_ModularInteger(): F3 = FF(3) a = F3(0) assert isinstance(a, F3.dtype) and a == 0 a = F3(1) assert isinstance(a, F3.dtype) and a == 1 a = F3(2) assert isinstance(a, F3.dtype) and a == 2 a = F3(3) assert isinstance(a, F3.dtype) and a == 0 a = F3(4) assert isinstance(a, F3.dtype) and a == 1 a = F3(F3(0)) assert isinstance(a, F3.dtype) and a == 0 a = F3(F3(1)) assert isinstance(a, F3.dtype) and a == 1 a = F3(F3(2)) assert isinstance(a, F3.dtype) and a == 2 a = F3(F3(3)) assert isinstance(a, F3.dtype) and a == 0 a = F3(F3(4)) assert isinstance(a, F3.dtype) and a == 1 a = -F3(1) assert isinstance(a, F3.dtype) and a == 2 a = -F3(2) assert isinstance(a, F3.dtype) and a == 1 a = 2 + F3(2) assert isinstance(a, F3.dtype) and a == 1 a = F3(2) + 2 assert isinstance(a, F3.dtype) and a == 1 a = F3(2) + F3(2) assert isinstance(a, F3.dtype) and a == 1 a = F3(2) + F3(2) assert isinstance(a, F3.dtype) and a == 1 a = 3 - F3(2) assert isinstance(a, F3.dtype) and a == 1 a = F3(3) - 2 assert isinstance(a, F3.dtype) and a == 1 a = F3(3) - F3(2) assert isinstance(a, F3.dtype) and a == 1 a = F3(3) - F3(2) assert isinstance(a, F3.dtype) and a == 1 a = 2*F3(2) assert isinstance(a, F3.dtype) and a == 1 a = F3(2)*2 assert isinstance(a, F3.dtype) and a == 1 a = F3(2)*F3(2) assert isinstance(a, F3.dtype) and a == 1 a = F3(2)*F3(2) assert isinstance(a, F3.dtype) and a == 1 a = 2/F3(2) assert isinstance(a, F3.dtype) and a == 1 a = F3(2)/2 assert isinstance(a, F3.dtype) and a == 1 a = F3(2)/F3(2) assert isinstance(a, F3.dtype) and a == 1 a = F3(2)/F3(2) assert isinstance(a, F3.dtype) and a == 1 a = 1 % F3(2) assert isinstance(a, F3.dtype) and a == 1 a = F3(1) % 2 assert isinstance(a, F3.dtype) and a == 1 a = F3(1) % F3(2) assert isinstance(a, F3.dtype) and a == 1 a = F3(1) % F3(2) assert isinstance(a, F3.dtype) and a == 1 a = F3(2)**0 assert isinstance(a, F3.dtype) and a == 1 a = F3(2)**1 assert isinstance(a, F3.dtype) and a == 2 a = F3(2)**2 assert isinstance(a, F3.dtype) and a == 1 assert bool(F3(3)) is False assert bool(F3(4)) is True F5 = FF(5) a = F5(1)**(-1) assert isinstance(a, F5.dtype) and a == 1 a = F5(2)**(-1) assert isinstance(a, F5.dtype) and a == 3 a = F5(3)**(-1) assert isinstance(a, F5.dtype) and a == 2 a = F5(4)**(-1) assert isinstance(a, F5.dtype) and a == 4 assert (F5(1) < F5(2)) is True assert (F5(1) <= F5(2)) is True assert (F5(1) > F5(2)) is False assert (F5(1) >= F5(2)) is False assert (F5(3) < F5(2)) is False assert (F5(3) <= F5(2)) is False assert (F5(3) > F5(2)) is True assert (F5(3) >= F5(2)) is True assert (F5(1) < F5(7)) is True assert (F5(1) <= F5(7)) is True assert (F5(1) > F5(7)) is False assert (F5(1) >= F5(7)) is False assert (F5(3) < F5(7)) is False assert (F5(3) <= F5(7)) is False assert (F5(3) > F5(7)) is True assert (F5(3) >= F5(7)) is True assert (F5(1) < 2) is True assert (F5(1) <= 2) is True assert (F5(1) > 2) is False assert (F5(1) >= 2) is False assert (F5(3) < 2) is False assert (F5(3) <= 2) is False assert (F5(3) > 2) is True assert (F5(3) >= 2) is True assert (F5(1) < 7) is True assert (F5(1) <= 7) is True assert (F5(1) > 7) is False assert (F5(1) >= 7) is False assert (F5(3) < 7) is False assert (F5(3) <= 7) is False assert (F5(3) > 7) is True assert (F5(3) >= 7) is True raises(NotInvertible, lambda: F5(0)**(-1)) raises(NotInvertible, lambda: F5(5)**(-1)) raises(ValueError, lambda: FF(0)) raises(ValueError, lambda: FF(2.1)) def test_QQ_int(): assert int(QQ(2**2000, 3**1250)) == 455431 assert int(QQ(2**100, 3)) == 422550200076076467165567735125 sympy-0.7.4.1/sympy/polys/domains/tests/test_polynomialring.py0000644000175000017500000000640212253362407025045 0ustar georgeskgeorgesk"""Tests for the PolynomialRing classes. """ from sympy.polys.domains import QQ, ZZ, PolynomialRing from sympy.polys.polyerrors import ExactQuotientFailed, CoercionFailed, NotReversible from sympy.abc import x, y from sympy.utilities.pytest import raises def test_build_order(): R = QQ.old_poly_ring(x, y, order=(("lex", x), ("ilex", y))) assert R.order((1, 5)) == ((1,), (-5,)) def test_globalring(): Qxy = QQ.old_frac_field(x, y) R = QQ.old_poly_ring(x, y) X = R.convert(x) Y = R.convert(y) assert x in R assert 1/x not in R assert 1/(1 + x) not in R assert Y in R assert X.ring == R assert X * (Y**2 + 1) == R.convert(x * (y**2 + 1)) assert X * y == X * Y == R.convert(x * y) == x * Y assert X + y == X + Y == R.convert(x + y) == x + Y assert X - y == X - Y == R.convert(x - y) == x - Y assert X + 1 == R.convert(x + 1) raises(ExactQuotientFailed, lambda: X/Y) raises(ExactQuotientFailed, lambda: x/Y) raises(ExactQuotientFailed, lambda: X/y) assert X**2 / X == X assert R.from_GlobalPolynomialRing(ZZ.old_poly_ring(x, y).convert(x), ZZ.old_poly_ring(x, y)) == X assert R.from_FractionField(Qxy.convert(x), Qxy) == X assert R.from_FractionField(Qxy.convert(x)/y, Qxy) is None assert R._sdm_to_vector(R._vector_to_sdm([X, Y], R.order), 2) == [X, Y] def test_localring(): Qxy = QQ.old_frac_field(x, y) R = QQ.old_poly_ring(x, y, order="ilex") X = R.convert(x) Y = R.convert(y) assert x in R assert 1/x not in R assert 1/(1 + x) in R assert Y in R assert X.ring == R assert X*(Y**2 + 1)/(1 + X) == R.convert(x*(y**2 + 1)/(1 + x)) assert X*y == X*Y raises(ExactQuotientFailed, lambda: X/Y) raises(ExactQuotientFailed, lambda: x/Y) raises(ExactQuotientFailed, lambda: X/y) assert X + y == X + Y == R.convert(x + y) == x + Y assert X - y == X - Y == R.convert(x - y) == x - Y assert X + 1 == R.convert(x + 1) assert X**2 / X == X assert R.from_GlobalPolynomialRing(ZZ.old_poly_ring(x, y).convert(x), ZZ.old_poly_ring(x, y)) == X assert R.from_FractionField(Qxy.convert(x), Qxy) == X raises(CoercionFailed, lambda: R.from_FractionField(Qxy.convert(x)/y, Qxy)) raises(ExactQuotientFailed, lambda: X/Y) raises(NotReversible, lambda: X.invert()) assert R._sdm_to_vector( R._vector_to_sdm([X/(X + 1), Y/(1 + X*Y)], R.order), 2) == \ [X*(1 + X*Y), Y*(1 + X)] def test_conversion(): L = QQ.old_poly_ring(x, y, order="ilex") G = QQ.old_poly_ring(x, y) assert L.convert(x) == L.convert(G.convert(x), G) assert G.convert(x) == G.convert(L.convert(x), L) raises(CoercionFailed, lambda: G.convert(L.convert(1/(1 + x)), L)) def test_units(): R = QQ.old_poly_ring(x) assert R.is_unit(R.convert(1)) assert R.is_unit(R.convert(2)) assert not R.is_unit(R.convert(x)) assert not R.is_unit(R.convert(1 + x)) R = QQ.old_poly_ring(x, order='ilex') assert R.is_unit(R.convert(1)) assert R.is_unit(R.convert(2)) assert not R.is_unit(R.convert(x)) assert R.is_unit(R.convert(1 + x)) R = ZZ.old_poly_ring(x) assert R.is_unit(R.convert(1)) assert not R.is_unit(R.convert(2)) assert not R.is_unit(R.convert(x)) assert not R.is_unit(R.convert(1 + x)) sympy-0.7.4.1/sympy/polys/domains/tests/__init__.py0000644000175000017500000000000012253362407022466 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/polys/domains/tests/test_quotientring.py0000644000175000017500000000255612253362407024540 0ustar georgeskgeorgesk"""Tests for quotient rings.""" from sympy import QQ, ZZ from sympy.abc import x, y from sympy.polys.polyerrors import NotReversible from sympy.utilities.pytest import raises def test_QuotientRingElement(): R = QQ.old_poly_ring(x)/[x**10] X = R.convert(x) assert X*(X + 1) == R.convert(x**2 + x) assert X*x == R.convert(x**2) assert x*X == R.convert(x**2) assert X + x == R.convert(2*x) assert x + X == 2*X assert X**2 == R.convert(x**2) assert 1/(1 - X) == R.convert(sum(x**i for i in range(10))) assert X**10 == R.zero assert X != x raises(NotReversible, lambda: 1/X) def test_QuotientRing(): I = QQ.old_poly_ring(x).ideal(x**2 + 1) R = QQ.old_poly_ring(x)/I assert R == QQ.old_poly_ring(x)/[x**2 + 1] assert R == QQ.old_poly_ring(x)/QQ.old_poly_ring(x).ideal(x**2 + 1) assert R != QQ.old_poly_ring(x) assert R.convert(1)/x == -x + I assert -1 + I == x**2 + I assert R.convert(ZZ(1), ZZ) == 1 + I assert R.convert(R.convert(x), R) == R.convert(x) X = R.convert(x) Y = QQ.old_poly_ring(x).convert(x) assert -1 + I == X**2 + I assert -1 + I == Y**2 + I assert R.to_sympy(X) == x raises(ValueError, lambda: QQ.old_poly_ring(x)/QQ.old_poly_ring(x, y).ideal(x)) R = QQ.old_poly_ring(x, order="ilex") I = R.ideal(x) assert R.convert(1) + I == (R/I).convert(1) sympy-0.7.4.1/sympy/polys/domains/__init__.py0000644000175000017500000000451612253362407021344 0ustar georgeskgeorgesk"""Implementation of mathematical domains. """ __all__ = [] from . import domain __all__.extend(domain.__all__) from .domain import * from . import finitefield __all__.extend(finitefield.__all__) from .finitefield import * from . import integerring __all__.extend(integerring.__all__) from .integerring import * from . import rationalfield __all__.extend(rationalfield.__all__) from .rationalfield import * from . import realfield __all__.extend(realfield.__all__) from .realfield import * from . import complexfield __all__.extend(complexfield.__all__) from .complexfield import * from . import pythonfinitefield __all__.extend(pythonfinitefield.__all__) from .pythonfinitefield import * from . import gmpyfinitefield __all__.extend(gmpyfinitefield.__all__) from .gmpyfinitefield import * from . import pythonintegerring __all__.extend(pythonintegerring.__all__) from .pythonintegerring import * from . import gmpyintegerring __all__.extend(gmpyintegerring.__all__) from .gmpyintegerring import * from . import pythonrationalfield __all__.extend(pythonrationalfield.__all__) from .pythonrationalfield import * from . import gmpyrationalfield __all__.extend(gmpyrationalfield.__all__) from .gmpyrationalfield import * from . import algebraicfield __all__.extend(algebraicfield.__all__) from .algebraicfield import * from . import polynomialring __all__.extend(polynomialring.__all__) from .polynomialring import * from . import fractionfield __all__.extend(fractionfield.__all__) from .fractionfield import * from . import expressiondomain __all__.extend(expressiondomain.__all__) from .expressiondomain import * FF_python = PythonFiniteField FF_gmpy = GMPYFiniteField ZZ_python = PythonIntegerRing ZZ_gmpy = GMPYIntegerRing QQ_python = PythonRationalField QQ_gmpy = GMPYRationalField RR = RealField() CC = ComplexField() from .pythonrational import PythonRational from sympy.core.compatibility import GROUND_TYPES _GROUND_TYPES_MAP = { 'gmpy': (FF_gmpy, ZZ_gmpy(), QQ_gmpy()), '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 EX = ExpressionDomain() __all__.extend([ "FF_python", "FF_gmpy", "ZZ_python", "ZZ_gmpy", "QQ_python", "QQ_gmpy", "GF", "FF", "ZZ", "QQ", "RR", "CC", "EX", ]) sympy-0.7.4.1/sympy/polys/polyoptions.py0000644000175000017500000005055612253362407020557 0ustar georgeskgeorgesk"""Options manager for :class:`Poly` and public API functions. """ from __future__ import print_function, division __all__ = ["Options"] from sympy.core import S, Basic, sympify from sympy.core.compatibility import string_types, with_metaclass from sympy.utilities import numbered_symbols, topological_sort, public from sympy.utilities.iterables import has_dups from sympy.polys.polyerrors import GeneratorsError, OptionError, FlagError import sympy.polys import re class Option(object): """Base class for all kinds of options. """ option = None is_Flag = False requires = [] excludes = [] after = [] before = [] @classmethod def default(cls): return None @classmethod def preprocess(cls, option): return None @classmethod def postprocess(cls, options): pass class Flag(Option): """Base class for all kinds of flags. """ is_Flag = True class BooleanOption(Option): """An option that must have a boolean value or equivalent assigned. """ @classmethod def preprocess(cls, value): if value in [True, False]: return bool(value) else: raise OptionError("'%s' must have a boolean value assigned, got %s" % (cls.option, value)) class OptionType(type): """Base type for all options that does registers options. """ def __init__(cls, *args, **kwargs): @property def getter(self): try: return self[cls.option] except KeyError: return cls.default() setattr(Options, cls.option, getter) Options.__options__[cls.option] = cls @public class Options(dict): """ Options manager for polynomial manipulation module. Examples ======== >>> from sympy.polys.polyoptions import Options >>> from sympy.polys.polyoptions import build_options >>> from sympy.abc import x, y, z >>> Options((x, y, z), {'domain': 'ZZ'}) {'auto': False, 'domain': ZZ, 'gens': (x, y, z)} >>> build_options((x, y, z), {'domain': 'ZZ'}) {'auto': False, 'domain': ZZ, 'gens': (x, y, z)} **Options** * Expand --- boolean option * Gens --- option * Wrt --- option * Sort --- option * Order --- option * Field --- boolean option * Greedy --- boolean option * Domain --- option * Split --- boolean option * Gaussian --- boolean option * Extension --- option * Modulus --- option * Symmetric --- boolean option * Strict --- boolean option **Flags** * Auto --- boolean flag * Frac --- boolean flag * Formal --- boolean flag * Polys --- boolean flag * Include --- boolean flag * All --- boolean flag * Gen --- flag """ __order__ = None __options__ = {} def __init__(self, gens, args, flags=None, strict=False): dict.__init__(self) if gens and args.get('gens', ()): raise OptionError( "both '*gens' and keyword argument 'gens' supplied") elif gens: args = dict(args) args['gens'] = gens defaults = args.pop('defaults', {}) def preprocess_options(args): for option, value in args.items(): try: cls = self.__options__[option] except KeyError: raise OptionError("'%s' is not a valid option" % option) if issubclass(cls, Flag): if flags is None or option not in flags: if strict: raise OptionError("'%s' flag is not allowed in this context" % option) if value is not None: self[option] = cls.preprocess(value) preprocess_options(args) for key, value in dict(defaults).items(): if key in self: del defaults[key] else: for option in self.keys(): cls = self.__options__[option] if key in cls.excludes: del defaults[key] break preprocess_options(defaults) for option in self.keys(): cls = self.__options__[option] for require_option in cls.requires: if self.get(require_option) is None: raise OptionError("'%s' option is only allowed together with '%s'" % (option, require_option)) for exclude_option in cls.excludes: if self.get(exclude_option) is not None: raise OptionError("'%s' option is not allowed together with '%s'" % (option, exclude_option)) for option in self.__order__: self.__options__[option].postprocess(self) @classmethod def _init_dependencies_order(cls): """Resolve the order of options' processing. """ if cls.__order__ is None: vertices, edges = [], set([]) for name, option in cls.__options__.items(): vertices.append(name) for _name in option.after: edges.add((_name, name)) for _name in option.before: edges.add((name, _name)) try: cls.__order__ = topological_sort((vertices, list(edges))) except ValueError: raise RuntimeError( "cycle detected in sympy.polys options framework") def clone(self, updates={}): """Clone ``self`` and update specified options. """ obj = dict.__new__(self.__class__) for option, value in self.items(): obj[option] = value for option, value in updates.items(): obj[option] = value return obj def __setattr__(self, attr, value): if attr in self.__options__: self[attr] = value else: super(Options, self).__setattr__(attr, value) @property def args(self): args = {} for option, value in self.items(): if value is not None and option != 'gens': cls = self.__options__[option] if not issubclass(cls, Flag): args[option] = value return args @property def options(self): options = {} for option, cls in self.__options__.items(): if not issubclass(cls, Flag): options[option] = getattr(self, option) return options @property def flags(self): flags = {} for option, cls in self.__options__.items(): if issubclass(cls, Flag): flags[option] = getattr(self, option) return flags class Expand(with_metaclass(OptionType, BooleanOption)): """``expand`` option to polynomial manipulation functions. """ option = 'expand' requires = [] excludes = [] @classmethod def default(cls): return True class Gens(with_metaclass(OptionType, Option)): """``gens`` option to polynomial manipulation functions. """ option = 'gens' requires = [] excludes = [] @classmethod def default(cls): return () @classmethod def preprocess(cls, gens): if isinstance(gens, Basic): gens = (gens,) elif len(gens) == 1 and hasattr(gens[0], '__iter__'): gens = gens[0] if gens == (None,): gens = () elif has_dups(gens): raise GeneratorsError("duplicated generators: %s" % str(gens)) elif any(gen.is_commutative is False for gen in gens): raise GeneratorsError("non-commutative generators: %s" % str(gens)) return tuple(gens) class Wrt(with_metaclass(OptionType, Option)): """``wrt`` option to polynomial manipulation functions. """ option = 'wrt' requires = [] excludes = [] _re_split = re.compile(r"\s*,\s*|\s+") @classmethod def preprocess(cls, wrt): if isinstance(wrt, Basic): return [str(wrt)] elif isinstance(wrt, str): wrt = wrt.strip() if wrt.endswith(','): raise OptionError('Bad input: missing parameter.') if not wrt: return [] return [ gen for gen in cls._re_split.split(wrt) ] elif hasattr(wrt, '__getitem__'): return list(map(str, wrt)) else: raise OptionError("invalid argument for 'wrt' option") class Sort(with_metaclass(OptionType, Option)): """``sort`` option to polynomial manipulation functions. """ option = 'sort' requires = [] excludes = [] @classmethod def default(cls): return [] @classmethod def preprocess(cls, sort): if isinstance(sort, str): return [ gen.strip() for gen in sort.split('>') ] elif hasattr(sort, '__getitem__'): return list(map(str, sort)) else: raise OptionError("invalid argument for 'sort' option") class Order(with_metaclass(OptionType, Option)): """``order`` option to polynomial manipulation functions. """ option = 'order' requires = [] excludes = [] @classmethod def default(cls): return sympy.polys.orderings.lex @classmethod def preprocess(cls, order): return sympy.polys.orderings.monomial_key(order) class Field(with_metaclass(OptionType, BooleanOption)): """``field`` option to polynomial manipulation functions. """ option = 'field' requires = [] excludes = ['domain', 'split', 'gaussian'] class Greedy(with_metaclass(OptionType, BooleanOption)): """``greedy`` option to polynomial manipulation functions. """ option = 'greedy' requires = [] excludes = ['domain', 'split', 'gaussian', 'extension', 'modulus', 'symmetric'] class Composite(with_metaclass(OptionType, BooleanOption)): """``composite`` option to polynomial manipulation functions. """ option = 'composite' @classmethod def default(cls): return None requires = [] excludes = ['domain', 'split', 'gaussian', 'extension', 'modulus', 'symmetric'] class Domain(with_metaclass(OptionType, Option)): """``domain`` option to polynomial manipulation functions. """ option = 'domain' requires = [] excludes = ['field', 'greedy', 'split', 'gaussian', 'extension'] after = ['gens'] _re_realfield = re.compile("^(R|RR)(_(\d+))?$") _re_complexfield = re.compile("^(C|CC)(_(\d+))?$") _re_finitefield = re.compile("^(FF|GF)\((\d+)\)$") _re_polynomial = re.compile("^(Z|ZZ|Q|QQ)\[(.+)\]$") _re_fraction = re.compile("^(Z|ZZ|Q|QQ)\((.+)\)$") _re_algebraic = re.compile("^(Q|QQ)\<(.+)\>$") @classmethod def preprocess(cls, domain): if isinstance(domain, sympy.polys.domains.Domain): return domain elif hasattr(domain, 'to_domain'): return domain.to_domain() elif isinstance(domain, string_types): if domain in ['Z', 'ZZ']: return sympy.polys.domains.ZZ if domain in ['Q', 'QQ']: return sympy.polys.domains.QQ if domain == 'EX': return sympy.polys.domains.EX r = cls._re_realfield.match(domain) if r is not None: _, _, prec = r.groups() if prec is None: return sympy.polys.domains.RR else: return sympy.polys.domains.RealField(int(prec)) r = cls._re_complexfield.match(domain) if r is not None: _, _, prec = r.groups() if prec is None: return sympy.polys.domains.CC else: return sympy.polys.domains.ComplexField(int(prec)) r = cls._re_finitefield.match(domain) if r is not None: return sympy.polys.domains.FF(int(r.groups()[1])) r = cls._re_polynomial.match(domain) if r is not None: ground, gens = r.groups() gens = list(map(sympify, gens.split(','))) if ground in ['Z', 'ZZ']: return sympy.polys.domains.ZZ.poly_ring(*gens) else: return sympy.polys.domains.QQ.poly_ring(*gens) r = cls._re_fraction.match(domain) if r is not None: ground, gens = r.groups() gens = list(map(sympify, gens.split(','))) if ground in ['Z', 'ZZ']: return sympy.polys.domains.ZZ.frac_field(*gens) else: return sympy.polys.domains.QQ.frac_field(*gens) r = cls._re_algebraic.match(domain) if r is not None: gens = list(map(sympify, r.groups()[1].split(','))) return sympy.polys.domains.QQ.algebraic_field(*gens) raise OptionError('expected a valid domain specification, got %s' % domain) @classmethod def postprocess(cls, options): if 'gens' in options and 'domain' in options and options['domain'].is_Composite and \ (set(options['domain'].symbols) & set(options['gens'])): raise GeneratorsError( "ground domain and generators interfere together") elif ('gens' not in options or not options['gens']) and \ 'domain' in options and options['domain'] == sympy.polys.domains.EX: raise GeneratorsError("you have to provide generators because EX domain was requested") class Split(with_metaclass(OptionType, BooleanOption)): """``split`` option to polynomial manipulation functions. """ option = 'split' requires = [] excludes = ['field', 'greedy', 'domain', 'gaussian', 'extension', 'modulus', 'symmetric'] @classmethod def postprocess(cls, options): if 'split' in options: raise NotImplementedError("'split' option is not implemented yet") class Gaussian(with_metaclass(OptionType, BooleanOption)): """``gaussian`` option to polynomial manipulation functions. """ option = 'gaussian' requires = [] excludes = ['field', 'greedy', 'domain', 'split', 'extension', 'modulus', 'symmetric'] @classmethod def postprocess(cls, options): if 'gaussian' in options and options['gaussian'] is True: options['extension'] = set([S.ImaginaryUnit]) Extension.postprocess(options) class Extension(with_metaclass(OptionType, Option)): """``extension`` option to polynomial manipulation functions. """ option = 'extension' requires = [] excludes = ['greedy', 'domain', 'split', 'gaussian', 'modulus', 'symmetric'] @classmethod def preprocess(cls, extension): if extension == 1: return bool(extension) elif extension == 0: raise OptionError("'False' is an invalid argument for 'extension'") else: if not hasattr(extension, '__iter__'): extension = set([extension]) else: if not extension: extension = None else: extension = set(extension) return extension @classmethod def postprocess(cls, options): if 'extension' in options and options['extension'] is not True: options['domain'] = sympy.polys.domains.QQ.algebraic_field( *options['extension']) class Modulus(with_metaclass(OptionType, Option)): """``modulus`` option to polynomial manipulation functions. """ option = 'modulus' requires = [] excludes = ['greedy', 'split', 'domain', 'gaussian', 'extension'] @classmethod def preprocess(cls, modulus): modulus = sympify(modulus) if modulus.is_Integer and modulus > 0: return int(modulus) else: raise OptionError( "'modulus' must a positive integer, got %s" % modulus) @classmethod def postprocess(cls, options): if 'modulus' in options: modulus = options['modulus'] symmetric = options.get('symmetric', True) options['domain'] = sympy.polys.domains.FF(modulus, symmetric) class Symmetric(with_metaclass(OptionType, BooleanOption)): """``symmetric`` option to polynomial manipulation functions. """ option = 'symmetric' requires = ['modulus'] excludes = ['greedy', 'domain', 'split', 'gaussian', 'extension'] class Strict(with_metaclass(OptionType, BooleanOption)): """``strict`` option to polynomial manipulation functions. """ option = 'strict' @classmethod def default(cls): return True class Auto(with_metaclass(OptionType, BooleanOption, Flag)): """``auto`` flag to polynomial manipulation functions. """ option = 'auto' after = ['field', 'domain', 'extension', 'gaussian'] @classmethod def default(cls): return True @classmethod def postprocess(cls, options): if ('domain' in options or 'field' in options) and 'auto' not in options: options['auto'] = False class Frac(with_metaclass(OptionType, BooleanOption, Flag)): """``auto`` option to polynomial manipulation functions. """ option = 'frac' @classmethod def default(cls): return False class Formal(with_metaclass(OptionType, BooleanOption, Flag)): """``formal`` flag to polynomial manipulation functions. """ option = 'formal' @classmethod def default(cls): return False class Polys(with_metaclass(OptionType, BooleanOption, Flag)): """``polys`` flag to polynomial manipulation functions. """ option = 'polys' class Include(with_metaclass(OptionType, BooleanOption, Flag)): """``include`` flag to polynomial manipulation functions. """ option = 'include' @classmethod def default(cls): return False class All(with_metaclass(OptionType, BooleanOption, Flag)): """``all`` flag to polynomial manipulation functions. """ option = 'all' @classmethod def default(cls): return False class Gen(with_metaclass(OptionType, Flag)): """``gen`` flag to polynomial manipulation functions. """ option = 'gen' @classmethod def default(cls): return 0 @classmethod def preprocess(cls, gen): if isinstance(gen, (Basic, int)): return gen else: raise OptionError("invalid argument for 'gen' option") class Symbols(with_metaclass(OptionType, Flag)): """``symbols`` flag to polynomial manipulation functions. """ option = 'symbols' @classmethod def default(cls): return numbered_symbols('s', start=1) @classmethod def preprocess(cls, symbols): if hasattr(symbols, '__iter__'): return iter(symbols) else: raise OptionError("expected an iterator or iterable container, got %s" % symbols) class Method(with_metaclass(OptionType, Flag)): """``method`` flag to polynomial manipulation functions. """ option = 'method' @classmethod def preprocess(cls, method): if isinstance(method, str): return method.lower() else: raise OptionError("expected a string, got %s" % method) def build_options(gens, args=None): """Construct options from keyword arguments or ... options. """ if args is None: gens, args = (), gens if len(args) != 1 or 'opt' not in args or gens: return Options(gens, args) else: return args['opt'] def allowed_flags(args, flags): """ Allow specified flags to be used in the given context. Examples ======== >>> from sympy.polys.polyoptions import allowed_flags >>> from sympy.polys.domains import ZZ >>> allowed_flags({'domain': ZZ}, []) >>> allowed_flags({'domain': ZZ, 'frac': True}, []) Traceback (most recent call last): ... FlagError: 'frac' flag is not allowed in this context >>> allowed_flags({'domain': ZZ, 'frac': True}, ['frac']) """ flags = set(flags) for arg in args.keys(): try: if Options.__options__[arg].is_Flag and not arg in flags: raise FlagError( "'%s' flag is not allowed in this context" % arg) except KeyError: raise OptionError("'%s' is not a valid option" % arg) def set_defaults(options, **defaults): """Update options with default values. """ if 'defaults' not in options: options = dict(options) options['defaults'] = defaults return options Options._init_dependencies_order() sympy-0.7.4.1/sympy/polys/solvers.py0000644000175000017500000000240112253362407017637 0ustar georgeskgeorgesk"""Low-level linear systems solver. """ from __future__ import print_function, division from sympy.matrices import Matrix, zeros class RawMatrix(Matrix): _sympify = staticmethod(lambda x: x) def eqs_to_matrix(eqs, ring): """Transform from equations to matrix form. """ xs = ring.gens M = zeros(len(eqs), len(xs)+1, cls=RawMatrix) for j, e_j in enumerate(eqs): for i, x_i in enumerate(xs): M[j, i] = e_j.coeff(x_i) M[j, -1] = -e_j.coeff(1) return M def solve_lin_sys(eqs, ring): """Solve a system of linear equations. """ assert ring.domain.has_Field # transform from equations to matrix form matrix = eqs_to_matrix(eqs, ring) # solve by row-reduction echelon, pivots = matrix.rref(iszerofunc=lambda x: not x, simplify=lambda x: x) # construct the returnable form of the solutions xs = ring.gens if pivots[-1] == len(xs): return None elif len(pivots) == len(xs): sol = [ ring.ground_new(s) for s in echelon[:, -1] ] return dict(zip(xs, sol)) else: sols = {} for i, p in enumerate(pivots): vect = RawMatrix([ [-x] for x in xs[p+1:] ] + [[ring.one]]) sols[xs[p]] = (echelon[i, p+1:]*vect)[0] return sols sympy-0.7.4.1/sympy/polys/densetools.py0000644000175000017500000006245112253362407020334 0ustar georgeskgeorgesk"""Advanced tools for dense recursive polynomials in ``K[x]`` or ``K[X]``. """ from __future__ import print_function, division 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 variations from math import ceil as _ceil, log as _log from sympy.core.compatibility import xrange def dup_integrate(f, m, K): """ Computes the indefinite integral of ``f`` in ``K[x]``. Examples ======== >>> from sympy.polys import ring, QQ >>> R, x = ring("x", QQ) >>> R.dup_integrate(x**2 + 2*x, 1) 1/3*x**3 + x**2 >>> R.dup_integrate(x**2 + 2*x, 2) 1/12*x**4 + 1/3*x**3 """ 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 xrange(1, m): n *= i + j + 1 g.insert(0, K.exquo(c, K(n))) return g def dmp_integrate(f, m, u, K): """ Computes the indefinite integral of ``f`` in ``x_0`` in ``K[X]``. Examples ======== >>> from sympy.polys import ring, QQ >>> R, x,y = ring("x,y", QQ) >>> R.dmp_integrate(x + 2*y, 1) 1/2*x**2 + 2*x*y >>> R.dmp_integrate(x + 2*y, 2) 1/6*x**3 + x**2*y """ 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 xrange(1, m): n *= i + j + 1 g.insert(0, dmp_quo_ground(c, K(n), v, K)) return g 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) 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 import ring, QQ >>> R, x,y = ring("x,y", QQ) >>> R.dmp_integrate_in(x + 2*y, 1, 0) 1/2*x**2 + 2*x*y >>> R.dmp_integrate_in(x + 2*y, 1, 1) x*y + y**2 """ 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) def dup_diff(f, m, K): """ ``m``-th order derivative of a polynomial in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_diff(x**3 + 2*x**2 + 3*x + 4, 1) 3*x**2 + 4*x + 3 >>> R.dup_diff(x**3 + 2*x**2 + 3*x + 4, 2) 6*x + 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 xrange(n - 1, n - m, -1): k *= i deriv.append(K(k)*coeff) n -= 1 return dup_strip(deriv) def dmp_diff(f, m, u, K): """ ``m``-th order derivative in ``x_0`` of a polynomial in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> f = x*y**2 + 2*x*y + 3*x + 2*y**2 + 3*y + 1 >>> R.dmp_diff(f, 1) y**2 + 2*y + 3 >>> R.dmp_diff(f, 2) 0 """ 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 xrange(n - 1, n - m, -1): k *= i deriv.append(dmp_mul_ground(coeff, K(k), v, K)) n -= 1 return dmp_strip(deriv, u) 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) 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 import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> f = x*y**2 + 2*x*y + 3*x + 2*y**2 + 3*y + 1 >>> R.dmp_diff_in(f, 1, 0) y**2 + 2*y + 3 >>> R.dmp_diff_in(f, 1, 1) 2*x*y + 2*x + 4*y + 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_eval(x**2 + 2*x + 3, 2) 11 """ if not a: return dup_TC(f, K) result = K.zero for c in f: result *= a result += c return result 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 import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_eval(2*x*y + 3*x + y + 2, 2) 5*y + 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 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) 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 import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> f = 2*x*y + 3*x + y + 2 >>> R.dmp_eval_in(f, 2, 0) 5*y + 8 >>> R.dmp_eval_in(f, 2, 1) 7*x + 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) 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) def dmp_eval_tail(f, A, u, K): """ Evaluate a polynomial at ``x_j = a_j, ...`` in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> f = 2*x*y + 3*x + y + 2 >>> R.dmp_eval_tail(f, [2]) 7*x + 4 >>> R.dmp_eval_tail(f, [2, 2]) 18 """ 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)) 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) 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 import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> f = x*y**2 + 2*x*y + 3*x + 2*y**2 + 3*y + 1 >>> R.dmp_diff_eval_in(f, 1, 2, 0) y**2 + 2*y + 3 >>> R.dmp_diff_eval_in(f, 1, 2, 1) 6*x + 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_trunc(2*x**3 + 3*x**2 + 5*x + 7, ZZ(3)) -x**3 - x + 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) def dmp_trunc(f, p, u, K): """ Reduce a ``K[X]`` polynomial modulo a polynomial ``p`` in ``K[Y]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> f = 3*x**2*y + 8*x**2 + 5*x*y + 6*x + 2*y + 3 >>> g = (y - 1).drop(x) >>> R.dmp_trunc(f, g) 11*x**2 + 11*x + 5 """ return dmp_strip([ dmp_rem(c, p, u - 1, K) for c in f ], u) def dmp_ground_trunc(f, p, u, K): """ Reduce a ``K[X]`` polynomial modulo a constant ``p`` in ``K``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> f = 3*x**2*y + 8*x**2 + 5*x*y + 6*x + 2*y + 3 >>> R.dmp_ground_trunc(f, ZZ(3)) -x**2 - x*y - y """ 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) def dup_monic(f, K): """ Divide all coefficients by ``LC(f)`` in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ, QQ >>> R, x = ring("x", ZZ) >>> R.dup_monic(3*x**2 + 6*x + 9) x**2 + 2*x + 3 >>> R, x = ring("x", QQ) >>> R.dup_monic(3*x**2 + 4*x + 2) x**2 + 4/3*x + 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) def dmp_ground_monic(f, u, K): """ Divide all coefficients by ``LC(f)`` in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ, QQ >>> R, x,y = ring("x,y", ZZ) >>> f = 3*x**2*y + 6*x**2 + 3*x*y + 9*y + 3 >>> R.dmp_ground_monic(f) x**2*y + 2*x**2 + x*y + 3*y + 1 >>> R, x,y = ring("x,y", QQ) >>> f = 3*x**2*y + 8*x**2 + 5*x*y + 6*x + 2*y + 3 >>> R.dmp_ground_monic(f) x**2*y + 8/3*x**2 + 5/3*x*y + 2*x + 2/3*y + 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) def dup_content(f, K): """ Compute the GCD of coefficients of ``f`` in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ, QQ >>> R, x = ring("x", ZZ) >>> f = 6*x**2 + 8*x + 12 >>> R.dup_content(f) 2 >>> R, x = ring("x", QQ) >>> f = 6*x**2 + 8*x + 12 >>> R.dup_content(f) 2 """ 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 def dmp_ground_content(f, u, K): """ Compute the GCD of coefficients of ``f`` in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ, QQ >>> R, x,y = ring("x,y", ZZ) >>> f = 2*x*y + 6*x + 4*y + 12 >>> R.dmp_ground_content(f) 2 >>> R, x,y = ring("x,y", QQ) >>> f = 2*x*y + 6*x + 4*y + 12 >>> R.dmp_ground_content(f) 2 """ 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 def dup_primitive(f, K): """ Compute content and the primitive form of ``f`` in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ, QQ >>> R, x = ring("x", ZZ) >>> f = 6*x**2 + 8*x + 12 >>> R.dup_primitive(f) (2, 3*x**2 + 4*x + 6) >>> R, x = ring("x", QQ) >>> f = 6*x**2 + 8*x + 12 >>> R.dup_primitive(f) (2, 3*x**2 + 4*x + 6) """ 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) def dmp_ground_primitive(f, u, K): """ Compute content and the primitive form of ``f`` in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ, QQ >>> R, x,y = ring("x,y", ZZ) >>> f = 2*x*y + 6*x + 4*y + 12 >>> R.dmp_ground_primitive(f) (2, x*y + 3*x + 2*y + 6) >>> R, x,y = ring("x,y", QQ) >>> f = 2*x*y + 6*x + 4*y + 12 >>> R.dmp_ground_primitive(f) (2, x*y + 3*x + 2*y + 6) """ 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) def dup_extract(f, g, K): """ Extract common content from a pair of polynomials in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_extract(6*x**2 + 12*x + 18, 4*x**2 + 8*x + 12) (2, 3*x**2 + 6*x + 9, 2*x**2 + 4*x + 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 def dmp_ground_extract(f, g, u, K): """ Extract common content from a pair of polynomials in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_ground_extract(6*x*y + 12*x + 18, 4*x*y + 8*x + 12) (2, 3*x*y + 6*x + 9, 2*x*y + 4*x + 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 def dup_real_imag(f, K): """ Return bivariate polynomials ``f1`` and ``f2``, such that ``f = f1 + f2*I``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dup_real_imag(x**3 + x**2 + x + 1) (x**3 + x**2 - 3*x*y**2 + x - y**2 + 1, 3*x**2*y + 2*x*y - y**3 + y) """ 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 def dup_mirror(f, K): """ Evaluate efficiently the composition ``f(-x)`` in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_mirror(x**3 + 2*x**2 - 4*x + 2) -x**3 + 2*x**2 + 4*x + 2 """ f, n, a = list(f), len(f) - 1, -K.one for i in xrange(n - 1, -1, -1): f[i], a = a*f[i], -a return f def dup_scale(f, a, K): """ Evaluate efficiently composition ``f(a*x)`` in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_scale(x**2 - 2*x + 1, ZZ(2)) 4*x**2 - 4*x + 1 """ f, n, b = list(f), len(f) - 1, a for i in xrange(n - 1, -1, -1): f[i], b = b*f[i], b*a return f def dup_shift(f, a, K): """ Evaluate efficiently Taylor shift ``f(x + a)`` in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_shift(x**2 - 2*x + 1, ZZ(2)) x**2 + 2*x + 1 """ f, n = list(f), len(f) - 1 for i in xrange(n, 0, -1): for j in xrange(0, i): f[j + 1] += a*f[j] return f def dup_transform(f, p, q, K): """ Evaluate functional transformation ``q**n * f(p/q)`` in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_transform(x**2 - 2*x + 1, x**2 + 1, x - 1) x**4 - 2*x**3 + 5*x**2 - 4*x + 4 """ if not f: return [] n = len(f) - 1 h, Q = [f[0]], [[K.one]] for i in xrange(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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_compose(x**2 + x, x - 1) x**2 - x """ 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 def dmp_compose(f, g, u, K): """ Evaluate functional composition ``f(g)`` in ``K[X]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_compose(x*y + 2*x + y, y) y**2 + 3*y """ 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 def _dup_right_decompose(f, s, K): """Helper function for :func:`_dup_decompose`.""" n = len(f) - 1 lc = dup_LC(f, K) f = dup_to_raw_dict(f) g = { s: K.one } r = n // s for i in xrange(1, s): coeff = K.zero for j in xrange(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) 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) def _dup_decompose(f, K): """Helper function for :func:`dup_decompose`.""" df = len(f) - 1 for s in xrange(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 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_decompose(x**4 - 2*x**3 + x**2) [x**2, x**2 - x] 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 def dmp_lift(f, u, K): """ Convert algebraic coefficients to integers in ``K[X]``. Examples ======== >>> from sympy.polys import ring, QQ >>> from sympy import I >>> K = QQ.algebraic_field(I) >>> R, x = ring("x", K) >>> f = x**2 + K([QQ(1), QQ(0)])*x + K([QQ(2), QQ(0)]) >>> R.dmp_lift(f) x**8 + 2*x**6 + 9*x**4 - 8*x**2 + 16 """ 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) def dup_sign_variations(f, K): """ Compute the number of sign variations of ``f`` in ``K[x]``. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_sign_variations(x**4 - x**2 - x + 1) 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 import ring, QQ >>> R, x = ring("x", QQ) >>> f = QQ(1,2)*x + QQ(1,3) >>> R.dup_clear_denoms(f, convert=False) (6, 3*x + 2) >>> R.dup_clear_denoms(f, convert=True) (6, 3*x + 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) 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 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 import ring, QQ >>> R, x,y = ring("x,y", QQ) >>> f = QQ(1,2)*x + QQ(1,3)*y + 1 >>> R.dmp_clear_denoms(f, convert=False) (6, 3*x + 2*y + 6) >>> R.dmp_clear_denoms(f, convert=True) (6, 3*x + 2*y + 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) 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 import ring, QQ >>> R, x = ring("x", QQ) >>> f = -QQ(1,720)*x**6 + QQ(1,24)*x**4 - QQ(1,2)*x**2 + 1 >>> R.dup_revert(f, 8) 61/720*x**6 + 5/24*x**4 + 1/2*x**2 + 1 """ g = [K.revert(dup_TC(f, K))] h = [K.one, K.zero, K.zero] N = int(_ceil(_log(n, 2))) for i in xrange(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 def dmp_revert(f, g, u, K): """ Compute ``f**(-1)`` mod ``x**n`` using Newton iteration. Examples ======== >>> from sympy.polys import ring, QQ >>> R, x,y = ring("x,y", QQ) """ if not u: return dup_revert(f, g, K) else: raise MultivariatePolynomialError(f, g) sympy-0.7.4.1/sympy/polys/rings.py0000644000175000017500000020153212253362407017272 0ustar georgeskgeorgesk"""Sparse polynomial rings. """ from __future__ import print_function, division from operator import add, mul, lt, le, gt, ge from types import GeneratorType from sympy.core.expr import Expr from sympy.core.symbol import Symbol, symbols as _symbols from sympy.core.numbers import igcd, oo from sympy.core.sympify import CantSympify, sympify from sympy.core.compatibility import is_sequence, reduce, string_types, xrange from sympy.ntheory.multinomial import multinomial_coefficients from sympy.polys.monomials import MonomialOps from sympy.polys.orderings import lex from sympy.polys.heuristicgcd import heugcd from sympy.polys.compatibility import IPolys from sympy.polys.polyutils import expr_from_dict, _dict_reorder, _parallel_dict_from_expr from sympy.polys.polyerrors import CoercionFailed, GeneratorsError, GeneratorsNeeded, ExactQuotientFailed, MultivariatePolynomialError from sympy.polys.domains.domainelement import DomainElement from sympy.polys.domains.polynomialring import PolynomialRing from sympy.polys.polyoptions import Domain as DomainOpt, Order as OrderOpt, build_options from sympy.polys.densebasic import dmp_to_dict, dmp_from_dict from sympy.polys.constructor import construct_domain from sympy.printing.defaults import DefaultPrinting from sympy.utilities import public from sympy.utilities.magic import pollute @public def ring(symbols, domain, order=lex): """Construct a polynomial ring returning ``(ring, x_1, ..., x_n)``. Parameters ---------- symbols : str, Symbol/Expr or sequence of str, Symbol/Expr (non-empty) domain : :class:`Domain` or coercible order : :class:`Order` or coercible, optional, defaults to ``lex`` Examples -------- >>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ >>> from sympy.polys.orderings import lex >>> R, x, y, z = ring("x,y,z", ZZ, lex) >>> R Polynomial ring in x, y, z over ZZ with lex order >>> x + y + z x + y + z >>> type(_) """ _ring = PolyRing(symbols, domain, order) return (_ring,) + _ring.gens @public def xring(symbols, domain, order=lex): """Construct a polynomial ring returning ``(ring, (x_1, ..., x_n))``. Parameters ---------- symbols : str, Symbol/Expr or sequence of str, Symbol/Expr (non-empty) domain : :class:`Domain` or coercible order : :class:`Order` or coercible, optional, defaults to ``lex`` Examples -------- >>> from sympy.polys.rings import xring >>> from sympy.polys.domains import ZZ >>> from sympy.polys.orderings import lex >>> R, (x, y, z) = xring("x,y,z", ZZ, lex) >>> R Polynomial ring in x, y, z over ZZ with lex order >>> x + y + z x + y + z >>> type(_) """ _ring = PolyRing(symbols, domain, order) return (_ring, _ring.gens) @public def vring(symbols, domain, order=lex): """Construct a polynomial ring and inject ``x_1, ..., x_n`` into the global namespace. Parameters ---------- symbols : str, Symbol/Expr or sequence of str, Symbol/Expr (non-empty) domain : :class:`Domain` or coercible order : :class:`Order` or coercible, optional, defaults to ``lex`` Examples -------- >>> from sympy.polys.rings import vring >>> from sympy.polys.domains import ZZ >>> from sympy.polys.orderings import lex >>> vring("x,y,z", ZZ, lex) Polynomial ring in x, y, z over ZZ with lex order >>> x + y + z x + y + z >>> type(_) """ _ring = PolyRing(symbols, domain, order) pollute([ sym.name for sym in _ring.symbols ], _ring.gens) return _ring @public def sring(exprs, *symbols, **options): """Construct a ring deriving generators and domain from options and input expressions. Parameters ---------- exprs : :class:`Expr` or sequence of :class:`Expr` (sympifiable) symbols : sequence of :class:`Symbol`/:class:`Expr` options : keyword arguments understood by :class:`Options` Examples -------- >>> from sympy.core import symbols >>> from sympy.polys.rings import sring >>> from sympy.polys.domains import ZZ >>> from sympy.polys.orderings import lex >>> x, y, z = symbols("x,y,z") >>> R, f = sring(x + 2*y + 3*z) >>> R Polynomial ring in x, y, z over ZZ with lex order >>> f x + 2*y + 3*z >>> type(_) """ single = False if not is_sequence(exprs): exprs, single = [exprs], True exprs = list(map(sympify, exprs)) opt = build_options(symbols, options) # TODO: rewrite this so that it doesn't use expand() (see poly()). reps, opt = _parallel_dict_from_expr(exprs, opt) if opt.domain is None: # NOTE: this is inefficient because construct_domain() automatically # performs conversion to the target domain. It shouldn't do this. coeffs = sum([ list(rep.values()) for rep in reps ], []) opt.domain, _ = construct_domain(coeffs, opt=opt) _ring = PolyRing(opt.gens, opt.domain, opt.order) polys = list(map(_ring.from_dict, reps)) if single: return (_ring, polys[0]) else: return (_ring, polys) def _parse_symbols(symbols): if not symbols: raise GeneratorsNeeded("generators weren't specified") if isinstance(symbols, string_types): return _symbols(symbols, seq=True) elif isinstance(symbols, Expr): return (symbols,) elif is_sequence(symbols): if all(isinstance(s, string_types) for s in symbols): return _symbols(symbols) elif all(isinstance(s, Expr) for s in symbols): return symbols raise GeneratorsError("expected a string, Symbol or expression or a non-empty sequence of strings, Symbols or expressions") _ring_cache = {} class PolyRing(DefaultPrinting, IPolys): """Multivariate distributed polynomial ring. """ def __new__(cls, symbols, domain, order=lex): symbols = tuple(_parse_symbols(symbols)) ngens = len(symbols) domain = DomainOpt.preprocess(domain) order = OrderOpt.preprocess(order) _hash = hash((cls.__name__, symbols, ngens, domain, order)) obj = _ring_cache.get(_hash) if obj is None: if domain.is_Composite and set(symbols) & set(domain.symbols): raise GeneratorsError("polynomial ring and it's ground domain share generators") obj = object.__new__(cls) obj._hash = _hash obj.dtype = type("PolyElement", (PolyElement,), {"ring": obj}) obj.symbols = symbols obj.ngens = ngens obj.domain = domain obj.order = order obj.zero_monom = (0,)*ngens obj.gens = obj._gens() obj._gens_set = set(obj.gens) obj._one = [(obj.zero_monom, domain.one)] codegen = MonomialOps(ngens) obj.monomial_mul = codegen.mul() obj.monomial_pow = codegen.pow() obj.monomial_mulpow = codegen.mulpow() obj.monomial_ldiv = codegen.ldiv() obj.monomial_div = codegen.div() obj.monomial_lcm = codegen.lcm() obj.monomial_gcd = codegen.gcd() if order is lex: obj.leading_expv = lambda f: max(f) else: obj.leading_expv = lambda f: max(f, key=order) for symbol, generator in zip(obj.symbols, obj.gens): if isinstance(symbol, Symbol): name = symbol.name if not hasattr(obj, name): setattr(obj, name, generator) _ring_cache[_hash] = obj return obj def _gens(self): """Return a list of polynomial generators. """ one = self.domain.one _gens = [] for i in xrange(self.ngens): expv = self.monomial_basis(i) poly = self.zero poly[expv] = one _gens.append(poly) return tuple(_gens) def __getnewargs__(self): return (self.symbols, self.domain, self.order) def __getstate__(self): state = self.__dict__.copy() del state["leading_expv"] for key, value in state.items(): if key.startswith("monomial_"): del state[key] return state def __hash__(self): return self._hash def __eq__(self, other): return self is other def __ne__(self, other): return self is not other def clone(self, symbols=None, domain=None, order=None): return self.__class__(symbols or self.symbols, domain or self.domain, order or self.order) def monomial_basis(self, i): """Return the ith-basis element. """ basis = [0]*self.ngens basis[i] = 1 return tuple(basis) @property def zero(self): return self.dtype() @property def one(self): return self.dtype(self._one) def domain_new(self, element, orig_domain=None): return self.domain.convert(element, orig_domain) def ground_new(self, coeff): return self.term_new(self.zero_monom, coeff) def term_new(self, monom, coeff): coeff = self.domain_new(coeff) poly = self.zero if coeff: poly[monom] = coeff return poly def ring_new(self, element): if isinstance(element, PolyElement): if self == element.ring: return element elif isinstance(self.domain, PolynomialRing) and self.domain.ring == element.ring: return self.ground_new(element) else: raise NotImplementedError("conversion") elif isinstance(element, string_types): raise NotImplementedError("parsing") elif isinstance(element, dict): return self.from_dict(element) elif isinstance(element, list): try: return self.from_terms(element) except ValueError: return self.from_list(element) elif isinstance(element, Expr): return self.from_expr(element) else: return self.ground_new(element) __call__ = ring_new def from_dict(self, element): domain_new = self.domain_new poly = self.zero for monom, coeff in element.items(): coeff = domain_new(coeff) if coeff: poly[monom] = coeff return poly def from_terms(self, element): return self.from_dict(dict(element)) def from_list(self, element): return self.from_dict(dmp_to_dict(element, self.ngens-1, self.domain)) def _rebuild_expr(self, expr, mapping): domain = self.domain def _rebuild(expr): generator = mapping.get(expr) if generator is not None: return generator elif expr.is_Add: return reduce(add, list(map(_rebuild, expr.args))) elif expr.is_Mul: return reduce(mul, list(map(_rebuild, expr.args))) elif expr.is_Pow and expr.exp.is_Integer and expr.exp >= 0: return _rebuild(expr.base)**int(expr.exp) else: return domain.convert(expr) return _rebuild(sympify(expr)) def from_expr(self, expr): mapping = dict(list(zip(self.symbols, self.gens))) try: poly = self._rebuild_expr(expr, mapping) except CoercionFailed: raise ValueError("expected an expression convertible to a polynomial in %s, got %s" % (self, expr)) else: return self.ring_new(poly) def index(self, gen): """Compute index of ``gen`` in ``self.gens``. """ if gen is None: i = 0 elif isinstance(gen, int): i = gen if 0 <= i and i < self.ngens: pass elif -self.ngens <= i and i <= -1: i = -i - 1 else: raise ValueError("invalid generator index: %s" % gen) elif isinstance(gen, self.dtype): try: i = self.gens.index(gen) except ValueError: raise ValueError("invalid generator: %s" % gen) elif isinstance(gen, string_types): try: i = self.symbols.index(gen) except ValueError: raise ValueError("invalid generator: %s" % gen) else: raise ValueError("expected a polynomial generator, an integer, a string or None, got %s" % gen) return i def drop(self, *gens): """Remove specified generators from this ring. """ indices = set(map(self.index, gens)) symbols = [ s for i, s in enumerate(self.symbols) if i not in indices ] if not symbols: return self.domain else: return self.clone(symbols=symbols) def __getitem__(self, key): symbols = self.symbols[key] if not symbols: return self.domain else: return self.clone(symbols=symbols) def to_ground(self): # TODO: should AlgebraicField be a Composite domain? if self.domain.is_Composite or hasattr(self.domain, 'domain'): return self.clone(domain=self.domain.domain) else: raise ValueError("%s is not a composite domain" % self.domain) def to_domain(self): return PolynomialRing(self) def to_field(self): from sympy.polys.fields import FracField return FracField(self.symbols, self.domain, self.order) @property def is_univariate(self): return len(self.gens) == 1 @property def is_multivariate(self): return len(self.gens) > 1 def add(self, *objs): """ Add a sequence of polynomials or containers of polynomials. Example ------- >>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ >>> R, x = ring("x", ZZ) >>> R.add([ x**2 + 2*i + 3 for i in range(4) ]) 4*x**2 + 24 >>> _.factor_list() (4, [(x**2 + 6, 1)]) """ p = self.zero for obj in objs: if is_sequence(obj, include=GeneratorType): p += self.add(*obj) else: p += obj return p def mul(self, *objs): """ Multiply a sequence of polynomials or containers of polynomials. Example ------- >>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ >>> R, x = ring("x", ZZ) >>> R.mul([ x**2 + 2*i + 3 for i in range(4) ]) x**8 + 24*x**6 + 206*x**4 + 744*x**2 + 945 >>> _.factor_list() (1, [(x**2 + 3, 1), (x**2 + 5, 1), (x**2 + 7, 1), (x**2 + 9, 1)]) """ p = self.one for obj in objs: if is_sequence(obj, include=GeneratorType): p *= self.mul(*obj) else: p *= obj return p def drop_to_ground(self, *gens): r""" Remove specified generators from the ring and inject them into its domain. """ indices = set(map(self.index, gens)) symbols = [s for i, s in enumerate(self.symbols) if i not in indices] gens = [gen for i, gen in enumerate(self.gens) if i not in indices] if not symbols: return self else: return self.clone(symbols=symbols, domain=self.drop(*gens)) class PolyElement(DomainElement, DefaultPrinting, CantSympify, dict): """Element of multivariate distributed polynomial ring. """ def new(self, init): return self.__class__(init) def parent(self): return self.ring.to_domain() def __getnewargs__(self): return (self.ring, list(self.iterterms())) _hash = None def __hash__(self): # XXX: This computes a hash of a dictionary, but currently we don't # protect dictionary from being changed so any use site modifications # will make hashing go wrong. Use this feature with caution until we # figure out how to make a safe API without compromising speed of this # low-level class. _hash = self._hash if _hash is None: self._hash = _hash = hash((self.ring, frozenset(self.items()))) return _hash def copy(self): """Return a copy of polynomial self. Polynomials are mutable; if one is interested in preserving a polynomial, and one plans to use inplace operations, one can copy the polynomial. This method makes a shallow copy. Examples -------- >>> from sympy.polys.domains import ZZ >>> from sympy.polys.rings import ring >>> R, x, y = ring('x, y', ZZ) >>> p = (x + y)**2 >>> p1 = p.copy() >>> p2 = p >>> p[R.zero_monom] = 3 >>> p x**2 + 2*x*y + y**2 + 3 >>> p1 x**2 + 2*x*y + y**2 >>> p2 x**2 + 2*x*y + y**2 + 3 """ return self.new(self) def set_ring(self, new_ring): if self.ring == new_ring: return self elif self.ring.symbols != new_ring.symbols: terms = list(zip(*_dict_reorder(self, self.ring.symbols, new_ring.symbols))) return new_ring.from_terms(terms) else: return new_ring.from_dict(self) def as_expr(self, *symbols): if symbols and len(symbols) != self.ring.ngens: raise ValueError("not enough symbols, expected %s got %s" % (self.ring.ngens, len(symbols))) else: symbols = self.ring.symbols return expr_from_dict(self.as_expr_dict(), *symbols) def as_expr_dict(self): to_sympy = self.ring.domain.to_sympy return dict([ (monom, to_sympy(coeff)) for monom, coeff in self.iterterms() ]) def clear_denoms(self): domain = self.ring.domain if not domain.has_Field or not domain.has_assoc_Ring: return domain.one, self ground_ring = domain.get_ring() common = ground_ring.one lcm = ground_ring.lcm denom = domain.denom for coeff in self.values(): common = lcm(common, denom(coeff)) poly = self.new([ (k, v*common) for k, v in self.items() ]) return common, poly def strip_zero(self): """Eliminate monomials with zero coefficient. """ for k, v in list(self.items()): if not v: del self[k] def __eq__(p1, p2): """Equality test for polynomials. Examples -------- >>> from sympy.polys.domains import ZZ >>> from sympy.polys.rings import ring >>> _, x, y = ring('x, y', ZZ) >>> p1 = (x + y)**2 + (x - y)**2 >>> p1 == 4*x*y False >>> p1 == 2*(x**2 + y**2) True """ if not p2: return not p1 elif isinstance(p2, p1.ring.dtype): return dict.__eq__(p1, p2) elif len(p1) > 1: return False else: return p1.get(p1.ring.zero_monom) == p2 def __ne__(p1, p2): return not p1.__eq__(p2) def almosteq(p1, p2, tolerance=None): """Approximate equality test for polynomials. """ ring = p1.ring if isinstance(p2, ring.dtype): if list(p1.keys()) != list(p2.keys()): return False almosteq = ring.domain.almosteq for c1, c2 in zip(p1.itercoeffs(), p2.itercoeffs()): if not almosteq(c1, c2, tolerance): return False else: return True elif len(p1) > 1: return False else: try: p2 = ring.domain.convert(p2) except CoercionFailed: return False else: return ring.domain.almosteq(p1.const(), p2, tolerance) def sort_key(self): return (len(self), self.terms()) def _cmp(p1, p2, op): if isinstance(p2, p1.ring.dtype): return op(p1.sort_key(), p2.sort_key()) else: return NotImplemented def __lt__(p1, p2): return p1._cmp(p2, lt) def __le__(p1, p2): return p1._cmp(p2, le) def __gt__(p1, p2): return p1._cmp(p2, gt) def __ge__(p1, p2): return p1._cmp(p2, ge) def _drop(self, gen): ring = self.ring i = ring.index(gen) if ring.ngens == 1: return i, ring.domain else: symbols = list(ring.symbols) del symbols[i] return i, ring.clone(symbols=symbols) def drop(self, gen): i, ring = self._drop(gen) if self.ring.ngens == 1: if self.is_ground: return self.coeff(1) else: raise ValueError("can't drop %s" % gen) else: poly = ring.zero for k, v in self.items(): if k[i] == 0: K = list(k) del K[i] poly[tuple(K)] = v else: raise ValueError("can't drop %s" % gen) return poly def _drop_to_ground(self, gen): ring = self.ring i = ring.index(gen) symbols = list(ring.symbols) del symbols[i] return i, ring.clone(symbols=symbols, domain=ring[i]) def drop_to_ground(self, gen): if self.ring.ngens == 1: raise ValueError("can't drop only generator to ground") i, ring = self._drop_to_ground(gen) poly = ring.zero gen = ring.domain.gens[0] for monom, coeff in self.iterterms(): mon = monom[:i] + monom[i+1:] if not mon in poly: poly[mon] = (gen**monom[i]).mul_ground(coeff) else: poly[mon] += (gen**monom[i]).mul_ground(coeff) return poly def to_dense(self): return dmp_from_dict(self, self.ring.ngens-1, self.ring.domain) def to_dict(self): return dict(self) def str(self, printer, precedence, exp_pattern, mul_symbol): if not self: return printer._print(self.ring.domain.zero) prec_add = precedence["Add"] prec_atom = precedence["Atom"] ring = self.ring symbols = ring.symbols ngens = ring.ngens zm = ring.zero_monom sexpvs = [] for expv, coeff in self.terms(): positive = ring.domain.is_positive(coeff) sign = " + " if positive else " - " sexpvs.append(sign) if expv == zm: scoeff = printer._print(coeff) if scoeff.startswith("-"): scoeff = scoeff[1:] else: if not positive: coeff = -coeff if coeff != 1: scoeff = printer.parenthesize(coeff, prec_add) else: scoeff = '' sexpv = [] for i in xrange(ngens): exp = expv[i] if not exp: continue symbol = printer.parenthesize(symbols[i], prec_atom-1) if exp != 1: sexpv.append(exp_pattern % (symbol, exp)) else: sexpv.append('%s' % symbol) if scoeff: sexpv = [scoeff] + sexpv sexpvs.append(mul_symbol.join(sexpv)) if sexpvs[0] in [" + ", " - "]: head = sexpvs.pop(0) if head == " - ": sexpvs.insert(0, "-") return "".join(sexpvs) @property def is_generator(self): return self in self.ring._gens_set @property def is_ground(self): return not self or (len(self) == 1 and self.ring.zero_monom in self) @property def is_monomial(self): return not self or (len(self) == 1 and self.LC == 1) @property def is_term(self): return len(self) <= 1 @property def is_negative(self): return self.ring.domain.is_negative(self.LC) @property def is_positive(self): return self.ring.domain.is_positive(self.LC) @property def is_nonnegative(self): return self.ring.domain.is_nonnegative(self.LC) @property def is_nonpositive(self): return self.ring.domain.is_nonpositive(self.LC) @property def is_zero(f): return not f @property def is_one(f): return f == f.ring.one @property def is_monic(f): return f.ring.domain.is_one(f.LC) @property def is_primitive(f): return f.ring.domain.is_one(f.content()) @property def is_linear(f): return all(sum(monom) <= 1 for monom in f.itermonoms()) @property def is_quadratic(f): return all(sum(monom) <= 2 for monom in f.itermonoms()) @property def is_squarefree(f): return f.ring.dmp_sqf_p(f) @property def is_irreducible(f): return f.ring.dmp_irreducible_p(f) @property def is_cyclotomic(f): if f.ring.is_univariate: return f.ring.dup_cyclotomic_p(f) else: raise MultivariatePolynomialError("cyclotomic polynomial") def __neg__(self): return self.new([ (monom, -coeff) for monom, coeff in self.iterterms() ]) def __pos__(self): return self def __add__(p1, p2): """Add two polynomials. Examples -------- >>> from sympy.polys.domains import ZZ >>> from sympy.polys.rings import ring >>> _, x, y = ring('x, y', ZZ) >>> (x + y)**2 + (x - y)**2 2*x**2 + 2*y**2 """ if not p2: return p1.copy() ring = p1.ring if isinstance(p2, ring.dtype): p = p1.copy() get = p.get zero = ring.domain.zero for k, v in p2.items(): v = get(k, zero) + v if v: p[k] = v else: del p[k] return p elif isinstance(p2, PolyElement): if isinstance(ring.domain, PolynomialRing) and ring.domain.ring == p2.ring: pass elif isinstance(p2.ring.domain, PolynomialRing) and p2.ring.domain.ring == ring: return p2.__radd__(p1) else: return NotImplemented try: cp2 = ring.domain_new(p2) except CoercionFailed: return NotImplemented else: p = p1.copy() if not cp2: return p zm = ring.zero_monom if zm not in p1.keys(): p[zm] = cp2 else: if p2 == -p[zm]: del p[zm] else: p[zm] += cp2 return p def __radd__(p1, n): p = p1.copy() if not n: return p ring = p1.ring try: n = ring.domain_new(n) except CoercionFailed: return NotImplemented else: zm = ring.zero_monom if zm not in p1.keys(): p[zm] = n else: if n == -p[zm]: del p[zm] else: p[zm] += n return p def __sub__(p1, p2): """Subtract polynomial p2 from p1. Examples -------- >>> from sympy.polys.domains import ZZ >>> from sympy.polys.rings import ring >>> _, x, y = ring('x, y', ZZ) >>> p1 = x + y**2 >>> p2 = x*y + y**2 >>> p1 - p2 -x*y + x """ if not p2: return p1.copy() ring = p1.ring if isinstance(p2, ring.dtype): p = p1.copy() get = p.get zero = ring.domain.zero for k, v in p2.items(): v = get(k, zero) - v if v: p[k] = v else: del p[k] return p elif isinstance(p2, PolyElement): if isinstance(ring.domain, PolynomialRing) and ring.domain.ring == p2.ring: pass elif isinstance(p2.ring.domain, PolynomialRing) and p2.ring.domain.ring == ring: return p2.__rsub__(p1) else: return NotImplemented try: p2 = ring.domain_new(p2) except CoercionFailed: return NotImplemented else: p = p1.copy() zm = ring.zero_monom if zm not in p1.keys(): p[zm] = -p2 else: if p2 == p[zm]: del p[zm] else: p[zm] -= p2 return p def __rsub__(p1, n): """n - p1 with n convertible to the coefficient domain. Examples -------- >>> from sympy.polys.domains import ZZ >>> from sympy.polys.rings import ring >>> _, x, y = ring('x, y', ZZ) >>> p = x + y >>> 4 - p -x - y + 4 """ ring = p1.ring try: n = ring.domain_new(n) except CoercionFailed: return NotImplemented else: p = ring.zero for expv in p1: p[expv] = -p1[expv] p += n return p def __mul__(p1, p2): """Multiply two polynomials. Examples -------- >>> from sympy.polys.domains import QQ >>> from sympy.polys.rings import ring >>> _, x, y = ring('x, y', QQ) >>> p1 = x + y >>> p2 = x - y >>> p1*p2 x**2 - y**2 """ ring = p1.ring p = ring.zero if not p1 or not p2: return p elif isinstance(p2, ring.dtype): get = p.get zero = ring.domain.zero monomial_mul = ring.monomial_mul p2it = list(p2.items()) for exp1, v1 in p1.items(): for exp2, v2 in p2it: exp = monomial_mul(exp1, exp2) p[exp] = get(exp, zero) + v1*v2 p.strip_zero() return p elif isinstance(p2, PolyElement): if isinstance(ring.domain, PolynomialRing) and ring.domain.ring == p2.ring: pass elif isinstance(p2.ring.domain, PolynomialRing) and p2.ring.domain.ring == ring: return p2.__rmul__(p1) else: return NotImplemented try: p2 = ring.domain_new(p2) except CoercionFailed: return NotImplemented else: for exp1, v1 in p1.items(): v = v1*p2 if v: p[exp1] = v return p def __rmul__(p1, p2): """p2 * p1 with p2 in the coefficient domain of p1. Examples -------- >>> from sympy.polys.domains import ZZ >>> from sympy.polys.rings import ring >>> _, x, y = ring('x, y', ZZ) >>> p = x + y >>> 4 * p 4*x + 4*y """ p = p1.ring.zero if not p2: return p try: p2 = p.ring.domain_new(p2) except CoercionFailed: return NotImplemented else: for exp1, v1 in p1.items(): v = p2*v1 if v: p[exp1] = v return p def __pow__(self, n): """raise polynomial to power `n` Examples -------- >>> from sympy.polys.domains import ZZ >>> from sympy.polys.rings import ring >>> _, x, y = ring('x, y', ZZ) >>> p = x + y**2 >>> p**3 x**3 + 3*x**2*y**2 + 3*x*y**4 + y**6 """ ring = self.ring n = int(n) if n < 0: raise ValueError("negative exponent") elif not n: if self: return ring.one else: raise ValueError("0**0") elif len(self) == 1: monom, coeff = list(self.items())[0] p = ring.zero p[ring.monomial_pow(monom, n)] = coeff**n return p elif n == 1: return self.copy() elif n == 2: return self.square() elif n == 3: return self*self.square() elif len(self) <= 5: # TODO: use an actuall density measure return self._pow_multinomial(n) else: return self._pow_generic(n) def _pow_generic(self, n): p = self.ring.one while True: if n & 1: p = p*self n -= 1 if not n: break self = self.square() n = n // 2 return p def _pow_multinomial(self, n): multinomials = list(multinomial_coefficients(len(self), n).items()) monomial_mulpow = self.ring.monomial_mulpow zero_monom = self.ring.zero_monom terms = list(self.iterterms()) zero = self.ring.domain.zero poly = self.ring.zero for multinomial, multinomial_coeff in multinomials: product_monom = zero_monom product_coeff = multinomial_coeff for exp, (monom, coeff) in zip(multinomial, terms): if exp: product_monom = monomial_mulpow(product_monom, monom, exp) product_coeff *= coeff**exp monom = tuple(product_monom) coeff = product_coeff coeff = poly.get(monom, zero) + coeff if coeff: poly[monom] = coeff else: del poly[monom] return poly def square(self): """square of a polynomial Examples -------- >>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ >>> _, x, y = ring('x, y', ZZ) >>> p = x + y**2 >>> p.square() x**2 + 2*x*y**2 + y**4 """ ring = self.ring p = ring.zero get = p.get keys = list(self.keys()) zero = ring.domain.zero monomial_mul = ring.monomial_mul for i in range(len(keys)): k1 = keys[i] pk = self[k1] for j in range(i): k2 = keys[j] exp = monomial_mul(k1, k2) p[exp] = get(exp, zero) + pk*self[k2] p = p.imul_num(2) get = p.get for k, v in self.items(): k2 = monomial_mul(k, k) p[k2] = get(k2, zero) + v**2 p.strip_zero() return p def __divmod__(p1, p2): ring = p1.ring p = ring.zero if not p2: raise ZeroDivisionError("polynomial division") elif isinstance(p2, ring.dtype): return p1.div(p2) elif isinstance(p2, PolyElement): if isinstance(ring.domain, PolynomialRing) and ring.domain.ring == p2.ring: pass elif isinstance(p2.ring.domain, PolynomialRing) and p2.ring.domain.ring == ring: return p2.__rdivmod__(p1) else: return NotImplemented try: p2 = ring.domain_new(p2) except CoercionFailed: return NotImplemented else: return (p1.quo_ground(p2), p1.rem_ground(p2)) def __rdivmod__(p1, p2): return NotImplemented def __mod__(p1, p2): ring = p1.ring p = ring.zero if not p2: raise ZeroDivisionError("polynomial division") elif isinstance(p2, ring.dtype): return p1.rem(p2) elif isinstance(p2, PolyElement): if isinstance(ring.domain, PolynomialRing) and ring.domain.ring == p2.ring: pass elif isinstance(p2.ring.domain, PolynomialRing) and p2.ring.domain.ring == ring: return p2.__rmod__(p1) else: return NotImplemented try: p2 = ring.domain_new(p2) except CoercionFailed: return NotImplemented else: return p1.rem_ground(p2) def __rmod__(p1, p2): return NotImplemented def __truediv__(p1, p2): ring = p1.ring p = ring.zero if not p2: raise ZeroDivisionError("polynomial division") elif isinstance(p2, ring.dtype): return p1.quo(p2) elif isinstance(p2, PolyElement): if isinstance(ring.domain, PolynomialRing) and ring.domain.ring == p2.ring: pass elif isinstance(p2.ring.domain, PolynomialRing) and p2.ring.domain.ring == ring: return p2.__rtruediv__(p1) else: return NotImplemented try: p2 = ring.domain_new(p2) except CoercionFailed: return NotImplemented else: return p1.quo_ground(p2) def __rtruediv__(p1, p2): return NotImplemented __floordiv__ = __div__ = __truediv__ __rfloordiv__ = __rdiv__ = __rtruediv__ # TODO: use // (__floordiv__) for exquo()? def _term_div(self): zm = self.ring.zero_monom domain = self.ring.domain domain_quo = domain.quo monomial_div = self.ring.monomial_div if domain.has_Field: def term_div(a_lm_a_lc, b_lm_b_lc): a_lm, a_lc = a_lm_a_lc b_lm, b_lc = b_lm_b_lc if b_lm == zm: # apparently this is a very common case monom = a_lm else: monom = monomial_div(a_lm, b_lm) if monom is not None: return monom, domain_quo(a_lc, b_lc) else: return None else: def term_div(a_lm_a_lc, b_lm_b_lc): a_lm, a_lc = a_lm_a_lc b_lm, b_lc = b_lm_b_lc if b_lm == zm: # apparently this is a very common case monom = a_lm else: monom = monomial_div(a_lm, b_lm) if not (monom is None or a_lc % b_lc): return monom, domain_quo(a_lc, b_lc) else: return None return term_div def div(self, fv): """Division algorithm, see [CLO] p64. fv array of polynomials return qv, r such that self = sum(fv[i]*qv[i]) + r All polynomials are required not to be Laurent polynomials. Examples -------- >>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ >>> _, x, y = ring('x, y', ZZ) >>> f = x**3 >>> f0 = x - y**2 >>> f1 = x - y >>> qv, r = f.div((f0, f1)) >>> qv[0] x**2 + x*y**2 + y**4 >>> qv[1] 0 >>> r y**6 """ ring = self.ring domain = ring.domain ret_single = False if isinstance(fv, PolyElement): ret_single = True fv = [fv] if any(not f for f in fv): raise ZeroDivisionError("polynomial division") if not self: if ret_single: return ring.zero, ring.zero else: return [], ring.zero for f in fv: if f.ring != ring: raise ValueError('self and f must have the same ring') s = len(fv) qv = [ring.zero for i in range(s)] p = self.copy() r = ring.zero term_div = self._term_div() expvs = [fx.leading_expv() for fx in fv] while p: i = 0 divoccurred = 0 while i < s and divoccurred == 0: expv = p.leading_expv() term = term_div((expv, p[expv]), (expvs[i], fv[i][expvs[i]])) if term is not None: expv1, c = term qv[i] = qv[i]._iadd_monom((expv1, c)) p = p._iadd_poly_monom(fv[i], (expv1, -c)) divoccurred = 1 else: i += 1 if not divoccurred: expv = p.leading_expv() r = r._iadd_monom((expv, p[expv])) del p[expv] if expv == ring.zero_monom: r += p if ret_single: if not qv: return ring.zero, r else: return qv[0], r else: return qv, r def rem(f, G): if isinstance(G, PolyElement): G = [G] if any(not g for g in G): raise ZeroDivisionError("polynomial division") ring = f.ring domain = ring.domain order = ring.order zero = domain.zero monomial_mul = ring.monomial_mul r = ring.zero term_div = f._term_div() ltf = f.LT f = f.copy() get = f.get while f: for g in G: tq = term_div(ltf, g.LT) if tq is not None: m, c = tq for mg, cg in g.iterterms(): m1 = monomial_mul(mg, m) c1 = get(m1, zero) - c*cg if not c1: del f[m1] else: f[m1] = c1 ltm = f.leading_expv() if ltm is not None: ltf = ltm, f[ltm] break else: ltm, ltc = ltf if ltm in r: r[ltm] += ltc else: r[ltm] = ltc del f[ltm] ltm = f.leading_expv() if ltm is not None: ltf = ltm, f[ltm] return r def quo(f, G): return f.div(G)[0] def exquo(f, G): q, r = f.div(G) if not r: return q else: raise ExactQuotientFailed(f, G) def _iadd_monom(self, mc): """add to self the monomial coeff*x0**i0*x1**i1*... unless self is a generator -- then just return the sum of the two. mc is a tuple, (monom, coeff), where monomial is (i0, i1, ...) Examples -------- >>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ >>> _, x, y = ring('x, y', ZZ) >>> p = x**4 + 2*y >>> m = (1, 2) >>> p1 = p._iadd_monom((m, 5)) >>> p1 x**4 + 5*x*y**2 + 2*y >>> p1 is p True >>> p = x >>> p1 = p._iadd_monom((m, 5)) >>> p1 5*x*y**2 + x >>> p1 is p False """ if self in self.ring._gens_set: self = self.copy() expv, coeff = mc c = self.get(expv) if c is None: self[expv] = coeff else: c += coeff if c: self[expv] = c else: del self[expv] return self def _iadd_poly_monom(p1, p2, mc): """add to self the product of (p)*(coeff*x0**i0*x1**i1*...) unless self is a generator -- then just return the sum of the two. mc is a tuple, (monom, coeff), where monomial is (i0, i1, ...) Examples -------- >>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ >>> _, x, y, z = ring('x, y, z', ZZ) >>> p1 = x**4 + 2*y >>> p2 = y + z >>> m = (1, 2, 3) >>> p1 = p1._iadd_poly_monom(p2, (m, 3)) >>> p1 x**4 + 3*x*y**3*z**3 + 3*x*y**2*z**4 + 2*y """ if p1 in p1.ring._gens_set: p1 = p1.copy() (m, c) = mc get = p1.get zero = p1.ring.domain.zero monomial_mul = p1.ring.monomial_mul for k, v in p2.items(): ka = monomial_mul(k, m) coeff = get(ka, zero) + v*c if coeff: p1[ka] = coeff else: del p1[ka] return p1 def degree(f, x=None): """ The leading degree in ``x`` or the main variable. Note that the degree of 0 is negative infinity (the SymPy object -oo). """ i = f.ring.index(x) if not f: return -oo else: return max([ monom[i] for monom in f.itermonoms() ]) def degrees(f): """ A tuple containing leading degrees in all variables. Note that the degree of 0 is negative infinity (the SymPy object -oo) """ if not f: return (-oo,)*f.ring.ngens else: return tuple(map(max, list(zip(*f.itermonoms())))) def tail_degree(f, x=None): """ The tail degree in ``x`` or the main variable. Note that the degree of 0 is negative infinity (the SymPy object -oo) """ i = f.ring.index(x) if not f: return -oo else: return min([ monom[i] for monom in f.itermonoms() ]) def tail_degrees(f): """ A tuple containing tail degrees in all variables. Note that the degree of 0 is negative infinity (the SymPy object -oo) """ if not f: return (-oo,)*f.ring.ngens else: return tuple(map(min, list(zip(*f.itermonoms())))) def leading_expv(self): """Leading monomial tuple according to the monomial ordering. Examples -------- >>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ >>> _, x, y, z = ring('x, y, z', ZZ) >>> p = x**4 + x**3*y + x**2*z**2 + z**7 >>> p.leading_expv() (4, 0, 0) """ if self: return self.ring.leading_expv(self) else: return None def _get_coeff(self, expv): return self.get(expv, self.ring.domain.zero) def coeff(self, element): """ Returns the coefficient that stands next to the given monomial. Parameters ---------- element : PolyElement (with ``is_monomial = True``) or 1 Examples -------- >>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ >>> _, x, y, z = ring("x,y,z", ZZ) >>> f = 3*x**2*y - x*y*z + 7*z**3 + 23 >>> f.coeff(x**2*y) 3 >>> f.coeff(x*y) 0 >>> f.coeff(1) 23 """ if element == 1: return self._get_coeff(self.ring.zero_monom) elif isinstance(element, self.ring.dtype): terms = list(element.iterterms()) if len(terms) == 1: monom, coeff = terms[0] if coeff == self.ring.domain.one: return self._get_coeff(monom) raise ValueError("expected a monomial, got %s" % element) def const(self): """Returns the constant coeffcient. """ return self._get_coeff(self.ring.zero_monom) @property def LC(self): return self._get_coeff(self.leading_expv()) @property def LM(self): expv = self.leading_expv() if expv is None: return self.ring.zero_monom else: return expv def leading_monom(self): """ Leading monomial as a polynomial element. Examples -------- >>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ >>> _, x, y = ring('x, y', ZZ) >>> (3*x*y + y**2).leading_monom() x*y """ p = self.ring.zero expv = self.leading_expv() if expv: p[expv] = self.ring.domain.one return p @property def LT(self): expv = self.leading_expv() if expv is None: return (self.ring.zero_monom, self.ring.domain.zero) else: return (expv, self._get_coeff(expv)) def leading_term(self): """Leading term as a polynomial element. Examples -------- >>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ >>> _, x, y = ring('x, y', ZZ) >>> (3*x*y + y**2).leading_term() 3*x*y """ p = self.ring.zero expv = self.leading_expv() if expv: p[expv] = self[expv] return p def _sorted(self, seq, order): if order is None: order = self.ring.order else: order = OrderOpt.preprocess(order) if order is lex: return sorted(seq, key=lambda monom: monom[0], reverse=True) else: return sorted(seq, key=lambda monom: order(monom[0]), reverse=True) def coeffs(self, order=None): """Ordered list of polynomial coefficients. Parameters ---------- order : :class:`Order` or coercible, optional Examples -------- >>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ >>> from sympy.polys.orderings import lex, grlex >>> _, x, y = ring("x, y", ZZ, lex) >>> f = x*y**7 + 2*x**2*y**3 >>> f.coeffs() [2, 1] >>> f.coeffs(grlex) [1, 2] """ return [ coeff for _, coeff in self.terms(order) ] def monoms(self, order=None): """Ordered list of polynomial monomials. Parameters ---------- order : :class:`Order` or coercible, optional Examples -------- >>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ >>> from sympy.polys.orderings import lex, grlex >>> _, x, y = ring("x, y", ZZ, lex) >>> f = x*y**7 + 2*x**2*y**3 >>> f.monoms() [(2, 3), (1, 7)] >>> f.monoms(grlex) [(1, 7), (2, 3)] """ return [ monom for monom, _ in self.terms(order) ] def terms(self, order=None): """Ordered list of polynomial terms. Parameters ---------- order : :class:`Order` or coercible, optional Examples -------- >>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ >>> from sympy.polys.orderings import lex, grlex >>> _, x, y = ring("x, y", ZZ, lex) >>> f = x*y**7 + 2*x**2*y**3 >>> f.terms() [((2, 3), 2), ((1, 7), 1)] >>> f.terms(grlex) [((1, 7), 1), ((2, 3), 2)] """ return self._sorted(list(self.items()), order) def itercoeffs(self): """Iterator over coefficients of a polynomial. """ return iter(self.values()) def itermonoms(self): """Iterator over monomials of a polynomial. """ return iter(self.keys()) def iterterms(self): """Iterator over terms of a polynomial. """ return iter(self.items()) def listcoeffs(self): """Unordered list of polynomial coefficients. """ return list(self.values()) def listmonoms(self): """Unordered list of polynomial monomials. """ return list(self.keys()) def listterms(self): """Unordered list of polynomial terms. """ return list(self.items()) def imul_num(p, c): """multiply inplace the polynomial p by an element in the coefficient ring, provided p is not one of the generators; else multiply not inplace Examples -------- >>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ >>> _, x, y = ring('x, y', ZZ) >>> p = x + y**2 >>> p1 = p.imul_num(3) >>> p1 3*x + 3*y**2 >>> p1 is p True >>> p = x >>> p1 = p.imul_num(3) >>> p1 3*x >>> p1 is p False """ if p in p.ring._gens_set: return p*c if not c: p.clear() return for exp in p: p[exp] *= c return p def content(f): """Returns GCD of polynomial's coefficients. """ domain = f.ring.domain cont = domain.zero gcd = domain.gcd for coeff in f.itercoeffs(): cont = gcd(cont, coeff) return cont def primitive(f): """Returns content and a primitive polynomial. """ cont = f.content() return cont, f.quo_ground(cont) def monic(f): """Divides all coefficients by the leading coefficient. """ if not f: return f else: return f.quo_ground(f.LC) def mul_ground(f, x): if not x: return f.ring.zero terms = [ (monom, coeff*x) for monom, coeff in f.iterterms() ] return f.new(terms) def mul_monom(f, monom): monomial_mul = f.ring.monomial_mul terms = [ (monomial_mul(f_monom, monom), f_coeff) for f_monom, f_coeff in f.items() ] return f.new(terms) def mul_term(f, term): monom, coeff = term if not f or not coeff: return f.ring.zero elif monom == f.ring.zero_monom: return f.mul_ground(coeff) monomial_mul = f.ring.monomial_mul terms = [ (monomial_mul(f_monom, monom), f_coeff*coeff) for f_monom, f_coeff in f.items() ] return f.new(terms) def quo_ground(f, x): domain = f.ring.domain if not x: raise ZeroDivisionError('polynomial division') if not f or x == domain.one: return f if domain.has_Field: quo = domain.quo terms = [ (monom, quo(coeff, x)) for monom, coeff in f.iterterms() ] else: terms = [ (monom, coeff // x) for monom, coeff in f.iterterms() if not (coeff % x) ] return f.new(terms) def quo_term(f, term): monom, coeff = term if not coeff: raise ZeroDivisionError("polynomial division") elif not f: return f.ring.zero elif monom == f.ring.zero_monom: return f.quo_ground(coeff) term_div = f._term_div() terms = [ term_div(t, term) for t in f.iterterms() ] return f.new([ t for t in terms if t is not None ]) def trunc_ground(f, p): if f.ring.domain.is_ZZ: terms = [] for monom, coeff in f.iterterms(): coeff = coeff % p if coeff > p // 2: coeff = coeff - p terms.append((monom, coeff)) else: terms = [ (monom, coeff % p) for monom, coeff in f.iterterms() ] poly = f.new(terms) poly.strip_zero() return poly rem_ground = trunc_ground def extract_ground(f, g): fc = f.content() gc = g.content() gcd = f.ring.domain.gcd(fc, gc) f = f.quo_ground(gcd) g = g.quo_ground(gcd) return gcd, f, g def _norm(f, norm_func): if not f: return f.ring.domain.zero else: ground_abs = f.ring.domain.abs return norm_func([ ground_abs(coeff) for coeff in f.itercoeffs() ]) def max_norm(f): return f._norm(max) def l1_norm(f): return f._norm(sum) def deflate(f, *G): ring = f.ring polys = [f] + list(G) J = [0]*ring.ngens for p in polys: for monom in p.itermonoms(): for i, m in enumerate(monom): J[i] = igcd(J[i], m) for i, b in enumerate(J): if not b: J[i] = 1 J = tuple(J) if all(b == 1 for b in J): return J, polys H = [] for p in polys: h = ring.zero for I, coeff in p.iterterms(): N = [ i // j for i, j in zip(I, J) ] h[tuple(N)] = coeff H.append(h) return J, H def inflate(f, J): poly = f.ring.zero for I, coeff in f.iterterms(): N = [ i*j for i, j in zip(I, J) ] poly[tuple(N)] = coeff return poly def lcm(f, g): domain = f.ring.domain if not domain.has_Field: fc, f = f.primitive() gc, g = g.primitive() c = domain.lcm(fc, gc) h = (f*g).quo(f.gcd(g)) if not domain.has_Field: return h.mul_ground(c) else: return h.monic() def gcd(f, g): return f.cofactors(g)[0] def cofactors(f, g): if not f and not g: zero = f.ring.zero return zero, zero, zero elif not f: h, cff, cfg = f._gcd_zero(g) return h, cff, cfg elif not g: h, cfg, cff = g._gcd_zero(f) return h, cff, cfg elif len(f) == 1: h, cff, cfg = f._gcd_monom(g) return h, cff, cfg elif len(g) == 1: h, cfg, cff = g._gcd_monom(f) return h, cff, cfg J, (f, g) = f.deflate(g) h, cff, cfg = f._gcd(g) return (h.inflate(J), cff.inflate(J), cfg.inflate(J)) def _gcd_zero(f, g): one, zero = f.ring.one, f.ring.zero if g.is_nonnegative: return g, zero, one else: return -g, zero, -one def _gcd_monom(f, g): ring = f.ring ground_gcd = ring.domain.gcd ground_quo = ring.domain.quo monomial_gcd = ring.monomial_gcd monomial_ldiv = ring.monomial_ldiv mf, cf = list(f.iterterms())[0] _mgcd, _cgcd = mf, cf for mg, cg in g.iterterms(): _mgcd = monomial_gcd(_mgcd, mg) _cgcd = ground_gcd(_cgcd, cg) h = f.new([(_mgcd, _cgcd)]) cff = f.new([(monomial_ldiv(mf, _mgcd), ground_quo(cf, _cgcd))]) cfg = f.new([(monomial_ldiv(mg, _mgcd), ground_quo(cg, _cgcd)) for mg, cg in g.iterterms()]) return h, cff, cfg def _gcd(f, g): ring = f.ring if ring.domain.is_QQ: return f._gcd_QQ(g) elif ring.domain.is_ZZ: return f._gcd_ZZ(g) else: # TODO: don't use dense representation (port PRS algorithms) return ring.dmp_inner_gcd(f, g) def _gcd_ZZ(f, g): return heugcd(f, g) def _gcd_QQ(f, g): ring = f.ring new_ring = ring.clone(domain=ring.domain.get_ring()) cf, f = f.clear_denoms() cg, g = g.clear_denoms() f = f.set_ring(new_ring) g = g.set_ring(new_ring) h, cff, cfg = f._gcd_ZZ(g) h = h.set_ring(ring) c, h = h.LC, h.monic() cff = cff.set_ring(ring).mul_ground(ring.domain.quo(c, cf)) cfg = cfg.set_ring(ring).mul_ground(ring.domain.quo(c, cg)) return h, cff, cfg def cancel(f, g): """ Cancel common factors in a rational function ``f/g``. Examples -------- >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> (2*x**2 - 2).cancel(x**2 - 2*x + 1) (2*x + 2, x - 1) """ ring = f.ring if not f: return f, ring.one domain = ring.domain if not (domain.has_Field and domain.has_assoc_Ring): _, p, q = f.cofactors(g) if q.is_negative: p, q = -p, -q else: new_ring = ring.clone(domain=domain.get_ring()) cq, f = f.clear_denoms() cp, g = g.clear_denoms() f = f.set_ring(new_ring) g = g.set_ring(new_ring) _, p, q = f.cofactors(g) _, cp, cq = new_ring.domain.cofactors(cp, cq) p = p.set_ring(ring) q = q.set_ring(ring) p_neg = p.is_negative q_neg = q.is_negative if p_neg and q_neg: p, q = -p, -q elif p_neg: cp, p = -cp, -p elif q_neg: cp, q = -cp, -q p = p.mul_ground(cp) q = q.mul_ground(cq) return p, q def diff(f, x): """Computes partial derivative in ``x``. Examples -------- >>> from sympy.polys.rings import ring >>> from sympy.polys.domains import ZZ >>> _, x, y = ring("x,y", ZZ) >>> p = x + x**2*y**3 >>> p.diff(x) 2*x*y**3 + 1 """ ring = f.ring i = ring.index(x) m = ring.monomial_basis(i) g = ring.zero for expv, coeff in f.iterterms(): if expv[i]: e = ring.monomial_ldiv(expv, m) g[e] = coeff*expv[i] return g def __call__(f, *values): if 0 < len(values) <= f.ring.ngens: return f.evaluate(list(zip(f.ring.gens, values))) else: raise ValueError("expected at least 1 and at most %s values, got %s" % (f.ring.ngens, len(values))) def evaluate(f, x, a=None): if isinstance(x, list) and a is None: (X, a), x = x[0], x[1:] f = f.evaluate(X, a) if not x: return f else: x = [ (Y.drop(X), a) for (Y, a) in x ] return f.evaluate(x) ring = f.ring i = ring.index(x) a = ring.domain.convert(a) if ring.ngens == 1: result = ring.domain.zero for (n,), coeff in f.iterterms(): result += coeff*a**n return result else: poly = ring.drop(x).zero for monom, coeff in f.iterterms(): n, monom = monom[i], monom[:i] + monom[i+1:] coeff = coeff*a**n if monom in poly: coeff = coeff + poly[monom] if coeff: poly[monom] = coeff else: del poly[monom] else: if coeff: poly[monom] = coeff return poly def subs(f, x, a=None): if isinstance(x, list) and a is None: for X, a in x: f = f.subs(X, a) return f ring = f.ring i = ring.index(x) a = ring.domain.convert(a) if ring.ngens == 1: result = ring.domain.zero for (n,), coeff in f.iterterms(): result += coeff*a**n return ring.ground_new(result) else: poly = ring.zero for monom, coeff in f.iterterms(): n, monom = monom[i], monom[:i] + (0,) + monom[i+1:] coeff = coeff*a**n if monom in poly: coeff = coeff + poly[monom] if coeff: poly[monom] = coeff else: del poly[monom] else: if coeff: poly[monom] = coeff return poly def compose(f, x, a=None): ring = f.ring poly = ring.zero gens_map = dict(list(zip(ring.gens, list(range(ring.ngens))))) if a is not None: replacements = [(x, a)] else: if isinstance(x, list): replacements = list(x) elif isinstance(x, dict): replacements = sorted(list(x.items()), key=lambda k: gens_map[k[0]]) else: raise ValueError("expected a generator, value pair a sequence of such pairs") for k, (x, g) in enumerate(replacements): replacements[k] = (gens_map[x], ring.ring_new(g)) for monom, coeff in f.iterterms(): monom = list(monom) subpoly = ring.one for i, g in replacements: n, monom[i] = monom[i], 0 if n: subpoly *= g**n subpoly = subpoly.mul_term((tuple(monom), coeff)) poly += subpoly return poly # TODO: following methods should point to polynomial # representation independent algorithm implementations. def pdiv(f, g): return f.ring.dmp_pdiv(f, g) def prem(f, g): return f.ring.dmp_prem(f, g) def pquo(f, g): return f.ring.dmp_quo(f, g) def pexquo(f, g): return f.ring.dmp_exquo(f, g) def half_gcdex(f, g): return f.ring.dmp_half_gcdex(f, g) def gcdex(f, g): return f.ring.dmp_gcdex(f, g) def subresultants(f, g): return f.ring.dmp_subresultants(f, g) def resultant(f, g): return f.ring.dmp_resultant(f, g) def discriminant(f): return f.ring.dmp_discriminant(f) def decompose(f): if f.ring.is_univariate: return f.ring.dup_decompose(f) else: raise MultivariatePolynomialError("polynomial decomposition") def shift(f, a): if f.ring.is_univariate: return f.ring.dup_shift(f, a) else: raise MultivariatePolynomialError("polynomial shift") def sturm(f): if f.ring.is_univariate: return f.ring.dup_sturm(f) else: raise MultivariatePolynomialError("sturm sequence") def gff_list(f): return f.ring.dmp_gff_list(f) def sqf_norm(f): return f.ring.dmp_sqf_norm(f) def sqf_part(f): return f.ring.dmp_sqf_part(f) def sqf_list(f, all=False): return f.ring.dmp_sqf_list(f, all=all) def factor_list(f): return f.ring.dmp_factor_list(f) sympy-0.7.4.1/sympy/polys/modulargcd.py0000644000175000017500000016260012253362407020273 0ustar georgeskgeorgeskfrom sympy.ntheory import nextprime from sympy.ntheory.modular import crt from sympy.polys.galoistools import ( gf_gcd, gf_from_dict, gf_gcdex, gf_div, gf_lcm, gf_rem) from sympy.polys.polyerrors import ModularGCDFailed from sympy.polys.domains import PolynomialRing from sympy.core.compatibility import xrange from sympy.mpmath import sqrt from sympy import Dummy import random def _trivial_gcd(f, g): """ Compute the GCD of two polynomials in trivial cases, i.e. when one or both polynomials are zero. """ ring = f.ring if not (f or g): return ring.zero, ring.zero, ring.zero elif not f: if g.LC < ring.domain.zero: return -g, ring.zero, -ring.one else: return g, ring.zero, ring.one elif not g: if f.LC < ring.domain.zero: return -f, -ring.one, ring.zero else: return f, ring.one, ring.zero return None def _gf_gcd(fp, gp, p): r""" Compute the GCD of two univariate polynomials in `\mathbb{Z}_p[x]`. """ dom = fp.ring.domain while gp: rem = fp deg = gp.degree() lcinv = dom.invert(gp.LC, p) while True: degrem = rem.degree() if degrem < deg: break rem = (rem - gp.mul_monom((degrem - deg,)).mul_ground(lcinv * rem.LC)).trunc_ground(p) fp = gp gp = rem return fp.mul_ground(dom.invert(fp.LC, p)).trunc_ground(p) def _degree_bound_univariate(f, g): r""" Compute an upper bound for the degree of the GCD of two univariate integer polynomials `f` and `g`. The function chooses a suitable prime `p` and computes the GCD of `f` and `g` in `\mathbb{Z}_p[x]`. The choice of `p` guarantees that the degree in `\mathbb{Z}_p[x]` is greater than or equal to the degree in `\mathbb{Z}[x]`. Parameters ========== f : PolyElement univariate integer polynomial g : PolyElement univariate integer polynomial """ gamma = f.ring.domain.gcd(f.LC, g.LC) p = 1 p = nextprime(p) while gamma % p == 0: p = nextprime(p) fp = f.trunc_ground(p) gp = g.trunc_ground(p) hp = _gf_gcd(fp, gp, p) deghp = hp.degree() return deghp def _chinese_remainder_reconstruction_univariate(hp, hq, p, q): r""" Construct a polynomial `h_{pq}` in `\mathbb{Z}_{p q}[x]` such that .. math :: h_{pq} = h_p \; \mathrm{mod} \, p h_{pq} = h_q \; \mathrm{mod} \, q for relatively prime integers `p` and `q` and polynomials `h_p` and `h_q` in `\mathbb{Z}_p[x]` and `\mathbb{Z}_q[x]` respectively. The coefficients of the polynomial `h_{pq}` are computed with the Chinese Remainder Theorem. The symmetric representation in `\mathbb{Z}_p[x]`, `\mathbb{Z}_q[x]` and `\mathbb{Z}_{p q}[x]` is used. It is assumed that `h_p` and `h_q` have the same degree. Parameters ========== hp : PolyElement univariate integer polynomial with coefficients in `\mathbb{Z}_p` hq : PolyElement univariate integer polynomial with coefficients in `\mathbb{Z}_q` p : Integer modulus of `h_p`, relatively prime to `q` q : Integer modulus of `h_q`, relatively prime to `p` Examples ======== >>> from sympy.polys.modulargcd import _chinese_remainder_reconstruction_univariate >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> p = 3 >>> q = 5 >>> hp = -x**3 - 1 >>> hq = 2*x**3 - 2*x**2 + x >>> hpq = _chinese_remainder_reconstruction_univariate(hp, hq, p, q) >>> hpq 2*x**3 + 3*x**2 + 6*x + 5 >>> hpq.trunc_ground(p) == hp True >>> hpq.trunc_ground(q) == hq True """ n = hp.degree() x = hp.ring.gens[0] hpq = hp.ring.zero for i in xrange(n+1): hpq[(i,)] = crt([p, q], [hp.coeff(x**i), hq.coeff(x**i)], symmetric=True)[0] hpq.strip_zero() return hpq def modgcd_univariate(f, g): r""" Computes the GCD of two polynomials in `\mathbb{Z}[x]` using a modular algorithm. The algorithm computes the GCD of two univariate integer polynomials `f` and `g` by computing the GCD in `\mathbb{Z}_p[x]` for suitable primes `p` and then reconstructing the coefficients with the Chinese Remainder Theorem. Trial division is only made for candidates which are very likely the desired GCD. Parameters ========== f : PolyElement univariate integer polynomial g : PolyElement univariate integer polynomial Returns ======= h : PolyElement GCD of the polynomials `f` and `g` cff : PolyElement cofactor of `f`, i.e. `\frac{f}{h}` cfg : PolyElement cofactor of `g`, i.e. `\frac{g}{h}` Examples ======== >>> from sympy.polys.modulargcd import modgcd_univariate >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> f = x**5 - 1 >>> g = x - 1 >>> h, cff, cfg = modgcd_univariate(f, g) >>> h, cff, cfg (x - 1, x**4 + x**3 + x**2 + x + 1, 1) >>> cff * h == f True >>> cfg * h == g True >>> f = 6*x**2 - 6 >>> g = 2*x**2 + 4*x + 2 >>> h, cff, cfg = modgcd_univariate(f, g) >>> h, cff, cfg (2*x + 2, 3*x - 3, x + 1) >>> cff * h == f True >>> cfg * h == g True References ========== 1. [Monagan00]_ """ assert f.ring == g.ring and f.ring.domain.is_ZZ result = _trivial_gcd(f, g) if result is not None: return result ring = f.ring cf, f = f.primitive() cg, g = g.primitive() ch = ring.domain.gcd(cf, cg) bound = _degree_bound_univariate(f, g) if bound == 0: return ring(ch), f.mul_ground(cf // ch), g.mul_ground(cg // ch) gamma = ring.domain.gcd(f.LC, g.LC) m = 1 p = 1 while True: p = nextprime(p) while gamma % p == 0: p = nextprime(p) fp = f.trunc_ground(p) gp = g.trunc_ground(p) hp = _gf_gcd(fp, gp, p) deghp = hp.degree() if deghp > bound: continue elif deghp < bound: m = 1 bound = deghp continue hp = hp.mul_ground(gamma).trunc_ground(p) if m == 1: m = p hlastm = hp continue hm = _chinese_remainder_reconstruction_univariate(hp, hlastm, p, m) m *= p if not hm == hlastm: hlastm = hm continue h = hm.quo_ground(hm.content()) fquo, frem = f.div(h) gquo, grem = g.div(h) if not frem and not grem: if h.LC < 0: ch = -ch h = h.mul_ground(ch) cff = fquo.mul_ground(cf // ch) cfg = gquo.mul_ground(cg // ch) return h, cff, cfg def _primitive(f, p): r""" Compute the content and the primitive part of a polynomial in `\mathbb{Z}_p[x_0, \ldots, x_{k-2}, y] \cong \mathbb{Z}_p[y][x_0, \ldots, x_{k-2}]`. Parameters ========== f : PolyElement integer polynomial in `\mathbb{Z}_p[x0, \ldots, x{k-2}, y]` p : Integer modulus of `f` Returns ======= contf : PolyElement integer polynomial in `\mathbb{Z}_p[y]`, content of `f` ppf : PolyElement primitive part of `f`, i.e. `\frac{f}{contf}` Examples ======== >>> from sympy.polys.modulargcd import _primitive >>> from sympy.polys import ring, ZZ >>> R, x, y = ring("x, y", ZZ) >>> p = 3 >>> f = x**2*y**2 + x**2*y - y**2 - y >>> _primitive(f, p) (y**2 + y, x**2 - 1) >>> R, x, y, z = ring("x, y, z", ZZ) >>> f = x*y*z - y**2*z**2 >>> _primitive(f, p) (z, x*y - y**2*z) """ ring = f.ring dom = ring.domain k = ring.ngens coeffs = {} for monom, coeff in f.iterterms(): if monom[:-1] not in coeffs: coeffs[monom[:-1]] = {} coeffs[monom[:-1]][monom[-1]] = coeff cont = [] for coeff in iter(coeffs.values()): cont = gf_gcd(cont, gf_from_dict(coeff, p, dom), p, dom) yring = ring.clone(symbols=ring.symbols[k-1]) contf = yring.from_dense(cont).trunc_ground(p) return contf, f.quo(contf.set_ring(ring)) def _deg(f): r""" Compute the degree of a multivariate polynomial `f \in K[x_0, \ldots, x_{k-2}, y] \cong K[y][x_0, \ldots, x_{k-2}]`. Parameters ========== f : PolyElement polynomial in `K[x_0, \ldots, x_{k-2}, y]` Returns ======= degf : Integer tuple degree of `f` in `x_0, \ldots, x_{k-2}` Examples ======== >>> from sympy.polys.modulargcd import _deg >>> from sympy.polys import ring, ZZ >>> R, x, y = ring("x, y", ZZ) >>> f = x**2*y**2 + x**2*y - 1 >>> _deg(f) (2,) >>> R, x, y, z = ring("x, y, z", ZZ) >>> f = x**2*y**2 + x**2*y - 1 >>> _deg(f) (2, 2) >>> f = x*y*z - y**2*z**2 >>> _deg(f) (1, 1) """ k = f.ring.ngens degf = (0,) * (k-1) for monom in f.itermonoms(): if monom[:-1] > degf: degf = monom[:-1] return degf def _LC(f): r""" Compute the leading coefficient of a multivariate polynomial `f \in K[x_0, \ldots, x_{k-2}, y] \cong K[y][x_0, \ldots, x_{k-2}]`. Parameters ========== f : PolyElement polynomial in `K[x_0, \ldots, x_{k-2}, y]` Returns ======= lcf : PolyElement polynomial in `K[y]`, leading coefficient of `f` Examples ======== >>> from sympy.polys.modulargcd import _LC >>> from sympy.polys import ring, ZZ >>> R, x, y = ring("x, y", ZZ) >>> f = x**2*y**2 + x**2*y - 1 >>> _LC(f) y**2 + y >>> R, x, y, z = ring("x, y, z", ZZ) >>> f = x**2*y**2 + x**2*y - 1 >>> _LC(f) 1 >>> f = x*y*z - y**2*z**2 >>> _LC(f) z """ ring = f.ring k = ring.ngens yring = ring.clone(symbols=ring.symbols[k-1]) y = yring.gens[0] degf = _deg(f) lcf = yring.zero for monom, coeff in f.iterterms(): if monom[:-1] == degf: lcf += coeff*y**monom[-1] return lcf def _swap(f, i): """ Make the variable `x_i` the leading one in a multivariate polynomial `f`. """ ring = f.ring k = ring.ngens fswap = ring.zero for monom, coeff in f.iterterms(): monomswap = (monom[i],) + monom[:i] + monom[i+1:] fswap[monomswap] = coeff return fswap def _degree_bound_bivariate(f, g): r""" Compute upper degree bounds for the GCD of two bivariate integer polynomials `f` and `g`. The GCD is viewed as a polynomial in `\mathbb{Z}[y][x]` and the function returns an upper bound for its degree and one for the degree of its content. This is done by choosing a suitable prime `p` and computing the GCD of the contents of `f \; \mathrm{mod} \, p` and `g \; \mathrm{mod} \, p`. The choice of `p` guarantees that the degree of the content in `\mathbb{Z}_p[y]` is greater than or equal to the degree in `\mathbb{Z}[y]`. To obtain the degree bound in the variable `x`, the polynomials are evaluated at `y = a` for a suitable `a \in \mathbb{Z}_p` and then their GCD in `\mathbb{Z}_p[x]` is computed. If no such `a` exists, i.e. the degree in `\mathbb{Z}_p[x]` is always smaller than the one in `\mathbb{Z}[y][x]`, then the bound is set to the minimum of the degrees of `f` and `g` in `x`. Parameters ========== f : PolyElement bivariate integer polynomial g : PolyElement bivariate integer polynomial Returns ======= xbound : Integer upper bound for the degree of the GCD of the polynomials `f` and `g` in the variable `x` ycontbound : Integer upper bound for the degree of the content of the GCD of the polynomials `f` and `g` in the variable `y` References ========== 1. [Monagan00]_ """ ring = f.ring gamma1 = ring.domain.gcd(f.LC, g.LC) gamma2 = ring.domain.gcd(_swap(f, 1).LC, _swap(g, 1).LC) badprimes = gamma1 * gamma2 p = 1 p = nextprime(p) while badprimes % p == 0: p = nextprime(p) fp = f.trunc_ground(p) gp = g.trunc_ground(p) contfp, fp = _primitive(fp, p) contgp, gp = _primitive(gp, p) conthp = _gf_gcd(contfp, contgp, p) # polynomial in Z_p[y] ycontbound = conthp.degree() # polynomial in Z_p[y] delta = _gf_gcd(_LC(fp), _LC(gp), p) for a in xrange(p): if not delta.evaluate(0, a) % p: continue fpa = fp.evaluate(1, a).trunc_ground(p) gpa = gp.evaluate(1, a).trunc_ground(p) hpa = _gf_gcd(fpa, gpa, p) xbound = hpa.degree() return xbound, ycontbound return min(fp.degree(), gp.degree()), ycontbound def _chinese_remainder_reconstruction_multivariate(hp, hq, p, q): r""" Construct a polynomial `h_{pq}` in `\mathbb{Z}_{p q}[x_0, \ldots, x_{k-1}]` such that .. math :: h_{pq} = h_p \; \mathrm{mod} \, p h_{pq} = h_q \; \mathrm{mod} \, q for relatively prime integers `p` and `q` and polynomials `h_p` and `h_q` in `\mathbb{Z}_p[x_0, \ldots, x_{k-1}]` and `\mathbb{Z}_q[x_0, \ldots, x_{k-1}]` respectively. The coefficients of the polynomial `h_{pq}` are computed with the Chinese Remainder Theorem. The symmetric representation in `\mathbb{Z}_p[x_0, \ldots, x_{k-1}]`, `\mathbb{Z}_q[x_0, \ldots, x_{k-1}]` and `\mathbb{Z}_{p q}[x_0, \ldots, x_{k-1}]` is used. Parameters ========== hp : PolyElement multivariate integer polynomial with coefficients in `\mathbb{Z}_p` hq : PolyElement multivariate integer polynomial with coefficients in `\mathbb{Z}_q` p : Integer modulus of `h_p`, relatively prime to `q` q : Integer modulus of `h_q`, relatively prime to `p` Examples ======== >>> from sympy.polys.modulargcd import _chinese_remainder_reconstruction_multivariate >>> from sympy.polys import ring, ZZ >>> R, x, y = ring("x, y", ZZ) >>> p = 3 >>> q = 5 >>> hp = x**3*y - x**2 - 1 >>> hq = -x**3*y - 2*x*y**2 + 2 >>> hpq = _chinese_remainder_reconstruction_multivariate(hp, hq, p, q) >>> hpq 4*x**3*y + 5*x**2 + 3*x*y**2 + 2 >>> hpq.trunc_ground(p) == hp True >>> hpq.trunc_ground(q) == hq True >>> R, x, y, z = ring("x, y, z", ZZ) >>> p = 6 >>> q = 5 >>> hp = 3*x**4 - y**3*z + z >>> hq = -2*x**4 + z >>> hpq = _chinese_remainder_reconstruction_multivariate(hp, hq, p, q) >>> hpq 3*x**4 + 5*y**3*z + z >>> hpq.trunc_ground(p) == hp True >>> hpq.trunc_ground(q) == hq True """ hpmonoms = set(hp.monoms()) hqmonoms = set(hq.monoms()) monoms = hpmonoms.intersection(hqmonoms) hpmonoms.difference_update(monoms) hqmonoms.difference_update(monoms) zero = hp.ring.domain.zero hpq = hp.ring.zero if isinstance(hp.ring.domain, PolynomialRing): crt_ = _chinese_remainder_reconstruction_multivariate else: def crt_(cp, cq, p, q): return crt([p, q], [cp, cq], symmetric=True)[0] for monom in monoms: hpq[monom] = crt_(hp[monom], hq[monom], p, q) for monom in hpmonoms: hpq[monom] = crt_(hp[monom], zero, p, q) for monom in hqmonoms: hpq[monom] = crt_(zero, hq[monom], p, q) return hpq def _interpolate_multivariate(evalpoints, hpeval, ring, i, p, ground=False): r""" Reconstruct a polynomial `h_p` in `\mathbb{Z}_p[x_0, \ldots, x_{k-1}]` from a list of evaluation points in `\mathbb{Z}_p` and a list of polynomials in `\mathbb{Z}_p[x_0, \ldots, x_{i-1}, x_{i+1}, \ldots, x_{k-1}]`, which are the images of `h_p` evaluated in the variable `x_i`. It is also possible to reconstruct a parameter of the ground domain, i.e. if `h_p` is a polynomial over `\mathbb{Z}_p[x_0, \ldots, x_{k-1}]`. In this case, one has to set ``ground=True``. Parameters ========== evalpoints : list of Integer objects list of evaluation points in `\mathbb{Z}_p` hpeval : list of PolyElement objects list of polynomials in (resp. over) `\mathbb{Z}_p[x_0, \ldots, x_{i-1}, x_{i+1}, \ldots, x_{k-1}]`, images of `h_p` evaluated in the variable `x_i` ring : PolyRing `h_p` will be an element of this ring i : Integer index of the variable which has to be reconstructed p : Integer prime number, modulus of `h_p` ground : Boolean indicates whether `x_i` is in the ground domain, default is ``False`` Returns ======= hp : PolyElement interpolated polynomial in (resp. over) `\mathbb{Z}_p[x_0, \ldots, x_{k-1}]` """ hp = ring.zero if ground: domain = ring.domain.domain y = ring.domain.gens[i] else: domain = ring.domain y = ring.gens[i] for a, hpa in zip(evalpoints, hpeval): numer = ring.one denom = domain.one for b in evalpoints: if b == a: continue numer *= y - b denom *= a - b denom = domain.invert(denom, p) coeff = numer.mul_ground(denom) hp += hpa.set_ring(ring) * coeff return hp.trunc_ground(p) def modgcd_bivariate(f, g): r""" Computes the GCD of two polynomials in `\mathbb{Z}[x, y]` using a modular algorithm. The algorithm computes the GCD of two bivariate integer polynomials `f` and `g` by calculating the GCD in `\mathbb{Z}_p[x, y]` for suitable primes `p` and then reconstructing the coefficients with the Chinese Remainder Theorem. To compute the bivariate GCD over `\mathbb{Z}_p`, the polynomials `f \; \mathrm{mod} \, p` and `g \; \mathrm{mod} \, p` are evaluated at `y = a` for certain `a \in \mathbb{Z}_p` and then their univariate GCD in `\mathbb{Z}_p[x]` is computed. Interpolating those yields the bivariate GCD in `\mathbb{Z}_p[x, y]`. To verify the result in `\mathbb{Z}[x, y]`, trial division is done, but only for candidates which are very likely the desired GCD. Parameters ========== f : PolyElement bivariate integer polynomial g : PolyElement bivariate integer polynomial Returns ======= h : PolyElement GCD of the polynomials `f` and `g` cff : PolyElement cofactor of `f`, i.e. `\frac{f}{h}` cfg : PolyElement cofactor of `g`, i.e. `\frac{g}{h}` Examples ======== >>> from sympy.polys.modulargcd import modgcd_bivariate >>> from sympy.polys import ring, ZZ >>> R, x, y = ring("x, y", ZZ) >>> f = x**2 - y**2 >>> g = x**2 + 2*x*y + y**2 >>> h, cff, cfg = modgcd_bivariate(f, g) >>> h, cff, cfg (x + y, x - y, x + y) >>> cff * h == f True >>> cfg * h == g True >>> f = x**2*y - x**2 - 4*y + 4 >>> g = x + 2 >>> h, cff, cfg = modgcd_bivariate(f, g) >>> h, cff, cfg (x + 2, x*y - x - 2*y + 2, 1) >>> cff * h == f True >>> cfg * h == g True References ========== 1. [Monagan00]_ """ assert f.ring == g.ring and f.ring.domain.is_ZZ result = _trivial_gcd(f, g) if result is not None: return result ring = f.ring cf, f = f.primitive() cg, g = g.primitive() ch = ring.domain.gcd(cf, cg) xbound, ycontbound = _degree_bound_bivariate(f, g) if xbound == ycontbound == 0: return ring(ch), f.mul_ground(cf // ch), g.mul_ground(cg // ch) fswap = _swap(f, 1) gswap = _swap(g, 1) degyf = fswap.degree() degyg = gswap.degree() ybound, xcontbound = _degree_bound_bivariate(fswap, gswap) if ybound == xcontbound == 0: return ring(ch), f.mul_ground(cf // ch), g.mul_ground(cg // ch) # TODO: to improve performance, choose the main variable here gamma1 = ring.domain.gcd(f.LC, g.LC) gamma2 = ring.domain.gcd(fswap.LC, gswap.LC) badprimes = gamma1 * gamma2 m = 1 p = 1 while True: p = nextprime(p) while badprimes % p == 0: p = nextprime(p) fp = f.trunc_ground(p) gp = g.trunc_ground(p) contfp, fp = _primitive(fp, p) contgp, gp = _primitive(gp, p) conthp = _gf_gcd(contfp, contgp, p) # monic polynomial in Z_p[y] degconthp = conthp.degree() if degconthp > ycontbound: continue elif degconthp < ycontbound: m = 1 ycontbound = degconthp continue # polynomial in Z_p[y] delta = _gf_gcd(_LC(fp), _LC(gp), p) degcontfp = contfp.degree() degcontgp = contgp.degree() degdelta = delta.degree() N = min(degyf - degcontfp, degyg - degcontgp, ybound - ycontbound + degdelta) + 1 if p < N: continue n = 0 evalpoints = [] hpeval = [] unlucky = False for a in xrange(p): deltaa = delta.evaluate(0, a) if not deltaa % p: continue fpa = fp.evaluate(1, a).trunc_ground(p) gpa = gp.evaluate(1, a).trunc_ground(p) hpa = _gf_gcd(fpa, gpa, p) # monic polynomial in Z_p[x] deghpa = hpa.degree() if deghpa > xbound: continue elif deghpa < xbound: m = 1 xbound = deghpa unlucky = True break hpa = hpa.mul_ground(deltaa).trunc_ground(p) evalpoints.append(a) hpeval.append(hpa) n += 1 if n == N: break if unlucky: continue if n < N: continue hp = _interpolate_multivariate(evalpoints, hpeval, ring, 1, p) hp = _primitive(hp, p)[1] hp = hp * conthp.set_ring(ring) degyhp = hp.degree(1) if degyhp > ybound: continue if degyhp < ybound: m = 1 ybound = degyhp continue hp = hp.mul_ground(gamma1).trunc_ground(p) if m == 1: m = p hlastm = hp continue hm = _chinese_remainder_reconstruction_multivariate(hp, hlastm, p, m) m *= p if not hm == hlastm: hlastm = hm continue h = hm.quo_ground(hm.content()) fquo, frem = f.div(h) gquo, grem = g.div(h) if not frem and not grem: if h.LC < 0: ch = -ch h = h.mul_ground(ch) cff = fquo.mul_ground(cf // ch) cfg = gquo.mul_ground(cg // ch) return h, cff, cfg def _modgcd_multivariate_p(f, g, p, degbound, contbound): r""" Compute the GCD of two polynomials in `\mathbb{Z}_p[x0, \ldots, x{k-1}]`. The algorithm reduces the problem step by step by evaluating the polynomials `f` and `g` at `x_{k-1} = a` for suitable `a \in \mathbb{Z}_p` and then calls itself recursively to compute the GCD in `\mathbb{Z}_p[x_0, \ldots, x_{k-2}]`. If these recursive calls are succsessful for enough evaluation points, the GCD in `k` variables is interpolated, otherwise the algorithm returns ``None``. Every time a GCD or a content is computed, their degrees are compared with the bounds. If a degree greater then the bound is encountered, then the current call returns ``None`` and a new evaluation point has to be chosen. If at some point the degree is smaller, the correspondent bound is updated and the algorithm fails. Parameters ========== f : PolyElement multivariate integer polynomial with coefficients in `\mathbb{Z}_p` g : PolyElement multivariate integer polynomial with coefficients in `\mathbb{Z}_p` p : Integer prime number, modulus of `f` and `g` degbound : list of Integer objects ``degbound[i]`` is an upper bound for the degree of the GCD of `f` and `g` in the variable `x_i` contbound : list of Integer objects ``contbound[i]`` is an upper bound for the degree of the content of the GCD in `\mathbb{Z}_p[x_i][x_0, \ldots, x_{i-1}]`, ``contbound[0]`` is not used can therefore be chosen arbitrarily. Returns ======= h : PolyElement GCD of the polynomials `f` and `g` or ``None`` References ========== 1. [Monagan00]_ 2. [Brown71]_ """ ring = f.ring k = ring.ngens if k == 1: h = _gf_gcd(f, g, p).trunc_ground(p) degh = h.degree() if degh > degbound[0]: return None if degh < degbound[0]: degbound[0] = degh raise ModularGCDFailed return h degyf = f.degree(k-1) degyg = g.degree(k-1) contf, f = _primitive(f, p) contg, g = _primitive(g, p) conth = _gf_gcd(contf, contg, p) # polynomial in Z_p[y] degcontf = contf.degree() degcontg = contg.degree() degconth = conth.degree() if degconth > contbound[k-1]: return None if degconth < contbound[k-1]: contbound[k-1] = degconth raise ModularGCDFailed lcf = _LC(f) lcg = _LC(g) delta = _gf_gcd(lcf, lcg, p) # polynomial in Z_p[y] evaltest = delta for i in xrange(k-1): evaltest *= _gf_gcd(_LC(_swap(f, i)), _LC(_swap(g, i)), p) degdelta = delta.degree() N = min(degyf - degcontf, degyg - degcontg, degbound[k-1] - contbound[k-1] + degdelta) + 1 if p < N: return None n = 0 d = 0 evalpoints = [] heval = [] points = set(range(p)) while points: a = random.sample(points, 1)[0] points.remove(a) if not evaltest.evaluate(0, a) % p: continue deltaa = delta.evaluate(0, a) % p fa = f.evaluate(k-1, a).trunc_ground(p) ga = g.evaluate(k-1, a).trunc_ground(p) # polynomials in Z_p[x_0, ..., x_{k-2}] ha = _modgcd_multivariate_p(fa, ga, p, degbound, contbound) if ha is None: d += 1 if d > n: return None continue if ha.is_ground: h = conth.set_ring(ring).trunc_ground(p) return h ha = ha.mul_ground(deltaa).trunc_ground(p) evalpoints.append(a) heval.append(ha) n += 1 if n == N: h = _interpolate_multivariate(evalpoints, heval, ring, k-1, p) h = _primitive(h, p)[1] * conth.set_ring(ring) degyh = h.degree(k-1) if degyh > degbound[k-1]: return None if degyh < degbound[k-1]: degbound[k-1] = degyh raise ModularGCDFailed return h return None def modgcd_multivariate(f, g): r""" Compute the GCD of two polynomials in `\mathbb{Z}[x_0, \ldots, x_{k-1}]` using a modular algorithm. The algorithm computes the GCD of two multivariate integer polynomials `f` and `g` by calculating the GCD in `\mathbb{Z}_p[x_0, \ldots, x_{k-1}]` for suitable primes `p` and then reconstructing the coefficients with the Chinese Remainder Theorem. To compute the multivariate GCD over `\mathbb{Z}_p` the recursive subroutine ``_modgcd_multivariate_p`` is used. To verify the result in `\mathbb{Z}[x_0, \ldots, x_{k-1}]`, trial division is done, but only for candidates which are very likely the desired GCD. Parameters ========== f : PolyElement multivariate integer polynomial g : PolyElement multivariate integer polynomial Returns ======= h : PolyElement GCD of the polynomials `f` and `g` cff : PolyElement cofactor of `f`, i.e. `\frac{f}{h}` cfg : PolyElement cofactor of `g`, i.e. `\frac{g}{h}` Examples ======== >>> from sympy.polys.modulargcd import modgcd_multivariate >>> from sympy.polys import ring, ZZ >>> R, x, y = ring("x, y", ZZ) >>> f = x**2 - y**2 >>> g = x**2 + 2*x*y + y**2 >>> h, cff, cfg = modgcd_multivariate(f, g) >>> h, cff, cfg (x + y, x - y, x + y) >>> cff * h == f True >>> cfg * h == g True >>> R, x, y, z = ring("x, y, z", ZZ) >>> f = x*z**2 - y*z**2 >>> g = x**2*z + z >>> h, cff, cfg = modgcd_multivariate(f, g) >>> h, cff, cfg (z, x*z - y*z, x**2 + 1) >>> cff * h == f True >>> cfg * h == g True References ========== 1. [Monagan00]_ 2. [Brown71]_ See also ======== _modgcd_multivariate_p """ assert f.ring == g.ring and f.ring.domain.is_ZZ result = _trivial_gcd(f, g) if result is not None: return result ring = f.ring k = ring.ngens # divide out integer content cf, f = f.primitive() cg, g = g.primitive() ch = ring.domain.gcd(cf, cg) gamma = ring.domain.gcd(f.LC, g.LC) badprimes = ring.domain.one for i in xrange(k): badprimes *= ring.domain.gcd(_swap(f, i).LC, _swap(g, i).LC) degbound = [min(fdeg, gdeg) for fdeg, gdeg in zip(f.degrees(), g.degrees())] contbound = list(degbound) m = 1 p = 1 while True: p = nextprime(p) while badprimes % p == 0: p = nextprime(p) fp = f.trunc_ground(p) gp = g.trunc_ground(p) try: # monic GCD of fp, gp in Z_p[x_0, ..., x_{k-2}, y] hp = _modgcd_multivariate_p(fp, gp, p, degbound, contbound) except ModularGCDFailed: m = 1 continue if hp is None: continue hp = hp.mul_ground(gamma).trunc_ground(p) if m == 1: m = p hlastm = hp continue hm = _chinese_remainder_reconstruction_multivariate(hp, hlastm, p, m) m *= p if not hm == hlastm: hlastm = hm continue h = hm.primitive()[1] fquo, frem = f.div(h) gquo, grem = g.div(h) if not frem and not grem: if h.LC < 0: ch = -ch h = h.mul_ground(ch) cff = fquo.mul_ground(cf // ch) cfg = gquo.mul_ground(cg // ch) return h, cff, cfg def _gf_div(f, g, p): r""" Compute `\frac f g` modulo `p` for two univariate polynomials over `\mathbb Z_p`. """ ring = f.ring densequo, denserem = gf_div(f.to_dense(), g.to_dense(), p, ring.domain) return ring.from_dense(densequo), ring.from_dense(denserem) def _rational_function_reconstruction(c, p, m): r""" Reconstruct a rational function `\frac a b` in `\mathbb Z_p(t)` from .. math:: c = \frac a b \; \mathrm{mod} \, m, where `c` and `m` are polynomials in `\mathbb Z_p[t]` and `m` has positive degree. The algorithm is based on the Euclidean Algorithm. In general, `m` is not irreducible, so it is possible that `b` is not invertible modulo `m`. In that case ``None`` is returned. Parameters ========== c : PolyElement univariate polynomial in `\mathbb Z[t]` p : Integer prime number m : PolyElement modulus, not necessarily irreducible Returns ======= frac : FracElement either `\frac a b` in `\mathbb Z(t)` or ``None`` References ========== 1. [Hoeij04]_ """ ring = c.ring domain = ring.domain M = m.degree() N = M // 2 D = M - N - 1 r0, s0 = m, ring.zero r1, s1 = c, ring.one while r1.degree() > N: quo = _gf_div(r0, r1, p)[0] r0, r1 = r1, (r0 - quo*r1).trunc_ground(p) s0, s1 = s1, (s0 - quo*s1).trunc_ground(p) a, b = r1, s1 if b.degree() > D or _gf_gcd(b, m, p) != 1: return None lc = b.LC if lc != 1: lcinv = domain.invert(lc, p) a = a.mul_ground(lcinv).trunc_ground(p) b = b.mul_ground(lcinv).trunc_ground(p) field = ring.to_field() return field(a) / field(b) def _rational_reconstruction_func_coeffs(hm, p, m, ring, k): r""" Reconstruct every coefficient `c_h` of a polynomial `h` in `\mathbb Z_p(t_k)[t_1, \ldots, t_{k-1}][x, z]` from the corresponding coefficient `c_{h_m}` of a polynomial `h_m` in `\mathbb Z_p[t_1, \ldots, t_k][x, z] \cong \mathbb Z_p[t_k][t_1, \ldots, t_{k-1}][x, z]` such that .. math:: c_{h_m} = c_h \; \mathrm{mod} \, m, where `m \in \mathbb Z_p[t]`. The reconstruction is based on the Euclidean Algorithm. In general, `m` is not irreducible, so it is possible that this fails for some coefficient. In that case ``None`` is returned. Parameters ========== hm : PolyElement polynomial in `\mathbb Z[t_1, \ldots, t_k][x, z]` p : Integer prime number, modulus of `\mathbb Z_p` m : PolyElement modulus, polynomial in `\mathbb Z[t]`, not necessarily irreducible ring : PolyRing `\mathbb Z(t_k)[t_1, \ldots, t_{k-1}][x, z]`, `h` will be an element of this ring k : Integer index of the parameter `t_k` which will be reconstructed Returns ======= h : PolyElement reconstructed polynomial in `\mathbb Z(t_k)[t_1, \ldots, t_{k-1}][x, z]` or ``None`` See also ======== _rational_function_reconstruction """ h = ring.zero for monom, coeff in hm.iterterms(): if k == 0: coeffh = _rational_function_reconstruction(coeff, p, m) if not coeffh: return None else: coeffh = ring.domain.zero for mon, c in coeff.drop_to_ground(k).iterterms(): ch = _rational_function_reconstruction(c, p, m) if not ch: return None coeffh[mon] = ch h[monom] = coeffh return h def _gf_gcdex(f, g, p): r""" Extended Euclidean Algorithm for two univariate polynomials over `\mathbb Z_p`. Returns polynomials `s, t` and `h`, such that `h` is the GCD of `f` and `g` and `sf + tg = h \; \mathrm{mod} \, p`. """ ring = f.ring s, t, h = gf_gcdex(f.to_dense(), g.to_dense(), p, ring.domain) return ring.from_dense(s), ring.from_dense(t), ring.from_dense(h) def _trunc(f, minpoly, p): r""" Compute the reduced representation of a polynomial `f` in `\mathbb Z_p[z] / (\check m_{\alpha}(z))[x]` Parameters ========== f : PolyElement polynomial in `\mathbb Z[x, z]` minpoly : PolyElement polynomial `\check m_{\alpha} \in \mathbb Z[z]`, not necessarily irreducible p : Integer prime number, modulus of `\mathbb Z_p` Returns ======= ftrunc : PolyElement polynomial in `\mathbb Z[x, z]`, reduced modulo `\check m_{\alpha}(z)` and `p` """ ring = f.ring minpoly = minpoly.set_ring(ring) p_ = ring.ground_new(p) return f.trunc_ground(p).rem([minpoly, p_]).trunc_ground(p) def _euclidean_algorithm(f, g, minpoly, p): r""" Compute the monic GCD of two univariate polynomials in `\mathbb{Z}_p[z]/(\check m_{\alpha}(z))[x]` with the Euclidean Algorithm. In general, `\check m_{\alpha}(z)` is not irreducible, so it is possible that some leading coefficient is not invertible modulo `\check m_{\alpha}(z)`. In that case ``None`` is returned. Parameters ========== f, g : PolyElement polynomials in `\mathbb Z[x, z]` minpoly : PolyElement polynomial in `\mathbb Z[z]`, not necessarily irreducible p : Integer prime number, modulus of `\mathbb Z_p` Returns ======= h : PolyElement GCD of `f` and `g` in `\mathbb Z[z, x]` or ``None``, coefficients are in `\left[ -\frac{p-1} 2, \frac{p-1} 2 \right]` """ ring = f.ring f = _trunc(f, minpoly, p) g = _trunc(g, minpoly, p) while g: rem = f deg = g.degree(0) # degree in x lcinv, _, gcd = _gf_gcdex(ring.dmp_LC(g), minpoly, p) if not gcd == 1: return None while True: degrem = rem.degree(0) # degree in x if degrem < deg: break quo = (lcinv * ring.dmp_LC(rem)).set_ring(ring) rem = _trunc(rem - g.mul_monom((degrem - deg, 0))*quo, minpoly, p) f = g g = rem lcfinv = _gf_gcdex(ring.dmp_LC(f), minpoly, p)[0].set_ring(ring) return _trunc(f * lcfinv, minpoly, p) def _trial_division(f, h, minpoly, p=None): r""" Check if `h` divides `f` in `\mathbb K[t_1, \ldots, t_k][z]/(m_{\alpha}(z))`, where `\mathbb K` is either `\mathbb Q` or `\mathbb Z_p`. This algorithm is based on pseudo division and does not use any fractions. By default `\mathbb K` is `\mathbb Q`, if a prime number `p` is given, `\mathbb Z_p` is chosen instead. Parameters ========== f, h : PolyElement polynomials in `\mathbb Z[t_1, \ldots, t_k][x, z]` minpoly : PolyElement polynomial `m_{\alpha}(z)` in `\mathbb Z[t_1, \ldots, t_k][z]` p : Integer or None if `p` is given, `\mathbb K` is set to `\mathbb Z_p` instead of `\mathbb Q`, default is ``None`` Returns ======= rem : PolyElement remainder of `\frac f h` References ========== 1. [Hoeij02]_ """ ring = f.ring domain = ring.domain zxring = ring.clone(symbols=(ring.symbols[1], ring.symbols[0])) minpoly = minpoly.set_ring(ring) rem = f degrem = rem.degree() degh = h.degree() degm = minpoly.degree(1) lch = _LC(h).set_ring(ring) lcm = minpoly.LC while rem and degrem >= degh: # polynomial in Z[t_1, ..., t_k][z] lcrem = _LC(rem).set_ring(ring) rem = rem*lch - h.mul_monom((degrem - degh, 0))*lcrem if p: rem = rem.trunc_ground(p) degrem = rem.degree(1) while rem and degrem >= degm: # polynomial in Z[t_1, ..., t_k][x] lcrem = _LC(rem.set_ring(zxring)).set_ring(ring) rem = rem.mul_ground(lcm) - minpoly.mul_monom((0, degrem - degm))*lcrem if p: rem = rem.trunc_ground(p) degrem = rem.degree(1) degrem = rem.degree() return rem def _evaluate_ground(f, i, a): r""" Evaluate a polynomial `f` at `a` in the `i`-th variable of the ground domain. """ ring = f.ring.clone(domain=f.ring.domain.ring.drop(i)) fa = ring.zero for monom, coeff in f.iterterms(): fa[monom] = coeff.evaluate(i, a) return fa def _func_field_modgcd_p(f, g, minpoly, p): r""" Compute the GCD of two polynomials `f` and `g` in `\mathbb Z_p(t_1, \ldots, t_k)[z]/(\check m_\alpha(z))[x]`. The algorithm reduces the problem step by step by evaluating the polynomials `f` and `g` at `t_k = a` for suitable `a \in \mathbb Z_p` and then calls itself recursively to compute the GCD in `\mathbb Z_p(t_1, \ldots, t_{k-1})[z]/(\check m_\alpha(z))[x]`. If these recursive calls are successful, the GCD over `k` variables is interpolated, otherwise the algorithm returns ``None``. After interpolation, Rational Function Reconstruction is used to obtain the correct coefficients. If this fails, a new evaluation point has to be chosen, otherwise the desired polynomial is obtained by clearing denominators. The result is verified with a fraction free trial division. Parameters ========== f, g : PolyElement polynomials in `\mathbb Z[t_1, \ldots, t_k][x, z]` minpoly : PolyElement polynomial in `\mathbb Z[t_1, \ldots, t_k][z]`, not necessarily irreducible p : Integer prime number, modulus of `\mathbb Z_p` Returns ======= h : PolyElement primitive associate in `\mathbb Z[t_1, \ldots, t_k][x, z]` of the GCD of the polynomials `f` and `g` or ``None``, coefficients are in `\left[ -\frac{p-1} 2, \frac{p-1} 2 \right]` References ========== 1. [Hoeij04]_ """ ring = f.ring domain = ring.domain # Z[t_1, ..., t_k] if isinstance(domain, PolynomialRing): k = domain.ngens else: return _euclidean_algorithm(f, g, minpoly, p) if k == 1: qdomain = domain.ring.to_field() else: qdomain = domain.ring.drop_to_ground(k - 1) qdomain = qdomain.clone(domain=qdomain.domain.ring.to_field()) qring = ring.clone(domain=qdomain) # = Z(t_k)[t_1, ..., t_{k-1}][x, z] n = 1 d = 1 # polynomial in Z_p[t_1, ..., t_k][z] gamma = ring.dmp_LC(f) * ring.dmp_LC(g) # polynomial in Z_p[t_1, ..., t_k] delta = minpoly.LC evalpoints = [] heval = [] LMlist = [] points = set(range(p)) while points: a = random.sample(points, 1)[0] points.remove(a) if k == 1: test = delta.evaluate(k-1, a) % p == 0 else: test = delta.evaluate(k-1, a).trunc_ground(p) == 0 if test: continue gammaa = _evaluate_ground(gamma, k-1, a) minpolya = _evaluate_ground(minpoly, k-1, a) if gammaa.rem([minpolya, gammaa.ring(p)]) == 0: continue fa = _evaluate_ground(f, k-1, a) ga = _evaluate_ground(g, k-1, a) # polynomial in Z_p[x, t_1, ..., t_{k-1}, z]/(minpoly) ha = _func_field_modgcd_p(fa, ga, minpolya, p) if ha is None: d += 1 if d > n: return None continue if ha == 1: return ha LM = [ha.degree()] + [0]*(k-1) if k > 1: for monom, coeff in ha.iterterms(): if monom[0] == LM[0] and coeff.LM > tuple(LM[1:]): LM[1:] = coeff.LM evalpoints_a = [a] heval_a = [ha] if k == 1: m = qring.domain.get_ring().one else: m = qring.domain.domain.get_ring().one t = m.ring.gens[0] for b, hb, LMhb in zip(evalpoints, heval, LMlist): if LMhb == LM: evalpoints_a.append(b) heval_a.append(hb) m *= (t - b) m = m.trunc_ground(p) evalpoints.append(a) heval.append(ha) LMlist.append(LM) n += 1 # polynomial in Z_p[t_1, ..., t_k][x, z] h = _interpolate_multivariate(evalpoints_a, heval_a, ring, k-1, p, ground=True) # polynomial in Z_p(t_k)[t_1, ..., t_{k-1}][x, z] h = _rational_reconstruction_func_coeffs(h, p, m, qring, k-1) if h is None: continue if k == 1: dom = qring.domain.field den = dom.ring.one for coeff in h.itercoeffs(): den = dom.ring.from_dense(gf_lcm(den.to_dense(), coeff.denom.to_dense(), p, dom.domain)) else: dom = qring.domain.domain.field den = dom.ring.one for coeff in h.itercoeffs(): for c in coeff.itercoeffs(): den = dom.ring.from_dense(gf_lcm(den.to_dense(), c.denom.to_dense(), p, dom.domain)) den = qring.domain_new(den.trunc_ground(p)) h = ring(h.mul_ground(den).as_expr()).trunc_ground(p) if not _trial_division(f, h, minpoly, p) and not _trial_division(g, h, minpoly, p): return h return None def _integer_rational_reconstruction(c, m, domain): r""" Reconstruct a rational number `\frac a b` from .. math:: c = \frac a b \; \mathrm{mod} \, m, where `c` and `m` are integers. The algorithm is based on the Euclidean Algorithm. In general, `m` is not a prime number, so it is possible that `b` is not invertible modulo `m`. In that case ``None`` is returned. Parameters ========== c : Integer `c = \frac a b \; \mathrm{mod} \, m` m : Integer modulus, not necessarily prime domain : IntegerRing `a, b, c` are elements of ``domain`` Returns ======= frac : Rational either `\frac a b` in `\mathbb Q` or ``None`` References ========== 1. [Wang81]_ """ if c < 0: c += m r0, s0 = m, domain.zero r1, s1 = c, domain.one bound = sqrt(m / 2) # still correct if replaced by ZZ.sqrt(m // 2) ? while r1 >= bound: quo = r0 // r1 r0, r1 = r1, r0 - quo*r1 s0, s1 = s1, s0 - quo*s1 if abs(s1) >= bound: return None if s1 < 0: a, b = -r1, -s1 elif s1 > 0: a, b = r1, s1 else: return None field = domain.get_field() return field(a) / field(b) def _rational_reconstruction_int_coeffs(hm, m, ring): r""" Reconstruct every rational coefficient `c_h` of a polynomial `h` in `\mathbb Q[t_1, \ldots, t_k][x, z]` from the corresponding integer coefficient `c_{h_m}` of a polynomial `h_m` in `\mathbb Z[t_1, \ldots, t_k][x, z]` such that .. math:: c_{h_m} = c_h \; \mathrm{mod} \, m, where `m \in \mathbb Z`. The reconstruction is based on the Euclidean Algorithm. In general, `m` is not a prime number, so it is possible that this fails for some coefficient. In that case ``None`` is returned. Parameters ========== hm : PolyElement polynomial in `\mathbb Z[t_1, \ldots, t_k][x, z]` m : Integer modulus, not necessarily prime ring : PolyRing `\mathbb Q[t_1, \ldots, t_k][x, z]`, `h` will be an element of this ring Returns ======= h : PolyElement reconstructed polynomial in `\mathbb Q[t_1, \ldots, t_k][x, z]` or ``None`` See also ======== _integer_rational_reconstruction """ h = ring.zero if isinstance(ring.domain, PolynomialRing): reconstruction = _rational_reconstruction_int_coeffs domain = ring.domain.ring else: reconstruction = _integer_rational_reconstruction domain = hm.ring.domain for monom, coeff in hm.iterterms(): coeffh = reconstruction(coeff, m, domain) if not coeffh: return None h[monom] = coeffh return h def _func_field_modgcd_m(f, g, minpoly): r""" Compute the GCD of two polynomials in `\mathbb Q(t_1, \ldots, t_k)[z]/(m_{\alpha}(z))[x]` using a modular algorithm. The algorithm computes the GCD of two polynomials `f` and `g` by calculating the GCD in `\mathbb Z_p(t_1, \ldots, t_k)[z] / (\check m_{\alpha}(z))[x]` for suitable primes `p` and the primitive associate `\check m_{\alpha}(z)` of `m_{\alpha}(z)`. Then the coefficients are reconstructed with the Chinese Remainder Theorem and Rational Reconstruction. To compute the GCD over `\mathbb Z_p(t_1, \ldots, t_k)[z] / (\check m_{\alpha})[x]`, the recursive subroutine ``_func_field_modgcd_p`` is used. To verify the result in `\mathbb Q(t_1, \ldots, t_k)[z] / (m_{\alpha}(z))[x]`, a fraction free trial division is used. Parameters ========== f, g : PolyElement polynomials in `\mathbb Z[t_1, \ldots, t_k][x, z]` minpoly : PolyElement irreducible polynomial in `\mathbb Z[t_1, \ldots, t_k][z]` Returns ======= h : PolyElement the primitive associate in `\mathbb Z[t_1, \ldots, t_k][x, z]` of the GCD of `f` and `g` Examples ======== >>> from sympy.polys.modulargcd import _func_field_modgcd_m >>> from sympy.polys import ring, ZZ >>> R, x, z = ring('x, z', ZZ) >>> minpoly = (z**2 - 2).drop(0) >>> f = x**2 + 2*x*z + 2 >>> g = x + z >>> _func_field_modgcd_m(f, g, minpoly) x + z >>> D, t = ring('t', ZZ) >>> R, x, z = ring('x, z', D) >>> minpoly = (z**2-3).drop(0) >>> f = x**2 + (t + 1)*x*z + 3*t >>> g = x*z + 3*t >>> _func_field_modgcd_m(f, g, minpoly) x + t*z References ========== 1. [Hoeij04]_ See also ======== _func_field_modgcd_p """ ring = f.ring domain = ring.domain if isinstance(domain, PolynomialRing): k = domain.ngens QQdomain = domain.ring.clone(domain=domain.domain.get_field()) QQring = ring.clone(domain=QQdomain) else: k = 0 QQring = ring.clone(domain=ring.domain.get_field()) cf, f = f.primitive() cg, g = g.primitive() # polynomial in Z[t_1, ..., t_k][z] gamma = ring.dmp_LC(f) * ring.dmp_LC(g) # polynomial in Z[t_1, ..., t_k] delta = minpoly.LC p = 1 primes = [] hplist = [] LMlist = [] while True: p = nextprime(p) if gamma.trunc_ground(p) == 0: continue if k == 0: test = (delta % p == 0) else: test = (delta.trunc_ground(p) == 0) if test: continue fp = f.trunc_ground(p) gp = g.trunc_ground(p) minpolyp = minpoly.trunc_ground(p) hp = _func_field_modgcd_p(fp, gp, minpolyp, p) if hp is None: continue if hp == 1: return ring.one LM = [hp.degree()] + [0]*k if k > 0: for monom, coeff in hp.iterterms(): if monom[0] == LM[0] and coeff.LM > tuple(LM[1:]): LM[1:] = coeff.LM hm = hp m = p for q, hq, LMhq in zip(primes, hplist, LMlist): if LMhq == LM: hm = _chinese_remainder_reconstruction_multivariate(hq, hm, q, m) m *= q primes.append(p) hplist.append(hp) LMlist.append(LM) hm = _rational_reconstruction_int_coeffs(hm, m, QQring) if hm is None: continue if k == 0: h = hm.clear_denoms()[1] else: den = domain.domain.one for coeff in hm.itercoeffs(): den = domain.domain.lcm(den, coeff.clear_denoms()[0]) h = hm.mul_ground(den) # convert back to Z[t_1, ..., t_k][x, z] from Q[t_1, ..., t_k][x, z] h = h.set_ring(ring) h = h.primitive()[1] if not (_trial_division(f.mul_ground(cf), h, minpoly) or _trial_division(g.mul_ground(cg), h, minpoly)): return h def _to_ZZ_poly(f, ring): r""" Compute an associate of a polynomial `f \in \mathbb Q(\alpha)[x_0, \ldots, x_{n-1}]` in `\mathbb Z[x_1, \ldots, x_{n-1}][z] / (\check m_{\alpha}(z))[x_0]`, where `\check m_{\alpha}(z) \in \mathbb Z[z]` is the primitive associate of the minimal polynomial `m_{\alpha}(z)` of `\alpha` over `\mathbb Q`. Parameters ========== f : PolyElement polynomial in `\mathbb Q(\alpha)[x_0, \ldots, x_{n-1}]` ring : PolyRing `\mathbb Z[x_1, \ldots, x_{n-1}][x_0, z]` Returns ======= f_ : PolyElement associate of `f` in `\mathbb Z[x_1, \ldots, x_{n-1}][x_0, z]` """ f_ = ring.zero if isinstance(ring.domain, PolynomialRing): domain = ring.domain.domain else: domain = ring.domain den = domain.one for coeff in f.itercoeffs(): for c in coeff.rep: if c: den = domain.lcm(den, c.denominator) for monom, coeff in f.iterterms(): coeff = coeff.rep m = ring.domain.one if isinstance(ring.domain, PolynomialRing): m = m.mul_monom(monom[1:]) n = len(coeff) for i in xrange(n): if coeff[i]: c = domain(coeff[i] * den) * m if (monom[0], n-i-1) not in f_: f_[(monom[0], n-i-1)] = c else: f_[(monom[0], n-i-1)] += c return f_ def _to_ANP_poly(f, ring): r""" Convert a polynomial `f \in \mathbb Z[x_1, \ldots, x_{n-1}][z]/(\check m_{\alpha}(z))[x_0]` to a polynomial in `\mathbb Q(\alpha)[x_0, \ldots, x_{n-1}]`, where `\check m_{\alpha}(z) \in \mathbb Z[z]` is the primitive associate of the minimal polynomial `m_{\alpha}(z)` of `\alpha` over `\mathbb Q`. Parameters ========== f : PolyElement polynomial in `\mathbb Z[x_1, \ldots, x_{n-1}][x_0, z]` ring : PolyRing `\mathbb Q(\alpha)[x_0, \ldots, x_{n-1}]` Returns ======= f_ : PolyElement polynomial in `\mathbb Q(\alpha)[x_0, \ldots, x_{n-1}]` """ domain = ring.domain f_ = ring.zero if isinstance(f.ring.domain, PolynomialRing): for monom, coeff in f.iterterms(): for mon, coef in coeff.iterterms(): m = (monom[0],) + mon c = domain([domain.domain(coef)] + [0]*monom[1]) if m not in f_: f_[m] = c else: f_[m] += c else: for monom, coeff in f.iterterms(): m = (monom[0],) c = domain([domain.domain(coeff)] + [0]*monom[1]) if m not in f_: f_[m] = c else: f_[m] += c return f_ def _minpoly_from_dense(minpoly, ring): r""" Change representation of the minimal polynomial from ``DMP`` to ``PolyElement`` for a given ring. """ minpoly_ = ring.zero for monom, coeff in minpoly.terms(): minpoly_[monom] = ring.domain(coeff) return minpoly_ def _primitive_in_x0(f): r""" Compute the content in `x_0` and the primitive part of a polynomial `f` in `\mathbb Q(\alpha)[x_0, x_1, \ldots, x_{n-1}] \cong \mathbb Q(\alpha)[x_1, \ldots, x_{n-1}][x_0]`. """ fring = f.ring ring = fring.drop_to_ground(*xrange(1, fring.ngens)) dom = ring.domain.ring f_ = ring(f.as_expr()) cont = dom.zero for coeff in f_.itercoeffs(): cont = func_field_modgcd(cont, coeff)[0] if cont == dom.one: return cont, f return cont, f.quo(cont.set_ring(fring)) # TODO: add support for algebraic function fields def func_field_modgcd(f, g): r""" Compute the GCD of two polynomials `f` and `g` in `\mathbb Q(\alpha)[x_0, \ldots, x_{n-1}]` using a modular algorithm. The algorithm first computes the primitive associate `\check m_{\alpha}(z)` of the minimal polynomial `m_{\alpha}` in `\mathbb{Z}[z]` and the primitive associates of `f` and `g` in `\mathbb{Z}[x_1, \ldots, x_{n-1}][z]/(\check m_{\alpha})[x_0]`. Then it computes the GCD in `\mathbb Q(x_1, \ldots, x_{n-1})[z]/(m_{\alpha}(z))[x_0]`. This is done by calculating the GCD in `\mathbb{Z}_p(x_1, \ldots, x_{n-1})[z]/(\check m_{\alpha}(z))[x_0]` for suitable primes `p` and then reconstructing the coefficients with the Chinese Remainder Theorem and Rational Reconstuction. The GCD over `\mathbb{Z}_p(x_1, \ldots, x_{n-1})[z]/(\check m_{\alpha}(z))[x_0]` is computed with a recursive subroutine, which evaluates the polynomials at `x_{n-1} = a` for suitable evaluation points `a \in \mathbb Z_p` and then calls itself recursively until the ground domain does no longer contain any parameters. For `\mathbb{Z}_p[z]/(\check m_{\alpha}(z))[x_0]` the Euclidean Algorithm is used. The results of those recursive calls are then interpolated and Rational Function Reconstruction is used to obtain the correct coefficients. The results, both in `\mathbb Q(x_1, \ldots, x_{n-1})[z]/(m_{\alpha}(z))[x_0]` and `\mathbb{Z}_p(x_1, \ldots, x_{n-1})[z]/(\check m_{\alpha}(z))[x_0]`, are verified by a fraction free trial division. Apart from the above GCD computation some GCDs in `\mathbb Q(\alpha)[x_1, \ldots, x_{n-1}]` have to be calculated, because treating the polynomials as univariate ones can result in a spurious content of the GCD. For this ``func_field_modgcd`` is called recursively. Parameters ========== f, g : PolyElement polynomials in `\mathbb Q(\alpha)[x_0, \ldots, x_{n-1}]` Returns ======= h : PolyElement monic GCD of the polynomials `f` and `g` cff : PolyElement cofactor of `f`, i.e. `\frac f h` cfg : PolyElement cofactor of `g`, i.e. `\frac g h` Examples ======== >>> from sympy.polys.modulargcd import func_field_modgcd >>> from sympy.polys import AlgebraicField, QQ, ring >>> from sympy import sqrt >>> A = AlgebraicField(QQ, sqrt(2)) >>> R, x = ring('x', A) >>> f = x**2 - 2 >>> g = x + sqrt(2) >>> h, cff, cfg = func_field_modgcd(f, g) >>> h == x + sqrt(2) True >>> cff * h == f True >>> cfg * h == g True >>> R, x, y = ring('x, y', A) >>> f = x**2 + 2*sqrt(2)*x*y + 2*y**2 >>> g = x + sqrt(2)*y >>> h, cff, cfg = func_field_modgcd(f, g) >>> h == x + sqrt(2)*y True >>> cff * h == f True >>> cfg * h == g True >>> f = x + sqrt(2)*y >>> g = x + y >>> h, cff, cfg = func_field_modgcd(f, g) >>> h == R.one True >>> cff * h == f True >>> cfg * h == g True References ========== 1. [Hoeij04]_ """ ring = f.ring domain = ring.domain n = ring.ngens assert ring == g.ring and domain.is_Algebraic result = _trivial_gcd(f, g) if result is not None: return result z = Dummy('z') ZZring = ring.clone(symbols=ring.symbols + (z,), domain=domain.domain.get_ring()) if n == 1: f_ = _to_ZZ_poly(f, ZZring) g_ = _to_ZZ_poly(g, ZZring) minpoly = ZZring.drop(0).from_dense(domain.mod.rep) h = _func_field_modgcd_m(f_, g_, minpoly) h = _to_ANP_poly(h, ring) else: # contx0f in Q(a)[x_1, ..., x_{n-1}], f in Q(a)[x_0, ..., x_{n-1}] contx0f, f = _primitive_in_x0(f) contx0g, g = _primitive_in_x0(g) contx0h = func_field_modgcd(contx0f, contx0g)[0] ZZring_ = ZZring.drop_to_ground(*xrange(1, n)) f_ = _to_ZZ_poly(f, ZZring_) g_ = _to_ZZ_poly(g, ZZring_) minpoly = _minpoly_from_dense(domain.mod, ZZring_.drop(0)) h = _func_field_modgcd_m(f_, g_, minpoly) h = _to_ANP_poly(h, ring) contx0h_, h = _primitive_in_x0(h) h *= contx0h.set_ring(ring) f *= contx0f.set_ring(ring) g *= contx0g.set_ring(ring) h = h.quo_ground(h.LC) return h, f.quo(h), g.quo(h) sympy-0.7.4.1/sympy/polys/euclidtools.py0000644000175000017500000012173112253362407020500 0ustar georgeskgeorgesk"""Euclidean algorithms, GCDs, LCMs and polynomial remainder sequences. """ from __future__ import print_function, division 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.ntheory import nextprime from sympy.core.compatibility import xrange 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 import ring, QQ >>> R, x = ring("x", QQ) >>> f = x**4 - 2*x**3 - 6*x**2 + 12*x + 15 >>> g = x**3 + x**2 - 4*x - 4 >>> R.dup_half_gcdex(f, g) (-1/5*x + 3/5, x + 1) """ if not K.has_Field: 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 def dmp_half_gcdex(f, g, u, K): """ Half extended Euclidean algorithm in `F[X]`. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) """ 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 import ring, QQ >>> R, x = ring("x", QQ) >>> f = x**4 - 2*x**3 - 6*x**2 + 12*x + 15 >>> g = x**3 + x**2 - 4*x - 4 >>> R.dup_gcdex(f, g) (-1/5*x + 3/5, 1/5*x**2 - 6/5*x + 2, x + 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 def dmp_gcdex(f, g, u, K): """ Extended Euclidean algorithm in `F[X]`. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) """ 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 import ring, QQ >>> R, x = ring("x", QQ) >>> f = x**2 - 1 >>> g = 2*x - 1 >>> h = x - 1 >>> R.dup_invert(f, g) -4/3 >>> R.dup_invert(f, h) 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") def dmp_invert(f, g, u, K): """ Compute multiplicative inverse of `f` modulo `g` in `F[X]`. Examples ======== >>> from sympy.polys import ring, QQ >>> R, x = ring("x", QQ) """ 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 import ring, QQ >>> R, x = ring("x", QQ) >>> f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 >>> g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 >>> prs = R.dup_euclidean_prs(f, g) >>> prs[0] x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 >>> prs[1] 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 >>> prs[2] -5/9*x**4 + 1/9*x**2 - 1/3 >>> prs[3] -117/25*x**2 - 9*x + 441/25 >>> prs[4] 233150/19773*x - 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 def dmp_euclidean_prs(f, g, u, K): """ Euclidean polynomial remainder sequence (PRS) in `K[X]`. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) """ 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 >>> g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 >>> prs = R.dup_primitive_prs(f, g) >>> prs[0] x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 >>> prs[1] 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 >>> prs[2] -5*x**4 + x**2 - 3 >>> prs[3] 13*x**2 + 25*x - 49 >>> prs[4] 4663*x - 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 def dmp_primitive_prs(f, g, u, K): """ Primitive polynomial remainder sequence (PRS) in `K[X]`. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) """ if not u: return dup_primitive_prs(f, g, K) else: raise MultivariatePolynomialError(f, g) 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_inner_subresultants(x**2 + 1, x**2 - 1) ([x**2 + 1, x**2 - 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_subresultants(x**2 + 1, x**2 - 1) [x**2 + 1, x**2 - 1, -2] """ return dup_inner_subresultants(f, g, K)[0] def dup_prs_resultant(f, g, K): """ Resultant algorithm in `K[x]` using subresultant PRS. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_prs_resultant(x**2 + 1, x**2 - 1) (4, [x**2 + 1, x**2 - 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_resultant(x**2 + 1, x**2 - 1) 4 """ if includePRS: return dup_prs_resultant(f, g, K) return dup_prs_resultant(f, g, K)[0] def dmp_inner_subresultants(f, g, u, K): """ Subresultant PRS algorithm in `K[X]`. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> f = 3*x**2*y - y**3 - 4 >>> g = x**2 + x*y**3 - 9 >>> a = 3*x*y**4 + y**3 - 27*y + 4 >>> b = -3*y**10 - 12*y**7 + y**6 - 54*y**4 + 8*y**3 + 729*y**2 - 216*y + 16 >>> prs = [f, g, a, b] >>> beta = [[-1], [1], [9, 0, 0, 0, 0, 0, 0, 0, 0]] >>> delta = [0, 1, 1] >>> R.dmp_inner_subresultants(f, g) == (prs, beta, delta) 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 def dmp_subresultants(f, g, u, K): """ Computes subresultant PRS of two polynomials in `K[X]`. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> f = 3*x**2*y - y**3 - 4 >>> g = x**2 + x*y**3 - 9 >>> a = 3*x*y**4 + y**3 - 27*y + 4 >>> b = -3*y**10 - 12*y**7 + y**6 - 54*y**4 + 8*y**3 + 729*y**2 - 216*y + 16 >>> R.dmp_subresultants(f, g) == [f, g, a, b] True """ return dmp_inner_subresultants(f, g, u, K)[0] def dmp_prs_resultant(f, g, u, K): """ Resultant algorithm in `K[X]` using subresultant PRS. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> f = 3*x**2*y - y**3 - 4 >>> g = x**2 + x*y**3 - 9 >>> a = 3*x*y**4 + y**3 - 27*y + 4 >>> b = -3*y**10 - 12*y**7 + y**6 - 54*y**4 + 8*y**3 + 729*y**2 - 216*y + 16 >>> res, prs = R.dmp_prs_resultant(f, g) >>> res == b # resultant has n-1 variables False >>> res == b.drop(x) True >>> prs == [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 def dmp_zz_modular_resultant(f, g, p, u, K): """ Compute resultant of `f` and `g` modulo a prime `p`. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> f = x + y + 2 >>> g = 2*x*y + x + 3 >>> R.dmp_zz_modular_resultant(f, g, 5) -2*y**2 + 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) def dmp_zz_collins_resultant(f, g, u, K): """ Collins's modular resultant algorithm in `Z[X]`. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> f = x + y + 2 >>> g = 2*x*y + x + 3 >>> R.dmp_zz_collins_resultant(f, g) -2*y**2 - 5*y + 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 def dmp_qq_collins_resultant(f, g, u, K0): """ Collins's modular resultant algorithm in `Q[X]`. Examples ======== >>> from sympy.polys import ring, QQ >>> R, x,y = ring("x,y", QQ) >>> f = QQ(1,2)*x + y + QQ(2,3) >>> g = 2*x*y + x + 3 >>> R.dmp_qq_collins_resultant(f, g) -2*y**2 - 7/3*y + 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) def dmp_resultant(f, g, u, K, includePRS=False): """ Computes resultant of two polynomials in `K[X]`. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> f = 3*x**2*y - y**3 - 4 >>> g = x**2 + x*y**3 - 9 >>> R.dmp_resultant(f, g) -3*y**10 - 12*y**7 + y**6 - 54*y**4 + 8*y**3 + 729*y**2 - 216*y + 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] def dup_discriminant(f, K): """ Computes discriminant of a polynomial in `K[x]`. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_discriminant(x**2 + 2*x + 3) -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)) def dmp_discriminant(f, u, K): """ Computes discriminant of a polynomial in `K[X]`. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y,z,t = ring("x,y,z,t", ZZ) >>> R.dmp_discriminant(x**2*y + x*z + t) -4*y*t + z**2 """ 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 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 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 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_rr_prs_gcd(x**2 - 1, x**2 - 3*x + 2) (x - 1, x + 1, x - 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 import ring, QQ >>> R, x = ring("x", QQ) >>> R.dup_ff_prs_gcd(x**2 - 1, x**2 - 3*x + 2) (x - 1, x + 1, x - 2) """ 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 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 import ring, ZZ >>> R, x,y, = ring("x,y", ZZ) >>> f = x**2 + 2*x*y + y**2 >>> g = x**2 + x*y >>> R.dmp_rr_prs_gcd(f, g) (x + y, x + y, x) """ 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 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 import ring, QQ >>> R, x,y, = ring("x,y", QQ) >>> f = QQ(1,2)*x**2 + x*y + QQ(1,2)*y**2 >>> g = x**2 + x*y >>> R.dmp_ff_prs_gcd(f, g) (x + y, 1/2*x + 1/2*y, x) """ 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 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_zz_heu_gcd(x**2 - 1, x**2 - 3*x + 2) (x - 1, x + 1, x - 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 xrange(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') 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 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 import ring, ZZ >>> R, x,y, = ring("x,y", ZZ) >>> f = x**2 + 2*x*y + y**2 >>> g = x**2 + x*y >>> R.dmp_zz_heu_gcd(f, g) (x + y, x + y, x) 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 xrange(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 import ring, QQ >>> R, x = ring("x", QQ) >>> f = QQ(1,2)*x**2 + QQ(7,4)*x + QQ(3,2) >>> g = QQ(1,2)*x**2 + x >>> R.dup_qq_heu_gcd(f, g) (x + 2, 1/2*x + 3/4, 1/2*x) """ 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 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 import ring, QQ >>> R, x,y, = ring("x,y", QQ) >>> f = QQ(1,4)*x**2 + x*y + y**2 >>> g = QQ(1,2)*x**2 + x*y >>> R.dmp_qq_heu_gcd(f, g) (x + 2*y, 1/4*x + 1/2*y, 1/2*x) """ 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_inner_gcd(x**2 - 1, x**2 - 3*x + 2) (x - 1, x + 1, x - 2) """ if not K.is_Exact: try: exact = K.get_exact() except DomainError: return [K.one], f, g f = dup_convert(f, K, exact) g = dup_convert(g, K, exact) h, cff, cfg = dup_inner_gcd(f, g, exact) h = dup_convert(h, exact, K) cff = dup_convert(cff, exact, K) cfg = dup_convert(cfg, exact, K) return h, cff, cfg elif K.has_Field: 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) def _dmp_inner_gcd(f, g, u, K): """Helper function for `dmp_inner_gcd()`. """ if not K.is_Exact: try: exact = K.get_exact() except DomainError: return dmp_one(u, K), f, g f = dmp_convert(f, u, K, exact) g = dmp_convert(g, u, K, exact) h, cff, cfg = _dmp_inner_gcd(f, g, u, exact) h = dmp_convert(h, u, exact, K) cff = dmp_convert(cff, u, exact, K) cfg = dmp_convert(cfg, u, exact, K) return h, cff, cfg elif K.has_Field: 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) 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 import ring, ZZ >>> R, x,y, = ring("x,y", ZZ) >>> f = x**2 + 2*x*y + y**2 >>> g = x**2 + x*y >>> R.dmp_inner_gcd(f, g) (x + y, x + y, x) """ 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_gcd(x**2 - 1, x**2 - 3*x + 2) x - 1 """ return dup_inner_gcd(f, g, K)[0] def dmp_gcd(f, g, u, K): """ Computes polynomial GCD of `f` and `g` in `K[X]`. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y, = ring("x,y", ZZ) >>> f = x**2 + 2*x*y + y**2 >>> g = x**2 + x*y >>> R.dmp_gcd(f, g) x + y """ 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_rr_lcm(x**2 - 1, x**2 - 3*x + 2) x**3 - 2*x**2 - x + 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 import ring, QQ >>> R, x = ring("x", QQ) >>> f = QQ(1,2)*x**2 + QQ(7,4)*x + QQ(3,2) >>> g = QQ(1,2)*x**2 + x >>> R.dup_ff_lcm(f, g) x**3 + 7/2*x**2 + 3*x """ 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_lcm(x**2 - 1, x**2 - 3*x + 2) x**3 - 2*x**2 - x + 2 """ if K.has_Field: return dup_ff_lcm(f, g, K) else: return dup_rr_lcm(f, g, K) def dmp_rr_lcm(f, g, u, K): """ Computes polynomial LCM over a ring in `K[X]`. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y, = ring("x,y", ZZ) >>> f = x**2 + 2*x*y + y**2 >>> g = x**2 + x*y >>> R.dmp_rr_lcm(f, g) x**3 + 2*x**2*y + x*y**2 """ 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) def dmp_ff_lcm(f, g, u, K): """ Computes polynomial LCM over a field in `K[X]`. Examples ======== >>> from sympy.polys import ring, QQ >>> R, x,y, = ring("x,y", QQ) >>> f = QQ(1,4)*x**2 + x*y + y**2 >>> g = QQ(1,2)*x**2 + x*y >>> R.dmp_ff_lcm(f, g) x**3 + 4*x**2*y + 4*x*y**2 """ h = dmp_quo(dmp_mul(f, g, u, K), dmp_gcd(f, g, u, K), u, K) return dmp_ground_monic(h, u, K) def dmp_lcm(f, g, u, K): """ Computes polynomial LCM of `f` and `g` in `K[X]`. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y, = ring("x,y", ZZ) >>> f = x**2 + 2*x*y + y**2 >>> g = x**2 + x*y >>> R.dmp_lcm(f, g) x**3 + 2*x**2*y + x*y**2 """ if not u: return dup_lcm(f, g, K) if K.has_Field: return dmp_ff_lcm(f, g, u, K) else: return dmp_rr_lcm(f, g, u, K) def dmp_content(f, u, K): """ Returns GCD of multivariate coefficients. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y, = ring("x,y", ZZ) >>> R.dmp_content(2*x*y + 6*x + 4*y + 12) 2*y + 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 def dmp_primitive(f, u, K): """ Returns multivariate content and a primitive polynomial. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y, = ring("x,y", ZZ) >>> R.dmp_primitive(2*x*y + 6*x + 4*y + 12) (2*y + 6, x + 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 import ring, ZZ >>> R, x = ring("x", ZZ) >>> R.dup_cancel(2*x**2 - 2, x**2 - 2*x + 1) (2*x + 2, x - 1) """ return dmp_cancel(f, g, 0, K, include=include) def dmp_cancel(f, g, u, K, include=True): """ Cancel common factors in a rational function `f/g`. Examples ======== >>> from sympy.polys import ring, ZZ >>> R, x,y = ring("x,y", ZZ) >>> R.dmp_cancel(2*x**2 - 2, x**2 - 2*x + 1) (2*x + 2, x - 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: _, cp, cq = K.cofactors(cp, cq) 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 sympy-0.7.4.1/sympy/polys/groebnertools.py0000644000175000017500000005536012253362407021042 0ustar georgeskgeorgesk"""Groebner bases algorithms. """ from __future__ import print_function, division from sympy.polys.monomials import monomial_mul, monomial_div, monomial_lcm, monomial_divides, term_div from sympy.polys.orderings import lex from sympy.polys.polyerrors import DomainError from sympy.polys.polyconfig import query from sympy.core.symbol import Dummy from sympy.core.compatibility import xrange def groebner(seq, ring, 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('groebner') _groebner_methods = { 'buchberger': _buchberger, 'f5b': _f5b, } try: _groebner = _groebner_methods[method] except KeyError: raise ValueError("'%s' is not a valid Groebner bases algorithm (valid are 'buchberger' and 'f5b')" % method) domain, orig = ring.domain, None if not domain.has_Field or not domain.has_assoc_Field: try: orig, ring = ring, ring.clone(domain=domain.get_field()) except DomainError: raise DomainError("can't compute a Groebner basis over %s" % domain) else: seq = [ s.set_ring(ring) for s in seq ] G = _groebner(seq, ring) if orig is not None: G = [ g.clear_denoms()[1].set_ring(orig) for g in G ] return G def _buchberger(f, ring): """ 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. """ order = ring.order domain = ring.domain monomial_mul = ring.monomial_mul monomial_div = ring.monomial_div monomial_lcm = ring.monomial_lcm def select(P): # normal selection strategy # select the pair with minimum LCM(LM(f), LM(g)) pr = min(P, key=lambda pair: order(monomial_lcm(f[pair[0]].LM, f[pair[1]].LM))) return pr def normal(g, J): h = g.rem([ f[j] for j in J ]) if not h: return None else: h = h.monic() if not h in I: I[h] = len(f) f.append(h) return h.LM, 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 = h.LM # 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 = g.LM LCMhg = monomial_lcm(mh, mg) def lcm_divides(ip): # LCM(LM(h), LM(p)) divides LCM(LM(h), LM(g)) m = monomial_lcm(mh, f[ip].LM) 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 = f[ig].LM 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 = f[ig1].LM mg2 = f[ig2].LM 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 = f[ig].LM 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 = p.rem(f[:i]) if r: f1.append(r.monic()) if f == f1: break 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 h = min([f[x] for x in F], key=lambda f: order(f.LM)) 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 = spoly(f[ig1], f[ig2], ring) # ordering divisors is on average more efficient [Cox] page 111 G1 = sorted(G, key=lambda g: order(f[g].LM)) 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 = [f[ig] for ig in Gr] # order according to the monomial ordering Gr = sorted(Gr, key=lambda f: order(f.LM), reverse=True) return Gr def spoly(p1, p2, ring): """ 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 = p1.LM LM2 = p2.LM LCM12 = ring.monomial_lcm(LM1, LM2) m1 = ring.monomial_div(LCM12, LM1) m2 = ring.monomial_div(LCM12, LM2) s1 = p1.mul_monom(m1) s2 = p2.mul_monom(m2) s = s1 - s2 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, order): """ 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. order u > v otherwise """ if u[1] > v[1]: return -1 if u[1] == v[1]: #if u[0] == v[0]: # return 0 if order(u[0]) < order(v[0]): return -1 return 1 def sig_key(s, order): """ 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], order(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): """ 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), Polyn(f).ring.order) < 0: max_poly = g else: max_poly = f ret = Polyn(f) - Polyn(g) return lbp(Sign(max_poly), ret, Num(max_poly)) def lbp_mul_term(f, cx): """ 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]), Polyn(f).mul_term(cx), Num(f)) def lbp_cmp(f, g): """ 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), Polyn(f).ring.order) == -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): """ Key for comparing two labeled polynomials. """ return (sig_key(Sign(f), Polyn(f).ring.order), -Num(f)) # algorithm and helper functions def critical_pair(f, g, ring): """ 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. """ domain = ring.domain ltf = Polyn(f).LT ltg = Polyn(g).LT lt = (monomial_lcm(ltf[0], ltg[0]), domain.one) um = term_div(lt, ltf, domain) vm = term_div(lt, ltg, domain) # The full information is not needed (now), so only the product # with the leading term is considered: fr = lbp_mul_term(lbp(Sign(f), Polyn(f).leading_term(), Num(f)), um) gr = lbp_mul_term(lbp(Sign(g), Polyn(g).leading_term(), Num(g)), vm) # return in proper order, such that the S-polynomial is just # u_first * f_first - u_second * f_second: if lbp_cmp(fr, gr) == -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): """ 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 """ zero = Polyn(c[2]).ring.zero c0 = lbp(c[0], zero, Num(c[2])) d0 = lbp(d[0], zero, Num(d[2])) r = lbp_cmp(c0, d0) if r == -1: return -1 if r == 0: c1 = lbp(c[3], zero, Num(c[5])) d1 = lbp(d[3], zero, Num(d[5])) r = lbp_cmp(c1, d1) if r == -1: return -1 #if r == 0: # return 0 return 1 def cp_key(c, ring): """ Key for comparing critical pairs. """ return (lbp_key(lbp(c[0], ring.zero, Num(c[2]))), lbp_key(lbp(c[3], ring.zero, Num(c[5])))) def s_poly(cp): """ 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]), lbp_mul_term(cp[5], cp[4])) def is_rewritable_or_comparable(sign, num, B): """ 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(Polyn(h).LM, sign[0]): return True # rewritable if sign[1] == Sign(h)[1]: if num < Num(h): if monomial_divides(Sign(h)[0], sign[0]): return True return False def f5_reduce(f, B): """ 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 need not be F5-reducible, e.g.: >>> from sympy.polys.groebnertools import lbp, sig, f5_reduce, Polyn >>> from sympy.polys import ring, QQ, lex >>> R, x,y,z = ring("x,y,z", QQ, lex) >>> f = lbp(sig((1, 1, 1), 4), x, 3) >>> g = lbp(sig((0, 0, 0), 2), x, 2) >>> Polyn(f).rem([Polyn(g)]) 0 >>> f5_reduce(f, [g]) (((1, 1, 1), 4), x, 3) """ order = Polyn(f).ring.order domain = Polyn(f).ring.domain if not Polyn(f): return f while True: g = f for h in B: if Polyn(h): if monomial_divides(Polyn(h).LM, Polyn(f).LM): t = term_div(Polyn(f).LT, Polyn(h).LT, domain) if sig_cmp(sig_mult(Sign(h), t[0]), Sign(f), order) < 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): hp = lbp_mul_term(h, t) f = lbp_sub(f, hp) break if g == f or not Polyn(f): return f def _f5b(F, ring): """ 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 """ order = ring.order domain = ring.domain # reduce polynomials (like in Mario Pernici's implementation) (Becker, Weispfenning, p. 203) B = F while True: F = B B = [] for i in xrange(len(F)): p = F[i] r = p.rem(F[:i]) if r: B.append(r) if F == B: break # basis B = [lbp(sig(ring.zero_monom, i + 1), F[i], i + 1) for i in xrange(len(F))] B.sort(key=lambda f: order(Polyn(f).LM), reverse=True) # critical pairs CP = [critical_pair(B[i], B[j], ring) for i in xrange(len(B)) for j in xrange(i + 1, len(B))] CP.sort(key=lambda cp: cp_key(cp, ring), 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): continue if is_rewritable_or_comparable(cp[3], Num(cp[5]), B): continue s = s_poly(cp) p = f5_reduce(s, B) p = lbp(Sign(p), Polyn(p).monic(), 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]): indices.append(i) elif is_rewritable_or_comparable(cp[3], Num(cp[5]), [p]): 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, ring) if is_rewritable_or_comparable(cp[0], Num(cp[2]), [p]): continue elif is_rewritable_or_comparable(cp[3], Num(cp[5]), [p]): continue CP.append(cp) # sort (other sorting methods/selection strategies were not as successful) CP.sort(key=lambda cp: cp_key(cp, ring), reverse=True) # insert p into B: m = Polyn(p).LM if order(m) <= order(Polyn(B[-1]).LM): B.append(p) else: for i, q in enumerate(B): if order(m) > order(Polyn(q).LM): B.insert(i, p) break k += 1 #print(len(B), len(CP), "%d critical pairs removed" % len(indices)) else: reductions_to_zero += 1 # reduce Groebner basis: H = [Polyn(g).monic() for g in B] H = red_groebner(H, ring) return sorted(H, key=lambda f: order(f.LM), reverse=True) def red_groebner(G, ring): """ 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): """ The actual reduction algorithm. """ Q = [] for i, p in enumerate(P): h = p.rem(P[:i] + P[i + 1:]) if h: Q.append(h) return [p.monic() for p in Q] F = G H = [] while F: f0 = F.pop() if not any(monomial_divides(f.LM, f0.LM) 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) def is_groebner(G, ring): """ Check if G is a Groebner basis. """ for i in xrange(len(G)): for j in xrange(i + 1, len(G)): s = spoly(G[i], G[j]) s = s.rem(G) if s: return False return True def is_minimal(G, ring): """ Checks if G is a minimal Groebner basis. """ order = ring.order domain = ring.domain G.sort(key=lambda g: order(g.LM)) for i, g in enumerate(G): if g.LC != domain.one: return False for h in G[:i] + G[i + 1:]: if monomial_divides(h.LM, g.LM): return False return True def is_reduced(G, ring): """ Checks if G is a reduced Groebner basis. """ order = ring.order domain = ring.domain G.sort(key=lambda g: order(g.LM)) for i, g in enumerate(G): if g.LC != domain.one: return False for term in g: for h in G[:i] + G[i + 1:]: if monomial_divides(h.LM, term[0]): return False return True def groebner_lcm(f, g): """ Computes LCM of two polynomials using Groebner bases. 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]_ """ assert f.ring == g.ring ring = f.ring domain = ring.domain if not f or not g: return ring.zero if len(f) <= 1 and len(g) <= 1: monom = monomial_lcm(f.LM, g.LM) coeff = domain.lcm(f.LC, g.LC) return ring.term_new(monom, coeff) fc, f = f.primitive() gc, g = g.primitive() lcm = domain.lcm(fc, gc) f_terms = [ ((1,) + monom, coeff) for monom, coeff in f.terms() ] g_terms = [ ((0,) + monom, coeff) for monom, coeff in g.terms() ] \ + [ ((1,) + monom,-coeff) for monom, coeff in g.terms() ] t = Dummy("t") t_ring = ring.clone(symbols=(t,) + ring.symbols, order=lex) F = t_ring.from_terms(f_terms) G = t_ring.from_terms(g_terms) basis = groebner([F, G], t_ring) def is_independent(h, j): return all(not monom[j] for monom in h.monoms()) H = [ h for h in basis if is_independent(h, 0) ] h_terms = [ (monom[1:], coeff*lcm) for monom, coeff in H[0].terms() ] h = ring.from_terms(h_terms) return h def groebner_gcd(f, g): """Computes GCD of two polynomials using Groebner bases. """ assert f.ring == g.ring domain = f.ring.domain if not domain.has_Field: fc, f = f.primitive() gc, g = g.primitive() gcd = domain.gcd(fc, gc) H = (f*g).quo([groebner_lcm(f, g)]) assert len(H) == 1 h = H[0] if not domain.has_Field: return gcd*h else: return h.monic() sympy-0.7.4.1/sympy/polys/agca/0000755000175000017500000000000012253362407016466 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/polys/agca/modules.py0000644000175000017500000013126712253362407020522 0ustar georgeskgeorgesk""" 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 __future__ import print_function, division from copy import copy from sympy.polys.polyerrors import CoercionFailed from sympy.polys.orderings import ProductOrder, monomial_key from sympy.polys.domains.field import Field from sympy.polys.agca.ideals import Ideal from sympy.core.compatibility import iterable, reduce # TODO # - module saturation # - module quotient/intersection for quotient rings # - free resoltutions / syzygies # - finding small/minimal generating sets # - ... ########################################################################## ## Abstract base classes ################################################# ########################################################################## 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.old_poly_ring(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 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 def submodule(self, *gens): """Generate a submodule.""" raise NotImplementedError 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__ 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) 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.old_poly_ring(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) def is_zero(self): """Returns True if ``self`` is a zero module.""" raise NotImplementedError def is_submodule(self, other): """Returns True if ``other`` is a submodule of ``self``.""" raise NotImplementedError 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__ 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] 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) 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.old_poly_ring(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 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.old_poly_ring(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 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.old_poly_ring(x).free_module(0).is_zero() True >>> QQ.old_poly_ring(x).free_module(1).is_zero() False """ return self.rank == 0 def basis(self): """ Return a set of basis elements. >>> from sympy.abc import x >>> from sympy import QQ >>> QQ.old_poly_ring(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)) def quotient_module(self, submodule): """ Return a quotient module. >>> from sympy.abc import x >>> from sympy import QQ >>> M = QQ.old_poly_ring(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.old_poly_ring(x).free_module(2) / [[1, x], [x, 2]] QQ[x]**2/<[1, x], [x, 2]> """ return QuotientModule(self.ring, self, submodule) def multiply_ideal(self, other): """ Multiply ``self`` by the ideal ``other``. >>> from sympy.abc import x >>> from sympy import QQ >>> I = QQ.old_poly_ring(x).ideal(x) >>> F = QQ.old_poly_ring(x).free_module(2) >>> F.multiply_ideal(I) <[x, 0], [0, x]> """ return self.submodule(*self.basis()).multiply_ideal(other) def identity_hom(self): """ Return the identity homomorphism on ``self``. >>> from sympy.abc import x >>> from sympy import QQ >>> QQ.old_poly_ring(x).free_module(2).identity_hom() Matrix([ [1, 0], : QQ[x]**2 -> QQ[x]**2 [0, 1]]) """ 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.old_poly_ring(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.old_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.old_poly_ring(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.old_poly_ring(x)/[x**2 + 1]).free_module(3) >>> F (QQ[x]/)**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.quotientring 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.old_poly_ring(x, y)/[x**2 - y**2]).free_module(2).submodule([x, x + y]) >>> M <[x + , x + y + ]> >>> 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.old_poly_ring(x)/[x**2 + 1]).free_module(2) >>> e = F.convert([1, 0]) >>> e [1 + , 0 + ] >>> 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.old_poly_ring(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 ########################################### ########################################################################## 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 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.old_poly_ring(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 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.old_poly_ring(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) 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.old_poly_ring(x, y).free_module(2) >>> S = F.submodule([x*y, x*y]) >>> T = F.submodule([x, x]) >>> S.module_quotient(T) 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) (, [[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) 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.old_poly_ring(x).free_module(1) >>> M = F.submodule([x**2 + x]) # >>> 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) def is_zero(self): """ Return True if ``self`` is a zero module. >>> from sympy.abc import x >>> from sympy import QQ >>> F = QQ.old_poly_ring(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) def submodule(self, *gens): """ Generate a submodule. >>> from sympy.abc import x >>> from sympy import QQ >>> M = QQ.old_poly_ring(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) 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.old_poly_ring(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()) 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.old_poly_ring(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 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.old_poly_ring(x).free_module(2).submodule([1, 0], [1, 1]).syzygy_module().is_zero() True A slightly more interesting example: >>> M = QQ.old_poly_ring(x, y).free_module(2).submodule([x, 2*x], [y, 2*y]) >>> S = QQ.old_poly_ring(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) 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.old_poly_ring(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) 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 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.old_poly_ring(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__ def multiply_ideal(self, I): """ Multiply ``self`` by the ideal ``I``. >>> from sympy.abc import x >>> from sympy import QQ >>> I = QQ.old_poly_ring(x).ideal(x**2) >>> M = QQ.old_poly_ring(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]) 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.old_poly_ring(x).free_module(2).submodule([x, x]).inclusion_hom() Matrix([ [1, 0], : <[x, x]> -> QQ[x]**2 [0, 1]]) """ return self.container.identity_hom().restrict_domain(self) def identity_hom(self): """ Return the identity homomorphism on ``self``. >>> from sympy.abc import x >>> from sympy import QQ >>> QQ.old_poly_ring(x).free_module(2).submodule([x, x]).identity_hom() Matrix([ [1, 0], : <[x, x]> -> <[x, x]> [0, 1]]) """ return self.container.identity_hom().restrict_domain( self).restrict_codomain(self) 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.old_poly_ring(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 used to form the quotient """ 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)] 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.old_poly_ring(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() 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.old_poly_ring(x).free_module(2) / [(1, x)]).submodule([1, 0]) >>> M.quotient_hom() Matrix([ [1, 0], : <[1, 0], [1, x]> -> <[1, 0] + <[1, x]>, [1, x] + <[1, x]>> [0, 1]]) """ 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.old_poly_ring(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 = [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 : = intersection of # {I : | 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.old_poly_ring(x, y)/[x**2 - y**2]).free_module(2).submodule([x, x + y]) >>> M <[x + , x + y + ]> >>> 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) 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 used to form the quotient - 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) 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.old_poly_ring(x).free_module(2) >>> (F/[(1, 0)]).is_zero() False >>> (F/[(1, 0), (0, 1)]).is_zero() True """ return self.base == self.killed_module 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.old_poly_ring(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 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.old_poly_ring(x).free_module(2) / [(x, x)] >>> Q.submodule([x, 0]) <[x, 0] + <[x, x]>> """ return SubQuotientModule(gens, self, **opts) 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.old_poly_ring(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)) def identity_hom(self): """ Return the identity homomorphism on ``self``. >>> from sympy.abc import x >>> from sympy import QQ >>> M = QQ.old_poly_ring(x).free_module(2) / [(1, 2), (1, x)] >>> M.identity_hom() Matrix([ [1, 0], : QQ[x]**2/<[1, 2], [1, x]> -> QQ[x]**2/<[1, 2], [1, x]> [0, 1]]) """ return self.base.identity_hom().quotient_codomain( self.killed_module).quotient_domain(self.killed_module) 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.old_poly_ring(x).free_module(2) / [(1, 2), (1, x)] >>> M.quotient_hom() Matrix([ [1, 0], : QQ[x]**2 -> QQ[x]**2/<[1, 2], [1, x]> [0, 1]]) """ return self.base.identity_hom().quotient_codomain( self.killed_module) sympy-0.7.4.1/sympy/polys/agca/homomorphisms.py0000644000175000017500000005223512253362407021753 0ustar georgeskgeorgesk""" 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 __future__ import print_function, division 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. class ModuleHomomorphism(object): """ Abstract base class for module homomoprhisms. Do not instantiate. Instead, use the ``homomorphism`` function: >>> from sympy import QQ >>> from sympy.abc import x >>> from sympy.polys.agca import homomorphism >>> F = QQ.old_poly_ring(x).free_module(2) >>> homomorphism(F, F, [[1, 0], [0, 1]]) Matrix([ [1, 0], : QQ[x]**2 -> QQ[x]**2 [0, 1]]) 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 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 QQ >>> from sympy.abc import x >>> from sympy.polys.agca import homomorphism >>> F = QQ.old_poly_ring(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 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 QQ >>> from sympy.abc import x >>> from sympy.polys.agca import homomorphism >>> F = QQ.old_poly_ring(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 quotient.""" raise NotImplementedError def _quotient_codomain(self, sm): """Implementation of codomain quotient.""" raise NotImplementedError 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 QQ >>> from sympy.abc import x >>> from sympy.polys.agca import homomorphism >>> F = QQ.old_poly_ring(x).free_module(2) >>> h = homomorphism(F, F, [[1, 0], [x, 0]]) >>> h Matrix([ [1, x], : QQ[x]**2 -> QQ[x]**2 [0, 0]]) >>> h.restrict_domain(F.submodule([1, 0])) Matrix([ [1, x], : <[1, 0]> -> QQ[x]**2 [0, 0]]) This is the same as just composing on the right with the submodule inclusion: >>> h * F.submodule([1, 0]).inclusion_hom() Matrix([ [1, x], : <[1, 0]> -> QQ[x]**2 [0, 0]]) """ 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) 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 QQ >>> from sympy.abc import x >>> from sympy.polys.agca import homomorphism >>> F = QQ.old_poly_ring(x).free_module(2) >>> h = homomorphism(F, F, [[1, 0], [x, 0]]) >>> h Matrix([ [1, x], : QQ[x]**2 -> QQ[x]**2 [0, 0]]) >>> h.restrict_codomain(F.submodule([1, 0])) Matrix([ [1, x], : QQ[x]**2 -> <[1, 0]> [0, 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) 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 QQ >>> from sympy.abc import x >>> from sympy.polys.agca import homomorphism >>> F = QQ.old_poly_ring(x).free_module(2) >>> h = homomorphism(F, F, [[1, 0], [x, 0]]) >>> h Matrix([ [1, x], : QQ[x]**2 -> QQ[x]**2 [0, 0]]) >>> h.quotient_domain(F.submodule([-x, 1])) Matrix([ [1, x], : QQ[x]**2/<[-x, 1]> -> QQ[x]**2 [0, 0]]) """ 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) 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 QQ >>> from sympy.abc import x >>> from sympy.polys.agca import homomorphism >>> F = QQ.old_poly_ring(x).free_module(2) >>> h = homomorphism(F, F, [[1, 0], [x, 0]]) >>> h Matrix([ [1, x], : QQ[x]**2 -> QQ[x]**2 [0, 0]]) >>> h.quotient_codomain(F.submodule([1, 1])) Matrix([ [1, x], : QQ[x]**2 -> QQ[x]**2/<[1, 1]> [0, 0]]) This is the same as composing with the quotient map on the left: >>> (F/[(1, 1)]).quotient_hom() * h Matrix([ [1, x], : QQ[x]**2 -> QQ[x]**2/<[1, 1]> [0, 0]]) """ 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 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 QQ >>> from sympy.abc import x >>> from sympy.polys.agca import homomorphism >>> F = QQ.old_poly_ring(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() 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 QQ >>> from sympy.abc import x >>> from sympy.polys.agca import homomorphism >>> F = QQ.old_poly_ring(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 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 QQ >>> from sympy.abc import x >>> from sympy.polys.agca import homomorphism >>> F = QQ.old_poly_ring(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() 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 QQ >>> from sympy.abc import x >>> from sympy.polys.agca import homomorphism >>> F = QQ.old_poly_ring(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 quotient.""" return self.__class__(self.domain/sm, self.codomain, self.matrix) def _quotient_codomain(self, sm): """Implementation of codomain quotient.""" 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 >>> from sympy.abc import x >>> from sympy.polys.agca import homomorphism >>> F = QQ.old_poly_ring(x).free_module(2) >>> homomorphism(F, F, [[1, 0], [0, 1]]) Matrix([ [1, 0], : QQ[x]**2 -> QQ[x]**2 [0, 1]]) """ 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 >>> from sympy.abc import x >>> from sympy.polys.agca import homomorphism >>> M = QQ.old_poly_ring(x).free_module(2)*x >>> homomorphism(M, M, [[1, 0], [0, 1]]) Matrix([ [1, 0], : <[x, 0], [0, x]> -> <[x, 0], [0, x]> [0, 1]]) """ 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]) 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 >>> from sympy.abc import x >>> from sympy.polys.agca import homomorphism >>> R = QQ.old_poly_ring(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 Matrix([ [1, x**2], : QQ[x]**2 -> QQ[x]**2 [x, 0]]) >>> 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]]) Matrix([ [1, x**2], : <[1, 0], [0, x]> -> QQ[x]**2 [x, 0]]) 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]]) Matrix([ [0, x**2], : <[1, 0] + <[1, 0]>, [0, x] + <[1, 0]>, [1, 0] + <[1, 0]>> -> QQ[x]**2 [0, 0]]) >>> 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) sympy-0.7.4.1/sympy/polys/agca/ideals.py0000644000175000017500000002441512253362407020307 0ustar georgeskgeorgesk"""Computations with ideals of polynomial rings.""" from __future__ import print_function, division from sympy.polys.polyerrors import CoercionFailed from sympy.core.compatibility import reduce 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.old_poly_ring(x).ideal(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 def is_whole_ring(self): """Return True if ``self`` is the whole ring.""" raise NotImplementedError 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) def is_prime(self): """Return True if ``self`` is a prime ideal.""" raise NotImplementedError def is_maximal(self): """Return True if ``self`` is a maximal ideal.""" raise NotImplementedError def is_radical(self): """Return True if ``self`` is a radical ideal.""" raise NotImplementedError def is_primary(self): """Return True if ``self`` is a primary ideal.""" raise NotImplementedError def is_principal(self): """Return True if ``self`` is a principal ideal.""" raise NotImplementedError def radical(self): """Compute the radical of ``self``.""" raise NotImplementedError def depth(self): """Compute the depth of ``self``.""" raise NotImplementedError 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)) def contains(self, elem): """ Return True if ``elem`` is an element of this ideal. >>> from sympy.abc import x >>> from sympy import QQ >>> QQ.old_poly_ring(x).ideal(x+1, x-1).contains(3) True >>> QQ.old_poly_ring(x).ideal(x**2, x**3).contains(x) False """ return self._contains_elem(self.ring.convert(elem)) 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.old_poly_ring(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.old_poly_ring(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) 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.old_poly_ring(x, y) >>> R.ideal(x*y).quotient(R.ideal(x)) """ self._check_ideal(J) return self._quotient(J, **opts) def intersect(self, J): """ Compute the intersection of self with ideal J. >>> from sympy.abc import x, y >>> from sympy import QQ >>> R = QQ.old_poly_ring(x, y) >>> R.ideal(x).intersect(R.ideal(y)) """ self._check_ideal(J) return self._intersect(J) 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 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.old_poly_ring(x).ideal(x**2 - 1).union(QQ.old_poly_ring(x).ideal((x+1)**2)) == QQ.old_poly_ring(x).ideal(x+1) True """ self._check_ideal(J) return self._union(J) 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.old_poly_ring(x, y).ideal(x).product(QQ.old_poly_ring(x, y).ideal(y)) """ self._check_ideal(J) return self._product(J) 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.old_poly_ring(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.old_poly_ring(x).ideal(x).is_zero() False >>> QQ.old_poly_ring(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.old_poly_ring(x).ideal(x).is_whole_ring() False >>> QQ.old_poly_ring(x).ideal(3).is_whole_ring() True >>> QQ.old_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.old_poly_ring(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] sympy-0.7.4.1/sympy/polys/agca/tests/0000755000175000017500000000000012253362407017630 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/polys/agca/tests/test_homomorphisms.py0000644000175000017500000001012612253362407024145 0ustar georgeskgeorgesk"""Tests for homomorphisms.""" from sympy import QQ, S from sympy.abc import x, y from sympy.polys.agca import homomorphism from sympy.utilities.pytest import raises def test_printing(): R = QQ.old_poly_ring(x) assert str(homomorphism(R.free_module(1), R.free_module(1), [0])) == \ 'Matrix([[0]]) : QQ[x]**1 -> QQ[x]**1' assert str(homomorphism(R.free_module(2), R.free_module(2), [0, 0])) == \ 'Matrix([ \n[0, 0], : QQ[x]**2 -> QQ[x]**2\n[0, 0]]) ' assert str(homomorphism(R.free_module(1), R.free_module(1) / [[x]], [0])) == \ 'Matrix([[0]]) : QQ[x]**1 -> QQ[x]**1/<[x]>' assert str(R.free_module(0).identity_hom()) == 'Matrix(0, 0, []) : QQ[x]**0 -> QQ[x]**0' def test_operations(): F = QQ.old_poly_ring(x).free_module(2) G = QQ.old_poly_ring(x).free_module(3) f = F.identity_hom() g = homomorphism(F, F, [0, [1, x]]) h = homomorphism(F, F, [[1, 0], 0]) i = homomorphism(F, G, [[1, 0, 0], [0, 1, 0]]) assert f == f assert f != g assert f != i assert (f != F.identity_hom()) is False assert 2*f == f*2 == homomorphism(F, F, [[2, 0], [0, 2]]) assert f/2 == homomorphism(F, F, [[S(1)/2, 0], [0, S(1)/2]]) assert f + g == homomorphism(F, F, [[1, 0], [1, x + 1]]) assert f - g == homomorphism(F, F, [[1, 0], [-1, 1 - x]]) assert f*g == g == g*f assert h*g == homomorphism(F, F, [0, [1, 0]]) assert g*h == homomorphism(F, F, [0, 0]) assert i*f == i assert f([1, 2]) == [1, 2] assert g([1, 2]) == [2, 2*x] assert i.restrict_domain(F.submodule([x, x]))([x, x]) == i([x, x]) h1 = h.quotient_domain(F.submodule([0, 1])) assert h1([1, 0]) == h([1, 0]) assert h1.restrict_domain(h1.domain.submodule([x, 0]))([x, 0]) == h([x, 0]) raises(TypeError, lambda: f/g) raises(TypeError, lambda: f + 1) raises(TypeError, lambda: f + i) raises(TypeError, lambda: f - 1) raises(TypeError, lambda: f*i) def test_creation(): F = QQ.old_poly_ring(x).free_module(3) G = QQ.old_poly_ring(x).free_module(2) SM = F.submodule([1, 1, 1]) Q = F / SM SQ = Q.submodule([1, 0, 0]) matrix = [[1, 0], [0, 1], [-1, -1]] h = homomorphism(F, G, matrix) h2 = homomorphism(Q, G, matrix) assert h.quotient_domain(SM) == h2 raises(ValueError, lambda: h.quotient_domain(F.submodule([1, 0, 0]))) assert h2.restrict_domain(SQ) == homomorphism(SQ, G, matrix) raises(ValueError, lambda: h.restrict_domain(G)) raises(ValueError, lambda: h.restrict_codomain(G.submodule([1, 0]))) raises(ValueError, lambda: h.quotient_codomain(F)) im = [[1, 0, 0], [0, 1, 0], [0, 0, 1]] for M in [F, SM, Q, SQ]: assert M.identity_hom() == homomorphism(M, M, im) assert SM.inclusion_hom() == homomorphism(SM, F, im) assert SQ.inclusion_hom() == homomorphism(SQ, Q, im) assert Q.quotient_hom() == homomorphism(F, Q, im) assert SQ.quotient_hom() == homomorphism(SQ.base, SQ, im) class conv(object): def convert(x, y=None): return x class dummy(object): container = conv() def submodule(*args): return None raises(TypeError, lambda: homomorphism(dummy(), G, matrix)) raises(TypeError, lambda: homomorphism(F, dummy(), matrix)) raises( ValueError, lambda: homomorphism(QQ.old_poly_ring(x, y).free_module(3), G, matrix)) raises(ValueError, lambda: homomorphism(F, G, [0, 0])) def test_properties(): R = QQ.old_poly_ring(x, y) F = R.free_module(2) h = homomorphism(F, F, [[x, 0], [y, 0]]) assert h.kernel() == F.submodule([-y, x]) assert h.image() == F.submodule([x, 0], [y, 0]) assert not h.is_injective() assert not h.is_surjective() assert h.restrict_codomain(h.image()).is_surjective() assert h.restrict_domain(F.submodule([1, 0])).is_injective() assert h.quotient_domain( h.kernel()).restrict_codomain(h.image()).is_isomorphism() R2 = QQ.old_poly_ring(x, y, order=(("lex", x), ("ilex", y))) / [x**2 + 1] F = R2.free_module(2) h = homomorphism(F, F, [[x, 0], [y, y + 1]]) assert h.is_isomorphism() sympy-0.7.4.1/sympy/polys/agca/tests/test_modules.py0000644000175000017500000003232612253362407022717 0ustar georgeskgeorgesk"""Test modules.py code.""" from sympy.polys.agca.modules import FreeModule, ModuleOrder, FreeModulePolyRing from sympy.polys import CoercionFailed, QQ, lex, grlex, ilex, ZZ from sympy.abc import x, y, z from sympy.utilities.pytest import raises from sympy import S def test_FreeModuleElement(): M = QQ.old_poly_ring(x).free_module(3) e = M.convert([1, x, x**2]) f = [QQ.old_poly_ring(x).convert(1), QQ.old_poly_ring(x).convert(x), QQ.old_poly_ring(x).convert(x**2)] assert list(e) == f assert f[0] == e[0] assert f[1] == e[1] assert f[2] == e[2] raises(IndexError, lambda: e[3]) g = M.convert([x, 0, 0]) assert e + g == M.convert([x + 1, x, x**2]) assert f + g == M.convert([x + 1, x, x**2]) assert -e == M.convert([-1, -x, -x**2]) assert e - g == M.convert([1 - x, x, x**2]) assert e != g assert M.convert([x, x, x]) / QQ.old_poly_ring(x).convert(x) == [1, 1, 1] R = QQ.old_poly_ring(x, order="ilex") assert R.free_module(1).convert([x]) / R.convert(x) == [1] def test_FreeModule(): M1 = FreeModule(QQ.old_poly_ring(x), 2) assert M1 == FreeModule(QQ.old_poly_ring(x), 2) assert M1 != FreeModule(QQ.old_poly_ring(y), 2) assert M1 != FreeModule(QQ.old_poly_ring(x), 3) M2 = FreeModule(QQ.old_poly_ring(x, order="ilex"), 2) assert [x, 1] in M1 assert [x] not in M1 assert [2, y] not in M1 assert [1/(x + 1), 2] not in M1 e = M1.convert([x, x**2 + 1]) X = QQ.old_poly_ring(x).convert(x) assert e == [X, X**2 + 1] assert e == [x, x**2 + 1] assert 2*e == [2*x, 2*x**2 + 2] assert e*2 == [2*x, 2*x**2 + 2] assert e/2 == [x/2, (x**2 + 1)/2] assert x*e == [x**2, x**3 + x] assert e*x == [x**2, x**3 + x] assert X*e == [x**2, x**3 + x] assert e*X == [x**2, x**3 + x] assert [x, 1] in M2 assert [x] not in M2 assert [2, y] not in M2 assert [1/(x + 1), 2] in M2 e = M2.convert([x, x**2 + 1]) X = QQ.old_poly_ring(x, order="ilex").convert(x) assert e == [X, X**2 + 1] assert e == [x, x**2 + 1] assert 2*e == [2*x, 2*x**2 + 2] assert e*2 == [2*x, 2*x**2 + 2] assert e/2 == [x/2, (x**2 + 1)/2] assert x*e == [x**2, x**3 + x] assert e*x == [x**2, x**3 + x] assert e/(1 + x) == [x/(1 + x), (x**2 + 1)/(1 + x)] assert X*e == [x**2, x**3 + x] assert e*X == [x**2, x**3 + x] M3 = FreeModule(QQ.old_poly_ring(x, y), 2) assert M3.convert(e) == M3.convert([x, x**2 + 1]) assert not M3.is_submodule(0) assert not M3.is_zero() raises(NotImplementedError, lambda: ZZ.old_poly_ring(x).free_module(2)) raises(NotImplementedError, lambda: FreeModulePolyRing(ZZ, 2)) raises(CoercionFailed, lambda: M1.convert(QQ.old_poly_ring(x).free_module(3) .convert([1, 2, 3]))) raises(CoercionFailed, lambda: M3.convert(1)) def test_ModuleOrder(): o1 = ModuleOrder(lex, grlex, False) o2 = ModuleOrder(ilex, lex, False) assert o1 == ModuleOrder(lex, grlex, False) assert (o1 != ModuleOrder(lex, grlex, False)) is False assert o1 != o2 assert o1((1, 2, 3)) == (1, (5, (2, 3))) assert o2((1, 2, 3)) == (-1, (2, 3)) def test_SubModulePolyRing_global(): R = QQ.old_poly_ring(x, y) F = R.free_module(3) Fd = F.submodule([1, 0, 0], [1, 2, 0], [1, 2, 3]) M = F.submodule([x**2 + y**2, 1, 0], [x, y, 1]) assert F == Fd assert Fd == F assert F != M assert M != F assert Fd != M assert M != Fd assert Fd == F.submodule(*F.basis()) assert Fd.is_full_module() assert not M.is_full_module() assert not Fd.is_zero() assert not M.is_zero() assert Fd.submodule().is_zero() assert M.contains([x**2 + y**2 + x, 1 + y, 1]) assert not M.contains([x**2 + y**2 + x, 1 + y, 2]) assert M.contains([y**2, 1 - x*y, -x]) assert not F.submodule([1 + x, 0, 0]) == F.submodule([1, 0, 0]) assert F.submodule([1, 0, 0], [0, 1, 0]).union(F.submodule([0, 0, 1])) == F assert not M.is_submodule(0) m = F.convert([x**2 + y**2, 1, 0]) n = M.convert(m) assert m.module is F assert n.module is M raises(ValueError, lambda: M.submodule([1, 0, 0])) raises(TypeError, lambda: M.union(1)) raises(ValueError, lambda: M.union(R.free_module(1).submodule([x]))) assert F.submodule([x, x, x]) != F.submodule([x, x, x], order="ilex") def test_SubModulePolyRing_local(): R = QQ.old_poly_ring(x, y, order=ilex) F = R.free_module(3) Fd = F.submodule([1 + x, 0, 0], [1 + y, 2 + 2*y, 0], [1, 2, 3]) M = F.submodule([x**2 + y**2, 1, 0], [x, y, 1]) assert F == Fd assert Fd == F assert F != M assert M != F assert Fd != M assert M != Fd assert Fd == F.submodule(*F.basis()) assert Fd.is_full_module() assert not M.is_full_module() assert not Fd.is_zero() assert not M.is_zero() assert Fd.submodule().is_zero() assert M.contains([x**2 + y**2 + x, 1 + y, 1]) assert not M.contains([x**2 + y**2 + x, 1 + y, 2]) assert M.contains([y**2, 1 - x*y, -x]) assert F.submodule([1 + x, 0, 0]) == F.submodule([1, 0, 0]) assert F.submodule( [1, 0, 0], [0, 1, 0]).union(F.submodule([0, 0, 1 + x*y])) == F raises(ValueError, lambda: M.submodule([1, 0, 0])) def test_SubModulePolyRing_nontriv_global(): R = QQ.old_poly_ring(x, y, z) F = R.free_module(1) def contains(I, f): return F.submodule(*[[g] for g in I]).contains([f]) assert contains([x, y], x) assert contains([x, y], x + y) assert not contains([x, y], 1) assert not contains([x, y], z) assert contains([x**2 + y, x**2 + x], x - y) assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x**2) assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**3) assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**4) assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x*y**2) assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**4 + y**3 + 2*z*y*x) assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x*y*z) assert contains([x, 1 + x + y, 5 - 7*y], 1) assert contains( [x**3 + y**3, y**3 + z**3, z**3 + x**3, x**2*y + x**2*z + y**2*z], x**3) assert not contains( [x**3 + y**3, y**3 + z**3, z**3 + x**3, x**2*y + x**2*z + y**2*z], x**2 + y**2) # compare local order assert not contains([x*(1 + x + y), y*(1 + z)], x) assert not contains([x*(1 + x + y), y*(1 + z)], x + y) def test_SubModulePolyRing_nontriv_local(): R = QQ.old_poly_ring(x, y, z, order=ilex) F = R.free_module(1) def contains(I, f): return F.submodule(*[[g] for g in I]).contains([f]) assert contains([x, y], x) assert contains([x, y], x + y) assert not contains([x, y], 1) assert not contains([x, y], z) assert contains([x**2 + y, x**2 + x], x - y) assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x**2) assert contains([x*(1 + x + y), y*(1 + z)], x) assert contains([x*(1 + x + y), y*(1 + z)], x + y) def test_syzygy(): R = QQ.old_poly_ring(x, y, z) M = R.free_module(1).submodule([x*y], [y*z], [x*z]) S = R.free_module(3).submodule([0, x, -y], [z, -x, 0]) assert M.syzygy_module() == S M2 = M / ([x*y*z],) S2 = R.free_module(3).submodule([z, 0, 0], [0, x, 0], [0, 0, y]) assert M2.syzygy_module() == S2 F = R.free_module(3) assert F.submodule(*F.basis()).syzygy_module() == F.submodule() R2 = QQ.old_poly_ring(x, y, z) / [x*y*z] M3 = R2.free_module(1).submodule([x*y], [y*z], [x*z]) S3 = R2.free_module(3).submodule([z, 0, 0], [0, x, 0], [0, 0, y]) assert M3.syzygy_module() == S3 def test_in_terms_of_generators(): R = QQ.old_poly_ring(x, order="ilex") M = R.free_module(2).submodule([2*x, 0], [1, 2]) assert M.in_terms_of_generators( [x, x]) == [R.convert(S(1)/4), R.convert(x/2)] raises(ValueError, lambda: M.in_terms_of_generators([1, 0])) M = R.free_module(2) / ([x, 0], [1, 1]) SM = M.submodule([1, x]) assert SM.in_terms_of_generators([2, 0]) == [R.convert(-2/(x - 1))] R = QQ.old_poly_ring(x, y) / [x**2 - y**2] M = R.free_module(2) SM = M.submodule([x, 0], [0, y]) assert SM.in_terms_of_generators( [x**2, x**2]) == [R.convert(x), R.convert(y)] def test_QuotientModuleElement(): R = QQ.old_poly_ring(x) F = R.free_module(3) N = F.submodule([1, x, x**2]) M = F/N e = M.convert([x**2, 2, 0]) assert M.convert([x + 1, x**2 + x, x**3 + x**2]) == 0 assert e == [x**2, 2, 0] + N == F.convert([x**2, 2, 0]) + N == \ M.convert(F.convert([x**2, 2, 0])) assert M.convert([x**2 + 1, 2*x + 2, x**2]) == e + [0, x, 0] == \ e + M.convert([0, x, 0]) == e + F.convert([0, x, 0]) assert M.convert([x**2 + 1, 2, x**2]) == e - [0, x, 0] == \ e - M.convert([0, x, 0]) == e - F.convert([0, x, 0]) assert M.convert([0, 2, 0]) == M.convert([x**2, 4, 0]) - e == \ [x**2, 4, 0] - e == F.convert([x**2, 4, 0]) - e assert M.convert([x**3 + x**2, 2*x + 2, 0]) == (1 + x)*e == \ R.convert(1 + x)*e == e*(1 + x) == e*R.convert(1 + x) assert -e == [-x**2, -2, 0] f = [x, x, 0] + N assert M.convert([1, 1, 0]) == f / x == f / R.convert(x) M2 = F/[(2, 2*x, 2*x**2), (0, 0, 1)] G = R.free_module(2) M3 = G/[[1, x]] M4 = F.submodule([1, x, x**2], [1, 0, 0]) / N raises(CoercionFailed, lambda: M.convert(G.convert([1, x]))) raises(CoercionFailed, lambda: M.convert(M3.convert([1, x]))) raises(CoercionFailed, lambda: M.convert(M2.convert([1, x, x]))) assert M2.convert(M.convert([2, x, x**2])) == [2, x, 0] assert M.convert(M4.convert([2, 0, 0])) == [2, 0, 0] def test_QuotientModule(): R = QQ.old_poly_ring(x) F = R.free_module(3) N = F.submodule([1, x, x**2]) M = F/N assert M != F assert M != N assert M == F / [(1, x, x**2)] assert not M.is_zero() assert (F / F.basis()).is_zero() SQ = F.submodule([1, x, x**2], [2, 0, 0]) / N assert SQ == M.submodule([2, x, x**2]) assert SQ != M.submodule([2, 1, 0]) assert SQ != M assert M.is_submodule(SQ) assert not SQ.is_full_module() raises(ValueError, lambda: N/F) raises(ValueError, lambda: F.submodule([2, 0, 0]) / N) raises(ValueError, lambda: R.free_module(2)/F) raises(CoercionFailed, lambda: F.convert(M.convert([1, x, x**2]))) M1 = F / [[1, 1, 1]] M2 = M1.submodule([1, 0, 0], [0, 1, 0]) assert M1 == M2 def test_ModulesQuotientRing(): R = QQ.old_poly_ring(x, y, order=(("lex", x), ("ilex", y))) / [x**2 + 1] M1 = R.free_module(2) assert M1 == R.free_module(2) assert M1 != QQ.old_poly_ring(x).free_module(2) assert M1 != R.free_module(3) assert [x, 1] in M1 assert [x] not in M1 assert [1/(R.convert(x) + 1), 2] in M1 assert [1, 2/(1 + y)] in M1 assert [1, 2/y] not in M1 assert M1.convert([x**2, y]) == [-1, y] F = R.free_module(3) Fd = F.submodule([x**2, 0, 0], [1, 2, 0], [1, 2, 3]) M = F.submodule([x**2 + y**2, 1, 0], [x, y, 1]) assert F == Fd assert Fd == F assert F != M assert M != F assert Fd != M assert M != Fd assert Fd == F.submodule(*F.basis()) assert Fd.is_full_module() assert not M.is_full_module() assert not Fd.is_zero() assert not M.is_zero() assert Fd.submodule().is_zero() assert M.contains([x**2 + y**2 + x, -x**2 + y, 1]) assert not M.contains([x**2 + y**2 + x, 1 + y, 2]) assert M.contains([y**2, 1 - x*y, -x]) assert F.submodule([x, 0, 0]) == F.submodule([1, 0, 0]) assert not F.submodule([y, 0, 0]) == F.submodule([1, 0, 0]) assert F.submodule([1, 0, 0], [0, 1, 0]).union(F.submodule([0, 0, 1])) == F assert not M.is_submodule(0) def test_module_mul(): R = QQ.old_poly_ring(x) M = R.free_module(2) S1 = M.submodule([x, 0], [0, x]) S2 = M.submodule([x**2, 0], [0, x**2]) I = R.ideal(x) assert I*M == M*I == S1 == x*M == M*x assert I*S1 == S2 == x*S1 def test_intersection(): # SCA, example 2.8.5 F = QQ.old_poly_ring(x, y).free_module(2) M1 = F.submodule([x, y], [y, 1]) M2 = F.submodule([0, y - 1], [x, 1], [y, x]) I = F.submodule([x, y], [y**2 - y, y - 1], [x*y + y, x + 1]) I1, rel1, rel2 = M1.intersect(M2, relations=True) assert I1 == M2.intersect(M1) == I for i, g in enumerate(I1.gens): assert g == sum(c*x for c, x in zip(rel1[i], M1.gens)) \ == sum(d*y for d, y in zip(rel2[i], M2.gens)) assert F.submodule([x, y]).intersect(F.submodule([y, x])).is_zero() def test_quotient(): # SCA, example 2.8.6 R = QQ.old_poly_ring(x, y, z) F = R.free_module(2) assert F.submodule([x*y, x*z], [y*z, x*y]).module_quotient( F.submodule([y, z], [z, y])) == QQ.old_poly_ring(x, y, z).ideal(x**2*y**2 - x*y*z**2) assert F.submodule([x, y]).module_quotient(F.submodule()).is_whole_ring() M = F.submodule([x**2, x**2], [y**2, y**2]) N = F.submodule([x + y, x + y]) q, rel = M.module_quotient(N, relations=True) assert q == R.ideal(y**2, x - y) for i, g in enumerate(q.gens): assert g*N.gens[0] == sum(c*x for c, x in zip(rel[i], M.gens)) def test_groebner_extendend(): M = QQ.old_poly_ring(x, y, z).free_module(3).submodule([x + 1, y, 1], [x*y, z, z**2]) G, R = M._groebner_vec(extended=True) for i, g in enumerate(G): assert g == sum(c*gen for c, gen in zip(R[i], M.gens)) sympy-0.7.4.1/sympy/polys/agca/tests/test_ideals.py0000644000175000017500000000732312253362407022507 0ustar georgeskgeorgesk"""Test ideals.py code.""" from sympy.polys import QQ, lex, ilex from sympy.abc import x, y, z from sympy.utilities.pytest import raises def test_ideal_operations(): R = QQ.old_poly_ring(x, y) I = R.ideal(x) J = R.ideal(y) S = R.ideal(x*y) T = R.ideal(x, y) assert not (I == J) assert I == I assert I.union(J) == T assert I + J == T assert I + T == T assert not I.subset(T) assert T.subset(I) assert I.product(J) == S assert I*J == S assert x*J == S assert I*y == S assert R.convert(x)*J == S assert I*R.convert(y) == S assert not I.is_zero() assert not J.is_whole_ring() assert R.ideal(x**2 + 1, x).is_whole_ring() assert R.ideal() == R.ideal(0) assert R.ideal().is_zero() assert T.contains(x*y) assert T.subset([x, y]) assert T.in_terms_of_generators(x) == [R(1), R(0)] assert T**0 == R.ideal(1) assert T**1 == T assert T**2 == R.ideal(x**2, y**2, x*y) assert I**5 == R.ideal(x**5) def test_exceptions(): I = QQ.old_poly_ring(x).ideal(x) J = QQ.old_poly_ring(y).ideal(1) raises(ValueError, lambda: I.union(x)) raises(ValueError, lambda: I + J) raises(ValueError, lambda: I * J) raises(ValueError, lambda: I.union(J)) assert (I == J) is False assert I != J def test_nontriv_global(): R = QQ.old_poly_ring(x, y, z) def contains(I, f): return R.ideal(*I).contains(f) assert contains([x, y], x) assert contains([x, y], x + y) assert not contains([x, y], 1) assert not contains([x, y], z) assert contains([x**2 + y, x**2 + x], x - y) assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x**2) assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**3) assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**4) assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x*y**2) assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**4 + y**3 + 2*z*y*x) assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x*y*z) assert contains([x, 1 + x + y, 5 - 7*y], 1) assert contains( [x**3 + y**3, y**3 + z**3, z**3 + x**3, x**2*y + x**2*z + y**2*z], x**3) assert not contains( [x**3 + y**3, y**3 + z**3, z**3 + x**3, x**2*y + x**2*z + y**2*z], x**2 + y**2) # compare local order assert not contains([x*(1 + x + y), y*(1 + z)], x) assert not contains([x*(1 + x + y), y*(1 + z)], x + y) def test_nontriv_local(): R = QQ.old_poly_ring(x, y, z, order=ilex) def contains(I, f): return R.ideal(*I).contains(f) assert contains([x, y], x) assert contains([x, y], x + y) assert not contains([x, y], 1) assert not contains([x, y], z) assert contains([x**2 + y, x**2 + x], x - y) assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x**2) assert contains([x*(1 + x + y), y*(1 + z)], x) assert contains([x*(1 + x + y), y*(1 + z)], x + y) def test_intersection(): R = QQ.old_poly_ring(x, y, z) # SCA, example 1.8.11 assert R.ideal(x, y).intersect(R.ideal(y**2, z)) == R.ideal(y**2, y*z, x*z) assert R.ideal(x, y).intersect(R.ideal()).is_zero() R = QQ.old_poly_ring(x, y, z, order="ilex") assert R.ideal(x, y).intersect(R.ideal(y**2 + y**2*z, z + z*x**3*y)) == \ R.ideal(y**2, y*z, x*z) def test_quotient(): # SCA, example 1.8.13 R = QQ.old_poly_ring(x, y, z) assert R.ideal(x, y).quotient(R.ideal(y**2, z)) == R.ideal(x, y) def test_reduction(): from sympy.polys.distributedmodules import sdm_nf_buchberger_reduced R = QQ.old_poly_ring(x, y) I = R.ideal(x**5, y) e = R.convert(x**3 + y**2) assert I.reduce_element(e) == e assert I.reduce_element(e, NF=sdm_nf_buchberger_reduced) == R.convert(x**3) sympy-0.7.4.1/sympy/polys/agca/tests/__init__.py0000644000175000017500000000000012253362407021727 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/polys/agca/__init__.py0000644000175000017500000000014512253362407020577 0ustar georgeskgeorgesk"""Module for algebraic geomery and commutative algebra.""" from .homomorphisms import homomorphism sympy-0.7.4.1/sympy/polys/constructor.py0000644000175000017500000001537112253362407020541 0ustar georgeskgeorgesk"""Tools for constructing domains for expressions. """ from __future__ import print_function, division 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.utilities import public from sympy.core import sympify, Symbol 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 opt.composite is None: if any(gen.is_number for gen in gens): return None # generators are number-like so lets better use EX all_symbols = set([]) for gen in gens: symbols = gen.atoms(Symbol) if all_symbols & symbols: return None # there could be algebraic relations between generators else: all_symbols |= symbols 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 @public 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): if not obj: monoms, coeffs = [], [] else: 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 is False: result = None else: result = _construct_composite(coeffs, opt) 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] sympy-0.7.4.1/sympy/polys/compatibility.py0000644000175000017500000015645212253362407021033 0ustar georgeskgeorgesk"""Compatibility interface between dense and sparse polys. """ from __future__ import print_function, division from sympy.polys.densearith import dup_add_term from sympy.polys.densearith import dmp_add_term from sympy.polys.densearith import dup_sub_term from sympy.polys.densearith import dmp_sub_term from sympy.polys.densearith import dup_mul_term from sympy.polys.densearith import dmp_mul_term from sympy.polys.densearith import dup_add_ground from sympy.polys.densearith import dmp_add_ground from sympy.polys.densearith import dup_sub_ground from sympy.polys.densearith import dmp_sub_ground from sympy.polys.densearith import dup_mul_ground from sympy.polys.densearith import dmp_mul_ground from sympy.polys.densearith import dup_quo_ground from sympy.polys.densearith import dmp_quo_ground from sympy.polys.densearith import dup_exquo_ground from sympy.polys.densearith import dmp_exquo_ground from sympy.polys.densearith import dup_lshift from sympy.polys.densearith import dup_rshift from sympy.polys.densearith import dup_abs from sympy.polys.densearith import dmp_abs from sympy.polys.densearith import dup_neg from sympy.polys.densearith import dmp_neg from sympy.polys.densearith import dup_add from sympy.polys.densearith import dmp_add from sympy.polys.densearith import dup_sub from sympy.polys.densearith import dmp_sub from sympy.polys.densearith import dup_add_mul from sympy.polys.densearith import dmp_add_mul from sympy.polys.densearith import dup_sub_mul from sympy.polys.densearith import dmp_sub_mul from sympy.polys.densearith import dup_mul from sympy.polys.densearith import dmp_mul from sympy.polys.densearith import dup_sqr from sympy.polys.densearith import dmp_sqr from sympy.polys.densearith import dup_pow from sympy.polys.densearith import dmp_pow from sympy.polys.densearith import dup_pdiv from sympy.polys.densearith import dup_prem from sympy.polys.densearith import dup_pquo from sympy.polys.densearith import dup_pexquo from sympy.polys.densearith import dmp_pdiv from sympy.polys.densearith import dmp_prem from sympy.polys.densearith import dmp_pquo from sympy.polys.densearith import dmp_pexquo from sympy.polys.densearith import dup_rr_div from sympy.polys.densearith import dmp_rr_div from sympy.polys.densearith import dup_ff_div from sympy.polys.densearith import dmp_ff_div from sympy.polys.densearith import dup_div from sympy.polys.densearith import dup_rem from sympy.polys.densearith import dup_quo from sympy.polys.densearith import dup_exquo from sympy.polys.densearith import dmp_div from sympy.polys.densearith import dmp_rem from sympy.polys.densearith import dmp_quo from sympy.polys.densearith import dmp_exquo from sympy.polys.densearith import dup_max_norm from sympy.polys.densearith import dmp_max_norm from sympy.polys.densearith import dup_l1_norm from sympy.polys.densearith import dmp_l1_norm from sympy.polys.densearith import dup_expand from sympy.polys.densearith import dmp_expand from sympy.polys.densebasic import dup_LC from sympy.polys.densebasic import dmp_LC from sympy.polys.densebasic import dup_TC from sympy.polys.densebasic import dmp_TC from sympy.polys.densebasic import dmp_ground_LC from sympy.polys.densebasic import dmp_ground_TC from sympy.polys.densebasic import dup_degree from sympy.polys.densebasic import dmp_degree from sympy.polys.densebasic import dmp_degree_in from sympy.polys.densebasic import dmp_to_dict from sympy.polys.densetools import dup_integrate from sympy.polys.densetools import dmp_integrate from sympy.polys.densetools import dmp_integrate_in from sympy.polys.densetools import dup_diff from sympy.polys.densetools import dmp_diff from sympy.polys.densetools import dmp_diff_in from sympy.polys.densetools import dup_eval from sympy.polys.densetools import dmp_eval from sympy.polys.densetools import dmp_eval_in from sympy.polys.densetools import dmp_eval_tail from sympy.polys.densetools import dmp_diff_eval_in from sympy.polys.densetools import dup_trunc from sympy.polys.densetools import dmp_trunc from sympy.polys.densetools import dmp_ground_trunc from sympy.polys.densetools import dup_monic from sympy.polys.densetools import dmp_ground_monic from sympy.polys.densetools import dup_content from sympy.polys.densetools import dmp_ground_content from sympy.polys.densetools import dup_primitive from sympy.polys.densetools import dmp_ground_primitive from sympy.polys.densetools import dup_extract from sympy.polys.densetools import dmp_ground_extract from sympy.polys.densetools import dup_real_imag from sympy.polys.densetools import dup_mirror from sympy.polys.densetools import dup_scale from sympy.polys.densetools import dup_shift from sympy.polys.densetools import dup_transform from sympy.polys.densetools import dup_compose from sympy.polys.densetools import dmp_compose from sympy.polys.densetools import dup_decompose from sympy.polys.densetools import dmp_lift from sympy.polys.densetools import dup_sign_variations from sympy.polys.densetools import dup_clear_denoms from sympy.polys.densetools import dmp_clear_denoms from sympy.polys.densetools import dup_revert from sympy.polys.euclidtools import dup_half_gcdex from sympy.polys.euclidtools import dmp_half_gcdex from sympy.polys.euclidtools import dup_gcdex from sympy.polys.euclidtools import dmp_gcdex from sympy.polys.euclidtools import dup_invert from sympy.polys.euclidtools import dmp_invert from sympy.polys.euclidtools import dup_euclidean_prs from sympy.polys.euclidtools import dmp_euclidean_prs from sympy.polys.euclidtools import dup_primitive_prs from sympy.polys.euclidtools import dmp_primitive_prs from sympy.polys.euclidtools import dup_inner_subresultants from sympy.polys.euclidtools import dup_subresultants from sympy.polys.euclidtools import dup_prs_resultant from sympy.polys.euclidtools import dup_resultant from sympy.polys.euclidtools import dmp_inner_subresultants from sympy.polys.euclidtools import dmp_subresultants from sympy.polys.euclidtools import dmp_prs_resultant from sympy.polys.euclidtools import dmp_zz_modular_resultant from sympy.polys.euclidtools import dmp_zz_collins_resultant from sympy.polys.euclidtools import dmp_qq_collins_resultant from sympy.polys.euclidtools import dmp_resultant from sympy.polys.euclidtools import dup_discriminant from sympy.polys.euclidtools import dmp_discriminant from sympy.polys.euclidtools import dup_rr_prs_gcd from sympy.polys.euclidtools import dup_ff_prs_gcd from sympy.polys.euclidtools import dmp_rr_prs_gcd from sympy.polys.euclidtools import dmp_ff_prs_gcd from sympy.polys.euclidtools import dup_zz_heu_gcd from sympy.polys.euclidtools import dmp_zz_heu_gcd from sympy.polys.euclidtools import dup_qq_heu_gcd from sympy.polys.euclidtools import dmp_qq_heu_gcd from sympy.polys.euclidtools import dup_inner_gcd from sympy.polys.euclidtools import dmp_inner_gcd from sympy.polys.euclidtools import dup_gcd from sympy.polys.euclidtools import dmp_gcd from sympy.polys.euclidtools import dup_rr_lcm from sympy.polys.euclidtools import dup_ff_lcm from sympy.polys.euclidtools import dup_lcm from sympy.polys.euclidtools import dmp_rr_lcm from sympy.polys.euclidtools import dmp_ff_lcm from sympy.polys.euclidtools import dmp_lcm from sympy.polys.euclidtools import dmp_content from sympy.polys.euclidtools import dmp_primitive from sympy.polys.euclidtools import dup_cancel from sympy.polys.euclidtools import dmp_cancel from sympy.polys.factortools import dup_trial_division from sympy.polys.factortools import dmp_trial_division from sympy.polys.factortools import dup_zz_mignotte_bound from sympy.polys.factortools import dmp_zz_mignotte_bound from sympy.polys.factortools import dup_zz_hensel_step from sympy.polys.factortools import dup_zz_hensel_lift from sympy.polys.factortools import dup_zz_zassenhaus from sympy.polys.factortools import dup_zz_irreducible_p from sympy.polys.factortools import dup_cyclotomic_p from sympy.polys.factortools import dup_zz_cyclotomic_poly from sympy.polys.factortools import dup_zz_cyclotomic_factor from sympy.polys.factortools import dup_zz_factor_sqf from sympy.polys.factortools import dup_zz_factor from sympy.polys.factortools import dmp_zz_wang_non_divisors from sympy.polys.factortools import dmp_zz_wang_lead_coeffs from sympy.polys.factortools import dup_zz_diophantine from sympy.polys.factortools import dmp_zz_diophantine from sympy.polys.factortools import dmp_zz_wang_hensel_lifting from sympy.polys.factortools import dmp_zz_wang from sympy.polys.factortools import dmp_zz_factor from sympy.polys.factortools import dup_ext_factor from sympy.polys.factortools import dmp_ext_factor from sympy.polys.factortools import dup_gf_factor from sympy.polys.factortools import dmp_gf_factor from sympy.polys.factortools import dup_factor_list from sympy.polys.factortools import dup_factor_list_include from sympy.polys.factortools import dmp_factor_list from sympy.polys.factortools import dmp_factor_list_include from sympy.polys.factortools import dup_irreducible_p from sympy.polys.factortools import dmp_irreducible_p from sympy.polys.rootisolation import dup_sturm from sympy.polys.rootisolation import dup_root_upper_bound from sympy.polys.rootisolation import dup_root_lower_bound from sympy.polys.rootisolation import dup_step_refine_real_root from sympy.polys.rootisolation import dup_inner_refine_real_root from sympy.polys.rootisolation import dup_outer_refine_real_root from sympy.polys.rootisolation import dup_refine_real_root from sympy.polys.rootisolation import dup_inner_isolate_real_roots from sympy.polys.rootisolation import dup_inner_isolate_positive_roots from sympy.polys.rootisolation import dup_inner_isolate_negative_roots from sympy.polys.rootisolation import dup_isolate_real_roots_sqf from sympy.polys.rootisolation import dup_isolate_real_roots from sympy.polys.rootisolation import dup_isolate_real_roots_list from sympy.polys.rootisolation import dup_count_real_roots from sympy.polys.rootisolation import dup_count_complex_roots from sympy.polys.rootisolation import dup_isolate_complex_roots_sqf from sympy.polys.rootisolation import dup_isolate_all_roots_sqf from sympy.polys.rootisolation import dup_isolate_all_roots from sympy.polys.sqfreetools import ( dup_sqf_p, dmp_sqf_p, dup_sqf_norm, dmp_sqf_norm, dup_gf_sqf_part, dmp_gf_sqf_part, dup_sqf_part, dmp_sqf_part, dup_gf_sqf_list, dmp_gf_sqf_list, dup_sqf_list, dup_sqf_list_include, dmp_sqf_list, dmp_sqf_list_include, dup_gff_list, dmp_gff_list) from sympy.polys.galoistools import ( gf_degree, gf_LC, gf_TC, gf_strip, gf_trunc, gf_normal, gf_from_dict, gf_to_dict, gf_from_int_poly, gf_to_int_poly, gf_neg, gf_add_ground, gf_sub_ground, gf_mul_ground, gf_quo_ground, gf_add, gf_sub, gf_mul, gf_sqr, gf_add_mul, gf_sub_mul, gf_expand, gf_div, gf_rem, gf_quo, gf_exquo, gf_lshift, gf_rshift, gf_pow, gf_pow_mod, gf_gcd, gf_lcm, gf_cofactors, gf_gcdex, gf_monic, gf_diff, gf_eval, gf_multi_eval, gf_compose, gf_compose_mod, gf_trace_map, gf_random, gf_irreducible, gf_irred_p_ben_or, gf_irred_p_rabin, gf_irreducible_p, gf_sqf_p, gf_sqf_part, gf_sqf_list, gf_Qmatrix, gf_Qbasis, gf_berlekamp, gf_ddf_zassenhaus, gf_edf_zassenhaus, gf_ddf_shoup, gf_edf_shoup, gf_zassenhaus, gf_shoup, gf_factor_sqf, gf_factor, gf_value, gf_csolve) from sympy.utilities import public @public class IPolys(object): symbols = None ngens = None domain = None order = None gens = None def drop(self, gen): pass def clone(self, symbols=None, domain=None, order=None): pass def to_ground(self): pass def ground_new(self, element): pass def domain_new(self, element): pass def from_dict(self, d): pass def wrap(self, element): from sympy.polys.rings import PolyElement if isinstance(element, PolyElement): if element.ring == self: return element else: raise NotImplementedError("domain conversions") else: return self.ground_new(element) def to_dense(self, element): return self.wrap(element).to_dense() def from_dense(self, element): return self.from_dict(dmp_to_dict(element, self.ngens-1, self.domain)) def dup_add_term(self, f, c, i): return self.from_dense(dup_add_term(self.to_dense(f), c, i, self.domain)) def dmp_add_term(self, f, c, i): return self.from_dense(dmp_add_term(self.to_dense(f), self.wrap(c).drop(0).to_dense(), i, self.ngens-1, self.domain)) def dup_sub_term(self, f, c, i): return self.from_dense(dup_sub_term(self.to_dense(f), c, i, self.domain)) def dmp_sub_term(self, f, c, i): return self.from_dense(dmp_sub_term(self.to_dense(f), self.wrap(c).drop(0).to_dense(), i, self.ngens-1, self.domain)) def dup_mul_term(self, f, c, i): return self.from_dense(dup_mul_term(self.to_dense(f), c, i, self.domain)) def dmp_mul_term(self, f, c, i): return self.from_dense(dmp_mul_term(self.to_dense(f), self.wrap(c).drop(0).to_dense(), i, self.ngens-1, self.domain)) def dup_add_ground(self, f, c): return self.from_dense(dup_add_ground(self.to_dense(f), c, self.domain)) def dmp_add_ground(self, f, c): return self.from_dense(dmp_add_ground(self.to_dense(f), c, self.ngens-1, self.domain)) def dup_sub_ground(self, f, c): return self.from_dense(dup_sub_ground(self.to_dense(f), c, self.domain)) def dmp_sub_ground(self, f, c): return self.from_dense(dmp_sub_ground(self.to_dense(f), c, self.ngens-1, self.domain)) def dup_mul_ground(self, f, c): return self.from_dense(dup_mul_ground(self.to_dense(f), c, self.domain)) def dmp_mul_ground(self, f, c): return self.from_dense(dmp_mul_ground(self.to_dense(f), c, self.ngens-1, self.domain)) def dup_quo_ground(self, f, c): return self.from_dense(dup_quo_ground(self.to_dense(f), c, self.domain)) def dmp_quo_ground(self, f, c): return self.from_dense(dmp_quo_ground(self.to_dense(f), c, self.ngens-1, self.domain)) def dup_exquo_ground(self, f, c): return self.from_dense(dup_exquo_ground(self.to_dense(f), c, self.domain)) def dmp_exquo_ground(self, f, c): return self.from_dense(dmp_exquo_ground(self.to_dense(f), c, self.ngens-1, self.domain)) def dup_lshift(self, f, n): return self.from_dense(dup_lshift(self.to_dense(f), n, self.domain)) def dup_rshift(self, f, n): return self.from_dense(dup_rshift(self.to_dense(f), n, self.domain)) def dup_abs(self, f): return self.from_dense(dup_abs(self.to_dense(f), self.domain)) def dmp_abs(self, f): return self.from_dense(dmp_abs(self.to_dense(f), self.ngens-1, self.domain)) def dup_neg(self, f): return self.from_dense(dup_neg(self.to_dense(f), self.domain)) def dmp_neg(self, f): return self.from_dense(dmp_neg(self.to_dense(f), self.ngens-1, self.domain)) def dup_add(self, f, g): return self.from_dense(dup_add(self.to_dense(f), self.to_dense(g), self.domain)) def dmp_add(self, f, g): return self.from_dense(dmp_add(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain)) def dup_sub(self, f, g): return self.from_dense(dup_sub(self.to_dense(f), self.to_dense(g), self.domain)) def dmp_sub(self, f, g): return self.from_dense(dmp_sub(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain)) def dup_add_mul(self, f, g, h): return self.from_dense(dup_add_mul(self.to_dense(f), self.to_dense(g), self.to_dense(h), self.domain)) def dmp_add_mul(self, f, g, h): return self.from_dense(dmp_add_mul(self.to_dense(f), self.to_dense(g), self.to_dense(h), self.ngens-1, self.domain)) def dup_sub_mul(self, f, g, h): return self.from_dense(dup_sub_mul(self.to_dense(f), self.to_dense(g), self.to_dense(h), self.domain)) def dmp_sub_mul(self, f, g, h): return self.from_dense(dmp_sub_mul(self.to_dense(f), self.to_dense(g), self.to_dense(h), self.ngens-1, self.domain)) def dup_mul(self, f, g): return self.from_dense(dup_mul(self.to_dense(f), self.to_dense(g), self.domain)) def dmp_mul(self, f, g): return self.from_dense(dmp_mul(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain)) def dup_sqr(self, f): return self.from_dense(dup_sqr(self.to_dense(f), self.domain)) def dmp_sqr(self, f): return self.from_dense(dmp_sqr(self.to_dense(f), self.ngens-1, self.domain)) def dup_pow(self, f, n): return self.from_dense(dup_pow(self.to_dense(f), n, self.domain)) def dmp_pow(self, f, n): return self.from_dense(dmp_pow(self.to_dense(f), n, self.ngens-1, self.domain)) def dup_pdiv(self, f, g): q, r = dup_pdiv(self.to_dense(f), self.to_dense(g), self.domain) return (self.from_dense(q), self.from_dense(r)) def dup_prem(self, f, g): return self.from_dense(dup_prem(self.to_dense(f), self.to_dense(g), self.domain)) def dup_pquo(self, f, g): return self.from_dense(dup_pquo(self.to_dense(f), self.to_dense(g), self.domain)) def dup_pexquo(self, f, g): return self.from_dense(dup_pexquo(self.to_dense(f), self.to_dense(g), self.domain)) def dmp_pdiv(self, f, g): q, r = dmp_pdiv(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) return (self.from_dense(q), self.from_dense(r)) def dmp_prem(self, f, g): return self.from_dense(dmp_prem(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain)) def dmp_pquo(self, f, g): return self.from_dense(dmp_pquo(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain)) def dmp_pexquo(self, f, g): return self.from_dense(dmp_pexquo(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain)) def dup_rr_div(self, f, g): q, r = dup_rr_div(self.to_dense(f), self.to_dense(g), self.domain) return (self.from_dense(q), self.from_dense(r)) def dmp_rr_div(self, f, g): q, r = dmp_rr_div(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) return (self.from_dense(q), self.from_dense(r)) def dup_ff_div(self, f, g): q, r = dup_ff_div(self.to_dense(f), self.to_dense(g), self.domain) return (self.from_dense(q), self.from_dense(r)) def dmp_ff_div(self, f, g): q, r = dmp_ff_div(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) return (self.from_dense(q), self.from_dense(r)) def dup_div(self, f, g): q, r = dup_div(self.to_dense(f), self.to_dense(g), self.domain) return (self.from_dense(q), self.from_dense(r)) def dup_rem(self, f, g): return self.from_dense(dup_rem(self.to_dense(f), self.to_dense(g), self.domain)) def dup_quo(self, f, g): return self.from_dense(dup_quo(self.to_dense(f), self.to_dense(g), self.domain)) def dup_exquo(self, f, g): return self.from_dense(dup_exquo(self.to_dense(f), self.to_dense(g), self.domain)) def dmp_div(self, f, g): q, r = dmp_div(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) return (self.from_dense(q), self.from_dense(r)) def dmp_rem(self, f, g): return self.from_dense(dmp_rem(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain)) def dmp_quo(self, f, g): return self.from_dense(dmp_quo(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain)) def dmp_exquo(self, f, g): return self.from_dense(dmp_exquo(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain)) def dup_max_norm(self, f): return dup_max_norm(self.to_dense(f), self.domain) def dmp_max_norm(self, f): return dmp_max_norm(self.to_dense(f), self.ngens-1, self.domain) def dup_l1_norm(self, f): return dup_l1_norm(self.to_dense(f), self.domain) def dmp_l1_norm(self, f): return dmp_l1_norm(self.to_dense(f), self.ngens-1, self.domain) def dup_expand(self, polys): return self.from_dense(dup_expand(list(map(self.to_dense, polys)), self.domain)) def dmp_expand(self, polys): return self.from_dense(dmp_expand(list(map(self.to_dense, polys)), self.ngens-1, self.domain)) def dup_LC(self, f): return dup_LC(self.to_dense(f), self.domain) def dmp_LC(self, f): LC = dmp_LC(self.to_dense(f), self.domain) if isinstance(LC, list): return self[1:].from_dense(LC) else: return LC def dup_TC(self, f): return dup_TC(self.to_dense(f), self.domain) def dmp_TC(self, f): TC = dmp_TC(self.to_dense(f), self.domain) if isinstance(TC, list): return self[1:].from_dense(TC) else: return TC def dmp_ground_LC(self, f): return dmp_ground_LC(self.to_dense(f), self.ngens-1, self.domain) def dmp_ground_TC(self, f): return dmp_ground_TC(self.to_dense(f), self.ngens-1, self.domain) def dup_degree(self, f): return dup_degree(self.to_dense(f)) def dmp_degree(self, f): return dmp_degree(self.to_dense(f), self.ngens-1) def dmp_degree_in(self, f, j): return dmp_degree_in(self.to_dense(f), j, self.ngens-1) def dup_integrate(self, f, m): return self.from_dense(dup_integrate(self.to_dense(f), m, self.domain)) def dmp_integrate(self, f, m): return self.from_dense(dmp_integrate(self.to_dense(f), m, self.ngens-1, self.domain)) def dup_diff(self, f, m): return self.from_dense(dup_diff(self.to_dense(f), m, self.domain)) def dmp_diff(self, f, m): return self.from_dense(dmp_diff(self.to_dense(f), m, self.ngens-1, self.domain)) def dmp_diff_in(self, f, m, j): return self.from_dense(dmp_diff_in(self.to_dense(f), m, j, self.ngens-1, self.domain)) def dmp_integrate_in(self, f, m, j): return self.from_dense(dmp_integrate_in(self.to_dense(f), m, j, self.ngens-1, self.domain)) def dup_eval(self, f, a): return dup_eval(self.to_dense(f), a, self.domain) def dmp_eval(self, f, a): result = dmp_eval(self.to_dense(f), a, self.ngens-1, self.domain) return self[1:].from_dense(result) def dmp_eval_in(self, f, a, j): result = dmp_eval_in(self.to_dense(f), a, j, self.ngens-1, self.domain) return self.drop(j).from_dense(result) def dmp_diff_eval_in(self, f, m, a, j): result = dmp_diff_eval_in(self.to_dense(f), m, a, j, self.ngens-1, self.domain) return self.drop(j).from_dense(result) def dmp_eval_tail(self, f, A): result = dmp_eval_tail(self.to_dense(f), A, self.ngens-1, self.domain) if isinstance(result, list): return self[:-len(A)].from_dense(result) else: return result def dup_trunc(self, f, p): return self.from_dense(dup_trunc(self.to_dense(f), p, self.domain)) def dmp_trunc(self, f, g): return self.from_dense(dmp_trunc(self.to_dense(f), self[1:].to_dense(g), self.ngens-1, self.domain)) def dmp_ground_trunc(self, f, p): return self.from_dense(dmp_ground_trunc(self.to_dense(f), p, self.ngens-1, self.domain)) def dup_monic(self, f): return self.from_dense(dup_monic(self.to_dense(f), self.domain)) def dmp_ground_monic(self, f): return self.from_dense(dmp_ground_monic(self.to_dense(f), self.ngens-1, self.domain)) def dup_extract(self, f, g): c, F, G = dup_extract(self.to_dense(f), self.to_dense(g), self.domain) return (c, self.from_dense(F), self.from_dense(G)) def dmp_ground_extract(self, f, g): c, F, G = dmp_ground_extract(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) return (c, self.from_dense(F), self.from_dense(G)) def dup_real_imag(self, f): p, q = dup_real_imag(self.wrap(f).drop(1).to_dense(), self.domain) return (self.from_dense(p), self.from_dense(q)) def dup_mirror(self, f): return self.from_dense(dup_mirror(self.to_dense(f), self.domain)) def dup_scale(self, f, a): return self.from_dense(dup_scale(self.to_dense(f), a, self.domain)) def dup_shift(self, f, a): return self.from_dense(dup_shift(self.to_dense(f), a, self.domain)) def dup_transform(self, f, p, q): return self.from_dense(dup_transform(self.to_dense(f), self.to_dense(p), self.to_dense(q), self.domain)) def dup_compose(self, f, g): return self.from_dense(dup_compose(self.to_dense(f), self.to_dense(g), self.domain)) def dmp_compose(self, f, g): return self.from_dense(dmp_compose(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain)) def dup_decompose(self, f): components = dup_decompose(self.to_dense(f), self.domain) return list(map(self.from_dense, components)) def dmp_lift(self, f): result = dmp_lift(self.to_dense(f), self.ngens-1, self.domain) return self.to_ground().from_dense(result) def dup_sign_variations(self, f): return dup_sign_variations(self.to_dense(f), self.domain) def dup_clear_denoms(self, f, convert=False): c, F = dup_clear_denoms(self.to_dense(f), self.domain, convert=convert) if convert: ring = self.clone(domain=self.domain.get_ring()) else: ring = self return (c, ring.from_dense(F)) def dmp_clear_denoms(self, f, convert=False): c, F = dmp_clear_denoms(self.to_dense(f), self.ngens-1, self.domain, convert=convert) if convert: ring = self.clone(domain=self.domain.get_ring()) else: ring = self return (c, ring.from_dense(F)) def dup_revert(self, f, n): return self.from_dense(dup_revert(self.to_dense(f), n, self.domain)) def dup_half_gcdex(self, f, g): s, h = dup_half_gcdex(self.to_dense(f), self.to_dense(g), self.domain) return (self.from_dense(s), self.from_dense(h)) def dmp_half_gcdex(self, f, g): s, h = dmp_half_gcdex(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) return (self.from_dense(s), self.from_dense(h)) def dup_gcdex(self, f, g): s, t, h = dup_gcdex(self.to_dense(f), self.to_dense(g), self.domain) return (self.from_dense(s), self.from_dense(t), self.from_dense(h)) def dmp_gcdex(self, f, g): s, t, h = dmp_gcdex(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) return (self.from_dense(s), self.from_dense(t), self.from_dense(h)) def dup_invert(self, f, g): return self.from_dense(dup_invert(self.to_dense(f), self.to_dense(g), self.domain)) def dmp_invert(self, f, g): return self.from_dense(dmp_invert(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain)) def dup_euclidean_prs(self, f, g): prs = dup_euclidean_prs(self.to_dense(f), self.to_dense(g), self.domain) return list(map(self.from_dense, prs)) def dmp_euclidean_prs(self, f, g): prs = dmp_euclidean_prs(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) return list(map(self.from_dense, prs)) def dup_primitive_prs(self, f, g): prs = dup_primitive_prs(self.to_dense(f), self.to_dense(g), self.domain) return list(map(self.from_dense, prs)) def dmp_primitive_prs(self, f, g): prs = dmp_primitive_prs(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) return list(map(self.from_dense, prs)) def dup_inner_subresultants(self, f, g): prs, beta, delta = dup_inner_subresultants(self.to_dense(f), self.to_dense(g), self.domain) return (list(map(self.from_dense, prs)), beta, delta) def dmp_inner_subresultants(self, f, g): prs, beta, delta = dmp_inner_subresultants(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) return (list(map(self.from_dense, prs)), beta, delta) def dup_subresultants(self, f, g): prs = dup_subresultants(self.to_dense(f), self.to_dense(g), self.domain) return list(map(self.from_dense, prs)) def dmp_subresultants(self, f, g): prs = dmp_subresultants(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) return list(map(self.from_dense, prs)) def dup_prs_resultant(self, f, g): res, prs = dup_prs_resultant(self.to_dense(f), self.to_dense(g), self.domain) return (res, list(map(self.from_dense, prs))) def dmp_prs_resultant(self, f, g): res, prs = dmp_prs_resultant(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) return (self[1:].from_dense(res), list(map(self.from_dense, prs))) def dmp_zz_modular_resultant(self, f, g, p): res = dmp_zz_modular_resultant(self.to_dense(f), self.to_dense(g), self.domain_new(p), self.ngens-1, self.domain) return self[1:].from_dense(res) def dmp_zz_collins_resultant(self, f, g): res = dmp_zz_collins_resultant(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) return self[1:].from_dense(res) def dmp_qq_collins_resultant(self, f, g): res = dmp_qq_collins_resultant(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) return self[1:].from_dense(res) def dup_resultant(self, f, g): #, includePRS=False): return dup_resultant(self.to_dense(f), self.to_dense(g), self.domain) #, includePRS=includePRS) def dmp_resultant(self, f, g): #, includePRS=False): res = dmp_resultant(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) #, includePRS=includePRS) if isinstance(res, list): return self[1:].from_dense(res) else: return res def dup_discriminant(self, f): return dup_discriminant(self.to_dense(f), self.domain) def dmp_discriminant(self, f): disc = dmp_discriminant(self.to_dense(f), self.ngens-1, self.domain) if isinstance(disc, list): return self[1:].from_dense(disc) else: return disc def dup_rr_prs_gcd(self, f, g): H, F, G = dup_rr_prs_gcd(self.to_dense(f), self.to_dense(g), self.domain) return (self.from_dense(H), self.from_dense(F), self.from_dense(G)) def dup_ff_prs_gcd(self, f, g): H, F, G = dup_ff_prs_gcd(self.to_dense(f), self.to_dense(g), self.domain) return (self.from_dense(H), self.from_dense(F), self.from_dense(G)) def dmp_rr_prs_gcd(self, f, g): H, F, G = dmp_rr_prs_gcd(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) return (self.from_dense(H), self.from_dense(F), self.from_dense(G)) def dmp_ff_prs_gcd(self, f, g): H, F, G = dmp_ff_prs_gcd(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) return (self.from_dense(H), self.from_dense(F), self.from_dense(G)) def dup_zz_heu_gcd(self, f, g): H, F, G = dup_zz_heu_gcd(self.to_dense(f), self.to_dense(g), self.domain) return (self.from_dense(H), self.from_dense(F), self.from_dense(G)) def dmp_zz_heu_gcd(self, f, g): H, F, G = dmp_zz_heu_gcd(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) return (self.from_dense(H), self.from_dense(F), self.from_dense(G)) def dup_qq_heu_gcd(self, f, g): H, F, G = dup_qq_heu_gcd(self.to_dense(f), self.to_dense(g), self.domain) return (self.from_dense(H), self.from_dense(F), self.from_dense(G)) def dmp_qq_heu_gcd(self, f, g): H, F, G = dmp_qq_heu_gcd(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) return (self.from_dense(H), self.from_dense(F), self.from_dense(G)) def dup_inner_gcd(self, f, g): H, F, G = dup_inner_gcd(self.to_dense(f), self.to_dense(g), self.domain) return (self.from_dense(H), self.from_dense(F), self.from_dense(G)) def dmp_inner_gcd(self, f, g): H, F, G = dmp_inner_gcd(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) return (self.from_dense(H), self.from_dense(F), self.from_dense(G)) def dup_gcd(self, f, g): H = dup_gcd(self.to_dense(f), self.to_dense(g), self.domain) return self.from_dense(H) def dmp_gcd(self, f, g): H = dmp_gcd(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) return self.from_dense(H) def dup_rr_lcm(self, f, g): H = dup_rr_lcm(self.to_dense(f), self.to_dense(g), self.domain) return self.from_dense(H) def dup_ff_lcm(self, f, g): H = dup_ff_lcm(self.to_dense(f), self.to_dense(g), self.domain) return self.from_dense(H) def dup_lcm(self, f, g): H = dup_lcm(self.to_dense(f), self.to_dense(g), self.domain) return self.from_dense(H) def dmp_rr_lcm(self, f, g): H = dmp_rr_lcm(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) return self.from_dense(H) def dmp_ff_lcm(self, f, g): H = dmp_ff_lcm(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) return self.from_dense(H) def dmp_lcm(self, f, g): H = dmp_lcm(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain) return self.from_dense(H) def dup_content(self, f): cont = dup_content(self.to_dense(f), self.domain) return cont def dup_primitive(self, f): cont, prim = dup_primitive(self.to_dense(f), self.domain) return cont, self.from_dense(prim) def dmp_content(self, f): cont = dmp_content(self.to_dense(f), self.ngens-1, self.domain) if isinstance(cont, list): return self[1:].from_dense(cont) else: return cont def dmp_primitive(self, f): cont, prim = dmp_primitive(self.to_dense(f), self.ngens-1, self.domain) if isinstance(cont, list): return (self[1:].from_dense(cont), self.from_dense(prim)) else: return (cont, self.from_dense(prim)) def dmp_ground_content(self, f): cont = dmp_ground_content(self.to_dense(f), self.ngens-1, self.domain) return cont def dmp_ground_primitive(self, f): cont, prim = dmp_ground_primitive(self.to_dense(f), self.ngens-1, self.domain) return (cont, self.from_dense(prim)) def dup_cancel(self, f, g, include=True): result = dup_cancel(self.to_dense(f), self.to_dense(g), self.domain, include=include) if not include: cf, cg, F, G = result return (cf, cg, self.from_dense(F), self.from_dense(G)) else: F, G = result return (self.from_dense(F), self.from_dense(G)) def dmp_cancel(self, f, g, include=True): result = dmp_cancel(self.to_dense(f), self.to_dense(g), self.ngens-1, self.domain, include=include) if not include: cf, cg, F, G = result return (cf, cg, self.from_dense(F), self.from_dense(G)) else: F, G = result return (self.from_dense(F), self.from_dense(G)) def dup_trial_division(self, f, factors): factors = dup_trial_division(self.to_dense(f), list(map(self.to_dense, factors)), self.domain) return [ (self.from_dense(g), k) for g, k in factors ] def dmp_trial_division(self, f, factors): factors = dmp_trial_division(self.to_dense(f), list(map(self.to_dense, factors)), self.ngens-1, self.domain) return [ (self.from_dense(g), k) for g, k in factors ] def dup_zz_mignotte_bound(self, f): return dup_zz_mignotte_bound(self.to_dense(f), self.domain) def dmp_zz_mignotte_bound(self, f): return dmp_zz_mignotte_bound(self.to_dense(f), self.ngens-1, self.domain) def dup_zz_hensel_step(self, m, f, g, h, s, t): D = self.to_dense G, H, S, T = dup_zz_hensel_step(m, D(f), D(g), D(h), D(s), D(t), self.domain) return (self.from_dense(G), self.from_dense(H), self.from_dense(S), self.from_dense(T)) def dup_zz_hensel_lift(self, p, f, f_list, l): D = self.to_dense polys = dup_zz_hensel_lift(p, D(f), list(map(D, f_list)), l, self.domain) return list(map(self.from_dense, polys)) def dup_zz_zassenhaus(self, f): factors = dup_zz_zassenhaus(self.to_dense(f), self.domain) return [ (self.from_dense(g), k) for g, k in factors ] def dup_zz_irreducible_p(self, f): return dup_zz_irreducible_p(self.to_dense(f), self.domain) def dup_cyclotomic_p(self, f, irreducible=False): return dup_cyclotomic_p(self.to_dense(f), self.domain, irreducible=irreducible) def dup_zz_cyclotomic_poly(self, n): F = dup_zz_cyclotomic_poly(n, self.domain) return self.from_dense(F) def dup_zz_cyclotomic_factor(self, f): result = dup_zz_cyclotomic_factor(self.to_dense(f), self.domain) if result is None: return result else: return list(map(self.from_dense, result)) # E: List[ZZ], cs: ZZ, ct: ZZ def dmp_zz_wang_non_divisors(self, E, cs, ct): return dmp_zz_wang_non_divisors(E, cs, ct, self.domain) # f: Poly, T: List[(Poly, int)], ct: ZZ, A: List[ZZ] #def dmp_zz_wang_test_points(f, T, ct, A): # dmp_zz_wang_test_points(self.to_dense(f), T, ct, A, self.ngens-1, self.domain) # f: Poly, T: List[(Poly, int)], cs: ZZ, E: List[ZZ], H: List[Poly], A: List[ZZ] def dmp_zz_wang_lead_coeffs(self, f, T, cs, E, H, A): mv = self[1:] T = [ (mv.to_dense(t), k) for t, k in T ] uv = self[:1] H = list(map(uv.to_dense, H)) f, HH, CC = dmp_zz_wang_lead_coeffs(self.to_dense(f), T, cs, E, H, A, self.ngens-1, self.domain) return self.from_dense(f), list(map(uv.from_dense, HH)), list(map(mv.from_dense, CC)) # f: List[Poly], m: int, p: ZZ def dup_zz_diophantine(self, F, m, p): result = dup_zz_diophantine(list(map(self.to_dense, F)), m, p, self.domain) return list(map(self.from_dense, result)) # f: List[Poly], c: List[Poly], A: List[ZZ], d: int, p: ZZ def dmp_zz_diophantine(self, F, c, A, d, p): result = dmp_zz_diophantine(list(map(self.to_dense, F)), self.to_dense(c), A, d, p, self.ngens-1, self.domain) return list(map(self.from_dense, result)) # f: Poly, H: List[Poly], LC: List[Poly], A: List[ZZ], p: ZZ def dmp_zz_wang_hensel_lifting(self, f, H, LC, A, p): uv = self[:1] mv = self[1:] H = list(map(uv.to_dense, H)) LC = list(map(mv.to_dense, LC)) result = dmp_zz_wang_hensel_lifting(self.to_dense(f), H, LC, A, p, self.ngens-1, self.domain) return list(map(self.from_dense, result)) def dmp_zz_wang(self, f, mod=None, seed=None): factors = dmp_zz_wang(self.to_dense(f), self.ngens-1, self.domain, mod=mod, seed=seed) return [ self.from_dense(g) for g in factors ] def dup_zz_factor_sqf(self, f): coeff, factors = dup_zz_factor_sqf(self.to_dense(f), self.domain) return (coeff, [ self.from_dense(g) for g in factors ]) def dup_zz_factor(self, f): coeff, factors = dup_zz_factor(self.to_dense(f), self.domain) return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) def dmp_zz_factor(self, f): coeff, factors = dmp_zz_factor(self.to_dense(f), self.ngens-1, self.domain) return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) def dup_ext_factor(self, f): coeff, factors = dup_ext_factor(self.to_dense(f), self.domain) return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) def dmp_ext_factor(self, f): coeff, factors = dmp_ext_factor(self.to_dense(f), self.ngens-1, self.domain) return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) def dup_gf_factor(self, f): coeff, factors = dup_gf_factor(self.to_dense(f), self.domain) return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) def dmp_gf_factor(self, f): coeff, factors = dmp_gf_factor(self.to_dense(f), self.ngens-1, self.domain) return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) def dup_factor_list(self, f): coeff, factors = dup_factor_list(self.to_dense(f), self.domain) return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) def dup_factor_list_include(self, f): factors = dup_factor_list_include(self.to_dense(f), self.domain) return [ (self.from_dense(g), k) for g, k in factors ] def dmp_factor_list(self, f): coeff, factors = dmp_factor_list(self.to_dense(f), self.ngens-1, self.domain) return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) def dmp_factor_list_include(self, f): factors = dmp_factor_list_include(self.to_dense(f), self.ngens-1, self.domain) return [ (self.from_dense(g), k) for g, k in factors ] def dup_irreducible_p(self, f): return dup_irreducible_p(self.to_dense(f), self.domain) def dmp_irreducible_p(self, f): return dmp_irreducible_p(self.to_dense(f), self.ngens-1, self.domain) def dup_sturm(self, f): seq = dup_sturm(self.to_dense(f), self.domain) return list(map(self.from_dense, seq)) def dup_sqf_p(self, f): return dup_sqf_p(self.to_dense(f), self.domain) def dmp_sqf_p(self, f): return dmp_sqf_p(self.to_dense(f), self.ngens-1, self.domain) def dup_sqf_norm(self, f): s, F, R = dup_sqf_norm(self.to_dense(f), self.domain) return (s, self.from_dense(F), self.to_ground().from_dense(R)) def dmp_sqf_norm(self, f): s, F, R = dmp_sqf_norm(self.to_dense(f), self.ngens-1, self.domain) return (s, self.from_dense(F), self.to_ground().from_dense(R)) def dup_gf_sqf_part(self, f): return self.from_dense(dup_gf_sqf_part(self.to_dense(f), self.domain)) def dmp_gf_sqf_part(self, f): return self.from_dense(dmp_gf_sqf_part(self.to_dense(f), self.domain)) def dup_sqf_part(self, f): return self.from_dense(dup_sqf_part(self.to_dense(f), self.domain)) def dmp_sqf_part(self, f): return self.from_dense(dmp_sqf_part(self.to_dense(f), self.ngens-1, self.domain)) def dup_gf_sqf_list(self, f, all=False): coeff, factors = dup_gf_sqf_list(self.to_dense(f), self.domain, all=all) return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) def dmp_gf_sqf_list(self, f, all=False): coeff, factors = dmp_gf_sqf_list(self.to_dense(f), self.ngens-1, self.domain, all=all) return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) def dup_sqf_list(self, f, all=False): coeff, factors = dup_sqf_list(self.to_dense(f), self.domain, all=all) return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) def dup_sqf_list_include(self, f, all=False): factors = dup_sqf_list_include(self.to_dense(f), self.domain, all=all) return [ (self.from_dense(g), k) for g, k in factors ] def dmp_sqf_list(self, f, all=False): coeff, factors = dmp_sqf_list(self.to_dense(f), self.ngens-1, self.domain, all=all) return (coeff, [ (self.from_dense(g), k) for g, k in factors ]) def dmp_sqf_list_include(self, f, all=False): factors = dmp_sqf_list_include(self.to_dense(f), self.ngens-1, self.domain, all=all) return [ (self.from_dense(g), k) for g, k in factors ] def dup_gff_list(self, f): factors = dup_gff_list(self.to_dense(f), self.domain) return [ (self.from_dense(g), k) for g, k in factors ] def dmp_gff_list(self, f): factors = dmp_gff_list(self.to_dense(f), self.ngens-1, self.domain) return [ (self.from_dense(g), k) for g, k in factors ] def dup_root_upper_bound(self, f): return dup_root_upper_bound(self.to_dense(f), self.domain) def dup_root_lower_bound(self, f): return dup_root_lower_bound(self.to_dense(f), self.domain) def dup_step_refine_real_root(self, f, M, fast=False): return dup_step_refine_real_root(self.to_dense(f), M, self.domain, fast=fast) def dup_inner_refine_real_root(self, f, M, eps=None, steps=None, disjoint=None, fast=False, mobius=False): return dup_inner_refine_real_root(self.to_dense(f), M, self.domain, eps=eps, steps=steps, disjoint=disjoint, fast=fast, mobius=mobius) def dup_outer_refine_real_root(self, f, s, t, eps=None, steps=None, disjoint=None, fast=False): return dup_outer_refine_real_root(self.to_dense(f), s, t, self.domain, eps=eps, steps=steps, disjoint=disjoint, fast=fast) def dup_refine_real_root(self, f, s, t, eps=None, steps=None, disjoint=None, fast=False): return dup_refine_real_root(self.to_dense(f), s, t, self.domain, eps=eps, steps=steps, disjoint=disjoint, fast=fast) def dup_inner_isolate_real_roots(self, f, eps=None, fast=False): return dup_inner_isolate_real_roots(self.to_dense(f), self.domain, eps=eps, fast=fast) def dup_inner_isolate_positive_roots(self, f, eps=None, inf=None, sup=None, fast=False, mobius=False): return dup_inner_isolate_positive_roots(self.to_dense(f), self.domain, eps=eps, inf=inf, sup=sup, fast=fast, mobius=mobius) def dup_inner_isolate_negative_roots(self, f, inf=None, sup=None, eps=None, fast=False, mobius=False): return dup_inner_isolate_negative_roots(self.to_dense(f), self.domain, inf=inf, sup=sup, eps=eps, fast=fast, mobius=mobius) def dup_isolate_real_roots_sqf(self, f, eps=None, inf=None, sup=None, fast=False, blackbox=False): return dup_isolate_real_roots_sqf(self.to_dense(f), self.domain, eps=eps, inf=inf, sup=sup, fast=fast, blackbox=blackbox) def dup_isolate_real_roots(self, f, eps=None, inf=None, sup=None, basis=False, fast=False): return dup_isolate_real_roots(self.to_dense(f), self.domain, eps=eps, inf=inf, sup=sup, basis=basis, fast=fast) def dup_isolate_real_roots_list(self, polys, eps=None, inf=None, sup=None, strict=False, basis=False, fast=False): return dup_isolate_real_roots_list(list(map(self.to_dense, polys)), self.domain, eps=eps, inf=inf, sup=sup, strict=strict, basis=basis, fast=fast) def dup_count_real_roots(self, f, inf=None, sup=None): return dup_count_real_roots(self.to_dense(f), self.domain, inf=inf, sup=sup) def dup_count_complex_roots(self, f, inf=None, sup=None, exclude=None): return dup_count_complex_roots(self.to_dense(f), self.domain, inf=inf, sup=sup, exclude=exclude) def dup_isolate_complex_roots_sqf(self, f, eps=None, inf=None, sup=None, blackbox=False): return dup_isolate_complex_roots_sqf(self.to_dense(f), self.domain, eps=eps, inf=inf, sup=sup, blackbox=blackbox) def dup_isolate_all_roots_sqf(self, f, eps=None, inf=None, sup=None, fast=False, blackbox=False): return dup_isolate_all_roots_sqf(self.to_dense(f), self.domain, eps=eps, inf=inf, sup=sup, fast=fast, blackbox=blackbox) def dup_isolate_all_roots(self, f, eps=None, inf=None, sup=None, fast=False): return dup_isolate_all_roots(self.to_dense(f), self.domain, eps=eps, inf=inf, sup=sup, fast=fast) def fateman_poly_F_1(self): from sympy.polys.specialpolys import dmp_fateman_poly_F_1 return tuple(map(self.from_dense, dmp_fateman_poly_F_1(self.ngens-1, self.domain))) def fateman_poly_F_2(self): from sympy.polys.specialpolys import dmp_fateman_poly_F_2 return tuple(map(self.from_dense, dmp_fateman_poly_F_2(self.ngens-1, self.domain))) def fateman_poly_F_3(self): from sympy.polys.specialpolys import dmp_fateman_poly_F_3 return tuple(map(self.from_dense, dmp_fateman_poly_F_3(self.ngens-1, self.domain))) def to_gf_dense(self, element): return gf_strip([ self.domain.dom.convert(c, self.domain) for c in self.wrap(element).to_dense() ]) def from_gf_dense(self, element): return self.from_dict(dmp_to_dict(element, self.ngens-1, self.domain.dom)) def gf_degree(self, f): return gf_degree(self.to_gf_dense(f)) def gf_LC(self, f): return gf_LC(self.to_gf_dense(f), self.domain.dom) def gf_TC(self, f): return gf_TC(self.to_gf_dense(f), self.domain.dom) def gf_strip(self, f): return self.from_gf_dense(gf_strip(self.to_gf_dense(f))) def gf_trunc(self, f): return self.from_gf_dense(gf_strip(self.to_gf_dense(f), self.domain.mod)) def gf_normal(self, f): return self.from_gf_dense(gf_strip(self.to_gf_dense(f), self.domain.mod, self.domain.dom)) def gf_from_dict(self, f): return self.from_gf_dense(gf_from_dict(f, self.domain.mod, self.domain.dom)) def gf_to_dict(self, f, symmetric=True): return gf_to_dict(self.to_gf_dense(f), self.domain.mod, symmetric=symmetric) def gf_from_int_poly(self, f): return self.from_gf_dense(gf_from_int_poly(f, self.domain.mod)) def gf_to_int_poly(self, f, symmetric=True): return gf_to_int_poly(self.to_gf_dense(f), self.domain.mod, symmetric=symmetric) def gf_neg(self, f): return self.from_gf_dense(gf_neg(self.to_gf_dense(f), self.domain.mod, self.domain.dom)) def gf_add_ground(self, f, a): return self.from_gf_dense(gf_add_ground(self.to_gf_dense(f), a, self.domain.mod, self.domain.dom)) def gf_sub_ground(self, f, a): return self.from_gf_dense(gf_sub_ground(self.to_gf_dense(f), a, self.domain.mod, self.domain.dom)) def gf_mul_ground(self, f, a): return self.from_gf_dense(gf_mul_ground(self.to_gf_dense(f), a, self.domain.mod, self.domain.dom)) def gf_quo_ground(self, f, a): return self.from_gf_dense(gf_quo_ground(self.to_gf_dense(f), a, self.domain.mod, self.domain.dom)) def gf_add(self, f, g): return self.from_gf_dense(gf_add(self.to_gf_dense(f), self.to_gf_dense(g), self.domain.mod, self.domain.dom)) def gf_sub(self, f, g): return self.from_gf_dense(gf_sub(self.to_gf_dense(f), self.to_gf_dense(g), self.domain.mod, self.domain.dom)) def gf_mul(self, f, g): return self.from_gf_dense(gf_mul(self.to_gf_dense(f), self.to_gf_dense(g), self.domain.mod, self.domain.dom)) def gf_sqr(self, f): return self.from_gf_dense(gf_sqr(self.to_gf_dense(f), self.domain.mod, self.domain.dom)) def gf_add_mul(self, f, g, h): return self.from_gf_dense(gf_add_mul(self.to_gf_dense(f), self.to_gf_dense(g), self.to_gf_dense(h), self.domain.mod, self.domain.dom)) def gf_sub_mul(self, f, g, h): return self.from_gf_dense(gf_sub_mul(self.to_gf_dense(f), self.to_gf_dense(g), self.to_gf_dense(h), self.domain.mod, self.domain.dom)) def gf_expand(self, F): return self.from_gf_dense(gf_expand(list(map(self.to_gf_dense, F)), self.domain.mod, self.domain.dom)) def gf_div(self, f, g): q, r = gf_div(self.to_gf_dense(f), self.to_gf_dense(g), self.domain.mod, self.domain.dom) return self.from_gf_dense(q), self.from_gf_dense(r) def gf_rem(self, f, g): return self.from_gf_dense(gf_rem(self.to_gf_dense(f), self.to_gf_dense(g), self.domain.mod, self.domain.dom)) def gf_quo(self, f, g): return self.from_gf_dense(gf_quo(self.to_gf_dense(f), self.to_gf_dense(g), self.domain.mod, self.domain.dom)) def gf_exquo(self, f, g): return self.from_gf_dense(gf_exquo(self.to_gf_dense(f), self.to_gf_dense(g), self.domain.mod, self.domain.dom)) def gf_lshift(self, f, n): return self.from_gf_dense(gf_lshift(self.to_gf_dense(f), n, self.domain.dom)) def gf_rshift(self, f, n): return self.from_gf_dense(gf_rshift(self.to_gf_dense(f), n, self.domain.dom)) def gf_pow(self, f, n): return self.from_gf_dense(gf_pow(self.to_gf_dense(f), n, self.domain.mod, self.domain.dom)) def gf_pow_mod(self, f, n, g): return self.from_gf_dense(gf_pow_mod(self.to_gf_dense(f), n, self.to_gf_dense(g), self.domain.mod, self.domain.dom)) def gf_cofactors(self, f, g): h, cff, cfg = gf_cofactors(self.to_gf_dense(f), self.to_gf_dense(g), self.domain.mod, self.domain.dom) return self.from_gf_dense(h), self.from_gf_dense(cff), self.from_gf_dense(cfg) def gf_gcd(self, f, g): return self.from_gf_dense(gf_gcd(self.to_gf_dense(f), self.to_gf_dense(g), self.domain.mod, self.domain.dom)) def gf_lcm(self, f, g): return self.from_gf_dense(gf_lcm(self.to_gf_dense(f), self.to_gf_dense(g), self.domain.mod, self.domain.dom)) def gf_gcdex(self, f, g): return self.from_gf_dense(gf_gcdex(self.to_gf_dense(f), self.to_gf_dense(g), self.domain.mod, self.domain.dom)) def gf_monic(self, f): return self.from_gf_dense(gf_monic(self.to_gf_dense(f), self.domain.mod, self.domain.dom)) def gf_diff(self, f): return self.from_gf_dense(gf_diff(self.to_gf_dense(f), self.domain.mod, self.domain.dom)) def gf_eval(self, f, a): return gf_eval(self.to_gf_dense(f), a, self.domain.mod, self.domain.dom) def gf_multi_eval(self, f, A): return gf_multi_eval(self.to_gf_dense(f), A, self.domain.mod, self.domain.dom) def gf_compose(self, f, g): return self.from_gf_dense(gf_compose(self.to_gf_dense(f), self.to_gf_dense(g), self.domain.mod, self.domain.dom)) def gf_compose_mod(self, g, h, f): return self.from_gf_dense(gf_compose_mod(self.to_gf_dense(g), self.to_gf_dense(h), self.to_gf_dense(f), self.domain.mod, self.domain.dom)) def gf_trace_map(self, a, b, c, n, f): a = self.to_gf_dense(a) b = self.to_gf_dense(b) c = self.to_gf_dense(c) f = self.to_gf_dense(f) U, V = gf_trace_map(a, b, c, n, f, self.domain.mod, self.domain.dom) return self.from_gf_dense(U), self.from_gf_dense(V) def gf_random(self, n): return self.from_gf_dense(gf_random(n, self.domain.mod, self.domain.dom)) def gf_irreducible(self, n): return self.from_gf_dense(gf_irreducible(n, self.domain.mod, self.domain.dom)) def gf_irred_p_ben_or(self, f): return gf_irred_p_ben_or(self.to_gf_dense(f), self.domain.mod, self.domain.dom) def gf_irred_p_rabin(self, f): return gf_irred_p_rabin(self.to_gf_dense(f), self.domain.mod, self.domain.dom) def gf_irreducible_p(self, f): return gf_irreducible_p(self.to_gf_dense(f), self.domain.mod, self.domain.dom) def gf_sqf_p(self, f): return gf_sqf_p(self.to_gf_dense(f), self.domain.mod, self.domain.dom) def gf_sqf_part(self, f): return self.from_gf_dense(gf_sqf_part(self.to_gf_dense(f), self.domain.mod, self.domain.dom)) def gf_sqf_list(self, f, all=False): coeff, factors = gf_sqf_part(self.to_gf_dense(f), self.domain.mod, self.domain.dom, all=all) return coeff, [ (self.from_gf_dense(g), k) for g, k in factors ] def gf_Qmatrix(self, f): return gf_Qmatrix(self.to_gf_dense(f), self.domain.mod, self.domain.dom) def gf_berlekamp(self, f): factors = gf_berlekamp(self.to_gf_dense(f), self.domain.mod, self.domain.dom) return [ self.from_gf_dense(g) for g in factors ] def gf_ddf_zassenhaus(self, f): factors = gf_ddf_zassenhaus(self.to_gf_dense(f), self.domain.mod, self.domain.dom) return [ (self.from_gf_dense(g), k) for g, k in factors ] def gf_edf_zassenhaus(self, f, n): factors = gf_edf_zassenhaus(self.to_gf_dense(f), self.domain.mod, self.domain.dom) return [ self.from_gf_dense(g) for g in factors ] def gf_ddf_shoup(self, f): factors = gf_ddf_shoup(self.to_gf_dense(f), self.domain.mod, self.domain.dom) return [ (self.from_gf_dense(g), k) for g, k in factors ] def gf_edf_shoup(self, f, n): factors = gf_edf_shoup(self.to_gf_dense(f), self.domain.mod, self.domain.dom) return [ self.from_gf_dense(g) for g in factors ] def gf_zassenhaus(self, f): factors = gf_zassenhaus(self.to_gf_dense(f), self.domain.mod, self.domain.dom) return [ self.from_gf_dense(g) for g in factors ] def gf_shoup(self, f): factors = gf_shoup(self.to_gf_dense(f), self.domain.mod, self.domain.dom) return [ self.from_gf_dense(g) for g in factors ] def gf_factor_sqf(self, f, method=None): coeff, factors = gf_factor_sqf(self.to_gf_dense(f), self.domain.mod, self.domain.dom, method=method) return coeff, [ self.from_gf_dense(g) for g in factors ] def gf_factor(self, f): coeff, factors = gf_factor(self.to_gf_dense(f), self.domain.mod, self.domain.dom) return coeff, [ (self.from_gf_dense(g), k) for g, k in factors ] sympy-0.7.4.1/sympy/polys/fields.py0000644000175000017500000004272512253362407017425 0ustar georgeskgeorgesk"""Sparse rational function fields. """ from __future__ import print_function, division from operator import add, mul, lt, le, gt, ge from sympy.core.compatibility import reduce, string_types from sympy.core.expr import Expr from sympy.core.symbol import Symbol from sympy.core.sympify import CantSympify, sympify from sympy.polys.rings import PolyElement from sympy.polys.orderings import lex from sympy.polys.polyerrors import ExactQuotientFailed, CoercionFailed from sympy.polys.domains.domainelement import DomainElement from sympy.polys.domains.polynomialring import PolynomialRing from sympy.polys.domains.fractionfield import FractionField from sympy.printing.defaults import DefaultPrinting from sympy.utilities import public from sympy.utilities.magic import pollute @public def field(symbols, domain, order=lex): """Construct new rational function field returning (field, x1, ..., xn). """ _field = FracField(symbols, domain, order) return (_field,) + _field.gens @public def xfield(symbols, domain, order=lex): """Construct new rational function field returning (field, (x1, ..., xn)). """ _field = FracField(symbols, domain, order) return (_field, _field.gens) @public def vfield(symbols, domain, order=lex): """Construct new rational function field and inject generators into global namespace. """ _field = FracField(symbols, domain, order) pollute([ sym.name for sym in _field.symbols ], _field.gens) return _field @public def sfield(exprs, *symbols, **options): """Construct a field deriving generators and domain from options and input expressions. """ raise NotImplementedError _field_cache = {} class FracField(DefaultPrinting): """Multivariate distributed rational function field. """ def __new__(cls, symbols, domain, order=lex): from sympy.polys.rings import PolyRing ring = PolyRing(symbols, domain, order) symbols = ring.symbols ngens = ring.ngens domain = ring.domain order = ring.order _hash = hash((cls.__name__, symbols, ngens, domain, order)) obj = _field_cache.get(_hash) if obj is None: obj = object.__new__(cls) obj._hash = _hash obj.ring = ring obj.dtype = type("FracElement", (FracElement,), {"field": obj}) obj.symbols = symbols obj.ngens = ngens obj.domain = domain obj.order = order obj.zero = obj.dtype(ring.zero) obj.one = obj.dtype(ring.one) obj.gens = obj._gens() for symbol, generator in zip(obj.symbols, obj.gens): if isinstance(symbol, Symbol): name = symbol.name if not hasattr(obj, name): setattr(obj, name, generator) _field_cache[_hash] = obj return obj def _gens(self): """Return a list of polynomial generators. """ return tuple([ self.dtype(gen) for gen in self.ring.gens ]) def __getnewargs__(self): return (self.symbols, self.domain, self.order) def __hash__(self): return self._hash def __eq__(self, other): return self is other def __ne__(self, other): return self is not other def raw_new(self, numer, denom=None): return self.dtype(numer, denom) def new(self, numer, denom=None): if denom is None: denom = self.ring.one numer, denom = numer.cancel(denom) return self.raw_new(numer, denom) def domain_new(self, element): return self.domain.convert(element) def ground_new(self, element): try: return self.new(self.ring.ground_new(element)) except CoercionFailed: domain = self.domain if not domain.has_Field and domain.has_assoc_Field: ring = self.ring ground_field = domain.get_field() element = ground_field.convert(element) numer = ring.ground_new(ground_field.numer(element)) denom = ring.ground_new(ground_field.denom(element)) return self.raw_new(numer, denom) else: raise def field_new(self, element): if isinstance(element, FracElement): if self == element.field: return element else: raise NotImplementedError("conversion") elif isinstance(element, PolyElement): denom, numer = element.clear_denoms() numer = numer.set_ring(self.ring) denom = self.ring.ground_new(denom) return self.raw_new(numer, denom) elif isinstance(element, tuple) and len(element) == 2: numer, denom = list(map(self.ring.ring_new, element)) return self.new(numer, denom) elif isinstance(element, string_types): raise NotImplementedError("parsing") elif isinstance(element, Expr): return self.from_expr(element) else: return self.ground_new(element) __call__ = field_new def _rebuild_expr(self, expr, mapping): domain = self.domain def _rebuild(expr): generator = mapping.get(expr) if generator is not None: return generator elif expr.is_Add: return reduce(add, list(map(_rebuild, expr.args))) elif expr.is_Mul: return reduce(mul, list(map(_rebuild, expr.args))) elif expr.is_Pow and expr.exp.is_Integer: return _rebuild(expr.base)**int(expr.exp) else: try: return domain.convert(expr) except CoercionFailed: if not domain.has_Field and domain.has_assoc_Field: return domain.get_field().convert(expr) else: raise return _rebuild(sympify(expr)) def from_expr(self, expr): mapping = dict(list(zip(self.symbols, self.gens))) try: frac = self._rebuild_expr(expr, mapping) except CoercionFailed: raise ValueError("expected an expression convertible to a rational function in %s, got %s" % (self, expr)) else: return self.field_new(frac) def to_domain(self): return FractionField(self) def to_ring(self): from sympy.polys.rings import PolyRing return PolyRing(self.symbols, self.domain, self.order) class FracElement(DomainElement, DefaultPrinting, CantSympify): """Element of multivariate distributed rational function field. """ def __init__(self, numer, denom=None): if denom is None: denom = self.field.ring.one elif not denom: raise ZeroDivisionError("zero denominator") self.numer = numer self.denom = denom def raw_new(f, numer, denom): return f.__class__(numer, denom) def new(f, numer, denom): return f.raw_new(*numer.cancel(denom)) def to_poly(f): assert f.denom == 1 return f.numer def parent(self): return self.field.to_domain() def __getnewargs__(self): return (self.field, self.numer, self.denom) _hash = None def __hash__(self): _hash = self._hash if _hash is None: self._hash = _hash = hash((self.field, self.numer, self.denom)) return _hash def copy(self): return self.raw_new(self.numer.copy(), self.denom.copy()) def set_field(self, new_field): if self.field == new_field: return self else: new_ring = new_field.ring numer = self.numer.set_ring(new_ring) denom = self.denom.set_ring(new_ring) return new_field.new(numer, denom) def as_expr(self, *symbols): return self.numer.as_expr(*symbols)/self.denom.as_expr(*symbols) def __eq__(f, g): if isinstance(g, f.field.dtype): return f.numer == g.numer and f.denom == g.denom else: return f.numer == g and f.denom == f.field.ring.one def __ne__(f, g): return not f.__eq__(g) def __nonzero__(f): return bool(f.numer) __bool__ = __nonzero__ def sort_key(self): return (self.denom.sort_key(), self.numer.sort_key()) def _cmp(f1, f2, op): if isinstance(f2, f1.field.dtype): return op(f1.sort_key(), f2.sort_key()) else: return NotImplemented def __lt__(f1, f2): return f1._cmp(f2, lt) def __le__(f1, f2): return f1._cmp(f2, le) def __gt__(f1, f2): return f1._cmp(f2, gt) def __ge__(f1, f2): return f1._cmp(f2, ge) def __pos__(f): """Negate all coefficients in ``f``. """ return f.raw_new(f.numer, f.denom) def __neg__(f): """Negate all coefficients in ``f``. """ return f.raw_new(-f.numer, f.denom) def _extract_ground(self, element): domain = self.field.domain try: element = domain.convert(element) except CoercionFailed: if not domain.has_Field and domain.has_assoc_Field: ground_field = domain.get_field() try: element = ground_field.convert(element) except CoercionFailed: pass else: return -1, ground_field.numer(element), ground_field.denom(element) return 0, None, None else: return 1, element, None def __add__(f, g): """Add rational functions ``f`` and ``g``. """ field = f.field if not g: return f elif not f: return g elif isinstance(g, field.dtype): if f.denom == g.denom: return f.new(f.numer + g.numer, f.denom) else: return f.new(f.numer*g.denom + f.denom*g.numer, f.denom*g.denom) elif isinstance(g, field.ring.dtype): return f.new(f.numer + f.denom*g, f.denom) else: if isinstance(g, FracElement): if isinstance(field.domain, FractionField) and field.domain.field == g.field: pass elif isinstance(g.field.domain, FractionField) and g.field.domain.field == field: return g.__radd__(f) else: return NotImplemented elif isinstance(g, PolyElement): if isinstance(field.domain, PolynomialRing) and field.domain.ring == g.ring: pass else: return g.__radd__(f) return f.__radd__(g) def __radd__(f, c): if isinstance(c, f.field.ring.dtype): return f.new(f.numer + f.denom*c, f.denom) op, g_numer, g_denom = f._extract_ground(c) if op == 1: return f.new(f.numer + f.denom*g_numer, f.denom) elif not op: return NotImplemented else: return f.new(f.numer*g_denom + f.denom*g_numer, f.denom*g_denom) def __sub__(f, g): """Subtract rational functions ``f`` and ``g``. """ field = f.field if not g: return f elif not f: return -g elif isinstance(g, field.dtype): if f.denom == g.denom: return f.new(f.numer - g.numer, f.denom) else: return f.new(f.numer*g.denom - f.denom*g.numer, f.denom*g.denom) elif isinstance(g, field.ring.dtype): return f.new(f.numer - f.denom*g, f.denom) else: if isinstance(g, FracElement): if isinstance(field.domain, FractionField) and field.domain.field == g.field: pass elif isinstance(g.field.domain, FractionField) and g.field.domain.field == field: return g.__rsub__(f) else: return NotImplemented elif isinstance(g, PolyElement): if isinstance(field.domain, PolynomialRing) and field.domain.ring == g.ring: pass else: return g.__rsub__(f) op, g_numer, g_denom = f._extract_ground(g) if op == 1: return f.new(f.numer - f.denom*g_numer, f.denom) elif not op: return NotImplemented else: return f.new(f.numer*g_denom - f.denom*g_numer, f.denom*g_denom) def __rsub__(f, c): if isinstance(c, f.field.ring.dtype): return f.new(-f.numer + f.denom*c, f.denom) op, g_numer, g_denom = f._extract_ground(c) if op == 1: return f.new(-f.numer + f.denom*g_numer, f.denom) elif not op: return NotImplemented else: return f.new(-f.numer*g_denom + f.denom*g_numer, f.denom*g_denom) def __mul__(f, g): """Multiply rational functions ``f`` and ``g``. """ field = f.field if not f or not g: return field.zero elif isinstance(g, field.dtype): return f.new(f.numer*g.numer, f.denom*g.denom) elif isinstance(g, field.ring.dtype): return f.new(f.numer*g, f.denom) else: if isinstance(g, FracElement): if isinstance(field.domain, FractionField) and field.domain.field == g.field: pass elif isinstance(g.field.domain, FractionField) and g.field.domain.field == field: return g.__rmul__(f) else: return NotImplemented elif isinstance(g, PolyElement): if isinstance(field.domain, PolynomialRing) and field.domain.ring == g.ring: pass else: return g.__rmul__(f) return f.__rmul__(g) def __rmul__(f, c): if isinstance(c, f.field.ring.dtype): return f.new(f.numer*c, f.denom) op, g_numer, g_denom = f._extract_ground(c) if op == 1: return f.new(f.numer*g_numer, f.denom) elif not op: return NotImplemented else: return f.new(f.numer*g_numer, f.denom*g_denom) def __truediv__(f, g): """Computes quotient of fractions ``f`` and ``g``. """ field = f.field if not g: raise ZeroDivisionError elif isinstance(g, field.dtype): return f.new(f.numer*g.denom, f.denom*g.numer) elif isinstance(g, field.ring.dtype): return f.new(f.numer, f.denom*g) else: if isinstance(g, FracElement): if isinstance(field.domain, FractionField) and field.domain.field == g.field: pass elif isinstance(g.field.domain, FractionField) and g.field.domain.field == field: return g.__rtruediv__(f) else: return NotImplemented elif isinstance(g, PolyElement): if isinstance(field.domain, PolynomialRing) and field.domain.ring == g.ring: pass else: return g.__rtruediv__(f) op, g_numer, g_denom = f._extract_ground(g) if op == 1: return f.new(f.numer, f.denom*g_numer) elif not op: return NotImplemented else: return f.new(f.numer*g_denom, f.denom*g_numer) __div__ = __truediv__ def __rtruediv__(f, c): if not f: raise ZeroDivisionError elif isinstance(c, f.field.ring.dtype): return f.new(f.denom*c, f.numer) op, g_numer, g_denom = f._extract_ground(c) if op == 1: return f.new(f.denom*g_numer, f.numer) elif not op: return NotImplemented else: return f.new(f.denom*g_numer, f.numer*g_denom) __rdiv__ = __rtruediv__ def __pow__(f, n): """Raise ``f`` to a non-negative power ``n``. """ if n >= 0: return f.raw_new(f.numer**n, f.denom**n) elif not f: raise ZeroDivisionError else: return f.raw_new(f.denom**-n, f.numer**-n) def diff(f, x): """Computes partial derivative in ``x``. Examples -------- >>> from sympy.polys.fields import field >>> from sympy.polys.domains import ZZ >>> _, x, y, z = field("x,y,z", ZZ) >>> ((x**2 + y)/(z + 1)).diff(x) 2*x/(z + 1) """ x = x.to_poly() return f.new(f.numer.diff(x)*f.denom - f.numer*f.denom.diff(x), f.denom**2) def __call__(f, *values): if 0 < len(values) <= f.field.ngens: return f.evaluate(list(zip(f.field.gens, values))) else: raise ValueError("expected at least 1 and at most %s values, got %s" % (f.field.ngens, len(values))) def evaluate(f, x, a=None): if isinstance(x, list) and a is None: x = [ (X.to_poly(), a) for X, a in x ] numer, denom = f.numer.evaluate(x), f.denom.evaluate(x) else: x = x.to_poly() numer, denom = f.numer.evaluate(x, a), f.denom.evaluate(x, a) field = numer.ring.to_field() return field.new(numer, denom) def subs(f, x, a=None): if isinstance(x, list) and a is None: x = [ (X.to_poly(), a) for X, a in x ] numer, denom = f.numer.subs(x), f.denom.subs(x) else: x = x.to_poly() numer, denom = f.numer.subs(x, a), f.denom.subs(x, a) return f.new(numer, denom) def compose(f, x, a=None): raise NotImplementedError sympy-0.7.4.1/sympy/polys/partfrac.py0000644000175000017500000003276112253362407017760 0ustar georgeskgeorgesk"""Algorithms for partial fraction decomposition of rational functions. """ from __future__ import print_function, division 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.polys.polyerrors import PolynomialError from sympy.core import S, Add, sympify, Function, Lambda, Dummy, Expr from sympy.core.basic import preorder_traversal from sympy.utilities import numbered_symbols, take, xthreaded, public from sympy.core.compatibility import xrange @xthreaded @public 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 By default, using the undetermined coefficients method: >>> apart(y/(x + 2)/(x + 1), x) -y/(x + 2) + y/(x + 1) You can choose Bronstein's algorithm by setting ``full=True``: >>> apart(y/(x**2 + x + 1), x) y/(x**2 + x + 1) >>> apart(y/(x**2 + x + 1), x, full=True) RootSum(_w**2 + _w + 1, Lambda(_a, (-2*_a*y/3 - y/3)/(-_a + x))) See Also ======== apart_list, assemble_partfrac_list """ allowed_flags(options, []) f = sympify(f) if f.is_Atom: return f else: P, Q = f.as_numer_denom() _options = options.copy() options = set_defaults(options, extension=True) try: (P, Q), opt = parallel_poly_from_expr((P, Q), x, **options) except PolynomialError as msg: if f.is_commutative: raise PolynomialError(msg) # non-commutative if f.is_Mul: c, nc = f.args_cnc(split_1=False) nc = f.func(*[apart(i, x=x, full=full, **_options) for i in nc]) if c: c = apart(f.func._from_args(c), x=x, full=full, **_options) return c*nc else: return nc elif f.is_Add: c = [] nc = [] for i in f.args: if i.is_commutative: c.append(i) else: try: nc.append(apart(i, x=x, full=full, **_options)) except NotImplementedError: nc.append(i) return apart(f.func(*c), x=x, full=full, **_options) + f.func(*nc) else: reps = [] pot = preorder_traversal(f) next(pot) for e in pot: try: reps.append((e, apart(e, x=x, full=full, **_options))) pot.skip() # this was handled successfully except NotImplementedError: pass return f.xreplace(dict(reps)) if P.is_multivariate: fc = f.cancel() if fc != f: return apart(fc, x=x, full=full, **_options) 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 xrange(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]_ """ return assemble_partfrac_list(apart_list(P/Q, P.gens[0])) @public def apart_list(f, x=None, dummies=None, **options): """ Compute partial fraction decomposition of a rational function and return the result in structured form. Given a rational function ``f`` compute the partial fraction decomposition of ``f``. Only Bronstein's full partial fraction decomposition algorithm is supported by this method. The return value is highly structured and perfectly suited for further algorithmic treatment rather than being human-readable. The function returns a tuple holding three elements: * The first item is the common coefficient, free of the variable `x` used for decomposition. (It is an element of the base field `K`.) * The second item is the polynomial part of the decomposition. This can be the zero polynomial. (It is an element of `K[x]`.) * The third part itself is a list of quadruples. Each quadruple has the following elements in this order: - The (not necessarily irreducible) polynomial `D` whose roots `w_i` appear in the linear denominator of a bunch of related fraction terms. (This item can also be a list of explicit roots. However, at the moment ``apart_list`` never returns a result this way, but the related ``assemble_partfrac_list`` function accepts this format as input.) - The numerator of the fraction, written as a function of the root `w` - The linear denominator of the fraction *excluding its power exponent*, written as a function of the root `w`. - The power to which the denominator has to be raised. On can always rebuild a plain expression by using the function ``assemble_partfrac_list``. Examples ======== A first example: >>> from sympy.polys.partfrac import apart_list, assemble_partfrac_list >>> from sympy.abc import x, t >>> f = (2*x**3 - 2*x) / (x**2 - 2*x + 1) >>> pfd = apart_list(f) >>> pfd (1, Poly(2*x + 4, x, domain='ZZ'), [(Poly(_w - 1, _w, domain='ZZ'), Lambda(_a, 4), Lambda(_a, -_a + x), 1)]) >>> assemble_partfrac_list(pfd) 2*x + 4 + 4/(x - 1) Second example: >>> f = (-2*x - 2*x**2) / (3*x**2 - 6*x) >>> pfd = apart_list(f) >>> pfd (-1, Poly(2/3, x, domain='QQ'), [(Poly(_w - 2, _w, domain='ZZ'), Lambda(_a, 2), Lambda(_a, -_a + x), 1)]) >>> assemble_partfrac_list(pfd) -2/3 - 2/(x - 2) Another example, showing symbolic parameters: >>> pfd = apart_list(t/(x**2 + x + t), x) >>> pfd (1, Poly(0, x, domain='ZZ[t]'), [(Poly(_w**2 + _w + t, _w, domain='ZZ[t]'), Lambda(_a, -2*_a*t/(4*t - 1) - t/(4*t - 1)), Lambda(_a, -_a + x), 1)]) >>> assemble_partfrac_list(pfd) RootSum(_w**2 + _w + t, Lambda(_a, (-2*_a*t/(4*t - 1) - t/(4*t - 1))/(-_a + x))) This example is taken from Bronstein's original paper: >>> f = 36 / (x**5 - 2*x**4 - 2*x**3 + 4*x**2 + x - 2) >>> pfd = apart_list(f) >>> pfd (1, Poly(0, x, domain='ZZ'), [(Poly(_w - 2, _w, domain='ZZ'), Lambda(_a, 4), Lambda(_a, -_a + x), 1), (Poly(_w**2 - 1, _w, domain='ZZ'), Lambda(_a, -3*_a - 6), Lambda(_a, -_a + x), 2), (Poly(_w + 1, _w, domain='ZZ'), Lambda(_a, -4), Lambda(_a, -_a + x), 1)]) >>> assemble_partfrac_list(pfd) -4/(x + 1) - 3/(x + 1)**2 - 9/(x - 1)**2 + 4/(x - 2) See also ======== apart, assemble_partfrac_list References ========== 1. [Bronstein93]_ """ 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) polypart = poly if dummies is None: def dummies(name): d = Dummy(name) while True: yield d dummies = dummies("w") rationalpart = apart_list_full_decomposition(P, Q, dummies) return (common, polypart, rationalpart) def apart_list_full_decomposition(P, Q, dummygen): """ 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 = [] 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) Dw = D.subs(x, next(dummygen)) numer = Lambda(a, b.as_expr().subs(x, a)) denom = Lambda(a, (x - a)) exponent = n-j partial.append((Dw, numer, denom, exponent)) return partial @public def assemble_partfrac_list(partial_list): r"""Reassemble a full partial fraction decomposition from a structured result obtained by the function ``apart_list``. Examples ======== This example is taken from Bronstein's original paper: >>> from sympy.polys.partfrac import apart_list, assemble_partfrac_list >>> from sympy.abc import x, y >>> f = 36 / (x**5 - 2*x**4 - 2*x**3 + 4*x**2 + x - 2) >>> pfd = apart_list(f) >>> pfd (1, Poly(0, x, domain='ZZ'), [(Poly(_w - 2, _w, domain='ZZ'), Lambda(_a, 4), Lambda(_a, -_a + x), 1), (Poly(_w**2 - 1, _w, domain='ZZ'), Lambda(_a, -3*_a - 6), Lambda(_a, -_a + x), 2), (Poly(_w + 1, _w, domain='ZZ'), Lambda(_a, -4), Lambda(_a, -_a + x), 1)]) >>> assemble_partfrac_list(pfd) -4/(x + 1) - 3/(x + 1)**2 - 9/(x - 1)**2 + 4/(x - 2) If we happen to know some roots we can provide them easily inside the structure: >>> pfd = apart_list(2/(x**2-2)) >>> pfd (1, Poly(0, x, domain='ZZ'), [(Poly(_w**2 - 2, _w, domain='ZZ'), Lambda(_a, _a/2), Lambda(_a, -_a + x), 1)]) >>> pfda = assemble_partfrac_list(pfd) >>> pfda RootSum(_w**2 - 2, Lambda(_a, _a/(-_a + x)))/2 >>> pfda.doit() -sqrt(2)/(2*(x + sqrt(2))) + sqrt(2)/(2*(x - sqrt(2))) >>> from sympy import Dummy, Poly, Lambda, sqrt >>> a = Dummy("a") >>> pfd = (1, Poly(0, x, domain='ZZ'), [([sqrt(2),-sqrt(2)], Lambda(a, a/2), Lambda(a, -a + x), 1)]) >>> assemble_partfrac_list(pfd) -sqrt(2)/(2*(x + sqrt(2))) + sqrt(2)/(2*(x - sqrt(2))) See also ======== apart, apart_list """ # Common factor common = partial_list[0] # Polynomial part polypart = partial_list[1] pfd = polypart.as_expr() # Rational parts for r, nf, df, ex in partial_list[2]: if isinstance(r, Poly): # Assemble in case the roots are given implicitly by a polynomials an, nu = nf.variables, nf.expr ad, de = df.variables, df.expr # Hack to make dummies equal because Lambda created new Dummies de = de.subs(ad[0], an[0]) func = Lambda(an, nu/de**ex) pfd += RootSum(r, func, auto=False, quadratic=False) else: # Assemble in case the roots are given explicitely by a list of algebraic numbers for root in r: pfd += nf(root)/df(root)**ex return common*pfd sympy-0.7.4.1/sympy/polys/tests/0000755000175000017500000000000012253362407016735 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/polys/tests/test_polyclasses.py0000644000175000017500000003063312253362407022714 0ustar georgeskgeorgesk"""Tests for OO layer of several polynomial representations. """ from sympy.polys.polyclasses import ( DMP, init_normal_DMP, DMF, init_normal_DMF, ANP, init_normal_ANP, ) from sympy.polys.domains import ZZ, QQ from sympy.polys.specialpolys import f_polys from sympy.polys.polyerrors import ExactQuotientFailed from sympy.core.compatibility import long from sympy.utilities.pytest import raises f_0, f_1, f_2, f_3, f_4, f_5, f_6 = [ f.to_dense() for f in f_polys() ] def test_DMP___init__(): f = DMP([[0], [], [0, 1, 2], [3]], ZZ) assert f.rep == [[1, 2], [3]] assert f.dom == ZZ assert f.lev == 1 f = DMP([[1, 2], [3]], ZZ, 1) assert f.rep == [[1, 2], [3]] assert f.dom == ZZ assert f.lev == 1 f = DMP({(1, 1): 1, (0, 0): 2}, ZZ, 1) assert f.rep == [[1, 0], [2]] assert f.dom == ZZ assert f.lev == 1 def test_DMP___eq__(): assert DMP([[ZZ(1), ZZ(2)], [ZZ(3)]], ZZ) == \ DMP([[ZZ(1), ZZ(2)], [ZZ(3)]], ZZ) assert DMP([[ZZ(1), ZZ(2)], [ZZ(3)]], ZZ) == \ DMP([[QQ(1), QQ(2)], [QQ(3)]], QQ) assert DMP([[QQ(1), QQ(2)], [QQ(3)]], QQ) == \ DMP([[ZZ(1), ZZ(2)], [ZZ(3)]], ZZ) assert DMP([[[ZZ(1)]]], ZZ) != DMP([[ZZ(1)]], ZZ) assert DMP([[ZZ(1)]], ZZ) != DMP([[[ZZ(1)]]], ZZ) def test_DMP___bool__(): assert bool(DMP([[]], ZZ)) is False assert bool(DMP([[1]], ZZ)) is True def test_DMP_to_dict(): f = DMP([[3], [], [2], [], [8]], ZZ) assert f.to_dict() == \ {(4, 0): 3, (2, 0): 2, (0, 0): 8} assert f.to_sympy_dict() == \ {(4, 0): ZZ.to_sympy(3), (2, 0): ZZ.to_sympy(2), (0, 0): ZZ.to_sympy(8)} def test_DMP_properties(): assert DMP([[]], ZZ).is_zero is True assert DMP([[1]], ZZ).is_zero is False assert DMP([[1]], ZZ).is_one is True assert DMP([[2]], ZZ).is_one is False assert DMP([[1]], ZZ).is_ground is True assert DMP([[1], [2], [1]], ZZ).is_ground is False assert DMP([[1], [2, 0], [1, 0]], ZZ).is_sqf is True assert DMP([[1], [2, 0], [1, 0, 0]], ZZ).is_sqf is False assert DMP([[1, 2], [3]], ZZ).is_monic is True assert DMP([[2, 2], [3]], ZZ).is_monic is False assert DMP([[1, 2], [3]], ZZ).is_primitive is True assert DMP([[2, 4], [6]], ZZ).is_primitive is False def test_DMP_arithmetics(): f = DMP([[2], [2, 0]], ZZ) assert f.mul_ground(2) == DMP([[4], [4, 0]], ZZ) assert f.quo_ground(2) == DMP([[1], [1, 0]], ZZ) raises(ExactQuotientFailed, lambda: f.exquo_ground(3)) f = DMP([[-5]], ZZ) g = DMP([[5]], ZZ) assert f.abs() == g assert abs(f) == g assert g.neg() == f assert -g == f h = DMP([[]], ZZ) assert f.add(g) == h assert f + g == h assert g + f == h assert f + 5 == h assert 5 + f == h h = DMP([[-10]], ZZ) assert f.sub(g) == h assert f - g == h assert g - f == -h assert f - 5 == h assert 5 - f == -h h = DMP([[-25]], ZZ) assert f.mul(g) == h assert f * g == h assert g * f == h assert f * 5 == h assert 5 * f == h h = DMP([[25]], ZZ) assert f.sqr() == h assert f.pow(2) == h assert f**2 == h raises(TypeError, lambda: f.pow('x')) f = DMP([[1], [], [1, 0, 0]], ZZ) g = DMP([[2], [-2, 0]], ZZ) q = DMP([[2], [2, 0]], ZZ) r = DMP([[8, 0, 0]], ZZ) assert f.pdiv(g) == (q, r) assert f.pquo(g) == q assert f.prem(g) == r raises(ExactQuotientFailed, lambda: f.pexquo(g)) f = DMP([[1], [], [1, 0, 0]], ZZ) g = DMP([[1], [-1, 0]], ZZ) q = DMP([[1], [1, 0]], ZZ) r = DMP([[2, 0, 0]], ZZ) assert f.div(g) == (q, r) assert f.quo(g) == q assert f.rem(g) == r assert divmod(f, g) == (q, r) assert f // g == q assert f % g == r raises(ExactQuotientFailed, lambda: f.exquo(g)) def test_DMP_functionality(): f = DMP([[1], [2, 0], [1, 0, 0]], ZZ) g = DMP([[1], [1, 0]], ZZ) h = DMP([[1]], ZZ) assert f.degree() == 2 assert f.degree_list() == (2, 2) assert f.total_degree() == 2 assert f.LC() == ZZ(1) assert f.TC() == ZZ(0) assert f.nth(1, 1) == ZZ(2) raises(TypeError, lambda: f.nth(0, 'x')) assert f.max_norm() == 2 assert f.l1_norm() == 4 u = DMP([[2], [2, 0]], ZZ) assert f.diff(m=1, j=0) == u assert f.diff(m=1, j=1) == u raises(TypeError, lambda: f.diff(m='x', j=0)) u = DMP([1, 2, 1], ZZ) v = DMP([1, 2, 1], ZZ) assert f.eval(a=1, j=0) == u assert f.eval(a=1, j=1) == v assert f.eval(1).eval(1) == ZZ(4) assert f.cofactors(g) == (g, g, h) assert f.gcd(g) == g assert f.lcm(g) == f u = DMP([[QQ(45), QQ(30), QQ(5)]], QQ) v = DMP([[QQ(1), QQ(2, 3), QQ(1, 9)]], QQ) assert u.monic() == v assert (4*f).content() == ZZ(4) assert (4*f).primitive() == (ZZ(4), f) f = DMP([[1], [2], [3], [4], [5], [6]], ZZ) assert f.trunc(3) == DMP([[1], [-1], [], [1], [-1], []], ZZ) f = DMP(f_4, ZZ) assert f.sqf_part() == -f assert f.sqf_list() == (ZZ(-1), [(-f, 1)]) f = DMP([[-1], [], [], [5]], ZZ) g = DMP([[3, 1], [], []], ZZ) h = DMP([[45, 30, 5]], ZZ) r = DMP([675, 675, 225, 25], ZZ) assert f.subresultants(g) == [f, g, h] assert f.resultant(g) == r f = DMP([1, 3, 9, -13], ZZ) assert f.discriminant() == -11664 f = DMP([QQ(2), QQ(0)], QQ) g = DMP([QQ(1), QQ(0), QQ(-16)], QQ) s = DMP([QQ(1, 32), QQ(0)], QQ) t = DMP([QQ(-1, 16)], QQ) h = DMP([QQ(1)], QQ) assert f.half_gcdex(g) == (s, h) assert f.gcdex(g) == (s, t, h) assert f.invert(g) == s f = DMP([[1], [2], [3]], QQ) raises(ValueError, lambda: f.half_gcdex(f)) raises(ValueError, lambda: f.gcdex(f)) raises(ValueError, lambda: f.invert(f)) f = DMP([1, 0, 20, 0, 150, 0, 500, 0, 625, -2, 0, -10, 9], ZZ) g = DMP([1, 0, 0, -2, 9], ZZ) h = DMP([1, 0, 5, 0], ZZ) assert g.compose(h) == f assert f.decompose() == [g, h] f = DMP([[1], [2], [3]], QQ) raises(ValueError, lambda: f.decompose()) raises(ValueError, lambda: f.sturm()) def test_DMP_exclude(): f = [[[[[[[[[[[[[[[[[[[[[[[[[[1]], [[]]]]]]]]]]]]]]]]]]]]]]]]]] J = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 25] assert DMP(f, ZZ).exclude() == (J, DMP([1, 0], ZZ)) assert DMP([[1], [1, 0]], ZZ).exclude() == ([], DMP([[1], [1, 0]], ZZ)) def test_DMF__init__(): f = DMF(([[0], [], [0, 1, 2], [3]], [[1, 2, 3]]), ZZ) assert f.num == [[1, 2], [3]] assert f.den == [[1, 2, 3]] assert f.lev == 1 assert f.dom == ZZ f = DMF(([[1, 2], [3]], [[1, 2, 3]]), ZZ, 1) assert f.num == [[1, 2], [3]] assert f.den == [[1, 2, 3]] assert f.lev == 1 assert f.dom == ZZ f = DMF(([[-1], [-2]], [[3], [-4]]), ZZ) assert f.num == [[-1], [-2]] assert f.den == [[3], [-4]] assert f.lev == 1 assert f.dom == ZZ f = DMF(([[1], [2]], [[-3], [4]]), ZZ) assert f.num == [[-1], [-2]] assert f.den == [[3], [-4]] assert f.lev == 1 assert f.dom == ZZ f = DMF(([[1], [2]], [[-3], [4]]), ZZ) assert f.num == [[-1], [-2]] assert f.den == [[3], [-4]] assert f.lev == 1 assert f.dom == ZZ f = DMF(([[]], [[-3], [4]]), ZZ) assert f.num == [[]] assert f.den == [[1]] assert f.lev == 1 assert f.dom == ZZ f = DMF(17, ZZ, 1) assert f.num == [[17]] assert f.den == [[1]] assert f.lev == 1 assert f.dom == ZZ f = DMF(([[1], [2]]), ZZ) assert f.num == [[1], [2]] assert f.den == [[1]] assert f.lev == 1 assert f.dom == ZZ f = DMF([[0], [], [0, 1, 2], [3]], ZZ) assert f.num == [[1, 2], [3]] assert f.den == [[1]] assert f.lev == 1 assert f.dom == ZZ f = DMF({(1, 1): 1, (0, 0): 2}, ZZ, 1) assert f.num == [[1, 0], [2]] assert f.den == [[1]] assert f.lev == 1 assert f.dom == ZZ f = DMF(([[QQ(1)], [QQ(2)]], [[-QQ(3)], [QQ(4)]]), QQ) assert f.num == [[-QQ(1)], [-QQ(2)]] assert f.den == [[QQ(3)], [-QQ(4)]] assert f.lev == 1 assert f.dom == QQ f = DMF(([[QQ(1, 5)], [QQ(2, 5)]], [[-QQ(3, 7)], [QQ(4, 7)]]), QQ) assert f.num == [[-QQ(7)], [-QQ(14)]] assert f.den == [[QQ(15)], [-QQ(20)]] assert f.lev == 1 assert f.dom == QQ raises(ValueError, lambda: DMF(([1], [[1]]), ZZ)) raises(ZeroDivisionError, lambda: DMF(([1], []), ZZ)) def test_DMF__bool__(): assert bool(DMF([[]], ZZ)) is False assert bool(DMF([[1]], ZZ)) is True def test_DMF_properties(): assert DMF([[]], ZZ).is_zero is True assert DMF([[]], ZZ).is_one is False assert DMF([[1]], ZZ).is_zero is False assert DMF([[1]], ZZ).is_one is True assert DMF(([[1]], [[2]]), ZZ).is_one is False def test_DMF_arithmetics(): f = DMF([[7], [-9]], ZZ) g = DMF([[-7], [9]], ZZ) assert f.neg() == -f == g f = DMF(([[1]], [[1], []]), ZZ) g = DMF(([[1]], [[1, 0]]), ZZ) h = DMF(([[1], [1, 0]], [[1, 0], []]), ZZ) assert f.add(g) == f + g == h assert g.add(f) == g + f == h h = DMF(([[-1], [1, 0]], [[1, 0], []]), ZZ) assert f.sub(g) == f - g == h h = DMF(([[1]], [[1, 0], []]), ZZ) assert f.mul(g) == f*g == h assert g.mul(f) == g*f == h h = DMF(([[1, 0]], [[1], []]), ZZ) assert f.quo(g) == f/g == h h = DMF(([[1]], [[1], [], [], []]), ZZ) assert f.pow(3) == f**3 == h h = DMF(([[1]], [[1, 0, 0, 0]]), ZZ) assert g.pow(3) == g**3 == h def test_ANP___init__(): rep = [QQ(1), QQ(1)] mod = [QQ(1), QQ(0), QQ(1)] f = ANP(rep, mod, QQ) assert f.rep == [QQ(1), QQ(1)] assert f.mod == [QQ(1), QQ(0), QQ(1)] assert f.dom == QQ rep = {1: QQ(1), 0: QQ(1)} mod = {2: QQ(1), 0: QQ(1)} f = ANP(rep, mod, QQ) assert f.rep == [QQ(1), QQ(1)] assert f.mod == [QQ(1), QQ(0), QQ(1)] assert f.dom == QQ f = ANP(1, mod, QQ) assert f.rep == [QQ(1)] assert f.mod == [QQ(1), QQ(0), QQ(1)] assert f.dom == QQ def test_ANP___eq__(): a = ANP([QQ(1), QQ(1)], [QQ(1), QQ(0), QQ(1)], QQ) b = ANP([QQ(1), QQ(1)], [QQ(1), QQ(0), QQ(2)], QQ) assert (a == a) is True assert (a != a) is False assert (a == b) is False assert (a != b) is True b = ANP([QQ(1), QQ(2)], [QQ(1), QQ(0), QQ(1)], QQ) assert (a == b) is False assert (a != b) is True def test_ANP___bool__(): assert bool(ANP([], [QQ(1), QQ(0), QQ(1)], QQ)) is False assert bool(ANP([QQ(1)], [QQ(1), QQ(0), QQ(1)], QQ)) is True def test_ANP_properties(): mod = [QQ(1), QQ(0), QQ(1)] assert ANP([QQ(0)], mod, QQ).is_zero is True assert ANP([QQ(1)], mod, QQ).is_zero is False assert ANP([QQ(1)], mod, QQ).is_one is True assert ANP([QQ(2)], mod, QQ).is_one is False def test_ANP_arithmetics(): mod = [QQ(1), QQ(0), QQ(0), QQ(-2)] a = ANP([QQ(2), QQ(-1), QQ(1)], mod, QQ) b = ANP([QQ(1), QQ(2)], mod, QQ) c = ANP([QQ(-2), QQ(1), QQ(-1)], mod, QQ) assert a.neg() == -a == c c = ANP([QQ(2), QQ(0), QQ(3)], mod, QQ) assert a.add(b) == a + b == c assert b.add(a) == b + a == c c = ANP([QQ(2), QQ(-2), QQ(-1)], mod, QQ) assert a.sub(b) == a - b == c c = ANP([QQ(-2), QQ(2), QQ(1)], mod, QQ) assert b.sub(a) == b - a == c c = ANP([QQ(3), QQ(-1), QQ(6)], mod, QQ) assert a.mul(b) == a*b == c assert b.mul(a) == b*a == c c = ANP([QQ(-1, 43), QQ(9, 43), QQ(5, 43)], mod, QQ) assert a.pow(0) == a**(0) == ANP(1, mod, QQ) assert a.pow(1) == a**(1) == a assert a.pow(-1) == a**(-1) == c assert a.quo(a) == a.mul(a.pow(-1)) == a*a**(-1) == ANP(1, mod, QQ) def test_ANP_unify(): mod = [QQ(1), QQ(0), QQ(-2)] a = ANP([QQ(1)], mod, QQ) b = ANP([ZZ(1)], mod, ZZ) assert a.unify(b)[0] == QQ assert b.unify(a)[0] == QQ assert a.unify(a)[0] == QQ assert b.unify(b)[0] == ZZ def test___hash__(): # Issue 2472 # Make sure int vs. long doesn't affect hashing with Python ground types assert DMP([[1, 2], [3]], ZZ) == DMP([[long(1), long(2)], [long(3)]], ZZ) assert hash(DMP([[1, 2], [3]], ZZ)) == hash(DMP([[long(1), long(2)], [long(3)]], ZZ)) assert DMF( ([[1, 2], [3]], [[1]]), ZZ) == DMF(([[long(1), long(2)], [long(3)]], [[long(1)]]), ZZ) assert hash(DMF(([[1, 2], [3]], [[1]]), ZZ)) == hash(DMF(([[long(1), long(2)], [long(3)]], [[long(1)]]), ZZ)) assert ANP([1, 1], [1, 0, 1], ZZ) == ANP([long(1), long(1)], [long(1), long(0), long(1)], ZZ) assert hash( ANP([1, 1], [1, 0, 1], ZZ)) == hash(ANP([long(1), long(1)], [long(1), long(0), long(1)], ZZ)) sympy-0.7.4.1/sympy/polys/tests/test_solvers.py0000644000175000017500000003206712253362407022053 0ustar georgeskgeorgesk"""Tests for low-level linear systems solver. """ from sympy.polys.rings import ring from sympy.polys.fields import field from sympy.polys.domains import ZZ, QQ from sympy.polys.solvers import solve_lin_sys from sympy.utilities.pytest import slow def test_solve_lin_sys_2x2_one(): domain, x1,x2 = ring("x1,x2", QQ) eqs = [x1 + x2 - 5, 2*x1 - x2] sol = {x1: QQ(5, 3), x2: QQ(10, 3)} _sol = solve_lin_sys(eqs, domain) assert _sol == sol and all(isinstance(s, domain.dtype) for s in _sol) def test_solve_lin_sys_2x4_none(): domain, x1,x2 = ring("x1,x2", QQ) eqs = [x1 - 1, x1 - x2, x1 - 2*x2, x2 - 1] assert solve_lin_sys(eqs, domain) == None def test_solve_lin_sys_3x4_one(): domain, x1,x2,x3 = ring("x1,x2,x3", QQ) eqs = [x1 + 2*x2 + 3*x3, 2*x1 - x2 + x3, 3*x1 + x2 + x3, 5*x2 + 2*x3] sol = {x1: 0, x2: 0, x3: 0} assert solve_lin_sys(eqs, domain) == sol def test_solve_lin_sys_3x3_inf(): domain, x1,x2,x3 = ring("x1,x2,x3", QQ) eqs = [x1 - x2 + 2*x3 - 1, 2*x1 + x2 + x3 - 8, x1 + x2 - 5] sol = {x1: -x3 + 3, x2: x3 + 2} assert solve_lin_sys(eqs, domain) == sol def test_solve_lin_sys_3x4_none(): domain, x1,x2,x3,x4 = ring("x1,x2,x3,x4", QQ) eqs = [2*x1 + x2 + 7*x3 - 7*x4 - 2, -3*x1 + 4*x2 - 5*x3 - 6*x4 - 3, x1 + x2 + 4*x3 - 5*x4 - 2] assert solve_lin_sys(eqs, domain) == None def test_solve_lin_sys_4x7_inf(): domain, x1,x2,x3,x4,x5,x6,x7 = ring("x1,x2,x3,x4,x5,x6,x7", QQ) eqs = [x1 + 4*x2 - x4 + 7*x6 - 9*x7 - 3, 2*x1 + 8*x2 - x3 + 3*x4 + 9*x5 - 13*x6 + 7*x7 - 9, 2*x3 - 3*x4 - 4*x5 + 12*x6 - 8*x7 - 1, -x1 - 4*x2 + 2*x3 + 4*x4 + 8*x5 - 31*x6 + 37*x7 - 4] sol = {x1: 4 - 4*x2 - 2*x5 - x6 + 3*x7, x3: 2 - x5 + 3*x6 - 5*x7, x4: 1 - 2*x5 + 6*x6 - 6*x7} assert solve_lin_sys(eqs, domain) == sol def test_solve_lin_sys_5x5_inf(): domain, x1,x2,x3,x4,x5 = ring("x1,x2,x3,x4,x5", QQ) eqs = [x1 - x2 - 2*x3 + x4 + 11*x5 - 13, x1 - x2 + x3 + x4 + 5*x5 - 16, 2*x1 - 2*x2 + x4 + 10*x5 - 21, 2*x1 - 2*x2 - x3 + 3*x4 + 20*x5 - 38, 2*x1 - 2*x2 + x3 + x4 + 8*x5 - 22] sol = {x1: 6 + x2 - 3*x5, x3: 1 + 2*x5, x4: 9 - 4*x5} assert solve_lin_sys(eqs, domain) == sol def test_solve_lin_sys_6x6_1(): ground, d,r,e,g,i,j,l,o,m,p,q = field("d,r,e,g,i,j,l,o,m,p,q", ZZ) domain, c,f,h,k,n,b = ring("c,f,h,k,n,b", ground) eqs = [b + q/d - c/d, c*(1/d + 1/e + 1/g) - f/g - q/d, f*(1/g + 1/i + 1/j) - c/g - h/i, h*(1/i + 1/l + 1/m) - f/i - k/m, k*(1/m + 1/o + 1/p) - h/m - n/p, n/p - k/p] sol = { b: (e*i*l*q + e*i*m*q + e*i*o*q + e*j*l*q + e*j*m*q + e*j*o*q + e*l*m*q + e*l*o*q + g*i*l*q + g*i*m*q + g*i*o*q + g*j*l*q + g*j*m*q + g*j*o*q + g*l*m*q + g*l*o*q + i*j*l*q + i*j*m*q + i*j*o*q + j*l*m*q + j*l*o*q)/(-d*e*i*l - d*e*i*m - d*e*i*o - d*e*j*l - d*e*j*m - d*e*j*o - d*e*l*m - d*e*l*o - d*g*i*l - d*g*i*m - d*g*i*o - d*g*j*l - d*g*j*m - d*g*j*o - d*g*l*m - d*g*l*o - d*i*j*l - d*i*j*m - d*i*j*o - d*j*l*m - d*j*l*o - e*g*i*l - e*g*i*m - e*g*i*o - e*g*j*l - e*g*j*m - e*g*j*o - e*g*l*m - e*g*l*o - e*i*j*l - e*i*j*m - e*i*j*o - e*j*l*m - e*j*l*o), c: (-e*g*i*l*q - e*g*i*m*q - e*g*i*o*q - e*g*j*l*q - e*g*j*m*q - e*g*j*o*q - e*g*l*m*q - e*g*l*o*q - e*i*j*l*q - e*i*j*m*q - e*i*j*o*q - e*j*l*m*q - e*j*l*o*q)/(-d*e*i*l - d*e*i*m - d*e*i*o - d*e*j*l - d*e*j*m - d*e*j*o - d*e*l*m - d*e*l*o - d*g*i*l - d*g*i*m - d*g*i*o - d*g*j*l - d*g*j*m - d*g*j*o - d*g*l*m - d*g*l*o - d*i*j*l - d*i*j*m - d*i*j*o - d*j*l*m - d*j*l*o - e*g*i*l - e*g*i*m - e*g*i*o - e*g*j*l - e*g*j*m - e*g*j*o - e*g*l*m - e*g*l*o - e*i*j*l - e*i*j*m - e*i*j*o - e*j*l*m - e*j*l*o), f: (-e*i*j*l*q - e*i*j*m*q - e*i*j*o*q - e*j*l*m*q - e*j*l*o*q)/(-d*e*i*l - d*e*i*m - d*e*i*o - d*e*j*l - d*e*j*m - d*e*j*o - d*e*l*m - d*e*l*o - d*g*i*l - d*g*i*m - d*g*i*o - d*g*j*l - d*g*j*m - d*g*j*o - d*g*l*m - d*g*l*o - d*i*j*l - d*i*j*m - d*i*j*o - d*j*l*m - d*j*l*o - e*g*i*l - e*g*i*m - e*g*i*o - e*g*j*l - e*g*j*m - e*g*j*o - e*g*l*m - e*g*l*o - e*i*j*l - e*i*j*m - e*i*j*o - e*j*l*m - e*j*l*o), h: (-e*j*l*m*q - e*j*l*o*q)/(-d*e*i*l - d*e*i*m - d*e*i*o - d*e*j*l - d*e*j*m - d*e*j*o - d*e*l*m - d*e*l*o - d*g*i*l - d*g*i*m - d*g*i*o - d*g*j*l - d*g*j*m - d*g*j*o - d*g*l*m - d*g*l*o - d*i*j*l - d*i*j*m - d*i*j*o - d*j*l*m - d*j*l*o - e*g*i*l - e*g*i*m - e*g*i*o - e*g*j*l - e*g*j*m - e*g*j*o - e*g*l*m - e*g*l*o - e*i*j*l - e*i*j*m - e*i*j*o - e*j*l*m - e*j*l*o), k: e*j*l*o*q/(d*e*i*l + d*e*i*m + d*e*i*o + d*e*j*l + d*e*j*m + d*e*j*o + d*e*l*m + d*e*l*o + d*g*i*l + d*g*i*m + d*g*i*o + d*g*j*l + d*g*j*m + d*g*j*o + d*g*l*m + d*g*l*o + d*i*j*l + d*i*j*m + d*i*j*o + d*j*l*m + d*j*l*o + e*g*i*l + e*g*i*m + e*g*i*o + e*g*j*l + e*g*j*m + e*g*j*o + e*g*l*m + e*g*l*o + e*i*j*l + e*i*j*m + e*i*j*o + e*j*l*m + e*j*l*o), n: e*j*l*o*q/(d*e*i*l + d*e*i*m + d*e*i*o + d*e*j*l + d*e*j*m + d*e*j*o + d*e*l*m + d*e*l*o + d*g*i*l + d*g*i*m + d*g*i*o + d*g*j*l + d*g*j*m + d*g*j*o + d*g*l*m + d*g*l*o + d*i*j*l + d*i*j*m + d*i*j*o + d*j*l*m + d*j*l*o + e*g*i*l + e*g*i*m + e*g*i*o + e*g*j*l + e*g*j*m + e*g*j*o + e*g*l*m + e*g*l*o + e*i*j*l + e*i*j*m + e*i*j*o + e*j*l*m + e*j*l*o), } assert solve_lin_sys(eqs, domain) == sol def test_solve_lin_sys_6x6_2(): ground, d,r,e,g,i,j,l,o,m,p,q = field("d,r,e,g,i,j,l,o,m,p,q", ZZ) domain, c,f,h,k,n,b = ring("c,f,h,k,n,b", ground) eqs = [b + r/d - c/d, c*(1/d + 1/e + 1/g) - f/g - r/d, f*(1/g + 1/i + 1/j) - c/g - h/i, h*(1/i + 1/l + 1/m) - f/i - k/m, k*(1/m + 1/o + 1/p) - h/m - n/p, n*(1/p + 1/q) - k/p] sol = { b: -((l*q*e*o + l*q*g*o + i*m*q*e + i*l*q*e + i*l*p*e + i*j*o*q + j*e*o*q + g*j*o*q + i*e*o*q + g*i*o*q + e*l*o*p + e*l*m*p + e*l*m*o + e*i*o*p + e*i*m*p + e*i*m*o + e*i*l*o + j*e*o*p + j*e*m*q + j*e*m*p + j*e*m*o + j*l*m*q + j*l*m*p + j*l*m*o + i*j*m*p + i*j*m*o + i*j*l*q + i*j*l*o + i*j*m*q + j*l*o*p + j*e*l*o + g*j*o*p + g*j*m*q + g*j*m*p + i*j*l*p + i*j*o*p + j*e*l*q + j*e*l*p + j*l*o*q + g*j*m*o + g*j*l*q + g*j*l*p + g*j*l*o + g*l*o*p + g*l*m*p + g*l*m*o + g*i*m*o + g*i*o*p + g*i*m*q + g*i*m*p + g*i*l*q + g*i*l*p + g*i*l*o + l*m*q*e + l*m*q*g)*r)/(l*q*d*e*o + l*q*d*g*o + l*q*e*g*o + i*j*d*o*q + i*j*e*o*q + j*d*e*o*q + g*j*d*o*q + g*j*e*o*q + g*i*e*o*q + i*d*e*o*q + g*i*d*o*q + g*i*d*o*p + g*i*d*m*q + g*i*d*m*p + g*i*d*m*o + g*i*d*l*q + g*i*d*l*p + g*i*d*l*o + g*e*l*m*p + g*e*l*o*p + g*j*e*l*q + g*e*l*m*o + g*j*e*m*p + g*j*e*m*o + d*e*l*m*p + d*e*l*m*o + i*d*e*m*p + g*j*e*l*p + g*j*e*l*o + d*e*l*o*p + i*j*d*l*o + i*j*e*o*p + i*j*e*m*q + i*j*d*m*q + i*j*d*m*p + i*j*d*m*o + i*j*d*l*q + i*j*d*l*p + i*j*e*m*p + i*j*e*m*o + i*j*e*l*q + i*j*e*l*p + i*j*e*l*o + i*d*e*m*q + i*d*e*m*o + i*d*e*l*q + i*d*e*l*p + j*d*l*o*p + j*d*e*l*o + g*j*d*o*p + g*j*d*m*q + g*j*d*m*p + g*j*d*m*o + g*j*d*l*q + g*j*d*l*p + g*j*d*l*o + g*j*e*o*p + g*j*e*m*q + g*d*l*o*p + g*d*l*m*p + g*d*l*m*o + j*d*e*m*p + i*d*e*o*p + j*e*o*q*l + j*e*o*p*l + j*e*m*q*l + j*d*e*o*p + j*d*e*m*q + i*j*d*o*p + g*i*e*o*p + j*d*e*m*o + j*d*e*l*q + j*d*e*l*p + j*e*m*p*l + j*e*m*o*l + g*i*e*m*q + g*i*e*m*p + g*i*e*m*o + g*i*e*l*q + g*i*e*l*p + g*i*e*l*o + j*d*l*o*q + j*d*l*m*q + j*d*l*m*p + j*d*l*m*o + i*d*e*l*o + l*m*q*d*e + l*m*q*d*g + l*m*q*e*g), c: (r*e*(l*q*g*o + i*j*o*q + g*j*o*q + g*i*o*q + j*l*m*q + j*l*m*p + j*l*m*o + i*j*m*p + i*j*m*o + i*j*l*q + i*j*l*o + i*j*m*q + j*l*o*p + g*j*o*p + g*j*m*q + g*j*m*p + i*j*l*p + i*j*o*p + j*l*o*q + g*j*m*o + g*j*l*q + g*j*l*p + g*j*l*o + g*l*o*p + g*l*m*p + g*l*m*o + g*i*m*o + g*i*o*p + g*i*m*q + g*i*m*p + g*i*l*q + g*i*l*p + g*i*l*o + l*m*q*g))/(l*q*d*e*o + l*q*d*g*o + l*q*e*g*o + i*j*d*o*q + i*j*e*o*q + j*d*e*o*q + g*j*d*o*q + g*j*e*o*q + g*i*e*o*q + i*d*e*o*q + g*i*d*o*q + g*i*d*o*p + g*i*d*m*q + g*i*d*m*p + g*i*d*m*o + g*i*d*l*q + g*i*d*l*p + g*i*d*l*o + g*e*l*m*p + g*e*l*o*p + g*j*e*l*q + g*e*l*m*o + g*j*e*m*p + g*j*e*m*o + d*e*l*m*p + d*e*l*m*o + i*d*e*m*p + g*j*e*l*p + g*j*e*l*o + d*e*l*o*p + i*j*d*l*o + i*j*e*o*p + i*j*e*m*q + i*j*d*m*q + i*j*d*m*p + i*j*d*m*o + i*j*d*l*q + i*j*d*l*p + i*j*e*m*p + i*j*e*m*o + i*j*e*l*q + i*j*e*l*p + i*j*e*l*o + i*d*e*m*q + i*d*e*m*o + i*d*e*l*q + i*d*e*l*p + j*d*l*o*p + j*d*e*l*o + g*j*d*o*p + g*j*d*m*q + g*j*d*m*p + g*j*d*m*o + g*j*d*l*q + g*j*d*l*p + g*j*d*l*o + g*j*e*o*p + g*j*e*m*q + g*d*l*o*p + g*d*l*m*p + g*d*l*m*o + j*d*e*m*p + i*d*e*o*p + j*e*o*q*l + j*e*o*p*l + j*e*m*q*l + j*d*e*o*p + j*d*e*m*q + i*j*d*o*p + g*i*e*o*p + j*d*e*m*o + j*d*e*l*q + j*d*e*l*p + j*e*m*p*l + j*e*m*o*l + g*i*e*m*q + g*i*e*m*p + g*i*e*m*o + g*i*e*l*q + g*i*e*l*p + g*i*e*l*o + j*d*l*o*q + j*d*l*m*q + j*d*l*m*p + j*d*l*m*o + i*d*e*l*o + l*m*q*d*e + l*m*q*d*g + l*m*q*e*g), f: (r*e*j*(l*q*o + l*o*p + l*m*q + l*m*p + l*m*o + i*o*q + i*o*p + i*m*q + i*m*p + i*m*o + i*l*q + i*l*p + i*l*o))/(l*q*d*e*o + l*q*d*g*o + l*q*e*g*o + i*j*d*o*q + i*j*e*o*q + j*d*e*o*q + g*j*d*o*q + g*j*e*o*q + g*i*e*o*q + i*d*e*o*q + g*i*d*o*q + g*i*d*o*p + g*i*d*m*q + g*i*d*m*p + g*i*d*m*o + g*i*d*l*q + g*i*d*l*p + g*i*d*l*o + g*e*l*m*p + g*e*l*o*p + g*j*e*l*q + g*e*l*m*o + g*j*e*m*p + g*j*e*m*o + d*e*l*m*p + d*e*l*m*o + i*d*e*m*p + g*j*e*l*p + g*j*e*l*o + d*e*l*o*p + i*j*d*l*o + i*j*e*o*p + i*j*e*m*q + i*j*d*m*q + i*j*d*m*p + i*j*d*m*o + i*j*d*l*q + i*j*d*l*p + i*j*e*m*p + i*j*e*m*o + i*j*e*l*q + i*j*e*l*p + i*j*e*l*o + i*d*e*m*q + i*d*e*m*o + i*d*e*l*q + i*d*e*l*p + j*d*l*o*p + j*d*e*l*o + g*j*d*o*p + g*j*d*m*q + g*j*d*m*p + g*j*d*m*o + g*j*d*l*q + g*j*d*l*p + g*j*d*l*o + g*j*e*o*p + g*j*e*m*q + g*d*l*o*p + g*d*l*m*p + g*d*l*m*o + j*d*e*m*p + i*d*e*o*p + j*e*o*q*l + j*e*o*p*l + j*e*m*q*l + j*d*e*o*p + j*d*e*m*q + i*j*d*o*p + g*i*e*o*p + j*d*e*m*o + j*d*e*l*q + j*d*e*l*p + j*e*m*p*l + j*e*m*o*l + g*i*e*m*q + g*i*e*m*p + g*i*e*m*o + g*i*e*l*q + g*i*e*l*p + g*i*e*l*o + j*d*l*o*q + j*d*l*m*q + j*d*l*m*p + j*d*l*m*o + i*d*e*l*o + l*m*q*d*e + l*m*q*d*g + l*m*q*e*g), h: (j*e*r*l*(o*q + o*p + m*q + m*p + m*o))/(l*q*d*e*o + l*q*d*g*o + l*q*e*g*o + i*j*d*o*q + i*j*e*o*q + j*d*e*o*q + g*j*d*o*q + g*j*e*o*q + g*i*e*o*q + i*d*e*o*q + g*i*d*o*q + g*i*d*o*p + g*i*d*m*q + g*i*d*m*p + g*i*d*m*o + g*i*d*l*q + g*i*d*l*p + g*i*d*l*o + g*e*l*m*p + g*e*l*o*p + g*j*e*l*q + g*e*l*m*o + g*j*e*m*p + g*j*e*m*o + d*e*l*m*p + d*e*l*m*o + i*d*e*m*p + g*j*e*l*p + g*j*e*l*o + d*e*l*o*p + i*j*d*l*o + i*j*e*o*p + i*j*e*m*q + i*j*d*m*q + i*j*d*m*p + i*j*d*m*o + i*j*d*l*q + i*j*d*l*p + i*j*e*m*p + i*j*e*m*o + i*j*e*l*q + i*j*e*l*p + i*j*e*l*o + i*d*e*m*q + i*d*e*m*o + i*d*e*l*q + i*d*e*l*p + j*d*l*o*p + j*d*e*l*o + g*j*d*o*p + g*j*d*m*q + g*j*d*m*p + g*j*d*m*o + g*j*d*l*q + g*j*d*l*p + g*j*d*l*o + g*j*e*o*p + g*j*e*m*q + g*d*l*o*p + g*d*l*m*p + g*d*l*m*o + j*d*e*m*p + i*d*e*o*p + j*e*o*q*l + j*e*o*p*l + j*e*m*q*l + j*d*e*o*p + j*d*e*m*q + i*j*d*o*p + g*i*e*o*p + j*d*e*m*o + j*d*e*l*q + j*d*e*l*p + j*e*m*p*l + j*e*m*o*l + g*i*e*m*q + g*i*e*m*p + g*i*e*m*o + g*i*e*l*q + g*i*e*l*p + g*i*e*l*o + j*d*l*o*q + j*d*l*m*q + j*d*l*m*p + j*d*l*m*o + i*d*e*l*o + l*m*q*d*e + l*m*q*d*g + l*m*q*e*g), k: (j*e*r*o*l*(q + p))/(l*q*d*e*o + l*q*d*g*o + l*q*e*g*o + i*j*d*o*q + i*j*e*o*q + j*d*e*o*q + g*j*d*o*q + g*j*e*o*q + g*i*e*o*q + i*d*e*o*q + g*i*d*o*q + g*i*d*o*p + g*i*d*m*q + g*i*d*m*p + g*i*d*m*o + g*i*d*l*q + g*i*d*l*p + g*i*d*l*o + g*e*l*m*p + g*e*l*o*p + g*j*e*l*q + g*e*l*m*o + g*j*e*m*p + g*j*e*m*o + d*e*l*m*p + d*e*l*m*o + i*d*e*m*p + g*j*e*l*p + g*j*e*l*o + d*e*l*o*p + i*j*d*l*o + i*j*e*o*p + i*j*e*m*q + i*j*d*m*q + i*j*d*m*p + i*j*d*m*o + i*j*d*l*q + i*j*d*l*p + i*j*e*m*p + i*j*e*m*o + i*j*e*l*q + i*j*e*l*p + i*j*e*l*o + i*d*e*m*q + i*d*e*m*o + i*d*e*l*q + i*d*e*l*p + j*d*l*o*p + j*d*e*l*o + g*j*d*o*p + g*j*d*m*q + g*j*d*m*p + g*j*d*m*o + g*j*d*l*q + g*j*d*l*p + g*j*d*l*o + g*j*e*o*p + g*j*e*m*q + g*d*l*o*p + g*d*l*m*p + g*d*l*m*o + j*d*e*m*p + i*d*e*o*p + j*e*o*q*l + j*e*o*p*l + j*e*m*q*l + j*d*e*o*p + j*d*e*m*q + i*j*d*o*p + g*i*e*o*p + j*d*e*m*o + j*d*e*l*q + j*d*e*l*p + j*e*m*p*l + j*e*m*o*l + g*i*e*m*q + g*i*e*m*p + g*i*e*m*o + g*i*e*l*q + g*i*e*l*p + g*i*e*l*o + j*d*l*o*q + j*d*l*m*q + j*d*l*m*p + j*d*l*m*o + i*d*e*l*o + l*m*q*d*e + l*m*q*d*g + l*m*q*e*g), n: (j*e*r*o*q*l)/(l*q*d*e*o + l*q*d*g*o + l*q*e*g*o + i*j*d*o*q + i*j*e*o*q + j*d*e*o*q + g*j*d*o*q + g*j*e*o*q + g*i*e*o*q + i*d*e*o*q + g*i*d*o*q + g*i*d*o*p + g*i*d*m*q + g*i*d*m*p + g*i*d*m*o + g*i*d*l*q + g*i*d*l*p + g*i*d*l*o + g*e*l*m*p + g*e*l*o*p + g*j*e*l*q + g*e*l*m*o + g*j*e*m*p + g*j*e*m*o + d*e*l*m*p + d*e*l*m*o + i*d*e*m*p + g*j*e*l*p + g*j*e*l*o + d*e*l*o*p + i*j*d*l*o + i*j*e*o*p + i*j*e*m*q + i*j*d*m*q + i*j*d*m*p + i*j*d*m*o + i*j*d*l*q + i*j*d*l*p + i*j*e*m*p + i*j*e*m*o + i*j*e*l*q + i*j*e*l*p + i*j*e*l*o + i*d*e*m*q + i*d*e*m*o + i*d*e*l*q + i*d*e*l*p + j*d*l*o*p + j*d*e*l*o + g*j*d*o*p + g*j*d*m*q + g*j*d*m*p + g*j*d*m*o + g*j*d*l*q + g*j*d*l*p + g*j*d*l*o + g*j*e*o*p + g*j*e*m*q + g*d*l*o*p + g*d*l*m*p + g*d*l*m*o + j*d*e*m*p + i*d*e*o*p + j*e*o*q*l + j*e*o*p*l + j*e*m*q*l + j*d*e*o*p + j*d*e*m*q + i*j*d*o*p + g*i*e*o*p + j*d*e*m*o + j*d*e*l*q + j*d*e*l*p + j*e*m*p*l + j*e*m*o*l + g*i*e*m*q + g*i*e*m*p + g*i*e*m*o + g*i*e*l*q + g*i*e*l*p + g*i*e*l*o + j*d*l*o*q + j*d*l*m*q + j*d*l*m*p + j*d*l*m*o + i*d*e*l*o + l*m*q*d*e + l*m*q*d*g + l*m*q*e*g), } assert solve_lin_sys(eqs, domain) == sol sympy-0.7.4.1/sympy/polys/tests/test_pythonrational.py0000644000175000017500000000750312253362407023426 0ustar georgeskgeorgesk"""Tests for PythonRational type. """ from sympy.polys.domains import PythonRational as QQ from sympy.utilities.pytest import raises def test_PythonRational__init__(): assert QQ(0).p == 0 assert QQ(0).q == 1 assert QQ(0, 1).p == 0 assert QQ(0, 1).q == 1 assert QQ(0, -1).p == 0 assert QQ(0, -1).q == 1 assert QQ(1).p == 1 assert QQ(1).q == 1 assert QQ(1, 1).p == 1 assert QQ(1, 1).q == 1 assert QQ(-1, -1).p == 1 assert QQ(-1, -1).q == 1 assert QQ(-1).p == -1 assert QQ(-1).q == 1 assert QQ(-1, 1).p == -1 assert QQ(-1, 1).q == 1 assert QQ( 1, -1).p == -1 assert QQ( 1, -1).q == 1 assert QQ(1, 2).p == 1 assert QQ(1, 2).q == 2 assert QQ(3, 4).p == 3 assert QQ(3, 4).q == 4 assert QQ(2, 2).p == 1 assert QQ(2, 2).q == 1 assert QQ(2, 4).p == 1 assert QQ(2, 4).q == 2 def test_PythonRational__hash__(): assert hash(QQ(0)) == hash(0) assert hash(QQ(1)) == hash(1) assert hash(QQ(117)) == hash(117) def test_PythonRational__int__(): assert int(QQ(-1, 4)) == 0 assert int(QQ( 1, 4)) == 0 assert int(QQ(-5, 4)) == -1 assert int(QQ( 5, 4)) == 1 def test_PythonRational__float__(): assert float(QQ(-1, 2)) == -0.5 assert float(QQ( 1, 2)) == 0.5 def test_PythonRational__abs__(): assert abs(QQ(-1, 2)) == QQ(1, 2) assert abs(QQ( 1, 2)) == QQ(1, 2) def test_PythonRational__pos__(): assert +QQ(-1, 2) == QQ(-1, 2) assert +QQ( 1, 2) == QQ( 1, 2) def test_PythonRational__neg__(): assert -QQ(-1, 2) == QQ( 1, 2) assert -QQ( 1, 2) == QQ(-1, 2) def test_PythonRational__add__(): assert QQ(-1, 2) + QQ( 1, 2) == QQ(0) assert QQ( 1, 2) + QQ(-1, 2) == QQ(0) assert QQ(1, 2) + QQ(1, 2) == QQ(1) assert QQ(1, 2) + QQ(3, 2) == QQ(2) assert QQ(3, 2) + QQ(1, 2) == QQ(2) assert QQ(3, 2) + QQ(3, 2) == QQ(3) assert 1 + QQ(1, 2) == QQ(3, 2) assert QQ(1, 2) + 1 == QQ(3, 2) def test_PythonRational__sub__(): assert QQ(-1, 2) - QQ( 1, 2) == QQ(-1) assert QQ( 1, 2) - QQ(-1, 2) == QQ( 1) assert QQ(1, 2) - QQ(1, 2) == QQ( 0) assert QQ(1, 2) - QQ(3, 2) == QQ(-1) assert QQ(3, 2) - QQ(1, 2) == QQ( 1) assert QQ(3, 2) - QQ(3, 2) == QQ( 0) assert 1 - QQ(1, 2) == QQ( 1, 2) assert QQ(1, 2) - 1 == QQ(-1, 2) def test_PythonRational__mul__(): assert QQ(-1, 2) * QQ( 1, 2) == QQ(-1, 4) assert QQ( 1, 2) * QQ(-1, 2) == QQ(-1, 4) assert QQ(1, 2) * QQ(1, 2) == QQ(1, 4) assert QQ(1, 2) * QQ(3, 2) == QQ(3, 4) assert QQ(3, 2) * QQ(1, 2) == QQ(3, 4) assert QQ(3, 2) * QQ(3, 2) == QQ(9, 4) assert 2 * QQ(1, 2) == QQ(1) assert QQ(1, 2) * 2 == QQ(1) def test_PythonRational__div__(): assert QQ(-1, 2) / QQ( 1, 2) == QQ(-1) assert QQ( 1, 2) / QQ(-1, 2) == QQ(-1) assert QQ(1, 2) / QQ(1, 2) == QQ(1) assert QQ(1, 2) / QQ(3, 2) == QQ(1, 3) assert QQ(3, 2) / QQ(1, 2) == QQ(3) assert QQ(3, 2) / QQ(3, 2) == QQ(1) assert 2 / QQ(1, 2) == QQ(4) assert QQ(1, 2) / 2 == QQ(1, 4) raises(ZeroDivisionError, lambda: QQ(1, 2) / QQ(0)) raises(ZeroDivisionError, lambda: QQ(1, 2) / 0) def test_PythonRational__pow__(): assert QQ(1)**10 == QQ(1) assert QQ(2)**10 == QQ(1024) assert QQ(1)**(-10) == QQ(1) assert QQ(2)**(-10) == QQ(1, 1024) def test_PythonRational__eq__(): assert (QQ(1, 2) == QQ(1, 2)) is True assert (QQ(1, 2) != QQ(1, 2)) is False assert (QQ(1, 2) == QQ(1, 3)) is False assert (QQ(1, 2) != QQ(1, 3)) is True def test_PythonRational__lt_le_gt_ge__(): assert (QQ(1, 2) < QQ(1, 4)) is False assert (QQ(1, 2) <= QQ(1, 4)) is False assert (QQ(1, 2) > QQ(1, 4)) is True assert (QQ(1, 2) >= QQ(1, 4)) is True assert (QQ(1, 4) < QQ(1, 2)) is True assert (QQ(1, 4) <= QQ(1, 2)) is True assert (QQ(1, 4) > QQ(1, 2)) is False assert (QQ(1, 4) >= QQ(1, 2)) is False sympy-0.7.4.1/sympy/polys/tests/test_rootisolation.py0000644000175000017500000007534312253362407023267 0ustar georgeskgeorgesk"""Tests for real and complex root isolation and refinement algorithms. """ from sympy.polys.rings import ring from sympy.polys.domains import ZZ, QQ, EX from sympy.polys.polyerrors import DomainError, RefinementFailed from sympy.utilities.pytest import raises def test_dup_sturm(): R, x = ring("x", QQ) assert R.dup_sturm(5) == [1] assert R.dup_sturm(x) == [x, 1] f = x**3 - 2*x**2 + 3*x - 5 assert R.dup_sturm(f) == [f, 3*x**2 - 4*x + 3, -QQ(10,9)*x + QQ(13,3), -QQ(3303,100)] def test_dup_refine_real_root(): R, x = ring("x", ZZ) f = x**2 - 2 assert R.dup_refine_real_root(f, QQ(1), QQ(1), steps=1) == (QQ(1), QQ(1)) assert R.dup_refine_real_root(f, QQ(1), QQ(1), steps=9) == (QQ(1), QQ(1)) raises(ValueError, lambda: R.dup_refine_real_root(f, QQ(-2), QQ(2))) s, t = QQ(1, 1), QQ(2, 1) assert R.dup_refine_real_root(f, s, t, steps=0) == (QQ(1, 1), QQ(2, 1)) assert R.dup_refine_real_root(f, s, t, steps=1) == (QQ(1, 1), QQ(3, 2)) assert R.dup_refine_real_root(f, s, t, steps=2) == (QQ(4, 3), QQ(3, 2)) assert R.dup_refine_real_root(f, s, t, steps=3) == (QQ(7, 5), QQ(3, 2)) assert R.dup_refine_real_root(f, s, t, steps=4) == (QQ(7, 5), QQ(10, 7)) s, t = QQ(1, 1), QQ(3, 2) assert R.dup_refine_real_root(f, s, t, steps=0) == (QQ(1, 1), QQ(3, 2)) assert R.dup_refine_real_root(f, s, t, steps=1) == (QQ(4, 3), QQ(3, 2)) assert R.dup_refine_real_root(f, s, t, steps=2) == (QQ(7, 5), QQ(3, 2)) assert R.dup_refine_real_root(f, s, t, steps=3) == (QQ(7, 5), QQ(10, 7)) assert R.dup_refine_real_root(f, s, t, steps=4) == (QQ(7, 5), QQ(17, 12)) s, t = QQ(1, 1), QQ(5, 3) assert R.dup_refine_real_root(f, s, t, steps=0) == (QQ(1, 1), QQ(5, 3)) assert R.dup_refine_real_root(f, s, t, steps=1) == (QQ(1, 1), QQ(3, 2)) assert R.dup_refine_real_root(f, s, t, steps=2) == (QQ(7, 5), QQ(3, 2)) assert R.dup_refine_real_root(f, s, t, steps=3) == (QQ(7, 5), QQ(13, 9)) assert R.dup_refine_real_root(f, s, t, steps=4) == (QQ(7, 5), QQ(10, 7)) s, t = QQ(-1, 1), QQ(-2, 1) assert R.dup_refine_real_root(f, s, t, steps=0) == (-QQ(2, 1), -QQ(1, 1)) assert R.dup_refine_real_root(f, s, t, steps=1) == (-QQ(3, 2), -QQ(1, 1)) assert R.dup_refine_real_root(f, s, t, steps=2) == (-QQ(3, 2), -QQ(4, 3)) assert R.dup_refine_real_root(f, s, t, steps=3) == (-QQ(3, 2), -QQ(7, 5)) assert R.dup_refine_real_root(f, s, t, steps=4) == (-QQ(10, 7), -QQ(7, 5)) raises(RefinementFailed, lambda: R.dup_refine_real_root(f, QQ(0), QQ(1))) s, t, u, v, w = QQ(1), QQ(2), QQ(24, 17), QQ(17, 12), QQ(7, 5) assert R.dup_refine_real_root(f, s, t, eps=QQ(1, 100)) == (u, v) assert R.dup_refine_real_root(f, s, t, steps=6) == (u, v) assert R.dup_refine_real_root(f, s, t, eps=QQ(1, 100), steps=5) == (w, v) assert R.dup_refine_real_root(f, s, t, eps=QQ(1, 100), steps=6) == (u, v) assert R.dup_refine_real_root(f, s, t, eps=QQ(1, 100), steps=7) == (u, v) s, t, u, v = QQ(-2), QQ(-1), QQ(-3, 2), QQ(-4, 3) assert R.dup_refine_real_root(f, s, t, disjoint=QQ(-5)) == (s, t) assert R.dup_refine_real_root(f, s, t, disjoint=-v) == (s, t) assert R.dup_refine_real_root(f, s, t, disjoint=v) == (u, v) s, t, u, v = QQ(1), QQ(2), QQ(4, 3), QQ(3, 2) assert R.dup_refine_real_root(f, s, t, disjoint=QQ(5)) == (s, t) assert R.dup_refine_real_root(f, s, t, disjoint=-u) == (s, t) assert R.dup_refine_real_root(f, s, t, disjoint=u) == (u, v) def test_dup_isolate_real_roots_sqf(): R, x = ring("x", ZZ) assert R.dup_isolate_real_roots_sqf(0) == [] assert R.dup_isolate_real_roots_sqf(5) == [] assert R.dup_isolate_real_roots_sqf(x**2 + x) == [(-1, -1), (0, 0)] assert R.dup_isolate_real_roots_sqf(x**2 - x) == [( 0, 0), (1, 1)] assert R.dup_isolate_real_roots_sqf(x**4 + x + 1) == [] I = [(-2, -1), (1, 2)] assert R.dup_isolate_real_roots_sqf(x**2 - 2) == I assert R.dup_isolate_real_roots_sqf(-x**2 + 2) == I assert R.dup_isolate_real_roots_sqf(x - 1) == \ [(1, 1)] assert R.dup_isolate_real_roots_sqf(x**2 - 3*x + 2) == \ [(1, 1), (2, 2)] assert R.dup_isolate_real_roots_sqf(x**3 - 6*x**2 + 11*x - 6) == \ [(1, 1), (2, 2), (3, 3)] assert R.dup_isolate_real_roots_sqf(x**4 - 10*x**3 + 35*x**2 - 50*x + 24) == \ [(1, 1), (2, 2), (3, 3), (4, 4)] assert R.dup_isolate_real_roots_sqf(x**5 - 15*x**4 + 85*x**3 - 225*x**2 + 274*x - 120) == \ [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)] assert R.dup_isolate_real_roots_sqf(x - 10) == \ [(10, 10)] assert R.dup_isolate_real_roots_sqf(x**2 - 30*x + 200) == \ [(10, 10), (20, 20)] assert R.dup_isolate_real_roots_sqf(x**3 - 60*x**2 + 1100*x - 6000) == \ [(10, 10), (20, 20), (30, 30)] assert R.dup_isolate_real_roots_sqf(x**4 - 100*x**3 + 3500*x**2 - 50000*x + 240000) == \ [(10, 10), (20, 20), (30, 30), (40, 40)] assert R.dup_isolate_real_roots_sqf(x**5 - 150*x**4 + 8500*x**3 - 225000*x**2 + 2740000*x - 12000000) == \ [(10, 10), (20, 20), (30, 30), (40, 40), (50, 50)] assert R.dup_isolate_real_roots_sqf(x + 1) == \ [(-1, -1)] assert R.dup_isolate_real_roots_sqf(x**2 + 3*x + 2) == \ [(-2, -2), (-1, -1)] assert R.dup_isolate_real_roots_sqf(x**3 + 6*x**2 + 11*x + 6) == \ [(-3, -3), (-2, -2), (-1, -1)] assert R.dup_isolate_real_roots_sqf(x**4 + 10*x**3 + 35*x**2 + 50*x + 24) == \ [(-4, -4), (-3, -3), (-2, -2), (-1, -1)] assert R.dup_isolate_real_roots_sqf(x**5 + 15*x**4 + 85*x**3 + 225*x**2 + 274*x + 120) == \ [(-5, -5), (-4, -4), (-3, -3), (-2, -2), (-1, -1)] assert R.dup_isolate_real_roots_sqf(x + 10) == \ [(-10, -10)] assert R.dup_isolate_real_roots_sqf(x**2 + 30*x + 200) == \ [(-20, -20), (-10, -10)] assert R.dup_isolate_real_roots_sqf(x**3 + 60*x**2 + 1100*x + 6000) == \ [(-30, -30), (-20, -20), (-10, -10)] assert R.dup_isolate_real_roots_sqf(x**4 + 100*x**3 + 3500*x**2 + 50000*x + 240000) == \ [(-40, -40), (-30, -30), (-20, -20), (-10, -10)] assert R.dup_isolate_real_roots_sqf(x**5 + 150*x**4 + 8500*x**3 + 225000*x**2 + 2740000*x + 12000000) == \ [(-50, -50), (-40, -40), (-30, -30), (-20, -20), (-10, -10)] assert R.dup_isolate_real_roots_sqf(x**2 - 5) == [(-3, -2), (2, 3)] assert R.dup_isolate_real_roots_sqf(x**3 - 5) == [(1, 2)] assert R.dup_isolate_real_roots_sqf(x**4 - 5) == [(-2, -1), (1, 2)] assert R.dup_isolate_real_roots_sqf(x**5 - 5) == [(1, 2)] assert R.dup_isolate_real_roots_sqf(x**6 - 5) == [(-2, -1), (1, 2)] assert R.dup_isolate_real_roots_sqf(x**7 - 5) == [(1, 2)] assert R.dup_isolate_real_roots_sqf(x**8 - 5) == [(-2, -1), (1, 2)] assert R.dup_isolate_real_roots_sqf(x**9 - 5) == [(1, 2)] assert R.dup_isolate_real_roots_sqf(x**2 - 1) == \ [(-1, -1), (1, 1)] assert R.dup_isolate_real_roots_sqf(x**3 + 2*x**2 - x - 2) == \ [(-2, -2), (-1, -1), (1, 1)] assert R.dup_isolate_real_roots_sqf(x**4 - 5*x**2 + 4) == \ [(-2, -2), (-1, -1), (1, 1), (2, 2)] assert R.dup_isolate_real_roots_sqf(x**5 + 3*x**4 - 5*x**3 - 15*x**2 + 4*x + 12) == \ [(-3, -3), (-2, -2), (-1, -1), (1, 1), (2, 2)] assert R.dup_isolate_real_roots_sqf(x**6 - 14*x**4 + 49*x**2 - 36) == \ [(-3, -3), (-2, -2), (-1, -1), (1, 1), (2, 2), (3, 3)] assert R.dup_isolate_real_roots_sqf(2*x**7 + x**6 - 28*x**5 - 14*x**4 + 98*x**3 + 49*x**2 - 72*x - 36) == \ [(-3, -3), (-2, -2), (-1, -1), (-1, 0), (1, 1), (2, 2), (3, 3)] assert R.dup_isolate_real_roots_sqf(4*x**8 - 57*x**6 + 210*x**4 - 193*x**2 + 36) == \ [(-3, -3), (-2, -2), (-1, -1), (-1, 0), (0, 1), (1, 1), (2, 2), (3, 3)] f = 9*x**2 - 2 assert R.dup_isolate_real_roots_sqf(f) == \ [(-1, 0), (0, 1)] assert R.dup_isolate_real_roots_sqf(f, eps=QQ(1, 10)) == \ [(QQ(-1, 2), QQ(-3, 7)), (QQ(3, 7), QQ(1, 2))] assert R.dup_isolate_real_roots_sqf(f, eps=QQ(1, 100)) == \ [(QQ(-9, 19), QQ(-8, 17)), (QQ(8, 17), QQ(9, 19))] assert R.dup_isolate_real_roots_sqf(f, eps=QQ(1, 1000)) == \ [(QQ(-33, 70), QQ(-8, 17)), (QQ(8, 17), QQ(33, 70))] assert R.dup_isolate_real_roots_sqf(f, eps=QQ(1, 10000)) == \ [(QQ(-33, 70), QQ(-107, 227)), (QQ(107, 227), QQ(33, 70))] assert R.dup_isolate_real_roots_sqf(f, eps=QQ(1, 100000)) == \ [(QQ(-305, 647), QQ(-272, 577)), (QQ(272, 577), QQ(305, 647))] assert R.dup_isolate_real_roots_sqf(f, eps=QQ(1, 1000000)) == \ [(QQ(-1121, 2378), QQ(-272, 577)), (QQ(272, 577), QQ(1121, 2378))] f = 200100012*x**5 - 700390052*x**4 + 700490079*x**3 - 200240054*x**2 + 40017*x - 2 assert R.dup_isolate_real_roots_sqf(f) == \ [(QQ(0), QQ(1, 10002)), (QQ(1, 10002), QQ(1, 10002)), (QQ(1, 2), QQ(1, 2)), (QQ(1), QQ(1)), (QQ(2), QQ(2))] assert R.dup_isolate_real_roots_sqf(f, eps=QQ(1, 100000)) == \ [(QQ(1, 10003), QQ(1, 10003)), (QQ(1, 10002), QQ(1, 10002)), (QQ(1, 2), QQ(1, 2)), (QQ(1), QQ(1)), (QQ(2), QQ(2))] a, b, c, d = 10000090000001, 2000100003, 10000300007, 10000005000008 f = 20001600074001600021*x**4 \ + 1700135866278935491773999857*x**3 \ - 2000179008931031182161141026995283662899200197*x**2 \ - 800027600594323913802305066986600025*x \ + 100000950000540000725000008 assert R.dup_isolate_real_roots_sqf(f) == \ [(-a, -a), (-1, 0), (0, 1), (d, d)] assert R.dup_isolate_real_roots_sqf(f, eps=QQ(1, 100000000000)) == \ [(-QQ(a), -QQ(a)), (-QQ(1, b), -QQ(1, b)), (QQ(1, c), QQ(1, c)), (QQ(d), QQ(d))] (u, v), B, C, (s, t) = R.dup_isolate_real_roots_sqf(f, fast=True) assert u < -a < v and B == (-QQ(1), QQ(0)) and C == (QQ(0), QQ(1)) and s < d < t assert R.dup_isolate_real_roots_sqf(f, fast=True, eps=QQ(1, 100000000000000000000000000000)) == \ [(-QQ(a), -QQ(a)), (-QQ(1, b), -QQ(1, b)), (QQ(1, c), QQ(1, c)), (QQ(d), QQ(d))] f = -10*x**4 + 8*x**3 + 80*x**2 - 32*x - 160 assert R.dup_isolate_real_roots_sqf(f) == \ [(-2, -2), (-2, -1), (2, 2), (2, 3)] assert R.dup_isolate_real_roots_sqf(f, eps=QQ(1, 100)) == \ [(-QQ(2), -QQ(2)), (-QQ(23, 14), -QQ(18, 11)), (QQ(2), QQ(2)), (QQ(39, 16), QQ(22, 9))] f = x - 1 assert R.dup_isolate_real_roots_sqf(f, inf=2) == [] assert R.dup_isolate_real_roots_sqf(f, sup=0) == [] assert R.dup_isolate_real_roots_sqf(f) == [(1, 1)] assert R.dup_isolate_real_roots_sqf(f, inf=1) == [(1, 1)] assert R.dup_isolate_real_roots_sqf(f, sup=1) == [(1, 1)] assert R.dup_isolate_real_roots_sqf(f, inf=1, sup=1) == [(1, 1)] f = x**2 - 2 assert R.dup_isolate_real_roots_sqf(f, inf=QQ(7, 4)) == [] assert R.dup_isolate_real_roots_sqf(f, inf=QQ(7, 5)) == [(QQ(7, 5), QQ(3, 2))] assert R.dup_isolate_real_roots_sqf(f, sup=QQ(7, 5)) == [(-2, -1)] assert R.dup_isolate_real_roots_sqf(f, sup=QQ(7, 4)) == [(-2, -1), (1, QQ(3, 2))] assert R.dup_isolate_real_roots_sqf(f, sup=-QQ(7, 4)) == [] assert R.dup_isolate_real_roots_sqf(f, sup=-QQ(7, 5)) == [(-QQ(3, 2), -QQ(7, 5))] assert R.dup_isolate_real_roots_sqf(f, inf=-QQ(7, 5)) == [(1, 2)] assert R.dup_isolate_real_roots_sqf(f, inf=-QQ(7, 4)) == [(-QQ(3, 2), -1), (1, 2)] I = [(-2, -1), (1, 2)] assert R.dup_isolate_real_roots_sqf(f, inf=-2) == I assert R.dup_isolate_real_roots_sqf(f, sup=+2) == I assert R.dup_isolate_real_roots_sqf(f, inf=-2, sup=2) == I R, x = ring("x", QQ) f = QQ(8, 5)*x**2 - QQ(87374, 3855)*x - QQ(17, 771) assert R.dup_isolate_real_roots_sqf(f) == [(-1, 0), (14, 15)] R, x = ring("x", EX) raises(DomainError, lambda: R.dup_isolate_real_roots_sqf(x + 3)) def test_dup_isolate_real_roots(): R, x = ring("x", ZZ) assert R.dup_isolate_real_roots(0) == [] assert R.dup_isolate_real_roots(3) == [] assert R.dup_isolate_real_roots(5*x) == [((0, 0), 1)] assert R.dup_isolate_real_roots(7*x**4) == [((0, 0), 4)] assert R.dup_isolate_real_roots(x**2 + x) == [((-1, -1), 1), ((0, 0), 1)] assert R.dup_isolate_real_roots(x**2 - x) == [((0, 0), 1), ((1, 1), 1)] assert R.dup_isolate_real_roots(x**4 + x + 1) == [] I = [((-2, -1), 1), ((1, 2), 1)] assert R.dup_isolate_real_roots(x**2 - 2) == I assert R.dup_isolate_real_roots(-x**2 + 2) == I f = 16*x**14 - 96*x**13 + 24*x**12 + 936*x**11 - 1599*x**10 - 2880*x**9 + 9196*x**8 \ + 552*x**7 - 21831*x**6 + 13968*x**5 + 21690*x**4 - 26784*x**3 - 2916*x**2 + 15552*x - 5832 g = R.dup_sqf_part(f) assert R.dup_isolate_real_roots(f) == \ [((-QQ(2), -QQ(3, 2)), 2), ((-QQ(3, 2), -QQ(1, 1)), 3), ((QQ(1), QQ(3, 2)), 3), ((QQ(3, 2), QQ(3, 2)), 4), ((QQ(5, 3), QQ(2)), 2)] assert R.dup_isolate_real_roots_sqf(g) == \ [(-QQ(2), -QQ(3, 2)), (-QQ(3, 2), -QQ(1, 1)), (QQ(1), QQ(3, 2)), (QQ(3, 2), QQ(3, 2)), (QQ(3, 2), QQ(2))] assert R.dup_isolate_real_roots(g) == \ [((-QQ(2), -QQ(3, 2)), 1), ((-QQ(3, 2), -QQ(1, 1)), 1), ((QQ(1), QQ(3, 2)), 1), ((QQ(3, 2), QQ(3, 2)), 1), ((QQ(3, 2), QQ(2)), 1)] f = x - 1 assert R.dup_isolate_real_roots(f, inf=2) == [] assert R.dup_isolate_real_roots(f, sup=0) == [] assert R.dup_isolate_real_roots(f) == [((1, 1), 1)] assert R.dup_isolate_real_roots(f, inf=1) == [((1, 1), 1)] assert R.dup_isolate_real_roots(f, sup=1) == [((1, 1), 1)] assert R.dup_isolate_real_roots(f, inf=1, sup=1) == [((1, 1), 1)] f = x**4 - 4*x**2 + 4 assert R.dup_isolate_real_roots(f, inf=QQ(7, 4)) == [] assert R.dup_isolate_real_roots(f, inf=QQ(7, 5)) == [((QQ(7, 5), QQ(3, 2)), 2)] assert R.dup_isolate_real_roots(f, sup=QQ(7, 5)) == [((-2, -1), 2)] assert R.dup_isolate_real_roots(f, sup=QQ(7, 4)) == [((-2, -1), 2), ((1, QQ(3, 2)), 2)] assert R.dup_isolate_real_roots(f, sup=-QQ(7, 4)) == [] assert R.dup_isolate_real_roots(f, sup=-QQ(7, 5)) == [((-QQ(3, 2), -QQ(7, 5)), 2)] assert R.dup_isolate_real_roots(f, inf=-QQ(7, 5)) == [((1, 2), 2)] assert R.dup_isolate_real_roots(f, inf=-QQ(7, 4)) == [((-QQ(3, 2), -1), 2), ((1, 2), 2)] I = [((-2, -1), 2), ((1, 2), 2)] assert R.dup_isolate_real_roots(f, inf=-2) == I assert R.dup_isolate_real_roots(f, sup=+2) == I assert R.dup_isolate_real_roots(f, inf=-2, sup=2) == I f = x**11 - 3*x**10 - x**9 + 11*x**8 - 8*x**7 - 8*x**6 + 12*x**5 - 4*x**4 assert R.dup_isolate_real_roots(f, basis=False) == \ [((-2, -1), 2), ((0, 0), 4), ((1, 1), 3), ((1, 2), 2)] assert R.dup_isolate_real_roots(f, basis=True) == \ [((-2, -1), 2, [1, 0, -2]), ((0, 0), 4, [1, 0]), ((1, 1), 3, [1, -1]), ((1, 2), 2, [1, 0, -2])] f = (x**45 - 45*x**44 + 990*x**43 - 1) g = (x**46 - 15180*x**43 + 9366819*x**40 - 53524680*x**39 + 260932815*x**38 - 1101716330*x**37 + 4076350421*x**36 - 13340783196*x**35 + 38910617655*x**34 - 101766230790*x**33 + 239877544005*x**32 - 511738760544*x**31 + 991493848554*x**30 - 1749695026860*x**29 + 2818953098830*x**28 - 4154246671960*x**27 + 5608233007146*x**26 - 6943526580276*x**25 + 7890371113950*x**24 - 8233430727600*x**23 + 7890371113950*x**22 - 6943526580276*x**21 + 5608233007146*x**20 - 4154246671960*x**19 + 2818953098830*x**18 - 1749695026860*x**17 + 991493848554*x**16 - 511738760544*x**15 + 239877544005*x**14 - 101766230790*x**13 + 38910617655*x**12 - 13340783196*x**11 + 4076350421*x**10 - 1101716330*x**9 + 260932815*x**8 - 53524680*x**7 + 9366819*x**6 - 1370754*x**5 + 163185*x**4 - 15180*x**3 + 1035*x**2 - 47*x + 1) assert R.dup_isolate_real_roots(f*g) == \ [((0, QQ(1, 2)), 1), ((QQ(2, 3), QQ(3, 4)), 1), ((QQ(3, 4), 1), 1), ((6, 7), 1), ((24, 25), 1)] R, x = ring("x", EX) raises(DomainError, lambda: R.dup_isolate_real_roots(x + 3)) def test_dup_isolate_real_roots_list(): R, x = ring("x", ZZ) assert R.dup_isolate_real_roots_list([x**2 + x, x]) == \ [((-1, -1), {0: 1}), ((0, 0), {0: 1, 1: 1})] assert R.dup_isolate_real_roots_list([x**2 - x, x]) == \ [((0, 0), {0: 1, 1: 1}), ((1, 1), {0: 1})] assert R.dup_isolate_real_roots_list([x + 1, x + 2, x - 1, x + 1, x - 1, x - 1]) == \ [((-QQ(2), -QQ(2)), {1: 1}), ((-QQ(1), -QQ(1)), {0: 1, 3: 1}), ((QQ(1), QQ(1)), {2: 1, 4: 1, 5: 1})] assert R.dup_isolate_real_roots_list([x + 1, x + 2, x - 1, x + 1, x - 1, x + 2]) == \ [((-QQ(2), -QQ(2)), {1: 1, 5: 1}), ((-QQ(1), -QQ(1)), {0: 1, 3: 1}), ((QQ(1), QQ(1)), {2: 1, 4: 1})] f, g = x**4 - 4*x**2 + 4, x - 1 assert R.dup_isolate_real_roots_list([f, g], inf=QQ(7, 4)) == [] assert R.dup_isolate_real_roots_list([f, g], inf=QQ(7, 5)) == \ [((QQ(7, 5), QQ(3, 2)), {0: 2})] assert R.dup_isolate_real_roots_list([f, g], sup=QQ(7, 5)) == \ [((-2, -1), {0: 2}), ((1, 1), {1: 1})] assert R.dup_isolate_real_roots_list([f, g], sup=QQ(7, 4)) == \ [((-2, -1), {0: 2}), ((1, 1), {1: 1}), ((1, QQ(3, 2)), {0: 2})] assert R.dup_isolate_real_roots_list([f, g], sup=-QQ(7, 4)) == [] assert R.dup_isolate_real_roots_list([f, g], sup=-QQ(7, 5)) == \ [((-QQ(3, 2), -QQ(7, 5)), {0: 2})] assert R.dup_isolate_real_roots_list([f, g], inf=-QQ(7, 5)) == \ [((1, 1), {1: 1}), ((1, 2), {0: 2})] assert R.dup_isolate_real_roots_list([f, g], inf=-QQ(7, 4)) == \ [((-QQ(3, 2), -1), {0: 2}), ((1, 1), {1: 1}), ((1, 2), {0: 2})] f, g = 2*x**2 - 1, x**2 - 2 assert R.dup_isolate_real_roots_list([f, g]) == \ [((-QQ(2), -QQ(1)), {1: 1}), ((-QQ(1), QQ(0)), {0: 1}), ((QQ(0), QQ(1)), {0: 1}), ((QQ(1), QQ(2)), {1: 1})] assert R.dup_isolate_real_roots_list([f, g], strict=True) == \ [((-QQ(3, 2), -QQ(4, 3)), {1: 1}), ((-QQ(1), -QQ(2, 3)), {0: 1}), ((QQ(2, 3), QQ(1)), {0: 1}), ((QQ(4, 3), QQ(3, 2)), {1: 1})] f, g = x**2 - 2, x**3 - x**2 - 2*x + 2 assert R.dup_isolate_real_roots_list([f, g]) == \ [((-QQ(2), -QQ(1)), {1: 1, 0: 1}), ((QQ(1), QQ(1)), {1: 1}), ((QQ(1), QQ(2)), {1: 1, 0: 1})] f, g = x**3 - 2*x, x**5 - x**4 - 2*x**3 + 2*x**2 assert R.dup_isolate_real_roots_list([f, g]) == \ [((-QQ(2), -QQ(1)), {1: 1, 0: 1}), ((QQ(0), QQ(0)), {0: 1, 1: 2}), ((QQ(1), QQ(1)), {1: 1}), ((QQ(1), QQ(2)), {1: 1, 0: 1})] f, g = x**9 - 3*x**8 - x**7 + 11*x**6 - 8*x**5 - 8*x**4 + 12*x**3 - 4*x**2, x**5 - 2*x**4 + 3*x**3 - 4*x**2 + 2*x assert R.dup_isolate_real_roots_list([f, g], basis=False) == \ [((-2, -1), {0: 2}), ((0, 0), {0: 2, 1: 1}), ((1, 1), {0: 3, 1: 2}), ((1, 2), {0: 2})] assert R.dup_isolate_real_roots_list([f, g], basis=True) == \ [((-2, -1), {0: 2}, [1, 0, -2]), ((0, 0), {0: 2, 1: 1}, [1, 0]), ((1, 1), {0: 3, 1: 2}, [1, -1]), ((1, 2), {0: 2}, [1, 0, -2])] R, x = ring("x", EX) raises(DomainError, lambda: R.dup_isolate_real_roots_list([x + 3])) def test_dup_isolate_real_roots_list_QQ(): R, x = ring("x", ZZ) f = x**5 - 200 g = x**5 - 201 assert R.dup_isolate_real_roots_list([f, g]) == \ [((QQ(75, 26), QQ(101, 35)), {0: 1}), ((QQ(283, 98), QQ(26, 9)), {1: 1})] R, x = ring("x", QQ) f = -QQ(1, 200)*x**5 + 1 g = -QQ(1, 201)*x**5 + 1 assert R.dup_isolate_real_roots_list([f, g]) == \ [((QQ(75, 26), QQ(101, 35)), {0: 1}), ((QQ(283, 98), QQ(26, 9)), {1: 1})] def test_dup_count_real_roots(): R, x = ring("x", ZZ) assert R.dup_count_real_roots(0) == 0 assert R.dup_count_real_roots(7) == 0 f = x - 1 assert R.dup_count_real_roots(f) == 1 assert R.dup_count_real_roots(f, inf=1) == 1 assert R.dup_count_real_roots(f, sup=0) == 0 assert R.dup_count_real_roots(f, sup=1) == 1 assert R.dup_count_real_roots(f, inf=0, sup=1) == 1 assert R.dup_count_real_roots(f, inf=0, sup=2) == 1 assert R.dup_count_real_roots(f, inf=1, sup=2) == 1 f = x**2 - 2 assert R.dup_count_real_roots(f) == 2 assert R.dup_count_real_roots(f, sup=0) == 1 assert R.dup_count_real_roots(f, inf=-1, sup=1) == 0 # parameters for test_dup_count_complex_roots_n(): n = 1..8 a, b = (-QQ(1), -QQ(1)), (QQ(1), QQ(1)) c, d = ( QQ(0), QQ(0)), (QQ(1), QQ(1)) def test_dup_count_complex_roots_1(): R, x = ring("x", ZZ) # z-1 f = x - 1 assert R.dup_count_complex_roots(f, a, b) == 1 assert R.dup_count_complex_roots(f, c, d) == 1 # z+1 f = x + 1 assert R.dup_count_complex_roots(f, a, b) == 1 assert R.dup_count_complex_roots(f, c, d) == 0 def test_dup_count_complex_roots_2(): R, x = ring("x", ZZ) # (z-1)*(z) f = x**2 - x assert R.dup_count_complex_roots(f, a, b) == 2 assert R.dup_count_complex_roots(f, c, d) == 2 # (z-1)*(-z) f = -x**2 + x assert R.dup_count_complex_roots(f, a, b) == 2 assert R.dup_count_complex_roots(f, c, d) == 2 # (z+1)*(z) f = x**2 + x assert R.dup_count_complex_roots(f, a, b) == 2 assert R.dup_count_complex_roots(f, c, d) == 1 # (z+1)*(-z) f = -x**2 - x assert R.dup_count_complex_roots(f, a, b) == 2 assert R.dup_count_complex_roots(f, c, d) == 1 def test_dup_count_complex_roots_3(): R, x = ring("x", ZZ) # (z-1)*(z+1) f = x**2 - 1 assert R.dup_count_complex_roots(f, a, b) == 2 assert R.dup_count_complex_roots(f, c, d) == 1 # (z-1)*(z+1)*(z) f = x**3 - x assert R.dup_count_complex_roots(f, a, b) == 3 assert R.dup_count_complex_roots(f, c, d) == 2 # (z-1)*(z+1)*(-z) f = -x**3 + x assert R.dup_count_complex_roots(f, a, b) == 3 assert R.dup_count_complex_roots(f, c, d) == 2 def test_dup_count_complex_roots_4(): R, x = ring("x", ZZ) # (z-I)*(z+I) f = x**2 + 1 assert R.dup_count_complex_roots(f, a, b) == 2 assert R.dup_count_complex_roots(f, c, d) == 1 # (z-I)*(z+I)*(z) f = x**3 + x assert R.dup_count_complex_roots(f, a, b) == 3 assert R.dup_count_complex_roots(f, c, d) == 2 # (z-I)*(z+I)*(-z) f = -x**3 - x assert R.dup_count_complex_roots(f, a, b) == 3 assert R.dup_count_complex_roots(f, c, d) == 2 # (z-I)*(z+I)*(z-1) f = x**3 - x**2 + x - 1 assert R.dup_count_complex_roots(f, a, b) == 3 assert R.dup_count_complex_roots(f, c, d) == 2 # (z-I)*(z+I)*(z-1)*(z) f = x**4 - x**3 + x**2 - x assert R.dup_count_complex_roots(f, a, b) == 4 assert R.dup_count_complex_roots(f, c, d) == 3 # (z-I)*(z+I)*(z-1)*(-z) f = -x**4 + x**3 - x**2 + x assert R.dup_count_complex_roots(f, a, b) == 4 assert R.dup_count_complex_roots(f, c, d) == 3 # (z-I)*(z+I)*(z-1)*(z+1) f = x**4 - 1 assert R.dup_count_complex_roots(f, a, b) == 4 assert R.dup_count_complex_roots(f, c, d) == 2 # (z-I)*(z+I)*(z-1)*(z+1)*(z) f = x**5 - x assert R.dup_count_complex_roots(f, a, b) == 5 assert R.dup_count_complex_roots(f, c, d) == 3 # (z-I)*(z+I)*(z-1)*(z+1)*(-z) f = -x**5 + x assert R.dup_count_complex_roots(f, a, b) == 5 assert R.dup_count_complex_roots(f, c, d) == 3 def test_dup_count_complex_roots_5(): R, x = ring("x", ZZ) # (z-I+1)*(z+I+1) f = x**2 + 2*x + 2 assert R.dup_count_complex_roots(f, a, b) == 2 assert R.dup_count_complex_roots(f, c, d) == 0 # (z-I+1)*(z+I+1)*(z-1) f = x**3 + x**2 - 2 assert R.dup_count_complex_roots(f, a, b) == 3 assert R.dup_count_complex_roots(f, c, d) == 1 # (z-I+1)*(z+I+1)*(z-1)*z f = x**4 + x**3 - 2*x assert R.dup_count_complex_roots(f, a, b) == 4 assert R.dup_count_complex_roots(f, c, d) == 2 # (z-I+1)*(z+I+1)*(z+1) f = x**3 + 3*x**2 + 4*x + 2 assert R.dup_count_complex_roots(f, a, b) == 3 assert R.dup_count_complex_roots(f, c, d) == 0 # (z-I+1)*(z+I+1)*(z+1)*z f = x**4 + 3*x**3 + 4*x**2 + 2*x assert R.dup_count_complex_roots(f, a, b) == 4 assert R.dup_count_complex_roots(f, c, d) == 1 # (z-I+1)*(z+I+1)*(z-1)*(z+1) f = x**4 + 2*x**3 + x**2 - 2*x - 2 assert R.dup_count_complex_roots(f, a, b) == 4 assert R.dup_count_complex_roots(f, c, d) == 1 # (z-I+1)*(z+I+1)*(z-1)*(z+1)*z f = x**5 + 2*x**4 + x**3 - 2*x**2 - 2*x assert R.dup_count_complex_roots(f, a, b) == 5 assert R.dup_count_complex_roots(f, c, d) == 2 def test_dup_count_complex_roots_6(): R, x = ring("x", ZZ) # (z-I-1)*(z+I-1) f = x**2 - 2*x + 2 assert R.dup_count_complex_roots(f, a, b) == 2 assert R.dup_count_complex_roots(f, c, d) == 1 # (z-I-1)*(z+I-1)*(z-1) f = x**3 - 3*x**2 + 4*x - 2 assert R.dup_count_complex_roots(f, a, b) == 3 assert R.dup_count_complex_roots(f, c, d) == 2 # (z-I-1)*(z+I-1)*(z-1)*z f = x**4 - 3*x**3 + 4*x**2 - 2*x assert R.dup_count_complex_roots(f, a, b) == 4 assert R.dup_count_complex_roots(f, c, d) == 3 # (z-I-1)*(z+I-1)*(z+1) f = x**3 - x**2 + 2 assert R.dup_count_complex_roots(f, a, b) == 3 assert R.dup_count_complex_roots(f, c, d) == 1 # (z-I-1)*(z+I-1)*(z+1)*z f = x**4 - x**3 + 2*x assert R.dup_count_complex_roots(f, a, b) == 4 assert R.dup_count_complex_roots(f, c, d) == 2 # (z-I-1)*(z+I-1)*(z-1)*(z+1) f = x**4 - 2*x**3 + x**2 + 2*x - 2 assert R.dup_count_complex_roots(f, a, b) == 4 assert R.dup_count_complex_roots(f, c, d) == 2 # (z-I-1)*(z+I-1)*(z-1)*(z+1)*z f = x**5 - 2*x**4 + x**3 + 2*x**2 - 2*x assert R.dup_count_complex_roots(f, a, b) == 5 assert R.dup_count_complex_roots(f, c, d) == 3 def test_dup_count_complex_roots_7(): R, x = ring("x", ZZ) # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1) f = x**4 + 4 assert R.dup_count_complex_roots(f, a, b) == 4 assert R.dup_count_complex_roots(f, c, d) == 1 # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z-2) f = x**5 - 2*x**4 + 4*x - 8 assert R.dup_count_complex_roots(f, a, b) == 4 assert R.dup_count_complex_roots(f, c, d) == 1 # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z**2-2) f = x**6 - 2*x**4 + 4*x**2 - 8 assert R.dup_count_complex_roots(f, a, b) == 4 assert R.dup_count_complex_roots(f, c, d) == 1 # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z-1) f = x**5 - x**4 + 4*x - 4 assert R.dup_count_complex_roots(f, a, b) == 5 assert R.dup_count_complex_roots(f, c, d) == 2 # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z-1)*z f = x**6 - x**5 + 4*x**2 - 4*x assert R.dup_count_complex_roots(f, a, b) == 6 assert R.dup_count_complex_roots(f, c, d) == 3 # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z+1) f = x**5 + x**4 + 4*x + 4 assert R.dup_count_complex_roots(f, a, b) == 5 assert R.dup_count_complex_roots(f, c, d) == 1 # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z+1)*z f = x**6 + x**5 + 4*x**2 + 4*x assert R.dup_count_complex_roots(f, a, b) == 6 assert R.dup_count_complex_roots(f, c, d) == 2 # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z-1)*(z+1) f = x**6 - x**4 + 4*x**2 - 4 assert R.dup_count_complex_roots(f, a, b) == 6 assert R.dup_count_complex_roots(f, c, d) == 2 # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z-1)*(z+1)*z f = x**7 - x**5 + 4*x**3 - 4*x assert R.dup_count_complex_roots(f, a, b) == 7 assert R.dup_count_complex_roots(f, c, d) == 3 # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z-1)*(z+1)*(z-I)*(z+I) f = x**8 + 3*x**4 - 4 assert R.dup_count_complex_roots(f, a, b) == 8 assert R.dup_count_complex_roots(f, c, d) == 3 def test_dup_count_complex_roots_8(): R, x = ring("x", ZZ) # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z-1)*(z+1)*(z-I)*(z+I)*z f = x**9 + 3*x**5 - 4*x assert R.dup_count_complex_roots(f, a, b) == 9 assert R.dup_count_complex_roots(f, c, d) == 4 # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z-1)*(z+1)*(z-I)*(z+I)*(z**2-2)*z f = x**11 - 2*x**9 + 3*x**7 - 6*x**5 - 4*x**3 + 8*x assert R.dup_count_complex_roots(f, a, b) == 9 assert R.dup_count_complex_roots(f, c, d) == 4 def test_dup_count_complex_roots_implicit(): R, x = ring("x", ZZ) # z*(z-1)*(z+1)*(z-I)*(z+I) f = x**5 - x assert R.dup_count_complex_roots(f) == 5 assert R.dup_count_complex_roots(f, sup=(0, 0)) == 3 assert R.dup_count_complex_roots(f, inf=(0, 0)) == 3 def test_dup_count_complex_roots_exclude(): R, x = ring("x", ZZ) # z*(z-1)*(z+1)*(z-I)*(z+I) f = x**5 - x a, b = (-QQ(1), QQ(0)), (QQ(1), QQ(1)) assert R.dup_count_complex_roots(f, a, b) == 4 assert R.dup_count_complex_roots(f, a, b, exclude=['S']) == 3 assert R.dup_count_complex_roots(f, a, b, exclude=['N']) == 3 assert R.dup_count_complex_roots(f, a, b, exclude=['S', 'N']) == 2 assert R.dup_count_complex_roots(f, a, b, exclude=['E']) == 4 assert R.dup_count_complex_roots(f, a, b, exclude=['W']) == 4 assert R.dup_count_complex_roots(f, a, b, exclude=['E', 'W']) == 4 assert R.dup_count_complex_roots(f, a, b, exclude=['N', 'S', 'E', 'W']) == 2 assert R.dup_count_complex_roots(f, a, b, exclude=['SW']) == 3 assert R.dup_count_complex_roots(f, a, b, exclude=['SE']) == 3 assert R.dup_count_complex_roots(f, a, b, exclude=['SW', 'SE']) == 2 assert R.dup_count_complex_roots(f, a, b, exclude=['SW', 'SE', 'S']) == 1 assert R.dup_count_complex_roots(f, a, b, exclude=['SW', 'SE', 'S', 'N']) == 0 a, b = (QQ(0), QQ(0)), (QQ(1), QQ(1)) assert R.dup_count_complex_roots(f, a, b, exclude=True) == 1 def test_dup_isolate_complex_roots_sqf(): R, x = ring("x", ZZ) f = x**2 - 2*x + 3 assert R.dup_isolate_complex_roots_sqf(f) == \ [((0, -6), (6, 0)), ((0, 0), (6, 6))] assert [ r.as_tuple() for r in R.dup_isolate_complex_roots_sqf(f, blackbox=True) ] == \ [((0, -6), (6, 0)), ((0, 0), (6, 6))] assert R.dup_isolate_complex_roots_sqf(f, eps=QQ(1, 10)) == \ [((QQ(15, 16), -QQ(3, 2)), (QQ(33, 32), -QQ(45, 32))), ((QQ(15, 16), QQ(45, 32)), (QQ(33, 32), QQ(3, 2)))] assert R.dup_isolate_complex_roots_sqf(f, eps=QQ(1, 100)) == \ [((QQ(255, 256), -QQ(363, 256)), (QQ(513, 512), -QQ(723, 512))), ((QQ(255, 256), QQ(723, 512)), (QQ(513, 512), QQ(363, 256)))] f = 7*x**4 - 19*x**3 + 20*x**2 + 17*x + 20 assert R.dup_isolate_complex_roots_sqf(f) == \ [((-QQ(40, 7), -QQ(40, 7)), (0, 0)), ((-QQ(40, 7), 0), (0, QQ(40, 7))), ((0, -QQ(40, 7)), (QQ(40, 7), 0)), ((0, 0), (QQ(40, 7), QQ(40, 7)))] def test_dup_isolate_all_roots_sqf(): R, x = ring("x", ZZ) f = 4*x**4 - x**3 + 2*x**2 + 5*x assert R.dup_isolate_all_roots_sqf(f) == \ ([(-1, 0), (0, 0)], [((0, -QQ(5, 2)), (QQ(5, 2), 0)), ((0, 0), (QQ(5, 2), QQ(5, 2)))]) assert R.dup_isolate_all_roots_sqf(f, eps=QQ(1, 10)) == \ ([(QQ(-7, 8), QQ(-6, 7)), (0, 0)], [((QQ(35, 64), -QQ(35, 32)), (QQ(5, 8), -QQ(65, 64))), ((QQ(35, 64), QQ(65, 64)), (QQ(5, 8), QQ(35, 32)))]) def test_dup_isolate_all_roots(): R, x = ring("x", ZZ) f = 4*x**4 - x**3 + 2*x**2 + 5*x assert R.dup_isolate_all_roots(f) == \ ([((-1, 0), 1), ((0, 0), 1)], [(((0, -QQ(5, 2)), (QQ(5, 2), 0)), 1), (((0, 0), (QQ(5, 2), QQ(5, 2))), 1)]) assert R.dup_isolate_all_roots(f, eps=QQ(1, 10)) == \ ([((QQ(-7, 8), QQ(-6, 7)), 1), ((0, 0), 1)], [(((QQ(35, 64), -QQ(35, 32)), (QQ(5, 8), -QQ(65, 64))), 1), (((QQ(35, 64), QQ(65, 64)), (QQ(5, 8), QQ(35, 32))), 1)]) f = x**5 + x**4 - 2*x**3 - 2*x**2 + x + 1 raises(NotImplementedError, lambda: R.dup_isolate_all_roots(f)) sympy-0.7.4.1/sympy/polys/tests/test_orthopolys.py0000644000175000017500000001207012253362407022570 0ustar georgeskgeorgesk"""Tests for efficient functions for generating orthogonal polynomials. """ from sympy import Poly, S, Rational as Q from sympy.utilities.pytest import raises from sympy.polys.orthopolys import ( jacobi_poly, gegenbauer_poly, chebyshevt_poly, chebyshevu_poly, hermite_poly, legendre_poly, laguerre_poly, ) from sympy.abc import x, a, b def test_jacobi_poly(): raises(ValueError, lambda: jacobi_poly(-1, a, b, x)) assert jacobi_poly(1, a, b, x, polys=True) == Poly( (a/2 + b/2 + 1)*x + a/2 - b/2, x, domain='ZZ(a,b)') assert jacobi_poly(0, a, b, x) == 1 assert jacobi_poly(1, a, b, x) == a/2 - b/2 + x*(a/2 + b/2 + 1) assert jacobi_poly(2, a, b, x) == (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 + S(3)/2) + x*(a**2/4 + 3*a/4 - b**2/4 - 3*b/4) - S(1)/2) assert jacobi_poly(1, a, b, polys=True) == Poly( (a/2 + b/2 + 1)*x + a/2 - b/2, x, domain='ZZ(a,b)') def test_gegenbauer_poly(): raises(ValueError, lambda: gegenbauer_poly(-1, a, x)) assert gegenbauer_poly( 1, a, x, polys=True) == Poly(2*a*x, x, domain='ZZ(a)') assert gegenbauer_poly(0, a, x) == 1 assert gegenbauer_poly(1, a, x) == 2*a*x assert gegenbauer_poly(2, a, x) == -a + x**2*(2*a**2 + 2*a) assert gegenbauer_poly( 3, a, x) == x**3*(4*a**3/3 + 4*a**2 + 8*a/3) + x*(-2*a**2 - 2*a) assert gegenbauer_poly(1, S.Half).dummy_eq(x) assert gegenbauer_poly(1, a, polys=True) == Poly(2*a*x, x, domain='ZZ(a)') def test_chebyshevt_poly(): raises(ValueError, lambda: chebyshevt_poly(-1, x)) assert chebyshevt_poly(1, x, polys=True) == Poly(x) assert chebyshevt_poly(0, x) == 1 assert chebyshevt_poly(1, x) == x assert chebyshevt_poly(2, x) == 2*x**2 - 1 assert chebyshevt_poly(3, x) == 4*x**3 - 3*x assert chebyshevt_poly(4, x) == 8*x**4 - 8*x**2 + 1 assert chebyshevt_poly(5, x) == 16*x**5 - 20*x**3 + 5*x assert chebyshevt_poly(6, x) == 32*x**6 - 48*x**4 + 18*x**2 - 1 assert chebyshevt_poly(1).dummy_eq(x) assert chebyshevt_poly(1, polys=True) == Poly(x) def test_chebyshevu_poly(): raises(ValueError, lambda: chebyshevu_poly(-1, x)) assert chebyshevu_poly(1, x, polys=True) == Poly(2*x) assert chebyshevu_poly(0, x) == 1 assert chebyshevu_poly(1, x) == 2*x assert chebyshevu_poly(2, x) == 4*x**2 - 1 assert chebyshevu_poly(3, x) == 8*x**3 - 4*x assert chebyshevu_poly(4, x) == 16*x**4 - 12*x**2 + 1 assert chebyshevu_poly(5, x) == 32*x**5 - 32*x**3 + 6*x assert chebyshevu_poly(6, x) == 64*x**6 - 80*x**4 + 24*x**2 - 1 assert chebyshevu_poly(1).dummy_eq(2*x) assert chebyshevu_poly(1, polys=True) == Poly(2*x) def test_hermite_poly(): raises(ValueError, lambda: hermite_poly(-1, x)) assert hermite_poly(1, x, polys=True) == Poly(2*x) assert hermite_poly(0, x) == 1 assert hermite_poly(1, x) == 2*x assert hermite_poly(2, x) == 4*x**2 - 2 assert hermite_poly(3, x) == 8*x**3 - 12*x assert hermite_poly(4, x) == 16*x**4 - 48*x**2 + 12 assert hermite_poly(5, x) == 32*x**5 - 160*x**3 + 120*x assert hermite_poly(6, x) == 64*x**6 - 480*x**4 + 720*x**2 - 120 assert hermite_poly(1).dummy_eq(2*x) assert hermite_poly(1, polys=True) == Poly(2*x) def test_legendre_poly(): raises(ValueError, lambda: legendre_poly(-1, x)) assert legendre_poly(1, x, polys=True) == Poly(x) assert legendre_poly(0, x) == 1 assert legendre_poly(1, x) == x assert legendre_poly(2, x) == Q(3, 2)*x**2 - Q(1, 2) assert legendre_poly(3, x) == Q(5, 2)*x**3 - Q(3, 2)*x assert legendre_poly(4, x) == Q(35, 8)*x**4 - Q(30, 8)*x**2 + Q(3, 8) assert legendre_poly(5, x) == Q(63, 8)*x**5 - Q(70, 8)*x**3 + Q(15, 8)*x assert legendre_poly(6, x) == Q( 231, 16)*x**6 - Q(315, 16)*x**4 + Q(105, 16)*x**2 - Q(5, 16) assert legendre_poly(1).dummy_eq(x) assert legendre_poly(1, polys=True) == Poly(x) def test_laguerre_poly(): raises(ValueError, lambda: laguerre_poly(-1, x)) assert laguerre_poly(1, x, polys=True) == Poly(-x + 1) assert laguerre_poly(0, x) == 1 assert laguerre_poly(1, x) == -x + 1 assert laguerre_poly(2, x) == Q(1, 2)*x**2 - Q(4, 2)*x + 1 assert laguerre_poly(3, x) == -Q(1, 6)*x**3 + Q(9, 6)*x**2 - Q(18, 6)*x + 1 assert laguerre_poly(4, x) == Q( 1, 24)*x**4 - Q(16, 24)*x**3 + Q(72, 24)*x**2 - Q(96, 24)*x + 1 assert laguerre_poly(5, x) == -Q(1, 120)*x**5 + Q(25, 120)*x**4 - Q( 200, 120)*x**3 + Q(600, 120)*x**2 - Q(600, 120)*x + 1 assert laguerre_poly(6, x) == Q(1, 720)*x**6 - Q(36, 720)*x**5 + Q(450, 720)*x**4 - Q(2400, 720)*x**3 + Q(5400, 720)*x**2 - Q(4320, 720)*x + 1 assert laguerre_poly(0, x, a) == 1 assert laguerre_poly(1, x, a) == -x + a + 1 assert laguerre_poly(2, x, a) == x**2/2 + (-a - 2)*x + a**2/2 + 3*a/2 + 1 assert laguerre_poly(3, x, a) == -x**3/6 + (a/2 + Q( 3)/2)*x**2 + (-a**2/2 - 5*a/2 - 3)*x + a**3/6 + a**2 + 11*a/6 + 1 assert laguerre_poly(1).dummy_eq(-x + 1) assert laguerre_poly(1, polys=True) == Poly(-x + 1) sympy-0.7.4.1/sympy/polys/tests/test_euclidtools.py0000644000175000017500000004603212253362407022701 0ustar georgeskgeorgesk"""Tests for Euclidean algorithms, GCDs, LCMs and polynomial remainder sequences. """ from sympy.polys.rings import ring from sympy.polys.domains import ZZ, QQ, RR from sympy.polys.specialpolys import ( f_polys, dmp_fateman_poly_F_1, dmp_fateman_poly_F_2, dmp_fateman_poly_F_3) f_0, f_1, f_2, f_3, f_4, f_5, f_6 = f_polys() def test_dup_gcdex(): R, x = ring("x", QQ) f = x**4 - 2*x**3 - 6*x**2 + 12*x + 15 g = x**3 + x**2 - 4*x - 4 s = -QQ(1,5)*x + QQ(3,5) t = QQ(1,5)*x**2 - QQ(6,5)*x + 2 h = x + 1 assert R.dup_half_gcdex(f, g) == (s, h) assert R.dup_gcdex(f, g) == (s, t, h) f = x**4 + 4*x**3 - x + 1 g = x**3 - x + 1 s, t, h = R.dup_gcdex(f, g) S, T, H = R.dup_gcdex(g, f) assert R.dup_add(R.dup_mul(s, f), R.dup_mul(t, g)) == h assert R.dup_add(R.dup_mul(S, g), R.dup_mul(T, f)) == H f = 2*x g = x**2 - 16 s = QQ(1,32)*x t = -QQ(1,16) h = 1 assert R.dup_half_gcdex(f, g) == (s, h) assert R.dup_gcdex(f, g) == (s, t, h) def test_dup_invert(): R, x = ring("x", QQ) assert R.dup_invert(2*x, x**2 - 16) == QQ(1,32)*x def test_dup_euclidean_prs(): R, x = ring("x", QQ) f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 assert R.dup_euclidean_prs(f, g) == [ f, g, -QQ(5,9)*x**4 + QQ(1,9)*x**2 - QQ(1,3), -QQ(117,25)*x**2 - 9*x + QQ(441,25), QQ(233150,19773)*x - QQ(102500,6591), -QQ(1288744821,543589225)] def test_dup_primitive_prs(): R, x = ring("x", ZZ) f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 assert R.dup_primitive_prs(f, g) == [ f, g, -5*x**4 + x**2 - 3, 13*x**2 + 25*x - 49, 4663*x - 6150, 1] def test_dup_subresultants(): R, x = ring("x", ZZ) assert R.dup_resultant(0, 0) == 0 assert R.dup_resultant(1, 0) == 0 assert R.dup_resultant(0, 1) == 0 f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 a = 15*x**4 - 3*x**2 + 9 b = 65*x**2 + 125*x - 245 c = 9326*x - 12300 d = 260708 assert R.dup_subresultants(f, g) == [f, g, a, b, c, d] assert R.dup_resultant(f, g) == R.dup_LC(d) f = x**2 - 2*x + 1 g = x**2 - 1 a = 2*x - 2 assert R.dup_subresultants(f, g) == [f, g, a] assert R.dup_resultant(f, g) == 0 f = x**2 + 1 g = x**2 - 1 a = -2 assert R.dup_subresultants(f, g) == [f, g, a] assert R.dup_resultant(f, g) == 4 f = x**2 - 1 g = x**3 - x**2 + 2 assert R.dup_resultant(f, g) == 0 f = 3*x**3 - x g = 5*x**2 + 1 assert R.dup_resultant(f, g) == 64 f = x**2 - 2*x + 7 g = x**3 - x + 5 assert R.dup_resultant(f, g) == 265 f = x**3 - 6*x**2 + 11*x - 6 g = x**3 - 15*x**2 + 74*x - 120 assert R.dup_resultant(f, g) == -8640 f = x**3 - 6*x**2 + 11*x - 6 g = x**3 - 10*x**2 + 29*x - 20 assert R.dup_resultant(f, g) == 0 f = x**3 - 1 g = x**3 + 2*x**2 + 2*x - 1 assert R.dup_resultant(f, g) == 16 f = x**8 - 2 g = x - 1 assert R.dup_resultant(f, g) == -1 def test_dmp_subresultants(): R, x, y = ring("x,y", ZZ) assert R.dmp_resultant(0, 0) == 0 assert R.dmp_prs_resultant(0, 0)[0] == 0 assert R.dmp_zz_collins_resultant(0, 0) == 0 assert R.dmp_qq_collins_resultant(0, 0) == 0 assert R.dmp_resultant(1, 0) == 0 assert R.dmp_resultant(1, 0) == 0 assert R.dmp_resultant(1, 0) == 0 assert R.dmp_resultant(0, 1) == 0 assert R.dmp_prs_resultant(0, 1)[0] == 0 assert R.dmp_zz_collins_resultant(0, 1) == 0 assert R.dmp_qq_collins_resultant(0, 1) == 0 f = 3*x**2*y - y**3 - 4 g = x**2 + x*y**3 - 9 a = 3*x*y**4 + y**3 - 27*y + 4 b = -3*y**10 - 12*y**7 + y**6 - 54*y**4 + 8*y**3 + 729*y**2 - 216*y + 16 r = R.dmp_LC(b) assert R.dmp_subresultants(f, g) == [f, g, a, b] assert R.dmp_resultant(f, g) == r assert R.dmp_prs_resultant(f, g)[0] == r assert R.dmp_zz_collins_resultant(f, g) == r assert R.dmp_qq_collins_resultant(f, g) == r f = -x**3 + 5 g = 3*x**2*y + x**2 a = 45*y**2 + 30*y + 5 b = 675*y**3 + 675*y**2 + 225*y + 25 r = R.dmp_LC(b) assert R.dmp_subresultants(f, g) == [f, g, a] assert R.dmp_resultant(f, g) == r assert R.dmp_prs_resultant(f, g)[0] == r assert R.dmp_zz_collins_resultant(f, g) == r assert R.dmp_qq_collins_resultant(f, g) == r R, x, y, z, u, v = ring("x,y,z,u,v", ZZ) f = 6*x**2 - 3*x*y - 2*x*z + y*z g = x**2 - x*u - x*v + u*v r = y**2*z**2 - 3*y**2*z*u - 3*y**2*z*v + 9*y**2*u*v - 2*y*z**2*u \ - 2*y*z**2*v + 6*y*z*u**2 + 12*y*z*u*v + 6*y*z*v**2 - 18*y*u**2*v \ - 18*y*u*v**2 + 4*z**2*u*v - 12*z*u**2*v - 12*z*u*v**2 + 36*u**2*v**2 assert R.dmp_zz_collins_resultant(f, g) == r.drop(x) R, x, y, z, u, v = ring("x,y,z,u,v", QQ) f = x**2 - QQ(1,2)*x*y - QQ(1,3)*x*z + QQ(1,6)*y*z g = x**2 - x*u - x*v + u*v r = QQ(1,36)*y**2*z**2 - QQ(1,12)*y**2*z*u - QQ(1,12)*y**2*z*v + QQ(1,4)*y**2*u*v \ - QQ(1,18)*y*z**2*u - QQ(1,18)*y*z**2*v + QQ(1,6)*y*z*u**2 + QQ(1,3)*y*z*u*v \ + QQ(1,6)*y*z*v**2 - QQ(1,2)*y*u**2*v - QQ(1,2)*y*u*v**2 + QQ(1,9)*z**2*u*v \ - QQ(1,3)*z*u**2*v - QQ(1,3)*z*u*v**2 + u**2*v**2 assert R.dmp_qq_collins_resultant(f, g) == r.drop(x) Rt, t = ring("t", ZZ) Rx, x = ring("x", Rt) f = x**6 - 5*x**4 + 5*x**2 + 4 g = -6*t*x**5 + x**4 + 20*t*x**3 - 3*x**2 - 10*t*x + 6 assert Rx.dup_resultant(f, g) == 2930944*t**6 + 2198208*t**4 + 549552*t**2 + 45796 def test_dup_discriminant(): R, x = ring("x", ZZ) assert R.dup_discriminant(0) == 0 assert R.dup_discriminant(x) == 1 assert R.dup_discriminant(x**3 + 3*x**2 + 9*x - 13) == -11664 assert R.dup_discriminant(5*x**5 + x**3 + 2) == 31252160 assert R.dup_discriminant(x**4 + 2*x**3 + 6*x**2 - 22*x + 13) == 0 assert R.dup_discriminant(12*x**7 + 15*x**4 + 30*x**3 + x**2 + 1) == -220289699947514112 def test_dmp_discriminant(): R, x = ring("x", ZZ) assert R.dmp_discriminant(0) == 0 R, x, y = ring("x,y", ZZ) assert R.dmp_discriminant(0) == 0 assert R.dmp_discriminant(y) == 0 assert R.dmp_discriminant(x**3 + 3*x**2 + 9*x - 13) == -11664 assert R.dmp_discriminant(5*x**5 + x**3 + 2) == 31252160 assert R.dmp_discriminant(x**4 + 2*x**3 + 6*x**2 - 22*x + 13) == 0 assert R.dmp_discriminant(12*x**7 + 15*x**4 + 30*x**3 + x**2 + 1) == -220289699947514112 assert R.dmp_discriminant(x**2*y + 2*y) == (-8*y**2).drop(x) assert R.dmp_discriminant(x*y**2 + 2*x) == 1 R, x, y, z = ring("x,y,z", ZZ) assert R.dmp_discriminant(x*y + z) == 1 R, x, y, z, u = ring("x,y,z,u", ZZ) assert R.dmp_discriminant(x**2*y + x*z + u) == (-4*y*u + z**2).drop(x) R, x, y, z, u, v = ring("x,y,z,u,v", ZZ) assert R.dmp_discriminant(x**3*y + x**2*z + x*u + v) == \ (-27*y**2*v**2 + 18*y*z*u*v - 4*y*u**3 - 4*z**3*v + z**2*u**2).drop(x) def test_dup_gcd(): R, x = ring("x", ZZ) f, g = 0, 0 assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (0, 0, 0) f, g = 2, 0 assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 1, 0) f, g = -2, 0 assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, -1, 0) f, g = 0, -2 assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 0, -1) f, g = 0, 2*x + 4 assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2*x + 4, 0, 1) f, g = 2*x + 4, 0 assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2*x + 4, 1, 0) f, g = 2, 2 assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 1, 1) f, g = -2, 2 assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, -1, 1) f, g = 2, -2 assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 1, -1) f, g = -2, -2 assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, -1, -1) f, g = x**2 + 2*x + 1, 1 assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (1, x**2 + 2*x + 1, 1) f, g = x**2 + 2*x + 1, 2 assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (1, x**2 + 2*x + 1, 2) f, g = 2*x**2 + 4*x + 2, 2 assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, x**2 + 2*x + 1, 1) f, g = 2, 2*x**2 + 4*x + 2 assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 1, x**2 + 2*x + 1) f, g = 2*x**2 + 4*x + 2, x + 1 assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (x + 1, 2*x + 2, 1) f, g = x + 1, 2*x**2 + 4*x + 2 assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (x + 1, 1, 2*x + 2) f, g = x - 31, x assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (1, f, g) f = x**4 + 8*x**3 + 21*x**2 + 22*x + 8 g = x**3 + 6*x**2 + 11*x + 6 h = x**2 + 3*x + 2 cff = x**2 + 5*x + 4 cfg = x + 3 assert R.dup_zz_heu_gcd(f, g) == (h, cff, cfg) assert R.dup_rr_prs_gcd(f, g) == (h, cff, cfg) f = x**4 - 4 g = x**4 + 4*x**2 + 4 h = x**2 + 2 cff = x**2 - 2 cfg = x**2 + 2 assert R.dup_zz_heu_gcd(f, g) == (h, cff, cfg) assert R.dup_rr_prs_gcd(f, g) == (h, cff, cfg) f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 h = 1 cff = f cfg = g assert R.dup_zz_heu_gcd(f, g) == (h, cff, cfg) assert R.dup_rr_prs_gcd(f, g) == (h, cff, cfg) R, x = ring("x", QQ) f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 h = 1 cff = f cfg = g assert R.dup_qq_heu_gcd(f, g) == (h, cff, cfg) assert R.dup_ff_prs_gcd(f, g) == (h, cff, cfg) R, x = ring("x", ZZ) f = - 352518131239247345597970242177235495263669787845475025293906825864749649589178600387510272*x**49 \ + 46818041807522713962450042363465092040687472354933295397472942006618953623327997952*x**42 \ + 378182690892293941192071663536490788434899030680411695933646320291525827756032*x**35 \ + 112806468807371824947796775491032386836656074179286744191026149539708928*x**28 \ - 12278371209708240950316872681744825481125965781519138077173235712*x**21 \ + 289127344604779611146960547954288113529690984687482920704*x**14 \ + 19007977035740498977629742919480623972236450681*x**7 \ + 311973482284542371301330321821976049 g = 365431878023781158602430064717380211405897160759702125019136*x**21 \ + 197599133478719444145775798221171663643171734081650688*x**14 \ - 9504116979659010018253915765478924103928886144*x**7 \ - 311973482284542371301330321821976049 assert R.dup_zz_heu_gcd(f, R.dup_diff(f, 1))[0] == g assert R.dup_rr_prs_gcd(f, R.dup_diff(f, 1))[0] == g R, x = ring("x", QQ) f = QQ(1,2)*x**2 + x + QQ(1,2) g = QQ(1,2)*x + QQ(1,2) h = x + 1 assert R.dup_qq_heu_gcd(f, g) == (h, g, QQ(1,2)) assert R.dup_ff_prs_gcd(f, g) == (h, g, QQ(1,2)) R, x = ring("x", ZZ) f = 1317378933230047068160*x + 2945748836994210856960 g = 120352542776360960*x + 269116466014453760 h = 120352542776360960*x + 269116466014453760 cff = 10946 cfg = 1 assert R.dup_zz_heu_gcd(f, g) == (h, cff, cfg) def test_dmp_gcd(): R, x, y = ring("x,y", ZZ) f, g = 0, 0 assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (0, 0, 0) f, g = 2, 0 assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 1, 0) f, g = -2, 0 assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, -1, 0) f, g = 0, -2 assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 0, -1) f, g = 0, 2*x + 4 assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2*x + 4, 0, 1) f, g = 2*x + 4, 0 assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2*x + 4, 1, 0) f, g = 2, 2 assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 1, 1) f, g = -2, 2 assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, -1, 1) f, g = 2, -2 assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 1, -1) f, g = -2, -2 assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, -1, -1) f, g = x**2 + 2*x + 1, 1 assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (1, x**2 + 2*x + 1, 1) f, g = x**2 + 2*x + 1, 2 assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (1, x**2 + 2*x + 1, 2) f, g = 2*x**2 + 4*x + 2, 2 assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, x**2 + 2*x + 1, 1) f, g = 2, 2*x**2 + 4*x + 2 assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 1, x**2 + 2*x + 1) f, g = 2*x**2 + 4*x + 2, x + 1 assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (x + 1, 2*x + 2, 1) f, g = x + 1, 2*x**2 + 4*x + 2 assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (x + 1, 1, 2*x + 2) R, x, y, z, u = ring("x,y,z,u", ZZ) f, g = u**2 + 2*u + 1, 2*u + 2 assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (u + 1, u + 1, 2) f, g = z**2*u**2 + 2*z**2*u + z**2 + z*u + z, u**2 + 2*u + 1 h, cff, cfg = u + 1, z**2*u + z**2 + z, u + 1 assert R.dmp_zz_heu_gcd(f, g) == (h, cff, cfg) assert R.dmp_rr_prs_gcd(f, g) == (h, cff, cfg) assert R.dmp_zz_heu_gcd(g, f) == (h, cfg, cff) assert R.dmp_rr_prs_gcd(g, f) == (h, cfg, cff) R, x, y, z = ring("x,y,z", ZZ) f, g, h = map(R.from_dense, dmp_fateman_poly_F_1(2, ZZ)) H, cff, cfg = R.dmp_zz_heu_gcd(f, g) assert H == h and R.dmp_mul(H, cff) == f \ and R.dmp_mul(H, cfg) == g H, cff, cfg = R.dmp_rr_prs_gcd(f, g) assert H == h and R.dmp_mul(H, cff) == f \ and R.dmp_mul(H, cfg) == g R, x, y, z, u, v = ring("x,y,z,u,v", ZZ) f, g, h = map(R.from_dense, dmp_fateman_poly_F_1(4, ZZ)) H, cff, cfg = R.dmp_zz_heu_gcd(f, g) assert H == h and R.dmp_mul(H, cff) == f \ and R.dmp_mul(H, cfg) == g R, x, y, z, u, v, a, b = ring("x,y,z,u,v,a,b", ZZ) f, g, h = map(R.from_dense, dmp_fateman_poly_F_1(6, ZZ)) H, cff, cfg = R.dmp_zz_heu_gcd(f, g) assert H == h and R.dmp_mul(H, cff) == f \ and R.dmp_mul(H, cfg) == g R, x, y, z, u, v, a, b, c, d = ring("x,y,z,u,v,a,b,c,d", ZZ) f, g, h = map(R.from_dense, dmp_fateman_poly_F_1(8, ZZ)) H, cff, cfg = R.dmp_zz_heu_gcd(f, g) assert H == h and R.dmp_mul(H, cff) == f \ and R.dmp_mul(H, cfg) == g R, x, y, z = ring("x,y,z", ZZ) f, g, h = map(R.from_dense, dmp_fateman_poly_F_2(2, ZZ)) H, cff, cfg = R.dmp_zz_heu_gcd(f, g) assert H == h and R.dmp_mul(H, cff) == f \ and R.dmp_mul(H, cfg) == g H, cff, cfg = R.dmp_rr_prs_gcd(f, g) assert H == h and R.dmp_mul(H, cff) == f \ and R.dmp_mul(H, cfg) == g f, g, h = map(R.from_dense, dmp_fateman_poly_F_3(2, ZZ)) H, cff, cfg = R.dmp_zz_heu_gcd(f, g) assert H == h and R.dmp_mul(H, cff) == f \ and R.dmp_mul(H, cfg) == g H, cff, cfg = R.dmp_rr_prs_gcd(f, g) assert H == h and R.dmp_mul(H, cff) == f \ and R.dmp_mul(H, cfg) == g R, x, y, z, u, v = ring("x,y,z,u,v", ZZ) f, g, h = map(R.from_dense, dmp_fateman_poly_F_3(4, ZZ)) H, cff, cfg = R.dmp_inner_gcd(f, g) assert H == h and R.dmp_mul(H, cff) == f \ and R.dmp_mul(H, cfg) == g R, x, y = ring("x,y", QQ) f = QQ(1,2)*x**2 + x + QQ(1,2) g = QQ(1,2)*x + QQ(1,2) h = x + 1 assert R.dmp_qq_heu_gcd(f, g) == (h, g, QQ(1,2)) assert R.dmp_ff_prs_gcd(f, g) == (h, g, QQ(1,2)) R, x, y = ring("x,y", RR) f = 2.1*x*y**2 - 2.2*x*y + 2.1*x g = 1.0*x**3 assert R.dmp_ff_prs_gcd(f, g) == \ (1.0*x, 2.1*y**2 - 2.2*y + 2.1, 1.0*x**2) def test_dup_lcm(): R, x = ring("x", ZZ) assert R.dup_lcm(2, 6) == 6 assert R.dup_lcm(2*x**3, 6*x) == 6*x**3 assert R.dup_lcm(2*x**3, 3*x) == 6*x**3 assert R.dup_lcm(x**2 + x, x) == x**2 + x assert R.dup_lcm(x**2 + x, 2*x) == 2*x**2 + 2*x assert R.dup_lcm(x**2 + 2*x, x) == x**2 + 2*x assert R.dup_lcm(2*x**2 + x, x) == 2*x**2 + x assert R.dup_lcm(2*x**2 + x, 2*x) == 4*x**2 + 2*x def test_dmp_lcm(): R, x, y = ring("x,y", ZZ) assert R.dmp_lcm(2, 6) == 6 assert R.dmp_lcm(x, y) == x*y assert R.dmp_lcm(2*x**3, 6*x*y**2) == 6*x**3*y**2 assert R.dmp_lcm(2*x**3, 3*x*y**2) == 6*x**3*y**2 assert R.dmp_lcm(x**2*y, x*y**2) == x**2*y**2 f = 2*x*y**5 - 3*x*y**4 - 2*x*y**3 + 3*x*y**2 g = y**5 - 2*y**3 + y h = 2*x*y**7 - 3*x*y**6 - 4*x*y**5 + 6*x*y**4 + 2*x*y**3 - 3*x*y**2 assert R.dmp_lcm(f, g) == h f = x**3 - 3*x**2*y - 9*x*y**2 - 5*y**3 g = x**4 + 6*x**3*y + 12*x**2*y**2 + 10*x*y**3 + 3*y**4 h = x**5 + x**4*y - 18*x**3*y**2 - 50*x**2*y**3 - 47*x*y**4 - 15*y**5 assert R.dmp_lcm(f, g) == h def test_dmp_content(): R, x,y = ring("x,y", ZZ) assert R.dmp_content(-2) == 2 f, g, F = 3*y**2 + 2*y + 1, 1, 0 for i in range(0, 5): g *= f F += x**i*g assert R.dmp_content(F) == f.drop(x) R, x,y,z = ring("x,y,z", ZZ) assert R.dmp_content(f_4) == 1 assert R.dmp_content(f_5) == 1 R, x,y,z,t = ring("x,y,z,t", ZZ) assert R.dmp_content(f_6) == 1 def test_dmp_primitive(): R, x,y = ring("x,y", ZZ) assert R.dmp_primitive(0) == (0, 0) assert R.dmp_primitive(1) == (1, 1) f, g, F = 3*y**2 + 2*y + 1, 1, 0 for i in range(0, 5): g *= f F += x**i*g assert R.dmp_primitive(F) == (f.drop(x), F / f) R, x,y,z = ring("x,y,z", ZZ) cont, f = R.dmp_primitive(f_4) assert cont == 1 and f == f_4 cont, f = R.dmp_primitive(f_5) assert cont == 1 and f == f_5 R, x,y,z,t = ring("x,y,z,t", ZZ) cont, f = R.dmp_primitive(f_6) assert cont == 1 and f == f_6 def test_dup_cancel(): R, x = ring("x", ZZ) f = 2*x**2 - 2 g = x**2 - 2*x + 1 p = 2*x + 2 q = x - 1 assert R.dup_cancel(f, g) == (p, q) assert R.dup_cancel(f, g, include=False) == (1, 1, p, q) f = -x - 2 g = 3*x - 4 F = x + 2 G = -3*x + 4 assert R.dup_cancel(f, g) == (f, g) assert R.dup_cancel(F, G) == (f, g) assert R.dup_cancel(0, 0) == (0, 0) assert R.dup_cancel(0, 0, include=False) == (1, 1, 0, 0) assert R.dup_cancel(x, 0) == (1, 0) assert R.dup_cancel(x, 0, include=False) == (1, 1, 1, 0) assert R.dup_cancel(0, x) == (0, 1) assert R.dup_cancel(0, x, include=False) == (1, 1, 0, 1) f = 0 g = x one = 1 assert R.dup_cancel(f, g, include=True) == (f, one) def test_dmp_cancel(): R, x, y = ring("x,y", ZZ) f = 2*x**2 - 2 g = x**2 - 2*x + 1 p = 2*x + 2 q = x - 1 assert R.dmp_cancel(f, g) == (p, q) assert R.dmp_cancel(f, g, include=False) == (1, 1, p, q) assert R.dmp_cancel(0, 0) == (0, 0) assert R.dmp_cancel(0, 0, include=False) == (1, 1, 0, 0) assert R.dmp_cancel(y, 0) == (1, 0) assert R.dmp_cancel(y, 0, include=False) == (1, 1, 1, 0) assert R.dmp_cancel(0, y) == (0, 1) assert R.dmp_cancel(0, y, include=False) == (1, 1, 0, 1) sympy-0.7.4.1/sympy/polys/tests/test_numberfields.py0000644000175000017500000006620312253362407023034 0ustar georgeskgeorgesk"""Tests for computational algebraic number field theory. """ from sympy import (S, Rational, Symbol, Poly, sin, sqrt, I, oo, Tuple, expand, Add, Mul, pi, cos, sin, exp) from sympy.utilities.pytest import raises, slow from sympy.polys.numberfields import ( minimal_polynomial, primitive_element, is_isomorphism_possible, field_isomorphism_pslq, field_isomorphism, to_number_field, AlgebraicNumber, isolate, IntervalPrinter, ) from sympy.polys.polyerrors import ( IsomorphismFailed, NotAlgebraic, GeneratorsError, ) from sympy.polys.polyclasses import DMP from sympy.polys.domains import QQ from sympy.polys.rootoftools import RootOf from sympy.polys.polytools import degree from sympy.solvers import solve from sympy.utilities.pytest import skip from sympy.abc import x, y, z Q = Rational def test_minimal_polynomial(): assert minimal_polynomial(-7, x) == x + 7 assert minimal_polynomial(-1, x) == x + 1 assert minimal_polynomial( 0, x) == x assert minimal_polynomial( 1, x) == x - 1 assert minimal_polynomial( 7, x) == x - 7 assert minimal_polynomial(sqrt(2), x) == x**2 - 2 assert minimal_polynomial(sqrt(5), x) == x**2 - 5 assert minimal_polynomial(sqrt(6), x) == x**2 - 6 assert minimal_polynomial(2*sqrt(2), x) == x**2 - 8 assert minimal_polynomial(3*sqrt(5), x) == x**2 - 45 assert minimal_polynomial(4*sqrt(6), x) == x**2 - 96 assert minimal_polynomial(2*sqrt(2) + 3, x) == x**2 - 6*x + 1 assert minimal_polynomial(3*sqrt(5) + 6, x) == x**2 - 12*x - 9 assert minimal_polynomial(4*sqrt(6) + 7, x) == x**2 - 14*x - 47 assert minimal_polynomial(2*sqrt(2) - 3, x) == x**2 + 6*x + 1 assert minimal_polynomial(3*sqrt(5) - 6, x) == x**2 + 12*x - 9 assert minimal_polynomial(4*sqrt(6) - 7, x) == x**2 + 14*x - 47 assert minimal_polynomial(sqrt(1 + sqrt(6)), x) == x**4 - 2*x**2 - 5 assert minimal_polynomial(sqrt(I + sqrt(6)), x) == x**8 - 10*x**4 + 49 assert minimal_polynomial(2*I + sqrt(2 + I), x) == x**4 + 4*x**2 + 8*x + 37 assert minimal_polynomial(sqrt(2) + sqrt(3), x) == x**4 - 10*x**2 + 1 assert minimal_polynomial( sqrt(2) + sqrt(3) + sqrt(6), x) == x**4 - 22*x**2 - 48*x - 23 a = 1 - 9*sqrt(2) + 7*sqrt(3) assert minimal_polynomial( 1/a, x) == 392*x**4 - 1232*x**3 + 612*x**2 + 4*x - 1 assert minimal_polynomial( 1/sqrt(a), x) == 392*x**8 - 1232*x**6 + 612*x**4 + 4*x**2 - 1 raises(NotAlgebraic, lambda: minimal_polynomial(oo, x)) raises(NotAlgebraic, lambda: minimal_polynomial(2**y, x)) raises(NotAlgebraic, lambda: minimal_polynomial(sin(1), x)) assert minimal_polynomial(sqrt(2)).dummy_eq(x**2 - 2) assert minimal_polynomial(sqrt(2), x) == x**2 - 2 assert minimal_polynomial(sqrt(2), polys=True) == Poly(x**2 - 2) assert minimal_polynomial(sqrt(2), x, polys=True) == Poly(x**2 - 2) assert minimal_polynomial(sqrt(2), x, polys=True, compose=False) == Poly(x**2 - 2) a = AlgebraicNumber(sqrt(2)) b = AlgebraicNumber(sqrt(3)) assert minimal_polynomial(a, x) == x**2 - 2 assert minimal_polynomial(b, x) == x**2 - 3 assert minimal_polynomial(a, x, polys=True) == Poly(x**2 - 2) assert minimal_polynomial(b, x, polys=True) == Poly(x**2 - 3) assert minimal_polynomial(sqrt(a/2 + 17), x) == 2*x**4 - 68*x**2 + 577 assert minimal_polynomial(sqrt(b/2 + 17), x) == 4*x**4 - 136*x**2 + 1153 a, b = sqrt(2)/3 + 7, AlgebraicNumber(sqrt(2)/3 + 7) f = 81*x**8 - 2268*x**6 - 4536*x**5 + 22644*x**4 + 63216*x**3 - \ 31608*x**2 - 189648*x + 141358 assert minimal_polynomial(sqrt(a) + sqrt(sqrt(a)), x) == f assert minimal_polynomial(sqrt(b) + sqrt(sqrt(b)), x) == f assert minimal_polynomial( a**Q(3, 2), x) == 729*x**4 - 506898*x**2 + 84604519 # issue 2895 eq = S(''' -1/(800*sqrt(-1/240 + 1/(18000*(-1/17280000 + sqrt(15)*I/28800000)**(1/3)) + 2*(-1/17280000 + sqrt(15)*I/28800000)**(1/3)))''') assert minimal_polynomial(eq, x) == 8000*x**2 - 1 ex = 1 + sqrt(2) + sqrt(3) mp = minimal_polynomial(ex, x) assert mp == x**4 - 4*x**3 - 4*x**2 + 16*x - 8 ex = 1/(1 + sqrt(2) + sqrt(3)) mp = minimal_polynomial(ex, x) assert mp == 8*x**4 - 16*x**3 + 4*x**2 + 4*x - 1 p = (expand((1 + sqrt(2) - 2*sqrt(3) + sqrt(7))**3))**Rational(1, 3) mp = minimal_polynomial(p, x) assert mp == x**8 - 8*x**7 - 56*x**6 + 448*x**5 + 480*x**4 - 5056*x**3 + 1984*x**2 + 7424*x - 3008 p = expand((1 + sqrt(2) - 2*sqrt(3) + sqrt(7))**3) mp = minimal_polynomial(p, x) assert mp == x**8 - 512*x**7 - 118208*x**6 + 31131136*x**5 + 647362560*x**4 - 56026611712*x**3 + 116994310144*x**2 + 404854931456*x - 27216576512 assert minimal_polynomial(S("-sqrt(5)/2 - 1/2 + (-sqrt(5)/2 - 1/2)**2"), x) == x - 1 a = 1 + sqrt(2) assert minimal_polynomial((a*sqrt(2) + a)**3, x) == x**2 - 198*x + 1 p = 1/(1 + sqrt(2) + sqrt(3)) assert minimal_polynomial(p, x, compose=False) == 8*x**4 - 16*x**3 + 4*x**2 + 4*x - 1 p = 2/(1 + sqrt(2) + sqrt(3)) assert minimal_polynomial(p, x, compose=False) == x**4 - 4*x**3 + 2*x**2 + 4*x - 2 assert minimal_polynomial(1 + sqrt(2)*I, x, compose=False) == x**2 - 2*x + 3 assert minimal_polynomial(1/(1 + sqrt(2)) + 1, x, compose=False) == x**2 - 2 assert minimal_polynomial(sqrt(2)*I + I*(1 + sqrt(2)), x, compose=False) == x**4 + 18*x**2 + 49 def test_minimal_polynomial_hi_prec(): p = 1/sqrt(1 - 9*sqrt(2) + 7*sqrt(3) + S(1)/10**30) mp = minimal_polynomial(p, x) # checked with Wolfram Alpha assert mp.coeff(x**6) == -1232000000000000000000000000001223999999999999999999999999999987999999999999999999999999999996000000000000000000000000000000 def test_minimal_polynomial_sq(): from sympy import Add, expand_multinomial p = expand_multinomial((1 + 5*sqrt(2) + 2*sqrt(3))**3) mp = minimal_polynomial(p**Rational(1, 3), x) assert mp == x**4 - 4*x**3 - 118*x**2 + 244*x + 1321 p = expand_multinomial((1 + sqrt(2) - 2*sqrt(3) + sqrt(7))**3) mp = minimal_polynomial(p**Rational(1, 3), x) assert mp == x**8 - 8*x**7 - 56*x**6 + 448*x**5 + 480*x**4 - 5056*x**3 + 1984*x**2 + 7424*x - 3008 p = Add(*[sqrt(i) for i in range(1, 12)]) mp = minimal_polynomial(p, x) assert mp.subs({x: 0}) == -71965773323122507776 def test_minpoly_compose(): # issue 3769 eq = S(''' -1/(800*sqrt(-1/240 + 1/(18000*(-1/17280000 + sqrt(15)*I/28800000)**(1/3)) + 2*(-1/17280000 + sqrt(15)*I/28800000)**(1/3)))''') mp = minimal_polynomial(eq + 3, x) assert mp == 8000*x**2 - 48000*x + 71999 # issue 2789 assert minimal_polynomial(exp(I*pi/8), x) == x**8 + 1 mp = minimal_polynomial(sin(pi/7) + sqrt(2), x) assert mp == 4096*x**12 - 63488*x**10 + 351488*x**8 - 826496*x**6 + \ 770912*x**4 - 268432*x**2 + 28561 mp = minimal_polynomial(cos(pi/7) + sqrt(2), x) assert mp == 64*x**6 - 64*x**5 - 432*x**4 + 304*x**3 + 712*x**2 - \ 232*x - 239 mp = minimal_polynomial(exp(I*pi/7) + sqrt(2), x) assert mp == x**12 - 2*x**11 - 9*x**10 + 16*x**9 + 43*x**8 - 70*x**7 - 97*x**6 + 126*x**5 + 211*x**4 - 212*x**3 - 37*x**2 + 142*x + 127 mp = minimal_polynomial(sin(pi/7) + sqrt(2), x) assert mp == 4096*x**12 - 63488*x**10 + 351488*x**8 - 826496*x**6 + \ 770912*x**4 - 268432*x**2 + 28561 mp = minimal_polynomial(cos(pi/7) + sqrt(2), x) assert mp == 64*x**6 - 64*x**5 - 432*x**4 + 304*x**3 + 712*x**2 - \ 232*x - 239 mp = minimal_polynomial(exp(I*pi/7) + sqrt(2), x) assert mp == x**12 - 2*x**11 - 9*x**10 + 16*x**9 + 43*x**8 - 70*x**7 - 97*x**6 + 126*x**5 + 211*x**4 - 212*x**3 - 37*x**2 + 142*x + 127 mp = minimal_polynomial(exp(2*I*pi/7), x) assert mp == x**6 + x**5 + x**4 + x**3 + x**2 + x + 1 mp = minimal_polynomial(exp(2*I*pi/15), x) assert mp == x**8 - x**7 + x**5 - x**4 + x**3 - x + 1 mp = minimal_polynomial(cos(2*pi/7), x) assert mp == 8*x**3 + 4*x**2 - 4*x - 1 mp = minimal_polynomial(sin(2*pi/7), x) ex = (5*cos(2*pi/7) - 7)/(9*cos(pi/7) - 5*cos(3*pi/7)) mp = minimal_polynomial(ex, x) assert mp == x**3 + 2*x**2 - x - 1 assert minimal_polynomial(-1/(2*cos(pi/7)), x) == x**3 + 2*x**2 - x - 1 assert minimal_polynomial(sin(2*pi/15), x) == \ 256*x**8 - 448*x**6 + 224*x**4 - 32*x**2 + 1 assert minimal_polynomial(sin(5*pi/14), x) == 8*x**3 - 4*x**2 - 4*x + 1 assert minimal_polynomial(cos(pi/15), x) == 16*x**4 + 8*x**3 - 16*x**2 - 8*x + 1 ex = RootOf(x**3 +x*4 + 1, 0) mp = minimal_polynomial(ex, x) assert mp == x**3 + 4*x + 1 mp = minimal_polynomial(ex + 1, x) assert mp == x**3 - 3*x**2 + 7*x - 4 assert minimal_polynomial(exp(I*pi/3), x) == x**2 - x + 1 assert minimal_polynomial(exp(I*pi/4), x) == x**4 + 1 assert minimal_polynomial(exp(I*pi/6), x) == x**4 - x**2 + 1 assert minimal_polynomial(exp(I*pi/9), x) == x**6 - x**3 + 1 assert minimal_polynomial(exp(I*pi/10), x) == x**8 - x**6 + x**4 - x**2 + 1 assert minimal_polynomial(sin(pi/9), x) == 64*x**6 - 96*x**4 + 36*x**2 - 3 assert minimal_polynomial(sin(pi/11), x) == 1024*x**10 - 2816*x**8 + \ 2816*x**6 - 1232*x**4 + 220*x**2 - 11 ex = 2**Rational(1, 3)*exp(Rational(2, 3)*I*pi) assert minimal_polynomial(ex, x) == x**3 - 2 raises(NotAlgebraic, lambda: minimal_polynomial(cos(pi*sqrt(2)), x)) raises(NotAlgebraic, lambda: minimal_polynomial(sin(pi*sqrt(2)), x)) raises(NotAlgebraic, lambda: minimal_polynomial(exp(I*pi*sqrt(2)), x)) # issue 2835 ex = 1/(-36000 - 7200*sqrt(5) + (12*sqrt(10)*sqrt(sqrt(5) + 5) + 24*sqrt(10)*sqrt(-sqrt(5) + 5))**2) + 1 raises(ZeroDivisionError, lambda: minimal_polynomial(ex, x)) ex = sqrt(1 + 2**Rational(1,3)) + sqrt(1 + 2**Rational(1,4)) + sqrt(2) mp = minimal_polynomial(ex, x) assert degree(mp) == 48 and mp.subs({x:0}) == -16630256576 def test_minpoly_issue4014(): # see discussion in https://github.com/sympy/sympy/pull/2234 from sympy.simplify.simplify import nsimplify r = nsimplify(pi, tolerance=0.000000001) mp = minimal_polynomial(r, x) assert mp == 1768292677839237920489538677417507171630859375*x**109 - \ 2734577732179183863586489182929671773182898498218854181690460140337930774573792597743853652058046464 def test_primitive_element(): assert primitive_element([sqrt(2)], x) == (x**2 - 2, [1]) assert primitive_element( [sqrt(2), sqrt(3)], x) == (x**4 - 10*x**2 + 1, [1, 1]) assert primitive_element([sqrt(2)], x, polys=True) == (Poly(x**2 - 2), [1]) assert primitive_element([sqrt( 2), sqrt(3)], x, polys=True) == (Poly(x**4 - 10*x**2 + 1), [1, 1]) assert primitive_element( [sqrt(2)], x, ex=True) == (x**2 - 2, [1], [[1, 0]]) assert primitive_element([sqrt(2), sqrt(3)], x, ex=True) == \ (x**4 - 10*x**2 + 1, [1, 1], [[Q(1, 2), 0, -Q(9, 2), 0], [- Q(1, 2), 0, Q(11, 2), 0]]) assert primitive_element( [sqrt(2)], x, ex=True, polys=True) == (Poly(x**2 - 2), [1], [[1, 0]]) assert primitive_element([sqrt(2), sqrt(3)], x, ex=True, polys=True) == \ (Poly(x**4 - 10*x**2 + 1), [1, 1], [[Q(1, 2), 0, -Q(9, 2), 0], [-Q(1, 2), 0, Q(11, 2), 0]]) assert primitive_element([sqrt(2)], polys=True) == (Poly(x**2 - 2), [1]) raises(ValueError, lambda: primitive_element([], x, ex=False)) raises(ValueError, lambda: primitive_element([], x, ex=True)) def test_field_isomorphism_pslq(): a = AlgebraicNumber(I) b = AlgebraicNumber(I*sqrt(3)) raises(NotImplementedError, lambda: field_isomorphism_pslq(a, b)) a = AlgebraicNumber(sqrt(2)) b = AlgebraicNumber(sqrt(3)) c = AlgebraicNumber(sqrt(7)) d = AlgebraicNumber(sqrt(2) + sqrt(3)) e = AlgebraicNumber(sqrt(2) + sqrt(3) + sqrt(7)) assert field_isomorphism_pslq(a, a) == [1, 0] assert field_isomorphism_pslq(a, b) is None assert field_isomorphism_pslq(a, c) is None assert field_isomorphism_pslq(a, d) == [Q(1, 2), 0, -Q(9, 2), 0] assert field_isomorphism_pslq( a, e) == [Q(1, 80), 0, -Q(1, 2), 0, Q(59, 20), 0] assert field_isomorphism_pslq(b, a) is None assert field_isomorphism_pslq(b, b) == [1, 0] assert field_isomorphism_pslq(b, c) is None assert field_isomorphism_pslq(b, d) == [-Q(1, 2), 0, Q(11, 2), 0] assert field_isomorphism_pslq(b, e) == [-Q( 3, 640), 0, Q(67, 320), 0, -Q(297, 160), 0, Q(313, 80), 0] assert field_isomorphism_pslq(c, a) is None assert field_isomorphism_pslq(c, b) is None assert field_isomorphism_pslq(c, c) == [1, 0] assert field_isomorphism_pslq(c, d) is None assert field_isomorphism_pslq(c, e) == [Q( 3, 640), 0, -Q(71, 320), 0, Q(377, 160), 0, -Q(469, 80), 0] assert field_isomorphism_pslq(d, a) is None assert field_isomorphism_pslq(d, b) is None assert field_isomorphism_pslq(d, c) is None assert field_isomorphism_pslq(d, d) == [1, 0] assert field_isomorphism_pslq(d, e) == [-Q( 3, 640), 0, Q(71, 320), 0, -Q(377, 160), 0, Q(549, 80), 0] assert field_isomorphism_pslq(e, a) is None assert field_isomorphism_pslq(e, b) is None assert field_isomorphism_pslq(e, c) is None assert field_isomorphism_pslq(e, d) is None assert field_isomorphism_pslq(e, e) == [1, 0] f = AlgebraicNumber(3*sqrt(2) + 8*sqrt(7) - 5) assert field_isomorphism_pslq( f, e) == [Q(3, 80), 0, -Q(139, 80), 0, Q(347, 20), 0, -Q(761, 20), -5] def test_field_isomorphism(): assert field_isomorphism(3, sqrt(2)) == [3] assert field_isomorphism( I*sqrt(3), I*sqrt(3)/2) == [ 2, 0] assert field_isomorphism(-I*sqrt(3), I*sqrt(3)/2) == [-2, 0] assert field_isomorphism( I*sqrt(3), -I*sqrt(3)/2) == [-2, 0] assert field_isomorphism(-I*sqrt(3), -I*sqrt(3)/2) == [ 2, 0] assert field_isomorphism( 2*I*sqrt(3)/7, 5*I*sqrt(3)/3) == [ S(6)/35, 0] assert field_isomorphism(-2*I*sqrt(3)/7, 5*I*sqrt(3)/3) == [-S(6)/35, 0] assert field_isomorphism( 2*I*sqrt(3)/7, -5*I*sqrt(3)/3) == [-S(6)/35, 0] assert field_isomorphism(-2*I*sqrt(3)/7, -5*I*sqrt(3)/3) == [ S(6)/35, 0] assert field_isomorphism( 2*I*sqrt(3)/7 + 27, 5*I*sqrt(3)/3) == [ S(6)/35, 27] assert field_isomorphism( -2*I*sqrt(3)/7 + 27, 5*I*sqrt(3)/3) == [-S(6)/35, 27] assert field_isomorphism( 2*I*sqrt(3)/7 + 27, -5*I*sqrt(3)/3) == [-S(6)/35, 27] assert field_isomorphism( -2*I*sqrt(3)/7 + 27, -5*I*sqrt(3)/3) == [ S(6)/35, 27] p = AlgebraicNumber( sqrt(2) + sqrt(3)) q = AlgebraicNumber(-sqrt(2) + sqrt(3)) r = AlgebraicNumber( sqrt(2) - sqrt(3)) s = AlgebraicNumber(-sqrt(2) - sqrt(3)) pos_coeffs = [ S(1)/2, S(0), -S(9)/2, S(0)] neg_coeffs = [-S(1)/2, S(0), S(9)/2, S(0)] a = AlgebraicNumber(sqrt(2)) assert is_isomorphism_possible(a, p) is True assert is_isomorphism_possible(a, q) is True assert is_isomorphism_possible(a, r) is True assert is_isomorphism_possible(a, s) is True assert field_isomorphism(a, p, fast=True) == pos_coeffs assert field_isomorphism(a, q, fast=True) == neg_coeffs assert field_isomorphism(a, r, fast=True) == pos_coeffs assert field_isomorphism(a, s, fast=True) == neg_coeffs assert field_isomorphism(a, p, fast=False) == pos_coeffs assert field_isomorphism(a, q, fast=False) == neg_coeffs assert field_isomorphism(a, r, fast=False) == pos_coeffs assert field_isomorphism(a, s, fast=False) == neg_coeffs a = AlgebraicNumber(-sqrt(2)) assert is_isomorphism_possible(a, p) is True assert is_isomorphism_possible(a, q) is True assert is_isomorphism_possible(a, r) is True assert is_isomorphism_possible(a, s) is True assert field_isomorphism(a, p, fast=True) == neg_coeffs assert field_isomorphism(a, q, fast=True) == pos_coeffs assert field_isomorphism(a, r, fast=True) == neg_coeffs assert field_isomorphism(a, s, fast=True) == pos_coeffs assert field_isomorphism(a, p, fast=False) == neg_coeffs assert field_isomorphism(a, q, fast=False) == pos_coeffs assert field_isomorphism(a, r, fast=False) == neg_coeffs assert field_isomorphism(a, s, fast=False) == pos_coeffs pos_coeffs = [ S(1)/2, S(0), -S(11)/2, S(0)] neg_coeffs = [-S(1)/2, S(0), S(11)/2, S(0)] a = AlgebraicNumber(sqrt(3)) assert is_isomorphism_possible(a, p) is True assert is_isomorphism_possible(a, q) is True assert is_isomorphism_possible(a, r) is True assert is_isomorphism_possible(a, s) is True assert field_isomorphism(a, p, fast=True) == neg_coeffs assert field_isomorphism(a, q, fast=True) == neg_coeffs assert field_isomorphism(a, r, fast=True) == pos_coeffs assert field_isomorphism(a, s, fast=True) == pos_coeffs assert field_isomorphism(a, p, fast=False) == neg_coeffs assert field_isomorphism(a, q, fast=False) == neg_coeffs assert field_isomorphism(a, r, fast=False) == pos_coeffs assert field_isomorphism(a, s, fast=False) == pos_coeffs a = AlgebraicNumber(-sqrt(3)) assert is_isomorphism_possible(a, p) is True assert is_isomorphism_possible(a, q) is True assert is_isomorphism_possible(a, r) is True assert is_isomorphism_possible(a, s) is True assert field_isomorphism(a, p, fast=True) == pos_coeffs assert field_isomorphism(a, q, fast=True) == pos_coeffs assert field_isomorphism(a, r, fast=True) == neg_coeffs assert field_isomorphism(a, s, fast=True) == neg_coeffs assert field_isomorphism(a, p, fast=False) == pos_coeffs assert field_isomorphism(a, q, fast=False) == pos_coeffs assert field_isomorphism(a, r, fast=False) == neg_coeffs assert field_isomorphism(a, s, fast=False) == neg_coeffs pos_coeffs = [ S(3)/2, S(0), -S(33)/2, -S(8)] neg_coeffs = [-S(3)/2, S(0), S(33)/2, -S(8)] a = AlgebraicNumber(3*sqrt(3) - 8) assert is_isomorphism_possible(a, p) is True assert is_isomorphism_possible(a, q) is True assert is_isomorphism_possible(a, r) is True assert is_isomorphism_possible(a, s) is True assert field_isomorphism(a, p, fast=True) == neg_coeffs assert field_isomorphism(a, q, fast=True) == neg_coeffs assert field_isomorphism(a, r, fast=True) == pos_coeffs assert field_isomorphism(a, s, fast=True) == pos_coeffs assert field_isomorphism(a, p, fast=False) == neg_coeffs assert field_isomorphism(a, q, fast=False) == neg_coeffs assert field_isomorphism(a, r, fast=False) == pos_coeffs assert field_isomorphism(a, s, fast=False) == pos_coeffs a = AlgebraicNumber(3*sqrt(2) + 2*sqrt(3) + 1) pos_1_coeffs = [ S(1)/2, S(0), -S(5)/2, S(1)] neg_5_coeffs = [-S(5)/2, S(0), S(49)/2, S(1)] pos_5_coeffs = [ S(5)/2, S(0), -S(49)/2, S(1)] neg_1_coeffs = [-S(1)/2, S(0), S(5)/2, S(1)] assert is_isomorphism_possible(a, p) is True assert is_isomorphism_possible(a, q) is True assert is_isomorphism_possible(a, r) is True assert is_isomorphism_possible(a, s) is True assert field_isomorphism(a, p, fast=True) == pos_1_coeffs assert field_isomorphism(a, q, fast=True) == neg_5_coeffs assert field_isomorphism(a, r, fast=True) == pos_5_coeffs assert field_isomorphism(a, s, fast=True) == neg_1_coeffs assert field_isomorphism(a, p, fast=False) == pos_1_coeffs assert field_isomorphism(a, q, fast=False) == neg_5_coeffs assert field_isomorphism(a, r, fast=False) == pos_5_coeffs assert field_isomorphism(a, s, fast=False) == neg_1_coeffs a = AlgebraicNumber(sqrt(2)) b = AlgebraicNumber(sqrt(3)) c = AlgebraicNumber(sqrt(7)) assert is_isomorphism_possible(a, b) is True assert is_isomorphism_possible(b, a) is True assert is_isomorphism_possible(c, p) is False assert field_isomorphism(sqrt(2), sqrt(3), fast=True) is None assert field_isomorphism(sqrt(3), sqrt(2), fast=True) is None assert field_isomorphism(sqrt(2), sqrt(3), fast=False) is None assert field_isomorphism(sqrt(3), sqrt(2), fast=False) is None def test_to_number_field(): assert to_number_field(sqrt(2)) == AlgebraicNumber(sqrt(2)) assert to_number_field( [sqrt(2), sqrt(3)]) == AlgebraicNumber(sqrt(2) + sqrt(3)) a = AlgebraicNumber(sqrt(2) + sqrt(3), [S(1)/2, S(0), -S(9)/2, S(0)]) assert to_number_field(sqrt(2), sqrt(2) + sqrt(3)) == a assert to_number_field(sqrt(2), AlgebraicNumber(sqrt(2) + sqrt(3))) == a raises(IsomorphismFailed, lambda: to_number_field(sqrt(2), sqrt(3))) def test_AlgebraicNumber(): minpoly, root = x**2 - 2, sqrt(2) a = AlgebraicNumber(root, gen=x) assert a.rep == DMP([QQ(1), QQ(0)], QQ) assert a.root == root assert a.alias is None assert a.minpoly == minpoly assert a.is_aliased is False assert a.coeffs() == [S(1), S(0)] assert a.native_coeffs() == [QQ(1), QQ(0)] a = AlgebraicNumber(root, gen=x, alias='y') assert a.rep == DMP([QQ(1), QQ(0)], QQ) assert a.root == root assert a.alias == Symbol('y') assert a.minpoly == minpoly assert a.is_aliased is True a = AlgebraicNumber(root, gen=x, alias=Symbol('y')) assert a.rep == DMP([QQ(1), QQ(0)], QQ) assert a.root == root assert a.alias == Symbol('y') assert a.minpoly == minpoly assert a.is_aliased is True assert AlgebraicNumber(sqrt(2), []).rep == DMP([], QQ) assert AlgebraicNumber(sqrt(2), [8]).rep == DMP([QQ(8)], QQ) assert AlgebraicNumber(sqrt(2), [S(8)/3]).rep == DMP([QQ(8, 3)], QQ) assert AlgebraicNumber(sqrt(2), [7, 3]).rep == DMP([QQ(7), QQ(3)], QQ) assert AlgebraicNumber( sqrt(2), [S(7)/9, S(3)/2]).rep == DMP([QQ(7, 9), QQ(3, 2)], QQ) assert AlgebraicNumber(sqrt(2), [1, 2, 3]).rep == DMP([QQ(2), QQ(5)], QQ) a = AlgebraicNumber(AlgebraicNumber(root, gen=x), [1, 2]) assert a.rep == DMP([QQ(1), QQ(2)], QQ) assert a.root == root assert a.alias is None assert a.minpoly == minpoly assert a.is_aliased is False assert a.coeffs() == [S(1), S(2)] assert a.native_coeffs() == [QQ(1), QQ(2)] a = AlgebraicNumber((minpoly, root), [1, 2]) assert a.rep == DMP([QQ(1), QQ(2)], QQ) assert a.root == root assert a.alias is None assert a.minpoly == minpoly assert a.is_aliased is False a = AlgebraicNumber((Poly(minpoly), root), [1, 2]) assert a.rep == DMP([QQ(1), QQ(2)], QQ) assert a.root == root assert a.alias is None assert a.minpoly == minpoly assert a.is_aliased is False assert AlgebraicNumber( sqrt(3)).rep == DMP([ QQ(1), QQ(0)], QQ) assert AlgebraicNumber(-sqrt(3)).rep == DMP([-QQ(1), QQ(0)], QQ) a = AlgebraicNumber(sqrt(2)) b = AlgebraicNumber(sqrt(2)) assert a == b c = AlgebraicNumber(sqrt(2), gen=x) d = AlgebraicNumber(sqrt(2), gen=x) assert a == b assert a == c a = AlgebraicNumber(sqrt(2), [1, 2]) b = AlgebraicNumber(sqrt(2), [1, 3]) assert a != b and a != sqrt(2) + 3 assert (a == x) is False and (a != x) is True a = AlgebraicNumber(sqrt(2), [1, 0]) b = AlgebraicNumber(sqrt(2), [1, 0], alias=y) assert a.as_poly(x) == Poly(x) assert b.as_poly() == Poly(y) assert a.as_expr() == sqrt(2) assert a.as_expr(x) == x assert b.as_expr() == sqrt(2) assert b.as_expr(x) == x a = AlgebraicNumber(sqrt(2), [2, 3]) b = AlgebraicNumber(sqrt(2), [2, 3], alias=y) p = a.as_poly() assert p == Poly(2*p.gen + 3) assert a.as_poly(x) == Poly(2*x + 3) assert b.as_poly() == Poly(2*y + 3) assert a.as_expr() == 2*sqrt(2) + 3 assert a.as_expr(x) == 2*x + 3 assert b.as_expr() == 2*sqrt(2) + 3 assert b.as_expr(x) == 2*x + 3 a = AlgebraicNumber(sqrt(2)) b = to_number_field(sqrt(2)) assert a.args == b.args == (sqrt(2), Tuple()) b = AlgebraicNumber(sqrt(2), alias='alpha') assert b.args == (sqrt(2), Tuple(), Symbol('alpha')) a = AlgebraicNumber(sqrt(2), [1, 2, 3]) assert a.args == (sqrt(2), Tuple(1, 2, 3)) def test_to_algebraic_integer(): a = AlgebraicNumber(sqrt(3), gen=x).to_algebraic_integer() assert a.minpoly == x**2 - 3 assert a.root == sqrt(3) assert a.rep == DMP([QQ(1), QQ(0)], QQ) a = AlgebraicNumber(2*sqrt(3), gen=x).to_algebraic_integer() assert a.minpoly == x**2 - 12 assert a.root == 2*sqrt(3) assert a.rep == DMP([QQ(1), QQ(0)], QQ) a = AlgebraicNumber(sqrt(3)/2, gen=x).to_algebraic_integer() assert a.minpoly == x**2 - 12 assert a.root == 2*sqrt(3) assert a.rep == DMP([QQ(1), QQ(0)], QQ) a = AlgebraicNumber(sqrt(3)/2, [S(7)/19, 3], gen=x).to_algebraic_integer() assert a.minpoly == x**2 - 12 assert a.root == 2*sqrt(3) assert a.rep == DMP([QQ(7, 19), QQ(3)], QQ) def test_IntervalPrinter(): ip = IntervalPrinter() assert ip.doprint(x**Q(1, 3)) == "x**(mpi('1/3'))" assert ip.doprint(sqrt(x)) == "x**(mpi('1/2'))" def test_isolate(): assert isolate(1) == (1, 1) assert isolate(S(1)/2) == (S(1)/2, S(1)/2) assert isolate(sqrt(2)) == (1, 2) assert isolate(-sqrt(2)) == (-2, -1) assert isolate(sqrt(2), eps=S(1)/100) == (S(24)/17, S(17)/12) assert isolate(-sqrt(2), eps=S(1)/100) == (-S(17)/12, -S(24)/17) raises(NotImplementedError, lambda: isolate(I)) def test_minpoly_fraction_field(): assert minimal_polynomial(1/x, y) == -x*y + 1 assert minimal_polynomial(1 / (x + 1), y) == (x + 1)*y - 1 assert minimal_polynomial(sqrt(x), y) == y**2 - x assert minimal_polynomial(sqrt(x + 1), y) == y**2 - x - 1 assert minimal_polynomial(sqrt(x) / x, y) == x*y**2 - 1 assert minimal_polynomial(sqrt(2) * sqrt(x), y) == y**2 - 2 * x assert minimal_polynomial(sqrt(2) + sqrt(x), y) == \ y**4 + (-2*x - 4)*y**2 + x**2 - 4*x + 4 assert minimal_polynomial(x**Rational(1,3), y) == y**3 - x assert minimal_polynomial(x**Rational(1,3) + sqrt(x), y) == \ y**6 - 3*x*y**4 - 2*x*y**3 + 3*x**2*y**2 - 6*x**2*y - x**3 + x**2 assert minimal_polynomial(sqrt(x) / z, y) == z**2*y**2 - x assert minimal_polynomial(sqrt(x) / (z + 1), y) == (z**2 + 2*z + 1)*y**2 - x assert minimal_polynomial(1/x, y, polys=True) == Poly(-x*y + 1, y) assert minimal_polynomial(1 / (x + 1), y, polys=True) == \ Poly((x + 1)*y - 1, y) assert minimal_polynomial(sqrt(x), y, polys=True) == Poly(y**2 - x, y) assert minimal_polynomial(sqrt(x) / z, y, polys=True) == \ Poly(z**2*y**2 - x, y) # this is (sqrt(1 + x**3)/x).integrate(x).diff(x) - sqrt(1 + x**3)/x a = sqrt(x)/sqrt(1 + x**(-3)) - sqrt(x**3 + 1)/x + 1/(x**(S(5)/2)* \ (1 + x**(-3))**(S(3)/2)) + 1/(x**(S(11)/2)*(1 + x**(-3))**(S(3)/2)) assert minimal_polynomial(a, y) == y raises(NotAlgebraic, lambda: minimal_polynomial(exp(x), y)) raises(GeneratorsError, lambda: minimal_polynomial(sqrt(x), x)) raises(GeneratorsError, lambda: minimal_polynomial(sqrt(x) - y, x)) raises(NotImplementedError, lambda: minimal_polynomial(sqrt(x), y, compose=False)) @slow def test_minpoly_fraction_field_slow(): assert minimal_polynomial(minimal_polynomial(sqrt(x**Rational(1,5) - 1), y).subs(y, sqrt(x**Rational(1,5) - 1)), z) == z def test_minpoly_domain(): assert minimal_polynomial(sqrt(2), x, domain=QQ.algebraic_field(sqrt(2))) == \ x - sqrt(2) assert minimal_polynomial(sqrt(8), x, domain=QQ.algebraic_field(sqrt(2))) == \ x - 2*sqrt(2) assert minimal_polynomial(sqrt(Rational(3,2)), x, domain=QQ.algebraic_field(sqrt(2))) == 2*x**2 - 3 raises(NotAlgebraic, lambda: minimal_polynomial(y, x, domain=QQ)) sympy-0.7.4.1/sympy/polys/tests/test_distributedmodules.py0000644000175000017500000001674412253362407024275 0ustar georgeskgeorgesk"""Tests for sparse distributed modules. """ from sympy.polys.distributedmodules import ( sdm_monomial_mul, sdm_monomial_deg, sdm_monomial_divides, sdm_add, sdm_LM, sdm_LT, sdm_mul_term, sdm_zero, sdm_deg, sdm_LC, sdm_from_dict, sdm_to_dict, sdm_spoly, sdm_ecart, sdm_nf_mora, sdm_groebner, sdm_from_vector, sdm_to_vector, sdm_monomial_lcm ) from sympy.polys.orderings import lex, grlex, InverseOrder from sympy.polys.domains import QQ from sympy.abc import x, y, z def test_sdm_monomial_mul(): assert sdm_monomial_mul((1, 1, 0), (1, 3)) == (1, 2, 3) def test_sdm_monomial_deg(): assert sdm_monomial_deg((5, 2, 1)) == 3 def test_sdm_monomial_lcm(): assert sdm_monomial_lcm((1, 2, 3), (1, 5, 0)) == (1, 5, 3) def test_sdm_monomial_divides(): assert sdm_monomial_divides((1, 0, 0), (1, 0, 0)) is True assert sdm_monomial_divides((1, 0, 0), (1, 2, 1)) is True assert sdm_monomial_divides((5, 1, 1), (5, 2, 1)) is True assert sdm_monomial_divides((1, 0, 0), (2, 0, 0)) is False assert sdm_monomial_divides((1, 1, 0), (1, 0, 0)) is False assert sdm_monomial_divides((5, 1, 2), (5, 0, 1)) is False def test_sdm_LC(): assert sdm_LC([((1, 2, 3), QQ(5))], QQ) == QQ(5) def test_sdm_from_dict(): dic = {(1, 2, 1, 1): QQ(1), (1, 1, 2, 1): QQ(1), (1, 0, 2, 1): QQ(1), (1, 0, 0, 3): QQ(1), (1, 1, 1, 0): QQ(1)} assert sdm_from_dict(dic, grlex) == \ [((1, 2, 1, 1), QQ(1)), ((1, 1, 2, 1), QQ(1)), ((1, 0, 2, 1), QQ(1)), ((1, 0, 0, 3), QQ(1)), ((1, 1, 1, 0), QQ(1))] # TODO test to_dict? def test_sdm_add(): assert sdm_add([((1, 1, 1), QQ(1))], [((2, 0, 0), QQ(1))], lex, QQ) == \ [((2, 0, 0), QQ(1)), ((1, 1, 1), QQ(1))] assert sdm_add([((1, 1, 1), QQ(1))], [((1, 1, 1), QQ(-1))], lex, QQ) == [] assert sdm_add([((1, 0, 0), QQ(1))], [((1, 0, 0), QQ(2))], lex, QQ) == \ [((1, 0, 0), QQ(3))] assert sdm_add([((1, 0, 1), QQ(1))], [((1, 1, 0), QQ(1))], lex, QQ) == \ [((1, 1, 0), QQ(1)), ((1, 0, 1), QQ(1))] def test_sdm_LM(): dic = {(1, 2, 3): QQ(1), (4, 0, 0): QQ(1), (4, 0, 1): QQ(1)} assert sdm_LM(sdm_from_dict(dic, lex)) == (4, 0, 1) def test_sdm_LT(): dic = {(1, 2, 3): QQ(1), (4, 0, 0): QQ(2), (4, 0, 1): QQ(3)} assert sdm_LT(sdm_from_dict(dic, lex)) == ((4, 0, 1), QQ(3)) def test_sdm_mul_term(): assert sdm_mul_term([((1, 0, 0), QQ(1))], ((0, 0), QQ(0)), lex, QQ) == [] assert sdm_mul_term([], ((1, 0), QQ(1)), lex, QQ) == [] assert sdm_mul_term([((1, 0, 0), QQ(1))], ((1, 0), QQ(1)), lex, QQ) == \ [((1, 1, 0), QQ(1))] f = [((2, 0, 1), QQ(4)), ((1, 1, 0), QQ(3))] assert sdm_mul_term(f, ((1, 1), QQ(2)), lex, QQ) == \ [((2, 1, 2), QQ(8)), ((1, 2, 1), QQ(6))] def test_sdm_zero(): assert sdm_zero() == [] def test_sdm_deg(): assert sdm_deg([((1, 2, 3), 1), ((10, 0, 1), 1), ((2, 3, 4), 4)]) == 7 def test_sdm_spoly(): f = [((2, 1, 1), QQ(1)), ((1, 0, 1), QQ(1))] g = [((2, 3, 0), QQ(1))] h = [((1, 2, 3), QQ(1))] assert sdm_spoly(f, h, lex, QQ) == [] assert sdm_spoly(f, g, lex, QQ) == [((1, 2, 1), QQ(1))] def test_sdm_ecart(): assert sdm_ecart([((1, 2, 3), 1), ((1, 0, 1), 1)]) == 0 assert sdm_ecart([((2, 2, 1), 1), ((1, 5, 1), 1)]) == 3 def test_sdm_nf_mora(): f = sdm_from_dict({(1, 2, 1, 1): QQ(1), (1, 1, 2, 1): QQ(1), (1, 0, 2, 1): QQ(1), (1, 0, 0, 3): QQ(1), (1, 1, 1, 0): QQ(1)}, grlex) f1 = sdm_from_dict({(1, 1, 1, 0): QQ(1), (1, 0, 2, 0): QQ(1), (1, 0, 0, 0): QQ(-1)}, grlex) f2 = sdm_from_dict({(1, 1, 1, 0): QQ(1)}, grlex) (id0, id1, id2) = [sdm_from_dict({(i, 0, 0, 0): QQ(1)}, grlex) for i in range(3)] assert sdm_nf_mora(f, [f1, f2], grlex, QQ, phantom=(id0, [id1, id2])) == \ ([((1, 0, 2, 1), QQ(1)), ((1, 0, 0, 3), QQ(1)), ((1, 1, 1, 0), QQ(1)), ((1, 1, 0, 1), QQ(1))], [((1, 1, 0, 1), QQ(-1)), ((0, 0, 0, 0), QQ(1))]) assert sdm_nf_mora(f, [f2, f1], grlex, QQ, phantom=(id0, [id2, id1])) == \ ([((1, 0, 2, 1), QQ(1)), ((1, 0, 0, 3), QQ(1)), ((1, 1, 1, 0), QQ(1))], [((2, 1, 0, 1), QQ(-1)), ((2, 0, 1, 1), QQ(-1)), ((0, 0, 0, 0), QQ(1))]) f = sdm_from_vector([x*z, y**2 + y*z - z, y], lex, QQ, gens=[x, y, z]) f1 = sdm_from_vector([x, y, 1], lex, QQ, gens=[x, y, z]) f2 = sdm_from_vector([x*y, z, z**2], lex, QQ, gens=[x, y, z]) assert sdm_nf_mora(f, [f1, f2], lex, QQ) == \ sdm_nf_mora(f, [f2, f1], lex, QQ) == \ [((1, 0, 1, 1), QQ(1)), ((1, 0, 0, 1), QQ(-1)), ((0, 1, 1, 0), QQ(-1)), ((0, 1, 0, 1), QQ(1))] def test_conversion(): f = [x**2 + y**2, 2*z] g = [((1, 0, 0, 1), QQ(2)), ((0, 2, 0, 0), QQ(1)), ((0, 0, 2, 0), QQ(1))] assert sdm_to_vector(g, [x, y, z], QQ) == f assert sdm_from_vector(f, lex, QQ) == g assert sdm_from_vector( [x, 1], lex, QQ) == [((1, 0), QQ(1)), ((0, 1), QQ(1))] assert sdm_to_vector([((1, 1, 0, 0), 1)], [x, y, z], QQ, n=3) == [0, x, 0] assert sdm_from_vector([0, 0], lex, QQ, gens=[x, y]) == sdm_zero() def test_nontrivial(): gens = [x, y, z] def contains(I, f): S = [sdm_from_vector([g], lex, QQ, gens=gens) for g in I] G = sdm_groebner(S, sdm_nf_mora, lex, QQ) return sdm_nf_mora(sdm_from_vector([f], lex, QQ, gens=gens), G, lex, QQ) == sdm_zero() assert contains([x, y], x) assert contains([x, y], x + y) assert not contains([x, y], 1) assert not contains([x, y], z) assert contains([x**2 + y, x**2 + x], x - y) assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x**2) assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**3) assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**4) assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x*y**2) assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**4 + y**3 + 2*z*y*x) assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x*y*z) assert contains([x, 1 + x + y, 5 - 7*y], 1) assert contains( [x**3 + y**3, y**3 + z**3, z**3 + x**3, x**2*y + x**2*z + y**2*z], x**3) assert not contains( [x**3 + y**3, y**3 + z**3, z**3 + x**3, x**2*y + x**2*z + y**2*z], x**2 + y**2) # compare local order assert not contains([x*(1 + x + y), y*(1 + z)], x) assert not contains([x*(1 + x + y), y*(1 + z)], x + y) def test_local(): igrlex = InverseOrder(grlex) gens = [x, y, z] def contains(I, f): S = [sdm_from_vector([g], igrlex, QQ, gens=gens) for g in I] G = sdm_groebner(S, sdm_nf_mora, igrlex, QQ) return sdm_nf_mora(sdm_from_vector([f], lex, QQ, gens=gens), G, lex, QQ) == sdm_zero() assert contains([x, y], x) assert contains([x, y], x + y) assert not contains([x, y], 1) assert not contains([x, y], z) assert contains([x**2 + y, x**2 + x], x - y) assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x**2) assert contains([x*(1 + x + y), y*(1 + z)], x) assert contains([x*(1 + x + y), y*(1 + z)], x + y) def test_uncovered_line(): gens = [x, y] f1 = sdm_zero() f2 = sdm_from_vector([x, 0], lex, QQ, gens=gens) f3 = sdm_from_vector([0, y], lex, QQ, gens=gens) assert sdm_spoly(f1, f2, lex, QQ) == sdm_zero() assert sdm_spoly(f3, f2, lex, QQ) == sdm_zero() def test_chain_criterion(): gens = [x] f1 = sdm_from_vector([1, x], grlex, QQ, gens=gens) f2 = sdm_from_vector([0, x - 2], grlex, QQ, gens=gens) assert len(sdm_groebner([f1, f2], sdm_nf_mora, grlex, QQ)) == 2 sympy-0.7.4.1/sympy/polys/tests/test_sqfreetools.py0000644000175000017500000001044512253362407022720 0ustar georgeskgeorgesk"""Tests for square-free decomposition algorithms and related tools. """ from sympy.polys.rings import ring from sympy.polys.domains import FF, ZZ, QQ from sympy.polys.specialpolys import f_polys from sympy.utilities.pytest import raises f_0, f_1, f_2, f_3, f_4, f_5, f_6 = f_polys() def test_dup_sqf(): R, x = ring("x", ZZ) assert R.dup_sqf_part(0) == 0 assert R.dup_sqf_p(0) is True assert R.dup_sqf_part(7) == 1 assert R.dup_sqf_p(7) is True assert R.dup_sqf_part(2*x + 2) == x + 1 assert R.dup_sqf_p(2*x + 2) is True assert R.dup_sqf_part(x**3 + x + 1) == x**3 + x + 1 assert R.dup_sqf_p(x**3 + x + 1) is True assert R.dup_sqf_part(-x**3 + x + 1) == x**3 - x - 1 assert R.dup_sqf_p(-x**3 + x + 1) is True assert R.dup_sqf_part(2*x**3 + 3*x**2) == 2*x**2 + 3*x assert R.dup_sqf_p(2*x**3 + 3*x**2) is False assert R.dup_sqf_part(-2*x**3 + 3*x**2) == 2*x**2 - 3*x assert R.dup_sqf_p(-2*x**3 + 3*x**2) is False assert R.dup_sqf_list(0) == (0, []) assert R.dup_sqf_list(1) == (1, []) assert R.dup_sqf_list(x) == (1, [(x, 1)]) assert R.dup_sqf_list(2*x**2) == (2, [(x, 2)]) assert R.dup_sqf_list(3*x**3) == (3, [(x, 3)]) assert R.dup_sqf_list(-x**5 + x**4 + x - 1) == \ (-1, [(x**3 + x**2 + x + 1, 1), (x - 1, 2)]) assert R.dup_sqf_list(x**8 + 6*x**6 + 12*x**4 + 8*x**2) == \ ( 1, [(x, 2), (x**2 + 2, 3)]) assert R.dup_sqf_list(2*x**2 + 4*x + 2) == (2, [(x + 1, 2)]) R, x = ring("x", QQ) assert R.dup_sqf_list(2*x**2 + 4*x + 2) == (2, [(x + 1, 2)]) R, x = ring("x", FF(2)) assert R.dup_sqf_list(x**2 + 1) == (1, [(x + 1, 2)]) R, x = ring("x", FF(3)) assert R.dup_sqf_list(x**10 + 2*x**7 + 2*x**4 + x) == \ (1, [(x, 1), (x + 1, 3), (x + 2, 6)]) R1, x = ring("x", ZZ) R2, y = ring("y", FF(3)) f = x**3 + 1 g = y**3 + 1 assert R1.dup_sqf_part(f) == f assert R2.dup_sqf_part(g) == y + 1 assert R1.dup_sqf_p(f) is True assert R2.dup_sqf_p(g) is False R, x, y = ring("x,y", ZZ) A = x**4 - 3*x**2 + 6 D = x**6 - 5*x**4 + 5*x**2 + 4 f, g = D, R.dmp_sub(A, R.dmp_mul(R.dmp_diff(D, 1), y)) res = R.dmp_resultant(f, g) h = (4*y**2 + 1).drop(x) assert R.drop(x).dup_sqf_list(res) == (45796, [(h, 3)]) Rt, t = ring("t", ZZ) R, x = ring("x", Rt) assert R.dup_sqf_list_include(t**3*x**2) == [(t**3, 1), (x, 2)] def test_dmp_sqf(): R, x, y = ring("x,y", ZZ) assert R.dmp_sqf_part(0) == 0 assert R.dmp_sqf_p(0) is True assert R.dmp_sqf_part(7) == 1 assert R.dmp_sqf_p(7) is True assert R.dmp_sqf_list(3) == (3, []) assert R.dmp_sqf_list_include(3) == [(3, 1)] R, x, y, z = ring("x,y,z", ZZ) assert R.dmp_sqf_p(f_0) is True assert R.dmp_sqf_p(f_0**2) is False assert R.dmp_sqf_p(f_1) is True assert R.dmp_sqf_p(f_1**2) is False assert R.dmp_sqf_p(f_2) is True assert R.dmp_sqf_p(f_2**2) is False assert R.dmp_sqf_p(f_3) is True assert R.dmp_sqf_p(f_3**2) is False assert R.dmp_sqf_p(f_5) is False assert R.dmp_sqf_p(f_5**2) is False assert R.dmp_sqf_p(f_4) is True assert R.dmp_sqf_part(f_4) == -f_4 assert R.dmp_sqf_part(f_5) == x + y - z R, x, y, z, t = ring("x,y,z,t", ZZ) assert R.dmp_sqf_p(f_6) is True assert R.dmp_sqf_part(f_6) == f_6 R, x = ring("x", ZZ) f = -x**5 + x**4 + x - 1 assert R.dmp_sqf_list(f) == (-1, [(x**3 + x**2 + x + 1, 1), (x - 1, 2)]) assert R.dmp_sqf_list_include(f) == [(-x**3 - x**2 - x - 1, 1), (x - 1, 2)] R, x, y = ring("x,y", ZZ) f = -x**5 + x**4 + x - 1 assert R.dmp_sqf_list(f) == (-1, [(x**3 + x**2 + x + 1, 1), (x - 1, 2)]) assert R.dmp_sqf_list_include(f) == [(-x**3 - x**2 - x - 1, 1), (x - 1, 2)] f = -x**2 + 2*x - 1 assert R.dmp_sqf_list_include(f) == [(-1, 1), (x - 1, 2)] R, x, y = ring("x,y", FF(2)) raises(NotImplementedError, lambda: R.dmp_sqf_list(y**2 + 1)) def test_dup_gff_list(): R, x = ring("x", ZZ) f = x**5 + 2*x**4 - x**3 - 2*x**2 assert R.dup_gff_list(f) == [(x, 1), (x + 2, 4)] g = x**9 - 20*x**8 + 166*x**7 - 744*x**6 + 1965*x**5 - 3132*x**4 + 2948*x**3 - 1504*x**2 + 320*x assert R.dup_gff_list(g) == [(x**2 - 5*x + 4, 1), (x**2 - 5*x + 4, 2), (x, 3)] raises(ValueError, lambda: R.dup_gff_list(0)) sympy-0.7.4.1/sympy/polys/tests/test_densebasic.py0000644000175000017500000005202712253362407022454 0ustar georgeskgeorgesk"""Tests for dense recursive polynomials' basic tools. """ from sympy.polys.densebasic import ( dup_LC, dmp_LC, dup_TC, dmp_TC, dmp_ground_LC, dmp_ground_TC, dmp_true_LT, dup_degree, dmp_degree, dmp_degree_in, dmp_degree_list, dup_strip, dmp_strip, dmp_validate, dup_reverse, dup_copy, dmp_copy, dup_normal, dmp_normal, dup_convert, dmp_convert, dup_from_sympy, dmp_from_sympy, dup_nth, dmp_nth, dmp_ground_nth, dmp_zero_p, dmp_zero, dmp_one_p, dmp_one, dmp_ground_p, dmp_ground, dmp_negative_p, dmp_positive_p, dmp_zeros, dmp_grounds, dup_from_dict, dup_from_raw_dict, dup_to_dict, dup_to_raw_dict, dmp_from_dict, dmp_to_dict, dmp_swap, dmp_permute, dmp_nest, dmp_raise, dup_deflate, dmp_deflate, dup_multi_deflate, dmp_multi_deflate, dup_inflate, dmp_inflate, dmp_exclude, dmp_include, dmp_inject, dmp_eject, dup_terms_gcd, dmp_terms_gcd, dmp_list_terms, dmp_apply_pairs, dup_slice, dmp_slice, dmp_slice_in, dup_random, ) from sympy.polys.specialpolys import f_polys from sympy.polys.polyclasses import DMP from sympy.polys.domains import ZZ, QQ from sympy.polys.rings import ring from sympy.core.singleton import S from sympy.utilities.pytest import raises from sympy import oo f_0, f_1, f_2, f_3, f_4, f_5, f_6 = [ f.to_dense() for f in f_polys() ] def test_dup_LC(): assert dup_LC([], ZZ) == 0 assert dup_LC([2, 3, 4, 5], ZZ) == 2 def test_dup_TC(): assert dup_TC([], ZZ) == 0 assert dup_TC([2, 3, 4, 5], ZZ) == 5 def test_dmp_LC(): assert dmp_LC([[]], ZZ) == [] assert dmp_LC([[2, 3, 4], [5]], ZZ) == [2, 3, 4] assert dmp_LC([[[]]], ZZ) == [[]] assert dmp_LC([[[2], [3, 4]], [[5]]], ZZ) == [[2], [3, 4]] def test_dmp_TC(): assert dmp_TC([[]], ZZ) == [] assert dmp_TC([[2, 3, 4], [5]], ZZ) == [5] assert dmp_TC([[[]]], ZZ) == [[]] assert dmp_TC([[[2], [3, 4]], [[5]]], ZZ) == [[5]] def test_dmp_ground_LC(): assert dmp_ground_LC([[]], 1, ZZ) == 0 assert dmp_ground_LC([[2, 3, 4], [5]], 1, ZZ) == 2 assert dmp_ground_LC([[[]]], 2, ZZ) == 0 assert dmp_ground_LC([[[2], [3, 4]], [[5]]], 2, ZZ) == 2 def test_dmp_ground_TC(): assert dmp_ground_TC([[]], 1, ZZ) == 0 assert dmp_ground_TC([[2, 3, 4], [5]], 1, ZZ) == 5 assert dmp_ground_TC([[[]]], 2, ZZ) == 0 assert dmp_ground_TC([[[2], [3, 4]], [[5]]], 2, ZZ) == 5 def test_dmp_true_LT(): assert dmp_true_LT([[]], 1, ZZ) == ((0, 0), 0) assert dmp_true_LT([[7]], 1, ZZ) == ((0, 0), 7) assert dmp_true_LT([[1, 0]], 1, ZZ) == ((0, 1), 1) assert dmp_true_LT([[1], []], 1, ZZ) == ((1, 0), 1) assert dmp_true_LT([[1, 0], []], 1, ZZ) == ((1, 1), 1) def test_dup_degree(): assert dup_degree([]) == -oo assert dup_degree([1]) == 0 assert dup_degree([1, 0]) == 1 assert dup_degree([1, 0, 0, 0, 1]) == 4 def test_dmp_degree(): assert dmp_degree([[]], 1) == -oo assert dmp_degree([[[]]], 2) == -oo assert dmp_degree([[1]], 1) == 0 assert dmp_degree([[2], [1]], 1) == 1 def test_dmp_degree_in(): assert dmp_degree_in([[[]]], 0, 2) == -oo assert dmp_degree_in([[[]]], 1, 2) == -oo assert dmp_degree_in([[[]]], 2, 2) == -oo assert dmp_degree_in([[[1]]], 0, 2) == 0 assert dmp_degree_in([[[1]]], 1, 2) == 0 assert dmp_degree_in([[[1]]], 2, 2) == 0 assert dmp_degree_in(f_4, 0, 2) == 9 assert dmp_degree_in(f_4, 1, 2) == 12 assert dmp_degree_in(f_4, 2, 2) == 8 assert dmp_degree_in(f_6, 0, 2) == 4 assert dmp_degree_in(f_6, 1, 2) == 4 assert dmp_degree_in(f_6, 2, 2) == 6 assert dmp_degree_in(f_6, 3, 3) == 3 raises(IndexError, lambda: dmp_degree_in([[1]], -5, 1)) def test_dmp_degree_list(): assert dmp_degree_list([[[[ ]]]], 3) == (-oo, -oo, -oo, -oo) assert dmp_degree_list([[[[1]]]], 3) == ( 0, 0, 0, 0) assert dmp_degree_list(f_0, 2) == (2, 2, 2) assert dmp_degree_list(f_1, 2) == (3, 3, 3) assert dmp_degree_list(f_2, 2) == (5, 3, 3) assert dmp_degree_list(f_3, 2) == (5, 4, 7) assert dmp_degree_list(f_4, 2) == (9, 12, 8) assert dmp_degree_list(f_5, 2) == (3, 3, 3) assert dmp_degree_list(f_6, 3) == (4, 4, 6, 3) def test_dup_strip(): assert dup_strip([]) == [] assert dup_strip([0]) == [] assert dup_strip([0, 0, 0]) == [] assert dup_strip([1]) == [1] assert dup_strip([0, 1]) == [1] assert dup_strip([0, 0, 0, 1]) == [1] assert dup_strip([1, 2, 0]) == [1, 2, 0] assert dup_strip([0, 1, 2, 0]) == [1, 2, 0] assert dup_strip([0, 0, 0, 1, 2, 0]) == [1, 2, 0] def test_dmp_strip(): assert dmp_strip([0, 1, 0], 0) == [1, 0] assert dmp_strip([[]], 1) == [[]] assert dmp_strip([[], []], 1) == [[]] assert dmp_strip([[], [], []], 1) == [[]] assert dmp_strip([[[]]], 2) == [[[]]] assert dmp_strip([[[]], [[]]], 2) == [[[]]] assert dmp_strip([[[]], [[]], [[]]], 2) == [[[]]] assert dmp_strip([[[1]]], 2) == [[[1]]] assert dmp_strip([[[]], [[1]]], 2) == [[[1]]] assert dmp_strip([[[]], [[1]], [[]]], 2) == [[[1]], [[]]] def test_dmp_validate(): assert dmp_validate([]) == ([], 0) assert dmp_validate([0, 0, 0, 1, 0]) == ([1, 0], 0) assert dmp_validate([[[]]]) == ([[[]]], 2) assert dmp_validate([[0], [], [0], [1], [0]]) == ([[1], []], 1) raises(ValueError, lambda: dmp_validate([[0], 0, [0], [1], [0]])) def test_dup_reverse(): assert dup_reverse([1, 2, 0, 3]) == [3, 0, 2, 1] assert dup_reverse([1, 2, 3, 0]) == [3, 2, 1] def test_dup_copy(): f = [ZZ(1), ZZ(0), ZZ(2)] g = dup_copy(f) g[0], g[2] = ZZ(7), ZZ(0) assert f != g def test_dmp_copy(): f = [[ZZ(1)], [ZZ(2), ZZ(0)]] g = dmp_copy(f, 1) g[0][0], g[1][1] = ZZ(7), ZZ(1) assert f != g def test_dup_normal(): assert dup_normal([0, 0, 2, 1, 0, 11, 0], ZZ) == \ [ZZ(2), ZZ(1), ZZ(0), ZZ(11), ZZ(0)] def test_dmp_normal(): assert dmp_normal([[0], [], [0, 2, 1], [0], [11], []], 1, ZZ) == \ [[ZZ(2), ZZ(1)], [], [ZZ(11)], []] def test_dup_convert(): K0, K1 = ZZ['x'], ZZ f = [K0(1), K0(2), K0(0), K0(3)] assert dup_convert(f, K0, K1) == \ [ZZ(1), ZZ(2), ZZ(0), ZZ(3)] def test_dmp_convert(): K0, K1 = ZZ['x'], ZZ f = [[K0(1)], [K0(2)], [], [K0(3)]] assert dmp_convert(f, 1, K0, K1) == \ [[ZZ(1)], [ZZ(2)], [], [ZZ(3)]] def test_dup_from_sympy(): assert dup_from_sympy([S(1), S(2)], ZZ) == \ [ZZ(1), ZZ(2)] assert dup_from_sympy([S(1)/2, S(3)], QQ) == \ [QQ(1, 2), QQ(3, 1)] def test_dmp_from_sympy(): assert dmp_from_sympy([[S(1), S(2)], [S(0)]], 1, ZZ) == \ [[ZZ(1), ZZ(2)], []] assert dmp_from_sympy([[S(1)/2, S(2)]], 1, QQ) == \ [[QQ(1, 2), QQ(2, 1)]] def test_dup_nth(): assert dup_nth([1, 2, 3], 0, ZZ) == 3 assert dup_nth([1, 2, 3], 1, ZZ) == 2 assert dup_nth([1, 2, 3], 2, ZZ) == 1 assert dup_nth([1, 2, 3], 9, ZZ) == 0 raises(IndexError, lambda: dup_nth([3, 4, 5], -1, ZZ)) def test_dmp_nth(): assert dmp_nth([[1], [2], [3]], 0, 1, ZZ) == [3] assert dmp_nth([[1], [2], [3]], 1, 1, ZZ) == [2] assert dmp_nth([[1], [2], [3]], 2, 1, ZZ) == [1] assert dmp_nth([[1], [2], [3]], 9, 1, ZZ) == [] raises(IndexError, lambda: dmp_nth([[3], [4], [5]], -1, 1, ZZ)) def test_dmp_ground_nth(): assert dmp_ground_nth([[]], (0, 0), 1, ZZ) == 0 assert dmp_ground_nth([[1], [2], [3]], (0, 0), 1, ZZ) == 3 assert dmp_ground_nth([[1], [2], [3]], (1, 0), 1, ZZ) == 2 assert dmp_ground_nth([[1], [2], [3]], (2, 0), 1, ZZ) == 1 assert dmp_ground_nth([[1], [2], [3]], (2, 1), 1, ZZ) == 0 assert dmp_ground_nth([[1], [2], [3]], (3, 0), 1, ZZ) == 0 raises(IndexError, lambda: dmp_ground_nth([[3], [4], [5]], (2, -1), 1, ZZ)) def test_dmp_zero_p(): assert dmp_zero_p([], 0) is True assert dmp_zero_p([[]], 1) is True assert dmp_zero_p([[[]]], 2) is True assert dmp_zero_p([[[1]]], 2) is False def test_dmp_zero(): assert dmp_zero(0) == [] assert dmp_zero(2) == [[[]]] def test_dmp_one_p(): assert dmp_one_p([1], 0, ZZ) is True assert dmp_one_p([[1]], 1, ZZ) is True assert dmp_one_p([[[1]]], 2, ZZ) is True assert dmp_one_p([[[12]]], 2, ZZ) is False def test_dmp_one(): assert dmp_one(0, ZZ) == [ZZ(1)] assert dmp_one(2, ZZ) == [[[ZZ(1)]]] def test_dmp_ground_p(): assert dmp_ground_p([], 0, 0) is True assert dmp_ground_p([[]], 0, 1) is True assert dmp_ground_p([[]], 1, 1) is False assert dmp_ground_p([[ZZ(1)]], 1, 1) is True assert dmp_ground_p([[[ZZ(2)]]], 2, 2) is True assert dmp_ground_p([[[ZZ(2)]]], 3, 2) is False assert dmp_ground_p([[[ZZ(3)], []]], 3, 2) is False assert dmp_ground_p([], None, 0) is True assert dmp_ground_p([[]], None, 1) is True assert dmp_ground_p([ZZ(1)], None, 0) is True assert dmp_ground_p([[[ZZ(1)]]], None, 2) is True assert dmp_ground_p([[[ZZ(3)], []]], None, 2) is False def test_dmp_ground(): assert dmp_ground(ZZ(0), 2) == [[[]]] assert dmp_ground(ZZ(7), -1) == ZZ(7) assert dmp_ground(ZZ(7), 0) == [ZZ(7)] assert dmp_ground(ZZ(7), 2) == [[[ZZ(7)]]] def test_dmp_zeros(): assert dmp_zeros(4, 0, ZZ) == [[], [], [], []] assert dmp_zeros(0, 2, ZZ) == [] assert dmp_zeros(1, 2, ZZ) == [[[[]]]] assert dmp_zeros(2, 2, ZZ) == [[[[]]], [[[]]]] assert dmp_zeros(3, 2, ZZ) == [[[[]]], [[[]]], [[[]]]] assert dmp_zeros(3, -1, ZZ) == [0, 0, 0] def test_dmp_grounds(): assert dmp_grounds(ZZ(7), 0, 2) == [] assert dmp_grounds(ZZ(7), 1, 2) == [[[[7]]]] assert dmp_grounds(ZZ(7), 2, 2) == [[[[7]]], [[[7]]]] assert dmp_grounds(ZZ(7), 3, 2) == [[[[7]]], [[[7]]], [[[7]]]] assert dmp_grounds(ZZ(7), 3, -1) == [7, 7, 7] def test_dmp_negative_p(): assert dmp_negative_p([[[]]], 2, ZZ) is False assert dmp_negative_p([[[1], [2]]], 2, ZZ) is False assert dmp_negative_p([[[-1], [2]]], 2, ZZ) is True def test_dmp_positive_p(): assert dmp_positive_p([[[]]], 2, ZZ) is False assert dmp_positive_p([[[1], [2]]], 2, ZZ) is True assert dmp_positive_p([[[-1], [2]]], 2, ZZ) is False def test_dup_from_to_dict(): assert dup_from_raw_dict({}, ZZ) == [] assert dup_from_dict({}, ZZ) == [] assert dup_to_raw_dict([]) == {} assert dup_to_dict([]) == {} assert dup_to_raw_dict([], ZZ, zero=True) == {0: ZZ(0)} assert dup_to_dict([], ZZ, zero=True) == {(0,): ZZ(0)} f = [3, 0, 0, 2, 0, 0, 0, 0, 8] g = {8: 3, 5: 2, 0: 8} h = {(8,): 3, (5,): 2, (0,): 8} assert dup_from_raw_dict(g, ZZ) == f assert dup_from_dict(h, ZZ) == f assert dup_to_raw_dict(f) == g assert dup_to_dict(f) == h R, x,y = ring("x,y", ZZ) K = R.to_domain() f = [R(3), R(0), R(2), R(0), R(0), R(8)] g = {5: R(3), 3: R(2), 0: R(8)} h = {(5,): R(3), (3,): R(2), (0,): R(8)} assert dup_from_raw_dict(g, K) == f assert dup_from_dict(h, K) == f assert dup_to_raw_dict(f) == g assert dup_to_dict(f) == h def test_dmp_from_to_dict(): assert dmp_from_dict({}, 1, ZZ) == [[]] assert dmp_to_dict([[]], 1) == {} assert dmp_to_dict([], 0, ZZ, zero=True) == {(0,): ZZ(0)} assert dmp_to_dict([[]], 1, ZZ, zero=True) == {(0, 0): ZZ(0)} f = [[3], [], [], [2], [], [], [], [], [8]] g = {(8, 0): 3, (5, 0): 2, (0, 0): 8} assert dmp_from_dict(g, 1, ZZ) == f assert dmp_to_dict(f, 1) == g def test_dmp_swap(): f = dmp_normal([[1, 0, 0], [], [1, 0], [], [1]], 1, ZZ) g = dmp_normal([[1, 0, 0, 0, 0], [1, 0, 0], [1]], 1, ZZ) assert dmp_swap(f, 1, 1, 1, ZZ) == f assert dmp_swap(f, 0, 1, 1, ZZ) == g assert dmp_swap(g, 0, 1, 1, ZZ) == f raises(IndexError, lambda: dmp_swap(f, -1, -7, 1, ZZ)) def test_dmp_permute(): f = dmp_normal([[1, 0, 0], [], [1, 0], [], [1]], 1, ZZ) g = dmp_normal([[1, 0, 0, 0, 0], [1, 0, 0], [1]], 1, ZZ) assert dmp_permute(f, [0, 1], 1, ZZ) == f assert dmp_permute(g, [0, 1], 1, ZZ) == g assert dmp_permute(f, [1, 0], 1, ZZ) == g assert dmp_permute(g, [1, 0], 1, ZZ) == f def test_dmp_nest(): assert dmp_nest(ZZ(1), 2, ZZ) == [[[1]]] assert dmp_nest([[1]], 0, ZZ) == [[1]] assert dmp_nest([[1]], 1, ZZ) == [[[1]]] assert dmp_nest([[1]], 2, ZZ) == [[[[1]]]] def test_dmp_raise(): assert dmp_raise([], 2, 0, ZZ) == [[[]]] assert dmp_raise([[1]], 0, 1, ZZ) == [[1]] assert dmp_raise([[1, 2, 3], [], [2, 3]], 2, 1, ZZ) == \ [[[[1]], [[2]], [[3]]], [[[]]], [[[2]], [[3]]]] def test_dup_deflate(): assert dup_deflate([], ZZ) == (1, []) assert dup_deflate([2], ZZ) == (1, [2]) assert dup_deflate([1, 2, 3], ZZ) == (1, [1, 2, 3]) assert dup_deflate([1, 0, 2, 0, 3], ZZ) == (2, [1, 2, 3]) assert dup_deflate(dup_from_raw_dict({7: 1, 1: 1}, ZZ), ZZ) == \ (1, [1, 0, 0, 0, 0, 0, 1, 0]) assert dup_deflate(dup_from_raw_dict({7: 1, 0: 1}, ZZ), ZZ) == \ (7, [1, 1]) assert dup_deflate(dup_from_raw_dict({7: 1, 3: 1}, ZZ), ZZ) == \ (1, [1, 0, 0, 0, 1, 0, 0, 0]) assert dup_deflate(dup_from_raw_dict({7: 1, 4: 1}, ZZ), ZZ) == \ (1, [1, 0, 0, 1, 0, 0, 0, 0]) assert dup_deflate(dup_from_raw_dict({8: 1, 4: 1}, ZZ), ZZ) == \ (4, [1, 1, 0]) assert dup_deflate(dup_from_raw_dict({8: 1}, ZZ), ZZ) == \ (8, [1, 0]) assert dup_deflate(dup_from_raw_dict({7: 1}, ZZ), ZZ) == \ (7, [1, 0]) assert dup_deflate(dup_from_raw_dict({1: 1}, ZZ), ZZ) == \ (1, [1, 0]) def test_dmp_deflate(): assert dmp_deflate([[]], 1, ZZ) == ((1, 1), [[]]) assert dmp_deflate([[2]], 1, ZZ) == ((1, 1), [[2]]) f = [[1, 0, 0], [], [1, 0], [], [1]] assert dmp_deflate(f, 1, ZZ) == ((2, 1), [[1, 0, 0], [1, 0], [1]]) def test_dup_multi_deflate(): assert dup_multi_deflate(([2],), ZZ) == (1, ([2],)) assert dup_multi_deflate(([], []), ZZ) == (1, ([], [])) assert dup_multi_deflate(([1, 2, 3],), ZZ) == (1, ([1, 2, 3],)) assert dup_multi_deflate(([1, 0, 2, 0, 3],), ZZ) == (2, ([1, 2, 3],)) assert dup_multi_deflate(([1, 0, 2, 0, 3], [2, 0, 0]), ZZ) == \ (2, ([1, 2, 3], [2, 0])) assert dup_multi_deflate(([1, 0, 2, 0, 3], [2, 1, 0]), ZZ) == \ (1, ([1, 0, 2, 0, 3], [2, 1, 0])) def test_dmp_multi_deflate(): assert dmp_multi_deflate(([[]],), 1, ZZ) == \ ((1, 1), ([[]],)) assert dmp_multi_deflate(([[]], [[]]), 1, ZZ) == \ ((1, 1), ([[]], [[]])) assert dmp_multi_deflate(([[1]], [[]]), 1, ZZ) == \ ((1, 1), ([[1]], [[]])) assert dmp_multi_deflate(([[1]], [[2]]), 1, ZZ) == \ ((1, 1), ([[1]], [[2]])) assert dmp_multi_deflate(([[1]], [[2, 0]]), 1, ZZ) == \ ((1, 1), ([[1]], [[2, 0]])) assert dmp_multi_deflate(([[2, 0]], [[2, 0]]), 1, ZZ) == \ ((1, 1), ([[2, 0]], [[2, 0]])) assert dmp_multi_deflate( ([[2]], [[2, 0, 0]]), 1, ZZ) == ((1, 2), ([[2]], [[2, 0]])) assert dmp_multi_deflate( ([[2, 0, 0]], [[2, 0, 0]]), 1, ZZ) == ((1, 2), ([[2, 0]], [[2, 0]])) assert dmp_multi_deflate(([2, 0, 0], [1, 0, 4, 0, 1]), 0, ZZ) == \ ((2,), ([2, 0], [1, 4, 1])) f = [[1, 0, 0], [], [1, 0], [], [1]] g = [[1, 0, 1, 0], [], [1]] assert dmp_multi_deflate((f,), 1, ZZ) == \ ((2, 1), ([[1, 0, 0], [1, 0], [1]],)) assert dmp_multi_deflate((f, g), 1, ZZ) == \ ((2, 1), ([[1, 0, 0], [1, 0], [1]], [[1, 0, 1, 0], [1]])) def test_dup_inflate(): assert dup_inflate([], 17, ZZ) == [] assert dup_inflate([1, 2, 3], 1, ZZ) == [1, 2, 3] assert dup_inflate([1, 2, 3], 2, ZZ) == [1, 0, 2, 0, 3] assert dup_inflate([1, 2, 3], 3, ZZ) == [1, 0, 0, 2, 0, 0, 3] assert dup_inflate([1, 2, 3], 4, ZZ) == [1, 0, 0, 0, 2, 0, 0, 0, 3] raises(IndexError, lambda: dup_inflate([1, 2, 3], 0, ZZ)) def test_dmp_inflate(): assert dmp_inflate([1], (3,), 0, ZZ) == [1] assert dmp_inflate([[]], (3, 7), 1, ZZ) == [[]] assert dmp_inflate([[2]], (1, 2), 1, ZZ) == [[2]] assert dmp_inflate([[2, 0]], (1, 1), 1, ZZ) == [[2, 0]] assert dmp_inflate([[2, 0]], (1, 2), 1, ZZ) == [[2, 0, 0]] assert dmp_inflate([[2, 0]], (1, 3), 1, ZZ) == [[2, 0, 0, 0]] assert dmp_inflate([[1, 0, 0], [1], [1, 0]], (2, 1), 1, ZZ) == \ [[1, 0, 0], [], [1], [], [1, 0]] raises(IndexError, lambda: dmp_inflate([[]], (-3, 7), 1, ZZ)) def test_dmp_exclude(): assert dmp_exclude([[[]]], 2, ZZ) == ([], [[[]]], 2) assert dmp_exclude([[[7]]], 2, ZZ) == ([], [[[7]]], 2) assert dmp_exclude([1, 2, 3], 0, ZZ) == ([], [1, 2, 3], 0) assert dmp_exclude([[1], [2, 3]], 1, ZZ) == ([], [[1], [2, 3]], 1) assert dmp_exclude([[1, 2, 3]], 1, ZZ) == ([0], [1, 2, 3], 0) assert dmp_exclude([[1], [2], [3]], 1, ZZ) == ([1], [1, 2, 3], 0) assert dmp_exclude([[[1, 2, 3]]], 2, ZZ) == ([0, 1], [1, 2, 3], 0) assert dmp_exclude([[[1]], [[2]], [[3]]], 2, ZZ) == ([1, 2], [1, 2, 3], 0) def test_dmp_include(): assert dmp_include([1, 2, 3], [], 0, ZZ) == [1, 2, 3] assert dmp_include([1, 2, 3], [0], 0, ZZ) == [[1, 2, 3]] assert dmp_include([1, 2, 3], [1], 0, ZZ) == [[1], [2], [3]] assert dmp_include([1, 2, 3], [0, 1], 0, ZZ) == [[[1, 2, 3]]] assert dmp_include([1, 2, 3], [1, 2], 0, ZZ) == [[[1]], [[2]], [[3]]] def test_dmp_inject(): R, x,y = ring("x,y", ZZ) K = R.to_domain() assert dmp_inject([], 0, K) == ([[[]]], 2) assert dmp_inject([[]], 1, K) == ([[[[]]]], 3) assert dmp_inject([R(1)], 0, K) == ([[[1]]], 2) assert dmp_inject([[R(1)]], 1, K) == ([[[[1]]]], 3) assert dmp_inject([R(1), 2*x + 3*y + 4], 0, K) == ([[[1]], [[2], [3, 4]]], 2) f = [3*x**2 + 7*x*y + 5*y**2, 2*x, R(0), x*y**2 + 11] g = [[[3], [7, 0], [5, 0, 0]], [[2], []], [[]], [[1, 0, 0], [11]]] assert dmp_inject(f, 0, K) == (g, 2) def test_dmp_eject(): R, x,y = ring("x,y", ZZ) K = R.to_domain() assert dmp_eject([[[]]], 2, K) == [] assert dmp_eject([[[[]]]], 3, K) == [[]] assert dmp_eject([[[1]]], 2, K) == [R(1)] assert dmp_eject([[[[1]]]], 3, K) == [[R(1)]] assert dmp_eject([[[1]], [[2], [3, 4]]], 2, K) == [R(1), 2*x + 3*y + 4] f = [3*x**2 + 7*x*y + 5*y**2, 2*x, R(0), x*y**2 + 11] g = [[[3], [7, 0], [5, 0, 0]], [[2], []], [[]], [[1, 0, 0], [11]]] assert dmp_eject(g, 2, K) == f def test_dup_terms_gcd(): assert dup_terms_gcd([], ZZ) == (0, []) assert dup_terms_gcd([1, 0, 1], ZZ) == (0, [1, 0, 1]) assert dup_terms_gcd([1, 0, 1, 0], ZZ) == (1, [1, 0, 1]) def test_dmp_terms_gcd(): assert dmp_terms_gcd([[]], 1, ZZ) == ((0, 0), [[]]) assert dmp_terms_gcd([1, 0, 1, 0], 0, ZZ) == ((1,), [1, 0, 1]) assert dmp_terms_gcd([[1], [], [1], []], 1, ZZ) == ((1, 0), [[1], [], [1]]) assert dmp_terms_gcd( [[1, 0], [], [1]], 1, ZZ) == ((0, 0), [[1, 0], [], [1]]) assert dmp_terms_gcd( [[1, 0], [1, 0, 0], [], []], 1, ZZ) == ((2, 1), [[1], [1, 0]]) def test_dmp_list_terms(): assert dmp_list_terms([[[]]], 2, ZZ) == [((0, 0, 0), 0)] assert dmp_list_terms([[[1]]], 2, ZZ) == [((0, 0, 0), 1)] assert dmp_list_terms([1, 2, 4, 3, 5], 0, ZZ) == \ [((4,), 1), ((3,), 2), ((2,), 4), ((1,), 3), ((0,), 5)] assert dmp_list_terms([[1], [2, 4], [3, 5, 0]], 1, ZZ) == \ [((2, 0), 1), ((1, 1), 2), ((1, 0), 4), ((0, 2), 3), ((0, 1), 5)] f = [[2, 0, 0, 0], [1, 0, 0], []] assert dmp_list_terms(f, 1, ZZ, order='lex') == [((2, 3), 2), ((1, 2), 1)] assert dmp_list_terms( f, 1, ZZ, order='grlex') == [((2, 3), 2), ((1, 2), 1)] f = [[2, 0, 0, 0], [1, 0, 0, 0, 0, 0], []] assert dmp_list_terms(f, 1, ZZ, order='lex') == [((2, 3), 2), ((1, 5), 1)] assert dmp_list_terms( f, 1, ZZ, order='grlex') == [((1, 5), 1), ((2, 3), 2)] def test_dmp_apply_pairs(): h = lambda a, b: a*b assert dmp_apply_pairs([1, 2, 3], [4, 5, 6], h, [], 0, ZZ) == [4, 10, 18] assert dmp_apply_pairs([2, 3], [4, 5, 6], h, [], 0, ZZ) == [10, 18] assert dmp_apply_pairs([1, 2, 3], [5, 6], h, [], 0, ZZ) == [10, 18] assert dmp_apply_pairs( [[1, 2], [3]], [[4, 5], [6]], h, [], 1, ZZ) == [[4, 10], [18]] assert dmp_apply_pairs( [[1, 2], [3]], [[4], [5, 6]], h, [], 1, ZZ) == [[8], [18]] assert dmp_apply_pairs( [[1], [2, 3]], [[4, 5], [6]], h, [], 1, ZZ) == [[5], [18]] def test_dup_slice(): f = [1, 2, 3, 4] assert dup_slice(f, 0, 0, ZZ) == [] assert dup_slice(f, 0, 1, ZZ) == [4] assert dup_slice(f, 0, 2, ZZ) == [3, 4] assert dup_slice(f, 0, 3, ZZ) == [2, 3, 4] assert dup_slice(f, 0, 4, ZZ) == [1, 2, 3, 4] assert dup_slice(f, 0, 4, ZZ) == f assert dup_slice(f, 0, 9, ZZ) == f assert dup_slice(f, 1, 0, ZZ) == [] assert dup_slice(f, 1, 1, ZZ) == [] assert dup_slice(f, 1, 2, ZZ) == [3, 0] assert dup_slice(f, 1, 3, ZZ) == [2, 3, 0] assert dup_slice(f, 1, 4, ZZ) == [1, 2, 3, 0] assert dup_slice([1, 2], 0, 3, ZZ) == [1, 2] def test_dup_random(): f = dup_random(0, -10, 10, ZZ) assert dup_degree(f) == 0 assert all(-10 <= c <= 10 for c in f) f = dup_random(1, -20, 20, ZZ) assert dup_degree(f) == 1 assert all(-20 <= c <= 20 for c in f) f = dup_random(2, -30, 30, ZZ) assert dup_degree(f) == 2 assert all(-30 <= c <= 30 for c in f) f = dup_random(3, -40, 40, ZZ) assert dup_degree(f) == 3 assert all(-40 <= c <= 40 for c in f) sympy-0.7.4.1/sympy/polys/tests/test_polytools.py0000644000175000017500000031476212253362407022427 0ustar georgeskgeorgesk"""Tests for user-friendly public interface to polynomial functions. """ from sympy.polys.polytools import ( Poly, PurePoly, poly, parallel_poly_from_expr, degree, degree_list, LC, LM, LT, pdiv, prem, pquo, pexquo, div, rem, quo, exquo, half_gcdex, gcdex, invert, subresultants, resultant, discriminant, terms_gcd, cofactors, gcd, gcd_list, lcm, lcm_list, trunc, monic, content, primitive, compose, decompose, sturm, gff_list, gff, sqf_norm, sqf_part, sqf_list, sqf, factor_list, factor, intervals, refine_root, count_roots, real_roots, nroots, ground_roots, nth_power_roots_poly, cancel, reduced, groebner, GroebnerBasis, is_zero_dimensional, _torational_factor_list) from sympy.polys.polyerrors import ( MultivariatePolynomialError, OperationNotSupported, ExactQuotientFailed, PolificationFailed, ComputationFailed, UnificationFailed, RefinementFailed, GeneratorsNeeded, GeneratorsError, PolynomialError, CoercionFailed, NotAlgebraic, DomainError, OptionError, FlagError) from sympy.polys.polyclasses import DMP from sympy.polys.fields import field from sympy.polys.domains import FF, ZZ, QQ, RR, EX from sympy.polys.orderings import lex, grlex, grevlex from sympy import ( S, Integer, Rational, Float, Mul, Symbol, symbols, sqrt, Piecewise, exp, sin, tanh, expand, oo, I, pi, re, im, RootOf, Eq, Tuple, Expr) from sympy.core.compatibility import iterable from sympy.core.mul import _keep_coeff from sympy.utilities.pytest import raises, XFAIL x, y, z, p, q, r, s, t, u, v, w, a, b, c, d, e = symbols( 'x,y,z,p,q,r,s,t,u,v,w,a,b,c,d,e') def _epsilon_eq(a, b): for x, y in zip(a, b): if abs(x - y) > 1e-10: return False return True def _strict_eq(a, b): if type(a) == type(b): if iterable(a): if len(a) == len(b): return all(_strict_eq(c, d) for c, d in zip(a, b)) else: return False else: return isinstance(a, Poly) and a.eq(b, strict=True) else: return False def test_Poly_from_dict(): K = FF(3) assert Poly.from_dict( {0: 1, 1: 2}, gens=x, domain=K).rep == DMP([K(2), K(1)], K) assert Poly.from_dict( {0: 1, 1: 5}, gens=x, domain=K).rep == DMP([K(2), K(1)], K) assert Poly.from_dict( {(0,): 1, (1,): 2}, gens=x, domain=K).rep == DMP([K(2), K(1)], K) assert Poly.from_dict( {(0,): 1, (1,): 5}, gens=x, domain=K).rep == DMP([K(2), K(1)], K) assert Poly.from_dict({(0, 0): 1, (1, 1): 2}, gens=( x, y), domain=K).rep == DMP([[K(2), K(0)], [K(1)]], K) assert Poly.from_dict({0: 1, 1: 2}, gens=x).rep == DMP([ZZ(2), ZZ(1)], ZZ) assert Poly.from_dict( {0: 1, 1: 2}, gens=x, field=True).rep == DMP([QQ(2), QQ(1)], QQ) assert Poly.from_dict( {0: 1, 1: 2}, gens=x, domain=ZZ).rep == DMP([ZZ(2), ZZ(1)], ZZ) assert Poly.from_dict( {0: 1, 1: 2}, gens=x, domain=QQ).rep == DMP([QQ(2), QQ(1)], QQ) assert Poly.from_dict( {(0,): 1, (1,): 2}, gens=x).rep == DMP([ZZ(2), ZZ(1)], ZZ) assert Poly.from_dict( {(0,): 1, (1,): 2}, gens=x, field=True).rep == DMP([QQ(2), QQ(1)], QQ) assert Poly.from_dict( {(0,): 1, (1,): 2}, gens=x, domain=ZZ).rep == DMP([ZZ(2), ZZ(1)], ZZ) assert Poly.from_dict( {(0,): 1, (1,): 2}, gens=x, domain=QQ).rep == DMP([QQ(2), QQ(1)], QQ) assert Poly.from_dict({(1,): sin(y)}, gens=x, composite=False) == \ Poly(sin(y)*x, x, domain='EX') assert Poly.from_dict({(1,): y}, gens=x, composite=False) == \ Poly(y*x, x, domain='EX') assert Poly.from_dict({(1, 1): 1}, gens=(x, y), composite=False) == \ Poly(x*y, x, y, domain='ZZ') assert Poly.from_dict({(1, 0): y}, gens=(x, z), composite=False) == \ Poly(y*x, x, z, domain='EX') def test_Poly_from_list(): K = FF(3) assert Poly.from_list([2, 1], gens=x, domain=K).rep == DMP([K(2), K(1)], K) assert Poly.from_list([5, 1], gens=x, domain=K).rep == DMP([K(2), K(1)], K) assert Poly.from_list([2, 1], gens=x).rep == DMP([ZZ(2), ZZ(1)], ZZ) assert Poly.from_list([2, 1], gens=x, field=True).rep == DMP([QQ(2), QQ(1)], QQ) assert Poly.from_list([2, 1], gens=x, domain=ZZ).rep == DMP([ZZ(2), ZZ(1)], ZZ) assert Poly.from_list([2, 1], gens=x, domain=QQ).rep == DMP([QQ(2), QQ(1)], QQ) assert Poly.from_list([0, 1.0], gens=x).rep == DMP([RR(1.0)], RR) assert Poly.from_list([1.0, 0], gens=x).rep == DMP([RR(1.0), RR(0.0)], RR) raises(MultivariatePolynomialError, lambda: Poly.from_list([[]], gens=(x, y))) def test_Poly_from_poly(): f = Poly(x + 7, x, domain=ZZ) g = Poly(x + 2, x, modulus=3) h = Poly(x + y, x, y, domain=ZZ) K = FF(3) assert Poly.from_poly(f) == f assert Poly.from_poly(f, domain=K).rep == DMP([K(1), K(1)], K) assert Poly.from_poly(f, domain=ZZ).rep == DMP([1, 7], ZZ) assert Poly.from_poly(f, domain=QQ).rep == DMP([1, 7], QQ) assert Poly.from_poly(f, gens=x) == f assert Poly.from_poly(f, gens=x, domain=K).rep == DMP([K(1), K(1)], K) assert Poly.from_poly(f, gens=x, domain=ZZ).rep == DMP([1, 7], ZZ) assert Poly.from_poly(f, gens=x, domain=QQ).rep == DMP([1, 7], QQ) assert Poly.from_poly(f, gens=y) == Poly(x + 7, y, domain='ZZ[x]') raises(CoercionFailed, lambda: Poly.from_poly(f, gens=y, domain=K)) raises(CoercionFailed, lambda: Poly.from_poly(f, gens=y, domain=ZZ)) raises(CoercionFailed, lambda: Poly.from_poly(f, gens=y, domain=QQ)) assert Poly.from_poly(f, gens=(x, y)) == Poly(x + 7, x, y, domain='ZZ') assert Poly.from_poly( f, gens=(x, y), domain=ZZ) == Poly(x + 7, x, y, domain='ZZ') assert Poly.from_poly( f, gens=(x, y), domain=QQ) == Poly(x + 7, x, y, domain='QQ') assert Poly.from_poly( f, gens=(x, y), modulus=3) == Poly(x + 7, x, y, domain='FF(3)') K = FF(2) assert Poly.from_poly(g) == g assert Poly.from_poly(g, domain=ZZ).rep == DMP([1, -1], ZZ) raises(CoercionFailed, lambda: Poly.from_poly(g, domain=QQ)) assert Poly.from_poly(g, domain=K).rep == DMP([K(1), K(0)], K) assert Poly.from_poly(g, gens=x) == g assert Poly.from_poly(g, gens=x, domain=ZZ).rep == DMP([1, -1], ZZ) raises(CoercionFailed, lambda: Poly.from_poly(g, gens=x, domain=QQ)) assert Poly.from_poly(g, gens=x, domain=K).rep == DMP([K(1), K(0)], K) K = FF(3) assert Poly.from_poly(h) == h assert Poly.from_poly( h, domain=ZZ).rep == DMP([[ZZ(1)], [ZZ(1), ZZ(0)]], ZZ) assert Poly.from_poly( h, domain=QQ).rep == DMP([[QQ(1)], [QQ(1), QQ(0)]], QQ) assert Poly.from_poly(h, domain=K).rep == DMP([[K(1)], [K(1), K(0)]], K) assert Poly.from_poly(h, gens=x) == Poly(x + y, x, domain=ZZ[y]) raises(CoercionFailed, lambda: Poly.from_poly(h, gens=x, domain=ZZ)) assert Poly.from_poly( h, gens=x, domain=ZZ[y]) == Poly(x + y, x, domain=ZZ[y]) raises(CoercionFailed, lambda: Poly.from_poly(h, gens=x, domain=QQ)) assert Poly.from_poly( h, gens=x, domain=QQ[y]) == Poly(x + y, x, domain=QQ[y]) raises(CoercionFailed, lambda: Poly.from_poly(h, gens=x, modulus=3)) assert Poly.from_poly(h, gens=y) == Poly(x + y, y, domain=ZZ[x]) raises(CoercionFailed, lambda: Poly.from_poly(h, gens=y, domain=ZZ)) assert Poly.from_poly( h, gens=y, domain=ZZ[x]) == Poly(x + y, y, domain=ZZ[x]) raises(CoercionFailed, lambda: Poly.from_poly(h, gens=y, domain=QQ)) assert Poly.from_poly( h, gens=y, domain=QQ[x]) == Poly(x + y, y, domain=QQ[x]) raises(CoercionFailed, lambda: Poly.from_poly(h, gens=y, modulus=3)) assert Poly.from_poly(h, gens=(x, y)) == h assert Poly.from_poly( h, gens=(x, y), domain=ZZ).rep == DMP([[ZZ(1)], [ZZ(1), ZZ(0)]], ZZ) assert Poly.from_poly( h, gens=(x, y), domain=QQ).rep == DMP([[QQ(1)], [QQ(1), QQ(0)]], QQ) assert Poly.from_poly( h, gens=(x, y), domain=K).rep == DMP([[K(1)], [K(1), K(0)]], K) assert Poly.from_poly( h, gens=(y, x)).rep == DMP([[ZZ(1)], [ZZ(1), ZZ(0)]], ZZ) assert Poly.from_poly( h, gens=(y, x), domain=ZZ).rep == DMP([[ZZ(1)], [ZZ(1), ZZ(0)]], ZZ) assert Poly.from_poly( h, gens=(y, x), domain=QQ).rep == DMP([[QQ(1)], [QQ(1), QQ(0)]], QQ) assert Poly.from_poly( h, gens=(y, x), domain=K).rep == DMP([[K(1)], [K(1), K(0)]], K) assert Poly.from_poly( h, gens=(x, y), field=True).rep == DMP([[QQ(1)], [QQ(1), QQ(0)]], QQ) assert Poly.from_poly( h, gens=(x, y), field=True).rep == DMP([[QQ(1)], [QQ(1), QQ(0)]], QQ) def test_Poly_from_expr(): raises(GeneratorsNeeded, lambda: Poly.from_expr(S(0))) raises(GeneratorsNeeded, lambda: Poly.from_expr(S(7))) F3 = FF(3) assert Poly.from_expr(x + 5, domain=F3).rep == DMP([F3(1), F3(2)], F3) assert Poly.from_expr(y + 5, domain=F3).rep == DMP([F3(1), F3(2)], F3) assert Poly.from_expr(x + 5, x, domain=F3).rep == DMP([F3(1), F3(2)], F3) assert Poly.from_expr(y + 5, y, domain=F3).rep == DMP([F3(1), F3(2)], F3) assert Poly.from_expr(x + y, domain=F3).rep == DMP([[F3(1)], [F3(1), F3(0)]], F3) assert Poly.from_expr(x + y, x, y, domain=F3).rep == DMP([[F3(1)], [F3(1), F3(0)]], F3) assert Poly.from_expr(x + 5).rep == DMP([1, 5], ZZ) assert Poly.from_expr(y + 5).rep == DMP([1, 5], ZZ) assert Poly.from_expr(x + 5, x).rep == DMP([1, 5], ZZ) assert Poly.from_expr(y + 5, y).rep == DMP([1, 5], ZZ) assert Poly.from_expr(x + 5, domain=ZZ).rep == DMP([1, 5], ZZ) assert Poly.from_expr(y + 5, domain=ZZ).rep == DMP([1, 5], ZZ) assert Poly.from_expr(x + 5, x, domain=ZZ).rep == DMP([1, 5], ZZ) assert Poly.from_expr(y + 5, y, domain=ZZ).rep == DMP([1, 5], ZZ) assert Poly.from_expr(x + 5, x, y, domain=ZZ).rep == DMP([[1], [5]], ZZ) assert Poly.from_expr(y + 5, x, y, domain=ZZ).rep == DMP([[1, 5]], ZZ) def test_Poly__new__(): raises(GeneratorsError, lambda: Poly(x + 1, x, x)) raises(GeneratorsError, lambda: Poly(x + y, x, y, domain=ZZ[x])) raises(GeneratorsError, lambda: Poly(x + y, x, y, domain=ZZ[y])) raises(OptionError, lambda: Poly(x, x, symmetric=True)) raises(OptionError, lambda: Poly(x + 2, x, modulus=3, domain=QQ)) raises(OptionError, lambda: Poly(x + 2, x, domain=ZZ, gaussian=True)) raises(OptionError, lambda: Poly(x + 2, x, modulus=3, gaussian=True)) raises(OptionError, lambda: Poly(x + 2, x, domain=ZZ, extension=[sqrt(3)])) raises(OptionError, lambda: Poly(x + 2, x, modulus=3, extension=[sqrt(3)])) raises(OptionError, lambda: Poly(x + 2, x, domain=ZZ, extension=True)) raises(OptionError, lambda: Poly(x + 2, x, modulus=3, extension=True)) raises(OptionError, lambda: Poly(x + 2, x, domain=ZZ, greedy=True)) raises(OptionError, lambda: Poly(x + 2, x, domain=QQ, field=True)) raises(OptionError, lambda: Poly(x + 2, x, domain=ZZ, greedy=False)) raises(OptionError, lambda: Poly(x + 2, x, domain=QQ, field=False)) raises(NotImplementedError, lambda: Poly(x + 1, x, modulus=3, order='grlex')) raises(NotImplementedError, lambda: Poly(x + 1, x, order='grlex')) raises(GeneratorsNeeded, lambda: Poly({1: 2, 0: 1})) raises(GeneratorsNeeded, lambda: Poly([2, 1])) raises(GeneratorsNeeded, lambda: Poly((2, 1))) raises(GeneratorsNeeded, lambda: Poly(1)) f = a*x**2 + b*x + c assert Poly({2: a, 1: b, 0: c}, x) == f assert Poly(iter([a, b, c]), x) == f assert Poly([a, b, c], x) == f assert Poly((a, b, c), x) == f f = Poly({}, x, y, z) assert f.gens == (x, y, z) and f.as_expr() == 0 assert Poly(Poly(a*x + b*y, x, y), x) == Poly(a*x + b*y, x) assert Poly(3*x**2 + 2*x + 1, domain='ZZ').all_coeffs() == [3, 2, 1] assert Poly(3*x**2 + 2*x + 1, domain='QQ').all_coeffs() == [3, 2, 1] assert Poly(3*x**2 + 2*x + 1, domain='RR').all_coeffs() == [3.0, 2.0, 1.0] raises(CoercionFailed, lambda: Poly(3*x**2/5 + 2*x/5 + 1, domain='ZZ')) assert Poly( 3*x**2/5 + 2*x/5 + 1, domain='QQ').all_coeffs() == [S(3)/5, S(2)/5, 1] assert _epsilon_eq( Poly(3*x**2/5 + 2*x/5 + 1, domain='RR').all_coeffs(), [0.6, 0.4, 1.0]) assert Poly(3.0*x**2 + 2.0*x + 1, domain='ZZ').all_coeffs() == [3, 2, 1] assert Poly(3.0*x**2 + 2.0*x + 1, domain='QQ').all_coeffs() == [3, 2, 1] assert Poly( 3.0*x**2 + 2.0*x + 1, domain='RR').all_coeffs() == [3.0, 2.0, 1.0] raises(CoercionFailed, lambda: Poly(3.1*x**2 + 2.1*x + 1, domain='ZZ')) assert Poly(3.1*x**2 + 2.1*x + 1, domain='QQ').all_coeffs() == [S(31)/10, S(21)/10, 1] assert Poly(3.1*x**2 + 2.1*x + 1, domain='RR').all_coeffs() == [3.1, 2.1, 1.0] assert Poly({(2, 1): 1, (1, 2): 2, (1, 1): 3}, x, y) == \ Poly(x**2*y + 2*x*y**2 + 3*x*y, x, y) assert Poly(x**2 + 1, extension=I).get_domain() == QQ.algebraic_field(I) f = 3*x**5 - x**4 + x**3 - x** 2 + 65538 assert Poly(f, x, modulus=65537, symmetric=True) == \ Poly(3*x**5 - x**4 + x**3 - x** 2 + 1, x, modulus=65537, symmetric=True) assert Poly(f, x, modulus=65537, symmetric=False) == \ Poly(3*x**5 + 65536*x**4 + x**3 + 65536*x** 2 + 1, x, modulus=65537, symmetric=False) assert Poly(x**2 + x + 1.0).get_domain() == RR def test_Poly__args(): assert Poly(x**2 + 1).args == (x**2 + 1,) def test_Poly__gens(): assert Poly((x - p)*(x - q), x).gens == (x,) assert Poly((x - p)*(x - q), p).gens == (p,) assert Poly((x - p)*(x - q), q).gens == (q,) assert Poly((x - p)*(x - q), x, p).gens == (x, p) assert Poly((x - p)*(x - q), x, q).gens == (x, q) assert Poly((x - p)*(x - q), x, p, q).gens == (x, p, q) assert Poly((x - p)*(x - q), p, x, q).gens == (p, x, q) assert Poly((x - p)*(x - q), p, q, x).gens == (p, q, x) assert Poly((x - p)*(x - q)).gens == (x, p, q) assert Poly((x - p)*(x - q), sort='x > p > q').gens == (x, p, q) assert Poly((x - p)*(x - q), sort='p > x > q').gens == (p, x, q) assert Poly((x - p)*(x - q), sort='p > q > x').gens == (p, q, x) assert Poly((x - p)*(x - q), x, p, q, sort='p > q > x').gens == (x, p, q) assert Poly((x - p)*(x - q), wrt='x').gens == (x, p, q) assert Poly((x - p)*(x - q), wrt='p').gens == (p, x, q) assert Poly((x - p)*(x - q), wrt='q').gens == (q, x, p) assert Poly((x - p)*(x - q), wrt=x).gens == (x, p, q) assert Poly((x - p)*(x - q), wrt=p).gens == (p, x, q) assert Poly((x - p)*(x - q), wrt=q).gens == (q, x, p) assert Poly((x - p)*(x - q), x, p, q, wrt='p').gens == (x, p, q) assert Poly((x - p)*(x - q), wrt='p', sort='q > x').gens == (p, q, x) assert Poly((x - p)*(x - q), wrt='q', sort='p > x').gens == (q, p, x) def test_Poly_zero(): assert Poly(x).zero == Poly(0, x, domain=ZZ) assert Poly(x/2).zero == Poly(0, x, domain=QQ) def test_Poly_one(): assert Poly(x).one == Poly(1, x, domain=ZZ) assert Poly(x/2).one == Poly(1, x, domain=QQ) def test_Poly__unify(): raises(UnificationFailed, lambda: Poly(x)._unify(y)) F3 = FF(3) F5 = FF(5) assert Poly(x, x, modulus=3)._unify(Poly(y, y, modulus=3))[2:] == ( DMP([[F3(1)], []], F3), DMP([[F3(1), F3(0)]], F3)) assert Poly(x, x, modulus=3)._unify(Poly(y, y, modulus=5))[2:] == ( DMP([[F5(1)], []], F5), DMP([[F5(1), F5(0)]], F5)) assert Poly(y, x, y)._unify(Poly(x, x, modulus=3))[2:] == (DMP([[F3(1), F3(0)]], F3), DMP([[F3(1)], []], F3)) assert Poly(x, x, modulus=3)._unify(Poly(y, x, y))[2:] == (DMP([[F3(1)], []], F3), DMP([[F3(1), F3(0)]], F3)) assert Poly(x + 1, x)._unify(Poly(x + 2, x))[2:] == (DMP([1, 1], ZZ), DMP([1, 2], ZZ)) assert Poly(x + 1, x, domain='QQ')._unify(Poly(x + 2, x))[2:] == (DMP([1, 1], QQ), DMP([1, 2], QQ)) assert Poly(x + 1, x)._unify(Poly(x + 2, x, domain='QQ'))[2:] == (DMP([1, 1], QQ), DMP([1, 2], QQ)) assert Poly(x + 1, x)._unify(Poly(x + 2, x, y))[2:] == (DMP([[1], [1]], ZZ), DMP([[1], [2]], ZZ)) assert Poly(x + 1, x, domain='QQ')._unify(Poly(x + 2, x, y))[2:] == (DMP([[1], [1]], QQ), DMP([[1], [2]], QQ)) assert Poly(x + 1, x)._unify(Poly(x + 2, x, y, domain='QQ'))[2:] == (DMP([[1], [1]], QQ), DMP([[1], [2]], QQ)) assert Poly(x + 1, x, y)._unify(Poly(x + 2, x))[2:] == (DMP([[1], [1]], ZZ), DMP([[1], [2]], ZZ)) assert Poly(x + 1, x, y, domain='QQ')._unify(Poly(x + 2, x))[2:] == (DMP([[1], [1]], QQ), DMP([[1], [2]], QQ)) assert Poly(x + 1, x, y)._unify(Poly(x + 2, x, domain='QQ'))[2:] == (DMP([[1], [1]], QQ), DMP([[1], [2]], QQ)) assert Poly(x + 1, x, y)._unify(Poly(x + 2, x, y))[2:] == (DMP([[1], [1]], ZZ), DMP([[1], [2]], ZZ)) assert Poly(x + 1, x, y, domain='QQ')._unify(Poly(x + 2, x, y))[2:] == (DMP([[1], [1]], QQ), DMP([[1], [2]], QQ)) assert Poly(x + 1, x, y)._unify(Poly(x + 2, x, y, domain='QQ'))[2:] == (DMP([[1], [1]], QQ), DMP([[1], [2]], QQ)) assert Poly(x + 1, x)._unify(Poly(x + 2, y, x))[2:] == (DMP([[1, 1]], ZZ), DMP([[1, 2]], ZZ)) assert Poly(x + 1, x, domain='QQ')._unify(Poly(x + 2, y, x))[2:] == (DMP([[1, 1]], QQ), DMP([[1, 2]], QQ)) assert Poly(x + 1, x)._unify(Poly(x + 2, y, x, domain='QQ'))[2:] == (DMP([[1, 1]], QQ), DMP([[1, 2]], QQ)) assert Poly(x + 1, y, x)._unify(Poly(x + 2, x))[2:] == (DMP([[1, 1]], ZZ), DMP([[1, 2]], ZZ)) assert Poly(x + 1, y, x, domain='QQ')._unify(Poly(x + 2, x))[2:] == (DMP([[1, 1]], QQ), DMP([[1, 2]], QQ)) assert Poly(x + 1, y, x)._unify(Poly(x + 2, x, domain='QQ'))[2:] == (DMP([[1, 1]], QQ), DMP([[1, 2]], QQ)) assert Poly(x + 1, x, y)._unify(Poly(x + 2, y, x))[2:] == (DMP([[1], [1]], ZZ), DMP([[1], [2]], ZZ)) assert Poly(x + 1, x, y, domain='QQ')._unify(Poly(x + 2, y, x))[2:] == (DMP([[1], [1]], QQ), DMP([[1], [2]], QQ)) assert Poly(x + 1, x, y)._unify(Poly(x + 2, y, x, domain='QQ'))[2:] == (DMP([[1], [1]], QQ), DMP([[1], [2]], QQ)) assert Poly(x + 1, y, x)._unify(Poly(x + 2, x, y))[2:] == (DMP([[1, 1]], ZZ), DMP([[1, 2]], ZZ)) assert Poly(x + 1, y, x, domain='QQ')._unify(Poly(x + 2, x, y))[2:] == (DMP([[1, 1]], QQ), DMP([[1, 2]], QQ)) assert Poly(x + 1, y, x)._unify(Poly(x + 2, x, y, domain='QQ'))[2:] == (DMP([[1, 1]], QQ), DMP([[1, 2]], QQ)) F, A, B = field("a,b", ZZ) assert Poly(a*x, x, domain='ZZ[a]')._unify(Poly(a*b*x, x, domain='ZZ(a,b)'))[2:] == \ (DMP([A, F(0)], F.to_domain()), DMP([A*B, F(0)], F.to_domain())) assert Poly(a*x, x, domain='ZZ(a)')._unify(Poly(a*b*x, x, domain='ZZ(a,b)'))[2:] == \ (DMP([A, F(0)], F.to_domain()), DMP([A*B, F(0)], F.to_domain())) raises(CoercionFailed, lambda: Poly(Poly(x**2 + x**2*z, y, field=True), domain='ZZ(x)')) f = Poly(t**2 + t/3 + x, t, domain='QQ(x)') g = Poly(t**2 + t/3 + x, t, domain='QQ[x]') assert f._unify(g)[2:] == (f.rep, f.rep) def test_Poly_free_symbols(): assert Poly(x**2 + 1).free_symbols == set([x]) assert Poly(x**2 + y*z).free_symbols == set([x, y, z]) assert Poly(x**2 + y*z, x).free_symbols == set([x, y, z]) assert Poly(x**2 + sin(y*z)).free_symbols == set([x, y, z]) assert Poly(x**2 + sin(y*z), x).free_symbols == set([x, y, z]) assert Poly(x**2 + sin(y*z), x, domain=EX).free_symbols == set([x, y, z]) def test_PurePoly_free_symbols(): assert PurePoly(x**2 + 1).free_symbols == set([]) assert PurePoly(x**2 + y*z).free_symbols == set([]) assert PurePoly(x**2 + y*z, x).free_symbols == set([y, z]) assert PurePoly(x**2 + sin(y*z)).free_symbols == set([]) assert PurePoly(x**2 + sin(y*z), x).free_symbols == set([y, z]) assert PurePoly(x**2 + sin(y*z), x, domain=EX).free_symbols == set([y, z]) def test_Poly__eq__(): assert (Poly(x, x) == Poly(x, x)) is True assert (Poly(x, x, domain=QQ) == Poly(x, x)) is True assert (Poly(x, x) == Poly(x, x, domain=QQ)) is True assert (Poly(x, x, domain=ZZ[a]) == Poly(x, x)) is True assert (Poly(x, x) == Poly(x, x, domain=ZZ[a])) is True assert (Poly(x*y, x, y) == Poly(x, x)) is False assert (Poly(x, x, y) == Poly(x, x)) is False assert (Poly(x, x) == Poly(x, x, y)) is False assert (Poly(x**2 + 1, x) == Poly(y**2 + 1, y)) is False assert (Poly(y**2 + 1, y) == Poly(x**2 + 1, x)) is False f = Poly(x, x, domain=ZZ) g = Poly(x, x, domain=QQ) assert f.eq(g) is True assert f.ne(g) is False assert f.eq(g, strict=True) is False assert f.ne(g, strict=True) is True t0 = Symbol('t0') f = Poly((t0/2 + x**2)*t**2 - x**2*t, t, domain='QQ[x,t0]') g = Poly((t0/2 + x**2)*t**2 - x**2*t, t, domain='ZZ(x,t0)') assert (f == g) is True def test_PurePoly__eq__(): assert (PurePoly(x, x) == PurePoly(x, x)) is True assert (PurePoly(x, x, domain=QQ) == PurePoly(x, x)) is True assert (PurePoly(x, x) == PurePoly(x, x, domain=QQ)) is True assert (PurePoly(x, x, domain=ZZ[a]) == PurePoly(x, x)) is True assert (PurePoly(x, x) == PurePoly(x, x, domain=ZZ[a])) is True assert (PurePoly(x*y, x, y) == PurePoly(x, x)) is False assert (PurePoly(x, x, y) == PurePoly(x, x)) is False assert (PurePoly(x, x) == PurePoly(x, x, y)) is False assert (PurePoly(x**2 + 1, x) == PurePoly(y**2 + 1, y)) is True assert (PurePoly(y**2 + 1, y) == PurePoly(x**2 + 1, x)) is True f = PurePoly(x, x, domain=ZZ) g = PurePoly(x, x, domain=QQ) assert f.eq(g) is True assert f.ne(g) is False assert f.eq(g, strict=True) is False assert f.ne(g, strict=True) is True f = PurePoly(x, x, domain=ZZ) g = PurePoly(y, y, domain=QQ) assert f.eq(g) is True assert f.ne(g) is False assert f.eq(g, strict=True) is False assert f.ne(g, strict=True) is True def test_PurePoly_Poly(): assert isinstance(PurePoly(Poly(x**2 + 1)), PurePoly) is True assert isinstance(Poly(PurePoly(x**2 + 1)), Poly) is True def test_Poly_get_domain(): assert Poly(2*x).get_domain() == ZZ assert Poly(2*x, domain='ZZ').get_domain() == ZZ assert Poly(2*x, domain='QQ').get_domain() == QQ assert Poly(x/2).get_domain() == QQ raises(CoercionFailed, lambda: Poly(x/2, domain='ZZ')) assert Poly(x/2, domain='QQ').get_domain() == QQ assert Poly(0.2*x).get_domain() == RR def test_Poly_set_domain(): assert Poly(2*x + 1).set_domain(ZZ) == Poly(2*x + 1) assert Poly(2*x + 1).set_domain('ZZ') == Poly(2*x + 1) assert Poly(2*x + 1).set_domain(QQ) == Poly(2*x + 1, domain='QQ') assert Poly(2*x + 1).set_domain('QQ') == Poly(2*x + 1, domain='QQ') assert Poly(S(2)/10*x + S(1)/10).set_domain('RR') == Poly(0.2*x + 0.1) assert Poly(0.2*x + 0.1).set_domain('QQ') == Poly(S(2)/10*x + S(1)/10) raises(CoercionFailed, lambda: Poly(x/2 + 1).set_domain(ZZ)) raises(CoercionFailed, lambda: Poly(x + 1, modulus=2).set_domain(QQ)) raises(GeneratorsError, lambda: Poly(x*y, x, y).set_domain(ZZ[y])) def test_Poly_get_modulus(): assert Poly(x**2 + 1, modulus=2).get_modulus() == 2 raises(PolynomialError, lambda: Poly(x**2 + 1).get_modulus()) def test_Poly_set_modulus(): assert Poly( x**2 + 1, modulus=2).set_modulus(7) == Poly(x**2 + 1, modulus=7) assert Poly( x**2 + 5, modulus=7).set_modulus(2) == Poly(x**2 + 1, modulus=2) assert Poly(x**2 + 1).set_modulus(2) == Poly(x**2 + 1, modulus=2) raises(CoercionFailed, lambda: Poly(x/2 + 1).set_modulus(2)) def test_Poly_add_ground(): assert Poly(x + 1).add_ground(2) == Poly(x + 3) def test_Poly_sub_ground(): assert Poly(x + 1).sub_ground(2) == Poly(x - 1) def test_Poly_mul_ground(): assert Poly(x + 1).mul_ground(2) == Poly(2*x + 2) def test_Poly_quo_ground(): assert Poly(2*x + 4).quo_ground(2) == Poly(x + 2) assert Poly(2*x + 3).quo_ground(2) == Poly(x + 1) def test_Poly_exquo_ground(): assert Poly(2*x + 4).exquo_ground(2) == Poly(x + 2) raises(ExactQuotientFailed, lambda: Poly(2*x + 3).exquo_ground(2)) def test_Poly_abs(): assert Poly(-x + 1, x).abs() == abs(Poly(-x + 1, x)) == Poly(x + 1, x) def test_Poly_neg(): assert Poly(-x + 1, x).neg() == -Poly(-x + 1, x) == Poly(x - 1, x) def test_Poly_add(): assert Poly(0, x).add(Poly(0, x)) == Poly(0, x) assert Poly(0, x) + Poly(0, x) == Poly(0, x) assert Poly(1, x).add(Poly(0, x)) == Poly(1, x) assert Poly(1, x, y) + Poly(0, x) == Poly(1, x, y) assert Poly(0, x).add(Poly(1, x, y)) == Poly(1, x, y) assert Poly(0, x, y) + Poly(1, x, y) == Poly(1, x, y) assert Poly(1, x) + x == Poly(x + 1, x) assert Poly(1, x) + sin(x) == 1 + sin(x) assert Poly(x, x) + 1 == Poly(x + 1, x) assert 1 + Poly(x, x) == Poly(x + 1, x) def test_Poly_sub(): assert Poly(0, x).sub(Poly(0, x)) == Poly(0, x) assert Poly(0, x) - Poly(0, x) == Poly(0, x) assert Poly(1, x).sub(Poly(0, x)) == Poly(1, x) assert Poly(1, x, y) - Poly(0, x) == Poly(1, x, y) assert Poly(0, x).sub(Poly(1, x, y)) == Poly(-1, x, y) assert Poly(0, x, y) - Poly(1, x, y) == Poly(-1, x, y) assert Poly(1, x) - x == Poly(1 - x, x) assert Poly(1, x) - sin(x) == 1 - sin(x) assert Poly(x, x) - 1 == Poly(x - 1, x) assert 1 - Poly(x, x) == Poly(1 - x, x) def test_Poly_mul(): assert Poly(0, x).mul(Poly(0, x)) == Poly(0, x) assert Poly(0, x) * Poly(0, x) == Poly(0, x) assert Poly(2, x).mul(Poly(4, x)) == Poly(8, x) assert Poly(2, x, y) * Poly(4, x) == Poly(8, x, y) assert Poly(4, x).mul(Poly(2, x, y)) == Poly(8, x, y) assert Poly(4, x, y) * Poly(2, x, y) == Poly(8, x, y) assert Poly(1, x) * x == Poly(x, x) assert Poly(1, x) * sin(x) == sin(x) assert Poly(x, x) * 2 == Poly(2*x, x) assert 2 * Poly(x, x) == Poly(2*x, x) def test_Poly_sqr(): assert Poly(x*y, x, y).sqr() == Poly(x**2*y**2, x, y) def test_Poly_pow(): assert Poly(x, x).pow(10) == Poly(x**10, x) assert Poly(x, x).pow(Integer(10)) == Poly(x**10, x) assert Poly(2*y, x, y).pow(4) == Poly(16*y**4, x, y) assert Poly(2*y, x, y).pow(Integer(4)) == Poly(16*y**4, x, y) assert Poly(7*x*y, x, y)**3 == Poly(343*x**3*y**3, x, y) assert Poly(x*y + 1, x, y)**(-1) == (x*y + 1)**(-1) assert Poly(x*y + 1, x, y)**x == (x*y + 1)**x def test_Poly_divmod(): f, g = Poly(x**2), Poly(x) q, r = g, Poly(0, x) assert divmod(f, g) == (q, r) assert f // g == q assert f % g == r assert divmod(f, x) == (q, r) assert f // x == q assert f % x == r q, r = Poly(0, x), Poly(2, x) assert divmod(2, g) == (q, r) assert 2 // g == q assert 2 % g == r assert Poly(x)/Poly(x) == 1 assert Poly(x**2)/Poly(x) == x assert Poly(x)/Poly(x**2) == 1/x def test_Poly_eq_ne(): assert (Poly(x + y, x, y) == Poly(x + y, x, y)) is True assert (Poly(x + y, x) == Poly(x + y, x, y)) is False assert (Poly(x + y, x, y) == Poly(x + y, x)) is False assert (Poly(x + y, x) == Poly(x + y, x)) is True assert (Poly(x + y, y) == Poly(x + y, y)) is True assert (Poly(x + y, x, y) == x + y) is True assert (Poly(x + y, x) == x + y) is True assert (Poly(x + y, x, y) == x + y) is True assert (Poly(x + y, x) == x + y) is True assert (Poly(x + y, y) == x + y) is True assert (Poly(x + y, x, y) != Poly(x + y, x, y)) is False assert (Poly(x + y, x) != Poly(x + y, x, y)) is True assert (Poly(x + y, x, y) != Poly(x + y, x)) is True assert (Poly(x + y, x) != Poly(x + y, x)) is False assert (Poly(x + y, y) != Poly(x + y, y)) is False assert (Poly(x + y, x, y) != x + y) is False assert (Poly(x + y, x) != x + y) is False assert (Poly(x + y, x, y) != x + y) is False assert (Poly(x + y, x) != x + y) is False assert (Poly(x + y, y) != x + y) is False assert (Poly(x, x) == sin(x)) is False assert (Poly(x, x) != sin(x)) is True def test_Poly_nonzero(): assert not bool(Poly(0, x)) is True assert not bool(Poly(1, x)) is False def test_Poly_properties(): assert Poly(0, x).is_zero is True assert Poly(1, x).is_zero is False assert Poly(1, x).is_one is True assert Poly(2, x).is_one is False assert Poly(x - 1, x).is_sqf is True assert Poly((x - 1)**2, x).is_sqf is False assert Poly(x - 1, x).is_monic is True assert Poly(2*x - 1, x).is_monic is False assert Poly(3*x + 2, x).is_primitive is True assert Poly(4*x + 2, x).is_primitive is False assert Poly(1, x).is_ground is True assert Poly(x, x).is_ground is False assert Poly(x + y + z + 1).is_linear is True assert Poly(x*y*z + 1).is_linear is False assert Poly(x*y + z + 1).is_quadratic is True assert Poly(x*y*z + 1).is_quadratic is False assert Poly(x*y).is_monomial is True assert Poly(x*y + 1).is_monomial is False assert Poly(x**2 + x*y).is_homogeneous is True assert Poly(x**3 + x*y).is_homogeneous is False assert Poly(x).is_univariate is True assert Poly(x*y).is_univariate is False assert Poly(x*y).is_multivariate is True assert Poly(x).is_multivariate is False assert Poly( x**16 + x**14 - x**10 + x**8 - x**6 + x**2 + 1).is_cyclotomic is False assert Poly( x**16 + x**14 - x**10 - x**8 - x**6 + x**2 + 1).is_cyclotomic is True def test_Poly_is_irreducible(): assert Poly(x**2 + x + 1).is_irreducible is True assert Poly(x**2 + 2*x + 1).is_irreducible is False assert Poly(7*x + 3, modulus=11).is_irreducible is True assert Poly(7*x**2 + 3*x + 1, modulus=11).is_irreducible is False def test_Poly_subs(): assert Poly(x + 1).subs(x, 0) == 1 assert Poly(x + 1).subs(x, x) == Poly(x + 1) assert Poly(x + 1).subs(x, y) == Poly(y + 1) assert Poly(x*y, x).subs(y, x) == x**2 assert Poly(x*y, x).subs(x, y) == y**2 def test_Poly_replace(): assert Poly(x + 1).replace(x) == Poly(x + 1) assert Poly(x + 1).replace(y) == Poly(y + 1) raises(PolynomialError, lambda: Poly(x + y).replace(z)) assert Poly(x + 1).replace(x, x) == Poly(x + 1) assert Poly(x + 1).replace(x, y) == Poly(y + 1) assert Poly(x + y).replace(x, x) == Poly(x + y) assert Poly(x + y).replace(x, z) == Poly(z + y, z, y) assert Poly(x + y).replace(y, y) == Poly(x + y) assert Poly(x + y).replace(y, z) == Poly(x + z, x, z) raises(PolynomialError, lambda: Poly(x + y).replace(x, y)) raises(PolynomialError, lambda: Poly(x + y).replace(z, t)) assert Poly(x + y, x).replace(x, z) == Poly(z + y, z) assert Poly(x + y, y).replace(y, z) == Poly(x + z, z) raises(PolynomialError, lambda: Poly(x + y, x).replace(x, y)) raises(PolynomialError, lambda: Poly(x + y, y).replace(y, x)) def test_Poly_reorder(): raises(PolynomialError, lambda: Poly(x + y).reorder(x, z)) assert Poly(x + y, x, y).reorder(x, y) == Poly(x + y, x, y) assert Poly(x + y, x, y).reorder(y, x) == Poly(x + y, y, x) assert Poly(x + y, y, x).reorder(x, y) == Poly(x + y, x, y) assert Poly(x + y, y, x).reorder(y, x) == Poly(x + y, y, x) assert Poly(x + y, x, y).reorder(wrt=x) == Poly(x + y, x, y) assert Poly(x + y, x, y).reorder(wrt=y) == Poly(x + y, y, x) def test_Poly_ltrim(): f = Poly(y**2 + y*z**2, x, y, z).ltrim(y) assert f.as_expr() == y**2 + y*z**2 and f.gens == (y, z) raises(PolynomialError, lambda: Poly(x*y**2 + y**2, x, y).ltrim(y)) def test_Poly_has_only_gens(): assert Poly(x*y + 1, x, y, z).has_only_gens(x, y) is True assert Poly(x*y + z, x, y, z).has_only_gens(x, y) is False raises(GeneratorsError, lambda: Poly(x*y**2 + y**2, x, y).has_only_gens(t)) def test_Poly_to_ring(): assert Poly(2*x + 1, domain='ZZ').to_ring() == Poly(2*x + 1, domain='ZZ') assert Poly(2*x + 1, domain='QQ').to_ring() == Poly(2*x + 1, domain='ZZ') raises(CoercionFailed, lambda: Poly(x/2 + 1).to_ring()) raises(DomainError, lambda: Poly(2*x + 1, modulus=3).to_ring()) def test_Poly_to_field(): assert Poly(2*x + 1, domain='ZZ').to_field() == Poly(2*x + 1, domain='QQ') assert Poly(2*x + 1, domain='QQ').to_field() == Poly(2*x + 1, domain='QQ') assert Poly(x/2 + 1, domain='QQ').to_field() == Poly(x/2 + 1, domain='QQ') assert Poly(2*x + 1, modulus=3).to_field() == Poly(2*x + 1, modulus=3) assert Poly(2.0*x + 1.0).to_field() == Poly(2.0*x + 1.0) def test_Poly_to_exact(): assert Poly(2*x).to_exact() == Poly(2*x) assert Poly(x/2).to_exact() == Poly(x/2) assert Poly(0.1*x).to_exact() == Poly(x/10) def test_Poly_retract(): f = Poly(x**2 + 1, x, domain=QQ[y]) assert f.retract() == Poly(x**2 + 1, x, domain='ZZ') assert f.retract(field=True) == Poly(x**2 + 1, x, domain='QQ') assert Poly(0, x, y).retract() == Poly(0, x, y) def test_Poly_slice(): f = Poly(x**3 + 2*x**2 + 3*x + 4) assert f.slice(0, 0) == Poly(0, x) assert f.slice(0, 1) == Poly(4, x) assert f.slice(0, 2) == Poly(3*x + 4, x) assert f.slice(0, 3) == Poly(2*x**2 + 3*x + 4, x) assert f.slice(0, 4) == Poly(x**3 + 2*x**2 + 3*x + 4, x) assert f.slice(x, 0, 0) == Poly(0, x) assert f.slice(x, 0, 1) == Poly(4, x) assert f.slice(x, 0, 2) == Poly(3*x + 4, x) assert f.slice(x, 0, 3) == Poly(2*x**2 + 3*x + 4, x) assert f.slice(x, 0, 4) == Poly(x**3 + 2*x**2 + 3*x + 4, x) def test_Poly_coeffs(): assert Poly(0, x).coeffs() == [0] assert Poly(1, x).coeffs() == [1] assert Poly(2*x + 1, x).coeffs() == [2, 1] assert Poly(7*x**2 + 2*x + 1, x).coeffs() == [7, 2, 1] assert Poly(7*x**4 + 2*x + 1, x).coeffs() == [7, 2, 1] assert Poly(x*y**7 + 2*x**2*y**3).coeffs('lex') == [2, 1] assert Poly(x*y**7 + 2*x**2*y**3).coeffs('grlex') == [1, 2] def test_Poly_monoms(): assert Poly(0, x).monoms() == [(0,)] assert Poly(1, x).monoms() == [(0,)] assert Poly(2*x + 1, x).monoms() == [(1,), (0,)] assert Poly(7*x**2 + 2*x + 1, x).monoms() == [(2,), (1,), (0,)] assert Poly(7*x**4 + 2*x + 1, x).monoms() == [(4,), (1,), (0,)] assert Poly(x*y**7 + 2*x**2*y**3).monoms('lex') == [(2, 3), (1, 7)] assert Poly(x*y**7 + 2*x**2*y**3).monoms('grlex') == [(1, 7), (2, 3)] def test_Poly_terms(): assert Poly(0, x).terms() == [((0,), 0)] assert Poly(1, x).terms() == [((0,), 1)] assert Poly(2*x + 1, x).terms() == [((1,), 2), ((0,), 1)] assert Poly(7*x**2 + 2*x + 1, x).terms() == [((2,), 7), ((1,), 2), ((0,), 1)] assert Poly(7*x**4 + 2*x + 1, x).terms() == [((4,), 7), ((1,), 2), ((0,), 1)] assert Poly( x*y**7 + 2*x**2*y**3).terms('lex') == [((2, 3), 2), ((1, 7), 1)] assert Poly( x*y**7 + 2*x**2*y**3).terms('grlex') == [((1, 7), 1), ((2, 3), 2)] def test_Poly_all_coeffs(): assert Poly(0, x).all_coeffs() == [0] assert Poly(1, x).all_coeffs() == [1] assert Poly(2*x + 1, x).all_coeffs() == [2, 1] assert Poly(7*x**2 + 2*x + 1, x).all_coeffs() == [7, 2, 1] assert Poly(7*x**4 + 2*x + 1, x).all_coeffs() == [7, 0, 0, 2, 1] def test_Poly_all_monoms(): assert Poly(0, x).all_monoms() == [(0,)] assert Poly(1, x).all_monoms() == [(0,)] assert Poly(2*x + 1, x).all_monoms() == [(1,), (0,)] assert Poly(7*x**2 + 2*x + 1, x).all_monoms() == [(2,), (1,), (0,)] assert Poly(7*x**4 + 2*x + 1, x).all_monoms() == [(4,), (3,), (2,), (1,), (0,)] def test_Poly_all_terms(): assert Poly(0, x).all_terms() == [((0,), 0)] assert Poly(1, x).all_terms() == [((0,), 1)] assert Poly(2*x + 1, x).all_terms() == [((1,), 2), ((0,), 1)] assert Poly(7*x**2 + 2*x + 1, x).all_terms() == \ [((2,), 7), ((1,), 2), ((0,), 1)] assert Poly(7*x**4 + 2*x + 1, x).all_terms() == \ [((4,), 7), ((3,), 0), ((2,), 0), ((1,), 2), ((0,), 1)] def test_Poly_termwise(): f = Poly(x**2 + 20*x + 400) g = Poly(x**2 + 2*x + 4) def func(monom, coeff): (k,) = monom return coeff//10**(2 - k) assert f.termwise(func) == g def func(monom, coeff): (k,) = monom return (k,), coeff//10**(2 - k) assert f.termwise(func) == g def test_Poly_length(): assert Poly(0, x).length() == 0 assert Poly(1, x).length() == 1 assert Poly(x, x).length() == 1 assert Poly(x + 1, x).length() == 2 assert Poly(x**2 + 1, x).length() == 2 assert Poly(x**2 + x + 1, x).length() == 3 def test_Poly_as_dict(): assert Poly(0, x).as_dict() == {} assert Poly(0, x, y, z).as_dict() == {} assert Poly(1, x).as_dict() == {(0,): 1} assert Poly(1, x, y, z).as_dict() == {(0, 0, 0): 1} assert Poly(x**2 + 3, x).as_dict() == {(2,): 1, (0,): 3} assert Poly(x**2 + 3, x, y, z).as_dict() == {(2, 0, 0): 1, (0, 0, 0): 3} assert Poly(3*x**2*y*z**3 + 4*x*y + 5*x*z).as_dict() == {(2, 1, 3): 3, (1, 1, 0): 4, (1, 0, 1): 5} def test_Poly_as_expr(): assert Poly(0, x).as_expr() == 0 assert Poly(0, x, y, z).as_expr() == 0 assert Poly(1, x).as_expr() == 1 assert Poly(1, x, y, z).as_expr() == 1 assert Poly(x**2 + 3, x).as_expr() == x**2 + 3 assert Poly(x**2 + 3, x, y, z).as_expr() == x**2 + 3 assert Poly( 3*x**2*y*z**3 + 4*x*y + 5*x*z).as_expr() == 3*x**2*y*z**3 + 4*x*y + 5*x*z f = Poly(x**2 + 2*x*y**2 - y, x, y) assert f.as_expr() == -y + x**2 + 2*x*y**2 assert f.as_expr({x: 5}) == 25 - y + 10*y**2 assert f.as_expr({y: 6}) == -6 + 72*x + x**2 assert f.as_expr({x: 5, y: 6}) == 379 assert f.as_expr(5, 6) == 379 raises(GeneratorsError, lambda: f.as_expr({z: 7})) def test_Poly_lift(): assert Poly(x**4 - I*x + 17*I, x, gaussian=True).lift() == \ Poly(x**16 + 2*x**10 + 578*x**8 + x**4 - 578*x**2 + 83521, x, domain='QQ') def test_Poly_deflate(): assert Poly(0, x).deflate() == ((1,), Poly(0, x)) assert Poly(1, x).deflate() == ((1,), Poly(1, x)) assert Poly(x, x).deflate() == ((1,), Poly(x, x)) assert Poly(x**2, x).deflate() == ((2,), Poly(x, x)) assert Poly(x**17, x).deflate() == ((17,), Poly(x, x)) assert Poly( x**2*y*z**11 + x**4*z**11).deflate() == ((2, 1, 11), Poly(x*y*z + x**2*z)) def test_Poly_inject(): f = Poly(x**2*y + x*y**3 + x*y + 1, x) assert f.inject() == Poly(x**2*y + x*y**3 + x*y + 1, x, y) assert f.inject(front=True) == Poly(y**3*x + y*x**2 + y*x + 1, y, x) def test_Poly_eject(): f = Poly(x**2*y + x*y**3 + x*y + 1, x, y) assert f.eject(x) == Poly(x*y**3 + (x**2 + x)*y + 1, y, domain='ZZ[x]') assert f.eject(y) == Poly(y*x**2 + (y**3 + y)*x + 1, x, domain='ZZ[y]') ex = x + y + z + t + w g = Poly(ex, x, y, z, t, w) assert g.eject(x) == Poly(ex, y, z, t, w, domain='ZZ[x]') assert g.eject(x, y) == Poly(ex, z, t, w, domain='ZZ[x, y]') assert g.eject(x, y, z) == Poly(ex, t, w, domain='ZZ[x, y, z]') assert g.eject(w) == Poly(ex, x, y, z, t, domain='ZZ[w]') assert g.eject(t, w) == Poly(ex, x, y, z, domain='ZZ[w, t]') assert g.eject(z, t, w) == Poly(ex, x, y, domain='ZZ[w, t, z]') raises(DomainError, lambda: Poly(x*y, x, y, domain=ZZ[z]).eject(y)) raises(NotImplementedError, lambda: Poly(x*y, x, y, z).eject(y)) def test_Poly_exclude(): assert Poly(x, x, y).exclude() == Poly(x, x) assert Poly(x*y, x, y).exclude() == Poly(x*y, x, y) assert Poly(1, x, y).exclude() == Poly(1, x, y) def test_Poly__gen_to_level(): assert Poly(1, x, y)._gen_to_level(-2) == 0 assert Poly(1, x, y)._gen_to_level(-1) == 1 assert Poly(1, x, y)._gen_to_level( 0) == 0 assert Poly(1, x, y)._gen_to_level( 1) == 1 raises(PolynomialError, lambda: Poly(1, x, y)._gen_to_level(-3)) raises(PolynomialError, lambda: Poly(1, x, y)._gen_to_level( 2)) assert Poly(1, x, y)._gen_to_level(x) == 0 assert Poly(1, x, y)._gen_to_level(y) == 1 assert Poly(1, x, y)._gen_to_level('x') == 0 assert Poly(1, x, y)._gen_to_level('y') == 1 raises(PolynomialError, lambda: Poly(1, x, y)._gen_to_level(z)) raises(PolynomialError, lambda: Poly(1, x, y)._gen_to_level('z')) def test_Poly_degree(): assert Poly(0, x).degree() == -oo assert Poly(1, x).degree() == 0 assert Poly(x, x).degree() == 1 assert Poly(0, x).degree(gen=0) == -oo assert Poly(1, x).degree(gen=0) == 0 assert Poly(x, x).degree(gen=0) == 1 assert Poly(0, x).degree(gen=x) == -oo assert Poly(1, x).degree(gen=x) == 0 assert Poly(x, x).degree(gen=x) == 1 assert Poly(0, x).degree(gen='x') == -oo assert Poly(1, x).degree(gen='x') == 0 assert Poly(x, x).degree(gen='x') == 1 raises(PolynomialError, lambda: Poly(1, x).degree(gen=1)) raises(PolynomialError, lambda: Poly(1, x).degree(gen=y)) raises(PolynomialError, lambda: Poly(1, x).degree(gen='y')) assert Poly(1, x, y).degree() == 0 assert Poly(2*y, x, y).degree() == 0 assert Poly(x*y, x, y).degree() == 1 assert Poly(1, x, y).degree(gen=x) == 0 assert Poly(2*y, x, y).degree(gen=x) == 0 assert Poly(x*y, x, y).degree(gen=x) == 1 assert Poly(1, x, y).degree(gen=y) == 0 assert Poly(2*y, x, y).degree(gen=y) == 1 assert Poly(x*y, x, y).degree(gen=y) == 1 assert degree(1, x) == 0 assert degree(x, x) == 1 assert degree(x*y**2, gen=x) == 1 assert degree(x*y**2, gen=y) == 2 assert degree(x*y**2, x, y) == 1 assert degree(x*y**2, y, x) == 2 raises(ComputationFailed, lambda: degree(1)) def test_Poly_degree_list(): assert Poly(0, x).degree_list() == (-oo,) assert Poly(0, x, y).degree_list() == (-oo, -oo) assert Poly(0, x, y, z).degree_list() == (-oo, -oo, -oo) assert Poly(1, x).degree_list() == (0,) assert Poly(1, x, y).degree_list() == (0, 0) assert Poly(1, x, y, z).degree_list() == (0, 0, 0) assert Poly(x**2*y + x**3*z**2 + 1).degree_list() == (3, 1, 2) assert degree_list(1, x) == (0,) assert degree_list(x, x) == (1,) assert degree_list(x*y**2) == (1, 2) raises(ComputationFailed, lambda: degree_list(1)) def test_Poly_total_degree(): assert Poly(x**2*y + x**3*z**2 + 1).total_degree() == 5 assert Poly(x**2 + z**3).total_degree() == 3 assert Poly(x*y*z + z**4).total_degree() == 4 assert Poly(x**3 + x + 1).total_degree() == 3 def test_Poly_homogenize(): assert Poly(x**2+y).homogenize(z) == Poly(x**2+y*z) assert Poly(x+y).homogenize(z) == Poly(x+y, x, y, z) assert Poly(x+y**2).homogenize(y) == Poly(x*y+y**2) def test_Poly_homogeneous_order(): assert Poly(0, x, y).homogeneous_order() == -oo assert Poly(1, x, y).homogeneous_order() == 0 assert Poly(x, x, y).homogeneous_order() == 1 assert Poly(x*y, x, y).homogeneous_order() == 2 assert Poly(x + 1, x, y).homogeneous_order() is None assert Poly(x*y + x, x, y).homogeneous_order() is None assert Poly(x**5 + 2*x**3*y**2 + 9*x*y**4).homogeneous_order() == 5 assert Poly(x**5 + 2*x**3*y**3 + 9*x*y**4).homogeneous_order() is None def test_Poly_LC(): assert Poly(0, x).LC() == 0 assert Poly(1, x).LC() == 1 assert Poly(2*x**2 + x, x).LC() == 2 assert Poly(x*y**7 + 2*x**2*y**3).LC('lex') == 2 assert Poly(x*y**7 + 2*x**2*y**3).LC('grlex') == 1 assert LC(x*y**7 + 2*x**2*y**3, order='lex') == 2 assert LC(x*y**7 + 2*x**2*y**3, order='grlex') == 1 def test_Poly_TC(): assert Poly(0, x).TC() == 0 assert Poly(1, x).TC() == 1 assert Poly(2*x**2 + x, x).TC() == 0 def test_Poly_EC(): assert Poly(0, x).EC() == 0 assert Poly(1, x).EC() == 1 assert Poly(2*x**2 + x, x).EC() == 1 assert Poly(x*y**7 + 2*x**2*y**3).EC('lex') == 1 assert Poly(x*y**7 + 2*x**2*y**3).EC('grlex') == 2 def test_Poly_coeff(): assert Poly(0, x).coeff_monomial(1) == 0 assert Poly(0, x).coeff_monomial(x) == 0 assert Poly(1, x).coeff_monomial(1) == 1 assert Poly(1, x).coeff_monomial(x) == 0 assert Poly(x**8, x).coeff_monomial(1) == 0 assert Poly(x**8, x).coeff_monomial(x**7) == 0 assert Poly(x**8, x).coeff_monomial(x**8) == 1 assert Poly(x**8, x).coeff_monomial(x**9) == 0 assert Poly(3*x*y**2 + 1, x, y).coeff_monomial(1) == 1 assert Poly(3*x*y**2 + 1, x, y).coeff_monomial(x*y**2) == 3 p = Poly(24*x*y*exp(8) + 23*x, x, y) assert p.coeff_monomial(x) == 23 assert p.coeff_monomial(y) == 0 assert p.coeff_monomial(x*y) == 24*exp(8) assert p.as_expr().coeff(x) == 24*y*exp(8) + 23 raises(NotImplementedError, lambda: p.coeff(x)) raises(ValueError, lambda: Poly(x + 1).coeff_monomial(0)) raises(ValueError, lambda: Poly(x + 1).coeff_monomial(3*x)) raises(ValueError, lambda: Poly(x + 1).coeff_monomial(3*x*y)) def test_Poly_nth(): assert Poly(0, x).nth(0) == 0 assert Poly(0, x).nth(1) == 0 assert Poly(1, x).nth(0) == 1 assert Poly(1, x).nth(1) == 0 assert Poly(x**8, x).nth(0) == 0 assert Poly(x**8, x).nth(7) == 0 assert Poly(x**8, x).nth(8) == 1 assert Poly(x**8, x).nth(9) == 0 assert Poly(3*x*y**2 + 1, x, y).nth(0, 0) == 1 assert Poly(3*x*y**2 + 1, x, y).nth(1, 2) == 3 def test_Poly_LM(): assert Poly(0, x).LM() == (0,) assert Poly(1, x).LM() == (0,) assert Poly(2*x**2 + x, x).LM() == (2,) assert Poly(x*y**7 + 2*x**2*y**3).LM('lex') == (2, 3) assert Poly(x*y**7 + 2*x**2*y**3).LM('grlex') == (1, 7) assert LM(x*y**7 + 2*x**2*y**3, order='lex') == x**2*y**3 assert LM(x*y**7 + 2*x**2*y**3, order='grlex') == x*y**7 def test_Poly_LM_custom_order(): f = Poly(x**2*y**3*z + x**2*y*z**3 + x*y*z + 1) rev_lex = lambda monom: tuple(reversed(monom)) assert f.LM(order='lex') == (2, 3, 1) assert f.LM(order=rev_lex) == (2, 1, 3) def test_Poly_EM(): assert Poly(0, x).EM() == (0,) assert Poly(1, x).EM() == (0,) assert Poly(2*x**2 + x, x).EM() == (1,) assert Poly(x*y**7 + 2*x**2*y**3).EM('lex') == (1, 7) assert Poly(x*y**7 + 2*x**2*y**3).EM('grlex') == (2, 3) def test_Poly_LT(): assert Poly(0, x).LT() == ((0,), 0) assert Poly(1, x).LT() == ((0,), 1) assert Poly(2*x**2 + x, x).LT() == ((2,), 2) assert Poly(x*y**7 + 2*x**2*y**3).LT('lex') == ((2, 3), 2) assert Poly(x*y**7 + 2*x**2*y**3).LT('grlex') == ((1, 7), 1) assert LT(x*y**7 + 2*x**2*y**3, order='lex') == 2*x**2*y**3 assert LT(x*y**7 + 2*x**2*y**3, order='grlex') == x*y**7 def test_Poly_ET(): assert Poly(0, x).ET() == ((0,), 0) assert Poly(1, x).ET() == ((0,), 1) assert Poly(2*x**2 + x, x).ET() == ((1,), 1) assert Poly(x*y**7 + 2*x**2*y**3).ET('lex') == ((1, 7), 1) assert Poly(x*y**7 + 2*x**2*y**3).ET('grlex') == ((2, 3), 2) def test_Poly_max_norm(): assert Poly(-1, x).max_norm() == 1 assert Poly( 0, x).max_norm() == 0 assert Poly( 1, x).max_norm() == 1 def test_Poly_l1_norm(): assert Poly(-1, x).l1_norm() == 1 assert Poly( 0, x).l1_norm() == 0 assert Poly( 1, x).l1_norm() == 1 def test_Poly_clear_denoms(): coeff, poly = Poly(x + 2, x).clear_denoms() assert coeff == 1 and poly == Poly( x + 2, x, domain='ZZ') and poly.get_domain() == ZZ coeff, poly = Poly(x/2 + 1, x).clear_denoms() assert coeff == 2 and poly == Poly( x + 2, x, domain='QQ') and poly.get_domain() == QQ coeff, poly = Poly(x/2 + 1, x).clear_denoms(convert=True) assert coeff == 2 and poly == Poly( x + 2, x, domain='ZZ') and poly.get_domain() == ZZ coeff, poly = Poly(x/y + 1, x).clear_denoms(convert=True) assert coeff == y and poly == Poly( x + y, x, domain='ZZ[y]') and poly.get_domain() == ZZ[y] coeff, poly = Poly(x/3 + sqrt(2), x, domain='EX').clear_denoms() assert coeff == 3 and poly == Poly( x + 3*sqrt(2), x, domain='EX') and poly.get_domain() == EX coeff, poly = Poly( x/3 + sqrt(2), x, domain='EX').clear_denoms(convert=True) assert coeff == 3 and poly == Poly( x + 3*sqrt(2), x, domain='EX') and poly.get_domain() == EX def test_Poly_rat_clear_denoms(): f = Poly(x**2/y + 1, x) g = Poly(x**3 + y, x) assert f.rat_clear_denoms(g) == \ (Poly(x**2 + y, x), Poly(y*x**3 + y**2, x)) f = f.set_domain(EX) g = g.set_domain(EX) assert f.rat_clear_denoms(g) == (f, g) def test_Poly_integrate(): assert Poly(x + 1).integrate() == Poly(x**2/2 + x) assert Poly(x + 1).integrate(x) == Poly(x**2/2 + x) assert Poly(x + 1).integrate((x, 1)) == Poly(x**2/2 + x) assert Poly(x*y + 1).integrate(x) == Poly(x**2*y/2 + x) assert Poly(x*y + 1).integrate(y) == Poly(x*y**2/2 + y) assert Poly(x*y + 1).integrate(x, x) == Poly(x**3*y/6 + x**2/2) assert Poly(x*y + 1).integrate(y, y) == Poly(x*y**3/6 + y**2/2) assert Poly(x*y + 1).integrate((x, 2)) == Poly(x**3*y/6 + x**2/2) assert Poly(x*y + 1).integrate((y, 2)) == Poly(x*y**3/6 + y**2/2) assert Poly(x*y + 1).integrate(x, y) == Poly(x**2*y**2/4 + x*y) assert Poly(x*y + 1).integrate(y, x) == Poly(x**2*y**2/4 + x*y) def test_Poly_diff(): assert Poly(x**2 + x).diff() == Poly(2*x + 1) assert Poly(x**2 + x).diff(x) == Poly(2*x + 1) assert Poly(x**2 + x).diff((x, 1)) == Poly(2*x + 1) assert Poly(x**2*y**2 + x*y).diff(x) == Poly(2*x*y**2 + y) assert Poly(x**2*y**2 + x*y).diff(y) == Poly(2*x**2*y + x) assert Poly(x**2*y**2 + x*y).diff(x, x) == Poly(2*y**2, x, y) assert Poly(x**2*y**2 + x*y).diff(y, y) == Poly(2*x**2, x, y) assert Poly(x**2*y**2 + x*y).diff((x, 2)) == Poly(2*y**2, x, y) assert Poly(x**2*y**2 + x*y).diff((y, 2)) == Poly(2*x**2, x, y) assert Poly(x**2*y**2 + x*y).diff(x, y) == Poly(4*x*y + 1) assert Poly(x**2*y**2 + x*y).diff(y, x) == Poly(4*x*y + 1) def test_Poly_eval(): assert Poly(0, x).eval(7) == 0 assert Poly(1, x).eval(7) == 1 assert Poly(x, x).eval(7) == 7 assert Poly(0, x).eval(0, 7) == 0 assert Poly(1, x).eval(0, 7) == 1 assert Poly(x, x).eval(0, 7) == 7 assert Poly(0, x).eval(x, 7) == 0 assert Poly(1, x).eval(x, 7) == 1 assert Poly(x, x).eval(x, 7) == 7 assert Poly(0, x).eval('x', 7) == 0 assert Poly(1, x).eval('x', 7) == 1 assert Poly(x, x).eval('x', 7) == 7 raises(PolynomialError, lambda: Poly(1, x).eval(1, 7)) raises(PolynomialError, lambda: Poly(1, x).eval(y, 7)) raises(PolynomialError, lambda: Poly(1, x).eval('y', 7)) assert Poly(123, x, y).eval(7) == Poly(123, y) assert Poly(2*y, x, y).eval(7) == Poly(2*y, y) assert Poly(x*y, x, y).eval(7) == Poly(7*y, y) assert Poly(123, x, y).eval(x, 7) == Poly(123, y) assert Poly(2*y, x, y).eval(x, 7) == Poly(2*y, y) assert Poly(x*y, x, y).eval(x, 7) == Poly(7*y, y) assert Poly(123, x, y).eval(y, 7) == Poly(123, x) assert Poly(2*y, x, y).eval(y, 7) == Poly(14, x) assert Poly(x*y, x, y).eval(y, 7) == Poly(7*x, x) assert Poly(x*y + y, x, y).eval({x: 7}) == Poly(8*y, y) assert Poly(x*y + y, x, y).eval({y: 7}) == Poly(7*x + 7, x) assert Poly(x*y + y, x, y).eval({x: 6, y: 7}) == 49 assert Poly(x*y + y, x, y).eval({x: 7, y: 6}) == 48 assert Poly(x*y + y, x, y).eval((6, 7)) == 49 assert Poly(x*y + y, x, y).eval([6, 7]) == 49 Poly(x + 1, domain='ZZ').eval(S(1)/2) == S(3)/2 Poly(x + 1, domain='ZZ').eval(sqrt(2)) == sqrt(2) + 1 raises(ValueError, lambda: Poly(x*y + y, x, y).eval((6, 7, 8))) raises(DomainError, lambda: Poly(x + 1, domain='ZZ').eval(S(1)/2, auto=False)) # issue 3245 alpha = Symbol('alpha') result = (2*alpha*z - 2*alpha + z**2 + 3)/(z**2 - 2*z + 1) f = Poly(x**2 + (alpha - 1)*x - alpha + 1, x, domain='ZZ[alpha]') assert f.eval((z + 1)/(z - 1)) == result g = Poly(x**2 + (alpha - 1)*x - alpha + 1, x, y, domain='ZZ[alpha]') assert g.eval((z + 1)/(z - 1)) == Poly(result, y, domain='ZZ(alpha,z)') def test_Poly___call__(): f = Poly(2*x*y + 3*x + y + 2*z) assert f(2) == Poly(5*y + 2*z + 6) assert f(2, 5) == Poly(2*z + 31) assert f(2, 5, 7) == 45 def test_parallel_poly_from_expr(): assert parallel_poly_from_expr( [x - 1, x**2 - 1], x)[0] == [Poly(x - 1, x), Poly(x**2 - 1, x)] assert parallel_poly_from_expr( [Poly(x - 1, x), x**2 - 1], x)[0] == [Poly(x - 1, x), Poly(x**2 - 1, x)] assert parallel_poly_from_expr( [x - 1, Poly(x**2 - 1, x)], x)[0] == [Poly(x - 1, x), Poly(x**2 - 1, x)] assert parallel_poly_from_expr([Poly( x - 1, x), Poly(x**2 - 1, x)], x)[0] == [Poly(x - 1, x), Poly(x**2 - 1, x)] assert parallel_poly_from_expr( [x - 1, x**2 - 1], x, y)[0] == [Poly(x - 1, x, y), Poly(x**2 - 1, x, y)] assert parallel_poly_from_expr([Poly( x - 1, x), x**2 - 1], x, y)[0] == [Poly(x - 1, x, y), Poly(x**2 - 1, x, y)] assert parallel_poly_from_expr([x - 1, Poly( x**2 - 1, x)], x, y)[0] == [Poly(x - 1, x, y), Poly(x**2 - 1, x, y)] assert parallel_poly_from_expr([Poly(x - 1, x), Poly( x**2 - 1, x)], x, y)[0] == [Poly(x - 1, x, y), Poly(x**2 - 1, x, y)] assert parallel_poly_from_expr( [x - 1, x**2 - 1])[0] == [Poly(x - 1, x), Poly(x**2 - 1, x)] assert parallel_poly_from_expr( [Poly(x - 1, x), x**2 - 1])[0] == [Poly(x - 1, x), Poly(x**2 - 1, x)] assert parallel_poly_from_expr( [x - 1, Poly(x**2 - 1, x)])[0] == [Poly(x - 1, x), Poly(x**2 - 1, x)] assert parallel_poly_from_expr( [Poly(x - 1, x), Poly(x**2 - 1, x)])[0] == [Poly(x - 1, x), Poly(x**2 - 1, x)] assert parallel_poly_from_expr( [1, x**2 - 1])[0] == [Poly(1, x), Poly(x**2 - 1, x)] assert parallel_poly_from_expr( [1, x**2 - 1])[0] == [Poly(1, x), Poly(x**2 - 1, x)] assert parallel_poly_from_expr( [1, Poly(x**2 - 1, x)])[0] == [Poly(1, x), Poly(x**2 - 1, x)] assert parallel_poly_from_expr( [1, Poly(x**2 - 1, x)])[0] == [Poly(1, x), Poly(x**2 - 1, x)] assert parallel_poly_from_expr( [x**2 - 1, 1])[0] == [Poly(x**2 - 1, x), Poly(1, x)] assert parallel_poly_from_expr( [x**2 - 1, 1])[0] == [Poly(x**2 - 1, x), Poly(1, x)] assert parallel_poly_from_expr( [Poly(x**2 - 1, x), 1])[0] == [Poly(x**2 - 1, x), Poly(1, x)] assert parallel_poly_from_expr( [Poly(x**2 - 1, x), 1])[0] == [Poly(x**2 - 1, x), Poly(1, x)] assert parallel_poly_from_expr([Poly(x, x, y), Poly(y, x, y)], x, y, order='lex')[0] == \ [Poly(x, x, y, domain='ZZ'), Poly(y, x, y, domain='ZZ')] raises(PolificationFailed, lambda: parallel_poly_from_expr([0, 1])) def test_pdiv(): f, g = x**2 - y**2, x - y q, r = x + y, 0 F, G, Q, R = [ Poly(h, x, y) for h in (f, g, q, r) ] assert F.pdiv(G) == (Q, R) assert F.prem(G) == R assert F.pquo(G) == Q assert F.pexquo(G) == Q assert pdiv(f, g) == (q, r) assert prem(f, g) == r assert pquo(f, g) == q assert pexquo(f, g) == q assert pdiv(f, g, x, y) == (q, r) assert prem(f, g, x, y) == r assert pquo(f, g, x, y) == q assert pexquo(f, g, x, y) == q assert pdiv(f, g, (x, y)) == (q, r) assert prem(f, g, (x, y)) == r assert pquo(f, g, (x, y)) == q assert pexquo(f, g, (x, y)) == q assert pdiv(F, G) == (Q, R) assert prem(F, G) == R assert pquo(F, G) == Q assert pexquo(F, G) == Q assert pdiv(f, g, polys=True) == (Q, R) assert prem(f, g, polys=True) == R assert pquo(f, g, polys=True) == Q assert pexquo(f, g, polys=True) == Q assert pdiv(F, G, polys=False) == (q, r) assert prem(F, G, polys=False) == r assert pquo(F, G, polys=False) == q assert pexquo(F, G, polys=False) == q raises(ComputationFailed, lambda: pdiv(4, 2)) raises(ComputationFailed, lambda: prem(4, 2)) raises(ComputationFailed, lambda: pquo(4, 2)) raises(ComputationFailed, lambda: pexquo(4, 2)) def test_div(): f, g = x**2 - y**2, x - y q, r = x + y, 0 F, G, Q, R = [ Poly(h, x, y) for h in (f, g, q, r) ] assert F.div(G) == (Q, R) assert F.rem(G) == R assert F.quo(G) == Q assert F.exquo(G) == Q assert div(f, g) == (q, r) assert rem(f, g) == r assert quo(f, g) == q assert exquo(f, g) == q assert div(f, g, x, y) == (q, r) assert rem(f, g, x, y) == r assert quo(f, g, x, y) == q assert exquo(f, g, x, y) == q assert div(f, g, (x, y)) == (q, r) assert rem(f, g, (x, y)) == r assert quo(f, g, (x, y)) == q assert exquo(f, g, (x, y)) == q assert div(F, G) == (Q, R) assert rem(F, G) == R assert quo(F, G) == Q assert exquo(F, G) == Q assert div(f, g, polys=True) == (Q, R) assert rem(f, g, polys=True) == R assert quo(f, g, polys=True) == Q assert exquo(f, g, polys=True) == Q assert div(F, G, polys=False) == (q, r) assert rem(F, G, polys=False) == r assert quo(F, G, polys=False) == q assert exquo(F, G, polys=False) == q raises(ComputationFailed, lambda: div(4, 2)) raises(ComputationFailed, lambda: rem(4, 2)) raises(ComputationFailed, lambda: quo(4, 2)) raises(ComputationFailed, lambda: exquo(4, 2)) f, g = x**2 + 1, 2*x - 4 qz, rz = 0, x**2 + 1 qq, rq = x/2 + 1, 5 assert div(f, g) == (qq, rq) assert div(f, g, auto=True) == (qq, rq) assert div(f, g, auto=False) == (qz, rz) assert div(f, g, domain=ZZ) == (qz, rz) assert div(f, g, domain=QQ) == (qq, rq) assert div(f, g, domain=ZZ, auto=True) == (qq, rq) assert div(f, g, domain=ZZ, auto=False) == (qz, rz) assert div(f, g, domain=QQ, auto=True) == (qq, rq) assert div(f, g, domain=QQ, auto=False) == (qq, rq) assert rem(f, g) == rq assert rem(f, g, auto=True) == rq assert rem(f, g, auto=False) == rz assert rem(f, g, domain=ZZ) == rz assert rem(f, g, domain=QQ) == rq assert rem(f, g, domain=ZZ, auto=True) == rq assert rem(f, g, domain=ZZ, auto=False) == rz assert rem(f, g, domain=QQ, auto=True) == rq assert rem(f, g, domain=QQ, auto=False) == rq assert quo(f, g) == qq assert quo(f, g, auto=True) == qq assert quo(f, g, auto=False) == qz assert quo(f, g, domain=ZZ) == qz assert quo(f, g, domain=QQ) == qq assert quo(f, g, domain=ZZ, auto=True) == qq assert quo(f, g, domain=ZZ, auto=False) == qz assert quo(f, g, domain=QQ, auto=True) == qq assert quo(f, g, domain=QQ, auto=False) == qq f, g, q = x**2, 2*x, x/2 assert exquo(f, g) == q assert exquo(f, g, auto=True) == q raises(ExactQuotientFailed, lambda: exquo(f, g, auto=False)) raises(ExactQuotientFailed, lambda: exquo(f, g, domain=ZZ)) assert exquo(f, g, domain=QQ) == q assert exquo(f, g, domain=ZZ, auto=True) == q raises(ExactQuotientFailed, lambda: exquo(f, g, domain=ZZ, auto=False)) assert exquo(f, g, domain=QQ, auto=True) == q assert exquo(f, g, domain=QQ, auto=False) == q f, g = Poly(x**2), Poly(x) q, r = f.div(g) assert q.get_domain().is_ZZ and r.get_domain().is_ZZ r = f.rem(g) assert r.get_domain().is_ZZ q = f.quo(g) assert q.get_domain().is_ZZ q = f.exquo(g) assert q.get_domain().is_ZZ def test_gcdex(): f, g = 2*x, x**2 - 16 s, t, h = x/32, -Rational(1, 16), 1 F, G, S, T, H = [ Poly(u, x, domain='QQ') for u in (f, g, s, t, h) ] assert F.half_gcdex(G) == (S, H) assert F.gcdex(G) == (S, T, H) assert F.invert(G) == S assert half_gcdex(f, g) == (s, h) assert gcdex(f, g) == (s, t, h) assert invert(f, g) == s assert half_gcdex(f, g, x) == (s, h) assert gcdex(f, g, x) == (s, t, h) assert invert(f, g, x) == s assert half_gcdex(f, g, (x,)) == (s, h) assert gcdex(f, g, (x,)) == (s, t, h) assert invert(f, g, (x,)) == s assert half_gcdex(F, G) == (S, H) assert gcdex(F, G) == (S, T, H) assert invert(F, G) == S assert half_gcdex(f, g, polys=True) == (S, H) assert gcdex(f, g, polys=True) == (S, T, H) assert invert(f, g, polys=True) == S assert half_gcdex(F, G, polys=False) == (s, h) assert gcdex(F, G, polys=False) == (s, t, h) assert invert(F, G, polys=False) == s assert half_gcdex(100, 2004) == (-20, 4) assert gcdex(100, 2004) == (-20, 1, 4) assert invert(3, 7) == 5 raises(DomainError, lambda: half_gcdex(x + 1, 2*x + 1, auto=False)) raises(DomainError, lambda: gcdex(x + 1, 2*x + 1, auto=False)) raises(DomainError, lambda: invert(x + 1, 2*x + 1, auto=False)) def test_revert(): f = Poly(1 - x**2/2 + x**4/24 - x**6/720) g = Poly(61*x**6/720 + 5*x**4/24 + x**2/2 + 1) assert f.revert(8) == g def test_subresultants(): f, g, h = x**2 - 2*x + 1, x**2 - 1, 2*x - 2 F, G, H = Poly(f), Poly(g), Poly(h) assert F.subresultants(G) == [F, G, H] assert subresultants(f, g) == [f, g, h] assert subresultants(f, g, x) == [f, g, h] assert subresultants(f, g, (x,)) == [f, g, h] assert subresultants(F, G) == [F, G, H] assert subresultants(f, g, polys=True) == [F, G, H] assert subresultants(F, G, polys=False) == [f, g, h] raises(ComputationFailed, lambda: subresultants(4, 2)) def test_resultant(): f, g, h = x**2 - 2*x + 1, x**2 - 1, 0 F, G = Poly(f), Poly(g) assert F.resultant(G) == h assert resultant(f, g) == h assert resultant(f, g, x) == h assert resultant(f, g, (x,)) == h assert resultant(F, G) == h assert resultant(f, g, polys=True) == h assert resultant(F, G, polys=False) == h assert resultant(f, g, includePRS=True) == (h, [f, g, 2*x - 2]) f, g, h = x - a, x - b, a - b F, G, H = Poly(f), Poly(g), Poly(h) assert F.resultant(G) == H assert resultant(f, g) == h assert resultant(f, g, x) == h assert resultant(f, g, (x,)) == h assert resultant(F, G) == H assert resultant(f, g, polys=True) == H assert resultant(F, G, polys=False) == h raises(ComputationFailed, lambda: resultant(4, 2)) def test_discriminant(): f, g = x**3 + 3*x**2 + 9*x - 13, -11664 F = Poly(f) assert F.discriminant() == g assert discriminant(f) == g assert discriminant(f, x) == g assert discriminant(f, (x,)) == g assert discriminant(F) == g assert discriminant(f, polys=True) == g assert discriminant(F, polys=False) == g f, g = a*x**2 + b*x + c, b**2 - 4*a*c F, G = Poly(f), Poly(g) assert F.discriminant() == G assert discriminant(f) == g assert discriminant(f, x, a, b, c) == g assert discriminant(f, (x, a, b, c)) == g assert discriminant(F) == G assert discriminant(f, polys=True) == G assert discriminant(F, polys=False) == g raises(ComputationFailed, lambda: discriminant(4)) def test_gcd_list(): F = [x**3 - 1, x**2 - 1, x**2 - 3*x + 2] assert gcd_list(F) == x - 1 assert gcd_list(F, polys=True) == Poly(x - 1) assert gcd_list([]) == 0 assert gcd_list([1, 2]) == 1 assert gcd_list([4, 6, 8]) == 2 gcd = gcd_list([], x) assert gcd.is_Number and gcd is S.Zero gcd = gcd_list([], x, polys=True) assert gcd.is_Poly and gcd.is_zero raises(ComputationFailed, lambda: gcd_list([], polys=True)) def test_lcm_list(): F = [x**3 - 1, x**2 - 1, x**2 - 3*x + 2] assert lcm_list(F) == x**5 - x**4 - 2*x**3 - x**2 + x + 2 assert lcm_list(F, polys=True) == Poly(x**5 - x**4 - 2*x**3 - x**2 + x + 2) assert lcm_list([]) == 1 assert lcm_list([1, 2]) == 2 assert lcm_list([4, 6, 8]) == 24 lcm = lcm_list([], x) assert lcm.is_Number and lcm is S.One lcm = lcm_list([], x, polys=True) assert lcm.is_Poly and lcm.is_one raises(ComputationFailed, lambda: lcm_list([], polys=True)) def test_gcd(): f, g = x**3 - 1, x**2 - 1 s, t = x**2 + x + 1, x + 1 h, r = x - 1, x**4 + x**3 - x - 1 F, G, S, T, H, R = [ Poly(u) for u in (f, g, s, t, h, r) ] assert F.cofactors(G) == (H, S, T) assert F.gcd(G) == H assert F.lcm(G) == R assert cofactors(f, g) == (h, s, t) assert gcd(f, g) == h assert lcm(f, g) == r assert cofactors(f, g, x) == (h, s, t) assert gcd(f, g, x) == h assert lcm(f, g, x) == r assert cofactors(f, g, (x,)) == (h, s, t) assert gcd(f, g, (x,)) == h assert lcm(f, g, (x,)) == r assert cofactors(F, G) == (H, S, T) assert gcd(F, G) == H assert lcm(F, G) == R assert cofactors(f, g, polys=True) == (H, S, T) assert gcd(f, g, polys=True) == H assert lcm(f, g, polys=True) == R assert cofactors(F, G, polys=False) == (h, s, t) assert gcd(F, G, polys=False) == h assert lcm(F, G, polys=False) == r f, g = 1.0*x**2 - 1.0, 1.0*x - 1.0 h, s, t = g, 1.0*x + 1.0, 1.0 assert cofactors(f, g) == (h, s, t) assert gcd(f, g) == h assert lcm(f, g) == f f, g = 1.0*x**2 - 1.0, 1.0*x - 1.0 h, s, t = g, 1.0*x + 1.0, 1.0 assert cofactors(f, g) == (h, s, t) assert gcd(f, g) == h assert lcm(f, g) == f assert cofactors(8, 6) == (2, 4, 3) assert gcd(8, 6) == 2 assert lcm(8, 6) == 24 f, g = x**2 - 3*x - 4, x**3 - 4*x**2 + x - 4 l = x**4 - 3*x**3 - 3*x**2 - 3*x - 4 h, s, t = x - 4, x + 1, x**2 + 1 assert cofactors(f, g, modulus=11) == (h, s, t) assert gcd(f, g, modulus=11) == h assert lcm(f, g, modulus=11) == l f, g = x**2 + 8*x + 7, x**3 + 7*x**2 + x + 7 l = x**4 + 8*x**3 + 8*x**2 + 8*x + 7 h, s, t = x + 7, x + 1, x**2 + 1 assert cofactors(f, g, modulus=11, symmetric=False) == (h, s, t) assert gcd(f, g, modulus=11, symmetric=False) == h assert lcm(f, g, modulus=11, symmetric=False) == l raises(TypeError, lambda: gcd(x)) raises(TypeError, lambda: lcm(x)) def test_gcd_numbers_vs_polys(): assert isinstance(gcd(3, 9), Integer) assert isinstance(gcd(3*x, 9), Integer) assert gcd(3, 9) == 3 assert gcd(3*x, 9) == 3 assert isinstance(gcd(S(3)/2, S(9)/4), Rational) assert isinstance(gcd(S(3)/2*x, S(9)/4), Rational) assert gcd(S(3)/2, S(9)/4) == S(3)/4 assert gcd(S(3)/2*x, S(9)/4) == 1 assert isinstance(gcd(3.0, 9.0), Float) assert isinstance(gcd(3.0*x, 9.0), Float) assert gcd(3.0, 9.0) == 1.0 assert gcd(3.0*x, 9.0) == 1.0 def test_terms_gcd(): assert terms_gcd(1) == 1 assert terms_gcd(1, x) == 1 assert terms_gcd(x - 1) == x - 1 assert terms_gcd(-x - 1) == -x - 1 assert terms_gcd(2*x + 3) == 2*x + 3 assert terms_gcd(6*x + 4) == Mul(2, 3*x + 2, evaluate=False) assert terms_gcd(x**3*y + x*y**3) == x*y*(x**2 + y**2) assert terms_gcd(2*x**3*y + 2*x*y**3) == 2*x*y*(x**2 + y**2) assert terms_gcd(x**3*y/2 + x*y**3/2) == x*y/2*(x**2 + y**2) assert terms_gcd(x**3*y + 2*x*y**3) == x*y*(x**2 + 2*y**2) assert terms_gcd(2*x**3*y + 4*x*y**3) == 2*x*y*(x**2 + 2*y**2) assert terms_gcd(2*x**3*y/3 + 4*x*y**3/5) == 2*x*y/15*(5*x**2 + 6*y**2) assert terms_gcd(2.0*x**3*y + 4.1*x*y**3) == 1.0*x*y*(2.0*x**2 + 4.1*y**2) assert terms_gcd((3 + 3*x)*(x + x*y), expand=False) == \ (3*x + 3)*(x*y + x) assert terms_gcd((3 + 3*x)*(x + x*sin(3 + 3*y)), expand=False, deep=True) == \ 3*x*(x + 1)*(sin(Mul(3, y + 1, evaluate=False)) + 1) assert terms_gcd(sin(x + x*y), deep=True) == \ sin(x*(y + 1)) def test_trunc(): f, g = x**5 + 2*x**4 + 3*x**3 + 4*x**2 + 5*x + 6, x**5 - x**4 + x**2 - x F, G = Poly(f), Poly(g) assert F.trunc(3) == G assert trunc(f, 3) == g assert trunc(f, 3, x) == g assert trunc(f, 3, (x,)) == g assert trunc(F, 3) == G assert trunc(f, 3, polys=True) == G assert trunc(F, 3, polys=False) == g f, g = 6*x**5 + 5*x**4 + 4*x**3 + 3*x**2 + 2*x + 1, -x**4 + x**3 - x + 1 F, G = Poly(f), Poly(g) assert F.trunc(3) == G assert trunc(f, 3) == g assert trunc(f, 3, x) == g assert trunc(f, 3, (x,)) == g assert trunc(F, 3) == G assert trunc(f, 3, polys=True) == G assert trunc(F, 3, polys=False) == g f = Poly(x**2 + 2*x + 3, modulus=5) assert f.trunc(2) == Poly(x**2 + 1, modulus=5) def test_monic(): f, g = 2*x - 1, x - S(1)/2 F, G = Poly(f, domain='QQ'), Poly(g) assert F.monic() == G assert monic(f) == g assert monic(f, x) == g assert monic(f, (x,)) == g assert monic(F) == G assert monic(f, polys=True) == G assert monic(F, polys=False) == g raises(ComputationFailed, lambda: monic(4)) assert monic(2*x**2 + 6*x + 4, auto=False) == x**2 + 3*x + 2 raises(ExactQuotientFailed, lambda: monic(2*x + 6*x + 1, auto=False)) assert monic(2.0*x**2 + 6.0*x + 4.0) == 1.0*x**2 + 3.0*x + 2.0 assert monic(2*x**2 + 3*x + 4, modulus=5) == x**2 - x + 2 def test_content(): f, F = 4*x + 2, Poly(4*x + 2) assert F.content() == 2 assert content(f) == 2 raises(ComputationFailed, lambda: content(4)) f = Poly(2*x, modulus=3) assert f.content() == 1 def test_primitive(): f, g = 4*x + 2, 2*x + 1 F, G = Poly(f), Poly(g) assert F.primitive() == (2, G) assert primitive(f) == (2, g) assert primitive(f, x) == (2, g) assert primitive(f, (x,)) == (2, g) assert primitive(F) == (2, G) assert primitive(f, polys=True) == (2, G) assert primitive(F, polys=False) == (2, g) raises(ComputationFailed, lambda: primitive(4)) f = Poly(2*x, modulus=3) g = Poly(2.0*x, domain=RR) assert f.primitive() == (1, f) assert g.primitive() == (1.0, g) assert primitive(S('-3*x/4 + y + 11/8')) == \ S('(1/8, -6*x + 8*y + 11)') def test_compose(): f = x**12 + 20*x**10 + 150*x**8 + 500*x**6 + 625*x**4 - 2*x**3 - 10*x + 9 g = x**4 - 2*x + 9 h = x**3 + 5*x F, G, H = map(Poly, (f, g, h)) assert G.compose(H) == F assert compose(g, h) == f assert compose(g, h, x) == f assert compose(g, h, (x,)) == f assert compose(G, H) == F assert compose(g, h, polys=True) == F assert compose(G, H, polys=False) == f assert F.decompose() == [G, H] assert decompose(f) == [g, h] assert decompose(f, x) == [g, h] assert decompose(f, (x,)) == [g, h] assert decompose(F) == [G, H] assert decompose(f, polys=True) == [G, H] assert decompose(F, polys=False) == [g, h] raises(ComputationFailed, lambda: compose(4, 2)) raises(ComputationFailed, lambda: decompose(4)) assert compose(x**2 - y**2, x - y, x, y) == x**2 - 2*x*y assert compose(x**2 - y**2, x - y, y, x) == -y**2 + 2*x*y def test_shift(): assert Poly(x**2 - 2*x + 1, x).shift(2) == Poly(x**2 + 2*x + 1, x) def test_sturm(): f, F = x, Poly(x, domain='QQ') g, G = 1, Poly(1, x, domain='QQ') assert F.sturm() == [F, G] assert sturm(f) == [f, g] assert sturm(f, x) == [f, g] assert sturm(f, (x,)) == [f, g] assert sturm(F) == [F, G] assert sturm(f, polys=True) == [F, G] assert sturm(F, polys=False) == [f, g] raises(ComputationFailed, lambda: sturm(4)) raises(DomainError, lambda: sturm(f, auto=False)) f = Poly(S(1024)/(15625*pi**8)*x**5 - S(4096)/(625*pi**8)*x**4 + S(32)/(15625*pi**4)*x**3 - S(128)/(625*pi**4)*x**2 + S(1)/62500*x - S(1)/625, x, domain='ZZ(pi)') assert sturm(f) == \ [Poly(x**3 - 100*x**2 + pi**4/64*x - 25*pi**4/16, x, domain='ZZ(pi)'), Poly(3*x**2 - 200*x + pi**4/64, x, domain='ZZ(pi)'), Poly((S(20000)/9 - pi**4/96)*x + 25*pi**4/18, x, domain='ZZ(pi)'), Poly((-3686400000000*pi**4 - 11520000*pi**8 - 9*pi**12)/(26214400000000 - 245760000*pi**4 + 576*pi**8), x, domain='ZZ(pi)')] def test_gff(): f = x**5 + 2*x**4 - x**3 - 2*x**2 assert Poly(f).gff_list() == [(Poly(x), 1), (Poly(x + 2), 4)] assert gff_list(f) == [(x, 1), (x + 2, 4)] raises(NotImplementedError, lambda: gff(f)) f = x*(x - 1)**3*(x - 2)**2*(x - 4)**2*(x - 5) assert Poly(f).gff_list() == [( Poly(x**2 - 5*x + 4), 1), (Poly(x**2 - 5*x + 4), 2), (Poly(x), 3)] assert gff_list(f) == [(x**2 - 5*x + 4, 1), (x**2 - 5*x + 4, 2), (x, 3)] raises(NotImplementedError, lambda: gff(f)) def test_sqf_norm(): assert sqf_norm(x**2 - 2, extension=sqrt(3)) == \ (1, x**2 - 2*sqrt(3)*x + 1, x**4 - 10*x**2 + 1) assert sqf_norm(x**2 - 3, extension=sqrt(2)) == \ (1, x**2 - 2*sqrt(2)*x - 1, x**4 - 10*x**2 + 1) assert Poly(x**2 - 2, extension=sqrt(3)).sqf_norm() == \ (1, Poly(x**2 - 2*sqrt(3)*x + 1, x, extension=sqrt(3)), Poly(x**4 - 10*x**2 + 1, x, domain='QQ')) assert Poly(x**2 - 3, extension=sqrt(2)).sqf_norm() == \ (1, Poly(x**2 - 2*sqrt(2)*x - 1, x, extension=sqrt(2)), Poly(x**4 - 10*x**2 + 1, x, domain='QQ')) def test_sqf(): f = x**5 - x**3 - x**2 + 1 g = x**3 + 2*x**2 + 2*x + 1 h = x - 1 p = x**4 + x**3 - x - 1 F, G, H, P = map(Poly, (f, g, h, p)) assert F.sqf_part() == P assert sqf_part(f) == p assert sqf_part(f, x) == p assert sqf_part(f, (x,)) == p assert sqf_part(F) == P assert sqf_part(f, polys=True) == P assert sqf_part(F, polys=False) == p assert F.sqf_list() == (1, [(G, 1), (H, 2)]) assert sqf_list(f) == (1, [(g, 1), (h, 2)]) assert sqf_list(f, x) == (1, [(g, 1), (h, 2)]) assert sqf_list(f, (x,)) == (1, [(g, 1), (h, 2)]) assert sqf_list(F) == (1, [(G, 1), (H, 2)]) assert sqf_list(f, polys=True) == (1, [(G, 1), (H, 2)]) assert sqf_list(F, polys=False) == (1, [(g, 1), (h, 2)]) assert F.sqf_list_include() == [(G, 1), (H, 2)] raises(ComputationFailed, lambda: sqf_part(4)) assert sqf(1) == 1 assert sqf_list(1) == (1, []) assert sqf((2*x**2 + 2)**7) == 128*(x**2 + 1)**7 assert sqf(f) == g*h**2 assert sqf(f, x) == g*h**2 assert sqf(f, (x,)) == g*h**2 d = x**2 + y**2 assert sqf(f/d) == (g*h**2)/d assert sqf(f/d, x) == (g*h**2)/d assert sqf(f/d, (x,)) == (g*h**2)/d assert sqf(x - 1) == x - 1 assert sqf(-x - 1) == -x - 1 assert sqf(x - 1) == x - 1 assert sqf(6*x - 10) == Mul(2, 3*x - 5, evaluate=False) assert sqf((6*x - 10)/(3*x - 6)) == S(2)/3*((3*x - 5)/(x - 2)) assert sqf(Poly(x**2 - 2*x + 1)) == (x - 1)**2 f = 3 + x - x*(1 + x) + x**2 assert sqf(f) == 3 f = (x**2 + 2*x + 1)**20000000000 assert sqf(f) == (x + 1)**40000000000 assert sqf_list(f) == (1, [(x + 1, 40000000000)]) def test_factor(): f = x**5 - x**3 - x**2 + 1 u = x + 1 v = x - 1 w = x**2 + x + 1 F, U, V, W = map(Poly, (f, u, v, w)) assert F.factor_list() == (1, [(U, 1), (V, 2), (W, 1)]) assert factor_list(f) == (1, [(u, 1), (v, 2), (w, 1)]) assert factor_list(f, x) == (1, [(u, 1), (v, 2), (w, 1)]) assert factor_list(f, (x,)) == (1, [(u, 1), (v, 2), (w, 1)]) assert factor_list(F) == (1, [(U, 1), (V, 2), (W, 1)]) assert factor_list(f, polys=True) == (1, [(U, 1), (V, 2), (W, 1)]) assert factor_list(F, polys=False) == (1, [(u, 1), (v, 2), (w, 1)]) assert F.factor_list_include() == [(U, 1), (V, 2), (W, 1)] assert factor_list(1) == (1, []) assert factor_list(6) == (6, []) assert factor_list(sqrt(3), x) == (1, [(3, S.Half)]) assert factor_list((-1)**x, x) == (1, [(-1, x)]) assert factor_list((2*x)**y, x) == (1, [(2, y), (x, y)]) assert factor_list(sqrt(x*y), x) == (1, [(x*y, S.Half)]) assert factor(6) == 6 and factor(6).is_Integer assert factor_list(3*x) == (3, [(x, 1)]) assert factor_list(3*x**2) == (3, [(x, 2)]) assert factor(3*x) == 3*x assert factor(3*x**2) == 3*x**2 assert factor((2*x**2 + 2)**7) == 128*(x**2 + 1)**7 assert factor(f) == u*v**2*w assert factor(f, x) == u*v**2*w assert factor(f, (x,)) == u*v**2*w g, p, q, r = x**2 - y**2, x - y, x + y, x**2 + 1 assert factor(f/g) == (u*v**2*w)/(p*q) assert factor(f/g, x) == (u*v**2*w)/(p*q) assert factor(f/g, (x,)) == (u*v**2*w)/(p*q) p = Symbol('p', positive=True) i = Symbol('i', integer=True) r = Symbol('r', real=True) assert factor(sqrt(x*y)).is_Pow is True assert factor(sqrt(3*x**2 - 3)) == sqrt(3)*sqrt((x - 1)*(x + 1)) assert factor(sqrt(3*x**2 + 3)) == sqrt(3)*sqrt(x**2 + 1) assert factor((y*x**2 - y)**i) == y**i*(x - 1)**i*(x + 1)**i assert factor((y*x**2 + y)**i) == y**i*(x**2 + 1)**i assert factor((y*x**2 - y)**t) == (y*(x - 1)*(x + 1))**t assert factor((y*x**2 + y)**t) == (y*(x**2 + 1))**t f = sqrt(expand((r**2 + 1)*(p + 1)*(p - 1)*(p - 2)**3)) g = sqrt((p - 2)**3*(p - 1))*sqrt(p + 1)*sqrt(r**2 + 1) assert factor(f) == g assert factor(g) == g f = sqrt(expand((x - 1)**5*(r**2 + 1))) g = sqrt(r**2 + 1)*(x - 1)**(S(5)/2) assert factor(f) == g assert factor(g) == g f = Poly(sin(1)*x + 1, x, domain=EX) assert f.factor_list() == (1, [(f, 1)]) f = x**4 + 1 assert factor(f) == f assert factor(f, extension=I) == (x**2 - I)*(x**2 + I) assert factor(f, gaussian=True) == (x**2 - I)*(x**2 + I) assert factor( f, extension=sqrt(2)) == (x**2 + sqrt(2)*x + 1)*(x**2 - sqrt(2)*x + 1) f = x**2 + 2*sqrt(2)*x + 2 assert factor(f, extension=sqrt(2)) == (x + sqrt(2))**2 assert factor(f**3, extension=sqrt(2)) == (x + sqrt(2))**6 assert factor(x**2 - 2*y**2, extension=sqrt(2)) == \ (x + sqrt(2)*y)*(x - sqrt(2)*y) assert factor(2*x**2 - 4*y**2, extension=sqrt(2)) == \ 2*((x + sqrt(2)*y)*(x - sqrt(2)*y)) assert factor(x - 1) == x - 1 assert factor(-x - 1) == -x - 1 assert factor(x - 1) == x - 1 assert factor(6*x - 10) == Mul(2, 3*x - 5, evaluate=False) assert factor(x**11 + x + 1, modulus=65537, symmetric=True) == \ (x**2 + x + 1)*(x**9 - x**8 + x**6 - x**5 + x**3 - x** 2 + 1) assert factor(x**11 + x + 1, modulus=65537, symmetric=False) == \ (x**2 + x + 1)*(x**9 + 65536*x**8 + x**6 + 65536*x**5 + x**3 + 65536*x** 2 + 1) f = x/pi + x*sin(x)/pi g = y/(pi**2 + 2*pi + 1) + y*sin(x)/(pi**2 + 2*pi + 1) assert factor(f) == x*(sin(x) + 1)/pi assert factor(g) == y*(sin(x) + 1)/(pi + 1)**2 assert factor(Eq( x**2 + 2*x + 1, x**3 + 1)) == Eq((x + 1)**2, (x + 1)*(x**2 - x + 1)) f = (x**2 - 1)/(x**2 + 4*x + 4) assert factor(f) == (x + 1)*(x - 1)/(x + 2)**2 assert factor(f, x) == (x + 1)*(x - 1)/(x + 2)**2 f = 3 + x - x*(1 + x) + x**2 assert factor(f) == 3 assert factor(f, x) == 3 assert factor(1/(x**2 + 2*x + 1/x) - 1) == -((1 - x + 2*x**2 + x**3)/(1 + 2*x**2 + x**3)) assert factor(f, expand=False) == f raises(PolynomialError, lambda: factor(f, x, expand=False)) raises(FlagError, lambda: factor(x**2 - 1, polys=True)) assert factor([x, Eq(x**2 - y**2, Tuple(x**2 - z**2, 1/x + 1/y))]) == \ [x, Eq((x - y)*(x + y), Tuple((x - z)*(x + z), (x + y)/x/y))] assert not isinstance( Poly(x**3 + x + 1).factor_list()[1][0][0], PurePoly) is True assert isinstance( PurePoly(x**3 + x + 1).factor_list()[1][0][0], PurePoly) is True assert factor(sqrt(-x)) == sqrt(-x) # issue 2818 e = (-2*x*(-x + 1)*(x - 1)*(-x*(-x + 1)*(x - 1) - x*(x - 1)**2)*(x**2*(x - 1) - x*(x - 1) - x) - (-2*x**2*(x - 1)**2 - x*(-x + 1)*(-x*(-x + 1) + x*(x - 1)))*(x**2*(x - 1)**4 - x*(-x*(-x + 1)*(x - 1) - x*(x - 1)**2))) assert factor(e) == 0 # deep option assert factor(sin(x**2 + x) + x, deep=True) == sin(x*(x + 1)) + x def test_factor_large(): f = (x**2 + 4*x + 4)**10000000*(x**2 + 1)*(x**2 + 2*x + 1)**1234567 g = ((x**2 + 2*x + 1)**3000*y**2 + (x**2 + 2*x + 1)**3000*2*y + ( x**2 + 2*x + 1)**3000) assert factor(f) == (x + 2)**20000000*(x**2 + 1)*(x + 1)**2469134 assert factor(g) == (x + 1)**6000*(y + 1)**2 assert factor_list( f) == (1, [(x + 1, 2469134), (x + 2, 20000000), (x**2 + 1, 1)]) assert factor_list(g) == (1, [(y + 1, 2), (x + 1, 6000)]) f = (x**2 - y**2)**200000*(x**7 + 1) g = (x**2 + y**2)**200000*(x**7 + 1) assert factor(f) == \ (x + 1)*(x - y)**200000*(x + y)**200000*(x**6 - x**5 + x**4 - x**3 + x**2 - x + 1) assert factor(g, gaussian=True) == \ (x + 1)*(x - I*y)**200000*(x + I*y)**200000*(x**6 - x**5 + x**4 - x**3 + x**2 - x + 1) assert factor_list(f) == \ (1, [(x + 1, 1), (x - y, 200000), (x + y, 200000), (x**6 - x**5 + x**4 - x**3 + x**2 - x + 1, 1)]) assert factor_list(g, gaussian=True) == \ (1, [(x + 1, 1), (x - I*y, 200000), (x + I*y, 200000), ( x**6 - x**5 + x**4 - x**3 + x**2 - x + 1, 1)]) @XFAIL def test_factor_noeval(): assert factor(6*x - 10) == 2*(3*x - 5) assert factor((6*x - 10)/(3*x - 6)) == S(2)/3*((3*x - 5)/(x - 2)) def test_intervals(): assert intervals(0) == [] assert intervals(1) == [] assert intervals(x, sqf=True) == [(0, 0)] assert intervals(x) == [((0, 0), 1)] assert intervals(x**128) == [((0, 0), 128)] assert intervals([x**2, x**4]) == [((0, 0), {0: 2, 1: 4})] f = Poly((2*x/5 - S(17)/3)*(4*x + S(1)/257)) assert f.intervals(sqf=True) == [(-1, 0), (14, 15)] assert f.intervals() == [((-1, 0), 1), ((14, 15), 1)] assert f.intervals(fast=True, sqf=True) == [(-1, 0), (14, 15)] assert f.intervals(fast=True) == [((-1, 0), 1), ((14, 15), 1)] assert f.intervals(eps=S(1)/10) == f.intervals(eps=0.1) == \ [((-S(1)/258, 0), 1), ((S(85)/6, S(85)/6), 1)] assert f.intervals(eps=S(1)/100) == f.intervals(eps=0.01) == \ [((-S(1)/258, 0), 1), ((S(85)/6, S(85)/6), 1)] assert f.intervals(eps=S(1)/1000) == f.intervals(eps=0.001) == \ [((-S(1)/1005, 0), 1), ((S(85)/6, S(85)/6), 1)] assert f.intervals(eps=S(1)/10000) == f.intervals(eps=0.0001) == \ [((-S(1)/1028, -S(1)/1028), 1), ((S(85)/6, S(85)/6), 1)] f = (2*x/5 - S(17)/3)*(4*x + S(1)/257) assert intervals(f, sqf=True) == [(-1, 0), (14, 15)] assert intervals(f) == [((-1, 0), 1), ((14, 15), 1)] assert intervals(f, eps=S(1)/10) == intervals(f, eps=0.1) == \ [((-S(1)/258, 0), 1), ((S(85)/6, S(85)/6), 1)] assert intervals(f, eps=S(1)/100) == intervals(f, eps=0.01) == \ [((-S(1)/258, 0), 1), ((S(85)/6, S(85)/6), 1)] assert intervals(f, eps=S(1)/1000) == intervals(f, eps=0.001) == \ [((-S(1)/1005, 0), 1), ((S(85)/6, S(85)/6), 1)] assert intervals(f, eps=S(1)/10000) == intervals(f, eps=0.0001) == \ [((-S(1)/1028, -S(1)/1028), 1), ((S(85)/6, S(85)/6), 1)] f = Poly((x**2 - 2)*(x**2 - 3)**7*(x + 1)*(7*x + 3)**3) assert f.intervals() == \ [((-2, -S(3)/2), 7), ((-S(3)/2, -1), 1), ((-1, -1), 1), ((-1, 0), 3), ((1, S(3)/2), 1), ((S(3)/2, 2), 7)] assert intervals([x**5 - 200, x**5 - 201]) == \ [((S(75)/26, S(101)/35), {0: 1}), ((S(283)/98, S(26)/9), {1: 1})] assert intervals([x**5 - 200, x**5 - 201], fast=True) == \ [((S(75)/26, S(101)/35), {0: 1}), ((S(283)/98, S(26)/9), {1: 1})] assert intervals([x**2 - 200, x**2 - 201]) == \ [((-S(71)/5, -S(85)/6), {1: 1}), ((-S(85)/6, -14), {0: 1}), ((14, S(85)/6), {0: 1}), ((S(85)/6, S(71)/5), {1: 1})] assert intervals([x + 1, x + 2, x - 1, x + 1, 1, x - 1, x - 1, (x - 2)**2]) == \ [((-2, -2), {1: 1}), ((-1, -1), {0: 1, 3: 1}), ((1, 1), {2: 1, 5: 1, 6: 1}), ((2, 2), {7: 2})] f, g, h = x**2 - 2, x**4 - 4*x**2 + 4, x - 1 assert intervals(f, inf=S(7)/4, sqf=True) == [] assert intervals(f, inf=S(7)/5, sqf=True) == [(S(7)/5, S(3)/2)] assert intervals(f, sup=S(7)/4, sqf=True) == [(-2, -1), (1, S(3)/2)] assert intervals(f, sup=S(7)/5, sqf=True) == [(-2, -1)] assert intervals(g, inf=S(7)/4) == [] assert intervals(g, inf=S(7)/5) == [((S(7)/5, S(3)/2), 2)] assert intervals(g, sup=S(7)/4) == [((-2, -1), 2), ((1, S(3)/2), 2)] assert intervals(g, sup=S(7)/5) == [((-2, -1), 2)] assert intervals([g, h], inf=S(7)/4) == [] assert intervals([g, h], inf=S(7)/5) == [((S(7)/5, S(3)/2), {0: 2})] assert intervals([g, h], sup=S( 7)/4) == [((-2, -1), {0: 2}), ((1, 1), {1: 1}), ((1, S(3)/2), {0: 2})] assert intervals( [g, h], sup=S(7)/5) == [((-2, -1), {0: 2}), ((1, 1), {1: 1})] assert intervals([x + 2, x**2 - 2]) == \ [((-2, -2), {0: 1}), ((-2, -1), {1: 1}), ((1, 2), {1: 1})] assert intervals([x + 2, x**2 - 2], strict=True) == \ [((-2, -2), {0: 1}), ((-S(3)/2, -1), {1: 1}), ((1, 2), {1: 1})] f = 7*z**4 - 19*z**3 + 20*z**2 + 17*z + 20 assert intervals(f) == [] real_part, complex_part = intervals(f, all=True, sqf=True) assert real_part == [] assert all(re(a) < re(r) < re(b) and im( a) < im(r) < im(b) for (a, b), r in zip(complex_part, nroots(f))) assert complex_part == [(-S(40)/7 - 40*I/7, 0), (-S(40)/7, 40*I/7), (-40*I/7, S(40)/7), (0, S(40)/7 + 40*I/7)] real_part, complex_part = intervals(f, all=True, sqf=True, eps=S(1)/10) assert real_part == [] assert all(re(a) < re(r) < re(b) and im( a) < im(r) < im(b) for (a, b), r in zip(complex_part, nroots(f))) raises(ValueError, lambda: intervals(x**2 - 2, eps=10**-100000)) raises(ValueError, lambda: Poly(x**2 - 2).intervals(eps=10**-100000)) raises( ValueError, lambda: intervals([x**2 - 2, x**2 - 3], eps=10**-100000)) def test_refine_root(): f = Poly(x**2 - 2) assert f.refine_root(1, 2, steps=0) == (1, 2) assert f.refine_root(-2, -1, steps=0) == (-2, -1) assert f.refine_root(1, 2, steps=None) == (1, S(3)/2) assert f.refine_root(-2, -1, steps=None) == (-S(3)/2, -1) assert f.refine_root(1, 2, steps=1) == (1, S(3)/2) assert f.refine_root(-2, -1, steps=1) == (-S(3)/2, -1) assert f.refine_root(1, 2, steps=1, fast=True) == (1, S(3)/2) assert f.refine_root(-2, -1, steps=1, fast=True) == (-S(3)/2, -1) assert f.refine_root(1, 2, eps=S(1)/100) == (S(24)/17, S(17)/12) assert f.refine_root(1, 2, eps=1e-2) == (S(24)/17, S(17)/12) raises(PolynomialError, lambda: (f**2).refine_root(1, 2, check_sqf=True)) raises(RefinementFailed, lambda: (f**2).refine_root(1, 2)) raises(RefinementFailed, lambda: (f**2).refine_root(2, 3)) f = x**2 - 2 assert refine_root(f, 1, 2, steps=1) == (1, S(3)/2) assert refine_root(f, -2, -1, steps=1) == (-S(3)/2, -1) assert refine_root(f, 1, 2, steps=1, fast=True) == (1, S(3)/2) assert refine_root(f, -2, -1, steps=1, fast=True) == (-S(3)/2, -1) assert refine_root(f, 1, 2, eps=S(1)/100) == (S(24)/17, S(17)/12) assert refine_root(f, 1, 2, eps=1e-2) == (S(24)/17, S(17)/12) raises(PolynomialError, lambda: refine_root(1, 7, 8, eps=S(1)/100)) raises(ValueError, lambda: Poly(f).refine_root(1, 2, eps=10**-100000)) raises(ValueError, lambda: refine_root(f, 1, 2, eps=10**-100000)) def test_count_roots(): assert count_roots(x**2 - 2) == 2 assert count_roots(x**2 - 2, inf=-oo) == 2 assert count_roots(x**2 - 2, sup=+oo) == 2 assert count_roots(x**2 - 2, inf=-oo, sup=+oo) == 2 assert count_roots(x**2 - 2, inf=-2) == 2 assert count_roots(x**2 - 2, inf=-1) == 1 assert count_roots(x**2 - 2, sup=1) == 1 assert count_roots(x**2 - 2, sup=2) == 2 assert count_roots(x**2 - 2, inf=-1, sup=1) == 0 assert count_roots(x**2 - 2, inf=-2, sup=2) == 2 assert count_roots(x**2 - 2, inf=-1, sup=1) == 0 assert count_roots(x**2 - 2, inf=-2, sup=2) == 2 assert count_roots(x**2 + 2) == 0 assert count_roots(x**2 + 2, inf=-2*I) == 2 assert count_roots(x**2 + 2, sup=+2*I) == 2 assert count_roots(x**2 + 2, inf=-2*I, sup=+2*I) == 2 assert count_roots(x**2 + 2, inf=0) == 0 assert count_roots(x**2 + 2, sup=0) == 0 assert count_roots(x**2 + 2, inf=-I) == 1 assert count_roots(x**2 + 2, sup=+I) == 1 assert count_roots(x**2 + 2, inf=+I/2, sup=+I) == 0 assert count_roots(x**2 + 2, inf=-I, sup=-I/2) == 0 raises(PolynomialError, lambda: count_roots(1)) def test_Poly_root(): f = Poly(2*x**3 - 7*x**2 + 4*x + 4) assert f.root(0) == -S(1)/2 assert f.root(1) == 2 assert f.root(2) == 2 raises(IndexError, lambda: f.root(3)) assert Poly(x**5 + x + 1).root(0) == RootOf(x**3 - x**2 + 1, 0) def test_real_roots(): assert real_roots(x) == [0] assert real_roots(x, multiple=False) == [(0, 1)] assert real_roots(x**3) == [0, 0, 0] assert real_roots(x**3, multiple=False) == [(0, 3)] assert real_roots(x*(x**3 + x + 3)) == [RootOf(x**3 + x + 3, 0), 0] assert real_roots(x*(x**3 + x + 3), multiple=False) == [(RootOf( x**3 + x + 3, 0), 1), (0, 1)] assert real_roots( x**3*(x**3 + x + 3)) == [RootOf(x**3 + x + 3, 0), 0, 0, 0] assert real_roots(x**3*(x**3 + x + 3), multiple=False) == [(RootOf( x**3 + x + 3, 0), 1), (0, 3)] f = 2*x**3 - 7*x**2 + 4*x + 4 g = x**3 + x + 1 assert Poly(f).real_roots() == [-S(1)/2, 2, 2] assert Poly(g).real_roots() == [RootOf(g, 0)] def test_all_roots(): f = 2*x**3 - 7*x**2 + 4*x + 4 g = x**3 + x + 1 assert Poly(f).all_roots() == [-S(1)/2, 2, 2] assert Poly(g).all_roots() == [RootOf(g, 0), RootOf(g, 1), RootOf(g, 2)] def test_nroots(): assert Poly(0, x).nroots() == [] assert Poly(1, x).nroots() == [] assert Poly(x**2 - 1, x).nroots() == [-1.0, 1.0] assert Poly(x**2 + 1, x).nroots() == [-1.0*I, 1.0*I] roots, error = Poly(x**2 - 1, x).nroots(error=True) assert roots == [-1.0, 1.0] and error < 1e25 roots, error = Poly(x**2 + 1, x).nroots(error=True) assert roots == [-1.0*I, 1.0*I] and error < 1e25 roots, error = Poly(x**2/3 - S(1)/3, x).nroots(error=True) assert roots == [-1.0, 1.0] and error < 1e25 roots, error = Poly(x**2/3 + S(1)/3, x).nroots(error=True) assert roots == [-1.0*I, 1.0*I] and error < 1e25 assert Poly(x**2 + 2*I, x).nroots() == [-1.0 + 1.0*I, 1.0 - 1.0*I] assert Poly( x**2 + 2*I, x, extension=I).nroots() == [-1.0 + 1.0*I, 1.0 - 1.0*I] assert Poly(0.2*x + 0.1).nroots() == [-0.5] roots = nroots(x**5 + x + 1, n=5) eps = Float("1e-5") assert re(roots[0]).epsilon_eq(-0.75487, eps) is True assert im(roots[0]) == 0.0 assert re(roots[1]) == -0.5 assert im(roots[1]).epsilon_eq(-0.86602, eps) is True assert re(roots[2]) == -0.5 assert im(roots[2]).epsilon_eq(+0.86602, eps) is True assert re(roots[3]).epsilon_eq(+0.87743, eps) is True assert im(roots[3]).epsilon_eq(-0.74486, eps) is True assert re(roots[4]).epsilon_eq(+0.87743, eps) is True assert im(roots[4]).epsilon_eq(+0.74486, eps) is True eps = Float("1e-6") assert re(roots[0]).epsilon_eq(-0.75487, eps) is False assert im(roots[0]) == 0.0 assert re(roots[1]) == -0.5 assert im(roots[1]).epsilon_eq(-0.86602, eps) is False assert re(roots[2]) == -0.5 assert im(roots[2]).epsilon_eq(+0.86602, eps) is False assert re(roots[3]).epsilon_eq(+0.87743, eps) is False assert im(roots[3]).epsilon_eq(-0.74486, eps) is False assert re(roots[4]).epsilon_eq(+0.87743, eps) is False assert im(roots[4]).epsilon_eq(+0.74486, eps) is False raises(DomainError, lambda: Poly(x + y, x).nroots()) raises(MultivariatePolynomialError, lambda: Poly(x + y).nroots()) assert nroots(x**2 - 1) == [-1.0, 1.0] roots, error = nroots(x**2 - 1, error=True) assert roots == [-1.0, 1.0] and error < 1e25 assert nroots(x + I) == [-1.0*I] assert nroots(x + 2*I) == [-2.0*I] raises(PolynomialError, lambda: nroots(0)) def test_ground_roots(): f = x**6 - 4*x**4 + 4*x**3 - x**2 assert Poly(f).ground_roots() == {S(1): 2, S(0): 2} assert ground_roots(f) == {S(1): 2, S(0): 2} def test_nth_power_roots_poly(): f = x**4 - x**2 + 1 f_2 = (x**2 - x + 1)**2 f_3 = (x**2 + 1)**2 f_4 = (x**2 + x + 1)**2 f_12 = (x - 1)**4 assert nth_power_roots_poly(f, 1) == f raises(ValueError, lambda: nth_power_roots_poly(f, 0)) raises(ValueError, lambda: nth_power_roots_poly(f, x)) assert factor(nth_power_roots_poly(f, 2)) == f_2 assert factor(nth_power_roots_poly(f, 3)) == f_3 assert factor(nth_power_roots_poly(f, 4)) == f_4 assert factor(nth_power_roots_poly(f, 12)) == f_12 raises(MultivariatePolynomialError, lambda: nth_power_roots_poly( x + y, 2, x, y)) def test_torational_factor_list(): p = expand(((x**2-1)*(x-2)).subs({x:x*(1 + sqrt(2))})) assert _torational_factor_list(p, x) == (-2, [ (-x*(1 + sqrt(2))/2 + 1, 1), (-x*(1 + sqrt(2)) - 1, 1), (-x*(1 + sqrt(2)) + 1, 1)]) p = expand(((x**2-1)*(x-2)).subs({x:x*(1 + 2**Rational(1, 4))})) assert _torational_factor_list(p, x) is None def test_cancel(): assert cancel(0) == 0 assert cancel(7) == 7 assert cancel(x) == x assert cancel(oo) == oo assert cancel((2, 3)) == (1, 2, 3) assert cancel((1, 0), x) == (1, 1, 0) assert cancel((0, 1), x) == (1, 0, 1) f, g, p, q = 4*x**2 - 4, 2*x - 2, 2*x + 2, 1 F, G, P, Q = [ Poly(u, x) for u in (f, g, p, q) ] assert F.cancel(G) == (1, P, Q) assert cancel((f, g)) == (1, p, q) assert cancel((f, g), x) == (1, p, q) assert cancel((f, g), (x,)) == (1, p, q) assert cancel((F, G)) == (1, P, Q) assert cancel((f, g), polys=True) == (1, P, Q) assert cancel((F, G), polys=False) == (1, p, q) f = (x**2 - 2)/(x + sqrt(2)) assert cancel(f) == f assert cancel(f, greedy=False) == x - sqrt(2) f = (x**2 - 2)/(x - sqrt(2)) assert cancel(f) == f assert cancel(f, greedy=False) == x + sqrt(2) assert cancel((x**2/4 - 1, x/2 - 1)) == (S(1)/2, x + 2, 1) assert cancel((x**2 - y)/(x - y)) == 1/(x - y)*(x**2 - y) assert cancel((x**2 - y**2)/(x - y), x) == x + y assert cancel((x**2 - y**2)/(x - y), y) == x + y assert cancel((x**2 - y**2)/(x - y)) == x + y assert cancel((x**3 - 1)/(x**2 - 1)) == (x**2 + x + 1)/(x + 1) assert cancel((x**3/2 - S(1)/2)/(x**2 - 1)) == (x**2 + x + 1)/(2*x + 2) assert cancel((exp(2*x) + 2*exp(x) + 1)/(exp(x) + 1)) == exp(x) + 1 f = Poly(x**2 - a**2, x) g = Poly(x - a, x) F = Poly(x + a, x) G = Poly(1, x) assert cancel((f, g)) == (1, F, G) f = x**3 + (sqrt(2) - 2)*x**2 - (2*sqrt(2) + 3)*x - 3*sqrt(2) g = x**2 - 2 assert cancel((f, g), extension=True) == (1, x**2 - 2*x - 3, x - sqrt(2)) f = Poly(-2*x + 3, x) g = Poly(-x**9 + x**8 + x**6 - x**5 + 2*x**2 - 3*x + 1, x) assert cancel((f, g)) == (1, -f, -g) f = Poly(y, y, domain='ZZ(x)') g = Poly(1, y, domain='ZZ[x]') assert f.cancel( g) == (1, Poly(y, y, domain='ZZ(x)'), Poly(1, y, domain='ZZ(x)')) assert f.cancel(g, include=True) == ( Poly(y, y, domain='ZZ(x)'), Poly(1, y, domain='ZZ(x)')) f = Poly(5*x*y + x, y, domain='ZZ(x)') g = Poly(2*x**2*y, y, domain='ZZ(x)') assert f.cancel(g, include=True) == ( Poly(5*y + 1, y, domain='ZZ(x)'), Poly(2*x*y, y, domain='ZZ(x)')) f = -(-2*x - 4*y + 0.005*(z - y)**2)/((z - y)*(-z + y + 2)) assert cancel(f).is_Mul == True P = tanh(x - 3.0) Q = tanh(x + 3.0) f = ((-2*P**2 + 2)*(-P**2 + 1)*Q**2/2 + (-2*P**2 + 2)*(-2*Q**2 + 2)*P*Q - (-2*P**2 + 2)*P**2*Q**2 + (-2*Q**2 + 2)*(-Q**2 + 1)*P**2/2 - (-2*Q**2 + 2)*P**2*Q**2)/(2*sqrt(P**2*Q**2 + 0.0001)) \ + (-(-2*P**2 + 2)*P*Q**2/2 - (-2*Q**2 + 2)*P**2*Q/2)*((-2*P**2 + 2)*P*Q**2/2 + (-2*Q**2 + 2)*P**2*Q/2)/(2*(P**2*Q**2 + 0.0001)**(S(3)/2)) assert cancel(f).is_Mul == True # issue 3923 A = Symbol('A', commutative=False) p1 = Piecewise((A*(x**2 - 1)/(x + 1), x > 1), ((x + 2)/(x**2 + 2*x), True)) p2 = Piecewise((A*(x - 1), x > 1), (1/x, True)) assert cancel(p1) == p2 assert cancel(2*p1) == 2*p2 assert cancel(1 + p1) == 1 + p2 assert cancel((x**2 - 1)/(x + 1)*p1) == (x - 1)*p2 assert cancel((x**2 - 1)/(x + 1) + p1) == (x - 1) + p2 p3 = Piecewise(((x**2 - 1)/(x + 1), x > 1), ((x + 2)/(x**2 + 2*x), True)) p4 = Piecewise(((x - 1), x > 1), (1/x, True)) assert cancel(p3) == p4 assert cancel(2*p3) == 2*p4 assert cancel(1 + p3) == 1 + p4 assert cancel((x**2 - 1)/(x + 1)*p3) == (x - 1)*p4 assert cancel((x**2 - 1)/(x + 1) + p3) == (x - 1) + p4 def test_reduced(): f = 2*x**4 + y**2 - x**2 + y**3 G = [x**3 - x, y**3 - y] Q = [2*x, 1] r = x**2 + y**2 + y assert reduced(f, G) == (Q, r) assert reduced(f, G, x, y) == (Q, r) H = groebner(G) assert H.reduce(f) == (Q, r) Q = [Poly(2*x, x, y), Poly(1, x, y)] r = Poly(x**2 + y**2 + y, x, y) assert _strict_eq(reduced(f, G, polys=True), (Q, r)) assert _strict_eq(reduced(f, G, x, y, polys=True), (Q, r)) H = groebner(G, polys=True) assert _strict_eq(H.reduce(f), (Q, r)) f = 2*x**3 + y**3 + 3*y G = groebner([x**2 + y**2 - 1, x*y - 2]) Q = [x**2 - x*y**3/2 + x*y/2 + y**6/4 - y**4/2 + y**2/4, -y**5/4 + y**3/2 + 3*y/4] r = 0 assert reduced(f, G) == (Q, r) assert G.reduce(f) == (Q, r) assert reduced(f, G, auto=False)[1] != 0 assert G.reduce(f, auto=False)[1] != 0 assert G.contains(f) is True assert G.contains(f + 1) is False assert reduced(1, [1], x) == ([1], 0) raises(ComputationFailed, lambda: reduced(1, [1])) def test_groebner(): assert groebner([], x, y, z) == [] assert groebner([x**2 + 1, y**4*x + x**3], x, y, order='lex') == [1 + x**2, -1 + y**4] assert groebner([x**2 + 1, y**4*x + x**3, x*y*z**3], x, y, z, order='grevlex') == [-1 + y**4, z**3, 1 + x**2] assert groebner([x**2 + 1, y**4*x + x**3], x, y, order='lex', polys=True) == \ [Poly(1 + x**2, x, y), Poly(-1 + y**4, x, y)] assert groebner([x**2 + 1, y**4*x + x**3, x*y*z**3], x, y, z, order='grevlex', polys=True) == \ [Poly(-1 + y**4, x, y, z), Poly(z**3, x, y, z), Poly(1 + x**2, x, y, z)] assert groebner([x**3 - 1, x**2 - 1]) == [x - 1] assert groebner([Eq(x**3, 1), Eq(x**2, 1)]) == [x - 1] F = [3*x**2 + y*z - 5*x - 1, 2*x + 3*x*y + y**2, x - 3*y + x*z - 2*z**2] f = z**9 - x**2*y**3 - 3*x*y**2*z + 11*y*z**2 + x**2*z**2 - 5 G = groebner(F, x, y, z, modulus=7, symmetric=False) assert G == [1 + x + y + 3*z + 2*z**2 + 2*z**3 + 6*z**4 + z**5, 1 + 3*y + y**2 + 6*z**2 + 3*z**3 + 3*z**4 + 3*z**5 + 4*z**6, 1 + 4*y + 4*z + y*z + 4*z**3 + z**4 + z**6, 6 + 6*z + z**2 + 4*z**3 + 3*z**4 + 6*z**5 + 3*z**6 + z**7] Q, r = reduced(f, G, x, y, z, modulus=7, symmetric=False, polys=True) assert sum([ q*g for q, g in zip(Q, G.polys)], r) == Poly(f, modulus=7) F = [x*y - 2*y, 2*y**2 - x**2] assert groebner(F, x, y, order='grevlex') == \ [y**3 - 2*y, x**2 - 2*y**2, x*y - 2*y] assert groebner(F, y, x, order='grevlex') == \ [x**3 - 2*x**2, -x**2 + 2*y**2, x*y - 2*y] assert groebner(F, order='grevlex', field=True) == \ [y**3 - 2*y, x**2 - 2*y**2, x*y - 2*y] assert groebner([1], x) == [1] assert groebner([x**2 + 2.0*y], x, y) == [1.0*x**2 + 2.0*y] raises(ComputationFailed, lambda: groebner([1])) assert groebner([x**2 - 1, x**3 + 1], method='buchberger') == [x + 1] assert groebner([x**2 - 1, x**3 + 1], method='f5b') == [x + 1] raises(ValueError, lambda: groebner([x, y], method='unknown')) def test_fglm(): F = [a + b + c + d, a*b + a*d + b*c + b*d, a*b*c + a*b*d + a*c*d + b*c*d, a*b*c*d - 1] G = groebner(F, a, b, c, d, order=grlex) B = [ 4*a + 3*d**9 - 4*d**5 - 3*d, 4*b + 4*c - 3*d**9 + 4*d**5 + 7*d, 4*c**2 + 3*d**10 - 4*d**6 - 3*d**2, 4*c*d**4 + 4*c - d**9 + 4*d**5 + 5*d, d**12 - d**8 - d**4 + 1, ] assert groebner(F, a, b, c, d, order=lex) == B assert G.fglm(lex) == B F = [9*x**8 + 36*x**7 - 32*x**6 - 252*x**5 - 78*x**4 + 468*x**3 + 288*x**2 - 108*x + 9, -72*t*x**7 - 252*t*x**6 + 192*t*x**5 + 1260*t*x**4 + 312*t*x**3 - 404*t*x**2 - 576*t*x + \ 108*t - 72*x**7 - 256*x**6 + 192*x**5 + 1280*x**4 + 312*x**3 - 576*x + 96] G = groebner(F, t, x, order=grlex) B = [ 203577793572507451707*t + 627982239411707112*x**7 - 666924143779443762*x**6 - \ 10874593056632447619*x**5 + 5119998792707079562*x**4 + 72917161949456066376*x**3 + \ 20362663855832380362*x**2 - 142079311455258371571*x + 183756699868981873194, 9*x**8 + 36*x**7 - 32*x**6 - 252*x**5 - 78*x**4 + 468*x**3 + 288*x**2 - 108*x + 9, ] assert groebner(F, t, x, order=lex) == B assert G.fglm(lex) == B F = [x**2 - x - 3*y + 1, -2*x + y**2 + y - 1] G = groebner(F, x, y, order=lex) B = [ x**2 - x - 3*y + 1, y**2 - 2*x + y - 1, ] assert groebner(F, x, y, order=grlex) == B assert G.fglm(grlex) == B def test_is_zero_dimensional(): assert is_zero_dimensional([x, y], x, y) is True assert is_zero_dimensional([x**3 + y**2], x, y) is False assert is_zero_dimensional([x, y, z], x, y, z) is True assert is_zero_dimensional([x, y, z], x, y, z, t) is False F = [x*y - z, y*z - x, x*y - y] assert is_zero_dimensional(F, x, y, z) is True F = [x**2 - 2*x*z + 5, x*y**2 + y*z**3, 3*y**2 - 8*z**2] assert is_zero_dimensional(F, x, y, z) is True def test_GroebnerBasis(): F = [x*y - 2*y, 2*y**2 - x**2] G = groebner(F, x, y, order='grevlex') H = [y**3 - 2*y, x**2 - 2*y**2, x*y - 2*y] P = [ Poly(h, x, y) for h in H ] assert isinstance(G, GroebnerBasis) is True assert len(G) == 3 assert G[0] == H[0] and not G[0].is_Poly assert G[1] == H[1] and not G[1].is_Poly assert G[2] == H[2] and not G[2].is_Poly assert G[1:] == H[1:] and not any(g.is_Poly for g in G[1:]) assert G[:2] == H[:2] and not any(g.is_Poly for g in G[1:]) assert G.exprs == H assert G.polys == P assert G.gens == (x, y) assert G.domain == ZZ assert G.order == grevlex assert G == H assert G == tuple(H) assert G == P assert G == tuple(P) assert G != [] G = groebner(F, x, y, order='grevlex', polys=True) assert G[0] == P[0] and G[0].is_Poly assert G[1] == P[1] and G[1].is_Poly assert G[2] == P[2] and G[2].is_Poly assert G[1:] == P[1:] and all(g.is_Poly for g in G[1:]) assert G[:2] == P[:2] and all(g.is_Poly for g in G[1:]) def test_poly(): assert poly(x) == Poly(x, x) assert poly(y) == Poly(y, y) assert poly(x + y) == Poly(x + y, x, y) assert poly(x + sin(x)) == Poly(x + sin(x), x, sin(x)) assert poly(x + y, wrt=y) == Poly(x + y, y, x) assert poly(x + sin(x), wrt=sin(x)) == Poly(x + sin(x), sin(x), x) assert poly(x*y + 2*x*z**2 + 17) == Poly(x*y + 2*x*z**2 + 17, x, y, z) assert poly(2*(y + z)**2 - 1) == Poly(2*y**2 + 4*y*z + 2*z**2 - 1, y, z) assert poly( x*(y + z)**2 - 1) == Poly(x*y**2 + 2*x*y*z + x*z**2 - 1, x, y, z) assert poly(2*x*( y + z)**2 - 1) == Poly(2*x*y**2 + 4*x*y*z + 2*x*z**2 - 1, x, y, z) assert poly(2*( y + z)**2 - x - 1) == Poly(2*y**2 + 4*y*z + 2*z**2 - x - 1, x, y, z) assert poly(x*( y + z)**2 - x - 1) == Poly(x*y**2 + 2*x*y*z + x*z**2 - x - 1, x, y, z) assert poly(2*x*(y + z)**2 - x - 1) == Poly(2*x*y**2 + 4*x*y*z + 2* x*z**2 - x - 1, x, y, z) assert poly(x*y + (x + y)**2 + (x + z)**2) == \ Poly(2*x*z + 3*x*y + y**2 + z**2 + 2*x**2, x, y, z) assert poly(x*y*(x + y)*(x + z)**2) == \ Poly(x**3*y**2 + x*y**2*z**2 + y*x**2*z**2 + 2*z*x**2* y**2 + 2*y*z*x**3 + y*x**4, x, y, z) assert poly(Poly(x + y + z, y, x, z)) == Poly(x + y + z, y, x, z) assert poly((x + y)**2, x) == Poly(x**2 + 2*x*y + y**2, x, domain=ZZ[y]) assert poly((x + y)**2, y) == Poly(x**2 + 2*x*y + y**2, y, domain=ZZ[x]) assert poly(1, x) == Poly(1, x) raises(GeneratorsNeeded, lambda: poly(1)) # issue 3085 assert poly(x + y, x, y) == Poly(x + y, x, y) assert poly(x + y, y, x) == Poly(x + y, y, x) def test_keep_coeff(): u = Mul(2, x + 1, evaluate=False) assert _keep_coeff(S(1), x) == x assert _keep_coeff(S(-1), x) == -x assert _keep_coeff(S(1), 2*x) == 2*x assert _keep_coeff(S(2), x/2) == x assert _keep_coeff(S(2), sin(x)) == 2*sin(x) assert _keep_coeff(S(2), x + 1) == u assert _keep_coeff(x, 1/x) == 1 assert _keep_coeff(x + 1, S(2)) == u @XFAIL def test_poly_matching_consistency(): # Test for this issue: # http://code.google.com/p/sympy/issues/detail?id=2415 assert I * Poly(x, x) == Poly(I*x, x) assert Poly(x, x) * I == Poly(I*x, x) @XFAIL def test_issue_2687(): assert expand(factor(expand( (x - I*y)*(z - I*t)), extension=[I])) == -I*t*x - t*y + x*z - I*y*z def test_noncommutative(): class foo(Expr): is_commutative=False e = x/(x + x*y) c = 1/( 1 + y) assert cancel(foo(e)) == foo(c) assert cancel(e + foo(e)) == c + foo(c) assert cancel(e*foo(c)) == c*foo(c) sympy-0.7.4.1/sympy/polys/tests/test_rings.py0000644000175000017500000012101712253362407021472 0ustar georgeskgeorgesk"""Test sparse polynomials. """ from operator import add, mul from sympy.polys.rings import ring, xring, sring, PolyRing, PolyElement from sympy.polys.fields import field, FracField from sympy.polys.domains import ZZ, QQ, RR, FF, EX from sympy.polys.orderings import lex, grlex from sympy.polys.polyerrors import GeneratorsError, GeneratorsNeeded, \ ExactQuotientFailed, MultivariatePolynomialError, CoercionFailed from sympy.utilities.pytest import raises from sympy.core import Symbol, symbols from sympy.core.compatibility import reduce from sympy import sqrt, pi, oo def test_PolyRing___init__(): x, y, z, t = map(Symbol, "xyzt") assert len(PolyRing("x,y,z", ZZ, lex).gens) == 3 assert len(PolyRing(x, ZZ, lex).gens) == 1 assert len(PolyRing(("x", "y", "z"), ZZ, lex).gens) == 3 assert len(PolyRing((x, y, z), ZZ, lex).gens) == 3 raises(GeneratorsNeeded, lambda: PolyRing([], ZZ, lex)) raises(GeneratorsError, lambda: PolyRing(0, ZZ, lex)) assert PolyRing("x", ZZ[t], lex).domain == ZZ[t] assert PolyRing("x", 'ZZ[t]', lex).domain == ZZ[t] assert PolyRing("x", PolyRing("t", ZZ, lex), lex).domain == ZZ[t] raises(GeneratorsError, lambda: PolyRing("x", PolyRing("x", ZZ, lex), lex)) _lex = Symbol("lex") assert PolyRing("x", ZZ, lex).order == lex assert PolyRing("x", ZZ, _lex).order == lex assert PolyRing("x", ZZ, 'lex').order == lex R1 = PolyRing("x,y", ZZ, lex) R2 = PolyRing("x,y", ZZ, lex) R3 = PolyRing("x,y,z", ZZ, lex) assert R1.x == R1.gens[0] assert R1.y == R1.gens[1] assert R1.x == R2.x assert R1.y == R2.y assert R1.x != R3.x assert R1.y != R3.y def test_PolyRing___hash__(): R, x, y, z = ring("x,y,z", QQ) assert hash(R) def test_PolyRing___eq__(): assert ring("x,y,z", QQ)[0] == ring("x,y,z", QQ)[0] assert ring("x,y,z", QQ)[0] is ring("x,y,z", QQ)[0] assert ring("x,y,z", QQ)[0] != ring("x,y,z", ZZ)[0] assert ring("x,y,z", QQ)[0] is not ring("x,y,z", ZZ)[0] assert ring("x,y,z", ZZ)[0] != ring("x,y,z", QQ)[0] assert ring("x,y,z", ZZ)[0] is not ring("x,y,z", QQ)[0] assert ring("x,y,z", QQ)[0] != ring("x,y", QQ)[0] assert ring("x,y,z", QQ)[0] is not ring("x,y", QQ)[0] assert ring("x,y", QQ)[0] != ring("x,y,z", QQ)[0] assert ring("x,y", QQ)[0] is not ring("x,y,z", QQ)[0] def test_PolyRing_ring_new(): R, x, y, z = ring("x,y,z", QQ) assert R.ring_new(7) == R(7) assert R.ring_new(7*x*y*z) == 7*x*y*z f = x**2 + 2*x*y + 3*x + 4*z**2 + 5*z + 6 assert R.ring_new([[[1]], [[2], [3]], [[4, 5, 6]]]) == f assert R.ring_new({(2, 0, 0): 1, (1, 1, 0): 2, (1, 0, 0): 3, (0, 0, 2): 4, (0, 0, 1): 5, (0, 0, 0): 6}) == f assert R.ring_new([((2, 0, 0), 1), ((1, 1, 0), 2), ((1, 0, 0), 3), ((0, 0, 2), 4), ((0, 0, 1), 5), ((0, 0, 0), 6)]) == f def test_PolyRing_drop(): R, x,y,z = ring("x,y,z", ZZ) assert R.drop(x) == PolyRing("y,z", ZZ, lex) assert R.drop(y) == PolyRing("x,z", ZZ, lex) assert R.drop(z) == PolyRing("x,y", ZZ, lex) assert R.drop(0) == PolyRing("y,z", ZZ, lex) assert R.drop(0).drop(0) == PolyRing("z", ZZ, lex) assert R.drop(0).drop(0).drop(0) == ZZ assert R.drop(1) == PolyRing("x,z", ZZ, lex) assert R.drop(2) == PolyRing("x,y", ZZ, lex) assert R.drop(2).drop(1) == PolyRing("x", ZZ, lex) assert R.drop(2).drop(1).drop(0) == ZZ raises(ValueError, lambda: R.drop(3)) raises(ValueError, lambda: R.drop(x).drop(y)) def test_PolyRing___getitem__(): R, x,y,z = ring("x,y,z", ZZ) assert R[0:] == PolyRing("x,y,z", ZZ, lex) assert R[1:] == PolyRing("y,z", ZZ, lex) assert R[2:] == PolyRing("z", ZZ, lex) assert R[3:] == ZZ def test_PolyRing_is_(): R = PolyRing("x", QQ, lex) assert R.is_univariate is True assert R.is_multivariate is False R = PolyRing("x,y,z", QQ, lex) assert R.is_univariate is False assert R.is_multivariate is True def test_PolyRing_add(): R, x = ring("x", ZZ) F = [ x**2 + 2*i + 3 for i in range(4) ] assert R.add(F) == reduce(add, F) == 4*x**2 + 24 def test_PolyRing_mul(): R, x = ring("x", ZZ) F = [ x**2 + 2*i + 3 for i in range(4) ] assert R.mul(F) == reduce(mul, F) == x**8 + 24*x**6 + 206*x**4 + 744*x**2 + 945 def test_sring(): x, y, z, t = symbols("x,y,z,t") R = PolyRing("x,y,z", ZZ, lex) assert sring(x + 2*y + 3*z) == (R, R.x + 2*R.y + 3*R.z) R = PolyRing("x,y,z", QQ, lex) assert sring(x + 2*y + z/3) == (R, R.x + 2*R.y + R.z/3) assert sring([x, 2*y, z/3]) == (R, [R.x, 2*R.y, R.z/3]) Rt = PolyRing("t", ZZ, lex) R = PolyRing("x,y,z", Rt, lex) assert sring(x + 2*t*y + 3*t**2*z, x, y, z) == (R, R.x + 2*Rt.t*R.y + 3*Rt.t**2*R.z) Rt = PolyRing("t", QQ, lex) R = PolyRing("x,y,z", Rt, lex) assert sring(x + t*y/2 + t**2*z/3, x, y, z) == (R, R.x + Rt.t*R.y/2 + Rt.t**2*R.z/3) Rt = FracField("t", ZZ, lex) R = PolyRing("x,y,z", Rt, lex) assert sring(x + 2*y/t + t**2*z/3, x, y, z) == (R, R.x + 2*R.y/Rt.t + Rt.t**2*R.z/3) def test_PolyElement___hash__(): R, x, y, z = ring("x,y,z", QQ) assert hash(x*y*z) def test_PolyElement___eq__(): R, x, y = ring("x,y", ZZ, lex) assert ((x*y + 5*x*y) == 6) == False assert ((x*y + 5*x*y) == 6*x*y) == True assert (6 == (x*y + 5*x*y)) == False assert (6*x*y == (x*y + 5*x*y)) == True assert ((x*y - x*y) == 0) == True assert (0 == (x*y - x*y)) == True assert ((x*y - x*y) == 1) == False assert (1 == (x*y - x*y)) == False assert ((x*y - x*y) == 1) == False assert (1 == (x*y - x*y)) == False assert ((x*y + 5*x*y) != 6) == True assert ((x*y + 5*x*y) != 6*x*y) == False assert (6 != (x*y + 5*x*y)) == True assert (6*x*y != (x*y + 5*x*y)) == False assert ((x*y - x*y) != 0) == False assert (0 != (x*y - x*y)) == False assert ((x*y - x*y) != 1) == True assert (1 != (x*y - x*y)) == True Rt, t = ring("t", ZZ) R, x, y = ring("x,y", Rt) assert (t**3*x/x == t**3) == True assert (t**3*x/x == t**4) == False def test_PolyElement__lt_le_gt_ge__(): R, x, y = ring("x,y", ZZ) assert R(1) < x < x**2 < x**3 assert R(1) <= x <= x**2 <= x**3 assert x**3 > x**2 > x > R(1) assert x**3 >= x**2 >= x >= R(1) def test_PolyElement_copy(): R, x, y, z = ring("x,y,z", ZZ) f = x*y + 3*z g = f.copy() assert f == g g[(1, 1, 1)] = 7 assert f != g def test_PolyElement_as_expr(): R, x, y, z = ring("x,y,z", ZZ) f = 3*x**2*y - x*y*z + 7*z**3 + 1 X, Y, Z = R.symbols g = 3*X**2*Y - X*Y*Z + 7*Z**3 + 1 assert f != g assert f.as_expr() == g X, Y, Z = symbols("x,y,z") g = 3*X**2*Y - X*Y*Z + 7*Z**3 + 1 assert f != g assert f.as_expr(X, Y, Z) == g raises(ValueError, lambda: f.as_expr(X)) def test_PolyElement_from_expr(): x, y, z = symbols("x,y,z") R, X, Y, Z = ring((x, y, z), ZZ) f = R.from_expr(1) assert f == 1 and isinstance(f, R.dtype) f = R.from_expr(x) assert f == X and isinstance(f, R.dtype) f = R.from_expr(x*y*z) assert f == X*Y*Z and isinstance(f, R.dtype) f = R.from_expr(x*y*z + x*y + x) assert f == X*Y*Z + X*Y + X and isinstance(f, R.dtype) f = R.from_expr(x**3*y*z + x**2*y**7 + 1) assert f == X**3*Y*Z + X**2*Y**7 + 1 and isinstance(f, R.dtype) raises(ValueError, lambda: R.from_expr(1/x)) raises(ValueError, lambda: R.from_expr(2**x)) raises(ValueError, lambda: R.from_expr(7*x + sqrt(2))) def test_PolyElement_degree(): R, x,y,z = ring("x,y,z", ZZ) assert R(0).degree() == -oo assert R(1).degree() == 0 assert (x + 1).degree() == 1 assert (2*y**3 + z).degree() == 0 assert (x*y**3 + z).degree() == 1 assert (x**5*y**3 + z).degree() == 5 assert R(0).degree(x) == -oo assert R(1).degree(x) == 0 assert (x + 1).degree(x) == 1 assert (2*y**3 + z).degree(x) == 0 assert (x*y**3 + z).degree(x) == 1 assert (7*x**5*y**3 + z).degree(x) == 5 assert R(0).degree(y) == -oo assert R(1).degree(y) == 0 assert (x + 1).degree(y) == 0 assert (2*y**3 + z).degree(y) == 3 assert (x*y**3 + z).degree(y) == 3 assert (7*x**5*y**3 + z).degree(y) == 3 assert R(0).degree(z) == -oo assert R(1).degree(z) == 0 assert (x + 1).degree(z) == 0 assert (2*y**3 + z).degree(z) == 1 assert (x*y**3 + z).degree(z) == 1 assert (7*x**5*y**3 + z).degree(z) == 1 def test_PolyElement_tail_degree(): R, x,y,z = ring("x,y,z", ZZ) assert R(0).tail_degree() == -oo assert R(1).tail_degree() == 0 assert (x + 1).tail_degree() == 0 assert (2*y**3 + x**3*z).tail_degree() == 0 assert (x*y**3 + x**3*z).tail_degree() == 1 assert (x**5*y**3 + x**3*z).tail_degree() == 3 assert R(0).tail_degree(x) == -oo assert R(1).tail_degree(x) == 0 assert (x + 1).tail_degree(x) == 0 assert (2*y**3 + x**3*z).tail_degree(x) == 0 assert (x*y**3 + x**3*z).tail_degree(x) == 1 assert (7*x**5*y**3 + x**3*z).tail_degree(x) == 3 assert R(0).tail_degree(y) == -oo assert R(1).tail_degree(y) == 0 assert (x + 1).tail_degree(y) == 0 assert (2*y**3 + x**3*z).tail_degree(y) == 0 assert (x*y**3 + x**3*z).tail_degree(y) == 0 assert (7*x**5*y**3 + x**3*z).tail_degree(y) == 0 assert R(0).tail_degree(z) == -oo assert R(1).tail_degree(z) == 0 assert (x + 1).tail_degree(z) == 0 assert (2*y**3 + x**3*z).tail_degree(z) == 0 assert (x*y**3 + x**3*z).tail_degree(z) == 0 assert (7*x**5*y**3 + x**3*z).tail_degree(z) == 0 def test_PolyElement_degrees(): R, x,y,z = ring("x,y,z", ZZ) assert R(0).degrees() == (-oo, -oo, -oo) assert R(1).degrees() == (0, 0, 0) assert (x**2*y + x**3*z**2).degrees() == (3, 1, 2) def test_PolyElement_tail_degrees(): R, x,y,z = ring("x,y,z", ZZ) assert R(0).tail_degrees() == (-oo, -oo, -oo) assert R(1).tail_degrees() == (0, 0, 0) assert (x**2*y + x**3*z**2).tail_degrees() == (2, 0, 0) def test_PolyElement_coeff(): R, x, y, z = ring("x,y,z", ZZ, lex) f = 3*x**2*y - x*y*z + 7*z**3 + 23 assert f.coeff(1) == 23 raises(ValueError, lambda: f.coeff(3)) assert f.coeff(x) == 0 assert f.coeff(y) == 0 assert f.coeff(z) == 0 assert f.coeff(x**2*y) == 3 assert f.coeff(x*y*z) == -1 assert f.coeff(z**3) == 7 raises(ValueError, lambda: f.coeff(3*x**2*y)) raises(ValueError, lambda: f.coeff(-x*y*z)) raises(ValueError, lambda: f.coeff(7*z**3)) def test_PolyElement_LC(): R, x, y = ring("x,y", QQ, lex) assert R(0).LC == QQ(0) assert (QQ(1,2)*x).LC == QQ(1, 2) assert (QQ(1,4)*x*y + QQ(1,2)*x).LC == QQ(1, 4) def test_PolyElement_LM(): R, x, y = ring("x,y", QQ, lex) assert R(0).LM == (0, 0) assert (QQ(1,2)*x).LM == (1, 0) assert (QQ(1,4)*x*y + QQ(1,2)*x).LM == (1, 1) def test_PolyElement_LT(): R, x, y = ring("x,y", QQ, lex) assert R(0).LT == ((0, 0), QQ(0)) assert (QQ(1,2)*x).LT == ((1, 0), QQ(1, 2)) assert (QQ(1,4)*x*y + QQ(1,2)*x).LT == ((1, 1), QQ(1, 4)) def test_PolyElement_leading_monom(): R, x, y = ring("x,y", QQ, lex) assert R(0).leading_monom() == 0 assert (QQ(1,2)*x).leading_monom() == x assert (QQ(1,4)*x*y + QQ(1,2)*x).leading_monom() == x*y def test_PolyElement_leading_term(): R, x, y = ring("x,y", QQ, lex) assert R(0).leading_term() == 0 assert (QQ(1,2)*x).leading_term() == QQ(1,2)*x assert (QQ(1,4)*x*y + QQ(1,2)*x).leading_term() == QQ(1,4)*x*y def test_PolyElement_terms(): R, x,y,z = ring("x,y,z", QQ) terms = (x**2/3 + y**3/4 + z**4/5).terms() assert terms == [((2,0,0), QQ(1,3)), ((0,3,0), QQ(1,4)), ((0,0,4), QQ(1,5))] R, x,y = ring("x,y", ZZ, lex) f = x*y**7 + 2*x**2*y**3 assert f.terms() == f.terms(lex) == f.terms('lex') == [((2, 3), 2), ((1, 7), 1)] assert f.terms(grlex) == f.terms('grlex') == [((1, 7), 1), ((2, 3), 2)] R, x,y = ring("x,y", ZZ, grlex) f = x*y**7 + 2*x**2*y**3 assert f.terms() == f.terms(grlex) == f.terms('grlex') == [((1, 7), 1), ((2, 3), 2)] assert f.terms(lex) == f.terms('lex') == [((2, 3), 2), ((1, 7), 1)] def test_PolyElement_monoms(): R, x,y,z = ring("x,y,z", QQ) monoms = (x**2/3 + y**3/4 + z**4/5).monoms() assert monoms == [(2,0,0), (0,3,0), (0,0,4)] R, x,y = ring("x,y", ZZ, lex) f = x*y**7 + 2*x**2*y**3 assert f.monoms() == f.monoms(lex) == f.monoms('lex') == [(2, 3), (1, 7)] assert f.monoms(grlex) == f.monoms('grlex') == [(1, 7), (2, 3)] R, x,y = ring("x,y", ZZ, grlex) f = x*y**7 + 2*x**2*y**3 assert f.monoms() == f.monoms(grlex) == f.monoms('grlex') == [(1, 7), (2, 3)] assert f.monoms(lex) == f.monoms('lex') == [(2, 3), (1, 7)] def test_PolyElement_coeffs(): R, x,y,z = ring("x,y,z", QQ) coeffs = (x**2/3 + y**3/4 + z**4/5).coeffs() assert coeffs == [QQ(1,3), QQ(1,4), QQ(1,5)] R, x,y = ring("x,y", ZZ, lex) f = x*y**7 + 2*x**2*y**3 assert f.coeffs() == f.coeffs(lex) == f.coeffs('lex') == [2, 1] assert f.coeffs(grlex) == f.coeffs('grlex') == [1, 2] R, x,y = ring("x,y", ZZ, grlex) f = x*y**7 + 2*x**2*y**3 assert f.coeffs() == f.coeffs(grlex) == f.coeffs('grlex') == [1, 2] assert f.coeffs(lex) == f.coeffs('lex') == [2, 1] def test_PolyElement___add__(): Rt, t = ring("t", ZZ) Ruv, u,v = ring("u,v", ZZ) Rxyz, x,y,z = ring("x,y,z", Ruv) assert dict(x + 3*y) == {(1, 0, 0): 1, (0, 1, 0): 3} assert dict(u + x) == dict(x + u) == {(1, 0, 0): 1, (0, 0, 0): u} assert dict(u + x*y) == dict(x*y + u) == {(1, 1, 0): 1, (0, 0, 0): u} assert dict(u + x*y + z) == dict(x*y + z + u) == {(1, 1, 0): 1, (0, 0, 1): 1, (0, 0, 0): u} assert dict(u*x + x) == dict(x + u*x) == {(1, 0, 0): u + 1} assert dict(u*x + x*y) == dict(x*y + u*x) == {(1, 1, 0): 1, (1, 0, 0): u} assert dict(u*x + x*y + z) == dict(x*y + z + u*x) == {(1, 1, 0): 1, (0, 0, 1): 1, (1, 0, 0): u} raises(TypeError, lambda: t + x) raises(TypeError, lambda: x + t) raises(TypeError, lambda: t + u) raises(TypeError, lambda: u + t) Fuv, u,v = field("u,v", ZZ) Rxyz, x,y,z = ring("x,y,z", Fuv) assert dict(u + x) == dict(x + u) == {(1, 0, 0): 1, (0, 0, 0): u} Rxyz, x,y,z = ring("x,y,z", EX) assert dict(EX(pi) + x*y*z) == dict(x*y*z + EX(pi)) == {(1, 1, 1): EX(1), (0, 0, 0): EX(pi)} def test_PolyElement___sub__(): Rt, t = ring("t", ZZ) Ruv, u,v = ring("u,v", ZZ) Rxyz, x,y,z = ring("x,y,z", Ruv) assert dict(x - 3*y) == {(1, 0, 0): 1, (0, 1, 0): -3} assert dict(-u + x) == dict(x - u) == {(1, 0, 0): 1, (0, 0, 0): -u} assert dict(-u + x*y) == dict(x*y - u) == {(1, 1, 0): 1, (0, 0, 0): -u} assert dict(-u + x*y + z) == dict(x*y + z - u) == {(1, 1, 0): 1, (0, 0, 1): 1, (0, 0, 0): -u} assert dict(-u*x + x) == dict(x - u*x) == {(1, 0, 0): -u + 1} assert dict(-u*x + x*y) == dict(x*y - u*x) == {(1, 1, 0): 1, (1, 0, 0): -u} assert dict(-u*x + x*y + z) == dict(x*y + z - u*x) == {(1, 1, 0): 1, (0, 0, 1): 1, (1, 0, 0): -u} raises(TypeError, lambda: t - x) raises(TypeError, lambda: x - t) raises(TypeError, lambda: t - u) raises(TypeError, lambda: u - t) Fuv, u,v = field("u,v", ZZ) Rxyz, x,y,z = ring("x,y,z", Fuv) assert dict(-u + x) == dict(x - u) == {(1, 0, 0): 1, (0, 0, 0): -u} Rxyz, x,y,z = ring("x,y,z", EX) assert dict(-EX(pi) + x*y*z) == dict(x*y*z - EX(pi)) == {(1, 1, 1): EX(1), (0, 0, 0): -EX(pi)} def test_PolyElement___mul__(): Rt, t = ring("t", ZZ) Ruv, u,v = ring("u,v", ZZ) Rxyz, x,y,z = ring("x,y,z", Ruv) assert dict(u*x) == dict(x*u) == {(1, 0, 0): u} assert dict(2*u*x + z) == dict(x*2*u + z) == {(1, 0, 0): 2*u, (0, 0, 1): 1} assert dict(u*2*x + z) == dict(2*x*u + z) == {(1, 0, 0): 2*u, (0, 0, 1): 1} assert dict(2*u*x + z) == dict(x*2*u + z) == {(1, 0, 0): 2*u, (0, 0, 1): 1} assert dict(u*x*2 + z) == dict(x*u*2 + z) == {(1, 0, 0): 2*u, (0, 0, 1): 1} assert dict(2*u*x*y + z) == dict(x*y*2*u + z) == {(1, 1, 0): 2*u, (0, 0, 1): 1} assert dict(u*2*x*y + z) == dict(2*x*y*u + z) == {(1, 1, 0): 2*u, (0, 0, 1): 1} assert dict(2*u*x*y + z) == dict(x*y*2*u + z) == {(1, 1, 0): 2*u, (0, 0, 1): 1} assert dict(u*x*y*2 + z) == dict(x*y*u*2 + z) == {(1, 1, 0): 2*u, (0, 0, 1): 1} assert dict(2*u*y*x + z) == dict(y*x*2*u + z) == {(1, 1, 0): 2*u, (0, 0, 1): 1} assert dict(u*2*y*x + z) == dict(2*y*x*u + z) == {(1, 1, 0): 2*u, (0, 0, 1): 1} assert dict(2*u*y*x + z) == dict(y*x*2*u + z) == {(1, 1, 0): 2*u, (0, 0, 1): 1} assert dict(u*y*x*2 + z) == dict(y*x*u*2 + z) == {(1, 1, 0): 2*u, (0, 0, 1): 1} assert dict(3*u*(x + y) + z) == dict((x + y)*3*u + z) == {(1, 0, 0): 3*u, (0, 1, 0): 3*u, (0, 0, 1): 1} raises(TypeError, lambda: t*x + z) raises(TypeError, lambda: x*t + z) raises(TypeError, lambda: t*u + z) raises(TypeError, lambda: u*t + z) Fuv, u,v = field("u,v", ZZ) Rxyz, x,y,z = ring("x,y,z", Fuv) assert dict(u*x) == dict(x*u) == {(1, 0, 0): u} Rxyz, x,y,z = ring("x,y,z", EX) assert dict(EX(pi)*x*y*z) == dict(x*y*z*EX(pi)) == {(1, 1, 1): EX(pi)} def test_PolyElement___div__(): R, x,y,z = ring("x,y,z", ZZ) assert (2*x**2 - 4)/2 == x**2 - 2 assert (2*x**2 - 3)/2 == x**2 assert (x**2 - 1)/x == (x**2 - 1).quo(x) == x assert (x**2 - x)/x == (x**2 - x).quo(x) == x - 1 assert (x**2 - 1)/(2*x) == (x**2 - 1).quo(2*x) == 0 assert (x**2 - x)/(x - 1) == (x**2 - x).quo(x - 1) == x R, x,y,z = ring("x,y,z", ZZ) assert len((x**2/3 + y**3/4 + z**4/5).terms()) == 0 R, x,y,z = ring("x,y,z", QQ) assert len((x**2/3 + y**3/4 + z**4/5).terms()) == 3 Rt, t = ring("t", ZZ) Ruv, u,v = ring("u,v", ZZ) Rxyz, x,y,z = ring("x,y,z", Ruv) assert dict((u**2*x + u)/u) == {(1, 0, 0): u, (0, 0, 0): 1} raises(TypeError, lambda: u/(u**2*x + u)) raises(TypeError, lambda: t/x) raises(TypeError, lambda: x/t) raises(TypeError, lambda: t/u) raises(TypeError, lambda: u/t) R, x = ring("x", ZZ) f, g = x**2 + 2*x + 3, R(0) raises(ZeroDivisionError, lambda: f.div(g)) raises(ZeroDivisionError, lambda: divmod(f, g)) raises(ZeroDivisionError, lambda: f.rem(g)) raises(ZeroDivisionError, lambda: f % g) raises(ZeroDivisionError, lambda: f.quo(g)) raises(ZeroDivisionError, lambda: f / g) raises(ZeroDivisionError, lambda: f.exquo(g)) R, x, y = ring("x,y", ZZ) f, g = x*y + 2*x + 3, R(0) raises(ZeroDivisionError, lambda: f.div(g)) raises(ZeroDivisionError, lambda: divmod(f, g)) raises(ZeroDivisionError, lambda: f.rem(g)) raises(ZeroDivisionError, lambda: f % g) raises(ZeroDivisionError, lambda: f.quo(g)) raises(ZeroDivisionError, lambda: f / g) raises(ZeroDivisionError, lambda: f.exquo(g)) R, x = ring("x", ZZ) f, g = x**2 + 1, 2*x - 4 q, r = R(0), x**2 + 1 assert f.div(g) == divmod(f, g) == (q, r) assert f.rem(g) == f % g == r assert f.quo(g) == f / g == q raises(ExactQuotientFailed, lambda: f.exquo(g)) f, g = 3*x**3 + x**2 + x + 5, 5*x**2 - 3*x + 1 q, r = R(0), f assert f.div(g) == divmod(f, g) == (q, r) assert f.rem(g) == f % g == r assert f.quo(g) == f / g == q raises(ExactQuotientFailed, lambda: f.exquo(g)) f, g = 5*x**4 + 4*x**3 + 3*x**2 + 2*x + 1, x**2 + 2*x + 3 q, r = 5*x**2 - 6*x, 20*x + 1 assert f.div(g) == divmod(f, g) == (q, r) assert f.rem(g) == f % g == r assert f.quo(g) == f / g == q raises(ExactQuotientFailed, lambda: f.exquo(g)) f, g = 5*x**5 + 4*x**4 + 3*x**3 + 2*x**2 + x, x**4 + 2*x**3 + 9 q, r = 5*x - 6, 15*x**3 + 2*x**2 - 44*x + 54 assert f.div(g) == divmod(f, g) == (q, r) assert f.rem(g) == f % g == r assert f.quo(g) == f / g == q raises(ExactQuotientFailed, lambda: f.exquo(g)) R, x = ring("x", QQ) f, g = x**2 + 1, 2*x - 4 q, r = x/2 + 1, R(5) assert f.div(g) == divmod(f, g) == (q, r) assert f.rem(g) == f % g == r assert f.quo(g) == f / g == q raises(ExactQuotientFailed, lambda: f.exquo(g)) f, g = 3*x**3 + x**2 + x + 5, 5*x**2 - 3*x + 1 q, r = QQ(3, 5)*x + QQ(14, 25), QQ(52, 25)*x + QQ(111, 25) assert f.div(g) == divmod(f, g) == (q, r) assert f.rem(g) == f % g == r assert f.quo(g) == f / g == q raises(ExactQuotientFailed, lambda: f.exquo(g)) R, x,y = ring("x,y", ZZ) f, g = x**2 - y**2, x - y q, r = x + y, R(0) assert f.div(g) == divmod(f, g) == (q, r) assert f.rem(g) == f % g == r assert f.quo(g) == f / g == q assert f.exquo(g) == q f, g = x**2 + y**2, x - y q, r = x + y, 2*y**2 assert f.div(g) == divmod(f, g) == (q, r) assert f.rem(g) == f % g == r assert f.quo(g) == f / g == q raises(ExactQuotientFailed, lambda: f.exquo(g)) f, g = x**2 + y**2, -x + y q, r = -x - y, 2*y**2 assert f.div(g) == divmod(f, g) == (q, r) assert f.rem(g) == f % g == r assert f.quo(g) == f / g == q raises(ExactQuotientFailed, lambda: f.exquo(g)) f, g = x**2 + y**2, 2*x - 2*y q, r = R(0), f assert f.div(g) == divmod(f, g) == (q, r) assert f.rem(g) == f % g == r assert f.quo(g) == f / g == q raises(ExactQuotientFailed, lambda: f.exquo(g)) R, x,y = ring("x,y", QQ) f, g = x**2 - y**2, x - y q, r = x + y, R(0) assert f.div(g) == divmod(f, g) == (q, r) assert f.rem(g) == f % g == r assert f.quo(g) == f / g == q assert f.exquo(g) == q f, g = x**2 + y**2, x - y q, r = x + y, 2*y**2 assert f.div(g) == divmod(f, g) == (q, r) assert f.rem(g) == f % g == r assert f.quo(g) == f / g == q raises(ExactQuotientFailed, lambda: f.exquo(g)) f, g = x**2 + y**2, -x + y q, r = -x - y, 2*y**2 assert f.div(g) == divmod(f, g) == (q, r) assert f.rem(g) == f % g == r assert f.quo(g) == f / g == q raises(ExactQuotientFailed, lambda: f.exquo(g)) f, g = x**2 + y**2, 2*x - 2*y q, r = x/2 + y/2, 2*y**2 assert f.div(g) == divmod(f, g) == (q, r) assert f.rem(g) == f % g == r assert f.quo(g) == f / g == q raises(ExactQuotientFailed, lambda: f.exquo(g)) def test_PolyElement___pow__(): R, x = ring("x", ZZ, grlex) f = 2*x + 3 assert f**0 == 1 assert f**1 == f assert f**2 == f._pow_generic(2) == f._pow_multinomial(2) == 4*x**2 + 12*x + 9 assert f**3 == f._pow_generic(3) == f._pow_multinomial(3) == 8*x**3 + 36*x**2 + 54*x + 27 assert f**4 == f._pow_generic(4) == f._pow_multinomial(4) == 16*x**4 + 96*x**3 + 216*x**2 + 216*x + 81 assert f**5 == f._pow_generic(5) == f._pow_multinomial(5) == 32*x**5 + 240*x**4 + 720*x**3 + 1080*x**2 + 810*x + 243 raises(ValueError, lambda: f**-2) R, x,y,z = ring("x,y,z", ZZ, grlex) f = x**3*y - 2*x*y**2 - 3*z + 1 g = x**6*y**2 - 4*x**4*y**3 - 6*x**3*y*z + 2*x**3*y + 4*x**2*y**4 + 12*x*y**2*z - 4*x*y**2 + 9*z**2 - 6*z + 1 assert f**2 == f._pow_generic(2) == f._pow_multinomial(2) == g R, t = ring("t", ZZ) f = -11200*t**4 - 2604*t**2 + 49 g = 15735193600000000*t**16 + 14633730048000000*t**14 + 4828147466240000*t**12 \ + 598976863027200*t**10 + 3130812416256*t**8 - 2620523775744*t**6 \ + 92413760096*t**4 - 1225431984*t**2 + 5764801 assert f**4 == f._pow_generic(4) == f._pow_multinomial(4) == g def test_PolyElement_div(): R, x = ring("x", ZZ, grlex) f = x**3 - 12*x**2 - 42 g = x - 3 q = x**2 - 9*x - 27 r = -123 assert f.div([g]) == ([q], r) R, x = ring("x", ZZ, grlex) f = x**2 + 2*x + 2 assert f.div([R(1)]) == ([f], 0) R, x = ring("x", QQ, grlex) f = x**2 + 2*x + 2 assert f.div([R(2)]) == ([QQ(1,2)*x**2 + x + 1], 0) R, x,y = ring("x,y", ZZ, grlex) f = 4*x**2*y - 2*x*y + 4*x - 2*y + 8 assert f.div([R(2)]) == ([2*x**2*y - x*y + 2*x - y + 4], 0) assert f.div([2*y]) == ([2*x**2 - x - 1], 4*x + 8) f = x - 1 g = y - 1 assert f.div([g]) == ([0], f) f = x*y**2 + 1 G = [x*y + 1, y + 1] Q = [y, -1] r = 2 assert f.div(G) == (Q, r) f = x**2*y + x*y**2 + y**2 G = [x*y - 1, y**2 - 1] Q = [x + y, 1] r = x + y + 1 assert f.div(G) == (Q, r) G = [y**2 - 1, x*y - 1] Q = [x + 1, x] r = 2*x + 1 assert f.div(G) == (Q, r) def test_PolyElement_rem(): R, x = ring("x", ZZ, grlex) f = x**3 - 12*x**2 - 42 g = x - 3 r = -123 assert f.rem([g]) == f.div([g])[1] == r R, x,y = ring("x,y", ZZ, grlex) f = 4*x**2*y - 2*x*y + 4*x - 2*y + 8 assert f.rem([R(2)]) == f.div([R(2)])[1] == 0 assert f.rem([2*y]) == f.div([2*y])[1] == 4*x + 8 f = x - 1 g = y - 1 assert f.rem([g]) == f.div([g])[1] == f f = x*y**2 + 1 G = [x*y + 1, y + 1] r = 2 assert f.rem(G) == f.div(G)[1] == r f = x**2*y + x*y**2 + y**2 G = [x*y - 1, y**2 - 1] r = x + y + 1 assert f.rem(G) == f.div(G)[1] == r G = [y**2 - 1, x*y - 1] r = 2*x + 1 assert f.rem(G) == f.div(G)[1] == r def test_PolyElement_deflate(): R, x = ring("x", ZZ) assert (2*x**2).deflate(x**4 + 4*x**2 + 1) == ((2,), [2*x, x**2 + 4*x + 1]) R, x,y = ring("x,y", ZZ) assert R(0).deflate(R(0)) == ((1, 1), [0, 0]) assert R(1).deflate(R(0)) == ((1, 1), [1, 0]) assert R(1).deflate(R(2)) == ((1, 1), [1, 2]) assert R(1).deflate(2*y) == ((1, 1), [1, 2*y]) assert (2*y).deflate(2*y) == ((1, 1), [2*y, 2*y]) assert R(2).deflate(2*y**2) == ((1, 2), [2, 2*y]) assert (2*y**2).deflate(2*y**2) == ((1, 2), [2*y, 2*y]) f = x**4*y**2 + x**2*y + 1 g = x**2*y**3 + x**2*y + 1 assert f.deflate(g) == ((2, 1), [x**2*y**2 + x*y + 1, x*y**3 + x*y + 1]) def test_PolyElement_clear_denoms(): R, x,y = ring("x,y", QQ) assert R(1).clear_denoms() == (ZZ(1), 1) assert R(7).clear_denoms() == (ZZ(1), 7) assert R(QQ(7,3)).clear_denoms() == (3, 7) assert R(QQ(7,3)).clear_denoms() == (3, 7) assert (3*x**2 + x).clear_denoms() == (1, 3*x**2 + x) assert (x**2 + QQ(1,2)*x).clear_denoms() == (2, 2*x**2 + x) rQQ, x,t = ring("x,t", QQ, lex) rZZ, X,T = ring("x,t", ZZ, lex) F = [x - QQ(17824537287975195925064602467992950991718052713078834557692023531499318507213727406844943097,413954288007559433755329699713866804710749652268151059918115348815925474842910720000)*t**7 - QQ(4882321164854282623427463828745855894130208215961904469205260756604820743234704900167747753,12936071500236232304854053116058337647210926633379720622441104650497671088840960000)*t**6 - QQ(36398103304520066098365558157422127347455927422509913596393052633155821154626830576085097433,25872143000472464609708106232116675294421853266759441244882209300995342177681920000)*t**5 - QQ(168108082231614049052707339295479262031324376786405372698857619250210703675982492356828810819,58212321751063045371843239022262519412449169850208742800984970927239519899784320000)*t**4 - QQ(5694176899498574510667890423110567593477487855183144378347226247962949388653159751849449037,1617008937529529038106756639507292205901365829172465077805138081312208886105120000)*t**3 - QQ(154482622347268833757819824809033388503591365487934245386958884099214649755244381307907779,60637835157357338929003373981523457721301218593967440417692678049207833228942000)*t**2 - QQ(2452813096069528207645703151222478123259511586701148682951852876484544822947007791153163,2425513406294293557160134959260938308852048743758697616707707121968313329157680)*t - QQ(34305265428126440542854669008203683099323146152358231964773310260498715579162112959703,202126117191191129763344579938411525737670728646558134725642260164026110763140), t**8 + QQ(693749860237914515552,67859264524169150569)*t**7 + QQ(27761407182086143225024,610733380717522355121)*t**6 + QQ(7785127652157884044288,67859264524169150569)*t**5 + QQ(36567075214771261409792,203577793572507451707)*t**4 + QQ(36336335165196147384320,203577793572507451707)*t**3 + QQ(7452455676042754048000,67859264524169150569)*t**2 + QQ(2593331082514399232000,67859264524169150569)*t + QQ(390399197427343360000,67859264524169150569)] G = [3725588592068034903797967297424801242396746870413359539263038139343329273586196480000*X - 160420835591776763325581422211936558925462474417709511019228211783493866564923546661604487873*T**7 - 1406108495478033395547109582678806497509499966197028487131115097902188374051595011248311352864*T**6 - 5241326875850889518164640374668786338033653548841427557880599579174438246266263602956254030352*T**5 - 10758917262823299139373269714910672770004760114329943852726887632013485035262879510837043892416*T**4 - 13119383576444715672578819534846747735372132018341964647712009275306635391456880068261130581248*T**3 - 9491412317016197146080450036267011389660653495578680036574753839055748080962214787557853941760*T**2 - 3767520915562795326943800040277726397326609797172964377014046018280260848046603967211258368000*T - 632314652371226552085897259159210286886724229880266931574701654721512325555116066073245696000, 610733380717522355121*T**8 + 6243748742141230639968*T**7 + 27761407182086143225024*T**6 + 70066148869420956398592*T**5 + 109701225644313784229376*T**4 + 109009005495588442152960*T**3 + 67072101084384786432000*T**2 + 23339979742629593088000*T + 3513592776846090240000] assert [ f.clear_denoms()[1].set_ring(rZZ) for f in F ] == G def test_PolyElement_cofactors(): R, x, y = ring("x,y", ZZ) f, g = R(0), R(0) assert f.cofactors(g) == (0, 0, 0) f, g = R(2), R(0) assert f.cofactors(g) == (2, 1, 0) f, g = R(-2), R(0) assert f.cofactors(g) == (2, -1, 0) f, g = R(0), R(-2) assert f.cofactors(g) == (2, 0, -1) f, g = R(0), 2*x + 4 assert f.cofactors(g) == (2*x + 4, 0, 1) f, g = 2*x + 4, R(0) assert f.cofactors(g) == (2*x + 4, 1, 0) f, g = R(2), R(2) assert f.cofactors(g) == (2, 1, 1) f, g = R(-2), R(2) assert f.cofactors(g) == (2, -1, 1) f, g = R(2), R(-2) assert f.cofactors(g) == (2, 1, -1) f, g = R(-2), R(-2) assert f.cofactors(g) == (2, -1, -1) f, g = x**2 + 2*x + 1, R(1) assert f.cofactors(g) == (1, x**2 + 2*x + 1, 1) f, g = x**2 + 2*x + 1, R(2) assert f.cofactors(g) == (1, x**2 + 2*x + 1, 2) f, g = 2*x**2 + 4*x + 2, R(2) assert f.cofactors(g) == (2, x**2 + 2*x + 1, 1) f, g = R(2), 2*x**2 + 4*x + 2 assert f.cofactors(g) == (2, 1, x**2 + 2*x + 1) f, g = 2*x**2 + 4*x + 2, x + 1 assert f.cofactors(g) == (x + 1, 2*x + 2, 1) f, g = x + 1, 2*x**2 + 4*x + 2 assert f.cofactors(g) == (x + 1, 1, 2*x + 2) R, x, y, z, t = ring("x,y,z,t", ZZ) f, g = t**2 + 2*t + 1, 2*t + 2 assert f.cofactors(g) == (t + 1, t + 1, 2) f, g = z**2*t**2 + 2*z**2*t + z**2 + z*t + z, t**2 + 2*t + 1 h, cff, cfg = t + 1, z**2*t + z**2 + z, t + 1 assert f.cofactors(g) == (h, cff, cfg) assert g.cofactors(f) == (h, cfg, cff) R, x, y = ring("x,y", QQ) f = QQ(1,2)*x**2 + x + QQ(1,2) g = QQ(1,2)*x + QQ(1,2) h = x + 1 assert f.cofactors(g) == (h, g, QQ(1,2)) assert g.cofactors(f) == (h, QQ(1,2), g) R, x, y = ring("x,y", RR) f = 2.1*x*y**2 - 2.1*x*y + 2.1*x g = 2.1*x**3 h = 1.0*x assert f.cofactors(g) == (h, f/h, g/h) assert g.cofactors(f) == (h, g/h, f/h) def test_PolyElement_gcd(): R, x, y = ring("x,y", QQ) f = QQ(1,2)*x**2 + x + QQ(1,2) g = QQ(1,2)*x + QQ(1,2) assert f.gcd(g) == x + 1 def test_PolyElement_cancel(): R, x, y = ring("x,y", ZZ) f = 2*x**3 + 4*x**2 + 2*x g = 3*x**2 + 3*x F = 2*x + 2 G = 3 assert f.cancel(g) == (F, G) assert (-f).cancel(g) == (-F, G) assert f.cancel(-g) == (-F, G) R, x, y = ring("x,y", QQ) f = QQ(1,2)*x**3 + x**2 + QQ(1,2)*x g = QQ(1,3)*x**2 + QQ(1,3)*x F = 3*x + 3 G = 2 assert f.cancel(g) == (F, G) assert (-f).cancel(g) == (-F, G) assert f.cancel(-g) == (-F, G) Fx, x = field("x", ZZ) Rt, t = ring("t", Fx) f = (-x**2 - 4)/4*t g = t**2 + (x**2 + 2)/2 assert f.cancel(g) == ((-x**2 - 4)*t, 4*t**2 + 2*x**2 + 4) def test_PolyElement_max_norm(): R, x, y = ring("x,y", ZZ) assert R(0).max_norm() == 0 assert R(1).max_norm() == 1 assert (x**3 + 4*x**2 + 2*x + 3).max_norm() == 4 def test_PolyElement_l1_norm(): R, x, y = ring("x,y", ZZ) assert R(0).l1_norm() == 0 assert R(1).l1_norm() == 1 assert (x**3 + 4*x**2 + 2*x + 3).l1_norm() == 10 def test_PolyElement_diff(): R, X = xring("x:11", QQ) f = QQ(288,5)*X[0]**8*X[1]**6*X[4]**3*X[10]**2 + 8*X[0]**2*X[2]**3*X[4]**3 +2*X[0]**2 - 2*X[1]**2 assert f.diff(X[0]) == QQ(2304,5)*X[0]**7*X[1]**6*X[4]**3*X[10]**2 + 16*X[0]*X[2]**3*X[4]**3 + 4*X[0] assert f.diff(X[4]) == QQ(864,5)*X[0]**8*X[1]**6*X[4]**2*X[10]**2 + 24*X[0]**2*X[2]**3*X[4]**2 assert f.diff(X[10]) == QQ(576,5)*X[0]**8*X[1]**6*X[4]**3*X[10] def test_PolyElement___call__(): R, x = ring("x", ZZ) f = 3*x + 1 f(0) == 1 f(1) == 4 raises(ValueError, lambda: f()) raises(ValueError, lambda: f(0, 1)) raises(CoercionFailed, lambda: f(QQ(1,7))) R, x,y = ring("x,y", ZZ) f = 3*x + y**2 + 1 f(0, 0) == 1 f(1, 7) == 53 Ry = R.drop(x) f(0) == Ry.y**2 + 1 f(1) == Ry.y**2 + 4 raises(ValueError, lambda: f()) raises(ValueError, lambda: f(0, 1, 2)) raises(CoercionFailed, lambda: f(1, QQ(1,7))) raises(CoercionFailed, lambda: f(QQ(1,7), 1)) raises(CoercionFailed, lambda: f(QQ(1,7), QQ(1,7))) def test_PolyElement_evaluate(): R, x = ring("x", ZZ) f = x**3 + 4*x**2 + 2*x + 3 r = f.evaluate(x, 0) assert r == 3 and not isinstance(r, PolyElement) raises(CoercionFailed, lambda: f.evaluate(x, QQ(1,7))) R, x, y, z = ring("x,y,z", ZZ) f = (x*y)**3 + 4*(x*y)**2 + 2*x*y + 3 r = f.evaluate(x, 0) assert r == 3 and isinstance(r, R.drop(x).dtype) r = f.evaluate([(x, 0), (y, 0)]) assert r == 3 and isinstance(r, R.drop(x, y).dtype) r = f.evaluate(y, 0) assert r == 3 and isinstance(r, R.drop(y).dtype) r = f.evaluate([(y, 0), (x, 0)]) assert r == 3 and isinstance(r, R.drop(y, x).dtype) r = f.evaluate([(x, 0), (y, 0), (z, 0)]) assert r == 3 and not isinstance(r, PolyElement) raises(CoercionFailed, lambda: f.evaluate([(x, 1), (y, QQ(1,7))])) raises(CoercionFailed, lambda: f.evaluate([(x, QQ(1,7)), (y, 1)])) raises(CoercionFailed, lambda: f.evaluate([(x, QQ(1,7)), (y, QQ(1,7))])) def test_PolyElement_subs(): R, x = ring("x", ZZ) f = x**3 + 4*x**2 + 2*x + 3 r = f.subs(x, 0) assert r == 3 and isinstance(r, R.dtype) raises(CoercionFailed, lambda: f.subs(x, QQ(1,7))) R, x, y, z = ring("x,y,z", ZZ) f = x**3 + 4*x**2 + 2*x + 3 r = f.subs(x, 0) assert r == 3 and isinstance(r, R.dtype) r = f.subs([(x, 0), (y, 0)]) assert r == 3 and isinstance(r, R.dtype) raises(CoercionFailed, lambda: f.subs([(x, 1), (y, QQ(1,7))])) raises(CoercionFailed, lambda: f.subs([(x, QQ(1,7)), (y, 1)])) raises(CoercionFailed, lambda: f.subs([(x, QQ(1,7)), (y, QQ(1,7))])) def test_PolyElement_compose(): R, x = ring("x", ZZ) f = x**3 + 4*x**2 + 2*x + 3 r = f.compose(x, 0) assert r == 3 and isinstance(r, R.dtype) assert f.compose(x, x) == f assert f.compose(x, x**2) == x**6 + 4*x**4 + 2*x**2 + 3 raises(CoercionFailed, lambda: f.compose(x, QQ(1,7))) R, x, y, z = ring("x,y,z", ZZ) f = x**3 + 4*x**2 + 2*x + 3 r = f.compose(x, 0) assert r == 3 and isinstance(r, R.dtype) r = f.compose([(x, 0), (y, 0)]) assert r == 3 and isinstance(r, R.dtype) r = (x**3 + 4*x**2 + 2*x*y*z + 3).compose(x, y*z**2 - 1) q = (y*z**2 - 1)**3 + 4*(y*z**2 - 1)**2 + 2*(y*z**2 - 1)*y*z + 3 assert r == q and isinstance(r, R.dtype) def test_PolyElement_is_(): R, x,y,z = ring("x,y,z", QQ) assert (x - x).is_generator == False assert (x - x).is_ground == True assert (x - x).is_monomial == True assert (x - x).is_term == True assert (x - x + 1).is_generator == False assert (x - x + 1).is_ground == True assert (x - x + 1).is_monomial == True assert (x - x + 1).is_term == True assert x.is_generator == True assert x.is_ground == False assert x.is_monomial == True assert x.is_term == True assert (x*y).is_generator == False assert (x*y).is_ground == False assert (x*y).is_monomial == True assert (x*y).is_term == True assert (3*x).is_generator == False assert (3*x).is_ground == False assert (3*x).is_monomial == False assert (3*x).is_term == True assert (3*x + 1).is_generator == False assert (3*x + 1).is_ground == False assert (3*x + 1).is_monomial == False assert (3*x + 1).is_term == False assert R(0).is_zero is True assert R(1).is_zero is False assert R(0).is_one is False assert R(1).is_one is True assert (x - 1).is_monic is True assert (2*x - 1).is_monic is False assert (3*x + 2).is_primitive is True assert (4*x + 2).is_primitive is False assert (x + y + z + 1).is_linear is True assert (x*y*z + 1).is_linear is False assert (x*y + z + 1).is_quadratic is True assert (x*y*z + 1).is_quadratic is False assert (x - 1).is_squarefree is True assert ((x - 1)**2).is_squarefree is False assert (x**2 + x + 1).is_irreducible is True assert (x**2 + 2*x + 1).is_irreducible is False _, t = ring("t", FF(11)) assert (7*t + 3).is_irreducible is True assert (7*t**2 + 3*t + 1).is_irreducible is False _, u = ring("u", ZZ) f = u**16 + u**14 - u**10 - u**8 - u**6 + u**2 assert f.is_cyclotomic is False assert (f + 1).is_cyclotomic is True raises(MultivariatePolynomialError, lambda: x.is_cyclotomic) def test_PolyElement_drop(): R, x,y,z = ring("x,y,z", ZZ) assert R(1).drop(0).ring == PolyRing("y,z", ZZ, lex) assert R(1).drop(0).drop(0).ring == PolyRing("z", ZZ, lex) assert isinstance(R(1).drop(0).drop(0).drop(0), R.dtype) is False raises(ValueError, lambda: z.drop(0).drop(0).drop(0)) raises(ValueError, lambda: x.drop(0)) def test_PolyElement_pdiv(): _, x, y = ring("x,y", ZZ) f, g = x**2 - y**2, x - y q, r = x + y, 0 assert f.pdiv(g) == (q, r) assert f.prem(g) == r assert f.pquo(g) == q assert f.pexquo(g) == q def test_PolyElement_gcdex(): _, x = ring("x", QQ) f, g = 2*x, x**2 - 16 s, t, h = x/32, -QQ(1, 16), 1 assert f.half_gcdex(g) == (s, h) assert f.gcdex(g) == (s, t, h) def test_PolyElement_subresultants(): _, x = ring("x", ZZ) f, g, h = x**2 - 2*x + 1, x**2 - 1, 2*x - 2 assert f.subresultants(g) == [f, g, h] def test_PolyElement_resultant(): _, x = ring("x", ZZ) f, g, h = x**2 - 2*x + 1, x**2 - 1, 0 assert f.resultant(g) == h def test_PolyElement_discriminant(): _, x = ring("x", ZZ) f, g = x**3 + 3*x**2 + 9*x - 13, -11664 assert f.discriminant() == g F, a, b, c = ring("a,b,c", ZZ) _, x = ring("x", F) f, g = a*x**2 + b*x + c, b**2 - 4*a*c assert f.discriminant() == g def test_PolyElement_decompose(): _, x = ring("x", ZZ) f = x**12 + 20*x**10 + 150*x**8 + 500*x**6 + 625*x**4 - 2*x**3 - 10*x + 9 g = x**4 - 2*x + 9 h = x**3 + 5*x assert g.compose(x, h) == f assert f.decompose() == [g, h] def test_PolyElement_shift(): _, x = ring("x", ZZ) assert (x**2 - 2*x + 1).shift(2) == x**2 + 2*x + 1 def test_PolyElement_sturm(): F, t = field("t", ZZ) _, x = ring("x", F) f = 1024/(15625*t**8)*x**5 - 4096/(625*t**8)*x**4 + 32/(15625*t**4)*x**3 - 128/(625*t**4)*x**2 + F(1)/62500*x - F(1)/625 assert f.sturm() == [ x**3 - 100*x**2 + t**4/64*x - 25*t**4/16, 3*x**2 - 200*x + t**4/64, (-t**4/96 + F(20000)/9)*x + 25*t**4/18, (-9*t**12 - 11520000*t**8 - 3686400000000*t**4)/(576*t**8 - 245760000*t**4 + 26214400000000), ] def test_PolyElement_gff_list(): _, x = ring("x", ZZ) f = x**5 + 2*x**4 - x**3 - 2*x**2 assert f.gff_list() == [(x, 1), (x + 2, 4)] f = x*(x - 1)**3*(x - 2)**2*(x - 4)**2*(x - 5) assert f.gff_list() == [(x**2 - 5*x + 4, 1), (x**2 - 5*x + 4, 2), (x, 3)] def test_PolyElement_sqf_norm(): R, x = ring("x", QQ.algebraic_field(sqrt(3))) X = R.to_ground().x assert (x**2 - 2).sqf_norm() == (1, x**2 - 2*sqrt(3)*x + 1, X**4 - 10*X**2 + 1) R, x = ring("x", QQ.algebraic_field(sqrt(2))) X = R.to_ground().x assert (x**2 - 3).sqf_norm() == (1, x**2 - 2*sqrt(2)*x - 1, X**4 - 10*X**2 + 1) def test_PolyElement_sqf_list(): _, x = ring("x", ZZ) f = x**5 - x**3 - x**2 + 1 g = x**3 + 2*x**2 + 2*x + 1 h = x - 1 p = x**4 + x**3 - x - 1 assert f.sqf_part() == p assert f.sqf_list() == (1, [(g, 1), (h, 2)]) def test_PolyElement_factor_list(): _, x = ring("x", ZZ) f = x**5 - x**3 - x**2 + 1 u = x + 1 v = x - 1 w = x**2 + x + 1 assert f.factor_list() == (1, [(u, 1), (v, 2), (w, 1)]) sympy-0.7.4.1/sympy/polys/tests/test_specialpolys.py0000644000175000017500000000663112253362407023063 0ustar georgeskgeorgesk"""Tests for functions for generating interesting polynomials. """ from sympy import Poly, ZZ, symbols from sympy.utilities.pytest import raises from sympy.polys.specialpolys import ( swinnerton_dyer_poly, cyclotomic_poly, symmetric_poly, random_poly, interpolating_poly, fateman_poly_F_1, dmp_fateman_poly_F_1, fateman_poly_F_2, dmp_fateman_poly_F_2, fateman_poly_F_3, dmp_fateman_poly_F_3, ) from sympy.abc import x, y, z def test_swinnerton_dyer_poly(): raises(ValueError, lambda: swinnerton_dyer_poly(0, x)) assert swinnerton_dyer_poly(1, x, polys=True) == Poly(x**2 - 2) assert swinnerton_dyer_poly(1, x) == x**2 - 2 assert swinnerton_dyer_poly(2, x) == x**4 - 10*x**2 + 1 assert swinnerton_dyer_poly( 3, x) == x**8 - 40*x**6 + 352*x**4 - 960*x**2 + 576 def test_cyclotomic_poly(): raises(ValueError, lambda: cyclotomic_poly(0, x)) assert cyclotomic_poly(1, x, polys=True) == Poly(x - 1) assert cyclotomic_poly(1, x) == x - 1 assert cyclotomic_poly(2, x) == x + 1 assert cyclotomic_poly(3, x) == x**2 + x + 1 assert cyclotomic_poly(4, x) == x**2 + 1 assert cyclotomic_poly(5, x) == x**4 + x**3 + x**2 + x + 1 assert cyclotomic_poly(6, x) == x**2 - x + 1 def test_symmetric_poly(): raises(ValueError, lambda: symmetric_poly(-1, x, y, z)) raises(ValueError, lambda: symmetric_poly(5, x, y, z)) assert symmetric_poly(1, x, y, z, polys=True) == Poly(x + y + z) assert symmetric_poly(1, (x, y, z), polys=True) == Poly(x + y + z) assert symmetric_poly(0, x, y, z) == 1 assert symmetric_poly(1, x, y, z) == x + y + z assert symmetric_poly(2, x, y, z) == x*y + x*z + y*z assert symmetric_poly(3, x, y, z) == x*y*z def test_random_poly(): poly = random_poly(x, 10, -100, 100, polys=False) assert Poly(poly).degree() == 10 assert all(-100 <= coeff <= 100 for coeff in Poly(poly).coeffs()) is True poly = random_poly(x, 10, -100, 100, polys=True) assert poly.degree() == 10 assert all(-100 <= coeff <= 100 for coeff in poly.coeffs()) is True def test_interpolating_poly(): x0, x1, x2, y0, y1, y2 = symbols('x:3, y:3') assert interpolating_poly(0, x) == 0 assert interpolating_poly(1, x) == y0 assert interpolating_poly(2, x) == \ y0*(x - x1)/(x0 - x1) + y1*(x - x0)/(x1 - x0) assert interpolating_poly(3, x) == \ y0*(x - x1)*(x - x2)/((x0 - x1)*(x0 - x2)) + \ y1*(x - x0)*(x - x2)/((x1 - x0)*(x1 - x2)) + \ y2*(x - x0)*(x - x1)/((x2 - x0)*(x2 - x1)) def test_fateman_poly_F_1(): f, g, h = fateman_poly_F_1(1) F, G, H = dmp_fateman_poly_F_1(1, ZZ) assert [ t.rep.rep for t in [f, g, h] ] == [F, G, H] f, g, h = fateman_poly_F_1(3) F, G, H = dmp_fateman_poly_F_1(3, ZZ) assert [ t.rep.rep for t in [f, g, h] ] == [F, G, H] def test_fateman_poly_F_2(): f, g, h = fateman_poly_F_2(1) F, G, H = dmp_fateman_poly_F_2(1, ZZ) assert [ t.rep.rep for t in [f, g, h] ] == [F, G, H] f, g, h = fateman_poly_F_2(3) F, G, H = dmp_fateman_poly_F_2(3, ZZ) assert [ t.rep.rep for t in [f, g, h] ] == [F, G, H] def test_fateman_poly_F_3(): f, g, h = fateman_poly_F_3(1) F, G, H = dmp_fateman_poly_F_3(1, ZZ) assert [ t.rep.rep for t in [f, g, h] ] == [F, G, H] f, g, h = fateman_poly_F_3(3) F, G, H = dmp_fateman_poly_F_3(3, ZZ) assert [ t.rep.rep for t in [f, g, h] ] == [F, G, H] sympy-0.7.4.1/sympy/polys/tests/test_polyroots.py0000644000175000017500000004154412253362407022430 0ustar georgeskgeorgesk"""Tests for algorithms for computing symbolic roots of polynomials. """ from sympy import (S, symbols, Symbol, Wild, Integer, Rational, sqrt, powsimp, Lambda, sin, cos, pi, I, Interval, re, im, exp, ZZ) from sympy.polys import (Poly, cyclotomic_poly, intervals, nroots, PolynomialError) from sympy.polys.polyroots import (root_factors, roots_linear, roots_quadratic, roots_cubic, roots_quartic, roots_cyclotomic, roots_binomial, preprocess_roots, roots) from sympy.utilities.pytest import raises from sympy.utilities.randtest import test_numerically a, b, c, d, e, q, t, x, y, z = symbols('a,b,c,d,e,q,t,x,y,z') def test_roots_linear(): assert roots_linear(Poly(2*x + 1, x)) == [-Rational(1, 2)] def test_roots_quadratic(): assert roots_quadratic(Poly(2*x**2, x)) == [0, 0] assert roots_quadratic(Poly(2*x**2 + 3*x, x)) == [-Rational(3, 2), 0] assert roots_quadratic(Poly(2*x**2 + 3, x)) == [-I*sqrt(6)/2, I*sqrt(6)/2] assert roots_quadratic(Poly(2*x**2 + 4*x + 3, x)) == [-1 - I*sqrt(2)/2, -1 + I*sqrt(2)/2] f = x**2 + (2*a*e + 2*c*e)/(a - c)*x + (d - b + a*e**2 - c*e**2)/(a - c) assert roots_quadratic(Poly(f, x)) == \ [-e*(a + c)/(a - c) - sqrt((a*b + c*d - a*d - b*c + 4*a*c*e**2)/(a - c)**2), -e*(a + c)/(a - c) + sqrt((a*b + c*d - a*d - b*c + 4*a*c*e**2)/(a - c)**2)] def test_roots_cubic(): assert roots_cubic(Poly(2*x**3, x)) == [0, 0, 0] assert roots_cubic(Poly(x**3 - 3*x**2 + 3*x - 1, x)) == [1, 1, 1] assert roots_cubic(Poly(x**3 + 1, x)) == \ [-1, S.Half - I*sqrt(3)/2, S.Half + I*sqrt(3)/2] assert roots_cubic(Poly(2*x**3 - 3*x**2 - 3*x - 1, x))[0] == \ S.Half + 3**Rational(1, 3)/2 + 3**Rational(2, 3)/2 def test_roots_quartic(): assert roots_quartic(Poly(x**4, x)) == [0, 0, 0, 0] assert roots_quartic(Poly(x**4 + x**3, x)) in [ [-1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, -1] ] assert roots_quartic(Poly(x**4 - x**3, x)) in [ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1] ] lhs = roots_quartic(Poly(x**4 + x, x)) rhs = [S.Half + I*sqrt(3)/2, S.Half - I*sqrt(3)/2, S.Zero, -S.One] assert sorted(lhs, key=hash) == sorted(rhs, key=hash) # test of all branches of roots quartic for i, (a, b, c, d) in enumerate([(1, 2, 3, 0), (3, -7, -9, 9), (1, 2, 3, 4), (1, 2, 3, 4), (-7, -3, 3, -6), (-3, 5, -6, -4), (6, -5, -10, -3)]): if i == 2: c = -a*(a**2/S(8) - b/S(2)) elif i == 3: d = a*(a*(3*a**2/S(256) - b/S(16)) + c/S(4)) eq = x**4 + a*x**3 + b*x**2 + c*x + d ans = roots_quartic(Poly(eq, x)) assert all(eq.subs(x, ai).n(chop=True) == 0 for ai in ans) # not all symbolic quartics are unresolvable eq = Poly(q*x + q/4 + x**4 + x**3 + 2*x**2 - Rational(1, 3), x) sol = roots_quartic(eq) assert all(test_numerically(eq.subs(x, i), 0) for i in sol) # but some are (see also iss 1890) raises(PolynomialError, lambda: roots_quartic(Poly(y*x**4 + x + z, x))) def test_roots_cyclotomic(): assert roots_cyclotomic(cyclotomic_poly(1, x, polys=True)) == [1] assert roots_cyclotomic(cyclotomic_poly(2, x, polys=True)) == [-1] assert roots_cyclotomic(cyclotomic_poly( 3, x, polys=True)) == [-S(1)/2 - I*sqrt(3)/2, -S(1)/2 + I*sqrt(3)/2] assert roots_cyclotomic(cyclotomic_poly(4, x, polys=True)) == [-I, I] assert roots_cyclotomic(cyclotomic_poly( 6, x, polys=True)) == [S(1)/2 - I*sqrt(3)/2, S(1)/2 + I*sqrt(3)/2] assert roots_cyclotomic(cyclotomic_poly(7, x, polys=True)) == [ -cos(pi/7) - I*sin(pi/7), -cos(pi/7) + I*sin(pi/7), cos(2*pi/7) - I*sin(2*pi/7), cos(2*pi/7) + I*sin(2*pi/7), -cos(3*pi/7) - I*sin(3*pi/7), -cos(3*pi/7) + I*sin(3*pi/7), ] assert roots_cyclotomic(cyclotomic_poly(8, x, polys=True)) == [ -sqrt(2)/2 - I*sqrt(2)/2, -sqrt(2)/2 + I*sqrt(2)/2, sqrt(2)/2 - I*sqrt(2)/2, sqrt(2)/2 + I*sqrt(2)/2, ] assert roots_cyclotomic(cyclotomic_poly(12, x, polys=True)) == [ -sqrt(3)/2 - I/2, -sqrt(3)/2 + I/2, sqrt(3)/2 - I/2, sqrt(3)/2 + I/2, ] assert roots_cyclotomic( cyclotomic_poly(1, x, polys=True), factor=True) == [1] assert roots_cyclotomic( cyclotomic_poly(2, x, polys=True), factor=True) == [-1] assert roots_cyclotomic(cyclotomic_poly(3, x, polys=True), factor=True) == \ [-(-1)**(S(1)/3), -1 + (-1)**(S(1)/3)] assert roots_cyclotomic(cyclotomic_poly(4, x, polys=True), factor=True) == \ [-I, I] assert roots_cyclotomic(cyclotomic_poly(5, x, polys=True), factor=True) == \ [-(-1)**(S(1)/5), (-1)**(S(2)/5), -(-1)**(S(3)/5), -1 + (-1)**(S(1)/5) - (-1)**(S(2)/5) + (-1)**(S(3)/5)] assert roots_cyclotomic(cyclotomic_poly(6, x, polys=True), factor=True) == \ [(-1)**(S(1)/3), 1 - (-1)**(S(1)/3)] def test_roots_binomial(): assert roots_binomial(Poly(5*x, x)) == [0] assert roots_binomial(Poly(5*x**4, x)) == [0, 0, 0, 0] assert roots_binomial(Poly(5*x + 2, x)) == [-Rational(2, 5)] A = 10**Rational(3, 4)/10 assert roots_binomial(Poly(5*x**4 + 2, x)) == \ [-A - A*I, -A + A*I, A - A*I, A + A*I] a1 = Symbol('a1', nonnegative=True) b1 = Symbol('b1', nonnegative=True) r0 = roots_quadratic(Poly(a1*x**2 + b1, x)) r1 = roots_binomial(Poly(a1*x**2 + b1, x)) assert powsimp(r0[0]) == powsimp(r1[0]) assert powsimp(r0[1]) == powsimp(r1[1]) def test_roots_preprocessing(): f = a*y*x**2 + y - b coeff, poly = preprocess_roots(Poly(f, x)) assert coeff == 1 assert poly == Poly(a*y*x**2 + y - b, x) f = c**3*x**3 + c**2*x**2 + c*x + a coeff, poly = preprocess_roots(Poly(f, x)) assert coeff == 1/c assert poly == Poly(x**3 + x**2 + x + a, x) f = c**3*x**3 + c**2*x**2 + a coeff, poly = preprocess_roots(Poly(f, x)) assert coeff == 1/c assert poly == Poly(x**3 + x**2 + a, x) f = c**3*x**3 + c*x + a coeff, poly = preprocess_roots(Poly(f, x)) assert coeff == 1/c assert poly == Poly(x**3 + x + a, x) f = c**3*x**3 + a coeff, poly = preprocess_roots(Poly(f, x)) assert coeff == 1/c assert poly == Poly(x**3 + a, x) E, F, J, L = symbols("E,F,J,L") f = -21601054687500000000*E**8*J**8/L**16 + \ 508232812500000000*F*x*E**7*J**7/L**14 - \ 4269543750000000*E**6*F**2*J**6*x**2/L**12 + \ 16194716250000*E**5*F**3*J**5*x**3/L**10 - \ 27633173750*E**4*F**4*J**4*x**4/L**8 + \ 14840215*E**3*F**5*J**3*x**5/L**6 + \ 54794*E**2*F**6*J**2*x**6/(5*L**4) - \ 1153*E*J*F**7*x**7/(80*L**2) + \ 633*F**8*x**8/160000 coeff, poly = preprocess_roots(Poly(f, x)) assert coeff == 20*E*J/(F*L**2) assert poly == 633*x**8 - 115300*x**7 + 4383520*x**6 + 296804300*x**5 - 27633173750*x**4 + \ 809735812500*x**3 - 10673859375000*x**2 + 63529101562500*x - 135006591796875 f = Poly(-y**2 + x**2*exp(x), y, domain=ZZ[x, exp(x)]) g = Poly(-y**2 + exp(x), y, domain=ZZ[exp(x)]) assert preprocess_roots(f) == (x, g) def test_roots(): assert roots(1, x) == {} assert roots(x, x) == {S.Zero: 1} assert roots(x**9, x) == {S.Zero: 9} assert roots(((x - 2)*(x + 3)*(x - 4)).expand(), x) == {-S(3): 1, S(2): 1, S(4): 1} assert roots(2*x + 1, x) == {-S.Half: 1} assert roots((2*x + 1)**2, x) == {-S.Half: 2} assert roots((2*x + 1)**5, x) == {-S.Half: 5} assert roots((2*x + 1)**10, x) == {-S.Half: 10} assert roots(x**4 - 1, x) == {I: 1, S.One: 1, -S.One: 1, -I: 1} assert roots((x**4 - 1)**2, x) == {I: 2, S.One: 2, -S.One: 2, -I: 2} assert roots(((2*x - 3)**2).expand(), x) == { Rational(3, 2): 2} assert roots(((2*x + 3)**2).expand(), x) == {-Rational(3, 2): 2} assert roots(((2*x - 3)**3).expand(), x) == { Rational(3, 2): 3} assert roots(((2*x + 3)**3).expand(), x) == {-Rational(3, 2): 3} assert roots(((2*x - 3)**5).expand(), x) == { Rational(3, 2): 5} assert roots(((2*x + 3)**5).expand(), x) == {-Rational(3, 2): 5} assert roots(((a*x - b)**5).expand(), x) == { b/a: 5} assert roots(((a*x + b)**5).expand(), x) == {-b/a: 5} assert roots(x**4 - 2*x**2 + 1, x) == {S.One: 2, -S.One: 2} assert roots(x**6 - 4*x**4 + 4*x**3 - x**2, x) == \ {S.One: 2, -1 - sqrt(2): 1, S.Zero: 2, -1 + sqrt(2): 1} assert roots(x**8 - 1, x) == { sqrt(2)/2 + I*sqrt(2)/2: 1, sqrt(2)/2 - I*sqrt(2)/2: 1, -sqrt(2)/2 + I*sqrt(2)/2: 1, -sqrt(2)/2 - I*sqrt(2)/2: 1, S.One: 1, -S.One: 1, I: 1, -I: 1 } f = -2016*x**2 - 5616*x**3 - 2056*x**4 + 3324*x**5 + 2176*x**6 - \ 224*x**7 - 384*x**8 - 64*x**9 assert roots(f) == {S(0): 2, -S(2): 2, S(2): 1, -S(7)/2: 1, -S(3)/2: 1, -S(1)/2: 1, S(3)/2: 1} assert roots((a + b + c)*x - (a + b + c + d), x) == {(a + b + c + d)/(a + b + c): 1} assert roots(x**3 + x**2 - x + 1, x, cubics=False) == {} assert roots(((x - 2)*( x + 3)*(x - 4)).expand(), x, cubics=False) == {-S(3): 1, S(2): 1, S(4): 1} assert roots(((x - 2)*(x + 3)*(x - 4)*(x - 5)).expand(), x, cubics=False) == \ {-S(3): 1, S(2): 1, S(4): 1, S(5): 1} assert roots(x**3 + 2*x**2 + 4*x + 8, x) == {-S(2): 1, -2*I: 1, 2*I: 1} assert roots(x**3 + 2*x**2 + 4*x + 8, x, cubics=True) == \ {-2*I: 1, 2*I: 1, -S(2): 1} assert roots((x**2 - x)*(x**3 + 2*x**2 + 4*x + 8), x ) == \ {S(1): 1, S(0): 1, -S(2): 1, -2*I: 1, 2*I: 1} r1_2, r1_3, r1_9, r4_9, r19_27 = [ Rational(*r) for r in ((1, 2), (1, 3), (1, 9), (4, 9), (19, 27)) ] U = -r1_2 - r1_2*I*3**r1_2 V = -r1_2 + r1_2*I*3**r1_2 W = (r19_27 + r1_9*33**r1_2)**r1_3 assert roots(x**3 + x**2 - x + 1, x, cubics=True) == { -r1_3 - U*W - r4_9*(U*W)**(-1): 1, -r1_3 - V*W - r4_9*(V*W)**(-1): 1, -r1_3 - W - r4_9*( W)**(-1): 1, } f = (x**2 + 2*x + 3).subs(x, 2*x**2 + 3*x).subs(x, 5*x - 4) r13_20, r1_20 = [ Rational(*r) for r in ((13, 20), (1, 20)) ] s2 = sqrt(2) assert roots(f, x) == { r13_20 + r1_20*sqrt(1 - 8*I*s2): 1, r13_20 - r1_20*sqrt(1 - 8*I*s2): 1, r13_20 + r1_20*sqrt(1 + 8*I*s2): 1, r13_20 - r1_20*sqrt(1 + 8*I*s2): 1, } f = x**4 + x**3 + x**2 + x + 1 r1_4, r1_8, r5_8 = [ Rational(*r) for r in ((1, 4), (1, 8), (5, 8)) ] assert roots(f, x) == { -r1_4 + r1_4*5**r1_2 + I*(r5_8 + r1_8*5**r1_2)**r1_2: 1, -r1_4 + r1_4*5**r1_2 - I*(r5_8 + r1_8*5**r1_2)**r1_2: 1, -r1_4 - r1_4*5**r1_2 + I*(r5_8 - r1_8*5**r1_2)**r1_2: 1, -r1_4 - r1_4*5**r1_2 - I*(r5_8 - r1_8*5**r1_2)**r1_2: 1, } f = z**3 + (-2 - y)*z**2 + (1 + 2*y - 2*x**2)*z - y + 2*x**2 assert roots(f, z) == { S.One: 1, S.Half + S.Half*y + S.Half*sqrt(1 - 2*y + y**2 + 8*x**2): 1, S.Half + S.Half*y - S.Half*sqrt(1 - 2*y + y**2 + 8*x**2): 1, } assert roots(a*b*c*x**3 + 2*x**2 + 4*x + 8, x, cubics=False) == {} assert roots(a*b*c*x**3 + 2*x**2 + 4*x + 8, x, cubics=True) != {} assert roots(x**4 - 1, x, filter='Z') == {S.One: 1, -S.One: 1} assert roots(x**4 - 1, x, filter='I') == {I: 1, -I: 1} assert roots((x - 1)*(x + 1), x) == {S.One: 1, -S.One: 1} assert roots( (x - 1)*(x + 1), x, predicate=lambda r: r.is_positive) == {S.One: 1} assert roots(x**4 - 1, x, filter='Z', multiple=True) == [-S.One, S.One] assert roots(x**4 - 1, x, filter='I', multiple=True) == [-I, I] assert roots(x**3, x, multiple=True) == [S.Zero, S.Zero, S.Zero] assert roots(1234, x, multiple=True) == [] f = x**6 - x**5 + x**4 - x**3 + x**2 - x + 1 assert roots(f) == { -I*sin(pi/7) + cos(pi/7): 1, -I*sin(2*pi/7) - cos(2*pi/7): 1, -I*sin(3*pi/7) + cos(3*pi/7): 1, I*sin(pi/7) + cos(pi/7): 1, I*sin(2*pi/7) - cos(2*pi/7): 1, I*sin(3*pi/7) + cos(3*pi/7): 1, } g = ((x**2 + 1)*f**2).expand() assert roots(g) == { -I*sin(pi/7) + cos(pi/7): 2, -I*sin(2*pi/7) - cos(2*pi/7): 2, -I*sin(3*pi/7) + cos(3*pi/7): 2, I*sin(pi/7) + cos(pi/7): 2, I*sin(2*pi/7) - cos(2*pi/7): 2, I*sin(3*pi/7) + cos(3*pi/7): 2, -I: 1, I: 1, } r = roots(x**3 + 40*x + 64) real_root = [rx for rx in r if rx.is_real][0] cr = 4 + 2*sqrt(1074)/9 assert real_root == -2*cr**(S(1)/3) + 20/(3*cr**(S(1)/3)) eq = Poly((7 + 5*sqrt(2))*x**3 + (-6 - 4*sqrt(2))*x**2 + (-sqrt(2) - 1)*x + 2, x, domain='EX') assert roots(eq) == {-1 + sqrt(2): 1, -2 + 2*sqrt(2): 1, -sqrt(2) + 1: 1} eq = Poly(41*x**5 + 29*sqrt(2)*x**5 - 153*x**4 - 108*sqrt(2)*x**4 + 175*x**3 + 125*sqrt(2)*x**3 - 45*x**2 - 30*sqrt(2)*x**2 - 26*sqrt(2)*x - 26*x + 24, x, domain='EX') assert roots(eq) == {-sqrt(2) + 1: 1, -2 + 2*sqrt(2): 1, -1 + sqrt(2): 1, -4 + 4*sqrt(2): 1, -3 + 3*sqrt(2): 1} eq = Poly(x**3 - 2*x**2 + 6*sqrt(2)*x**2 - 8*sqrt(2)*x + 23*x - 14 + 14*sqrt(2), x, domain='EX') assert roots(eq) == {-2*sqrt(2) + 2: 1, -2*sqrt(2) + 1: 1, -2*sqrt(2) - 1: 1} assert roots(Poly((x + sqrt(2))**3 - 7, x, domain='EX')) == \ {-sqrt(2) - 7**(S(1)/3)/2 - sqrt(3)*7**(S(1)/3)*I/2: 1, -sqrt(2) - 7**(S(1)/3)/2 + sqrt(3)*7**(S(1)/3)*I/2: 1, -sqrt(2) + 7**(S(1)/3): 1} def test_roots_slow(): """Just test that calculating these roots does not hang. """ a, b, c, d, x = symbols("a,b,c,d,x") f1 = x**2*c + (a/b) + x*c*d - a f2 = x**2*(a + b*(c - d)*a) + x*a*b*c/(b*d - d) + (a*d - c/d) assert list(roots(f1, x).values()) == [1, 1] assert list(roots(f2, x).values()) == [1, 1] (zz, yy, xx, zy, zx, yx, k) = symbols("zz,yy,xx,zy,zx,yx,k") e1 = (zz - k)*(yy - k)*(xx - k) + zy*yx*zx + zx - zy - yx e2 = (zz - k)*yx*yx + zx*(yy - k)*zx + zy*zy*(xx - k) assert list(roots(e1 - e2, k).values()) == [1, 1, 1] f = x**3 + 2*x**2 + 8 R = list(roots(f).keys()) assert f.subs(x, R[0]).simplify() == 0 assert f.subs(x, R[1]).simplify() == 0 assert f.subs(x, R[2]).simplify() == 0 def test_roots_inexact(): R1 = roots(x**2 + x + 1, x, multiple=True) R2 = roots(x**2 + x + 1.0, x, multiple=True) for r1, r2 in zip(R1, R2): assert abs(r1 - r2) < 1e-12 f = x**4 + 3.0*sqrt(2.0)*x**3 - (78.0 + 24.0*sqrt(3.0))*x**2 \ + 144.0*(2*sqrt(3.0) + 9.0) R1 = roots(f, multiple=True) R2 = (-12.7530479110482, -3.85012393732929, 4.89897948556636, 7.46155167569183) for r1, r2 in zip(R1, R2): assert abs(r1 - r2) < 1e-10 def test_roots_preprocessed(): E, F, J, L = symbols("E,F,J,L") f = -21601054687500000000*E**8*J**8/L**16 + \ 508232812500000000*F*x*E**7*J**7/L**14 - \ 4269543750000000*E**6*F**2*J**6*x**2/L**12 + \ 16194716250000*E**5*F**3*J**5*x**3/L**10 - \ 27633173750*E**4*F**4*J**4*x**4/L**8 + \ 14840215*E**3*F**5*J**3*x**5/L**6 + \ 54794*E**2*F**6*J**2*x**6/(5*L**4) - \ 1153*E*J*F**7*x**7/(80*L**2) + \ 633*F**8*x**8/160000 assert roots(f, x) == {} R1 = roots(f.evalf(), x, multiple=True) R2 = [-1304.88375606366, 97.1168816800648, 186.946430171876, 245.526792947065, 503.441004174773, 791.549343830097, 1273.16678129348, 1850.10650616851] w = Wild('w') p = w*E*J/(F*L**2) assert len(R1) == len(R2) for r1, r2 in zip(R1, R2): match = r1.match(p) assert match is not None and abs(match[w] - r2) < 1e-10 def test_roots_mixed(): f = -1936 - 5056*x - 7592*x**2 + 2704*x**3 - 49*x**4 _re, _im = intervals(f, all=True) _nroots = nroots(f) _sroots = roots(f, multiple=True) _re = [ Interval(a, b) for (a, b), _ in _re ] _im = [ Interval(re(a), re(b))*Interval(im(a), im(b)) for (a, b), _ in _im ] _intervals = _re + _im _sroots = [ r.evalf() for r in _sroots ] _nroots = sorted(_nroots, key=lambda x: x.sort_key()) _sroots = sorted(_sroots, key=lambda x: x.sort_key()) for _roots in (_nroots, _sroots): for i, r in zip(_intervals, _roots): if r.is_real: assert r in i else: assert (re(r), im(r)) in i def test_root_factors(): assert root_factors(Poly(1, x)) == [Poly(1, x)] assert root_factors(Poly(x, x)) == [Poly(x, x)] assert root_factors(x**2 - 1, x) == [x - 1, x + 1] assert root_factors(x**2 - y, x) == [x - sqrt(y), x + sqrt(y)] assert root_factors((x**4 - 1)**2) == \ [x - 1, x - 1, x + 1, x + 1, x - I, x - I, x + I, x + I] assert root_factors(Poly(x**4 - 1, x), filter='Z') == \ [Poly(x - 1, x), Poly(x + 1, x), Poly(x**2 + 1, x)] assert root_factors(8*x**2 + 12*x**4 + 6*x**6 + x**8, x, filter='Q') == \ [x, x, x**6 + 6*x**4 + 12*x**2 + 8] sympy-0.7.4.1/sympy/polys/tests/test_injections.py0000644000175000017500000000340312253362407022513 0ustar georgeskgeorgesk"""Tests for functions that inject symbols into the global namespace. """ from sympy.polys.rings import vring from sympy.polys.fields import vfield from sympy.polys.domains import QQ from sympy.utilities.pytest import raises # make r1 with call-depth = 1 def _make_r1(): return vring("r1", QQ) # make r2 with call-depth = 2 def __make_r2(): return vring("r2", QQ) def _make_r2(): return __make_r2() def test_vring(): R = vring("r", QQ) assert r == R.gens[0] R = vring("rb rbb rcc rzz _rx", QQ) assert rb == R.gens[0] assert rbb == R.gens[1] assert rcc == R.gens[2] assert rzz == R.gens[3] assert _rx == R.gens[4] R = vring(['rd', 're', 'rfg'], QQ) assert rd == R.gens[0] assert re == R.gens[1] assert rfg == R.gens[2] # see if vring() really injects into global namespace raises(NameError, lambda: r1) R = _make_r1() assert r1 == R.gens[0] raises(NameError, lambda: r2) R = _make_r2() assert r2 == R.gens[0] # make f1 with call-depth = 1 def _make_f1(): return vfield("f1", QQ) # make f2 with call-depth = 2 def __make_f2(): return vfield("f2", QQ) def _make_f2(): return __make_f2() def test_vfield(): F = vfield("f", QQ) assert f == F.gens[0] F = vfield("fb fbb fcc fzz _fx", QQ) assert fb == F.gens[0] assert fbb == F.gens[1] assert fcc == F.gens[2] assert fzz == F.gens[3] assert _fx == F.gens[4] F = vfield(['fd', 'fe', 'ffg'], QQ) assert fd == F.gens[0] assert fe == F.gens[1] assert ffg == F.gens[2] # see if vfield() really injects into global namespace raises(NameError, lambda: f1) F = _make_f1() assert f1 == F.gens[0] raises(NameError, lambda: f2) F = _make_f2() assert f2 == F.gens[0] sympy-0.7.4.1/sympy/polys/tests/test_polyfuncs.py0000644000175000017500000000553612253362407022401 0ustar georgeskgeorgesk"""Tests for high-level polynomials manipulation functions. """ from sympy.polys.polyfuncs import ( symmetrize, horner, interpolate, viete, ) from sympy.polys.polyerrors import ( MultivariatePolynomialError, ) from sympy import symbols from sympy.utilities.pytest import raises from sympy.abc import a, b, c, d, e, x, y, z def test_symmetrize(): assert symmetrize(0, x, y, z) == (0, 0) assert symmetrize(1, x, y, z) == (1, 0) s1 = x + y + z s2 = x*y + x*z + y*z s3 = x*y*z assert symmetrize(1) == (1, 0) assert symmetrize(1, formal=True) == (1, 0, []) assert symmetrize(x) == (x, 0) assert symmetrize(x + 1) == (x + 1, 0) assert symmetrize(x, x, y) == (x + y, -y) assert symmetrize(x + 1, x, y) == (x + y + 1, -y) assert symmetrize(x, x, y, z) == (s1, -y - z) assert symmetrize(x + 1, x, y, z) == (s1 + 1, -y - z) assert symmetrize(x**2, x, y, z) == (s1**2 - 2*s2, -y**2 - z**2) assert symmetrize(x**2 + y**2) == (-2*x*y + (x + y)**2, 0) assert symmetrize(x**2 - y**2) == (-2*x*y + (x + y)**2, -2*y**2) assert symmetrize(x**3 + y**2 + a*x**2 + b*y**3, x, y) == \ (-3*x*y*(x + y) - 2*a*x*y + a*(x + y)**2 + (x + y)**3, y**2*(1 - a) + y**3*(b - 1)) U = [u0, u1, u2] = symbols('u:3') assert symmetrize(x + 1, x, y, z, formal=True, symbols=U) == \ (u0 + 1, -y - z, [(u0, x + y + z), (u1, x*y + x*z + y*z), (u2, x*y*z)]) assert symmetrize([1, 2, 3]) == [(1, 0), (2, 0), (3, 0)] assert symmetrize([1, 2, 3], formal=True) == ([(1, 0), (2, 0), (3, 0)], []) assert symmetrize([x + y, x - y]) == [(x + y, 0), (x + y, -2*y)] def test_horner(): assert horner(0) == 0 assert horner(1) == 1 assert horner(x) == x assert horner(x + 1) == x + 1 assert horner(x**2 + 1) == x**2 + 1 assert horner(x**2 + x) == (x + 1)*x assert horner(x**2 + x + 1) == (x + 1)*x + 1 assert horner( 9*x**4 + 8*x**3 + 7*x**2 + 6*x + 5) == (((9*x + 8)*x + 7)*x + 6)*x + 5 assert horner( a*x**4 + b*x**3 + c*x**2 + d*x + e) == (((a*x + b)*x + c)*x + d)*x + e assert horner(4*x**2*y**2 + 2*x**2*y + 2*x*y**2 + x*y, wrt=x) == (( 4*y + 2)*x*y + (2*y + 1)*y)*x assert horner(4*x**2*y**2 + 2*x**2*y + 2*x*y**2 + x*y, wrt=y) == (( 4*x + 2)*y*x + (2*x + 1)*x)*y def test_interpolate(): assert interpolate([1, 4, 9, 16], x) == x**2 assert interpolate([(1, 1), (2, 4), (3, 9)], x) == x**2 assert interpolate([(1, 2), (2, 5), (3, 10)], x) == 1 + x**2 assert interpolate({1: 2, 2: 5, 3: 10}, x) == 1 + x**2 def test_viete(): r1, r2 = symbols('r1, r2') assert viete( a*x**2 + b*x + c, [r1, r2], x) == [(r1 + r2, -b/a), (r1*r2, c/a)] raises(ValueError, lambda: viete(1, [], x)) raises(ValueError, lambda: viete(x**2 + 1, [r1])) raises(MultivariatePolynomialError, lambda: viete(x + y, [r1])) sympy-0.7.4.1/sympy/polys/tests/test_rootoftools.py0000644000175000017500000002771412253362407022752 0ustar georgeskgeorgesk"""Tests for the implementation of RootOf class and related tools. """ from sympy.polys.polytools import Poly from sympy.polys.rootoftools import RootOf, RootSum from sympy.polys.orthopolys import legendre_poly from sympy.polys.polyerrors import ( MultivariatePolynomialError, GeneratorsNeeded, PolynomialError, ) from sympy import ( S, symbols, sqrt, I, Rational, Float, Lambda, log, exp, tan, ) from sympy.utilities.pytest import raises from sympy.abc import a, b, c, d, x, y, z, r def test_RootOf___new__(): assert RootOf(x, 0) == 0 assert RootOf(x, -1) == 0 assert RootOf(x, S.Zero) == 0 assert RootOf(x - 1, 0) == 1 assert RootOf(x - 1, -1) == 1 assert RootOf(x + 1, 0) == -1 assert RootOf(x + 1, -1) == -1 assert RootOf(x**2 + 2*x + 3, 0) == -1 - I*sqrt(2) assert RootOf(x**2 + 2*x + 3, 1) == -1 + I*sqrt(2) assert RootOf(x**2 + 2*x + 3, -1) == -1 + I*sqrt(2) assert RootOf(x**2 + 2*x + 3, -2) == -1 - I*sqrt(2) r = RootOf(x**2 + 2*x + 3, 0, radicals=False) assert isinstance(r, RootOf) is True r = RootOf(x**2 + 2*x + 3, 1, radicals=False) assert isinstance(r, RootOf) is True r = RootOf(x**2 + 2*x + 3, -1, radicals=False) assert isinstance(r, RootOf) is True r = RootOf(x**2 + 2*x + 3, -2, radicals=False) assert isinstance(r, RootOf) is True assert RootOf((x - 1)*(x + 1), 0, radicals=False) == -1 assert RootOf((x - 1)*(x + 1), 1, radicals=False) == 1 assert RootOf((x - 1)*(x + 1), -1, radicals=False) == 1 assert RootOf((x - 1)*(x + 1), -2, radicals=False) == -1 assert RootOf((x - 1)*(x + 1), 0, radicals=True) == -1 assert RootOf((x - 1)*(x + 1), 1, radicals=True) == 1 assert RootOf((x - 1)*(x + 1), -1, radicals=True) == 1 assert RootOf((x - 1)*(x + 1), -2, radicals=True) == -1 assert RootOf((x - 1)*(x**3 + x + 3), 0) == RootOf(x**3 + x + 3, 0) assert RootOf((x - 1)*(x**3 + x + 3), 1) == 1 assert RootOf((x - 1)*(x**3 + x + 3), 2) == RootOf(x**3 + x + 3, 1) assert RootOf((x - 1)*(x**3 + x + 3), 3) == RootOf(x**3 + x + 3, 2) assert RootOf((x - 1)*(x**3 + x + 3), -1) == RootOf(x**3 + x + 3, 2) assert RootOf((x - 1)*(x**3 + x + 3), -2) == RootOf(x**3 + x + 3, 1) assert RootOf((x - 1)*(x**3 + x + 3), -3) == 1 assert RootOf((x - 1)*(x**3 + x + 3), -4) == RootOf(x**3 + x + 3, 0) assert RootOf(x**4 + 3*x**3, 0) == -3 assert RootOf(x**4 + 3*x**3, 1) == 0 assert RootOf(x**4 + 3*x**3, 2) == 0 assert RootOf(x**4 + 3*x**3, 3) == 0 raises(GeneratorsNeeded, lambda: RootOf(0, 0)) raises(GeneratorsNeeded, lambda: RootOf(1, 0)) raises(PolynomialError, lambda: RootOf(Poly(0, x), 0)) raises(PolynomialError, lambda: RootOf(Poly(1, x), 0)) raises(PolynomialError, lambda: RootOf(x - y, 0)) raises(NotImplementedError, lambda: RootOf(x**3 - x + sqrt(2), 0)) raises(NotImplementedError, lambda: RootOf(x**3 - x + I, 0)) raises(IndexError, lambda: RootOf(x**2 - 1, -4)) raises(IndexError, lambda: RootOf(x**2 - 1, -3)) raises(IndexError, lambda: RootOf(x**2 - 1, 2)) raises(IndexError, lambda: RootOf(x**2 - 1, 3)) assert RootOf(Poly(x - y, x), 0) == y assert RootOf(Poly(x**2 - y, x), 0) == -sqrt(y) assert RootOf(Poly(x**2 - y, x), 1) == sqrt(y) assert RootOf(Poly(x**3 - y, x), 0) == y**Rational(1, 3) assert RootOf(y*x**3 + y*x + 2*y, x, 0) == -1 raises(NotImplementedError, lambda: RootOf(x**3 + x + 2*y, x, 0)) assert RootOf(x**3 + x + 1, 0).is_commutative is True def test_RootOf_free_symbols(): assert RootOf(x**3 + x + 3, 0).free_symbols == set() # if the following assertion fails then multivariate polynomials # are apparently supported and the RootOf.free_symbols routine # should be changed to return whatever symbols would not be # the PurePoly dummy symbol raises(NotImplementedError, lambda: RootOf(Poly(x**3 + y*x + 1, x), 0)) def test_RootOf___eq__(): assert (RootOf(x**3 + x + 3, 0) == RootOf(x**3 + x + 3, 0)) is True assert (RootOf(x**3 + x + 3, 0) == RootOf(x**3 + x + 3, 1)) is False assert (RootOf(x**3 + x + 3, 1) == RootOf(x**3 + x + 3, 1)) is True assert (RootOf(x**3 + x + 3, 1) == RootOf(x**3 + x + 3, 2)) is False assert (RootOf(x**3 + x + 3, 2) == RootOf(x**3 + x + 3, 2)) is True assert (RootOf(x**3 + x + 3, 0) == RootOf(y**3 + y + 3, 0)) is True assert (RootOf(x**3 + x + 3, 0) == RootOf(y**3 + y + 3, 1)) is False assert (RootOf(x**3 + x + 3, 1) == RootOf(y**3 + y + 3, 1)) is True assert (RootOf(x**3 + x + 3, 1) == RootOf(y**3 + y + 3, 2)) is False assert (RootOf(x**3 + x + 3, 2) == RootOf(y**3 + y + 3, 2)) is True def test_RootOf_is_real(): assert RootOf(x**3 + x + 3, 0).is_real is True assert RootOf(x**3 + x + 3, 1).is_real is False assert RootOf(x**3 + x + 3, 2).is_real is False def test_RootOf_is_complex(): assert RootOf(x**3 + x + 3, 0).is_complex is True def test_RootOf_subs(): assert RootOf(x**3 + x + 1, 0).subs(x, y) == RootOf(y**3 + y + 1, 0) def test_RootOf_diff(): assert RootOf(x**3 + x + 1, 0).diff(x) == 0 assert RootOf(x**3 + x + 1, 0).diff(y) == 0 def test_RootOf_evalf(): real = RootOf(x**3 + x + 3, 0).evalf(n=20) assert real.epsilon_eq(Float("-1.2134116627622296341")) re, im = RootOf(x**3 + x + 3, 1).evalf(n=20).as_real_imag() assert re.epsilon_eq( Float("0.60670583138111481707")) assert im.epsilon_eq(-Float("1.45061224918844152650")) re, im = RootOf(x**3 + x + 3, 2).evalf(n=20).as_real_imag() assert re.epsilon_eq(Float("0.60670583138111481707")) assert im.epsilon_eq(Float("1.45061224918844152650")) p = legendre_poly(4, x, polys=True) roots = [str(r.n(17)) for r in p.real_roots()] assert roots == [ "-0.86113631159405258", "-0.33998104358485626", "0.33998104358485626", "0.86113631159405258", ] re = RootOf(x**5 - 5*x + 12, 0).evalf(n=20) assert re.epsilon_eq(Float("-1.84208596619025438271")) re, im = RootOf(x**5 - 5*x + 12, 1).evalf(n=20).as_real_imag() assert re.epsilon_eq(Float("-0.351854240827371999559")) assert im.epsilon_eq(Float("-1.709561043370328882010")) re, im = RootOf(x**5 - 5*x + 12, 2).evalf(n=20).as_real_imag() assert re.epsilon_eq(Float("-0.351854240827371999559")) assert im.epsilon_eq(Float("+1.709561043370328882010")) re, im = RootOf(x**5 - 5*x + 12, 3).evalf(n=20).as_real_imag() assert re.epsilon_eq(Float("+1.272897223922499190910")) assert im.epsilon_eq(Float("-0.719798681483861386681")) re, im = RootOf(x**5 - 5*x + 12, 4).evalf(n=20).as_real_imag() assert re.epsilon_eq(Float("+1.272897223922499190910")) assert im.epsilon_eq(Float("+0.719798681483861386681")) def test_RootOf_evalf_caching_bug(): r = RootOf(x**5 - 5*x + 12, 1) r.n() a = r._get_interval() r = RootOf(x**5 - 5*x + 12, 1) r.n() b = r._get_interval() assert a == b def test_RootOf_real_roots(): assert Poly(x**5 + x + 1).real_roots() == [RootOf(x**3 - x**2 + 1, 0)] assert Poly(x**5 + x + 1).real_roots(radicals=False) == [RootOf( x**3 - x**2 + 1, 0)] def test_RootOf_all_roots(): assert Poly(x**5 + x + 1).all_roots() == [ RootOf(x**3 - x**2 + 1, 0), -S(1)/2 - sqrt(3)*I/2, -S(1)/2 + sqrt(3)*I/2, RootOf(x**3 - x**2 + 1, 1), RootOf(x**3 - x**2 + 1, 2), ] assert Poly(x**5 + x + 1).all_roots(radicals=False) == [ RootOf(x**3 - x**2 + 1, 0), RootOf(x**2 + x + 1, 0, radicals=False), RootOf(x**2 + x + 1, 1, radicals=False), RootOf(x**3 - x**2 + 1, 1), RootOf(x**3 - x**2 + 1, 2), ] def test_RootOf_eval_rational(): p = legendre_poly(4, x, polys=True) roots = [r.eval_rational(S(1)/10**20) for r in p.real_roots()] for r in roots: assert isinstance(r, Rational) # All we know is that the Rational instance will be at most 1/10^20 from # the exact root. So if we evaluate to 17 digits, it must be exactly equal # to: roots = [str(r.n(17)) for r in roots] assert roots == [ "-0.86113631159405258", "-0.33998104358485626", "0.33998104358485626", "0.86113631159405258", ] def test_RootSum___new__(): f = x**3 + x + 3 g = Lambda(r, log(r*x)) s = RootSum(f, g) assert isinstance(s, RootSum) is True assert RootSum(f**2, g) == 2*RootSum(f, g) assert RootSum((x - 7)*f**3, g) == log(7*x) + 3*RootSum(f, g) # Issue 2472 assert hash(RootSum((x - 7)*f**3, g)) == hash(log(7*x) + 3*RootSum(f, g)) raises(MultivariatePolynomialError, lambda: RootSum(x**3 + x + y)) raises(ValueError, lambda: RootSum(x**2 + 3, lambda x: x)) assert RootSum(f, exp) == RootSum(f, Lambda(x, exp(x))) assert RootSum(f, log) == RootSum(f, Lambda(x, log(x))) assert isinstance(RootSum(f, auto=False), RootSum) is True assert RootSum(f) == 0 assert RootSum(f, Lambda(x, x)) == 0 assert RootSum(f, Lambda(x, x**2)) == -2 assert RootSum(f, Lambda(x, 1)) == 3 assert RootSum(f, Lambda(x, 2)) == 6 assert RootSum(f, auto=False).is_commutative is True assert RootSum(f, Lambda(x, 1/(x + x**2))) == S(11)/3 assert RootSum(f, Lambda(x, y/(x + x**2))) == S(11)/3*y assert RootSum(x**2 - 1, Lambda(x, 3*x**2), x) == 6 assert RootSum(x**2 - y, Lambda(x, 3*x**2), x) == 6*y assert RootSum(x**2 - 1, Lambda(x, z*x**2), x) == 2*z assert RootSum(x**2 - y, Lambda(x, z*x**2), x) == 2*z*y assert RootSum( x**2 - 1, Lambda(x, exp(x)), quadratic=True) == exp(-1) + exp(1) assert RootSum(x**3 + a*x + a**3, tan, x) == \ RootSum(x**3 + x + 1, Lambda(x, tan(a*x))) assert RootSum(a**3*x**3 + a*x + 1, tan, x) == \ RootSum(x**3 + x + 1, Lambda(x, tan(x/a))) def test_RootSum_free_symbols(): assert RootSum(x**3 + x + 3, Lambda(r, exp(r))).free_symbols == set() assert RootSum(x**3 + x + 3, Lambda(r, exp(a*r))).free_symbols == set([a]) assert RootSum( x**3 + x + y, Lambda(r, exp(a*r)), x).free_symbols == set([a, y]) def test_RootSum___eq__(): f = Lambda(x, exp(x)) assert (RootSum(x**3 + x + 1, f) == RootSum(x**3 + x + 1, f)) is True assert (RootSum(x**3 + x + 1, f) == RootSum(y**3 + y + 1, f)) is True assert (RootSum(x**3 + x + 1, f) == RootSum(x**3 + x + 2, f)) is False assert (RootSum(x**3 + x + 1, f) == RootSum(y**3 + y + 2, f)) is False def test_RootSum_doit(): rs = RootSum(x**2 + 1, exp) assert isinstance(rs, RootSum) is True assert rs.doit() == exp(-I) + exp(I) rs = RootSum(x**2 + a, exp, x) assert isinstance(rs, RootSum) is True assert rs.doit() == exp(-sqrt(-a)) + exp(sqrt(-a)) def test_RootSum_evalf(): rs = RootSum(x**2 + 1, exp) assert rs.evalf(n=20, chop=True).epsilon_eq( Float("1.0806046117362794348", 20), Float("1e-20")) is True assert rs.evalf(n=15, chop=True).epsilon_eq( Float("1.08060461173628", 15), Float("1e-15")) is True rs = RootSum(x**2 + a, exp, x) assert rs.evalf() == rs def test_RootSum_diff(): f = x**3 + x + 3 g = Lambda(r, exp(r*x)) h = Lambda(r, r*exp(r*x)) assert RootSum(f, g).diff(x) == RootSum(f, h) def test_RootSum_subs(): f = x**3 + x + 3 g = Lambda(r, exp(r*x)) F = y**3 + y + 3 G = Lambda(r, exp(r*y)) assert RootSum(f, g).subs(y, 1) == RootSum(f, g) assert RootSum(f, g).subs(x, y) == RootSum(F, G) def test_RootSum_rational(): assert RootSum( z**5 - z + 1, Lambda(z, z/(x - z))) == (4*x - 5)/(x**5 - x + 1) f = 161*z**3 + 115*z**2 + 19*z + 1 g = Lambda(z, z*log( -3381*z**4/4 - 3381*z**3/4 - 625*z**2/2 - 125*z/2 - 5 + exp(x))) assert RootSum(f, g).diff(x) == -( (5*exp(2*x) - 6*exp(x) + 4)*exp(x)/(exp(3*x) - exp(2*x) + 1))/7 def test_RootSum_independent(): f = (x**3 - a)**2*(x**4 - b)**3 g = Lambda(x, 5*tan(x) + 7) h = Lambda(x, tan(x)) r0 = RootSum(x**3 - a, h, x) r1 = RootSum(x**4 - b, h, x) assert RootSum(f, g, x).as_ordered_terms() == [10*r0, 15*r1, 126] sympy-0.7.4.1/sympy/polys/tests/test_monomials.py0000644000175000017500000000630212253362407022345 0ustar georgeskgeorgesk"""Tests for tools and arithmetics for monomials of distributed polynomials. """ from sympy.polys.monomials import ( itermonomials, monomial_count, monomial_mul, monomial_div, monomial_gcd, monomial_lcm, monomial_max, monomial_min, monomial_divides, Monomial, ) from sympy.polys.polyerrors import ExactQuotientFailed from sympy.abc import a, b, c, x, y, z from sympy.core import S from sympy.utilities.pytest import raises def test_monomials(): assert itermonomials([], 0) == set([S(1)]) assert itermonomials([], 1) == set([S(1)]) assert itermonomials([], 2) == set([S(1)]) assert itermonomials([], 3) == set([S(1)]) assert itermonomials([x], 0) == set([S(1)]) assert itermonomials([x], 1) == set([S(1), x]) assert itermonomials([x], 2) == set([S(1), x, x**2]) assert itermonomials([x], 3) == set([S(1), x, x**2, x**3]) assert itermonomials([x, y], 0) == set([S(1)]) assert itermonomials([x, y], 1) == set([S(1), x, y]) assert itermonomials([x, y], 2) == set([S(1), x, y, x**2, y**2, x*y]) assert itermonomials([x, y], 3) == \ set([S(1), x, y, x**2, x**3, y**2, y**3, x*y, x*y**2, y*x**2]) def test_monomial_count(): assert monomial_count(2, 2) == 6 assert monomial_count(2, 3) == 10 def test_monomial_mul(): assert monomial_mul((3, 4, 1), (1, 2, 0)) == (4, 6, 1) def test_monomial_div(): assert monomial_div((3, 4, 1), (1, 2, 0)) == (2, 2, 1) def test_monomial_gcd(): assert monomial_gcd((3, 4, 1), (1, 2, 0)) == (1, 2, 0) def test_monomial_lcm(): assert monomial_lcm((3, 4, 1), (1, 2, 0)) == (3, 4, 1) def test_monomial_max(): assert monomial_max((3, 4, 5), (0, 5, 1), (6, 3, 9)) == (6, 5, 9) def test_monomial_min(): assert monomial_min((3, 4, 5), (0, 5, 1), (6, 3, 9)) == (0, 3, 1) def test_monomial_divides(): assert monomial_divides((1, 2, 3), (4, 5, 6)) is True assert monomial_divides((1, 2, 3), (0, 5, 6)) is False def test_Monomial(): m = Monomial((3, 4, 1), (x, y, z)) n = Monomial((1, 2, 0), (x, y, z)) assert m.as_expr() == x**3*y**4*z assert n.as_expr() == x**1*y**2 assert m.as_expr(a, b, c) == a**3*b**4*c assert n.as_expr(a, b, c) == a**1*b**2 assert m.exponents == (3, 4, 1) assert m.gens == (x, y, z) assert n.exponents == (1, 2, 0) assert n.gens == (x, y, z) assert m == (3, 4, 1) assert n != (3, 4, 1) assert m != (1, 2, 0) assert n == (1, 2, 0) assert m[0] == m[-3] == 3 assert m[1] == m[-2] == 4 assert m[2] == m[-1] == 1 assert n[0] == n[-3] == 1 assert n[1] == n[-2] == 2 assert n[2] == n[-1] == 0 assert m[:2] == (3, 4) assert n[:2] == (1, 2) assert m*n == Monomial((4, 6, 1)) assert m/n == Monomial((2, 2, 1)) assert m*(1, 2, 0) == Monomial((4, 6, 1)) assert m/(1, 2, 0) == Monomial((2, 2, 1)) assert m.gcd(n) == Monomial((1, 2, 0)) assert m.lcm(n) == Monomial((3, 4, 1)) assert m.gcd((1, 2, 0)) == Monomial((1, 2, 0)) assert m.lcm((1, 2, 0)) == Monomial((3, 4, 1)) assert m**0 == Monomial((0, 0, 0)) assert m**1 == m assert m**2 == Monomial((6, 8, 2)) assert m**3 == Monomial((9, 12, 3)) raises(ExactQuotientFailed, lambda: m/Monomial((5, 2, 0))) sympy-0.7.4.1/sympy/polys/tests/test_heuristicgcd.py0000644000175000017500000000674212253362407023034 0ustar georgeskgeorgeskfrom sympy.polys.rings import ring from sympy.polys.domains import ZZ from sympy.polys.heuristicgcd import heugcd def test_heugcd_univariate_integers(): R, x = ring("x", ZZ) f = x**4 + 8*x**3 + 21*x**2 + 22*x + 8 g = x**3 + 6*x**2 + 11*x + 6 h = x**2 + 3*x + 2 cff = x**2 + 5*x + 4 cfg = x + 3 assert heugcd(f, g) == (h, cff, cfg) f = x**4 - 4 g = x**4 + 4*x**2 + 4 h = x**2 + 2 cff = x**2 - 2 cfg = x**2 + 2 assert heugcd(f, g) == (h, cff, cfg) f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 h = 1 cff = f cfg = g assert heugcd(f, g) == (h, cff, cfg) f = - 352518131239247345597970242177235495263669787845475025293906825864749649589178600387510272*x**49 \ + 46818041807522713962450042363465092040687472354933295397472942006618953623327997952*x**42 \ + 378182690892293941192071663536490788434899030680411695933646320291525827756032*x**35 \ + 112806468807371824947796775491032386836656074179286744191026149539708928*x**28 \ - 12278371209708240950316872681744825481125965781519138077173235712*x**21 \ + 289127344604779611146960547954288113529690984687482920704*x**14 \ + 19007977035740498977629742919480623972236450681*x**7 \ + 311973482284542371301330321821976049 g = 365431878023781158602430064717380211405897160759702125019136*x**21 \ + 197599133478719444145775798221171663643171734081650688*x**14 \ - 9504116979659010018253915765478924103928886144*x**7 \ - 311973482284542371301330321821976049 # TODO: assert heugcd(f, f.diff(x))[0] == g f = 1317378933230047068160*x + 2945748836994210856960 g = 120352542776360960*x + 269116466014453760 h = 120352542776360960*x + 269116466014453760 cff = 10946 cfg = 1 assert heugcd(f, g) == (h, cff, cfg) def test_heugcd_multivariate_integers(): R, x, y = ring("x,y", ZZ) f, g = 2*x**2 + 4*x + 2, x + 1 assert heugcd(f, g) == (x + 1, 2*x + 2, 1) f, g = x + 1, 2*x**2 + 4*x + 2 assert heugcd(f, g) == (x + 1, 1, 2*x + 2) R, x, y, z, u = ring("x,y,z,u", ZZ) f, g = u**2 + 2*u + 1, 2*u + 2 assert heugcd(f, g) == (u + 1, u + 1, 2) f, g = z**2*u**2 + 2*z**2*u + z**2 + z*u + z, u**2 + 2*u + 1 h, cff, cfg = u + 1, z**2*u + z**2 + z, u + 1 assert heugcd(f, g) == (h, cff, cfg) assert heugcd(g, f) == (h, cfg, cff) R, x, y, z = ring("x,y,z", ZZ) f, g, h = R.fateman_poly_F_1() H, cff, cfg = heugcd(f, g) assert H == h and H*cff == f and H*cfg == g R, x, y, z, u, v = ring("x,y,z,u,v", ZZ) f, g, h = R.fateman_poly_F_1() H, cff, cfg = heugcd(f, g) assert H == h and H*cff == f and H*cfg == g R, x, y, z, u, v, a, b = ring("x,y,z,u,v,a,b", ZZ) f, g, h = R.fateman_poly_F_1() H, cff, cfg = heugcd(f, g) assert H == h and H*cff == f and H*cfg == g R, x, y, z, u, v, a, b, c, d = ring("x,y,z,u,v,a,b,c,d", ZZ) f, g, h = R.fateman_poly_F_1() H, cff, cfg = heugcd(f, g) assert H == h and H*cff == f and H*cfg == g R, x, y, z = ring("x,y,z", ZZ) f, g, h = R.fateman_poly_F_2() H, cff, cfg = heugcd(f, g) assert H == h and H*cff == f and H*cfg == g f, g, h = R.fateman_poly_F_3() H, cff, cfg = heugcd(f, g) assert H == h and H*cff == f and H*cfg == g R, x, y, z, t = ring("x,y,z,t", ZZ) f, g, h = R.fateman_poly_F_3() H, cff, cfg = heugcd(f, g) assert H == h and H*cff == f and H*cfg == g sympy-0.7.4.1/sympy/polys/tests/test_constructor.py0000644000175000017500000000636612253362407022746 0ustar georgeskgeorgesk"""Tests for tools for constructing domains for expressions. """ from sympy.polys.constructor import construct_domain from sympy.polys.domains import ZZ, QQ, RR, EX from sympy import S, sqrt, sin from sympy.abc import x, y def test_construct_domain(): assert construct_domain([1, 2, 3]) == (ZZ, [ZZ(1), ZZ(2), ZZ(3)]) assert construct_domain([1, 2, 3], field=True) == (QQ, [QQ(1), QQ(2), QQ(3)]) assert construct_domain([S(1), S(2), S(3)]) == (ZZ, [ZZ(1), ZZ(2), ZZ(3)]) assert construct_domain([S(1), S(2), S(3)], field=True) == (QQ, [QQ(1), QQ(2), QQ(3)]) assert construct_domain([S(1)/2, S(2)]) == (QQ, [QQ(1, 2), QQ(2)]) assert construct_domain([3.14, 1, S(1)/2]) == (RR, [RR(3.14), RR(1.0), RR(0.5)]) assert construct_domain([3.14, sqrt(2)], extension=None) == (EX, [EX(3.14), EX(sqrt(2))]) assert construct_domain([3.14, sqrt(2)], extension=True) == (EX, [EX(3.14), EX(sqrt(2))]) assert construct_domain([1, sqrt(2)], extension=None) == (EX, [EX(1), EX(sqrt(2))]) assert construct_domain([x, sqrt(x)]) == (EX, [EX(x), EX(sqrt(x))]) assert construct_domain([x, sqrt(x), sqrt(y)]) == (EX, [EX(x), EX(sqrt(x)), EX(sqrt(y))]) alg = QQ.algebraic_field(sqrt(2)) assert construct_domain([7, S(1)/2, sqrt(2)], extension=True) == \ (alg, [alg.convert(7), alg.convert(S(1)/2), alg.convert(sqrt(2))]) alg = QQ.algebraic_field(sqrt(2) + sqrt(3)) assert construct_domain([7, sqrt(2), sqrt(3)], extension=True) == \ (alg, [alg.convert(7), alg.convert(sqrt(2)), alg.convert(sqrt(3))]) dom = ZZ[x] assert construct_domain([2*x, 3]) == \ (dom, [dom.convert(2*x), dom.convert(3)]) dom = ZZ[x, y] assert construct_domain([2*x, 3*y]) == \ (dom, [dom.convert(2*x), dom.convert(3*y)]) dom = QQ[x] assert construct_domain([x/2, 3]) == \ (dom, [dom.convert(x/2), dom.convert(3)]) dom = QQ[x, y] assert construct_domain([x/2, 3*y]) == \ (dom, [dom.convert(x/2), dom.convert(3*y)]) dom = RR[x] assert construct_domain([x/2, 3.5]) == \ (dom, [dom.convert(x/2), dom.convert(3.5)]) dom = RR[x, y] assert construct_domain([x/2, 3.5*y]) == \ (dom, [dom.convert(x/2), dom.convert(3.5*y)]) dom = ZZ.frac_field(x) assert construct_domain([2/x, 3]) == \ (dom, [dom.convert(2/x), dom.convert(3)]) dom = ZZ.frac_field(x, y) assert construct_domain([2/x, 3*y]) == \ (dom, [dom.convert(2/x), dom.convert(3*y)]) dom = RR.frac_field(x) assert construct_domain([2/x, 3.5]) == \ (dom, [dom.convert(2/x), dom.convert(3.5)]) dom = RR.frac_field(x, y) assert construct_domain([2/x, 3.5*y]) == \ (dom, [dom.convert(2/x), dom.convert(3.5*y)]) assert construct_domain(2) == (ZZ, ZZ(2)) assert construct_domain(S(2)/3) == (QQ, QQ(2, 3)) assert construct_domain({}) == (ZZ, {}) def test_composite_option(): assert construct_domain({(1,): sin(y)}, composite=False) == \ (EX, {(1,): EX(sin(y))}) assert construct_domain({(1,): y}, composite=False) == \ (EX, {(1,): EX(y)}) assert construct_domain({(1, 1): 1}, composite=False) == \ (ZZ, {(1, 1): 1}) assert construct_domain({(1, 0): y}, composite=False) == \ (EX, {(1, 0): EX(y)}) sympy-0.7.4.1/sympy/polys/tests/test_partfrac.py0000644000175000017500000001227512253362407022157 0ustar georgeskgeorgesk"""Tests for algorithms for partial fraction decomposition of rational functions. """ from sympy.polys.partfrac import ( apart_undetermined_coeffs, apart_full_decomposition, apart, apart_list_full_decomposition, apart_list, assemble_partfrac_list ) from sympy import (S, Poly, E, pi, I, Matrix, Eq, RootSum, Lambda, Symbol, Dummy, factor, together, sqrt, Expr) from sympy.utilities.pytest import raises from sympy.abc import x, y, a, b, c def test_apart(): assert apart(1) == 1 assert apart(1, x) == 1 f, g = (x**2 + 1)/(x + 1), 2/(x + 1) + x - 1 assert apart(f, full=False) == g assert apart(f, full=True) == g f, g = 1/(x + 2)/(x + 1), 1/(1 + x) - 1/(2 + x) assert apart(f, full=False) == g assert apart(f, full=True) == g f, g = 1/(x + 1)/(x + 5), -1/(5 + x)/4 + 1/(1 + x)/4 assert apart(f, full=False) == g assert apart(f, full=True) == g assert apart((E*x + 2)/(x - pi)*(x - 1), x) == \ 2 - E + E*pi + E*x + (E*pi + 2)*(pi - 1)/(x - pi) assert apart(Eq((x**2 + 1)/(x + 1), x), x) == Eq(x - 1 + 2/(x + 1), x) raises(NotImplementedError, lambda: apart(1/(x + 1)/(y + 2))) def test_apart_matrix(): M = Matrix(2, 2, lambda i, j: 1/(x + i + 1)/(x + j)) assert apart(M) == Matrix([ [1/x - 1/(x + 1), (x + 1)**(-2)], [1/(2*x) - (S(1)/2)/(x + 2), 1/(x + 1) - 1/(x + 2)], ]) def test_apart_symbolic(): f = a*x**4 + (2*b + 2*a*c)*x**3 + (4*b*c - a**2 + a*c**2)*x**2 + \ (-2*a*b + 2*b*c**2)*x - b**2 g = a**2*x**4 + (2*a*b + 2*c*a**2)*x**3 + (4*a*b*c + b**2 + a**2*c**2)*x**2 + (2*c*b**2 + 2*a*b*c**2)*x + b**2*c**2 assert apart(f/g, x) == 1/a - 1/(x + c)**2 - b**2/(a*(a*x + b)**2) assert apart(1/((x + a)*(x + b)*(x + c)), x) == \ 1/((a - c)*(b - c)*(c + x)) - 1/((a - b)*(b - c)*(b + x)) + \ 1/((a - b)*(a - c)*(a + x)) def test_apart_extension(): f = 2/(x**2 + 1) g = I/(x + I) - I/(x - I) assert apart(f, extension=I) == g assert apart(f, gaussian=True) == g f = x/((x - 2)*(x + I)) assert factor(together(apart(f))) == f def test_apart_full(): f = 1/(x**2 + 1) assert apart(f, full=False) == f assert apart(f, full=True) == \ -RootSum(x**2 + 1, Lambda(a, a/(x - a)), auto=False)/2 f = 1/(x**3 + x + 1) assert apart(f, full=False) == f assert apart(f, full=True) == \ RootSum(x**3 + x + 1, Lambda(a, (6*a**2/31 - 9*a/31 + S(4)/31)/(x - a)), auto=False) f = 1/(x**5 + 1) assert apart(f, full=False) == \ (-S(1)/5)*((x**3 - 2*x**2 + 3*x - 4)/(x**4 - x**3 + x**2 - x + 1)) + (S(1)/5)/(x + 1) assert apart(f, full=True) == \ -RootSum(x**4 - x**3 + x**2 - x + 1, Lambda(a, a/(x - a)), auto=False)/5 + (S(1)/5)/(x + 1) def test_apart_undetermined_coeffs(): p = Poly(2*x - 3) q = Poly(x**9 - x**8 - x**6 + x**5 - 2*x**2 + 3*x - 1) r = (-x**7 - x**6 - x**5 + 4)/(x**8 - x**5 - 2*x + 1) + 1/(x - 1) assert apart_undetermined_coeffs(p, q) == r p = Poly(1, x, domain='ZZ[a,b]') q = Poly((x + a)*(x + b), x, domain='ZZ[a,b]') r = 1/((a - b)*(b + x)) - 1/((a - b)*(a + x)) assert apart_undetermined_coeffs(p, q) == r def test_apart_list(): from sympy.utilities.iterables import numbered_symbols w0, w1, w2 = Symbol("w0"), Symbol("w1"), Symbol("w2") _a = Dummy("a") f = (-2*x - 2*x**2) / (3*x**2 - 6*x) assert apart_list(f, x, dummies=numbered_symbols("w")) == (-1, Poly(S(2)/3, x, domain='QQ'), [(Poly(w0 - 2, w0, domain='ZZ'), Lambda(_a, 2), Lambda(_a, -_a + x), 1)]) assert apart_list(2/(x**2-2), x, dummies=numbered_symbols("w")) == (1, Poly(0, x, domain='ZZ'), [(Poly(w0**2 - 2, w0, domain='ZZ'), Lambda(_a, _a/2), Lambda(_a, -_a + x), 1)]) f = 36 / (x**5 - 2*x**4 - 2*x**3 + 4*x**2 + x - 2) assert apart_list(f, x, dummies=numbered_symbols("w")) == (1, Poly(0, x, domain='ZZ'), [(Poly(w0 - 2, w0, domain='ZZ'), Lambda(_a, 4), Lambda(_a, -_a + x), 1), (Poly(w1**2 - 1, w1, domain='ZZ'), Lambda(_a, -3*_a - 6), Lambda(_a, -_a + x), 2), (Poly(w2 + 1, w2, domain='ZZ'), Lambda(_a, -4), Lambda(_a, -_a + x), 1)]) def test_assemble_partfrac_list(): f = 36 / (x**5 - 2*x**4 - 2*x**3 + 4*x**2 + x - 2) pfd = apart_list(f) assert assemble_partfrac_list(pfd) == -4/(x + 1) - 3/(x + 1)**2 - 9/(x - 1)**2 + 4/(x - 2) a = Dummy("a") pfd = (1, Poly(0, x, domain='ZZ'), [([sqrt(2),-sqrt(2)], Lambda(a, a/2), Lambda(a, -a + x), 1)]) assert assemble_partfrac_list(pfd) == -1/(sqrt(2)*(x + sqrt(2))) + 1/(sqrt(2)*(x - sqrt(2))) def test_noncommutative_pseudomultivariate(): class foo(Expr): is_commutative=False e = x/(x + x*y) c = 1/(1 + y) assert apart(e + foo(e)) == c + foo(c) assert apart(e*foo(e)) == c*foo(c) def test_issue_2699(): assert apart( 2*x/(x**2 + 1) - (x - 1)/(2*(x**2 + 1)) + 1/(2*(x + 1)) - 2/x) == \ (3*x + 1)/(x**2 + 1)/2 + 1/(x + 1)/2 - 2/x sympy-0.7.4.1/sympy/polys/tests/test_polyoptions.py0000644000175000017500000002733612253362407022760 0ustar georgeskgeorgesk"""Tests for options manager for :class:`Poly` and public API functions. """ from sympy.polys.polyoptions import ( Options, Expand, Gens, Wrt, Sort, Order, Field, Greedy, Domain, Split, Gaussian, Extension, Modulus, Symmetric, Strict, Auto, Frac, Formal, Polys, Include, All, Gen, Symbols, Method) from sympy.polys.orderings import lex from sympy.polys.domains import FF, GF, ZZ, QQ, RR, EX from sympy.polys.polyerrors import OptionError, GeneratorsError from sympy import Integer, Symbol, I, sqrt from sympy.utilities.pytest import raises from sympy.abc import x, y, z def test_Options_clone(): opt = Options((x, y, z), {'domain': 'ZZ'}) assert opt.gens == (x, y, z) assert opt.domain == ZZ assert ('order' in opt) is False new_opt = opt.clone({'gens': (x, y), 'order': 'lex'}) assert opt.gens == (x, y, z) assert opt.domain == ZZ assert ('order' in opt) is False assert new_opt.gens == (x, y) assert new_opt.domain == ZZ assert ('order' in new_opt) is True def test_Expand_preprocess(): assert Expand.preprocess(False) is False assert Expand.preprocess(True) is True assert Expand.preprocess(0) is False assert Expand.preprocess(1) is True raises(OptionError, lambda: Expand.preprocess(x)) def test_Expand_postprocess(): opt = {'expand': True} Expand.postprocess(opt) assert opt == {'expand': True} def test_Gens_preprocess(): assert Gens.preprocess((None,)) == () assert Gens.preprocess((x, y, z)) == (x, y, z) assert Gens.preprocess(((x, y, z),)) == (x, y, z) a = Symbol('a', commutative=False) raises(GeneratorsError, lambda: Gens.preprocess((x, x, y))) raises(GeneratorsError, lambda: Gens.preprocess((x, y, a))) def test_Gens_postprocess(): opt = {'gens': (x, y)} Gens.postprocess(opt) assert opt == {'gens': (x, y)} def test_Wrt_preprocess(): assert Wrt.preprocess(x) == ['x'] assert Wrt.preprocess('') == [] assert Wrt.preprocess(' ') == [] assert Wrt.preprocess('x,y') == ['x', 'y'] assert Wrt.preprocess('x y') == ['x', 'y'] assert Wrt.preprocess('x, y') == ['x', 'y'] assert Wrt.preprocess('x , y') == ['x', 'y'] assert Wrt.preprocess(' x, y') == ['x', 'y'] assert Wrt.preprocess(' x, y') == ['x', 'y'] assert Wrt.preprocess([x, y]) == ['x', 'y'] raises(OptionError, lambda: Wrt.preprocess(',')) raises(OptionError, lambda: Wrt.preprocess(0)) def test_Wrt_postprocess(): opt = {'wrt': ['x']} Wrt.postprocess(opt) assert opt == {'wrt': ['x']} def test_Sort_preprocess(): assert Sort.preprocess([x, y, z]) == ['x', 'y', 'z'] assert Sort.preprocess((x, y, z)) == ['x', 'y', 'z'] assert Sort.preprocess('x > y > z') == ['x', 'y', 'z'] assert Sort.preprocess('x>y>z') == ['x', 'y', 'z'] raises(OptionError, lambda: Sort.preprocess(0)) raises(OptionError, lambda: Sort.preprocess(set([x, y, z]))) def test_Sort_postprocess(): opt = {'sort': 'x > y'} Sort.postprocess(opt) assert opt == {'sort': 'x > y'} def test_Order_preprocess(): assert Order.preprocess('lex') == lex def test_Order_postprocess(): opt = {'order': True} Order.postprocess(opt) assert opt == {'order': True} def test_Field_preprocess(): assert Field.preprocess(False) is False assert Field.preprocess(True) is True assert Field.preprocess(0) is False assert Field.preprocess(1) is True raises(OptionError, lambda: Field.preprocess(x)) def test_Field_postprocess(): opt = {'field': True} Field.postprocess(opt) assert opt == {'field': True} def test_Greedy_preprocess(): assert Greedy.preprocess(False) is False assert Greedy.preprocess(True) is True assert Greedy.preprocess(0) is False assert Greedy.preprocess(1) is True raises(OptionError, lambda: Greedy.preprocess(x)) def test_Greedy_postprocess(): opt = {'greedy': True} Greedy.postprocess(opt) assert opt == {'greedy': True} def test_Domain_preprocess(): assert Domain.preprocess(ZZ) == ZZ assert Domain.preprocess(QQ) == QQ assert Domain.preprocess(EX) == EX assert Domain.preprocess(FF(2)) == FF(2) assert Domain.preprocess(ZZ[x, y]) == ZZ[x, y] assert Domain.preprocess('Z') == ZZ assert Domain.preprocess('Q') == QQ assert Domain.preprocess('ZZ') == ZZ assert Domain.preprocess('QQ') == QQ assert Domain.preprocess('EX') == EX assert Domain.preprocess('FF(23)') == FF(23) assert Domain.preprocess('GF(23)') == GF(23) raises(OptionError, lambda: Domain.preprocess('Z[]')) assert Domain.preprocess('Z[x]') == ZZ[x] assert Domain.preprocess('Q[x]') == QQ[x] assert Domain.preprocess('ZZ[x]') == ZZ[x] assert Domain.preprocess('QQ[x]') == QQ[x] assert Domain.preprocess('Z[x,y]') == ZZ[x, y] assert Domain.preprocess('Q[x,y]') == QQ[x, y] assert Domain.preprocess('ZZ[x,y]') == ZZ[x, y] assert Domain.preprocess('QQ[x,y]') == QQ[x, y] raises(OptionError, lambda: Domain.preprocess('Z()')) assert Domain.preprocess('Z(x)') == ZZ.frac_field(x) assert Domain.preprocess('Q(x)') == QQ.frac_field(x) assert Domain.preprocess('ZZ(x)') == ZZ.frac_field(x) assert Domain.preprocess('QQ(x)') == QQ.frac_field(x) assert Domain.preprocess('Z(x,y)') == ZZ.frac_field(x, y) assert Domain.preprocess('Q(x,y)') == QQ.frac_field(x, y) assert Domain.preprocess('ZZ(x,y)') == ZZ.frac_field(x, y) assert Domain.preprocess('QQ(x,y)') == QQ.frac_field(x, y) assert Domain.preprocess('Q') == QQ.algebraic_field(I) assert Domain.preprocess('QQ') == QQ.algebraic_field(I) assert Domain.preprocess('Q') == QQ.algebraic_field(sqrt(2), I) assert Domain.preprocess( 'QQ') == QQ.algebraic_field(sqrt(2), I) raises(OptionError, lambda: Domain.preprocess('abc')) def test_Domain_postprocess(): raises(GeneratorsError, lambda: Domain.postprocess({'gens': (x, y), 'domain': ZZ[y, z]})) raises(GeneratorsError, lambda: Domain.postprocess({'gens': (), 'domain': EX})) raises(GeneratorsError, lambda: Domain.postprocess({'domain': EX})) def test_Split_preprocess(): assert Split.preprocess(False) is False assert Split.preprocess(True) is True assert Split.preprocess(0) is False assert Split.preprocess(1) is True raises(OptionError, lambda: Split.preprocess(x)) def test_Split_postprocess(): raises(NotImplementedError, lambda: Split.postprocess({'split': True})) def test_Gaussian_preprocess(): assert Gaussian.preprocess(False) is False assert Gaussian.preprocess(True) is True assert Gaussian.preprocess(0) is False assert Gaussian.preprocess(1) is True raises(OptionError, lambda: Gaussian.preprocess(x)) def test_Gaussian_postprocess(): opt = {'gaussian': True} Gaussian.postprocess(opt) assert opt == { 'gaussian': True, 'extension': set([I]), 'domain': QQ.algebraic_field(I), } def test_Extension_preprocess(): assert Extension.preprocess(True) is True assert Extension.preprocess(1) is True assert Extension.preprocess([]) is None assert Extension.preprocess(sqrt(2)) == set([sqrt(2)]) assert Extension.preprocess([sqrt(2)]) == set([sqrt(2)]) assert Extension.preprocess([sqrt(2), I]) == set([sqrt(2), I]) raises(OptionError, lambda: Extension.preprocess(False)) raises(OptionError, lambda: Extension.preprocess(0)) def test_Extension_postprocess(): opt = {'extension': set([sqrt(2)])} Extension.postprocess(opt) assert opt == { 'extension': set([sqrt(2)]), 'domain': QQ.algebraic_field(sqrt(2)), } opt = {'extension': True} Extension.postprocess(opt) assert opt == {'extension': True} def test_Modulus_preprocess(): assert Modulus.preprocess(23) == 23 assert Modulus.preprocess(Integer(23)) == 23 raises(OptionError, lambda: Modulus.preprocess(0)) raises(OptionError, lambda: Modulus.preprocess(x)) def test_Modulus_postprocess(): opt = {'modulus': 5} Modulus.postprocess(opt) assert opt == { 'modulus': 5, 'domain': FF(5), } opt = {'modulus': 5, 'symmetric': False} Modulus.postprocess(opt) assert opt == { 'modulus': 5, 'domain': FF(5, False), 'symmetric': False, } def test_Symmetric_preprocess(): assert Symmetric.preprocess(False) is False assert Symmetric.preprocess(True) is True assert Symmetric.preprocess(0) is False assert Symmetric.preprocess(1) is True raises(OptionError, lambda: Symmetric.preprocess(x)) def test_Symmetric_postprocess(): opt = {'symmetric': True} Symmetric.postprocess(opt) assert opt == {'symmetric': True} def test_Strict_preprocess(): assert Strict.preprocess(False) is False assert Strict.preprocess(True) is True assert Strict.preprocess(0) is False assert Strict.preprocess(1) is True raises(OptionError, lambda: Strict.preprocess(x)) def test_Strict_postprocess(): opt = {'strict': True} Strict.postprocess(opt) assert opt == {'strict': True} def test_Auto_preprocess(): assert Auto.preprocess(False) is False assert Auto.preprocess(True) is True assert Auto.preprocess(0) is False assert Auto.preprocess(1) is True raises(OptionError, lambda: Auto.preprocess(x)) def test_Auto_postprocess(): opt = {'auto': True} Auto.postprocess(opt) assert opt == {'auto': True} def test_Frac_preprocess(): assert Frac.preprocess(False) is False assert Frac.preprocess(True) is True assert Frac.preprocess(0) is False assert Frac.preprocess(1) is True raises(OptionError, lambda: Frac.preprocess(x)) def test_Frac_postprocess(): opt = {'frac': True} Frac.postprocess(opt) assert opt == {'frac': True} def test_Formal_preprocess(): assert Formal.preprocess(False) is False assert Formal.preprocess(True) is True assert Formal.preprocess(0) is False assert Formal.preprocess(1) is True raises(OptionError, lambda: Formal.preprocess(x)) def test_Formal_postprocess(): opt = {'formal': True} Formal.postprocess(opt) assert opt == {'formal': True} def test_Polys_preprocess(): assert Polys.preprocess(False) is False assert Polys.preprocess(True) is True assert Polys.preprocess(0) is False assert Polys.preprocess(1) is True raises(OptionError, lambda: Polys.preprocess(x)) def test_Polys_postprocess(): opt = {'polys': True} Polys.postprocess(opt) assert opt == {'polys': True} def test_Include_preprocess(): assert Include.preprocess(False) is False assert Include.preprocess(True) is True assert Include.preprocess(0) is False assert Include.preprocess(1) is True raises(OptionError, lambda: Include.preprocess(x)) def test_Include_postprocess(): opt = {'include': True} Include.postprocess(opt) assert opt == {'include': True} def test_All_preprocess(): assert All.preprocess(False) is False assert All.preprocess(True) is True assert All.preprocess(0) is False assert All.preprocess(1) is True raises(OptionError, lambda: All.preprocess(x)) def test_All_postprocess(): opt = {'all': True} All.postprocess(opt) assert opt == {'all': True} def test_Gen_postprocess(): opt = {'gen': x} Gen.postprocess(opt) assert opt == {'gen': x} def test_Symbols_preprocess(): raises(OptionError, lambda: Symbols.preprocess(x)) def test_Symbols_postprocess(): opt = {'symbols': [x, y, z]} Symbols.postprocess(opt) assert opt == {'symbols': [x, y, z]} def test_Method_preprocess(): raises(OptionError, lambda: Method.preprocess(10)) def test_Method_postprocess(): opt = {'method': 'f5b'} Method.postprocess(opt) assert opt == {'method': 'f5b'} sympy-0.7.4.1/sympy/polys/tests/test_factortools.py0000644000175000017500000005201012253362407022703 0ustar georgeskgeorgesk"""Tools for polynomial factorization routines in characteristic zero. """ from sympy.polys.rings import ring, xring from sympy.polys.domains import FF, ZZ, QQ, RR, EX from sympy.polys import polyconfig as config from sympy.polys.polyerrors import DomainError from sympy.polys.polyclasses import ANP from sympy.polys.specialpolys import f_polys, w_polys from sympy import nextprime, sin, sqrt, I from sympy.utilities.pytest import raises from sympy.core.compatibility import xrange f_0, f_1, f_2, f_3, f_4, f_5, f_6 = f_polys() w_1, w_2 = w_polys() def test_dup_trial_division(): R, x = ring("x", ZZ) assert R.dup_trial_division(x**5 + 8*x**4 + 25*x**3 + 38*x**2 + 28*x + 8, (x + 1, x + 2)) == [(x + 1, 2), (x + 2, 3)] def test_dmp_trial_division(): R, x, y = ring("x,y", ZZ) assert R.dmp_trial_division(x**5 + 8*x**4 + 25*x**3 + 38*x**2 + 28*x + 8, (x + 1, x + 2)) == [(x + 1, 2), (x + 2, 3)] def test_dup_zz_mignotte_bound(): R, x = ring("x", ZZ) assert R.dup_zz_mignotte_bound(2*x**2 + 3*x + 4) == 32 def test_dmp_zz_mignotte_bound(): R, x, y = ring("x,y", ZZ) assert R.dmp_zz_mignotte_bound(2*x**2 + 3*x + 4) == 32 def test_dup_zz_hensel_step(): R, x = ring("x", ZZ) f = x**4 - 1 g = x**3 + 2*x**2 - x - 2 h = x - 2 s = -2 t = 2*x**2 - 2*x - 1 G, H, S, T = R.dup_zz_hensel_step(5, f, g, h, s, t) assert G == x**3 + 7*x**2 - x - 7 assert H == x - 7 assert S == 8 assert T == -8*x**2 - 12*x - 1 def test_dup_zz_hensel_lift(): R, x = ring("x", ZZ) f = x**4 - 1 F = [x - 1, x - 2, x + 2, x + 1] assert R.dup_zz_hensel_lift(ZZ(5), f, F, 4) == \ [x - 1, x - 182, x + 182, x + 1] def test_dup_zz_irreducible_p(): R, x = ring("x", ZZ) assert R.dup_zz_irreducible_p(3*x**4 + 2*x**3 + 6*x**2 + 8*x + 7) is None assert R.dup_zz_irreducible_p(3*x**4 + 2*x**3 + 6*x**2 + 8*x + 4) is None assert R.dup_zz_irreducible_p(3*x**4 + 2*x**3 + 6*x**2 + 8*x + 10) is True assert R.dup_zz_irreducible_p(3*x**4 + 2*x**3 + 6*x**2 + 8*x + 14) is True def test_dup_cyclotomic_p(): R, x = ring("x", ZZ) assert R.dup_cyclotomic_p(x - 1) is True assert R.dup_cyclotomic_p(x + 1) is True assert R.dup_cyclotomic_p(x**2 + x + 1) is True assert R.dup_cyclotomic_p(x**2 + 1) is True assert R.dup_cyclotomic_p(x**4 + x**3 + x**2 + x + 1) is True assert R.dup_cyclotomic_p(x**2 - x + 1) is True assert R.dup_cyclotomic_p(x**6 + x**5 + x**4 + x**3 + x**2 + x + 1) is True assert R.dup_cyclotomic_p(x**4 + 1) is True assert R.dup_cyclotomic_p(x**6 + x**3 + 1) is True assert R.dup_cyclotomic_p(0) is False assert R.dup_cyclotomic_p(1) is False assert R.dup_cyclotomic_p(x) is False assert R.dup_cyclotomic_p(x + 2) is False assert R.dup_cyclotomic_p(3*x + 1) is False assert R.dup_cyclotomic_p(x**2 - 1) is False f = x**16 + x**14 - x**10 + x**8 - x**6 + x**2 + 1 assert R.dup_cyclotomic_p(f) is False g = x**16 + x**14 - x**10 - x**8 - x**6 + x**2 + 1 assert R.dup_cyclotomic_p(g) is True R, x = ring("x", QQ) assert R.dup_cyclotomic_p(x**2 + x + 1) is True assert R.dup_cyclotomic_p(QQ(1,2)*x**2 + x + 1) is False R, x = ring("x", ZZ["y"]) assert R.dup_cyclotomic_p(x**2 + x + 1) is False def test_dup_zz_cyclotomic_poly(): R, x = ring("x", ZZ) assert R.dup_zz_cyclotomic_poly(1) == x - 1 assert R.dup_zz_cyclotomic_poly(2) == x + 1 assert R.dup_zz_cyclotomic_poly(3) == x**2 + x + 1 assert R.dup_zz_cyclotomic_poly(4) == x**2 + 1 assert R.dup_zz_cyclotomic_poly(5) == x**4 + x**3 + x**2 + x + 1 assert R.dup_zz_cyclotomic_poly(6) == x**2 - x + 1 assert R.dup_zz_cyclotomic_poly(7) == x**6 + x**5 + x**4 + x**3 + x**2 + x + 1 assert R.dup_zz_cyclotomic_poly(8) == x**4 + 1 assert R.dup_zz_cyclotomic_poly(9) == x**6 + x**3 + 1 def test_dup_zz_cyclotomic_factor(): R, x = ring("x", ZZ) assert R.dup_zz_cyclotomic_factor(0) is None assert R.dup_zz_cyclotomic_factor(1) is None assert R.dup_zz_cyclotomic_factor(2*x**10 - 1) is None assert R.dup_zz_cyclotomic_factor(x**10 - 3) is None assert R.dup_zz_cyclotomic_factor(x**10 + x**5 - 1) is None assert R.dup_zz_cyclotomic_factor(x + 1) == [x + 1] assert R.dup_zz_cyclotomic_factor(x - 1) == [x - 1] assert R.dup_zz_cyclotomic_factor(x**2 + 1) == [x**2 + 1] assert R.dup_zz_cyclotomic_factor(x**2 - 1) == [x - 1, x + 1] assert R.dup_zz_cyclotomic_factor(x**27 + 1) == \ [x + 1, x**2 - x + 1, x**6 - x**3 + 1, x**18 - x**9 + 1] assert R.dup_zz_cyclotomic_factor(x**27 - 1) == \ [x - 1, x**2 + x + 1, x**6 + x**3 + 1, x**18 + x**9 + 1] def test_dup_zz_factor(): R, x = ring("x", ZZ) assert R.dup_zz_factor(0) == (0, []) assert R.dup_zz_factor(7) == (7, []) assert R.dup_zz_factor(-7) == (-7, []) assert R.dup_zz_factor_sqf(0) == (0, []) assert R.dup_zz_factor_sqf(7) == (7, []) assert R.dup_zz_factor_sqf(-7) == (-7, []) assert R.dup_zz_factor(2*x + 4) == (2, [(x + 2, 1)]) assert R.dup_zz_factor_sqf(2*x + 4) == (2, [x + 2]) f = x**4 + x + 1 for i in xrange(0, 20): assert R.dup_zz_factor(f) == (1, [(f, 1)]) assert R.dup_zz_factor(x**2 + 2*x + 2) == \ (1, [(x**2 + 2*x + 2, 1)]) assert R.dup_zz_factor(18*x**2 + 12*x + 2) == \ (2, [(3*x + 1, 2)]) assert R.dup_zz_factor(-9*x**2 + 1) == \ (-1, [(3*x - 1, 1), (3*x + 1, 1)]) assert R.dup_zz_factor_sqf(-9*x**2 + 1) == \ (-1, [3*x - 1, 3*x + 1]) assert R.dup_zz_factor(x**3 - 6*x**2 + 11*x - 6) == \ (1, [(x - 3, 1), (x - 2, 1), (x - 1, 1)]) assert R.dup_zz_factor_sqf(x**3 - 6*x**2 + 11*x - 6) == \ (1, [x - 3, x - 2, x - 1]) assert R.dup_zz_factor(3*x**3 + 10*x**2 + 13*x + 10) == \ (1, [(x + 2, 1), (3*x**2 + 4*x + 5, 1)]) assert R.dup_zz_factor_sqf(3*x**3 + 10*x**2 + 13*x + 10) == \ (1, [x + 2, 3*x**2 + 4*x + 5]) assert R.dup_zz_factor(-x**6 + x**2) == \ (-1, [(x - 1, 1), (x + 1, 1), (x, 2), (x**2 + 1, 1)]) f = 1080*x**8 + 5184*x**7 + 2099*x**6 + 744*x**5 + 2736*x**4 - 648*x**3 + 129*x**2 - 324 assert R.dup_zz_factor(f) == \ (1, [(5*x**4 + 24*x**3 + 9*x**2 + 12, 1), (216*x**4 + 31*x**2 - 27, 1)]) f = -29802322387695312500000000000000000000*x**25 \ + 2980232238769531250000000000000000*x**20 \ + 1743435859680175781250000000000*x**15 \ + 114142894744873046875000000*x**10 \ - 210106372833251953125*x**5 \ + 95367431640625 assert R.dup_zz_factor(f) == \ (-95367431640625, [(5*x - 1, 1), (100*x**2 + 10*x - 1, 2), (625*x**4 + 125*x**3 + 25*x**2 + 5*x + 1, 1), (10000*x**4 - 3000*x**3 + 400*x**2 - 20*x + 1, 2), (10000*x**4 + 2000*x**3 + 400*x**2 + 30*x + 1, 2)]) f = x**10 - 1 config.setup('USE_CYCLOTOMIC_FACTOR', True) F_0 = R.dup_zz_factor(f) config.setup('USE_CYCLOTOMIC_FACTOR', False) F_1 = R.dup_zz_factor(f) assert F_0 == F_1 == \ (1, [(x - 1, 1), (x + 1, 1), (x**4 - x**3 + x**2 - x + 1, 1), (x**4 + x**3 + x**2 + x + 1, 1)]) config.setup('USE_CYCLOTOMIC_FACTOR') f = x**10 + 1 config.setup('USE_CYCLOTOMIC_FACTOR', True) F_0 = R.dup_zz_factor(f) config.setup('USE_CYCLOTOMIC_FACTOR', False) F_1 = R.dup_zz_factor(f) assert F_0 == F_1 == \ (1, [(x**2 + 1, 1), (x**8 - x**6 + x**4 - x**2 + 1, 1)]) config.setup('USE_CYCLOTOMIC_FACTOR') def test_dmp_zz_wang(): R, x,y,z = ring("x,y,z", ZZ) UV, _x = ring("x", ZZ) p = ZZ(nextprime(R.dmp_zz_mignotte_bound(w_1))) assert p == 6291469 t_1, k_1, e_1 = y, 1, ZZ(-14) t_2, k_2, e_2 = z, 2, ZZ(3) t_3, k_3, e_3 = y + z, 2, ZZ(-11) t_4, k_4, e_4 = y - z, 1, ZZ(-17) T = [t_1, t_2, t_3, t_4] K = [k_1, k_2, k_3, k_4] E = [e_1, e_2, e_3, e_4] T = zip([ t.drop(x) for t in T ], K) A = [ZZ(-14), ZZ(3)] S = R.dmp_eval_tail(w_1, A) cs, s = UV.dup_primitive(S) assert cs == 1 and s == S == \ 1036728*_x**6 + 915552*_x**5 + 55748*_x**4 + 105621*_x**3 - 17304*_x**2 - 26841*_x - 644 assert R.dmp_zz_wang_non_divisors(E, cs, ZZ(4)) == [7, 3, 11, 17] assert UV.dup_sqf_p(s) and UV.dup_degree(s) == R.dmp_degree(w_1) _, H = UV.dup_zz_factor_sqf(s) h_1 = 44*_x**2 + 42*_x + 1 h_2 = 126*_x**2 - 9*_x + 28 h_3 = 187*_x**2 - 23 assert H == [h_1, h_2, h_3] LC = [ lc.drop(x) for lc in [-4*y - 4*z, -y*z**2, y**2 - z**2] ] assert R.dmp_zz_wang_lead_coeffs(w_1, T, cs, E, H, A) == (w_1, H, LC) H_1 = [44*x**2 + 42*x + 1, 126*x**2 - 9*x + 28, 187*x**2 - 23] H_2 = [-4*x**2*y - 12*x**2 - 3*x*y + 1, -9*x**2*y - 9*x - 2*y, x**2*y**2 - 9*x**2 + y - 9] H_3 = [-4*x**2*y - 12*x**2 - 3*x*y + 1, -9*x**2*y - 9*x - 2*y, x**2*y**2 - 9*x**2 + y - 9] c_1 = -70686*x**5 - 5863*x**4 - 17826*x**3 + 2009*x**2 + 5031*x + 74 c_2 = 9*x**5*y**4 + 12*x**5*y**3 - 45*x**5*y**2 - 108*x**5*y - 324*x**5 + 18*x**4*y**3 - 216*x**4*y**2 - 810*x**4*y + 2*x**3*y**4 + 9*x**3*y**3 - 252*x**3*y**2 - 288*x**3*y - 945*x**3 - 30*x**2*y**2 - 414*x**2*y + 2*x*y**3 - 54*x*y**2 - 3*x*y + 81*x + 12*y c_3 = -36*x**4*y**2 - 108*x**4*y - 27*x**3*y**2 - 36*x**3*y - 108*x**3 - 8*x**2*y**2 - 42*x**2*y - 6*x*y**2 + 9*x + 2*y # TODO #assert R.dmp_zz_diophantine(H_1, c_1, [], 5, p) == [-3*x, -2, 1] #assert R.dmp_zz_diophantine(H_2, c_2, [ZZ(-14)], 5, p) == [-x*y, -3*x, -6] #assert R.dmp_zz_diophantine(H_3, c_3, [ZZ(-14)], 5, p) == [0, 0, -1] factors = R.dmp_zz_wang_hensel_lifting(w_1, H, LC, A, p) assert R.dmp_expand(factors) == w_1 def test_issue_3256(): # This tests a bug in the Wang algorithm that occured only with a very # specific set of random numbers. random_sequence = [-1, -1, 0, 0, 0, 0, -1, -1, 0, -1, 3, -1, 3, 3, 3, 3, -1, 3] R, x, y, z = ring("x,y,z", ZZ) f = 2*x**2 + y*z - y - z**2 + z assert R.dmp_zz_wang(f, seed=random_sequence) == [f] def test_dmp_zz_factor(): R, x = ring("x", ZZ) assert R.dmp_zz_factor(0) == (0, []) assert R.dmp_zz_factor(7) == (7, []) assert R.dmp_zz_factor(-7) == (-7, []) assert R.dmp_zz_factor(x**2 - 9) == (1, [(x - 3, 1), (x + 3, 1)]) R, x, y = ring("x,y", ZZ) assert R.dmp_zz_factor(0) == (0, []) assert R.dmp_zz_factor(7) == (7, []) assert R.dmp_zz_factor(-7) == (-7, []) assert R.dmp_zz_factor(x) == (1, [(x, 1)]) assert R.dmp_zz_factor(4*x) == (4, [(x, 1)]) assert R.dmp_zz_factor(4*x + 2) == (2, [(2*x + 1, 1)]) assert R.dmp_zz_factor(x*y + 1) == (1, [(x*y + 1, 1)]) assert R.dmp_zz_factor(y**2 + 1) == (1, [(y**2 + 1, 1)]) assert R.dmp_zz_factor(y**2 - 1) == (1, [(y - 1, 1), (y + 1, 1)]) assert R.dmp_zz_factor(x**2*y**2 + 6*x**2*y + 9*x**2 - 1) == (1, [(x*y + 3*x - 1, 1), (x*y + 3*x + 1, 1)]) assert R.dmp_zz_factor(x**2*y**2 - 9) == (1, [(x*y - 3, 1), (x*y + 3, 1)]) R, x, y, z = ring("x,y,z", ZZ) assert R.dmp_zz_factor(x**2*y**2*z**2 - 9) == \ (1, [(x*y*z - 3, 1), (x*y*z + 3, 1)]) R, x, y, z, u = ring("x,y,z,u", ZZ) assert R.dmp_zz_factor(x**2*y**2*z**2*u**2 - 9) == \ (1, [(x*y*z*u - 3, 1), (x*y*z*u + 3, 1)]) R, x, y, z = ring("x,y,z", ZZ) assert R.dmp_zz_factor(f_1) == \ (1, [(x + y*z + 20, 1), (x*y + z + 10, 1), (x*z + y + 30, 1)]) assert R.dmp_zz_factor(f_2) == \ (1, [(x**2*y**2 + x**2*z**2 + y + 90, 1), (x**3*y + x**3*z + z - 11, 1)]) assert R.dmp_zz_factor(f_3) == \ (1, [(x**2*y**2 + x*z**4 + x + z, 1), (x**3 + x*y*z + y**2 + y*z**3, 1)]) assert R.dmp_zz_factor(f_4) == \ (-1, [(x*y**3 + z**2, 1), (x**2*z + y**4*z**2 + 5, 1), (x**3*y - z**2 - 3, 1), (x**3*y**4 + z**2, 1)]) assert R.dmp_zz_factor(f_5) == \ (-1, [(x + y - z, 3)]) R, x, y, z, t = ring("x,y,z,t", ZZ) assert R.dmp_zz_factor(f_6) == \ (1, [(47*x*y + z**3*t**2 - t**2, 1), (45*x**3 - 9*y**3 - y**2 + 3*z**3 + 2*z*t, 1)]) R, x, y, z = ring("x,y,z", ZZ) assert R.dmp_zz_factor(w_1) == \ (1, [(x**2*y**2 - x**2*z**2 + y - z**2, 1), (x**2*y*z**2 + 3*x*z + 2*y, 1), (4*x**2*y + 4*x**2*z + x*y*z - 1, 1)]) R, x, y = ring("x,y", ZZ) f = -12*x**16*y + 240*x**12*y**3 - 768*x**10*y**4 + 1080*x**8*y**5 - 768*x**6*y**6 + 240*x**4*y**7 - 12*y**9 assert R.dmp_zz_factor(f) == \ (-12, [(y, 1), (x**2 - y, 6), (x**4 + 6*x**2*y + y**2, 1)]) def test_dup_ext_factor(): R, x = ring("x", QQ.algebraic_field(I)) def anp(element): return ANP(element, [QQ(1), QQ(0), QQ(1)], QQ) assert R.dup_ext_factor(0) == (anp([]), []) f = anp([QQ(1)])*x + anp([QQ(1)]) assert R.dup_ext_factor(f) == (anp([QQ(1)]), [(f, 1)]) g = anp([QQ(2)])*x + anp([QQ(2)]) assert R.dup_ext_factor(g) == (anp([QQ(2)]), [(f, 1)]) f = anp([QQ(7)])*x**4 + anp([QQ(1, 1)]) g = anp([QQ(1)])*x**4 + anp([QQ(1, 7)]) assert R.dup_ext_factor(f) == (anp([QQ(7)]), [(g, 1)]) f = anp([QQ(1)])*x**4 + anp([QQ(1)]) assert R.dup_ext_factor(f) == \ (anp([QQ(1, 1)]), [(anp([QQ(1)])*x**2 + anp([QQ(-1), QQ(0)]), 1), (anp([QQ(1)])*x**2 + anp([QQ( 1), QQ(0)]), 1)]) f = anp([QQ(4, 1)])*x**2 + anp([QQ(9, 1)]) assert R.dup_ext_factor(f) == \ (anp([QQ(4, 1)]), [(anp([QQ(1, 1)])*x + anp([-QQ(3, 2), QQ(0, 1)]), 1), (anp([QQ(1, 1)])*x + anp([ QQ(3, 2), QQ(0, 1)]), 1)]) f = anp([QQ(4, 1)])*x**4 + anp([QQ(8, 1)])*x**3 + anp([QQ(77, 1)])*x**2 + anp([QQ(18, 1)])*x + anp([QQ(153, 1)]) assert R.dup_ext_factor(f) == \ (anp([QQ(4, 1)]), [(anp([QQ(1, 1)])*x + anp([-QQ(4, 1), QQ(1, 1)]), 1), (anp([QQ(1, 1)])*x + anp([-QQ(3, 2), QQ(0, 1)]), 1), (anp([QQ(1, 1)])*x + anp([ QQ(3, 2), QQ(0, 1)]), 1), (anp([QQ(1, 1)])*x + anp([ QQ(4, 1), QQ(1, 1)]), 1)]) R, x = ring("x", QQ.algebraic_field(sqrt(2))) def anp(element): return ANP(element, [QQ(1), QQ(0), QQ(-2)], QQ) f = anp([QQ(1)])*x**4 + anp([QQ(1, 1)]) assert R.dup_ext_factor(f) == \ (anp([QQ(1)]), [(anp([QQ(1)])*x**2 + anp([QQ(-1), QQ(0)])*x + anp([QQ(1)]), 1), (anp([QQ(1)])*x**2 + anp([QQ( 1), QQ(0)])*x + anp([QQ(1)]), 1)]) f = anp([QQ(1, 1)])*x**2 + anp([QQ(2), QQ(0)])*x + anp([QQ(2, 1)]) assert R.dup_ext_factor(f) == \ (anp([QQ(1, 1)]), [(anp([1])*x + anp([1, 0]), 2)]) assert R.dup_ext_factor(f**3) == \ (anp([QQ(1, 1)]), [(anp([1])*x + anp([1, 0]), 6)]) f *= anp([QQ(2, 1)]) assert R.dup_ext_factor(f) == \ (anp([QQ(2, 1)]), [(anp([1])*x + anp([1, 0]), 2)]) assert R.dup_ext_factor(f**3) == \ (anp([QQ(8, 1)]), [(anp([1])*x + anp([1, 0]), 6)]) def test_dmp_ext_factor(): R, x,y = ring("x,y", QQ.algebraic_field(sqrt(2))) def anp(x): return ANP(x, [QQ(1), QQ(0), QQ(-2)], QQ) assert R.dmp_ext_factor(0) == (anp([]), []) f = anp([QQ(1)])*x + anp([QQ(1)]) assert R.dmp_ext_factor(f) == (anp([QQ(1)]), [(f, 1)]) g = anp([QQ(2)])*x + anp([QQ(2)]) assert R.dmp_ext_factor(g) == (anp([QQ(2)]), [(f, 1)]) f = anp([QQ(1)])*x**2 + anp([QQ(-2)])*y**2 assert R.dmp_ext_factor(f) == \ (anp([QQ(1)]), [(anp([QQ(1)])*x + anp([QQ(-1), QQ(0)])*y, 1), (anp([QQ(1)])*x + anp([QQ( 1), QQ(0)])*y, 1)]) f = anp([QQ(2)])*x**2 + anp([QQ(-4)])*y**2 assert R.dmp_ext_factor(f) == \ (anp([QQ(2)]), [(anp([QQ(1)])*x + anp([QQ(-1), QQ(0)])*y, 1), (anp([QQ(1)])*x + anp([QQ( 1), QQ(0)])*y, 1)]) def test_dup_factor_list(): R, x = ring("x", ZZ) assert R.dup_factor_list(0) == (0, []) assert R.dup_factor_list(7) == (7, []) R, x = ring("x", QQ) assert R.dup_factor_list(0) == (0, []) assert R.dup_factor_list(QQ(1, 7)) == (QQ(1, 7), []) R, x = ring("x", ZZ['t']) assert R.dup_factor_list(0) == (0, []) assert R.dup_factor_list(7) == (7, []) R, x = ring("x", QQ['t']) assert R.dup_factor_list(0) == (0, []) assert R.dup_factor_list(QQ(1, 7)) == (QQ(1, 7), []) R, x = ring("x", ZZ) assert R.dup_factor_list_include(0) == [(0, 1)] assert R.dup_factor_list_include(7) == [(7, 1)] assert R.dup_factor_list(x**2 + 2*x + 1) == (1, [(x + 1, 2)]) assert R.dup_factor_list_include(x**2 + 2*x + 1) == [(x + 1, 2)] R, x = ring("x", QQ) assert R.dup_factor_list(QQ(1,2)*x**2 + x + QQ(1,2)) == (QQ(1, 2), [(x + 1, 2)]) R, x = ring("x", FF(2)) assert R.dup_factor_list(x**2 + 1) == (1, [(x + 1, 2)]) R, x = ring("x", RR) assert R.dup_factor_list(1.0*x**2 + 2.0*x + 1.0) == (1.0, [(1.0*x + 1.0, 2)]) assert R.dup_factor_list(2.0*x**2 + 4.0*x + 2.0) == (2.0, [(1.0*x + 1.0, 2)]) f = 6.7225336055071*x**2 - 10.6463972754741*x - 0.33469524022264 coeff, factors = R.dup_factor_list(f) assert coeff == RR(1.0) and len(factors) == 1 and factors[0][0].almosteq(f, 1e-10) and factors[0][1] == 1 Rt, t = ring("t", ZZ) R, x = ring("x", Rt) f = 4*t*x**2 + 4*t**2*x assert R.dup_factor_list(f) == \ (4*t, [(x, 1), (x + t, 1)]) Rt, t = ring("t", QQ) R, x = ring("x", Rt) f = QQ(1, 2)*t*x**2 + QQ(1, 2)*t**2*x assert R.dup_factor_list(f) == \ (QQ(1, 2)*t, [(x, 1), (x + t, 1)]) R, x = ring("x", QQ.algebraic_field(I)) def anp(element): return ANP(element, [QQ(1), QQ(0), QQ(1)], QQ) f = anp([QQ(1, 1)])*x**4 + anp([QQ(2, 1)])*x**2 assert R.dup_factor_list(f) == \ (anp([QQ(1, 1)]), [(anp([QQ(1, 1)])*x, 2), (anp([QQ(1, 1)])*x**2 + anp([])*x + anp([QQ(2, 1)]), 1)]) R, x = ring("x", EX) raises(DomainError, lambda: R.dup_factor_list(EX(sin(1)))) def test_dmp_factor_list(): R, x, y = ring("x,y", ZZ) assert R.dmp_factor_list(0) == (ZZ(0), []) assert R.dmp_factor_list(7) == (7, []) R, x, y = ring("x,y", QQ) assert R.dmp_factor_list(0) == (QQ(0), []) assert R.dmp_factor_list(QQ(1, 7)) == (QQ(1, 7), []) Rt, t = ring("t", ZZ) R, x, y = ring("x,y", Rt) assert R.dmp_factor_list(0) == (0, []) assert R.dmp_factor_list(7) == (ZZ(7), []) Rt, t = ring("t", QQ) R, x, y = ring("x,y", Rt) assert R.dmp_factor_list(0) == (0, []) assert R.dmp_factor_list(QQ(1, 7)) == (QQ(1, 7), []) R, x, y = ring("x,y", ZZ) assert R.dmp_factor_list_include(0) == [(0, 1)] assert R.dmp_factor_list_include(7) == [(7, 1)] R, X = xring("x:200", ZZ) f, g = X[0]**2 + 2*X[0] + 1, X[0] + 1 assert R.dmp_factor_list(f) == (1, [(g, 2)]) f, g = X[-1]**2 + 2*X[-1] + 1, X[-1] + 1 assert R.dmp_factor_list(f) == (1, [(g, 2)]) R, x = ring("x", ZZ) assert R.dmp_factor_list(x**2 + 2*x + 1) == (1, [(x + 1, 2)]) R, x = ring("x", QQ) assert R.dmp_factor_list(QQ(1,2)*x**2 + x + QQ(1,2)) == (QQ(1,2), [(x + 1, 2)]) R, x, y = ring("x,y", ZZ) assert R.dmp_factor_list(x**2 + 2*x + 1) == (1, [(x + 1, 2)]) R, x, y = ring("x,y", QQ) assert R.dmp_factor_list(QQ(1,2)*x**2 + x + QQ(1,2)) == (QQ(1,2), [(x + 1, 2)]) R, x, y = ring("x,y", ZZ) f = 4*x**2*y + 4*x*y**2 assert R.dmp_factor_list(f) == \ (4, [(y, 1), (x, 1), (x + y, 1)]) assert R.dmp_factor_list_include(f) == \ [(4*y, 1), (x, 1), (x + y, 1)] R, x, y = ring("x,y", QQ) f = QQ(1,2)*x**2*y + QQ(1,2)*x*y**2 assert R.dmp_factor_list(f) == \ (QQ(1,2), [(y, 1), (x, 1), (x + y, 1)]) R, x, y = ring("x,y", RR) f = 2.0*x**2 - 8.0*y**2 assert R.dmp_factor_list(f) == \ (RR(2.0), [(1.0*x - 2.0*y, 1), (1.0*x + 2.0*y, 1)]) f = 6.7225336055071*x**2*y**2 - 10.6463972754741*x*y - 0.33469524022264 coeff, factors = R.dmp_factor_list(f) assert coeff == RR(1.0) and len(factors) == 1 and factors[0][0].almosteq(f, 1e-10) and factors[0][1] == 1 Rt, t = ring("t", ZZ) R, x, y = ring("x,y", Rt) f = 4*t*x**2 + 4*t**2*x assert R.dmp_factor_list(f) == \ (4*t, [(x, 1), (x + t, 1)]) Rt, t = ring("t", QQ) R, x, y = ring("x,y", Rt) f = QQ(1, 2)*t*x**2 + QQ(1, 2)*t**2*x assert R.dmp_factor_list(f) == \ (QQ(1, 2)*t, [(x, 1), (x + t, 1)]) R, x, y = ring("x,y", FF(2)) raises(NotImplementedError, lambda: R.dmp_factor_list(x**2 + y**2)) R, x, y = ring("x,y", EX) raises(DomainError, lambda: R.dmp_factor_list(EX(sin(1)))) def test_dup_irreducible_p(): R, x = ring("x", ZZ) assert R.dup_irreducible_p(x**2 + x + 1) is True assert R.dup_irreducible_p(x**2 + 2*x + 1) is False def test_dmp_irreducible_p(): R, x, y = ring("x,y", ZZ) assert R.dmp_irreducible_p(x**2 + x + 1) is True assert R.dmp_irreducible_p(x**2 + 2*x + 1) is False sympy-0.7.4.1/sympy/polys/tests/test_modulargcd.py0000644000175000017500000002145712253362407022500 0ustar georgeskgeorgeskfrom sympy.polys.rings import ring from sympy.polys.domains import ZZ, QQ, AlgebraicField from sympy.polys.modulargcd import ( modgcd_univariate, modgcd_bivariate, _chinese_remainder_reconstruction_multivariate, modgcd_multivariate, _to_ZZ_poly, _to_ANP_poly, func_field_modgcd, _func_field_modgcd_m) from sympy import sqrt def test_modgcd_univariate_integers(): R, x = ring("x", ZZ) f, g = R.zero, R.zero assert modgcd_univariate(f, g) == (0, 0, 0) f, g = R.zero, x assert modgcd_univariate(f, g) == (x, 0, 1) assert modgcd_univariate(g, f) == (x, 1, 0) f, g = R.zero, -x assert modgcd_univariate(f, g) == (x, 0, -1) assert modgcd_univariate(g, f) == (x, -1, 0) f, g = 2*x, R(2) assert modgcd_univariate(f, g) == (2, x, 1) f, g = 2*x + 2, 6*x**2 - 6 assert modgcd_univariate(f, g) == (2*x + 2, 1, 3*x - 3) f = x**4 + 8*x**3 + 21*x**2 + 22*x + 8 g = x**3 + 6*x**2 + 11*x + 6 h = x**2 + 3*x + 2 cff = x**2 + 5*x + 4 cfg = x + 3 assert modgcd_univariate(f, g) == (h, cff, cfg) f = x**4 - 4 g = x**4 + 4*x**2 + 4 h = x**2 + 2 cff = x**2 - 2 cfg = x**2 + 2 assert modgcd_univariate(f, g) == (h, cff, cfg) f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 h = 1 cff = f cfg = g assert modgcd_univariate(f, g) == (h, cff, cfg) f = - 352518131239247345597970242177235495263669787845475025293906825864749649589178600387510272*x**49 \ + 46818041807522713962450042363465092040687472354933295397472942006618953623327997952*x**42 \ + 378182690892293941192071663536490788434899030680411695933646320291525827756032*x**35 \ + 112806468807371824947796775491032386836656074179286744191026149539708928*x**28 \ - 12278371209708240950316872681744825481125965781519138077173235712*x**21 \ + 289127344604779611146960547954288113529690984687482920704*x**14 \ + 19007977035740498977629742919480623972236450681*x**7 \ + 311973482284542371301330321821976049 g = 365431878023781158602430064717380211405897160759702125019136*x**21 \ + 197599133478719444145775798221171663643171734081650688*x**14 \ - 9504116979659010018253915765478924103928886144*x**7 \ - 311973482284542371301330321821976049 assert modgcd_univariate(f, f.diff(x))[0] == g f = 1317378933230047068160*x + 2945748836994210856960 g = 120352542776360960*x + 269116466014453760 h = 120352542776360960*x + 269116466014453760 cff = 10946 cfg = 1 assert modgcd_univariate(f, g) == (h, cff, cfg) def test_modgcd_bivariate_integers(): R, x, y = ring("x,y", ZZ) f, g = R.zero, R.zero assert modgcd_bivariate(f, g) == (0, 0, 0) f, g = 2*x, R(2) assert modgcd_bivariate(f, g) == (2, x, 1) f, g = x + 2*y, x + y assert modgcd_bivariate(f, g) == (1, f, g) f, g = x**2 + 2*x*y + y**2, x**3 + y**3 assert modgcd_bivariate(f, g) == (x + y, x + y, x**2 - x*y + y**2) f, g = x*y**2 + 2*x*y + x, x*y**3 + x assert modgcd_bivariate(f, g) == (x*y + x, y + 1, y**2 - y + 1) f, g = x**2*y**2 + x**2*y + 1, x*y**2 + x*y + 1 assert modgcd_bivariate(f, g) == (1, f, g) f = 2*x*y**2 + 4*x*y + 2*x + y**2 + 2*y + 1 g = 2*x*y**3 + 2*x + y**3 + 1 assert modgcd_bivariate(f, g) == (2*x*y + 2*x + y + 1, y + 1, y**2 - y + 1) f, g = 2*x**2 + 4*x + 2, x + 1 assert modgcd_bivariate(f, g) == (x + 1, 2*x + 2, 1) f, g = x + 1, 2*x**2 + 4*x + 2 assert modgcd_bivariate(f, g) == (x + 1, 1, 2*x + 2) f = 2*x**2 + 4*x*y - 2*x - 4*y g = x**2 + x - 2 assert modgcd_bivariate(f, g) == (x - 1, 2*x + 4*y, x + 2) f = 2*x**2 + 2*x*y - 3*x - 3*y g = 4*x*y - 2*x + 4*y**2 - 2*y assert modgcd_bivariate(f, g) == (x + y, 2*x - 3, 4*y - 2) def test_chinese_remainder(): R, x, y = ring("x, y", ZZ) p, q = 3, 5 hp = x**3*y - x**2 - 1 hq = -x**3*y - 2*x*y**2 + 2 hpq = _chinese_remainder_reconstruction_multivariate(hp, hq, p, q) assert hpq.trunc_ground(p) == hp assert hpq.trunc_ground(q) == hq T, z = ring("z", R) p, q = 3, 7 hp = (x*y + 1)*z**2 + x hq = (x**2 - 3*y)*z + 2 hpq = _chinese_remainder_reconstruction_multivariate(hp, hq, p, q) assert hpq.trunc_ground(p) == hp assert hpq.trunc_ground(q) == hq def test_modgcd_multivariate_integers(): R, x, y = ring("x,y", ZZ) f, g = R.zero, R.zero assert modgcd_multivariate(f, g) == (0, 0, 0) f, g = 2*x**2 + 4*x + 2, x + 1 assert modgcd_multivariate(f, g) == (x + 1, 2*x + 2, 1) f, g = x + 1, 2*x**2 + 4*x + 2 assert modgcd_multivariate(f, g) == (x + 1, 1, 2*x + 2) f = 2*x**2 + 2*x*y - 3*x - 3*y g = 4*x*y - 2*x + 4*y**2 - 2*y assert modgcd_multivariate(f, g) == (x + y, 2*x - 3, 4*y - 2) f, g = x*y**2 + 2*x*y + x, x*y**3 + x assert modgcd_multivariate(f, g) == (x*y + x, y + 1, y**2 - y + 1) f, g = x**2*y**2 + x**2*y + 1, x*y**2 + x*y + 1 assert modgcd_multivariate(f, g) == (1, f, g) f = x**4 + 8*x**3 + 21*x**2 + 22*x + 8 g = x**3 + 6*x**2 + 11*x + 6 h = x**2 + 3*x + 2 cff = x**2 + 5*x + 4 cfg = x + 3 assert modgcd_multivariate(f, g) == (h, cff, cfg) R, x, y, z, u = ring("x,y,z,u", ZZ) f, g = x + y + z, -x - y - z - u assert modgcd_multivariate(f, g) == (1, f, g) f, g = u**2 + 2*u + 1, 2*u + 2 assert modgcd_multivariate(f, g) == (u + 1, u + 1, 2) f, g = z**2*u**2 + 2*z**2*u + z**2 + z*u + z, u**2 + 2*u + 1 h, cff, cfg = u + 1, z**2*u + z**2 + z, u + 1 assert modgcd_multivariate(f, g) == (h, cff, cfg) assert modgcd_multivariate(g, f) == (h, cfg, cff) R, x, y, z = ring("x,y,z", ZZ) f, g = x - y*z, x - y*z assert modgcd_multivariate(f, g) == (x - y*z, 1, 1) f, g, h = R.fateman_poly_F_1() H, cff, cfg = modgcd_multivariate(f, g) assert H == h and H*cff == f and H*cfg == g R, x, y, z, u, v = ring("x,y,z,u,v", ZZ) f, g, h = R.fateman_poly_F_1() H, cff, cfg = modgcd_multivariate(f, g) assert H == h and H*cff == f and H*cfg == g R, x, y, z, u, v, a, b = ring("x,y,z,u,v,a,b", ZZ) f, g, h = R.fateman_poly_F_1() H, cff, cfg = modgcd_multivariate(f, g) assert H == h and H*cff == f and H*cfg == g R, x, y, z, u, v, a, b, c, d = ring("x,y,z,u,v,a,b,c,d", ZZ) f, g, h = R.fateman_poly_F_1() H, cff, cfg = modgcd_multivariate(f, g) assert H == h and H*cff == f and H*cfg == g R, x, y, z = ring("x,y,z", ZZ) f, g, h = R.fateman_poly_F_2() H, cff, cfg = modgcd_multivariate(f, g) assert H == h and H*cff == f and H*cfg == g f, g, h = R.fateman_poly_F_3() H, cff, cfg = modgcd_multivariate(f, g) assert H == h and H*cff == f and H*cfg == g R, x, y, z, t = ring("x,y,z,t", ZZ) f, g, h = R.fateman_poly_F_3() H, cff, cfg = modgcd_multivariate(f, g) assert H == h and H*cff == f and H*cfg == g def test_to_ZZ_ANP_poly(): A = AlgebraicField(QQ, sqrt(2)) R, x = ring("x", A) f = x*(sqrt(2) + 1) T, x_, z_ = ring("x_, z_", ZZ) f_ = x_*z_ + x_ assert _to_ZZ_poly(f, T) == f_ assert _to_ANP_poly(f_, R) == f R, x, t, s = ring("x, t, s", A) f = x*t**2 + x*s + sqrt(2) D, t_, s_ = ring("t_, s_", ZZ) T, x_, z_ = ring("x_, z_", D) f_ = (t_**2 + s_)*x_ + z_ assert _to_ZZ_poly(f, T) == f_ assert _to_ANP_poly(f_, R) == f def test_modgcd_algebraic_field(): A = AlgebraicField(QQ, sqrt(2)) R, x = ring("x", A) one = A.one f, g = 2*x, R(2) assert func_field_modgcd(f, g) == (one, f, g) f, g = 2*x, R(sqrt(2)) assert func_field_modgcd(f, g) == (one, f, g) f, g = 2*x + 2, 6*x**2 - 6 assert func_field_modgcd(f, g) == (x + 1, R(2), 6*x - 6) R, x, y = ring("x, y", A) f, g = x + sqrt(2)*y, x + y assert func_field_modgcd(f, g) == (one, f, g) f, g = x*y + sqrt(2)*y**2, R(sqrt(2))*y assert func_field_modgcd(f, g) == (y, x + sqrt(2)*y, R(sqrt(2))) f, g = x**2 + 2*sqrt(2)*x*y + 2*y**2, x + sqrt(2)*y assert func_field_modgcd(f, g) == (g, g, one) A = AlgebraicField(QQ, sqrt(2), sqrt(3)) R, x, y, z = ring("x, y, z", A) h = x**2*y**7 + sqrt(6)/21*z f, g = h*(27*y**3 + 1), h*(y + x) assert func_field_modgcd(f, g) == (h, 27*y**3+1, y+x) h = x**13*y**3 + 1/2*x**10 + 1/sqrt(2) f, g = h*(x + 1), h*sqrt(2)/sqrt(3) assert func_field_modgcd(f, g) == (h, x + 1, R(sqrt(2)/sqrt(3))) A = AlgebraicField(QQ, sqrt(2)**(-1)*sqrt(3)) R, x = ring("x", A) f, g = x + 1, x - 1 assert func_field_modgcd(f, g) == (A.one, f, g) # when func_field_modgcd suppors function fields, this test can be changed def test_modgcd_func_field(): D, t = ring("t", ZZ) R, x, z = ring("x, z", D) minpoly = (z**2*t**2 + z**2*t - 1).drop(0) f, g = x + 1, x - 1 assert _func_field_modgcd_m(f, g, minpoly) == R.one sympy-0.7.4.1/sympy/polys/tests/test_groebnertools.py0000644000175000017500000004342312253362407023240 0ustar georgeskgeorgesk"""Tests for Groebner bases. """ from sympy.polys.groebnertools import ( groebner, sig, sig_key, sig_cmp, lbp, lbp_cmp, lbp_key, critical_pair, cp_cmp, cp_key, is_rewritable_or_comparable, Sign, Polyn, Num, s_poly, f5_reduce, groebner_lcm, groebner_gcd, ) from sympy.polys.fglmtools import _representing_matrices from sympy.polys.orderings import lex, grlex, grevlex from sympy.polys.polyerrors import ExactQuotientFailed, DomainError from sympy.polys.rings import ring, xring from sympy.polys.domains import ZZ, QQ from sympy.utilities.pytest import raises, slow from sympy.polys import polyconfig as config def _do_test_groebner(): R, x,y = ring("x,y", QQ, lex) f = x**2 + 2*x*y**2 g = x*y + 2*y**3 - 1 assert groebner([f, g], R) == [x, y**3 - QQ(1,2)] R, y,x = ring("y,x", QQ, lex) f = 2*x**2*y + y**2 g = 2*x**3 + x*y - 1 assert groebner([f, g], R) == [y, x**3 - QQ(1,2)] R, x,y,z = ring("x,y,z", QQ, lex) f = x - z**2 g = y - z**3 assert groebner([f, g], R) == [f, g] R, x,y = ring("x,y", QQ, grlex) f = x**3 - 2*x*y g = x**2*y + x - 2*y**2 assert groebner([f, g], R) == [x**2, x*y, -QQ(1,2)*x + y**2] R, x,y,z = ring("x,y,z", QQ, lex) f = -x**2 + y g = -x**3 + z assert groebner([f, g], R) == [x**2 - y, x*y - z, x*z - y**2, y**3 - z**2] R, x,y,z = ring("x,y,z", QQ, grlex) f = -x**2 + y g = -x**3 + z assert groebner([f, g], R) == [y**3 - z**2, x**2 - y, x*y - z, x*z - y**2] R, x,y,z = ring("x,y,z", QQ, lex) f = -x**2 + z g = -x**3 + y assert groebner([f, g], R) == [x**2 - z, x*y - z**2, x*z - y, y**2 - z**3] R, x,y,z = ring("x,y,z", QQ, grlex) f = -x**2 + z g = -x**3 + y assert groebner([f, g], R) == [-y**2 + z**3, x**2 - z, x*y - z**2, x*z - y] R, x,y,z = ring("x,y,z", QQ, lex) f = x - y**2 g = -y**3 + z assert groebner([f, g], R) == [x - y**2, y**3 - z] R, x,y,z = ring("x,y,z", QQ, grlex) f = x - y**2 g = -y**3 + z assert groebner([f, g], R) == [x**2 - y*z, x*y - z, -x + y**2] R, x,y,z = ring("x,y,z", QQ, lex) f = x - z**2 g = y - z**3 assert groebner([f, g], R) == [x - z**2, y - z**3] R, x,y,z = ring("x,y,z", QQ, grlex) f = x - z**2 g = y - z**3 assert groebner([f, g], R) == [x**2 - y*z, x*z - y, -x + z**2] R, x,y,z = ring("x,y,z", QQ, lex) f = -y**2 + z g = x - y**3 assert groebner([f, g], R) == [x - y*z, y**2 - z] R, x,y,z = ring("x,y,z", QQ, grlex) f = -y**2 + z g = x - y**3 assert groebner([f, g], R) == [-x**2 + z**3, x*y - z**2, y**2 - z, -x + y*z] R, x,y,z = ring("x,y,z", QQ, lex) f = y - z**2 g = x - z**3 assert groebner([f, g], R) == [x - z**3, y - z**2] R, x,y,z = ring("x,y,z", QQ, grlex) f = y - z**2 g = x - z**3 assert groebner([f, g], R) == [-x**2 + y**3, x*z - y**2, -x + y*z, -y + z**2] R, x,y,z = ring("x,y,z", QQ, lex) f = 4*x**2*y**2 + 4*x*y + 1 g = x**2 + y**2 - 1 assert groebner([f, g], R) == [ x - 4*y**7 + 8*y**5 - 7*y**3 + 3*y, y**8 - 2*y**6 + QQ(3,2)*y**4 - QQ(1,2)*y**2 + QQ(1,16), ] def test_groebner_buchberger(): with config.using(groebner='buchberger'): _do_test_groebner() def test_groebner_f5b(): with config.using(groebner='f5b'): _do_test_groebner() def _do_test_benchmark_minpoly(): R, x,y,z = ring("x,y,z", QQ, lex) F = [x**3 + x + 1, y**2 + y + 1, (x + y) * z - (x**2 + y)] G = [x + QQ(155,2067)*z**5 - QQ(355,689)*z**4 + QQ(6062,2067)*z**3 - QQ(3687,689)*z**2 + QQ(6878,2067)*z - QQ(25,53), y + QQ(4,53)*z**5 - QQ(91,159)*z**4 + QQ(523,159)*z**3 - QQ(387,53)*z**2 + QQ(1043,159)*z - QQ(308,159), z**6 - 7*z**5 + 41*z**4 - 82*z**3 + 89*z**2 - 46*z + 13] assert groebner(F, R) == G def test_benchmark_minpoly_buchberger(): with config.using(groebner='buchberger'): _do_test_benchmark_minpoly() def test_benchmark_minpoly_f5b(): with config.using(groebner='f5b'): _do_test_benchmark_minpoly() @slow def test_benchmark_coloring(): V = range(1, 12 + 1) E = [(1, 2), (2, 3), (1, 4), (1, 6), (1, 12), (2, 5), (2, 7), (3, 8), (3, 10), (4, 11), (4, 9), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), (11, 12), (5, 12), (5, 9), (6, 10), (7, 11), (8, 12), (3, 4)] R, V = xring([ "x%d" % v for v in V ], QQ, lex) E = [(V[i - 1], V[j - 1]) for i, j in E] x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12 = V I3 = [x**3 - 1 for x in V] Ig = [x**2 + x*y + y**2 for x, y in E] I = I3 + Ig assert groebner(I[:-1], R) == [ x1 + x11 + x12, x2 - x11, x3 - x12, x4 - x12, x5 + x11 + x12, x6 - x11, x7 - x12, x8 + x11 + x12, x9 - x11, x10 + x11 + x12, x11**2 + x11*x12 + x12**2, x12**3 - 1, ] assert groebner(I, R) == [1] def _do_test_benchmark_katsura_3(): R, x0,x1,x2 = ring("x:3", ZZ, lex) I = [x0 + 2*x1 + 2*x2 - 1, x0**2 + 2*x1**2 + 2*x2**2 - x0, 2*x0*x1 + 2*x1*x2 - x1] assert groebner(I, R) == [ -7 + 7*x0 + 8*x2 + 158*x2**2 - 420*x2**3, 7*x1 + 3*x2 - 79*x2**2 + 210*x2**3, x2 + x2**2 - 40*x2**3 + 84*x2**4, ] R, x0,x1,x2 = ring("x:3", ZZ, grlex) I = [ i.set_ring(R) for i in I ] assert groebner(I, R) == [ 7*x1 + 3*x2 - 79*x2**2 + 210*x2**3, -x1 + x2 - 3*x2**2 + 5*x1**2, -x1 - 4*x2 + 10*x1*x2 + 12*x2**2, -1 + x0 + 2*x1 + 2*x2, ] def test_benchmark_katsura3_buchberger(): with config.using(groebner='buchberger'): _do_test_benchmark_katsura_3() def test_benchmark_katsura3_f5b(): with config.using(groebner='f5b'): _do_test_benchmark_katsura_3() def _do_test_benchmark_katsura_4(): R, x0,x1,x2,x3 = ring("x:4", ZZ, lex) I = [x0 + 2*x1 + 2*x2 + 2*x3 - 1, x0**2 + 2*x1**2 + 2*x2**2 + 2*x3**2 - x0, 2*x0*x1 + 2*x1*x2 + 2*x2*x3 - x1, x1**2 + 2*x0*x2 + 2*x1*x3 - x2] assert groebner(I, R) == [ 5913075*x0 - 159690237696*x3**7 + 31246269696*x3**6 + 27439610544*x3**5 - 6475723368*x3**4 - 838935856*x3**3 + 275119624*x3**2 + 4884038*x3 - 5913075, 1971025*x1 - 97197721632*x3**7 + 73975630752*x3**6 - 12121915032*x3**5 - 2760941496*x3**4 + 814792828*x3**3 - 1678512*x3**2 - 9158924*x3, 5913075*x2 + 371438283744*x3**7 - 237550027104*x3**6 + 22645939824*x3**5 + 11520686172*x3**4 - 2024910556*x3**3 - 132524276*x3**2 + 30947828*x3, 128304*x3**8 - 93312*x3**7 + 15552*x3**6 + 3144*x3**5 - 1120*x3**4 + 36*x3**3 + 15*x3**2 - x3, ] R, x0,x1,x2,x3 = ring("x:4", ZZ, grlex) I = [ i.set_ring(R) for i in I ] assert groebner(I, R) == [ 393*x1 - 4662*x2**2 + 4462*x2*x3 - 59*x2 + 224532*x3**4 - 91224*x3**3 - 678*x3**2 + 2046*x3, -x1 + 196*x2**3 - 21*x2**2 + 60*x2*x3 - 18*x2 - 168*x3**3 + 83*x3**2 - 9*x3, -6*x1 + 1134*x2**2*x3 - 189*x2**2 - 466*x2*x3 + 32*x2 - 630*x3**3 + 57*x3**2 + 51*x3, 33*x1 + 63*x2**2 + 2268*x2*x3**2 - 188*x2*x3 + 34*x2 + 2520*x3**3 - 849*x3**2 + 3*x3, 7*x1**2 - x1 - 7*x2**2 - 24*x2*x3 + 3*x2 - 15*x3**2 + 5*x3, 14*x1*x2 - x1 + 14*x2**2 + 18*x2*x3 - 4*x2 + 6*x3**2 - 2*x3, 14*x1*x3 - x1 + 7*x2**2 + 32*x2*x3 - 4*x2 + 27*x3**2 - 9*x3, x0 + 2*x1 + 2*x2 + 2*x3 - 1, ] def test_benchmark_kastura_4_buchberger(): with config.using(groebner='buchberger'): _do_test_benchmark_katsura_4() def test_benchmark_kastura_4_f5b(): with config.using(groebner='f5b'): _do_test_benchmark_katsura_4() def _do_test_benchmark_czichowski(): R, x,t = ring("x,t", ZZ, lex) I = [9*x**8 + 36*x**7 - 32*x**6 - 252*x**5 - 78*x**4 + 468*x**3 + 288*x**2 - 108*x + 9, (-72 - 72*t)*x**7 + (-256 - 252*t)*x**6 + (192 + 192*t)*x**5 + (1280 + 1260*t)*x**4 + (312 + 312*t)*x**3 + (-404*t)*x**2 + (-576 - 576*t)*x + 96 + 108*t] assert groebner(I, R) == [ 3725588592068034903797967297424801242396746870413359539263038139343329273586196480000*x - 160420835591776763325581422211936558925462474417709511019228211783493866564923546661604487873*t**7 - 1406108495478033395547109582678806497509499966197028487131115097902188374051595011248311352864*t**6 - 5241326875850889518164640374668786338033653548841427557880599579174438246266263602956254030352*t**5 - 10758917262823299139373269714910672770004760114329943852726887632013485035262879510837043892416*t**4 - 13119383576444715672578819534846747735372132018341964647712009275306635391456880068261130581248*t**3 - 9491412317016197146080450036267011389660653495578680036574753839055748080962214787557853941760*t**2 - 3767520915562795326943800040277726397326609797172964377014046018280260848046603967211258368000*t - 632314652371226552085897259159210286886724229880266931574701654721512325555116066073245696000, 610733380717522355121*t**8 + 6243748742141230639968*t**7 + 27761407182086143225024*t**6 + 70066148869420956398592*t**5 + 109701225644313784229376*t**4 + 109009005495588442152960*t**3 + 67072101084384786432000*t**2 + 23339979742629593088000*t + 3513592776846090240000, ] R, x,t = ring("x,t", ZZ, grlex) I = [ i.set_ring(R) for i in I ] assert groebner(I, R) == [ 16996618586000601590732959134095643086442*t**3*x - 32936701459297092865176560282688198064839*t**3 + 78592411049800639484139414821529525782364*t**2*x - 120753953358671750165454009478961405619916*t**2 + 120988399875140799712152158915653654637280*t*x - 144576390266626470824138354942076045758736*t + 60017634054270480831259316163620768960*x**2 + 61976058033571109604821862786675242894400*x - 56266268491293858791834120380427754600960, 576689018321912327136790519059646508441672750656050290242749*t**4 + 2326673103677477425562248201573604572527893938459296513327336*t**3 + 110743790416688497407826310048520299245819959064297990236000*t**2*x + 3308669114229100853338245486174247752683277925010505284338016*t**2 + 323150205645687941261103426627818874426097912639158572428800*t*x + 1914335199925152083917206349978534224695445819017286960055680*t + 861662882561803377986838989464278045397192862768588480000*x**2 + 235296483281783440197069672204341465480107019878814196672000*x + 361850798943225141738895123621685122544503614946436727532800, -117584925286448670474763406733005510014188341867*t**3 + 68566565876066068463853874568722190223721653044*t**2*x - 435970731348366266878180788833437896139920683940*t**2 + 196297602447033751918195568051376792491869233408*t*x - 525011527660010557871349062870980202067479780112*t + 517905853447200553360289634770487684447317120*x**3 + 569119014870778921949288951688799397569321920*x**2 + 138877356748142786670127389526667463202210102080*x - 205109210539096046121625447192779783475018619520, -3725142681462373002731339445216700112264527*t**3 + 583711207282060457652784180668273817487940*t**2*x - 12381382393074485225164741437227437062814908*t**2 + 151081054097783125250959636747516827435040*t*x**2 + 1814103857455163948531448580501928933873280*t*x - 13353115629395094645843682074271212731433648*t + 236415091385250007660606958022544983766080*x**2 + 1390443278862804663728298060085399578417600*x - 4716885828494075789338754454248931750698880, ] # NOTE: This is very slow (> 2 minutes on 3.4 GHz) without GMPY @slow def test_benchmark_czichowski_buchberger(): with config.using(groebner='buchberger'): _do_test_benchmark_czichowski() @slow def test_benchmark_czichowski_f5b(): with config.using(groebner='f5b'): _do_test_benchmark_czichowski() def _do_test_benchmark_cyclic_4(): R, a,b,c,d = ring("a,b,c,d", ZZ, lex) I = [a + b + c + d, a*b + a*d + b*c + b*d, a*b*c + a*b*d + a*c*d + b*c*d, a*b*c*d - 1] assert groebner(I, R) == [ 4*a + 3*d**9 - 4*d**5 - 3*d, 4*b + 4*c - 3*d**9 + 4*d**5 + 7*d, 4*c**2 + 3*d**10 - 4*d**6 - 3*d**2, 4*c*d**4 + 4*c - d**9 + 4*d**5 + 5*d, d**12 - d**8 - d**4 + 1 ] R, a,b,c,d = ring("a,b,c,d", ZZ, grlex) I = [ i.set_ring(R) for i in I ] assert groebner(I, R) == [ 3*b*c - c**2 + d**6 - 3*d**2, -b + 3*c**2*d**3 - c - d**5 - 4*d, -b + 3*c*d**4 + 2*c + 2*d**5 + 2*d, c**4 + 2*c**2*d**2 - d**4 - 2, c**3*d + c*d**3 + d**4 + 1, b*c**2 - c**3 - c**2*d - 2*c*d**2 - d**3, b**2 - c**2, b*d + c**2 + c*d + d**2, a + b + c + d ] def test_benchmark_cyclic_4_buchberger(): with config.using(groebner='buchberger'): _do_test_benchmark_cyclic_4() def test_benchmark_cyclic_4_f5b(): with config.using(groebner='f5b'): _do_test_benchmark_cyclic_4() def test_sig_key(): s1 = sig((0,) * 3, 2) s2 = sig((1,) * 3, 4) s3 = sig((2,) * 3, 2) assert sig_key(s1, lex) > sig_key(s2, lex) assert sig_key(s2, lex) < sig_key(s3, lex) def test_lbp_key(): R, x,y,z,t = ring("x,y,z,t", ZZ, lex) p1 = lbp(sig((0,) * 4, 3), R.zero, 12) p2 = lbp(sig((0,) * 4, 4), R.zero, 13) p3 = lbp(sig((0,) * 4, 4), R.zero, 12) assert lbp_key(p1) > lbp_key(p2) assert lbp_key(p2) < lbp_key(p3) def test_critical_pair(): # from cyclic4 with grlex R, x,y,z,t = ring("x,y,z,t", QQ, grlex) p1 = (((0, 0, 0, 0), 4), y*z*t**2 + z**2*t**2 - t**4 - 1, 4) q1 = (((0, 0, 0, 0), 2), -y**2 - y*t - z*t - t**2, 2) p2 = (((0, 0, 0, 2), 3), z**3*t**2 + z**2*t**3 - z - t, 5) q2 = (((0, 0, 2, 2), 2), y*z + z*t**5 + z*t + t**6, 13) assert critical_pair(p1, q1, R) == ( ((0, 0, 1, 2), 2), ((0, 0, 1, 2), QQ(-1, 1)), (((0, 0, 0, 0), 2), -y**2 - y*t - z*t - t**2, 2), ((0, 1, 0, 0), 4), ((0, 1, 0, 0), QQ(1, 1)), (((0, 0, 0, 0), 4), y*z*t**2 + z**2*t**2 - t**4 - 1, 4) ) assert critical_pair(p2, q2, R) == ( ((0, 0, 4, 2), 2), ((0, 0, 2, 0), QQ(1, 1)), (((0, 0, 2, 2), 2), y*z + z*t**5 + z*t + t**6, 13), ((0, 0, 0, 5), 3), ((0, 0, 0, 3), QQ(1, 1)), (((0, 0, 0, 2), 3), z**3*t**2 + z**2*t**3 - z - t, 5) ) def test_cp_key(): # from cyclic4 with grlex R, x,y,z,t = ring("x,y,z,t", QQ, grlex) p1 = (((0, 0, 0, 0), 4), y*z*t**2 + z**2*t**2 - t**4 - 1, 4) q1 = (((0, 0, 0, 0), 2), -y**2 - y*t - z*t - t**2, 2) p2 = (((0, 0, 0, 2), 3), z**3*t**2 + z**2*t**3 - z - t, 5) q2 = (((0, 0, 2, 2), 2), y*z + z*t**5 + z*t + t**6, 13) cp1 = critical_pair(p1, q1, R) cp2 = critical_pair(p2, q2, R) assert cp_key(cp1, R) < cp_key(cp2, R) cp1 = critical_pair(p1, p2, R) cp2 = critical_pair(q1, q2, R) assert cp_key(cp1, R) < cp_key(cp2, R) def test_is_rewritable_or_comparable(): # from katsura4 with grlex R, x,y,z,t = ring("x,y,z,t", QQ, grlex) p = lbp(sig((0, 0, 2, 1), 2), R.zero, 2) B = [lbp(sig((0, 0, 0, 1), 2), QQ(2,45)*y**2 + QQ(1,5)*y*z + QQ(5,63)*y*t + z**2*t + QQ(4,45)*z**2 + QQ(76,35)*z*t**2 - QQ(32,105)*z*t + QQ(13,7)*t**3 - QQ(13,21)*t**2, 6)] # rewritable: assert is_rewritable_or_comparable(Sign(p), Num(p), B) is True p = lbp(sig((0, 1, 1, 0), 2), R.zero, 7) B = [lbp(sig((0, 0, 0, 0), 3), QQ(10,3)*y*z + QQ(4,3)*y*t - QQ(1,3)*y + 4*z**2 + QQ(22,3)*z*t - QQ(4,3)*z + 4*t**2 - QQ(4,3)*t, 3)] # comparable: assert is_rewritable_or_comparable(Sign(p), Num(p), B) is True def test_f5_reduce(): # katsura3 with lex R, x,y,z = ring("x,y,z", QQ, lex) F = [(((0, 0, 0), 1), x + 2*y + 2*z - 1, 1), (((0, 0, 0), 2), 6*y**2 + 8*y*z - 2*y + 6*z**2 - 2*z, 2), (((0, 0, 0), 3), QQ(10,3)*y*z - QQ(1,3)*y + 4*z**2 - QQ(4,3)*z, 3), (((0, 0, 1), 2), y + 30*z**3 - QQ(79,7)*z**2 + QQ(3,7)*z, 4), (((0, 0, 2), 2), z**4 - QQ(10,21)*z**3 + QQ(1,84)*z**2 + QQ(1,84)*z, 5)] cp = critical_pair(F[0], F[1], R) s = s_poly(cp) assert f5_reduce(s, F) == (((0, 2, 0), 1), R.zero, 1) s = lbp(sig(Sign(s)[0], 100), Polyn(s), Num(s)) assert f5_reduce(s, F) == s def test_representing_matrices(): R, x,y = ring("x,y", QQ, grlex) basis = [(0, 0), (0, 1), (1, 0), (1, 1)] F = [x**2 - x - 3*y + 1, -2*x + y**2 + y - 1] assert _representing_matrices(basis, F, R) == [ [[QQ(0, 1), QQ(0, 1),-QQ(1, 1), QQ(3, 1)], [QQ(0, 1), QQ(0, 1), QQ(3, 1),-QQ(4, 1)], [QQ(1, 1), QQ(0, 1), QQ(1, 1), QQ(6, 1)], [QQ(0, 1), QQ(1, 1), QQ(0, 1), QQ(1, 1)]], [[QQ(0, 1), QQ(1, 1), QQ(0, 1),-QQ(2, 1)], [QQ(1, 1),-QQ(1, 1), QQ(0, 1), QQ(6, 1)], [QQ(0, 1), QQ(2, 1), QQ(0, 1), QQ(3, 1)], [QQ(0, 1), QQ(0, 1), QQ(1, 1),-QQ(1, 1)]]] def test_groebner_lcm(): R, x,y,z = ring("x,y,z", ZZ) assert groebner_lcm(x**2 - y**2, x - y) == x**2 - y**2 assert groebner_lcm(2*x**2 - 2*y**2, 2*x - 2*y) == 2*x**2 - 2*y**2 R, x,y,z = ring("x,y,z", QQ) assert groebner_lcm(x**2 - y**2, x - y) == x**2 - y**2 assert groebner_lcm(2*x**2 - 2*y**2, 2*x - 2*y) == 2*x**2 - 2*y**2 R, x,y = ring("x,y", ZZ) assert groebner_lcm(x**2*y, x*y**2) == x**2*y**2 f = 2*x*y**5 - 3*x*y**4 - 2*x*y**3 + 3*x*y**2 g = y**5 - 2*y**3 + y h = 2*x*y**7 - 3*x*y**6 - 4*x*y**5 + 6*x*y**4 + 2*x*y**3 - 3*x*y**2 assert groebner_lcm(f, g) == h f = x**3 - 3*x**2*y - 9*x*y**2 - 5*y**3 g = x**4 + 6*x**3*y + 12*x**2*y**2 + 10*x*y**3 + 3*y**4 h = x**5 + x**4*y - 18*x**3*y**2 - 50*x**2*y**3 - 47*x*y**4 - 15*y**5 assert groebner_lcm(f, g) == h def test_groebner_gcd(): R, x,y,z = ring("x,y,z", ZZ) assert groebner_gcd(x**2 - y**2, x - y) == x - y assert groebner_gcd(2*x**2 - 2*y**2, 2*x - 2*y) == 2*x - 2*y R, x,y,z = ring("x,y,z", QQ) assert groebner_gcd(x**2 - y**2, x - y) == x - y assert groebner_gcd(2*x**2 - 2*y**2, 2*x - 2*y) == x - y sympy-0.7.4.1/sympy/polys/tests/test_rationaltools.py0000644000175000017500000000412712253362407023244 0ustar georgeskgeorgesk"""Tests for tools for manipulation of rational expressions. """ from sympy.polys.rationaltools import together from sympy import S, symbols, Rational, sin, exp, Eq, Integral, Mul from sympy.abc import x, y, z A, B = symbols('A,B', commutative=False) def test_together(): assert together(0) == 0 assert together(1) == 1 assert together(x*y*z) == x*y*z assert together(x + y) == x + y assert together(1/x) == 1/x assert together(1/x + 1) == (x + 1)/x assert together(1/x + 3) == (3*x + 1)/x assert together(1/x + x) == (x**2 + 1)/x assert together(1/x + Rational(1, 2)) == (x + 2)/(2*x) assert together(Rational(1, 2) + x/2) == Mul(S.Half, x + 1, evaluate=False) assert together(1/x + 2/y) == (2*x + y)/(y*x) assert together(1/(1 + 1/x)) == x/(1 + x) assert together(x/(1 + 1/x)) == x**2/(1 + x) assert together(1/x + 1/y + 1/z) == (x*y + x*z + y*z)/(x*y*z) assert together(1/(1 + x + 1/y + 1/z)) == y*z/(y + z + y*z + x*y*z) assert together(1/(x*y) + 1/(x*y)**2) == y**(-2)*x**(-2)*(1 + x*y) assert together(1/(x*y) + 1/(x*y)**4) == y**(-4)*x**(-4)*(1 + x**3*y**3) assert together(1/(x**7*y) + 1/(x*y)**4) == y**(-4)*x**(-7)*(x**3 + y**3) assert together(5/(2 + 6/(3 + 7/(4 + 8/(5 + 9/x))))) == \ (S(5)/2)*((171 + 119*x)/(279 + 203*x)) assert together(1 + 1/(x + 1)**2) == (1 + (x + 1)**2)/(x + 1)**2 assert together(1 + 1/(x*(1 + x))) == (1 + x*(1 + x))/(x*(1 + x)) assert together( 1/(x*(x + 1)) + 1/(x*(x + 2))) == (3 + 2*x)/(x*(1 + x)*(2 + x)) assert together(1 + 1/(2*x + 2)**2) == (4*(x + 1)**2 + 1)/(4*(x + 1)**2) assert together(sin(1/x + 1/y)) == sin(1/x + 1/y) assert together(sin(1/x + 1/y), deep=True) == sin((x + y)/(x*y)) assert together(1/exp(x) + 1/(x*exp(x))) == (1 + x)/(x*exp(x)) assert together(1/exp(2*x) + 1/(x*exp(3*x))) == (1 + exp(x)*x)/(x*exp(3*x)) assert together(Integral(1/x + 1/y, x)) == Integral((x + y)/(x*y), x) assert together(Eq(1/x + 1/y, 1 + 1/z)) == Eq((x + y)/(x*y), (z + 1)/z) assert together((A*B)**-1 + (B*A)**-1) == (A*B)**-1 + (B*A)**-1 sympy-0.7.4.1/sympy/polys/tests/test_orderings.py0000644000175000017500000001024612253362407022345 0ustar georgeskgeorgesk"""Tests of monomial orderings. """ from sympy.polys.orderings import ( monomial_key, lex, grlex, grevlex, ilex, igrlex, igrevlex, LexOrder, InverseOrder, ProductOrder, build_product_order, ) from sympy.abc import x, y, z, t from sympy.core import S from sympy.utilities.pytest import raises def test_lex_order(): assert lex((1, 2, 3)) == (1, 2, 3) assert str(lex) == 'lex' assert lex((1, 2, 3)) == lex((1, 2, 3)) assert lex((2, 2, 3)) > lex((1, 2, 3)) assert lex((1, 3, 3)) > lex((1, 2, 3)) assert lex((1, 2, 4)) > lex((1, 2, 3)) assert lex((0, 2, 3)) < lex((1, 2, 3)) assert lex((1, 1, 3)) < lex((1, 2, 3)) assert lex((1, 2, 2)) < lex((1, 2, 3)) assert lex.is_global is True assert lex == LexOrder() assert lex != grlex def test_grlex_order(): assert grlex((1, 2, 3)) == (6, (1, 2, 3)) assert str(grlex) == 'grlex' assert grlex((1, 2, 3)) == grlex((1, 2, 3)) assert grlex((2, 2, 3)) > grlex((1, 2, 3)) assert grlex((1, 3, 3)) > grlex((1, 2, 3)) assert grlex((1, 2, 4)) > grlex((1, 2, 3)) assert grlex((0, 2, 3)) < grlex((1, 2, 3)) assert grlex((1, 1, 3)) < grlex((1, 2, 3)) assert grlex((1, 2, 2)) < grlex((1, 2, 3)) assert grlex((2, 2, 3)) > grlex((1, 2, 4)) assert grlex((1, 3, 3)) > grlex((1, 2, 4)) assert grlex((0, 2, 3)) < grlex((1, 2, 2)) assert grlex((1, 1, 3)) < grlex((1, 2, 2)) assert grlex((0, 1, 1)) > grlex((0, 0, 2)) assert grlex((0, 3, 1)) < grlex((2, 2, 1)) assert grlex.is_global is True def test_grevlex_order(): assert grevlex((1, 2, 3)) == (6, (-3, -2, -1)) assert str(grevlex) == 'grevlex' assert grevlex((1, 2, 3)) == grevlex((1, 2, 3)) assert grevlex((2, 2, 3)) > grevlex((1, 2, 3)) assert grevlex((1, 3, 3)) > grevlex((1, 2, 3)) assert grevlex((1, 2, 4)) > grevlex((1, 2, 3)) assert grevlex((0, 2, 3)) < grevlex((1, 2, 3)) assert grevlex((1, 1, 3)) < grevlex((1, 2, 3)) assert grevlex((1, 2, 2)) < grevlex((1, 2, 3)) assert grevlex((2, 2, 3)) > grevlex((1, 2, 4)) assert grevlex((1, 3, 3)) > grevlex((1, 2, 4)) assert grevlex((0, 2, 3)) < grevlex((1, 2, 2)) assert grevlex((1, 1, 3)) < grevlex((1, 2, 2)) assert grevlex((0, 1, 1)) > grevlex((0, 0, 2)) assert grevlex((0, 3, 1)) < grevlex((2, 2, 1)) assert grevlex.is_global is True def test_InverseOrder(): ilex = InverseOrder(lex) igrlex = InverseOrder(grlex) assert ilex((1, 2, 3)) > ilex((2, 0, 3)) assert igrlex((1, 2, 3)) < igrlex((0, 2, 3)) assert str(ilex) == "ilex" assert str(igrlex) == "igrlex" assert ilex.is_global is False assert igrlex.is_global is False assert ilex != igrlex assert ilex == InverseOrder(LexOrder()) def test_ProductOrder(): P = ProductOrder((grlex, lambda m: m[:2]), (grlex, lambda m: m[2:])) assert P((1, 3, 3, 4, 5)) > P((2, 1, 5, 5, 5)) assert str(P) == "ProductOrder(grlex, grlex)" assert P.is_global is True assert ProductOrder((grlex, None), (ilex, None)).is_global is None assert ProductOrder((igrlex, None), (ilex, None)).is_global is False def test_monomial_key(): assert monomial_key() == lex assert monomial_key('lex') == lex assert monomial_key('grlex') == grlex assert monomial_key('grevlex') == grevlex raises(ValueError, lambda: monomial_key('foo')) raises(ValueError, lambda: monomial_key(1)) M = [x, x**2*z**2, x*y, x**2, S(1), y**2, x**3, y, z, x*y**2*z, x**2*y**2] assert sorted(M, key=monomial_key('lex', [z, y, x])) == \ [S(1), x, x**2, x**3, y, x*y, y**2, x**2*y**2, z, x*y**2*z, x**2*z**2] assert sorted(M, key=monomial_key('grlex', [z, y, x])) == \ [S(1), x, y, z, x**2, x*y, y**2, x**3, x**2*y**2, x*y**2*z, x**2*z**2] assert sorted(M, key=monomial_key('grevlex', [z, y, x])) == \ [S(1), x, y, z, x**2, x*y, y**2, x**3, x**2*y**2, x**2*z**2, x*y**2*z] def test_build_product_order(): assert build_product_order((("grlex", x, y), ("grlex", z, t)), [x, y, z, t])((4, 5, 6, 7)) == \ ((9, (4, 5)), (13, (6, 7))) assert build_product_order((("grlex", x, y), ("grlex", z, t)), [x, y, z, t]) == \ build_product_order((("grlex", x, y), ("grlex", z, t)), [x, y, z, t]) sympy-0.7.4.1/sympy/polys/tests/test_fields.py0000644000175000017500000001757612253362407021634 0ustar georgeskgeorgesk"""Test sparse rational functions. """ from sympy.polys.fields import field, FracField from sympy.polys.rings import ring from sympy.polys.domains import ZZ, QQ, RR from sympy.polys.orderings import lex, grlex from sympy.utilities.pytest import raises, XFAIL from sympy.core import Symbol, symbols from sympy import sqrt, Rational def test_FracField___init__(): F1 = FracField("x,y", ZZ, lex) F2 = FracField("x,y", ZZ, lex) F3 = FracField("x,y,z", ZZ, lex) assert F1.x == F1.gens[0] assert F1.y == F1.gens[1] assert F1.x == F2.x assert F1.y == F2.y assert F1.x != F3.x assert F1.y != F3.y def test_FracField___hash__(): F, x, y, z = field("x,y,z", QQ) assert hash(F) def test_FracField___eq__(): assert field("x,y,z", QQ)[0] == field("x,y,z", QQ)[0] assert field("x,y,z", QQ)[0] is field("x,y,z", QQ)[0] assert field("x,y,z", QQ)[0] != field("x,y,z", ZZ)[0] assert field("x,y,z", QQ)[0] is not field("x,y,z", ZZ)[0] assert field("x,y,z", ZZ)[0] != field("x,y,z", QQ)[0] assert field("x,y,z", ZZ)[0] is not field("x,y,z", QQ)[0] assert field("x,y,z", QQ)[0] != field("x,y", QQ)[0] assert field("x,y,z", QQ)[0] is not field("x,y", QQ)[0] assert field("x,y", QQ)[0] != field("x,y,z", QQ)[0] assert field("x,y", QQ)[0] is not field("x,y,z", QQ)[0] def test_FracElement___hash__(): F, x, y, z = field("x,y,z", QQ) assert hash(x*y/z) def test_FracElement_copy(): F, x, y, z = field("x,y,z", ZZ) f = x*y/3*z g = f.copy() assert f == g g.numer[(1, 1, 1)] = 7 assert f != g def test_FracElement_as_expr(): F, x, y, z = field("x,y,z", ZZ) f = (3*x**2*y - x*y*z)/(7*z**3 + 1) X, Y, Z = F.symbols g = (3*X**2*Y - X*Y*Z)/(7*Z**3 + 1) assert f != g assert f.as_expr() == g X, Y, Z = symbols("x,y,z") g = (3*X**2*Y - X*Y*Z)/(7*Z**3 + 1) assert f != g assert f.as_expr(X, Y, Z) == g raises(ValueError, lambda: f.as_expr(X)) def test_FracElement_from_expr(): x, y, z = symbols("x,y,z") F, X, Y, Z = field((x, y, z), ZZ) f = F.from_expr(1) assert f == 1 and isinstance(f, F.dtype) f = F.from_expr(Rational(3, 7)) assert f == F(3)/7 and isinstance(f, F.dtype) f = F.from_expr(x) assert f == X and isinstance(f, F.dtype) f = F.from_expr(Rational(3,7)*x) assert f == 3*X/7 and isinstance(f, F.dtype) f = F.from_expr(1/x) assert f == 1/X and isinstance(f, F.dtype) f = F.from_expr(x*y*z) assert f == X*Y*Z and isinstance(f, F.dtype) f = F.from_expr(x*y/z) assert f == X*Y/Z and isinstance(f, F.dtype) f = F.from_expr(x*y*z + x*y + x) assert f == X*Y*Z + X*Y + X and isinstance(f, F.dtype) f = F.from_expr((x*y*z + x*y + x)/(x*y + 7)) assert f == (X*Y*Z + X*Y + X)/(X*Y + 7) and isinstance(f, F.dtype) f = F.from_expr(x**3*y*z + x**2*y**7 + 1) assert f == X**3*Y*Z + X**2*Y**7 + 1 and isinstance(f, F.dtype) raises(ValueError, lambda: F.from_expr(2**x)) raises(ValueError, lambda: F.from_expr(7*x + sqrt(2))) def test_FracElement__lt_le_gt_ge__(): F, x, y = field("x,y", ZZ) assert F(1) < 1/x < 1/x**2 < 1/x**3 assert F(1) <= 1/x <= 1/x**2 <= 1/x**3 assert -7/x < 1/x < 3/x < y/x < 1/x**2 assert -7/x <= 1/x <= 3/x <= y/x <= 1/x**2 assert 1/x**3 > 1/x**2 > 1/x > F(1) assert 1/x**3 >= 1/x**2 >= 1/x >= F(1) assert 1/x**2 > y/x > 3/x > 1/x > -7/x assert 1/x**2 >= y/x >= 3/x >= 1/x >= -7/x def test_FracElement___neg__(): F, x,y = field("x,y", QQ) f = (7*x - 9)/y g = (-7*x + 9)/y assert -f == g assert -g == f def test_FracElement___add__(): F, x,y = field("x,y", QQ) f, g = 1/x, 1/y assert f + g == g + f == (x + y)/(x*y) assert x + F.ring.gens[0] == F.ring.gens[0] + x == 2*x F, x,y = field("x,y", ZZ) assert x + 3 == 3 + x assert x + QQ(3,7) == QQ(3,7) + x == (7*x + 3)/7 Fuv, u,v = field("u,v", ZZ); Fxyzt, x,y,z,t = field("x,y,z,t", Fuv) f = (u*v + x)/(y + u*v) assert dict(f.numer) == {(1, 0, 0, 0): 1, (0, 0, 0, 0): u*v} assert dict(f.denom) == {(0, 1, 0, 0): 1, (0, 0, 0, 0): u*v} Ruv, u,v = ring("u,v", ZZ); Fxyzt, x,y,z,t = field("x,y,z,t", Ruv) f = (u*v + x)/(y + u*v) assert dict(f.numer) == {(1, 0, 0, 0): 1, (0, 0, 0, 0): u*v} assert dict(f.denom) == {(0, 1, 0, 0): 1, (0, 0, 0, 0): u*v} def test_FracElement___sub__(): F, x,y = field("x,y", QQ) f, g = 1/x, 1/y assert f - g == (-x + y)/(x*y) assert x - F.ring.gens[0] == F.ring.gens[0] - x == 0 F, x,y = field("x,y", ZZ) assert x - 3 == -(3 - x) assert x - QQ(3,7) == -(QQ(3,7) - x) == (7*x - 3)/7 Fuv, u,v = field("u,v", ZZ); Fxyzt, x,y,z,t = field("x,y,z,t", Fuv) f = (u*v - x)/(y - u*v) assert dict(f.numer) == {(1, 0, 0, 0):-1, (0, 0, 0, 0): u*v} assert dict(f.denom) == {(0, 1, 0, 0): 1, (0, 0, 0, 0):-u*v} Ruv, u,v = ring("u,v", ZZ); Fxyzt, x,y,z,t = field("x,y,z,t", Ruv) f = (u*v - x)/(y - u*v) assert dict(f.numer) == {(1, 0, 0, 0):-1, (0, 0, 0, 0): u*v} assert dict(f.denom) == {(0, 1, 0, 0): 1, (0, 0, 0, 0):-u*v} def test_FracElement___mul__(): F, x,y = field("x,y", QQ) f, g = 1/x, 1/y assert f*g == g*f == 1/(x*y) assert x*F.ring.gens[0] == F.ring.gens[0]*x == x**2 F, x,y = field("x,y", ZZ) assert x*3 == 3*x assert x*QQ(3,7) == QQ(3,7)*x == 3*x/7 Fuv, u,v = field("u,v", ZZ); Fxyzt, x,y,z,t = field("x,y,z,t", Fuv) f = ((u + 1)*x*y + 1)/((v - 1)*z - t*u*v - 1) assert dict(f.numer) == {(1, 1, 0, 0): u + 1, (0, 0, 0, 0): 1} assert dict(f.denom) == {(0, 0, 1, 0): v - 1, (0, 0, 0, 1): -u*v, (0, 0, 0, 0): -1} Ruv, u,v = ring("u,v", ZZ); Fxyzt, x,y,z,t = field("x,y,z,t", Ruv) f = ((u + 1)*x*y + 1)/((v - 1)*z - t*u*v - 1) assert dict(f.numer) == {(1, 1, 0, 0): u + 1, (0, 0, 0, 0): 1} assert dict(f.denom) == {(0, 0, 1, 0): v - 1, (0, 0, 0, 1): -u*v, (0, 0, 0, 0): -1} def test_FracElement___div__(): F, x,y = field("x,y", QQ) f, g = 1/x, 1/y assert f/g == y/x assert x/F.ring.gens[0] == F.ring.gens[0]/x == 1 F, x,y = field("x,y", ZZ) assert x*3 == 3*x assert x/QQ(3,7) == (QQ(3,7)/x)**-1 == 7*x/3 raises(ZeroDivisionError, lambda: x/0) raises(ZeroDivisionError, lambda: 1/(x - x)) raises(ZeroDivisionError, lambda: x/(x - x)) Fuv, u,v = field("u,v", ZZ); Fxyzt, x,y,z,t = field("x,y,z,t", Fuv) f = (u*v)/(x*y) assert dict(f.numer) == {(0, 0, 0, 0): u*v} assert dict(f.denom) == {(1, 1, 0, 0): 1} g = (x*y)/(u*v) assert dict(g.numer) == {(1, 1, 0, 0): 1} assert dict(g.denom) == {(0, 0, 0, 0): u*v} Ruv, u,v = ring("u,v", ZZ); Fxyzt, x,y,z,t = field("x,y,z,t", Ruv) f = (u*v)/(x*y) assert dict(f.numer) == {(0, 0, 0, 0): u*v} assert dict(f.denom) == {(1, 1, 0, 0): 1} g = (x*y)/(u*v) assert dict(g.numer) == {(1, 1, 0, 0): 1} assert dict(g.denom) == {(0, 0, 0, 0): u*v} def test_FracElement___pow__(): F, x,y = field("x,y", QQ) f, g = 1/x, 1/y assert f**3 == 1/x**3 assert g**3 == 1/y**3 assert (f*g)**3 == 1/(x**3*y**3) assert (f*g)**-3 == (x*y)**3 raises(ZeroDivisionError, lambda: (x - x)**-3) def test_FracElement_diff(): F, x,y,z = field("x,y,z", ZZ) assert ((x**2 + y)/(z + 1)).diff(x) == 2*x/(z + 1) @XFAIL def test_FracElement___call__(): F, x,y,z = field("x,y,z", ZZ) f = (x**2 + 3*y)/z r = f(1, 1, 1) assert r == 4 and not isinstance(r, FracElement) raises(ZeroDivisionError, lambda: f(1, 1, 0)) def test_FracElement_evaluate(): F, x,y,z = field("x,y,z", ZZ) Fyz = field("y,z", ZZ)[0] f = (x**2 + 3*y)/z assert f.evaluate(x, 0) == 3*Fyz.y/Fyz.z raises(ZeroDivisionError, lambda: f.evaluate(z, 0)) def test_FracElement_subs(): F, x,y,z = field("x,y,z", ZZ) f = (x**2 + 3*y)/z assert f.subs(x, 0) == 3*y/z raises(ZeroDivisionError, lambda: f.subs(z, 0)) def test_FracElement_compose(): pass sympy-0.7.4.1/sympy/polys/tests/test_densearith.py0000644000175000017500000011524512253362407022504 0ustar georgeskgeorgesk"""Tests for dense recursive polynomials' arithmetics. """ from sympy.polys.densebasic import ( dup_normal, dmp_normal, ) from sympy.polys.densearith import ( dup_add_term, dmp_add_term, dup_sub_term, dmp_sub_term, dup_mul_term, dmp_mul_term, dup_add_ground, dmp_add_ground, dup_sub_ground, dmp_sub_ground, dup_mul_ground, dmp_mul_ground, dup_quo_ground, dmp_quo_ground, dup_exquo_ground, dmp_exquo_ground, dup_lshift, dup_rshift, dup_abs, dmp_abs, dup_neg, dmp_neg, dup_add, dmp_add, dup_sub, dmp_sub, dup_mul, dmp_mul, dup_sqr, dmp_sqr, dup_pow, dmp_pow, dup_add_mul, dmp_add_mul, dup_sub_mul, dmp_sub_mul, dup_pdiv, dup_prem, dup_pquo, dup_pexquo, dmp_pdiv, dmp_prem, dmp_pquo, dmp_pexquo, dup_rr_div, dmp_rr_div, dup_ff_div, dmp_ff_div, dup_div, dup_rem, dup_quo, dup_exquo, dmp_div, dmp_rem, dmp_quo, dmp_exquo, dup_max_norm, dmp_max_norm, dup_l1_norm, dmp_l1_norm, dup_expand, dmp_expand, ) from sympy.polys.polyerrors import ( ExactQuotientFailed, ) from sympy.polys.specialpolys import f_polys from sympy.polys.domains import FF, ZZ, QQ from sympy.utilities.pytest import raises f_0, f_1, f_2, f_3, f_4, f_5, f_6 = [ f.to_dense() for f in f_polys() ] F_0 = dmp_mul_ground(dmp_normal(f_0, 2, QQ), QQ(1, 7), 2, QQ) def test_dup_add_term(): f = dup_normal([], ZZ) assert dup_add_term(f, ZZ(0), 0, ZZ) == dup_normal([], ZZ) assert dup_add_term(f, ZZ(1), 0, ZZ) == dup_normal([1], ZZ) assert dup_add_term(f, ZZ(1), 1, ZZ) == dup_normal([1, 0], ZZ) assert dup_add_term(f, ZZ(1), 2, ZZ) == dup_normal([1, 0, 0], ZZ) f = dup_normal([1, 1, 1], ZZ) assert dup_add_term(f, ZZ(1), 0, ZZ) == dup_normal([1, 1, 2], ZZ) assert dup_add_term(f, ZZ(1), 1, ZZ) == dup_normal([1, 2, 1], ZZ) assert dup_add_term(f, ZZ(1), 2, ZZ) == dup_normal([2, 1, 1], ZZ) assert dup_add_term(f, ZZ(1), 3, ZZ) == dup_normal([1, 1, 1, 1], ZZ) assert dup_add_term(f, ZZ(1), 4, ZZ) == dup_normal([1, 0, 1, 1, 1], ZZ) assert dup_add_term(f, ZZ(1), 5, ZZ) == dup_normal([1, 0, 0, 1, 1, 1], ZZ) assert dup_add_term( f, ZZ(1), 6, ZZ) == dup_normal([1, 0, 0, 0, 1, 1, 1], ZZ) assert dup_add_term(f, ZZ(-1), 2, ZZ) == dup_normal([1, 1], ZZ) def test_dmp_add_term(): assert dmp_add_term([ZZ(1), ZZ(1), ZZ(1)], ZZ(1), 2, 0, ZZ) == \ dup_add_term([ZZ(1), ZZ(1), ZZ(1)], ZZ(1), 2, ZZ) assert dmp_add_term(f_0, [[]], 3, 2, ZZ) == f_0 assert dmp_add_term(F_0, [[]], 3, 2, QQ) == F_0 def test_dup_sub_term(): f = dup_normal([], ZZ) assert dup_sub_term(f, ZZ(0), 0, ZZ) == dup_normal([], ZZ) assert dup_sub_term(f, ZZ(1), 0, ZZ) == dup_normal([-1], ZZ) assert dup_sub_term(f, ZZ(1), 1, ZZ) == dup_normal([-1, 0], ZZ) assert dup_sub_term(f, ZZ(1), 2, ZZ) == dup_normal([-1, 0, 0], ZZ) f = dup_normal([1, 1, 1], ZZ) assert dup_sub_term(f, ZZ(2), 0, ZZ) == dup_normal([ 1, 1, -1], ZZ) assert dup_sub_term(f, ZZ(2), 1, ZZ) == dup_normal([ 1, -1, 1], ZZ) assert dup_sub_term(f, ZZ(2), 2, ZZ) == dup_normal([-1, 1, 1], ZZ) assert dup_sub_term(f, ZZ(1), 3, ZZ) == dup_normal([-1, 1, 1, 1], ZZ) assert dup_sub_term(f, ZZ(1), 4, ZZ) == dup_normal([-1, 0, 1, 1, 1], ZZ) assert dup_sub_term(f, ZZ(1), 5, ZZ) == dup_normal([-1, 0, 0, 1, 1, 1], ZZ) assert dup_sub_term( f, ZZ(1), 6, ZZ) == dup_normal([-1, 0, 0, 0, 1, 1, 1], ZZ) assert dup_sub_term(f, ZZ(1), 2, ZZ) == dup_normal([1, 1], ZZ) def test_dmp_sub_term(): assert dmp_sub_term([ZZ(1), ZZ(1), ZZ(1)], ZZ(1), 2, 0, ZZ) == \ dup_sub_term([ZZ(1), ZZ(1), ZZ(1)], ZZ(1), 2, ZZ) assert dmp_sub_term(f_0, [[]], 3, 2, ZZ) == f_0 assert dmp_sub_term(F_0, [[]], 3, 2, QQ) == F_0 def test_dup_mul_term(): f = dup_normal([], ZZ) assert dup_mul_term(f, ZZ(2), 3, ZZ) == dup_normal([], ZZ) f = dup_normal([1, 1], ZZ) assert dup_mul_term(f, ZZ(0), 3, ZZ) == dup_normal([], ZZ) f = dup_normal([1, 2, 3], ZZ) assert dup_mul_term(f, ZZ(2), 0, ZZ) == dup_normal([2, 4, 6], ZZ) assert dup_mul_term(f, ZZ(2), 1, ZZ) == dup_normal([2, 4, 6, 0], ZZ) assert dup_mul_term(f, ZZ(2), 2, ZZ) == dup_normal([2, 4, 6, 0, 0], ZZ) assert dup_mul_term(f, ZZ(2), 3, ZZ) == dup_normal([2, 4, 6, 0, 0, 0], ZZ) def test_dmp_mul_term(): assert dmp_mul_term([ZZ(1), ZZ(2), ZZ(3)], ZZ(2), 1, 0, ZZ) == \ dup_mul_term([ZZ(1), ZZ(2), ZZ(3)], ZZ(2), 1, ZZ) assert dmp_mul_term([[]], [ZZ(2)], 3, 1, ZZ) == [[]] assert dmp_mul_term([[ZZ(1)]], [], 3, 1, ZZ) == [[]] assert dmp_mul_term([[ZZ(1), ZZ(2)], [ZZ(3)]], [ZZ(2)], 2, 1, ZZ) == \ [[ZZ(2), ZZ(4)], [ZZ(6)], [], []] assert dmp_mul_term([[]], [QQ(2, 3)], 3, 1, QQ) == [[]] assert dmp_mul_term([[QQ(1, 2)]], [], 3, 1, QQ) == [[]] assert dmp_mul_term([[QQ(1, 5), QQ(2, 5)], [QQ(3, 5)]], [QQ(2, 3)], 2, 1, QQ) == \ [[QQ(2, 15), QQ(4, 15)], [QQ(6, 15)], [], []] def test_dup_add_ground(): f = ZZ.map([1, 2, 3, 4]) g = ZZ.map([1, 2, 3, 8]) assert dup_add_ground(f, ZZ(4), ZZ) == g def test_dmp_add_ground(): f = ZZ.map([[1], [2], [3], [4]]) g = ZZ.map([[1], [2], [3], [8]]) assert dmp_add_ground(f, ZZ(4), 1, ZZ) == g def test_dup_sub_ground(): f = ZZ.map([1, 2, 3, 4]) g = ZZ.map([1, 2, 3, 0]) assert dup_sub_ground(f, ZZ(4), ZZ) == g def test_dmp_sub_ground(): f = ZZ.map([[1], [2], [3], [4]]) g = ZZ.map([[1], [2], [3], []]) assert dmp_sub_ground(f, ZZ(4), 1, ZZ) == g def test_dup_mul_ground(): f = dup_normal([], ZZ) assert dup_mul_ground(f, ZZ(2), ZZ) == dup_normal([], ZZ) f = dup_normal([1, 2, 3], ZZ) assert dup_mul_ground(f, ZZ(0), ZZ) == dup_normal([], ZZ) assert dup_mul_ground(f, ZZ(2), ZZ) == dup_normal([2, 4, 6], ZZ) def test_dmp_mul_ground(): assert dmp_mul_ground(f_0, ZZ(2), 2, ZZ) == [ [[ZZ(2), ZZ(4), ZZ(6)], [ZZ(4)]], [[ZZ(6)]], [[ZZ(8), ZZ(10), ZZ(12)], [ZZ(2), ZZ(4), ZZ(2)], [ZZ(2)]] ] assert dmp_mul_ground(F_0, QQ(1, 2), 2, QQ) == [ [[QQ(1, 14), QQ(2, 14), QQ(3, 14)], [QQ(2, 14)]], [[QQ(3, 14)]], [[QQ(4, 14), QQ(5, 14), QQ(6, 14)], [QQ(1, 14), QQ(2, 14), QQ(1, 14)], [QQ(1, 14)]] ] def test_dup_quo_ground(): raises(ZeroDivisionError, lambda: dup_quo_ground(dup_normal([1, 2, 3], ZZ), ZZ(0), ZZ)) f = dup_normal([], ZZ) assert dup_quo_ground(f, ZZ(3), ZZ) == dup_normal([], ZZ) f = dup_normal([6, 2, 8], ZZ) assert dup_quo_ground(f, ZZ(1), ZZ) == f assert dup_quo_ground(f, ZZ(2), ZZ) == dup_normal([3, 1, 4], ZZ) assert dup_quo_ground(f, ZZ(3), ZZ) == dup_normal([2, 0, 2], ZZ) f = dup_normal([6, 2, 8], QQ) assert dup_quo_ground(f, QQ(1), QQ) == f assert dup_quo_ground(f, QQ(2), QQ) == [QQ(3), QQ(1), QQ(4)] assert dup_quo_ground(f, QQ(7), QQ) == [QQ(6, 7), QQ(2, 7), QQ(8, 7)] def test_dup_exquo_ground(): raises(ZeroDivisionError, lambda: dup_exquo_ground(dup_normal([1, 2, 3], ZZ), ZZ(0), ZZ)) raises(ExactQuotientFailed, lambda: dup_exquo_ground(dup_normal([1, 2, 3], ZZ), ZZ(3), ZZ)) f = dup_normal([], ZZ) assert dup_exquo_ground(f, ZZ(3), ZZ) == dup_normal([], ZZ) f = dup_normal([6, 2, 8], ZZ) assert dup_exquo_ground(f, ZZ(1), ZZ) == f assert dup_exquo_ground(f, ZZ(2), ZZ) == dup_normal([3, 1, 4], ZZ) f = dup_normal([6, 2, 8], QQ) assert dup_exquo_ground(f, QQ(1), QQ) == f assert dup_exquo_ground(f, QQ(2), QQ) == [QQ(3), QQ(1), QQ(4)] assert dup_exquo_ground(f, QQ(7), QQ) == [QQ(6, 7), QQ(2, 7), QQ(8, 7)] def test_dmp_quo_ground(): f = dmp_normal([[6], [2], [8]], 1, ZZ) assert dmp_quo_ground(f, ZZ(1), 1, ZZ) == f assert dmp_quo_ground( f, ZZ(2), 1, ZZ) == dmp_normal([[3], [1], [4]], 1, ZZ) assert dmp_normal(dmp_quo_ground( f, ZZ(3), 1, ZZ), 1, ZZ) == dmp_normal([[2], [], [2]], 1, ZZ) def test_dmp_exquo_ground(): f = dmp_normal([[6], [2], [8]], 1, ZZ) assert dmp_exquo_ground(f, ZZ(1), 1, ZZ) == f assert dmp_exquo_ground( f, ZZ(2), 1, ZZ) == dmp_normal([[3], [1], [4]], 1, ZZ) def test_dup_lshift(): assert dup_lshift([], 3, ZZ) == [] assert dup_lshift([1], 3, ZZ) == [1, 0, 0, 0] def test_dup_rshift(): assert dup_rshift([], 3, ZZ) == [] assert dup_rshift([1, 0, 0, 0], 3, ZZ) == [1] def test_dup_abs(): assert dup_abs([], ZZ) == [] assert dup_abs([ZZ( 1)], ZZ) == [ZZ(1)] assert dup_abs([ZZ(-7)], ZZ) == [ZZ(7)] assert dup_abs([ZZ(-1), ZZ(2), ZZ(3)], ZZ) == [ZZ(1), ZZ(2), ZZ(3)] assert dup_abs([], QQ) == [] assert dup_abs([QQ( 1, 2)], QQ) == [QQ(1, 2)] assert dup_abs([QQ(-7, 3)], QQ) == [QQ(7, 3)] assert dup_abs( [QQ(-1, 7), QQ(2, 7), QQ(3, 7)], QQ) == [QQ(1, 7), QQ(2, 7), QQ(3, 7)] def test_dmp_abs(): assert dmp_abs([ZZ(-1)], 0, ZZ) == [ZZ(1)] assert dmp_abs([QQ(-1, 2)], 0, QQ) == [QQ(1, 2)] assert dmp_abs([[[]]], 2, ZZ) == [[[]]] assert dmp_abs([[[ZZ(1)]]], 2, ZZ) == [[[ZZ(1)]]] assert dmp_abs([[[ZZ(-7)]]], 2, ZZ) == [[[ZZ(7)]]] assert dmp_abs([[[]]], 2, QQ) == [[[]]] assert dmp_abs([[[QQ(1, 2)]]], 2, QQ) == [[[QQ(1, 2)]]] assert dmp_abs([[[QQ(-7, 9)]]], 2, QQ) == [[[QQ(7, 9)]]] def test_dup_neg(): assert dup_neg([], ZZ) == [] assert dup_neg([ZZ(1)], ZZ) == [ZZ(-1)] assert dup_neg([ZZ(-7)], ZZ) == [ZZ(7)] assert dup_neg([ZZ(-1), ZZ(2), ZZ(3)], ZZ) == [ZZ(1), ZZ(-2), ZZ(-3)] assert dup_neg([], QQ) == [] assert dup_neg([QQ(1, 2)], QQ) == [QQ(-1, 2)] assert dup_neg([QQ(-7, 9)], QQ) == [QQ(7, 9)] assert dup_neg([QQ( -1, 7), QQ(2, 7), QQ(3, 7)], QQ) == [QQ(1, 7), QQ(-2, 7), QQ(-3, 7)] def test_dmp_neg(): assert dmp_neg([ZZ(-1)], 0, ZZ) == [ZZ(1)] assert dmp_neg([QQ(-1, 2)], 0, QQ) == [QQ(1, 2)] assert dmp_neg([[[]]], 2, ZZ) == [[[]]] assert dmp_neg([[[ZZ(1)]]], 2, ZZ) == [[[ZZ(-1)]]] assert dmp_neg([[[ZZ(-7)]]], 2, ZZ) == [[[ZZ(7)]]] assert dmp_neg([[[]]], 2, QQ) == [[[]]] assert dmp_neg([[[QQ(1, 9)]]], 2, QQ) == [[[QQ(-1, 9)]]] assert dmp_neg([[[QQ(-7, 9)]]], 2, QQ) == [[[QQ(7, 9)]]] def test_dup_add(): assert dup_add([], [], ZZ) == [] assert dup_add([ZZ(1)], [], ZZ) == [ZZ(1)] assert dup_add([], [ZZ(1)], ZZ) == [ZZ(1)] assert dup_add([ZZ(1)], [ZZ(1)], ZZ) == [ZZ(2)] assert dup_add([ZZ(1)], [ZZ(2)], ZZ) == [ZZ(3)] assert dup_add([ZZ(1), ZZ(2)], [ZZ(1)], ZZ) == [ZZ(1), ZZ(3)] assert dup_add([ZZ(1)], [ZZ(1), ZZ(2)], ZZ) == [ZZ(1), ZZ(3)] assert dup_add([ZZ(1), ZZ( 2), ZZ(3)], [ZZ(8), ZZ(9), ZZ(10)], ZZ) == [ZZ(9), ZZ(11), ZZ(13)] assert dup_add([], [], QQ) == [] assert dup_add([QQ(1, 2)], [], QQ) == [QQ(1, 2)] assert dup_add([], [QQ(1, 2)], QQ) == [QQ(1, 2)] assert dup_add([QQ(1, 4)], [QQ(1, 4)], QQ) == [QQ(1, 2)] assert dup_add([QQ(1, 4)], [QQ(1, 2)], QQ) == [QQ(3, 4)] assert dup_add([QQ(1, 2), QQ(2, 3)], [QQ(1)], QQ) == [QQ(1, 2), QQ(5, 3)] assert dup_add([QQ(1)], [QQ(1, 2), QQ(2, 3)], QQ) == [QQ(1, 2), QQ(5, 3)] assert dup_add([QQ(1, 7), QQ(2, 7), QQ(3, 7)], [QQ( 8, 7), QQ(9, 7), QQ(10, 7)], QQ) == [QQ(9, 7), QQ(11, 7), QQ(13, 7)] def test_dmp_add(): assert dmp_add([ZZ(1), ZZ(2)], [ZZ(1)], 0, ZZ) == \ dup_add([ZZ(1), ZZ(2)], [ZZ(1)], ZZ) assert dmp_add([QQ(1, 2), QQ(2, 3)], [QQ(1)], 0, QQ) == \ dup_add([QQ(1, 2), QQ(2, 3)], [QQ(1)], QQ) assert dmp_add([[[]]], [[[]]], 2, ZZ) == [[[]]] assert dmp_add([[[ZZ(1)]]], [[[]]], 2, ZZ) == [[[ZZ(1)]]] assert dmp_add([[[]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(1)]]] assert dmp_add([[[ZZ(2)]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(3)]]] assert dmp_add([[[ZZ(1)]]], [[[ZZ(2)]]], 2, ZZ) == [[[ZZ(3)]]] assert dmp_add([[[]]], [[[]]], 2, QQ) == [[[]]] assert dmp_add([[[QQ(1, 2)]]], [[[]]], 2, QQ) == [[[QQ(1, 2)]]] assert dmp_add([[[]]], [[[QQ(1, 2)]]], 2, QQ) == [[[QQ(1, 2)]]] assert dmp_add([[[QQ(2, 7)]]], [[[QQ(1, 7)]]], 2, QQ) == [[[QQ(3, 7)]]] assert dmp_add([[[QQ(1, 7)]]], [[[QQ(2, 7)]]], 2, QQ) == [[[QQ(3, 7)]]] def test_dup_sub(): assert dup_sub([], [], ZZ) == [] assert dup_sub([ZZ(1)], [], ZZ) == [ZZ(1)] assert dup_sub([], [ZZ(1)], ZZ) == [ZZ(-1)] assert dup_sub([ZZ(1)], [ZZ(1)], ZZ) == [] assert dup_sub([ZZ(1)], [ZZ(2)], ZZ) == [ZZ(-1)] assert dup_sub([ZZ(1), ZZ(2)], [ZZ(1)], ZZ) == [ZZ(1), ZZ(1)] assert dup_sub([ZZ(1)], [ZZ(1), ZZ(2)], ZZ) == [ZZ(-1), ZZ(-1)] assert dup_sub([ZZ(3), ZZ( 2), ZZ(1)], [ZZ(8), ZZ(9), ZZ(10)], ZZ) == [ZZ(-5), ZZ(-7), ZZ(-9)] assert dup_sub([], [], QQ) == [] assert dup_sub([QQ(1, 2)], [], QQ) == [QQ(1, 2)] assert dup_sub([], [QQ(1, 2)], QQ) == [QQ(-1, 2)] assert dup_sub([QQ(1, 3)], [QQ(1, 3)], QQ) == [] assert dup_sub([QQ(1, 3)], [QQ(2, 3)], QQ) == [QQ(-1, 3)] assert dup_sub([QQ(1, 7), QQ(2, 7)], [QQ(1)], QQ) == [QQ(1, 7), QQ(-5, 7)] assert dup_sub([QQ(1)], [QQ(1, 7), QQ(2, 7)], QQ) == [QQ(-1, 7), QQ(5, 7)] assert dup_sub([QQ(3, 7), QQ(2, 7), QQ(1, 7)], [QQ( 8, 7), QQ(9, 7), QQ(10, 7)], QQ) == [QQ(-5, 7), QQ(-7, 7), QQ(-9, 7)] def test_dmp_sub(): assert dmp_sub([ZZ(1), ZZ(2)], [ZZ(1)], 0, ZZ) == \ dup_sub([ZZ(1), ZZ(2)], [ZZ(1)], ZZ) assert dmp_sub([QQ(1, 2), QQ(2, 3)], [QQ(1)], 0, QQ) == \ dup_sub([QQ(1, 2), QQ(2, 3)], [QQ(1)], QQ) assert dmp_sub([[[]]], [[[]]], 2, ZZ) == [[[]]] assert dmp_sub([[[ZZ(1)]]], [[[]]], 2, ZZ) == [[[ZZ(1)]]] assert dmp_sub([[[]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(-1)]]] assert dmp_sub([[[ZZ(2)]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(1)]]] assert dmp_sub([[[ZZ(1)]]], [[[ZZ(2)]]], 2, ZZ) == [[[ZZ(-1)]]] assert dmp_sub([[[]]], [[[]]], 2, QQ) == [[[]]] assert dmp_sub([[[QQ(1, 2)]]], [[[]]], 2, QQ) == [[[QQ(1, 2)]]] assert dmp_sub([[[]]], [[[QQ(1, 2)]]], 2, QQ) == [[[QQ(-1, 2)]]] assert dmp_sub([[[QQ(2, 7)]]], [[[QQ(1, 7)]]], 2, QQ) == [[[QQ(1, 7)]]] assert dmp_sub([[[QQ(1, 7)]]], [[[QQ(2, 7)]]], 2, QQ) == [[[QQ(-1, 7)]]] def test_dup_add_mul(): assert dup_add_mul([ZZ(1), ZZ(2), ZZ(3)], [ZZ(3), ZZ(2), ZZ(1)], [ZZ(1), ZZ(2)], ZZ) == [ZZ(3), ZZ(9), ZZ(7), ZZ(5)] def test_dup_add_mul(): assert dmp_add_mul([[ZZ(1), ZZ(2)], [ZZ(3)]], [[ZZ(3)], [ZZ(2), ZZ(1)]], [[ZZ(1)], [ZZ(2)]], 1, ZZ) == [[ZZ(3)], [ZZ(3), ZZ(9)], [ZZ(4), ZZ(5)]] def test_dup_sub_mul(): assert dup_sub_mul([ZZ(1), ZZ(2), ZZ(3)], [ZZ(3), ZZ(2), ZZ(1)], [ZZ(1), ZZ(2)], ZZ) == [ZZ(-3), ZZ(-7), ZZ(-3), ZZ(1)] def test_dup_sub_mul(): assert dmp_sub_mul([[ZZ(1), ZZ(2)], [ZZ(3)]], [[ZZ(3)], [ZZ(2), ZZ(1)]], [[ZZ(1)], [ZZ(2)]], 1, ZZ) == [[ZZ(-3)], [ZZ(-1), ZZ(-5)], [ZZ(-4), ZZ(1)]] def test_dup_mul(): assert dup_mul([], [], ZZ) == [] assert dup_mul([], [ZZ(1)], ZZ) == [] assert dup_mul([ZZ(1)], [], ZZ) == [] assert dup_mul([ZZ(1)], [ZZ(1)], ZZ) == [ZZ(1)] assert dup_mul([ZZ(5)], [ZZ(7)], ZZ) == [ZZ(35)] assert dup_mul([], [], QQ) == [] assert dup_mul([], [QQ(1, 2)], QQ) == [] assert dup_mul([QQ(1, 2)], [], QQ) == [] assert dup_mul([QQ(1, 2)], [QQ(4, 7)], QQ) == [QQ(2, 7)] assert dup_mul([QQ(5, 7)], [QQ(3, 7)], QQ) == [QQ(15, 49)] f = dup_normal([3, 0, 0, 6, 1, 2], ZZ) g = dup_normal([4, 0, 1, 0], ZZ) h = dup_normal([12, 0, 3, 24, 4, 14, 1, 2, 0], ZZ) assert dup_mul(f, g, ZZ) == h assert dup_mul(g, f, ZZ) == h f = dup_normal([2, 0, 0, 1, 7], ZZ) h = dup_normal([4, 0, 0, 4, 28, 0, 1, 14, 49], ZZ) assert dup_mul(f, f, ZZ) == h K = FF(6) assert dup_mul([K(2), K(1)], [K(3), K(4)], K) == [K(5), K(4)] p1 = dup_normal([79, -1, 78, -94, -10, 11, 32, -19, 78, 2, -89, 30, 73, 42, 85, 77, 83, -30, -34, -2, 95, -81, 37, -49, -46, -58, -16, 37, 35, -11, -57, -15, -31, 67, -20, 27, 76, 2, 70, 67, -65, 65, -26, -93, -44, -12, -92, 57, -90, -57, -11, -67, -98, -69, 97, -41, 89, 33, 89, -50, 81, -31, 60, -27, 43, 29, -77, 44, 21, -91, 32, -57, 33, 3, 53, -51, -38, -99, -84, 23, -50, 66, -100, 1, -75, -25, 27, -60, 98, -51, -87, 6, 8, 78, -28, -95, -88, 12, -35, 26, -9, 16, -92, 55, -7, -86, 68, -39, -46, 84, 94, 45, 60, 92, 68, -75, -74, -19, 8, 75, 78, 91, 57, 34, 14, -3, -49, 65, 78, -18, 6, -29, -80, -98, 17, 13, 58, 21, 20, 9, 37, 7, -30, -53, -20, 34, 67, -42, 89, -22, 73, 43, -6, 5, 51, -8, -15, -52, -22, -58, -72, -3, 43, -92, 82, 83, -2, -13, -23, -60, 16, -94, -8, -28, -95, -72, 63, -90, 76, 6, -43, -100, -59, 76, 3, 3, 46, -85, 75, 62, -71, -76, 88, 97, -72, -1, 30, -64, 72, -48, 14, -78, 58, 63, -91, 24, -87, -27, -80, -100, -44, 98, 70, 100, -29, -38, 11, 77, 100, 52, 86, 65, -5, -42, -81, -38, -42, 43, -2, -70, -63, -52], ZZ) p2 = dup_normal([65, -19, -47, 1, 90, 81, -15, -34, 25, -75, 9, -83, 50, -5, -44, 31, 1, 70, -7, 78, 74, 80, 85, 65, 21, 41, 66, 19, -40, 63, -21, -27, 32, 69, 83, 34, -35, 14, 81, 57, -75, 32, -67, -89, -100, -61, 46, 84, -78, -29, -50, -94, -24, -32, -68, -16, 100, -7, -72, -89, 35, 82, 58, 81, -92, 62, 5, -47, -39, -58, -72, -13, 84, 44, 55, -25, 48, -54, -31, -56, -11, -50, -84, 10, 67, 17, 13, -14, 61, 76, -64, -44, -40, -96, 11, -11, -94, 2, 6, 27, -6, 68, -54, 66, -74, -14, -1, -24, -73, 96, 89, -11, -89, 56, -53, 72, -43, 96, 25, 63, -31, 29, 68, 83, 91, -93, -19, -38, -40, 40, -12, -19, -79, 44, 100, -66, -29, -77, 62, 39, -8, 11, -97, 14, 87, 64, 21, -18, 13, 15, -59, -75, -99, -88, 57, 54, 56, -67, 6, -63, -59, -14, 28, 87, -20, -39, 84, -91, -2, 49, -75, 11, -24, -95, 36, 66, 5, 25, -72, -40, 86, 90, 37, -33, 57, -35, 29, -18, 4, -79, 64, -17, -27, 21, 29, -5, -44, -87, -24, 52, 78, 11, -23, -53, 36, 42, 21, -68, 94, -91, -51, -21, 51, -76, 72, 31, 24, -48, -80, -9, 37, -47, -6, -8, -63, -91, 79, -79, -100, 38, -20, 38, 100, 83, -90, 87, 63, -36, 82, -19, 18, -98, -38, 26, 98, -70, 79, 92, 12, 12, 70, 74, 36, 48, -13, 31, 31, -47, -71, -12, -64, 36, -42, 32, -86, 60, 83, 70, 55, 0, 1, 29, -35, 8, -82, 8, -73, -46, -50, 43, 48, -5, -86, -72, 44, -90, 19, 19, 5, -20, 97, -13, -66, -5, 5, -69, 64, -30, 41, 51, 36, 13, -99, -61, 94, -12, 74, 98, 68, 24, 46, -97, -87, -6, -27, 82, 62, -11, -77, 86, 66, -47, -49, -50, 13, 18, 89, -89, 46, -80, 13, 98, -35, -36, -25, 12, 20, 26, -52, 79, 27, 79, 100, 8, 62, -58, -28, 37], ZZ) res = dup_normal([5135, -1566, 1376, -7466, 4579, 11710, 8001, -7183, -3737, -7439, 345, -10084, 24522, -1201, 1070, -10245, 9582, 9264, 1903, 23312, 18953, 10037, -15268, -5450, 6442, -6243, -3777, 5110, 10936, -16649, -6022, 16255, 31300, 24818, 31922, 32760, 7854, 27080, 15766, 29596, 7139, 31945, -19810, 465, -38026, -3971, 9641, 465, -19375, 5524, -30112, -11960, -12813, 13535, 30670, 5925, -43725, -14089, 11503, -22782, 6371, 43881, 37465, -33529, -33590, -39798, -37854, -18466, -7908, -35825, -26020, -36923, -11332, -5699, 25166, -3147, 19885, 12962, -20659, -1642, 27723, -56331, -24580, -11010, -20206, 20087, -23772, -16038, 38580, 20901, -50731, 32037, -4299, 26508, 18038, -28357, 31846, -7405, -20172, -15894, 2096, 25110, -45786, 45918, -55333, -31928, -49428, -29824, -58796, -24609, -15408, 69, -35415, -18439, 10123, -20360, -65949, 33356, -20333, 26476, -32073, 33621, 930, 28803, -42791, 44716, 38164, 12302, -1739, 11421, 73385, -7613, 14297, 38155, -414, 77587, 24338, -21415, 29367, 42639, 13901, -288, 51027, -11827, 91260, 43407, 88521, -15186, 70572, -12049, 5090, -12208, -56374, 15520, -623, -7742, 50825, 11199, -14894, 40892, 59591, -31356, -28696, -57842, -87751, -33744, -28436, -28945, -40287, 37957, -35638, 33401, -61534, 14870, 40292, 70366, -10803, 102290, -71719, -85251, 7902, -22409, 75009, 99927, 35298, -1175, -762, -34744, -10587, -47574, -62629, -19581, -43659, -54369, -32250, -39545, 15225, -24454, 11241, -67308, -30148, 39929, 37639, 14383, -73475, -77636, -81048, -35992, 41601, -90143, 76937, -8112, 56588, 9124, -40094, -32340, 13253, 10898, -51639, 36390, 12086, -1885, 100714, -28561, -23784, -18735, 18916, 16286, 10742, -87360, -13697, 10689, -19477, -29770, 5060, 20189, -8297, 112407, 47071, 47743, 45519, -4109, 17468, -68831, 78325, -6481, -21641, -19459, 30919, 96115, 8607, 53341, 32105, -16211, 23538, 57259, -76272, -40583, 62093, 38511, -34255, -40665, -40604, -37606, -15274, 33156, -13885, 103636, 118678, -14101, -92682, -100791, 2634, 63791, 98266, 19286, -34590, -21067, -71130, 25380, -40839, -27614, -26060, 52358, -15537, 27138, -6749, 36269, -33306, 13207, -91084, -5540, -57116, 69548, 44169, -57742, -41234, -103327, -62904, -8566, 41149, -12866, 71188, 23980, 1838, 58230, 73950, 5594, 43113, -8159, -15925, 6911, 85598, -75016, -16214, -62726, -39016, 8618, -63882, -4299, 23182, 49959, 49342, -3238, -24913, -37138, 78361, 32451, 6337, -11438, -36241, -37737, 8169, -3077, -24829, 57953, 53016, -31511, -91168, 12599, -41849, 41576, 55275, -62539, 47814, -62319, 12300, -32076, -55137, -84881, -27546, 4312, -3433, -54382, 113288, -30157, 74469, 18219, 79880, -2124, 98911, 17655, -33499, -32861, 47242, -37393, 99765, 14831, -44483, 10800, -31617, -52710, 37406, 22105, 29704, -20050, 13778, 43683, 36628, 8494, 60964, -22644, 31550, -17693, 33805, -124879, -12302, 19343, 20400, -30937, -21574, -34037, -33380, 56539, -24993, -75513, -1527, 53563, 65407, -101, 53577, 37991, 18717, -23795, -8090, -47987, -94717, 41967, 5170, -14815, -94311, 17896, -17734, -57718, -774, -38410, 24830, 29682, 76480, 58802, -46416, -20348, -61353, -68225, -68306, 23822, -31598, 42972, 36327, 28968, -65638, -21638, 24354, -8356, 26777, 52982, -11783, -44051, -26467, -44721, -28435, -53265, -25574, -2669, 44155, 22946, -18454, -30718, -11252, 58420, 8711, 67447, 4425, 41749, 67543, 43162, 11793, -41907, 20477, -13080, 6559, -6104, -13244, 42853, 42935, 29793, 36730, -28087, 28657, 17946, 7503, 7204, 21491, -27450, -24241, -98156, -18082, -42613, -24928, 10775, -14842, -44127, 55910, 14777, 31151, -2194, 39206, -2100, -4211, 11827, -8918, -19471, 72567, 36447, -65590, -34861, -17147, -45303, 9025, -7333, -35473, 11101, 11638, 3441, 6626, -41800, 9416, 13679, 33508, 40502, -60542, 16358, 8392, -43242, -35864, -34127, -48721, 35878, 30598, 28630, 20279, -19983, -14638, -24455, -1851, -11344, 45150, 42051, 26034, -28889, -32382, -3527, -14532, 22564, -22346, 477, 11706, 28338, -25972, -9185, -22867, -12522, 32120, -4424, 11339, -33913, -7184, 5101, -23552, -17115, -31401, -6104, 21906, 25708, 8406, 6317, -7525, 5014, 20750, 20179, 22724, 11692, 13297, 2493, -253, -16841, -17339, -6753, -4808, 2976, -10881, -10228, -13816, -12686, 1385, 2316, 2190, -875, -1924], ZZ) assert dup_mul(p1, p2, ZZ) == res p1 = dup_normal([83, -61, -86, -24, 12, 43, -88, -9, 42, 55, -66, 74, 95, -25, -12, 68, -99, 4, 45, 6, -15, -19, 78, 65, -55, 47, -13, 17, 86, 81, -58, -27, 50, -40, -24, 39, -41, -92, 75, 90, -1, 40, -15, -27, -35, 68, 70, -64, -40, 78, -88, -58, -39, 69, 46, 12, 28, -94, -37, -50, -80, -96, -61, 25, 1, 71, 4, 12, 48, 4, 34, -47, -75, 5, 48, 82, 88, 23, 98, 35, 17, -10, 48, -61, -95, 47, 65, -19, -66, -57, -6, -51, -42, -89, 66, -13, 18, 37, 90, -23, 72, 96, -53, 0, 40, -73, -52, -68, 32, -25, -53, 79, -52, 18, 44, 73, -81, 31, -90, 70, 3, 36, 48, 76, -24, -44, 23, 98, -4, 73, 69, 88, -70, 14, -68, 94, -78, -15, -64, -97, -70, -35, 65, 88, 49, -53, -7, 12, -45, -7, 59, -94, 99, -2, 67, -60, -71, 29, -62, -77, 1, 51, 17, 80, -20, -47, -19, 24, -9, 39, -23, 21, -84, 10, 84, 56, -17, -21, -66, 85, 70, 46, -51, -22, -95, 78, -60, -96, -97, -45, 72, 35, 30, -61, -92, -93, -60, -61, 4, -4, -81, -73, 46, 53, -11, 26, 94, 45, 14, -78, 55, 84, -68, 98, 60, 23, 100, -63, 68, 96, -16, 3, 56, 21, -58, 62, -67, 66, 85, 41, -79, -22, 97, -67, 82, 82, -96, -20, -7, 48, -67, 48, -9, -39, 78], ZZ) p2 = dup_normal([52, 88, 76, 66, 9, -64, 46, -20, -28, 69, 60, 96, -36, -92, -30, -11, -35, 35, 55, 63, -92, -7, 25, -58, 74, 55, -6, 4, 47, -92, -65, 67, -45, 74, -76, 59, -6, 69, 39, 24, -71, -7, 39, -45, 60, -68, 98, 97, -79, 17, 4, 94, -64, 68, -100, -96, -2, 3, 22, 96, 54, -77, -86, 67, 6, 57, 37, 40, 89, -78, 64, -94, -45, -92, 57, 87, -26, 36, 19, 97, 25, 77, -87, 24, 43, -5, 35, 57, 83, 71, 35, 63, 61, 96, -22, 8, -1, 96, 43, 45, 94, -93, 36, 71, -41, -99, 85, -48, 59, 52, -17, 5, 87, -16, -68, -54, 76, -18, 100, 91, -42, -70, -66, -88, -12, 1, 95, -82, 52, 43, -29, 3, 12, 72, -99, -43, -32, -93, -51, 16, -20, -12, -11, 5, 33, -38, 93, -5, -74, 25, 74, -58, 93, 59, -63, -86, 63, -20, -4, -74, -73, -95, 29, -28, 93, -91, -2, -38, -62, 77, -58, -85, -28, 95, 38, 19, -69, 86, 94, 25, -2, -4, 47, 34, -59, 35, -48, 29, -63, -53, 34, 29, 66, 73, 6, 92, -84, 89, 15, 81, 93, 97, 51, -72, -78, 25, 60, 90, -45, 39, 67, -84, -62, 57, 26, -32, -56, -14, -83, 76, 5, -2, 99, -100, 28, 46, 94, -7, 53, -25, 16, -23, -36, 89, -78, -63, 31, 1, 84, -99, -52, 76, 48, 90, -76, 44, -19, 54, -36, -9, -73, -100, -69, 31, 42, 25, -39, 76, -26, -8, -14, 51, 3, 37, 45, 2, -54, 13, -34, -92, 17, -25, -65, 53, -63, 30, 4, -70, -67, 90, 52, 51, 18, -3, 31, -45, -9, 59, 63, -87, 22, -32, 29, -38, 21, 36, -82, 27, -11], ZZ) res = dup_normal([4316, 4132, -3532, -7974, -11303, -10069, 5484, -3330, -5874, 7734, 4673, 11327, -9884, -8031, 17343, 21035, -10570, -9285, 15893, 3780, -14083, 8819, 17592, 10159, 7174, -11587, 8598, -16479, 3602, 25596, 9781, 12163, 150, 18749, -21782, -12307, 27578, -2757, -12573, 12565, 6345, -18956, 19503, -15617, 1443, -16778, 36851, 23588, -28474, 5749, 40695, -7521, -53669, -2497, -18530, 6770, 57038, 3926, -6927, -15399, 1848, -64649, -27728, 3644, 49608, 15187, -8902, -9480, -7398, -40425, 4824, 23767, -7594, -6905, 33089, 18786, 12192, 24670, 31114, 35334, -4501, -14676, 7107, -59018, -21352, 20777, 19661, 20653, 33754, -885, -43758, 6269, 51897, -28719, -97488, -9527, 13746, 11644, 17644, -21720, 23782, -10481, 47867, 20752, 33810, -1875, 39918, -7710, -40840, 19808, -47075, 23066, 46616, 25201, 9287, 35436, -1602, 9645, -11978, 13273, 15544, 33465, 20063, 44539, 11687, 27314, -6538, -37467, 14031, 32970, -27086, 41323, 29551, 65910, -39027, -37800, -22232, 8212, 46316, -28981, -55282, 50417, -44929, -44062, 73879, 37573, -2596, -10877, -21893, -133218, -33707, -25753, -9531, 17530, 61126, 2748, -56235, 43874, -10872, -90459, -30387, 115267, -7264, -44452, 122626, 14839, -599, 10337, 57166, -67467, -54957, 63669, 1202, 18488, 52594, 7205, -97822, 612, 78069, -5403, -63562, 47236, 36873, -154827, -26188, 82427, -39521, 5628, 7416, 5276, -53095, 47050, 26121, -42207, 79021, -13035, 2499, -66943, 29040, -72355, -23480, 23416, -12885, -44225, -42688, -4224, 19858, 55299, 15735, 11465, 101876, -39169, 51786, 14723, 43280, -68697, 16410, 92295, 56767, 7183, 111850, 4550, 115451, -38443, -19642, -35058, 10230, 93829, 8925, 63047, 3146, 29250, 8530, 5255, -98117, -115517, -76817, -8724, 41044, 1312, -35974, 79333, -28567, 7547, -10580, -24559, -16238, 10794, -3867, 24848, 57770, -51536, -35040, 71033, 29853, 62029, -7125, -125585, -32169, -47907, 156811, -65176, -58006, -15757, -57861, 11963, 30225, -41901, -41681, 31310, 27982, 18613, 61760, 60746, -59096, 33499, 30097, -17997, 24032, 56442, -83042, 23747, -20931, -21978, -158752, -9883, -73598, -7987, -7333, -125403, -116329, 30585, 53281, 51018, -29193, 88575, 8264, -40147, -16289, 113088, 12810, -6508, 101552, -13037, 34440, -41840, 101643, 24263, 80532, 61748, 65574, 6423, -20672, 6591, -10834, -71716, 86919, -92626, 39161, 28490, 81319, 46676, 106720, 43530, 26998, 57456, -8862, 60989, 13982, 3119, -2224, 14743, 55415, -49093, -29303, 28999, 1789, 55953, -84043, -7780, -65013, 57129, -47251, 61484, 61994, -78361, -82778, 22487, -26894, 9756, -74637, -15519, -4360, 30115, 42433, 35475, 15286, 69768, 21509, -20214, 78675, -21163, 13596, 11443, -10698, -53621, -53867, -24155, 64500, -42784, -33077, -16500, 873, -52788, 14546, -38011, 36974, -39849, -34029, -94311, 83068, -50437, -26169, -46746, 59185, 42259, -101379, -12943, 30089, -59086, 36271, 22723, -30253, -52472, -70826, -23289, 3331, -31687, 14183, -857, -28627, 35246, -51284, 5636, -6933, 66539, 36654, 50927, 24783, 3457, 33276, 45281, 45650, -4938, -9968, -22590, 47995, 69229, 5214, -58365, -17907, -14651, 18668, 18009, 12649, -11851, -13387, 20339, 52472, -1087, -21458, -68647, 52295, 15849, 40608, 15323, 25164, -29368, 10352, -7055, 7159, 21695, -5373, -54849, 101103, -24963, -10511, 33227, 7659, 41042, -69588, 26718, -20515, 6441, 38135, -63, 24088, -35364, -12785, -18709, 47843, 48533, -48575, 17251, -19394, 32878, -9010, -9050, 504, -12407, 28076, -3429, 25324, -4210, -26119, 752, -29203, 28251, -11324, -32140, -3366, -25135, 18702, -31588, -7047, -24267, 49987, -14975, -33169, 37744, -7720, -9035, 16964, -2807, -421, 14114, -17097, -13662, 40628, -12139, -9427, 5369, 17551, -13232, -16211, 9804, -7422, 2677, 28635, -8280, -4906, 2908, -22558, 5604, 12459, 8756, -3980, -4745, -18525, 7913, 5970, -16457, 20230, -6247, -13812, 2505, 11899, 1409, -15094, 22540, -18863, 137, 11123, -4516, 2290, -8594, 12150, -10380, 3005, 5235, -7350, 2535, -858], ZZ) assert dup_mul(p1, p2, ZZ) == res def test_dmp_mul(): assert dmp_mul([ZZ(5)], [ZZ(7)], 0, ZZ) == \ dup_mul([ZZ(5)], [ZZ(7)], ZZ) assert dmp_mul([QQ(5, 7)], [QQ(3, 7)], 0, QQ) == \ dup_mul([QQ(5, 7)], [QQ(3, 7)], QQ) assert dmp_mul([[[]]], [[[]]], 2, ZZ) == [[[]]] assert dmp_mul([[[ZZ(1)]]], [[[]]], 2, ZZ) == [[[]]] assert dmp_mul([[[]]], [[[ZZ(1)]]], 2, ZZ) == [[[]]] assert dmp_mul([[[ZZ(2)]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(2)]]] assert dmp_mul([[[ZZ(1)]]], [[[ZZ(2)]]], 2, ZZ) == [[[ZZ(2)]]] assert dmp_mul([[[]]], [[[]]], 2, QQ) == [[[]]] assert dmp_mul([[[QQ(1, 2)]]], [[[]]], 2, QQ) == [[[]]] assert dmp_mul([[[]]], [[[QQ(1, 2)]]], 2, QQ) == [[[]]] assert dmp_mul([[[QQ(2, 7)]]], [[[QQ(1, 3)]]], 2, QQ) == [[[QQ(2, 21)]]] assert dmp_mul([[[QQ(1, 7)]]], [[[QQ(2, 3)]]], 2, QQ) == [[[QQ(2, 21)]]] K = FF(6) assert dmp_mul( [[K(2)], [K(1)]], [[K(3)], [K(4)]], 1, K) == [[K(5)], [K(4)]] def test_dup_sqr(): assert dup_sqr([], ZZ) == [] assert dup_sqr([ZZ(2)], ZZ) == [ZZ(4)] assert dup_sqr([ZZ(1), ZZ(2)], ZZ) == [ZZ(1), ZZ(4), ZZ(4)] assert dup_sqr([], QQ) == [] assert dup_sqr([QQ(2, 3)], QQ) == [QQ(4, 9)] assert dup_sqr([QQ(1, 3), QQ(2, 3)], QQ) == [QQ(1, 9), QQ(4, 9), QQ(4, 9)] f = dup_normal([2, 0, 0, 1, 7], ZZ) assert dup_sqr(f, ZZ) == dup_normal([4, 0, 0, 4, 28, 0, 1, 14, 49], ZZ) K = FF(9) assert dup_sqr([K(3), K(4)], K) == [K(6), K(7)] def test_dmp_sqr(): assert dmp_sqr([ZZ(1), ZZ(2)], 0, ZZ) == \ dup_sqr([ZZ(1), ZZ(2)], ZZ) assert dmp_sqr([[[]]], 2, ZZ) == [[[]]] assert dmp_sqr([[[ZZ(2)]]], 2, ZZ) == [[[ZZ(4)]]] assert dmp_sqr([[[]]], 2, QQ) == [[[]]] assert dmp_sqr([[[QQ(2, 3)]]], 2, QQ) == [[[QQ(4, 9)]]] K = FF(9) assert dmp_sqr([[K(3)], [K(4)]], 1, K) == [[K(6)], [K(7)]] def test_dup_pow(): assert dup_pow([], 0, ZZ) == [ZZ(1)] assert dup_pow([], 0, QQ) == [QQ(1)] assert dup_pow([], 1, ZZ) == [] assert dup_pow([], 7, ZZ) == [] assert dup_pow([ZZ(1)], 0, ZZ) == [ZZ(1)] assert dup_pow([ZZ(1)], 1, ZZ) == [ZZ(1)] assert dup_pow([ZZ(1)], 7, ZZ) == [ZZ(1)] assert dup_pow([ZZ(3)], 0, ZZ) == [ZZ(1)] assert dup_pow([ZZ(3)], 1, ZZ) == [ZZ(3)] assert dup_pow([ZZ(3)], 7, ZZ) == [ZZ(2187)] assert dup_pow([QQ(1, 1)], 0, QQ) == [QQ(1, 1)] assert dup_pow([QQ(1, 1)], 1, QQ) == [QQ(1, 1)] assert dup_pow([QQ(1, 1)], 7, QQ) == [QQ(1, 1)] assert dup_pow([QQ(3, 7)], 0, QQ) == [QQ(1, 1)] assert dup_pow([QQ(3, 7)], 1, QQ) == [QQ(3, 7)] assert dup_pow([QQ(3, 7)], 7, QQ) == [QQ(2187, 823543)] f = dup_normal([2, 0, 0, 1, 7], ZZ) assert dup_pow(f, 0, ZZ) == dup_normal([1], ZZ) assert dup_pow(f, 1, ZZ) == dup_normal([2, 0, 0, 1, 7], ZZ) assert dup_pow(f, 2, ZZ) == dup_normal([4, 0, 0, 4, 28, 0, 1, 14, 49], ZZ) assert dup_pow(f, 3, ZZ) == dup_normal( [8, 0, 0, 12, 84, 0, 6, 84, 294, 1, 21, 147, 343], ZZ) def test_dmp_pow(): assert dmp_pow([[]], 0, 1, ZZ) == [[ZZ(1)]] assert dmp_pow([[]], 0, 1, QQ) == [[QQ(1)]] assert dmp_pow([[]], 1, 1, ZZ) == [[]] assert dmp_pow([[]], 7, 1, ZZ) == [[]] assert dmp_pow([[ZZ(1)]], 0, 1, ZZ) == [[ZZ(1)]] assert dmp_pow([[ZZ(1)]], 1, 1, ZZ) == [[ZZ(1)]] assert dmp_pow([[ZZ(1)]], 7, 1, ZZ) == [[ZZ(1)]] assert dmp_pow([[QQ(3, 7)]], 0, 1, QQ) == [[QQ(1, 1)]] assert dmp_pow([[QQ(3, 7)]], 1, 1, QQ) == [[QQ(3, 7)]] assert dmp_pow([[QQ(3, 7)]], 7, 1, QQ) == [[QQ(2187, 823543)]] f = dup_normal([2, 0, 0, 1, 7], ZZ) assert dmp_pow(f, 2, 0, ZZ) == dup_pow(f, 2, ZZ) def test_dup_pdiv(): f = dup_normal([3, 1, 1, 5], ZZ) g = dup_normal([5, -3, 1], ZZ) q = dup_normal([15, 14], ZZ) r = dup_normal([52, 111], ZZ) assert dup_pdiv(f, g, ZZ) == (q, r) assert dup_pquo(f, g, ZZ) == q assert dup_prem(f, g, ZZ) == r raises(ExactQuotientFailed, lambda: dup_pexquo(f, g, ZZ)) f = dup_normal([3, 1, 1, 5], QQ) g = dup_normal([5, -3, 1], QQ) q = dup_normal([15, 14], QQ) r = dup_normal([52, 111], QQ) assert dup_pdiv(f, g, QQ) == (q, r) assert dup_pquo(f, g, QQ) == q assert dup_prem(f, g, QQ) == r raises(ExactQuotientFailed, lambda: dup_pexquo(f, g, QQ)) def test_dmp_pdiv(): f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ) g = dmp_normal([[1], [-1, 0]], 1, ZZ) q = dmp_normal([[1], [1, 0]], 1, ZZ) r = dmp_normal([[2, 0, 0]], 1, ZZ) assert dmp_pdiv(f, g, 1, ZZ) == (q, r) assert dmp_pquo(f, g, 1, ZZ) == q assert dmp_prem(f, g, 1, ZZ) == r raises(ExactQuotientFailed, lambda: dmp_pexquo(f, g, 1, ZZ)) f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ) g = dmp_normal([[2], [-2, 0]], 1, ZZ) q = dmp_normal([[2], [2, 0]], 1, ZZ) r = dmp_normal([[8, 0, 0]], 1, ZZ) assert dmp_pdiv(f, g, 1, ZZ) == (q, r) assert dmp_pquo(f, g, 1, ZZ) == q assert dmp_prem(f, g, 1, ZZ) == r raises(ExactQuotientFailed, lambda: dmp_pexquo(f, g, 1, ZZ)) def test_dup_rr_div(): raises(ZeroDivisionError, lambda: dup_rr_div([1, 2, 3], [], ZZ)) f = dup_normal([3, 1, 1, 5], ZZ) g = dup_normal([5, -3, 1], ZZ) q, r = [], f assert dup_rr_div(f, g, ZZ) == (q, r) def test_dmp_rr_div(): raises(ZeroDivisionError, lambda: dmp_rr_div([[1, 2], [3]], [[]], 1, ZZ)) f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ) g = dmp_normal([[1], [-1, 0]], 1, ZZ) q = dmp_normal([[1], [1, 0]], 1, ZZ) r = dmp_normal([[2, 0, 0]], 1, ZZ) assert dmp_rr_div(f, g, 1, ZZ) == (q, r) f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ) g = dmp_normal([[-1], [1, 0]], 1, ZZ) q = dmp_normal([[-1], [-1, 0]], 1, ZZ) r = dmp_normal([[2, 0, 0]], 1, ZZ) assert dmp_rr_div(f, g, 1, ZZ) == (q, r) f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ) g = dmp_normal([[2], [-2, 0]], 1, ZZ) q, r = [[]], f assert dmp_rr_div(f, g, 1, ZZ) == (q, r) def test_dup_ff_div(): raises(ZeroDivisionError, lambda: dup_ff_div([1, 2, 3], [], QQ)) f = dup_normal([3, 1, 1, 5], QQ) g = dup_normal([5, -3, 1], QQ) q = [QQ(3, 5), QQ(14, 25)] r = [QQ(52, 25), QQ(111, 25)] assert dup_ff_div(f, g, QQ) == (q, r) def test_dmp_ff_div(): raises(ZeroDivisionError, lambda: dmp_ff_div([[1, 2], [3]], [[]], 1, QQ)) f = dmp_normal([[1], [], [1, 0, 0]], 1, QQ) g = dmp_normal([[1], [-1, 0]], 1, QQ) q = [[QQ(1, 1)], [QQ(1, 1), QQ(0, 1)]] r = [[QQ(2, 1), QQ(0, 1), QQ(0, 1)]] assert dmp_ff_div(f, g, 1, QQ) == (q, r) f = dmp_normal([[1], [], [1, 0, 0]], 1, QQ) g = dmp_normal([[-1], [1, 0]], 1, QQ) q = [[QQ(-1, 1)], [QQ(-1, 1), QQ(0, 1)]] r = [[QQ(2, 1), QQ(0, 1), QQ(0, 1)]] assert dmp_ff_div(f, g, 1, QQ) == (q, r) f = dmp_normal([[1], [], [1, 0, 0]], 1, QQ) g = dmp_normal([[2], [-2, 0]], 1, QQ) q = [[QQ(1, 2)], [QQ(1, 2), QQ(0, 1)]] r = [[QQ(2, 1), QQ(0, 1), QQ(0, 1)]] assert dmp_ff_div(f, g, 1, QQ) == (q, r) def test_dup_div(): f, g, q, r = [5, 4, 3, 2, 1], [1, 2, 3], [5, -6, 0], [20, 1] assert dup_div(f, g, ZZ) == (q, r) assert dup_quo(f, g, ZZ) == q assert dup_rem(f, g, ZZ) == r raises(ExactQuotientFailed, lambda: dup_exquo(f, g, ZZ)) f, g, q, r = [5, 4, 3, 2, 1, 0], [1, 2, 0, 0, 9], [5, -6], [15, 2, -44, 54] assert dup_div(f, g, ZZ) == (q, r) assert dup_quo(f, g, ZZ) == q assert dup_rem(f, g, ZZ) == r raises(ExactQuotientFailed, lambda: dup_exquo(f, g, ZZ)) def test_dmp_div(): f, g, q, r = [5, 4, 3, 2, 1], [1, 2, 3], [5, -6, 0], [20, 1] assert dmp_div(f, g, 0, ZZ) == (q, r) assert dmp_quo(f, g, 0, ZZ) == q assert dmp_rem(f, g, 0, ZZ) == r raises(ExactQuotientFailed, lambda: dmp_exquo(f, g, 0, ZZ)) f, g, q, r = [[[1]]], [[[2]], [1]], [[[]]], [[[1]]] assert dmp_div(f, g, 2, ZZ) == (q, r) assert dmp_quo(f, g, 2, ZZ) == q assert dmp_rem(f, g, 2, ZZ) == r raises(ExactQuotientFailed, lambda: dmp_exquo(f, g, 2, ZZ)) def test_dup_max_norm(): assert dup_max_norm([], ZZ) == 0 assert dup_max_norm([1], ZZ) == 1 assert dup_max_norm([1, 4, 2, 3], ZZ) == 4 def test_dmp_max_norm(): assert dmp_max_norm([[[]]], 2, ZZ) == 0 assert dmp_max_norm([[[1]]], 2, ZZ) == 1 assert dmp_max_norm(f_0, 2, ZZ) == 6 def test_dup_l1_norm(): assert dup_l1_norm([], ZZ) == 0 assert dup_l1_norm([1], ZZ) == 1 assert dup_l1_norm([1, 4, 2, 3], ZZ) == 10 def test_dmp_l1_norm(): assert dmp_l1_norm([[[]]], 2, ZZ) == 0 assert dmp_l1_norm([[[1]]], 2, ZZ) == 1 assert dmp_l1_norm(f_0, 2, ZZ) == 31 def test_dup_expand(): assert dup_expand((), ZZ) == [1] assert dup_expand(([1, 2, 3], [1, 2], [7, 5, 4, 3]), ZZ) == \ dup_mul([1, 2, 3], dup_mul([1, 2], [7, 5, 4, 3], ZZ), ZZ) def test_dmp_expand(): assert dmp_expand((), 1, ZZ) == [[1]] assert dmp_expand(([[1], [2], [3]], [[1], [2]], [[7], [5], [4], [3]]), 1, ZZ) == \ dmp_mul([[1], [2], [3]], dmp_mul([[1], [2]], [[7], [5], [ 4], [3]], 1, ZZ), 1, ZZ) sympy-0.7.4.1/sympy/polys/tests/test_polyutils.py0000644000175000017500000002246312253362407022421 0ustar georgeskgeorgesk"""Tests for useful utilities for higher level polynomial classes. """ from sympy import S, I, Integer, sin, cos, sqrt, symbols, pi, Eq, Integral from sympy.utilities.pytest import raises from sympy.polys.polyutils import ( _sort_gens, _unify_gens, _analyze_gens, _sort_factors, _parallel_dict_from_expr_if_gens, _parallel_dict_from_expr_no_gens, _dict_from_expr_if_gens, _dict_from_expr_no_gens, parallel_dict_from_expr, dict_from_expr, expr_from_dict, ) from sympy.polys.polyerrors import ( GeneratorsNeeded, PolynomialError, ) from sympy.polys.domains import ZZ, QQ, EX x, y, z, p, q, r, s, t, u, v, w = symbols('x,y,z,p,q,r,s,t,u,v,w') A, B = symbols('A,B', commutative=False) def test__sort_gens(): assert _sort_gens([]) == () assert _sort_gens([x]) == (x,) assert _sort_gens([p]) == (p,) assert _sort_gens([q]) == (q,) assert _sort_gens([x, p]) == (x, p) assert _sort_gens([p, x]) == (x, p) assert _sort_gens([q, p]) == (p, q) assert _sort_gens([q, p, x]) == (x, p, q) assert _sort_gens([x, p, q], wrt=x) == (x, p, q) assert _sort_gens([x, p, q], wrt=p) == (p, x, q) assert _sort_gens([x, p, q], wrt=q) == (q, x, p) assert _sort_gens([x, p, q], wrt='x') == (x, p, q) assert _sort_gens([x, p, q], wrt='p') == (p, x, q) assert _sort_gens([x, p, q], wrt='q') == (q, x, p) assert _sort_gens([x, p, q], wrt='x,q') == (x, q, p) assert _sort_gens([x, p, q], wrt='q,x') == (q, x, p) assert _sort_gens([x, p, q], wrt='p,q') == (p, q, x) assert _sort_gens([x, p, q], wrt='q,p') == (q, p, x) assert _sort_gens([x, p, q], wrt='x, q') == (x, q, p) assert _sort_gens([x, p, q], wrt='q, x') == (q, x, p) assert _sort_gens([x, p, q], wrt='p, q') == (p, q, x) assert _sort_gens([x, p, q], wrt='q, p') == (q, p, x) assert _sort_gens([x, p, q], wrt=[x, 'q']) == (x, q, p) assert _sort_gens([x, p, q], wrt=[q, 'x']) == (q, x, p) assert _sort_gens([x, p, q], wrt=[p, 'q']) == (p, q, x) assert _sort_gens([x, p, q], wrt=[q, 'p']) == (q, p, x) assert _sort_gens([x, p, q], wrt=['x', 'q']) == (x, q, p) assert _sort_gens([x, p, q], wrt=['q', 'x']) == (q, x, p) assert _sort_gens([x, p, q], wrt=['p', 'q']) == (p, q, x) assert _sort_gens([x, p, q], wrt=['q', 'p']) == (q, p, x) assert _sort_gens([x, p, q], sort='x > p > q') == (x, p, q) assert _sort_gens([x, p, q], sort='p > x > q') == (p, x, q) assert _sort_gens([x, p, q], sort='p > q > x') == (p, q, x) assert _sort_gens([x, p, q], wrt='x', sort='q > p') == (x, q, p) assert _sort_gens([x, p, q], wrt='p', sort='q > x') == (p, q, x) assert _sort_gens([x, p, q], wrt='q', sort='p > x') == (q, p, x) X = symbols('x0,x1,x2,x10,x11,x12,x20,x21,x22') assert _sort_gens(X) == X def test__unify_gens(): assert _unify_gens([], []) == () assert _unify_gens([x], [x]) == (x,) assert _unify_gens([y], [y]) == (y,) assert _unify_gens([x, y], [x]) == (x, y) assert _unify_gens([x], [x, y]) == (x, y) assert _unify_gens([x, y], [x, y]) == (x, y) assert _unify_gens([y, x], [y, x]) == (y, x) assert _unify_gens([x], [y]) == (x, y) assert _unify_gens([y], [x]) == (y, x) assert _unify_gens([x], [y, x]) == (y, x) assert _unify_gens([y, x], [x]) == (y, x) assert _unify_gens([x, y, z], [x, y, z]) == (x, y, z) assert _unify_gens([z, y, x], [x, y, z]) == (z, y, x) assert _unify_gens([x, y, z], [z, y, x]) == (x, y, z) assert _unify_gens([z, y, x], [z, y, x]) == (z, y, x) assert _unify_gens([x, y, z], [t, x, p, q, z]) == (t, x, y, p, q, z) def test__analyze_gens(): assert _analyze_gens((x, y, z)) == (x, y, z) assert _analyze_gens([x, y, z]) == (x, y, z) assert _analyze_gens(([x, y, z],)) == (x, y, z) assert _analyze_gens(((x, y, z),)) == (x, y, z) def test__sort_factors(): assert _sort_factors([], multiple=True) == [] assert _sort_factors([], multiple=False) == [] F = [[1, 2, 3], [1, 2], [1]] G = [[1], [1, 2], [1, 2, 3]] assert _sort_factors(F, multiple=False) == G F = [[1, 2], [1, 2, 3], [1, 2], [1]] G = [[1], [1, 2], [1, 2], [1, 2, 3]] assert _sort_factors(F, multiple=False) == G F = [[2, 2], [1, 2, 3], [1, 2], [1]] G = [[1], [1, 2], [2, 2], [1, 2, 3]] assert _sort_factors(F, multiple=False) == G F = [([1, 2, 3], 1), ([1, 2], 1), ([1], 1)] G = [([1], 1), ([1, 2], 1), ([1, 2, 3], 1)] assert _sort_factors(F, multiple=True) == G F = [([1, 2], 1), ([1, 2, 3], 1), ([1, 2], 1), ([1], 1)] G = [([1], 1), ([1, 2], 1), ([1, 2], 1), ([1, 2, 3], 1)] assert _sort_factors(F, multiple=True) == G F = [([2, 2], 1), ([1, 2, 3], 1), ([1, 2], 1), ([1], 1)] G = [([1], 1), ([1, 2], 1), ([2, 2], 1), ([1, 2, 3], 1)] assert _sort_factors(F, multiple=True) == G F = [([2, 2], 1), ([1, 2, 3], 1), ([1, 2], 2), ([1], 1)] G = [([1], 1), ([2, 2], 1), ([1, 2], 2), ([1, 2, 3], 1)] assert _sort_factors(F, multiple=True) == G def test__dict_from_expr_if_gens(): assert dict_from_expr( Integer(17), gens=(x,)) == ({(0,): Integer(17)}, (x,)) assert dict_from_expr( Integer(17), gens=(x, y)) == ({(0, 0): Integer(17)}, (x, y)) assert dict_from_expr( Integer(17), gens=(x, y, z)) == ({(0, 0, 0): Integer(17)}, (x, y, z)) assert dict_from_expr( Integer(-17), gens=(x,)) == ({(0,): Integer(-17)}, (x,)) assert dict_from_expr( Integer(-17), gens=(x, y)) == ({(0, 0): Integer(-17)}, (x, y)) assert dict_from_expr(Integer( -17), gens=(x, y, z)) == ({(0, 0, 0): Integer(-17)}, (x, y, z)) assert dict_from_expr( Integer(17)*x, gens=(x,)) == ({(1,): Integer(17)}, (x,)) assert dict_from_expr( Integer(17)*x, gens=(x, y)) == ({(1, 0): Integer(17)}, (x, y)) assert dict_from_expr(Integer( 17)*x, gens=(x, y, z)) == ({(1, 0, 0): Integer(17)}, (x, y, z)) assert dict_from_expr( Integer(17)*x**7, gens=(x,)) == ({(7,): Integer(17)}, (x,)) assert dict_from_expr( Integer(17)*x**7*y, gens=(x, y)) == ({(7, 1): Integer(17)}, (x, y)) assert dict_from_expr(Integer(17)*x**7*y*z**12, gens=( x, y, z)) == ({(7, 1, 12): Integer(17)}, (x, y, z)) assert dict_from_expr(x + 2*y + 3*z, gens=(x,)) == \ ({(1,): Integer(1), (0,): 2*y + 3*z}, (x,)) assert dict_from_expr(x + 2*y + 3*z, gens=(x, y)) == \ ({(1, 0): Integer(1), (0, 1): Integer(2), (0, 0): 3*z}, (x, y)) assert dict_from_expr(x + 2*y + 3*z, gens=(x, y, z)) == \ ({(1, 0, 0): Integer( 1), (0, 1, 0): Integer(2), (0, 0, 1): Integer(3)}, (x, y, z)) assert dict_from_expr(x*y + 2*x*z + 3*y*z, gens=(x,)) == \ ({(1,): y + 2*z, (0,): 3*y*z}, (x,)) assert dict_from_expr(x*y + 2*x*z + 3*y*z, gens=(x, y)) == \ ({(1, 1): Integer(1), (1, 0): 2*z, (0, 1): 3*z}, (x, y)) assert dict_from_expr(x*y + 2*x*z + 3*y*z, gens=(x, y, z)) == \ ({(1, 1, 0): Integer( 1), (1, 0, 1): Integer(2), (0, 1, 1): Integer(3)}, (x, y, z)) assert dict_from_expr(2**y*x, gens=(x,)) == ({(1,): 2**y}, (x,)) assert dict_from_expr(Integral(x, (x, 1, 2)) + x) == ( {(0, 1): 1, (1, 0): 1}, (x, Integral(x, (x, 1, 2)))) raises(PolynomialError, lambda: dict_from_expr(2**y*x, gens=(x, y))) def test__dict_from_expr_no_gens(): raises(GeneratorsNeeded, lambda: dict_from_expr(Integer(17))) assert dict_from_expr(x) == ({(1,): Integer(1)}, (x,)) assert dict_from_expr(y) == ({(1,): Integer(1)}, (y,)) assert dict_from_expr(x*y) == ({(1, 1): Integer(1)}, (x, y)) assert dict_from_expr( x + y) == ({(1, 0): Integer(1), (0, 1): Integer(1)}, (x, y)) assert dict_from_expr(sqrt(2)) == ({(1,): Integer(1)}, (sqrt(2),)) raises(GeneratorsNeeded, lambda: dict_from_expr(sqrt(2), greedy=False)) assert dict_from_expr(x*y, domain=ZZ[x]) == ({(1,): x}, (y,)) assert dict_from_expr(x*y, domain=ZZ[y]) == ({(1,): y}, (x,)) assert dict_from_expr(3*sqrt( 2)*pi*x*y, extension=None) == ({(1, 1, 1, 1): 3}, (x, y, pi, sqrt(2))) assert dict_from_expr(3*sqrt( 2)*pi*x*y, extension=True) == ({(1, 1, 1): 3*sqrt(2)}, (x, y, pi)) assert dict_from_expr(3*sqrt( 2)*pi*x*y, extension=True) == ({(1, 1, 1): 3*sqrt(2)}, (x, y, pi)) f = cos(x)*sin(x) + cos(x)*sin(y) + cos(y)*sin(x) + cos(y)*sin(y) assert dict_from_expr(f) == ({(0, 1, 0, 1): 1, (0, 1, 1, 0): 1, (1, 0, 0, 1): 1, (1, 0, 1, 0): 1}, (cos(x), cos(y), sin(x), sin(y))) def test__parallel_dict_from_expr_if_gens(): assert parallel_dict_from_expr([x + 2*y + 3*z, Integer(7)], gens=(x,)) == \ ([{(1,): Integer(1), (0,): 2*y + 3*z}, {(0,): Integer(7)}], (x,)) def test__parallel_dict_from_expr_no_gens(): assert parallel_dict_from_expr([x*y, Integer(3)]) == \ ([{(1, 1): Integer(1)}, {(0, 0): Integer(3)}], (x, y)) assert parallel_dict_from_expr([x*y, 2*z, Integer(3)]) == \ ([{(1, 1, 0): Integer( 1)}, {(0, 0, 1): Integer(2)}, {(0, 0, 0): Integer(3)}], (x, y, z)) def test_parallel_dict_from_expr(): parallel_dict_from_expr([Eq(x, 1), Eq( x**2, 2)]) == ([{(1,): Integer(1)}, {(2,): Integer(2)}], (x,)) raises(PolynomialError, lambda: parallel_dict_from_expr([A*B - B*A])) def test_dict_from_expr(): dict_from_expr(Eq(x, 1)) == ({(1,): Integer(1)}, (x,)) raises(PolynomialError, lambda: dict_from_expr(A*B - B*A)) sympy-0.7.4.1/sympy/polys/tests/test_galoistools.py0000644000175000017500000006643212253362407022720 0ustar georgeskgeorgesk from sympy.polys.galoistools import ( gf_crt, gf_crt1, gf_crt2, gf_int, gf_degree, gf_strip, gf_trunc, gf_normal, gf_from_dict, gf_to_dict, gf_from_int_poly, gf_to_int_poly, gf_neg, gf_add_ground, gf_sub_ground, gf_mul_ground, gf_quo_ground, gf_add, gf_sub, gf_add_mul, gf_sub_mul, gf_mul, gf_sqr, gf_div, gf_rem, gf_quo, gf_exquo, gf_lshift, gf_rshift, gf_expand, gf_pow, gf_pow_mod, gf_gcdex, gf_gcd, gf_lcm, gf_cofactors, gf_LC, gf_TC, gf_monic, gf_eval, gf_multi_eval, gf_compose, gf_compose_mod, gf_trace_map, gf_diff, gf_random, gf_irreducible, gf_irreducible_p, gf_irred_p_ben_or, gf_irred_p_rabin, gf_sqf_list, gf_sqf_part, gf_sqf_p, gf_Qmatrix, gf_Qbasis, gf_ddf_zassenhaus, gf_ddf_shoup, gf_edf_zassenhaus, gf_edf_shoup, gf_berlekamp, gf_zassenhaus, gf_shoup, gf_factor_sqf, gf_factor, gf_value, linear_congruence, csolve_prime, gf_csolve, gf_frobenius_map, gf_frobenius_monomial_base ) from sympy.polys.polyerrors import ( ExactQuotientFailed, ) from sympy.polys import polyconfig as config from sympy.polys.domains import ZZ from sympy import pi, nextprime from sympy.utilities.pytest import raises def test_gf_crt(): U = [49, 76, 65] M = [99, 97, 95] p = 912285 u = 639985 assert gf_crt(U, M, ZZ) == u E = [9215, 9405, 9603] S = [62, 24, 12] assert gf_crt1(M, ZZ) == (p, E, S) assert gf_crt2(U, M, p, E, S, ZZ) == u def test_gf_int(): assert gf_int(0, 5) == 0 assert gf_int(1, 5) == 1 assert gf_int(2, 5) == 2 assert gf_int(3, 5) == -2 assert gf_int(4, 5) == -1 assert gf_int(5, 5) == 0 def test_gf_degree(): assert gf_degree([]) == -1 assert gf_degree([1]) == 0 assert gf_degree([1, 0]) == 1 assert gf_degree([1, 0, 0, 0, 1]) == 4 def test_gf_strip(): assert gf_strip([]) == [] assert gf_strip([0]) == [] assert gf_strip([0, 0, 0]) == [] assert gf_strip([1]) == [1] assert gf_strip([0, 1]) == [1] assert gf_strip([0, 0, 0, 1]) == [1] assert gf_strip([1, 2, 0]) == [1, 2, 0] assert gf_strip([0, 1, 2, 0]) == [1, 2, 0] assert gf_strip([0, 0, 0, 1, 2, 0]) == [1, 2, 0] def test_gf_trunc(): assert gf_trunc([], 11) == [] assert gf_trunc([1], 11) == [1] assert gf_trunc([22], 11) == [] assert gf_trunc([12], 11) == [1] assert gf_trunc([11, 22, 17, 1, 0], 11) == [6, 1, 0] assert gf_trunc([12, 23, 17, 1, 0], 11) == [1, 1, 6, 1, 0] def test_gf_normal(): assert gf_normal([11, 22, 17, 1, 0], 11, ZZ) == [6, 1, 0] def test_gf_from_to_dict(): f = {11: 12, 6: 2, 0: 25} F = {11: 1, 6: 2, 0: 3} g = [1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 3] assert gf_from_dict(f, 11, ZZ) == g assert gf_to_dict(g, 11) == F f = {11: -5, 4: 0, 3: 1, 0: 12} F = {11: -5, 3: 1, 0: 1} g = [6, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1] assert gf_from_dict(f, 11, ZZ) == g assert gf_to_dict(g, 11) == F assert gf_to_dict([10], 11, symmetric=True) == {0: -1} assert gf_to_dict([10], 11, symmetric=False) == {0: 10} def test_gf_from_to_int_poly(): assert gf_from_int_poly([1, 0, 7, 2, 20], 5) == [1, 0, 2, 2, 0] assert gf_to_int_poly([1, 0, 4, 2, 3], 5) == [1, 0, -1, 2, -2] assert gf_to_int_poly([10], 11, symmetric=True) == [-1] assert gf_to_int_poly([10], 11, symmetric=False) == [10] def test_gf_LC(): assert gf_LC([], ZZ) == 0 assert gf_LC([1], ZZ) == 1 assert gf_LC([1, 2], ZZ) == 1 def test_gf_TC(): assert gf_TC([], ZZ) == 0 assert gf_TC([1], ZZ) == 1 assert gf_TC([1, 2], ZZ) == 2 def test_gf_monic(): assert gf_monic(ZZ.map([]), 11, ZZ) == (0, []) assert gf_monic(ZZ.map([1]), 11, ZZ) == (1, [1]) assert gf_monic(ZZ.map([2]), 11, ZZ) == (2, [1]) assert gf_monic(ZZ.map([1, 2, 3, 4]), 11, ZZ) == (1, [1, 2, 3, 4]) assert gf_monic(ZZ.map([2, 3, 4, 5]), 11, ZZ) == (2, [1, 7, 2, 8]) def test_gf_arith(): assert gf_neg([], 11, ZZ) == [] assert gf_neg([1], 11, ZZ) == [10] assert gf_neg([1, 2, 3], 11, ZZ) == [10, 9, 8] assert gf_add_ground([], 0, 11, ZZ) == [] assert gf_sub_ground([], 0, 11, ZZ) == [] assert gf_add_ground([], 3, 11, ZZ) == [3] assert gf_sub_ground([], 3, 11, ZZ) == [8] assert gf_add_ground([1], 3, 11, ZZ) == [4] assert gf_sub_ground([1], 3, 11, ZZ) == [9] assert gf_add_ground([8], 3, 11, ZZ) == [] assert gf_sub_ground([3], 3, 11, ZZ) == [] assert gf_add_ground([1, 2, 3], 3, 11, ZZ) == [1, 2, 6] assert gf_sub_ground([1, 2, 3], 3, 11, ZZ) == [1, 2, 0] assert gf_mul_ground([], 0, 11, ZZ) == [] assert gf_mul_ground([], 1, 11, ZZ) == [] assert gf_mul_ground([1], 0, 11, ZZ) == [] assert gf_mul_ground([1], 1, 11, ZZ) == [1] assert gf_mul_ground([1, 2, 3], 0, 11, ZZ) == [] assert gf_mul_ground([1, 2, 3], 1, 11, ZZ) == [1, 2, 3] assert gf_mul_ground([1, 2, 3], 7, 11, ZZ) == [7, 3, 10] assert gf_add([], [], 11, ZZ) == [] assert gf_add([1], [], 11, ZZ) == [1] assert gf_add([], [1], 11, ZZ) == [1] assert gf_add([1], [1], 11, ZZ) == [2] assert gf_add([1], [2], 11, ZZ) == [3] assert gf_add([1, 2], [1], 11, ZZ) == [1, 3] assert gf_add([1], [1, 2], 11, ZZ) == [1, 3] assert gf_add([1, 2, 3], [8, 9, 10], 11, ZZ) == [9, 0, 2] assert gf_sub([], [], 11, ZZ) == [] assert gf_sub([1], [], 11, ZZ) == [1] assert gf_sub([], [1], 11, ZZ) == [10] assert gf_sub([1], [1], 11, ZZ) == [] assert gf_sub([1], [2], 11, ZZ) == [10] assert gf_sub([1, 2], [1], 11, ZZ) == [1, 1] assert gf_sub([1], [1, 2], 11, ZZ) == [10, 10] assert gf_sub([3, 2, 1], [8, 9, 10], 11, ZZ) == [6, 4, 2] assert gf_add_mul( [1, 5, 6], [7, 3], [8, 0, 6, 1], 11, ZZ) == [1, 2, 10, 8, 9] assert gf_sub_mul( [1, 5, 6], [7, 3], [8, 0, 6, 1], 11, ZZ) == [10, 9, 3, 2, 3] assert gf_mul([], [], 11, ZZ) == [] assert gf_mul([], [1], 11, ZZ) == [] assert gf_mul([1], [], 11, ZZ) == [] assert gf_mul([1], [1], 11, ZZ) == [1] assert gf_mul([5], [7], 11, ZZ) == [2] assert gf_mul([3, 0, 0, 6, 1, 2], [4, 0, 1, 0], 11, ZZ) == [1, 0, 3, 2, 4, 3, 1, 2, 0] assert gf_mul([4, 0, 1, 0], [3, 0, 0, 6, 1, 2], 11, ZZ) == [1, 0, 3, 2, 4, 3, 1, 2, 0] assert gf_mul([2, 0, 0, 1, 7], [2, 0, 0, 1, 7], 11, ZZ) == [4, 0, 0, 4, 6, 0, 1, 3, 5] assert gf_sqr([], 11, ZZ) == [] assert gf_sqr([2], 11, ZZ) == [4] assert gf_sqr([1, 2], 11, ZZ) == [1, 4, 4] assert gf_sqr([2, 0, 0, 1, 7], 11, ZZ) == [4, 0, 0, 4, 6, 0, 1, 3, 5] def test_gf_division(): raises(ZeroDivisionError, lambda: gf_div([1, 2, 3], [], 11, ZZ)) raises(ZeroDivisionError, lambda: gf_rem([1, 2, 3], [], 11, ZZ)) raises(ZeroDivisionError, lambda: gf_quo([1, 2, 3], [], 11, ZZ)) raises(ZeroDivisionError, lambda: gf_quo([1, 2, 3], [], 11, ZZ)) assert gf_div([1], [1, 2, 3], 7, ZZ) == ([], [1]) assert gf_rem([1], [1, 2, 3], 7, ZZ) == [1] assert gf_quo([1], [1, 2, 3], 7, ZZ) == [] f = ZZ.map([5, 4, 3, 2, 1, 0]) g = ZZ.map([1, 2, 3]) q = [5, 1, 0, 6] r = [3, 3] assert gf_div(f, g, 7, ZZ) == (q, r) assert gf_rem(f, g, 7, ZZ) == r assert gf_quo(f, g, 7, ZZ) == q raises(ExactQuotientFailed, lambda: gf_exquo(f, g, 7, ZZ)) f = ZZ.map([5, 4, 3, 2, 1, 0]) g = ZZ.map([1, 2, 3, 0]) q = [5, 1, 0] r = [6, 1, 0] assert gf_div(f, g, 7, ZZ) == (q, r) assert gf_rem(f, g, 7, ZZ) == r assert gf_quo(f, g, 7, ZZ) == q raises(ExactQuotientFailed, lambda: gf_exquo(f, g, 7, ZZ)) assert gf_quo(ZZ.map([1, 2, 1]), ZZ.map([1, 1]), 11, ZZ) == [1, 1] def test_gf_shift(): f = [1, 2, 3, 4, 5] assert gf_lshift([], 5, ZZ) == [] assert gf_rshift([], 5, ZZ) == ([], []) assert gf_lshift(f, 1, ZZ) == [1, 2, 3, 4, 5, 0] assert gf_lshift(f, 2, ZZ) == [1, 2, 3, 4, 5, 0, 0] assert gf_rshift(f, 0, ZZ) == (f, []) assert gf_rshift(f, 1, ZZ) == ([1, 2, 3, 4], [5]) assert gf_rshift(f, 3, ZZ) == ([1, 2], [3, 4, 5]) assert gf_rshift(f, 5, ZZ) == ([], f) def test_gf_expand(): F = [([1, 1], 2), ([1, 2], 3)] assert gf_expand(F, 11, ZZ) == [1, 8, 3, 5, 6, 8] assert gf_expand((4, F), 11, ZZ) == [4, 10, 1, 9, 2, 10] def test_gf_powering(): assert gf_pow([1, 0, 0, 1, 8], 0, 11, ZZ) == [1] assert gf_pow([1, 0, 0, 1, 8], 1, 11, ZZ) == [1, 0, 0, 1, 8] assert gf_pow([1, 0, 0, 1, 8], 2, 11, ZZ) == [1, 0, 0, 2, 5, 0, 1, 5, 9] assert gf_pow([1, 0, 0, 1, 8], 5, 11, ZZ) == \ [1, 0, 0, 5, 7, 0, 10, 6, 2, 10, 9, 6, 10, 6, 6, 0, 5, 2, 5, 9, 10] assert gf_pow([1, 0, 0, 1, 8], 8, 11, ZZ) == \ [1, 0, 0, 8, 9, 0, 6, 8, 10, 1, 2, 5, 10, 7, 7, 9, 1, 2, 0, 0, 6, 2, 5, 2, 5, 7, 7, 9, 10, 10, 7, 5, 5] assert gf_pow([1, 0, 0, 1, 8], 45, 11, ZZ) == \ [ 1, 0, 0, 1, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 4, 10, 0, 0, 0, 0, 0, 0, 10, 0, 0, 10, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 6, 4, 0, 0, 0, 0, 0, 0, 8, 0, 0, 8, 9, 0, 0, 0, 0, 0, 0, 10, 0, 0, 10, 3, 0, 0, 0, 0, 0, 0, 4, 0, 0, 4, 10, 0, 0, 0, 0, 0, 0, 8, 0, 0, 8, 9, 0, 0, 0, 0, 0, 0, 9, 0, 0, 9, 6, 0, 0, 0, 0, 0, 0, 3, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0, 10, 0, 0, 10, 3, 0, 0, 0, 0, 0, 0, 10, 0, 0, 10, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 5, 0, 0, 0, 0, 0, 0, 4, 0, 0, 4, 10] assert gf_pow_mod(ZZ.map([1, 0, 0, 1, 8]), 0, ZZ.map([2, 0, 7]), 11, ZZ) == [1] assert gf_pow_mod(ZZ.map([1, 0, 0, 1, 8]), 1, ZZ.map([2, 0, 7]), 11, ZZ) == [1, 1] assert gf_pow_mod(ZZ.map([1, 0, 0, 1, 8]), 2, ZZ.map([2, 0, 7]), 11, ZZ) == [2, 3] assert gf_pow_mod(ZZ.map([1, 0, 0, 1, 8]), 5, ZZ.map([2, 0, 7]), 11, ZZ) == [7, 8] assert gf_pow_mod(ZZ.map([1, 0, 0, 1, 8]), 8, ZZ.map([2, 0, 7]), 11, ZZ) == [1, 5] assert gf_pow_mod(ZZ.map([1, 0, 0, 1, 8]), 45, ZZ.map([2, 0, 7]), 11, ZZ) == [5, 4] def test_gf_gcdex(): assert gf_gcdex(ZZ.map([]), ZZ.map([]), 11, ZZ) == ([1], [], []) assert gf_gcdex(ZZ.map([2]), ZZ.map([]), 11, ZZ) == ([6], [], [1]) assert gf_gcdex(ZZ.map([]), ZZ.map([2]), 11, ZZ) == ([], [6], [1]) assert gf_gcdex(ZZ.map([2]), ZZ.map([2]), 11, ZZ) == ([], [6], [1]) assert gf_gcdex(ZZ.map([]), ZZ.map([3, 0]), 11, ZZ) == ([], [4], [1, 0]) assert gf_gcdex(ZZ.map([3, 0]), ZZ.map([]), 11, ZZ) == ([4], [], [1, 0]) assert gf_gcdex(ZZ.map([3, 0]), ZZ.map([3, 0]), 11, ZZ) == ([], [4], [1, 0]) assert gf_gcdex(ZZ.map([1, 8, 7]), ZZ.map([1, 7, 1, 7]), 11, ZZ) == ([5, 6], [6], [1, 7]) def test_gf_gcd(): assert gf_gcd(ZZ.map([]), ZZ.map([]), 11, ZZ) == [] assert gf_gcd(ZZ.map([2]), ZZ.map([]), 11, ZZ) == [1] assert gf_gcd(ZZ.map([]), ZZ.map([2]), 11, ZZ) == [1] assert gf_gcd(ZZ.map([2]), ZZ.map([2]), 11, ZZ) == [1] assert gf_gcd(ZZ.map([]), ZZ.map([1, 0]), 11, ZZ) == [1, 0] assert gf_gcd(ZZ.map([1, 0]), ZZ.map([]), 11, ZZ) == [1, 0] assert gf_gcd(ZZ.map([3, 0]), ZZ.map([3, 0]), 11, ZZ) == [1, 0] assert gf_gcd(ZZ.map([1, 8, 7]), ZZ.map([1, 7, 1, 7]), 11, ZZ) == [1, 7] def test_gf_lcm(): assert gf_lcm(ZZ.map([]), ZZ.map([]), 11, ZZ) == [] assert gf_lcm(ZZ.map([2]), ZZ.map([]), 11, ZZ) == [] assert gf_lcm(ZZ.map([]), ZZ.map([2]), 11, ZZ) == [] assert gf_lcm(ZZ.map([2]), ZZ.map([2]), 11, ZZ) == [1] assert gf_lcm(ZZ.map([]), ZZ.map([1, 0]), 11, ZZ) == [] assert gf_lcm(ZZ.map([1, 0]), ZZ.map([]), 11, ZZ) == [] assert gf_lcm(ZZ.map([3, 0]), ZZ.map([3, 0]), 11, ZZ) == [1, 0] assert gf_lcm(ZZ.map([1, 8, 7]), ZZ.map([1, 7, 1, 7]), 11, ZZ) == [1, 8, 8, 8, 7] def test_gf_cofactors(): assert gf_cofactors(ZZ.map([]), ZZ.map([]), 11, ZZ) == ([], [], []) assert gf_cofactors(ZZ.map([2]), ZZ.map([]), 11, ZZ) == ([1], [2], []) assert gf_cofactors(ZZ.map([]), ZZ.map([2]), 11, ZZ) == ([1], [], [2]) assert gf_cofactors(ZZ.map([2]), ZZ.map([2]), 11, ZZ) == ([1], [2], [2]) assert gf_cofactors(ZZ.map([]), ZZ.map([1, 0]), 11, ZZ) == ([1, 0], [], [1]) assert gf_cofactors(ZZ.map([1, 0]), ZZ.map([]), 11, ZZ) == ([1, 0], [1], []) assert gf_cofactors(ZZ.map([3, 0]), ZZ.map([3, 0]), 11, ZZ) == ( [1, 0], [3], [3]) assert gf_cofactors(ZZ.map([1, 8, 7]), ZZ.map([1, 7, 1, 7]), 11, ZZ) == ( ([1, 7], [1, 1], [1, 0, 1])) def test_gf_diff(): assert gf_diff([], 11, ZZ) == [] assert gf_diff([7], 11, ZZ) == [] assert gf_diff([7, 3], 11, ZZ) == [7] assert gf_diff([7, 3, 1], 11, ZZ) == [3, 3] assert gf_diff([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 11, ZZ) == [] def test_gf_eval(): assert gf_eval([], 4, 11, ZZ) == 0 assert gf_eval([], 27, 11, ZZ) == 0 assert gf_eval([7], 4, 11, ZZ) == 7 assert gf_eval([7], 27, 11, ZZ) == 7 assert gf_eval([1, 0, 3, 2, 4, 3, 1, 2, 0], 0, 11, ZZ) == 0 assert gf_eval([1, 0, 3, 2, 4, 3, 1, 2, 0], 4, 11, ZZ) == 9 assert gf_eval([1, 0, 3, 2, 4, 3, 1, 2, 0], 27, 11, ZZ) == 5 assert gf_eval([4, 0, 0, 4, 6, 0, 1, 3, 5], 0, 11, ZZ) == 5 assert gf_eval([4, 0, 0, 4, 6, 0, 1, 3, 5], 4, 11, ZZ) == 3 assert gf_eval([4, 0, 0, 4, 6, 0, 1, 3, 5], 27, 11, ZZ) == 9 assert gf_multi_eval([3, 2, 1], [0, 1, 2, 3], 11, ZZ) == [1, 6, 6, 1] def test_gf_compose(): assert gf_compose([], [1, 0], 11, ZZ) == [] assert gf_compose_mod([], [1, 0], [1, 0], 11, ZZ) == [] assert gf_compose([1], [], 11, ZZ) == [1] assert gf_compose([1, 0], [], 11, ZZ) == [] assert gf_compose([1, 0], [1, 0], 11, ZZ) == [1, 0] f = ZZ.map([1, 1, 4, 9, 1]) g = ZZ.map([1, 1, 1]) h = ZZ.map([1, 0, 0, 2]) assert gf_compose(g, h, 11, ZZ) == [1, 0, 0, 5, 0, 0, 7] assert gf_compose_mod(g, h, f, 11, ZZ) == [3, 9, 6, 10] def test_gf_trace_map(): f = ZZ.map([1, 1, 4, 9, 1]) a = [1, 1, 1] c = ZZ.map([1, 0]) b = gf_pow_mod(c, 11, f, 11, ZZ) assert gf_trace_map(a, b, c, 0, f, 11, ZZ) == \ ([1, 1, 1], [1, 1, 1]) assert gf_trace_map(a, b, c, 1, f, 11, ZZ) == \ ([5, 2, 10, 3], [5, 3, 0, 4]) assert gf_trace_map(a, b, c, 2, f, 11, ZZ) == \ ([5, 9, 5, 3], [10, 1, 5, 7]) assert gf_trace_map(a, b, c, 3, f, 11, ZZ) == \ ([1, 10, 6, 0], [7]) assert gf_trace_map(a, b, c, 4, f, 11, ZZ) == \ ([1, 1, 1], [1, 1, 8]) assert gf_trace_map(a, b, c, 5, f, 11, ZZ) == \ ([5, 2, 10, 3], [5, 3, 0, 0]) assert gf_trace_map(a, b, c, 11, f, 11, ZZ) == \ ([1, 10, 6, 0], [10]) def test_gf_irreducible(): assert gf_irreducible_p(gf_irreducible(1, 11, ZZ), 11, ZZ) is True assert gf_irreducible_p(gf_irreducible(2, 11, ZZ), 11, ZZ) is True assert gf_irreducible_p(gf_irreducible(3, 11, ZZ), 11, ZZ) is True assert gf_irreducible_p(gf_irreducible(4, 11, ZZ), 11, ZZ) is True assert gf_irreducible_p(gf_irreducible(5, 11, ZZ), 11, ZZ) is True assert gf_irreducible_p(gf_irreducible(6, 11, ZZ), 11, ZZ) is True assert gf_irreducible_p(gf_irreducible(7, 11, ZZ), 11, ZZ) is True def test_gf_irreducible_p(): assert gf_irred_p_ben_or(ZZ.map([7]), 11, ZZ) is True assert gf_irred_p_ben_or(ZZ.map([7, 3]), 11, ZZ) is True assert gf_irred_p_ben_or(ZZ.map([7, 3, 1]), 11, ZZ) is False assert gf_irred_p_rabin(ZZ.map([7]), 11, ZZ) is True assert gf_irred_p_rabin(ZZ.map([7, 3]), 11, ZZ) is True assert gf_irred_p_rabin(ZZ.map([7, 3, 1]), 11, ZZ) is False config.setup('GF_IRRED_METHOD', 'ben-or') assert gf_irreducible_p(ZZ.map([7]), 11, ZZ) is True assert gf_irreducible_p(ZZ.map([7, 3]), 11, ZZ) is True assert gf_irreducible_p(ZZ.map([7, 3, 1]), 11, ZZ) is False config.setup('GF_IRRED_METHOD', 'rabin') assert gf_irreducible_p(ZZ.map([7]), 11, ZZ) is True assert gf_irreducible_p(ZZ.map([7, 3]), 11, ZZ) is True assert gf_irreducible_p(ZZ.map([7, 3, 1]), 11, ZZ) is False config.setup('GF_IRRED_METHOD', 'other') raises(KeyError, lambda: gf_irreducible_p([7], 11, ZZ)) config.setup('GF_IRRED_METHOD') f = ZZ.map([1, 9, 9, 13, 16, 15, 6, 7, 7, 7, 10]) g = ZZ.map([1, 7, 16, 7, 15, 13, 13, 11, 16, 10, 9]) h = gf_mul(f, g, 17, ZZ) assert gf_irred_p_ben_or(f, 17, ZZ) is True assert gf_irred_p_ben_or(g, 17, ZZ) is True assert gf_irred_p_ben_or(h, 17, ZZ) is False assert gf_irred_p_rabin(f, 17, ZZ) is True assert gf_irred_p_rabin(g, 17, ZZ) is True assert gf_irred_p_rabin(h, 17, ZZ) is False def test_gf_squarefree(): assert gf_sqf_list([], 11, ZZ) == (0, []) assert gf_sqf_list([1], 11, ZZ) == (1, []) assert gf_sqf_list([1, 1], 11, ZZ) == (1, [([1, 1], 1)]) assert gf_sqf_p([], 11, ZZ) is True assert gf_sqf_p([1], 11, ZZ) is True assert gf_sqf_p([1, 1], 11, ZZ) is True f = gf_from_dict({11: 1, 0: 1}, 11, ZZ) assert gf_sqf_p(f, 11, ZZ) is False assert gf_sqf_list(f, 11, ZZ) == \ (1, [([1, 1], 11)]) f = [1, 5, 8, 4] assert gf_sqf_p(f, 11, ZZ) is False assert gf_sqf_list(f, 11, ZZ) == \ (1, [([1, 1], 1), ([1, 2], 2)]) assert gf_sqf_part(f, 11, ZZ) == [1, 3, 2] f = [1, 0, 0, 2, 0, 0, 2, 0, 0, 1, 0] assert gf_sqf_list(f, 3, ZZ) == \ (1, [([1, 0], 1), ([1, 1], 3), ([1, 2], 6)]) def test_gf_frobenius_map(): f = ZZ.map([2, 0, 1, 0, 2, 2, 0, 2, 2, 2]) g = ZZ.map([1,1,0,2,0,1,0,2,0,1]) p = 3 n = 4 b = gf_frobenius_monomial_base(g, p, ZZ) h = gf_frobenius_map(f, g, b, p, ZZ) h1 = gf_pow_mod(f, p, g, p, ZZ) assert h == h1 def test_gf_berlekamp(): f = gf_from_int_poly([1, -3, 1, -3, -1, -3, 1], 11) Q = [[1, 0, 0, 0, 0, 0], [3, 5, 8, 8, 6, 5], [3, 6, 6, 1, 10, 0], [9, 4, 10, 3, 7, 9], [7, 8, 10, 0, 0, 8], [8, 10, 7, 8, 10, 8]] V = [[1, 0, 0, 0, 0, 0], [0, 1, 1, 1, 1, 0], [0, 0, 7, 9, 0, 1]] assert gf_Qmatrix(f, 11, ZZ) == Q assert gf_Qbasis(Q, 11, ZZ) == V assert gf_berlekamp(f, 11, ZZ) == \ [[1, 1], [1, 5, 3], [1, 2, 3, 4]] f = ZZ.map([1, 0, 1, 0, 10, 10, 8, 2, 8]) Q = ZZ.map([[1, 0, 0, 0, 0, 0, 0, 0], [2, 1, 7, 11, 10, 12, 5, 11], [3, 6, 4, 3, 0, 4, 7, 2], [4, 3, 6, 5, 1, 6, 2, 3], [2, 11, 8, 8, 3, 1, 3, 11], [6, 11, 8, 6, 2, 7, 10, 9], [5, 11, 7, 10, 0, 11, 7, 12], [3, 3, 12, 5, 0, 11, 9, 12]]) V = [[1, 0, 0, 0, 0, 0, 0, 0], [0, 5, 5, 0, 9, 5, 1, 0], [0, 9, 11, 9, 10, 12, 0, 1]] assert gf_Qmatrix(f, 13, ZZ) == Q assert gf_Qbasis(Q, 13, ZZ) == V assert gf_berlekamp(f, 13, ZZ) == \ [[1, 3], [1, 8, 4, 12], [1, 2, 3, 4, 6]] def test_gf_ddf(): f = gf_from_dict({15: ZZ(1), 0: ZZ(-1)}, 11, ZZ) g = [([1, 0, 0, 0, 0, 10], 1), ([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2)] assert gf_ddf_zassenhaus(f, 11, ZZ) == g assert gf_ddf_shoup(f, 11, ZZ) == g f = gf_from_dict({63: ZZ(1), 0: ZZ(1)}, 2, ZZ) g = [([1, 1], 1), ([1, 1, 1], 2), ([1, 1, 1, 1, 1, 1, 1], 3), ([1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1], 6)] assert gf_ddf_zassenhaus(f, 2, ZZ) == g assert gf_ddf_shoup(f, 2, ZZ) == g f = gf_from_dict({6: ZZ(1), 5: ZZ(-1), 4: ZZ(1), 3: ZZ(1), 1: ZZ(-1)}, 3, ZZ) g = [([1, 1, 0], 1), ([1, 1, 0, 1, 2], 2)] assert gf_ddf_zassenhaus(f, 3, ZZ) == g assert gf_ddf_shoup(f, 3, ZZ) == g f = ZZ.map([1, 2, 5, 26, 677, 436, 791, 325, 456, 24, 577]) g = [([1, 701], 1), ([1, 110, 559, 532, 694, 151, 110, 70, 735, 122], 9)] assert gf_ddf_zassenhaus(f, 809, ZZ) == g assert gf_ddf_shoup(f, 809, ZZ) == g p = ZZ(nextprime(int((2**15 * pi).evalf()))) f = gf_from_dict({15: 1, 1: 1, 0: 1}, p, ZZ) g = [([1, 22730, 68144], 2), ([1, 64876, 83977, 10787, 12561, 68608, 52650, 88001, 84356], 4), ([1, 15347, 95022, 84569, 94508, 92335], 5)] assert gf_ddf_zassenhaus(f, p, ZZ) == g assert gf_ddf_shoup(f, p, ZZ) == g def test_gf_edf(): f = ZZ.map([1, 1, 0, 1, 2]) g = ZZ.map([[1, 0, 1], [1, 1, 2]]) assert gf_edf_zassenhaus(f, 2, 3, ZZ) == g assert gf_edf_shoup(f, 2, 3, ZZ) == g def test_gf_factor(): assert gf_factor([], 11, ZZ) == (0, []) assert gf_factor([1], 11, ZZ) == (1, []) assert gf_factor([1, 1], 11, ZZ) == (1, [([1, 1], 1)]) assert gf_factor_sqf([], 11, ZZ) == (0, []) assert gf_factor_sqf([1], 11, ZZ) == (1, []) assert gf_factor_sqf([1, 1], 11, ZZ) == (1, [[1, 1]]) config.setup('GF_FACTOR_METHOD', 'berlekamp') assert gf_factor_sqf([], 11, ZZ) == (0, []) assert gf_factor_sqf([1], 11, ZZ) == (1, []) assert gf_factor_sqf([1, 1], 11, ZZ) == (1, [[1, 1]]) config.setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor_sqf([], 11, ZZ) == (0, []) assert gf_factor_sqf([1], 11, ZZ) == (1, []) assert gf_factor_sqf([1, 1], 11, ZZ) == (1, [[1, 1]]) config.setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor_sqf(ZZ.map([]), 11, ZZ) == (0, []) assert gf_factor_sqf(ZZ.map([1]), 11, ZZ) == (1, []) assert gf_factor_sqf(ZZ.map([1, 1]), 11, ZZ) == (1, [[1, 1]]) f, p = ZZ.map([1, 0, 0, 1, 0]), 2 g = (1, [([1, 0], 1), ([1, 1], 1), ([1, 1, 1], 1)]) config.setup('GF_FACTOR_METHOD', 'berlekamp') assert gf_factor(f, p, ZZ) == g config.setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor(f, p, ZZ) == g config.setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor(f, p, ZZ) == g g = (1, [[1, 0], [1, 1], [1, 1, 1]]) config.setup('GF_FACTOR_METHOD', 'berlekamp') assert gf_factor_sqf(f, p, ZZ) == g config.setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor_sqf(f, p, ZZ) == g config.setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor_sqf(f, p, ZZ) == g f, p = gf_from_int_poly([1, -3, 1, -3, -1, -3, 1], 11), 11 g = (1, [([1, 1], 1), ([1, 5, 3], 1), ([1, 2, 3, 4], 1)]) config.setup('GF_FACTOR_METHOD', 'berlekamp') assert gf_factor(f, p, ZZ) == g config.setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor(f, p, ZZ) == g config.setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor(f, p, ZZ) == g f, p = [1, 5, 8, 4], 11 g = (1, [([1, 1], 1), ([1, 2], 2)]) config.setup('GF_FACTOR_METHOD', 'berlekamp') assert gf_factor(f, p, ZZ) == g config.setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor(f, p, ZZ) == g config.setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor(f, p, ZZ) == g f, p = [1, 1, 10, 1, 0, 10, 10, 10, 0, 0], 11 g = (1, [([1, 0], 2), ([1, 9, 5], 1), ([1, 3, 0, 8, 5, 2], 1)]) config.setup('GF_FACTOR_METHOD', 'berlekamp') assert gf_factor(f, p, ZZ) == g config.setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor(f, p, ZZ) == g config.setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor(f, p, ZZ) == g f, p = gf_from_dict({32: 1, 0: 1}, 11, ZZ), 11 g = (1, [([1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 10], 1), ([1, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 10], 1)]) config.setup('GF_FACTOR_METHOD', 'berlekamp') assert gf_factor(f, p, ZZ) == g config.setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor(f, p, ZZ) == g config.setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor(f, p, ZZ) == g f, p = gf_from_dict({32: ZZ(8), 0: ZZ(5)}, 11, ZZ), 11 g = (8, [([1, 3], 1), ([1, 8], 1), ([1, 0, 9], 1), ([1, 2, 2], 1), ([1, 9, 2], 1), ([1, 0, 5, 0, 7], 1), ([1, 0, 6, 0, 7], 1), ([1, 0, 0, 0, 1, 0, 0, 0, 6], 1), ([1, 0, 0, 0, 10, 0, 0, 0, 6], 1)]) config.setup('GF_FACTOR_METHOD', 'berlekamp') assert gf_factor(f, p, ZZ) == g config.setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor(f, p, ZZ) == g config.setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor(f, p, ZZ) == g f, p = gf_from_dict({63: ZZ(8), 0: ZZ(5)}, 11, ZZ), 11 g = (8, [([1, 7], 1), ([1, 4, 5], 1), ([1, 6, 8, 2], 1), ([1, 9, 9, 2], 1), ([1, 0, 0, 9, 0, 0, 4], 1), ([1, 2, 0, 8, 4, 6, 4], 1), ([1, 2, 3, 8, 0, 6, 4], 1), ([1, 2, 6, 0, 8, 4, 4], 1), ([1, 3, 3, 1, 6, 8, 4], 1), ([1, 5, 6, 0, 8, 6, 4], 1), ([1, 6, 2, 7, 9, 8, 4], 1), ([1, 10, 4, 7, 10, 7, 4], 1), ([1, 10, 10, 1, 4, 9, 4], 1)]) config.setup('GF_FACTOR_METHOD', 'berlekamp') assert gf_factor(f, p, ZZ) == g config.setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor(f, p, ZZ) == g config.setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor(f, p, ZZ) == g # Gathen polynomials: x**n + x + 1 (mod p > 2**n * pi) p = ZZ(nextprime(int((2**15 * pi).evalf()))) f = gf_from_dict({15: 1, 1: 1, 0: 1}, p, ZZ) assert gf_sqf_p(f, p, ZZ) is True g = (1, [([1, 22730, 68144], 1), ([1, 81553, 77449, 86810, 4724], 1), ([1, 86276, 56779, 14859, 31575], 1), ([1, 15347, 95022, 84569, 94508, 92335], 1)]) config.setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor(f, p, ZZ) == g config.setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor(f, p, ZZ) == g g = (1, [[1, 22730, 68144], [1, 81553, 77449, 86810, 4724], [1, 86276, 56779, 14859, 31575], [1, 15347, 95022, 84569, 94508, 92335]]) config.setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor_sqf(f, p, ZZ) == g config.setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor_sqf(f, p, ZZ) == g # Shoup polynomials: f = a_0 x**n + a_1 x**(n-1) + ... + a_n # (mod p > 2**(n-2) * pi), where a_n = a_{n-1}**2 + 1, a_0 = 1 p = ZZ(nextprime(int((2**4 * pi).evalf()))) f = ZZ.map([1, 2, 5, 26, 41, 39, 38]) assert gf_sqf_p(f, p, ZZ) is True g = (1, [([1, 44, 26], 1), ([1, 11, 25, 18, 30], 1)]) config.setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor(f, p, ZZ) == g config.setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor(f, p, ZZ) == g g = (1, [[1, 44, 26], [1, 11, 25, 18, 30]]) config.setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor_sqf(f, p, ZZ) == g config.setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor_sqf(f, p, ZZ) == g config.setup('GF_FACTOR_METHOD', 'other') raises(KeyError, lambda: gf_factor([1, 1], 11, ZZ)) config.setup('GF_FACTOR_METHOD') def test_gf_csolve(): assert gf_value([1, 7, 2, 4], 11) == 2204 assert linear_congruence(4, 3, 5) == [2] assert linear_congruence(0, 3, 5) == [] assert linear_congruence(6, 1, 4) == [] assert linear_congruence(0, 5, 5) == [0, 1, 2, 3, 4] assert linear_congruence(3, 12, 15) == [4, 9, 14] assert linear_congruence(6, 0, 18) == [0, 3, 6, 9, 12, 15] # with power = 1 assert csolve_prime([1, 3, 2, 17], 7) == [3] assert csolve_prime([1, 3, 1, 5], 5) == [0, 1] assert csolve_prime([3, 6, 9, 3], 3) == [0, 1, 2] # with power > 1 assert csolve_prime( [1, 1, 223], 3, 4) == [4, 13, 22, 31, 40, 49, 58, 67, 76] assert csolve_prime([3, 5, 2, 25], 5, 3) == [16, 50, 99] assert csolve_prime([3, 2, 2, 49], 7, 3) == [147, 190, 234] assert gf_csolve([1, 1, 7], 189) == [13, 49, 76, 112, 139, 175] assert gf_csolve([1, 3, 4, 1, 30], 60) == [10, 30] assert gf_csolve([1, 1, 7], 15) == [] sympy-0.7.4.1/sympy/polys/tests/test_densetools.py0000644000175000017500000006001612253362407022530 0ustar georgeskgeorgesk"""Tests for dense recursive polynomials' tools. """ from sympy.polys.densebasic import ( dup_LC, dmp_LC, dup_normal, dmp_normal, dup_from_raw_dict, dmp_from_dict, dmp_convert, dmp_swap, dmp_one_p, ) from sympy.polys.densearith import ( dup_add, dup_mul, dup_exquo, dmp_neg, dmp_sub, dmp_mul_ground, dmp_mul, dmp_sqr, ) from sympy.polys.densetools import ( dup_clear_denoms, dmp_clear_denoms, dup_integrate, dmp_integrate, dmp_integrate_in, dup_diff, dmp_diff, dmp_diff_in, dup_eval, dmp_eval, dmp_eval_in, dmp_eval_tail, dmp_diff_eval_in, dup_trunc, dmp_trunc, dmp_ground_trunc, dup_monic, dmp_ground_monic, dup_content, dmp_ground_content, dup_primitive, dmp_ground_primitive, dup_extract, dmp_ground_extract, dup_real_imag, dup_mirror, dup_scale, dup_shift, dup_transform, dup_compose, dmp_compose, dup_decompose, dmp_lift, dup_sign_variations, dup_revert, dmp_revert, ) from sympy.polys.polyclasses import DMP, ANP from sympy.polys.polyerrors import ( MultivariatePolynomialError, ExactQuotientFailed, NotReversible, DomainError, ) from sympy.polys.specialpolys import ( f_polys, dmp_fateman_poly_F_1, dmp_fateman_poly_F_2, dmp_fateman_poly_F_3, ) from sympy.polys.domains import FF, ZZ, QQ, EX from sympy.polys.rings import ring from sympy import S, I, sin from sympy.core.compatibility import long from sympy.abc import x from sympy.utilities.pytest import raises f_0, f_1, f_2, f_3, f_4, f_5, f_6 = [ f.to_dense() for f in f_polys() ] def test_dup_integrate(): assert dup_integrate([], 1, QQ) == [] assert dup_integrate([], 2, QQ) == [] assert dup_integrate([QQ(1)], 1, QQ) == [QQ(1), QQ(0)] assert dup_integrate([QQ(1)], 2, QQ) == [QQ(1, 2), QQ(0), QQ(0)] assert dup_integrate([QQ(1), QQ(2), QQ(3)], 0, QQ) == \ [QQ(1), QQ(2), QQ(3)] assert dup_integrate([QQ(1), QQ(2), QQ(3)], 1, QQ) == \ [QQ(1, 3), QQ(1), QQ(3), QQ(0)] assert dup_integrate([QQ(1), QQ(2), QQ(3)], 2, QQ) == \ [QQ(1, 12), QQ(1, 3), QQ(3, 2), QQ(0), QQ(0)] assert dup_integrate([QQ(1), QQ(2), QQ(3)], 3, QQ) == \ [QQ(1, 60), QQ(1, 12), QQ(1, 2), QQ(0), QQ(0), QQ(0)] assert dup_integrate(dup_from_raw_dict({29: QQ(17)}, QQ), 3, QQ) == \ dup_from_raw_dict({32: QQ(17, 29760)}, QQ) assert dup_integrate(dup_from_raw_dict({29: QQ(17), 5: QQ(1, 2)}, QQ), 3, QQ) == \ dup_from_raw_dict({32: QQ(17, 29760), 8: QQ(1, 672)}, QQ) def test_dmp_integrate(): assert dmp_integrate([[[]]], 1, 2, QQ) == [[[]]] assert dmp_integrate([[[]]], 2, 2, QQ) == [[[]]] assert dmp_integrate([[[QQ(1)]]], 1, 2, QQ) == [[[QQ(1)]], [[]]] assert dmp_integrate([[[QQ(1)]]], 2, 2, QQ) == [[[QQ(1, 2)]], [[]], [[]]] assert dmp_integrate([[QQ(1)], [QQ(2)], [QQ(3)]], 0, 1, QQ) == \ [[QQ(1)], [QQ(2)], [QQ(3)]] assert dmp_integrate([[QQ(1)], [QQ(2)], [QQ(3)]], 1, 1, QQ) == \ [[QQ(1, 3)], [QQ(1)], [QQ(3)], []] assert dmp_integrate([[QQ(1)], [QQ(2)], [QQ(3)]], 2, 1, QQ) == \ [[QQ(1, 12)], [QQ(1, 3)], [QQ(3, 2)], [], []] assert dmp_integrate([[QQ(1)], [QQ(2)], [QQ(3)]], 3, 1, QQ) == \ [[QQ(1, 60)], [QQ(1, 12)], [QQ(1, 2)], [], [], []] def test_dmp_integrate_in(): f = dmp_convert(f_6, 3, ZZ, QQ) assert dmp_integrate_in(f, 2, 1, 3, QQ) == \ dmp_swap( dmp_integrate(dmp_swap(f, 0, 1, 3, QQ), 2, 3, QQ), 0, 1, 3, QQ) assert dmp_integrate_in(f, 3, 1, 3, QQ) == \ dmp_swap( dmp_integrate(dmp_swap(f, 0, 1, 3, QQ), 3, 3, QQ), 0, 1, 3, QQ) assert dmp_integrate_in(f, 2, 2, 3, QQ) == \ dmp_swap( dmp_integrate(dmp_swap(f, 0, 2, 3, QQ), 2, 3, QQ), 0, 2, 3, QQ) assert dmp_integrate_in(f, 3, 2, 3, QQ) == \ dmp_swap( dmp_integrate(dmp_swap(f, 0, 2, 3, QQ), 3, 3, QQ), 0, 2, 3, QQ) def test_dup_diff(): assert dup_diff([], 1, ZZ) == [] assert dup_diff([7], 1, ZZ) == [] assert dup_diff([2, 7], 1, ZZ) == [2] assert dup_diff([1, 2, 1], 1, ZZ) == [2, 2] assert dup_diff([1, 2, 3, 4], 1, ZZ) == [3, 4, 3] assert dup_diff([1, -1, 0, 0, 2], 1, ZZ) == [4, -3, 0, 0] f = dup_normal([17, 34, 56, -345, 23, 76, 0, 0, 12, 3, 7], ZZ) assert dup_diff(f, 0, ZZ) == f assert dup_diff(f, 1, ZZ) == dup_diff(f, 1, ZZ) assert dup_diff(f, 2, ZZ) == dup_diff(dup_diff(f, 1, ZZ), 1, ZZ) assert dup_diff( f, 3, ZZ) == dup_diff(dup_diff(dup_diff(f, 1, ZZ), 1, ZZ), 1, ZZ) K = FF(3) f = dup_normal([17, 34, 56, -345, 23, 76, 0, 0, 12, 3, 7], K) assert dup_diff(f, 1, K) == dup_normal([2, 0, 1, 0, 0, 2, 0, 0, 0, 0], K) assert dup_diff(f, 2, K) == dup_normal([1, 0, 0, 2, 0, 0, 0], K) assert dup_diff(f, 3, K) == dup_normal([], K) assert dup_diff(f, 0, K) == f assert dup_diff(f, 1, K) == dup_diff(f, 1, K) assert dup_diff(f, 2, K) == dup_diff(dup_diff(f, 1, K), 1, K) assert dup_diff( f, 3, K) == dup_diff(dup_diff(dup_diff(f, 1, K), 1, K), 1, K) def test_dmp_diff(): assert dmp_diff([], 1, 0, ZZ) == [] assert dmp_diff([[]], 1, 1, ZZ) == [[]] assert dmp_diff([[[]]], 1, 2, ZZ) == [[[]]] assert dmp_diff([[[1], [2]]], 1, 2, ZZ) == [[[]]] assert dmp_diff([[[1]], [[]]], 1, 2, ZZ) == [[[1]]] assert dmp_diff([[[3]], [[1]], [[]]], 1, 2, ZZ) == [[[6]], [[1]]] assert dmp_diff([1, -1, 0, 0, 2], 1, 0, ZZ) == \ dup_diff([1, -1, 0, 0, 2], 1, ZZ) assert dmp_diff(f_6, 0, 3, ZZ) == f_6 assert dmp_diff(f_6, 1, 3, ZZ) == dmp_diff(f_6, 1, 3, ZZ) assert dmp_diff( f_6, 2, 3, ZZ) == dmp_diff(dmp_diff(f_6, 1, 3, ZZ), 1, 3, ZZ) assert dmp_diff(f_6, 3, 3, ZZ) == dmp_diff( dmp_diff(dmp_diff(f_6, 1, 3, ZZ), 1, 3, ZZ), 1, 3, ZZ) K = FF(23) F_6 = dmp_normal(f_6, 3, K) assert dmp_diff(F_6, 0, 3, K) == F_6 assert dmp_diff(F_6, 1, 3, K) == dmp_diff(F_6, 1, 3, K) assert dmp_diff(F_6, 2, 3, K) == dmp_diff(dmp_diff(F_6, 1, 3, K), 1, 3, K) assert dmp_diff(F_6, 3, 3, K) == dmp_diff( dmp_diff(dmp_diff(F_6, 1, 3, K), 1, 3, K), 1, 3, K) def test_dmp_diff_in(): assert dmp_diff_in(f_6, 2, 1, 3, ZZ) == \ dmp_swap(dmp_diff(dmp_swap(f_6, 0, 1, 3, ZZ), 2, 3, ZZ), 0, 1, 3, ZZ) assert dmp_diff_in(f_6, 3, 1, 3, ZZ) == \ dmp_swap(dmp_diff(dmp_swap(f_6, 0, 1, 3, ZZ), 3, 3, ZZ), 0, 1, 3, ZZ) assert dmp_diff_in(f_6, 2, 2, 3, ZZ) == \ dmp_swap(dmp_diff(dmp_swap(f_6, 0, 2, 3, ZZ), 2, 3, ZZ), 0, 2, 3, ZZ) assert dmp_diff_in(f_6, 3, 2, 3, ZZ) == \ dmp_swap(dmp_diff(dmp_swap(f_6, 0, 2, 3, ZZ), 3, 3, ZZ), 0, 2, 3, ZZ) def test_dup_eval(): assert dup_eval([], 7, ZZ) == 0 assert dup_eval([1, 2], 0, ZZ) == 2 assert dup_eval([1, 2, 3], 7, ZZ) == 66 def test_dmp_eval(): assert dmp_eval([], 3, 0, ZZ) == 0 assert dmp_eval([[]], 3, 1, ZZ) == [] assert dmp_eval([[[]]], 3, 2, ZZ) == [[]] assert dmp_eval([[1, 2]], 0, 1, ZZ) == [1, 2] assert dmp_eval([[[1]]], 3, 2, ZZ) == [[1]] assert dmp_eval([[[1, 2]]], 3, 2, ZZ) == [[1, 2]] assert dmp_eval([[3, 2], [1, 2]], 3, 1, ZZ) == [10, 8] assert dmp_eval([[[3, 2]], [[1, 2]]], 3, 2, ZZ) == [[10, 8]] def test_dmp_eval_in(): assert dmp_eval_in( f_6, -2, 1, 3, ZZ) == dmp_eval(dmp_swap(f_6, 0, 1, 3, ZZ), -2, 3, ZZ) assert dmp_eval_in( f_6, 7, 1, 3, ZZ) == dmp_eval(dmp_swap(f_6, 0, 1, 3, ZZ), 7, 3, ZZ) assert dmp_eval_in(f_6, -2, 2, 3, ZZ) == dmp_swap( dmp_eval(dmp_swap(f_6, 0, 2, 3, ZZ), -2, 3, ZZ), 0, 1, 2, ZZ) assert dmp_eval_in(f_6, 7, 2, 3, ZZ) == dmp_swap( dmp_eval(dmp_swap(f_6, 0, 2, 3, ZZ), 7, 3, ZZ), 0, 1, 2, ZZ) f = [[[long(45)]], [[]], [[]], [[long(-9)], [-1], [], [long(3), long(0), long(10), long(0)]]] assert dmp_eval_in(f, -2, 2, 2, ZZ) == \ [[45], [], [], [-9, -1, 0, -44]] def test_dmp_eval_tail(): assert dmp_eval_tail([[]], [1], 1, ZZ) == [] assert dmp_eval_tail([[[]]], [1], 2, ZZ) == [[]] assert dmp_eval_tail([[[]]], [1, 2], 2, ZZ) == [] assert dmp_eval_tail(f_0, [], 2, ZZ) == f_0 assert dmp_eval_tail(f_0, [1, -17, 8], 2, ZZ) == 84496 assert dmp_eval_tail(f_0, [-17, 8], 2, ZZ) == [-1409, 3, 85902] assert dmp_eval_tail(f_0, [8], 2, ZZ) == [[83, 2], [3], [302, 81, 1]] assert dmp_eval_tail(f_1, [-17, 8], 2, ZZ) == [-136, 15699, 9166, -27144] assert dmp_eval_tail( f_2, [-12, 3], 2, ZZ) == [-1377, 0, -702, -1224, 0, -624] assert dmp_eval_tail( f_3, [-12, 3], 2, ZZ) == [144, 82, -5181, -28872, -14868, -540] assert dmp_eval_tail( f_4, [25, -1], 2, ZZ) == [152587890625, 9765625, -59605407714843750, -3839159765625, -1562475, 9536712644531250, 610349546750, -4, 24414375000, 1562520] assert dmp_eval_tail(f_5, [25, -1], 2, ZZ) == [-1, -78, -2028, -17576] assert dmp_eval_tail(f_6, [0, 2, 4], 3, ZZ) == [5040, 0, 0, 4480] def test_dmp_diff_eval_in(): assert dmp_diff_eval_in(f_6, 2, 7, 1, 3, ZZ) == \ dmp_eval(dmp_diff(dmp_swap(f_6, 0, 1, 3, ZZ), 2, 3, ZZ), 7, 3, ZZ) def test_dup_revert(): f = [-QQ(1, 720), QQ(0), QQ(1, 24), QQ(0), -QQ(1, 2), QQ(0), QQ(1)] g = [QQ(61, 720), QQ(0), QQ(5, 24), QQ(0), QQ(1, 2), QQ(0), QQ(1)] assert dup_revert(f, 8, QQ) == g raises(NotReversible, lambda: dup_revert([QQ(1), QQ(0)], 3, QQ)) def test_dmp_revert(): f = [-QQ(1, 720), QQ(0), QQ(1, 24), QQ(0), -QQ(1, 2), QQ(0), QQ(1)] g = [QQ(61, 720), QQ(0), QQ(5, 24), QQ(0), QQ(1, 2), QQ(0), QQ(1)] assert dmp_revert(f, 8, 0, QQ) == g raises(MultivariatePolynomialError, lambda: dmp_revert([[1]], 2, 1, QQ)) def test_dup_trunc(): assert dup_trunc([1, 2, 3, 4, 5, 6], ZZ(3), ZZ) == [1, -1, 0, 1, -1, 0] assert dup_trunc([6, 5, 4, 3, 2, 1], ZZ(3), ZZ) == [-1, 1, 0, -1, 1] def test_dmp_trunc(): assert dmp_trunc([[]], [1, 2], 2, ZZ) == [[]] assert dmp_trunc([[1, 2], [1, 4, 1], [1]], [1, 2], 1, ZZ) == [[-3], [1]] def test_dmp_ground_trunc(): assert dmp_ground_trunc(f_0, ZZ(3), 2, ZZ) == \ dmp_normal( [[[1, -1, 0], [-1]], [[]], [[1, -1, 0], [1, -1, 1], [1]]], 2, ZZ) def test_dup_monic(): assert dup_monic([3, 6, 9], ZZ) == [1, 2, 3] raises(ExactQuotientFailed, lambda: dup_monic([3, 4, 5], ZZ)) assert dup_monic([], QQ) == [] assert dup_monic([QQ(1)], QQ) == [QQ(1)] assert dup_monic([QQ(7), QQ(1), QQ(21)], QQ) == [QQ(1), QQ(1, 7), QQ(3)] def test_dmp_ground_monic(): assert dmp_ground_monic([[3], [6], [9]], 1, ZZ) == [[1], [2], [3]] raises( ExactQuotientFailed, lambda: dmp_ground_monic([[3], [4], [5]], 1, ZZ)) assert dmp_ground_monic([[]], 1, QQ) == [[]] assert dmp_ground_monic([[QQ(1)]], 1, QQ) == [[QQ(1)]] assert dmp_ground_monic( [[QQ(7)], [QQ(1)], [QQ(21)]], 1, QQ) == [[QQ(1)], [QQ(1, 7)], [QQ(3)]] def test_dup_content(): assert dup_content([], ZZ) == ZZ(0) assert dup_content([1], ZZ) == ZZ(1) assert dup_content([-1], ZZ) == ZZ(1) assert dup_content([1, 1], ZZ) == ZZ(1) assert dup_content([2, 2], ZZ) == ZZ(2) assert dup_content([1, 2, 1], ZZ) == ZZ(1) assert dup_content([2, 4, 2], ZZ) == ZZ(2) assert dup_content([QQ(2, 3), QQ(4, 9)], QQ) == QQ(2, 9) assert dup_content([QQ(2, 3), QQ(4, 5)], QQ) == QQ(2, 15) def test_dmp_ground_content(): assert dmp_ground_content([[]], 1, ZZ) == ZZ(0) assert dmp_ground_content([[]], 1, QQ) == QQ(0) assert dmp_ground_content([[1]], 1, ZZ) == ZZ(1) assert dmp_ground_content([[-1]], 1, ZZ) == ZZ(1) assert dmp_ground_content([[1], [1]], 1, ZZ) == ZZ(1) assert dmp_ground_content([[2], [2]], 1, ZZ) == ZZ(2) assert dmp_ground_content([[1], [2], [1]], 1, ZZ) == ZZ(1) assert dmp_ground_content([[2], [4], [2]], 1, ZZ) == ZZ(2) assert dmp_ground_content([[QQ(2, 3)], [QQ(4, 9)]], 1, QQ) == QQ(2, 9) assert dmp_ground_content([[QQ(2, 3)], [QQ(4, 5)]], 1, QQ) == QQ(2, 15) assert dmp_ground_content(f_0, 2, ZZ) == ZZ(1) assert dmp_ground_content( dmp_mul_ground(f_0, ZZ(2), 2, ZZ), 2, ZZ) == ZZ(2) assert dmp_ground_content(f_1, 2, ZZ) == ZZ(1) assert dmp_ground_content( dmp_mul_ground(f_1, ZZ(3), 2, ZZ), 2, ZZ) == ZZ(3) assert dmp_ground_content(f_2, 2, ZZ) == ZZ(1) assert dmp_ground_content( dmp_mul_ground(f_2, ZZ(4), 2, ZZ), 2, ZZ) == ZZ(4) assert dmp_ground_content(f_3, 2, ZZ) == ZZ(1) assert dmp_ground_content( dmp_mul_ground(f_3, ZZ(5), 2, ZZ), 2, ZZ) == ZZ(5) assert dmp_ground_content(f_4, 2, ZZ) == ZZ(1) assert dmp_ground_content( dmp_mul_ground(f_4, ZZ(6), 2, ZZ), 2, ZZ) == ZZ(6) assert dmp_ground_content(f_5, 2, ZZ) == ZZ(1) assert dmp_ground_content( dmp_mul_ground(f_5, ZZ(7), 2, ZZ), 2, ZZ) == ZZ(7) assert dmp_ground_content(f_6, 3, ZZ) == ZZ(1) assert dmp_ground_content( dmp_mul_ground(f_6, ZZ(8), 3, ZZ), 3, ZZ) == ZZ(8) def test_dup_primitive(): assert dup_primitive([], ZZ) == (ZZ(0), []) assert dup_primitive([ZZ(1)], ZZ) == (ZZ(1), [ZZ(1)]) assert dup_primitive([ZZ(1), ZZ(1)], ZZ) == (ZZ(1), [ZZ(1), ZZ(1)]) assert dup_primitive([ZZ(2), ZZ(2)], ZZ) == (ZZ(2), [ZZ(1), ZZ(1)]) assert dup_primitive( [ZZ(1), ZZ(2), ZZ(1)], ZZ) == (ZZ(1), [ZZ(1), ZZ(2), ZZ(1)]) assert dup_primitive( [ZZ(2), ZZ(4), ZZ(2)], ZZ) == (ZZ(2), [ZZ(1), ZZ(2), ZZ(1)]) assert dup_primitive([], QQ) == (QQ(0), []) assert dup_primitive([QQ(1)], QQ) == (QQ(1), [QQ(1)]) assert dup_primitive([QQ(1), QQ(1)], QQ) == (QQ(1), [QQ(1), QQ(1)]) assert dup_primitive([QQ(2), QQ(2)], QQ) == (QQ(2), [QQ(1), QQ(1)]) assert dup_primitive( [QQ(1), QQ(2), QQ(1)], QQ) == (QQ(1), [QQ(1), QQ(2), QQ(1)]) assert dup_primitive( [QQ(2), QQ(4), QQ(2)], QQ) == (QQ(2), [QQ(1), QQ(2), QQ(1)]) assert dup_primitive( [QQ(2, 3), QQ(4, 9)], QQ) == (QQ(2, 9), [QQ(3), QQ(2)]) assert dup_primitive( [QQ(2, 3), QQ(4, 5)], QQ) == (QQ(2, 15), [QQ(5), QQ(6)]) def test_dmp_ground_primitive(): assert dmp_ground_primitive([[]], 1, ZZ) == (ZZ(0), [[]]) assert dmp_ground_primitive(f_0, 2, ZZ) == (ZZ(1), f_0) assert dmp_ground_primitive( dmp_mul_ground(f_0, ZZ(2), 2, ZZ), 2, ZZ) == (ZZ(2), f_0) assert dmp_ground_primitive(f_1, 2, ZZ) == (ZZ(1), f_1) assert dmp_ground_primitive( dmp_mul_ground(f_1, ZZ(3), 2, ZZ), 2, ZZ) == (ZZ(3), f_1) assert dmp_ground_primitive(f_2, 2, ZZ) == (ZZ(1), f_2) assert dmp_ground_primitive( dmp_mul_ground(f_2, ZZ(4), 2, ZZ), 2, ZZ) == (ZZ(4), f_2) assert dmp_ground_primitive(f_3, 2, ZZ) == (ZZ(1), f_3) assert dmp_ground_primitive( dmp_mul_ground(f_3, ZZ(5), 2, ZZ), 2, ZZ) == (ZZ(5), f_3) assert dmp_ground_primitive(f_4, 2, ZZ) == (ZZ(1), f_4) assert dmp_ground_primitive( dmp_mul_ground(f_4, ZZ(6), 2, ZZ), 2, ZZ) == (ZZ(6), f_4) assert dmp_ground_primitive(f_5, 2, ZZ) == (ZZ(1), f_5) assert dmp_ground_primitive( dmp_mul_ground(f_5, ZZ(7), 2, ZZ), 2, ZZ) == (ZZ(7), f_5) assert dmp_ground_primitive(f_6, 3, ZZ) == (ZZ(1), f_6) assert dmp_ground_primitive( dmp_mul_ground(f_6, ZZ(8), 3, ZZ), 3, ZZ) == (ZZ(8), f_6) assert dmp_ground_primitive([[ZZ(2)]], 1, ZZ) == (ZZ(2), [[ZZ(1)]]) assert dmp_ground_primitive([[QQ(2)]], 1, QQ) == (QQ(2), [[QQ(1)]]) assert dmp_ground_primitive( [[QQ(2, 3)], [QQ(4, 9)]], 1, QQ) == (QQ(2, 9), [[QQ(3)], [QQ(2)]]) assert dmp_ground_primitive( [[QQ(2, 3)], [QQ(4, 5)]], 1, QQ) == (QQ(2, 15), [[QQ(5)], [QQ(6)]]) def test_dup_extract(): f = dup_normal([2930944, 0, 2198208, 0, 549552, 0, 45796], ZZ) g = dup_normal([17585664, 0, 8792832, 0, 1099104, 0], ZZ) F = dup_normal([64, 0, 48, 0, 12, 0, 1], ZZ) G = dup_normal([384, 0, 192, 0, 24, 0], ZZ) assert dup_extract(f, g, ZZ) == (45796, F, G) def test_dmp_ground_extract(): f = dmp_normal( [[2930944], [], [2198208], [], [549552], [], [45796]], 1, ZZ) g = dmp_normal([[17585664], [], [8792832], [], [1099104], []], 1, ZZ) F = dmp_normal([[64], [], [48], [], [12], [], [1]], 1, ZZ) G = dmp_normal([[384], [], [192], [], [24], []], 1, ZZ) assert dmp_ground_extract(f, g, 1, ZZ) == (45796, F, G) def test_dup_real_imag(): assert dup_real_imag([], ZZ) == ([[]], [[]]) assert dup_real_imag([1], ZZ) == ([[1]], [[]]) assert dup_real_imag([1, 1], ZZ) == ([[1], [1]], [[1, 0]]) assert dup_real_imag([1, 2], ZZ) == ([[1], [2]], [[1, 0]]) assert dup_real_imag( [1, 2, 3], ZZ) == ([[1], [2], [-1, 0, 3]], [[2, 0], [2, 0]]) raises(DomainError, lambda: dup_real_imag([EX(1), EX(2)], EX)) def test_dup_mirror(): assert dup_mirror([], ZZ) == [] assert dup_mirror([1], ZZ) == [1] assert dup_mirror([1, 2, 3, 4, 5], ZZ) == [1, -2, 3, -4, 5] assert dup_mirror([1, 2, 3, 4, 5, 6], ZZ) == [-1, 2, -3, 4, -5, 6] def test_dup_scale(): assert dup_scale([], -1, ZZ) == [] assert dup_scale([1], -1, ZZ) == [1] assert dup_scale([1, 2, 3, 4, 5], -1, ZZ) == [1, -2, 3, -4, 5] assert dup_scale([1, 2, 3, 4, 5], -7, ZZ) == [2401, -686, 147, -28, 5] def test_dup_shift(): assert dup_shift([], 1, ZZ) == [] assert dup_shift([1], 1, ZZ) == [1] assert dup_shift([1, 2, 3, 4, 5], 1, ZZ) == [1, 6, 15, 20, 15] assert dup_shift([1, 2, 3, 4, 5], 7, ZZ) == [1, 30, 339, 1712, 3267] def test_dup_transform(): assert dup_transform([], [], [1, 1], ZZ) == [] assert dup_transform([], [1], [1, 1], ZZ) == [] assert dup_transform([], [1, 2], [1, 1], ZZ) == [] assert dup_transform([6, -5, 4, -3, 17], [1, -3, 4], [2, -3], ZZ) == \ [6, -82, 541, -2205, 6277, -12723, 17191, -13603, 4773] def test_dup_compose(): assert dup_compose([], [], ZZ) == [] assert dup_compose([], [1], ZZ) == [] assert dup_compose([], [1, 2], ZZ) == [] assert dup_compose([1], [], ZZ) == [1] assert dup_compose([1, 2, 0], [], ZZ) == [] assert dup_compose([1, 2, 1], [], ZZ) == [1] assert dup_compose([1, 2, 1], [1], ZZ) == [4] assert dup_compose([1, 2, 1], [7], ZZ) == [64] assert dup_compose([1, 2, 1], [1, -1], ZZ) == [1, 0, 0] assert dup_compose([1, 2, 1], [1, 1], ZZ) == [1, 4, 4] assert dup_compose([1, 2, 1], [1, 2, 1], ZZ) == [1, 4, 8, 8, 4] def test_dmp_compose(): assert dmp_compose([1, 2, 1], [1, 2, 1], 0, ZZ) == [1, 4, 8, 8, 4] assert dmp_compose([[[]]], [[[]]], 2, ZZ) == [[[]]] assert dmp_compose([[[]]], [[[1]]], 2, ZZ) == [[[]]] assert dmp_compose([[[]]], [[[1]], [[2]]], 2, ZZ) == [[[]]] assert dmp_compose([[[1]]], [], 2, ZZ) == [[[1]]] assert dmp_compose([[1], [2], [ ]], [[]], 1, ZZ) == [[]] assert dmp_compose([[1], [2], [1]], [[]], 1, ZZ) == [[1]] assert dmp_compose([[1], [2], [1]], [[1]], 1, ZZ) == [[4]] assert dmp_compose([[1], [2], [1]], [[7]], 1, ZZ) == [[64]] assert dmp_compose([[1], [2], [1]], [[1], [-1]], 1, ZZ) == [[1], [ ], [ ]] assert dmp_compose([[1], [2], [1]], [[1], [ 1]], 1, ZZ) == [[1], [4], [4]] assert dmp_compose( [[1], [2], [1]], [[1], [2], [1]], 1, ZZ) == [[1], [4], [8], [8], [4]] def test_dup_decompose(): assert dup_decompose([1], ZZ) == [[1]] assert dup_decompose([1, 0], ZZ) == [[1, 0]] assert dup_decompose([1, 0, 0, 0], ZZ) == [[1, 0, 0, 0]] assert dup_decompose([1, 0, 0, 0, 0], ZZ) == [[1, 0, 0], [1, 0, 0]] assert dup_decompose( [1, 0, 0, 0, 0, 0, 0], ZZ) == [[1, 0, 0, 0], [1, 0, 0]] assert dup_decompose([7, 0, 0, 0, 1], ZZ) == [[7, 0, 1], [1, 0, 0]] assert dup_decompose([4, 0, 3, 0, 2], ZZ) == [[4, 3, 2], [1, 0, 0]] f = [1, 0, 20, 0, 150, 0, 500, 0, 625, -2, 0, -10, 9] assert dup_decompose(f, ZZ) == [[1, 0, 0, -2, 9], [1, 0, 5, 0]] f = [2, 0, 40, 0, 300, 0, 1000, 0, 1250, -4, 0, -20, 18] assert dup_decompose(f, ZZ) == [[2, 0, 0, -4, 18], [1, 0, 5, 0]] f = [1, 0, 20, -8, 150, -120, 524, -600, 865, -1034, 600, -170, 29] assert dup_decompose(f, ZZ) == [[1, -8, 24, -34, 29], [1, 0, 5, 0]] R, t = ring("t", ZZ) f = [6*t**2 - 42, 48*t**2 + 96, 144*t**2 + 648*t + 288, 624*t**2 + 864*t + 384, 108*t**3 + 312*t**2 + 432*t + 192] assert dup_decompose(f, R.to_domain()) == [f] def test_dmp_lift(): q = [QQ(1, 1), QQ(0, 1), QQ(1, 1)] f = [ANP([QQ(1, 1)], q, QQ), ANP([], q, QQ), ANP([], q, QQ), ANP([QQ(1, 1), QQ(0, 1)], q, QQ), ANP([QQ(17, 1), QQ(0, 1)], q, QQ)] assert dmp_lift(f, 0, QQ.algebraic_field(I)) == \ [QQ(1), QQ(0), QQ(0), QQ(0), QQ(0), QQ(0), QQ(2), QQ(0), QQ(578), QQ(0), QQ(0), QQ(0), QQ(1), QQ(0), QQ(-578), QQ(0), QQ(83521)] raises(DomainError, lambda: dmp_lift([EX(1), EX(2)], 0, EX)) def test_dup_sign_variations(): assert dup_sign_variations([], ZZ) == 0 assert dup_sign_variations([1, 0], ZZ) == 0 assert dup_sign_variations([1, 0, 2], ZZ) == 0 assert dup_sign_variations([1, 0, 3, 0], ZZ) == 0 assert dup_sign_variations([1, 0, 4, 0, 5], ZZ) == 0 assert dup_sign_variations([-1, 0, 2], ZZ) == 1 assert dup_sign_variations([-1, 0, 3, 0], ZZ) == 1 assert dup_sign_variations([-1, 0, 4, 0, 5], ZZ) == 1 assert dup_sign_variations([-1, -4, -5], ZZ) == 0 assert dup_sign_variations([ 1, -4, -5], ZZ) == 1 assert dup_sign_variations([ 1, 4, -5], ZZ) == 1 assert dup_sign_variations([ 1, -4, 5], ZZ) == 2 assert dup_sign_variations([-1, 4, -5], ZZ) == 2 assert dup_sign_variations([-1, 4, 5], ZZ) == 1 assert dup_sign_variations([-1, -4, 5], ZZ) == 1 assert dup_sign_variations([ 1, 4, 5], ZZ) == 0 assert dup_sign_variations([-1, 0, -4, 0, -5], ZZ) == 0 assert dup_sign_variations([ 1, 0, -4, 0, -5], ZZ) == 1 assert dup_sign_variations([ 1, 0, 4, 0, -5], ZZ) == 1 assert dup_sign_variations([ 1, 0, -4, 0, 5], ZZ) == 2 assert dup_sign_variations([-1, 0, 4, 0, -5], ZZ) == 2 assert dup_sign_variations([-1, 0, 4, 0, 5], ZZ) == 1 assert dup_sign_variations([-1, 0, -4, 0, 5], ZZ) == 1 assert dup_sign_variations([ 1, 0, 4, 0, 5], ZZ) == 0 def test_dup_clear_denoms(): assert dup_clear_denoms([], QQ, ZZ) == (ZZ(1), []) assert dup_clear_denoms([QQ(1)], QQ, ZZ) == (ZZ(1), [QQ(1)]) assert dup_clear_denoms([QQ(7)], QQ, ZZ) == (ZZ(1), [QQ(7)]) assert dup_clear_denoms([QQ(7, 3)], QQ) == (ZZ(3), [QQ(7)]) assert dup_clear_denoms([QQ(7, 3)], QQ, ZZ) == (ZZ(3), [QQ(7)]) assert dup_clear_denoms( [QQ(3), QQ(1), QQ(0)], QQ, ZZ) == (ZZ(1), [QQ(3), QQ(1), QQ(0)]) assert dup_clear_denoms( [QQ(1), QQ(1, 2), QQ(0)], QQ, ZZ) == (ZZ(2), [QQ(2), QQ(1), QQ(0)]) assert dup_clear_denoms([QQ(3), QQ( 1), QQ(0)], QQ, ZZ, convert=True) == (ZZ(1), [ZZ(3), ZZ(1), ZZ(0)]) assert dup_clear_denoms([QQ(1), QQ( 1, 2), QQ(0)], QQ, ZZ, convert=True) == (ZZ(2), [ZZ(2), ZZ(1), ZZ(0)]) assert dup_clear_denoms( [EX(S(3)/2), EX(S(9)/4)], EX) == (EX(4), [EX(6), EX(9)]) assert dup_clear_denoms([EX(7)], EX) == (EX(1), [EX(7)]) assert dup_clear_denoms([EX(sin(x)/x), EX(0)], EX) == (EX(x), [EX(sin(x)), EX(0)]) def test_dmp_clear_denoms(): assert dmp_clear_denoms([[]], 1, QQ, ZZ) == (ZZ(1), [[]]) assert dmp_clear_denoms([[QQ(1)]], 1, QQ, ZZ) == (ZZ(1), [[QQ(1)]]) assert dmp_clear_denoms([[QQ(7)]], 1, QQ, ZZ) == (ZZ(1), [[QQ(7)]]) assert dmp_clear_denoms([[QQ(7, 3)]], 1, QQ) == (ZZ(3), [[QQ(7)]]) assert dmp_clear_denoms([[QQ(7, 3)]], 1, QQ, ZZ) == (ZZ(3), [[QQ(7)]]) assert dmp_clear_denoms( [[QQ(3)], [QQ(1)], []], 1, QQ, ZZ) == (ZZ(1), [[QQ(3)], [QQ(1)], []]) assert dmp_clear_denoms([[QQ( 1)], [QQ(1, 2)], []], 1, QQ, ZZ) == (ZZ(2), [[QQ(2)], [QQ(1)], []]) assert dmp_clear_denoms([QQ(3), QQ( 1), QQ(0)], 0, QQ, ZZ, convert=True) == (ZZ(1), [ZZ(3), ZZ(1), ZZ(0)]) assert dmp_clear_denoms([QQ(1), QQ(1, 2), QQ( 0)], 0, QQ, ZZ, convert=True) == (ZZ(2), [ZZ(2), ZZ(1), ZZ(0)]) assert dmp_clear_denoms([[QQ(3)], [QQ( 1)], []], 1, QQ, ZZ, convert=True) == (ZZ(1), [[QQ(3)], [QQ(1)], []]) assert dmp_clear_denoms([[QQ(1)], [QQ(1, 2)], []], 1, QQ, ZZ, convert=True) == (ZZ(2), [[QQ(2)], [QQ(1)], []]) assert dmp_clear_denoms( [[EX(S(3)/2)], [EX(S(9)/4)]], 1, EX) == (EX(4), [[EX(6)], [EX(9)]]) assert dmp_clear_denoms([[EX(7)]], 1, EX) == (EX(1), [[EX(7)]]) assert dmp_clear_denoms([[EX(sin(x)/x), EX(0)]], 1, EX) == (EX(x), [[EX(sin(x)), EX(0)]]) sympy-0.7.4.1/sympy/polys/tests/__init__.py0000644000175000017500000000000012253362407021034 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/polys/__init__.py0000644000175000017500000000277312253362407017715 0ustar georgeskgeorgesk"""Polynomial manipulation algorithms and algebraic objects. """ __all__ = [] from . import polytools __all__.extend(polytools.__all__) from .polytools import * from . import polyfuncs __all__.extend(polyfuncs.__all__) from .polyfuncs import * from . import rationaltools __all__.extend(rationaltools.__all__) from .rationaltools import * from . import polyerrors __all__.extend(polyerrors.__all__) from .polyerrors import * from . import numberfields __all__.extend(numberfields.__all__) from .numberfields import * from . import monomials __all__.extend(monomials.__all__) from .monomials import * from . import orderings __all__.extend(orderings.__all__) from .orderings import * from . import rootoftools __all__.extend(rootoftools.__all__) from .rootoftools import * from . import polyroots __all__.extend(polyroots.__all__) from .polyroots import * from . import domains __all__.extend(domains.__all__) from .domains import * from . import constructor __all__.extend(constructor.__all__) from .constructor import * from . import specialpolys __all__.extend(specialpolys.__all__) from .specialpolys import * from . import orthopolys __all__.extend(orthopolys.__all__) from .orthopolys import * from . import partfrac __all__.extend(partfrac.__all__) from .partfrac import * from . import polyoptions __all__.extend(polyoptions.__all__) from .polyoptions import * from . import rings __all__.extend(rings.__all__) from .rings import * from . import fields __all__.extend(fields.__all__) from .fields import * sympy-0.7.4.1/sympy/tensor/0000755000175000017500000000000012253362407015737 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/tensor/index_methods.py0000644000175000017500000003535612253362407021157 0ustar georgeskgeorgesk"""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 __future__ import print_function, division from sympy.tensor.indexed import Idx, Indexed from sympy.functions import exp from sympy.core import C from sympy.core.compatibility 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 = 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 = list(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 = 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 = 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 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 = 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)) def get_contraction_structure(expr): """Determine dummy indices of ``expr`` and describe its 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 its 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 = map(IndexedBase, ['x', 'y', 'A']) >>> i, j, k, l = 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(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(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)) sympy-0.7.4.1/sympy/tensor/tensor.py0000644000175000017500000032312112253362407017625 0ustar georgeskgeorgesk""" This module defines tensors with abstract index notation. The abstract index notation has been first formalized by Penrose. Tensor indices are formal objects, with a tensor type; there is no notion of index range, it is only possible to assign the dimension, used to trace the Kronecker delta; the dimension can be a Symbol. The Einstein summation convention is used. The covariant indices are indicated with a minus sign in front of the index. For instance the tensor ``t = p(a)*A(b,c)*q(-c)`` has the index ``c`` contracted. A tensor expression ``t`` can be called; called with its indices in sorted order it is equal to itself: in the above example ``t(a, b) == t``; one can call ``t`` with different indices; ``t(c, d) == p(c)*A(d,a)*q(-a)``. The contracted indices are dummy indices, internally they have no name, the indices being represented by a graph-like structure. Tensors are put in canonical form using ``canon_bp``, which uses the Butler-Portugal algorithm for canonicalization using the monoterm symmetries of the tensors. If there is a (anti)symmetric metric, the indices can be raised and lowered when the tensor is put in canonical form. """ from __future__ import print_function, division from collections import defaultdict from sympy.core import Basic, sympify, Add, S from sympy.core.symbol import Symbol, symbols from sympy.core.compatibility import string_types from sympy.combinatorics.tensor_can import get_symmetric_group_sgs, bsgs_direct_product, canonicalize, riemann_bsgs from sympy.core.containers import Tuple from sympy import Matrix, Rational from sympy.external import import_module from sympy.utilities.decorator import doctest_depends_on class TIDS(object): """ Tensor internal data structure. This contains internal data about components of a tensor expression, its free and dummy indices. To create a `TIDS` object via the standard constructor, the required arguments are ``components`` `TensorHead` objects representing the components of the tensor expression. ``free`` Free indices in their internal representation. ``dum`` Dummy indices in their internal representation. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, TIDS, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> m0, m1, m2, m3 = tensor_indices('m0,m1,m2,m3', Lorentz) >>> T = tensorhead('T', [Lorentz]*4, [[1]*4]) >>> TIDS([T], [(m0, 0, 0), (m3, 3, 0)], [(1, 2, 0, 0)]) TIDS([T(Lorentz,Lorentz,Lorentz,Lorentz)], [(m0, 0, 0), (m3, 3, 0)], [(1, 2, 0, 0)]) Details ======= In short, this has created the components, free and dummy indices for the internal representation of a tensor T(m0, m1, -m1, m3). Free indices are represented as a list of triplets. The elements of each triplet identify a single free index and are 1. TensorIndex object 2. position inside the component 3. component number Dummy indices are represented as a list of 4-plets. Each 4-plet stands for couple for contracted indices, their original TensorIndex is not stored as it is no longer required. The four elements of the 4-plet are 1. position inside the component of the first index. 2. position inside the component of the second index. 3. component number of the first index. 4. component number of the second index. """ def __init__(self, components, free, dum): self.components = components self.free = free self.dum = dum self._ext_rank = len(self.free) + 2*len(self.dum) def get_components_with_free_indices(self): """ Get a list of components with their associated indices. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, TIDS, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> m0, m1, m2, m3 = tensor_indices('m0,m1,m2,m3', Lorentz) >>> T = tensorhead('T', [Lorentz]*4, [[1]*4]) >>> A = tensorhead('A', [Lorentz], [[1]]) >>> t = TIDS.from_components_and_indices([T], [m0, m1, -m1, m3]) >>> t.get_components_with_free_indices() [(T(Lorentz,Lorentz,Lorentz,Lorentz), [(m0, 0, 0), (m3, 3, 0)])] >>> t2 = (A(m0)*A(-m0))._tids >>> t2.get_components_with_free_indices() [(A(Lorentz), []), (A(Lorentz), [])] >>> t3 = (A(m0)*A(-m1)*A(-m0)*A(m1))._tids >>> t3.get_components_with_free_indices() [(A(Lorentz), []), (A(Lorentz), []), (A(Lorentz), []), (A(Lorentz), [])] >>> t4 = (A(m0)*A(m1)*A(-m0))._tids >>> t4.get_components_with_free_indices() [(A(Lorentz), []), (A(Lorentz), [(m1, 0, 1)]), (A(Lorentz), [])] >>> t5 = (A(m0)*A(m1)*A(m2))._tids >>> t5.get_components_with_free_indices() [(A(Lorentz), [(m0, 0, 0)]), (A(Lorentz), [(m1, 0, 1)]), (A(Lorentz), [(m2, 0, 2)])] """ components = self.components ret_comp = [] free_counter = 0 # dum_counter1 = 0 # dum_counter2 = 0 if len(self.free) == 0: return [(comp, []) for comp in components] for i, comp in enumerate(components): c_free = [] while free_counter < len(self.free): if not self.free[free_counter][2] == i: break c_free.append(self.free[free_counter]) free_counter += 1 if free_counter >= len(self.free): break ret_comp.append((comp, c_free)) return ret_comp @staticmethod def from_components_and_indices(components, indices): """ Create a new `TIDS` object from `components` and `indices` ``components`` `TensorHead` objects representing the components of the tensor expression. ``indices`` `TensorIndex` objects, the indices. Contractions are detected upon construction. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, TIDS, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> m0, m1, m2, m3 = tensor_indices('m0,m1,m2,m3', Lorentz) >>> T = tensorhead('T', [Lorentz]*4, [[1]*4]) >>> TIDS.from_components_and_indices([T], [m0, m1, -m1, m3]) TIDS([T(Lorentz,Lorentz,Lorentz,Lorentz)], [(m0, 0, 0), (m3, 3, 0)], [(1, 2, 0, 0)]) In case of many components the same indices have slightly different indexes: >>> A = tensorhead('A', [Lorentz], [[1]]) >>> TIDS.from_components_and_indices([A]*4, [m0, m1, -m1, m3]) TIDS([A(Lorentz), A(Lorentz), A(Lorentz), A(Lorentz)], [(m0, 0, 0), (m3, 0, 3)], [(0, 0, 1, 2)]) """ tids = None cur_pos = 0 for i in components: tids_sing = TIDS([i], *TIDS.free_dum_from_indices(*indices[cur_pos:cur_pos+i.rank])) if tids is None: tids = tids_sing else: tids *= tids_sing cur_pos += i.rank if tids is None: tids = TIDS([], [], []) tids.free.sort(key=lambda x: x[0].name) tids.dum.sort() return tids def to_indices(self): """ Get a list of indices, creating new tensor indices to complete dummy indices. """ component_indices = [] for i in self.components: component_indices.append([None]*i.rank) for i in self.free: component_indices[i[2]][i[1]] = i[0] for i, dummy_pos in enumerate(self.dum): tensor_index_type = self.components[dummy_pos[2]].args[1].args[0][0] dummy_index = TensorIndex('dummy_index_{0}'.format(i), tensor_index_type) component_indices[dummy_pos[2]][dummy_pos[0]] = dummy_index component_indices[dummy_pos[3]][dummy_pos[1]] = -dummy_index indices = [] for i in component_indices: indices.extend(i) return indices @staticmethod def free_dum_from_indices(*indices): """ Convert ``indices`` into ``free``, ``dum`` for single component tensor ``free`` list of tuples ``(index, pos, 0)``, where ``pos`` is the position of index in the list of indices formed by the component tensors ``dum`` list of tuples ``(pos_contr, pos_cov, 0, 0)`` Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, TIDS >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> m0, m1, m2, m3 = tensor_indices('m0,m1,m2,m3', Lorentz) >>> TIDS.free_dum_from_indices(m0, m1, -m1, m3) ([(m0, 0, 0), (m3, 3, 0)], [(1, 2, 0, 0)]) """ n = len(indices) if n == 1: return [(indices[0], 0, 0)], [] # find the positions of the free indices and of the dummy indices free = [True]*len(indices) index_dict = {} dum = [] for i, index in enumerate(indices): name = index._name typ = index._tensortype contr = index._is_up if (name, typ) in index_dict: # found a pair of dummy indices is_contr, pos = index_dict[(name, typ)] # check consistency and update free if is_contr: if contr: raise ValueError('two equal contravariant indices in slots %d and %d' %(pos, i)) else: free[pos] = False free[i] = False else: if contr: free[pos] = False free[i] = False else: raise ValueError('two equal covariant indices in slots %d and %d' %(pos, i)) if contr: dum.append((i, pos, 0, 0)) else: dum.append((pos, i, 0, 0)) else: index_dict[(name, typ)] = index._is_up, i free = [(index, i, 0) for i, index in enumerate(indices) if free[i]] free.sort() return free, dum @staticmethod def mul(f, g): """ The algorithms performing the multiplication of two TIDS instances. In short, it forms a new TIDS object, joining components and indices, checking that abstract indices are compatible, and possibly contracting them. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, TIDS, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> m0, m1, m2, m3 = tensor_indices('m0,m1,m2,m3', Lorentz) >>> T = tensorhead('T', [Lorentz]*4, [[1]*4]) >>> A = tensorhead('A', [Lorentz], [[1]]) >>> tids_1 = TIDS.from_components_and_indices([T], [m0, m1, -m1, m3]) >>> tids_2 = TIDS.from_components_and_indices([A], [m2]) >>> tids_1 * tids_2 TIDS([T(Lorentz,Lorentz,Lorentz,Lorentz), A(Lorentz)],\ [(m0, 0, 0), (m3, 3, 0), (m2, 0, 1)], [(1, 2, 0, 0)]) In this case no contraction has been performed. >>> tids_3 = TIDS.from_components_and_indices([A], [-m3]) >>> tids_1 * tids_3 TIDS([T(Lorentz,Lorentz,Lorentz,Lorentz), A(Lorentz)],\ [(m0, 0, 0)], [(1, 2, 0, 0), (3, 0, 0, 1)]) Free indices m3 and -m3 are identified as a contracted couple, and are therefore transformed into dummy indices. A wrong index construction (for example, trying to contract two contravariant indices or using indices multiple times) would result in an exception: >>> tids_4 = TIDS.from_components_and_indices([A], [m3]) >>> # This raises an exception: >>> # tids_1 * tids_4 """ index_up = lambda u: u if u.is_up else -u # find out which free indices of f and g are contracted free_dict1 = dict([(i if i.is_up else -i, (pos, cpos, i)) for i, pos, cpos in f.free]) free_dict2 = dict([(i if i.is_up else -i, (pos, cpos, i)) for i, pos, cpos in g.free]) free_names = set(free_dict1.keys()) & set(free_dict2.keys()) # find the new `free` and `dum` nc1 = len(f.components) dum2 = [(i1, i2, c1 + nc1, c2 + nc1) for i1, i2, c1, c2 in g.dum] free1 = [(ind, i, c) for ind, i, c in f.free if index_up(ind) not in free_names] free2 = [(ind, i, c + nc1) for ind, i, c in g.free if index_up(ind) not in free_names] free = free1 + free2 dum = f.dum + dum2 for name in free_names: ipos1, cpos1, ind1 = free_dict1[name] ipos2, cpos2, ind2 = free_dict2[name] cpos2 += nc1 if ind1._is_up == ind2._is_up: raise ValueError('wrong index construction {0}'.format(ind1)) if ind1._is_up: new_dummy = (ipos1, ipos2, cpos1, cpos2) else: new_dummy = (ipos2, ipos1, cpos2, cpos1) dum.append(new_dummy) return (f.components + g.components, free, dum) def __mul__(self, other): return TIDS(*self.mul(self, other)) def __str__(self): return "TIDS({0}, {1}, {2})".format(self.components, self.free, self.dum) def __repr__(self): return self.__str__() def sorted_components(self): """ Returns a TIDS with sorted components The sorting is done taking into account the commutation group of the component tensors. """ from sympy.combinatorics.permutations import _af_invert cv = list(zip(self.components, range(len(self.components)))) sign = 1 n = len(cv) - 1 for i in range(n): for j in range(n, i, -1): c = cv[j-1][0].commutes_with(cv[j][0]) if c not in [0, 1]: continue if (cv[j-1][0]._types, cv[j-1][0]._name) > \ (cv[j][0]._types, cv[j][0]._name): cv[j-1], cv[j] = cv[j], cv[j-1] if c: sign = -sign # perm_inv[new_pos] = old_pos components = [x[0] for x in cv] perm_inv = [x[1] for x in cv] perm = _af_invert(perm_inv) free = [(ind, i, perm[c]) for ind, i, c in self.free] free.sort() dum = [(i1, i2, perm[c1], perm[c2]) for i1, i2, c1, c2 in self.dum] dum.sort(key=lambda x: components[x[2]].index_types[x[0]]) return TIDS(components, free, dum), sign def canon_args(self): """ Returns ``(g, dummies, msym, v)``, the entries of ``canonicalize`` see ``canonicalize`` in ``tensor_can.py`` """ # to be called after sorted_components from sympy.combinatorics.permutations import _af_new # types = list(set(self._types)) # types.sort(key = lambda x: x._name) n = self._ext_rank g = [None]*n + [n, n+1] pos = 0 vpos = [] components = self.components for t in components: vpos.append(pos) pos += t._rank # ordered indices: first the free indices, ordered by types # then the dummy indices, ordered by types and contravariant before # covariant # g[position in tensor] = position in ordered indices for i, (indx, ipos, cpos) in enumerate(self.free): pos = vpos[cpos] + ipos g[pos] = i pos = len(self.free) j = len(self.free) dummies = [] prev = None a = [] msym = [] for ipos1, ipos2, cpos1, cpos2 in self.dum: pos1 = vpos[cpos1] + ipos1 pos2 = vpos[cpos2] + ipos2 g[pos1] = j g[pos2] = j + 1 j += 2 typ = components[cpos1].index_types[ipos1] if typ != prev: if a: dummies.append(a) a = [pos, pos + 1] prev = typ msym.append(typ.metric_antisym) else: a.extend([pos, pos + 1]) pos += 2 if a: dummies.append(a) numtyp = [] prev = None for t in components: if t == prev: numtyp[-1][1] += 1 else: prev = t numtyp.append([prev, 1]) v = [] for h, n in numtyp: if h._comm == 0 or h._comm == 1: comm = h._comm else: comm = TensorManager.get_comm(h._comm, h._comm) v.append((h._symmetry.base, h._symmetry.generators, n, comm)) return _af_new(g), dummies, msym, v def perm2tensor(self, g, canon_bp=False): """ Returns a `TIDS` instance corresponding to the permutation ``g`` ``g`` permutation corresponding to the tensor in the representation used in canonicalization ``canon_bp`` if True, then ``g`` is the permutation corresponding to the canonical form of the tensor """ vpos = [] components = self.components pos = 0 for t in components: vpos.append(pos) pos += t._rank sorted_free = [x[0] for x in self.free] sorted_free.sort() nfree = len(sorted_free) rank = self._ext_rank dum = [[None]*4 for i in range((rank - nfree)//2)] free = [] icomp = -1 for i in range(rank): if i in vpos: icomp += vpos.count(i) pos0 = i ipos = i - pos0 gi = g[i] if gi < nfree: ind = sorted_free[gi] free.append((ind, ipos, icomp)) else: j = gi - nfree idum, cov = divmod(j, 2) if cov: dum[idum][1] = ipos dum[idum][3] = icomp else: dum[idum][0] = ipos dum[idum][2] = icomp dum = [tuple(x) for x in dum] return TIDS(components, free, dum) @doctest_depends_on(modules=('numpy',)) class VTIDS(TIDS): """ This class handles a ``VTIDS`` object, which is a ``TIDS`` object with an attached ``numpy`` ``ndarray``. To create a `TIDS` object via the standard constructor, the required arguments are ``components`` `TensorHead` objects representing the components of the tensor expression. ``free`` Free indices in their internal representation. ``dum`` Dummy indices in their internal representation. ``data`` Data as a ``numpy`` ``ndarray``. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, VTIDS, tensorhead >>> import numpy >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> m0, m1, m2, m3 = tensor_indices('m0,m1,m2,m3', Lorentz) >>> T = tensorhead('T', [Lorentz]*4, [[1]*4]) >>> data = numpy.array([2,9,6,-5]).reshape(2, 2) >>> VTIDS([T], [(m0, 0, 0), (m3, 3, 0)], [(1, 2, 0, 0)], data) VTIDS([T(Lorentz,Lorentz,Lorentz,Lorentz)], [(m0, 0, 0), (m3, 3, 0)], [(1, 2, 0, 0)], [[ 2 9] [ 6 -5]]) """ def __init__(self, components, free, dum, data): super(VTIDS, self).__init__(components, free, dum) self.data = data @staticmethod def _contract_ndarray(free1, free2, ndarray1, ndarray2): numpy = import_module('numpy') def ikey(x): return x[1:] free1 = free1[:] free2 = free2[:] free1.sort(key=ikey) free2.sort(key=ikey) self_free = [_[0] for _ in free1] axes1 = [] axes2 = [] for jpos, jindex in enumerate(free2): if -jindex[0] in self_free: nidx = self_free.index(-jindex[0]) else: continue axes1.append(nidx) axes2.append(jpos) contracted_ndarray = numpy.tensordot( ndarray1, ndarray2, (axes1, axes2) ) return contracted_ndarray @staticmethod def mul(f, g): """ Multiplies two ``VTIDS`` objects, it first calls its super method on ``TIDS``, then creates a new ``VTIDS`` object, adding ``ndarray`` data according to the metric contractions of indices. """ components, free, dum = TIDS.mul(f, g) data = VTIDS._contract_ndarray(f.free, g.free, f.data, g.data) return components, free, dum, data def __mul__(f, g): return VTIDS(*VTIDS.mul(f, g)) @staticmethod def flip_index_by_metric(data, metric, pos): numpy = import_module('numpy') data = numpy.tensordot( metric, data, (1, pos)) return numpy.rollaxis(data, 0, pos+1) def correct_signature_from_indices(self, data, indices, free, dum): """ Utility function to correct the values inside the data ndarray according to whether indices are covariant or contravariant. It uses the metric matrix to lower values of covariant indices. """ numpy = import_module('numpy') # change the ndarray values according covariantness/contravariantness of the indices # use the metric for i, indx in enumerate(indices): if not indx.is_up: data = VTIDS.flip_index_by_metric(data, indx._tensortype.data, i) if len(dum) > 0: ### perform contractions ### axes1 = [] axes2 = [] for i, indx1 in enumerate(indices): try: nd = indices[:i].index(-indx1) except ValueError: continue axes1.append(nd) axes2.append(i) for ax1, ax2 in zip(axes1, axes2): data = numpy.trace(data, axis1=ax1, axis2=ax2) self.data = data @staticmethod @doctest_depends_on(modules=('numpy',)) def parse_data(data): """ Transform data to a numpy ndarray. Examples ======== >>> from sympy.tensor.tensor import VTIDS >>> VTIDS.parse_data([1, 3, -6, 12]) [1 3 -6 12] >>> VTIDS.parse_data([[1, 2], [4, 7]]) [[1 2] [4 7]] """ numpy = import_module('numpy') if (numpy is not None) and (not isinstance(data, numpy.ndarray)): if len(data) == 2 and hasattr(data[0], '__call__'): def fromfunction_sympify(*x): return sympify(data[0](*x)) data = numpy.fromfunction(fromfunction_sympify, data[1]) else: vsympify = numpy.vectorize(sympify) data = vsympify(numpy.array(data)) return data def _sort_data_axes(self, ret): numpy = import_module('numpy') new_data = self.data.copy() old_free = [i[0] for i in self.free] new_free = [i[0] for i in ret.free] for i in range(len(new_free)): for j in range(i, len(old_free)): if old_free[j] == new_free[i]: old_free[i], old_free[j] = old_free[j], old_free[i] new_data = numpy.swapaxes(new_data, i, j) break return new_data def sorted_components(self): ret, sign = TIDS.sorted_components(self) new_data = self._sort_data_axes(ret) vtids = VTIDS(ret.components, ret.free, ret.dum, new_data) return vtids, sign def perm2tensor(self, g, canon_bp=False): ret = TIDS.perm2tensor(self, g, canon_bp) new_data = self._sort_data_axes(ret) return VTIDS(ret.components, ret.free, ret.dum, new_data) def __str__(self): return "VTIDS(%s, %s, %s, %s)" % (self.components, self.free, self.dum, self.data) def __repr__(self): return str(self) class _TensorManager(object): """ Class to manage tensor properties. Notes ===== Tensors belong to tensor commutation groups; each group has a label ``comm``; there are predefined labels: ``0`` tensors commuting with any other tensor ``1`` tensors anticommuting among themselves ``2`` tensors not commuting, apart with those with ``comm=0`` Other groups can be defined using ``set_comm``; tensors in those groups commute with those with ``comm=0``; by default they do not commute with any other group. """ def __init__(self): self._comm_init() def _comm_init(self): self._comm = [{} for i in range(3)] for i in range(3): self._comm[0][i] = 0 self._comm[i][0] = 0 self._comm[1][1] = 1 self._comm[2][1] = None self._comm[1][2] = None self._comm_symbols2i = {0:0, 1:1, 2:2} self._comm_i2symbol = {0:0, 1:1, 2:2} @property def comm(self): return self._comm def comm_symbols2i(self, i): """ get the commutation group number corresponding to ``i`` ``i`` can be a symbol or a number or a string If ``i`` is not already defined its commutation group number is set. """ if i not in self._comm_symbols2i: n = len(self._comm) self._comm.append({}) self._comm[n][0] = 0 self._comm[0][n] = 0 self._comm_symbols2i[i] = n self._comm_i2symbol[n] = i return n return self._comm_symbols2i[i] def comm_i2symbol(self, i): """ Returns the symbol corresponding to the commutation group number. """ return self._comm_i2symbol[i] def set_comm(self, i, j, c): """ set the commutation parameter ``c`` for commutation groups ``i, j`` Parameters ========== i, j : symbols representing commutation groups c : group commutation number Notes ===== ``i, j`` can be symbols, strings or numbers, apart from ``0, 1`` and ``2`` which are reserved respectively for commuting, anticommuting tensors and tensors not commuting with any other group apart with the commuting tensors. For the remaining cases, use this method to set the commutation rules; by default ``c=None``. The group commutation number ``c`` is assigned in correspondence to the group commutation symbols; it can be 0 commuting 1 anticommuting None no commutation property Examples ======== ``G`` and ``GH`` do not commute with themselves and commute with each other; A is commuting. >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensorhead, TensorManager >>> Lorentz = TensorIndexType('Lorentz') >>> i0,i1,i2,i3,i4 = tensor_indices('i0:5', Lorentz) >>> A = tensorhead('A', [Lorentz], [[1]]) >>> G = tensorhead('G', [Lorentz], [[1]], 'Gcomm') >>> GH = tensorhead('GH', [Lorentz], [[1]], 'GHcomm') >>> TensorManager.set_comm('Gcomm', 'GHcomm', 0) >>> (GH(i1)*G(i0)).canon_bp() G(i0)*GH(i1) >>> (G(i1)*G(i0)).canon_bp() G(i1)*G(i0) >>> (G(i1)*A(i0)).canon_bp() A(i0)*G(i1) """ if c not in (0, 1, None): raise ValueError('`c` can assume only the values 0, 1 or None') if i not in self._comm_symbols2i: n = len(self._comm) self._comm.append({}) self._comm[n][0] = 0 self._comm[0][n] = 0 self._comm_symbols2i[i] = n self._comm_i2symbol[n] = i if j not in self._comm_symbols2i: n = len(self._comm) self._comm.append({}) self._comm[0][n] = 0 self._comm[n][0] = 0 self._comm_symbols2i[j] = n self._comm_i2symbol[n] = j ni = self._comm_symbols2i[i] nj = self._comm_symbols2i[j] self._comm[ni][nj] = c self._comm[nj][ni] = c def set_comms(self, *args): """ set the commutation group numbers ``c`` for symbols ``i, j`` Parameters ========== args : sequence of ``(i, j, c)`` """ for i, j, c in args: self.set_comm(i, j, c) def get_comm(self, i, j): """ Return the commutation parameter for commutation group numbers ``i, j`` see ``_TensorManager.set_comm`` """ return self._comm[i].get(j, 0 if i == 0 or j == 0 else None) def clear(self): """ Clear the TensorManager. """ self._comm_init() TensorManager = _TensorManager() @doctest_depends_on(modules=('numpy',)) class TensorIndexType(Basic): """ A TensorIndexType is characterized by its name and its metric. Parameters ========== name : name of the tensor type metric : metric symmetry or metric object or ``None`` dim : dimension, it can be a symbol or an integer or ``None`` eps_dim : dimension of the epsilon tensor dummy_fmt : name of the head of dummy indices Attributes ========== ``name`` ``metric_name`` : it is 'metric' or metric.name ``metric_antisym`` ``metric`` : the metric tensor ``delta`` : ``Kronecker delta`` ``epsilon`` : the ``Levi-Civita epsilon`` tensor ``dim`` ``dim_eps`` ``dummy_fmt`` ``data`` : a property to add ``ndarray`` values, to work in a specified basis. Notes ===== The ``metric`` parameter can be: ``metric = False`` symmetric metric (in Riemannian geometry) ``metric = True`` antisymmetric metric (for spinor calculus) ``metric = None`` there is no metric ``metric`` can be an object having ``name`` and ``antisym`` attributes. If there is a metric the metric is used to raise and lower indices. In the case of antisymmetric metric, the following raising and lowering conventions will be adopted: ``psi(a) = g(a, b)*psi(-b); chi(-a) = chi(b)*g(-b, -a)`` ``g(-a, b) = delta(-a, b); g(b, -a) = -delta(a, -b)`` where ``delta(-a, b) = delta(b, -a)`` is the ``Kronecker delta`` (see ``TensorIndex`` for the conventions on indices). If there is no metric it is not possible to raise or lower indices; e.g. the index of the defining representation of ``SU(N)`` is 'covariant' and the conjugate representation is 'contravariant'; for ``N > 2`` they are linearly independent. ``eps_dim`` is by default equal to ``dim``, if the latter is an integer; else it can be assigned (for use in naive dimensional regularization); if ``eps_dim`` is not an integer ``epsilon`` is ``None``. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> Lorentz.metric metric(Lorentz,Lorentz) Examples with metric data added, this means it is working on a fixed basis: >>> Lorentz.data = [1, -1, -1, -1] >>> Lorentz TensorIndexType(Lorentz, 0) >>> Lorentz.data [[1 0 0 0] [0 -1 0 0] [0 0 -1 0] [0 0 0 -1]] """ def __new__(cls, name, metric=False, dim=None, eps_dim=None, dummy_fmt=None): if isinstance(name, string_types): name = Symbol(name) obj = Basic.__new__(cls, name, S.One if metric else S.Zero) obj._name = str(name) if not dummy_fmt: obj._dummy_fmt = '%s_%%d' % obj.name else: obj._dummy_fmt = '%s_%%d' % dummy_fmt if metric is None: obj.metric_antisym = None obj.metric = None else: if metric in (True, False, 0, 1): metric_name = 'metric' obj.metric_antisym = metric else: metric_name = metric.name obj.metric_antisym = metric.antisym sym2 = TensorSymmetry(get_symmetric_group_sgs(2, obj.metric_antisym)) S2 = TensorType([obj]*2, sym2) obj.metric = S2(metric_name) obj.metric._matrix_behavior = True obj._dim = dim obj._delta = obj.get_kronecker_delta() obj._eps_dim = eps_dim if eps_dim else dim obj._epsilon = obj.get_epsilon() obj._autogenerated = [] obj._data = None return obj @property def auto_right(self): if not hasattr(self, '_auto_right'): self._auto_right = TensorIndex("auto_right", self) return self._auto_right @property def auto_left(self): if not hasattr(self, '_auto_left'): self._auto_left = TensorIndex("auto_left", self) return self._auto_left @property def auto_index(self): if not hasattr(self, '_auto_index'): self._auto_index = TensorIndex("auto_index", self) return self._auto_index @property def data(self): return self._data @data.setter def data(self, data): numpy = import_module('numpy') data = VTIDS.parse_data(data) if data.ndim > 2: raise ValueError("data have to be of rank 1 (diagonal metric) or 2.") if data.ndim == 1: if self.dim is not None: nda_dim = data.shape[0] if nda_dim != self.dim: raise ValueError("Dimension mismatch") dim = data.shape[0] newndarray = numpy.zeros((dim, dim), dtype=object) for i, val in enumerate(data): newndarray[i, i] = val data = newndarray dim1, dim2 = data.shape if dim1 != dim2: raise ValueError("Non-square matrix tensor.") if self.dim is not None: if self.dim != dim1: raise ValueError("Dimension mismatch") self._data = data self.metric.data = data @data.deleter def data(self): del self._data self._data = None del self.metric.data @property def name(self): return self._name @property def dim(self): return self._dim @property def delta(self): return self._delta @property def eps_dim(self): return self._eps_dim @property def epsilon(self): return self._epsilon @property def dummy_fmt(self): return self._dummy_fmt def get_kronecker_delta(self): sym2 = TensorSymmetry(get_symmetric_group_sgs(2)) S2 = TensorType([self]*2, sym2) delta = S2('KD') delta._matrix_behavior = True return delta def get_epsilon(self): if not isinstance(self._eps_dim, int): return None sym = TensorSymmetry(get_symmetric_group_sgs(self._eps_dim, 1)) Sdim = TensorType([self]*self._eps_dim, sym) epsilon = Sdim('Eps') return epsilon def __lt__(self, other): return self.name < other.name def __str__(self): return self.name __repr__ = __str__ @doctest_depends_on(modules=('numpy',)) class TensorIndex(Basic): """ Represents an abstract tensor index. Parameters ========== name : name of the index, or ``True`` if you want it to be automatically assigned tensortype : ``TensorIndexType`` of the index is_up : flag for contravariant index Attributes ========== ``name`` ``tensortype`` ``is_up`` Notes ===== Tensor indices are contracted with the Einstein summation convention. An index can be in contravariant or in covariant form; in the latter case it is represented prepending a ``-`` to the index name. Dummy indices have a name with head given by ``tensortype._dummy_fmt`` Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, TensorIndex, TensorSymmetry, TensorType, get_symmetric_group_sgs >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> i = TensorIndex('i', Lorentz); i i >>> sym1 = TensorSymmetry(*get_symmetric_group_sgs(1)) >>> S1 = TensorType([Lorentz], sym1) >>> A, B = S1('A,B') >>> A(i)*B(-i) A(L_0)*B(-L_0) If you want the index name to be automatically assigned, just put ``True`` in the ``name`` field, it will be generated using the reserved character ``_`` in front of its name, in order to avoid conflicts with possible existing indices: >>> i0 = TensorIndex(True, Lorentz) >>> i0 _i0 >>> i1 = TensorIndex(True, Lorentz) >>> i1 _i1 >>> A(i0)*B(-i1) A(_i0)*B(-_i1) >>> A(i0)*B(-i0) A(L_0)*B(-L_0) """ def __new__(cls, name, tensortype, is_up=True): if isinstance(name, string_types): name_symbol = Symbol(name) elif isinstance(name, Symbol): name_symbol = name elif name is True: name = "_i{0}".format(len(tensortype._autogenerated)) name_symbol = Symbol(name) tensortype._autogenerated.append(name_symbol) else: raise ValueError("invalid name") obj = Basic.__new__(cls, name_symbol, tensortype, S.One if is_up else S.Zero) obj._name = str(name) obj._tensortype = tensortype obj._is_up = is_up return obj @property def name(self): return self._name @property def tensortype(self): return self._tensortype @property def is_up(self): return self._is_up def _print(self): s = self._name if not self._is_up: s = '-%s' % s return s def __lt__(self, other): return (self._tensortype, self._name) < (other._tensortype, other._name) def __neg__(self): t1 = TensorIndex(self._name, self._tensortype, (not self._is_up)) return t1 def tensor_indices(s, typ): """ Returns list of tensor indices given their names and their types Parameters ========== s : string of comma separated names of indices typ : list of ``TensorIndexType`` of the indices Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> a, b, c, d = tensor_indices('a,b,c,d', Lorentz) """ if isinstance(s, str): a = [x.name for x in symbols(s, seq=True)] else: raise ValueError('expecting a string') tilist = [TensorIndex(i, typ) for i in a] if len(tilist) == 1: return tilist[0] return tilist @doctest_depends_on(modules=('numpy',)) class TensorSymmetry(Basic): """ Monoterm symmetry of a tensor Parameters ========== bsgs : tuple ``(base, sgs)`` BSGS of the symmetry of the tensor Attributes ========== ``base`` : base of the BSGS ``generators`` : generators of the BSGS ``rank`` : rank of the tensor Notes ===== A tensor can have an arbitrary monoterm symmetry provided by its BSGS. Multiterm symmetries, like the cyclic symmetry of the Riemann tensor, are not covered. See Also ======== sympy.combinatorics.tensor_can.get_symmetric_group_sgs Examples ======== Define a symmetric tensor >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, TensorSymmetry, TensorType, get_symmetric_group_sgs >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> sym2 = TensorSymmetry(get_symmetric_group_sgs(2)) >>> S2 = TensorType([Lorentz]*2, sym2) >>> V = S2('V') """ def __new__(cls, *args, **kw_args): if len(args) == 1: base, generators = args[0] elif len(args) == 2: base, generators = args else: raise TypeError("bsgs required, either two separate parameters or one tuple") if not isinstance(base, Tuple): base = Tuple(*base) if not isinstance(generators, Tuple): generators = Tuple(*generators) obj = Basic.__new__(cls, base, generators, **kw_args) return obj @property def base(self): return self.args[0] @property def generators(self): return self.args[1] @property def rank(self): return self.args[1][0].size - 2 def tensorsymmetry(*args): """ Return a ``TensorSymmetry`` object. One can represent a tensor with any monoterm slot symmetry group using a BSGS. ``args`` can be a BSGS ``args[0]`` base ``args[1]`` sgs Usually tensors are in (direct products of) representations of the symmetric group; ``args`` can be a list of lists representing the shapes of Young tableaux Notes ===== For instance: ``[[1]]`` vector ``[[1]*n]`` symmetric tensor of rank ``n`` ``[[n]]`` antisymmetric tensor of rank ``n`` ``[[2, 2]]`` monoterm slot symmetry of the Riemann tensor ``[[1],[1]]`` vector*vector ``[[2],[1],[1]`` (antisymmetric tensor)*vector*vector Notice that with the shape ``[2, 2]`` we associate only the monoterm symmetries of the Riemann tensor; this is an abuse of notation, since the shape ``[2, 2]`` corresponds usually to the irreducible representation characterized by the monoterm symmetries and by the cyclic symmetry. Examples ======== Symmetric tensor using a Young tableau >>> from sympy.tensor.tensor import TensorIndexType, TensorType, tensorsymmetry >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> sym2 = tensorsymmetry([1, 1]) >>> S2 = TensorType([Lorentz]*2, sym2) >>> V = S2('V') Symmetric tensor using a BSGS >>> from sympy.tensor.tensor import TensorSymmetry, get_symmetric_group_sgs >>> sym2 = tensorsymmetry(*get_symmetric_group_sgs(2)) >>> S2 = TensorType([Lorentz]*2, sym2) >>> V = S2('V') """ from sympy.combinatorics import Permutation def tableau2bsgs(a): if len(a) == 1: # antisymmetric vector n = a[0] bsgs = get_symmetric_group_sgs(n, 1) else: if all(x == 1 for x in a): # symmetric vector n = len(a) bsgs = get_symmetric_group_sgs(n) elif a == [2, 2]: bsgs = riemann_bsgs else: raise NotImplementedError return bsgs if not args: return TensorSymmetry(Tuple(), Tuple(Permutation(1))) if len(args) == 2 and isinstance(args[1][0], Permutation): return TensorSymmetry(args) base, sgs = tableau2bsgs(args[0]) for a in args[1:]: basex, sgsx = tableau2bsgs(a) base, sgs = bsgs_direct_product(base, sgs, basex, sgsx) return TensorSymmetry(Tuple(base, sgs)) @doctest_depends_on(modules=('numpy',)) class TensorType(Basic): """ Class of tensor types. Parameters ========== index_types : list of ``TensorIndexType`` of the tensor indices symmetry : ``TensorSymmetry`` of the tensor Attributes ========== ``index_types`` ``symmetry`` ``types`` : list of ``TensorIndexType`` without repetitions Examples ======== Define a symmetric tensor >>> from sympy.tensor.tensor import TensorIndexType, tensorsymmetry, TensorType >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> sym2 = tensorsymmetry([1, 1]) >>> S2 = TensorType([Lorentz]*2, sym2) >>> V = S2('V') """ is_commutative = False def __new__(cls, index_types, symmetry, **kw_args): assert symmetry.rank == len(index_types) obj = Basic.__new__(cls, Tuple(*index_types), symmetry, **kw_args) return obj @property def index_types(self): return self.args[0] @property def symmetry(self): return self.args[1] @property def types(self): return sorted(set(self.index_types), key=lambda x: x.name) def __str__(self): return 'TensorType(%s)' % ([str(x) for x in self.index_types]) def __call__(self, s, comm=0, matrix_behavior=0): """ Return a TensorHead object or a list of TensorHead objects. ``s`` name or string of names ``comm``: commutation group number see ``_TensorManager.set_comm`` Examples ======== Define symmetric tensors ``V``, ``W`` and ``G``, respectively commuting, anticommuting and with no commutation symmetry >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensorsymmetry, TensorType, canon_bp >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> a, b = tensor_indices('a,b', Lorentz) >>> sym2 = tensorsymmetry([1]*2) >>> S2 = TensorType([Lorentz]*2, sym2) >>> V = S2('V') >>> W = S2('W', 1) >>> G = S2('G', 2) >>> canon_bp(V(a, b)*V(-b, -a)) V(L_0, L_1)*V(-L_0, -L_1) >>> canon_bp(W(a, b)*W(-b, -a)) 0 """ if isinstance(s, str): names = [x.name for x in symbols(s, seq=True)] else: raise ValueError('expecting a string') if len(names) == 1: return TensorHead(names[0], self, comm, matrix_behavior=matrix_behavior) else: return [TensorHead(name, self, comm, matrix_behavior=matrix_behavior) for name in names] def tensorhead(name, typ, sym, comm=0, matrix_behavior=0): """ Function generating tensorhead(s). Parameters ========== name : name or sequence of names (as in ``symbol``) typ : index types sym : same as ``*args`` in ``tensorsymmetry`` comm : commutation group number see ``_TensorManager.set_comm`` Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> a, b = tensor_indices('a,b', Lorentz) >>> A = tensorhead('A', [Lorentz]*2, [[1]*2]) >>> A(a, -b) A(a, -b) """ sym = tensorsymmetry(*sym) S = TensorType(typ, sym) th = S(name, comm, matrix_behavior=matrix_behavior) return th @doctest_depends_on(modules=('numpy',)) class TensorHead(Basic): """ Tensor head of the tensor Parameters ========== name : name of the tensor typ : list of TensorIndexType comm : commutation group number Attributes ========== ``name`` ``index_types`` ``rank`` ``types`` : equal to ``typ.types`` ``symmetry`` : equal to ``typ.symmetry`` ``comm`` : commutation group Notes ===== A ``TensorHead`` belongs to a commutation group, defined by a symbol on number ``comm`` (see ``_TensorManager.set_comm``); tensors in a commutation group have the same commutation properties; by default ``comm`` is ``0``, the group of the commuting tensors. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensorsymmetry, TensorType >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> sym2 = tensorsymmetry([1]*2) >>> S2 = TensorType([Lorentz]*2, sym2) >>> A = S2('A') Examples with ndarray values: >>> from sympy.tensor.tensor import tensor_indices, tensorhead >>> Lorentz.data = [1, -1, -1, -1] >>> i0, i1 = tensor_indices('i0:2', Lorentz) >>> A.data = [[j+2*i for j in range(4)] for i in range(4)] in order to retrieve data, it is also necessary to specify abstract indices enclosed by round brackets, then numerical indices inside square brackets. >>> A(i0, i1)[0, 0] 0 >>> A(i0, i1)[2, 3] == 3+2*2 True Notice that square brackets create a valued tensor expression instance: >>> A(i0, i1) A(i0, i1) To view the data, just type: >>> A.data [[0 1 2 3] [2 3 4 5] [4 5 6 7] [6 7 8 9]] Turning to a tensor expression, covariant indices get the corresponding data corrected by the metric: >>> A(i0, -i1).data [[0 -1 -2 -3] [2 -3 -4 -5] [4 -5 -6 -7] [6 -7 -8 -9]] >>> A(-i0, -i1).data [[0 -1 -2 -3] [-2 3 4 5] [-4 5 6 7] [-6 7 8 9]] while if all indices are contravariant, the ``ndarray`` remains the same >>> A(i0, i1).data [[0 1 2 3] [2 3 4 5] [4 5 6 7] [6 7 8 9]] When all indices are contracted and data are added to the tensor, it will return a scalar resulting from all contractions: >>> A(i0, -i0) -18 """ is_commutative = False def __new__(cls, name, typ, comm=0, matrix_behavior=0, **kw_args): if isinstance(name, string_types): name_symbol = Symbol(name) elif isinstance(name, Symbol): name_symbol = name else: raise ValueError("invalid name") comm2i = TensorManager.comm_symbols2i(comm) obj = Basic.__new__(cls, name_symbol, typ, **kw_args) obj._matrix_behavior = matrix_behavior obj._name = obj.args[0].name obj._rank = len(obj.index_types) obj._types = typ.types obj._symmetry = typ.symmetry obj._comm = comm2i obj._data = None return obj @property def name(self): return self._name @property def rank(self): return self._rank @property def types(self): return self._types[:] @property def symmetry(self): return self._symmetry @property def typ(self): return self.args[1] @property def comm(self): return self._comm @property def index_types(self): return self.args[1].index_types[:] def __lt__(self, other): return (self.name, self.index_types) < (other.name, other.index_types) def commutes_with(self, other): """ Returns ``0`` if ``self`` and ``other`` commute, ``1`` if they anticommute. Returns ``None`` if ``self`` and ``other`` neither commute nor anticommute. """ r = TensorManager.get_comm(self._comm, other._comm) return r def _print(self): return '%s(%s)' %(self.name, ','.join([str(x) for x in self.index_types])) def __call__(self, *indices): """ Returns a tensor with indices. There is a special behavior in case of indices denoted by ``True``, they are considered auto-matrix indices, their slots are automatically filled, and confer to the tensor the behavior of a matrix or vector upon multiplication with another tensor containing auto-matrix indices of the same ``TensorIndexType``. This means indices get summed over the same way as in matrix multiplication. For matrix behavior, define two auto-matrix indices, for vector behavior define just one. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> a, b = tensor_indices('a,b', Lorentz) >>> A = tensorhead('A', [Lorentz]*2, [[1]*2]) >>> t = A(a, -b) >>> t A(a, -b) To use the auto-matrix index behavior, just put a ``True`` on the desired index position. >>> r = A(True, True) >>> r A(auto_left, -auto_right) Here ``auto_left`` and ``auto_right`` are automatically generated tensor indices, they are only two for every ``TensorIndexType`` and can be assigned to just one or two indices of a given type. Auto-matrix indices can be assigned many times in a tensor, if indices are of different ``TensorIndexType`` >>> Spinor = TensorIndexType('Spinor', dummy_fmt='S') >>> B = tensorhead('B', [Lorentz, Lorentz, Spinor, Spinor], [[1]*4]) >>> s = B(True, True, True, True) >>> s B(auto_left, -auto_right, auto_left, -auto_right) Here, ``auto_left`` and ``auto_right`` are repeated twice, but they are not the same indices, as they refer to different ``TensorIndexType``s. Auto-matrix indices are automatically contracted upon multiplication, >>> r*s A(auto_left, -L_0)*B(L_0, -auto_right, auto_left, -auto_right) The multiplication algorithm has found an ``auto_right`` index in ``A`` and an ``auto_left`` index in ``B`` referring to the same ``TensorIndexType`` (``Lorentz``), so they have been contracted. Auto-matrix indices can be accessed from the ``TensorIndexType``: >>> Lorentz.auto_right auto_right >>> Lorentz.auto_left auto_left There is a special case, in which the ``True`` parameter is not needed to declare an auto-matrix index, i.e. when the matrix behavior has been declared upon ``TensorHead`` construction, in that case the last one or two tensor indices may be omitted, so that they automatically become auto-matrix indices: >>> C = tensorhead('C', [Lorentz, Lorentz], [[1]*2], matrix_behavior=True) >>> C() C(auto_left, -auto_right) """ matrix_behavior_kinds = dict() if len(indices) != len(self.index_types): if not self._matrix_behavior: raise ValueError('wrong number of indices') # _matrix_behavior is True, so take the last one or two missing # indices as auto-matrix indices: ldiff = len(self.index_types) - len(indices) if ldiff > 2: raise ValueError('wrong number of indices') if ldiff == 2: mat_ind = [len(indices), len(indices) + 1] elif ldiff == 1: mat_ind = [len(indices)] not_equal = True else: not_equal = False mat_ind = [i for i, e in enumerate(indices) if e is True] if mat_ind: not_equal = True indices = tuple([_ for _ in indices if _ is not True]) for i, el in enumerate(indices): if not isinstance(el, TensorIndex): not_equal = True break if el._tensortype != self.index_types[i]: not_equal = True break if not_equal: for el in mat_ind: eltyp = self.index_types[el] if eltyp in matrix_behavior_kinds: elind = -self.index_types[el].auto_right matrix_behavior_kinds[eltyp].append(elind) else: elind = self.index_types[el].auto_left matrix_behavior_kinds[eltyp] = [elind] indices = indices[:el] + (elind,) + indices[el:] components = [self] tids = TIDS.from_components_and_indices(components, indices) if self.data is not None: tids = VTIDS(tids.components, tids.free, tids.dum, self.data) tids.correct_signature_from_indices(self.data, indices, tids.free, tids.dum) numpy = import_module('numpy') if not isinstance(tids.data, numpy.ndarray): return tids.data tmul = TensMul.from_TIDS(S.One, tids) tmul._matrix_behavior_kinds = matrix_behavior_kinds return tmul def __pow__(self, other): if self.data is None: raise ValueError("No power on abstract tensors.") numpy = import_module('numpy') metrics = [_.data for _ in self.args[1].args[0]] marray = self.data for metric in metrics: marray = numpy.tensordot(marray, numpy.tensordot(metric, marray, (1, 0)), (0, 0)) pow2 = marray[()] return pow2 ** (Rational(1, 2) * other) @property def data(self): return self._data @data.setter def data(self, data): data = VTIDS.parse_data(data) for dim, indextype in zip(data.shape, self.index_types): if indextype.data is None: raise ValueError("index type {} has no data associated (needed to raise/lower index)".format(indextype)) if indextype.dim is None: continue if dim != indextype.dim: raise ValueError("wrong dimension of ndarray") self._data = data @data.deleter def data(self): del self._data self._data = None def applyfunc(self, func): th = TensorHead(*self.args) th.data = func(self.data) return th def __iter__(self): return self.data.flatten().__iter__() def strip(self): """ Return an identical ``TensorHead``, just with ``ndarray`` data removed. """ return TensorHead(*self.args) @doctest_depends_on(modules=('numpy',)) class TensExpr(Basic): """ Abstract base class for tensor expressions Notes ===== A tensor expression is an expression formed by tensors; currently the sums of tensors are distributed. A ``TensExpr`` can be a ``TensAdd`` or a ``TensMul``. ``TensAdd`` objects are put in canonical form using the Butler-Portugal algorithm for canonicalization under monoterm symmetries. ``TensMul`` objects are formed by products of component tensors, and include a coefficient, which is a SymPy expression. In the internal representation contracted indices are represented by ``(ipos1, ipos2, icomp1, icomp2)``, where ``icomp1`` is the position of the component tensor with contravariant index, ``ipos1`` is the slot which the index occupies in that component tensor. Contracted indices are therefore nameless in the internal representation. """ _op_priority = 11.0 is_commutative = False def __neg__(self): return self*S.NegativeOne def __abs__(self): raise NotImplementedError def __add__(self, other): raise NotImplementedError def __radd__(self, other): raise NotImplementedError def __sub__(self, other): raise NotImplementedError def __rsub__(self, other): raise NotImplementedError def __mul__(self, other): raise NotImplementedError def __rmul__(self, other): raise NotImplementedError def __pow__(self, other): if self.data is None: raise ValueError("No power without ndarray data.") numpy = import_module('numpy') free = self.free marray = self.data for metric in free: marray = numpy.tensordot( marray, numpy.tensordot( metric[0]._tensortype.data, marray, (1, 0) ), (0, 0) ) pow2 = marray[()] return pow2 ** (Rational(1, 2) * other) def __rpow__(self, other): raise NotImplementedError def __div__(self, other): raise NotImplementedError def __rdiv__(self, other): raise NotImplementedError() __truediv__ = __div__ __rtruediv__ = __rdiv__ @doctest_depends_on(modules=('numpy',)) def get_matrix(self): """ Returns ndarray data as a matrix, if data are available and ndarray dimension does not exceed 2. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensorsymmetry, TensorType >>> from sympy import ones >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> sym2 = tensorsymmetry([1]*2) >>> S2 = TensorType([Lorentz]*2, sym2) >>> A = S2('A') >>> from sympy.tensor.tensor import tensor_indices, tensorhead >>> Lorentz.data = [1, -1, -1, -1] >>> i0, i1 = tensor_indices('i0:2', Lorentz) >>> A.data = [[j+2*i for j in range(4)] for i in range(4)] >>> A(i0, i1).get_matrix() Matrix([ [0, 1, 2, 3], [2, 3, 4, 5], [4, 5, 6, 7], [6, 7, 8, 9]]) It is possible to perform usual operation on matrices, such as the matrix multiplication: >>> A(i0, i1).get_matrix()*ones(4, 1) Matrix([ [ 6], [14], [22], [30]]) """ if 0 < self.rank <= 2: rows = self.data.shape[0] columns = self.data.shape[1] if self.rank == 2 else 1 if self.rank == 2: mat_list = [] * rows for i in range(rows): mat_list.append([]) for j in range(columns): mat_list[i].append(self[i, j]) else: mat_list = [None] * rows for i in range(rows): mat_list[i] = self[i] return Matrix(mat_list) else: raise NotImplementedError( "missing multidimensional reduction to matrix.") def _eval_simplify(self, ratio, measure): # this is a way to simplify a tensor expression. # This part walks for all `TensorHead`s appearing in the tensor expr # and looks for `simplify_this_type`, to specifically act on a subexpr # containing one type of `TensorHead` instance only: expr = self for i in list(set(self.components)): if hasattr(i, 'simplify_this_type'): expr = i.simplify_this_type(expr) # TODO: missing feature, perform metric contraction. return expr def strip(self): """ Return an identical tensor expression, just with ``ndarray`` data removed. """ return self.func(*self.args) @doctest_depends_on(modules=('numpy',)) class TensAdd(TensExpr): """ Sum of tensors Parameters ========== free_args : list of the free indices Attributes ========== ``args`` : tuple of addends ``rank`` : rank of the tensor ``free_args`` : list of the free indices in sorted order Notes ===== Sum of more than one tensor are put automatically in canonical form. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensorhead, tensor_indices >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> a, b = tensor_indices('a,b', Lorentz) >>> p, q = tensorhead('p,q', [Lorentz], [[1]]) >>> t = p(a) + q(a); t p(a) + q(a) >>> t(b) p(b) + q(b) Examples with data added to the tensor expression: >>> from sympy import eye >>> Lorentz.data = [1, -1, -1, -1] >>> a, b = tensor_indices('a, b', Lorentz) >>> p.data = [2, 3, -2, 7] >>> q.data = [2, 3, -2, 7] >>> t = p(a) + q(a); t p(a) + q(a) >>> t(b) p(b) + q(b) The following are: 2**2 - 3**2 - 2**2 - 7**2 ==> -58 >>> p(a)*p(-a) -58 >>> p(a)**2 -58 """ def __new__(cls, *args, **kw_args): args = [sympify(x) for x in args if x] old_args = args[:] args, data = TensAdd._tensAdd_flatten(args) if not args: return S.Zero # replace auto-matrix indices so that they are the same in all addends args = TensAdd._tensAdd_check_automatrix(args) # now check that all addends have the same indices: TensAdd._tensAdd_check(args) args = Tuple(*args) # if TensAdd has only 1 TensMul element in its `args`: if len(args) == 1 and isinstance(args[0], TensMul): obj = Basic.__new__(cls, *args, **kw_args) obj._data = data return obj # canonicalize all TensMul args = [x.canon_bp() for x in args if x] args = [x for x in args if x] # if there are no more args (i.e. have cancelled out), # just return zero: if not args: return S.Zero # collect canonicalized terms args.sort(key=lambda x: (x.components, x.free, x.dum)) a = TensAdd._tensAdd_collect_terms(args) if not a: return S.Zero # it there is only a component tensor return it if len(a) == 1: if data is not None: a[0].data = old_args[0].data return a[0] args = Tuple(*args) obj = Basic.__new__(cls, *args, **kw_args) obj._args = tuple(a) obj._data = data return obj @staticmethod def _tensAdd_flatten(args): # flatten TensAdd, coerce terms which are not tensors to tensors data_list = [] if not all(isinstance(x, TensExpr) for x in args): args1 = [] for x in args: if isinstance(x, TensExpr): if isinstance(x, TensAdd): args1.extend(list(x.args)) else: args1.append(x) args1 = [x for x in args1 if isinstance(x, TensExpr) and x._coeff] args2 = [x for x in args if not isinstance(x, TensExpr)] t1 = TensMul.from_data(Add(*args2), [], [], []) args = [t1] + args1 a = [] for x in args: data_list.append(x.data) if isinstance(x, TensAdd): a.extend(list(x.args)) else: a.append(x) args = [x for x in a if x._coeff] data_p = [_ is None for _ in data_list] data = None if data_p: if any(data_p) != all(data_p): raise ValueError("attempting to mix tensors with data and tensors without data") if not any(data_p): data = S.Zero for i in args: if isinstance(i, TensAdd): data += i.data else: data += i.coeff * i.data if not args[0].rank: # autodrop point return data return args, data @staticmethod def _tensAdd_check_automatrix(args): # check that all automatrix indices are the same. # if there are no addends, just return. if not args: return args # @type auto_left_types: set auto_left_types = set([]) auto_right_types = set([]) args_auto_left_types = [] args_auto_right_types = [] for i, arg in enumerate(args): arg_auto_left_types = set([]) arg_auto_right_types = set([]) for index in arg.get_indices(): # @type index: TensorIndex if index in (index._tensortype.auto_left, -index._tensortype.auto_left): auto_left_types.add(index._tensortype) arg_auto_left_types.add(index._tensortype) if index in (index._tensortype.auto_right, -index._tensortype.auto_right): auto_right_types.add(index._tensortype) arg_auto_right_types.add(index._tensortype) args_auto_left_types.append(arg_auto_left_types) args_auto_right_types.append(arg_auto_right_types) for arg, aas_left, aas_right in zip(args, args_auto_left_types, args_auto_right_types): missing_left = auto_left_types - aas_left missing_right = auto_right_types - aas_right missing_intersection = missing_left & missing_right for j in missing_intersection: args[i] *= j.delta(j.auto_left, -j.auto_right) if missing_left != missing_right: raise ValueError("cannot determine how to add auto-matrix indices on some args") return args @staticmethod def _tensAdd_check(args): # check that all addends have the same free indices indices0 = set([x[0] for x in args[0].free]) list_indices = [set([y[0] for y in x.free]) for x in args[1:]] if not all(x == indices0 for x in list_indices): raise ValueError('all tensors must have the same indices') @staticmethod def _tensAdd_collect_terms(args): # collect TensMul terms differing at most by their coefficient a = [] prev = args[0] prev_coeff = prev._coeff changed = False for x in args[1:]: # if x and prev have the same tensor, update the coeff of prev if x.components == prev.components \ and x.free == prev.free and x.dum == prev.dum: prev_coeff = prev_coeff + x._coeff changed = True op = 0 else: # x and prev are different; if not changed, prev has not # been updated; store it if not changed: a.append(prev) else: # get a tensor from prev with coeff=prev_coeff and store it if prev_coeff: t = TensMul.from_data(prev_coeff, prev.components, prev.free, prev.dum) a.append(t) # move x to prev op = 1 pprev, prev = prev, x pprev_coeff, prev_coeff = prev_coeff, x._coeff changed = False # if the case op=0 prev was not stored; store it now # in the case op=1 x was not stored; store it now (as prev) if op == 0 and prev_coeff: prev = TensMul.from_data(prev_coeff, prev.components, prev.free, prev.dum) a.append(prev) elif op == 1: a.append(prev) return a @property def rank(self): return self.args[0].rank @property def free_args(self): return self.args[0].free_args def __call__(self, *indices): """Returns tensor with ordered free indices replaced by ``indices`` Parameters ========== indices Examples ======== >>> from sympy import Symbol >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensorhead >>> D = Symbol('D') >>> Lorentz = TensorIndexType('Lorentz', dim=D, dummy_fmt='L') >>> i0,i1,i2,i3,i4 = tensor_indices('i0:5', Lorentz) >>> p, q = tensorhead('p,q', [Lorentz], [[1]]) >>> g = Lorentz.metric >>> t = p(i0)*p(i1) + g(i0,i1)*q(i2)*q(-i2) >>> t(i0,i2) metric(i0, i2)*q(L_0)*q(-L_0) + p(i0)*p(i2) >>> t(i0,i1) - t(i1,i0) 0 """ free_args = self.free_args indices = list(indices) if [x._tensortype for x in indices] != [x._tensortype for x in free_args]: raise ValueError('incompatible types') if indices == free_args: return self index_tuples = list(zip(free_args, indices)) a = [x.fun_eval(*index_tuples) for x in self.args] res = TensAdd(*a) return res def canon_bp(self): """ canonicalize using the Butler-Portugal algorithm for canonicalization under monoterm symmetries. """ args = [x.canon_bp() for x in self.args] res = TensAdd(*args) return res def equals(self, other): other = sympify(other) if isinstance(other, TensMul) and other._coeff == 0: return all(x._coeff == 0 for x in self.args) if isinstance(other, TensExpr): if self.rank != other.rank: return False if isinstance(other, TensAdd): if set(self.args) != set(other.args): return False t = self - other if not isinstance(t, TensExpr): return t == 0 else: if isinstance(t, TensMul): return t._coeff == 0 else: return all(x._coeff == 0 for x in t.args) def __eq__(self, other): return self.equals(other) def __add__(self, other): return TensAdd(self, other) def __radd__(self, other): return TensAdd(other, self) def __sub__(self, other): return TensAdd(self, -other) def __rsub__(self, other): return TensAdd(other, -self) def __mul__(self, other): tadd = TensAdd(*(x*other for x in self.args)) if not isinstance(tadd, TensExpr): if (self.data is not None): tadd.data = self.data * other return tadd if self.data is not None: if isinstance(other, TensExpr): if other.data is None: raise ValueError("Cannot multiply abstract and valued tensors.") data = VTIDS._contract_ndarray(self.args[0].free, other.free, self.data, other.data) if data.ndim == 0: return data[()] else: data = self.data * other else: data = None tadd.data = data return tadd def __rmul__(self, other): tadd = self*other if self.data is not None: tadd.data = other*self.data return tadd def __div__(self, other): other = sympify(other) if isinstance(other, TensExpr): raise ValueError('cannot divide by a tensor') tadd = TensAdd(*(x/other for x in self.args)) if self.data is not None: tadd.data = self.data / other return tadd def __rdiv__(self, other): raise ValueError('cannot divide by a tensor') def __getitem__(self, item): return self.data[item] __truediv__ = __div__ __truerdiv__ = __rdiv__ def _hashable_content(self): return tuple(self.args) def __hash__(self): return super(TensAdd, self).__hash__() def __ne__(self, other): return not (self == other) def contract_delta(self, delta): args = [x.contract_delta(delta) for x in self.args] t = TensAdd(*args) return canon_bp(t) def contract_metric(self, g): """ Raise or lower indices with the metric ``g`` Parameters ========== g : metric contract_all : if True, eliminate all ``g`` which are contracted Notes ===== see the ``TensorIndexType`` docstring for the contraction conventions """ args = [x.contract_metric(g) for x in self.args] t = TensAdd(*args) return canon_bp(t) def fun_eval(self, *index_tuples): """ Return a tensor with free indices substituted according to ``index_tuples`` Parameters ========== index_types : list of tuples ``(old_index, new_index)`` Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> i, j, k, l = tensor_indices('i,j,k,l', Lorentz) >>> A, B = tensorhead('A,B', [Lorentz]*2, [[1]*2]) >>> t = A(i, k)*B(-k, -j) + A(i, -j) >>> t.fun_eval((i, k),(-j, l)) A(k, L_0)*B(l, -L_0) + A(k, l) """ args = self.args args1 = [] for x in args: y = x.fun_eval(*index_tuples) args1.append(y) return TensAdd(*args1) def substitute_indices(self, *index_tuples): """ Return a tensor with free indices substituted according to ``index_tuples`` Parameters ========== index_types : list of tuples ``(old_index, new_index)`` Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> i, j, k, l = tensor_indices('i,j,k,l', Lorentz) >>> A, B = tensorhead('A,B', [Lorentz]*2, [[1]*2]) >>> t = A(i, k)*B(-k, -j); t A(i, L_0)*B(-L_0, -j) >>> t.substitute_indices((i,j), (j, k)) A(j, L_0)*B(-L_0, -k) """ args = self.args args1 = [] for x in args: y = x.substitute_indices(*index_tuples) args1.append(y) return TensAdd(*args1) def _print(self): a = [] args = self.args for x in args: a.append(str(x)) a.sort() s = ' + '.join(a) s = s.replace('+ -', '- ') return s @staticmethod def from_TIDS_list(coeff, tids_list): """ Given a list of coefficients and a list of `TIDS` objects, construct a `TensAdd` instance, equivalent to the one that would result from creating single instances of `TensMul` and then adding them. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensorhead, TensAdd >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> i, j = tensor_indices('i,j', Lorentz) >>> A, B = tensorhead('A,B', [Lorentz]*2, [[1]*2]) >>> eA = 3*A(i, j) >>> eB = 2*B(j, i) >>> t1 = eA._tids >>> t2 = eB._tids >>> c1 = eA.coeff >>> c2 = eB.coeff >>> TensAdd.from_TIDS_list([c1, c2], [t1, t2]) 2*B(i, j) + 3*A(i, j) If the coefficient parameter is a scalar, then it will be applied as a coefficient on all `TIDS` objects. >>> TensAdd.from_TIDS_list(4, [t1, t2]) 4*A(i, j) + 4*B(i, j) """ if not isinstance(coeff, (list, tuple, Tuple)): coeff = [coeff] * len(tids_list) tensmul_list = [TensMul.from_TIDS(c, t) for c, t in zip(coeff, tids_list)] return TensAdd(*tensmul_list) def applyfunc(self, func): """ Return a new ``TensAdd`` object, whose data ndarray will be the elementwise map of the current data ndarray by function ``func``. """ new_tadd = TensAdd(*self.args) new_tadd.data = func(self.data) return new_tadd @property def data(self): if hasattr(self, "_data"): return self._data return None @data.setter def data(self, data): # TODO: check data compatibility with properties of tensor. self._data = data @data.deleter def data(self): del self._data self._data = None def __iter__(self): if not self.data: raise ValueError("No iteration on abstract tensors") return self.data.flatten().__iter__() @doctest_depends_on(modules=('numpy',)) class TensMul(TensExpr): """ Product of tensors Parameters ========== coeff : SymPy coefficient of the tensor args Attributes ========== ``components`` : list of ``TensorHead`` of the component tensors ``types`` : list of nonrepeated ``TensorIndexType`` ``free`` : list of ``(ind, ipos, icomp)``, see Notes ``dum`` : list of ``(ipos1, ipos2, icomp1, icomp2)``, see Notes ``ext_rank`` : rank of the tensor counting the dummy indices ``rank`` : rank of the tensor ``coeff`` : SymPy coefficient of the tensor ``free_args`` : list of the free indices in sorted order ``is_canon_bp`` : ``True`` if the tensor in in canonical form Notes ===== ``args[0]`` list of ``TensorHead`` of the component tensors. ``args[1]`` list of ``(ind, ipos, icomp)`` where ``ind`` is a free index, ``ipos`` is the slot position of ``ind`` in the ``icomp``-th component tensor. ``args[2]`` list of tuples representing dummy indices. ``(ipos1, ipos2, icomp1, icomp2)`` indicates that the contravariant dummy index is the ``ipos1``-th slot position in the ``icomp1``-th component tensor; the corresponding covariant index is in the ``ipos2`` slot position in the ``icomp2``-th component tensor. """ def __new__(cls, coeff, *args, **kw_args): coeff = sympify(coeff) if len(args) == 2: components = args[0] indices = args[1] tids = TIDS.from_components_and_indices(components, indices) elif len(args) == 1: tids = args[0] components = tids.components indices = tids.to_indices() else: raise TypeError("wrong construction") for i in indices: assert isinstance(i, TensorIndex) t_components = Tuple(*components) t_indices = Tuple(*indices) obj = Basic.__new__(cls, coeff, t_components, t_indices) obj._types = [] for t in tids.components: obj._types.extend(t._types) obj._tids = tids obj._ext_rank = len(obj._tids.free) + 2*len(obj._tids.dum) obj._coeff = coeff obj._is_canon_bp = kw_args.get('is_canon_bp', False) obj._matrix_behavior_kinds = dict() return obj @staticmethod def from_data(coeff, components, free, dum, data=None, **kw_args): if data is None: tids = TIDS(components, free, dum) else: tids = VTIDS(components, free, dum, data) return TensMul.from_TIDS(coeff, tids, **kw_args) @staticmethod def from_TIDS(coeff, tids, **kw_args): # t_indices = tids.to_indices() if isinstance(tids, VTIDS) and len(tids.free) == 0: # autodrop point return coeff * tids.data[()] return TensMul(coeff, tids, **kw_args) @property def free_args(self): return sorted([x[0] for x in self.free]) @property def components(self): return self._tids.components[:] @property def free(self): return self._tids.free[:] @property def coeff(self): return self._coeff @property def dum(self): return self._tids.dum[:] @property def rank(self): return len(self.free) @property def types(self): return self._types[:] def equals(self, other): if other == 0: return self._coeff == 0 other = sympify(other) if not isinstance(other, TensExpr): assert not self.components return self._coeff == other res = self - other return res == 0 def _hashable_content(self): t = self.canon_bp() r = (t._coeff, tuple(t.components), \ tuple(sorted(t.free)), tuple(sorted(t.dum))) return r def __hash__(self): return super(TensMul, self).__hash__() def __eq__(self, other): # Basic's equality comparison considers 0 and a zero TensMul # as never equal, here is a workaround: if other == 0 and self.coeff == 0: return True # now call the Basic equality method, based on the args: return super(TensMul, self).__eq__(other) def __ne__(self, other): return not self == other def get_indices(self): """ Returns the list of indices of the tensor The indices are listed in the order in which they appear in the component tensors. The dummy indices are given a name which does not collide with the names of the free indices. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> m0, m1, m2 = tensor_indices('m0,m1,m2', Lorentz) >>> g = Lorentz.metric >>> p, q = tensorhead('p,q', [Lorentz], [[1]]) >>> t = p(m1)*g(m0,m2) >>> t.get_indices() [m1, m0, m2] """ indices = [None]*self._ext_rank start = 0 pos = 0 vpos = [] components = self.components for t in components: vpos.append(pos) pos += t._rank cdt = defaultdict(int) # if the free indices have names with dummy_fmt, start with an # index higher than those for the dummy indices # to avoid name collisions for indx, ipos, cpos in self.free: if indx._name.split('_')[0] == indx._tensortype._dummy_fmt[:-3]: cdt[indx._tensortype] = max(cdt[indx._tensortype], int(indx._name.split('_')[1]) + 1) start = vpos[cpos] indices[start + ipos] = indx for ipos1, ipos2, cpos1, cpos2 in self.dum: start1 = vpos[cpos1] start2 = vpos[cpos2] typ1 = components[cpos1].index_types[ipos1] assert typ1 == components[cpos2].index_types[ipos2] fmt = typ1._dummy_fmt nd = cdt[typ1] indices[start1 + ipos1] = TensorIndex(fmt % nd, typ1) indices[start2 + ipos2] = TensorIndex(fmt % nd, typ1, False) cdt[typ1] += 1 return indices def split(self): """ Returns a list of tensors, whose product is ``self`` Dummy indices contracted among different tensor components become free indices with the same name as the one used to represent the dummy indices. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> a, b, c, d = tensor_indices('a,b,c,d', Lorentz) >>> A, B = tensorhead('A,B', [Lorentz]*2, [[1]*2]) >>> t = A(a,b)*B(-b,c) >>> t A(a, L_0)*B(-L_0, c) >>> t.split() [A(a, L_0), B(-L_0, c)] """ indices = self.get_indices() pos = 0 components = self.components if not components: return [TensMul.from_data(self._coeff, [], [], [])] res = [] for t in components: t1 = t(*indices[pos:pos + t._rank]) pos += t._rank res.append(t1) res[0] = TensMul.from_data(self._coeff, res[0].components, res[0]._tids.free, res[0]._tids.dum, is_canon_bp=res[0]._is_canon_bp) return res def __add__(self, other): return TensAdd(self, other) def __radd__(self, other): return TensAdd(other, self) def __sub__(self, other): return TensAdd(self, -other) def __rsub__(self, other): return TensAdd(other, -self) def __mul__(self, other): """ Multiply two tensors using Einstein summation convention. If the two tensors have an index in common, one contravariant and the other covariant, in their product the indices are summed Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> m0, m1, m2 = tensor_indices('m0,m1,m2', Lorentz) >>> g = Lorentz.metric >>> p, q = tensorhead('p,q', [Lorentz], [[1]]) >>> t1 = p(m0) >>> t2 = q(-m0) >>> t1*t2 p(L_0)*q(-L_0) """ other = sympify(other) if not isinstance(other, TensExpr): coeff = self._coeff*other tmul = TensMul.from_TIDS(coeff, self._tids, is_canon_bp=self._is_canon_bp) tmul._matrix_behavior_kinds = self._matrix_behavior_kinds return tmul if isinstance(other, TensAdd): return TensAdd(*[self*x for x in other.args]) matrix_behavior_kinds = dict() self_matrix_behavior_kinds = self._matrix_behavior_kinds other_matrix_behavior_kinds = other._matrix_behavior_kinds for key, v1 in self_matrix_behavior_kinds.items(): if key in other_matrix_behavior_kinds: v2 = other_matrix_behavior_kinds[key] if len(v1) == 1: other = other.substitute_indices((v2[0], -v1[0])) if len(v2) == 2: matrix_behavior_kinds[key] = (v2[1],) elif len(v1) == 2: auto_index = v1[1]._tensortype.auto_index self = self.substitute_indices((v1[1], -auto_index)) other = other.substitute_indices((v2[0], auto_index)) if len(v2) == 1: matrix_behavior_kinds[key] = (v1[0],) elif len(v2) == 2: matrix_behavior_kinds[key] = (v1[0], v2[1]) else: matrix_behavior_kinds[key] = v1 for key, v2 in other_matrix_behavior_kinds.items(): if key in self_matrix_behavior_kinds: continue matrix_behavior_kinds[key] = v2 new_tids = self._tids*other._tids coeff = self._coeff*other._coeff tmul = TensMul.from_TIDS(coeff, new_tids) if isinstance(tmul, TensExpr): tmul._matrix_behavior_kinds = matrix_behavior_kinds return tmul def __rmul__(self, other): other = sympify(other) coeff = other*self._coeff tmul = TensMul.from_TIDS(coeff, self._tids) tmul._matrix_behavior_kinds = self._matrix_behavior_kinds return tmul def __div__(self, other): other = sympify(other) if isinstance(other, TensExpr): raise ValueError('cannot divide by a tensor') coeff = self._coeff/other tmul = TensMul.from_TIDS(coeff, self._tids, is_canon_bp=self._is_canon_bp) tmul._matrix_behavior_kinds = self._matrix_behavior_kinds return tmul def __rdiv__(self, other): raise ValueError('cannot divide by a tensor') def __getitem__(self, item): return self.coeff * self.data[item] __truediv__ = __div__ __truerdiv__ = __rdiv__ def sorted_components(self): """ Returns a tensor with sorted components calling the corresponding method in a `TIDS` object. """ new_tids, sign = self._tids.sorted_components() coeff = -self._coeff if sign == -1 else self._coeff t = TensMul.from_TIDS(coeff, new_tids) return t def perm2tensor(self, g, canon_bp=False): """ Returns the tensor corresponding to the permutation ``g`` For further details, see the method in `TIDS` with the same name. """ new_tids = self._tids.perm2tensor(g, canon_bp) coeff = self._coeff if g[-1] != len(g) - 1: coeff = -coeff res = TensMul.from_TIDS(coeff, new_tids, is_canon_bp=canon_bp) res._matrix_behavior_kinds = self._matrix_behavior_kinds return res def canon_bp(self): """ Canonicalize using the Butler-Portugal algorithm for canonicalization under monoterm symmetries. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> m0, m1, m2 = tensor_indices('m0,m1,m2', Lorentz) >>> A = tensorhead('A', [Lorentz]*2, [[2]]) >>> t = A(m0,-m1)*A(m1,-m0) >>> t.canon_bp() -A(L_0, L_1)*A(-L_0, -L_1) >>> t = A(m0,-m1)*A(m1,-m2)*A(m2,-m0) >>> t.canon_bp() 0 """ if self._is_canon_bp: return self if not self.components: return self t = self.sorted_components() g, dummies, msym, v = t._tids.canon_args() can = canonicalize(g, dummies, msym, *v) if can == 0: return S.Zero tmul = t.perm2tensor(can, True) tmul._matrix_behavior_kinds = self._matrix_behavior_kinds return tmul def contract_delta(self, delta): t = self.contract_metric(delta) return t def contract_metric(self, g): """ Raise or lower indices with the metric ``g`` ``g`` metric Notes ===== see the ``TensorIndexType`` docstring for the contraction conventions Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> m0, m1, m2 = tensor_indices('m0,m1,m2', Lorentz) >>> g = Lorentz.metric >>> p, q = tensorhead('p,q', [Lorentz], [[1]]) >>> t = p(m0)*q(m1)*g(-m0, -m1) >>> t.canon_bp() metric(L_0, L_1)*p(-L_0)*q(-L_1) >>> t.contract_metric(g).canon_bp() p(L_0)*q(-L_0) """ components = self.components antisym = g.index_types[0].metric_antisym #if not any(x == g for x in components): # return self # list of positions of the metric ``g`` gpos = [i for i, x in enumerate(components) if x == g] if not gpos: return self coeff = self._coeff tids = self._tids dum = tids.dum[:] free = tids.free[:] elim = set() for gposx in gpos: if gposx in elim: continue free1 = [x for x in free if x[-1] == gposx] dum1 = [x for x in dum if x[-2] == gposx or x[-1] == gposx] if not dum1: continue elim.add(gposx) if len(dum1) == 2: if not antisym: dum10, dum11 = dum1 if dum10[3] == gposx: # the index with pos p0 and component c0 is contravariant c0 = dum10[2] p0 = dum10[0] else: # the index with pos p0 and component c0 is covariant c0 = dum10[3] p0 = dum10[1] if dum11[3] == gposx: # the index with pos p1 and component c1 is contravariant c1 = dum11[2] p1 = dum11[0] else: # the index with pos p1 and component c1 is covariant c1 = dum11[3] p1 = dum11[1] dum.append((p0, p1, c0, c1)) else: dum10, dum11 = dum1 # change the sign to bring the indices of the metric to contravariant # form; change the sign if dum10 has the metric index in position 0 if dum10[3] == gposx: # the index with pos p0 and component c0 is contravariant c0 = dum10[2] p0 = dum10[0] if dum10[1] == 1: coeff = -coeff else: # the index with pos p0 and component c0 is covariant c0 = dum10[3] p0 = dum10[1] if dum10[0] == 0: coeff = -coeff if dum11[3] == gposx: # the index with pos p1 and component c1 is contravariant c1 = dum11[2] p1 = dum11[0] coeff = -coeff else: # the index with pos p1 and component c1 is covariant c1 = dum11[3] p1 = dum11[1] dum.append((p0, p1, c0, c1)) elif len(dum1) == 1: if not antisym: dp0, dp1, dc0, dc1 = dum1[0] if dc0 == dc1: # g(i, -i) typ = g.index_types[0] if typ._dim is None: raise ValueError('dimension not assigned') coeff = coeff*typ._dim else: # g(i0, i1)*p(-i1) if dc0 == gposx: p1 = dp1 c1 = dc1 else: p1 = dp0 c1 = dc0 ind, p, c = free1[0] free.append((ind, p1, c1)) else: dp0, dp1, dc0, dc1 = dum1[0] if dc0 == dc1: # g(i, -i) typ = g.index_types[0] if typ._dim is None: raise ValueError('dimension not assigned') coeff = coeff*typ._dim if dp0 < dp1: # g(i, -i) = -D with antisymmetric metric coeff = -coeff else: # g(i0, i1)*p(-i1) if dc0 == gposx: p1 = dp1 c1 = dc1 if dp0 == 0: coeff = -coeff else: p1 = dp0 c1 = dc0 ind, p, c = free1[0] free.append((ind, p1, c1)) dum = [x for x in dum if x not in dum1] free = [x for x in free if x not in free1] shift = 0 shifts = [0]*len(components) for i in range(len(components)): if i in elim: shift += 1 continue shifts[i] = shift free = [(ind, p, c - shifts[c]) for (ind, p, c) in free if c not in elim] dum = [(p0, p1, c0 - shifts[c0], c1 - shifts[c1]) for i, (p0, p1, c0, c1) in enumerate(dum) if c0 not in elim and c1 not in elim] components = [c for i, c in enumerate(components) if i not in elim] tids = TIDS(components, free, dum) res = TensMul.from_TIDS(coeff, tids) return res def substitute_indices(self, *index_tuples): """ Return a tensor with free indices substituted according to ``index_tuples`` ``index_types`` list of tuples ``(old_index, new_index)`` Note: this method will neither raise or lower the indices, it will just replace their symbol. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> i, j, k, l = tensor_indices('i,j,k,l', Lorentz) >>> A, B = tensorhead('A,B', [Lorentz]*2, [[1]*2]) >>> t = A(i, k)*B(-k, -j); t A(i, L_0)*B(-L_0, -j) >>> t.substitute_indices((i,j), (j, k)) A(j, L_0)*B(-L_0, -k) """ free = self.free free1 = [] for j, ipos, cpos in free: for i, v in index_tuples: if i._name == j._name and i._tensortype == j._tensortype: if i._is_up == j._is_up: free1.append((v, ipos, cpos)) else: free1.append((-v, ipos, cpos)) break else: free1.append((j, ipos, cpos)) return TensMul.from_data(self._coeff, self.components, free1, self.dum, self.data) def fun_eval(self, *index_tuples): """ Return a tensor with free indices substituted according to ``index_tuples`` ``index_types`` list of tuples ``(old_index, new_index)`` Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensorhead >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> i, j, k, l = tensor_indices('i,j,k,l', Lorentz) >>> A, B = tensorhead('A,B', [Lorentz]*2, [[1]*2]) >>> t = A(i, k)*B(-k, -j); t A(i, L_0)*B(-L_0, -j) >>> t.fun_eval((i, k),(-j, l)) A(k, L_0)*B(-L_0, l) """ free = self.free free1 = [] for j, ipos, cpos in free: # search j in index_tuples for i, v in index_tuples: if i == j: free1.append((v, ipos, cpos)) break else: free1.append((j, ipos, cpos)) return TensMul.from_data(self._coeff, self.components, free1, self.dum) def __call__(self, *indices): """Returns tensor with ordered free indices replaced by ``indices`` Examples ======== >>> from sympy import Symbol >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensorhead >>> D = Symbol('D') >>> Lorentz = TensorIndexType('Lorentz', dim=D, dummy_fmt='L') >>> i0,i1,i2,i3,i4 = tensor_indices('i0:5', Lorentz) >>> g = Lorentz.metric >>> p, q = tensorhead('p,q', [Lorentz], [[1]]) >>> t = p(i0)*q(i1)*q(-i1) >>> t(i1) p(i1)*q(L_0)*q(-L_0) """ free_args = self.free_args indices = list(indices) if [x._tensortype for x in indices] != [x._tensortype for x in free_args]: raise ValueError('incompatible types') if indices == free_args: return self t = self.fun_eval(*list(zip(free_args, indices))) return t def _print(self): if len(self.components) == 0: return str(self._coeff) indices = [str(ind) for ind in self.get_indices()] pos = 0 a = [] for t in self.components: if t._rank > 0: a.append('%s(%s)' % (t.name, ', '.join(indices[pos:pos + t._rank]))) else: a.append('%s' % t.name) pos += t._rank res = '*'. join(a) if self._coeff == S.One: return res elif self._coeff == -S.One: return '-%s' % res if self._coeff.is_Atom: return '%s*%s' % (self._coeff, res) else: return '(%s)*%s' %(self._coeff, res) def applyfunc(self, func): """ Return a new ``TensAdd`` object, whose data ndarray will be the elementwise map of the current data ndarray by function ``func``. """ new_tmul = TensMul(*self.args) tids = new_tmul._tids new_tmul._tids = VTIDS(tids.components, tids.free, tids.dum, func(self.data)) return new_tmul @property def data(self): if isinstance(self._tids, VTIDS): return self._tids.data return None @data.setter def data(self, data): # TODO: check data compatibility with properties of tensor. self._tids = VTIDS(self.components, self.free, self.dum, data) @data.deleter def data(self): self._tids = TIDS(self._tids.components, self._tids.free, self._tids.dum) self._data = None def __iter__(self): if self.data is None: raise ValueError("No iteration on abstract tensors") return (self.coeff * self.data.flatten()).__iter__() def canon_bp(p): """ Butler-Portugal canonicalization """ if isinstance(p, TensExpr): return p.canon_bp() return p def tensor_mul(*a): """ product of tensors """ if not a: return TensMul.from_data(S.One, [], [], []) t = a[0] for tx in a[1:]: t = t*tx return t def riemann_cyclic_replace(t_r): """ replace Riemann tensor with an equivalent expression ``R(m,n,p,q) -> 2/3*R(m,n,p,q) - 1/3*R(m,q,n,p) + 1/3*R(m,p,n,q)`` """ free = sorted(t_r.free, key=lambda x: x[1]) m, n, p, q = [x[0] for x in free] t0 = S(2)/3*t_r t1 = - S(1)/3*t_r.substitute_indices((m,m),(n,q),(p,n),(q,p)) t2 = S(1)/3*t_r.substitute_indices((m,m),(n,p),(p,n),(q,q)) t3 = t0 + t1 + t2 return t3 def riemann_cyclic(t2): """ replace each Riemann tensor with an equivalent expression satisfying the cyclic identity. This trick is discussed in the reference guide to Cadabra. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensorhead, riemann_cyclic >>> Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') >>> i, j, k, l = tensor_indices('i,j,k,l', Lorentz) >>> R = tensorhead('R', [Lorentz]*4, [[2, 2]]) >>> t = R(i,j,k,l)*(R(-i,-j,-k,-l) - 2*R(-i,-k,-j,-l)) >>> riemann_cyclic(t) 0 """ if isinstance(t2, TensMul): args = [t2] else: args = t2.args a1 = [x.split() for x in args] a2 = [[riemann_cyclic_replace(tx) for tx in y] for y in a1] a3 = [tensor_mul(*v) for v in a2] t3 = TensAdd(*a3) if not t3: return t3 else: return canon_bp(t3) def get_lines(ex, index_type): """ returns ``(lines, traces, rest)`` for an index type, where ``lines`` is the list of list of positions of a matrix line, ``traces`` is the list of list of traced matrix lines, ``rest`` is the rest of the elements ot the tensor. """ def _join_lines(a): i = 0 while i < len(a): x = a[i] xend = x[-1] hit = True while hit: hit = False for j in range(i + 1, len(a)): if j >= len(a): break if a[j][0] == xend: hit = True x.extend(a[j][1:]) xend = x[-1] a.pop(j) i += 1 return a tids = ex._tids components = tids.components dt = {} for c in components: if c in dt: continue index_types = c.index_types a = [] for i in range(len(index_types)): if index_types[i] is index_type: a.append(i) if len(a) > 2: raise ValueError('at most two indices of type %s allowed' % index_type) if len(a) == 2: dt[c] = a dum = tids.dum lines = [] traces = [] traces1 = [] for p0, p1, c0, c1 in dum: if components[c0] not in dt: continue if c0 == c1: traces.append([c0]) continue ta0 = dt[components[c0]] ta1 = dt[components[c1]] if p0 not in ta0: continue if ta0.index(p0) == ta1.index(p1): # case gamma(i,s0,-s1)in c0, gamma(j,-s0,s2) in c1; # to deal with this case one could add to the position # a flag for transposition; # one could write [(c0, False), (c1, True)] raise NotImplementedError # if p0 == ta0[1] then G in pos c0 is mult on the right by G in c1 # if p0 == ta0[0] then G in pos c1 is mult on the right by G in c0 ta0 = dt[components[c0]] b0, b1 = (c0, c1) if p0 == ta0[1] else (c1, c0) lines1 = lines[:] for line in lines: if line[-1] == b0: if line[0] == b1: n = line.index(min(line)) traces1.append(line) traces.append(line[n:] + line[:n]) else: line.append(b1) break elif line[0] == b1: line.insert(0, b0) break else: lines1.append([b0, b1]) lines = [x for x in lines1 if x not in traces1] lines = _join_lines(lines) rest = [] for line in lines: for y in line: rest.append(y) for line in traces: for y in line: rest.append(y) rest = [x for x in range(len(components)) if x not in rest] return lines, traces, rest sympy-0.7.4.1/sympy/tensor/indexed.py0000644000175000017500000004211112253362407017730 0ustar georgeskgeorgesk"""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 its 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 __future__ import print_function, division from sympy.core import Expr, Tuple, Symbol, sympify, S from sympy.core.compatibility import is_sequence, string_types class IndexException(Exception): pass 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, (string_types, 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 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 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 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 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 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)) 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) 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]) 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, (string_types, 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 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 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 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) 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, string_types): 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 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 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 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) sympy-0.7.4.1/sympy/tensor/tests/0000755000175000017500000000000012253362407017101 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/tensor/tests/test_indexed.py0000644000175000017500000001235712253362407022142 0ustar georgeskgeorgeskfrom sympy.core import symbols, Integer, Symbol, Tuple, oo from sympy.tensor.indexed import IndexException from sympy.utilities.pytest import raises # import test: from sympy import IndexedBase, Idx, Indexed def test_Idx_construction(): i, a, b = symbols('i a b', integer=True) assert Idx(i) != Idx(i, 1) assert Idx(i, a) == Idx(i, (0, a - 1)) assert Idx(i, oo) == Idx(i, (0, oo)) x = symbols('x') raises(TypeError, lambda: Idx(x)) raises(TypeError, lambda: Idx(0.5)) raises(TypeError, lambda: Idx(i, x)) raises(TypeError, lambda: Idx(i, 0.5)) raises(TypeError, lambda: Idx(i, (x, 5))) raises(TypeError, lambda: Idx(i, (2, x))) raises(TypeError, lambda: Idx(i, (2, 3.5))) def test_Idx_properties(): i, a, b = symbols('i a b', integer=True) assert Idx(i).is_integer def test_Idx_bounds(): i, a, b = symbols('i a b', integer=True) assert Idx(i).lower is None assert Idx(i).upper is None assert Idx(i, a).lower == 0 assert Idx(i, a).upper == a - 1 assert Idx(i, 5).lower == 0 assert Idx(i, 5).upper == 4 assert Idx(i, oo).lower == 0 assert Idx(i, oo).upper == oo assert Idx(i, (a, b)).lower == a assert Idx(i, (a, b)).upper == b assert Idx(i, (1, 5)).lower == 1 assert Idx(i, (1, 5)).upper == 5 assert Idx(i, (-oo, oo)).lower == -oo assert Idx(i, (-oo, oo)).upper == oo def test_Idx_fixed_bounds(): i, a, b = symbols('i a b', integer=True) assert Idx(2).lower is None assert Idx(2).upper is None assert Idx(2, a).lower == 0 assert Idx(2, a).upper == a - 1 assert Idx(2, 5).lower == 0 assert Idx(2, 5).upper == 4 assert Idx(2, oo).lower == 0 assert Idx(2, oo).upper == oo assert Idx(2, (a, b)).lower == a assert Idx(2, (a, b)).upper == b assert Idx(2, (1, 5)).lower == 1 assert Idx(2, (1, 5)).upper == 5 assert Idx(2, (-oo, oo)).lower == -oo assert Idx(2, (-oo, oo)).upper == oo def test_Idx_func_args(): i, a, b = symbols('i a b', integer=True) ii = Idx(i) assert ii.func(*ii.args) == ii ii = Idx(i, a) assert ii.func(*ii.args) == ii ii = Idx(i, (a, b)) assert ii.func(*ii.args) == ii def test_Idx_subs(): i, a, b = symbols('i a b', integer=True) assert Idx(i, a).subs(a, b) == Idx(i, b) assert Idx(i, a).subs(i, b) == Idx(b, a) assert Idx(i).subs(i, 2) == Idx(2) assert Idx(i, a).subs(a, 2) == Idx(i, 2) assert Idx(i, (a, b)).subs(i, 2) == Idx(2, (a, b)) def test_IndexedBase_sugar(): i, j = symbols('i j', integer=True) a = symbols('a') A1 = Indexed(a, i, j) A2 = IndexedBase(a) assert A1 == A2[i, j] assert A1 == A2[(i, j)] assert A1 == A2[[i, j]] assert A1 == A2[Tuple(i, j)] assert all(a.is_Integer for a in A2[1, 0].args[1:]) def test_IndexedBase_subs(): i, j, k = symbols('i j k', integer=True) a, b = symbols('a b') A = IndexedBase(a) B = IndexedBase(b) assert A[i] == B[i].subs(b, a) def test_IndexedBase_shape(): i, j, m, n = symbols('i j m n', integer=True) a = IndexedBase('a', shape=(m, m)) b = IndexedBase('a', shape=(m, n)) assert b.shape == Tuple(m, n) assert a[i, j] != b[i, j] assert a[i, j] == b[i, j].subs(n, m) assert b.func(*b.args) == b assert b[i, j].func(*b[i, j].args) == b[i, j] raises(IndexException, lambda: b[i]) raises(IndexException, lambda: b[i, i, j]) def test_Indexed_constructor(): i, j = symbols('i j', integer=True) A = Indexed('A', i, j) assert A == Indexed(Symbol('A'), i, j) assert A == Indexed(IndexedBase('A'), i, j) raises(TypeError, lambda: Indexed(A, i, j)) raises(IndexException, lambda: Indexed("A")) def test_Indexed_func_args(): i, j = symbols('i j', integer=True) a = symbols('a') A = Indexed(a, i, j) assert A == A.func(*A.args) def test_Indexed_subs(): i, j, k = symbols('i j k', integer=True) a, b = symbols('a b') A = IndexedBase(a) B = IndexedBase(b) assert A[i, j] == B[i, j].subs(b, a) assert A[i, j] == A[i, k].subs(k, j) def test_Indexed_properties(): i, j = symbols('i j', integer=True) A = Indexed('A', i, j) assert A.rank == 2 assert A.indices == (i, j) assert A.base == IndexedBase('A') assert A.ranges == [None, None] raises(IndexException, lambda: A.shape) n, m = symbols('n m', integer=True) assert Indexed('A', Idx( i, m), Idx(j, n)).ranges == [Tuple(0, m - 1), Tuple(0, n - 1)] assert Indexed('A', Idx(i, m), Idx(j, n)).shape == Tuple(m, n) raises(IndexException, lambda: Indexed("A", Idx(i, m), Idx(j)).shape) def test_Indexed_shape_precedence(): i, j = symbols('i j', integer=True) o, p = symbols('o p', integer=True) n, m = symbols('n m', integer=True) a = IndexedBase('a', shape=(o, p)) assert a.shape == Tuple(o, p) assert Indexed( a, Idx(i, m), Idx(j, n)).ranges == [Tuple(0, m - 1), Tuple(0, n - 1)] assert Indexed(a, Idx(i, m), Idx(j, n)).shape == Tuple(o, p) assert Indexed( a, Idx(i, m), Idx(j)).ranges == [Tuple(0, m - 1), Tuple(None, None)] assert Indexed(a, Idx(i, m), Idx(j)).shape == Tuple(o, p) def test_complex_indices(): i, j = symbols('i j', integer=True) A = Indexed('A', i, i + j) assert A.rank == 2 assert A.indices == (i, i + j) sympy-0.7.4.1/sympy/tensor/tests/test_tensor.py0000644000175000017500000014221712253362407022033 0ustar georgeskgeorgeskfrom sympy import Matrix, eye from sympy.combinatorics import Permutation from sympy.core import S, Rational, Symbol, Basic from sympy.core.containers import Tuple from sympy.core.symbol import symbols from sympy.external import import_module from sympy.functions.elementary.miscellaneous import sqrt from sympy.printing.pretty.pretty import pretty from sympy.tensor.tensor import TensorIndexType, tensor_indices, TensorSymmetry, \ get_symmetric_group_sgs, TensorType, TensorIndex, tensor_mul, TensAdd, \ riemann_cyclic_replace, riemann_cyclic, TensMul, \ tensorsymmetry, tensorhead, TensorManager, TensExpr, TIDS from sympy.utilities.pytest import raises, skip #################### Tests from tensor_can.py ####################### def test_canonicalize_no_slot_sym(): # A_d0 * B^d0; T_c = A^d0*B_d0 Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') a, b, d0, d1 = tensor_indices('a,b,d0,d1', Lorentz) sym1 = tensorsymmetry([1]) S1 = TensorType([Lorentz], sym1) A, B = S1('A,B') t = A(-d0)*B(d0) tc = t.canon_bp() assert str(tc) == 'A(L_0)*B(-L_0)' # A^a * B^b; T_c = T t = A(a)*B(b) tc = t.canon_bp() assert tc == t # B^b * A^a t1 = B(b)*A(a) tc = t1.canon_bp() assert str(tc) == 'A(a)*B(b)' # A symmetric # A^{b}_{d0}*A^{d0, a}; T_c = A^{a d0}*A{b}_{d0} sym2 = tensorsymmetry([1]*2) S2 = TensorType([Lorentz]*2, sym2) A = S2('A') t = A(b, -d0)*A(d0, a) tc = t.canon_bp() assert str(tc) == 'A(a, L_0)*A(b, -L_0)' # A^{d1}_{d0}*B^d0*C_d1 # T_c = A^{d0 d1}*B_d0*C_d1 B, C = S1('B,C') t = A(d1, -d0)*B(d0)*C(-d1) tc = t.canon_bp() assert str(tc) == 'A(L_0, L_1)*B(-L_0)*C(-L_1)' # A without symmetry # A^{d1}_{d0}*B^d0*C_d1 ord=[d0,-d0,d1,-d1]; g = [2,1,0,3,4,5] # T_c = A^{d0 d1}*B_d1*C_d0; can = [0,2,3,1,4,5] nsym2 = tensorsymmetry([1],[1]) NS2 = TensorType([Lorentz]*2, nsym2) A = NS2('A') B, C = S1('B, C') t = A(d1, -d0)*B(d0)*C(-d1) tc = t.canon_bp() assert str(tc) == 'A(L_0, L_1)*B(-L_1)*C(-L_0)' # A, B without symmetry # A^{d1}_{d0}*B_{d1}^{d0} # T_c = A^{d0 d1}*B_{d0 d1} B = NS2('B') t = A(d1, -d0)*B(-d1, d0) tc = t.canon_bp() assert str(tc) == 'A(L_0, L_1)*B(-L_0, -L_1)' # A_{d0}^{d1}*B_{d1}^{d0} # T_c = A^{d0 d1}*B_{d1 d0} t = A(-d0, d1)*B(-d1, d0) tc = t.canon_bp() assert str(tc) == 'A(L_0, L_1)*B(-L_1, -L_0)' # A, B, C without symmetry # A^{d1 d0}*B_{a d0}*C_{d1 b} # T_c=A^{d0 d1}*B_{a d1}*C_{d0 b} C = NS2('C') t = A(d1, d0)*B(-a, -d0)*C(-d1, -b) tc = t.canon_bp() assert str(tc) == 'A(L_0, L_1)*B(-a, -L_1)*C(-L_0, -b)' # A symmetric, B and C without symmetry # A^{d1 d0}*B_{a d0}*C_{d1 b} # T_c = A^{d0 d1}*B_{a d0}*C_{d1 b} A = S2('A') t = A(d1, d0)*B(-a, -d0)*C(-d1, -b) tc = t.canon_bp() assert str(tc) == 'A(L_0, L_1)*B(-a, -L_0)*C(-L_1, -b)' # A and C symmetric, B without symmetry # A^{d1 d0}*B_{a d0}*C_{d1 b} ord=[a,b,d0,-d0,d1,-d1] # T_c = A^{d0 d1}*B_{a d0}*C_{b d1} C = S2('C') t = A(d1, d0)*B(-a, -d0)*C(-d1, -b) tc = t.canon_bp() assert str(tc) == 'A(L_0, L_1)*B(-a, -L_0)*C(-b, -L_1)' def test_canonicalize_no_dummies(): Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') a, b, c, d = tensor_indices('a, b, c, d', Lorentz) sym1 = tensorsymmetry([1]) sym2 = tensorsymmetry([1]*2) sym2a = tensorsymmetry([2]) # A commuting # A^c A^b A^a # T_c = A^a A^b A^c S1 = TensorType([Lorentz], sym1) A = S1('A') t = A(c)*A(b)*A(a) tc = t.canon_bp() assert str(tc) == 'A(a)*A(b)*A(c)' # A anticommuting # A^c A^b A^a # T_c = -A^a A^b A^c A = S1('A', 1) t = A(c)*A(b)*A(a) tc = t.canon_bp() assert str(tc) == '-A(a)*A(b)*A(c)' # A commuting and symmetric # A^{b,d}*A^{c,a} # T_c = A^{a c}*A^{b d} S2 = TensorType([Lorentz]*2, sym2) A = S2('A') t = A(b, d)*A(c, a) tc = t.canon_bp() assert str(tc) == 'A(a, c)*A(b, d)' # A anticommuting and symmetric # A^{b,d}*A^{c,a} # T_c = -A^{a c}*A^{b d} A = S2('A', 1) t = A(b, d)*A(c, a) tc = t.canon_bp() assert str(tc) == '-A(a, c)*A(b, d)' # A^{c,a}*A^{b,d} # T_c = A^{a c}*A^{b d} t = A(c, a)*A(b, d) tc = t.canon_bp() assert str(tc) == 'A(a, c)*A(b, d)' def test_no_metric_symmetry(): # no metric symmetry; A no symmetry # A^d1_d0 * A^d0_d1 # T_c = A^d0_d1 * A^d1_d0 Lorentz = TensorIndexType('Lorentz', metric=None, dummy_fmt='L') d0, d1, d2, d3 = tensor_indices('d:4', Lorentz) A = tensorhead('A', [Lorentz]*2, [[1], [1]]) t = A(d1, -d0)*A(d0, -d1) tc = t.canon_bp() assert str(tc) == 'A(L_0, -L_1)*A(L_1, -L_0)' # A^d1_d2 * A^d0_d3 * A^d2_d1 * A^d3_d0 # T_c = A^d0_d1 * A^d1_d0 * A^d2_d3 * A^d3_d2 t = A(d1, -d2)*A(d0, -d3)*A(d2,-d1)*A(d3,-d0) tc = t.canon_bp() assert str(tc) == 'A(L_0, -L_1)*A(L_1, -L_0)*A(L_2, -L_3)*A(L_3, -L_2)' # A^d0_d2 * A^d1_d3 * A^d3_d0 * A^d2_d1 # T_c = A^d0_d1 * A^d1_d2 * A^d2_d3 * A^d3_d0 t = A(d0, -d1)*A(d1, -d2)*A(d2, -d3)*A(d3,-d0) tc = t.canon_bp() assert str(tc) == 'A(L_0, -L_1)*A(L_1, -L_2)*A(L_2, -L_3)*A(L_3, -L_0)' def test_canonicalize1(): Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') a, a0, a1, a2, a3, b, d0, d1, d2, d3 = \ tensor_indices('a,a0,a1,a2,a3,b,d0,d1,d2,d3', Lorentz) sym1 = tensorsymmetry([1]) base3, gens3 = get_symmetric_group_sgs(3) sym2 = tensorsymmetry([1]*2) sym2a = tensorsymmetry([2]) sym3 = tensorsymmetry([1]*3) sym3a = tensorsymmetry([3]) # A_d0*A^d0; ord = [d0,-d0] # T_c = A^d0*A_d0 S1 = TensorType([Lorentz], sym1) A = S1('A') t = A(-d0)*A(d0) tc = t.canon_bp() assert str(tc) == 'A(L_0)*A(-L_0)' # A commuting # A_d0*A_d1*A_d2*A^d2*A^d1*A^d0 # T_c = A^d0*A_d0*A^d1*A_d1*A^d2*A_d2 t = A(-d0)*A(-d1)*A(-d2)*A(d2)*A(d1)*A(d0) tc = t.canon_bp() assert str(tc) == 'A(L_0)*A(-L_0)*A(L_1)*A(-L_1)*A(L_2)*A(-L_2)' # A anticommuting # A_d0*A_d1*A_d2*A^d2*A^d1*A^d0 # T_c 0 A = S1('A', 1) t = A(-d0)*A(-d1)*A(-d2)*A(d2)*A(d1)*A(d0) tc = t.canon_bp() assert tc == 0 # A commuting symmetric # A^{d0 b}*A^a_d1*A^d1_d0 # T_c = A^{a d0}*A^{b d1}*A_{d0 d1} S2 = TensorType([Lorentz]*2, sym2) A = S2('A') t = A(d0, b)*A(a, -d1)*A(d1, -d0) tc = t.canon_bp() assert str(tc) == 'A(a, L_0)*A(b, L_1)*A(-L_0, -L_1)' # A, B commuting symmetric # A^{d0 b}*A^d1_d0*B^a_d1 # T_c = A^{b d0}*A_d0^d1*B^a_d1 B = S2('B') t = A(d0, b)*A(d1, -d0)*B(a, -d1) tc = t.canon_bp() assert str(tc) == 'A(b, L_0)*A(-L_0, L_1)*B(a, -L_1)' # A commuting symmetric # A^{d1 d0 b}*A^{a}_{d1 d0}; ord=[a,b, d0,-d0,d1,-d1] # T_c = A^{a d0 d1}*A^{b}_{d0 d1} S3 = TensorType([Lorentz]*3, sym3) A = S3('A') t = A(d1, d0, b)*A(a, -d1, -d0) tc = t.canon_bp() assert str(tc) == 'A(a, L_0, L_1)*A(b, -L_0, -L_1)' # A^{d3 d0 d2}*A^a0_{d1 d2}*A^d1_d3^a1*A^{a2 a3}_d0 # T_c = A^{a0 d0 d1}*A^a1_d0^d2*A^{a2 a3 d3}*A_{d1 d2 d3} t = A(d3, d0, d2)*A(a0, -d1, -d2)*A(d1, -d3, a1)*A(a2, a3, -d0) tc = t.canon_bp() assert str(tc) == 'A(a0, L_0, L_1)*A(a1, -L_0, L_2)*A(a2, a3, L_3)*A(-L_1, -L_2, -L_3)' # A commuting symmetric, B antisymmetric # A^{d0 d1 d2} * A_{d2 d3 d1} * B_d0^d3 # in this esxample and in the next three, # renaming dummy indices and using symmetry of A, # T = A^{d0 d1 d2} * A_{d0 d1 d3} * B_d2^d3 # can = 0 S2a = TensorType([Lorentz]*2, sym2a) A = S3('A') B = S2a('B') t = A(d0, d1, d2)*A(-d2, -d3, -d1)*B(-d0, d3) tc = t.canon_bp() assert tc == 0 # A anticommuting symmetric, B anticommuting # A^{d0 d1 d2} * A_{d2 d3 d1} * B_d0^d3 # T_c = A^{d0 d1 d2} * A_{d0 d1}^d3 * B_{d2 d3} A = S3('A', 1) B = S2a('B') t = A(d0, d1, d2)*A(-d2, -d3, -d1)*B(-d0, d3) tc = t.canon_bp() assert str(tc) == 'A(L_0, L_1, L_2)*A(-L_0, -L_1, L_3)*B(-L_2, -L_3)' # A anticommuting symmetric, B antisymmetric commuting, antisymmetric metric # A^{d0 d1 d2} * A_{d2 d3 d1} * B_d0^d3 # T_c = -A^{d0 d1 d2} * A_{d0 d1}^d3 * B_{d2 d3} Spinor = TensorIndexType('Spinor', metric=1, dummy_fmt='S') a, a0, a1, a2, a3, b, d0, d1, d2, d3 = \ tensor_indices('a,a0,a1,a2,a3,b,d0,d1,d2,d3', Spinor) S3 = TensorType([Spinor]*3, sym3) S2a = TensorType([Spinor]*2, sym2a) A = S3('A', 1) B = S2a('B') t = A(d0, d1, d2)*A(-d2, -d3, -d1)*B(-d0, d3) tc = t.canon_bp() assert str(tc) == '-A(S_0, S_1, S_2)*A(-S_0, -S_1, S_3)*B(-S_2, -S_3)' # A anticommuting symmetric, B antisymmetric anticommuting, # no metric symmetry # A^{d0 d1 d2} * A_{d2 d3 d1} * B_d0^d3 # T_c = A^{d0 d1 d2} * A_{d0 d1 d3} * B_d2^d3 Mat = TensorIndexType('Mat', metric=None, dummy_fmt='M') a, a0, a1, a2, a3, b, d0, d1, d2, d3 = \ tensor_indices('a,a0,a1,a2,a3,b,d0,d1,d2,d3', Mat) S3 = TensorType([Mat]*3, sym3) S2a = TensorType([Mat]*2, sym2a) A = S3('A', 1) B = S2a('B') t = A(d0, d1, d2)*A(-d2, -d3, -d1)*B(-d0, d3) tc = t.canon_bp() assert str(tc) == 'A(M_0, M_1, M_2)*A(-M_0, -M_1, -M_3)*B(-M_2, M_3)' # Gamma anticommuting # Gamma_{mu nu} * gamma^rho * Gamma^{nu mu alpha} # T_c = -Gamma^{mu nu} * gamma^rho * Gamma_{alpha mu nu} S1 = TensorType([Lorentz], sym1) S2a = TensorType([Lorentz]*2, sym2a) S3a = TensorType([Lorentz]*3, sym3a) alpha, beta, gamma, mu, nu, rho = \ tensor_indices('alpha,beta,gamma,mu,nu,rho', Lorentz) Gamma = S1('Gamma', 2) Gamma2 = S2a('Gamma', 2) Gamma3 = S3a('Gamma', 2) t = Gamma2(-mu,-nu)*Gamma(rho)*Gamma3(nu, mu, alpha) tc = t.canon_bp() assert str(tc) == '-Gamma(L_0, L_1)*Gamma(rho)*Gamma(alpha, -L_0, -L_1)' # Gamma_{mu nu} * Gamma^{gamma beta} * gamma_rho * Gamma^{nu mu alpha} # T_c = Gamma^{mu nu} * Gamma^{beta gamma} * gamma_rho * Gamma^alpha_{mu nu} t = Gamma2(mu, nu)*Gamma2(beta, gamma)*Gamma(-rho)*Gamma3(alpha, -mu, -nu) tc = t.canon_bp() assert str(tc) == 'Gamma(L_0, L_1)*Gamma(beta, gamma)*Gamma(-rho)*Gamma(alpha, -L_0, -L_1)' # f^a_{b,c} antisymmetric in b,c; A_mu^a no symmetry # f^c_{d a} * f_{c e b} * A_mu^d * A_nu^a * A^{nu e} * A^{mu b} # g = [8,11,5, 9,13,7, 1,10, 3,4, 2,12, 0,6, 14,15] # T_c = -f^{a b c} * f_a^{d e} * A^mu_b * A_{mu d} * A^nu_c * A_{nu e} Flavor = TensorIndexType('Flavor', dummy_fmt='F') a, b, c, d, e, ff = tensor_indices('a,b,c,d,e,f', Flavor) mu, nu = tensor_indices('mu,nu', Lorentz) sym_f = tensorsymmetry([1], [2]) S_f = TensorType([Flavor]*3, sym_f) sym_A = tensorsymmetry([1], [1]) S_A = TensorType([Lorentz, Flavor], sym_A) f = S_f('f') A = S_A('A') t = f(c, -d, -a)*f(-c, -e, -b)*A(-mu, d)*A(-nu, a)*A(nu, e)*A(mu, b) tc = t.canon_bp() assert str(tc) == '-f(F_0, F_1, F_2)*f(-F_0, F_3, F_4)*A(L_0, -F_1)*A(-L_0, -F_3)*A(L_1, -F_2)*A(-L_1, -F_4)' def test_bug_correction_tensor_indices(): # to make sure that tensor_indices does not return a list if creating # only one index: from sympy.tensor.tensor import tensor_indices, TensorIndexType, TensorIndex A = TensorIndexType("A") i = tensor_indices('i', A) assert not isinstance(i, (tuple, list)) assert isinstance(i, TensorIndex) def test_riemann_invariants(): Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11 = \ tensor_indices('d0:12', Lorentz) # R^{d0 d1}_{d1 d0}; ord = [d0,-d0,d1,-d1] # T_c = -R^{d0 d1}_{d0 d1} R = tensorhead('R', [Lorentz]*4, [[2, 2]]) t = R(d0, d1, -d1, -d0) tc = t.canon_bp() assert str(tc) == '-R(L_0, L_1, -L_0, -L_1)' # R_d11^d1_d0^d5 * R^{d6 d4 d0}_d5 * R_{d7 d2 d8 d9} * # R_{d10 d3 d6 d4} * R^{d2 d7 d11}_d1 * R^{d8 d9 d3 d10} # can = [0,2,4,6, 1,3,8,10, 5,7,12,14, 9,11,16,18, 13,15,20,22, # 17,19,21>> 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 if not isinstance(new_expr, Expr): return new_expr return refine(new_expr, assumptions) 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 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.elementary.complexes import Abs from sympy.functions import sign if isinstance(expr.base, Abs): if ask(Q.real(expr.base.args[0]), assumptions) and \ ask(Q.even(expr.exp), assumptions): return expr.base.args[0] ** expr.exp 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: old = expr # 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) expr = expr.base**(Add(*terms)) # Handle (-1)**((-1)**n/2 + m/2) e2 = 2*expr.exp if ask(Q.even(e2), assumptions): if e2.could_extract_minus_sign(): e2 *= expr.base if e2.is_Add: i, p = e2.as_two_terms() if p.is_Pow and p.base is S.NegativeOne: if ask(Q.integer(p.exp), assumptions): i = (i + 1)/2 if ask(Q.even(i), assumptions): return expr.base**p.exp elif ask(Q.odd(i), assumptions): return expr.base**(p.exp + 1) else: return expr.base**(p.exp + i) if old != expr: return expr 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 def refine_Relational(expr, assumptions): """ Handler for Relational >>> from sympy.assumptions.refine import refine_Relational >>> from sympy.assumptions.ask import Q >>> from sympy.abc import x >>> refine_Relational(x<0, ~Q.is_true(x<0)) False """ return ask(Q.is_true(expr), assumptions) handlers_dict = { 'Abs': refine_abs, 'Pow': refine_Pow, 'exp': refine_exp, 'Equality' : refine_Relational, 'Unequality' : refine_Relational, 'GreaterThan' : refine_Relational, 'LessThan' : refine_Relational, 'StrictGreaterThan' : refine_Relational, 'StrictLessThan' : refine_Relational } sympy-0.7.4.1/sympy/assumptions/ask_generated.py0000644000175000017500000001374412253362407022171 0ustar georgeskgeorgesk""" The contents of this file are the return value of ``sympy.assumptions.ask.compute_known_facts``. Do NOT manually edit this file. Instead, run ./bin/ask_update.py. """ from sympy.logic.boolalg import And, Not, Or from sympy.assumptions.ask import Q # -{ Known facts in CNF }- known_facts_cnf = And( Or(Q.invertible, Q.singular), Or(Not(Q.rational), Q.algebraic), Or(Not(Q.imaginary), Q.antihermitian), Or(Not(Q.algebraic), Q.complex), Or(Not(Q.imaginary), Q.complex), Or(Not(Q.real), Q.complex), Or(Not(Q.real_elements), Q.complex_elements), Or(Not(Q.zero), Q.even), Or(Not(Q.infinity), Q.extended_real), Or(Not(Q.real), Q.extended_real), Or(Not(Q.invertible), Q.fullrank), Or(Not(Q.real), Q.hermitian), Or(Not(Q.even), Q.integer), Or(Not(Q.odd), Q.integer), Or(Not(Q.prime), Q.integer), Or(Not(Q.positive_definite), Q.invertible), Or(Not(Q.unitary), Q.invertible), Or(Not(Q.diagonal), Q.lower_triangular), Or(Not(Q.negative), Q.nonzero), Or(Not(Q.positive), Q.nonzero), Or(Not(Q.diagonal), Q.normal), Or(Not(Q.unitary), Q.normal), Or(Not(Q.prime), Q.positive), Or(Not(Q.orthogonal), Q.positive_definite), Or(Not(Q.integer), Q.rational), Or(Not(Q.irrational), Q.real), Or(Not(Q.nonnegative), Q.real), Or(Not(Q.nonpositive), Q.real), Or(Not(Q.nonzero), Q.real), Or(Not(Q.rational), Q.real), Or(Not(Q.zero), Q.real), Or(Not(Q.integer_elements), Q.real_elements), Or(Not(Q.invertible), Q.square), Or(Not(Q.normal), Q.square), Or(Not(Q.symmetric), Q.square), Or(Not(Q.diagonal), Q.symmetric), Or(Not(Q.lower_triangular), Q.triangular), Or(Not(Q.unit_triangular), Q.triangular), Or(Not(Q.upper_triangular), Q.triangular), Or(Not(Q.orthogonal), Q.unitary), Or(Not(Q.diagonal), Q.upper_triangular), Or(Not(Q.antihermitian), Not(Q.hermitian)), Or(Not(Q.composite), Not(Q.prime)), Or(Not(Q.even), Not(Q.odd)), Or(Not(Q.imaginary), Not(Q.real)), Or(Not(Q.invertible), Not(Q.singular)), Or(Not(Q.irrational), Not(Q.rational)), Or(Not(Q.negative), Not(Q.nonnegative)), Or(Not(Q.negative), Not(Q.positive)), Or(Not(Q.nonpositive), Not(Q.positive)), Or(Not(Q.nonzero), Not(Q.zero)), Or(Not(Q.integer), Q.even, Q.odd), Or(Not(Q.extended_real), Q.infinity, Q.real), Or(Not(Q.real), Q.irrational, Q.rational), Or(Not(Q.triangular), Q.lower_triangular, Q.upper_triangular), Or(Not(Q.real), Q.negative, Q.nonnegative), Or(Not(Q.nonzero), Q.negative, Q.positive), Or(Not(Q.real), Q.nonpositive, Q.positive), Or(Not(Q.real), Q.nonzero, Q.zero), Or(Not(Q.lower_triangular), Not(Q.upper_triangular), Q.diagonal), Or(Not(Q.fullrank), Not(Q.square), Q.invertible), Or(Not(Q.real), Not(Q.unitary), Q.orthogonal), Or(Not(Q.integer), Not(Q.positive), Q.composite, Q.prime) ) # -{ Known facts in compressed sets }- known_facts_dict = { Q.algebraic: set([Q.algebraic, Q.complex]), Q.antihermitian: set([Q.antihermitian]), Q.bounded: set([Q.bounded]), Q.commutative: set([Q.commutative]), Q.complex: set([Q.complex]), Q.complex_elements: set([Q.complex_elements]), Q.composite: set([Q.composite]), Q.diagonal: set([Q.diagonal, Q.lower_triangular, Q.normal, Q.square, Q.symmetric, Q.triangular, Q.upper_triangular]), Q.even: set([Q.algebraic, Q.complex, Q.even, Q.extended_real, Q.hermitian, Q.integer, Q.rational, Q.real]), Q.extended_real: set([Q.extended_real]), Q.fullrank: set([Q.fullrank]), Q.hermitian: set([Q.hermitian]), Q.imaginary: set([Q.antihermitian, Q.complex, Q.imaginary]), Q.infinitesimal: set([Q.infinitesimal]), Q.infinity: set([Q.extended_real, Q.infinity]), Q.integer: set([Q.algebraic, Q.complex, Q.extended_real, Q.hermitian, Q.integer, Q.rational, Q.real]), Q.integer_elements: set([Q.complex_elements, Q.integer_elements, Q.real_elements]), Q.invertible: set([Q.fullrank, Q.invertible, Q.square]), Q.irrational: set([Q.complex, Q.extended_real, Q.hermitian, Q.irrational, Q.nonzero, Q.real]), Q.is_true: set([Q.is_true]), Q.lower_triangular: set([Q.lower_triangular, Q.triangular]), Q.negative: set([Q.complex, Q.extended_real, Q.hermitian, Q.negative, Q.nonpositive, Q.nonzero, Q.real]), Q.nonnegative: set([Q.complex, Q.extended_real, Q.hermitian, Q.nonnegative, Q.real]), Q.nonpositive: set([Q.complex, Q.extended_real, Q.hermitian, Q.nonpositive, Q.real]), Q.nonzero: set([Q.complex, Q.extended_real, Q.hermitian, Q.nonzero, Q.real]), Q.normal: set([Q.normal, Q.square]), Q.odd: set([Q.algebraic, Q.complex, Q.extended_real, Q.hermitian, Q.integer, Q.nonzero, Q.odd, Q.rational, Q.real]), Q.orthogonal: set([Q.fullrank, Q.invertible, Q.normal, Q.orthogonal, Q.positive_definite, Q.square, Q.unitary]), Q.positive: set([Q.complex, Q.extended_real, Q.hermitian, Q.nonnegative, Q.nonzero, Q.positive, Q.real]), Q.positive_definite: set([Q.fullrank, Q.invertible, Q.positive_definite, Q.square]), Q.prime: set([Q.algebraic, Q.complex, Q.extended_real, Q.hermitian, Q.integer, Q.nonnegative, Q.nonzero, Q.positive, Q.prime, Q.rational, Q.real]), Q.rational: set([Q.algebraic, Q.complex, Q.extended_real, Q.hermitian, Q.rational, Q.real]), Q.real: set([Q.complex, Q.extended_real, Q.hermitian, Q.real]), Q.real_elements: set([Q.complex_elements, Q.real_elements]), Q.singular: set([Q.singular]), Q.square: set([Q.square]), Q.symmetric: set([Q.square, Q.symmetric]), Q.triangular: set([Q.triangular]), Q.unit_triangular: set([Q.triangular, Q.unit_triangular]), Q.unitary: set([Q.fullrank, Q.invertible, Q.normal, Q.square, Q.unitary]), Q.upper_triangular: set([Q.triangular, Q.upper_triangular]), Q.zero: set([Q.algebraic, Q.complex, Q.even, Q.extended_real, Q.hermitian, Q.integer, Q.nonnegative, Q.nonpositive, Q.rational, Q.real, Q.zero]), } sympy-0.7.4.1/sympy/assumptions/handlers/0000755000175000017500000000000012253362407020612 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/assumptions/handlers/sets.py0000644000175000017500000004210212253362407022141 0ustar georgeskgeorgesk""" Handlers for predicates related to set membership: integer, rational, etc. """ from __future__ import print_function, division from sympy.assumptions import Q, ask from sympy.assumptions.handlers import CommonHandler, test_closed_group from sympy import I, S 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 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 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 int, Integer = [staticmethod(CommonHandler.AlwaysTrue)]*2 Pi, Exp1, GoldenRatio, Infinity, NegativeInfinity, ImaginaryUnit = \ [staticmethod(CommonHandler.AlwaysFalse)]*6 @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 Abs(expr, assumptions): return ask(Q.integer(expr.args[0]), assumptions) @staticmethod def MatrixElement(expr, assumptions): return ask(Q.integer_elements(expr.args[0]), assumptions) Determinant = Trace = MatrixElement class AskRationalHandler(CommonHandler): """ Handler for Q.rational Test that an expression belongs to the field of rational numbers """ @staticmethod 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 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 Rational, Float = \ [staticmethod(CommonHandler.AlwaysTrue)]*2 # Float is finite-precision ImaginaryUnit, Infinity, NegativeInfinity, Pi, Exp1, GoldenRatio = \ [staticmethod(CommonHandler.AlwaysFalse)]*6 @staticmethod def exp(expr, assumptions): x = expr.args[0] if ask(Q.rational(x), assumptions): return ask(~Q.nonzero(x), assumptions) @staticmethod def cot(expr, assumptions): x = expr.args[0] if ask(Q.rational(x), assumptions): return False @staticmethod def log(expr, assumptions): x = expr.args[0] if ask(Q.rational(x), assumptions): return ask(~Q.nonzero(x - 1), assumptions) sin, cos, tan, asin, atan = [exp]*5 acos, acot = log, cot 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 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 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 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 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 Rational, Float, Pi, Exp1, GoldenRatio, Abs, re, im = \ [staticmethod(CommonHandler.AlwaysTrue)]*8 ImaginaryUnit, Infinity, NegativeInfinity = \ [staticmethod(CommonHandler.AlwaysFalse)]*3 @staticmethod def sin(expr, assumptions): if ask(Q.real(expr.args[0]), assumptions): return True cos, exp = [sin]*2 @staticmethod def MatrixElement(expr, assumptions): return ask(Q.real_elements(expr.args[0]), assumptions) Determinant = Trace = MatrixElement @staticmethod def log(expr, assumptions): if ask(Q.positive(expr.args[0]), assumptions): return True 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]*2 Infinity, NegativeInfinity = [staticmethod(CommonHandler.AlwaysTrue)]*2 class AskHermitianHandler(AskRealHandler): """ Handler for Q.hermitian Test that an expression belongs to the field of Hermitian operators """ @staticmethod 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 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 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]*2 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]*2 Number, sin, cos, exp, re, im, NumberSymbol, Abs, ImaginaryUnit = \ [staticmethod(CommonHandler.AlwaysTrue)]*9 # they are all complex functions or expressions Infinity, NegativeInfinity = [staticmethod(CommonHandler.AlwaysFalse)]*2 @staticmethod def MatrixElement(expr, assumptions): return ask(Q.complex_elements(expr.args[0]), assumptions) Determinant = Trace = MatrixElement 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 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 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 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 ImaginaryUnit = staticmethod(CommonHandler.AlwaysTrue) 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 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 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 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 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 Rational(expr, assumptions): return expr.q != 0 Float, GoldenRatio, ImaginaryUnit, AlgebraicNumber = \ [staticmethod(CommonHandler.AlwaysTrue)]*4 Infinity, NegativeInfinity, ComplexInfinity, Pi, Exp1 = \ [staticmethod(CommonHandler.AlwaysFalse)]*5 @staticmethod def exp(expr, assumptions): x = expr.args[0] if ask(Q.algebraic(x), assumptions): return ask(~Q.nonzero(x), assumptions) @staticmethod def cot(expr, assumptions): x = expr.args[0] if ask(Q.algebraic(x), assumptions): return False @staticmethod def log(expr, assumptions): x = expr.args[0] if ask(Q.algebraic(x), assumptions): return ask(~Q.nonzero(x - 1), assumptions) sin, cos, tan, asin, atan = [exp]*5 acos, acot = log, cot sympy-0.7.4.1/sympy/assumptions/handlers/ntheory.py0000644000175000017500000001531712253362407022663 0ustar georgeskgeorgesk""" Handlers for keys related to number theory: prime, even, odd, etc. """ from __future__ import print_function, division from sympy.assumptions import Q, ask from sympy.assumptions.handlers import CommonHandler from sympy.ntheory import isprime from sympy.core import S 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 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) Rational, Infinity, NegativeInfinity, ImaginaryUnit = [staticmethod(CommonHandler.AlwaysFalse)]*4 @staticmethod def Float(expr, assumptions): return AskPrimeHandler._number(expr, assumptions) @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 Even * Even -> Even Integer * Integer -> Even if Integer + Integer = Odd -> ? otherwise """ if expr.is_number: return AskEvenHandler._number(expr, assumptions) even, odd, irrational, acc = False, 0, False, 1 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 not even and acc != 1: if ask(Q.odd(acc + arg), assumptions): even = True elif ask(Q.irrational(arg), assumptions): # one irrational makes the result False # two makes it undefined if irrational: break irrational = True else: break acc = arg 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 Pow(expr, assumptions): if expr.is_number: return AskEvenHandler._number(expr, assumptions) if ask(Q.integer(expr.exp), assumptions): if ask(Q.positive(expr.exp), assumptions): return ask(Q.even(expr.base), assumptions) elif ask(~Q.negative(expr.exp) & Q.odd(expr.base), assumptions): return False elif expr.base is S.NegativeOne: return False @staticmethod def Integer(expr, assumptions): return not bool(expr.p & 1) Rational, Infinity, NegativeInfinity, ImaginaryUnit = [staticmethod(CommonHandler.AlwaysFalse)]*4 @staticmethod def Float(expr, assumptions): return expr % 2 == 0 @staticmethod def NumberSymbol(expr, assumptions): return AskEvenHandler._number(expr, assumptions) @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 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 sympy-0.7.4.1/sympy/assumptions/handlers/order.py0000644000175000017500000001706012253362407022303 0ustar georgeskgeorgesk""" AskHandlers related to order relations: positive, negative, etc. """ from __future__ import print_function, division from sympy.assumptions import Q, ask from sympy.assumptions.handlers import CommonHandler from sympy.core.logic import fuzzy_not, fuzzy_and, fuzzy_or 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 def Add(expr, assumptions): """ Positive + Positive -> Positive, Negative + Negative -> Negative """ if expr.is_number: return AskNegativeHandler._number(expr, assumptions) nonpos = 0 for arg in expr.args: if ask(Q.negative(arg), assumptions) is not True: if ask(Q.positive(arg), assumptions) is False: nonpos += 1 else: break else: if nonpos < len(expr.args): 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 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): if ask(Q.real(expr.exp), 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) ImaginaryUnit, Abs = [staticmethod(CommonHandler.AlwaysFalse)]*2 @staticmethod def exp(expr, assumptions): if ask(Q.real(expr.args[0]), assumptions): return False class AskNonNegativeHandler(CommonHandler): @staticmethod def Basic(expr, assumptions): if expr.is_number: notnegative = fuzzy_not(AskNegativeHandler._number(expr, assumptions)) if notnegative: return ask(Q.real(expr), assumptions) else: return notnegative 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) NaN = staticmethod(CommonHandler.AlwaysTrue) @staticmethod def Abs(expr, assumptions): return ask(Q.nonzero(expr.args[0]), assumptions) class AskZeroHandler(CommonHandler): @staticmethod def Basic(expr, assumptions): return fuzzy_and([fuzzy_not(ask(Q.nonzero(expr), assumptions)), ask(Q.real(expr), assumptions)]) @staticmethod def Mul(expr, assumptions): # TODO: This should be deducible from the nonzero handler return fuzzy_or(ask(Q.zero(arg), assumptions) for arg in expr.args) class AskNonPositiveHandler(CommonHandler): @staticmethod def Basic(expr, assumptions): if expr.is_number: notpositive = fuzzy_not(AskPositiveHandler._number(expr, assumptions)) if notpositive: return ask(Q.real(expr), assumptions) else: return notpositive 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) nonneg = 0 for arg in expr.args: if ask(Q.positive(arg), assumptions) is not True: if ask(Q.negative(arg), assumptions) is False: nonneg += 1 else: break else: if nonneg < len(expr.args): return True @staticmethod def Pow(expr, assumptions): if expr.is_number: return expr.evalf() > 0 if ask(Q.positive(expr.base), assumptions): if ask(Q.real(expr.exp), 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 factorial(expr, assumptions): x = expr.args[0] if ask(Q.integer(x) & Q.positive(x), assumptions): return True ImaginaryUnit = staticmethod(CommonHandler.AlwaysFalse) @staticmethod def Abs(expr, assumptions): return ask(Q.nonzero(expr), assumptions) @staticmethod def Trace(expr, assumptions): if ask(Q.positive_definite(expr.arg), assumptions): return True @staticmethod def Determinant(expr, assumptions): if ask(Q.positive_definite(expr.arg), assumptions): return True @staticmethod def MatrixElement(expr, assumptions): if (expr.i == expr.j and ask(Q.positive_definite(expr.parent), assumptions)): return True sympy-0.7.4.1/sympy/assumptions/handlers/calculus.py0000644000175000017500000001763212253362407023010 0ustar georgeskgeorgesk""" This module contains query handlers responsible for calculus queries: infinitesimal, bounded, etc. """ from __future__ import print_function, division from sympy.logic.boolalg import conjuncts from sympy.assumptions import Q, ask from sympy.assumptions.handlers import CommonHandler 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 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]*2 @staticmethod def Number(expr, assumptions): return expr == 0 NumberSymbol = Number ImaginaryUnit = staticmethod(CommonHandler.AlwaysFalse) 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 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 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 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 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) is True and ask(Q.positive(expr.exp), assumptions): return True if (abs(expr.base) >= 1) is True and ask(Q.negative(expr.exp), assumptions): return True if (abs(expr.base) >= 1) is True and exp_bounded is False: return False return None @staticmethod def log(expr, assumptions): return ask(Q.bounded(expr.args[0]), assumptions) exp = log cos, sin, Number, Pi, Exp1, GoldenRatio, ImaginaryUnit, sign = \ [staticmethod(CommonHandler.AlwaysTrue)]*8 Infinity, NegativeInfinity = [staticmethod(CommonHandler.AlwaysFalse)]*2 sympy-0.7.4.1/sympy/assumptions/handlers/common.py0000644000175000017500000000616512253362407022464 0ustar georgeskgeorgeskfrom sympy.logic.boolalg import conjuncts from sympy.assumptions import Q, ask class AskHandler(object): """Base class that all Ask Handlers must inherit""" pass class CommonHandler(AskHandler): """Defines some useful methods common to most Handlers """ @staticmethod def AlwaysTrue(expr, assumptions): return True @staticmethod def AlwaysFalse(expr, assumptions): return False NaN = AlwaysFalse class AskCommutativeHandler(CommonHandler): """ Handler for key 'commutative' """ @staticmethod def Symbol(expr, assumptions): """Objects are expected to be commutative unless otherwise stated""" assumps = conjuncts(assumptions) if Q.commutative(expr) in assumps: return True elif ~Q.commutative(expr) in assumps: return False return True @staticmethod def Basic(expr, assumptions): for arg in expr.args: if not ask(Q.commutative(arg), assumptions): return False return True Number, NaN = [staticmethod(CommonHandler.AlwaysTrue)]*2 class TautologicalHandler(AskHandler): """Wrapper allowing to query the truth value of a boolean expression.""" @staticmethod def bool(expr, assumptions): return expr BooleanTrue = staticmethod(CommonHandler.AlwaysTrue) BooleanFalse = staticmethod(CommonHandler.AlwaysFalse) @staticmethod def AppliedPredicate(expr, assumptions): return ask(expr, assumptions) @staticmethod def Not(expr, assumptions): value = ask(expr.args[0], assumptions=assumptions) if value in (True, False): return not value else: return None @staticmethod def Or(expr, assumptions): result = False for arg in expr.args: p = ask(arg, assumptions=assumptions) if p is True: return True if p is None: result = None return result @staticmethod def And(expr, assumptions): result = True for arg in expr.args: p = ask(arg, assumptions=assumptions) if p is False: return False if p is None: result = None return result @staticmethod def Implies(expr, assumptions): p, q = expr.args return ask(~p | q, assumptions=assumptions) @staticmethod def Equivalent(expr, assumptions): p, q = expr.args pt = ask(p, assumptions=assumptions) if pt is None: return None qt = ask(q, assumptions=assumptions) if qt is None: return None return pt == qt #### Helper methods 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 sympy-0.7.4.1/sympy/assumptions/handlers/__init__.py0000644000175000017500000000016312253362407022723 0ustar georgeskgeorgeskfrom .common import (AskHandler, CommonHandler, AskCommutativeHandler, TautologicalHandler, test_closed_group) sympy-0.7.4.1/sympy/assumptions/handlers/matrices.py0000644000175000017500000003264412253362407023004 0ustar georgeskgeorgesk""" This module contains query handlers responsible for calculus queries: infinitesimal, bounded, etc. """ from __future__ import print_function, division from sympy.logic.boolalg import conjuncts from sympy.assumptions import Q, ask from sympy.assumptions.handlers import CommonHandler, test_closed_group from sympy.matrices.expressions import MatMul, MatrixExpr from sympy.core.logic import fuzzy_and from sympy.utilities.iterables import sift from sympy.core import Basic from functools import partial def _Factorization(predicate, expr, assumptions): if predicate in expr.predicates: return True class AskSquareHandler(CommonHandler): """ Handler for key 'square' """ @staticmethod def MatrixExpr(expr, assumptions): return expr.shape[0] == expr.shape[1] class AskSymmetricHandler(CommonHandler): """ Handler for key 'symmetric' """ @staticmethod def MatMul(expr, assumptions): factor, mmul = expr.as_coeff_mmul() if all(ask(Q.symmetric(arg), assumptions) for arg in mmul.args): return True if len(mmul.args) >= 2 and mmul.args[0] == mmul.args[-1].T: return ask(Q.symmetric(MatMul(*mmul.args[1:-1])), assumptions) @staticmethod def MatAdd(expr, assumptions): return all(ask(Q.symmetric(arg), assumptions) for arg in expr.args) @staticmethod def MatrixSymbol(expr, assumptions): if not expr.is_square: return False if Q.symmetric(expr) in conjuncts(assumptions): return True @staticmethod def ZeroMatrix(expr, assumptions): return ask(Q.square(expr), assumptions) @staticmethod def Transpose(expr, assumptions): return ask(Q.symmetric(expr.arg), assumptions) Inverse = Transpose @staticmethod def MatrixSlice(expr, assumptions): if not expr.on_diag: return None else: return ask(Q.symmetric(expr.parent), assumptions) Identity = staticmethod(CommonHandler.AlwaysTrue) class AskInvertibleHandler(CommonHandler): """ Handler for key 'invertible' """ @staticmethod def MatMul(expr, assumptions): factor, mmul = expr.as_coeff_mmul() if all(ask(Q.invertible(arg), assumptions) for arg in mmul.args): return True if any(ask(Q.invertible(arg), assumptions) is False for arg in mmul.args): return False @staticmethod def MatAdd(expr, assumptions): return None @staticmethod def MatrixSymbol(expr, assumptions): if not expr.is_square: return False if Q.invertible(expr) in conjuncts(assumptions): return True Identity, Inverse = [staticmethod(CommonHandler.AlwaysTrue)]*2 ZeroMatrix = staticmethod(CommonHandler.AlwaysFalse) @staticmethod def Transpose(expr, assumptions): return ask(Q.invertible(expr.arg), assumptions) @staticmethod def MatrixSlice(expr, assumptions): if not expr.on_diag: return None else: return ask(Q.invertible(expr.parent), assumptions) class AskOrthogonalHandler(CommonHandler): """ Handler for key 'orthogonal' """ predicate = Q.orthogonal @staticmethod def MatMul(expr, assumptions): factor, mmul = expr.as_coeff_mmul() if (all(ask(Q.orthogonal(arg), assumptions) for arg in mmul.args) and factor == 1): return True if any(ask(Q.invertible(arg), assumptions) is False for arg in mmul.args): return False @staticmethod def MatAdd(expr, assumptions): if (len(expr.args) == 1 and ask(Q.orthogonal(expr.args[0]), assumptions)): return True @staticmethod def MatrixSymbol(expr, assumptions): if not expr.is_square: return False if Q.orthogonal(expr) in conjuncts(assumptions): return True Identity = staticmethod(CommonHandler.AlwaysTrue) ZeroMatrix = staticmethod(CommonHandler.AlwaysFalse) @staticmethod def Transpose(expr, assumptions): return ask(Q.orthogonal(expr.arg), assumptions) Inverse = Transpose @staticmethod def MatrixSlice(expr, assumptions): if not expr.on_diag: return None else: return ask(Q.orthogonal(expr.parent), assumptions) Factorization = staticmethod(partial(_Factorization, Q.orthogonal)) class AskUnitaryHandler(CommonHandler): """ Handler for key 'unitary' """ predicate = Q.unitary @staticmethod def MatMul(expr, assumptions): factor, mmul = expr.as_coeff_mmul() if (all(ask(Q.unitary(arg), assumptions) for arg in mmul.args) and abs(factor) == 1): return True if any(ask(Q.invertible(arg), assumptions) is False for arg in mmul.args): return False @staticmethod def MatrixSymbol(expr, assumptions): if not expr.is_square: return False if Q.unitary(expr) in conjuncts(assumptions): return True @staticmethod def Transpose(expr, assumptions): return ask(Q.unitary(expr.arg), assumptions) Inverse = Transpose @staticmethod def MatrixSlice(expr, assumptions): if not expr.on_diag: return None else: return ask(Q.unitary(expr.parent), assumptions) @staticmethod def DFT(expr, assumptions): return True Factorization = staticmethod(partial(_Factorization, Q.unitary)) Identity = staticmethod(CommonHandler.AlwaysTrue) ZeroMatrix = staticmethod(CommonHandler.AlwaysFalse) class AskFullRankHandler(CommonHandler): """ Handler for key 'fullrank' """ @staticmethod def MatMul(expr, assumptions): if all(ask(Q.fullrank(arg), assumptions) for arg in expr.args): return True Identity = staticmethod(CommonHandler.AlwaysTrue) ZeroMatrix = staticmethod(CommonHandler.AlwaysFalse) @staticmethod def Transpose(expr, assumptions): return ask(Q.fullrank(expr.arg), assumptions) Inverse = Transpose @staticmethod def MatrixSlice(expr, assumptions): if ask(Q.orthogonal(expr.parent), assumptions): return True class AskPositiveDefiniteHandler(CommonHandler): """ Handler for key 'positive_definite' """ @staticmethod def MatMul(expr, assumptions): factor, mmul = expr.as_coeff_mmul() if (all(ask(Q.positive_definite(arg), assumptions) for arg in mmul.args) and factor > 0): return True if (len(mmul.args) >= 2 and mmul.args[0] == mmul.args[-1].T and ask(Q.fullrank(mmul.args[0]), assumptions)): return ask(Q.positive_definite( MatMul(*mmul.args[1:-1])), assumptions) @staticmethod def MatAdd(expr, assumptions): if all(ask(Q.positive_definite(arg), assumptions) for arg in expr.args): return True @staticmethod def MatrixSymbol(expr, assumptions): if not expr.is_square: return False if Q.positive_definite(expr) in conjuncts(assumptions): return True Identity = staticmethod(CommonHandler.AlwaysTrue) ZeroMatrix = staticmethod(CommonHandler.AlwaysFalse) @staticmethod def Transpose(expr, assumptions): return ask(Q.positive_definite(expr.arg), assumptions) Inverse = Transpose @staticmethod def MatrixSlice(expr, assumptions): if not expr.on_diag: return None else: return ask(Q.positive_definite(expr.parent), assumptions) class AskUpperTriangularHandler(CommonHandler): """ Handler for key 'upper_triangular' """ @staticmethod def MatMul(expr, assumptions): factor, matrices = expr.as_coeff_matrices() if all(ask(Q.upper_triangular(m), assumptions) for m in matrices): return True @staticmethod def MatAdd(expr, assumptions): if all(ask(Q.upper_triangular(arg), assumptions) for arg in expr.args): return True @staticmethod def MatrixSymbol(expr, assumptions): if Q.upper_triangular(expr) in conjuncts(assumptions): return True Identity, ZeroMatrix = [staticmethod(CommonHandler.AlwaysTrue)]*2 @staticmethod def Transpose(expr, assumptions): return ask(Q.lower_triangular(expr.arg), assumptions) @staticmethod def Inverse(expr, assumptions): return ask(Q.upper_triangular(expr.arg), assumptions) @staticmethod def MatrixSlice(expr, assumptions): if not expr.on_diag: return None else: return ask(Q.upper_triangular(expr.parent), assumptions) Factorization = staticmethod(partial(_Factorization, Q.upper_triangular)) class AskLowerTriangularHandler(CommonHandler): """ Handler for key 'lower_triangular' """ @staticmethod def MatMul(expr, assumptions): factor, matrices = expr.as_coeff_matrices() if all(ask(Q.lower_triangular(m), assumptions) for m in matrices): return True @staticmethod def MatAdd(expr, assumptions): if all(ask(Q.lower_triangular(arg), assumptions) for arg in expr.args): return True @staticmethod def MatrixSymbol(expr, assumptions): if Q.lower_triangular(expr) in conjuncts(assumptions): return True Identity, ZeroMatrix = [staticmethod(CommonHandler.AlwaysTrue)]*2 @staticmethod def Transpose(expr, assumptions): return ask(Q.upper_triangular(expr.arg), assumptions) @staticmethod def Inverse(expr, assumptions): return ask(Q.lower_triangular(expr.arg), assumptions) @staticmethod def MatrixSlice(expr, assumptions): if not expr.on_diag: return None else: return ask(Q.lower_triangular(expr.parent), assumptions) Factorization = staticmethod(partial(_Factorization, Q.lower_triangular)) class AskDiagonalHandler(CommonHandler): """ Handler for key 'diagonal' """ @staticmethod def MatMul(expr, assumptions): factor, matrices = expr.as_coeff_matrices() if all(ask(Q.diagonal(m), assumptions) for m in matrices): return True @staticmethod def MatAdd(expr, assumptions): if all(ask(Q.diagonal(arg), assumptions) for arg in expr.args): return True @staticmethod def MatrixSymbol(expr, assumptions): if Q.diagonal(expr) in conjuncts(assumptions): return True Identity, ZeroMatrix = [staticmethod(CommonHandler.AlwaysTrue)]*2 @staticmethod def Transpose(expr, assumptions): return ask(Q.diagonal(expr.arg), assumptions) @staticmethod def Inverse(expr, assumptions): return ask(Q.diagonal(expr.arg), assumptions) @staticmethod def MatrixSlice(expr, assumptions): if not expr.on_diag: return None else: return ask(Q.diagonal(expr.parent), assumptions) @staticmethod def DiagonalMatrix(expr, assumptions): return True Factorization = staticmethod(partial(_Factorization, Q.diagonal)) def BM_elements(predicate, expr, assumptions): """ Block Matrix elements """ return all(ask(predicate(b), assumptions) for b in expr.blocks) def MS_elements(predicate, expr, assumptions): """ Matrix Slice elements """ return ask(predicate(expr.parent), assumptions) def MatMul_elements(matrix_predicate, scalar_predicate, expr, assumptions): d = sift(expr.args, lambda x: isinstance(x, MatrixExpr)) factors, matrices = d[False], d[True] return fuzzy_and([ test_closed_group(Basic(*factors), assumptions, scalar_predicate), test_closed_group(Basic(*matrices), assumptions, matrix_predicate)]) class AskIntegerElementsHandler(CommonHandler): @staticmethod def MatAdd(expr, assumptions): return test_closed_group(expr, assumptions, Q.integer_elements) HadamardProduct, Determinant, Trace, Transpose = [MatAdd]*4 ZeroMatrix, Identity = [staticmethod(CommonHandler.AlwaysTrue)]*2 MatMul = staticmethod(partial(MatMul_elements, Q.integer_elements, Q.integer)) MatrixSlice = staticmethod(partial(MS_elements, Q.integer_elements)) BlockMatrix = staticmethod(partial(BM_elements, Q.integer_elements)) class AskRealElementsHandler(CommonHandler): @staticmethod def MatAdd(expr, assumptions): return test_closed_group(expr, assumptions, Q.real_elements) HadamardProduct, Determinant, Trace, Transpose, Inverse, \ Factorization = [MatAdd]*6 MatMul = staticmethod(partial(MatMul_elements, Q.real_elements, Q.real)) MatrixSlice = staticmethod(partial(MS_elements, Q.real_elements)) BlockMatrix = staticmethod(partial(BM_elements, Q.real_elements)) class AskComplexElementsHandler(CommonHandler): @staticmethod def MatAdd(expr, assumptions): return test_closed_group(expr, assumptions, Q.complex_elements) HadamardProduct, Determinant, Trace, Transpose, Inverse, \ Factorization = [MatAdd]*6 MatMul = staticmethod(partial(MatMul_elements, Q.complex_elements, Q.complex)) MatrixSlice = staticmethod(partial(MS_elements, Q.complex_elements)) BlockMatrix = staticmethod(partial(BM_elements, Q.complex_elements)) DFT = staticmethod(CommonHandler.AlwaysTrue) sympy-0.7.4.1/sympy/assumptions/ask.py0000644000175000017500000003237412253362407020153 0ustar georgeskgeorgesk"""Module for querying SymPy objects about assumptions.""" from __future__ import print_function, division from sympy.core import sympify from sympy.logic.boolalg import (to_cnf, And, Not, Or, Implies, Equivalent, BooleanFunction, true, false, BooleanAtom) from sympy.logic.inference import satisfiable from sympy.assumptions.assume import (global_assumptions, Predicate, AppliedPredicate) 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') nonpositive = Predicate('nonpositive') nonnegative = Predicate('nonnegative') zero = Predicate('zero') symmetric = Predicate('symmetric') invertible = Predicate('invertible') singular = Predicate('singular') orthogonal = Predicate('orthogonal') unitary = Predicate('unitary') normal = Predicate('normal') 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') fullrank = Predicate('fullrank') square = Predicate('square') real_elements = Predicate('real_elements') complex_elements = Predicate('complex_elements') integer_elements = Predicate('integer_elements') 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 isinstance(expr, bool): return if not expr.has(symbol): return if isinstance(expr, AppliedPredicate): if expr.arg == symbol: return expr.func else: return args = [_extract_facts(arg, symbol) for arg in expr.args] if isinstance(expr, And): args = [x for x in args if x is not None] if args: return expr.func(*args) if args and all(x != None for x in args): return expr.func(*args) 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. """ if not isinstance(proposition, (BooleanFunction, AppliedPredicate, bool, BooleanAtom)): raise TypeError("proposition must be a valid logical expression") if not isinstance(assumptions, (BooleanFunction, AppliedPredicate, bool, BooleanAtom)): raise TypeError("assumptions must be a valid logical expression") if isinstance(proposition, AppliedPredicate): key, expr = proposition.func, sympify(proposition.arg) else: key, expr = Q.is_true, sympify(proposition) assumptions = And(assumptions, And(*context)) assumptions = to_cnf(assumptions) local_facts = _extract_facts(assumptions, expr) if local_facts and satisfiable(And(local_facts, known_facts_cnf)) is False: raise ValueError("inconsistent assumptions %s" % assumptions) # direct resolution method, no logic res = key(expr)._eval_ask(assumptions) if res is not None: return res if assumptions == True: return if local_facts is None: 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) 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 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])) 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 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. Instead, run ./bin/ask_update.py. """ 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) items = sorted(mapping.items(), key=str) keys = [str(i[0]) for i in items] values = ['set(%s)' % sorted(i[1], key=str) for i in items] m = LINE.join(['\n'.join( wrap("%s: %s" % (k, v), subsequent_indent=HANG, break_long_words=False)) for k, v in zip(keys, values)]) + ',' return fact_string % (c, m) # handlers 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"), ("nonpositive", "order.AskNonPositiveHandler"), ("nonnegative", "order.AskNonNegativeHandler"), ("zero", "order.AskZeroHandler"), ("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"), ("unitary", "matrices.AskUnitaryHandler"), ("positive_definite", "matrices.AskPositiveDefiniteHandler"), ("upper_triangular", "matrices.AskUpperTriangularHandler"), ("lower_triangular", "matrices.AskLowerTriangularHandler"), ("diagonal", "matrices.AskDiagonalHandler"), ("fullrank", "matrices.AskFullRankHandler"), ("square", "matrices.AskSquareHandler"), ("integer_elements", "matrices.AskIntegerElementsHandler"), ("real_elements", "matrices.AskRealElementsHandler"), ("complex_elements", "matrices.AskComplexElementsHandler"), ] 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.rational, Q.algebraic), Implies(Q.algebraic, Q.complex), 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), Equivalent(Q.nonpositive, ~Q.positive & Q.real), Equivalent(Q.nonnegative, ~Q.negative & Q.real), Equivalent(Q.zero, Q.real & ~Q.nonzero), Implies(Q.zero, Q.even), Implies(Q.orthogonal, Q.positive_definite), Implies(Q.orthogonal, Q.unitary), Implies(Q.unitary & Q.real, Q.orthogonal), Implies(Q.unitary, Q.normal), Implies(Q.unitary, Q.invertible), Implies(Q.normal, Q.square), Implies(Q.diagonal, Q.normal), 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), Implies(Q.invertible, Q.fullrank), Implies(Q.invertible, Q.square), Implies(Q.symmetric, Q.square), Implies(Q.fullrank & Q.square, Q.invertible), Equivalent(Q.invertible, ~Q.singular), Implies(Q.integer_elements, Q.real_elements), Implies(Q.real_elements, Q.complex_elements), ) from sympy.assumptions.ask_generated import known_facts_dict, known_facts_cnf sympy-0.7.4.1/sympy/assumptions/assume.py0000644000175000017500000001307412253362407020666 0ustar georgeskgeorgeskfrom __future__ import print_function, division import inspect from sympy.core.cache import cacheit from sympy.core.singleton import S from sympy.core.sympify import _sympify from sympy.logic.boolalg import Boolean from sympy.utilities.source import get_class from contextlib import contextmanager 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 AppliedPredicate, Q >>> from sympy.assumptions.assume import global_assumptions >>> 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() """ def add(self, *assumptions): """Add an assumption.""" for a in assumptions: super(AssumptionsContext, self).add(a) global_assumptions = AssumptionsContext() 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)) """ __slots__ = [] def __new__(cls, predicate, arg): if not isinstance(arg, bool): # XXX: There is not yet a Basic type for True and False arg = _sympify(arg) return Boolean.__new__(cls, predicate, arg) is_Atom = True # do not attempt to decompose this @property 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) 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 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 @contextmanager def assuming(*assumptions): """ Context manager for assumptions >>> from sympy.assumptions import assuming, Q, ask >>> from sympy.abc import x, y >>> print(ask(Q.integer(x + y))) None >>> with assuming(Q.integer(x), Q.integer(y)): ... print(ask(Q.integer(x + y))) True """ old_global_assumptions = global_assumptions.copy() global_assumptions.update(assumptions) try: yield finally: global_assumptions.clear() global_assumptions.update(old_global_assumptions) sympy-0.7.4.1/sympy/assumptions/tests/0000755000175000017500000000000012253362407020154 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/assumptions/tests/test_assumptions_2.py0000644000175000017500000000365112253362407024400 0ustar georgeskgeorgesk""" rename this to test_assumptions.py when the old assumptions system is deleted """ from sympy.abc import x, y from sympy.assumptions.assume import global_assumptions, Predicate from sympy.assumptions.ask import _extract_facts, Q from sympy.core import symbols from sympy.logic.boolalg import Or from sympy.printing import pretty def test_equal(): """Test for equality""" assert Q.positive(x) == Q.positive(x) assert Q.positive(x) != ~Q.positive(x) assert ~Q.positive(x) == ~Q.positive(x) def test_pretty(): assert pretty(Q.positive(x)) == "Q.positive(x)" assert pretty( set([Q.positive, Q.integer])) == "set([Q.integer, Q.positive])" def test_extract_facts(): a, b = symbols('a b', cls=Predicate) assert _extract_facts(a(x), x) == a assert _extract_facts(a(x), y) is None assert _extract_facts(~a(x), x) == ~a assert _extract_facts(~a(x), y) is None assert _extract_facts(a(x) | b(x), x) == a | b assert _extract_facts(a(x) | ~b(x), x) == a | ~b assert _extract_facts(a(x) & b(y), x) == a assert _extract_facts(a(x) & b(y), y) == b assert _extract_facts(a(x) | b(y), x) == None assert _extract_facts(~(a(x) | b(y)), x) == ~a def test_global(): """Test for global assumptions""" global_assumptions.add(Q.is_true(x > 0)) assert Q.is_true(x > 0) in global_assumptions global_assumptions.remove(Q.is_true(x > 0)) assert not Q.is_true(x > 0) in global_assumptions # same with multiple of assumptions global_assumptions.add(Q.is_true(x > 0), Q.is_true(y > 0)) assert Q.is_true(x > 0) in global_assumptions assert Q.is_true(y > 0) in global_assumptions global_assumptions.clear() assert not Q.is_true(x > 0) in global_assumptions assert not Q.is_true(y > 0) in global_assumptions def test_composite_predicates(): pred = Q.integer | ~Q.positive assert type(pred(x)) is Or assert pred(x) == Q.integer(x) | ~Q.positive(x) sympy-0.7.4.1/sympy/assumptions/tests/test_context.py0000644000175000017500000000220112253362407023244 0ustar georgeskgeorgeskfrom sympy.assumptions import ask, Q from sympy.assumptions.assume import assuming, global_assumptions from sympy.abc import x, y def test_assuming(): with assuming(Q.integer(x)): assert ask(Q.integer(x)) assert not ask(Q.integer(x)) def test_assuming_nested(): assert not ask(Q.integer(x)) assert not ask(Q.integer(y)) with assuming(Q.integer(x)): assert ask(Q.integer(x)) assert not ask(Q.integer(y)) with assuming(Q.integer(y)): assert ask(Q.integer(x)) assert ask(Q.integer(y)) assert ask(Q.integer(x)) assert not ask(Q.integer(y)) assert not ask(Q.integer(x)) assert not ask(Q.integer(y)) def test_finally(): try: with assuming(Q.integer(x)): 1/0 except ZeroDivisionError: pass assert not ask(Q.integer(x)) def test_remove_safe(): global_assumptions.add(Q.integer(x)) with assuming(): assert ask(Q.integer(x)) global_assumptions.remove(Q.integer(x)) assert not ask(Q.integer(x)) assert ask(Q.integer(x)) global_assumptions.clear() # for the benefit of other tests sympy-0.7.4.1/sympy/assumptions/tests/test_query.py0000644000175000017500000024555212253362407022747 0ustar georgeskgeorgeskfrom sympy.abc import t, w, x, y, z, n, k, m, p from sympy.assumptions import (ask, AssumptionsContext, Q, register_handler, remove_handler) from sympy.assumptions.assume import global_assumptions from sympy.assumptions.ask import (compute_known_facts, known_facts_cnf, known_facts_dict, single_fact_lookup) from sympy.assumptions.handlers import AskHandler from sympy.core import I, Integer, oo, pi, Rational, S, symbols, Add from sympy.core.compatibility import exec_ from sympy.functions import (Abs, cos, exp, im, log, re, sign, sin, sqrt, tan, atan, acos, asin, cot, acot) from sympy.logic import Equivalent, Implies, Xor, And, to_cnf, Not from sympy.utilities.pytest import raises, XFAIL, slow from sympy.assumptions.assume import assuming def test_int_1(): z = 1 assert ask(Q.commutative(z)) is True assert ask(Q.integer(z)) is True assert ask(Q.rational(z)) is True assert ask(Q.real(z)) is True assert ask(Q.complex(z)) is True assert ask(Q.irrational(z)) is False assert ask(Q.imaginary(z)) is False assert ask(Q.positive(z)) is True assert ask(Q.negative(z)) is False assert ask(Q.even(z)) is False assert ask(Q.odd(z)) is True assert ask(Q.bounded(z)) is True assert ask(Q.infinitesimal(z)) is False assert ask(Q.prime(z)) is False assert ask(Q.composite(z)) is True assert ask(Q.hermitian(z)) is True assert ask(Q.antihermitian(z)) is False def test_int_11(): z = 11 assert ask(Q.commutative(z)) is True assert ask(Q.integer(z)) is True assert ask(Q.rational(z)) is True assert ask(Q.real(z)) is True assert ask(Q.complex(z)) is True assert ask(Q.irrational(z)) is False assert ask(Q.imaginary(z)) is False assert ask(Q.positive(z)) is True assert ask(Q.negative(z)) is False assert ask(Q.even(z)) is False assert ask(Q.odd(z)) is True assert ask(Q.bounded(z)) is True assert ask(Q.infinitesimal(z)) is False assert ask(Q.prime(z)) is True assert ask(Q.composite(z)) is False assert ask(Q.hermitian(z)) is True assert ask(Q.antihermitian(z)) is False def test_int_12(): z = 12 assert ask(Q.commutative(z)) is True assert ask(Q.integer(z)) is True assert ask(Q.rational(z)) is True assert ask(Q.real(z)) is True assert ask(Q.complex(z)) is True assert ask(Q.irrational(z)) is False assert ask(Q.imaginary(z)) is False assert ask(Q.positive(z)) is True assert ask(Q.negative(z)) is False assert ask(Q.even(z)) is True assert ask(Q.odd(z)) is False assert ask(Q.bounded(z)) is True assert ask(Q.infinitesimal(z)) is False assert ask(Q.prime(z)) is False assert ask(Q.composite(z)) is True assert ask(Q.hermitian(z)) is True assert ask(Q.antihermitian(z)) is False def test_float_1(): z = 1.0 assert ask(Q.commutative(z)) is True assert ask(Q.integer(z)) is True assert ask(Q.rational(z)) is True assert ask(Q.real(z)) is True assert ask(Q.complex(z)) is True assert ask(Q.irrational(z)) is False assert ask(Q.imaginary(z)) is False assert ask(Q.positive(z)) is True assert ask(Q.negative(z)) is False assert ask(Q.even(z)) is False assert ask(Q.odd(z)) is True assert ask(Q.bounded(z)) is True assert ask(Q.infinitesimal(z)) is False assert ask(Q.prime(z)) is False assert ask(Q.composite(z)) is True assert ask(Q.hermitian(z)) is True assert ask(Q.antihermitian(z)) is False z = 7.2123 assert ask(Q.commutative(z)) is True assert ask(Q.integer(z)) is False assert ask(Q.rational(z)) is True assert ask(Q.real(z)) is True assert ask(Q.complex(z)) is True assert ask(Q.irrational(z)) is False assert ask(Q.imaginary(z)) is False assert ask(Q.positive(z)) is True assert ask(Q.negative(z)) is False assert ask(Q.even(z)) is False assert ask(Q.odd(z)) is False assert ask(Q.bounded(z)) is True assert ask(Q.infinitesimal(z)) is False assert ask(Q.prime(z)) is False assert ask(Q.composite(z)) is False assert ask(Q.hermitian(z)) is True assert ask(Q.antihermitian(z)) is False def test_zero_0(): z = Integer(0) assert ask(Q.nonzero(z)) is False assert ask(Q.zero(z)) is True assert ask(Q.commutative(z)) is True assert ask(Q.integer(z)) is True assert ask(Q.rational(z)) is True assert ask(Q.real(z)) is True assert ask(Q.complex(z)) is True assert ask(Q.imaginary(z)) is False assert ask(Q.positive(z)) is False assert ask(Q.negative(z)) is False assert ask(Q.even(z)) is True assert ask(Q.odd(z)) is False assert ask(Q.bounded(z)) is True assert ask(Q.infinitesimal(z)) is True assert ask(Q.prime(z)) is False assert ask(Q.composite(z)) is False assert ask(Q.hermitian(z)) is True assert ask(Q.antihermitian(z)) is False def test_negativeone(): z = Integer(-1) assert ask(Q.nonzero(z)) is True assert ask(Q.zero(z)) is False assert ask(Q.commutative(z)) is True assert ask(Q.integer(z)) is True assert ask(Q.rational(z)) is True assert ask(Q.real(z)) is True assert ask(Q.complex(z)) is True assert ask(Q.irrational(z)) is False assert ask(Q.imaginary(z)) is False assert ask(Q.positive(z)) is False assert ask(Q.negative(z)) is True assert ask(Q.even(z)) is False assert ask(Q.odd(z)) is True assert ask(Q.bounded(z)) is True assert ask(Q.infinitesimal(z)) is False assert ask(Q.prime(z)) is False assert ask(Q.composite(z)) is False assert ask(Q.hermitian(z)) is True assert ask(Q.antihermitian(z)) is False def test_infinity(): assert ask(Q.commutative(oo)) is True assert ask(Q.integer(oo)) is False assert ask(Q.rational(oo)) is False assert ask(Q.algebraic(oo)) is False assert ask(Q.real(oo)) is False assert ask(Q.extended_real(oo)) is True assert ask(Q.complex(oo)) is False assert ask(Q.irrational(oo)) is False assert ask(Q.imaginary(oo)) is False assert ask(Q.positive(oo)) is True assert ask(Q.negative(oo)) is False assert ask(Q.even(oo)) is False assert ask(Q.odd(oo)) is False assert ask(Q.bounded(oo)) is False assert ask(Q.infinitesimal(oo)) is False assert ask(Q.prime(oo)) is False assert ask(Q.composite(oo)) is False assert ask(Q.hermitian(oo)) is False assert ask(Q.antihermitian(oo)) is False def test_neg_infinity(): mm = S.NegativeInfinity assert ask(Q.commutative(mm)) is True assert ask(Q.integer(mm)) is False assert ask(Q.rational(mm)) is False assert ask(Q.algebraic(mm)) is False assert ask(Q.real(mm)) is False assert ask(Q.extended_real(mm)) is True assert ask(Q.complex(mm)) is False assert ask(Q.irrational(mm)) is False assert ask(Q.imaginary(mm)) is False assert ask(Q.positive(mm)) is False assert ask(Q.negative(mm)) is True assert ask(Q.even(mm)) is False assert ask(Q.odd(mm)) is False assert ask(Q.bounded(mm)) is False assert ask(Q.infinitesimal(mm)) is False assert ask(Q.prime(mm)) is False assert ask(Q.composite(mm)) is False assert ask(Q.hermitian(mm)) is False assert ask(Q.antihermitian(mm)) is False def test_nan(): nan = S.NaN assert ask(Q.commutative(nan)) is True assert ask(Q.integer(nan)) is False assert ask(Q.rational(nan)) is False assert ask(Q.algebraic(nan)) is False assert ask(Q.real(nan)) is False assert ask(Q.extended_real(nan)) is False assert ask(Q.complex(nan)) is False assert ask(Q.irrational(nan)) is False assert ask(Q.imaginary(nan)) is False assert ask(Q.positive(nan)) is False assert ask(Q.nonzero(nan)) is True assert ask(Q.zero(nan)) is False assert ask(Q.even(nan)) is False assert ask(Q.odd(nan)) is False assert ask(Q.bounded(nan)) is False assert ask(Q.infinitesimal(nan)) is False assert ask(Q.prime(nan)) is False assert ask(Q.composite(nan)) is False assert ask(Q.hermitian(nan)) is False assert ask(Q.antihermitian(nan)) is False def test_Rational_number(): r = Rational(3, 4) assert ask(Q.commutative(r)) is True assert ask(Q.integer(r)) is False assert ask(Q.rational(r)) is True assert ask(Q.real(r)) is True assert ask(Q.complex(r)) is True assert ask(Q.irrational(r)) is False assert ask(Q.imaginary(r)) is False assert ask(Q.positive(r)) is True assert ask(Q.negative(r)) is False assert ask(Q.even(r)) is False assert ask(Q.odd(r)) is False assert ask(Q.bounded(r)) is True assert ask(Q.infinitesimal(r)) is False assert ask(Q.prime(r)) is False assert ask(Q.composite(r)) is False assert ask(Q.hermitian(r)) is True assert ask(Q.antihermitian(r)) is False r = Rational(1, 4) assert ask(Q.positive(r)) is True assert ask(Q.negative(r)) is False r = Rational(5, 4) assert ask(Q.negative(r)) is False assert ask(Q.positive(r)) is True r = Rational(5, 3) assert ask(Q.positive(r)) is True assert ask(Q.negative(r)) is False r = Rational(-3, 4) assert ask(Q.positive(r)) is False assert ask(Q.negative(r)) is True r = Rational(-1, 4) assert ask(Q.positive(r)) is False assert ask(Q.negative(r)) is True r = Rational(-5, 4) assert ask(Q.negative(r)) is True assert ask(Q.positive(r)) is False r = Rational(-5, 3) assert ask(Q.positive(r)) is False assert ask(Q.negative(r)) is True def test_sqrt_2(): z = sqrt(2) assert ask(Q.commutative(z)) is True assert ask(Q.integer(z)) is False assert ask(Q.rational(z)) is False assert ask(Q.real(z)) is True assert ask(Q.complex(z)) is True assert ask(Q.irrational(z)) is True assert ask(Q.imaginary(z)) is False assert ask(Q.positive(z)) is True assert ask(Q.negative(z)) is False assert ask(Q.even(z)) is False assert ask(Q.odd(z)) is False assert ask(Q.bounded(z)) is True assert ask(Q.infinitesimal(z)) is False assert ask(Q.prime(z)) is False assert ask(Q.composite(z)) is False assert ask(Q.hermitian(z)) is True assert ask(Q.antihermitian(z)) is False def test_pi(): z = S.Pi assert ask(Q.commutative(z)) is True assert ask(Q.integer(z)) is False assert ask(Q.rational(z)) is False assert ask(Q.algebraic(z)) is False assert ask(Q.real(z)) is True assert ask(Q.complex(z)) is True assert ask(Q.irrational(z)) is True assert ask(Q.imaginary(z)) is False assert ask(Q.positive(z)) is True assert ask(Q.negative(z)) is False assert ask(Q.even(z)) is False assert ask(Q.odd(z)) is False assert ask(Q.bounded(z)) is True assert ask(Q.infinitesimal(z)) is False assert ask(Q.prime(z)) is False assert ask(Q.composite(z)) is False assert ask(Q.hermitian(z)) is True assert ask(Q.antihermitian(z)) is False z = S.Pi + 1 assert ask(Q.commutative(z)) is True assert ask(Q.integer(z)) is False assert ask(Q.rational(z)) is False assert ask(Q.algebraic(z)) is False assert ask(Q.real(z)) is True assert ask(Q.complex(z)) is True assert ask(Q.irrational(z)) is True assert ask(Q.imaginary(z)) is False assert ask(Q.positive(z)) is True assert ask(Q.negative(z)) is False assert ask(Q.even(z)) is False assert ask(Q.odd(z)) is False assert ask(Q.bounded(z)) is True assert ask(Q.infinitesimal(z)) is False assert ask(Q.prime(z)) is False assert ask(Q.composite(z)) is False assert ask(Q.hermitian(z)) is True assert ask(Q.antihermitian(z)) is False z = 2*S.Pi assert ask(Q.commutative(z)) is True assert ask(Q.integer(z)) is False assert ask(Q.rational(z)) is False assert ask(Q.algebraic(z)) is False assert ask(Q.real(z)) is True assert ask(Q.complex(z)) is True assert ask(Q.irrational(z)) is True assert ask(Q.imaginary(z)) is False assert ask(Q.positive(z)) is True assert ask(Q.negative(z)) is False assert ask(Q.even(z)) is False assert ask(Q.odd(z)) is False assert ask(Q.bounded(z)) is True assert ask(Q.infinitesimal(z)) is False assert ask(Q.prime(z)) is False assert ask(Q.composite(z)) is False assert ask(Q.hermitian(z)) is True assert ask(Q.antihermitian(z)) is False z = S.Pi ** 2 assert ask(Q.commutative(z)) is True assert ask(Q.integer(z)) is False assert ask(Q.rational(z)) is False assert ask(Q.algebraic(z)) is False assert ask(Q.real(z)) is True assert ask(Q.complex(z)) is True assert ask(Q.irrational(z)) is True assert ask(Q.imaginary(z)) is False assert ask(Q.positive(z)) is True assert ask(Q.negative(z)) is False assert ask(Q.even(z)) is False assert ask(Q.odd(z)) is False assert ask(Q.bounded(z)) is True assert ask(Q.infinitesimal(z)) is False assert ask(Q.prime(z)) is False assert ask(Q.composite(z)) is False assert ask(Q.hermitian(z)) is True assert ask(Q.antihermitian(z)) is False z = (1 + S.Pi) ** 2 assert ask(Q.commutative(z)) is True assert ask(Q.integer(z)) is False assert ask(Q.rational(z)) is False assert ask(Q.algebraic(z)) is False assert ask(Q.real(z)) is True assert ask(Q.complex(z)) is True assert ask(Q.irrational(z)) is True assert ask(Q.imaginary(z)) is False assert ask(Q.positive(z)) is True assert ask(Q.negative(z)) is False assert ask(Q.even(z)) is False assert ask(Q.odd(z)) is False assert ask(Q.bounded(z)) is True assert ask(Q.infinitesimal(z)) is False assert ask(Q.prime(z)) is False assert ask(Q.composite(z)) is False assert ask(Q.hermitian(z)) is True assert ask(Q.antihermitian(z)) is False def test_E(): z = S.Exp1 assert ask(Q.commutative(z)) is True assert ask(Q.integer(z)) is False assert ask(Q.rational(z)) is False assert ask(Q.algebraic(z)) is False assert ask(Q.real(z)) is True assert ask(Q.complex(z)) is True assert ask(Q.irrational(z)) is True assert ask(Q.imaginary(z)) is False assert ask(Q.positive(z)) is True assert ask(Q.negative(z)) is False assert ask(Q.even(z)) is False assert ask(Q.odd(z)) is False assert ask(Q.bounded(z)) is True assert ask(Q.infinitesimal(z)) is False assert ask(Q.prime(z)) is False assert ask(Q.composite(z)) is False assert ask(Q.hermitian(z)) is True assert ask(Q.antihermitian(z)) is False def test_GoldenRatio(): z = S.GoldenRatio assert ask(Q.commutative(z)) is True assert ask(Q.integer(z)) is False assert ask(Q.rational(z)) is False assert ask(Q.algebraic(z)) is True assert ask(Q.real(z)) is True assert ask(Q.complex(z)) is True assert ask(Q.irrational(z)) is True assert ask(Q.imaginary(z)) is False assert ask(Q.positive(z)) is True assert ask(Q.negative(z)) is False assert ask(Q.even(z)) is False assert ask(Q.odd(z)) is False assert ask(Q.bounded(z)) is True assert ask(Q.infinitesimal(z)) is False assert ask(Q.prime(z)) is False assert ask(Q.composite(z)) is False assert ask(Q.hermitian(z)) is True assert ask(Q.antihermitian(z)) is False def test_I(): z = I assert ask(Q.commutative(z)) is True assert ask(Q.integer(z)) is False assert ask(Q.rational(z)) is False assert ask(Q.algebraic(z)) is True assert ask(Q.real(z)) is False assert ask(Q.complex(z)) is True assert ask(Q.irrational(z)) is False assert ask(Q.imaginary(z)) is True assert ask(Q.positive(z)) is False assert ask(Q.negative(z)) is False assert ask(Q.even(z)) is False assert ask(Q.odd(z)) is False assert ask(Q.bounded(z)) is True assert ask(Q.infinitesimal(z)) is False assert ask(Q.prime(z)) is False assert ask(Q.composite(z)) is False assert ask(Q.hermitian(z)) is False assert ask(Q.antihermitian(z)) is True z = 1 + I assert ask(Q.commutative(z)) is True assert ask(Q.integer(z)) is False assert ask(Q.rational(z)) is False assert ask(Q.algebraic(z)) is True assert ask(Q.real(z)) is False assert ask(Q.complex(z)) is True assert ask(Q.irrational(z)) is False assert ask(Q.imaginary(z)) is False assert ask(Q.positive(z)) is False assert ask(Q.negative(z)) is False assert ask(Q.even(z)) is False assert ask(Q.odd(z)) is False assert ask(Q.bounded(z)) is True assert ask(Q.infinitesimal(z)) is False assert ask(Q.prime(z)) is False assert ask(Q.composite(z)) is False assert ask(Q.hermitian(z)) is False assert ask(Q.antihermitian(z)) is False z = I*(1 + I) assert ask(Q.commutative(z)) is True assert ask(Q.integer(z)) is False assert ask(Q.rational(z)) is False assert ask(Q.algebraic(z)) is True assert ask(Q.real(z)) is False assert ask(Q.complex(z)) is True assert ask(Q.irrational(z)) is False assert ask(Q.imaginary(z)) is False assert ask(Q.positive(z)) is False assert ask(Q.negative(z)) is False assert ask(Q.even(z)) is False assert ask(Q.odd(z)) is False assert ask(Q.bounded(z)) is True assert ask(Q.infinitesimal(z)) is False assert ask(Q.prime(z)) is False assert ask(Q.composite(z)) is False assert ask(Q.hermitian(z)) is False assert ask(Q.antihermitian(z)) is False z = I**(I) assert ask(Q.imaginary(z)) is False assert ask(Q.real(z)) is True z = (-I)**(I) assert ask(Q.imaginary(z)) is False assert ask(Q.real(z)) is True z = (3*I)**(I) assert ask(Q.imaginary(z)) is False assert ask(Q.real(z)) is False z = (1)**(I) assert ask(Q.imaginary(z)) is False assert ask(Q.real(z)) is True z = (-1)**(I) assert ask(Q.imaginary(z)) is False assert ask(Q.real(z)) is True z = (1+I)**(I) assert ask(Q.imaginary(z)) is False assert ask(Q.real(z)) is False z = (I)**(I+3) assert ask(Q.imaginary(z)) is True assert ask(Q.real(z)) is False z = (I)**(I+2) assert ask(Q.imaginary(z)) is False assert ask(Q.real(z)) is True z = (I)**(2) assert ask(Q.imaginary(z)) is False assert ask(Q.real(z)) is True z = (I)**(3) assert ask(Q.imaginary(z)) is True assert ask(Q.real(z)) is False z = (3)**(I) assert ask(Q.imaginary(z)) is False assert ask(Q.real(z)) is False z = (I)**(0) assert ask(Q.imaginary(z)) is False assert ask(Q.real(z)) is True @slow def test_bounded(): x, y, z = symbols('x,y,z') assert ask(Q.bounded(x)) is None assert ask(Q.bounded(x), Q.bounded(x)) is True assert ask(Q.bounded(x), Q.bounded(y)) is None assert ask(Q.bounded(x), Q.complex(x)) is None assert ask(Q.bounded(x + 1)) is None assert ask(Q.bounded(x + 1), Q.bounded(x)) is True a = x + y x, y = a.args # B + B assert ask(Q.bounded(a), Q.bounded(x) & Q.bounded(y)) is True assert ask( Q.bounded(a), Q.bounded(x) & Q.bounded(y) & Q.positive(x)) is True assert ask( Q.bounded(a), Q.bounded(x) & Q.bounded(y) & Q.positive(y)) is True assert ask(Q.bounded(a), Q.bounded(x) & Q.bounded(y) & Q.positive(x) & Q.positive(y)) is True assert ask(Q.bounded(a), Q.bounded(x) & Q.bounded(y) & Q.positive(x) & ~Q.positive(y)) is True assert ask(Q.bounded(a), Q.bounded(x) & Q.bounded(y) & ~Q.positive(x) & Q.positive(y)) is True assert ask(Q.bounded(a), Q.bounded(x) & Q.bounded(y) & ~Q.positive(x) & ~Q.positive(y)) is True # B + U assert ask(Q.bounded(a), Q.bounded(x) & ~Q.bounded(y)) is False assert ask( Q.bounded(a), Q.bounded(x) & ~Q.bounded(y) & Q.positive(x)) is False assert ask( Q.bounded(a), Q.bounded(x) & ~Q.bounded(y) & Q.positive(y)) is False assert ask(Q.bounded(a), Q.bounded(x) & ~Q.bounded(y) & Q.positive(x) & Q.positive(y)) is False assert ask(Q.bounded(a), Q.bounded(x) & ~Q.bounded(y) & Q.positive(x) & ~Q.positive(y)) is False assert ask(Q.bounded(a), Q.bounded(x) & ~Q.bounded(y) & ~Q.positive(x) & Q.positive(y)) is False assert ask(Q.bounded(a), Q.bounded(x) & ~Q.bounded(y) & ~Q.positive(x) & ~Q.positive(y)) is False # B + ? assert ask(Q.bounded(a), Q.bounded(x)) is None assert ask(Q.bounded(a), Q.bounded(x) & Q.positive(x)) is None assert ask(Q.bounded(a), Q.bounded(x) & Q.positive(y)) is None assert ask( Q.bounded(a), Q.bounded(x) & Q.positive(x) & Q.positive(y)) is None assert ask( Q.bounded(a), Q.bounded(x) & Q.positive(x) & ~Q.positive(y)) is None assert ask( Q.bounded(a), Q.bounded(x) & ~Q.positive(x) & Q.positive(y)) is None assert ask( Q.bounded(a), Q.bounded(x) & ~Q.positive(x) & ~Q.positive(y)) is None # U + U assert ask(Q.bounded(a), ~Q.bounded(x) & ~Q.bounded(y)) is None assert ask( Q.bounded(a), ~Q.bounded(x) & ~Q.bounded(y) & Q.positive(x)) is None assert ask( Q.bounded(a), ~Q.bounded(x) & ~Q.bounded(y) & Q.positive(y)) is None assert ask(Q.bounded(a), ~Q.bounded(x) & ~Q.bounded(y) & Q.positive(x) & Q.positive(y)) is False assert ask(Q.bounded(a), ~Q.bounded(x) & ~Q.bounded(y) & Q.positive(x) & ~Q.positive(y)) is None assert ask(Q.bounded(a), ~Q.bounded(x) & ~Q.bounded(y) & ~Q.positive(x) & Q.positive(y)) is None assert ask(Q.bounded(a), ~Q.bounded(x) & ~Q.bounded(y) & ~Q.positive(x) & ~Q.positive(y)) is False # U + ? assert ask(Q.bounded(a), ~Q.bounded(y)) is None assert ask(Q.bounded(a), ~Q.bounded(y) & Q.positive(x)) is None assert ask(Q.bounded(a), ~Q.bounded(y) & Q.positive(y)) is None assert ask( Q.bounded(a), ~Q.bounded(y) & Q.positive(x) & Q.positive(y)) is False assert ask( Q.bounded(a), ~Q.bounded(y) & Q.positive(x) & ~Q.positive(y)) is None assert ask( Q.bounded(a), ~Q.bounded(y) & ~Q.positive(x) & Q.positive(y)) is None assert ask( Q.bounded(a), ~Q.bounded(y) & ~Q.positive(x) & ~Q.positive(y)) is False # ? + ? assert ask(Q.bounded(a),) is None assert ask(Q.bounded(a), Q.positive(x)) is None assert ask(Q.bounded(a), Q.positive(y)) is None assert ask(Q.bounded(a), Q.positive(x) & Q.positive(y)) is None assert ask(Q.bounded(a), Q.positive(x) & ~Q.positive(y)) is None assert ask(Q.bounded(a), ~Q.positive(x) & Q.positive(y)) is None assert ask(Q.bounded(a), ~Q.positive(x) & ~Q.positive(y)) is None a = x + y + z x, y, z = a.args assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.negative(y) & Q.bounded(y) & Q.negative(z) & Q.bounded(z)) is True assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.negative(y) & Q.bounded(y) & Q.bounded(z)) is True assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.negative(y) & Q.bounded(y) & Q.positive(z) & Q.bounded(z)) is True assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.negative(y) & Q.bounded(y) & Q.negative(z) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.negative(y) & Q.bounded(y) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.negative(y) & Q.bounded(y) & Q.positive(z) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.negative(y) & Q.bounded(y) & Q.negative(z)) is None assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.negative(y) & Q.bounded(y)) is None assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.negative(y) & Q.bounded(y) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.bounded(y) & Q.bounded(z)) is True assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.bounded(y) & Q.positive(z) & Q.bounded(z)) is True assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.bounded(y) & Q.negative(z) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.bounded(y) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.bounded(y) & Q.positive(z) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.bounded(y) & Q.negative(z)) is None assert ask( Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.bounded(y)) is None assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.bounded(y) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.positive(y) & Q.bounded(y) & Q.positive(z) & Q.bounded(z)) is True assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.positive(y) & Q.bounded(y) & Q.negative(z) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.positive(y) & Q.bounded(y) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.positive(y) & Q.bounded(y) & Q.positive(z) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.positive(y) & Q.bounded(y) & Q.negative(z)) is None assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.positive(y) & Q.bounded(y)) is None assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.positive(y) & Q.bounded(y) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.negative(y) & ~Q.bounded(y) & Q.negative(z) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.negative(y) & ~Q.bounded(y) & ~Q.bounded(z)) is None assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.negative(y) & ~Q.bounded(y) & Q.positive(z) & ~Q.bounded(z)) is None assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.negative(y) & ~Q.bounded(y) & Q.negative(z)) is False assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.negative(y) & ~Q.bounded(y)) is None assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.negative(y) & ~Q.bounded(y) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & ~Q.bounded(y) & ~Q.bounded(z)) is None assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & ~Q.bounded(y) & Q.positive(z) & ~Q.bounded(z)) is None assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & ~Q.bounded(y) & Q.negative(z)) is None assert ask( Q.bounded(a), Q.negative(x) & Q.bounded(x) & ~Q.bounded(y)) is None assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & ~Q.bounded(y) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.positive(y) & ~Q.bounded(y) & Q.positive(z) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.positive(y) & ~Q.bounded(y) & Q.negative(z)) is None assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.positive(y) & ~Q.bounded(y)) is None assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.positive(y) & ~Q.bounded(y) & Q.positive(z)) is False assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.negative(y) & Q.negative(z)) is None assert ask( Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.negative(y)) is None assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.negative(y) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x)) is None assert ask( Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.negative(x) & Q.bounded(x) & Q.positive(y) & Q.positive(z)) is None assert ask( Q.bounded(a), Q.bounded(x) & Q.bounded(y) & Q.bounded(z)) is True assert ask(Q.bounded(a), Q.bounded(x) & Q.bounded(y) & Q.positive(z) & Q.bounded(z)) is True assert ask(Q.bounded(a), Q.bounded(x) & Q.bounded(y) & Q.negative(z) & ~Q.bounded(z)) is False assert ask( Q.bounded(a), Q.bounded(x) & Q.bounded(y) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.bounded(x) & Q.bounded(y) & Q.positive(z) & ~Q.bounded(z)) is False assert ask( Q.bounded(a), Q.bounded(x) & Q.bounded(y) & Q.negative(z)) is None assert ask(Q.bounded(a), Q.bounded(x) & Q.bounded(y)) is None assert ask( Q.bounded(a), Q.bounded(x) & Q.bounded(y) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.bounded(x) & Q.positive(y) & Q.bounded(y) & Q.positive(z) & Q.bounded(z)) is True assert ask(Q.bounded(a), Q.bounded(x) & Q.positive(y) & Q.bounded(y) & Q.negative(z) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.bounded(x) & Q.positive(y) & Q.bounded(y) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.bounded(x) & Q.positive(y) & Q.bounded(y) & Q.positive(z) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.bounded(x) & Q.positive(y) & Q.bounded(y) & Q.negative(z)) is None assert ask( Q.bounded(a), Q.bounded(x) & Q.positive(y) & Q.bounded(y)) is None assert ask(Q.bounded(a), Q.bounded(x) & Q.positive(y) & Q.bounded(y) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.bounded(x) & Q.negative(y) & ~Q.bounded(y) & Q.negative(z) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.bounded(x) & Q.negative(y) & ~Q.bounded(y) & ~Q.bounded(z)) is None assert ask(Q.bounded(a), Q.bounded(x) & Q.negative(y) & ~Q.bounded(y) & Q.positive(z) & ~Q.bounded(z)) is None assert ask(Q.bounded(a), Q.bounded(x) & Q.negative(y) & ~Q.bounded(y) & Q.negative(z)) is False assert ask( Q.bounded(a), Q.bounded(x) & Q.negative(y) & ~Q.bounded(y)) is None assert ask(Q.bounded(a), Q.bounded(x) & Q.negative(y) & ~Q.bounded(y) & Q.positive(z)) is None assert ask( Q.bounded(a), Q.bounded(x) & ~Q.bounded(y) & ~Q.bounded(z)) is None assert ask(Q.bounded(a), Q.bounded(x) & ~Q.bounded(y) & Q.positive(z) & ~Q.bounded(z)) is None assert ask( Q.bounded(a), Q.bounded(x) & ~Q.bounded(y) & Q.negative(z)) is None assert ask(Q.bounded(a), Q.bounded(x) & ~Q.bounded(y)) is None assert ask( Q.bounded(a), Q.bounded(x) & ~Q.bounded(y) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.bounded(x) & Q.positive(y) & ~Q.bounded(y) & Q.positive(z) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.bounded(x) & Q.positive(y) & ~Q.bounded(y) & Q.negative(z)) is None assert ask( Q.bounded(a), Q.bounded(x) & Q.positive(y) & ~Q.bounded(y)) is None assert ask(Q.bounded(a), Q.bounded(x) & Q.positive(y) & ~Q.bounded(y) & Q.positive(z)) is False assert ask( Q.bounded(a), Q.bounded(x) & Q.negative(y) & Q.negative(z)) is None assert ask(Q.bounded(a), Q.bounded(x) & Q.negative(y)) is None assert ask( Q.bounded(a), Q.bounded(x) & Q.negative(y) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.bounded(x)) is None assert ask(Q.bounded(a), Q.bounded(x) & Q.positive(z)) is None assert ask( Q.bounded(a), Q.bounded(x) & Q.positive(y) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x) & Q.positive(y) & Q.bounded(y) & Q.positive(z) & Q.bounded(z)) is True assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x) & Q.positive(y) & Q.bounded(y) & Q.negative(z) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x) & Q.positive(y) & Q.bounded(y) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x) & Q.positive(y) & Q.bounded(y) & Q.positive(z) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x) & Q.positive(y) & Q.bounded(y) & Q.negative(z)) is None assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x) & Q.positive(y) & Q.bounded(y)) is None assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x) & Q.positive(y) & Q.bounded(y) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x) & Q.negative(y) & ~Q.bounded(y) & Q.negative(z) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x) & Q.negative(y) & ~Q.bounded(y) & ~Q.bounded(z)) is None assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x) & Q.negative(y) & ~Q.bounded(y) & Q.positive(z) & ~Q.bounded(z)) is None assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x) & Q.negative(y) & ~Q.bounded(y) & Q.negative(z)) is False assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x) & Q.negative(y) & ~Q.bounded(y)) is None assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x) & Q.negative(y) & ~Q.bounded(y) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x) & ~Q.bounded(y) & ~Q.bounded(z)) is None assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x) & ~Q.bounded(y) & Q.positive(z) & ~Q.bounded(z)) is None assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x) & ~Q.bounded(y) & Q.negative(z)) is None assert ask( Q.bounded(a), Q.positive(x) & Q.bounded(x) & ~Q.bounded(y)) is None assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x) & ~Q.bounded(y) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x) & Q.positive(y) & ~Q.bounded(y) & Q.positive(z) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x) & Q.positive(y) & ~Q.bounded(y) & Q.negative(z)) is None assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x) & Q.positive(y) & ~Q.bounded(y)) is None assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x) & Q.positive(y) & ~Q.bounded(y) & Q.positive(z)) is False assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x) & Q.negative(y) & Q.negative(z)) is None assert ask( Q.bounded(a), Q.positive(x) & Q.bounded(x) & Q.negative(y)) is None assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x) & Q.negative(y) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x)) is None assert ask( Q.bounded(a), Q.positive(x) & Q.bounded(x) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.positive(x) & Q.bounded(x) & Q.positive(y) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.negative(x) & ~Q.bounded(x) & Q.negative(y) & ~Q.bounded(y) & Q.negative(z) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.negative(x) & ~Q.bounded(x) & Q.negative(y) & ~Q.bounded(y) & ~Q.bounded(z)) is None assert ask(Q.bounded(a), Q.negative(x) & ~Q.bounded(x) & Q.negative(y) & ~Q.bounded(y) & Q.positive(z) & ~Q.bounded(z)) is None assert ask(Q.bounded(a), Q.negative(x) & ~Q.bounded(x) & Q.negative(y) & ~Q.bounded(y) & Q.negative(z)) is False assert ask(Q.bounded(a), Q.negative(x) & ~Q.bounded(x) & Q.negative(y) & ~Q.bounded(y)) is None assert ask(Q.bounded(a), Q.negative(x) & ~Q.bounded(x) & Q.negative(y) & ~Q.bounded(y) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.negative(x) & ~Q.bounded(x) & ~Q.bounded(y) & ~Q.bounded(z)) is None assert ask(Q.bounded(a), Q.negative(x) & ~Q.bounded(x) & ~Q.bounded(y) & Q.positive(z) & ~Q.bounded(z)) is None assert ask(Q.bounded(a), Q.negative(x) & ~Q.bounded(x) & ~Q.bounded(y) & Q.negative(z)) is None assert ask( Q.bounded(a), Q.negative(x) & ~Q.bounded(x) & ~Q.bounded(y)) is None assert ask(Q.bounded(a), Q.negative(x) & ~Q.bounded(x) & ~Q.bounded(y) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.negative(x) & ~Q.bounded(x) & Q.positive(y) & ~Q.bounded(y) & Q.positive(z) & ~Q.bounded(z)) is None assert ask(Q.bounded(a), Q.negative(x) & ~Q.bounded(x) & Q.positive(y) & ~Q.bounded(y) & Q.negative(z)) is None assert ask(Q.bounded(a), Q.negative(x) & ~Q.bounded(x) & Q.positive(y) & ~Q.bounded(y)) is None assert ask(Q.bounded(a), Q.negative(x) & ~Q.bounded(x) & Q.positive(y) & ~Q.bounded(y) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.negative(x) & ~Q.bounded(x) & Q.negative(y) & Q.negative(z)) is False assert ask( Q.bounded(a), Q.negative(x) & ~Q.bounded(x) & Q.negative(y)) is None assert ask(Q.bounded(a), Q.negative(x) & ~Q.bounded(x) & Q.negative(y) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.negative(x) & ~Q.bounded(x)) is None assert ask( Q.bounded(a), Q.negative(x) & ~Q.bounded(x) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.negative(x) & ~Q.bounded(x) & Q.positive(y) & Q.positive(z)) is None assert ask( Q.bounded(a), ~Q.bounded(x) & ~Q.bounded(y) & ~Q.bounded(z)) is None assert ask(Q.bounded(a), ~Q.bounded(x) & ~Q.bounded(y) & Q.positive(z) & ~Q.bounded(z)) is None assert ask( Q.bounded(a), ~Q.bounded(x) & ~Q.bounded(y) & Q.negative(z)) is None assert ask(Q.bounded(a), ~Q.bounded(x) & ~Q.bounded(y)) is None assert ask( Q.bounded(a), ~Q.bounded(x) & ~Q.bounded(y) & Q.positive(z)) is None assert ask(Q.bounded(a), ~Q.bounded(x) & Q.positive(y) & ~Q.bounded(y) & Q.positive(z) & ~Q.bounded(z)) is None assert ask(Q.bounded(a), ~Q.bounded(x) & Q.positive(y) & ~Q.bounded(y) & Q.negative(z)) is None assert ask( Q.bounded(a), ~Q.bounded(x) & Q.positive(y) & ~Q.bounded(y)) is None assert ask(Q.bounded(a), ~Q.bounded(x) & Q.positive(y) & ~Q.bounded(y) & Q.positive(z)) is None assert ask( Q.bounded(a), ~Q.bounded(x) & Q.negative(y) & Q.negative(z)) is None assert ask(Q.bounded(a), ~Q.bounded(x) & Q.negative(y)) is None assert ask( Q.bounded(a), ~Q.bounded(x) & Q.negative(y) & Q.positive(z)) is None assert ask(Q.bounded(a), ~Q.bounded(x)) is None assert ask(Q.bounded(a), ~Q.bounded(x) & Q.positive(z)) is None assert ask( Q.bounded(a), ~Q.bounded(x) & Q.positive(y) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.positive(x) & ~Q.bounded(x) & Q.positive(y) & ~Q.bounded(y) & Q.positive(z) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.positive(x) & ~Q.bounded(x) & Q.positive(y) & ~Q.bounded(y) & Q.negative(z)) is None assert ask(Q.bounded(a), Q.positive(x) & ~Q.bounded(x) & Q.positive(y) & ~Q.bounded(y)) is None assert ask(Q.bounded(a), Q.positive(x) & ~Q.bounded(x) & Q.positive(y) & ~Q.bounded(y) & Q.positive(z)) is False assert ask(Q.bounded(a), Q.positive(x) & ~Q.bounded(x) & Q.negative(y) & Q.negative(z)) is None assert ask( Q.bounded(a), Q.positive(x) & ~Q.bounded(x) & Q.negative(y)) is None assert ask(Q.bounded(a), Q.positive(x) & ~Q.bounded(x) & Q.negative(y) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.positive(x) & ~Q.bounded(x)) is None assert ask( Q.bounded(a), Q.positive(x) & ~Q.bounded(x) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.positive(x) & ~Q.bounded(x) & Q.positive(y) & Q.positive(z)) is False assert ask( Q.bounded(a), Q.negative(x) & Q.negative(y) & Q.negative(z)) is None assert ask(Q.bounded(a), Q.negative(x) & Q.negative(y)) is None assert ask( Q.bounded(a), Q.negative(x) & Q.negative(y) & Q.positive(z)) is None assert ask(Q.bounded(a), Q.negative(x)) is None assert ask(Q.bounded(a), Q.negative(x) & Q.positive(z)) is None assert ask( Q.bounded(a), Q.negative(x) & Q.positive(y) & Q.positive(z)) is None assert ask(Q.bounded(a)) is None assert ask(Q.bounded(a), Q.positive(z)) is None assert ask(Q.bounded(a), Q.positive(y) & Q.positive(z)) is None assert ask( Q.bounded(a), Q.positive(x) & Q.positive(y) & Q.positive(z)) is None x, y, z = symbols('x,y,z') assert ask(Q.bounded(2*x)) is None assert ask(Q.bounded(2*x), Q.bounded(x)) is True a = x*y x, y = a.args assert ask(Q.bounded(a), Q.bounded(x) & Q.bounded(y)) is True assert ask(Q.bounded(a), Q.bounded(x) & ~Q.bounded(y)) is False assert ask(Q.bounded(a), Q.bounded(x)) is None assert ask(Q.bounded(a), ~Q.bounded(x) & Q.bounded(y)) is False assert ask(Q.bounded(a), ~Q.bounded(x) & ~Q.bounded(y)) is False assert ask(Q.bounded(a), ~Q.bounded(x)) is None assert ask(Q.bounded(a), Q.bounded(y)) is None assert ask(Q.bounded(a), ~Q.bounded(y)) is None assert ask(Q.bounded(a)) is None a = x*y*z x, y, z = a.args assert ask( Q.bounded(a), Q.bounded(x) & Q.bounded(y) & Q.bounded(z)) is True assert ask( Q.bounded(a), Q.bounded(x) & Q.bounded(y) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.bounded(x) & Q.bounded(y)) is None assert ask( Q.bounded(a), Q.bounded(x) & ~Q.bounded(y) & Q.bounded(z)) is False assert ask( Q.bounded(a), Q.bounded(x) & ~Q.bounded(y) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), Q.bounded(x) & ~Q.bounded(y)) is None assert ask(Q.bounded(a), Q.bounded(x) & Q.bounded(z)) is None assert ask(Q.bounded(a), Q.bounded(x) & ~Q.bounded(z)) is None assert ask(Q.bounded(a), Q.bounded(x)) is None assert ask( Q.bounded(a), ~Q.bounded(x) & Q.bounded(y) & Q.bounded(z)) is False assert ask( Q.bounded(a), ~Q.bounded(x) & Q.bounded(y) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), ~Q.bounded(x) & Q.bounded(y)) is None assert ask( Q.bounded(a), ~Q.bounded(x) & ~Q.bounded(y) & Q.bounded(z)) is False assert ask( Q.bounded(a), ~Q.bounded(x) & ~Q.bounded(y) & ~Q.bounded(z)) is False assert ask(Q.bounded(a), ~Q.bounded(x) & ~Q.bounded(y)) is None assert ask(Q.bounded(a), ~Q.bounded(x) & Q.bounded(z)) is None assert ask(Q.bounded(a), ~Q.bounded(x) & ~Q.bounded(z)) is None assert ask(Q.bounded(a), ~Q.bounded(x)) is None assert ask(Q.bounded(a), Q.bounded(y) & Q.bounded(z)) is None assert ask(Q.bounded(a), Q.bounded(y) & ~Q.bounded(z)) is None assert ask(Q.bounded(a), Q.bounded(y)) is None assert ask(Q.bounded(a), ~Q.bounded(y) & Q.bounded(z)) is None assert ask(Q.bounded(a), ~Q.bounded(y) & ~Q.bounded(z)) is None assert ask(Q.bounded(a), ~Q.bounded(y)) is None assert ask(Q.bounded(a), Q.bounded(z)) is None assert ask(Q.bounded(a), ~Q.bounded(z)) is None assert ask(Q.bounded(a), ~Q.bounded(z) & Q.nonzero(x) & Q.nonzero(y) & Q.nonzero(z)) is None assert ask(Q.bounded(a), ~Q.bounded(y) & ~Q.bounded(z) & Q.nonzero(x) & Q.nonzero(y) & Q.nonzero(z)) is False x, y, z = symbols('x,y,z') assert ask(Q.bounded(x**2)) is None assert ask(Q.bounded(2**x)) is None assert ask(Q.bounded(2**x), Q.bounded(x)) is True assert ask(Q.bounded(x**x)) is None assert ask(Q.bounded(Rational(1, 2) ** x)) is None assert ask(Q.bounded(Rational(1, 2) ** x), Q.positive(x)) is True assert ask(Q.bounded(Rational(1, 2) ** x), Q.negative(x)) is None assert ask(Q.bounded(S(2) ** x), Q.negative(x)) is True assert ask(Q.bounded(sqrt(x))) is None assert ask(Q.bounded(2**x), ~Q.bounded(x)) is False assert ask(Q.bounded(x**2), ~Q.bounded(x)) is False # sign function assert ask(Q.bounded(sign(x))) is True assert ask(Q.bounded(sign(x)), ~Q.bounded(x)) is True # exponential functions assert ask(Q.bounded(log(x))) is None assert ask(Q.bounded(log(x)), Q.bounded(x)) is True assert ask(Q.bounded(exp(x))) is None assert ask(Q.bounded(exp(x)), Q.bounded(x)) is True assert ask(Q.bounded(exp(2))) is True # trigonometric functions assert ask(Q.bounded(sin(x))) is True assert ask(Q.bounded(sin(x)), ~Q.bounded(x)) is True assert ask(Q.bounded(cos(x))) is True assert ask(Q.bounded(cos(x)), ~Q.bounded(x)) is True assert ask(Q.bounded(2*sin(x))) is True assert ask(Q.bounded(sin(x)**2)) is True assert ask(Q.bounded(cos(x)**2)) is True assert ask(Q.bounded(cos(x) + sin(x))) is True @XFAIL def test_bounded_xfail(): """We need to support relations in ask for this to work""" assert ask(Q.bounded(sin(x)**x)) is True assert ask(Q.bounded(cos(x)**x)) is True @XFAIL def test_imaginary_xfail(): assert ask(Q.imaginary(0**I)) is None assert ask(Q.imaginary(0**(-I))) is None @XFAIL def test_real_xfail(): assert ask(Q.real(0**I)) is None assert ask(Q.real(0**(-I))) is None def test_commutative(): """By default objects are Q.commutative that is why it returns True for both key=True and key=False""" assert ask(Q.commutative(x)) is True assert ask(Q.commutative(x), ~Q.commutative(x)) is False assert ask(Q.commutative(x), Q.complex(x)) is True assert ask(Q.commutative(x), Q.imaginary(x)) is True assert ask(Q.commutative(x), Q.real(x)) is True assert ask(Q.commutative(x), Q.positive(x)) is True assert ask(Q.commutative(x), ~Q.commutative(y)) is True assert ask(Q.commutative(2*x)) is True assert ask(Q.commutative(2*x), ~Q.commutative(x)) is False assert ask(Q.commutative(x + 1)) is True assert ask(Q.commutative(x + 1), ~Q.commutative(x)) is False assert ask(Q.commutative(x**2)) is True assert ask(Q.commutative(x**2), ~Q.commutative(x)) is False assert ask(Q.commutative(log(x))) is True def test_complex(): assert ask(Q.complex(x)) is None assert ask(Q.complex(x), Q.complex(x)) is True assert ask(Q.complex(x), Q.complex(y)) is None assert ask(Q.complex(x), ~Q.complex(x)) is False assert ask(Q.complex(x), Q.real(x)) is True assert ask(Q.complex(x), ~Q.real(x)) is None assert ask(Q.complex(x), Q.rational(x)) is True assert ask(Q.complex(x), Q.irrational(x)) is True assert ask(Q.complex(x), Q.positive(x)) is True assert ask(Q.complex(x), Q.imaginary(x)) is True assert ask(Q.complex(x), Q.algebraic(x)) is True # a+b assert ask(Q.complex(x + 1), Q.complex(x)) is True assert ask(Q.complex(x + 1), Q.real(x)) is True assert ask(Q.complex(x + 1), Q.rational(x)) is True assert ask(Q.complex(x + 1), Q.irrational(x)) is True assert ask(Q.complex(x + 1), Q.imaginary(x)) is True assert ask(Q.complex(x + 1), Q.integer(x)) is True assert ask(Q.complex(x + 1), Q.even(x)) is True assert ask(Q.complex(x + 1), Q.odd(x)) is True assert ask(Q.complex(x + y), Q.complex(x) & Q.complex(y)) is True assert ask(Q.complex(x + y), Q.real(x) & Q.imaginary(y)) is True # a*x +b assert ask(Q.complex(2*x + 1), Q.complex(x)) is True assert ask(Q.complex(2*x + 1), Q.real(x)) is True assert ask(Q.complex(2*x + 1), Q.positive(x)) is True assert ask(Q.complex(2*x + 1), Q.rational(x)) is True assert ask(Q.complex(2*x + 1), Q.irrational(x)) is True assert ask(Q.complex(2*x + 1), Q.imaginary(x)) is True assert ask(Q.complex(2*x + 1), Q.integer(x)) is True assert ask(Q.complex(2*x + 1), Q.even(x)) is True assert ask(Q.complex(2*x + 1), Q.odd(x)) is True # x**2 assert ask(Q.complex(x**2), Q.complex(x)) is True assert ask(Q.complex(x**2), Q.real(x)) is True assert ask(Q.complex(x**2), Q.positive(x)) is True assert ask(Q.complex(x**2), Q.rational(x)) is True assert ask(Q.complex(x**2), Q.irrational(x)) is True assert ask(Q.complex(x**2), Q.imaginary(x)) is True assert ask(Q.complex(x**2), Q.integer(x)) is True assert ask(Q.complex(x**2), Q.even(x)) is True assert ask(Q.complex(x**2), Q.odd(x)) is True # 2**x assert ask(Q.complex(2**x), Q.complex(x)) is True assert ask(Q.complex(2**x), Q.real(x)) is True assert ask(Q.complex(2**x), Q.positive(x)) is True assert ask(Q.complex(2**x), Q.rational(x)) is True assert ask(Q.complex(2**x), Q.irrational(x)) is True assert ask(Q.complex(2**x), Q.imaginary(x)) is True assert ask(Q.complex(2**x), Q.integer(x)) is True assert ask(Q.complex(2**x), Q.even(x)) is True assert ask(Q.complex(2**x), Q.odd(x)) is True assert ask(Q.complex(x**y), Q.complex(x) & Q.complex(y)) is True # trigonometric expressions assert ask(Q.complex(sin(x))) is True assert ask(Q.complex(sin(2*x + 1))) is True assert ask(Q.complex(cos(x))) is True assert ask(Q.complex(cos(2*x + 1))) is True # exponential assert ask(Q.complex(exp(x))) is True assert ask(Q.complex(exp(x))) is True # Q.complexes assert ask(Q.complex(Abs(x))) is True assert ask(Q.complex(re(x))) is True assert ask(Q.complex(im(x))) is True def test_even(): assert ask(Q.even(x)) is None assert ask(Q.even(x), Q.integer(x)) is None assert ask(Q.even(x), ~Q.integer(x)) is False assert ask(Q.even(x), Q.rational(x)) is None assert ask(Q.even(x), Q.positive(x)) is None assert ask(Q.even(2*x)) is None assert ask(Q.even(2*x), Q.integer(x)) is True assert ask(Q.even(2*x), Q.even(x)) is True assert ask(Q.even(2*x), Q.irrational(x)) is False assert ask(Q.even(2*x), Q.odd(x)) is True assert ask(Q.even(2*x), ~Q.integer(x)) is None assert ask(Q.even(3*x), Q.integer(x)) is None assert ask(Q.even(3*x), Q.even(x)) is True assert ask(Q.even(3*x), Q.odd(x)) is False assert ask(Q.even(x + 1), Q.odd(x)) is True assert ask(Q.even(x + 1), Q.even(x)) is False assert ask(Q.even(x + 2), Q.odd(x)) is False assert ask(Q.even(x + 2), Q.even(x)) is True assert ask(Q.even(7 - x), Q.odd(x)) is True assert ask(Q.even(7 + x), Q.odd(x)) is True assert ask(Q.even(x + y), Q.odd(x) & Q.odd(y)) is True assert ask(Q.even(x + y), Q.odd(x) & Q.even(y)) is False assert ask(Q.even(x + y), Q.even(x) & Q.even(y)) is True assert ask(Q.even(2*x + 1), Q.integer(x)) is False assert ask(Q.even(2*x*y), Q.rational(x) & Q.rational(x)) is None assert ask(Q.even(2*x*y), Q.irrational(x) & Q.irrational(x)) is None assert ask(Q.even(x + y + z), Q.odd(x) & Q.odd(y) & Q.even(z)) is True assert ask(Q.even(x + y + z + t), Q.odd(x) & Q.odd(y) & Q.even(z) & Q.integer(t)) is None assert ask(Q.even(Abs(x)), Q.even(x)) is True assert ask(Q.even(Abs(x)), ~Q.even(x)) is None assert ask(Q.even(re(x)), Q.even(x)) is True assert ask(Q.even(re(x)), ~Q.even(x)) is None assert ask(Q.even(im(x)), Q.even(x)) is True assert ask(Q.even(im(x)), Q.real(x)) is True assert ask(Q.even((-1)**n), Q.integer(n)) is False assert ask(Q.even(k**2), Q.even(k)) is True assert ask(Q.even(n**2), Q.odd(n)) is False assert ask(Q.even(2**k), Q.even(k)) is None assert ask(Q.even(x**2)) is None assert ask(Q.even(k**m), Q.even(k) & Q.integer(m) & ~Q.negative(m)) is None assert ask(Q.even(n**m), Q.odd(n) & Q.integer(m) & ~Q.negative(m)) is False assert ask(Q.even(k**p), Q.even(k) & Q.integer(p) & Q.positive(p)) is True assert ask(Q.even(n**p), Q.odd(n) & Q.integer(p) & Q.positive(p)) is False assert ask(Q.even(m**k), Q.even(k) & Q.integer(m) & ~Q.negative(m)) is None assert ask(Q.even(p**k), Q.even(k) & Q.integer(p) & Q.positive(p)) is None assert ask(Q.even(m**n), Q.odd(n) & Q.integer(m) & ~Q.negative(m)) is None assert ask(Q.even(p**n), Q.odd(n) & Q.integer(p) & Q.positive(p)) is None assert ask(Q.even(k**x), Q.even(k)) is None assert ask(Q.even(n**x), Q.odd(n)) is None assert ask(Q.even(x*y), Q.integer(x) & Q.integer(y)) is None assert ask(Q.even(x*x), Q.integer(x)) is None assert ask(Q.even(x*(x + y)), Q.integer(x) & Q.odd(y)) is True assert ask(Q.even(x*(x + y)), Q.integer(x) & Q.even(y)) is None assert ask(Q.even(x*y*(y + z)), Q.integer(x) & Q.integer(y) & Q.odd(z)) is True assert ask(Q.even(x*y*(y + z)), Q.integer(x) & Q.integer(y) & Q.even(z)) is None def test_extended_real(): assert ask(Q.extended_real(x), Q.positive(x)) is True assert ask(Q.extended_real(-x), Q.positive(x)) is True assert ask(Q.extended_real(-x), Q.negative(x)) is True assert ask(Q.extended_real(x + S.Infinity), Q.real(x)) is True def test_rational(): assert ask(Q.rational(x), Q.integer(x)) is True assert ask(Q.rational(x), Q.irrational(x)) is False assert ask(Q.rational(x), Q.real(x)) is None assert ask(Q.rational(x), Q.positive(x)) is None assert ask(Q.rational(x), Q.negative(x)) is None assert ask(Q.rational(x), Q.nonzero(x)) is None assert ask(Q.rational(x), ~Q.algebraic(x)) is False assert ask(Q.rational(2*x), Q.rational(x)) is True assert ask(Q.rational(2*x), Q.integer(x)) is True assert ask(Q.rational(2*x), Q.even(x)) is True assert ask(Q.rational(2*x), Q.odd(x)) is True assert ask(Q.rational(2*x), Q.irrational(x)) is False assert ask(Q.rational(x/2), Q.rational(x)) is True assert ask(Q.rational(x/2), Q.integer(x)) is True assert ask(Q.rational(x/2), Q.even(x)) is True assert ask(Q.rational(x/2), Q.odd(x)) is True assert ask(Q.rational(x/2), Q.irrational(x)) is False assert ask(Q.rational(1/x), Q.rational(x)) is True assert ask(Q.rational(1/x), Q.integer(x)) is True assert ask(Q.rational(1/x), Q.even(x)) is True assert ask(Q.rational(1/x), Q.odd(x)) is True assert ask(Q.rational(1/x), Q.irrational(x)) is False assert ask(Q.rational(2/x), Q.rational(x)) is True assert ask(Q.rational(2/x), Q.integer(x)) is True assert ask(Q.rational(2/x), Q.even(x)) is True assert ask(Q.rational(2/x), Q.odd(x)) is True assert ask(Q.rational(2/x), Q.irrational(x)) is False assert ask(Q.rational(x), ~Q.algebraic(x)) is False # with multiple symbols assert ask(Q.rational(x*y), Q.irrational(x) & Q.irrational(y)) is None assert ask(Q.rational(y/x), Q.rational(x) & Q.rational(y)) is True assert ask(Q.rational(y/x), Q.integer(x) & Q.rational(y)) is True assert ask(Q.rational(y/x), Q.even(x) & Q.rational(y)) is True assert ask(Q.rational(y/x), Q.odd(x) & Q.rational(y)) is True assert ask(Q.rational(y/x), Q.irrational(x) & Q.rational(y)) is False for f in [exp, sin, tan, asin, atan, cos]: assert ask(Q.rational(f(7))) is False assert ask(Q.rational(f(7, evaluate=False))) is False assert ask(Q.rational(f(0, evaluate=False))) is True assert ask(Q.rational(f(x)), Q.rational(x)) is None assert ask(Q.rational(f(x)), Q.rational(x) & Q.nonzero(x)) is False for g in [log, acos]: assert ask(Q.rational(g(7))) is False assert ask(Q.rational(g(7, evaluate=False))) is False assert ask(Q.rational(g(1, evaluate=False))) is True assert ask(Q.rational(g(x)), Q.rational(x)) is None assert ask(Q.rational(g(x)), Q.rational(x) & Q.nonzero(x - 1)) is False for h in [cot, acot]: assert ask(Q.rational(h(7))) is False assert ask(Q.rational(h(7, evaluate=False))) is False assert ask(Q.rational(h(x)), Q.rational(x)) is False def test_hermitian(): assert ask(Q.hermitian(x)) is None assert ask(Q.hermitian(x), Q.antihermitian(x)) is False assert ask(Q.hermitian(x), Q.imaginary(x)) is False assert ask(Q.hermitian(x), Q.prime(x)) is True assert ask(Q.hermitian(x), Q.real(x)) is True assert ask(Q.hermitian(x + 1), Q.antihermitian(x)) is False assert ask(Q.hermitian(x + 1), Q.complex(x)) is None assert ask(Q.hermitian(x + 1), Q.hermitian(x)) is True assert ask(Q.hermitian(x + 1), Q.imaginary(x)) is False assert ask(Q.hermitian(x + 1), Q.real(x)) is True assert ask(Q.hermitian(x + I), Q.antihermitian(x)) is None assert ask(Q.hermitian(x + I), Q.complex(x)) is None assert ask(Q.hermitian(x + I), Q.hermitian(x)) is False assert ask(Q.hermitian(x + I), Q.imaginary(x)) is None assert ask(Q.hermitian(x + I), Q.real(x)) is False assert ask( Q.hermitian(x + y), Q.antihermitian(x) & Q.antihermitian(y)) is None assert ask(Q.hermitian(x + y), Q.antihermitian(x) & Q.complex(y)) is None assert ask( Q.hermitian(x + y), Q.antihermitian(x) & Q.hermitian(y)) is False assert ask(Q.hermitian(x + y), Q.antihermitian(x) & Q.imaginary(y)) is None assert ask(Q.hermitian(x + y), Q.antihermitian(x) & Q.real(y)) is False assert ask(Q.hermitian(x + y), Q.hermitian(x) & Q.complex(y)) is None assert ask(Q.hermitian(x + y), Q.hermitian(x) & Q.hermitian(y)) is True assert ask(Q.hermitian(x + y), Q.hermitian(x) & Q.imaginary(y)) is False assert ask(Q.hermitian(x + y), Q.hermitian(x) & Q.real(y)) is True assert ask(Q.hermitian(x + y), Q.imaginary(x) & Q.complex(y)) is None assert ask(Q.hermitian(x + y), Q.imaginary(x) & Q.imaginary(y)) is None assert ask(Q.hermitian(x + y), Q.imaginary(x) & Q.real(y)) is False assert ask(Q.hermitian(x + y), Q.real(x) & Q.complex(y)) is None assert ask(Q.hermitian(x + y), Q.real(x) & Q.real(y)) is True assert ask(Q.hermitian(I*x), Q.antihermitian(x)) is True assert ask(Q.hermitian(I*x), Q.complex(x)) is None assert ask(Q.hermitian(I*x), Q.hermitian(x)) is False assert ask(Q.hermitian(I*x), Q.imaginary(x)) is True assert ask(Q.hermitian(I*x), Q.real(x)) is False assert ask(Q.hermitian(x*y), Q.hermitian(x) & Q.real(y)) is True assert ask( Q.hermitian(x + y + z), Q.real(x) & Q.real(y) & Q.real(z)) is True assert ask(Q.hermitian(x + y + z), Q.real(x) & Q.real(y) & Q.imaginary(z)) is False assert ask(Q.hermitian(x + y + z), Q.real(x) & Q.imaginary(y) & Q.imaginary(z)) is None assert ask(Q.hermitian(x + y + z), Q.imaginary(x) & Q.imaginary(y) & Q.imaginary(z)) is None assert ask(Q.antihermitian(x)) is None assert ask(Q.antihermitian(x), Q.real(x)) is False assert ask(Q.antihermitian(x), Q.prime(x)) is False assert ask(Q.antihermitian(x + 1), Q.antihermitian(x)) is False assert ask(Q.antihermitian(x + 1), Q.complex(x)) is None assert ask(Q.antihermitian(x + 1), Q.hermitian(x)) is None assert ask(Q.antihermitian(x + 1), Q.imaginary(x)) is False assert ask(Q.antihermitian(x + 1), Q.real(x)) is None assert ask(Q.antihermitian(x + I), Q.antihermitian(x)) is True assert ask(Q.antihermitian(x + I), Q.complex(x)) is None assert ask(Q.antihermitian(x + I), Q.hermitian(x)) is False assert ask(Q.antihermitian(x + I), Q.imaginary(x)) is True assert ask(Q.antihermitian(x + I), Q.real(x)) is False assert ask( Q.antihermitian(x + y), Q.antihermitian(x) & Q.antihermitian(y) ) is True assert ask( Q.antihermitian(x + y), Q.antihermitian(x) & Q.complex(y)) is None assert ask( Q.antihermitian(x + y), Q.antihermitian(x) & Q.hermitian(y)) is False assert ask( Q.antihermitian(x + y), Q.antihermitian(x) & Q.imaginary(y)) is True assert ask(Q.antihermitian(x + y), Q.antihermitian(x) & Q.real(y) ) is False assert ask(Q.antihermitian(x + y), Q.hermitian(x) & Q.complex(y)) is None assert ask(Q.antihermitian(x + y), Q.hermitian(x) & Q.hermitian(y) ) is None assert ask( Q.antihermitian(x + y), Q.hermitian(x) & Q.imaginary(y)) is False assert ask(Q.antihermitian(x + y), Q.hermitian(x) & Q.real(y)) is None assert ask(Q.antihermitian(x + y), Q.imaginary(x) & Q.complex(y)) is None assert ask(Q.antihermitian(x + y), Q.imaginary(x) & Q.imaginary(y)) is True assert ask(Q.antihermitian(x + y), Q.imaginary(x) & Q.real(y)) is False assert ask(Q.antihermitian(x + y), Q.real(x) & Q.complex(y)) is None assert ask(Q.antihermitian(x + y), Q.real(x) & Q.real(y)) is None assert ask(Q.antihermitian(I*x), Q.real(x)) is True assert ask(Q.antihermitian(I*x), Q.antihermitian(x)) is False assert ask(Q.antihermitian(I*x), Q.complex(x)) is None assert ask(Q.antihermitian(x*y), Q.antihermitian(x) & Q.real(y)) is True assert ask(Q.antihermitian(x + y + z), Q.real(x) & Q.real(y) & Q.real(z)) is None assert ask(Q.antihermitian(x + y + z), Q.real(x) & Q.real(y) & Q.imaginary(z)) is None assert ask(Q.antihermitian(x + y + z), Q.real(x) & Q.imaginary(y) & Q.imaginary(z)) is False assert ask(Q.antihermitian(x + y + z), Q.imaginary(x) & Q.imaginary(y) & Q.imaginary(z)) is True def test_imaginary(): assert ask(Q.imaginary(x)) is None assert ask(Q.imaginary(x), Q.real(x)) is False assert ask(Q.imaginary(x), Q.prime(x)) is False assert ask(Q.imaginary(x + 1), Q.real(x)) is False assert ask(Q.imaginary(x + 1), Q.imaginary(x)) is False assert ask(Q.imaginary(x + I), Q.real(x)) is False assert ask(Q.imaginary(x + I), Q.imaginary(x)) is True assert ask(Q.imaginary(x + y), Q.imaginary(x) & Q.imaginary(y)) is True assert ask(Q.imaginary(x + y), Q.real(x) & Q.real(y)) is False assert ask(Q.imaginary(x + y), Q.imaginary(x) & Q.real(y)) is False assert ask(Q.imaginary(x + y), Q.complex(x) & Q.real(y)) is None assert ask(Q.imaginary(I*x), Q.real(x)) is True assert ask(Q.imaginary(I*x), Q.imaginary(x)) is False assert ask(Q.imaginary(I*x), Q.complex(x)) is None assert ask(Q.imaginary(I**x), Q.negative(x)) is None assert ask(Q.imaginary(I**x), Q.positive(x)) is None assert ask(Q.imaginary(I**x), Q.even(x)) is False assert ask(Q.imaginary(I**y), Q.odd(y)) is True assert ask(Q.imaginary(x*y), Q.imaginary(x) & Q.real(y)) is True assert ask( Q.imaginary(x + y + z), Q.real(x) & Q.real(y) & Q.real(z)) is False assert ask(Q.imaginary(x + y + z), Q.real(x) & Q.real(y) & Q.imaginary(z)) is None assert ask(Q.imaginary(x + y + z), Q.real(x) & Q.imaginary(y) & Q.imaginary(z)) is False assert ask(Q.imaginary(x**0), Q.imaginary(x)) is False assert ask(Q.imaginary(x**y), Q.imaginary(x) & Q.imaginary(y)) is None assert ask(Q.imaginary(x**y), Q.imaginary(x) & Q.real(y)) is None assert ask(Q.imaginary(x**y), Q.real(x) & Q.imaginary(y)) is None assert ask(Q.imaginary(x**y), Q.real(x) & Q.real(y)) is None assert ask(Q.imaginary(x**y), Q.imaginary(x) & Q.integer(y)) is None assert ask(Q.imaginary(x**y), Q.imaginary(y) & Q.integer(x)) is None assert ask(Q.imaginary(x**y), Q.imaginary(x) & Q.odd(y)) is True assert ask(Q.imaginary(x**y), Q.imaginary(x) & Q.rational(y)) is None assert ask(Q.imaginary(x**y), Q.imaginary(x) & Q.even(y)) is False assert ask(Q.imaginary(x**y), Q.real(x) & Q.integer(y)) is False assert ask(Q.imaginary(x**y), Q.positive(x) & Q.real(y)) is False assert ask(Q.imaginary(x**y), Q.negative(x) & Q.real(y)) is True assert ask(Q.imaginary(x**y), Q.integer(x) & Q.imaginary(y)) is None assert ask(Q.imaginary(x**(y/z)), Q.real(x) & Q.real(y/z) & Q.rational(y/z) & Q.even(z) & Q.negative(x)) is True assert ask(Q.imaginary(x**(y/z)), Q.real(x) & Q.rational(y/z) & Q.even(z) & Q.negative(x)) is True assert ask(Q.imaginary(x**(y/z)), Q.real(x) & Q.integer(y/z)) is False assert ask(Q.imaginary(x**(y/z)), Q.real(x) & Q.real(y/z) & Q.positive(x)) is False assert ask(Q.imaginary(x**(y/z)), Q.real(x) & Q.real(y/z) & Q.negative(x)) is True def test_infinitesimal(): assert ask(Q.infinitesimal(x)) is None assert ask(Q.infinitesimal(x), Q.infinitesimal(x)) is True assert ask(Q.infinitesimal(2*x), Q.infinitesimal(x)) is True assert ask(Q.infinitesimal(x*y), Q.infinitesimal(x)) is None assert ask( Q.infinitesimal(x*y), Q.infinitesimal(x) & Q.infinitesimal(y)) is True assert ask(Q.infinitesimal(x*y), Q.infinitesimal(x) & Q.bounded(y)) is True assert ask(Q.infinitesimal(x**2), Q.infinitesimal(x)) is True def test_integer(): assert ask(Q.integer(x)) is None assert ask(Q.integer(x), Q.integer(x)) is True assert ask(Q.integer(x), ~Q.integer(x)) is False assert ask(Q.integer(x), ~Q.real(x)) is False assert ask(Q.integer(x), ~Q.positive(x)) is None assert ask(Q.integer(x), Q.even(x) | Q.odd(x)) is True assert ask(Q.integer(2*x), Q.integer(x)) is True assert ask(Q.integer(2*x), Q.even(x)) is True assert ask(Q.integer(2*x), Q.prime(x)) is True assert ask(Q.integer(2*x), Q.rational(x)) is None assert ask(Q.integer(2*x), Q.real(x)) is None assert ask(Q.integer(sqrt(2)*x), Q.integer(x)) is False assert ask(Q.integer(x/2), Q.odd(x)) is False assert ask(Q.integer(x/2), Q.even(x)) is True assert ask(Q.integer(x/3), Q.odd(x)) is None assert ask(Q.integer(x/3), Q.even(x)) is None def test_negative(): assert ask(Q.negative(x), Q.negative(x)) is True assert ask(Q.negative(x), Q.positive(x)) is False assert ask(Q.negative(x), ~Q.real(x)) is False assert ask(Q.negative(x), Q.prime(x)) is False assert ask(Q.negative(x), ~Q.prime(x)) is None assert ask(Q.negative(-x), Q.positive(x)) is True assert ask(Q.negative(-x), ~Q.positive(x)) is None assert ask(Q.negative(-x), Q.negative(x)) is False assert ask(Q.negative(-x), Q.positive(x)) is True assert ask(Q.negative(x - 1), Q.negative(x)) is True assert ask(Q.negative(x + y)) is None assert ask(Q.negative(x + y), Q.negative(x)) is None assert ask(Q.negative(x + y), Q.negative(x) & Q.negative(y)) is True assert ask(Q.negative(x + y), Q.negative(x) & ~Q.positive(y)) is True assert ask(Q.negative(x**2)) is None assert ask(Q.negative(x**2), Q.real(x)) is False assert ask(Q.negative(x**1.4), Q.real(x)) is None assert ask(Q.negative(x**I), Q.positive(x)) is None assert ask(Q.negative(x*y)) is None assert ask(Q.negative(x*y), Q.positive(x) & Q.positive(y)) is False assert ask(Q.negative(x*y), Q.positive(x) & Q.negative(y)) is True assert ask(Q.negative(x*y), Q.complex(x) & Q.complex(y)) is None assert ask(Q.negative(x**y)) is None assert ask(Q.negative(x**y), Q.negative(x) & Q.even(y)) is False assert ask(Q.negative(x**y), Q.negative(x) & Q.odd(y)) is True assert ask(Q.negative(x**y), Q.positive(x) & Q.integer(y)) is False assert ask(Q.negative(Abs(x))) is False def test_nonzero(): assert ask(Q.nonzero(x)) is None assert ask(Q.nonzero(x), Q.real(x)) is None assert ask(Q.nonzero(x), Q.positive(x)) is True assert ask(Q.nonzero(x), Q.negative(x)) is True assert ask(Q.nonzero(x), Q.negative(x) | Q.positive(x)) is True assert ask(Q.nonzero(x + y)) is None assert ask(Q.nonzero(x + y), Q.positive(x) & Q.positive(y)) is True assert ask(Q.nonzero(x + y), Q.positive(x) & Q.negative(y)) is None assert ask(Q.nonzero(x + y), Q.negative(x) & Q.negative(y)) is True assert ask(Q.nonzero(2*x)) is None assert ask(Q.nonzero(2*x), Q.positive(x)) is True assert ask(Q.nonzero(2*x), Q.negative(x)) is True assert ask(Q.nonzero(x*y), Q.nonzero(x)) is None assert ask(Q.nonzero(x*y), Q.nonzero(x) & Q.nonzero(y)) is True assert ask(Q.nonzero(Abs(x))) is None assert ask(Q.nonzero(Abs(x)), Q.nonzero(x)) is True def test_zero(): assert ask(Q.zero(x)) is None assert ask(Q.zero(x), Q.real(x)) is None assert ask(Q.zero(x), Q.positive(x)) is False assert ask(Q.zero(x), Q.negative(x)) is False assert ask(Q.zero(x), Q.negative(x) | Q.positive(x)) is False assert ask(Q.zero(x), Q.nonnegative(x) & Q.nonpositive(x)) is True assert ask(Q.zero(x + y)) is None assert ask(Q.zero(x + y), Q.positive(x) & Q.positive(y)) is False assert ask(Q.zero(x + y), Q.positive(x) & Q.negative(y)) is None assert ask(Q.zero(x + y), Q.negative(x) & Q.negative(y)) is False assert ask(Q.zero(2*x)) is None assert ask(Q.zero(2*x), Q.positive(x)) is False assert ask(Q.zero(2*x), Q.negative(x)) is False assert ask(Q.zero(x*y), Q.nonzero(x)) is None assert ask(Q.zero(Abs(x))) is None assert ask(Q.zero(Abs(x)), Q.zero(x)) is True assert ask(Q.integer(x), Q.zero(x)) is True assert ask(Q.even(x), Q.zero(x)) is True assert ask(Q.odd(x), Q.zero(x)) is False assert ask(Q.zero(x), Q.even(x)) is None assert ask(Q.zero(x), Q.odd(x)) is False @XFAIL def test_zero_doesnt_work(): # This requires moving logic from the handler to the deduction system assert ask(Q.zero(x*y), Q.zero(x) | Q.zero(y)) is True def test_odd(): assert ask(Q.odd(x)) is None assert ask(Q.odd(x), Q.odd(x)) is True assert ask(Q.odd(x), Q.integer(x)) is None assert ask(Q.odd(x), ~Q.integer(x)) is False assert ask(Q.odd(x), Q.rational(x)) is None assert ask(Q.odd(x), Q.positive(x)) is None assert ask(Q.odd(-x), Q.odd(x)) is True assert ask(Q.odd(2*x)) is None assert ask(Q.odd(2*x), Q.integer(x)) is False assert ask(Q.odd(2*x), Q.odd(x)) is False assert ask(Q.odd(2*x), Q.irrational(x)) is False assert ask(Q.odd(2*x), ~Q.integer(x)) is None assert ask(Q.odd(3*x), Q.integer(x)) is None assert ask(Q.odd(x/3), Q.odd(x)) is None assert ask(Q.odd(x/3), Q.even(x)) is None assert ask(Q.odd(x + 1), Q.even(x)) is True assert ask(Q.odd(x + 2), Q.even(x)) is False assert ask(Q.odd(x + 2), Q.odd(x)) is True assert ask(Q.odd(3 - x), Q.odd(x)) is False assert ask(Q.odd(3 - x), Q.even(x)) is True assert ask(Q.odd(3 + x), Q.odd(x)) is False assert ask(Q.odd(3 + x), Q.even(x)) is True assert ask(Q.odd(x + y), Q.odd(x) & Q.odd(y)) is False assert ask(Q.odd(x + y), Q.odd(x) & Q.even(y)) is True assert ask(Q.odd(x - y), Q.even(x) & Q.odd(y)) is True assert ask(Q.odd(x - y), Q.odd(x) & Q.odd(y)) is False assert ask(Q.odd(x + y + z), Q.odd(x) & Q.odd(y) & Q.even(z)) is False assert ask(Q.odd(x + y + z + t), Q.odd(x) & Q.odd(y) & Q.even(z) & Q.integer(t)) is None assert ask(Q.odd(2*x + 1), Q.integer(x)) is True assert ask(Q.odd(2*x + y), Q.integer(x) & Q.odd(y)) is True assert ask(Q.odd(2*x + y), Q.integer(x) & Q.even(y)) is False assert ask(Q.odd(2*x + y), Q.integer(x) & Q.integer(y)) is None assert ask(Q.odd(x*y), Q.odd(x) & Q.even(y)) is False assert ask(Q.odd(x*y), Q.odd(x) & Q.odd(y)) is True assert ask(Q.odd(2*x*y), Q.rational(x) & Q.rational(x)) is None assert ask(Q.odd(2*x*y), Q.irrational(x) & Q.irrational(x)) is None assert ask(Q.odd(Abs(x)), Q.odd(x)) is True assert ask(Q.odd((-1)**n), Q.integer(n)) is True assert ask(Q.odd(k**2), Q.even(k)) is False assert ask(Q.odd(n**2), Q.odd(n)) is True assert ask(Q.odd(3**k), Q.even(k)) is None assert ask(Q.odd(k**m), Q.even(k) & Q.integer(m) & ~Q.negative(m)) is None assert ask(Q.odd(n**m), Q.odd(n) & Q.integer(m) & ~Q.negative(m)) is True assert ask(Q.odd(k**p), Q.even(k) & Q.integer(p) & Q.positive(p)) is False assert ask(Q.odd(n**p), Q.odd(n) & Q.integer(p) & Q.positive(p)) is True assert ask(Q.odd(m**k), Q.even(k) & Q.integer(m) & ~Q.negative(m)) is None assert ask(Q.odd(p**k), Q.even(k) & Q.integer(p) & Q.positive(p)) is None assert ask(Q.odd(m**n), Q.odd(n) & Q.integer(m) & ~Q.negative(m)) is None assert ask(Q.odd(p**n), Q.odd(n) & Q.integer(p) & Q.positive(p)) is None assert ask(Q.odd(k**x), Q.even(k)) is None assert ask(Q.odd(n**x), Q.odd(n)) is None assert ask(Q.odd(x*y), Q.integer(x) & Q.integer(y)) is None assert ask(Q.odd(x*x), Q.integer(x)) is None assert ask(Q.odd(x*(x + y)), Q.integer(x) & Q.odd(y)) is False assert ask(Q.odd(x*(x + y)), Q.integer(x) & Q.even(y)) is None assert ask(Q.odd(x*y*(y + z)), Q.integer(x) & Q.integer(y) & Q.odd(z)) is False assert ask(Q.odd(x*y*(y + z)), Q.integer(x) & Q.integer(y) & Q.even(z)) is None def test_prime(): assert ask(Q.prime(x), Q.prime(x)) is True assert ask(Q.prime(x), ~Q.prime(x)) is False assert ask(Q.prime(x), Q.integer(x)) is None assert ask(Q.prime(x), ~Q.integer(x)) is False assert ask(Q.prime(2*x), Q.integer(x)) is False assert ask(Q.prime(x*y)) is None assert ask(Q.prime(x*y), Q.prime(x)) is None assert ask(Q.prime(x*y), Q.integer(x) & Q.integer(y)) is False assert ask(Q.prime(x**2), Q.integer(x)) is False assert ask(Q.prime(x**2), Q.prime(x)) is False assert ask(Q.prime(x**y), Q.integer(x) & Q.integer(y)) is False def test_positive(): assert ask(Q.positive(x), Q.positive(x)) is True assert ask(Q.positive(x), Q.negative(x)) is False assert ask(Q.positive(x), Q.nonzero(x)) is None assert ask(Q.positive(-x), Q.positive(x)) is False assert ask(Q.positive(-x), Q.negative(x)) is True assert ask(Q.positive(x + y), Q.positive(x) & Q.positive(y)) is True assert ask(Q.positive(x + y), Q.positive(x) & ~Q.negative(y)) is True assert ask(Q.positive(x + y), Q.positive(x) & Q.negative(y)) is None assert ask(Q.positive(2*x), Q.positive(x)) is True assumptions = Q.positive(x) & Q.negative(y) & Q.negative(z) & Q.positive(w) assert ask(Q.positive(x*y*z)) is None assert ask(Q.positive(x*y*z), assumptions) is True assert ask(Q.positive(-x*y*z), assumptions) is False assert ask(Q.positive(x**I), Q.positive(x)) is None assert ask(Q.positive(x**2), Q.positive(x)) is True assert ask(Q.positive(x**2), Q.negative(x)) is True assert ask(Q.positive(1/(1 + x**2)), Q.real(x)) is True #exponential assert ask(Q.positive(exp(x)), Q.real(x)) is True assert ask(~Q.negative(exp(x)), Q.real(x)) is True assert ask(Q.positive(x + exp(x)), Q.real(x)) is None # factorial assert ask(Q.positive(factorial(x)), Q.integer(x) & Q.positive(x)) assert ask(Q.positive(factorial(x)), Q.integer(x)) is None #absolute value assert ask(Q.positive(Abs(x))) is None # Abs(0) = 0 assert ask(Q.positive(Abs(x)), Q.positive(x)) is True def test_nonpositive(): assert ask(Q.nonpositive(-1)) assert ask(Q.nonpositive(0)) assert ask(Q.nonpositive(1)) is False assert ask(~Q.positive(x), Q.nonpositive(x)) assert ask(Q.nonpositive(x), Q.positive(x)) is False assert ask(Q.nonpositive(sqrt(-1))) is False assert ask(Q.nonpositive(x), Q.imaginary(x)) is False def test_nonnegative(): assert ask(Q.nonnegative(-1)) is False assert ask(Q.nonnegative(0)) assert ask(Q.nonnegative(1)) assert ask(~Q.negative(x), Q.nonnegative(x)) assert ask(Q.nonnegative(x), Q.negative(x)) is False assert ask(Q.nonnegative(sqrt(-1))) is False assert ask(Q.nonnegative(x), Q.imaginary(x)) is False def test_real(): assert ask(Q.real(x)) is None assert ask(Q.real(x), Q.real(x)) is True assert ask(Q.real(x), Q.nonzero(x)) is True assert ask(Q.real(x), Q.positive(x)) is True assert ask(Q.real(x), Q.negative(x)) is True assert ask(Q.real(x), Q.integer(x)) is True assert ask(Q.real(x), Q.even(x)) is True assert ask(Q.real(x), Q.prime(x)) is True assert ask(Q.real(x/sqrt(2)), Q.real(x)) is True assert ask(Q.real(x/sqrt(-2)), Q.real(x)) is False assert ask(Q.real(x + 1), Q.real(x)) is True assert ask(Q.real(x + I), Q.real(x)) is False assert ask(Q.real(x + I), Q.complex(x)) is None assert ask(Q.real(2*x), Q.real(x)) is True assert ask(Q.real(I*x), Q.real(x)) is False assert ask(Q.real(I*x), Q.imaginary(x)) is True assert ask(Q.real(I*x), Q.complex(x)) is None assert ask(Q.real(x**2), Q.real(x)) is True assert ask(Q.real(sqrt(x)), Q.negative(x)) is False assert ask(Q.real(x**y), Q.real(x) & Q.integer(y)) is True assert ask(Q.real(x**y), Q.real(x) & Q.real(y)) is None assert ask(Q.real(x**y), Q.positive(x) & Q.real(y)) is True assert ask(Q.real(x**y), Q.imaginary(x) & Q.imaginary(y)) is None assert ask(Q.real(x**y), Q.imaginary(x) & Q.real(y)) is None assert ask(Q.real(x**y), Q.real(x) & Q.imaginary(y)) is None assert ask(Q.real(x**0), Q.imaginary(x)) is True assert ask(Q.real(x**y), Q.real(x) & Q.integer(y)) is True assert ask(Q.real(x**y), Q.positive(x) & Q.real(y)) is True assert ask(Q.real(x**y), Q.real(x) & Q.rational(y)) is None assert ask(Q.real(x**y), Q.imaginary(x) & Q.integer(y)) is None assert ask(Q.real(x**y), Q.imaginary(x) & Q.odd(y)) is False assert ask(Q.real(x**y), Q.imaginary(x) & Q.even(y)) is True assert ask(Q.real(x**(y/z)), Q.real(x) & Q.real(y/z) & Q.rational(y/z) & Q.even(z) & Q.positive(x)) is True assert ask(Q.real(x**(y/z)), Q.real(x) & Q.rational(y/z) & Q.even(z) & Q.negative(x)) is False assert ask(Q.real(x**(y/z)), Q.real(x) & Q.integer(y/z)) is True assert ask(Q.real(x**(y/z)), Q.real(x) & Q.real(y/z) & Q.positive(x)) is True assert ask(Q.real(x**(y/z)), Q.real(x) & Q.real(y/z) & Q.negative(x)) is False # trigonometric functions assert ask(Q.real(sin(x))) is None assert ask(Q.real(cos(x))) is None assert ask(Q.real(sin(x)), Q.real(x)) is True assert ask(Q.real(cos(x)), Q.real(x)) is True # exponential function assert ask(Q.real(exp(x))) is None assert ask(Q.real(exp(x)), Q.real(x)) is True assert ask(Q.real(x + exp(x)), Q.real(x)) is True # Q.complexes assert ask(Q.real(re(x))) is True assert ask(Q.real(im(x))) is True def test_algebraic(): assert ask(Q.algebraic(x)) is None assert ask(Q.algebraic(I)) is True assert ask(Q.algebraic(2*I)) is True assert ask(Q.algebraic(I/3)) is True assert ask(Q.algebraic(sqrt(7))) is True assert ask(Q.algebraic(2*sqrt(7))) is True assert ask(Q.algebraic(sqrt(7)/3)) is True assert ask(Q.algebraic(I*sqrt(3))) is True assert ask(Q.algebraic(sqrt(1 + I*sqrt(3)))) is True assert ask(Q.algebraic((1 + I*sqrt(3)**(S(17)/31)))) is True assert ask(Q.algebraic((1 + I*sqrt(3)**(S(17)/pi)))) is False for f in [exp, sin, tan, asin, atan, cos]: assert ask(Q.algebraic(f(7))) is False assert ask(Q.algebraic(f(7, evaluate=False))) is False assert ask(Q.algebraic(f(0, evaluate=False))) is True assert ask(Q.algebraic(f(x)), Q.algebraic(x)) is None assert ask(Q.algebraic(f(x)), Q.algebraic(x) & Q.nonzero(x)) is False for g in [log, acos]: assert ask(Q.algebraic(g(7))) is False assert ask(Q.algebraic(g(7, evaluate=False))) is False assert ask(Q.algebraic(g(1, evaluate=False))) is True assert ask(Q.algebraic(g(x)), Q.algebraic(x)) is None assert ask(Q.algebraic(g(x)), Q.algebraic(x) & Q.nonzero(x - 1)) is False for h in [cot, acot]: assert ask(Q.algebraic(h(7))) is False assert ask(Q.algebraic(h(7, evaluate=False))) is False assert ask(Q.algebraic(h(x)), Q.algebraic(x)) is False assert ask(Q.algebraic(sqrt(sin(7)))) is False assert ask(Q.algebraic(sqrt(y + I*sqrt(7)))) is None assert ask(Q.algebraic(2.47)) is True def test_global(): """Test ask with global assumptions""" assert ask(Q.integer(x)) is None global_assumptions.add(Q.integer(x)) assert ask(Q.integer(x)) is True global_assumptions.clear() assert ask(Q.integer(x)) is None def test_custom_context(): """Test ask with custom assumptions context""" assert ask(Q.integer(x)) is None local_context = AssumptionsContext() local_context.add(Q.integer(x)) assert ask(Q.integer(x), context=local_context) is True assert ask(Q.integer(x)) is None def test_functions_in_assumptions(): assert ask(Q.negative(x), Q.real(x) >> Q.positive(x)) is False assert ask(Q.negative(x), Equivalent(Q.real(x), Q.positive(x))) is False assert ask(Q.negative(x), Xor(Q.real(x), Q.negative(x))) is False def test_composite_ask(): assert ask(Q.negative(x) & Q.integer(x), assumptions=Q.real(x) >> Q.positive(x)) is False def test_composite_proposition(): assert ask(True) is True assert ask(False) is False assert ask(~Q.negative(x), Q.positive(x)) is True assert ask(~Q.real(x), Q.commutative(x)) is None assert ask(Q.negative(x) & Q.integer(x), Q.positive(x)) is False assert ask(Q.negative(x) & Q.integer(x)) is None assert ask(Q.real(x) | Q.integer(x), Q.positive(x)) is True assert ask(Q.real(x) | Q.integer(x)) is None assert ask(Q.real(x) >> Q.positive(x), Q.negative(x)) is False assert ask(Implies( Q.real(x), Q.positive(x), evaluate=False), Q.negative(x)) is False assert ask(Implies(Q.real(x), Q.positive(x), evaluate=False)) is None assert ask(Equivalent(Q.integer(x), Q.even(x)), Q.even(x)) is True assert ask(Equivalent(Q.integer(x), Q.even(x))) is None assert ask(Equivalent(Q.positive(x), Q.integer(x)), Q.integer(x)) is None assert ask(Q.real(x) | Q.integer(x), Q.real(x) | Q.integer(x)) is True def test_composite_assumptions(): assert ask(Q.real(x), Q.real(x) & Q.real(y)) is True assert ask(Q.positive(x), Q.positive(x) | Q.positive(y)) is None assert ask(Q.positive(x), Q.real(x) >> Q.positive(y)) is None assert ask(Q.real(x), ~(Q.real(x) >> Q.real(y))) is True def test_incompatible_resolutors(): class Prime2AskHandler(AskHandler): @staticmethod def Number(expr, assumptions): return True register_handler('prime', Prime2AskHandler) raises(ValueError, lambda: ask(Q.prime(4))) remove_handler('prime', Prime2AskHandler) class InconclusiveHandler(AskHandler): @staticmethod def Number(expr, assumptions): return None register_handler('prime', InconclusiveHandler) assert ask(Q.prime(3)) is True def test_key_extensibility(): """test that you can add keys to the ask system at runtime""" # make sure the key is not defined raises(AttributeError, lambda: ask(Q.my_key(x))) class MyAskHandler(AskHandler): @staticmethod def Symbol(expr, assumptions): return True register_handler('my_key', MyAskHandler) assert ask(Q.my_key(x)) is True assert ask(Q.my_key(x + 1)) is None remove_handler('my_key', MyAskHandler) del Q.my_key raises(AttributeError, lambda: ask(Q.my_key(x))) def test_type_extensibility(): """test that new types can be added to the ask system at runtime We create a custom type MyType, and override ask Q.prime=True with handler MyAskHandler for this type TODO: test incompatible resolutors """ from sympy.core import Basic class MyType(Basic): pass class MyAskHandler(AskHandler): @staticmethod def MyType(expr, assumptions): return True a = MyType() register_handler(Q.prime, MyAskHandler) assert ask(Q.prime(a)) is True def test_single_fact_lookup(): known_facts = And(Implies(Q.integer, Q.rational), Implies(Q.rational, Q.real), Implies(Q.real, Q.complex)) known_facts_keys = set([Q.integer, Q.rational, Q.real, Q.complex]) known_facts_cnf = to_cnf(known_facts) mapping = single_fact_lookup(known_facts_keys, known_facts_cnf) assert mapping[Q.rational] == set([Q.real, Q.rational, Q.complex]) def test_compute_known_facts(): known_facts = And(Implies(Q.integer, Q.rational), Implies(Q.rational, Q.real), Implies(Q.real, Q.complex)) known_facts_keys = set([Q.integer, Q.rational, Q.real, Q.complex]) s = compute_known_facts(known_facts, known_facts_keys) def test_known_facts_consistent(): from sympy.assumptions.ask import known_facts, known_facts_keys ns = {} exec_('from sympy.logic.boolalg import And, Or, Not', globals(), ns) exec_(compute_known_facts(known_facts, known_facts_keys), globals(), ns) assert ns['known_facts_cnf'] == known_facts_cnf assert ns['known_facts_dict'] == known_facts_dict def test_Add_queries(): assert ask(Q.prime(12345678901234567890 + (cos(1)**2 + sin(1)**2))) is True assert ask(Q.even(Add(S(2), S(2), evaluate=0))) is True assert ask(Q.prime(Add(S(2), S(2), evaluate=0))) is False assert ask(Q.integer(Add(S(2), S(2), evaluate=0))) is True def test_positive(): with assuming(Q.positive(x + 1)): assert not ask(Q.positive(x)) def test_issue_2322(): raises(TypeError, lambda: ask(pi/log(x), Q.real)) def test_issue_3906(): raises(TypeError, lambda: ask(Q.positive)) def test_issue_2734(): assert ask(Q.positive(log(x)**2), Q.positive(x)) is None assert ask(~Q.negative(log(x)**2), Q.positive(x)) is True def test_issue_3633(): raises(ValueError, lambda: ask(Q.positive(x), Q.positive(x) & Q.negative(x))) raises(ValueError, lambda: ask(Q.negative(x), Q.positive(x) & Q.negative(x))) sympy-0.7.4.1/sympy/assumptions/tests/test_matrices.py0000644000175000017500000002021512253362407023374 0ustar georgeskgeorgeskfrom sympy import Q, ask, Symbol from sympy.matrices.expressions import (MatrixSymbol, Identity, ZeroMatrix, Trace, MatrixSlice, Determinant) from sympy.matrices.expressions.factorizations import LofLU from sympy.utilities.pytest import XFAIL from sympy.assumptions import assuming X = MatrixSymbol('X', 2, 2) Y = MatrixSymbol('Y', 2, 3) Z = MatrixSymbol('Z', 2, 2) def test_square(): assert ask(Q.square(X)) assert not ask(Q.square(Y)) assert ask(Q.square(Y*Y.T)) def test_invertible(): assert ask(Q.invertible(X), Q.invertible(X)) assert ask(Q.invertible(Y)) is False assert ask(Q.invertible(X*Y), Q.invertible(X)) is False assert ask(Q.invertible(X*Z), Q.invertible(X)) is None assert ask(Q.invertible(X*Z), Q.invertible(X) & Q.invertible(Z)) is True assert ask(Q.invertible(X.T)) is None assert ask(Q.invertible(X.T), Q.invertible(X)) is True assert ask(Q.invertible(X.I)) is True assert ask(Q.invertible(Identity(3))) is True assert ask(Q.invertible(ZeroMatrix(3, 3))) is False assert ask(Q.invertible(X), Q.fullrank(X) & Q.square(X)) def test_singular(): assert ask(Q.singular(X)) is None assert ask(Q.singular(X), Q.invertible(X)) is False assert ask(Q.singular(X), ~Q.invertible(X)) is True @XFAIL def test_invertible_fullrank(): assert ask(Q.invertible(X), Q.fullrank(X)) def test_symmetric(): assert ask(Q.symmetric(X), Q.symmetric(X)) assert ask(Q.symmetric(X*Z), Q.symmetric(X)) is None assert ask(Q.symmetric(X*Z), Q.symmetric(X) & Q.symmetric(Z)) is True assert ask(Q.symmetric(X + Z), Q.symmetric(X) & Q.symmetric(Z)) is True assert ask(Q.symmetric(Y)) is False assert ask(Q.symmetric(Y*Y.T)) is True assert ask(Q.symmetric(Y.T*X*Y)) is None assert ask(Q.symmetric(Y.T*X*Y), Q.symmetric(X)) is True assert ask(Q.symmetric(X*X*X*X*X*X*X*X*X*X), Q.symmetric(X)) is True def _test_orthogonal_unitary(predicate): assert ask(predicate(X), predicate(X)) assert ask(predicate(X.T), predicate(X)) is True assert ask(predicate(X.I), predicate(X)) is True assert ask(predicate(Y)) is False assert ask(predicate(X)) is None assert ask(predicate(X*Z*X), predicate(X) & predicate(Z)) is True assert ask(predicate(Identity(3))) is True assert ask(predicate(ZeroMatrix(3, 3))) is False assert ask(Q.invertible(X), predicate(X)) assert not ask(predicate(X + Z), predicate(X) & predicate(Z)) def test_orthogonal(): _test_orthogonal_unitary(Q.orthogonal) def test_unitary(): _test_orthogonal_unitary(Q.unitary) assert ask(Q.unitary(X), Q.orthogonal(X)) def test_fullrank(): assert ask(Q.fullrank(X), Q.fullrank(X)) assert ask(Q.fullrank(X.T), Q.fullrank(X)) is True assert ask(Q.fullrank(X)) is None assert ask(Q.fullrank(Y)) is None assert ask(Q.fullrank(X*Z), Q.fullrank(X) & Q.fullrank(Z)) is True assert ask(Q.fullrank(Identity(3))) is True assert ask(Q.fullrank(ZeroMatrix(3, 3))) is False assert ask(Q.invertible(X), ~Q.fullrank(X)) == False def test_positive_definite(): assert ask(Q.positive_definite(X), Q.positive_definite(X)) assert ask(Q.positive_definite(X.T), Q.positive_definite(X)) is True assert ask(Q.positive_definite(X.I), Q.positive_definite(X)) is True assert ask(Q.positive_definite(Y)) is False assert ask(Q.positive_definite(X)) is None assert ask(Q.positive_definite(X*Z*X), Q.positive_definite(X) & Q.positive_definite(Z)) is True assert ask(Q.positive_definite(X), Q.orthogonal(X)) assert ask(Q.positive_definite(Y.T*X*Y), Q.positive_definite(X) & Q.fullrank(Y)) is True assert not ask(Q.positive_definite(Y.T*X*Y), Q.positive_definite(X)) assert ask(Q.positive_definite(Identity(3))) is True assert ask(Q.positive_definite(ZeroMatrix(3, 3))) is False assert ask(Q.positive_definite(X + Z), Q.positive_definite(X) & Q.positive_definite(Z)) is True assert not ask(Q.positive_definite(-X), Q.positive_definite(X)) assert ask(Q.positive(X[1, 1]), Q.positive_definite(X)) def test_triangular(): assert ask(Q.upper_triangular(X + Z.T + Identity(2)), Q.upper_triangular(X) & Q.lower_triangular(Z)) is True assert ask(Q.upper_triangular(X*Z.T), Q.upper_triangular(X) & Q.lower_triangular(Z)) is True assert ask(Q.lower_triangular(Identity(3))) is True assert ask(Q.lower_triangular(ZeroMatrix(3, 3))) is True assert ask(Q.triangular(X), Q.unit_triangular(X)) def test_diagonal(): assert ask(Q.diagonal(X + Z.T + Identity(2)), Q.diagonal(X) & Q.diagonal(Z)) is True assert ask(Q.diagonal(ZeroMatrix(3, 3))) assert ask(Q.lower_triangular(X) & Q.upper_triangular(X), Q.diagonal(X)) assert ask(Q.diagonal(X), Q.lower_triangular(X) & Q.upper_triangular(X)) assert ask(Q.symmetric(X), Q.diagonal(X)) assert ask(Q.triangular(X), Q.diagonal(X)) def test_non_atoms(): assert ask(Q.real(Trace(X)), Q.positive(Trace(X))) @XFAIL def test_non_trivial_implies(): X = MatrixSymbol('X', 3, 3) Y = MatrixSymbol('Y', 3, 3) assert ask(Q.lower_triangular(X+Y), Q.lower_triangular(X) & Q.lower_triangular(Y)) assert ask(Q.triangular(X), Q.lower_triangular(X)) assert ask(Q.triangular(X+Y), Q.lower_triangular(X) & Q.lower_triangular(Y)) def test_MatrixSlice(): X = MatrixSymbol('X', 4, 4) B = MatrixSlice(X, (1, 3), (1, 3)) C = MatrixSlice(X, (0, 3), (1, 3)) assert ask(Q.symmetric(B), Q.symmetric(X)) assert ask(Q.invertible(B), Q.invertible(X)) assert ask(Q.diagonal(B), Q.diagonal(X)) assert ask(Q.orthogonal(B), Q.orthogonal(X)) assert ask(Q.upper_triangular(B), Q.upper_triangular(X)) assert not ask(Q.symmetric(C), Q.symmetric(X)) assert not ask(Q.invertible(C), Q.invertible(X)) assert not ask(Q.diagonal(C), Q.diagonal(X)) assert not ask(Q.orthogonal(C), Q.orthogonal(X)) assert not ask(Q.upper_triangular(C), Q.upper_triangular(X)) def test_det_trace_positive(): X = MatrixSymbol('X', 4, 4) assert ask(Q.positive(Trace(X)), Q.positive_definite(X)) assert ask(Q.positive(Determinant(X)), Q.positive_definite(X)) def test_field_assumptions(): X = MatrixSymbol('X', 4, 4) Y = MatrixSymbol('Y', 4, 4) assert ask(Q.real_elements(X), Q.real_elements(X)) assert not ask(Q.integer_elements(X), Q.real_elements(X)) assert ask(Q.complex_elements(X), Q.real_elements(X)) assert ask(Q.real_elements(X+Y), Q.real_elements(X)) is None assert ask(Q.real_elements(X+Y), Q.real_elements(X) & Q.real_elements(Y)) from sympy.matrices.expressions.hadamard import HadamardProduct assert ask(Q.real_elements(HadamardProduct(X, Y)), Q.real_elements(X) & Q.real_elements(Y)) assert ask(Q.complex_elements(X+Y), Q.real_elements(X) & Q.complex_elements(Y)) assert ask(Q.real_elements(X.T), Q.real_elements(X)) assert ask(Q.real_elements(X.I), Q.real_elements(X) & Q.invertible(X)) assert ask(Q.real_elements(Trace(X)), Q.real_elements(X)) assert ask(Q.integer_elements(Determinant(X)), Q.integer_elements(X)) assert not ask(Q.integer_elements(X.I), Q.integer_elements(X)) alpha = Symbol('alpha') assert ask(Q.real_elements(alpha*X), Q.real_elements(X) & Q.real(alpha)) assert ask(Q.real_elements(LofLU(X)), Q.real_elements(X)) def test_matrix_element_sets(): X = MatrixSymbol('X', 4, 4) assert ask(Q.real(X[1, 2]), Q.real_elements(X)) assert ask(Q.integer(X[1, 2]), Q.integer_elements(X)) assert ask(Q.complex(X[1, 2]), Q.complex_elements(X)) assert ask(Q.integer_elements(Identity(3))) assert ask(Q.integer_elements(ZeroMatrix(3, 3))) from sympy.matrices.expressions.fourier import DFT assert ask(Q.complex_elements(DFT(3))) def test_matrix_element_sets_slices_blocks(): from sympy.matrices.expressions import BlockMatrix X = MatrixSymbol('X', 4, 4) assert ask(Q.integer_elements(X[:, 3]), Q.integer_elements(X)) assert ask(Q.integer_elements(BlockMatrix([[X], [X]])), Q.integer_elements(X)) def test_matrix_element_sets_determinant_trace(): assert ask(Q.integer(Determinant(X)), Q.integer_elements(X)) assert ask(Q.integer(Trace(X)), Q.integer_elements(X)) sympy-0.7.4.1/sympy/assumptions/tests/test_refine.py0000644000175000017500000001244212253362407023040 0ustar georgeskgeorgeskfrom sympy import Abs, exp, Expr, I, pi, Q, Rational, refine, S, sqrt from sympy.abc import x, y, z from sympy.core.relational import Eq, Ne from sympy.functions.elementary.piecewise import Piecewise def test_Abs(): assert refine(Abs(x), Q.positive(x)) == x assert refine(1 + Abs(x), Q.positive(x)) == 1 + x assert refine(Abs(x), Q.negative(x)) == -x assert refine(1 + Abs(x), Q.negative(x)) == 1 - x assert refine(Abs(x**2)) != x**2 assert refine(Abs(x**2), Q.real(x)) == x**2 def test_pow(): assert refine((-1)**x, Q.even(x)) == 1 assert refine((-1)**x, Q.odd(x)) == -1 assert refine((-2)**x, Q.even(x)) == 2**x # nested powers assert refine(sqrt(x**2)) != Abs(x) assert refine(sqrt(x**2), Q.complex(x)) != Abs(x) assert refine(sqrt(x**2), Q.real(x)) == Abs(x) assert refine(sqrt(x**2), Q.positive(x)) == x assert refine((x**3)**(S(1)/3)) != x assert refine((x**3)**(S(1)/3), Q.real(x)) != x assert refine((x**3)**(S(1)/3), Q.positive(x)) == x assert refine(sqrt(1/x), Q.real(x)) != 1/sqrt(x) assert refine(sqrt(1/x), Q.positive(x)) == 1/sqrt(x) # powers of (-1) assert refine((-1)**(x + y), Q.even(x)) == (-1)**y assert refine((-1)**(x + y + z), Q.odd(x) & Q.odd(z)) == (-1)**y assert refine((-1)**(x + y + 1), Q.odd(x)) == (-1)**y assert refine((-1)**(x + y + 2), Q.odd(x)) == (-1)**(y + 1) assert refine((-1)**(x + 3)) == (-1)**(x + 1) assert refine((-1)**((-1)**x/2 - S.Half), Q.integer(x)) == (-1)**x assert refine((-1)**((-1)**x/2 + S.Half), Q.integer(x)) == (-1)**(x + 1) assert refine((-1)**((-1)**x/2 + 5*S.Half), Q.integer(x)) == (-1)**(x + 1) assert refine((-1)**((-1)**x/2 - 7*S.Half), Q.integer(x)) == (-1)**(x + 1) assert refine((-1)**((-1)**x/2 - 9*S.Half), Q.integer(x)) == (-1)**x # powers of Abs assert refine(Abs(x)**2, Q.real(x)) == x**2 assert refine(Abs(x)**3, Q.real(x)) == Abs(x)**3 assert refine(Abs(x)**2) == Abs(x)**2 def test_exp(): assert refine(exp(pi*I*2*x), Q.integer(x)) == 1 assert refine(exp(pi*I*2*(x + Rational(1, 2))), Q.integer(x)) == -1 assert refine(exp(pi*I*2*(x + Rational(1, 4))), Q.integer(x)) == I assert refine(exp(pi*I*2*(x + Rational(3, 4))), Q.integer(x)) == -I def test_Relational(): assert refine(x < 0, ~Q.is_true(x < 0)) == False assert refine(x < 0, Q.is_true(x < 0)) == True assert refine(x < 0, Q.is_true(y < 0)) == (x < 0) assert refine(x <= 0, ~Q.is_true(x <= 0)) == False assert refine(x <= 0, Q.is_true(x <= 0)) == True assert refine(x <= 0, Q.is_true(y <= 0)) == (x <= 0) assert refine(x > 0, ~Q.is_true(x > 0)) == False assert refine(x > 0, Q.is_true(x > 0)) == True assert refine(x > 0, Q.is_true(y > 0)) == (x > 0) assert refine(x >= 0, ~Q.is_true(x >= 0)) == False assert refine(x >= 0, Q.is_true(x >= 0)) == True assert refine(x >= 0, Q.is_true(y >= 0)) == (x >= 0) assert refine(Eq(x, 0), ~Q.is_true(Eq(x, 0))) == False assert refine(Eq(x, 0), Q.is_true(Eq(x, 0))) == True assert refine(Eq(x, 0), Q.is_true(Eq(y, 0))) == Eq(x, 0) assert refine(Ne(x, 0), ~Q.is_true(Ne(x, 0))) == False assert refine(Ne(x, 0), Q.is_true(Ne(x, 0))) == True assert refine(Ne(x, 0), Q.is_true(Ne(y, 0))) == (Ne(x, 0)) def test_Piecewise(): assert refine(Piecewise((1, x < 0), (3, True)), Q.is_true(x < 0)) == 1 assert refine(Piecewise((1, x < 0), (3, True)), ~Q.is_true(x < 0)) == 3 assert refine(Piecewise((1, x < 0), (3, True)), Q.is_true(y < 0)) == Piecewise((1, x < 0), (3, True)) assert refine(Piecewise((1, x > 0), (3, True)), Q.is_true(x > 0)) == 1 assert refine(Piecewise((1, x > 0), (3, True)), ~Q.is_true(x > 0)) == 3 assert refine(Piecewise((1, x > 0), (3, True)), Q.is_true(y > 0)) == Piecewise((1, x > 0), (3, True)) assert refine(Piecewise((1, x <= 0), (3, True)), Q.is_true(x <= 0)) == 1 assert refine(Piecewise((1, x <= 0), (3, True)), ~Q.is_true(x <= 0)) == 3 assert refine(Piecewise((1, x <= 0), (3, True)), Q.is_true(y <= 0)) == Piecewise((1, x <= 0), (3, True)) assert refine(Piecewise((1, x >= 0), (3, True)), Q.is_true(x >= 0)) == 1 assert refine(Piecewise((1, x >= 0), (3, True)), ~Q.is_true(x >= 0)) == 3 assert refine(Piecewise((1, x >= 0), (3, True)), Q.is_true(y >= 0)) == Piecewise((1, x >= 0), (3, True)) assert refine(Piecewise((1, Eq(x, 0)), (3, True)), Q.is_true(Eq(x, 0))) == 1 assert refine(Piecewise((1, Eq(x, 0)), (3, True)), ~Q.is_true(Eq(x, 0))) == 3 assert refine(Piecewise((1, Eq(x, 0)), (3, True)), Q.is_true(Eq(y, 0))) == Piecewise((1, Eq(x, 0)), (3, True)) assert refine(Piecewise((1, Ne(x, 0)), (3, True)), Q.is_true(Ne(x, 0))) == 1 assert refine(Piecewise((1, Ne(x, 0)), (3, True)), ~Q.is_true(Ne(x, 0))) == 3 assert refine(Piecewise((1, Ne(x, 0)), (3, True)), Q.is_true(Ne(y, 0))) == Piecewise((1, Ne(x, 0)), (3, True)) def test_func_args(): class MyClass(Expr): # A class with nontrivial .func def __init__(self, *args): self.my_member = "" @property def func(self): def my_func(*args): obj = MyClass(*args) obj.my_member = self.my_member return obj return my_func x = MyClass() x.my_member = "A very important value" assert x.my_member == refine(x).my_member sympy-0.7.4.1/sympy/assumptions/tests/__init__.py0000644000175000017500000000000012253362407022253 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/assumptions/__init__.py0000644000175000017500000000024312253362407021122 0ustar georgeskgeorgeskfrom .assume import AppliedPredicate, Predicate, AssumptionsContext, assuming from .ask import Q, ask, register_handler, remove_handler from .refine import refine sympy-0.7.4.1/sympy/solvers/0000755000175000017500000000000012253362407016122 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/solvers/diophantine.py0000644000175000017500000023171512253362407021007 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import (degree_list, Poly, igcd, divisors, sign, symbols, S, Integer, Wild, Symbol, factorint, Add, Mul, solve, ceiling, floor, sqrt, sympify, Subs, ilcm, Matrix, factor_list, perfect_power, isprime, nextprime, integer_nthroot) from sympy.simplify.simplify import rad_rationalize, _mexpand from sympy.ntheory.modular import solve_congruence from sympy.utilities import default_sort_key from sympy.core.numbers import igcdex from sympy.ntheory.residue_ntheory import sqrt_mod from sympy.core.compatibility import xrange __all__ = ['diophantine', 'diop_solve', 'classify_diop', 'diop_linear', 'base_solution_linear', 'diop_quadratic', 'diop_DN', 'cornacchia', 'diop_bf_DN', 'transformation_to_DN', 'find_DN', 'diop_ternary_quadratic', 'square_factor', 'descent', 'diop_general_pythagorean', 'diop_general_sum_of_squares', 'partition', 'sum_of_three_squares', 'sum_of_four_squares'] def diophantine(eq, param=symbols("t", Integer=True)): """ Simplify the solution procedure of diophantine equation ``eq`` by converting it into a product of terms which should equal zero. For example, when solving, `x^2 - y^2 = 0` this is treated as `(x + y)(x - y) = 0` and `x+y = 0` and `x-y = 0` are solved independently and combined. Each term is solved by calling ``diop_solve()``. Usage ===== ``diophantine(eq, t)``: Solve the diophantine equation ``eq``. ``t`` is the parameter to be used by ``diop_solve()``. Details ======= ``eq`` should be an expression which is assumed to be zero. ``t`` is the parameter to be used in the solution. Examples ======== >>> from sympy.solvers.diophantine import diophantine >>> from sympy.abc import x, y, z >>> diophantine(x**2 - y**2) set([(-t, -t), (t, -t)]) #>>> diophantine(x*(2*x + 3*y - z)) #set([(0, n1, n2), (3*t - z, -2*t + z, z)]) #>>> diophantine(x**2 + 3*x*y + 4*x) #set([(0, n1), (3*t - 4, -t)]) See Also ======== diop_solve() """ var = list(eq.expand(force=True).free_symbols) var.sort(key=default_sort_key) terms = factor_list(eq)[1] sols = set([]) for term in terms: base = term[0] var_t, jnk, eq_type = classify_diop(base) solution = diop_solve(base, param) if eq_type in ["linear", "homogeneous_ternary_quadratic", "general_pythagorean"]: if merge_solution(var, var_t, solution) != (): sols.add(merge_solution(var, var_t, solution)) elif eq_type in ["binary_quadratic", "general_sum_of_squares"]: for sol in solution: if merge_solution(var, var_t, sol) != (): sols.add(merge_solution(var, var_t, sol)) elif eq_type == "univariable": sols = solution return sols def merge_solution(var, var_t, solution): """ This is used to construct the full solution from the solutions of sub equations. For example when solving the equation `(x - y)(x^2 + y^2 - z^2) = 0`, solutions for each of the equations `x-y = 0` and `x^2 + y^2 - z^2` are found independently. Solutions for `x - y = 0` are `(x, y) = (t, t)`. But we should introduce a value for z when we output the solution for the original equation. This function converts `(t, t)` into `(t, t, n_{1})` where `n_{1}` is an integer parameter. """ # currently more than 3 parameters are not required. n1, n2, n3 = symbols("n1, n2, n3", Integer=True) params = [n1, n2, n3] l = [] count1 = 0 count2 = 0 if None not in solution: for v in var: if v in var_t: l.append(solution[count1]) count1 = count1 + 1 else: l.append(params[count2]) count2 = count2 + 1 return tuple(l) def diop_solve(eq, param=symbols("t", Integer=True)): """ Solves the diophantine equation ``eq``. Similar to ``diophantine()`` but doesn't try to factor ``eq`` as latter does. Uses ``classify_diop()`` to determine the type of the eqaution and calls the appropriate solver function. Usage ===== ``diop_solve(eq, t)``: Solve diophantine equation, ``eq`` using ``t`` as a parameter if needed. Details ======= ``eq`` should be an expression which is assumed to be zero. ``t`` is a parameter to be used in the solution. Examples ======== >>> from sympy.solvers.diophantine import diop_solve >>> from sympy.abc import x, y, z, w >>> diop_solve(2*x + 3*y - 5) (3*t - 5, -2*t + 5) >>> diop_solve(4*x + 3*y -4*z + 5) (3*t + 4*z - 5, -4*t - 4*z + 5, z) >>> diop_solve(x + 3*y - 4*z + w -6) (t, -t - 3*y + 4*z + 6, y, z) >>> diop_solve(x**2 + y**2 - 5) set([(-2, -1), (-2, 1), (2, -1), (2, 1)]) See Also ======== diophantine() """ var, coeff, eq_type = classify_diop(eq) if eq_type == "linear": return _diop_linear(var, coeff, param) elif eq_type == "binary_quadratic": return _diop_quadratic(var, coeff, param) elif eq_type == "homogeneous_ternary_quadratic": x_0, y_0, z_0 = _diop_ternary_quadratic(var, coeff) return _parametrize_ternary_quadratic((x_0, y_0, z_0), var, coeff) elif eq_type == "general_pythagorean": return _diop_general_pythagorean(var, coeff, param) elif eq_type == "univariable": l = solve(eq) s = set([]) for soln in l: if isinstance(soln, Integer): s.add(soln) return s elif eq_type == "general_sum_of_squares": return _diop_general_sum_of_squares(var, coeff) def classify_diop(eq): """ Helper routine used by diop_solve() to find the type of the ``eq`` etc. Returns a tuple containing the type of the diophantine equation along with the variables(free symbols) and their coefficients. Variables are returned as a list and coefficients are returned as a dict with the key being the respective term and the constant term is keyed to Integer(1). Type is an element in the set {"linear", "binary_quadratic", "general_pythagorean", "homogeneous_ternary_quadratic", "univariable", "general_sum_of_squares"} Usage ===== ``classify_diop(eq)``: Return variables, coefficients and type of the ``eq``. Details ======= ``eq`` should be an expression which is assumed to be zero. Examples ======== >>> from sympy.solvers.diophantine import classify_diop >>> from sympy.abc import x, y, z, w, t >>> classify_diop(4*x + 6*y - 4) ([x, y], {1: -4, x: 4, y: 6}, 'linear') >>> classify_diop(x + 3*y -4*z + 5) ([x, y, z], {1: 5, x: 1, y: 3, z: -4}, 'linear') >>> classify_diop(x**2 + y**2 - x*y + x + 5) ([x, y], {1: 5, x: 1, x**2: 1, y: 0, y**2: 1, x*y: -1}, 'binary_quadratic') """ eq = eq.expand(force=True) var = list(eq.free_symbols) var.sort(key=default_sort_key) coeff = {} diop_type = None coeff = dict([reversed(t.as_independent(*var)) for t in eq.args]) for v in coeff: if not isinstance(coeff[v], Integer): raise TypeError("Coefficients should be Integers") if len(var) == 1: diop_type = "univariable" elif Poly(eq).total_degree() == 1: diop_type = "linear" elif Poly(eq).total_degree() == 2 and len(var) == 2: diop_type = "binary_quadratic" x, y = var[:2] if isinstance(eq, Mul): coeff = {x**2: 0, x*y: eq.args[0], y**2: 0, x: 0, y: 0, Integer(1): 0} else: for term in [x**2, y**2, x*y, x, y, Integer(1)]: if term not in coeff.keys(): coeff[term] = Integer(0) elif Poly(eq).total_degree() == 2 and len(var) == 3 and Integer(1) not in coeff.keys(): for v in var: if v in coeff.keys(): diop_type = "inhomogeneous_ternary_quadratic" break else: diop_type = "homogeneous_ternary_quadratic" x, y, z = var[:3] for term in [x**2, y**2, z**2, x*y, y*z, x*z]: if term not in coeff.keys(): coeff[term] = Integer(0) elif Poly(eq).degree() == 2 and len(var) >= 3: for v in var: if v in coeff.keys(): diop_type = "inhomogeneous_general_quadratic" break else: if Integer(1) in coeff.keys(): constant_term = True else: constant_term = False non_square_degree_2_terms = False for v in var: for u in var: if u != v and u*v in coeff.keys(): non_square_degree_2_terms = True break if non_square_degree_2_terms: break if constant_term and non_square_degree_2_terms: diop_type = "inhomogeneous_general_quadratic" elif constant_term and not non_square_degree_2_terms: for v in var: if coeff[v**2] != 1: break else: diop_type = "general_sum_of_squares" elif not constant_term and non_square_degree_2_terms: diop_type = "homogeneous_general_quadratic" else: coeff_sign_sum = 0 for v in var: if not isinstance(sqrt(abs(Integer(coeff[v**2]))), Integer): break coeff_sign_sum = coeff_sign_sum + sign(coeff[v**2]) else: if abs(coeff_sign_sum) == len(var) - 2 and not constant_term: diop_type = "general_pythagorean" elif Poly(eq).total_degree() == 3 and len(var) == 2: x, y = var[:2] diop_type = "cubic_thue" for term in [x**3, x**2*y, x*y**2, y**3, Integer(1)]: if term not in coeff.keys(): coeff[term] == Integer(0) if diop_type is not None: return var, coeff, diop_type else: raise NotImplementedError("Still not implemented") def diop_linear(eq, param=symbols("t", Integer=True)): """ Solves linear diophantine equations. A linear diophantine equation is an equation of the form `a_{1}x_{1} + a_{2}x_{2} + .. + a_{n}x_{n} = 0` where `a_{1}, a_{2}, ..a_{n}` are integer constants and `x_{1}, x_{2}, ..x_{n}` are integer variables. Usage ===== ``diop_linear(eq)``: Returns a tuple containing solutions to the diophantine equation ``eq``. Values in the tuple is arranged in the same order as the sorted variables. Details ======= ``eq`` is a linear diophantine equation which is assumed to be zero. ``param`` is the parameter to be used in the solution. Examples ======== >>> from sympy.solvers.diophantine import diop_linear >>> from sympy.abc import x, y, z, t >>> from sympy import Integer >>> diop_linear(2*x - 3*y - 5) #solves equation 2*x - 3*y -5 = 0 (-3*t - 5, -2*t - 5) Here x = -3*t - 5 and y = -2*t - 5 >>> diop_linear(2*x - 3*y - 4*z -3) (-3*t - 4*z - 3, -2*t - 4*z - 3, z) See Also ======== diop_quadratic(), diop_ternary_quadratic(), diop_general_pythagorean(), diop_general_sum_of_squares() """ var, coeff, diop_type = classify_diop(eq) if diop_type == "linear": return _diop_linear(var, coeff, param) def _diop_linear(var, coeff, param): x, y = var[:2] a = coeff[x] b = coeff[y] if len(var) == len(coeff): c = 0 else: c = -coeff[Integer(1)] if len(var) == 2: sol_x, sol_y = base_solution_linear(c, a, b, param) return (sol_x, sol_y) elif len(var) > 2: X = [] Y = [] for v in var[2:]: sol_x, sol_y = base_solution_linear(-coeff[v], a, b) X.append(sol_x*v) Y.append(sol_y*v) sol_x, sol_y = base_solution_linear(c, a, b, param) X.append(sol_x) Y.append(sol_y) l = [] if None not in X and None not in Y: l.append(Add(*X)) l.append(Add(*Y)) for v in var[2:]: l.append(v) else: for v in var: l.append(None) return tuple(l) def base_solution_linear(c, a, b, t=None): """ Return the base solution for a linear diophantine equation with two variables. Used by ``diop_linear()`` to find the base solution of a linear Diophantine equation. If ``t`` is given then the parametrized solution is returned. Usage ===== ``base_solution_linear(c, a, b, t)``: ``a``, ``b``, ``c`` are coefficients in `ax + by = c` and ``t`` is the parameter to be used in the solution. Examples ======== >>> from sympy.solvers.diophantine import base_solution_linear >>> from sympy.abc import t >>> base_solution_linear(5, 2, 3) # equation 2*x + 3*y = 5 (-5, 5) >>> base_solution_linear(0, 5, 7) # equation 5*x + 7*y = 0 (0, 0) >>> base_solution_linear(5, 2, 3, t) # equation 2*x + 3*y = 5 (3*t - 5, -2*t + 5) >>> base_solution_linear(0, 5, 7, t) # equation 5*x + 7*y = 0 (7*t, -5*t) """ d = igcd(a, igcd(b, c)) a = a // d b = b // d c = c // d if c == 0: if t != None: return (b*t , -a*t) else: return (S.Zero, S.Zero) else: x0, y0, d = extended_euclid(int(abs(a)), int(abs(b))) x0 = x0 * sign(a) y0 = y0 * sign(b) if divisible(c, d): if t != None: return (c*x0 + b*t, c*y0 - a*t) else: return (Integer(c*x0), Integer(c*y0)) else: return (None, None) def extended_euclid(a, b): """ For given ``a``, ``b`` returns a tuple containing integers `x`, `y` and `d` such that `ax + by = d`. Here `d = gcd(a, b)`. Usage ===== ``extended_euclid(a, b)``: returns `x`, `y` and `\gcd(a, b)`. Details ======= ``a`` Any instance of Integer. ``b`` Any instance of Integer. Examples ======== >>> from sympy.solvers.diophantine import extended_euclid >>> extended_euclid(4, 6) (-1, 1, 2) >>> extended_euclid(3, 5) (2, -1, 1) """ if b == 0: return (1, 0, a) x0, y0, d = extended_euclid(b, a%b) x, y = y0, x0 - (a//b) * y0 return x, y, d def divisible(a, b): """ Returns `True` if ``a`` is divisible by ``b`` and `False` otherwise. """ return igcd(int(a), int(b)) == abs(int(b)) def diop_quadratic(eq, param=symbols("t", Integer=True)): """ Solves quadratic diophantine equations. i.e. equations of the form `Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0`. Returns a set containing the tuples `(x, y)` which contains the solutions. If there are no solutions then `(None, None)` is returned. Usage ===== ``diop_quadratic(eq, param)``: ``eq`` is a quadratic binary diophantine equation. ``param`` is used to indicate the parameter to be used in the solution. Details ======= ``eq`` should be an expression which is assumed to be zero. ``param`` is a parameter to be used in the solution. Examples ======== >>> from sympy.abc import x, y, t >>> from sympy.solvers.diophantine import diop_quadratic >>> diop_quadratic(x**2 + y**2 + 2*x + 2*y + 2, t) set([(-1, -1)]) References ========== .. [1] Methods to solve Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0,[online], Available: http://www.alpertron.com.ar/METHODS.HTM .. [2] Solving the equation ax^2+ bxy + cy^2 + dx + ey + f= 0, [online], Available: http://www.jpr2718.org/ax2p.pdf See Also ======== diop_linear(), diop_ternary_quadratic(), diop_general_sum_of_squares(), diop_general_pythagorean() """ var, coeff, diop_type = classify_diop(eq) if diop_type == "binary_quadratic": return _diop_quadratic(var, coeff, param) def _diop_quadratic(var, coeff, t): x, y = var[:2] for term in [x**2, y**2, x*y, x, y, Integer(1)]: if term not in coeff.keys(): coeff[term] = Integer(0) A = coeff[x**2] B = coeff[x*y] C = coeff[y**2] D = coeff[x] E = coeff[y] F = coeff[Integer(1)] d = igcd(A, igcd(B, igcd(C, igcd(D, igcd(E, F))))) A = A // d B = B // d C = C // d D = D // d E = E // d F = F // d # (1) Linear case: A = B = C = 0 ==> considered under linear diophantine equations # (2) Simple-Hyperbolic case:A = C = 0, B != 0 # In this case equation can be converted to (Bx + E)(By + D) = DE - BF # We consider two cases; DE - BF = 0 and DE - BF != 0 # More details, http://www.alpertron.com.ar/METHODS.HTM#SHyperb l = set([]) if A == 0 and C == 0 and B != 0: if D*E - B*F == 0: if divisible(int(E), int(B)): l.add((-E/B, t)) if divisible(int(D), int(B)): l.add((t, -D/B)) else: div = divisors(D*E - B*F) div = div + [-term for term in div] for d in div: if divisible(int(d - E), int(B)): x0 = (d - E) // B if divisible(int(D*E - B*F), int(d)): if divisible(int((D*E - B*F)// d - D), int(B)): y0 = ((D*E - B*F) // d - D) // B l.add((x0, y0)) # (3) Parabolic case: B**2 - 4*A*C = 0 # There are two subcases to be considered in this case. # sqrt(c)D - sqrt(a)E = 0 and sqrt(c)D - sqrt(a)E != 0 # More Details, http://www.alpertron.com.ar/METHODS.HTM#Parabol elif B**2 - 4*A*C == 0: if A == 0: s = _diop_quadratic([y, x], coeff, t) for soln in s: l.add((soln[1], soln[0])) else: g = igcd(A, C) g = abs(g) * sign(A) a = A // g b = B // g c = C // g e = sign(B/A) if e*sqrt(c)*D - sqrt(a)*E == 0: z = symbols("z", real=True) roots = solve(sqrt(a)*g*z**2 + D*z + sqrt(a)*F) for root in roots: if isinstance(root, Integer): l.add((diop_solve(sqrt(a)*x + e*sqrt(c)*y - root)[0], diop_solve(sqrt(a)*x + e*sqrt(c)*y - root)[1])) elif isinstance(e*sqrt(c)*D - sqrt(a)*E, Integer): solve_x = lambda u: e*sqrt(c)*g*(sqrt(a)*E - e*sqrt(c)*D)*t**2 - (E + 2*e*sqrt(c)*g*u)*t\ - (e*sqrt(c)*g*u**2 + E*u + e*sqrt(c)*F) // (e*sqrt(c)*D - sqrt(a)*E) solve_y = lambda u: sqrt(a)*g*(e*sqrt(c)*D - sqrt(a)*E)*t**2 + (D + 2*sqrt(a)*g*u)*t \ + (sqrt(a)*g*u**2 + D*u + sqrt(a)*F) // (e*sqrt(c)*D - sqrt(a)*E) for z0 in xrange(0, abs(e*sqrt(c)*D - sqrt(a)*E)): if divisible(sqrt(a)*g*z0**2 + D*z0 + sqrt(a)*F, e*sqrt(c)*D - sqrt(a)*E): l.add((solve_x(z0), solve_y(z0))) # (4) Method used when B**2 - 4*A*C is a square, is descibed in p. 6 of the below paper # by John P. Robertson. # http://www.jpr2718.org/ax2p.pdf elif isinstance(sqrt(B**2 - 4*A*C), Integer): if A != 0: r = sqrt(B**2 - 4*A*C) u, v = symbols("u, v", integer=True) eq = _mexpand(4*A*r*u*v + 4*A*D*(B*v + r*u + r*v - B*u) + 2*A*4*A*E*(u - v) + 4*A*r*4*A*F) sol = diop_solve(eq, t) sol = list(sol) for solution in sol: s0 = solution[0] t0 = solution[1] x_0 = S(B*t0 + r*s0 + r*t0 - B*s0)/(4*A*r) y_0 = S(s0 - t0)/(2*r) if isinstance(s0, Symbol) or isinstance(t0, Symbol): if check_param(x_0, y_0, 4*A*r, t) != (None, None): l.add((check_param(x_0, y_0, 4*A*r, t)[0], check_param(x_0, y_0, 4*A*r, t)[1])) elif divisible(B*t0 + r*s0 + r*t0 - B*s0, 4*A*r): if divisible(s0 - t0, 2*r): if is_solution_quad(var, coeff, x_0, y_0): l.add((x_0, y_0)) else: _var = var _var[0], _var[1] = _var[1], _var[0] # Interchange x and y s = _diop_quadratic(_var, coeff, t) while len(s) > 0: sol = s.pop() l.add((sol[1], sol[0])) # (5) B**2 - 4*A*C > 0 and B**2 - 4*A*C not a square or B**2 - 4*A*C < 0 else: P, Q = _transformation_to_DN(var, coeff) D, N = _find_DN(var, coeff) solns_pell = diop_DN(D, N) if D < 0: for solution in solns_pell: for X_i in [-solution[0], solution[0]]: for Y_i in [-solution[1], solution[1]]: x_i, y_i = (P*Matrix([X_i, Y_i]) + Q)[0], (P*Matrix([X_i, Y_i]) + Q)[1] if isinstance(x_i, Integer) and isinstance(y_i, Integer): l.add((x_i, y_i)) else: # In this case equation can be transformed into a Pell equation #n = symbols("n", integer=True) a = diop_DN(D, 1) T = a[0][0] U = a[0][1] if (isinstance(P[0], Integer) and isinstance(P[1], Integer) and isinstance(P[2], Integer) and isinstance(P[3], Integer) and isinstance(Q[0], Integer) and isinstance(Q[1], Integer)): for sol in solns_pell: r = sol[0] s = sol[1] x_n = S((r + s*sqrt(D))*(T + U*sqrt(D))**t + (r - s*sqrt(D))*(T - U*sqrt(D))**t)/2 y_n = S((r + s*sqrt(D))*(T + U*sqrt(D))**t - (r - s*sqrt(D))*(T - U*sqrt(D))**t)/(2*sqrt(D)) x_n = _mexpand(x_n) y_n = _mexpand(y_n) x_n, y_n = (P*Matrix([x_n, y_n]) + Q)[0], (P*Matrix([x_n, y_n]) + Q)[1] l.add((x_n, y_n)) else: L = ilcm(S(P[0]).q, ilcm(S(P[1]).q, ilcm(S(P[2]).q, ilcm(S(P[3]).q, ilcm(S(Q[0]).q, S(Q[1]).q))))) k = 0 done = False T_k = T U_k = U while not done: k = k + 1 if (T_k - 1) % L == 0 and U_k % L == 0: done = True T_k, U_k = T_k*T + D*U_k*U, T_k*U + U_k*T for soln in solns_pell: X = soln[0] Y = soln[1] done = False for i in xrange(k): X_1 = X*T + D*U*Y Y_1 = X*U + Y*T x = (P*Matrix([X_1, Y_1]) + Q)[0] y = (P*Matrix([X_1, Y_1]) + Q)[1] if is_solution_quad(var, coeff, x, y): done = True x_n = S( (X_1 + sqrt(D)*Y_1)*(T + sqrt(D)*U)**(t*L) + (X_1 - sqrt(D)*Y_1)*(T - sqrt(D)*U)**(t*L) )/ 2 y_n = S( (X_1 + sqrt(D)*Y_1)*(T + sqrt(D)*U)**(t*L) - (X_1 - sqrt(D)*Y_1)*(T - sqrt(D)*U)**(t*L) )/ (2*sqrt(D)) x_n = _mexpand(x_n) y_n = _mexpand(y_n) x_n, y_n = (P*Matrix([x_n, y_n]) + Q)[0], (P*Matrix([x_n, y_n]) + Q)[1] l.add((x_n, y_n)) if done: break return l def is_solution_quad(var, coeff, u, v): """ Check whether `(u, v)` is solution to the quadratic binary diophantine equation with the variable list ``var`` and coefficient dictionary ``coeff``. Not intended for use by normal users. """ x, y = var[:2] eq = x**2*coeff[x**2] + x*y*coeff[x*y] + y**2*coeff[y**2] + x*coeff[x] + y*coeff[y] + coeff[Integer(1)] return _mexpand(Subs(eq, (x, y), (u, v)).doit()) == 0 def diop_DN(D, N, t=symbols("t", Integer=True)): """ Solves the equation `x^2 - Dy^2 = N`. Mainly concerned in the case `D > 0, D` is not a perfect square, which is the same as generalized Pell equation. To solve the generalized Pell equation this function Uses LMM algorithm. Refer [1]_ for more details on the algorithm. Returns one solution for each class of the solutions. Other solutions of the class can be constructed according to the values of ``D`` and ``N``. Returns a list containing the solution tuples `(x, y)`. Usage ===== ``diop_DN(D, N, t)``: D and N are integers as in `x^2 - Dy^2 = N` and ``t`` is the parameter to be used in the solutions. Details ======= ``D`` and ``N`` correspond to D and N in the equation. ``t`` is the parameter to be used in the solutions. Examples ======== >>> from sympy.solvers.diophantine import diop_DN >>> diop_DN(13, -4) # Solves equation x**2 - 13*y**2 = -4 [(3, 1), (393, 109), (36, 10)] The output can be interpreted as follows: There are three fundamental solutions to the equation `x^2 - 13y^2 = -4` given by (3, 1), (393, 109) and (36, 10). Each tuple is in the form (x, y), i. e solution (3, 1) means that `x = 3` and `y = 1`. >>> diop_DN(986, 1) # Solves equation x**2 - 986*y**2 = 1 [(49299, 1570)] See Also ======== find_DN(), diop_bf_DN() References ========== .. [1] Solving the generalized Pell equation x**2 - D*y**2 = N, John P. Robertson, July 31, 2004, Pages 16 - 17. [online], Available: http://www.jpr2718.org/pell.pdf """ if D < 0: if N == 0: return [(S.Zero, S.Zero)] elif N < 0: return [] elif N > 0: d = divisors(square_factor(N)) sol = [] for divisor in d: sols = cornacchia(1, -D, N // divisor**2) if sols: for x, y in sols: sol.append((divisor*x, divisor*y)) return sol elif D == 0: if N < 0 or not isinstance(sqrt(N), Integer): return [] if N == 0: return [(S.Zero, t)] if isinstance(sqrt(N), Integer): return [(sqrt(N), t)] else: # D > 0 if isinstance(sqrt(D), Integer): r = sqrt(D) if N == 0: return [(r*t, t)] else: sol = [] for y in xrange(floor(sign(N)*(N - 1)/(2*r)) + 1): if isinstance(sqrt(D*y**2 + N), Integer): sol.append((sqrt(D*y**2 + N), y)) return sol else: if N == 0: return [(S.Zero, S.Zero)] elif abs(N) == 1: pqa = PQa(0, 1, D) a_0 = floor(sqrt(D)) l = 0 G = [] B = [] for i in pqa: a = i[2] G.append(i[5]) B.append(i[4]) if l != 0 and a == 2*a_0: break l = l + 1 if l % 2 == 1: if N == -1: x = G[l-1] y = B[l-1] else: count = l while count < 2*l - 1: i = next(pqa) G.append(i[5]) B.append(i[4]) count = count + 1 x = G[count] y = B[count] else: if N == 1: x = G[l-1] y = B[l-1] else: return [] return [(x, y)] else: fs = [] sol = [] div = divisors(N) for d in div: if divisible(N, d**2): fs.append(d) for f in fs: m = N // f**2 zs = sqrt_mod(D, abs(m), True) zs = [i for i in zs if i <= abs(m) // 2 ] if abs(m) != 2: zs = zs + [-i for i in zs] if S.Zero in zs: zs.remove(S.Zero) # Remove duplicate zero for z in zs: pqa = PQa(z, abs(m), D) l = 0 G = [] B = [] for i in pqa: a = i[2] G.append(i[5]) B.append(i[4]) if l != 0 and abs(i[1]) == 1: r = G[l-1] s = B[l-1] if r**2 - D*s**2 == m: sol.append((f*r, f*s)) elif diop_DN(D, -1) != []: a = diop_DN(D, -1) sol.append((f*(r*a[0][0] + a[0][1]*s*D), f*(r*a[0][1] + s*a[0][0]))) break l = l + 1 if l == length(z, abs(m), D): break return sol def cornacchia(a, b, m): """ Solves `ax^2 + by^2 = m` where `\gcd(a, b) = 1 = gcd(a, m)` and `a, b > 0`. Uses the algorithm due to Cornacchia. The method only finds primitive solutions, i.e. ones with `\gcd(x, y) = 1`. So this method can't be used to find the solutions of `x^2 + y^2 = 20` since the only solution to former is `(x,y) = (4, 2)` and it is not primitive. When ` a = b = 1`, only the solutions with `x \geq y` are found. For more details, see the References. Examples ======== >>> from sympy.solvers.diophantine import cornacchia >>> cornacchia(2, 3, 35) # equation 2x**2 + 3y**2 = 35 set([(2, 3), (4, 1)]) >>> cornacchia(1, 1, 25) # equation x**2 + y**2 = 25 set([(4, 3)]) References =========== .. [1] A. Nitaj, "L'algorithme de Cornacchia" .. [2] Solving the diophantine equation ax**2 + by**2 = m by Cornacchia's method, [online], Available: http://www.numbertheory.org/php/cornacchia.html """ sols = set([]) a1 = igcdex(a, m)[0] v = sqrt_mod(-b*a1, m, True) if v is None: return None if not isinstance(v, list): v = [v] for t in v: if t < m // 2: continue u, r = t, m while True: u, r = r, u % r if a*r**2 < m: break m1 = m - a*r**2 if m1 % b == 0: m1 = m1 // b if isinstance(sqrt(m1), Integer): s = sqrt(m1) sols.add((int(r), int(s))) return sols def PQa(P_0, Q_0, D): """ Returns useful information needed to solve the Pell equation. There are six sequences of integers defined related to the continued fraction representation of `\\frac{P + \sqrt{D}}{Q}`, namely {`P_{i}`}, {`Q_{i}`}, {`a_{i}`},{`A_{i}`}, {`B_{i}`}, {`G_{i}`}. ``PQa()`` Returns these values as a 6-tuple in the same order as mentioned above. Refer [1]_ for more detailed information. Usage ===== ``PQa(P_0, Q_0, D)``: ``P_0``, ``Q_0`` and ``D`` are integers corresponding to `P_{0}`, `Q_{0}` and `D` in the continued fraction `\\frac{P_{0} + \sqrt{D}}{Q_{0}}`. Also it's assumed that `P_{0}^2 == D mod(|Q_{0}|)` and `D` is square free. Examples ======== >>> from sympy.solvers.diophantine import PQa >>> pqa = PQa(13, 4, 5) # (13 + sqrt(5))/4 >>> next(pqa) # (P_0, Q_0, a_0, A_0, B_0, G_0) (13, 4, 3, 3, 1, -1) >>> next(pqa) # (P_1, Q_1, a_1, A_1, B_1, G_1) (-1, 1, 1, 4, 1, 3) References ========== .. [1] Solving the generalized Pell equation x^2 - Dy^2 = N, John P. Robertson, July 31, 2004, Pages 4 - 8. http://www.jpr2718.org/pell.pdf """ A_i_2 = B_i_1 = 0 A_i_1 = B_i_2 = 1 G_i_2 = -P_0 G_i_1 = Q_0 P_i = P_0 Q_i = Q_0 while(1): a_i = floor((P_i + sqrt(D))/Q_i) A_i = a_i*A_i_1 + A_i_2 B_i = a_i*B_i_1 + B_i_2 G_i = a_i*G_i_1 + G_i_2 yield P_i, Q_i, a_i, A_i, B_i, G_i A_i_1, A_i_2 = A_i, A_i_1 B_i_1, B_i_2 = B_i, B_i_1 G_i_1, G_i_2 = G_i, G_i_1 P_i = a_i*Q_i - P_i Q_i = (D - P_i**2)/Q_i def diop_bf_DN(D, N, t=symbols("t", Integer=True)): """ Uses brute force to solve the equation, `x^2 - Dy^2 = N`. Mainly concerned with the generalized Pell equation which is the case when `D > 0, D` is not a perfect square. For more information on the case refer [1]_. Let `(t, u)` be the minimal positive solution of the equation `x^2 - Dy^2 = 1`. Then this method requires `\sqrt{\\frac{\mid N \mid (t \pm 1)}{2D}}` to be small. Usage ===== ``diop_bf_DN(D, N, t)``: ``D`` and ``N`` are coefficients in `x^2 - Dy^2 = N` and ``t`` is the parameter to be used in the solutions. Details ======= ``D`` and ``N`` correspond to D and N in the equation. ``t`` is the parameter to be used in the solutions. Examples ======== >>> from sympy.solvers.diophantine import diop_bf_DN >>> diop_bf_DN(13, -4) [(3, 1), (-3, 1), (36, 10)] >>> diop_bf_DN(986, 1) [(49299, 1570)] See Also ======== diop_DN() References ========== .. [1] Solving the generalized Pell equation x**2 - D*y**2 = N, John P. Robertson, July 31, 2004, Page 15. http://www.jpr2718.org/pell.pdf """ sol = [] a = diop_DN(D, 1) u = a[0][0] v = a[0][1] if abs(N) == 1: return diop_DN(D, N) elif N > 1: L1 = 0 L2 = floor(sqrt(S(N*(u - 1))/(2*D))) + 1 elif N < -1: L1 = ceiling(sqrt(S(-N)/D)) L2 = floor(sqrt(S(-N*(u + 1))/(2*D))) + 1 else: if D < 0: return [(S.Zero, S.Zero)] elif D == 0: return [(S.Zero, t)] else: if isinstance(sqrt(D), Integer): return [(sqrt(D)*t, t), (-sqrt(D)*t, t)] else: return [(S.Zero, S.Zero)] for y in xrange(L1, L2): if isinstance(sqrt(N + D*y**2), Integer): x = sqrt(N + D*y**2) sol.append((x, y)) if not equivalent(x, y, -x, y, D, N): sol.append((-x, y)) return sol def equivalent(u, v, r, s, D, N): """ Returns True if two solutions `(u, v)` and `(r, s)` of `x^2 - Dy^2 = N` belongs to the same equivalence class and False otherwise. Two solutions `(u, v)` and `(r, s)` to the above equation fall to the same equivalence class iff both `(ur - Dvs)` and `(us - vr)` are divisible by `N`. See reference [1]_. No test is performed to test whether `(u, v)` and `(r, s)` are actually solutions to the equation. User should take care of this. Usage ===== ``equivalent(u, v, r, s, D, N)``: `(u, v)` and `(r, s)` are two solutions of the equation `x^2 - Dy^2 = N` and all parameters involved are integers. Examples ======== >>> from sympy.solvers.diophantine import equivalent >>> equivalent(18, 5, -18, -5, 13, -1) True >>> equivalent(3, 1, -18, 393, 109, -4) False References ========== .. [1] Solving the generalized Pell equation x**2 - D*y**2 = N, John P. Robertson, July 31, 2004, Page 12. http://www.jpr2718.org/pell.pdf """ return divisible(u*r - D*v*s, N) and divisible(u*s - v*r, N) def length(P, Q, D): """ Returns the (length of aperiodic part + length of periodic part) of continued fraction representation of `\\frac{P + \sqrt{D}}{Q}`. It is important to remember that this does NOT return the length of the periodic part but the addition of the legths of the two parts as mentioned above. Usage ===== ``length(P, Q, D)``: ``P``, ``Q`` and ``D`` are integers corresponding to the continued fraction `\\frac{P + \sqrt{D}}{Q}`. Details ======= ``P``, ``D`` and ``Q`` corresponds to P, D and Q in the continued fraction, `\\frac{P + \sqrt{D}}{Q}`. Examples ======== >>> from sympy.solvers.diophantine import length >>> length(-2 , 4, 5) # (-2 + sqrt(5))/4 3 >>> length(-5, 4, 17) # (-5 + sqrt(17))/4 4 """ x = P + sqrt(D) y = Q x = sympify(x) v, res = [], [] q = x/y if q < 0: v.append(q) res.append(floor(q)) q = q - floor(q) num, den = rad_rationalize(1, q) q = num / den while 1: v.append(q) a = int(q) res.append(a) if q == a: return len(res) num, den = rad_rationalize(1,(q - a)) q = num / den if q in v: return len(res) def transformation_to_DN(eq): """ This function transforms general quadratic, `ax^2 + bxy + cy^2 + dx + ey + f = 0` to more easy to deal with `X^2 - DY^2 = N` form. This is used to solve the general quadratic equation by transforming it to the latter form. Refer [1]_ for more detailed information on the transformation. This function returns a tuple (A, B) where A is a 2 X 2 matrix and B is a 2 X 1 matrix such that, Transpose([x y]) = A * Transpose([X Y]) + B Usage ===== ``transformation_to_DN(eq)``: where ``eq`` is the quadratic to be transformed. Examples ======== >>> from sympy.abc import x, y >>> from sympy.solvers.diophantine import transformation_to_DN >>> from sympy.solvers.diophantine import classify_diop >>> A, B = transformation_to_DN(x**2 - 3*x*y - y**2 - 2*y + 1) >>> A Matrix([ [1/26, 3/26], [ 0, 1/13]]) >>> B Matrix([ [-6/13], [-4/13]]) A, B returned are such that Transpose((x y)) = A * Transpose((X Y)) + B. Substituting these values for `x` and `y` and a bit of simplifying work will give an equation of the form `x^2 - Dy^2 = N`. >>> from sympy.abc import X, Y >>> from sympy import Matrix, simplify, Subs >>> u = (A*Matrix([X, Y]) + B)[0] # Transformation for x >>> u X/26 + 3*Y/26 - 6/13 >>> v = (A*Matrix([X, Y]) + B)[1] # Transformation for y >>> v Y/13 - 4/13 Next we will substitute these formulas for `x` and `y` and do ``simplify()``. >>> eq = simplify(Subs(x**2 - 3*x*y - y**2 - 2*y + 1, (x, y), (u, v)).doit()) >>> eq X**2/676 - Y**2/52 + 17/13 By multiplying the denominator appropriately, we can get a Pell equation in the standard form. >>> eq * 676 X**2 - 13*Y**2 + 884 If only the final equation is needed, ``find_DN()`` can be used. See Also ======== find_DN() References ========== .. [1] Solving the equation ax^2 + bxy + cy^2 + dx + ey + f = 0, John P.Robertson, May 8, 2003, Page 7 - 11. http://www.jpr2718.org/ax2p.pdf """ var, coeff, diop_type = classify_diop(eq) if diop_type == "binary_quadratic": return _transformation_to_DN(var, coeff) def _transformation_to_DN(var, coeff): x, y = var[:2] a = coeff[x**2] b = coeff[x*y] c = coeff[y**2] d = coeff[x] e = coeff[y] f = coeff[Integer(1)] g = igcd(a, igcd(b, igcd(c, igcd(d, igcd(e, f))))) a = a // g b = b // g c = c // g d = d // g e = e // g f = f // g X, Y = symbols("X, Y", integer=True) if b != Integer(0): B = (S(2*a)/b).p C = (S(2*a)/b).q A = (S(a)/B**2).p T = (S(a)/B**2).q # eq_1 = A*B*X**2 + B*(c*T - A*C**2)*Y**2 + d*T*X + (B*e*T - d*T*C)*Y + f*T*B coeff = {X**2: A*B, X*Y: 0, Y**2: B*(c*T - A*C**2), X: d*T, Y: B*e*T - d*T*C, Integer(1): f*T*B} A_0, B_0 = _transformation_to_DN([X, Y], coeff) return Matrix(2, 2, [S(1)/B, -S(C)/B, 0, 1])*A_0, Matrix(2, 2, [S(1)/B, -S(C)/B, 0, 1])*B_0 else: if d != Integer(0): B = (S(2*a)/d).p C = (S(2*a)/d).q A = (S(a)/B**2).p T = (S(a)/B**2).q # eq_2 = A*X**2 + c*T*Y**2 + e*T*Y + f*T - A*C**2 coeff = {X**2: A, X*Y: 0, Y**2: c*T, X: 0, Y: e*T, Integer(1): f*T - A*C**2} A_0, B_0 = _transformation_to_DN([X, Y], coeff) return Matrix(2, 2, [S(1)/B, 0, 0, 1])*A_0, Matrix(2, 2, [S(1)/B, 0, 0, 1])*B_0 + Matrix([-S(C)/B, 0]) else: if e != Integer(0): B = (S(2*c)/e).p C = (S(2*c)/e).q A = (S(c)/B**2).p T = (S(c)/B**2).q # eq_3 = a*T*X**2 + A*Y**2 + f*T - A*C**2 coeff = {X**2: a*T, X*Y: 0, Y**2: A, X: 0, Y: 0, Integer(1): f*T - A*C**2} A_0, B_0 = _transformation_to_DN([X, Y], coeff) return Matrix(2, 2, [1, 0, 0, S(1)/B])*A_0, Matrix(2, 2, [1, 0, 0, S(1)/B])*B_0 + Matrix([0, -S(C)/B]) else: # TODO: pre-simplification: Not necessary but may simplify # the equation. return Matrix(2, 2, [S(1)/a, 0, 0, 1]), Matrix([0, 0]) def find_DN(eq): """ This function returns a tuple, `(D, N)` of the simplified form, `x^2 - Dy^2 = N`, corresponding to the general quadratic, `ax^2 + bxy + cy^2 + dx + ey + f = 0`. Solving the general quadratic is then equivalent to solving the equation `X^2 - DY^2 = N` and transforming the solutions by using the transformation matrices returned by ``transformation_to_DN()``. Usage ===== ``find_DN(eq)``: where ``eq`` is the quadratic to be transformed. Examples ======== >>> from sympy.abc import x, y >>> from sympy.solvers.diophantine import find_DN >>> find_DN(x**2 - 3*x*y - y**2 - 2*y + 1) (13, -884) Interpretation of the output is that we get `X^2 -13Y^2 = -884` after transforming `x^2 - 3xy - y^2 - 2y + 1` using the transformation returned by ``transformation_to_DN()``. See Also ======== transformation_to_DN() References ========== .. [1] Solving the equation ax^2 + bxy + cy^2 + dx + ey + f = 0, John P.Robertson, May 8, 2003, Page 7 - 11. http://www.jpr2718.org/ax2p.pdf """ var, coeff, diop_type = classify_diop(eq) if diop_type == "binary_quadratic": return _find_DN(var, coeff) def _find_DN(var, coeff): x, y = var[:2] X, Y = symbols("X, Y", integer=True) A , B = _transformation_to_DN(var, coeff) u = (A*Matrix([X, Y]) + B)[0] v = (A*Matrix([X, Y]) + B)[1] eq = x**2*coeff[x**2] + x*y*coeff[x*y] + y**2*coeff[y**2] + x*coeff[x] + y*coeff[y] + coeff[Integer(1)] simplified = _mexpand(Subs(eq, (x, y), (u, v)).doit()) coeff = dict([reversed(t.as_independent(*[X, Y])) for t in simplified.args]) for term in [X**2, Y**2, Integer(1)]: if term not in coeff.keys(): coeff[term] = Integer(0) return -coeff[Y**2]/coeff[X**2], -coeff[Integer(1)]/coeff[X**2] def check_param(x, y, a, t): """ Check if there is a number modulo ``a`` such that ``x`` and ``y`` are both integers. If exist, then find a parametric representation for ``x`` and ``y``. Here ``x`` and ``y`` are functions of ``t``. """ k, m, n = symbols("k, m, n", Integer=True) p = Wild("p", exclude=[k]) q = Wild("q", exclude=[k]) ok = False for i in xrange(a): z_x = _mexpand(Subs(x, t, a*k + i).doit()).match(p*k + q) z_y = _mexpand(Subs(y, t, a*k + i).doit()).match(p*k + q) if (isinstance(z_x[p], Integer) and isinstance(z_x[q], Integer) and isinstance(z_y[p], Integer) and isinstance(z_y[q], Integer)): ok = True break if ok == True: x_param = x.match(p*t + q) y_param = y.match(p*t + q) if x_param[p] == 0 or y_param[p] == 0: if x_param[p] == 0: l1, junk = Poly(y).clear_denoms() else: l1 = 1 if y_param[p] == 0: l2, junk = Poly(x).clear_denoms() else: l2 = 1 return x*ilcm(l1, l2), y*ilcm(l1, l2) eq = S(m - x_param[q])/x_param[p] - S(n - y_param[q])/y_param[p] lcm_denom, junk = Poly(eq).clear_denoms() eq = eq * lcm_denom return diop_solve(eq, t)[0], diop_solve(eq, t)[1] else: return (None, None) def diop_ternary_quadratic(eq): """ Solves the general quadratic ternary form, `ax^2 + by^2 + cz^2 + fxy + gyz + hxz = 0`. Returns a tuple `(x, y, z)` which is a base solution for the above equation. If there are no solutions, `(None, None, None)` is returned. Usage ===== ``diop_ternary_quadratic(eq)``: Return a tuple containing an basic solution to ``eq``. Details ======= ``eq`` should be an homogeneous expression of degree two in three variables and it is assumed to be zero. Examples ======== >>> from sympy.abc import x, y, z >>> from sympy.solvers.diophantine import diop_ternary_quadratic >>> diop_ternary_quadratic(x**2 + 3*y**2 - z**2) (1, 0, 1) >>> diop_ternary_quadratic(4*x**2 + 5*y**2 - z**2) (1, 0, 2) >>> diop_ternary_quadratic(45*x**2 - 7*y**2 - 8*x*y - z**2) (28, 45, 105) >>> diop_ternary_quadratic(x**2 - 49*y**2 - z**2 + 13*z*y -8*x*y) (9, 1, 5) """ var, coeff, diop_type = classify_diop(eq) if diop_type == "homogeneous_ternary_quadratic": return _diop_ternary_quadratic(var, coeff) def _diop_ternary_quadratic(_var, coeff): x, y, z = _var[:3] var = [x]*3 var[0], var[1], var[2] = _var[0], _var[1], _var[2] # Equations of the form B*x*y + C*z*x + E*y*z = 0 and At least two of the # coefficients A, B, C are non-zero. # There are infinitely many solutions for the equation. # Ex: (0, 0, t), (0, t, 0), (t, 0, 0) # Equation can be re-written as y*(B*x + E*z) = -C*x*z and we can find rather # unobviuos solutions. Set y = -C and B*x + E*z = x*z. The latter can be solved by # using methods for binary quadratic diophantine equations. Let's select the # solution which minimizes |x| + |z| if coeff[x**2] == 0 and coeff[y**2] == 0 and coeff[z**2] == 0: if coeff[x*z] != 0: sols = diophantine(coeff[x*y]*x + coeff[y*z]*z - x*z) s = sols.pop() min_sum = abs(s[0]) + abs(s[1]) for r in sols: if abs(r[0]) + abs(r[1]) < min_sum: s = r min_sum = abs(s[0]) + abs(s[1]) x_0, y_0, z_0 = s[0], -coeff[x*z], s[1] else: var[0], var[1] = _var[1], _var[0] y_0, x_0, z_0 = _diop_ternary_quadratic(var, coeff) return simplified(x_0, y_0, z_0) if coeff[x**2] == 0: # If the coefficient of x is zero change the variables if coeff[y**2] == 0: var[0], var[2] = _var[2], _var[0] z_0, y_0, x_0 = _diop_ternary_quadratic(var, coeff) else: var[0], var[1] = _var[1], _var[0] y_0, x_0, z_0 = _diop_ternary_quadratic(var, coeff) else: if coeff[x*y] != 0 or coeff[x*z] != 0: # Apply the transformation x --> X - (B*y + C*z)/(2*A) A = coeff[x**2] B = coeff[x*y] C = coeff[x*z] D = coeff[y**2] E = coeff[y*z] F = coeff[z**2] _coeff = dict() _coeff[x**2] = 4*A**2 _coeff[y**2] = 4*A*D - B**2 _coeff[z**2] = 4*A*F - C**2 _coeff[y*z] = 4*A*E - 2*B*C _coeff[x*y] = 0 _coeff[x*z] = 0 X_0, y_0, z_0 = _diop_ternary_quadratic(var, _coeff) if X_0 == None: return (None, None, None) l = (S(B*y_0 + C*z_0)/(2*A)).q x_0, y_0, z_0 = X_0*l - (S(B*y_0 + C*z_0)/(2*A)).p, y_0*l, z_0*l elif coeff[z*y] != 0: if coeff[y**2] == 0: if coeff[z**2] == 0: # Equations of the form A*x**2 + E*yz = 0. A = coeff[x**2] E = coeff[y*z] b = (S(-E)/A).p a = (S(-E)/A).q x_0, y_0, z_0 = b, a, b else: # Ax**2 + E*y*z + F*z**2 = 0 var[0], var[2] = _var[2], _var[0] z_0, y_0, x_0 = _diop_ternary_quadratic(var, coeff) else: # A*x**2 + D*y**2 + E*y*z + F*z**2 = 0, C may be zero var[0], var[1] = _var[1], _var[0] y_0, x_0, z_0 = _diop_ternary_quadratic(var, coeff) else: # Ax**2 + D*y**2 + F*z**2 = 0, C may be zero x_0, y_0, z_0 = _diop_ternary_quadratic_normal(var, coeff) return simplified(x_0, y_0, z_0) def transformation_to_normal(eq): """ Returns the transformation Matrix from general ternary quadratic equation `eq` to normal form. General form of the ternary quadratic equation is `ax^2 + by^2 cz^2 + dxy + eyz + fxz`. This function returns a 3X3 transformation Matrix which transforms the former equation to the form `ax^2 + by^2 + cz^2 = 0`. This is not used in solving ternary quadratics. Only implemented for the sake of completeness. """ var, coeff, diop_type = classify_diop(eq) if diop_type == "homogeneous_ternary_quadratic": return _transformation_to_normal(var, coeff) def _transformation_to_normal(var, coeff): _var = [var[0]]*3 _var[1], _var[2] = var[1], var[2] x, y, z = var[:3] if coeff[x**2] == 0: # If the coefficient of x is zero change the variables if coeff[y**2] == 0: _var[0], _var[2] = var[2], var[0] T = _transformation_to_normal(_var, coeff) T.row_swap(0, 2) T.col_swap(0, 2) return T else: _var[0], _var[1] = var[1], var[0] T = _transformation_to_normal(_var, coeff) T.row_swap(0, 1) T.col_swap(0, 1) return T else: # Apply the transformation x --> X - (B*Y + C*Z)/(2*A) if coeff[x*y] != 0 or coeff[x*z] != 0: A = coeff[x**2] B = coeff[x*y] C = coeff[x*z] D = coeff[y**2] E = coeff[y*z] F = coeff[z**2] _coeff = dict() _coeff[x**2] = 4*A**2 _coeff[y**2] = 4*A*D - B**2 _coeff[z**2] = 4*A*F - C**2 _coeff[y*z] = 4*A*E - 2*B*C _coeff[x*y] = 0 _coeff[x*z] = 0 T_0 = _transformation_to_normal(_var, _coeff) return Matrix(3, 3, [1, S(-B)/(2*A), S(-C)/(2*A), 0, 1, 0, 0, 0, 1]) * T_0 elif coeff[y*z] != 0: if coeff[y**2] == 0: if coeff[z**2] == 0: # Equations of the form A*x**2 + E*yz = 0. # Apply transformation y -> Y + Z ans z -> Y - Z return Matrix(3, 3, [1, 0, 0, 0, 1, 1, 0, 1, -1]) else: # Ax**2 + E*y*z + F*z**2 = 0 _var[0], _var[2] = var[2], var[0] T = _transformtion_to_normal(_var, coeff) T.row_swap(0, 2) T.col_swap(0, 2) return T else: # A*x**2 + D*y**2 + E*y*z + F*z**2 = 0, F may be zero _var[0], _var[1] = var[1], var[0] T = _transformation_to_normal(_var, coeff) T.row_swap(0, 1) T.col_swap(0, 1) return T else: return Matrix(3, 3, [1, 0, 0, 0, 1, 0, 0, 0, 1]) def simplified(x, y, z): """ Simplify the solution `(x, y, z)`. """ if x == None or y == None or z == None: return (x, y, z) g = igcd(x, igcd(y, z)) return x // g, y // g, z // g def parametrize_ternary_quadratic(eq): """ Returns the parametrized general solution for the ternary quadratic equation ``eq`` which has the form `ax^2 + by^2 + cz^2 + fxy + gyz + hxz = 0`. Examples ======== >>> from sympy.abc import x, y, z >>> from sympy.solvers.diophantine import parametrize_ternary_quadratic >>> parametrize_ternary_quadratic(x**2 + y**2 - z**2) (2*p*q, p**2 - q**2, p**2 + q**2) Here `p` and `q` are two co-prime integers. >>> parametrize_ternary_quadratic(3*x**2 + 2*y**2 - z**2 - 2*x*y + 5*y*z - 7*y*z) (2*p**2 - 2*p*q - q**2, 2*p**2 + 2*p*q - q**2, 2*p**2 - 2*p*q + 3*q**2) >>> parametrize_ternary_quadratic(124*x**2 - 30*y**2 - 7729*z**2) (-1410*p**2 - 363263*q**2, 2700*p**2 + 30916*p*q - 695610*q**2, -60*p**2 + 5400*p*q + 15458*q**2) References ========== .. [1] The algorithmic resolution of Diophantine equations, Nigel P. Smart, London Mathematical Society Student Texts 41, Cambridge University Press, Cambridge, 1998. """ var, coeff, diop_type = classify_diop(eq) if diop_type == "homogeneous_ternary_quadratic": x_0, y_0, z_0 = _diop_ternary_quadratic(var, coeff) return _parametrize_ternary_quadratic((x_0, y_0, z_0), var, coeff) def _parametrize_ternary_quadratic(solution, _var, coeff): x, y, z = _var[:3] x_0, y_0, z_0 = solution[:3] v = [x]*3 v[0], v[1], v[2] = _var[0], _var[1], _var[2] if x_0 == None: return (None, None, None) if x_0 == 0: if y_0 == 0: v[0], v[2] = v[2], v[0] z_p, y_p, x_p = _parametrize_ternary_quadratic((z_0, y_0, x_0), v, coeff) return x_p, y_p, z_p else: v[0], v[1] = v[1], v[0] y_p, x_p, z_p = _parametrize_ternary_quadratic((y_0, x_0, z_0), v, coeff) return x_p, y_p, z_p x, y, z = v[:3] r, p, q = symbols("r, p, q", Integer=True) eq = x**2*coeff[x**2] + y**2*coeff[y**2] + z**2*coeff[z**2] + x*y*coeff[x*y] + y*z*coeff[y*z] + z*x*coeff[z*x] eq_1 = Subs(eq, (x, y, z), (r*x_0, r*y_0 + p, r*z_0 + q)).doit() eq_1 = _mexpand(eq_1) A, B = eq_1.as_independent(r, as_Add=True) x = A*x_0 y = (A*y_0 - _mexpand(B/r*p)) z = (A*z_0 - _mexpand(B/r*q)) return x, y, z def diop_ternary_quadratic_normal(eq): """ Solves the quadratic ternary diophantine equation, `ax^2 + by^2 + cz^2 = 0`. Here the coefficients `a`, `b`, and `c` should be non zero. Otherwise the equation will be a quadratic binary or univariable equation. If solvable, returns a tuple `(x, y, z)` that satisifes the given equation. If the equation does not have integer solutions, `(None, None, None)` is returned. Usage ===== ``diop_ternary_quadratic_normal(eq)``: where ``eq`` is an equation of the form `ax^2 + by^2 + cz^2 = 0`. Examples ======== >>> from sympy.abc import x, y, z >>> from sympy.solvers.diophantine import diop_ternary_quadratic_normal >>> diop_ternary_quadratic_normal(x**2 + 3*y**2 - z**2) (1, 0, 1) >>> diop_ternary_quadratic_normal(4*x**2 + 5*y**2 - z**2) (1, 0, 2) >>> diop_ternary_quadratic_normal(34*x**2 - 3*y**2 - 301*z**2) (4, 9, 1) """ var, coeff, diop_type = classify_diop(eq) if diop_type == "homogeneous_ternary_quadratic": return _diop_ternary_quadratic_normal(var, coeff) def _diop_ternary_quadratic_normal(var, coeff): x, y, z = var[:3] a = coeff[x**2] b = coeff[y**2] c = coeff[z**2] if a*b*c == 0: raise ValueError("Try factoring out you equation or using diophantine()") g = igcd(a, igcd(b, c)) a = a // g b = b // g c = c // g a_0 = square_factor(a) b_0 = square_factor(b) c_0 = square_factor(c) a_1 = a // a_0**2 b_1 = b // b_0**2 c_1 = c // c_0**2 a_2, b_2, c_2 = pairwise_prime(a_1, b_1, c_1) A = -a_2*c_2 B = -b_2*c_2 # If following two conditions are satisified then there are no solutions if A < 0 and B < 0: return (None, None, None) if (sqrt_mod(-b_2*c_2, a_2) == None or sqrt_mod(-c_2*a_2, b_2) == None or sqrt_mod(-a_2*b_2, c_2) == None): return (None, None, None) z_0, x_0, y_0 = descent(A, B) if divisible(z_0, c_2) == True: z_0 = z_0 // abs(c_2) else: x_0 = x_0*(S(z_0)/c_2).q y_0 = y_0*(S(z_0)/c_2).q z_0 = (S(z_0)/c_2).p x_0, y_0, z_0 = simplified(x_0, y_0, z_0) # Holzer reduction if sign(a) == sign(b): x_0, y_0, z_0 = holzer(x_0, y_0, z_0, abs(a_2), abs(b_2), abs(c_2)) elif sign(a) == sign(c): x_0, z_0, y_0 = holzer(x_0, z_0, y_0, abs(a_2), abs(c_2), abs(b_2)) else: y_0, z_0, x_0 = holzer(y_0, z_0, x_0, abs(b_2), abs(c_2), abs(a_2)) x_0 = reconstruct(b_1, c_1, x_0) y_0 = reconstruct(a_1, c_1, y_0) z_0 = reconstruct(a_1, b_1, z_0) l = ilcm(a_0, ilcm(b_0, c_0)) x_0 = abs(x_0*l//a_0) y_0 = abs(y_0*l//b_0) z_0 = abs(z_0*l//c_0) return simplified(x_0, y_0, z_0) def square_factor(a): """ Returns an integer `c` s.t. `a = c^2k, \ c,k \in Z`. Here `k` is square free. Examples ======== >>> from sympy.solvers.diophantine import square_factor >>> square_factor(24) 2 >>> square_factor(36) 6 >>> square_factor(1) 1 """ f = factorint(abs(a)) c = 1 for p, e in f.items(): c = c * p**(e//2) return c def pairwise_prime(a, b, c): """ Transform `ax^2 + by^2 + cz^2 = 0` into an equivalent equation `a'x^2 + b'y^2 + c'z^2 = 0` where `a', b', c'` are pairwise relatively prime. Returns a tuple containing `a', b', c'`. `\gcd(a, b, c)` should equal `1` for this to work. The solutions for `ax^2 + by^2 + cz^2 = 0` can be recovered from the solutions of `a'x^2 + b'y^2 + c'z^2 = 0`. Examples ======== >>> from sympy.solvers.diophantine import pairwise_prime >>> pairwise_prime(6, 15, 10) (5, 2, 3) See Also ======== make_prime(), reocnstruct() """ a, b, c = make_prime(a, b, c) b, c, a = make_prime(b, c, a) c, a, b = make_prime(c, a, b) return a, b, c def make_prime(a, b, c): """ Transform the equation `ax^2 + by^2 + cz^2 = 0` to an equivalent equation `a'x^2 + b'y^2 + c'z^2 = 0` with `\gcd(a', b') = 1`. Returns a tuple `(a', b', c')` which satisfies above conditions. Note that in the returned tuple `\gcd(a', c')` and `\gcd(b', c')` can take any value. Examples ======== >>> from sympy.solvers.diophantine import make_prime >>> make_prime(4, 2, 7) (2, 1, 14) See Also ======== pairwaise_prime(), reconstruct() """ g = igcd(a, b) if g != 1: f = factorint(g) for p, e in f.items(): a = a // p**e b = b // p**e if e % 2 == 1: c = p*c return a, b, c def reconstruct(a, b, z): """ Reconstruct the `z` value of an equivalent solution of `ax^2 + by^2 + cz^2` from the `z` value of a solution of a transformed version of the above equation. """ g = igcd(a, b) if g != 1: f = factorint(g) for p, e in f.items(): if e %2 == 0: z = z*p**(e//2) else: z = z*p**((e//2)+1) return z def ldescent(A, B): """ Uses Lagrange's method to find a non trivial solution to `w^2 = Ax^2 + By^2`. Here, `A \\neq 0` and `B \\neq 0` and `A` and `B` are square free. Output a tuple `(w_0, x_0, y_0)` which is a solution to the above equation. Examples ======== >>> from sympy.solvers.diophantine import ldescent >>> ldescent(1, 1) # w^2 = x^2 + y^2 (1, 1, 0) >>> ldescent(4, -7) # w^2 = 4x^2 - 7y^2 (2, -1, 0) This means that `x = -1, y = 0` and `w = 2` is a solution to the equation `w^2 = 4x^2 - 7y^2` >>> ldescent(5, -1) # w^2 = 5x^2 - y^2 (2, 1, -1) References ========== .. [1] The algorithmic resolution of Diophantine equations, Nigel P. Smart, London Mathematical Society Student Texts 41, Cambridge University Press, Cambridge, 1998. .. [2] Efficient Solution of Rational Conices, J. E. Cremona and D. Rusin, Mathematics of Computation, Volume 00, Number 0. """ if abs(A) > abs(B): w, y, x = ldescent(B, A) return w, x, y if A == 1: return (S.One, S.One, 0) if B == 1: return (S.One, 0, S.One) r = sqrt_mod(A, B) Q = (r**2 - A) // B if Q == 0: B_0 = 1 d = 0 else: div = divisors(Q) B_0 = None for i in div: if isinstance(sqrt(abs(Q) // i), Integer): B_0, d = sign(Q)*i, sqrt(abs(Q) // i) break if B_0 != None: W, X, Y = ldescent(A, B_0) return simplified((-A*X + r*W), (r*X - W), Y*(B_0*d)) # In this module Descent will always be called with inputs which have solutions. def descent(A, B): """ Lagrange's `descent()` with lattice-reduction to find solutions to `x^2 = Ay^2 + Bz^2`. Here `A` and `B` should be square free and pairwise prime. Always should be called with suitable ``A`` and ``B`` so that the above equation has solutions. This is more faster than the normal Lagrange's descent algorithm because the gaussian reduction is used. Examples ======== >>> from sympy.solvers.diophantine import descent >>> descent(3, 1) # x**2 = 3*y**2 + z**2 (1, 0, 1) `(x, y, z) = (1, 0, 1)` is a solution to the above equation. >>> descent(41, -113) (-16, -3, 1) References ========== .. [1] Efficient Solution of Rational Conices, J. E. Cremona and D. Rusin, Mathematics of Computation, Volume 00, Number 0. """ if abs(A) > abs(B): x, y, z = descent(B, A) return x, z, y if B == 1: return (1, 0, 1) if A == 1: return (1, 1, 0) if B == -1: return (None, None, None) if B == -A: return (0, 1, 1) if B == A: x, z, y = descent(-1, A) return (A*y, z, x) w = sqrt_mod(A, B) x_0, z_0 = gaussian_reduce(w, A, B) t = (x_0**2 - A*z_0**2) // B t_2 = square_factor(t) t_1 = t // t_2**2 x_1, z_1, y_1 = descent(A, t_1) return simplified(x_0*x_1 + A*z_0*z_1, z_0*x_1 + x_0*z_1, t_1*t_2*y_1) def gaussian_reduce(w, a, b): """ Returns a reduced solution `(x, z)` to the congruence `X^2 - aZ^2 \equiv 0 \ (mod \ b)` so that `x^2 + |a|z^2` is minimal. Details ======= Here ``w`` is a solution of the congruence `x^2 \equiv a \ (mod \ b)` References ========== .. [1] Gaussian lattice Reduction [online]. Available: http://home.ie.cuhk.edu.hk/~wkshum/wordpress/?p=404 .. [2] Efficient Solution of Rational Conices, J. E. Cremona and D. Rusin, Mathematics of Computation, Volume 00, Number 0. """ u = (0, 1) v = (1, 0) if dot(u, v, w, a, b) < 0: v = (-v[0], -v[1]) if norm(u, w, a, b) < norm(v, w, a, b): u, v = v, u while norm(u, w, a, b) > norm(v, w, a, b): k = dot(u, v, w, a, b) // dot(v, v, w, a, b) u, v = v, (u[0]- k*v[0], u[1]- k*v[1]) u, v = v, u if dot(u, v, w, a, b) < dot(v, v, w, a, b)/2 or norm((u[0]-v[0], u[1]-v[1]), w, a, b) > norm(v, w, a, b): c = v else: c = (u[0] - v[0], u[1] - v[1]) return c[0]*w + b*c[1], c[0] def dot(u, v, w, a, b): """ Returns a special dot product of the vectors `u = (u_{1}, u_{2})` and `v = (v_{1}, v_{2})` which is defined in order to reduce solution of the congruence equation `X^2 - aZ^2 \equiv 0 \ (mod \ b)`. """ u_1, u_2 = u[:2] v_1, v_2 = v[:2] return (w*u_1 + b*u_2)*(w*v_1 + b*v_2) + abs(a)*u_1*v_1 def norm(u, w, a, b): """ Returns the norm of the vector `u = (u_{1}, u_{2})` under the dot product defined by `u \cdot v = (wu_{1} + bu_{2})(w*v_{1} + bv_{2}) + |a|*u_{1}*v_{1}` where `u = (u_{1}, u_{2})` and `v = (v_{1}, v_{2})`. """ u_1, u_2 = u[:2] return sqrt(dot((u_1, u_2), (u_1, u_2), w, a, b)) def holzer(x_0, y_0, z_0, a, b, c): """ Simplify the solution `(x_{0}, y_{0}, z_{0})` of the equation `ax^2 + by^2 = cz^2` with `a, b, c > 0` and `z_{0}^2 \geq \mid ab \mid` to a new reduced solution `(x, y, z)` such that `z^2 \leq \mid ab \mid`. """ while z_0 > sqrt(a*b): if c % 2 == 0: k = c // 2 u_0, v_0 = base_solution_linear(k, y_0, -x_0) else: k = 2*c u_0, v_0 = base_solution_linear(c, y_0, -x_0) w = -(a*u_0*x_0 + b*v_0*y_0) // (c*z_0) if c % 2 == 1: if w % 2 != (a*u_0 + b*v_0) % 2: w = w + 1 x = (x_0*(a*u_0**2 + b*v_0**2 + c*w**2) - 2*u_0*(a*u_0*x_0 + b*v_0*y_0 + c*w*z_0)) // k y = (y_0*(a*u_0**2 + b*v_0**2 + c*w**2) - 2*v_0*(a*u_0*x_0 + b*v_0*y_0 + c*w*z_0)) // k z = (z_0*(a*u_0**2 + b*v_0**2 + c*w**2) - 2*w*(a*u_0*x_0 + b*v_0*y_0 + c*w*z_0)) // k x_0, y_0, z_0 = x, y, z return x_0, y_0, z_0 def diop_general_pythagorean(eq, param=symbols("m", Integer=True)): """ Solves the general pythagorean equation, `a_{1}^2x_{1}^2 + a_{2}^2x_{2}^2 + . . . + a_{n}^2x_{n}^2 - a_{n + 1}^2x_{n + 1}^2 = 0`. Returns a tuple which contains a parametrized solution to the equation, sorted in the same order as the input variables. Usage ===== ``diop_general_pythagorean(eq, param)``: where ``eq`` is a general pythagorean equation which is assumed to be zero and ``param`` is the base parameter used to construct other parameters by subscripting. Examples ======== >>> from sympy.solvers.diophantine import diop_general_pythagorean >>> from sympy.abc import a, b, c, d, e >>> diop_general_pythagorean(a**2 + b**2 + c**2 - d**2) (m1**2 + m2**2 - m3**2, 2*m1*m3, 2*m2*m3, m1**2 + m2**2 + m3**2) >>> diop_general_pythagorean(9*a**2 - 4*b**2 + 16*c**2 + 25*d**2 + e**2) (10*m1**2 + 10*m2**2 + 10*m3**2 - 10*m4**2, 15*m1**2 + 15*m2**2 + 15*m3**2 + 15*m4**2, 15*m1*m4, 12*m2*m4, 60*m3*m4) """ var, coeff, diop_type = classify_diop(eq) if diop_type == "general_pythagorean": return _diop_general_pythagorean(var, coeff, param) def _diop_general_pythagorean(var, coeff, t): if sign(coeff[var[0]**2]) + sign(coeff[var[1]**2]) + sign(coeff[var[2]**2]) < 0: for key in coeff.keys(): coeff[key] = coeff[key] * -1 n = len(var) index = 0 for i, v in enumerate(var): if sign(coeff[v**2]) == -1: index = i m = symbols(str(t) + "1:" + str(n), Integer=True) l = [] ith = 0 for m_i in m: ith = ith + m_i**2 l.append(ith - 2*m[n - 2]**2) for i in xrange(n - 2): l.append(2*m[i]*m[n-2]) sol = l[:index] + [ith] + l[index:] lcm = 1 for i, v in enumerate(var): if i == index or (index > 0 and i == 0) or (index == 0 and i == 1): lcm = ilcm(lcm, sqrt(abs(coeff[v**2]))) else: lcm = ilcm(lcm, sqrt(coeff[v**2]) if sqrt(coeff[v**2]) % 2 else sqrt(coeff[v**2]) // 2) for i, v in enumerate(var): sol[i] = (lcm*sol[i]) / sqrt(abs(coeff[v**2])) return tuple(sol) def diop_general_sum_of_squares(eq, limit=1): """ Solves the equation `x_{1}^2 + x_{2}^2 + . . . + x_{n}^2 - k = 0`. Returns at most ``limit`` number of solutions. Currently there is no way to set ``limit`` using higher level API's like ``diophantine()`` or ``diop_solve()`` but that will be fixed soon. Usage ===== ``general_sum_of_squares(eq, limit)`` : Here ``eq`` is an expression which is assumed to be zero. Also, ``eq`` should be in the form, `x_{1}^2 + x_{2}^2 + . . . + x_{n}^2 - k = 0`. At most ``limit`` number of solutions are returned. Details ======= When `n = 3` if `k = 4^a(8m + 7)` for some `a, m \in Z` then there will be no solutions. Refer [1]_ for more details. Examples ======== >>> from sympy.solvers.diophantine import diop_general_sum_of_squares >>> from sympy.abc import a, b, c, d, e, f >>> diop_general_sum_of_squares(a**2 + b**2 + c**2 + d**2 + e**2 - 2345) set([(0, 48, 5, 4, 0)]) Reference ========= .. [1] Representing an Integer as a sum of three squares, [online], Available: http://www.proofwiki.org/wiki/Integer_as_Sum_of_Three_Squares """ var, coeff, diop_type = classify_diop(eq) if diop_type == "general_sum_of_squares": return _diop_general_sum_of_squares(var, coeff, limit) def _diop_general_sum_of_squares(var, coeff, limit=1): n = len(var) k = -int(coeff[Integer(1)]) s = set([]) if k < 0: return set([]) if n == 3: s.add(sum_of_three_squares(k)) elif n == 4: s.add(sum_of_four_squares(k)) else: m = n // 4 f = partition(k, m, True) for j in xrange(limit): soln = [] try: l = next(f) except StopIteration: break for n_i in l: a, b, c, d = sum_of_four_squares(n_i) soln = soln + [a, b, c, d] soln = soln + [0] * (n % 4) s.add(tuple(soln)) return s ## Functions below this comment can be more suitably grouped under an Additive number theory module ## rather than the Diophantine equation module. def partition(n, k=None, zeros=False): """ Returns a generator that can be used to generate partitions of an integer `n`. A partition of `n` is a set of positive integers which add upto `n`. For example, partitions of 3 are 3 , 1 + 2, 1 + 1+ 1. A partition is returned as a tuple. If ``k`` equals None, then all possible partitions are returned irrespective of their size, otherwise only the partitions of size ``k`` are returned. If there are no partions of `n` with size `k` then an empty tuple is returned. If the ``zero`` parameter is set to True then a suitable number of zeros are added at the end of every partition of size less than ``k``. ``zero`` parameter is considered only if ``k`` is not None. When the partitions are over, the last `next()` call throws the ``StopIteration`` exception, so this function should always be used inside a try - except block. Details ======= ``partition(n, k)``: Here ``n`` is a positive integer and ``k`` is the size of the partition which is also positive integer. Examples ======== >>> from sympy.solvers.diophantine import partition >>> f = partition(5) >>> next(f) (1, 1, 1, 1, 1) >>> next(f) (1, 1, 1, 2) >>> g = partition(5, 3) >>> next(g) (3, 1, 1) >>> next(g) (2, 2, 1) Reference ========= .. [1] Generating Integer Partitions, [online], Available: http://homepages.ed.ac.uk/jkellehe/partitions.php """ if n < 1: yield tuple() if k is not None: if k < 1: yield tuple() elif k > n: if zeros: for i in xrange(1, n): for t in partition(n, i): yield (t,) + (0,) * (k - i) else: yield tuple() else: a = [1 for i in xrange(k)] a[0] = n - k + 1 yield tuple(a) i = 1 while a[0] >= n // k + 1: j = 0 while j < i and j + 1 < k: a[j] = a[j] - 1 a[j + 1] = a[j + 1] + 1 yield tuple(a) j = j + 1 i = i + 1 if zeros: for m in xrange(1, k): for a in partition(n, m): yield tuple(a) + (0,) * (k - m) else: a = [0 for i in xrange(n + 1)] l = 1 y = n - 1 while l != 0: x = a[l - 1] + 1 l -= 1 while 2*x <= y: a[l] = x y -= x l += 1 m = l + 1 while x <= y: a[l] = x a[m] = y yield tuple(a[:l + 2]) x += 1 y -= 1 a[l] = x + y y = x + y - 1 yield tuple(a[:l + 1]) def prime_as_sum_of_two_squares(p): """ Represent a prime `p` which is congruent to 1 mod 4, as a sum of two squares. Examples ======== >>> from sympy.solvers.diophantine import prime_as_sum_of_two_squares >>> prime_as_sum_of_two_squares(5) (2, 1) Reference ========= .. [1] Representing a number as a sum of four squares, [online], Available: http://www.schorn.ch/howto.html """ if p % 8 == 5: b = 2 else: b = 3 while pow(b, (p - 1) // 2, p) == 1: b = nextprime(b) b = pow(b, (p - 1) // 4, p) a = p while b**2 > p: a, b = b, a % b return (b, a % b) def sum_of_three_squares(n): """ Returns a 3-tuple `(a, b, c)` such that `a^2 + b^2 + c^2 = n` and `a, b, c \geq 0`. Returns (None, None, None) if `n = 4^a(8m + 7)` for some `a, m \in Z`. See [1]_ for more details. Usage ===== ``sum_of_three_squares(n)``: Here ``n`` is a non-negative integer. Examples ======== >>> from sympy.solvers.diophantine import sum_of_three_squares >>> sum_of_three_squares(44542) (207, 37, 18) References ========== .. [1] Representing a number as a sum of three squares, [online], Available: http://www.schorn.ch/howto.html """ special = {1:(1, 0, 0), 2:(1, 1, 0), 3:(1, 1, 1), 10: (1, 3, 0), 34: (3, 3, 4), 58:(3, 7, 0), 85:(6, 7, 0), 130:(3, 11, 0), 214:(3, 6, 13), 226:(8, 9, 9), 370:(8, 9, 15), 526:(6, 7, 21), 706:(15, 15, 16), 730:(1, 27, 0), 1414:(6, 17, 33), 1906:(13, 21, 36), 2986: (21, 32, 39), 9634: (56, 57, 57)} v = 0 if n == 0: return (0, 0, 0) while n % 4 == 0: v = v + 1 n = n // 4 if n % 8 == 7: return (None, None, None) if n in special.keys(): x, y, z = special[n] return (2**v*x, 2**v*y, 2**v*z) l = int(sqrt(n)) if n == l**2: return (2**v*l, 0, 0) x = None if n % 8 == 3: l = l if l % 2 else l - 1 for i in xrange(l, -1, -2): if isprime((n - i**2) // 2): x = i break y, z = prime_as_sum_of_two_squares((n - x**2) // 2) return (2**v*x, 2**v*(y + z), 2**v*abs(y - z)) if n % 8 == 2 or n % 8 == 6: l = l if l % 2 else l - 1 else: l = l - 1 if l % 2 else l for i in xrange(l, -1, -2): if isprime(n - i**2): x = i break y, z = prime_as_sum_of_two_squares(n - x**2) return (2**v*x, 2**v*y, 2**v*z) def sum_of_four_squares(n): """ Returns a 4-tuple `(a, b, c, d)` such that `a^2 + b^2 + c^2 + d^2 = n`. Here `a, b, c, d \geq 0`. Usage ===== ``sum_of_four_squares(n)``: Here ``n`` is a non-negative integer. Examples ======== >>> from sympy.solvers.diophantine import sum_of_four_squares >>> sum_of_four_squares(3456) (8, 48, 32, 8) >>> sum_of_four_squares(1294585930293) (0, 1137796, 2161, 1234) References ========== .. [1] Representing a number as a sum of four squares, [online], Available: http://www.schorn.ch/howto.html """ if n == 0: return (0, 0, 0, 0) v = 0 while n % 4 == 0: v = v + 1 n = n // 4 if n % 8 == 7: d = 2 n = n - 4 elif n % 8 == 6 or n % 8 == 2: d = 1 n = n - 1 else: d = 0 x, y, z = sum_of_three_squares(n) return (2**v*d, 2**v*x, 2**v*y, 2**v*z) def power_representation(n, p, k, zeros=False): """ Returns a generator for finding k-tuples `(n_{1}, n_{2}, . . . n_{k})` such that `n = n_{1}^p + n_{2}^p + . . . n_{k}^p`. Here `n` is a non-negative integer. StopIteration exception is raised after all the solutions are generated, so should always be used within a try- catch block. Usage ===== ``power_representation(n, p, k, zeros)``: Represent number ``n`` as a sum of ``k``, ``p``th powers. If ``zeros`` is true, then the solutions will contain zeros. Examples ======== >>> from sympy.solvers.diophantine import power_representation >>> f = power_representation(1729, 3, 2) # Represent 1729 as a sum of two cubes >>> next(f) (12, 1) >>> next(f) (10, 9) """ if p < 1 or k < 1 or n < 1: raise ValueError("Expected: n > 0 and k >= 1 and p >= 1") if k == 1: if perfect_power(n): yield (perfect_power(n)[0],) else: yield tuple() elif p == 1: for t in partition(n, k, zeros): yield t else: l = [] a = integer_nthroot(n, p)[0] for t in pow_rep_recursive(a, k, n, [], p): yield t if zeros: for i in xrange(2, k): for t in pow_rep_recursive(a, i, n, [], p): yield t + (0,) * (k - i) def pow_rep_recursive(n_i, k, n_remaining, terms, p): if k == 0 and n_remaining == 0: yield tuple(terms) else: if n_i >= 1 and k > 0 and n_remaining >= 0: if n_i**p <= n_remaining: for t in pow_rep_recursive(n_i, k - 1, n_remaining - n_i**p, terms + [n_i], p): yield t for t in pow_rep_recursive(n_i - 1, k, n_remaining, terms, p): yield t sympy-0.7.4.1/sympy/solvers/benchmarks/0000755000175000017500000000000012253362407020237 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/solvers/benchmarks/bench_solvers.py0000644000175000017500000000040212253362407023441 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import zeros, eye, Symbol, solve_linear_system N = 8 M = zeros(N, N + 1) M[:, :N] = eye(N) S = [Symbol('A%i' % i) for i in range(N)] def timeit_linsolve_trivial(): solve_linear_system(M, *S) sympy-0.7.4.1/sympy/solvers/solvers.py0000644000175000017500000030616112253362407020200 0ustar georgeskgeorgesk""" 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 __future__ import print_function, division from sympy.core.compatibility import (iterable, is_sequence, ordered, default_sort_key, reduce, xrange) 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, Pow) from sympy.core.exprtools import factor_terms from sympy.core.function import (expand_mul, expand_multinomial, expand_log, Derivative, AppliedUndef, UndefinedFunction, nfloat, count_ops, Function, expand_power_exp) 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, re, im, arg, sqrt, atan2) from sympy.functions.elementary.miscellaneous import real_root from sympy.simplify import (simplify, collect, powsimp, posify, powdenest, nsimplify, denom, logcombine) from sympy.simplify.sqrtdenest import sqrt_depth, _mexpand from sympy.simplify.fu import TR1, hyper_as_trig from sympy.matrices import Matrix, zeros from sympy.polys import (roots, cancel, factor, Poly, together, RootOf, degree, PolynomialError) from sympy.functions.elementary.piecewise import piecewise_fold, Piecewise from sympy.utilities.lambdify import lambdify from sympy.utilities.misc import filldedent from sympy.utilities.iterables import uniq 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 import warnings def _ispow(e): """Return True if e is a Pow or is exp.""" return isinstance(e, Expr) and (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 will be returned. Examples ======== >>> from sympy.solvers.solvers import denoms >>> from sympy.abc import x, y, z >>> from sympy import sqrt >>> denoms(x/y) set([y]) >>> denoms(x/(y*z)) set([y, z]) >>> denoms(3/x + y/z) set([x, z]) >>> denoms(x/2 + y/z) set([2, z]) """ pot = preorder_traversal(eq) dens = set() for p in pot: den = denom(p) if den is S.One: continue for d in Mul.make_args(den): dens.add(d) if not symbols: return dens rv = [] for d in dens: free = d.free_symbols if any(s in free for s in symbols): rv.append(d) return set(rv) 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)' show 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. """ minimal = flags.get('minimal', False) 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()), simplify=not minimal): 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 minimal: 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): warnings.warn("\n\tWarning: could not verify solution %s." % sol) # returns None if it can't conclude # TODO: improve solution testing 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 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)' show 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, .... 'particular=True (default is False)' instructs solve to try to find a particular solution to a linear system with as many zeros as possible; this is very expensive 'quick=True (default is False)' when using particular=True, use a fast heuristic instead to find a solution with many zeros (instead of using the very slow method guaranteed to find the largest number of zeros possible) 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] >>> solve(x**2 - y**2, x, set=True) ([x], set([(-y,), (y,)])) >>> solve(x**4 - 1, x, set=True) ([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 solutions is 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) [{x: -y}, {x: y}] >>> solve(z**2*x**2 - z**2*y**2) [{x: -y}, {x: y}, {z: 0}] >>> solve(z**2*x - z**2*y**2) [{x: y**2}, {z: 0}] * 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)] >>> solve(x + exp(x)**2, exp(x), set=True) ([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 >>> solve((a + b)*x - b**2 + 2, a, b, set=True) ([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) [{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) [{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 >>> solve([x**2 + y -2, y**2 - 4], x, y, set=True) ([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) # 0 is excluded [pi] 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, pi] 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. # However, this is necessary only if one of the piecewise # functions depends on one of the symbols we are solving for. def _has_piecewise(e): if e.is_Piecewise: return e.has(*symbols) return any([_has_piecewise(a) for a in e.args]) if _has_piecewise(f[i]): f[i] = piecewise_fold(f[i]) # if we have a Matrix, we need to iterate over its elements again if f[i].is_Matrix: bare_f = False f.extend(list(f[i])) f[i] = S.Zero # if we can split it into real and imaginary parts then do so freei = f[i].free_symbols if freei and all(s.is_real or s.is_imaginary for s in freei): fr, fi = f[i].as_real_imag() if fr and fi and not any(i.has(re, im, arg) for i in (fr, fi)): if bare_f: bare_f = False f[i: i + 1] = [fr, fi] # 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] # real/imag handling for i, fi in enumerate(f): _abs = [a for a in fi.atoms(Abs) if a.has(*symbols)] fi = f[i] = fi.xreplace(dict(list(zip(_abs, [sqrt(a.args[0]**2) for a in _abs])))) if fi.has(*_abs): if any(s.assumptions0 for a in _abs for s in a.free_symbols): raise NotImplementedError(filldedent(''' All absolute values were not removed from %s. In order to solve this equation, try replacing your symbols with Dummy symbols (or other symbols without assumptions). ''' % fi)) else: raise NotImplementedError(filldedent(''' Removal of absolute values from %s failed.''' % fi)) _arg = [a for a in fi.atoms(arg) if a.has(*symbols)] f[i] = fi.xreplace(dict(list(zip(_arg, [atan(im(a.args[0])/re(a.args[0])) for a in _arg])))) # see if re(s) or im(s) appear irf = [] for s in symbols: # if s is real or complex then re(s) or im(s) will not appear in the equation; if s.is_real or s.is_complex: continue # if re(s) or im(s) appear, the auxiliary equation must be present irs = re(s), im(s) if any(_f.has(i) for _f in f for i in irs): symbols.extend(irs) irf.append((s, re(s) + S.ImaginaryUnit*im(s))) if irf: for s, rhs in irf: for i, fi in enumerate(f): f[i] = fi.xreplace({s: rhs}) if bare_f: bare_f = False flags['dict'] = True f.extend(s - rhs for s, rhs in irf) # end of real/imag handling symbols = list(uniq(symbols)) 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 not isinstance(p, Expr) 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 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: warnings.warn(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: warnings.warn(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 [] # 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 = [] got_s = set([]) result = [] 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) if got_s and any([ss in d.free_symbols for ss in got_s]): # sol depends on previously solved symbols: discard it continue got_s.add(n) result.append({n: d}) elif n and d: # otherwise there was no solution for s failed.append(s) if not failed: return result for s in failed: try: soln = _solve(f, s, **flags) for sol in soln: if got_s and any([ss in sol.free_symbols for ss in got_s]): # sol depends on previously solved symbols: discard it continue got_s.add(s) result.append({s: sol}) except NotImplementedError: continue if got_s: return result 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 n, (expr, cond) in enumerate(f.args): candidates = _solve(expr, *symbols, **flags) for candidate in candidates: if candidate in result: continue cond = cond is True or cond.subs(symbol, candidate) if cond is not False: # Only include solutions that do not match the condition # of any previous pieces. matches_other_piece = False for other_n, (other_expr, other_cond) in enumerate(f.args): if other_n == n: break if other_cond is False: continue if other_cond.subs(symbol, candidate) is True: matches_other_piece = True break if not matches_other_piece: result.add(Piecewise( (candidate, cond is True or cond.doit()), (S.NaN, True) )) 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. # but first remove radicals as this will help Polys if flags.pop('unrad', True): try: # try remove all... u = unrad(f_num) except ValueError: # ...else hope for the best while letting some remain try: u = unrad(f, symbol) except ValueError: u = None # hope for best with original equation if u: flags['unrad'] = False # don't unrad next time eq, cov, dens2 = u dens.update(dens2) 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; the solution # will be checked below absent = Dummy() check = flags.get('check', absent) flags['check'] = False sol = _solve(eq, isym, **flags) inv = _solve(ieq, symbol, **flags) result = [] for s in sol: for i in inv: result.append(i.subs(isym, s)) if check == absent: flags.pop('check') else: flags['check'] = check else: result = _solve(eq, symbol, **flags) if result is False: # rewrite hyperbolics in terms of exp f_num = f_num.replace(lambda w: isinstance(w, C.HyperbolicFunction), lambda w: w.rewrite(exp)) 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 for b in bases if b.is_Function) trig = set([_ for _ in funcs if isinstance(_, C.TrigonometricFunction)]) other = funcs - trig if not other and len(funcs.intersection(trig)) > 1: newf = TR1(f_num).rewrite(tan) if newf != f_num: return _solve(newf, 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. if funcs: funcs = list(ordered(funcs)) # put shallowest function first f1 = funcs[0] t = Dummy() # 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, **flags) cv_inv = _solve(t - f1, symbol, **flags)[0] sols = list() for sol in cv_sols: sols.append(cv_inv.subs(t, sol)) return list(ordered(sols)) msg = 'multiple generators %s' % gens 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, **flags) if isinstance(u, (Pow, exp)): # this will be resolved by factor in _tsolve but we might # as well try a simple expansion here to get things in # order so something like the following will work now without # having to factor: # >>> eq = (exp(I*(-x-2))+exp(I*(x+2))) # >>> eq.subs(exp(x),y) # fails # exp(I*(-x - 2)) + exp(I*(x + 2)) # >>> eq.expand().subs(exp(x),y) # works # y**I*exp(2*I) + y**(-I)*exp(-2*I) def _expand(p): b, e = p.as_base_exp() e = expand_mul(e) return expand_power_exp(b**e) ftry = f_num.replace( lambda w: w.is_Pow or isinstance(w, exp), _expand).subs(u, t) if not ftry.has(symbol): soln = _solve(ftry, t, **flags) sols = list() for sol in soln: for i in inv: sols.append(i.subs(t, sol)) return list(ordered(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 aren't on the tsolve-pass, use roots if not flags.pop('tsolve', False): flags['tsolve'] = True if poly.degree() == 1 and ( poly.gen.is_Pow and poly.gen.exp.is_Rational and not poly.gen.exp.is_Integer): pass else: # 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. deg = poly.degree() if deg > 2: flags['simplify'] = flags.get('simplify', False) # TODO: Just pass composite=True to roots() poly = Poly(poly.as_expr(), poly.gen, composite=True) soln = list(roots(poly, cubics=True, quartics=True, quintics=True).keys()) if len(soln) < deg: try: # get all_roots if possible soln = list(ordered(uniq(poly.all_roots()))) except NotImplementedError: pass gen = poly.gen if gen != symbol: u = Dummy() inversion = _solve(gen - u, symbol, **flags) soln = list(ordered(set([i.subs(u, s) for i in inversion for s in soln]))) result = soln # fallback if above fails if result is False: # allow tsolve to be used on next pass if needed flags.pop('tsolve', None) try: result = _tsolve(f_num, symbol, **flags) except PolynomialError: result = None 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 isinstance(s, RootOf) or 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 isinstance(r, RootOf) or 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 = monom.index(1) matrix[i, j] = coeff except ValueError: matrix[i, m] = -coeff # returns a dictionary ({symbols: values}) or None if flags.pop('particular', False): result = minsolve_linear_system(matrix, *symbols, **flags) else: 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 free = set.union(*[p.free_symbols for p in polys]) free = list(free.intersection(symbols)) free.sort(key=default_sort_key) got_s = set([]) result = [] for syms in subsets(free, len(polys)): try: # returns [] or list of tuples of solutions for syms res = solve_poly_system(polys, *syms) if res: for r in res: skip = False for r1 in r: if got_s and any([ss in r1.free_symbols for ss in got_s]): # sol depends on previously # solved symbols: discard it skip = True if not skip: got_s.update(syms) result.extend([dict(list(zip(syms, r)))]) except NotImplementedError: pass if got_s: solved_syms = list(got_s) 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 = set([]) 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 got_s and any([ss in sol.free_symbols for ss in got_s]): # sol depends on previously solved symbols: discard it continue 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.add(s) if not got_s: raise NotImplementedError('could not solve %s' % eq2) if got_s: result = newresult for b in bad_results: result.remove(b) # if there is only one result should we return just the dictionary? return result 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? def minsolve_linear_system(system, *symbols, **flags): r""" Find a particular solution to a linear system. In particular, try to find a solution with the minimal possible number of non-zero variables. This is a very computationally hard prolem. If ``quick=True``, a heuristic is used. Otherwise a naive algorithm with exponential complexity is used. """ quick = flags.get('quick', False) # Check if there are any non-zero solutions at all s0 = solve_linear_system(system, *symbols, **flags) if not s0 or all(v == 0 for v in s0.values()): return s0 if quick: # We just solve the system and try to heuristically find a nice # solution. s = solve_linear_system(system, *symbols) def update(determined, solution): delete = [] for k, v in solution.items(): solution[k] = v.subs(determined) if not solution[k].free_symbols: delete.append(k) determined[k] = solution[k] for k in delete: del solution[k] determined = {} update(determined, s) while s: # NOTE sort by default_sort_key to get deterministic result k = max((k for k in s.values()), key=lambda x: (len(x.free_symbols), default_sort_key(x))) x = max(k.free_symbols, key=default_sort_key) if len(k.free_symbols) != 1: determined[x] = S(0) else: val = solve(k)[0] if val == 0 and all(v.subs(x, val) == 0 for v in s.values()): determined[x] = S(1) else: determined[x] = val update(determined, s) return determined else: # We try to select n variables which we want to be non-zero. # All others will be assumed zero. We try to solve the modified system. # If there is a non-trivial solution, just set the free variables to # one. If we do this for increasing n, trying all combinations of # variables, we will find an optimal solution. # We speed up slightly by starting at one less than the number of # variables the quick method manages. from itertools import combinations from sympy.utilities.misc import debug N = len(symbols) bestsol = minsolve_linear_system(system, *symbols, quick=True) n0 = len([x for x in bestsol.values() if x != 0]) for n in range(n0 - 1, 1, -1): debug('minsolve: %s' % n) thissol = None for nonzeros in combinations(list(range(N)), n): subm = Matrix([system.col(i).T for i in nonzeros] + [system.col(-1).T]).T s = solve_linear_system(subm, *[symbols[i] for i in nonzeros]) if s and not all(v == 0 for v in s.values()): subs = [(symbols[v], S(1)) for v in nonzeros] for k, v in s.items(): s[k] = v.subs(subs) for sym in symbols: if sym not in s: if symbols.index(sym) in nonzeros: s[sym] = S(1) else: s[sym] = S(0) thissol = s break if thissol is None: break bestsol = thissol return bestsol 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 xrange(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 xrange(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 xrange(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 xrange(k + 1, i): content -= matrix[k, j]*solutions[syms[j]] # run back-substitution for parameters for j in xrange(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 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 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 def tsolve(eq, sym): SymPyDeprecationWarning( feature="tsolve()", useinstead="solve()", issue=3385, deprecated_since_version="0.7.2", ).warn() return _tsolve(eq, sym) # these are functions that have multiple inverse values per period multi_inverses = { sin: lambda x: (asin(x), S.Pi - asin(x)), cos: lambda x: (acos(x), 2*S.Pi - acos(x)), } 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. There is currently no guarantee that all solutions will be returned or that a real solution will be favored over a complex one. Examples ======== >>> 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/2 + log(2)/log(3), log(-2*sqrt(3)/27)/log(3)] >>> tsolve(log(x) + 2*x, x) [LambertW(2)/2] """ if 'tsolve_saw' not in flags: flags['tsolve_saw'] = [] if eq in flags['tsolve_saw']: return None else: flags['tsolve_saw'].append(eq) rhs, lhs = _invert(eq, sym) if lhs == sym: return [rhs] try: if lhs.is_Add: # it's time to try factoring; powdenest is used # to try get powers in standard form for better factoring f = factor(powdenest(lhs - rhs)) if f.is_Mul: return _solve(f, sym, **flags) if rhs: f = logcombine(lhs, force=flags.get('force', False)) if f.count(log) != lhs.count(log): if f.func is log: return _solve(f.args[0] - exp(rhs), sym, **flags) return _tsolve(f - rhs, sym) elif lhs.is_Pow: if lhs.exp.is_Integer: if lhs - rhs != eq: return _solve(lhs - rhs, sym, **flags) elif sym not in lhs.exp.free_symbols: return _solve(lhs.base - rhs**(1/lhs.exp), sym, **flags) 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, **flags) if not sol_base: return sol_base # no solutions to remove so return now return list(ordered(set(sol_base) - set( _solve(lhs.exp, sym, **flags)))) 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, **flags) elif lhs.is_Mul and rhs.is_positive: llhs = expand_log(log(lhs)) if llhs.is_Add: return _solve(llhs - log(rhs), sym, **flags) elif lhs.is_Function and lhs.nargs == 1 and lhs.func in multi_inverses: # sin(x) = 1/3 -> x - asin(1/3) & x - (pi - asin(1/3)) soln = [] for i in multi_inverses[lhs.func](rhs): soln.extend(_solve(lhs.args[0] - i, sym, **flags)) return list(ordered(soln)) rewrite = lhs.rewrite(exp) if rewrite != lhs: return _solve(rewrite - rhs, sym, **flags) except NotImplementedError: pass # maybe it is a lambert pattern if flags.pop('bivariate', True): # lambert forms may need some help being recognized, e.g. changing # 2**(3*x) + x**3*log(2)**3 + 3*x**2*log(2)**2 + 3*x*log(2) + 1 # to 2**(3*x) + (x*log(2) + 1)**3 g = _filtered_gens(eq.as_poly(), sym) up_or_log = set() for gi in g: if gi.func is exp or gi.func is log: up_or_log.add(gi) elif gi.is_Pow: gisimp = powdenest(expand_power_exp(gi)) if gisimp.is_Pow and sym in gisimp.exp.free_symbols: up_or_log.add(gi) down = g.difference(up_or_log) eq_down = expand_log(expand_power_exp(eq)).subs( dict(list(zip(up_or_log, [0]*len(up_or_log))))) eq = expand_power_exp(factor(eq_down, deep=True) + (eq - eq_down)) rhs, lhs = _invert(eq, sym) if lhs.has(sym): try: poly = lhs.as_poly() g = _filtered_gens(poly, sym) return _solve_lambert(lhs - rhs, sym, g) except NotImplementedError: # maybe it's a convoluted function if len(g) == 2: try: gpu = bivariate_type(lhs - rhs, *g) if gpu is None: raise NotImplementedError g, p, u = gpu flags['bivariate'] = False inversion = _tsolve(g - u, sym, **flags) if inversion: sol = _solve(p, u, **flags) return list(ordered([i.subs(u, s) for i in inversion for s in sol])) except NotImplementedError: pass 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 list(ordered([s.subs(reps) for s in soln])) # TODO: option for calculating J numerically 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 to 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) (1/2, cos(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)) 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 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 and \ not lhs.is_polynomial(*symbols): a, b = ordered(lhs.args) 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: rat = ad/bd _lhs = powsimp(ad/bd) if _lhs != rat: lhs = _lhs rhs = -bi/ai if ai*bi is S.NegativeOne: if all( isinstance(i, Function) for i in (ad, bd)) and \ ad.func == bd.func and ad.nargs == bd.nargs: if len(ad.args) == 1: lhs = ad.args[0] - bd.args[0] else: # should be able to solve # f(x, y) == f(2, 3) -> x == 2 # f(x, x + y) == f(2, 3) -> x == 2 or x == 3 - y raise NotImplementedError('equal function with more than 1 argument') elif lhs.is_Mul and any(_ispow(a) for a in lhs.args): lhs = powsimp(powdenest(lhs)) if lhs.is_Function: if hasattr(lhs, 'inverse') and len(lhs.args) == 1: # -1 # f(x) = g -> x = f (g) # # /!\ inverse should not be defined if there are multiple values # for the function -- these are handled in _tsolve # rhs = lhs.inverse()(rhs) lhs = lhs.args[0] elif lhs.func is atan2: y, x = lhs.args lhs = 2*atan(y/(sqrt(x**2 + y**2) + x)) 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 the tuple, ``(eq, cov, dens)``, is returned where:: ``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. Two options are also recognized:: ``take``, when defined, is interpreted as a single-argument function that returns True if a given Pow should be handled. ``all``, when True, will signify that an attempt should be made to remove all radicals. ``take``, if present, has priority over ``all``. 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)], []) """ def _canonical(eq): # remove constants since these don't change the location of the root # and expand the expression eq = factor_terms(eq) if eq.is_Mul: eq = Mul(*[f for f in eq.args if not f.is_number]) eq = _mexpand(eq) # make sign canonical free = eq.free_symbols if len(free) == 1: if (eq.coeff(free.pop()**degree(eq)) < 0) is True: eq = -eq elif eq.could_extract_minus_sign(): eq = -eq return eq if eq.is_Atom: return cov, dens, nwas, rpt = [flags.get(k, v) for k, v in sorted(dict(dens=None, cov=None, n=None, rpt=0).items())] if flags.get('take', None): _take = flags.pop('take') elif flags.pop('all', None): _rad = lambda w: w.is_Pow and w.exp.is_Rational and w.exp.q != 1 def _take(d): return _rad(d) or any(_rad(i) for i in d.atoms(Pow)) if eq.has(S.ImaginaryUnit): i = Dummy() flags['take'] = _take try: rv = unrad(eq.xreplace({S.ImaginaryUnit: sqrt(i)}), *syms, **flags) rep = {i: S.NegativeOne} rv = (_canonical(rv[0].xreplace(rep)), [tuple([j.xreplace(rep) for j in i]) for i in rv[1]], [i.xreplace(rep) for i in rv[2]]) return rv except ValueError as msg: raise msg else: 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(factor_terms(eq, radical=True)) 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 rterms.keys()] # the output will depend on the order terms are processed, so # make it canonical quickly rterms = list(reversed(list(ordered(rterms)))) # 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): def _norm2(a, b): return a**2 + b**2 + 2*a*b if len(rterms) == 4: # (r0+r1)**2 - (r2+r3)**2 r0, r1, r2, r3 = rterms eq = _norm2(r0, r1) - _norm2(r2, r3) elif len(rterms) == 3: # (r1+r2)**2 - (r0+args)**2 r0, r1, r2 = rterms eq = _norm2(r1, r2) - _norm2(r0, args) elif len(rterms) == 2: # r0**2 - (r1+args)**2 r0, r1 = rterms eq = r0**2 - _norm2(r1, args) 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) rpt += 1 # XXX how many repeats with others unchanging is enough? if not ok or ( nwas is not None and len(rterms) == nwas and new_depth is not None and new_depth == depth and rpt > 3): # XXX: XFAIL tests indicate other cases that should be handled. raise ValueError('Cannot remove all radicals from %s' % eq) neq = unrad(eq, *syms, cov=cov, dens=dens, n=len(rterms), rpt=rpt, take=_take) if neq: eq = neq[0] return (_canonical(eq), cov, list(dens)) from sympy.solvers.bivariate import ( bivariate_type, _solve_lambert, _filtered_gens) sympy-0.7.4.1/sympy/solvers/ode.py0000644000175000017500000064375612253362407017270 0ustar georgeskgeorgeskr""" This module contains :py:meth:`~sympy.solvers.ode.dsolve` and different helper functions that it uses. :py:meth:`~sympy.solvers.ode.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 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 :py:meth:`~sympy.solvers.ode.dsolve`. **Functions in this module** These are the user functions in this module: - :py:meth:`~sympy.solvers.ode.dsolve` - Solves ODEs. - :py:meth:`~sympy.solvers.ode.classify_ode` - Classifies ODEs into possible hints for :py:meth:`~sympy.solvers.ode.dsolve`. - :py:meth:`~sympy.solvers.ode.checkodesol` - Checks if an equation is the solution to an ODE. - :py:meth:`~sympy.solvers.ode.homogeneous_order` - Returns the homogeneous order of an expression. - :py:meth:`~sympy.solvers.ode.infinitesimals` - Returns the infinitesimals of the Lie group of point transformations of an ODE, such that it is invariant. - :py:meth:`~sympy.solvers.ode_checkinfsol` - Checks if the given infinitesimals are the actual infinitesimals of a first order ODE. These are the non-solver helper functions that are for internal use. The user should use the various options to :py:meth:`~sympy.solvers.ode.dsolve` to obtain the functionality provided by these functions: - :py:meth:`~sympy.solvers.ode.odesimp` - Does all forms of ODE simplification. - :py:meth:`~sympy.solvers.ode.ode_sol_simplicity` - A key function for comparing solutions by simplicity. - :py:meth:`~sympy.solvers.ode.constantsimp` - Simplifies arbitrary constants. - :py:meth:`~sympy.solvers.ode.constant_renumber` - Renumber arbitrary constants. - :py:meth:`~sympy.solvers.ode._handle_Integral` - Evaluate unevaluated Integrals. 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 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. - Power series solutions for first order differential equations. - Lie Group method of solving first order differential equations. - 2nd order Liouville differential equations. - Power series solutions for second order differential equations at ordinary and regular singular points. - `n`\th order linear homogeneous differential equation with constant coefficients. - `n`\th order linear inhomogeneous differential equation with constant coefficients using the method of undetermined coefficients. - `n`\th 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 :py:meth:`~sympy.solvers.ode.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_``. That function takes in the ODE and any match expression gathered by :py:meth:`~sympy.solvers.ode.classify_ode` and returns a solved result. If this result has any integrals in it, the hint function will return an unevaluated :py:class:`~sympy.integrals.Integral` class. :py:meth:`~sympy.solvers.ode.dsolve`, which is the user wrapper function around all of this, will then call :py:meth:`~sympy.solvers.ode.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 :py:meth:`~sympy.solvers.ode.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 :py:mod:`~sympy.solvers.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 `n`\th 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 ``_best`` hint. Your ``ode__best()`` function should choose the best using min with ``ode_sol_simplicity`` as the key argument. See :py:meth:`~sympy.solvers.ode.ode_1st_homogeneous_coeff_best`, for example. The function that uses your method will be called ``ode_()``, 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 (:py:meth:`~sympy.solvers.ode.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 :py:meth:`~sympy.solvers.ode.dsolve` code. See also the :py:meth:`~sympy.solvers.ode.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 :py:data:`~sympy.solvers.ode.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 :py:meth:`~sympy.solvers.ode.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 :py:meth:`~sympy.solvers.ode.classify_ode` (if the match function is more than just a few lines, like :py:meth:`~sympy.solvers.ode._undetermined_coefficients_match`, it should go outside of :py:meth:`~sympy.solvers.ode.classify_ode`). It should match the ODE without solving for it as much as possible, so that :py:meth:`~sympy.solvers.ode.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 :py:meth:`~sympy.solvers.ode.classify_ode`. :py:meth:`~sympy.solvers.ode.classify_ode` will then send this to :py:meth:`~sympy.solvers.ode.dsolve`, which will send it to your function as the ``match`` argument. Your function should be named ``ode_(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 :py:meth:`~sympy.solvers.ode.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``. :py:meth:`~sympy.solvers.ode.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 :py:meth:`C.Integral() ` instead of :py:meth:`~sympy.core.expr.Expr.integrate`. This allows the user to bypass hard/slow integration by using the ``_Integral`` variant of your hint. In most cases, calling :py:meth:`sympy.core.basic.Basic.doit` will integrate your solution. If this is not the case, you will need to write special code in :py:meth:`~sympy.solvers.ode._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_()`` function. :py:meth:`~sympy.solvers.ode.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 :py:meth:`~sympy.solvers.ode.odesimp` for it. For example, solutions returned from the ``1st_homogeneous_coeff`` hints often have many :py:meth:`~sympy.functions.log` terms, so :py:meth:`~sympy.solvers.ode.odesimp` calls :py:meth:`~sympy.simplify.simplify.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 :py:meth:`~sympy.solvers.ode.constantsimp` take better advantage of it. It is better to put simplification in :py:meth:`~sympy.solvers.ode.odesimp` than in your method, because it can then be turned off with the simplify flag in :py:meth:`~sympy.solvers.ode.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 :py:meth:`~sympy.solvers.ode.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 :py:meth:`~sympy.solvers.ode.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 `n`\th 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 __future__ import print_function, division from collections import defaultdict from itertools import islice from sympy.core import Add, C, S, Mul, Pow, oo from sympy.core.compatibility import ordered, iterable, is_sequence, xrange from sympy.utilities.exceptions import SymPyDeprecationWarning from sympy.core.exprtools import factor_terms, gcd_terms from sympy.core.function import (Function, Derivative, AppliedUndef, diff, expand, expand_mul, Subs) from sympy.core.multidimensional import vectorize from sympy.core.numbers import Rational, NaN, zoo from sympy.core.relational import Equality, Eq from sympy.core.symbol import Symbol, Wild, Dummy, symbols from sympy.core.sympify import sympify from sympy.logic.boolalg import BooleanAtom from sympy.functions import cos, exp, im, log, re, sin, tan, sqrt, sign, Piecewise from sympy.functions.combinatorial.factorials import factorial from sympy.matrices import wronskian from sympy.polys import Poly, RootOf, terms_gcd, PolynomialError from sympy.polys.polytools import cancel, degree, div from sympy.series import Order from sympy.series.series import series from sympy.simplify import collect, logcombine, powsimp, separatevars, \ simplify, trigsimp, denom, fraction, posify from sympy.simplify.simplify import _mexpand from sympy.solvers import solve from sympy.utilities import numbered_symbols, default_sort_key, sift from sympy.solvers.deutils import _preprocess, ode_order, _desolve #: This is a list of hints in the order that they should be preferred by #: :py:meth:`~sympy.solvers.ode.classify_ode`. In general, hints earlier in the #: list should produce simpler solutions than those later in the list (for #: ODEs that fit both). For now, the order of this list is based on empirical #: observations by the developers of SymPy. #: #: The hint used by :py:meth:`~sympy.solvers.ode.dsolve` for a specific ODE #: can be overridden (see the docstring). #: #: In general, ``_Integral`` hints are grouped at the end of the list, unless #: there is a method that returns an unevaluable integral most of the time #: (which 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", "almost_linear", "linear_coefficients", "separable_reduced", "1st_power_series", "lie_group", "nth_linear_constant_coeff_homogeneous", "nth_linear_euler_eq_homogeneous", "nth_linear_constant_coeff_undetermined_coefficients", "nth_linear_constant_coeff_variation_of_parameters", "Liouville", "2nd_power_series_ordinary", "2nd_power_series_regular", "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", "almost_linear_Integral", "linear_coefficients_Integral", "separable_reduced_Integral", "nth_linear_constant_coeff_variation_of_parameters_Integral", "Liouville_Integral", ) lie_heuristics = ( "abaco1_simple", "abaco1_product", "abaco2_similar", "abaco2_unique_unknown", "abaco2_unique_general", "linear", "function_sum", "bivariate", "chi" ) def sub_func_doit(eq, func, new): r""" 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) def dsolve(eq, func=None, hint="default", simplify=True, ics= None, xi=None, eta=None, x0=0, n=6, **kwargs): r""" 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 :py:mod:`~sympy.solvers.ode` docstring for supported methods). This can either be an :py:class:`~sympy.core.relational.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 :py:meth:`~sympy.solvers.ode.classify_ode`. See Hints below for more options that you can use for hint. ``simplify`` enables simplification by :py:meth:`~sympy.solvers.ode.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. ``xi`` and ``eta`` are the infinitesimal functions of an ordinary differential equation. They are the infinitesimals of the Lie group of point transformations for which the differential equation is invariant. The user can specify values for the infinitesimals. If nothing is specified, ``xi`` and ``eta`` are calculated using :py:meth:`~sympy.solvers.ode.infinitesimals` with the help of various heuristics. ``ics`` is the set of boundary conditions for the differential equation. It should be given in the form of ``{f(x0): x1, f(x).diff(x).subs(x, x2): x3}`` and so on. For now initial conditions are implemented only for power series solutions of first-order differential equations which should be given in the form of ``{f(x0): x1}`` (See Issue 1621). If nothing is specified for this case ``f(0)`` is assumed to be ``C0`` and the power series solution is calculated about 0. ``x0`` is the point about which the power series solution of a differential equation is to be evaluated. ``n`` gives the exponent of the dependent variable up to which the power series solution of a differential equation is to be evaluated. **Hints** Aside from the various solving methods, there are also some meta-hints that you can pass to :py:meth:`~sympy.solvers.ode.dsolve`: ``default``: This uses whatever hint is returned first by :py:meth:`~sympy.solvers.ode.classify_ode`. This is the default argument to :py:meth:`~sympy.solvers.ode.dsolve`. ``all``: To make :py:meth:`~sympy.solvers.ode.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 :py:meth:`~sympy.solvers.deutils.ode_order` in ``deutils.py``. - ``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 :py:meth:`~sympy.solvers.ode.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 :py:meth:`~sympy.solvers.ode.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 :py:meth:`~sympy.solvers.ode.dsolve` to hang because of a difficult or impossible integral. This meta-hint will also be much faster than ``all``, because :py:meth:`~sympy.core.expr.Expr.integrate` is an expensive routine. ``best``: To have :py:meth:`~sympy.solvers.ode.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 :py:meth:`~sympy.solvers.ode.classify_ode` docstring for more info on hints, and the :py:mod:`~sympy.solvers.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 :py:meth:`~sympy.solvers.ode.dsolve`. - :py:meth:`~sympy.solvers.ode.dsolve` always returns an :py:class:`~sympy.core.relational.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_)`` to get help more information on a specific hint, where ```` 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) >>> eq = sin(x)*cos(f(x)) + cos(x)*sin(f(x))*f(x).diff(x) >>> dsolve(eq, hint='separable_reduced') f(x) == C1/(C2*x - 1) >>> dsolve(eq, hint='1st_exact') [f(x) == -acos(C1/cos(x)) + 2*pi, f(x) == acos(C1/cos(x))] >>> dsolve(eq, hint='almost_linear') [f(x) == -acos(-sqrt(C1/cos(x)**2)) + 2*pi, f(x) == -acos(sqrt(C1/cos(x)**2)) + 2*pi, f(x) == acos(-sqrt(C1/cos(x)**2)), f(x) == acos(sqrt(C1/cos(x)**2))] """ given_hint = hint # hint given by the user # See the docstring of _desolve for more details. hints = _desolve(eq, func=func, hint=hint, simplify=True, xi=xi, eta=eta, type='ode', ics=ics, x0=x0, n=n, **kwargs) eq = hints.pop('eq', eq) all_ = hints.pop('all', False) if all_: retdict = {} failed_hints = {} gethints = classify_ode(eq, dict=True) orderedhints = gethints['ordered_hints'] for hint in hints: try: rv = _helper_simplify(eq, hint, hints[hint], simplify) except NotImplementedError as detail: failed_hints[hint] = detail else: retdict[hint] = rv func = hints[hint]['func'] retdict['best'] = min(list(retdict.values()), key=lambda x: ode_sol_simplicity(x, func, trysolving=not simplify)) if given_hint == 'best': return retdict['best'] for i in orderedhints: if retdict['best'] == retdict.get(i, None): retdict['best_hint'] = i break retdict['default'] = gethints['default'] retdict['order'] = gethints['order'] retdict.update(failed_hints) return retdict else: # The key 'hint' stores the hint needed to be solved for. hint = hints['hint'] return _helper_simplify(eq, hint, hints, simplify) def _helper_simplify(eq, hint, match, simplify=True, **kwargs): r""" Helper function of dsolve that calls the respective :py:mod:`~sympy.solvers.ode` functions to solve for the ordinary differential equations. This minimises the computation in calling :py:meth:`~sympy.solvers.deutils._desolve` multiple times. """ r = match if hint.endswith('_Integral'): solvefunc = globals()['ode_' + hint[:-len('_Integral')]] else: solvefunc = globals()['ode_' + hint] func = r['func'] order = r['order'] match = r[hint] if simplify: # odesimp() will attempt to integrate, if necessary, apply constantsimp(), # attempt to solve for func, and apply any other hint specific # simplifications rv = odesimp(solvefunc(eq, func, order, match), func, order, hint) return rv else: # We still want to integrate (you can disable it separately with the hint) match['simplify'] = False # Some hints can take advantage of this option rv = _handle_Integral(solvefunc(eq, func, order, match), func, order, hint) return rv def classify_ode(eq, func=None, dict=False, ics=None, **kwargs): r""" Returns a tuple of possible :py:meth:`~sympy.solvers.ode.dsolve` classifications for an ODE. The tuple is ordered so that first item is the classification that :py:meth:`~sympy.solvers.ode.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 :py:meth:`~sympy.solvers.ode.dsolve` use a different classification, use ``dsolve(ODE, func, hint=)``. See also the :py:meth:`~sympy.solvers.ode.dsolve` docstring for different meta-hints you can use. If ``dict`` is true, :py:meth:`~sympy.solvers.ode.classify_ode` will return a dictionary of ``hint:match`` expression terms. This is intended for internal use by :py:meth:`~sympy.solvers.ode.dsolve`. Note that because dictionaries are ordered arbitrarily, this will most likely not be in the same order as the tuple. You can get help on different hints by executing ``help(ode.ode_hintname)``, where ``hintname`` is the name of the hint without ``_Integral``. See :py:data:`~sympy.solvers.ode.allhints` or the :py:mod:`~sympy.solvers.ode` docstring for a list of all supported hints that can be returned from :py:meth:`~sympy.solvers.ode.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 :py:class:`~sympy.integrals.Integral` class in it. Note that a hint may do this anyway if :py:meth:`~sympy.core.expr.Expr.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 :py:meth:`~sympy.core.expr.Expr.integrate` is an expensive routine. If :py:meth:`~sympy.solvers.ode.dsolve` hangs, it is probably because :py:meth:`~sympy.core.expr.Expr.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 :py:meth:`~sympy.solvers.ode.integrate` is not used in solving the ODE for those method. For example, `n`\th 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 :py:class:`~sympy.integrals.Integral`\s 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', '1st_power_series', 'lie_group', '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') """ prep = kwargs.pop('prep', True) from sympy import expand if func and len(func.args) != 1: raise ValueError("dsolve() and classify_ode() only " "work with functions of one variable, not %s" % func) 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') xi = kwargs.get('xi') eta = kwargs.get('eta') terms = kwargs.get('n') if isinstance(eq, Equality): if eq.rhs != 0: return classify_ode(eq.lhs - eq.rhs, func, ics=ics, xi=xi, n=terms, eta=eta, prep=False) eq = eq.lhs order = ode_order(eq, f(x)) # hint:matchdict or hint:(tuple of matchdicts) # Also will contain "default": 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]) a3 = Wild('a3', exclude=[f(x), df, f(x).diff(x, 2)]) b3 = Wild('b3', exclude=[f(x), df, f(x).diff(x, 2)]) c3 = Wild('c3', exclude=[f(x), df, f(x).diff(x, 2)]) r3 = {'xi': xi, 'eta': eta} # Used for the lie_group hint boundary = {} # Used to extract initial conditions C0 = Symbol("C0") eq = expand(eq) # Preprocessing to get the initial conditions out if ics is not None: for funcarg in ics: # Separating derivatives if isinstance(funcarg, Subs): deriv = funcarg.expr old = funcarg.variables[0] new = funcarg.point[0] if isinstance(deriv, Derivative) and isinstance(deriv.args[0], AppliedUndef) and deriv.args[0].func == f and old == x and not new.has(x): dorder = ode_order(deriv, x) temp = 'f' + str(dorder) boundary.update({temp: new, temp + 'val': ics[funcarg]}) else: raise ValueError("Enter valid boundary conditions for Derivatives") # Separating functions elif isinstance(funcarg, AppliedUndef): if funcarg.func == f and len(funcarg.args) == 1 and \ not funcarg.args[0].has(x): boundary.update({'f0': funcarg.args[0], 'f0val': ics[funcarg]}) else: raise ValueError("Enter valid boundary conditions for Function") else: raise ValueError("Enter boundary conditions of the form ics " " = {f(point}: value, f(point).diff(point, order).subs(arg, point) " ":value") # 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 not in (1, 0): 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 # NON-REDUCED FORM OF EQUATION matches 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) # FIRST ORDER POWER SERIES WHICH NEEDS INITIAL CONDITIONS # TODO: Hint first order series should match only if d/e is analytic. # For now, only d/e and (d/e).diff(arg) is checked for existence at # at a given point. # This is currently done internally in ode_1st_power_series. point = boundary.get('f0', 0) value = boundary.get('f0val', C0) check = cancel(r[d]/r[e]) check1 = check.subs({x: point, y: value}) if not check1.has(oo) and not check1.has(zoo) and \ not check1.has(NaN) and not check1.has(-oo): check2 = (check1.diff(x)).subs({x: point, y: value}) if not check2.has(oo) and not check2.has(zoo) and \ not check2.has(NaN) and not check2.has(-oo): rseries = r.copy() rseries.update({'terms': terms, 'f0': point, 'f0val': value}) matching_hints["1st_power_series"] = rseries r3.update(r) ## Exact Differential Equation: P(x, y) + Q(x, y)*y' = 0 where # dP/dy == dQ/dx try: if r[d] != 0: numerator = simplify(r[d].diff(y) - r[e].diff(x)) # The following few conditions try to convert a non-exact # differential equation into an exact one. # References : Differential equations with applications # and historical notes - George E. Simmons if numerator: # If (dP/dy - dQ/dx) / Q = f(x) # then exp(integral(f(x))*equation becomes exact factor = simplify(numerator/r[e]) variables = factor.free_symbols if len(variables) == 1 and x == variables.pop(): factor = exp(C.Integral(factor).doit()) r[d] *= factor r[e] *= factor matching_hints["1st_exact"] = r matching_hints["1st_exact_Integral"] = r else: # If (dP/dy - dQ/dx) / -P = f(y) # then exp(integral(f(y))*equation becomes exact factor = simplify(-numerator/r[d]) variables = factor.free_symbols if len(variables) == 1 and y == variables.pop(): factor = exp(C.Integral(factor).doit()) r[d] *= factor r[e] *= factor matching_hints["1st_exact"] = r matching_hints["1st_exact_Integral"] = r else: 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 # Any first order ODE can be ideally solved by the Lie Group # method matching_hints["lie_group"] = r3 # 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: # Using r[d] and r[e] without any modification for hints # linear-coefficients and separable-reduced. num, den = r[d], r[e] # ODE = d/e + df r['d'] = d r['e'] = e r['y'] = y r[d] = num.subs(f(x), y) r[e] = den.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) if ordera is not None: orderb = homogeneous_order(r[e], x, y) if ordera == orderb: # u1=y/x and u2=x/y u1 = Dummy('u1') u2 = Dummy('u2') s = "1st_homogeneous_coeff_subs" s1 = s + "_dep_div_indep" s2 = s + "_indep_div_dep" if simplify((r[d] + u1*r[e]).subs({x: 1, y: u1})) != 0: matching_hints[s1] = r matching_hints[s1 + "_Integral"] = r if simplify((r[e] + u2*r[d]).subs({x: u2, y: 1})) != 0: matching_hints[s2] = r matching_hints[s2 + "_Integral"] = r if s1 in matching_hints and s2 in matching_hints: matching_hints["1st_homogeneous_coeff_best"] = r ## Linear coefficients of the form # y'+ F((a*x + b*y + c)/(a'*x + b'y + c')) = 0 # that can be reduced to homogeneous form. F = num/den params = _linear_coeff_match(F, func) if params: xarg, yarg = params u = Dummy('u') t = Dummy('t') # Dummy substitution for df and f(x). dummy_eq = reduced_eq.subs(((df, t), (f(x), u))) reps = ((x, x + xarg), (u, u + yarg), (t, df), (u, f(x))) dummy_eq = simplify(dummy_eq.subs(reps)) # get the re-cast values for e and d r2 = collect(expand(dummy_eq), [df, f(x)]).match(e*df + d) if r2: orderd = homogeneous_order(r2[d], x, f(x)) if orderd is not None: ordere = homogeneous_order(r2[e], x, f(x)) if orderd == ordere: # Match arguments are passed in such a way that it # is coherent with the already existing homogeneous # functions. r2[d] = r2[d].subs(f(x), y) r2[e] = r2[e].subs(f(x), y) r2.update({'xarg': xarg, 'yarg': yarg, 'd': d, 'e': e, 'y': y}) matching_hints["linear_coefficients"] = r2 matching_hints["linear_coefficients_Integral"] = r2 ## Equation of the form y' + (y/x)*H(x^n*y) = 0 # that can be reduced to separable form factor = simplify(x/f(x)*num/den) # Try representing factor in terms of x^n*y # where n is lowest power of x in factor; # first remove terms like sqrt(2)*3 from factor.atoms(Mul) u = None for mul in ordered(factor.atoms(Mul)): if mul.has(x): _, u = mul.as_independent(x, f(x)) break if u and u.has(f(x)): t = Dummy('t') r2 = {'t': t} xpart, ypart = u.as_independent(f(x)) test = factor.subs(((u, t), (1/u, 1/t))) free = test.free_symbols if len(free) == 1 and free.pop() == t: r2.update({'power': xpart.as_base_exp()[1], 'u': test}) matching_hints["separable_reduced"] = r2 matching_hints["separable_reduced_Integral"] = r2 ## Almost-linear equation of the form f(x)*g(y)*y' + k(x)*l(y) + m(x) = 0 r = collect(eq, [df, f(x)]).match(e*df + d) if r: r2 = r.copy() r2[c] = S.Zero if r2[d].is_Add: # Separate the terms having f(x) to r[d] and # remaining to r[c] no_f, r2[d] = r2[d].as_independent(f(x)) r2[c] += no_f factor = simplify(r2[d].diff(f(x))/r[e]) if factor and not factor.has(f(x)): r2[d] = factor_terms(r2[d]) u = r2[d].as_independent(f(x), as_Add=False)[1] r2.update({'a': e, 'b': d, 'c': c, 'u': u}) r2[d] /= u r2[e] /= u.diff(f(x)) matching_hints["almost_linear"] = r2 matching_hints["almost_linear_Integral"] = r2 elif order == 2: # Liouville ODE in the form # 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 # Homogeneous second order differential equation of the form # a3*f(x).diff(x, 2) + b3*f(x).diff(x) + c3, where # for simplicity, a3, b3 and c3 are assumed to be polynomials. # It has a definite power series solution at point x0 if, b3/a3 and c3/a3 # are analytic at x0. deq = a3*(f(x).diff(x, 2)) + b3*df + c3*f(x) r = collect(reduced_eq, [f(x).diff(x, 2), f(x).diff(x), f(x)]).match(deq) ordinary = False if r and r[a3] != 0: if all([r[key].is_polynomial() for key in r]): p = cancel(r[b3]/r[a3]) # Used below q = cancel(r[c3]/r[a3]) # Used below point = kwargs.get('x0', 0) check = p.subs(x, point) if not check.has(oo) and not check.has(NaN) and \ not check.has(zoo) and not check.has(-oo): check = q.subs(x, point) if not check.has(oo) and not check.has(NaN) and \ not check.has(zoo) and not check.has(-oo): ordinary = True r.update({'a3': a3, 'b3': b3, 'c3': c3, 'x0': point, 'terms': terms}) matching_hints["2nd_power_series_ordinary"] = r # Checking if the differential equation has a regular singular point # at x0. It has a regular singular point at x0, if (b3/a3)*(x - x0) # and (c3/a3)*((x - x0)**2) are analytic at x0. if not ordinary: p = cancel((x - point)*p) check = p.subs(x, point) if not check.has(oo) and not check.has(NaN) and \ not check.has(zoo) and not check.has(-oo): q = cancel(((x - point)**2)*q) check = q.subs(x, point) if not check.has(oo) and not check.has(NaN) and \ not check.has(zoo) and not check.has(-oo): coeff_dict = {'p': p, 'q': q, 'x0': point, 'terms': terms} matching_hints["2nd_power_series_regular"] = coeff_dict 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) s = "nth_linear_constant_coeff_variation_of_parameters" matching_hints[s] = r matching_hints[s + "_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): r""" 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 = [i for i in allhints if i in matching_hints] if dict: # Dictionaries are ordered arbitrarily, so make note of which # hint would come first for dsolve(). Use an ordered dict in Py 3. matching_hints["default"] = retlist[0] if retlist else None matching_hints["ordered_hints"] = tuple(retlist) return matching_hints else: return tuple(retlist) @vectorize(0) def odesimp(eq, func, order, hint): r""" Simplifies ODEs, including trying to solve for ``func`` and running :py:meth:`~sympy.solvers.ode.constantsimp`. It may use knowledge of the type of solution that the hint returns to apply additional simplifications. It also attempts to integrate any :py:class:`~sympy.integrals.Integral`\s in the expression, if the hint is not an ``_Integral`` hint. This function should have no effect on expressions returned by :py:meth:`~sympy.solvers.ode.dsolve`, as :py:meth:`~sympy.solvers.ode.dsolve` already calls :py:meth:`~sympy.solvers.ode.odesimp`, but the individual hint functions do not call :py:meth:`~sympy.solvers.ode.odesimp` (because the :py:meth:`~sympy.solvers.ode.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, wrap_line=False) x ---- f(x) / | | / 1 \ | -|u2 + -------| | | /1 \| | | sin|--|| | \ \u2// log(f(x)) = log(C1) + | ---------------- d(u2) | 2 | 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, force=True) if not eqsol: raise NotImplementedError except (NotImplementedError, PolynomialError): 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.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): eqi = constantsimp(eqi, x, 2*order) eq[i] = constant_renumber(eqi, '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 def checkodesol(ode, sol, func=None, order='auto', solve_for_func=True): r""" Substitutes ``sol`` into ``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 :py:class:`~sympy.core.relational.Equality` that the solution satisfies, e.g. ``Eq(f(x), C1), Eq(f(x) + C1, 0)``; or simply an :py:class:`~sympy.core.expr.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 ``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, ..., `n`\th 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 :py:meth:`~sympy.simplify.simplify.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(force=True) 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 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, BooleanAtom)): 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 that the # numerator goes to zero num = trigsimp((lhs - rhs).as_numer_denom()[0]) # since solutions are obtained using force=True we test # using the same level of assumptions ## replace function with dummy so assumptions will work _func = Dummy('func') num = num.subs(func, _func) ## posify the expression num, reps = posify(num) s = simplify(num).xreplace(reps).xreplace({_func: func}) 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) def ode_sol_simplicity(sol, func, trysolving=True): r""" 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 :py:class:`~sympy.integrals.Integral`\s, 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. +----------------------------------------------+-------------------+ | Simplicity | Return | +==============================================+===================+ | ``sol`` solved for ``func`` | ``-2`` | +----------------------------------------------+-------------------+ | ``sol`` not solved for ``func`` but can be | ``-1`` | +----------------------------------------------+-------------------+ | ``sol`` is not solved nor solvable for | ``len(str(sol))`` | | ``func`` | | +----------------------------------------------+-------------------+ | ``sol`` contains an | ``oo`` | | :py:class:`~sympy.integrals.Integral` | | +----------------------------------------------+-------------------+ ``oo`` here means the SymPy infinity, which should compare greater than any integer. If you already know :py:meth:`~sympy.solvers.solvers.solve` cannot solve ``sol``, you can use ``trysolving=False`` to skip that step, which is the only potentially slow step. For example, :py:meth:`~sympy.solvers.ode.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) def constantsimp(expr, independentsymbol, endnumber, startnumber=1, symbolname='C'): r""" Simplifies an expression with arbitrary constants in it. This function is written specifically to work with :py:meth:`~sympy.solvers.ode.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 ``endnumber`` 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 of constants is done with limited assistance: 1. terms of :py:class:`~sympy.core.add.Add`\s are collected to try join constants so `e^x (C_1 \cos(x) + C_2 \cos(x))` will simplify to `e^x C_1 \cos(x)`; 2. powers with exponents that are :py:class:`~sympy.core.add.Add`\s are expanded so `e^{C_1 + x}` will be simplified to `C_1 e^x`. Use :py:meth:`~sympy.solvers.ode.constant_renumber` to renumber constants after simplification or else arbitrary numbers on constants may appear, e.g. `C_1 + C_3 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 `C_1` and `C_2` in an expression, when `C_1` is actually equal to `C_2`. Use your discretion in such situations, and also take advantage of the ability to use hints in :py:meth:`~sympy.solvers.ode.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. if type(symbolname) is tuple: x, endnumber, startnumber, constantsymbols = symbolname else: constantsymbols = symbols( symbolname + '%i:%i' % (startnumber, endnumber + 1)) x = independentsymbol con_set = set(constantsymbols) ARGS = None, None, None, ( x, endnumber, startnumber, constantsymbols) if isinstance(expr, Equality): # For now, only treat the special case where one side of the equation # is a constant if expr.lhs in con_set: return Eq(expr.lhs, constantsimp(expr.rhs + expr.lhs, *ARGS) - 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 con_set: return Eq(constantsimp(expr.lhs + expr.rhs, *ARGS) - expr.rhs, expr.rhs) else: return Eq(constantsimp(expr.lhs, *ARGS), constantsimp(expr.rhs, *ARGS)) if not hasattr(expr, 'has') or not expr.has(*constantsymbols): return expr else: # ================ pre-processing ================ def _take(i): # return the lowest numbered constant symbol that appears in ``i`` # else return ``i`` c = i.free_symbols & con_set if c: return min(c, key=str) return i if not (expr.has(x) and x in expr.free_symbols): return constantsymbols[0] # collect terms to get constants together new_expr = terms_gcd(expr, clear=False, deep=True, expand=False) 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] 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 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.xreplace(dict(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.xreplace(dict(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], *ARGS) 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, *ARGS) for t in expr.args] return expr.func(*newfuncargs) else: newexpr = expr.func(*newargs) if reeval: return constantsimp(newexpr, *ARGS) else: return newexpr def constant_renumber(expr, symbolname, startnumber, endnumber): r""" 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 :py:class:`~sympy.core.symbol.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 :py:meth:`~sympy.solvers.ode.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): r""" 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. Hope there aren't constants inside # of some other class, because they won't be renumbered. return expr elif expr.is_Piecewise: return expr elif expr in constantsymbols: # Renumbering happens here newconst = Symbol(symbolname + str(newstartnumber)) newstartnumber += 1 return newconst else: from sympy.core.containers import Tuple if expr.is_Function or expr.is_Pow or isinstance(expr, Tuple): 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): r""" Converts a solution with Integrals in it into an actual solution. For most hints, this simply runs ``expr.doit()``. """ global y x = func.args[0] f = func.func if hint == "1st_exact": sol = (expr.doit()).subs(y, f(x)) del y elif hint == "1st_exact_Integral": sol = expr.subs(y, f(x)) del y elif hint == "nth_linear_constant_coeff_homogeneous": sol = expr elif not hint.endswith("_Integral"): sol = expr.doit() else: sol = expr return sol # 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. 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 .. math:: P(x, y) \,\partial{}x + Q(x, y) \,\partial{}y = 0 is exact if there is some function `F(x, y)` such that `P(x, y) = \partial{}F/\partial{}x` and `Q(x, y) = \partial{}F/\partial{}y`. It can be shown that a necessary and sufficient condition for a first order ODE to be exact is that `\partial{}P/\partial{}y = \partial{}Q/\partial{}x`. 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= 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) e = r[r['e']] d = r[r['d']] global y # This is the only way to pass dummy y to _handle_Integral y = r['y'] C1 = Symbol('C1') # Refer Joel Moses, "Symbolic Integration - The Stormy Decade", # Communications of the ACM, Volume 14, Number 8, August 1971, pp. 558 # which gives the method to solve an exact differential equation. sol = C.Integral(d, x) + C.Integral((e - (C.Integral(d, x).diff(y))), y) return Eq(sol, C1) 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 :py:meth:`~sympy.solvers.ode.ode_sol_simplicity`. See the :py:meth:`~sympy.solvers.ode.ode_1st_homogeneous_coeff_subs_indep_div_dep` and :py:meth:`~sympy.solvers.ode.ode_1st_homogeneous_coeff_subs_dep_div_indep` docstrings for more information on these hints. Note that there is no ``ode_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) / log(f(x)) = log(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)) 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 `u_1 = \frac{\text{}}{\text{}}`. This is a differential equation .. math:: P(x, y) + Q(x, y) dy/dx = 0 such that `P` and `Q` are homogeneous and of the same order. A function `F(x, y)` is homogeneous of order `n` if `F(x t, y t) = 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 :py:meth:`~sympy.solvers.ode.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 = u_1 x` (i.e. `u_1 = y/x`) will turn the differential equation into an equation separable in the variables `x` and `u`. If `h(u_1)` is the function that results from making the substitution `u_1 = f(x)/x` on `P(x, f(x))` and `g(u_2)` 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)) f'(x) = 0`, then the general solution is:: >>> from sympy import Function, dsolve, pprint >>> from sympy.abc import x >>> f, g, h = 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(x) = C1 + | ---------------- d(u1) | u1*h(u1) + g(u1) | / Where `u_1 h(u_1) + g(u_1) \ne 0` and `x \ne 0`. See also the docstrings of :py:meth:`~sympy.solvers.ode.ode_1st_homogeneous_coeff_best` and :py:meth:`~sympy.solvers.ode.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 / log(x) = log(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 u = Dummy('u') u1 = Dummy('u1') # u1 == f(x)/x r = match # d+e*diff(f(x),x) C1 = Symbol('C1') xarg = match.get('xarg', 0) yarg = match.get('yarg', 0) 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) sol = sol.subs(f(x), u).subs(((u, u - yarg), (x, x - xarg), (u, f(x)))) return sol 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 `u_2 = \frac{\text{}}{\text{}}`. This is a differential equation .. math:: P(x, y) + Q(x, y) dy/dx = 0 such that `P` and `Q` are homogeneous and of the same order. A function `F(x, y)` is homogeneous of order `n` if `F(x t, y t) = 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 :py:meth:`~sympy.solvers.ode.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 = u_2 y` (i.e. `u_2 = x/y`) will turn the differential equation into an equation separable in the variables `y` and `u_2`. If `h(u_2)` is the function that results from making the substitution `u_2 = x/f(x)` on `P(x, f(x))` and `g(u_2)` 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)) f'(x) = 0`, then the general solution is: >>> from sympy import Function, dsolve, pprint >>> from sympy.abc import x >>> f, g, h = 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) | / f(x) = C1*e Where `u_2 g(u_2) + h(u_2) \ne 0` and `f(x) \ne 0`. See also the docstrings of :py:meth:`~sympy.solvers.ode.ode_1st_homogeneous_coeff_best` and :py:meth:`~sympy.solvers.ode.ode_1st_homogeneous_coeff_subs_dep_div_indep`. Examples ======== >>> from sympy import Function, pprint, 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_indep_div_dep', ... simplify=False)) / 2 \ | 3*x | log|----- + 1| | 2 | \f (x) / log(f(x)) = log(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 u = Dummy('u') u2 = Dummy('u2') # u2 == x/f(x) r = match # d+e*diff(f(x),x) C1 = Symbol('C1') xarg = match.get('xarg', 0) # If xarg present take xarg, else zero yarg = match.get('yarg', 0) # If yarg present take yarg, else zero 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) sol = sol.subs(f(x), u).subs(((u, u - yarg), (x, x - xarg), (u, f(x)))) return sol # XXX: Should this function maybe go somewhere else? def homogeneous_order(eq, *symbols): r""" 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, \cdots)` is homogeneous of order `n` if `f(t x, t y, \cdots) = t^n f(x, y, \cdots)`. 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 :py:meth:`~solvers.ode.ode_1st_homogeneous_coeff_subs_dep_div_indep` and :py:meth:`~solvers.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 # assuming order of a nested function can only be equal to zero if isinstance(eq, Function): return None if homogeneous_order( eq.args[0], *tuple(symset)) != 0 else S.Zero # 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 def ode_1st_linear(eq, func, order, match): r""" Solves 1st order linear differential equations. These are differential equations of the form .. math:: dy/dx + P(x) y = Q(x)\text{.} These kinds of differential equations can be solved in a general way. The integrating factor `e^{\int P(x) \,dx}` 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 = 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) f = match.get('u', f(x)) # take almost-linear u if present, else f(x) return Eq(f, (tt + C1)/t) def ode_Bernoulli(eq, func, order, match): r""" Solves Bernoulli differential equations. These are equations of the form .. math:: dy/dx + P(x) y = Q(x) y^n\text{, }n \ne 1`\text{.} The substitution `w = 1/y^{1-n}` will transform an equation of this form into one that is linear (see the docstring of :py:meth:`~sympy.solvers.ode.ode_1st_linear`). The general solution is:: >>> from sympy import Function, dsolve, Eq, pprint >>> from sympy.abc import x, n >>> f, P, Q = 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 the equation is separable when `n = 1` (see the docstring of :py:meth:`~sympy.solvers.ode.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']]))) def ode_Riccati_special_minus2(eq, func, order, match): r""" The general Riccati equation has the form .. math:: dy/dx = f(x) y^2 + g(x) y + h(x)\text{.} 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, wrap_line=False) / / __________________ \\ | __________________ | / 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)) def ode_Liouville(eq, func, order, match): r""" Solves 2nd order Liouville differential equations. The general form of a Liouville ODE is .. math:: \frac{d^2 y}{dx^2} + g(y) \left(\! \frac{dy}{dx}\!\right)^2 + h(x) \frac{dy}{dx}\text{.} The general solution is: >>> from sympy import Function, dsolve, Eq, pprint, diff >>> from sympy.abc import x >>> f, g, h = 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 ode_2nd_power_series_ordinary(eq, func, order, match): r""" Gives a power series solution to a second order homogeneous differential equation with polynomial coefficients at an ordinary point. A homogenous differential equation is of the form .. math :: P(x)\frac{d^2y}{dx^2} + Q(x)\frac{dy}{dx} + R(x) = 0 For simplicity it is assumed that `P(x)`, `Q(x)` and `R(x)` are polynomials, it is sufficient that `\frac{Q(x)}{P(x)}` and `\frac{R(x)}{P(x)}` exists at `x0`. A recurrence relation is obtained by substituting `y` as `\sum_{n=0}^\infty anx^n`, in the differential equation, and equating the nth term. Using this relation various terms can be generated. Examples ======== >>> from sympy import dsolve, Function, pprint >>> from sympy.abc import x, y >>> f = Function("f") >>> eq = f(x).diff(x, 2) + f(x) >>> pprint(dsolve(eq, hint='2nd_power_series_ordinary')) / 2 \ / 4 2 \ | x | |x x | / 6\ f(x) = C1*x*|- -- + 1| + C0*|-- - -- + 1| + O\x / \ 6 / \24 2 / References ========== - http://tutorial.math.lamar.edu/Classes/DE/SeriesSolutions.aspx - George E. Simmons, "Differential Equations with Applications and Historical Notes", p.p 176 - 184 """ x = func.args[0] f = func.func C0, C1 = symbols("C0 C1") n = Dummy("n") s = Wild("s") k = Wild("k", exclude=[x]) x0 = match.get('x0') terms = match.get('terms', 5) p = match[match['a3']] q = match[match['b3']] r = match[match['c3']] seriesdict = {} recurr = Function("r") # Generating the recurrence relation which works this way # a] For the second order term the summation begins at n = 2. The coefficient # p is multiplied with an*(n - 1)*(n - 2)*x**n-2 and a substitution is made such that # the exponent of x becomes n. # For example, if p is x, then the second degree recurrence term is # an*(n - 1)*(n - 2)*x**n-1, substituting (n - 1) as n, it transforms to # an+1*n*(n - 1)*x**n. # A similar process is done with the first order and zeroth order term. coefflist = [(recurr(n), r), (n*recurr(n), q), (n*(n - 1)*recurr(n), p)] for index, coeff in enumerate(coefflist): if coeff[1]: f2 = powsimp(expand((coeff[1]*(x - x0)**(n - index)).subs(x, x + x0))) if f2.is_Add: addargs = f2.args else: addargs = [f2] for arg in addargs: powm = arg.match(s*x**k) term = coeff[0]*powm[s] if not powm[k].is_Symbol: term = term.subs(n, n - powm[k].as_independent(n)[0]) startind = powm[k].subs(n, index) # Seeing if the startterm can be reduced further. # If it vanishes for n lesser than startind, it is # equal to summation from n. if startind: for i in reversed(range(startind)): if not term.subs(n, i): seriesdict[term] = i else: seriesdict[term] = i + 1 break else: seriesdict[term] = S(0) # Stripping of terms so that the sum starts with the same number. teq = S(0) suminit = seriesdict.values() rkeys = seriesdict.keys() req = Add(*rkeys) if any(suminit): maxval = max(suminit) for term in seriesdict: val = seriesdict[term] if val != maxval: for i in range(val, maxval): teq += term.subs(n, val) finaldict = {} if teq: fargs = teq.atoms(AppliedUndef) if len(fargs) == 1: finaldict[fargs.pop()] = 0 else: maxf = max(fargs, key = lambda x: x.args[0]) sol = solve(teq, maxf) if isinstance(sol, list): sol = sol[0] finaldict[maxf] = sol # Finding the recurrence relation in terms of the largest term. fargs = req.atoms(AppliedUndef) maxf = max(fargs, key = lambda x: x.args[0]) minf = min(fargs, key = lambda x: x.args[0]) if minf.args[0].is_Symbol: startiter = 0 else: startiter = -minf.args[0].as_independent(n)[0] lhs = maxf rhs = solve(req, maxf) if isinstance(rhs, list): rhs = rhs[0] # Checking how many values are already present tcounter = len([t for t in finaldict.values() if t]) for count in range(tcounter, terms - 3): # Assuming c0 and c1 to be arbitrary #while tcounter < terms - 2: # Assuming c0 and c1 to be arbitrary check = rhs.subs(n, startiter) nlhs = lhs.subs(n, startiter) nrhs = check.subs(finaldict) finaldict[nlhs] = nrhs startiter += 1 # Post processing series = C0 + C1*(x - x0) for term in finaldict: if finaldict[term]: fact = term.args[0] series += (finaldict[term].subs([(recurr(0), C0), (recurr(1), C1)])*( x - x0)**fact) series = collect(expand_mul(series), [C0, C1]) + Order(x**terms) return Eq(f(x), series) def ode_2nd_power_series_regular(eq, func, order, match): r""" Gives a power series solution to a second order homogeneous differential equation with polynomial coefficients at a regular point. A second order homogenous differential equation is of the form .. math :: P(x)\frac{d^2y}{dx^2} + Q(x)\frac{dy}{dx} + R(x) = 0 A point is said to regular singular at `x0` if `x - x0\frac{Q(x)}{P(x)}` and `(x - x0)^{2}\frac{R(x)}{P(x)}` are analytic at `x0`. For simplicity `P(x)`, `Q(x)` and `R(x)` are assumed to be polynomials. The algorithm for finding the power series solutions is: 1. Try expressing `(x - x0)P(x)` and `((x - x0)^{2})Q(x)` as power series solutions about x0. Find `p0` and `q0` which are the constants of the power series expansions. 2. Solve the indicial equation `f(m) = m(m - 1) + m*p0 + q0`, to obtain the roots `m1` and `m2` of the indicial equation. 3. If `m1 - m2` is a non integer there exists two series solutions. If `m1 = m2`, there exists only one solution. If `m1 - m2` is an integer, then the existence of one solution is confirmed. The other solution may or may not exist. The power series solution is of the form `x^{m}\sum_{n=0}^\infty anx^n`. The coefficients are determined by the following recurrence relation. `an = -\frac{\sum_{k=0}^{n-1} q_{n-k} + (m + k)p_{n-k}}{f(m + n)}`. For the case in which `m1 - m2` is an integer, it can be seen from the recurrence relation that for the lower root `m`, when `n` equals the difference of both the roots, the denominator becomes zero. So if the numerator is not equal to zero, a second series solution exists. Examples ======== >>> from sympy import dsolve, Function, pprint >>> from sympy.abc import x, y >>> f = Function("f") >>> eq = x*(f(x).diff(x, 2)) + 2*(f(x).diff(x)) + x*f(x) >>> pprint(dsolve(eq)) / 6 4 2 \ | x x x | C1*|- --- + -- - -- + 1| / 4 2 \ \ 720 24 2 / | x x | / 6\ f(x) = ------------------------ + C0*|--- - -- + 1| + O\x / x \120 6 / References ========== - George E. Simmons, "Differential Equations with Applications and Historical Notes", p.p 176 - 184 """ x = func.args[0] f = func.func C0, C1 = symbols("C0 C1") n = Dummy("n") m = Dummy("m") # for solving the indicial equation s = Wild("s") k = Wild("k", exclude=[x]) x0 = match.get('x0') terms = match.get('terms', 5) p = match['p'] q = match['q'] # Generating the indicial equation indicial = [] for term in [p, q]: if not term.has(x): indicial.append(term) else: term = series(term, n=1, x0=x0) if isinstance(term, Order): indicial.append(S(0)) else: for arg in term.args: if not arg.has(x): indicial.append(arg) break p0, q0 = indicial sollist = solve(m*(m - 1) + m*p0 + q0, m) if sollist and isinstance(sollist, list) and all( [sol.is_real for sol in sollist]): serdict1 = {} serdict2 = {} if len(sollist) == 1: # Only one series solution exists in this case. m1 = m2 = sollist.pop() if terms-m1-1 <= 0: return Eq(f(x), Order(terms)) serdict1 = _frobenius(terms-m1-1, m1, p0, q0, p, q, x0, x, C0) else: m1 = sollist[0] m2 = sollist[1] if m1 < m2: m1, m2 = m2, m1 # Irrespective of whether m1 - m2 is an integer or not, one # Frobenius series solution exists. serdict1 = _frobenius(terms-m1-1, m1, p0, q0, p, q, x0, x, C0) if not (m1 - m2).is_integer: # Second frobenius series solution exists. serdict2 = _frobenius(terms-m2-1, m2, p0, q0, p, q, x0, x, C1) else: # Check if second frobenius series solution exists. serdict2 = _frobenius(terms-m2-1, m2, p0, q0, p, q, x0, x, C1, check=m1) if serdict1: finalseries1 = C0 for key in serdict1: power = int(key.name[1:]) finalseries1 += serdict1[key]*(x - x0)**power finalseries1 = (x - x0)**m1*finalseries1 finalseries2 = S(0) if serdict2: for key in serdict2: power = int(key.name[1:]) finalseries2 += serdict2[key]*(x - x0)**power finalseries2 += C1 finalseries2 = (x - x0)**m2*finalseries2 return Eq(f(x), collect(finalseries1 + finalseries2, [C0, C1]) + Order(x**terms)) def _frobenius(n, m, p0, q0, p, q, x0, x, c, check=None): r""" Returns a dict with keys as coefficients and values as their values in terms of C0 """ n = int(n) # In cases where m1 - m2 is not an integer m2 = check d = Dummy("d") numsyms = numbered_symbols("C", start=0) numsyms = [next(numsyms) for i in range(n + 1)] C0 = Symbol("C0") serlist = [] for ser in [p, q]: # Order term not present if ser.is_polynomial(x) and Poly(ser, x).degree() <= n: if x0: ser = ser.subs(x, x + x0) dict_ = Poly(ser, x).as_dict() # Order term present else: tseries = series(ser, x=x0, n=n+1) # Removing order dict_ = Poly(list(ordered(tseries.args))[: -1], x).as_dict() # Fill in with zeros, if coefficients are zero. for i in range(n + 1): if (i, ) not in dict_: dict_[(i,)] = S(0) serlist.append(dict_) pseries = serlist[0] qseries = serlist[1] indicial = d*(d - 1) + d*p0 + q0 frobdict = {} for i in range(1, n + 1): num = c*(m*pseries[(i,)] + qseries[(i,)]) for j in range(1, i): sym = Symbol("C" + str(j)) num += frobdict[sym]*((m + j)*pseries[(i - j,)] + qseries[(i - j,)]) # Checking for cases when m1 - m2 is an integer. If num equals zero # then a second Frobenius series solution cannot be found. If num is not zero # then set constant as zero and proceed. if m2 is not None and i == m2 - m: if num: return False else: frobdict[numsyms[i]] = S(0) else: frobdict[numsyms[i]] = -num/(indicial.subs(d, m+i)) return frobdict def _nth_linear_match(eq, func, order): r""" Matches a differential equation to the linear form: .. math:: a_n(x) y^{(n)} + \cdots + 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 `n`\th order linear homogeneous variable-coefficient Cauchy-Euler equidimensional ordinary differential equation. This is an equation with form `0 = a_0 f(x) + a_1 x f'(x) + a_2 x^2 f''(x) \cdots`. 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 `C_{r k} \ln^k(x) x^r`, where `C_{r k}` 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 `C_1 x^a \sin(b \log(x)) + C_2 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 :py:class:`~sympy.polys.rootoftools.RootOf` instance will be returned instead. >>> 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': , 'list': }``. 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 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 xrange(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 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".') def ode_almost_linear(eq, func, order, match): r""" Solves an almost-linear differential equation. The general form of an almost linear differential equation is .. math:: f(x) g(y) y + k(x) l(y) + m(x) = 0 \text{where} l'(y) = g(y)\text{.} This can be solved by substituting `l(y) = u(y)`. Making the given substitution reduces it to a linear differential equation of the form `u' + P(x) u + Q(x) = 0`. The general solution is >>> from sympy import Function, dsolve, Eq, pprint >>> from sympy.abc import x, y, n >>> f, g, k, l = map(Function, ['f', 'g', 'k', 'l']) >>> genform = Eq(f(x)*(l(y).diff(y)) + k(x)*l(y) + g(x)) >>> pprint(genform) d f(x)*--(l(y)) + g(x) + k(x)*l(y) = 0 dy >>> pprint(dsolve(genform, hint = 'almost_linear')) / // -y*g(x) \\ | || -------- for k(x) = 0|| | || f(x) || -y*k(x) | || || -------- | || y*k(x) || f(x) l(y) = |C1 + |< ------ ||*e | || f(x) || | ||-g(x)*e || | ||-------------- otherwise || | || k(x) || \ \\ // See Also ======== :meth:`sympy.solvers.ode.ode_1st_linear` Examples ======== >>> from sympy import Function, Derivative, pprint >>> from sympy.solvers.ode import dsolve, classify_ode >>> from sympy.abc import x >>> f = Function('f') >>> d = f(x).diff(x) >>> eq = x*d + x*f(x) + 1 >>> dsolve(eq, f(x), hint='almost_linear') f(x) == (C1 - Ei(x))*exp(-x) >>> pprint(dsolve(eq, f(x), hint='almost_linear')) -x f(x) = (C1 - Ei(x))*e References ========== - Joel Moses, "Symbolic Integration - The Stormy Decade", Communications of the ACM, Volume 14, Number 8, August 1971, pp. 558 """ # Since ode_1st_linear has already been implemented, and the # coefficients have been modified to the required form in # classify_ode, just passing eq, func, order and match to # ode_1st_linear will give the required output. return ode_1st_linear(eq, func, order, match) def _linear_coeff_match(expr, func): r""" Helper function to match hint ``linear_coefficients``. Matches the expression to the form `(a_1 x + b_1 f(x) + c_1)/(a_2 x + b_2 f(x) + c_2)` where the following conditions hold: 1. `a_1`, `b_1`, `c_1`, `a_2`, `b_2`, `c_2` are Rationals; 2. `c_1` or `c_2` are not equal to zero; 3. `a_2 b_1 - a_1 b_2` is not equal to zero. Return ``xarg``, ``yarg`` where 1. ``xarg`` = `(b_2 c_1 - b_1 c_2)/(a_2 b_1 - a_1 b_2)` 2. ``yarg`` = `(a_1 c_2 - a_2 c_1)/(a_2 b_1 - a_1 b_2)` Examples ======== >>> from sympy import Function >>> from sympy.abc import x >>> from sympy.solvers.ode import _linear_coeff_match >>> from sympy.functions.elementary.trigonometric import sin >>> f = Function('f') >>> _linear_coeff_match(( ... (-25*f(x) - 8*x + 62)/(4*f(x) + 11*x - 11)), f(x)) (1/9, 22/9) >>> _linear_coeff_match( ... sin((-5*f(x) - 8*x + 6)/(4*f(x) + x - 1)), f(x)) (19/27, 2/27) >>> _linear_coeff_match(sin(f(x)/x), f(x)) """ f = func.func x = func.args[0] def abc(eq): r''' Internal function of _linear_coeff_match that returns Rationals a, b, c if eq is a*x + b*f(x) + c, else None. ''' eq = _mexpand(eq) c = eq.as_independent(x, f(x), as_Add = True)[0] if not c.is_Rational: return a = eq.coeff(x) if not a.is_Rational: return b = eq.coeff(f(x)) if not b.is_Rational: return if eq == a*x + b*f(x) + c: return a, b, c def match(arg): r''' Internal function of _linear_coeff_match that returns Rationals a1, b1, c1, a2, b2, c2 and a2*b1 - a1*b2 of the expression (a1*x + b1*f(x) + c1)/(a2*x + b2*f(x) + c2) if one of c1 or c2 and a2*b1 - a1*b2 is non-zero, else None. ''' n, d = arg.together().as_numer_denom() m = abc(n) if m is not None: a1, b1, c1 = m m = abc(d) if m is not None: a2, b2, c2 = m d = a2*b1 - a1*b2 if (c1 or c2) and d: return a1, b1, c1, a2, b2, c2, d m = [fi.args[0] for fi in expr.atoms(Function) if fi.func != f and fi.nargs == 1 and not fi.args[0].is_Function] or set([expr]) m1 = match(m.pop()) if m1 and all(match(mi) == m1 for mi in m): a1, b1, c1, a2, b2, c2, denom = m1 return (b2*c1 - b1*c2)/denom, (a1*c2 - a2*c1)/denom def ode_linear_coefficients(eq, func, order, match): r""" Solves a differential equation with linear coefficients. The general form of a differential equation with linear coefficients is .. math:: y' + F\left(\!\frac{a_1 x + b_1 y + c_1}{a_2 x + b_2 y + c_2}\!\right) = 0\text{,} where `a_1`, `b_1`, `c_1`, `a_2`, `b_2`, `c_2` are constants and `a_1 b_2 - a_2 b_1 \ne 0`. This can be solved by substituting: .. math:: x = x' + \frac{b_2 c_1 - b_1 c_2}{a_2 b_1 - a_1 b_2} y = y' + \frac{a_1 c_2 - a_2 c_1}{a_2 b_1 - a_1 b_2}\text{.} This substitution reduces the equation to a homogeneous differential equation. See Also ======== :meth:`sympy.solvers.ode.ode_1st_homogeneous_coeff_best` :meth:`sympy.solvers.ode.ode_1st_homogeneous_coeff_subs_indep_div_dep` :meth:`sympy.solvers.ode.ode_1st_homogeneous_coeff_subs_dep_div_indep` Examples ======== >>> from sympy import Function, Derivative, pprint >>> from sympy.solvers.ode import dsolve, classify_ode >>> from sympy.abc import x >>> f = Function('f') >>> df = f(x).diff(x) >>> eq = (x + f(x) + 1)*df + (f(x) - 6*x + 1) >>> dsolve(eq, hint='linear_coefficients') [f(x) == -x - sqrt(C1 + 7*x**2) - 1, f(x) == -x + sqrt(C1 + 7*x**2) - 1] >>> pprint(dsolve(eq, hint='linear_coefficients')) ___________ ___________ / 2 / 2 [f(x) = -x - \/ C1 + 7*x - 1, f(x) = -x + \/ C1 + 7*x - 1] References ========== - Joel Moses, "Symbolic Integration - The Stormy Decade", Communications of the ACM, Volume 14, Number 8, August 1971, pp. 558 """ return ode_1st_homogeneous_coeff_best(eq, func, order, match) def ode_separable_reduced(eq, func, order, match): r""" Solves a differential equation that can be reduced to the separable form. The general form of this equation is .. math:: y' + (y/x) H(x^n y) = 0\text{}. This can be solved by substituting `u(y) = x^n y`. The equation then reduces to the separable form `\frac{u'}{u (\mathrm{power} - H(u))} - \frac{1}{x} = 0`. The general solution is: >>> from sympy import Function, dsolve, Eq, pprint >>> from sympy.abc import x, n >>> f, g = map(Function, ['f', 'g']) >>> genform = f(x).diff(x) + (f(x)/x)*g(x**n*f(x)) >>> pprint(genform) / n \ d f(x)*g\x *f(x)/ --(f(x)) + --------------- dx x >>> pprint(dsolve(genform, hint='separable_reduced')) n x *f(x) / | | 1 | ------------ dy = C1 + log(x) | y*(n - g(y)) | / See Also ======== :meth:`sympy.solvers.ode.ode_separable` Examples ======== >>> from sympy import Function, Derivative, pprint >>> from sympy.solvers.ode import dsolve, classify_ode >>> from sympy.abc import x >>> f = Function('f') >>> d = f(x).diff(x) >>> eq = (x - x**2*f(x))*d - f(x) >>> dsolve(eq, hint='separable_reduced') [f(x) == (-sqrt(C1*x**2 + 1) + 1)/x, f(x) == (sqrt(C1*x**2 + 1) + 1)/x] >>> pprint(dsolve(eq, hint='separable_reduced')) ___________ ___________ / 2 / 2 - \/ C1*x + 1 + 1 \/ C1*x + 1 + 1 [f(x) = --------------------, f(x) = ------------------] x x References ========== - Joel Moses, "Symbolic Integration - The Stormy Decade", Communications of the ACM, Volume 14, Number 8, August 1971, pp. 558 """ # Arguments are passed in a way so that they are coherent with the # ode_separable function x = func.args[0] f = func.func y = Dummy('y') u = match['u'].subs(match['t'], y) ycoeff = 1/(y*(match['power'] - u)) m1 = {y: 1, x: -1/x, 'coeff': 1} m2 = {y: ycoeff, x: 1, 'coeff': 1} r = {'m1': m1, 'm2': m2, 'y': y, 'hint': x**match['power']*f(x)} return ode_separable(eq, func, order, r) def ode_1st_power_series(eq, func, order, match): r""" The power series solution is a method which gives the Taylor series expansion to the solution of a differential equation. For a first order differential equation `\frac{dy}{dx} = h(x, y)`, a power series solution exists at a point `x = x0` if `h(x, y)` is analytic at `x0`. The solution is given by .. math:: f(x0) + \sum_{n = 1}^{\infty} \frac{f^n(x)(x0)(x - x0)^n}{n!} The following algorithm is followed, till the required number of terms are generated. 1. F_1 = `h(x, y)` 2. F_n+1 = \frac{\partial F_n}{\partial x} + \frac{\partial F_n}{\partial y}F_1 Examples ======== >>> from sympy import Function, Derivative, pprint, exp >>> from sympy.solvers.ode import dsolve >>> from sympy.abc import x >>> f = Function('f') >>> eq = exp(x)*(f(x).diff(x)) - f(x) >>> pprint(dsolve(eq, hint='1st_power_series')) 3 4 5 C0*x C0*x C0*x / 6\ f(x) = C0 + C0*x - ----- + ----- + ----- + O\x / 6 24 60 References ========== - Travis W. Walker, Analytic power series technique for solving first-order differential equations, p.p 17, 18 """ x = func.args[0] y = match['y'] f = func.func h = -match[match['d']]/match[match['e']] C0 = Symbol("C0") point = match.get('f0') value = match.get('f0val') terms = match.get('terms') # First term F = h if not h: return Eq(f(x), value) # Initialisation series = value if terms > 1: hc = h.subs({x: point, y: value}) if hc.has(oo) or hc.has(NaN) or hc.has(zoo): # Derivative does not exist, not analytic return Eq(f(x), oo) elif hc: series += hc*(x - point) for factcount in range(2, terms): Fnew = F.diff(x) + F.diff(y)*h Fnewc = Fnew.subs({x: point, y: value}) # Same logic as above if Fnewc.has(oo) or Fnewc.has(NaN) or Fnewc.has(-oo) or Fnewc.has(zoo): return Eq(f(x), oo) series += Fnewc*((x - point)**factcount)/factorial(factcount) F = Fnew series += Order(x**terms) return Eq(f(x), series) def ode_nth_linear_constant_coeff_homogeneous(eq, func, order, match, returns='sol'): r""" Solves an `n`\th order linear homogeneous differential equation with constant coefficients. This is an equation of the form .. math:: a_n f^{(n)}(x) + a_{n-1} f^{(n-1)}(x) + \cdots + a_1 f'(x) + a_0 f(x) = 0\text{.} 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} + \cdots + a_1 m + a_0 = 0`. The solution will then be the sum of `C_n x^i e^{r x}` terms, for each where `C_n` is an arbitrary constant, `r` is a root of the characteristic equation and `i` 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 `C_1 e^{3 x} + C_2 x e^{3 x}`). The exponential is usually expanded for complex roots using Euler's equation `e{I x} = \cos(x) + I \sin(x)`. Complex roots always come in conjugate pairs in polynomials with real coefficients, so the two roots will be represented (after simplifying the constants) as `e^{a x} \left(C_1 \cos(b x) + C_2 \sin(b x)\right)`. If SymPy cannot find exact roots to the characteristic equation, a :py:class:`~sympy.polys.rootoftools.RootOf` instance will be return instead. >>> 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': , 'list': }``. 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 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 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".') def ode_nth_linear_constant_coeff_undetermined_coefficients(eq, func, order, match): r""" Solves an `n`\th order linear differential equation with constant coefficients using the method of undetermined coefficients. This method works on differential equations of the form .. math:: a_n f^{(n)}(x) + a_{n-1} f^{(n-1)}(x) + \cdots + a_1 f'(x) + a_0 f(x) = P(x)\text{,} 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 e^{b x} \sin(c x + d)` or `a x^i e^{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 e^{2 x}`, `x \sin(x)`, and `e^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^2(x)` 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): r""" Helper function for the method of undetermined coefficients. See the :py:meth:`~sympy.solvers.ode.ode_nth_linear_constant_coeff_undetermined_coefficients` docstring for more information on this method. The parameter ``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 `%s` using the " "method of undetermined coefficients " "(unable to solve for coefficients)." % eq) psol = trialfunc.subs(coeffvals) return Eq(f(x), gsol.rhs + psol) def _undetermined_coefficients_match(expr, x): r""" 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 `e^{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^n(x) \cos^m(y)` 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^2(x)` 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): r""" 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([])): r""" 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): r""" 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 def ode_nth_linear_constant_coeff_variation_of_parameters(eq, func, order, match): r""" Solves an `n`\th order linear differential equation with constant coefficients using the method of variation of parameters. This method works on any differential equations of the form .. math:: f^{(n)}(x) + a_{n-1} f^{(n-1)}(x) + \cdots + a_1 f'(x) + a_0 f(x) = P(x)\text{.} This method works by assuming that the particular solution takes the form .. math:: \sum_{x=1}^{n} c_i(x) y_i(x)\text{,} where `y_i` is the `i`\th solution to the homogeneous equation. The solution is then solved using Wronskian's and Cramer's Rule. The particular solution is given by .. math:: \sum_{x=1}^n \left( \int \frac{W_i(x)}{W(x)} \,dx \right) y_i(x) \text{,} 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 `i`\th column replaced with `[0, 0, \cdots, 0, P(x)]`. This method is general enough to solve any `n`\th 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 :py:meth:`~sympy.solvers.ode.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): r""" Helper function for the method of variation of parameters. See the :py:meth:`~sympy.solvers.ode.ode_nth_linear_constant_coeff_variation_of_parameters` docstring for more information on this method. The parameter ``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([sol for sol in gensols if sol != 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) 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) \tfrac{dy}{dx} = Q(x)`. The solution can then just be found by rearranging terms and integrating: `\int P(y) \,dy = \int Q(x) \,dx`. This hint uses :py:meth:`sympy.simplify.simplify.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. :py:meth:`~sympy.simplify.simplify.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)\cdot{}Q(y)`. The general solution is:: >>> from sympy import Function, dsolve, Eq, pprint >>> from sympy.abc import x >>> a, b, c, d, f = 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} u = r.get('hint', f(x)) # get u from separable_reduced else get f(x) return Eq(C.Integral(r['m2']['coeff']*r['m2'][r['y']]/r['m1'][r['y']], (r['y'], None, u)), C.Integral(-r['m1']['coeff']*r['m1'][x]/ r['m2'][x], x) + C1) def checkinfsol(eq, infinitesimals, func=None, order=None): r""" This function is used to check if the given infinitesimals are the actual infinitesimals of the given first order differential equation. This method is specific to the Lie Group Solver of ODEs. As of now, it simply checks, by substituting the infinitesimals in the partial differential equation. .. math:: \frac{\partial \eta}{\partial x} + \left(\frac{\partial \eta}{\partial y} - \frac{\partial \xi}{\partial x}\right)*h - \frac{\partial \xi}{\partial y}*h^{2} - \xi\frac{\partial h}{\partial x} - \eta\frac{\partial h}{\partial y} = 0 where `\eta`, and `\xi` are the infinitesimals and `h(x,y) = \frac{dy}{dx}` The infinitesimals should be given in the form of a list of dicts ``[{xi(x, y): inf, eta(x, y): inf}]``, corresponding to the output of the function infinitesimals. It returns a list of values of the form ``[(True/False, sol)]`` where ``sol`` is the value obtained after substituting the infinitesimals in the PDE. If it is ``True``, then ``sol`` would be 0. """ if isinstance(eq, Equality): eq = eq.lhs - eq.rhs if not func: eq, func = _preprocess(eq) variables = func.args if len(variables) != 1: raise ValueError("ODE's have only one independent variable") else: x = variables[0] if not order: order = ode_order(eq, func) if order != 1: raise NotImplementedError("Lie groups solver has been implemented " "only for first order differential equations") else: df = func.diff(x) a = Wild('a', exclude = [df]) b = Wild('b', exclude = [df]) match = collect(expand(eq), df).match(a*df + b) if match: h = -simplify(match[b]/match[a]) else: try: sol = solve(eq, df) except NotImplementedError: raise NotImplementedError("Infinitesimals for the " "first order ODE could not be found") else: h = sol[0] # Find infinitesimals for one solution y = Dummy('y') h = h.subs(func, y) xi = Function('xi')(x, y) eta = Function('eta')(x, y) dxi = Function('xi')(x, func) deta = Function('eta')(x, func) pde = (eta.diff(x) + (eta.diff(y) - xi.diff(x))*h - (xi.diff(y))*h**2 - xi*(h.diff(x)) - eta*(h.diff(y))) soltup = [] for sol in infinitesimals: tsol = {xi: S(sol[dxi]).subs(func, y), eta: S(sol[deta]).subs(func, y)} sol = simplify(pde.subs(tsol).doit()) if sol: soltup.append((False, sol.subs(y, func))) else: soltup.append((True, 0)) return soltup def ode_lie_group(eq, func, order, match): r""" This hint implements the Lie group method of solving first order differential equations. The aim is to convert the given differential equation from the given coordinate given system into another coordinate system where it becomes invariant under the one-parameter Lie group of translations. The converted ODE is quadrature and can be solved easily. It makes use of the :py:meth:`sympy.solvers.ode.infinitesimals` function which returns the infinitesimals of the transformation. The coordinates `r` and `s` can be found by solving the following Partial Differential Equations. .. math :: \xi\frac{\partial r}{\partial x} + \eta\frac{\partial r}{\partial y} = 0 .. math :: \xi\frac{\partial s}{\partial x} + \eta\frac{\partial s}{\partial y} = 1 The differential equation becomes separable in the new coordinate system .. math :: \frac{ds}{dr} = \frac{\frac{\partial s}{\partial x} + h(x, y)\frac{\partial s}{\partial y}}{ \frac{\partial r}{\partial x} + h(x, y)\frac{\partial r}{\partial y}} After finding the solution by integration, it is then converted back to the original coordinate system by subsituting `r` and `s` in terms of `x` and `y` again. Examples ======== >>> from sympy import Function, dsolve, Eq, exp, pprint >>> from sympy.abc import x >>> f = Function('f') >>> pprint(dsolve(f(x).diff(x) + 2*x*f(x) - x*exp(-x**2), f(x), ... hint='lie_group')) / 2\ 2 | x | -x f(x) = |C1 + --|*e \ 2 / References ========== - Solving differential equations by Symmetry Groups, John Starrett, pp. 1 - pp. 14 """ from sympy.integrals.integrals import integrate from sympy.solvers.pde import pdsolve heuristics = lie_heuristics inf = {} f = func.func x = func.args[0] df = func.diff(x) xi = Function("xi") eta = Function("eta") a = Wild('a', exclude = [df]) b = Wild('b', exclude = [df]) xis = match.pop('xi') etas = match.pop('eta') if match: h = -simplify(match[match['d']]/match[match['e']]) y = match['y'] else: try: sol = solve(eq, df) except NotImplementedError: raise NotImplementedError("Unable to solve the differential equation " + str(eq) + " by the lie group method") else: y = Dummy("y") h = sol[0].subs(func, y) if xis is not None and etas is not None: inf = [{xi(x, f(x)): S(xis), eta(x, f(x)): S(etas)}] if not checkinfsol(eq, inf, func=f(x), order=1)[0][0]: raise ValueError("The given infinitesimals xi and eta" " are not the infinitesimals to the given equation") else: heuristics = ["user_defined"] match = {'h': h, 'y': y} # This is done so that if: # a] solve raises a NotImplementedError. # b] any heuristic raises a ValueError # another heuristic can be used. tempsol = [] # Used by solve below for heuristic in heuristics: try: if not inf: inf = infinitesimals(eq, hint=heuristic, func=func, order=1, match=match) except ValueError: continue else: for infsim in inf: xiinf = (infsim[xi(x, func)]).subs(func, y) etainf = (infsim[eta(x, func)]).subs(func, y) # This condition creates recursion while using pdsolve. # Since the first step while solving a PDE of form # a*(f(x, y).diff(x)) + b*(f(x, y).diff(y)) + c = 0 # is to solve the ODE dy/dx = b/a if simplify(etainf/xiinf) == h: continue rpde = f(x, y).diff(x)*xiinf + f(x, y).diff(y)*etainf r = pdsolve(rpde, func=f(x, y)).rhs s = pdsolve(rpde - 1, func=f(x, y)).rhs newcoord = [_lie_group_remove(coord) for coord in [r, s]] r = Dummy("r") s = Dummy("s") C1 = Symbol("C1") rcoord = newcoord[0] scoord = newcoord[-1] try: sol = solve([r - rcoord, s - scoord], x, y, dict=True) except NotImplementedError: continue else: sol = sol[0] xsub = sol[x] ysub = sol[y] num = simplify(scoord.diff(x) + scoord.diff(y)*h) denom = simplify(rcoord.diff(x) + rcoord.diff(y)*h) if num and denom: diffeq = simplify((num/denom).subs([(x, xsub), (y, ysub)])) sep = separatevars(diffeq, symbols=[r, s], dict=True) if sep: # Trying to separate, r and s coordinates deq = integrate((1/sep[s]), s) + C1 - integrate(sep['coeff']*sep[r], r) # Substituting and reverting back to original coordinates deq = deq.subs([(r, rcoord), (s, scoord)]) try: sdeq = solve(deq, y) except NotImplementedError: tempsol.append(deq) else: if len(sdeq) == 1: return Eq(f(x), sdeq.pop()) else: return [Eq(f(x), sol) for sol in sdeq] elif denom: # (ds/dr) is zero which means s is constant return Eq(f(x), solve(scoord - C1, y)[0]) elif num: # (dr/ds) is zero which means r is constant return Eq(f(x), solve(rcoord - C1, y)[0]) # If nothing works, return solution as it is, without solving for y if tempsol: if len(tempsol) == 1: return Eq(tempsol.pop().subs(y, f(x)), 0) else: return [Eq(sol.subs(y, f(x)), 0) for sol in tempsol] raise NotImplementedError("The given ODE " + str(eq) + " cannot be solved by" + " the lie group method") def _lie_group_remove(coords): r""" This function is strictly meant for internal use by the Lie group ODE solving method. It replaces arbitrary functions returned by pdsolve with either 0 or 1 or the args of the arbitrary function. The algorithm used is: 1] If coords is an instance of an Undefined Function, then the args are returned 2] If the arbitrary function is present in an Add object, it is replaced by zero. 3] If the arbitrary function is present in an Mul object, it is replaced by one. 4] If coords has no Undefined Function, it is returned as it is. Examples ======== >>> from sympy.solvers.ode import _lie_group_remove >>> from sympy import Function >>> from sympy.abc import x, y >>> F = Function("F") >>> eq = x**2*y >>> _lie_group_remove(eq) x**2*y >>> eq = F(x**2*y) >>> _lie_group_remove(eq) x**2*y >>> eq = y**2*x + F(x**3) >>> _lie_group_remove(eq) x*y**2 >>> eq = (F(x**3) + y)*x**4 >>> _lie_group_remove(eq) x**4*y """ if isinstance(coords, AppliedUndef): return coords.args[0] elif coords.is_Add: subfunc = coords.atoms(AppliedUndef) if subfunc: for func in subfunc: coords = coords.subs(func, 0) return coords elif coords.is_Pow: base, expr = coords.as_base_exp() base = _lie_group_remove(base) expr = _lie_group_remove(expr) return base**expr elif coords.is_Mul: mulargs = [] coordargs = coords.args for arg in coordargs: if not isinstance(coords, AppliedUndef): mulargs.append(_lie_group_remove(arg)) return Mul(*mulargs) return coords def infinitesimals(eq, func=None, order=None, hint='default', match=None): r""" The infinitesimal functions of an ordinary differential equation, `\xi(x,y)` and `\eta(x,y)`, are the infinitesimals of the Lie group of point transformations for which the differential equation is invariant. So, the ODE `y'=f(x,y)` would admit a Lie group `x^*=X(x,y;\varepsilon)=x+\varepsilon\xi(x,y)`, `y^*=Y(x,y;\varepsilon)=y+\varepsilon\eta(x,y)` such that `(y^*)'=f(x^*, y^*)`. A change of coordinates, to `r(x,y)` and `s(x,y)`, can be performed so this Lie group becomes the translation group, `r^*=r` and `s^*=s+\varepsilon`. They are tangents to the coordinate curves of the new system. Consider the transformation `(x, y) \to (X, Y)` such that the differential equation remains invariant. `\xi` and `\eta` are the tangents to the transformed coordinates `X` and `Y`, at `\varepsilon=0`. .. math:: \left(\frac{\partial X(x,y;\varepsilon)}{\partial\varepsilon }\right)|_{\varepsilon=0} = \xi, \left(\frac{\partial Y(x,y;\varepsilon)}{\partial\varepsilon }\right)|_{\varepsilon=0} = \eta, The infinitesimals can be found by solving the following PDE: >>> from sympy import Function, diff, Eq, pprint >>> from sympy.abc import x, y >>> xi, eta, h = map(Function, ['xi', 'eta', 'h']) >>> h = h(x, y) # dy/dx = h >>> eta = eta(x, y) >>> xi = xi(x, y) >>> genform = Eq(eta.diff(x) + (eta.diff(y) - xi.diff(x))*h ... - (xi.diff(y))*h**2 - xi*(h.diff(x)) - eta*(h.diff(y)), 0) >>> pprint(genform) /d d \ d 2 d |--(eta(x, y)) - --(xi(x, y))|*h(x, y) - eta(x, y)*--(h(x, y)) - h (x, y)*--(x \dy dx / dy dy d d i(x, y)) - xi(x, y)*--(h(x, y)) + --(eta(x, y)) = 0 dx dx Solving the above mentioned PDE is not trivial, and can be solved only by making intelligent assumptions for `\xi` and `\eta` (heuristics). Once an infinitesimal is found, the attempt to find more heuristics stops. This is done to optimise the speed of solving the differential equation. If a list of all the infinitesimals is needed, ``hint`` should be flagged as ``all``, which gives the complete list of infinitesimals. If the infinitesimals for a particular heuristic needs to be found, it can be passed as a flag to ``hint``. Examples ======== >>> from sympy import Function, diff >>> from sympy.solvers.ode import infinitesimals >>> from sympy.abc import x >>> f = Function('f') >>> eq = f(x).diff(x) - x**2*f(x) >>> infinitesimals(eq) [{eta(x, f(x)): exp(x**3/3), xi(x, f(x)): 0}] References ========== - Solving differential equations by Symmetry Groups, John Starrett, pp. 1 - pp. 14 """ if isinstance(eq, Equality): eq = eq.lhs - eq.rhs if not func: eq, func = _preprocess(eq) variables = func.args if len(variables) != 1: raise ValueError("ODE's have only one independent variable") else: x = variables[0] if not order: order = ode_order(eq, func) if order != 1: raise NotImplementedError("Infinitesimals for only " "first order ODE's have been implemented") else: df = func.diff(x) # Matching differential equation of the form a*df + b a = Wild('a', exclude = [df]) b = Wild('b', exclude = [df]) if match: # Used by lie_group hint h = match['h'] y = match['y'] else: match = collect(expand(eq), df).match(a*df + b) if match: h = -simplify(match[b]/match[a]) else: try: sol = solve(eq, df) except NotImplementedError: raise NotImplementedError("Infinitesimals for the " "first order ODE could not be found") else: h = sol[0] # Find infinitesimals for one solution y = Dummy("y") h = h.subs(func, y) u = Dummy("u") hx = h.diff(x) hy = h.diff(y) hinv = ((1/h).subs([(x, u), (y, x)])).subs(u, y) # Inverse ODE match = {'h': h, 'func': func, 'hx': hx, 'hy': hy, 'y': y, 'hinv': hinv} if hint == 'all': xieta = [] for heuristic in lie_heuristics: function = globals()['lie_heuristic_' + heuristic] inflist = function(match, comp=True) if inflist: xieta.extend([inf for inf in inflist if inf not in xieta]) if xieta: return xieta else: raise NotImplementedError("Infinitesimals could not be found for" "the given ODE") elif hint == 'default': for heuristic in lie_heuristics: function = globals()['lie_heuristic_' + heuristic] xieta = function(match, comp=False) if xieta: return xieta raise NotImplementedError("Infinitesimals could not be found for" " the given ODE") elif hint not in lie_heuristics: raise ValueError("Heuristic not recognized: " + hint) else: function = globals()['lie_heuristic_' + hint] xieta = function(match, comp=True) if xieta: return xieta else: raise ValueError("Infinitesimals could not be found using the" " given heuristic") def lie_heuristic_abaco1_simple(match, comp=False): r""" The first heuristic uses the following four sets of assumptions on `\xi` and `\eta` .. math:: \xi = 0, \eta = f(x) .. math:: \xi = 0, \eta = f(y) .. math:: \xi = f(x), \eta = 0 .. math:: \xi = f(y), \eta = 0 The success of this heuristic is determined by algebraic factorisation. For the first assumption `\xi = 0` and `eta` to be a function of `x`, the PDE .. math:: \frac{\partial \eta}{\partial x} + (\frac{\partial \eta}{\partial y} - \frac{\partial \xi}{\partial x})*h - \frac{\partial \xi}{\partial y}*h^{2} - \xi*\frac{\partial h}{\partial x} - \eta*\frac{\partial h}{\partial y} = 0 reduces to `f'(x) - f\frac{\partial h}{\partial y} = 0` If `\frac{\partial h}{\partial y}` is a function of `x`, then this can usually be integrated easily. A similar idea is applied to the other 3 assumptions as well. References ========== - E.S Cheb-Terrab, L.G.S Duarte and L.A,C.P da Mota, Computer Algebra Solving of First Order ODEs Using Symmetry Methods, pp. 8 """ from sympy.integrals.integrals import integrate xieta = [] y = match['y'] h = match['h'] func = match['func'] x = func.args[0] hx = match['hx'] hy = match['hy'] xi = Function('xi')(x, func) eta = Function('eta')(x, func) hysym = hy.free_symbols if y not in hysym: try: fx = exp(integrate(hy, x)) except NotImplementedError: pass else: inf = {xi: S(0), eta: fx} if not comp: return [inf] if comp and inf not in xieta: xieta.append(inf) factor = hy/h facsym = factor.free_symbols if x not in facsym: try: fy = exp(integrate(factor, y)) except NotImplementedError: pass else: inf = {xi: S(0), eta: fy.subs(y, func)} if not comp: return [inf] if comp and inf not in xieta: xieta.append(inf) factor = -hx/h facsym = factor.free_symbols if y not in facsym: try: fx = exp(integrate(factor, x)) except NotImplementedError: pass else: inf = {xi: fx, eta: S(0)} if not comp: return [inf] if comp and inf not in xieta: xieta.append(inf) factor = -hx/(h**2) facsym = factor.free_symbols if x not in facsym: try: fy = exp(integrate(factor, y)) except NotImplementedError: pass else: inf = {xi: fy.subs(y, func), eta: S(0)} if not comp: return [inf] if comp and inf not in xieta: xieta.append(inf) if xieta: return xieta def lie_heuristic_abaco1_product(match, comp=False): r""" The second heuristic uses the following two assumptions on `\xi` and `\eta` .. math:: \eta = 0, \xi = f(x)*g(y) .. math:: \eta = f(x)*g(y), \xi = 0 The first assumption of this heuristic holds good if `\frac{1}{h^{2}}\frac{\partial^2}{\partial x \partial y}\log(h)` is separable in `x` and `y`, then the separated factors containing `x` is `f(x)`, and `g(y)` is obtained by .. math:: exp^{\int f\frac{\partial}{\partial x}\left(\frac{1}{f*h}\right)\,dy} provided `f\frac{\partial}{\partial x}\left(\frac{1}{f*h}\right)` is a function of `y` only. The second assumption holds good if `\frac{dy}{dx} = h(x, y)` is rewritten as `\frac{dy}{dx} = \frac{1}{h(y, x)}` and the same properties of the first assumption satisifes. After obtaining `f(x)` and `g(y)`, the coordinates are again interchanged, to get `\eta` as `f(x)*g(y)` References ========== - E.S. Cheb-Terrab, A.D. Roche, Symmetries and First Order ODE Patterns, pp. 7 - pp. 8 """ from sympy.integrals.integrals import integrate xieta = [] y = match['y'] h = match['h'] hinv = match['hinv'] func = match['func'] x = func.args[0] xi = Function('xi')(x, func) eta = Function('eta')(x, func) inf = separatevars(((log(h).diff(y)).diff(x))/h**2, dict=True, symbols=[x, y]) if inf and inf['coeff']: fx = inf[x] gy = simplify(fx*((1/(fx*h)).diff(x))) gysyms = gy.free_symbols if x not in gysyms: gy = exp(integrate(gy, y)) inf = {eta: S(0), xi: (fx*gy).subs(y, func)} if not comp: return [inf] if comp and inf not in xieta: xieta.append(inf) u1 = Dummy("u1") inf = separatevars(((log(hinv).diff(y)).diff(x))/hinv**2, dict=True, symbols=[x, y]) if inf and inf['coeff']: fx = inf[x] gy = simplify(fx*((1/(fx*hinv)).diff(x))) gysyms = gy.free_symbols if x not in gysyms: gy = exp(integrate(gy, y)) etaval = fx*gy etaval = (etaval.subs([(x, u1), (y, x)])).subs(u1, y) inf = {eta: etaval.subs(y, func), xi: S(0)} if not comp: return [inf] if comp and inf not in xieta: xieta.append(inf) if xieta: return xieta def lie_heuristic_bivariate(match, comp=False): r""" The third heuristic assumes the infinitesimals `\xi` and `\eta` to be bi-variate polynomials in `x` and `y`. The assumption made here for the logic below is that `h` is a rational function in `x` and `y` though that may not be necessary for the infinitesimals to be bivariate polynomials. The coefficients of the infinitesimals are found out by substituting them in the PDE and grouping similar terms that are polynomials and since they form a linear system, solve and check for non trivial solutions. The degree of the assumed bivariates are increased till a certain maximum value. References ========== - Lie Groups and Differential Equations pp. 327 - pp. 329 """ h = match['h'] hx = match['hx'] hy = match['hy'] func = match['func'] x = func.args[0] y = match['y'] xi = Function('xi')(x, func) eta = Function('eta')(x, func) if h.is_rational_function(): # The maximum degree that the infinitesimals can take is # calculated by this technique. etax, etay, etad, xix, xiy, xid = symbols("etax etay etad xix xiy xid") ipde = etax + (etay - xix)*h - xiy*h**2 - xid*hx - etad*hy num, denom = cancel(ipde).as_numer_denom() deg = Poly(num, x, y).total_degree() deta = Function('deta')(x, y) dxi = Function('dxi')(x, y) ipde = (deta.diff(x) + (deta.diff(y) - dxi.diff(x))*h - (dxi.diff(y))*h**2 - dxi*hx - deta*hy) xieq = Symbol("xi0") etaeq = Symbol("eta0") for i in range(deg + 1): if i: xieq += Add(*[ Symbol("xi_" + str(power) + "_" + str(i - power))*x**power*y**(i - power) for power in range(i + 1)]) etaeq += Add(*[ Symbol("eta_" + str(power) + "_" + str(i - power))*x**power*y**(i - power) for power in range(i + 1)]) pden, denom = (ipde.subs({dxi: xieq, deta: etaeq}).doit()).as_numer_denom() pden = expand(pden) # If the individual terms are monomials, the coefficients # are grouped if pden.is_polynomial(x, y) and pden.is_Add: polyy = Poly(pden, x, y).as_dict() if polyy: symset = xieq.free_symbols.union(etaeq.free_symbols) - set([x, y]) soldict = solve(polyy.values(), *symset) if isinstance(soldict, list): soldict = soldict[0] if any(x for x in soldict.values()): xired = xieq.subs(soldict) etared = etaeq.subs(soldict) # Scaling is done by substituting one for the parameters # This can be any number except zero. dict_ = dict((sym, 1) for sym in symset) inf = {eta: etared.subs(dict_).subs(y, func), xi: xired.subs(dict_).subs(y, func)} return [inf] def lie_heuristic_chi(match, comp=False): r""" The aim of the fourth heuristic is to find the function `\chi(x, y)` that satisifies the PDE `\frac{d\chi}{dx} + h\frac{d\chi}{dx} - \frac{\partial h}{\partial y}\chi = 0`. This assumes `\chi` to be a bivariate polynomial in `x` and `y`. By intution, `h` should be a rational function in `x` and `y`. The method used here is to substitute a general binomial for `\chi` up to a certain maximum degree is reached. The coefficients of the polynomials, are calculated by by collecting terms of the same order in `x` and `y`. After finding `\chi`, the next step is to use `\eta = \xi*h + \chi`, to determine `\xi` and `\eta`. This can be done by dividing `\chi` by `h` which would give `-\xi` as the quotient and `\eta` as the remainder. References ========== - E.S Cheb-Terrab, L.G.S Duarte and L.A,C.P da Mota, Computer Algebra Solving of First Order ODEs Using Symmetry Methods, pp. 8 """ h = match['h'] hx = match['hx'] hy = match['hy'] func = match['func'] x = func.args[0] y = match['y'] xi = Function('xi')(x, func) eta = Function('eta')(x, func) if h.is_rational_function(): schi, schix, schiy = symbols("schi, schix, schiy") cpde = schix + h*schiy - hy*schi num, denom = cancel(cpde).as_numer_denom() deg = Poly(num, x, y).total_degree() chi = Function('chi')(x, y) chix = chi.diff(x) chiy = chi.diff(y) cpde = chix + h*chiy - hy*chi chieq = Symbol("chi") for i in range(1, deg + 1): chieq += Add(*[ Symbol("chi_" + str(power) + "_" + str(i - power))*x**power*y**(i - power) for power in range(i + 1)]) cnum, cden = cancel(cpde.subs({chi : chieq}).doit()).as_numer_denom() cnum = expand(cnum) if cnum.is_polynomial(x, y) and cnum.is_Add: cpoly = Poly(cnum, x, y).as_dict() if cpoly: solsyms = chieq.free_symbols - set([x, y]) soldict = solve(cpoly.values(), *solsyms) if isinstance(soldict, list): soldict = soldict[0] if any(x for x in soldict.values()): chieq = chieq.subs(soldict) dict_ = dict((sym, 1) for sym in solsyms) chieq = chieq.subs(dict_) # After finding chi, the main aim is to find out # eta, xi by the equation eta = xi*h + chi # One method to set xi, would be rearranging it to # (eta/h) - xi = (chi/h). This would mean dividing # chi by h would give -xi as the quotient and eta # as the remainder. Thanks to Sean Vig for suggesting # this method. xic, etac = div(chieq, h) inf = {eta: etac.subs(y, func), xi: -xic.subs(y, func)} return [inf] def lie_heuristic_function_sum(match, comp=False): r""" This heuristic uses the following two assumptions on `\xi` and `\eta` .. math:: \eta = 0, \xi = f(x) + g(y) .. math:: \eta = f(x) + g(y), \xi = 0 The first assumption of this heuristic holds good if .. math:: \frac{\partial}{\partial y}[(h\frac{\partial^{2}}{ \partial x^{2}}(h^{-1}))^{-1}] is separable in `x` and `y`, 1. The separated factors containing `y` is `\frac{\partial g}{\partial y}`. From this `g(y)` can be determined. 2. The separated factors containing `x` is `f''(x)`. 3. `h\frac{\partial^{2}}{\partial x^{2}}(h^{-1})` equals `\frac{f''(x)}{f(x) + g(y)}`. From this `f(x)` can be determined. The second assumption holds good if `\frac{dy}{dx} = h(x, y)` is rewritten as `\frac{dy}{dx} = \frac{1}{h(y, x)}` and the same properties of the first assumption satisifes. After obtaining `f(x)` and `g(y)`, the coordinates are again interchanged, to get `\eta` as `f(x) + g(y)`. For both assumptions, the constant factors are separated among `g(y)` and `f''(x)`, such that `f''(x)` obtained from 3] is the same as that obtained from 2]. If not possible, then this heuristic fails. References ========== - E.S. Cheb-Terrab, A.D. Roche, Symmetries and First Order ODE Patterns, pp. 7 - pp. 8 """ from sympy.integrals.integrals import integrate xieta = [] h = match['h'] hx = match['hx'] hy = match['hy'] func = match['func'] hinv = match['hinv'] x = func.args[0] y = match['y'] xi = Function('xi')(x, func) eta = Function('eta')(x, func) for odefac in [h, hinv]: factor = odefac*((1/odefac).diff(x, 2)) sep = separatevars((1/factor).diff(y), dict=True, symbols=[x, y]) if sep and sep['coeff'] and sep[x].has(x) and sep[y].has(y): k = Dummy("k") try: gy = k*integrate(sep[y], y) except NotImplementedError: pass else: fdd = 1/(k*sep[x]*sep['coeff']) fx = simplify(fdd/factor - gy) check = simplify(fx.diff(x, 2) - fdd) if fx: if not check: fx = fx.subs(k, 1) gy = (gy/k) else: sol = solve(check, k) if sol: sol = sol[0] fx = fx.subs(k, sol) gy = (gy/k)*sol else: continue if odefac == hinv: # Inverse ODE fx = fx.subs(x, y) gy = gy.subs(y, x) etaval = factor_terms(fx + gy) if etaval.is_Mul: etaval = Mul(*[arg for arg in etaval.args if arg.has(x, y)]) if odefac == hinv: # Inverse ODE inf = {eta: etaval.subs(y, func), xi : S(0)} else: inf = {xi: etaval.subs(y, func), eta : S(0)} if not comp: return [inf] else: xieta.append(inf) if xieta: return xieta def lie_heuristic_abaco2_similar(match, comp=False): r""" This heuristic uses the following two assumptions on `\xi` and `\eta` .. math:: \eta = g(x), \xi = f(x) .. math:: \eta = f(y), \xi = g(y) For the first assumption, 1. First `\frac{\frac{\partial h}{\partial y}}{\frac{\partial^{2} h}{ \partial yy}}` is calculated. Let us say this value is A 2. If this is constant, then `h` is matched to the form `A(x) + B(x)e^{ \frac{y}{C}}` then, `\frac{e^{\int \frac{A(x)}{C} \,dx}}{B(x)}` gives `f(x)` and `A(x)*f(x)` gives `g(x)` 3. Otherwise `\frac{\frac{\partial A}{\partial X}}{\frac{\partial A}{ \partial Y}} = \gamma` is calculated. If a] `\gamma` is a function of `x` alone b] `\frac{\gamma\frac{\partial h}{\partial y} - \gamma'(x) - \frac{ \partial h}{\partial x}}{h + \gamma} = G` is a function of `x` alone. then, `e^{\int G \,dx}` gives `f(x)` and `-\gamma*f(x)` gives `g(x)` The second assumption holds good if `\frac{dy}{dx} = h(x, y)` is rewritten as `\frac{dy}{dx} = \frac{1}{h(y, x)}` and the same properties of the first assumption satisifes. After obtaining `f(x)` and `g(x)`, the coordinates are again interchanged, to get `\xi` as `f(x^*)` and `\eta` as `g(y^*)` References ========== - E.S. Cheb-Terrab, A.D. Roche, Symmetries and First Order ODE Patterns, pp. 10 - pp. 12 """ from sympy.integrals.integrals import integrate xieta = [] h = match['h'] hx = match['hx'] hy = match['hy'] func = match['func'] hinv = match['hinv'] x = func.args[0] y = match['y'] xi = Function('xi')(x, func) eta = Function('eta')(x, func) factor = cancel(h.diff(y)/h.diff(y, 2)) factorx = factor.diff(x) factory = factor.diff(y) if not factor.has(x) and not factor.has(y): A = Wild('A', exclude=[y]) B = Wild('B', exclude=[y]) C = Wild('C', exclude=[x, y]) match = h.match(A + B*exp(y/C)) try: tau = exp(-integrate(match[A]/match[C]), x)/match[B] except NotImplementedError: pass else: gx = match[A]*tau return [{xi: tau, eta: gx}] else: gamma = cancel(factorx/factory) if not gamma.has(y): tauint = cancel((gamma*hy - gamma.diff(x) - hx)/(h + gamma)) if not tauint.has(y): try: tau = exp(integrate(tauint, x)) except NotImplementedError: pass else: gx = -tau*gamma return [{xi: tau, eta: gx}] factor = cancel(hinv.diff(y)/hinv.diff(y, 2)) factorx = factor.diff(x) factory = factor.diff(y) if not factor.has(x) and not factor.has(y): A = Wild('A', exclude=[y]) B = Wild('B', exclude=[y]) C = Wild('C', exclude=[x, y]) match = h.match(A + B*exp(y/C)) try: tau = exp(-integrate(match[A]/match[C]), x)/match[B] except NotImplementedError: pass else: gx = match[A]*tau return [{eta: tau.subs(x, func), xi: gx.subs(x, func)}] else: gamma = cancel(factorx/factory) if not gamma.has(y): tauint = cancel((gamma*hinv.diff(y) - gamma.diff(x) - hinv.diff(x))/( hinv + gamma)) if not tauint.has(y): try: tau = exp(integrate(tauint, x)) except NotImplementedError: pass else: gx = -tau*gamma return [{eta: tau.subs(x, func), xi: gx.subs(x, func)}] def lie_heuristic_abaco2_unique_unknown(match, comp=False): r""" This heuristic assumes the presence of unknown functions or known functions with non-integer powers. 1. A list of all functions and non-integer powers containing x and y 2. Loop over each element `f` in the list, find `\frac{\frac{\partial f}{\partial x}}{ \frac{\partial f}{\partial x}} = R` If it is separable in `x` and `y`, let `X` be the factors containing `x`. Then a] Check if `\xi = X` and `\eta = -\frac{X}{R}` satisfy the PDE. If yes, then return `\xi` and `\eta` b] Check if `\xi = \frac{-R}{X}` and `\eta = -\frac{1}{X}` satisfy the PDE. If yes, then return `\xi` and `\eta` If not, check if the following satisfy the ODE a] `\xi = -R`, `\eta = 1` b] `\xi = 1`, `\eta = -\frac{1}{R}` References ========== - E.S. Cheb-Terrab, A.D. Roche, Symmetries and First Order ODE Patterns, pp. 10 - pp. 12 """ xieta = [] h = match['h'] hx = match['hx'] hy = match['hy'] func = match['func'] hinv = match['hinv'] x = func.args[0] y = match['y'] xi = Function('xi')(x, func) eta = Function('eta')(x, func) funclist = [] for atom in h.atoms(Pow): base, exp = atom.as_base_exp() if base.has(x) and base.has(y): if not exp.is_Integer: funclist.append(atom) for function in h.atoms(AppliedUndef): syms = function.free_symbols if x in syms and y in syms: funclist.append(function) for f in funclist: frac = cancel(f.diff(y)/f.diff(x)) sep = separatevars(frac, dict=True, symbols=[x, y]) if sep and sep['coeff']: xitry1 = sep[x] etatry1 = -1/(sep[y]*sep['coeff']) pde1 = etatry1.diff(y)*h - xitry1.diff(x)*h - xitry1*hx - etatry1*hy if not simplify(pde1): return [{xi: xitry1, eta: etatry1.subs(y, func)}] xitry2 = 1/etatry1 etatry2 = 1/xitry1 pde2 = etatry2.diff(x) - (xitry2.diff(y))*h**2 - xitry2*hx - etatry2*hy if not simplify(expand(pde2)): return [{xi: xitry2.subs(y, func), eta: etatry2}] else: etatry = -1/frac pde = etatry.diff(x) + etatry.diff(y)*h - hx - etatry*hy if not simplify(pde): return [{xi: S(1), eta: etatry.subs(y, func)}] xitry = -frac pde = -xitry.diff(x)*h -xitry.diff(y)*h**2 - xitry*hx -hy if not simplify(expand(pde)): return [{xi: xitry.subs(y, func), eta: S(1)}] def lie_heuristic_abaco2_unique_general(match, comp=False): r""" This heuristic finds if infinitesimals of the form `\eta = f(x)`, `\xi = g(y)` without making any assumptions on `h`. The complete sequence of steps is given in the paper mentioned below. References ========== - E.S. Cheb-Terrab, A.D. Roche, Symmetries and First Order ODE Patterns, pp. 10 - pp. 12 """ xieta = [] h = match['h'] hx = match['hx'] hy = match['hy'] func = match['func'] hinv = match['hinv'] x = func.args[0] y = match['y'] xi = Function('xi')(x, func) eta = Function('eta')(x, func) C = S(0) A = hx.diff(y) B = hy.diff(y) + hy**2 C = hx.diff(x) - hx**2 if not (A and B and C): return Ax = A.diff(x) Ay = A.diff(y) Axy = Ax.diff(y) Axx = Ax.diff(x) Ayy = Ay.diff(y) D = simplify(2*Axy + hx*Ay - Ax*hy + (hx*hy + 2*A)*A)*A - 3*Ax*Ay if not D: E1 = simplify(3*Ax**2 + ((hx**2 + 2*C)*A - 2*Axx)*A) if E1: E2 = simplify((2*Ayy + (2*B - hy**2)*A)*A - 3*Ay**2) if not E2: E3 = simplify( E1*((28*Ax + 4*hx*A)*A**3 - E1*(hy*A + Ay)) - E1.diff(x)*8*A**4) if not E3: etaval = cancel((4*A**3*(Ax - hx*A) + E1*(hy*A - Ay))/(S(2)*A*E1)) if x not in etaval: try: etaval = exp(integrate(etaval, y)) except NotImplementedError: pass else: xival = -4*A**3*etaval/E1 if y not in xival: return [{xi: xival, eta: etaval.subs(y, func)}] else: E1 = simplify((2*Ayy + (2*B - hy**2)*A)*A - 3*Ay**2) if E1: E2 = simplify( 4*A**3*D - D**2 + E1*((2*Axx - (hx**2 + 2*C)*A)*A - 3*Ax**2)) if not E2: E3 = simplify( -(A*D)*E1.diff(y) + ((E1.diff(x) - hy*D)*A + 3*Ay*D + (A*hx - 3*Ax)*E1)*E1) if not E3: etaval = cancel(((A*hx - Ax)*E1 - (Ay + A*hy)*D)/(S(2)*A*D)) if x not in etaval: try: etaval = exp(integrate(etaval, y)) except NotImplementedError: pass else: xival = -E1*etaval/D if y not in xival: return [{xi: xival, eta: etaval.subs(y, func)}] def lie_heuristic_linear(match, comp=False): r""" This heuristic assumes 1. `\xi = ax + by + c` and 2. `\eta = fx + gy + h` After substituting the following assumptions in the determining PDE, it reduces to .. math:: f + (g - a)h - bh^{2} - (ax + by + c)\frac{\partial h}{\partial x} - (fx + gy + c)\frac{\partial h}{\partial y} Solving the reduced PDE obtained, using the method of characteristics, becomes impractical. The method followed is grouping similar terms and solving the system of linear equations obtained. The difference between the bivariate heuristic is that `h` need not be a rational function in this case. References ========== - E.S. Cheb-Terrab, A.D. Roche, Symmetries and First Order ODE Patterns, pp. 10 - pp. 12 """ xieta = [] h = match['h'] hx = match['hx'] hy = match['hy'] func = match['func'] hinv = match['hinv'] x = func.args[0] y = match['y'] xi = Function('xi')(x, func) eta = Function('eta')(x, func) coeffdict = {} symbols = numbered_symbols("c", cls=Dummy) symlist = [next(symbols) for i in islice(symbols, 6)] C0, C1, C2, C3, C4, C5 = symlist pde = C3 + (C4 - C0)*h -(C0*x + C1*y + C2)*hx - (C3*x + C4*y + C5)*hy - C1*h**2 pde, denom = pde.as_numer_denom() pde = powsimp(expand(pde)) if pde.is_Add: terms = pde.args for term in terms: if term.is_Mul: rem = Mul(*[m for m in term.args if not m.has(x, y)]) xypart = term/rem if xypart not in coeffdict: coeffdict[xypart] = rem else: coeffdict[xypart] += rem else: if term not in coeffdict: coeffdict[term] = S(1) else: coeffdict[term] += S(1) sollist = coeffdict.values() soldict = solve(sollist, symlist) if soldict: if isinstance(soldict, list): soldict = soldict[0] subval = soldict.values() if any(t for t in subval): onedict = dict(zip(symlist, [1]*6)) xival = C0*x + C1*func + C2 etaval = C3*x + C4*func + C5 xival = xival.subs(soldict) etaval = etaval.subs(soldict) xival = xival.subs(onedict) etaval = etaval.subs(onedict) return [{xi: xival, eta: etaval}] sympy-0.7.4.1/sympy/solvers/polysys.py0000644000175000017500000002223212253362407020217 0ustar georgeskgeorgesk"""Solvers of systems of polynomial equations. """ from __future__ import print_function, division from sympy.core import S 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.simplify import rcollect from sympy.utilities import default_sort_key, postfixes class SolveFailed(Exception): """Raised when solver's conditions weren't met. """ 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 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, key=default_sort_key) 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, key=default_sort_key) else: return None 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, key=default_sort_key) sympy-0.7.4.1/sympy/solvers/bivariate.py0000644000175000017500000003071112253362407020444 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core.add import Add from sympy.core.compatibility import ordered from sympy.core.function import Function, expand_log, expand_mul from sympy.core.mul import Mul from sympy.core.power import Pow from sympy.core.singleton import S from sympy.core.symbol import (Dummy, Wild) from sympy.functions.elementary.exponential import (LambertW, exp, log) from sympy.functions.elementary.miscellaneous import root from sympy.polys.polytools import (Poly, primitive, factor) from sympy.simplify.simplify import (_mexpand, collect, separatevars) from sympy.solvers.solvers import solve, _invert def _filtered_gens(poly, symbol): """process the generators of ``poly``, returning the set of generators that have ``symbol``. If there are two generators that are inverses of each other, prefer the one that has no denominator. Examples ======== >>> from sympy.solvers.bivariate import _filtered_gens >>> from sympy import Poly, exp >>> from sympy.abc import x >>> _filtered_gens(Poly(x + 1/x + exp(x)), x) set([x, exp(x)]) """ gens = set([g for g in poly.gens if symbol in g.free_symbols]) for g in list(gens): ag = 1/g if g in gens and ag in gens: if ag.as_numer_denom()[1] is not S.One: g = ag gens.remove(g) return gens def _mostfunc(lhs, func, X=None): """Returns the term in lhs which contains the most of the func-type things e.g. log(log(x)) wins over log(x) if both terms appear. ``func`` can be a function (exp, log, etc...) or any other SymPy object, like Pow. Examples ======== >>> from sympy.solvers.bivariate import _mostfunc >>> from sympy.functions.elementary.exponential import exp >>> from sympy.utilities.pytest import raises >>> from sympy.abc import x, y >>> _mostfunc(exp(x) + exp(exp(x) + 2), exp) exp(exp(x) + 2) >>> _mostfunc(exp(x) + exp(exp(y) + 2), exp, x) exp(x) >>> _mostfunc(exp(x) + exp(exp(y) + 2), exp, x) exp(x) >>> _mostfunc(x, exp, x) is None True >>> _mostfunc(exp(x) + exp(x*y), exp, x) exp(x) """ fterms = [tmp for tmp in lhs.atoms(func) if (not X or X.is_Symbol and X in tmp.free_symbols or not X.is_Symbol and tmp.has(X))] if len(fterms) == 1: return fterms[0] elif fterms: return max(list(ordered(fterms)), key=lambda x: x.count(func)) return None def _linab(arg, symbol): """Return ``a, b, X`` assuming ``arg`` can be written as ``a*X + b`` where ``X`` is a symbol-dependent factor and ``a`` and ``b`` are independent of ``symbol``. Examples ======== >>> from sympy.functions.elementary.exponential import exp >>> from sympy.solvers.bivariate import _linab >>> from sympy.abc import x, y >>> from sympy import S >>> _linab(S(2), x) (2, 0, 1) >>> _linab(2*x, x) (2, 0, x) >>> _linab(y + y*x + 2*x, x) (y + 2, y, x) >>> _linab(3 + 2*exp(x), x) (2, 3, exp(x)) """ arg = arg.expand() ind, dep = arg.as_independent(symbol) if not arg.is_Add: b = 0 a, x = ind, dep else: b = ind a, x = separatevars(dep).as_independent(symbol, as_Add=False) if x.could_extract_minus_sign(): a = -a x = -x return a, b, x def _lambert(eq, x): """ Given an expression assumed to be in the form ``F(X, a..f) = a*log(b*X + c) + d*X + f = 0`` where X = g(x) and x = g^-1(X), return the Lambert solution if possible: ``x = g^-1(-c/b + (a/d)*W(d/(a*b)*exp(c*d/a/b)*exp(-f/a)))``. """ eq = _mexpand(expand_log(eq)) mainlog = _mostfunc(eq, log, x) if not mainlog: return [] # violated assumptions other = eq.subs(mainlog, 0) if (-other).func is log: eq = (eq - other).subs(mainlog, mainlog.args[0]) mainlog = mainlog.args[0] if mainlog.func is not log: return [] # violated assumptions other = -(-other).args[0] eq += other if not x in other.free_symbols: return [] # violated assumptions d, f, X2 = _linab(other, x) logterm = collect(eq - other, mainlog) a = logterm.as_coefficient(mainlog) if a is None or x in a.free_symbols: return [] # violated assumptions logarg = mainlog.args[0] b, c, X1 = _linab(logarg, x) if X1 != X2: return [] # violated assumptions u = Dummy('rhs') rhs = -c/b + (a/d)*LambertW(d/(a*b)*exp(c*d/a/b)*exp(-f/a)) # if W's arg is between -1/e and 0 there is a -1 branch solution, too. # Check here to see if exp(W(s)) appears and return s/W(s) instead? solns = solve(X1 - u, x) for i, tmp in enumerate(solns): solns[i] = tmp.subs(u, rhs) return solns def _solve_lambert(f, symbol, gens): """Return solution to ``f`` if it is a Lambert-type expression else raise NotImplementedError. The equality, ``f(x, a..f) = a*log(b*X + c) + d*X - f = 0`` has the solution, `X = -c/b + (a/d)*W(d/(a*b)*exp(c*d/a/b)*exp(f/a))`. There are a variety of forms for `f(X, a..f)` as enumerated below: 1a1) if B**B = R for R not [0, 1] then log(B) + log(log(B)) = log(log(R)) X = log(B), a = 1, b = 1, c = 0, d = 1, f = log(log(R)) 1a2) if B*(b*log(B) + c)**a = R then log(B) + a*log(b*log(B) + c) = log(R) X = log(B); d=1, f=log(R) 1b) if a*log(b*B + c) + d*B = R then X = B, f = R 2a) if (b*B + c)*exp(d*B + g) = R then log(b*B + c) + d*B + g = log(R) a = 1, f = log(R) - g, X = B 2b) if -b*B + g*exp(d*B + h) = c then log(g) + d*B + h - log(b*B + c) = 0 a = -1, f = -h - log(g), X = B 3) if d*p**(a*B + g) - b*B = c then log(d) + (a*B + g)*log(p) - log(c + b*B) = 0 a = -1, d = a*log(p), f = -log(d) - g*log(p) """ nrhs, lhs = f.as_independent(symbol, as_Add=True) rhs = -nrhs lamcheck = [tmp for tmp in gens if (tmp.func in [exp, log] or (tmp.is_Pow and symbol in tmp.exp.free_symbols))] if not lamcheck: raise NotImplementedError() if lhs.is_Mul: lhs = expand_log(log(lhs)) rhs = log(rhs) lhs = factor(lhs, deep=True) # make sure we are inverted as completely as possible r = Dummy() i, lhs = _invert(lhs - r, symbol) rhs = i.xreplace({r: rhs}) # For the first ones: # 1a1) B**B = R != 0 (when 0, there is only a solution if the base is 0, # but if it is, the exp is 0 and 0**0=1 # comes back as B*log(B) = log(R) # 1a2) B*(a + b*log(B))**p = R or with monomial expanded or with whole # thing expanded comes back unchanged # log(B) + p*log(a + b*log(B)) = log(R) # lhs is Mul: # expand log of both sides to give: # log(B) + log(log(B)) = log(log(R)) # 1b) d*log(a*B + b) + c*B = R # lhs is Add: # isolate c*B and expand log of both sides: # log(c) + log(B) = log(R - d*log(a*B + b)) soln = [] if not soln: mainlog = _mostfunc(lhs, log, symbol) if mainlog: if lhs.is_Mul and rhs != 0: soln = _lambert(log(lhs) - log(rhs), symbol) elif lhs.is_Add: other = lhs.subs(mainlog, 0) if other and not other.is_Add and [ tmp for tmp in other.atoms(Pow) if symbol in tmp.free_symbols]: if not rhs: diff = log(other) - log(other - lhs) else: diff = log(lhs - other) - log(rhs - other) soln = _lambert(expand_log(diff), symbol) else: #it's ready to go soln = _lambert(lhs - rhs, symbol) # For the next two, # collect on main exp # 2a) (b*B + c)*exp(d*B + g) = R # lhs is mul: # log to give # log(b*B + c) + d*B = log(R) - g # 2b) -b*B + g*exp(d*B + h) = R # lhs is add: # add b*B # log and rearrange # log(R + b*B) - d*B = log(g) + h if not soln: mainexp = _mostfunc(lhs, exp, symbol) if mainexp: lhs = collect(lhs, mainexp) if lhs.is_Mul and rhs != 0: soln = _lambert(expand_log(log(lhs) - log(rhs)), symbol) elif lhs.is_Add: # move all but mainexp-containing term to rhs other = lhs.subs(mainexp, 0) mainterm = lhs - other rhs=rhs - other if (mainterm.could_extract_minus_sign() and rhs.could_extract_minus_sign()): mainterm *= -1 rhs *= -1 diff = log(mainterm) - log(rhs) soln = _lambert(expand_log(diff), symbol) # 3) d*p**(a*B + b) + c*B = R # collect on main pow # log(R - c*B) - a*B*log(p) = log(d) + b*log(p) if not soln: mainpow = _mostfunc(lhs, Pow, symbol) if mainpow and symbol in mainpow.exp.free_symbols: lhs = collect(lhs, mainpow) if lhs.is_Mul and rhs != 0: soln = _lambert(expand_log(log(lhs) - log(rhs)), symbol) elif lhs.is_Add: # move all but mainpow-containing term to rhs other = lhs.subs(mainpow, 0) mainterm = lhs - other rhs = rhs - other diff = log(mainterm) - log(rhs) soln = _lambert(expand_log(diff), symbol) if not soln: raise NotImplementedError('%s does not appear to have a solution in ' 'terms of LambertW' % f) return list(ordered(soln)) def bivariate_type(f, x, y, **kwargs): """Given an expression, f, 3 tests will be done to see what type of composite bivariate it might be, options for u(x, y) are:: x*y x+y x*y+x x*y+y If it matches one of these types, ``u(x, y)``, ``P(u)`` and dummy variable ``u`` will be returned. Solving ``P(u)`` for ``u`` and equating the solutions to ``u(x, y)`` and then solving for ``x`` or ``y`` is equivalent to solving the original expression for ``x`` or ``y``. If ``x`` and ``y`` represent two functions in the same variable, e.g. ``x = g(t)`` and ``y = h(t)``, then if ``u(x, y) - p`` can be solved for ``t`` then these represent the solutions to ``P(u) = 0`` when ``p`` are the solutions of ``P(u) = 0``. Only positive values of ``u`` are considered. Examples ======== >>> from sympy.solvers.solvers import solve >>> from sympy.solvers.bivariate import bivariate_type >>> from sympy.abc import x, y >>> eq = (x**2 - 3).subs(x, x + y) >>> bivariate_type(eq, x, y) (x + y, _u**2 - 3, _u) >>> uxy, pu, u = _ >>> usol = solve(pu, u); usol [sqrt(3)] >>> [solve(uxy - s) for s in solve(pu, u)] [[{x: -y + sqrt(3)}]] >>> all(eq.subs(s).equals(0) for sol in _ for s in sol) True """ u = Dummy('u', positive=True) if kwargs.pop('first', True): p = Poly(f, x, y) f = p.as_expr() _x = Dummy() _y = Dummy() rv = bivariate_type(Poly(f.subs({x: _x, y: _y}), _x, _y), _x, _y, first=False) if rv: reps = {_x: x, _y: y} return rv[0].xreplace(reps), rv[1].xreplace(reps), rv[2] return p = f f = p.as_expr() # f(x*y) args = Add.make_args(p.as_expr()) new = [] for a in args: a = _mexpand(a.subs(x, u/y)) free = a.free_symbols if x in free or y in free: break new.append(a) else: return x*y, Add(*new), u def ok(f, v, c): new = _mexpand(f.subs(v, c)) free = new.free_symbols return None if (x in free or y in free) else new # f(a*x + b*y) new = [] d = p.degree(x) if p.degree(y) == d: a = root(p.coeff_monomial(x**d), d) b = root(p.coeff_monomial(y**d), d) new = ok(f, x, (u - b*y)/a) if new is not None: return a*x + b*y, new, u # f(a*x*y + b*y) new = [] d = p.degree(x) if p.degree(y) == d: for itry in range(2): a = root(p.coeff_monomial(x**d*y**d), d) b = root(p.coeff_monomial(y**d), d) new = ok(f, x, (u - b*y)/a/y) if new is not None: return a*x*y + b*y, new, u x, y = y, x sympy-0.7.4.1/sympy/solvers/recurr.py0000644000175000017500000005742212253362407020010 0ustar georgeskgeorgesk""" 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: .. math:: 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, \dots, 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 __future__ import print_function, division 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, xrange from sympy.utilities.iterables import numbered_symbols def rsolve_poly(coeffs, f, n, **hints): """ Given linear recurrence operator `\operatorname{L}` of order `k` with polynomial coefficients and inhomogeneous equation `\operatorname{L} y = 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 `\operatorname{L} y = 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 References ========== .. [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 xrange(0, r + 1): for j in xrange(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 xrange(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 xrange(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 xrange(0, N + 1): C.append(Symbol('C' + str(i))) y += C[i] * n**i for i in xrange(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 xrange(1, k + 1): B *= -Rational(k - i + 1, i) D += B * p.subs(n, a + k - i) return D alpha = {} for i in xrange(-A, d + 1): I = _one_vector(d + 1) for k in xrange(1, d + 1): I[k] = I[k - 1] * (x + i - k + 1)/k alpha[i] = S.Zero for j in xrange(0, A + 1): for k in xrange(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 xrange(A, U): v = _zero_vector(A) for k in xrange(1, A + b + 1): if i - k < 0: break B = alpha[k - A].subs(x, i - k) for j in xrange(0, A): v[j] += B * V[i - k, j] denom = alpha[-A].subs(x, i) for j in xrange(0, A): V[i, j] = -v[j] / denom else: G = _zero_vector(U) for i in xrange(A, U): v = _zero_vector(A) g = S.Zero for k in xrange(1, A + b + 1): if i - k < 0: break B = alpha[k - A].subs(x, i - k) for j in xrange(0, A): v[j] += B * V[i - k, j] g += B * G[i - k] denom = alpha[-A].subs(x, i) for j in xrange(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 xrange(1, U): P[i] = (P[i - 1] * (n - a - i + 1)/i).expand() for i in xrange(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 xrange(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 xrange(N + 1, U) ] else: E = [ g(i) + _delta(h, i) for i in xrange(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 def rsolve_ratio(coeffs, f, n, **hints): """ Given linear recurrence operator `\operatorname{L}` of order `k` with polynomial coefficients and inhomogeneous equation `\operatorname{L} y = 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 `\operatorname{L} y = 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. 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)) References ========== .. [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 See Also ======== rsolve_hyper """ 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 xrange(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 xrange(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 xrange(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 def rsolve_hyper(coeffs, f, n, **hints): """ Given linear recurrence operator `\operatorname{L}` of order `k` with polynomial coefficients and inhomogeneous equation `\operatorname{L} y = 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 `\operatorname{L} y = f`, and find particular solution using Abramov's algorithm. (2) Compute generating set of `\operatorname{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 `\operatorname{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 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 References ========== .. [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. """ coeffs = list(map(sympify, coeffs)) f = sympify(f) r, kernel, symbols = len(coeffs) - 1, [], set() 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 xrange(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 xrange(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 xrange(0, r + 1): a = Mul(*[ A.subs(n, n + j) for j in xrange(0, i) ]) b = Mul(*[ B.subs(n, n + j) for j in xrange(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 xrange(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 z.is_zero: continue (C, s) = rsolve_poly([ polys[i]*z**i for i in xrange(r + 1) ], 0, n, symbols=True) if C is not None and C is not S.Zero: symbols |= set(s) 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 roots(ratio.as_numer_denom()[1], n).keys(): if (n0 < (n_root + 1)) is True: 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) kernel.sort(key=default_sort_key) sk = list(zip(numbered_symbols('C'), kernel)) if sk: for C, ker in sk: result += C * ker else: return None if hints.get('symbols', False): symbols |= set([s for s, k in sk]) return (result, list(symbols)) else: return result def rsolve(f, y, init=None): """ Solve univariate recurrence with rational coefficients. Given `k`-th order linear recurrence `\operatorname{L} y = f`, or equivalently: .. math:: a_{k}(n) y(n+k) + a_{k-1}(n) y(n+k-1) + \dots + a_{0}(n) y(n) = f(n) where `a_{i}(n)`, for `i=0, \dots, 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, \dots, m`, maps to `y(n_i)`. Examples ======== Lets consider the following recurrence: .. math:: (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*factorial(n) >>> rsolve(f, y(n), { y(0):0, y(1):3 }) 3*2**n - 3*factorial(n) See Also ======== rsolve_poly, rsolve_ratio, rsolve_hyper """ if isinstance(f, Equality): f = f.lhs - f.rhs n = y.args[0] k = Wild('k', exclude=(n,)) # Preprocess user input to allow things like # y(n) + a*(y(n + 1) + y(n - 1))/2 f = f.expand().collect(y.func(Wild('m', integer=True))) 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 xrange(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: if type(init) is list: init = dict([(i, init[i]) for i in xrange(len(init))]) equations = [] 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) try: eq = solution.limit(n, i) - v except NotImplementedError: eq = solution.subs(n, i) - v equations.append(eq) result = solve(equations, *symbols) if not result: return None else: solution = solution.subs(result) return solution sympy-0.7.4.1/sympy/solvers/deutils.py0000644000175000017500000002336512253362407020156 0ustar georgeskgeorgesk"""Utility functions for classifying and solving ordinary and partial differential equations. Contains ======== _preprocess ode_order _desolve """ from __future__ import print_function, division from sympy.core.function import Function, Derivative, AppliedUndef from sympy.core.relational import Equality, Eq from sympy.core.symbol import Wild 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.deutils import _preprocess >>> from sympy import Derivative, Function, Integral, sin >>> from sympy.abc import x, y, z >>> f, g = 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: >>> eq = Derivative(f(x) + 1, x) + Derivative(f(x), y) >>> _preprocess(eq, 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 ode_order(expr, func): """ Returns the order of a given differential equation with respect to func. This function is implemented recursively. Examples ======== >>> from sympy import Function >>> from sympy.solvers.deutils import ode_order >>> from sympy.abc import x >>> f, g = 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 def _desolve(eq, func=None, hint="default", ics=None, simplify=True, **kwargs): """This is a helper function to dsolve and pdsolve in the ode and pde modules. If the hint provided to the function is "default", then a dict with the following keys are returned 'func' - It provides the function for which the differential equation has to be solved. This is useful when the function 'default' - The default key as returned by classifier functions in ode and pde.py 'hint' - The hint given by the user for which the differential equation is to be solved. If the hint given by the user is 'default', then the value of 'hint' and 'default' is the same. 'order' - The order of the function as returned by ode_order 'match' - It returns the match as given by the classifier functions, for the default hint. If the hint provided to the function is not "default" and is not in ('all', 'all_Integral', 'best'), then a dict with the above mentioned keys is returned along with the keys which are returned when dict in classify_ode or classify_pde is set True If the hint given is in ('all', 'all_Integral', 'best'), then this function returns a nested dict, with the keys, being the set of classified hints returned by classifier functions, and the values being the dict of form as mentioned above. Key 'eq' is a common key to all the above mentioned hints which returns an expression if eq given by user is an Equality. See Also ======== classify_ode(ode.py) classify_pde(pde.py) """ prep = kwargs.pop('prep', True) 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 # type is an argument passed by the solve functions in ode and pde.py # that identifies whether the function caller is an ordinary # or partial differential equation. Accordingly corresponding # changes are made in the function. type = kwargs.get('type', None) xi = kwargs.get('xi') eta = kwargs.get('eta') x0 = kwargs.get('x0', 0) terms = kwargs.get('n') if type == 'ode': from sympy.solvers.ode import classify_ode, allhints classifier = classify_ode string = 'ODE ' dummy = '' elif type == 'pde': from sympy.solvers.pde import classify_pde, allhints classifier = classify_pde string = 'PDE ' dummy = 'p' # 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 = classifier(eq, func, dict=True, ics=ics, xi=xi, eta=eta, n=terms, x0=x0, prep=prep) else: # Here is what all this means: # # hint: The hint method given to _desolve() by the user. # hints: The dictionary of hints that match the DE, 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; obtained from classify_ode(). # match: Dictionary containing the match dictionary for each hint # (the parts of the DE for solving). When going through the # hints in "all", this holds the match string for the current # hint. # order: The order of the DE, 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. if hint not in allhints and hint != 'default': raise ValueError("Hint not recognized: " + hint) elif hint not in hints['ordered_hints'] and hint != 'default': raise ValueError(string + str(eq) + " does not match hint " + hint) else: raise NotImplementedError(dummy + "solve" + ": Cannot solve " + str(eq)) if hint == 'default': return _desolve(eq, func, ics=ics, hint=hints['default'], simplify=simplify, prep=prep, x0=x0, classify=False, order=hints['order'], match=hints[hints['default']], xi=xi, eta=eta, n=terms, type=type) 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 cases for k in ["1st_homogeneous_coeff_best", "1st_power_series", "lie_group", "2nd_power_series_ordinary", "2nd_power_series_regular"]: if k in gethints: gethints.remove(k) for i in gethints: sol = _desolve(eq, func, ics=ics, hint=i, x0=x0, simplify=simplify, prep=prep, classify=False, n=terms, order=hints['order'], match=hints[i], type=type) retdict[i] = sol retdict['all'] = True retdict['eq'] = eq 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(string + str(eq) + " does not match hint " + hint) else: # Key added to identify the hint needed to solve the equation hints['hint'] = hint hints.update({'func': func, 'eq': eq}) return hints sympy-0.7.4.1/sympy/solvers/pde.py0000644000175000017500000010546312253362407017255 0ustar georgeskgeorgesk""" This module contains pdsolve() and different helper functions that it uses. It is heavily inspired by the ode module and hence the basic infrastructure remains the same. **Functions in this module** These are the user functions in this module: - pdsolve() - Solves PDE's - classify_pde() - Classifies PDEs into possible hints for dsolve(). - pde_separate() - Separate variables in partial differential equation either by additive or multiplicative separation approach. These are the helper functions in this module: - pde_separate_add() - Helper function for searching additive separable solutions. - pde_separate_mul() - Helper function for searching multiplicative separable solutions. **Currently implemented solver methods** The following methods are implemented for solving partial differential equations. See the docstrings of the various pde_hint() functions for more information on each (run help(pde)): - 1st order linear homogeneous partial differential equations with constant coefficients. - 1st order linear general partial differential equations with constant coefficients. - 1st order linear partial differential equations with variable coefficients. """ from __future__ import print_function, division from copy import deepcopy from sympy.simplify import simplify from sympy.core import Add, C, S, Mul, Pow, oo from sympy.core.compatibility import (reduce, combinations_with_replacement, is_sequence) from sympy.core.function import (Function, Derivative, expand, diff, AppliedUndef, Subs) from sympy.core.numbers import Rational from sympy.core.relational import Equality, Eq from sympy.core.symbol import Symbol, Wild, Dummy, symbols from sympy.functions import exp from sympy.utilities.iterables import has_dups from sympy.solvers.deutils import _preprocess, ode_order, _desolve from sympy.solvers.solvers import solve from sympy.simplify.simplify import collect import operator allhints = ( "1st_linear_constant_coeff_homogeneous", "1st_linear_constant_coeff", "1st_linear_constant_coeff_Integral", "1st_linear_variable_coeff" ) def pdsolve(eq, func=None, hint='default', dict=False, solvefun=None, **kwargs): """ Solves any (supported) kind of partial differential equation. **Usage** pdsolve(eq, f(x,y), hint) -> Solve partial differential equation eq for function f(x,y), using method hint. **Details** ``eq`` can be any supported partial differential equation (see the pde docstring for supported methods). This can either be an Equality, or an expression, which is assumed to be equal to 0. ``f(x,y)`` is a function of two variables whose derivatives in that variable make up the partial differential equation. 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 pdsolve to use. Use classify_pde(eq, f(x,y)) to get all of the possible hints for a PDE. The default hint, 'default', will use whatever hint is returned first by classify_pde(). See Hints below for more options that you can use for hint. ``solvefun`` is the convention used for arbitrary functions returned by the PDE solver. If not set by the user, it is set by default to be F. **Hints** Aside from the various solving methods, there are also some meta-hints that you can pass to pdsolve(): "default": This uses whatever hint is returned first by classify_pde(). This is the default argument to pdsolve(). "all": To make pdsolve apply all relevant classification hints, use pdsolve(PDE, func, hint="all"). This will return a dictionary of hint:solution terms. If a hint causes pdsolve 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 PDE. See also ode_order() in deutils.py - 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_pde(). "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 pdsolve() 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. See also the classify_pde() docstring for more info on hints, and the pde 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, y # x and y are the independent variables >>> f = Function("f")(x, y) # f is a function of x and y >>> # fx will be the partial derivative of f with respect to x >>> fx = Derivative(f, x) >>> # fy will be the partial derivative of f with respect to y >>> fy = Derivative(f, y) - See test_pde.py for many tests, which serves also as a set of examples for how to use pdsolve(). - pdsolve always returns an Equality class (except for the case when the hint is "all" or "all_Integral"). Note that it is not possible to get an explicit solution for f(x, y) as in the case of ODE's - Do help(pde.pde_hintname) to get help more information on a specific hint Examples ======== >>> from sympy.solvers.pde import pdsolve >>> from sympy import Function, diff, Eq >>> from sympy.abc import x, y >>> f = Function('f') >>> u = f(x, y) >>> ux = u.diff(x) >>> uy = u.diff(y) >>> eq = Eq(1 + (2*(ux/u)) + (3*(uy/u))) >>> pdsolve(eq) f(x, y) == F(3*x - 2*y)*exp(-2*x/13 - 3*y/13) """ given_hint = hint # hint given by the user. if not solvefun: solvefun = Function('F') # See the docstring of _desolve for more details. hints = _desolve(eq, func=func, hint=hint, simplify=True, type='pde', **kwargs) eq = hints.pop('eq', False) all_ = hints.pop('all', False) if all_: # TODO : 'best' hint should be implemented when adequate # number of hints are added. pdedict = {} failed_hints = {} gethints = classify_pde(eq, dict=True) pdedict.update({'order': gethints['order'], 'default': gethints['default']}) for hint in hints: try: rv = _helper_simplify(eq, hint, hints[hint]['func'], hints[hint]['order'], hints[hint][hint], solvefun) except NotImplementedError as detail: failed_hints[hint] = detail else: pdedict[hint] = rv pdedict.update(failed_hints) return pdedict else: return _helper_simplify(eq, hints['hint'], hints['func'], hints['order'], hints[hints['hint']], solvefun) def _helper_simplify(eq, hint, func, order, match, solvefun): """Helper function of pdsolve that calls the respective pde functions to solve for the partial differential equations. This minimises the computation in calling _desolve multiple times. """ if hint.endswith("_Integral"): solvefunc = globals()[ "pde_" + hint[:-len("_Integral")]] else: solvefunc = globals()["pde_" + hint] return _handle_Integral(solvefunc(eq, func, order, match, solvefun), func, order, hint) def _handle_Integral(expr, func, order, hint): r""" Converts a solution with integrals in it into an actual solution. Simplifies the integral mainly using doit() """ if hint.endswith("_Integral"): return expr elif hint == "1st_linear_constant_coeff": return simplify(expr.doit()) else: return expr def classify_pde(eq, func=None, dict=False, **kwargs): """ Returns a tuple of possible pdsolve() classifications for a PDE. The tuple is ordered so that first item is the classification that pdsolve() uses to solve the PDE 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 pdsolve use a different classification, use pdsolve(PDE, func, hint=). See also the pdsolve() docstring for different meta-hints you can use. If ``dict`` is true, classify_pde() will return a dictionary of hint:match expression terms. This is intended for internal use by pdsolve(). Note that because dictionaries are ordered arbitrarily, this will most likely not be in the same order as the tuple. You can get help on different hints by doing help(pde.pde_hintname), where hintname is the name of the hint without "_Integral". See sympy.pde.allhints or the sympy.pde docstring for a list of all supported hints that can be returned from classify_pde. Examples ======== >>> from sympy.solvers.pde import classify_pde >>> from sympy import Function, diff, Eq >>> from sympy.abc import x, y >>> f = Function('f') >>> u = f(x, y) >>> ux = u.diff(x) >>> uy = u.diff(y) >>> eq = Eq(1 + (2*(ux/u)) + (3*(uy/u))) >>> classify_pde(eq) ('1st_linear_constant_coeff_homogeneous',) """ prep = kwargs.pop('prep', True) if func and len(func.args) != 2: raise NotImplementedError("Right now only partial " "differential equations of two variables are supported") if prep or func is None: prep, func_ = _preprocess(eq, func) if func is None: func = func_ if isinstance(eq, Equality): if eq.rhs != 0: return classify_pde(eq.lhs - eq.rhs, func) eq = eq.lhs f = func.func x = func.args[0] y = func.args[1] fx = f(x,y).diff(x) fy = f(x,y).diff(y) # TODO : For now pde.py uses support offered by the ode_order function # to find the order with respect to a multi-variable function. An # improvement could be to classify the order of the PDE on the basis of # individual variables. order = ode_order(eq, f(x,y)) # hint:matchdict or hint:(tuple of matchdicts) # Also will contain "default": and "order":order items. matching_hints = {'order': order} if not order: if dict: matching_hints["default"] = None return matching_hints else: return () eq = expand(eq) a = Wild('a', exclude = [f(x,y)]) b = Wild('b', exclude = [f(x,y), fx, fy, x, y]) c = Wild('c', exclude = [f(x,y), fx, fy, x, y]) d = Wild('d', exclude = [f(x,y), fx, fy, x, y]) e = Wild('e', exclude = [f(x,y), fx, fy]) n = Wild('n', exclude = [x, y]) # Try removing the smallest power of f(x,y) # from the highest partial derivatives of f(x,y) reduced_eq = None if eq.is_Add: var = set(combinations_with_replacement((x,y), order)) dummyvar = deepcopy(var) power = None for i in var: coeff = eq.coeff(f(x,y).diff(*i)) if coeff != 1: match = coeff.match(a*f(x,y)**n) if match and match[a]: power = match[n] dummyvar.remove(i) break dummyvar.remove(i) for i in dummyvar: coeff = eq.coeff(f(x,y).diff(*i)) if coeff != 1: match = coeff.match(a*f(x,y)**n) if match and match[a] and match[n] < power: power = match[n] if power: den = f(x,y)**power reduced_eq = Add(*[arg/den for arg in eq.args]) if not reduced_eq: reduced_eq = eq if order == 1: reduced_eq = collect(reduced_eq, f(x, y)) r = reduced_eq.match(b*fx + c*fy + d*f(x,y) + e) if r: if not r[e]: ## Linear first-order homogeneous partial-differential ## equation with constant coefficients r.update({'b': b, 'c': c, 'd': d}) matching_hints["1st_linear_constant_coeff_homogeneous"] = r else: if r[b]**2 + r[c]**2 != 0: ## Linear first-order general partial-differential ## equation with constant coefficients r.update({'b': b, 'c': c, 'd': d, 'e': e}) matching_hints["1st_linear_constant_coeff"] = r matching_hints[ "1st_linear_constant_coeff_Integral"] = r else: b = Wild('b', exclude=[f(x, y), fx, fy]) c = Wild('c', exclude=[f(x, y), fx, fy]) d = Wild('d', exclude=[f(x, y), fx, fy]) r = reduced_eq.match(b*fx + c*fy + d*f(x,y) + e) if r: r.update({'b': b, 'c': c, 'd': d, 'e': e}) matching_hints["1st_linear_variable_coeff"] = 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 make note of which # hint would come first for pdsolve(). Use an ordered dict in Py 3. 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) def checkpdesol(pde, sol, func=None, solve_for_func=True): """ Checks if the given solution satisfies the partial differential equation. pde is the partial differential equation which can be given in the form of an equation or an expression. sol is the solution for which the pde is to be checked. This can also be given in an equation or an expression form. If the function is not provided, the helper function _preprocess from deutils is used to identify the function. If a sequence of solutions is passed, the same sort of container will be used to return the result for each solution. The following methods are currently being implemented to check if the solution satisfies the PDE: 1. Directly substitute the solution in the PDE and check. If the solution hasn't been solved for f, then it will solve for f provided solve_for_func hasn't been set to False. If the solution satisfies the PDE, then a tuple (True, 0) is returned. Otherwise a tuple (False, expr) where expr is the value obtained after substituting the solution in the PDE. However if a known solution returns False, it may be due to the inability of doit() to simplify it to zero. Examples ======== >>> from sympy import Function, symbols, diff >>> from sympy.solvers.pde import checkpdesol, pdsolve >>> x, y = symbols('x y') >>> f = Function('f') >>> eq = 2*f(x,y) + 3*f(x,y).diff(x) + 4*f(x,y).diff(y) >>> sol = pdsolve(eq) >>> assert checkpdesol(eq, sol)[0] >>> eq = x*f(x,y) + f(x,y).diff(x) >>> checkpdesol(eq, sol) (False, (x*F(4*x - 3*y) - 6*F(4*x - 3*y)/25 + 4*Subs(Derivative(F(_xi_1), _xi_1), (_xi_1,), (4*x - 3*y,)))*exp(-6*x/25 - 8*y/25)) """ # Converting the pde into an equation if not isinstance(pde, Equality): pde = Eq(pde, 0) # If no function is given, try finding the function present. if func is None: try: _, func = _preprocess(pde.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 checkpdesol for this case.') func = funcs.pop() # If the given solution is in the form of a list or a set # then return a list or set of tuples. if is_sequence(sol, set): return type(sol)(map(lambda i: checkpdesol(pde, i, solve_for_func=solve_for_func), sol)) # Convert solution into an equation if not isinstance(sol, Equality): sol = Eq(func, sol) # Try solving for the function 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 = checkpdesol(pde, Eq(func, solved[0]), order=order, solve_for_func=False) else: result = checkpdesol(pde, [Eq(func, t) for t in solved], order=order, solve_for_func=False) # The first method includes direct substitution of the solution in # the PDE and simplifying. pde = pde.lhs - pde.rhs if sol.lhs == func: s = pde.subs(func, sol.rhs).doit() elif sol.rhs == func: s = pde.subs(func, sol.lhs).doit() if s: ss = simplify(s) if ss: return False, ss else: return True, 0 else: return True, 0 def pde_1st_linear_constant_coeff_homogeneous(eq, func, order, match, solvefun): r""" Solves a first order linear homogeneous partial differential equation with constant coefficients. The general form of this partial differential equation is .. math:: a \frac{df(x,y)}{dx} + b \frac{df(x,y)}{dy} + c f(x,y) = 0 where `a`, `b` and `c` are constants. The general solution is of the form:: >>> from sympy.solvers import pdsolve >>> from sympy.abc import x, y, a, b, c >>> from sympy import Function, pprint >>> f = Function('f') >>> u = f(x,y) >>> ux = u.diff(x) >>> uy = u.diff(y) >>> genform = a*ux + b*uy + c*u >>> pprint(genform) d d a*--(f(x, y)) + b*--(f(x, y)) + c*f(x, y) dx dy >>> pprint(pdsolve(genform)) -c*(a*x + b*y) --------------- 2 2 a + b f(x, y) = F(-a*y + b*x)*e Examples ======== >>> from sympy.solvers.pde import ( ... pde_1st_linear_constant_coeff_homogeneous) >>> from sympy import pdsolve >>> from sympy import Function, diff, pprint >>> from sympy.abc import x,y >>> f = Function('f') >>> pdsolve(f(x,y) + f(x,y).diff(x) + f(x,y).diff(y)) f(x, y) == F(x - y)*exp(-x/2 - y/2) >>> pprint(pdsolve(f(x,y) + f(x,y).diff(x) + f(x,y).diff(y))) x y - - - - 2 2 f(x, y) = F(x - y)*e References ========== - Viktor Grigoryan, "Partial Differential Equations" Math 124A - Fall 2010, pp.7 """ # TODO : For now homogeneous first order linear PDE's having # two variables are implemented. Once there is support for # solving systems of ODE's, this can be extended to n variables. f = func.func x = func.args[0] y = func.args[1] b = match[match['b']] c = match[match['c']] d = match[match['d']] return Eq(f(x,y), exp(-S(d)/(b**2 + c**2)*(b*x + c*y))*solvefun(c*x - b*y)) def pde_1st_linear_constant_coeff(eq, func, order, match, solvefun): r""" Solves a first order linear partial differential equation with constant coefficients. The general form of this partial differential equation is .. math:: a \frac{df(x,y)}{dx} + b \frac{df(x,y)}{dy} + c f(x,y) = G(x,y) where `a`, `b` and `c` are constants and `G(x, y)` can be an arbitrary function in `x` and `y`. The general solution of the PDE is:: >>> from sympy.solvers import pdsolve >>> from sympy.abc import x, y, a, b, c >>> from sympy import Function, pprint >>> f = Function('f') >>> G = Function('G') >>> u = f(x,y) >>> ux = u.diff(x) >>> uy = u.diff(y) >>> genform = a*u + b*ux + c*uy - G(x,y) >>> pprint(genform) d d a*f(x, y) + b*--(f(x, y)) + c*--(f(x, y)) - G(x, y) dx dy >>> pprint(pdsolve(genform, hint='1st_linear_constant_coeff_Integral')) // b*x + c*y \ || / | || | | || | a*xi | || | ------- | || | 2 2 | || | /b*xi + c*eta -b*eta + c*xi\ b + c | || | G|------------, -------------|*e d(xi)| || | | 2 2 2 2 | | || | \ b + c b + c / | || | | || / | || | f(x, y) = ||F(eta) + -------------------------------------------------------|* || 2 2 | \\ b + c / \| || || || || || || || || -a*xi || -------|| 2 2|| b + c || e || || /|eta=-b*y + c*x, xi=b*x + c*y Examples ======== >>> from sympy.solvers.pde import pdsolve >>> from sympy import Function, diff, pprint, exp >>> from sympy.abc import x,y >>> f = Function('f') >>> eq = -2*f(x,y).diff(x) + 4*f(x,y).diff(y) + 5*f(x,y) - exp(x + 3*y) >>> pdsolve(eq) f(x, y) == (F(4*x + 2*y) + exp(x/2 + 4*y)/15)*exp(x/2 - y) References ========== - Viktor Grigoryan, "Partial Differential Equations" Math 124A - Fall 2010, pp.7 """ # TODO : For now homogeneous first order linear PDE's having # two variables are implemented. Once there is support for # solving systems of ODE's, this can be extended to n variables. xi, eta = symbols("xi eta") f = func.func x = func.args[0] y = func.args[1] b = match[match['b']] c = match[match['c']] d = match[match['d']] e = -match[match['e']] expterm = exp(-S(d)/(b**2 + c**2)*xi) functerm = solvefun(eta) solvedict = solve((b*x + c*y - xi, c*x - b*y - eta), x, y) # Integral should remain as it is in terms of xi, # doit() should be done in _handle_Integral. genterm = (1/S(b**2 + c**2))*C.Integral( (1/expterm*e).subs(solvedict), (xi, b*x + c*y)) return Eq(f(x,y), Subs(expterm*(functerm + genterm), (eta, xi), (c*x - b*y, b*x + c*y))) def pde_1st_linear_variable_coeff(eq, func, order, match, solvefun): r""" Solves a first order linear partial differential equation with variable coefficients. The general form of this partial differential equation is .. math:: a(x, y) \frac{df(x, y)}{dx} + a(x, y) \frac{df(x, y)}{dy} + c(x, y) f(x, y) - G(x, y) where `a(x, y)`, `b(x, y)`, `c(x, y)` and `G(x, y)` are arbitrary functions in `x` and `y`. This PDE is converted into an ODE by making the following transformation. 1] `\xi` as `x` 2] `\eta` as the constant in the solution to the differential equation `\frac{dy}{dx} = -\frac{b}{a}` Making the following substitutions reduces it to the linear ODE .. math:: a(\xi, \eta)\frac{du}{d\xi} + c(\xi, \eta)u - d(\xi, \eta) = 0 which can be solved using dsolve. The general form of this PDE is:: >>> from sympy.solvers.pde import pdsolve >>> from sympy.abc import x, y >>> from sympy import Function, pprint >>> a, b, c, G, f= [Function(i) for i in ['a', 'b', 'c', 'G', 'f']] >>> u = f(x,y) >>> ux = u.diff(x) >>> uy = u.diff(y) >>> genform = a(x, y)*u + b(x, y)*ux + c(x, y)*uy - G(x,y) >>> pprint(genform) d d -G(x, y) + a(x, y)*f(x, y) + b(x, y)*--(f(x, y)) + c(x, y)*--(f(x, y)) dx dy Examples ======== >>> from sympy.solvers.pde import pdsolve >>> from sympy import Function, diff, pprint, exp >>> from sympy.abc import x,y >>> f = Function('f') >>> eq = x*(u.diff(x)) - y*(u.diff(y)) + y**2*u - y**2 >>> pdsolve(eq) f(x, y) == F(x*y)*exp(y**2/2) + 1 References ========== - Viktor Grigoryan, "Partial Differential Equations" Math 124A - Fall 2010, pp.7 """ from sympy.integrals.integrals import integrate from sympy.solvers.ode import dsolve xi, eta = symbols("xi eta") f = func.func x = func.args[0] y = func.args[1] b = match[match['b']] c = match[match['c']] d = match[match['d']] e = -match[match['e']] if not d: # To deal with cases like b*ux = e or c*uy = e if not (b and c): if c: try: tsol = integrate(e/c, y) except NotImplementedError: raise NotImplementedError("Unable to find a solution" " due to inability of integrate") else: return Eq(f(x,y), solvefun(x) + tsol) if b: try: tsol = integrate(e/b, x) except NotImplementedError: raise NotImplementedError("Unable to find a solution" " due to inability of integrate") else: return Eq(f(x,y), solvefun(y) + tsol) if not c: # To deal with cases when c is 0, a simpler method is used. # The PDE reduces to b*(u.diff(x)) + d*u = e, which is a linear ODE in x plode = f(x).diff(x)*b + d*f(x) - e sol = dsolve(plode, f(x)) syms = sol.free_symbols - plode.free_symbols - set([x, y]) rhs = _simplify_variable_coeff(sol.rhs, syms, solvefun, y) return Eq(f(x, y), rhs) if not b: # To deal with cases when b is 0, a simpler method is used. # The PDE reduces to c*(u.diff(y)) + d*u = e, which is a linear ODE in y plode = f(y).diff(y)*c + d*f(y) - e sol = dsolve(plode, f(y)) syms = sol.free_symbols - plode.free_symbols - set([x, y]) rhs = _simplify_variable_coeff(sol.rhs, syms, solvefun, x) return Eq(f(x, y), rhs) dummy = Function('d') h = (c/b).subs(y, dummy(x)) sol = dsolve(dummy(x).diff(x) - h, dummy(x)) if isinstance(sol, list): sol = sol[0] solsym = sol.free_symbols - h.free_symbols - set([x, y]) if len(solsym) == 1: solsym = solsym.pop() etat = (solve(sol, solsym)[0]).subs(dummy(x), y) ysub = solve(eta - etat, y)[0] deq = (b*(f(x).diff(x)) + d*f(x) - e).subs(y, ysub) final = (dsolve(deq, f(x), hint='1st_linear')).rhs if isinstance(final, list): final = final[0] finsyms = final.free_symbols - deq.free_symbols - set([x, y]) rhs = _simplify_variable_coeff(final, finsyms, solvefun, etat) return Eq(f(x, y), rhs) else: raise NotImplementedError("Cannot solve the partial differential equation due" " to inability of constantsimp") def _simplify_variable_coeff(sol, syms, func, funcarg): r""" Helper function to replace constants by functions in 1st_linear_variable_coeff """ eta = Symbol("eta") if len(syms) == 1: sym = syms.pop() final = sol.subs(sym, func(funcarg)) else: fname = func.__name__ for key, sym in enumerate(syms): tempfun = Function(fname + str(key)) final = sol.subs(sym, func(funcarg)) return simplify(final.subs(eta, funcarg)) 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 = 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) 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 = 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') 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 = 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] sympy-0.7.4.1/sympy/solvers/inequalities.py0000644000175000017500000003064312253362407021176 0ustar georgeskgeorgesk"""Tools for solving inequalities and systems of inequalities. """ from __future__ import print_function, division from sympy.core import Symbol, Interval from sympy.core.relational import Relational, Eq, Ge, Lt from sympy.core.singleton import S from sympy.assumptions import ask, AppliedPredicate, Q from sympy.functions import re, im, Abs from sympy.logic import And from sympy.polys import Poly, PolynomialError, parallel_poly_from_expr def solve_poly_inequality(poly, rel): """Solve a polynomial inequality with rational coefficients. Examples ======== >>> from sympy import Poly >>> from sympy.abc import x >>> from sympy.solvers.inequalities import solve_poly_inequality >>> solve_poly_inequality(Poly(x, x, domain='ZZ'), '==') [{0}] >>> solve_poly_inequality(Poly(x**2 - 1, x, domain='ZZ'), '!=') [(-oo, -1), (-1, 1), (1, oo)] >>> solve_poly_inequality(Poly(x**2 - 1, x, domain='ZZ'), '==') [{-1}, {1}] See Also ======== solve_poly_inequalities """ reals, intervals = poly.real_roots(multiple=False), [] if rel == '==': for root, _ in reals: interval = Interval(root, root) intervals.append(interval) elif rel == '!=': left = S.NegativeInfinity for right, _ in reals + [(S.Infinity, 1)]: interval = Interval(left, right, True, True) intervals.append(interval) left = right else: if poly.LC() > 0: sign = +1 else: sign = -1 eq_sign, equal = None, False if rel == '>': eq_sign = +1 elif rel == '<': eq_sign = -1 elif rel == '>=': eq_sign, equal = +1, True elif rel == '<=': eq_sign, equal = -1, True else: raise ValueError("'%s' is not a valid relation" % rel) right, right_open = S.Infinity, True reals.sort(key=lambda w: w[0], reverse=True) for left, multiplicity in reals: if multiplicity % 2: if sign == eq_sign: intervals.insert( 0, Interval(left, right, not equal, right_open)) sign, right, right_open = -sign, left, not equal else: if sign == eq_sign and not equal: intervals.insert( 0, Interval(left, right, True, right_open)) right, right_open = left, True elif sign != eq_sign and equal: intervals.insert(0, Interval(left, left)) if sign == eq_sign: intervals.insert( 0, Interval(S.NegativeInfinity, right, True, right_open)) return intervals def solve_rational_inequalities(eqs): """Solve a system of rational inequalities with rational coefficients. Examples ======== >>> from sympy.abc import x >>> from sympy import Poly >>> from sympy.solvers.inequalities import solve_rational_inequalities >>> solve_rational_inequalities([[ ... ((Poly(-x + 1), Poly(1, x)), '>='), ... ((Poly(-x + 1), Poly(1, x)), '<=')]]) {1} >>> solve_rational_inequalities([[ ... ((Poly(x), Poly(1, x)), '!='), ... ((Poly(-x + 1), Poly(1, x)), '>=')]]) (-oo, 0) U (0, 1] See Also ======== solve_poly_inequality """ result = S.EmptySet for _eqs in eqs: global_intervals = None for (numer, denom), rel in _eqs: numer_intervals = solve_poly_inequality(numer*denom, rel) denom_intervals = solve_poly_inequality(denom, '==') if global_intervals is None: global_intervals = numer_intervals else: intervals = [] for numer_interval in numer_intervals: for global_interval in global_intervals: interval = numer_interval.intersect(global_interval) if interval is not S.EmptySet: intervals.append(interval) global_intervals = intervals intervals = [] for global_interval in global_intervals: for denom_interval in denom_intervals: global_interval -= denom_interval if global_interval is not S.EmptySet: intervals.append(global_interval) global_intervals = intervals if not global_intervals: break for interval in global_intervals: result = result.union(interval) return result def reduce_rational_inequalities(exprs, gen, assume=True, relational=True): """Reduce a system of rational inequalities with rational coefficients. Examples ======== >>> from sympy import Poly, Symbol >>> from sympy.solvers.inequalities import reduce_rational_inequalities >>> x = Symbol('x', real=True) >>> reduce_rational_inequalities([[x**2 <= 0]], x) x == 0 >>> reduce_rational_inequalities([[x + 2 > 0]], x) x > -2 """ exact = True eqs = [] for _exprs in exprs: _eqs = [] for expr in _exprs: if isinstance(expr, tuple): expr, rel = expr else: if expr.is_Relational: expr, rel = expr.lhs - expr.rhs, expr.rel_op else: expr, rel = expr, '==' try: (numer, denom), opt = parallel_poly_from_expr(expr.together().as_numer_denom(), gen) except PolynomialError: raise PolynomialError("only polynomials and rational functions are supported in this context") if not opt.domain.is_Exact: numer, denom, exact = numer.to_exact(), denom.to_exact(), False domain = opt.domain.get_exact() if not (domain.is_ZZ or domain.is_QQ): raise NotImplementedError("inequality solving is not supported over %s" % opt.domain) _eqs.append(((numer, denom), rel)) eqs.append(_eqs) solution = solve_rational_inequalities(eqs) if not exact: solution = solution.evalf() if not relational: return solution real = ask(Q.real(gen), assumptions=assume) if not real: result = And(solution.as_relational(re(gen)), Eq(im(gen), 0)) else: result = solution.as_relational(gen) return result def reduce_abs_inequality(expr, rel, gen, assume=True): """Reduce an inequality with nested absolute values. Examples ======== >>> from sympy import Q, Abs >>> from sympy.abc import x >>> from sympy.solvers.inequalities import reduce_abs_inequality >>> reduce_abs_inequality(Abs(x - 5) - 3, '<', x, assume=Q.real(x)) And(2 < x, x < 8) >>> reduce_abs_inequality(Abs(x + 2)*3 - 13, '<', x, assume=Q.real(x)) And(-19/3 < x, x < 7/3) See Also ======== reduce_abs_inequalities """ if not ask(Q.real(gen), assumptions=assume): raise NotImplementedError("can't solve inequalities with absolute values of a complex variable") def _bottom_up_scan(expr): exprs = [] if expr.is_Add or expr.is_Mul: op = expr.__class__ for arg in expr.args: _exprs = _bottom_up_scan(arg) if not exprs: exprs = _exprs else: args = [] for expr, conds in exprs: for _expr, _conds in _exprs: args.append((op(expr, _expr), conds + _conds)) exprs = args elif expr.is_Pow: n = expr.exp if not n.is_Integer or n < 0: raise ValueError( "only non-negative integer powers are allowed") _exprs = _bottom_up_scan(expr.base) for expr, conds in _exprs: exprs.append((expr**n, conds)) elif isinstance(expr, Abs): _exprs = _bottom_up_scan(expr.args[0]) for expr, conds in _exprs: exprs.append(( expr, conds + [Ge(expr, 0)])) exprs.append((-expr, conds + [Lt(expr, 0)])) else: exprs = [(expr, [])] return exprs exprs = _bottom_up_scan(expr) mapping = {'<': '>', '<=': '>='} inequalities = [] for expr, conds in exprs: if rel not in mapping.keys(): expr = Relational( expr, 0, rel) else: expr = Relational(-expr, 0, mapping[rel]) inequalities.append([expr] + conds) return reduce_rational_inequalities(inequalities, gen, assume) def reduce_abs_inequalities(exprs, gen, assume=True): """Reduce a system of inequalities with nested absolute values. Examples ======== >>> from sympy import Q, Abs >>> from sympy.abc import x >>> from sympy.solvers.inequalities import reduce_abs_inequalities >>> reduce_abs_inequalities([(Abs(3*x - 5) - 7, '<'), ... (Abs(x + 25) - 13, '>')], x, assume=Q.real(x)) And(-2/3 < x, Or(x < -38, x > -12), x < 4) >>> reduce_abs_inequalities([(Abs(x - 4) + Abs(3*x - 5) - 7, '<')], x, ... assume=Q.real(x)) And(1/2 < x, x < 4) See Also ======== reduce_abs_inequality """ return And(*[ reduce_abs_inequality(expr, rel, gen, assume) for expr, rel in exprs ]) def _solve_inequality(ie, s): """ A hacky replacement for solve, since the latter only works for univariate inequalities. """ if not ie.rel_op in ('>', '>=', '<', '<='): raise NotImplementedError expr = ie.lhs - ie.rhs try: p = Poly(expr, s) except PolynomialError: raise NotImplementedError if p.degree() != 1: raise NotImplementedError('%s' % ie) a, b = p.all_coeffs() if a.is_positive: return ie.func(s, -b/a) elif a.is_negative: return ie.func(-b/a, s) else: raise NotImplementedError def reduce_inequalities(inequalities, assume=True, symbols=[]): """Reduce a system of inequalities with rational coefficients. Examples ======== >>> from sympy import Q, sympify as S >>> from sympy.abc import x, y >>> from sympy.solvers.inequalities import reduce_inequalities >>> reduce_inequalities(S(0) <= x + 3, Q.real(x), []) x >= -3 >>> reduce_inequalities(S(0) <= x + y*2 - 1, True, [x]) -2*y + 1 <= x """ if not hasattr(inequalities, '__iter__'): inequalities = [inequalities] if len(inequalities) == 1 and len(symbols) == 1 \ and inequalities[0].is_Relational: try: return _solve_inequality(inequalities[0], symbols[0]) except NotImplementedError: pass poly_part, abs_part, extra_assume = {}, {}, [] for inequality in inequalities: if isinstance(inequality, bool): if inequality is False: return False else: continue if isinstance(inequality, AppliedPredicate): extra_assume.append(inequality) continue if inequality.is_Relational: expr, rel = inequality.lhs - inequality.rhs, inequality.rel_op else: expr, rel = inequality, '==' gens = expr.atoms(Symbol) if not gens: return False elif len(gens) == 1: gen = gens.pop() else: raise NotImplementedError( "only univariate inequalities are supported") components = expr.find(lambda u: u.is_Function) if not components: if gen in poly_part: poly_part[gen].append((expr, rel)) else: poly_part[gen] = [(expr, rel)] else: if all(isinstance(comp, Abs) for comp in components): if gen in abs_part: abs_part[gen].append((expr, rel)) else: abs_part[gen] = [(expr, rel)] else: raise NotImplementedError("can't reduce %s" % inequalities) extra_assume = And(*extra_assume) if assume is not None: assume = And(assume, extra_assume) else: assume = extra_assume poly_reduced = [] abs_reduced = [] for gen, exprs in poly_part.items(): poly_reduced.append(reduce_rational_inequalities([exprs], gen, assume)) for gen, exprs in abs_part.items(): abs_reduced.append(reduce_abs_inequalities(exprs, gen, assume)) return And(*(poly_reduced + abs_reduced)) sympy-0.7.4.1/sympy/solvers/tests/0000755000175000017500000000000012253362407017264 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/solvers/tests/test_solvers.py0000644000175000017500000015022412253362407022376 0ustar georgeskgeorgeskfrom sympy import ( Abs, And, Derivative, Dummy, Eq, Float, Function, Gt, I, Integral, LambertW, Lt, Matrix, Or, Piecewise, Poly, Q, Rational, S, Symbol, Wild, acos, asin, atan, atanh, cos, cosh, diff, exp, expand, im, log, pi, re, sec, sin, sinh, solve, solve_linear, sqrt, sstr, symbols, sympify, tan, tanh, root, simplify, atan2, arg) from sympy.core.function import nfloat from sympy.solvers import solve_linear_system, solve_linear_system_LU, \ solve_undetermined_coeffs from sympy.solvers.solvers import _invert, unrad, checksol, posify, _ispow from sympy.polys.rootoftools import RootOf from sympy.utilities.pytest import slow, XFAIL, raises, skip from sympy.utilities.randtest import test_numerically as tn from sympy.abc import a, b, c, d, k, h, p, x, y, z, t, q, m def NS(e, n=15, **options): return sstr(sympify(e).evalf(n, **options), full_prec=True) def test_swap_back(): f, g = map(Function, 'fg') fx, gx = f(x), g(x) assert solve([fx + y - 2, fx - gx - 5], fx, y, gx) == \ {fx: gx + 5, y: -gx - 3} assert solve(fx + gx*x - 2, [fx, gx]) == {fx: 2, gx: 0} assert solve(fx + gx**2*x - y, [fx, gx]) == [{fx: y - gx**2*x}] assert solve([f(1) - 2, x + 2]) == [{x: -2, f(1): 2}] def guess_solve_strategy(eq, symbol): try: solve(eq, symbol) return True except (TypeError, NotImplementedError): return False def test_guess_poly(): # polynomial equations assert guess_solve_strategy( S(4), x ) # == GS_POLY assert guess_solve_strategy( x, x ) # == GS_POLY assert guess_solve_strategy( x + a, x ) # == GS_POLY assert guess_solve_strategy( 2*x, x ) # == GS_POLY assert guess_solve_strategy( x + sqrt(2), x) # == GS_POLY assert guess_solve_strategy( x + 2**Rational(1, 4), x) # == GS_POLY assert guess_solve_strategy( x**2 + 1, x ) # == GS_POLY assert guess_solve_strategy( x**2 - 1, x ) # == GS_POLY assert guess_solve_strategy( x*y + y, x ) # == GS_POLY assert guess_solve_strategy( x*exp(y) + y, x) # == GS_POLY assert guess_solve_strategy( (x - y**3)/(y**2*sqrt(1 - y**2)), x) # == GS_POLY def test_guess_poly_cv(): # polynomial equations via a change of variable assert guess_solve_strategy( sqrt(x) + 1, x ) # == GS_POLY_CV_1 assert guess_solve_strategy( x**Rational(1, 3) + sqrt(x) + 1, x ) # == GS_POLY_CV_1 assert guess_solve_strategy( 4*x*(1 - sqrt(x)), x ) # == GS_POLY_CV_1 # polynomial equation multiplying both sides by x**n assert guess_solve_strategy( x + 1/x + y, x ) # == GS_POLY_CV_2 def test_guess_rational_cv(): # rational functions assert guess_solve_strategy( (x + 1)/(x**2 + 2), x) # == GS_RATIONAL assert guess_solve_strategy( (x - y**3)/(y**2*sqrt(1 - y**2)), y) # == GS_RATIONAL_CV_1 # rational functions via the change of variable y -> x**n assert guess_solve_strategy( (sqrt(x) + 1)/(x**Rational(1, 3) + sqrt(x) + 1), x ) \ #== GS_RATIONAL_CV_1 def test_guess_transcendental(): #transcendental functions assert guess_solve_strategy( exp(x) + 1, x ) # == GS_TRANSCENDENTAL assert guess_solve_strategy( 2*cos(x) - y, x ) # == GS_TRANSCENDENTAL assert guess_solve_strategy( exp(x) + exp(-x) - y, x ) # == GS_TRANSCENDENTAL assert guess_solve_strategy(3**x - 10, x) # == GS_TRANSCENDENTAL assert guess_solve_strategy(-3**x + 10, x) # == GS_TRANSCENDENTAL assert guess_solve_strategy(a*x**b - y, x) # == GS_TRANSCENDENTAL def test_solve_args(): # implicit symbol to solve for assert set(solve(x**2 - 4)) == set([S(2), -S(2)]) assert solve([x + y - 3, x - y - 5]) == {x: 4, y: -1} assert solve(x - exp(x), x, implicit=True) == [exp(x)] # no symbol to solve for assert solve(42) == [] assert solve([1, 2]) == [] # duplicate symbols removed assert solve((x - 3, y + 2), x, y, x) == {x: 3, y: -2} # unordered symbols # only 1 assert solve(y - 3, set([y])) == [3] # more than 1 assert solve(y - 3, set([x, y])) == [{y: 3}] # multiple symbols: take the first linear solution assert solve(x + y - 3, [x, y]) == [{x: 3 - y}] # unless it is an undetermined coefficients system assert solve(a + b*x - 2, [a, b]) == {a: 2, b: 0} assert solve(a*x**2 + b*x + c - ((x - h)**2 + 4*p*k)/4/p, [h, p, k], exclude=[a, b, c], dict=True) == \ [{k: (4*a*c - b**2)/(4*a), h: -b/(2*a), p: 1/(4*a)}] # failing undetermined system assert solve(a*x + b**2/(x + 4) - 3*x - 4/x, a, b) == \ [{a: (-b**2*x + 3*x**3 + 12*x**2 + 4*x + 16)/(x**2*(x + 4))}] # failed single equation assert solve(1/(1/x - y + exp(y))) == [] raises( NotImplementedError, lambda: solve(exp(x) + sin(x) + exp(y) + sin(y))) # failed system # -- when no symbols given, 1 fails assert solve([y, exp(x) + x]) == [{x: -LambertW(1), y: 0}] # both fail assert solve( (exp(x) - x, exp(y) - y)) == [{x: -LambertW(-1), y: -LambertW(-1)}] # -- when symbols given solve([y, exp(x) + x], x, y) == [(-LambertW(1), 0)] # symbol is a number assert solve(x**2 - pi, pi) == [x**2] # no equations assert solve([], [x]) == [] # overdetermined system # - nonlinear assert solve([(x + y)**2 - 4, x + y - 2]) == [{x: -y + 2}] # - linear assert solve((x + y - 2, 2*x + 2*y - 4)) == {x: -y + 2} def test_solve_polynomial1(): assert solve(3*x - 2, x) == [Rational(2, 3)] assert solve(Eq(3*x, 2), x) == [Rational(2, 3)] assert set(solve(x**2 - 1, x)) == set([-S(1), S(1)]) assert set(solve(Eq(x**2, 1), x)) == set([-S(1), S(1)]) assert solve(x - y**3, x) == [y**3] assert set(solve(x - y**3, y)) == set([ (-x**Rational(1, 3))/2 + I*sqrt(3)*x**Rational(1, 3)/2, x**Rational(1, 3), (-x**Rational(1, 3))/2 - I*sqrt(3)*x**Rational(1, 3)/2, ]) a11, a12, a21, a22, b1, b2 = symbols('a11,a12,a21,a22,b1,b2') assert solve([a11*x + a12*y - b1, a21*x + a22*y - b2], x, y) == \ { x: (a22*b1 - a12*b2)/(a11*a22 - a12*a21), y: (a11*b2 - a21*b1)/(a11*a22 - a12*a21), } solution = {y: S.Zero, x: S.Zero} assert solve((x - y, x + y), x, y ) == solution assert solve((x - y, x + y), (x, y)) == solution assert solve((x - y, x + y), [x, y]) == solution assert set(solve(x**3 - 15*x - 4, x)) == set([ -2 + 3**Rational(1, 2), S(4), -2 - 3**Rational(1, 2) ]) assert set(solve((x**2 - 1)**2 - a, x)) == \ set([sqrt(1 + sqrt(a)), -sqrt(1 + sqrt(a)), sqrt(1 - sqrt(a)), -sqrt(1 - sqrt(a))]) def test_solve_polynomial2(): assert solve(4, x) == [] def test_solve_polynomial_cv_1a(): """ Test for solving on equations that can be converted to a polynomial equation using the change of variable y -> x**Rational(p, q) """ assert solve( sqrt(x) - 1, x) == [1] assert solve( sqrt(x) - 2, x) == [4] assert solve( x**Rational(1, 4) - 2, x) == [16] assert solve( x**Rational(1, 3) - 3, x) == [27] assert solve(sqrt(x) + x**Rational(1, 3) + x**Rational(1, 4), x) == [0] def test_solve_polynomial_cv_1b(): assert set(solve(4*x*(1 - a*sqrt(x)), x)) == set([S(0), 1/a**2]) assert set(solve(x * (x**(S(1)/3) - 3), x)) == set([S(0), S(27)]) def test_solve_polynomial_cv_2(): """ Test for solving on equations that can be converted to a polynomial equation multiplying both sides of the equation by x**m """ assert solve(x + 1/x - 1, x) in \ [[ Rational(1, 2) + I*sqrt(3)/2, Rational(1, 2) - I*sqrt(3)/2], [ Rational(1, 2) - I*sqrt(3)/2, Rational(1, 2) + I*sqrt(3)/2]] def test_quintics_1(): f = x**5 - 110*x**3 - 55*x**2 + 2310*x + 979 s = solve(f, check=False) for root in s: res = f.subs(x, root.n()).n() assert tn(res, 0) f = x**5 - 15*x**3 - 5*x**2 + 10*x + 20 s = solve(f) for root in s: assert root.func == RootOf # if one uses solve to get the roots of a polynomial that has a RootOf # solution, make sure that the use of nfloat during the solve process # doesn't fail. Note: if you want numerical solutions to a polynomial # it is *much* faster to use nroots to get them than to solve the # equation only to get RootOf solutions which are then numerically # evaluated. So for eq = x**5 + 3*x + 7 do Poly(eq).nroots() rather # than [i.n() for i in solve(eq)] to get the numerical roots of eq. assert nfloat(solve(x**5 + 3*x**3 + 7)[0], exponent=False) == \ RootOf(x**5 + 3*x**3 + 7, 0).n() def test_highorder_poly(): # just testing that the uniq generator is unpacked sol = solve(x**6 - 2*x + 2) assert all(isinstance(i, RootOf) for i in sol) and len(sol) == 6 @XFAIL @slow def test_quintics_2(): f = x**5 + 15*x + 12 s = solve(f, check=False) for root in s: res = f.subs(x, root.n()).n() assert tn(res, 0) f = x**5 - 15*x**3 - 5*x**2 + 10*x + 20 s = solve(f) for root in s: assert root.func == RootOf def test_solve_rational(): """Test solve for rational functions""" assert solve( ( x - y**3 )/( (y**2)*sqrt(1 - y**2) ), x) == [y**3] def test_solve_nonlinear(): assert solve(x**2 - y**2, x, y) == [{x: -y}, {x: y}] assert solve(x**2 - y**2/exp(x), x, y) == [{x: 2*LambertW(y/2)}] assert solve(x**2 - y**2/exp(x), y, x) == [{y: -x*exp(x/2)}, {y: x*exp(x/2)}] def test_linear_system(): x, y, z, t, n = symbols('x, y, z, t, n') assert solve([x - 1, x - y, x - 2*y, y - 1], [x, y]) == [] assert solve([x - 1, x - y, x - 2*y, x - 1], [x, y]) == [] assert solve([x - 1, x - 1, x - y, x - 2*y], [x, y]) == [] assert solve([x + 5*y - 2, -3*x + 6*y - 15], x, y) == {x: -3, y: 1} M = Matrix([[0, 0, n*(n + 1), (n + 1)**2, 0], [n + 1, n + 1, -2*n - 1, -(n + 1), 0], [-1, 0, 1, 0, 0]]) assert solve_linear_system(M, x, y, z, t) == \ {x: -t - t/n, z: -t - t/n, y: 0} assert solve([x + y + z + t, -z - t], x, y, z, t) == {x: -y, z: -t} def test_linear_system_function(): a = Function('a') assert solve([a(0, 0) + a(0, 1) + a(1, 0) + a(1, 1), -a(1, 0) - a(1, 1)], a(0, 0), a(0, 1), a(1, 0), a(1, 1)) == {a(1, 0): -a(1, 1), a(0, 0): -a(0, 1)} def test_linear_systemLU(): n = Symbol('n') M = Matrix([[1, 2, 0, 1], [1, 3, 2*n, 1], [4, -1, n**2, 1]]) assert solve_linear_system_LU(M, [x, y, z]) == {z: -3/(n**2 + 18*n), x: 1 - 12*n/(n**2 + 18*n), y: 6*n/(n**2 + 18*n)} # Note: multiple solutions exist for some of these equations, so the tests # should be expected to break if the implementation of the solver changes # in such a way that a different branch is chosen def test_tsolve(): assert solve(exp(x) - 3, x) == [log(3)] assert set(solve((a*x + b)*(exp(x) - 3), x)) == set([-b/a, log(3)]) assert solve(cos(x) - y, x) == [-acos(y) + 2*pi, acos(y)] assert solve(2*cos(x) - y, x) == [-acos(y/2) + 2*pi, acos(y/2)] assert solve(Eq(cos(x), sin(x)), x) == [-3*pi/4, pi/4] assert set(solve(exp(x) + exp(-x) - y, x)) in [set([ log(y/2 - sqrt(y**2 - 4)/2), log(y/2 + sqrt(y**2 - 4)/2), ]), set([ log(y - sqrt(y**2 - 4)) - log(2), log(y + sqrt(y**2 - 4)) - log(2)]), set([ log(y/2 - sqrt((y - 2)*(y + 2))/2), log(y/2 + sqrt((y - 2)*(y + 2))/2)])] assert solve(exp(x) - 3, x) == [log(3)] assert solve(Eq(exp(x), 3), x) == [log(3)] assert solve(log(x) - 3, x) == [exp(3)] assert solve(sqrt(3*x) - 4, x) == [Rational(16, 3)] assert solve(3**(x + 2), x) == [] assert solve(3**(2 - x), x) == [] assert solve(x + 2**x, x) == [-LambertW(log(2))/log(2)] ans = solve(3*x + 5 + 2**(-5*x + 3), x) assert len(ans) == 1 and ans[0].expand() == \ -Rational(5, 3) + LambertW(-10240*2**(S(1)/3)*log(2)/3)/(5*log(2)) assert solve(5*x - 1 + 3*exp(2 - 7*x), x) == \ [Rational(1, 5) + LambertW(-21*exp(Rational(3, 5))/5)/7] assert solve(2*x + 5 + log(3*x - 2), x) == \ [Rational(2, 3) + LambertW(2*exp(-Rational(19, 3))/3)/2] assert solve(3*x + log(4*x), x) == [LambertW(Rational(3, 4))/3] assert set(solve((2*x + 8)*(8 + exp(x)), x)) == set([S(-4), log(8) + pi*I]) eq = 2*exp(3*x + 4) - 3 ans = solve(eq, x) # this generated a failure in flatten assert len(ans) == 3 and all(eq.subs(x, a).n(chop=True) == 0 for a in ans) assert solve(2*log(3*x + 4) - 3, x) == [(exp(Rational(3, 2)) - 4)/3] assert solve(exp(x) + 1, x) == [pi*I] eq = 2*(3*x + 4)**5 - 6*7**(3*x + 9) result = solve(eq, x) ans = [(log(2401) + 5*LambertW(-log(7**(7*3**Rational(1, 5)/5))))/(3*log(7))/-1] assert result == ans # it works if expanded, too assert solve(eq.expand(), x) == result assert solve(z*cos(x) - y, x) == [-acos(y/z) + 2*pi, acos(y/z)] assert solve(z*cos(2*x) - y, x) == [-acos(y/z)/2 + pi, acos(y/z)/2] assert solve(z*cos(sin(x)) - y, x) == [ asin(acos(y/z) - 2*pi) + pi, -asin(acos(y/z)) + pi, -asin(acos(y/z) - 2*pi), asin(acos(y/z))] assert solve(z*cos(x), x) == [pi/2, 3*pi/2] # issue #1409 assert solve(y - b*x/(a + x), x) in [[-a*y/(y - b)], [a*y/(b - y)]] assert solve(y - b*exp(a/x), x) == [a/log(y/b)] # issue #1408 assert solve(y - b/(1 + a*x), x) in [[(b - y)/(a*y)], [-((y - b)/(a*y))]] # issue #1407 assert solve(y - a*x**b, x) == [(y/a)**(1/b)] # issue #1406 assert solve(z**x - y, x) == [log(y)/log(z)] # issue #1405 assert solve(2**x - 10, x) == [log(10)/log(2)] # issue #3645 assert solve(x*y) == [{x: 0}, {y: 0}] assert solve([x*y]) == [{x: 0}, {y: 0}] assert solve(x**y - 1) == [{x: 1}, {y: 0}] assert solve([x**y - 1]) == [{x: 1}, {y: 0}] assert solve(x*y*(x**2 - y**2)) == [{x: 0}, {x: -y}, {x: y}, {y: 0}] assert solve([x*y*(x**2 - y**2)]) == [{x: 0}, {x: -y}, {x: y}, {y: 0}] #issue #1640 assert solve(exp(log(5)*x) - 2**x, x) == [0] def test_solve_for_functions_derivatives(): t = Symbol('t') x = Function('x')(t) y = Function('y')(t) a11, a12, a21, a22, b1, b2 = symbols('a11,a12,a21,a22,b1,b2') soln = solve([a11*x + a12*y - b1, a21*x + a22*y - b2], x, y) assert soln == { x: (a22*b1 - a12*b2)/(a11*a22 - a12*a21), y: (a11*b2 - a21*b1)/(a11*a22 - a12*a21), } assert solve(x - 1, x) == [1] assert solve(3*x - 2, x) == [Rational(2, 3)] soln = solve([a11*x.diff(t) + a12*y.diff(t) - b1, a21*x.diff(t) + a22*y.diff(t) - b2], x.diff(t), y.diff(t)) assert soln == { y.diff(t): (a11*b2 - a21*b1)/(a11*a22 - a12*a21), x.diff(t): (a22*b1 - a12*b2)/(a11*a22 - a12*a21) } assert solve(x.diff(t) - 1, x.diff(t)) == [1] assert solve(3*x.diff(t) - 2, x.diff(t)) == [Rational(2, 3)] eqns = set((3*x - 1, 2*y - 4)) assert solve(eqns, set((x, y))) == { x: Rational(1, 3), y: 2 } x = Symbol('x') f = Function('f') F = x**2 + f(x)**2 - 4*x - 1 assert solve(F.diff(x), diff(f(x), x)) == [(-x + 2)/f(x)] # Mixed cased with a Symbol and a Function x = Symbol('x') y = Function('y')(t) soln = solve([a11*x + a12*y.diff(t) - b1, a21*x + a22*y.diff(t) - b2], x, y.diff(t)) assert soln == { y.diff(t): (a11*b2 - a21*b1)/(a11*a22 - a12*a21), x: (a22*b1 - a12*b2)/(a11*a22 - a12*a21) } def test_issue626(): f = Function('f') F = x**2 + f(x)**2 - 4*x - 1 e = F.diff(x) assert solve(e, f(x).diff(x)) in [[(2 - x)/f(x)], [-((x - 2)/f(x))]] def test_issue771(): a, b, c, d = symbols('a b c d') A = Matrix(2, 2, [a, b, c, d]) B = Matrix(2, 2, [0, 2, -3, 0]) C = Matrix(2, 2, [1, 2, 3, 4]) assert solve(A*B - C, [a, b, c, d]) == {a: 1, b: -S(1)/3, c: 2, d: -1} assert solve([A*B - C], [a, b, c, d]) == {a: 1, b: -S(1)/3, c: 2, d: -1} assert solve(Eq(A*B, C), [a, b, c, d]) == {a: 1, b: -S(1)/3, c: 2, d: -1} assert solve([A*B - B*A], [a, b, c, d]) == {a: d, b: -S(2)/3*c} assert solve([A*C - C*A], [a, b, c, d]) == {a: d - c, b: S(2)/3*c} assert solve([A*B - B*A, A*C - C*A], [a, b, c, d]) == {a: d, b: 0, c: 0} assert solve([Eq(A*B, B*A)], [a, b, c, d]) == {a: d, b: -S(2)/3*c} assert solve([Eq(A*C, C*A)], [a, b, c, d]) == {a: d - c, b: S(2)/3*c} assert solve([Eq(A*B, B*A), Eq(A*C, C*A)], [a, b, c, d]) == {a: d, b: 0, c: 0} def test_solve_linear(): w = Wild('w') assert solve_linear(x, x) == (0, 1) assert solve_linear(x, y - 2*x) in [(x, y/3), (y, 3*x)] assert solve_linear(x, y - 2*x, exclude=[x]) == (y, 3*x) assert solve_linear(3*x - y, 0) in [(x, y/3), (y, 3*x)] assert solve_linear(3*x - y, 0, [x]) == (x, y/3) assert solve_linear(3*x - y, 0, [y]) == (y, 3*x) assert solve_linear(x**2/y, 1) == (y, x**2) assert solve_linear(w, x) in [(w, x), (x, w)] assert solve_linear(cos(x)**2 + sin(x)**2 + 2 + y) == \ (y, -2 - cos(x)**2 - sin(x)**2) assert solve_linear(cos(x)**2 + sin(x)**2 + 2 + y, symbols=[x]) == (0, 1) assert solve_linear(Eq(x, 3)) == (x, 3) assert solve_linear(1/(1/x - 2)) == (0, 0) assert solve_linear((x + 1)*exp(-x), symbols=[x]) == (x + 1, exp(x)) assert solve_linear((x + 1)*exp(x), symbols=[x]) == ((x + 1)*exp(x), 1) assert solve_linear(x*exp(-x**2), symbols=[x]) == (0, 0) raises(ValueError, lambda: solve_linear(Eq(x, 3), 3)) def test_solve_undetermined_coeffs(): assert solve_undetermined_coeffs(a*x**2 + b*x**2 + b*x + 2*c*x + c + 1, [a, b, c], x) == \ {a: -2, b: 2, c: -1} # Test that rational functions work assert solve_undetermined_coeffs(a/x + b/(x + 1) - (2*x + 1)/(x**2 + x), [a, b], x) == \ {a: 1, b: 1} # Test cancellation in rational functions assert solve_undetermined_coeffs(((c + 1)*a*x**2 + (c + 1)*b*x**2 + (c + 1)*b*x + (c + 1)*2*c*x + (c + 1)**2)/(c + 1), [a, b, c], x) == \ {a: -2, b: 2, c: -1} def test_solve_inequalities(): system = [Lt(x**2 - 2, 0), Gt(x**2 - 1, 0)] assert solve(system) == \ And(Or(And(Lt(-sqrt(2), re(x)), Lt(re(x), -1)), And(Lt(1, re(x)), Lt(re(x), sqrt(2)))), Eq(im(x), 0)) assert solve(system, assume=Q.real(x)) == \ Or(And(Lt(-sqrt(2), x), Lt(x, -1)), And(Lt(1, x), Lt(x, sqrt(2)))) # issue 3528, 3448 assert solve((x - 3)/(x - 2) < 0, x, assume=Q.real(x)) == And(Lt(2, x), Lt(x, 3)) assert solve(x/(x + 1) > 1, x, assume=Q.real(x)) == Lt(x, -1) def test_issue_1694(): assert solve(1/x) == [] assert solve(x*(1 - 5/x)) == [5] assert solve(x + sqrt(x) - 2) == [1] assert solve(-(1 + x)/(2 + x)**2 + 1/(2 + x)) == [] assert solve(-x**2 - 2*x + (x + 1)**2 - 1) == [] assert solve((x/(x + 1) + 3)**(-2)) == [] assert solve(x/sqrt(x**2 + 1), x) == [0] assert solve(exp(x) - y, x) == [log(y)] assert solve(exp(x)) == [] assert solve(x**2 + x + sin(y)**2 + cos(y)**2 - 1, x) in [[0, -1], [-1, 0]] eq = 4*3**(5*x + 2) - 7 ans = solve(eq, x) assert len(ans) == 5 and all(eq.subs(x, a).n(chop=True) == 0 for a in ans) assert solve(log(x**2) - y**2/exp(x), x, y, set=True) == \ ([y], set([ (-sqrt(exp(x)*log(x**2)),), (sqrt(exp(x)*log(x**2)),)])) assert solve(x**2*z**2 - z**2*y**2) == [{x: -y}, {x: y}, {z: 0}] assert solve((x - 1)/(1 + 1/(x - 1))) == [] assert solve(x**(y*z) - x, x) == [1] raises(NotImplementedError, lambda: solve(log(x) - exp(x), x)) raises(NotImplementedError, lambda: solve(2**x - exp(x) - 3)) def test_PR1964(): # 2072 assert solve(sqrt(x)) == solve(sqrt(x**3)) == [0] assert solve(sqrt(x - 1)) == [1] # 1363 a = Symbol('a') assert solve(-3*a/sqrt(x), x) == [] # 1387 assert solve(2*x/(x + 2) - 1, x) == [2] # 1397 assert set(solve((x**2/(7 - x)).diff(x))) == set([S(0), S(14)]) # 1596 f = Function('f') assert solve((3 - 5*x/f(x))*f(x), f(x)) == [5*x/3] # 1398 assert solve(1/(5 + x)**(S(1)/5) - 9, x) == [-295244/S(59049)] assert solve(sqrt(x) + sqrt(sqrt(x)) - 4) == [-9*sqrt(17)/2 + 49*S.Half] assert set(solve(Poly(sqrt(exp(x)) + sqrt(exp(-x)) - 4))) in \ [ set([2*log(-sqrt(3) + 2), 2*log(sqrt(3) + 2)]), set([log(-4*sqrt(3) + 7), log(4*sqrt(3) + 7)]), ] assert set(solve(Poly(exp(x) + exp(-x) - 4))) == \ set([log(-sqrt(3) + 2), log(sqrt(3) + 2)]) assert set(solve(x**y + x**(2*y) - 1, x)) == \ set([(-S.Half + sqrt(5)/2)**(1/y), (-S.Half - sqrt(5)/2)**(1/y)]) assert solve(exp(x/y)*exp(-z/y) - 2, y) == [(x - z)/log(2)] assert solve( x**z*y**z - 2, z) in [[log(2)/(log(x) + log(y))], [log(2)/(log(x*y))]] # if you do inversion too soon then multiple roots as for the following will # be missed, e.g. if exp(3*x) = exp(3) -> 3*x = 3 E = S.Exp1 assert set(solve(exp(3*x) - exp(3), x)) in [ set([S(1), log(-E/2 - sqrt(3)*E*I/2), log(-E/2 + sqrt(3)*E*I/2)]), set([S(1), log(E*(-S(1)/2 - sqrt(3)*I/2)), log(E*(-S(1)/2 + sqrt(3)*I/2))]), ] # coverage test p = Symbol('p', positive=True) assert solve((1/p + 1)**(p + 1)) == [] def test_issue_2098(): x = Symbol('x', real=True) assert solve(x**2 + 1, x) == [] n = Symbol('n', integer=True, positive=True) assert solve((n - 1)*(n + 2)*(2*n - 1), n) == [1] x = Symbol('x', positive=True) y = Symbol('y') assert solve([x + 5*y - 2, -3*x + 6*y - 15], x, y) == [] # not {x: -3, y: 1} b/c x is positive # The solution following should not contain (-sqrt(2), sqrt(2)) assert solve((x + y)*n - y**2 + 2, x, y) == [(sqrt(2), -sqrt(2))] y = Symbol('y', positive=True) # The solution following should not contain {y: -x*exp(x/2)} assert solve(x**2 - y**2/exp(x), y, x) == [{y: x*exp(x/2)}] assert solve(x**2 - y**2/exp(x), x, y) == [{x: 2*LambertW(y/2)}] x, y, z = symbols('x y z', positive=True) assert solve(z**2*x**2 - z**2*y**2/exp(x), y, x, z) == [{y: x*exp(x/2)}] def test_checking(): assert set( solve(x*(x - y/x), x, check=False)) == set([sqrt(y), S(0), -sqrt(y)]) assert set(solve(x*(x - y/x), x, check=True)) == set([sqrt(y), -sqrt(y)]) # {x: 0, y: 4} sets denominator to 0 in the following so system should return None assert solve((1/(1/x + 2), 1/(y - 3) - 1)) == [] # 0 sets denominator of 1/x to zero so None is returned assert solve(1/(1/x + 2)) == [] def test_issue_1572_1364_1368(): assert solve((sqrt(x**2 - 1) - 2)) in ([sqrt(5), -sqrt(5)], [-sqrt(5), sqrt(5)]) assert set(solve((2**exp(y**2/x) + 2)/(x**2 + 15), y)) == set([ -sqrt(x)*sqrt(-log(log(2)) + log(log(2) + I*pi)), sqrt(x)*sqrt(-log(log(2)) + log(log(2) + I*pi))]) C1, C2 = symbols('C1 C2') f = Function('f') assert solve(C1 + C2/x**2 - exp(-f(x)), f(x)) == [log(x**2/(C1*x**2 + C2))] a = Symbol('a') E = S.Exp1 assert solve(1 - log(a + 4*x**2), x) in ( [-sqrt(-a + E)/2, sqrt(-a + E)/2], [sqrt(-a + E)/2, -sqrt(-a + E)/2] ) assert solve(log(a**(-3) - x**2)/a, x) in ( [-sqrt(-1 + a**(-3)), sqrt(-1 + a**(-3))], [sqrt(-1 + a**(-3)), -sqrt(-1 + a**(-3))],) assert solve(1 - log(a + 4*x**2), x) in ( [-sqrt(-a + E)/2, sqrt(-a + E)/2], [sqrt(-a + E)/2, -sqrt(-a + E)/2],) assert set(solve(( a**2 + 1) * (sin(a*x) + cos(a*x)), x)) == set([-pi/(4*a), 3*pi/(4*a)]) assert solve(3 - (sinh(a*x) + cosh(a*x)), x) == [log(3)/a] assert set(solve(3 - (sinh(a*x) + cosh(a*x)**2), x)) == \ set([log(-2 + sqrt(5))/a, log(-sqrt(2) + 1)/a, log(-sqrt(5) - 2)/a, log(1 + sqrt(2))/a]) assert solve(atan(x) - 1) == [tan(1)] def test_issue_2033(): r, t = symbols('r,t') assert set(solve([r - x**2 - y**2, tan(t) - y/x], [x, y])) == \ set([ (-sqrt(r*tan(t)**2/(tan(t)**2 + 1))/tan(t), -sqrt(r*tan(t)**2/(tan(t)**2 + 1))), (sqrt(r*tan(t)**2/(tan(t)**2 + 1))/tan(t), sqrt(r*tan(t)**2/(tan(t)**2 + 1)))]) assert solve([exp(x) - sin(y), 1/y - 3], [x, y]) == \ [(log(sin(S(1)/3)), S(1)/3)] assert solve([exp(x) - sin(y), 1/exp(y) - 3], [x, y]) == \ [(log(-sin(log(3))), -log(3))] assert set(solve([exp(x) - sin(y), y**2 - 4], [x, y])) == \ set([(log(-sin(2)), -S(2)), (log(sin(2)), S(2))]) eqs = [exp(x)**2 - sin(y) + z**2, 1/exp(y) - 3] assert solve(eqs, set=True) == \ ([x, y], set([ (log(-sqrt(-z**2 - sin(log(3)))), -log(3)), (log(sqrt(-z**2 - sin(log(3)))), -log(3))])) assert solve(eqs, x, z, set=True) == \ ([x], set([ (log(-sqrt(-z**2 + sin(y))),), (log(sqrt(-z**2 + sin(y))),)])) assert set(solve(eqs, x, y)) == \ set([ (log(-sqrt(-z**2 - sin(log(3)))), -log(3)), (log(sqrt(-z**2 - sin(log(3)))), -log(3))]) assert set(solve(eqs, y, z)) == \ set([ (-log(3), -sqrt(-exp(2*x) - sin(log(3)))), (-log(3), sqrt(-exp(2*x) - sin(log(3))))]) eqs = [exp(x)**2 - sin(y) + z, 1/exp(y) - 3] assert solve(eqs, set=True) == ([x, y], set( [ (log(-sqrt(-z - sin(log(3)))), -log(3)), (log(sqrt(-z - sin(log(3)))), -log(3))])) assert solve(eqs, x, z, set=True) == ([x], set( [ (log(-sqrt(-z + sin(y))),), (log(sqrt(-z + sin(y))),)])) assert set(solve(eqs, x, y)) == set( [ (log(-sqrt(-z - sin(log(3)))), -log(3)), (log(sqrt(-z - sin(log(3)))), -log(3))]) assert solve(eqs, z, y) == \ [(-exp(2*x) - sin(log(3)), -log(3))] assert solve((sqrt(x**2 + y**2) - sqrt(10), x + y - 4), set=True) == ( [x, y], set([(S(1), S(3)), (S(3), S(1))])) assert set(solve((sqrt(x**2 + y**2) - sqrt(10), x + y - 4), x, y)) == \ set([(S(1), S(3)), (S(3), S(1))]) def test_issue_2236(): lam, a0, conc = symbols('lam a0 conc') eqs = [lam + 2*y - a0*(1 - x/2)*x - 0.005*x/2*x, a0*(1 - x/2)*x - 1*y - 0.743436700916726*y, x + y - conc] sym = [x, y, a0] # there are 4 solutions but only two are valid assert len(solve(eqs, sym, manual=True, minimal=True, simplify=False)) == 2 def test_issue_2236_float(): skip("This test hangs.") lam, a0, conc = symbols('lam a0 conc') eqs = [lam + 2*y - a0*(1 - x/2)*x - 0.005*x/2*x, a0*(1 - x/2)*x - 1*y - 0.743436700916726*y, x + y - conc] sym = [x, y, a0] assert len( solve(eqs, sym, rational=False, check=False, simplify=False)) == 2 def test_issue_2668(): assert set(solve([x**2 + y + 4], [x])) == \ set([(-sqrt(-y - 4),), (sqrt(-y - 4),)]) def test_polysys(): assert set(solve([x**2 + 2/y - 2, x + y - 3], [x, y])) == \ set([(S(1), S(2)), (1 + sqrt(5), 2 - sqrt(5)), (1 - sqrt(5), 2 + sqrt(5))]) assert solve([x**2 + y - 2, x**2 + y]) == [] # the ordering should be whatever the user requested assert solve([x**2 + y - 3, x - y - 4], (x, y)) != solve([x**2 + y - 3, x - y - 4], (y, x)) def test_unrad(): s = symbols('s', cls=Dummy) # checkers to deal with possibility of answer coming # back with a sign change (cf issue 2104) def check(rv, ans): rv, ans = list(rv), list(ans) rv[0] = rv[0].expand() ans[0] = ans[0].expand() return rv[0] in [ans[0], -ans[0]] and rv[1:] == ans[1:] def s_check(rv, ans): # get the dummy rv = list(rv) d = rv[0].atoms(Dummy) reps = list(zip(d, [s]*len(d))) # replace s with this dummy rv = (rv[0].subs(reps).expand(), [(p[0].subs(reps), p[1].subs(reps)) for p in rv[1]], [a.subs(reps) for a in rv[2]]) ans = (ans[0].subs(reps).expand(), [(p[0].subs(reps), p[1].subs(reps)) for p in ans[1]], [a.subs(reps) for a in ans[2]]) return str(rv[0]) in [str(ans[0]), str(-ans[0])] and \ str(rv[1:]) == str(ans[1:]) assert check(unrad(sqrt(x)), (x, [], [])) assert check(unrad(sqrt(x) + 1), (x - 1, [], [])) assert s_check(unrad(sqrt(x) + x**Rational(1, 3) + 2), (2 + s**2 + s**3, [(s, x - s**6)], [])) assert check(unrad(sqrt(x)*x**Rational(1, 3) + 2), (x**5 - 64, [], [])) assert check(unrad(sqrt(x) + (x + 1)**Rational(1, 3)), (x**3 - (x + 1)**2, [], [])) assert check(unrad(sqrt(x) + sqrt(x + 1) + sqrt(2*x)), (-2*sqrt(2)*x - 2*x + 1, [], [])) assert check(unrad(sqrt(x) + sqrt(x + 1) + 2), (16*x - 9, [], [])) assert check(unrad(sqrt(x) + sqrt(x + 1) + sqrt(1 - x)), (-4*x + 5*x**2, [], [])) assert check(unrad(a*sqrt(x) + b*sqrt(x) + c*sqrt(y) + d*sqrt(y)), ((a*sqrt(x) + b*sqrt(x))**2 - (c*sqrt(y) + d*sqrt(y))**2, [], [])) assert check(unrad(sqrt(x) + sqrt(1 - x)), (2*x - 1, [], [])) assert check(unrad(sqrt(x) + sqrt(1 - x) - 3), (9*x + (x - 5)**2 - 9, [], [])) assert check(unrad(sqrt(x) + sqrt(1 - x) + sqrt(2 + x)), (-5*x**2 + 2*x - 1, [], [])) assert check(unrad(sqrt(x) + sqrt(1 - x) + sqrt(2 + x) - 3), (-25*x**4 - 376*x**3 - 1256*x**2 + 2272*x - 784, [], [])) assert check(unrad(sqrt(x) + sqrt(1 - x) + sqrt(2 + x) - sqrt(1 - 2*x)), (-41*x**4 - 40*x**3 - 232*x**2 + 160*x - 16, [], [])) assert check(unrad(sqrt(x) + sqrt(x + 1)), (S(1), [], [])) eq = sqrt(x) + sqrt(x + 1) + sqrt(1 - sqrt(x)) assert check(unrad(eq), (16*x**3 - 9*x**2, [], [])) assert set(solve(eq, check=False)) == set([S(0), S(9)/16]) assert solve(eq) == [] # but this one really does have those solutions assert set(solve(sqrt(x) - sqrt(x + 1) + sqrt(1 - sqrt(x)))) == \ set([S.Zero, S(9)/16]) '''NOTE real_root changes the value of the result if the solution is simplified; `a` in the text below is the root that is not 4/5: >>> eq sqrt(x) + sqrt(-x + 1) + sqrt(x + 1) - 6*sqrt(5)/5 >>> eq.subs(x, a).n() -0.e-123 + 0.e-127*I >>> real_root(eq.subs(x, a)).n() -0.e-123 + 0.e-127*I >>> (eq.subs(x,simplify(a))).n() -0.e-126 >>> real_root(eq.subs(x, simplify(a))).n() 0.194825975605452 + 2.15093623885838*I >>> sqrt(x).subs(x, real_root(a)).n() 0.809823827278194 - 0.e-25*I >>> sqrt(x).subs(x, (a)).n() 0.809823827278194 - 0.e-25*I >>> sqrt(x).subs(x, simplify(a)).n() 0.809823827278194 - 5.32999467690853e-25*I >>> sqrt(x).subs(x, real_root(simplify(a))).n() 0.49864610868139 + 1.44572604257047*I ''' eq = (sqrt(x) + sqrt(x + 1) + sqrt(1 - x) - 6*sqrt(5)/5) ra = S('''-1484/375 - 4*(-1/2 + sqrt(3)*I/2)*(-12459439/52734375 + 114*sqrt(12657)/78125)**(1/3) - 172564/(140625*(-1/2 + sqrt(3)*I/2)*(-12459439/52734375 + 114*sqrt(12657)/78125)**(1/3))''') rb = S(4)/5 ans = solve(sqrt(x) + sqrt(x + 1) + sqrt(1 - x) - 6*sqrt(5)/5) assert all(abs(eq.subs(x, i).n()) < 1e-10 for i in (ra, rb)) and \ len(ans) == 2 and \ set([i.n(chop=True) for i in ans]) == \ set([i.n(chop=True) for i in (ra, rb)]) raises(ValueError, lambda: unrad(-root(x,3)**2 + 2**pi*root(x,3) - x + 2**pi)) raises(ValueError, lambda: unrad(sqrt(x) + sqrt(x + 1) + sqrt(1 - sqrt(x)) + 3)) raises(ValueError, lambda: unrad(sqrt(x) + (x + 1)**Rational(1, 3) + 2*sqrt(y))) # same as last but consider only y assert check(unrad(sqrt(x) + (x + 1)**Rational(1, 3) + 2*sqrt(y), y), (4*y - (sqrt(x) + (x + 1)**(S(1)/3))**2, [], [])) assert check(unrad(sqrt(x/(1 - x)) + (x + 1)**Rational(1, 3)), (x**3/(-x + 1)**3 - (x + 1)**2, [], [(-x + 1)**3])) # same as last but consider only y; no y-containing denominators now assert s_check(unrad(sqrt(x/(1 - x)) + 2*sqrt(y), y), (x/(-x + 1) - 4*y, [], [])) assert check(unrad(sqrt(x)*sqrt(1 - x) + 2, x), (x*(-x + 1) - 4, [], [])) # http://tutorial.math.lamar.edu/ # Classes/Alg/SolveRadicalEqns.aspx#Solve_Rad_Ex2_a assert solve(Eq(x, sqrt(x + 6))) == [3] assert solve(Eq(x + sqrt(x - 4), 4)) == [4] assert solve(Eq(1, x + sqrt(2*x - 3))) == [] assert set(solve(Eq(sqrt(5*x + 6) - 2, x))) == set([-S(1), S(2)]) assert set(solve(Eq(sqrt(2*x - 1) - sqrt(x - 4), 2))) == set([S(5), S(13)]) assert solve(Eq(sqrt(x + 7) + 2, sqrt(3 - x))) == [-6] # http://www.purplemath.com/modules/solverad.htm assert solve((2*x - 5)**Rational(1, 3) - 3) == [16] assert solve((x**3 - 3*x**2)**Rational(1, 3) + 1 - x) == [] assert set(solve(x + 1 - (x**4 + 4*x**3 - x)**Rational(1, 4))) == \ set([-S(1)/2, -S(1)/3]) assert set(solve(sqrt(2*x**2 - 7) - (3 - x))) == set([-S(8), S(2)]) assert solve(sqrt(2*x + 9) - sqrt(x + 1) - sqrt(x + 4)) == [0] assert solve(sqrt(x + 4) + sqrt(2*x - 1) - 3*sqrt(x - 1)) == [5] assert solve(sqrt(x)*sqrt(x - 7) - 12) == [16] assert solve(sqrt(x - 3) + sqrt(x) - 3) == [4] assert solve(sqrt(9*x**2 + 4) - (3*x + 2)) == [0] assert solve(sqrt(x) - 2 - 5) == [49] assert solve(sqrt(x - 3) - sqrt(x) - 3) == [] assert solve(sqrt(x - 1) - x + 7) == [10] assert solve(sqrt(x - 2) - 5) == [27] assert solve(sqrt(17*x - sqrt(x**2 - 5)) - 7) == [3] assert solve(sqrt(x) - sqrt(x - 1) + sqrt(sqrt(x))) == [] # don't posify the expression in unrad and use _mexpand z = sqrt(2*x + 1)/sqrt(x) - sqrt(2 + 1/x) p = posify(z)[0] assert solve(p) == [] assert solve(z) == [] assert solve(z + 6*I) == [-S(1)/11] assert solve(p + 6*I) == [] eq = sqrt(2 + I) + 2*I assert unrad(eq - x, x, all=True) == (x**4 + 4*x**2 + 8*x + 37, [], []) ans = (81*x**8 - 2268*x**6 - 4536*x**5 + 22644*x**4 + 63216*x**3 - 31608*x**2 - 189648*x + 141358, [], []) r = sqrt(sqrt(2)/3 + 7) eq = sqrt(r) + r - x assert unrad(eq, all=1) r2 = sqrt(sqrt(2) + 21)/sqrt(3) assert r != r2 and r.equals(r2) assert unrad(eq - r + r2, all=True) == ans @slow def test_unrad_slow(): ans = solve(sqrt(x) + sqrt(x + 1) - sqrt(1 - x) - sqrt(2 + x)) assert len(ans) == 1 and NS(ans[0])[:4] == '0.73' # the fence optimization problem # http://code.google.com/p/sympy/issues/detail?id=1694#c159 F = Symbol('F') eq = F - (2*x + 2*y + sqrt(x**2 + y**2)) X = solve(eq, x, hint='minimal')[0] Y = solve((x*y).subs(x, X).diff(y), y, simplify=False, minimal=True) ans = 2*F/7 - sqrt(2)*F/14 assert any((a - ans).expand().is_zero for a in Y) eq = S(''' -x + (1/2 - sqrt(3)*I/2)*(3*x**3/2 - x*(3*x**2 - 34)/2 + sqrt((-3*x**3 + x*(3*x**2 - 34) + 90)**2/4 - 39304/27) - 45)**(1/3) + 34/(3*(1/2 - sqrt(3)*I/2)*(3*x**3/2 - x*(3*x**2 - 34)/2 + sqrt((-3*x**3 + x*(3*x**2 - 34) + 90)**2/4 - 39304/27) - 45)**(1/3))''') raises(NotImplementedError, lambda: solve(eq)) # not other code errors def test__invert(): assert _invert(x - 2) == (2, x) assert _invert(2) == (2, 0) assert _invert(exp(1/x) - 3, x) == (1/log(3), x) assert _invert(exp(1/x + a/x) - 3, x) == ((a + 1)/log(3), x) assert _invert(a, x) == (a, 0) def test_issue_1364(): assert solve(-a*x + 2*x*log(x), x) == [exp(a/2)] assert solve(a/x + exp(x/2), x) == [2*LambertW(-a/2)] assert solve(x**x) == [] assert solve(x**x - 2) == [exp(LambertW(log(2)))] assert solve(((x - 3)*(x - 2))**((x - 3)*(x - 4))) == [2] assert solve( (a/x + exp(x/2)).diff(x), x) == [4*LambertW(sqrt(2)*sqrt(a)/4)] def test_issue_2015(): a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r = symbols('a:r') # there is no 'a' in the equation set but this is how the # problem was originally posed syms = a, b, c, f, h, k, n eqs = [b + r/d - c/d, c*(1/d + 1/e + 1/g) - f/g - r/d, f*(1/g + 1/i + 1/j) - c/g - h/i, h*(1/i + 1/l + 1/m) - f/i - k/m, k*(1/m + 1/o + 1/p) - h/m - n/p, n*(1/p + 1/q) - k/p] assert len(solve(eqs, syms, manual=True, check=False, simplify=False)) == 1 def test_misc(): # make sure that the right variables is picked up in tsolve raises(NotImplementedError, lambda: solve((exp(x) + 1)**x)) def test_issue_2750(): I1, I2, I3, I4, I5, I6 = symbols('I1:7') dI1, dI4, dQ2, dQ4, Q2, Q4 = symbols('dI1,dI4,dQ2,dQ4,Q2,Q4') e = ( I1 - I2 - I3, I3 - I4 - I5, I4 + I5 - I6, -I1 + I2 + I6, -2*I1 - 2*I3 - 2*I5 - 3*I6 - dI1/2 + 12, -I4 + dQ4, -I2 + dQ2, 2*I3 + 2*I5 + 3*I6 - Q2, I4 - 2*I5 + 2*Q4 + dI4 ) ans = [{ dQ4: I3 - I5, dI1: -4*I2 - 8*I3 - 4*I5 - 6*I6 + 24, I4: I3 - I5, dQ2: I2, Q2: 2*I3 + 2*I5 + 3*I6, I1: I2 + I3, Q4: -I3/2 + 3*I5/2 - dI4/2}] assert solve(e, I1, I4, Q2, Q4, dI1, dI4, dQ2, dQ4, manual=True) == ans # the matrix solver (tested below) doesn't like this because it produces # a zero row in the matrix. Is this related to issue 1452? assert [ei.subs( ans[0]) for ei in e] == [0, 0, I3 - I6, -I3 + I6, 0, 0, 0, 0, 0] def test_2750_matrix(): '''Same as test_2750 but solved with the matrix solver.''' I1, I2, I3, I4, I5, I6 = symbols('I1:7') dI1, dI4, dQ2, dQ4, Q2, Q4 = symbols('dI1,dI4,dQ2,dQ4,Q2,Q4') e = ( I1 - I2 - I3, I3 - I4 - I5, I4 + I5 - I6, -I1 + I2 + I6, -2*I1 - 2*I3 - 2*I5 - 3*I6 - dI1/2 + 12, -I4 + dQ4, -I2 + dQ2, 2*I3 + 2*I5 + 3*I6 - Q2, I4 - 2*I5 + 2*Q4 + dI4 ) assert solve(e, I1, I4, Q2, Q4, dI1, dI4, dQ2, dQ4) == { dI4: -I3 + 3*I5 - 2*Q4, dI1: -4*I2 - 8*I3 - 4*I5 - 6*I6 + 24, dQ2: I2, I1: I2 + I3, Q2: 2*I3 + 2*I5 + 3*I6, dQ4: I3 - I5, I4: I3 - I5} def test_issue_2802(): f, g, h = map(Function, 'fgh') a = Symbol('a') D = Derivative(f(x), x) G = Derivative(g(a), a) assert solve(f(x) + f(x).diff(x), f(x)) == \ [-D] assert solve(f(x) - 3, f(x)) == \ [3] assert solve(f(x) - 3*f(x).diff(x), f(x)) == \ [3*D] assert solve([f(x) - 3*f(x).diff(x)], f(x)) == \ {f(x): 3*D} assert solve([f(x) - 3*f(x).diff(x), f(x)**2 - y + 4], f(x), y) == \ [{f(x): 3*D, y: 9*D**2 + 4}] assert solve(-f(a)**2*g(a)**2 + f(a)**2*h(a)**2 + g(a).diff(a), h(a), g(a), set=True) == \ ([g(a)], set([ (-sqrt(h(a)**2 + G/f(a)**2),), (sqrt(h(a)**2 + G/f(a)**2),)])) args = [f(x).diff(x, 2)*(f(x) + g(x)) - g(x)**2 + 2, f(x), g(x)] assert set(solve(*args)) == \ set([(-sqrt(2), sqrt(2)), (sqrt(2), -sqrt(2))]) eqs = [f(x)**2 + g(x) - 2*f(x).diff(x), g(x)**2 - 4] assert solve(eqs, f(x), g(x), set=True) == \ ([f(x), g(x)], set([ (-sqrt(2*D - 2), S(2)), (sqrt(2*D - 2), S(2)), (-sqrt(2*D + 2), -S(2)), (sqrt(2*D + 2), -S(2))])) # the underlying problem was in solve_linear that was not masking off # anything but a Mul or Add; it now raises an error if it gets anything # but a symbol and solve handles the substitutions necessary so solve_linear # won't make this error raises( ValueError, lambda: solve_linear(f(x) + f(x).diff(x), symbols=[f(x)])) assert solve_linear(f(x) + f(x).diff(x), symbols=[x]) == \ (f(x) + Derivative(f(x), x), 1) assert solve_linear(f(x) + Integral(x, (x, y)), symbols=[x]) == \ (f(x) + Integral(x, (x, y)), 1) assert solve_linear(f(x) + Integral(x, (x, y)) + x, symbols=[x]) == \ (x + f(x) + Integral(x, (x, y)), 1) assert solve_linear(f(y) + Integral(x, (x, y)) + x, symbols=[x]) == \ (x, -f(y) - Integral(x, (x, y))) assert solve_linear(x - f(x)/a + (f(x) - 1)/a, symbols=[x]) == \ (x, 1/a) assert solve_linear(x + Derivative(2*x, x)) == \ (x, -2) assert solve_linear(x + Integral(x, y), symbols=[x]) == \ (x, 0) assert solve_linear(x + Integral(x, y) - 2, symbols=[x]) == \ (x, 2/(y + 1)) assert set(solve(x + exp(x)**2, exp(x))) == \ set([-sqrt(-x), sqrt(-x)]) assert solve(x + exp(x), x, implicit=True) == \ [-exp(x)] assert solve(cos(x) - sin(x), x, implicit=True) == [] assert solve(x - sin(x), x, implicit=True) == \ [sin(x)] assert solve(x**2 + x - 3, x, implicit=True) == \ [-x**2 + 3] assert solve(x**2 + x - 3, x**2, implicit=True) == \ [-x + 3] def test_issue_2813(): assert set(solve(x**2 - x - 0.1, rational=True)) == \ set([S(1)/2 + sqrt(35)/10, -sqrt(35)/10 + S(1)/2]) # [-0.0916079783099616, 1.09160797830996] ans = solve(x**2 - x - 0.1, rational=False) assert len(ans) == 2 and all(a.is_Number for a in ans) ans = solve(x**2 - x - 0.1) assert len(ans) == 2 and all(a.is_Number for a in ans) def test_float_handling(): def test(e1, e2): return len(e1.atoms(Float)) == len(e2.atoms(Float)) assert solve(x - 0.5, rational=True)[0].is_Rational assert solve(x - 0.5, rational=False)[0].is_Float assert solve(x - S.Half, rational=False)[0].is_Rational assert solve(x - 0.5, rational=None)[0].is_Float assert solve(x - S.Half, rational=None)[0].is_Rational assert test(nfloat(1 + 2*x), 1.0 + 2.0*x) for contain in [list, tuple, set]: ans = nfloat(contain([1 + 2*x])) assert type(ans) is contain and test(list(ans)[0], 1.0 + 2.0*x) k, v = list(nfloat({2*x: [1 + 2*x]}).items())[0] assert test(k, 2*x) and test(v[0], 1.0 + 2.0*x) assert test(nfloat(cos(2*x)), cos(2.0*x)) assert test(nfloat(3*x**2), 3.0*x**2) assert test(nfloat(3*x**2, exponent=True), 3.0*x**2.0) assert test(nfloat(exp(2*x)), exp(2.0*x)) assert test(nfloat(x/3), x/3.0) assert test(nfloat(x**4 + 2*x + cos(S(1)/3) + 1), x**4 + 2.0*x + 1.94495694631474) # don't call nfloat if there is no solution tot = 100 + c + z + t assert solve(((.7 + c)/tot - .6, (.2 + z)/tot - .3, t/tot - .1)) == [] def test_check_assumptions(): x = symbols('x', positive=True) assert solve(x**2 - 1) == [1] def test_solve_abs(): assert set(solve(abs(x - 7) - 8)) == set([-S(1), S(15)]) r = symbols('r', real=True) raises(NotImplementedError, lambda: solve(2*abs(r) - abs(r - 1))) def test_issue_2957(): assert solve(tanh(x + 3)*tanh(x - 3) - 1) == [] assert set([simplify(w) for w in solve(tanh(x - 1)*tanh(x + 1) + 1)]) == set([ -log(2)/2 + log(1 - I), -log(2)/2 + log(-1 - I), -log(2)/2 + log(1 + I), -log(2)/2 + log(-1 + I),]) assert set([simplify(w) for w in solve((tanh(x + 3)*tanh(x - 3) + 1)**2)]) == set([ -log(2)/2 + log(1 - I), -log(2)/2 + log(-1 - I), -log(2)/2 + log(1 + I), -log(2)/2 + log(-1 + I),]) def test_issue_2961(): x = Symbol('x') absxm3 = Piecewise( (x - 3, S(0) <= x - 3), (3 - x, S(0) > x - 3) ) y = Symbol('y') assert solve(absxm3 - y, x) == [ Piecewise((-y + 3, S(0) > -y), (S.NaN, True)), Piecewise((y + 3, S(0) <= y), (S.NaN, True)) ] y = Symbol('y', positive=True) assert solve(absxm3 - y, x) == [-y + 3, y + 3] def test_issue_2574(): eq = -x + exp(exp(LambertW(log(x)))*LambertW(log(x))) assert checksol(eq, x, 2) is True assert checksol(eq, x, 2, numerical=False) is None def test_exclude(): R, C, Ri, Vout, V1, Vminus, Vplus, s = \ symbols('R, C, Ri, Vout, V1, Vminus, Vplus, s') Rf = symbols('Rf', positive=True) # to eliminate Rf = 0 soln eqs = [C*V1*s + Vplus*(-2*C*s - 1/R), Vminus*(-1/Ri - 1/Rf) + Vout/Rf, C*Vplus*s + V1*(-C*s - 1/R) + Vout/R, -Vminus + Vplus] assert solve(eqs, exclude=s*C*R) == [ { Rf: Ri*(C*R*s + 1)**2/(C*R*s), Vminus: Vplus, V1: Vplus*(2*C*R*s + 1)/(C*R*s), Vout: Vplus*(C**2*R**2*s**2 + 3*C*R*s + 1)/(C*R*s)}, { Vplus: 0, Vminus: 0, V1: 0, Vout: 0}, ] # TODO: Investingate why currently solution [0] is preferred over [1]. assert solve(eqs, exclude=[Vplus, s, C]) in [[{ Vminus: Vplus, V1: Vout/2 + Vplus/2 + sqrt((Vout - 5*Vplus)*(Vout - Vplus))/2, R: (Vout - 3*Vplus - sqrt(Vout**2 - 6*Vout*Vplus + 5*Vplus**2))/(2*C*Vplus*s), Rf: Ri*(Vout - Vplus)/Vplus, }, { Vminus: Vplus, V1: Vout/2 + Vplus/2 - sqrt((Vout - 5*Vplus)*(Vout - Vplus))/2, R: (Vout - 3*Vplus + sqrt(Vout**2 - 6*Vout*Vplus + 5*Vplus**2))/(2*C*Vplus*s), Rf: Ri*(Vout - Vplus)/Vplus, }], [{ Vminus: Vplus, Vout: (V1**2 - V1*Vplus - Vplus**2)/(V1 - 2*Vplus), Rf: Ri*(V1 - Vplus)**2/(Vplus*(V1 - 2*Vplus)), R: Vplus/(C*s*(V1 - 2*Vplus)), }]] def test_high_order_roots(): s = x**5 + 4*x**3 + 3*x**2 + S(7)/4 assert set(solve(s)) == set(Poly(s*4, domain='ZZ').all_roots()) def test_minsolve_linear_system(): def count(dic): return len([x for x in dic.values() if x == 0]) assert count(solve([x + y + z, y + z + a + t], particular=True, quick=True)) \ == 3 assert count(solve([x + y + z, y + z + a + t], particular=True, quick=False)) \ == 3 assert count(solve([x + y + z, y + z + a], particular=True, quick=True)) == 1 assert count(solve([x + y + z, y + z + a], particular=True, quick=False)) == 2 def test_real_roots(): # cf. issue 3551 x = Symbol('x', real=True) assert len(solve(x**5 + x**3 + 1)) == 1 @slow def test_issue3429(): eqs = [ 327600995*x**2 - 37869137*x + 1809975124*y**2 - 9998905626, 895613949*x**2 - 273830224*x*y + 530506983*y**2 - 10000000000] assert len(solve(eqs, y, x)) == len(solve(eqs, y, x, manual=True)) == 4 def test_overdetermined(): eqs = [Abs(4*x - 7) - 5, Abs(3 - 8*x) - 1] assert solve(eqs, x) == [(S.Half,)] assert solve(eqs, x, manual=True) == [(S.Half,)] assert solve(eqs, x, manual=True, check=False) == [(S.Half/2,), (S.Half,)] def test_issue_3506(): x = symbols('x') assert solve(4**(x/2) - 2**(x/3)) == [0] # while the first one passed, this one failed x = symbols('x', real=True) assert solve(5**(x/2) - 2**(x/3)) == [0] b = sqrt(6)*sqrt(log(2))/sqrt(log(5)) assert solve(5**(x/2) - 2**(3/x)) == [-b, b] def test__ispow(): assert _ispow(x**2) assert not _ispow(x) assert not _ispow(True) def test_issue_3545(): eq = -sqrt((m - q)**2 + (-m/(2*q) + S(1)/2)**2) + sqrt((-m**2/2 - sqrt( 4*m**4 - 4*m**2 + 8*m + 1)/4 - S(1)/4)**2 + (m**2/2 - m - sqrt( 4*m**4 - 4*m**2 + 8*m + 1)/4 - S(1)/4)**2) assert solve(eq, q) == [ m**2/2 - sqrt(4*m**4 - 4*m**2 + 8*m + 1)/4 - S(1)/4, m**2/2 + sqrt(4*m**4 - 4*m**2 + 8*m + 1)/4 - S(1)/4] def test_issue_3653(): assert solve([a**2 + a, a - b], [a, b]) == [(-1, -1), (0, 0)] assert solve([a**2 + a*c, a - b], [a, b]) == [(0, 0), (-c, -c)] def test_issue_3693(): assert solve(x*(x - 1)**2*(x + 1)*(x**6 - x + 1)) == [ -1, 0, 1, RootOf(x**6 - x + 1, 0), RootOf(x**6 - x + 1, 1), RootOf(x**6 - x + 1, 2), RootOf(x**6 - x + 1, 3), RootOf(x**6 - x + 1, 4), RootOf(x**6 - x + 1, 5)] def test_issues_3720_3721_3722_3149(): # 3722 x, y = symbols('x y') assert solve(abs(x + 3) - 2*abs(x - 3)) == [1, 9] assert solve([abs(x) - 2, arg(x) - pi], x) == [ {re(x): -2, x: -2, im(x): 0}, {re(x): 2, x: 2, im(x): 0}] assert solve([re(x) - 1, im(x) - 2], x) == [ {re(x): 1, x: 1 + 2*I, im(x): 2}] w = symbols('w', integer=True) assert solve(2*x**w - 4*y**w, w) == solve((x/y)**w - 2, w) x, y = symbols('x y', real=True) assert solve(x + y*I + 3) == {y: 0, x: -3} x, y = symbols('x y', imaginary=True) assert solve(x + y*I + 3 + 2*I) == {x: -2*I, y: 3*I} x = symbols('x', real=True) assert solve(x + y + 3 + 2*I) == {x: -3, y: -2*I} # issue 3149 f = Function('f') assert solve(f(x + 1) - f(2*x - 1)) == [2] assert solve(log(x + 1) - log(2*x - 1)) == [2] x = symbols('x') assert solve(2**x + 4**x) == [I*pi/log(2)] def test_issue_3890(): f = Function('f') assert solve(Eq(-f(x), Piecewise((1, x > 0), (0, True))), f(x)) == \ [Piecewise((-1, x > 0), (0, True))] def test_lambert_multivariate(): from sympy.abc import a, x, y from sympy.solvers.bivariate import _filtered_gens, _lambert, _solve_lambert assert _filtered_gens(Poly(x + 1/x + exp(x) + y), x) == set([x, exp(x)]) assert _lambert(x, x) == [] assert solve((x**2 - 2*x + 1).subs(x, log(x) + 3*x)) == [LambertW(3*S.Exp1)/3] assert solve((x**2 - 2*x + 1).subs(x, (log(x) + 3*x)**2 - 1)) == \ [LambertW(3*exp(-sqrt(2)))/3, LambertW(3*exp(sqrt(2)))/3] assert solve((x**2 - 2*x - 2).subs(x, log(x) + 3*x)) == \ [LambertW(3*exp(1 + sqrt(3)))/3, LambertW(3*exp(-sqrt(3) + 1))/3] assert solve(x*log(x) + 3*x + 1, x) == [exp(-3 + LambertW(-exp(3)))] eq = (x*exp(x) - 3).subs(x, x*exp(x)) assert solve(eq) == [LambertW(3*exp(-LambertW(3)))] # coverage test raises(NotImplementedError, lambda: solve(x - sin(x)*log(y - x), x)) # if sign is unknown then only this one solution is obtained assert solve(3*log(a**(3*x + 5)) + a**(3*x + 5), x) == [ -((log(a**5) + LambertW(S(1)/3))/(3*log(a)))] # tested numerically p = symbols('p', positive=True) assert solve(3*log(p**(3*x + 5)) + p**(3*x + 5), x) == [ log((-3**(S(1)/3) - 3**(S(5)/6)*I)*LambertW(S(1)/3)**(S(1)/3)/(2*p**(S(5)/3)))/log(p), log((-3**(S(1)/3) + 3**(S(5)/6)*I)*LambertW(S(1)/3)**(S(1)/3)/(2*p**(S(5)/3)))/log(p), log((3*LambertW(S(1)/3)/p**5)**(1/(3*log(p)))),] # checked numerically # check collection assert solve(3*log(a**(3*x + 5)) + b*log(a**(3*x + 5)) + a**(3*x + 5), x) == [ -((log(a**5) + LambertW(1/(b + 3)))/(3*log(a)))] eq = 4*2**(2*p + 3) - 2*p - 3 assert _solve_lambert(eq, p, _filtered_gens(Poly(eq), p)) == [ -S(3)/2 - LambertW(-4*log(2))/(2*log(2))] # issue 1172 assert solve((a/x + exp(x/2)).diff(x, 2), x) == [ 6*LambertW((-1)**(S(1)/3)*a**(S(1)/3)/3)] assert solve((log(x) + x).subs(x, x**2 + 1)) == [ -I*sqrt(-LambertW(1) + 1), sqrt(-1 + LambertW(1))] # these only give one of the solutions (see XFAIL below) assert solve(x**3 - 3**x, x) == [-3/log(3)*LambertW(-log(3)/3)] # replacing 3 with 2 in the above solution gives 2 assert solve(x**2 - 2**x, x) == [2] assert solve(-x**2 + 2**x, x) == [2] assert solve(3**cos(x) - cos(x)**3) == [ acos(-3*LambertW(-log(3)/3)/log(3))] @XFAIL def test_other_lambert(): from sympy.abc import x assert solve(3*sin(x) - x*sin(3), x) == [3] assert set(solve(3*log(x) - x*log(3))) == set( [3, -3*LambertW(-log(3)/3)/log(3)]) a = S(6)/5 assert set(solve(x**a - a**x)) == set( [a, -a*LambertW(-log(a)/a)/log(a)]) assert set(solve(3**cos(x) - cos(x)**3)) == set( [acos(3), acos(-3*LambertW(-log(3)/3)/log(3))]) assert set(solve(x**2 - 2**x)) == set( [2, -2/log(2)*LambertW(log(2)/2)]) def test_rewrite_trig(): assert solve(sin(x) + tan(x)) == [0, 2*pi] assert solve(sin(x) + sec(x)) == [ -2*atan(-S.Half + sqrt(2 - 2*sqrt(3)*I)/2 + sqrt(3)*I/2), 2*atan(S.Half - sqrt(3)*I/2 + sqrt(2 - 2*sqrt(3)*I)/2), 2*atan(S.Half - sqrt(2 + 2*sqrt(3)*I)/2 + sqrt(3)*I/2), 2*atan(S.Half + sqrt(2 + 2*sqrt(3)*I)/2 + sqrt(3)*I/2)] assert solve(sinh(x) + tanh(x)) == [0, I*pi] @XFAIL def test_rewrite_trigh(): # if this import passes then the test below should also pass from sympy import sech assert solve(sinh(x) + sech(x)) == [ 2*atanh(-S.Half + sqrt(5)/2 - sqrt(-2*sqrt(5) + 2)/2), 2*atanh(-S.Half + sqrt(5)/2 + sqrt(-2*sqrt(5) + 2)/2), 2*atanh(-sqrt(5)/2 - S.Half + sqrt(2 + 2*sqrt(5))/2), 2*atanh(-sqrt(2 + 2*sqrt(5))/2 - sqrt(5)/2 - S.Half)] def test_uselogcombine(): eq = z - log(x) + log(y/(x*(-1 + y**2/x**2))) assert solve(eq, x, force=True) == [-sqrt(y*(y - exp(z))), sqrt(y*(y - exp(z)))] assert solve(log(x + 3) + log(1 + 3/x) - 3) == [ -3 + sqrt(-12 + exp(3))*exp(S(3)/2)/2 + exp(3)/2, -sqrt(-12 + exp(3))*exp(S(3)/2)/2 - 3 + exp(3)/2] def test_atan2(): assert solve(atan2(x, 2) - pi/3, x) == [2*sqrt(3)] def test_misc(): # shouldn't generate a GeneratorsNeeded error in _tsolve when the NaN is generated # for eq_down. Actual answers, as determined numerically are approx. +/- 0.83 assert solve(sinh(x)*sinh(sinh(x)) + cosh(x)*cosh(sinh(x)) - 3) is not None # watch out for recursive loop in tsolve raises(NotImplementedError, lambda: solve((x+2)**y*x-3,x)) sympy-0.7.4.1/sympy/solvers/tests/test_recurr.py0000644000175000017500000001551112253362407022202 0ustar georgeskgeorgeskfrom sympy import Eq, factorial, Function, Lambda, rf, S, sqrt, symbols, I, expand_func, binomial, gamma from sympy.solvers.recurr import rsolve, rsolve_hyper, rsolve_poly, rsolve_ratio from sympy.utilities.pytest import raises from sympy.abc import a, b, c y = Function('y') n, k = symbols('n,k', integer=True) C0, C1, C2 = symbols('C0,C1,C2') def test_rsolve_poly(): assert rsolve_poly([-1, -1, 1], 0, n) == 0 assert rsolve_poly([-1, -1, 1], 1, n) == -1 assert rsolve_poly([-1, n + 1], n, n) == 1 assert rsolve_poly([-1, 1], n, n) == C0 + (n**2 - n)/2 assert rsolve_poly([-n - 1, n], 1, n) == C1*n - 1 assert rsolve_poly([-4*n - 2, 1], 4*n + 1, n) == -1 assert rsolve_poly([-1, 1], n**5 + n**3, n) == \ C0 - n**3 / 2 - n**5 / 2 + n**2 / 6 + n**6 / 6 + 2*n**4 / 3 def test_rsolve_ratio(): solution = rsolve_ratio([-2*n**3 + n**2 + 2*n - 1, 2*n**3 + n**2 - 6*n, -2*n**3 - 11*n**2 - 18*n - 9, 2*n**3 + 13*n**2 + 22*n + 8], 0, n) assert solution in [ C1*((-2*n + 3)/(n**2 - 1))/3, (S(1)/2)*(C1*(-3 + 2*n)/(-1 + n**2)), (S(1)/2)*(C1*( 3 - 2*n)/( 1 - n**2)), (S(1)/2)*(C2*(-3 + 2*n)/(-1 + n**2)), (S(1)/2)*(C2*( 3 - 2*n)/( 1 - n**2)), ] def test_rsolve_hyper(): assert rsolve_hyper([-1, -1, 1], 0, n) in [ C0*(S.Half - S.Half*sqrt(5))**n + C1*(S.Half + S.Half*sqrt(5))**n, C1*(S.Half - S.Half*sqrt(5))**n + C0*(S.Half + S.Half*sqrt(5))**n, ] assert rsolve_hyper([n**2 - 2, -2*n - 1, 1], 0, n) in [ C0*rf(sqrt(2), n) + C1*rf(-sqrt(2), n), C1*rf(sqrt(2), n) + C0*rf(-sqrt(2), n), ] assert rsolve_hyper([n**2 - k, -2*n - 1, 1], 0, n) in [ C0*rf(sqrt(k), n) + C1*rf(-sqrt(k), n), C1*rf(sqrt(k), n) + C0*rf(-sqrt(k), n), ] assert rsolve_hyper( [2*n*(n + 1), -n**2 - 3*n + 2, n - 1], 0, n) == C1*factorial(n) + C0*2**n assert rsolve_hyper( [n + 2, -(2*n + 3)*(17*n**2 + 51*n + 39), n + 1], 0, n) == None assert rsolve_hyper([-n - 1, -1, 1], 0, n) == None assert rsolve_hyper([-1, 1], n, n).expand() == C0 + n**2/2 - n/2 assert rsolve_hyper([-1, 1], 1 + n, n).expand() == C0 + n**2/2 + n/2 assert rsolve_hyper([-1, 1], 3*(n + n**2), n).expand() == C0 + n**3 - n assert rsolve_hyper([-a, 1],0,n).expand() == C0*a**n assert rsolve_hyper([-a, 0, 1], 0, n).expand() == (-1)**n*C1*a**(n/2) + C0*a**(n/2) assert rsolve_hyper([1, 1, 1], 0, n).expand() == \ C0*(-S(1)/2 - sqrt(3)*I/2)**n + C1*(-S(1)/2 + sqrt(3)*I/2)**n assert rsolve_hyper([1, -2*n/a - 2/a, 1], 0, n) == None def recurrence_term(c, f): """Compute RHS of recurrence in f(n) with coefficients in c.""" return sum(c[i]*f.subs(n, n + i) for i in range(len(c))) def test_rsolve_bulk(): """Some bulk-generated tests.""" funcs = [ n, n + 1, n**2, n**3, n**4, n + n**2, 27*n + 52*n**2 - 3* n**3 + 12*n**4 - 52*n**5 ] coeffs = [ [-2, 1], [-2, -1, 1], [-1, 1, 1, -1, 1], [-n, 1], [n**2 - n + 12, 1] ] for p in funcs: # compute difference for c in coeffs: q = recurrence_term(c, p) if p.is_polynomial(n): assert rsolve_poly(c, q, n) == p #if p.is_hypergeometric(n): # assert rsolve_hyper(c, q, n) == p def test_rsolve(): f = y(n + 2) - y(n + 1) - y(n) h = sqrt(5)*(S.Half + S.Half*sqrt(5))**n \ - sqrt(5)*(S.Half - S.Half*sqrt(5))**n assert rsolve(f, y(n)) in [ C0*(S.Half - S.Half*sqrt(5))**n + C1*(S.Half + S.Half*sqrt(5))**n, C1*(S.Half - S.Half*sqrt(5))**n + C0*(S.Half + S.Half*sqrt(5))**n, ] assert rsolve(f, y(n), [0, 5]) == h assert rsolve(f, y(n), {0: 0, 1: 5}) == h assert rsolve(f, y(n), {y(0): 0, y(1): 5}) == h assert rsolve(y(n) - y(n - 1) - y(n - 2), y(n), [0, 5]) == h assert rsolve(Eq(y(n), y(n - 1) + y(n - 2)), y(n), [0, 5]) == h assert f.subs(y, Lambda(k, rsolve(f, y(n)).subs(n, k))).simplify() == 0 f = (n - 1)*y(n + 2) - (n**2 + 3*n - 2)*y(n + 1) + 2*n*(n + 1)*y(n) g = C1*factorial(n) + C0*2**n h = -3*factorial(n) + 3*2**n assert rsolve(f, y(n)) == g assert rsolve(f, y(n), []) == g assert rsolve(f, y(n), {}) == g assert rsolve(f, y(n), [0, 3]) == h assert rsolve(f, y(n), {0: 0, 1: 3}) == h assert rsolve(f, y(n), {y(0): 0, y(1): 3}) == h assert f.subs(y, Lambda(k, rsolve(f, y(n)).subs(n, k))).simplify() == 0 f = y(n) - y(n - 1) - 2 assert rsolve(f, y(n), {y(0): 0}) == 2*n assert rsolve(f, y(n), {y(0): 1}) == 2*n + 1 assert rsolve(f, y(n), {y(0): 0, y(1): 1}) is None assert f.subs(y, Lambda(k, rsolve(f, y(n)).subs(n, k))).simplify() == 0 f = 3*y(n - 1) - y(n) - 1 assert rsolve(f, y(n), {y(0): 0}) == -3**n/2 + S.Half assert rsolve(f, y(n), {y(0): 1}) == 3**n/2 + S.Half assert rsolve(f, y(n), {y(0): 2}) == 3*3**n/2 + S.Half assert f.subs(y, Lambda(k, rsolve(f, y(n)).subs(n, k))).simplify() == 0 f = y(n) - 1/n*y(n - 1) assert rsolve(f, y(n)) == C0/factorial(n) assert f.subs(y, Lambda(k, rsolve(f, y(n)).subs(n, k))).simplify() == 0 f = y(n) - 1/n*y(n - 1) - 1 assert rsolve(f, y(n)) is None f = 2*y(n - 1) + (1 - n)*y(n)/n assert rsolve(f, y(n), {y(1): 1}) == 2**(n - 1)*n assert rsolve(f, y(n), {y(1): 2}) == 2**(n - 1)*n*2 assert rsolve(f, y(n), {y(1): 3}) == 2**(n - 1)*n*3 assert f.subs(y, Lambda(k, rsolve(f, y(n)).subs(n, k))).simplify() == 0 f = (n - 1)*(n - 2)*y(n + 2) - (n + 1)*(n + 2)*y(n) assert rsolve(f, y(n), {y(3): 6, y(4): 24}) == n*(n - 1)*(n - 2) assert rsolve( f, y(n), {y(3): 6, y(4): -24}) == -n*(n - 1)*(n - 2)*(-1)**(n) assert f.subs(y, Lambda(k, rsolve(f, y(n)).subs(n, k))).simplify() == 0 assert rsolve(Eq(y(n + 1), a*y(n)), y(n), {y(1): a}).simplify() == a**n assert rsolve(y(n) - a*y(n-2),y(n), \ {y(1): sqrt(a)*(a + b), y(2): a*(a - b)}).simplify() == \ a**(n/2)*(-(-1)**n*b + a) f = (-16*n**2 + 32*n - 12)*y(n - 1) + (4*n**2 - 12*n + 9)*y(n) assert expand_func(rsolve(f, y(n), \ {y(1): binomial(2*n + 1, 3)}).rewrite(gamma)).simplify() == \ 2**(2*n)*n*(2*n - 1)*(4*n**2 - 1)/12 assert (rsolve(y(n) + a*(y(n + 1) + y(n - 1))/2, y(n)) - (C0*((sqrt(-a**2 + 1) - 1)/a)**n + C1*((-sqrt(-a**2 + 1) - 1)/a)**n)).simplify() == 0 def test_rsolve_raises(): x = Function('x') raises(ValueError, lambda: rsolve(y(n) - y(k + 1), y(n))) raises(ValueError, lambda: rsolve(y(n) - y(n + 1), x(n))) raises(ValueError, lambda: rsolve(y(n) - x(n + 1), y(n))) raises(ValueError, lambda: rsolve(y(n) - sqrt(n)*y(n + 1), y(n))) raises(ValueError, lambda: rsolve(y(n) - y(n + 1), y(n), {x(0): 0})) def test_issue_3745(): f = y(n + 2) - y(n + 1) + y(n)/4 assert rsolve(f, y(n)) == 2**(-n)*(C0 + C1*n) assert rsolve(f, y(n), {y(0): 0, y(1): 1}) == 2*2**(-n)*n sympy-0.7.4.1/sympy/solvers/tests/test_numeric.py0000644000175000017500000000401312253362407022335 0ustar georgeskgeorgeskfrom sympy import Eq, Matrix, pi, sin, sqrt, Symbol, Integral, Piecewise, symbols from sympy.mpmath import mnorm, mpf from sympy.solvers import nsolve from sympy.utilities.lambdify import lambdify from sympy.utilities.pytest import raises, XFAIL def test_nsolve(): # onedimensional x = Symbol('x') assert nsolve(sin(x), 2) - pi.evalf() < 1e-15 assert nsolve(Eq(2*x, 2), x, -10) == nsolve(2*x - 2, -10) # Testing checks on number of inputs raises(TypeError, lambda: nsolve(Eq(2*x, 2))) raises(TypeError, lambda: nsolve(Eq(2*x, 2), x, 1, 2)) # Issue 1730 assert nsolve(x**2/(1 - x)/(1 - 2*x)**2 - 100, x, 0) # doesn't fail # multidimensional x1 = Symbol('x1') x2 = Symbol('x2') f1 = 3 * x1**2 - 2 * x2**2 - 1 f2 = x1**2 - 2 * x1 + x2**2 + 2 * x2 - 8 f = Matrix((f1, f2)).T F = lambdify((x1, x2), f.T, modules='mpmath') for x0 in [(-1, 1), (1, -2), (4, 4), (-4, -4)]: x = nsolve(f, (x1, x2), x0, tol=1.e-8) assert mnorm(F(*x), 1) <= 1.e-10 # The Chinese mathematician Zhu Shijie was the very first to solve this # nonlinear system 700 years ago (z was added to make it 3-dimensional) x = Symbol('x') y = Symbol('y') z = Symbol('z') f1 = -x + 2*y f2 = (x**2 + x*(y**2 - 2) - 4*y) / (x + 4) f3 = sqrt(x**2 + y**2)*z f = Matrix((f1, f2, f3)).T F = lambdify((x, y, z), f.T, modules='mpmath') def getroot(x0): root = nsolve(f, (x, y, z), x0) assert mnorm(F(*root), 1) <= 1.e-8 return root assert list(map(round, getroot((1, 1, 1)))) == [2.0, 1.0, 0.0] assert nsolve([Eq( f1), Eq(f2), Eq(f3)], [x, y, z], (1, 1, 1)) # just see that it works a = Symbol('a') assert nsolve(1/(0.001 + a)**3 - 6/(0.9 - a)**3, a, 0.3).ae( mpf('0.31883011387318591')) def test_issue_3309(): x = Symbol('x') assert nsolve(Piecewise((x, x < 1), (x**2, True)), x, 2) == 0.0 @XFAIL def test_issue_3309_fail(): x, y = symbols('x y') assert nsolve(Integral(x*y, (x, 0, 5)), y, 2) == 0.0 sympy-0.7.4.1/sympy/solvers/tests/test_polysys.py0000644000175000017500000000737212253362407022430 0ustar georgeskgeorgesk"""Tests for solvers of systems of polynomial equations. """ from sympy import flatten, I, Integer, Poly, QQ, Rational, S, sqrt, symbols from sympy.abc import x, y, z from sympy.polys import PolynomialError from sympy.solvers.polysys import solve_poly_system, solve_triangulated from sympy.utilities.pytest import raises def test_solve_poly_system(): assert solve_poly_system([x - 1], x) == [(S.One,)] assert solve_poly_system([y - x, y - x - 1], x, y) is None assert solve_poly_system([y - x**2, y + x**2], x, y) == [(S.Zero, S.Zero)] assert solve_poly_system([2*x - 3, 3*y/2 - 2*x, z - 5*y], x, y, z) == \ [(Rational(3, 2), Integer(2), Integer(10))] assert solve_poly_system([x*y - 2*y, 2*y**2 - x**2], x, y) == \ [(0, 0), (2, -sqrt(2)), (2, sqrt(2))] assert solve_poly_system([y - x**2, y + x**2 + 1], x, y) == \ [(-I*sqrt(S.Half), -S.Half), (I*sqrt(S.Half), -S.Half)] f_1 = x**2 + y + z - 1 f_2 = x + y**2 + z - 1 f_3 = x + y + z**2 - 1 a, b = sqrt(2) - 1, -sqrt(2) - 1 assert solve_poly_system([f_1, f_2, f_3], x, y, z) == \ [(0, 0, 1), (0, 1, 0), (1, 0, 0), (a, a, a), (b, b, b)] solution = [(1, -1), (1, 1)] assert solve_poly_system([Poly(x**2 - y**2), Poly(x - 1)]) == solution assert solve_poly_system([x**2 - y**2, x - 1], x, y) == solution assert solve_poly_system([x**2 - y**2, x - 1]) == solution assert solve_poly_system( [x + x*y - 3, y + x*y - 4], x, y) == [(-3, -2), (1, 2)] raises(NotImplementedError, lambda: solve_poly_system([x**3 - y**3], x, y)) raises(PolynomialError, lambda: solve_poly_system([1/x], x)) def test_solve_biquadratic(): x0, y0, x1, y1, r = symbols('x0 y0 x1 y1 r') f_1 = (x - 1)**2 + (y - 1)**2 - r**2 f_2 = (x - 2)**2 + (y - 2)**2 - r**2 assert solve_poly_system([f_1, f_2], x, y) == \ [(S(3)/2 - sqrt(-1 + 2*r**2)/2, S(3)/2 + sqrt(-1 + 2*r**2)/2), (S(3)/2 + sqrt(-1 + 2*r**2)/2, S(3)/2 - sqrt(-1 + 2*r**2)/2)] f_1 = (x - 1)**2 + (y - 2)**2 - r**2 f_2 = (x - 1)**2 + (y - 1)**2 - r**2 assert solve_poly_system([f_1, f_2], x, y) == \ [(1 - sqrt(((2*r - 1)*(2*r + 1)))/2, S(3)/2), (1 + sqrt(((2*r - 1)*(2*r + 1)))/2, S(3)/2)] query = lambda expr: expr.is_Pow and expr.exp is S.Half f_1 = (x - 1 )**2 + (y - 2)**2 - r**2 f_2 = (x - x1)**2 + (y - 1)**2 - r**2 result = solve_poly_system([f_1, f_2], x, y) assert len(result) == 2 and all(len(r) == 2 for r in result) assert all(r.count(query) == 1 for r in flatten(result)) f_1 = (x - x0)**2 + (y - y0)**2 - r**2 f_2 = (x - x1)**2 + (y - y1)**2 - r**2 result = solve_poly_system([f_1, f_2], x, y) assert len(result) == 2 and all(len(r) == 2 for r in result) assert all(len(r.find(query)) == 1 for r in flatten(result)) def test_solve_triangualted(): f_1 = x**2 + y + z - 1 f_2 = x + y**2 + z - 1 f_3 = x + y + z**2 - 1 a, b = sqrt(2) - 1, -sqrt(2) - 1 assert solve_triangulated([f_1, f_2, f_3], x, y, z) == \ [(0, 0, 1), (0, 1, 0), (1, 0, 0)] dom = QQ.algebraic_field(sqrt(2)) assert solve_triangulated([f_1, f_2, f_3], x, y, z, domain=dom) == \ [(0, 0, 1), (0, 1, 0), (1, 0, 0), (a, a, a), (b, b, b)] def test_solve_issue_3686(): roots = solve_poly_system([((x - 5)**2/250000 + (y - S(5)/10)**2/250000) - 1, x], x, y) assert roots == [(0, S(1)/2 + 15*sqrt(1111)), (0, S(1)/2 - 15*sqrt(1111))] roots = solve_poly_system([((x - 5)**2/250000 + (y - 5.0/10)**2/250000) - 1, x], x, y) # TODO: does this really have to be so complicated?! assert len(roots) == 2 assert roots[0][0] == 0 assert roots[0][1].epsilon_eq(-499.474999374969, 1e12) assert roots[1][0] == 0 assert roots[1][1].epsilon_eq(500.474999374969, 1e12) sympy-0.7.4.1/sympy/solvers/tests/test_diophantine.py0000644000175000017500000005262712253362407023213 0ustar georgeskgeorgeskfrom sympy.solvers.diophantine import (diop_solve, diop_DN, diop_bf_DN, length, transformation_to_DN, find_DN, equivalent, parametrize_ternary_quadratic, square_factor, pairwise_prime, diop_ternary_quadratic, diop_ternary_quadratic_normal, descent, ldescent, classify_diop, diophantine, transformation_to_normal, diop_general_pythagorean, sum_of_four_squares, sum_of_three_squares, prime_as_sum_of_two_squares, partition, power_representation) from sympy import symbols, Integer, Matrix, simplify, Subs, S, factorint, factor_list from sympy.utilities.pytest import XFAIL, slow from sympy.utilities import default_sort_key from sympy.simplify.simplify import _mexpand x, y, z, w, t, X, Y, Z = symbols("x, y, z, w, t, X, Y, Z", Integer=True) def test_linear(): assert diop_solve(2*x + 3*y - 5) == (3*t - 5, -2*t + 5) assert diop_solve(3*y + 2*x - 5) == (3*t - 5, -2*t + 5) assert diop_solve(2*x - 3*y - 5) == (-3*t - 5, -2*t - 5) assert diop_solve(-2*x - 3*y - 5) == (-3*t + 5, 2*t - 5) assert diop_solve(7*x + 5*y) == (5*t, -7*t) assert diop_solve(2*x + 4*y) == (2*t, -t) assert diop_solve(4*x + 6*y - 4) == (3*t - 2, -2*t + 2) assert diop_solve(4*x + 6*y - 3) == (None, None) assert diop_solve(4*x + 3*y -4*z + 5) == \ (3*t + 4*z - 5, -4*t - 4*z + 5, z) assert diop_solve(4*x + 2*y + 8*z - 5) == (None, None, None) assert diop_solve(5*x + 7*y - 2*z - 6) == \ (7*t + 6*z + 18, -5*t - 4*z - 12, z) assert diop_solve(3*x - 6*y + 12*z - 9) == \ (-2*t - 4*z + 3, -t, z) assert diop_solve(x + 3*y - 4*z + w - 6) == \ (t, -t - 3*y + 4*z + 6, y, z) def test_quadratic_simple_hyperbolic_case(): # Simple Hyperbolic case: A = C = 0 and B != 0 assert diop_solve(3*x*y + 34*x - 12*y + 1) == \ set([(-Integer(133), -Integer(11)), (Integer(5), -Integer(57))]) assert diop_solve(6*x*y + 2*x + 3*y + 1) == set([]) assert diop_solve(-13*x*y + 2*x - 4*y - 54) == set([(Integer(27), Integer(0))]) assert diop_solve(-27*x*y - 30*x - 12*y - 54) == set([(-Integer(14), -Integer(1))]) assert diop_solve(2*x*y + 5*x + 56*y + 7) == set([(-Integer(161), -Integer(3)),\ (-Integer(47),-Integer(6)), (-Integer(35), -Integer(12)), (-Integer(29), -Integer(69)),\ (-Integer(27), Integer(64)), (-Integer(21), Integer(7)),(-Integer(9), Integer(1)),\ (Integer(105), -Integer(2))]) assert diop_solve(6*x*y + 9*x + 2*y + 3) == set([]) assert diop_solve(x*y + x + y + 1) == set([(-Integer(1), t), (t, -Integer(1))]) assert diophantine(48*x*y) def test_quadratic_elliptical_case(): # Elliptical case: B**2 - 4AC < 0 # Two test cases highlighted require lot of memory due to quadratic_congruence() method. # This above method should be replaced by Pernici's square_mod() method when his PR gets merged. #assert diop_solve(42*x**2 + 8*x*y + 15*y**2 + 23*x + 17*y - 4915) == set([(-Integer(11), -Integer(1))]) assert diop_solve(4*x**2 + 3*y**2 + 5*x - 11*y + 12) == set([]) assert diop_solve(x**2 + y**2 + 2*x + 2*y + 2) == set([(-Integer(1), -Integer(1))]) #assert diop_solve(15*x**2 - 9*x*y + 14*y**2 - 23*x - 14*y - 4950) == set([(-Integer(15), Integer(6))]) assert diop_solve(10*x**2 + 12*x*y + 12*y**2 - 34) == \ set([(Integer(1), -Integer(2)), (-Integer(1), -Integer(1)),(Integer(1), Integer(1)), (-Integer(1), Integer(2))]) def test_quadratic_parabolic_case(): # Parabolic case: B**2 - 4AC = 0 assert diop_solve(8*x**2 - 24*x*y + 18*y**2 + 5*x + 7*y + 16) == \ set([(-174*t**2 + 17*t - 2, -116*t**2 + 21*t - 2), (-174*t**2 + 41*t - 4, -116*t**2 + 37*t - 4)]) assert diop_solve(8*x**2 - 24*x*y + 18*y**2 + 6*x + 12*y - 6) == \ set([(-63*t**2 + 12*t, -42*t**2 + 15*t -1), (-63*t**2 + 30*t - 3, -42*t**2 + 27*t - 4)]) assert diop_solve(8*x**2 + 24*x*y + 18*y**2 + 4*x + 6*y - 7) == set([]) assert diop_solve(x**2 + 2*x*y + y**2 + 2*x + 2*y + 1) == set([(t,-t - 1)]) assert diop_solve(x**2 - 2*x*y + y**2 + 2*x + 2*y + 1) == \ set([(-4*t**2, -4*t**2 + 4*t - 1),(-4*t**2 + 4*t -1, -4*t**2 + 8*t - 4)]) assert check_solutions(y**2 - 41*x + 40) def test_quadratic_perfect_square(): # B**2 - 4*A*C > 0 # B**2 - 4*A*C is a perfect square assert diop_solve(48*x*y) == set([(Integer(0), t), (t, Integer(0))]) assert diop_solve(4*x**2 - 5*x*y + y**2 + 2) == \ set([(-Integer(1), -Integer(3)),(-Integer(1), -Integer(2)),(Integer(1), Integer(2)),(Integer(1), Integer(3))]) assert diop_solve(-2*x**2 - 3*x*y + 2*y**2 -2*x - 17*y + 25) == set([(Integer(4), Integer(15))]) assert diop_solve(12*x**2 + 13*x*y + 3*y**2 - 2*x + 3*y - 12) == \ set([(-Integer(6), Integer(9)), (-Integer(2), Integer(5)), (Integer(4), -Integer(4)), (-Integer(6), Integer(16))]) assert diop_solve(8*x**2 + 10*x*y + 2*y**2 - 32*x - 13*y - 23) == \ set([(-Integer(44), Integer(47)), (Integer(22), -Integer(85))]) assert diop_solve(4*x**2 - 4*x*y - 3*y- 8*x - 3) == \ set([(-Integer(1), -Integer(9)), (-Integer(6), -Integer(9)), (Integer(0), -Integer(1)), (Integer(1), -Integer(1))]) assert diop_solve(- 4*x*y - 4*y**2 - 3*y- 5*x - 10) == \ set([(-Integer(2), Integer(0)), (-Integer(11), -Integer(1)), (-Integer(5), Integer(5))]) assert diop_solve(x**2 - y**2 - 2*x - 2*y) == set([(t, -t), (-t, -t - 2)]) assert diop_solve(x**2 - 9*y**2 - 2*x - 6*y) == set([(-3*t + 2, -t), (3*t, -t)]) assert diop_solve(4*x**2 - 9*y**2 - 4*x - 12*y - 3) == set([(-3*t - 3, -2*t - 3), (3*t + 1, -2*t - 1)]) def test_quadratic_non_perfect_square(): # B**2 - 4*A*C is not a perfect square # Used check_solutions() since the solutions are complex expressions involving # square roots and exponents assert check_solutions(x**2 - 2*x - 5*y**2) assert check_solutions(3*x**2 - 2*y**2 - 2*x - 2*y) assert check_solutions(x**2 - x*y - y**2 - 3*y) assert check_solutions(x**2 - 9*y**2 - 2*x - 6*y) @slow def test_quadratic_non_perfect_slow(): assert check_solutions(8*x**2 + 10*x*y - 2*y**2 - 32*x - 13*y - 23) assert check_solutions(5*x**2 - 13*x*y + y**2 - 4*x - 4*y - 15) assert check_solutions(-3*x**2 - 2*x*y + 7*y**2 - 5*x - 7) assert check_solutions(-4 - x + 4*x**2 - y - 3*x*y - 4*y**2) assert check_solutions(1 + 2*x + 2*x**2 + 2*y + x*y - 2*y**2) def test_DN(): # Most of the test cases were adapted from, # Solving the generalized Pell equation x**2 - D*y**2 = N, John P. Robertson, July 31, 2004. # http://www.jpr2718.org/pell.pdf # others are verified using Wolfram Alpha. # Covers cases where D <= 0 or D > 0 and D is a square or N = 0 # Solutions are straightforward in these cases. assert diop_DN(3, 0) == [(0, 0)] assert diop_DN(-17, -5) == [] assert diop_DN(-19, 23) == [(2, 1)] assert diop_DN(-13, 17) == [(2, 1)] assert diop_DN(-15, 13) == [] assert diop_DN(0, 5) == [] assert diop_DN(0, 9) == [(3, t)] assert diop_DN(9, 0) == [(3*t, t)] assert diop_DN(16, 24) == [] assert diop_DN(9, 180) == [(18, 4)] assert diop_DN(9, -180) == [(12, 6)] assert diop_DN(7, 0) == [(0, 0)] # When equation is x**2 + y**2 = N # Solutions are interchangeable assert diop_DN(-1, 5) == [(2, 1)] assert diop_DN(-1, 169) == [(12, 5), (0, 13)] # D > 0 and D is not a square # N = 1 assert diop_DN(13, 1) == [(649, 180)] assert diop_DN(980, 1) == [(51841, 1656)] assert diop_DN(981, 1) == [(158070671986249, 5046808151700)] assert diop_DN(986, 1) == [(49299, 1570)] assert diop_DN(991, 1) == [(379516400906811930638014896080, 12055735790331359447442538767)] assert diop_DN(17, 1) == [(33, 8)] assert diop_DN(19, 1) == [(170, 39)] # N = -1 assert diop_DN(13, -1) == [(18, 5)] assert diop_DN(991, -1) == [] assert diop_DN(41, -1) == [(32, 5)] assert diop_DN(290, -1) == [(17, 1)] assert diop_DN(21257, -1) == [(13913102721304, 95427381109)] assert diop_DN(32, -1) == [] # |N| > 1 # Some tests were created using calculator at # http://www.numbertheory.org/php/patz.html assert diop_DN(13, -4) == [(3, 1), (393, 109), (36, 10)] # Source I referred returned (3, 1), (393, 109) and (-3, 1) as fundamental solutions # So (-3, 1) and (393, 109) should be in the same equivalent class assert equivalent(-3, 1, 393, 109, 13, -4) == True assert diop_DN(13, 27) == [(220, 61), (40, 11), (768, 213), (12, 3)] assert set(diop_DN(157, 12)) == \ set([(Integer(13), Integer(1)), (Integer(10663), Integer(851)), (Integer(579160), Integer(46222)), \ (Integer(483790960),Integer(38610722)), (Integer(26277068347), Integer(2097138361)), (Integer(21950079635497), Integer(1751807067011))]) assert diop_DN(13, 25) == [(3245, 900)] assert diop_DN(192, 18) == [] assert diop_DN(23, 13) == [(-6, 1), (6, 1)] assert diop_DN(167, 2) == [(13, 1)] assert diop_DN(167, -2) == [] assert diop_DN(123, -2) == [(11, 1)] # One calculator returned [(11, 1), (-11, 1)] but both of these are in # the same equivalence class assert equivalent(11, 1, -11, 1, 123, -2) assert diop_DN(123, -23) == [(-10, 1), (10, 1)] def test_bf_pell(): assert diop_bf_DN(13, -4) == [(3, 1), (-3, 1), (36, 10)] assert diop_bf_DN(13, 27) == [(12, 3), (-12, 3), (40, 11), (-40, 11)] assert diop_bf_DN(167, -2) == [] assert diop_bf_DN(1729, 1) == [(44611924489705, 1072885712316)] assert diop_bf_DN(89, -8) == [(9, 1), (-9, 1)] assert diop_bf_DN(21257, -1) == [(13913102721304, 95427381109)] assert diop_bf_DN(340, -4) == [(756, 41)] def test_length(): assert length(-2, 4, 5) == 3 assert length(-5, 4, 17) == 4 assert length(0, 4, 13) == 6 assert length(-31, 8, 613) == 67 assert length(7, 13, 11) == 23 assert length(-40, 5, 23) == 4 def is_pell_transformation_ok(eq): """ Test whether X*Y, X, or Y terms are present in the equation after transforming the equation using the transformation returned by transformation_to_pell(). If they are not present we are good. Moreover, coefficient of X**2 should be a divisor of coefficient of Y**2 and the constant term. """ A, B = transformation_to_DN(eq) u = (A*Matrix([X, Y]) + B)[0] v = (A*Matrix([X, Y]) + B)[1] simplified = _mexpand(Subs(eq, (x, y), (u, v)).doit()) coeff = dict([reversed(t.as_independent(*[X, Y])) for t in simplified.args]) for term in [X*Y, X, Y]: if term in coeff.keys(): return False for term in [X**2, Y**2, Integer(1)]: if term not in coeff.keys(): coeff[term] = Integer(0) if coeff[X**2] != 0: return isinstance(S(coeff[Y**2])/coeff[X**2], Integer) and isinstance(S(coeff[Integer(1)])/coeff[X**2], Integer) return True def test_transformation_to_pell(): assert is_pell_transformation_ok(-13*x**2 - 7*x*y + y**2 + 2*x - 2*y - 14) assert is_pell_transformation_ok(-17*x**2 + 19*x*y - 7*y**2 - 5*x - 13*y - 23) assert is_pell_transformation_ok(x**2 - y**2 + 17) assert is_pell_transformation_ok(-x**2 + 7*y**2 - 23) assert is_pell_transformation_ok(25*x**2 - 45*x*y + 5*y**2 - 5*x - 10*y + 5) assert is_pell_transformation_ok(190*x**2 + 30*x*y + y**2 - 3*y - 170*x - 130) assert is_pell_transformation_ok(x**2 - 2*x*y -190*y**2 - 7*y - 23*x - 89) assert is_pell_transformation_ok(15*x**2 - 9*x*y + 14*y**2 - 23*x - 14*y - 4950) def test_find_DN(): assert find_DN(x**2 - 2*x - y**2) == (1, 1) assert find_DN(x**2 - 3*y**2 - 5) == (3, 5) assert find_DN(x**2 - 2*x*y - 4*y**2 - 7) == (5, 7) assert find_DN(4*x**2 - 8*x*y - y**2 - 9) == (20, 36) assert find_DN(7*x**2 - 2*x*y - y**2 - 12) == (8, 84) assert find_DN(-3*x**2 + 4*x*y -y**2) == (1, 0) assert find_DN(-13*x**2 - 7*x*y + y**2 + 2*x - 2*y -14) == (101, -7825480) def test_ldescent(): # Equations which have solutions u = ([(13, 23), (3, -11), (41, -113), (4, -7), (-7, 4), (91, -3), (1, 1), (1, -1), (4, 32), (17, 13), (123689, 1), (19, -570)]) for a, b in u: w, x, y = ldescent(a, b) assert a*x**2 + b*y**2 == w**2 def test_diop_ternary_quadratic_normal(): assert check_solutions(234*x**2 - 65601*y**2 - z**2) assert check_solutions(23*x**2 + 616*y**2 - z**2) assert check_solutions(5*x**2 + 4*y**2 - z**2) assert check_solutions(3*x**2 + 6*y**2 - 3*z**2) assert check_solutions(x**2 + 3*y**2 - z**2) assert check_solutions(4*x**2 + 5*y**2 - z**2) assert check_solutions(x**2 + y**2 - z**2) assert check_solutions(16*x**2 + y**2 - 25*z**2) assert check_solutions(6*x**2 - y**2 + 10*z**2) assert check_solutions(213*x**2 + 12*y**2 - 9*z**2) assert check_solutions(34*x**2 - 3*y**2 - 301*z**2) assert check_solutions(124*x**2 - 30*y**2 - 7729*z**2) def is_normal_transformation_ok(eq): A = transformation_to_normal(eq) X, Y, Z = A*Matrix([x, y, z]) simplified = _mexpand(Subs(eq, (x, y, z), (X, Y, Z)).doit()) coeff = dict([reversed(t.as_independent(*[X, Y, Z])) for t in simplified.args]) for term in [X*Y, Y*Z, X*Z]: if term in coeff.keys(): return False return True def test_transformation_to_normal(): assert is_normal_transformation_ok(x**2 + 3*y**2 + z**2 - 13*x*y - 16*y*z + 12*x*z) assert is_normal_transformation_ok(x**2 + 3*y**2 - 100*z**2) assert is_normal_transformation_ok(x**2 + 23*y*z) assert is_normal_transformation_ok(3*y**2 - 100*z**2 - 12*x*y) assert is_normal_transformation_ok(x**2 + 23*x*y - 34*y*z + 12*x*z) assert is_normal_transformation_ok(z**2 + 34*x*y - 23*y*z + x*z) assert is_normal_transformation_ok(x**2 + y**2 + z**2 - x*y - y*z - x*z) def test_diop_ternary_quadratic(): # Commented out test cases should be uncommented after # the bug with factor_list() gets merged. assert check_solutions(2*x**2 + z**2 + y**2 - 4*x*y) assert check_solutions(x**2 - y**2 - z**2 - x*y - y*z) assert check_solutions(3*x**2 - x*y - y*z - x*z) assert check_solutions(x**2 - y*z - x*z) #assert check_solutions(5*x**2 - 3*x*y - x*z) assert check_solutions(4*x**2 - 5*y**2 - x*z) assert check_solutions(3*x**2 + 2*y**2 - z**2 - 2*x*y + 5*y*z - 7*y*z) assert check_solutions(8*x**2 - 12*y*z) assert check_solutions(45*x**2 - 7*y**2 - 8*x*y - z**2) assert check_solutions(x**2 - 49*y**2 - z**2 + 13*z*y -8*x*y) assert check_solutions(90*x**2 + 3*y**2 + 5*x*y + 2*z*y + 5*x*z) assert check_solutions(x**2 + 3*y**2 + z**2 - x*y - 17*y*z) assert check_solutions(x**2 + 3*y**2 + z**2 - x*y - 16*y*z + 12*x*z) assert check_solutions(x**2 + 3*y**2 + z**2 - 13*x*y - 16*y*z + 12*x*z) assert check_solutions(x*y - 7*y*z + 13*x*z) def test_pairwise_prime(): assert pairwise_prime(6, 10, 15) == (5, 3, 2) assert pairwise_prime(2, 3, 5) == (2, 3, 5) assert pairwise_prime(1, 4, 7) == (1, 4, 7) assert pairwise_prime(4, 6, 5) == (1, 6, 5) assert pairwise_prime(6, 10, -15) == (5, 3, -2) assert pairwise_prime(-6, -10, -15) == (-5, -3, -2) assert pairwise_prime(4, -6, -5) == (1, -6, -5) def test_square_factor(): assert square_factor(1) == square_factor(-1) == 1 assert square_factor(0) == 1 assert square_factor(5) == square_factor(-5) == 1 assert square_factor(4) == square_factor(-4) == 2 assert square_factor(12) == square_factor(-12) == 2 assert square_factor(6) == 1 assert square_factor(18) == 3 assert square_factor(52) == 2 assert square_factor(49) == 7 assert square_factor(392) == 14 def test_parametrize_ternary_quadratic(): assert check_solutions(x**2 + y**2 - z**2) assert check_solutions(x**2 + 2*x*y + z**2) assert check_solutions(234*x**2 - 65601*y**2 - z**2) assert check_solutions(3*x**2 + 2*y**2 - z**2 - 2*x*y + 5*y*z - 7*y*z) assert check_solutions(x**2 - y**2 - z**2) assert check_solutions(x**2 - 49*y**2 - z**2 + 13*z*y - 8*x*y) assert check_solutions(8*x*y + z**2) assert check_solutions(124*x**2 - 30*y**2 - 7729*z**2) assert check_solutions(236*x**2 - 225*y**2 - 11*x*y - 13*y*z - 17*x*z) assert check_solutions(90*x**2 + 3*y**2 + 5*x*y + 2*z*y + 5*x*z) assert check_solutions(124*x**2 - 30*y**2 - 7729*z**2) def test_no_square_ternary_quadratic(): # Commented out test cases should be uncommented after # the bug with factor_list() gets merged. assert check_solutions(2*x*y + y*z - 3*x*z) assert check_solutions(189*x*y - 345*y*z - 12*x*z) #assert check_solutions(23*x*y + 34*y*z) assert check_solutions(x*y + y*z + z*x) assert check_solutions(23*x*y + 23*y*z + 23*x*z) def test_descent(): u = ([(13, 23), (3, -11), (41, -113), (91, -3), (1, 1), (1, -1), (17, 13), (123689, 1), (19, -570)]) for a, b in u: w, x, y = descent(a, b) assert a*x**2 + b*y**2 == w**2 def test_diophantine(): # Commented out test cases should be uncommented after # the bug with factor_list() gets merged. assert check_solutions((x - y)*(y - z)*(z - x)) assert check_solutions((x - y)*(x**2 + y**2 - z**2)) assert check_solutions((x - 3*y + 7*z)*(x**2 + y**2 - z**2)) assert check_solutions((x**2 - 3*y**2 - 1)) #assert check_solutions(y**2 + 7*x*y) #assert check_solutions(x**2 - 3*x*y + y**2) #assert check_solutions(z*(x**2 - y**2 - 15)) #assert check_solutions(x*(2*y - 2*z + 5)) assert check_solutions((x**2 - 3*y**2 - 1)*(x**2 - y**2 - 15)) assert check_solutions((x**2 - 3*y**2 - 1)*(y - 7*z)) assert check_solutions((x**2 + y**2 - z**2)*(x - 7*y - 3*z + 4*w)) # Following test case caused problems in parametric representation # But this can be solved by factroing out y. # No need to use methods for ternary quadratic equations. #assert check_solutions(y**2 - 7*x*y + 4*y*z) assert check_solutions(x**2 - 2*x + 1) def test_general_pythagorean(): from sympy.abc import a, b, c, d, e assert check_solutions(a**2 + b**2 + c**2 - d**2) assert check_solutions(a**2 + 4*b**2 + 4*c**2 - d**2) assert check_solutions(9*a**2 + 4*b**2 + 4*c**2 - d**2) assert check_solutions(9*a**2 + 4*b**2 - 25*d**2 + 4*c**2 ) assert check_solutions(9*a**2 - 16*d**2 + 4*b**2 + 4*c**2) assert check_solutions(-e**2 + 9*a**2 + 4*b**2 + 4*c**2 + 25*d**2) assert check_solutions(16*a**2 - b**2 + 9*c**2 + d**2 + 25*e**2) def test_diop_general_sum_of_squares(): from sympy.abc import a, b, c, d, e, f, g, h, i assert check_solutions(a**2 + b**2 + c**2 - 5) assert check_solutions(a**2 + b**2 + c**2 - 57) assert check_solutions(a**2 + b**2 + c**2 - 349560695) assert check_solutions(a**2 + b**2 + c**2 + d**2 - 304) assert check_solutions(a**2 + b**2 + c**2 + d**2 - 23345) assert check_solutions(a**2 + b**2 + c**2 + d**2 - 23345494) assert check_solutions(a**2 + b**2 + c**2 + d**2 + e**2 - 1344545) assert check_solutions(a**2 + b**2 + c**2 + d**2 + e**2 + f**2 - 6933949) assert check_solutions(a**2 + b**2 + c**2 + d**2 + e**2 + f**2 + g**2 - 753934) assert check_solutions(a**2 + b**2 + c**2 + d**2 + e**2 + f**2 + g**2 + h**2 - 5) assert check_solutions(a**2 + b**2 + c**2 + d**2 + e**2 + f**2 + g**2 + h**2 + i**2 - 693940) def test_partition(): tests = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] for test in tests: f = partition(test) while True: try: l = next(f) except StopIteration: break tests_k = [8, 10] for test in tests_k: for k in range(8): f = partition(test, k) while True: try: l = next(f) assert len(l) == k except StopIteration: break def test_prime_as_sum_of_two_squares(): for i in [5, 13, 17, 29, 37, 41, 2341, 3557, 34841, 64601]: a, b = prime_as_sum_of_two_squares(i) assert a**2 + b**2 == i def test_sum_of_three_squares(): for i in [0, 1, 2, 34, 123, 34304595905, 34304595905394941, 343045959052344, 800, 801, 802, 803, 804, 805, 806]: a, b, c = sum_of_three_squares(i) assert a**2 + b**2 + c**2 == i assert sum_of_three_squares(7) == (None, None, None) assert sum_of_three_squares((4**5)*15) == (None, None, None) def test_sum_of_four_squares(): from random import randint for i in range(10): n = randint(1, 100000000000000) a, b, c, d = sum_of_four_squares(n) assert a**2 + b**2 + c**2 + d**2 == n def test_power_representation(): tests = [(1729, 3, 2), (234, 2, 4), (2, 1, 2), (3, 1, 3), (5, 2, 2), (12352, 2, 4), (32760, 2, 3)] for test in tests: n, p, k = test f = power_representation(n, p, k) while True: try: l = next(f) assert len(l) == k chk_sum = 0 for l_i in l: chk_sum = chk_sum + l_i**p assert chk_sum == n except StopIteration: break def check_solutions(eq): """ Determines whether solutions returned by diophantine() satisfy the original equation. Hope to generalize this so we can remove functions like check_ternay_quadratic, check_solutions_normal, check_solutions() """ s = diophantine(eq) terms = factor_list(eq)[1] var = list(eq.free_symbols) var.sort(key=default_sort_key) okay = True while len(s) and okay: solution = s.pop() okay = False for term in terms: subeq = term[0] if simplify(_mexpand(Subs(subeq, var, solution).doit())) == 0: okay = True break return okay sympy-0.7.4.1/sympy/solvers/tests/test_inequalities.py0000644000175000017500000002725412253362407023403 0ustar georgeskgeorgesk"""Tests for tools for solving inequalities and systems of inequalities. """ from sympy import (And, Eq, FiniteSet, Ge, Gt, im, Interval, Le, Lt, Ne, oo, Or, Q, re, S, sin, sqrt, Union) from sympy.assumptions import assuming from sympy.abc import x, y from sympy.solvers.inequalities import (reduce_inequalities, reduce_rational_inequalities) from sympy.utilities.pytest import raises inf = oo.evalf() def test_reduce_poly_inequalities_real_interval(): with assuming(Q.real(x), Q.real(y)): assert reduce_rational_inequalities( [[Eq(x**2, 0)]], x, relational=False) == FiniteSet(0) assert reduce_rational_inequalities( [[Le(x**2, 0)]], x, relational=False) == FiniteSet(0) assert reduce_rational_inequalities( [[Lt(x**2, 0)]], x, relational=False) == S.EmptySet assert reduce_rational_inequalities( [[Ge(x**2, 0)]], x, relational=False) == Interval(-oo, oo) assert reduce_rational_inequalities( [[Gt(x**2, 0)]], x, relational=False) == FiniteSet(0).complement assert reduce_rational_inequalities( [[Ne(x**2, 0)]], x, relational=False) == FiniteSet(0).complement assert reduce_rational_inequalities( [[Eq(x**2, 1)]], x, relational=False) == FiniteSet(-1, 1) assert reduce_rational_inequalities( [[Le(x**2, 1)]], x, relational=False) == Interval(-1, 1) assert reduce_rational_inequalities( [[Lt(x**2, 1)]], x, relational=False) == Interval(-1, 1, True, True) assert reduce_rational_inequalities([[Ge(x**2, 1)]], x, relational=False) == Union(Interval(-oo, -1), Interval(1, oo)) assert reduce_rational_inequalities( [[Gt(x**2, 1)]], x, relational=False) == Interval(-1, 1).complement assert reduce_rational_inequalities( [[Ne(x**2, 1)]], x, relational=False) == FiniteSet(-1, 1).complement assert reduce_rational_inequalities([[Eq( x**2, 1.0)]], x, relational=False) == FiniteSet(-1.0, 1.0).evalf() assert reduce_rational_inequalities( [[Le(x**2, 1.0)]], x, relational=False) == Interval(-1.0, 1.0) assert reduce_rational_inequalities([[Lt( x**2, 1.0)]], x, relational=False) == Interval(-1.0, 1.0, True, True) assert reduce_rational_inequalities([[Ge(x**2, 1.0)]], x, relational=False) == Union(Interval(-inf, -1.0), Interval(1.0, inf)) assert reduce_rational_inequalities([[Gt(x**2, 1.0)]], x, relational=False) == Union(Interval(-inf, -1.0, right_open=True), Interval(1.0, inf, left_open=True)) assert reduce_rational_inequalities([[Ne( x**2, 1.0)]], x, relational=False) == FiniteSet(-1.0, 1.0).complement s = sqrt(2) assert reduce_rational_inequalities([[Lt( x**2 - 1, 0), Gt(x**2 - 1, 0)]], x, relational=False) == S.EmptySet assert reduce_rational_inequalities([[Le(x**2 - 1, 0), Ge( x**2 - 1, 0)]], x, relational=False) == FiniteSet(-1, 1) assert reduce_rational_inequalities([[Le(x**2 - 2, 0), Ge(x**2 - 1, 0)]], x, relational=False) == Union(Interval(-s, -1, False, False), Interval(1, s, False, False)) assert reduce_rational_inequalities([[Le(x**2 - 2, 0), Gt(x**2 - 1, 0)]], x, relational=False) == Union(Interval(-s, -1, False, True), Interval(1, s, True, False)) assert reduce_rational_inequalities([[Lt(x**2 - 2, 0), Ge(x**2 - 1, 0)]], x, relational=False) == Union(Interval(-s, -1, True, False), Interval(1, s, False, True)) assert reduce_rational_inequalities([[Lt(x**2 - 2, 0), Gt(x**2 - 1, 0)]], x, relational=False) == Union(Interval(-s, -1, True, True), Interval(1, s, True, True)) assert reduce_rational_inequalities([[Lt(x**2 - 2, 0), Ne(x**2 - 1, 0)]], x, relational=False) == Union(Interval(-s, -1, True, True), Interval(-1, 1, True, True), Interval(1, s, True, True)) def test_reduce_poly_inequalities_real_relational(): with assuming(Q.real(x), Q.real(y)): assert reduce_rational_inequalities( [[Eq(x**2, 0)]], x, relational=True) == Eq(x, 0) assert reduce_rational_inequalities( [[Le(x**2, 0)]], x, relational=True) == Eq(x, 0) assert reduce_rational_inequalities( [[Lt(x**2, 0)]], x, relational=True) is False assert reduce_rational_inequalities( [[Ge(x**2, 0)]], x, relational=True) is True assert reduce_rational_inequalities( [[Gt(x**2, 0)]], x, relational=True) == Or(Lt(x, 0), Gt(x, 0)) assert reduce_rational_inequalities( [[Ne(x**2, 0)]], x, relational=True) == Or(Lt(x, 0), Gt(x, 0)) assert reduce_rational_inequalities( [[Eq(x**2, 1)]], x, relational=True) == Or(Eq(x, -1), Eq(x, 1)) assert reduce_rational_inequalities( [[Le(x**2, 1)]], x, relational=True) == And(Le(-1, x), Le(x, 1)) assert reduce_rational_inequalities( [[Lt(x**2, 1)]], x, relational=True) == And(Lt(-1, x), Lt(x, 1)) assert reduce_rational_inequalities( [[Ge(x**2, 1)]], x, relational=True) == Or(Le(x, -1), Ge(x, 1)) assert reduce_rational_inequalities( [[Gt(x**2, 1)]], x, relational=True) == Or(Lt(x, -1), Gt(x, 1)) assert reduce_rational_inequalities([[Ne(x**2, 1)]], x, relational=True) == Or( Lt(x, -1), And(Lt(-1, x), Lt(x, 1)), Gt(x, 1)) assert reduce_rational_inequalities( [[Le(x**2, 1.0)]], x, relational=True) == And(Le(-1.0, x), Le(x, 1.0)) assert reduce_rational_inequalities( [[Lt(x**2, 1.0)]], x, relational=True) == And(Lt(-1.0, x), Lt(x, 1.0)) assert reduce_rational_inequalities( [[Ge(x**2, 1.0)]], x, relational=True) == Or(Le(x, -1.0), Ge(x, 1.0)) assert reduce_rational_inequalities( [[Gt(x**2, 1.0)]], x, relational=True) == Or(Lt(x, -1.0), Gt(x, 1.0)) assert reduce_rational_inequalities([[Ne(x**2, 1.0)]], x, relational=True) == \ Or(Lt(x, -1.0), And(Lt(-1.0, x), Lt(x, 1.0)), Gt(x, 1.0)) def test_reduce_poly_inequalities_complex_relational(): cond = Eq(im(x), 0) assert reduce_rational_inequalities( [[Eq(x**2, 0)]], x, relational=True) == And(Eq(re(x), 0), cond) assert reduce_rational_inequalities( [[Le(x**2, 0)]], x, relational=True) == And(Eq(re(x), 0), cond) assert reduce_rational_inequalities( [[Lt(x**2, 0)]], x, relational=True) == False assert reduce_rational_inequalities( [[Ge(x**2, 0)]], x, relational=True) == cond assert reduce_rational_inequalities([[Gt(x**2, 0)]], x, relational=True) == \ And(Or(Lt(re(x), 0), Gt(re(x), 0)), cond) assert reduce_rational_inequalities([[Ne(x**2, 0)]], x, relational=True) == \ And(Or(Lt(re(x), 0), Gt(re(x), 0)), cond) assert reduce_rational_inequalities([[Eq(x**2, 1)]], x, relational=True) == \ And(Or(Eq(re(x), -1), Eq(re(x), 1)), cond) assert reduce_rational_inequalities([[Le(x**2, 1)]], x, relational=True) == \ And(And(Le(-1, re(x)), Le(re(x), 1)), cond) assert reduce_rational_inequalities([[Lt(x**2, 1)]], x, relational=True) == \ And(And(Lt(-1, re(x)), Lt(re(x), 1)), cond) assert reduce_rational_inequalities([[Ge(x**2, 1)]], x, relational=True) == \ And(Or(Le(re(x), -1), Ge(re(x), 1)), cond) assert reduce_rational_inequalities([[Gt(x**2, 1)]], x, relational=True) == \ And(Or(Lt(re(x), -1), Gt(re(x), 1)), cond) assert reduce_rational_inequalities([[Ne(x**2, 1)]], x, relational=True) == \ And(Or(Lt(re(x), -1), And(Lt(-1, re(x)), Lt(re(x), 1)), Gt(re(x), 1)), cond) assert reduce_rational_inequalities([[Le(x**2, 1.0)]], x, relational=True) == \ And(And(Le(-1.0, re(x)), Le(re(x), 1.0)), cond) assert reduce_rational_inequalities([[Lt(x**2, 1.0)]], x, relational=True) == \ And(And(Lt(-1.0, re(x)), Lt(re(x), 1.0)), cond) assert reduce_rational_inequalities([[Ge(x**2, 1.0)]], x, relational=True) == \ And(Or(Le(re(x), -1.0), Ge(re(x), 1.0)), cond) assert reduce_rational_inequalities([[Gt(x**2, 1.0)]], x, relational=True) == \ And(Or(Lt(re(x), -1.0), Gt(re(x), 1.0)), cond) assert reduce_rational_inequalities([[Ne(x**2, 1.0)]], x, relational=True) == \ And(Or(Lt(re(x), -1.0), And(Lt(-1.0, re(x)), Lt(re(x), 1.0)), Gt(re(x), 1.0)), cond) def test_reduce_rational_inequalities_real_relational(): def OpenInterval(a, b): return Interval(a, b, True, True) def LeftOpenInterval(a, b): return Interval(a, b, True, False) def RightOpenInterval(a, b): return Interval(a, b, False, True) with assuming(Q.real(x)): assert reduce_rational_inequalities([[(x**2 + 3*x + 2)/(x**2 - 16) >= 0]], x, relational=False) == \ Union(OpenInterval(-oo, -4), Interval(-2, -1), OpenInterval(4, oo)) assert reduce_rational_inequalities([[((-2*x - 10)*(3 - x))/((x**2 + 5)*(x - 2)**2) < 0]], x, relational=False) == \ Union(OpenInterval(-5, 2), OpenInterval(2, 3)) assert reduce_rational_inequalities([[(x + 1)/(x - 5) <= 0]], x, assume=Q.real(x), relational=False) == \ RightOpenInterval(-1, 5) assert reduce_rational_inequalities([[(x**2 + 4*x + 3)/(x - 1) > 0]], x, assume=Q.real(x), relational=False) == \ Union(OpenInterval(-3, -1), OpenInterval(1, oo)) assert reduce_rational_inequalities([[(x**2 - 16)/(x - 1)**2 < 0]], x, assume=Q.real(x), relational=False) == \ Union(OpenInterval(-4, 1), OpenInterval(1, 4)) assert reduce_rational_inequalities([[(3*x + 1)/(x + 4) >= 1]], x, assume=Q.real(x), relational=False) == \ Union(OpenInterval(-oo, -4), RightOpenInterval(S(3)/2, oo)) assert reduce_rational_inequalities([[(x - 8)/x <= 3 - x]], x, assume=Q.real(x), relational=False) == \ Union(LeftOpenInterval(-oo, -2), LeftOpenInterval(0, 4)) def test_reduce_abs_inequalities(): real = Q.real(x) assert reduce_inequalities( abs(x - 5) < 3, assume=real) == And(Lt(2, x), Lt(x, 8)) assert reduce_inequalities( abs(2*x + 3) >= 8, assume=real) == Or(Le(x, -S(11)/2), Ge(x, S(5)/2)) assert reduce_inequalities(abs(x - 4) + abs( 3*x - 5) < 7, assume=real) == And(Lt(S(1)/2, x), Lt(x, 4)) assert reduce_inequalities(abs(x - 4) + abs(3*abs(x) - 5) < 7, assume=real) == Or(And(S(-2) < x, x < -1), And(S(1)/2 < x, x < 4)) raises(NotImplementedError, lambda: reduce_inequalities(abs(x - 5) < 3)) def test_reduce_inequalities_boolean(): assert reduce_inequalities( [Eq(x**2, 0), True]) == And(Eq(re(x), 0), Eq(im(x), 0)) assert reduce_inequalities([Eq(x**2, 0), False]) is False def test_reduce_inequalities_assume(): assert reduce_inequalities( [Le(x**2, 1), Q.real(x)]) == And(Le(-1, x), Le(x, 1)) assert reduce_inequalities( [Le(x**2, 1)], Q.real(x)) == And(Le(-1, x), Le(x, 1)) def test_reduce_inequalities_multivariate(): assert reduce_inequalities([Ge(x**2, 1), Ge(y**2, 1)]) == \ And(And(Or(Le(re(x), -1), Ge(re(x), 1)), Eq(im(x), 0)), And(Or(Le(re(y), -1), Ge(re(y), 1)), Eq(im(y), 0))) def test_reduce_inequalities_errors(): raises(NotImplementedError, lambda: reduce_inequalities(Ge(sin(x) + x, 1))) raises(NotImplementedError, lambda: reduce_inequalities(Ge(x**2*y + y, 1))) raises(NotImplementedError, lambda: reduce_inequalities(Ge(sqrt(2)*x, 1))) def test_hacky_inequalities(): assert reduce_inequalities(x + y < 1, symbols=[x]) == (x < 1 - y) assert reduce_inequalities(x + y >= 1, symbols=[x]) == (x >= 1 - y) def test_issue_3244(): eq = -3*x**2/2 - 45*x/4 + S(33)/2 > 0 assert reduce_inequalities(eq, Q.real(x)) == \ And(x < -S(15)/4 + sqrt(401)/4, -sqrt(401)/4 - S(15)/4 < x) sympy-0.7.4.1/sympy/solvers/tests/test_pde.py0000644000175000017500000002024112253362407021444 0ustar georgeskgeorgeskfrom sympy import (Derivative as D, Eq, exp, sin, Function, Symbol, symbols, cos, log) from sympy.core import S from sympy.solvers.pde import (pde_separate_add, pde_separate_mul, pdsolve, classify_pde, checkpdesol) from sympy.utilities.pytest import raises a, b, c, x, y = symbols('a b c x y') def test_pde_separate_add(): x, y, z, t = symbols("x,y,z,t") F, T, X, Y, Z, u = map(Function, 'FTXYZu') eq = Eq(D(u(x, t), x), D(u(x, t), t)*exp(u(x, t))) res = pde_separate_add(eq, u(x, t), [X(x), T(t)]) assert res == [D(X(x), x)*exp(-X(x)), D(T(t), t)*exp(T(t))] def test_pde_separate_mul(): x, y, z, t = symbols("x,y,z,t") c = Symbol("C", real=True) Phi = Function('Phi') F, R, T, X, Y, Z, u = map(Function, 'FRTXYZu') r, theta, z = symbols('r,theta,z') # Something simple :) eq = Eq(D(F(x, y, z), x) + D(F(x, y, z), y) + D(F(x, y, z), z)) # Duplicate arguments in functions raises( ValueError, lambda: pde_separate_mul(eq, F(x, y, z), [X(x), u(z, z)])) # Wrong number of arguments raises(ValueError, lambda: pde_separate_mul(eq, F(x, y, z), [X(x), Y(y)])) # Wrong variables: [x, y] -> [x, z] raises( ValueError, lambda: pde_separate_mul(eq, F(x, y, z), [X(t), Y(x, y)])) assert pde_separate_mul(eq, F(x, y, z), [Y(y), u(x, z)]) == \ [D(Y(y), y)/Y(y), -D(u(x, z), x)/u(x, z) - D(u(x, z), z)/u(x, z)] assert pde_separate_mul(eq, F(x, y, z), [X(x), Y(y), Z(z)]) == \ [D(X(x), x)/X(x), -D(Z(z), z)/Z(z) - D(Y(y), y)/Y(y)] # wave equation wave = Eq(D(u(x, t), t, t), c**2*D(u(x, t), x, x)) res = pde_separate_mul(wave, u(x, t), [X(x), T(t)]) assert res == [D(X(x), x, x)/X(x), D(T(t), t, t)/(c**2*T(t))] # Laplace equation in cylindrical coords eq = Eq(1/r * D(Phi(r, theta, z), r) + D(Phi(r, theta, z), r, 2) + 1/r**2 * D(Phi(r, theta, z), theta, 2) + D(Phi(r, theta, z), z, 2)) # Separate z res = pde_separate_mul(eq, Phi(r, theta, z), [Z(z), u(theta, r)]) assert res == [D(Z(z), z, z)/Z(z), -D(u(theta, r), r, r)/u(theta, r) - D(u(theta, r), r)/(r*u(theta, r)) - D(u(theta, r), theta, theta)/(r**2*u(theta, r))] # Lets use the result to create a new equation... eq = Eq(res[1], c) # ...and separate theta... res = pde_separate_mul(eq, u(theta, r), [T(theta), R(r)]) assert res == [D(T(theta), theta, theta)/T(theta), -r*D(R(r), r)/R(r) - r**2*D(R(r), r, r)/R(r) - c*r**2] # ...or r... res = pde_separate_mul(eq, u(theta, r), [R(r), T(theta)]) assert res == [r*D(R(r), r)/R(r) + r**2*D(R(r), r, r)/R(r) + c*r**2, -D(T(theta), theta, theta)/T(theta)] def test_pde_classify(): # When more number of hints are added, add tests for classifying here. f = Function('f') eq1 = a*f(x,y) + b*f(x,y).diff(x) + c*f(x,y).diff(y) eq2 = 3*f(x,y) + 2*f(x,y).diff(x) + f(x,y).diff(y) eq3 = a*f(x,y) + b*f(x,y).diff(x) + 2*f(x,y).diff(y) eq4 = x*f(x,y) + f(x,y).diff(x) + 3*f(x,y).diff(y) eq5 = x**2*f(x,y) + x*f(x,y).diff(x) + x*y*f(x,y).diff(y) eq6 = y*x**2*f(x,y) + y*f(x,y).diff(x) + f(x,y).diff(y) for eq in [eq1, eq2, eq3]: assert classify_pde(eq) == ('1st_linear_constant_coeff_homogeneous',) for eq in [eq4, eq5, eq6]: assert classify_pde(eq) == ('1st_linear_variable_coeff',) def test_checkpdesol(): f, F = map(Function, ['f', 'F']) eq1 = a*f(x,y) + b*f(x,y).diff(x) + c*f(x,y).diff(y) eq2 = 3*f(x,y) + 2*f(x,y).diff(x) + f(x,y).diff(y) eq3 = a*f(x,y) + b*f(x,y).diff(x) + 2*f(x,y).diff(y) for eq in [eq1, eq2, eq3]: assert checkpdesol(eq, pdsolve(eq))[0] eq4 = x*f(x,y) + f(x,y).diff(x) + 3*f(x,y).diff(y) eq5 = 2*f(x,y) + 1*f(x,y).diff(x) + 3*f(x,y).diff(y) eq6 = f(x,y) + 1*f(x,y).diff(x) + 3*f(x,y).diff(y) assert checkpdesol(eq4, [pdsolve(eq5), pdsolve(eq6)]) == [ (False, (x - 2)*F(3*x - y)*exp(-x/S(5) - 3*y/S(5))), (False, (x - 1)*F(3*x - y)*exp(-x/S(10) - 3*y/S(10)))] for eq in [eq4, eq5, eq6]: assert checkpdesol(eq, pdsolve(eq))[0] def test_solvefun(): f, F, G, H = map(Function, ['f', 'F', 'G', 'H']) eq1 = f(x,y) + f(x,y).diff(x) + f(x,y).diff(y) assert pdsolve(eq1) == Eq(f(x, y), F(x - y)*exp(-x/2 - y/2)) assert pdsolve(eq1, solvefun=G) == Eq(f(x, y), G(x - y)*exp(-x/2 - y/2)) assert pdsolve(eq1, solvefun=H) == Eq(f(x, y), H(x - y)*exp(-x/2 - y/2)) def test_pde_1st_linear_constant_coeff_homogeneous(): f, F = map(Function, ['f', 'F']) u = f(x, y) eq = 2*u + u.diff(x) + u.diff(y) assert classify_pde(eq) == ('1st_linear_constant_coeff_homogeneous',) sol = pdsolve(eq) assert sol == Eq(u, F(x - y)*exp(-x - y)) assert checkpdesol(eq, sol)[0] eq = 4 + (3*u.diff(x)/u) + (2*u.diff(y)/u) assert classify_pde(eq) == ('1st_linear_constant_coeff_homogeneous',) sol = pdsolve(eq) assert sol == Eq(u, F(2*x - 3*y)*exp(-S(12)*x/13 - S(8)*y/13)) assert checkpdesol(eq, sol)[0] eq = u + (6*u.diff(x)) + (7*u.diff(y)) assert classify_pde(eq) == ('1st_linear_constant_coeff_homogeneous',) sol = pdsolve(eq) assert sol == Eq(u, F(7*x - 6*y)*exp(-6*x/S(85) - 7*y/S(85))) assert checkpdesol(eq, sol)[0] eq = a*u + b*u.diff(x) + c*u.diff(y) sol = pdsolve(eq) assert checkpdesol(eq, sol)[0] def test_pde_1st_linear_constant_coeff(): f, F = map(Function, ['f', 'F']) u = f(x,y) eq = -2*u.diff(x) + 4*u.diff(y) + 5*u - exp(x + 3*y) sol = pdsolve(eq) assert sol == Eq(f(x,y), (F(4*x + 2*y) + exp(x/S(2) + 4*y)/S(15))*exp(x/S(2) - y)) assert classify_pde(eq) == ('1st_linear_constant_coeff', '1st_linear_constant_coeff_Integral') assert checkpdesol(eq, sol)[0] eq = (u.diff(x)/u) + (u.diff(y)/u) + 1 - (exp(x + y)/u) sol = pdsolve(eq) assert sol == Eq(f(x, y), F(x - y)*exp(-x/2 - y/2) + exp(x + y)/S(3)) assert classify_pde(eq) == ('1st_linear_constant_coeff', '1st_linear_constant_coeff_Integral') assert checkpdesol(eq, sol)[0] eq = 2*u + -u.diff(x) + 3*u.diff(y) + sin(x) sol = pdsolve(eq) assert sol == Eq(f(x, y), F(3*x + y)*exp(x/S(5) - 3*y/S(5)) - 2*sin(x)/S(5) - cos(x)/S(5)) assert classify_pde(eq) == ('1st_linear_constant_coeff', '1st_linear_constant_coeff_Integral') assert checkpdesol(eq, sol)[0] eq = u + u.diff(x) + u.diff(y) + x*y sol = pdsolve(eq) assert sol == Eq(f(x, y), -x*y + x + y + F(x - y)*exp(-x/S(2) - y/S(2)) - 2) assert classify_pde(eq) == ('1st_linear_constant_coeff', '1st_linear_constant_coeff_Integral') assert checkpdesol(eq, sol)[0] eq = u + u.diff(x) + u.diff(y) + log(x) assert classify_pde(eq) == ('1st_linear_constant_coeff', '1st_linear_constant_coeff_Integral') def test_pdsolve_all(): f, F = map(Function, ['f', 'F']) u = f(x,y) eq = u + u.diff(x) + u.diff(y) + x**2*y sol = pdsolve(eq, hint = 'all') keys = ['1st_linear_constant_coeff', '1st_linear_constant_coeff_Integral', 'default', 'order'] assert sorted(sol.keys()) == keys assert sol['order'] == 1 assert sol['default'] == '1st_linear_constant_coeff' assert sol['1st_linear_constant_coeff'] == Eq(f(x, y), -x**2*y + x**2 + 2*x*y - 4*x - 2*y + F(x - y)*exp(-x/S(2) - y/S(2)) + 6) def test_pdsolve_variable_coeff(): f, F = map(Function, ['f', 'F']) u = f(x, y) eq = x*(u.diff(x)) - y*(u.diff(y)) + y**2*u - y**2 sol = pdsolve(eq, hint="1st_linear_variable_coeff") assert sol == Eq(u, F(x*y)*exp(y**2/2) + 1) assert checkpdesol(eq, sol)[0] eq = x**2*u + x*u.diff(x) + x*y*u.diff(y) sol = pdsolve(eq, hint='1st_linear_variable_coeff') assert sol == Eq(u, F(y*exp(-x))*exp(-x**2/2)) assert checkpdesol(eq, sol)[0] eq = y*x**2*u + y*u.diff(x) + u.diff(y) sol = pdsolve(eq, hint='1st_linear_variable_coeff') assert sol == Eq(u, F(-x + y**2/2)*exp(-x**3/3)) assert checkpdesol(eq, sol)[0] eq = exp(x)**2*(u.diff(x)) + y sol = pdsolve(eq, hint='1st_linear_variable_coeff') assert sol == Eq(u, y*exp(-2*x)/2 + F(y)) assert checkpdesol(eq, sol)[0] eq = exp(2*x)*(u.diff(y)) + y*u - u sol = pdsolve(eq, hint='1st_linear_variable_coeff') assert sol == Eq(u, exp((-y**2 + 2*y + 2*F(x))*exp(-2*x)/2)) sympy-0.7.4.1/sympy/solvers/tests/test_ode.py0000644000175000017500000024722012253362407021453 0ustar georgeskgeorgeskfrom __future__ import division from sympy import (acos, acosh, asinh, atan, cos, Derivative, diff, dsolve, Dummy, Eq, erf, erfi, exp, Function, I, Integral, LambertW, log, O, pi, Rational, RootOf, S, simplify, sin, sqrt, Symbol, tan, asin, Piecewise, symbols, Poly) from sympy.solvers.ode import (_undetermined_coefficients_match, checkodesol, classify_ode, constant_renumber, constantsimp, homogeneous_order, infinitesimals, checkinfsol) from sympy.solvers.deutils import ode_order from sympy.utilities.pytest import XFAIL, skip, raises, slow C1, C2, C3, C4, C5, C6, C7, C8, C9, C10 = symbols('C1:11') x, y, z = symbols('x:z', real=True) f = Function('f') g = Function('g') # Note: the tests below may fail (but still be correct) if ODE solver, # the integral engine, solve(), or even simplify() changes. Also, in # differently formatted solutions, the arbitrary constants might not be # equal. Using specific hints in tests can help to avoid this. # Tests of order higher than 1 should run the solutions through # constant_renumber because it will normalize it (constant_renumber causes # dsolve() to return different results on different machines) def test_checkodesol(): # For the most part, checkodesol is well tested in the tests below. # These tests only handle cases not checked below. raises(ValueError, lambda: checkodesol(f(x, y).diff(x), Eq(f(x, y), x))) raises(ValueError, lambda: checkodesol(f(x).diff(x), Eq(f(x, y), x), f(x, y))) assert checkodesol(f(x).diff(x), Eq(f(x, y), x)) == \ (False, -f(x).diff(x) + f(x, y).diff(x) - 1) assert checkodesol(f(x).diff(x), Eq(f(x), x)) is not True assert checkodesol(f(x).diff(x), Eq(f(x), x)) == (False, 1) sol1 = Eq(f(x)**5 + 11*f(x) - 2*f(x) + x, 0) assert checkodesol(diff(sol1.lhs, x), sol1) == (True, 0) assert checkodesol(diff(sol1.lhs, x)*exp(f(x)), sol1) == (True, 0) assert checkodesol(diff(sol1.lhs, x, 2), sol1) == (True, 0) assert checkodesol(diff(sol1.lhs, x, 2)*exp(f(x)), sol1) == (True, 0) assert checkodesol(diff(sol1.lhs, x, 3), sol1) == (True, 0) assert checkodesol(diff(sol1.lhs, x, 3)*exp(f(x)), sol1) == (True, 0) assert checkodesol(diff(sol1.lhs, x, 3), Eq(f(x), x*log(x))) == \ (False, 60*x**4*((log(x) + 1)**2 + log(x))*( log(x) + 1)*log(x)**2 - 5*x**4*log(x)**4 - 9) assert checkodesol(diff(exp(f(x)) + x, x)*x, Eq(exp(f(x)) + x)) == \ (True, 0) assert checkodesol(diff(exp(f(x)) + x, x)*x, Eq(exp(f(x)) + x), solve_for_func=False) == (True, 0) assert checkodesol(f(x).diff(x, 2), [Eq(f(x), C1 + C2*x), Eq(f(x), C2 + C1*x), Eq(f(x), C1*x + C2*x**2)]) == \ [(True, 0), (True, 0), (False, 2*C2)] assert checkodesol(f(x).diff(x, 2), set([Eq(f(x), C1 + C2*x), Eq(f(x), C2 + C1*x), Eq(f(x), C1*x + C2*x**2)])) == \ set([(True, 0), (True, 0), (False, 2*C2)]) assert checkodesol(f(x).diff(x) - 1/f(x)/2, Eq(f(x)**2, x)) == \ [(True, 0), (True, 0)] assert checkodesol(f(x).diff(x) - f(x), Eq(C1*exp(x), f(x))) == (True, 0) # Based on test_1st_homogeneous_coeff_ode2_eq3sol. Make sure that # checkodesol tries back substituting f(x) when it can. eq3 = x*exp(f(x)/x) + f(x) - x*f(x).diff(x) sol3 = Eq(f(x), log(log(C1/x)**(-x))) assert not checkodesol(eq3, sol3)[1].has(f(x)) def test_dsolve_options(): eq = x*f(x).diff(x) + f(x) a = dsolve(eq, hint='all') b = dsolve(eq, hint='all', simplify=False) c = dsolve(eq, hint='all_Integral') keys = ['1st_exact', '1st_exact_Integral', '1st_homogeneous_coeff_best', '1st_homogeneous_coeff_subs_dep_div_indep', '1st_homogeneous_coeff_subs_dep_div_indep_Integral', '1st_homogeneous_coeff_subs_indep_div_dep', '1st_homogeneous_coeff_subs_indep_div_dep_Integral', '1st_linear', '1st_linear_Integral', 'almost_linear', 'almost_linear_Integral', 'best', 'best_hint', 'default', 'lie_group', 'nth_linear_euler_eq_homogeneous', 'order', 'separable', 'separable_Integral'] Integral_keys = ['1st_exact_Integral', '1st_homogeneous_coeff_subs_dep_div_indep_Integral', '1st_homogeneous_coeff_subs_indep_div_dep_Integral', '1st_linear_Integral', 'almost_linear_Integral', 'best', 'best_hint', 'default', 'nth_linear_euler_eq_homogeneous', 'order', 'separable_Integral'] assert sorted(a.keys()) == keys assert a['order'] == ode_order(eq, f(x)) assert a['best'] == Eq(f(x), C1/x) assert dsolve(eq, hint='best') == Eq(f(x), C1/x) assert a['default'] == 'separable' assert a['best_hint'] == 'separable' assert not a['1st_exact'].has(Integral) assert not a['separable'].has(Integral) assert not a['1st_homogeneous_coeff_best'].has(Integral) assert not a['1st_homogeneous_coeff_subs_dep_div_indep'].has(Integral) assert not a['1st_homogeneous_coeff_subs_indep_div_dep'].has(Integral) assert not a['1st_linear'].has(Integral) assert a['1st_linear_Integral'].has(Integral) assert a['1st_exact_Integral'].has(Integral) assert a['1st_homogeneous_coeff_subs_dep_div_indep_Integral'].has(Integral) assert a['1st_homogeneous_coeff_subs_indep_div_dep_Integral'].has(Integral) assert a['separable_Integral'].has(Integral) assert sorted(b.keys()) == keys assert b['order'] == ode_order(eq, f(x)) assert b['best'] == Eq(f(x), C1/x) assert dsolve(eq, hint='best', simplify=False) == Eq(f(x), C1/x) assert b['default'] == 'separable' assert b['best_hint'] == '1st_linear' assert a['separable'] != b['separable'] assert a['1st_homogeneous_coeff_subs_dep_div_indep'] != \ b['1st_homogeneous_coeff_subs_dep_div_indep'] assert a['1st_homogeneous_coeff_subs_indep_div_dep'] != \ b['1st_homogeneous_coeff_subs_indep_div_dep'] assert not b['1st_exact'].has(Integral) assert not b['separable'].has(Integral) assert not b['1st_homogeneous_coeff_best'].has(Integral) assert not b['1st_homogeneous_coeff_subs_dep_div_indep'].has(Integral) assert not b['1st_homogeneous_coeff_subs_indep_div_dep'].has(Integral) assert not b['1st_linear'].has(Integral) assert b['1st_linear_Integral'].has(Integral) assert b['1st_exact_Integral'].has(Integral) assert b['1st_homogeneous_coeff_subs_dep_div_indep_Integral'].has(Integral) assert b['1st_homogeneous_coeff_subs_indep_div_dep_Integral'].has(Integral) assert b['separable_Integral'].has(Integral) assert sorted(c.keys()) == Integral_keys raises(ValueError, lambda: dsolve(eq, hint='notarealhint')) raises(ValueError, lambda: dsolve(eq, hint='Liouville')) assert dsolve(f(x).diff(x) - 1/f(x)**2, hint='all')['best'] == \ dsolve(f(x).diff(x) - 1/f(x)**2, hint='best') assert dsolve(f(x) + f(x).diff(x) + sin(x).diff(x) + 1, f(x), hint="1st_linear_Integral") == \ Eq(f(x), (C1 + Integral((-sin(x).diff(x) - 1)* exp(Integral(1, x)), x))*exp(-Integral(1, x))) def test_classify_ode(): assert classify_ode(f(x).diff(x, 2), f(x)) == \ ('nth_linear_constant_coeff_homogeneous', 'Liouville', '2nd_power_series_ordinary' ,'Liouville_Integral') assert classify_ode(f(x), f(x)) == () assert 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', '1st_power_series', 'lie_group', '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') assert classify_ode(f(x).diff(x)**2, f(x)) == ('lie_group',) # 1650: f(x) should be cleared from highest derivative before classifying a = classify_ode(Eq(f(x).diff(x) + f(x), x), f(x)) b = classify_ode(f(x).diff(x)*f(x) + f(x)*f(x) - x*f(x), f(x)) c = classify_ode(f(x).diff(x)/f(x) + f(x)/f(x) - x/f(x), f(x)) assert a == ('1st_linear', 'Bernoulli', 'almost_linear', '1st_power_series', "lie_group", 'nth_linear_constant_coeff_undetermined_coefficients', 'nth_linear_constant_coeff_variation_of_parameters', '1st_linear_Integral', 'Bernoulli_Integral', 'almost_linear_Integral', 'nth_linear_constant_coeff_variation_of_parameters_Integral') assert b == c != () assert classify_ode( 2*x*f(x)*f(x).diff(x) + (1 + x)*f(x)**2 - exp(x), f(x) ) == ('Bernoulli', 'almost_linear', 'lie_group', 'Bernoulli_Integral', 'almost_linear_Integral') assert 'Riccati_special_minus2' in \ classify_ode(2*f(x).diff(x) + f(x)**2 - f(x)/x + 3*x**(-2), f(x)) raises(ValueError, lambda: classify_ode(x + f(x, y).diff(x).diff( y), f(x, y))) # 2077 k = Symbol('k') assert classify_ode(f(x).diff(x)/(k*f(x) + k*x*f(x)) + 2*f(x)/(k*f(x) + k*x*f(x)) + x*f(x).diff(x)/(k*f(x) + k*x*f(x)) + z, f(x)) == \ ('separable', '1st_exact', '1st_power_series', 'lie_group', 'separable_Integral', '1st_exact_Integral') # preprocessing ans = ('separable', '1st_exact', '1st_linear', 'Bernoulli', '1st_homogeneous_coeff_best', '1st_homogeneous_coeff_subs_indep_div_dep', '1st_homogeneous_coeff_subs_dep_div_indep', 'separable_reduced', '1st_power_series', 'lie_group', 'nth_linear_constant_coeff_undetermined_coefficients', 'nth_linear_constant_coeff_variation_of_parameters', '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', 'separable_reduced_Integral', 'nth_linear_constant_coeff_variation_of_parameters_Integral') # w/o f(x) given assert classify_ode(diff(f(x) + x, x) + diff(f(x), x)) == ans # w/ f(x) and prep=True assert classify_ode(diff(f(x) + x, x) + diff(f(x), x), f(x), prep=True) == ans def test_ode_order(): f = Function('f') g = Function('g') x = Symbol('x') assert ode_order(3*x*exp(f(x)), f(x)) == 0 assert ode_order(x*diff(f(x), x) + 3*x*f(x) - sin(x)/x, f(x)) == 1 assert ode_order(x**2*f(x).diff(x, x) + x*diff(f(x), x) - f(x), f(x)) == 2 assert ode_order(diff(x*exp(f(x)), x, x), f(x)) == 2 assert ode_order(diff(x*diff(x*exp(f(x)), x, x), x), f(x)) == 3 assert ode_order(diff(f(x), x, x), g(x)) == 0 assert ode_order(diff(f(x), x, x)*diff(g(x), x), f(x)) == 2 assert ode_order(diff(f(x), x, x)*diff(g(x), x), g(x)) == 1 assert ode_order(diff(x*diff(x*exp(f(x)), x, x), x), g(x)) == 0 # issue 2736: ode_order has to also work for unevaluated derivatives # (ie, without using doit()). assert ode_order(Derivative(x*f(x), x), f(x)) == 1 assert ode_order(x*sin(Derivative(x*f(x)**2, x, x)), f(x)) == 2 assert ode_order(Derivative(x*Derivative(x*exp(f(x)), x, x), x), g(x)) == 0 assert ode_order(Derivative(f(x), x, x), g(x)) == 0 assert ode_order(Derivative(x*exp(f(x)), x, x), f(x)) == 2 assert ode_order(Derivative(f(x), x, x)*Derivative(g(x), x), g(x)) == 1 assert ode_order(Derivative(x*Derivative(f(x), x, x), x), f(x)) == 3 assert ode_order( x*sin(Derivative(x*Derivative(f(x), x)**2, x, x)), f(x)) == 3 # In all tests below, checkodesol has the order option set to prevent # superfluous calls to ode_order(), and the solve_for_func flag set to False # because dsolve() already tries to solve for the function, unless the # simplify=False option is set. def test_old_ode_tests(): # These are simple tests from the old ode module eq1 = Eq(f(x).diff(x), 0) eq2 = Eq(3*f(x).diff(x) - 5, 0) eq3 = Eq(3*f(x).diff(x), 5) eq4 = Eq(9*f(x).diff(x, x) + f(x), 0) eq5 = Eq(9*f(x).diff(x, x), f(x)) # Type: a(x)f'(x)+b(x)*f(x)+c(x)=0 eq6 = Eq(x**2*f(x).diff(x) + 3*x*f(x) - sin(x)/x, 0) eq7 = Eq(f(x).diff(x, x) - 3*diff(f(x), x) + 2*f(x), 0) # Type: 2nd order, constant coefficients (two real different roots) eq8 = Eq(f(x).diff(x, x) - 4*diff(f(x), x) + 4*f(x), 0) # Type: 2nd order, constant coefficients (two real equal roots) eq9 = Eq(f(x).diff(x, x) + 2*diff(f(x), x) + 3*f(x), 0) # Type: 2nd order, constant coefficients (two complex roots) eq10 = Eq(3*f(x).diff(x) - 1, 0) eq11 = Eq(x*f(x).diff(x) - 1, 0) sol1 = Eq(f(x), C1) sol2 = Eq(f(x), C1 + 5*x/3) sol3 = Eq(f(x), C1 + 5*x/3) sol4 = Eq(f(x), C1*sin(x/3) + C2*cos(x/3)) sol5 = Eq(f(x), C1*exp(-x/3) + C2*exp(x/3)) sol6 = Eq(f(x), (C1 - cos(x))/x**3) sol7 = Eq(f(x), C1*exp(x) + C2*exp(2*x)) sol8 = Eq(f(x), (C1 + C2*x)*exp(2*x)) sol9 = Eq(f(x), (C1*sin(x*sqrt(2)) + C2*cos(x*sqrt(2)))*exp(-x)) sol10 = Eq(f(x), C1 + x/3) sol11 = Eq(f(x), C1 + log(x)) assert dsolve(eq1) == sol1 assert dsolve(eq1.lhs) == sol1 assert dsolve(eq2) == sol2 assert dsolve(eq3) == sol3 assert dsolve(eq4) == sol4 assert dsolve(eq5) == sol5 assert dsolve(eq6) == sol6 assert dsolve(eq7) == sol7 assert dsolve(eq8) == sol8 assert dsolve(eq9) == sol9 assert dsolve(eq10) == sol10 assert dsolve(eq11) == sol11 assert checkodesol(eq1, sol1, order=1, solve_for_func=False)[0] assert checkodesol(eq2, sol2, order=1, solve_for_func=False)[0] assert checkodesol(eq3, sol3, order=1, solve_for_func=False)[0] assert checkodesol(eq4, sol4, order=2, solve_for_func=False)[0] assert checkodesol(eq5, sol5, order=2, solve_for_func=False)[0] assert checkodesol(eq6, sol6, order=1, solve_for_func=False)[0] assert checkodesol(eq7, sol7, order=2, solve_for_func=False)[0] assert checkodesol(eq8, sol8, order=2, solve_for_func=False)[0] assert checkodesol(eq9, sol9, order=2, solve_for_func=False)[0] assert checkodesol(eq10, sol10, order=1, solve_for_func=False)[0] assert checkodesol(eq11, sol11, order=1, solve_for_func=False)[0] def test_1st_linear(): # Type: first order linear form f'(x)+p(x)f(x)=q(x) eq = Eq(f(x).diff(x) + x*f(x), x**2) sol = Eq(f(x), (C1 + x*exp(x**2/2) - sqrt(2)*sqrt(pi)*erfi(sqrt(2)*x/2)/2)*exp(-x**2/2)) assert dsolve(eq, hint='1st_linear') == sol assert checkodesol(eq, sol, order=1, solve_for_func=False)[0] def test_Bernoulli(): # Type: Bernoulli, f'(x) + p(x)*f(x) == q(x)*f(x)**n eq = Eq(x*f(x).diff(x) + f(x) - f(x)**2, 0) sol = dsolve(eq, f(x), hint='Bernoulli') assert sol == Eq(f(x), 1/(x*(C1 + 1/x))) assert checkodesol(eq, sol, order=1, solve_for_func=False)[0] def test_Riccati_special_minus2(): # Type: Riccati special alpha = -2, a*dy/dx + b*y**2 + c*y/x +d/x**2 eq = 2*f(x).diff(x) + f(x)**2 - f(x)/x + 3*x**(-2) sol = dsolve(eq, f(x), hint='Riccati_special_minus2') assert checkodesol(eq, sol, order=1, solve_for_func=False)[0] def test_1st_exact1(): # Type: Exact differential equation, p(x,f) + q(x,f)*f' == 0, # where dp/df == dq/dx eq1 = sin(x)*cos(f(x)) + cos(x)*sin(f(x))*f(x).diff(x) eq2 = (2*x*f(x) + 1)/f(x) + (f(x) - x)/f(x)**2*f(x).diff(x) eq3 = 2*x + f(x)*cos(x) + (2*f(x) + sin(x) - sin(f(x)))*f(x).diff(x) eq4 = cos(f(x)) - (x*sin(f(x)) - f(x)**2)*f(x).diff(x) eq5 = 2*x*f(x) + (x**2 + f(x)**2)*f(x).diff(x) sol1 = [Eq(f(x), -acos(C1/cos(x)) + 2*pi), Eq(f(x), acos(C1/cos(x)))] sol2 = Eq(f(x), C1*exp(-x**2 + LambertW(C2*x*exp(x**2)))) sol2b = Eq(log(f(x)) + x/f(x) + x**2, C1) sol3 = Eq(f(x)*sin(x) + cos(f(x)) + x**2 + f(x)**2, C1) sol4 = Eq(x*cos(f(x)) + f(x)**3/3, C1) sol5 = Eq(x**2*f(x) + f(x)**3/3, C1) assert dsolve(eq1, f(x), hint='1st_exact') == sol1 assert dsolve(eq2, f(x), hint='1st_exact') == sol2 assert dsolve(eq3, f(x), hint='1st_exact') == sol3 assert dsolve(eq4, hint='1st_exact') == sol4 assert dsolve(eq5, hint='1st_exact', simplify=False) == sol5 assert checkodesol(eq1, sol1, order=1, solve_for_func=False)[0] # issue 1981 needs to be addressed to test these # assert checkodesol(eq2, sol2, order=1, solve_for_func=False)[0] assert checkodesol(eq2, sol2b, order=1, solve_for_func=False)[0] assert checkodesol(eq3, sol3, order=1, solve_for_func=False)[0] assert checkodesol(eq4, sol4, order=1, solve_for_func=False)[0] assert checkodesol(eq5, sol5, order=1, solve_for_func=False)[0] @slow @XFAIL def test_1st_exact2(): """ This is an exact equation that fails under the exact engine. It is caught by first order homogeneous albeit with a much contorted solution. The exact engine fails because of a poorly simplified integral of q(0,y)dy, where q is the function multiplying f'. The solutions should be Eq(sqrt(x**2+f(x)**2)**3+y**3, C1). The equation below is equivalent, but it is so complex that checkodesol fails, and takes a long time to do so. """ eq = (x*sqrt(x**2 + f(x)**2) - (x**2*f(x)/(f(x) - sqrt(x**2 + f(x)**2)))*f(x).diff(x)) sol = dsolve(eq) assert sol == Eq(log(x), C1 - 9*sqrt(1 + f(x)**2/x**2)*asinh(f(x)/x)/(-27*f(x)/x + 27*sqrt(1 + f(x)**2/x**2)) - 9*sqrt(1 + f(x)**2/x**2)* log(1 - sqrt(1 + f(x)**2/x**2)*f(x)/x + 2*f(x)**2/x**2)/ (-27*f(x)/x + 27*sqrt(1 + f(x)**2/x**2)) + 9*asinh(f(x)/x)*f(x)/(x*(-27*f(x)/x + 27*sqrt(1 + f(x)**2/x**2))) + 9*f(x)*log(1 - sqrt(1 + f(x)**2/x**2)*f(x)/x + 2*f(x)**2/x**2)/ (x*(-27*f(x)/x + 27*sqrt(1 + f(x)**2/x**2)))) assert checkodesol(eq, sol, order=1, solve_for_func=False)[0] def test_separable1(): # test_separable1-5 are from Ordinary Differential Equations, Tenenbaum and # Pollard, pg. 55 eq1 = f(x).diff(x) - f(x) eq2 = x*f(x).diff(x) - f(x) eq3 = f(x).diff(x) + sin(x) eq4 = f(x)**2 + 1 - (x**2 + 1)*f(x).diff(x) eq5 = f(x).diff(x)/tan(x) - f(x) - 2 sol1 = Eq(f(x), C1*exp(x)) sol2 = Eq(f(x), C1*x) sol3 = Eq(f(x), C1 + cos(x)) sol4 = Eq(atan(f(x)), C1 + atan(x)) sol5 = Eq(f(x), -2 + C1*sqrt(1 + tan(x)**2)) #sol5 = Eq(f(x), C1*(C2 + sqrt(1 + tan(x)**2))) #sol5 = Eq(-log(2 + f(x)), C1 - log(1 + tan(x)**2)/2) assert dsolve(eq1, hint='separable') == sol1 assert dsolve(eq2, hint='separable') == sol2 assert dsolve(eq3, hint='separable') == sol3 assert dsolve(eq4, hint='separable', simplify=False) == sol4 assert dsolve(eq5, hint='separable') == simplify(sol5).expand() assert checkodesol(eq1, sol1, order=1, solve_for_func=False)[0] assert checkodesol(eq2, sol2, order=1, solve_for_func=False)[0] assert checkodesol(eq3, sol3, order=1, solve_for_func=False)[0] assert checkodesol(eq4, sol4, order=1, solve_for_func=False)[0] assert checkodesol(eq5, sol5, order=1, solve_for_func=False)[0] def test_separable2(): a = Symbol('a') eq6 = f(x)*x**2*f(x).diff(x) - f(x)**3 - 2*x**2*f(x).diff(x) eq7 = f(x)**2 - 1 - (2*f(x) + x*f(x))*f(x).diff(x) eq8 = x*log(x)*f(x).diff(x) + sqrt(1 + f(x)**2) eq9 = exp(x + 1)*tan(f(x)) + cos(f(x))*f(x).diff(x) eq10 = (x*cos(f(x)) + x**2*sin(f(x))*f(x).diff(x) - a**2*sin(f(x))*f(x).diff(x)) # solve() messes this one up a little bit, so lets test _Integral here # We have to test strings with _Integral because y is a dummy variable. sol6str = ("Integral((_y - 2)/_y**3, (_y, f(x))) " "== C1 + Integral(x**(-2), x)") sol7 = Eq(-log(-1 + f(x)**2)/2, C1 - log(2 + x)) sol8 = Eq(asinh(f(x)), C1 - log(log(x))) # integrate cannot handle the integral on the lhs (cos/tan) sol9str = ("Integral(cos(_y)/tan(_y), (_y, f(x)))" " == C1 + Integral(-E*exp(x), x)") sol10 = Eq(-log(-1 + sin(f(x))**2)/2, C1 - log(x**2 - a**2)/2) assert str(dsolve(eq6, hint='separable_Integral')) == sol6str assert dsolve(eq7, hint='separable', simplify=False) == sol7 assert dsolve(eq8, hint='separable', simplify=False) == sol8 assert str(dsolve(eq9, hint='separable_Integral')) == sol9str assert dsolve(eq10, hint='separable', simplify=False) == sol10 assert checkodesol(eq7, sol7, order=1, solve_for_func=False)[0] assert checkodesol(eq8, sol8, order=1, solve_for_func=False)[0] assert checkodesol(eq10, sol10, order=1, solve_for_func=False)[0] def test_separable3(): eq11 = f(x).diff(x) - f(x)*tan(x) eq12 = (x - 1)*cos(f(x))*f(x).diff(x) - 2*x*sin(f(x)) eq13 = f(x).diff(x) - f(x)*log(f(x))/tan(x) sol11 = Eq(f(x), C1*sqrt(1 + tan(x)**2)) sol12 = Eq(log(-1 + cos(f(x))**2)/2, C1 + 2*x + 2*log(x - 1)) sol13 = Eq(log(log(f(x))), C1 + log(cos(x)**2 - 1)/2) assert dsolve(eq11, hint='separable') == simplify(sol11) assert dsolve(eq12, hint='separable', simplify=False) == sol12 assert dsolve(eq13, hint='separable', simplify=False) == sol13 assert checkodesol(eq11, sol11, order=1, solve_for_func=False)[0] assert checkodesol(eq13, sol13, order=1, solve_for_func=False)[0] def test_separable4(): # This has a slow integral (1/((1 + y**2)*atan(y))), so we isolate it. eq14 = x*f(x).diff(x) + (1 + f(x)**2)*atan(f(x)) sol14 = Eq(log(atan(f(x))), C1 - log(x)) assert dsolve(eq14, hint='separable', simplify=False) == sol14 assert checkodesol(eq14, sol14, order=1, solve_for_func=False)[0] def test_separable5(): eq15 = f(x).diff(x) + x*(f(x) + 1) eq16 = exp(f(x)**2)*(x**2 + 2*x + 1) + (x*f(x) + f(x))*f(x).diff(x) eq17 = f(x).diff(x) + f(x) eq18 = sin(x)*cos(2*f(x)) + cos(x)*sin(2*f(x))*f(x).diff(x) eq19 = (1 - x)*f(x).diff(x) - x*(f(x) + 1) eq20 = f(x)*diff(f(x), x) + x - 3*x*f(x)**2 eq21 = f(x).diff(x) - exp(x + f(x)) sol15 = Eq(f(x), -1 + C1*exp(-x**2/2)) sol16 = Eq(-exp(-f(x)**2)/2, C1 - x - x**2/2) sol17 = Eq(f(x), C1*exp(-x)) sol18 = Eq(-log(-1 + sin(2*f(x))**2)/4, C1 + log(-1 + sin(x)**2)/2) sol19 = Eq(f(x), (C1*exp(-x) - x + 1)/(x - 1)) sol20 = Eq(log(-1 + 3*f(x)**2)/6, C1 + x**2/2) sol21 = Eq(-exp(-f(x)), C1 + exp(x)) assert dsolve(eq15, hint='separable') == sol15 assert dsolve(eq16, hint='separable', simplify=False) == sol16 assert dsolve(eq17, hint='separable') == sol17 assert dsolve(eq18, hint='separable', simplify=False) == sol18 assert dsolve(eq19, hint='separable') == sol19 assert dsolve(eq20, hint='separable', simplify=False) == sol20 assert dsolve(eq21, hint='separable', simplify=False) == sol21 assert checkodesol(eq15, sol15, order=1, solve_for_func=False)[0] assert checkodesol(eq16, sol16, order=1, solve_for_func=False)[0] assert checkodesol(eq17, sol17, order=1, solve_for_func=False)[0] assert checkodesol(eq18, sol18, order=1, solve_for_func=False)[0] assert checkodesol(eq19, sol19, order=1, solve_for_func=False)[0] assert checkodesol(eq20, sol20, order=1, solve_for_func=False)[0] assert checkodesol(eq21, sol21, order=1, solve_for_func=False)[0] def test_separable_1_5_checkodesol(): eq12 = (x - 1)*cos(f(x))*f(x).diff(x) - 2*x*sin(f(x)) sol12 = Eq(-log(1 - cos(f(x))**2)/2, C1 - 2*x - 2*log(1 - x)) assert checkodesol(eq12, sol12, order=1, solve_for_func=False)[0] def test_homogeneous_order(): assert homogeneous_order(exp(y/x) + tan(y/x), x, y) == 0 assert homogeneous_order(x**2 + sin(x)*cos(y), x, y) is None assert homogeneous_order(x - y - x*sin(y/x), x, y) == 1 assert homogeneous_order((x*y + sqrt(x**4 + y**4) + x**2*(log(x) - log(y)))/ (pi*x**Rational(2, 3)*sqrt(y)**3), x, y) == Rational(-1, 6) assert homogeneous_order(y/x*cos(y/x) - x/y*sin(y/x) + cos(y/x), x, y) == 0 assert homogeneous_order(f(x), x, f(x)) == 1 assert homogeneous_order(f(x)**2, x, f(x)) == 2 assert homogeneous_order(x*y*z, x, y) == 2 assert homogeneous_order(x*y*z, x, y, z) == 3 assert homogeneous_order(x**2*f(x)/sqrt(x**2 + f(x)**2), f(x)) is None assert homogeneous_order(f(x, y)**2, x, f(x, y), y) == 2 assert homogeneous_order(f(x, y)**2, x, f(x), y) is None assert homogeneous_order(f(x, y)**2, x, f(x, y)) is None assert homogeneous_order(f(y, x)**2, x, y, f(x, y)) is None assert homogeneous_order(f(y), f(x), x) is None assert homogeneous_order(-f(x)/x + 1/sin(f(x)/ x), f(x), x) == 0 assert homogeneous_order(log(1/y) + log(x**2), x, y) is None assert homogeneous_order(log(1/y) + log(x), x, y) == 0 assert homogeneous_order(log(x/y), x, y) == 0 assert homogeneous_order(2*log(1/y) + 2*log(x), x, y) == 0 a = Symbol('a') assert homogeneous_order(a*log(1/y) + a*log(x), x, y) == 0 assert homogeneous_order(f(x).diff(x), x, y) is None assert homogeneous_order(-f(x).diff(x) + x, x, y) is None assert homogeneous_order(O(x), x, y) is None assert homogeneous_order(x + O(x**2), x, y) is None assert homogeneous_order(x**pi, x) == pi assert homogeneous_order(x**x, x) is None raises(ValueError, lambda: homogeneous_order(x*y)) def test_1st_homogeneous_coeff_ode(): # Type: First order homogeneous, y'=f(y/x) eq1 = f(x)/x*cos(f(x)/x) - (x/f(x)*sin(f(x)/x) + cos(f(x)/x))*f(x).diff(x) eq2 = x*f(x).diff(x) - f(x) - x*sin(f(x)/x) eq3 = f(x) + (x*log(f(x)/x) - 2*x)*diff(f(x), x) eq4 = 2*f(x)*exp(x/f(x)) + f(x)*f(x).diff(x) - 2*x*exp(x/f(x))*f(x).diff(x) eq5 = 2*x**2*f(x) + f(x)**3 + (x*f(x)**2 - 2*x**3)*f(x).diff(x) eq6 = x*exp(f(x)/x) - f(x)*sin(f(x)/x) + x*sin(f(x)/x)*f(x).diff(x) eq7 = (x + sqrt(f(x)**2 - x*f(x)))*f(x).diff(x) - f(x) eq8 = x + f(x) - (x - f(x))*f(x).diff(x) sol1 = Eq(log(x), C1 - log(f(x)*sin(f(x)/x)/x)) sol2 = Eq(log(x), log(C1) + log(cos(f(x)/x) - 1)/2 - log(cos(f(x)/x) + 1)/2) sol3 = Eq(f(x), C1*LambertW(C2*x)) # Eq(f(x), x*exp(-LambertW(C1*x) + 1)) sol4 = Eq(log(f(x)), C1 - 2*exp(x/f(x))) sol5 = Eq(f(x), C1*exp(LambertW(C2*x**4)/2)/x) sol6 = Eq(log(x), C1 + exp(-f(x)/x)*sin(f(x)/x)/2 + exp(-f(x)/x)*cos(f(x)/x)/2) sol7 = Eq(log(f(x)), C1 - 2*sqrt(-x/f(x) + 1)) sol8 = Eq(log(x), C1 - log(sqrt(1 + f(x)**2/x**2)) + atan(f(x)/x)) assert dsolve(eq1, hint='1st_homogeneous_coeff_subs_dep_div_indep') == \ sol1 # indep_div_dep actually has a simpler solution for eq2, # but it runs too slow assert dsolve(eq2, hint='1st_homogeneous_coeff_subs_dep_div_indep', simplify=False) == sol2 assert dsolve(eq3, hint='1st_homogeneous_coeff_best') == sol3 assert dsolve(eq4, hint='1st_homogeneous_coeff_best') == sol4 assert dsolve(eq5, hint='1st_homogeneous_coeff_best') == sol5 assert dsolve(eq6, hint='1st_homogeneous_coeff_subs_dep_div_indep') == \ sol6 assert dsolve(eq7, hint='1st_homogeneous_coeff_best') == sol7 assert dsolve(eq8, hint='1st_homogeneous_coeff_best') == sol8 # checks are below @slow def test_1st_homogeneous_coeff_ode_check134568(): # These are the checkodesols from test_homogeneous_coeff_ode1. eq1 = f(x)/x*cos(f(x)/x) - (x/f(x)*sin(f(x)/x) + cos(f(x)/x))*f(x).diff(x) eq3 = f(x) + (x*log(f(x)/x) - 2*x)*diff(f(x), x) eq4 = 2*f(x)*exp(x/f(x)) + f(x)*f(x).diff(x) - 2*x*exp(x/f(x))*f(x).diff(x) eq5 = 2*x**2*f(x) + f(x)**3 + (x*f(x)**2 - 2*x**3)*f(x).diff(x) eq6 = x*exp(f(x)/x) - f(x)*sin(f(x)/x) + x*sin(f(x)/x)*f(x).diff(x) eq8 = x + f(x) - (x - f(x))*f(x).diff(x) sol1 = Eq(f(x)*sin(f(x)/x), C1) sol4 = Eq(log(C1*f(x)) + 2*exp(x/f(x)), 0) sol3 = Eq(-f(x)/(1 + log(x/f(x))), C1) sol5 = Eq(log(C1*x*sqrt(1/x)*sqrt(f(x))) + x**2/(2*f(x)**2), 0) sol6 = Eq(-exp(-f(x)/x)*sin(f(x)/x)/2 + log(C1*x) - cos(f(x)/x)*exp(-f(x)/x)/2, 0) sol8 = Eq(-atan(f(x)/x) + log(C1*x*sqrt(1 + f(x)**2/x**2)), 0) assert checkodesol(eq1, sol1, order=1, solve_for_func=False)[0] assert checkodesol(eq3, sol3, order=1, solve_for_func=False)[0] assert checkodesol(eq4, sol4, order=1, solve_for_func=False)[0] assert checkodesol(eq5, sol5, order=1, solve_for_func=False)[0] assert checkodesol(eq6, sol6, order=1, solve_for_func=False)[0] assert checkodesol(eq8, sol8, order=1, solve_for_func=False)[0] def test_1st_homogeneous_coeff_ode_check2(): eq2 = x*f(x).diff(x) - f(x) - x*sin(f(x)/x) sol2 = Eq(x/tan(f(x)/(2*x)), C1) assert checkodesol(eq2, sol2, order=1, solve_for_func=False)[0] @XFAIL def test_1st_homogeneous_coeff_ode_check3(): skip('This is a known issue.') # checker cannot determine that the following expression is zero: # (False, # x*(log(exp(-LambertW(C1*x))) + # LambertW(C1*x))*exp(-LambertW(C1*x) + 1)) # This is blocked by issue 1981. eq3 = f(x) + (x*log(f(x)/x) - 2*x)*diff(f(x), x) sol3a = Eq(f(x), x*exp(1 - LambertW(C1*x))) assert checkodesol(eq3, sol3a, solve_for_func=True)[0] # Checker can't verify this form either # (False, # C1*(log(C1*LambertW(C2*x)/x) + LambertW(C2*x) - 1)*LambertW(C2*x)) # It is because a = W(a)*exp(W(a)), so log(a) == log(W(a)) + W(a) and C2 = # -E/C1 (which can be verified by solving with simplify=False). sol3b = Eq(f(x), C1*LambertW(C2*x)) assert checkodesol(eq3, sol3b, solve_for_func=True)[0] def test_1st_homogeneous_coeff_ode_check7(): eq7 = (x + sqrt(f(x)**2 - x*f(x)))*f(x).diff(x) - f(x) sol7 = Eq(log(C1*f(x)) + 2*sqrt(1 - x/f(x)), 0) assert checkodesol(eq7, sol7, order=1, solve_for_func=False)[0] def test_1st_homogeneous_coeff_ode2(): eq1 = f(x).diff(x) - f(x)/x + 1/sin(f(x)/x) eq2 = x**2 + f(x)**2 - 2*x*f(x)*f(x).diff(x) eq3 = x*exp(f(x)/x) + f(x) - x*f(x).diff(x) sol1 = [Eq(f(x), x*(-acos(C1 + log(x)) + 2*pi)), Eq(f(x), x*acos(C1 + log(x)))] sol2 = Eq(log(f(x)), log(C1) + log(x/f(x)) - log(x**2/f(x)**2 - 1)) sol3 = Eq(f(x), log((1/(C1 - log(x)))**x)) # specific hints are applied for speed reasons assert dsolve(eq1, hint='1st_homogeneous_coeff_subs_dep_div_indep') == sol1 assert dsolve(eq2, hint='1st_homogeneous_coeff_best', simplify=False) == sol2 assert dsolve(eq3, hint='1st_homogeneous_coeff_subs_dep_div_indep') == sol3 assert checkodesol(eq1, sol1, order=1, solve_for_func=False)[0] assert checkodesol(eq2, sol2, order=1, solve_for_func=False)[0] # test for eq3 is in test_1st_homogeneous_coeff_ode2_check3 below def test_1st_homogeneous_coeff_ode2_check3(): eq3 = x*exp(f(x)/x) + f(x) - x*f(x).diff(x) sol3 = Eq(f(x), log(log(C1/x)**(-x))) assert checkodesol(eq3, sol3, order=1, solve_for_func=False)[0] def test_1st_homogeneous_coeff_ode_check9(): _u2 = Dummy('u2') __a = Dummy('a') eq9 = f(x)**2 + (x*sqrt(f(x)**2 - x**2) - x*f(x))*f(x).diff(x) sol9 = Eq(-Integral(-1/(-(1 - sqrt(1 - _u2**2))*_u2 + _u2), (_u2, __a, x/f(x))) + log(C1*f(x)), 0) assert checkodesol(eq9, sol9, order=1, solve_for_func=False)[0] def test_1st_homogeneous_coeff_ode3(): # The standard integration engine cannot handle one of the integrals # involved (see issue 1452). meijerg code comes up with an answer, but in # unconventional form. # checkodesol fails for this equation, so its test is in # test_1st_homogeneous_coeff_ode_check9 above. It has to compare string # expressions because u2 is a dummy variable. eq = f(x)**2 + (x*sqrt(f(x)**2 - x**2) - x*f(x))*f(x).diff(x) sol = Eq(log(f(x)), C1 - Piecewise( (-acosh(f(x)/x), abs(f(x)**2)/x**2 > 1), (I*asin(f(x)/x), True))) assert dsolve(eq, hint='1st_homogeneous_coeff_subs_indep_div_dep') == sol def test_1st_homogeneous_coeff_corner_case(): eq1 = f(x).diff(x) - f(x)/x c1 = classify_ode(eq1, f(x)) eq2 = x*f(x).diff(x) - f(x) c2 = classify_ode(eq2, f(x)) sdi = "1st_homogeneous_coeff_subs_dep_div_indep" sid = "1st_homogeneous_coeff_subs_indep_div_dep" assert sid not in c1 and sdi not in c1 assert sid not in c2 and sdi not in c2 def test_nth_linear_constant_coeff_homogeneous(): # From Exercise 20, in Ordinary Differential Equations, # Tenenbaum and Pollard, pg. 220 a = Symbol('a', positive=True) k = Symbol('k', real=True) eq1 = f(x).diff(x, 2) + 2*f(x).diff(x) eq2 = f(x).diff(x, 2) - 3*f(x).diff(x) + 2*f(x) eq3 = f(x).diff(x, 2) - f(x) eq4 = f(x).diff(x, 3) + f(x).diff(x, 2) - 6*f(x).diff(x) eq5 = 6*f(x).diff(x, 2) - 11*f(x).diff(x) + 4*f(x) eq6 = Eq(f(x).diff(x, 2) + 2*f(x).diff(x) - f(x), 0) eq7 = diff(f(x), x, 3) + diff(f(x), x, 2) - 10*diff(f(x), x) - 6*f(x) eq8 = f(x).diff(x, 4) - f(x).diff(x, 3) - 4*f(x).diff(x, 2) + \ 4*f(x).diff(x) eq9 = f(x).diff(x, 4) + 4*f(x).diff(x, 3) + f(x).diff(x, 2) - \ 4*f(x).diff(x) - 2*f(x) eq10 = f(x).diff(x, 4) - a**2*f(x) eq11 = f(x).diff(x, 2) - 2*k*f(x).diff(x) - 2*f(x) eq12 = f(x).diff(x, 2) + 4*k*f(x).diff(x) - 12*k**2*f(x) eq13 = f(x).diff(x, 4) eq14 = f(x).diff(x, 2) + 4*f(x).diff(x) + 4*f(x) eq15 = 3*f(x).diff(x, 3) + 5*f(x).diff(x, 2) + f(x).diff(x) - f(x) eq16 = f(x).diff(x, 3) - 6*f(x).diff(x, 2) + 12*f(x).diff(x) - 8*f(x) eq17 = f(x).diff(x, 2) - 2*a*f(x).diff(x) + a**2*f(x) eq18 = f(x).diff(x, 4) + 3*f(x).diff(x, 3) eq19 = f(x).diff(x, 4) - 2*f(x).diff(x, 2) eq20 = f(x).diff(x, 4) + 2*f(x).diff(x, 3) - 11*f(x).diff(x, 2) - \ 12*f(x).diff(x) + 36*f(x) eq21 = 36*f(x).diff(x, 4) - 37*f(x).diff(x, 2) + 4*f(x).diff(x) + 5*f(x) eq22 = f(x).diff(x, 4) - 8*f(x).diff(x, 2) + 16*f(x) eq23 = f(x).diff(x, 2) - 2*f(x).diff(x) + 5*f(x) eq24 = f(x).diff(x, 2) - f(x).diff(x) + f(x) eq25 = f(x).diff(x, 4) + 5*f(x).diff(x, 2) + 6*f(x) eq26 = f(x).diff(x, 2) - 4*f(x).diff(x) + 20*f(x) eq27 = f(x).diff(x, 4) + 4*f(x).diff(x, 2) + 4*f(x) eq28 = f(x).diff(x, 3) + 8*f(x) eq29 = f(x).diff(x, 4) + 4*f(x).diff(x, 2) eq30 = f(x).diff(x, 5) + 2*f(x).diff(x, 3) + f(x).diff(x) sol1 = Eq(f(x), C1 + C2*exp(-2*x)) sol2 = Eq(f(x), (C1*exp(x) + C2*exp(2*x))) sol3 = Eq(f(x), C1*exp(x) + C2*exp(-x)) sol4 = Eq(f(x), C1 + C2*exp(-3*x) + C3*exp(2*x)) sol5 = Eq(f(x), C1*exp(x/2) + C2*exp(4*x/3)) sol6 = Eq(f(x), C1*exp(x*(-1 + sqrt(2))) + C2*exp(x*(-sqrt(2) - 1))) sol7 = Eq(f(x), C1*exp(3*x) + C2*exp(x*(-2 - sqrt(2))) + C3*exp(x*(-2 + sqrt(2)))) sol8 = Eq(f(x), C1 + C2*exp(x) + C3*exp(-2*x) + C4*exp(2*x)) sol9 = Eq(f(x), C1*exp(x) + C2*exp(-x) + C3*exp(x*(-2 + sqrt(2))) + C4*exp(x*(-2 - sqrt(2)))) sol10 = Eq(f(x), C1*sin(x*sqrt(a)) + C2*cos(x*sqrt(a)) + C3*exp(x*sqrt(a)) + C4*exp(-x*sqrt(a))) sol11 = Eq(f(x), C1*exp(x*(k - sqrt(k**2 + 2))) + C2*exp(x*(k + sqrt(k**2 + 2)))) sol12 = Eq(f(x), C1*exp(2*x*(-2*abs(k) - k)) + C2*exp(2*x*(2*abs(k) - k))) sol13 = Eq(f(x), C1 + C2*x + C3*x**2 + C4*x**3) sol14 = Eq(f(x), (C1 + C2*x)*exp(-2*x)) sol15 = Eq(f(x), (C1 + C2*x)*exp(-x) + C3*exp(x/3)) sol16 = Eq(f(x), (C1 + C2*x + C3*x**2)*exp(2*x)) sol17 = Eq(f(x), (C1 + C2*x)*exp(a*x)) sol18 = Eq(f(x), C1 + C2*x + C3*x**2 + C4*exp(-3*x)) sol19 = Eq(f(x), C1 + C2*x + C3*exp(x*sqrt(2)) + C4*exp(-x*sqrt(2))) sol20 = Eq(f(x), (C1 + C2*x)*exp(-3*x) + (C3 + C4*x)*exp(2*x)) sol21 = Eq(f(x), C1*exp(x/2) + C2*exp(-x) + C3*exp(-x/3) + C4*exp(5*x/6)) sol22 = Eq(f(x), (C1 + C2*x)*exp(-2*x) + (C3 + C4*x)*exp(2*x)) sol23 = Eq(f(x), (C1*sin(2*x) + C2*cos(2*x))*exp(x)) sol24 = Eq(f(x), (C1*sin(x*sqrt(3)/2) + C2*cos(x*sqrt(3)/2))*exp(x/2)) sol25 = Eq(f(x), C1*cos(x*sqrt(3)) + C2*sin(x*sqrt(3)) + C3*sin(x*sqrt(2)) + C4*cos(x*sqrt(2))) sol26 = Eq(f(x), (C1*sin(4*x) + C2*cos(4*x))*exp(2*x)) sol27 = Eq(f(x), (C1 + C2*x)*sin(x*sqrt(2)) + (C3 + C4*x)*cos(x*sqrt(2))) sol28 = Eq(f(x), (C1*sin(x*sqrt(3)) + C2*cos(x*sqrt(3)))*exp(x) + C3*exp(-2*x)) sol29 = Eq(f(x), C1 + C2*sin(2*x) + C3*cos(2*x) + C4*x) sol30 = Eq(f(x), C1 + (C2 + C3*x)*sin(x) + (C4 + C5*x)*cos(x)) sol1s = constant_renumber(sol1, 'C', 1, 2) sol2s = constant_renumber(sol2, 'C', 1, 2) sol3s = constant_renumber(sol3, 'C', 1, 2) sol4s = constant_renumber(sol4, 'C', 1, 3) sol5s = constant_renumber(sol5, 'C', 1, 2) sol6s = constant_renumber(sol6, 'C', 1, 2) sol7s = constant_renumber(sol7, 'C', 1, 3) sol8s = constant_renumber(sol8, 'C', 1, 4) sol9s = constant_renumber(sol9, 'C', 1, 4) sol10s = constant_renumber(sol10, 'C', 1, 4) sol11s = constant_renumber(sol11, 'C', 1, 2) sol12s = constant_renumber(sol12, 'C', 1, 2) sol13s = constant_renumber(sol13, 'C', 1, 4) sol14s = constant_renumber(sol14, 'C', 1, 2) sol15s = constant_renumber(sol15, 'C', 1, 3) sol16s = constant_renumber(sol16, 'C', 1, 3) sol17s = constant_renumber(sol17, 'C', 1, 2) sol18s = constant_renumber(sol18, 'C', 1, 4) sol19s = constant_renumber(sol19, 'C', 1, 4) sol20s = constant_renumber(sol20, 'C', 1, 4) sol21s = constant_renumber(sol21, 'C', 1, 4) sol22s = constant_renumber(sol22, 'C', 1, 4) sol23s = constant_renumber(sol23, 'C', 1, 2) sol24s = constant_renumber(sol24, 'C', 1, 2) sol25s = constant_renumber(sol25, 'C', 1, 4) sol26s = constant_renumber(sol26, 'C', 1, 2) sol27s = constant_renumber(sol27, 'C', 1, 4) sol28s = constant_renumber(sol28, 'C', 1, 3) sol29s = constant_renumber(sol29, 'C', 1, 4) sol30s = constant_renumber(sol30, 'C', 1, 5) assert dsolve(eq1) in (sol1, sol1s) assert dsolve(eq2) in (sol2, sol2s) assert dsolve(eq3) in (sol3, sol3s) assert dsolve(eq4) in (sol4, sol4s) assert dsolve(eq5) in (sol5, sol5s) assert dsolve(eq6) in (sol6, sol6s) assert dsolve(eq7) in (sol7, sol7s) assert dsolve(eq8) in (sol8, sol8s) assert dsolve(eq9) in (sol9, sol9s) assert dsolve(eq10) in (sol10, sol10s) assert dsolve(eq11) in (sol11, sol11s) assert dsolve(eq12) in (sol12, sol12s) assert dsolve(eq13) in (sol13, sol13s) assert dsolve(eq14) in (sol14, sol14s) assert dsolve(eq15) in (sol15, sol15s) assert dsolve(eq16) in (sol16, sol16s) assert dsolve(eq17) in (sol17, sol17s) assert dsolve(eq18) in (sol18, sol18s) assert dsolve(eq19) in (sol19, sol19s) assert dsolve(eq20) in (sol20, sol20s) assert dsolve(eq21) in (sol21, sol21s) assert dsolve(eq22) in (sol22, sol22s) assert dsolve(eq23) in (sol23, sol23s) assert dsolve(eq24) in (sol24, sol24s) assert dsolve(eq25) in (sol25, sol25s) assert dsolve(eq26) in (sol26, sol26s) assert dsolve(eq27) in (sol27, sol27s) assert dsolve(eq28) in (sol28, sol28s) assert dsolve(eq29) in (sol29, sol29s) assert dsolve(eq30) in (sol30, sol30s) assert checkodesol(eq1, sol1, order=2, solve_for_func=False)[0] assert checkodesol(eq2, sol2, order=2, solve_for_func=False)[0] assert checkodesol(eq3, sol3, order=2, solve_for_func=False)[0] assert checkodesol(eq4, sol4, order=3, solve_for_func=False)[0] assert checkodesol(eq5, sol5, order=2, solve_for_func=False)[0] assert checkodesol(eq6, sol6, order=2, solve_for_func=False)[0] assert checkodesol(eq7, sol7, order=3, solve_for_func=False)[0] assert checkodesol(eq8, sol8, order=4, solve_for_func=False)[0] assert checkodesol(eq9, sol9, order=4, solve_for_func=False)[0] assert checkodesol(eq10, sol10, order=4, solve_for_func=False)[0] assert checkodesol(eq11, sol11, order=2, solve_for_func=False)[0] assert checkodesol(eq12, sol12, order=2, solve_for_func=False)[0] assert checkodesol(eq13, sol13, order=4, solve_for_func=False)[0] assert checkodesol(eq14, sol14, order=2, solve_for_func=False)[0] assert checkodesol(eq15, sol15, order=3, solve_for_func=False)[0] assert checkodesol(eq16, sol16, order=3, solve_for_func=False)[0] assert checkodesol(eq17, sol17, order=2, solve_for_func=False)[0] assert checkodesol(eq18, sol18, order=4, solve_for_func=False)[0] assert checkodesol(eq19, sol19, order=4, solve_for_func=False)[0] assert checkodesol(eq20, sol20, order=4, solve_for_func=False)[0] assert checkodesol(eq21, sol21, order=4, solve_for_func=False)[0] assert checkodesol(eq22, sol22, order=4, solve_for_func=False)[0] assert checkodesol(eq23, sol23, order=2, solve_for_func=False)[0] assert checkodesol(eq24, sol24, order=2, solve_for_func=False)[0] assert checkodesol(eq25, sol25, order=4, solve_for_func=False)[0] assert checkodesol(eq26, sol26, order=2, solve_for_func=False)[0] assert checkodesol(eq27, sol27, order=4, solve_for_func=False)[0] assert checkodesol(eq28, sol28, order=3, solve_for_func=False)[0] assert checkodesol(eq29, sol29, order=4, solve_for_func=False)[0] assert checkodesol(eq30, sol30, order=5, solve_for_func=False)[0] def test_nth_linear_constant_coeff_homogeneous_RootOf(): eq = f(x).diff(x, 5) + 11*f(x).diff(x) - 2*f(x) sol = Eq(f(x), C1*exp(x*RootOf(x**5 + 11*x - 2, 0)) + C2*exp(x*RootOf(x**5 + 11*x - 2, 1)) + C3*exp(x*RootOf(x**5 + 11*x - 2, 2)) + C4*exp(x*RootOf(x**5 + 11*x - 2, 3)) + C5*exp(x*RootOf(x**5 + 11*x - 2, 4))) assert dsolve(eq) == sol @XFAIL def test_nth_linear_constant_coeff_homogeneous_RootOf_sol(): eq = f(x).diff(x, 5) + 11*f(x).diff(x) - 2*f(x) sol = Eq(f(x), C1*exp(x*RootOf(x**5 + 11*x - 2, 0)) + C2*exp(x*RootOf(x**5 + 11*x - 2, 1)) + C3*exp(x*RootOf(x**5 + 11*x - 2, 2)) + C4*exp(x*RootOf(x**5 + 11*x - 2, 3)) + C5*exp(x*RootOf(x**5 + 11*x - 2, 4))) assert checkodesol(eq, sol, order=5, solve_for_func=False)[0] def test_undetermined_coefficients_match(): assert _undetermined_coefficients_match(g(x), x) == {'test': False} assert _undetermined_coefficients_match(sin(2*x + sqrt(5)), x) == \ {'test': True, 'trialset': set([cos(2*x + sqrt(5)), sin(2*x + sqrt(5))])} assert _undetermined_coefficients_match(sin(x)*cos(x), x) == \ {'test': False} s = set([cos(x), x*cos(x), x**2*cos(x), x**2*sin(x), x*sin(x), sin(x)]) assert _undetermined_coefficients_match(sin(x)*(x**2 + x + 1), x) == \ {'test': True, 'trialset': s} assert _undetermined_coefficients_match( sin(x)*x**2 + sin(x)*x + sin(x), x) == {'test': True, 'trialset': s} assert _undetermined_coefficients_match( exp(2*x)*sin(x)*(x**2 + x + 1), x ) == { 'test': True, 'trialset': set([exp(2*x)*sin(x), x**2*exp(2*x)*sin(x), cos(x)*exp(2*x), x**2*cos(x)*exp(2*x), x*cos(x)*exp(2*x), x*exp(2*x)*sin(x)])} assert _undetermined_coefficients_match(1/sin(x), x) == {'test': False} assert _undetermined_coefficients_match(log(x), x) == {'test': False} assert _undetermined_coefficients_match(2**(x)*(x**2 + x + 1), x) == \ {'test': True, 'trialset': set([2**x, x*2**x, x**2*2**x])} assert _undetermined_coefficients_match(x**y, x) == {'test': False} assert _undetermined_coefficients_match(exp(x)*exp(2*x + 1), x) == \ {'test': True, 'trialset': set([exp(1 + 3*x)])} assert _undetermined_coefficients_match(sin(x)*(x**2 + x + 1), x) == \ {'test': True, 'trialset': set([x*cos(x), x*sin(x), x**2*cos(x), x**2*sin(x), cos(x), sin(x)])} assert _undetermined_coefficients_match(sin(x)*(x + sin(x)), x) == \ {'test': False} assert _undetermined_coefficients_match(sin(x)*(x + sin(2*x)), x) == \ {'test': False} assert _undetermined_coefficients_match(sin(x)*tan(x), x) == \ {'test': False} assert _undetermined_coefficients_match( x**2*sin(x)*exp(x) + x*sin(x) + x, x ) == { 'test': True, 'trialset': set([x**2*cos(x)*exp(x), x, cos(x), S(1), exp(x)*sin(x), sin(x), x*exp(x)*sin(x), x*cos(x), x*cos(x)*exp(x), x*sin(x), cos(x)*exp(x), x**2*exp(x)*sin(x)])} assert _undetermined_coefficients_match(4*x*sin(x - 2), x) == { 'trialset': set([x*cos(x - 2), x*sin(x - 2), cos(x - 2), sin(x - 2)]), 'test': True, } assert _undetermined_coefficients_match(2**x*x, x) == \ {'test': True, 'trialset': set([2**x, x*2**x])} assert _undetermined_coefficients_match(2**x*exp(2*x), x) == \ {'test': True, 'trialset': set([2**x*exp(2*x)])} assert _undetermined_coefficients_match(exp(-x)/x, x) == \ {'test': False} # Below are from Ordinary Differential Equations, # Tenenbaum and Pollard, pg. 231 assert _undetermined_coefficients_match(S(4), x) == \ {'test': True, 'trialset': set([S(1)])} assert _undetermined_coefficients_match(12*exp(x), x) == \ {'test': True, 'trialset': set([exp(x)])} assert _undetermined_coefficients_match(exp(I*x), x) == \ {'test': True, 'trialset': set([exp(I*x)])} assert _undetermined_coefficients_match(sin(x), x) == \ {'test': True, 'trialset': set([cos(x), sin(x)])} assert _undetermined_coefficients_match(cos(x), x) == \ {'test': True, 'trialset': set([cos(x), sin(x)])} assert _undetermined_coefficients_match(8 + 6*exp(x) + 2*sin(x), x) == \ {'test': True, 'trialset': set([S(1), cos(x), sin(x), exp(x)])} assert _undetermined_coefficients_match(x**2, x) == \ {'test': True, 'trialset': set([S(1), x, x**2])} assert _undetermined_coefficients_match(9*x*exp(x) + exp(-x), x) == \ {'test': True, 'trialset': set([x*exp(x), exp(x), exp(-x)])} assert _undetermined_coefficients_match(2*exp(2*x)*sin(x), x) == \ {'test': True, 'trialset': set([exp(2*x)*sin(x), cos(x)*exp(2*x)])} assert _undetermined_coefficients_match(x - sin(x), x) == \ {'test': True, 'trialset': set([S(1), x, cos(x), sin(x)])} assert _undetermined_coefficients_match(x**2 + 2*x, x) == \ {'test': True, 'trialset': set([S(1), x, x**2])} assert _undetermined_coefficients_match(4*x*sin(x), x) == \ {'test': True, 'trialset': set([x*cos(x), x*sin(x), cos(x), sin(x)])} assert _undetermined_coefficients_match(x*sin(2*x), x) == \ {'test': True, 'trialset': set([x*cos(2*x), x*sin(2*x), cos(2*x), sin(2*x)])} assert _undetermined_coefficients_match(x**2*exp(-x), x) == \ {'test': True, 'trialset': set([x*exp(-x), x**2*exp(-x), exp(-x)])} assert _undetermined_coefficients_match(2*exp(-x) - x**2*exp(-x), x) == \ {'test': True, 'trialset': set([x*exp(-x), x**2*exp(-x), exp(-x)])} assert _undetermined_coefficients_match(exp(-2*x) + x**2, x) == \ {'test': True, 'trialset': set([S(1), x, x**2, exp(-2*x)])} assert _undetermined_coefficients_match(x*exp(-x), x) == \ {'test': True, 'trialset': set([x*exp(-x), exp(-x)])} assert _undetermined_coefficients_match(x + exp(2*x), x) == \ {'test': True, 'trialset': set([S(1), x, exp(2*x)])} assert _undetermined_coefficients_match(sin(x) + exp(-x), x) == \ {'test': True, 'trialset': set([cos(x), sin(x), exp(-x)])} assert _undetermined_coefficients_match(exp(x), x) == \ {'test': True, 'trialset': set([exp(x)])} # converted from sin(x)**2 assert _undetermined_coefficients_match(S(1)/2 - cos(2*x)/2, x) == \ {'test': True, 'trialset': set([S(1), cos(2*x), sin(2*x)])} # converted from exp(2*x)*sin(x)**2 assert _undetermined_coefficients_match( exp(2*x)*(S(1)/2 + cos(2*x)/2), x ) == { 'test': True, 'trialset': set([exp(2*x)*sin(2*x), cos(2*x)*exp(2*x), exp(2*x)])} assert _undetermined_coefficients_match(2*x + sin(x) + cos(x), x) == \ {'test': True, 'trialset': set([S(1), x, cos(x), sin(x)])} # converted from sin(2*x)*sin(x) assert _undetermined_coefficients_match(cos(x)/2 - cos(3*x)/2, x) == \ {'test': True, 'trialset': set([cos(x), cos(3*x), sin(x), sin(3*x)])} assert _undetermined_coefficients_match(cos(x**2), x) == {'test': False} assert _undetermined_coefficients_match(2**(x**2), x) == {'test': False} def test_nth_linear_constant_coeff_undetermined_coefficients(): hint = 'nth_linear_constant_coeff_undetermined_coefficients' g = exp(-x) f2 = f(x).diff(x, 2) c = 3*f(x).diff(x, 3) + 5*f2 + f(x).diff(x) - f(x) - x eq1 = c - x*g eq2 = c - g # 3-27 below are from Ordinary Differential Equations, # Tenenbaum and Pollard, pg. 231 eq3 = f2 + 3*f(x).diff(x) + 2*f(x) - 4 eq4 = f2 + 3*f(x).diff(x) + 2*f(x) - 12*exp(x) eq5 = f2 + 3*f(x).diff(x) + 2*f(x) - exp(I*x) eq6 = f2 + 3*f(x).diff(x) + 2*f(x) - sin(x) eq7 = f2 + 3*f(x).diff(x) + 2*f(x) - cos(x) eq8 = f2 + 3*f(x).diff(x) + 2*f(x) - (8 + 6*exp(x) + 2*sin(x)) eq9 = f2 + f(x).diff(x) + f(x) - x**2 eq10 = f2 - 2*f(x).diff(x) - 8*f(x) - 9*x*exp(x) - 10*exp(-x) eq11 = f2 - 3*f(x).diff(x) - 2*exp(2*x)*sin(x) eq12 = f(x).diff(x, 4) - 2*f2 + f(x) - x + sin(x) eq13 = f2 + f(x).diff(x) - x**2 - 2*x eq14 = f2 + f(x).diff(x) - x - sin(2*x) eq15 = f2 + f(x) - 4*x*sin(x) eq16 = f2 + 4*f(x) - x*sin(2*x) eq17 = f2 + 2*f(x).diff(x) + f(x) - x**2*exp(-x) eq18 = f(x).diff(x, 3) + 3*f2 + 3*f(x).diff(x) + f(x) - 2*exp(-x) + \ x**2*exp(-x) eq19 = f2 + 3*f(x).diff(x) + 2*f(x) - exp(-2*x) - x**2 eq20 = f2 - 3*f(x).diff(x) + 2*f(x) - x*exp(-x) eq21 = f2 + f(x).diff(x) - 6*f(x) - x - exp(2*x) eq22 = f2 + f(x) - sin(x) - exp(-x) eq23 = f(x).diff(x, 3) - 3*f2 + 3*f(x).diff(x) - f(x) - exp(x) # sin(x)**2 eq24 = f2 + f(x) - S(1)/2 - cos(2*x)/2 # exp(2*x)*sin(x)**2 eq25 = f(x).diff(x, 3) - f(x).diff(x) - exp(2*x)*(S(1)/2 - cos(2*x)/2) eq26 = (f(x).diff(x, 5) + 2*f(x).diff(x, 3) + f(x).diff(x) - 2*x - sin(x) - cos(x)) # sin(2*x)*sin(x), skip 3127 for now, match bug eq27 = f2 + f(x) - cos(x)/2 + cos(3*x)/2 eq28 = f(x).diff(x) - 1 sol1 = Eq(f(x), -1 - x + (C1 + C2*x - 3*x**2/32 - x**3/24)*exp(-x) + C3*exp(x/3)) sol2 = Eq(f(x), -1 - x + (C1 + C2*x - x**2/8)*exp(-x) + C3*exp(x/3)) sol3 = Eq(f(x), 2 + C1*exp(-x) + C2*exp(-2*x)) sol4 = Eq(f(x), 2*exp(x) + C1*exp(-x) + C2*exp(-2*x)) sol5 = Eq(f(x), C1*exp(-x) + C2*exp(-2*x) + (S(1)/10 - 3*I/10)*exp(I*x)) sol6 = Eq(f(x), -3*cos(x)/10 + sin(x)/10 + C1*exp(-x) + C2*exp(-2*x)) sol7 = Eq(f(x), cos(x)/10 + 3*sin(x)/10 + C1*exp(-x) + C2*exp(-2*x)) sol8 = Eq(f(x), 4 - 3*cos(x)/5 + sin(x)/5 + exp(x) + C1*exp(-x) + C2*exp(-2*x)) sol9 = Eq(f(x), -2*x + x**2 + (C1*sin(x*sqrt(3)/2) + C2*cos(x*sqrt(3)/2))*exp(-x/2)) sol10 = Eq(f(x), -x*exp(x) - 2*exp(-x) + C1*exp(-2*x) + C2*exp(4*x)) sol11 = Eq(f(x), C1 + C2*exp(3*x) + (-3*sin(x) - cos(x))*exp(2*x)/5) sol12 = Eq(f(x), x - sin(x)/4 + (C1 + C2*x)*exp(-x) + (C3 + C4*x)*exp(x)) sol13 = Eq(f(x), C1 + x**3/3 + C2*exp(-x)) sol14 = Eq(f(x), C1 - x - sin(2*x)/5 - cos(2*x)/10 + x**2/2 + C2*exp(-x)) sol15 = Eq(f(x), (C1 + x)*sin(x) + (C2 - x**2)*cos(x)) sol16 = Eq(f(x), (C1 + x/16)*sin(2*x) + (C2 - x**2/8)*cos(2*x)) sol17 = Eq(f(x), (C1 + C2*x + x**4/12)*exp(-x)) sol18 = Eq(f(x), (C1 + C2*x + C3*x**2 - x**5/60 + x**3/3)*exp(-x)) sol19 = Eq(f(x), S(7)/4 - 3*x/2 + x**2/2 + C1*exp(-x) + (C2 - x)*exp(-2*x)) sol20 = Eq(f(x), C1*exp(x) + C2*exp(2*x) + (6*x + 5)*exp(-x)/36) sol21 = Eq(f(x), -S(1)/36 - x/6 + C1*exp(-3*x) + (C2 + x/5)*exp(2*x)) sol22 = Eq(f(x), C1*sin(x) + (C2 - x/2)*cos(x) + exp(-x)/2) sol23 = Eq(f(x), (C1 + C2*x + C3*x**2 + x**3/6)*exp(x)) sol24 = Eq(f(x), S(1)/2 - cos(2*x)/6 + C1*sin(x) + C2*cos(x)) sol25 = Eq(f(x), C1 + C2*exp(-x) + C3*exp(x) + (-21*sin(2*x) + 27*cos(2*x) + 130)*exp(2*x)/1560) sol26 = Eq(f(x), C1 + (C2 + C3*x - x**2/8)*sin(x) + (C4 + C5*x + x**2/8)*cos(x) + x**2) sol27 = Eq(f(x), cos(3*x)/16 + C1*cos(x) + (C2 + x/4)*sin(x)) sol28 = Eq(f(x), C1 + x) sol1s = constant_renumber(sol1, 'C', 1, 3) sol2s = constant_renumber(sol2, 'C', 1, 3) sol3s = constant_renumber(sol3, 'C', 1, 2) sol4s = constant_renumber(sol4, 'C', 1, 2) sol5s = constant_renumber(sol5, 'C', 1, 2) sol6s = constant_renumber(sol6, 'C', 1, 2) sol7s = constant_renumber(sol7, 'C', 1, 2) sol8s = constant_renumber(sol8, 'C', 1, 2) sol9s = constant_renumber(sol9, 'C', 1, 2) sol10s = constant_renumber(sol10, 'C', 1, 2) sol11s = constant_renumber(sol11, 'C', 1, 2) sol12s = constant_renumber(sol12, 'C', 1, 2) sol13s = constant_renumber(sol13, 'C', 1, 4) sol14s = constant_renumber(sol14, 'C', 1, 2) sol15s = constant_renumber(sol15, 'C', 1, 2) sol16s = constant_renumber(sol16, 'C', 1, 2) sol17s = constant_renumber(sol17, 'C', 1, 2) sol18s = constant_renumber(sol18, 'C', 1, 3) sol19s = constant_renumber(sol19, 'C', 1, 2) sol20s = constant_renumber(sol20, 'C', 1, 2) sol21s = constant_renumber(sol21, 'C', 1, 2) sol22s = constant_renumber(sol22, 'C', 1, 2) sol23s = constant_renumber(sol23, 'C', 1, 3) sol24s = constant_renumber(sol24, 'C', 1, 2) sol25s = constant_renumber(sol25, 'C', 1, 3) sol26s = constant_renumber(sol26, 'C', 1, 5) sol27s = constant_renumber(sol27, 'C', 1, 2) assert dsolve(eq1, hint=hint) in (sol1, sol1s) assert dsolve(eq2, hint=hint) in (sol2, sol2s) assert dsolve(eq3, hint=hint) in (sol3, sol3s) assert dsolve(eq4, hint=hint) in (sol4, sol4s) assert dsolve(eq5, hint=hint) in (sol5, sol5s) assert dsolve(eq6, hint=hint) in (sol6, sol6s) assert dsolve(eq7, hint=hint) in (sol7, sol7s) assert dsolve(eq8, hint=hint) in (sol8, sol8s) assert dsolve(eq9, hint=hint) in (sol9, sol9s) assert dsolve(eq10, hint=hint) in (sol10, sol10s) assert dsolve(eq11, hint=hint) in (sol11, sol11s) assert dsolve(eq12, hint=hint) in (sol12, sol12s) assert dsolve(eq13, hint=hint) in (sol13, sol13s) assert dsolve(eq14, hint=hint) in (sol14, sol14s) assert dsolve(eq15, hint=hint) in (sol15, sol15s) assert dsolve(eq16, hint=hint) in (sol16, sol16s) assert dsolve(eq17, hint=hint) in (sol17, sol17s) assert dsolve(eq18, hint=hint) in (sol18, sol18s) assert dsolve(eq19, hint=hint) in (sol19, sol19s) assert dsolve(eq20, hint=hint) in (sol20, sol20s) assert dsolve(eq21, hint=hint) in (sol21, sol21s) assert dsolve(eq22, hint=hint) in (sol22, sol22s) assert dsolve(eq23, hint=hint) in (sol23, sol23s) assert dsolve(eq24, hint=hint) in (sol24, sol24s) assert dsolve(eq25, hint=hint) in (sol25, sol25s) assert dsolve(eq26, hint=hint) in (sol26, sol26s) assert dsolve(eq27, hint=hint) in (sol27, sol27s) assert dsolve(eq28, hint=hint) == sol28 assert checkodesol(eq1, sol1, order=3, solve_for_func=False)[0] assert checkodesol(eq2, sol2, order=3, solve_for_func=False)[0] assert checkodesol(eq3, sol3, order=2, solve_for_func=False)[0] assert checkodesol(eq4, sol4, order=2, solve_for_func=False)[0] assert checkodesol(eq5, sol5, order=2, solve_for_func=False)[0] assert checkodesol(eq6, sol6, order=2, solve_for_func=False)[0] assert checkodesol(eq7, sol7, order=2, solve_for_func=False)[0] assert checkodesol(eq8, sol8, order=2, solve_for_func=False)[0] assert checkodesol(eq9, sol9, order=2, solve_for_func=False)[0] assert checkodesol(eq10, sol10, order=2, solve_for_func=False)[0] assert checkodesol(eq11, sol11, order=2, solve_for_func=False)[0] assert checkodesol(eq12, sol12, order=4, solve_for_func=False)[0] assert checkodesol(eq13, sol13, order=2, solve_for_func=False)[0] assert checkodesol(eq14, sol14, order=2, solve_for_func=False)[0] assert checkodesol(eq15, sol15, order=2, solve_for_func=False)[0] assert checkodesol(eq16, sol16, order=2, solve_for_func=False)[0] assert checkodesol(eq17, sol17, order=2, solve_for_func=False)[0] assert checkodesol(eq18, sol18, order=3, solve_for_func=False)[0] assert checkodesol(eq19, sol19, order=2, solve_for_func=False)[0] assert checkodesol(eq20, sol20, order=2, solve_for_func=False)[0] assert checkodesol(eq21, sol21, order=2, solve_for_func=False)[0] assert checkodesol(eq22, sol22, order=2, solve_for_func=False)[0] assert checkodesol(eq23, sol23, order=3, solve_for_func=False)[0] assert checkodesol(eq24, sol24, order=2, solve_for_func=False)[0] assert checkodesol(eq25, sol25, order=3, solve_for_func=False)[0] assert checkodesol(eq26, sol26, order=5, solve_for_func=False)[0] assert checkodesol(eq27, sol27, order=2, solve_for_func=False)[0] assert checkodesol(eq28, sol28, order=1, solve_for_func=False)[0] @XFAIL def test_nth_linear_constant_coeff_undetermined_coefficients_imaginary_exp(): # Equivalent to eq26 in # test_nth_linear_constant_coeff_undetermined_coefficients above. # This fails because the algorithm for undetermined coefficients # doesn't know to multiply exp(I*x) by sufficient x because it is linearly # dependent on sin(x) and cos(x). hint = 'nth_linear_constant_coeff_undetermined_coefficients' eq26a = f(x).diff(x, 5) + 2*f(x).diff(x, 3) + f(x).diff(x) - 2*x - exp(I*x) sol26 = Eq(f(x), C1 + (C2 + C3*x - x**2/8)*sin(x) + (C4 + C5*x + x**2/8)*cos(x) + x**2) assert dsolve(eq26a, hint=hint) == sol26 assert checkodesol(eq26a, sol26, order=5, solve_for_func=False)[0] def test_nth_linear_constant_coeff_variation_of_parameters(): hint = 'nth_linear_constant_coeff_variation_of_parameters' g = exp(-x) f2 = f(x).diff(x, 2) c = 3*f(x).diff(x, 3) + 5*f2 + f(x).diff(x) - f(x) - x eq1 = c - x*g eq2 = c - g eq3 = f(x).diff(x) - 1 eq4 = f2 + 3*f(x).diff(x) + 2*f(x) - 4 eq5 = f2 + 3*f(x).diff(x) + 2*f(x) - 12*exp(x) eq6 = f2 - 2*f(x).diff(x) - 8*f(x) - 9*x*exp(x) - 10*exp(-x) eq7 = f2 + 2*f(x).diff(x) + f(x) - x**2*exp(-x) eq8 = f2 - 3*f(x).diff(x) + 2*f(x) - x*exp(-x) eq9 = f(x).diff(x, 3) - 3*f2 + 3*f(x).diff(x) - f(x) - exp(x) eq10 = f2 + 2*f(x).diff(x) + f(x) - exp(-x)/x eq11 = f2 + f(x) - 1/sin(x)*1/cos(x) eq12 = f(x).diff(x, 4) - 1/x sol1 = Eq(f(x), -1 - x + (C1 + C2*x - 3*x**2/32 - x**3/24)*exp(-x) + C3*exp(x/3)) sol2 = Eq(f(x), -1 - x + (C1 + C2*x - x**2/8)*exp(-x) + C3*exp(x/3)) sol3 = Eq(f(x), C1 + x) sol4 = Eq(f(x), 2 + C1*exp(-x) + C2*exp(-2*x)) sol5 = Eq(f(x), 2*exp(x) + C1*exp(-x) + C2*exp(-2*x)) sol6 = Eq(f(x), -x*exp(x) - 2*exp(-x) + C1*exp(-2*x) + C2*exp(4*x)) sol7 = Eq(f(x), (C1 + C2*x + x**4/12)*exp(-x)) sol8 = Eq(f(x), C1*exp(x) + C2*exp(2*x) + (6*x + 5)*exp(-x)/36) sol9 = Eq(f(x), (C1 + C2*x + C3*x**2 + x**3/6)*exp(x)) sol10 = Eq(f(x), (C1 + x*(C2 + log(x)))*exp(-x)) sol11 = Eq(f(x), cos(x)*(C2 - Integral(1/cos(x), x)) + sin(x)*(C1 + Integral(1/sin(x), x))) sol12 = Eq(f(x), C1 + C2*x + x**3*(C3 + log(x)/6) + C4*x**2) sol1s = constant_renumber(sol1, 'C', 1, 3) sol2s = constant_renumber(sol2, 'C', 1, 3) sol3s = constant_renumber(sol3, 'C', 1, 2) sol4s = constant_renumber(sol4, 'C', 1, 2) sol5s = constant_renumber(sol5, 'C', 1, 2) sol6s = constant_renumber(sol6, 'C', 1, 2) sol7s = constant_renumber(sol7, 'C', 1, 2) sol8s = constant_renumber(sol8, 'C', 1, 2) sol9s = constant_renumber(sol9, 'C', 1, 3) sol10s = constant_renumber(sol10, 'C', 1, 2) sol11s = constant_renumber(sol11, 'C', 1, 2) sol12s = constant_renumber(sol12, 'C', 1, 4) assert dsolve(eq1, hint=hint) in (sol1, sol1s) assert dsolve(eq2, hint=hint) in (sol2, sol2s) assert dsolve(eq3, hint=hint) in (sol3, sol3s) assert dsolve(eq4, hint=hint) in (sol4, sol4s) assert dsolve(eq5, hint=hint) in (sol5, sol5s) assert dsolve(eq6, hint=hint) in (sol6, sol6s) assert dsolve(eq7, hint=hint) in (sol7, sol7s) assert dsolve(eq8, hint=hint) in (sol8, sol8s) assert dsolve(eq9, hint=hint) in (sol9, sol9s) assert dsolve(eq10, hint=hint) in (sol10, sol10s) assert dsolve(eq11, hint=hint + '_Integral') in (sol11, sol11s) assert dsolve(eq12, hint=hint) in (sol12, sol12s) assert checkodesol(eq1, sol1, order=3, solve_for_func=False)[0] assert checkodesol(eq2, sol2, order=3, solve_for_func=False)[0] assert checkodesol(eq3, sol3, order=1, solve_for_func=False)[0] assert checkodesol(eq4, sol4, order=2, solve_for_func=False)[0] assert checkodesol(eq5, sol5, order=2, solve_for_func=False)[0] assert checkodesol(eq6, sol6, order=2, solve_for_func=False)[0] assert checkodesol(eq7, sol7, order=2, solve_for_func=False)[0] assert checkodesol(eq8, sol8, order=2, solve_for_func=False)[0] assert checkodesol(eq9, sol9, order=3, solve_for_func=False)[0] assert checkodesol(eq10, sol10, order=2, solve_for_func=False)[0] assert checkodesol(eq12, sol12, order=4, solve_for_func=False)[0] def test_nth_linear_constant_coeff_variation_of_parameters_simplify_False(): # solve_variation_of_parameters shouldn't attempt to simplify the # Wronskian if simplify=False. If wronskian() ever gets good enough # to simplify the result itself, this test might fail. hint = 'nth_linear_constant_coeff_variation_of_parameters' assert dsolve(f(x).diff(x, 5) + 2*f(x).diff(x, 3) + f(x).diff(x) - 2*x - exp(I*x), f(x), hint + "_Integral", simplify=False) != \ dsolve(f(x).diff(x, 5) + 2*f(x).diff(x, 3) + f(x).diff(x) - 2*x - exp(I*x), f(x), hint + "_Integral", simplify=True) def test_Liouville_ODE(): hint = 'Liouville' # The first part here used to be test_ODE_1() from test_solvers.py eq1 = diff(f(x), x)/x + diff(f(x), x, x)/2 - diff(f(x), x)**2/2 eq1a = diff(x*exp(-f(x)), x, x) # compare to test_unexpanded_Liouville_ODE() below eq2 = (eq1*exp(-f(x))/exp(f(x))).expand() eq3 = diff(f(x), x, x) + 1/f(x)*(diff(f(x), x))**2 + 1/x*diff(f(x), x) eq4 = x*diff(f(x), x, x) + x/f(x)*diff(f(x), x)**2 + x*diff(f(x), x) eq5 = Eq((x*exp(f(x))).diff(x, x), 0) sol1 = Eq(f(x), log(x/(C1 + C2*x))) sol1a = Eq(C1 + C2/x - exp(-f(x)), 0) sol2 = sol1 sol3 = set( [Eq(f(x), -sqrt(C1 + C2*log(x))), Eq(f(x), sqrt(C1 + C2*log(x)))]) sol4 = set([Eq(f(x), sqrt(C1 + C2*exp(x))*exp(-x/2)), Eq(f(x), -sqrt(C1 + C2*exp(x))*exp(-x/2))]) sol5 = Eq(f(x), log(C1 + C2/x)) sol1s = constant_renumber(sol1, 'C', 1, 2) sol2s = constant_renumber(sol2, 'C', 1, 2) sol3s = constant_renumber(sol3, 'C', 1, 2) sol4s = constant_renumber(sol4, 'C', 1, 2) sol5s = constant_renumber(sol5, 'C', 1, 2) assert dsolve(eq1, hint=hint) in (sol1, sol1s) assert dsolve(eq1a, hint=hint) in (sol1, sol1s) assert dsolve(eq2, hint=hint) in (sol2, sol2s) assert set(dsolve(eq3, hint=hint)) in (sol3, sol3s) assert set(dsolve(eq4, hint=hint)) in (sol4, sol4s) assert dsolve(eq5, hint=hint) in (sol5, sol5s) assert checkodesol(eq1, sol1, order=2, solve_for_func=False)[0] assert checkodesol(eq1a, sol1a, order=2, solve_for_func=False)[0] assert checkodesol(eq2, sol2, order=2, solve_for_func=False)[0] assert all(i[0] for i in checkodesol(eq3, sol3, order=2, solve_for_func=False)) assert all(i[0] for i in checkodesol(eq4, sol4, order=2, solve_for_func=False)) assert checkodesol(eq5, sol5, order=2, solve_for_func=False)[0] not_Liouville1 = classify_ode(diff(f(x), x)/x + f(x)*diff(f(x), x, x)/2 - diff(f(x), x)**2/2, f(x)) not_Liouville2 = classify_ode(diff(f(x), x)/x + diff(f(x), x, x)/2 - x*diff(f(x), x)**2/2, f(x)) assert hint not in not_Liouville1 assert hint not in not_Liouville2 assert hint + '_Integral' not in not_Liouville1 assert hint + '_Integral' not in not_Liouville2 def test_unexpanded_Liouville_ODE(): # This is the same as eq1 from test_Liouville_ODE() above. eq1 = diff(f(x), x)/x + diff(f(x), x, x)/2 - diff(f(x), x)**2/2 eq2 = eq1*exp(-f(x))/exp(f(x)) sol2 = Eq(f(x), log(x/(C1 + C2*x))) sol2s = constant_renumber(sol2, 'C', 1, 2) assert dsolve(eq2) in (sol2, sol2s) assert checkodesol(eq2, sol2, order=2, solve_for_func=False)[0] def test_1686(): from sympy.abc import A eq = x + A*(x + diff(f(x), x) + f(x)) + diff(f(x), x) + f(x) + 2 assert classify_ode(eq, f(x)) == ('1st_linear', 'almost_linear', '1st_power_series', 'lie_group', 'nth_linear_constant_coeff_undetermined_coefficients', 'nth_linear_constant_coeff_variation_of_parameters', '1st_linear_Integral', 'almost_linear_Integral', 'nth_linear_constant_coeff_variation_of_parameters_Integral') # 1765 eq = (x**2 + f(x)**2)*f(x).diff(x) - 2*x*f(x) assert classify_ode(eq, f(x)) == ('1st_exact', '1st_homogeneous_coeff_best', '1st_homogeneous_coeff_subs_indep_div_dep', '1st_homogeneous_coeff_subs_dep_div_indep', 'separable_reduced', '1st_power_series', 'lie_group', '1st_exact_Integral', '1st_homogeneous_coeff_subs_indep_div_dep_Integral', '1st_homogeneous_coeff_subs_dep_div_indep_Integral', 'separable_reduced_Integral') def test_1726(): raises(ValueError, lambda: dsolve(f(x, y).diff(x) - y*f(x, y), f(x))) assert classify_ode(f(x, y).diff(x) - y*f(x, y), f(x), dict=True) == \ {'default': None, 'order': 0} # See also issue 694, test Z13. raises(ValueError, lambda: dsolve(f(x).diff(x), f(y))) assert classify_ode(f(x).diff(x), f(y), dict=True) == \ {'default': None, 'order': 0} def test_constant_renumber_order_issue2209(): from sympy.utilities.iterables import variations assert constant_renumber(C1*x + C2*y, "C", 1, 2) == \ constant_renumber(C1*y + C2*x, "C", 1, 2) == \ C1*x + C2*y e = C1*(C2 + x)*(C3 + y) for a, b, c in variations([C1, C2, C3], 3): assert constant_renumber(a*(b + x)*(c + y), "C", 1, 3) == e def test_issue_2671(): k = Symbol("k", real=True) t = Symbol('t') w = Function('w') sol = dsolve(w(t).diff(t, 6) - k**6*w(t), w(t)) assert len([s for s in sol.atoms(Symbol) if s.name.startswith('C')]) == 6 assert constantsimp((C1*cos(x) + C2*cos(x))*exp(x), x, 2) == \ C1*cos(x)*exp(x) assert constantsimp(C1*cos(x) + C2*cos(x) + C3*sin(x), x, 2) == \ C1*cos(x) + C3*sin(x) assert constantsimp(exp(C1 + x), x, 1) == C1*exp(x) assert constantsimp(2**(C1 + x), x, 1) == C1*2**x assert constantsimp(2**(C1 + x), x, 1) == C1*2**x assert constantsimp(x + C1 + y, x, 1) == C1 + x assert constantsimp(x + C1 + Integral(x, (x, 1, 2)), x, 1) == C1 + x def test_issue_2013_2331(): assert homogeneous_order(-log(x) + acosh(x), x) is None assert homogeneous_order(y - log(x), x, y) is None def test_nth_order_linear_euler_eq_homogeneous(): x, t, a, b, c = symbols('x t a b c') y = Function('y') our_hint = "nth_linear_euler_eq_homogeneous" eq = diff(f(t), t, 4)*t**4 - 13*diff(f(t), t, 2)*t**2 + 36*f(t) assert our_hint in classify_ode(eq) eq = a*y(t) + b*t*diff(y(t), t) + c*t**2*diff(y(t), t, 2) assert our_hint in classify_ode(eq) eq = Eq(-3*diff(f(x), x)*x + 2*x**2*diff(f(x), x, x), 0) sol = C1 + C2*x**Rational(5, 2) sols = constant_renumber(sol, 'C', 1, 3) assert our_hint in classify_ode(eq) assert dsolve(eq, f(x), hint=our_hint).rhs in (sol, sols) assert checkodesol(eq, sol, order=2, solve_for_func=False)[0] eq = Eq(3*f(x) - 5*diff(f(x), x)*x + 2*x**2*diff(f(x), x, x), 0) sol = C1*sqrt(x) + C2*x**3 sols = constant_renumber(sol, 'C', 1, 3) assert our_hint in classify_ode(eq) assert dsolve(eq, f(x), hint=our_hint).rhs in (sol, sols) assert checkodesol(eq, sol, order=2, solve_for_func=False)[0] eq = Eq(4*f(x) + 5*diff(f(x), x)*x + x**2*diff(f(x), x, x), 0) sol = (C1 + C2*log(x))/x**2 sols = constant_renumber(sol, 'C', 1, 3) assert our_hint in classify_ode(eq) assert dsolve(eq, f(x), hint=our_hint).rhs in (sol, sols) assert checkodesol(eq, sol, order=2, solve_for_func=False)[0] eq = Eq(6*f(x) - 6*diff(f(x), x)*x + 1*x**2*diff(f(x), x, x) + x**3*diff(f(x), x, x, x), 0) sol = dsolve(eq, f(x), hint=our_hint) sol = C1/x**2 + C2*x + C3*x**3 sols = constant_renumber(sol, 'C', 1, 4) assert our_hint in classify_ode(eq) assert dsolve(eq, f(x), hint=our_hint).rhs in (sol, sols) assert checkodesol(eq, sol, order=2, solve_for_func=False)[0] eq = Eq(-125*f(x) + 61*diff(f(x), x)*x - 12*x**2*diff(f(x), x, x) + x**3*diff(f(x), x, x, x), 0) sol = x**5*(C1 + C2*log(x) + C3*log(x)**2) sols = constant_renumber(sol, 'C', 1, 4) assert our_hint in classify_ode(eq) assert dsolve(eq, f(x), hint=our_hint).rhs in (sol, sols) assert checkodesol(eq, sol, order=2, solve_for_func=False)[0] eq = t**2*diff(y(t), t, 2) + t*diff(y(t), t) - 9*y(t) sol = C1*t**3 + C2*t**-3 sols = constant_renumber(sol, 'C', 1, 3) assert our_hint in classify_ode(eq) assert dsolve(eq, y(t), hint=our_hint).rhs in (sol, sols) assert checkodesol(eq, sol, order=2, solve_for_func=False)[0] def test_issue_1996(): f = Function('f') raises(ValueError, lambda: dsolve(f(x).diff(x)**2, f(x), 'separable')) raises(ValueError, lambda: dsolve(f(x).diff(x)**2, f(x), 'fdsjf')) def test_almost_linear(): from sympy import Ei A = Symbol('A', positive=True) our_hint = 'almost_linear' f = Function('f') d = f(x).diff(x) eq = x**2*f(x)**2*d + f(x)**3 + 1 sol = dsolve(eq, f(x), hint = 'almost_linear') assert sol[0].rhs == (C1*exp(3/x) - 1)**(1/3) assert checkodesol(eq, sol, order=1, solve_for_func=False)[0] eq = x*f(x)*d + 2*x*f(x)**2 + 1 sol = dsolve(eq, f(x), hint = 'almost_linear') assert sol[0].rhs == -sqrt((C1 - 2*Ei(4*x))*exp(-4*x)) assert checkodesol(eq, sol, order=1, solve_for_func=False)[0] eq = x*d + x*f(x) + 1 sol = dsolve(eq, f(x), hint = 'almost_linear') assert sol.rhs == (C1 - Ei(x))*exp(-x) assert checkodesol(eq, sol, order=1, solve_for_func=False)[0] assert our_hint in classify_ode(eq, f(x)) eq = x*exp(f(x))*d + exp(f(x)) + 3*x sol = dsolve(eq, f(x), hint = 'almost_linear') assert sol.rhs == log(C1/x - 3*x/2) assert checkodesol(eq, sol, order=1, solve_for_func=False)[0] eq = x + A*(x + diff(f(x), x) + f(x)) + diff(f(x), x) + f(x) + 2 sol = dsolve(eq, f(x), hint = 'almost_linear') assert sol.rhs == (C1 + Piecewise( (x, Eq(A + 1, 0)), ((-A*x + A - x - 1)*exp(x)/(A + 1), True)))*exp(-x) assert checkodesol(eq, sol, order=1, solve_for_func=False)[0] def test_exact_enhancement(): f = Function('f')(x) df = Derivative(f, x) eq = f/x**2 + ((f*x - 1)/x)*df sol = dsolve(eq, f) rhs = [eq.rhs for eq in sol] assert rhs == [(-sqrt(C1*x**2 + 1) + 1)/x, (sqrt(C1*x**2 + 1) + 1)/x] eq = (x*f - 1) + df*(x**2 - x*f) rhs = [sol.rhs for sol in dsolve(eq, f)] assert rhs[0] == x - sqrt(C1 + x**2 - 2*log(x)) assert rhs[1] == x + sqrt(C1 + x**2 - 2*log(x)) eq = (x + 2)*sin(f) + df*x*cos(f) rhs = [sol.rhs for sol in dsolve(eq, f)] assert rhs == [ -acos(-sqrt(C1*exp(-2*x) + x**4)/x**2) + 2*pi, -acos(sqrt(C1*exp(-2*x) + x**4)/x**2) + 2*pi, acos(-sqrt(C1*exp(-2*x) + x**4)/x**2), acos(sqrt(C1*exp(-2*x) + x**4)/x**2)] def test_separable_reduced(): f = Function('f') df = f(x).diff(x) eq = (x / f(x))*df + tan(x**2*f(x) / (x**2*f(x) - 1)) assert classify_ode(eq) == ('1st_linear', 'separable_reduced', 'lie_group', '1st_linear_Integral', 'separable_reduced_Integral') eq = x* df + f(x)* (1 / (x**2*f(x) - 1)) assert classify_ode(eq) == ('1st_linear', 'separable_reduced', 'lie_group', '1st_linear_Integral', 'separable_reduced_Integral') sol = dsolve(eq, hint = 'separable_reduced', simplify=False) assert sol.lhs == log(x**2*f(x))/3 + log(x**2*f(x) - S(3)/2)/6 assert sol.rhs == C1 + log(x) assert checkodesol(eq, sol, order=1, solve_for_func=False)[0] eq = df + (f(x) / (x**4*f(x) - x)) assert classify_ode(eq) == ('1st_linear', 'separable_reduced', 'lie_group', '1st_linear_Integral', 'separable_reduced_Integral') # generates PolynomialError in solve attempt sol = dsolve(eq, hint = 'separable_reduced') assert sol.lhs == log(x**3*f(x))/4 + log(x**3*f(x) - S(4)/3)/12 assert sol.rhs == C1 + log(x) assert checkodesol(eq, sol, order=1, solve_for_func=False)[0] eq = x*df + f(x)*(x**2*f(x)) sol = dsolve(eq, hint = 'separable_reduced', simplify=False) assert sol == Eq(log(x**2*f(x))/2 - log(x**2*f(x) - 2)/2, C1 + log(x)) assert checkodesol(eq, sol, order=1, solve_for_func=False)[0] def test_homogeneous_function(): f = Function('f') eq1 = tan(x + f(x)) eq2 = sin((3*x)/(4*f(x))) eq3 = cos(3*x/4*f(x)) eq4 = log((3*x + 4*f(x))/(5*f(x) + 7*x)) eq5 = exp((2*x**2)/(3*f(x)**2)) eq6 = log((3*x + 4*f(x))/(5*f(x) + 7*x) + exp((2*x**2)/(3*f(x)**2))) eq7 = sin((3*x)/(5*f(x) + x**2)) assert homogeneous_order(eq1, x, f(x)) == None assert homogeneous_order(eq2, x, f(x)) == 0 assert homogeneous_order(eq3, x, f(x)) == None assert homogeneous_order(eq4, x, f(x)) == 0 assert homogeneous_order(eq5, x, f(x)) == 0 assert homogeneous_order(eq6, x, f(x)) == 0 assert homogeneous_order(eq7, x, f(x)) == None def test_linear_coeff_match(): from sympy.solvers.ode import _linear_coeff_match n, d = z*(2*x + 3*f(x) + 5), z*(7*x + 9*f(x) + 11) rat = n/d eq1 = sin(rat) + cos(rat.expand()) eq2 = rat eq3 = log(sin(rat)) ans = (4, -S(13)/3) assert _linear_coeff_match(eq1, f(x)) == ans assert _linear_coeff_match(eq2, f(x)) == ans assert _linear_coeff_match(eq3, f(x)) == ans # no c eq4 = (3*x)/f(x) # not x and f(x) eq5 = (3*x + 2)/x # denom will be zero eq6 = (3*x + 2*f(x) + 1)/(3*x + 2*f(x) + 5) # not rational coefficient eq7 = (3*x + 2*f(x) + sqrt(2))/(3*x + 2*f(x) + 5) assert _linear_coeff_match(eq4, f(x)) is None assert _linear_coeff_match(eq5, f(x)) is None assert _linear_coeff_match(eq6, f(x)) is None assert _linear_coeff_match(eq7, f(x)) is None def test_linear_coefficients(): f = Function('f') df = f(x).diff(x) sol = Eq(f(x), C1/(x**2 + 6*x + 9) - S(3)/2) # XXX if force is not used in solve, the following is returned which, # for C1 = -81/2, will satisfy the original equation. Should there be # another free symbol so a family of solutions can be obtained, e.g. # (C2 + C1*x + ... etc)/(...): # Eq(f(x), (C1 + C1*x - 3*x**3/2 - 27*x**2/2)/(x**3 + 9*x**2 + 27*x + 27)) eq = df + (3 + 2*f(x))/(x + 3) assert dsolve(eq, hint='linear_coefficients') == sol assert checkodesol(eq, sol, order=1, solve_for_func=False)[0] @XFAIL def test_constantsimp_take_problem(): # should this have C1 and C2 or C1 and exp(C1) in addition to x? # see note in test_linear_coefficients above c = exp(C1) + 2 assert len(Poly(constantsimp(exp(C1) + c + c*x, x, 2)).gens) > 2 def test_issue_3780(): f = Function('f') eq = Eq(Derivative(f(x), x, 2) - 2*Derivative(f(x), x) + f(x), sin(x)) sol = (C1 + C2*x)*exp(x) + cos(x)/S(2) assert dsolve(eq).rhs == sol assert checkodesol(eq, sol, order=1, solve_for_func=False)[0] def test_issue_3890(): f = Function('f') k = Symbol('k') assert dsolve(f(x).diff(x) - x*exp(-k*x), f(x)) == \ Eq(f(x), C1 + Piecewise( (x**2/2, Eq(k**3, 0)), ((-k**2*x - k)*exp(-k*x)/k**3, True) )) assert dsolve(-f(x).diff(x) + x*exp(-k*x), f(x)) == \ Eq(f(x), Piecewise((C1 + x**2/2, Eq(k**3, 0)), (C1 - x*exp(-k*x)/k - exp(-k*x)/k**2, True) )) def test_heuristic1(): y, a, b, c, a4, a3, a2, a1, a0 = symbols("y a b c a4 a3 a2 a1 a0") y = Symbol('y') f = Function('f') xi = Function('xi') eta = Function('eta') df = f(x).diff(x) eq = Eq(df, x**2*f(x)) eq1 = f(x).diff(x) + a*f(x) - c*exp(b*x) eq2 = f(x).diff(x) + 2*x*f(x) - x*exp(-x**2) eq3 = (1 + 2*x)*df + 2 - 4*exp(-f(x)) eq4 = f(x).diff(x) - (a4*x**4 + a3*x**3 + a2*x**2 + a1*x + a0)**(S(-1)/2) eq5 = x**2*df - f(x) + x**2*exp(x - (1/x)) eqlist = [eq, eq1, eq2, eq3, eq4, eq5] i = infinitesimals(eq, hint='abaco1_simple') assert i == [{eta(x, f(x)): exp(x**3/3), xi(x, f(x)): 0}, {eta(x, f(x)): f(x), xi(x, f(x)): 0}, {eta(x, f(x)): 0, xi(x, f(x)): x**(-2)}] i1 = infinitesimals(eq1, hint='abaco1_simple') assert i1 == [{eta(x, f(x)): exp(-a*x), xi(x, f(x)): 0}] i2 = infinitesimals(eq2, hint='abaco1_simple') assert i2 == [{eta(x, f(x)): exp(-x**2), xi(x, f(x)): 0}] i3 = infinitesimals(eq3, hint='abaco1_simple') assert i3 == [{eta(x, f(x)): 0, xi(x, f(x)): 2*x + 1}, {eta(x, f(x)): 0, xi(x, f(x)): 1/(exp(f(x)) - 2)}] i4 = infinitesimals(eq4, hint='abaco1_simple') assert i4 == [{eta(x, f(x)): 1, xi(x, f(x)): 0}, {eta(x, f(x)): 0, xi(x, f(x)): sqrt(2*a0 + 2*a1*x + 2*a2*x**2 + 2*a3*x**3 + 2*a4*x**4)}] i5 = infinitesimals(eq5, hint='abaco1_simple') assert i5 == [{xi(x, f(x)): 0, eta(x, f(x)): exp(-1/x)}] ilist = [i, i1, i2, i3, i4, i5] for eq, i in (zip(eqlist, ilist)): check = checkinfsol(eq, i) assert check[0] @XFAIL def test_issue_3148(): eq = x**2*f(x)**2 + x*Derivative(f(x), x) sol = dsolve(eq, hint = 'separable_reduced') assert checkodesol(eq, sol, order=1)[0] def test_heuristic2(): y = Symbol('y') xi = Function('xi') eta = Function('eta') df = f(x).diff(x) # This ODE can be solved by the Lie Group method, when there are # better assumptions eq = df - (f(x)/x)*(x*log(x**2/f(x)) + 2) i = infinitesimals(eq, hint='abaco1_product') assert i == [{eta(x, f(x)): f(x)*exp(-x), xi(x, f(x)): 0}] assert checkinfsol(eq, i)[0] def test_heuristic3(): y = Symbol('y') xi = Function('xi') eta = Function('eta') a, b = symbols("a b") df = f(x).diff(x) eq = x**2*df + x*f(x) + f(x)**2 + x**2 i = infinitesimals(eq, hint='bivariate') assert i == [{eta(x, f(x)): f(x), xi(x, f(x)): x}] assert checkinfsol(eq, i)[0] eq = x**2*(-f(x)**2 + df)- a*x**2*f(x) + 2 - a*x i = infinitesimals(eq, hint='bivariate') assert checkinfsol(eq, i)[0] def test_heuristic_4(): y, a = symbols("y a") xi = Function('xi') eta = Function('eta') eq = x*(f(x).diff(x)) + 1 - f(x)**2 i = infinitesimals(eq, hint='chi') assert checkinfsol(eq, i)[0] def test_heuristic_function_sum(): xi = Function('xi') eta = Function('eta') eq = f(x).diff(x) - (3*(1 + x**2/f(x)**2)*atan(f(x)/x) + (1 - 2*f(x))/x + (1 - 3*f(x))*(x/f(x)**2)) i = infinitesimals(eq, hint='function_sum') assert i == [{eta(x, f(x)): f(x)**(-2) + x**(-2), xi(x, f(x)): 0}] assert checkinfsol(eq, i)[0] def test_heuristic_abaco2_similar(): xi = Function('xi') eta = Function('eta') F = Function('F') a, b = symbols("a b") eq = f(x).diff(x) - F(a*x + b*f(x)) i = infinitesimals(eq, hint='abaco2_similar') assert i == [{eta(x, f(x)): -a/b, xi(x, f(x)): 1}] assert checkinfsol(eq, i)[0] eq = f(x).diff(x) - (f(x)**2 / (sin(f(x) - x) - x**2 + 2*x*f(x))) i = infinitesimals(eq, hint='abaco2_similar') assert i == [{eta(x, f(x)): f(x)**2, xi(x, f(x)): f(x)**2}] assert checkinfsol(eq, i)[0] def test_heuristic_abaco2_unique_unknown(): xi = Function('xi') eta = Function('eta') F = Function('F') a, b = symbols("a b") x = Symbol("x", positive=True) eq = f(x).diff(x) - x**(a - 1)*(f(x)**(1 - b))*F(x**a/a + f(x)**b/b) i = infinitesimals(eq, hint='abaco2_unique_unknown') assert i == [{eta(x, f(x)): -f(x)*f(x)**(-b), xi(x, f(x)): x*x**(-a)}] assert checkinfsol(eq, i)[0] eq = f(x).diff(x) + tan(F(x**2 + f(x)**2) + atan(x/f(x))) i = infinitesimals(eq, hint='abaco2_unique_unknown') assert i == [{eta(x, f(x)): x, xi(x, f(x)): -f(x)}] assert checkinfsol(eq, i)[0] eq = (x*f(x).diff(x) + f(x) + 2*x)**2 -4*x*f(x) -4*x**2 -4*a i = infinitesimals(eq, hint='abaco2_unique_unknown') assert checkinfsol(eq, i)[0] def test_heuristic_linear(): xi = Function('xi') eta = Function('eta') F = Function('F') a, b, m, n = symbols("a b m n") eq = x**(n*(m + 1) - m)*(f(x).diff(x)) - a*f(x)**n -b*x**(n*(m + 1)) i = infinitesimals(eq, hint='linear') assert checkinfsol(eq, i)[0] @XFAIL def test_kamke(): a, b, alpha, c = symbols("a b alpha c") eq = x**2*(a*f(x)**2+(f(x).diff(x))) + b*x**alpha + c i = infinitesimals(eq, hint='sum_function') assert checkinfsol(eq, i)[0] def test_series(): C0 = Symbol("C0") eq = f(x).diff(x) - f(x) assert dsolve(eq, hint='1st_power_series') == Eq(f(x), C0 + C0*x + C0*x**2/S(2) + C0*x**3/S(6) + C0*x**4/S(24) + C0*x**5/S(120) + O(x**6)) eq = f(x).diff(x) - x*f(x) assert dsolve(eq, hint='1st_power_series') == Eq(f(x), C0*x**4/S(8) + C0*x**2/S(2) + C0 + O(x**6)) eq = f(x).diff(x) - sin(x*f(x)) assert dsolve(eq, hint='1st_power_series', ics={f(2): 2}, n=3) == Eq( f(x), (x - 2)**2*(2*cos(4) + 2*sin(4)*cos(4))/S(2) + (x - 2)*sin(4) + 2 + O(x**3)) def test_lie_group(): C1 = Symbol("C1") a, b, c = symbols("a b c") eq = f(x).diff(x)**2 sol = dsolve(eq, f(x), hint='lie_group') assert checkodesol(eq, sol)[0] eq = Eq(f(x).diff(x), x**2*f(x)) sol = dsolve(eq, f(x), hint='lie_group') assert sol == Eq(f(x), C1*exp(x**3/S(3))) assert checkodesol(eq, sol)[0] eq = f(x).diff(x) + a*f(x) - c*exp(b*x) sol = dsolve(eq, f(x), hint='lie_group') assert checkodesol(eq, sol)[0] eq = f(x).diff(x) + 2*x*f(x) - x*exp(-x**2) sol = dsolve(eq, f(x), hint='lie_group') assert sol == Eq(f(x), (C1 + x**2/S(2))*exp(-x**2)) assert checkodesol(eq, sol)[0] eq = (1 + 2*x)*(f(x).diff(x)) + 2 - 4*exp(-f(x)) sol = dsolve(eq, f(x), hint='lie_group') assert sol == Eq(f(x), log(C1/(2*x + 1) + 2)) assert checkodesol(eq, sol)[0] eq = x**2*(f(x).diff(x)) - f(x) + x**2*exp(x - (1/x)) sol = dsolve(eq, f(x), hint='lie_group') assert checkodesol(eq, sol)[0] eq = x**2*f(x)**2 + x*Derivative(f(x), x) sol = dsolve(eq, f(x), hint='lie_group') assert sol == Eq(f(x), 2/(C1 + x**2)) assert checkodesol(eq, sol)[0] def test_user_infinitesimals(): C2 = Symbol("C2") eq = x*(f(x).diff(x)) + 1 - f(x)**2 sol = dsolve(eq, hint='lie_group', xi=sqrt(f(x) - 1)/sqrt(f(x) + 1), eta=0) assert sol == Eq(f(x), (C2 + x**2)/(C1 - x**2)) raises(ValueError, lambda: dsolve(eq, hint='lie_group', xi=0, eta=f(x))) @XFAIL def test_issue_3982(): eq = x*(f(x).diff(x)) + 1 - f(x)**2 assert dsolve(eq) == Eq(f(x), (C2 + x**2)/(C1 - x**2)) def test_2nd_power_series_ordinary(): C0, C1 = symbols("C0 C1") eq = f(x).diff(x, 2) - x*f(x) assert classify_ode(eq) == ('2nd_power_series_ordinary',) assert dsolve(eq) == Eq(f(x), C0*(x**3/S(6) + 1) + C1*x*(x**3/S(12) + 1) + O(x**6)) assert dsolve(eq, x0=-2) == Eq(f(x), C0*((x + 2)**4/S(6) + (x + 2)**3/S(6) - (x + 2)**2 + 1) + C1*(x + (x + 2)**4/S(12) - (x + 2)**3/3 + S(2)) + O(x**6)) assert dsolve(eq, n=2) == Eq(f(x), C1*x + C0 + O(x**2)) eq = (1 + x**2)*(f(x).diff(x, 2)) + 2*x*(f(x).diff(x)) -2*f(x) assert classify_ode(eq) == ('2nd_power_series_ordinary',) assert dsolve(eq) == Eq(f(x), C0*(-x**4/S(3) + x**2 + 1) + C1*x + O(x**6)) eq = f(x).diff(x, 2) + x*(f(x).diff(x)) + f(x) assert classify_ode(eq) == ('2nd_power_series_ordinary',) assert dsolve(eq) == Eq(f(x), C0*( x**4/S(8) - x**2/S(2) + 1) + C1*x*(-x**2/S(3) + 1) + O(x**6)) eq = f(x).diff(x, 2) + f(x).diff(x) - x*f(x) assert classify_ode(eq) == ('2nd_power_series_ordinary',) assert dsolve(eq) == Eq(f(x), C0*( -x**4/S(24) + x**3/S(6) + 1) + C1*x*(x**3/S(24) + x**2/S(6) - x/S(2) + 1) + O(x**6)) eq = f(x).diff(x, 2) + x*f(x) assert classify_ode(eq) == ('2nd_power_series_ordinary',) assert dsolve(eq, n=7) == Eq(f(x), C0*( x**6/S(180) - x**3/S(6) + 1) + C1*x*(-x**3/S(12) + 1) + O(x**7)) def test_2nd_power_series_ordinary(): C0, C1 = symbols("C0 C1") eq = x**2*(f(x).diff(x, 2)) - 3*x*(f(x).diff(x)) + (4*x + 4)*f(x) assert dsolve(eq) == Eq(f(x), C0*x**2*(-16*x**3/S(9) + 4*x**2 - 4*x + 1) + O(x**6)) eq = 4*x**2*(f(x).diff(x, 2)) -8*x**2*(f(x).diff(x)) + (4*x**2 + 1)*f(x) assert dsolve(eq) == Eq(f(x), C0*sqrt(x)*( x**4/S(24) + x**3/S(6) + x**2/S(2) + x + 1) + O(x**6)) eq = x**2*(f(x).diff(x, 2)) - x**2*(f(x).diff(x)) + ( x**2 - 2)*f(x) assert dsolve(eq) == Eq(f(x), C1*(-x**6/S(720) - 3*x**5/S(80) - x**4/S(8) + x**2/S(2) + x/S(2) + 1)/x + C0*x**2*(-x**3/S(60) + x**2/S(20) + x/S(2) + 1) + O(x**6)) eq = x**2*(f(x).diff(x, 2)) + x*(f(x).diff(x)) + (x**2 - 1/S(4))*f(x) assert dsolve(eq) == Eq(f(x), C1*(x**4/S(24) - x**2/S(2) + 1)/sqrt(x) + C0*sqrt(x)*(x**4/S(120) - x**2/S(6) + 1) + O(x**6)) eq = x*(f(x).diff(x, 2)) - f(x).diff(x) + 4*x**3*f(x) assert dsolve(eq) == Eq(f(x), C1*(-x**4/S(2) + 1) + C0*x**2 + O(x**6)) def test_issue3994(): sol = Eq(f(x), C1 - 2*x*sqrt(x**3)/5) eq = Derivative(f(x), x)**2 - x**3 assert dsolve(eq) == sol and checkodesol(eq, sol) == (True, 0) sympy-0.7.4.1/sympy/solvers/tests/test_constantsimp.py0000644000175000017500000002167112253362407023426 0ustar georgeskgeorgesk""" If the arbitrary constant class from issue 1336 is ever implemented, this should serve as a set of test cases. """ from sympy import (acos, cos, cosh, Eq, exp, Function, I, Integral, log, Pow, S, sin, sinh, sqrt, Symbol) from sympy.solvers.ode import constant_renumber, constantsimp from sympy.utilities.pytest import XFAIL x = Symbol('x') y = Symbol('y') z = Symbol('z') C1 = Symbol('C1') C2 = Symbol('C2') C3 = Symbol('C3') f = Function('f') def test_constant_mul(): # We want C1 (Constant) below to absorb the y's, but not the x's assert constant_renumber(constantsimp(y*C1, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(x*C1, x, 1), 'C', 1, 1) == x*C1 assert constant_renumber(constantsimp(C1*y, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(C1*x, x, 1), 'C', 1, 1) == x*C1 assert constant_renumber(constantsimp(2*C1, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(C1*2, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(y*C1*x, x, 1), 'C', 1, 1) == C1*x assert constant_renumber(constantsimp(x*y*C1, x, 1), 'C', 1, 1) == x*C1 assert constant_renumber(constantsimp(y*x*C1, x, 1), 'C', 1, 1) == x*C1 assert constant_renumber(constantsimp(C1*y*(y + 1), x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(y*C1*(y + 1), x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(x*(y*C1), x, 1), 'C', 1, 1) == x*C1 assert constant_renumber(constantsimp(x*(C1*y), x, 1), 'C', 1, 1) == x*C1 assert constant_renumber(constantsimp(C1*(x*y), x, 1), 'C', 1, 1) == C1*x assert constant_renumber(constantsimp((x*y)*C1, x, 1), 'C', 1, 1) == x*C1 assert constant_renumber(constantsimp((y*x)*C1, x, 1), 'C', 1, 1) == x*C1 assert constant_renumber(constantsimp(y*(y + 1)*C1, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(C1*x*y, x, 1), 'C', 1, 1) == C1*x assert constant_renumber(constantsimp(x*C1*y, x, 1), 'C', 1, 1) == x*C1 assert constant_renumber(constantsimp((C1*x)*y, x, 1), 'C', 1, 1) == C1*x assert constant_renumber(constantsimp(y*(x*C1), x, 1), 'C', 1, 1) == x*C1 assert constant_renumber(constantsimp((x*C1)*y, x, 1), 'C', 1, 1) == x*C1 assert constant_renumber( constantsimp(C1*x*y*x*y*2, x, 1), 'C', 1, 1) == C1*x**2 assert constant_renumber(constantsimp(C1*x*y*z, x, 1), 'C', 1, 1) == C1*x assert constant_renumber( constantsimp(C1*x*y**2*sin(z), x, 1), 'C', 1, 1) == C1*x assert constant_renumber(constantsimp(C1*C1, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(C1*C2, x, 2), 'C', 1, 2) == C1 assert constant_renumber(constantsimp(C2*C2, x, 2), 'C', 1, 2) == C1 assert constant_renumber(constantsimp(C1*C1*C2, x, 2), 'C', 1, 2) == C1 assert constant_renumber( constantsimp(C1*x*2**x, x, 1), 'C', 1, 1) == C1*x*2**x def test_constant_add(): assert constant_renumber(constantsimp(C1 + C1, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(C1 + 2, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(2 + C1, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(C1 + y, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(C1 + x, x, 1), 'C', 1, 1) == C1 + x assert constant_renumber(constantsimp(C1 + x + y + x*y + 2, x, 1), 'C', 1, 1) == \ C1 + x*(y + 1) assert constant_renumber(constantsimp(C1 + x + 2**x + y + 2, x, 1), 'C', 1, 1) == \ C1 + x + 2**x assert constant_renumber(constantsimp(C1 + C1, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(C1 + C2, x, 2), 'C', 1, 2) == C1 assert constant_renumber(constantsimp(C2 + C1, x, 2), 'C', 1, 2) == C1 assert constant_renumber(constantsimp(C1 + C2 + C1, x, 2), 'C', 1, 2) == C1 def test_constant_power_as_base(): assert constant_renumber(constantsimp(C1**C1, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(Pow(C1, C1), x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(C1**C1, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(C1**C2, x, 2), 'C', 1, 2) == C1 assert constant_renumber(constantsimp(C2**C1, x, 2), 'C', 1, 2) == C1 assert constant_renumber(constantsimp(C2**C2, x, 2), 'C', 1, 2) == C1 assert constant_renumber(constantsimp(C1**y, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(C1**x, x, 1), 'C', 1, 1) == C1**x assert constant_renumber(constantsimp(C1**2, x, 1), 'C', 1, 1) == C1 assert constant_renumber( constantsimp(C1**(x*y), x, 1), 'C', 1, 1) == C1**(x*y) def test_constant_power_as_exp(): assert constant_renumber(constantsimp(x**C1, x, 1), 'C', 1, 1) == x**C1 assert constant_renumber(constantsimp(y**C1, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(x**y**C1, x, 1), 'C', 1, 1) == x**C1 assert constant_renumber( constantsimp((x**y)**C1, x, 1), 'C', 1, 1) == (x**y)**C1 assert constant_renumber( constantsimp(x**(y**C1), x, 1), 'C', 1, 1) == x**C1 assert constant_renumber(constantsimp(x**C1**y, x, 1), 'C', 1, 1) == x**C1 assert constant_renumber( constantsimp(x**(C1**y), x, 1), 'C', 1, 1) == x**C1 assert constant_renumber( constantsimp((x**C1)**y, x, 1), 'C', 1, 1) == (x**C1)**y assert constant_renumber(constantsimp(2**C1, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(S(2)**C1, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(exp(C1), x, 1), 'C', 1, 1) == C1 assert constant_renumber( constantsimp(exp(C1 + x), x, 1), 'C', 1, 1) == C1*exp(x) assert constant_renumber(constantsimp(Pow(2, C1), x, 1), 'C', 1, 1) == C1 def test_constant_function(): assert constant_renumber(constantsimp(sin(C1), x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(f(C1), x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(f(C1, C1), x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(f(C1, C2), x, 2), 'C', 1, 2) == C1 assert constant_renumber(constantsimp(f(C2, C1), x, 2), 'C', 1, 2) == C1 assert constant_renumber(constantsimp(f(C2, C2), x, 2), 'C', 1, 2) == C1 assert constant_renumber( constantsimp(f(C1, x), x, 1), 'C', 1, 2) == f(C1, x) assert constant_renumber(constantsimp(f(C1, y), x, 1), 'C', 1, 2) == C1 assert constant_renumber(constantsimp(f(y, C1), x, 1), 'C', 1, 2) == C1 assert constant_renumber(constantsimp(f(C1, y, C2), x, 2), 'C', 1, 2) == C1 @XFAIL def test_constant_function_multiple(): # The rules to not renumber in this case would be too complicated, and # dsolve is not likely to ever encounter anything remotely like this. assert constant_renumber( constantsimp(f(C1, C1, x), x, 1), 'C', 1, 1) == f(C1, C1, x) def test_constant_multiple(): assert constant_renumber(constantsimp(C1*2 + 2, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(x*2/C1, x, 1), 'C', 1, 1) == C1*x assert constant_renumber(constantsimp(C1**2*2 + 2, x, 1), 'C', 1, 1) == C1 assert constant_renumber( constantsimp(sin(2*C1) + x + sqrt(2), x, 1), 'C', 1, 1) == C1 + x assert constant_renumber(constantsimp(2*C1 + C2, x, 2), 'C', 1, 2) == C1 def test_ode_solutions(): # only a few examples here, the rest will be tested in the actual dsolve tests assert constant_renumber(constantsimp(C1*exp(2*x) + exp(x)*(C2 + C3), x, 3), 'C', 1, 3) == \ constant_renumber((C1*exp(x) + C2*exp(2*x)), 'C', 1, 2) assert constant_renumber( constantsimp(Eq(f(x), I*C1*sinh(x/3) + C2*cosh(x/3)), x, 2), 'C', 1, 2) == constant_renumber(Eq(f(x), C1*sinh(x/3) + C2*cosh(x/3)), 'C', 1, 2) assert constant_renumber(constantsimp(Eq(f(x), acos((-C1)/cos(x))), x, 1), 'C', 1, 1) == \ Eq(f(x), acos(C1/cos(x))) assert constant_renumber( constantsimp(Eq(log(f(x)/C1) + 2*exp(x/f(x)), 0), x, 1), 'C', 1, 1) == Eq(log(C1*f(x)) + 2*exp(x/f(x)), 0) assert constant_renumber(constantsimp(Eq(log(x*sqrt(2)*sqrt(1/x)*sqrt(f(x)) /C1) + x**2/(2*f(x)**2), 0), x, 1), 'C', 1, 1) == \ Eq(log(C1*x*sqrt(1/x)*sqrt(f(x))) + x**2/(2*f(x)**2), 0) assert constant_renumber(constantsimp(Eq(-exp(-f(x)/x)*sin(f(x)/x)/2 + log(x/C1) - cos(f(x)/x)*exp(-f(x)/x)/2, 0), x, 1), 'C', 1, 1) == \ Eq(-exp(-f(x)/x)*sin(f(x)/x)/2 + log(C1*x) - cos(f(x)/x)* exp(-f(x)/x)/2, 0) u2 = Symbol('u2') _a = Symbol('_a') assert constant_renumber(constantsimp(Eq(-Integral(-1/(sqrt(1 - u2**2)*u2), (u2, _a, x/f(x))) + log(f(x)/C1), 0), x, 1), 'C', 1, 1) == \ Eq(-Integral(-1/(u2*sqrt(1 - u2**2)), (u2, _a, x/f(x))) + log(C1*f(x)), 0) assert [constant_renumber(constantsimp(i, x, 1), 'C', 1, 1) for i in [Eq(f(x), sqrt(-C1*x + x**2)), Eq(f(x), -sqrt(-C1*x + x**2))]] == \ [Eq(f(x), sqrt(x*(C1 + x))), Eq(f(x), -sqrt(x*(C1 + x)))] def test_constant_Eq(): # C1 on the rhs is well-tested, but the lhs is only tested here assert constantsimp(Eq(C1, 3 + f(x)*x), x, 1) == Eq(C1, f(x)*x) sympy-0.7.4.1/sympy/solvers/tests/__init__.py0000644000175000017500000000000012253362407021363 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/solvers/__init__.py0000644000175000017500000000127712253362407020242 0ustar georgeskgeorgesk"""A module for solving all kinds of equations. Examples -------- >>> from sympy.solvers import solve >>> from sympy.abc import x >>> solve(x**5+5*x**4+10*x**3+10*x**2+5*x+1,x) [-1] """ from .solvers import solve, solve_linear_system, solve_linear_system_LU, \ solve_undetermined_coeffs, tsolve, nsolve, solve_linear, checksol from .recurr import rsolve, rsolve_poly, rsolve_ratio, rsolve_hyper from .ode import checkodesol, classify_ode, dsolve, \ homogeneous_order from .polysys import solve_poly_system, solve_triangulated from .pde import pde_separate, pde_separate_add, pde_separate_mul, \ pdsolve, classify_pde, checkpdesol from .deutils import ode_order sympy-0.7.4.1/sympy/logic/0000755000175000017500000000000012253362407015522 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/logic/algorithms/0000755000175000017500000000000012253362407017673 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/logic/algorithms/dpll.py0000644000175000017500000002210312253362407021176 0ustar georgeskgeorgesk"""Implementation of DPLL algorithm Further improvements: eliminate calls to pl_true, implement branching rules, efficient unit propagation. References: - http://en.wikipedia.org/wiki/DPLL_algorithm - http://bioinformatics.louisville.edu/ouyang/MingOuyangThesis.pdf """ from __future__ import print_function, division from sympy import default_sort_key from sympy.core.compatibility import reduce from sympy.logic.boolalg import Or, Not, conjuncts, disjuncts, to_cnf, \ to_int_repr, _find_predicates from sympy.logic.inference import pl_true, literal_symbol def dpll_satisfiable(expr): """ Check satisfiability of a propositional sentence. It returns a model rather than True when it succeeds >>> from sympy.abc import A, B >>> from sympy.logic.algorithms.dpll import dpll_satisfiable >>> dpll_satisfiable(A & ~B) {A: True, B: False} >>> dpll_satisfiable(A & ~A) False """ symbols = sorted(_find_predicates(expr), key=default_sort_key) symbols_int_repr = set(range(1, len(symbols) + 1)) clauses = conjuncts(to_cnf(expr)) clauses_int_repr = to_int_repr(clauses, symbols) result = dpll_int_repr(clauses_int_repr, symbols_int_repr, {}) if not result: return result output = {} for key in result: output.update({symbols[key - 1]: result[key]}) return output def dpll(clauses, symbols, model): """ Compute satisfiability in a partial model. Clauses is an array of conjuncts. >>> from sympy.abc import A, B, D >>> from sympy.logic.algorithms.dpll import dpll >>> dpll([A, B, D], [A, B], {D: False}) False """ # compute DP kernel P, value = find_unit_clause(clauses, model) while P: model.update({P: value}) symbols.remove(P) if not value: P = ~P clauses = unit_propagate(clauses, P) P, value = find_unit_clause(clauses, model) P, value = find_pure_symbol(symbols, clauses) while P: model.update({P: value}) symbols.remove(P) if not value: P = ~P clauses = unit_propagate(clauses, P) P, value = find_pure_symbol(symbols, clauses) # end DP kernel unknown_clauses = [] for c in clauses: val = pl_true(c, model) if val is False: return False if val is not True: unknown_clauses.append(c) if not unknown_clauses: return model if not clauses: return model P = symbols.pop() model_copy = model.copy() model.update({P: True}) model_copy.update({P: False}) symbols_copy = symbols[:] return (dpll(unit_propagate(unknown_clauses, P), symbols, model) or dpll(unit_propagate(unknown_clauses, Not(P)), symbols_copy, model_copy)) def dpll_int_repr(clauses, symbols, model): """ Compute satisfiability in a partial model. Arguments are expected to be in integer representation >>> from sympy.logic.algorithms.dpll import dpll_int_repr >>> dpll_int_repr([set([1]), set([2]), set([3])], set([1, 2]), {3: False}) False """ # compute DP kernel P, value = find_unit_clause_int_repr(clauses, model) while P: model.update({P: value}) symbols.remove(P) if not value: P = -P clauses = unit_propagate_int_repr(clauses, P) P, value = find_unit_clause_int_repr(clauses, model) P, value = find_pure_symbol_int_repr(symbols, clauses) while P: model.update({P: value}) symbols.remove(P) if not value: P = -P clauses = unit_propagate_int_repr(clauses, P) P, value = find_pure_symbol_int_repr(symbols, clauses) # end DP kernel unknown_clauses = [] for c in clauses: val = pl_true_int_repr(c, model) if val is False: return False if val is not True: unknown_clauses.append(c) if not unknown_clauses: return model P = symbols.pop() model_copy = model.copy() model.update({P: True}) model_copy.update({P: False}) symbols_copy = symbols.copy() return (dpll_int_repr(unit_propagate_int_repr(unknown_clauses, P), symbols, model) or dpll_int_repr(unit_propagate_int_repr(unknown_clauses, -P), symbols_copy, model_copy)) ### helper methods for DPLL def pl_true_int_repr(clause, model={}): """ Lightweight version of pl_true. Argument clause represents the set of args of an Or clause. This is used inside dpll_int_repr, it is not meant to be used directly. >>> from sympy.logic.algorithms.dpll import pl_true_int_repr >>> pl_true_int_repr(set([1, 2]), {1: False}) >>> pl_true_int_repr(set([1, 2]), {1: False, 2: False}) False """ result = False for lit in clause: if lit < 0: p = model.get(-lit) if p is not None: p = not p else: p = model.get(lit) if p is True: return True elif p is None: result = None return result def unit_propagate(clauses, symbol): """ Returns an equivalent set of clauses If a set of clauses contains the unit clause l, the other clauses are simplified by the application of the two following rules: 1. every clause containing l is removed 2. in every clause that contains ~l this literal is deleted Arguments are expected to be in CNF. >>> from sympy import symbols >>> from sympy.abc import A, B, D >>> from sympy.logic.algorithms.dpll import unit_propagate >>> unit_propagate([A | B, D | ~B, B], B) [D, B] """ output = [] for c in clauses: if c.func != Or: output.append(c) continue for arg in c.args: if arg == ~symbol: output.append(Or(*[x for x in c.args if x != ~symbol])) break if arg == symbol: break else: output.append(c) return output def unit_propagate_int_repr(clauses, s): """ Same as unit_propagate, but arguments are expected to be in integer representation >>> from sympy.logic.algorithms.dpll import unit_propagate_int_repr >>> unit_propagate_int_repr([set([1, 2]), set([3, -2]), set([2])], 2) [set([3])] """ negated = set([-s]) return [clause - negated for clause in clauses if s not in clause] def find_pure_symbol(symbols, unknown_clauses): """ Find a symbol and its value if it appears only as a positive literal (or only as a negative) in clauses. >>> from sympy import symbols >>> from sympy.abc import A, B, D >>> from sympy.logic.algorithms.dpll import find_pure_symbol >>> find_pure_symbol([A, B, D], [A|~B,~B|~D,D|A]) (A, True) """ for sym in symbols: found_pos, found_neg = False, False for c in unknown_clauses: if not found_pos and sym in disjuncts(c): found_pos = True if not found_neg and Not(sym) in disjuncts(c): found_neg = True if found_pos != found_neg: return sym, found_pos return None, None def find_pure_symbol_int_repr(symbols, unknown_clauses): """ Same as find_pure_symbol, but arguments are expected to be in integer representation >>> from sympy.logic.algorithms.dpll import find_pure_symbol_int_repr >>> find_pure_symbol_int_repr(set([1,2,3]), ... [set([1, -2]), set([-2, -3]), set([3, 1])]) (1, True) """ all_symbols = reduce(set.union, unknown_clauses, set()) found_pos = all_symbols.intersection(symbols) found_neg = all_symbols.intersection([-s for s in symbols]) for p in found_pos: if -p not in found_neg: return p, True for p in found_neg: if -p not in found_pos: return -p, False return None, None def find_unit_clause(clauses, model): """ A unit clause has only 1 variable that is not bound in the model. >>> from sympy import symbols >>> from sympy.abc import A, B, D >>> from sympy.logic.algorithms.dpll import find_unit_clause >>> find_unit_clause([A | B | D, B | ~D, A | ~B], {A:True}) (B, False) """ for clause in clauses: num_not_in_model = 0 for literal in disjuncts(clause): sym = literal_symbol(literal) if sym not in model: num_not_in_model += 1 P, value = sym, not (literal.func is Not) if num_not_in_model == 1: return P, value return None, None def find_unit_clause_int_repr(clauses, model): """ Same as find_unit_clause, but arguments are expected to be in integer representation. >>> from sympy.logic.algorithms.dpll import find_unit_clause_int_repr >>> find_unit_clause_int_repr([set([1, 2, 3]), ... set([2, -3]), set([1, -2])], {1: True}) (2, False) """ bound = set(model) | set(-sym for sym in model) for clause in clauses: unbound = clause - bound if len(unbound) == 1: p = unbound.pop() if p < 0: return -p, False else: return p, True return None, None sympy-0.7.4.1/sympy/logic/algorithms/dpll2.py0000644000175000017500000004562612253362407021277 0ustar georgeskgeorgesk"""Implementation of DPLL algorithm Features: - Clause learning - Watch literal scheme - VSIDS heuristic References: - http://en.wikipedia.org/wiki/DPLL_algorithm """ from __future__ import print_function, division from collections import defaultdict from heapq import heappush, heappop from sympy import default_sort_key from sympy.logic.boolalg import conjuncts, to_cnf, to_int_repr, _find_predicates def dpll_satisfiable(expr): """ Check satisfiability of a propositional sentence. It returns a model rather than True when it succeeds Examples ======== >>> from sympy.abc import A, B >>> from sympy.logic.algorithms.dpll2 import dpll_satisfiable >>> dpll_satisfiable(A & ~B) {A: True, B: False} >>> dpll_satisfiable(A & ~A) False """ symbols = sorted(_find_predicates(expr), key=default_sort_key) symbols_int_repr = range(1, len(symbols) + 1) clauses = conjuncts(to_cnf(expr)) clauses_int_repr = to_int_repr(clauses, symbols) solver = SATSolver(clauses_int_repr, symbols_int_repr, set()) result = solver._find_model() if not result: return result # Uncomment to confirm the solution is valid (hitting set for the clauses) #else: #for cls in clauses_int_repr: #assert solver.var_settings.intersection(cls) return dict((symbols[abs(lit) - 1], lit > 0) for lit in solver.var_settings) class SATSolver(object): """ Class for representing a SAT solver capable of finding a model to a boolean theory in conjunctive normal form. """ def __init__(self, clauses, variables, var_settings, heuristic='vsids', clause_learning='none', INTERVAL=500): self.var_settings = var_settings self.heuristic = heuristic self.is_unsatisfied = False self._unit_prop_queue = [] self.update_functions = [] self.INTERVAL = INTERVAL self._initialize_variables(variables) self._initialize_clauses(clauses) if 'vsids' == heuristic: self._vsids_init() self.heur_calculate = self._vsids_calculate self.heur_lit_assigned = self._vsids_lit_assigned self.heur_lit_unset = self._vsids_lit_unset self.heur_clause_added = self._vsids_clause_added # Note: Uncomment this if/when clause learning is enabled #self.update_functions.append(self._vsids_decay) else: raise NotImplementedError if 'simple' == clause_learning: self.add_learned_clause = self._simple_add_learned_clause self.compute_conflict = self.simple_compute_conflict self.update_functions.append(self.simple_clean_clauses) elif 'none' == clause_learning: self.add_learned_clause = lambda x: None self.compute_conflict = lambda: None else: raise NotImplementedError # Create the base level self.levels = [Level(0)] self._current_level.varsettings = var_settings # Keep stats self.num_decisions = 0 self.num_learned_clauses = 0 self.original_num_clauses = len(self.clauses) def _initialize_variables(self, variables): """Set up the variable data structures needed.""" self.sentinels = defaultdict(set) self.occurrence_count = defaultdict(int) self.variable_set = [False] * (len(variables) + 1) def _initialize_clauses(self, clauses): """Set up the clause data structures needed. For each clause, the following changes are made: - Unit clauses are queued for propagation right away. - Non-unit clauses have their first and last literals set as sentinels. - The number of clauses a literal appears in is computed. """ self.clauses = [] for cls in clauses: self.clauses.append(list(cls)) for i in range(len(self.clauses)): # Handle the unit clauses if 1 == len(self.clauses[i]): self._unit_prop_queue.append(self.clauses[i][0]) continue self.sentinels[self.clauses[i][0]].add(i) self.sentinels[self.clauses[i][-1]].add(i) for lit in self.clauses[i]: self.occurrence_count[lit] += 1 def _find_model(self): """Main DPLL loop. Variables are chosen successively, and assigned to be either True or False. If a solution is not found with this setting, the opposite is chosen and the search continues. The solver halts when every variable has a setting. Examples ======== >>> from sympy.logic.algorithms.dpll2 import SATSolver >>> SATSolver([set([-1]), set([1])], set([1]), set([]))._find_model() False >>> SATSolver([set([1]), set([-2])], set([-2, 3]), set([]))._find_model() True """ # We use this variable to keep track of if we should flip a # variable setting in successive rounds flip_var = False # Check if unit prop says the theory is unsat right off the bat self._simplify() if self.is_unsatisfied: return False # While the theory still has clauses remaining while True: # Perform cleanup / fixup at regular intervals if self.num_decisions % self.INTERVAL == 0: for func in self.update_functions: func() if flip_var: # We have just backtracked and we are trying to opposite literal flip_var = False lit = self._current_level.decision else: # Pick a literal to set lit = self.heur_calculate() self.num_decisions += 1 # Stopping condition for a satisfying theory if 0 == lit: return True # Start the new decision level self.levels.append(Level(lit)) # Assign the literal, updating the clauses it satisfies self._assign_literal(lit) # _simplify the theory self._simplify() # Check if we've made the theory unsat if self.is_unsatisfied: self.is_unsatisfied = False # We unroll all of the decisions until we can flip a literal while self._current_level.flipped: self._undo() # If we've unrolled all the way, the theory is unsat if 1 == len(self.levels): return False # Detect and add a learned clause self.add_learned_clause(self.compute_conflict()) # Try the opposite setting of the most recent decision flip_lit = -self._current_level.decision self._undo() self.levels.append(Level(flip_lit, flipped=True)) flip_var = True ######################## # Helper Methods # ######################## @property def _current_level(self): """The current decision level data structure Examples ======== >>> from sympy.logic.algorithms.dpll2 import SATSolver >>> l = SATSolver([set([1]), set([2])], set([1, 2]), set([])) >>> l._find_model() True >>> l._current_level.decision 0 >>> l._current_level.flipped False >>> l._current_level.var_settings set([1, 2]) """ return self.levels[-1] def _clause_sat(self, cls): """Check if a clause is satisfied by the current variable setting. Examples ======== >>> from sympy.logic.algorithms.dpll2 import SATSolver >>> l = SATSolver([set([1]), set([-1])], set([1]), set([])) >>> l._find_model() False >>> l._clause_sat(0) False >>> l._clause_sat(1) True """ for lit in self.clauses[cls]: if lit in self.var_settings: return True return False def _is_sentinel(self, lit, cls): """Check if a literal is a sentinel of a given clause. Examples ======== >>> from sympy.logic.algorithms.dpll2 import SATSolver >>> l = SATSolver([set([2, -3]), set([1]), set([3, -3]), set([2, -2]), ... set([3, -2])], set([1, 2, 3]), set([])) >>> l._find_model() True >>> l._is_sentinel(2, 3) True >>> l._is_sentinel(-3, 1) False """ return cls in self.sentinels[lit] def _assign_literal(self, lit): """Make a literal assignment. The literal assignment must be recorded as part of the current decision level. Additionally, if the literal is marked as a sentinel of any clause, then a new sentinel must be chosen. If this is not possible, then unit propagation is triggered and another literal is added to the queue to be set in the future. Examples ======== >>> from sympy.logic.algorithms.dpll2 import SATSolver >>> l = SATSolver([set([2, -3]), set([1]), set([3, -3]), set([2, -2]), ... set([3, -2])], set([1, 2, 3]), set([])) >>> l._find_model() True >>> l.var_settings set([-3, -2, 1]) >>> l = SATSolver([set([2, -3]), set([1]), set([3, -3]), set([2, -2]), ... set([3, -2])], set([1, 2, 3]), set([])) >>> l._assign_literal(-1) >>> l._find_model() False >>> l.var_settings set([-1]) """ self.var_settings.add(lit) self._current_level.var_settings.add(lit) self.variable_set[abs(lit)] = True self.heur_lit_assigned(lit) sentinel_list = list(self.sentinels[-lit]) for cls in sentinel_list: if not self._clause_sat(cls): other_sentinel = None for newlit in self.clauses[cls]: if newlit != -lit: if self._is_sentinel(newlit, cls): other_sentinel = newlit elif not self.variable_set[abs(newlit)]: self.sentinels[-lit].remove(cls) self.sentinels[newlit].add(cls) other_sentinel = None break # Check if no sentinel update exists if other_sentinel: self._unit_prop_queue.append(other_sentinel) def _undo(self): """ _undo the changes of the most recent decision level. Examples ======== >>> from sympy.logic.algorithms.dpll2 import SATSolver >>> l = SATSolver([set([2, -3]), set([1]), set([3, -3]), set([2, -2]), ... set([3, -2])], set([1, 2, 3]), set([])) >>> l._find_model() True >>> level = l._current_level >>> level.decision, level.var_settings, level.flipped (-3, set([-3, -2]), False) >>> l._undo() >>> level = l._current_level >>> level.decision, level.var_settings, level.flipped (0, set([1]), False) """ # Undo the variable settings for lit in self._current_level.var_settings: self.var_settings.remove(lit) self.heur_lit_unset(lit) self.variable_set[abs(lit)] = False # Pop the level off the stack self.levels.pop() ######################### # Propagation # ######################### """ Propagation methods should attempt to soundly simplify the boolean theory, and return True if any simplification occurred and False otherwise. """ def _simplify(self): """Iterate over the various forms of propagation to simplify the theory. Examples ======== >>> from sympy.logic.algorithms.dpll2 import SATSolver >>> l = SATSolver([set([2, -3]), set([1]), set([3, -3]), set([2, -2]), ... set([3, -2])], set([1, 2, 3]), set([])) >>> l.variable_set [False, False, False, False] >>> l.sentinels {-3: set([0, 2]), -2: set([3, 4]), 2: set([0, 3]), 3: set([2, 4])} >>> l._simplify() >>> l.variable_set [False, True, False, False] >>> l.sentinels {-3: set([0, 2]), -2: set([3, 4]), -1: set(), 2: set([0, 3]), ...3: set([2, 4])} """ changed = True while changed: changed = False changed |= self._unit_prop() changed |= self._pure_literal() def _unit_prop(self): """Perform unit propagation on the current theory.""" result = len(self._unit_prop_queue) > 0 while self._unit_prop_queue: next_lit = self._unit_prop_queue.pop() if -next_lit in self.var_settings: self.is_unsatisfied = True self._unit_prop_queue = [] return False else: self._assign_literal(next_lit) return result def _pure_literal(self): """Look for pure literals and assign them when found.""" return False ######################### # Heuristics # ######################### def _vsids_init(self): """Initialize the data structures needed for the VSIDS heuristic.""" self.lit_heap = [] self.lit_scores = {} for var in range(1, len(self.variable_set)): self.lit_scores[var] = float(-self.occurrence_count[var]) self.lit_scores[-var] = float(-self.occurrence_count[-var]) heappush(self.lit_heap, (self.lit_scores[var], var)) heappush(self.lit_heap, (self.lit_scores[-var], -var)) def _vsids_decay(self): """Decay the VSIDS scores for every literal. Examples ======== >>> from sympy.logic.algorithms.dpll2 import SATSolver >>> l = SATSolver([set([2, -3]), set([1]), set([3, -3]), set([2, -2]), ... set([3, -2])], set([1, 2, 3]), set([])) >>> l.lit_scores {-3: -2.0, -2: -2.0, -1: 0.0, 1: 0.0, 2: -2.0, 3: -2.0} >>> l._vsids_decay() >>> l.lit_scores {-3: -1.0, -2: -1.0, -1: 0.0, 1: 0.0, 2: -1.0, 3: -1.0} """ # We divide every literal score by 2 for a decay factor # Note: This doesn't change the heap property for lit in self.lit_scores.keys(): self.lit_scores[lit] /= 2.0 def _vsids_calculate(self): """ VSIDS Heuristic Calculation Examples ======== >>> from sympy.logic.algorithms.dpll2 import SATSolver >>> l = SATSolver([set([2, -3]), set([1]), set([3, -3]), set([2, -2]), ... set([3, -2])], set([1, 2, 3]), set([])) >>> l.lit_heap [(-2.0, -3), (-2.0, 2), (-2.0, -2), (0.0, 1), (-2.0, 3), (0.0, -1)] >>> l._vsids_calculate() -3 >>> l.lit_heap [(-2.0, -2), (-2.0, 2), (0.0, -1), (0.0, 1), (-2.0, 3)] """ if len(self.lit_heap) == 0: return 0 # Clean out the front of the heap as long the variables are set while self.variable_set[abs(self.lit_heap[0][1])]: heappop(self.lit_heap) if len(self.lit_heap) == 0: return 0 return heappop(self.lit_heap)[1] def _vsids_lit_assigned(self, lit): """Handle the assignment of a literal for the VSIDS heuristic.""" pass def _vsids_lit_unset(self, lit): """Handle the unsetting of a literal for the VSIDS heuristic. Examples ======== >>> from sympy.logic.algorithms.dpll2 import SATSolver >>> l = SATSolver([set([2, -3]), set([1]), set([3, -3]), set([2, -2]), ... set([3, -2])], set([1, 2, 3]), set([])) >>> l.lit_heap [(-2.0, -3), (-2.0, 2), (-2.0, -2), (0.0, 1), (-2.0, 3), (0.0, -1)] >>> l._vsids_lit_unset(2) >>> l.lit_heap [(-2.0, -3), (-2.0, -2), (-2.0, -2), (-2.0, 2), (-2.0, 3), (0.0, -1), ...(-2.0, 2), (0.0, 1)] """ var = abs(lit) heappush(self.lit_heap, (self.lit_scores[var], var)) heappush(self.lit_heap, (self.lit_scores[-var], -var)) def _vsids_clause_added(self, cls): """Handle the addition of a new clause for the VSIDS heuristic. Examples ======== >>> from sympy.logic.algorithms.dpll2 import SATSolver >>> l = SATSolver([set([2, -3]), set([1]), set([3, -3]), set([2, -2]), ... set([3, -2])], set([1, 2, 3]), set([])) >>> l.num_learned_clauses 0 >>> l.lit_scores {-3: -2.0, -2: -2.0, -1: 0.0, 1: 0.0, 2: -2.0, 3: -2.0} >>> l._vsids_clause_added(set([2, -3])) >>> l.num_learned_clauses 1 >>> l.lit_scores {-3: -1.0, -2: -2.0, -1: 0.0, 1: 0.0, 2: -1.0, 3: -2.0} """ self.num_learned_clauses += 1 for lit in cls: self.lit_scores[lit] += 1 ######################## # Clause Learning # ######################## def _simple_add_learned_clause(self, cls): """Add a new clause to the theory. Examples ======== >>> from sympy.logic.algorithms.dpll2 import SATSolver >>> l = SATSolver([set([2, -3]), set([1]), set([3, -3]), set([2, -2]), ... set([3, -2])], set([1, 2, 3]), set([])) >>> l.num_learned_clauses 0 >>> l.clauses [[2, -3], [1], [3, -3], [2, -2], [3, -2]] >>> l.sentinels {-3: set([0, 2]), -2: set([3, 4]), 2: set([0, 3]), 3: set([2, 4])} >>> l._simple_add_learned_clause([3]) >>> l.clauses [[2, -3], [1], [3, -3], [2, -2], [3, -2], [3]] >>> l.sentinels {-3: set([0, 2]), -2: set([3, 4]), 2: set([0, 3]), 3: set([2, 4, 5])} """ cls_num = len(self.clauses) self.clauses.append(cls) for lit in cls: self.occurrence_count[lit] += 1 self.sentinels[cls[0]].add(cls_num) self.sentinels[cls[-1]].add(cls_num) self.heur_clause_added(cls) def _simple_compute_conflict(self): """ Build a clause representing the fact that at least one decision made so far is wrong. Examples ======== >>> from sympy.logic.algorithms.dpll2 import SATSolver >>> l = SATSolver([set([2, -3]), set([1]), set([3, -3]), set([2, -2]), ... set([3, -2])], set([1, 2, 3]), set([])) >>> l._find_model() True >>> l._simple_compute_conflict() [3] """ return [-(level.decision) for level in self.levels[1:]] def _simple_clean_clauses(self): """Clean up learned clauses.""" pass class Level(object): """ Represents a single level in the DPLL algorithm, and contains enough information for a sound backtracking procedure. """ def __init__(self, decision, flipped=False): self.decision = decision self.var_settings = set() self.flipped = flipped sympy-0.7.4.1/sympy/logic/algorithms/__init__.py0000644000175000017500000000000012253362407021772 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/logic/benchmarks/0000755000175000017500000000000012253362407017637 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/logic/benchmarks/input/0000755000175000017500000000000012253362407020776 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/logic/benchmarks/input/90.cnf0000644000175000017500000001116212253362407021717 0ustar georgeskgeorgeskc c SAT instance in DIMACS CNF input format. c p cnf 90 382 9 58 -89 0 -5 -26 -2 0 -53 -25 -26 0 28 68 41 0 -54 -42 16 0 67 50 16 0 -7 69 80 0 -29 68 81 0 -68 -19 48 0 -88 -30 -25 0 -77 40 1 0 -14 -66 -63 0 -58 -88 87 0 -2 35 22 0 -58 -34 13 0 78 63 -6 0 90 36 -61 0 35 31 75 0 65 -79 78 0 -38 13 -17 0 -13 -67 -52 0 27 48 -23 0 -55 85 -73 0 38 -51 23 0 -4 52 -7 0 63 19 83 0 -39 -34 68 0 -32 76 -90 0 -70 -88 -44 0 23 -49 -22 0 32 51 11 0 60 -29 -80 0 -39 -85 31 0 46 -20 -85 0 54 -62 88 0 9 51 4 0 90 50 -82 0 13 90 -26 0 42 84 29 0 -5 65 -68 0 42 -22 -45 0 -44 48 -29 0 54 45 77 0 -12 27 8 0 -85 -7 49 0 -43 76 -89 0 -82 -39 31 0 16 -77 21 0 57 -79 -60 0 84 -30 8 0 47 -31 81 0 62 19 11 0 -59 -85 -4 0 -31 54 53 0 75 -54 -9 0 -54 21 36 0 66 -52 -60 0 -21 38 -90 0 66 2 -9 0 31 -70 69 0 -39 -10 72 0 -81 86 32 0 25 -45 -31 0 87 -9 -55 0 -48 -22 -33 0 -60 -61 22 0 88 -26 18 0 52 -65 -86 0 56 -65 -71 0 56 70 -73 0 -49 -38 82 0 -68 76 -37 0 57 -7 65 0 -75 -53 -89 0 78 19 -84 0 71 21 -2 0 34 -26 1 0 -16 5 3 0 -43 87 -71 0 -33 38 49 0 19 -34 58 0 -33 56 48 0 -63 82 29 0 -35 31 61 0 18 -85 62 0 19 -44 -39 0 -45 12 24 0 60 -61 -7 0 81 43 -56 0 32 38 61 0 -9 -82 52 0 16 21 -1 0 -53 -48 78 0 90 41 74 0 -40 70 1 0 73 -69 84 0 43 26 14 0 -58 44 84 0 69 46 -67 0 24 2 12 0 -66 4 6 0 72 6 -44 0 -21 -27 -71 0 29 74 -18 0 -23 -87 48 0 22 -61 -16 0 73 -47 52 0 60 84 -36 0 2 61 12 0 -89 -83 73 0 10 52 -24 0 73 -23 -84 0 -6 -12 89 0 -25 -52 -46 0 26 -70 -67 0 -69 34 47 0 80 67 -23 0 -55 -61 -64 0 -25 50 -59 0 -1 17 -84 0 -21 74 -13 0 -71 44 1 0 -64 62 -4 0 -32 -1 -58 0 81 37 -43 0 41 -68 -8 0 -12 -22 44 0 61 77 -88 0 1 22 71 0 -55 -6 -73 0 -86 76 -53 0 -32 35 79 0 -56 -43 23 0 -55 -67 -72 0 7 -1 26 0 -86 62 14 0 -45 -43 -85 0 79 67 -3 0 58 -2 -75 0 -59 -27 70 0 76 89 78 0 53 -20 -75 0 17 72 -27 0 64 20 38 0 41 82 46 0 83 5 62 0 -10 57 -5 0 52 66 68 0 -10 -65 -3 0 14 -30 85 0 -79 59 85 0 -46 -84 -35 0 78 -21 76 0 -26 -84 23 0 -82 -48 -15 0 16 17 36 0 45 -48 -77 0 -88 -71 38 0 -83 -18 90 0 -42 15 7 0 77 45 -39 0 26 -52 2 0 -27 -73 6 0 -88 77 -2 0 9 44 -41 0 -77 -10 -11 0 68 58 -16 0 35 -71 -40 0 -80 -44 -12 0 42 -17 31 0 -76 -77 -59 0 -82 50 59 0 16 89 50 0 65 -5 23 0 42 -90 -21 0 18 84 -30 0 -74 12 53 0 -72 -30 -39 0 17 87 -18 0 -68 -15 -85 0 89 -29 -90 0 -71 -68 8 0 88 71 59 0 38 6 12 0 81 -36 -37 0 82 76 66 0 38 85 8 0 -72 58 -30 0 3 -7 76 0 12 46 86 0 68 -74 -17 0 57 -63 -3 0 -39 65 -34 0 6 -22 -37 0 -35 76 -45 0 10 -63 -1 0 -51 -45 -20 0 8 -44 -43 0 39 63 28 0 83 48 31 0 -57 85 51 0 17 10 -61 0 39 -16 73 0 90 -25 -51 0 38 17 39 0 -38 -60 5 0 5 28 -23 0 -58 -76 -40 0 -83 -55 46 0 -46 49 -87 0 -61 -32 -11 0 11 -80 -52 0 85 79 82 0 -88 -58 18 0 28 56 -88 0 26 16 68 0 -49 -62 17 0 90 47 -50 0 -61 -90 -82 0 -63 14 67 0 -34 -71 83 0 6 -39 50 0 68 73 -42 0 73 -36 72 0 51 17 35 0 -35 -14 -48 0 -25 -78 -81 0 -13 -85 -50 0 -54 -84 21 0 -70 -73 -82 0 -2 86 -51 0 -25 86 17 0 -66 80 -73 0 41 22 86 0 -7 32 -31 0 89 -81 -36 0 74 22 -21 0 41 26 21 0 -3 76 -40 0 -50 72 24 0 51 -64 -70 0 -11 13 56 0 71 8 -82 0 64 -43 -76 0 51 -11 -82 0 -65 72 -71 0 -21 -16 37 0 54 -62 -26 0 13 -2 78 0 -77 58 -64 0 -11 47 -79 0 -53 -80 8 0 -58 -83 -17 0 -42 40 24 0 69 75 9 0 27 -74 -33 0 -48 69 -41 0 62 89 11 0 20 -33 -27 0 -1 -18 -4 0 61 24 -80 0 -59 56 44 0 27 90 37 0 66 24 10 0 -75 -11 14 0 -72 54 34 0 54 -55 76 0 81 -16 -22 0 52 -13 11 0 20 -13 -14 0 71 -43 -22 0 -54 52 44 0 -68 89 10 0 60 58 21 0 -14 -50 -38 0 28 20 78 0 -5 -39 -72 0 -36 -2 -20 0 17 -47 -30 0 -69 48 37 0 -4 -16 5 0 -2 89 14 0 4 -6 -66 0 14 -9 -63 0 -23 -33 -1 0 34 40 -15 0 15 76 1 0 -65 69 -83 0 -87 -23 -16 0 25 16 33 0 -10 -86 66 0 84 62 56 0 -7 -24 -73 0 -28 62 32 0 83 -23 46 0 27 -26 -37 0 34 80 46 0 -42 -4 -47 0 -68 -45 78 0 70 -23 62 0 -76 -55 45 0 -22 9 70 0 59 -88 -60 0 82 -85 -80 0 -29 25 74 0 -20 -62 -77 0 68 -76 18 0 61 -72 69 0 13 44 -63 0 69 7 9 0 83 86 -34 0 -82 -77 -89 0 30 -67 71 0 44 33 -28 0 -18 -81 50 0 -20 59 -89 0 -68 42 14 0 -81 -47 31 0 23 -9 -22 0 38 -18 88 0 -13 74 -81 0 -63 -13 23 0 -42 68 21 0 -49 87 -8 0 -82 60 -71 0 38 -68 -80 0 -47 -73 -1 0 -54 -16 11 0 56 53 90 0 -81 -59 -48 0 -40 -23 -87 0 13 -60 -28 0 3 -87 22 0 -31 83 80 0 -10 87 -80 0 86 87 -11 0 -61 58 11 0 14 25 -16 0 51 59 41 0 -65 -49 -53 0 3 -11 37 0 62 -9 77 0 -56 20 -40 0 81 65 -55 0 -10 14 -11 0 -47 -34 -40 0 88 -29 5 0 8 89 -10 0 50 27 -87 0 -58 56 -5 0 67 82 16 0 -34 -33 9 0 39 -61 72 0 -53 32 -14 0 5 -13 -49 0 -66 71 -39 0 26 -59 8 0 43 -8 18 0 -48 -58 -73 0 -35 -47 -50 0 -35 -16 21 0 -25 55 24 0 84 -48 -28 0 4 24 -90 0 65 37 46 0 -26 -90 83 0 -69 34 74 0 -6 84 -87 0 52 -31 79 0 9 -3 68 0 -90 -12 -1 0 -45 32 -41 0 39 19 14 0 -4 -82 21 0 -56 -5 15 0 -6 21 59 0 16 10 73 0 -46 63 -33 0 -9 -27 17 0 12 18 57 0 -71 48 62 0 -71 14 75 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/115.cnf0000644000175000017500000001406212253362407021777 0ustar georgeskgeorgeskp cnf 115 488 46 -8 -79 0 66 -28 77 0 -67 -99 32 0 -12 22 19 0 -85 104 113 0 -60 16 30 0 95 -37 -26 0 -19 -57 63 0 -95 49 29 0 11 5 71 0 29 -80 -113 0 44 -69 -25 0 -26 88 -18 0 82 30 -75 0 35 -55 -110 0 44 35 43 0 -90 -3 113 0 -113 -68 -10 0 54 98 -10 0 -72 54 39 0 -53 57 47 0 -48 -92 71 0 51 27 -14 0 35 37 -68 0 68 -6 -115 0 -2 72 30 0 -43 56 -109 0 28 36 -89 0 -38 17 -61 0 42 -63 -76 0 -100 -19 42 0 -56 90 -14 0 -78 -94 53 0 -35 -54 39 0 -101 -59 -16 0 65 20 33 0 12 100 9 0 100 -83 -22 0 -45 -73 48 0 91 95 32 0 4 21 -29 0 -89 61 -15 0 -37 63 111 0 7 -105 -40 0 -105 33 46 0 -66 -47 -59 0 73 -23 112 0 -34 -64 77 0 -53 -28 -54 0 108 50 34 0 -84 -88 -43 0 -109 65 -101 0 8 94 -115 0 -41 50 2 0 -91 50 -82 0 -76 59 68 0 -73 -65 -22 0 47 -45 -92 0 -76 -52 22 0 -81 -82 -75 0 -9 2 25 0 -22 62 87 0 -61 8 -58 0 77 -39 -25 0 103 -24 -74 0 -108 -25 81 0 -92 49 -81 0 -36 55 -14 0 35 94 -69 0 -55 -103 -7 0 74 -7 9 0 -82 -11 92 0 -44 52 -84 0 -67 101 63 0 103 -44 -84 0 105 -75 97 0 88 -24 66 0 77 19 92 0 -30 -52 20 0 -42 57 -24 0 -73 63 115 0 -84 69 -68 0 88 -115 75 0 -52 17 -81 0 52 93 -92 0 -93 -41 -95 0 -85 103 -48 0 35 36 -108 0 -19 54 111 0 -59 -43 -24 0 -79 -61 89 0 -92 60 -8 0 106 -18 84 0 -69 41 105 0 -50 23 95 0 -91 -18 -93 0 3 33 12 0 57 -12 109 0 -85 108 -115 0 103 -45 -96 0 76 59 -65 0 -69 32 22 0 61 45 51 0 79 53 66 0 97 -89 -5 0 61 48 -15 0 68 21 -47 0 104 -23 42 0 -59 86 -83 0 -12 -77 16 0 -103 -18 23 0 -84 -95 20 0 47 61 54 0 -104 -26 112 0 -78 -65 108 0 -44 -9 -106 0 24 71 90 0 77 -100 -86 0 78 -14 101 0 -87 21 8 0 35 -4 14 0 -38 -91 -105 0 -26 -102 6 0 -39 -105 -36 0 -60 98 20 0 -88 115 58 0 41 -114 19 0 -6 -42 -59 0 35 -78 45 0 -57 34 15 0 74 -2 -84 0 86 107 -105 0 98 74 -44 0 -59 -20 61 0 95 12 -115 0 33 9 -1 0 36 -108 -41 0 -33 21 -61 0 47 -16 -106 0 95 48 24 0 7 -53 70 0 -74 105 -1 0 -87 -16 -103 0 -82 -66 -50 0 115 -32 45 0 91 -98 69 0 -53 87 32 0 -13 41 -58 0 -113 20 -14 0 113 -88 -81 0 51 -78 91 0 67 -39 -32 0 54 -52 72 0 85 -49 -114 0 -67 -30 109 0 42 -23 67 0 -105 6 84 0 -31 87 -2 0 9 61 112 0 76 -20 -39 0 33 90 97 0 -78 -71 34 0 -76 -71 59 0 -105 -35 -71 0 -10 112 -111 0 -8 60 83 0 -106 -42 -57 0 109 81 -58 0 5 -38 -77 0 -36 -85 -5 0 -73 -20 86 0 16 -57 111 0 -73 -23 -16 0 11 12 50 0 88 80 -111 0 -5 -110 70 0 -8 -105 -84 0 23 -28 60 0 -27 77 102 0 -3 76 -78 0 47 -58 -49 0 -94 110 60 0 14 90 -99 0 -66 -90 39 0 54 -56 65 0 -14 -108 -20 0 -39 56 95 0 25 65 36 0 10 -80 75 0 79 -30 -102 0 -68 -65 -72 0 -8 -16 -92 0 64 8 -43 0 -106 -82 62 0 -112 -22 26 0 59 113 -70 0 -98 29 -61 0 -51 -26 15 0 53 -36 -81 0 8 3 -88 0 -5 90 11 0 68 -42 -88 0 77 50 -93 0 34 -90 -75 0 74 65 -21 0 62 33 -3 0 64 -76 39 0 98 113 38 0 -112 113 -83 0 -9 -114 -94 0 86 61 -36 0 -96 -58 88 0 67 115 -110 0 47 -115 -10 0 50 -88 99 0 -45 19 -17 0 18 -10 -44 0 1 64 -19 0 -48 93 6 0 -105 -63 -71 0 -105 -99 -19 0 97 19 -68 0 16 -30 29 0 14 75 46 0 11 108 85 0 109 115 45 0 78 110 73 0 -59 90 -87 0 -88 -17 -101 0 49 45 114 0 -7 61 -8 0 -57 55 84 0 -62 20 -12 0 -79 95 -99 0 24 -54 -53 0 71 15 -90 0 16 99 -36 0 -51 -7 10 0 -71 80 18 0 -53 -28 44 0 -29 41 -105 0 -6 17 -34 0 -53 2 79 0 -108 37 76 0 57 -66 -81 0 93 90 -1 0 -43 -95 -55 0 -3 -51 -18 0 -25 -12 -17 0 -69 -57 -26 0 97 9 53 0 43 38 91 0 -6 20 98 0 -45 -73 6 0 88 -26 109 0 -50 -103 58 0 24 -63 113 0 -80 -89 -100 0 72 103 37 0 -92 -30 -45 0 -12 64 51 0 -53 56 -86 0 -4 -25 78 0 81 115 24 0 51 20 103 0 -33 74 -87 0 -60 -19 -12 0 -111 37 12 0 75 -104 47 0 -70 2 34 0 72 6 78 0 -16 84 -52 0 25 89 -44 0 90 101 -105 0 -10 107 16 0 -88 23 64 0 114 -102 -44 0 90 -56 30 0 86 -84 25 0 -21 36 92 0 83 66 7 0 45 -16 47 0 -18 11 -97 0 78 16 18 0 103 91 100 0 103 17 -64 0 69 -49 5 0 -39 -90 105 0 -58 82 113 0 -60 -101 64 0 39 19 -30 0 -64 24 -55 0 -98 46 -90 0 -100 -88 72 0 -41 -47 64 0 -18 26 85 0 110 14 4 0 -88 -102 113 0 -17 -8 19 0 2 34 8 0 112 46 -93 0 43 86 45 0 -36 -51 -35 0 -84 83 104 0 7 -23 42 0 55 -71 101 0 -10 -94 78 0 -9 -106 -78 0 35 108 -4 0 -30 -47 19 0 -5 63 -103 0 -18 70 -64 0 24 -78 91 0 46 -27 -4 0 51 -59 93 0 -42 -103 -2 0 -32 -84 42 0 -54 95 -8 0 52 -31 77 0 77 91 -5 0 -41 -103 -9 0 87 6 12 0 -59 -103 -29 0 89 3 19 0 115 -113 86 0 105 -22 -33 0 18 99 107 0 56 55 2 0 52 78 -44 0 -27 79 -32 0 70 113 28 0 51 -113 3 0 10 -14 -84 0 31 -20 62 0 17 64 52 0 45 -43 -1 0 -92 -105 -27 0 17 96 -19 0 76 87 -49 0 21 55 -36 0 -50 51 69 0 -47 90 55 0 -53 -16 -25 0 100 -20 71 0 7 101 87 0 -71 106 -115 0 -52 -3 -87 0 57 16 45 0 53 13 101 0 90 82 -111 0 115 -52 51 0 53 -54 109 0 -84 43 -111 0 -48 -43 -36 0 81 -98 43 0 -107 78 -83 0 -86 26 -37 0 -79 93 -67 0 8 -102 101 0 -9 -17 4 0 -114 -101 67 0 51 72 69 0 -81 -11 -85 0 -82 24 -96 0 27 85 90 0 -54 23 -92 0 27 115 -112 0 -61 -105 -50 0 39 -35 64 0 -107 13 55 0 -18 -111 -80 0 -106 7 10 0 53 54 -34 0 -10 70 60 0 -78 52 11 0 77 22 95 0 16 39 -88 0 104 -32 15 0 71 10 -87 0 39 -46 79 0 -68 110 95 0 -68 -29 -31 0 -33 59 -37 0 -97 44 -11 0 70 75 -29 0 -20 -5 36 0 -8 -35 -68 0 -43 17 85 0 41 109 52 0 -87 -32 -11 0 23 -60 -66 0 27 -86 -10 0 105 113 79 0 39 -57 -28 0 -52 74 -38 0 70 115 1 0 91 84 114 0 -68 42 -113 0 14 18 -11 0 33 -81 52 0 64 -16 32 0 69 6 -23 0 51 -102 -100 0 -60 14 -36 0 99 -78 104 0 -15 -96 -45 0 -34 -27 26 0 104 114 1 0 53 58 -84 0 -73 62 105 0 96 61 -93 0 53 -30 39 0 105 2 -83 0 114 -86 -55 0 107 72 -96 0 -92 -72 110 0 -15 -6 91 0 -82 -21 13 0 44 105 -13 0 7 98 47 0 -42 110 -1 0 86 84 51 0 59 -42 101 0 95 106 -18 0 -87 -86 -78 0 -61 31 -58 0 -111 78 21 0 77 91 17 0 -21 -95 -10 0 3 15 24 0 41 -38 40 0 15 51 -60 0 -12 44 54 0 -65 -102 35 0 -99 61 5 0 -24 95 -61 0 63 16 -7 0 -24 79 -37 0 -94 10 106 0 25 113 53 0 90 -25 30 0 6 -24 45 0 113 -67 87 0 -80 47 -50 0 87 102 -7 0 -70 -82 -46 0 -41 -24 -71 0 82 -53 95 0 114 -60 -107 0 -62 43 -50 0 25 6 -88 0 40 -50 4 0 41 59 74 0 68 -78 51 0 -12 -13 101 0 35 -87 -100 0 106 -96 -98 0 -7 -62 -41 0 27 -77 51 0 18 -95 -59 0 40 -92 -51 0 72 82 -79 0 17 74 39 0 -64 -41 48 0 -20 -91 -98 0 13 51 -42 0 108 -85 -25 0 -5 -20 -40 0 55 10 20 0 82 -17 -72 0 91 -64 85 0 6 -14 104 0 -84 114 -69 0 112 -14 -79 0 -90 17 21 0 -71 -54 -41 0 39 84 97 0 111 -71 95 0 83 112 -110 0 16 -49 65 0 -18 7 10 0 -83 -47 -82 0 -10 75 -99 0 43 -46 -25 0 50 80 30 0 103 52 -59 0 -52 15 27 0 108 -90 97 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/15.cnf0000644000175000017500000000134412253362407021715 0ustar georgeskgeorgeskc c SAT instance in DIMACS CNF input format. c p cnf 15 63 -9 2 -14 0 -11 -1 -9 0 -14 1 -6 0 10 14 -8 0 3 -12 -13 0 -2 5 -10 0 3 4 -11 0 5 -11 -8 0 -15 -11 10 0 8 -9 -7 0 -7 8 6 0 -6 -2 8 0 4 2 -9 0 13 1 11 0 -15 -7 -10 0 -5 -12 -10 0 7 4 -5 0 11 7 -10 0 -9 8 -5 0 -9 -6 5 0 -13 9 3 0 13 -3 10 0 15 -10 -14 0 13 1 4 0 -9 12 14 0 -12 -10 -13 0 -6 1 13 0 5 -12 10 0 10 13 -2 0 -14 6 -11 0 11 10 -12 0 12 -7 -6 0 -10 8 -15 0 -1 9 -2 0 -13 9 -11 0 -11 7 -15 0 -3 14 -8 0 8 3 -15 0 3 1 7 0 -8 -15 -6 0 -15 5 -12 0 8 7 14 0 -5 11 8 0 2 -10 -1 0 -9 11 -2 0 -13 15 -6 0 5 -3 -10 0 -10 15 -8 0 8 14 10 0 -1 -11 -14 0 -11 -10 5 0 -2 4 3 0 8 6 -9 0 9 -3 -7 0 -7 -12 -11 0 5 -13 1 0 11 5 -4 0 -9 10 -1 0 -12 7 2 0 6 8 5 0 11 -7 6 0 8 -12 3 0 -8 13 4 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/60.cnf0000644000175000017500000000612212253362407021714 0ustar georgeskgeorgeskc c SAT instance in DIMACS CNF input format. c p cnf 60 255 24 -42 -57 0 18 4 -60 0 -40 29 -19 0 -32 7 2 0 39 -36 -40 0 16 -24 30 0 -57 -36 -29 0 -1 26 -11 0 21 -54 41 0 51 30 -25 0 32 12 -36 0 57 31 -60 0 -9 -26 -3 0 22 16 -60 0 13 21 18 0 14 -35 -47 0 3 -53 -39 0 -40 -33 36 0 38 22 45 0 -7 -8 -30 0 58 -25 -33 0 -13 -25 -60 0 44 10 57 0 -30 15 -50 0 -12 -49 -6 0 -46 53 30 0 12 -45 47 0 -30 53 -14 0 -4 -52 -36 0 59 -45 -56 0 -39 46 -58 0 34 59 -51 0 1 25 -8 0 45 -56 58 0 -4 28 -53 0 -19 50 -30 0 9 -60 14 0 26 -58 -12 0 -59 54 31 0 -19 27 -43 0 34 32 2 0 34 22 -54 0 -30 -55 24 0 -24 -29 16 0 20 -26 22 0 -31 -16 46 0 32 -50 38 0 23 1 39 0 -33 24 -11 0 -11 -1 17 0 20 -40 -32 0 42 -12 -15 0 25 35 -59 0 -17 -27 43 0 -3 29 43 0 49 -59 52 0 15 -38 55 0 -52 -14 -57 0 -52 -4 6 0 -28 -23 14 0 32 -21 -50 0 -27 -48 -49 0 -38 -56 44 0 -44 -53 39 0 -19 22 45 0 -7 -18 -59 0 15 23 -48 0 -23 -56 43 0 -17 -56 -15 0 33 51 -44 0 53 22 23 0 14 -13 9 0 53 38 -42 0 -14 -6 -60 0 60 6 -45 0 -48 -20 -29 0 39 24 38 0 29 9 24 0 -4 57 -30 0 9 -55 -57 0 56 -7 -53 0 18 -57 44 0 -8 35 -1 0 -16 -13 -35 0 -53 59 -13 0 30 8 53 0 30 -33 -24 0 27 -46 -34 0 -28 34 -3 0 32 23 -30 0 -54 52 41 0 -24 -48 -59 0 10 17 -38 0 -23 -15 -5 0 -52 55 26 0 47 2 26 0 54 11 -9 0 45 20 -27 0 39 48 43 0 -26 7 17 0 60 -59 2 0 11 50 36 0 34 13 1 0 16 -11 41 0 -21 -31 20 0 -53 -43 44 0 -12 32 -23 0 52 -49 -2 0 28 -44 -38 0 58 -56 17 0 31 -38 -6 0 -43 38 -27 0 -48 -5 -4 0 -49 -41 -3 0 6 -50 36 0 -3 -31 47 0 -39 46 47 0 22 -55 -23 0 -7 8 37 0 14 44 1 0 -34 -19 -23 0 36 46 -56 0 19 -9 26 0 4 17 -48 0 -29 -60 19 0 -43 -38 22 0 58 23 5 0 -19 -12 13 0 -19 8 57 0 15 -9 -4 0 39 -8 25 0 60 -37 -36 0 -22 26 29 0 -60 -39 -13 0 1 53 -58 0 -23 13 12 0 -4 40 47 0 1 -39 -55 0 -7 -30 29 0 -10 -17 25 0 -28 -53 -56 0 15 48 34 0 40 -36 47 0 29 41 37 0 54 -57 -29 0 1 44 -33 0 34 -14 40 0 23 32 30 0 -15 -39 -8 0 27 -35 -59 0 -29 52 -38 0 -3 -12 -26 0 15 -4 -24 0 -27 -32 -47 0 -38 37 5 0 54 42 37 0 4 7 18 0 -47 46 9 0 -34 -24 44 0 -38 -15 -47 0 10 46 21 0 29 5 -39 0 45 13 -40 0 5 -44 31 0 -48 35 54 0 26 -56 -50 0 50 -40 -43 0 20 -51 -24 0 -21 -7 -29 0 13 -43 48 0 -59 -49 9 0 -51 35 41 0 -23 45 20 0 -54 -53 33 0 -19 21 -15 0 28 16 24 0 -23 46 -14 0 13 -32 -12 0 -18 5 22 0 59 17 56 0 6 -22 -52 0 -10 13 21 0 41 43 57 0 36 -28 -60 0 -50 -29 34 0 33 -9 55 0 44 -5 -16 0 -38 3 52 0 8 16 28 0 -52 51 -54 0 -35 -60 -43 0 15 52 51 0 52 -26 -53 0 23 -36 -3 0 -51 32 28 0 -59 52 1 0 33 -15 1 0 -53 -20 31 0 3 -27 -60 0 -32 -54 -11 0 -38 15 12 0 -35 46 29 0 29 -18 -2 0 23 51 31 0 -23 -14 -17 0 -34 60 51 0 -18 36 8 0 -55 -51 -45 0 7 32 -15 0 -52 42 -36 0 19 -26 -2 0 -31 -20 -59 0 32 -37 -12 0 1 19 57 0 -58 -52 -47 0 -59 37 -15 0 13 40 -48 0 22 17 20 0 40 -17 -58 0 53 21 1 0 38 -13 -28 0 -50 -48 -23 0 28 20 18 0 30 -59 -35 0 -39 26 -36 0 -60 32 29 0 -9 56 15 0 -12 -44 37 0 1 -58 -42 0 -8 22 51 0 42 -56 -46 0 58 23 -45 0 -20 -33 52 0 55 -30 57 0 -1 45 -8 0 26 -11 -38 0 38 5 46 0 -15 -32 3 0 39 13 -15 0 48 -17 -27 0 13 -6 25 0 12 13 -28 0 -40 32 -14 0 56 48 44 0 -11 28 40 0 -12 -29 -40 0 -52 10 -30 0 53 5 -30 0 -14 12 52 0 20 -43 -7 0 -5 -46 56 0 -8 -32 33 0 13 -49 -43 0 11 -49 -3 0 -21 54 -36 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/70.cnf0000644000175000017500000000714612253362407021724 0ustar georgeskgeorgeskc c SAT instance in DIMACS CNF input format. c p cnf 70 297 -48 24 -39 0 -11 49 32 0 37 -55 -45 0 -18 41 55 0 -51 -44 -8 0 -21 60 -51 0 39 16 -2 0 40 29 -45 0 67 -20 11 0 26 66 18 0 -61 -62 7 0 -48 -44 39 0 50 37 38 0 -15 -51 -52 0 -62 64 52 0 20 -26 28 0 57 -37 25 0 61 57 38 0 17 -29 -14 0 56 -19 -16 0 38 -22 61 0 48 -46 11 0 -66 -14 -1 0 32 60 39 0 54 42 -69 0 -22 -26 -2 0 -7 -5 9 0 -10 23 -37 0 -63 70 -12 0 4 -63 65 0 66 11 56 0 -7 -17 70 0 -54 9 -4 0 48 13 8 0 -64 50 -58 0 -31 -48 -54 0 -18 -9 -31 0 -62 -69 13 0 9 43 -26 0 -2 67 14 0 69 41 -67 0 -7 -70 67 0 -38 -58 3 0 43 45 -63 0 59 22 -68 0 13 69 43 0 33 3 -14 0 68 55 2 0 3 -35 53 0 -4 -34 -68 0 63 -14 -51 0 15 -42 10 0 -60 39 -47 0 -37 22 -4 0 37 62 8 0 -55 -33 47 0 33 -12 -1 0 -8 60 -61 0 41 -48 -39 0 -34 58 -55 0 -68 -48 -35 0 -55 -10 -2 0 63 -23 -28 0 -35 -58 57 0 49 -67 39 0 48 19 40 0 -11 29 -35 0 -43 -5 7 0 -36 -24 -61 0 -30 58 23 0 26 -22 68 0 21 52 47 0 -50 -15 -7 0 30 -64 -20 0 64 -53 -37 0 53 64 -65 0 38 -40 54 0 2 -12 -14 0 -27 -4 64 0 -62 -12 -30 0 -59 42 -49 0 56 70 -1 0 51 66 45 0 -53 -24 26 0 11 -14 -53 0 67 -10 27 0 -48 32 -27 0 53 52 5 0 6 -36 -39 0 -19 -32 30 0 -5 -68 -13 0 36 -66 10 0 69 -12 -51 0 8 -40 -41 0 12 70 6 0 13 -37 -57 0 9 -10 -37 0 -55 31 3 0 -26 -25 -29 0 7 -39 -3 0 20 -27 2 0 -54 -65 -66 0 -60 -39 14 0 49 15 12 0 5 -34 7 0 -65 -56 -38 0 25 -54 -5 0 -39 23 5 0 70 22 -10 0 -70 -13 -5 0 10 -58 -50 0 5 40 -28 0 6 63 9 0 -36 10 57 0 -59 7 46 0 -6 3 -17 0 -33 62 32 0 -32 -42 -47 0 27 6 -7 0 -7 -47 27 0 -57 -26 -11 0 38 -9 64 0 -60 -11 62 0 -50 -31 6 0 -44 30 -2 0 -37 -45 69 0 -25 -21 -62 0 61 39 -21 0 45 63 -13 0 -46 59 -33 0 -3 -58 -55 0 17 69 -38 0 -51 38 -31 0 12 58 -41 0 68 67 46 0 14 -59 -33 0 53 49 -8 0 30 -31 51 0 57 -34 10 0 32 -21 44 0 68 25 -27 0 -6 12 -17 0 17 -34 40 0 55 28 -44 0 60 -24 64 0 29 -39 28 0 3 -27 -52 0 -21 69 -46 0 -25 52 60 0 -40 26 69 0 -13 -62 -14 0 58 59 62 0 61 8 -44 0 14 -16 -27 0 -53 9 8 0 44 22 64 0 -69 -43 -20 0 -60 -53 -41 0 34 10 45 0 21 -63 8 0 54 -65 63 0 -23 -48 -46 0 -59 38 -16 0 23 -54 15 0 -65 -61 -6 0 -7 -33 -3 0 32 -26 60 0 37 -46 -66 0 15 -19 22 0 -29 -49 67 0 33 -61 32 0 62 4 -19 0 51 -5 10 0 26 42 -10 0 23 -12 -21 0 -64 13 -1 0 65 -47 -64 0 -20 8 -62 0 -44 -48 18 0 29 -41 -27 0 -70 -28 23 0 -70 -62 -66 0 -59 -19 -47 0 -15 20 45 0 19 -23 29 0 67 -42 -35 0 -22 -16 -37 0 -42 2 16 0 53 -12 -62 0 -3 -32 -15 0 -43 32 36 0 -59 61 -8 0 -7 25 -67 0 -53 -13 -16 0 -32 53 -67 0 63 -55 50 0 65 12 -35 0 -10 17 31 0 -8 34 -27 0 -25 -17 -20 0 -50 38 66 0 46 34 10 0 21 -66 -12 0 43 51 -57 0 -10 70 66 0 -31 60 48 0 9 -11 62 0 53 26 -38 0 23 -60 -53 0 14 57 24 0 67 37 28 0 37 -3 46 0 17 -70 52 0 -39 52 -49 0 42 20 2 0 -17 -21 7 0 57 -53 23 0 -16 -39 65 0 2 -33 -61 0 -32 31 -55 0 17 -26 -53 0 -8 4 30 0 70 18 -29 0 24 38 59 0 27 -13 41 0 8 59 66 0 -9 -8 13 0 -43 -31 56 0 45 -37 -28 0 -17 20 -21 0 19 -62 -6 0 -45 -53 1 0 -12 46 -59 0 19 -34 -67 0 -38 -63 -45 0 -70 61 -39 0 10 -61 43 0 -40 12 70 0 -65 -54 20 0 27 48 -8 0 69 -28 -20 0 -42 -56 -19 0 -51 -13 -10 0 -10 64 -34 0 21 -65 -4 0 70 40 17 0 -27 21 -16 0 53 -59 -29 0 -38 -33 -62 0 66 -27 -14 0 -70 -49 -33 0 -54 -42 -17 0 -19 -61 59 0 -9 47 20 0 22 -12 32 0 26 58 -36 0 -68 12 16 0 -62 47 -3 0 45 44 69 0 -69 -4 -35 0 -41 -29 -62 0 62 69 2 0 -47 20 -1 0 -14 -18 -55 0 -22 68 8 0 5 -62 -52 0 29 11 24 0 -29 -47 1 0 -30 52 39 0 -23 -37 -35 0 24 46 -60 0 -9 34 -39 0 -66 -21 50 0 -15 35 -10 0 1 59 25 0 -18 44 -67 0 41 -37 -47 0 -60 -61 36 0 -42 -26 -5 0 15 -20 -18 0 -11 -3 41 0 -38 -64 20 0 49 -40 39 0 70 20 -52 0 -22 58 32 0 -54 56 21 0 -44 -30 -32 0 19 64 26 0 16 -41 36 0 4 -45 7 0 25 31 36 0 -3 65 -32 0 -28 18 30 0 -5 -66 35 0 -9 22 61 0 -18 46 66 0 64 -69 -54 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/80.cnf0000644000175000017500000001020512253362407021713 0ustar georgeskgeorgeskc c SAT instance in DIMACS CNF input format. c p cnf 80 340 -66 -42 -15 0 -4 9 -30 0 -7 13 -42 0 59 63 -7 0 16 60 74 0 -28 -75 51 0 -56 13 -17 0 -47 -56 -13 0 -52 -60 75 0 -43 -10 -61 0 -30 78 -19 0 25 26 35 0 -52 -62 58 0 32 73 11 0 -13 -5 -25 0 -43 21 1 0 64 -8 -26 0 28 -33 -58 0 74 -67 28 0 54 -45 63 0 20 -38 -15 0 57 6 -27 0 55 41 -14 0 -15 -28 40 0 -22 -66 -32 0 14 -49 -56 0 -14 -36 21 0 9 -67 -59 0 -50 1 8 0 -74 66 -51 0 8 -11 -38 0 -73 43 39 0 48 -27 -8 0 50 -67 57 0 1 61 20 0 70 -27 -58 0 -18 36 -14 0 51 -37 -30 0 17 -19 68 0 54 -19 -36 0 34 63 68 0 -5 -8 -68 0 -65 8 41 0 -76 -1 25 0 -66 11 23 0 -19 62 -42 0 18 -23 67 0 -53 18 21 0 -18 58 28 0 21 37 52 0 70 -41 74 0 -78 56 6 0 61 60 66 0 65 -46 -21 0 -50 -54 -74 0 54 1 -71 0 -46 -4 -73 0 -52 68 73 0 52 3 73 0 -26 -61 -33 0 -64 76 61 0 -34 16 -67 0 7 -5 -9 0 37 -4 -69 0 60 76 -30 0 -25 -18 8 0 58 -51 -52 0 -40 -12 68 0 -39 -76 -80 0 53 -73 6 0 3 -22 -77 0 -67 37 16 0 14 76 9 0 -12 -58 -3 0 -25 -49 20 0 -67 17 -56 0 56 -72 -45 0 -11 -15 -1 0 51 -10 43 0 -59 -13 27 0 -11 -14 -22 0 42 -37 -5 0 26 60 31 0 -12 8 38 0 36 47 -49 0 58 -24 67 0 65 25 12 0 -56 -50 -39 0 -43 -34 -47 0 33 21 -77 0 -31 -23 -74 0 19 7 32 0 48 -4 -71 0 -70 -30 48 0 24 19 -7 0 2 67 -24 0 -31 -79 -35 0 -55 -37 -12 0 -6 69 79 0 44 -27 79 0 -77 -41 -55 0 -7 10 9 0 -63 -20 6 0 1 10 -39 0 -4 71 -1 0 24 1 -80 0 12 73 75 0 -74 -55 -13 0 72 58 -23 0 23 65 -26 0 32 74 21 0 57 30 42 0 67 -44 75 0 -77 69 -57 0 46 -30 -36 0 5 -21 75 0 51 -39 59 0 61 -24 -64 0 58 -18 -2 0 71 34 -8 0 47 62 -17 0 -62 58 28 0 -48 3 17 0 -46 24 75 0 -47 3 42 0 -46 15 57 0 41 -17 -53 0 47 -20 24 0 -53 -27 21 0 -25 -34 55 0 68 54 38 0 29 -41 16 0 72 -12 -32 0 76 -39 -9 0 47 -36 -56 0 43 40 27 0 -17 -67 -16 0 32 63 -50 0 -19 -74 77 0 18 2 43 0 -53 -18 -45 0 -57 31 -24 0 -48 -80 -68 0 -52 23 -66 0 52 -36 -59 0 5 13 -66 0 -4 42 68 0 -79 -69 6 0 -15 -60 -72 0 -61 75 41 0 66 -21 -68 0 -9 -38 -32 0 65 11 -51 0 47 -26 13 0 33 69 -75 0 72 -23 -8 0 -48 -15 78 0 79 61 23 0 45 47 12 0 22 -57 73 0 -17 -4 -60 0 -22 73 46 0 79 -60 -56 0 60 -19 48 0 16 -54 -64 0 -74 -29 67 0 40 4 -29 0 68 -47 -27 0 61 -59 -19 0 -52 59 -64 0 -61 -20 -74 0 13 8 26 0 -15 -5 -45 0 63 9 -68 0 -78 -70 -29 0 37 64 -53 0 4 51 -24 0 65 -57 6 0 1 -72 53 0 48 -40 44 0 -1 -2 55 0 -27 -14 75 0 -21 72 -48 0 -29 -8 -53 0 10 -8 61 0 -80 -26 62 0 80 70 -64 0 29 72 70 0 -68 56 44 0 -80 12 -36 0 -69 -78 -41 0 29 -64 -2 0 37 15 -76 0 -20 60 37 0 16 -58 -52 0 74 45 -63 0 66 36 -1 0 -18 -27 30 0 -27 -74 -69 0 -52 -56 53 0 -6 -15 -27 0 -12 64 -23 0 42 -66 -52 0 31 -66 69 0 -18 38 46 0 -53 50 77 0 -47 62 59 0 54 -61 -48 0 79 27 66 0 -67 -59 -69 0 -56 -20 -4 0 -63 -7 -71 0 67 76 22 0 -2 74 76 0 40 12 15 0 -44 -79 -54 0 52 -74 -44 0 41 8 -60 0 -33 40 9 0 -51 -68 -1 0 67 -22 -50 0 50 25 35 0 48 16 -11 0 -77 -33 55 0 75 -21 -10 0 -32 -36 -1 0 -56 14 -11 0 35 -44 -11 0 37 -70 -43 0 -80 -75 39 0 -44 22 -64 0 -75 22 -46 0 65 -13 -46 0 61 -77 41 0 18 -35 -58 0 -45 -63 -26 0 46 -58 -34 0 70 -21 -60 0 46 3 37 0 -10 23 -76 0 -32 -28 -42 0 76 -44 33 0 30 -8 32 0 -62 32 -67 0 33 9 -5 0 -75 37 9 0 32 35 -40 0 63 71 -56 0 -7 -50 -41 0 41 -35 -46 0 19 51 23 0 -69 -78 -18 0 30 28 -16 0 35 -46 28 0 48 27 37 0 -34 8 -50 0 -50 -23 -65 0 74 -6 -41 0 53 41 73 0 20 -8 -2 0 10 -56 -64 0 -31 -40 79 0 -14 -68 -11 0 52 56 -42 0 73 -2 -51 0 12 38 57 0 -71 -68 -34 0 -3 -27 64 0 48 61 44 0 79 -39 51 0 11 79 -3 0 -22 36 -25 0 70 -25 -31 0 31 59 -66 0 -42 47 7 0 -4 -5 -63 0 -80 2 -65 0 62 20 5 0 30 4 -31 0 57 -36 -17 0 -30 26 40 0 -8 80 -79 0 -28 -7 -6 0 46 -22 20 0 -79 -28 -53 0 -78 -32 -35 0 -7 11 -46 0 77 -65 14 0 78 8 40 0 -50 -38 55 0 -46 33 22 0 35 32 -53 0 -39 -28 -2 0 -72 77 71 0 9 2 23 0 30 67 -16 0 -79 -57 42 0 -24 -27 61 0 -3 78 79 0 44 -18 38 0 -36 -18 -9 0 70 26 7 0 -45 -46 53 0 45 -33 -22 0 -30 53 -40 0 79 -15 56 0 31 -18 -8 0 -10 -78 -25 0 -29 38 67 0 -2 50 -38 0 -15 -39 -23 0 -20 -4 -71 0 54 52 -35 0 -40 34 74 0 -72 -60 -15 0 -60 -58 -56 0 -48 30 -62 0 -76 22 -59 0 18 71 -10 0 -54 25 -26 0 -24 65 -66 0 14 -12 -8 0 -23 22 19 0 -63 70 -62 0 -56 43 -13 0 -17 59 -5 0 -6 -44 -26 0 33 32 15 0 -5 -22 14 0 19 32 42 0 9 43 17 0 -63 64 -22 0 13 -8 -79 0 -52 -15 -50 0 32 -38 -9 0 -21 -56 -37 0 -26 -66 74 0 -74 44 46 0 -2 -34 -67 0 2 -14 -50 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/65.cnf0000644000175000017500000000647712253362407021736 0ustar georgeskgeorgeskc c SAT instance in DIMACS CNF input format. c p cnf 65 276 -28 51 -65 0 63 48 17 0 65 -50 -46 0 43 61 45 0 40 10 33 0 -10 -40 45 0 18 55 -29 0 54 41 45 0 -57 -1 53 0 32 22 62 0 -6 -14 -8 0 22 47 27 0 -16 -54 -50 0 -31 2 -32 0 -55 54 -20 0 -26 37 4 0 64 14 -55 0 51 14 -17 0 3 10 20 0 -38 -44 21 0 55 65 26 0 -9 -43 41 0 -9 -52 11 0 18 52 17 0 48 23 9 0 7 -45 -28 0 -55 2 51 0 5 -61 44 0 56 13 22 0 55 5 22 0 -28 6 -65 0 59 -47 28 0 -12 47 -30 0 27 -8 60 0 30 -55 41 0 27 -23 -20 0 -27 54 -60 0 -40 -51 -16 0 -19 45 -37 0 5 -20 21 0 -65 1 -30 0 -64 35 -48 0 -10 -3 41 0 48 -19 -31 0 61 -64 58 0 7 53 26 0 57 -17 63 0 -16 -19 46 0 56 18 15 0 63 -50 59 0 -47 -2 29 0 62 -1 -28 0 -41 -8 64 0 -18 34 -64 0 61 26 -17 0 -47 -25 37 0 64 -41 52 0 -8 58 -32 0 39 -63 -45 0 -18 -30 -63 0 -32 -51 30 0 34 -46 25 0 -41 11 34 0 -37 15 6 0 64 62 17 0 -24 -20 30 0 10 40 2 0 -46 -18 5 0 -64 -30 23 0 35 44 8 0 -9 22 46 0 -22 28 5 0 -40 -18 -46 0 -26 -41 63 0 4 -59 37 0 36 24 7 0 37 -17 13 0 30 -55 -63 0 -59 38 -6 0 42 31 -14 0 -18 20 25 0 33 -50 53 0 -38 39 57 0 1 57 59 0 56 -65 -7 0 28 -8 -26 0 -48 -60 32 0 -59 13 -37 0 -20 29 41 0 4 -36 55 0 6 -14 -42 0 -17 9 -2 0 -38 4 54 0 -24 -59 52 0 29 45 -61 0 25 15 43 0 -57 -47 -12 0 11 -30 -54 0 51 10 63 0 53 -23 19 0 -9 34 -55 0 -44 61 -59 0 63 -53 38 0 -54 -51 -37 0 -20 -33 -29 0 35 64 40 0 31 38 -5 0 36 55 -22 0 -10 -26 36 0 34 -54 -38 0 -20 38 -16 0 -2 -60 -33 0 39 52 57 0 -39 3 -12 0 37 10 47 0 -58 -40 18 0 26 37 -52 0 51 -29 -6 0 49 -54 -36 0 39 -10 -3 0 -29 18 -16 0 -34 6 29 0 43 -18 21 0 8 52 47 0 17 4 -40 0 -61 -60 -62 0 26 -58 -48 0 -2 28 21 0 -33 59 -65 0 51 -29 58 0 -37 -38 58 0 -4 -45 42 0 1 55 -49 0 -18 -57 20 0 4 -65 25 0 -39 65 -18 0 -12 -30 15 0 -4 33 -1 0 19 2 40 0 -55 5 -44 0 10 52 2 0 52 10 43 0 10 29 -32 0 -25 -39 59 0 13 46 61 0 -6 -38 19 0 -46 -11 60 0 -9 -39 21 0 29 -7 -15 0 37 -50 49 0 45 -42 63 0 54 -40 -50 0 35 -31 -49 0 -4 48 6 0 -46 -7 57 0 -64 -47 -52 0 -14 59 9 0 17 -13 51 0 30 27 -56 0 -6 49 46 0 -24 -50 52 0 52 3 -46 0 20 50 41 0 49 56 -23 0 -56 8 14 0 -56 -41 -9 0 35 -43 -27 0 -7 -26 39 0 -14 -32 29 0 38 53 62 0 -61 -2 -41 0 14 -47 44 0 -59 -28 -26 0 -6 -20 44 0 -35 -51 -55 0 -23 -55 30 0 -32 -65 26 0 55 52 14 0 -22 -29 -59 0 -29 -36 9 0 -33 -18 -20 0 -23 -36 -29 0 -19 33 -48 0 8 -35 38 0 40 58 21 0 -48 -55 -49 0 42 60 -53 0 64 43 -27 0 39 23 -4 0 -65 50 28 0 -38 -52 43 0 21 -64 -53 0 -36 14 52 0 -16 21 49 0 -26 18 -7 0 27 50 -59 0 35 1 -23 0 13 -32 -54 0 -44 -42 20 0 -12 52 6 0 26 10 -53 0 -22 -17 -56 0 27 -30 22 0 -53 44 -4 0 -14 -37 -21 0 -47 -21 51 0 -46 -47 -9 0 19 64 -47 0 -38 37 7 0 -35 -43 7 0 38 -61 -30 0 27 -37 11 0 1 -17 20 0 -21 22 -50 0 25 35 28 0 -29 9 -64 0 27 3 59 0 -62 -3 -50 0 19 -20 45 0 -37 60 43 0 56 -22 -47 0 -51 -2 52 0 -30 -23 -3 0 -49 35 -16 0 22 29 -61 0 31 -14 22 0 59 19 6 0 -11 42 -48 0 31 54 -29 0 -34 21 -33 0 53 -56 37 0 53 3 33 0 11 -63 -1 0 -52 64 40 0 31 -43 -26 0 -20 -47 14 0 -39 -38 64 0 56 32 5 0 50 30 -54 0 -32 3 -21 0 14 10 18 0 -15 -51 -39 0 -49 -42 -18 0 8 -59 7 0 -51 28 -34 0 52 27 40 0 29 -28 25 0 -51 64 -21 0 -16 -42 41 0 56 -39 40 0 26 60 43 0 -27 39 35 0 65 -50 62 0 -11 10 44 0 56 62 20 0 57 33 -63 0 -46 -1 -50 0 20 4 58 0 8 28 -60 0 -22 -30 -56 0 5 -8 63 0 46 -29 -10 0 28 13 18 0 -33 25 46 0 -14 -46 -30 0 18 50 -3 0 38 10 -42 0 -15 56 -17 0 -4 34 -21 0 57 55 58 0 -20 -50 6 0 39 -11 18 0 -18 26 -43 0 -22 -31 58 0 10 37 -39 0 14 -16 54 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/150.cnf0000644000175000017500000002044712253362407022002 0ustar georgeskgeorgeskp cnf 150 637 10 114 39 0 73 108 99 0 31 73 8 0 110 -62 8 0 106 -119 16 0 89 54 100 0 -26 -60 65 0 45 77 -140 0 93 121 135 0 142 -11 -51 0 -64 -31 -1 0 -60 20 149 0 11 87 -19 0 -32 135 47 0 96 -131 60 0 -3 137 -26 0 -34 103 73 0 56 -52 66 0 -60 -128 -136 0 44 -32 63 0 -96 -117 -145 0 112 113 -141 0 56 127 -130 0 -47 -125 -42 0 -110 93 97 0 -13 -44 57 0 -11 111 -3 0 134 64 -25 0 108 59 144 0 -135 -66 42 0 109 18 26 0 -128 -141 -56 0 102 -134 127 0 84 51 59 0 -10 -51 147 0 -84 -85 144 0 -147 -103 -149 0 116 51 141 0 143 -57 -111 0 55 -49 109 0 -136 -27 119 0 26 16 -142 0 -69 128 -86 0 -81 8 -83 0 -108 68 -73 0 79 81 148 0 64 -30 122 0 -20 -89 82 0 49 1 -41 0 135 -32 75 0 -136 18 -12 0 -53 31 -149 0 86 -122 67 0 15 -62 -32 0 -92 111 146 0 137 128 85 0 40 101 -108 0 23 -13 -114 0 63 -19 -95 0 -62 39 -44 0 38 -5 -58 0 -49 58 -40 0 -41 -119 145 0 -139 99 127 0 9 -88 43 0 -20 69 150 0 -102 -2 47 0 135 -94 28 0 147 -137 -18 0 65 -121 -67 0 79 73 -17 0 -136 75 121 0 37 -63 107 0 144 14 9 0 5 -89 -109 0 70 140 71 0 17 -102 124 0 42 -70 -69 0 27 -133 -84 0 75 -109 113 0 -70 -57 79 0 -122 -96 53 0 63 -37 -84 0 9 121 13 0 48 -36 -140 0 111 65 29 0 -77 -146 1 0 66 -55 146 0 -16 -118 -91 0 -71 -61 -84 0 -142 9 13 0 -15 -20 -19 0 -92 -113 138 0 14 20 12 0 54 40 -110 0 -100 -105 -11 0 -122 102 118 0 -125 -30 -76 0 22 105 -47 0 27 89 -8 0 134 -37 86 0 -89 99 -71 0 -34 33 -146 0 -147 -26 -87 0 -127 -148 -62 0 -83 -149 -25 0 -84 -33 31 0 -130 10 36 0 40 25 -32 0 -60 -108 -135 0 131 -18 118 0 134 140 -31 0 -105 37 31 0 12 39 -101 0 62 34 -96 0 -39 111 136 0 -95 -142 -79 0 -134 52 -10 0 3 -51 74 0 -141 -40 -52 0 -121 60 133 0 75 149 -129 0 56 79 85 0 128 29 -9 0 -12 -36 53 0 50 117 142 0 -115 75 -84 0 17 66 16 0 147 17 31 0 147 148 144 0 -71 -89 142 0 -2 -150 -48 0 -72 -28 -49 0 -150 -62 -10 0 -30 -33 -35 0 -42 17 147 0 -75 -59 -149 0 53 -48 -145 0 -30 50 86 0 -53 109 -94 0 -48 -123 114 0 83 112 -142 0 20 57 -103 0 11 39 -86 0 -101 -47 -124 0 -81 -93 46 0 135 104 -94 0 -118 -43 113 0 -142 -70 -30 0 -39 -78 143 0 -63 66 -80 0 45 48 -44 0 60 -23 130 0 25 59 46 0 75 147 52 0 -147 -70 63 0 86 117 75 0 -55 -78 -42 0 -67 -2 -28 0 -132 75 29 0 -48 -122 32 0 56 -47 -139 0 -134 89 16 0 19 150 109 0 53 -139 -29 0 -98 147 40 0 -17 126 55 0 -107 -71 -68 0 145 -49 114 0 -129 -132 -41 0 -5 99 -85 0 95 11 56 0 52 -7 25 0 106 27 -56 0 146 -7 -8 0 38 -65 -68 0 -23 91 59 0 -116 25 128 0 -43 -74 -2 0 -115 110 -9 0 58 -43 3 0 -74 125 -83 0 56 10 25 0 -52 139 61 0 14 42 -94 0 142 44 32 0 46 137 -52 0 -107 24 45 0 116 74 -113 0 142 -67 -139 0 145 -76 89 0 -5 10 -101 0 67 -141 -40 0 35 143 138 0 32 150 42 0 85 -84 103 0 97 -9 26 0 -51 -75 110 0 -136 -140 11 0 -1 -132 -38 0 -59 -137 139 0 -146 144 -82 0 -148 55 20 0 -9 -146 -60 0 139 29 35 0 -6 -30 60 0 95 73 150 0 118 -35 -13 0 146 74 23 0 38 30 39 0 118 -54 1 0 -65 -57 -122 0 -118 -150 132 0 92 -145 -32 0 148 -81 -90 0 -83 36 97 0 85 -125 59 0 19 130 -94 0 51 -110 -44 0 -91 20 107 0 107 108 -79 0 -76 44 13 0 -145 -3 -29 0 145 -33 -133 0 35 -53 -83 0 138 -16 129 0 95 122 -124 0 4 -111 -56 0 -39 42 73 0 -81 -128 62 0 -95 -141 2 0 -6 -66 -60 0 60 57 -36 0 117 86 -18 0 36 80 -54 0 -133 2 -46 0 129 145 15 0 47 -66 -18 0 120 137 -109 0 -47 81 -29 0 65 -126 -99 0 -47 34 1 0 66 -5 22 0 130 139 133 0 50 -36 85 0 -62 22 -125 0 -18 79 88 0 -8 53 -23 0 54 124 145 0 -33 48 105 0 -136 32 115 0 -96 116 -121 0 -29 132 20 0 68 -73 -32 0 47 134 129 0 29 51 131 0 38 89 -52 0 32 -81 -7 0 130 -60 -54 0 -128 89 -116 0 -129 -118 14 0 109 -62 21 0 16 -43 102 0 -31 10 -15 0 109 -62 11 0 121 -128 149 0 136 42 -52 0 124 140 126 0 108 -42 18 0 18 103 -41 0 -22 -44 -59 0 68 -145 -49 0 -108 110 -44 0 -115 -131 -130 0 86 -150 111 0 49 141 -144 0 66 -102 -104 0 22 122 78 0 -22 35 107 0 37 -58 36 0 -141 -2 54 0 -55 -136 -48 0 24 133 86 0 147 40 -86 0 4 67 96 0 69 -76 -64 0 128 130 -46 0 -97 -120 -7 0 106 -36 132 0 -107 147 139 0 96 67 -6 0 48 -67 66 0 -62 -41 3 0 -22 -66 -95 0 -115 92 -86 0 -114 78 121 0 9 10 125 0 -110 65 -126 0 67 64 69 0 -78 -91 139 0 -91 125 -137 0 -93 91 -115 0 -106 -118 109 0 5 71 64 0 -46 134 -66 0 97 138 134 0 87 -25 -8 0 -44 106 -77 0 -115 -18 -123 0 141 -115 -75 0 -71 86 -73 0 127 -112 -124 0 -69 -72 100 0 -149 137 -9 0 -49 -72 -32 0 64 96 37 0 -97 -83 115 0 133 -92 105 0 -55 121 149 0 -49 -140 69 0 -27 -134 59 0 -87 -11 -47 0 -70 124 113 0 12 -143 47 0 -43 -34 -36 0 23 -102 114 0 -45 -105 -109 0 44 -99 -46 0 -27 -122 125 0 126 4 11 0 -132 -79 34 0 110 -150 8 0 -142 -20 -97 0 -51 35 91 0 101 -16 -71 0 -96 101 -121 0 44 -84 98 0 76 62 134 0 112 -35 -70 0 -53 -105 -10 0 -85 -19 131 0 125 -80 149 0 -53 -3 121 0 -102 56 86 0 73 53 -117 0 95 -50 25 0 137 144 118 0 -55 51 32 0 -55 -141 -11 0 -82 -36 -12 0 45 -47 68 0 22 -120 110 0 -102 96 148 0 -46 68 -119 0 16 50 85 0 33 -3 96 0 44 88 -145 0 32 132 -55 0 -129 -143 -82 0 -147 -42 33 0 17 82 107 0 89 62 39 0 -142 139 -21 0 -132 -108 -29 0 58 -137 -3 0 -113 -106 66 0 88 -31 -110 0 149 57 67 0 84 -112 -94 0 89 60 -79 0 -8 -114 34 0 -61 -1 122 0 -60 -29 66 0 -113 30 -43 0 33 -89 -78 0 21 125 -23 0 107 -48 149 0 98 -136 -92 0 98 -65 -45 0 72 144 -114 0 -25 145 -128 0 -30 25 -132 0 -26 -83 -58 0 137 -77 45 0 70 -62 117 0 -149 8 134 0 80 60 -100 0 83 -123 -120 0 75 -76 -6 0 -150 37 73 0 -87 8 75 0 18 119 53 0 148 109 17 0 74 -14 -56 0 -21 -44 19 0 -140 -51 -104 0 -111 -105 -26 0 -131 -9 125 0 130 106 -24 0 -11 102 55 0 -114 34 -71 0 -40 129 -79 0 -39 -79 -71 0 -12 -144 -84 0 -20 -23 44 0 103 77 -5 0 -52 -15 -30 0 136 -90 98 0 149 -73 -117 0 -34 -53 68 0 92 -49 37 0 120 -33 -7 0 108 -121 26 0 52 -145 3 0 -40 -43 4 0 95 134 139 0 -18 -95 37 0 -137 -104 68 0 -20 5 68 0 28 12 50 0 -54 -64 -92 0 102 27 118 0 47 -121 -4 0 3 22 -39 0 -28 -129 -90 0 -31 -21 33 0 143 60 31 0 -78 -3 54 0 17 105 140 0 23 -43 -108 0 -94 -90 -68 0 -82 14 42 0 -70 120 28 0 97 134 41 0 -40 15 72 0 62 -8 137 0 21 -129 27 0 21 40 124 0 100 -93 27 0 -140 99 46 0 -87 63 -129 0 -86 134 67 0 90 -111 65 0 133 23 -56 0 -103 -142 35 0 28 95 100 0 -79 29 -112 0 -6 49 73 0 125 -17 -126 0 -43 -135 51 0 27 8 -143 0 -49 91 -37 0 6 48 -3 0 78 -40 -38 0 91 41 -104 0 9 6 -13 0 -105 -24 -132 0 133 -58 -28 0 -61 15 72 0 44 46 -124 0 53 -130 43 0 -94 -95 31 0 106 -20 18 0 59 -24 -70 0 -97 120 62 0 84 31 -87 0 95 -99 -28 0 -118 -17 -102 0 -115 -9 122 0 -123 135 2 0 134 39 -130 0 93 42 -65 0 -94 -73 78 0 -67 -7 130 0 -119 20 -7 0 -59 -92 -95 0 -35 136 145 0 -140 -80 -16 0 -114 62 -135 0 53 137 147 0 -150 23 -134 0 -48 -146 -47 0 78 -82 66 0 -50 -10 -85 0 1 40 140 0 101 -74 -17 0 142 -16 94 0 5 -50 -97 0 118 -80 -30 0 -119 -115 -35 0 124 99 -45 0 83 40 -28 0 99 -39 8 0 -47 20 84 0 -149 6 59 0 125 -18 -144 0 49 128 65 0 102 -121 -105 0 72 -18 -139 0 -19 -99 83 0 12 -91 27 0 93 51 117 0 -136 -23 41 0 -43 13 -100 0 -141 -62 -21 0 125 -90 -93 0 99 28 147 0 111 144 -11 0 127 30 128 0 -44 -85 133 0 10 53 139 0 48 -120 119 0 5 142 104 0 -78 50 29 0 82 85 51 0 -6 4 -22 0 136 35 -116 0 44 114 -10 0 -37 30 -26 0 -111 -114 113 0 -122 112 -7 0 22 -149 -7 0 127 36 -113 0 -18 120 126 0 80 -99 -134 0 120 -60 -67 0 141 -139 -77 0 -70 -142 135 0 84 133 -62 0 -145 138 94 0 77 62 -71 0 47 -92 -8 0 -123 125 -8 0 116 -19 63 0 51 -99 -121 0 139 -59 -149 0 17 -5 -71 0 -135 -88 63 0 147 -61 31 0 -7 39 24 0 -86 3 42 0 88 -27 70 0 112 -109 41 0 -127 -36 134 0 32 -136 124 0 15 -43 53 0 -14 -50 40 0 33 -106 -101 0 14 -56 -150 0 88 -100 -56 0 80 121 -78 0 -125 118 67 0 -129 8 95 0 -64 -89 -149 0 -102 46 150 0 39 88 -111 0 -61 75 69 0 -105 65 51 0 145 27 -139 0 90 -28 -57 0 -54 133 98 0 -36 -95 145 0 -18 44 -132 0 24 100 -107 0 -35 116 20 0 114 -143 102 0 135 -47 51 0 110 -70 96 0 111 -73 -43 0 -55 -34 -69 0 -58 91 -17 0 62 -51 76 0 73 -89 75 0 10 61 -59 0 -141 -36 -23 0 72 -43 -93 0 -8 20 58 0 -134 -5 19 0 38 -117 -94 0 -13 -20 -78 0 141 126 -43 0 -17 -13 107 0 43 47 -12 0 -10 83 -21 0 -143 -36 -136 0 -35 118 -104 0 78 121 136 0 -41 -6 -119 0 -77 -10 124 0 -31 -117 -35 0 -126 46 29 0 -75 58 81 0 123 77 101 0 53 -9 43 0 42 -4 -50 0 -141 47 67 0 56 41 -73 0 49 -134 55 0 72 55 91 0 102 -23 84 0 -4 -11 10 0 37 -60 -3 0 -103 5 -17 0 119 -78 -102 0 125 -38 76 0 -7 19 29 0 72 -49 -58 0 -121 -94 -122 0 -19 125 61 0 141 -135 -74 0 117 48 129 0 84 -3 -121 0 90 111 -110 0 -49 146 -15 0 36 31 -72 0 56 -53 -6 0 8 58 -36 0 -112 -71 22 0 44 -97 -61 0 -125 28 -2 0 -147 149 13 0 88 60 -98 0 -132 -143 122 0 44 -129 -66 0 -93 11 143 0 68 47 -94 0 -74 -44 76 0 -64 148 -145 0 -8 55 105 0 51 -55 -84 0 -72 123 130 0 140 54 119 0 125 31 42 0 94 -74 -95 0 -76 -95 67 0 119 -56 -94 0 17 12 123 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/45.cnf0000644000175000017500000000442712253362407021725 0ustar georgeskgeorgeskc c SAT instance in DIMACS CNF input format. c p cnf 45 191 39 14 -6 0 25 -19 -26 0 37 33 20 0 21 -12 37 0 -22 -35 7 0 25 -28 -20 0 -3 -29 -37 0 19 -13 32 0 -9 42 2 0 -9 6 18 0 -28 -14 -15 0 30 -29 22 0 30 -17 8 0 -26 31 -10 0 45 14 -36 0 15 -37 24 0 37 20 -3 0 25 -2 -37 0 -39 25 31 0 44 -42 40 0 14 9 -43 0 -18 23 45 0 -43 -1 39 0 45 -19 -9 0 -26 -37 -36 0 22 -6 38 0 -30 8 34 0 13 29 -34 0 21 11 2 0 -43 -31 13 0 29 42 39 0 5 42 -6 0 4 24 -39 0 -29 18 42 0 6 -12 26 0 39 35 21 0 -37 7 16 0 -11 -10 -28 0 -19 -12 2 0 42 -43 -4 0 -7 -30 18 0 -21 -1 13 0 -40 -31 38 0 -1 9 -20 0 28 19 -26 0 18 32 45 0 44 31 -45 0 35 20 6 0 -18 9 -13 0 -17 -21 8 0 2 21 34 0 20 -36 -5 0 -21 -33 -45 0 -41 -15 26 0 16 -34 -40 0 29 -18 -39 0 38 42 -22 0 -31 43 -36 0 -34 37 -29 0 37 -14 -31 0 26 -32 22 0 -22 -45 -28 0 26 45 -44 0 -5 40 -34 0 -31 -10 39 0 40 9 35 0 11 2 -20 0 -30 -11 31 0 -43 -14 40 0 41 -21 -11 0 -21 11 13 0 4 -8 45 0 5 3 9 0 27 6 -22 0 18 -36 29 0 45 1 -29 0 35 38 -8 0 -37 14 -10 0 -23 -43 -31 0 6 17 -32 0 19 -43 -45 0 11 5 35 0 10 3 -29 0 8 34 37 0 17 -10 43 0 14 27 34 0 27 15 -25 0 3 -14 19 0 30 -19 25 0 18 -29 -10 0 20 23 -19 0 36 -45 11 0 12 -11 38 0 16 42 -13 0 14 -33 11 0 22 -43 -10 0 -44 -7 -32 0 14 19 40 0 32 12 26 0 -36 28 -24 0 -6 -26 8 0 31 -18 12 0 -17 -10 43 0 -27 -15 30 0 15 -29 -10 0 -5 -36 11 0 -9 -36 -37 0 3 18 5 0 -12 9 -25 0 16 -43 7 0 25 44 10 0 21 -22 18 0 26 -30 -2 0 5 -38 25 0 -36 26 23 0 13 -20 33 0 -30 -40 -13 0 4 19 36 0 -35 28 33 0 35 19 -3 0 29 -9 -20 0 -45 14 2 0 42 41 -15 0 -33 2 5 0 -31 -16 -40 0 10 20 -31 0 24 33 16 0 43 16 -27 0 35 32 -19 0 25 -38 8 0 -39 32 -27 0 -10 35 11 0 14 -40 7 0 -25 27 -16 0 -25 -11 16 0 -40 5 41 0 35 2 -38 0 -15 -43 -34 0 32 -22 4 0 -42 -19 -30 0 -45 -23 38 0 -33 -9 -22 0 -14 31 -18 0 44 25 -18 0 25 33 8 0 33 45 -2 0 41 -23 -3 0 19 -5 15 0 -34 -38 -8 0 5 11 45 0 -2 43 41 0 7 -1 -25 0 -44 37 8 0 -13 26 12 0 6 33 -16 0 -26 -25 -6 0 20 16 -35 0 -45 21 -24 0 4 -42 -21 0 -21 2 -18 0 -36 -11 -40 0 5 -34 18 0 37 18 45 0 -33 -23 44 0 38 -13 -11 0 7 -3 28 0 36 32 39 0 27 -15 16 0 28 -35 -19 0 24 42 31 0 -31 37 5 0 16 -1 -18 0 -38 37 -23 0 42 -7 39 0 20 11 12 0 27 33 -1 0 32 36 23 0 18 -39 5 0 32 24 -4 0 30 44 -26 0 40 -17 -25 0 12 37 3 0 -19 37 -2 0 -4 6 -43 0 -40 -24 -33 0 -2 41 -3 0 24 26 -35 0 -31 -38 -45 0 -42 -5 -13 0 -4 32 -25 0 -28 -29 -38 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/40.cnf0000644000175000017500000000402612253362407021713 0ustar georgeskgeorgeskc c SAT instance in DIMACS CNF input format. c p cnf 40 170 34 5 -22 0 35 -20 16 0 26 1 -13 0 -35 27 -2 0 -31 -30 -1 0 5 -40 1 0 15 -22 26 0 25 38 8 0 9 -18 -7 0 -23 32 6 0 -37 -38 5 0 17 16 31 0 -15 6 40 0 30 16 -2 0 -14 -17 3 0 25 -17 6 0 9 37 1 0 34 23 29 0 2 -38 -18 0 -15 27 -38 0 -31 9 3 0 8 -12 -20 0 -30 18 -8 0 18 -20 -7 0 -10 -22 5 0 5 33 -17 0 39 27 24 0 -2 -19 -7 0 24 -34 -26 0 -15 8 19 0 38 -29 -6 0 30 -32 3 0 36 6 -24 0 -20 18 25 0 -40 -39 -17 0 31 7 39 0 -20 6 -38 0 7 -3 -40 0 -9 8 -12 0 -15 4 -38 0 -27 -33 -25 0 24 38 -16 0 38 23 -21 0 9 12 13 0 9 -5 38 0 -34 -7 -13 0 4 -11 -8 0 36 -38 -8 0 -4 -11 33 0 -25 -23 -35 0 28 -1 9 0 38 27 12 0 1 -28 -12 0 -19 -35 -32 0 -2 -32 14 0 -33 24 31 0 38 -11 12 0 3 4 31 0 -3 4 -29 0 -24 9 -8 0 -13 -39 -6 0 -19 33 -4 0 29 -35 33 0 12 -35 36 0 -21 11 30 0 -21 33 -39 0 -20 -31 -10 0 16 -11 27 0 5 40 -30 0 3 -4 -37 0 -38 -9 -24 0 -4 11 -14 0 -37 38 -13 0 40 -2 9 0 -20 -39 -5 0 -13 34 18 0 -5 -32 -14 0 13 37 14 0 36 11 -28 0 14 -39 -25 0 23 35 14 0 17 -16 -31 0 -33 -8 -5 0 17 -18 -4 0 24 35 -23 0 -17 -13 -40 0 -36 19 -16 0 -20 -10 2 0 -6 38 -4 0 29 -3 35 0 12 35 22 0 -3 -11 -8 0 -25 -12 -2 0 26 -11 -3 0 -25 -21 31 0 18 40 30 0 -30 18 5 0 33 26 40 0 12 8 19 0 5 20 2 0 31 -13 -5 0 -6 9 -38 0 -30 29 23 0 -21 33 -25 0 13 -22 -1 0 32 35 13 0 14 33 26 0 28 27 -9 0 -1 -31 -7 0 20 10 8 0 34 -32 23 0 28 25 -24 0 1 -39 3 0 -34 31 -9 0 -17 -15 4 0 -7 -11 -20 0 8 -23 -20 0 -5 -15 -40 0 -30 19 2 0 -18 10 33 0 -33 -11 10 0 -26 13 -17 0 -40 -23 8 0 33 -11 25 0 20 -13 9 0 -3 30 -21 0 33 -27 14 0 -28 35 -1 0 21 -28 33 0 -16 -39 -36 0 -13 -39 35 0 -18 -14 33 0 7 -21 -8 0 -39 6 -36 0 18 23 -40 0 11 37 -38 0 -15 37 -29 0 15 30 17 0 -38 -4 31 0 -1 -9 -35 0 -22 -15 -27 0 -29 36 -4 0 -11 -30 -38 0 25 -14 8 0 37 10 -21 0 37 3 -26 0 4 -40 -36 0 -21 4 34 0 18 -26 21 0 4 -36 -29 0 17 5 -24 0 -6 -28 19 0 -4 -13 -25 0 -25 -40 -14 0 -14 1 -24 0 6 13 -34 0 -12 -1 36 0 24 -6 27 0 40 34 4 0 -14 -30 40 0 -13 -36 12 0 28 3 -20 0 -16 -3 -17 0 -6 7 11 0 -30 9 -35 0 -40 -27 31 0 -30 -4 -24 0 28 -23 -11 0 -24 19 11 0 -27 6 -24 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/105.cnf0000644000175000017500000001271612253362407022002 0ustar georgeskgeorgeskp cnf 105 446 54 -71 -24 0 85 61 92 0 -86 27 -65 0 89 47 -72 0 62 -5 45 0 56 59 -30 0 26 -73 -91 0 69 -50 46 0 -88 -50 -42 0 -56 -30 -21 0 -19 -80 38 0 11 78 105 0 -85 -23 -25 0 -9 -40 88 0 -101 -70 -52 0 -32 20 -2 0 -86 53 -66 0 -8 35 -86 0 -92 -35 73 0 -2 -73 -44 0 -90 80 -35 0 -68 -7 79 0 44 -79 -21 0 37 24 12 0 25 56 73 0 -76 -56 74 0 -2 68 84 0 99 -100 65 0 11 70 -104 0 -39 -56 21 0 -32 -87 51 0 48 78 -35 0 -15 -9 16 0 24 -47 53 0 87 35 49 0 83 28 101 0 -52 45 -4 0 78 28 38 0 -31 -81 -7 0 -9 94 43 0 -73 -57 -84 0 41 -46 -71 0 5 103 1 0 17 -4 14 0 33 -14 37 0 -48 -66 72 0 -64 42 -38 0 47 -25 32 0 89 86 -81 0 27 93 -79 0 -45 73 -79 0 -44 -74 71 0 -102 -87 18 0 -54 85 14 0 10 -96 -84 0 101 97 -75 0 -10 17 -11 0 -24 20 -81 0 -98 -46 40 0 -6 70 -101 0 -73 92 70 0 8 31 -55 0 98 -8 1 0 53 -59 60 0 -21 74 -5 0 68 86 59 0 -13 89 -12 0 27 -11 4 0 -59 -16 -7 0 -89 -28 -33 0 20 -19 -99 0 -38 34 16 0 -2 48 -91 0 82 89 54 0 -16 -50 43 0 -85 39 -73 0 -13 4 42 0 75 -15 21 0 -63 90 -53 0 -105 -33 -68 0 91 -19 86 0 -84 92 8 0 -80 47 -95 0 21 -37 -50 0 38 -59 -27 0 69 65 -53 0 53 -25 38 0 -49 -7 -14 0 -29 -38 -21 0 -53 -102 27 0 -31 94 95 0 40 -15 -52 0 12 -9 -35 0 56 -52 48 0 -67 85 -13 0 -58 -51 98 0 -11 10 -48 0 -87 88 -1 0 -5 66 14 0 -87 89 28 0 -105 -8 97 0 47 -60 76 0 -89 66 -69 0 86 -34 -8 0 -19 90 103 0 16 95 63 0 -22 8 -87 0 66 50 79 0 17 78 -51 0 53 104 -90 0 -99 -64 56 0 56 55 96 0 -98 94 -67 0 -39 -64 -85 0 -27 86 44 0 75 104 85 0 23 -1 -38 0 -20 95 64 0 -52 67 -6 0 -44 24 62 0 -72 -57 -71 0 84 52 20 0 19 -25 -31 0 -20 73 79 0 -41 85 78 0 61 31 88 0 97 -45 -49 0 25 -104 -99 0 97 43 1 0 69 45 -15 0 105 -39 5 0 -63 -103 -92 0 67 2 -74 0 -28 -75 -42 0 73 -102 -97 0 -25 22 -55 0 -81 86 -19 0 -73 -69 -53 0 -51 43 103 0 -68 73 13 0 89 -11 32 0 -60 -23 -38 0 16 44 58 0 21 99 -35 0 -28 9 -48 0 -48 43 -14 0 -40 -45 -6 0 -67 15 -100 0 10 62 92 0 5 3 -49 0 -35 70 18 0 -62 -5 -84 0 18 -63 53 0 -85 24 102 0 -3 -51 75 0 -48 -10 6 0 -4 -73 -67 0 27 90 -99 0 33 72 19 0 -32 25 -12 0 77 104 91 0 -54 -46 -104 0 -16 -88 54 0 -90 -31 -70 0 10 35 97 0 63 -36 -45 0 -94 30 -37 0 -67 100 -26 0 -100 20 25 0 100 49 -42 0 -28 14 93 0 93 -13 -32 0 -83 89 38 0 -60 -4 102 0 -62 100 -40 0 -63 44 -5 0 43 70 -89 0 -11 72 87 0 74 -25 5 0 -83 91 24 0 -23 -34 -11 0 -27 87 -73 0 -79 -7 -16 0 31 74 -30 0 58 24 -93 0 19 -75 -73 0 -10 -79 -30 0 88 -15 82 0 49 4 105 0 -32 104 89 0 -105 -13 -54 0 33 34 58 0 28 -8 55 0 68 97 -89 0 42 -37 -56 0 -53 -45 58 0 -94 -50 -82 0 47 14 82 0 90 -103 1 0 -56 47 11 0 -5 66 -92 0 -70 -72 -38 0 91 78 25 0 20 -45 -78 0 77 -52 35 0 -75 69 -29 0 -25 71 30 0 -51 -40 104 0 17 60 -5 0 93 -13 -81 0 62 -12 105 0 -60 -9 -30 0 -1 -84 -43 0 72 -84 -70 0 -88 -51 -75 0 -83 105 61 0 -105 -66 41 0 64 91 76 0 -91 -8 62 0 83 23 41 0 -20 44 -89 0 38 -89 -63 0 14 19 13 0 -28 5 54 0 -42 78 26 0 101 -97 75 0 3 -7 -101 0 33 -43 -65 0 -102 23 -30 0 -31 -63 -61 0 103 61 -89 0 10 42 -31 0 82 -18 -86 0 71 -59 -18 0 104 93 -34 0 44 -96 80 0 47 -58 37 0 -9 31 32 0 87 -97 22 0 -60 -49 -64 0 -60 66 51 0 -98 39 -83 0 38 -51 -105 0 -83 -95 84 0 -40 -25 90 0 15 -99 55 0 34 56 87 0 -98 66 -55 0 25 -74 -48 0 -76 -1 44 0 -21 -84 18 0 -86 17 74 0 -60 -19 39 0 -5 -3 15 0 -24 -82 73 0 -99 90 -96 0 -74 -72 90 0 -53 99 52 0 -53 -42 105 0 82 73 -15 0 -90 47 10 0 -80 16 -38 0 -73 12 -18 0 43 11 -44 0 39 -36 73 0 50 103 31 0 -38 -29 100 0 38 81 87 0 56 87 -16 0 -55 69 -62 0 -98 -80 89 0 -90 -25 -12 0 33 9 -42 0 38 -12 10 0 20 35 -19 0 -20 -4 -2 0 -50 -24 -56 0 -61 63 -54 0 84 48 42 0 -91 -88 101 0 -58 29 72 0 93 -42 -34 0 34 -18 -89 0 -62 60 -57 0 -105 78 -92 0 -30 -7 -71 0 63 69 17 0 28 -68 -80 0 17 97 -60 0 32 97 -73 0 -69 79 -62 0 -41 -15 -79 0 -104 91 52 0 -67 92 61 0 -2 -22 12 0 28 -7 86 0 -34 -19 79 0 -50 -20 9 0 -17 -5 64 0 -63 -84 -20 0 104 10 -70 0 42 28 8 0 -103 -43 55 0 17 -10 -67 0 71 16 70 0 -44 -38 5 0 -66 15 89 0 58 -3 9 0 99 -103 91 0 2 -86 -9 0 -91 -49 101 0 -46 -34 70 0 -103 -105 25 0 62 66 -26 0 -96 26 -10 0 101 40 4 0 23 17 87 0 16 -75 -17 0 -76 92 87 0 -89 -53 -84 0 46 52 -42 0 25 -60 23 0 -59 1 -34 0 -77 12 105 0 88 90 -105 0 -19 -87 -30 0 -1 98 -44 0 84 99 -49 0 -16 -11 -75 0 -34 83 -84 0 12 97 100 0 -69 -88 -86 0 -85 -13 104 0 48 28 72 0 -32 95 49 0 10 -17 -90 0 -40 11 -24 0 -75 35 -26 0 -99 -81 -5 0 55 -14 30 0 103 -78 69 0 -25 -79 46 0 -96 -79 41 0 11 -82 61 0 12 -70 -40 0 3 101 37 0 -11 -37 -1 0 93 13 52 0 -68 56 20 0 40 -8 -80 0 -27 -15 17 0 -54 35 -86 0 55 82 78 0 2 -60 -63 0 -67 101 94 0 -75 -10 93 0 -36 23 -48 0 91 -69 -88 0 -15 63 -47 0 -2 78 -23 0 35 -87 -6 0 57 -104 32 0 -44 32 -42 0 -38 77 52 0 20 77 16 0 51 -86 94 0 -19 -6 105 0 -22 91 -30 0 74 1 38 0 -101 -22 56 0 -69 -46 -17 0 -46 -5 -86 0 70 -80 -99 0 54 55 -92 0 51 -98 27 0 -61 66 75 0 40 47 35 0 52 43 -27 0 -54 76 -74 0 39 -25 -22 0 81 9 -60 0 -14 -71 92 0 41 -58 47 0 54 -73 89 0 -40 -82 -16 0 -81 64 -10 0 -54 -7 -37 0 -92 -99 -87 0 90 104 74 0 89 -32 22 0 90 -58 -80 0 55 86 -27 0 69 43 67 0 -75 81 56 0 -87 89 -88 0 62 77 -56 0 -68 -72 -101 0 31 20 -76 0 -85 -52 39 0 22 13 -104 0 -12 -88 30 0 49 61 55 0 44 25 -58 0 60 69 -19 0 -17 71 -37 0 -15 -69 84 0 -59 -97 41 0 -75 -7 23 0 -25 -70 -58 0 101 40 90 0 70 104 47 0 44 -31 -30 0 65 -49 17 0 -97 -73 17 0 -2 -65 -95 0 -59 94 -16 0 6 -32 57 0 36 -81 -68 0 15 -76 19 0 16 -71 4 0 55 -102 94 0 9 -75 31 0 -50 66 92 0 25 -76 -50 0 -27 -35 90 0 46 10 -89 0 -37 40 -17 0 -33 -88 -1 0 56 55 99 0 57 43 30 0 -103 48 -53 0 86 -61 75 0 -72 -19 -36 0 -95 73 -80 0 94 -39 24 0 -72 4 14 0 73 52 101 0 -56 61 27 0 75 56 13 0 -63 -14 -30 0 -47 -51 -95 0 -73 -60 72 0 19 58 41 0 -72 92 -80 0 -36 79 -26 0 -60 -4 77 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/35.cnf0000644000175000017500000000342212253362407021716 0ustar georgeskgeorgeskc c SAT instance in DIMACS CNF input format. c p cnf 35 148 -15 -30 5 0 11 -29 30 0 -3 -35 7 0 27 9 21 0 20 -7 34 0 -1 19 24 0 3 -20 7 0 10 -3 -30 0 30 -5 -1 0 -30 -25 27 0 2 -10 8 0 -32 5 21 0 25 33 34 0 35 3 31 0 -5 -34 -35 0 15 9 -21 0 17 -21 -23 0 -14 34 -4 0 -3 18 -19 0 30 19 28 0 28 2 -21 0 -33 -19 18 0 -18 -31 -26 0 -26 -2 -22 0 11 -19 31 0 -24 22 -29 0 -11 5 -21 0 13 3 -16 0 12 10 21 0 21 4 28 0 -28 -27 21 0 1 3 21 0 28 -1 13 0 -21 11 -29 0 26 35 11 0 9 3 6 0 -1 -8 14 0 13 12 27 0 -26 14 13 0 2 -33 23 0 27 24 -22 0 34 -13 20 0 -25 -10 -11 0 -2 -24 15 0 -32 -17 -20 0 -34 -9 -26 0 -17 -27 22 0 -13 9 -5 0 -9 7 -35 0 5 -25 -33 0 -17 -28 13 0 20 -15 -13 0 -27 -20 -23 0 -3 18 4 0 25 -5 3 0 -3 -33 22 0 -11 22 18 0 18 -26 19 0 22 -32 -2 0 30 -16 -28 0 4 -6 34 0 10 -30 -17 0 9 6 21 0 -21 20 23 0 -20 17 -29 0 -19 1 -22 0 -16 -30 -15 0 -29 -10 -15 0 25 -20 17 0 11 26 -7 0 5 20 33 0 7 17 -6 0 29 -10 -22 0 -35 -34 -31 0 7 17 32 0 -21 12 -20 0 16 -19 17 0 -27 -9 2 0 5 35 3 0 -21 -20 8 0 -27 14 -4 0 4 -28 1 0 6 -28 29 0 -22 6 -14 0 -26 8 35 0 22 -7 -13 0 -10 30 -19 0 -11 -16 -20 0 13 29 -8 0 -16 29 1 0 18 -5 19 0 -11 29 -19 0 -12 14 15 0 24 -34 -32 0 -4 -13 -7 0 27 31 10 0 -5 -11 16 0 26 29 19 0 -9 34 -16 0 -28 -27 1 0 21 -20 -30 0 -21 -7 32 0 15 -13 22 0 -34 13 17 0 -3 10 -4 0 25 -35 5 0 -7 11 28 0 -22 -5 2 0 26 21 -35 0 -10 31 30 0 13 -30 8 0 -5 -19 -14 0 -3 -27 -12 0 12 9 30 0 -16 12 34 0 -13 -21 -7 0 -17 13 -33 0 -14 -22 -4 0 11 -29 -10 0 7 -31 28 0 28 -20 15 0 16 19 -3 0 33 -34 25 0 -9 23 -32 0 11 9 -31 0 25 -5 -33 0 -15 27 3 0 11 29 10 0 14 -17 -4 0 -3 -24 -4 0 -11 -23 -25 0 -22 -3 -5 0 15 34 6 0 -20 -33 14 0 34 -20 -33 0 -19 -21 -35 0 -8 -25 17 0 -20 33 26 0 -29 -11 14 0 19 -15 -20 0 15 -20 1 0 14 -4 32 0 -29 -5 -35 0 -12 1 -23 0 7 -35 17 0 -34 25 -4 0 28 -34 -12 0 -18 -25 -11 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/120.cnf0000644000175000017500000001462112253362407021774 0ustar georgeskgeorgeskp cnf 120 510 -1 95 -80 0 -68 -66 -12 0 45 57 14 0 -100 11 -67 0 119 39 61 0 63 19 46 0 113 19 12 0 41 -79 -87 0 42 -37 -69 0 64 -74 -30 0 -116 -55 -64 0 119 10 104 0 -119 11 -2 0 -40 94 -90 0 102 92 -53 0 -6 -57 109 0 -9 43 -29 0 -114 79 16 0 64 -47 59 0 -17 -6 -16 0 61 63 55 0 28 83 -70 0 -45 -86 79 0 98 48 111 0 32 -28 25 0 107 65 38 0 -20 -120 -1 0 -82 -86 -51 0 -79 -8 2 0 15 -37 6 0 90 -97 -110 0 -71 -100 -115 0 90 -11 97 0 -25 -119 -110 0 1 37 -47 0 51 59 -41 0 2 89 50 0 84 -22 -88 0 93 107 104 0 -115 -105 -33 0 -18 81 -15 0 32 -17 15 0 -31 34 -81 0 22 -36 -51 0 -20 4 96 0 83 -3 -95 0 -7 -84 70 0 98 -120 4 0 -44 -104 8 0 -42 -48 -67 0 13 -55 111 0 -119 42 -107 0 86 -31 88 0 112 -28 62 0 -22 111 -10 0 61 -57 -86 0 27 112 52 0 -47 34 -72 0 -47 72 -56 0 67 -46 -106 0 107 9 73 0 -5 16 3 0 -67 53 -97 0 50 -105 -115 0 106 -94 -62 0 -22 -116 44 0 -14 -84 21 0 53 83 -77 0 52 -3 -104 0 -85 -67 -90 0 -48 -99 59 0 82 117 -98 0 12 106 47 0 30 -116 -79 0 83 -62 -56 0 73 -109 83 0 -54 8 28 0 46 -20 31 0 -74 99 7 0 -20 91 79 0 54 -12 -104 0 -85 12 -91 0 43 -75 -15 0 -45 -26 29 0 -11 22 -31 0 -77 90 96 0 -4 72 68 0 -35 17 76 0 8 -105 70 0 -67 -21 75 0 -67 -38 -107 0 52 50 -91 0 41 -9 40 0 -94 33 -31 0 -58 -86 21 0 -58 -77 -37 0 23 69 60 0 -69 -11 106 0 -79 99 -17 0 112 -33 -12 0 30 -9 -6 0 104 -112 -99 0 -113 30 42 0 -109 86 -3 0 12 100 3 0 80 -79 -33 0 -62 -34 70 0 79 -108 -109 0 -43 26 -10 0 -54 -25 37 0 -22 53 46 0 -83 65 64 0 3 -25 6 0 97 -105 -5 0 -62 -119 107 0 46 32 -73 0 76 24 28 0 -68 110 117 0 -53 80 -39 0 120 -59 -24 0 101 105 64 0 -25 -56 17 0 -115 50 45 0 97 -95 37 0 -103 -82 61 0 22 50 33 0 26 -102 101 0 58 -120 101 0 61 14 55 0 118 -64 92 0 -89 61 -37 0 90 45 93 0 79 42 15 0 -24 80 107 0 -22 66 54 0 -2 -54 110 0 51 105 -71 0 -88 -47 -102 0 -81 59 -78 0 79 -9 -57 0 48 -56 -119 0 -79 -100 91 0 96 -40 -25 0 86 -61 16 0 -67 -33 -90 0 -20 -17 -78 0 3 20 -28 0 -56 34 -120 0 111 -10 -117 0 48 16 27 0 -24 -106 -90 0 -106 91 116 0 113 10 25 0 -74 -36 -100 0 -47 -28 -62 0 45 -27 -74 0 -11 -71 28 0 29 -111 -34 0 19 -110 53 0 41 62 -92 0 95 32 -30 0 -23 -14 40 0 -39 89 -118 0 42 81 37 0 -102 -112 -31 0 -91 -70 24 0 11 -6 -59 0 11 10 -113 0 13 21 44 0 -65 -103 44 0 -7 60 83 0 61 22 -49 0 105 -4 -93 0 54 101 -48 0 -52 -16 22 0 -85 76 59 0 -94 69 71 0 49 -87 -110 0 -100 111 -58 0 -38 -15 113 0 19 -55 -95 0 71 -108 34 0 -79 -38 33 0 71 91 -43 0 -26 -87 -114 0 67 73 84 0 113 -82 -90 0 38 -18 67 0 -46 5 12 0 100 82 24 0 81 -10 -110 0 -115 -36 -48 0 -12 95 30 0 -46 -98 102 0 -18 -11 53 0 -81 -103 91 0 -81 -4 12 0 -89 119 -13 0 61 -25 56 0 -72 87 -10 0 -106 9 -115 0 -32 87 45 0 32 -16 -79 0 -50 -80 -27 0 -60 -53 -36 0 36 80 -110 0 -33 -30 -84 0 -107 43 -34 0 62 -100 -30 0 -27 39 25 0 8 120 -24 0 93 -46 -50 0 -7 35 91 0 -115 -110 -53 0 -63 84 -37 0 -3 98 -10 0 33 72 108 0 97 115 -22 0 15 -33 24 0 -5 -62 81 0 48 -16 -46 0 -39 102 -44 0 27 -62 -57 0 26 -40 69 0 24 -70 -101 0 21 46 32 0 100 73 104 0 -46 69 29 0 -97 -107 -113 0 49 -10 90 0 11 -64 97 0 -20 59 117 0 81 5 105 0 -84 72 45 0 2 104 -17 0 -112 18 -42 0 113 -54 76 0 6 10 -62 0 -69 -80 -73 0 26 -107 -8 0 -53 5 47 0 23 89 -120 0 -19 -27 3 0 71 47 119 0 -11 -48 53 0 40 113 -109 0 -37 -116 -94 0 -38 34 -37 0 -10 -110 -47 0 -30 23 102 0 25 -94 1 0 13 -81 39 0 12 38 -9 0 38 37 116 0 38 15 -110 0 -91 -37 -73 0 1 87 -19 0 2 74 -35 0 -19 -7 6 0 101 -63 -102 0 -59 3 -98 0 -59 104 52 0 103 110 -77 0 -105 10 81 0 20 30 62 0 -13 -57 63 0 107 13 3 0 -108 -120 73 0 71 -69 -6 0 40 -56 -77 0 105 -50 113 0 2 -20 -94 0 99 -58 -117 0 -16 20 119 0 -109 12 -88 0 -101 42 18 0 -80 -117 -12 0 119 101 45 0 69 5 -102 0 -48 57 -35 0 20 -73 22 0 -111 9 26 0 7 -71 -67 0 -120 -59 -106 0 43 95 97 0 -113 61 100 0 -66 73 -74 0 95 -24 -26 0 -50 104 -72 0 63 102 -29 0 40 -104 -81 0 114 -73 13 0 -110 -25 -113 0 8 35 -15 0 -52 -17 57 0 -7 104 89 0 109 -2 13 0 63 -115 -36 0 38 -5 -29 0 4 -6 -113 0 9 30 21 0 -85 60 102 0 2 -53 102 0 117 59 27 0 118 -15 72 0 -33 38 -94 0 103 117 -41 0 -24 99 -110 0 6 74 102 0 98 92 -69 0 106 63 48 0 109 96 80 0 51 -44 6 0 73 19 67 0 -21 -109 22 0 -38 19 -37 0 86 16 44 0 -90 58 -93 0 82 20 -74 0 13 34 48 0 80 -84 3 0 61 -104 3 0 -103 -33 -88 0 51 99 29 0 37 -63 4 0 -23 -19 -108 0 -33 13 -52 0 -109 17 87 0 -52 45 -105 0 20 -109 -82 0 98 63 -112 0 -78 35 112 0 2 31 10 0 -101 25 20 0 -120 -108 117 0 -15 -12 10 0 -13 118 26 0 61 -85 -34 0 -72 -29 4 0 61 83 82 0 -26 92 65 0 -82 -56 6 0 -41 -40 18 0 23 5 -17 0 13 -91 109 0 -16 33 -49 0 102 -20 -19 0 -24 31 -43 0 -57 24 119 0 26 80 93 0 -15 32 39 0 -87 55 -103 0 20 3 -108 0 97 13 -63 0 43 71 -55 0 -79 65 -66 0 -110 -55 -99 0 -48 -49 37 0 90 12 93 0 70 -78 -83 0 -84 103 50 0 -65 4 -3 0 99 64 108 0 -22 -62 104 0 -17 -34 93 0 -120 96 -34 0 -116 -30 -39 0 64 -78 -26 0 56 115 30 0 -78 119 65 0 -40 13 -97 0 -45 115 36 0 -65 -63 -29 0 24 16 -87 0 99 -14 -13 0 -76 -42 -85 0 -8 -62 35 0 31 -54 -91 0 -92 -94 -57 0 18 -65 -95 0 18 73 -12 0 -81 -19 54 0 92 -31 27 0 104 69 72 0 -116 86 72 0 -117 -5 -57 0 -101 34 -68 0 110 61 54 0 -41 95 -116 0 -50 101 -36 0 -108 105 -116 0 -105 -82 32 0 18 -77 -118 0 -61 -82 41 0 53 29 33 0 116 -23 58 0 -101 -13 61 0 -91 -78 -59 0 110 -27 -116 0 37 71 28 0 26 60 -54 0 -3 -108 -120 0 27 -9 -34 0 2 73 -109 0 11 -58 -82 0 -40 -28 70 0 -100 -119 -109 0 76 -104 39 0 48 27 8 0 -75 -116 119 0 80 24 -5 0 -43 35 90 0 -8 80 -103 0 -87 75 21 0 -81 -74 -60 0 63 82 -30 0 107 -60 13 0 -22 10 -99 0 72 -49 -68 0 14 16 -39 0 21 63 98 0 -8 -45 -49 0 72 -50 113 0 89 -10 -24 0 -119 -52 -39 0 22 105 -67 0 -34 116 79 0 114 14 38 0 13 119 -86 0 21 -100 70 0 -70 22 105 0 57 -18 88 0 -27 16 -17 0 -38 76 31 0 33 -50 89 0 80 -84 -92 0 111 -28 -4 0 29 -58 81 0 85 -120 -36 0 25 68 48 0 27 26 104 0 57 12 -14 0 -35 -87 40 0 -6 -105 109 0 119 13 31 0 -100 7 15 0 94 44 50 0 98 66 44 0 -73 78 -110 0 -111 -45 -89 0 -109 46 42 0 -55 43 -15 0 38 91 63 0 -44 -67 -111 0 -43 99 85 0 99 -1 37 0 -76 49 -48 0 57 -109 83 0 13 -57 64 0 56 -73 -59 0 99 71 66 0 109 79 -112 0 -32 -84 -75 0 111 43 -38 0 -19 -107 77 0 6 75 56 0 36 -93 78 0 85 72 87 0 -84 10 73 0 99 -48 95 0 49 76 -87 0 33 68 75 0 38 10 -36 0 7 -66 31 0 -26 -31 -30 0 -57 66 -61 0 -98 25 -46 0 93 116 -7 0 39 5 17 0 -18 -40 -116 0 -109 33 4 0 75 -45 -11 0 92 24 -63 0 114 51 -62 0 39 -117 -62 0 -112 39 -33 0 84 -85 -108 0 27 -58 103 0 36 -72 39 0 59 -9 -56 0 83 28 -56 0 -88 -13 -67 0 -102 -38 -80 0 -51 -44 57 0 2 14 40 0 -117 21 115 0 97 108 -18 0 -56 20 -38 0 -93 -22 -5 0 51 36 41 0 -17 104 52 0 113 94 -16 0 92 113 -31 0 -118 67 115 0 108 -25 -94 0 -115 92 -95 0 -61 92 -59 0 42 96 105 0 35 -40 38 0 100 -63 -7 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/95.cnf0000644000175000017500000001157012253362407021727 0ustar georgeskgeorgeskc c SAT instance in DIMACS CNF input format. c p cnf 95 403 -50 -61 95 0 -63 12 79 0 -25 -61 36 0 -6 55 58 0 -72 6 -91 0 8 -44 12 0 10 2 90 0 -64 -70 -71 0 -44 90 61 0 24 -15 26 0 39 -87 90 0 88 -53 25 0 -3 20 45 0 -7 81 95 0 77 -24 49 0 -14 83 85 0 50 7 36 0 94 -29 -76 0 89 -5 -26 0 84 -27 88 0 -40 -9 38 0 -22 23 14 0 16 -85 35 0 59 7 -86 0 -62 56 34 0 46 84 -29 0 65 -42 35 0 37 -15 -16 0 -77 53 -59 0 83 58 21 0 -14 32 72 0 -78 54 60 0 -34 -12 41 0 -53 9 -61 0 -1 82 90 0 81 91 -92 0 -53 -56 79 0 36 -7 -12 0 -48 -39 -5 0 -79 94 -48 0 91 -87 -23 0 -82 31 9 0 -9 1 -60 0 16 25 -7 0 10 50 -81 0 -65 92 75 0 -61 -16 33 0 47 -94 13 0 -29 81 -78 0 -61 -10 49 0 -53 93 94 0 -87 12 -54 0 3 -56 -21 0 69 95 50 0 80 56 -69 0 -66 -56 32 0 -45 18 -43 0 73 77 15 0 -50 -36 34 0 -94 93 -13 0 -19 50 53 0 57 83 3 0 -54 7 6 0 -52 51 86 0 11 59 56 0 30 57 -13 0 31 57 3 0 -4 -53 -62 0 -83 -27 44 0 5 -49 -2 0 -39 -24 57 0 28 -26 -72 0 31 8 -79 0 41 92 -11 0 -3 86 84 0 -20 52 -34 0 -89 -25 -29 0 -45 -87 53 0 -41 38 -11 0 -9 46 -30 0 32 87 85 0 -39 -15 -30 0 65 -27 -14 0 -90 -95 44 0 71 -88 -59 0 -1 -81 87 0 -4 30 -89 0 -92 27 -62 0 62 -46 -47 0 -28 -79 61 0 -82 -81 -58 0 53 11 58 0 -17 -1 26 0 -89 -33 74 0 -81 53 89 0 -2 92 -40 0 95 22 -45 0 21 -78 55 0 -3 -1 57 0 90 28 76 0 36 92 25 0 27 64 -4 0 -65 -24 39 0 -41 -53 68 0 91 -31 -11 0 -95 -66 28 0 54 -50 -59 0 -14 62 -51 0 -79 -67 39 0 52 92 67 0 32 23 69 0 -38 93 -18 0 -52 -80 56 0 -67 94 36 0 15 -46 -95 0 -73 49 14 0 -56 -32 88 0 -57 -83 -29 0 -45 -17 -20 0 49 8 -52 0 78 -80 -56 0 -39 38 84 0 -21 -89 63 0 -68 52 93 0 -28 19 -41 0 81 42 -91 0 -9 36 73 0 80 -56 54 0 -74 -19 -65 0 58 2 41 0 -3 50 -57 0 10 30 2 0 -1 -85 -91 0 -26 92 -95 0 27 39 51 0 10 -4 -58 0 -1 -30 87 0 4 55 14 0 28 54 45 0 47 -11 -30 0 -51 12 14 0 -1 2 -15 0 -3 71 8 0 -7 22 -75 0 -40 85 -68 0 26 29 -11 0 43 94 -64 0 -84 74 38 0 62 -82 -30 0 -84 4 48 0 8 92 47 0 -90 89 -73 0 93 -87 30 0 30 -8 4 0 -65 -44 -30 0 50 93 6 0 -19 28 88 0 91 -95 13 0 31 -62 -95 0 -38 -27 -95 0 36 66 -54 0 -3 -63 10 0 69 49 59 0 19 75 88 0 36 11 20 0 76 67 -3 0 -38 16 44 0 42 24 -65 0 -77 -55 32 0 -63 -81 30 0 -1 -58 75 0 26 84 -85 0 93 -49 -70 0 -57 -90 -60 0 -18 -16 -81 0 87 -5 -59 0 62 -2 -95 0 -24 -9 -10 0 -45 52 3 0 39 25 83 0 36 28 -89 0 42 33 -59 0 -25 36 -66 0 -55 74 54 0 -25 -1 -6 0 -6 -93 43 0 68 -84 17 0 -54 53 -24 0 39 22 47 0 -37 -4 54 0 88 -79 -83 0 -14 88 19 0 66 -48 95 0 -28 10 -48 0 3 22 40 0 18 36 -76 0 -17 16 -7 0 60 8 28 0 -45 -64 -82 0 -14 -67 -92 0 26 -82 7 0 -20 -92 84 0 34 -62 35 0 62 -57 -37 0 27 32 -57 0 16 -20 -81 0 -5 24 -30 0 -15 52 -35 0 15 5 66 0 44 -65 60 0 17 -52 -22 0 57 75 85 0 -94 -49 -50 0 22 50 -21 0 93 43 5 0 65 -58 13 0 13 15 32 0 50 -62 33 0 -88 -3 -33 0 -81 26 43 0 66 37 -7 0 34 -89 -53 0 -4 34 -71 0 59 23 -52 0 55 -4 -94 0 -61 42 72 0 43 59 -3 0 -85 34 4 0 -66 -25 77 0 77 21 -10 0 71 46 -27 0 23 -76 -77 0 18 16 -64 0 -83 -58 -85 0 67 -14 -45 0 85 77 66 0 -13 -2 92 0 -90 92 -23 0 40 -2 72 0 87 -17 30 0 -94 -85 49 0 88 -11 -43 0 70 76 92 0 78 -43 10 0 45 -20 -26 0 39 77 -35 0 75 82 61 0 -19 93 -21 0 -68 82 -39 0 -11 -58 -13 0 -57 85 95 0 -17 -60 -24 0 8 -40 43 0 -49 -76 -47 0 -35 -48 36 0 91 -28 -51 0 -46 17 -29 0 -46 -74 3 0 78 -83 -68 0 22 -82 -40 0 63 30 42 0 -9 64 -13 0 -80 -46 -89 0 -33 -74 -64 0 58 52 -80 0 -44 23 88 0 -38 86 -40 0 -81 -71 -68 0 -17 91 -90 0 -44 -78 -94 0 -59 20 46 0 -52 86 31 0 83 -44 94 0 -48 -19 -86 0 -52 69 -30 0 2 -26 -56 0 19 -49 -21 0 39 3 33 0 83 85 -30 0 -85 -9 -10 0 -5 -82 -7 0 27 -47 -79 0 -48 -84 -55 0 -53 -73 -36 0 62 -34 83 0 -13 -22 47 0 71 -52 -16 0 -39 48 10 0 12 -16 2 0 -65 88 64 0 -84 -61 -22 0 19 66 -43 0 -15 -64 -84 0 -57 25 51 0 4 -88 50 0 35 55 2 0 -77 -73 -92 0 -32 63 20 0 -29 66 -43 0 93 -88 1 0 3 54 61 0 -46 -94 55 0 27 -28 53 0 -13 -19 -2 0 -57 -8 -75 0 -71 55 -17 0 -83 -15 -48 0 44 -22 90 0 -34 -93 -31 0 -82 67 51 0 -80 37 87 0 -60 -29 8 0 73 68 -51 0 -76 -25 -9 0 -5 -85 63 0 -49 -71 65 0 89 77 81 0 91 -67 4 0 -2 -14 -20 0 -73 -19 88 0 -39 -42 8 0 -23 61 12 0 45 7 11 0 86 54 36 0 -2 -95 47 0 5 9 92 0 -39 69 -71 0 35 85 -77 0 -31 -23 -33 0 56 -4 -89 0 -26 23 -20 0 30 90 25 0 -37 -94 -30 0 32 -27 -53 0 -8 -86 43 0 -72 3 50 0 -26 6 -76 0 -58 38 -41 0 50 -40 64 0 18 -14 32 0 80 -94 -52 0 -6 79 90 0 71 -74 -70 0 -5 64 -40 0 20 -79 83 0 24 10 36 0 74 83 -54 0 48 -12 20 0 -72 -37 -80 0 -22 14 -18 0 -94 52 41 0 -82 17 -37 0 -9 -21 -19 0 -5 28 -78 0 48 -67 -16 0 51 -1 -70 0 95 -21 -93 0 -3 -70 -69 0 50 -70 -24 0 77 -57 -78 0 84 -22 -19 0 -16 63 55 0 50 30 77 0 -69 -79 88 0 -79 89 -87 0 75 80 -68 0 6 -38 41 0 14 -58 -24 0 -70 -12 -53 0 -27 -38 63 0 38 -36 -64 0 83 27 88 0 -53 -64 94 0 36 -75 -95 0 7 84 -23 0 76 -45 72 0 -38 66 -62 0 -45 49 21 0 46 -41 76 0 29 -82 -2 0 -74 45 81 0 -21 78 87 0 -65 59 -25 0 84 61 -77 0 -13 93 -27 0 93 -92 24 0 54 12 68 0 32 5 -30 0 59 66 10 0 -44 -93 -70 0 -23 -54 -88 0 -36 -69 23 0 -77 6 -79 0 83 80 -59 0 27 -29 41 0 46 14 68 0 -13 -16 89 0 -75 -45 -30 0 74 63 87 0 38 40 82 0 74 -68 -31 0 88 13 -72 0 72 43 10 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/50.cnf0000644000175000017500000000500612253362407021713 0ustar georgeskgeorgeskc c SAT instance in DIMACS CNF input format. c p cnf 50 212 -1 -22 -25 0 25 -37 -21 0 -36 26 -45 0 -7 -39 9 0 3 2 -14 0 -14 37 -13 0 22 45 12 0 -11 -21 20 0 -31 29 -49 0 -16 38 6 0 49 50 -39 0 17 -28 18 0 -6 12 -9 0 -2 -32 -45 0 -10 -18 -28 0 27 14 40 0 6 49 9 0 24 18 22 0 33 25 -23 0 -5 -14 -39 0 31 41 29 0 50 19 -33 0 39 -29 16 0 13 -34 29 0 -34 -28 11 0 -35 34 -3 0 -19 44 1 0 -36 10 49 0 35 -30 -21 0 37 -43 9 0 34 16 45 0 -9 -1 26 0 18 -42 14 0 45 11 7 0 -40 -21 -3 0 20 41 12 0 -6 -21 -15 0 -39 42 -24 0 -23 -40 -45 0 -36 44 21 0 -4 21 -48 0 -42 29 35 0 20 4 48 0 -15 6 -40 0 36 -15 48 0 11 5 31 0 3 -21 -23 0 33 2 -5 0 2 -29 32 0 19 32 -26 0 -16 -1 -17 0 43 24 -40 0 -31 -40 14 0 -16 13 43 0 32 -38 14 0 -9 -3 -5 0 -4 18 -6 0 5 48 49 0 50 -43 -4 0 22 13 -33 0 8 7 -21 0 -31 -30 36 0 -46 -4 7 0 15 5 19 0 3 41 20 0 -10 -31 29 0 -43 44 -39 0 7 29 39 0 -12 42 -44 0 8 -41 47 0 -30 -26 -8 0 21 23 29 0 39 -50 40 0 -30 50 41 0 -22 -18 14 0 19 -34 26 0 -49 9 2 0 26 47 6 0 -8 -5 46 0 -43 -17 -5 0 39 47 -40 0 -5 21 -6 0 34 28 13 0 25 38 -30 0 -14 28 40 0 7 -49 11 0 -46 -21 -28 0 1 14 -10 0 -9 -49 2 0 36 -4 30 0 43 23 40 0 21 -17 47 0 31 -1 30 0 -33 -29 -37 0 -27 -14 34 0 -17 13 -22 0 17 -6 -8 0 6 -23 -34 0 19 -43 -39 0 36 -45 33 0 23 6 -20 0 23 -7 13 0 50 39 23 0 1 -26 -38 0 -16 29 -42 0 50 -24 6 0 -14 -19 -12 0 27 9 -33 0 21 -13 46 0 -48 -2 -16 0 14 -8 40 0 31 -12 30 0 50 41 46 0 29 37 24 0 -22 -46 11 0 -5 -9 -30 0 25 -7 -34 0 28 26 41 0 -2 20 45 0 -43 6 8 0 -32 13 31 0 47 -33 -18 0 -13 19 30 0 -38 20 -50 0 -28 45 -20 0 27 9 3 0 -9 -8 -18 0 -42 22 -45 0 -1 -12 -44 0 -26 -12 25 0 2 -32 -48 0 -11 -41 -48 0 -42 -26 -31 0 39 42 48 0 2 36 44 0 -37 -9 -24 0 -31 -6 28 0 -14 46 13 0 -2 40 -21 0 -5 3 -21 0 20 7 31 0 -43 3 5 0 -33 -36 -50 0 -29 -44 -35 0 40 30 -48 0 11 17 -45 0 30 -34 -35 0 -20 -21 19 0 -33 30 -29 0 -35 1 43 0 45 -37 -20 0 47 7 -32 0 -20 17 48 0 -44 8 -7 0 20 9 17 0 42 -25 -34 0 -49 -32 -39 0 -1 36 -30 0 16 11 7 0 -48 41 7 0 40 -45 41 0 50 -7 -9 0 23 14 43 0 25 9 -46 0 -31 15 9 0 31 35 23 0 1 42 15 0 -9 35 1 0 -35 -39 -16 0 -46 -8 28 0 -5 33 32 0 -34 -25 -21 0 -36 -48 15 0 1 47 28 0 32 37 -39 0 -27 -31 8 0 1 -28 5 0 39 23 -29 0 -23 -42 34 0 -22 38 -1 0 2 -27 -45 0 -41 -27 -29 0 -12 -41 49 0 19 46 -2 0 -11 24 44 0 2 -27 -6 0 46 -25 -14 0 -10 -6 -34 0 49 -45 37 0 9 -17 37 0 14 39 1 0 11 40 44 0 -18 23 1 0 -8 34 -26 0 30 -11 -45 0 45 13 -34 0 -38 12 10 0 -2 44 14 0 11 47 -32 0 49 -42 10 0 -9 21 26 0 35 38 41 0 -12 17 -13 0 22 -24 40 0 36 -22 26 0 29 -38 26 0 42 -9 34 0 21 26 9 0 24 16 -11 0 -34 24 -47 0 20 13 -14 0 -12 -1 -31 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/55.cnf0000644000175000017500000000546312253362407021727 0ustar georgeskgeorgeskc c SAT instance in DIMACS CNF input format. c p cnf 55 233 -28 -37 -48 0 -48 29 3 0 12 -44 53 0 46 6 -30 0 -38 -34 16 0 -22 19 -52 0 -53 -48 -45 0 55 6 -54 0 -51 30 39 0 -29 52 45 0 -48 50 2 0 12 -31 -42 0 -26 50 -13 0 -42 -54 48 0 22 -50 -53 0 30 -41 13 0 -53 -20 -11 0 -28 29 44 0 -42 48 -4 0 48 -42 33 0 36 37 -53 0 8 9 55 0 -34 -54 -51 0 -32 28 -21 0 -53 -8 -22 0 -45 -23 -11 0 26 -31 -13 0 8 -43 35 0 -32 36 8 0 -50 15 -53 0 28 -42 -26 0 10 -19 54 0 26 -10 49 0 33 1 -27 0 -4 -50 -16 0 44 34 -11 0 -1 -16 36 0 50 46 32 0 -20 -23 -30 0 -25 21 -18 0 8 -38 27 0 -46 35 -7 0 47 19 -48 0 7 30 36 0 43 -54 -11 0 -17 -53 5 0 51 -54 -47 0 3 -1 -44 0 -40 -52 14 0 16 -30 50 0 5 4 -43 0 -9 -48 -10 0 20 -28 -32 0 -15 -26 37 0 -1 -39 38 0 39 -53 49 0 10 -29 -37 0 -11 -27 -23 0 -50 -38 26 0 -33 40 -4 0 -21 -45 -12 0 -2 -45 -29 0 -7 49 36 0 15 2 47 0 -13 -20 24 0 -40 46 -27 0 -23 -15 11 0 -17 37 -15 0 26 -20 -51 0 53 -1 51 0 45 -43 -3 0 38 -43 -52 0 10 -38 48 0 31 -25 9 0 -7 -1 -32 0 2 -18 -5 0 34 -47 -19 0 2 -26 -50 0 -35 -8 12 0 -14 -19 -28 0 -55 -23 9 0 11 53 -36 0 -41 12 54 0 -42 24 15 0 41 5 -22 0 9 12 34 0 -43 35 -41 0 12 -2 -41 0 33 28 -20 0 -9 -7 -47 0 -2 -26 21 0 1 42 -40 0 5 -1 33 0 11 -10 -30 0 -6 20 -19 0 -28 9 3 0 14 -42 12 0 -55 19 -50 0 -2 38 30 0 34 21 -43 0 36 -28 -9 0 -15 -34 11 0 2 -16 -31 0 -11 -33 43 0 -9 47 1 0 5 -43 -42 0 2 37 -18 0 -36 -35 -44 0 22 20 -52 0 23 -35 16 0 40 44 -7 0 -8 -23 -17 0 -3 -40 -49 0 -19 -14 -32 0 35 -13 -21 0 12 50 13 0 29 -9 -11 0 -51 -43 -12 0 51 3 32 0 23 51 -33 0 28 -21 55 0 -33 -34 -2 0 2 21 23 0 -6 -34 -15 0 -48 53 38 0 2 37 12 0 34 -29 -48 0 4 48 -25 0 37 41 35 0 8 39 -3 0 -15 37 -49 0 -55 -54 -14 0 25 21 -34 0 -7 38 27 0 -46 -17 -5 0 7 29 -42 0 -11 -2 24 0 -35 -39 -23 0 24 -48 15 0 22 -45 -15 0 15 -22 -52 0 -49 -41 47 0 25 12 39 0 -32 27 24 0 -34 44 -7 0 -8 -54 42 0 -20 41 26 0 20 -17 11 0 -3 29 -26 0 49 -12 -29 0 -26 13 23 0 13 18 38 0 -5 7 13 0 20 -6 42 0 -8 -4 20 0 -41 47 19 0 53 -1 32 0 11 7 55 0 -29 -50 40 0 29 -54 1 0 5 3 47 0 1 -19 -17 0 -7 36 54 0 38 2 25 0 -8 55 -9 0 -26 -11 6 0 -9 12 31 0 -49 25 -7 0 -49 -10 -9 0 24 3 22 0 47 9 -31 0 -23 39 27 0 23 -14 40 0 -9 -32 -26 0 -6 -29 -54 0 45 -29 -2 0 -21 28 -4 0 -41 52 -50 0 -11 18 43 0 15 -50 11 0 -50 -2 32 0 21 40 -54 0 -34 29 41 0 -4 12 33 0 -46 10 15 0 -22 37 54 0 2 -36 -40 0 20 12 -14 0 -40 28 -2 0 29 13 27 0 53 50 51 0 -8 -52 -36 0 -4 -52 -30 0 -24 14 11 0 -49 53 44 0 37 -21 55 0 46 -24 28 0 14 54 -43 0 50 25 29 0 -4 -45 31 0 -27 1 8 0 -38 -10 12 0 31 -42 18 0 -21 19 -38 0 17 20 26 0 9 -42 49 0 -32 29 -3 0 -53 -39 -24 0 19 -16 6 0 25 -27 -38 0 -39 -33 -38 0 35 38 -42 0 32 14 37 0 51 -37 -34 0 14 -48 44 0 22 47 44 0 -38 -30 -21 0 51 -43 -3 0 -20 5 32 0 12 -31 34 0 47 43 33 0 -14 2 30 0 -36 44 52 0 -12 11 -13 0 33 -52 -3 0 31 49 35 0 -23 -8 16 0 -12 -41 -36 0 -36 14 -39 0 20 15 -33 0 21 -45 26 0 2 -49 -41 0 -55 -45 53 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/145.cnf0000644000175000017500000001774012253362407022010 0ustar georgeskgeorgeskp cnf 145 616 95 -19 8 0 74 -136 -101 0 92 -57 -85 0 139 52 -93 0 11 102 -38 0 -37 121 -101 0 100 130 25 0 -26 -107 -84 0 84 -121 57 0 105 -22 -7 0 -66 45 -72 0 -75 -6 -103 0 25 57 -110 0 -114 -12 -31 0 15 -82 29 0 -129 67 1 0 39 -85 -1 0 -126 -10 -140 0 7 -119 -118 0 28 -63 8 0 131 -101 118 0 128 3 -56 0 -12 -134 1 0 12 -37 -47 0 79 17 111 0 94 -40 60 0 -114 -112 25 0 43 119 -66 0 125 89 -30 0 39 -136 37 0 -80 13 -76 0 46 121 -54 0 -135 92 43 0 18 -138 -134 0 -40 79 -26 0 91 131 54 0 -99 88 136 0 142 14 -92 0 -107 42 -22 0 65 61 123 0 -60 120 44 0 -16 -101 142 0 -96 4 -28 0 -91 57 129 0 136 -47 -139 0 -45 119 -29 0 -76 -61 16 0 -1 121 105 0 101 -74 50 0 17 57 -13 0 122 -132 -69 0 -99 -88 -68 0 120 -51 108 0 124 -3 -105 0 74 -9 -136 0 44 6 107 0 19 -137 -101 0 44 134 -25 0 77 -71 -58 0 -74 -38 -116 0 -9 73 -2 0 48 92 63 0 81 -28 134 0 -110 76 87 0 61 28 -137 0 -8 117 33 0 -22 -38 49 0 -130 -19 -2 0 110 11 -9 0 -72 -136 13 0 -109 72 -108 0 -127 -21 94 0 82 -23 97 0 -73 -99 139 0 -84 -52 1 0 112 -71 -126 0 30 75 -29 0 -42 -88 4 0 -84 108 53 0 94 72 -130 0 -99 -110 32 0 -33 -125 -112 0 29 63 -67 0 -140 -80 -88 0 -26 133 -64 0 107 46 -30 0 -18 -50 -106 0 115 -139 2 0 -48 55 71 0 -45 72 50 0 -10 -14 72 0 -145 61 71 0 -61 -119 73 0 75 -87 -16 0 -122 75 74 0 52 -135 -40 0 -55 39 100 0 8 -80 -34 0 127 131 64 0 -135 4 -141 0 92 -141 44 0 -114 -131 -133 0 -97 -60 -64 0 -113 19 105 0 124 53 57 0 -19 -62 -144 0 37 110 -125 0 8 18 38 0 -63 -129 -9 0 -123 -17 -115 0 -21 106 -117 0 -76 18 -71 0 131 -96 -99 0 87 96 -57 0 -76 63 -103 0 52 26 44 0 -80 -16 139 0 59 76 133 0 26 54 21 0 118 -89 2 0 93 -85 -32 0 24 -15 39 0 60 -34 -28 0 59 -74 71 0 112 -131 129 0 -56 -133 52 0 -59 71 -114 0 -112 29 -14 0 113 27 48 0 3 -39 118 0 -72 29 76 0 -44 80 94 0 -7 -23 -130 0 -40 -57 48 0 102 -36 -42 0 37 48 19 0 -33 -7 -32 0 7 110 58 0 -44 -61 48 0 -116 13 53 0 -99 -102 20 0 58 25 56 0 38 -12 137 0 11 97 -138 0 -10 -49 7 0 49 135 30 0 -70 -28 54 0 -131 118 76 0 81 56 -97 0 106 75 -86 0 -48 -51 -115 0 81 24 -1 0 92 -52 68 0 30 34 100 0 35 -114 89 0 81 97 -128 0 -83 -43 19 0 -54 122 -91 0 90 -130 -95 0 -65 -140 -39 0 -67 -111 44 0 -124 69 135 0 -23 -29 21 0 87 75 -13 0 45 64 -4 0 -104 111 -38 0 127 -42 15 0 -126 72 7 0 118 96 -31 0 53 -11 -130 0 -1 -27 34 0 -39 117 -136 0 119 -82 68 0 77 -20 14 0 118 -108 95 0 66 6 -2 0 62 -35 89 0 113 -123 5 0 -76 10 46 0 -133 128 144 0 2 27 40 0 -98 -66 45 0 -10 -78 30 0 41 52 8 0 -120 -37 144 0 -18 -27 1 0 -97 35 13 0 -139 -63 34 0 -92 -99 43 0 25 110 58 0 -108 114 142 0 105 104 -13 0 -43 -59 105 0 -20 -25 -145 0 -83 -15 46 0 77 -43 122 0 44 -91 122 0 45 -49 -73 0 74 39 28 0 -139 59 96 0 62 77 -85 0 -111 117 72 0 101 138 1 0 -85 111 12 0 -38 -131 -17 0 -81 -142 133 0 96 114 32 0 84 -142 133 0 100 109 55 0 -58 -144 -118 0 51 -143 139 0 124 -22 -8 0 45 -18 138 0 -2 -19 81 0 55 20 -144 0 -59 -65 -117 0 115 -33 101 0 -59 121 104 0 118 143 66 0 111 -32 24 0 116 -23 -53 0 -11 61 -5 0 68 -46 11 0 -82 -29 26 0 135 115 -96 0 -51 -130 65 0 -35 60 72 0 -124 -8 60 0 20 -87 -14 0 -144 74 -67 0 -38 -108 -40 0 -54 76 59 0 -12 128 34 0 -142 -131 62 0 -42 74 69 0 -47 -5 131 0 78 125 -116 0 87 88 136 0 -42 132 4 0 124 -111 31 0 98 121 -94 0 101 80 -133 0 -118 142 -64 0 55 -14 -25 0 49 -39 114 0 139 81 91 0 4 -94 77 0 73 -80 51 0 50 -45 144 0 63 10 -98 0 19 -42 73 0 25 39 -37 0 -101 49 -4 0 7 139 54 0 143 -34 4 0 55 -52 -44 0 145 1 108 0 96 -22 116 0 -143 47 58 0 -46 25 -145 0 53 -95 115 0 85 70 -91 0 141 -126 -131 0 -91 129 -75 0 -97 51 76 0 105 2 1 0 31 -73 34 0 -83 134 -136 0 -62 73 50 0 50 20 -30 0 25 137 -70 0 -34 -38 -5 0 82 7 116 0 -68 75 50 0 52 -104 -143 0 -131 133 -62 0 -73 -11 -7 0 -66 -30 136 0 -70 -142 -113 0 103 134 74 0 -5 77 9 0 141 -13 43 0 119 116 129 0 111 -8 -33 0 89 -74 101 0 -109 -76 18 0 -65 35 118 0 12 96 -47 0 -121 -34 -61 0 -143 -128 31 0 117 -67 -58 0 37 84 -42 0 -92 -27 -3 0 1 52 41 0 113 125 26 0 -87 -92 -135 0 64 -32 23 0 -1 -68 -99 0 -124 -106 -34 0 -57 76 65 0 -98 -52 -83 0 -43 -31 -62 0 -24 -144 -120 0 38 121 51 0 -131 6 65 0 -1 -44 91 0 91 15 112 0 -68 94 67 0 -140 -50 -68 0 -66 -115 50 0 -12 80 112 0 -118 -139 65 0 -33 -116 -79 0 -14 17 59 0 -50 -36 -15 0 121 -98 -122 0 -142 -34 -29 0 -107 -50 -49 0 -10 30 36 0 -56 17 10 0 -121 29 36 0 -29 53 -103 0 51 59 -56 0 79 -127 -49 0 -46 136 121 0 121 -109 11 0 -48 12 91 0 -38 -78 -116 0 -88 -5 89 0 71 -55 -104 0 1 109 -57 0 -70 134 75 0 -59 -18 32 0 -118 126 -104 0 -111 -67 -1 0 -26 81 22 0 -124 132 -81 0 120 59 -44 0 6 99 -109 0 41 -9 -102 0 26 -44 30 0 -55 14 -102 0 123 75 -17 0 86 -121 81 0 13 -43 -81 0 12 31 -98 0 -126 -87 86 0 -121 -15 83 0 -114 24 111 0 -94 19 46 0 84 -75 98 0 -137 -17 -19 0 -7 104 108 0 41 -138 -93 0 -118 -117 11 0 -50 -132 117 0 126 134 56 0 113 -17 -47 0 15 71 144 0 84 92 62 0 135 -93 13 0 -55 90 -132 0 64 91 81 0 20 -33 -61 0 -52 46 63 0 -18 -56 1 0 22 -144 -118 0 73 61 -118 0 25 -45 42 0 77 136 127 0 141 -98 110 0 -82 -1 12 0 38 -19 15 0 126 -28 65 0 -102 80 -124 0 87 -68 19 0 -144 -8 47 0 -134 100 114 0 -75 112 -36 0 94 -96 -81 0 -108 -83 5 0 -60 -46 -116 0 -51 -132 70 0 137 109 -85 0 85 2 102 0 -73 -141 -32 0 94 11 7 0 52 -123 -25 0 73 113 123 0 118 -42 108 0 -38 61 124 0 24 -32 5 0 -130 64 -83 0 34 138 119 0 -33 -11 -83 0 -111 74 14 0 14 115 -43 0 144 -11 95 0 -79 35 -14 0 -3 -37 11 0 -74 23 112 0 106 52 66 0 -104 -106 -79 0 -102 133 -86 0 58 -77 -9 0 -18 102 -47 0 -21 77 139 0 83 -123 -37 0 20 -134 -97 0 117 -85 17 0 -124 -78 64 0 -117 7 -15 0 -115 -44 -16 0 -135 125 141 0 -12 -79 -68 0 42 125 13 0 -2 -55 126 0 18 117 -85 0 34 103 -144 0 45 -86 -62 0 77 142 -136 0 -37 -12 -85 0 -123 131 -65 0 -59 123 -93 0 -123 95 -55 0 -91 -141 114 0 -127 21 -27 0 -25 -104 -112 0 -127 111 -66 0 -140 115 114 0 -68 -49 89 0 -3 100 121 0 -69 -11 -22 0 20 -81 -102 0 2 -3 119 0 61 63 108 0 -80 -83 -41 0 -57 -27 111 0 93 31 66 0 -48 10 -86 0 32 -132 -134 0 -87 113 -121 0 -100 -55 -47 0 -40 26 94 0 -11 58 -36 0 24 -50 -80 0 -93 36 45 0 142 103 130 0 15 20 -114 0 4 23 -84 0 28 -133 22 0 41 -145 44 0 -137 68 88 0 88 132 -6 0 123 -38 -142 0 -113 66 -122 0 43 46 -138 0 -69 -41 65 0 141 -72 62 0 100 -13 74 0 -67 104 90 0 21 138 -124 0 142 -10 133 0 -26 82 -29 0 -59 -145 -93 0 77 132 145 0 65 -143 116 0 111 -113 -129 0 -114 -81 112 0 -109 -141 85 0 -118 114 127 0 -126 -35 -134 0 -45 -40 59 0 -142 131 122 0 -74 -92 -23 0 -14 123 141 0 -102 -47 -87 0 -88 106 73 0 57 126 -46 0 -98 -89 68 0 -98 -21 102 0 -5 -63 130 0 34 -1 15 0 -68 109 91 0 123 23 20 0 -139 92 -86 0 -109 -118 -122 0 -65 93 143 0 101 48 80 0 -25 -52 -114 0 -31 13 -109 0 43 -97 -101 0 -138 32 12 0 -56 -2 139 0 29 -63 -11 0 -109 59 -136 0 112 -99 54 0 82 22 -121 0 -141 -137 134 0 -57 32 -34 0 -20 -136 135 0 41 86 93 0 1 -106 -78 0 4 41 -101 0 -133 127 126 0 -9 -106 38 0 -116 -119 -14 0 -80 -45 -133 0 -52 -62 -92 0 -46 49 135 0 138 48 126 0 -4 -142 -35 0 -122 6 9 0 -74 -53 78 0 -2 55 7 0 99 -144 135 0 87 -66 -18 0 125 -41 -104 0 90 138 119 0 -19 -27 -90 0 -55 68 140 0 115 55 -22 0 -81 -84 44 0 47 81 -24 0 123 86 48 0 -21 -27 82 0 82 93 63 0 118 50 140 0 90 40 137 0 -100 113 29 0 112 -4 77 0 -78 84 -25 0 -92 101 -84 0 100 -21 -56 0 -87 121 -25 0 141 -23 58 0 -75 26 53 0 -5 -143 28 0 -25 -14 11 0 50 106 116 0 84 112 61 0 57 84 -122 0 -136 -82 124 0 -129 135 29 0 -104 -93 -38 0 128 27 132 0 87 13 50 0 -39 -52 89 0 -60 -122 52 0 -31 29 136 0 -130 48 84 0 7 -109 142 0 134 14 -2 0 -77 23 -57 0 -9 -40 -100 0 110 -30 -56 0 30 -67 -8 0 -52 124 -62 0 -50 -63 21 0 -19 -22 -132 0 -16 -2 36 0 -98 -65 -30 0 8 74 142 0 57 126 35 0 -138 132 -119 0 78 -59 -128 0 22 -117 -84 0 -111 -117 9 0 27 -2 77 0 15 38 98 0 51 -125 11 0 28 111 -54 0 -91 -38 -93 0 -65 -145 104 0 48 -91 107 0 2 40 -65 0 -22 -40 -74 0 -78 101 24 0 59 124 -39 0 104 51 3 0 118 145 -71 0 56 -53 -9 0 49 -22 -27 0 -2 65 -97 0 137 86 -11 0 -140 46 -54 0 -6 -72 49 0 65 -55 -127 0 92 24 29 0 -22 73 -4 0 -103 79 59 0 -6 -81 5 0 -43 47 -19 0 -142 28 56 0 25 -23 133 0 91 14 79 0 -31 119 -96 0 -3 -140 -109 0 22 -110 75 0 -3 -101 -11 0 125 1 -121 0 144 92 -83 0 62 52 -74 0 -33 -111 -128 0 -50 17 66 0 -92 -112 6 0 68 135 -106 0 92 -80 -54 0 -116 48 145 0 47 121 62 0 120 -76 89 0 -33 -134 -118 0 128 85 119 0 -52 -58 105 0 86 26 -130 0 81 92 15 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/75.cnf0000644000175000017500000000751012253362407021724 0ustar georgeskgeorgeskc c SAT instance in DIMACS CNF input format. c p cnf 75 318 8 34 -29 0 29 -21 16 0 62 61 1 0 -74 -8 -1 0 69 -71 -40 0 -73 50 -54 0 7 -35 -32 0 -52 35 67 0 17 23 27 0 21 -28 63 0 15 -12 62 0 1 12 27 0 48 -5 20 0 75 29 -3 0 65 -52 56 0 29 -69 13 0 -34 -42 -74 0 71 46 -10 0 -69 -17 28 0 61 6 -16 0 -23 13 45 0 44 -23 51 0 17 28 -50 0 63 61 -43 0 10 31 -41 0 15 13 73 0 -42 -58 30 0 -27 -55 22 0 55 -2 -69 0 -5 -63 32 0 55 -2 -74 0 -52 69 43 0 -23 46 -59 0 -13 74 -19 0 -20 -70 23 0 5 -65 40 0 -2 -48 -15 0 29 -65 48 0 -49 -59 -25 0 -26 -75 -50 0 57 39 -8 0 71 55 -72 0 70 5 75 0 71 -36 -9 0 -3 49 22 0 -4 1 64 0 52 -12 -27 0 -38 -5 -59 0 -58 50 -74 0 20 -65 40 0 4 34 12 0 -55 -35 51 0 -4 -1 69 0 -9 -70 3 0 -19 44 71 0 6 -16 -52 0 -60 19 67 0 -7 -13 52 0 11 -2 -13 0 -2 9 -75 0 -16 56 36 0 7 38 -49 0 34 27 -71 0 45 55 40 0 -48 -54 -2 0 7 -33 -60 0 -60 43 70 0 -61 40 7 0 11 35 -23 0 -54 -69 -52 0 -28 -74 70 0 19 64 23 0 31 16 -47 0 -52 -21 14 0 -30 23 -47 0 63 -54 -69 0 -7 23 -27 0 -24 -70 8 0 -50 -19 9 0 -44 67 -65 0 46 57 24 0 -21 30 -17 0 45 43 73 0 64 1 26 0 -4 -27 21 0 33 16 -34 0 -57 70 -26 0 -64 -53 -61 0 68 58 14 0 -10 70 20 0 42 -54 -59 0 -65 62 5 0 62 28 -57 0 -61 2 29 0 -20 -21 68 0 -58 -19 44 0 -43 -63 -30 0 -3 66 -46 0 -37 -7 52 0 49 -47 -42 0 55 18 -44 0 35 68 47 0 -61 -54 23 0 63 -20 21 0 -64 60 71 0 23 -11 49 0 36 58 -16 0 -17 -66 -24 0 -3 -68 64 0 -74 2 -44 0 -47 -67 -25 0 68 4 -48 0 27 74 -44 0 -20 -68 -21 0 -70 48 9 0 40 -42 -43 0 -25 48 -6 0 28 42 -59 0 -28 -58 7 0 11 -61 72 0 -9 48 -67 0 20 -19 49 0 58 28 40 0 -18 -16 67 0 -12 -27 -52 0 -45 75 -37 0 52 -12 13 0 55 16 73 0 10 44 23 0 -65 57 37 0 3 -22 19 0 -20 -31 24 0 69 -53 -24 0 33 50 -39 0 -66 -50 -38 0 36 69 20 0 -71 -65 -24 0 56 -47 54 0 4 -53 -24 0 -18 -51 50 0 -64 5 -75 0 -68 -53 -22 0 27 3 58 0 47 28 -9 0 -53 -44 -42 0 -29 -18 17 0 -73 -25 -70 0 67 -37 -61 0 45 -44 -33 0 1 45 3 0 -19 -58 35 0 6 3 50 0 43 59 38 0 -66 27 10 0 57 28 -35 0 -12 -23 -32 0 2 57 -37 0 -39 -10 -25 0 64 -11 -48 0 54 -56 -18 0 39 -5 49 0 -10 35 -39 0 -70 -56 71 0 -20 72 29 0 4 -24 2 0 -12 -8 65 0 -61 -8 -5 0 -32 -36 -71 0 -7 -46 57 0 39 -53 30 0 -54 -16 -45 0 62 45 49 0 72 -1 69 0 -46 57 62 0 -4 -41 -56 0 -17 -11 -21 0 3 28 40 0 -6 -40 -71 0 66 58 64 0 22 -45 60 0 75 41 64 0 37 43 -52 0 2 56 -27 0 1 10 -6 0 -64 -56 -74 0 -66 4 50 0 17 52 1 0 -67 -19 -59 0 -57 -49 18 0 54 -43 -50 0 -70 -11 31 0 -13 -27 -61 0 42 7 -58 0 -17 -37 -6 0 -21 50 -9 0 -30 60 11 0 16 -73 45 0 -74 -44 -36 0 -27 -46 -53 0 24 27 -18 0 -3 -67 63 0 37 59 73 0 -72 4 64 0 12 42 -23 0 -63 8 41 0 18 67 -13 0 60 22 -62 0 -11 38 33 0 -21 55 38 0 19 -5 68 0 -18 33 -26 0 23 2 -28 0 32 -49 -63 0 -58 40 -42 0 64 -46 -57 0 13 -25 -55 0 75 42 -9 0 33 7 -20 0 22 -62 43 0 -27 55 8 0 -28 12 -27 0 11 47 -13 0 17 -59 31 0 65 -53 51 0 -22 7 -20 0 -1 66 35 0 53 4 65 0 35 -19 15 0 -10 50 -28 0 -20 -6 -71 0 -50 58 -49 0 34 26 72 0 29 -3 -1 0 -18 68 -32 0 -31 42 44 0 -24 -25 -35 0 33 54 -10 0 23 -1 -18 0 -17 12 39 0 -17 15 32 0 64 58 43 0 9 34 15 0 -7 59 38 0 -29 39 -50 0 -25 -49 35 0 -27 54 11 0 14 -38 7 0 36 58 75 0 65 66 -71 0 62 4 -23 0 -49 -75 48 0 74 33 49 0 -33 13 63 0 34 47 44 0 -60 67 50 0 33 -50 58 0 -37 6 70 0 48 9 -10 0 52 -25 1 0 -62 -14 -12 0 -6 55 -12 0 -48 34 -7 0 -15 68 -12 0 -16 68 13 0 -4 60 -9 0 -4 -72 -26 0 1 -48 -4 0 -38 30 44 0 29 -2 -56 0 -37 65 17 0 16 32 18 0 -32 1 -49 0 -23 75 66 0 -16 -4 56 0 -60 -33 -48 0 34 59 64 0 -12 -28 45 0 12 43 -63 0 -52 26 -20 0 14 -38 -62 0 -10 -67 -47 0 51 -49 -75 0 37 68 -70 0 24 -74 59 0 -15 28 40 0 -45 -60 66 0 -27 -42 -54 0 -11 -18 57 0 -72 -10 -6 0 -64 59 -9 0 18 10 64 0 -20 -47 -71 0 40 22 64 0 74 9 16 0 -44 -72 -13 0 -33 -56 -54 0 36 2 -48 0 66 33 -53 0 38 -43 -29 0 35 71 -36 0 33 -47 -13 0 -65 32 -56 0 -65 -32 50 0 59 -9 -62 0 59 4 -10 0 -24 14 -42 0 -27 26 63 0 7 -32 -36 0 -30 39 25 0 56 39 -31 0 15 56 50 0 22 34 56 0 16 38 -8 0 -9 38 67 0 60 -59 -23 0 -38 -37 -39 0 32 -15 -13 0 -16 50 -1 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/30.cnf0000644000175000017500000000300612253362407021707 0ustar georgeskgeorgeskc c SAT instance in DIMACS CNF input format. c p cnf 30 127 7 25 24 0 -10 -18 26 0 28 -22 4 0 20 22 4 0 20 -8 -2 0 5 12 -25 0 -5 28 2 0 -13 -5 -30 0 21 -8 30 0 -24 -12 26 0 22 1 -3 0 -10 27 -28 0 1 28 -29 0 -30 25 6 0 1 -22 -14 0 -15 24 23 0 -3 18 -14 0 29 12 -7 0 4 -22 14 0 5 11 28 0 17 7 -6 0 -15 3 -5 0 -5 -14 29 0 8 11 -4 0 -17 27 -11 0 -1 -8 -17 0 -2 24 -1 0 5 -18 14 0 -10 -20 21 0 -12 13 29 0 -26 -7 3 0 -21 -4 -26 0 -7 27 4 0 15 -18 11 0 -24 -18 -7 0 28 5 -20 0 -28 14 -22 0 -26 19 9 0 -7 2 30 0 -10 -24 -25 0 -18 -1 25 0 -13 -12 4 0 15 -16 19 0 1 -16 3 0 -14 9 -2 0 8 1 -20 0 13 8 -15 0 -28 -11 -22 0 -9 21 -8 0 21 -20 -22 0 -15 -7 4 0 8 -23 6 0 19 15 23 0 19 -4 -11 0 19 -5 11 0 12 8 -27 0 -28 7 26 0 17 -1 -9 0 -20 -11 -27 0 -19 -8 -26 0 -14 -23 27 0 -6 -12 -18 0 3 -9 18 0 -25 -11 -2 0 15 14 4 0 1 10 12 0 23 27 -9 0 3 23 -8 0 -10 -25 22 0 20 28 -3 0 -25 22 -26 0 -11 -30 -16 0 -3 -18 27 0 15 -22 27 0 3 -8 12 0 8 10 19 0 24 11 15 0 30 8 -6 0 11 -28 20 0 -18 30 -10 0 -5 -25 -27 0 -5 30 3 0 -22 12 -2 0 22 21 -1 0 29 -17 14 0 17 -11 -13 0 -20 -6 -1 0 -28 27 25 0 -3 28 17 0 23 -21 -19 0 -22 -7 -25 0 -15 -4 -9 0 -24 -17 26 0 23 22 -18 0 -11 -13 -30 0 30 25 14 0 -7 13 10 0 11 -6 -19 0 14 4 17 0 29 8 -15 0 -19 -24 -13 0 17 -16 20 0 -12 20 -6 0 24 -14 -9 0 6 7 12 0 -19 -3 -8 0 -29 -18 13 0 -12 -7 -1 0 -29 5 -1 0 9 -18 5 0 2 22 -3 0 2 13 -28 0 -26 -29 18 0 -19 -26 -30 0 -27 2 -26 0 -23 22 12 0 -6 16 -7 0 18 17 26 0 -16 -22 -20 0 30 -5 -16 0 -12 -22 24 0 -21 -27 -29 0 -15 9 8 0 -18 -11 -3 0 -30 23 27 0 -9 7 -17 0 -18 29 -17 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/110.cnf0000644000175000017500000001332712253362407021775 0ustar georgeskgeorgeskp cnf 110 467 27 84 19 0 -76 -59 41 0 97 -31 -100 0 12 -2 71 0 59 -44 103 0 -25 94 46 0 -53 75 58 0 26 95 -32 0 -6 -32 94 0 78 14 107 0 -85 8 44 0 -58 34 -18 0 -85 63 91 0 27 -90 -85 0 -30 48 18 0 99 96 -77 0 84 47 -80 0 76 -104 -5 0 77 -47 40 0 -34 -26 -53 0 93 -2 -52 0 33 -78 26 0 26 -37 19 0 24 54 82 0 -7 64 -41 0 80 82 -43 0 -77 -72 27 0 -17 -7 83 0 -78 102 30 0 28 -57 4 0 -89 61 32 0 -13 -101 18 0 5 40 7 0 -101 -97 18 0 50 -34 -24 0 -37 36 35 0 -34 -33 -2 0 24 -80 43 0 -92 -35 -93 0 26 -18 -31 0 -72 67 48 0 55 92 87 0 82 96 -46 0 -19 77 30 0 40 -105 83 0 11 65 -62 0 31 -63 -24 0 14 -15 26 0 -66 37 93 0 -101 -88 21 0 99 -11 -97 0 99 -83 -106 0 75 -41 12 0 3 8 20 0 -44 87 106 0 109 -27 19 0 109 -100 10 0 43 -98 -110 0 -6 65 88 0 -29 -46 -78 0 85 -33 91 0 58 -87 10 0 95 -98 -108 0 104 66 -80 0 64 -88 -35 0 -87 104 -53 0 29 105 2 0 -40 73 105 0 -38 -96 -62 0 22 70 39 0 95 -24 7 0 27 107 -41 0 -35 -64 60 0 -69 -44 63 0 -65 -103 67 0 -83 14 61 0 -7 -14 32 0 -102 -39 -15 0 53 61 -110 0 24 14 79 0 -78 65 90 0 -62 -53 -49 0 -90 -93 -44 0 -101 -91 17 0 -17 -108 51 0 -41 -48 43 0 -30 -85 42 0 -9 -63 76 0 -63 -42 65 0 25 -54 109 0 100 90 12 0 -104 -88 -23 0 -103 -25 -37 0 -49 97 -82 0 49 -102 66 0 -46 -110 -108 0 70 -30 -93 0 47 -23 -34 0 96 60 86 0 -109 -86 82 0 -43 85 89 0 -78 38 -92 0 -106 -6 71 0 26 -18 67 0 11 89 82 0 -58 -94 -88 0 -74 43 88 0 8 75 -63 0 7 -72 -95 0 -49 77 -47 0 -67 14 56 0 -94 78 100 0 -28 -10 66 0 25 -22 -34 0 -40 -36 -5 0 -86 -72 104 0 20 -73 -23 0 26 -91 101 0 58 -62 -17 0 57 29 -16 0 64 28 19 0 105 -54 102 0 85 -57 33 0 81 44 -99 0 -99 -27 51 0 -25 10 -8 0 27 -66 -68 0 62 -55 -43 0 91 51 11 0 49 75 101 0 -58 -28 53 0 -108 -6 -83 0 69 64 -110 0 88 -15 71 0 -7 -2 69 0 -16 -2 50 0 57 -52 -25 0 82 50 25 0 103 -99 -106 0 -22 -92 27 0 109 -80 39 0 -68 88 87 0 42 -87 35 0 -17 100 -46 0 -54 13 44 0 57 24 74 0 -7 81 -3 0 48 -90 65 0 -53 -49 71 0 59 35 106 0 -32 105 16 0 -16 43 -93 0 44 -50 104 0 -19 -2 108 0 -4 87 -43 0 -63 17 59 0 94 -77 -68 0 87 64 29 0 58 -47 17 0 10 -16 55 0 18 -65 51 0 -9 -47 -14 0 97 26 -90 0 -74 31 110 0 94 -36 57 0 -12 16 -15 0 -32 43 108 0 -32 -44 110 0 -39 -17 -110 0 -104 -12 -6 0 46 -16 -87 0 105 40 88 0 101 -71 44 0 104 -73 19 0 -22 -79 45 0 -74 -85 58 0 -11 -94 109 0 72 -60 -5 0 6 42 36 0 -48 -17 18 0 -88 -89 97 0 -87 -49 105 0 84 -106 9 0 -47 25 -109 0 -39 81 69 0 12 -105 -48 0 68 -26 -46 0 84 77 -11 0 -69 64 -63 0 86 23 46 0 -7 -103 69 0 -94 -84 4 0 -46 -31 -52 0 103 -43 -73 0 58 2 -86 0 -76 77 33 0 83 -71 -3 0 -18 7 -4 0 5 30 -51 0 -69 2 58 0 54 49 100 0 -55 -71 -93 0 -76 99 88 0 -32 95 23 0 57 40 -66 0 11 -98 -32 0 -80 83 -87 0 -3 87 92 0 47 -68 26 0 64 5 30 0 32 -41 92 0 71 -37 24 0 40 55 43 0 -13 -12 -92 0 81 -10 -23 0 100 -85 73 0 -105 36 24 0 86 -101 24 0 91 -25 -73 0 -73 80 -110 0 -48 -100 34 0 -9 -87 -57 0 -16 59 -65 0 -8 -96 9 0 -80 -62 -44 0 63 91 83 0 75 78 -44 0 67 -104 21 0 -86 -106 53 0 -13 -65 5 0 -80 77 -24 0 7 72 -74 0 87 2 -48 0 -44 66 -32 0 65 -51 -34 0 56 87 38 0 -29 -65 -48 0 105 78 -103 0 -95 75 32 0 -80 -2 -93 0 -41 45 85 0 94 2 -86 0 6 72 -87 0 30 103 14 0 -54 7 11 0 -74 41 -48 0 60 -102 89 0 15 -91 77 0 56 -108 -35 0 9 72 -82 0 -13 67 -87 0 -69 70 93 0 73 39 -108 0 19 107 96 0 69 -36 -35 0 43 69 39 0 47 97 12 0 49 109 -85 0 -101 14 99 0 38 -78 -66 0 95 23 7 0 5 -7 -67 0 -79 -5 90 0 33 98 19 0 60 14 -75 0 36 3 -100 0 87 53 62 0 97 -37 -69 0 30 -62 -79 0 -69 21 20 0 -5 -96 67 0 -15 -9 -101 0 4 39 62 0 63 -53 109 0 26 108 90 0 58 110 44 0 -51 -60 -71 0 39 -60 -97 0 -94 -32 -1 0 13 -44 89 0 -109 96 66 0 -90 -59 61 0 13 16 -71 0 110 17 75 0 41 34 -20 0 -87 94 108 0 19 -41 -90 0 -4 -91 55 0 -9 57 31 0 25 55 -101 0 36 -100 42 0 82 -75 88 0 -59 -43 72 0 52 56 -21 0 46 -15 16 0 109 40 65 0 58 -94 -72 0 31 98 -73 0 25 41 33 0 -17 -80 54 0 -25 -98 -41 0 18 82 36 0 51 -9 28 0 -87 -80 79 0 45 33 -91 0 19 47 27 0 106 5 -60 0 -62 -48 45 0 -31 -15 -86 0 -39 -78 -21 0 2 70 82 0 108 25 -85 0 -105 7 63 0 -87 17 -100 0 -94 34 -106 0 -46 52 -15 0 -43 -75 -19 0 43 -54 -84 0 94 -88 -83 0 93 -81 99 0 87 -73 -56 0 81 -9 41 0 93 -64 -65 0 -21 56 -8 0 -73 -88 -89 0 -59 33 78 0 -38 -101 -56 0 -102 -42 71 0 -52 85 86 0 -74 6 26 0 3 -76 -43 0 11 38 42 0 -99 -54 -93 0 -3 104 97 0 19 34 -82 0 -27 -70 -102 0 59 -29 57 0 91 53 -55 0 -51 -72 -61 0 80 9 48 0 36 -97 -8 0 20 -83 53 0 89 -72 -77 0 27 -93 58 0 64 -18 54 0 78 6 109 0 -51 20 -79 0 23 -92 -103 0 -64 7 -8 0 54 50 18 0 -49 106 80 0 -105 10 -11 0 -4 24 91 0 48 -46 -9 0 -53 2 18 0 98 -78 -18 0 -20 -37 109 0 -38 22 -68 0 -82 42 -59 0 -12 -15 -78 0 -76 -54 13 0 27 -109 97 0 -62 109 -88 0 -66 -56 43 0 -26 -18 81 0 -106 77 56 0 100 -54 -12 0 -81 -82 90 0 -91 29 -72 0 13 32 -10 0 15 -10 96 0 -60 75 16 0 -1 61 -72 0 3 -46 -7 0 31 15 -65 0 -28 91 109 0 35 48 98 0 -26 102 -3 0 -54 56 15 0 5 -25 49 0 58 -37 -56 0 -86 -94 22 0 2 -6 -94 0 -94 40 45 0 77 12 102 0 19 -88 63 0 91 -56 -87 0 -60 -17 -91 0 -78 77 20 0 -13 56 28 0 -57 -102 -64 0 -56 106 -96 0 -17 100 -1 0 -68 -27 59 0 -75 1 -66 0 -57 98 -105 0 86 -59 44 0 66 15 44 0 -80 -67 -68 0 -104 89 27 0 24 63 -97 0 1 9 21 0 56 78 82 0 -12 25 -33 0 -101 74 87 0 65 52 78 0 98 -1 -95 0 -1 -28 39 0 -12 -56 -22 0 17 16 -33 0 -52 -84 -93 0 -2 88 -82 0 -33 -85 -84 0 78 46 89 0 46 -79 -67 0 38 -98 21 0 39 12 55 0 -13 79 -50 0 -91 -97 -17 0 -109 -3 95 0 -79 49 32 0 -5 -41 -92 0 -17 81 97 0 48 -101 55 0 -79 -84 47 0 82 -12 -70 0 29 105 9 0 -50 67 -15 0 72 86 76 0 -33 8 -86 0 -67 -105 -39 0 10 18 -59 0 15 39 19 0 27 97 52 0 102 93 -50 0 69 -38 -72 0 -57 22 15 0 -80 11 53 0 78 65 -80 0 6 102 71 0 33 -15 -35 0 67 -33 100 0 -1 -96 92 0 50 98 109 0 -45 -63 -55 0 56 -51 73 0 -54 6 -23 0 -107 -33 -36 0 85 66 -75 0 -85 6 97 0 17 41 23 0 -47 48 -33 0 81 -65 91 0 -42 96 -69 0 63 -8 -95 0 -92 28 6 0 -106 -12 -13 0 63 10 71 0 90 -94 101 0 -60 15 -20 0 -2 -106 3 0 8 -42 28 0 -60 -14 -77 0 54 87 1 0 44 70 40 0 -32 61 -98 0 23 -40 95 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/130.cnf0000644000175000017500000001605512253362407022000 0ustar georgeskgeorgeskp cnf 130 552 83 -14 128 0 -69 -14 86 0 -4 90 -56 0 -69 -56 94 0 65 -5 -26 0 -29 4 -126 0 -5 -10 43 0 -50 109 -98 0 115 -91 41 0 112 -69 51 0 63 19 -31 0 -74 -67 -66 0 -24 40 65 0 -8 19 39 0 60 21 101 0 26 94 -83 0 19 10 64 0 -101 -120 64 0 -128 -32 -6 0 -35 -106 26 0 50 -123 -11 0 -125 -73 61 0 -76 -53 17 0 61 12 43 0 105 -80 -106 0 52 -68 92 0 51 -112 52 0 -49 94 129 0 47 -124 54 0 20 -6 -111 0 88 9 44 0 -17 92 -40 0 -106 -126 -114 0 29 75 -79 0 -24 -79 -30 0 43 26 -9 0 -103 51 127 0 128 11 -25 0 75 -47 96 0 107 -116 -26 0 47 97 -105 0 -109 82 48 0 38 -25 -89 0 -85 6 76 0 -41 86 -29 0 -39 -60 -67 0 -65 -116 40 0 -66 6 17 0 -80 -15 64 0 120 23 122 0 37 115 65 0 34 60 -17 0 -128 46 118 0 -105 66 -70 0 114 62 -129 0 -50 -37 96 0 -54 88 52 0 -98 -95 125 0 99 78 -19 0 -36 -4 34 0 -127 120 44 0 56 -3 99 0 -85 -18 24 0 -48 -49 -114 0 126 111 -58 0 124 -69 89 0 81 106 -17 0 48 -55 -105 0 -39 94 -41 0 -110 66 -111 0 50 -47 40 0 95 -51 65 0 112 -65 -4 0 18 81 102 0 -55 -36 21 0 -80 68 -119 0 -64 14 100 0 85 -22 -16 0 118 -122 -116 0 80 -116 30 0 -115 71 15 0 -103 112 97 0 -25 -15 -39 0 52 -85 -20 0 44 96 57 0 -115 100 21 0 94 130 -96 0 60 85 78 0 71 121 13 0 64 8 -44 0 41 127 11 0 125 37 -13 0 -26 -22 6 0 118 -45 107 0 129 122 -16 0 3 121 27 0 10 -95 71 0 64 -23 113 0 21 -35 -20 0 31 -28 -46 0 56 100 -84 0 52 -101 -32 0 -97 128 -38 0 -68 53 50 0 87 -54 41 0 -41 76 -56 0 -118 106 115 0 43 123 -72 0 22 7 -102 0 4 72 -96 0 120 -19 54 0 50 -108 -36 0 -67 80 -19 0 -94 12 -3 0 54 -16 12 0 -8 -48 70 0 69 -2 78 0 8 32 65 0 -53 -108 34 0 -124 -127 -7 0 -90 17 85 0 108 51 -123 0 13 89 23 0 72 100 43 0 -122 -2 -40 0 -39 9 50 0 -70 -90 78 0 -38 84 -62 0 46 -79 100 0 78 -72 -126 0 -26 86 120 0 -17 -6 43 0 41 -11 5 0 -36 -26 46 0 11 -78 -39 0 -42 110 116 0 -107 -63 -38 0 -55 100 -129 0 112 -16 93 0 -7 -104 101 0 69 -43 -8 0 -35 15 29 0 -71 -105 8 0 54 84 -6 0 -112 15 4 0 127 -1 94 0 20 103 -96 0 -46 55 -66 0 130 -74 50 0 -51 66 -14 0 12 -126 -111 0 27 -81 -105 0 29 -130 103 0 78 39 -103 0 -98 92 -54 0 102 -89 -10 0 -112 82 38 0 1 -23 -21 0 17 -89 -111 0 -9 -127 -104 0 -128 31 -111 0 -47 79 -13 0 -16 -7 115 0 -44 -66 82 0 -4 -36 -64 0 107 47 -72 0 -73 113 -55 0 72 -104 82 0 -18 -73 104 0 47 -45 -59 0 79 -7 54 0 115 6 -5 0 10 79 113 0 51 49 101 0 123 -13 -52 0 82 124 21 0 -57 89 -100 0 -18 -19 84 0 87 -95 -101 0 -94 19 -21 0 112 -37 -14 0 116 -85 -89 0 -33 91 108 0 92 -67 129 0 -78 -110 -69 0 17 -59 -79 0 -9 125 31 0 -43 -108 36 0 19 -20 78 0 50 -47 -119 0 -4 31 98 0 -74 128 29 0 -36 -87 124 0 -74 114 40 0 -128 1 -84 0 -58 64 3 0 -102 -124 -59 0 -120 -39 72 0 123 84 110 0 -110 59 124 0 -50 96 78 0 71 -61 47 0 -67 76 65 0 -99 -62 74 0 29 -61 40 0 -107 -22 20 0 -33 121 -30 0 -65 42 -82 0 -78 -67 109 0 -112 -42 -5 0 -96 105 -86 0 -48 6 99 0 -73 -58 -108 0 109 -89 80 0 43 -83 99 0 -84 -118 -68 0 4 90 -35 0 -40 122 29 0 -48 40 -49 0 61 56 -23 0 128 -26 5 0 -22 78 62 0 127 108 -39 0 -56 -92 109 0 101 -73 -48 0 -41 111 124 0 36 98 119 0 -11 109 114 0 -112 -6 -111 0 123 77 -92 0 62 -63 123 0 -74 40 108 0 -14 -87 129 0 42 13 -88 0 2 -16 -122 0 -110 -16 18 0 -97 -99 27 0 92 59 110 0 -51 23 52 0 99 85 54 0 -113 58 -63 0 57 -101 55 0 121 -104 -33 0 -15 77 127 0 26 -28 -127 0 43 123 -1 0 7 -46 -126 0 130 86 91 0 127 -58 -65 0 62 -99 127 0 32 61 -87 0 -4 -76 127 0 20 -23 79 0 11 58 43 0 129 -43 -106 0 -29 33 -67 0 -107 69 29 0 -40 130 -106 0 -93 21 120 0 102 -97 20 0 -6 117 -67 0 117 72 -42 0 123 90 129 0 87 83 128 0 19 119 74 0 52 -53 50 0 -60 -37 -31 0 127 22 124 0 113 69 -72 0 24 -6 -111 0 20 67 111 0 -59 124 18 0 -72 -34 -94 0 15 115 53 0 -64 -63 -38 0 -117 -60 22 0 91 41 61 0 45 -1 -16 0 -80 113 -8 0 6 -50 27 0 51 124 -83 0 -22 -124 -99 0 88 39 -31 0 105 -20 86 0 20 116 130 0 -54 -104 -30 0 117 -21 113 0 -11 -67 -70 0 98 -77 43 0 -113 85 77 0 73 -89 -119 0 45 -100 3 0 -67 112 124 0 -3 -91 -106 0 7 70 87 0 -20 55 32 0 -23 118 101 0 -56 -46 129 0 68 75 -24 0 -70 -7 -40 0 -66 -42 47 0 106 -28 115 0 -130 87 -114 0 87 118 -9 0 -81 -20 35 0 1 -11 93 0 -22 -97 43 0 -66 43 -71 0 -93 -95 -124 0 -52 99 -77 0 -58 73 48 0 27 83 130 0 125 -24 -60 0 -127 -11 33 0 54 27 -122 0 91 -101 -76 0 -69 -80 -92 0 -82 38 -21 0 -103 58 -96 0 -59 130 -22 0 115 125 -41 0 1 81 118 0 13 -87 -58 0 -79 67 40 0 9 -84 -130 0 -20 -100 39 0 -65 -19 -125 0 -32 90 79 0 -101 -90 -29 0 94 38 113 0 46 -130 126 0 115 -48 -51 0 -94 115 24 0 -105 -11 -82 0 25 70 100 0 -39 22 30 0 -20 -112 128 0 -128 -77 118 0 -85 -14 114 0 -64 128 32 0 38 -8 -91 0 -95 -3 -111 0 -129 21 115 0 -73 -45 -20 0 39 -99 13 0 74 -27 -67 0 -36 124 -47 0 -48 11 -85 0 -70 -20 -57 0 -48 -104 -110 0 -110 123 -97 0 68 -98 89 0 60 -55 -123 0 -1 42 83 0 38 -28 -48 0 21 -10 -26 0 69 -93 75 0 28 119 91 0 -70 94 25 0 28 127 81 0 -116 -82 -67 0 129 4 -88 0 -22 66 71 0 -11 68 -13 0 -48 45 -77 0 56 60 73 0 31 110 -40 0 -127 43 -130 0 46 -30 93 0 79 -122 65 0 -25 -128 -87 0 -104 46 -19 0 -81 73 123 0 -16 101 -34 0 -89 -72 -54 0 -33 -57 5 0 -117 39 54 0 -95 79 -68 0 112 109 108 0 -18 67 129 0 -122 -40 -82 0 -94 9 -39 0 -107 -34 -74 0 108 130 -74 0 -48 -85 65 0 -92 -4 86 0 -64 112 -120 0 13 -25 -74 0 113 48 32 0 -92 38 -124 0 -38 -89 73 0 120 109 -88 0 -20 112 84 0 -94 -77 78 0 39 -93 9 0 -128 116 106 0 94 -49 -101 0 100 -106 -124 0 -89 69 -41 0 6 99 129 0 -128 -59 98 0 -10 99 -74 0 25 58 -85 0 106 23 -82 0 81 -82 47 0 101 118 -56 0 -105 -1 43 0 7 89 -60 0 -29 -104 -124 0 59 -6 -96 0 -2 25 5 0 -86 61 -38 0 94 -7 -105 0 14 118 -114 0 24 -101 79 0 94 -37 48 0 54 130 8 0 92 -107 -94 0 20 -120 -74 0 -89 74 7 0 57 -41 20 0 -62 -89 69 0 49 83 92 0 115 19 -91 0 -46 113 112 0 -90 71 -28 0 118 -47 35 0 88 33 119 0 27 -40 -100 0 -119 39 125 0 -5 125 -55 0 -128 112 83 0 128 49 -83 0 -29 48 67 0 83 -34 -52 0 -30 -31 -64 0 -104 82 124 0 -36 60 129 0 -24 127 -31 0 23 -72 -123 0 -104 -106 95 0 10 130 126 0 85 34 -44 0 -82 111 -35 0 98 -73 -4 0 -18 -81 13 0 34 -13 -47 0 61 107 54 0 122 -70 -24 0 -113 16 7 0 -79 -108 51 0 117 116 111 0 -81 82 -59 0 -30 87 41 0 -124 3 -130 0 96 45 99 0 110 -120 67 0 32 66 3 0 90 118 66 0 -80 -79 70 0 -78 -48 85 0 121 -94 -15 0 41 -47 27 0 -42 7 -88 0 -52 117 -69 0 19 -36 -91 0 62 -125 45 0 -117 -98 42 0 23 -79 116 0 -64 -101 26 0 -69 90 97 0 -39 115 88 0 21 119 -26 0 87 15 122 0 -96 -112 41 0 5 11 -110 0 -75 19 64 0 -106 127 103 0 1 -124 -80 0 8 -21 45 0 91 -24 82 0 -66 127 124 0 -94 -12 -126 0 -95 46 -5 0 113 115 -64 0 -47 79 -77 0 -110 -79 100 0 15 123 -65 0 27 -100 38 0 17 -81 -121 0 -48 27 29 0 14 -36 58 0 115 127 -122 0 -80 -85 6 0 -92 37 73 0 -67 -94 116 0 107 58 -46 0 -23 127 -64 0 61 -19 -108 0 3 -128 -72 0 -107 105 -128 0 -95 -3 -46 0 -68 -1 40 0 -11 -35 59 0 119 90 123 0 97 -38 25 0 -6 -37 -52 0 -116 -62 18 0 -130 -127 34 0 -40 75 94 0 -83 -97 20 0 -35 43 99 0 54 103 -17 0 -77 -10 4 0 40 11 -86 0 89 93 110 0 -46 -13 94 0 114 -16 -22 0 59 75 -68 0 -130 115 71 0 68 33 -94 0 8 -40 -11 0 94 -109 -127 0 -4 -43 -46 0 90 -128 -7 0 97 -125 103 0 -89 110 -94 0 10 -14 -44 0 27 -16 47 0 -40 -60 -84 0 17 -122 -32 0 64 26 -10 0 87 -88 -38 0 10 -90 -66 0 102 -125 -62 0 -87 -47 -71 0 -48 -96 52 0 14 -47 77 0 -116 52 -78 0 -50 27 -8 0 18 37 -29 0 -69 -39 63 0 -25 102 -63 0 13 98 81 0 -87 15 -72 0 -37 111 -106 0 58 118 80 0 -104 5 80 0 -31 -87 102 0 90 107 -110 0 52 97 38 0 -9 10 20 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/25.cnf0000644000175000017500000000235512253362407021721 0ustar georgeskgeorgeskc c SAT instance in DIMACS CNF input format. c p cnf 25 106 -6 -17 -5 0 -16 6 -1 0 -1 -9 3 0 -7 2 1 0 -11 20 -19 0 6 -9 -22 0 17 -13 8 0 -16 21 9 0 21 4 12 0 2 -12 13 0 18 17 -19 0 -20 -15 -6 0 11 18 -13 0 15 -24 -22 0 19 13 14 0 3 -22 -6 0 -21 1 15 0 19 -4 11 0 -19 -5 10 0 -2 5 -15 0 -25 20 -5 0 9 -18 -1 0 -17 18 -20 0 9 -13 5 0 5 23 7 0 -8 10 -3 0 -12 2 -24 0 11 12 9 0 -4 -15 -20 0 -5 14 4 0 -19 20 -11 0 -1 9 -23 0 1 -4 12 0 17 6 25 0 -9 -1 12 0 -2 19 -13 0 -17 21 8 0 -2 -16 -18 0 -22 9 21 0 -24 3 20 0 -20 9 -16 0 -19 -2 -22 0 9 20 -19 0 17 21 3 0 7 -1 -2 0 -3 -7 -1 0 2 9 10 0 10 3 -9 0 21 23 -19 0 -6 -15 -9 0 11 -8 9 0 13 3 22 0 -24 8 -15 0 20 23 -13 0 -4 -2 23 0 -1 15 2 0 16 -25 -20 0 23 -2 5 0 18 6 12 0 -3 22 -12 0 -25 15 10 0 -10 5 14 0 -10 -13 4 0 -21 1 -12 0 -14 9 -18 0 16 8 -22 0 17 -23 1 0 15 -2 -5 0 -11 -17 -20 0 -18 10 15 0 19 -1 -15 0 -22 13 -5 0 1 25 -9 0 -2 -17 1 0 -5 -18 -22 0 8 23 5 0 -17 20 19 0 -19 -6 -5 0 -16 -6 17 0 24 3 -25 0 -1 4 7 0 -4 19 -10 0 23 5 22 0 1 -11 -13 0 -18 -17 -14 0 7 -25 18 0 21 11 20 0 25 -8 10 0 -10 12 -5 0 20 -7 -9 0 9 10 -23 0 -16 13 -20 0 5 16 14 0 -13 -7 -22 0 -3 21 25 0 22 -2 -14 0 8 -2 -1 0 -6 -22 -1 0 19 -1 -22 0 -4 -5 -18 0 -1 9 20 0 16 14 -22 0 -6 -17 -13 0 -2 4 -13 0 -13 9 -22 0 14 -2 -11 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/135.cnf0000644000175000017500000001662512253362407022010 0ustar georgeskgeorgeskp cnf 135 573 -135 -127 42 0 -7 49 -124 0 76 122 3 0 88 31 -41 0 97 -91 42 0 95 -80 -38 0 -83 50 -66 0 -56 18 -65 0 72 -38 -126 0 -107 78 43 0 -15 -120 59 0 -77 -71 5 0 -51 -118 36 0 -34 -1 42 0 -28 -133 54 0 -47 88 121 0 58 -7 115 0 38 108 -88 0 -53 -61 -113 0 -65 82 -104 0 115 -5 103 0 -65 32 -3 0 58 -68 -132 0 67 -54 -23 0 -96 42 29 0 119 -48 63 0 -131 -44 27 0 -33 94 67 0 125 -38 117 0 -111 -131 14 0 -24 86 -101 0 -94 102 93 0 -65 -22 114 0 -30 -100 -96 0 -75 48 -6 0 102 57 124 0 -55 -76 -104 0 -81 -131 -13 0 -88 -63 71 0 -105 61 -29 0 -38 -92 116 0 -118 124 -17 0 94 15 -81 0 -85 77 -45 0 104 53 69 0 -63 97 -29 0 99 -59 -70 0 21 79 -45 0 19 -21 -1 0 -76 -14 35 0 -68 -132 -113 0 128 135 21 0 110 106 -135 0 -102 55 10 0 -38 74 68 0 43 -125 132 0 -128 80 -93 0 -106 114 29 0 -102 37 -5 0 24 32 71 0 92 3 96 0 -13 61 -84 0 92 93 -4 0 103 -64 -41 0 -112 19 -107 0 87 -90 83 0 48 -125 30 0 -82 -58 101 0 -8 -88 -30 0 14 -39 113 0 -119 121 -26 0 56 2 -37 0 -18 1 -51 0 -54 -26 36 0 -9 -79 -105 0 -13 77 -104 0 -67 -30 37 0 -22 38 10 0 6 -85 124 0 -128 108 -100 0 59 68 44 0 41 -102 -101 0 29 71 100 0 -13 -17 -110 0 21 -88 -23 0 81 29 31 0 84 -83 3 0 66 -68 120 0 -83 31 -67 0 -96 1 -124 0 111 -97 82 0 8 -72 -122 0 -55 32 78 0 -96 -82 133 0 -45 -97 -29 0 92 22 32 0 129 -70 2 0 -73 -134 -19 0 -29 3 -81 0 117 32 132 0 -16 -93 -14 0 -72 101 121 0 -64 -111 129 0 12 106 85 0 -108 89 -21 0 81 86 109 0 -41 97 -50 0 16 -91 104 0 96 35 52 0 -50 -9 22 0 -114 119 78 0 -88 -124 63 0 -75 -50 -74 0 -63 42 -99 0 -125 -16 24 0 76 -19 -31 0 129 74 -113 0 23 -86 -1 0 -36 2 114 0 -98 35 -25 0 108 -76 43 0 106 127 -65 0 -131 -74 104 0 71 51 -3 0 -68 -125 -120 0 95 45 -80 0 110 31 -90 0 -83 -27 24 0 43 -101 -82 0 65 -76 -49 0 -12 -122 -62 0 44 -121 28 0 11 106 -13 0 49 -115 61 0 20 61 -135 0 -112 133 125 0 75 69 -92 0 67 -34 -51 0 23 -84 -34 0 -119 -29 -13 0 35 -12 32 0 -111 -40 -26 0 -99 -68 -56 0 -109 -9 -112 0 52 66 -75 0 44 -99 19 0 -39 79 127 0 135 106 63 0 68 -66 64 0 -71 -124 135 0 11 15 74 0 -119 99 -74 0 4 -35 -42 0 92 -79 46 0 -92 107 -79 0 68 -2 107 0 -18 -130 -19 0 -97 89 -105 0 97 -17 -109 0 -74 -77 43 0 -37 -6 40 0 -105 28 -3 0 -76 57 -32 0 -67 -78 83 0 -25 -70 -88 0 33 -24 99 0 29 9 -5 0 117 -7 -20 0 -44 53 -134 0 1 -92 -134 0 -20 64 55 0 30 -77 86 0 -15 27 -1 0 25 57 -40 0 -65 -63 129 0 -42 -82 -60 0 -49 -134 -67 0 -4 -95 90 0 -66 -133 -41 0 -129 -77 70 0 95 123 -13 0 108 3 -42 0 109 -50 -81 0 81 2 108 0 -79 17 39 0 10 80 -40 0 -70 -135 50 0 106 -107 123 0 -135 -129 -87 0 73 -82 129 0 36 43 -65 0 104 -116 -18 0 -82 85 -32 0 79 -1 -23 0 71 -46 -23 0 42 69 119 0 -79 -73 61 0 -108 85 88 0 26 122 126 0 -124 -69 107 0 -134 -90 -42 0 -43 28 -83 0 -16 -41 -83 0 24 21 88 0 -104 48 -114 0 -33 -102 62 0 -88 -41 46 0 -54 -129 -50 0 44 -82 -51 0 -58 -111 -126 0 -56 -128 -11 0 80 26 -55 0 -124 -84 -35 0 -132 -87 -1 0 56 -38 41 0 -46 -30 15 0 95 8 75 0 -90 78 47 0 64 -19 -80 0 92 39 73 0 -50 103 85 0 121 50 -71 0 -42 119 132 0 -28 -5 61 0 -71 -74 87 0 -123 23 42 0 -118 -70 -33 0 2 -130 -56 0 -107 -101 -93 0 112 -96 -24 0 45 102 91 0 97 124 -4 0 -43 116 -55 0 91 -27 30 0 -129 -100 -16 0 -61 -55 -115 0 -60 43 -99 0 -113 -77 66 0 -131 49 83 0 39 103 -15 0 85 -124 -77 0 -129 -63 -91 0 -42 -77 54 0 -118 -119 -126 0 34 -120 -24 0 -90 -7 -100 0 -39 112 -35 0 70 10 -134 0 126 -55 -88 0 -34 94 3 0 -118 -128 116 0 33 17 -129 0 72 -11 -50 0 -33 -45 -130 0 -87 -41 -23 0 47 -34 109 0 -31 -52 109 0 75 -102 -118 0 -13 50 -83 0 124 -44 70 0 127 132 126 0 -35 42 -6 0 120 -74 105 0 92 26 77 0 80 128 10 0 -46 70 94 0 -1 -76 69 0 106 -86 -58 0 -114 -19 -42 0 -131 -52 -79 0 -48 47 62 0 -18 -92 -17 0 -45 81 -110 0 117 -35 55 0 -70 -123 62 0 -89 -110 23 0 112 64 91 0 -27 -12 65 0 35 -116 -43 0 -55 109 -126 0 32 -4 -74 0 -119 10 -132 0 -118 -32 -63 0 -8 -60 -19 0 -132 -60 -43 0 -28 90 -41 0 -44 -2 100 0 83 -82 -13 0 126 53 -77 0 62 116 33 0 4 -106 -51 0 -38 113 -132 0 7 -38 -13 0 -132 -91 -3 0 115 -86 -75 0 -72 56 113 0 -61 60 -125 0 103 89 -85 0 -56 -78 52 0 36 85 69 0 116 19 -92 0 -123 13 79 0 -56 116 41 0 69 115 -63 0 -68 -1 95 0 100 -68 -55 0 45 57 -122 0 -128 73 43 0 42 53 -93 0 -21 103 59 0 -131 -125 15 0 88 -32 -71 0 8 91 -103 0 25 -16 -93 0 120 45 51 0 111 -121 125 0 134 -58 112 0 -134 -127 3 0 15 -83 -131 0 -47 -132 80 0 -25 97 -28 0 -85 132 -41 0 50 -10 -13 0 131 120 130 0 43 107 91 0 -133 -51 97 0 80 105 79 0 60 -31 -135 0 -37 -75 -55 0 -130 92 -35 0 -41 -14 18 0 132 -88 -133 0 67 51 -108 0 112 -34 -126 0 72 -94 -1 0 61 -100 59 0 -22 -48 -34 0 115 -89 53 0 90 19 112 0 -31 85 -100 0 -117 11 -37 0 -18 -94 -24 0 119 46 99 0 -98 -116 -20 0 -93 -68 128 0 -28 73 58 0 -5 -17 -91 0 -80 108 -132 0 -68 26 -111 0 -71 -126 50 0 125 92 131 0 60 -3 -31 0 99 -17 -86 0 -53 109 90 0 74 -40 -41 0 78 -99 30 0 -123 42 -8 0 120 24 101 0 -9 80 -42 0 -103 -21 37 0 107 -113 68 0 -121 -80 -100 0 -8 82 -9 0 -53 85 10 0 50 -78 96 0 68 110 -9 0 -19 -22 -126 0 91 -119 -32 0 78 47 -40 0 81 90 -65 0 104 115 -50 0 -63 -89 118 0 -31 -84 94 0 -64 -71 132 0 -125 31 13 0 30 -70 -66 0 -46 103 -44 0 -126 -128 -91 0 -41 -8 97 0 -57 53 -117 0 21 62 -57 0 -108 -51 -113 0 88 80 -85 0 25 -7 -23 0 94 70 54 0 69 23 20 0 -132 -6 23 0 -69 10 30 0 23 -11 98 0 -106 3 -29 0 89 35 -71 0 60 110 -70 0 -37 58 -39 0 7 75 -102 0 89 39 -92 0 7 -1 -71 0 -132 107 -46 0 -103 72 -127 0 -75 69 -1 0 77 -60 -95 0 73 -33 -80 0 97 66 -118 0 -81 -64 67 0 47 56 72 0 128 33 103 0 -40 -81 -63 0 -59 -107 74 0 -49 -33 -43 0 62 84 31 0 15 -88 -97 0 -7 42 -70 0 -13 53 49 0 88 -111 115 0 -55 -130 -111 0 11 -92 -99 0 -50 -34 -87 0 -120 77 -108 0 -73 17 -68 0 77 65 66 0 49 121 -65 0 -135 -130 -96 0 -51 -65 62 0 -58 -11 -83 0 -61 -22 -129 0 78 -83 4 0 -43 30 104 0 125 -24 -54 0 129 -3 114 0 43 70 6 0 77 -119 113 0 98 -2 19 0 135 -30 -119 0 18 -72 -87 0 -84 54 -98 0 13 -24 113 0 -47 -113 -57 0 55 -59 -50 0 67 82 -33 0 -93 -118 35 0 -70 34 62 0 -6 118 -43 0 86 -92 118 0 -53 88 -79 0 -133 88 56 0 81 92 -22 0 80 83 -105 0 58 -32 33 0 -1 108 -44 0 51 111 125 0 -52 -77 -78 0 113 -70 -73 0 25 -100 -26 0 -42 41 -130 0 -23 50 47 0 117 -21 -99 0 -49 -62 43 0 -21 -68 -63 0 -53 41 -25 0 -25 -2 -30 0 114 -16 -57 0 32 112 113 0 -87 -135 -126 0 16 -30 -17 0 -131 -41 -27 0 89 -17 105 0 103 -34 -48 0 43 -61 90 0 71 58 -122 0 -75 -124 79 0 36 -60 -120 0 71 -90 64 0 -45 -36 -76 0 -40 -24 -108 0 -56 -19 24 0 111 -70 -36 0 106 88 7 0 111 -102 47 0 -48 -77 -33 0 -114 36 133 0 -109 3 -98 0 50 -65 -103 0 63 -105 4 0 110 29 113 0 32 -124 63 0 44 -111 71 0 -20 -70 4 0 64 134 93 0 109 -103 -56 0 85 -7 134 0 60 -80 -108 0 -44 60 -95 0 -104 -56 52 0 -112 -10 -47 0 -19 44 -49 0 120 67 41 0 64 7 -125 0 121 -118 -113 0 101 57 95 0 -117 -31 -10 0 30 51 27 0 42 -99 -80 0 -78 -40 -94 0 -60 -42 -52 0 99 -57 6 0 -24 36 -52 0 125 -20 -76 0 120 2 84 0 -107 -2 95 0 75 73 -97 0 -68 122 42 0 -57 -25 -92 0 -122 27 9 0 84 133 88 0 -37 130 45 0 16 -11 44 0 48 -16 -67 0 104 75 121 0 -21 9 10 0 87 -113 35 0 106 -115 3 0 88 126 57 0 -85 51 -56 0 -63 -19 68 0 -56 -43 -55 0 -98 42 -121 0 102 -90 -99 0 -95 -130 1 0 -124 11 -75 0 -50 -110 -87 0 8 52 93 0 -19 -29 28 0 -76 -75 -88 0 21 36 39 0 -38 -132 -122 0 -37 -51 -124 0 26 -6 16 0 -63 -29 42 0 87 -102 4 0 112 29 80 0 -51 32 30 0 -93 36 10 0 121 95 107 0 97 59 66 0 -108 132 -113 0 -52 61 36 0 82 84 -5 0 46 -52 108 0 1 50 3 0 -18 47 124 0 -120 103 -130 0 101 -120 -15 0 -131 91 86 0 81 -97 -115 0 41 -35 76 0 43 94 -88 0 38 -110 16 0 -105 3 134 0 58 -71 -8 0 -46 -58 47 0 -26 -130 10 0 -112 -17 -98 0 -37 126 -73 0 -97 -96 52 0 126 129 -48 0 56 -95 -7 0 53 80 -4 0 73 74 7 0 -95 128 61 0 66 -107 119 0 127 126 70 0 -118 21 108 0 -114 -69 10 0 -13 88 -59 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/20.cnf0000644000175000017500000000174012253362407021711 0ustar georgeskgeorgeskc c SAT instance in DIMACS CNF input format. c p cnf 20 85 -7 -6 -10 0 -2 -17 16 0 -19 -16 -6 0 12 -18 7 0 14 -10 7 0 18 4 20 0 -17 9 -18 0 -11 20 6 0 -20 7 4 0 16 6 18 0 12 11 -4 0 -15 -16 13 0 -16 -17 12 0 20 11 8 0 19 11 18 0 18 19 -12 0 7 13 -1 0 -4 18 13 0 7 -18 -1 0 -17 6 3 0 18 2 19 0 -19 4 1 0 -11 2 -12 0 -11 9 6 0 -20 1 -4 0 -5 3 -17 0 17 9 15 0 -15 18 -13 0 3 18 -20 0 7 8 -18 0 -8 16 7 0 -11 14 -9 0 -4 8 -15 0 1 13 4 0 -19 -9 4 0 18 6 -2 0 16 13 4 0 -19 -2 14 0 6 -12 -3 0 -8 -16 6 0 -9 18 2 0 19 -12 -18 0 -17 -15 8 0 1 17 15 0 -19 -2 18 0 12 -1 10 0 8 17 19 0 11 -4 19 0 18 -7 -5 0 -2 8 -7 0 2 19 -6 0 5 -7 -13 0 -5 7 14 0 7 8 -19 0 19 -3 -5 0 19 6 -5 0 5 -12 -10 0 3 -5 -4 0 5 18 7 0 15 -13 -3 0 -20 -5 14 0 14 -18 8 0 -8 2 19 0 -18 14 -20 0 -2 19 3 0 -14 12 -6 0 -11 -20 5 0 -14 -11 -17 0 17 19 -6 0 -2 -1 -8 0 19 17 -2 0 6 19 3 0 -18 2 17 0 -5 -15 -11 0 10 -20 -18 0 -9 -18 -20 0 -18 8 -6 0 3 12 19 0 -6 7 17 0 1 -13 11 0 -17 7 6 0 5 -15 -18 0 3 -7 20 0 13 -1 -5 0 2 8 5 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/125.cnf0000644000175000017500000001535412253362407022005 0ustar georgeskgeorgeskp cnf 125 531 -38 -124 -112 0 -84 30 64 0 71 -125 114 0 85 -115 19 0 -80 32 -113 0 103 -82 65 0 118 50 -39 0 92 -9 60 0 -9 -118 42 0 8 -118 -97 0 14 2 -63 0 100 2 44 0 -67 123 -4 0 -113 24 -29 0 -122 71 123 0 93 95 -97 0 -101 -64 -30 0 82 -9 -44 0 -91 -41 -12 0 36 -124 6 0 -112 63 -76 0 -113 -122 -70 0 59 123 109 0 43 3 -45 0 107 -36 -12 0 14 102 105 0 110 -71 85 0 -105 -63 -99 0 43 50 -51 0 105 122 -117 0 40 -9 29 0 94 20 -76 0 -12 -42 97 0 25 -57 14 0 47 -17 -69 0 123 80 -19 0 -94 75 -67 0 -66 60 -55 0 -46 89 16 0 84 10 6 0 -4 -86 -42 0 -81 -97 -4 0 -71 -24 97 0 82 4 -42 0 123 24 55 0 83 88 -17 0 -53 5 -29 0 107 -86 -46 0 65 75 46 0 -85 45 10 0 -69 -73 -36 0 -116 -79 109 0 -98 78 -112 0 -100 61 72 0 -88 100 -102 0 59 122 -57 0 -11 -61 -29 0 28 55 -33 0 -29 110 -9 0 -116 -91 27 0 3 -77 -52 0 51 35 -110 0 51 20 42 0 77 -9 61 0 -62 -79 -96 0 16 35 -27 0 9 2 -18 0 26 105 104 0 -103 99 -94 0 43 22 -68 0 -88 -15 56 0 -41 -69 95 0 -107 -14 52 0 -112 30 -52 0 93 32 -13 0 -45 -75 7 0 -35 -102 -75 0 -67 -80 -116 0 -27 -98 66 0 109 79 91 0 66 71 -94 0 91 115 97 0 27 54 82 0 -69 -93 108 0 -119 -65 75 0 85 -3 -102 0 102 11 -114 0 -47 18 -43 0 -79 -96 105 0 119 24 -23 0 111 81 35 0 24 103 42 0 -12 -53 -27 0 27 32 107 0 -44 113 60 0 109 47 -107 0 -98 -4 7 0 11 109 -87 0 89 125 107 0 -6 37 -47 0 -63 57 -20 0 -109 -18 64 0 -64 -43 42 0 -25 -62 -94 0 -4 17 94 0 47 49 -77 0 117 -23 13 0 10 -8 98 0 -118 -42 -103 0 -24 -115 76 0 -102 80 -100 0 11 -100 -5 0 63 77 -94 0 -93 27 32 0 27 57 67 0 89 81 -82 0 8 -85 -76 0 121 101 89 0 -116 105 -64 0 -99 -55 11 0 109 -4 -41 0 77 -30 -65 0 104 20 69 0 -86 -11 -54 0 48 -11 -53 0 36 -39 22 0 115 -102 99 0 -110 -35 -15 0 58 -117 64 0 22 48 47 0 12 -21 -119 0 -72 88 40 0 60 5 -108 0 52 -61 -70 0 58 114 7 0 16 -30 -100 0 28 -120 -43 0 10 8 -78 0 -55 6 -51 0 73 71 -80 0 96 69 -79 0 -34 -78 74 0 -54 99 -59 0 -67 -58 110 0 117 -85 75 0 -73 -33 -49 0 110 -100 25 0 107 123 55 0 -91 -49 -85 0 -101 -90 37 0 -37 106 26 0 19 -92 80 0 -94 102 91 0 73 74 -32 0 -47 75 61 0 66 6 106 0 26 120 -69 0 16 -103 -113 0 61 -4 89 0 2 58 -114 0 37 109 -121 0 23 104 93 0 79 105 -110 0 75 10 61 0 -29 -107 -114 0 77 -11 -7 0 -92 73 5 0 -115 70 -57 0 -95 -51 -114 0 1 -13 -59 0 115 94 88 0 -104 68 -26 0 54 -80 -8 0 -76 4 13 0 37 116 -69 0 -116 -80 13 0 32 64 94 0 62 -21 1 0 -42 60 11 0 10 108 69 0 77 61 82 0 -77 -89 30 0 -88 -25 -44 0 -111 108 -102 0 -10 -59 114 0 -99 12 -125 0 75 -70 -62 0 -25 -33 4 0 -86 124 5 0 -6 -95 -25 0 30 82 40 0 -37 -95 -110 0 -117 30 25 0 68 -83 41 0 105 -107 -124 0 -23 -31 -47 0 -95 -50 19 0 81 -101 116 0 71 -44 -112 0 -79 45 84 0 60 101 -111 0 -1 -11 115 0 -12 28 -115 0 -11 49 -14 0 -120 -18 8 0 -112 34 59 0 -56 24 -119 0 -95 -79 -8 0 -111 57 74 0 -63 80 -65 0 -77 -75 -90 0 83 45 -5 0 -60 57 -63 0 63 -115 -108 0 -120 91 -124 0 109 42 82 0 -113 101 -42 0 -115 -10 -14 0 13 -99 -71 0 108 6 -91 0 -123 25 -41 0 -10 -44 -34 0 9 64 -69 0 68 24 31 0 -62 -86 5 0 27 6 -68 0 -116 36 -18 0 -35 -75 -88 0 -2 8 -38 0 49 -116 54 0 -104 -71 29 0 -21 -83 -40 0 -4 -95 98 0 -47 -88 -70 0 84 4 20 0 105 18 119 0 44 76 -116 0 -36 104 -85 0 -88 79 23 0 3 118 -51 0 116 118 -107 0 -6 -101 92 0 -83 -86 63 0 35 54 -105 0 -78 -110 -16 0 -7 52 -18 0 -93 -44 -119 0 102 119 -44 0 -26 -118 59 0 -103 123 -114 0 48 6 -10 0 -19 118 84 0 -61 65 11 0 -7 -108 119 0 95 68 -108 0 -61 40 3 0 70 69 -63 0 -120 -1 22 0 105 -43 90 0 62 119 -53 0 15 86 -70 0 -80 101 -63 0 -69 -30 -80 0 64 109 -125 0 -65 60 101 0 -111 -30 -67 0 45 -109 92 0 46 -76 -45 0 4 18 42 0 -106 85 24 0 108 12 82 0 -15 108 -23 0 21 -110 107 0 -118 63 95 0 -51 -4 11 0 -112 -31 -77 0 -43 30 -108 0 -46 -76 -3 0 -101 -44 -20 0 -1 14 -49 0 -43 -104 101 0 -5 -20 -8 0 -37 -87 -9 0 -61 26 -1 0 118 -104 11 0 83 -38 -13 0 -81 -62 50 0 -82 -100 -51 0 25 -62 -33 0 -42 22 -74 0 -120 81 11 0 47 1 53 0 40 36 116 0 52 -45 118 0 -49 34 45 0 -20 54 -14 0 99 70 -24 0 120 33 119 0 -18 -35 115 0 92 -91 -109 0 114 10 28 0 72 79 1 0 -67 89 -78 0 -31 59 60 0 -96 -45 123 0 68 -74 -17 0 -90 -124 -73 0 117 -101 110 0 -74 100 -57 0 -102 -3 -15 0 40 73 -52 0 -46 92 -54 0 -92 57 -86 0 -68 -80 -31 0 -116 71 -75 0 78 46 -76 0 -32 -10 -125 0 55 -108 5 0 -66 125 -62 0 21 114 -47 0 52 44 -97 0 -59 86 108 0 -112 18 108 0 86 -96 75 0 -16 60 31 0 85 -16 -79 0 -19 -67 -5 0 122 -73 -48 0 -80 73 44 0 74 88 -19 0 -89 10 4 0 -29 90 -118 0 -41 -45 10 0 110 85 16 0 -60 121 -56 0 -93 -63 48 0 21 42 37 0 43 -76 -29 0 -37 97 -7 0 56 117 -19 0 30 49 -3 0 -80 -103 -81 0 -6 -2 56 0 55 -81 37 0 98 70 -36 0 -65 10 13 0 110 81 -113 0 -104 112 78 0 67 -64 -16 0 99 122 33 0 -4 12 -33 0 62 -119 -8 0 111 49 92 0 -106 -33 -80 0 -89 -84 104 0 -14 104 77 0 -6 89 -19 0 95 70 54 0 59 -84 -73 0 100 -87 -48 0 59 124 78 0 -41 -83 25 0 -105 -34 -77 0 60 30 83 0 89 -13 -80 0 -21 30 45 0 50 -63 -1 0 73 -54 -68 0 -20 50 74 0 -77 85 -13 0 109 -103 72 0 -69 -52 8 0 -14 121 25 0 -93 -39 72 0 42 -66 -111 0 53 60 21 0 -93 -61 -111 0 -106 50 72 0 -97 -56 20 0 -17 118 -121 0 43 92 83 0 -59 83 38 0 -48 17 -20 0 33 6 -103 0 87 53 -98 0 -69 75 -41 0 64 85 -97 0 -118 60 11 0 -29 -122 48 0 -73 -20 -16 0 -14 59 69 0 -24 -66 -81 0 73 3 -56 0 4 91 27 0 -91 65 7 0 67 -82 -30 0 -18 -54 74 0 -38 86 -119 0 -117 99 77 0 18 -51 -30 0 3 -50 -47 0 86 76 -40 0 -98 -70 47 0 -21 -83 3 0 85 -43 107 0 -41 125 32 0 -40 76 -49 0 -122 -23 67 0 -89 -9 27 0 -2 121 55 0 -102 -73 103 0 -124 87 14 0 47 32 -94 0 -64 44 -86 0 -52 42 -51 0 84 96 45 0 101 -67 -87 0 -69 107 -55 0 120 18 -107 0 -124 -37 100 0 14 -75 123 0 119 -47 72 0 -44 37 111 0 -1 115 28 0 -93 -98 -59 0 95 18 -3 0 -78 40 71 0 117 -79 78 0 -112 107 -29 0 -40 108 32 0 -98 -80 -86 0 -95 -36 79 0 -94 -41 -92 0 -87 92 109 0 48 86 27 0 -1 120 -104 0 50 -79 100 0 62 20 107 0 -67 51 -90 0 32 17 -7 0 59 58 -95 0 -85 -18 -103 0 -42 30 115 0 68 -113 -98 0 69 -89 -45 0 22 10 -3 0 52 74 -69 0 -82 -80 3 0 -124 -97 -80 0 -112 -35 -73 0 82 103 -3 0 -41 -53 106 0 -109 -80 38 0 -43 110 -35 0 -27 -51 1 0 -122 -120 -107 0 34 61 28 0 -10 94 6 0 -122 102 -117 0 29 60 94 0 35 -48 -27 0 116 -28 -45 0 99 -6 17 0 -30 77 -60 0 -79 -59 113 0 78 -21 107 0 -45 14 -106 0 1 50 -25 0 -109 58 75 0 76 -62 116 0 8 46 -54 0 11 8 -109 0 66 14 -19 0 -21 -97 84 0 45 -7 32 0 -89 -120 -67 0 -82 97 -46 0 22 -121 96 0 66 24 -118 0 -26 -32 -67 0 77 -110 -86 0 -46 19 -48 0 -8 -1 -104 0 84 -24 66 0 -82 18 -71 0 117 104 93 0 -62 -38 -101 0 111 -70 18 0 109 -36 -14 0 -114 -99 12 0 -55 118 -17 0 -91 -19 53 0 -89 -98 112 0 76 23 -52 0 14 -20 120 0 60 -57 26 0 -79 -7 -16 0 99 -43 -119 0 47 93 45 0 -17 -110 106 0 -105 -123 22 0 84 -123 59 0 53 -70 -39 0 86 -121 77 0 -29 51 -88 0 15 103 9 0 73 122 -64 0 16 119 56 0 76 -13 107 0 48 -82 8 0 -16 52 -37 0 23 -37 40 0 -2 -79 -63 0 -106 37 -61 0 -32 51 99 0 -106 -46 32 0 -112 -63 46 0 64 61 -71 0 -40 79 108 0 -20 -120 -112 0 51 -40 82 0 91 -109 89 0 89 -32 61 0 -67 47 108 0 -56 -31 -80 0 116 -106 -28 0 -36 -74 -58 0 -49 54 68 0 124 -56 61 0 117 46 -41 0 -13 -108 31 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/10.cnf0000644000175000017500000000073212253362407021710 0ustar georgeskgeorgeskc c SAT instance in DIMACS CNF input format. c p cnf 10 42 -2 8 -9 0 7 2 10 0 -6 1 -8 0 3 -6 7 0 -9 3 4 0 -10 -8 9 0 -2 -6 -9 0 8 -10 7 0 -5 -4 -6 0 7 -9 5 0 -8 4 5 0 3 7 10 0 -5 -6 -8 0 -1 8 -6 0 1 -3 4 0 1 2 -5 0 4 -10 7 0 -5 2 3 0 8 7 5 0 9 -3 4 0 -3 1 -10 0 8 2 -4 0 2 -8 9 0 4 -7 2 0 -2 -7 10 0 -6 -10 -1 0 -9 5 -8 0 8 10 1 0 -7 -2 -10 0 -10 -5 4 0 -7 5 -4 0 -3 -1 -8 0 -1 5 7 0 2 -4 -3 0 -2 3 8 0 -2 -9 -5 0 -8 5 2 0 6 -10 2 0 -4 6 -9 0 9 10 -7 0 -4 -10 7 0 -10 4 7 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/140.cnf0000644000175000017500000001731712253362407022003 0ustar georgeskgeorgeskp cnf 140 595 -33 3 128 0 39 -127 14 0 36 71 26 0 105 45 115 0 1 119 85 0 110 -47 118 0 88 -27 -123 0 38 -140 77 0 50 -89 3 0 133 129 100 0 104 -63 15 0 -135 55 44 0 46 40 49 0 -3 14 -49 0 105 7 -73 0 -44 26 114 0 -123 -128 98 0 5 130 45 0 33 38 55 0 -2 70 -19 0 36 -96 -70 0 -132 -110 31 0 -140 -137 -53 0 -39 19 -77 0 -119 -122 94 0 -73 -51 1 0 -15 29 34 0 39 -11 -59 0 -42 -80 17 0 -43 -52 -34 0 -16 104 79 0 -115 18 71 0 59 111 15 0 70 134 -98 0 -73 66 -83 0 -113 -3 30 0 95 -5 40 0 -54 68 -21 0 21 114 25 0 7 23 25 0 17 -127 -132 0 -117 -49 100 0 136 82 -85 0 -92 99 47 0 90 4 128 0 -27 38 127 0 46 -96 -40 0 112 25 40 0 123 124 114 0 43 -74 -21 0 107 -130 22 0 26 123 -111 0 83 113 -12 0 -74 12 22 0 -51 -133 96 0 58 -78 -101 0 92 -87 31 0 -64 -78 -65 0 82 128 -73 0 -122 11 59 0 116 128 -55 0 95 -112 124 0 -91 -24 -130 0 -37 -91 -139 0 76 25 1 0 33 -136 102 0 -31 50 3 0 82 22 -69 0 -120 -84 -55 0 -60 -52 106 0 -22 -97 -15 0 -104 -117 65 0 -37 137 -49 0 5 -113 -20 0 -78 -125 48 0 86 53 39 0 -18 42 -102 0 -100 -91 76 0 -98 -16 -112 0 31 -116 109 0 20 -38 140 0 -10 -34 47 0 133 5 72 0 102 -14 135 0 28 -72 -109 0 1 -35 -25 0 -19 -46 -31 0 38 -19 -90 0 129 57 131 0 77 -101 78 0 -126 57 29 0 -107 -47 -117 0 39 -13 -6 0 48 57 -47 0 -50 -23 72 0 -7 101 -39 0 71 117 -29 0 -60 125 -44 0 78 46 130 0 -130 -59 -63 0 30 16 92 0 66 -95 -69 0 49 -119 29 0 17 72 64 0 4 56 -15 0 -30 20 -35 0 -51 112 -63 0 -24 -83 10 0 -93 -129 70 0 135 131 7 0 -122 -42 -61 0 19 -15 9 0 -73 -69 140 0 69 76 89 0 -110 -72 -75 0 29 -117 115 0 -50 2 -133 0 104 -49 82 0 -58 17 -61 0 126 -45 90 0 -79 29 -134 0 6 30 -135 0 67 19 -24 0 -80 13 57 0 88 41 16 0 67 -50 -33 0 -124 83 69 0 74 119 137 0 57 -86 -69 0 39 96 29 0 105 49 -91 0 80 -1 -126 0 -14 132 80 0 -52 62 -67 0 44 -78 -134 0 119 -7 10 0 -17 -1 -4 0 71 -78 -3 0 88 -55 -3 0 -73 62 65 0 54 92 -88 0 11 -65 124 0 25 71 98 0 28 -120 -7 0 -29 -76 64 0 104 -109 94 0 -12 136 -139 0 59 68 -120 0 -40 73 -59 0 80 79 139 0 -137 -112 -133 0 119 61 43 0 50 110 78 0 116 36 111 0 -34 -28 -59 0 68 1 131 0 -50 75 -113 0 -126 -133 48 0 -11 134 -20 0 -84 122 -139 0 -50 -26 -23 0 17 93 -55 0 -80 26 10 0 -38 115 78 0 -137 -67 -21 0 -13 -4 -42 0 -118 -62 -28 0 -63 139 -64 0 61 -59 -43 0 12 87 -88 0 -54 15 29 0 59 -14 -70 0 -43 -94 -27 0 -116 106 -42 0 -89 63 -38 0 49 -35 63 0 -60 23 -110 0 -55 -93 39 0 28 -15 -34 0 -2 -85 65 0 89 115 102 0 67 12 74 0 -29 64 -3 0 -57 10 15 0 -78 107 122 0 -64 -83 138 0 -97 49 -71 0 30 -112 17 0 10 -5 -116 0 -97 -136 -46 0 -70 97 -115 0 10 34 1 0 114 -105 -7 0 -83 25 -85 0 51 -131 110 0 108 -133 -93 0 87 -93 -59 0 -45 96 -98 0 108 -77 61 0 -24 -121 67 0 44 -8 6 0 46 8 -105 0 48 7 -92 0 -41 138 1 0 -51 3 -30 0 -87 -35 130 0 -117 -65 119 0 82 -45 71 0 -91 -84 135 0 -52 -83 -114 0 57 -118 -104 0 -22 -79 20 0 -49 -63 -57 0 -69 53 42 0 -121 -26 -9 0 126 -68 -110 0 -105 10 134 0 120 63 66 0 85 51 -130 0 9 50 131 0 -34 -96 51 0 -1 73 13 0 -13 71 -97 0 117 135 121 0 127 -45 -69 0 7 95 -118 0 -54 -20 -75 0 -139 -121 99 0 47 48 -118 0 -135 -2 105 0 66 102 43 0 53 12 -88 0 32 19 -28 0 -76 -126 27 0 95 81 53 0 -68 137 -50 0 -26 -35 -63 0 -68 -132 -10 0 86 30 -124 0 -116 -77 34 0 69 82 -125 0 -4 31 -100 0 108 12 138 0 -136 56 97 0 42 -25 97 0 -58 88 -80 0 25 118 38 0 116 -24 110 0 -98 -43 -85 0 -93 140 -78 0 88 93 42 0 140 133 34 0 -119 37 -100 0 -93 9 -124 0 -30 -108 -46 0 -9 -72 24 0 -32 -89 79 0 66 13 -87 0 72 101 105 0 -95 -128 -69 0 108 69 -24 0 83 53 30 0 114 116 98 0 -36 131 -50 0 -101 -123 43 0 69 -21 113 0 11 -121 -119 0 -105 -15 74 0 -51 140 37 0 62 57 -24 0 106 -108 -112 0 98 -70 7 0 38 -131 26 0 -14 -18 -109 0 -56 72 7 0 115 71 13 0 13 18 102 0 -132 -23 -106 0 -16 -25 -70 0 -120 62 -96 0 57 105 -28 0 -101 -123 -55 0 -51 -19 -86 0 43 85 -93 0 -19 14 -113 0 -35 -107 32 0 107 96 24 0 84 130 -25 0 50 -33 76 0 101 82 117 0 -139 -98 43 0 -139 48 -113 0 -69 -84 97 0 -112 101 11 0 -84 -94 -1 0 -12 -34 120 0 103 -137 -34 0 116 -133 103 0 115 42 9 0 -39 -134 -27 0 -4 110 -31 0 106 58 -116 0 2 80 109 0 -13 -122 -52 0 105 70 14 0 -87 110 83 0 -132 -14 137 0 47 -56 -41 0 45 86 62 0 110 75 -20 0 82 -2 112 0 83 -126 31 0 -96 -98 -85 0 -36 -100 -135 0 -139 -7 -25 0 60 38 102 0 77 -58 126 0 -80 -79 51 0 -67 -140 125 0 -24 -128 -123 0 16 -5 65 0 -86 10 -42 0 46 -88 -79 0 81 44 -74 0 82 115 27 0 37 55 86 0 -71 -117 -29 0 131 -57 86 0 -107 -36 -85 0 129 -56 133 0 -22 87 -41 0 107 52 2 0 -89 108 93 0 81 82 -111 0 -73 -2 -66 0 128 -27 121 0 12 -94 109 0 -83 -107 64 0 -50 92 95 0 -39 -73 107 0 -35 89 -41 0 59 -53 78 0 -113 -39 -107 0 73 84 -27 0 -30 -132 103 0 40 -13 85 0 -6 -139 42 0 12 -23 -50 0 82 -22 95 0 -102 -59 -87 0 52 -71 54 0 -93 115 -9 0 -12 -95 7 0 79 35 93 0 -102 -3 -91 0 -28 -13 -125 0 6 -66 -97 0 126 76 -39 0 96 136 -12 0 -110 15 -93 0 6 -5 58 0 -115 1 -55 0 134 5 -18 0 140 27 -16 0 63 -125 136 0 23 3 7 0 -129 -59 122 0 -47 -84 124 0 123 -25 52 0 5 -16 -8 0 -76 93 -23 0 -69 5 -46 0 124 -71 -26 0 -132 -14 -140 0 -94 -138 82 0 113 3 -4 0 -13 -64 50 0 -73 -89 -133 0 16 109 -140 0 66 138 75 0 131 -3 50 0 69 66 77 0 -68 -64 -6 0 -36 109 -93 0 -88 70 86 0 -2 -134 -53 0 -42 29 138 0 101 -20 -55 0 31 62 -108 0 130 29 42 0 -4 19 -69 0 -139 -20 -2 0 30 -107 93 0 -132 -68 137 0 35 42 -132 0 136 134 36 0 -74 63 -92 0 86 50 -131 0 105 81 -47 0 50 75 -120 0 -120 6 -49 0 82 -37 30 0 -119 -88 46 0 -82 108 -44 0 -42 119 -29 0 -21 -69 36 0 -123 -54 -134 0 138 29 -46 0 -81 131 122 0 -138 -32 68 0 -41 -36 -118 0 -36 34 124 0 51 69 -133 0 -108 -101 107 0 87 -122 46 0 -127 16 -139 0 40 20 49 0 -52 131 133 0 52 137 -58 0 129 5 -12 0 14 28 -74 0 92 93 -127 0 44 50 131 0 23 46 105 0 104 -137 127 0 96 -125 76 0 -137 43 55 0 18 -53 61 0 -75 27 86 0 -140 -52 21 0 22 -54 -55 0 96 98 -47 0 -76 128 -73 0 -45 -38 70 0 115 -27 74 0 -116 -72 -96 0 -69 -127 50 0 -78 -122 -108 0 124 25 140 0 -57 -12 99 0 15 -123 -131 0 46 45 81 0 -97 54 59 0 43 -76 26 0 -97 113 -135 0 -139 -76 -140 0 -71 44 -99 0 46 -52 -101 0 20 113 49 0 -128 69 3 0 -120 -54 -75 0 100 -64 111 0 127 -122 58 0 -73 47 -110 0 -114 -131 -30 0 -42 -37 17 0 43 -2 -125 0 16 -58 -35 0 -37 -123 139 0 -122 72 92 0 -71 91 30 0 60 50 -139 0 -117 9 -61 0 -29 -80 -7 0 78 -138 97 0 33 -28 -107 0 -94 -53 55 0 -126 114 101 0 48 -47 -132 0 -10 -31 -115 0 29 -15 110 0 -135 -32 -121 0 121 99 125 0 -83 -74 -73 0 4 134 18 0 -71 98 -27 0 18 135 -60 0 102 -119 -46 0 57 27 -59 0 -125 103 -124 0 120 125 -95 0 32 -42 -66 0 72 -110 109 0 -139 -92 -50 0 -94 38 55 0 78 -63 62 0 -130 -67 -74 0 137 -116 27 0 77 -88 16 0 113 -109 19 0 41 -93 72 0 -60 125 68 0 80 123 128 0 -127 121 -65 0 -77 -52 135 0 107 118 117 0 45 74 -64 0 72 -41 -124 0 139 -96 21 0 -112 -95 53 0 -100 118 67 0 116 88 73 0 75 -108 46 0 -49 -130 2 0 -133 -76 88 0 67 -24 -73 0 -100 -135 97 0 120 -131 -51 0 -135 -106 32 0 -129 52 -91 0 -94 10 -126 0 -93 -68 -47 0 -139 -15 -40 0 -20 -111 103 0 -96 40 -58 0 -111 -119 -63 0 137 -126 -80 0 65 -60 98 0 -118 76 78 0 -7 33 -3 0 -22 -37 -77 0 44 112 125 0 -47 33 74 0 -35 84 135 0 -48 -116 -101 0 75 97 13 0 130 -12 -26 0 -61 57 -69 0 -89 -80 76 0 -66 7 104 0 -72 -89 -6 0 130 -19 15 0 106 -136 129 0 -95 -130 30 0 -94 -127 132 0 135 93 53 0 -50 -119 -78 0 27 -59 79 0 122 -8 -108 0 42 -112 -131 0 -116 -65 78 0 6 60 132 0 40 -4 132 0 -107 -122 86 0 113 -109 51 0 -81 -68 -52 0 -87 33 108 0 -91 -110 78 0 14 118 39 0 48 109 -23 0 -108 -9 85 0 -17 -133 131 0 -73 -126 -106 0 41 -110 139 0 35 -96 -106 0 135 -16 -107 0 119 -129 132 0 -47 -8 -64 0 111 -7 122 0 -92 -30 60 0 -105 -110 91 0 -39 -125 19 0 -85 -113 92 0 -121 87 86 0 59 -21 114 0 -20 139 -91 0 -52 84 -8 0 21 19 -34 0 -42 111 -91 0 -73 33 14 0 94 -32 119 0 -64 138 43 0 -86 -59 36 0 -1 28 134 0 -88 40 -117 0 -98 -29 127 0 68 -105 -118 0 4 139 44 0 61 122 91 0 -113 49 -130 0 -20 85 -105 0 -71 -38 -64 0 116 -114 14 0 -71 23 132 0 -100 131 104 0 104 -32 74 0 140 -1 44 0 103 70 -60 0 123 76 -20 0 -59 38 -134 0 -35 75 -40 0 -13 -11 38 0 65 95 -130 0 -113 -125 20 0 132 -67 49 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/85.cnf0000644000175000017500000001055712253362407021732 0ustar georgeskgeorgeskc c SAT instance in DIMACS CNF input format. c p cnf 85 361 -46 -16 3 0 37 8 58 0 66 10 -81 0 -9 37 -56 0 -57 58 28 0 -19 61 -53 0 74 -1 -40 0 19 -56 -2 0 -1 6 -84 0 -76 -42 -64 0 73 -51 14 0 -29 -57 14 0 73 -40 7 0 10 66 75 0 55 12 43 0 -16 49 -31 0 27 -65 -7 0 -27 52 -38 0 71 15 85 0 -62 79 15 0 15 79 69 0 44 -47 -75 0 25 22 -33 0 -71 -84 -30 0 49 15 -6 0 -21 -29 85 0 -16 -6 -73 0 -70 -58 35 0 -35 24 -55 0 58 75 -76 0 -17 74 40 0 47 56 83 0 47 -75 29 0 48 12 -40 0 -49 63 -65 0 -66 -64 81 0 59 -54 82 0 42 27 -7 0 11 37 35 0 37 12 73 0 -23 36 -15 0 -26 39 -8 0 -50 40 10 0 29 77 -76 0 -57 -39 -30 0 -16 80 48 0 31 -55 25 0 -73 -29 -5 0 -62 11 38 0 28 23 66 0 -3 9 2 0 70 -29 -25 0 82 75 20 0 -67 62 -13 0 37 72 83 0 5 -83 -68 0 48 82 80 0 -17 -11 16 0 -15 -76 -28 0 22 -33 -75 0 -33 -21 37 0 -2 73 -4 0 -46 62 24 0 17 -24 6 0 33 10 -80 0 56 -25 31 0 -58 80 42 0 67 28 12 0 30 2 44 0 -72 83 -71 0 5 69 3 0 -7 16 -30 0 -13 35 -64 0 43 84 -67 0 -22 56 10 0 22 23 48 0 25 5 -59 0 -73 -21 -36 0 -56 -33 21 0 21 55 84 0 80 -69 20 0 -62 -79 19 0 -26 -17 71 0 3 -17 -23 0 -11 -52 -39 0 -18 -53 -5 0 -1 -81 -65 0 -40 70 -74 0 66 32 -59 0 -14 37 4 0 -81 -70 57 0 -6 -76 28 0 22 68 -28 0 80 71 26 0 15 -6 -14 0 45 -52 47 0 -45 -58 -34 0 18 82 61 0 26 -48 -23 0 -37 16 -35 0 -31 34 39 0 55 -72 -63 0 -83 45 81 0 -82 20 79 0 80 42 -82 0 -14 -71 34 0 -83 70 -44 0 -71 -66 60 0 6 -84 -77 0 -1 -79 -65 0 -8 4 79 0 21 7 18 0 -4 -7 27 0 -32 -25 -27 0 -54 36 30 0 58 -85 2 0 1 65 -67 0 44 54 -2 0 -72 -41 -83 0 7 -75 -81 0 -41 30 -39 0 -78 83 45 0 63 29 -18 0 76 -60 70 0 -20 43 46 0 78 -72 23 0 3 -73 -2 0 49 -67 63 0 32 71 -59 0 -49 -21 -60 0 -68 -43 -79 0 -30 -45 62 0 37 -85 -62 0 -64 -19 76 0 6 -63 15 0 -28 -34 38 0 13 -64 -4 0 58 -50 78 0 -14 12 -68 0 78 -26 51 0 33 18 -34 0 -5 -42 72 0 68 36 12 0 21 44 -3 0 -73 -51 83 0 -32 7 43 0 -66 32 -14 0 -26 11 54 0 85 20 -76 0 -24 53 -76 0 -82 66 -3 0 -14 -13 24 0 38 67 5 0 16 -82 -69 0 19 30 18 0 64 -85 -28 0 -37 79 55 0 -58 66 -85 0 -54 20 -56 0 -32 78 -37 0 19 -16 -12 0 77 52 -49 0 -36 -33 -81 0 16 -4 74 0 65 21 60 0 -72 -32 7 0 25 22 -18 0 -45 -30 39 0 -16 20 72 0 -59 -38 -39 0 65 -52 -62 0 -7 -27 -34 0 -13 -2 -65 0 -24 -42 4 0 -63 1 5 0 -55 -31 -69 0 45 11 24 0 -63 -49 9 0 66 23 21 0 85 -37 21 0 62 -83 -76 0 6 85 -54 0 67 -47 -43 0 -25 49 -27 0 -79 -85 62 0 74 57 -29 0 51 53 -80 0 -63 80 48 0 -37 -12 64 0 -62 65 75 0 -79 -68 45 0 -37 -84 -53 0 -72 43 55 0 50 62 -39 0 48 63 -7 0 -20 -25 -24 0 20 -15 54 0 -57 -55 81 0 -48 -69 6 0 -19 -1 -51 0 -27 20 -78 0 -57 55 67 0 9 45 -1 0 13 81 -66 0 -60 -72 68 0 -31 -47 -61 0 48 4 -80 0 5 44 28 0 -33 66 -6 0 -43 35 46 0 46 34 47 0 -3 52 54 0 -15 2 -82 0 27 -79 -35 0 -20 17 47 0 46 14 -35 0 54 -78 -47 0 19 -31 63 0 -3 -1 33 0 -1 46 55 0 -22 -15 72 0 31 60 -15 0 63 24 -4 0 -8 -44 -83 0 34 -61 -76 0 -5 82 -36 0 18 26 -61 0 66 -10 -17 0 9 15 31 0 -55 23 -19 0 -28 -5 -69 0 70 -30 -83 0 -76 -48 -77 0 -7 61 -32 0 44 51 20 0 13 -20 70 0 64 -54 -12 0 -26 73 77 0 -83 -61 -45 0 -14 13 -33 0 84 -34 -8 0 37 -2 -26 0 23 14 -55 0 -51 -71 -57 0 9 43 31 0 -6 -25 -21 0 65 -26 -27 0 23 46 -27 0 -41 -3 -44 0 -70 -10 -65 0 30 -81 -72 0 39 -40 79 0 36 -22 -43 0 10 -42 -76 0 49 -21 -24 0 -85 -28 -22 0 -36 13 -30 0 -50 28 -79 0 -79 -71 -57 0 18 19 22 0 -62 -8 -11 0 -14 17 15 0 19 31 13 0 47 -44 41 0 64 -73 -67 0 15 76 -42 0 76 -36 -62 0 -14 -41 47 0 24 4 -16 0 21 -67 -35 0 53 -61 41 0 -84 -46 -76 0 -22 34 10 0 -48 33 -85 0 -39 -48 50 0 -55 -56 1 0 66 -23 -51 0 -68 32 -80 0 75 65 -82 0 -18 37 -85 0 33 -53 -54 0 -85 84 -79 0 28 22 -41 0 -5 52 -29 0 8 33 9 0 -28 -39 -78 0 -30 -21 85 0 -43 12 78 0 -74 -13 -83 0 67 -57 35 0 -42 23 82 0 59 -61 -48 0 -53 -67 -40 0 45 76 75 0 38 9 -75 0 66 -21 16 0 1 6 8 0 69 -14 3 0 -6 -45 -9 0 -46 30 55 0 76 -84 -24 0 -22 -59 -67 0 54 27 52 0 49 45 -38 0 -21 8 -16 0 17 -50 -72 0 -70 -27 -28 0 33 -30 35 0 51 -17 21 0 47 -6 5 0 -23 50 72 0 -14 -82 70 0 41 -66 36 0 -27 -33 -66 0 46 27 -25 0 -47 19 45 0 -47 1 20 0 -12 -73 78 0 29 -36 37 0 18 2 77 0 18 70 -30 0 26 1 13 0 21 -72 66 0 66 13 53 0 39 51 -68 0 53 56 80 0 -80 -75 49 0 -14 -11 6 0 -5 60 -36 0 58 68 -36 0 62 -2 -41 0 -23 62 -70 0 -75 -42 30 0 34 -65 57 0 -66 -45 -71 0 75 77 5 0 13 -66 48 0 72 -40 -29 0 61 74 25 0 -77 -49 -62 0 11 -61 -1 0 -31 -34 64 0 67 47 57 0 -75 -28 -19 0 22 11 -15 0 -2 28 -84 0 -66 -37 -84 0 75 63 50 0 84 9 -80 0 -69 76 -21 0 30 83 33 0 41 -85 -23 0 7 5 3 0 50 -47 -40 0 -69 79 30 0 75 -46 -72 0 69 3 -23 0 42 19 -12 0 29 54 44 0 -12 31 67 0 21 -64 -44 0 sympy-0.7.4.1/sympy/logic/benchmarks/input/100.cnf0000644000175000017500000001224712253362407021774 0ustar georgeskgeorgeskc c SAT instance in DIMACS CNF input format. c p cnf 100 425 -46 -1 -36 0 57 30 39 0 94 -68 -29 0 -89 94 -7 0 25 14 27 0 -22 -6 33 0 -6 -96 -59 0 5 -88 -70 0 -51 -70 -99 0 40 -66 -28 0 -7 71 42 0 -70 -78 -63 0 -86 96 -23 0 -72 94 -66 0 71 63 42 0 54 65 39 0 72 -69 -51 0 -83 8 -65 0 -62 -84 12 0 -8 -85 -95 0 24 -47 -62 0 -45 6 24 0 28 84 -88 0 75 -13 49 0 64 -17 -13 0 -7 -29 -65 0 -90 21 -27 0 -48 93 36 0 -64 -29 -1 0 26 20 -89 0 97 -33 -28 0 24 -19 20 0 -39 61 -58 0 -33 14 40 0 -20 17 84 0 12 -59 15 0 -32 -98 -63 0 15 24 -2 0 -55 81 -46 0 -19 -12 -77 0 -61 3 9 0 56 -85 77 0 55 -87 82 0 93 -92 79 0 -71 -54 42 0 8 26 -85 0 -46 -19 11 0 -8 57 23 0 16 17 9 0 -58 80 -10 0 -50 99 25 0 -23 -64 -4 0 61 -21 67 0 35 -57 -75 0 -40 53 -64 0 25 -84 33 0 -2 12 37 0 -86 -49 52 0 -28 23 77 0 45 -26 70 0 33 21 54 0 -36 -56 81 0 -31 98 -99 0 -86 -29 14 0 83 -42 -99 0 -33 69 -49 0 94 -75 85 0 -94 6 9 0 -64 -20 -94 0 -27 -93 -42 0 74 32 -55 0 -43 52 -78 0 -99 19 17 0 99 -22 48 0 36 21 -6 0 -33 89 -100 0 -58 -69 -74 0 -60 13 36 0 -28 60 24 0 -69 32 73 0 -65 100 74 0 87 43 63 0 16 -15 11 0 -4 -25 31 0 -100 -22 65 0 99 83 -76 0 -43 31 53 0 -33 78 31 0 -23 -37 -91 0 -69 91 -72 0 57 -86 -61 0 75 -14 4 0 -49 -58 71 0 65 48 -79 0 8 80 36 0 88 -97 -65 0 -25 78 -42 0 87 -48 -49 0 -63 -9 41 0 13 19 36 0 -66 93 -32 0 37 40 -55 0 -5 -90 -100 0 -98 -8 16 0 59 52 -17 0 76 -34 83 0 22 20 -90 0 18 8 -47 0 -83 -55 -38 0 -5 18 -44 0 38 -3 48 0 72 79 54 0 21 -92 -90 0 -31 92 42 0 -89 51 -90 0 -16 -18 -76 0 -54 -48 -97 0 7 20 -1 0 -36 -16 2 0 84 -34 -52 0 -12 71 67 0 -52 -65 -66 0 38 -4 -66 0 -72 -94 -45 0 -58 35 52 0 8 -51 5 0 47 -33 -52 0 -2 63 18 0 -13 -59 -99 0 48 -79 -14 0 28 94 40 0 96 -62 -58 0 26 25 -59 0 -94 -55 -44 0 -63 90 31 0 32 28 -34 0 53 -20 -99 0 78 -20 -84 0 -39 17 33 0 32 14 -24 0 28 21 -46 0 87 30 -6 0 55 -41 42 0 -89 -37 36 0 91 8 -98 0 -50 36 100 0 53 -30 86 0 -57 32 -20 0 43 83 -40 0 20 -84 -59 0 -13 80 21 0 20 42 -57 0 64 42 27 0 -21 -29 -22 0 -96 54 86 0 -43 73 50 0 73 -13 -21 0 -6 -55 -37 0 -95 60 17 0 38 83 85 0 -74 -54 3 0 69 -10 -13 0 -74 32 -82 0 49 90 -56 0 -11 51 -31 0 1 -27 -21 0 55 -4 -9 0 -73 81 -33 0 -28 -48 66 0 -19 25 -1 0 -46 94 37 0 42 -63 89 0 47 -58 -66 0 54 25 13 0 -95 23 100 0 21 -38 11 0 -32 -12 94 0 -46 80 88 0 36 -28 60 0 -74 69 50 0 -80 -4 -13 0 -73 -66 -36 0 58 -14 -64 0 56 28 -47 0 24 -74 -83 0 -1 34 -4 0 -82 -52 -5 0 54 10 -49 0 71 -10 -23 0 -72 98 93 0 62 -69 21 0 -39 -64 -7 0 84 85 -83 0 11 19 20 0 47 33 -36 0 98 -73 60 0 55 93 12 0 -61 -68 93 0 37 -30 72 0 -30 -66 -99 0 -61 -83 -14 0 69 -11 -51 0 -68 -94 -89 0 55 -7 48 0 100 24 58 0 96 -74 2 0 20 90 -29 0 3 98 -65 0 11 68 16 0 6 78 24 0 4 -17 88 0 -20 26 8 0 -68 96 -16 0 22 -14 -91 0 20 -85 5 0 99 -27 -98 0 14 74 93 0 -3 -53 -91 0 12 -29 83 0 -95 -77 55 0 35 -50 -75 0 44 -95 4 0 66 -64 -2 0 11 39 40 0 -47 -66 4 0 27 96 -19 0 25 -34 -55 0 35 20 -31 0 -32 -30 38 0 70 83 -33 0 26 -36 13 0 -51 36 40 0 42 -92 -35 0 -38 -66 -13 0 -56 85 -5 0 -66 97 14 0 -54 57 -17 0 68 -82 -83 0 19 -77 31 0 91 -74 -95 0 76 -38 72 0 70 -74 73 0 -23 -33 -1 0 -24 -18 90 0 -54 -15 59 0 32 -4 -25 0 31 -80 79 0 -13 -79 37 0 1 3 -42 0 -51 -44 -34 0 -92 68 55 0 -38 -36 39 0 60 -73 47 0 100 77 -68 0 -65 18 62 0 -2 44 3 0 26 -43 6 0 -16 85 -88 0 60 -34 -89 0 -29 18 -52 0 92 -83 66 0 66 49 4 0 -95 -36 39 0 38 79 -83 0 -95 59 21 0 -78 -8 88 0 -87 30 -51 0 -74 -93 -64 0 -19 31 -41 0 85 -23 25 0 25 20 37 0 71 -50 -23 0 34 69 71 0 53 93 -29 0 -48 -31 -49 0 -88 32 -45 0 -93 -46 68 0 -94 -38 66 0 17 -61 -99 0 -56 73 75 0 -40 18 83 0 76 57 -20 0 3 -46 41 0 -24 -49 20 0 66 10 50 0 74 -85 18 0 -95 65 20 0 -41 47 -22 0 10 17 -54 0 74 96 -31 0 -2 -50 39 0 -20 90 -95 0 -98 -18 36 0 -40 -35 19 0 -60 -33 82 0 -55 76 -63 0 4 -7 17 0 -17 -92 7 0 -24 -47 51 0 31 -55 45 0 23 -100 48 0 70 -22 99 0 98 -27 6 0 -82 -94 -56 0 -51 14 79 0 -15 -69 -23 0 -32 -47 -11 0 -58 72 -10 0 -7 75 -98 0 38 -33 67 0 76 -70 -49 0 95 -88 6 0 -13 -43 -35 0 -20 12 -10 0 -20 -7 -68 0 44 -65 82 0 -62 -69 2 0 -69 -64 -62 0 -9 -20 72 0 -25 83 6 0 47 35 -82 0 -71 33 17 0 77 26 12 0 -95 -72 -28 0 -99 -15 68 0 51 -53 34 0 -96 -66 -93 0 86 57 34 0 80 87 69 0 -23 10 42 0 42 36 -85 0 10 57 95 0 98 8 -87 0 -6 8 -96 0 -74 -4 -97 0 -21 -56 37 0 -51 -17 -64 0 54 -80 55 0 81 -37 9 0 -35 -91 74 0 67 2 9 0 33 -4 29 0 -29 -83 41 0 -77 -61 90 0 -20 -41 -79 0 -21 37 24 0 -78 -68 67 0 -6 82 -29 0 -93 11 -83 0 -17 -33 39 0 -1 -99 -8 0 -4 43 -27 0 -26 -35 66 0 -39 79 46 0 -69 -83 89 0 -18 91 -71 0 88 57 -61 0 -30 -96 22 0 -12 -13 11 0 44 -94 -21 0 -61 27 37 0 60 67 -25 0 14 -49 7 0 -70 58 -6 0 -36 -4 -70 0 -64 -65 -81 0 -20 38 40 0 42 74 29 0 70 10 -99 0 -70 -18 58 0 -72 -36 -68 0 8 -63 97 0 6 -2 -15 0 98 32 56 0 36 -20 -35 0 51 45 2 0 72 -89 -83 0 -38 39 -54 0 -71 30 58 0 87 37 44 0 86 -46 -41 0 -83 69 -8 0 -61 -15 -67 0 1 99 26 0 -75 89 -18 0 -100 67 -83 0 4 7 78 0 81 -4 -69 0 -87 97 79 0 90 89 78 0 20 -49 56 0 -63 -81 51 0 -96 -92 71 0 62 83 -44 0 -91 27 57 0 51 5 -71 0 76 57 -48 0 69 65 -1 0 23 2 60 0 31 -70 17 0 49 87 56 0 -35 -3 -88 0 -78 57 10 0 36 47 -72 0 69 73 -99 0 7 -47 -52 0 -55 63 -5 0 -85 77 82 0 39 -41 85 0 -65 -73 49 0 79 -17 -67 0 14 -75 69 0 -86 -6 -2 0 -85 -5 -53 0 -89 45 54 0 34 56 -20 0 -71 28 10 0 99 -21 57 0 -65 -18 36 0 -74 -57 -88 0 34 -14 28 0 65 -53 -64 0 -75 -41 32 0 40 -55 -37 0 -83 78 -77 0 sympy-0.7.4.1/sympy/logic/benchmarks/run-solvers.py0000644000175000017500000000163312253362407022513 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.logic.utilities import load_file from sympy.logic import satisfiable import time import os import sys input_path = os.getcwd() + '/' + '/'.join(sys.argv[0].split('/')[:-1]) INPUT = [5 * i for i in range(2, 16)] ALGORITHMS = ['dpll', 'dpll2'] results = {} for test in INPUT: results[test] = {} for test in INPUT: for alg in ALGORITHMS: file_name = "%s/input/%d.cnf" % (input_path, test) theory = load_file(file_name) start = time.time() assert satisfiable(theory, algorithm=alg) end = time.time() results[test][alg] = end - start print("Test %d in time %.2f seconds for algorithm %s." % (test, end - start, alg)) print("problem," + ','.join(ALGORITHMS)) for test in INPUT: line = "%d" % test for alg in ALGORITHMS: line += ",%f" % results[test][alg] print(line) sympy-0.7.4.1/sympy/logic/boolalg.py0000644000175000017500000011271512253362407017522 0ustar georgeskgeorgesk""" Boolean algebra module for SymPy """ from __future__ import print_function, division from collections import defaultdict from itertools import product from sympy.core.basic import Basic from sympy.core.cache import cacheit from sympy.core.numbers import Number from sympy.core.decorators import deprecated from sympy.core.operations import LatticeOp, AssocOp from sympy.core.function import Application from sympy.core.compatibility import ordered, xrange, with_metaclass from sympy.core.sympify import converter, _sympify, sympify from sympy.core.singleton import Singleton, S 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) __rand__ = __and__ def __or__(self, other): """Overloading for |""" return Or(self, other) __ror__ = __or__ 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) __rrshift__ = __lshift__ __rlshift__ = __rshift__ def __xor__(self, other): return Xor(self, other) __rxor__ = __xor__ # Developer note: There is liable to be some confusion as to when True should # be used and when S.true should be used in various contexts throughout SymPy. # An important thing to remember is that sympify(True) returns S.true. This # means that for the most part, you can just use True and it will # automatically be converted to S.true when necessary, similar to how you can # generally use 1 instead of S.One. # The rule of thumb is: # "If the boolean in question can be replaced by an arbitrary symbolic # Boolean, like Or(x, y) or x > 1, use S.true. Otherwise, use True" # In other words, use S.true only on those contexts where the boolean is being # used as a symbolic representation of truth. For example, if the object ends # up in the .args of any expression, then it must necessarily be S.true # instead of True, as elements of .args must be Basic. On the other hand, == # is not a symbolic operation in SymPy, since it always returns True or False, # and does so in terms of structural equality rather than mathematical, so it # should return True. The assumptions system should use True and False. Aside # from not satisfying the above rule of thumb, the assumptions system uses a # three-valued logic (True, False, None), whereas S.true and S.false represent # a two-valued logic. When it doubt, use True. # 2. "S.true == True" is True. # While "S.true is True" is False, "S.true == True" is True, so if there is # any doubt over whether a function or expression will return S.true or True, # just use "==" instead of "is" to do the comparison, and it will work in # either case. Finally, for boolean flags, it's better to just use "if x" # instead of "if x is True". To quote PEP 8: # Don't compare boolean values to True or False using ==. # Yes: if greeting: # No: if greeting == True: # Worse: if greeting is True: class BooleanAtom(Boolean): """ Base class of BooleanTrue and BooleanFalse. """ class BooleanTrue(with_metaclass(Singleton, BooleanAtom)): """ SymPy version of True. The instances of this class are singletonized and can be accessed via S.true. This is the SymPy version of True, for use in the logic module. The primary advantage of using true instead of True is that shorthand boolean operations like ~ and >> will work as expected on this class, whereas with True they act bitwise on 1. Functions in the logic module will return this class when they evaluate to true. Examples ======== >>> from sympy import sympify, true, Or >>> sympify(True) True >>> ~true False >>> ~True -2 >>> Or(True, False) True See Also ======== sympy.logic.boolalg.BooleanFalse """ def __nonzero__(self): return True __bool__ = __nonzero__ def __hash__(self): return hash(True) class BooleanFalse(with_metaclass(Singleton, BooleanAtom)): """ SymPy version of False. The instances of this class are singletonized and can be accessed via S.false. This is the SymPy version of False, for use in the logic module. The primary advantage of using false instead of False is that shorthand boolean operations like ~ and >> will work as expected on this class, whereas with False they act bitwise on 0. Functions in the logic module will return this class when they evaluate to false. Examples ======== >>> from sympy import sympify, false, Or, true >>> sympify(False) False >>> false >> false True >>> False >> False 0 >>> Or(True, False) True See Also ======== sympy.logic.boolalg.BooleanTrue """ def __nonzero__(self): return False __bool__ = __nonzero__ def __hash__(self): return hash(False) true = BooleanTrue() false = BooleanFalse() # We want S.true and S.false to work, rather than S.BooleanTrue and # S.BooleanFalse, but making the class and instance names the same causes some # major issues (like the inability to import the class directly from this # file). S.true = true S.false = false converter[bool] = lambda x: S.true if x else S.false 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) 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 >>> from sympy.logic.boolalg import And >>> x & y And(x, y) Notes ===== The ``&`` operator is provided as a convenience, but note that its use here is different from its normal use in Python, which is bitwise and. Hence, ``And(a, b)`` and ``a & b`` will return different things if ``a`` and ``b`` are integers. >>> And(x, y).subs(x, 1) y """ zero = false identity = true @classmethod def _new_args_filter(cls, args): newargs = [] for x in args: if isinstance(x, Number) or x in (0, 1): newargs.append(True if x else False) else: newargs.append(x) return LatticeOp._new_args_filter(newargs, And) 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. Examples ======== >>> from sympy.core import symbols >>> from sympy.abc import x, y >>> from sympy.logic.boolalg import Or >>> x | y Or(x, y) Notes ===== The ``|`` operator is provided as a convenience, but note that its use here is different from its normal use in Python, which is bitwise or. Hence, ``Or(a, b)`` and ``a | b`` will return different things if ``a`` and ``b`` are integers. >>> Or(x, y).subs(x, 0) y """ zero = true identity = false @classmethod def _new_args_filter(cls, args): newargs = [] for x in args: if isinstance(x, Number) or x in (0, 1): newargs.append(True if x else False) else: newargs.append(x) return LatticeOp._new_args_filter(newargs, Or) class Not(BooleanFunction): """ 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 >>> Not(And(And(True, x), Or(x, False))) Not(x) >>> ~x Not(x) Notes ===== - De Morgan rules are applied automatically. - The ``~`` operator is provided as a convenience, but note that its use here is different from its normal use in Python, which is bitwise not. In particular, ``~a`` and ``Not(a)`` will be different if ``a`` is an integer. Furthermore, since bools in Python subclass from ``int``, ``~True`` is the same as ``~1`` which is ``-2``, which has a boolean value of True. To avoid this issue, use the SymPy boolean types ``true`` and ``false``. >>> from sympy import true >>> ~True -2 >>> ~true False """ is_Not = True @classmethod def eval(cls, arg): if isinstance(arg, Number) or arg in (True, False): return false if arg else true # 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] class Xor(BooleanFunction): """ 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 >>> from sympy import symbols >>> x, y = symbols('x y') >>> Xor(True, False) True >>> Xor(True, True) False >>> Xor(True, False, True, True, False) True >>> Xor(True, False, True, False) False >>> x ^ y Or(And(Not(x), y), And(Not(y), x)) Notes ===== The ``^`` operator is provided as a convenience, but note that its use here is different from its normal use in Python, which is bitwise xor. In particular, ``a ^ b`` and ``Xor(a, b)`` will be different if ``a`` and ``b`` are integers. >>> Xor(x, y).subs(y, 0) x """ def __new__(cls, *args, **options): args = [_sympify(arg) for arg in args] argset = set(args) truecount = 0 for x in args: if isinstance(x, Number) or x in [True, False]: # Includes 0, 1 argset.discard(x) if x: truecount += 1 if len(argset) < 1: return true if truecount % 2 != 0 else false if truecount % 2 != 0: return Not(Xor(*argset)) _args = frozenset(argset) obj = super(Xor, cls).__new__(cls, *_args, **options) if isinstance(obj, Xor): obj._argset = _args return obj @property @cacheit def args(self): return tuple(ordered(self._argset)) @classmethod def eval(cls, *args): 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 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. Returns True if any of the arguments are False Returns False if all arguments are True Examples ======== >>> from sympy.logic.boolalg import Nand >>> from sympy import symbols >>> x, y = symbols('x y') >>> Nand(False, True) True >>> Nand(True, True) False >>> Nand(x, y) Or(Not(x), Not(y)) """ @classmethod def eval(cls, *args): return Not(And(*args)) 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. Returns False if any argument is True Returns True if all arguments are False Examples ======== >>> from sympy.logic.boolalg import Nor >>> from sympy import symbols >>> x, y = symbols('x y') >>> Nor(True, False) False >>> Nor(True, True) False >>> Nor(False, True) False >>> Nor(False, False) True >>> Nor(x, y) And(Not(x), Not(y)) """ @classmethod def eval(cls, *args): return Not(Or(*args)) class Implies(BooleanFunction): """ Logical implication. A implies B is equivalent to !A v B 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 >>> from sympy import symbols >>> x, y = symbols('x y') >>> Implies(True, False) False >>> Implies(False, False) True >>> Implies(True, True) True >>> Implies(False, True) True >>> x >> y Implies(x, y) >>> y << x Implies(x, y) Notes ===== The ``>>`` and ``<<`` operators are provided as a convenience, but note that their use here is different from their normal use in Python, which is bit shifts. Hence, ``Implies(a, b)`` and ``a >> b`` will return different things if ``a`` and ``b`` are integers. In particular, since Python considers ``True`` and ``False`` to be integers, ``True >> True`` will be the same as ``1 >> 1``, i.e., 0, which has a truth value of False. To avoid this issue, use the SymPy objects ``true`` and ``false``. >>> from sympy import true, false >>> True >> False 1 >>> true >> false False """ @classmethod def eval(cls, *args): try: newargs = [] for x in args: if isinstance(x, Number) or x in (0, 1): newargs.append(True if x else False) else: newargs.append(x) A, B = newargs except ValueError: raise ValueError( "%d operand(s) used for an Implies " "(pairs are required): %s" % (len(args), str(args))) if A == True or A == False or B == True or B == False: return Or(Not(A), B) else: return Basic.__new__(cls, *args) class Equivalent(BooleanFunction): """ Equivalence relation. Equivalent(A, B) is True iff A and B are both True or both False 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, y >>> Equivalent(False, False, False) True >>> Equivalent(True, False, False) False >>> Equivalent(x, And(x, True)) True """ def __new__(cls, *args, **options): args = [_sympify(arg) for arg in args] argset = set(args) for x in args: if isinstance(x, Number) or x in [True, False]: # Includes 0, 1 argset.discard(x) argset.add(True if x else False) 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) _args = frozenset(argset) obj = super(Equivalent, cls).__new__(cls, _args) obj._argset = _args return obj @property @cacheit def args(self): return tuple(ordered(self._argset)) class ITE(BooleanFunction): """ 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 >>> ITE(True, False, True) False >>> ITE(Or(True, False), And(True, True), Xor(True, True)) True >>> ITE(x, y, z) Or(And(Not(x), z), And(x, y)) """ @classmethod def eval(cls, *args): 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))) """ return _distribute((expr, And, Or)) def distribute_or_over_and(expr): """ Given a sentence s consisting of conjunctions and disjunctions of literals, return an equivalent sentence in DNF. Note that the output is NOT simplified. Examples ======== >>> from sympy.logic.boolalg import distribute_or_over_and, And, Or, Not >>> from sympy.abc import A, B, C >>> distribute_or_over_and(And(Or(Not(A), B), C)) Or(And(B, C), And(C, Not(A))) """ return _distribute((expr, Or, And)) def _distribute(info): """ Distributes info[1] over info[2] with respect to info[0]. """ if info[0].func is info[2]: for arg in info[0].args: if arg.func is info[1]: conj = arg break else: return info[0] rest = info[2](*[a for a in info[0].args if a is not conj]) return info[1](*list(map(_distribute, [(info[2](c, rest), info[1], info[2]) for c in conj.args]))) elif info[0].func is info[1]: return info[1](*list(map(_distribute, [(x, info[1], info[2]) for x in info[0].args]))) else: return info[0] def to_cnf(expr, simplify=False): """ Convert a propositional logical sentence s to conjunctive normal form. That is, of the form ((A | ~B | ...) & (B | C | ...) & ...) If simplify is True, the expr is evaluated to its simplest CNF form. 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))) >>> to_cnf((A | B) & (A | ~A), True) Or(A, B) """ expr = sympify(expr) if not isinstance(expr, BooleanFunction): return expr if simplify: return simplify_logic(expr, 'cnf', True) # Don't convert unless we have to if is_cnf(expr): return expr expr = eliminate_implications(expr) return distribute_and_over_or(expr) def to_dnf(expr, simplify=False): """ Convert a propositional logical sentence s to disjunctive normal form. That is, of the form ((A & ~B & ...) | (B & C & ...) | ...) If simplify is True, the expr is evaluated to its simplest DNF form. Examples ======== >>> from sympy.logic.boolalg import to_dnf >>> from sympy.abc import A, B, C >>> to_dnf(B & (A | C)) Or(And(A, B), And(B, C)) >>> to_dnf((A & B) | (A & ~B) | (B & C) | (~B & C), True) Or(A, C) """ expr = sympify(expr) if not isinstance(expr, BooleanFunction): return expr if simplify: return simplify_logic(expr, 'dnf', True) # Don't convert unless we have to if is_dnf(expr): return expr expr = eliminate_implications(expr) return distribute_or_over_and(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 """ return _is_form(expr, And, Or) def is_dnf(expr): """ Test whether or not an expression is in disjunctive normal form. Examples ======== >>> from sympy.logic.boolalg import is_dnf >>> from sympy.abc import A, B, C >>> is_dnf(A | B | C) True >>> is_dnf(A & B & C) True >>> is_dnf((A & B) | C) True >>> is_dnf(A & (B | C)) False """ return _is_form(expr, Or, And) def _is_form(expr, function1, function2): """ Test whether or not an expression is of the required form. """ expr = sympify(expr) # Special case of an Atom if expr.is_Atom: return True # Special case of a single expression of function2 if expr.func is function2: 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 function1: 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 function2: 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=3451, 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 function is deprecated. Use sympify() instead. """ 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(xrange(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(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 = [sympify(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 = [sympify(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 _find_predicates(expr): """Helper to find logical predicates in BooleanFunctions. A logical predicate is defined here as anything within a BooleanFunction that is not a BooleanFunction itself. """ if not isinstance(expr, BooleanFunction): return set([expr]) return set.union(*(_find_predicates(i) for i in expr.args)) def simplify_logic(expr, form=None, deep=True): """ 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. form can be 'cnf' or 'dnf' or None. If its 'cnf' or 'dnf' the simplest expression in the corresponding normal form is returned. If form is None, the answer is returned according to the form with lesser number of args (CNF by default) The optional parameter deep indicates whether to recursively simplify any non-boolean-functions contained within the input. 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)) """ if form == 'cnf' or form == 'dnf' or form is None: expr = sympify(expr) if not isinstance(expr, BooleanFunction): return expr variables = _find_predicates(expr) truthtable = [] for t in product([0, 1], repeat=len(variables)): t = list(t) if expr.xreplace(dict(zip(variables, t))) == True: truthtable.append(t) if deep: from sympy.simplify.simplify import simplify variables = [simplify(v) for v in variables] if form == 'dnf' or \ (form is None and len(truthtable) >= (2 ** (len(variables) - 1))): return SOPform(variables, truthtable) elif form == 'cnf' or form is None: return POSform(variables, truthtable) else: raise ValueError("form can be cnf or dnf only") 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_map(bool1, bool2): """ Return the simplified version of bool1, and the mapping of variables that makes the two expressions bool1 and bool2 represent the same logical behaviour for some correspondence between the variables of each. If more than one mappings of this sort exist, one of them is returned. For example, And(x, y) is logically equivalent to And(a, b) for the mapping {x: a, y:b} or {x: b, y:a}. If no such mapping exists, return False. Examples ======== >>> from sympy import SOPform, bool_map, 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_map(function1, function2) (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_map(eq, eq2) (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_map(eq, eq.subs(c, x)) (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 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: return a, m return m is not None @deprecated( useinstead="bool_map", issue=4098, deprecated_since_version="0.7.4") def bool_equal(bool1, bool2, info=False): """Return True if the two expressions represent the same logical behaviour 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 mapping of variables will be returned. """ mapping = bool_map(bool1, bool2) if not mapping: return False if info: return mapping return True sympy-0.7.4.1/sympy/logic/inference.py0000644000175000017500000001402712253362407020036 0ustar georgeskgeorgesk"""Inference in propositional logic""" from __future__ import print_function, division 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 is_literal(expr): """ Returns True if expr is a literal, else False. Examples ======== >>> from sympy import Symbol, Or >>> from sympy.abc import A, B >>> from sympy.logic.inference import is_literal >>> is_literal(A) True >>> is_literal(~A) True >>> is_literal(Or(A, B)) False """ try: literal_symbol(expr) return True except (ValueError): return False 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 is True or literal is False: return literal try: if literal.is_Symbol: return literal if literal.is_Not: return literal_symbol(literal.args[0]) else: raise ValueError except (AttributeError, ValueError): raise ValueError("Argument must be a boolean literal.") 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 """ if expr is True: return {} if expr is False: return 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) sympy-0.7.4.1/sympy/logic/utilities/0000755000175000017500000000000012253362407017535 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/logic/utilities/dimacs.py0000644000175000017500000000327212253362407021353 0ustar georgeskgeorgesk"""For reading in DIMACS file format www.cs.ubc.ca/~hoos/SATLIB/Benchmarks/SAT/satformat.ps """ from __future__ import print_function, division from sympy.core import Symbol from sympy.logic.boolalg import And, Or import re def load(s): """Loads a boolean expression from a string. Examples ======== >>> from sympy.logic.utilities.dimacs import load >>> load('1') cnf_1 >>> load('1 2') Or(cnf_1, cnf_2) >>> load('1 \\n 2') And(cnf_1, cnf_2) >>> load('1 2 \\n 3') And(Or(cnf_1, cnf_2), cnf_3) """ clauses = [] lines = s.split('\n') pComment = re.compile('c.*') pStats = re.compile('p\s*cnf\s*(\d*)\s*(\d*)') while len(lines) > 0: line = lines.pop(0) # Only deal with lines that aren't comments if not pComment.match(line): m = pStats.match(line) if not m: nums = line.rstrip('\n').split(' ') list = [] for lit in nums: if lit != '': if int(lit) == 0: continue num = abs(int(lit)) sign = True if int(lit) < 0: sign = False if sign: list.append(Symbol("cnf_%s" % num)) else: list.append(~Symbol("cnf_%s" % num)) if len(list) > 0: clauses.append(Or(*list)) return And(*clauses) def load_file(location): """Loads a boolean expression from a file.""" with open(location) as f: s = f.read() return load(s) sympy-0.7.4.1/sympy/logic/utilities/__init__.py0000644000175000017500000000003612253362407021645 0ustar georgeskgeorgeskfrom .dimacs import load_file sympy-0.7.4.1/sympy/logic/tests/0000755000175000017500000000000012253362407016664 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/logic/tests/test_boolalg.py0000644000175000017500000004100712253362407021716 0ustar georgeskgeorgeskfrom sympy import symbols, sympify, Dummy, simplify, Equality, S from sympy.logic.boolalg import ( And, Boolean, Equivalent, ITE, Implies, Nand, Nor, Not, Or, POSform, SOPform, Xor, conjuncts, disjuncts, distribute_or_over_and, distribute_and_over_or, eliminate_implications, is_cnf, is_dnf, simplify_logic, to_cnf, to_dnf, to_int_repr, bool_map, true, false, BooleanAtom ) from sympy.utilities.pytest import raises from sympy.utilities import cartes A, B, C = symbols('A,B,C') def test_overloading(): """Test that |, & are overloaded as expected""" assert A & B == And(A, B) assert A | B == Or(A, B) assert (A & B) | C == Or(And(A, B), C) assert A >> B == Implies(A, B) assert A << B == Implies(B, A) assert ~A == Not(A) assert A ^ B == Xor(A, B) def test_And(): assert And() is true assert And(A) == A assert And(True) is true assert And(False) is false assert And(True, True ) is true assert And(True, False) is false assert And(False, False) is false assert And(True, A) == A assert And(False, A) is false assert And(True, True, True) is true assert And(True, True, A) == A assert And(True, False, A) is false assert And(2, A) == A assert And(2, 3) is true def test_Or(): assert Or() is false assert Or(A) == A assert Or(True) is true assert Or(False) is false assert Or(True, True ) is true assert Or(True, False) is true assert Or(False, False) is false assert Or(True, A) is true assert Or(False, A) == A assert Or(True, False, False) is true assert Or(True, False, A) is true assert Or(False, False, A) == A assert Or(2, A) is true def test_Xor(): assert Xor() is false assert Xor(A) == A assert Xor(True) is true assert Xor(False) is false assert Xor(True, True ) is false assert Xor(True, False) is true assert Xor(False, False) is false assert Xor(True, A) == ~A assert Xor(False, A) == A assert Xor(True, False, False) is true assert Xor(True, False, A) == ~A assert Xor(False, False, A) == A def test_Not(): raises(TypeError, lambda: Not(True, False)) assert Not(True) is false assert Not(False) is true assert Not(0) is true assert Not(1) is false assert Not(2) is false def test_Nand(): assert Nand() is false assert Nand(A) == ~A assert Nand(True) is false assert Nand(False) is true assert Nand(True, True ) is false assert Nand(True, False) is true assert Nand(False, False) is true assert Nand(True, A) == ~A assert Nand(False, A) is true assert Nand(True, True, True) is false assert Nand(True, True, A) == ~A assert Nand(True, False, A) is true def test_Nor(): assert Nor() is true assert Nor(A) == ~A assert Nor(True) is false assert Nor(False) is true assert Nor(True, True ) is false assert Nor(True, False) is false assert Nor(False, False) is true assert Nor(True, A) is false assert Nor(False, A) == ~A assert Nor(True, True, True) is false assert Nor(True, True, A) is false assert Nor(True, False, A) is false def test_Implies(): raises(ValueError, lambda: Implies(A, B, C)) assert Implies(True, True) is true assert Implies(True, False) is false assert Implies(False, True) is true assert Implies(False, False) is true assert Implies(0, A) is true assert Implies(1, 1) is true assert Implies(1, 0) is false assert A >> B == B << A def test_Equivalent(): assert Equivalent(A, B) == Equivalent(B, A) == Equivalent(A, B, A) assert Equivalent() is true assert Equivalent(A, A) == Equivalent(A) is true assert Equivalent(True, True) == Equivalent(False, False) is true assert Equivalent(True, False) == Equivalent(False, True) is false assert Equivalent(A, True) == A assert Equivalent(A, False) == Not(A) assert Equivalent(A, B, True) == A & B assert Equivalent(A, B, False) == ~A & ~B assert Equivalent(1, A) == A assert Equivalent(0, A) == Not(A) assert Equivalent(A, Equivalent(B, C)) != Equivalent(Equivalent(A, B), C) def test_simplification(): """ Test working of simplification methods. """ set1 = [[0, 0, 1], [0, 1, 1], [1, 0, 0], [1, 1, 0]] set2 = [[0, 0, 0], [0, 1, 0], [1, 0, 1], [1, 1, 1]] from sympy.abc import w, x, y, z assert SOPform('xyz', set1) == Or(And(Not(x), z), And(Not(z), x)) assert Not(SOPform('xyz', set2)) == And(Or(Not(x), Not(z)), Or(x, z)) assert POSform('xyz', set1 + set2) is true assert SOPform('xyz', set1 + set2) is true assert SOPform([Dummy(), Dummy(), Dummy()], set1 + set2) is true 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]] assert ( SOPform('wxyz', minterms, dontcares) == Or(And(Not(w), z), And(y, z))) assert POSform('wxyz', minterms, dontcares) == And(Or(Not(w), y), z) # test simplification ans = And(A, Or(B, C)) assert simplify_logic('A & (B | C)') == ans assert simplify_logic('(A & B) | (A & C)') == ans assert simplify_logic(Implies(A, B)) == Or(Not(A), B) assert simplify_logic(Equivalent(A, B)) == \ Or(And(A, B), And(Not(A), Not(B))) assert simplify_logic(And(Equality(A, 2), C)) == And(Equality(A, 2), C) assert simplify_logic(And(Equality(A, 2), A)) == And(Equality(A, 2), A) assert simplify_logic(And(Equality(A, B), C)) == And(Equality(A, B), C) assert simplify_logic(Or(And(Equality(A, 3), B), And(Equality(A, 3), C))) \ == And(Equality(A, 3), Or(B, C)) assert simplify_logic(And(A, x**2-x)) == And(A, x*(x-1)) assert simplify_logic(And(A, x**2-x), deep=False) == And(A, x**2-x) # check input ans = SOPform('xy', [[1, 0]]) assert SOPform([x, y], [[1, 0]]) == ans assert POSform(['x', 'y'], [[1, 0]]) == ans raises(ValueError, lambda: SOPform('x', [[1]], [[1]])) assert SOPform('x', [[1]], [[0]]) is true assert SOPform('x', [[0]], [[1]]) is true assert SOPform('x', [], []) is false raises(ValueError, lambda: POSform('x', [[1]], [[1]])) assert POSform('x', [[1]], [[0]]) is true assert POSform('x', [[0]], [[1]]) is true assert POSform('x', [], []) is false #check working of simplify assert simplify('(A & B) | (A & C)') == sympify('And(A, Or(B, C))') assert simplify(And(x, Not(x))) == False assert simplify(Or(x, Not(x))) == True def test_bool_map(): """ Test working of bool_map function. """ minterms = [[0, 0, 0, 1], [0, 0, 1, 1], [0, 1, 1, 1], [1, 0, 1, 1], [1, 1, 1, 1]] from sympy.abc import a, b, c, w, x, y, z assert bool_map(Not(Not(a)), a) == (a, {a: a}) assert bool_map(SOPform(['w', 'x', 'y', 'z'], minterms), POSform(['w', 'x', 'y', 'z'], minterms)) == \ (And(Or(Not(w), y), Or(Not(x), y), z), {x: x, w: w, z: z, y: y}) assert bool_map(SOPform(['x', 'z', 'y'],[[1, 0, 1]]), SOPform(['a', 'b', 'c'],[[1, 0, 1]])) != False function1 = SOPform(['x','z','y'],[[1, 0, 1], [0, 0, 1]]) function2 = SOPform(['a','b','c'],[[1, 0, 1], [1, 0, 0]]) assert bool_map(function1, function2) == \ (function1, {y: a, z: b}) def test_bool_symbol(): """Test that mixing symbols with boolean values works as expected""" assert And(A, True) == A assert And(A, True, True) == A assert And(A, False) is false assert And(A, True, False) is false assert Or(A, True) is true assert Or(A, False) == A def test_subs(): assert (A & B).subs(A, True) == B assert (A & B).subs(A, False) is false assert (A & B).subs(B, True) == A assert (A & B).subs(B, False) is false assert (A & B).subs({A: True, B: True}) is true assert (A | B).subs(A, True) is true assert (A | B).subs(A, False) == B assert (A | B).subs(B, True) is true assert (A | B).subs(B, False) == A assert (A | B).subs({A: True, B: True}) is true """ we test for axioms of boolean algebra see http://en.wikipedia.org/wiki/Boolean_algebra_(structure) """ def test_commutative(): """Test for commutativity of And and Or""" A, B = map(Boolean, symbols('A,B')) assert A & B == B & A assert A | B == B | A def test_and_associativity(): """Test for associativity of And""" assert (A & B) & C == A & (B & C) def test_or_assicativity(): assert ((A | B) | C) == (A | (B | C)) def test_double_negation(): a = Boolean() assert ~(~a) == a def test_De_Morgan(): assert ~(A & B) == (~A) | (~B) assert ~(A | B) == (~A) & (~B) assert ~(A | B | C) == ~A & ~B & ~C # test methods def test_eliminate_implications(): assert eliminate_implications(Implies(A, B, evaluate=False)) == (~A) | B assert eliminate_implications( A >> (C >> Not(B))) == Or(Or(Not(B), Not(C)), Not(A)) def test_conjuncts(): assert conjuncts(A & B & C) == set([A, B, C]) assert conjuncts((A | B) & C) == set([A | B, C]) assert conjuncts(A) == set([A]) assert conjuncts(True) == set([True]) assert conjuncts(False) == set([False]) def test_disjuncts(): assert disjuncts(A | B | C) == set([A, B, C]) assert disjuncts((A | B) & C) == set([(A | B) & C]) assert disjuncts(A) == set([A]) assert disjuncts(True) == set([True]) assert disjuncts(False) == set([False]) def test_distribute(): assert distribute_and_over_or(Or(And(A, B), C)) == And(Or(A, C), Or(B, C)) assert distribute_or_over_and(And(A, Or(B, C))) == Or(And(A, B), And(A, C)) def test_to_cnf(): assert to_cnf(~(B | C)) == And(Not(B), Not(C)) assert to_cnf((A & B) | C) == And(Or(A, C), Or(B, C)) assert to_cnf(A >> B) == (~A) | B assert to_cnf(A >> (B & C)) == (~A | B) & (~A | C) assert to_cnf(A & (B | C) | ~A & (B | C), True) == B | C assert to_cnf(Equivalent(A, B)) == And(Or(A, Not(B)), Or(B, Not(A))) assert to_cnf(Equivalent(A, B & C)) == \ (~A | B) & (~A | C) & (~B | ~C | A) assert to_cnf(Equivalent(A, B | C), True) == \ And(Or(Not(B), A), Or(Not(C), A), Or(B, C, Not(A))) def test_to_dnf(): assert to_dnf(~(B | C)) == And(Not(B), Not(C)) assert to_dnf(A & (B | C)) == Or(And(A, B), And(A, C)) assert to_dnf(A >> B) == (~A) | B assert to_dnf(A >> (B & C)) == (~A) | (B & C) assert to_dnf(Equivalent(A, B), True) == \ Or(And(A, B), And(Not(A), Not(B))) assert to_dnf(Equivalent(A, B & C), True) == \ Or(And(A, B, C), And(Not(A), Not(B)), And(Not(A), Not(C))) def test_to_int_repr(): x, y, z = map(Boolean, symbols('x,y,z')) def sorted_recursive(arg): try: return sorted(sorted_recursive(x) for x in arg) except TypeError: # arg is not a sequence return arg assert sorted_recursive(to_int_repr([x | y, z | x], [x, y, z])) == \ sorted_recursive([[1, 2], [1, 3]]) assert sorted_recursive(to_int_repr([x | y, z | ~x], [x, y, z])) == \ sorted_recursive([[1, 2], [3, -1]]) def test_is_cnf(): x, y, z = symbols('x,y,z') assert is_cnf(x) is True assert is_cnf(x | y | z) is True assert is_cnf(x & y & z) is True assert is_cnf((x | y) & z) is True assert is_cnf((x & y) | z) is False def test_is_dnf(): x, y, z = symbols('x,y,z') assert is_dnf(x) is True assert is_dnf(x | y | z) is True assert is_dnf(x & y & z) is True assert is_dnf((x & y) | z) is True assert is_dnf((x | y) & z) is False def test_ITE(): A, B, C = map(Boolean, symbols('A,B,C')) assert ITE(True, False, True) is false assert ITE(True, True, False) is true assert ITE(False, True, False) is false assert ITE(False, False, True) is true A = True assert ITE(A, B, C) == B A = False assert ITE(A, B, C) == C B = True assert ITE(And(A, B), B, C) == C assert ITE(Or(A, False), And(B, True), False) is false def test_operators(): # Mostly test __and__, __rand__, and so on assert True & A == A & True == A assert False & A == A & False == False assert A & B == And(A, B) assert True | A == A | True == True assert False | A == A | False == A assert A | B == Or(A, B) assert ~A == Not(A) assert True >> A == A << True == A assert False >> A == A << False == True assert A >> True == True << A == True assert A >> False == False << A == ~A assert A >> B == B << A == Implies(A, B) assert True ^ A == A ^ True == ~A assert False ^ A == A ^ False == A assert A ^ B == Xor(A, B) def test_true_false(): x = symbols('x') assert true is S.true assert false is S.false assert true is not True assert false is not False assert true assert not false assert true == True assert false == False assert not (true == False) assert not (false == True) assert not (true == false) assert hash(true) == hash(True) assert hash(false) == hash(False) assert len(set([true, True])) == len(set([false, False])) == 1 assert isinstance(true, BooleanAtom) assert isinstance(false, BooleanAtom) # We don't want to subclass from bool, because bool subclasses from # int. But operators like &, |, ^, <<, >>, and ~ act differently on 0 and # 1 then we want them to on true and false. See the docstrings of the # various And, Or, etc. functions for examples. assert not isinstance(true, bool) assert not isinstance(false, bool) # Note: using 'is' comparison is important here. We want these to return # true and false, not True and False assert Not(true) is false assert Not(True) is false assert Not(false) is true assert Not(False) is true assert ~true is false assert ~false is true for T, F in cartes([True, true], [False, false]): assert And(T, F) is false assert And(F, T) is false assert And(F, F) is false assert And(T, T) is true assert And(T, x) == x assert And(F, x) is false if not (T is True and F is False): assert T & F is false assert F & T is false if not F is False: assert F & F is false if not T is True: assert T & T is true assert Or(T, F) is true assert Or(F, T) is true assert Or(F, F) is false assert Or(T, T) is true assert Or(T, x) is true assert Or(F, x) == x if not (T is True and F is False): assert T | F is true assert F | T is true if not F is False: assert F | F is false if not T is True: assert T | T is true assert Xor(T, F) is true assert Xor(F, T) is true assert Xor(F, F) is false assert Xor(T, T) is false assert Xor(T, x) == ~x assert Xor(F, x) == x if not (T is True and F is False): assert T ^ F is true assert F ^ T is true if not F is False: assert F ^ F is false if not T is True: assert T ^ T is false assert Nand(T, F) is true assert Nand(F, T) is true assert Nand(F, F) is true assert Nand(T, T) is false assert Nand(T, x) == ~x assert Nand(F, x) is true assert Nor(T, F) is false assert Nor(F, T) is false assert Nor(F, F) is true assert Nor(T, T) is false assert Nor(T, x) is false assert Nor(F, x) == ~x assert Implies(T, F) is false assert Implies(F, T) is true assert Implies(F, F) is true assert Implies(T, T) is true assert Implies(T, x) == x assert Implies(F, x) is true assert Implies(x, T) is true assert Implies(x, F) == ~x if not (T is True and F is False): assert T >> F is false assert F << T is false assert F >> T is true assert T << F is true if not F is False: assert F >> F is true assert F << F is true if not T is True: assert T >> T is true assert T << T is true assert Equivalent(T, F) is false assert Equivalent(F, T) is false assert Equivalent(F, F) is true assert Equivalent(T, T) is true assert Equivalent(T, x) == x assert Equivalent(F, x) == ~x assert Equivalent(x, T) == x assert Equivalent(x, F) == ~x assert ITE(T, T, T) is true assert ITE(T, T, F) is true assert ITE(T, F, T) is false assert ITE(T, F, F) is false assert ITE(F, T, T) is true assert ITE(F, T, F) is false assert ITE(F, F, T) is true assert ITE(F, F, F) is false sympy-0.7.4.1/sympy/logic/tests/test_inference.py0000644000175000017500000001702412253362407022237 0ustar georgeskgeorgesk"""For more tests on satisfiability, see test_dimacs""" from sympy import symbols, Q from sympy.logic.boolalg import Or, Equivalent, Implies, And from sympy.logic.inference import is_literal, literal_symbol, \ pl_true, satisfiable, PropKB from sympy.logic.algorithms.dpll import dpll, dpll_satisfiable, \ find_pure_symbol, find_unit_clause, unit_propagate, \ find_pure_symbol_int_repr, find_unit_clause_int_repr, \ unit_propagate_int_repr from sympy.utilities.pytest import raises def test_literal(): A, B = symbols('A,B') assert is_literal(True) is True assert is_literal(False) is True assert is_literal(A) is True assert is_literal(~A) is True assert is_literal(Or(A, B)) is False assert literal_symbol(True) is True assert literal_symbol(False) is False assert literal_symbol(A) is A assert literal_symbol(~A) is A def test_find_pure_symbol(): A, B, C = symbols('A,B,C') assert find_pure_symbol([A], [A]) == (A, True) assert find_pure_symbol([A, B], [~A | B, ~B | A]) == (None, None) assert find_pure_symbol([A, B, C], [ A | ~B, ~B | ~C, C | A]) == (A, True) assert find_pure_symbol([A, B, C], [~A | B, B | ~C, C | A]) == (B, True) assert find_pure_symbol([A, B, C], [~A | ~B, ~B | ~C, C | A]) == (B, False) assert find_pure_symbol( [A, B, C], [~A | B, ~B | ~C, C | A]) == (None, None) def test_find_pure_symbol_int_repr(): assert find_pure_symbol_int_repr([1], [set([1])]) == (1, True) assert find_pure_symbol_int_repr([1, 2], [set([-1, 2]), set([-2, 1])]) == (None, None) assert find_pure_symbol_int_repr([1, 2, 3], [set([1, -2]), set([-2, -3]), set([3, 1])]) == (1, True) assert find_pure_symbol_int_repr([1, 2, 3], [set([-1, 2]), set([2, -3]), set([3, 1])]) == (2, True) assert find_pure_symbol_int_repr([1, 2, 3], [set([-1, -2]), set([-2, -3]), set([3, 1])]) == (2, False) assert find_pure_symbol_int_repr([1, 2, 3], [set([-1, 2]), set([-2, -3]), set([3, 1])]) == (None, None) def test_unit_clause(): A, B, C = symbols('A,B,C') assert find_unit_clause([A], {}) == (A, True) assert find_unit_clause([A, ~A], {}) == (A, True) # Wrong ?? assert find_unit_clause([A | B], {A: True}) == (B, True) assert find_unit_clause([A | B], {B: True}) == (A, True) assert find_unit_clause( [A | B | C, B | ~C, A | ~B], {A: True}) == (B, False) assert find_unit_clause([A | B | C, B | ~C, A | B], {A: True}) == (B, True) assert find_unit_clause([A | B | C, B | ~C, A ], {}) == (A, True) def test_unit_clause_int_repr(): assert find_unit_clause_int_repr(map(set, [[1]]), {}) == (1, True) assert find_unit_clause_int_repr(map(set, [[1], [-1]]), {}) == (1, True) assert find_unit_clause_int_repr([set([1, 2])], {1: True}) == (2, True) assert find_unit_clause_int_repr([set([1, 2])], {2: True}) == (1, True) assert find_unit_clause_int_repr(map(set, [[1, 2, 3], [2, -3], [1, -2]]), {1: True}) == (2, False) assert find_unit_clause_int_repr(map(set, [[1, 2, 3], [3, -3], [1, 2]]), {1: True}) == (2, True) A, B, C = symbols('A,B,C') assert find_unit_clause([A | B | C, B | ~C, A ], {}) == (A, True) def test_unit_propagate(): A, B, C = symbols('A,B,C') assert unit_propagate([A | B], A) == [] assert unit_propagate([A | B, ~A | C, ~C | B, A], A) == [C, ~C | B, A] def test_unit_propagate_int_repr(): assert unit_propagate_int_repr([set([1, 2])], 1) == [] assert unit_propagate_int_repr(map(set, [[1, 2], [-1, 3], [-3, 2], [1]]), 1) == [set([3]), set([-3, 2])] def test_dpll(): """This is also tested in test_dimacs""" A, B, C = symbols('A,B,C') assert dpll([A | B], [A, B], {A: True, B: True}) == {A: True, B: True} def test_dpll_satisfiable(): A, B, C = symbols('A,B,C') assert dpll_satisfiable( A & ~A ) is False assert dpll_satisfiable( A & ~B ) == {A: True, B: False} assert dpll_satisfiable( A | B ) in ({A: True}, {B: True}, {A: True, B: True}) assert dpll_satisfiable( (~A | B) & (~B | A) ) in ({A: True, B: True}, {A: False, B: False}) assert dpll_satisfiable( (A | B) & (~B | C) ) in ({A: True, B: False}, {A: True, C: True}, {B: True, C: True}) assert dpll_satisfiable( A & B & C ) == {A: True, B: True, C: True} assert dpll_satisfiable( (A | B) & (A >> B) ) == {B: True} assert dpll_satisfiable( Equivalent(A, B) & A ) == {A: True, B: True} assert dpll_satisfiable( Equivalent(A, B) & ~A ) == {A: False, B: False} def test_satisfiable(): A, B, C = symbols('A,B,C') assert satisfiable(A & (A >> B) & ~B) is False def test_pl_true(): A, B, C = symbols('A,B,C') assert pl_true(True) is True assert pl_true( A & B, {A: True, B: True}) is True assert pl_true( A | B, {A: True}) is True assert pl_true( A | B, {B: True}) is True assert pl_true( A | B, {A: None, B: True}) is True assert pl_true( A >> B, {A: False}) is True assert pl_true( A | B | ~C, {A: False, B: True, C: True}) is True assert pl_true(Equivalent(A, B), {A: False, B: False}) is True # test for false assert pl_true(False) is False assert pl_true( A & B, {A: False, B: False}) is False assert pl_true( A & B, {A: False}) is False assert pl_true( A & B, {B: False}) is False assert pl_true( A | B, {A: False, B: False}) is False #test for None assert pl_true(B, {B: None}) is None assert pl_true( A & B, {A: True, B: None}) is None assert pl_true( A >> B, {A: True, B: None}) is None assert pl_true(Equivalent(A, B), {A: None}) is None assert pl_true(Equivalent(A, B), {A: True, B: None}) is None def test_pl_true_wrong_input(): from sympy import pi raises(ValueError, lambda: pl_true('John Cleese')) raises(ValueError, lambda: pl_true(42 + pi + pi ** 2)) raises(ValueError, lambda: pl_true(42)) def test_PropKB(): A, B, C = symbols('A,B,C') kb = PropKB() kb.tell(A >> B) kb.tell(B >> C) assert kb.ask(A) is True assert kb.ask(B) is True assert kb.ask(C) is True assert kb.ask(~A) is True assert kb.ask(~B) is True assert kb.ask(~C) is True kb.tell(A) assert kb.ask(A) is True assert kb.ask(B) is True assert kb.ask(C) is True assert kb.ask(~C) is False kb.retract(A) assert kb.ask(~C) is True kb2 = PropKB(Equivalent(A, B)) assert kb2.ask(A) is True assert kb2.ask(B) is True kb2.tell(A) assert kb2.ask(A) is True kb3 = PropKB() kb3.tell(A) def test_propKB_tolerant(): """"tolerant to bad input""" kb = PropKB() A, B, C = symbols('A,B,C') assert kb.ask(B) is False def test_satisfiable_non_symbols(): x, y = symbols('x y') assumptions = Q.zero(x*y) facts = Implies(Q.zero(x*y), Q.zero(x) | Q.zero(y)) query = ~Q.zero(x) & ~Q.zero(y) refutations = [ {Q.zero(x): True, Q.zero(x*y): True}, {Q.zero(y): True, Q.zero(x*y): True}, {Q.zero(x): True, Q.zero(y): True, Q.zero(x*y): True}, {Q.zero(x): True, Q.zero(y): False, Q.zero(x*y): True}, {Q.zero(x): False, Q.zero(y): True, Q.zero(x*y): True}] assert not satisfiable(And(assumptions, facts, query), algorithm='dpll') assert satisfiable(And(assumptions, facts, ~query), algorithm='dpll') in refutations assert not satisfiable(And(assumptions, facts, query), algorithm='dpll2') assert satisfiable(And(assumptions, facts, ~query), algorithm='dpll2') in refutations def test_satisfiable_bool(): assert satisfiable(True) == {} assert satisfiable(False) == False sympy-0.7.4.1/sympy/logic/tests/test_dimacs.py0000644000175000017500000000740312253362407021541 0ustar georgeskgeorgesk"""Various tests on satisfiability using dimacs cnf file syntax You can find lots of cnf files in ftp://dimacs.rutgers.edu/pub/challenge/satisfiability/benchmarks/cnf/ """ from sympy.logic.utilities.dimacs import load from sympy.logic.algorithms.dpll import dpll_satisfiable from sympy.utilities.pytest import skip def test_f1(): assert bool(dpll_satisfiable(load(f1))) def test_f2(): assert bool(dpll_satisfiable(load(f2))) def test_f3(): assert bool(dpll_satisfiable(load(f3))) def test_f4(): assert not bool(dpll_satisfiable(load(f4))) def test_f5(): assert bool(dpll_satisfiable(load(f5))) f1 = """c simple example c Resolution: SATISFIABLE c p cnf 3 2 1 -3 0 2 3 -1 0 """ f2 = """c an example from Quinn's text, 16 variables and 18 clauses. c Resolution: SATISFIABLE c p cnf 16 18 1 2 0 -2 -4 0 3 4 0 -4 -5 0 5 -6 0 6 -7 0 6 7 0 7 -16 0 8 -9 0 -8 -14 0 9 10 0 9 -10 0 -10 -11 0 10 12 0 11 12 0 13 14 0 14 -15 0 15 16 0 """ f3 = """c p cnf 6 9 -1 0 -3 0 2 -1 0 2 -4 0 5 -4 0 -1 -3 0 -4 -6 0 1 3 -2 0 4 6 -2 -5 0 """ f4 = """c c c SOURCE: John Hooker (jh38+@andrew.cmu.edu) c c DESCRIPTION: Pigeon hole problem of placing n (for file holen) pigeons c in n+1 holes without placing 2 pigeons in the same hole c c NOTE: Part of the collection at the Forschungsinstitut fuer c anwendungsorientierte Wissensverarbeitung in Ulm Germany. c c NOTE: Not satisfiable c p cnf 42 133 -1 -7 0 -1 -13 0 -1 -19 0 -1 -25 0 -1 -31 0 -1 -37 0 -7 -13 0 -7 -19 0 -7 -25 0 -7 -31 0 -7 -37 0 -13 -19 0 -13 -25 0 -13 -31 0 -13 -37 0 -19 -25 0 -19 -31 0 -19 -37 0 -25 -31 0 -25 -37 0 -31 -37 0 -2 -8 0 -2 -14 0 -2 -20 0 -2 -26 0 -2 -32 0 -2 -38 0 -8 -14 0 -8 -20 0 -8 -26 0 -8 -32 0 -8 -38 0 -14 -20 0 -14 -26 0 -14 -32 0 -14 -38 0 -20 -26 0 -20 -32 0 -20 -38 0 -26 -32 0 -26 -38 0 -32 -38 0 -3 -9 0 -3 -15 0 -3 -21 0 -3 -27 0 -3 -33 0 -3 -39 0 -9 -15 0 -9 -21 0 -9 -27 0 -9 -33 0 -9 -39 0 -15 -21 0 -15 -27 0 -15 -33 0 -15 -39 0 -21 -27 0 -21 -33 0 -21 -39 0 -27 -33 0 -27 -39 0 -33 -39 0 -4 -10 0 -4 -16 0 -4 -22 0 -4 -28 0 -4 -34 0 -4 -40 0 -10 -16 0 -10 -22 0 -10 -28 0 -10 -34 0 -10 -40 0 -16 -22 0 -16 -28 0 -16 -34 0 -16 -40 0 -22 -28 0 -22 -34 0 -22 -40 0 -28 -34 0 -28 -40 0 -34 -40 0 -5 -11 0 -5 -17 0 -5 -23 0 -5 -29 0 -5 -35 0 -5 -41 0 -11 -17 0 -11 -23 0 -11 -29 0 -11 -35 0 -11 -41 0 -17 -23 0 -17 -29 0 -17 -35 0 -17 -41 0 -23 -29 0 -23 -35 0 -23 -41 0 -29 -35 0 -29 -41 0 -35 -41 0 -6 -12 0 -6 -18 0 -6 -24 0 -6 -30 0 -6 -36 0 -6 -42 0 -12 -18 0 -12 -24 0 -12 -30 0 -12 -36 0 -12 -42 0 -18 -24 0 -18 -30 0 -18 -36 0 -18 -42 0 -24 -30 0 -24 -36 0 -24 -42 0 -30 -36 0 -30 -42 0 -36 -42 0 6 5 4 3 2 1 0 12 11 10 9 8 7 0 18 17 16 15 14 13 0 24 23 22 21 20 19 0 30 29 28 27 26 25 0 36 35 34 33 32 31 0 42 41 40 39 38 37 0 """ f5 = """c simple example requiring variable selection c c NOTE: Satisfiable c p cnf 5 5 1 2 3 0 1 -2 3 0 4 5 -3 0 1 -4 -3 0 -1 -5 0 """ sympy-0.7.4.1/sympy/logic/tests/__init__.py0000644000175000017500000000000012253362407020763 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/logic/__init__.py0000644000175000017500000000031612253362407017633 0ustar georgeskgeorgeskfrom .boolalg import (to_cnf, to_dnf, And, Or, Not, Xor, Nand, Nor, Implies, Equivalent, ITE, POSform, SOPform, simplify_logic, bool_equal, bool_map, true, false) from .inference import satisfiable sympy-0.7.4.1/sympy/diffgeom/0000755000175000017500000000000012253362407016205 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/diffgeom/diffgeom.py0000644000175000017500000015344212253362407020350 0ustar georgeskgeorgeskfrom __future__ import print_function, division from itertools import permutations from sympy.matrices import Matrix from sympy.core import Basic, Expr, Dummy, Function, sympify, diff, Pow, Mul, Add from sympy.core.numbers import Zero from sympy.solvers import solve from sympy.functions import factorial from sympy.simplify import simplify from sympy.core.compatibility import reduce from sympy.combinatorics import Permutation # TODO you are a bit excessive in the use of Dummies # TODO dummy point, literal field # TODO too often one needs to call doit or simplify on the output, check the # tests and find out why class Manifold(Basic): """Object representing a mathematical manifold. The only role that this object plays is to keep a list of all patches defined on the manifold. It does not provide any means to study the topological characteristics of the manifold that it represents. """ def __init__(self, name, dim): super(Manifold, self).__init__() self.name = name self.dim = dim self.patches = [] # The patches list is necessary if a Patch instance needs to enumerate # other Patch instance on the same manifold. def _latex(self, printer, *args): return r'\mathrm{%s}' % self.name class Patch(Basic): """Object representing a patch on a manifold. On a manifold one can have many patches that do not always include the whole manifold. On these patches coordinate charts can be defined that permit the parametrization of any point on the patch in terms of a tuple of real numbers (the coordinates). This object serves as a container/parent for all coordinate system charts that can be defined on the patch it represents. Examples: ========= Define a Manifold and a Patch on that Manifold: >>> from sympy.diffgeom import Manifold, Patch >>> m = Manifold('M', 3) >>> p = Patch('P', m) >>> p in m.patches True """ # Contains a reference to the parent manifold in order to be able to access # other patches. def __init__(self, name, manifold): super(Patch, self).__init__() self.name = name self.manifold = manifold self.manifold.patches.append(self) self.coord_systems = [] # The list of coordinate systems is necessary for an instance of # CoordSystem to enumerate other coord systems on the patch. @property def dim(self): return self.manifold.dim def _latex(self, printer, *args): return r'\mathrm{%s}_{%s}' % (self.name, self.manifold._latex(printer, *args)) class CoordSystem(Basic): """Contains all coordinate transformation logic. Examples ======== Define a Manifold and a Patch, and then define two coord systems on that patch: >>> from sympy import symbols, sin, cos, pi >>> from sympy.diffgeom import Manifold, Patch, CoordSystem >>> r, theta = symbols('r, theta') >>> m = Manifold('M', 2) >>> patch = Patch('P', m) >>> rect = CoordSystem('rect', patch) >>> polar = CoordSystem('polar', patch) >>> rect in patch.coord_systems True Connect the coordinate systems. An inverse transformation is automatically found by ``solve`` when possible: >>> polar.connect_to(rect, [r, theta], [r*cos(theta), r*sin(theta)]) >>> polar.coord_tuple_transform_to(rect, [0, 2]) Matrix([ [0], [0]]) >>> polar.coord_tuple_transform_to(rect, [2, pi/2]) Matrix([ [0], [2]]) >>> rect.coord_tuple_transform_to(polar, [1, 1]) Matrix([ [sqrt(2)], [ pi/4]]) Calculate the jacobian of the polar to cartesian transformation: >>> polar.jacobian(rect, [r, theta]) Matrix([ [cos(theta), -r*sin(theta)], [sin(theta), r*cos(theta)]]) Define a point using coordinates in one of the coordinate systems: >>> p = polar.point([1, 3*pi/4]) >>> rect.point_to_coords(p) Matrix([ [-sqrt(2)/2], [ sqrt(2)/2]]) Define a basis scalar field (i.e. a coordinate function), that takes a point and returns its coordinates. It is an instance of ``BaseScalarField``. >>> rect.coord_function(0)(p) -sqrt(2)/2 >>> rect.coord_function(1)(p) sqrt(2)/2 Define a basis vector field (i.e. a unit vector field along the coordinate line). Vectors are also differential operators on scalar fields. It is an instance of ``BaseVectorField``. >>> v_x = rect.base_vector(0) >>> x = rect.coord_function(0) >>> v_x(x) 1 >>> v_x(v_x(x)) 0 Define a basis oneform field: >>> dx = rect.base_oneform(0) >>> dx(v_x) 1 If you provide a list of names the fields will print nicely: - without provided names: >>> x, v_x, dx (rect_0, e_rect_0, drect_0) - with provided names >>> rect = CoordSystem('rect', patch, ['x', 'y']) >>> rect.coord_function(0), rect.base_vector(0), rect.base_oneform(0) (x, e_x, dx) """ # Contains a reference to the parent patch in order to be able to access # other coordinate system charts. def __init__(self, name, patch, names=None): super(CoordSystem, self).__init__() self.name = name if not names: names = ['%s_%d' % (name, i) for i in range(patch.dim)] self._names = names self.patch = patch self._args = self.name, self.patch # names is not in args because it is related only to printing, not to # identifying the CoordSystem instance. self.patch.coord_systems.append(self) self.transforms = {} # All the coordinate transformation logic is in this dictionary in the # form of: # key = other coordinate system # value = tuple of # TODO make these Lambda instances # - list of `Dummy` coordinates in this coordinate system # - list of expressions as a function of the Dummies giving # the coordinates in another coordinate system self._dummies = [Dummy(str(n)) for n in names] self._dummy = Dummy() @property def dim(self): return self.patch.dim ########################################################################## # Coordinate transformations. ########################################################################## def connect_to(self, to_sys, from_coords, to_exprs, inverse=True, fill_in_gaps=False): """Register the transformation used to switch to another coordinate system. Parameters ========== to_sys another instance of ``CoordSystem`` from_coords list of symbols in terms of which ``to_exprs`` is given to_exprs list of the expressions of the new coordinate tuple inverse try to deduce and register the inverse transformation fill_in_gaps try to deduce other transformation that are made possible by composing the present transformation with other already registered transformation """ from_coords, to_exprs = dummyfy(from_coords, to_exprs) self.transforms[to_sys] = Matrix(from_coords), Matrix(to_exprs) if inverse: to_sys.transforms[self] = self._inv_transf(from_coords, to_exprs) if fill_in_gaps: self._fill_gaps_in_transformations() @staticmethod def _inv_transf(from_coords, to_exprs): # TODO, check for results, get solve to return results in definite # format instead of wondering dict/tuple/whatever. # As it is at the moment this is an ugly hack for changing the format inv_from = [i.as_dummy() for i in from_coords] inv_to = solve( [t[0] - t[1] for t in zip(inv_from, to_exprs)], list(from_coords)) if isinstance(inv_to, dict): inv_to = [inv_to[fc] for fc in from_coords] else: inv_to = inv_to[0] return Matrix(inv_from), Matrix(inv_to) @staticmethod def _fill_gaps_in_transformations(): raise NotImplementedError # TODO def coord_tuple_transform_to(self, to_sys, coords): """Transform ``coords`` to coord system ``to_sys``. See the docstring of ``CoordSystem`` for examples.""" coords = Matrix(coords) if self != to_sys: transf = self.transforms[to_sys] coords = transf[1].subs(list(zip(transf[0], coords))) return coords def jacobian(self, to_sys, coords): """Return the jacobian matrix of a transformation.""" with_dummies = self.coord_tuple_transform_to( to_sys, self._dummies).jacobian(self._dummies) return with_dummies.subs(list(zip(self._dummies, coords))) ########################################################################## # Base fields. ########################################################################## def coord_function(self, coord_index): """Return a ``BaseScalarField`` that takes a point and returns one of the coords. Takes a point and returns its coordinate in this coordinate system. See the docstring of ``CoordSystem`` for examples.""" return BaseScalarField(self, coord_index) def coord_functions(self): """Returns a list of all coordinate functions. For more details see the ``coord_function`` method of this class.""" return [self.coord_function(i) for i in range(self.dim)] def base_vector(self, coord_index): """Return a basis vector field. The basis vector field for this coordinate system. It is also an operator on scalar fields. See the docstring of ``CoordSystem`` for examples.""" return BaseVectorField(self, coord_index) def base_vectors(self): """Returns a list of all base vectors. For more details see the ``base_vector`` method of this class.""" return [self.base_vector(i) for i in range(self.dim)] def base_oneform(self, coord_index): """Return a basis 1-form field. The basis one-form field for this coordinate system. It is also an operator on vector fields. See the docstring of ``CoordSystem`` for examples.""" return Differential(self.coord_function(coord_index)) def base_oneforms(self): """Returns a list of all base oneforms. For more details see the ``base_oneform`` method of this class.""" return [self.base_oneform(i) for i in range(self.dim)] ########################################################################## # Points. ########################################################################## def point(self, coords): """Create a ``Point`` with coordinates given in this coord system. See the docstring of ``CoordSystem`` for examples.""" return Point(self, coords) def point_to_coords(self, point): """Calculate the coordinates of a point in this coord system. See the docstring of ``CoordSystem`` for examples.""" return point.coords(self) ########################################################################## # Printing. ########################################################################## def _latex(self, printer, *args): return r'\mathrm{%s}^{\mathrm{%s}}_{%s}' % ( self.name, self.patch.name, self.patch.manifold._latex(printer, *args)) class Point(Basic): """Point in a Manifold object. To define a point you must supply coordinates and a coordinate system. The usage of this object after its definition is independent of the coordinate system that was used in order to define it, however due to limitations in the simplification routines you can arrive at complicated expressions if you use inappropriate coordinate systems. Examples ======== Define the boilerplate Manifold, Patch and coordinate systems: >>> from sympy import symbols, sin, cos, pi >>> from sympy.diffgeom import ( ... Manifold, Patch, CoordSystem, Point) >>> r, theta = symbols('r, theta') >>> m = Manifold('M', 2) >>> p = Patch('P', m) >>> rect = CoordSystem('rect', p) >>> polar = CoordSystem('polar', p) >>> polar.connect_to(rect, [r, theta], [r*cos(theta), r*sin(theta)]) Define a point using coordinates from one of the coordinate systems: >>> p = Point(polar, [r, 3*pi/4]) >>> p.coords() Matrix([ [ r], [3*pi/4]]) >>> p.coords(rect) Matrix([ [-sqrt(2)*r/2], [ sqrt(2)*r/2]]) """ def __init__(self, coord_sys, coords): super(Point, self).__init__() self._coord_sys = coord_sys self._coords = Matrix(coords) self._args = self._coord_sys, self._coords def coords(self, to_sys=None): """Coordinates of the point in a given coordinate system. If ``to_sys`` is ``None`` it returns the coordinates in the system in which the point was defined.""" if to_sys: return self._coord_sys.coord_tuple_transform_to(to_sys, self._coords) else: return self._coords @property def free_symbols(self): raise NotImplementedError return self._coords.free_symbols class BaseScalarField(Expr): """Base Scalar Field over a Manifold for a given Coordinate System. A scalar field takes a point as an argument and returns a scalar. A base scalar field of a coordinate system takes a point and returns one of the coordinates of that point in the coordinate system in question. To define a scalar field you need to choose the coordinate system and the index of the coordinate. The use of the scalar field after its definition is independent of the coordinate system in which it was defined, however due to limitations in the simplification routines you may arrive at more complicated expression if you use unappropriate coordinate systems. You can build complicated scalar fields by just building up SymPy expressions containing ``BaseScalarField`` instances. Examples ======== Define boilerplate Manifold, Patch and coordinate systems: >>> from sympy import symbols, sin, cos, pi, Function >>> from sympy.diffgeom import ( ... Manifold, Patch, CoordSystem, Point, BaseScalarField) >>> r0, theta0 = symbols('r0, theta0') >>> m = Manifold('M', 2) >>> p = Patch('P', m) >>> rect = CoordSystem('rect', p) >>> polar = CoordSystem('polar', p) >>> polar.connect_to(rect, [r0, theta0], [r0*cos(theta0), r0*sin(theta0)]) Point to be used as an argument for the filed: >>> point = polar.point([r0, 0]) Examples of fields: >>> fx = BaseScalarField(rect, 0) >>> fy = BaseScalarField(rect, 1) >>> (fx**2+fy**2).rcall(point) r0**2 >>> g = Function('g') >>> ftheta = BaseScalarField(polar, 1) >>> fg = g(ftheta-pi) >>> fg.rcall(point) g(-pi) """ def __init__(self, coord_sys, index): super(BaseScalarField, self).__init__() self._coord_sys = coord_sys self._index = index self._args = self._coord_sys, self._index def __call__(self, *args): """Evaluating the field at a point or doing nothing. If the argument is a ``Point`` instance, the field is evaluated at that point. The field is returned itself if the argument is any other object. It is so in order to have working recursive calling mechanics for all fields (check the ``__call__`` method of ``Expr``). """ point = args[0] if len(args) != 1 or not isinstance(point, Point): return self coords = point.coords(self._coord_sys) # XXX Calling doit is necessary with all the Subs expressions # XXX Calling simplify is necessary with all the trig expressions return simplify(coords[self._index]).doit() # XXX Workaround for limitations on the content of args free_symbols = set() def doit(self): return self class BaseVectorField(Expr): r"""Vector Field over a Manifold. A vector field is an operator taking a scalar field and returning a directional derivative (which is also a scalar field). A base vector field is the same type of operator, however the derivation is specifically done wrt a chosen coordinate. To define a base vector field you need to choose the coordinate system and the index of the coordinate. The use of the vector field after its definition is independent of the coordinate system in which it was defined, however due to limitations in the simplification routines you may arrive at more complicated expression if you use unappropriate coordinate systems. Examples ======== Use the predefined R2 manifold, setup some boilerplate. >>> from sympy import symbols, pi, Function >>> from sympy.diffgeom.rn import R2, R2_p, R2_r >>> from sympy.diffgeom import BaseVectorField >>> from sympy import pprint >>> x0, y0, r0, theta0 = symbols('x0, y0, r0, theta0') Points to be used as arguments for the field: >>> point_p = R2_p.point([r0, theta0]) >>> point_r = R2_r.point([x0, y0]) Scalar field to operate on: >>> g = Function('g') >>> s_field = g(R2.x, R2.y) >>> s_field.rcall(point_r) g(x0, y0) >>> s_field.rcall(point_p) g(r0*cos(theta0), r0*sin(theta0)) Vector field: >>> v = BaseVectorField(R2_r, 1) >>> pprint(v(s_field)) / d \| |-----(g(x, xi_2))|| \dxi_2 /|xi_2=y >>> pprint(v(s_field).rcall(point_r).doit()) d ---(g(x0, y0)) dy0 >>> pprint(v(s_field).rcall(point_p).doit()) / d \| |-----(g(r0*cos(theta0), xi_2))|| \dxi_2 /|xi_2=r0*sin(theta0) """ def __init__(self, coord_sys, index): super(BaseVectorField, self).__init__() self._coord_sys = coord_sys self._index = index self._args = self._coord_sys, self._index def __call__(self, scalar_field): """Apply on a scalar field. The action of a vector field on a scalar field is a directional differentiation. If the argument is not a scalar field an error is raised. """ if covariant_order(scalar_field) or contravariant_order(scalar_field): raise ValueError('Only scalar fields can be supplied as arguments to vector fields.') base_scalars = list(scalar_field.atoms(BaseScalarField)) # First step: e_x(x+r**2) -> e_x(x) + 2*r*e_x(r) d_var = self._coord_sys._dummy # TODO: you need a real dummy function for the next line d_funcs = [Function('_#_%s' % i)(d_var) for i, b in enumerate(base_scalars)] d_result = scalar_field.subs(list(zip(base_scalars, d_funcs))) d_result = d_result.diff(d_var) # Second step: e_x(x) -> 1 and e_x(r) -> cos(atan2(x, y)) coords = self._coord_sys._dummies d_funcs_deriv = [f.diff(d_var) for f in d_funcs] d_funcs_deriv_sub = [] for b in base_scalars: jac = self._coord_sys.jacobian(b._coord_sys, coords) d_funcs_deriv_sub.append(jac[b._index, self._index]) d_result = d_result.subs(list(zip(d_funcs_deriv, d_funcs_deriv_sub))) # Remove the dummies result = d_result.subs(list(zip(d_funcs, base_scalars))) result = result.subs(list(zip(coords, self._coord_sys.coord_functions()))) return result.doit() # XXX doit for the Subs instances class Commutator(Expr): r"""Commutator of two vector fields. The commutator of two vector fields `v_1` and `v_2` is defined as the vector field `[v_1, v_2]` that evaluated on each scalar field `f` is equal to `v_1(v_2(f)) - v_2(v_1(f))`. Examples ======== Use the predefined R2 manifold, setup some boilerplate. >>> from sympy.diffgeom.rn import R2 >>> from sympy.diffgeom import Commutator >>> from sympy import pprint >>> from sympy.simplify import simplify Vector fields: >>> e_x, e_y, e_r = R2.e_x, R2.e_y, R2.e_r >>> c_xy = Commutator(e_x, e_y) >>> c_xr = Commutator(e_x, e_r) >>> c_xy 0 """ # TODO simplify fails with an error #>>> pprint(simplify(c_xr(R2.y**2).doit())) # -1 # / 2 2\ #-2*y*\x + y / *cos(theta)*y #""" def __new__(cls, v1, v2): if (covariant_order(v1) or contravariant_order(v1) != 1 or covariant_order(v2) or contravariant_order(v2) != 1): raise ValueError( 'Only commutators of vector fields are supported.') if v1 == v2: return Zero() coord_sys = set.union(*[v.atoms(CoordSystem) for v in (v1, v2)]) if len(coord_sys) == 1: # Only one coordinate systems is used, hence it is easy enough to # actually evaluate the commutator. if all(isinstance(v, BaseVectorField) for v in (v1, v2)): return Zero() bases_1, bases_2 = [list(v.atoms(BaseVectorField)) for v in (v1, v2)] coeffs_1 = [v1.expand().coeff(b) for b in bases_1] coeffs_2 = [v2.expand().coeff(b) for b in bases_2] res = 0 for c1, b1 in zip(coeffs_1, bases_1): for c2, b2 in zip(coeffs_2, bases_2): res += c1*b1(c2)*b2 - c2*b2(c1)*b1 return res else: return super(Commutator, cls).__new__(cls, v1, v2) def __init__(self, v1, v2): super(Commutator, self).__init__() self._args = (v1, v2) self._v1 = v1 self._v2 = v2 def __call__(self, scalar_field): """Apply on a scalar field. If the argument is not a scalar field an error is raised. """ return self._v1(self._v2(scalar_field)) - self._v2(self._v1(scalar_field)) class Differential(Expr): """Return the differential (exterior derivative) of a form field. The differential of a form (i.e. the exterior derivative) has a complicated definition in the general case. The differential `df` of the 0-form `f` is defined for any vector field `v` as `df(v) = v(f)`. Examples ======== Use the predefined R2 manifold, setup some boilerplate. >>> from sympy import Function >>> from sympy.diffgeom.rn import R2 >>> from sympy.diffgeom import Differential >>> from sympy import pprint Scalar field (0-forms): >>> g = Function('g') >>> s_field = g(R2.x, R2.y) Vector fields: >>> e_x, e_y, = R2.e_x, R2.e_y Differentials: >>> dg = Differential(s_field) >>> dg d(g(x, y)) >>> pprint(dg(e_x)) / d \| |-----(g(xi_1, y))|| \dxi_1 /|xi_1=x >>> pprint(dg(e_y)) / d \| |-----(g(x, xi_2))|| \dxi_2 /|xi_2=y Applying the exterior derivative operator twice always results in: >>> Differential(dg) 0 """ def __new__(cls, form_field): if contravariant_order(form_field): raise ValueError( 'A vector field was supplied as an argument to Differential.') if isinstance(form_field, Differential): return Zero() else: return super(Differential, cls).__new__(cls, form_field) def __init__(self, form_field): super(Differential, self).__init__() self._form_field = form_field self._args = (self._form_field, ) def __call__(self, *vector_fields): """Apply on a list of vector_fields. If the number of vector fields supplied is not equal to 1 + the order of the form field inside the differential the result is undefined. For 1-forms (i.e. differentials of scalar fields) the evaluation is done as `df(v)=v(f)`. However if `v` is ``None`` instead of a vector field, the differential is returned unchanged. This is done in order to permit partial contractions for higher forms. In the general case the evaluation is done by applying the form field inside the differential on a list with one less elements than the number of elements in the original list. Lowering the number of vector fields is achieved through replacing each pair of fields by their commutator. If the arguments are not vectors or ``None``s an error is raised. """ if any((contravariant_order(a) != 1 or covariant_order(a)) and a is not None for a in vector_fields): raise ValueError('The arguments supplied to Differential should be vector fields or Nones.') k = len(vector_fields) if k == 1: if vector_fields[0]: return vector_fields[0].rcall(self._form_field) return self else: # For higher form it is more complicated: # Invariant formula: # http://en.wikipedia.org/wiki/Exterior_derivative#Invariant_formula # df(v1, ... vn) = +/- vi(f(v1..no i..vn)) # +/- f([vi,vj],v1..no i, no j..vn) f = self._form_field v = vector_fields ret = 0 for i in range(k): t = v[i].rcall(f.rcall(*v[:i] + v[i + 1:])) ret += (-1)**i*t for j in range(i + 1, k): c = Commutator(v[i], v[j]) if c: # TODO this is ugly - the Commutator can be Zero and # this causes the next line to fail t = f.rcall(*(c,) + v[:i] + v[i + 1:j] + v[j + 1:]) ret += (-1)**(i + j)*t return ret class TensorProduct(Expr): """Tensor product of forms. The tensor product permits the creation of multilinear functionals (i.e. higher order tensors) out of lower order forms (e.g. 1-forms). However, the higher tensors thus created lack the interesting features provided by the other type of product, the wedge product, namely they are not antisymetric and hence are not form fields. Examples ======== Use the predefined R2 manifold, setup some boilerplate. >>> from sympy import Function >>> from sympy.diffgeom.rn import R2 >>> from sympy.diffgeom import TensorProduct >>> from sympy import pprint >>> TensorProduct(R2.dx, R2.dy)(R2.e_x, R2.e_y) 1 >>> TensorProduct(R2.dx, R2.dy)(R2.e_y, R2.e_x) 0 >>> TensorProduct(R2.dx, R2.x*R2.dy)(R2.x*R2.e_x, R2.e_y) x**2 You can nest tensor products. >>> tp1 = TensorProduct(R2.dx, R2.dy) >>> TensorProduct(tp1, R2.dx)(R2.e_x, R2.e_y, R2.e_x) 1 You can make partial contaction for instance when 'raising an index'. >>> TP = TensorProduct >>> metric = TP(R2.dx, R2.dx) + 3*TP(R2.dy, R2.dy) >>> metric.rcall(R2.e_y, None) 3*dy Or automatically pad the args with ``None``s. >>> metric.rcall(R2.e_y) 3*dy """ def __new__(cls, *args): if any(contravariant_order(a) for a in args): raise ValueError('A vector field was supplied as an argument to TensorProduct.') scalar = Mul(*[m for m in args if covariant_order(m) == 0]) forms = [m for m in args if covariant_order(m)] if forms: if len(forms) == 1: return scalar*forms[0] return scalar*super(TensorProduct, cls).__new__(cls, *forms) else: return scalar def __init__(self, *args): super(TensorProduct, self).__init__() self._args = args def __call__(self, *v_fields): """Apply on a list of vector_fields. If the number of vector fields supplied is not equal to the order of the form field the list of arguments is padded with ``None``'s. The list of arguments is divided in sublists depending on the order of the forms inside the tensor product. The sublists are provided as arguments to these forms and the resulting expressions are given to the constructor of ``TensorProduct``. """ tot_order = covariant_order(self) tot_args = len(v_fields) if tot_args != tot_order: v_fields = list(v_fields) + [None]*(tot_order - tot_args) orders = [covariant_order(f) for f in self._args] indices = [sum(orders[:i + 1]) for i in range(len(orders) - 1)] v_fields = [v_fields[i:j] for i, j in zip([0] + indices, indices + [None])] multipliers = [t[0].rcall(*t[1]) for t in zip(self._args, v_fields)] return TensorProduct(*multipliers) def _latex(self, printer, *args): elements = [printer._print(a) for a in self.args] return r'\otimes'.join(elements) class WedgeProduct(TensorProduct): """Wedge product of forms. In the context of integration only completely antisymetric forms make sense. The wedge product permits the creation of such forms. Examples ======== Use the predefined R2 manifold, setup some boilerplate. >>> from sympy import Function >>> from sympy.diffgeom.rn import R2 >>> from sympy.diffgeom import WedgeProduct >>> from sympy import pprint >>> WedgeProduct(R2.dx, R2.dy)(R2.e_x, R2.e_y) 1 >>> WedgeProduct(R2.dx, R2.dy)(R2.e_y, R2.e_x) -1 >>> WedgeProduct(R2.dx, R2.x*R2.dy)(R2.x*R2.e_x, R2.e_y) x**2 You can nest wedge products. >>> wp1 = WedgeProduct(R2.dx, R2.dy) >>> WedgeProduct(wp1, R2.dx)(R2.e_x, R2.e_y, R2.e_x) 0 """ # TODO the calculation of signatures is slow # TODO you do not need all these permutations (neither the prefactor) def __call__(self, *vector_fields): """Apply on a list of vector_fields. The expression is rewritten internally in terms of tensor products and evaluated.""" orders = (covariant_order(e) for e in self.args) mul = 1/Mul(*(factorial(o) for o in orders)) perms = permutations(vector_fields) perms_par = (Permutation( p).signature() for p in permutations(list(range(len(vector_fields))))) tensor_prod = TensorProduct(*self.args) return mul*Add(*[tensor_prod(*p[0])*p[1] for p in zip(perms, perms_par)]) class LieDerivative(Expr): """Lie derivative wrt a vector field. The transport operator that defines the Lie derivative is the pushforward of the field to be derived along the integral curve of the field wrt which one derives. Examples ======== >>> #TODO """ def __new__(cls, v_field, expr): expr_form_ord = covariant_order(expr) if contravariant_order(v_field) != 1 or covariant_order(v_field): raise ValueError('Lie derivatives are defined only wrt vector fields.' ' The supplied argument was not a vector field.') if expr_form_ord > 0: return super(LieDerivative, cls).__new__(cls, v_field, expr) if expr.atoms(BaseVectorField): return Commutator(v_field, expr) else: return v_field.rcall(expr) def __init__(self, v_field, expr): super(LieDerivative, self).__init__() self._v_field = v_field self._expr = expr self._args = (self._v_field, self._expr) def __call__(self, *args): v = self._v_field expr = self._expr lead_term = v(expr(*args)) rest = Add(*[Mul(*args[:i] + (Commutator(v, args[i]),) + args[i + 1:]) for i in range(len(args))]) return lead_term - rest class BaseCovarDerivativeOp(Expr): """Covariant derivative operator wrt a base vector. Examples ======== >>> from sympy.diffgeom.rn import R2, R2_r >>> from sympy.diffgeom import BaseCovarDerivativeOp >>> from sympy.diffgeom import metric_to_Christoffel_2nd, TensorProduct >>> TP = TensorProduct >>> ch = metric_to_Christoffel_2nd(TP(R2.dx, R2.dx) + TP(R2.dy, R2.dy)) >>> ch (((0, 0), (0, 0)), ((0, 0), (0, 0))) >>> cvd = BaseCovarDerivativeOp(R2_r, 0, ch) >>> cvd(R2.x) 1 >>> cvd(R2.x*R2.e_x) e_x """ def __init__(self, coord_sys, index, christoffel): super(BaseCovarDerivativeOp, self).__init__() self._coord_sys = coord_sys self._index = index self._christoffel = christoffel self._args = self._coord_sys, self._index, self._christoffel def __call__(self, field): """Apply on a scalar field. The action of a vector field on a scalar field is a directional differentiation. If the argument is not a scalar field the behaviour is undefined. """ if covariant_order(field) != 0: raise NotImplementedError() field = vectors_in_basis(field, self._coord_sys) wrt_vector = self._coord_sys.base_vector(self._index) wrt_scalar = self._coord_sys.coord_function(self._index) vectors = list(field.atoms(BaseVectorField)) # First step: replace all vectors with something susceptible to # derivation and do the derivation # TODO: you need a real dummy function for the next line d_funcs = [Function('_#_%s' % i)(wrt_scalar) for i, b in enumerate(vectors)] d_result = field.subs(list(zip(vectors, d_funcs))) d_result = wrt_vector(d_result) # Second step: backsubstitute the vectors in d_result = d_result.subs(list(zip(d_funcs, vectors))) # Third step: evaluate the derivatives of the vectors derivs = [] for v in vectors: d = Add(*[(self._christoffel[k][wrt_vector._index][v._index] *v._coord_sys.base_vector(k)) for k in range(v._coord_sys.dim)]) derivs.append(d) to_subs = [wrt_vector(d) for d in d_funcs] result = d_result.subs(list(zip(to_subs, derivs))) return result # TODO .doit() # XXX doit for the Subs instances class CovarDerivativeOp(Expr): """Covariant derivative operator. Examples ======== >>> from sympy.diffgeom.rn import R2 >>> from sympy.diffgeom import CovarDerivativeOp >>> from sympy.diffgeom import metric_to_Christoffel_2nd, TensorProduct >>> TP = TensorProduct >>> ch = metric_to_Christoffel_2nd(TP(R2.dx, R2.dx) + TP(R2.dy, R2.dy)) >>> ch (((0, 0), (0, 0)), ((0, 0), (0, 0))) >>> cvd = CovarDerivativeOp(R2.x*R2.e_x, ch) >>> cvd(R2.x) x >>> cvd(R2.x*R2.e_x) x*e_x """ def __init__(self, wrt, christoffel): super(CovarDerivativeOp, self).__init__() if len(set(v._coord_sys for v in wrt.atoms(BaseVectorField))) > 1: raise NotImplementedError() if contravariant_order(wrt) != 1 or covariant_order(wrt): raise ValueError('Covariant derivatives are defined only wrt vector fields.' ' The supplied argument was not a vector field.') self._wrt = wrt self._christoffel = christoffel self._args = self._wrt, self._christoffel def __call__(self, field): vectors = list(self._wrt.atoms(BaseVectorField)) base_ops = [BaseCovarDerivativeOp(v._coord_sys, v._index, self._christoffel) for v in vectors] return self._wrt.subs(list(zip(vectors, base_ops))).rcall(field) def _latex(self, printer, *args): return r'\mathbb{\nabla}_{%s}' % printer._print(self._wrt) ############################################################################### # Integral curves on vector fields ############################################################################### def intcurve_series(vector_field, param, start_point, n=6, coord_sys=None, coeffs=False): """Return the series expansion for an integral curve of the field. Integral curve is a function `gamma` taking a parameter in R to a point in the manifold. It verifies the equation: `vector_field(f)(gamma(param)) = diff(f(gamma(t)), t)` for any value `t` for the parameter and any scalar field `f`. This function returns a series expansion of `gamma(t)` in terms of the coordinate system `coord_sys`. The equations and expansions are necessarily done in coordinate-system-dependent way as there is no other way to represent movement between points on the manifold (i.e. there is no such thing as a difference of points for a general manifold). Parameters ========== vector_field the vector field for which an integral curve will be given param the argument of the function `gamma` from R to the curve start_point the point which coresponds to `gamma(0)` n the order to which to expand coord_sys the coordinate system in which to expand coeffs (default False) - if True return a list of elements of the expansion See Also: intcurve_diffequ Examples ======== Use the predefined R2 manifold: >>> from sympy.abc import t, x, y >>> from sympy.diffgeom.rn import R2, R2_p, R2_r >>> from sympy.diffgeom import intcurve_series Specify a starting point and a vector field: >>> start_point = R2_r.point([x, y]) >>> vector_field = R2_r.e_x Calculate the series: >>> intcurve_series(vector_field, t, start_point, n=3) Matrix([ [t + x], [ y]]) Or get the elements of the expansion in a list: >>> series = intcurve_series(vector_field, t, start_point, n=3, coeffs=True) >>> series[0] Matrix([ [x], [y]]) >>> series[1] Matrix([ [t], [0]]) >>> series[2] Matrix([ [0], [0]]) The series in the polar coordinate system: >>> series = intcurve_series(vector_field, t, start_point, ... n=3, coord_sys=R2_p, coeffs=True) >>> series[0] Matrix([ [sqrt(x**2 + y**2)], [ atan2(y, x)]]) >>> series[1] Matrix([ [t*x/sqrt(x**2 + y**2)], [ -t*y/(x**2 + y**2)]]) >>> series[2] Matrix([ [t**2*(-x**2/(x**2 + y**2)**(3/2) + 1/sqrt(x**2 + y**2))/2], [ t**2*x*y/(x**2 + y**2)**2]]) """ if contravariant_order(vector_field) != 1 or covariant_order(vector_field): raise ValueError('The supplied field was not a vector field.') def iter_vfield(scalar_field, i): """Return `vector_field` called `i` times on `scalar_field`.""" return reduce(lambda s, v: v.rcall(s), [vector_field, ]*i, scalar_field) def taylor_terms_per_coord(coord_function): """Return the series for one of the coordinates.""" return [param**i*iter_vfield(coord_function, i).rcall(start_point)/factorial(i) for i in range(n)] coord_sys = coord_sys if coord_sys else start_point._coord_sys coord_functions = coord_sys.coord_functions() taylor_terms = [taylor_terms_per_coord(f) for f in coord_functions] if coeffs: return [Matrix(t) for t in zip(*taylor_terms)] else: return Matrix([sum(c) for c in taylor_terms]) def intcurve_diffequ(vector_field, param, start_point, coord_sys=None): """Return the differential equation for an integral curve of the field. Integral curve is a function `gamma` taking a parameter in R to a point in the manifold. It verifies the equation: `vector_field(f)(gamma(param)) = diff(f(gamma(t)), t)` for any value `t` for the parameter and any scalar field `f`. This function returns the differential equation of `gamma(t)` in terms of the coordinate system `coord_sys`. The equations and expansions are necessarily done in coordinate-system-dependent way as there is no other way to represent movement between points on the manifold (i.e. there is no such thing as a difference of points for a general manifold). Parameters ========== vector_field the vector field for which an integral curve will be given param the argument of the function `gamma` from R to the curve start_point the point which coresponds to `gamma(0)` coord_sys the coordinate system in which to give the equations Returns ======= a tuple of (equations, initial conditions) See Also: intcurve_series Examples ======== Use the predefined R2 manifold: >>> from sympy.abc import t >>> from sympy.diffgeom.rn import R2, R2_p, R2_r >>> from sympy.diffgeom import intcurve_diffequ Specify a starting point and a vector field: >>> start_point = R2_r.point([0, 1]) >>> vector_field = -R2.y*R2.e_x + R2.x*R2.e_y Get the equation: >>> equations, init_cond = intcurve_diffequ(vector_field, t, start_point) >>> equations [f_1(t) + Derivative(f_0(t), t), -f_0(t) + Derivative(f_1(t), t)] >>> init_cond [f_0(0), f_1(0) - 1] The series in the polar coordinate system: >>> equations, init_cond = intcurve_diffequ(vector_field, t, start_point, R2_p) >>> equations [Derivative(f_0(t), t), Derivative(f_1(t), t) - 1] >>> init_cond [f_0(0) - 1, f_1(0) - pi/2] """ if contravariant_order(vector_field) != 1 or covariant_order(vector_field): raise ValueError('The supplied field was not a vector field.') coord_sys = coord_sys if coord_sys else start_point._coord_sys gammas = [Function('f_%d' % i)(param) for i in range( start_point._coord_sys.dim)] arbitrary_p = Point(coord_sys, gammas) coord_functions = coord_sys.coord_functions() equations = [simplify(diff(cf.rcall(arbitrary_p), param) - vector_field.rcall(cf).rcall(arbitrary_p)) for cf in coord_functions] init_cond = [simplify(cf.rcall(arbitrary_p).subs(param, 0) - cf.rcall(start_point)) for cf in coord_functions] return equations, init_cond ############################################################################### # Helpers ############################################################################### def dummyfy(args, exprs): # TODO Is this a good idea? d_args = Matrix([s.as_dummy() for s in args]) d_exprs = Matrix([sympify(expr).subs(list(zip(args, d_args))) for expr in exprs]) return d_args, d_exprs def list_to_tuple_rec(the_list): # TODO remove in favor of tensor classes if isinstance(the_list, list): return tuple(list_to_tuple_rec(e) for e in the_list) return the_list ############################################################################### # Helpers ############################################################################### def contravariant_order(expr, _strict=False): """Return the contravariant order of an expression. Examples ======== >>> from sympy.diffgeom import contravariant_order >>> from sympy.diffgeom.rn import R2 >>> from sympy.abc import a >>> contravariant_order(a) 0 >>> contravariant_order(a*R2.x + 2) 0 >>> contravariant_order(a*R2.x*R2.e_y + R2.e_x) 1 """ # TODO move some of this to class methods. # TODO rewrite using the .as_blah_blah methods if isinstance(expr, Add): orders = [contravariant_order(e) for e in expr.args] if len(set(orders)) != 1: raise ValueError('Misformed expression containing contravariant fields of varying order.') return orders[0] elif isinstance(expr, Mul): orders = [contravariant_order(e) for e in expr.args] not_zero = [o for o in orders if o != 0] if len(not_zero) > 1: raise ValueError('Misformed expression containing multiplication between vectors.') return 0 if not not_zero else not_zero[0] elif isinstance(expr, Pow): if covariant_order(expr.base) or covariant_order(expr.exp): raise ValueError( 'Misformed expression containing a power of a vector.') return 0 elif isinstance(expr, BaseVectorField): return 1 elif not _strict or expr.atoms(BaseScalarField): return 0 else: # If it does not contain anything related to the diffgeom module and it is _strict return -1 def covariant_order(expr, _strict=False): """Return the covariant order of an expression. Examples ======== >>> from sympy.diffgeom import covariant_order >>> from sympy.diffgeom.rn import R2 >>> from sympy.abc import a >>> covariant_order(a) 0 >>> covariant_order(a*R2.x + 2) 0 >>> covariant_order(a*R2.x*R2.dy + R2.dx) 1 """ # TODO move some of this to class methods. # TODO rewrite using the .as_blah_blah methods if isinstance(expr, Add): orders = [covariant_order(e) for e in expr.args] if len(set(orders)) != 1: raise ValueError('Misformed expression containing form fields of varying order.') return orders[0] elif isinstance(expr, Mul): orders = [covariant_order(e) for e in expr.args] not_zero = [o for o in orders if o != 0] if len(not_zero) > 1: raise ValueError('Misformed expression containing multiplication between forms.') return 0 if not not_zero else not_zero[0] elif isinstance(expr, Pow): if covariant_order(expr.base) or covariant_order(expr.exp): raise ValueError( 'Misformed expression containing a power of a form.') return 0 elif isinstance(expr, Differential): return covariant_order(*expr.args) + 1 elif isinstance(expr, TensorProduct): return sum(covariant_order(a) for a in expr.args) elif not _strict or expr.atoms(BaseScalarField): return 0 else: # If it does not contain anything related to the diffgeom module and it is _strict return -1 ############################################################################### # Coordinate transformation functions ############################################################################### def vectors_in_basis(expr, to_sys): """Transform all base vectors in base vectors of a specified coord basis. While the new base vectors are in the new coordinate system basis, any coefficients are kept in the old system. Examples ======== >>> from sympy.diffgeom import vectors_in_basis >>> from sympy.diffgeom.rn import R2_r, R2_p >>> vectors_in_basis(R2_r.e_x, R2_p) (x**2 + y**2)**(-1/2)*x*e_r - y*(x**2 + y**2)**(-1)*e_theta >>> vectors_in_basis(R2_p.e_r, R2_r) sin(theta)*e_y + cos(theta)*e_x """ vectors = list(expr.atoms(BaseVectorField)) new_vectors = [] for v in vectors: cs = v._coord_sys jac = cs.jacobian(to_sys, cs.coord_functions()) new = (jac.T*Matrix(to_sys.base_vectors()))[v._index] new_vectors.append(new) return expr.subs(list(zip(vectors, new_vectors))) ############################################################################### # Coordinate-dependent functions ############################################################################### def twoform_to_matrix(expr): """Return the matrix representing the twoform. For the twoform `w` return the matrix `M` such that `M[i,j]=w(e_i, e_j)`, where `e_i` is the i-th base vector field for the coordinate system in which the expression of `w` is given. Examples ======== >>> from sympy.diffgeom.rn import R2 >>> from sympy.diffgeom import twoform_to_matrix, TensorProduct >>> TP = TensorProduct >>> twoform_to_matrix(TP(R2.dx, R2.dx) + TP(R2.dy, R2.dy)) Matrix([ [1, 0], [0, 1]]) >>> twoform_to_matrix(R2.x*TP(R2.dx, R2.dx) + TP(R2.dy, R2.dy)) Matrix([ [x, 0], [0, 1]]) >>> twoform_to_matrix(TP(R2.dx, R2.dx) + TP(R2.dy, R2.dy) - TP(R2.dx, R2.dy)/2) Matrix([ [ 1, 0], [-1/2, 1]]) """ if covariant_order(expr) != 2 or contravariant_order(expr): raise ValueError('The input expression is not a two-form.') coord_sys = expr.atoms(CoordSystem) if len(coord_sys) != 1: raise ValueError('The input expression concerns more than one ' 'coordinate systems, hence there is no unambiguous ' 'way to choose a coordinate system for the matrix.') coord_sys = coord_sys.pop() vectors = coord_sys.base_vectors() expr = expr.expand() matrix_content = [[expr.rcall(v1, v2) for v1 in vectors] for v2 in vectors] return Matrix(matrix_content) def metric_to_Christoffel_1st(expr): """Return the nested list of Christoffel symbols for the given metric. This returns the Christoffel symbol of first kind that represents the Levi-Civita connection for the given metric. Examples ======== >>> from sympy.diffgeom.rn import R2 >>> from sympy.diffgeom import metric_to_Christoffel_1st, TensorProduct >>> TP = TensorProduct >>> metric_to_Christoffel_1st(TP(R2.dx, R2.dx) + TP(R2.dy, R2.dy)) (((0, 0), (0, 0)), ((0, 0), (0, 0))) >>> metric_to_Christoffel_1st(R2.x*TP(R2.dx, R2.dx) + TP(R2.dy, R2.dy)) (((1/2, 0), (0, 0)), ((0, 0), (0, 0))) """ matrix = twoform_to_matrix(expr) if not matrix.is_symmetric(): raise ValueError( 'The two-form representing the metric is not symmetric.') coord_sys = expr.atoms(CoordSystem).pop() deriv_matrices = [matrix.applyfunc(lambda a: d(a)) for d in coord_sys.base_vectors()] indices = list(range(coord_sys.dim)) christoffel = [[[(deriv_matrices[k][i, j] + deriv_matrices[j][i, k] - deriv_matrices[i][j, k])/2 for k in indices] for j in indices] for i in indices] return list_to_tuple_rec(christoffel) def metric_to_Christoffel_2nd(expr): """Return the nested list of Christoffel symbols for the given metric. This returns the Christoffel symbol of second kind that represents the Levi-Civita connection for the given metric. Examples ======== >>> from sympy.diffgeom.rn import R2 >>> from sympy.diffgeom import metric_to_Christoffel_2nd, TensorProduct >>> TP = TensorProduct >>> metric_to_Christoffel_2nd(TP(R2.dx, R2.dx) + TP(R2.dy, R2.dy)) (((0, 0), (0, 0)), ((0, 0), (0, 0))) >>> metric_to_Christoffel_2nd(R2.x*TP(R2.dx, R2.dx) + TP(R2.dy, R2.dy)) (((x**(-1)/2, 0), (0, 0)), ((0, 0), (0, 0))) """ ch_1st = metric_to_Christoffel_1st(expr) coord_sys = expr.atoms(CoordSystem).pop() indices = list(range(coord_sys.dim)) # XXX workaround, inverting a matrix does not work if it contains non # symbols #matrix = twoform_to_matrix(expr).inv() matrix = twoform_to_matrix(expr) s_fields = set() for e in matrix: s_fields.update(e.atoms(BaseScalarField)) s_fields = list(s_fields) dums = coord_sys._dummies matrix = matrix.subs(list(zip(s_fields, dums))).inv().subs(list(zip(dums, s_fields))) # XXX end of workaround christoffel = [[[Add(*[matrix[i, l]*ch_1st[l][j][k] for l in indices]) for k in indices] for j in indices] for i in indices] return list_to_tuple_rec(christoffel) def metric_to_Riemann_components(expr): """Return the components of the Riemann tensor expressed in a given basis. Given a metric it calculates the components of the Riemann tensor in the canonical basis of the coordinate system in which the metric expression is given. Examples ======== >>> from sympy import pprint, exp >>> from sympy.diffgeom.rn import R2 >>> from sympy.diffgeom import metric_to_Riemann_components, TensorProduct >>> TP = TensorProduct >>> metric_to_Riemann_components(TP(R2.dx, R2.dx) + TP(R2.dy, R2.dy)) ((((0, 0), (0, 0)), ((0, 0), (0, 0))), (((0, 0), (0, 0)), ((0, 0), (0, 0)))) >>> non_trivial_metric = exp(2*R2.r)*TP(R2.dr, R2.dr) + \ R2.r**2*TP(R2.dtheta, R2.dtheta) >>> non_trivial_metric exp(2*r)*TensorProduct(dr, dr) + r**2*TensorProduct(dtheta, dtheta) >>> riemann = metric_to_Riemann_components(non_trivial_metric) >>> riemann[0] (((0, 0), (0, 0)), ((0, -exp(-2*r)*r + 2*r*exp(-2*r)), (exp(-2*r)*r - 2*r*exp(-2*r), 0))) >>> riemann[1] (((0, -r**(-1)), (r**(-1), 0)), ((0, 0), (0, 0))) """ ch_2nd = metric_to_Christoffel_2nd(expr) coord_sys = expr.atoms(CoordSystem).pop() indices = list(range(coord_sys.dim)) deriv_ch = [[[[d(ch_2nd[i][j][k]) for d in coord_sys.base_vectors()] for k in indices] for j in indices] for i in indices] riemann_a = [[[[deriv_ch[rho][sig][nu][mu] - deriv_ch[rho][sig][mu][nu] for nu in indices] for mu in indices] for sig in indices] for rho in indices] riemann_b = [[[[Add(*[ch_2nd[rho][l][mu]*ch_2nd[l][sig][nu] - ch_2nd[rho][l][nu]*ch_2nd[l][sig][mu] for l in indices]) for nu in indices] for mu in indices] for sig in indices] for rho in indices] riemann = [[[[riemann_a[rho][sig][mu][nu] + riemann_b[rho][sig][mu][nu] for nu in indices] for mu in indices] for sig in indices] for rho in indices] return list_to_tuple_rec(riemann) def metric_to_Ricci_components(expr): """Return the components of the Ricci tensor expressed in a given basis. Given a metric it calculates the components of the Ricci tensor in the canonical basis of the coordinate system in which the metric expression is given. Examples ======== >>> from sympy import pprint, exp >>> from sympy.diffgeom.rn import R2 >>> from sympy.diffgeom import metric_to_Ricci_components, TensorProduct >>> TP = TensorProduct >>> metric_to_Ricci_components(TP(R2.dx, R2.dx) + TP(R2.dy, R2.dy)) ((0, 0), (0, 0)) >>> non_trivial_metric = exp(2*R2.r)*TP(R2.dr, R2.dr) + \ R2.r**2*TP(R2.dtheta, R2.dtheta) >>> non_trivial_metric exp(2*r)*TensorProduct(dr, dr) + r**2*TensorProduct(dtheta, dtheta) >>> metric_to_Ricci_components(non_trivial_metric) #TODO why is this not simpler ((r**(-1), 0), (0, -exp(-2*r)*r + 2*r*exp(-2*r))) """ riemann = metric_to_Riemann_components(expr) coord_sys = expr.atoms(CoordSystem).pop() indices = list(range(coord_sys.dim)) ricci = [[Add(*[riemann[k][i][k][j] for k in indices]) for j in indices] for i in indices] return list_to_tuple_rec(ricci) sympy-0.7.4.1/sympy/diffgeom/rn.py0000644000175000017500000001062512253362407017202 0ustar georgeskgeorgesk"""Predefined R^n manifolds together with common coord. systems. Coordinate systems are predefined as well as the transformation laws between them. Coordinate functions can be accessed as attributes of the manifold (eg `R2.x`), as attributes of the coordinate systems (eg `R2_r.x` and `R2_p.theta`), or by using the usual `coord_sys.coord_function(index, name)` interface. """ from __future__ import print_function, division from .diffgeom import Manifold, Patch, CoordSystem from sympy import sqrt, atan2, acos, sin, cos, Dummy ############################################################################### # R2 ############################################################################### R2 = Manifold('R^2', 2) # Patch and coordinate systems. R2_origin = Patch('origin', R2) R2_r = CoordSystem('rectangular', R2_origin, ['x', 'y']) R2_p = CoordSystem('polar', R2_origin, ['r', 'theta']) # Connecting the coordinate charts. x, y, r, theta = [Dummy(s) for s in ['x', 'y', 'r', 'theta']] R2_r.connect_to(R2_p, [x, y], [sqrt(x**2 + y**2), atan2(y, x)], inverse=False, fill_in_gaps=False) R2_p.connect_to(R2_r, [r, theta], [r*cos(theta), r*sin(theta)], inverse=False, fill_in_gaps=False) del x, y, r, theta # Defining the basis coordinate functions and adding shortcuts for them to the # manifold and the patch. R2.x, R2.y = R2_origin.x, R2_origin.y = R2_r.x, R2_r.y = R2_r.coord_functions() R2.r, R2.theta = R2_origin.r, R2_origin.theta = R2_p.r, R2_p.theta = R2_p.coord_functions() # Defining the basis vector fields and adding shortcuts for them to the # manifold and the patch. R2.e_x, R2.e_y = R2_origin.e_x, R2_origin.e_y = R2_r.e_x, R2_r.e_y = R2_r.base_vectors() R2.e_r, R2.e_theta = R2_origin.e_r, R2_origin.e_theta = R2_p.e_r, R2_p.e_theta = R2_p.base_vectors() # Defining the basis oneform fields and adding shortcuts for them to the # manifold and the patch. R2.dx, R2.dy = R2_origin.dx, R2_origin.dy = R2_r.dx, R2_r.dy = R2_r.base_oneforms() R2.dr, R2.dtheta = R2_origin.dr, R2_origin.dtheta = R2_p.dr, R2_p.dtheta = R2_p.base_oneforms() ############################################################################### # R3 ############################################################################### R3 = Manifold('R^3', 3) # Patch and coordinate systems. R3_origin = Patch('origin', R3) R3_r = CoordSystem('rectangular', R3_origin, ['x', 'y', 'z']) R3_c = CoordSystem('cylindrical', R3_origin, ['rho', 'psi', 'z']) R3_s = CoordSystem('spherical', R3_origin, ['r', 'theta', 'phi']) # Connecting the coordinate charts. x, y, z, rho, psi, r, theta, phi = [Dummy(s) for s in ['x', 'y', 'z', 'rho', 'psi', 'r', 'theta', 'phi']] ## rectangular <-> cylindrical R3_r.connect_to(R3_c, [x, y, z], [sqrt(x**2 + y**2), atan2(y, x), z], inverse=False, fill_in_gaps=False) R3_c.connect_to(R3_r, [rho, psi, z], [rho*cos(psi), rho*sin(psi), z], inverse=False, fill_in_gaps=False) ## rectangular <-> spherical R3_r.connect_to(R3_s, [x, y, z], [sqrt(x**2 + y**2 + z**2), acos(z/ sqrt(x**2 + y**2 + z**2)), atan2(y, x)], inverse=False, fill_in_gaps=False) R3_s.connect_to(R3_r, [r, theta, phi], [r*sin(theta)*cos(phi), r*sin( theta)*sin(phi), r*cos(theta)], inverse=False, fill_in_gaps=False) ## cylindrical <-> spherical R3_c.connect_to(R3_s, [rho, psi, z], [sqrt(rho**2 + z**2), acos(z/sqrt(rho**2 + z**2)), psi], inverse=False, fill_in_gaps=False) R3_s.connect_to(R3_c, [r, theta, phi], [r*sin(theta), phi, r*cos(theta)], inverse=False, fill_in_gaps=False) del x, y, z, rho, psi, r, theta, phi # Defining the basis coordinate functions. R3_r.x, R3_r.y, R3_r.z = R3_r.coord_functions() R3_c.rho, R3_c.psi, R3_c.z = R3_c.coord_functions() R3_s.r, R3_s.theta, R3_s.phi = R3_s.coord_functions() # Defining the basis vector fields. R3_r.e_x, R3_r.e_y, R3_r.e_z = R3_r.base_vectors() R3_c.e_rho, R3_c.e_psi, R3_c.e_z = R3_c.base_vectors() R3_s.e_r, R3_s.e_theta, R3_s.e_phi = R3_s.base_vectors() # Defining the basis oneform fields. R3_r.dx, R3_r.dy, R3_r.dz = R3_r.base_oneforms() R3_c.drho, R3_c.dpsi, R3_c.dz = R3_c.base_oneforms() R3_s.dr, R3_s.dtheta, R3_s.dphi = R3_s.base_oneforms() sympy-0.7.4.1/sympy/diffgeom/tests/0000755000175000017500000000000012253362407017347 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/diffgeom/tests/test_hyperbolic_space.py0000644000175000017500000000461712253362407024303 0ustar georgeskgeorgesk''' unit test describing the hyperbolic half-plane with the Poincare metric. This is a basic model of hyperbolic geometry on the (positive) half-space {(x,y) \in R^2 | y > 0} with the Riemannian metric ds^2 = (dx^2 + dy^2)/y^2 It has constant negative scalar curvature = -2 https://en.wikipedia.org/wiki/Poincare_half-plane_model ''' from sympy import diag from sympy.diffgeom import (twoform_to_matrix, metric_to_Christoffel_1st, metric_to_Christoffel_2nd, metric_to_Riemann_components, metric_to_Ricci_components) import sympy.diffgeom.rn def test_H2(): TP = sympy.diffgeom.TensorProduct R2 = sympy.diffgeom.rn.R2 y = R2.y dy = R2.dy dx = R2.dx g = (TP(dx, dx) + TP(dy, dy))*y**(-2) automat = twoform_to_matrix(g) mat = diag(y**(-2), y**(-2)) assert mat == automat gamma1 = metric_to_Christoffel_1st(g) assert gamma1[0][0][0] == 0 assert gamma1[0][0][1] == -y**(-3) assert gamma1[0][1][0] == -y**(-3) assert gamma1[0][1][1] == 0 assert gamma1[1][1][1] == -y**(-3) assert gamma1[1][1][0] == 0 assert gamma1[1][0][1] == 0 assert gamma1[1][0][0] == y**(-3) gamma2 = metric_to_Christoffel_2nd(g) assert gamma2[0][0][0] == 0 assert gamma2[0][0][1] == -y**(-1) assert gamma2[0][1][0] == -y**(-1) assert gamma2[0][1][1] == 0 assert gamma2[1][1][1] == -y**(-1) assert gamma2[1][1][0] == 0 assert gamma2[1][0][1] == 0 assert gamma2[1][0][0] == y**(-1) Rm = metric_to_Riemann_components(g) assert Rm[0][0][0][0] == 0 assert Rm[0][0][0][1] == 0 assert Rm[0][0][1][0] == 0 assert Rm[0][0][1][1] == 0 assert Rm[0][1][0][0] == 0 assert Rm[0][1][0][1] == -y**(-2) assert Rm[0][1][1][0] == y**(-2) assert Rm[0][1][1][1] == 0 assert Rm[1][0][0][0] == 0 assert Rm[1][0][0][1] == y**(-2) assert Rm[1][0][1][0] == -y**(-2) assert Rm[1][0][1][1] == 0 assert Rm[1][1][0][0] == 0 assert Rm[1][1][0][1] == 0 assert Rm[1][1][1][0] == 0 assert Rm[1][1][1][1] == 0 Ric = metric_to_Ricci_components(g) assert Ric[0][0] == -y**(-2) assert Ric[0][1] == 0 assert Ric[1][0] == 0 assert Ric[0][0] == -y**(-2) ## scalar curvature is -2 #TODO - it would be nice to have index contraction built-in R = (Ric[0][0] + Ric[1][1])*y**2 assert R == -2 ## Gauss curvature is -1 assert R/2 == -1 sympy-0.7.4.1/sympy/diffgeom/tests/test_diffgeom.py0000644000175000017500000001743212253362407022547 0ustar georgeskgeorgeskfrom sympy.diffgeom.rn import R2, R2_p, R2_r, R3, R3_r, R3_c, R3_s from sympy.diffgeom import (Manifold, Patch, CoordSystem, Point, Commutator, BaseScalarField, BaseVectorField, Differential, TensorProduct, WedgeProduct, BaseCovarDerivativeOp, CovarDerivativeOp, LieDerivative, covariant_order, contravariant_order, twoform_to_matrix, metric_to_Christoffel_1st, metric_to_Christoffel_2nd, metric_to_Riemann_components, metric_to_Ricci_components, intcurve_diffequ, intcurve_series) from sympy.core import Symbol, symbols, Function, Derivative, S from sympy.simplify import trigsimp, simplify from sympy.functions import sqrt, atan2, sin, cos from sympy.matrices import Matrix, eye from sympy.utilities.pytest import raises TP = TensorProduct def test_R2(): x0, y0, r0, theta0 = symbols('x0, y0, r0, theta0', real=True) point_r = R2_r.point([x0, y0]) point_p = R2_p.point([r0, theta0]) # r**2 = x**2 + y**2 assert (R2.r**2 - R2.x**2 - R2.y**2).rcall(point_r) == 0 assert trigsimp( (R2.r**2 - R2.x**2 - R2.y**2).rcall(point_p) ) == 0 assert trigsimp(R2.e_r(R2.x**2 + R2.y**2).rcall(point_p).doit()) == 2*r0 # polar->rect->polar == Id a, b = symbols('a b', positive=True) m = Matrix([[a], [b]]) #TODO assert m == R2_r.coord_tuple_transform_to(R2_p, R2_p.coord_tuple_transform_to(R2_r, [a, b])).applyfunc(simplify) assert m == R2_p.coord_tuple_transform_to( R2_r, R2_r.coord_tuple_transform_to(R2_p, m)).applyfunc(simplify) def test_R3(): a, b, c = symbols('a b c', positive=True) m = Matrix([[a], [b], [c]]) assert m == R3_c.coord_tuple_transform_to( R3_r, R3_r.coord_tuple_transform_to(R3_c, m)).applyfunc(simplify) #TODO assert m == R3_r.coord_tuple_transform_to(R3_c, R3_c.coord_tuple_transform_to(R3_r, m)).applyfunc(simplify) assert m == R3_s.coord_tuple_transform_to( R3_r, R3_r.coord_tuple_transform_to(R3_s, m)).applyfunc(simplify) #TODO assert m == R3_r.coord_tuple_transform_to(R3_s, R3_s.coord_tuple_transform_to(R3_r, m)).applyfunc(simplify) assert m == R3_s.coord_tuple_transform_to( R3_c, R3_c.coord_tuple_transform_to(R3_s, m)).applyfunc(simplify) #TODO assert m == R3_c.coord_tuple_transform_to(R3_s, R3_s.coord_tuple_transform_to(R3_c, m)).applyfunc(simplify) def test_point(): x, y = symbols('x, y') p = R2_r.point([x, y]) #TODO assert p.free_symbols() == set([x, y]) assert p.coords(R2_r) == p.coords() == Matrix([x, y]) assert p.coords(R2_p) == Matrix([sqrt(x**2 + y**2), atan2(y, x)]) def test_commutator(): assert Commutator(R2.e_x, R2.e_y) == 0 assert Commutator(R2.x*R2.e_x, R2.x*R2.e_x) == 0 assert Commutator(R2.x*R2.e_x, R2.x*R2.e_y) == R2.x*R2.e_y c = Commutator(R2.e_x, R2.e_r) assert c(R2.x) == R2.y*(R2.x**2 + R2.y**2)**(-1)*sin(R2.theta) def test_differential(): xdy = R2.x*R2.dy dxdy = Differential(xdy) assert xdy.rcall(None) == xdy assert dxdy(R2.e_x, R2.e_y) == 1 assert dxdy(R2.e_x, R2.x*R2.e_y) == R2.x assert Differential(dxdy) == 0 def test_products(): assert TensorProduct( R2.dx, R2.dy)(R2.e_x, R2.e_y) == R2.dx(R2.e_x)*R2.dy(R2.e_y) == 1 assert WedgeProduct(R2.dx, R2.dy)(R2.e_x, R2.e_y) == 1 assert TensorProduct(R2.dx, R2.dy)(None, R2.e_y) == R2.dx assert TensorProduct(R2.dx, R2.dy)(R2.e_x, None) == R2.dy assert TensorProduct(R2.dx, R2.dy)(R2.e_x) == R2.dy assert TensorProduct(R2.x, R2.dx) == R2.x*R2.dx def test_lie_derivative(): assert LieDerivative(R2.e_x, R2.y) == R2.e_x(R2.y) == 0 assert LieDerivative(R2.e_x, R2.x) == R2.e_x(R2.x) == 1 assert LieDerivative(R2.e_x, R2.e_x) == Commutator(R2.e_x, R2.e_x) == 0 assert LieDerivative(R2.e_x, R2.e_r) == Commutator(R2.e_x, R2.e_r) assert LieDerivative(R2.e_x + R2.e_y, R2.x) == 1 assert LieDerivative( R2.e_x, TensorProduct(R2.dx, R2.dy))(R2.e_x, R2.e_y) == 0 def test_covar_deriv(): ch = metric_to_Christoffel_2nd(TP(R2.dx, R2.dx) + TP(R2.dy, R2.dy)) cvd = BaseCovarDerivativeOp(R2_r, 0, ch) assert cvd(R2.x) == 1 assert cvd(R2.x*R2.e_x) == R2.e_x cvd = CovarDerivativeOp(R2.x*R2.e_x, ch) assert cvd(R2.x) == R2.x assert cvd(R2.x*R2.e_x) == R2.x*R2.e_x def test_intcurve_diffequ(): t = symbols('t') start_point = R2_r.point([1, 0]) vector_field = -R2.y*R2.e_x + R2.x*R2.e_y equations, init_cond = intcurve_diffequ(vector_field, t, start_point) assert str(equations) == '[f_1(t) + Derivative(f_0(t), t), -f_0(t) + Derivative(f_1(t), t)]' assert str(init_cond) == '[f_0(0) - 1, f_1(0)]' equations, init_cond = intcurve_diffequ(vector_field, t, start_point, R2_p) assert str( equations) == '[Derivative(f_0(t), t), Derivative(f_1(t), t) - 1]' assert str(init_cond) == '[f_0(0) - 1, f_1(0)]' def test_helpers_and_coordinate_dependent(): one_form = R2.dr + R2.dx two_form = Differential(R2.x*R2.dr + R2.r*R2.dx) three_form = Differential( R2.y*two_form) + Differential(R2.x*Differential(R2.r*R2.dr)) metric = TensorProduct(R2.dx, R2.dx) + TensorProduct(R2.dy, R2.dy) metric_ambig = TensorProduct(R2.dx, R2.dx) + TensorProduct(R2.dr, R2.dr) misform_a = TensorProduct(R2.dr, R2.dr) + R2.dr misform_b = R2.dr**4 misform_c = R2.dx*R2.dy twoform_not_sym = TensorProduct(R2.dx, R2.dx) + TensorProduct(R2.dx, R2.dy) twoform_not_TP = WedgeProduct(R2.dx, R2.dy) assert covariant_order(one_form) == 1 assert covariant_order(two_form) == 2 assert covariant_order(three_form) == 3 assert covariant_order(two_form + metric) == 2 assert covariant_order(two_form + metric_ambig) == 2 assert covariant_order(two_form + twoform_not_sym) == 2 assert covariant_order(two_form + twoform_not_TP) == 2 raises(ValueError, lambda: covariant_order(misform_a)) raises(ValueError, lambda: covariant_order(misform_b)) raises(ValueError, lambda: covariant_order(misform_c)) assert twoform_to_matrix(metric) == Matrix([[1, 0], [0, 1]]) assert twoform_to_matrix(twoform_not_sym) == Matrix([[1, 0], [1, 0]]) assert twoform_to_matrix(twoform_not_TP) == Matrix([[0, -1], [1, 0]]) raises(ValueError, lambda: twoform_to_matrix(one_form)) raises(ValueError, lambda: twoform_to_matrix(three_form)) raises(ValueError, lambda: twoform_to_matrix(metric_ambig)) raises(ValueError, lambda: metric_to_Christoffel_1st(twoform_not_sym)) raises(ValueError, lambda: metric_to_Christoffel_2nd(twoform_not_sym)) raises(ValueError, lambda: metric_to_Riemann_components(twoform_not_sym)) raises(ValueError, lambda: metric_to_Ricci_components(twoform_not_sym)) def test_correct_arguments(): raises(ValueError, lambda: R2.e_x(R2.e_x)) raises(ValueError, lambda: R2.e_x(R2.dx)) raises(ValueError, lambda: Commutator(R2.e_x, R2.x)) raises(ValueError, lambda: Commutator(R2.dx, R2.e_x)) raises(ValueError, lambda: Differential(Differential(R2.e_x))) raises(ValueError, lambda: R2.dx(R2.x)) raises(ValueError, lambda: TensorProduct(R2.e_x, R2.dx)) raises(ValueError, lambda: LieDerivative(R2.dx, R2.dx)) raises(ValueError, lambda: LieDerivative(R2.x, R2.dx)) raises(ValueError, lambda: CovarDerivativeOp(R2.dx, [])) raises(ValueError, lambda: CovarDerivativeOp(R2.x, [])) a = Symbol('a') raises(ValueError, lambda: intcurve_series(R2.dx, a, R2_r.point([1, 2]))) raises(ValueError, lambda: intcurve_series(R2.x, a, R2_r.point([1, 2]))) raises(ValueError, lambda: intcurve_diffequ(R2.dx, a, R2_r.point([1, 2]))) raises(ValueError, lambda: intcurve_diffequ(R2.x, a, R2_r.point([1, 2]))) raises(ValueError, lambda: contravariant_order(R2.e_x + R2.dx)) raises(ValueError, lambda: covariant_order(R2.e_x + R2.dx)) raises(ValueError, lambda: contravariant_order(R2.e_x*R2.e_y)) raises(ValueError, lambda: covariant_order(R2.dx*R2.dy)) sympy-0.7.4.1/sympy/diffgeom/tests/test_function_diffgeom_book.py0000644000175000017500000001224612253362407025464 0ustar georgeskgeorgeskfrom sympy.diffgeom.rn import R2, R2_p, R2_r, R3_r from sympy.diffgeom import (intcurve_series, intcurve_diffequ, Differential, WedgeProduct) from sympy.core import symbols, Function, Derivative from sympy.simplify import trigsimp, simplify from sympy.functions import sqrt, atan2, sin, cos from sympy.matrices import Matrix # Most of the functionality is covered in the # test_functional_diffgeom_ch* tests which are based on the # example from the paper of Sussman and Wisdom. # If they do not cover something, additional tests are added in other test # functions. # From "Functional Differential Geometry" as of 2011 # by Sussman and Wisdom. def test_functional_diffgeom_ch2(): x0, y0, r0, theta0 = symbols('x0, y0, r0, theta0', real=True) x, y = symbols('x, y', real=True) f = Function('f') assert (R2_p.point_to_coords(R2_r.point([x0, y0])) == Matrix([sqrt(x0**2 + y0**2), atan2(y0, x0)])) assert (R2_r.point_to_coords(R2_p.point([r0, theta0])) == Matrix([r0*cos(theta0), r0*sin(theta0)])) assert R2_p.jacobian(R2_r, [r0, theta0]) == Matrix( [[cos(theta0), -r0*sin(theta0)], [sin(theta0), r0*cos(theta0)]]) field = f(R2.x, R2.y) p1_in_rect = R2_r.point([x0, y0]) p1_in_polar = R2_p.point([sqrt(x0**2 + y0**2), atan2(y0, x0)]) assert field.rcall(p1_in_rect) == f(x0, y0) assert field.rcall(p1_in_polar) == f(x0, y0) p_r = R2_r.point([x0, y0]) p_p = R2_p.point([r0, theta0]) assert R2.x(p_r) == x0 assert R2.x(p_p) == r0*cos(theta0) assert R2.r(p_p) == r0 assert R2.r(p_r) == sqrt(x0**2 + y0**2) assert R2.theta(p_r) == atan2(y0, x0) h = R2.x*R2.r**2 + R2.y**3 assert h.rcall(p_r) == x0*(x0**2 + y0**2) + y0**3 assert h.rcall(p_p) == r0**3*sin(theta0)**3 + r0**3*cos(theta0) def test_functional_diffgeom_ch3(): x0, y0 = symbols('x0, y0', real=True) x, y, t = symbols('x, y, t', real=True) f = Function('f') b1 = Function('b1') b2 = Function('b2') p_r = R2_r.point([x0, y0]) s_field = f(R2.x, R2.y) v_field = b1(R2.x)*R2.e_x + b2(R2.y)*R2.e_y assert v_field.rcall(s_field).rcall(p_r).doit() == b1( x0)*Derivative(f(x0, y0), x0) + b2(y0)*Derivative(f(x0, y0), y0) assert R2.e_x(R2.r**2).rcall(p_r) == 2*x0 v = R2.e_x + 2*R2.e_y s = R2.r**2 + 3*R2.x assert v.rcall(s).rcall(p_r).doit() == 2*x0 + 4*y0 + 3 circ = -R2.y*R2.e_x + R2.x*R2.e_y series = intcurve_series(circ, t, R2_r.point([1, 0]), coeffs=True) series_x, series_y = zip(*series) assert all( [term == cos(t).taylor_term(i, t) for i, term in enumerate(series_x)]) assert all( [term == sin(t).taylor_term(i, t) for i, term in enumerate(series_y)]) def test_functional_diffgeom_ch4(): x0, y0, theta0 = symbols('x0, y0, theta0', real=True) x, y, r, theta = symbols('x, y, r, theta', real=True) r0 = symbols('r0', positive=True) f = Function('f') b1 = Function('b1') b2 = Function('b2') p_r = R2_r.point([x0, y0]) p_p = R2_p.point([r0, theta0]) f_field = b1(R2.x, R2.y)*R2.dx + b2(R2.x, R2.y)*R2.dy assert f_field.rcall(R2.e_x).rcall(p_r) == b1(x0, y0) assert f_field.rcall(R2.e_y).rcall(p_r) == b2(x0, y0) s_field_r = f(R2.x, R2.y) df = Differential(s_field_r) assert df(R2.e_x).rcall(p_r).doit() == Derivative(f(x0, y0), x0) assert df(R2.e_y).rcall(p_r).doit() == Derivative(f(x0, y0), y0) s_field_p = f(R2.r, R2.theta) df = Differential(s_field_p) assert trigsimp(df(R2.e_x).rcall(p_p).doit()) == ( cos(theta0)*Derivative(f(r0, theta0), r0) - sin(theta0)*Derivative(f(r0, theta0), theta0)/r0) assert trigsimp(df(R2.e_y).rcall(p_p).doit()) == ( sin(theta0)*Derivative(f(r0, theta0), r0) + cos(theta0)*Derivative(f(r0, theta0), theta0)/r0) assert R2.dx(R2.e_x).rcall(p_r) == 1 assert R2.dx(R2.e_x) == 1 assert R2.dx(R2.e_y).rcall(p_r) == 0 assert R2.dx(R2.e_y) == 0 circ = -R2.y*R2.e_x + R2.x*R2.e_y assert R2.dx(circ).rcall(p_r).doit() == -y0 assert R2.dy(circ).rcall(p_r) == x0 assert R2.dr(circ).rcall(p_r) == 0 assert simplify(R2.dtheta(circ).rcall(p_r)) == 1 assert (circ - R2.e_theta).rcall(s_field_r).rcall(p_r) == 0 def test_functional_diffgeom_ch6(): u0, u1, u2, v0, v1, v2, w0, w1, w2 = symbols('u0:3, v0:3, w0:3', real=True) u = u0*R2.e_x + u1*R2.e_y v = v0*R2.e_x + v1*R2.e_y wp = WedgeProduct(R2.dx, R2.dy) assert wp(u, v) == u0*v1 - u1*v0 u = u0*R3_r.e_x + u1*R3_r.e_y + u2*R3_r.e_z v = v0*R3_r.e_x + v1*R3_r.e_y + v2*R3_r.e_z w = w0*R3_r.e_x + w1*R3_r.e_y + w2*R3_r.e_z wp = WedgeProduct(R3_r.dx, R3_r.dy, R3_r.dz) assert wp( u, v, w) == Matrix(3, 3, [u0, u1, u2, v0, v1, v2, w0, w1, w2]).det() a, b, c = symbols('a, b, c', cls=Function) a_f = a(R3_r.x, R3_r.y, R3_r.z) b_f = b(R3_r.x, R3_r.y, R3_r.z) c_f = c(R3_r.x, R3_r.y, R3_r.z) theta = a_f*R3_r.dx + b_f*R3_r.dy + c_f*R3_r.dz dtheta = Differential(theta) da = Differential(a_f) db = Differential(b_f) dc = Differential(c_f) expr = dtheta - WedgeProduct( da, R3_r.dx) - WedgeProduct(db, R3_r.dy) - WedgeProduct(dc, R3_r.dz) assert expr.rcall(R3_r.e_x, R3_r.e_y) == 0 sympy-0.7.4.1/sympy/diffgeom/tests/test_class_structure.py0000644000175000017500000000214412253362407024206 0ustar georgeskgeorgeskfrom sympy.diffgeom import Manifold, Patch, CoordSystem, Point from sympy import symbols, Function m = Manifold('m', 2) p = Patch('p', m) cs = CoordSystem('cs', p, ['a', 'b']) cs_noname = CoordSystem('cs', p) x, y = symbols('x y') f = Function('f') s1, s2 = cs.coord_functions() v1, v2 = cs.base_vectors() f1, f2 = cs.base_oneforms() def test_point(): point = Point(cs, [x, y]) assert point == point.func(*point.args) assert point != Point(cs, [2, y]) #TODO assert point.subs(x, 2) == Point(cs, [2, y]) #TODO assert point.free_symbols == set([x, y]) def test_rebuild(): assert m == m.func(*m.args) assert p == p.func(*p.args) assert cs == cs.func(*cs.args) assert cs_noname == cs_noname.func(*cs_noname.args) assert s1 == s1.func(*s1.args) assert v1 == v1.func(*v1.args) assert f1 == f1.func(*f1.args) def test_subs(): assert s1.subs(s1, s2) == s2 assert v1.subs(v1, v2) == v2 assert f1.subs(f1, f2) == f2 assert (x*f(s1) + y).subs(s1, s2) == x*f(s2) + y assert (f(s1)*v1).subs(v1, v2) == f(s1)*v2 assert (y*f(s1)*f1).subs(f1, f2) == y*f(s1)*f2 sympy-0.7.4.1/sympy/diffgeom/tests/__init__.py0000644000175000017500000000000012253362407021446 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/diffgeom/__init__.py0000644000175000017500000000071312253362407020317 0ustar georgeskgeorgeskfrom .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, ) sympy-0.7.4.1/sympy/benchmarks/0000755000175000017500000000000012253362407016542 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/benchmarks/bench_symbench.py0000644000175000017500000000573512253362407022075 0ustar georgeskgeorgesk#!/usr/bin/env python from __future__ import print_function, division from sympy.core.compatibility import xrange from random import random from sympy import factor, I, Integer, pi, simplify, sin, sqrt, Symbol, sympify from sympy.abc import x, y, z from timeit import default_timer as clock def bench_R1(): "real(f(f(f(f(f(f(f(f(f(f(i/2)))))))))))" def f(z): return sqrt(Integer(1)/3)*z**2 + I/3 e = f(f(f(f(f(f(f(f(f(f(I/2)))))))))).as_real_imag()[0] def bench_R2(): "Hermite polynomial hermite(15, y)" def hermite(n, y): if n == 1: return 2*y if n == 0: return 1 return (2*y*hermite(n - 1, y) - 2*(n - 1)*hermite(n - 2, y)).expand() #def phi(n, y): # return 1/(sqrt(2**n*factorial(n))*pi**(Integer(1)/4))*exp(-y**2/2)* \ # hermite(n,y) a = hermite(15, y) def bench_R3(): "a = [bool(f==f) for _ in range(10)]" f = x + y + z a = [bool(f == f) for _ in range(10)] def bench_R4(): # we don't have Tuples pass def bench_R5(): "blowup(L, 8); L=uniq(L)" def blowup(L, n): for i in range(n): L.append( (L[i] + L[i + 1]) * L[i + 2] ) def uniq(x): v = list(set(x)) v.sort() return v L = [x, y, z] blowup(L, 8) L = uniq(L) def bench_R6(): "sum(simplify((x+sin(i))/x+(x-sin(i))/x) for i in xrange(100))" s = sum(simplify((x + sin(i))/x + (x - sin(i))/x) for i in xrange(100)) def bench_R7(): "[f.subs(x, random()) for _ in xrange(10**4)]" f = x**24 + 34*x**12 + 45*x**3 + 9*x**18 + 34*x**10 + 32*x**21 a = [f.subs(x, random()) for _ in xrange(10**4)] def bench_R8(): "right(x^2,0,5,10^4)" def right(f, a, b, n): a = sympify(a) b = sympify(b) n = sympify(n) x = f.atoms(Symbol).pop() Deltax = (b - a)/n c = a est = 0 for i in range(n): c += Deltax est += f.subs(x, c) return est*Deltax a = right(x**2, 0, 5, 10**4) def _bench_R9(): "factor(x^20 - pi^5*y^20)" factor(x**20 - pi**5*y**20) def bench_R10(): "v = [-pi,-pi+1/10..,pi]" def srange(min, max, step): v = [min] while (max - v[-1]).evalf() > 0: v.append(v[-1] + step) return v[:-1] v = srange(-pi, pi, sympify(1)/10) def bench_R11(): "a = [random() + random()*I for w in [0..1000]]" a = [random() + random()*I for w in range(1000)] a.sort() def bench_S1(): "e=(x+y+z+1)**7;f=e*(e+1);f.expand()" e = (x + y + z + 1)**7 f = e*(e + 1) f = f.expand() if __name__ == '__main__': benchmarks = [ bench_R1, bench_R2, bench_R3, bench_R5, bench_R6, bench_R7, bench_R8, #_bench_R9, bench_R10, bench_R11, #bench_S1, ] report = [] for b in benchmarks: t = clock() b() t = clock() - t print("%s%65s: %f" % (b.__name__, b.__doc__, t)) sympy-0.7.4.1/sympy/benchmarks/bench_meijerint.py0000644000175000017500000002564512253362407022255 0ustar georgeskgeorgesk# conceal the implicit import from the code quality tester from __future__ import print_function, division exec("from sympy import *") LT = laplace_transform FT = fourier_transform MT = mellin_transform IFT = inverse_fourier_transform ILT = inverse_laplace_transform IMT = inverse_mellin_transform from sympy.abc import x, s, a, b, c, d, t, y, z nu, beta, rho = symbols('nu beta rho') apos, bpos, cpos, dpos, posk, p = symbols('a b c d k p', positive=True) k = Symbol('k', real=True) negk = Symbol('k', negative=True) mu1, mu2 = symbols('mu1 mu2', real=True, finite=True, bounded=True) sigma1, sigma2 = symbols('sigma1 sigma2', real=True, finite=True, bounded=True, positive=True) rate = Symbol('lambda', real=True, positive=True, bounded=True) def normal(x, mu, sigma): return 1/sqrt(2*pi*sigma**2)*exp(-(x - mu)**2/2/sigma**2) def exponential(x, rate): return rate*exp(-rate*x) alpha, beta = symbols('alpha beta', positive=True) betadist = x**(alpha - 1)*(1 + x)**(-alpha - beta)*gamma(alpha + beta) \ /gamma(alpha)/gamma(beta) kint = Symbol('k', integer=True, positive=True) chi = 2**(1 - kint/2)*x**(kint - 1)*exp(-x**2/2)/gamma(kint/2) chisquared = 2**(-k/2)/gamma(k/2)*x**(k/2 - 1)*exp(-x/2) dagum = apos*p/x*(x/bpos)**(apos*p)/(1 + x**apos/bpos**apos)**(p + 1) d1, d2 = symbols('d1 d2', positive=True) f = sqrt(((d1*x)**d1 * d2**d2)/(d1*x + d2)**(d1 + d2))/x \ /gamma(d1/2)/gamma(d2/2)*gamma((d1 + d2)/2) nupos, sigmapos = symbols('nu sigma', positive=True) rice = x/sigmapos**2*exp(-(x**2 + nupos**2)/2/sigmapos**2)*besseli(0, x* nupos/sigmapos**2) mu = Symbol('mu', real=True) laplace = exp(-abs(x - mu)/bpos)/2/bpos u = Symbol('u', polar=True) tpos = Symbol('t', positive=True) from sympy import Chi as cosint def E(expr): res1 = integrate(expr*exponential(x, rate)*normal(y, mu1, sigma1), (x, 0, oo), (y, -oo, oo), meijerg=True) res2 = integrate(expr*exponential(x, rate)*normal(y, mu1, sigma1), (y, -oo, oo), (x, 0, oo), meijerg=True) bench = [ 'MT(x**nu*Heaviside(x - 1), x, s)', 'MT(x**nu*Heaviside(1 - x), x, s)', 'MT((1-x)**(beta - 1)*Heaviside(1-x), x, s)', 'MT((x-1)**(beta - 1)*Heaviside(x-1), x, s)', 'MT((1+x)**(-rho), x, s)', 'MT(abs(1-x)**(-rho), x, s)', 'MT((1-x)**(beta-1)*Heaviside(1-x) + a*(x-1)**(beta-1)*Heaviside(x-1), x, s)', 'MT((x**a-b**a)/(x-b), x, s)', 'MT((x**a-bpos**a)/(x-bpos), x, s)', 'MT(exp(-x), x, s)', 'MT(exp(-1/x), x, s)', 'MT(log(x)**4*Heaviside(1-x), x, s)', 'MT(log(x)**3*Heaviside(x-1), x, s)', 'MT(log(x + 1), x, s)', 'MT(log(1/x + 1), x, s)', 'MT(log(abs(1 - x)), x, s)', 'MT(log(abs(1 - 1/x)), x, s)', 'MT(log(x)/(x+1), x, s)', 'MT(log(x)**2/(x+1), x, s)', 'MT(log(x)/(x+1)**2, x, s)', 'MT(erf(sqrt(x)), x, s)', 'MT(besselj(a, 2*sqrt(x)), x, s)', 'MT(sin(sqrt(x))*besselj(a, sqrt(x)), x, s)', 'MT(cos(sqrt(x))*besselj(a, sqrt(x)), x, s)', 'MT(besselj(a, sqrt(x))**2, x, s)', 'MT(besselj(a, sqrt(x))*besselj(-a, sqrt(x)), x, s)', 'MT(besselj(a - 1, sqrt(x))*besselj(a, sqrt(x)), x, s)', 'MT(besselj(a, sqrt(x))*besselj(b, sqrt(x)), x, s)', 'MT(besselj(a, sqrt(x))**2 + besselj(-a, sqrt(x))**2, x, s)', 'MT(bessely(a, 2*sqrt(x)), x, s)', 'MT(sin(sqrt(x))*bessely(a, sqrt(x)), x, s)', 'MT(cos(sqrt(x))*bessely(a, sqrt(x)), x, s)', 'MT(besselj(a, sqrt(x))*bessely(a, sqrt(x)), x, s)', 'MT(besselj(a, sqrt(x))*bessely(b, sqrt(x)), x, s)', 'MT(bessely(a, sqrt(x))**2, x, s)', 'MT(besselk(a, 2*sqrt(x)), x, s)', 'MT(besselj(a, 2*sqrt(2*sqrt(x)))*besselk(a, 2*sqrt(2*sqrt(x))), x, s)', 'MT(besseli(a, sqrt(x))*besselk(a, sqrt(x)), x, s)', 'MT(besseli(b, sqrt(x))*besselk(a, sqrt(x)), x, s)', 'MT(exp(-x/2)*besselk(a, x/2), x, s)', # later: ILT, IMT 'LT((t-apos)**bpos*exp(-cpos*(t-apos))*Heaviside(t-apos), t, s)', 'LT(t**apos, t, s)', 'LT(Heaviside(t), t, s)', 'LT(Heaviside(t - apos), t, s)', 'LT(1 - exp(-apos*t), t, s)', 'LT((exp(2*t)-1)*exp(-bpos - t)*Heaviside(t)/2, t, s, noconds=True)', 'LT(exp(t), t, s)', 'LT(exp(2*t), t, s)', 'LT(exp(apos*t), t, s)', 'LT(log(t/apos), t, s)', 'LT(erf(t), t, s)', 'LT(sin(apos*t), t, s)', 'LT(cos(apos*t), t, s)', 'LT(exp(-apos*t)*sin(bpos*t), t, s)', 'LT(exp(-apos*t)*cos(bpos*t), t, s)', 'LT(besselj(0, t), t, s, noconds=True)', 'LT(besselj(1, t), t, s, noconds=True)', 'FT(Heaviside(1 - abs(2*apos*x)), x, k)', 'FT(Heaviside(1-abs(apos*x))*(1-abs(apos*x)), x, k)', 'FT(exp(-apos*x)*Heaviside(x), x, k)', 'IFT(1/(apos + 2*pi*I*x), x, posk, noconds=False)', 'IFT(1/(apos + 2*pi*I*x), x, -posk, noconds=False)', 'IFT(1/(apos + 2*pi*I*x), x, negk)', 'FT(x*exp(-apos*x)*Heaviside(x), x, k)', 'FT(exp(-apos*x)*sin(bpos*x)*Heaviside(x), x, k)', 'FT(exp(-apos*x**2), x, k)', 'IFT(sqrt(pi/apos)*exp(-(pi*k)**2/apos), k, x)', 'FT(exp(-apos*abs(x)), x, k)', 'integrate(normal(x, mu1, sigma1), (x, -oo, oo), meijerg=True)', 'integrate(x*normal(x, mu1, sigma1), (x, -oo, oo), meijerg=True)', 'integrate(x**2*normal(x, mu1, sigma1), (x, -oo, oo), meijerg=True)', 'integrate(x**3*normal(x, mu1, sigma1), (x, -oo, oo), meijerg=True)', 'integrate(normal(x, mu1, sigma1)*normal(y, mu2, sigma2),' ' (x, -oo, oo), (y, -oo, oo), meijerg=True)', 'integrate(x*normal(x, mu1, sigma1)*normal(y, mu2, sigma2),' ' (x, -oo, oo), (y, -oo, oo), meijerg=True)', 'integrate(y*normal(x, mu1, sigma1)*normal(y, mu2, sigma2),' ' (x, -oo, oo), (y, -oo, oo), meijerg=True)', 'integrate(x*y*normal(x, mu1, sigma1)*normal(y, mu2, sigma2),' ' (x, -oo, oo), (y, -oo, oo), meijerg=True)', 'integrate((x+y+1)*normal(x, mu1, sigma1)*normal(y, mu2, sigma2),' ' (x, -oo, oo), (y, -oo, oo), meijerg=True)', 'integrate((x+y-1)*normal(x, mu1, sigma1)*normal(y, mu2, sigma2),' ' (x, -oo, oo), (y, -oo, oo), meijerg=True)', 'integrate(x**2*normal(x, mu1, sigma1)*normal(y, mu2, sigma2),' ' (x, -oo, oo), (y, -oo, oo), meijerg=True)', 'integrate(y**2*normal(x, mu1, sigma1)*normal(y, mu2, sigma2),' ' (x, -oo, oo), (y, -oo, oo), meijerg=True)', 'integrate(exponential(x, rate), (x, 0, oo), meijerg=True)', 'integrate(x*exponential(x, rate), (x, 0, oo), meijerg=True)', 'integrate(x**2*exponential(x, rate), (x, 0, oo), meijerg=True)', 'E(1)', 'E(x*y)', 'E(x*y**2)', 'E((x+y+1)**2)', 'E(x+y+1)', 'E((x+y-1)**2)', 'integrate(betadist, (x, 0, oo), meijerg=True)', 'integrate(x*betadist, (x, 0, oo), meijerg=True)', 'integrate(x**2*betadist, (x, 0, oo), meijerg=True)', 'integrate(chi, (x, 0, oo), meijerg=True)', 'integrate(x*chi, (x, 0, oo), meijerg=True)', 'integrate(x**2*chi, (x, 0, oo), meijerg=True)', 'integrate(chisquared, (x, 0, oo), meijerg=True)', 'integrate(x*chisquared, (x, 0, oo), meijerg=True)', 'integrate(x**2*chisquared, (x, 0, oo), meijerg=True)', 'integrate(((x-k)/sqrt(2*k))**3*chisquared, (x, 0, oo), meijerg=True)', 'integrate(dagum, (x, 0, oo), meijerg=True)', 'integrate(x*dagum, (x, 0, oo), meijerg=True)', 'integrate(x**2*dagum, (x, 0, oo), meijerg=True)', 'integrate(f, (x, 0, oo), meijerg=True)', 'integrate(x*f, (x, 0, oo), meijerg=True)', 'integrate(x**2*f, (x, 0, oo), meijerg=True)', 'integrate(rice, (x, 0, oo), meijerg=True)', 'integrate(laplace, (x, -oo, oo), meijerg=True)', 'integrate(x*laplace, (x, -oo, oo), meijerg=True)', 'integrate(x**2*laplace, (x, -oo, oo), meijerg=True)', 'integrate(log(x) * x**(k-1) * exp(-x) / gamma(k), (x, 0, oo))', 'integrate(sin(z*x)*(x**2-1)**(-(y+S(1)/2)), (x, 1, oo), meijerg=True)', 'integrate(besselj(0,x)*besselj(1,x)*exp(-x**2), (x, 0, oo), meijerg=True)', 'integrate(besselj(0,x)*besselj(1,x)*besselk(0,x), (x, 0, oo), meijerg=True)', 'integrate(besselj(0,x)*besselj(1,x)*exp(-x**2), (x, 0, oo), meijerg=True)', 'integrate(besselj(a,x)*besselj(b,x)/x, (x,0,oo), meijerg=True)', 'hyperexpand(meijerg((-s - a/2 + 1, -s + a/2 + 1), (-a/2 - S(1)/2, -s + a/2 + S(3)/2), (a/2, -a/2), (-a/2 - S(1)/2, -s + a/2 + S(3)/2), 1))', "combsimp(S('2**(2*s)*(-pi*gamma(-a + 1)*gamma(a + 1)*gamma(-a - s + 1)*gamma(-a + s - 1/2)*gamma(a - s + 3/2)*gamma(a + s + 1)/(a*(a + s)) - gamma(-a - 1/2)*gamma(-a + 1)*gamma(a + 1)*gamma(a + 3/2)*gamma(-s + 3/2)*gamma(s - 1/2)*gamma(-a + s + 1)*gamma(a - s + 1)/(a*(-a + s)))*gamma(-2*s + 1)*gamma(s + 1)/(pi*s*gamma(-a - 1/2)*gamma(a + 3/2)*gamma(-s + 1)*gamma(-s + 3/2)*gamma(s - 1/2)*gamma(-a - s + 1)*gamma(-a + s - 1/2)*gamma(a - s + 1)*gamma(a - s + 3/2))'))", 'mellin_transform(E1(x), x, s)', 'inverse_mellin_transform(gamma(s)/s, s, x, (0, oo))', 'mellin_transform(expint(a, x), x, s)', 'mellin_transform(Si(x), x, s)', 'inverse_mellin_transform(-2**s*sqrt(pi)*gamma((s + 1)/2)/(2*s*gamma(-s/2 + 1)), s, x, (-1, 0))', 'mellin_transform(Ci(sqrt(x)), x, s)', 'inverse_mellin_transform(-4**s*sqrt(pi)*gamma(s)/(2*s*gamma(-s + S(1)/2)),s, u, (0, 1))', 'laplace_transform(Ci(x), x, s)', 'laplace_transform(expint(a, x), x, s)', 'laplace_transform(expint(1, x), x, s)', 'laplace_transform(expint(2, x), x, s)', 'inverse_laplace_transform(-log(1 + s**2)/2/s, s, u)', 'inverse_laplace_transform(log(s + 1)/s, s, x)', 'inverse_laplace_transform((s - log(s + 1))/s**2, s, x)', 'laplace_transform(Chi(x), x, s)', 'laplace_transform(Shi(x), x, s)', 'integrate(exp(-z*x)/x, (x, 1, oo), meijerg=True, conds="none")', 'integrate(exp(-z*x)/x**2, (x, 1, oo), meijerg=True, conds="none")', 'integrate(exp(-z*x)/x**3, (x, 1, oo), meijerg=True,conds="none")', 'integrate(-cos(x)/x, (x, tpos, oo), meijerg=True)', 'integrate(-sin(x)/x, (x, tpos, oo), meijerg=True)', 'integrate(sin(x)/x, (x, 0, z), meijerg=True)', 'integrate(sinh(x)/x, (x, 0, z), meijerg=True)', 'integrate(exp(-x)/x, x, meijerg=True)', 'integrate(exp(-x)/x**2, x, meijerg=True)', 'integrate(cos(u)/u, u, meijerg=True)', 'integrate(cosh(u)/u, u, meijerg=True)', 'integrate(expint(1, x), x, meijerg=True)', 'integrate(expint(2, x), x, meijerg=True)', 'integrate(Si(x), x, meijerg=True)', 'integrate(Ci(u), u, meijerg=True)', 'integrate(Shi(x), x, meijerg=True)', 'integrate(cosint(u), u, meijerg=True)', 'integrate(Si(x)*exp(-x), (x, 0, oo), meijerg=True)', 'integrate(expint(1, x)*sin(x), (x, 0, oo), meijerg=True)' ] from time import time from sympy.core.cache import clear_cache import sys timings = [] for n, string in enumerate(bench): #print string clear_cache() _t = time() exec(string) _t = time() - _t timings += [(_t, string)] sys.stdout.write('.') sys.stdout.flush() if n % (len(bench) // 10) == 0: sys.stdout.write('%s' % (10*n // len(bench))) print() timings.sort(key=lambda x: -x[0]) for t, string in timings: print('%.2fs %s' % (t, string)) sympy-0.7.4.1/sympy/abc.py0000644000175000017500000000352512253362407015531 0ustar georgeskgeorgeskfrom __future__ import print_function, division import string from .core import Symbol from .core.alphabets import greeks from .core.compatibility import exec_ _latin = list(string.ascii_letters) # COSINEQ should not be imported as they clash; gamma, pi and zeta clash, too _greek = list(greeks) # make a copy, so we can mutate it # Note: We import lamda since lambda is a reserved keyword in Python _greek.remove("lambda") _greek.append("lamda") for _s in _latin + _greek: exec_("%s = Symbol('%s')" % (_s, _s)) def clashing(): """Return the clashing-symbols dictionaries. ``clash1`` defines all the single letter variables that clash with SymPy objects; ``clash2`` defines the multi-letter clashing symbols; and ``clash`` is the union of both. These can be passed for ``locals`` during sympification if one desires Symbols rather than the non-Symbol objects for those names. Examples ======== >>> from sympy import S >>> from sympy.abc import _clash1, _clash2, _clash >>> S("Q & C", locals=_clash1) And(C, Q) >>> S('pi(x)', locals=_clash2) pi(x) >>> S('pi(C, Q)', locals=_clash) pi(C, Q) Note: if changes are made to the docstring examples they can only be tested after removing "clashing" from the list of deleted items at the bottom of this file which removes this function from the namespace. """ ns = {} exec_('from sympy import *', ns) clash1 = {} clash2 = {} while ns: k, _ = ns.popitem() if k in _greek: clash2[k] = Symbol(k) _greek.remove(k) elif k in _latin: clash1[k] = Symbol(k) _latin.remove(k) clash = {} clash.update(clash1) clash.update(clash2) return clash1, clash2, clash _clash1, _clash2, _clash = clashing() del _latin, _greek, _s, clashing, Symbol sympy-0.7.4.1/sympy/external/0000755000175000017500000000000012253362407016247 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/external/importtools.py0000644000175000017500000001570312253362407021222 0ustar georgeskgeorgesk"""Tools to assist importing optional external modules.""" from __future__ import print_function, division import sys # Override these in the module to change the default warning behavior. # For example, you might set both to False before running the tests so that # warnings are not printed to the console, or set both to True for debugging. WARN_NOT_INSTALLED = None # Default is False WARN_OLD_VERSION = None # Default is True def __sympy_debug(): # helper function from sympy/__init__.py # We don't just import SYMPY_DEBUG from that file because we don't want to # import all of sympy just to use this module. import os return eval(os.getenv('SYMPY_DEBUG', 'False')) if __sympy_debug(): WARN_OLD_VERSION = True WARN_NOT_INSTALLED = True def import_module(module, min_module_version=None, min_python_version=None, warn_not_installed=None, warn_old_version=None, module_version_attr='__version__', module_version_attr_call_args=None, __import__kwargs={}, catch=()): """ Import and return a module if it is installed. If the module is not installed, it returns None. A minimum version for the module can be given as the keyword argument min_module_version. This should be comparable against the module version. By default, module.__version__ is used to get the module version. To override this, set the module_version_attr keyword argument. If the attribute of the module to get the version should be called (e.g., module.version()), then set module_version_attr_call_args to the args such that module.module_version_attr(*module_version_attr_call_args) returns the module's version. If the module version is less than min_module_version using the Python < comparison, None will be returned, even if the module is installed. You can use this to keep from importing an incompatible older version of a module. You can also specify a minimum Python version by using the min_python_version keyword argument. This should be comparable against sys.version_info. If the keyword argument warn_not_installed is set to True, the function will emit a UserWarning when the module is not installed. If the keyword argument warn_old_version is set to True, the function will emit a UserWarning when the library is installed, but cannot be imported because of the min_module_version or min_python_version options. Note that because of the way warnings are handled, a warning will be emitted for each module only once. You can change the default warning behavior by overriding the values of WARN_NOT_INSTALLED and WARN_OLD_VERSION in sympy.external.importtools. By default, WARN_NOT_INSTALLED is False and WARN_OLD_VERSION is True. This function uses __import__() to import the module. To pass additional options to __import__(), use the __import__kwargs keyword argument. For example, to import a submodule A.B, you must pass a nonempty fromlist option to __import__. See the docstring of __import__(). This catches ImportError to determine if the module is not installed. To catch additional errors, pass them as a tuple to the catch keyword argument. Examples ======== >>> from sympy.external import import_module >>> numpy = import_module('numpy') >>> numpy = import_module('numpy', min_python_version=(2, 7), ... warn_old_version=False) >>> numpy = import_module('numpy', min_module_version='1.5', ... warn_old_version=False) # numpy.__version__ is a string >>> # gmpy does not have __version__, but it does have gmpy.version() >>> gmpy = import_module('gmpy', min_module_version='1.14', ... module_version_attr='version', module_version_attr_call_args=(), ... warn_old_version=False) >>> # To import a submodule, you must pass a nonempty fromlist to >>> # __import__(). The values do not matter. >>> p3 = import_module('mpl_toolkits.mplot3d', ... __import__kwargs={'fromlist':['something']}) >>> # matplotlib.pyplot can raise RuntimeError when the display cannot be opened >>> matplotlib = import_module('matplotlib', ... __import__kwargs={'fromlist':['pyplot']}, catch=(RuntimeError,)) """ # keyword argument overrides default, and global variable overrides # keyword argument. warn_old_version = (WARN_OLD_VERSION if WARN_OLD_VERSION is not None else warn_old_version or True) warn_not_installed = (WARN_NOT_INSTALLED if WARN_NOT_INSTALLED is not None else warn_not_installed or False) import warnings # Check Python first so we don't waste time importing a module we can't use if min_python_version: if sys.version_info < min_python_version: if warn_old_version: warnings.warn("Python version is too old to use %s " "(%s or newer required)" % ( module, '.'.join(map(str, min_python_version))), UserWarning) return # PyPy 1.6 has rudimentary NumPy support and importing it produces errors, so skip it if module == 'numpy' and '__pypy__' in sys.builtin_module_names: return try: mod = __import__(module, **__import__kwargs) ## there's something funny about imports with matplotlib and py3k. doing ## from matplotlib import collections ## gives python's stdlib collections module. explicitly re-importing ## the module fixes this. from_list = __import__kwargs.get('fromlist', tuple()) for submod in from_list: if submod == 'collections' and mod.__name__ == 'matplotlib': __import__(module + '.' + submod) except ImportError: if warn_not_installed: warnings.warn("%s module is not installed" % module, UserWarning) return except catch as e: if warn_not_installed: warnings.warn( "%s module could not be used (%s)" % (module, repr(e))) return if min_module_version: modversion = getattr(mod, module_version_attr) if module_version_attr_call_args is not None: modversion = modversion(*module_version_attr_call_args) if modversion < min_module_version: if warn_old_version: # Attempt to create a pretty string version of the version if isinstance(min_module_version, basestring): verstr = min_module_version elif isinstance(min_module_version, (tuple, list)): verstr = '.'.join(map(str, min_module_version)) else: # Either don't know what this is. Hopefully # it's something that has a nice str version, like an int. verstr = str(min_module_version) warnings.warn("%s version is too old to use " "(%s or newer required)" % (module, verstr), UserWarning) return return mod sympy-0.7.4.1/sympy/external/tests/0000755000175000017500000000000012253362407017411 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/external/tests/test_scipy.py0000644000175000017500000000244012253362407022151 0ustar georgeskgeorgesk# This testfile tests SymPy <-> SciPy compatibility # Don't test any SymPy features here. Just pure interaction with SciPy. # Always write regular SymPy tests for anything, that can be tested in pure # Python (without scipy). Here we test everything, that a user may need when # using SymPy with SciPy from sympy.external import import_module scipy = import_module('scipy') if not scipy: #bin/test will not execute any tests now disabled = True def setup_module(module): """py.test support""" if getattr(module, 'disabled', False): import pytest pytest.skip("scipy isn't available.") from sympy import jn_zeros def eq(a, b, tol=1e-6): for x, y in zip(a, b): if not (abs(x - y) < tol): return False return True def test_jn_zeros(): assert eq(jn_zeros(0, 4, method="scipy"), [3.141592, 6.283185, 9.424777, 12.566370]) assert eq(jn_zeros(1, 4, method="scipy"), [4.493409, 7.725251, 10.904121, 14.066193]) assert eq(jn_zeros(2, 4, method="scipy"), [5.763459, 9.095011, 12.322940, 15.514603]) assert eq(jn_zeros(3, 4, method="scipy"), [6.987932, 10.417118, 13.698023, 16.923621]) assert eq(jn_zeros(4, 4, method="scipy"), [8.182561, 11.704907, 15.039664, 18.301255]) sympy-0.7.4.1/sympy/external/tests/test_autowrap.py0000644000175000017500000001030412253362407022662 0ustar georgeskgeorgeskfrom sympy import symbols, Eq from sympy.external import import_module from sympy.tensor import IndexedBase, Idx from sympy.utilities.autowrap import autowrap, ufuncify, CodeWrapError from sympy.utilities.pytest import XFAIL, skip numpy = import_module('numpy') Cython = import_module('Cython') f2py = import_module('numpy.f2py', __import__kwargs={'fromlist': ['f2py']}) f2pyworks = False if f2py: try: autowrap(symbols('x'), 'f95', 'f2py') except (CodeWrapError, ImportError, OSError): f2pyworks = False else: f2pyworks = True a, b, c = symbols('a b c') n, m, d = symbols('n m d', integer=True) A, B, C = symbols('A B C', cls=IndexedBase) i = Idx('i', m) j = Idx('j', n) k = Idx('k', d) def has_module(module): """ Return True if module exists, otherwise run skip(). module should be a string. """ # To give a string of the module name to skip(), this function takes a # string. So we don't waste time running import_module() more than once, # just map the three modules tested here in this dict. modnames = {'numpy': numpy, 'Cython': Cython, 'f2py': f2py} if modnames[module]: if module == 'f2py' and not f2pyworks: skip("Couldn't run f2py.") return True skip("Couldn't import %s." % module) # # test runners used by several language-backend combinations # def runtest_autowrap_twice(language, backend): f = autowrap((((a + b)/c)**5).expand(), language, backend) g = autowrap((((a + b)/c)**4).expand(), language, backend) # check that autowrap updates the module name. Else, g gives the same as f assert f(1, -2, 1) == -1.0 assert g(1, -2, 1) == 1.0 def runtest_autowrap_trace(language, backend): has_module('numpy') trace = autowrap(A[i, i], language, backend) assert trace(numpy.eye(100)) == 100 def runtest_autowrap_matrix_vector(language, backend): has_module('numpy') x, y = symbols('x y', cls=IndexedBase) expr = Eq(y[i], A[i, j]*x[j]) mv = autowrap(expr, language, backend) # compare with numpy's dot product M = numpy.random.rand(10, 20) x = numpy.random.rand(20) y = numpy.dot(M, x) assert numpy.sum(numpy.abs(y - mv(M, x))) < 1e-13 def runtest_autowrap_matrix_matrix(language, backend): has_module('numpy') expr = Eq(C[i, j], A[i, k]*B[k, j]) matmat = autowrap(expr, language, backend) # compare with numpy's dot product M1 = numpy.random.rand(10, 20) M2 = numpy.random.rand(20, 15) M3 = numpy.dot(M1, M2) assert numpy.sum(numpy.abs(M3 - matmat(M1, M2))) < 1e-13 def runtest_ufuncify(language, backend): has_module('numpy') a, b, c = symbols('a b c') f = ufuncify([a, b, c], a*b + c, language=language, backend=backend) grid = numpy.linspace(-2, 2, 50) for b in numpy.linspace(-5, 4, 3): for c in numpy.linspace(-1, 1, 3): expected = grid*b + c assert numpy.sum(numpy.abs(expected - f(grid, b, c))) < 1e-13 # # tests of language-backend combinations # # f2py def test_wrap_twice_f95_f2py(): has_module('f2py') runtest_autowrap_twice('f95', 'f2py') def test_autowrap_trace_f95_f2py(): has_module('f2py') runtest_autowrap_trace('f95', 'f2py') def test_autowrap_matrix_vector_f95_f2py(): has_module('f2py') runtest_autowrap_matrix_vector('f95', 'f2py') def test_autowrap_matrix_matrix_f95_f2py(): has_module('f2py') runtest_autowrap_matrix_matrix('f95', 'f2py') def test_ufuncify_f95_f2py(): has_module('f2py') runtest_ufuncify('f95', 'f2py') # Cython # See issue 3008. This XFAIL can be removed if we can accurately determine the # correct minimum Cython version required. @XFAIL def test_wrap_twice_c_cython(): has_module('Cython') runtest_autowrap_twice('C', 'cython') @XFAIL def test_autowrap_trace_C_Cython(): has_module('Cython') runtest_autowrap_trace('C', 'cython') @XFAIL def test_autowrap_matrix_vector_C_cython(): has_module('Cython') runtest_autowrap_matrix_vector('C', 'cython') @XFAIL def test_autowrap_matrix_matrix_C_cython(): has_module('Cython') runtest_autowrap_matrix_matrix('C', 'cython') @XFAIL def test_ufuncify_C_Cython(): has_module('Cython') runtest_ufuncify('C', 'cython') sympy-0.7.4.1/sympy/external/tests/test_sage.py0000644000175000017500000001203012253362407021735 0ustar georgeskgeorgesk# This testfile tests SymPy <-> Sage compatibility # # Execute this test inside Sage, e.g. with: # sage -python bin/test sympy/external/tests/test_sage.py # # This file can be tested by Sage itself by: # sage -t sympy/external/tests/test_sage.py # and if all tests pass, it should be copied (verbatim) to Sage, so that it is # automatically doctested by Sage. Note that this second method imports the # version of SymPy in Sage, whereas the -python method imports the local version # of SymPy (both use the local version of the tests, however). # # Don't test any SymPy features here. Just pure interaction with Sage. # Always write regular SymPy tests for anything, that can be tested in pure # Python (without Sage). Here we test everything, that a user may need when # using SymPy with Sage. import os import re import sys from sympy.external import import_module sage = import_module('sage.all', __import__kwargs={'fromlist': ['all']}) if not sage: #bin/test will not execute any tests now disabled = True if sys.version_info[0] == 3: # Sage does not support Python 3 currently disabled = True def setup_module(module): """py.test support""" if getattr(module, 'disabled', False): import pytest pytest.skip("Sage isn't available.") import sympy from sympy.utilities.pytest import XFAIL def check_expression(expr, var_symbols): """Does eval(expr) both in Sage and SymPy and does other checks.""" # evaluate the expression in the context of Sage: if var_symbols: sage.var(var_symbols) a = globals().copy() # safety checks... assert not "sin" in a a.update(sage.__dict__) assert "sin" in a e_sage = eval(expr, a) assert not isinstance(e_sage, sympy.Basic) # evaluate the expression in the context of SymPy: if var_symbols: sympy.var(var_symbols) b = globals().copy() assert not "sin" in b b.update(sympy.__dict__) assert "sin" in b b.update(sympy.__dict__) e_sympy = eval(expr, b) assert isinstance(e_sympy, sympy.Basic) # Do the actual checks: assert sympy.S(e_sage) == e_sympy assert e_sage == sage.SR(e_sympy) def test_basics(): check_expression("x", "x") check_expression("x**2", "x") check_expression("x**2+y**3", "x y") check_expression("1/(x+y)**2-x**3/4", "x y") def test_complex(): check_expression("I", "") check_expression("23+I*4", "x") @XFAIL def test_complex_fail(): # Sage doesn't properly implement _sympy_ on I check_expression("I*y", "y") check_expression("x+I*y", "x y") def test_integer(): check_expression("4*x", "x") check_expression("-4*x", "x") def test_real(): check_expression("1.123*x", "x") check_expression("-18.22*x", "x") def test_E(): assert sympy.sympify(sage.e) == sympy.E assert sage.e == sage.SR(sympy.E) def test_pi(): assert sympy.sympify(sage.pi) == sympy.pi assert sage.pi == sage.SR(sympy.pi) def test_euler_gamma(): assert sympy.sympify(sage.euler_gamma) == sympy.EulerGamma assert sage.euler_gamma == sage.SR(sympy.EulerGamma) def test_oo(): assert sympy.sympify(sage.oo) == sympy.oo assert sage.oo == sage.SR(sympy.oo) assert sympy.sympify(-sage.oo) == -sympy.oo assert -sage.oo == sage.SR(-sympy.oo) def test_NaN(): assert sympy.sympify(sage.NaN) == sympy.nan assert sage.NaN == sage.SR(sympy.nan) def test_Catalan(): assert sympy.sympify(sage.catalan) == sympy.Catalan assert sage.catalan == sage.SR(sympy.Catalan) def test_GoldenRation(): assert sympy.sympify(sage.golden_ratio) == sympy.GoldenRatio assert sage.golden_ratio == sage.SR(sympy.GoldenRatio) def test_functions(): check_expression("sin(x)", "x") check_expression("cos(x)", "x") check_expression("tan(x)", "x") check_expression("cot(x)", "x") check_expression("asin(x)", "x") check_expression("acos(x)", "x") check_expression("atan(x)", "x") check_expression("atan2(y, x)", "x, y") check_expression("acot(x)", "x") check_expression("sinh(x)", "x") check_expression("cosh(x)", "x") check_expression("tanh(x)", "x") check_expression("coth(x)", "x") check_expression("asinh(x)", "x") check_expression("acosh(x)", "x") check_expression("atanh(x)", "x") check_expression("acoth(x)", "x") check_expression("exp(x)", "x") check_expression("log(x)", "x") def test_issue924(): sage.var("a x") log = sage.log i = sympy.integrate(log(x)/a, (x, a, a + 1)) i2 = sympy.simplify(i) s = sage.SR(i2) assert s == (a*log(1 + a) - a*log(a) + log(1 + a) - 1)/a # This string contains Sage doctests, that execute all the functions above. # When you add a new function, please add it here as well. """ TESTS:: sage: test_basics() sage: test_basics() sage: test_complex() sage: test_integer() sage: test_real() sage: test_E() sage: test_pi() sage: test_euler_gamma() sage: test_oo() sage: test_NaN() sage: test_Catalan() sage: test_GoldenRation() sage: test_functions() sage: test_issue924() """ sympy-0.7.4.1/sympy/external/tests/test_importtools.py0000644000175000017500000000227712253362407023425 0ustar georgeskgeorgeskfrom sympy.external import import_module # fixes issue that arose in addressing issue 3434 def test_no_stdlib_collections(): ''' make sure we get the right collections when it is not part of a larger list ''' import collections matplotlib = import_module('matplotlib', __import__kwargs={'fromlist': ['cm', 'collections']}, min_module_version='1.1.0', catch=(RuntimeError,)) if matplotlib: assert collections != matplotlib.collections def test_no_stdlib_collections2(): ''' make sure we get the right collections when it is not part of a larger list ''' import collections matplotlib = import_module('matplotlib', __import__kwargs={'fromlist': ['collections']}, min_module_version='1.1.0', catch=(RuntimeError,)) if matplotlib: assert collections != matplotlib.collections def test_no_stdlib_collections3(): '''make sure we get the right collections with no catch''' import collections matplotlib = import_module('matplotlib', __import__kwargs={'fromlist': ['cm', 'collections']}, min_module_version='1.1.0') if matplotlib: assert collections != matplotlib.collections sympy-0.7.4.1/sympy/external/tests/test_codegen.py0000644000175000017500000002710712253362407022435 0ustar georgeskgeorgesk# This tests the compilation and execution of the source code generated with # utilities.codegen. The compilation takes place in a temporary directory that # is removed after the test. By default the test directory is always removed, # but this behavior can be changed by setting the environment variable # SYMPY_TEST_CLEAN_TEMP to: # export SYMPY_TEST_CLEAN_TEMP=always : the default behavior. # export SYMPY_TEST_CLEAN_TEMP=success : only remove the directories of working tests. # export SYMPY_TEST_CLEAN_TEMP=never : never remove the directories with the test code. # When a directory is not removed, the necessary information is printed on # screen to find the files that belong to the (failed) tests. If a test does # not fail, py.test captures all the output and you will not see the directories # corresponding to the successful tests. Use the --nocapture option to see all # the output. # All tests below have a counterpart in utilities/test/test_codegen.py. In the # latter file, the resulting code is compared with predefined strings, without # compilation or execution. # All the generated Fortran code should conform with the Fortran 95 standard, # and all the generated C code should be ANSI C, which facilitates the # incorporation in various projects. The tests below assume that the binary cc # is somewhere in the path and that it can compile ANSI C code. from __future__ import print_function from sympy.abc import x, y, z from sympy.utilities.pytest import skip from sympy.utilities.codegen import( codegen, Routine, InputArgument, Result, get_code_generator ) import sys import os import tempfile import subprocess # templates for the main program that will test the generated code. main_template = {} main_template['F95'] = """ program main include "codegen.h" integer :: result; result = 0 %(statements)s call exit(result) end program """ main_template['C'] = """ #include "codegen.h" #include #include int main() { int result = 0; %(statements)s return result; } """ # templates for the numerical tests numerical_test_template = {} numerical_test_template['C'] = """ if (fabs(%(call)s)>%(threshold)s) { printf("Numerical validation failed: %(call)s=%%e threshold=%(threshold)s\\n", %(call)s); result = -1; } """ numerical_test_template['F95'] = """ if (abs(%(call)s)>%(threshold)s) then write(6,"('Numerical validation failed:')") write(6,"('%(call)s=',e15.5,'threshold=',e15.5)") %(call)s, %(threshold)s result = -1; end if """ # command sequences for supported compilers compile_commands = {} compile_commands['cc'] = [ "cc -c codegen.c -o codegen.o", "cc -c main.c -o main.o", "cc main.o codegen.o -lm -o test.exe" ] compile_commands['gfortran'] = [ "gfortran -c codegen.f90 -o codegen.o", "gfortran -ffree-line-length-none -c main.f90 -o main.o", "gfortran main.o codegen.o -o test.exe" ] compile_commands['g95'] = [ "g95 -c codegen.f90 -o codegen.o", "g95 -ffree-line-length-huge -c main.f90 -o main.o", "g95 main.o codegen.o -o test.exe" ] compile_commands['ifort'] = [ "ifort -c codegen.f90 -o codegen.o", "ifort -c main.f90 -o main.o", "ifort main.o codegen.o -o test.exe" ] combinations_lang_compiler = [ ('C', 'cc'), ('F95', 'ifort'), ('F95', 'gfortran'), ('F95', 'g95') ] def try_run(commands): """Run a series of commands and only return True if all ran fine.""" null = open(os.devnull, 'w') for command in commands: retcode = subprocess.call(command, stdout=null, shell=True, stderr=subprocess.STDOUT) if retcode != 0: return False return True def run_test(label, routines, numerical_tests, language, commands, friendly=True): """A driver for the codegen tests. This driver assumes that a compiler ifort is present in the PATH and that ifort is (at least) a Fortran 90 compiler. The generated code is written in a temporary directory, together with a main program that validates the generated code. The test passes when the compilation and the validation run correctly. """ # Check input arguments before touching the file system language = language.upper() assert language in main_template assert language in numerical_test_template # Check that evironment variable makes sense clean = os.getenv('SYMPY_TEST_CLEAN_TEMP', 'always').lower() if clean not in ('always', 'success', 'never'): raise ValueError("SYMPY_TEST_CLEAN_TEMP must be one of the following: 'always', 'success' or 'never'.") # Do all the magic to compile, run and validate the test code # 1) prepare the temporary working directory, switch to that dir work = tempfile.mkdtemp("_sympy_%s_test" % language, "%s_" % label) oldwork = os.getcwd() os.chdir(work) # 2) write the generated code if friendly: # interpret the routines as a name_expr list and call the friendly # function codegen codegen(routines, language, "codegen", to_files=True) else: code_gen = get_code_generator(language, "codegen") code_gen.write(routines, "codegen", to_files=True) # 3) write a simple main program that links to the generated code, and that # includes the numerical tests test_strings = [] for fn_name, args, expected, threshold in numerical_tests: call_string = "%s(%s)-(%s)" % ( fn_name, ",".join(str(arg) for arg in args), expected) if language == "F95": call_string = fortranize_double_constants(call_string) threshold = fortranize_double_constants(str(threshold)) test_strings.append(numerical_test_template[language] % { "call": call_string, "threshold": threshold, }) if language == "F95": f_name = "main.f90" elif language == "C": f_name = "main.c" else: raise NotImplemented( "FIXME: filename extension unknown for language: %s" % language) with open(f_name, "w") as f: f.write( main_template[language] % {'statements': "".join(test_strings)}) # 4) Compile and link compiled = try_run(commands) # 5) Run if compiled if compiled: executed = try_run(["./test.exe"]) else: executed = False # 6) Clean up stuff if clean == 'always' or (clean == 'success' and compiled and executed): def safe_remove(filename): if os.path.isfile(filename): os.remove(filename) safe_remove("codegen.f90") safe_remove("codegen.c") safe_remove("codegen.h") safe_remove("codegen.o") safe_remove("main.f90") safe_remove("main.c") safe_remove("main.o") safe_remove("test.exe") os.chdir(oldwork) os.rmdir(work) else: print("TEST NOT REMOVED: %s" % work, file=sys.stderr) os.chdir(oldwork) # 7) Do the assertions in the end assert compiled, "failed to compile %s code with:\n%s" % ( language, "\n".join(commands)) assert executed, "failed to execute %s code from:\n%s" % ( language, "\n".join(commands)) def fortranize_double_constants(code_string): """ Replaces every literal float with literal doubles """ import re pattern_exp = re.compile('\d+(\.)?\d*[eE]-?\d+') pattern_float = re.compile('\d+\.\d*(?!\d*d)') def subs_exp(matchobj): return re.sub('[eE]', 'd', matchobj.group(0)) def subs_float(matchobj): return "%sd0" % matchobj.group(0) code_string = pattern_exp.sub(subs_exp, code_string) code_string = pattern_float.sub(subs_float, code_string) return code_string def is_feasible(language, commands): # This test should always work, otherwise the compiler is not present. routine = Routine("test", x) numerical_tests = [ ("test", ( 1.0,), 1.0, 1e-15), ("test", (-1.0,), -1.0, 1e-15), ] try: run_test("is_feasible", [routine], numerical_tests, language, commands, friendly=False) return True except AssertionError: return False valid_lang_commands = [] invalid_lang_compilers = [] for lang, compiler in combinations_lang_compiler: commands = compile_commands[compiler] if is_feasible(lang, commands): valid_lang_commands.append((lang, commands)) else: invalid_lang_compilers.append((lang, compiler)) # We test all language-compiler combinations, just to report what is skipped def test_C_cc(): if ("C", 'cc') in invalid_lang_compilers: skip("`cc' command didn't work as expected") def test_F95_ifort(): if ("F95", 'ifort') in invalid_lang_compilers: skip("`ifort' command didn't work as expected") def test_F95_gfortran(): if ("F95", 'gfortran') in invalid_lang_compilers: skip("`gfortran' command didn't work as expected") def test_F95_g95(): if ("F95", 'g95') in invalid_lang_compilers: skip("`g95' command didn't work as expected") # Here comes the actual tests def test_basic_codegen(): numerical_tests = [ ("test", (1.0, 6.0, 3.0), 21.0, 1e-15), ("test", (-1.0, 2.0, -2.5), -2.5, 1e-15), ] name_expr = [("test", (x + y)*z)] for lang, commands in valid_lang_commands: run_test("basic_codegen", name_expr, numerical_tests, lang, commands) def test_intrinsic_math1_codegen(): # not included: log10 from sympy import acos, asin, atan, ceiling, cos, cosh, floor, log, ln, \ sin, sinh, sqrt, tan, tanh, N name_expr = [ ("test_fabs", abs(x)), ("test_acos", acos(x)), ("test_asin", asin(x)), ("test_atan", atan(x)), ("test_cos", cos(x)), ("test_cosh", cosh(x)), ("test_log", log(x)), ("test_ln", ln(x)), ("test_sin", sin(x)), ("test_sinh", sinh(x)), ("test_sqrt", sqrt(x)), ("test_tan", tan(x)), ("test_tanh", tanh(x)), ] numerical_tests = [] for name, expr in name_expr: for xval in 0.2, 0.5, 0.8: expected = N(expr.subs(x, xval)) numerical_tests.append((name, (xval,), expected, 1e-14)) for lang, commands in valid_lang_commands: if lang == "C": name_expr_C = [("test_floor", floor(x)), ("test_ceil", ceiling(x))] else: name_expr_C = [] run_test("intrinsic_math1", name_expr + name_expr_C, numerical_tests, lang, commands) def test_instrinsic_math2_codegen(): # not included: frexp, ldexp, modf, fmod from sympy import atan2, N name_expr = [ ("test_atan2", atan2(x, y)), ("test_pow", x**y), ] numerical_tests = [] for name, expr in name_expr: for xval, yval in (0.2, 1.3), (0.5, -0.2), (0.8, 0.8): expected = N(expr.subs(x, xval).subs(y, yval)) numerical_tests.append((name, (xval, yval), expected, 1e-14)) for lang, commands in valid_lang_commands: run_test("intrinsic_math2", name_expr, numerical_tests, lang, commands) def test_complicated_codegen(): from sympy import sin, cos, tan, N name_expr = [ ("test1", ((sin(x) + cos(y) + tan(z))**7).expand()), ("test2", cos(cos(cos(cos(cos(cos(cos(cos(x + y + z))))))))), ] numerical_tests = [] for name, expr in name_expr: for xval, yval, zval in (0.2, 1.3, -0.3), (0.5, -0.2, 0.0), (0.8, 2.1, 0.8): expected = N(expr.subs(x, xval).subs(y, yval).subs(z, zval)) numerical_tests.append((name, (xval, yval, zval), expected, 1e-12)) for lang, commands in valid_lang_commands: run_test( "complicated_codegen", name_expr, numerical_tests, lang, commands) sympy-0.7.4.1/sympy/external/tests/test_numpy.py0000644000175000017500000002110512253362407022171 0ustar georgeskgeorgesk# This testfile tests SymPy <-> NumPy compatibility # Don't test any SymPy features here. Just pure interaction with NumPy. # Always write regular SymPy tests for anything, that can be tested in pure # Python (without numpy). Here we test everything, that a user may need when # using SymPy with NumPy from __future__ import division from sympy.external import import_module numpy = import_module('numpy') if numpy: array, matrix, ndarray = numpy.array, numpy.matrix, numpy.ndarray else: #bin/test will not execute any tests now disabled = True def setup_module(module): """py.test support""" if getattr(module, 'disabled', False): import pytest pytest.skip("numpy isn't available.") from sympy import (Rational, Symbol, list2numpy, sin, Float, Matrix, lambdify, symarray, symbols, Integer) import sympy from sympy import mpmath from sympy.abc import x, y, z from sympy.utilities.decorator import conserve_mpmath_dps # first, systematically check, that all operations are implemented and don't # raise an exception def test_systematic_basic(): def s(sympy_object, numpy_array): x = sympy_object + numpy_array x = numpy_array + sympy_object x = sympy_object - numpy_array x = numpy_array - sympy_object x = sympy_object * numpy_array x = numpy_array * sympy_object x = sympy_object / numpy_array x = numpy_array / sympy_object x = sympy_object ** numpy_array x = numpy_array ** sympy_object x = Symbol("x") y = Symbol("y") sympy_objs = [ Rational(2, 3), Float("1.3"), x, y, pow(x, y)*y, Integer(5), Float(5.5), ] numpy_objs = [ array([1]), array([3, 8, -1]), array([x, x**2, Rational(5)]), array([x/y*sin(y), 5, Rational(5)]), ] for x in sympy_objs: for y in numpy_objs: s(x, y) # now some random tests, that test particular problems and that also # check that the results of the operations are correct def test_basics(): one = Rational(1) zero = Rational(0) assert array(1) == array(one) assert array([one]) == array([one]) assert array([x]) == array([x]) assert array(x) == array(Symbol("x")) assert array(one + x) == array(1 + x) X = array([one, zero, zero]) assert (X == array([one, zero, zero])).all() assert (X == array([one, 0, 0])).all() def test_arrays(): one = Rational(1) zero = Rational(0) X = array([one, zero, zero]) Y = one*X X = array([Symbol("a") + Rational(1, 2)]) Y = X + X assert Y == array([1 + 2*Symbol("a")]) Y = Y + 1 assert Y == array([2 + 2*Symbol("a")]) Y = X - X assert Y == array([0]) def test_conversion1(): a = list2numpy([x**2, x]) #looks like an array? assert isinstance(a, ndarray) assert a[0] == x**2 assert a[1] == x assert len(a) == 2 #yes, it's the array def test_conversion2(): a = 2*list2numpy([x**2, x]) b = list2numpy([2*x**2, 2*x]) assert (a == b).all() one = Rational(1) zero = Rational(0) X = list2numpy([one, zero, zero]) Y = one*X X = list2numpy([Symbol("a") + Rational(1, 2)]) Y = X + X assert Y == array([1 + 2*Symbol("a")]) Y = Y + 1 assert Y == array([2 + 2*Symbol("a")]) Y = X - X assert Y == array([0]) def test_list2numpy(): assert (array([x**2, x]) == list2numpy([x**2, x])).all() def test_Matrix1(): m = Matrix([[x, x**2], [5, 2/x]]) assert (array(m.subs(x, 2)) == array([[2, 4], [5, 1]])).all() m = Matrix([[sin(x), x**2], [5, 2/x]]) assert (array(m.subs(x, 2)) == array([[sin(2), 4], [5, 1]])).all() def test_Matrix2(): m = Matrix([[x, x**2], [5, 2/x]]) assert (matrix(m.subs(x, 2)) == matrix([[2, 4], [5, 1]])).all() m = Matrix([[sin(x), x**2], [5, 2/x]]) assert (matrix(m.subs(x, 2)) == matrix([[sin(2), 4], [5, 1]])).all() def test_Matrix3(): a = array([[2, 4], [5, 1]]) assert Matrix(a) == Matrix([[2, 4], [5, 1]]) assert Matrix(a) != Matrix([[2, 4], [5, 2]]) a = array([[sin(2), 4], [5, 1]]) assert Matrix(a) == Matrix([[sin(2), 4], [5, 1]]) assert Matrix(a) != Matrix([[sin(0), 4], [5, 1]]) def test_Matrix4(): a = matrix([[2, 4], [5, 1]]) assert Matrix(a) == Matrix([[2, 4], [5, 1]]) assert Matrix(a) != Matrix([[2, 4], [5, 2]]) a = matrix([[sin(2), 4], [5, 1]]) assert Matrix(a) == Matrix([[sin(2), 4], [5, 1]]) assert Matrix(a) != Matrix([[sin(0), 4], [5, 1]]) def test_Matrix_sum(): M = Matrix([[1, 2, 3], [x, y, x], [2*y, -50, z*x]]) m = matrix([[2, 3, 4], [x, 5, 6], [x, y, z**2]]) assert M + m == Matrix([[3, 5, 7], [2*x, y + 5, x + 6], [2*y + x, y - 50, z*x + z**2]]) assert m + M == Matrix([[3, 5, 7], [2*x, y + 5, x + 6], [2*y + x, y - 50, z*x + z**2]]) assert M + m == M.add(m) def test_Matrix_mul(): M = Matrix([[1, 2, 3], [x, y, x]]) m = matrix([[2, 4], [x, 6], [x, z**2]]) assert M*m == Matrix([ [ 2 + 5*x, 16 + 3*z**2], [2*x + x*y + x**2, 4*x + 6*y + x*z**2], ]) assert m*M == Matrix([ [ 2 + 4*x, 4 + 4*y, 6 + 4*x], [ 7*x, 2*x + 6*y, 9*x], [x + x*z**2, 2*x + y*z**2, 3*x + x*z**2], ]) a = array([2]) assert a[0] * M == 2 * M assert M * a[0] == 2 * M def test_Matrix_array(): class matarray(object): def __array__(self): from numpy import array return array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) matarr = matarray() assert Matrix(matarr) == Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) def test_issue629(): assert (Rational(1, 2)*array([2*x, 0]) == array([x, 0])).all() assert (Rational(1, 2) + array( [2*x, 0]) == array([2*x + Rational(1, 2), Rational(1, 2)])).all() assert (Float("0.5")*array([2*x, 0]) == array([Float("1.0")*x, 0])).all() assert (Float("0.5") + array( [2*x, 0]) == array([2*x + Float("0.5"), Float("0.5")])).all() @conserve_mpmath_dps def test_lambdify(): mpmath.mp.dps = 16 sin02 = mpmath.mpf("0.198669330795061215459412627") f = lambdify(x, sin(x), "numpy") prec = 1e-15 assert -prec < f(0.2) - sin02 < prec try: f(x) # if this succeeds, it can't be a numpy function assert False except AttributeError: pass def test_lambdify_matrix(): f = lambdify(x, Matrix([[x, 2*x], [1, 2]]), "numpy") assert (f(1) == matrix([[1, 2], [1, 2]])).all() def test_lambdify_matrix_multi_input(): M = sympy.Matrix([[x**2, x*y, x*z], [y*x, y**2, y*z], [z*x, z*y, z**2]]) f = lambdify((x, y, z), M, "numpy") xh, yh, zh = 1.0, 2.0, 3.0 expected = matrix([[xh**2, xh*yh, xh*zh], [yh*xh, yh**2, yh*zh], [zh*xh, zh*yh, zh**2]]) actual = f(xh, yh, zh) assert numpy.allclose(actual, expected) def test_lambdify_matrix_vec_input(): X = sympy.DeferredVector('X') M = Matrix([ [X[0]**2, X[0]*X[1], X[0]*X[2]], [X[1]*X[0], X[1]**2, X[1]*X[2]], [X[2]*X[0], X[2]*X[1], X[2]**2]]) f = lambdify(X, M, "numpy") Xh = array([1.0, 2.0, 3.0]) expected = matrix([[Xh[0]**2, Xh[0]*Xh[1], Xh[0]*Xh[2]], [Xh[1]*Xh[0], Xh[1]**2, Xh[1]*Xh[2]], [Xh[2]*Xh[0], Xh[2]*Xh[1], Xh[2]**2]]) actual = f(Xh) assert numpy.allclose(actual, expected) def test_lambdify_transl(): from sympy.utilities.lambdify import NUMPY_TRANSLATIONS for sym, mat in NUMPY_TRANSLATIONS.items(): assert sym in sympy.__dict__ assert mat in numpy.__dict__ def test_symarray(): """Test creation of numpy arrays of sympy symbols.""" import numpy as np import numpy.testing as npt syms = symbols('_0,_1,_2') s1 = symarray("", 3) s2 = symarray("", 3) npt.assert_array_equal(s1, np.array(syms, dtype=object)) assert s1[0] == s2[0] a = symarray('a', 3) b = symarray('b', 3) assert not(a[0] == b[0]) asyms = symbols('a_0,a_1,a_2') npt.assert_array_equal(a, np.array(asyms, dtype=object)) # Multidimensional checks a2d = symarray('a', (2, 3)) assert a2d.shape == (2, 3) a00, a12 = symbols('a_0_0,a_1_2') assert a2d[0, 0] == a00 assert a2d[1, 2] == a12 a3d = symarray('a', (2, 3, 2)) assert a3d.shape == (2, 3, 2) a000, a120, a121 = symbols('a_0_0_0,a_1_2_0,a_1_2_1') assert a3d[0, 0, 0] == a000 assert a3d[1, 2, 0] == a120 assert a3d[1, 2, 1] == a121 def test_vectorize(): assert (numpy.vectorize( sin)([1, 2, 3]) == numpy.array([sin(1), sin(2), sin(3)])).all() sympy-0.7.4.1/sympy/external/tests/__init__.py0000644000175000017500000000000012253362407021510 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/external/__init__.py0000644000175000017500000000104512253362407020360 0ustar georgeskgeorgesk""" Unified place for determining if external dependencies are installed or not. You should import all external modules using the import_module() function. For example >>> from sympy.external import import_module >>> numpy = import_module('numpy') If the resulting library is not installed, or if the installed version is less than a given minimum version, the function will return None. Otherwise, it will return the library. See the docstring of import_module() for more information. """ from sympy.external.importtools import import_module sympy-0.7.4.1/sympy/strategies/0000755000175000017500000000000012253362407016577 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/strategies/tools.py0000644000175000017500000000243712253362407020317 0ustar georgeskgeorgeskfrom __future__ import print_function, division from . import rl from .core import do_one, exhaust, switch from .traverse import top_down def subs(d, **kwargs): """ Full simultaneous exact substitution Example ======= >>> from sympy.strategies.tools import subs >>> from sympy import Basic >>> mapping = {1: 4, 4: 1, Basic(5): Basic(6, 7)} >>> expr = Basic(1, Basic(2, 3), Basic(4, Basic(5))) >>> subs(mapping)(expr) Basic(4, Basic(2, 3), Basic(1, Basic(6, 7))) """ if d: return top_down(do_one(*map(rl.subs, *zip(*d.items()))), **kwargs) else: return lambda x: x def canon(*rules, **kwargs): """ Strategy for canonicalization Apply each rule in a bottom_up fashion through the tree. Do each one in turn. Keep doing this until there is no change. """ return exhaust(top_down(exhaust(do_one(*rules)), **kwargs)) def typed(ruletypes): """ Apply rules based on the expression type inputs: ruletypes -- a dict mapping {Type: rule} >>> from sympy.strategies import rm_id, typed >>> from sympy import Add, Mul >>> rm_zeros = rm_id(lambda x: x==0) >>> rm_ones = rm_id(lambda x: x==1) >>> remove_idents = typed({Add: rm_zeros, Mul: rm_ones}) """ return switch(type, ruletypes) sympy-0.7.4.1/sympy/strategies/branch/0000755000175000017500000000000012253362407020034 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/strategies/branch/tools.py0000644000175000017500000000076112253362407021552 0ustar georgeskgeorgeskfrom __future__ import print_function, division from .core import (exhaust, multiplex, debug, notempty, condition, chain, onaction, sfilter, yieldify, do_one, identity) from .traverse import top_down def canon(*rules): """ Strategy for canonicalization Apply each branching rule in a top-down fashion through the tree. Multiplex through all branching rule traversals Keep doing this until there is no change. """ return exhaust(multiplex(*map(top_down, rules))) sympy-0.7.4.1/sympy/strategies/branch/core.py0000644000175000017500000000567012253362407021346 0ustar georgeskgeorgesk""" Generic SymPy-Independent Strategies """ from __future__ import print_function, division from sympy.core.compatibility import get_function_name def identity(x): yield x def exhaust(brule): """ Apply a branching rule repeatedly until it has no effect """ def exhaust_brl(expr): seen = set([expr]) for nexpr in brule(expr): if nexpr not in seen: seen.add(nexpr) for nnexpr in exhaust_brl(nexpr): yield nnexpr if seen == set([expr]): yield expr return exhaust_brl def onaction(brule, fn): def onaction_brl(expr): for result in brule(expr): if result != expr: fn(brule, expr, result) yield result return onaction_brl def debug(brule, file=None): """ Print the input and output expressions at each rule application """ if not file: from sys import stdout file = stdout def write(brl, expr, result): file.write("Rule: %s\n" % get_function_name(brl)) file.write("In: %s\nOut: %s\n\n" % (expr, result)) return onaction(brule, write) def multiplex(*brules): """ Multiplex many branching rules into one """ def multiplex_brl(expr): seen = set([]) for brl in brules: for nexpr in brl(expr): if nexpr not in seen: seen.add(nexpr) yield nexpr return multiplex_brl def condition(cond, brule): """ Only apply branching rule if condition is true """ def conditioned_brl(expr): if cond(expr): for x in brule(expr): yield x else: pass return conditioned_brl def sfilter(pred, brule): """ Yield only those results which satisfy the predicate """ def filtered_brl(expr): for x in filter(pred, brule(expr)): yield x return filtered_brl def notempty(brule): def notempty_brl(expr): yielded = False for nexpr in brule(expr): yielded = True yield nexpr if not yielded: yield expr return notempty_brl def do_one(*brules): """ Execute one of the branching rules """ def do_one_brl(expr): yielded = False for brl in brules: for nexpr in brl(expr): yielded = True yield nexpr if yielded: raise StopIteration() return do_one_brl def chain(*brules): """ Compose a sequence of brules so that they apply to the expr sequentially """ def chain_brl(expr): if not brules: yield expr raise StopIteration() head, tail = brules[0], brules[1:] for nexpr in head(expr): for nnexpr in chain(*tail)(nexpr): yield nnexpr return chain_brl def yieldify(rl): """ Turn a rule into a branching rule """ def brl(expr): yield rl(expr) return brl sympy-0.7.4.1/sympy/strategies/branch/traverse.py0000644000175000017500000000151712253362407022245 0ustar georgeskgeorgesk""" Branching Strategies to Traverse a Tree """ from __future__ import print_function, division from itertools import product from sympy.strategies.util import basic_fns from .core import chain, identity, do_one def top_down(brule, fns=basic_fns): """ Apply a rule down a tree running it on the top nodes first """ return chain(do_one(brule, identity), lambda expr: sall(top_down(brule, fns), fns)(expr)) def sall(brule, fns=basic_fns): """ Strategic all - apply rule to args """ op, new, children, leaf = map(fns.get, ('op', 'new', 'children', 'leaf')) def all_rl(expr): if leaf(expr): yield expr else: myop = op(expr) argss = product(*map(brule, children(expr))) for args in argss: yield new(myop, *args) return all_rl sympy-0.7.4.1/sympy/strategies/branch/tests/0000755000175000017500000000000012253362407021176 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/strategies/branch/tests/test_traverse.py0000644000175000017500000000223212253362407024441 0ustar georgeskgeorgeskfrom sympy import Basic, symbols, Symbol, S from sympy.strategies.branch.traverse import top_down, sall from sympy.strategies.branch.core import do_one, identity def inc(x): if isinstance(x, int): yield x + 1 def test_top_down_easy(): expr = Basic(1, 2) expected = Basic(2, 3) brl = top_down(inc) assert set(brl(expr)) == set([expected]) def test_top_down_big_tree(): expr = Basic(1, Basic(2), Basic(3, Basic(4), 5)) expected = Basic(2, Basic(3), Basic(4, Basic(5), 6)) brl = top_down(inc) assert set(brl(expr)) == set([expected]) def test_top_down_harder_function(): def split5(x): if x == 5: yield x - 1 yield x + 1 expr = Basic(Basic(5, 6), 1) expected = set([Basic(Basic(4, 6), 1), Basic(Basic(6, 6), 1)]) brl = top_down(split5) assert set(brl(expr)) == expected def test_sall(): expr = Basic(1, 2) expected = Basic(2, 3) brl = sall(inc) assert list(brl(expr)) == [expected] expr = Basic(1, 2, Basic(3, 4)) expected = Basic(2, 3, Basic(3, 4)) brl = sall(do_one(inc, identity)) assert list(brl(expr)) == [expected] sympy-0.7.4.1/sympy/strategies/branch/tests/test_tools.py0000644000175000017500000000142412253362407023750 0ustar georgeskgeorgeskfrom sympy.strategies.branch.tools import canon from sympy import Basic def posdec(x): if isinstance(x, int) and x > 0: yield x-1 else: yield x def branch5(x): if isinstance(x, int): if 0 < x < 5: yield x-1 elif 5 < x < 10: yield x+1 elif x == 5: yield x+1 yield x-1 else: yield x def test_zero_ints(): expr = Basic(2, Basic(5, 3), 8) expected = set([Basic(0, Basic(0, 0), 0)]) brl = canon(posdec) assert set(brl(expr)) == expected def test_split5(): expr = Basic(2, Basic(5, 3), 8) expected = set([Basic(0, Basic(0, 0), 10), Basic(0, Basic(10, 0), 10)]) brl = canon(branch5) assert set(brl(expr)) == expected sympy-0.7.4.1/sympy/strategies/branch/tests/test_core.py0000644000175000017500000000455112253362407023544 0ustar georgeskgeorgeskfrom sympy.strategies.branch.core import (exhaust, debug, multiplex, condition, notempty, chain, onaction, sfilter, yieldify, do_one, identity) from sympy.core.compatibility import get_function_name def posdec(x): if x > 0: yield x-1 else: yield x def branch5(x): if 0 < x < 5: yield x-1 elif 5 < x < 10: yield x+1 elif x == 5: yield x+1 yield x-1 else: yield x even = lambda x: x%2 == 0 def inc(x): yield x + 1 def one_to_n(n): for i in range(n): yield i def test_exhaust(): brl = exhaust(branch5) assert set(brl(3)) == set([0]) assert set(brl(7)) == set([10]) assert set(brl(5)) == set([0, 10]) def test_debug(): from sympy.core.compatibility import StringIO file = StringIO() rl = debug(posdec, file) list(rl(5)) log = file.getvalue() file.close() assert get_function_name(posdec) in log assert '5' in log assert '4' in log def test_multiplex(): brl = multiplex(posdec, branch5) assert set(brl(3)) == set([2]) assert set(brl(7)) == set([6, 8]) assert set(brl(5)) == set([4, 6]) def test_condition(): brl = condition(even, branch5) assert set(brl(4)) == set(branch5(4)) assert set(brl(5)) == set([]) def test_sfilter(): brl = sfilter(even, one_to_n) assert set(brl(10)) == set([0, 2, 4, 6, 8]) def test_notempty(): def ident_if_even(x): if even(x): yield x brl = notempty(ident_if_even) assert set(brl(4)) == set([4]) assert set(brl(5)) == set([5]) def test_chain(): assert list(chain()(2)) == [2] # identity assert list(chain(inc, inc)(2)) == [4] assert list(chain(branch5, inc)(4)) == [4] assert set(chain(branch5, inc)(5)) == set([5, 7]) assert list(chain(inc, branch5)(5)) == [7] def test_onaction(): L = [] def record(fn, input, output): L.append((input, output)) list(onaction(inc, record)(2)) assert L == [(2, 3)] list(onaction(identity, record)(2)) assert L == [(2, 3)] def test_yieldify(): inc = lambda x: x + 1 yinc = yieldify(inc) assert list(yinc(3)) == [4] def test_do_one(): def bad(expr): raise ValueError() yield False assert list(do_one(inc)(3)) == [4] assert list(do_one(inc, bad)(3)) == [4] assert list(do_one(inc, posdec)(3)) == [4] sympy-0.7.4.1/sympy/strategies/branch/tests/__init__.py0000644000175000017500000000000012253362407023275 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/strategies/branch/__init__.py0000644000175000017500000000026112253362407022144 0ustar georgeskgeorgeskfrom . import traverse from .core import (condition, debug, multiplex, exhaust, notempty, chain, onaction, sfilter, yieldify, do_one, identity) from .tools import canon sympy-0.7.4.1/sympy/strategies/rl.py0000644000175000017500000001027512253362407017573 0ustar georgeskgeorgesk""" Generic Rules for SymPy This file assumes knowledge of Basic and little else. """ from __future__ import print_function, division from sympy.utilities.iterables import sift from .util import new # Functions that create rules def rm_id(isid, new=new): """ Create a rule to remove identities isid - fn :: x -> Bool --- whether or not this element is an identity >>> from sympy.strategies import rm_id >>> from sympy import Basic >>> remove_zeros = rm_id(lambda x: x==0) >>> remove_zeros(Basic(1, 0, 2)) Basic(1, 2) >>> remove_zeros(Basic(0, 0)) # If only identites then we keep one Basic(0) See Also: unpack """ def ident_remove(expr): """ Remove identities """ ids = list(map(isid, expr.args)) if sum(ids) == 0: # No identities. Common case return expr elif sum(ids) != len(ids): # there is at least one non-identity return new(expr.__class__, *[arg for arg, x in zip(expr.args, ids) if not x]) else: return new(expr.__class__, expr.args[0]) return ident_remove def glom(key, count, combine): """ Create a rule to conglomerate identical args >>> from sympy.strategies import glom >>> from sympy import Add >>> from sympy.abc import x >>> key = lambda x: x.as_coeff_Mul()[1] >>> count = lambda x: x.as_coeff_Mul()[0] >>> combine = lambda cnt, arg: cnt * arg >>> rl = glom(key, count, combine) >>> rl(Add(x, -x, 3*x, 2, 3, evaluate=False)) 3*x + 5 Wait, how are key, count and combine supposed to work? >>> key(2*x) x >>> count(2*x) 2 >>> combine(2, x) 2*x """ def conglomerate(expr): """ Conglomerate together identical args x + x -> 2x """ groups = sift(expr.args, key) counts = dict((k, sum(map(count, args))) for k, args in groups.items()) newargs = [combine(cnt, mat) for mat, cnt in counts.items()] if set(newargs) != set(expr.args): return new(type(expr), *newargs) else: return expr return conglomerate def sort(key, new=new): """ Create a rule to sort by a key function >>> from sympy.strategies import sort >>> from sympy import Basic >>> sort_rl = sort(str) >>> sort_rl(Basic(3, 1, 2)) Basic(1, 2, 3) """ def sort_rl(expr): return new(expr.__class__, *sorted(expr.args, key=key)) return sort_rl def distribute(A, B): """ Turns an A containing Bs into a B of As where A, B are container types >>> from sympy.strategies import distribute >>> from sympy import Add, Mul, symbols >>> x, y = symbols('x,y') >>> dist = distribute(Mul, Add) >>> expr = Mul(2, x+y, evaluate=False) >>> expr 2*(x + y) >>> dist(expr) 2*x + 2*y """ def distribute_rl(expr): for i, arg in enumerate(expr.args): if isinstance(arg, B): first, b, tail = expr.args[:i], expr.args[i], expr.args[i+1:] return B(*[A(*(first + (arg,) + tail)) for arg in b.args]) return expr return distribute_rl def subs(a, b): """ Replace expressions exactly """ def subs_rl(expr): if expr == a: return b else: return expr return subs_rl # Functions that are rules def unpack(expr): """ Rule to unpack singleton args >>> from sympy.strategies import unpack >>> from sympy import Basic >>> unpack(Basic(2)) 2 """ if len(expr.args) == 1: return expr.args[0] else: return expr def flatten(expr, new=new): """ Flatten T(a, b, T(c, d), T2(e)) to T(a, b, c, d, T2(e)) """ cls = expr.__class__ args = [] for arg in expr.args: if arg.__class__ == cls: args.extend(arg.args) else: args.append(arg) return new(expr.__class__, *args) def rebuild(expr): """ Rebuild a SymPy tree This function recursively calls constructors in the expression tree. This forces canonicalization and removes ugliness introduced by the use of Basic.__new__ """ try: return type(expr)(*list(map(rebuild, expr.args))) except: return expr sympy-0.7.4.1/sympy/strategies/tree.py0000644000175000017500000000725712253362407020123 0ustar georgeskgeorgeskfrom __future__ import print_function, division from functools import partial from sympy.strategies import chain, minimize import sympy.strategies.branch as branch from sympy.strategies.branch import yieldify identity = lambda x: x def treeapply(tree, join, leaf=identity): """ Apply functions onto recursive containers (tree) join - a dictionary mapping container types to functions e.g. ``{list: minimize, tuple: chain}`` Keys are containers/iterables. Values are functions [a] -> a. Examples -------- >>> from sympy.strategies.tree import treeapply >>> tree = [(3, 2), (4, 1)] >>> treeapply(tree, {list: max, tuple: min}) 2 >>> add = lambda *args: sum(args) >>> def mul(*args): ... total = 1 ... for arg in args: ... total *= arg ... return total >>> treeapply(tree, {list: mul, tuple: add}) 25 """ for typ in join: if isinstance(tree, typ): return join[typ](*map(partial(treeapply, join=join, leaf=leaf), tree)) return leaf(tree) def greedy(tree, objective=identity, **kwargs): """ Execute a strategic tree. Select alternatives greedily Trees ----- Nodes in a tree can be either function - a leaf list - a selection among operations tuple - a sequence of chained operations Textual examples ---------------- Text: Run f, then run g, e.g. ``lambda x: g(f(x))`` Code: ``(f, g)`` Text: Run either f or g, whichever minimizes the objective Code: ``[f, g]`` Textx: Run either f or g, whichever is better, then run h Code: ``([f, g], h)`` Text: Either expand then simplify or try factor then foosimp. Finally print Code: ``([(expand, simplify), (factor, foosimp)], print)`` Objective --------- "Better" is determined by the objective keyword. This function makes choices to minimize the objective. It defaults to the identity. Example ------- >>> from sympy.strategies.tree import greedy >>> inc = lambda x: x + 1 >>> dec = lambda x: x - 1 >>> double = lambda x: 2*x >>> tree = [inc, (dec, double)] # either inc or dec-then-double >>> fn = greedy(tree) >>> fn(4) # lowest value comes from the inc 5 >>> fn(1) # lowest value comes from dec then double 0 This funcion selects between options in a tuple. The result is chosen that minimizes the objective function. >>> fn = greedy(tree, objective=lambda x: -x) # maximize >>> fn(4) # highest value comes from the dec then double 6 >>> fn(1) # highest value comes from the inc 2 Greediness ---------- This is a greedy algorithm. In the example: ([a, b], c) # do either a or b, then do c the choice between running ``a`` or ``b`` is made without foresight to c """ optimize = partial(minimize, objective=objective) return treeapply(tree, {list: optimize, tuple: chain}, **kwargs) def allresults(tree, leaf=yieldify): """ Execute a strategic tree. Return all possibilities. Returns a lazy iterator of all possible results Exhaustiveness -------------- This is an exhaustive algorithm. In the example ([a, b], [c, d]) All of the results from (a, c), (b, c), (a, d), (b, d) are returned. This can lead to combinatorial blowup. See sympy.strategies.greedy for details on input """ return treeapply(tree, {list: branch.multiplex, tuple: branch.chain}, leaf=leaf) def brute(tree, objective=identity, **kwargs): return lambda expr: min(tuple(allresults(tree, **kwargs)(expr)), key=objective) sympy-0.7.4.1/sympy/strategies/util.py0000644000175000017500000000061512253362407020130 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import Basic new = Basic.__new__ def assoc(d, k, v): d = d.copy() d[k] = v return d basic_fns = {'op': type, 'new': Basic.__new__, 'leaf': lambda x: not isinstance(x, Basic) or x.is_Atom, 'children': lambda x: x.args} expr_fns = assoc(basic_fns, 'new', lambda op, *args: op(*args)) sympy-0.7.4.1/sympy/strategies/core.py0000644000175000017500000000607612253362407020112 0ustar georgeskgeorgesk""" Generic SymPy-Independent Strategies """ from __future__ import print_function, division from functools import partial from sympy.core.compatibility import get_function_name identity = lambda x: x def exhaust(rule): """ Apply a rule repeatedly until it has no effect """ def exhaustive_rl(expr): new, old = rule(expr), expr while(new != old): new, old = rule(new), new return new return exhaustive_rl def memoize(rule): """ Memoized version of a rule """ cache = {} def memoized_rl(expr): if expr in cache: return cache[expr] else: result = rule(expr) cache[expr] = result return result return memoized_rl def condition(cond, rule): """ Only apply rule if condition is true """ def conditioned_rl(expr): if cond(expr): return rule(expr) else: return expr return conditioned_rl def chain(*rules): """ Compose a sequence of rules so that they apply to the expr sequentially """ def chain_rl(expr): for rule in rules: expr = rule(expr) return expr return chain_rl def debug(rule, file=None): """ Print out before and after expressions each time rule is used """ if file is None: from sys import stdout file = stdout def debug_rl(*args, **kwargs): expr = args[0] result = rule(*args, **kwargs) if result != expr: file.write("Rule: %s\n" % get_function_name(rule)) file.write("In: %s\nOut: %s\n\n"%(expr, result)) return result return debug_rl def null_safe(rule): """ Return original expr if rule returns None """ def null_safe_rl(expr): result = rule(expr) if result is None: return expr else: return result return null_safe_rl def tryit(rule): """ Return original expr if rule raises exception """ def try_rl(expr): try: return rule(expr) except: return expr return try_rl def do_one(*rules): """ Try each of the rules until one works. Then stop. """ def do_one_rl(expr): for rl in rules: result = rl(expr) if result != expr: return result return expr return do_one_rl def switch(key, ruledict): """ Select a rule based on the result of key called on the function """ def switch_rl(expr): rl = ruledict.get(key(expr), identity) return rl(expr) return switch_rl identity = lambda x: x def minimize(*rules, **kwargs): """ Select result of rules that minimizes objective >>> from sympy.strategies import minimize >>> inc = lambda x: x + 1 >>> dec = lambda x: x - 1 >>> rl = minimize(inc, dec) >>> rl(4) 3 >>> rl = minimize(inc, dec, objective=lambda x: -x) # maximize >>> rl(4) 5 """ objective = kwargs.get('objective', identity) def minrule(expr): return min([rule(expr) for rule in rules], key=objective) return minrule sympy-0.7.4.1/sympy/strategies/traverse.py0000644000175000017500000000231112253362407021001 0ustar georgeskgeorgesk""" Strategies to Traverse a Tree """ from __future__ import print_function, division from .util import basic_fns, expr_fns from sympy.strategies.core import chain, do_one def top_down(rule, fns=basic_fns): """ Apply a rule down a tree running it on the top nodes first """ return chain(rule, lambda expr: sall(top_down(rule, fns), fns)(expr)) def bottom_up(rule, fns=basic_fns): """ Apply a rule down a tree running it on the bottom nodes first """ return chain(lambda expr: sall(bottom_up(rule, fns), fns)(expr), rule) def top_down_once(rule, fns=basic_fns): """ Apply a rule down a tree - stop on success """ return do_one(rule, lambda expr: sall(top_down(rule, fns), fns)(expr)) def bottom_up_once(rule, fns=basic_fns): """ Apply a rule up a tree - stop on success """ return do_one(lambda expr: sall(bottom_up(rule, fns), fns)(expr), rule) def sall(rule, fns=basic_fns): """ Strategic all - apply rule to args """ op, new, children, leaf = map(fns.get, ('op', 'new', 'children', 'leaf')) def all_rl(expr): if leaf(expr): return expr else: args = map(rule, children(expr)) return new(op(expr), *args) return all_rl sympy-0.7.4.1/sympy/strategies/tests/0000755000175000017500000000000012253362407017741 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/strategies/tests/test_traverse.py0000644000175000017500000000343412253362407023211 0ustar georgeskgeorgeskfrom sympy.strategies.traverse import (top_down, bottom_up, sall, top_down_once, bottom_up_once, expr_fns, basic_fns) from sympy import Basic, symbols, Symbol, S zero_symbols = lambda x: S.Zero if isinstance(x, Symbol) else x x,y,z = symbols('x,y,z') def test_sall(): zero_onelevel = sall(zero_symbols) assert zero_onelevel(Basic(x, y, Basic(x, z))) == \ Basic(0, 0, Basic(x, z)) def test_bottom_up(): _test_global_traversal(bottom_up) _test_stop_on_non_basics(bottom_up) def test_top_down(): _test_global_traversal(top_down) _test_stop_on_non_basics(top_down) def _test_global_traversal(trav): x,y,z = symbols('x,y,z') zero_all_symbols = trav(zero_symbols) assert zero_all_symbols(Basic(x, y, Basic(x, z))) == \ Basic(0, 0, Basic(0, 0)) def _test_stop_on_non_basics(trav): def add_one_if_can(expr): try: return expr + 1 except: return expr expr = Basic(1, 'a', Basic(2, 'b')) expected = Basic(2, 'a', Basic(3, 'b')) rl = trav(add_one_if_can) assert rl(expr) == expected class Basic2(Basic): pass rl = lambda x: Basic2(*x.args) if isinstance(x, Basic) else x def test_top_down_once(): top_rl = top_down_once(rl) assert top_rl(Basic(1, 2, Basic(3, 4))) == \ Basic2(1, 2, Basic(3, 4)) def test_bottom_up_once(): bottom_rl = bottom_up_once(rl) assert bottom_rl(Basic(1, 2, Basic(3, 4))) == \ Basic(1, 2, Basic2(3, 4)) def test_expr_fns(): from sympy.strategies.rl import rebuild from sympy import Add x, y = map(Symbol, 'xy') expr = x + y**3 e = bottom_up(lambda x: x + 1, expr_fns)(expr) b = bottom_up(lambda x: Basic.__new__(Add, x, 1), basic_fns)(expr) assert rebuild(b) == e sympy-0.7.4.1/sympy/strategies/tests/test_rl.py0000644000175000017500000000326212253362407021772 0ustar georgeskgeorgeskfrom sympy.strategies.rl import (rm_id, glom, flatten, unpack, sort, distribute, subs, rebuild) from sympy import Basic def test_rm_id(): rmzeros = rm_id(lambda x: x == 0) assert rmzeros(Basic(0, 1)) == Basic(1) assert rmzeros(Basic(0, 0)) == Basic(0) assert rmzeros(Basic(2, 1)) == Basic(2, 1) def test_glom(): from sympy import Add from sympy.abc import x key = lambda x: x.as_coeff_Mul()[1] count = lambda x: x.as_coeff_Mul()[0] newargs = lambda cnt, arg: cnt * arg rl = glom(key, count, newargs) result = rl(Add(x, -x, 3*x, 2, 3, evaluate=False)) expected = Add(3*x, 5) assert set(result.args) == set(expected.args) def test_flatten(): assert flatten(Basic(1, 2, Basic(3, 4))) == Basic(1, 2, 3, 4) def test_unpack(): assert unpack(Basic(2)) == 2 assert unpack(Basic(2, 3)) == Basic(2, 3) def test_sort(): assert sort(str)(Basic(3,1,2)) == Basic(1,2,3) def test_distribute(): class T1(Basic): pass class T2(Basic): pass distribute_t12 = distribute(T1, T2) assert distribute_t12(T1(1, 2, T2(3, 4), 5)) == \ T2(T1(1, 2, 3, 5), T1(1, 2, 4, 5)) assert distribute_t12(T1(1, 2, 3)) == T1(1, 2, 3) def test_distribute_add_mul(): from sympy import Add, Mul, symbols x, y = symbols('x, y') expr = Mul(2, Add(x, y), evaluate=False) expected = Add(Mul(2, x), Mul(2, y)) distribute_mul = distribute(Mul, Add) assert distribute_mul(expr) == expected def test_subs(): rl = subs(1, 2) assert rl(1) == 2 assert rl(3) == 3 def test_rebuild(): from sympy import Add expr = Basic.__new__(Add, 1, 2) assert rebuild(expr) == 3 sympy-0.7.4.1/sympy/strategies/tests/test_strat.py0000644000175000017500000000000012253362407022475 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/strategies/tests/test_tools.py0000644000175000017500000000140612253362407022513 0ustar georgeskgeorgeskfrom sympy.strategies.tools import subs, typed from sympy.strategies.rl import rm_id from sympy import Basic def test_subs(): from sympy import symbols a,b,c,d,e,f = symbols('a,b,c,d,e,f') mapping = {a: d, d: a, Basic(e): Basic(f)} expr = Basic(a, Basic(b, c), Basic(d, Basic(e))) result = Basic(d, Basic(b, c), Basic(a, Basic(f))) assert subs(mapping)(expr) == result def test_subs_empty(): assert subs({})(Basic(1, 2)) == Basic(1, 2) def test_typed(): class A(Basic): pass class B(Basic): pass rmzeros = rm_id(lambda x: x == 0) rmones = rm_id(lambda x: x == 1) remove_something = typed({A: rmzeros, B: rmones}) assert remove_something(A(0, 1)) == A(1) assert remove_something(B(0, 1)) == B(0) sympy-0.7.4.1/sympy/strategies/tests/test_tree.py0000644000175000017500000000511212253362407022310 0ustar georgeskgeorgeskfrom sympy.strategies.tree import (treeapply, treeapply, greedy, allresults, brute) from sympy.core.compatibility import reduce from functools import partial def test_treeapply(): tree = ([3, 3], [4, 1], 2) assert treeapply(tree, {list: min, tuple: max}) == 3 add = lambda *args: sum(args) mul = lambda *args: reduce(lambda a, b: a*b, args, 1) assert treeapply(tree, {list: add, tuple: mul}) == 60 def test_treeapply_leaf(): assert treeapply(3, {}, leaf=lambda x: x**2) == 9 tree = ([3, 3], [4, 1], 2) treep1 = ([4, 4], [5, 2], 3) assert treeapply(tree, {list: min, tuple: max}, leaf=lambda x: x+1) == \ treeapply(treep1, {list: min, tuple: max}) def test_treeapply_strategies(): from sympy.strategies import chain, minimize join = {list: chain, tuple: minimize} inc = lambda x: x + 1 dec = lambda x: x - 1 double = lambda x: 2*x assert treeapply(inc, join) == inc assert treeapply((inc, dec), join)(5) == minimize(inc, dec)(5) assert treeapply([inc, dec], join)(5) == chain(inc, dec)(5) tree = (inc, [dec, double]) # either inc or dec-then-double assert treeapply(tree, join)(5) == 6 assert treeapply(tree, join)(1) == 0 maximize = partial(minimize, objective=lambda x: -x) join = {list: chain, tuple: maximize} fn = treeapply(tree, join) assert fn(4) == 6 # highest value comes from the dec then double assert fn(1) == 2 # highest value comes from the inc def test_greedy(): inc = lambda x: x + 1 dec = lambda x: x - 1 double = lambda x: 2*x tree = [inc, (dec, double)] # either inc or dec-then-double fn = greedy(tree, objective=lambda x: -x) assert fn(4) == 6 # highest value comes from the dec then double assert fn(1) == 2 # highest value comes from the inc tree = [inc, dec, [inc, dec, [(inc, inc), (dec, dec)]]] lowest = greedy(tree) assert lowest(10) == 8 highest = greedy(tree, objective=lambda x: -x) assert highest(10) == 12 def test_allresults(): inc = lambda x: x+1 dec = lambda x: x-1 double = lambda x: x*2 square = lambda x: x**2 assert set(allresults(inc)(3)) == set([inc(3)]) assert set(allresults([inc, dec])(3)) == set([2, 4]) assert set(allresults((inc, dec))(3)) == set([3]) assert set(allresults([inc, (dec, double)])(4)) == set([5, 6]) def test_brute(): inc = lambda x: x+1 dec = lambda x: x-1 square = lambda x: x**2 tree = ([inc, dec], square) fn = brute(tree, lambda x: -x) assert fn(2) == (2 + 1)**2 assert fn(-2) == (-2 - 1)**2 assert brute(inc)(1) == 2 sympy-0.7.4.1/sympy/strategies/tests/test_core.py0000644000175000017500000000365312253362407022311 0ustar georgeskgeorgeskfrom sympy.strategies.core import (null_safe, exhaust, memoize, condition, chain, tryit, do_one, debug, switch, minimize) from sympy.core.compatibility import get_function_name from functools import partial def test_null_safe(): def rl(expr): if expr == 1: return 2 safe_rl = null_safe(rl) assert rl(1) == safe_rl(1) assert rl(3) == None assert safe_rl(3) == 3 def posdec(x): if x > 0: return x-1 else: return x def test_exhaust(): sink = exhaust(posdec) assert sink(5) == 0 assert sink(10) == 0 def test_memoize(): rl = memoize(posdec) assert rl(5) == posdec(5) assert rl(5) == posdec(5) assert rl(-2) == posdec(-2) def test_condition(): rl = condition(lambda x: x%2 == 0, posdec) assert rl(5) == 5 assert rl(4) == 3 def test_chain(): rl = chain(posdec, posdec) assert rl(5) == 3 assert rl(1) == 0 def test_tryit(): def rl(expr): assert False safe_rl = tryit(rl) assert safe_rl(1) == 1 def test_do_one(): rl = do_one(posdec, posdec) assert rl(5) == 4 def test_debug(): from sympy.core.compatibility import StringIO file = StringIO() rl = debug(posdec, file) rl(5) log = file.getvalue() file.close() assert get_function_name(posdec) in log assert '5' in log assert '4' in log def test_switch(): inc = lambda x: x + 1 dec = lambda x: x - 1 key = lambda x: x % 3 rl = switch(key, {0: inc, 1: dec}) assert rl(3) == 4 assert rl(4) == 3 assert rl(5) == 5 def test_minimize(): inc = lambda x: x + 1 dec = lambda x: x - 1 rl = minimize(inc, dec) assert rl(4) == 3 rl = minimize(inc, dec, objective=lambda x: -x) assert rl(4) == 5 def test_do_one(): rl1 = lambda x: 2 if x == 1 else x rl2 = lambda x: 3 if x == 2 else x rule = do_one(rl1, rl2) assert rule(1) == 2 assert rule(rule(1)) == 3 sympy-0.7.4.1/sympy/strategies/tests/__init__.py0000644000175000017500000000000012253362407022040 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/strategies/__init__.py0000644000175000017500000000216612253362407020715 0ustar georgeskgeorgesk""" Rewrite Rules DISCLAIMER: This module is experimental. The interface is subject to change. A rule is a function that transforms one expression into another Rule :: Expr -> Expr A strategy is a function that says how a rule should be applied to a syntax tree. In general strategies take rules and produce a new rule Strategy :: [Rules], Other-stuff -> Rule This allows developers to separate a mathematical transformation from the algorithmic details of applying that transformation. The goal is to separate the work of mathematical programming from algorithmic programming. Submodules strategies.rl - some fundamental rules strategies.core - generic non-SymPy specific strategies strategies.traverse - strategies that traverse a SymPy tree strategies.tools - some conglomerate strategies that do depend on SymPy """ from . import rl from . import traverse from .rl import rm_id, unpack, flatten, sort, glom, distribute, rebuild from .util import new from .core import (condition, debug, chain, null_safe, do_one, exhaust, minimize, tryit) from .tools import canon, typed from . import branch sympy-0.7.4.1/sympy/functions/0000755000175000017500000000000012253362407016435 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/functions/elementary/0000755000175000017500000000000012253362407020602 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/functions/elementary/complexes.py0000644000175000017500000006147712253362407023172 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import S, C from sympy.core.compatibility import u 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 from sympy.functions.elementary.trigonometric import atan, atan2 ############################################################################### ######################### REAL and IMAGINARY PARTS ############################ ############################################################################### 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 = map(lambda xs: Add(*xs), [included, reverted, excluded]) return cls(a) - im(b) + c 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 = map(lambda xs: Add(*xs), [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 ################ ############################################################################### 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): # handle what we can if arg.is_Mul: c, args = arg.as_coeff_mul() unk = [] is_imag = c.is_imaginary is_neg = c.is_negative for a in args: if a.is_negative: is_neg = not is_neg elif a.is_positive: pass else: ai = im(a) if a.is_imaginary and ai.is_comparable: # i.e. a = I*real is_imag = not is_imag if ai.is_negative: is_neg = not is_neg else: unk.append(a) 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)) if arg is S.NaN: return S.NaN if arg.is_zero: # it may be an Expr that is 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 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_()) def _eval_rewrite_as_Piecewise(self, arg): if arg.is_real: return Piecewise((1, arg > 0), (-1, arg < 0), (0, True)) def _eval_simplify(self, ratio, measure): return self.func(self.args[0].factor()) 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)) Abs will always return a sympy object. See Also ======== sign, conjugate """ nargs = 1 is_real = True is_negative = False unbranched = True 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 # handle what we can 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_zero: # it may be an Expr that is zero return S.Zero 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 if exponent.is_integer and base is S.NegativeOne: return S.One 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)) def _eval_rewrite_as_Piecewise(self, arg): if arg.is_real: return Piecewise((arg, arg >= 0), (-arg, True)) 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) def _eval_rewrite_as_atan2(self, arg): x, y = re(self.args[0]), im(self.args[0]) return atan2(y, x) 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(u('\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 _ sympy-0.7.4.1/sympy/functions/elementary/hyperbolic.py0000644000175000017500000006460212253362407023324 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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 ############################## ############################################################################### class HyperbolicFunction(Function): """ Base class for hyperbolic functions. See Also ======== sinh, cosh, tanh, coth """ unbranched = True class sinh(HyperbolicFunction): r""" The hyperbolic sine function, `\frac{e^x - e^{-x}}{2}`. * sinh(x) -> Returns the hyperbolic sine of x See Also ======== cosh, tanh, asinh """ nargs = 1 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) 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 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()) 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_expand_complex(self, deep=True, **hints): re_part, im_part = self.as_real_imag(deep=deep, **hints) return re_part + im_part*S.ImaginaryUnit def _eval_expand_trig(self, deep=True, **hints): if deep: arg = self.args[0].expand(deep, **hints) else: arg = self.args[0] x = None if arg.is_Add: # TODO, implement more if deep stuff here x, y = arg.as_two_terms() else: coeff, terms = arg.as_coeff_Mul(rational=True) if coeff is not S.One and coeff.is_Integer and terms is not S.One: x = terms y = (coeff - 1)*x if x is not None: return (sinh(x)*cosh(y) + sinh(y)*cosh(x)).expand(trig=True) return sinh(arg) def _eval_rewrite_as_tractable(self, arg): return (C.exp(arg) - C.exp(-arg)) / 2 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_()) class cosh(HyperbolicFunction): r""" The hyperbolic cosine function, `\frac{e^x + e^{-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) @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_expand_complex(self, deep=True, **hints): re_part, im_part = self.as_real_imag(deep=deep, **hints) return re_part + im_part*S.ImaginaryUnit def _eval_expand_trig(self, deep=True, **hints): if deep: arg = self.args[0].expand(deep, **hints) else: arg = self.args[0] x = None if arg.is_Add: # TODO, implement more if deep stuff here x, y = arg.as_two_terms() else: coeff, terms = arg.as_coeff_Mul(rational=True) if coeff is not S.One and coeff.is_Integer and terms is not S.One: x = terms y = (coeff - 1)*x if x is not None: return (cosh(x)*cosh(y) + sinh(x)*sinh(y)).expand(trig=True) return cosh(arg) def _eval_rewrite_as_tractable(self, arg): return (C.exp(arg) + C.exp(-arg)) / 2 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_()) class tanh(HyperbolicFunction): r""" The hyperbolic tangent function, `\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) 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_tractable(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_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_()) class coth(HyperbolicFunction): r""" The hyperbolic cotangent function, `\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) 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_tractable(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_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 ############################# ############################################################################### 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 inverse(self, argindex=1): """ Returns the inverse of this function. """ return sinh def _sage_(self): import sage.all as sage return sage.asinh(self.args[0]._sage_()) 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 inverse(self, argindex=1): """ Returns the inverse of this function. """ return cosh def _sage_(self): import sage.all as sage return sage.acosh(self.args[0]._sage_()) 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 inverse(self, argindex=1): """ Returns the inverse of this function. """ return tanh def _sage_(self): import sage.all as sage return sage.atanh(self.args[0]._sage_()) 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 inverse(self, argindex=1): """ Returns the inverse of this function. """ return coth def _sage_(self): import sage.all as sage return sage.acoth(self.args[0]._sage_()) sympy-0.7.4.1/sympy/functions/elementary/integers.py0000644000175000017500000001176712253362407023010 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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 ######################### ############################################################################### 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 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 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 sympy-0.7.4.1/sympy/functions/elementary/trigonometric.py0000644000175000017500000016100112253362407024040 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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 from sympy.core.compatibility import xrange ############################################################################### ########################## TRIGONOMETRIC FUNCTIONS ############################ ############################################################################### class TrigonometricFunction(Function): """Base class for trigonometric functions. """ unbranched = 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 _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 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 ========== .. [1] 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) @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 (2*pi_coeff).is_integer: return S.NegativeOne**(pi_coeff - S.Half) 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_()) 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 ========== .. [1] 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) @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 (2*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 # 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 ReciprocalTrigonometricFunction(TrigonometricFunction): """Base class for reciprocal functions of trigonometric functions. """ nargs = 1 _reciprocal_of = None # mandatory, to be defined in subclass # _is_even and _is_odd are used for correct evaluation of csc(-x), sec(-x) # TODO refactor into TrigonometricFunction common parts of # trigonometric functions eval() like even/odd, func(x+2*k*pi), etc. _is_even = None # optional, to be defined in subclass _is_odd = None # optional, to be defined in subclass def _call_reciprocal(self, method_name, *args, **kwargs): # Calls method_name on _reciprocal_of o = self._reciprocal_of(self.args[0]) if kwargs: return getattr(o, method_name)(**kwargs) else: return getattr(o, method_name)(*args) def _calculate_reciprocal(self, method_name, *args, **kwargs): # If calling method_name on _reciprocal_of returns a value != None # then return the reciprocal of that value t = self._call_reciprocal(method_name, *args, **kwargs) return 1/t if t != None else t def _rewrite_reciprocal(self, method_name, arg): # Special handling for rewrite functions. If reciprocal rewrite returns # unmodified expression, then return None t = self._call_reciprocal(method_name, arg) if t != None and t != self._reciprocal_of(arg): return 1/t else: return def fdiff(self, argindex=1): return self._calculate_reciprocal("fdiff", argindex) def _eval_rewrite_as_exp(self, arg): return self._rewrite_reciprocal("_eval_rewrite_as_exp", arg) def _eval_rewrite_as_Pow(self, arg): return self._rewrite_reciprocal("_eval_rewrite_as_Pow", arg) def _eval_rewrite_as_sin(self, arg): return self._rewrite_reciprocal("_eval_rewrite_as_sin", arg) def _eval_rewrite_as_cos(self, arg): return self._rewrite_reciprocal("_eval_rewrite_as_cos", arg) def _eval_rewrite_as_tan(self, arg): return self._rewrite_reciprocal("_eval_rewrite_as_tan", arg) def _eval_rewrite_as_pow(self, arg): return self._rewrite_reciprocal("_eval_rewrite_as_pow", arg) def _eval_rewrite_as_sqrt(self, arg): return self._rewrite_reciprocal("_eval_rewrite_as_sqrt", arg) def _eval_conjugate(self): return self.func(self.args[0].conjugate()) def as_real_imag(self, deep=True, **hints): return (1/self._reciprocal_of(self.args[0])).as_real_imag(deep, **hints) def _eval_expand_trig(self, **hints): return self._calculate_reciprocal("_eval_expand_trig", **hints) def _eval_is_real(self): return self._reciprocal_of(self.args[0])._eval_is_real() def _eval_as_leading_term(self, x): return (1/self._reciprocal_of(self.args[0]))._eval_as_leading_term(x) def _eval_is_bounded(self): return (1/self._reciprocal_of(self.args[0])).is_bounded def _eval_nseries(self, x, n, logx): return (1/self._reciprocal_of(self.args[0]))._eval_nseries(x, n, logx) @classmethod def eval(cls, arg): if arg.could_extract_minus_sign(): if cls._is_even: return cls(-arg) if cls._is_odd: return -cls(-arg) pi_coeff = _pi_coeff(arg) if (pi_coeff is not None and not (2*pi_coeff).is_integer and 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) t = cls._reciprocal_of.eval(arg) return 1/t if t != None else t class sec(ReciprocalTrigonometricFunction): _reciprocal_of = cos _is_even = True 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)) def fdiff(self, argindex=1): if argindex == 1: return tan(self.args[0])*sec(self.args[0]) else: raise ArgumentIndexError(self, argindex) # TODO def taylor_term(n, x, *previous_terms): def _sage_(self): import sage.all as sage return sage.sec(self.args[0]._sage_()) class csc(ReciprocalTrigonometricFunction): _reciprocal_of = sin _is_odd = True 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)) def fdiff(self, argindex=1): if argindex == 1: return -cot(self.args[0])*csc(self.args[0]) else: raise ArgumentIndexError(self, argindex) # TODO def taylor_term(n, x, *previous_terms): def _sage_(self): import sage.all as sage return sage.csc(self.args[0]._sage_()) 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 ========== .. [1] 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) 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 xrange(n) ] p = [0, 0] for i in xrange(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_()) 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) def inverse(self, argindex=1): """ Returns 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 self.rewrite(tan)._eval_nseries(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 xrange(n) ] p = [0, 0] for i in xrange(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 ############################ ############################################################################### 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) 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 @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 inverse(self, argindex=1): """ Returns the inverse of this function. """ return sin def _sage_(self): import sage.all as sage return sage.asin(self.args[0]._sage_()) 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) 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 @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): return atan(sqrt(1 - x**2)/x) + (S.Pi/2)*(1 - x*sqrt(1/x**2)) def inverse(self, argindex=1): """ Returns the inverse of this function. """ return cos def _sage_(self): import sage.all as sage return sage.acos(self.args[0]._sage_()) 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) 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 @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 inverse(self, argindex=1): """ Returns the inverse of this function. """ return tan def _sage_(self): import sage.all as sage return sage.atan(self.args[0]._sage_()) 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) 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 @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 _eval_rewrite_as_log(self, x): return S.ImaginaryUnit/2 * \ (C.log((x - S.ImaginaryUnit)/(x + S.ImaginaryUnit))) def inverse(self, argindex=1): """ Returns the inverse of this function. """ return cot def _sage_(self): import sage.all as sage return sage.acot(self.args[0]._sage_()) class atan2(Function): r""" The function ``atan2(y, x)`` computes `\operatorname{atan}(y/x)` taking two arguments `y` and `x`. Signs of both `y` and `x` are considered to determine the appropriate quadrant of `\operatorname{atan}(y/x)`. The range is `(-\pi, \pi]`. The complete definition reads as follows: .. math:: \operatorname{atan2}(y, x) = \begin{cases} \arctan\left(\frac y x\right) & \qquad x > 0 \\ \arctan\left(\frac y x\right) + \pi& \qquad y \ge 0 , x < 0 \\ \arctan\left(\frac y x\right) - \pi& \qquad y < 0 , x < 0 \\ +\frac{\pi}{2} & \qquad y > 0 , x = 0 \\ -\frac{\pi}{2} & \qquad y < 0 , x = 0 \\ \text{undefined} & \qquad y = 0, x = 0 \end{cases} Attention: Note the role reversal of both arguments. The `y`-coordinate is the first argument and the `x`-coordinate the second. Examples ======== Going counter-clock wise around the origin we find the following angles: >>> from sympy import atan2 >>> atan2(0, 1) 0 >>> atan2(1, 1) pi/4 >>> atan2(1, 0) pi/2 >>> atan2(1, -1) 3*pi/4 >>> atan2(0, -1) pi >>> atan2(-1, -1) -3*pi/4 >>> atan2(-1, 0) -pi/2 >>> atan2(-1, 1) -pi/4 which are all correct. Compare this to the results of the ordinary `\operatorname{atan}` function for the point `(x, y) = (-1, 1)` >>> from sympy import atan, S >>> atan(S(1) / -1) -pi/4 >>> atan2(1, -1) 3*pi/4 where only the `\operatorname{atan2}` function reurns what we expect. We can differentiate the function with respect to both arguments: >>> from sympy import diff >>> from sympy.abc import x, y >>> diff(atan2(y, x), x) -y/(x**2 + y**2) >>> diff(atan2(y, x), y) x/(x**2 + y**2) We can express the `\operatorname{atan2}` function in terms of complex logarithms: >>> from sympy import log >>> atan2(y, x).rewrite(log) -I*log((x + I*y)/sqrt(x**2 + y**2)) and in terms of `\operatorname(atan)`: >>> from sympy import atan >>> atan2(y, x).rewrite(atan) 2*atan(y/(x + sqrt(x**2 + y**2))) but note that this form is undefined on the negative real axis. See Also ======== sin, cos, sec, csc, tan, cot asin, acos, atan References ========== .. [1] http://en.wikipedia.org/wiki/Atan2 .. [2] http://functions.wolfram.com/ElementaryFunctions/ArcTan2/ """ nargs = 2 @classmethod def eval(cls, y, x): if x is S.NegativeInfinity: if y.is_zero: # Special case y = 0 because we define Heaviside(0) = 1/2 return S.Pi return 2*S.Pi*(C.Heaviside(C.re(y))) - S.Pi elif x is S.Infinity: return S.Zero if x.is_real and y.is_real: if x.is_positive: return atan(y / x) elif x.is_negative: if y.is_negative: return atan(y / x) - S.Pi else: return atan(y / x) + S.Pi elif x.is_zero: if y.is_positive: return S.Pi/2 elif y.is_negative: return -S.Pi/2 elif y.is_zero: return S.NaN if y.is_zero and x.is_real and x.is_nonzero: return S.Pi * (S.One - C.Heaviside(x)) def _eval_rewrite_as_log(self, y, x): return -S.ImaginaryUnit*C.log((x + S.ImaginaryUnit*y) / sqrt(x**2 + y**2)) def _eval_rewrite_as_atan(self, y, x): return 2*atan(y / (sqrt(x**2 + y**2) + x)) def _eval_rewrite_as_arg(self, y, x): if (x.is_real or x.is_imaginary) and (y.is_real or y.is_imaginary): return C.arg(x + y*S.ImaginaryUnit) def _eval_is_real(self): return self.args[0].is_real and self.args[1].is_real def _eval_conjugate(self): return self.func(self.args[0].conjugate(), self.args[1].conjugate()) def fdiff(self, argindex): y, x = self.args if argindex == 1: # Diff wrt y return x/(x**2 + y**2) elif argindex == 2: # Diff wrt x return -y/(x**2 + y**2) else: raise ArgumentIndexError(self, argindex) def _eval_evalf(self, prec): y, x = self.args if x.is_real and y.is_real: super(atan2, self)._eval_evalf(prec) def _sage_(self): import sage.all as sage return sage.atan2(self.args[0]._sage_(), self.args[1]._sage_()) sympy-0.7.4.1/sympy/functions/elementary/benchmarks/0000755000175000017500000000000012253362407022717 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/functions/elementary/benchmarks/bench_exp.py0000644000175000017500000000025412253362407025225 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import exp, symbols x, y = symbols('x,y') e = exp(2*x) q = exp(3*x) def timeit_exp_subs(): e.subs(q, y) sympy-0.7.4.1/sympy/functions/elementary/piecewise.py0000644000175000017500000005453112253362407023141 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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, distribute_and_over_or, Not, Or, true, false) from sympy.core.compatibility import default_sort_key, xrange class ExprCondPair(Tuple): """Represents an expression, condition pair.""" def __new__(cls, expr, cond): return Tuple.__new__(cls, expr, cond) @property def expr(self): """ Returns the expression of this pair. """ return self.args[0] @property def cond(self): """ Returns the condition of this pair. """ return self.args[1] @property 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 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 == 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 == 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 != 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 != 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 != 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: newcond = Or(cond, non_false_ecpairs[-1].cond) if isinstance(newcond, (And, Or)): newcond = distribute_and_over_or(newcond) non_false_ecpairs[-1] = ExprCondPair(expr, newcond) continue non_false_ecpairs.append(ExprCondPair(expr, cond)) if len(non_false_ecpairs) != len(args) or piecewise_again: return cls(*non_false_ecpairs) return None 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 self.func(*newargs) def _eval_as_leading_term(self, x): for e, c in self.args: if c == True or c.subs(x, 0) == True: return e.as_leading_term(x) def _eval_adjoint(self): return self.func(*[(e.adjoint(), c) for e, c in self.args]) def _eval_conjugate(self): return self.func(*[(e.conjugate(), c) for e, c in self.args]) def _eval_derivative(self, x): return self.func(*[(diff(e, x), c) for e, c in self.args]) def _eval_evalf(self, prec): return self.func(*[(e.evalf(prec), c) for e, c in self.args]) def _eval_integral(self, x): from sympy.integrals import integrate return self.func(*[(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 piecewise_fold( 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, expr in intervals: if (a < lower) is True: mid = lower rep = b val = e._eval_interval(sym, mid, b) val += self._eval_interval(sym, a, mid) elif (a > upper) is True: mid = upper rep = b val = e._eval_interval(sym, mid, b) val += self._eval_interval(sym, a, mid) elif (a >= lower) is True and (a <= upper) is True: rep = b val = e._eval_interval(sym, a, b) elif (b < lower) is True: mid = lower rep = a val = e._eval_interval(sym, a, mid) val += self._eval_interval(sym, mid, b) elif (b > upper) is True: mid = upper rep = a val = e._eval_interval(sym, a, mid) val += self._eval_interval(sym, mid, b) elif ((b >= lower) is True) and ((b <= upper) is True): rep = a val = e._eval_interval(sym, a, b) 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 == True and i == len(values) - 1) or And(rep >= intervals[i][0], rep <= intervals[i][1]))) return self.func(*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: if isinstance(expr, Piecewise): # If we still have a Piecewise by now, _sort_expr_cond would # already have determined that its conditions are independent # of the integration variable, thus we just use substitution. ret_fun += piecewise_fold( super(Piecewise, expr)._eval_interval(sym, Max(a, int_a), Min(b, int_b))) else: 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.""" from sympy.solvers.inequalities import _solve_inequality default = None int_expr = [] expr_cond = [] or_cond = False or_intervals = [] independent_expr_cond = [] 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 == True: break for expr, cond in expr_cond: if cond == True: independent_expr_cond.append((expr, cond)) default = self.func(*independent_expr_cond) break orig_cond = cond if sym not in cond.free_symbols: independent_expr_cond.append((expr, cond)) continue elif isinstance(cond, Equality): continue elif isinstance(cond, And): lower = S.NegativeInfinity upper = S.Infinity for cond2 in cond.args: if sym not in [cond2.lts, cond2.gts]: cond2 = _solve_inequality(cond2, sym) if cond2.lts == sym: upper = Min(cond2.gts, upper) elif cond2.gts == sym: lower = Max(cond2.lts, lower) else: raise NotImplementedError( "Unable to handle interval evaluation of expression.") else: if sym not in [cond.lts, cond.gts]: cond = _solve_inequality(cond, sym) lower, upper = cond.lts, cond.gts # part 1: initialize with givens if cond.lts == sym: # part 1a: expand the side ... lower = S.NegativeInfinity # e.g. x <= 0 ---> -oo <= 0 elif cond.gts == 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 xrange(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][0].free_symbols) and \ self.__eval_cond(upper == int_expr[n][1]): upper = Min(upper, int_expr[n][0]) elif len(int_expr[n][1].free_symbols) and \ (lower >= int_expr[n][0]) is not True and \ (int_expr[n][1] == Min(lower, upper)) 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 orig_cond == targetcond: return [(lower, upper, None)] elif isinstance(targetcond, Or) and cond in targetcond.args: or_cond = Or(or_cond, cond) or_intervals.append((lower, upper, None)) 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()) for n in xrange(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 == True: return [(h[0], h[1], None) 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 self.func(*[(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. """ args = list(self.args) for i, (e, c) in enumerate(args): if isinstance(c, bool): pass elif isinstance(c, Basic): c = c._subs(old, new) if c != False: e = e._subs(old, new) args[i] = e, c if c == True: return self.func(*args) return self.func(*args) def _eval_transpose(self): return self.func(*[(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.""" from sympy.solvers.solvers import checksol if cond == True: return True if isinstance(cond, Equality): if checksol(cond, {}, minimal=True): # the equality is trivially solved return True diff = cond.lhs - cond.rhs if diff.is_commutative: return diff.is_zero return None 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 isinstance(arg, 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 isinstance(expr, Boolean): # If expr is Boolean, we must return some kind of PiecewiseBoolean. # This is constructed by means of Or, And and Not. # piecewise_fold(0 < Piecewise( (sin(x), x<0), (cos(x), True))) # can't return Piecewise((0 < sin(x), x < 0), (0 < cos(x), True)) # but instead Or(And(x < 0, 0 < sin(x)), And(0 < cos(x), Not(x<0))) other = True rtn = False for e, c in new_args: rtn = Or(rtn, And(other, c, e)) other = And(other, Not(c)) if len(piecewise_args) > 1: return piecewise_fold(rtn) return rtn if len(piecewise_args) > 1: return piecewise_fold(Piecewise(*new_args)) return Piecewise(*new_args) else: return expr.func(*new_args) sympy-0.7.4.1/sympy/functions/elementary/miscellaneous.py0000644000175000017500000003657112253362407024033 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import S, C, sympify from sympy.core.add import Add 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, ArgumentIndexError 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, with_metaclass, xrange from sympy.core.logic import fuzzy_and class IdentityFunction(with_metaclass(Singleton, Lambda)): """ 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 ################### ############################################################################### 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: >>> from sympy import Eq >>> Eq(sqrt(x**2), x).subs(x, -1) False 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, real_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) def cbrt(arg): """This function computes the principial cube root of `arg`, so it's just a shortcut for `arg**Rational(1, 3)`. Examples ======== >>> from sympy import cbrt, Symbol >>> x = Symbol('x') >>> cbrt(x) x**(1/3) >>> cbrt(x)**3 x Note that cbrt(x**3) does not simplify to x. >>> cbrt(x**3) (x**3)**(1/3) This is because the two are not equal to each other in general. For example, consider `x == -1`: >>> from sympy import Eq >>> Eq(cbrt(x**3), x).subs(x, -1) False This is because cbrt computes the principal cube root, this identity does hold if `x` is positive: >>> y = Symbol('y', positive=True) >>> cbrt(y**3) y See Also ======== sympy.polys.rootoftools.RootOf, root, real_root References ========== * http://en.wikipedia.org/wiki/Cube_root * http://en.wikipedia.org/wiki/Principal_value """ return C.Pow(arg, C.Rational(1, 3)) 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 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) @property def is_real(self): return fuzzy_and(arg.is_real for arg in self.args) 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) def fdiff( self, argindex ): from sympy.functions.special.delta_functions import Heaviside n = len(self.args) if 0 < argindex and argindex <= n: argindex -= 1 if n == 2: return Heaviside( self.args[argindex] - self.args[1-argindex] ) newargs = tuple([self.args[i] for i in xrange(n) if i != argindex]) return Heaviside( self.args[argindex] - Max(*newargs) ) else: raise ArgumentIndexError(self, argindex) 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) def fdiff( self, argindex ): from sympy.functions.special.delta_functions import Heaviside n = len(self.args) if 0 < argindex and argindex <= n: argindex -= 1 if n == 2: return Heaviside( self.args[1-argindex] - self.args[argindex] ) newargs = tuple([ self.args[i] for i in xrange(n) if i != argindex]) return Heaviside( Min(*newargs) - self.args[argindex] ) else: raise ArgumentIndexError(self, argindex) sympy-0.7.4.1/sympy/functions/elementary/exponential.py0000644000175000017500000005647312253362407023521 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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 from sympy.core.compatibility import xrange # 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_negative: 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 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 is True: 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) class exp(ExpBase): """ The exponential function, :math:`e^x`. See Also ======== log """ 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) elif arg.is_Matrix: from sympy import Matrix return arg.exp() @property def base(self): """ Returns the base of the exponential function. """ return S.Exp1 @staticmethod @cacheit 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) 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_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 xrange(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_()) class log(Function): """ The natural logarithm function `\ln(x)` or `\log(x)`. Logarithms are taken with the natural base, `e`. To get a logarithm of a different base ``b``, use ``log(x, b)``, which is essentially short-hand for ``log(x)/log(b)``. See Also ======== exp """ nargs = (1, 2) 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) def inverse(self, argindex=1): """ Returns `e^x`, the inverse function of `\log(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: # handle extraction of powers of the base now # or else expand_log in Mul would have to handle this n = multiplicity(base, arg) if n: den = base**n if den.is_Integer: return n + log(arg // den) / log(base) else: return n + log(arg / den) / log(base) else: return log(arg)/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) 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) def as_base_exp(self): """ Returns this function in the form (base, exponent). """ return self, S.One @staticmethod @cacheit 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 from sympy.concrete import Sum, Product force = hints.get('force', False) arg = self.args[0] if arg.is_Integer: # remove perfect powers p = perfect_power(int(arg)) if p is not False: return p[1]*self.func(p[0]) elif 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) elif x.is_negative: a = self.func(-x) expr.append(a) nonpos.append(S.NegativeOne) 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 elif isinstance(arg, Product): if arg.function.is_positive: return Sum(log(arg.function), *arg.limits) return self.func(arg) def _eval_simplify(self, ratio, measure): from sympy.simplify.simplify import expand_log, logcombine, simplify expr = self.func(simplify(self.args[0], ratio=ratio, measure=measure)) expr = expand_log(expr, deep=True) return min([expr, self], key=measure) 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 xrange(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_()) 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 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 sympy-0.7.4.1/sympy/functions/elementary/tests/0000755000175000017500000000000012253362407021744 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/functions/elementary/tests/test_interface.py0000644000175000017500000000363012253362407025317 0ustar georgeskgeorgesk# This test file tests the SymPy function interface, that people use to create # their own new functions. It should be as easy as possible. from sympy import Function, sympify, sin, cos, limit, tanh from sympy.abc import x def test_function_series1(): """Create our new "sin" function.""" 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) #Test that the taylor series is correct assert my_function(x).series(x, 0, 10) == sin(x).series(x, 0, 10) assert limit(my_function(x)/x, x, 0) == 1 def test_function_series2(): """Create our new "cos" function.""" class my_function2(Function): nargs = 1 def fdiff(self, argindex=1): return -sin(self.args[0]) @classmethod def eval(cls, arg): arg = sympify(arg) if arg == 0: return sympify(1) #Test that the taylor series is correct assert my_function2(x).series(x, 0, 10) == cos(x).series(x, 0, 10) def test_function_series3(): """ Test our easy "tanh" function. This test tests two things: * that the Function interface works as expected and it's easy to use * that the general algorithm for the series expansion works even when the derivative is defined recursively in terms of the original function, since tanh(x).diff(x) == 1-tanh(x)**2 """ class mytanh(Function): nargs = 1 def fdiff(self, argindex=1): return 1 - mytanh(self.args[0])**2 @classmethod def eval(cls, arg): arg = sympify(arg) if arg == 0: return sympify(0) e = tanh(x) f = mytanh(x) assert tanh(x).series(x, 0, 6) == mytanh(x).series(x, 0, 6) sympy-0.7.4.1/sympy/functions/elementary/tests/test_miscellaneous.py0000644000175000017500000001407512253362407026227 0ustar georgeskgeorgeskfrom sympy.core.symbol import Symbol from sympy.core.numbers import Rational from sympy.utilities.pytest import raises from sympy.functions.elementary.miscellaneous import sqrt, cbrt, root, Min, Max, real_root from sympy import S, Float, I, cos, sin, oo, pi, Add def test_Min(): from sympy.abc import x, y, z n = Symbol('n', negative=True) n_ = Symbol('n_', negative=True) nn = Symbol('nn', nonnegative=True) nn_ = Symbol('nn_', nonnegative=True) p = Symbol('p', positive=True) p_ = Symbol('p_', positive=True) np = Symbol('np', nonpositive=True) np_ = Symbol('np_', nonpositive=True) assert Min(5, 4) == 4 assert Min(-oo, -oo) == -oo assert Min(-oo, n) == -oo assert Min(n, -oo) == -oo assert Min(-oo, np) == -oo assert Min(np, -oo) == -oo assert Min(-oo, 0) == -oo assert Min(0, -oo) == -oo assert Min(-oo, nn) == -oo assert Min(nn, -oo) == -oo assert Min(-oo, p) == -oo assert Min(p, -oo) == -oo assert Min(-oo, oo) == -oo assert Min(oo, -oo) == -oo assert Min(n, n) == n assert Min(n, np) == Min(n, np) assert Min(np, n) == Min(np, n) assert Min(n, 0) == n assert Min(0, n) == n assert Min(n, nn) == n assert Min(nn, n) == n assert Min(n, p) == n assert Min(p, n) == n assert Min(n, oo) == n assert Min(oo, n) == n assert Min(np, np) == np assert Min(np, 0) == np assert Min(0, np) == np assert Min(np, nn) == np assert Min(nn, np) == np assert Min(np, p) == np assert Min(p, np) == np assert Min(np, oo) == np assert Min(oo, np) == np assert Min(0, 0) == 0 assert Min(0, nn) == 0 assert Min(nn, 0) == 0 assert Min(0, p) == 0 assert Min(p, 0) == 0 assert Min(0, oo) == 0 assert Min(oo, 0) == 0 assert Min(nn, nn) == nn assert Min(nn, p) == Min(nn, p) assert Min(p, nn) == Min(p, nn) assert Min(nn, oo) == nn assert Min(oo, nn) == nn assert Min(p, p) == p assert Min(p, oo) == p assert Min(oo, p) == p assert Min(oo, oo) == oo assert Min(n, n_).func is Min assert Min(nn, nn_).func is Min assert Min(np, np_).func is Min assert Min(p, p_).func is Min # lists raises(ValueError, lambda: Min()) assert Min(x, y) == Min(y, x) assert Min(x, y, z) == Min(z, y, x) assert Min(x, Min(y, z)) == Min(z, y, x) assert Min(x, Max(y, -oo)) == Min(x, y) assert Min(p, oo, n, p, p, p_) == n assert Min(p_, n_, p) == n_ assert Min(n, oo, -7, p, p, 2) == Min(n, -7) assert Min(2, x, p, n, oo, n_, p, 2, -2, -2) == Min(-2, x, n, n_) assert Min(0, x, 1, y) == Min(0, x, y) assert Min(1000, 100, -100, x, p, n) == Min(n, x, -100) assert Min(cos(x), sin(x)) == Min(cos(x), sin(x)) assert Min(cos(x), sin(x)).subs(x, 1) == cos(1) assert Min(cos(x), sin(x)).subs(x, S(1)/2) == sin(S(1)/2) raises(ValueError, lambda: Min(cos(x), sin(x)).subs(x, I)) raises(ValueError, lambda: Min(I)) raises(ValueError, lambda: Min(I, x)) raises(ValueError, lambda: Min(S.ComplexInfinity, x)) from sympy.functions.special.delta_functions import Heaviside assert Min(1,x).diff(x) == Heaviside(1-x) assert Min(x,1).diff(x) == Heaviside(1-x) assert Min(0,-x,1-2*x).diff(x) == -Heaviside(x + Min(0, -2*x + 1)) \ - 2*Heaviside(2*x + Min(0, -x) - 1) a, b = Symbol('a', real=True), Symbol('b', real=True) # a and b are both real, Min(a, b) should be real assert Min(a, b).is_real def test_Max(): from sympy.abc import x, y, z n = Symbol('n', negative=True) n_ = Symbol('n_', negative=True) nn = Symbol('nn', nonnegative=True) nn_ = Symbol('nn_', nonnegative=True) p = Symbol('p', positive=True) p_ = Symbol('p_', positive=True) np = Symbol('np', nonpositive=True) np_ = Symbol('np_', nonpositive=True) assert Max(5, 4) == 5 # lists raises(ValueError, lambda: Max()) assert Max(x, y) == Max(y, x) assert Max(x, y, z) == Max(z, y, x) assert Max(x, Max(y, z)) == Max(z, y, x) assert Max(x, Min(y, oo)) == Max(x, y) assert Max(n, -oo, n_, p, 2) == Max(p, 2) assert Max(n, -oo, n_, p) == p assert Max(2, x, p, n, -oo, S.NegativeInfinity, n_, p, 2) == Max(2, x, p) assert Max(0, x, 1, y) == Max(1, x, y) assert Max(x, x + 1, x - 1) == 1 + x assert Max(1000, 100, -100, x, p, n) == Max(p, x, 1000) assert Max(cos(x), sin(x)) == Max(sin(x), cos(x)) assert Max(cos(x), sin(x)).subs(x, 1) == sin(1) assert Max(cos(x), sin(x)).subs(x, S(1)/2) == cos(S(1)/2) raises(ValueError, lambda: Max(cos(x), sin(x)).subs(x, I)) raises(ValueError, lambda: Max(I)) raises(ValueError, lambda: Max(I, x)) raises(ValueError, lambda: Max(S.ComplexInfinity, 1)) # interesting: # Max(n, -oo, n_, p, 2) == Max(p, 2) # True # Max(n, -oo, n_, p, 1000) == Max(p, 1000) # False from sympy.functions.special.delta_functions import Heaviside assert Max(1,x).diff(x) == Heaviside(x-1) assert Max(x,1).diff(x) == Heaviside(x-1) assert Max(x**2, 1+x, 1).diff(x) == 2*x*Heaviside(x**2 - Max(1,x+1)) \ + Heaviside(x - Max(1,x**2) + 1) a, b = Symbol('a', real=True), Symbol('b', real=True) # a and b are both real, Max(a, b) should be real assert Max(a, b).is_real def test_root(): from sympy.abc import x, y, z n = Symbol('n', integer=True) assert root(2, 2) == sqrt(2) assert root(2, 1) == 2 assert root(2, 3) == 2**Rational(1, 3) assert root(2, 3) == cbrt(2) assert root(2, -5) == 2**Rational(4, 5)/2 assert root(-2, 1) == -2 assert root(-2, 2) == sqrt(2)*I assert root(-2, 1) == -2 assert root(x, 2) == sqrt(x) assert root(x, 1) == x assert root(x, 3) == x**Rational(1, 3) assert root(x, 3) == cbrt(x) assert root(x, -5) == x**Rational(-1, 5) assert root(x, n) == x**(1/n) assert root(x, -n) == x**(-1/n) def test_nthroot(): assert real_root(-8, 3) == -2 assert real_root(-16, 4) == root(-16, 4) r = root(-7, 4) assert real_root(r) == r r1 = root(-1, 3) r2 = r1**2 r3 = root(-1, 4) assert real_root(r1 + r2 + r3) == -1 + r2 + r3 sympy-0.7.4.1/sympy/functions/elementary/tests/test_hyperbolic.py0000644000175000017500000004203212253362407025516 0ustar georgeskgeorgeskfrom sympy import symbols, Symbol, sinh, nan, oo, zoo, pi, asinh, acosh, log, sqrt, \ coth, I, cot, E, tanh, tan, cosh, cos, S, sin, Rational, atanh, acoth, \ Integer, O, exp from sympy.utilities.pytest import XFAIL, raises def test_sinh(): x, y = symbols('x,y') k = Symbol('k', integer=True) assert sinh(nan) == nan assert sinh(zoo) == nan assert sinh(oo) == oo assert sinh(-oo) == -oo assert sinh(0) == 0 assert sinh(1) == sinh(1) assert sinh(-1) == -sinh(1) assert sinh(x) == sinh(x) assert sinh(-x) == -sinh(x) assert sinh(pi) == sinh(pi) assert sinh(-pi) == -sinh(pi) assert sinh(2**1024 * E) == sinh(2**1024 * E) assert sinh(-2**1024 * E) == -sinh(2**1024 * E) assert sinh(pi*I) == 0 assert sinh(-pi*I) == 0 assert sinh(2*pi*I) == 0 assert sinh(-2*pi*I) == 0 assert sinh(-3*10**73*pi*I) == 0 assert sinh(7*10**103*pi*I) == 0 assert sinh(pi*I/2) == I assert sinh(-pi*I/2) == -I assert sinh(5*pi*I/2) == I assert sinh(7*pi*I/2) == -I assert sinh(pi*I/3) == S.Half*sqrt(3)*I assert sinh(-2*pi*I/3) == -S.Half*sqrt(3)*I assert sinh(pi*I/4) == S.Half*sqrt(2)*I assert sinh(-pi*I/4) == -S.Half*sqrt(2)*I assert sinh(17*pi*I/4) == S.Half*sqrt(2)*I assert sinh(-3*pi*I/4) == -S.Half*sqrt(2)*I assert sinh(pi*I/6) == S.Half*I assert sinh(-pi*I/6) == -S.Half*I assert sinh(7*pi*I/6) == -S.Half*I assert sinh(-5*pi*I/6) == -S.Half*I assert sinh(pi*I/105) == sin(pi/105)*I assert sinh(-pi*I/105) == -sin(pi/105)*I assert sinh(2 + 3*I) == sinh(2 + 3*I) assert sinh(x*I) == sin(x)*I assert sinh(k*pi*I) == 0 assert sinh(17*k*pi*I) == 0 assert sinh(k*pi*I/2) == sin(k*pi/2)*I def test_sinh_series(): x = Symbol('x') assert sinh(x).series(x, 0, 10) == \ x + x**3/6 + x**5/120 + x**7/5040 + x**9/362880 + O(x**10) def test_cosh(): x, y = symbols('x,y') k = Symbol('k', integer=True) assert cosh(nan) == nan assert cosh(zoo) == nan assert cosh(oo) == oo assert cosh(-oo) == oo assert cosh(0) == 1 assert cosh(1) == cosh(1) assert cosh(-1) == cosh(1) assert cosh(x) == cosh(x) assert cosh(-x) == cosh(x) assert cosh(pi*I) == cos(pi) assert cosh(-pi*I) == cos(pi) assert cosh(2**1024 * E) == cosh(2**1024 * E) assert cosh(-2**1024 * E) == cosh(2**1024 * E) assert cosh(pi*I/2) == 0 assert cosh(-pi*I/2) == 0 assert cosh(pi*I/2) == 0 assert cosh(-pi*I/2) == 0 assert cosh((-3*10**73 + 1)*pi*I/2) == 0 assert cosh((7*10**103 + 1)*pi*I/2) == 0 assert cosh(pi*I) == -1 assert cosh(-pi*I) == -1 assert cosh(5*pi*I) == -1 assert cosh(8*pi*I) == 1 assert cosh(pi*I/3) == S.Half assert cosh(-2*pi*I/3) == -S.Half assert cosh(pi*I/4) == S.Half*sqrt(2) assert cosh(-pi*I/4) == S.Half*sqrt(2) assert cosh(11*pi*I/4) == -S.Half*sqrt(2) assert cosh(-3*pi*I/4) == -S.Half*sqrt(2) assert cosh(pi*I/6) == S.Half*sqrt(3) assert cosh(-pi*I/6) == S.Half*sqrt(3) assert cosh(7*pi*I/6) == -S.Half*sqrt(3) assert cosh(-5*pi*I/6) == -S.Half*sqrt(3) assert cosh(pi*I/105) == cos(pi/105) assert cosh(-pi*I/105) == cos(pi/105) assert cosh(2 + 3*I) == cosh(2 + 3*I) assert cosh(x*I) == cos(x) assert cosh(k*pi*I) == cos(k*pi) assert cosh(17*k*pi*I) == cos(17*k*pi) assert cosh(k*pi) == cosh(k*pi) def test_cosh_series(): x = Symbol('x') assert cosh(x).series(x, 0, 10) == \ 1 + x**2/2 + x**4/24 + x**6/720 + x**8/40320 + O(x**10) def test_tanh(): x, y = symbols('x,y') k = Symbol('k', integer=True) assert tanh(nan) == nan assert tanh(zoo) == nan assert tanh(oo) == 1 assert tanh(-oo) == -1 assert tanh(0) == 0 assert tanh(1) == tanh(1) assert tanh(-1) == -tanh(1) assert tanh(x) == tanh(x) assert tanh(-x) == -tanh(x) assert tanh(pi) == tanh(pi) assert tanh(-pi) == -tanh(pi) assert tanh(2**1024 * E) == tanh(2**1024 * E) assert tanh(-2**1024 * E) == -tanh(2**1024 * E) assert tanh(pi*I) == 0 assert tanh(-pi*I) == 0 assert tanh(2*pi*I) == 0 assert tanh(-2*pi*I) == 0 assert tanh(-3*10**73*pi*I) == 0 assert tanh(7*10**103*pi*I) == 0 assert tanh(pi*I/2) == tanh(pi*I/2) assert tanh(-pi*I/2) == -tanh(pi*I/2) assert tanh(5*pi*I/2) == tanh(5*pi*I/2) assert tanh(7*pi*I/2) == tanh(7*pi*I/2) assert tanh(pi*I/3) == sqrt(3)*I assert tanh(-2*pi*I/3) == sqrt(3)*I assert tanh(pi*I/4) == I assert tanh(-pi*I/4) == -I assert tanh(17*pi*I/4) == I assert tanh(-3*pi*I/4) == I assert tanh(pi*I/6) == I/sqrt(3) assert tanh(-pi*I/6) == -I/sqrt(3) assert tanh(7*pi*I/6) == I/sqrt(3) assert tanh(-5*pi*I/6) == I/sqrt(3) assert tanh(pi*I/105) == tan(pi/105)*I assert tanh(-pi*I/105) == -tan(pi/105)*I assert tanh(2 + 3*I) == tanh(2 + 3*I) assert tanh(x*I) == tan(x)*I assert tanh(k*pi*I) == 0 assert tanh(17*k*pi*I) == 0 assert tanh(k*pi*I/2) == tan(k*pi/2)*I def test_tanh_series(): x = Symbol('x') assert tanh(x).series(x, 0, 10) == \ x - x**3/3 + 2*x**5/15 - 17*x**7/315 + 62*x**9/2835 + O(x**10) def test_coth(): x, y = symbols('x,y') k = Symbol('k', integer=True) assert coth(nan) == nan assert coth(zoo) == nan assert coth(oo) == 1 assert coth(-oo) == -1 assert coth(0) == coth(0) assert coth(1) == coth(1) assert coth(-1) == -coth(1) assert coth(x) == coth(x) assert coth(-x) == -coth(x) assert coth(pi*I) == -I*cot(pi) assert coth(-pi*I) == cot(pi)*I assert coth(2**1024 * E) == coth(2**1024 * E) assert coth(-2**1024 * E) == -coth(2**1024 * E) assert coth(pi*I) == -I*cot(pi) assert coth(-pi*I) == I*cot(pi) assert coth(2*pi*I) == -I*cot(2*pi) assert coth(-2*pi*I) == I*cot(2*pi) assert coth(-3*10**73*pi*I) == I*cot(3*10**73*pi) assert coth(7*10**103*pi*I) == -I*cot(7*10**103*pi) assert coth(pi*I/2) == 0 assert coth(-pi*I/2) == 0 assert coth(5*pi*I/2) == 0 assert coth(7*pi*I/2) == 0 assert coth(pi*I/3) == -I/sqrt(3) assert coth(-2*pi*I/3) == -I/sqrt(3) assert coth(pi*I/4) == -I assert coth(-pi*I/4) == I assert coth(17*pi*I/4) == -I assert coth(-3*pi*I/4) == -I assert coth(pi*I/6) == -sqrt(3)*I assert coth(-pi*I/6) == sqrt(3)*I assert coth(7*pi*I/6) == -sqrt(3)*I assert coth(-5*pi*I/6) == -sqrt(3)*I assert coth(pi*I/105) == -cot(pi/105)*I assert coth(-pi*I/105) == cot(pi/105)*I assert coth(2 + 3*I) == coth(2 + 3*I) assert coth(x*I) == -cot(x)*I assert coth(k*pi*I) == -cot(k*pi)*I assert coth(17*k*pi*I) == -cot(17*k*pi)*I assert coth(k*pi*I) == -cot(k*pi)*I def test_coth_series(): x = Symbol('x') assert coth(x).series(x, 0, 8) == \ 1/x + x/3 - x**3/45 + 2*x**5/945 - x**7/4725 + O(x**8) def test_asinh(): x, y = symbols('x,y') assert asinh(x) == asinh(x) assert asinh(-x) == -asinh(x) assert asinh(nan) == nan assert asinh( 0) == 0 assert asinh(+1) == log(sqrt(2) + 1) assert asinh(-1) == log(sqrt(2) - 1) assert asinh(I) == pi*I/2 assert asinh(-I) == -pi*I/2 assert asinh(I/2) == pi*I/6 assert asinh(-I/2) == -pi*I/6 assert asinh(oo) == oo assert asinh(-oo) == -oo assert asinh(I*oo) == oo assert asinh(-I *oo) == -oo assert asinh(zoo) == zoo assert asinh(I *(sqrt(3) - 1)/(2**(S(3)/2))) == pi*I/12 assert asinh(-I *(sqrt(3) - 1)/(2**(S(3)/2))) == -pi*I/12 assert asinh(I*(sqrt(5) - 1)/4) == pi*I/10 assert asinh(-I*(sqrt(5) - 1)/4) == -pi*I/10 assert asinh(I*(sqrt(5) + 1)/4) == 3*pi*I/10 assert asinh(-I*(sqrt(5) + 1)/4) == -3*pi*I/10 def test_asinh_series(): x = Symbol('x') assert asinh(x).series(x, 0, 8) == \ x - x**3/6 + 3*x**5/40 - 5*x**7/112 + O(x**8) t5 = asinh(x).taylor_term(5, x) assert t5 == 3*x**5/40 assert asinh(x).taylor_term(7, x, t5, 0) == -5*x**7/112 def test_acosh(): # TODO please write more tests -- see #652 # From http://functions.wolfram.com/ElementaryFunctions/ArcCosh/03/01/ # at specific points assert acosh(1) == 0 assert acosh(-1) == pi*I assert acosh(0) == I*pi/2 assert acosh(Rational(1, 2)) == I*pi/3 assert acosh(Rational(-1, 2)) == 2*pi*I/3 assert acosh(zoo) == oo assert acosh(I) == log(I*(1 + sqrt(2))) assert acosh(-I) == log(-I*(1 + sqrt(2))) assert acosh((sqrt(3) - 1)/(2*sqrt(2))) == 5*pi*I/12 assert acosh(-(sqrt(3) - 1)/(2*sqrt(2))) == 7*pi*I/12 assert acosh(sqrt(2)/2) == I*pi/4 assert acosh(-sqrt(2)/2) == 3*I*pi/4 assert acosh(sqrt(3)/2) == I*pi/6 assert acosh(-sqrt(3)/2) == 5*I*pi/6 assert acosh(sqrt(2 + sqrt(2))/2) == I*pi/8 assert acosh(-sqrt(2 + sqrt(2))/2) == 7*I*pi/8 assert acosh(sqrt(2 - sqrt(2))/2) == 3*I*pi/8 assert acosh(-sqrt(2 - sqrt(2))/2) == 5*I*pi/8 assert acosh((1 + sqrt(3))/(2*sqrt(2))) == I*pi/12 assert acosh(-(1 + sqrt(3))/(2*sqrt(2))) == 11*I*pi/12 assert acosh((sqrt(5) + 1)/4) == I*pi/5 assert acosh(-(sqrt(5) + 1)/4) == 4*I*pi/5 def test_acosh_infinities(): assert acosh(oo) == oo assert acosh(-oo) == oo assert acosh(I*oo) == oo assert acosh(-I*oo) == oo def test_acosh_series(): x = Symbol('x') assert acosh(x).series(x, 0, 8) == \ -I*x + pi*I/2 - I*x**3/6 - 3*I*x**5/40 - 5*I*x**7/112 + O(x**8) t5 = acosh(x).taylor_term(5, x) assert t5 == - 3*I*x**5/40 assert acosh(x).taylor_term(7, x, t5, 0) == - 5*I*x**7/112 # TODO please write more tests -- see #652 def test_atanh(): # TODO please write more tests -- see #652 # From http://functions.wolfram.com/ElementaryFunctions/ArcTanh/03/01/ # at specific points x = Symbol('x') #at specific points assert atanh(0) == 0 assert atanh(I) == I*pi/4 assert atanh(-I) == -I*pi/4 assert atanh(1) == oo assert atanh(-1) == -oo # at infinites assert atanh(I*oo) == I*pi/2 assert atanh(-I*oo) == -I*pi/2 assert atanh(zoo) == nan #properties assert atanh(-x) == -atanh(x) assert atanh(I/sqrt(3)) == I*pi/6 assert atanh(-I/sqrt(3)) == -I*pi/6 assert atanh(I*sqrt(3)) == I*pi/3 assert atanh(-I*sqrt(3)) == -I*pi/3 assert atanh(I*(1 + sqrt(2))) == 3*pi*I/8 assert atanh(I*(sqrt(2) - 1)) == pi*I/8 assert atanh(I*(1 - sqrt(2))) == -pi*I/8 assert atanh(-I*(1 + sqrt(2))) == -3*pi*I/8 assert atanh(I*sqrt(5 + 2*sqrt(5))) == 2*I*pi/5 assert atanh(-I*sqrt(5 + 2*sqrt(5))) == -2*I*pi/5 assert atanh(I*(2 - sqrt(3))) == pi*I/12 assert atanh(I*(sqrt(3) - 2)) == -pi*I/12 assert atanh(oo) == -I*pi/2 def test_atanh_series(): x = Symbol('x') assert atanh(x).series(x, 0, 10) == \ x + x**3/3 + x**5/5 + x**7/7 + x**9/9 + O(x**10) def test_atanh_infinities(): assert atanh(oo) == -I*pi/2 assert atanh(-oo) == I*pi/2 # TODO please write more tests -- see #652 def test_acoth(): # TODO please write more tests -- see #652 # From http://functions.wolfram.com/ElementaryFunctions/ArcCoth/03/01/ # at specific points x = Symbol('x') #at specific points assert acoth(0) == I*pi/2 assert acoth(I) == -I*pi/4 assert acoth(-I) == I*pi/4 assert acoth(1) == oo assert acoth(-1) == -oo # at infinites assert acoth(oo) == 0 assert acoth(-oo) == 0 assert acoth(I*oo) == 0 assert acoth(-I*oo) == 0 assert acoth(zoo) == 0 #properties assert acoth(-x) == -acoth(x) assert acoth(I/sqrt(3)) == -I*pi/3 assert acoth(-I/sqrt(3)) == I*pi/3 assert acoth(I*sqrt(3)) == -I*pi/6 assert acoth(-I*sqrt(3)) == I*pi/6 assert acoth(I*(1 + sqrt(2))) == -pi*I/8 assert acoth(-I*(sqrt(2) + 1)) == pi*I/8 assert acoth(I*(1 - sqrt(2))) == 3*pi*I/8 assert acoth(I*(sqrt(2) - 1)) == -3*pi*I/8 assert acoth(I*sqrt(5 + 2*sqrt(5))) == -I*pi/10 assert acoth(-I*sqrt(5 + 2*sqrt(5))) == I*pi/10 assert acoth(I*(2 + sqrt(3))) == -pi*I/12 assert acoth(-I*(2 + sqrt(3))) == pi*I/12 assert acoth(I*(2 - sqrt(3))) == -5*pi*I/12 assert acoth(I*(sqrt(3) - 2)) == 5*pi*I/12 def test_acoth_series(): x = Symbol('x') assert acoth(x).series(x, 0, 10) == \ I*pi/2 + x + x**3/3 + x**5/5 + x**7/7 + x**9/9 + O(x**10) def test_inverses(): x = Symbol('x') assert sinh(x).inverse() == asinh raises(AttributeError, lambda: cosh(x).inverse()) assert tanh(x).inverse() == atanh assert coth(x).inverse() == acoth assert asinh(x).inverse() == sinh assert acosh(x).inverse() == cosh assert atanh(x).inverse() == tanh assert acoth(x).inverse() == coth def test_leading_term(): x = Symbol('x') for func in [sinh, cosh, tanh, coth]: assert func(x).as_leading_term(x) == 1 for func in [asinh, acosh, atanh, acoth]: assert func(x).as_leading_term(x) == x for func in [sinh, cosh, tanh, coth, asinh, acosh, atanh, acoth]: for arg in (1/x, S.Half): eq = func(arg) assert eq.as_leading_term(x) == eq def test_complex(): a, b = symbols('a,b', real=True) z = a + b*I for func in [sinh, cosh, tanh, coth]: assert func(z).conjugate() == func(a - b*I) for deep in [True, False]: assert sinh(z).expand( complex=True, deep=deep) == sinh(a)*cos(b) + I*cosh(a)*sin(b) assert cosh(z).expand( complex=True, deep=deep) == cosh(a)*cos(b) + I*sinh(a)*sin(b) assert tanh(z).expand(complex=True, deep=deep) == sinh(a)*cosh( a)/(cos(b)**2 + sinh(a)**2) + I*sin(b)*cos(b)/(cos(b)**2 + sinh(a)**2) assert coth(z).expand(complex=True, deep=deep) == sinh(a)*cosh( a)/(sin(b)**2 + sinh(a)**2) - I*sin(b)*cos(b)/(sin(b)**2 + sinh(a)**2) def test_complex_2899(): a, b = symbols('a,b', real=True) for deep in [True, False]: for func in [sinh, cosh, tanh, coth]: assert func(a).expand(complex=True, deep=deep) == func(a) def test_simplifications(): x = Symbol('x') assert sinh(asinh(x)) == x assert sinh(acosh(x)) == sqrt(x - 1) * sqrt(x + 1) assert sinh(atanh(x)) == x/sqrt(1 - x**2) assert sinh(acoth(x)) == 1/(sqrt(x - 1) * sqrt(x + 1)) assert cosh(asinh(x)) == sqrt(1 + x**2) assert cosh(acosh(x)) == x assert cosh(atanh(x)) == 1/sqrt(1 - x**2) assert cosh(acoth(x)) == x/(sqrt(x - 1) * sqrt(x + 1)) assert tanh(asinh(x)) == x/sqrt(1 + x**2) assert tanh(acosh(x)) == sqrt(x - 1) * sqrt(x + 1) / x assert tanh(atanh(x)) == x assert tanh(acoth(x)) == 1/x assert coth(asinh(x)) == sqrt(1 + x**2)/x assert coth(acosh(x)) == x/(sqrt(x - 1) * sqrt(x + 1)) assert coth(atanh(x)) == 1/x assert coth(acoth(x)) == x def test_issue1037(): assert cosh(asinh(Integer(3)/2)) == sqrt(Integer(13)/4) def test_sinh_rewrite(): x = Symbol('x') assert sinh(x).rewrite(exp) == (exp(x) - exp(-x))/2 \ == sinh(x).rewrite('tractable') assert sinh(x).rewrite(cosh) == -I*cosh(x + I*pi/2) tanh_half = tanh(S.Half*x) assert sinh(x).rewrite(tanh) == 2*tanh_half/(1 - tanh_half**2) coth_half = coth(S.Half*x) assert sinh(x).rewrite(coth) == 2*coth_half/(coth_half**2 - 1) def test_cosh_rewrite(): x = Symbol('x') assert cosh(x).rewrite(exp) == (exp(x) + exp(-x))/2 \ == cosh(x).rewrite('tractable') assert cosh(x).rewrite(sinh) == -I*sinh(x + I*pi/2) tanh_half = tanh(S.Half*x)**2 assert cosh(x).rewrite(tanh) == (1 + tanh_half)/(1 - tanh_half) coth_half = coth(S.Half*x)**2 assert cosh(x).rewrite(coth) == (coth_half + 1)/(coth_half - 1) def test_tanh_rewrite(): x = Symbol('x') assert tanh(x).rewrite(exp) == (exp(x) - exp(-x))/(exp(x) + exp(-x)) \ == tanh(x).rewrite('tractable') assert tanh(x).rewrite(sinh) == I*sinh(x)/sinh(I*pi/2 - x) assert tanh(x).rewrite(cosh) == I*cosh(I*pi/2 - x)/cosh(x) assert tanh(x).rewrite(coth) == 1/coth(x) def test_coth_rewrite(): x = Symbol('x') assert coth(x).rewrite(exp) == (exp(x) + exp(-x))/(exp(x) - exp(-x)) \ == coth(x).rewrite('tractable') assert coth(x).rewrite(sinh) == -I*sinh(I*pi/2 - x)/sinh(x) assert coth(x).rewrite(cosh) == -I*cosh(x)/cosh(I*pi/2 - x) assert coth(x).rewrite(tanh) == 1/tanh(x) def test_derivs(): x = Symbol('x') assert coth(x).diff(x) == -sinh(x)**(-2) assert sinh(x).diff(x) == cosh(x) assert cosh(x).diff(x) == sinh(x) assert tanh(x).diff(x) == -tanh(x)**2 + 1 assert acoth(x).diff(x) == 1/(-x**2 + 1) assert asinh(x).diff(x) == 1/sqrt(x**2 + 1) assert acosh(x).diff(x) == 1/sqrt(x**2 - 1) assert atanh(x).diff(x) == 1/(-x**2 + 1) def test_sinh_expansion(): x,y = symbols('x,y') assert sinh(x+y).expand(trig=True) == sinh(x)*cosh(y) + cosh(x)*sinh(y) assert sinh(2*x).expand(trig=True) == 2*sinh(x)*cosh(x) assert sinh(3*x).expand(trig=True).expand() == \ sinh(x)**3 + 3*sinh(x)*cosh(x)**2 def test_cosh_expansion(): x,y = symbols('x,y') assert cosh(x+y).expand(trig=True) == cosh(x)*cosh(y) + sinh(x)*sinh(y) assert cosh(2*x).expand(trig=True) == cosh(x)**2 + sinh(x)**2 assert cosh(3*x).expand(trig=True).expand() == \ 3*sinh(x)**2*cosh(x) + cosh(x)**3 sympy-0.7.4.1/sympy/functions/elementary/tests/test_integers.py0000644000175000017500000001233112253362407025175 0ustar georgeskgeorgeskfrom sympy import Symbol, floor, nan, oo, E, symbols, ceiling, pi, Rational, \ Float, I, sin, exp, log, factorial from sympy.utilities.pytest import XFAIL def test_floor(): x = Symbol('x') y = Symbol('y', real=True) k, n = symbols('k,n', integer=True) assert floor(nan) == nan assert floor(oo) == oo assert floor(-oo) == -oo assert floor(0) == 0 assert floor(1) == 1 assert floor(-1) == -1 assert floor(E) == 2 assert floor(-E) == -3 assert floor(2*E) == 5 assert floor(-2*E) == -6 assert floor(pi) == 3 assert floor(-pi) == -4 assert floor(Rational(1, 2)) == 0 assert floor(-Rational(1, 2)) == -1 assert floor(Rational(7, 3)) == 2 assert floor(-Rational(7, 3)) == -3 assert floor(Float(17.0)) == 17 assert floor(-Float(17.0)) == -17 assert floor(Float(7.69)) == 7 assert floor(-Float(7.69)) == -8 assert floor(I) == I assert floor(-I) == -I assert floor(oo*I) == oo*I assert floor(-oo*I) == -oo*I assert floor(2*I) == 2*I assert floor(-2*I) == -2*I assert floor(I/2) == 0 assert floor(-I/2) == -I assert floor(E + 17) == 19 assert floor(pi + 2) == 5 assert floor(E + pi) == floor(E + pi) assert floor(I + pi) == floor(I + pi) assert floor(floor(pi)) == 3 assert floor(floor(y)) == floor(y) assert floor(floor(x)) == floor(floor(x)) assert floor(x) == floor(x) assert floor(2*x) == floor(2*x) assert floor(k*x) == floor(k*x) assert floor(k) == k assert floor(2*k) == 2*k assert floor(k*n) == k*n assert floor(k/2) == floor(k/2) assert floor(x + y) == floor(x + y) assert floor(x + 3) == floor(x + 3) assert floor(x + k) == floor(x + k) assert floor(y + 3) == floor(y) + 3 assert floor(y + k) == floor(y) + k assert floor(3 + I*y + pi) == 6 + floor(y)*I assert floor(k + n) == k + n assert floor(x*I) == floor(x*I) assert floor(k*I) == k*I assert floor(Rational(23, 10) - E*I) == 2 - 3*I assert floor(sin(1)) == 0 assert floor(sin(-1)) == -1 assert floor(exp(2)) == 7 assert floor(log(8)/log(2)) != 2 assert int(floor(log(8)/log(2)).evalf(chop=True)) == 3 assert floor(factorial(50)/exp(1)) == \ 11188719610782480504630258070757734324011354208865721592720336800 def test_ceiling(): x = Symbol('x') y = Symbol('y', real=True) k, n = symbols('k,n', integer=True) assert ceiling(nan) == nan assert ceiling(oo) == oo assert ceiling(-oo) == -oo assert ceiling(0) == 0 assert ceiling(1) == 1 assert ceiling(-1) == -1 assert ceiling(E) == 3 assert ceiling(-E) == -2 assert ceiling(2*E) == 6 assert ceiling(-2*E) == -5 assert ceiling(pi) == 4 assert ceiling(-pi) == -3 assert ceiling(Rational(1, 2)) == 1 assert ceiling(-Rational(1, 2)) == 0 assert ceiling(Rational(7, 3)) == 3 assert ceiling(-Rational(7, 3)) == -2 assert ceiling(Float(17.0)) == 17 assert ceiling(-Float(17.0)) == -17 assert ceiling(Float(7.69)) == 8 assert ceiling(-Float(7.69)) == -7 assert ceiling(I) == I assert ceiling(-I) == -I assert ceiling(oo*I) == oo*I assert ceiling(-oo*I) == -oo*I assert ceiling(2*I) == 2*I assert ceiling(-2*I) == -2*I assert ceiling(I/2) == I assert ceiling(-I/2) == 0 assert ceiling(E + 17) == 20 assert ceiling(pi + 2) == 6 assert ceiling(E + pi) == ceiling(E + pi) assert ceiling(I + pi) == ceiling(I + pi) assert ceiling(ceiling(pi)) == 4 assert ceiling(ceiling(y)) == ceiling(y) assert ceiling(ceiling(x)) == ceiling(ceiling(x)) assert ceiling(x) == ceiling(x) assert ceiling(2*x) == ceiling(2*x) assert ceiling(k*x) == ceiling(k*x) assert ceiling(k) == k assert ceiling(2*k) == 2*k assert ceiling(k*n) == k*n assert ceiling(k/2) == ceiling(k/2) assert ceiling(x + y) == ceiling(x + y) assert ceiling(x + 3) == ceiling(x + 3) assert ceiling(x + k) == ceiling(x + k) assert ceiling(y + 3) == ceiling(y) + 3 assert ceiling(y + k) == ceiling(y) + k assert ceiling(3 + pi + y*I) == 7 + ceiling(y)*I assert ceiling(k + n) == k + n assert ceiling(x*I) == ceiling(x*I) assert ceiling(k*I) == k*I assert ceiling(Rational(23, 10) - E*I) == 3 - 2*I assert ceiling(sin(1)) == 1 assert ceiling(sin(-1)) == 0 assert ceiling(exp(2)) == 8 assert ceiling(-log(8)/log(2)) != -2 assert int(ceiling(-log(8)/log(2)).evalf(chop=True)) == -3 assert ceiling(factorial(50)/exp(1)) == \ 11188719610782480504630258070757734324011354208865721592720336801 def test_series(): x, y = symbols('x,y') assert floor(x).nseries(x, y, 100) == floor(y) assert ceiling(x).nseries(x, y, 100) == ceiling(y) assert floor(x).nseries(x, pi, 100) == 3 assert ceiling(x).nseries(x, pi, 100) == 4 assert floor(x).nseries(x, 0, 100) == 0 assert ceiling(x).nseries(x, 0, 100) == 1 assert floor(-x).nseries(x, 0, 100) == -1 assert ceiling(-x).nseries(x, 0, 100) == 0 @XFAIL def test_issue_1050(): assert floor(3 + pi*I + y*I) == 3 + floor(pi + y)*I assert floor(3*I + pi*I + y*I) == floor(3 + pi + y)*I assert floor(3 + E + pi*I + y*I) == 5 + floor(pi + y)*I sympy-0.7.4.1/sympy/functions/elementary/tests/test_complexes.py0000644000175000017500000004462412253362407025366 0ustar georgeskgeorgeskfrom sympy import ( Abs, adjoint, arg, atan2, conjugate, cos, DiracDelta, E, exp, expand, Expr, Function, Heaviside, I, im, log, nan, oo, pi, Rational, re, S, sign, sin, sqrt, Symbol, symbols, transpose, zoo, exp_polar, Piecewise ) from sympy.utilities.pytest import XFAIL from sympy.utilities.randtest import comp def N_equals(a, b): """Check whether two complex numbers are numerically close""" return comp(a.n(), b.n(), 1.e-6) def test_re(): x, y = symbols('x,y') a, b = symbols('a,b', real=True) r = Symbol('r', real=True) i = Symbol('i', imaginary=True) assert re(nan) == nan assert re(oo) == oo assert re(-oo) == -oo assert re(0) == 0 assert re(1) == 1 assert re(-1) == -1 assert re(E) == E assert re(-E) == -E assert re(x) == re(x) assert re(x*I) == -im(x) assert re(r*I) == 0 assert re(r) == r assert re(i*I) == I * i assert re(i) == 0 assert re(x + y) == re(x + y) assert re(x + r) == re(x) + r assert re(re(x)) == re(x) assert re(2 + I) == 2 assert re(x + I) == re(x) assert re(x + y*I) == re(x) - im(y) assert re(x + r*I) == re(x) assert re(log(2*I)) == log(2) assert re((2 + I)**2).expand(complex=True) == 3 assert re(conjugate(x)) == re(x) assert conjugate(re(x)) == re(x) assert re(x).as_real_imag() == (re(x), 0) assert re(i*r*x).diff(r) == re(i*x) assert re(i*r*x).diff(i) == I*r*im(x) assert re( sqrt(a + b*I)) == (a**2 + b**2)**Rational(1, 4)*cos(atan2(b, a)/2) assert re(a * (2 + b*I)) == 2*a assert re((1 + sqrt(a + b*I))/2) == \ (a**2 + b**2)**Rational(1, 4)*cos(atan2(b, a)/2)/2 + Rational(1, 2) def test_im(): x, y = symbols('x,y') a, b = symbols('a,b', real=True) r = Symbol('r', real=True) i = Symbol('i', imaginary=True) assert im(nan) == nan assert im(oo*I) == oo assert im(-oo*I) == -oo assert im(0) == 0 assert im(1) == 0 assert im(-1) == 0 assert im(E*I) == E assert im(-E*I) == -E assert im(x) == im(x) assert im(x*I) == re(x) assert im(r*I) == r assert im(r) == 0 assert im(i*I) == 0 assert im(i) == -I * i assert im(x + y) == im(x + y) assert im(x + r) == im(x) assert im(x + r*I) == im(x) + r assert im(im(x)*I) == im(x) assert im(2 + I) == 1 assert im(x + I) == im(x) + 1 assert im(x + y*I) == im(x) + re(y) assert im(x + r*I) == im(x) + r assert im(log(2*I)) == pi/2 assert im((2 + I)**2).expand(complex=True) == 4 assert im(conjugate(x)) == -im(x) assert conjugate(im(x)) == im(x) assert im(x).as_real_imag() == (im(x), 0) assert im(i*r*x).diff(r) == im(i*x) assert im(i*r*x).diff(i) == -I * re(r*x) assert im( sqrt(a + b*I)) == (a**2 + b**2)**Rational(1, 4)*sin(atan2(b, a)/2) assert im(a * (2 + b*I)) == a*b assert im((1 + sqrt(a + b*I))/2) == \ (a**2 + b**2)**Rational(1, 4)*sin(atan2(b, a)/2)/2 def test_sign(): assert sign(1.2) == 1 assert sign(-1.2) == -1 assert sign(3*I) == I assert sign(-3*I) == -I assert sign(0) == 0 assert sign(nan) == nan assert sign(2 + 2*I).doit() == sqrt(2)*(2 + 2*I)/4 assert sign(2 + 3*I).simplify() == sign(2 + 3*I) assert sign(2 + 2*I).simplify() == sign(1 + I) x = Symbol('x') assert sign(x).is_bounded is True assert sign(x).is_complex is True assert sign(x).is_imaginary is None assert sign(x).is_integer is None assert sign(x).is_real is None assert sign(x).is_zero is None assert sign(x).doit() == sign(x) assert sign(1.2*x) == sign(x) assert sign(2*x) == sign(x) assert sign(I*x) == I*sign(x) assert sign(-2*I*x) == -I*sign(x) assert sign(conjugate(x)) == conjugate(sign(x)) p = Symbol('p', positive=True) n = Symbol('n', negative=True) m = Symbol('m', negative=True) assert sign(2*p*x) == sign(x) assert sign(n*x) == -sign(x) assert sign(n*m*x) == sign(x) x = Symbol('x', imaginary=True) assert sign(x).is_imaginary is True assert sign(x).is_integer is False assert sign(x).is_real is False assert sign(x).is_zero is False assert sign(x).diff(x) == 2*DiracDelta(-I*x) assert sign(x).doit() == x / Abs(x) assert conjugate(sign(x)) == -sign(x) x = Symbol('x', real=True) assert sign(x).is_imaginary is False assert sign(x).is_integer is True assert sign(x).is_real is True assert sign(x).is_zero is None assert sign(x).diff(x) == 2*DiracDelta(x) assert sign(x).doit() == sign(x) assert conjugate(sign(x)) == sign(x) x = Symbol('x', nonzero=True) assert sign(x).is_imaginary is None assert sign(x).is_integer is None assert sign(x).is_real is None assert sign(x).is_zero is False assert sign(x).doit() == x / Abs(x) assert sign(Abs(x)) == 1 assert Abs(sign(x)) == 1 x = Symbol('x', positive=True) assert sign(x).is_imaginary is False assert sign(x).is_integer is True assert sign(x).is_real is True assert sign(x).is_zero is False assert sign(x).doit() == x / Abs(x) assert sign(Abs(x)) == 1 assert Abs(sign(x)) == 1 x = 0 assert sign(x).is_imaginary is False assert sign(x).is_integer is True assert sign(x).is_real is True assert sign(x).is_zero is True assert sign(x).doit() == 0 assert sign(Abs(x)) == 0 assert Abs(sign(x)) == 0 nz = Symbol('nz', nonzero=True, integer=True) assert sign(nz).is_imaginary is False assert sign(nz).is_integer is True assert sign(nz).is_real is True assert sign(nz).is_zero is False assert sign(nz)**2 == 1 assert (sign(nz)**3).args == (sign(nz), 3) x, y = Symbol('x', real=True), Symbol('y') assert sign(x).rewrite(Piecewise) == \ Piecewise((1, x > 0), (-1, x < 0), (0, True)) assert sign(y).rewrite(Piecewise) == sign(y) # evaluate what can be evaluated assert sign(exp_polar(I*pi)*pi) is S.NegativeOne eq = -sqrt(10 + 6*sqrt(3)) + sqrt(1 + sqrt(3)) + sqrt(3 + 3*sqrt(3)) # if there is a fast way to know when and when you cannot prove an # expression like this is zero then the equality to zero is ok assert sign(eq).func is sign or sign(eq) == 0 # but sometimes it's hard to do this so it's better not to load # abs down with tests that will be very slow q = 1 + sqrt(2) - 2*sqrt(3) + 1331*sqrt(6) p = expand(q**3)**Rational(1, 3) d = p - q assert sign(d).func is sign or sign(d) == 0 def test_as_real_imag(): n = pi**1000 # the special code for working out the real # and complex parts of a power with Integer exponent # should not run if there is no imaginary part, hence # this should not hang assert n.as_real_imag() == (n, 0) # issue 3162 x = Symbol('x') assert sqrt(x).as_real_imag() == \ ((re(x)**2 + im(x)**2)**(S(1)/4)*cos(atan2(im(x), re(x))/2), (re(x)**2 + im(x)**2)**(S(1)/4)*sin(atan2(im(x), re(x))/2)) # issue 754 a, b = symbols('a,b', real=True) assert ((1 + sqrt(a + b*I))/2).as_real_imag() == \ ( (a**2 + b**2)**Rational( 1, 4)*cos(atan2(b, a)/2)/2 + Rational(1, 2), (a**2 + b**2)**Rational(1, 4)*sin(atan2(b, a)/2)/2) @XFAIL def test_sign_issue_3068(): n = pi**1000 i = int(n) assert (n - i).round() == 1 # doesn't hang assert sign(n - i) == 1 # perhaps it's not possible to get the sign right when # only 1 digit is being requested for this situtation; # 2 digits works assert (n - x).n(1, subs={x: i}) > 0 assert (n - x).n(2, subs={x: i}) > 0 def test_Abs(): x, y = symbols('x,y') assert sign(sign(x)) == sign(x) assert sign(x*y).func is sign assert Abs(0) == 0 assert Abs(1) == 1 assert Abs(-1) == 1 assert Abs(I) == 1 assert Abs(-I) == 1 assert Abs(nan) == nan assert Abs(I * pi) == pi assert Abs(-I * pi) == pi assert Abs(I * x) == Abs(x) assert Abs(-I * x) == Abs(x) assert Abs(-2*x) == 2*Abs(x) assert Abs(-2.0*x) == 2.0*Abs(x) assert Abs(2*pi*x*y) == 2*pi*Abs(x*y) assert Abs(conjugate(x)) == Abs(x) assert conjugate(Abs(x)) == Abs(x) a = Symbol('a', positive=True) assert Abs(2*pi*x*a) == 2*pi*a*Abs(x) assert Abs(2*pi*I*x*a) == 2*pi*a*Abs(x) x = Symbol('x', real=True) n = Symbol('n', integer=True) assert Abs((-1)**n) == 1 assert x**(2*n) == Abs(x)**(2*n) assert Abs(x).diff(x) == sign(x) assert abs(x) == Abs(x) # Python built-in assert Abs(x)**3 == x**2*Abs(x) assert Abs(x)**4 == x**4 assert ( Abs(x)**(3*n)).args == (Abs(x), 3*n) # leave symbolic odd unchanged assert (1/Abs(x)).args == (Abs(x), -1) assert 1/Abs(x)**3 == 1/(x**2*Abs(x)) x = Symbol('x', imaginary=True) assert Abs(x).diff(x) == -sign(x) eq = -sqrt(10 + 6*sqrt(3)) + sqrt(1 + sqrt(3)) + sqrt(3 + 3*sqrt(3)) # if there is a fast way to know when and when you cannot prove an # expression like this is zero then the equality to zero is ok assert abs(eq).func is Abs or abs(eq) == 0 # but sometimes it's hard to do this so it's better not to load # abs down with tests that will be very slow q = 1 + sqrt(2) - 2*sqrt(3) + 1331*sqrt(6) p = expand(q**3)**Rational(1, 3) d = p - q assert abs(d).func is Abs or abs(d) == 0 def test_Abs_rewrite(): x = Symbol('x', real=True) a = Abs(x).rewrite(Heaviside).expand() assert a == x*Heaviside(x) - x*Heaviside(-x) for i in [-2, -1, 0, 1, 2]: assert a.subs(x, i) == abs(i) y = Symbol('y') assert Abs(y).rewrite(Heaviside) == Abs(y) x, y = Symbol('x', real=True), Symbol('y') assert Abs(x).rewrite(Piecewise) == Piecewise((x, x >= 0), (-x, True)) assert Abs(y).rewrite(Piecewise) == Abs(y) def test_Abs_real(): # test some properties of abs that only apply # to real numbers x = Symbol('x', complex=True) assert sqrt(x**2) != Abs(x) assert Abs(x**2) != x**2 x = Symbol('x', real=True) assert sqrt(x**2) == Abs(x) assert Abs(x**2) == x**2 # if the symbol is zero, the following will still apply nn = Symbol('nn', nonnegative=True, real=True) np = Symbol('np', nonpositive=True, real=True) assert Abs(nn) == nn assert Abs(np) == -np def test_Abs_properties(): x = Symbol('x') assert Abs(x).is_real is True assert Abs(x).is_positive is None assert Abs(x).is_nonnegative is True w = Symbol('w', complex=True, zero=False) assert Abs(w).is_real is True assert Abs(w).is_positive is True assert Abs(w).is_zero is False q = Symbol('q', positive=True) assert Abs(q).is_real is True assert Abs(q).is_positive is True assert Abs(q).is_zero is False def test_abs(): # this tests that abs calls Abs; don't rename to # test_Abs since that test is already above a = Symbol('a', positive=True) assert abs(I*(1 + a)**2) == (1 + a)**2 def test_arg(): assert arg(0) == nan assert arg(1) == 0 assert arg(-1) == pi assert arg(I) == pi/2 assert arg(-I) == -pi/2 assert arg(1 + I) == pi/4 assert arg(-1 + I) == 3*pi/4 assert arg(1 - I) == -pi/4 p = Symbol('p', positive=True) assert arg(p) == 0 n = Symbol('n', negative=True) assert arg(n) == pi x = Symbol('x') assert conjugate(arg(x)) == arg(x) def test_arg_rewrite(): assert arg(1 + I) == atan2(1, 1) x = Symbol('x', real=True) y = Symbol('y', real=True) assert arg(x + I*y).rewrite(atan2) == atan2(y, x) def test_adjoint(): a = Symbol('a', antihermitian=True) b = Symbol('b', hermitian=True) assert adjoint(a) == -a assert adjoint(I*a) == I*a assert adjoint(b) == b assert adjoint(I*b) == -I*b assert adjoint(a*b) == -b*a assert adjoint(I*a*b) == I*b*a x, y = symbols('x y') assert adjoint(adjoint(x)) == x assert adjoint(x + y) == adjoint(x) + adjoint(y) assert adjoint(x - y) == adjoint(x) - adjoint(y) assert adjoint(x * y) == adjoint(x) * adjoint(y) assert adjoint(x / y) == adjoint(x) / adjoint(y) assert adjoint(-x) == -adjoint(x) x, y = symbols('x y', commutative=False) assert adjoint(adjoint(x)) == x assert adjoint(x + y) == adjoint(x) + adjoint(y) assert adjoint(x - y) == adjoint(x) - adjoint(y) assert adjoint(x * y) == adjoint(y) * adjoint(x) assert adjoint(x / y) == 1 / adjoint(y) * adjoint(x) assert adjoint(-x) == -adjoint(x) def test_conjugate(): a = Symbol('a', real=True) b = Symbol('b', imaginary=True) assert conjugate(a) == a assert conjugate(I*a) == -I*a assert conjugate(b) == -b assert conjugate(I*b) == I*b assert conjugate(a*b) == -a*b assert conjugate(I*a*b) == I*a*b x, y = symbols('x y') assert conjugate(conjugate(x)) == x assert conjugate(x + y) == conjugate(x) + conjugate(y) assert conjugate(x - y) == conjugate(x) - conjugate(y) assert conjugate(x * y) == conjugate(x) * conjugate(y) assert conjugate(x / y) == conjugate(x) / conjugate(y) assert conjugate(-x) == -conjugate(x) def test_conjugate_transpose(): x = Symbol('x') assert conjugate(transpose(x)) == adjoint(x) assert transpose(conjugate(x)) == adjoint(x) assert adjoint(transpose(x)) == conjugate(x) assert transpose(adjoint(x)) == conjugate(x) assert adjoint(conjugate(x)) == transpose(x) assert conjugate(adjoint(x)) == transpose(x) class Symmetric(Expr): def _eval_adjoint(self): return None def _eval_conjugate(self): return None def _eval_transpose(self): return self x = Symmetric() assert conjugate(x) == adjoint(x) assert transpose(x) == x def test_transpose(): a = Symbol('a', complex=True) assert transpose(a) == a assert transpose(I*a) == I*a x, y = symbols('x y') assert transpose(transpose(x)) == x assert transpose(x + y) == transpose(x) + transpose(y) assert transpose(x - y) == transpose(x) - transpose(y) assert transpose(x * y) == transpose(x) * transpose(y) assert transpose(x / y) == transpose(x) / transpose(y) assert transpose(-x) == -transpose(x) x, y = symbols('x y', commutative=False) assert transpose(transpose(x)) == x assert transpose(x + y) == transpose(x) + transpose(y) assert transpose(x - y) == transpose(x) - transpose(y) assert transpose(x * y) == transpose(y) * transpose(x) assert transpose(x / y) == 1 / transpose(y) * transpose(x) assert transpose(-x) == -transpose(x) def test_issue936(): x = Symbol('x') assert Abs(x).expand(trig=True) == Abs(x) assert sign(x).expand(trig=True) == sign(x) assert arg(x).expand(trig=True) == arg(x) def test_issue3206(): x = Symbol('x') assert Abs(Abs(x)) == Abs(x) def test_issue1655_derivative_conjugate(): x = Symbol('x', real=True) y = Symbol('y', imaginary=True) f = Function('f') assert (f(x).conjugate()).diff(x) == (f(x).diff(x)).conjugate() assert (f(y).conjugate()).diff(y) == -(f(y).diff(y)).conjugate() def test_derivatives_issue1658(): x = Symbol('x', real=True) y = Symbol('y', imaginary=True) f = Function('f') assert re(f(x)).diff(x) == re(f(x).diff(x)) assert im(f(x)).diff(x) == im(f(x).diff(x)) assert re(f(y)).diff(y) == -I*im(f(y).diff(y)) assert im(f(y)).diff(y) == -I*re(f(y).diff(y)) assert Abs(f(x)).diff(x).subs(f(x), 1 + I*x).doit() == x/sqrt(1 + x**2) assert arg(f(x)).diff(x).subs(f(x), 1 + I*x**2).doit() == 2*x/(1 + x**4) assert Abs(f(y)).diff(y).subs(f(y), 1 + y).doit() == -y/sqrt(1 - y**2) assert arg(f(y)).diff(y).subs(f(y), I + y**2).doit() == 2*y/(1 + y**4) def test_periodic_argument(): from sympy import (periodic_argument, unbranched_argument, oo, principal_branch, polar_lift, pi) x = Symbol('x') p = Symbol('p', positive=True) assert unbranched_argument(2 + I) == periodic_argument(2 + I, oo) assert unbranched_argument(1 + x) == periodic_argument(1 + x, oo) assert N_equals(unbranched_argument((1 + I)**2), pi/2) assert N_equals(unbranched_argument((1 - I)**2), -pi/2) assert N_equals(periodic_argument((1 + I)**2, 3*pi), pi/2) assert N_equals(periodic_argument((1 - I)**2, 3*pi), -pi/2) assert unbranched_argument(principal_branch(x, pi)) == \ periodic_argument(x, pi) assert unbranched_argument(polar_lift(2 + I)) == unbranched_argument(2 + I) assert periodic_argument(polar_lift(2 + I), 2*pi) == \ periodic_argument(2 + I, 2*pi) assert periodic_argument(polar_lift(2 + I), 3*pi) == \ periodic_argument(2 + I, 3*pi) assert periodic_argument(polar_lift(2 + I), pi) == \ periodic_argument(polar_lift(2 + I), pi) assert unbranched_argument(polar_lift(1 + I)) == pi/4 assert periodic_argument(2*p, p) == periodic_argument(p, p) assert periodic_argument(pi*p, p) == periodic_argument(p, p) @XFAIL def test_principal_branch_fail(): # TODO XXX why does abs(x)._eval_evalf() not fall back to global evalf? assert N_equals(principal_branch((1 + I)**2, pi/2), 0) def test_principal_branch(): from sympy import principal_branch, polar_lift, exp_polar p = Symbol('p', positive=True) x = Symbol('x') neg = Symbol('x', negative=True) assert principal_branch(polar_lift(x), p) == principal_branch(x, p) assert principal_branch(polar_lift(2 + I), p) == principal_branch(2 + I, p) assert principal_branch(2*x, p) == 2*principal_branch(x, p) assert principal_branch(1, pi) == exp_polar(0) assert principal_branch(-1, 2*pi) == exp_polar(I*pi) assert principal_branch(-1, pi) == exp_polar(0) assert principal_branch(exp_polar(3*pi*I)*x, 2*pi) == \ principal_branch(exp_polar(I*pi)*x, 2*pi) assert principal_branch(neg*exp_polar(pi*I), 2*pi) == neg*exp_polar(-I*pi) assert N_equals(principal_branch((1 + I)**2, 2*pi), 2*I) assert N_equals(principal_branch((1 + I)**2, 3*pi), 2*I) assert N_equals(principal_branch((1 + I)**2, 1*pi), 2*I) # test argument sanitization assert principal_branch(x, I).func is principal_branch assert principal_branch(x, -4).func is principal_branch assert principal_branch(x, -oo).func is principal_branch assert principal_branch(x, zoo).func is principal_branch @XFAIL def test_issue_3068_3052(): n = pi**1000 i = int(n) assert sign(n - i) == 1 assert abs(n - i) == n - i eps = pi**-1500 big = pi**1000 one = cos(x)**2 + sin(x)**2 e = big*one - big + eps assert sign(simplify(e)) == 1 for xi in (111, 11, 1, S(1)/10): assert sign(e.subs(x, xi)) == 1 sympy-0.7.4.1/sympy/functions/elementary/tests/test_exponential.py0000644000175000017500000002540712253362407025713 0ustar georgeskgeorgeskfrom sympy import (symbols, log, Float, nan, oo, zoo, I, pi, E, exp, Symbol, LambertW, sqrt, Rational, expand_log, S, sign, nextprime, conjugate, sin, cos, sinh, cosh, exp_polar, re, Function, simplify, Eq) def test_exp_values(): x, y = symbols('x,y') k = Symbol('k', integer=True) assert exp(nan) == nan assert exp(oo) == oo assert exp(-oo) == 0 assert exp(0) == 1 assert exp(1) == E assert exp(-1 + x).as_base_exp() == (S.Exp1, x - 1) assert exp(1 + x).as_base_exp() == (S.Exp1, x + 1) assert exp(pi*I/2) == I assert exp(pi*I) == -1 assert exp(3*pi*I/2) == -I assert exp(2*pi*I) == 1 assert exp(pi*I*2*k) == 1 assert exp(pi*I*2*(k + Rational(1, 2))) == -1 assert exp(pi*I*2*(k + Rational(1, 4))) == I assert exp(pi*I*2*(k + Rational(3, 4))) == -I assert exp(log(x)) == x assert exp(2*log(x)) == x**2 assert exp(pi*log(x)) == x**pi assert exp(17*log(x) + E*log(y)) == x**17 * y**E assert exp(x*log(x)) != x**x assert exp(sin(x)*log(x)) != x assert exp(3*log(x) + oo*x) == exp(oo*x) * x**3 assert exp(4*log(x)*log(y) + 3*log(x)) == x**3 * exp(4*log(x)*log(y)) def test_exp_log(): x = Symbol("x", real=True) assert log(exp(x)) == x assert exp(log(x)) == x assert log(x).inverse() == exp assert exp(x).inverse() == log y = Symbol("y", polar=True) z = Symbol("z") assert log(exp_polar(z)) == z assert exp(log(y)) == y def test_exp_expand(): x = Symbol("x") y = Symbol("y") e = exp(log(Rational(2))*(1 + x) - log(Rational(2))*x) assert e.expand() == 2 assert exp(x + y) != exp(x)*exp(y) assert exp(x + y).expand() == exp(x)*exp(y) def test_exp__as_base_exp(): x, y = symbols('x,y') assert exp(x).as_base_exp() == (E, x) assert exp(2*x).as_base_exp() == (E, 2*x) assert exp(x*y).as_base_exp() == (E, x*y) assert exp(-x).as_base_exp() == (E, -x) # Pow( *expr.as_base_exp() ) == expr invariant should hold assert E**x == exp(x) assert E**(2*x) == exp(2*x) assert E**(x*y) == exp(x*y) assert exp(x).base is S.Exp1 assert exp(x).exp == x def test_exp_infinity(): y = Symbol('y') assert exp(I*y) != nan assert exp(I*oo) == nan assert exp(-I*oo) == nan assert exp(y*I*oo) != nan def test_exp_subs(): x, y = symbols('x,y') e = (exp(3*log(x), evaluate=False)) assert e.subs(x**3, y**3) == e assert e.subs(x**2, 5) == e assert exp(3*log(x)).subs(x**2, y) == x**3 assert exp(5*x).subs(exp(7*x), y) == y**Rational(5, 7) assert exp(2*x + 7).subs(exp(3*x), y) == y**Rational(2, 3) * exp(7) assert exp(exp(x) + exp(x**2)).subs(exp(exp(x)), y) == y * exp(exp(x**2)) assert exp(x).subs(E, y) == y**x x = symbols('x', positive=True) assert exp(3*log(x)).subs(x**2, y) == y**Rational(3, 2) def test_exp_conjugate(): x = Symbol('x') assert conjugate(exp(x)) == exp(conjugate(x)) def test_exp_rewrite(): x = symbols('x') assert exp(x).rewrite(sin) == sinh(x) + cosh(x) assert exp(x*I).rewrite(cos) == cos(x) + I*sin(x) assert exp(1).rewrite(cos) == sinh(1) + cosh(1) assert exp(1).rewrite(sin) == sinh(1) + cosh(1) def test_exp_leading_term(): x = symbols('x') assert exp(x).as_leading_term(x) == 1 assert exp(1/x).as_leading_term(x) == exp(1/x) assert exp(2 + x).as_leading_term(x) == exp(2) def test_log_values(): assert log(nan) == nan assert log(oo) == oo assert log(-oo) == oo assert log(zoo) == zoo assert log(-zoo) == zoo assert log(0) == zoo assert log(1) == 0 assert log(-1) == I*pi assert log(E) == 1 assert log(-E).expand() == 1 + I*pi assert log(pi) == log(pi) assert log(-pi).expand() == log(pi) + I*pi assert log(17) == log(17) assert log(-17) == log(17) + I*pi assert log(I) == I*pi/2 assert log(-I) == -I*pi/2 assert log(17*I) == I*pi/2 + log(17) assert log(-17*I).expand() == -I*pi/2 + log(17) assert log(oo*I) == oo assert log(-oo*I) == oo assert exp(-log(3))**(-1) == 3 assert log(S.Half) == -log(2) assert log(2*3).func is log assert log(2*3**2).func is log def test_log_base(): assert log(1, 2) == 0 assert log(2, 2) == 1 assert log(3, 2) == log(3)/log(2) assert log(6, 2) == 1 + log(3)/log(2) assert log(6, 3) == 1 + log(2)/log(3) assert log(2**3, 2) == 3 assert log(3**3, 3) == 3 assert log(5, 1) == zoo assert log(1, 1) == nan assert log(Rational(2, 3), 10) == (-log(3) + log(2))/log(10) assert log(Rational(2, 3), Rational(1, 3)) == -log(2)/log(3) + 1 assert log(Rational(2, 3), Rational(2, 5)) == \ (-log(3) + log(2))/(-log(5) + log(2)) def test_log_symbolic(): x, y = symbols('x,y') assert log(x, exp(1)) == log(x) assert log(exp(x)) != x assert log(x, exp(1)) == log(x) assert log(x*y) != log(x) + log(y) assert log(x/y).expand() != log(x) - log(y) assert log(x/y).expand(force=True) == log(x) - log(y) assert log(x**y).expand() != y*log(x) assert log(x**y).expand(force=True) == y*log(x) assert log(x, 2) == log(x)/log(2) assert log(E, 2) == 1/log(2) p, q = symbols('p,q', positive=True) r = Symbol('r', real=True) assert log(p**2) != 2*log(p) assert log(p**2).expand() == 2*log(p) assert log(x**2).expand() != 2*log(x) assert log(p**q) != q*log(p) assert log(exp(p)) == p assert log(p*q) != log(p) + log(q) assert log(p*q).expand() == log(p) + log(q) assert log(-exp(p)) != p + I*pi assert log(-exp(x)).expand() != x + I*pi assert log(-exp(r)).expand() == r + I*pi assert log(x**y) != y*log(x) assert (log(x**-5)**-1).expand() != -1/log(x)/5 assert (log(p**-5)**-1).expand() == -1/log(p)/5 assert log(-x).func is log and log(-x).args[0] == -x assert log(-p).func is log and log(-p).args[0] == -p def test_exp_assumptions(): x = Symbol('x') r = Symbol('r', real=True) i = Symbol('i', imaginary=True) for e in exp, exp_polar: assert e(x).is_real is None assert e(x).is_imaginary is None assert e(i).is_real is None assert e(i).is_imaginary is None assert e(r).is_real is True assert e(r).is_imaginary is False assert e(re(x)).is_real is True assert e(re(x)).is_imaginary is False def test_log_assumptions(): p = symbols('p', positive=True) n = symbols('n', negative=True) assert log(2) > 0 assert log(1).is_zero assert log(2 - pi - pi*(1/pi - 1)).is_zero assert log(p).is_zero is None assert log(n).is_zero is False assert log(0.5).is_negative is True def test_log_hashing(): x = Symbol("y") assert x != log(log(x)) assert hash(x) != hash(log(log(x))) assert log(x) != log(log(log(x))) e = 1/log(log(x) + log(log(x))) assert e.base.func is log e = 1/log(log(x) + log(log(log(x)))) assert e.base.func is log x = Symbol("x") e = log(log(x)) assert e.func is log assert not x.func is log assert hash(log(log(x))) != hash(x) assert e != x def test_log_sign(): assert sign(log(2)) == 1 def test_log_expand_complex(): assert log(1 + I).expand(complex=True) == log(2)/2 + I*pi/4 assert log(1 - sqrt(2)).expand(complex=True) == log(sqrt(2) - 1) + I*pi def test_log_apply_evalf(): value = (log(3)/log(2) - 1).evalf() assert value.epsilon_eq(Float("0.58496250072115618145373")) def test_log_expand(): w = Symbol("w", positive=True) e = log(w**(log(5)/log(3))) assert e.expand() == log(5)/log(3) * log(w) x, y, z = symbols('x,y,z', positive=True) assert log(x*(y + z)).expand(mul=False) == log(x) + log(y + z) assert log(log(x**2)*log(y*z)).expand() in [log(2*log(x)*log(y) + 2*log(x)*log(z)), log(log(x)*log(z) + log(y)*log(x)) + log(2), log((log(y) + log(z))*log(x)) + log(2)] assert log(x**log(x**2)).expand(deep=False) == log(x)*log(x**2) assert log(x**log(x**2)).expand() == 2*log(x)**2 assert (log(x*(y + z))*(x + y)), expand(mul=True, log=True) == y*log( x) + y*log(y + z) + z*log(x) + z*log(y + z) x, y = symbols('x,y') assert log(x*y).expand(force=True) == log(x) + log(y) assert log(x**y).expand(force=True) == y*log(x) # there's generally no need to expand out logs since this requires # factoring and if simplification is sought, it's cheaper to put # logs together than it is to take them apart. assert log(2*3**2).expand() != 2*log(3) + log(2) def test_log_simplify(): x = Symbol("x", positive=True) assert log(x**2).expand() == 2*log(x) assert expand_log(log(x**(2 + log(2)))) == (2 + log(2))*log(x) def test_lambertw(): x = Symbol('x') assert LambertW(x) == LambertW(x) assert LambertW(0) == 0 assert LambertW(E) == 1 assert LambertW(-1/E) == -1 assert LambertW(-log(2)/2) == -log(2) assert LambertW(oo) == oo assert LambertW(x**2).diff(x) == 2*LambertW(x**2)/x/(1 + LambertW(x**2)) assert LambertW(sqrt(2)).evalf(30).epsilon_eq( Float("0.701338383413663009202120278965", 30), 1e-29) def test_exp_expand(): A, B, C = symbols('A,B,C', commutative=False) x, y, z = symbols('x,y,z') assert exp(A + B).expand() == exp(A + B) assert exp(A + B + C).expand() == exp(A + B + C) assert exp(x + y).expand() == exp(x)*exp(y) assert exp(x + y + z).expand() == exp(x)*exp(y)*exp(z) def test_as_numer_denom(): from sympy.abc import x n = symbols('n', negative=True) assert exp(x).as_numer_denom() == (exp(x), 1) assert exp(-x).as_numer_denom() == (1, exp(x)) assert exp(-2*x).as_numer_denom() == (1, exp(2*x)) assert exp(-2).as_numer_denom() == (1, exp(2)) assert exp(n).as_numer_denom() == (1, exp(-n)) assert exp(-n).as_numer_denom() == (exp(-n), 1) assert exp(-I*x).as_numer_denom() == (1, exp(I*x)) assert exp(-I*n).as_numer_denom() == (1, exp(I*n)) assert exp(-n).as_numer_denom() == (exp(-n), 1) def test_polar(): x, y = symbols('x y', polar=True) z = Symbol('z') assert abs(exp_polar(I*4)) == 1 assert exp_polar(I*10).n() == exp_polar(I*10) assert log(exp_polar(z)) == z assert log(x*y).expand() == log(x) + log(y) assert log(x**z).expand() == z*log(x) assert exp_polar(3).exp == 3 # Compare exp(1.0*pi*I). assert (exp_polar(1.0*pi*I).n(n=5)).as_real_imag()[1] >= 0 def test_log_product(): from sympy.abc import n, m i, j = symbols('i,j', positive=True, integer=True) x, y = symbols('x,y', positive=True) from sympy.concrete import Product, Sum f, g = Function('f'), Function('g') assert simplify(log(Product(x**i, (i, 1, n)))) == Sum(i*log(x), (i, 1, n)) assert simplify(log(Product(x**i*y**j, (i, 1, n), (j, 1, m)))) == \ log(Product(x**i*y**j, (i, 1, n), (j, 1, m))) expr = log(Product(-2, (n, 0, 4))) assert Eq(simplify(expr), expr) sympy-0.7.4.1/sympy/functions/elementary/tests/test_piecewise.py0000644000175000017500000004325212253362407025340 0ustar georgeskgeorgeskfrom sympy import ( adjoint, And, Basic, conjugate, diff, expand, Eq, Function, I, im, Integral, integrate, Interval, lambdify, log, Max, Min, oo, Or, pi, Piecewise, piecewise_fold, Rational, re, solve, symbols, transpose, cos, exp, Abs, Not ) from sympy.utilities.pytest import XFAIL, raises x, y = symbols('x y') z = symbols('z', nonzero=True) def test_piecewise(): # Test canonization assert Piecewise((x, x < 1), (0, True)) == Piecewise((x, x < 1), (0, True)) assert Piecewise((x, x < 1), (0, True), (1, True)) == \ Piecewise((x, x < 1), (0, True)) assert Piecewise((x, x < 1), (0, False), (-1, 1 > 2)) == \ Piecewise((x, x < 1)) assert Piecewise((x, x < 1), (0, x < 1), (0, True)) == \ Piecewise((x, x < 1), (0, True)) assert Piecewise((x, x < 1), (0, x < 2), (0, True)) == \ Piecewise((x, x < 1), (0, True)) assert Piecewise((x, x < 1), (x, x < 2), (0, True)) == \ Piecewise((x, Or(x < 1, x < 2)), (0, True)) assert Piecewise((x, x < 1), (x, x < 2), (x, True)) == x assert Piecewise((x, True)) == x raises(TypeError, lambda: Piecewise(x)) raises(TypeError, lambda: Piecewise((x, x**2))) # Test subs p = Piecewise((-1, x < -1), (x**2, x < 0), (log(x), x >= 0)) p_x2 = Piecewise((-1, x**2 < -1), (x**4, x**2 < 0), (log(x**2), x**2 >= 0)) assert p.subs(x, x**2) == p_x2 assert p.subs(x, -5) == -1 assert p.subs(x, -1) == 1 assert p.subs(x, 1) == log(1) # More subs tests p2 = Piecewise((1, x < pi), (-1, x < 2*pi), (0, x > 2*pi)) p3 = Piecewise((1, Eq(x, 0)), (1/x, True)) p4 = Piecewise((1, Eq(x, 0)), (2, 1/x>2)) assert p2.subs(x, 2) == 1 assert p2.subs(x, 4) == -1 assert p2.subs(x, 10) == 0 assert p3.subs(x, 0.0) == 1 assert p4.subs(x, 0.0) == 1 f, g, h = symbols('f,g,h', cls=Function) pf = Piecewise((f(x), x < -1), (f(x) + h(x) + 2, x <= 1)) pg = Piecewise((g(x), x < -1), (g(x) + h(x) + 2, x <= 1)) assert pg.subs(g, f) == pf assert Piecewise((1, Eq(x, 0)), (0, True)).subs(x, 0) == 1 assert Piecewise((1, Eq(x, 0)), (0, True)).subs(x, 1) == 0 assert Piecewise((1, Eq(x, y)), (0, True)).subs(x, y) == 1 assert Piecewise((1, Eq(x, z)), (0, True)).subs(x, z) == 1 assert Piecewise((1, Eq(exp(x), cos(z))), (0, True)).subs(x, z) == \ Piecewise((1, Eq(exp(z), cos(z))), (0, True)) assert Piecewise((1, Eq(x, y*(y + 1))), (0, True)).subs(x, y**2 + y) == 1 p5 = Piecewise( (0, Eq(cos(x) + y, 0)), (1, True)) assert p5.subs(y, 0) == Piecewise( (0, Eq(cos(x), 0)), (1, True)) # Test evalf assert p.evalf() == p assert p.evalf(subs={x: -2}) == -1 assert p.evalf(subs={x: -1}) == 1 assert p.evalf(subs={x: 1}) == log(1) # Test doit f_int = Piecewise((Integral(x, (x, 0, 1)), x < 1)) assert f_int.doit() == Piecewise( (1.0/2.0, x < 1) ) # Test differentiation f = x fp = x*p dp = Piecewise((0, x < -1), (2*x, x < 0), (1/x, x >= 0)) fp_dx = x*dp + p assert diff(p, x) == dp assert diff(f*p, x) == fp_dx # Test simple arithmetic assert x*p == fp assert x*p + p == p + x*p assert p + f == f + p assert p + dp == dp + p assert p - dp == -(dp - p) # Test power dp2 = Piecewise((0, x < -1), (4*x**2, x < 0), (1/x**2, x >= 0)) assert dp**2 == dp2 # Test _eval_interval f1 = x*y + 2 f2 = x*y**2 + 3 peval = Piecewise( (f1, x < 0), (f2, x > 0)) peval_interval = f1.subs( x, 0) - f1.subs(x, -1) + f2.subs(x, 1) - f2.subs(x, 0) assert peval._eval_interval(x, 0, 0) == 0 assert peval._eval_interval(x, -1, 1) == peval_interval peval2 = Piecewise((f1, x < 0), (f2, True)) assert peval2._eval_interval(x, 0, 0) == 0 assert peval2._eval_interval(x, 1, -1) == -peval_interval assert peval2._eval_interval(x, -1, -2) == f1.subs(x, -2) - f1.subs(x, -1) assert peval2._eval_interval(x, -1, 1) == peval_interval assert peval2._eval_interval(x, None, 0) == peval2.subs(x, 0) assert peval2._eval_interval(x, -1, None) == -peval2.subs(x, -1) # Test integration p_int = Piecewise((-x, x < -1), (x**3/3.0, x < 0), (-x + x*log(x), x >= 0)) assert integrate(p, x) == p_int p = Piecewise((x, x < 1), (x**2, -1 <= x), (x, 3 < x)) assert integrate(p, (x, -2, 2)) == 5.0/6.0 assert integrate(p, (x, 2, -2)) == -5.0/6.0 p = Piecewise((0, x < 0), (1, x < 1), (0, x < 2), (1, x < 3), (0, True)) assert integrate(p, (x, -oo, oo)) == 2 p = Piecewise((x, x < -10), (x**2, x <= -1), (x, 1 < x)) raises(ValueError, lambda: integrate(p, (x, -2, 2))) # Test commutativity assert p.is_commutative is True def test_piecewise_free_symbols(): a = symbols('a') f = Piecewise((x, a < 0), (y, True)) assert f.free_symbols == set([x, y, a]) def test_piecewise_integrate(): # XXX Use '<=' here! '>=' is not yet implemented .. f = Piecewise(((x - 2)**2, 0 <= x), (1, True)) assert integrate(f, (x, -2, 2)) == Rational(14, 3) g = Piecewise(((x - 5)**5, 4 <= x), (f, True)) assert integrate(g, (x, -2, 2)) == Rational(14, 3) assert integrate(g, (x, -2, 5)) == Rational(43, 6) g = Piecewise(((x - 5)**5, 4 <= x), (f, x < 4)) assert integrate(g, (x, -2, 2)) == Rational(14, 3) assert integrate(g, (x, -2, 5)) == Rational(43, 6) g = Piecewise(((x - 5)**5, 2 <= x), (f, x < 2)) assert integrate(g, (x, -2, 2)) == Rational(14, 3) assert integrate(g, (x, -2, 5)) == -Rational(701, 6) g = Piecewise(((x - 5)**5, 2 <= x), (f, True)) assert integrate(g, (x, -2, 2)) == Rational(14, 3) assert integrate(g, (x, -2, 5)) == -Rational(701, 6) g = Piecewise(((x - 5)**5, 2 <= x), (2 * f, True)) assert integrate(g, (x, -2, 2)) == 2 * Rational(14, 3) assert integrate(g, (x, -2, 5)) == -Rational(673, 6) g = Piecewise((1, x > 0), (0, Eq(x, 0)), (-1, x < 0)) assert integrate(g, (x, -1, 1)) == 0 g = Piecewise((1, x - y < 0), (0, True)) assert integrate(g, (y, -oo, 0)) == -Min(0, x) assert integrate(g, (y, 0, oo)) == oo - Max(0, x) assert integrate(g, (y, -oo, oo)) == oo - x g = Piecewise((0, x < 0), (x, x <= 1), (1, True)) assert integrate(g, (x, -5, 1)) == Rational(1, 2) assert integrate(g, (x, -5, y)).subs(y, 1) == Rational(1, 2) assert integrate(g, (x, y, 1)).subs(y, -5) == Rational(1, 2) assert integrate(g, (x, 1, -5)) == -Rational(1, 2) assert integrate(g, (x, 1, y)).subs(y, -5) == -Rational(1, 2) assert integrate(g, (x, y, -5)).subs(y, 1) == -Rational(1, 2) assert integrate(g, (x, -5, y)) == Piecewise((0, y < 0), (y**2/2, y <= 1), (y - 0.5, True)) assert integrate(g, (x, y, 1)) == Piecewise((0.5, y < 0), (0.5 - y**2/2, y <= 1), (1 - y, True)) g = Piecewise((1 - x, Interval(0, 1).contains(x)), (1 + x, Interval(-1, 0).contains(x)), (0, True)) assert integrate(g, (x, -5, 1)) == 1 assert integrate(g, (x, -5, y)).subs(y, 1) == 1 assert integrate(g, (x, y, 1)).subs(y, -5) == 1 assert integrate(g, (x, 1, -5)) == -1 assert integrate(g, (x, 1, y)).subs(y, -5) == -1 assert integrate(g, (x, y, -5)).subs(y, 1) == -1 assert integrate(g, (x, -5, y)) == Piecewise( (-y**2/2 + y + 0.5, Interval(0, 1).contains(y)), (y**2/2 + y + 0.5, Interval(-1, 0).contains(y)), (0, y <= -1), (1, True)) assert integrate(g, (x, y, 1)) == Piecewise( (y**2/2 - y + 0.5, Interval(0, 1).contains(y)), (-y**2/2 - y + 0.5, Interval(-1, 0).contains(y)), (1, y <= -1), (0, True)) g = Piecewise((0, Or(x <= -1, x >= 1)), (1 - x, x > 0), (1 + x, True)) assert integrate(g, (x, -5, 1)) == 1 assert integrate(g, (x, -5, y)).subs(y, 1) == 1 assert integrate(g, (x, y, 1)).subs(y, -5) == 1 assert integrate(g, (x, 1, -5)) == -1 assert integrate(g, (x, 1, y)).subs(y, -5) == -1 assert integrate(g, (x, y, -5)).subs(y, 1) == -1 assert integrate(g, (x, -5, y)) == Piecewise((0, y <= -1), (1, y >= 1), (-y**2/2 + y + 0.5, y > 0), (y**2/2 + y + 0.5, True)) assert integrate(g, (x, y, 1)) == Piecewise((1, y <= -1), (0, y >= 1), (y**2/2 - y + 0.5, y > 0), (-y**2/2 - y + 0.5, True)) def test_piecewise_integrate_inequality_conditions(): c1, c2 = symbols("c1 c2", positive=True) g = Piecewise((0, c1*x > 1), (1, c1*x > 0), (0, True)) assert integrate(g, (x, -oo, 0)) == 0 assert integrate(g, (x, -5, 0)) == 0 assert integrate(g, (x, 0, 5)) == Min(5, 1/c1) assert integrate(g, (x, 0, oo)) == 1/c1 g = Piecewise((0, c1*x + c2*y > 1), (1, c1*x + c2*y > 0), (0, True)) assert integrate(g, (x, -oo, 0)).subs(y, 0) == 0 assert integrate(g, (x, -5, 0)).subs(y, 0) == 0 assert integrate(g, (x, 0, 5)).subs(y, 0) == Min(5, 1/c1) assert integrate(g, (x, 0, oo)).subs(y, 0) == 1/c1 def test_piecewise_integrate_symbolic_conditions(): from sympy.abc import a, b, x, y p0 = Piecewise((0, Or(x < a, x > b)), (1, True)) p1 = Piecewise((0, x < a), (0, x > b), (1, True)) p2 = Piecewise((0, x > b), (0, x < a), (1, True)) p3 = Piecewise((0, x < a), (1, x < b), (0, True)) p4 = Piecewise((0, x > b), (1, x > a), (0, True)) p5 = Piecewise((1, And(a < x, x < b)), (0, True)) assert integrate(p0, (x, -oo, y)) == Min(b, y) - Min(a, b, y) assert integrate(p1, (x, -oo, y)) == Min(b, y) - Min(a, b, y) assert integrate(p2, (x, -oo, y)) == Min(b, y) - Min(a, b, y) assert integrate(p3, (x, -oo, y)) == Min(b, y) - Min(a, b, y) assert integrate(p4, (x, -oo, y)) == Min(b, y) - Min(a, b, y) assert integrate(p5, (x, -oo, y)) == Min(b, y) - Min(a, b, y) assert integrate(p0, (x, y, oo)) == Max(a, b, y) - Max(a, y) assert integrate(p1, (x, y, oo)) == Max(a, b, y) - Max(a, y) assert integrate(p2, (x, y, oo)) == Max(a, b, y) - Max(a, y) assert integrate(p3, (x, y, oo)) == Max(a, b, y) - Max(a, y) assert integrate(p4, (x, y, oo)) == Max(a, b, y) - Max(a, y) assert integrate(p5, (x, y, oo)) == Max(a, b, y) - Max(a, y) assert integrate(p0, x) == Piecewise((0, Or(x < a, x > b)), (x, True)) assert integrate(p1, x) == Piecewise((0, Or(x < a, x > b)), (x, True)) assert integrate(p2, x) == Piecewise((0, Or(x < a, x > b)), (x, True)) p1 = Piecewise((0, x < a), (0.5, x > b), (1, True)) p2 = Piecewise((0.5, x > b), (0, x < a), (1, True)) p3 = Piecewise((0, x < a), (1, x < b), (0.5, True)) p4 = Piecewise((0.5, x > b), (1, x > a), (0, True)) p5 = Piecewise((1, And(a < x, x < b)), (0.5, x > b), (0, True)) assert integrate(p1, (x, -oo, y)) == 0.5*y + 0.5*Min(b, y) - Min(a, b, y) assert integrate(p2, (x, -oo, y)) == 0.5*y + 0.5*Min(b, y) - Min(a, b, y) assert integrate(p3, (x, -oo, y)) == 0.5*y + 0.5*Min(b, y) - Min(a, b, y) assert integrate(p4, (x, -oo, y)) == 0.5*y + 0.5*Min(b, y) - Min(a, b, y) assert integrate(p5, (x, -oo, y)) == 0.5*y + 0.5*Min(b, y) - Min(a, b, y) def test_piecewise_integrate_independent_conditions(): p = Piecewise((0, Eq(y, 0)), (x*y, True)) assert integrate(p, (x, 1, 3)) == \ Piecewise((0, Eq(y, 0)), (4*y, True)) def test_piecewise_simplify(): p = Piecewise(((x**2 + 1)/x**2, Eq(x*(1 + x) - x**2, 0)), ((-1)**x*(-1), True)) assert p.simplify() == \ Piecewise((1 + 1/x**2, Eq(x, 0)), ((-1)**(x + 1), True)) def test_piecewise_solve(): abs2 = Piecewise((-x, x <= 0), (x, x > 0)) f = abs2.subs(x, x - 2) assert solve(f, x) == [2] assert solve(f - 1, x) == [1, 3] f = Piecewise(((x - 2)**2, x >= 0), (1, True)) assert solve(f, x) == [2] g = Piecewise(((x - 5)**5, x >= 4), (f, True)) assert solve(g, x) == [2, 5] g = Piecewise(((x - 5)**5, x >= 4), (f, x < 4)) assert solve(g, x) == [2, 5] g = Piecewise(((x - 5)**5, x >= 2), (f, x < 2)) assert solve(g, x) == [5] g = Piecewise(((x - 5)**5, x >= 2), (f, True)) assert solve(g, x) == [5] g = Piecewise(((x - 5)**5, x >= 2), (f, True), (10, False)) assert solve(g, x) == [5] g = Piecewise(((x - 5)**5, x >= 2), (-x + 2, x - 2 <= 0), (x - 2, x - 2 > 0)) assert solve(g, x) == [5] # See issue 1253 (enhance the solver to handle inequalities). @XFAIL def test_piecewise_solve2(): f = Piecewise(((x - 2)**2, x >= 0), (0, True)) assert solve(f, x) == [2, Interval(0, oo, True, True)] def test_piecewise_fold(): p = Piecewise((x, x < 1), (1, 1 <= x)) assert piecewise_fold(x*p) == Piecewise((x**2, x < 1), (x, 1 <= x)) assert piecewise_fold(p + p) == Piecewise((2*x, x < 1), (2, 1 <= x)) assert piecewise_fold(Piecewise((1, x < 0), (2, True)) + Piecewise((10, x < 0), (-10, True))) == \ Piecewise((11, x < 0), (-8, True)) p1 = Piecewise((0, x < 0), (x, x <= 1), (0, True)) p2 = Piecewise((0, x < 0), (1 - x, x <= 1), (0, True)) p = 4*p1 + 2*p2 assert integrate( piecewise_fold(p), (x, -oo, oo)) == integrate(2*x + 2, (x, 0, 1)) def test_piecewise_fold_piecewise_in_cond(): p1 = Piecewise((cos(x), x < 0), (0, True)) p2 = Piecewise((0, Eq(p1, 0)), (p1 / Abs(p1), True)) p3 = piecewise_fold(p2) assert(p2.subs(x, -pi/2) == 0.0) assert(p2.subs(x, 1) == 0.0) assert(p2.subs(x, -pi/4) == 1.0) p4 = Piecewise((0, Eq(p1, 0)), (1,True)) assert(piecewise_fold(p4) == Piecewise( (0, Or(And(Eq(cos(x), 0), x < 0), Not(x < 0))), (1, True))) r1 = 1 < Piecewise((1, x < 1), (3, True)) assert(piecewise_fold(r1) == Not(x < 1)) p5 = Piecewise((1, x < 0), (3, True)) p6 = Piecewise((1, x < 1), (3, True)) p7 = piecewise_fold(Piecewise((1, p5 < p6), (0, True))) assert(Piecewise((1, And(Not(x < 1), x < 0)), (0, True))) @XFAIL def test_piecewise_fold_piecewise_in_cond_2(): p1 = Piecewise((cos(x), x < 0), (0, True)) p2 = Piecewise((0, Eq(p1, 0)), (1 / p1, True)) p3 = Piecewise((0, Or(And(Eq(cos(x), 0), x < 0), Not(x < 0))), (1 / cos(x), True)) assert(piecewise_fold(p2) == p3) def test_piecewise_fold_expand(): p1 = Piecewise((1, Interval(0, 1, False, True).contains(x)), (0, True)) p2 = piecewise_fold(expand((1 - x)*p1)) assert p2 == Piecewise((1 - x, Interval(0, 1, False, True).contains(x)), (Piecewise((-x, Interval(0, 1, False, True).contains(x)), (0, True)), True)) p2 = expand(piecewise_fold((1 - x)*p1)) assert p2 == Piecewise( (1 - x, Interval(0, 1, False, True).contains(x)), (0, True)) def test_piecewise_duplicate(): p = Piecewise((x, x < -10), (x**2, x <= -1), (x, 1 < x)) assert p == Piecewise(*p.args) def test_doit(): p1 = Piecewise((x, x < 1), (x**2, -1 <= x), (x, 3 < x)) p2 = Piecewise((x, x < 1), (Integral(2 * x), -1 <= x), (x, 3 < x)) assert p2.doit() == p1 assert p2.doit(deep=False) == p2 def test_piecewise_interval(): p1 = Piecewise((x, Interval(0, 1).contains(x)), (0, True)) assert p1.subs(x, -0.5) == 0 assert p1.subs(x, 0.5) == 0.5 assert p1.diff(x) == Piecewise((1, Interval(0, 1).contains(x)), (0, True)) assert integrate( p1, x) == Piecewise((x**2/2, Interval(0, 1).contains(x)), (0, True)) def test_piecewise_collapse(): p1 = Piecewise((x, x < 0), (x**2, x > 1)) p2 = Piecewise((p1, x < 0), (p1, x > 1)) assert p2 == Piecewise((x, x < 0), (x**2, 1 < x)) p1 = Piecewise((Piecewise((x, x < 0), (1, True)), True)) assert p1 == Piecewise((Piecewise((x, x < 0), (1, True)), True)) def test_piecewise_lambdify(): p = Piecewise( (x**2, x < 0), (x, Interval(0, 1, False, True).contains(x)), (2 - x, x >= 1), (0, True) ) f = lambdify(x, p) assert f(-2.0) == 4.0 assert f(0.0) == 0.0 assert f(0.5) == 0.5 assert f(2.0) == 0.0 def test_piecewise_series(): from sympy import sin, cos, O p1 = Piecewise((sin(x), x < 0), (cos(x), x > 0)) p2 = Piecewise((x + O(x**2), x < 0), (1 + O(x**2), x > 0)) assert p1.nseries(x, n=2) == p2 def test_piecewise_as_leading_term(): p1 = Piecewise((1/x, x > 1), (0, True)) p2 = Piecewise((x, x > 1), (0, True)) p3 = Piecewise((1/x, x > 1), (x, True)) p4 = Piecewise((x, x > 1), (1/x, True)) p5 = Piecewise((1/x, x > 1), (x, True)) p6 = Piecewise((1/x, x < 1), (x, True)) p7 = Piecewise((x, x < 1), (1/x, True)) p8 = Piecewise((x, x > 1), (1/x, True)) assert p1.as_leading_term(x) == 0 assert p2.as_leading_term(x) == 0 assert p3.as_leading_term(x) == x assert p4.as_leading_term(x) == 1/x assert p5.as_leading_term(x) == x assert p6.as_leading_term(x) == 1/x assert p7.as_leading_term(x) == x assert p8.as_leading_term(x) == 1/x def test_piecewise_complex(): p1 = Piecewise((2, x < 0), (1, 0 <= x)) p2 = Piecewise((2*I, x < 0), (I, 0 <= x)) p3 = Piecewise((I*x, x > 1), (1 + I, True)) p4 = Piecewise((-I*conjugate(x), x > 1), (1 - I, True)) assert conjugate(p1) == p1 assert conjugate(p2) == piecewise_fold(-p2) assert conjugate(p3) == p4 assert p1.is_imaginary is False assert p1.is_real is True assert p2.is_imaginary is True assert p2.is_real is False assert p3.is_imaginary is None assert p3.is_real is None assert p1.as_real_imag() == (p1, 0) assert p2.as_real_imag() == (0, -I*p2) def test_conjugate_transpose(): A, B = symbols("A B", commutative=False) p = Piecewise((A*B**2, x > 0), (A**2*B, True)) assert p.adjoint() == \ Piecewise((adjoint(A*B**2), x > 0), (adjoint(A**2*B), True)) assert p.conjugate() == \ Piecewise((conjugate(A*B**2), x > 0), (conjugate(A**2*B), True)) assert p.transpose() == \ Piecewise((transpose(A*B**2), x > 0), (transpose(A**2*B), True)) def test_piecewise_evaluate(): assert Piecewise((x, True)) == x assert Piecewise((x, True), evaluate=True) == x p = Piecewise((x, True), evaluate=False) assert p != x assert p.is_Piecewise assert all(isinstance(i, Basic) for i in p.args) sympy-0.7.4.1/sympy/functions/elementary/tests/test_trigonometric.py0000644000175000017500000010554012253362407026247 0ustar georgeskgeorgeskfrom sympy import (symbols, Symbol, nan, oo, zoo, I, sinh, sin, pi, atan, acos, Rational, sqrt, asin, acot, coth, E, S, tan, tanh, cos, cosh, atan2, exp, log, asinh, acoth, atanh, O, cancel, Matrix, re, im, Float, Pow, gcd, sec, csc, cot, diff, simplify, Heaviside, arg, conjugate, series) from sympy.utilities.pytest import XFAIL, slow, raises from sympy.core.compatibility import xrange x, y, z = symbols('x y z') r = Symbol('r', real=True) k = Symbol('k', integer=True) def test_sin(): x, y = symbols('x y') assert sin(nan) == nan assert sin(oo*I) == oo*I assert sin(-oo*I) == -oo*I assert sin(oo).args[0] == oo assert sin(0) == 0 assert sin(asin(x)) == x assert sin(atan(x)) == x / sqrt(1 + x**2) assert sin(acos(x)) == sqrt(1 - x**2) assert sin(acot(x)) == 1 / (sqrt(1 + 1 / x**2) * x) assert sin(atan2(y, x)) == y / sqrt(x**2 + y**2) assert sin(pi*I) == sinh(pi)*I assert sin(-pi*I) == -sinh(pi)*I assert sin(-2*I) == -sinh(2)*I assert sin(pi) == 0 assert sin(-pi) == 0 assert sin(2*pi) == 0 assert sin(-2*pi) == 0 assert sin(-3*10**73*pi) == 0 assert sin(7*10**103*pi) == 0 assert sin(pi/2) == 1 assert sin(-pi/2) == -1 assert sin(5*pi/2) == 1 assert sin(7*pi/2) == -1 n = symbols('n', integer=True) assert sin(pi*n/2) == (-1)**(n/2 - S.Half) assert sin(pi/3) == S.Half*sqrt(3) assert sin(-2*pi/3) == -S.Half*sqrt(3) assert sin(pi/4) == S.Half*sqrt(2) assert sin(-pi/4) == -S.Half*sqrt(2) assert sin(17*pi/4) == S.Half*sqrt(2) assert sin(-3*pi/4) == -S.Half*sqrt(2) assert sin(pi/6) == S.Half assert sin(-pi/6) == -S.Half assert sin(7*pi/6) == -S.Half assert sin(-5*pi/6) == -S.Half assert sin(1*pi/5) == sqrt((5 - sqrt(5)) / 8) assert sin(2*pi/5) == sqrt((5 + sqrt(5)) / 8) assert sin(3*pi/5) == sin(2*pi/5) assert sin(4*pi/5) == sin(1*pi/5) assert sin(6*pi/5) == -sin(1*pi/5) assert sin(8*pi/5) == -sin(2*pi/5) assert sin(-1273*pi/5) == -sin(2*pi/5) assert sin(pi/8) == sqrt((2 - sqrt(2))/4) assert sin(104*pi/105) == sin(pi/105) assert sin(106*pi/105) == -sin(pi/105) assert sin(-104*pi/105) == -sin(pi/105) assert sin(-106*pi/105) == sin(pi/105) assert sin(x*I) == sinh(x)*I assert sin(k*pi) == 0 assert sin(17*k*pi) == 0 assert sin(k*pi*I) == sinh(k*pi)*I assert sin(r).is_real is True assert isinstance(sin( re(x) - im(y)), sin) is True assert isinstance(sin(-re(x) + im(y)), sin) is False for d in list(range(1, 22)) + [60, 85]: for n in xrange(0, d*2 + 1): x = n*pi/d e = abs( float(sin(x)) - sin(float(x)) ) assert e < 1e-12 def test_sin_cos(): for d in [1, 2, 3, 4, 5, 6, 10, 12]: # list is not exhaustive... for n in xrange(-2*d, d*2): x = n*pi/d assert sin(x + pi/2) == cos(x), "fails for %d*pi/%d" % (n, d) assert sin(x - pi/2) == -cos(x), "fails for %d*pi/%d" % (n, d) assert sin(x) == cos(x - pi/2), "fails for %d*pi/%d" % (n, d) assert -sin(x) == cos(x + pi/2), "fails for %d*pi/%d" % (n, d) def test_sin_series(): assert sin(x).series(x, 0, 9) == \ x - x**3/6 + x**5/120 - x**7/5040 + O(x**9) def test_sin_rewrite(): assert sin(x).rewrite(exp) == -I*(exp(I*x) - exp(-I*x))/2 assert sin(x).rewrite(tan) == 2*tan(x/2)/(1 + tan(x/2)**2) assert sin(x).rewrite(cot) == 2*cot(x/2)/(1 + cot(x/2)**2) assert sin(sinh(x)).rewrite( exp).subs(x, 3).n() == sin(x).rewrite(exp).subs(x, sinh(3)).n() assert sin(cosh(x)).rewrite( exp).subs(x, 3).n() == sin(x).rewrite(exp).subs(x, cosh(3)).n() assert sin(tanh(x)).rewrite( exp).subs(x, 3).n() == sin(x).rewrite(exp).subs(x, tanh(3)).n() assert sin(coth(x)).rewrite( exp).subs(x, 3).n() == sin(x).rewrite(exp).subs(x, coth(3)).n() assert sin(sin(x)).rewrite( exp).subs(x, 3).n() == sin(x).rewrite(exp).subs(x, sin(3)).n() assert sin(cos(x)).rewrite( exp).subs(x, 3).n() == sin(x).rewrite(exp).subs(x, cos(3)).n() assert sin(tan(x)).rewrite( exp).subs(x, 3).n() == sin(x).rewrite(exp).subs(x, tan(3)).n() assert sin(cot(x)).rewrite( exp).subs(x, 3).n() == sin(x).rewrite(exp).subs(x, cot(3)).n() assert sin(log(x)).rewrite(Pow) == I*x**-I / 2 - I*x**I /2 def test_sin_expansion(): # Note: these formulas are not unique. The ones here come from the # Chebyshev formulas. assert sin(x + y).expand(trig=True) == sin(x)*cos(y) + cos(x)*sin(y) assert sin(x - y).expand(trig=True) == sin(x)*cos(y) - cos(x)*sin(y) assert sin(y - x).expand(trig=True) == cos(x)*sin(y) - sin(x)*cos(y) assert sin(2*x).expand(trig=True) == 2*sin(x)*cos(x) assert sin(3*x).expand(trig=True) == -4*sin(x)**3 + 3*sin(x) assert sin(4*x).expand(trig=True) == -8*sin(x)**3*cos(x) + 4*sin(x)*cos(x) assert sin(2).expand(trig=True) == 2*sin(1)*cos(1) assert sin(3).expand(trig=True) == -4*sin(1)**3 + 3*sin(1) def test_trig_symmetry(): assert sin(-x) == -sin(x) assert cos(-x) == cos(x) assert tan(-x) == -tan(x) assert cot(-x) == -cot(x) assert sin(x + pi) == -sin(x) assert sin(x + 2*pi) == sin(x) assert sin(x + 3*pi) == -sin(x) assert sin(x + 4*pi) == sin(x) assert sin(x - 5*pi) == -sin(x) assert cos(x + pi) == -cos(x) assert cos(x + 2*pi) == cos(x) assert cos(x + 3*pi) == -cos(x) assert cos(x + 4*pi) == cos(x) assert cos(x - 5*pi) == -cos(x) assert tan(x + pi) == tan(x) assert tan(x - 3*pi) == tan(x) assert cot(x + pi) == cot(x) assert cot(x - 3*pi) == cot(x) assert sin(pi/2 - x) == cos(x) assert sin(3*pi/2 - x) == -cos(x) assert sin(5*pi/2 - x) == cos(x) assert cos(pi/2 - x) == sin(x) assert cos(3*pi/2 - x) == -sin(x) assert cos(5*pi/2 - x) == sin(x) assert tan(pi/2 - x) == cot(x) assert tan(3*pi/2 - x) == cot(x) assert tan(5*pi/2 - x) == cot(x) assert cot(pi/2 - x) == tan(x) assert cot(3*pi/2 - x) == tan(x) assert cot(5*pi/2 - x) == tan(x) assert sin(pi/2 + x) == cos(x) assert cos(pi/2 + x) == -sin(x) assert tan(pi/2 + x) == -cot(x) assert cot(pi/2 + x) == -tan(x) def test_cos(): x, y = symbols('x y') assert cos(nan) == nan assert cos(oo*I) == oo assert cos(-oo*I) == oo assert cos(0) == 1 assert cos(acos(x)) == x assert cos(atan(x)) == 1 / sqrt(1 + x**2) assert cos(asin(x)) == sqrt(1 - x**2) assert cos(acot(x)) == 1 / sqrt(1 + 1 / x**2) assert cos(atan2(y, x)) == x / sqrt(x**2 + y**2) assert cos(pi*I) == cosh(pi) assert cos(-pi*I) == cosh(pi) assert cos(-2*I) == cosh(2) assert cos(pi/2) == 0 assert cos(-pi/2) == 0 assert cos(pi/2) == 0 assert cos(-pi/2) == 0 assert cos((-3*10**73 + 1)*pi/2) == 0 assert cos((7*10**103 + 1)*pi/2) == 0 n = symbols('n', integer=True) assert cos(pi*n/2) == 0 assert cos(pi) == -1 assert cos(-pi) == -1 assert cos(2*pi) == 1 assert cos(5*pi) == -1 assert cos(8*pi) == 1 assert cos(pi/3) == S.Half assert cos(-2*pi/3) == -S.Half assert cos(pi/4) == S.Half*sqrt(2) assert cos(-pi/4) == S.Half*sqrt(2) assert cos(11*pi/4) == -S.Half*sqrt(2) assert cos(-3*pi/4) == -S.Half*sqrt(2) assert cos(pi/6) == S.Half*sqrt(3) assert cos(-pi/6) == S.Half*sqrt(3) assert cos(7*pi/6) == -S.Half*sqrt(3) assert cos(-5*pi/6) == -S.Half*sqrt(3) assert cos(1*pi/5) == (sqrt(5) + 1)/4 assert cos(2*pi/5) == (sqrt(5) - 1)/4 assert cos(3*pi/5) == -cos(2*pi/5) assert cos(4*pi/5) == -cos(1*pi/5) assert cos(6*pi/5) == -cos(1*pi/5) assert cos(8*pi/5) == cos(2*pi/5) assert cos(-1273*pi/5) == -cos(2*pi/5) assert cos(pi/8) == sqrt((2 + sqrt(2))/4) assert cos(104*pi/105) == -cos(pi/105) assert cos(106*pi/105) == -cos(pi/105) assert cos(-104*pi/105) == -cos(pi/105) assert cos(-106*pi/105) == -cos(pi/105) assert cos(x*I) == cosh(x) assert cos(k*pi*I) == cosh(k*pi) assert cos(r).is_real is True assert cos(k*pi) == (-1)**k assert cos(2*k*pi) == 1 for d in list(range(1, 22)) + [60, 85]: for n in xrange(0, 2*d + 1): x = n*pi/d e = abs( float(cos(x)) - cos(float(x)) ) assert e < 1e-12 def test_issue_3091(): c = Float('123456789012345678901234567890.25', '') for cls in [sin, cos, tan, cot]: assert cls(c*pi) == cls(pi/4) assert cls(4.125*pi) == cls(pi/8) assert cls(4.7*pi) == cls((4.7 % 2)*pi) def test_cos_series(): assert cos(x).series(x, 0, 9) == \ 1 - x**2/2 + x**4/24 - x**6/720 + x**8/40320 + O(x**9) def test_cos_rewrite(): assert cos(x).rewrite(exp) == exp(I*x)/2 + exp(-I*x)/2 assert cos(x).rewrite(tan) == (1 - tan(x/2)**2)/(1 + tan(x/2)**2) assert cos(x).rewrite(cot) == -(1 - cot(x/2)**2)/(1 + cot(x/2)**2) assert cos(sinh(x)).rewrite( exp).subs(x, 3).n() == cos(x).rewrite(exp).subs(x, sinh(3)).n() assert cos(cosh(x)).rewrite( exp).subs(x, 3).n() == cos(x).rewrite(exp).subs(x, cosh(3)).n() assert cos(tanh(x)).rewrite( exp).subs(x, 3).n() == cos(x).rewrite(exp).subs(x, tanh(3)).n() assert cos(coth(x)).rewrite( exp).subs(x, 3).n() == cos(x).rewrite(exp).subs(x, coth(3)).n() assert cos(sin(x)).rewrite( exp).subs(x, 3).n() == cos(x).rewrite(exp).subs(x, sin(3)).n() assert cos(cos(x)).rewrite( exp).subs(x, 3).n() == cos(x).rewrite(exp).subs(x, cos(3)).n() assert cos(tan(x)).rewrite( exp).subs(x, 3).n() == cos(x).rewrite(exp).subs(x, tan(3)).n() assert cos(cot(x)).rewrite( exp).subs(x, 3).n() == cos(x).rewrite(exp).subs(x, cot(3)).n() assert cos(log(x)).rewrite(Pow) == x**I/2 + x**-I/2 def test_cos_expansion(): assert cos(x + y).expand(trig=True) == cos(x)*cos(y) - sin(x)*sin(y) assert cos(x - y).expand(trig=True) == cos(x)*cos(y) + sin(x)*sin(y) assert cos(y - x).expand(trig=True) == cos(x)*cos(y) + sin(x)*sin(y) assert cos(2*x).expand(trig=True) == 2*cos(x)**2 - 1 assert cos(3*x).expand(trig=True) == 4*cos(x)**3 - 3*cos(x) assert cos(4*x).expand(trig=True) == 8*cos(x)**4 - 8*cos(x)**2 + 1 assert cos(2).expand(trig=True) == 2*cos(1)**2 - 1 assert cos(3).expand(trig=True) == 4*cos(1)**3 - 3*cos(1) def test_tan(): assert tan(nan) == nan assert tan(oo*I) == I assert tan(-oo*I) == -I assert tan(0) == 0 assert tan(atan(x)) == x assert tan(asin(x)) == x / sqrt(1 - x**2) assert tan(acos(x)) == sqrt(1 - x**2) / x assert tan(acot(x)) == 1 / x assert tan(atan2(y, x)) == y/x assert tan(pi*I) == tanh(pi)*I assert tan(-pi*I) == -tanh(pi)*I assert tan(-2*I) == -tanh(2)*I assert tan(pi) == 0 assert tan(-pi) == 0 assert tan(2*pi) == 0 assert tan(-2*pi) == 0 assert tan(-3*10**73*pi) == 0 assert tan(pi/2) == zoo assert tan(3*pi/2) == zoo assert tan(pi/3) == sqrt(3) assert tan(-2*pi/3) == sqrt(3) assert tan(pi/4) == S.One assert tan(-pi/4) == -S.One assert tan(17*pi/4) == S.One assert tan(-3*pi/4) == S.One assert tan(pi/6) == 1/sqrt(3) assert tan(-pi/6) == -1/sqrt(3) assert tan(7*pi/6) == 1/sqrt(3) assert tan(-5*pi/6) == 1/sqrt(3) assert tan(x*I) == tanh(x)*I assert tan(k*pi) == 0 assert tan(17*k*pi) == 0 assert tan(k*pi*I) == tanh(k*pi)*I assert tan(r).is_real is True assert tan(10*pi/7) == tan(3*pi/7) assert tan(11*pi/7) == -tan(3*pi/7) assert tan(-11*pi/7) == tan(3*pi/7) def test_tan_series(): assert tan(x).series(x, 0, 9) == \ x + x**3/3 + 2*x**5/15 + 17*x**7/315 + O(x**9) def test_tan_rewrite(): neg_exp, pos_exp = exp(-x*I), exp(x*I) assert tan(x).rewrite(exp) == I*(neg_exp - pos_exp)/(neg_exp + pos_exp) assert tan(x).rewrite(sin) == 2*sin(x)**2/sin(2*x) assert tan(x).rewrite(cos) == -cos(x + S.Pi/2)/cos(x) assert tan(x).rewrite(cot) == 1/cot(x) assert tan(sinh(x)).rewrite( exp).subs(x, 3).n() == tan(x).rewrite(exp).subs(x, sinh(3)).n() assert tan(cosh(x)).rewrite( exp).subs(x, 3).n() == tan(x).rewrite(exp).subs(x, cosh(3)).n() assert tan(tanh(x)).rewrite( exp).subs(x, 3).n() == tan(x).rewrite(exp).subs(x, tanh(3)).n() assert tan(coth(x)).rewrite( exp).subs(x, 3).n() == tan(x).rewrite(exp).subs(x, coth(3)).n() assert tan(sin(x)).rewrite( exp).subs(x, 3).n() == tan(x).rewrite(exp).subs(x, sin(3)).n() assert tan(cos(x)).rewrite( exp).subs(x, 3).n() == tan(x).rewrite(exp).subs(x, cos(3)).n() assert tan(tan(x)).rewrite( exp).subs(x, 3).n() == tan(x).rewrite(exp).subs(x, tan(3)).n() assert tan(cot(x)).rewrite( exp).subs(x, 3).n() == tan(x).rewrite(exp).subs(x, cot(3)).n() assert tan(log(x)).rewrite(Pow) == I*(x**-I - x**I)/(x**-I + x**I) assert 0 == (cos(pi/15)*tan(pi/15) - sin(pi/15)).rewrite(pow) assert tan(pi/19).rewrite(pow) == tan(pi/19) assert tan(8*pi/19).rewrite(sqrt) == tan(8*pi/19) def test_tan_subs(): assert tan(x).subs(tan(x), y) == y assert tan(x).subs(x, y) == tan(y) assert tan(x).subs(x, S.Pi/2) == zoo assert tan(x).subs(x, 3*S.Pi/2) == zoo def test_tan_expansion(): assert tan(x + y).expand(trig=True) == ((tan(x) + tan(y))/(1 - tan(x)*tan(y))).expand() assert tan(x - y).expand(trig=True) == ((tan(x) - tan(y))/(1 + tan(x)*tan(y))).expand() assert tan(x + y + z).expand(trig=True) == ( (tan(x) + tan(y) + tan(z) - tan(x)*tan(y)*tan(z))/ (1 - tan(x)*tan(y) - tan(x)*tan(z) - tan(y)*tan(z))).expand() assert 0 == tan(2*x).expand(trig=True).rewrite(tan).subs([(tan(x), Rational(1, 7))])*24 - 7 assert 0 == tan(3*x).expand(trig=True).rewrite(tan).subs([(tan(x), Rational(1, 5))])*55 - 37 assert 0 == tan(4*x - pi/4).expand(trig=True).rewrite(tan).subs([(tan(x), Rational(1, 5))])*239 - 1 def test_cot(): assert cot(nan) == nan assert cot(oo*I) == -I assert cot(-oo*I) == I assert cot(0) == zoo assert cot(2*pi) == zoo assert cot(acot(x)) == x assert cot(atan(x)) == 1 / x assert cot(asin(x)) == sqrt(1 - x**2) / x assert cot(acos(x)) == x / sqrt(1 - x**2) assert cot(atan2(y, x)) == x/y assert cot(pi*I) == -coth(pi)*I assert cot(-pi*I) == coth(pi)*I assert cot(-2*I) == coth(2)*I assert cot(pi) == cot(2*pi) == cot(3*pi) assert cot(-pi) == cot(-2*pi) == cot(-3*pi) assert cot(pi/2) == 0 assert cot(-pi/2) == 0 assert cot(5*pi/2) == 0 assert cot(7*pi/2) == 0 assert cot(pi/3) == 1/sqrt(3) assert cot(-2*pi/3) == 1/sqrt(3) assert cot(pi/4) == S.One assert cot(-pi/4) == -S.One assert cot(17*pi/4) == S.One assert cot(-3*pi/4) == S.One assert cot(pi/6) == sqrt(3) assert cot(-pi/6) == -sqrt(3) assert cot(7*pi/6) == sqrt(3) assert cot(-5*pi/6) == sqrt(3) assert cot(x*I) == -coth(x)*I assert cot(k*pi*I) == -coth(k*pi)*I assert cot(r).is_real is True assert cot(10*pi/7) == cot(3*pi/7) assert cot(11*pi/7) == -cot(3*pi/7) assert cot(-11*pi/7) == cot(3*pi/7) def test_cot_series(): assert cot(x).series(x, 0, 9) == \ 1/x - x/3 - x**3/45 - 2*x**5/945 - x**7/4725 + O(x**9) # issue 3111: assert cot(x**20 + x**21 + x**22).series(x, 0, 4) == \ x**(-20) - 1/x**19 + x**(-17) - 1/x**16 + x**(-14) - 1/x**13 + \ x**(-11) - 1/x**10 + x**(-8) - 1/x**7 + x**(-5) - 1/x**4 + \ x**(-2) - 1/x + x - x**2 + O(x**4) def test_cot_rewrite(): neg_exp, pos_exp = exp(-x*I), exp(x*I) assert cot(x).rewrite(exp) == I*(pos_exp + neg_exp)/(pos_exp - neg_exp) assert cot(x).rewrite(sin) == 2*sin(2*x)/sin(x)**2 assert cot(x).rewrite(cos) == -cos(x)/cos(x + S.Pi/2) assert cot(x).rewrite(tan) == 1/tan(x) assert cot(sinh(x)).rewrite( exp).subs(x, 3).n() == cot(x).rewrite(exp).subs(x, sinh(3)).n() assert cot(cosh(x)).rewrite( exp).subs(x, 3).n() == cot(x).rewrite(exp).subs(x, cosh(3)).n() assert cot(tanh(x)).rewrite( exp).subs(x, 3).n() == cot(x).rewrite(exp).subs(x, tanh(3)).n() assert cot(coth(x)).rewrite( exp).subs(x, 3).n() == cot(x).rewrite(exp).subs(x, coth(3)).n() assert cot(sin(x)).rewrite( exp).subs(x, 3).n() == cot(x).rewrite(exp).subs(x, sin(3)).n() assert cot(tan(x)).rewrite( exp).subs(x, 3).n() == cot(x).rewrite(exp).subs(x, tan(3)).n() assert cot(log(x)).rewrite(Pow) == -I*(x**-I + x**I)/(x**-I - x**I) assert cot(4*pi/15).rewrite(pow) == (cos(4*pi/15)/sin(4*pi/15)).rewrite(pow) assert cot(pi/19).rewrite(pow) == cot(pi/19) assert cot(pi/19).rewrite(sqrt) == cot(pi/19) def test_cot_subs(): assert cot(x).subs(cot(x), y) == y assert cot(x).subs(x, y) == cot(y) assert cot(x).subs(x, 0) == zoo assert cot(x).subs(x, S.Pi) == zoo def test_cot_expansion(): assert cot(x + y).expand(trig=True) == ((cot(x)*cot(y) - 1)/(cot(x) + cot(y))).expand() assert cot(x - y).expand(trig=True) == (-(cot(x)*cot(y) + 1)/(cot(x) - cot(y))).expand() assert cot(x + y + z).expand(trig=True) == ( (cot(x)*cot(y)*cot(z) - cot(x) - cot(y) - cot(z))/ (-1 + cot(x)*cot(y) + cot(x)*cot(z) + cot(y)*cot(z))).expand() assert cot(3*x).expand(trig=True) == ((cot(x)**3 - 3*cot(x))/(3*cot(x)**2 - 1)).expand() assert 0 == cot(2*x).expand(trig=True).rewrite(cot).subs([(cot(x), Rational(1, 3))])*3 + 4 assert 0 == cot(3*x).expand(trig=True).rewrite(cot).subs([(cot(x), Rational(1, 5))])*55 - 37 assert 0 == cot(4*x - pi/4).expand(trig=True).rewrite(cot).subs([(cot(x), Rational(1, 7))])*863 + 191 def test_asin(): assert asin(nan) == nan assert asin(oo) == -I*oo assert asin(-oo) == I*oo # Note: asin(-x) = - asin(x) assert asin(0) == 0 assert asin(1) == pi/2 assert asin(-1) == -pi/2 assert asin(sqrt(3)/2) == pi/3 assert asin(-sqrt(3)/2) == -pi/3 assert asin(sqrt(2)/2) == pi/4 assert asin(-sqrt(2)/2) == -pi/4 assert asin(sqrt((5 - sqrt(5))/8)) == pi/5 assert asin(-sqrt((5 - sqrt(5))/8)) == -pi/5 assert asin(Rational(1, 2)) == pi/6 assert asin(-Rational(1, 2)) == -pi/6 assert asin((sqrt(2 - sqrt(2)))/2) == pi/8 assert asin(-(sqrt(2 - sqrt(2)))/2) == -pi/8 assert asin((sqrt(5) - 1)/4) == pi/10 assert asin(-(sqrt(5) - 1)/4) == -pi/10 assert asin((sqrt(3) - 1)/sqrt(2**3)) == pi/12 assert asin(-(sqrt(3) - 1)/sqrt(2**3)) == -pi/12 assert asin(x).diff(x) == 1/sqrt(1 - x**2) assert asin(0.2).is_real is True assert asin(-2).is_real is False assert asin(-2*I) == -I*asinh(2) def test_asin_series(): assert asin(x).series(x, 0, 9) == \ x + x**3/6 + 3*x**5/40 + 5*x**7/112 + O(x**9) t5 = asin(x).taylor_term(5, x) assert t5 == 3*x**5/40 assert asin(x).taylor_term(7, x, t5, 0) == 5*x**7/112 def test_asin_rewrite(): assert asin(x).rewrite(log) == -I*log(I*x + sqrt(1 - x**2)) assert asin(x).rewrite(atan) == 2*atan(x/(1 + sqrt(1 - x**2))) assert asin(x).rewrite(acos) == S.Pi/2 - acos(x) def test_acos(): assert acos(nan) == nan assert acos(oo) == I*oo assert acos(-oo) == -I*oo # Note: acos(-x) = pi - acos(x) assert acos(0) == pi/2 assert acos(Rational(1, 2)) == pi/3 assert acos(-Rational(1, 2)) == (2*pi)/3 assert acos(1) == 0 assert acos(-1) == pi assert acos(sqrt(2)/2) == pi/4 assert acos(-sqrt(2)/2) == (3*pi)/4 assert acos(x).diff(x) == -1/sqrt(1 - x**2) assert acos(0.2).is_real is True assert acos(-2).is_real is False def test_acos_series(): assert acos(x).series(x, 0, 8) == \ pi/2 - x - x**3/6 - 3*x**5/40 - 5*x**7/112 + O(x**8) assert acos(x).series(x, 0, 8) == pi/2 - asin(x).series(x, 0, 8) t5 = acos(x).taylor_term(5, x) assert t5 == -3*x**5/40 assert acos(x).taylor_term(7, x, t5, 0) == -5*x**7/112 def test_acos_rewrite(): assert acos(x).rewrite(log) == pi/2 + I*log(I*x + sqrt(1 - x**2)) assert acos(x).rewrite(atan) == \ atan(sqrt(1 - x**2)/x) + (pi/2)*(1 - x*sqrt(1/x**2)) assert acos(0).rewrite(atan) == S.Pi/2 assert acos(0.5).rewrite(atan) == acos(0.5).rewrite(log) assert acos(x).rewrite(asin) == S.Pi/2 - asin(x) def test_atan(): assert atan(nan) == nan assert atan(oo) == pi/2 assert atan(-oo) == -pi/2 assert atan(0) == 0 assert atan(1) == pi/4 assert atan(sqrt(3)) == pi/3 assert atan(oo) == pi/2 assert atan(x).diff(x) == 1/(1 + x**2) assert atan(r).is_real is True assert atan(-2*I) == -I*atanh(2) def test_atan_rewrite(): assert atan(x).rewrite(log) == I*log((1 - I*x)/(1 + I*x))/2 def test_atan2(): assert atan2(0, 0) == S.NaN assert atan2(0, 1) == 0 assert atan2(1, 1) == pi/4 assert atan2(1, 0) == pi/2 assert atan2(1, -1) == 3*pi/4 assert atan2(0, -1) == pi assert atan2(-1, -1) == -3*pi/4 assert atan2(-1, 0) == -pi/2 assert atan2(-1, 1) == -pi/4 u = Symbol("u", positive=True) assert atan2(0, u) == 0 u = Symbol("u", negative=True) assert atan2(0, u) == pi assert atan2(y, oo) == 0 assert atan2(y, -oo)== 2*pi*Heaviside(re(y)) - pi assert atan2(y, x).rewrite(log) == -I*log((x + I*y)/sqrt(x**2 + y**2)) assert atan2(y, x).rewrite(atan) == 2*atan(y/(x + sqrt(x**2 + y**2))) ex = atan2(y, x) - arg(x + I*y) assert ex.subs({x:2, y:3}).rewrite(arg) == 0 assert ex.subs({x:2, y:3*I}).rewrite(arg) == 0 assert ex.subs({x:2*I, y:3}).rewrite(arg) == 0 assert ex.subs({x:2*I, y:3*I}).rewrite(arg) == 0 assert conjugate(atan2(x, y)) == atan2(conjugate(x), conjugate(y)) assert diff(atan2(y, x), x) == -y/(x**2 + y**2) assert diff(atan2(y, x), y) == x/(x**2 + y**2) assert simplify(diff(atan2(y, x).rewrite(log), x)) == -y/(x**2 + y**2) assert simplify(diff(atan2(y, x).rewrite(log), y)) == x/(x**2 + y**2) assert isinstance(atan2(2, 3*I).n(), atan2) def test_acot(): assert acot(nan) == nan assert acot(-oo) == 0 assert acot(oo) == 0 assert acot(1) == pi/4 assert acot(0) == pi/2 assert acot(sqrt(3)/3) == pi/3 assert acot(1/sqrt(3)) == pi/3 assert acot(-1/sqrt(3)) == -pi/3 assert acot(x).diff(x) == -1/(1 + x**2) assert acot(r).is_real is True assert acot(I*pi) == -I*acoth(pi) assert acot(-2*I) == I*acoth(2) def test_acot_rewrite(): assert acot(x).rewrite(log) == I*log((x - I)/(x + I))/2 def test_attributes(): assert sin(x).args == (x,) def test_sincos_rewrite(): assert sin(pi/2 - x) == cos(x) assert sin(pi - x) == sin(x) assert cos(pi/2 - x) == sin(x) assert cos(pi - x) == -cos(x) def _check_even_rewrite(func, arg): """Checks that the expr has been rewritten using f(-x) -> f(x) arg : -x """ return func(arg).args[0] == -arg def _check_odd_rewrite(func, arg): """Checks that the expr has been rewritten using f(-x) -> -f(x) arg : -x """ return func(arg).func.is_Mul def _check_no_rewrite(func, arg): """Checks that the expr is not rewritten""" return func(arg).args[0] == arg def test_evenodd_rewrite(): a = cos(2) # negative b = sin(1) # positive even = [cos] odd = [sin, tan, cot, asin, atan, acot] with_minus = [-1, -2**1024 * E, -pi/105, -x*y, -x - y] for func in even: for expr in with_minus: assert _check_even_rewrite(func, expr) assert _check_no_rewrite(func, a*b) assert func( x - y) == func(y - x) # it doesn't matter which form is canonical for func in odd: for expr in with_minus: assert _check_odd_rewrite(func, expr) assert _check_no_rewrite(func, a*b) assert func( x - y) == -func(y - x) # it doesn't matter which form is canonical def test_issue1448(): assert sin(x).rewrite(cot) == 2*cot(x/2)/(1 + cot(x/2)**2) assert cos(x).rewrite(cot) == -(1 - cot(x/2)**2)/(1 + cot(x/2)**2) assert tan(x).rewrite(cot) == 1/cot(x) assert cot(x).fdiff() == -1 - cot(x)**2 def test_as_leading_term_issue2173(): assert sin(x).as_leading_term(x) == x assert cos(x).as_leading_term(x) == 1 assert tan(x).as_leading_term(x) == x assert cot(x).as_leading_term(x) == 1/x assert asin(x).as_leading_term(x) == x assert acos(x).as_leading_term(x) == x assert atan(x).as_leading_term(x) == x assert acot(x).as_leading_term(x) == x def test_leading_terms(): for func in [sin, cos, tan, cot, asin, acos, atan, acot]: for arg in (1/x, S.Half): eq = func(arg) assert eq.as_leading_term(x) == eq def test_atan2_expansion(): assert cancel(atan2(x**2, x + 1).diff(x) - atan(x**2/(x + 1)).diff(x)) == 0 assert cancel(atan(y/x).series(y, 0, 5) - atan2(y, x).series(y, 0, 5) + atan2(0, x) - atan(0)) == O(y**5) assert cancel(atan(y/x).series(x, 1, 4) - atan2(y, x).series(x, 1, 4) + atan2(y, 1) - atan(y)) == O(x**4) assert cancel(atan((y + x)/x).series(x, 1, 3) - atan2(y + x, x).series(x, 1, 3) + atan2(1 + y, 1) - atan(1 + y)) == O(x**3) assert Matrix([atan2(y, x)]).jacobian([y, x]) == \ Matrix([[x/(y**2 + x**2), -y/(y**2 + x**2)]]) def test_aseries(): def t(n, v, d, e): assert abs( n(1/v).evalf() - n(1/x).series(x, dir=d).removeO().subs(x, v)) < e t(atan, 0.1, '+', 1e-5) t(atan, -0.1, '-', 1e-5) t(acot, 0.1, '+', 1e-5) t(acot, -0.1, '-', 1e-5) def test_issue_1321(): i = Symbol('i', integer=True) e = Symbol('e', even=True) o = Symbol('o', odd=True) # unknown parity for variable assert cos(4*i*pi) == 1 assert sin(4*i*pi) == 0 assert tan(4*i*pi) == 0 assert cot(4*i*pi) == zoo assert cos(3*i*pi) == cos(pi*i) # +/-1 assert sin(3*i*pi) == 0 assert tan(3*i*pi) == 0 assert cot(3*i*pi) == zoo assert cos(4.0*i*pi) == 1 assert sin(4.0*i*pi) == 0 assert tan(4.0*i*pi) == 0 assert cot(4.0*i*pi) == zoo assert cos(3.0*i*pi) == cos(pi*i) # +/-1 assert sin(3.0*i*pi) == 0 assert tan(3.0*i*pi) == 0 assert cot(3.0*i*pi) == zoo assert cos(4.5*i*pi) == cos(0.5*pi*i) assert sin(4.5*i*pi) == sin(0.5*pi*i) assert tan(4.5*i*pi) == tan(0.5*pi*i) assert cot(4.5*i*pi) == cot(0.5*pi*i) # parity of variable is known assert cos(4*e*pi) == 1 assert sin(4*e*pi) == 0 assert tan(4*e*pi) == 0 assert cot(4*e*pi) == zoo assert cos(3*e*pi) == 1 assert sin(3*e*pi) == 0 assert tan(3*e*pi) == 0 assert cot(3*e*pi) == zoo assert cos(4.0*e*pi) == 1 assert sin(4.0*e*pi) == 0 assert tan(4.0*e*pi) == 0 assert cot(4.0*e*pi) == zoo assert cos(3.0*e*pi) == 1 assert sin(3.0*e*pi) == 0 assert tan(3.0*e*pi) == 0 assert cot(3.0*e*pi) == zoo assert cos(4.5*e*pi) == cos(0.5*pi*e) assert sin(4.5*e*pi) == sin(0.5*pi*e) assert tan(4.5*e*pi) == tan(0.5*pi*e) assert cot(4.5*e*pi) == cot(0.5*pi*e) assert cos(4*o*pi) == 1 assert sin(4*o*pi) == 0 assert tan(4*o*pi) == 0 assert cot(4*o*pi) == zoo assert cos(3*o*pi) == -1 assert sin(3*o*pi) == 0 assert tan(3*o*pi) == 0 assert cot(3*o*pi) == zoo assert cos(4.0*o*pi) == 1 assert sin(4.0*o*pi) == 0 assert tan(4.0*o*pi) == 0 assert cot(4.0*o*pi) == zoo assert cos(3.0*o*pi) == -1 assert sin(3.0*o*pi) == 0 assert tan(3.0*o*pi) == 0 assert cot(3.0*o*pi) == zoo assert cos(4.5*o*pi) == cos(0.5*pi*o) assert sin(4.5*o*pi) == sin(0.5*pi*o) assert tan(4.5*o*pi) == tan(0.5*pi*o) assert cot(4.5*o*pi) == cot(0.5*pi*o) # x could be imaginary assert cos(4*x*pi) == cos(4*pi*x) assert sin(4*x*pi) == sin(4*pi*x) assert tan(4*x*pi) == tan(4*pi*x) assert cot(4*x*pi) == cot(4*pi*x) assert cos(3*x*pi) == cos(3*pi*x) assert sin(3*x*pi) == sin(3*pi*x) assert tan(3*x*pi) == tan(3*pi*x) assert cot(3*x*pi) == cot(3*pi*x) assert cos(4.0*x*pi) == cos(4.0*pi*x) assert sin(4.0*x*pi) == sin(4.0*pi*x) assert tan(4.0*x*pi) == tan(4.0*pi*x) assert cot(4.0*x*pi) == cot(4.0*pi*x) assert cos(3.0*x*pi) == cos(3.0*pi*x) assert sin(3.0*x*pi) == sin(3.0*pi*x) assert tan(3.0*x*pi) == tan(3.0*pi*x) assert cot(3.0*x*pi) == cot(3.0*pi*x) assert cos(4.5*x*pi) == cos(4.5*pi*x) assert sin(4.5*x*pi) == sin(4.5*pi*x) assert tan(4.5*x*pi) == tan(4.5*pi*x) assert cot(4.5*x*pi) == cot(4.5*pi*x) def test_inverses(): raises(AttributeError, lambda: sin(x).inverse()) raises(AttributeError, lambda: cos(x).inverse()) assert tan(x).inverse() == atan assert cot(x).inverse() == acot raises(AttributeError, lambda: csc(x).inverse()) raises(AttributeError, lambda: sec(x).inverse()) assert asin(x).inverse() == sin assert acos(x).inverse() == cos assert atan(x).inverse() == tan assert acot(x).inverse() == cot def test_real_imag(): a, b = symbols('a b', real=True) z = a + b*I for deep in [True, False]: assert sin( z).as_real_imag(deep=deep) == (sin(a)*cosh(b), cos(a)*sinh(b)) assert cos( z).as_real_imag(deep=deep) == (cos(a)*cosh(b), -sin(a)*sinh(b)) assert tan(z).as_real_imag(deep=deep) == (sin(a)*cos( a)/(cos(a)**2 + sinh(b)**2), sinh(b)*cosh(b)/(cos(a)**2 + sinh(b)**2)) assert cot(z).as_real_imag(deep=deep) == (sin(a)*cos(a)/( sin(a)**2 + sinh(b)**2), -sinh(b)*cosh(b)/(sin(a)**2 + sinh(b)**2)) assert sin(a).as_real_imag(deep=deep) == (sin(a), 0) assert cos(a).as_real_imag(deep=deep) == (cos(a), 0) assert tan(a).as_real_imag(deep=deep) == (tan(a), 0) assert cot(a).as_real_imag(deep=deep) == (cot(a), 0) @XFAIL def test_sin_cos_with_infinity(): # Test for issue 2097 # http://code.google.com/p/sympy/issues/detail?id=2097 assert sin(oo) == S.NaN assert cos(oo) == S.NaN @slow def test_sincos_rewrite_sqrt(): # equivalent to testing rewrite(pow) for p in [1, 3, 5, 17]: for t in [1, 8]: n = t*p for i in xrange(1, (n + 1)//2 + 1): if 1 == gcd(i, n): x = i*pi/n s1 = sin(x).rewrite(sqrt) c1 = cos(x).rewrite(sqrt) assert not s1.has(cos, sin), "fails for %d*pi/%d" % (i, n) assert not c1.has(cos, sin), "fails for %d*pi/%d" % (i, n) assert 1e-3 > abs(sin(x.evalf(5)) - s1.evalf(2)), "fails for %d*pi/%d" % (i, n) assert 1e-3 > abs(cos(x.evalf(5)) - c1.evalf(2)), "fails for %d*pi/%d" % (i, n) @slow def test_tancot_rewrite_sqrt(): # equivalent to testing rewrite(pow) for p in [1, 3, 5, 17]: for t in [1, 8]: n = t*p for i in xrange(1, (n + 1)//2 + 1): if 1 == gcd(i, n): x = i*pi/n if 2*i != n and 3*i != 2*n: t1 = tan(x).rewrite(sqrt) assert not t1.has(cot, tan), "fails for %d*pi/%d" % (i, n) assert 1e-3 > abs( tan(x.evalf(7)) - t1.evalf(4) ), "fails for %d*pi/%d" % (i, n) if i != 0 and i != n: c1 = cot(x).rewrite(sqrt) assert not c1.has(cot, tan), "fails for %d*pi/%d" % (i, n) assert 1e-3 > abs( cot(x.evalf(7)) - c1.evalf(4) ), "fails for %d*pi/%d" % (i, n) def test_sec(): x = symbols('x', real=True) z = symbols('z') assert sec.nargs == 1 assert sec(0) == 1 assert sec(pi) == -1 assert sec(pi/2) == oo assert sec(-pi/2) == oo assert sec(pi/6) == 2*sqrt(3)/3 assert sec(pi/3) == 2 assert sec(5*pi/2) == oo assert sec(9*pi/7) == -sec(2*pi/7) assert sec(I) == 1/cosh(1) assert sec(x*I) == 1/cosh(x) assert sec(-x) == sec(x) assert sec(x).rewrite(exp) == 1/(exp(I*x)/2 + exp(-I*x)/2) assert sec(x).rewrite(sin) == sec(x) assert sec(x).rewrite(cos) == 1/cos(x) assert sec(x).rewrite(tan) == (tan(x/2)**2 + 1)/(-tan(x/2)**2 + 1) assert sec(x).rewrite(pow) == sec(x) assert sec(x).rewrite(sqrt) == sec(x) assert sec(z).conjugate() == sec(conjugate(z)) assert (sec(z).as_real_imag() == (cos(re(z))*cosh(im(z))/(sin(re(z))**2*sinh(im(z))**2 + cos(re(z))**2*cosh(im(z))**2), sin(re(z))*sinh(im(z))/(sin(re(z))**2*sinh(im(z))**2 + cos(re(z))**2*cosh(im(z))**2))) assert sec(x).expand(trig=True) == 1/cos(x) assert sec(2*x).expand(trig=True) == 1/(2*cos(x)**2 - 1) assert sec(x).is_real == True assert sec(z).is_real == None assert sec(x).as_leading_term() == sec(x) assert sec(0).is_bounded == True assert sec(x).is_bounded == None assert sec(pi/2).is_bounded == False assert series(sec(x), x, x0=0, n=6) == 1 + x**2/2 + 5*x**4/24 + O(x**6) # https://code.google.com/p/sympy/issues/detail?id=4067 assert series(sqrt(sec(x))) == 1 + x**2/4 + 7*x**4/96 + O(x**6) # https://code.google.com/p/sympy/issues/detail?id=4068 assert (series(sqrt(sec(x)), x, x0=pi*3/2, n=4) == 1/sqrt(x) +x**(S(3)/2)/12 + x**(S(7)/2)/160 + O(x**4)) assert sec(x).diff(x) == tan(x)*sec(x) def test_csc(): x = symbols('x', real=True) z = symbols('z') # https://code.google.com/p/sympy/issues/detail?id=3608 cosecant = csc('x') alternate = 1/sin('x') assert cosecant.equals(alternate) == True assert alternate.equals(cosecant) == True assert csc.nargs == 1 assert csc(0) == oo assert csc(pi) == oo assert csc(pi/2) == 1 assert csc(-pi/2) == -1 assert csc(pi/6) == 2 assert csc(pi/3) == 2*sqrt(3)/3 assert csc(5*pi/2) == 1 assert csc(9*pi/7) == -csc(2*pi/7) assert csc(I) == -I/sinh(1) assert csc(x*I) == -I/sinh(x) assert csc(-x) == -csc(x) assert csc(x).rewrite(exp) == 2*I/(exp(I*x) - exp(-I*x)) assert csc(x).rewrite(sin) == 1/sin(x) assert csc(x).rewrite(cos) == csc(x) assert csc(x).rewrite(tan) == (tan(x/2)**2 + 1)/(2*tan(x/2)) assert csc(z).conjugate() == csc(conjugate(z)) assert (csc(z).as_real_imag() == (sin(re(z))*cosh(im(z))/(sin(re(z))**2*cosh(im(z))**2 + cos(re(z))**2*sinh(im(z))**2), -cos(re(z))*sinh(im(z))/(sin(re(z))**2*cosh(im(z))**2 + cos(re(z))**2*sinh(im(z))**2))) assert csc(x).expand(trig=True) == 1/sin(x) assert csc(2*x).expand(trig=True) == 1/(2*sin(x)*cos(x)) assert csc(x).is_real == True assert csc(z).is_real == None assert csc(x).as_leading_term() == csc(x) assert csc(0).is_bounded == False assert csc(x).is_bounded == None assert csc(pi/2).is_bounded == True assert series(csc(x), x, x0=pi/2, n=6) == 1 + x**2/2 + 5*x**4/24 + O(x**6) assert series(csc(x), x, x0=0, n=6) == \ 1/x + x/6 + 7*x**3/360 + 31*x**5/15120 + O(x**6) assert csc(x).diff(x) == -cot(x)*csc(x) @XFAIL @slow def test_csc_rewrite_failing(): # Move these 2 tests to test_csc() once bugs fixed # sin(x).rewrite(pow) raises RuntimeError: maximum recursion depth # https://code.google.com/p/sympy/issues/detail?id=4072 assert csc(x).rewrite(pow) == csc(x) assert csc(x).rewrite(sqrt) == csc(x) sympy-0.7.4.1/sympy/functions/elementary/tests/__init__.py0000644000175000017500000000000012253362407024043 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/functions/elementary/__init__.py0000644000175000017500000000023212253362407022710 0ustar georgeskgeorgeskfrom . import complexes from . import exponential from . import hyperbolic from . import integers from . import trigonometric from . import miscellaneous sympy-0.7.4.1/sympy/functions/combinatorial/0000755000175000017500000000000012253362407021260 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/functions/combinatorial/numbers.py0000644000175000017500000012006212253362407023306 0ustar georgeskgeorgesk""" 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 __future__ import print_function, division from sympy.core.function import Function, expand_mul from sympy.core import S, Symbol, Rational, oo, Integer, C, Add, Dummy from sympy.core.compatibility import as_int, SYMPY_INTS, xrange from sympy.core.cache import cacheit from sympy.functions.combinatorial.factorials import factorial from sympy.mpmath import bernfrac from sympy.mpmath.libmp import ifib as _ifib def _product(a, b): p = 1 for k in xrange(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 # # # #----------------------------------------------------------------------------# 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 ========== .. [1] http://en.wikipedia.org/wiki/Fibonacci_number .. [2] http://mathworld.wolfram.com/FibonacciNumber.html See Also ======== bell, bernoulli, catalan, euler, harmonic, 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) 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 ========== .. [1] http://en.wikipedia.org/wiki/Lucas_number .. [2] http://mathworld.wolfram.com/LucasNumber.html See Also ======== bell, bernoulli, catalan, euler, fibonacci, harmonic """ @classmethod def eval(cls, n): if n.is_Integer: return fibonacci(n + 1) + fibonacci(n - 1) #----------------------------------------------------------------------------# # # # Bernoulli numbers # # # #----------------------------------------------------------------------------# 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 ========== .. [1] http://en.wikipedia.org/wiki/Bernoulli_number .. [2] http://en.wikipedia.org/wiki/Bernoulli_polynomial .. [3] http://mathworld.wolfram.com/BernoulliNumber.html .. [4] http://mathworld.wolfram.com/BernoulliPolynomial.html See Also ======== bell, catalan, euler, fibonacci, harmonic, lucas """ # 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 xrange(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 xrange(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 xrange(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 # # # #----------------------------------------------------------------------------# 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 ========== .. [1] http://en.wikipedia.org/wiki/Bell_number .. [2] http://mathworld.wolfram.com/BellNumber.html .. [3] http://mathworld.wolfram.com/BellPolynomial.html See Also ======== bernoulli, catalan, euler, fibonacci, harmonic, lucas """ @staticmethod @recurrence_memo([1, 1]) def _bell(n, prev): s = 1 a = 1 for k in xrange(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 xrange(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 xrange(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 # # # #----------------------------------------------------------------------------# class harmonic(Function): r""" Harmonic numbers The nth harmonic number is given by `\operatorname{H}_{n} = 1 + \frac{1}{2} + \frac{1}{3} + \ldots + \frac{1}{n}`. More generally: .. math:: \operatorname{H}_{n,m} = \sum_{k=1}^{n} \frac{1}{k^m} As `n \rightarrow \infty`, `\operatorname{H}_{n,m} \rightarrow \zeta(m)`, the Riemann zeta function. * ``harmonic(n)`` gives the nth harmonic number, `\operatorname{H}_n` * ``harmonic(n, m)`` gives the nth generalized harmonic number of order `m`, `\operatorname{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 >>> from sympy import Symbol, Sum >>> n = Symbol("n") >>> harmonic(n).rewrite(Sum) Sum(1/_k, (_k, 1, n)) We can rewrite harmonic numbers in terms of polygamma functions: >>> from sympy import digamma, polygamma >>> m = Symbol("m") >>> harmonic(n).rewrite(digamma) polygamma(0, n + 1) + EulerGamma >>> harmonic(n).rewrite(polygamma) polygamma(0, n + 1) + EulerGamma >>> harmonic(n,3).rewrite(polygamma) polygamma(2, n + 1)/2 - polygamma(2, 1)/2 >>> harmonic(n,m).rewrite(polygamma) (-1)**m*(polygamma(m - 1, 1) - polygamma(m - 1, n + 1))/factorial(m - 1) Integer offsets in the argument can be pulled out: >>> from sympy import expand_func >>> expand_func(harmonic(n+4)) harmonic(n) + 1/(n + 4) + 1/(n + 3) + 1/(n + 2) + 1/(n + 1) >>> expand_func(harmonic(n-4)) harmonic(n) - 1/(n - 1) - 1/(n - 2) - 1/(n - 3) - 1/n Some limits can be computed as well: >>> from sympy import limit, oo >>> limit(harmonic(n), n, oo) oo >>> limit(harmonic(n, 2), n, oo) pi**2/6 >>> limit(harmonic(n, 3), n, oo) -polygamma(2, 1)/2 >>> limit(harmonic(m, n), m, oo) zeta(n) References ========== .. [1] http://en.wikipedia.org/wiki/Harmonic_number .. [2] http://functions.wolfram.com/GammaBetaErf/HarmonicNumber/ .. [3] http://functions.wolfram.com/GammaBetaErf/HarmonicNumber2/ See Also ======== bell, bernoulli, catalan, euler, fibonacci, lucas """ # 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)) def _eval_rewrite_as_polygamma(self, n, m=1): from sympy.functions.special.gamma_functions import polygamma return S.NegativeOne**m/factorial(m - 1) * (polygamma(m - 1, 1) - polygamma(m - 1, n + 1)) def _eval_rewrite_as_digamma(self, n, m=1): from sympy.functions.special.gamma_functions import polygamma return self.rewrite(polygamma) def _eval_rewrite_as_trigamma(self, n, m=1): from sympy.functions.special.gamma_functions import polygamma return self.rewrite(polygamma) def _eval_rewrite_as_Sum(self, n, m=None): k = C.Dummy("k", integer=True) if m is None: m = S.One return C.Sum(k**(-m), (k, 1, n)) def _eval_expand_func(self, **hints): n = self.args[0] m = self.args[1] if len(self.args) == 2 else 1 if m == S.One: if n.is_Add: off = n.args[0] nnew = n - off if off.is_Integer and off.is_positive: result = [S.One/(nnew + i) for i in xrange(off, 0, -1)] + [harmonic(nnew)] return Add(*result) elif off.is_Integer and off.is_negative: result = [-S.One/(nnew + i) for i in xrange(0, off, -1)] + [harmonic(nnew)] return Add(*result) return self def _eval_rewrite_as_tractable(self, n, m=1): from sympy.functions.special.gamma_functions import polygamma return self.rewrite(polygamma).rewrite("tractable", deep=True) #----------------------------------------------------------------------------# # # # Euler numbers # # # #----------------------------------------------------------------------------# 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 ========== .. [1] http://en.wikipedia.org/wiki/Euler_numbers .. [2] http://mathworld.wolfram.com/EulerNumber.html .. [3] http://en.wikipedia.org/wiki/Alternating_permutation .. [4] http://mathworld.wolfram.com/AlternatingPermutation.html See Also ======== bell, bernoulli, catalan, fibonacci, harmonic, lucas """ 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 # # # #----------------------------------------------------------------------------# 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) + log(4))*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 ========== .. [1] http://en.wikipedia.org/wiki/Catalan_number .. [2] http://mathworld.wolfram.com/CatalanNumber.html .. [3] http://functions.wolfram.com/GammaBetaErf/CatalanNumber/ .. [4] http://geometer.org/mathcircles/catalan.pdf See Also ======== bell, bernoulli, euler, fibonacci, harmonic, lucas 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) ####################################################################### ### ### Functions for enumerating partitions, permutations and combinations ### ####################################################################### class _MultisetHistogram(tuple): pass _N = -1 _ITEMS = -2 _M = slice(None, _ITEMS) def _multiset_histogram(n): """Return tuple used in permutation and combination counting. Input is a dictionary giving items with counts as values or a sequence of items (which need not be sorted). The data is stored in a class deriving from tuple so it is easily recognized and so it can be converted easily to a list. """ if type(n) is dict: # item: count if not all(isinstance(v, int) and v >= 0 for v in n.values()): raise ValueError tot = sum(n.values()) items = sum(1 for k in n if n[k] > 0) return _MultisetHistogram([n[k] for k in n if n[k] > 0] + [items, tot]) else: n = list(n) s = set(n) if len(s) == len(n): n = [1]*len(n) n.extend([len(n), len(n)]) return _MultisetHistogram(n) m = dict(zip(s, range(len(s)))) d = dict(zip(range(len(s)), [0]*len(s))) for i in n: d[m[i]] += 1 return _multiset_histogram(d) def nP(n, k=None, replacement=False): """Return the number of permutations of ``n`` items taken ``k`` at a time. Possible values for ``n``:: integer - set of length ``n`` sequence - converted to a multiset internally multiset - {element: multiplicity} If ``k`` is None then the total of all permutations of length 0 through the number of items represented by ``n`` will be returned. If ``replacement`` is True then a given item can appear more than once in the ``k`` items. (For example, for 'ab' permutations of 2 would include 'aa', 'ab', 'ba' and 'bb'.) The multiplicity of elements in ``n`` is ignored when ``replacement`` is True but the total number of elements is considered since no element can appear more times than the number of elements in ``n``. Examples ======== >>> from sympy.functions.combinatorial.numbers import nP >>> from sympy.utilities.iterables import multiset_permutations, multiset >>> nP(3, 2) 6 >>> nP('abc', 2) == nP(multiset('abc'), 2) == 6 True >>> nP('aab', 2) 3 >>> nP([1, 2, 2], 2) 3 >>> [nP(3, i) for i in range(4)] [1, 3, 6, 6] >>> nP(3) == sum(_) True When ``replacement`` is True, each item can have multiplicity equal to the length represented by ``n``: >>> nP('aabc', replacement=True) 121 >>> [len(list(multiset_permutations('aaaabbbbcccc', i))) for i in range(5)] [1, 3, 9, 27, 81] >>> sum(_) 121 References ========== .. [1] http://en.wikipedia.org/wiki/Permutation See Also ======== sympy.utilities.iterables.multiset_permutations """ try: n = as_int(n) except ValueError: return Integer(_nP(_multiset_histogram(n), k, replacement)) return Integer(_nP(n, k, replacement)) @cacheit def _nP(n, k=None, replacement=False): from sympy.functions.combinatorial.factorials import factorial from sympy.core.mul import prod if k == 0: return 1 if isinstance(n, SYMPY_INTS): # n different items # assert n >= 0 if k is None: return sum(_nP(n, i, replacement) for i in range(n + 1)) elif replacement: return n**k elif k > n: return 0 elif k == n: return factorial(k) elif k == 1: return n else: # assert k >= 0 return _product(n - k + 1, n) elif isinstance(n, _MultisetHistogram): if k is None: return sum(_nP(n, i, replacement) for i in range(n[_N] + 1)) elif replacement: return n[_ITEMS]**k elif k == n[_N]: return factorial(k)/prod([factorial(i) for i in n[_M] if i > 1]) elif k > n[_N]: return 0 elif k == 1: return n[_ITEMS] else: # assert k >= 0 tot = 0 n = list(n) for i in range(len(n[_M])): if not n[i]: continue n[_N] -= 1 if n[i] == 1: n[i] = 0 n[_ITEMS] -= 1 tot += _nP(_MultisetHistogram(n), k - 1) n[_ITEMS] += 1 n[i] = 1 else: n[i] -= 1 tot += _nP(_MultisetHistogram(n), k - 1) n[i] += 1 n[_N] += 1 return tot @cacheit def _AOP_product(n): """for n = (m1, m2, .., mk) return the coefficients of the polynomial, prod(sum(x**i for i in range(nj + 1)) for nj in n); i.e. the coefficients of the product of AOPs (all-one polynomials) or order given in n. The resulting coefficient corresponding to x**r is the number of r-length combinations of sum(n) elements with multiplicities given in n. The coefficients are given as a default dictionary (so if a query is made for a key that is not present, 0 will be returned). Examples ======== >>> from sympy.functions.combinatorial.numbers import _AOP_product >>> from sympy.abc import x >>> n = (2, 2, 3) # e.g. aabbccc >>> prod = ((x**2 + x + 1)*(x**2 + x + 1)*(x**3 + x**2 + x + 1)).expand() >>> c = _AOP_product(n); dict(c) {0: 1, 1: 3, 2: 6, 3: 8, 4: 8, 5: 6, 6: 3, 7: 1} >>> [c[i] for i in range(8)] == [prod.coeff(x, i) for i in range(8)] True The generating poly used here is the same as that listed in http://tinyurl.com/cep849r, but in a refactored form. """ from collections import defaultdict n = list(n) ord = sum(n) need = (ord + 2)//2 rv = [1]*(n.pop() + 1) rv.extend([0]*(need - len(rv))) rv = rv[:need] while n: ni = n.pop() N = ni + 1 was = rv[:] for i in range(1, min(N, len(rv))): rv[i] += rv[i - 1] for i in range(N, need): rv[i] += rv[i - 1] - was[i - N] rev = list(reversed(rv)) if ord % 2: rv = rv + rev else: rv[-1:] = rev d = defaultdict(int) for i in range(len(rv)): d[i] = rv[i] return d def nC(n, k=None, replacement=False): """Return the number of combinations of ``n`` items taken ``k`` at a time. Possible values for ``n``:: integer - set of length ``n`` sequence - converted to a multiset internally multiset - {element: multiplicity} If ``k`` is None then the total of all combinations of length 0 through the number of items represented in ``n`` will be returned. If ``replacement`` is True then a given item can appear more than once in the ``k`` items. (For example, for 'ab' sets of 2 would include 'aa', 'ab', and 'bb'.) The multiplicity of elements in ``n`` is ignored when ``replacement`` is True but the total number of elements is considered since no element can appear more times than the number of elements in ``n``. Examples ======== >>> from sympy.functions.combinatorial.numbers import nC >>> from sympy.utilities.iterables import multiset_combinations >>> nC(3, 2) 3 >>> nC('abc', 2) 3 >>> nC('aab', 2) 2 When ``replacement`` is True, each item can have multiplicity equal to the length represented by ``n``: >>> nC('aabc', replacement=True) 35 >>> [len(list(multiset_combinations('aaaabbbbcccc', i))) for i in range(5)] [1, 3, 6, 10, 15] >>> sum(_) 35 If there are ``k`` items with multiplicities ``m_1, m_2, ..., m_k`` then the total of all combinations of length 0 hrough ``k`` is the product, ``(m_1 + 1)*(m_2 + 1)*...*(m_k + 1)``. When the multiplicity of each item is 1 (i.e., k unique items) then there are 2**k combinations. For example, if there are 4 unique items, the total number of combinations is 16: >>> sum(nC(4, i) for i in range(5)) 16 References ========== .. [1] http://en.wikipedia.org/wiki/Combination .. [2] http://tinyurl.com/cep849r See Also ======== sympy.utilities.iterables.multiset_combinations """ from sympy.functions.combinatorial.factorials import binomial from sympy.core.mul import prod if isinstance(n, SYMPY_INTS): if k is None: if not replacement: return 2**n return sum(nC(n, i, replacement) for i in range(n + 1)) assert k >= 0 if replacement: return binomial(n + k - 1, k) return binomial(n, k) if isinstance(n, _MultisetHistogram): N = n[_N] if k is None: if not replacement: return prod(m + 1 for m in n[_M]) return sum(nC(n, i, replacement) for i in range(N + 1)) elif replacement: return nC(n[_ITEMS], k, replacement) # assert k >= 0 elif k in (1, N - 1): return n[_ITEMS] elif k in (0, N): return 1 return _AOP_product(tuple(n[_M]))[k] else: return nC(_multiset_histogram(n), k, replacement) @cacheit def _stirling1(n, k): if n == k == 0: return S.One if 0 in (n, k): return S.Zero n1 = n - 1 # some special values if n == k: return S.One elif k == 1: return factorial(n1) elif k == n1: return C.binomial(n, 2) elif k == n - 2: return (3*n - 1)*C.binomial(n, 3)/4 elif k == n - 3: return C.binomial(n, 2)*C.binomial(n, 4) # general recurrence return n1*_stirling1(n1, k) + _stirling1(n1, k - 1) @cacheit def _stirling2(n, k): if n == k == 0: return S.One if 0 in (n, k): return S.Zero n1 = n - 1 # some special values if k == n1: return C.binomial(n, 2) elif k == 2: return 2**n1 - 1 # general recurrence return k*_stirling2(n1, k) + _stirling2(n1, k - 1) def stirling(n, k, d=None, kind=2, signed=False): """Return Stirling number S(n, k) of the first or second (default) kind. The sum of all Stirling numbers of the second kind for k = 1 through n is bell(n). The recurrence relationship for these numbers is:: {0} {n} {0} {n + 1} {n} { n } { } = 1; { } = { } = 0; { } = j*{ } + { } {0} {0} {k} { k } {k} {k - 1} where ``j`` is:: ``n`` for Stirling numbers of the first kind ``-n`` for signed Stirling numbers of the first kind ``k`` for Stirling numbers of the second kind The first kind of Stirling number counts the number of permutations of ``n`` distinct items that have ``k`` cycles; the second kind counts the ways in which ``n`` distinct items can be partitioned into ``k`` parts. If ``d`` is given, the "reduced Stirling number of the second kind" is returned: ``S^{d}(n, k) = S(n - d + 1, k - d + 1)`` with ``n >= k >= d``. (This counts the ways to partition ``n`` consecutive integers into ``k`` groups with no pairwise difference less than ``d``. See example below.) To obtain the signed Stirling numbers of the first kind, use keyword ``signed=True``. Using this keyword automatically sets ``kind`` to 1. Examples ======== >>> from sympy.functions.combinatorial.numbers import stirling, bell >>> from sympy.combinatorics import Permutation >>> from sympy.utilities.iterables import multiset_partitions, permutations First kind (unsigned by default): >>> [stirling(6, i, kind=1) for i in range(7)] [0, 120, 274, 225, 85, 15, 1] >>> perms = list(permutations(range(4))) >>> [sum(Permutation(p).cycles == i for p in perms) for i in range(5)] [0, 6, 11, 6, 1] >>> [stirling(4, i, kind=1) for i in range(5)] [0, 6, 11, 6, 1] First kind (signed): >>> [stirling(4, i, signed=True) for i in range(5)] [0, -6, 11, -6, 1] Second kind: >>> [stirling(10, i) for i in range(12)] [0, 1, 511, 9330, 34105, 42525, 22827, 5880, 750, 45, 1, 0] >>> sum(_) == bell(10) True >>> len(list(multiset_partitions(range(4), 2))) == stirling(4, 2) True Reduced second kind: >>> from sympy import subsets, oo >>> def delta(p): ... if len(p) == 1: ... return oo ... return min(abs(i[0] - i[1]) for i in subsets(p, 2)) >>> parts = multiset_partitions(range(5), 3) >>> d = 2 >>> sum(1 for p in parts if all(delta(i) >= d for i in p)) 7 >>> stirling(5, 3, 2) 7 References ========== .. [1] http://en.wikipedia.org/wiki/Stirling_numbers_of_the_first_kind .. [2] http://en.wikipedia.org/wiki/Stirling_numbers_of_the_second_kind See Also ======== sympy.utilities.iterables.multiset_partitions """ # TODO: make this a class like bell() n = as_int(n) k = as_int(k) if n < 0: raise ValueError('n must be nonnegative') if k > n: return S.Zero if d: # assert k >= d # kind is ignored -- only kind=2 is supported return _stirling2(n - d + 1, k - d + 1) elif signed: # kind is ignored -- only kind=1 is supported return (-1)**(n - k)*_stirling1(n, k) if kind == 1: return _stirling1(n, k) elif kind == 2: return _stirling2(n, k) else: raise ValueError('kind must be 1 or 2, not %s' % k) @cacheit def _nT(n, k): """Return the partitions of ``n`` items into ``k`` parts. This is used by ``nT`` for the case when ``n`` is an integer.""" if k == 0: return 1 if k == n else 0 return sum(_nT(n - k, j) for j in range(min(k, n - k) + 1)) def nT(n, k=None): """Return the number of ``k``-sized partitions of ``n`` items. Possible values for ``n``:: integer - ``n`` identical items sequence - converted to a multiset internally multiset - {element: multiplicity} Note: the convention for ``nT`` is different than that of ``nC`` and``nP`` in that here an integer indicates ``n`` *identical* items instead of a set of length ``n``; this is in keepng with the ``partitions`` function which treats its integer-``n`` input like a list of ``n`` 1s. One can use ``range(n)`` for ``n`` to indicate ``n`` distinct items. If ``k`` is None then the total number of ways to partition the elements represented in ``n`` will be returned. Examples ======== >>> from sympy.functions.combinatorial.numbers import nT Partitions of the given multiset: >>> [nT('aabbc', i) for i in range(1, 7)] [1, 8, 11, 5, 1, 0] >>> nT('aabbc') == sum(_) True (TODO The following can be activated with >>> when taocp_multiset_permutation is in place.) >> [nT("mississippi", i) for i in range(1, 12)] [1, 74, 609, 1521, 1768, 1224, 579, 197, 50, 9, 1] Partitions when all items are identical: >>> [nT(5, i) for i in range(1, 6)] [1, 2, 2, 1, 1] >>> nT('1'*5) == sum(_) True When all items are different: >>> [nT(range(5), i) for i in range(1, 6)] [1, 15, 25, 10, 1] >>> nT(range(5)) == sum(_) True References ========== .. [1] http://undergraduate.csse.uwa.edu.au/units/CITS7209/partition.pdf See Also ======== sympy.utilities.iterables.partitions sympy.utilities.iterables.multiset_partitions """ from sympy.utilities.iterables import multiset_partitions if isinstance(n, SYMPY_INTS): # assert n >= 0 # all the same if k is None: return sum(_nT(n, k) for k in range(1, n + 1)) return _nT(n, k) if not isinstance(n, _MultisetHistogram): try: # if n contains hashable items there is some # quick handling that can be done u = len(set(n)) if u == 1: return nT(len(n), k) elif u == len(n): n = range(u) raise TypeError except TypeError: n = _multiset_histogram(n) N = n[_N] if k is None and N == 1: return 1 if k in (1, N): return 1 if k == 2 or N == 2 and k is None: m, r = divmod(N, 2) rv = sum(nC(n, i) for i in range(1, m + 1)) if not r: rv -= nC(n, m)//2 if k is None: rv += 1 # for k == 1 return rv if N == n[_ITEMS]: # all distinct if k is None: return bell(N) return stirling(N, k) if k is None: return sum(nT(n, k) for k in range(1, N + 1)) tot = 0 for p in multiset_partitions( [i for i, j in enumerate(n[_M]) for ii in range(j)]): tot += len(p) == k return tot sympy-0.7.4.1/sympy/functions/combinatorial/factorials.py0000644000175000017500000004166012253362407023770 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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, xrange from sympy.core.cache import cacheit class CombinatorialFunction(Function): """Base class for combinatorial functions. """ def _eval_simplify(self, ratio, measure): from sympy.simplify.simplify import combsimp expr = combsimp(self) if measure(expr) <= ratio*measure(self): return expr return self ############################################################################### ######################## FACTORIAL and MULTI-FACTORIAL ######################## ############################################################################### 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) factorial(n) >>> factorial(2*n) factorial(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 S.Infinity: return S.Infinity 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 def _eval_is_positive(self): if self.args[0].is_integer and self.args[0].is_positive: return True 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 ========== .. [1] http://en.wikipedia.org/wiki/Subfactorial Examples ======== >>> from sympy import subfactorial >>> from sympy.abc import n >>> subfactorial(n + 1) subfactorial(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") 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) factorial2(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 ############################################################################### ######################## RISING and FALLING FACTORIALS ######################## ############################################################################### 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), xrange(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), xrange(1, abs(int(k)) + 1), 1) def _eval_rewrite_as_gamma(self, x, k): return C.gamma(x + k) / C.gamma(x) 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), xrange(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), xrange(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 ############################# ############################################################################### 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. To expand the binomial when n is a symbol, use either expand_func() or expand(func=True). The former will keep the polynomial in factored form while the latter will expand the polynomial itself. See examples for details. Examples ======== >>> from sympy import Symbol, Rational, binomial, expand_func >>> 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) binomial(n, 3) >>> binomial(n, 3).expand(func=True) n**3/6 - n**2/2 + n/3 >>> expand_func(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 = 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) elif n.is_Number: result = n - k + 1 for i in xrange(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_expand_func(self, **hints): """ Function to expand binomial(n,k) when m is positive integer Also, n is self.args[0] and k is self.args[1] while using binomial(n, k) """ n = self.args[0] if n.is_Number: return binomial(*self.args) k = self.args[1] if k.is_Add and n in k.args: k = n - k if k.is_Integer: if k == S.Zero: return S.One elif k < 0: return S.Zero else: n = self.args[0] result = n - k + 1 for i in xrange(2, k + 1): result *= n - k + i result /= i return result else: return binomial(*self.args) 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 sympy-0.7.4.1/sympy/functions/combinatorial/tests/0000755000175000017500000000000012253362407022422 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/functions/combinatorial/tests/test_comb_numbers.py0000644000175000017500000002713612253362407026517 0ustar georgeskgeorgeskimport string from sympy import (bernoulli, Symbol, symbols, Dummy, Sum, harmonic, Rational, oo, zoo, pi, I, bell, fibonacci, lucas, euler, catalan, binomial, gamma, sqrt, hyper, log, digamma, trigamma, polygamma, diff, Expr, sympify, expand_func, EulerGamma, factorial) from sympy.utilities.pytest import XFAIL, raises x = Symbol('x') def test_bernoulli(): assert bernoulli(0) == 1 assert bernoulli(1) == Rational(-1, 2) assert bernoulli(2) == Rational(1, 6) assert bernoulli(3) == 0 assert bernoulli(4) == Rational(-1, 30) assert bernoulli(5) == 0 assert bernoulli(6) == Rational(1, 42) assert bernoulli(7) == 0 assert bernoulli(8) == Rational(-1, 30) assert bernoulli(10) == Rational(5, 66) assert bernoulli(1000001) == 0 assert bernoulli(0, x) == 1 assert bernoulli(1, x) == x - Rational(1, 2) assert bernoulli(2, x) == x**2 - x + Rational(1, 6) assert bernoulli(3, x) == x**3 - (3*x**2)/2 + x/2 # Should be fast; computed with mpmath b = bernoulli(1000) assert b.p % 10**10 == 7950421099 assert b.q == 342999030 b = bernoulli(10**6, evaluate=False).evalf() assert str(b) == '-2.23799235765713e+4767529' def test_fibonacci(): assert [fibonacci(n) for n in range(-3, 5)] == [2, -1, 1, 0, 1, 1, 2, 3] assert fibonacci(100) == 354224848179261915075 assert [lucas(n) for n in range(-3, 5)] == [-4, 3, -1, 2, 1, 3, 4, 7] assert lucas(100) == 792070839848372253127 assert fibonacci(1, x) == 1 assert fibonacci(2, x) == x assert fibonacci(3, x) == x**2 + 1 assert fibonacci(4, x) == x**3 + 2*x def test_bell(): assert [bell(n) for n in range(8)] == [1, 1, 2, 5, 15, 52, 203, 877] assert bell(0, x) == 1 assert bell(1, x) == x assert bell(2, x) == x**2 + x assert bell(5, x) == x**5 + 10*x**4 + 25*x**3 + 15*x**2 + x X = symbols('x:6') # X = (x0, x1, .. x5) # at the same time: X[1] = x1, X[2] = x2 for standard readablity. # but we must supply zero-based indexed object X[1:] = (x1, .. x5) assert bell(6, 2, X[1:]) == 6*X[5]*X[1] + 15*X[4]*X[2] + 10*X[3]**2 assert bell( 6, 3, X[1:]) == 15*X[4]*X[1]**2 + 60*X[3]*X[2]*X[1] + 15*X[2]**3 X = (1, 10, 100, 1000, 10000) assert bell(6, 2, X) == (6 + 15 + 10)*10000 X = (1, 2, 3, 3, 5) assert bell(6, 2, X) == 6*5 + 15*3*2 + 10*3**2 X = (1, 2, 3, 5) assert bell(6, 3, X) == 15*5 + 60*3*2 + 15*2**3 def test_harmonic(): assert harmonic(1, 1) == 1 assert harmonic(2, 1) == Rational(3, 2) assert harmonic(3, 1) == Rational(11, 6) assert harmonic(4, 1) == Rational(25, 12) assert harmonic(3, 1) == harmonic(3) assert harmonic(3, 5) == 1 + Rational(1, 2**5) + Rational(1, 3**5) assert harmonic(10, 0) == 10 assert harmonic(oo, 1) == zoo assert harmonic(oo, 2) == (pi**2)/6 def replace_dummy(expr, sym): dum = expr.atoms(Dummy) if not dum: return expr assert len(dum) == 1 return expr.xreplace({dum.pop(): sym}) def test_harmonic_rewrite_sum(): n = Symbol("n") m = Symbol("m") _k = Dummy("k") assert replace_dummy(harmonic(n).rewrite(Sum), _k) == Sum(1/_k, (_k, 1, n)) assert replace_dummy(harmonic(n, m).rewrite(Sum), _k) == Sum(_k**(-m), (_k, 1, n)) @XFAIL def test_harmonic_rewrite_sum(): n = Symbol("n") m = Symbol("m") assert harmonic(n).rewrite(digamma) == polygamma(0, n + 1) + EulerGamma assert harmonic(n).rewrite(trigamma) == polygamma(0, n + 1) + EulerGamma assert harmonic(n).rewrite(polygamma) == polygamma(0, n + 1) + EulerGamma assert harmonic(n,3).rewrite(polygamma) == polygamma(2, n + 1)/2 - polygamma(2, 1)/2 assert harmonic(n,m).rewrite(polygamma) == (-1)**m*(polygamma(m - 1, 1) - polygamma(m - 1, n + 1))/factorial(m - 1) assert expand_func(harmonic(n+4)) == harmonic(n) + 1/(n + 4) + 1/(n + 3) + 1/(n + 2) + 1/(n + 1) assert expand_func(harmonic(n-4)) == harmonic(n) - 1/(n - 1) - 1/(n - 2) - 1/(n - 3) - 1/n assert harmonic(n, m).rewrite("tractable") == harmonic(n, m).rewrite(polygamma) _k = Dummy("k") assert harmonic(n).rewrite(Sum) == Sum(1/_k, (_k, 1, n)) assert harmonic(n, m).rewrite(Sum) == Sum(_k**(-m), (_k, 1, n)) def test_euler(): assert euler(0) == 1 assert euler(1) == 0 assert euler(2) == -1 assert euler(3) == 0 assert euler(4) == 5 assert euler(6) == -61 assert euler(8) == 1385 assert euler(20, evaluate=False) != 370371188237525 n = Symbol('n', integer=True) assert euler(n) != -1 assert euler(n).subs(n, 2) == -1 assert euler(20).evalf() == 370371188237525.0 assert euler(20, evaluate=False).evalf() == 370371188237525.0 assert euler(n).rewrite(Sum) == euler(n) # XXX: Not sure what the guy who wrote this test was trying to do with the _j and _k stuff assert euler(2*n + 1).rewrite(Sum) == 0 @XFAIL def test_euler_failing(): # depends on dummy variables being implemented http://code.google.com/p/sympy/issues/detail?id=2566 assert euler(2*n).rewrite(Sum) == I*Sum(Sum((-1)**_j*2**(-_k)*I**(-_k)*(-2*_j + _k)**(2*n + 1)*binomial(_k, _j)/_k, (_j, 0, _k)), (_k, 1, 2*n + 1)) def test_catalan(): assert catalan(1) == 1 assert catalan(2) == 2 assert catalan(3) == 5 assert catalan(4) == 14 assert catalan(x) == catalan(x) assert catalan(2*x).rewrite(binomial) == binomial(4*x, 2*x)/(2*x + 1) assert catalan(Rational(1, 2)).rewrite(gamma) == 8/(3*pi) assert catalan(3*x).rewrite(gamma) == 4**( 3*x)*gamma(3*x + Rational(1, 2))/(sqrt(pi)*gamma(3*x + 2)) assert catalan(x).rewrite(hyper) == hyper((-x + 1, -x), (2,), 1) assert diff(catalan(x), x) == (polygamma( 0, x + Rational(1, 2)) - polygamma(0, x + 2) + log(4))*catalan(x) c = catalan(0.5).evalf() assert str(c) == '0.848826363156775' def test_nC_nP_nT(): from sympy.utilities.iterables import ( multiset_permutations, multiset_combinations, multiset_partitions, partitions, subsets, permutations) from sympy.functions.combinatorial.numbers import ( nP, nC, nT, stirling, _multiset_histogram, _AOP_product) from sympy.combinatorics.permutations import Permutation from sympy.core.numbers import oo from random import choice c = string.ascii_lowercase for i in range(100): s = ''.join(choice(c) for i in range(7)) u = len(s) == len(set(s)) try: tot = 0 for i in range(8): check = nP(s, i) tot += check assert len(list(multiset_permutations(s, i))) == check if u: assert nP(len(s), i) == check assert nP(s) == tot except AssertionError: print(s, i, 'failed perm test') raise ValueError() for i in range(100): s = ''.join(choice(c) for i in range(7)) u = len(s) == len(set(s)) try: tot = 0 for i in range(8): check = nC(s, i) tot += check assert len(list(multiset_combinations(s, i))) == check if u: assert nC(len(s), i) == check assert nC(s) == tot if u: assert nC(len(s)) == tot except AssertionError: print(s, i, 'failed combo test') raise ValueError() for i in range(1, 10): tot = 0 for j in range(1, i + 2): check = nT(i, j) tot += check assert sum(1 for p in partitions(i, j, size=True) if p[0] == j) == check assert nT(i) == tot for i in range(1, 10): tot = 0 for j in range(1, i + 2): check = nT(range(i), j) tot += check assert len(list(multiset_partitions(range(i), j))) == check assert nT(range(i)) == tot for i in range(100): s = ''.join(choice(c) for i in range(7)) u = len(s) == len(set(s)) try: tot = 0 for i in range(1, 8): check = nT(s, i) tot += check assert len(list(multiset_partitions(s, i))) == check if u: assert nT(range(len(s)), i) == check if u: assert nT(range(len(s))) == tot assert nT(s) == tot except AssertionError: print(s, i, 'failed partition test') raise ValueError() # tests for Stirling numbers of the first kind that are not tested in the # above assert [stirling(9, i, kind=1) for i in range(11)] == [ 0, 40320, 109584, 118124, 67284, 22449, 4536, 546, 36, 1, 0] perms = list(permutations(range(4))) assert [sum(1 for p in perms if Permutation(p).cycles == i) for i in range(5)] == [0, 6, 11, 6, 1] == [ stirling(4, i, kind=1) for i in range(5)] # http://oeis.org/A008275 assert [stirling(n, k, signed=1) for n in range(10) for k in range(1, n + 1)] == [ 1, -1, 1, 2, -3, 1, -6, 11, -6, 1, 24, -50, 35, -10, 1, -120, 274, -225, 85, -15, 1, 720, -1764, 1624, -735, 175, -21, 1, -5040, 13068, -13132, 6769, -1960, 322, -28, 1, 40320, -109584, 118124, -67284, 22449, -4536, 546, -36, 1] # http://en.wikipedia.org/wiki/Stirling_numbers_of_the_first_kind assert [stirling(n, k, kind=1) for n in range(10) for k in range(n+1)] == [ 1, 0, 1, 0, 1, 1, 0, 2, 3, 1, 0, 6, 11, 6, 1, 0, 24, 50, 35, 10, 1, 0, 120, 274, 225, 85, 15, 1, 0, 720, 1764, 1624, 735, 175, 21, 1, 0, 5040, 13068, 13132, 6769, 1960, 322, 28, 1, 0, 40320, 109584, 118124, 67284, 22449, 4536, 546, 36, 1] # http://en.wikipedia.org/wiki/Stirling_numbers_of_the_second_kind assert [stirling(n, k, kind=2) for n in range(10) for k in range(n+1)] == [ 1, 0, 1, 0, 1, 1, 0, 1, 3, 1, 0, 1, 7, 6, 1, 0, 1, 15, 25, 10, 1, 0, 1, 31, 90, 65, 15, 1, 0, 1, 63, 301, 350, 140, 21, 1, 0, 1, 127, 966, 1701, 1050, 266, 28, 1, 0, 1, 255, 3025, 7770, 6951, 2646, 462, 36, 1] assert stirling(3, 4, kind=1) == stirling(3, 4, kind=1) == 0 raises(ValueError, lambda: stirling(-2, 2)) def delta(p): if len(p) == 1: return oo return min(abs(i[0] - i[1]) for i in subsets(p, 2)) parts = multiset_partitions(range(5), 3) d = 2 assert (sum(1 for p in parts if all(delta(i) >= d for i in p)) == stirling(5, 3, d=d) == 7) # other coverage tests assert nC('abb', 2) == nC('aab', 2) == 2 assert nP(3, 3, replacement=True) == nP('aabc', 3, replacement=True) == 27 assert nP(3, 4) == 0 assert nP('aabc', 5) == 0 assert nC(4, 2, replacement=True) == nC('abcdd', 2, replacement=True) == \ len(list(multiset_combinations('aabbccdd', 2))) == 10 assert nC('abcdd') == sum(nC('abcdd', i) for i in range(6)) == 24 assert nC(list('abcdd'), 4) == 4 assert nT('aaaa') == nT(4) == len(list(partitions(4))) == 5 assert nT('aaab') == len(list(multiset_partitions('aaab'))) == 7 assert nC('aabb'*3, 3) == 4 # aaa, bbb, abb, baa assert dict(_AOP_product((4,1,1,1))) == { 0: 1, 1: 4, 2: 7, 3: 8, 4: 8, 5: 7, 6: 4, 7: 1} # the following was the first t that showed a problem in a previous form of # the function, so it's not as random as it may appear t = (3, 9, 4, 6, 6, 5, 5, 2, 10, 4) assert sum(_AOP_product(t)[i] for i in range(55)) == 58212000 raises(ValueError, lambda: _multiset_histogram({1:'a'})) sympy-0.7.4.1/sympy/functions/combinatorial/tests/test_comb_factorials.py0000644000175000017500000001237712253362407027174 0ustar georgeskgeorgeskfrom sympy import (Symbol, symbols, factorial, factorial2, binomial, rf, ff, gamma, polygamma, EulerGamma, O, pi, nan, oo, simplify, expand_func) from sympy.functions.combinatorial.factorials import subfactorial from sympy.utilities.pytest import XFAIL, raises def test_rf_eval_apply(): x, y = symbols('x,y') assert rf(nan, y) == nan assert rf(x, y) == rf(x, y) assert rf(oo, 0) == 1 assert rf(-oo, 0) == 1 assert rf(oo, 6) == oo assert rf(-oo, 7) == -oo assert rf(oo, -6) == oo assert rf(-oo, -7) == oo assert rf(x, 0) == 1 assert rf(x, 1) == x assert rf(x, 2) == x*(x + 1) assert rf(x, 3) == x*(x + 1)*(x + 2) assert rf(x, 5) == x*(x + 1)*(x + 2)*(x + 3)*(x + 4) assert rf(x, -1) == 1/(x - 1) assert rf(x, -2) == 1/((x - 1)*(x - 2)) assert rf(x, -3) == 1/((x - 1)*(x - 2)*(x - 3)) assert rf(1, 100) == factorial(100) def test_ff_eval_apply(): x, y = symbols('x,y') assert ff(nan, y) == nan assert ff(x, y) == ff(x, y) assert ff(oo, 0) == 1 assert ff(-oo, 0) == 1 assert ff(oo, 6) == oo assert ff(-oo, 7) == -oo assert ff(oo, -6) == oo assert ff(-oo, -7) == oo assert ff(x, 0) == 1 assert ff(x, 1) == x assert ff(x, 2) == x*(x - 1) assert ff(x, 3) == x*(x - 1)*(x - 2) assert ff(x, 5) == x*(x - 1)*(x - 2)*(x - 3)*(x - 4) assert ff(x, -1) == 1/(x + 1) assert ff(x, -2) == 1/((x + 1)*(x + 2)) assert ff(x, -3) == 1/((x + 1)*(x + 2)*(x + 3)) assert ff(100, 100) == factorial(100) def test_factorial(): n = Symbol('n', integer=True) k = Symbol('k', integer=True, positive=True) assert factorial(-2) == 0 assert factorial(0) == 1 assert factorial(7) == 5040 assert factorial(n).func == factorial assert factorial(2*n).func == factorial assert factorial(n).is_integer assert factorial(n).is_positive is None assert factorial(k).is_positive assert factorial(oo) == oo def test_factorial_diff(): n = Symbol('n', integer=True) assert factorial(n).diff(n) == \ gamma(1 + n)*polygamma(0, 1 + n) assert factorial(n**2).diff(n) == \ 2*n*gamma(1 + n**2)*polygamma(0, 1 + n**2) def test_factorial_series(): n = Symbol('n', integer=True) assert factorial(n).series(n, 0, 3) == \ 1 - n*EulerGamma + n**2*(EulerGamma**2/2 + pi**2/12) + O(n**3) def test_factorial_rewrite(): n = Symbol('n', integer=True) assert factorial(n).rewrite(gamma) == gamma(n + 1) def test_factorial2(): n = Symbol('n', integer=True) assert factorial2(-1) == 1 assert factorial2(0) == 1 assert factorial2(7) == 105 assert factorial2(8) == 384 assert factorial2(n).func == factorial2 def test_binomial(): n = Symbol('n', integer=True) k = Symbol('k', integer=True) u = Symbol('v', negative=True) v = Symbol('m', positive=True) assert binomial(0, 0) == 1 assert binomial(1, 1) == 1 assert binomial(10, 10) == 1 assert binomial(1, 2) == 0 assert binomial(1, -1) == 0 assert binomial(-1, 1) == -1 assert binomial(-10, 1) == -10 assert binomial(-10, 7) == -11440 assert binomial(n, -1) == 0 assert binomial(n, 0) == 1 assert expand_func(binomial(n, 1)) == n assert expand_func(binomial(n, 2)) == n*(n - 1)/2 assert expand_func(binomial(n, n - 2)) == n*(n - 1)/2 assert expand_func(binomial(n, n - 1)) == n assert binomial(n, 3).func == binomial assert binomial(n, 3).expand(func=True) == n**3/6 - n**2/2 + n/3 assert expand_func(binomial(n, 3)) == n*(n - 2)*(n - 1)/6 assert binomial(n, n) == 1 assert binomial(n, n + 1) == 0 assert binomial(n, u) == 0 assert binomial(n, v).func == binomial assert binomial(n, k).func == binomial assert binomial(n, n + v) == 0 assert expand_func(binomial(n, n-3)) == n*(n - 2)*(n - 1)/6 def test_binomial_diff(): n = Symbol('n', integer=True) k = Symbol('k', integer=True) assert binomial(n, k).diff(n) == \ (-polygamma(0, 1 + n - k) + polygamma(0, 1 + n))*binomial(n, k) assert binomial(n**2, k**3).diff(n) == \ 2*n*(-polygamma( 0, 1 + n**2 - k**3) + polygamma(0, 1 + n**2))*binomial(n**2, k**3) assert binomial(n, k).diff(k) == \ (-polygamma(0, 1 + k) + polygamma(0, 1 + n - k))*binomial(n, k) assert binomial(n**2, k**3).diff(k) == \ 3*k**2*(-polygamma( 0, 1 + k**3) + polygamma(0, 1 + n**2 - k**3))*binomial(n**2, k**3) def test_binomial_rewrite(): n = Symbol('n', integer=True) k = Symbol('k', integer=True) assert binomial(n, k).rewrite( factorial) == factorial(n)/(factorial(k)*factorial(n - k)) assert binomial( n, k).rewrite(gamma) == gamma(n + 1)/(gamma(k + 1)*gamma(n - k + 1)) @XFAIL def test_factorial_simplify_fail(): # simplify(factorial(x + 1).diff(x) - ((x + 1)*factorial(x)).diff(x))) == 0 from sympy.abc import x assert simplify(x*polygamma(0, x + 1) - x*polygamma(0, x + 2) + polygamma(0, x + 1) - polygamma(0, x + 2) + 1) == 0 def test_subfactorial(): assert all(subfactorial(i) == ans for i, ans in enumerate( [1, 0, 1, 2, 9, 44, 265, 1854, 14833, 133496])) raises(ValueError, lambda: subfactorial(0.1)) raises(ValueError, lambda: subfactorial(-2)) sympy-0.7.4.1/sympy/functions/combinatorial/tests/__init__.py0000644000175000017500000000000012253362407024521 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/functions/combinatorial/__init__.py0000644000175000017500000000005712253362407023373 0ustar georgeskgeorgeskfrom . import factorials from . import numbers sympy-0.7.4.1/sympy/functions/special/0000755000175000017500000000000012253362407020055 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/functions/special/delta_functions.py0000644000175000017500000001550412253362407023615 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import S, sympify, diff from sympy.core.function import Function, ArgumentIndexError from sympy.core.relational import Eq from sympy.polys.polyerrors import PolynomialError from sympy.functions.elementary.complexes import im from sympy.functions.elementary.piecewise import Piecewise ############################################################################### ################################ DELTA FUNCTION ############################### ############################################################################### 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 ========== .. [1] 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 self.func(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 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 += self.func(x - r)/abs(darg.subs(x, r)) else: valid = False break if valid: return result except PolynomialError: pass return self 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 @staticmethod def _latex_no_arg(printer): return r'\delta' ############################################################################### ############################## HEAVISIDE FUNCTION ############################# ############################################################################### 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 ========== .. [1] 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 def _eval_rewrite_as_Piecewise(self, arg): if arg.is_real: return Piecewise((1, arg > 0), (S(1)/2, Eq(arg, 0)), (0, True)) sympy-0.7.4.1/sympy/functions/special/bsplines.py0000644000175000017500000001146412253362407022254 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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: rv = piecewise_fold(d*b2) elif b2 == S.Zero or d == S.Zero: rv = piecewise_fold(c*b1) else: new_args = [] n_intervals = len(b1.args) assert(n_intervals == len(b2.args)) new_args.append((c*b1.args[0].expr, b1.args[0].cond)) for i in range(1, n_intervals - 1): new_args.append(( c*b1.args[i].expr + d*b2.args[i - 1].expr, b1.args[i].cond )) new_args.append((d*b2.args[-2].expr, b2.args[-2].cond)) new_args.append(b2.args[-1]) rv = Piecewise(*new_args) return rv.expand() 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 = 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, 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 = 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 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 = 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)] sympy-0.7.4.1/sympy/functions/special/tensor_functions.py0000644000175000017500000002731512253362407024041 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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, default_sort_key) from sympy.core.compatibility import xrange ############################################################################### ###################### Kronecker Delta, Levi-Civita etc. ###################### ############################################################################### def Eijk(*args, **kwargs): """ Represent the Levi-Civita symbol. This is just compatibility wrapper to ``LeviCivita()``. See Also ======== LeviCivita """ return LeviCivita(*args, **kwargs) 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 xrange(i + 1, n)) / factorial(i) for i in xrange(n)) # converting factorial(i) to int is slightly faster 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) 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 ========== .. [1] http://en.wikipedia.org/wiki/Kronecker_delta """ nargs = 2 is_integer = True @classmethod 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 cls(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 # to make KroneckerDelta canonical # following lines will check if inputs are in order # if not, will return KroneckerDelta with correct order if i is not min(i, j, key=default_sort_key): return cls(j, i) def _eval_power(self, expt): if expt.is_positive: return self if expt.is_negative and not -expt is S.One: return 1/self @property 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 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 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 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 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 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 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 @staticmethod def _latex_no_arg(printer): return r'\delta' sympy-0.7.4.1/sympy/functions/special/gamma_functions.py0000644000175000017500000005234512253362407023612 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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 from sympy.functions.combinatorial.numbers import harmonic from sympy.core.compatibility import xrange ############################################################################### ############################ COMPLETE GAMMA FUNCTION ########################## ############################################################################### 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)!` References ========== .. [1] 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, 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) def _latex(self, printer, exp=None): assert len(self.args) == 1 aa = printer._print(self.args[0]) if exp: return r'\Gamma^{%s}{\left(%s \right)}' % (printer._print(exp), aa) else: return r'\Gamma{\left(%s \right)}' % aa @staticmethod def _latex_no_arg(printer): return r'\Gamma' ############################################################################### ################## LOWER and UPPER INCOMPLETE GAMMA FUNCTIONS ################# ############################################################################### 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 ========== .. [1] Abramowitz, Milton; Stegun, Irene A., eds. (1965), Chapter 6, Section 5, Handbook of Mathematical Functions with Formulas, Graphs, and Mathematical Tables .. [2] 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.is_positive: nx = unpolarify(x) if nx != x: return lowergamma(a, nx) elif a.is_integer and a.is_nonpositive: 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) @staticmethod def _latex_no_arg(printer): return r'\gamma' 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). where `\gamma(s, x)` is the lower incomplete gamma function, :class:`lowergamma`. 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: .. math :: \operatorname{E}_{n}(x) = \int_{1}^{\infty}{\frac{e^{-xt}}{t^n} \, dt} = x^{n-1}\Gamma(1-n,x). 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 ========== .. [1] Abramowitz, Milton; Stegun, Irene A., eds. (1965), Chapter 6, Section 5, Handbook of Mathematical Functions with Formulas, Graphs, and Mathematical Tables .. [2] http://en.wikipedia.org/wiki/Incomplete_gamma_function .. [3] http://en.wikipedia.org/wiki/Exponential_integral#Relation_with_other_functions """ 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) is True: nx = unpolarify(z) if z != nx: return uppergamma(a, nx) elif a.is_integer and (a <= 0) is True: 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 ########################### ############################################################################### class polygamma(Function): r"""The function ``polygamma(n, z)`` returns ``log(gamma(z)).diff(n + 1)`` .. math :: \psi^{(n)}(z) = \frac{d^n}{d x^n} \log \Gamma(z) Examples ======== We can rewrite polygamma functions in terms of harmonic numbers: >>> from sympy import polygamma, harmonic, Symbol >>> x = Symbol("x") >>> polygamma(0, x).rewrite(harmonic) harmonic(x - 1) - EulerGamma >>> polygamma(2, x).rewrite(harmonic) 2*harmonic(x - 1, 3) - 2*zeta(3) >>> ni = Symbol("n", integer=True) >>> polygamma(ni, x).rewrite(harmonic) (-1)**(n + 1)*(-harmonic(x - 1, n + 1) + zeta(n + 1))*factorial(n) See Also ======== gamma, digamma, trigamma References ========== .. [1] http://en.wikipedia.org/wiki/Polygamma_function .. [2] http://functions.wolfram.com/GammaBetaErf/PolyGamma/ .. [3] http://functions.wolfram.com/GammaBetaErf/PolyGamma2/ """ 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) is True: return self.args[0].is_odd def _eval_is_negative(self): if self.args[1].is_positive and (self.args[0] > 0) is True: 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 xrange(1, int(coeff) + 1)]) else: tail = -Add(*[C.Pow( z + i, e) for i in xrange(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 xrange(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): if n >= S.One: return (-1)**(n + 1)*C.factorial(n)*zeta(n + 1, z) else: return self def _eval_rewrite_as_harmonic(self, n, z): if n.is_integer: if n == S.Zero: return harmonic(z - 1) - S.EulerGamma else: return S.NegativeOne**(n+1) * C.factorial(n) * (C.zeta(n+1) - harmonic(z-1, n+1)) def _eval_as_leading_term(self, x): n, z = [a.as_leading_term(x) for a in self.args] o = C.Order(z, x) if n == 0 and o.contains(1/x): return o.getn() * log(x) else: return self.func(n, z) class loggamma(Function): """ The loggamma function is `\log(\Gamma(x))`. References ========== .. [1] http://mathworld.wolfram.com/LogGammaFunction.html """ nargs = 1 def _eval_nseries(self, x, n, logx=None): x0 = self.args[0].limit(x, 0) if x0 is S.Zero: f = self._eval_rewrite_as_intractable(*self.args) return f._eval_nseries(x, n, logx) return super(loggamma, self)._eval_nseries(x, n, logx) 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) 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) 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) 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) sympy-0.7.4.1/sympy/functions/special/hyper.py0000644000175000017500000010213012253362407021553 0ustar georgeskgeorgesk"""Hypergeometric and Meijer G-functions""" from __future__ import print_function, division from sympy.core import S, I, pi, oo, ilcm, Mod, C from sympy.core.function import Function, Derivative, ArgumentIndexError from sympy.core.containers import Tuple from sympy.core.compatibility import reduce from sympy.core.mul import Mul from sympy.functions import (sqrt, exp, log, sin, cos, asin, atan, sinh, cosh, asinh, acosh, atanh, acoth) class TupleArg(Tuple): def limit(self, x, xlim, dir='+'): """ Compute limit x->xlim. """ from sympy.series.limits import limit return TupleArg(*[limit(f, x, xlim, dir) for f in self.args]) # 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 TupleArg(*[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) 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 ========== .. [1] Luke, Y. L. (1969), The Special Functions and Their Approximations, Volume 1 .. [2] 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) def _eval_rewrite_as_Sum(self, ap, bq, z): from sympy.functions import factorial, RisingFactorial, Piecewise n = C.Dummy("n", integer=True) rfap = Tuple(*[RisingFactorial(a, n) for a in ap]) rfbq = Tuple(*[RisingFactorial(b, n) for b in bq]) coeff = Mul(*rfap) / Mul(*rfbq) return Piecewise((C.Sum(coeff * z**n / factorial(n), (n, 0, oo)), self.convergence_statement), (self, True)) @property def argument(self): """ Argument of the hypergeometric function. """ return self.args[2] @property def ap(self): """ Numerator parameters of the hypergeometric function. """ return Tuple(*self.args[0]) @property def bq(self): """ Denominator parameters of the hypergeometric function. """ return Tuple(*self.args[1]) @property def _diffargs(self): return self.ap + self.bq @property def eta(self): """ A quantity related to the convergence of the series. """ return sum(self.ap) - sum(self.bq) @property 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) is True for a in self.ap + self.bq): aints = [a for a in self.ap if a.is_Integer and (a <= 0) is True] bints = [a for a in self.bq if a.is_Integer and (a <= 0) is True] 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 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) def _eval_simplify(self, ratio, measure): from sympy.simplify.hyperexpand import hyperexpand return hyperexpand(self) 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 ========== .. [1] Luke, Y. L. (1969), The Special Functions and Their Approximations, Volume 1 .. [2] 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 TupleArg(_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 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) 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 def argument(self): """ Argument of the Meijer G-function. """ return self.args[2] @property def an(self): """ First set of numerator parameters. """ return Tuple(*self.args[0][0]) @property def ap(self): """ Combined numerator parameters. """ return Tuple(*(self.args[0][0] + self.args[0][1])) @property def aother(self): """ Second set of numerator parameters. """ return Tuple(*self.args[0][1]) @property def bm(self): """ First set of denominator parameters. """ return Tuple(*self.args[1][0]) @property def bq(self): """ Combined denominator parameters. """ return Tuple(*(self.args[1][0] + self.args[1][1])) @property def bother(self): """ Second set of denominator parameters. """ return Tuple(*self.args[1][1]) @property def _diffargs(self): return self.ap + self.bq @property def nu(self): """ A quantity related to the convergence region of the integral, c.f. references. """ return sum(self.bq) - sum(self.ap) @property 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) sympy-0.7.4.1/sympy/functions/special/zeta_functions.py0000644000175000017500000003743012253362407023471 0ustar georgeskgeorgesk""" Riemann zeta and related function. """ from __future__ import print_function, division from sympy.core import Function, S, C, sympify, pi from sympy.core.function import ArgumentIndexError from sympy.core.compatibility import xrange ############################################################################### ###################### LERCH TRANSCENDENT ##################################### ############################################################################### class lerchphi(Function): r""" Lerch transcendent (Lerch phi function). For :math:`\operatorname{Re}(a) > 0`, `|z| < 1` and `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 `\operatorname{Re}(a) \le 0`. Assume now `\operatorname{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 ========== .. [1] Bateman, H.; Erdelyi, A. (1953), Higher Transcendental Functions, Vol. I, New York: McGraw-Hill. Section 1.11. .. [2] http://dlmf.nist.gov/25.14 .. [3] 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 xrange(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 xrange(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 xrange(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 xrange(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 ########################################## ############################################################################### 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 ###################### ############################################################################### class zeta(Function): r""" Hurwitz zeta function (or Riemann zeta function). For `\operatorname{Re}(a) > 0` and `\operatorname{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 `\operatorname{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 `\operatorname{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 ========== .. [1] http://dlmf.nist.gov/25.11 .. [2] 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) >>> zeta(s) 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 class dirichlet_eta(Function): r""" Dirichlet eta function. For `\operatorname{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 ========== .. [1] 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) sympy-0.7.4.1/sympy/functions/special/spherical_harmonics.py0000644000175000017500000002356712253362407024461 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import pi, I from sympy.core.basic import C from sympy.core.singleton import S from sympy.core import Dummy, sympify from sympy.core.function import Function, ArgumentIndexError from sympy.functions import assoc_legendre from sympy.functions.elementary.trigonometric import sin, cos from sympy.functions.elementary.complexes import Abs from sympy.functions.elementary.miscellaneous import sqrt _x = Dummy("x") class Ynm(Function): r""" Spherical harmonics defined as .. math:: Y_n^m(\theta, \varphi) := \sqrt{\frac{(2n+1)(n-m)!}{4\pi(n+m)!}} \exp(i m \varphi) \mathrm{P}_n^m\left(\cos(\theta)\right) Ynm() gives the spherical harmonic function of order `n` and `m` in `\theta` and `\varphi`, `Y_n^m(\theta, \varphi)`. The four parameters are as follows: `n \geq 0` an integer and `m` an integer such that `-n \leq m \leq n` holds. The two angles are real-valued with `\theta \in [0, \pi]` and `\varphi \in [0, 2\pi]`. Examples ======== >>> from sympy import Ynm, Symbol >>> from sympy.abc import n,m >>> theta = Symbol("theta") >>> phi = Symbol("phi") >>> Ynm(n, m, theta, phi) Ynm(n, m, theta, phi) Several symmetries are known, for the order >>> from sympy import Ynm, Symbol >>> from sympy.abc import n,m >>> theta = Symbol("theta") >>> phi = Symbol("phi") >>> Ynm(n, -m, theta, phi) (-1)**m*exp(-2*I*m*phi)*Ynm(n, m, theta, phi) as well as for the angles >>> from sympy import Ynm, Symbol, simplify >>> from sympy.abc import n,m >>> theta = Symbol("theta") >>> phi = Symbol("phi") >>> Ynm(n, m, -theta, phi) Ynm(n, m, theta, phi) >>> Ynm(n, m, theta, -phi) exp(-2*I*m*phi)*Ynm(n, m, theta, phi) For specific integers n and m we can evalute the harmonics to more useful expressions >>> simplify(Ynm(0, 0, theta, phi).expand(func=True)) 1/(2*sqrt(pi)) >>> simplify(Ynm(1, -1, theta, phi).expand(func=True)) sqrt(6)*exp(-I*phi)*sin(theta)/(4*sqrt(pi)) >>> simplify(Ynm(1, 0, theta, phi).expand(func=True)) sqrt(3)*cos(theta)/(2*sqrt(pi)) >>> simplify(Ynm(1, 1, theta, phi).expand(func=True)) -sqrt(6)*exp(I*phi)*sin(theta)/(4*sqrt(pi)) >>> simplify(Ynm(2, -2, theta, phi).expand(func=True)) sqrt(30)*exp(-2*I*phi)*sin(theta)**2/(8*sqrt(pi)) >>> simplify(Ynm(2, -1, theta, phi).expand(func=True)) sqrt(30)*exp(-I*phi)*sin(2*theta)/(8*sqrt(pi)) >>> simplify(Ynm(2, 0, theta, phi).expand(func=True)) sqrt(5)*(3*cos(theta)**2 - 1)/(4*sqrt(pi)) >>> simplify(Ynm(2, 1, theta, phi).expand(func=True)) -sqrt(30)*exp(I*phi)*sin(2*theta)/(8*sqrt(pi)) >>> simplify(Ynm(2, 2, theta, phi).expand(func=True)) sqrt(30)*exp(2*I*phi)*sin(theta)**2/(8*sqrt(pi)) We can differentiate the functions with respect to both angles >>> from sympy import Ynm, Symbol, diff >>> from sympy.abc import n,m >>> theta = Symbol("theta") >>> phi = Symbol("phi") >>> diff(Ynm(n, m, theta, phi), theta) m*cot(theta)*Ynm(n, m, theta, phi) + sqrt((-m + n)*(m + n + 1))*exp(-I*phi)*Ynm(n, m + 1, theta, phi) >>> diff(Ynm(n, m, theta, phi), phi) I*m*Ynm(n, m, theta, phi) Further we can compute the complex conjugation >>> from sympy import Ynm, Symbol, conjugate >>> from sympy.abc import n,m >>> theta = Symbol("theta") >>> phi = Symbol("phi") >>> conjugate(Ynm(n, m, theta, phi)) (-1)**(2*m)*exp(-2*I*m*phi)*Ynm(n, m, theta, phi) To get back the well known expressions in spherical coordinates we use full expansion >>> from sympy import Ynm, Symbol, expand_func >>> from sympy.abc import n,m >>> theta = Symbol("theta") >>> phi = Symbol("phi") >>> expand_func(Ynm(n, m, theta, phi)) sqrt((2*n + 1)*factorial(-m + n)/factorial(m + n))*exp(I*m*phi)*assoc_legendre(n, m, cos(theta))/(2*sqrt(pi)) See Also ======== Ynm_c, Znm References ========== .. [1] http://en.wikipedia.org/wiki/Spherical_harmonics .. [2] http://mathworld.wolfram.com/SphericalHarmonic.html .. [3] http://functions.wolfram.com/Polynomials/SphericalHarmonicY/ .. [4] http://dlmf.nist.gov/14.30 """ nargs = 4 @classmethod def eval(cls, n, m, theta, phi): n, m, theta, phi = [sympify(x) for x in (n, m, theta, phi)] # Handle negative index m and arguments theta, phi if m.could_extract_minus_sign(): m = -m return S.NegativeOne**m * C.exp(-2*I*m*phi) * Ynm(n, m, theta, phi) if theta.could_extract_minus_sign(): theta = -theta return Ynm(n, m, theta, phi) if phi.could_extract_minus_sign(): phi = -phi return C.exp(-2*I*m*phi) * Ynm(n, m, theta, phi) # TODO Add more simplififcation here def _eval_expand_func(self, **hints): n, m, theta, phi = self.args return (sqrt((2*n + 1)/(4*pi) * C.factorial(n - m)/C.factorial(n + m)) * C.exp(I*m*phi) * assoc_legendre(n, m, C.cos(theta))) def fdiff(self, argindex=4): 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 theta n, m, theta, phi = self.args return (m * C.cot(theta) * Ynm(n, m, theta, phi) + sqrt((n - m)*(n + m + 1)) * C.exp(-I*phi) * Ynm(n, m + 1, theta, phi)) elif argindex == 4: # Diff wrt phi n, m, theta, phi = self.args return I * m * Ynm(n, m, theta, phi) else: raise ArgumentIndexError(self, argindex) def _eval_rewrite_as_polynomial(self, n, m, theta, phi): # TODO: Make sure n \in N # TODO: Assert |m| <= n ortherwise we should return 0 return self.expand(func=True) def _eval_rewrite_as_sin(self, n, m, theta, phi): return self.rewrite(cos) def _eval_rewrite_as_cos(self, n, m, theta, phi): # This method can be expensive due to extensive use of simplification! from sympy.simplify import simplify, trigsimp # TODO: Make sure n \in N # TODO: Assert |m| <= n ortherwise we should return 0 term = simplify(self.expand(func=True)) # We can do this because of the range of theta term = term.xreplace({Abs(sin(theta)):sin(theta)}) return simplify(trigsimp(term)) def _eval_conjugate(self): # TODO: Make sure theta \in R and phi \in R n, m, theta, phi = self.args return S.NegativeOne**m * self.func(n, -m, theta, phi) def as_real_imag(self, deep=True, **hints): # TODO: Handle deep and hints n, m, theta, phi = self.args re = (sqrt((2*n + 1)/(4*pi) * C.factorial(n - m)/C.factorial(n + m)) * C.cos(m*phi) * assoc_legendre(n, m, C.cos(theta))) im = (sqrt((2*n + 1)/(4*pi) * C.factorial(n - m)/C.factorial(n + m)) * C.sin(m*phi) * assoc_legendre(n, m, C.cos(theta))) return (re, im) def _eval_evalf(self, prec): # Note: works without this function by just calling # mpmath for Legendre polynomials. But using # the dedicated function directly is cleaner. from sympy.mpmath import mp from sympy import Expr n = self.args[0]._to_mpmath(prec) m = self.args[1]._to_mpmath(prec) theta = self.args[2]._to_mpmath(prec) phi = self.args[3]._to_mpmath(prec) oprec = mp.prec mp.prec = prec res = mp.spherharm(n, m, theta, phi) mp.prec = oprec return Expr._from_mpmath(res, prec) def Ynm_c(n, m, theta, phi): r"""Conjugate spherical harmonics defined as .. math:: \overline{Y_n^m(\theta, \varphi)} := (-1)^m Y_n^{-m}(\theta, \varphi) See Also ======== Ynm, Znm References ========== .. [1] http://en.wikipedia.org/wiki/Spherical_harmonics .. [2] http://mathworld.wolfram.com/SphericalHarmonic.html .. [3] http://functions.wolfram.com/Polynomials/SphericalHarmonicY/ """ from sympy import conjugate return conjugate(Ynm(n, m, theta, phi)) class Znm(Function): r""" Real spherical harmonics defined as .. math:: Z_n^m(\theta, \varphi) := \begin{cases} \frac{Y_n^m(\theta, \varphi) + \overline{Y_n^m(\theta, \varphi)}}{\sqrt{2}} &\quad m > 0 \\ Y_n^m(\theta, \varphi) &\quad m = 0 \\ \frac{Y_n^m(\theta, \varphi) - \overline{Y_n^m(\theta, \varphi)}}{i \sqrt{2}} &\quad m < 0 \\ \end{cases} which gives in simplified form .. math:: Z_n^m(\theta, \varphi) = \begin{cases} \frac{Y_n^m(\theta, \varphi) + (-1)^m Y_n^{-m}(\theta, \varphi)}{\sqrt{2}} &\quad m > 0 \\ Y_n^m(\theta, \varphi) &\quad m = 0 \\ \frac{Y_n^m(\theta, \varphi) - (-1)^m Y_n^{-m}(\theta, \varphi)}{i \sqrt{2}} &\quad m < 0 \\ \end{cases} See Also ======== Ynm, Ynm_c References ========== .. [1] http://en.wikipedia.org/wiki/Spherical_harmonics .. [2] http://mathworld.wolfram.com/SphericalHarmonic.html .. [3] http://functions.wolfram.com/Polynomials/SphericalHarmonicY/ """ nargs = 4 @classmethod def eval(cls, n, m, theta, phi): n, m, th, ph = [sympify(x) for x in (n, m, theta, phi)] if m.is_positive: zz = (Ynm(n, m, th, ph) + Ynm_c(n, m, th, ph)) / sqrt(2) #zz = zz.expand(complex=True) #zz = simplify(zz) return zz elif m.is_zero: return Ynm(n, m, th, ph) elif m.is_negative: zz = (Ynm(n, m, th, ph) - Ynm_c(n, m, th, ph)) / (sqrt(2)*I) #zz = zz.expand(complex=True) #zz = simplify(zz) return zz sympy-0.7.4.1/sympy/functions/special/benchmarks/0000755000175000017500000000000012253362407022172 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/functions/special/benchmarks/bench_special.py0000644000175000017500000000031112253362407025316 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import symbols from sympy.functions.special.spherical_harmonics import Ynm x, y = symbols('x,y') def timeit_Ynm_xy(): Ynm(1, 1, x, y) sympy-0.7.4.1/sympy/functions/special/elliptic_integrals.py0000644000175000017500000002702712253362407024314 0ustar georgeskgeorgesk""" Elliptic integrals. """ from __future__ import print_function, division from sympy.core import S, pi, I from sympy.core.function import Function, ArgumentIndexError from sympy.functions.elementary.hyperbolic import atanh from sympy.functions.elementary.trigonometric import sin, tan from sympy.functions.elementary.miscellaneous import sqrt from sympy.functions.elementary.complexes import sign from sympy.functions.special.hyper import hyper, meijerg from sympy.functions.special.gamma_functions import gamma class elliptic_k(Function): r""" The complete elliptic integral of the first kind, defined by .. math:: K(z) = F\left(\tfrac{\pi}{2}\middle| z\right) where `F\left(z\middle| m\right)` is the Legendre incomplete elliptic integral of the first kind. The function `K(z)` is a single-valued function on the complex plane with branch cut along the interval `(1, \infty)`. Examples ======== >>> from sympy import elliptic_k, I, pi >>> from sympy.abc import z >>> elliptic_k(0) pi/2 >>> elliptic_k(1.0 + I) 1.50923695405127 + 0.625146415202697*I >>> elliptic_k(z).series(z, n=3) pi/2 + pi*z/8 + 9*pi*z**2/128 + O(z**3) References ========== .. [1] http://en.wikipedia.org/wiki/Elliptic_integrals .. [2] http://functions.wolfram.com/EllipticIntegrals/EllipticK See Also ======== elliptic_f """ nargs = 1 @classmethod def eval(cls, z): if z is S.Zero: return pi/2 elif z is S.Half: return 8*pi**(S(3)/2)/gamma(-S(1)/4)**2 elif z is S.One: return S.ComplexInfinity elif z is S.NegativeOne: return gamma(S(1)/4)**2/(4*sqrt(2*pi)) elif z in (S.Infinity, S.NegativeInfinity, I*S.Infinity, I*S.NegativeInfinity, S.ComplexInfinity): return S.Zero def fdiff(self, argindex=1): z = self.args[0] return (elliptic_e(z) - (1 - z)*elliptic_k(z))/(2*z*(1 - z)) def _eval_conjugate(self): z = self.args[0] if (z.is_real and (z - 1).is_positive) is False: return self.func(z.conjugate()) def _eval_nseries(self, x, n, logx): from sympy.simplify import hyperexpand return hyperexpand(self.rewrite(hyper)._eval_nseries(x, n=n, logx=logx)) def _eval_rewrite_as_hyper(self, z): return (pi/2)*hyper((S.Half, S.Half), (S.One,), z) def _eval_rewrite_as_meijerg(self, z): return meijerg(((S.Half, S.Half), []), ((S.Zero,), (S.Zero,)), -z)/2 class elliptic_f(Function): r""" The Legendre incomplete elliptic integral of the first kind, defined by .. math:: F\left(z\middle| m\right) = \int_0^z \frac{dt}{\sqrt{1 - m \sin^2 t}} This function reduces to a complete elliptic integral of the first kind, `K(m)`, when `z = \pi/2`. Examples ======== >>> from sympy import elliptic_f, I, O >>> from sympy.abc import z, m >>> elliptic_f(z, m).series(z) z + z**5*(3*m**2/40 - m/30) + m*z**3/6 + O(z**6) >>> elliptic_f(3.0 + I/2, 1.0 + I) 2.909449841483 + 1.74720545502474*I References ========== .. [1] http://en.wikipedia.org/wiki/Elliptic_integrals .. [2] http://functions.wolfram.com/EllipticIntegrals/EllipticF See Also ======== elliptic_k """ nargs = 2 @classmethod def eval(cls, z, m): k = 2*z/pi if m.is_zero: return z elif z.is_zero: return S.Zero elif k.is_integer: return k*elliptic_k(m) elif m in (S.Infinity, S.NegativeInfinity): return S.Zero elif z.could_extract_minus_sign(): return -elliptic_f(-z, m) def fdiff(self, argindex=1): z, m = self.args fm = sqrt(1 - m*sin(z)**2) if argindex == 1: return 1/fm elif argindex == 2: return (elliptic_e(z, m)/(2*m*(1 - m)) - elliptic_f(z, m)/(2*m) - sin(2*z)/(4*(1 - m)*fm)) raise ArgumentIndexError(self, argindex) def _eval_conjugate(self): z, m = self.args if (m.is_real and (m - 1).is_positive) is False: return self.func(z.conjugate(), m.conjugate()) class elliptic_e(Function): r""" Called with two arguments `z` and `m`, evaluates the incomplete elliptic integral of the second kind, defined by .. math:: E\left(z\middle| m\right) = \int_0^z \sqrt{1 - m \sin^2 t} dt Called with a single argument `z`, evaluates the Legendre complete elliptic integral of the second kind .. math:: E(z) = E\left(\tfrac{\pi}{2}\middle| z\right) The function `E(z)` is a single-valued function on the complex plane with branch cut along the interval `(1, \infty)`. Examples ======== >>> from sympy import elliptic_e, I, pi, O >>> from sympy.abc import z, m >>> elliptic_e(z, m).series(z) z + z**5*(-m**2/40 + m/30) - m*z**3/6 + O(z**6) >>> elliptic_e(z).series(z, n=4) pi/2 - pi*z/8 - 3*pi*z**2/128 - 5*pi*z**3/512 + O(z**4) >>> elliptic_e(1 + I, 2 - I/2).n() 1.55203744279187 + 0.290764986058437*I >>> elliptic_e(0) pi/2 >>> elliptic_e(2.0 - I) 0.991052601328069 + 0.81879421395609*I References ========== .. [1] http://en.wikipedia.org/wiki/Elliptic_integrals .. [2] http://functions.wolfram.com/EllipticIntegrals/EllipticE2 .. [3] http://functions.wolfram.com/EllipticIntegrals/EllipticE """ nargs = (1, 2) @classmethod def eval(cls, *args): if len(args) == 2: z, m = args k = 2*z/pi if m.is_zero: return z if z.is_zero: return S.Zero elif k.is_integer: return k*elliptic_e(m) elif m in (S.Infinity, S.NegativeInfinity): return S.ComplexInfinity elif z.could_extract_minus_sign(): return -elliptic_e(-z, m) else: z = args[0] if z.is_zero: return pi/2 elif z is S.One: return S.One elif z is S.Infinity: return I*S.Infinity elif z is S.NegativeInfinity: return S.Infinity elif z is S.ComplexInfinity: return S.ComplexInfinity def fdiff(self, argindex=1): if len(self.args) == 2: z, m = self.args if argindex == 1: return sqrt(1 - m*sin(z)**2) elif argindex == 2: return (elliptic_e(z, m) - elliptic_f(z, m))/(2*m) else: z = self.args[0] if argindex == 1: return (elliptic_e(z) - elliptic_k(z))/(2*z) raise ArgumentIndexError(self, argindex) def _eval_conjugate(self): z, m = self.args if (m.is_real and (m - 1).is_positive) is False: return self.func(z.conjugate(), m.conjugate()) def _eval_nseries(self, x, n, logx): from sympy.simplify import hyperexpand if len(self.args) == 1: return hyperexpand(self.rewrite(hyper)._eval_nseries(x, n=n, logx=logx)) return super(elliptic_e, self)._eval_nseries(x, n=n, logx=logx) def _eval_rewrite_as_hyper(self, *args): if len(args) == 1: z = args[0] return (pi/2)*hyper((-S.Half, S.Half), (S.One,), z) def _eval_rewrite_as_meijerg(self, *args): if len(args) == 1: z = args[0] return -meijerg(((S.Half, S(3)/2), []), \ ((S.Zero,), (S.Zero,)), -z)/4 class elliptic_pi(Function): r""" Called with three arguments `n`, `z` and `m`, evaluates the Legendre incomplete elliptic integral of the third kind, defined by .. math:: \Pi\left(n; z\middle| m\right) = \int_0^z \frac{dt} {\left(1 - n \sin^2 t\right) \sqrt{1 - m \sin^2 t}} Called with two arguments `n` and `m`, evaluates the complete elliptic integral of the third kind: .. math:: \Pi\left(n\middle| m\right) = \Pi\left(n; \tfrac{\pi}{2}\middle| m\right) Examples ======== >>> from sympy import elliptic_pi, I, pi, O, S >>> from sympy.abc import z, n, m >>> elliptic_pi(n, z, m).series(z, n=4) z + z**3*(m/6 + n/3) + O(z**4) >>> elliptic_pi(0.5 + I, 1.0 - I, 1.2) 2.50232379629182 - 0.760939574180767*I >>> elliptic_pi(0, 0) pi/2 >>> elliptic_pi(1.0 - I/3, 2.0 + I) 3.29136443417283 + 0.32555634906645*I References ========== .. [1] http://en.wikipedia.org/wiki/Elliptic_integrals .. [2] http://functions.wolfram.com/EllipticIntegrals/EllipticPi3 .. [3] http://functions.wolfram.com/EllipticIntegrals/EllipticPi """ nargs = (2, 3) @classmethod def eval(cls, *args): if len(args) == 3: n, z, m = args k = 2*z/pi if n == S.Zero: return elliptic_f(z, m) elif n == S.One: return (elliptic_f(z, m) + (sqrt(1 - m*sin(z)**2)*tan(z) - elliptic_e(z, m))/(1 - m)) elif k.is_integer: return k*elliptic_pi(n, m) elif m == S.Zero: return atanh(sqrt(n - 1)*tan(z))/sqrt(n - 1) elif n == m: return (elliptic_f(z, n) - elliptic_pi(1, z, n) + tan(z)/sqrt(1 - n*sin(z)**2)) elif n in (S.Infinity, S.NegativeInfinity): return S.Zero elif m in (S.Infinity, S.NegativeInfinity): return S.Zero elif z.could_extract_minus_sign(): return -elliptic_pi(n, -z, m) else: n, m = args if n == S.Zero: return elliptic_k(m) elif n == S.One: return S.ComplexInfinity elif m == S.Zero: return pi/(2*sqrt(1 - n)) elif m == S.One: return -S.Infinity/sign(n - 1) elif n == m: return elliptic_e(n)/(1 - n) elif n in (S.Infinity, S.NegativeInfinity): return S.Zero elif m in (S.Infinity, S.NegativeInfinity): return S.Zero def _eval_conjugate(self): if len(self.args) == 3: n, z, m = self.args if (n.is_real and (n - 1).is_positive) is False and \ (m.is_real and (m - 1).is_positive) is False: return self.func(n.conjugate(), z.conjugate(), m.conjugate()) else: n, m = self.args return self.func(n.conjugate(), m.conjugate()) def fdiff(self, argindex=1): if len(self.args) == 3: n, z, m = self.args fm, fn = sqrt(1 - m*sin(z)**2), 1 - n*sin(z)**2 if argindex == 1: return (elliptic_e(z, m) + (m - n)*elliptic_f(z, m)/n + (n**2 - m)*elliptic_pi(n, z, m)/n - n*fm*sin(2*z)/(2*fn))/(2*(m - n)*(n - 1)) elif argindex == 2: return 1/(fm*fn) elif argindex == 3: return (elliptic_e(z, m)/(m - 1) + elliptic_pi(n, z, m) - m*sin(2*z)/(2*(m - 1)*fm))/(2*(n - m)) else: n, m = self.args if argindex == 1: return (elliptic_e(m) + (m - n)*elliptic_k(m)/n + (n**2 - m)*elliptic_pi(n, m)/n)/(2*(m - n)*(n - 1)) elif argindex == 2: return (elliptic_e(m)/(m - 1) + elliptic_pi(n, m))/(2*(n - m)) raise ArgumentIndexError(self, argindex) sympy-0.7.4.1/sympy/functions/special/bessel.py0000644000175000017500000005061112253362407021707 0ustar georgeskgeorgesk"""Bessel type functions""" from __future__ import print_function, division from sympy import S, pi, I from sympy.core.function import Function, ArgumentIndexError, expand_func from sympy.functions.elementary.trigonometric import sin, cos, csc, cot from sympy.functions.elementary.miscellaneous import sqrt from sympy.functions.elementary.complexes import re, im from sympy.core.compatibility import xrange # 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). 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 def order(self): """ The order of the bessel-type function. """ return self.args[0] @property 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) \ def _eval_conjugate(self): z = self.argument if (z.is_real and z.is_negative) is False: return self.__class__(self.order.conjugate(), z.conjugate()) def _eval_expand_func(self, **hints): nu, z, f = self.order, self.argument, self.__class__ if nu.is_real: if (nu - 1).is_positive: return (-self._a*self._b*f(nu - 2, z)._eval_expand_func() + 2*self._a*(nu - 1)*f(nu - 1, z)._eval_expand_func()/z) elif (nu + 1).is_negative: return (2*self._b*(nu + 1)*f(nu + 1, z)._eval_expand_func()/z - self._a*self._b*f(nu + 2, z)._eval_expand_func()) return self def _eval_simplify(self, ratio, measure): from sympy.simplify.simplify import besselsimp return besselsimp(self) class besselj(BesselBase): r""" Bessel function of the first kind. The Bessel `J` function of order `\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 ========== .. [1] Abramowitz, Milton; Stegun, Irene A., eds. (1965), "Chapter 9", Handbook of Mathematical Functions with Formulas, Graphs, and Mathematical Tables .. [2] Luke, Y. L. (1969), The Special Functions and Their Approximations, Volume 1 .. [3] http://en.wikipedia.org/wiki/Bessel_function .. [4] http://functions.wolfram.com/Bessel-TypeFunctions/BesselJ/ """ _a = S.One _b = S.One @classmethod def eval(cls, nu, z): if z.is_zero: if nu.is_zero: return S.One elif (nu.is_integer and nu.is_zero is False) or re(nu).is_positive: return S.Zero elif re(nu).is_negative and not (nu.is_integer is True): return S.ComplexInfinity elif nu.is_imaginary: return S.NaN if z is S.Infinity or (z is S.NegativeInfinity): return S.Zero if z.could_extract_minus_sign(): return (z)**nu*(-z)**(-nu)*besselj(nu, -z) if nu.is_integer: if nu.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_rewrite_as_besseli(self, nu, z): from sympy import polar_lift, exp return exp(I*pi*nu/2)*besseli(nu, polar_lift(-I)*z) def _eval_rewrite_as_bessely(self, nu, z): if nu.is_integer is False: return csc(pi*nu)*bessely(-nu, z) - cot(pi*nu)*bessely(nu, z) def _eval_rewrite_as_jn(self, nu, z): return sqrt(2*z/pi)*jn(nu - S.Half, self.argument) def _eval_is_real(self): nu, z = self.args if nu.is_integer and z.is_real: return True class bessely(BesselBase): r""" Bessel function of the second kind. The Bessel `Y` function of order `\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 References ========== .. [1] http://functions.wolfram.com/Bessel-TypeFunctions/BesselY/ """ _a = S.One _b = S.One @classmethod def eval(cls, nu, z): if z.is_zero: if nu.is_zero: return S.NegativeInfinity elif re(nu).is_zero is False: return S.ComplexInfinity elif re(nu).is_zero: return S.NaN if z is S.Infinity or z is S.NegativeInfinity: return S.Zero if nu.is_integer: if nu.could_extract_minus_sign(): return S(-1)**(-nu)*bessely(-nu, z) def _eval_rewrite_as_besselj(self, nu, z): if nu.is_integer is False: return csc(pi*nu)*(cos(pi*nu)*besselj(nu, z) - besselj(-nu, z)) def _eval_rewrite_as_besseli(self, nu, z): aj = self._eval_rewrite_as_besselj(*self.args) if aj: return aj.rewrite(besseli) def _eval_rewrite_as_yn(self, nu, z): return sqrt(2*z/pi) * yn(nu - S.Half, self.argument) def _eval_is_real(self): nu, z = self.args if nu.is_integer and z.is_positive: return True 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_\nu(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 References ========== .. [1] http://functions.wolfram.com/Bessel-TypeFunctions/BesselI/ """ _a = -S.One _b = S.One @classmethod def eval(cls, nu, z): if z.is_zero: if nu.is_zero: return S.One elif (nu.is_integer and nu.is_zero is False) or re(nu).is_positive: return S.Zero elif re(nu).is_negative and not (nu.is_integer is True): return S.ComplexInfinity elif nu.is_imaginary: return S.NaN if z.is_imaginary: if im(z) is S.Infinity or im(z) is S.NegativeInfinity: return S.Zero if z.could_extract_minus_sign(): return (z)**nu*(-z)**(-nu)*besseli(nu, -z) if nu.is_integer: if nu.could_extract_minus_sign(): return besseli(-nu, z) 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) def _eval_rewrite_as_bessely(self, nu, z): aj = self._eval_rewrite_as_besselj(*self.args) if aj: return aj.rewrite(bessely) def _eval_rewrite_as_jn(self, nu, z): return self._eval_rewrite_as_besselj(*self.args).rewrite(jn) def _eval_is_real(self): nu, z = self.args if nu.is_integer and z.is_real: return True 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 References ========== .. [1] http://functions.wolfram.com/Bessel-TypeFunctions/BesselK/ """ _a = S.One _b = -S.One @classmethod def eval(cls, nu, z): if z.is_zero: if nu.is_zero: return S.Infinity elif re(nu).is_zero is False: return S.ComplexInfinity elif re(nu).is_zero: return S.NaN if z.is_imaginary: if im(z) is S.Infinity or im(z) is S.NegativeInfinity: return S.Zero if nu.is_integer: if nu.could_extract_minus_sign(): return besselk(-nu, z) def _eval_rewrite_as_besseli(self, nu, z): if nu.is_integer is False: return pi*csc(pi*nu)*(besseli(-nu, z) - besseli(nu, z))/2 def _eval_rewrite_as_besselj(self, nu, z): ai = self._eval_rewrite_as_besseli(*self.args) if ai: return ai.rewrite(besselj) def _eval_rewrite_as_bessely(self, nu, z): aj = self._eval_rewrite_as_besselj(*self.args) if aj: return aj.rewrite(bessely) def _eval_rewrite_as_yn(self, nu, z): ay = self._eval_rewrite_as_bessely(*self.args) if ay: return ay.rewrite(yn) def _eval_is_real(self): nu, z = self.args if nu.is_integer and z.is_positive: return True 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 References ========== .. [1] http://functions.wolfram.com/Bessel-TypeFunctions/HankelH1/ """ _a = S.One _b = S.One def _eval_conjugate(self): z = self.argument if (z.is_real and z.is_negative) is False: return hankel2(self.order.conjugate(), z.conjugate()) 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 References ========== .. [1] http://functions.wolfram.com/Bessel-TypeFunctions/HankelH2/ """ _a = S.One _b = S.One def _eval_conjugate(self): z = self.argument if (z.is_real and z.is_negative) is False: return hankel1(self.order.conjugate(), z.conjugate()) 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 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) 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)) 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 :func:`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 xrange(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 sympy-0.7.4.1/sympy/functions/special/polynomials.py0000644000175000017500000011354112253362407023002 0ustar georgeskgeorgesk""" This module mainly implements special orthogonal polynomials. See also functions.combinatorial.numbers which contains some combinatorial polynomials. """ from __future__ import print_function, division 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.functions.special.gamma_functions import gamma from sympy.functions.combinatorial.factorials import factorial 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 # 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)/factorial(n + 1) >>> jacobi(n, -S(1)/2, -S(1)/2, x) RisingFactorial(1/2, n)*chebyshevt(n, x)/factorial(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)/(factorial(n)*gamma(a + 1)) >>> jacobi(n, a, b, 1) RisingFactorial(a + 1, n)/factorial(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()) def jacobi_normalized(n, a, b, x): r""" Jacobi polynomial :math:`P_n^{\left(\alpha, \beta\right)}(x)` jacobi_normalized(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`. This functions returns the polynomials normilzed: .. math:: \int_{-1}^{1} P_m^{\left(\alpha, \beta\right)}(x) P_n^{\left(\alpha, \beta\right)}(x) (1-x)^{\alpha} (1+x)^{\beta} \mathrm{d}x = \delta_{m,n} Examples ======== >>> from sympy import jacobi_normalized >>> from sympy.abc import n,a,b,x >>> jacobi_normalized(n, a, b, x) jacobi(n, a, b, x)/sqrt(2**(a + b + 1)*gamma(a + n + 1)*gamma(b + n + 1)/((a + b + 2*n + 1)*factorial(n)*gamma(a + b + n + 1))) 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/ """ nfactor = (S(2)**(a + b + 1) * (gamma(n + a + 1) * gamma(n + b + 1)) / (2*n + a + b + 1) / (factorial(n) * gamma(n + a + b + 1))) return jacobi(n, a, b, x) / sqrt(nfactor) #---------------------------------------------------------------------------- # Gegenbauer polynomials # 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) is True: 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 None # 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 # 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))) 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))) 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) is (k < n) is True): raise ValueError("must have 0 <= k < n, " "got k = %s and n = %s" % (k, n)) return C.cos(S.Pi*(2*k + 1)/(2*n)) 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) is (k < n) is True): raise ValueError("must have 0 <= k < n, " "got k = %s and n = %s" % (k, n)) return C.cos(S.Pi*(k + 1)/(n + 1)) #---------------------------------------------------------------------------- # Legendre polynomials and Associated Legendre polynomials # 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)) 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 # 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 # 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)) 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)) sympy-0.7.4.1/sympy/functions/special/error_functions.py0000644000175000017500000017553612253362407023671 0ustar georgeskgeorgesk""" This module contains various functions that are special cases of incomplete gamma functions. It should probably be renamed. """ from __future__ import print_function, division 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.exponential import exp, log from sympy.functions.elementary.complexes import polar_lift from sympy.functions.special.hyper import hyper, meijerg from sympy.core.compatibility import xrange # TODO series expansions # TODO see the "Note:" in Ei ############################################################################### ################################ ERROR FUNCTION ############################### ############################################################################### class erf(Function): r""" 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}t. 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 See Also ======== erfc: Complementary error function. erfi: Imaginary error function. erf2: Two-argument error function. erfinv: Inverse error function. erfcinv: Inverse Complementary error function. erf2inv: Inverse two-argument error function. 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 if arg.func is erfinv: return arg.args[0] if arg.func is erfcinv: return S.One - arg.args[0] if arg.func is erf2inv and arg.args[0] is S.Zero: return arg.args[1] # Try to pull out factors of I t = arg.extract_multiplicatively(S.ImaginaryUnit) if t is S.Infinity or t is S.NegativeInfinity: return arg # Try to pull out factors of -1 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_fresnels(self, z): arg = (S.One - S.ImaginaryUnit)*z/sqrt(pi) return (S.One + S.ImaginaryUnit)*(fresnelc(arg) - I*fresnels(arg)) def _eval_rewrite_as_fresnelc(self, z): arg = (S.One - S.ImaginaryUnit)*z/sqrt(pi) return (S.One + S.ImaginaryUnit)*(fresnelc(arg) - I*fresnels(arg)) def _eval_rewrite_as_meijerg(self, z): return z/sqrt(pi)*meijerg([S.Half], [], [0], [-S.Half], z**2) def _eval_rewrite_as_hyper(self, z): return 2*z/sqrt(pi)*hyper([S.Half], [3*S.Half], -z**2) def _eval_rewrite_as_expint(self, z): return sqrt(z**2)/z - z*expint(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_rewrite_as_erfc(self, z): return S.One - erfc(z) def _eval_rewrite_as_erfi(self, z): return -I*erfi(I*z) 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) 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: x, y = self.args[0].expand(deep, **hints).as_real_imag() else: x, y = self.args[0].as_real_imag() 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) class erfc(Function): r""" Complementary Error Function. The function is defined as: .. math :: \mathrm{erfc}(x) = \frac{2}{\sqrt{\pi}} \int_x^\infty e^{-t^2} \mathrm{d}t Examples ======== >>> from sympy import I, oo, erfc >>> from sympy.abc import z Several special values are known: >>> erfc(0) 1 >>> erfc(oo) 0 >>> erfc(-oo) 2 >>> erfc(I*oo) -oo*I >>> erfc(-I*oo) oo*I The error function obeys the mirror symmetry: >>> from sympy import conjugate >>> conjugate(erfc(z)) erfc(conjugate(z)) Differentiation with respect to z is supported: >>> from sympy import diff >>> diff(erfc(z), z) -2*exp(-z**2)/sqrt(pi) It also follows >>> erfc(-z) -erfc(z) + 2 We can numerically evaluate the complementary error function to arbitrary precision on the whole complex plane: >>> erfc(4).evalf(30) 0.0000000154172579002800188521596734869 >>> erfc(4*I).evalf(30) 1.0 - 1296959.73071763923152794095062*I See Also ======== erf: Gaussian error function. erfi: Imaginary error function. erf2: Two-argument error function. erfinv: Inverse error function. erfcinv: Inverse Complementary error function. erf2inv: Inverse two-argument error function. References ========== .. [1] http://en.wikipedia.org/wiki/Error_function .. [2] http://dlmf.nist.gov/7 .. [3] http://mathworld.wolfram.com/Erfc.html .. [4] http://functions.wolfram.com/GammaBetaErf/Erfc """ 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.Zero elif arg is S.Zero: return S.One if arg.func is erfinv: return S.One - arg.args[0] if arg.func is erfcinv: return arg.args[0] # Try to pull out factors of I t = arg.extract_multiplicatively(S.ImaginaryUnit) if t is S.Infinity or t is S.NegativeInfinity: return -arg # Try to pull out factors of -1 if arg.could_extract_minus_sign(): return S(2) - cls(-arg) @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n == 0: return S.One elif 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_tractable(self, z): return self.rewrite(erf).rewrite("tractable", deep=True) def _eval_rewrite_as_erf(self, z): return S.One - erf(z) def _eval_rewrite_as_erfi(self, z): return S.One + I*erfi(I*z) def _eval_rewrite_as_fresnels(self, z): arg = (S.One - S.ImaginaryUnit)*z/sqrt(pi) return S.One - (S.One + S.ImaginaryUnit)*(fresnelc(arg) - I*fresnels(arg)) def _eval_rewrite_as_fresnelc(self, z): arg = (S.One-S.ImaginaryUnit)*z/sqrt(pi) return S.One - (S.One + S.ImaginaryUnit)*(fresnelc(arg) - I*fresnels(arg)) def _eval_rewrite_as_meijerg(self, z): return S.One - z/sqrt(pi)*meijerg([S.Half], [], [0], [-S.Half], z**2) def _eval_rewrite_as_hyper(self, z): return S.One - 2*z/sqrt(pi)*hyper([S.Half], [3*S.Half], -z**2) def _eval_rewrite_as_uppergamma(self, z): return S.One - sqrt(z**2)/z*(S.One - C.uppergamma(S.Half, z**2)/sqrt(S.Pi)) def _eval_rewrite_as_expint(self, z): return S.One - sqrt(z**2)/z + z*expint(S.Half, z**2)/sqrt(S.Pi) 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 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: x, y = self.args[0].expand(deep, **hints).as_real_imag() else: x, y = self.args[0].as_real_imag() 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) class erfi(Function): r""" Imaginary error function. The function erfi is defined as: .. math :: \mathrm{erfi}(x) = \frac{2}{\sqrt{\pi}} \int_0^x e^{t^2} \mathrm{d}t Examples ======== >>> from sympy import I, oo, erfi >>> from sympy.abc import z Several special values are known: >>> erfi(0) 0 >>> erfi(oo) oo >>> erfi(-oo) -oo >>> erfi(I*oo) I >>> erfi(-I*oo) -I In general one can pull out factors of -1 and I from the argument: >>> erfi(-z) -erfi(z) >>> from sympy import conjugate >>> conjugate(erfi(z)) erfi(conjugate(z)) Differentiation with respect to z is supported: >>> from sympy import diff >>> diff(erfi(z), z) 2*exp(z**2)/sqrt(pi) We can numerically evaluate the imaginary error function to arbitrary precision on the whole complex plane: >>> erfi(2).evalf(30) 18.5648024145755525987042919132 >>> erfi(-2*I).evalf(30) -0.995322265018952734162069256367*I See Also ======== erf: Gaussian error function. erfc: Complementary error function. erf2: Two-argument error function. erfinv: Inverse error function. erfcinv: Inverse Complementary error function. erf2inv: Inverse two-argument error function. References ========== .. [1] http://en.wikipedia.org/wiki/Error_function .. [2] http://mathworld.wolfram.com/Erfi.html .. [3] http://functions.wolfram.com/GammaBetaErf/Erfi """ 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, z): if z.is_Number: if z is S.NaN: return S.NaN elif z is S.Zero: return S.Zero elif z is S.Infinity: return S.Infinity # Try to pull out factors of -1 if z.could_extract_minus_sign(): return -cls(-z) # Try to pull out factors of I nz = z.extract_multiplicatively(I) if nz is not None: if nz is S.Infinity: return I if nz.func is erfinv: return I*nz.args[0] if nz.func is erfcinv: return I*(S.One - nz.args[0]) if nz.func is erf2inv and nz.args[0] is S.Zero: return I*nz.args[1] @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 * 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_tractable(self, z): return self.rewrite(erf).rewrite("tractable", deep=True) def _eval_rewrite_as_erf(self, z): return -I*erf(I*z) def _eval_rewrite_as_erfc(self, z): return I*erfc(I*z) - I def _eval_rewrite_as_fresnels(self, z): arg = (S.One + S.ImaginaryUnit)*z/sqrt(pi) return (S.One - S.ImaginaryUnit)*(fresnelc(arg) - I*fresnels(arg)) def _eval_rewrite_as_fresnelc(self, z): arg = (S.One + S.ImaginaryUnit)*z/sqrt(pi) return (S.One - S.ImaginaryUnit)*(fresnelc(arg) - I*fresnels(arg)) def _eval_rewrite_as_meijerg(self, z): return z/sqrt(pi)*meijerg([S.Half], [], [0], [-S.Half], -z**2) def _eval_rewrite_as_hyper(self, z): return 2*z/sqrt(pi)*hyper([S.Half], [3*S.Half], z**2) def _eval_rewrite_as_uppergamma(self, z): return sqrt(-z**2)/z*(C.uppergamma(S.Half, -z**2)/sqrt(S.Pi) - S.One) def _eval_rewrite_as_expint(self, z): return sqrt(-z**2)/z - z*expint(S.Half, -z**2)/sqrt(S.Pi) 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: x, y = self.args[0].expand(deep, **hints).as_real_imag() else: x, y = self.args[0].as_real_imag() 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) class erf2(Function): r""" Two-argument error function. This function is defined as: .. math :: \mathrm{erf2}(x, y) = \frac{2}{\sqrt{\pi}} \int_x^y e^{-t^2} \mathrm{d}t Examples ======== >>> from sympy import I, oo, erf2 >>> from sympy.abc import x, y Several special values are known: >>> erf2(0, 0) 0 >>> erf2(x, x) 0 >>> erf2(x, oo) -erf(x) + 1 >>> erf2(x, -oo) -erf(x) - 1 >>> erf2(oo, y) erf(y) - 1 >>> erf2(-oo, y) erf(y) + 1 In general one can pull out factors of -1: >>> erf2(-x, -y) -erf2(x, y) The error function obeys the mirror symmetry: >>> from sympy import conjugate >>> conjugate(erf2(x, y)) erf2(conjugate(x), conjugate(y)) Differentiation with respect to x, y is supported: >>> from sympy import diff >>> diff(erf2(x, y), x) -2*exp(-x**2)/sqrt(pi) >>> diff(erf2(x, y), y) 2*exp(-y**2)/sqrt(pi) See Also ======== erf: Gaussian error function. erfc: Complementary error function. erfi: Imaginary error function. erfinv: Inverse error function. erfcinv: Inverse Complementary error function. erf2inv: Inverse two-argument error function. References ========== .. [1] http://functions.wolfram.com/GammaBetaErf/Erf2/ """ nargs = 2 def fdiff(self, argindex): x, y = self.args if argindex == 1: return -2*C.exp(-x**2)/sqrt(S.Pi) elif argindex == 2: return 2*C.exp(-y**2)/sqrt(S.Pi) else: raise ArgumentIndexError(self, argindex) @classmethod def eval(cls, x, y): I = S.Infinity N = S.NegativeInfinity O = S.Zero if x is S.NaN or y is S.NaN: return S.NaN elif x == y: return S.Zero elif (x is I or x is N or x is O) or (y is I or y is N or y is O): return erf(y) - erf(x) if y.func is erf2inv and y.args[0] == x: return y.args[1] #Try to pull out -1 factor sign_x = x.could_extract_minus_sign() sign_y = y.could_extract_minus_sign() if (sign_x and sign_y): return -cls(-x, -y) elif (sign_x or sign_y): return erf(y)-erf(x) def _eval_conjugate(self): return self.func(self.args[0].conjugate(), self.args[1].conjugate()) def _eval_is_real(self): return self.args[0].is_real and self.args[1].is_real def _eval_rewrite_as_erf(self, x, y): return erf(y) - erf(x) def _eval_rewrite_as_erfc(self, x, y): return erfc(x) - erfc(y) def _eval_rewrite_as_erfi(self, x, y): return I*(erfi(I*x)-erfi(I*y)) def _eval_rewrite_as_fresnels(self, x, y): return erf(y).rewrite(fresnels) - erf(x).rewrite(fresnels) def _eval_rewrite_as_fresnelc(self, x, y): return erf(y).rewrite(fresnelc) - erf(x).rewrite(fresnelc) def _eval_rewrite_as_meijerg(self, x, y): return erf(y).rewrite(meijerg) - erf(x).rewrite(meijerg) def _eval_rewrite_as_hyper(self, x, y): return erf(y).rewrite(hyper) - erf(x).rewrite(hyper) def _eval_rewrite_as_uppergamma(self, x, y): return (sqrt(y**2)/y*(S.One - C.uppergamma(S.Half, y**2)/sqrt(S.Pi)) - sqrt(x**2)/x*(S.One - C.uppergamma(S.Half, x**2)/sqrt(S.Pi))) def _eval_rewrite_as_expint(self, x, y): return erf(y).rewrite(expint) - erf(x).rewrite(expint) class erfinv(Function): r""" Inverse Error Function. The erfinv function is defined as: .. math :: \mathrm{erf}(x) = y \quad \Rightarrow \quad \mathrm{erfinv}(y) = x Examples ======== >>> from sympy import I, oo, erfinv >>> from sympy.abc import x Several special values are known: >>> erfinv(0) 0 >>> erfinv(1) oo Differentiation with respect to x is supported: >>> from sympy import diff >>> diff(erfinv(x), x) sqrt(pi)*exp(erfinv(x)**2)/2 We can numerically evaluate the inverse error function to arbitrary precision on [-1, 1]: >>> erfinv(0.2).evalf(30) 0.179143454621291692285822705344 See Also ======== erf: Gaussian error function. erfc: Complementary error function. erfi: Imaginary error function. erf2: Two-argument error function. erfcinv: Inverse Complementary error function. erf2inv: Inverse two-argument error function. References ========== .. [1] http://en.wikipedia.org/wiki/Error_function#Inverse_functions .. [2] http://functions.wolfram.com/GammaBetaErf/InverseErf/ """ nargs = 1 def fdiff(self, argindex =1): if argindex == 1: return sqrt(S.Pi)*C.exp(self.func(self.args[0])**2)*S.Half else : raise ArgumentIndexError(self, argindex) @classmethod def eval(cls, z): if z is S.NaN: return S.NaN elif z is S.NegativeOne: return S.NegativeInfinity elif z is S.Zero: return S.Zero elif z is S.One: return S.Infinity if (z.func is erf) and z.args[0].is_real: return z.args[0] # Try to pull out factors of -1 nz = z.extract_multiplicatively(-1) if nz is not None and ((nz.func is erf) and (nz.args[0]).is_real): return -nz.args[0] def _eval_rewrite_as_erfcinv(self, z): return erfcinv(1-z) class erfcinv (Function): r""" Inverse Complementary Error Function. The erfcinv function is defined as: .. math :: \mathrm{erfc}(x) = y \quad \Rightarrow \quad \mathrm{erfcinv}(y) = x Examples ======== >>> from sympy import I, oo, erfcinv >>> from sympy.abc import x Several special values are known: >>> erfcinv(1) 0 >>> erfcinv(0) oo Differentiation with respect to x is supported: >>> from sympy import diff >>> diff(erfcinv(x), x) -sqrt(pi)*exp(erfcinv(x)**2)/2 See Also ======== erf: Gaussian error function. erfc: Complementary error function. erfi: Imaginary error function. erf2: Two-argument error function. erfinv: Inverse error function. erf2inv: Inverse two-argument error function. References ========== .. [1] http://en.wikipedia.org/wiki/Error_function#Inverse_functions .. [2] http://functions.wolfram.com/GammaBetaErf/InverseErfc/ """ nargs = 1 def fdiff(self, argindex =1): if argindex == 1: return -sqrt(S.Pi)*C.exp(self.func(self.args[0])**2)*S.Half else: raise ArgumentIndexError(self, argindex) @classmethod def eval(cls, z): if z is S.NaN: return S.NaN elif z is S.Zero: return S.Infinity elif z is S.One: return S.Zero elif z == 2: return S.NegativeInfinity def _eval_rewrite_as_erfinv(self, z): return erfinv(1-z) class erf2inv(Function): r""" Two-argument Inverse error function. The erf2inv function is defined as: .. math :: \mathrm{erf2}(x, w) = y \quad \Rightarrow \quad \mathrm{erf2inv}(x, y) = w Examples ======== >>> from sympy import I, oo, erf2inv, erfinv, erfcinv >>> from sympy.abc import x, y Several special values are known: >>> erf2inv(0, 0) 0 >>> erf2inv(1, 0) 1 >>> erf2inv(0, 1) oo >>> erf2inv(0, y) erfinv(y) >>> erf2inv(oo, y) erfcinv(-y) Differentiation with respect to x and y is supported: >>> from sympy import diff >>> diff(erf2inv(x, y), x) exp(-x**2 + erf2inv(x, y)**2) >>> diff(erf2inv(x, y), y) sqrt(pi)*exp(erf2inv(x, y)**2)/2 See Also ======== erf: Gaussian error function. erfc: Complementary error function. erfi: Imaginary error function. erf2: Two-argument error function. erfinv: Inverse error function. erfcinv: Inverse complementary error function. References ========== .. [1] http://functions.wolfram.com/GammaBetaErf/InverseErf2/ """ nargs = 2 def fdiff(self, argindex): x, y = self.args if argindex == 1: return C.exp(self.func(x,y)**2-x**2) elif argindex == 2: return sqrt(S.Pi)*S.Half*C.exp(self.func(x,y)**2) else: raise ArgumentIndexError(self, argindex) @classmethod def eval(cls, x, y): if x is S.NaN or y is S.NaN: return S.NaN elif x is S.Zero and y is S.Zero: return S.Zero elif x is S.Zero and y is S.One: return S.Infinity elif x is S.One and y is S.Zero: return S.One elif x is S.Zero: return erfinv(y) elif x is S.Infinity: return erfcinv(-y) elif y is S.Zero: return x elif y is S.Infinity: return erfinv(x) ############################################################################### #################### EXPONENTIAL INTEGRALS #################################### ############################################################################### class Ei(Function): r""" The classical exponential integral. For 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 `\gamma` is the Euler-Mascheroni constant. If `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 `\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 `x > 0` and `\operatorname{Ei}(x)` as defined above. Note that we carefully avoided defining `\operatorname{Ei}(x)` for negative real `x`. This is because above integral formula does not hold for any polar lift of such `x`, indeed all branches of `\operatorname{Ei}(x)` above the negative reals are imaginary. However, the following statement holds for all `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 `x > 0`, and `|x|e^{i \arg(x)}`, `|x|e^{- i \arg(x)}` denote two conjugate polar lifts of `x`. 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) See Also ======== expint: Generalised exponential integral. E1: Special case of the generalised exponential integral. li: Logarithmic integral. Li: Offset logarithmic integral. Si: Sine integral. Ci: Cosine integral. Shi: Hyperbolic sine integral. Chi: Hyperbolic cosine integral. sympy.functions.special.gamma_functions.uppergamma References ========== .. [1] http://dlmf.nist.gov/6.6 .. [2] http://en.wikipedia.org/wiki/Exponential_integral .. [3] Abramowitz & Stegun, section 5: http://www.math.sfu.ca/~cbm/aands/page_228.htm """ 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_li(self, z): if isinstance(z, log): return li(z.args[0]) # TODO: # Actually it only holds that: # Ei(z) = li(exp(z)) # for -pi < imag(z) <= pi return li(exp(z)) 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 def _eval_rewrite_as_tractable(self, z): return C.exp(z) * _eis(z) def _eval_nseries(self, x, n, logx): x0 = self.args[0].limit(x, 0) if x0 is S.Zero: f = self._eval_rewrite_as_Si(*self.args) return f._eval_nseries(x, n, logx) return super(Ei, self)._eval_nseries(x, n, logx) 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. 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) See Also ======== Ei: Another related function called exponential integral. E1: The classical case, returns expint(1, z). li: Logarithmic integral. Li: Offset logarithmic integral. Si: Sine integral. Ci: Cosine integral. Shi: Hyperbolic sine integral. Chi: Hyperbolic cosine integral. sympy.functions.special.gamma_functions.uppergamma References ========== .. [1] http://dlmf.nist.gov/8.19 .. [2] http://functions.wolfram.com/GammaBetaErf/ExpIntegralE/ .. [3] http://en.wikipedia.org/wiki/Exponential_integral """ 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 def _eval_nseries(self, x, n, logx): if not self.args[0].has(x): nu = self.args[0] if nu == 1: f = self._eval_rewrite_as_Si(*self.args) return f._eval_nseries(x, n, logx) elif nu.is_Integer and nu > 1: f = self._eval_rewrite_as_Ei(*self.args) return f._eval_nseries(x, n, logx) return super(expint, self)._eval_nseries(x, n, logx) def E1(z): """ Classical case of the generalized exponential integral. This is equivalent to ``expint(1, z)``. See Also ======== Ei: Exponential integral. expint: Generalised exponential integral. li: Logarithmic integral. Li: Offset logarithmic integral. Si: Sine integral. Ci: Cosine integral. Shi: Hyperbolic sine integral. Chi: Hyperbolic cosine integral. """ return expint(1, z) class li(Function): r""" The classical logarithmic integral. For the use in SymPy, this function is defined as .. math:: \operatorname{li}(x) = \int_0^x \frac{1}{\log(t)} \mathrm{d}t \,. Examples ======== >>> from sympy import I, oo, li >>> from sympy.abc import z Several special values are known: >>> li(0) 0 >>> li(1) -oo >>> li(oo) oo Differentiation with respect to z is supported: >>> from sympy import diff >>> diff(li(z), z) 1/log(z) Defining the `li` function via an integral: The logarithmic integral can also be defined in terms of Ei: >>> from sympy import Ei >>> li(z).rewrite(Ei) Ei(log(z)) >>> diff(li(z).rewrite(Ei), z) 1/log(z) We can numerically evaluate the logarithmic integral to arbitrary precision on the whole complex plane (except the singular points): >>> li(2).evalf(30) 1.04516378011749278484458888919 >>> li(2*I).evalf(30) 1.0652795784357498247001125598 + 3.08346052231061726610939702133*I We can even compute Soldner's constant by the help of mpmath: >>> from sympy.mpmath import findroot >>> findroot(li, 2) 1.45136923488338 Further transformations include rewriting `li` in terms of the trigonometric integrals `Si`, `Ci`, `Shi` and `Chi`: >>> from sympy import Si, Ci, Shi, Chi >>> li(z).rewrite(Si) -log(I*log(z)) - log(1/log(z))/2 + log(log(z))/2 + Ci(I*log(z)) + Shi(log(z)) >>> li(z).rewrite(Ci) -log(I*log(z)) - log(1/log(z))/2 + log(log(z))/2 + Ci(I*log(z)) + Shi(log(z)) >>> li(z).rewrite(Shi) -log(1/log(z))/2 + log(log(z))/2 + Chi(log(z)) - Shi(log(z)) >>> li(z).rewrite(Chi) -log(1/log(z))/2 + log(log(z))/2 + Chi(log(z)) - Shi(log(z)) See Also ======== Li: Offset logarithmic integral. Ei: Exponential integral. expint: Generalised exponential integral. E1: Special case of the generalised exponential integral. Si: Sine integral. Ci: Cosine integral. Shi: Hyperbolic sine integral. Chi: Hyperbolic cosine integral. References ========== .. [1] http://en.wikipedia.org/wiki/Logarithmic_integral .. [2] http://mathworld.wolfram.com/LogarithmicIntegral.html .. [3] http://dlmf.nist.gov/6 .. [4] http://mathworld.wolfram.com/SoldnersConstant.html """ nargs = 1 @classmethod def eval(cls, z): if z is S.Zero: return S.Zero elif z is S.One: return S.NegativeInfinity elif z is S.Infinity: return S.Infinity def fdiff(self, argindex=1): arg = self.args[0] if argindex == 1: return S.One / C.log(arg) else: raise ArgumentIndexError(self, argindex) def _eval_conjugate(self): z = self.args[0] # Exclude values on the branch cut (-oo, 0) if not (z.is_real and z.is_negative): return self.func(z.conjugate()) def _eval_rewrite_as_Li(self, z): return Li(z) + li(2) def _eval_rewrite_as_Ei(self, z): return Ei(C.log(z)) def _eval_rewrite_as_uppergamma(self, z): from sympy import uppergamma return (-uppergamma(0, -C.log(z)) + S.Half*(C.log(C.log(z)) - C.log(S.One/C.log(z))) - C.log(-C.log(z))) def _eval_rewrite_as_Si(self, z): return (Ci(I*C.log(z)) - I*Si(I*C.log(z)) - S.Half*(C.log(S.One/C.log(z)) - C.log(C.log(z))) - C.log(I*C.log(z))) _eval_rewrite_as_Ci = _eval_rewrite_as_Si def _eval_rewrite_as_Shi(self, z): return (Chi(C.log(z)) - Shi(C.log(z)) - S.Half*(C.log(S.One/C.log(z)) - C.log(C.log(z)))) _eval_rewrite_as_Chi = _eval_rewrite_as_Shi def _eval_rewrite_as_hyper(self, z): return (C.log(z)*hyper((1, 1), (2, 2), C.log(z)) + S.Half*(C.log(C.log(z)) - C.log(S.One/C.log(z))) + S.EulerGamma) def _eval_rewrite_as_meijerg(self, z): return (-C.log(-C.log(z)) - S.Half*(C.log(S.One/C.log(z)) - C.log(C.log(z))) - meijerg(((), (1,)), ((0, 0), ()), -C.log(z))) def _eval_rewrite_as_tractable(self, z): return z * _eis(C.log(z)) class Li(Function): r""" The offset logarithmic integral. For the use in SymPy, this function is defined as .. math:: \operatorname{Li}(x) = \operatorname{li}(x) - \operatorname{li}(2) Examples ======== >>> from sympy import I, oo, Li >>> from sympy.abc import z The following special value is known: >>> Li(2) 0 Differentiation with respect to z is supported: >>> from sympy import diff >>> diff(Li(z), z) 1/log(z) The shifted logarithmic integral can be written in terms of `li(z)`: >>> from sympy import li >>> Li(z).rewrite(li) li(z) - li(2) We can numerically evaluate the logarithmic integral to arbitrary precision on the whole complex plane (except the singular points): >>> Li(2).evalf(30) 0 >>> Li(4).evalf(30) 1.92242131492155809316615998938 See Also ======== li: Logarithmic integral. Ei: Exponential integral. expint: Generalised exponential integral. E1: Special case of the generalised exponential integral. Si: Sine integral. Ci: Cosine integral. Shi: Hyperbolic sine integral. Chi: Hyperbolic cosine integral. References ========== .. [1] http://en.wikipedia.org/wiki/Logarithmic_integral .. [2] http://mathworld.wolfram.com/LogarithmicIntegral.html .. [3] http://dlmf.nist.gov/6 """ nargs = 1 @classmethod def eval(cls, z): if z is S.Infinity: return S.Infinity elif z is 2*S.One: return S.Zero def fdiff(self, argindex=1): arg = self.args[0] if argindex == 1: return S.One / C.log(arg) else: raise ArgumentIndexError(self, argindex) def _eval_evalf(self, prec): return self.rewrite(li).evalf(prec) def _eval_rewrite_as_li(self, z): return li(z) - li(2) def _eval_rewrite_as_tractable(self, z): return self.rewrite(li).rewrite("tractable", deep=True) ############################################################################### #################### 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, simultaneous=False) if self._trigfunc(0) != 0: baseseries += EulerGamma + log(x) return baseseries.subs(x, self.args[0])._eval_nseries(x, n, logx) 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. 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 See Also ======== Ci: Cosine integral. Shi: Hyperbolic sine integral. Chi: Hyperbolic cosine integral. Ei: Exponential integral. expint: Generalised exponential integral. E1: Special case of the generalised exponential integral. li: Logarithmic integral. Li: Offset logarithmic integral. References ========== .. [1] http://en.wikipedia.org/wiki/Trigonometric_integral """ _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 class Ci(TrigonometricIntegral): r""" Cosine integral. This function is defined for positive `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 `\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 `z` and thus provides an analytic continuation to the Riemann surface of the logarithm. The formula also holds as stated for `z \in \mathbb{C}` with `\Re(z) > 0`. By lifting to the principal branch we obtain an analytic function on the cut complex plane. 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 The 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 See Also ======== Si: Sine integral. Shi: Hyperbolic sine integral. Chi: Hyperbolic cosine integral. Ei: Exponential integral. expint: Generalised exponential integral. E1: Special case of the generalised exponential integral. li: Logarithmic integral. Li: Offset logarithmic integral. References ========== .. [1] http://en.wikipedia.org/wiki/Trigonometric_integral """ _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 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. 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) The `\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 See Also ======== Si: Sine integral. Ci: Cosine integral. Chi: Hyperbolic cosine integral. Ei: Exponential integral. expint: Generalised exponential integral. E1: Special case of the generalised exponential integral. li: Logarithmic integral. Li: Offset logarithmic integral. References ========== .. [1] http://en.wikipedia.org/wiki/Trigonometric_integral """ _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 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. 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 The `\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 See Also ======== Si: Sine integral. Ci: Cosine integral. Shi: Hyperbolic sine integral. Ei: Exponential integral. expint: Generalised exponential integral. E1: Special case of the generalised exponential integral. li: Logarithmic integral. Li: Offset logarithmic integral. References ========== .. [1] http://en.wikipedia.org/wiki/Trigonometric_integral """ _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 def _latex(self, printer, exp=None): assert len(self.args) == 1 if exp: return r'\operatorname{Chi}^{%s}{\left (%s \right )}' \ % (printer._print(exp), printer._print(self.args[0])) else: return r'\operatorname{Chi}{\left (%s \right )}' \ % printer._print(self.args[0]) @staticmethod def _latex_no_arg(printer): return r'\operatorname{Chi}' ############################################################################### #################### FRESNEL INTEGRALS ######################################## ############################################################################### 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) 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 `\overline{S(z)} = S(\bar{z})`: >>> 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)) 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 `\overline{C(z)} = C(\bar{z})`: >>> 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 `\\mathrm{erf}(z)` function tractable for the Gruntz algorithm. """ nargs = 1 def _eval_aseries(self, n, args0, x, logx): point = args0[0] # Expansion at oo if point is S.Infinity: 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 xrange(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 # Expansion at I*oo t = point.extract_multiplicatively(S.ImaginaryUnit) if t is S.Infinity: z = self.args[0] # TODO: is the series really correct? l = [ 1/sqrt(S.Pi) * C.factorial(2*k)*(-S( 4))**(-k)/C.factorial(k) * (1/z)**(2*k + 1) for k in xrange(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 # All other points are not handled return super(_erfs, self)._eval_aseries(n, args0, x, logx) 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) class _eis(Function): """ Helper function to make the `\\mathrm{Ei}(z)` and `\\mathrm{li}(z)` functions 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 = [ C.factorial(k) * (1/z)**(k + 1) for k in xrange(0, n) ] o = C.Order(1/z**(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 S.One / z - _eis(z) else: raise ArgumentIndexError(self, argindex) def _eval_rewrite_as_intractable(self, z): return C.exp(-z)*Ei(z) def _eval_nseries(self, x, n, logx): x0 = self.args[0].limit(x, 0) if x0 is S.Zero: f = self._eval_rewrite_as_intractable(*self.args) return f._eval_nseries(x, n, logx) return super(_eis, self)._eval_nseries(x, n, logx) sympy-0.7.4.1/sympy/functions/special/tests/0000755000175000017500000000000012253362407021217 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/functions/special/tests/test_hyper.py0000644000175000017500000003212312253362407023760 0ustar georgeskgeorgeskfrom sympy import (hyper, meijerg, S, Tuple, pi, I, exp, log, cos, sqrt, symbols, oo, Derivative, gamma, O) from sympy.series.limits import limit from sympy.abc import x, z, k from sympy.utilities.pytest import raises from sympy.utilities.randtest import ( random_complex_number as randcplx, test_numerically as tn, test_derivative_numerically as td) def test_TupleParametersBase(): # test that our implementation of the chain rule works p = hyper((), (), z**2) assert p.diff(z) == p*2*z def test_hyper(): raises(TypeError, lambda: hyper(1, 2, z)) assert hyper((1, 2), (1,), z) == hyper(Tuple(1, 2), Tuple(1), z) h = hyper((1, 2), (3, 4, 5), z) assert h.ap == Tuple(1, 2) assert h.bq == Tuple(3, 4, 5) assert h.argument == z assert h.is_commutative is True # just a few checks to make sure that all arguments go where they should assert tn(hyper(Tuple(), Tuple(), z), exp(z), z) assert tn(z*hyper((1, 1), Tuple(2), -z), log(1 + z), z) # differentiation h = hyper( (randcplx(), randcplx(), randcplx()), (randcplx(), randcplx()), z) assert td(h, z) a1, a2, b1, b2, b3 = symbols('a1:3, b1:4') assert hyper((a1, a2), (b1, b2, b3), z).diff(z) == \ a1*a2/(b1*b2*b3) * hyper((a1 + 1, a2 + 1), (b1 + 1, b2 + 1, b3 + 1), z) # differentiation wrt parameters is not supported assert hyper([z], [], z).diff(z) == Derivative(hyper([z], [], z), z) # hyper is unbranched wrt parameters from sympy import polar_lift assert hyper([polar_lift(z)], [polar_lift(k)], polar_lift(x)) == \ hyper([z], [k], polar_lift(x)) def test_expand_func(): # evaluation at 1 of Gauss' hypergeometric function: from sympy.abc import a, b, c from sympy import gamma, expand_func a1, b1, c1 = randcplx(), randcplx(), randcplx() + 5 assert expand_func(hyper([a, b], [c], 1)) == \ gamma(c)*gamma(-a - b + c)/(gamma(-a + c)*gamma(-b + c)) assert abs(expand_func(hyper([a1, b1], [c1], 1)).n() - hyper([a1, b1], [c1], 1).n()) < 1e-10 # hyperexpand wrapper for hyper: assert expand_func(hyper([], [], z)) == exp(z) assert expand_func(hyper([1, 2, 3], [], z)) == hyper([1, 2, 3], [], z) assert expand_func(meijerg([[1, 1], []], [[1], [0]], z)) == log(z + 1) assert expand_func(meijerg([[1, 1], []], [[], []], z)) == \ meijerg([[1, 1], []], [[], []], z) def replace_dummy(expr, sym): from sympy import Dummy dum = expr.atoms(Dummy) if not dum: return expr assert len(dum) == 1 return expr.xreplace({dum.pop(): sym}) def test_hyper_rewrite_sum(): from sympy import RisingFactorial, factorial, Dummy, Sum _k = Dummy("k") assert replace_dummy(hyper((1, 2), (1, 3), x).rewrite(Sum), _k) == \ Sum(x**_k / factorial(_k) * RisingFactorial(2, _k) / RisingFactorial(3, _k), (_k, 0, oo)) assert hyper((1, 2, 3), (-1, 3), z).rewrite(Sum) == \ hyper((1, 2, 3), (-1, 3), z) def test_radius_of_convergence(): assert hyper((1, 2), [3], z).radius_of_convergence == 1 assert hyper((1, 2), [3, 4], z).radius_of_convergence == oo assert hyper((1, 2, 3), [4], z).radius_of_convergence == 0 assert hyper((0, 1, 2), [4], z).radius_of_convergence == oo assert hyper((-1, 1, 2), [-4], z).radius_of_convergence == 0 assert hyper((-1, -2, 2), [-1], z).radius_of_convergence == oo assert hyper((-1, 2), [-1, -2], z).radius_of_convergence == 0 assert hyper([-1, 1, 3], [-2, 2], z).radius_of_convergence == 1 assert hyper([-1, 1], [-2, 2], z).radius_of_convergence == oo assert hyper([-1, 1, 3], [-2], z).radius_of_convergence == 0 assert hyper((-1, 2, 3, 4), [], z).radius_of_convergence == oo assert hyper([1, 1], [3], 1).convergence_statement == True assert hyper([1, 1], [2], 1).convergence_statement == False assert hyper([1, 1], [2], -1).convergence_statement == True assert hyper([1, 1], [1], -1).convergence_statement == False def test_meijer(): raises(TypeError, lambda: meijerg(1, z)) raises(TypeError, lambda: meijerg(((1,), (2,)), (3,), (4,), z)) assert meijerg(((1, 2), (3,)), ((4,), (5,)), z) == \ meijerg(Tuple(1, 2), Tuple(3), Tuple(4), Tuple(5), z) g = meijerg((1, 2), (3, 4, 5), (6, 7, 8, 9), (10, 11, 12, 13, 14), z) assert g.an == Tuple(1, 2) assert g.ap == Tuple(1, 2, 3, 4, 5) assert g.aother == Tuple(3, 4, 5) assert g.bm == Tuple(6, 7, 8, 9) assert g.bq == Tuple(6, 7, 8, 9, 10, 11, 12, 13, 14) assert g.bother == Tuple(10, 11, 12, 13, 14) assert g.argument == z assert g.nu == 75 assert g.delta == -1 assert g.is_commutative is True assert meijerg([1, 2], [3], [4], [5], z).delta == S(1)/2 # just a few checks to make sure that all arguments go where they should assert tn(meijerg(Tuple(), Tuple(), Tuple(0), Tuple(), -z), exp(z), z) assert tn(sqrt(pi)*meijerg(Tuple(), Tuple(), Tuple(0), Tuple(S(1)/2), z**2/4), cos(z), z) assert tn(meijerg(Tuple(1, 1), Tuple(), Tuple(1), Tuple(0), z), log(1 + z), z) # differentiation g = meijerg((randcplx(),), (randcplx() + 2*I,), Tuple(), (randcplx(), randcplx()), z) assert td(g, z) g = meijerg(Tuple(), (randcplx(),), Tuple(), (randcplx(), randcplx()), z) assert td(g, z) g = meijerg(Tuple(), Tuple(), Tuple(randcplx()), Tuple(randcplx(), randcplx()), z) assert td(g, z) a1, a2, b1, b2, c1, c2, d1, d2 = symbols('a1:3, b1:3, c1:3, d1:3') assert meijerg((a1, a2), (b1, b2), (c1, c2), (d1, d2), z).diff(z) == \ (meijerg((a1 - 1, a2), (b1, b2), (c1, c2), (d1, d2), z) + (a1 - 1)*meijerg((a1, a2), (b1, b2), (c1, c2), (d1, d2), z))/z assert meijerg([z, z], [], [], [], z).diff(z) == \ Derivative(meijerg([z, z], [], [], [], z), z) # meijerg is unbranched wrt parameters from sympy import polar_lift as pl assert meijerg([pl(a1)], [pl(a2)], [pl(b1)], [pl(b2)], pl(z)) == \ meijerg([a1], [a2], [b1], [b2], pl(z)) # integrand from sympy.abc import a, b, c, d, s assert meijerg([a], [b], [c], [d], z).integrand(s) == \ z**s*gamma(c - s)*gamma(-a + s + 1)/(gamma(b - s)*gamma(-d + s + 1)) def test_meijerg_derivative(): assert meijerg([], [1, 1], [0, 0, x], [], z).diff(x) == \ log(z)*meijerg([], [1, 1], [0, 0, x], [], z) \ + 2*meijerg([], [1, 1, 1], [0, 0, x, 0], [], z) y = randcplx() a = 5 # mpmath chokes with non-real numbers, and Mod1 with floats assert td(meijerg([x], [], [], [], y), x) assert td(meijerg([x**2], [], [], [], y), x) assert td(meijerg([], [x], [], [], y), x) assert td(meijerg([], [], [x], [], y), x) assert td(meijerg([], [], [], [x], y), x) assert td(meijerg([x], [a], [a + 1], [], y), x) assert td(meijerg([x], [a + 1], [a], [], y), x) assert td(meijerg([x, a], [], [], [a + 1], y), x) assert td(meijerg([x, a + 1], [], [], [a], y), x) b = S(3)/2 assert td(meijerg([a + 2], [b], [b - 3, x], [a], y), x) def test_meijerg_period(): assert meijerg([], [1], [0], [], x).get_period() == 2*pi assert meijerg([1], [], [], [0], x).get_period() == 2*pi assert meijerg([], [], [0], [], x).get_period() == 2*pi # exp(x) assert meijerg( [], [], [0], [S(1)/2], x).get_period() == 2*pi # cos(sqrt(x)) assert meijerg( [], [], [S(1)/2], [0], x).get_period() == 4*pi # sin(sqrt(x)) assert meijerg([1, 1], [], [1], [0], x).get_period() == oo # log(1 + x) def test_hyper_unpolarify(): from sympy import exp_polar a = exp_polar(2*pi*I)*x b = x assert hyper([], [], a).argument == b assert hyper([0], [], a).argument == a assert hyper([0], [0], a).argument == b assert hyper([0, 1], [0], a).argument == a def test_hyperrep(): from sympy.functions.special.hyper import (HyperRep, HyperRep_atanh, HyperRep_power1, HyperRep_power2, HyperRep_log1, HyperRep_asin1, HyperRep_asin2, HyperRep_sqrts1, HyperRep_sqrts2, HyperRep_log2, HyperRep_cosasin, HyperRep_sinasin) # First test the base class works. from sympy import Piecewise, exp_polar a, b, c, d, z = symbols('a b c d z') class myrep(HyperRep): @classmethod def _expr_small(cls, x): return a @classmethod def _expr_small_minus(cls, x): return b @classmethod def _expr_big(cls, x, n): return c*n @classmethod def _expr_big_minus(cls, x, n): return d*n assert myrep(z).rewrite('nonrep') == Piecewise((0, abs(z) > 1), (a, True)) assert myrep(exp_polar(I*pi)*z).rewrite('nonrep') == \ Piecewise((0, abs(z) > 1), (b, True)) assert myrep(exp_polar(2*I*pi)*z).rewrite('nonrep') == \ Piecewise((c, abs(z) > 1), (a, True)) assert myrep(exp_polar(3*I*pi)*z).rewrite('nonrep') == \ Piecewise((d, abs(z) > 1), (b, True)) assert myrep(exp_polar(4*I*pi)*z).rewrite('nonrep') == \ Piecewise((2*c, abs(z) > 1), (a, True)) assert myrep(exp_polar(5*I*pi)*z).rewrite('nonrep') == \ Piecewise((2*d, abs(z) > 1), (b, True)) assert myrep(z).rewrite('nonrepsmall') == a assert myrep(exp_polar(I*pi)*z).rewrite('nonrepsmall') == b def t(func, hyp, z): """ Test that func is a valid representation of hyp. """ # First test that func agrees with hyp for small z if not tn(func.rewrite('nonrepsmall'), hyp, z, a=S(-1)/2, b=S(-1)/2, c=S(1)/2, d=S(1)/2): return False # Next check that the two small representations agree. if not tn( func.rewrite('nonrepsmall').subs( z, exp_polar(I*pi)*z).replace(exp_polar, exp), func.subs(z, exp_polar(I*pi)*z).rewrite('nonrepsmall'), z, a=S(-1)/2, b=S(-1)/2, c=S(1)/2, d=S(1)/2): return False # Next check continuity along exp_polar(I*pi)*t expr = func.subs(z, exp_polar(I*pi)*z).rewrite('nonrep') if abs(expr.subs(z, 1 + 1e-15).n() - expr.subs(z, 1 - 1e-15).n()) > 1e-10: return False # Finally check continuity of the big reps. def dosubs(func, a, b): rv = func.subs(z, exp_polar(a)*z).rewrite('nonrep') return rv.subs(z, exp_polar(b)*z).replace(exp_polar, exp) for n in [0, 1, 2, 3, 4, -1, -2, -3, -4]: expr1 = dosubs(func, 2*I*pi*n, I*pi/2) expr2 = dosubs(func, 2*I*pi*n + I*pi, -I*pi/2) if not tn(expr1, expr2, z): return False expr1 = dosubs(func, 2*I*pi*(n + 1), -I*pi/2) expr2 = dosubs(func, 2*I*pi*n + I*pi, I*pi/2) if not tn(expr1, expr2, z): return False return True # Now test the various representatives. a = S(1)/3 assert t(HyperRep_atanh(z), hyper([S(1)/2, 1], [S(3)/2], z), z) assert t(HyperRep_power1(a, z), hyper([-a], [], z), z) assert t(HyperRep_power2(a, z), hyper([a, a - S(1)/2], [2*a], z), z) assert t(HyperRep_log1(z), -z*hyper([1, 1], [2], z), z) assert t(HyperRep_asin1(z), hyper([S(1)/2, S(1)/2], [S(3)/2], z), z) assert t(HyperRep_asin2(z), hyper([1, 1], [S(3)/2], z), z) assert t(HyperRep_sqrts1(a, z), hyper([-a, S(1)/2 - a], [S(1)/2], z), z) assert t(HyperRep_sqrts2(a, z), -2*z/(2*a + 1)*hyper([-a - S(1)/2, -a], [S(1)/2], z).diff(z), z) assert t(HyperRep_log2(z), -z/4*hyper([S(3)/2, 1, 1], [2, 2], z), z) assert t(HyperRep_cosasin(a, z), hyper([-a, a], [S(1)/2], z), z) assert t(HyperRep_sinasin(a, z), 2*a*z*hyper([1 - a, 1 + a], [S(3)/2], z), z) def test_meijerg_eval(): from sympy import besseli, exp_polar from sympy.abc import l a = randcplx() arg = x*exp_polar(k*pi*I) expr1 = pi*meijerg([[], [(a + 1)/2]], [[a/2], [-a/2, (a + 1)/2]], arg**2/4) expr2 = besseli(a, arg) # Test that the two expressions agree for all arguments. for x_ in [0.5, 1.5]: for k_ in [0.0, 0.1, 0.3, 0.5, 0.8, 1, 5.751, 15.3]: assert abs((expr1 - expr2).n(subs={x: x_, k: k_})) < 1e-10 assert abs((expr1 - expr2).n(subs={x: x_, k: -k_})) < 1e-10 # Test continuity independently eps = 1e-13 expr2 = expr1.subs(k, l) for x_ in [0.5, 1.5]: for k_ in [0.5, S(1)/3, 0.25, 0.75, S(2)/3, 1.0, 1.5]: assert abs((expr1 - expr2).n( subs={x: x_, k: k_ + eps, l: k_ - eps})) < 1e-10 assert abs((expr1 - expr2).n( subs={x: x_, k: -k_ + eps, l: -k_ - eps})) < 1e-10 expr = (meijerg(((0.5,), ()), ((0.5, 0, 0.5), ()), exp_polar(-I*pi)/4) + meijerg(((0.5,), ()), ((0.5, 0, 0.5), ()), exp_polar(I*pi)/4)) \ /(2*sqrt(pi)) assert (expr - pi/exp(1)).n(chop=True) == 0 def test_limits(): k, x = symbols('k, x') assert hyper((1,), (S(4)/3, S(5)/3), k**2).series(k) == \ hyper((1,), (S(4)/3, S(5)/3), 0) + \ 9*k**2*hyper((2,), (S(7)/3, S(8)/3), 0)/20 + \ 81*k**4*hyper((3,), (S(10)/3, S(11)/3), 0)/1120 + \ O(k**6) # issue 3251 assert limit(meijerg((), (), (1,), (0,), -x), x, 0) == \ meijerg(((), ()), ((1,), (0,)), 0) # issue 2953 sympy-0.7.4.1/sympy/functions/special/tests/test_bessel.py0000644000175000017500000003141712253362407024113 0ustar georgeskgeorgeskfrom sympy import jn, yn, symbols, sin, cos, pi, S, jn_zeros, besselj, \ bessely, besseli, besselk, hankel1, hankel2, expand_func, \ latex, sqrt, sinh, cosh from sympy.functions.special.bessel import fn from sympy.utilities.pytest import raises, skip from sympy.utilities.randtest import \ random_complex_number as randcplx, \ test_numerically as tn, \ test_derivative_numerically as td, \ _randint from sympy.abc import z, n, k, x randint = _randint() def test_bessel_rand(): for f in [besselj, bessely, besseli, besselk, hankel1, hankel2, jn, yn]: assert td(f(randcplx(), z), z) def test_diff(): assert besselj(n, z).diff(z) == besselj(n - 1, z)/2 - besselj(n + 1, z)/2 assert bessely(n, z).diff(z) == bessely(n - 1, z)/2 - bessely(n + 1, z)/2 assert besseli(n, z).diff(z) == besseli(n - 1, z)/2 + besseli(n + 1, z)/2 assert besselk(n, z).diff(z) == -besselk(n - 1, z)/2 - besselk(n + 1, z)/2 assert hankel1(n, z).diff(z) == hankel1(n - 1, z)/2 - hankel1(n + 1, z)/2 assert hankel2(n, z).diff(z) == hankel2(n - 1, z)/2 - hankel2(n + 1, z)/2 def test_rewrite(): from sympy import polar_lift, exp, I assert besselj(n, z).rewrite(jn) == sqrt(2*z/pi)*jn(n - S(1)/2, z) assert bessely(n, z).rewrite(yn) == sqrt(2*z/pi)*yn(n - S(1)/2, z) assert besseli(n, z).rewrite(besselj) == \ exp(-I*n*pi/2)*besselj(n, polar_lift(I)*z) assert besselj(n, z).rewrite(besseli) == \ exp(I*n*pi/2)*besseli(n, polar_lift(-I)*z) nu = randcplx() assert tn(besselj(nu, z), besselj(nu, z).rewrite(besseli), z) assert tn(besselj(nu, z), besselj(nu, z).rewrite(bessely), z) assert tn(besseli(nu, z), besseli(nu, z).rewrite(besselj), z) assert tn(besseli(nu, z), besseli(nu, z).rewrite(bessely), z) assert tn(bessely(nu, z), bessely(nu, z).rewrite(besselj), z) assert tn(bessely(nu, z), bessely(nu, z).rewrite(besseli), z) assert tn(besselk(nu, z), besselk(nu, z).rewrite(besselj), z) assert tn(besselk(nu, z), besselk(nu, z).rewrite(besseli), z) assert tn(besselk(nu, z), besselk(nu, z).rewrite(bessely), z) def test_expand(): from sympy import besselsimp, Symbol, exp, exp_polar, I assert expand_func(besselj(S(1)/2, z).rewrite(jn)) == \ sqrt(2)*sin(z)/(sqrt(pi)*sqrt(z)) assert expand_func(bessely(S(1)/2, z).rewrite(yn)) == \ -sqrt(2)*cos(z)/(sqrt(pi)*sqrt(z)) # XXX: teach sin/cos to work around arguments like # x*exp_polar(I*pi*n/2). Then change besselsimp -> expand_func assert besselsimp(besselj(S(1)/2, z)) == sqrt(2)*sin(z)/(sqrt(pi)*sqrt(z)) assert besselsimp(besselj(S(-1)/2, z)) == sqrt(2)*cos(z)/(sqrt(pi)*sqrt(z)) assert besselsimp(besselj(S(5)/2, z)) == \ -sqrt(2)*(z**2*sin(z) + 3*z*cos(z) - 3*sin(z))/(sqrt(pi)*z**(S(5)/2)) assert besselsimp(besselj(-S(5)/2, z)) == \ -sqrt(2)*(z**2*cos(z) - 3*z*sin(z) - 3*cos(z))/(sqrt(pi)*z**(S(5)/2)) assert besselsimp(bessely(S(1)/2, z)) == \ -(sqrt(2)*cos(z))/(sqrt(pi)*sqrt(z)) assert besselsimp(bessely(S(-1)/2, z)) == sqrt(2)*sin(z)/(sqrt(pi)*sqrt(z)) assert besselsimp(bessely(S(5)/2, z)) == \ sqrt(2)*(z**2*cos(z) - 3*z*sin(z) - 3*cos(z))/(sqrt(pi)*z**(S(5)/2)) assert besselsimp(bessely(S(-5)/2, z)) == \ -sqrt(2)*(z**2*sin(z) + 3*z*cos(z) - 3*sin(z))/(sqrt(pi)*z**(S(5)/2)) assert besselsimp(besseli(S(1)/2, z)) == sqrt(2)*sinh(z)/(sqrt(pi)*sqrt(z)) assert besselsimp(besseli(S(-1)/2, z)) == \ sqrt(2)*cosh(z)/(sqrt(pi)*sqrt(z)) assert besselsimp(besseli(S(5)/2, z)) == \ sqrt(2)*(z**2*sinh(z) - 3*z*cosh(z) + 3*sinh(z))/(sqrt(pi)*z**(S(5)/2)) assert besselsimp(besseli(S(-5)/2, z)) == \ sqrt(2)*(z**2*cosh(z) - 3*z*sinh(z) + 3*cosh(z))/(sqrt(pi)*z**(S(5)/2)) assert besselsimp(besselk(S(1)/2, z)) == \ besselsimp(besselk(S(-1)/2, z)) == sqrt(pi)*exp(-z)/(sqrt(2)*sqrt(z)) assert besselsimp(besselk(S(5)/2, z)) == \ besselsimp(besselk(S(-5)/2, z)) == \ sqrt(2)*sqrt(pi)*(z**2 + 3*z + 3)*exp(-z)/(2*z**(S(5)/2)) def check(eq, ans): return tn(eq, ans) and eq == ans rn = randcplx(a=1, b=0, d=0, c=2) for besselx in [besselj, bessely, besseli, besselk]: ri = S(2*randint(-11, 10) + 1) / 2 # half integer in [-21/2, 21/2] assert tn(besselsimp(besselx(ri, z)), besselx(ri, z)) assert check(expand_func(besseli(rn, x)), \ besseli(rn - 2, x) - 2*(rn - 1)*besseli(rn - 1, x)/x) assert check(expand_func(besseli(-rn, x)), \ besseli(-rn + 2, x) + 2*(-rn + 1)*besseli(-rn + 1, x)/x) assert check(expand_func(besselj(rn, x)), \ -besselj(rn - 2, x) + 2*(rn - 1)*besselj(rn - 1, x)/x) assert check(expand_func(besselj(-rn, x)), \ -besselj(-rn + 2, x) + 2*(-rn + 1)*besselj(-rn + 1, x)/x) assert check(expand_func(besselk(rn, x)), \ besselk(rn - 2, x) + 2*(rn - 1)*besselk(rn - 1, x)/x) assert check(expand_func(besselk(-rn, x)), \ besselk(-rn + 2, x) - 2*(-rn + 1)*besselk(-rn + 1, x)/x) assert check(expand_func(bessely(rn, x)), \ -bessely(rn - 2, x) + 2*(rn - 1)*bessely(rn - 1, x)/x) assert check(expand_func(bessely(-rn, x)), \ -bessely(-rn + 2, x) + 2*(-rn + 1)*bessely(-rn + 1, x)/x) n = Symbol('n', integer=True, positive=True) assert expand_func(besseli(n + 2, z)) == \ besseli(n, z) + (-2*n - 2)*(-2*n*besseli(n, z)/z + besseli(n - 1, z))/z assert expand_func(besselj(n + 2, z)) == \ -besselj(n, z) + (2*n + 2)*(2*n*besselj(n, z)/z - besselj(n - 1, z))/z assert expand_func(besselk(n + 2, z)) == \ besselk(n, z) + (2*n + 2)*(2*n*besselk(n, z)/z + besselk(n - 1, z))/z assert expand_func(bessely(n + 2, z)) == \ -bessely(n, z) + (2*n + 2)*(2*n*bessely(n, z)/z - bessely(n - 1, z))/z assert expand_func(besseli(n + S(1)/2, z).rewrite(jn)) == \ sqrt(2)*sqrt(z)*exp(-I*pi*(n + S(1)/2)/2)* \ exp_polar(I*pi/4)*jn(n, z*exp_polar(I*pi/2))/sqrt(pi) assert expand_func(besselj(n + S(1)/2, z).rewrite(jn)) == \ sqrt(2)*sqrt(z)*jn(n, z)/sqrt(pi) r = Symbol('r', real=True) p = Symbol('p', positive=True) i = Symbol('i', integer=True) for besselx in [besselj, bessely, besseli, besselk]: assert besselx(i, p).is_real assert besselx(i, x).is_real is None assert besselx(x, z).is_real is None for besselx in [besselj, besseli]: assert besselx(i, r).is_real for besselx in [bessely, besselk]: assert besselx(i, r).is_real is None def test_fn(): x, z = symbols("x z") assert fn(1, z) == 1/z**2 assert fn(2, z) == -1/z + 3/z**3 assert fn(3, z) == -6/z**2 + 15/z**4 assert fn(4, z) == 1/z - 45/z**3 + 105/z**5 def mjn(n, z): return expand_func(jn(n, z)) def myn(n, z): return expand_func(yn(n, z)) def test_jn(): z = symbols("z") assert mjn(0, z) == sin(z)/z assert mjn(1, z) == sin(z)/z**2 - cos(z)/z assert mjn(2, z) == (3/z**3 - 1/z)*sin(z) - (3/z**2) * cos(z) assert mjn(3, z) == (15/z**4 - 6/z**2)*sin(z) + (1/z - 15/z**3)*cos(z) assert mjn(4, z) == (1/z + 105/z**5 - 45/z**3)*sin(z) + \ (-105/z**4 + 10/z**2)*cos(z) assert mjn(5, z) == (945/z**6 - 420/z**4 + 15/z**2)*sin(z) + \ (-1/z - 945/z**5 + 105/z**3)*cos(z) assert mjn(6, z) == (-1/z + 10395/z**7 - 4725/z**5 + 210/z**3)*sin(z) + \ (-10395/z**6 + 1260/z**4 - 21/z**2)*cos(z) assert expand_func(jn(n, z)) == jn(n, z) def test_yn(): z = symbols("z") assert myn(0, z) == -cos(z)/z assert myn(1, z) == -cos(z)/z**2 - sin(z)/z assert myn(2, z) == -((3/z**3 - 1/z)*cos(z) + (3/z**2)*sin(z)) assert expand_func(yn(n, z)) == yn(n, z) def test_sympify_yn(): assert S(15) in myn(3, pi).atoms() assert myn(3, pi) == 15/pi**4 - 6/pi**2 def eq(a, b, tol=1e-6): for x, y in zip(a, b): if not (abs(x - y) < tol): return False return True def test_jn_zeros(): assert eq(jn_zeros(0, 4), [3.141592, 6.283185, 9.424777, 12.566370]) assert eq(jn_zeros(1, 4), [4.493409, 7.725251, 10.904121, 14.066193]) assert eq(jn_zeros(2, 4), [5.763459, 9.095011, 12.322940, 15.514603]) assert eq(jn_zeros(3, 4), [6.987932, 10.417118, 13.698023, 16.923621]) assert eq(jn_zeros(4, 4), [8.182561, 11.704907, 15.039664, 18.301255]) def test_bessel_eval(): from sympy import I, Symbol n, m, k = Symbol('n', integer=True), Symbol('m'), Symbol('k', integer=True, zero=False) for f in [besselj, besseli]: assert f(0, 0) == S.One assert f(2.1, 0) == S.Zero assert f(-3, 0) == S.Zero assert f(-10.2, 0) == S.ComplexInfinity assert f(1 + 3*I, 0) == S.Zero assert f(-3 + I, 0) == S.ComplexInfinity assert f(-2*I, 0) == S.NaN assert f(n, 0) != S.One and f(n, 0) != S.Zero assert f(m, 0) != S.One and f(m, 0) != S.Zero assert f(k, 0) == S.Zero assert bessely(0, 0) == S.NegativeInfinity assert besselk(0, 0) == S.Infinity for f in [bessely, besselk]: assert f(1 + I, 0) == S.ComplexInfinity assert f(I, 0) == S.NaN for f in [besselj, bessely]: assert f(m, S.Infinity) == S.Zero assert f(m, S.NegativeInfinity) == S.Zero for f in [besseli, besselk]: assert f(m, I*S.Infinity) == S.Zero assert f(m, I*S.NegativeInfinity) == S.Zero for f in [besseli, besselk]: assert f(-4, z) == f(4, z) assert f(-3, z) == f(3, z) assert f(-n, z) == f(n, z) assert f(-m, z) != f(m, z) for f in [besselj, bessely]: assert f(-4, z) == f(4, z) assert f(-3, z) == -f(3, z) assert f(-n, z) == (-1)**n*f(n, z) assert f(-m, z) != (-1)**m*f(m, z) for f in [besselj, besseli]: assert f(m, -z) == (-z)**m*z**(-m)*f(m, z) assert besseli(2, -z) == besseli(2, z) assert besseli(3, -z) == -besseli(3, z) assert besselj(0, -z) == besselj(0, z) assert besselj(1, -z) == -besselj(1, z) assert besseli(0, I*z) == besselj(0, z) assert besseli(1, I*z) == I*besselj(1, z) assert besselj(3, I*z) == -I*besseli(3, z) def test_conjugate(): from sympy import conjugate, I, Symbol n, z, x = Symbol('n'), Symbol('z', real=False), Symbol('x', real=True) y, t = Symbol('y', real=True, positive=True), Symbol('t', negative=True) for f in [besseli, besselj, besselk, bessely, jn, yn, hankel1, hankel2]: assert f(n, -1).conjugate() != f(conjugate(n), -1) assert f(n, x).conjugate() != f(conjugate(n), x) assert f(n, t).conjugate() != f(conjugate(n), t) rz = randcplx(b=0.5) for f in [besseli, besselj, besselk, bessely, jn, yn]: assert f(n, 1 + I).conjugate() == f(conjugate(n), 1 - I) assert f(n, 0).conjugate() == f(conjugate(n), 0) assert f(n, 1).conjugate() == f(conjugate(n), 1) assert f(n, z).conjugate() == f(conjugate(n), conjugate(z)) assert f(n, y).conjugate() == f(conjugate(n), y) assert tn(f(n, rz).conjugate(), f(conjugate(n), conjugate(rz))) assert hankel1(n, 1 + I).conjugate() == hankel2(conjugate(n), 1 - I) assert hankel1(n, 0).conjugate() == hankel2(conjugate(n), 0) assert hankel1(n, 1).conjugate() == hankel2(conjugate(n), 1) assert hankel1(n, y).conjugate() == hankel2(conjugate(n), y) assert hankel1(n, z).conjugate() == hankel2(conjugate(n), conjugate(z)) assert tn(hankel1(n, rz).conjugate(), hankel2(conjugate(n), conjugate(rz))) assert hankel2(n, 1 + I).conjugate() == hankel1(conjugate(n), 1 - I) assert hankel2(n, 0).conjugate() == hankel1(conjugate(n), 0) assert hankel2(n, 1).conjugate() == hankel1(conjugate(n), 1) assert hankel2(n, y).conjugate() == hankel1(conjugate(n), y) assert hankel2(n, z).conjugate() == hankel1(conjugate(n), conjugate(z)) assert tn(hankel2(n, rz).conjugate(), hankel1(conjugate(n), conjugate(rz))) def test_branching(): from sympy import exp_polar, polar_lift, Symbol, I, exp assert besselj(polar_lift(k), x) == besselj(k, x) assert besseli(polar_lift(k), x) == besseli(k, x) n = Symbol('n', integer=True) assert besselj(n, exp_polar(2*pi*I)*x) == besselj(n, x) assert besselj(n, polar_lift(x)) == besselj(n, x) assert besseli(n, exp_polar(2*pi*I)*x) == besseli(n, x) assert besseli(n, polar_lift(x)) == besseli(n, x) def tn(func, s): from random import uniform c = uniform(1, 5) expr = func(s, c*exp_polar(I*pi)) - func(s, c*exp_polar(-I*pi)) eps = 1e-15 expr2 = func(s + eps, -c + eps*I) - func(s + eps, -c - eps*I) return abs(expr.n() - expr2.n()).n() < 1e-10 nu = Symbol('nu') assert besselj(nu, exp_polar(2*pi*I)*x) == exp(2*pi*I*nu)*besselj(nu, x) assert besseli(nu, exp_polar(2*pi*I)*x) == exp(2*pi*I*nu)*besseli(nu, x) assert tn(besselj, 2) assert tn(besselj, pi) assert tn(besselj, I) assert tn(besseli, 2) assert tn(besseli, pi) assert tn(besseli, I) sympy-0.7.4.1/sympy/functions/special/tests/test_zeta_functions.py0000644000175000017500000001217112253362407025665 0ustar georgeskgeorgeskfrom sympy import (Symbol, zeta, nan, Rational, Float, pi, dirichlet_eta, log, zoo, expand_func, polylog, lerchphi, S, exp, sqrt, I, exp_polar, polar_lift, O) from sympy.utilities.randtest import (test_derivative_numerically as td, random_complex_number as randcplx, test_numerically as tn) from sympy.utilities.pytest import XFAIL x = Symbol('x') a = Symbol('a') z = Symbol('z') s = Symbol('s') def test_zeta_eval(): assert zeta(nan) == nan assert zeta(x, nan) == nan assert zeta(0) == Rational(-1, 2) assert zeta(0, x) == Rational(1, 2) - x assert zeta(1) == zoo assert zeta(1, 2) == zoo assert zeta(1, -7) == zoo assert zeta(1, x) == zoo assert zeta(2, 1) == pi**2/6 assert zeta(2) == pi**2/6 assert zeta(4) == pi**4/90 assert zeta(6) == pi**6/945 assert zeta(2, 2) == pi**2/6 - 1 assert zeta(4, 3) == pi**4/90 - Rational(17, 16) assert zeta(6, 4) == pi**6/945 - Rational(47449, 46656) assert zeta(2, -2) == pi**2/6 + Rational(5, 4) assert zeta(4, -3) == pi**4/90 + Rational(1393, 1296) assert zeta(6, -4) == pi**6/945 + Rational(3037465, 2985984) assert zeta(-1) == -Rational(1, 12) assert zeta(-2) == 0 assert zeta(-3) == Rational(1, 120) assert zeta(-4) == 0 assert zeta(-5) == -Rational(1, 252) assert zeta(-1, 3) == -Rational(37, 12) assert zeta(-1, 7) == -Rational(253, 12) assert zeta(-1, -4) == Rational(119, 12) assert zeta(-1, -9) == Rational(539, 12) assert zeta(-4, 3) == -17 assert zeta(-4, -8) == 8772 assert zeta(0, 1) == -Rational(1, 2) assert zeta(0, -1) == Rational(1, 2) assert zeta(0, 2) == -Rational(3, 2) assert zeta(0, -2) == Rational(3, 2) assert zeta( 3).evalf(20).epsilon_eq(Float("1.2020569031595942854", 20), 1e-19) def test_zeta_series(): assert zeta(x, a).series(a, 0, 2) == \ zeta(x, 0) - x*a*zeta(x + 1, 0) + O(a**2) def test_dirichlet_eta_eval(): assert dirichlet_eta(0) == Rational(1, 2) assert dirichlet_eta(-1) == Rational(1, 4) assert dirichlet_eta(1) == log(2) assert dirichlet_eta(2) == pi**2/12 assert dirichlet_eta(4) == pi**4*Rational(7, 720) def test_rewriting(): assert dirichlet_eta(x).rewrite(zeta) == (1 - 2**(1 - x))*zeta(x) assert zeta(x).rewrite(dirichlet_eta) == dirichlet_eta(x)/(1 - 2**(1 - x)) assert tn(dirichlet_eta(x), dirichlet_eta(x).rewrite(zeta), x) assert tn(zeta(x), zeta(x).rewrite(dirichlet_eta), x) assert zeta(x, a).rewrite(lerchphi) == lerchphi(1, x, a) assert polylog(s, z).rewrite(lerchphi) == lerchphi(z, s, 1)*z assert lerchphi(1, x, a).rewrite(zeta) == zeta(x, a) assert z*lerchphi(z, s, 1).rewrite(polylog) == polylog(s, z) def test_derivatives(): from sympy import Derivative assert zeta(x, a).diff(x) == Derivative(zeta(x, a), x) assert zeta(x, a).diff(a) == -x*zeta(x + 1, a) assert lerchphi( z, s, a).diff(z) == (lerchphi(z, s - 1, a) - a*lerchphi(z, s, a))/z assert lerchphi(z, s, a).diff(a) == -s*lerchphi(z, s + 1, a) assert polylog(s, z).diff(z) == polylog(s - 1, z)/z b = randcplx() c = randcplx() assert td(zeta(b, x), x) assert td(polylog(b, z), z) assert td(lerchphi(c, b, x), x) assert td(lerchphi(x, b, c), x) def myexpand(func, target): expanded = expand_func(func) if target is not None: return expanded == target if expanded == func: # it didn't expand return False # check to see that the expanded and original evaluate to the same value subs = {} for a in func.free_symbols: subs[a] = randcplx() return abs(func.subs(subs).n() - expanded.replace(exp_polar, exp).subs(subs).n()) < 1e-10 def test_polylog_expansion(): from sympy import factor, log assert polylog(s, 0) == 0 assert polylog(s, 1) == zeta(s) assert polylog(s, -1) == dirichlet_eta(s) assert myexpand(polylog(1, z), -log(1 + exp_polar(-I*pi)*z)) assert myexpand(polylog(0, z), z/(1 - z)) assert myexpand(polylog(-1, z), z**2/(1 - z)**2 + z/(1 - z)) assert myexpand(polylog(-5, z), None) def test_lerchphi_expansion(): assert myexpand(lerchphi(1, s, a), zeta(s, a)) assert myexpand(lerchphi(z, s, 1), polylog(s, z)/z) # direct summation assert myexpand(lerchphi(z, -1, a), a/(1 - z) + z/(1 - z)**2) assert myexpand(lerchphi(z, -3, a), None) # polylog reduction assert myexpand(lerchphi(z, s, S(1)/2), 2**(s - 1)*(polylog(s, sqrt(z))/sqrt(z) - polylog(s, polar_lift(-1)*sqrt(z))/sqrt(z))) assert myexpand(lerchphi(z, s, 2), -1/z + polylog(s, z)/z**2) assert myexpand(lerchphi(z, s, S(3)/2), None) assert myexpand(lerchphi(z, s, S(7)/3), None) assert myexpand(lerchphi(z, s, -S(1)/3), None) assert myexpand(lerchphi(z, s, -S(5)/2), None) # hurwitz zeta reduction assert myexpand(lerchphi(-1, s, a), 2**(-s)*zeta(s, a/2) - 2**(-s)*zeta(s, (a + 1)/2)) assert myexpand(lerchphi(I, s, a), None) assert myexpand(lerchphi(-I, s, a), None) assert myexpand(lerchphi(exp(2*I*pi/5), s, a), None) sympy-0.7.4.1/sympy/functions/special/tests/test_elliptic_integrals.py0000644000175000017500000001260512253362407026511 0ustar georgeskgeorgeskfrom sympy import (S, Symbol, pi, I, oo, zoo, sin, sqrt, tan, atan, gamma, atanh, hyper, meijerg, O) from sympy.functions.special.elliptic_integrals import (elliptic_k as K, elliptic_f as F, elliptic_e as E, elliptic_pi as P) from sympy.utilities.randtest import (test_derivative_numerically as td, random_complex_number as randcplx, test_numerically as tn) from sympy.abc import x, y, z, m, n i = Symbol('i', integer=True) j = Symbol('k', integer=True, positive=True) def test_K(): assert K(0) == pi/2 assert K(S(1)/2) == 8*pi**(S(3)/2)/gamma(-S(1)/4)**2 assert K(1) == zoo assert K(-1) == gamma(S(1)/4)**2/(4*sqrt(2*pi)) assert K(oo) == 0 assert K(-oo) == 0 assert K(I*oo) == 0 assert K(-I*oo) == 0 assert K(zoo) == 0 assert K(z).diff(z) == (E(z) - (1 - z)*K(z))/(2*z*(1 - z)) assert td(K(z), z) zi = Symbol('z', real=False) assert K(zi).conjugate() == K(zi.conjugate()) zr = Symbol('z', real=True, negative=True) assert K(zr).conjugate() == K(zr) assert K(z).rewrite(hyper) == \ (pi/2)*hyper((S.Half, S.Half), (S.One,), z) assert tn(K(z), (pi/2)*hyper((S.Half, S.Half), (S.One,), z)) assert K(z).rewrite(meijerg) == \ meijerg(((S.Half, S.Half), []), ((S.Zero,), (S.Zero,)), -z)/2 assert tn(K(z), meijerg(((S.Half, S.Half), []), ((S.Zero,), (S.Zero,)), -z)/2) assert K(z).series(z) == pi/2 + pi*z/8 + 9*pi*z**2/128 + \ 25*pi*z**3/512 + 1225*pi*z**4/32768 + 3969*pi*z**5/131072 + O(z**6) def test_F(): assert F(z, 0) == z assert F(0, m) == 0 assert F(pi*i/2, m) == i*K(m) assert F(z, oo) == 0 assert F(z, -oo) == 0 assert F(-z, m) == -F(z, m) assert F(z, m).diff(z) == 1/sqrt(1 - m*sin(z)**2) assert F(z, m).diff(m) == E(z, m)/(2*m*(1 - m)) - F(z, m)/(2*m) - \ sin(2*z)/(4*(1 - m)*sqrt(1 - m*sin(z)**2)) r = randcplx() assert td(F(z, r), z) assert td(F(r, m), m) mi = Symbol('m', real=False) assert F(z, mi).conjugate() == F(z.conjugate(), mi.conjugate()) mr = Symbol('m', real=True, negative=True) assert F(z, mr).conjugate() == F(z.conjugate(), mr) assert F(z, m).series(z) == \ z + z**5*(3*m**2/40 - m/30) + m*z**3/6 + O(z**6) def test_E(): assert E(z, 0) == z assert E(0, m) == 0 assert E(i*pi/2, m) == i*E(m) assert E(z, oo) == zoo assert E(z, -oo) == zoo assert E(0) == pi/2 assert E(1) == 1 assert E(oo) == I*oo assert E(-oo) == oo assert E(zoo) == zoo assert E(-z, m) == -E(z, m) assert E(z, m).diff(z) == sqrt(1 - m*sin(z)**2) assert E(z, m).diff(m) == (E(z, m) - F(z, m))/(2*m) assert E(z).diff(z) == (E(z) - K(z))/(2*z) r = randcplx() assert td(E(r, m), m) assert td(E(z, r), z) assert td(E(z), z) mi = Symbol('m', real=False) assert E(z, mi).conjugate() == E(z.conjugate(), mi.conjugate()) mr = Symbol('m', real=True, negative=True) assert E(z, mr).conjugate() == E(z.conjugate(), mr) assert E(z).rewrite(hyper) == (pi/2)*hyper((-S.Half, S.Half), (S.One,), z) assert tn(E(z), (pi/2)*hyper((-S.Half, S.Half), (S.One,), z)) assert E(z).rewrite(meijerg) == \ -meijerg(((S.Half, S(3)/2), []), ((S.Zero,), (S.Zero,)), -z)/4 assert tn(E(z), -meijerg(((S.Half, S(3)/2), []), ((S.Zero,), (S.Zero,)), -z)/4) assert E(z, m).series(z) == \ z + z**5*(-m**2/40 + m/30) - m*z**3/6 + O(z**6) assert E(z).series(z) == pi/2 - pi*z/8 - 3*pi*z**2/128 - \ 5*pi*z**3/512 - 175*pi*z**4/32768 - 441*pi*z**5/131072 + O(z**6) def test_P(): assert P(0, z, m) == F(z, m) assert P(1, z, m) == F(z, m) + \ (sqrt(1 - m*sin(z)**2)*tan(z) - E(z, m))/(1 - m) assert P(n, i*pi/2, m) == i*P(n, m) assert P(n, z, 0) == atanh(sqrt(n - 1)*tan(z))/sqrt(n - 1) assert P(n, z, n) == F(z, n) - P(1, z, n) + tan(z)/sqrt(1 - n*sin(z)**2) assert P(oo, z, m) == 0 assert P(-oo, z, m) == 0 assert P(n, z, oo) == 0 assert P(n, z, -oo) == 0 assert P(0, m) == K(m) assert P(1, m) == zoo assert P(n, 0) == pi/(2*sqrt(1 - n)) assert P(2, 1) == -oo assert P(-1, 1) == oo assert P(n, n) == E(n)/(1 - n) assert P(n, -z, m) == -P(n, z, m) ni, mi = Symbol('n', real=False), Symbol('m', real=False) assert P(ni, z, mi).conjugate() == \ P(ni.conjugate(), z.conjugate(), mi.conjugate()) nr, mr = Symbol('n', real=True, negative=True), \ Symbol('m', real=True, negative=True) assert P(nr, z, mr).conjugate() == P(nr, z.conjugate(), mr) assert P(n, m).conjugate() == P(n.conjugate(), m.conjugate()) assert P(n, z, m).diff(n) == (E(z, m) + (m - n)*F(z, m)/n + (n**2 - m)*P(n, z, m)/n - n*sqrt(1 - m*sin(z)**2)*sin(2*z)/(2*(1 - n*sin(z)**2)))/(2*(m - n)*(n - 1)) assert P(n, z, m).diff(z) == 1/(sqrt(1 - m*sin(z)**2)*(1 - n*sin(z)**2)) assert P(n, z, m).diff(m) == (E(z, m)/(m - 1) + P(n, z, m) - m*sin(2*z)/(2*(m - 1)*sqrt(1 - m*sin(z)**2)))/(2*(n - m)) assert P(n, m).diff(n) == (E(m) + (m - n)*K(m)/n + (n**2 - m)*P(n, m)/n)/(2*(m - n)*(n - 1)) assert P(n, m).diff(m) == (E(m)/(m - 1) + P(n, m))/(2*(n - m)) rx, ry = randcplx(), randcplx() assert td(P(n, rx, ry), n) assert td(P(rx, z, ry), z) assert td(P(rx, ry, m), m) assert P(n, z, m).series(z) == z + z**3*(m/6 + n/3) + \ z**5*(3*m**2/40 + m*n/10 - m/30 + n**2/5 - n/15) + O(z**6) sympy-0.7.4.1/sympy/functions/special/tests/test_spherical_harmonics.py0000644000175000017500000000747612253362407026663 0ustar georgeskgeorgeskfrom sympy import Symbol, sqrt, pi, sin, cos, cot, exp, I, S, diff, conjugate from sympy.functions.special.spherical_harmonics import Ynm, Znm, Ynm_c from sympy.utilities.pytest import XFAIL def test_Ynm(): # http://en.wikipedia.org/wiki/Spherical_harmonics th, ph = Symbol("theta", real=True), Symbol("phi", real=True) from sympy.abc import n,m assert Ynm(0, 0, th, ph).expand(func=True) == 1/(2*sqrt(pi)) assert Ynm(1, -1, th, ph) == -exp(-2*I*ph)*Ynm(1, 1, th, ph) assert Ynm(1, -1, th, ph).expand(func=True) == sqrt(6)*sqrt(-cos(th)**2 + 1)*exp(-I*ph)/(4*sqrt(pi)) assert Ynm(1, -1, th, ph).expand(func=True) == sqrt(6)*sqrt(-cos(th)**2 + 1)*exp(-I*ph)/(4*sqrt(pi)) assert Ynm(1, 0, th, ph).expand(func=True) == sqrt(3)*cos(th)/(2*sqrt(pi)) assert Ynm(1, 1, th, ph).expand(func=True) == -sqrt(6)*sqrt(-cos(th)**2 + 1)*exp(I*ph)/(4*sqrt(pi)) assert Ynm(2, 0, th, ph).expand(func=True) == 3*sqrt(5)*cos(th)**2/(4*sqrt(pi)) - sqrt(5)/(4*sqrt(pi)) assert Ynm(2, 1, th, ph).expand(func=True) == -sqrt(30)*sqrt(-cos(th)**2 + 1)*exp(I*ph)*cos(th)/(4*sqrt(pi)) assert Ynm(2, -2, th, ph).expand(func=True) == (-sqrt(30)*exp(-2*I*ph)*cos(th)**2/(8*sqrt(pi)) + sqrt(30)*exp(-2*I*ph)/(8*sqrt(pi))) assert Ynm(2, 2, th, ph).expand(func=True) == (-sqrt(30)*exp(2*I*ph)*cos(th)**2/(8*sqrt(pi)) + sqrt(30)*exp(2*I*ph)/(8*sqrt(pi))) assert diff(Ynm(n, m, th, ph), th) == (m*cot(th)*Ynm(n, m, th, ph) + sqrt((-m + n)*(m + n + 1))*exp(-I*ph)*Ynm(n, m + 1, th, ph)) assert diff(Ynm(n, m, th, ph), ph) == I*m*Ynm(n, m, th, ph) assert conjugate(Ynm(n, m, th, ph)) == (-1)**(2*m)*exp(-2*I*m*ph)*Ynm(n, m, th, ph) assert Ynm(n, m, -th, ph) == Ynm(n, m, th, ph) assert Ynm(n, m, th, -ph) == exp(-2*I*m*ph)*Ynm(n, m, th, ph) assert Ynm(n, -m, th, ph) == (-1)**m*exp(-2*I*m*ph)*Ynm(n, m, th, ph) def test_Ynm_c(): th, ph = Symbol("theta", real=True), Symbol("phi", real=True) from sympy.abc import n,m assert Ynm_c(n, m, th, ph) == (-1)**(2*m)*exp(-2*I*m*ph)*Ynm(n, m, th, ph) def test_Znm(): # http://en.wikipedia.org/wiki/Solid_harmonics#List_of_lowest_functions th, ph = Symbol("theta", real=True), Symbol("phi", real=True) from sympy.abc import n,m assert Znm(0, 0, th, ph) == Ynm(0, 0, th, ph) assert Znm(1, -1, th, ph) == (-sqrt(2)*I*(Ynm(1, 1, th, ph) - exp(-2*I*ph)*Ynm(1, 1, th, ph))/2) assert Znm(1, 0, th, ph) == Ynm(1, 0, th, ph) assert Znm(1, 1, th, ph) == (sqrt(2)*(Ynm(1, 1, th, ph) + exp(-2*I*ph)*Ynm(1, 1, th, ph))/2) assert Znm(0, 0, th, ph).expand(func=True) == 1/(2*sqrt(pi)) assert Znm(1, -1, th, ph).expand(func=True) == (sqrt(3)*I*sqrt(-cos(th)**2 + 1)*exp(I*ph)/(4*sqrt(pi)) - sqrt(3)*I*sqrt(-cos(th)**2 + 1)*exp(-I*ph)/(4*sqrt(pi))) assert Znm(1, 0, th, ph).expand(func=True) == sqrt(3)*cos(th)/(2*sqrt(pi)) assert Znm(1, 1, th, ph).expand(func=True) == (-sqrt(3)*sqrt(-cos(th)**2 + 1)*exp(I*ph)/(4*sqrt(pi)) - sqrt(3)*sqrt(-cos(th)**2 + 1)*exp(-I*ph)/(4*sqrt(pi))) assert Znm(2, -1, th, ph).expand(func=True) == (sqrt(15)*I*sqrt(-cos(th)**2 + 1)*exp(I*ph)*cos(th)/(4*sqrt(pi)) - sqrt(15)*I*sqrt(-cos(th)**2 + 1)*exp(-I*ph)*cos(th)/(4*sqrt(pi))) assert Znm(2, 0, th, ph).expand(func=True) == 3*sqrt(5)*cos(th)**2/(4*sqrt(pi)) - sqrt(5)/(4*sqrt(pi)) assert Znm(2, 1, th, ph).expand(func=True) == (-sqrt(15)*sqrt(-cos(th)**2 + 1)*exp(I*ph)*cos(th)/(4*sqrt(pi)) - sqrt(15)*sqrt(-cos(th)**2 + 1)*exp(-I*ph)*cos(th)/(4*sqrt(pi))) sympy-0.7.4.1/sympy/functions/special/tests/test_tensor_functions.py0000644000175000017500000001134012253362407026231 0ustar georgeskgeorgeskfrom sympy import ( adjoint, conjugate, Dummy, Eijk, KroneckerDelta, LeviCivita, Symbol, symbols, transpose, ) from sympy.physics.secondquant import evaluate_deltas, F from sympy.utilities.pytest import XFAIL x, y = symbols('x y') def test_levicivita(): assert Eijk(1, 2, 3) == LeviCivita(1, 2, 3) assert LeviCivita(1, 2, 3) == 1 assert LeviCivita(1, 3, 2) == -1 assert LeviCivita(1, 2, 2) == 0 i, j, k = symbols('i j k') assert LeviCivita(i, j, k) == LeviCivita(i, j, k, evaluate=False) assert LeviCivita(i, j, i) == 0 assert LeviCivita(1, i, i) == 0 assert LeviCivita(i, j, k).doit() == (j - i)*(k - i)*(k - j)/2 assert LeviCivita(1, 2, 3, 1) == 0 assert LeviCivita(4, 5, 1, 2, 3) == 1 assert LeviCivita(4, 5, 2, 1, 3) == -1 assert LeviCivita(i, j, k).is_integer is True assert adjoint(LeviCivita(i, j, k)) == LeviCivita(i, j, k) assert conjugate(LeviCivita(i, j, k)) == LeviCivita(i, j, k) assert transpose(LeviCivita(i, j, k)) == LeviCivita(i, j, k) def test_kronecker_delta(): i, j = symbols('i j') k = Symbol('k', nonzero=True) assert KroneckerDelta(1, 1) == 1 assert KroneckerDelta(1, 2) == 0 assert KroneckerDelta(x, x) == 1 assert KroneckerDelta(x**2 - y**2, x**2 - y**2) == 1 assert KroneckerDelta(i, i) == 1 assert KroneckerDelta(i, i + 1) == 0 assert KroneckerDelta(0, 0) == 1 assert KroneckerDelta(0, 1) == 0 assert KroneckerDelta(i + k, i) == KroneckerDelta(0, k) assert KroneckerDelta(i + k, i + k) == 1 assert KroneckerDelta(i + k, i + 1 + k) == 0 assert KroneckerDelta(i, j).subs(dict(i=1, j=0)) == 0 assert KroneckerDelta(i, j).subs(dict(i=3, j=3)) == 1 assert KroneckerDelta(i, j)**0 == 1 for n in range(1, 10): assert KroneckerDelta(i, j)**n == KroneckerDelta(i, j) assert KroneckerDelta(i, j)**-n == 1/KroneckerDelta(i, j) assert KroneckerDelta(i, j).is_integer is True assert adjoint(KroneckerDelta(i, j)) == KroneckerDelta(i, j) assert conjugate(KroneckerDelta(i, j)) == KroneckerDelta(i, j) assert transpose(KroneckerDelta(i, j)) == KroneckerDelta(i, j) # to test if canonical assert (KroneckerDelta(i, j) == KroneckerDelta(j, i)) == True def test_kronecker_delta_secondquant(): """secondquant-specific methods""" D = KroneckerDelta i, j, v, w = symbols('i j v w', below_fermi=True, cls=Dummy) a, b, t, u = symbols('a b t u', above_fermi=True, cls=Dummy) p, q, r, s = symbols('p q r s', cls=Dummy) assert D(i, a) == 0 assert D(i, t) == 0 assert D(i, j).is_above_fermi is False assert D(a, b).is_above_fermi is True assert D(p, q).is_above_fermi is True assert D(i, q).is_above_fermi is False assert D(q, i).is_above_fermi is False assert D(q, v).is_above_fermi is False assert D(a, q).is_above_fermi is True assert D(i, j).is_below_fermi is True assert D(a, b).is_below_fermi is False assert D(p, q).is_below_fermi is True assert D(p, j).is_below_fermi is True assert D(q, b).is_below_fermi is False assert D(i, j).is_only_above_fermi is False assert D(a, b).is_only_above_fermi is True assert D(p, q).is_only_above_fermi is False assert D(i, q).is_only_above_fermi is False assert D(q, i).is_only_above_fermi is False assert D(a, q).is_only_above_fermi is True assert D(i, j).is_only_below_fermi is True assert D(a, b).is_only_below_fermi is False assert D(p, q).is_only_below_fermi is False assert D(p, j).is_only_below_fermi is True assert D(q, b).is_only_below_fermi is False assert not D(i, q).indices_contain_equal_information assert not D(a, q).indices_contain_equal_information assert D(p, q).indices_contain_equal_information assert D(a, b).indices_contain_equal_information assert D(i, j).indices_contain_equal_information assert D(q, b).preferred_index == b assert D(q, b).killable_index == q assert D(q, t).preferred_index == t assert D(q, t).killable_index == q assert D(q, i).preferred_index == i assert D(q, i).killable_index == q assert D(q, v).preferred_index == v assert D(q, v).killable_index == q assert D(q, p).preferred_index == p assert D(q, p).killable_index == q EV = evaluate_deltas assert EV(D(a, q)*F(q)) == F(a) assert EV(D(i, q)*F(q)) == F(i) assert EV(D(a, q)*F(a)) == D(a, q)*F(a) assert EV(D(i, q)*F(i)) == D(i, q)*F(i) assert EV(D(a, b)*F(a)) == F(b) assert EV(D(a, b)*F(b)) == F(a) assert EV(D(i, j)*F(i)) == F(j) assert EV(D(i, j)*F(j)) == F(i) assert EV(D(p, q)*F(q)) == F(p) assert EV(D(p, q)*F(p)) == F(q) assert EV(D(p, j)*D(p, i)*F(i)) == F(j) assert EV(D(p, j)*D(p, i)*F(j)) == F(i) assert EV(D(p, q)*D(p, i))*F(i) == D(q, i)*F(i) sympy-0.7.4.1/sympy/functions/special/tests/test_delta_functions.py0000644000175000017500000000504712253362407026017 0ustar georgeskgeorgeskfrom sympy import ( adjoint, conjugate, DiracDelta, Heaviside, nan, oo, pi, sqrt, symbols, transpose, Symbol, Piecewise, I, S, Eq ) from sympy.utilities.pytest import raises from sympy.core.function import ArgumentIndexError x, y = symbols('x y') def test_DiracDelta(): assert DiracDelta(1) == 0 assert DiracDelta(5.1) == 0 assert DiracDelta(-pi) == 0 assert DiracDelta(5, 7) == 0 assert DiracDelta(nan) == nan assert DiracDelta(0).func is DiracDelta assert DiracDelta(x).func is DiracDelta assert adjoint(DiracDelta(x)) == DiracDelta(x) assert adjoint(DiracDelta(x - y)) == DiracDelta(x - y) assert conjugate(DiracDelta(x)) == DiracDelta(x) assert conjugate(DiracDelta(x - y)) == DiracDelta(x - y) assert transpose(DiracDelta(x)) == DiracDelta(x) assert transpose(DiracDelta(x - y)) == DiracDelta(x - y) assert DiracDelta(x).diff(x) == DiracDelta(x, 1) assert DiracDelta(x, 1).diff(x) == DiracDelta(x, 2) assert DiracDelta(x).is_simple(x) is True assert DiracDelta(3*x).is_simple(x) is True assert DiracDelta(x**2).is_simple(x) is False assert DiracDelta(sqrt(x)).is_simple(x) is False assert DiracDelta(x).is_simple(y) is False assert DiracDelta(x*y).simplify(x) == DiracDelta(x)/abs(y) assert DiracDelta(x*y).simplify(y) == DiracDelta(y)/abs(x) assert DiracDelta(x**2*y).simplify(x) == DiracDelta(x**2*y) assert DiracDelta(y).simplify(x) == DiracDelta(y) raises(ArgumentIndexError, lambda: DiracDelta(x).fdiff(2)) raises(ValueError, lambda: DiracDelta(x, -1)) def test_heaviside(): assert Heaviside(0) == 0.5 assert Heaviside(-5) == 0 assert Heaviside(1) == 1 assert Heaviside(nan) == nan assert adjoint(Heaviside(x)) == Heaviside(x) assert adjoint(Heaviside(x - y)) == Heaviside(x - y) assert conjugate(Heaviside(x)) == Heaviside(x) assert conjugate(Heaviside(x - y)) == Heaviside(x - y) assert transpose(Heaviside(x)) == Heaviside(x) assert transpose(Heaviside(x - y)) == Heaviside(x - y) assert Heaviside(x).diff(x) == DiracDelta(x) assert Heaviside(x + I).is_Function is True assert Heaviside(I*x).is_Function is True raises(ArgumentIndexError, lambda: Heaviside(x).fdiff(2)) raises(ValueError, lambda: Heaviside(I)) raises(ValueError, lambda: Heaviside(2 + 3*I)) def test_rewrite(): x, y = Symbol('x', real=True), Symbol('y') assert Heaviside(x).rewrite(Piecewise) == \ Piecewise((1, x > 0), (S(1)/2, Eq(x, 0)), (0, True)) assert Heaviside(y).rewrite(Piecewise) == Heaviside(y) sympy-0.7.4.1/sympy/functions/special/tests/test_gamma_functions.py0000644000175000017500000003062312253362407026006 0ustar georgeskgeorgeskfrom sympy import ( Symbol, gamma, I, oo, nan, zoo, factorial, sqrt, Rational, log, polygamma, EulerGamma, pi, uppergamma, S, expand_func, loggamma, sin, cos, O, cancel, lowergamma, exp, erf, beta, exp_polar, harmonic, zeta, factorial) from sympy.core.function import ArgumentIndexError from sympy.utilities.randtest import (test_derivative_numerically as td, random_complex_number as randcplx, test_numerically as tn) from sympy.utilities.pytest import raises x = Symbol('x') y = Symbol('y') n = Symbol('n', integer=True) def test_gamma(): assert gamma(nan) == nan assert gamma(oo) == oo assert gamma(-100) == zoo assert gamma(0) == zoo assert gamma(1) == 1 assert gamma(2) == 1 assert gamma(3) == 2 assert gamma(102) == factorial(101) assert gamma(Rational(1, 2)) == sqrt(pi) assert gamma(Rational(3, 2)) == Rational(1, 2)*sqrt(pi) assert gamma(Rational(5, 2)) == Rational(3, 4)*sqrt(pi) assert gamma(Rational(7, 2)) == Rational(15, 8)*sqrt(pi) assert gamma(Rational(-1, 2)) == -2*sqrt(pi) assert gamma(Rational(-3, 2)) == Rational(4, 3)*sqrt(pi) assert gamma(Rational(-5, 2)) == -Rational(8, 15)*sqrt(pi) assert gamma(Rational(-15, 2)) == Rational(256, 2027025)*sqrt(pi) assert gamma(Rational( -11, 8)).expand(func=True) == Rational(64, 33)*gamma(Rational(5, 8)) assert gamma(Rational( -10, 3)).expand(func=True) == Rational(81, 280)*gamma(Rational(2, 3)) assert gamma(Rational( 14, 3)).expand(func=True) == Rational(880, 81)*gamma(Rational(2, 3)) assert gamma(Rational( 17, 7)).expand(func=True) == Rational(30, 49)*gamma(Rational(3, 7)) assert gamma(Rational( 19, 8)).expand(func=True) == Rational(33, 64)*gamma(Rational(3, 8)) assert gamma(x).diff(x) == gamma(x)*polygamma(0, x) assert gamma(x - 1).expand(func=True) == gamma(x)/(x - 1) assert gamma(x + 2).expand(func=True, mul=False) == x*(x + 1)*gamma(x) assert expand_func(gamma(x + Rational(3, 2))) == \ (x + Rational(1, 2))*gamma(x + Rational(1, 2)) assert expand_func(gamma(x - Rational(1, 2))) == \ gamma(Rational(1, 2) + x)/(x - Rational(1, 2)) # Test a bug: assert expand_func(gamma(x + Rational(3, 4))) == gamma(x + Rational(3, 4)) assert gamma(3*exp_polar(I*pi)/4).is_nonnegative is False assert gamma(3*exp_polar(I*pi)/4).is_nonpositive is True def test_gamma_series(): assert gamma(x + 1).series(x, 0, 3) == \ 1 - EulerGamma*x + x**2*(EulerGamma**2/2 + pi**2/12) + O(x**3) assert gamma(x).series(x, -1, 3) == \ -1/x + EulerGamma - 1 + x*(-1 - pi**2/12 - EulerGamma**2/2 + EulerGamma) \ + x**2*(-1 - pi**2/12 - EulerGamma**2/2 + EulerGamma**3/6 - polygamma(2, 1)/6 + EulerGamma*pi**2/12 + EulerGamma) + O(x**3) def tn_branch(s, func): from sympy import I, pi, exp_polar from random import uniform c = uniform(1, 5) expr = func(s, c*exp_polar(I*pi)) - func(s, c*exp_polar(-I*pi)) eps = 1e-15 expr2 = func(s + eps, -c + eps*I) - func(s + eps, -c - eps*I) return abs(expr.n() - expr2.n()).n() < 1e-10 def test_lowergamma(): from sympy import meijerg, exp_polar, I, expint assert lowergamma(x, y).diff(y) == y**(x - 1)*exp(-y) assert td(lowergamma(randcplx(), y), y) assert td(lowergamma(x, randcplx()), x) assert lowergamma(x, y).diff(x) == \ gamma(x)*polygamma(0, x) - uppergamma(x, y)*log(y) \ - meijerg([], [1, 1], [0, 0, x], [], y) assert lowergamma(S.Half, x) == sqrt(pi)*erf(sqrt(x)) assert not lowergamma(S.Half - 3, x).has(lowergamma) assert not lowergamma(S.Half + 3, x).has(lowergamma) assert lowergamma(S.Half, x, evaluate=False).has(lowergamma) assert tn(lowergamma(S.Half + 3, x, evaluate=False), lowergamma(S.Half + 3, x), x) assert tn(lowergamma(S.Half - 3, x, evaluate=False), lowergamma(S.Half - 3, x), x) assert lowergamma(x, y).rewrite(uppergamma) == gamma(x) - uppergamma(x, y) assert tn_branch(-3, lowergamma) assert tn_branch(-4, lowergamma) assert tn_branch(S(1)/3, lowergamma) assert tn_branch(pi, lowergamma) assert lowergamma(3, exp_polar(4*pi*I)*x) == lowergamma(3, x) assert lowergamma(y, exp_polar(5*pi*I)*x) == \ exp(4*I*pi*y)*lowergamma(y, x*exp_polar(pi*I)) assert lowergamma(-2, exp_polar(5*pi*I)*x) == \ lowergamma(-2, x*exp_polar(I*pi)) + 2*pi*I assert lowergamma( x, y).rewrite(expint) == -y**x*expint(-x + 1, y) + gamma(x) k = Symbol('k', integer=True) assert lowergamma( k, y).rewrite(expint) == -y**k*expint(-k + 1, y) + gamma(k) k = Symbol('k', integer=True, positive=False) assert lowergamma(k, y).rewrite(expint) == lowergamma(k, y) def test_uppergamma(): from sympy import meijerg, exp_polar, I, expint assert uppergamma(4, 0) == 6 assert uppergamma(x, y).diff(y) == -y**(x - 1)*exp(-y) assert td(uppergamma(randcplx(), y), y) assert uppergamma(x, y).diff(x) == \ uppergamma(x, y)*log(y) + meijerg([], [1, 1], [0, 0, x], [], y) assert td(uppergamma(x, randcplx()), x) assert uppergamma(S.Half, x) == sqrt(pi)*(1 - erf(sqrt(x))) assert not uppergamma(S.Half - 3, x).has(uppergamma) assert not uppergamma(S.Half + 3, x).has(uppergamma) assert uppergamma(S.Half, x, evaluate=False).has(uppergamma) assert tn(uppergamma(S.Half + 3, x, evaluate=False), uppergamma(S.Half + 3, x), x) assert tn(uppergamma(S.Half - 3, x, evaluate=False), uppergamma(S.Half - 3, x), x) assert uppergamma(x, y).rewrite(lowergamma) == gamma(x) - lowergamma(x, y) assert tn_branch(-3, uppergamma) assert tn_branch(-4, uppergamma) assert tn_branch(S(1)/3, uppergamma) assert tn_branch(pi, uppergamma) assert uppergamma(3, exp_polar(4*pi*I)*x) == uppergamma(3, x) assert uppergamma(y, exp_polar(5*pi*I)*x) == \ exp(4*I*pi*y)*uppergamma(y, x*exp_polar(pi*I)) + \ gamma(y)*(1 - exp(4*pi*I*y)) assert uppergamma(-2, exp_polar(5*pi*I)*x) == \ uppergamma(-2, x*exp_polar(I*pi)) - 2*pi*I assert uppergamma(-2, x) == expint(3, x)/x**2 assert uppergamma(x, y).rewrite(expint) == y**x*expint(-x + 1, y) def test_polygamma(): from sympy import I assert polygamma(n, nan) == nan assert polygamma(0, oo) == oo assert polygamma(1, oo) == 0 assert polygamma(5, oo) == 0 assert polygamma(0, -9) == zoo assert polygamma(0, -9) == zoo assert polygamma(0, -1) == zoo assert polygamma(0, 0) == zoo assert polygamma(0, 1) == -EulerGamma assert polygamma(0, 7) == Rational(49, 20) - EulerGamma assert polygamma(1, 1) == pi**2/6 assert polygamma(1, 2) == pi**2/6 - 1 assert polygamma(1, 3) == pi**2/6 - Rational(5, 4) assert polygamma(3, 1) == pi**4 / 15 assert polygamma(3, 5) == 6*(Rational(-22369, 20736) + pi**4/90) assert polygamma(5, 1) == 8 * pi**6 / 63 def t(m, n): x = S(m)/n r = polygamma(0, x) if r.has(polygamma): return False return abs(polygamma(0, x.n()).n() - r.n()).n() < 1e-10 assert t(1, 2) assert t(3, 2) assert t(-1, 2) assert t(1, 4) assert t(-3, 4) assert t(1, 3) assert t(4, 3) assert t(3, 4) assert t(2, 3) assert polygamma(0, x).rewrite(zeta) == polygamma(0, x) assert polygamma(1, x).rewrite(zeta) == zeta(2, x) assert polygamma(2, x).rewrite(zeta) == -2*zeta(3, x) assert polygamma(3, 7*x).diff(x) == 7*polygamma(4, 7*x) assert polygamma(0, x).rewrite(harmonic) == harmonic(x - 1) - EulerGamma assert polygamma(2, x).rewrite(harmonic) == 2*harmonic(x - 1, 3) - 2*zeta(3) ni = Symbol("n", integer=True) assert polygamma(ni, x).rewrite(harmonic) == (-1)**(ni + 1)*(-harmonic(x - 1, ni + 1) + zeta(ni + 1))*factorial(ni) # Polygamma of non-negative integer order is unbranched: from sympy import exp_polar k = Symbol('n', integer=True, nonnegative=True) assert polygamma(k, exp_polar(2*I*pi)*x) == polygamma(k, x) # but negative integers are branched! k = Symbol('n', integer=True) assert polygamma(k, exp_polar(2*I*pi)*x).args == (k, exp_polar(2*I*pi)*x) # Polygamma of order -1 is loggamma: assert polygamma(-1, x) == loggamma(x) # But smaller orders are iterated integrals and don't have a special name assert polygamma(-2, x).func is polygamma # Test a bug assert polygamma(0, -x).expand(func=True) == polygamma(0, -x) def test_polygamma_expand_func(): assert polygamma(0, x).expand(func=True) == polygamma(0, x) assert polygamma(0, 2*x).expand(func=True) == \ polygamma(0, x)/2 + polygamma(0, Rational(1, 2) + x)/2 + log(2) assert polygamma(1, 2*x).expand(func=True) == \ polygamma(1, x)/4 + polygamma(1, Rational(1, 2) + x)/4 assert polygamma(2, x).expand(func=True) == \ polygamma(2, x) assert polygamma(0, -1 + x).expand(func=True) == \ polygamma(0, x) - 1/(x - 1) assert polygamma(0, 1 + x).expand(func=True) == \ 1/x + polygamma(0, x ) assert polygamma(0, 2 + x).expand(func=True) == \ 1/x + 1/(1 + x) + polygamma(0, x) assert polygamma(0, 3 + x).expand(func=True) == \ polygamma(0, x) + 1/x + 1/(1 + x) + 1/(2 + x) assert polygamma(0, 4 + x).expand(func=True) == \ polygamma(0, x) + 1/x + 1/(1 + x) + 1/(2 + x) + 1/(3 + x) assert polygamma(1, 1 + x).expand(func=True) == \ polygamma(1, x) - 1/x**2 assert polygamma(1, 2 + x).expand(func=True, multinomial=False) == \ polygamma(1, x) - 1/x**2 - 1/(1 + x)**2 assert polygamma(1, 3 + x).expand(func=True, multinomial=False) == \ polygamma(1, x) - 1/x**2 - 1/(1 + x)**2 - 1/(2 + x)**2 assert polygamma(1, 4 + x).expand(func=True, multinomial=False) == \ polygamma(1, x) - 1/x**2 - 1/(1 + x)**2 - \ 1/(2 + x)**2 - 1/(3 + x)**2 assert polygamma(0, x + y).expand(func=True) == \ polygamma(0, x + y) assert polygamma(1, x + y).expand(func=True) == \ polygamma(1, x + y) assert polygamma(1, 3 + 4*x + y).expand(func=True, multinomial=False) == \ polygamma(1, y + 4*x) - 1/(y + 4*x)**2 - \ 1/(1 + y + 4*x)**2 - 1/(2 + y + 4*x)**2 assert polygamma(3, 3 + 4*x + y).expand(func=True, multinomial=False) == \ polygamma(3, y + 4*x) - 6/(y + 4*x)**4 - \ 6/(1 + y + 4*x)**4 - 6/(2 + y + 4*x)**4 assert polygamma(3, 4*x + y + 1).expand(func=True, multinomial=False) == \ polygamma(3, y + 4*x) - 6/(y + 4*x)**4 e = polygamma(3, 4*x + y + S(3)/2) assert e.expand(func=True) == e e = polygamma(3, x + y + S(3)/4) assert e.expand(func=True, basic=False) == e def test_loggamma(): raises(TypeError, lambda: loggamma(2, 3)) raises(ArgumentIndexError, lambda: loggamma(x).fdiff(2)) assert loggamma(x).diff(x) == polygamma(0, x) s1 = loggamma(1/(x + sin(x)) + cos(x)).nseries(x, n=4) s2 = (-log(2*x) - 1)/(2*x) - log(x/pi)/2 + (4 - log(2*x))*x/24 + O(x**2) + \ log(x)*x**2/2 assert (s1 - s2).expand(force=True).removeO() == 0 s1 = loggamma(1/x).series(x) s2 = (1/x - S(1)/2)*log(1/x) - 1/x + log(2*pi)/2 + \ x/12 - x**3/360 + x**5/1260 + O(x**7) assert ((s1 - s2).expand(force=True)).removeO() == 0 assert loggamma(x).rewrite('intractable') == log(gamma(x)) s1 = loggamma(x).series(x) assert s1 == -log(x) - EulerGamma*x + pi**2*x**2/12 + x**3*polygamma(2, 1)/6 + \ pi**4*x**4/360 + x**5*polygamma(4, 1)/120 + O(x**6) assert s1 == loggamma(x).rewrite('intractable').series(x) assert loggamma(x).is_real is None y, z = Symbol('y', real=True), Symbol('z', imaginary=True) assert loggamma(y).is_real assert loggamma(z).is_real is False def tN(N, M): assert loggamma(1/x)._eval_nseries(x, n=N).getn() == M tN(0, 0) tN(1, 1) tN(2, 3) tN(3, 3) tN(4, 5) tN(5, 5) def test_polygamma_expansion(): # A. & S., pa. 259 and 260 assert polygamma(0, 1/x).nseries(x, n=3) == \ -log(x) - x/2 - x**2/12 + O(x**4) assert polygamma(1, 1/x).series(x, n=5) == \ x + x**2/2 + x**3/6 + O(x**5) assert polygamma(3, 1/x).nseries(x, n=11) == \ 2*x**3 + 3*x**4 + 2*x**5 - x**7 + 4*x**9/3 + O(x**11) def test_beta_function(): x, y = Symbol('x'), Symbol('y') assert beta(x, y) == gamma(x)*gamma(y)/gamma(x + y) assert beta(x, y) == beta(y, x) # Symmetric sympy-0.7.4.1/sympy/functions/special/tests/test_bsplines.py0000644000175000017500000000576312253362407024462 0ustar georgeskgeorgeskfrom sympy.functions import bspline_basis, bspline_basis_set from sympy import Piecewise, Interval from sympy import symbols, Rational x, y = symbols('x,y') def test_basic_degree_0(): d = 0 knots = range(5) splines = bspline_basis_set(d, knots, x) for i in range(len(splines)): assert splines[i] == Piecewise((1, Interval(i, i + 1) .contains(x)), (0, True)) def test_basic_degree_1(): d = 1 knots = range(5) splines = bspline_basis_set(d, knots, x) assert splines[0] == Piecewise( (x, Interval(0, 1, False, True).contains(x)), (2 - x, Interval(1, 2).contains(x)), (0, True)) assert splines[1] == Piecewise( (-1 + x, Interval(1, 2, False, True).contains(x)), (3 - x, Interval(2, 3).contains(x)), (0, True)) assert splines[2] == Piecewise( (-2 + x, Interval(2, 3, False, True).contains(x)), (4 - x, Interval(3, 4).contains(x)), (0, True)) def test_basic_degree_2(): d = 2 knots = range(5) splines = bspline_basis_set(d, knots, x) b0 = Piecewise((x**2/2, Interval(0, 1, False, True).contains(x)), (Rational( -3, 2) + 3*x - x**2, Interval(1, 2, False, True).contains(x)), (Rational(9, 2) - 3*x + x**2/2, Interval(2, 3).contains(x)), (0, True)) b1 = Piecewise( (Rational(1, 2) - x + x**2/2, Interval(1, 2, False, True).contains(x)), (Rational( -11, 2) + 5*x - x**2, Interval(2, 3, False, True).contains(x)), (8 - 4*x + x**2/2, Interval(3, 4).contains(x)), (0, True)) assert splines[0] == b0 assert splines[1] == b1 def test_basic_degree_3(): d = 3 knots = range(5) splines = bspline_basis_set(d, knots, x) b0 = Piecewise( (x**3/6, Interval(0, 1, False, True).contains(x)), (Rational(2, 3) - 2*x + 2*x**2 - x**3/2, Interval(1, 2, False, True).contains(x)), (Rational(-22, 3) + 10*x - 4*x**2 + x**3/2, Interval(2, 3, False, True).contains(x)), (Rational(32, 3) - 8*x + 2*x**2 - x**3/6, Interval(3, 4).contains(x)), (0, True) ) assert splines[0] == b0 def test_repeated_degree_1(): d = 1 knots = [0, 0, 1, 2, 2, 3, 4, 4] splines = bspline_basis_set(d, knots, x) assert splines[0] == Piecewise((1 - x, Interval(0, 1).contains(x)), (0, True)) assert splines[1] == Piecewise( (x, Interval(0, 1, False, True).contains(x)), (2 - x, Interval(1, 2).contains(x)), (0, True)) assert splines[2] == Piecewise((-1 + x, Interval(1, 2).contains(x) ), (0, True)) assert splines[3] == Piecewise((3 - x, Interval(2, 3).contains(x)), (0, True)) assert splines[4] == Piecewise( (-2 + x, Interval(2, 3, False, True).contains(x)), (4 - x, Interval(3, 4).contains(x)), (0, True)) assert splines[5] == Piecewise((-3 + x, Interval(3, 4).contains(x) ), (0, True)) sympy-0.7.4.1/sympy/functions/special/tests/test_spec_polynomials.py0000644000175000017500000002177012253362407026217 0ustar georgeskgeorgeskfrom sympy import ( Symbol, Dummy, diff, Derivative, Rational, roots, sympify, S, sqrt, hyper, cos, gamma, conjugate, factorial, pi, oo, zoo, binomial, Sum, RisingFactorial, legendre, assoc_legendre, chebyshevu, chebyshevt, chebyshevt_root, chebyshevu_root, laguerre, assoc_laguerre, laguerre_poly, hermite, gegenbauer, jacobi, jacobi_normalized) from sympy.utilities.pytest import raises x = Symbol('x') def test_jacobi(): n = Symbol("n") a = Symbol("a") b = Symbol("b") assert jacobi(0, a, b, x) == 1 assert jacobi(1, a, b, x) == a/2 - b/2 + x*(a/2 + b/2 + 1) assert jacobi(n, a, a, x) == RisingFactorial( a + 1, n)*gegenbauer(n, a + S(1)/2, x)/RisingFactorial(2*a + 1, n) assert jacobi(n, a, -a, x) == ((-1)**a*(-x + 1)**(-a/2)*(x + 1)**(a/2)*assoc_legendre(n, a, x)* factorial(-a + n)*gamma(a + n + 1)/(factorial(a + n)*gamma(n + 1))) assert jacobi(n, -b, b, x) == ((-x + 1)**(b/2)*(x + 1)**(-b/2)*assoc_legendre(n, b, x)* gamma(-b + n + 1)/gamma(n + 1)) assert jacobi(n, 0, 0, x) == legendre(n, x) assert jacobi(n, S.Half, S.Half, x) == RisingFactorial( S(3)/2, n)*chebyshevu(n, x)/factorial(n + 1) assert jacobi(n, -S.Half, -S.Half, x) == RisingFactorial( S(1)/2, n)*chebyshevt(n, x)/factorial(n) X = jacobi(n, a, b, x) assert isinstance(X, jacobi) assert jacobi(n, a, b, -x) == (-1)**n*jacobi(n, b, a, x) assert jacobi(n, a, b, 0) == 2**(-n)*gamma(a + n + 1)*hyper( (-b - n, -n), (a + 1,), -1)/(factorial(n)*gamma(a + 1)) assert jacobi(n, a, b, 1) == RisingFactorial(a + 1, n)/factorial(n) m = Symbol("m", positive=True) assert jacobi(m, a, b, oo) == oo*RisingFactorial(a + b + m + 1, m) assert conjugate(jacobi(m, a, b, x)) == \ jacobi(m, conjugate(a), conjugate(b), conjugate(x)) assert diff(jacobi(n, a, b, x), n) == Derivative(jacobi(n, a, b, x), n) assert diff(jacobi(n, a, b, x), x) == \ (a/2 + b/2 + n/2 + S(1)/2)*jacobi(n - 1, a + 1, b + 1, x) assert jacobi_normalized(n, a, b, x) == \ (jacobi(n, a, b, x)/sqrt(2**(a + b + 1)*gamma(a + n + 1)*gamma(b + n + 1) /((a + b + 2*n + 1)*factorial(n)*gamma(a + b + n + 1)))) def test_gegenbauer(): n = Symbol("n") a = Symbol("a") assert gegenbauer(0, a, x) == 1 assert gegenbauer(1, a, x) == 2*a*x assert gegenbauer(2, a, x) == -a + x**2*(2*a**2 + 2*a) assert gegenbauer(3, a, x) == \ x**3*(4*a**3/3 + 4*a**2 + 8*a/3) + x*(-2*a**2 - 2*a) assert gegenbauer(-1, a, x) == 0 assert gegenbauer(n, S(1)/2, x) == legendre(n, x) assert gegenbauer(n, 1, x) == chebyshevu(n, x) assert gegenbauer(n, -1, x) == 0 X = gegenbauer(n, a, x) assert isinstance(X, gegenbauer) assert gegenbauer(n, a, -x) == (-1)**n*gegenbauer(n, a, x) assert gegenbauer(n, a, 0) == 2**n*sqrt(pi) * \ gamma(a + n/2)/(gamma(a)*gamma(-n/2 + S(1)/2)*gamma(n + 1)) assert gegenbauer(n, a, 1) == gamma(2*a + n)/(gamma(2*a)*gamma(n + 1)) assert gegenbauer(n, Rational(3, 4), -1) == zoo m = Symbol("m", positive=True) assert gegenbauer(m, a, oo) == oo*RisingFactorial(a, m) assert conjugate(gegenbauer(n, a, x)) == \ gegenbauer(n, conjugate(a), conjugate(x)) assert diff(gegenbauer(n, a, x), n) == Derivative(gegenbauer(n, a, x), n) assert diff(gegenbauer(n, a, x), x) == 2*a*gegenbauer(n - 1, a + 1, x) def test_legendre(): raises(ValueError, lambda: legendre(-1, x)) assert legendre(0, x) == 1 assert legendre(1, x) == x assert legendre(2, x) == ((3*x**2 - 1)/2).expand() assert legendre(3, x) == ((5*x**3 - 3*x)/2).expand() assert legendre(4, x) == ((35*x**4 - 30*x**2 + 3)/8).expand() assert legendre(5, x) == ((63*x**5 - 70*x**3 + 15*x)/8).expand() assert legendre(6, x) == ((231*x**6 - 315*x**4 + 105*x**2 - 5)/16).expand() assert legendre(10, -1) == 1 assert legendre(11, -1) == -1 assert legendre(10, 1) == 1 assert legendre(11, 1) == 1 assert legendre(10, 0) != 0 assert legendre(11, 0) == 0 assert roots(legendre(4, x), x) == { sqrt(Rational(3, 7) - Rational(2, 35)*sqrt(30)): 1, -sqrt(Rational(3, 7) - Rational(2, 35)*sqrt(30)): 1, sqrt(Rational(3, 7) + Rational(2, 35)*sqrt(30)): 1, -sqrt(Rational(3, 7) + Rational(2, 35)*sqrt(30)): 1, } n = Symbol("n") X = legendre(n, x) assert isinstance(X, legendre) assert legendre(-n, x) == legendre(n - 1, x) assert legendre(n, -x) == (-1)**n*legendre(n, x) assert diff(legendre(n, x), x) == \ n*(x*legendre(n, x) - legendre(n - 1, x))/(x**2 - 1) assert diff(legendre(n, x), n) == Derivative(legendre(n, x), n) def test_assoc_legendre(): Plm = assoc_legendre Q = sqrt(1 - x**2) assert Plm(0, 0, x) == 1 assert Plm(1, 0, x) == x assert Plm(1, 1, x) == -Q assert Plm(2, 0, x) == (3*x**2 - 1)/2 assert Plm(2, 1, x) == -3*x*Q assert Plm(2, 2, x) == 3*Q**2 assert Plm(3, 0, x) == (5*x**3 - 3*x)/2 assert Plm(3, 1, x).expand() == (( 3*(1 - 5*x**2)/2 ).expand() * Q).expand() assert Plm(3, 2, x) == 15*x * Q**2 assert Plm(3, 3, x) == -15 * Q**3 # negative m assert Plm(1, -1, x) == -Plm(1, 1, x)/2 assert Plm(2, -2, x) == Plm(2, 2, x)/24 assert Plm(2, -1, x) == -Plm(2, 1, x)/6 assert Plm(3, -3, x) == -Plm(3, 3, x)/720 assert Plm(3, -2, x) == Plm(3, 2, x)/120 assert Plm(3, -1, x) == -Plm(3, 1, x)/12 n = Symbol("n") m = Symbol("m") X = Plm(n, m, x) assert isinstance(X, assoc_legendre) assert Plm(n, 0, x) == legendre(n, x) raises(ValueError, lambda: Plm(-1, 0, x)) raises(ValueError, lambda: Plm(0, 1, x)) def test_chebyshev(): assert chebyshevt(0, x) == 1 assert chebyshevt(1, x) == x assert chebyshevt(2, x) == 2*x**2 - 1 assert chebyshevt(3, x) == 4*x**3 - 3*x for n in range(1, 4): for k in range(n): z = chebyshevt_root(n, k) assert chebyshevt(n, z) == 0 raises(ValueError, lambda: chebyshevt_root(n, n)) for n in range(1, 4): for k in range(n): z = chebyshevu_root(n, k) assert chebyshevu(n, z) == 0 raises(ValueError, lambda: chebyshevu_root(n, n)) n = Symbol("n") X = chebyshevt(n, x) assert isinstance(X, chebyshevt) assert chebyshevt(n, -x) == (-1)**n*chebyshevt(n, x) assert chebyshevt(-n, x) == chebyshevt(n, x) assert chebyshevt(n, 0) == cos(pi*n/2) assert chebyshevt(n, 1) == 1 assert diff(chebyshevt(n, x), x) == n*chebyshevu(n - 1, x) X = chebyshevu(n, x) assert isinstance(X, chebyshevu) assert chebyshevu(n, -x) == (-1)**n*chebyshevu(n, x) assert chebyshevu(-n, x) == -chebyshevu(n - 2, x) assert chebyshevu(n, 0) == cos(pi*n/2) assert chebyshevu(n, 1) == n + 1 assert diff(chebyshevu(n, x), x) == \ (-x*chebyshevu(n, x) + (n + 1)*chebyshevt(n + 1, x))/(x**2 - 1) def test_hermite(): assert hermite(0, x) == 1 assert hermite(1, x) == 2*x assert hermite(2, x) == 4*x**2 - 2 assert hermite(3, x) == 8*x**3 - 12*x assert hermite(4, x) == 16*x**4 - 48*x**2 + 12 assert hermite(6, x) == 64*x**6 - 480*x**4 + 720*x**2 - 120 n = Symbol("n") assert hermite(n, x) == hermite(n, x) assert hermite(n, -x) == (-1)**n*hermite(n, x) assert hermite(-n, x) == hermite(-n, x) assert diff(hermite(n, x), x) == 2*n*hermite(n - 1, x) assert diff(hermite(n, x), n) == Derivative(hermite(n, x), n) def test_laguerre(): alpha = Symbol("alpha") # generalized Laguerre polynomials: assert assoc_laguerre(0, alpha, x) == 1 assert assoc_laguerre(1, alpha, x) == -x + alpha + 1 assert assoc_laguerre(2, alpha, x).expand() == \ (x**2/2 - (alpha + 2)*x + (alpha + 2)*(alpha + 1)/2).expand() assert assoc_laguerre(3, alpha, x).expand() == \ (-x**3/6 + (alpha + 3)*x**2/2 - (alpha + 2)*(alpha + 3)*x/2 + (alpha + 1)*(alpha + 2)*(alpha + 3)/6).expand() # Laguerre polynomials: assert assoc_laguerre(0, 0, x) == 1 assert assoc_laguerre(1, 0, x) == 1 - x assert assoc_laguerre(2, 0, x).expand() == 1 - 2*x + x**2/2 assert assoc_laguerre(3, 0, x).expand() == 1 - 3*x + 3*x**2/2 - x**3/6 # Test the lowest 10 polynomials with laguerre_poly, to make sure that it # works: for i in range(10): assert assoc_laguerre(i, 0, x).expand() == laguerre_poly(i, x) n = Symbol("n") X = laguerre(n, x) assert isinstance(X, laguerre) assert laguerre(n, 0) == 1 assert diff(laguerre(n, x), x) == -assoc_laguerre(n - 1, 1, x) m = Symbol("m") X = assoc_laguerre(n, m, x) assert isinstance(X, assoc_laguerre) assert assoc_laguerre(n, 0, x) == laguerre(n, x) assert assoc_laguerre(n, alpha, 0) == binomial(alpha + n, alpha) assert diff(assoc_laguerre(n, alpha, x), x) == \ -assoc_laguerre(n - 1, alpha + 1, x) #k = Dummy("k") #assert diff(assoc_laguerre(n, alpha, x), alpha) == Sum(assoc_laguerre(k, alpha, x)/(-alpha + n), (k, 0, n - 1)) sympy-0.7.4.1/sympy/functions/special/tests/__init__.py0000644000175000017500000000000012253362407023316 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/functions/special/tests/test_error_functions.py0000644000175000017500000005541412253362407026062 0ustar georgeskgeorgeskfrom sympy import ( symbols, expand, expand_func, nan, oo, Float, conjugate, diff, re, im, Abs, O, factorial, exp_polar, polar_lift, gruntz, limit, Symbol, I, integrate, S, sqrt, sin, cos, sinh, cosh, exp, log, pi, EulerGamma, erf, erfc, erfi, erf2, erfinv, erfcinv, erf2inv, gamma, uppergamma, loggamma, Ei, expint, E1, li, Li, Si, Ci, Shi, Chi, fresnels, fresnelc, hyper, meijerg) from sympy.functions.special.error_functions import _erfs, _eis from sympy.core.function import ArgumentIndexError from sympy.utilities.pytest import raises x, y, z = symbols('x,y,z') w = Symbol("w", real=True) n = Symbol("n", integer=True) def test_erf(): assert erf(nan) == nan assert erf(oo) == 1 assert erf(-oo) == -1 assert erf(0) == 0 assert erf(I*oo) == oo*I assert erf(-I*oo) == -oo*I assert erf(-2) == -erf(2) assert erf(-x*y) == -erf(x*y) assert erf(-x - y) == -erf(x + y) assert erf(erfinv(x)) == x assert erf(erfcinv(x)) == 1 - x assert erf(erf2inv(0, x)) == x assert erf(erf2inv(0, erf(erfcinv(1 - erf(erfinv(x)))))) == x assert erf(I).is_real is False assert erf(0).is_real is True assert conjugate(erf(z)) == erf(conjugate(z)) assert erf(x).as_leading_term(x) == 2*x/sqrt(pi) assert erf(1/x).as_leading_term(x) == erf(1/x) assert erf(z).rewrite('uppergamma') == sqrt(z**2)*erf(sqrt(z**2))/z assert erf(z).rewrite('erfc') == S.One - erfc(z) assert erf(z).rewrite('erfi') == -I*erfi(I*z) assert erf(z).rewrite('fresnels') == (1 + I)*(fresnelc(z*(1 - I)/sqrt(pi)) - I*fresnels(z*(1 - I)/sqrt(pi))) assert erf(z).rewrite('fresnelc') == (1 + I)*(fresnelc(z*(1 - I)/sqrt(pi)) - I*fresnels(z*(1 - I)/sqrt(pi))) assert erf(z).rewrite('hyper') == 2*z*hyper([S.Half], [3*S.Half], -z**2)/sqrt(pi) assert erf(z).rewrite('meijerg') == z*meijerg([S.Half], [], [0], [-S.Half], z**2)/sqrt(pi) assert erf(z).rewrite('expint') == sqrt(z**2)/z - z*expint(S.Half, z**2)/sqrt(S.Pi) assert limit(exp(x)*exp(x**2)*(erf(x + 1/exp(x)) - erf(x)), x, oo) == \ 2/sqrt(pi) assert limit((1 - erf(z))*exp(z**2)*z, z, oo) == 1/sqrt(pi) assert limit((1 - erf(x))*exp(x**2)*sqrt(pi)*x, x, oo) == 1 assert limit(((1 - erf(x))*exp(x**2)*sqrt(pi)*x - 1)*2*x**2, x, oo) == -1 assert erf(x).as_real_imag() == \ ((erf(re(x) - I*re(x)*Abs(im(x))/Abs(re(x)))/2 + erf(re(x) + I*re(x)*Abs(im(x))/Abs(re(x)))/2, I*(erf(re(x) - I*re(x)*Abs(im(x))/Abs(re(x))) - erf(re(x) + I*re(x)*Abs(im(x))/Abs(re(x)))) * re(x)*Abs(im(x))/(2*im(x)*Abs(re(x))))) raises(ArgumentIndexError, lambda: erf(x).fdiff(2)) def test_erf_series(): assert erf(x).series(x, 0, 7) == 2*x/sqrt(pi) - \ 2*x**3/3/sqrt(pi) + x**5/5/sqrt(pi) + O(x**7) def test_erf_evalf(): assert abs( erf(Float(2.0)) - 0.995322265 ) < 1E-8 # XXX def test__erfs(): assert _erfs(z).diff(z) == -2/sqrt(S.Pi) + 2*z*_erfs(z) assert _erfs(1/z).series(z) == \ z/sqrt(pi) - z**3/(2*sqrt(pi)) + 3*z**5/(4*sqrt(pi)) + O(z**6) assert expand(erf(z).rewrite('tractable').diff(z).rewrite('intractable')) \ == erf(z).diff(z) assert _erfs(z).rewrite("intractable") == (-erf(z) + 1)*exp(z**2) def test_erfc(): assert erfc(nan) == nan assert erfc(oo) == 0 assert erfc(-oo) == 2 assert erfc(0) == 1 assert erfc(I*oo) == -oo*I assert erfc(-I*oo) == oo*I assert erfc(-x) == S(2) - erfc(x) assert erfc(erfcinv(x)) == x assert erfc(I).is_real is False assert erfc(0).is_real is True assert conjugate(erfc(z)) == erfc(conjugate(z)) assert erfc(x).as_leading_term(x) == S.One assert erfc(1/x).as_leading_term(x) == erfc(1/x) assert erfc(z).rewrite('erf') == 1 - erf(z) assert erfc(z).rewrite('erfi') == 1 + I*erfi(I*z) assert erfc(z).rewrite('fresnels') == 1 - (1 + I)*(fresnelc(z*(1 - I)/sqrt(pi)) - I*fresnels(z*(1 - I)/sqrt(pi))) assert erfc(z).rewrite('fresnelc') == 1 - (1 + I)*(fresnelc(z*(1 - I)/sqrt(pi)) - I*fresnels(z*(1 - I)/sqrt(pi))) assert erfc(z).rewrite('hyper') == 1 - 2*z*hyper([S.Half], [3*S.Half], -z**2)/sqrt(pi) assert erfc(z).rewrite('meijerg') == 1 - z*meijerg([S.Half], [], [0], [-S.Half], z**2)/sqrt(pi) assert erfc(z).rewrite('uppergamma') == 1 - sqrt(z**2)*erf(sqrt(z**2))/z assert erfc(z).rewrite('expint') == S.One - sqrt(z**2)/z + z*expint(S.Half, z**2)/sqrt(S.Pi) assert erfc(x).as_real_imag() == \ ((erfc(re(x) - I*re(x)*Abs(im(x))/Abs(re(x)))/2 + erfc(re(x) + I*re(x)*Abs(im(x))/Abs(re(x)))/2, I*(erfc(re(x) - I*re(x)*Abs(im(x))/Abs(re(x))) - erfc(re(x) + I*re(x)*Abs(im(x))/Abs(re(x)))) * re(x)*Abs(im(x))/(2*im(x)*Abs(re(x))))) raises(ArgumentIndexError, lambda: erfc(x).fdiff(2)) def test_erfc_series(): assert erfc(x).series(x, 0, 7) == 1 - 2*x/sqrt(pi) + \ 2*x**3/3/sqrt(pi) - x**5/5/sqrt(pi) + O(x**7) def test_erfc_evalf(): assert abs( erfc(Float(2.0)) - 0.00467773 ) < 1E-8 # XXX def test_erfi(): assert erfi(nan) == nan assert erfi(oo) == S.Infinity assert erfi(-oo) == S.NegativeInfinity assert erfi(0) == S.Zero assert erfi(I*oo) == I assert erfi(-I*oo) == -I assert erfi(-x) == -erfi(x) assert erfi(I*erfinv(x)) == I*x assert erfi(I*erfcinv(x)) == I*(1 - x) assert erfi(I*erf2inv(0, x)) == I*x assert erfi(I).is_real is False assert erfi(0).is_real is True assert conjugate(erfi(z)) == erfi(conjugate(z)) assert erfi(z).rewrite('erf') == -I*erf(I*z) assert erfi(z).rewrite('erfc') == I*erfc(I*z) - I assert erfi(z).rewrite('fresnels') == (1 - I)*(fresnelc(z*(1 + I)/sqrt(pi)) - I*fresnels(z*(1 + I)/sqrt(pi))) assert erfi(z).rewrite('fresnelc') == (1 - I)*(fresnelc(z*(1 + I)/sqrt(pi)) - I*fresnels(z*(1 + I)/sqrt(pi))) assert erfi(z).rewrite('hyper') == 2*z*hyper([S.Half], [3*S.Half], z**2)/sqrt(pi) assert erfi(z).rewrite('meijerg') == z*meijerg([S.Half], [], [0], [-S.Half], -z**2)/sqrt(pi) assert erfi(z).rewrite('uppergamma') == (sqrt(-z**2)/z*(uppergamma(S.Half, -z**2)/sqrt(S.Pi) - S.One)) assert erfi(z).rewrite('expint') == sqrt(-z**2)/z - z*expint(S.Half, -z**2)/sqrt(S.Pi) assert erfi(x).as_real_imag() == \ ((erfi(re(x) - I*re(x)*Abs(im(x))/Abs(re(x)))/2 + erfi(re(x) + I*re(x)*Abs(im(x))/Abs(re(x)))/2, I*(erfi(re(x) - I*re(x)*Abs(im(x))/Abs(re(x))) - erfi(re(x) + I*re(x)*Abs(im(x))/Abs(re(x)))) * re(x)*Abs(im(x))/(2*im(x)*Abs(re(x))))) raises(ArgumentIndexError, lambda: erfi(x).fdiff(2)) def test_erfi_series(): assert erfi(x).series(x, 0, 7) == 2*x/sqrt(pi) + \ 2*x**3/3/sqrt(pi) + x**5/5/sqrt(pi) + O(x**7) def test_erfi_evalf(): assert abs( erfi(Float(2.0)) - 18.5648024145756 ) < 1E-13 # XXX def test_erf2(): assert erf2(0, 0) == S.Zero assert erf2(x, x) == S.Zero assert erf2(nan, 0) == nan assert erf2(-oo, y) == erf(y) + 1 assert erf2( oo, y) == erf(y) - 1 assert erf2( x, oo) == 1 - erf(x) assert erf2( x,-oo) == -1 - erf(x) assert erf2(x, erf2inv(x, y)) == y assert erf2(-x, -y) == -erf2(x,y) assert erf2(-x, y) == erf(y) + erf(x) assert erf2( x, -y) == -erf(y) - erf(x) assert erf2(x, y).rewrite('fresnels') == erf(y).rewrite(fresnels)-erf(x).rewrite(fresnels) assert erf2(x, y).rewrite('fresnelc') == erf(y).rewrite(fresnelc)-erf(x).rewrite(fresnelc) assert erf2(x, y).rewrite('hyper') == erf(y).rewrite(hyper)-erf(x).rewrite(hyper) assert erf2(x, y).rewrite('meijerg') == erf(y).rewrite(meijerg)-erf(x).rewrite(meijerg) assert erf2(x, y).rewrite('uppergamma') == erf(y).rewrite(uppergamma) - erf(x).rewrite(uppergamma) assert erf2(x, y).rewrite('expint') == erf(y).rewrite(expint)-erf(x).rewrite(expint) assert erf2(I, 0).is_real is False assert erf2(0, 0).is_real is True #assert conjugate(erf2(x, y)) == erf2(conjugate(x), conjugate(y)) assert erf2(x, y).rewrite('erf') == erf(y) - erf(x) assert erf2(x, y).rewrite('erfc') == erfc(x) - erfc(y) assert erf2(x, y).rewrite('erfi') == I*(erfi(I*x) - erfi(I*y)) raises(ArgumentIndexError, lambda: erfi(x).fdiff(3)) def test_erfinv(): assert erfinv(0) == 0 assert erfinv(1) == S.Infinity assert erfinv(nan) == S.NaN assert erfinv(erf(w)) == w assert erfinv(erf(-w)) == -w assert erfinv(x).diff() == sqrt(pi)*exp(erfinv(x)**2)/2 assert erfinv(z).rewrite('erfcinv') == erfcinv(1-z) def test_erfinv_evalf(): assert abs( erfinv(Float(0.2)) - 0.179143454621292 ) < 1E-13 def test_erfcinv(): assert erfcinv(1) == 0 assert erfcinv(0) == S.Infinity assert erfcinv(nan) == S.NaN assert erfcinv(x).diff() == -sqrt(pi)*exp(erfcinv(x)**2)/2 assert erfcinv(z).rewrite('erfinv') == erfinv(1-z) def test_erf2inv(): assert erf2inv(0, 0) == S.Zero assert erf2inv(0, 1) == S.Infinity assert erf2inv(1, 0) == S.One assert erf2inv(0, y) == erfinv(y) assert erf2inv(oo,y) == erfcinv(-y) assert erf2inv(x, y).diff(x) == exp(-x**2 + erf2inv(x, y)**2) assert erf2inv(x, y).diff(y) == sqrt(pi)*exp(erf2inv(x, y)**2)/2 # NOTE we multiply by exp_polar(I*pi) and need this to be on the principal # branch, hence take x in the lower half plane (d=0). def mytn(expr1, expr2, expr3, x, d=0): from sympy.utilities.randtest import test_numerically, random_complex_number subs = {} for a in expr1.free_symbols: if a != x: subs[a] = random_complex_number() return expr2 == expr3 and test_numerically(expr1.subs(subs), expr2.subs(subs), x, d=d) def mytd(expr1, expr2, x): from sympy.utilities.randtest import test_derivative_numerically, \ random_complex_number subs = {} for a in expr1.free_symbols: if a != x: subs[a] = random_complex_number() return expr1.diff(x) == expr2 and test_derivative_numerically(expr1.subs(subs), x) def tn_branch(func, s=None): from sympy import I, pi, exp_polar from random import uniform def fn(x): if s is None: return func(x) return func(s, x) c = uniform(1, 5) expr = fn(c*exp_polar(I*pi)) - fn(c*exp_polar(-I*pi)) eps = 1e-15 expr2 = fn(-c + eps*I) - fn(-c - eps*I) return abs(expr.n() - expr2.n()).n() < 1e-10 def test_ei(): pos = Symbol('p', positive=True) neg = Symbol('n', negative=True) assert Ei(-pos) == Ei(polar_lift(-1)*pos) - I*pi assert Ei(neg) == Ei(polar_lift(neg)) - I*pi assert tn_branch(Ei) assert mytd(Ei(x), exp(x)/x, x) assert mytn(Ei(x), Ei(x).rewrite(uppergamma), -uppergamma(0, x*polar_lift(-1)) - I*pi, x) assert mytn(Ei(x), Ei(x).rewrite(expint), -expint(1, x*polar_lift(-1)) - I*pi, x) assert Ei(x).rewrite(expint).rewrite(Ei) == Ei(x) assert Ei(x*exp_polar(2*I*pi)) == Ei(x) + 2*I*pi assert Ei(x*exp_polar(-2*I*pi)) == Ei(x) - 2*I*pi assert mytn(Ei(x), Ei(x).rewrite(Shi), Chi(x) + Shi(x), x) assert mytn(Ei(x*polar_lift(I)), Ei(x*polar_lift(I)).rewrite(Si), Ci(x) + I*Si(x) + I*pi/2, x) assert Ei(log(x)).rewrite(li) == li(x) assert Ei(2*log(x)).rewrite(li) == li(x**2) assert gruntz(Ei(x+exp(-x))*exp(-x)*x, x, oo) == 1 assert Ei(x).series(x) == EulerGamma + log(x) + x + x**2/4 + \ x**3/18 + x**4/96 + x**5/600 + O(x**6) def test_expint(): assert mytn(expint(x, y), expint(x, y).rewrite(uppergamma), y**(x - 1)*uppergamma(1 - x, y), x) assert mytd( expint(x, y), -y**(x - 1)*meijerg([], [1, 1], [0, 0, 1 - x], [], y), x) assert mytd(expint(x, y), -expint(x - 1, y), y) assert mytn(expint(1, x), expint(1, x).rewrite(Ei), -Ei(x*polar_lift(-1)) + I*pi, x) assert expint(-4, x) == exp(-x)/x + 4*exp(-x)/x**2 + 12*exp(-x)/x**3 \ + 24*exp(-x)/x**4 + 24*exp(-x)/x**5 assert expint(-S(3)/2, x) == \ exp(-x)/x + 3*exp(-x)/(2*x**2) - 3*sqrt(pi)*erf(sqrt(x))/(4*x**S('5/2')) \ + 3*sqrt(pi)/(4*x**S('5/2')) assert tn_branch(expint, 1) assert tn_branch(expint, 2) assert tn_branch(expint, 3) assert tn_branch(expint, 1.7) assert tn_branch(expint, pi) assert expint(y, x*exp_polar(2*I*pi)) == \ x**(y - 1)*(exp(2*I*pi*y) - 1)*gamma(-y + 1) + expint(y, x) assert expint(y, x*exp_polar(-2*I*pi)) == \ x**(y - 1)*(exp(-2*I*pi*y) - 1)*gamma(-y + 1) + expint(y, x) assert expint(2, x*exp_polar(2*I*pi)) == 2*I*pi*x + expint(2, x) assert expint(2, x*exp_polar(-2*I*pi)) == -2*I*pi*x + expint(2, x) assert expint(1, x).rewrite(Ei).rewrite(expint) == expint(1, x) assert mytn(E1(x), E1(x).rewrite(Shi), Shi(x) - Chi(x), x) assert mytn(E1(polar_lift(I)*x), E1(polar_lift(I)*x).rewrite(Si), -Ci(x) + I*Si(x) - I*pi/2, x) assert mytn(expint(2, x), expint(2, x).rewrite(Ei).rewrite(expint), -x*E1(x) + exp(-x), x) assert mytn(expint(3, x), expint(3, x).rewrite(Ei).rewrite(expint), x**2*E1(x)/2 + (1 - x)*exp(-x)/2, x) assert expint(S(3)/2, z).nseries(z) == \ 2 + 2*z - z**2/3 + z**3/15 - z**4/84 + z**5/540 - \ 2*sqrt(pi)*sqrt(z) + O(z**6) assert E1(z).series(z) == -EulerGamma - log(z) + z - \ z**2/4 + z**3/18 - z**4/96 + z**5/600 + O(z**6) assert expint(4, z).series(z) == S(1)/3 - z/2 + z**2/2 + \ z**3*(log(z)/6 - S(11)/36 + EulerGamma/6) - z**4/24 + \ z**5/240 + O(z**6) def test__eis(): assert _eis(z).diff(z) == -_eis(z) + 1/z assert _eis(1/z).series(z) == \ z + z**2 + 2*z**3 + 6*z**4 + 24*z**5 + O(z**6) assert Ei(z).rewrite('tractable') == exp(z)*_eis(z) assert li(z).rewrite('tractable') == z*_eis(log(z)) assert _eis(z).rewrite('intractable') == exp(-z)*Ei(z) assert expand(li(z).rewrite('tractable').diff(z).rewrite('intractable')) \ == li(z).diff(z) assert expand(Ei(z).rewrite('tractable').diff(z).rewrite('intractable')) \ == Ei(z).diff(z) assert _eis(z).series(z, n=3) == EulerGamma + log(z) + z*(-log(z) - \ EulerGamma + 1) + z**2*(log(z)/2 - S(3)/4 + EulerGamma/2) + O(z**3*log(z)) def tn_arg(func): def test(arg, e1, e2): from random import uniform v = uniform(1, 5) v1 = func(arg*x).subs(x, v).n() v2 = func(e1*v + e2*1e-15).n() return abs(v1 - v2).n() < 1e-10 return test(exp_polar(I*pi/2), I, 1) and \ test(exp_polar(-I*pi/2), -I, 1) and \ test(exp_polar(I*pi), -1, I) and \ test(exp_polar(-I*pi), -1, -I) def test_li(): z = Symbol("z") zr = Symbol("z", real=True) zp = Symbol("z", positive=True) zn = Symbol("z", negative=True) assert li(0) == 0 assert li(1) == -oo assert li(oo) == oo assert isinstance(li(z), li) assert diff(li(z), z) == 1/log(z) assert conjugate(li(z)) == li(conjugate(z)) assert conjugate(li(-zr)) == li(-zr) assert conjugate(li(-zp)) == conjugate(li(-zp)) assert conjugate(li(zn)) == conjugate(li(zn)) assert li(z).rewrite(Li) == Li(z) + li(2) assert li(z).rewrite(Ei) == Ei(log(z)) assert li(z).rewrite(uppergamma) == (-log(1/log(z))/2 - log(-log(z)) + log(log(z))/2 - expint(1, -log(z))) assert li(z).rewrite(Si) == (-log(I*log(z)) - log(1/log(z))/2 + log(log(z))/2 + Ci(I*log(z)) + Shi(log(z))) assert li(z).rewrite(Ci) == (-log(I*log(z)) - log(1/log(z))/2 + log(log(z))/2 + Ci(I*log(z)) + Shi(log(z))) assert li(z).rewrite(Shi) == (-log(1/log(z))/2 + log(log(z))/2 + Chi(log(z)) - Shi(log(z))) assert li(z).rewrite(Chi) == (-log(1/log(z))/2 + log(log(z))/2 + Chi(log(z)) - Shi(log(z))) assert li(z).rewrite(hyper) ==(log(z)*hyper((1, 1), (2, 2), log(z)) - log(1/log(z))/2 + log(log(z))/2 + EulerGamma) assert li(z).rewrite(meijerg) == (-log(1/log(z))/2 - log(-log(z)) + log(log(z))/2 - meijerg(((), (1,)), ((0, 0), ()), -log(z))) assert gruntz(1/li(z), z, oo) == 0 def test_Li(): assert Li(2) == 0 assert Li(oo) == oo assert isinstance(Li(z), Li) assert diff(Li(z), z) == 1/log(z) assert gruntz(1/Li(z), z, oo) == 0 assert Li(z).rewrite(li) == li(z) - li(2) def test_si(): assert Si(I*x) == I*Shi(x) assert Shi(I*x) == I*Si(x) assert Si(-I*x) == -I*Shi(x) assert Shi(-I*x) == -I*Si(x) assert Si(-x) == -Si(x) assert Shi(-x) == -Shi(x) assert Si(exp_polar(2*pi*I)*x) == Si(x) assert Si(exp_polar(-2*pi*I)*x) == Si(x) assert Shi(exp_polar(2*pi*I)*x) == Shi(x) assert Shi(exp_polar(-2*pi*I)*x) == Shi(x) assert Si(oo) == pi/2 assert Si(-oo) == -pi/2 assert Shi(oo) == oo assert Shi(-oo) == -oo assert mytd(Si(x), sin(x)/x, x) assert mytd(Shi(x), sinh(x)/x, x) assert mytn(Si(x), Si(x).rewrite(Ei), -I*(-Ei(x*exp_polar(-I*pi/2))/2 + Ei(x*exp_polar(I*pi/2))/2 - I*pi) + pi/2, x) assert mytn(Si(x), Si(x).rewrite(expint), -I*(-expint(1, x*exp_polar(-I*pi/2))/2 + expint(1, x*exp_polar(I*pi/2))/2) + pi/2, x) assert mytn(Shi(x), Shi(x).rewrite(Ei), Ei(x)/2 - Ei(x*exp_polar(I*pi))/2 + I*pi/2, x) assert mytn(Shi(x), Shi(x).rewrite(expint), expint(1, x)/2 - expint(1, x*exp_polar(I*pi))/2 - I*pi/2, x) assert tn_arg(Si) assert tn_arg(Shi) assert Si(x).nseries(x, n=8) == \ x - x**3/18 + x**5/600 - x**7/35280 + O(x**9) assert Shi(x).nseries(x, n=8) == \ x + x**3/18 + x**5/600 + x**7/35280 + O(x**9) assert Si(sin(x)).nseries(x, n=5) == x - 2*x**3/9 + 17*x**5/450 + O(x**6) assert Si(x).nseries(x, 1, n=3) == \ Si(1) + x*sin(1) + x**2*(-sin(1)/2 + cos(1)/2) + O(x**3) def test_ci(): m1 = exp_polar(I*pi) m1_ = exp_polar(-I*pi) pI = exp_polar(I*pi/2) mI = exp_polar(-I*pi/2) assert Ci(m1*x) == Ci(x) + I*pi assert Ci(m1_*x) == Ci(x) - I*pi assert Ci(pI*x) == Chi(x) + I*pi/2 assert Ci(mI*x) == Chi(x) - I*pi/2 assert Chi(m1*x) == Chi(x) + I*pi assert Chi(m1_*x) == Chi(x) - I*pi assert Chi(pI*x) == Ci(x) + I*pi/2 assert Chi(mI*x) == Ci(x) - I*pi/2 assert Ci(exp_polar(2*I*pi)*x) == Ci(x) + 2*I*pi assert Chi(exp_polar(-2*I*pi)*x) == Chi(x) - 2*I*pi assert Chi(exp_polar(2*I*pi)*x) == Chi(x) + 2*I*pi assert Ci(exp_polar(-2*I*pi)*x) == Ci(x) - 2*I*pi assert Ci(oo) == 0 assert Ci(-oo) == I*pi assert Chi(oo) == oo assert Chi(-oo) == oo assert mytd(Ci(x), cos(x)/x, x) assert mytd(Chi(x), cosh(x)/x, x) assert mytn(Ci(x), Ci(x).rewrite(Ei), Ei(x*exp_polar(-I*pi/2))/2 + Ei(x*exp_polar(I*pi/2))/2, x) assert mytn(Chi(x), Chi(x).rewrite(Ei), Ei(x)/2 + Ei(x*exp_polar(I*pi))/2 - I*pi/2, x) assert tn_arg(Ci) assert tn_arg(Chi) from sympy import O, EulerGamma, log, limit assert Ci(x).nseries(x, n=4) == \ EulerGamma + log(x) - x**2/4 + x**4/96 + O(x**5) assert Chi(x).nseries(x, n=4) == \ EulerGamma + log(x) + x**2/4 + x**4/96 + O(x**5) assert limit(log(x) - Ci(2*x), x, 0) == -log(2) - EulerGamma def test_fresnel(): assert fresnels(0) == 0 assert fresnels(oo) == S.Half assert fresnels(-oo) == -S.Half assert fresnels(z) == fresnels(z) assert fresnels(-z) == -fresnels(z) assert fresnels(I*z) == -I*fresnels(z) assert fresnels(-I*z) == I*fresnels(z) assert conjugate(fresnels(z)) == fresnels(conjugate(z)) assert fresnels(z).diff(z) == sin(pi*z**2/2) assert fresnels(z).rewrite(erf) == (S.One + I)/4 * ( erf((S.One + I)/2*sqrt(pi)*z) - I*erf((S.One - I)/2*sqrt(pi)*z)) assert fresnels(z).rewrite(hyper) == \ pi*z**3/6 * hyper([S(3)/4], [S(3)/2, S(7)/4], -pi**2*z**4/16) assert fresnels(z).series(z, n=15) == \ pi*z**3/6 - pi**3*z**7/336 + pi**5*z**11/42240 + O(z**15) assert fresnels(w).is_real is True assert fresnels(z).as_real_imag() == \ ((fresnels(re(z) - I*re(z)*Abs(im(z))/Abs(re(z)))/2 + fresnels(re(z) + I*re(z)*Abs(im(z))/Abs(re(z)))/2, I*(fresnels(re(z) - I*re(z)*Abs(im(z))/Abs(re(z))) - fresnels(re(z) + I*re(z)*Abs(im(z))/Abs(re(z)))) * re(z)*Abs(im(z))/(2*im(z)*Abs(re(z))))) assert fresnels(2 + 3*I).as_real_imag() == ( fresnels(2 + 3*I)/2 + fresnels(2 - 3*I)/2, I*(fresnels(2 - 3*I) - fresnels(2 + 3*I))/2 ) assert expand_func(integrate(fresnels(z), z)) == \ z*fresnels(z) + cos(pi*z**2/2)/pi assert fresnels(z).rewrite(meijerg) == sqrt(2)*pi*z**(S(9)/4) * \ meijerg(((), (1,)), ((S(3)/4,), (S(1)/4, 0)), -pi**2*z**4/16)/(2*(-z)**(S(3)/4)*(z**2)**(S(3)/4)) assert fresnelc(0) == 0 assert fresnelc(oo) == S.Half assert fresnelc(-oo) == -S.Half assert fresnelc(z) == fresnelc(z) assert fresnelc(-z) == -fresnelc(z) assert fresnelc(I*z) == I*fresnelc(z) assert fresnelc(-I*z) == -I*fresnelc(z) assert conjugate(fresnelc(z)) == fresnelc(conjugate(z)) assert fresnelc(z).diff(z) == cos(pi*z**2/2) assert fresnelc(z).rewrite(erf) == (S.One - I)/4 * ( erf((S.One + I)/2*sqrt(pi)*z) + I*erf((S.One - I)/2*sqrt(pi)*z)) assert fresnelc(z).rewrite(hyper) == \ z * hyper([S.One/4], [S.One/2, S(5)/4], -pi**2*z**4/16) assert fresnelc(z).series(z, n=15) == \ z - pi**2*z**5/40 + pi**4*z**9/3456 - pi**6*z**13/599040 + O(z**15) assert fresnelc(w).is_real is True assert fresnelc(z).as_real_imag() == \ ((fresnelc(re(z) - I*re(z)*Abs(im(z))/Abs(re(z)))/2 + fresnelc(re(z) + I*re(z)*Abs(im(z))/Abs(re(z)))/2, I*(fresnelc(re(z) - I*re(z)*Abs(im(z))/Abs(re(z))) - fresnelc(re(z) + I*re(z)*Abs(im(z))/Abs(re(z)))) * re(z)*Abs(im(z))/(2*im(z)*Abs(re(z))))) assert fresnelc(2 + 3*I).as_real_imag() == ( fresnelc(2 - 3*I)/2 + fresnelc(2 + 3*I)/2, I*(fresnelc(2 - 3*I) - fresnelc(2 + 3*I))/2 ) assert expand_func(integrate(fresnelc(z), z)) == \ z*fresnelc(z) - sin(pi*z**2/2)/pi assert fresnelc(z).rewrite(meijerg) == sqrt(2)*pi*z**(S(3)/4) * \ meijerg(((), (1,)), ((S(1)/4,), (S(3)/4, 0)), -pi**2*z**4/16)/(2*(-z)**(S(1)/4)*(z**2)**(S(1)/4)) from sympy.utilities.randtest import test_numerically test_numerically(re(fresnels(z)), fresnels(z).as_real_imag()[0], z) test_numerically(im(fresnels(z)), fresnels(z).as_real_imag()[1], z) test_numerically(fresnels(z), fresnels(z).rewrite(hyper), z) test_numerically(fresnels(z), fresnels(z).rewrite(meijerg), z) test_numerically(re(fresnelc(z)), fresnelc(z).as_real_imag()[0], z) test_numerically(im(fresnelc(z)), fresnelc(z).as_real_imag()[1], z) test_numerically(fresnelc(z), fresnelc(z).rewrite(hyper), z) test_numerically(fresnelc(z), fresnelc(z).rewrite(meijerg), z) sympy-0.7.4.1/sympy/functions/special/__init__.py0000644000175000017500000000032212253362407022163 0ustar georgeskgeorgeskfrom . import gamma_functions from . import error_functions from . import zeta_functions from . import tensor_functions from . import delta_functions from . import elliptic_integrals from . import polynomials sympy-0.7.4.1/sympy/functions/__init__.py0000644000175000017500000000476412253362407020561 0ustar georgeskgeorgesk"""A functions module, includes all the standard functions. Combinatorial - factorial, fibonacci, harmonic, bernoulli... Elementary - hyperbolic, trigonometric, exponential, floor and ceiling, sqrt... Special - gamma, zeta,spherical harmonics... """ from sympy.functions.combinatorial.factorials import (factorial, factorial2, rf, ff, binomial, RisingFactorial, FallingFactorial, subfactorial) from sympy.functions.combinatorial.numbers import (fibonacci, lucas, harmonic, bernoulli, bell, euler, catalan) from sympy.functions.elementary.miscellaneous import (sqrt, root, Min, Max, Id, real_root, cbrt) from sympy.functions.elementary.complexes import (re, im, sign, Abs, conjugate, arg, polar_lift, periodic_argument, unbranched_argument, principal_branch, transpose, adjoint) from sympy.functions.elementary.trigonometric import (tan, cos, sin, asin, acos, atan, atan2, acot, cot, sec, csc) from sympy.functions.elementary.exponential import (exp_polar, exp, log, LambertW) from sympy.functions.elementary.hyperbolic import (sinh, cosh, tanh, coth, asinh, acosh, atanh, acoth) from sympy.functions.elementary.integers import floor, ceiling from sympy.functions.elementary.piecewise import Piecewise, piecewise_fold from sympy.functions.special.error_functions import (erf, erfc, erfi, erf2, erfinv, erfcinv, erf2inv, Ei, expint, E1, li, Li, Si, Ci, Shi, Chi, fresnels, fresnelc) from sympy.functions.special.gamma_functions import (gamma, lowergamma, uppergamma, polygamma, loggamma, digamma, trigamma, beta) from sympy.functions.special.zeta_functions import (dirichlet_eta, zeta, lerchphi, polylog) from sympy.functions.special.tensor_functions import (Eijk, LeviCivita, KroneckerDelta) from sympy.functions.special.delta_functions import DiracDelta, Heaviside from sympy.functions.special.bsplines import bspline_basis, bspline_basis_set from sympy.functions.special.bessel import (besselj, bessely, besseli, besselk, hankel1, hankel2, jn, yn, jn_zeros) from sympy.functions.special.hyper import hyper, meijerg from sympy.functions.special.polynomials import (legendre, assoc_legendre, hermite, chebyshevt, chebyshevu, chebyshevu_root, chebyshevt_root, laguerre, assoc_laguerre, gegenbauer, jacobi, jacobi_normalized) from sympy.functions.special.spherical_harmonics import Ynm, Ynm_c, Znm from sympy.functions.special.elliptic_integrals import (elliptic_k, elliptic_f, elliptic_e, elliptic_pi) ln = log sympy-0.7.4.1/sympy/core/0000755000175000017500000000000012253362407015355 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/core/expr.py0000644000175000017500000032236612253362407016721 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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, xrange from sympy.mpmath.libmp import mpf_log, prec_to_dps from collections import defaultdict 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 # *************** # * 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 __long__ = __int__ 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_number and dif.is_real is False: raise TypeError("Invalid comparison of complex %s" % dif) if dif.is_nonnegative is not None and \ dif.is_nonnegative is not 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_number and dif.is_real is False: raise TypeError("Invalid comparison of complex %s" % dif) if dif.is_nonpositive is not None and \ dif.is_nonpositive is not 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_number and dif.is_real is False: raise TypeError("Invalid comparison of complex %s" % dif) if dif.is_positive is not None and \ dif.is_positive is not 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_number and dif.is_real is False: raise TypeError("Invalid comparison of complex %s" % dif) if dif.is_negative is not None and \ dif.is_negative is not 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 def is_number(self): """Returns True if 'self' has no free symbols. It will be faster than `if not self.free_symbols`, however, since `is_number` will fail as soon as it hits a free symbol. Examples ======== >>> from sympy import log, Integral >>> from sympy.abc import x >>> 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 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() False >>> Sum(x, (x, 1, n)).is_constant(y) True >>> Sum(x, (x, 1, n)).is_constant(n) 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() in (True, False) # could be 0 or 1 True """ 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) if simplify: deriv = deriv.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 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. """ from sympy.simplify.simplify import nsimplify, simplify from sympy.solvers.solvers import solve from sympy.polys.polyerrors import NotAlgebraic from sympy.polys.numberfields import minimal_polynomial 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 = factor_terms((self - other).simplify(), radical=True) if not diff: return True if not diff.has(Add): # 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: return False if constant is None and (diff.free_symbols or not diff.is_number): # e.g. unless the right simplification is done, a symbolic # zero is possible (see expression of issue 3730: without # simplification constant will be None). return if constant is True: ndiff = diff._random() if ndiff: return False # sometimes we can use a simplified result to give a clue as to # what the expression should be; if the expression is *not* zero # then we should have been able to compute that and so now # we can just consider the cases where the approximation appears # to be zero -- we try to prove it via minimal_polynomial. if diff.is_number: approx = diff.nsimplify() if not approx: # try to prove via self-consistency surds = [s for s in diff.atoms(Pow) if s.args[0].is_Integer] # it seems to work better to try big ones first surds.sort(key=lambda x: -x.args[0]) for s in surds: try: # simplify is False here -- this expression has already # been identified as being hard to identify as zero; # we will handle the checking ourselves using nsimplify # to see if we are in the right ballpark or not and if so # *then* the simplification will be attempted. sol = solve(diff, s, check=False, simplify=False) if sol: if s in sol: return True if any(nsimplify(si, [s]) == s and simplify(si) == s for si in sol): return True except NotImplementedError: pass # try to prove with minimal_polynomial but know when # *not* to use this or else it can take a long time. # Pernici noted the following: # >>> q = -73*sqrt(3) + 1 + 128*sqrt(5) + 1315*sqrt(2) # >>> p = expand(q**3)**Rational(1, 3) # >>> minimal_polynomial(p - q) # hangs for at least 15 minutes if False: # change False to condition that assures non-hang try: mp = minimal_polynomial(diff) if mp.is_Symbol: return True return False except NotAlgebraic: pass # 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) if n2 is None: raise AttributeError if n2._prec == 1: # no significance raise AttributeError except (AttributeError, ValueError): 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) if n2 is None: raise AttributeError if n2._prec == 1: # no significance raise AttributeError except (AttributeError, ValueError): 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) or A.has(S.Infinity): 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) or B.has(S.Infinity): 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.orderings 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 def as_ordered_factors(self, order=None): """Return list of ordered factors (if Mul) else [self].""" return [self] 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 >>> (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 ] 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 def removeO(self): """Removes the additive O(..) symbol if there is one""" return self def getO(self): """Returns the additive O(..) symbol if there is one, else None.""" return None 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) 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) 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] is not 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] def coeff(self, x, n=1, right=False): """ Returns the coefficient from the term(s) containing ``x**n`` or None. 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: separate the expression into a coefficient and factor as_coeff_Add: separate the additive constant from an expression as_coeff_Mul: separate the multiplicative constant from an expression as_independent: separate x-dependent terms/factors from others sympy.polys.polytools.coeff_monomial: efficiently find the single coefficient of a monomial in Poly sympy.polys.polytools.nth: like coeff_monomial but powers of monomial terms are used 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(range(4))] [1, 3, 3, 2] >>> eq -= 2 >>> [eq.coeff(x, i) for i in reversed(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 """ 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 xrange(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 xrange(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 xrange(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 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 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. Examples ======== >>> from sympy import E, pi, sin, I, Poly >>> from sympy.abc import x >>> E.as_coefficient(E) 1 >>> (2*E).as_coefficient(E) 2 >>> (2*sin(E)*E).as_coefficient(E) Two terms have E in them so a sum is returned. (If one were desiring the coefficient of the term exactly matching E then the constant from the returned expression could be selected. Or, for greater precision, a method of Poly can be used to indicate the desired term from which the coefficient is desired.) >>> (2*E + x*E).as_coefficient(E) x + 2 >>> _.args[0] # just want the exact match 2 >>> p = Poly(2*E + x*E); p Poly(x*E + 2*E, x, E, domain='ZZ') >>> p.coeff_monomial(E) 2 >>> p.nth(0,1) 2 Since the following cannot be written as a product containing E as a factor, None is returned. (If the coefficient ``2*x`` is desired then the ``coeff`` method should be used.) >>> (2*E*x + x).as_coefficient(E) >>> (2*E*x + x).coeff(E) 2*x >>> (E*(x + 1) + x).as_coefficient(E) >>> (2*pi*I).as_coefficient(pi*I) 2 >>> (2*I).as_coefficient(pi*I) See Also ======== coeff: return sum of terms have a given factor as_coeff_Add: separate the additive constant from an expression as_coeff_Mul: separate the multiplicative constant from an expression as_independent: separate x-dependent terms/factors from others sympy.polys.polytools.coeff_monomial: efficiently find the single coefficient of a monomial in Poly sympy.polys.polytools.nth: like coeff_monomial but powers of monomial terms are used """ r = self.extract_multiplicatively(expr) if r and not r.has(expr): return r 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) 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)) 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 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 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,) 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,) 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 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 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 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) 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.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) 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 = list(filter(None, arg_signs)) 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() 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 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 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 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 >>> 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_algebraic_expr(). """ 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) def _eval_is_algebraic_expr(self, syms): if self.free_symbols.intersection(syms) == set([]): return True return False def is_algebraic_expr(self, *syms): ''' This tests whether a given expression is algebraic or not, 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 "algebraic expressions" with symbolic exponents. This is a simple extension to the is_rational_function, including rational exponentiation. Examples ======== >>> from sympy import Symbol, sqrt >>> x = Symbol('x') >>> sqrt(1 + x).is_rational_function() False >>> sqrt(1 + x).is_algebraic_expr() True This function does not attempt any nontrivial simplifications that may result in an expression that does not appear to be an algebraic expression to become one. >>> from sympy import sin, factor >>> a = sqrt(sin(x)**2 + 2*sin(x) + 1)/(sin(x) + 1) >>> a.is_algebraic_expr(x) False >>> factor(a).is_algebraic_expr() True See Also ======== is_rational_function() References ========== - http://en.wikipedia.org/wiki/Algebraic_expression ''' if syms: syms = set(map(sympify, syms)) else: syms = self.free_symbols if syms.intersection(self.free_symbols) == set([]): # constant algebraic expression return True else: return self._eval_is_algebraic_expr(syms) ################################################################################### ##################### SERIES, LEADING TERM, LIMIT, ORDER METHODS ################## ################################################################################### def series(self, x=None, x0=0, n=6, dir="+", logx=None): """ 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 a generator 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 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) return s.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='+', logx=logx) 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 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, bounded=True) rv = self.subs(x, xpos).series(xpos, x0, n, dir, logx=logx) if n is None: return (s.subs(xpos, x) for s in rv) else: return rv.subs(xpos, x) if n is not None: # nseries handling s1 = self._eval_nseries(x, n=n, logx=logx) 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 if n != 0: s1 += o.subs(x, x**C.Rational(n, ngot)) else: s1 += C.Order(1, x) 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=logx) newn = s1.getn() if newn != ngot: ndo = n + (n - ngot)*more/(newn - ngot) s1 = self._eval_nseries(x, n=ndo, logx=logx) while s1.getn() < n: s1 = self._eval_nseries(x, n=ndo, logx=logx) ndo += 1 break else: raise ValueError('Could not calculate %s terms for %s' % (str(n), self)) s1 += C.Order(x**n, x) o = s1.getO() s1 = s1.removeO() else: o = C.Order(x**n, x) 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)*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: break yielded += do return yield_lseries(self.removeO()._eval_lseries(x, logx=logx)) def taylor_term(self, 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) _x = C.Dummy('x') return self.subs(x, _x).diff(_x, n).subs(_x, x).subs(x, 0) * x**n / C.factorial(n) def lseries(self, x=None, x0=0, dir='+', logx=None): """ 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, logx=logx) def _eval_lseries(self, x, logx=None): # 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=logx) 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=logx) e = series.removeO() yield e while 1: while 1: n += 1 series = self._eval_nseries(x, n=n, logx=logx).removeO() if e != series: break yield series - e e = series 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. The optional ``logx`` parameter can be used to replace any log(x) in the returned series with a symbolic value to avoid evaluating log(x) at 0. A symbol to use in place of log(x) should be provided. 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(). Examples ======== >>> from sympy import sin, log, Symbol >>> from sympy.abc import x, y >>> sin(x).nseries(x, 0, 6) x - x**3/6 + x**5/120 + O(x**6) >>> log(x+1).nseries(x, 0, 5) x - x**2/2 + x**3/3 - x**4/4 + O(x**5) Handling of the ``logx`` parameter --- in the following example the expansion fails since ``sin`` does not have an asymptotic expansion at -oo (the limit of log(x) as x approaches 0): >>> e = sin(log(x)) >>> e.nseries(x, 0, 6) Traceback (most recent call last): ... PoleError: ... ... >>> logx = Symbol('logx') >>> e.nseries(x, 0, 6, logx=logx) sin(logx) In the following example, the expansion works but gives only an Order term unless the ``logx`` parameter is used: >>> e = x**y >>> e.nseries(x, 0, 2) O(log(x)**2) >>> e.nseries(x, 0, 2, logx=logx) exp(logx*y) """ if x and not x in self.free_symbols: return self if x is None or x0 or dir != '+': # {see XPOS above} or (x.is_positive == x.is_negative == 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) ) def limit(self, x, xlim, dir='+'): """ Compute limit x->xlim. """ from sympy.series.limits import limit return limit(self, x, xlim, dir) def compute_leading_term(self, x, logx=None): """ as_leading_term is only allowed for results of .series() This is a wrapper to compute a series first. """ from sympy.series.gruntz import calculate_series if self.removeO() == 0: return self if logx is None: d = C.Dummy('logx') s = calculate_series(self, x, d).subs(d, C.log(x)) else: s = calculate_series(self, x, logx) return s.as_leading_term(x) @cacheit 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 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 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 def as_coeff_Mul(self, rational=False): """Efficiently extract the coefficient of a product. """ return S.One, self 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, hint): newexpr = getattr(expr, hint)(**hints) if newexpr != expr: return (newexpr, True) return (expr, hit) @cacheit 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(hints.keys(), key=_expand_hint_key): use_hint = hints[hint] if use_hint: hint = '_eval_expand_' + hint expr, hit = Expr._expand_hint(expr, hint, deep=deep, **hints) while True: was = expr if hints.get('multinomial', False): expr, _ = Expr._expand_hint( expr, '_eval_expand_multinomial', deep=deep, **hints) if hints.get('mul', False): expr, _ = Expr._expand_hint( expr, '_eval_expand_mul', deep=deep, **hints) if hints.get('log', False): expr, _ = Expr._expand_hint( expr, '_eval_expand_log', deep=deep, **hints) if expr == was: break 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 #################### ########################################################################### def integrate(self, *args, **kwargs): """See the integrate function in sympy.integrals""" from sympy.integrals import integrate return integrate(self, *args, **kwargs) 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) 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) def separate(self, deep=False, force=False): """See the separate function in sympy.simplify""" from sympy.simplify import separate return separate(self, deep) 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) def together(self, *args, **kwargs): """See the together function in sympy.polys""" from sympy.polys import together return together(self, *args, **kwargs) def apart(self, x=None, **args): """See the apart function in sympy.polys""" from sympy.polys import apart return apart(self, x, **args) def ratsimp(self): """See the ratsimp function in sympy.simplify""" from sympy.simplify import ratsimp return ratsimp(self) def trigsimp(self, **args): """See the trigsimp function in sympy.simplify""" from sympy.simplify import trigsimp return trigsimp(self, **args) def radsimp(self): """See the radsimp function in sympy.simplify""" from sympy.simplify import radsimp return radsimp(self) def powsimp(self, deep=False, combine='all'): """See the powsimp function in sympy.simplify""" from sympy.simplify import powsimp return powsimp(self, deep, combine) def combsimp(self): """See the combsimp function in sympy.simplify""" from sympy.simplify import combsimp return combsimp(self) def factor(self, *gens, **args): """See the factor() function in sympy.polys.polytools""" from sympy.polys import factor return factor(self, *gens, **args) def refine(self, assumption=True): """See the refine function in sympy.assumptions""" from sympy.assumptions import refine return refine(self, assumption) def cancel(self, *gens, **args): """See the cancel function in sympy.polys""" from sympy.polys import cancel return cancel(self, *gens, **args) def invert(self, g): """See the invert function in sympy.polys""" from sympy.polys import invert return invert(self, g) 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() 6. + 3.*I The round method has a chopping effect: >>> (2*pi + I/10).round() 6. >>> (pi/10 + 2*I).round() 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 """ 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) 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_is_algebraic_expr(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 sympy-0.7.4.1/sympy/core/rules.py0000644000175000017500000000302012253362407017054 0ustar georgeskgeorgesk""" Replacement rules. """ from __future__ import print_function, division class Transform(object): """ Immutable mapping that can be used as a generic transformation rule. Parameters ---------- transform : callable Computes the value corresponding to any key. filter : callable, optional If supplied, specifies which objects are in the mapping. Examples -------- >>> from sympy.core.rules import Transform >>> from sympy.abc import x This Transform will return, as a value, one more than the key: >>> add1 = Transform(lambda x: x + 1) >>> add1[1] 2 >>> add1[x] x + 1 By default, all values are considered to be in the dictionary. If a filter is supplied, only the objects for which it returns True are considered as being in the dictionary: >>> add1_odd = Transform(lambda x: x + 1, lambda x: x%2 == 1) >>> 2 in add1_odd False >>> add1_odd.get(2, 0) 0 >>> 3 in add1_odd True >>> add1_odd[3] 4 >>> add1_odd.get(3, 0) 4 """ def __init__(self, transform, filter=lambda x: True): self._transform = transform self._filter = filter def __contains__(self, item): return self._filter(item) def __getitem__(self, key): if self._filter(key): return self._transform(key) else: raise KeyError(key) def get(self, item, default=None): if item in self: return self[item] else: return default sympy-0.7.4.1/sympy/core/alphabets.py0000644000175000017500000000047312253362407017676 0ustar georgeskgeorgeskfrom __future__ import print_function, division greeks = ('alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta', 'eta', 'theta', 'iota', 'kappa', 'lambda', 'mu', 'nu', 'xi', 'omicron', 'pi', 'rho', 'sigma', 'tau', 'upsilon', 'phi', 'chi', 'psi', 'omega') sympy-0.7.4.1/sympy/core/sets.py0000644000175000017500000011234712253362407016715 0ustar georgeskgeorgeskfrom __future__ import print_function, division from itertools import product 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, with_metaclass from sympy.mpmath import mpi, mpf from sympy.assumptions import ask from sympy.logic.boolalg import And, Or, true, false from sympy.utilities import default_sort_key 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 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 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) 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 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 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 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) 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 """ c = self._contains(sympify(other, strict=True)) if c in (true, false): # TODO: would we want to return the Basic type here? return bool(c) return c def _contains(self, other): raise NotImplementedError("(%s)._contains(%s)" % (self, other)) def subset(self, other): """ Returns True if 'other' is a subset of 'self'. >>> from sympy import Interval >>> Interval(0, 1).subset(Interval(0, 0.5)) True >>> Interval(0, 1, left_open=True).subset(Interval(0, 1)) False """ if isinstance(other, Set): return self.intersect(other) == other else: raise ValueError("Unknown argument '%s'" % other) @property 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 def _eval_imageset(self, f): from sympy.sets.fancysets import ImageSet return ImageSet(f, self) @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 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(map(flatten, arg.args), []) else: return [arg] elif iterable(arg): return sum(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: 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 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 >>> 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 ========== """ is_Interval = True is_real = True def __new__(cls, start, end, left_open=False, right_open=False): start = _sympify(start) end = _sympify(end) inftys = [S.Infinity, S.NegativeInfinity] # Only allow real intervals (use symbols with 'is_real=True'). if not (start.is_real or start in inftys) or not (end.is_real or end in inftys): 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 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 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 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 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 def _eval_imageset(self, f): # Cut out 0, perform image, add back in image of 0 if self.contains(0) == True: return imageset(f, self - FiniteSet(0)) + imageset(f, FiniteSet(0)) from sympy.functions.elementary.miscellaneous import Min, Max # TODO: manage left_open and right_open better in case of # non-comparable left/right (e.g. Interval(x, y)) _left, _right = f(self.left), f(self.right) left, right = Min(_left, _right), Max(_left, _right) if _right == left: # switch happened left_open, right_open = self.right_open, self.left_open else: left_open, right_open = self.left_open, self.right_open return Interval(left, right, left_open, right_open) @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 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 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") def as_relational(self, symbol): """Rewrite an interval in terms of inequalities and logic operators. """ other = sympify(symbol) if self.right_open: right = other < self.end else: right = other <= self.end if right is True: if self.left_open: return other > self.start else: return other >= self.start if self.left_open: left = self.start < other else: left = self.start <= other return And(left, right) @property def free_symbols(self): return self.start.free_symbols | self.end.free_symbols 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 ========== """ 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(map(flatten, arg.args), []) else: return [arg] if iterable(arg): # and not isinstance(arg, Set) (implicit) return sum(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 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 def _eval_imageset(self, f): return Union(imageset(f, arg) for arg in self.args) 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) 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 ========== """ 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(map(flatten, arg.args), []) else: return [arg] if iterable(arg): # and not isinstance(arg, Set) (implicit) return sum(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 _eval_imageset(self, f): return Intersection(imageset(f, arg) for arg in self.args) 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 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) 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]) class EmptySet(with_metaclass(Singleton, Set)): """ 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([]) def _eval_imageset(self, f): return self class UniversalSet(with_metaclass(Singleton, Set)): """ 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 class FiniteSet(Set, EvalfMixin): """ Represents a finite set of discrete numbers Examples ======== >>> from sympy import FiniteSet >>> 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 def _eval_imageset(self, f): return FiniteSet(*map(f, self)) @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) 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) def imageset(*args): """ Image of set under transformation ``f`` .. math:: { f(x) | x \in self } Examples ======== >>> from sympy import Interval, Symbol, imageset >>> x = Symbol('x') >>> imageset(x, 2*x, Interval(0, 2)) [0, 4] >>> imageset(lambda x: 2*x, Interval(0, 2)) [0, 4] See Also: ImageSet """ if len(args) == 3: from sympy import Lambda f = Lambda(*args[:2]) else: f = args[0] set = args[-1] return set._eval_imageset(f) sympy-0.7.4.1/sympy/core/mul.py0000644000175000017500000015043612253362407016535 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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, reduce, xrange from sympy.core.expr import Expr # 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 # Key for sorting commutative args in canonical order _args_sortkey = cmp_to_key(Basic.compare) def _mulsort(args): # in-place sorting of args args.sort(key=_args_sortkey) def _unevaluated_Mul(*args): """Return a well-formed unevaluated Mul: Numbers are collected and put in slot 0 and args are sorted. Use this when args have changed but you still want to return an unevaluated Mul. Examples ======== >>> from sympy.core.mul import _unevaluated_Mul as uMul >>> from sympy import S, sqrt, Mul >>> from sympy.abc import x >>> a = uMul(*[S(3.0), x, S(2)]) >>> a.args[0] 6.00000000000000 >>> a.args[1] x Beyond the Number being in slot 0, there is no other flattening of arguments, but two unevaluated Muls with the same arguments will always compare as equal during testing: >>> m = uMul(sqrt(2), sqrt(3)) >>> m == uMul(sqrt(3), sqrt(2)) True >>> m == Mul(*m.args) False """ args = list(args) newargs = [] ncargs = [] co = S.One while args: a = args.pop() if a.is_Mul: c, nc = a.args_cnc() args.extend(c) if nc: ncargs.append(Mul._from_args(nc)) elif a.is_Number: co *= a else: newargs.append(a) _mulsort(newargs) if co is not S.One: newargs.insert(0, co) if ncargs: newargs.append(Mul._from_args(ncargs)) return Mul._from_args(newargs) class Mul(Expr, AssocOp): __slots__ = [] is_Mul = True #identity = S.One # cyclic import, so defined in numbers.py @classmethod 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 not a.is_zero and a.is_Rational: r, b = b.as_coeff_Mul() if b.is_Add: if r is not S.One: # 2-arg hack # leave the Mul as a Mul rv = [cls(a*r, b, evaluate=False)], [], None elif 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)] _addsort(bargs) 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 * ... c_powers = [] # (base,exp) n # e.g. (x,n) for x num_exp = [] # (num-base, exp) y # e.g. (3, y) for ... * 3 * ... neg1e = S.Zero # exponent on -1 extracted from Number-based Pow and I 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: neg1e += S.Half continue elif o.is_commutative: # e # o = b b, e = o.as_base_exp() # y # 3 if o.is_Pow: if 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 elif b is S.ImaginaryUnit and e.is_Rational: neg1e += e/2 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) # 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 common_b.items(): for di, li in d.items(): d[di] = Add(*li) for b, e in common_b.items(): for t, c in 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 inv_exp_dict.items(): inv_exp_dict[e] = cls(*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 = cls(*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 = bi.gcd(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] = cls(*b) # handle -1 and I if neg1e: # treat I as (-1)**(1/2) and compute -1's total exponent p, q = neg1e.as_numer_denom() # if the integer part is odd, extract -1 n, p = divmod(p, q) if n % 2: coeff = -coeff # if it's a multiple of 1/2 extract I if q == 2: c_part.append(S.ImaginaryUnit) elif p: # see if there is any positive base this power of # -1 can join neg1e = Rational(p, q) for e, b in pnew.items(): if e == neg1e and b.is_positive: pnew[e] = -b break else: # keep it separate; we've already evaluated it as # much as possible so evaluate=False c_part.append(Pow(S.NegativeOne, neg1e, evaluate=False)) # 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 _mulsort(c_part) # 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 (not nc_part and 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 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 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): from sympy import expand_mul other = [] coeff = S.One addterms = S.One for a in self.args: if a.is_real or a.is_imaginary: coeff *= a elif a.is_commutative: # search for complex conjugate pairs: for i, x in enumerate(other): if x == a.conjugate(): coeff *= C.Abs(x)**2 del other[i] break else: if a.is_Add: addterms *= a else: other.append(a) else: other.append(a) addre, addim = expand_mul(addterms, deep=False).as_real_imag() m = self.func(*other) if hints.get('ignore') == m: return None else: if coeff.is_real: return (coeff*(C.re(m)*addre - C.im(m)*addim), coeff*(C.im(m)*addre + C.re(m)*addim)) else: re = - C.im(coeff)*C.im(m) im = C.im(coeff)*C.re(m) return (re*addre - im*addim, re*addim + im*addre) @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 # 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: n, d = [i._eval_expand_mul(**hints) if i.is_Mul else i for i in (n, d)] expr = n/d if not expr.is_Mul: return expr 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 = self.func(*plain) if sums: terms = self.func._expandsums(sums) args = [] for term in terms: t = self.func(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 xrange(len(terms)): t = terms[i].diff(s) if t is S.Zero: continue factors.append(self.func(*(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 = self.func(*c1) if isinstance(a, AssocOp): repl_dict = a._matches_commutative(self.func(*c2), repl_dict, old) else: repl_dict = a.matches(self.func(*c2), repl_dict) if repl_dict: a = self.func(*nc1) if isinstance(a, self.func): repl_dict = a._matches(self.func(*nc2), repl_dict) else: repl_dict = a.matches(self.func(*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 lhs.func(*a)/rhs.func(*b) return lhs/rhs def as_powers_dict(self): d = defaultdict(int) for term in self.args: b, e = term.as_base_exp() 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 self.func(*numers), self.func(*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 self.func(*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) def _eval_is_algebraic_expr(self, syms): return all(term._eval_is_algebraic_expr(syms) for term in self.args) _eval_is_bounded = lambda self: self._eval_template_is_attr('is_bounded') _eval_is_commutative = lambda self: self._eval_template_is_attr( 'is_commutative') _eval_is_rational = lambda self: self._eval_template_is_attr('is_rational', when_multiple=None) def _eval_is_integer(self): is_rational = self.is_rational if is_rational: n, d = self.as_numer_denom() if d is S.One: return True elif d is S(2): return n.is_even elif is_rational is False: return False 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, acc = True, 1 for t in self.args: if not t.is_integer: return None elif t.is_even: r = False elif t.is_integer: if r is False: pass elif acc != 1 and (acc + t).is_odd: r = False elif t.is_odd is None: r = None acc = t 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.functions.elementary.complexes import sign from sympy.ntheory.factor_ import multiplicity from sympy.simplify.simplify import powdenest, fraction if not old.is_Mul: return None # try keep replacement literal so -2*x doesn't replace 4*x if old.args[0].is_Number and old.args[0] < 0: if self.args[0].is_Number: if self.args[0] < 0: return self._subs(-old, -new) return None 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 and abs(co_old) != 1: mult = S(multiplicity(abs(co_old), co_self)) c.pop(co_self) if co_old in c: c[co_old] += mult else: 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 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(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*self.func(*margs)*self.func(*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] res = powsimp(self.func(*terms).expand(), combine='exp', deep=True) if res.has(C.Order): res += C.Order(x**n, x) return res def _eval_as_leading_term(self, x): return self.func(*[t.as_leading_term(x) for t in self.args]) def _eval_conjugate(self): return self.func(*[t.conjugate() for t in self.args]) def _eval_transpose(self): return self.func(*[t.transpose() for t in self.args[::-1]]) def _eval_adjoint(self): return self.func(*[t.adjoint() for t in self.args[::-1]]) def _sage_(self): s = 1 for x in self.args: s *= x._sage_() return s 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, self.func(*args) 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() 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(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, sign=False): """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. If ``sign`` is True, allow a coefficient of -1 to remain factored out. 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 >>> _keep_coeff(S(-1), x + y) -x - y >>> _keep_coeff(S(-1), x + y, sign=True) -(x + y) """ 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 and not 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 def expand_2arg(e): from sympy.simplify.simplify import bottom_up def do(e): if e.is_Mul: c, r = e.as_coeff_Mul() if c.is_Number and r.is_Add: return _unevaluated_Add(*[c*ri for ri in r.args]) return e return bottom_up(e, do) from .numbers import Rational from .power import Pow from .add import Add, _addsort, _unevaluated_Add sympy-0.7.4.1/sympy/core/relational.py0000644000175000017500000004017212253362407020065 0ustar georgeskgeorgeskfrom __future__ import print_function, division from .basic import S from .expr import Expr from .evalf import EvalfMixin from .symbol import Symbol 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', ) 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) 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, '==') 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, '!=') 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, '<') 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, '<=') 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, '>') 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 = cls.ValidRelationOperator[ rop ] except KeyError: msg = "Invalid relational operator symbol: '%r'" raise ValueError(msg % repr(rop)) diff = S.NaN if isinstance(lhs, Expr) and isinstance(rhs, Expr): diff = lhs - rhs if not (diff is S.NaN or diff.has(Symbol)): know = diff.equals(0, failing_expression=True) if know is True: # exclude failing expression case diff = S.Zero elif know is False: diff = diff.n() if rop_cls is Equality: if (lhs == rhs) is True or (diff == S.Zero) is True: return True elif diff is S.NaN: pass elif diff.is_Number or diff.is_Float: return False elif lhs.is_real is not rhs.is_real and \ lhs.is_real is not None and \ rhs.is_real is not None: return False elif rop_cls is Unequality: if (lhs == rhs) is True or (diff == S.Zero) is True: return False elif diff is S.NaN: pass elif diff.is_Number or diff.is_Float: return True elif lhs.is_real is not rhs.is_real and \ lhs.is_real is not None and \ rhs.is_real is not None: return True elif diff.is_Number and diff.is_real: return rop_cls._eval_relation(diff, 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)) def __nonzero__(self): raise TypeError("symbolic boolean expression has no truth value.") __bool__ = __nonzero__ 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) 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) 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] 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 Traceback (most recent call last): ... TypeError: symbolic boolean expression has no truth value. 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 ) >>> 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. The bool of the --Than operators will raise TypeError on purpose, because SymPy cannot determine the mathematical ordering of symbolic expressions. 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) TypeError Because of the "and" added at step 2, the statement gets turned into a weak ternary statement, and the first object's __nonzero__ method will raise TypeError. Thus, creating a chained inequality is not possible. 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 `_ "It right 0 < x < 1 ?" `Issue 2960 `_ """ rel_op = '>=' __slots__ = () @classmethod def _eval_relation(cls, lhs, rhs): return lhs >= rhs class LessThan(_Less): __doc__ = GreaterThan.__doc__ __slots__ = () rel_op = '<=' @classmethod def _eval_relation(cls, lhs, rhs): return lhs <= rhs class StrictGreaterThan(_Greater): __doc__ = GreaterThan.__doc__ __slots__ = () rel_op = '>' @classmethod def _eval_relation(cls, lhs, rhs): return lhs > rhs class StrictLessThan(_Less): __doc__ = GreaterThan.__doc__ __slots__ = () rel_op = '<' @classmethod def _eval_relation(cls, lhs, rhs): return lhs < rhs # 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, } sympy-0.7.4.1/sympy/core/operations.py0000644000175000017500000004033312253362407020115 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core.core import C from sympy.core.sympify import _sympify, sympify from sympy.core.basic import Basic, _aresame from sympy.core.cache import cacheit from sympy.core.compatibility import ordered, xrange from sympy.core.logic import fuzzy_and class AssocOp(Basic): """ Associative operations, can separate noncommutative and commutative parts. (a op b) op c == a op (b op c) == a op b op c. Base class for Add and Mul. This is an abstract base class, concrete derived classes must define the attribute `identity`. """ # for performance reason, we don't let is_commutative go to assumptions, # and keep it right here __slots__ = ['is_commutative'] @cacheit def __new__(cls, *args, **options): args = list(map(_sympify, args)) args = [a for a in args if a is not cls.identity] if not options.pop('evaluate', True): return cls._from_args(args) if len(args) == 0: return cls.identity if len(args) == 1: return args[0] c_part, nc_part, order_symbols = cls.flatten(args) is_commutative = not nc_part obj = cls._from_args(c_part + nc_part, is_commutative) if order_symbols is not None: return C.Order(obj, *order_symbols) return obj @classmethod def _from_args(cls, args, is_commutative=None): """Create new instance with already-processed args""" if len(args) == 0: return cls.identity elif len(args) == 1: return args[0] obj = super(AssocOp, cls).__new__(cls, *args) if is_commutative is None: is_commutative = fuzzy_and(a.is_commutative for a in args) obj.is_commutative = is_commutative return obj def _new_rawargs(self, *args, **kwargs): """Create new instance of own class with args exactly as provided by caller but returning the self class identity if args is empty. This is handy when we want to optimize things, e.g. >>> from sympy import Mul, S >>> from sympy.abc import x, y >>> e = Mul(3, x, y) >>> e.args (3, x, y) >>> Mul(*e.args[1:]) x*y >>> e._new_rawargs(*e.args[1:]) # the same as above, but faster x*y Note: use this with caution. There is no checking of arguments at all. This is best used when you are rebuilding an Add or Mul after simply removing one or more terms. If modification which result, for example, in extra 1s being inserted (as when collecting an expression's numerators and denominators) they will not show up in the result but a Mul will be returned nonetheless: >>> m = (x*y)._new_rawargs(S.One, x); m x >>> m == x False >>> m.is_Mul True Another issue to be aware of is that the commutativity of the result is based on the commutativity of self. If you are rebuilding the terms that came from a commutative object then there will be no problem, but if self was non-commutative then what you are rebuilding may now be commutative. Although this routine tries to do as little as possible with the input, getting the commutativity right is important, so this level of safety is enforced: commutativity will always be recomputed if self is non-commutative and kwarg `reeval=False` has not been passed. """ if kwargs.pop('reeval', True) and self.is_commutative is False: is_commutative = None else: is_commutative = self.is_commutative return self._from_args(args, is_commutative) @classmethod def flatten(cls, seq): """Return seq so that none of the elements are of type `cls`. This is the vanilla routine that will be used if a class derived from AssocOp does not define its own flatten routine.""" # apply associativity, no commutativity property is used new_seq = [] while seq: o = seq.pop() if o.__class__ is cls: # classes must match exactly seq.extend(o.args) else: new_seq.append(o) # c_part, nc_part, order_symbols return [], new_seq, None def _matches_commutative(self, expr, repl_dict={}, old=False): """ Matches Add/Mul "pattern" to an expression "expr". repl_dict ... a dictionary of (wild: expression) pairs, that get returned with the results This function is the main workhorse for Add/Mul. For instance: >>> from sympy import symbols, Wild, sin >>> a = Wild("a") >>> b = Wild("b") >>> c = Wild("c") >>> x, y, z = symbols("x y z") >>> (a+sin(b)*c)._matches_commutative(x+sin(y)*z) {a_: x, b_: y, c_: z} In the example above, "a+sin(b)*c" is the pattern, and "x+sin(y)*z" is the expression. The repl_dict contains parts that were already matched. For example here: >>> (x+sin(b)*c)._matches_commutative(x+sin(y)*z, repl_dict={a: x}) {a_: x, b_: y, c_: z} the only function of the repl_dict is to return it in the result, e.g. if you omit it: >>> (x+sin(b)*c)._matches_commutative(x+sin(y)*z) {b_: y, c_: z} the "a: x" is not returned in the result, but otherwise it is equivalent. """ # handle simple patterns if self == expr: return repl_dict d = self._matches_simple(expr, repl_dict) if d is not None: return d # eliminate exact part from pattern: (2+a+w1+w2).matches(expr) -> (w1+w2).matches(expr-a-2) from .function import WildFunction from .symbol import Wild wild_part = [] exact_part = [] for p in ordered(self.args): if p.has(Wild, WildFunction) and (not expr.has(p)): # not all Wild should stay Wilds, for example: # (w2+w3).matches(w1) -> (w1+w3).matches(w1) -> w3.matches(0) wild_part.append(p) else: exact_part.append(p) if exact_part: exact = self.func(*exact_part) free = expr.free_symbols if free and (exact.free_symbols - free): # there are symbols in the exact part that are not # in the expr; but if there are no free symbols, let # the matching continue return None newpattern = self.func(*wild_part) newexpr = self._combine_inverse(expr, exact) if not old and (expr.is_Add or expr.is_Mul): if newexpr.count_ops() > expr.count_ops(): return None return newpattern.matches(newexpr, repl_dict) # now to real work ;) i = 0 saw = set() while expr not in saw: saw.add(expr) expr_list = (self.identity,) + tuple(ordered(self.make_args(expr))) for last_op in reversed(expr_list): for w in reversed(wild_part): d1 = w.matches(last_op, repl_dict) if d1 is not None: d2 = self.xreplace(d1).matches(expr, d1) if d2 is not None: return d2 if i == 0: if self.is_Mul: # make e**i look like Mul if expr.is_Pow and expr.exp.is_Integer: if expr.exp > 0: expr = C.Mul(*[expr.base, expr.base**(expr.exp - 1)], evaluate=False) else: expr = C.Mul(*[1/expr.base, expr.base**(expr.exp + 1)], evaluate=False) i += 1 continue elif self.is_Add: # make i*e look like Add c, e = expr.as_coeff_Mul() if abs(c) > 1: if c > 0: expr = C.Add(*[e, (c - 1)*e], evaluate=False) else: expr = C.Add(*[-e, (c + 1)*e], evaluate=False) i += 1 continue # try collection on non-Wild symbols from sympy.simplify.simplify import collect was = expr did = set() for w in reversed(wild_part): c, w = w.as_coeff_mul(Wild) free = c.free_symbols - did if free: did.update(free) expr = collect(expr, free) if expr != was: i += 0 continue break # if we didn't continue, there is nothing more to do return def _has_matcher(self): """Helper for .has()""" def _ncsplit(expr): # this is not the same as args_cnc because here # we don't assume expr is a Mul -- hence deal with args -- # and always return a set. cpart, ncpart = [], [] for arg in expr.args: if arg.is_commutative: cpart.append(arg) else: ncpart.append(arg) return set(cpart), ncpart c, nc = _ncsplit(self) cls = self.__class__ def is_in(expr): if expr == self: return True elif not isinstance(expr, Basic): return False elif isinstance(expr, cls): _c, _nc = _ncsplit(expr) if (c & _c) == c: if not nc: return True elif len(nc) <= len(_nc): for i in xrange(len(_nc) - len(nc)): if _nc[i:i + len(nc)] == nc: return True return False return is_in def _eval_template_is_attr(self, is_attr, when_multiple=False): # return True if all elements have the property; # False if one doesn't have the property; and # if more than one doesn't have property, return # False if when_multiple = False # None if when_multiple is not False quick = when_multiple is None multi = False for t in self.args: a = getattr(t, is_attr) if a is True: continue if a is None: return if quick and multi: return None multi = True return not multi def _eval_evalf(self, prec): """ Evaluate the parts of self that are numbers; if the whole thing was a number with no functions it would have been evaluated, but it wasn't so we must judiciously extract the numbers and reconstruct the object. This is *not* simply replacing numbers with evaluated numbers. Nunmbers should be handled in the largest pure-number expression as possible. So the code below separates ``self`` into number and non-number parts and evaluates the number parts and walks the args of the non-number part recursively (doing the same thing). """ x, tail = self.as_independent(C.Symbol) if tail is not self.identity: # here, we have a number so we just call to _evalf with prec; # prec is not the same as n, it is the binary precision so # that's why we don't call to evalf. x = x._evalf(prec) if x is not self.identity else self.identity args = [] for a in self.func.make_args(tail): # here we call to _eval_evalf since we don't know what we # are dealing with and all other _eval_evalf routines should # be doing the same thing (i.e. taking binary prec and # finding the evalf-able args) newa = a._eval_evalf(prec) if newa is None: args.append(a) else: args.append(newa) if not _aresame(tuple(args), self.func.make_args(tail)): tail = self.func(*args) return self.func(x, tail) # this is the same as above, but there were no pure-number args to # deal with args = [] for a in self.args: newa = a._eval_evalf(prec) if newa is None: args.append(a) else: args.append(newa) if not _aresame(tuple(args), self.args): return self.func(*args) return self @classmethod def make_args(cls, expr): """ Return a sequence of elements `args` such that cls(*args) == expr >>> from sympy import Symbol, Mul, Add >>> x, y = map(Symbol, 'xy') >>> Mul.make_args(x*y) (x, y) >>> Add.make_args(x*y) (x*y,) >>> set(Add.make_args(x*y + y)) == set([y, x*y]) True """ if isinstance(expr, cls): return expr.args else: return (expr,) class ShortCircuit(Exception): pass class LatticeOp(AssocOp): """ Join/meet operations of an algebraic lattice[1]. These binary operations are associative (op(op(a, b), c) = op(a, op(b, c))), commutative (op(a, b) = op(b, a)) and idempotent (op(a, a) = op(a) = a). Common examples are AND, OR, Union, Intersection, max or min. They have an identity element (op(identity, a) = a) and an absorbing element conventionally called zero (op(zero, a) = zero). This is an abstract base class, concrete derived classes must declare attributes zero and identity. All defining properties are then respected. >>> from sympy import Integer >>> from sympy.core.operations import LatticeOp >>> class my_join(LatticeOp): ... zero = Integer(0) ... identity = Integer(1) >>> my_join(2, 3) == my_join(3, 2) True >>> my_join(2, my_join(3, 4)) == my_join(2, 3, 4) True >>> my_join(0, 1, 4, 2, 3, 4) 0 >>> my_join(1, 2) 2 References: [1] - http://en.wikipedia.org/wiki/Lattice_%28order%29 """ is_commutative = True def __new__(cls, *args, **options): args = (_sympify(arg) for arg in args) try: _args = frozenset(cls._new_args_filter(args)) except ShortCircuit: return sympify(cls.zero) if not _args: return sympify(cls.identity) elif len(_args) == 1: return set(_args).pop() else: # XXX in almost every other case for __new__, *_args is # passed along, but the expectation here is for _args obj = super(AssocOp, cls).__new__(cls, _args) obj._argset = _args return obj @classmethod def _new_args_filter(cls, arg_sequence, call_cls=None): """Generator filtering args""" cls = call_cls or cls for arg in arg_sequence: 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 make_args(cls, expr): """ Return a sequence of elements `args` such that cls(*args) == expr >>> from sympy import Symbol, Mul, Add >>> x, y = map(Symbol, 'xy') >>> Mul.make_args(x*y) (x, y) >>> Add.make_args(x*y) (x*y,) >>> set(Add.make_args(x*y + y)) == set([y, x*y]) True """ if isinstance(expr, cls): return expr._argset else: return frozenset([expr]) @property @cacheit def args(self): return tuple(ordered(self._argset)) @staticmethod def _compare_pretty(a, b): return (str(a) > str(b)) - (str(a) < str(b)) sympy-0.7.4.1/sympy/core/cache.py0000644000175000017500000000703612253362407017000 0ustar georgeskgeorgesk""" Caching facility for SymPy """ from __future__ import print_function, division # 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 list(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, TypeError): pass r = func(*args, **kw_args) try: func_cache_it_cache[k] = r except TypeError: # k is unhashable # Note, collections.Hashable is not smart enough to be used here. pass 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) sympy-0.7.4.1/sympy/core/containers.py0000644000175000017500000001714012253362407020077 0ustar georgeskgeorgesk"""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 __future__ import print_function, division from sympy.core.basic import Basic from sympy.core.compatibility import as_int from sympy.core.sympify import sympify, converter from sympy.utilities.iterables import iterable 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[j] for j 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 __mul__(self, other): try: n = as_int(other) except ValueError: raise TypeError("Can't multiply sequence by non-integer of type '%s'" % type(other)) return self.func(*(self.args*n)) __rmul__ = __mul__ 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 # XXX: Basic defines count() as something different, so we can't # redefine it here. Originally this lead to cse() test failure. def tuple_count(self, value): """T.count(value) -> integer -- return number of occurrences of value""" return self.args.count(value) def index(self, value, start=None, stop=None): """T.index(value, [start, [stop]]) -> integer -- return first index of value. Raises ValueError if the value is not present.""" # XXX: One would expect: # # return self.args.index(value, start, stop) # # here. Any trouble with that? Yes: # # >>> (1,).index(1, None, None) # Traceback (most recent call last): # File "", line 1, in # TypeError: slice indices must be integers or None or have an __index__ method # # See: http://bugs.python.org/issue13340 if start is None and stop is None: return self.args.index(value) elif stop is None: return self.args.index(value, start) else: return self.args.index(value, start, stop) 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 >>> 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 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.core.containers import Dict >>> D = Dict({1: 'one', 2: 'two'}) >>> for key in D: ... if key == 1: ... print('%s %s' % (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 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) def items(self): '''D.items() -> list of D's (key, value) pairs, as 2-tuples''' return self._dict.items() def keys(self): '''D.keys() -> list of D's keys''' return self._dict.keys() def values(self): '''D.values() -> list of D's values''' return 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__() 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) sympy-0.7.4.1/sympy/core/multidimensional.py0000644000175000017500000001037712253362407021314 0ustar georgeskgeorgesk""" Provides functionality for multidimensional usage of scalar-functions. Read the vectorize docstring for more details. """ from __future__ import print_function, division 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) class vectorize: """ Generalizes a function taking scalars to accept multidimensional arguments. For example >>> from sympy import diff, sin, symbols, Function >>> from sympy.core.multidimensional import vectorize >>> x, y, z = symbols('x y z') >>> f, g, h = list(map(Function, 'fgh')) >>> @vectorize(0) ... def vsin(x): ... return sin(x) >>> vsin([1, x, y]) [sin(1), sin(x), sin(y)] >>> @vectorize(0, 1) ... def vdiff(f, y): ... return diff(f, y) >>> vdiff([f(x, y, z), g(x, y, z), h(x, y, z)], [x, y, z]) [[Derivative(f(x, y, z), x), Derivative(f(x, y, z), y), Derivative(f(x, y, z), z)], [Derivative(g(x, y, z), x), Derivative(g(x, y, z), y), Derivative(g(x, y, z), z)], [Derivative(h(x, y, z), x), Derivative(h(x, y, z), y), Derivative(h(x, y, z), z)]] """ 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 = range(len(args)) + 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 sympy-0.7.4.1/sympy/core/facts.py0000644000175000017500000003536112253362407017037 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- """This is rule-based deduction system for SymPy The whole thing is split into two parts - rules compilation and preparation of tables - runtime inference For rule-based inference engines, the classical work is RETE algorithm [1], [2] Although we are not implementing it in full (or even significantly) it's still still worth a read to understand the underlying ideas. In short, every rule in a system of rules is one of two forms: - atom -> ... (alpha rule) - And(atom1, atom2, ...) -> ... (beta rule) The major complexity is in efficient beta-rules processing and usually for an expert system a lot of effort goes into code that operates on beta-rules. Here we take minimalistic approach to get something usable first. - (preparation) of alpha- and beta- networks, everything except - (runtime) FactRules.deduce_all_facts _____________________________________ ( Kirr: I've never thought that doing ) ( logic stuff is that difficult... ) ------------------------------------- o ^__^ o (oo)\_______ (__)\ )\/\ ||----w | || || Some references on the topic ---------------------------- [1] http://en.wikipedia.org/wiki/Rete_algorithm [2] http://reports-archive.adm.cs.cmu.edu/anon/1995/CMU-CS-95-113.pdf http://en.wikipedia.org/wiki/Propositional_formula http://en.wikipedia.org/wiki/Inference_rule http://en.wikipedia.org/wiki/List_of_rules_of_inference """ from __future__ import print_function, division from collections import defaultdict from .logic import Logic, And, Or, Not from sympy.core.compatibility import string_types def _base_fact(atom): """Return the literal fact of an atom. Effectively, this merely strips the Not around a fact. """ if isinstance(atom, Not): return atom.arg else: return atom def _as_pair(atom): if isinstance(atom, Not): return (atom.arg, False) else: return (atom, True) # XXX this prepares forward-chaining rules for alpha-network def deduce_alpha_implications(implications): """deduce all implications Description by example ---------------------- given set of logic rules: a -> b b -> c we deduce all possible rules: a -> b, c b -> c implications: [] of (a,b) return: {} of a -> set([b, c, ...]) """ res = defaultdict(set) for a, b in implications: if a == b: continue # skip a->a cyclic input res[a].add(b) # (x >> a) & (a >> b) => x >> b for fact in res: implied = res[fact] if a in implied: implied.add(b) # (a >> b) & (b >> x) => a >> x if b in res: res[a] |= res[b] # Clean up tautologies and check consistency for a, impl in res.items(): impl.discard(a) na = Not(a) if na in impl: raise ValueError( 'implications are inconsistent: %s -> %s %s' % (a, na, impl)) return res def apply_beta_to_alpha_route(alpha_implications, beta_rules): """apply additional beta-rules (And conditions) to already-built alpha implication tables TODO: write about - static extension of alpha-chains - attaching refs to beta-nodes to alpha chains e.g. alpha_implications: a -> [b, !c, d] b -> [d] ... beta_rules: &(b,d) -> e then we'll extend a's rule to the following a -> [b, !c, d, e] """ x_impl = {} for x in alpha_implications.keys(): x_impl[x] = (set(alpha_implications[x]), []) for bcond, bimpl in beta_rules: for bk in bcond.args: if bk in x_impl: continue x_impl[bk] = (set(), []) # static extensions to alpha rules: # A: x -> a,b B: &(a,b) -> c ==> A: x -> a,b,c seen_static_extension = True while seen_static_extension: seen_static_extension = False for bcond, bimpl in beta_rules: assert isinstance(bcond, And) bargs = set(bcond.args) for x, (ximpls, bb) in x_impl.items(): x_all = ximpls | set([x]) # A: ... -> a B: &(...) -> a is non-informative if bimpl not in x_all and bargs.issubset(x_all): ximpls.add(bimpl) # we introduced new implication - now we have to restore # completness of the whole set. bimpl_impl = x_impl.get(bimpl) if bimpl_impl is not None: ximpls |= bimpl_impl[0] seen_static_extension = True # attach beta-nodes which can be possibly triggered by an alpha-chain for bidx, (bcond, bimpl) in enumerate(beta_rules): bargs = set(bcond.args) for x, (ximpls, bb) in x_impl.items(): x_all = ximpls | set([x]) # A: ... -> a B: &(...) -> a (non-informative) if bimpl in x_all: continue # A: x -> a... B: &(!a,...) -> ... (will never trigger) # A: x -> a... B: &(...) -> !a (will never trigger) if any(Not(xi) in bargs or Not(xi) == bimpl for xi in x_all): continue if bargs & x_all: bb.append(bidx) return x_impl def rules_2prereq(rules): """build prerequisites table from rules Description by example ---------------------- given set of logic rules: a -> b, c b -> c we build prerequisites (from what points something can be deduced): b <- a c <- a, b rules: {} of a -> [b, c, ...] return: {} of c <- [a, b, ...] Note however, that this prerequisites may be *not* enough to prove a fact. An example is 'a -> b' rule, where prereq(a) is b, and prereq(b) is a. That's because a=T -> b=T, and b=F -> a=F, but a=F -> b=? """ prereq = defaultdict(set) for (a, _), impl in rules.items(): for (i, _) in impl: prereq[i].add(a) return prereq ################ # RULES PROVER # ################ class TautologyDetected(Exception): """(internal) Prover uses it for reporting detected tautology""" pass class Prover(object): """ai - prover of logic rules given a set of initial rules, Prover tries to prove all possible rules which follow from given premises. As a result proved_rules are always either in one of two forms: alpha or beta: Alpha rules ----------- This are rules of the form:: a -> b & c & d & ... Beta rules ---------- This are rules of the form:: &(a,b,...) -> c & d & ... i.e. beta rules are join conditions that say that something follows when *several* facts are true at the same time. """ def __init__(self): self.proved_rules = [] self._rules_seen = set() def split_alpha_beta(self): """split proved rules into alpha and beta chains""" rules_alpha = [] # a -> b rules_beta = [] # &(...) -> b for a, b in self.proved_rules: if isinstance(a, And): rules_beta.append((a, b)) else: rules_alpha.append((a, b) ) return rules_alpha, rules_beta @property def rules_alpha(self): return self.split_alpha_beta()[0] @property def rules_beta(self): return self.split_alpha_beta()[1] def process_rule(self, a, b): """process a -> b rule""" # TODO write more? if (not a) or isinstance(b, bool): return if isinstance(a, bool): return if (a, b) in self._rules_seen: return else: self._rules_seen.add((a, b)) # this is the core of processing try: self._process_rule(a, b) except TautologyDetected: pass def _process_rule(self, a, b): # right part first # a -> b & c --> a -> b ; a -> c # (?) FIXME this is only correct when b & c != null ! if isinstance(b, And): for barg in b.args: self.process_rule(a, barg) # a -> b | c --> !b & !c -> !a # --> a & !b -> c # --> a & !c -> b elif isinstance(b, Or): # detect tautology first if not isinstance(a, Logic): # Atom # tautology: a -> a|c|... if a in b.args: raise TautologyDetected(a, b, 'a -> a|c|...') self.process_rule(And(*[Not(barg) for barg in b.args]), Not(a)) for bidx in range(len(b.args)): barg = b.args[bidx] brest = b.args[:bidx] + b.args[bidx + 1:] self.process_rule(And(a, Not(barg)), Or(*brest)) # left part # a & b -> c --> IRREDUCIBLE CASE -- WE STORE IT AS IS # (this will be the basis of beta-network) elif isinstance(a, And): if b in a.args: raise TautologyDetected(a, b, 'a & b -> a') self.proved_rules.append((a, b)) # XXX NOTE at present we ignore !c -> !a | !b elif isinstance(a, Or): if b in a.args: raise TautologyDetected(a, b, 'a | b -> a') for aarg in a.args: self.process_rule(aarg, b) else: # both `a` and `b` are atoms self.proved_rules.append((a, b)) # a -> b self.proved_rules.append((Not(b), Not(a))) # !b -> !a ######################################## class FactRules(object): """Rules that describe how to deduce facts in logic space When defined, these rules allow implications to quickly be determined for a set of facts. For this precomputed deduction tables are used. see `deduce_all_facts` (forward-chaining) Also it is possible to gather prerequisites for a fact, which is tried to be proven. (backward-chaining) Definition Syntax ----------------- a -> b -- a=T -> b=T (and automatically b=F -> a=F) a -> !b -- a=T -> b=F a == b -- a -> b & b -> a a -> b & c -- a=T -> b=T & c=T # TODO b | c Internals --------- .full_implications[k, v]: all the implications of fact k=v .beta_triggers[k, v]: beta rules that might be triggered when k=v .prereq -- {} k <- [] of k's prerequisites .defined_facts -- set of defined fact names """ def __init__(self, rules): """Compile rules into internal lookup tables""" if isinstance(rules, string_types): rules = rules.splitlines() # --- parse and process rules --- P = Prover() for rule in rules: # XXX `a` is hardcoded to be always atom a, op, b = rule.split(None, 2) a = Logic.fromstring(a) b = Logic.fromstring(b) if op == '->': P.process_rule(a, b) elif op == '==': P.process_rule(a, b) P.process_rule(b, a) else: raise ValueError('unknown op %r' % op) # --- build deduction networks --- self.beta_rules = [] for bcond, bimpl in P.rules_beta: self.beta_rules.append( (set(_as_pair(a) for a in bcond.args), _as_pair(bimpl))) # deduce alpha implications impl_a = deduce_alpha_implications(P.rules_alpha) # now: # - apply beta rules to alpha chains (static extension), and # - further associate beta rules to alpha chain (for inference at runtime) impl_ab = apply_beta_to_alpha_route(impl_a, P.rules_beta) # extract defined fact names self.defined_facts = set(_base_fact(k) for k in impl_ab.keys()) # build rels (forward chains) full_implications = defaultdict(set) beta_triggers = defaultdict(set) for k, (impl, betaidxs) in impl_ab.items(): full_implications[_as_pair(k)] = set(_as_pair(i) for i in impl) beta_triggers[_as_pair(k)] = betaidxs self.full_implications = full_implications self.beta_triggers = beta_triggers # build prereq (backward chains) prereq = defaultdict(set) rel_prereq = rules_2prereq(full_implications) for k, pitems in rel_prereq.items(): prereq[k] |= pitems self.prereq = prereq class InconsistentAssumptions(ValueError): def __str__(self): kb, fact, value = self.args return "%s, %s=%s" % (kb, fact, value) class FactKB(dict): """ A simple propositional knowledge base relying on compiled inference rules. """ def __init__(self, rules): self.rules = rules def _tell(self, k, v): """Add fact k=v to the knowledge base. Returns True if the KB has actually been updated, False otherwise. """ if k in self and self[k] is not None: if self[k] == v: return False else: raise InconsistentAssumptions(self, k, v) else: self[k] = v return True # ********************************************* # * This is the workhorse, so keep it *fast*. * # ********************************************* def deduce_all_facts(self, facts): """ Update the KB with all the implications of a list of facts. Facts can be specified as a dictionary or as a list of (key, value) pairs. """ # keep frequently used attributes locally, so we'll avoid extra # attribute access overhead full_implications = self.rules.full_implications beta_triggers = self.rules.beta_triggers beta_rules = self.rules.beta_rules if isinstance(facts, dict): facts = facts.items() while facts: beta_maytrigger = set() # --- alpha chains --- for k, v in facts: if not self._tell(k, v) or v is None: continue # lookup routing tables for key, value in full_implications[k, v]: self._tell(key, value) beta_maytrigger.update(beta_triggers[k, v]) # --- beta chains --- facts = [] for bidx in beta_maytrigger: bcond, bimpl = beta_rules[bidx] if all(self.get(k) is v for k, v in bcond): facts.append(bimpl) sympy-0.7.4.1/sympy/core/function.py0000644000175000017500000022626212253362407017566 0ustar georgeskgeorgesk""" 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 = 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 __future__ import print_function, division 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.core.compatibility import string_types, with_metaclass, xrange 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 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])) class FunctionClass(with_metaclass(BasicMeta, ManagedProperties)): """ Base class for function classes. FunctionClass is a subclass of type. Use Function('' [ , signature ]) to create undefined function classes. """ _new = type.__new__ def __repr__(cls): return cls.__name__ class Application(with_metaclass(FunctionClass, Basic)): """ 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) 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 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] def bad(m): from sympy.mpmath import mpf, mpc # the precision of an mpf value is the last element # if that is 1 (and m[1] is not 1 which would indicate a # power of 2), then the eval failed; so check that none of # the arguments failed to compute to a finite precision. # Note: An mpc value has two parts, the re and imag tuple; # check each of those parts, too. Anything else is allowed to # pass if isinstance(m, mpf): m = m._mpf_ return m[1] !=1 and m[-1] == 1 elif isinstance(m, mpc): m, n = m._mpc_ return m[1] !=1 and m[-1] == 1 and \ n[1] !=1 and n[-1] == 1 else: return False if any(bad(a) for a in args): raise ValueError # one or more args failed to compute with significance 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) def _eval_is_complex(self): return fuzzy_and(a.is_complex for a in self.args) 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 >>> 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) """ 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 is None or (self.func.nargs == 1 and args0[0]) or isinstance(self.func.nargs, tuple) 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 _x = Dummy('x') e = e.subs(x, _x) 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 # try to predict a number of terms needed nterms = n + 2 cf = C.Order(arg.as_leading_term(x), x).getn() if cf != 0: nterms = int(nterms / cf) for i in xrange(nterms): 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) 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) arg_dummy.dummy_index = hash(self.args[argindex - 1]) 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) 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 def _eval_as_leading_term(self, x): return self class UndefinedFunction(FunctionClass): """ The (meta)class of undefined functions. """ def __new__(mcl, name): ret = BasicMeta.__new__(mcl, name, (AppliedUndef,), {}) ret.__module__ = None return ret UndefinedFunction.__eq__ = lambda s, o: (isinstance(o, s.__class__) and (s.class_key() == o.class_key())) class WildFunction(Function, AtomicExpr): """ A WildFunction function matches any function (with its arguments). Examples ======== >>> from sympy import WildFunction, Function, cos >>> from sympy.abc import x, y >>> 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 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. Simplification of high-order derivatives: Because there can be a significant amount of simplification that can be done when multiple differentiations are performed, results will be automatically simplified in a fairly conservative fashion unless the keyword ``simplify`` is set to False. >>> from sympy import sqrt, diff >>> from sympy.abc import x >>> e = sqrt((x + 1)**2 + x) >>> diff(e, x, 5, simplify=False).count_ops() 136 >>> diff(e, x, 5).count_ops() 30 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 derivatives to be taken 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 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 xrange(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 aggressive 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 nderivs = 0 # how many derivatives were performed 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) new_v.dummy_index = hash(v) expr = expr.subs(v, new_v) old_v = v v = new_v obj = expr._eval_derivative(v) nderivs += 1 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 = cls( expr.args[0], *cls._sort_variables(expr.args[1:]) ) if nderivs > 1 and assumptions.get('simplify', True): from sympy.core.exprtools import factor_terms from sympy.simplify.simplify import signsimp expr = factor_terms(signsimp(expr)) 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 don't 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 obj.func(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 self.func(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 self.func(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 self.func(expr, *self.variables, **hints) @_sympifyit('z0', NotImplementedError) 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) # If both are Derivatives with the same expr, check if old is # equivalent to self or if old is a subderivative of self. if old.is_Derivative and old.expr == self.args[0]: # Check if canonnical order of variables is equal. old_vars = Derivative._sort_variables(old.variables) self_vars = Derivative._sort_variables(self.args[1:]) if old_vars == self_vars: return new # Check if olf is a subderivative of self. if len(old_vars) < len(self_vars): self_vars_front = [] match = True while old_vars and self_vars and match: if old_vars[0] == self_vars[0]: old_vars.pop(0) self_vars.pop(0) else: # If self_v does not match old_v, we need to check if # the types are the same (symbol vs non-symbol). If # they are, we can continue checking self_vars for a # match. if old_vars[0].is_Symbol != self_vars[0].is_Symbol: match = False else: self_vars_front.append(self_vars.pop(0)) if match: variables = self_vars_front + self_vars return Derivative(new, *variables) return Derivative(*map(lambda x: x._subs(old, new), self.args)) def _eval_lseries(self, x, logx): dx = self.args[1:] for term in self.args[0].lseries(x, logx=logx): yield self.func(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 = [self.func(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) 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: for v in variables if iterable(variables) else [variables]: assert v.is_Symbol except (AssertionError, AttributeError): raise ValueError('variable is not a Symbol: %s' % v) try: variables = Tuple(*variables) except TypeError: variables = Tuple(variables) if len(variables) == 1 and variables[0] == expr: return S.IdentityFunction obj = Expr.__new__(cls, Tuple(*variables), S(expr)) return obj @property def variables(self): """The variables used in the internal representation of the function""" return self._args[0] @property 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 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: raise TypeError('%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.expr.xreplace(self.canonical_variables),) @property 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 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 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 def variables(self): """The variables to be evaluated""" return self._args[1] @property def expr(self): """The expression on which the substitution operates""" return self._args[0] @property 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.xreplace(self.canonical_variables),) def _eval_subs(self, old, new): if old in self.variables: pts = list(self.point.args) pts[self.variables.index(old)] = new return self.func(self.expr, self.variables, pts) def _eval_derivative(self, s): if s not in self.free_symbols: return S.Zero return self.func(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) ]) 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)) >>> 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) 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 >>> 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 `_ 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, 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 mul expanded form, either of the following will work:: >>> expand_mul(log(x*(y + z))) log(x*y + x*z) >>> expand(log(x*(y + z)), log=False) log(x*y + x*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. 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) 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) 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) 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) 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 >>> 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) 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) 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) 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) 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)) 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, 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.power import Pow from sympy.polys.rootoftools import RootOf if iterable(expr, exclude=string_types): if isinstance(expr, (dict, Dict)): return type(expr)([(k, nfloat(v, n, exponent)) for k, v in list(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 # watch out for RootOf instances that don't like to have # their exponents replaced with Dummies and also sometimes have # problems with evaluating at low precision (issue 3294) rv = rv.xreplace(dict([(ro, ro.n(n)) for ro in rv.atoms(RootOf)])) 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 sympy-0.7.4.1/sympy/core/decorators.py0000644000175000017500000000766412253362407020111 0ustar georgeskgeorgesk""" SymPy core decorators. The purpose of this module is to expose decorators without any other dependencies, so that they can be easily imported anywhere in sympy/core. """ from __future__ import print_function, division from functools import wraps from .sympify import SympifyError, sympify from sympy.core.compatibility import get_function_code def deprecated(**decorator_kwargs): """This is a decorator which can be used to mark functions as deprecated. It will result in a warning being emitted when the function is used.""" def deprecated_decorator(func): @wraps(func) def new_func(*args, **kwargs): from sympy.utilities.exceptions import SymPyDeprecationWarning decorator_kwargs.setdefault('feature', func.__name__) SymPyDeprecationWarning(**decorator_kwargs).warn(stacklevel=3) return func(*args, **kwargs) return new_func return deprecated_decorator def _sympifyit(arg, retval=None): """decorator to smartly _sympify function arguments @_sympifyit('other', NotImplemented) def add(self, other): ... In add, other can be thought of as already being a SymPy object. If it is not, the code is likely to catch an exception, then other will be explicitly _sympified, and the whole code restarted. if _sympify(arg) fails, NotImplemented will be returned see: __sympifyit """ def deco(func): return __sympifyit(func, arg, retval) return deco def __sympifyit(func, arg, retval=None): """decorator to _sympify `arg` argument for function `func` don't use directly -- use _sympifyit instead """ # we support f(a,b) only assert get_function_code(func).co_argcount # only b is _sympified assert get_function_code(func).co_varnames[1] == arg if retval is None: @wraps(func) def __sympifyit_wrapper(a, b): return func(a, sympify(b, strict=True)) else: @wraps(func) def __sympifyit_wrapper(a, b): try: # If an external class has _op_priority, it knows how to deal # with sympy objects. Otherwise, it must be converted. if not hasattr(b, '_op_priority'): b = sympify(b, strict=True) return func(a, b) except SympifyError: return retval return __sympifyit_wrapper def call_highest_priority(method_name): """A decorator for binary special methods to handle _op_priority. Binary special methods in Expr and its subclasses use a special attribute '_op_priority' to determine whose special method will be called to handle the operation. In general, the object having the highest value of '_op_priority' will handle the operation. Expr and subclasses that define custom binary special methods (__mul__, etc.) should decorate those methods with this decorator to add the priority logic. The ``method_name`` argument is the name of the method of the other class that will be called. Use this decorator in the following manner:: # Call other.__rmul__ if other._op_priority > self._op_priority @call_highest_priority('__rmul__') def __mul__(self, other): ... # Call other.__mul__ if other._op_priority > self._op_priority @call_highest_priority('__mul__') def __rmul__(self, other): ... """ def priority_decorator(func): @wraps(func) def binary_op_wrapper(self, other): if hasattr(other, '_op_priority'): if other._op_priority > self._op_priority: try: f = getattr(other, method_name) except AttributeError: pass else: return f(self) return func(self, other) return binary_op_wrapper return priority_decorator sympy-0.7.4.1/sympy/core/sympify.py0000644000175000017500000003245712253362407017442 0ustar georgeskgeorgesk"""sympify -- convert objects SymPy internal format""" from __future__ import print_function, division from inspect import getmro from .core import all_classes as sympy_classes from .compatibility import iterable, string_types 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. class CantSympify(object): """ Mix in this trait to a class to disallow sympification of its instances. Example ======= >>> from sympy.core.sympify import sympify, CantSympify >>> class Something(dict): ... pass ... >>> sympify(Something()) {} >>> class Something(dict, CantSympify): ... pass ... >>> sympify(Something()) Traceback (most recent call last): ... SympifyError: SympifyError: {} """ pass def sympify(a, locals=None, convert_xor=True, strict=False, rational=False, evaluate=True): """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 - standard numeric python types: int, long, float, Decimal - strings (like "0.09" or "2e-19") - booleans, including ``None`` (will leave ``None`` 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: >>> from sympy.core.compatibility import exec_ >>> 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. >>> print(sympify(None)) None >>> sympify(None, strict=True) Traceback (most recent call last): ... SympifyError: SympifyError: None Evaluation ---------- If the option ``evaluate`` is set to ``False``, then arithmetic and operators will be converted into their SymPy equivalents and the ``evaluate=False`` option will be added. Nested ``Add`` or ``Mul`` will be denested first. This is done via an AST transformation that replaces operators with their SymPy equivalents, so if an operand redefines any of those operations, the redefined operators will not be used. >>> sympify('2**2 / 3 + 5') 19/3 >>> sympify('2**2 / 3 + 5', evaluate=False) 2**2/3 + 5 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()) Matrix([ [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()) Matrix([ [1], [2]]) Notes ===== Sometimes autosimplification during sympification results in expressions that are very different in structure than what was entered. Until such autosimplification is no longer done, the ``kernS`` function might be of some use. In the example below you can see how an expression reduces to -1 by autosimplification, but does not do so when ``kernS`` is used. >>> from sympy.core.sympify import kernS >>> from sympy.abc import x >>> -2*(-(-x + 1/x)/(x*(x - 1/x)**2) - 1/(x*(x - 1/x))) - 1 -1 >>> s = '-2*(-(-x + 1/x)/(x*(x - 1/x)**2) - 1/(x*(x - 1/x))) - 1' >>> sympify(s) -1 >>> kernS(s) -2*(-(-x + 1/x)/(x*(x - 1/x)**2) - 1/(x*(x - 1/x))) - 1 """ 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 is 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 if isinstance(a, CantSympify): raise SympifyError(a) try: return a._sympy_() except AttributeError: pass if not isinstance(a, string_types): 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: from .compatibility import unicode a = unicode(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, local_dict=locals, transformations=transformations, evaluate=evaluate) except (TokenError, SyntaxError) as exc: raise SympifyError('could not parse %r' % a, exc) 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.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) def kernS(s): """Use a hack to try keep autosimplification from joining Integer or minus sign into an Add of a Mul; this modification doesn't prevent the 2-arg Mul from becoming an Add, however. Examples ======== >>> from sympy.core.sympify import kernS >>> from sympy.abc import x, y, z The 2-arg Mul allows a leading Integer to be distributed but kernS will prevent that: >>> 2*(x + y) 2*x + 2*y >>> kernS('2*(x + y)') 2*(x + y) If use of the hack fails, the un-hacked string will be passed to sympify... and you get what you get. XXX This hack should not be necessary once issue 1497 has been resolved. """ import re from sympy.core.symbol import Symbol hit = False if '(' in s: if s.count('(') != s.count(")"): raise SympifyError('unmatched left parenthesis') kern = '_kern' while kern in s: kern += "_" olds = s # digits*( -> digits*kern*( s = re.sub(r'(\d+)( *\* *)\(', r'\1*%s\2(' % kern, s) # negated parenthetical kern2 = kern + "2" while kern2 in s: kern2 += "_" # step 1: -(...) --> kern-kern*(...) target = r'%s-%s*(' % (kern, kern) s = re.sub(r'- *\(', target, s) # step 2: double the matching closing parenthesis # kern-kern*(...) --> kern-kern*(...)kern2 i = nest = 0 while True: j = s.find(target, i) if j == -1: break j = s.find('(') for j in range(j, len(s)): if s[j] == "(": nest += 1 elif s[j] == ")": nest -= 1 if nest == 0: break s = s[:j] + kern2 + s[j:] i = j # step 3: put in the parentheses # kern-kern*(...)kern2 --> (-kern*(...)) s = s.replace(target, target.replace(kern, "(", 1)) s = s.replace(kern2, ')') hit = kern in s for i in range(2): try: expr = sympify(s) break except: # the kern might cause unknown errors, so use bare except if hit: s = olds # maybe it didn't like the kern; use un-kerned s hit = False continue expr = sympify(s) # let original error raise if not hit: return expr rep = {Symbol(kern): 1} def _clear(expr): if isinstance(expr, (list, tuple, set)): return type(expr)([_clear(e) for e in expr]) if hasattr(expr, 'subs'): return expr.subs(rep, hack2=True) return expr expr = _clear(expr) # hope that kern is not there anymore return expr sympy-0.7.4.1/sympy/core/basic.py0000644000175000017500000016507512253362407017026 0ustar georgeskgeorgesk"""Base class for all the objects in SymPy""" from __future__ import print_function, division 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 (reduce, iterable, Iterator, ordered, string_types, with_metaclass, zip_longest) from sympy.core.decorators import deprecated from sympy.core.singleton import S from inspect import getmro class Basic(with_metaclass(ManagedProperties)): """ Base class for all objects in SymPy. Conventions: 1) Always use ``.args``, when accessing parameters of some instance: >>> from sympy import 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 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 relevant 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 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 {} 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 lines: if self is other: return 0 n1 = self.__class__ n2 = other.__class__ c = (n1 > n2) - (n1 < n2) if c: return c # st = self._hashable_content() ot = other._hashable_content() c = (len(st) > len(ot)) - (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 = (l > r) - (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: l = a.p * b.q r = b.p * a.q return (l > r) - (l < r) 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") 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 if not isinstance(b, Basic): return +1 # sympy > other # now both objects are from SymPy, so we can proceed to usual comparison a = a.sort_key() b = b.sort_key() return (a > b) - (a < b) @classmethod 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 def class_key(cls): """Nice order of classes. """ return 5, 0, cls.__name__ @cacheit def sort_key(self, order=None): """ Return a sort key. Examples ======== >>> from sympy.core import S, I >>> 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__ = .__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 self is other: return True from .function import AppliedUndef, UndefinedFunction as UndefFunc if isinstance(self, UndefFunc) and isinstance(other, UndefFunc): if self.class_key() == other.class_key(): return True else: return False 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 isinstance(self, AppliedUndef) and isinstance(other, AppliedUndef): if self.class_key() != other.class_key(): return False elif 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() 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) 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 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()) @property def canonical_variables(self): """Return a dictionary mapping any variable defined in ``self.variables`` as underscore-suffixed numbers corresponding to their position in ``self.variables``. Enough underscores are added to ensure that there will be no clash with existing free symbols. Examples ======== >>> from sympy import Lambda >>> from sympy.abc import x >>> Lambda(x, 2*x).canonical_variables {x: 0_} """ if not hasattr(self, 'variables'): return {} u = "_" while any(s.name.endswith(u) for s in self.free_symbols): u += "_" name = '%%i%s' % u V = self.variables return dict(list(zip(V, [C.Symbol(name % i, **v.assumptions0) for i, v in enumerate(V)]))) def rcall(self, *args): """Apply on the argument recursively through the expression tree. This method is used to simulate a common abuse of notation for operators. For instance in SymPy the the following will not work: ``(x+Lambda(y, 2*y))(z) == x+2*z``, however you can use >>> from sympy import Lambda >>> from sympy.abc import x,y,z >>> (x + Lambda(y, 2*y)).rcall(z) x + 2*z """ return Basic._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 != Basic if callable(expr_to_call) 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 = [Basic._recursive_call( sub, on_args) for sub in expr_to_call.args] return type(expr_to_call)(*args) else: return expr_to_call def is_hypergeometric(self, k): from sympy.simplify import hypersimp return hypersimp(self, k) is not None @property def is_number(self): """Returns ``True`` if 'self' contains no free symbols. See Also ======== is_comparable sympy.core.expr.is_number """ # should be overriden by subclasses return False @property def is_comparable(self): """Return True if self can be computed to a real number with precision, else False. Examples ======== >>> from sympy import exp_polar, pi, I >>> (I*exp_polar(I*pi/2)).is_comparable True >>> (I*exp_polar(I*pi*2)).is_comparable False """ 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 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 >>> a.args (2, x) >>> a.func(*a.args) 2*x >>> a == a.func(*a.args) True """ return self.__class__ @property def args(self): """Returns a tuple of arguments of 'self'. Examples ======== >>> from sympy import 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 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) def as_poly(self, *gens, **args): """Converts ``self`` to a polynomial or returns ``None``. >>> from sympy import 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 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 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 >>> 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 = 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 = list(filter(None, sequence)) 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(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, **kwargs) 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, **kwargs) 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 Add >>> 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 != args[i]: hit = True args[i] = arg if hit: rv = self.func(*args) hack2 = hints.get('hack2', False) if hack2 and self.is_Mul and not rv.is_Mul: # 2-arg hack coeff = S.One nonnumber = [] for i in args: if i.is_Number: coeff *= i else: nonnumber.append(i) nonnumber = self.func(*nonnumber) if coeff is S.One: return nonnumber else: return self.func(coeff, nonnumber, evaluate=False) return rv 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 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 = [] for a in self.args: try: args.append(a.xreplace(rule)) except AttributeError: args.append(a) args = tuple(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 def has(self, *patterns): """ Test whether any subexpression matches any of the patterns. Examples ======== >>> from sympy import sin >>> 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__ def replace(self, query, value, map=False, simultaneous=True, exact=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. If the expression itself doesn't match the query, then the returned value will be ``self.xreplace(map)`` otherwise it should be ``self.subs(ordered(map.items()))``. Traverses an expression tree and performs replacement of matching subexpressions from the bottom to the top of the tree. The default approach is to do the replacement in a simultaneous fashion so changes made are targeted only once. If this is not desired or causes problems, ``simultaneous`` can be set to False. In addition, if an expression containing more than one Wild symbol is being used to match subexpressions and the ``exact`` flag is True, then the match will only succeed if non-zero values are received for each Wild that appears in the match pattern. The list of possible combinations of queries and replacement values is listed below: Examples ======== Initial setup >>> from sympy import log, sin, cos, tan, Wild, Mul, Add >>> from sympy.abc import x, y >>> f = log(sin(x)) + tan(sin(x**2)) 1.1. type -> type obj.replace(type, newtype) When object of type ``type`` is found, replace it with the result of passing its argument(s) to ``newtype``. >>> f.replace(sin, cos) log(cos(x)) + tan(cos(x**2)) >>> sin(x).replace(sin, cos, map=True) (cos(x), {sin(x): cos(x)}) >>> (x*y).replace(Mul, Add) x + y 1.2. type -> func obj.replace(type, func) When object of type ``type`` is found, apply ``func`` to its argument(s). ``func`` must be written to handle the number of arguments of ``type``. >>> f.replace(sin, lambda arg: sin(2*arg)) log(sin(2*x)) + tan(sin(2*x**2)) >>> (x*y).replace(Mul, lambda *args: sin(2*Mul(*args))) sin(2*x*y) 2.1. pattern -> expr obj.replace(pattern(wild), expr(wild)) Replace subexpressions matching ``pattern`` with the expression written in terms of the Wild symbols in ``pattern``. >>> a = Wild('a') >>> f.replace(sin(a), tan(a)) log(tan(x)) + tan(tan(x**2)) >>> f.replace(sin(a), tan(a/2)) log(tan(x/2)) + tan(tan(x**2/2)) >>> f.replace(sin(a), a) log(x) + tan(x**2) >>> (x*y).replace(a*x, a) y When the default value of False is used with patterns that have more than one Wild symbol, non-intuitive results may be obtained: >>> b = Wild('b') >>> (2*x).replace(a*x + b, b - a) 2/x For this reason, the ``exact`` option can be used to make the replacement only when the match gives non-zero values for all Wild symbols: >>> (2*x + y).replace(a*x + b, b - a, exact=True) y - 2 >>> (2*x).replace(a*x + b, b - a, exact=True) 2*x 2.2. pattern -> func obj.replace(pattern(wild), lambda wild: expr(wild)) All behavior is the same as in 2.1 but now a function in terms of pattern variables is used rather than an expression: >>> f.replace(sin(a), lambda a: sin(2*a)) log(sin(2*x)) + tan(sin(2*x**2)) 3.1. func -> func obj.replace(filter, func) Replace subexpression ``e`` with ``func(e)`` if ``filter(e)`` is True. >>> g = 2*sin(x**3) >>> g.replace(lambda expr: expr.is_Number, lambda expr: expr**2) 4*sin(x**9) The expression itself is also targeted by the query but is done in such a fashion that changes are not made twice. >>> e = x*(x*y + 1) >>> e.replace(lambda x: x.is_Mul, lambda x: 2*x) 2*x*(2*x*y + 1) 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 """ from sympy.core.symbol import Dummy from sympy.simplify.simplify import bottom_up try: query = sympify(query) except SympifyError: pass try: value = sympify(value) except SympifyError: pass if isinstance(query, type): _query = lambda expr: isinstance(expr, query) if isinstance(value, type): _value = lambda expr, result: value(*expr.args) elif callable(value): _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) # XXX remove the exact flag and make multi-symbol # patterns use exact=True semantics; to do this the query must # be tested to find out how many Wild symbols are present. # See https://groups.google.com/forum/ # ?fromgroups=#!topic/sympy/zPzo5FtRiqI # for a method of inspecting a function to know how many # parameters it has. if isinstance(value, Basic): if exact: _value = lambda expr, result: (value.subs(result) if all(val for val in result.values()) else expr) else: _value = lambda expr, result: value.subs(result) elif callable(value): # match dictionary keys get the trailing underscore stripped # from them and are then passed as keywords to the callable; # if ``exact`` is True, only accept match if there are no null # values amongst those matched. if exact: _value = lambda expr, result: (value(**dict([ ( str(key)[:-1], val) for key, val in result.items()])) if all(val for val in result.values()) else expr) else: _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 callable(query): _query = query if callable(value): _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 = {} # changes that took place mask = [] # the dummies that were used as change placeholders def rec_replace(expr): result = _query(expr) if result or result == {}: new = _value(expr, result) if new is not None and new != expr: mapping[expr] = new if simultaneous: # don't let this expression be changed during rebuilding d = Dummy() mask.append((d, new)) expr = d else: expr = new return expr rv = bottom_up(self, rec_replace, atoms=True) # restore original expressions for Dummy symbols if simultaneous: mask = list(reversed(mask)) for o, n in mask: r = {o: n} rv = rv.xreplace(r) if not map: return rv else: if simultaneous: # restore subexpressions in mapping for o, n in mask: r = {o: n} mapping = dict([(k.xreplace(r), v.xreplace(r)) for k, v in mapping.items()]) return rv, mapping 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 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)) 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, 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 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 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 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 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) 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 >>> 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) if isinstance(term, Basic) else term for term in self.args ] return self.func(*terms) else: return self def _eval_rewrite(self, pattern, rule, **hints): if self.is_Atom: if hasattr(self, rule): return getattr(self, rule)() return self sargs = self.args terms = [ t._eval_rewrite(pattern, rule, **hints) if isinstance(t, Basic) else t for t in sargs ] return self.func(*terms) 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. Examples ======== >>> from sympy import sin, exp >>> from sympy.abc import x 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 not args: return self else: pattern = args[:-1] if isinstance(args[-1], string_types): 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 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, hack2=False): 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 >>> 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 """ from .function import AppliedUndef, UndefinedFunction as UndefFunc for i, j in zip_longest(preorder_traversal(a), preorder_traversal(b)): if i != j or type(i) != type(j): if ((isinstance(i, UndefFunc) and isinstance(j, UndefFunc)) or (isinstance(i, AppliedUndef) and isinstance(j, AppliedUndef))): if i.class_key() != j.class_key(): return False else: 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(Iterator): """ 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.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): if not keys and hasattr(node, '_argset'): # LatticeOp keeps args as a set. We should use this if we # don't care about the order, to prevent unnecessary sorting. args = node._argset else: 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 sympy-0.7.4.1/sympy/core/core.py0000644000175000017500000000734612253362407016671 0ustar georgeskgeorgesk""" The core's core. """ from __future__ import print_function, division # used for canonical ordering of symbolic sequences # via __cmp__ method: # FIXME this is *so* irrelevant and outdated! ordering_of_classes = [ # singleton numbers 'Zero', 'One', 'Half', 'Infinity', 'NaN', 'NegativeOne', 'NegativeInfinity', # numbers 'Integer', 'Rational', 'Float', # singleton symbols 'Exp1', 'Pi', 'ImaginaryUnit', # symbols 'Symbol', 'Wild', 'Temporary', # arithmetic operations 'Pow', 'Mul', 'Add', # function values 'Derivative', 'Integral', # defined singleton functions 'Abs', 'Sign', 'Sqrt', 'Floor', 'Ceiling', 'Re', 'Im', 'Arg', 'Conjugate', 'Exp', 'Log', 'Sin', 'Cos', 'Tan', 'Cot', 'ASin', 'ACos', 'ATan', 'ACot', 'Sinh', 'Cosh', 'Tanh', 'Coth', 'ASinh', 'ACosh', 'ATanh', 'ACoth', 'RisingFactorial', 'FallingFactorial', 'factorial', 'binomial', 'Gamma', 'LowerGamma', 'UpperGamma', 'PolyGamma', 'Erf', # special polynomials 'Chebyshev', 'Chebyshev2', # undefined functions 'Function', 'WildFunction', # anonymous functions 'Lambda', # Landau O symbol 'Order', # relational operations 'Equality', 'Unequality', 'StrictGreaterThan', 'StrictLessThan', 'GreaterThan', 'LessThan', ] class BasicType(type): pass class Registry(object): """ Base class for registry objects. Registries map a name to an object using attribute notation. Registry classes behave singletonically: all their instances share the same state, which is stored in the class object. All subclasses should set `__slots__ = []`. """ __slots__ = [] def __setattr__(self, name, obj): setattr(self.__class__, name, obj) def __delattr__(self, name): delattr(self.__class__, name) #A set containing all sympy class objects, kept in sync with C all_classes = set() class ClassRegistry(Registry): """ Namespace for SymPy classes This is needed to avoid problems with cyclic imports. To get a SymPy class, use `C.` e.g. `C.Rational`, `C.Add`. For performance reasons, this is coupled with a set `all_classes` holding the classes, which should not be modified directly. """ __slots__ = [] def __setattr__(self, name, cls): Registry.__setattr__(self, name, cls) all_classes.add(cls) def __delattr__(self, name): cls = getattr(self, name) Registry.__delattr__(self, name) # The same class could have different names, so make sure # it's really gone from C before removing it from all_classes. if cls not in self.__class__.__dict__.itervalues(): all_classes.remove(cls) C = ClassRegistry() class BasicMeta(BasicType): def __init__(cls, *args, **kws): setattr(C, cls.__name__, cls) def __cmp__(cls, other): # If the other object is not a Basic subclass, then we are not equal to # it. if not isinstance(other, BasicType): return -1 n1 = cls.__name__ n2 = other.__name__ if n1 == n2: return 0 UNKNOWN = len(ordering_of_classes) + 1 try: i1 = ordering_of_classes.index(n1) except ValueError: i1 = UNKNOWN try: i2 = ordering_of_classes.index(n2) except ValueError: i2 = UNKNOWN if i1 == UNKNOWN and i2 == UNKNOWN: return (n1 > n2) - (n1 < n2) return (i1 > i2) - (i1 < i2) def __lt__(cls, other): if cls.__cmp__(other) == -1: return True return False def __gt__(cls, other): if cls.__cmp__(other) == 1: return True return False C.BasicMeta = BasicMeta sympy-0.7.4.1/sympy/core/benchmarks/0000755000175000017500000000000012253362407017472 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/core/benchmarks/bench_assumptions.py0000644000175000017500000000034212253362407023567 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import Symbol, Integer x = Symbol('x') i3 = Integer(3) def timeit_x_is_integer(): x.is_integer def timeit_Integer_is_irrational(): i3.is_irrational sympy-0.7.4.1/sympy/core/benchmarks/bench_sympify.py0000644000175000017500000000027312253362407022705 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import sympify, Symbol x = Symbol('x') def timeit_sympify_1(): sympify(1) def timeit_sympify_x(): sympify(x) sympy-0.7.4.1/sympy/core/benchmarks/bench_arit.py0000644000175000017500000000071512253362407022145 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import Add, Mul, symbols x, y, z = symbols('x,y,z') def timeit_neg(): -x def timeit_Add_x1(): x + 1 def timeit_Add_1x(): 1 + x def timeit_Add_x05(): x + 0.5 def timeit_Add_xy(): x + y def timeit_Add_xyz(): Add(*[x, y, z]) def timeit_Mul_xy(): x*y def timeit_Mul_xyz(): Mul(*[x, y, z]) def timeit_Div_xy(): x/y def timeit_Div_2y(): 2/y sympy-0.7.4.1/sympy/core/benchmarks/bench_expand.py0000644000175000017500000000073412253362407022466 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import symbols, I x, y, z = symbols('x,y,z') p = 3*x**2*y*z**7 + 7*x*y*z**2 + 4*x + x*y**4 e = (x + y + z + 1)**32 def timeit_expand_nothing_todo(): p.expand() def bench_expand_32(): """(x+y+z+1)**32 -> expand""" e.expand() def timeit_expand_complex_number_1(): ((2 + 3*I)**1000).expand(complex=True) def timeit_expand_complex_number_2(): ((2 + 3*I/4)**1000).expand(complex=True) sympy-0.7.4.1/sympy/core/benchmarks/bench_basic.py0000644000175000017500000000045112253362407022264 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import symbols, S, C x, y = symbols('x,y') def timeit_Symbol_meth_lookup(): x.diff # no call, just method lookup def timeit_S_lookup(): S.Exp1 def timeit_C_lookup(): C.Add def timeit_Symbol_eq_xy(): x == y sympy-0.7.4.1/sympy/core/benchmarks/bench_numbers.py0000644000175000017500000000216312253362407022660 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core.numbers import Integer, Rational, integer_nthroot, igcd from sympy import S, pi, oo i3 = Integer(3) i4 = Integer(4) r34 = Rational(3, 4) q45 = Rational(4, 5) def timeit_Integer_create(): Integer(2) def timeit_Integer_int(): int(i3) def timeit_neg_one(): -S.One def timeit_Integer_neg(): -i3 def timeit_Integer_abs(): abs(i3) def timeit_Integer_sub(): i3 - i3 def timeit_abs_pi(): abs(pi) def timeit_neg_oo(): -oo def timeit_Integer_add_i1(): i3 + 1 def timeit_Integer_add_ij(): i3 + i4 def timeit_Integer_add_Rational(): i3 + r34 def timeit_Integer_mul_i4(): i3*4 def timeit_Integer_mul_ij(): i3*i4 def timeit_Integer_mul_Rational(): i3*r34 def timeit_Integer_eq_i3(): i3 == 3 def timeit_Integer_ed_Rational(): i3 == r34 def timeit_integer_nthroot(): integer_nthroot(100, 2) def timeit_number_igcd_23_17(): igcd(23, 17) def timeit_number_igcd_60_3600(): igcd(60, 3600) def timeit_Rational_add_r1(): r34 + 1 def timeit_Rational_add_rq(): r34 + q45 sympy-0.7.4.1/sympy/core/exprtools.py0000644000175000017500000012121512253362407017770 0ustar georgeskgeorgesk"""Tools for manipulating of large commutative expressions. """ from __future__ import print_function, division from sympy.core.add import Add from sympy.core.compatibility import iterable, is_sequence, SYMPY_INTS 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, Number, I 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, ordered) from collections import defaultdict def _isnumber(i): return isinstance(i, (SYMPY_INTS, float)) or i.is_Number 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 """Initialize Factors from dict or expr. Examples ======== >>> from sympy.core.exprtools import Factors >>> from sympy.abc import x >>> from sympy import I >>> e = 2*x**3 >>> Factors(e) Factors({2: 1, x: 3}) >>> Factors(e.as_powers_dict()) Factors({2: 1, x: 3}) >>> f = _ >>> f.factors # underlying dictionary {2: 1, x: 3} >>> f.gens # base of each factor frozenset([2, x]) >>> Factors(0) Factors({0: 1}) >>> Factors(I) Factors({I: 1}) Notes ===== Although a dictionary can be passed, only minimal checking is performed: powers of -1 and I are made canonical. """ if isinstance(factors, (SYMPY_INTS, float)): factors = S(factors) if isinstance(factors, Factors): factors = factors.factors.copy() elif factors is None or factors is S.One: factors = {} elif factors is S.Zero or factors == 0: factors = {S.Zero: S.One} elif isinstance(factors, Number): n = factors factors = {} if n < 0: factors[S.NegativeOne] = S.One n = -n if n is not S.One: if n.is_Float or n.is_Integer or n is S.Infinity: factors[n] = S.One elif n.is_Rational: # since we're processing Numbers, the denominator is # stored with a negative exponent; all other factors # are left . if n.p != 1: factors[Integer(n.p)] = S.One factors[Integer(n.q)] = S.NegativeOne else: raise ValueError('Expected Float|Rational|Integer, not %s' % n) elif isinstance(factors, Basic) and not factors.args: factors = {factors: S.One} elif isinstance(factors, Expr): c, nc = factors.args_cnc() i = c.count(I) for _ in range(i): c.remove(I) factors = dict(Mul._from_args(c).as_powers_dict()) if i: factors[I] = S.One*i if nc: factors[Mul(*nc, evaluate=False)] = S.One else: factors = factors.copy() # /!\ should be dict-like # tidy up -/+1 and I exponents if Rational handle = [] for k in factors: if k is I or k in (-1, 1): handle.append(k) if handle: i1 = S.One for k in handle: if not _isnumber(factors[k]): continue i1 *= k**factors.pop(k) if i1 is not S.One: for a in i1.args if i1.is_Mul else [i1]: # at worst, -1.0*I*(-1)**e if a is S.NegativeOne: factors[a] = S.One elif a is I: factors[I] = S.One elif a.is_Pow: if S.NegativeOne not in factors: factors[S.NegativeOne] = S.Zero factors[S.NegativeOne] += a.exp elif a == 1: factors[a] = S.One elif a == -1: factors[-a] = S.One factors[S.NegativeOne] = S.One else: raise ValueError('unexpected factor in i1: %s' % a) self.factors = factors try: self.gens = frozenset(factors.keys()) except AttributeError: raise TypeError('expecting Expr or dictionary') def __hash__(self): # Factors keys = tuple(ordered(self.factors.keys())) values = [self.factors[k] for k in keys] return hash((keys, values)) def __repr__(self): # Factors return "Factors({%s})" % ', '.join( ['%s: %s' % (k, v) for k, v in ordered(self.factors.items())]) @property def is_zero(self): # Factors """ >>> from sympy.core.exprtools import Factors >>> Factors(0).is_zero True """ f = self.factors return len(f) == 1 and S.Zero in f @property def is_one(self): # Factors """ >>> from sympy.core.exprtools import Factors >>> Factors(1).is_one True """ return not self.factors def as_expr(self): # Factors """Return the underlying expression. Examples ======== >>> from sympy.core.exprtools import Factors >>> from sympy.abc import x, y >>> Factors((x*y**2).as_powers_dict()).as_expr() x*y**2 """ args = [] for factor, exp in self.factors.items(): if exp != 1: b, e = factor.as_base_exp() if isinstance(exp, int): e = _keep_coeff(Integer(exp), e) elif isinstance(exp, Rational): e = _keep_coeff(exp, e) else: e *= exp args.append(b**e) else: args.append(factor) return Mul(*args) def mul(self, other): # Factors """Return Factors of ``self * other``. Examples ======== >>> from sympy.core.exprtools import Factors >>> from sympy.abc import x, y, z >>> a = Factors((x*y**2).as_powers_dict()) >>> b = Factors((x*y/z).as_powers_dict()) >>> a.mul(b) Factors({x: 2, y: 3, z: -1}) >>> a*b Factors({x: 2, y: 3, z: -1}) """ if not isinstance(other, Factors): other = Factors(other) if any(f.is_zero for f in (self, other)): return Factors(S.Zero) 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 normal(self, other): """Return ``self`` and ``other`` with ``gcd`` removed from each. The only differences between this and method ``div`` is that this is 1) optimized for the case when there are few factors in common and 2) this does not raise an error if ``other`` is zero. See Also ======== div """ if not isinstance(other, Factors): other = Factors(other) if other.is_zero: return (Factors(), Factors(S.Zero)) if self.is_zero: return (Factors(S.Zero), 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] elif _isnumber(exp): if exp > 0: self_factors[factor] = exp del other_factors[factor] else: del self_factors[factor] other_factors[factor] = -exp else: r = self_exp.extract_additively(other_exp) if r is not None: if r: self_factors[factor] = r del other_factors[factor] else: # should be handled already del self_factors[factor] del other_factors[factor] else: sc, sa = self_exp.as_coeff_Add() if sc: oc, oa = other_exp.as_coeff_Add() diff = sc - oc if diff > 0: self_factors[factor] -= oc other_exp = oa elif diff < 0: self_factors[factor] -= sc other_factors[factor] -= sc other_exp = oa - diff else: self_factors[factor] = sa other_exp = oa if other_exp: other_factors[factor] = other_exp else: del other_factors[factor] return Factors(self_factors), Factors(other_factors) def div(self, other): # Factors """Return ``self`` and ``other`` with ``gcd`` removed from each. This is optimized for the case when there are many factors in common. Examples ======== >>> from sympy.core.exprtools import Factors >>> from sympy.abc import x, y, z >>> from sympy import S >>> a = Factors((x*y**2).as_powers_dict()) >>> a.div(a) (Factors({}), Factors({})) >>> a.div(x*z) (Factors({y: 2}), Factors({z: 1})) The ``/`` operator only gives ``quo``: >>> a/x Factors({y: 2}) Factors treats its factors as though they are all in the numerator, so if you violate this assumption the results will be correct but will not strictly correspond to the numerator and denominator of the ratio: >>> a.div(x/z) (Factors({y: 2}), Factors({z: -1})) Factors is also naive about bases: it does not attempt any denesting of Rational-base terms, for example the following does not become 2**(2*x)/2. >>> Factors(2**(2*x + 2)).div(S(8)) (Factors({2: 2*x + 2}), Factors({8: 1})) factor_terms can clean up such Rational-bases powers: >>> from sympy.core.exprtools import factor_terms >>> n, d = Factors(2**(2*x + 2)).div(S(8)) >>> n.as_expr()/d.as_expr() 2**(2*x + 2)/8 >>> factor_terms(_) 2**(2*x)/2 """ quo, rem = dict(self.factors), {} if not isinstance(other, Factors): other = Factors(other) if other.is_zero: raise ZeroDivisionError if self.is_zero: return (Factors(S.Zero), Factors()) for factor, exp in other.factors.items(): if factor in quo: d = quo[factor] - exp if _isnumber(d): if d <= 0: del quo[factor] if d >= 0: if d: quo[factor] = d continue exp = -d else: r = quo[factor].extract_additively(exp) if r is not None: if r: quo[factor] = r else: # should be handled already del quo[factor] else: other_exp = exp sc, sa = quo[factor].as_coeff_Add() if sc: oc, oa = other_exp.as_coeff_Add() diff = sc - oc if diff > 0: quo[factor] -= oc other_exp = oa elif diff < 0: quo[factor] -= sc other_exp = oa - diff else: quo[factor] = sa other_exp = oa if other_exp: rem[factor] = other_exp else: assert factor not in rem continue rem[factor] = exp return Factors(quo), Factors(rem) def quo(self, other): # Factors """Return numerator Factor of ``self / other``. Examples ======== >>> from sympy.core.exprtools import Factors >>> from sympy.abc import x, y, z >>> a = Factors((x*y**2).as_powers_dict()) >>> b = Factors((x*y/z).as_powers_dict()) >>> a.quo(b) # same as a/b Factors({y: 1}) """ return self.div(other)[0] def rem(self, other): # Factors """Return denominator Factors of ``self / other``. Examples ======== >>> from sympy.core.exprtools import Factors >>> from sympy.abc import x, y, z >>> a = Factors((x*y**2).as_powers_dict()) >>> b = Factors((x*y/z).as_powers_dict()) >>> a.rem(b) Factors({z: -1}) >>> a.rem(a) Factors({}) """ return self.div(other)[1] def pow(self, other): # Factors """Return self raised to a non-negative integer power. Examples ======== >>> from sympy.core.exprtools import Factors >>> from sympy.abc import x, y >>> a = Factors((x*y**2).as_powers_dict()) >>> a**2 Factors({x: 2, y: 4}) """ if isinstance(other, Factors): other = other.as_expr() if other.is_Integer: other = int(other) if isinstance(other, SYMPY_INTS) 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 """Return Factors of ``gcd(self, other)``. The keys are the intersection of factors with the minimum exponent for each factor. Examples ======== >>> from sympy.core.exprtools import Factors >>> from sympy.abc import x, y, z >>> a = Factors((x*y**2).as_powers_dict()) >>> b = Factors((x*y/z).as_powers_dict()) >>> a.gcd(b) Factors({x: 1, y: 1}) """ if not isinstance(other, Factors): other = Factors(other) if other.is_zero: return Factors(self.factors) factors = {} for factor, exp in self.factors.items(): if factor in other.factors: lt = (exp < other.factors[factor]) if lt is True: factors[factor] = exp elif lt is False: factors[factor] = other.factors[factor] return Factors(factors) def lcm(self, other): # Factors """Return Factors of ``lcm(self, other)`` which are the union of factors with the maximum exponent for each factor. Examples ======== >>> from sympy.core.exprtools import Factors >>> from sympy.abc import x, y, z >>> a = Factors((x*y**2).as_powers_dict()) >>> b = Factors((x*y/z).as_powers_dict()) >>> a.lcm(b) Factors({x: 1, y: 2, z: -1}) """ if not isinstance(other, Factors): other = Factors(other) if any(f.is_zero for f in (self, other)): return Factors(S.Zero) 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 return self.mul(other) def __divmod__(self, other): # Factors return self.div(other) def __div__(self, other): # Factors return self.quo(other) __truediv__ = __div__ def __mod__(self, other): # Factors return self.rem(other) def __pow__(self, other): # Factors return self.pow(other) def __eq__(self, other): # Factors if not isinstance(other, Factors): other = Factors(other) 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 isinstance(other, SYMPY_INTS): 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 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) The ``clear`` flag was ignored in this case because the returned expression was a rational expression, not a simple sum. 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]) def factor_terms(expr, radical=False, clear=False, fraction=False, sign=True): """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. If sign=True (default) then even if the only factor in common is a -1, it will be factored out of the expression. Examples ======== >>> from sympy import factor_terms, Symbol >>> 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 If a -1 is all that can be factored out, to *not* factor it out, the flag ``sign`` must be False: >>> factor_terms(-x - y) -(x + y) >>> factor_terms(-x - y, sign=False) -x - y >>> factor_terms(-2*x - 2*y, sign=False) -2*(x + y) See Also ======== gcd_terms, sympy.polys.polytools.terms_gcd """ from sympy.simplify.simplify import bottom_up def do(expr): is_iterable = iterable(expr) if not isinstance(expr, Basic) or expr.is_Atom: if is_iterable: return type(expr)([do(i) 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([do(i) 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 = [do(a) for a in Add.make_args(p)] # get a common negative (if there) which gcd_terms does not remove if all(a.as_coeff_Mul()[0] < 0 for a in list_args): cont = -cont list_args = [-a for a in list_args] # watch out for exp(-(x+2)) which gcd_terms will change to exp(-x-2) special = {} for i, a in enumerate(list_args): b, e = a.as_base_exp() if e.is_Mul and e != Mul(*e.args): list_args[i] = Dummy() special[list_args[i]] = a # rebuild p not worrying about the order which gcd_terms will fix p = Add._from_args(list_args) p = gcd_terms(p, isprimitive=True, clear=clear, fraction=fraction).xreplace(special) elif p.args: p = p.func( *[do(a) for a in p.args]) rv = _keep_coeff(cont, p, clear=clear, sign=sign) return rv expr = sympify(expr) return do(expr) def _mask_nc(eq, name=None): """ 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. ``name``, if given, is the name that will be used with numered Dummy variables that will replace the non-commutative objects and is mainly used for doctesting purposes. 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 symbols, Mul >>> from sympy.core.exprtools import _mask_nc >>> from sympy.abc import x, y >>> A, B, C = symbols('A,B,C', commutative=False) One nc-symbol: >>> _mask_nc(A**2 - x**2, 'd') (_d0**2 - x**2, {_d0: A}, []) Multiple nc-symbols: >>> _mask_nc(A**2 - B**2, 'd') (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), 'd') (_d0*x + 1, {_d0: Commutator(A, B)}, []) >>> _mask_nc(NO(Fd(x)*F(y)), 'd') (_d0, {_d0: NO(CreateFermion(x)*AnnihilateFermion(y))}, []) Multiple nc-objects: >>> eq = x*Commutator(A, B) + x*Commutator(A, C)*Commutator(A, B) >>> _mask_nc(eq, 'd') (x*_d0 + x*_d1*_d0, {_d0: Commutator(A, B), _d1: Commutator(A, C)}, [_d0, _d1]) Multiple nc-objects and nc-symbols: >>> eq = A*Commutator(A, B) + B*Commutator(A, C) >>> _mask_nc(eq, 'd') (A*_d0 + B*_d1, {_d0: Commutator(A, B), _d1: Commutator(A, C)}, [_d0, _d1, 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 >>> eq = (1 + Mul(Basic(), Basic(), evaluate=False)) >>> eq.is_commutative False >>> _mask_nc(eq, 'd') (_d0**2 + 1, {_d0: Basic()}, []) """ name = name or 'mask' # Make Dummy() append sequential numbers to the name def numbered_names(): i = 0 while True: yield name + str(i) i += 1 names = numbered_names() def Dummy(*args, **kwargs): from sympy import Dummy return Dummy(next(names), *args, **kwargs) 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 powsimp from sympy.polys import gcd, factor def _pemexpand(expr): "Expand with the minimal set of hints necessary to check the result." return expr.expand(deep=True, mul=True, power_exp=True, power_base=False, basic=False, multinomial=True, log=False) 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 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 = powsimp(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() if e.is_Integer: ncfac.extend([b]*e) else: ncfac.append(f) pre_mid = g*Mul(*cfac)*l target = _pemexpand(expr/c) for s in variations(ncfac, len(ncfac)): ok = pre_mid*Mul(*s)*r if _pemexpand(ok) == target: return _keep_coeff(c, ok) # mid was an Add that didn't factor successfully return _keep_coeff(c, g*l*mid*r) sympy-0.7.4.1/sympy/core/symbol.py0000644000175000017500000003647612253362407017254 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core.assumptions import StdFactKB from sympy.core.compatibility import string_types 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.iterables import cartes from sympy.utilities.exceptions import SymPyDeprecationWarning import string import re as _re 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 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 for key in assumptions.keys(): assumptions[key] = bool(assumptions[key]) return Symbol.__xnew_cached_(cls, name, **assumptions) def __new_stage2__(cls, name, **assumptions): if not isinstance(name, string_types): raise TypeError("name should be a string, not %s" % 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]) 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() #doctest: +SKIP _Dummy_10 """ _count = 0 __slots__ = ['dummy_index'] is_Dummy = True def __new__(cls, name=None, **assumptions): if name is None: name = "Dummy_" + 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,) 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__) _range = _re.compile('([0-9]*:[0-9]+|[a-zA-Z]?:[a-zA-Z])') 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. Ranges are indicated by a colon and the type of range is determined by the character to the right of the colon. If the character is a digit then all continguous digits to the left are taken as the nonnegative starting value (or 0 if there are no digit of the colon) and all contiguous digits to the right are taken as 1 greater than the ending value:: >>> symbols('x:10') (x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) >>> symbols('x5:10') (x5, x6, x7, x8, x9) >>> symbols('x5(:2)') (x50, x51) >>> 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)) If the character to the right of the colon is a letter, then the single letter to the left (or 'a' if there is none) is taken as the start and all characters in the lexicographic range *through* the letter to the right are used as the range:: >>> symbols('x:z') (x, y, z) >>> symbols('x:c') # null range () >>> symbols('x(:c)') (xa, xb, xc) >>> symbols(':c') (a, b, c) >>> symbols('a:d, x:z') (a, b, c, d, x, y, z) >>> symbols(('a:d', 'x:z')) ((a, b, c, d), (x, y, z)) Multiple ranges are supported; contiguous numerical ranges should be separated by parentheses to disambiguate the ending number of one range from the starting number of the next:: >>> symbols('x:2(1:3)') (x01, x02, x11, x12) >>> symbols(':3:2') # parsing is from left to right (00, 01, 10, 11, 20, 21) Only one pair of parentheses surrounding ranges are removed, so to include parentheses around ranges, double them. And to include spaces, commas, or colons, escape them with a backslash:: >>> symbols('x((a:b))') (x(a), x(b)) >>> symbols('x(:1\,:2)') # or 'x((:1)\,(:2))' (x(0,0), x(0,1)) All newly created symbols have assumptions set according 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 like 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]) """ result = [] if isinstance(names, string_types): marker = 0 literals = ['\,', '\:', '\ '] for i in range(len(literals)): lit = literals.pop(0) if lit in names: while chr(marker) in names: marker += 1 lit_char = chr(marker) marker += 1 names = names.replace(lit, lit_char) literals.append((lit_char, lit[1:])) def literal(s): if literals: for c, l in literals: s = s.replace(c, l) return s names = names.strip() as_seq = names.endswith(',') if as_seq: names = names[:-1].rstrip() if not names: raise ValueError('no symbols given') # split on commas names = [n.strip() for n in names.split(',')] if not all(n for n in names): raise ValueError('missing symbol between commas') # split on spaces for i in range(len(names) - 1, -1, -1): names[i: i + 1] = names[i].split() 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(literal(name), **args) result.append(symbol) continue split = _range.split(name) # remove 1 layer of bounding parentheses around ranges for i in range(len(split) - 1): if i and ':' in split[i] and split[i] != ':' and \ split[i - 1].endswith('(') and \ split[i + 1].startswith(')'): split[i - 1] = split[i - 1][:-1] split[i + 1] = split[i + 1][1:] for i, s in enumerate(split): if ':' in s: if s[-1].endswith(':'): raise ValueError('missing end range') a, b = s.split(':') if b[-1] in string.digits: a = 0 if not a else int(a) b = int(b) split[i] = [str(c) for c in range(a, b)] else: a = a or 'a' split[i] = [string.ascii_letters[c] for c in range( string.ascii_letters.index(a), string.ascii_letters.index(b) + 1)] # inclusive if not split[i]: break else: split[i] = [s] else: seq = True if len(split) == 1: names = split[0] else: names = [''.join(s) for s in cartes(*split)] if literals: result.extend([cls(literal(s), **args) for s in names]) else: result.extend([cls(s, **args) for s in names]) if not seq and len(result) <= 1: if not result: return () return result[0] return tuple(result) else: for name in names: result.append(symbols(name, **args)) return type(names)(result) 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 sympy-0.7.4.1/sympy/core/add.py0000644000175000017500000007145212253362407016470 0ustar georgeskgeorgeskfrom __future__ import print_function, division from collections import defaultdict from sympy.core.core import C from sympy.core.compatibility import reduce, is_sequence 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 def _addsort(args): # in-place sorting of args # 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. args.sort(key=hash) def _unevaluated_Add(*args): """Return a well-formed unevaluated Add: Numbers are collected and put in slot 0 and args are sorted. Use this when args have changed but you still want to return an unevaluated Add. Examples ======== >>> from sympy.core.add import _unevaluated_Add as uAdd >>> from sympy import S, Add >>> from sympy.abc import x, y >>> a = uAdd(*[S(1.0), x, S(2)]) >>> a.args[0] 3.00000000000000 >>> a.args[1] x Beyond the Number being in slot 0, there is no other assurance of order for the arguments since they are hash sorted. So, for testing purposes, output produced by this in some other function can only be tested against the output of this function or as one of several options: >>> opts = (Add(x, y, evaluated=False), Add(y, x, evaluated=False)) >>> a = uAdd(x, y) >>> assert a in opts and a == uAdd(x, y) """ args = list(args) newargs = [] co = S.Zero while args: a = args.pop() if a.is_Add: # this will keep nesting from building up # so that x + (x + 1) -> x + x + 1 (3 args) args.extend(a.args) elif a.is_Number: co += a else: newargs.append(a) _addsort(newargs) if co: newargs.insert(0, co) return Add._from_args(newargs) class Add(Expr, AssocOp): __slots__ = [] is_Add = True #identity = S.Zero # cyclic import, so defined in numbers.py @classmethod 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: 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() # 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 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) elif s.is_Add: # we just re-create the unevaluated Mul newseq.append(Mul(c, s, evaluate=False)) 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_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_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 _addsort(newseq) # 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 def class_key(cls): """Nice order of classes""" return 3, 1, cls.__name__ 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 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 >>> (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 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 self.func(*[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 self.func(*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 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 self.func( *[_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] = self.func(*n) # assemble single numerator and denominator denoms, numers = [list(i) for i in zip(*iter(nd.items()))] n, d = self.func(*[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) def _eval_is_algebraic_expr(self, syms): return all(term._eval_is_algebraic_expr(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_rational = lambda self: self._eval_template_is_attr( 'is_rational', 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 self.func(new, coeff_self, -coeff_old) if terms_self == -terms_old: # (2 + a).subs(-3 - a, y) -> -1 - y return self.func(-new, coeff_self, coeff_old) if coeff_self.is_Rational and coeff_old.is_Rational \ or coeff_self == coeff_old: args_old, args_self = self.func.make_args( terms_old), self.func.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 self.func(new, coeff_self, -coeff_old, *[s._subs(old, new) for s in ret_set]) args_old = self.func.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 self.func(-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 def extract_leading_order(self, symbols, point=None): """ 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 = [] symbols = list(symbols if is_sequence(symbols) else [symbols]) if not point: point = [0]*len(symbols) seq = [(f, C.Order(f, *zip(symbols, point))) 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) 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) >>> ((1 + I)/(1 - I)).as_real_imag() (0, 1) >>> ((1 + 2*I)*(1 + 3*I)).as_real_imag() (-5, 5) """ 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] self = self.func(*[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 self is S.NaN: return old.func._from_args(unbounded) elif not self.is_Add: return self else: plain = self.func(*[s for s, _ in self.extract_leading_order(x)]) rv = factor_terms(plain, fraction=False) rv_simplify = rv.simplify() # 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_simplify.free_symbols: if rv_simplify.is_zero and plain.is_zero is not True: return (self - plain)._eval_as_leading_term(x) return rv_simplify return rv def _eval_adjoint(self): return self.func(*[t.adjoint() for t in self.args]) def _eval_conjugate(self): return self.func(*[t.conjugate() for t in self.args]) def _eval_transpose(self): return self.func(*[t.transpose() for t in self.args]) def __neg__(self): return self.func(*[-t for t in self.args]) def _sage_(self): s = 0 for x in self.args: s += x._sage_() return s 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 _addsort(terms) if c: terms.insert(0, c) return Rational(ngcd, dlcm), self._new_rawargs(*terms) 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 = self.func(*[_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: term_rads[e.q].append(abs(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*prim.func(*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 sympy-0.7.4.1/sympy/core/mod.py0000644000175000017500000000716312253362407016515 0ustar georgeskgeorgeskfrom __future__ import print_function, division from .function import Function 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.core.add import Add from sympy.core.mul import Mul from sympy.core.singleton import S from sympy.core.exprtools import gcd_terms from sympy.polys.polytools import gcd def doit(p, q): """Try to return p % q if both are numbers or +/-p is known to be less than q. """ if p == q or p == -q or p.is_Pow and p.exp.is_Integer and p.base == q: return S.Zero if p.is_Number and q.is_Number: return (p % q) # by ratio r = p/q try: d = int(r) except TypeError: pass else: if type(d) is int: rv = p - d*q if (rv*q < 0) is True: rv += q return rv # by differencec d = p - q if d.is_negative: if q.is_negative: return d elif q.is_positive: return p rv = doit(p, q) if rv is not None: return rv # denest if p.func is cls: # easy qinner = p.args[1] if qinner == q: return p # XXX other possibilities? # extract gcd; any further simplification should be done by the user G = gcd(p, q) if G is not S.One: p, q = [ gcd_terms(i/G, clear=False, fraction=False) for i in (p, q)] pwas, qwas = p, q # simplify terms # (x + y + 2) % x -> Mod(y + 2, x) if p.is_Add: args = [] for i in p.args: a = cls(i, q) if a.count(cls) > i.count(cls): args.append(i) else: args.append(a) if args != list(p.args): p = Add(*args) else: # handle coefficients if they are not Rational # since those are not handled by factor_terms # e.g. Mod(.6*x, .3*y) -> 0.3*Mod(2*x, y) cp, p = p.as_coeff_Mul() cq, q = q.as_coeff_Mul() ok = False if not cp.is_Rational or not cq.is_Rational: r = cp % cq if r == 0: G *= cq p *= int(cp/cq) ok = True if not ok: p = cp*p q = cq*q # simple -1 extraction if p.could_extract_minus_sign() and q.could_extract_minus_sign(): G, p, q = [-i for i in (G, p, q)] # check again to see if p and q can now be handled as numbers rv = doit(p, q) if rv is not None: return rv*G # put 1.0 from G on inside if G.is_Float and G == 1: p *= G return cls(p, q, evaluate=False) elif G.is_Mul and G.args[0].is_Float and G.args[0] == 1: p = G.args[0]*p G = Mul._from_args(G.args[1:]) return G*cls(p, q, evaluate=(p, q) != (pwas, qwas)) sympy-0.7.4.1/sympy/core/numbers.py0000644000175000017500000023714112253362407017412 0ustar georgeskgeorgeskfrom __future__ import print_function, division import decimal import math import re as regex from collections import defaultdict from .core import C from .containers import Tuple from .sympify import converter, sympify, _sympify, SympifyError from .singleton import S, Singleton from .expr import Expr, AtomicExpr from .decorators import _sympifyit, deprecated from .cache import cacheit, clear_cache from sympy.core.compatibility import ( as_int, integer_types, long, string_types, with_metaclass, HAS_GMPY, SYMPY_INTS) 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 ( finf as _mpf_inf, fninf as _mpf_ninf, fnan as _mpf_nan, fzero as _mpf_zero, _normalize as mpf_normalize, prec_to_dps) rnd = mlib.round_nearest _LOG2 = math.log(2) def mpf_norm(mpf, prec): """Return the mpf tuple normalized appropriately for the indicated precision after doing a check to see if zero should be returned or not when the mantissa is 0. ``mpf_normlize`` always assumes that this is zero, but it may not be since the mantissa for mpf's values "+inf", "-inf" and "nan" have a mantissa of zero, too. Note: this is not intended to validate a given mpf tuple, so sending mpf tuples that were not created by mpmath may produce bad results. This is only a wrapper to ``mpf_normalize`` which provides the check for non- zero mpfs that have a 0 for the mantissa. """ 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} 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 int(p), int(q) def _decimal_to_Rational_prec(dec): """Convert an ordinary decimal instance to a Rational.""" if not dec.is_finite(): # NOTE: this is_finite is not SymPy's 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 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 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) 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_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, SYMPY_INTS): 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, string_types): 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 def _eval_is_bounded(self): return True def _eval_is_finite(self): return True @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 elif isinstance(other, Tuple): return NotImplemented 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 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,) 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 def as_coeff_Add(self): """Efficiently extract the coefficient of a summation. """ return self, S.Zero def gcd(self, other): """Compute GCD of `self` and `other`. """ from sympy.polys import gcd return gcd(self, other) def lcm(self, other): """Compute LCM of `self` and `other`. """ from sympy.polys import lcm return lcm(self, other) def cofactors(self, other): """Compute GCD and cofactors of `self` and `other`. """ from sympy.polys import cofactors return cofactors(self, other) class Float(Number): """ Represents a floating point number. It is capable of representing arbitrary-precision floating-point numbers. Examples ======== >>> from sympy import Float >>> 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(int(m), Pow(2, int(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: >>> _._mpf_ (1, 5, 0, 3) This is not needed for instantiation and is not the same thing as the precision. The mpf tuple and the precision are two separate quantities that Float tracks. """ __slots__ = ['_mpf_', '_prec'] is_rational = True is_real = True is_Float = True def __new__(cls, num, prec=15): if isinstance(num, string_types): 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, (SYMPY_INTS, Integer)): num = str(num) # faster than mlib.from_int elif isinstance(num, mpmath.mpf): num = num._mpf_ if prec == '': if not isinstance(num, string_types): raise ValueError('The null string can only be used when ' 'the number to Float is passed as a string or an integer.') 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] = long(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 obj = Expr.__new__(cls) obj._mpf_ = mpf_norm(_mpf_, _prec) 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 != self._mpf_ and self._prec == prec: # print self._mpf_, rv return rv def _as_mpf_op(self, prec): return self._mpf_, max(prec, self._prec) def _eval_is_bounded(self): if self._mpf_ in (_mpf_inf, _mpf_ninf): return False return True def _eval_is_finite(self): if self._mpf_ in (_mpf_inf, _mpf_ninf, _mpf_zero): return False return True def _eval_is_integer(self): return self._mpf_ == _mpf_zero def _eval_is_negative(self): if self._mpf_ == _mpf_ninf: return True if self._mpf_ == _mpf_inf: return False return self.num < 0 def _eval_is_positive(self): if self._mpf_ == _mpf_inf: return True if self._mpf_ == _mpf_ninf: return False return self.num > 0 def _eval_is_zero(self): return self._mpf_ == _mpf_zero def __nonzero__(self): return self._mpf_ != _mpf_zero __bool__ = __nonzero__ 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) and other != 0: 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, Rational) and other.q != 1: # calculate mod with Rationals, *then* round the result return Float(Rational.__mod__(Rational(self), other), prec_to_dps(self._prec)) if isinstance(other, Float): r = self/other if r == int(r): prec = max([prec_to_dps(i) for i in (self._prec, other._prec)]) return Float(0, prec) 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, Float): return other.__mod__(self) 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 self == 0: if expt.is_positive: return S.Zero if expt.is_negative: return Float('inf') 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): if self._mpf_ == _mpf_zero: return 0 return int(mlib.to_int(self._mpf_)) # uses round_fast = round_down __long__ = __int__ 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="1e-15"): 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 class Rational(Number): """Represents integers and rational numbers (p/q) of any size. Examples ======== >>> from sympy import Rational, nsimplify, S, pi >>> 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.75 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, string_types): 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: 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, SYMPY_INTS + (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 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): # calculate mod with Rationals, *then* round the answer return Float(self.__mod__(Rational(other)), prec_to_dps(other._prec)) return Number.__mod__(self, other) @_sympifyit('other', NotImplemented) def __rmod__(self, other): if isinstance(other, Rational): return Rational.__mod__(other, self) 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 __long__ = __int__ 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 other is S.NaN: return None 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__() 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 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) @_sympifyit('other', NotImplemented) def gcd(self, other): if isinstance(other, Rational): if other is S.Zero: return other return Rational( Integer(igcd(self.p, other.p)), Integer(ilcm(self.q, other.q))) return Number.gcd(self, other) @_sympifyit('other', NotImplemented) def lcm(self, other): if isinstance(other, Rational): return Rational( self.p*other.p//igcd(self.p, other.p), igcd(self.q, other.q)) return Number.lcm(self, other) 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) 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 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, string_types): 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 __long__ = __int__ 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, integer_types): 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, integer_types): 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, integer_types): return Integer(other + self.p) return Rational.__add__(self, other) def __sub__(self, other): if isinstance(other, integer_types): 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, integer_types): return Integer(other - self.p) return Rational.__rsub__(self, other) def __mul__(self, other): if isinstance(other, integer_types): 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, integer_types): return Integer(other*self.p) return Rational.__mul__(self, other) def __mod__(self, other): if isinstance(other, integer_types): 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, integer_types): 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, integer_types): 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, integer_types): 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, integer_types): 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, integer_types): 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, integer_types): 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 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 for i_type in integer_types: converter[i_type] = 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(with_metaclass(Singleton, IntegerConstant)): 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 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 __nonzero__(self): return False __bool__ = __nonzero__ class One(with_metaclass(Singleton, IntegerConstant)): 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(with_metaclass(Singleton, IntegerConstant)): 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(with_metaclass(Singleton, RationalConstant)): p = 1 q = 2 __slots__ = [] @staticmethod def __abs__(): return S.Half class Infinity(with_metaclass(Singleton, Number)): 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) def _latex(self, printer): return r"\infty" @_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(with_metaclass(Singleton, Number)): 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) def _latex(self, printer): return r"-\infty" @_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(with_metaclass(Singleton, Number)): """ 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) def _latex(self, printer): return r"\mathrm{NaN}" @_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(with_metaclass(Singleton, AtomicExpr)): is_commutative = True is_bounded = False is_real = None is_number = False __slots__ = [] def __new__(cls): return AtomicExpr.__new__(cls) def _latex(self, printer): return r"\tilde{\infty}" @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 class NumberSymbol(AtomicExpr): is_commutative = True is_bounded = True is_finite = True is_number = True __slots__ = [] is_NumberSymbol = True def __new__(cls): return AtomicExpr.__new__(cls) 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 __long__(self): return self.__int__() def __hash__(self): return super(NumberSymbol, self).__hash__() class Exp1(with_metaclass(Singleton, NumberSymbol)): is_real = True is_positive = True is_negative = False # XXX Forces is_negative/is_nonnegative is_irrational = True __slots__ = [] def _latex(self, printer): return r"e" @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 _eval_rewrite_as_sin(self): I = S.ImaginaryUnit return C.sin(I + S.Pi/2) - I*C.sin(I) def _eval_rewrite_as_cos(self): I = S.ImaginaryUnit return C.cos(I) + I*C.cos(I + S.Pi/2) def _sage_(self): import sage.all as sage return sage.e E = S.Exp1 class Pi(with_metaclass(Singleton, NumberSymbol)): is_real = True is_positive = True is_negative = False is_irrational = True __slots__ = [] def _latex(self, printer): return r"\pi" @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(with_metaclass(Singleton, NumberSymbol)): is_real = True is_positive = True is_negative = False is_irrational = True __slots__ = [] def _latex(self, printer): return r"\phi" 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(with_metaclass(Singleton, NumberSymbol)): is_real = True is_positive = True is_negative = False is_irrational = None __slots__ = [] def _latex(self, printer): return r"\gamma" 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(with_metaclass(Singleton, NumberSymbol)): 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(with_metaclass(Singleton, AtomicExpr)): is_commutative = True is_imaginary = True is_bounded = True is_finite = True is_number = True __slots__ = [] def _latex(self, printer): return r"i" @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: if HAS_GMPY == 2: import gmpy2 as gmpy elif HAS_GMPY == 1: import gmpy else: raise ImportError def sympify_mpz(x): return Integer(long(x)) def sympify_mpq(x): return Rational(long(x.numerator), long(x.denominator)) 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() sympy-0.7.4.1/sympy/core/logic.py0000644000175000017500000001656112253362407017035 0ustar georgeskgeorgesk"""Logic expressions handling NOTE ---- at present this is mainly needed for facts.py , feel free however to improve this stuff for general purpose. """ from __future__ import print_function, division from sympy.core.compatibility import iterable def fuzzy_bool(x): """Return True, False or None according to x. Whereas bool(x) returns True or False, fuzzy_bool allows for the None value. """ if x is None: return None return bool(x) def fuzzy_and(args): """Return True (all True), False (any False) or None. >>> from sympy.core.logic import fuzzy_and >>> from sympy import Dummy If you had a list of objects to test the commutivity of and you want the fuzzy_and logic applied, passing an iterator will allow the commutativity to only be computed as many times as necessary. With this list, False can be returned after analyzing the first symbol: >>> syms = [Dummy(commutative=False), Dummy()] >>> fuzzy_and(s.is_commutative for s in syms) False That False would require less work than if a list of pre-computed items was sent: >>> fuzzy_and([s.is_commutative for s in syms]) False """ rv = True for ai in args: ai = fuzzy_bool(ai) if ai is False: return False if rv: # this will stop updating if a None is ever trapped rv = ai return rv def fuzzy_not(v): """ Not in fuzzy logic Will return Not if arg is a boolean value, and None if argument is None. Examples: >>> from sympy.core.logic import fuzzy_not >>> fuzzy_not(True) False >>> fuzzy_not(None) >>> fuzzy_not(False) True """ if v is None: return v else: return not v def fuzzy_or(args): """ Or in fuzzy logic. Returns True (any True), False (all False), or None See the docstrings of fuzzy_and and fuzzy_not for more info. fuzzy_or is related to the two by the standard De Morgan's law. >>> from sympy.core.logic import fuzzy_or >>> fuzzy_or([True, False]) True >>> fuzzy_or([True, None]) True >>> fuzzy_or([False, False]) False >>> print(fuzzy_or([False, None])) None """ return fuzzy_not(fuzzy_and(fuzzy_not(i) for i in args)) class Logic(object): """Logical expression""" # {} 'op' -> LogicClass op_2class = {} def __new__(cls, *args): obj = object.__new__(cls) obj.args = args return obj def __getnewargs__(self): return self.args def __hash__(self): return hash( (type(self).__name__,) + tuple(self.args) ) def __eq__(a, b): if not isinstance(b, type(a)): return False else: return a.args == b.args def __ne__(a, b): if not isinstance(b, type(a)): return True else: return a.args != b.args def __lt__(cls, other): if cls.__cmp__(other) == -1: return True return False def __cmp__(a, b): if type(a) is not type(b): a = str(type(a)) b = str(type(b)) else: a = a.args b = b.args return (a > b) - (a < b) def __str__(self): return '%s(%s)' % (self.__class__.__name__, ', '.join(str(a) for a in self.args)) __repr__ = __str__ @staticmethod def fromstring(text): """Logic from string e.g. !a & !b | c """ lexpr = None # current logical expression schedop = None # scheduled operation for term in text.split(): # operation symbol if term in '&|': if schedop is not None: raise ValueError( 'double op forbidden: "%s %s"' % (term, schedop)) if lexpr is None: raise ValueError( '%s cannot be in the beginning of expression' % term) schedop = term continue if term[0] == '!': term = Not(term[1:]) # already scheduled operation, e.g. '&' if schedop: lexpr = Logic.op_2class[schedop](lexpr, term) schedop = None continue # this should be atom if lexpr is not None: raise ValueError( 'missing op between "%s" and "%s"' % (lexpr, term)) lexpr = term # let's check that we ended up in correct state if schedop is not None: raise ValueError('premature end-of-expression in "%s"' % text) if lexpr is None: raise ValueError('"%s" is empty' % text) # everything looks good now return lexpr class AndOr_Base(Logic): def __new__(cls, *args): bargs = [] for a in args: if a == cls.op_x_notx: return a elif a == (not cls.op_x_notx): continue # skip this argument bargs.append(a) args = cls.flatten(bargs) args = set(args) for a in args: if Not(a) in args: return cls.op_x_notx if len(args) == 1: return args.pop() elif len(args) == 0: return not cls.op_x_notx return Logic.__new__(cls, *sorted(args, key=hash)) @classmethod def flatten(cls, args): # quick-n-dirty flattening for And and Or args_queue = list(args) res = [] while True: try: arg = args_queue.pop(0) except IndexError: break if isinstance(arg, Logic): if isinstance(arg, cls): args_queue.extend(arg.args) continue res.append(arg) args = tuple(res) return args class And(AndOr_Base): op_x_notx = False def _eval_propagate_not(self): # !(a&b&c ...) == !a | !b | !c ... return Or( *[Not(a) for a in self.args] ) # (a|b|...) & c == (a&c) | (b&c) | ... def expand(self): # first locate Or for i in range(len(self.args)): arg = self.args[i] if isinstance(arg, Or): arest = self.args[:i] + self.args[i + 1:] orterms = [And( *(arest + (a,)) ) for a in arg.args] for j in range(len(orterms)): if isinstance(orterms[j], Logic): orterms[j] = orterms[j].expand() res = Or(*orterms) return res else: return self class Or(AndOr_Base): op_x_notx = True def _eval_propagate_not(self): # !(a|b|c ...) == !a & !b & !c ... return And( *[Not(a) for a in self.args] ) class Not(Logic): def __new__(cls, arg): if isinstance(arg, str): return Logic.__new__(cls, arg) elif isinstance(arg, bool): return not arg elif isinstance(arg, Not): return arg.args[0] elif isinstance(arg, Logic): # XXX this is a hack to expand right from the beginning arg = arg._eval_propagate_not() return arg else: raise ValueError('Not: unknown argument %r' % (arg,)) @property def arg(self): return self.args[0] Logic.op_2class['&'] = And Logic.op_2class['|'] = Or Logic.op_2class['!'] = Not sympy-0.7.4.1/sympy/core/evalf.py0000644000175000017500000013152012253362407017026 0ustar georgeskgeorgesk""" Adaptive numerical evaluation of SymPy expressions, using mpmath for mathematical functions. """ from __future__ import print_function, division import math 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 (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 import bitcount as mpmath_bitcount from sympy.mpmath.libmp.backend import MPZ from sympy.mpmath.libmp.libmpc import _infs_nan from sympy.mpmath.libmp.libmpf import dps_to_prec from sympy.mpmath.libmp.gammazeta import mpf_bernoulli from .compatibility import SYMPY_INTS from .sympify import sympify from .core import C from .singleton import S from .containers import Tuple LG10 = math.log(10, 2) rnd = round_nearest def bitcount(n): return mpmath_bitcount(int(n)) # 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 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 isinstance(mag, SYMPY_INTS): 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 """ from sympy.core.core import C terms = [t for t in terms if not iszero(t)] if not terms: return None, None elif len(terms) == 1: return terms[0] # see if any argument is NaN or oo and thus warrants a special return special = [] for t in terms: arg = C.Float._new(t[0], 1) if arg is S.NaN or arg.is_unbounded: special.append(arg) if special: from sympy.core.add import Add rv = evalf(Add(*special), prec + 4, {}) return rv[0], rv[2] 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): from sympy.core.core import C 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) # see if any argument is NaN or oo and thus warrants a special return special = [] for arg in args: arg = evalf(arg, prec, options) if arg[0] is None: continue arg = C.Float._new(arg[0], 1) if arg is S.NaN or arg.is_unbounded: special.append(arg) if special: from sympy.core.mul import Mul special = Mul(*special) return evalf(special, prec + 4, {}) # 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 complex arguments. TODO: should also handle tan of 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: if 'subs' in options: v = v.subs(options['subs']) return evalf(v._eval_evalf(prec), prec, options) 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 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): limits = expr.limits if len(limits) != 1 or len(limits[0]) != 3: raise NotImplementedError 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): if 'subs' in options: expr = expr.subs(options['subs']) func = expr.function limits = expr.limits if len(limits) != 1 or len(limits[0]) != 3: raise NotImplementedError if func is S.Zero: return mpf(0), None, None, None 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]) re, im, re_acc, im_acc = evalf(s, prec2, options) 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= Substitute numerical values for symbols, e.g. subs={x:3, y:1+pi}. maxn= Allow a maximum temporary working precision of maxn digits (default=100) chop= Replace tiny real or imaginary parts in subresults by exact zeros (default=False) strict= Raise PrecisionExhausted if any subresult fails to evaluate to full accuracy, given the available maxprec (default=False) quad= Choose algorithm for numerical quadrature. By default, tanh-sinh quadrature is used. For oscillatory integrals on an infinite interval, try quad='osc'. verbose= 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)) 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, 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) sympy-0.7.4.1/sympy/core/compatibility.py0000644000175000017500000005403712253362407020611 0ustar georgeskgeorgesk""" 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 __future__ import print_function, division import operator from collections import defaultdict from sympy.external import import_module """ Python 2 and Python 3 compatible imports String and Unicode compatible changes: * `unicode()` removed in Python 3, import `unicode` for Python 2/3 compatible function * `unichr()` removed in Python 3, import `unichr` for Python 2/3 compatible function * Use `u()` for escaped unicode sequences (e.g. u'\u2020' -> u('\u2020')) * Use `u_decode()` to decode utf-8 formatted unicode strings * `string_types` gives str in Python 3, unicode and str in Python 2, equivalent to basestring Integer related changes: * `long()` removed in Python 3, import `long` for Python 2/3 compatible function * `integer_types` gives int in Python 3, int and long in Python 2 Types related changes: * `class_types` gives type in Python 3, type and ClassType in Python 2 Renamed function attributes: * Python 2 `.func_code`, Python 3 `.__func__`, access with `get_function_code()` * Python 2 `.func_globals`, Python 3 `.__globals__`, access with `get_function_globals()` * Python 2 `.func_name`, Python 3 `.__name__`, access with `get_function_name()` Moved modules: * `reduce()` * `StringIO()` * `cStringIO()` (same as `StingIO()` in Python 3) * Python 2 `__builtins__`, access with Python 3 name, `builtins` Iterator/list changes: * `xrange` removed in Python 3, import `xrange` for Python 2/3 compatible iterator version of range exec: * Use `exec_()`, with parameters `exec_(code, globs=None, locs=None)` Metaclasses: * Use `with_metaclass()`, examples below * Define class `Foo` with metaclass `Meta`, and no parent: class Foo(with_metaclass(Meta)): pass * Define class `Foo` with metaclass `Meta` and parent class `Bar`: class Foo(with_metaclass(Meta, Bar)): pass """ import sys PY3 = sys.version_info[0] > 2 if PY3: class_types = type, integer_types = (int,) string_types = (str,) long = int # String / unicode compatibility unicode = str unichr = chr def u(x): return x def u_decode(x): return x Iterator = object # Moved definitions get_function_code = operator.attrgetter("__code__") get_function_globals = operator.attrgetter("__globals__") get_function_name = operator.attrgetter("__name__") import builtins from functools import reduce from io import StringIO cStringIO = StringIO exec_ = getattr(builtins, "exec") xrange = range else: import codecs import types class_types = (type, types.ClassType) integer_types = (int, long) string_types = (str, unicode) long = long # String / unicode compatibility unicode = unicode unichr = unichr def u(x): return codecs.unicode_escape_decode(x)[0] def u_decode(x): return x.decode('utf-8') class Iterator(object): def next(self): return type(self).__next__(self) # Moved definitions get_function_code = operator.attrgetter("func_code") get_function_globals = operator.attrgetter("func_globals") get_function_name = operator.attrgetter("func_name") import __builtin__ as builtins reduce = reduce from StringIO import StringIO from cStringIO import StringIO as cStringIO def exec_(_code_, _globs_=None, _locs_=None): """Execute code in a namespace.""" if _globs_ is None: frame = sys._getframe(1) _globs_ = frame.f_globals if _locs_ is None: _locs_ = frame.f_locals del frame elif _locs_ is None: _locs_ = _globs_ exec("exec _code_ in _globs_, _locs_") xrange = xrange def with_metaclass(meta, *bases): """ Create a base class with a metaclass. For example, if you have the metaclass >>> class Meta(type): ... pass Use this as the metaclass by doing >>> from sympy.core.compatibility import with_metaclass >>> class MyClass(with_metaclass(Meta, object)): ... pass This is equivalent to the Python 2:: class MyClass(object): __metaclass__ = Meta or Python 3:: class MyClass(object, metaclass=Meta): pass That is, the first argument is the metaclass, and the remaining arguments are the base classes. Note that if the base class is just ``object``, you may omit it. >>> MyClass.__mro__ (, <... 'object'>) >>> type(MyClass) """ class metaclass(meta): __call__ = type.__call__ __init__ = type.__init__ def __new__(cls, name, this_bases, d): if this_bases is None: return type.__new__(cls, name, (), d) return meta(name, bases, d) return metaclass("NewBase", None, {}) # 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. def iterable(i, exclude=(string_types, 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('%s %s' % (iterable(i), type(i))) True <... 'list'> True <... 'tuple'> True <... 'set'> True 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 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)) try: from functools import cmp_to_key except ImportError: # <= Python 2.6 def cmp_to_key(mycmp): """ Convert a cmp= function into a key= function """ 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: from itertools import zip_longest except ImportError: # <= Python 2.7 from itertools import izip_longest as zip_longest try: from itertools import combinations_with_replacement except ImportError: # <= Python 2.6 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(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) 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 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=string_types): if isinstance(item, dict): args = 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, string_types): 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 >>> 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) # If HAS_GMPY is 0, no supported version of gmpy is available. Otherwise, # HAS_GMPY contains the major version number of gmpy; i.e. 1 for gmpy, and # 2 for gmpy2. # Versions of gmpy prior to 1.03 do not work correctly with int(largempz) # For example, int(gmpy.mpz(2**256)) would raise OverflowError. # See issue 1881. # Minimum version of gmpy changed to 1.13 to allow a single code base to also # work with gmpy2. def _getenv(key, default=None): from os import getenv return getenv(key, default) GROUND_TYPES = _getenv('SYMPY_GROUND_TYPES', 'auto').lower() HAS_GMPY = 0 if GROUND_TYPES != 'python': # Don't try to import gmpy2 if ground types is set to gmpy1. This is # primarily intended for testing. if GROUND_TYPES != 'gmpy1': gmpy = import_module('gmpy2', min_module_version='2.0.0', module_version_attr='version', module_version_attr_call_args=()) if gmpy: HAS_GMPY = 2 else: GROUND_TYPES = 'gmpy' if not HAS_GMPY: gmpy = import_module('gmpy', min_module_version='1.13', module_version_attr='version', module_version_attr_call_args=()) if gmpy: HAS_GMPY = 1 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' # SYMPY_INTS is a tuple containing the base types for valid integer types. SYMPY_INTS = integer_types if GROUND_TYPES == 'gmpy': SYMPY_INTS += (type(gmpy.mpz(0)),) # check_output() is new in Python 2.7 import os try: try: from subprocess import check_output except ImportError: # <= Python 2.6 from subprocess import CalledProcessError, check_call def check_output(*args, **kwargs): with open(os.devnull, 'w') as fh: kwargs['stdout'] = fh try: return check_call(*args, **kwargs) except CalledProcessError as e: e.output = ("program output is not available for Python 2.6.x") raise e except ImportError: # running on platform like App Engine, no subprocess at all pass sympy-0.7.4.1/sympy/core/trace.py0000644000175000017500000001401612253362407017027 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import Expr, Add, Mul, Matrix, Pow, sympify, Matrix, Tuple from sympy.core.compatibility import xrange from sympy.utilities import default_sort_key def _is_scalar(e): """ Helper method used in Tr""" # sympify to set proper attributes e = sympify(e) if isinstance(e, Expr): if (e.is_Integer or e.is_Float or e.is_Rational or e.is_Number or (e.is_Symbol and e.is_commutative) ): return True return False def _cycle_permute(l): """ Cyclic permutations based on canonical ordering This method does the sort based ascii values while a better approach would be to used lexicographic sort. TODO: Handle condition such as symbols have subscripts/superscripts in case of lexicographic sort """ if len(l) == 1: return l min_item = min(l, key=default_sort_key) indices = [i for i, x in enumerate(l) if x == min_item] le = list(l) le.extend(l) # duplicate and extend string for easy processing # adding the first min_item index back for easier looping indices.append(len(l) + indices[0]) # create sublist of items with first item as min_item and last_item # in each of the sublist is item just before the next occurence of # minitem in the cycle formed. sublist = [[le[indices[i]:indices[i + 1]]] for i in xrange(len(indices) - 1)] # we do comparison of strings by comparing elements # in each sublist idx = sublist.index(min(sublist)) ordered_l = le[indices[idx]:indices[idx] + len(l)] return ordered_l def _rearrange_args(l): """ this just moves the last arg to first position to enable expansion of args A,B,A ==> A**2,B """ if len(l) == 1: return l x = list(l[-1:]) x.extend(l[0:-1]) return Mul(*x).args class Tr(Expr): """ Generic Trace operation than can trace over: a) sympy matrix b) operators c) outer products Parameters ========== o : operator, matrix, expr i : tuple/list indices (optional) Examples ======== # TODO: Need to handle printing a) Trace(A+B) = Tr(A) + Tr(B) b) Trace(scalar*Operator) = scalar*Trace(Operator) >>> from sympy.core.trace import Tr >>> from sympy import symbols, Matrix >>> a, b = symbols('a b', commutative=True) >>> A, B = symbols('A B', commutative=False) >>> Tr(a*A,[2]) a*Tr(A) >>> m = Matrix([[1,2],[1,1]]) >>> Tr(m) 2 """ def __new__(cls, *args): """ Construct a Trace object. Parameters ========== args = sympy expression indices = tuple/list if indices, optional """ # expect no indices,int or a tuple/list/Tuple if (len(args) == 2): if not isinstance(args[1], (list, Tuple, tuple)): indices = Tuple(args[1]) else: indices = Tuple(*args[1]) expr = args[0] elif (len(args) == 1): indices = Tuple() expr = args[0] else: raise ValueError("Arguments to Tr should be of form " "(expr[, [indices]])") if isinstance(expr, Matrix): return expr.trace() elif hasattr(expr, 'trace') and callable(expr.trace): #for any objects that have trace() defined e.g numpy return expr.trace() elif isinstance(expr, Add): return Add(*[Tr(arg, indices) for arg in expr.args]) elif isinstance(expr, Mul): c_part, nc_part = expr.args_cnc() if len(nc_part) == 0: return Mul(*c_part) else: obj = Expr.__new__(cls, Mul(*nc_part), indices ) #this check is needed to prevent cached instances #being returned even if len(c_part)==0 return Mul(*c_part)*obj if len(c_part) > 0 else obj elif isinstance(expr, Pow): if (_is_scalar(expr.args[0]) and _is_scalar(expr.args[1])): return expr else: return Expr.__new__(cls, expr, indices) else: if (_is_scalar(expr)): return expr return Expr.__new__(cls, expr, indices) def doit(self, **kwargs): """ Perform the trace operation. #TODO: Current version ignores the indices set for partial trace. >>> from sympy.core.trace import Tr >>> from sympy.physics.quantum.operator import OuterProduct >>> from sympy.physics.quantum.spin import JzKet, JzBra >>> t = Tr(OuterProduct(JzKet(1,1), JzBra(1,1))) >>> t.doit() 1 """ if hasattr(self.args[0], '_eval_trace'): return self.args[0]._eval_trace(indices=self.args[1]) return self @property def is_number(self): #TODO : This function to be reviewed # and implementation improved. return True #TODO: Review if the permute method is needed # and if it needs to return a new instance def permute(self, pos): """ Permute the arguments cyclically. Parameters ========== pos : integer, if positive, shift-right, else shift-left Examples ========= >>> from sympy.core.trace import Tr >>> from sympy import symbols >>> A, B, C, D = symbols('A B C D', commutative=False) >>> t = Tr(A*B*C*D) >>> t.permute(2) Tr(C*D*A*B) >>> t.permute(-2) Tr(C*D*A*B) """ if pos > 0: pos = pos % len(self.args[0].args) else: pos = -(abs(pos) % len(self.args[0].args)) args = list(self.args[0].args[-pos:] + self.args[0].args[0:-pos]) return Tr(Mul(*(args))) def _hashable_content(self): if isinstance(self.args[0], Mul): args = _cycle_permute(_rearrange_args(self.args[0].args)) else: args = [self.args[0]] return tuple(args) + (self.args[1], ) sympy-0.7.4.1/sympy/core/assumptions.py0000644000175000017500000002026612253362407020322 0ustar georgeskgeorgesk""" This module contains the machinery handling assumptions. All symbolic objects have assumption attributes that can be accessed via .is_ attribute. Assumptions determine certain properties of symbolic objects. Assumptions can have 3 possible values: True, False, None. None is returned when it is impossible to say something about the property. For example, a generic Symbol is not known beforehand to be positive. By default, all symbolic values are in the largest set in the given context without specifying the property. For example, a symbol that has a property being integer, is also real, complex, etc. Here follows a list of possible assumption names: - commutative - object commutes with any other object with respect to multiplication operation. - real - object can have only values from the set of real numbers - integer - object can have only values from the set of integers - bounded - object absolute value is bounded - positive - object can have only positive values - negative - object can have only negative values - nonpositive - object can have only nonpositive values - nonnegative - object can have only nonnegative values - irrational - object value cannot be represented exactly by Rational - unbounded - object value is arbitrarily large - infinitesimal - object value is infinitesimal Implementation note: assumption values are stored in ._assumptions dictionary or are returned by getter methods (with property decorators) or are attributes of objects/classes. Examples ======== >>> from sympy import Symbol >>> Symbol('x', real = True) x """ from __future__ import print_function, division from sympy.core.facts import FactRules, FactKB from sympy.core.core import BasicMeta from sympy.core.compatibility import integer_types, with_metaclass # This are the rules under which our assumptions function # # References # ---------- # # negative, -- http://en.wikipedia.org/wiki/Negative_number # nonnegative # # even, odd -- http://en.wikipedia.org/wiki/Parity_(mathematics) # imaginary -- http://en.wikipedia.org/wiki/Imaginary_number # composite -- http://en.wikipedia.org/wiki/Composite_number # finite -- http://en.wikipedia.org/wiki/Finite # infinitesimal -- http://en.wikipedia.org/wiki/Infinitesimal # irrational -- http://en.wikipedia.org/wiki/Irrational_number # ... _assume_rules = FactRules([ 'integer -> rational', 'rational -> real', 'real -> complex', 'real -> hermitian', 'imaginary -> complex', 'imaginary -> antihermitian', 'complex -> commutative', 'odd == integer & !even', 'even == integer & !odd', 'real == negative | zero | positive', 'negative == nonpositive & nonzero', 'positive == nonnegative & nonzero', 'zero == nonnegative & nonpositive', 'nonpositive == real & !positive', 'nonnegative == real & !negative', 'zero -> infinitesimal & even', 'prime -> integer & positive', 'composite == integer & positive & !prime', 'irrational == real & !rational', 'imaginary -> !real', '!bounded == unbounded', 'noninteger == real & !integer', '!zero == nonzero', # XXX do we need this ? 'finite -> bounded', # XXX do we need this? 'finite -> !zero', # XXX wrong? 'infinitesimal -> !finite', # XXX is this ok? ]) _assume_defined = _assume_rules.defined_facts.copy() _assume_defined.add('polar') _assume_defined = frozenset(_assume_defined) class StdFactKB(FactKB): """A FactKB specialised for the built-in rules This is the only kind of FactKB that Basic objects should use. """ rules = _assume_rules def __init__(self, facts=None): if facts: self.deduce_all_facts(facts) def copy(self): return self.__class__(self) def as_property(fact): """Convert a fact name to the name of the corresponding property""" return 'is_%s' % fact def make_property(fact): """Create the automagic property corresponding to a fact.""" def getit(self): try: return self._assumptions[fact] except KeyError: if self._assumptions is self.default_assumptions: self._assumptions = self.default_assumptions.copy() return _ask(fact, self) getit.func_name = as_property(fact) return property(getit) def _ask(fact, obj): """ Find the truth value for a property of an object. This function is called when a request is made to see what a fact value is. For this we use several techniques: First, the fact-evaluation function is tried, if it exists (for example _eval_is_integer). Then we try related facts. For example rational --> integer another example is joined rule: integer & !odd --> even so in the latter case if we are looking at what 'even' value is, 'integer' and 'odd' facts will be asked. In all cases, when we settle on some fact value, its implications are deduced, and the result is cached in ._assumptions. """ assumptions = obj._assumptions handler_map = obj._prop_handler # Store None into the assumptions so that recursive attempts at # evaluating the same fact don't trigger infinite recursion. assumptions._tell(fact, None) # First try the assumption evaluation function if it exists try: evaluate = handler_map[fact] except KeyError: pass else: a = evaluate(obj) if a is not None: assumptions.deduce_all_facts(((fact, a),)) return a # Try assumption's prerequisites for pk in _assume_rules.prereq[fact]: if pk in assumptions: continue if pk in handler_map: _ask(pk, obj) # we might have found the value of fact ret_val = assumptions.get(fact) if ret_val is not None: return ret_val # Note: the result has already been cached return None class ManagedProperties(with_metaclass(BasicMeta, BasicMeta)): """Metaclass for classes with old-style assumptions""" def __init__(cls, *args, **kws): BasicMeta.__init__(cls, *args, **kws) local_defs = {} for k in _assume_defined: attrname = as_property(k) v = cls.__dict__.get(attrname, '') if isinstance(v, (bool, integer_types, type(None))): if v is not None: v = bool(v) local_defs[k] = v defs = {} for base in reversed(cls.__bases__): try: defs.update(base._explicit_class_assumptions) except AttributeError: pass defs.update(local_defs) cls._explicit_class_assumptions = defs cls.default_assumptions = StdFactKB(defs) cls._prop_handler = {} for k in _assume_defined: try: cls._prop_handler[k] = getattr(cls, '_eval_is_%s' % k) except AttributeError: pass # Put definite results directly into the class dict, for speed for k, v in cls.default_assumptions.items(): setattr(cls, as_property(k), v) # protection e.g. for Integer.is_even=F <- (Rational.is_integer=F) derived_from_bases = set() for base in cls.__bases__: try: derived_from_bases |= set(base.default_assumptions) except AttributeError: continue # not an assumption-aware class for fact in derived_from_bases - set(cls.default_assumptions): pname = as_property(fact) if pname not in cls.__dict__: setattr(cls, pname, make_property(fact)) # Finally, add any missing automagic property (e.g. for Basic) for fact in _assume_defined: pname = as_property(fact) if not hasattr(cls, pname): setattr(cls, pname, make_property(fact)) sympy-0.7.4.1/sympy/core/power.py0000644000175000017500000011613112253362407017066 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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, xrange from sympy.mpmath.libmp import sqrtrem as mpmath_sqrtrem from sympy.utilities.iterables import sift 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 class Pow(Expr): is_Pow = True __slots__ = ['is_commutative'] @cacheit def __new__(cls, b, e, evaluate=True): from sympy.functions.elementary.exponential import exp_polar # 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: # recognize base as E if not e.is_Atom and b is not S.Exp1 and b.func is not exp_polar: from sympy import numer, denom, log, sign, im, factor_terms c, ex = factor_terms(e, sign=False).as_coeff_Mul() den = denom(ex) if den.func is log and den.args[0] == b: return S.Exp1**(c*numer(ex)) elif den.is_Add: s = sign(im(b)) if s.is_Number and s and den == \ log(-factor_terms(b, sign=False)) + s*S.ImaginaryUnit*S.Pi: return S.Exp1**(c*numer(ex)) 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 # Special case for when b is nan. See pull req 1714 for details if b is S.NaN: smallarg = (abs(e) <= S.Zero) else: 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 -self.func(b, e*other) if (other.is_integer or e.is_real and (b_nneg or (abs(e) < 1) is True) or e.is_real is False and smallarg is True or b.is_polar): return self.func(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 b is S.NegativeOne: return True 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 = self.func(*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 elif self.base is S.NegativeOne: 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 self.func(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 self.func(new, pow) # (2**x).subs(exp(x*log(2)), z) -> z 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(self.func(self.base, x)) return Mul(*expr) return self.func(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) if cargs: rv *= Mul(*cargs)**e return rv if not cargs: return self.func(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(*[self.func(b, e, evaluate=False) for b in cargs]) if other: rv *= self.func(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""" base, exp = self.args result = self 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 result else: radical, result = self.func(base, exp - n), [] expanded_base_n = self.func(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 b in base.args: if b.is_Order: order_terms.append(b) else: other_terms.append(b) if order_terms: # (f(x) + O(x^n))^m -> f(x)^m + m*f(x)^{m-1} *O(x^n) f = Add(*other_terms) o = Add(*order_terms) if n == 2: return expand_multinomial(f**n, deep=False) + n*f*o else: g = expand_multinomial(f**(n - 1), deep=False) return expand_mul(f*g, deep=False) + n*g*o 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 = self.func(a.q * b.q, n) a, b = a.p*b.q, a.q*b.p else: k = self.func(a.q, n) a, b = a.p, a.q*b elif not b.is_Integer: k = self.func(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 from sympy.polys.polyutils import basic_from_dict 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. return basic_from_dict(expansion_dict, *p) 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: # XXX can this ever happen if base was an Add? 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 / self.func(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 *= self.func(base, term) else: tail += term return coeff * self.func(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 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 = self.func(self.func(re, 2) + self.func(im, 2), S.Half) t = C.atan2(im, re) rp, tp = self.func(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.is_negative and base.is_number and base.is_real is False: base = base.conjugate() / (base * base.conjugate())._evalf(prec) exp = -exp return self.func(base, exp).expand() return self.func(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) is True 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 _eval_is_algebraic_expr(self, syms): if self.exp.has(*syms): return False if self.base.has(*syms): return self.base._eval_is_algebraic_expr(syms) and \ self.exp.is_Rational 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 if not neg_exp and not (-exp).is_negative: neg_exp = _coeff_isneg(exp) int_exp = exp.is_integer # 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 self.func(n, exp), self.func(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(self.func(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" nuse = n cf = 1 try: ord = b.as_leading_term(x) cf = C.Order(ord, x).getn() if cf and cf.is_Number: nuse = n + 2*ceiling(cf) else: cf = 1 except NotImplementedError: pass b_orig, prefactor = b, O(1, x) while prefactor.is_Order: nuse += 1 b = b_orig._eval_nseries(x, n=nuse, 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.is_Order: return 1/prefactor + rest/prefactor + O(x**n, x) k, l = rest.leadterm(x) if l.is_Rational and l > 0: pass elif l.is_number and l > 0: l = l.evalf() elif l == 0: k = k.simplify() if k == 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) else: raise NotImplementedError() else: raise NotImplementedError() if cf < 0: cf = S.One/abs(cf) try: dn = C.Order(1/prefactor, x).getn() if dn and dn < 0: pass else: dn = 0 except NotImplementedError: dn = 0 terms = [1/prefactor] for m in xrange(1, ceiling((n - dn)/l*cf)): 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) terms.append(O(x**n, x)) 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: nuse, denominator = n, O(1, x) while denominator.is_Order: denominator = (b**(-e))._eval_nseries(x, n=nuse, logx=logx) nuse += 1 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 if e.is_real and e.is_positive: lt = b.as_leading_term(x) # Try to correct nuse (= m) guess from: # (lt + rest + O(x**m))**e = # lt**e*(1 + rest/lt + O(x**m)/lt)**e = # lt**e + ... + O(x**m)*lt**(e - 1) = ... + O(x**n) try: cf = C.Order(lt, x).getn() nuse = ceiling(n - cf*(e - 1)) except NotImplementedError: pass 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 ((self.func(lt, e) * self.func((bs/lt).expand(), e).nseries( x, n=nuse, logx=logx)).expand() + order) if bs.is_Add: from sympy import O # So, bs + O() == terms c = Dummy('c') res = [] for arg in bs.args: if arg.is_Order: arg = c*arg.expr res.append(arg) bs = Add(*res) rv = (bs**e).series(x).subs(c, O(1, x)) rv += order return rv 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 xrange(n + 2): g = self._taylor_term(i, z, g) g = g.nseries(x, n=n, logx=logx) l.append(g) r = Add(*l) return expand_mul(r*b0**e) + order def _eval_as_leading_term(self, x): if not self.exp.has(x): return self.func(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 return C.binomial(self.exp, n) * self.func(x, n) def _sage_(self): return self.args[0]._sage_()**self.args[1]._sage_() 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(*_)) 3**(2*x + 2) >>> 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 = self.func(b, ceh) r = S.Zero if not c.is_Rational: iceh, r = divmod(ceh.p, ceh.q) c = self.func(b, iceh) return c, self.func(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 = self.func(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, self.func(_keep_coeff(m, t), e) return S.One, self.func(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 sympy-0.7.4.1/sympy/core/singleton.py0000644000175000017500000000431312253362407017732 0ustar georgeskgeorgesk"""Singleton mechanism""" from __future__ import print_function, division 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.. Examples ======== >>> from sympy import S, Basic >>> from sympy.core.singleton import Singleton >>> from sympy.core.compatibility import with_metaclass >>> class MySingleton(with_metaclass(Singleton, Basic)): ... pass >>> Basic() is Basic() False >>> MySingleton() is MySingleton() True >>> S.MySingleton is MySingleton() True ** Developer notes ** The class is instantiated 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__ sympy-0.7.4.1/sympy/core/tests/0000755000175000017500000000000012253362407016517 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/core/tests/test_assumptions.py0000644000175000017500000005023212253362407022517 0ustar georgeskgeorgeskfrom sympy.core import Symbol, S, Rational, Integer from sympy.utilities.pytest import raises, XFAIL from sympy import I, sqrt, log, exp, sin, asin from sympy.core.facts import InconsistentAssumptions def test_symbol_unset(): x = Symbol('x', real=True, integer=True) assert x.is_real is True assert x.is_integer is True assert x.is_imaginary is False assert x.is_noninteger is False assert x.is_number is False def test_zero(): z = Integer(0) assert z.is_commutative is True assert z.is_integer is True assert z.is_rational is True assert z.is_real is True assert z.is_complex is True assert z.is_noninteger is False assert z.is_irrational is False assert z.is_imaginary is False assert z.is_positive is False assert z.is_negative is False assert z.is_nonpositive is True assert z.is_nonnegative is True assert z.is_even is True assert z.is_odd is False assert z.is_bounded is True assert z.is_unbounded is False assert z.is_finite is False assert z.is_infinitesimal is True assert z.is_comparable is True assert z.is_prime is False assert z.is_composite is False assert z.is_number is True def test_one(): z = Integer(1) assert z.is_commutative is True assert z.is_integer is True assert z.is_rational is True assert z.is_real is True assert z.is_complex is True assert z.is_noninteger is False assert z.is_irrational is False assert z.is_imaginary is False assert z.is_positive is True assert z.is_negative is False assert z.is_nonpositive is False assert z.is_nonnegative is True assert z.is_even is False assert z.is_odd is True assert z.is_bounded is True assert z.is_unbounded is False assert z.is_finite is True assert z.is_infinitesimal is False assert z.is_comparable is True assert z.is_prime is False assert z.is_number is True @XFAIL def test_one_is_composite(): assert S(1).is_composite is False def test_negativeone(): z = Integer(-1) assert z.is_commutative is True assert z.is_integer is True assert z.is_rational is True assert z.is_real is True assert z.is_complex is True assert z.is_noninteger is False assert z.is_irrational is False assert z.is_imaginary is False assert z.is_positive is False assert z.is_negative is True assert z.is_nonpositive is True assert z.is_nonnegative is False assert z.is_even is False assert z.is_odd is True assert z.is_bounded is True assert z.is_unbounded is False assert z.is_finite is True assert z.is_infinitesimal is False assert z.is_comparable is True assert z.is_prime is False assert z.is_composite is False assert z.is_number is True def test_infinity(): oo = S.Infinity assert oo.is_commutative is True assert oo.is_integer is None assert oo.is_rational is None assert oo.is_real is True assert oo.is_complex is True assert oo.is_noninteger is None assert oo.is_irrational is None assert oo.is_imaginary is False assert oo.is_positive is True assert oo.is_negative is False assert oo.is_nonpositive is False assert oo.is_nonnegative is True assert oo.is_even is None assert oo.is_odd is None assert oo.is_bounded is False assert oo.is_unbounded is True assert oo.is_finite is False assert oo.is_infinitesimal is False assert oo.is_comparable is True assert oo.is_prime is None assert oo.is_composite is None assert oo.is_number is True def test_neg_infinity(): mm = S.NegativeInfinity assert mm.is_commutative is True assert mm.is_integer is None assert mm.is_rational is None assert mm.is_real is True assert mm.is_complex is True assert mm.is_noninteger is None assert mm.is_irrational is None assert mm.is_imaginary is False assert mm.is_positive is False assert mm.is_negative is True assert mm.is_nonpositive is True assert mm.is_nonnegative is False assert mm.is_even is None assert mm.is_odd is None assert mm.is_bounded is False assert mm.is_unbounded is True assert mm.is_finite is False assert mm.is_infinitesimal is False assert mm.is_comparable is True assert mm.is_prime is False assert mm.is_composite is False assert mm.is_number is True def test_nan(): nan = S.NaN assert nan.is_commutative is True assert nan.is_integer is None assert nan.is_rational is None assert nan.is_real is None assert nan.is_complex is None assert nan.is_noninteger is None assert nan.is_irrational is None assert nan.is_imaginary is None assert nan.is_positive is None assert nan.is_negative is None assert nan.is_nonpositive is None assert nan.is_nonnegative is None assert nan.is_even is None assert nan.is_odd is None assert nan.is_bounded is None assert nan.is_unbounded is None assert nan.is_finite is None assert nan.is_infinitesimal is None assert nan.is_comparable is False assert nan.is_prime is None assert nan.is_composite is None assert nan.is_number is True def test_pos_rational(): r = Rational(3, 4) assert r.is_commutative is True assert r.is_integer is False assert r.is_rational is True assert r.is_real is True assert r.is_complex is True assert r.is_noninteger is True assert r.is_irrational is False assert r.is_imaginary is False assert r.is_positive is True assert r.is_negative is False assert r.is_nonpositive is False assert r.is_nonnegative is True assert r.is_even is False assert r.is_odd is False assert r.is_bounded is True assert r.is_unbounded is False assert r.is_finite is True assert r.is_infinitesimal is False assert r.is_comparable is True assert r.is_prime is False assert r.is_composite is False r = Rational(1, 4) assert r.is_nonpositive is False assert r.is_positive is True assert r.is_negative is False assert r.is_nonnegative is True r = Rational(5, 4) assert r.is_negative is False assert r.is_positive is True assert r.is_nonpositive is False assert r.is_nonnegative is True r = Rational(5, 3) assert r.is_nonnegative is True assert r.is_positive is True assert r.is_negative is False assert r.is_nonpositive is False def test_neg_rational(): r = Rational(-3, 4) assert r.is_positive is False assert r.is_nonpositive is True assert r.is_negative is True assert r.is_nonnegative is False r = Rational(-1, 4) assert r.is_nonpositive is True assert r.is_positive is False assert r.is_negative is True assert r.is_nonnegative is False r = Rational(-5, 4) assert r.is_negative is True assert r.is_positive is False assert r.is_nonpositive is True assert r.is_nonnegative is False r = Rational(-5, 3) assert r.is_nonnegative is False assert r.is_positive is False assert r.is_negative is True assert r.is_nonpositive is True def test_pi(): z = S.Pi assert z.is_commutative is True assert z.is_integer is False assert z.is_rational is False assert z.is_real is True assert z.is_complex is True assert z.is_noninteger is True assert z.is_irrational is True assert z.is_imaginary is False assert z.is_positive is True assert z.is_negative is False assert z.is_nonpositive is False assert z.is_nonnegative is True assert z.is_even is False assert z.is_odd is False assert z.is_bounded is True assert z.is_unbounded is False assert z.is_finite is True assert z.is_infinitesimal is False assert z.is_comparable is True assert z.is_prime is False assert z.is_composite is False def test_E(): z = S.Exp1 assert z.is_commutative is True assert z.is_integer is False assert z.is_rational is False assert z.is_real is True assert z.is_complex is True assert z.is_noninteger is True assert z.is_irrational is True assert z.is_imaginary is False assert z.is_positive is True assert z.is_negative is False assert z.is_nonpositive is False assert z.is_nonnegative is True assert z.is_even is False assert z.is_odd is False assert z.is_bounded is True assert z.is_unbounded is False assert z.is_finite is True assert z.is_infinitesimal is False assert z.is_comparable is True assert z.is_prime is False assert z.is_composite is False def test_I(): z = S.ImaginaryUnit assert z.is_commutative is True assert z.is_integer is False assert z.is_rational is False assert z.is_real is False assert z.is_complex is True assert z.is_noninteger is False assert z.is_irrational is False assert z.is_imaginary is True assert z.is_positive is False assert z.is_negative is False assert z.is_nonpositive is False assert z.is_nonnegative is False assert z.is_even is False assert z.is_odd is False assert z.is_bounded is True assert z.is_unbounded is False assert z.is_finite is True assert z.is_infinitesimal is False assert z.is_comparable is False assert z.is_prime is False assert z.is_composite is False def test_symbol_real(): # issue 749 a = Symbol('a', real=False) assert a.is_real is False assert a.is_integer is False assert a.is_negative is False assert a.is_positive is False assert a.is_nonnegative is False assert a.is_nonpositive is False assert a.is_zero is False def test_symbol_zero(): x = Symbol('x', zero=True) assert x.is_positive is False assert x.is_nonpositive is True assert x.is_negative is False assert x.is_nonnegative is True assert x.is_zero is True assert x.is_nonzero is False def test_symbol_positive(): x = Symbol('x', positive=True) assert x.is_positive is True assert x.is_nonpositive is False assert x.is_negative is False assert x.is_nonnegative is True assert x.is_zero is False assert x.is_nonzero is True def test_neg_symbol_positive(): x = -Symbol('x', positive=True) assert x.is_positive is False assert x.is_nonpositive is True assert x.is_negative is True assert x.is_nonnegative is False assert x.is_zero is False assert x.is_nonzero is True def test_symbol_nonpositive(): x = Symbol('x', nonpositive=True) assert x.is_positive is False assert x.is_nonpositive is True assert x.is_negative is None assert x.is_nonnegative is None assert x.is_zero is None assert x.is_nonzero is None def test_neg_symbol_nonpositive(): x = -Symbol('x', nonpositive=True) assert x.is_positive is None assert x.is_nonpositive is None assert x.is_negative is False assert x.is_nonnegative is True assert x.is_zero is None assert x.is_nonzero is None def test_symbol_falsepositive(): x = Symbol('x', positive=False) assert x.is_positive is False assert x.is_nonpositive is None assert x.is_negative is None assert x.is_nonnegative is None assert x.is_zero is None assert x.is_nonzero is None @XFAIL def test_neg_symbol_falsepositive(): x = -Symbol('x', positive=False) assert x.is_positive is None assert x.is_nonpositive is None assert x.is_negative is False # this currently returns None assert x.is_nonnegative is None assert x.is_zero is None assert x.is_nonzero is None def test_symbol_falsepositive_real(): x = Symbol('x', positive=False, real=True) assert x.is_positive is False assert x.is_nonpositive is True assert x.is_negative is None assert x.is_nonnegative is None assert x.is_zero is None assert x.is_nonzero is None def test_neg_symbol_falsepositive_real(): x = -Symbol('x', positive=False, real=True) assert x.is_positive is None assert x.is_nonpositive is None assert x.is_negative is False assert x.is_nonnegative is True assert x.is_zero is None assert x.is_nonzero is None def test_symbol_falsenonnegative(): x = Symbol('x', nonnegative=False) assert x.is_positive is False assert x.is_nonpositive is None assert x.is_negative is None assert x.is_nonnegative is False assert x.is_zero is False assert x.is_nonzero is True @XFAIL def test_neg_symbol_falsenonnegative(): x = -Symbol('x', nonnegative=False) assert x.is_positive is None assert x.is_nonpositive is False # this currently returns None assert x.is_negative is False # this currently returns None assert x.is_nonnegative is None assert x.is_zero is False # this currently returns None assert x.is_nonzero is True # this currently returns None def test_symbol_falsenonnegative_real(): x = Symbol('x', nonnegative=False, real=True) assert x.is_positive is False assert x.is_nonpositive is True assert x.is_negative is True assert x.is_nonnegative is False assert x.is_zero is False assert x.is_nonzero is True def test_neg_symbol_falsenonnegative_real(): x = -Symbol('x', nonnegative=False, real=True) assert x.is_positive is True assert x.is_nonpositive is False assert x.is_negative is False assert x.is_nonnegative is True assert x.is_zero is False assert x.is_nonzero is True def test_prime(): assert S(-1).is_prime is False assert S(-2).is_prime is False assert S(-4).is_prime is False assert S(0).is_prime is False assert S(1).is_prime is False assert S(2).is_prime is True assert S(17).is_prime is True assert S(4).is_prime is False def test_composite(): assert S(-1).is_composite is False assert S(-2).is_composite is False assert S(-4).is_composite is False assert S(0).is_composite is False assert S(2).is_composite is False assert S(17).is_composite is False assert S(4).is_composite is True def test_prime_symbol(): x = Symbol('x', prime=True) assert x.is_prime is True assert x.is_integer is True assert x.is_positive is True assert x.is_negative is False assert x.is_nonpositive is False assert x.is_nonnegative is True x = Symbol('x', prime=False) assert x.is_prime is False assert x.is_integer is None assert x.is_positive is None assert x.is_negative is None assert x.is_nonpositive is None assert x.is_nonnegative is None def test_symbol_noncommutative(): x = Symbol('x', commutative=True) assert x.is_complex is None x = Symbol('x', commutative=False) assert x.is_integer is False assert x.is_rational is False assert x.is_irrational is False assert x.is_real is False assert x.is_complex is False def test_other_symbol(): x = Symbol('x', integer=True) assert x.is_integer is True assert x.is_real is True x = Symbol('x', integer=True, nonnegative=True) assert x.is_integer is True assert x.is_nonnegative is True assert x.is_negative is False assert x.is_positive is None x = Symbol('x', integer=True, nonpositive=True) assert x.is_integer is True assert x.is_nonpositive is True assert x.is_positive is False assert x.is_negative is None x = Symbol('x', odd=True) assert x.is_odd is True assert x.is_even is False assert x.is_integer is True x = Symbol('x', odd=False) assert x.is_odd is False assert x.is_even is None assert x.is_integer is None x = Symbol('x', even=True) assert x.is_even is True assert x.is_odd is False assert x.is_integer is True x = Symbol('x', even=False) assert x.is_even is False assert x.is_odd is None assert x.is_integer is None x = Symbol('x', integer=True, nonnegative=True) assert x.is_integer is True assert x.is_nonnegative is True x = Symbol('x', integer=True, nonpositive=True) assert x.is_integer is True assert x.is_nonpositive is True with raises(AttributeError): x.is_real = False def test_issue726(): """catch: hash instability""" x = Symbol("x") y = Symbol("y") a1 = x + y a2 = y + x a2.is_comparable h1 = hash(a1) h2 = hash(a2) assert h1 == h2 def test_issue1723(): z = (-1)**Rational(1, 3)*(1 - I*sqrt(3)) assert z.is_real in [True, None] def test_hash_vs_typeinfo(): """seemingly different typeinfo, but in fact equal""" # the following two are semantically equal x1 = Symbol('x', even=True) x2 = Symbol('x', integer=True, odd=False) assert hash(x1) == hash(x2) assert x1 == x2 def test_hash_vs_typeinfo_2(): """different typeinfo should mean !eq""" # the following two are semantically different x = Symbol('x') x1 = Symbol('x', even=True) assert x != x1 assert hash(x) != hash(x1) # This might fail with very low probability def test_hash_vs_eq(): """catch: different hash for equal objects""" a = 1 + S.Pi # important: do not fold it into a Number instance ha = hash(a) # it should be Add/Mul/... to trigger the bug a.is_positive # this uses .evalf() and deduces it is positive assert a.is_positive is True # be sure that hash stayed the same assert ha == hash(a) # now b should be the same expression b = a.expand(trig=True) hb = hash(b) assert a == b assert ha == hb def test_Add_is_pos_neg(): # these cover lines not covered by the rest of tests in core n = Symbol('n', negative=True, bounded=False) p = Symbol('p', positive=True, bounded=False) x = Symbol('x') assert (n + p).is_positive is None assert (n + x).is_positive is False assert (p + x).is_positive is True assert (n + p).is_negative is None assert (n + x).is_negative is True assert (p + x).is_negative is False def test_special_is_rational(): i = Symbol('i', integer=True) r = Symbol('r', rational=True) x = Symbol('x') assert sqrt(3).is_rational is False assert (3 + sqrt(3)).is_rational is False assert (3*sqrt(3)).is_rational is False assert exp(3).is_rational is False assert exp(i).is_rational is False assert exp(r).is_rational is False assert exp(x).is_rational is None assert exp(log(3), evaluate=False).is_rational is True assert log(exp(3), evaluate=False).is_rational is True assert log(3).is_rational is False assert log(i).is_rational is False assert log(r).is_rational is False assert log(x).is_rational is None assert (sqrt(3) + sqrt(5)).is_rational is None assert (sqrt(3) + S.Pi).is_rational is None assert (x**i).is_rational is None assert (i**i).is_rational is True assert (r**i).is_rational is True assert (r**r).is_rational is None assert (r**x).is_rational is None assert sin(1).is_rational is False assert sin(i).is_rational is False assert sin(r).is_rational is False assert sin(x).is_rational is None assert asin(r).is_rational is False assert sin(asin(3), evaluate=False).is_rational is True @XFAIL def test_issue_3176(): x = Symbol('x') # both zero or both Muls...but neither "change would be very appreciated. # This is similar to x/x => 1 even though if x = 0, it is really nan. assert isinstance(x*0, type(0*S.Infinity)) if 0*S.Infinity is S.NaN: b = Symbol('b', bounded=None) assert (b*0).is_zero is None def test_sanitize_assumptions(): # issue 3567 x = Symbol('x', real=1, positive=0) assert x.is_real is True assert x.is_positive is False def test_special_assumptions(): x = Symbol('x') z2 = z = Symbol('z', zero=True) assert z2 == z == S.Zero assert (2*z).is_positive is False assert (2*z).is_negative is False assert (2*z).is_zero is True assert (z2*z).is_positive is False assert (z2*z).is_negative is False assert (z2*z).is_zero is True e = -3 - sqrt(5) + (-sqrt(10)/2 - sqrt(2)/2)**2 assert (e < 0) is False assert (e > 0) is False assert (e == 0) is False # it's not a literal 0 assert e.equals(0) is True def test_inconsistent(): # cf. issues 2696 and 2446 raises(InconsistentAssumptions, lambda: Symbol('x', real=True, commutative=False)) def test_issue_3532(): assert ((-1)**(I)).is_real is True assert ((-1)**(I*2)).is_real is True assert ((-1)**(I/2)).is_real is True assert ((-1)**(I*S.Pi)).is_real is True assert (I**(I + 2)).is_real is True sympy-0.7.4.1/sympy/core/tests/test_logic.py0000644000175000017500000000723312253362407021232 0ustar georgeskgeorgeskfrom sympy.core.logic import fuzzy_not, Logic, And, Or, Not, fuzzy_and, fuzzy_or from sympy.utilities.pytest import raises T = True F = False U = None def test_fuzzy_not(): assert fuzzy_not(T) == F assert fuzzy_not(F) == T assert fuzzy_not(U) == U def test_fuzzy_and(): assert fuzzy_and([T, T]) == T assert fuzzy_and([T, F]) == F assert fuzzy_and([T, U]) == U assert fuzzy_and([F, F]) == F assert fuzzy_and([F, U]) == F assert fuzzy_and([U, U]) == U assert [fuzzy_and([w]) for w in [U, T, F]] == [U, T, F] assert fuzzy_and([T, F, U]) == F assert fuzzy_and([]) == T raises(TypeError, lambda: fuzzy_and()) def test_fuzzy_or(): assert fuzzy_or([T, T]) == T assert fuzzy_or([T, F]) == T assert fuzzy_or([T, U]) == T assert fuzzy_or([F, F]) == F assert fuzzy_or([F, U]) == U assert fuzzy_or([U, U]) == U assert [fuzzy_or([w]) for w in [U, T, F]] == [U, T, F] assert fuzzy_or([T, F, U]) == T assert fuzzy_or([]) == F raises(TypeError, lambda: fuzzy_or()) def test_logic_cmp(): l1 = And('a', Not('b')) l2 = And('a', Not('b')) assert hash(l1) == hash(l2) assert (l1 == l2) == T assert (l1 != l2) == F assert And('a', 'b', 'c') == And('b', 'a', 'c') assert And('a', 'b', 'c') == And('c', 'b', 'a') assert And('a', 'b', 'c') == And('c', 'a', 'b') def test_logic_onearg(): assert And() is True assert Or() is False assert And(T) == T assert And(F) == F assert Or(T) == T assert Or(F) == F assert And('a') == 'a' assert Or('a') == 'a' def test_logic_xnotx(): assert And('a', Not('a')) == F assert Or('a', Not('a')) == T def test_logic_eval_TF(): assert And(F, F) == F assert And(F, T) == F assert And(T, F) == F assert And(T, T) == T assert Or(F, F) == F assert Or(F, T) == T assert Or(T, F) == T assert Or(T, T) == T assert And('a', T) == 'a' assert And('a', F) == F assert Or('a', T) == T assert Or('a', F) == 'a' def test_logic_combine_args(): assert And('a', 'b', 'a') == And('a', 'b') assert Or('a', 'b', 'a') == Or('a', 'b') assert And( And('a', 'b'), And('c', 'd') ) == And('a', 'b', 'c', 'd') assert Or( Or('a', 'b'), Or('c', 'd') ) == Or('a', 'b', 'c', 'd') assert Or( 't', And('n', 'p', 'r'), And('n', 'r'), And('n', 'p', 'r'), 't', And('n', 'r') ) == \ Or('t', And('n', 'p', 'r'), And('n', 'r')) def test_logic_expand(): t = And(Or('a', 'b'), 'c') assert t.expand() == Or(And('a', 'c'), And('b', 'c')) t = And(Or('a', Not('b')), 'b') assert t.expand() == And('a', 'b') t = And(Or('a', 'b'), Or('c', 'd')) assert t.expand() == \ Or(And('a', 'c'), And('a', 'd'), And('b', 'c'), And('b', 'd')) def test_logic_fromstring(): S = Logic.fromstring assert S('a') == 'a' assert S('!a') == Not('a') assert S('a & b') == And('a', 'b') assert S('a | b') == Or('a', 'b') assert S('a | b & c') == And(Or('a', 'b'), 'c') assert S('a & b | c') == Or(And('a', 'b'), 'c') assert S('a & b & c') == And('a', 'b', 'c') assert S('a | b | c') == Or('a', 'b', 'c') raises(ValueError, lambda: S('| a')) raises(ValueError, lambda: S('& a')) raises(ValueError, lambda: S('a | | b')) raises(ValueError, lambda: S('a | & b')) raises(ValueError, lambda: S('a & & b')) raises(ValueError, lambda: S('a |')) def test_logic_not(): assert Not('a') != '!a' assert Not('!a') != 'a' # NOTE: we may want to change default Not behaviour and put this # functionality into some method. assert Not(And('a', 'b')) == Or(Not('a'), Not('b')) assert Not(Or('a', 'b')) == And(Not('a'), Not('b')) sympy-0.7.4.1/sympy/core/tests/test_eval.py0000644000175000017500000000410612253362407021060 0ustar georgeskgeorgeskfrom sympy import Symbol, Function, exp, sqrt, Rational, I, cos, tan from sympy.utilities.pytest import XFAIL def test_add_eval(): a = Symbol("a") b = Symbol("b") c = Rational(1) p = Rational(5) assert a*b + c + p == a*b + 6 assert c + a + p == a + 6 assert c + a - p == a + (-4) assert a + a == 2*a assert a + p + a == 2*a + 5 assert c + p == Rational(6) assert b + a - b == a def test_addmul_eval(): a = Symbol("a") b = Symbol("b") c = Rational(1) p = Rational(5) assert c + a + b*c + a - p == 2*a + b + (-4) assert a*2 + p + a == a*2 + 5 + a assert a*2 + p + a == 3*a + 5 assert a*2 + a == 3*a def test_pow_eval(): # XXX Pow does not fully support conversion of negative numbers # to their complex equivalent assert sqrt(-1) == I assert sqrt(-4) == 2*I assert sqrt( 4) == 2 assert (8)**Rational(1, 3) == 2 assert (-8)**Rational(1, 3) == 2*((-1)**Rational(1, 3)) assert sqrt(-2) == I*sqrt(2) assert (-1)**Rational(1, 3) != I assert (-10)**Rational(1, 3) != I*((10)**Rational(1, 3)) assert (-2)**Rational(1, 4) != (2)**Rational(1, 4) assert 64**Rational(1, 3) == 4 assert 64**Rational(2, 3) == 16 assert 24/sqrt(64) == 3 assert (-27)**Rational(1, 3) == 3*(-1)**Rational(1, 3) assert (cos(2) / tan(2))**2 == (cos(2) / tan(2))**2 @XFAIL def test_pow_eval_X1(): assert (-1)**Rational(1, 3) == Rational(1, 2) + Rational(1, 2)*I*sqrt(3) def test_mulpow_eval(): x = Symbol('x') assert sqrt(50)/(sqrt(2)*x) == 5/x assert sqrt(27)/sqrt(3) == 3 def test_evalpow_bug(): x = Symbol("x") assert 1/(1/x) == x assert 1/(-1/x) == -x def test_symbol_expand(): x = Symbol('x') y = Symbol('y') f = x**4*y**4 assert f == x**4*y**4 assert f == f.expand() g = (x*y)**4 assert g == f assert g.expand() == f assert g.expand() == g.expand().expand() def test_function(): f = Function('f') l, x = map(Symbol, 'lx') assert exp(l(x))*l(x)/exp(l(x)) == l(x) assert exp(f(x))*f(x)/exp(f(x)) == f(x) sympy-0.7.4.1/sympy/core/tests/test_equal.py0000644000175000017500000000310712253362407021240 0ustar georgeskgeorgeskfrom sympy import Symbol, Dummy, Rational, exp def test_equal(): b = Symbol("b") a = Symbol("a") e1 = a + b e2 = 2*a*b e3 = a**3*b**2 e4 = a*b + b*a assert not e1 == e2 assert not e1 == e2 assert e1 != e2 assert e2 == e4 assert e2 != e3 assert not e2 == e3 x = Symbol("x") e1 = exp(x + 1/x) y = Symbol("x") e2 = exp(y + 1/y) assert e1 == e2 assert not e1 != e2 y = Symbol("y") e2 = exp(y + 1/y) assert not e1 == e2 assert e1 != e2 e5 = Rational(3) + 2*x - x - x assert e5 == 3 assert 3 == e5 assert e5 != 4 assert 4 != e5 assert e5 != 3 + x assert 3 + x != e5 def test_expevalbug(): x = Symbol("x") e1 = exp(1*x) e3 = exp(x) assert e1 == e3 def test_cmp_bug1(): class T(object): pass t = T() x = Symbol("x") assert not (x == t) assert (x != t) def test_cmp_bug2(): class T(object): pass t = T() assert not (Symbol == t) assert (Symbol != t) def test_cmp_bug1258(): """ Check that Basic subclasses can be compared with sympifiable objects. http://code.google.com/p/sympy/issues/detail?id=1258 """ assert not (Symbol == 1) assert (Symbol != 1) assert not (Symbol == 'x') assert (Symbol != 'x') def test_dummy_eq(): x = Symbol('x') y = Symbol('y') u = Dummy('u') assert (u**2 + 1).dummy_eq(x**2 + 1) is True assert ((u**2 + 1) == (x**2 + 1)) is False assert (u**2 + y).dummy_eq(x**2 + y, x) is True assert (u**2 + y).dummy_eq(x**2 + y, y) is False sympy-0.7.4.1/sympy/core/tests/test_var.py0000644000175000017500000000304412253362407020721 0ustar georgeskgeorgesk# Tests for var are in their own file, because var pollutes global namespace. from sympy import Symbol, var, Function, FunctionClass from sympy.utilities.pytest import raises # make z1 with call-depth = 1 def _make_z1(): var("z1") # make z2 with call-depth = 2 def __make_z2(): var("z2") def _make_z2(): __make_z2() def test_var(): var("a") assert a == Symbol("a") var("b bb cc zz _x") assert b == Symbol("b") assert bb == Symbol("bb") assert cc == Symbol("cc") assert zz == Symbol("zz") assert _x == Symbol("_x") v = var(['d', 'e', 'fg']) assert d == Symbol('d') assert e == Symbol('e') assert fg == Symbol('fg') # check return value assert v == [d, e, fg] # see if var() really injects into global namespace raises(NameError, lambda: z1) _make_z1() assert z1 == Symbol("z1") raises(NameError, lambda: z2) _make_z2() assert z2 == Symbol("z2") def test_var_return(): raises(ValueError, lambda: var('')) v2 = var('q') v3 = var('q p') assert v2 == Symbol('q') assert v3 == (Symbol('q'), Symbol('p')) def test_var_accepts_comma(): v1 = var('x y z') v2 = var('x,y,z') v3 = var('x,y z') assert v1 == v2 assert v1 == v3 def test_var_keywords(): var('x y', real=True) assert x.is_real and y.is_real def test_var_cls(): f = var('f', cls=Function) assert isinstance(f, FunctionClass) g, h = var('g,h', cls=Function) assert isinstance(g, FunctionClass) assert isinstance(h, FunctionClass) sympy-0.7.4.1/sympy/core/tests/test_expand.py0000644000175000017500000002707312253362407021420 0ustar georgeskgeorgeskfrom sympy import (log, sqrt, Rational as R, Symbol, I, exp, pi, S, cos, sin, Mul, Pow, cse, O) from sympy.simplify.simplify import expand_numer, expand from sympy.core.function import ( expand_power_base, expand_multinomial) from sympy.utilities.pytest import raises from sympy.core.function import expand_power_base from sympy.utilities.randtest import test_numerically from sympy.abc import x, y, z def test_expand_no_log(): assert ( (1 + log(x**4))**2).expand(log=False) == 1 + 2*log(x**4) + log(x**4)**2 assert ((1 + log(x**4))*(1 + log(x**3))).expand( log=False) == 1 + log(x**4) + log(x**3) + log(x**4)*log(x**3) def test_expand_no_multinomial(): assert ((1 + x)*(1 + (1 + x)**4)).expand(multinomial=False) == \ 1 + x + (1 + x)**4 + x*(1 + x)**4 def test_expand_negative_integer_powers(): expr = (x + y)**(-2) assert expr.expand() == 1 / (2*x*y + x**2 + y**2) assert expr.expand(multinomial=False) == (x + y)**(-2) expr = (x + y)**(-3) assert expr.expand() == 1 / (3*x*x*y + 3*x*y*y + x**3 + y**3) assert expr.expand(multinomial=False) == (x + y)**(-3) expr = (x + y)**(2) * (x + y)**(-4) assert expr.expand() == 1 / (2*x*y + x**2 + y**2) assert expr.expand(multinomial=False) == (x + y)**(-2) def test_expand_non_commutative(): A = Symbol('A', commutative=False) B = Symbol('B', commutative=False) C = Symbol('C', commutative=False) a = Symbol('a') b = Symbol('b') i = Symbol('i', integer=True) n = Symbol('n', negative=True) m = Symbol('m', negative=True) p = Symbol('p', polar=True) np = Symbol('p', polar=False) assert (C*(A + B)).expand() == C*A + C*B assert (C*(A + B)).expand() != A*C + B*C assert ((A + B)**2).expand() == A**2 + A*B + B*A + B**2 assert ((A + B)**3).expand() == (A**2*B + B**2*A + A*B**2 + B*A**2 + A**3 + B**3 + A*B*A + B*A*B) # 3120 assert ((a*A*B*A**-1)**2).expand() == a**2*A*B**2/A # Note that (a*A*B*A**-1)**2 is automatically converted to a**2*(A*B*A**-1)**2 assert ((a*A*B*A**-1)**2).expand(deep=False) == a**2*(A*B*A**-1)**2 assert ((a*A*B*A**-1)**2).expand() == a**2*(A*B**2*A**-1) assert ((a*A*B*A**-1)**2).expand(force=True) == a**2*A*B**2*A**(-1) assert ((a*A*B)**2).expand() == a**2*A*B*A*B assert ((a*A)**2).expand() == a**2*A**2 assert ((a*A*B)**i).expand() == a**i*(A*B)**i assert ((a*A*(B*(A*B/A)**2))**i).expand() == a**i*(A*B*A*B**2/A)**i # 3459 assert (A*B*(A*B)**-1).expand() == A*B*(A*B)**-1 assert ((a*A)**i).expand() == a**i*A**i assert ((a*A*B*A**-1)**3).expand() == a**3*A*B**3/A assert ((a*A*B*A*B/A)**3).expand() == \ a**3*A*B*(A*B**2)*(A*B**2)*A*B*A**(-1) assert ((a*A*B*A*B/A)**-3).expand() == \ a**-3*(A*B*(A*B**2)*(A*B**2)*A*B*A**(-1))**-1 assert ((a*b*A*B*A**-1)**i).expand() == a**i*b**i*(A*B/A)**i assert ((a*(a*b)**i)**i).expand() == a**i*a**(i**2)*b**(i**2) e = Pow(Mul(a, 1/a, A, B, evaluate=False), S(2), evaluate=False) assert e.expand() == A*B*A*B assert sqrt(a*(A*b)**i).expand() == sqrt(a*b**i*A**i) assert (sqrt(-a)**a).expand() == sqrt(-a)**a assert expand((-2*n)**(i/3)) == 2**(i/3)*(-n)**(i/3) assert expand((-2*n*m)**(i/a)) == (-2)**(i/a)*(-n)**(i/a)*(-m)**(i/a) assert expand((-2*a*p)**b) == 2**b*p**b*(-a)**b assert expand((-2*a*np)**b) == 2**b*(-a*np)**b assert expand(sqrt(A*B)) == sqrt(A*B) assert expand(sqrt(-2*a*b)) == sqrt(2)*sqrt(-a*b) def test_expand_radicals(): a = (x + y)**R(1, 2) assert (a**1).expand() == a assert (a**3).expand() == x*a + y*a assert (a**5).expand() == x**2*a + 2*x*y*a + y**2*a assert (1/a**1).expand() == 1/a assert (1/a**3).expand() == 1/(x*a + y*a) assert (1/a**5).expand() == 1/(x**2*a + 2*x*y*a + y**2*a) a = (x + y)**R(1, 3) assert (a**1).expand() == a assert (a**2).expand() == a**2 assert (a**4).expand() == x*a + y*a assert (a**5).expand() == x*a**2 + y*a**2 assert (a**7).expand() == x**2*a + 2*x*y*a + y**2*a def test_expand_modulus(): assert ((x + y)**11).expand(modulus=11) == x**11 + y**11 assert ((x + sqrt(2)*y)**11).expand(modulus=11) == x**11 + 10*sqrt(2)*y**11 assert (x + y/2).expand(modulus=1) == y/2 raises(ValueError, lambda: ((x + y)**11).expand(modulus=0)) raises(ValueError, lambda: ((x + y)**11).expand(modulus=x)) def test_issue_2644(): assert (x*sqrt( x + y)*(1 + sqrt(x + y))).expand() == x**2 + x*y + x*sqrt(x + y) assert (x*sqrt( x + y)*(1 + x*sqrt(x + y))).expand() == x**3 + x**2*y + x*sqrt(x + y) def test_expand_frac(): assert expand((x + y)*y/x/(x + 1), frac=True) == \ (x*y + y**2)/(x**2 + x) assert expand((x + y)*y/x/(x + 1), numer=True) == \ (x*y + y**2)/(x*(x + 1)) assert expand((x + y)*y/x/(x + 1), denom=True) == \ y*(x + y)/(x**2 + x) eq = (x + 1)**2/y assert expand_numer(eq, multinomial=False) == eq def test_issue_3022(): eq = -I*exp(-3*I*pi/4)/(4*pi**(S(3)/2)*sqrt(x)) assert cse((eq).expand(complex=True), optimizations='basic') \ == S(''' ([(x0, re(x)), (x1, im(x)), (x2, x0**2 + x1**2), (x3, atan2(x1, x0)/2), (x4, sin(x3)), (x5, atan2(0, x2)/4), (x6, sin(x5)), (x7, x4*x6), (x8, cos(x3)), (x9, x6*x8), (x10, cos(x5)), (x11, x10*x4), (x12, x10*x8)], [sqrt(2)*(x11 + I*x11 - x12 + I*x12 + x7 - I*x7 + x9 + I*x9)/(8*pi**(3/2)*x2**(1/4))])''') def test_expand_power_base(): # was test_separate() assert expand_power_base((x*y*z)**4) == x**4*y**4*z**4 assert expand_power_base((x*y*z)**x).is_Pow assert expand_power_base((x*y*z)**x, force=True) == x**x*y**x*z**x assert expand_power_base((x*(y*z)**2)**3) == x**3*y**6*z**6 assert expand_power_base((sin((x*y)**2)*y)**z).is_Pow assert expand_power_base( (sin((x*y)**2)*y)**z, force=True) == sin((x*y)**2)**z*y**z assert expand_power_base( (sin((x*y)**2)*y)**z, deep=True) == (sin(x**2*y**2)*y)**z assert expand_power_base(exp(x)**2) == exp(2*x) assert expand_power_base((exp(x)*exp(y))**2) == exp(2*x)*exp(2*y) assert expand_power_base( (exp((x*y)**z)*exp(y))**2) == exp(2*(x*y)**z)*exp(2*y) assert expand_power_base((exp((x*y)**z)*exp( y))**2, deep=True, force=True) == exp(2*x**z*y**z)*exp(2*y) assert expand_power_base((exp(x)*exp(y))**z).is_Pow assert expand_power_base( (exp(x)*exp(y))**z, force=True) == exp(x)**z*exp(y)**z def test_expand_arit(): a = Symbol("a") b = Symbol("b", positive=True) c = Symbol("c") p = R(5) e = (a + b)*c assert e == c*(a + b) assert (e.expand() - a*c - b*c) == R(0) e = (a + b)*(a + b) assert e == (a + b)**2 assert e.expand() == 2*a*b + a**2 + b**2 e = (a + b)*(a + b)**R(2) assert e == (a + b)**3 assert e.expand() == 3*b*a**2 + 3*a*b**2 + a**3 + b**3 assert e.expand() == 3*b*a**2 + 3*a*b**2 + a**3 + b**3 e = (a + b)*(a + c)*(b + c) assert e == (a + c)*(a + b)*(b + c) assert e.expand() == 2*a*b*c + b*a**2 + c*a**2 + b*c**2 + a*c**2 + c*b**2 + a*b**2 e = (a + R(1))**p assert e == (1 + a)**5 assert e.expand() == 1 + 5*a + 10*a**2 + 10*a**3 + 5*a**4 + a**5 e = (a + b + c)*(a + c + p) assert e == (5 + a + c)*(a + b + c) assert e.expand() == 5*a + 5*b + 5*c + 2*a*c + b*c + a*b + a**2 + c**2 x = Symbol("x") s = exp(x*x) - 1 e = s.nseries(x, 0, 3)/x**2 assert e.expand() == 1 + x**2/2 + O(x**4) e = (x*(y + z))**(x*(y + z))*(x + y) assert e.expand(power_exp=False, power_base=False) == x*(x*y + x* z)**(x*y + x*z) + y*(x*y + x*z)**(x*y + x*z) assert e.expand(power_exp=False, power_base=False, deep=False) == x* \ (x*(y + z))**(x*(y + z)) + y*(x*(y + z))**(x*(y + z)) e = (x*(y + z))**z assert e.expand(power_base=True, mul=True, deep=True) in [x**z*(y + z)**z, (x*y + x*z)**z] assert ((2*y)**z).expand() == 2**z*y**z p = Symbol('p', positive=True) assert sqrt(-x).expand().is_Pow assert sqrt(-x).expand(force=True) == I*sqrt(x) assert ((2*y*p)**z).expand() == 2**z*p**z*y**z assert ((2*y*p*x)**z).expand() == 2**z*p**z*(x*y)**z assert ((2*y*p*x)**z).expand(force=True) == 2**z*p**z*x**z*y**z assert ((2*y*p*-pi)**z).expand() == 2**z*pi**z*p**z*(-y)**z assert ((2*y*p*-pi*x)**z).expand() == 2**z*pi**z*p**z*(-x*y)**z n = Symbol('n', negative=True) m = Symbol('m', negative=True) assert ((-2*x*y*n)**z).expand() == 2**z*(-n)**z*(x*y)**z assert ((-2*x*y*n*m)**z).expand() == 2**z*(-m)**z*(-n)**z*(-x*y)**z # issue 2383 assert sqrt(-2*x*n) == sqrt(2)*sqrt(-n)*sqrt(x) # issue 2506 (2) assert (cos(x + y)**2).expand(trig=True) in [ (-sin(x)*sin(y) + cos(x)*cos(y))**2, sin(x)**2*sin(y)**2 - 2*sin(x)*sin(y)*cos(x)*cos(y) + cos(x)**2*cos(y)**2 ] # Check that this isn't too slow x = Symbol('x') W = 1 for i in range(1, 21): W = W * (x - i) W = W.expand() assert W.has(-1672280820*x**15) def test_power_expand(): """Test for Pow.expand()""" a = Symbol('a') b = Symbol('b') p = (a + b)**2 assert p.expand() == a**2 + b**2 + 2*a*b p = (1 + 2*(1 + a))**2 assert p.expand() == 9 + 4*(a**2) + 12*a p = 2**(a + b) assert p.expand() == 2**a*2**b A = Symbol('A', commutative=False) B = Symbol('B', commutative=False) assert (2**(A + B)).expand() == 2**(A + B) assert (A**(a + b)).expand() != A**(a + b) def test_issues_2820_3731(): # 2820 n = -1 + 1/x z = n/x/(-n)**2 - 1/n/x assert expand(z) == 1/(x**2 - 2*x + 1) - 1/(x - 2 + 1/x) - 1/(-x + 1) # 3731 p = (1 + x)**2 assert expand_multinomial((1 + x*p)**2) == ( x**2*(x**4 + 4*x**3 + 6*x**2 + 4*x + 1) + 2*x*(x**2 + 2*x + 1) + 1) assert expand_multinomial((1 + (y + x)*p)**2) == ( 2*((x + y)*(x**2 + 2*x + 1)) + (x**2 + 2*x*y + y**2)* (x**4 + 4*x**3 + 6*x**2 + 4*x + 1) + 1) A = Symbol('A', commutative=False) p = (1 + A)**2 assert expand_multinomial((1 + x*p)**2) == ( x**2*(1 + 4*A + 6*A**2 + 4*A**3 + A**4) + 2*x*(1 + 2*A + A**2) + 1) assert expand_multinomial((1 + (y + x)*p)**2) == ( (x + y)*(1 + 2*A + A**2)*2 + (x**2 + 2*x*y + y**2)* (1 + 4*A + 6*A**2 + 4*A**3 + A**4) + 1) assert expand_multinomial((1 + (y + x)*p)**3) == ( (x + y)*(1 + 2*A + A**2)*3 + (x**2 + 2*x*y + y**2)*(1 + 4*A + 6*A**2 + 4*A**3 + A**4)*3 + (x**3 + 3*x**2*y + 3*x*y**2 + y**3)*(1 + 6*A + 15*A**2 + 20*A**3 + 15*A**4 + 6*A**5 + A**6) + 1) # unevaluate powers eq = (Pow((x + 1)*((A + 1)**2), 2, evaluate=False)) # - in this case the base is not an Add so no further # expansion is done assert expand_multinomial(eq) == \ (x**2 + 2*x + 1)*(1 + 4*A + 6*A**2 + 4*A**3 + A**4) # - but here, the expanded base *is* an Add so it gets expanded eq = (Pow(((A + 1)**2), 2, evaluate=False)) assert expand_multinomial(eq) == 1 + 4*A + 6*A**2 + 4*A**3 + A**4 # coverage def ok(a, b, n): e = (a + I*b)**n return test_numerically(e, expand_multinomial(e)) for a in [2, S.Half]: for b in [3, S(1)/3]: for n in range(2, 6): assert ok(a, b, n) assert expand_multinomial((x + 1 + O(z))**2) == \ 1 + 2*x + x**2 + O(z) assert expand_multinomial((x + 1 + O(z))**3) == \ 1 + 3*x + 3*x**2 + x**3 + O(z) assert expand_multinomial(3**(x + y + 3)) == 27*3**(x + y) def test_expand_log(): t = Symbol('t', positive=True) # after first expansion, -2*log(2) + log(4); then 0 after second assert expand(log(t**2) - log(t**2/4) - 2*log(2)) == 0 sympy-0.7.4.1/sympy/core/tests/test_noncommutative.py0000644000175000017500000000765212253362407023212 0ustar georgeskgeorgesk"""Tests for noncommutative symbols and expressions.""" from sympy import ( adjoint, cancel, collect, combsimp, conjugate, cos, expand, factor, posify, radsimp, ratsimp, rcollect, sin, simplify, symbols, transpose, trigsimp, I, ) from sympy.abc import x, y, z from sympy.utilities.pytest import XFAIL A, B, C = symbols("A B C", commutative=False) X = symbols("X", commutative=False, hermitian=True) Y = symbols("Y", commutative=False, antihermitian=True) def test_adjoint(): assert adjoint(A).is_commutative is False assert adjoint(A*A) == adjoint(A)**2 assert adjoint(A*B) == adjoint(B)*adjoint(A) assert adjoint(A*B**2) == adjoint(B)**2*adjoint(A) assert adjoint(A*B - B*A) == adjoint(B)*adjoint(A) - adjoint(A)*adjoint(B) assert adjoint(A + I*B) == adjoint(A) - I*adjoint(B) assert adjoint(X) == X assert adjoint(-I*X) == I*X assert adjoint(Y) == -Y assert adjoint(-I*Y) == -I*Y assert adjoint(X) == conjugate(transpose(X)) assert adjoint(Y) == conjugate(transpose(Y)) assert adjoint(X) == transpose(conjugate(X)) assert adjoint(Y) == transpose(conjugate(Y)) def test_cancel(): assert cancel(A*B - B*A) == A*B - B*A assert cancel(A*B*(x - 1)) == A*B*(x - 1) assert cancel(A*B*(x**2 - 1)/(x + 1)) == A*B*(x - 1) assert cancel(A*B*(x**2 - 1)/(x + 1) - B*A*(x - 1)) == A*B*(x - 1) + (1 - x)*B*A @XFAIL def test_collect(): assert collect(A*B - B*A, A) == A*B - B*A assert collect(A*B - B*A, B) == A*B - B*A assert collect(A*B - B*A, x) == A*B - B*A def test_combsimp(): assert combsimp(A*B - B*A) == A*B - B*A def test_conjugate(): assert conjugate(A).is_commutative is False assert (A*A).conjugate() == conjugate(A)**2 assert (A*B).conjugate() == conjugate(A)*conjugate(B) assert (A*B**2).conjugate() == conjugate(A)*conjugate(B)**2 assert (A*B - B*A).conjugate() == \ conjugate(A)*conjugate(B) - conjugate(B)*conjugate(A) assert (A*B).conjugate() - (B*A).conjugate() == \ conjugate(A)*conjugate(B) - conjugate(B)*conjugate(A) assert (A + I*B).conjugate() == conjugate(A) - I*conjugate(B) def test_expand(): assert expand((A*B)**2) == A*B*A*B assert expand(A*B - B*A) == A*B - B*A assert expand((A*B/A)**2) == A*B*B/A assert expand(B*A*(A + B)*B) == B*A**2*B + B*A*B**2 assert expand(B*A*(A + C)*B) == B*A**2*B + B*A*C*B def test_factor(): assert factor(A*B - B*A) == A*B - B*A def test_posify(): assert posify(A)[0].is_commutative is False for q in (A*B/A, (A*B/A)**2, (A*B)**2, A*B - B*A): p = posify(q) assert p[0].subs(p[1]) == q def test_radsimp(): assert radsimp(A*B - B*A) == A*B - B*A @XFAIL def test_ratsimp(): assert ratsimp(A*B - B*A) == A*B - B*A @XFAIL def test_rcollect(): assert rcollect(A*B - B*A, A) == A*B - B*A assert rcollect(A*B - B*A, B) == A*B - B*A assert rcollect(A*B - B*A, x) == A*B - B*A def test_simplify(): assert simplify(A*B - B*A) == A*B - B*A def test_subs(): assert (x*y*A).subs(x*y, z) == A*z assert (x*A*B).subs(x*A, C) == C*B assert (x*A*x*x).subs(x**2*A, C) == x*C assert (x*A*x*B).subs(x**2*A, C) == C*B assert (A**2*B**2).subs(A*B**2, C) == A*C assert (A*A*A + A*B*A).subs(A*A*A, C) == C + A*B*A def test_transpose(): assert transpose(A).is_commutative is False assert transpose(A*A) == transpose(A)**2 assert transpose(A*B) == transpose(B)*transpose(A) assert transpose(A*B**2) == transpose(B)**2*transpose(A) assert transpose(A*B - B*A) == \ transpose(B)*transpose(A) - transpose(A)*transpose(B) assert transpose(A + I*B) == transpose(A) + I*transpose(B) assert transpose(X) == conjugate(X) assert transpose(-I*X) == -I*conjugate(X) assert transpose(Y) == -conjugate(Y) assert transpose(-I*Y) == I*conjugate(Y) def test_trigsimp(): assert trigsimp(A*sin(x)**2 + A*cos(x)**2) == A sympy-0.7.4.1/sympy/core/tests/test_numbers.py0000644000175000017500000013036312253362407021611 0ustar georgeskgeorgeskimport decimal from sympy import (Rational, Symbol, Float, I, sqrt, oo, nan, pi, E, Integer, S, factorial, Catalan, EulerGamma, GoldenRatio, cos, exp, Number, zoo, log, Mul, Pow, Tuple, latex) from sympy.core.basic import _aresame from sympy.core.compatibility import long, u from sympy.core.power import integer_nthroot from sympy.core.numbers import igcd, ilcm, igcdex, seterr, _intcache, mpf_norm from sympy.mpmath import mpf from sympy.utilities.pytest import XFAIL, slow, raises from sympy import mpmath def test_integers_cache(): python_int = 2**65 + 3175259 while python_int in _intcache or hash(python_int) in _intcache: python_int += 1 sympy_int = Integer(python_int) assert python_int in _intcache assert hash(python_int) not in _intcache assert sympy_int not in _intcache sympy_int_int = Integer(sympy_int) assert python_int in _intcache assert hash(python_int) not in _intcache assert sympy_int_int not in _intcache sympy_hash_int = Integer(hash(python_int)) assert python_int in _intcache assert hash(python_int) in _intcache assert sympy_hash_int not in _intcache def test_seterr(): seterr(divide=True) raises(ValueError, lambda: S.Zero/S.Zero) seterr(divide=False) assert S.Zero / S.Zero == S.NaN def test_mod(): x = Rational(1, 2) y = Rational(3, 4) z = Rational(5, 18043) assert x % x == 0 assert x % y == 1/S(2) assert x % z == 3/S(36086) assert y % x == 1/S(4) assert y % y == 0 assert y % z == 9/S(72172) assert z % x == 5/S(18043) assert z % y == 5/S(18043) assert z % z == 0 a = Float(2.6) assert (a % .2) == 0 assert (a % 2).round(15) == 0.6 assert (a % 0.5).round(15) == 0.1 # In these two tests, if the precision of m does # not match the precision of the ans, then it is # likely that the change made now gives an answer # with degraded accuracy. r = Rational(500, 41) f = Float('.36', 3) m = r % f ans = Float(r % Rational(f), 3) assert m == ans and m._prec == ans._prec f = Float('8.36', 3) m = f % r ans = Float(Rational(f) % r, 3) assert m == ans and m._prec == ans._prec s = S.Zero assert s % float(1) == S.Zero # No rounding required since these numbers can be represented # exactly. assert Rational(3, 4) % Float(1.1) == 0.75 assert Float(1.5) % Rational(5, 4) == 0.25 assert Rational(5, 4).__rmod__(Float('1.5')) == 0.25 assert Float('1.5').__rmod__(Float('2.75')) == Float('1.25') assert 2.75 % Float('1.5') == Float('1.25') a = Integer(7) b = Integer(4) assert type(a % b) == Integer assert a % b == Integer(3) assert Integer(1) % Rational(2, 3) == Rational(1, 3) assert Rational(7, 5) % Integer(1) == Rational(2, 5) assert Integer(2) % 1.5 == 0.5 assert Integer(3).__rmod__(Integer(10)) == Integer(1) assert Integer(10) % 4 == Integer(2) assert 15 % Integer(4) == Integer(3) def test_divmod(): assert divmod(S(12), S(8)) == Tuple(1, 4) assert divmod(-S(12), S(8)) == Tuple(-2, 4) assert divmod(S(0), S(1)) == Tuple(0, 0) raises(ZeroDivisionError, lambda: divmod(S(0), S(0))) raises(ZeroDivisionError, lambda: divmod(S(1), S(0))) assert divmod(S(12), 8) == Tuple(1, 4) assert divmod(12, S(8)) == Tuple(1, 4) assert divmod(S("2"), S("3/2")) == Tuple(S("1"), S("1/2")) assert divmod(S("3/2"), S("2")) == Tuple(S("0"), S("3/2")) assert divmod(S("2"), S("3.5")) == Tuple(S("0"), S("2")) assert divmod(S("3.5"), S("2")) == Tuple(S("1"), S("1.5")) assert divmod(S("2"), S("1/3")) == Tuple(S("6"), S("0")) assert divmod(S("1/3"), S("2")) == Tuple(S("0"), S("1/3")) assert divmod(S("2"), S("0.1")) == Tuple(S("20"), S("0")) assert divmod(S("0.1"), S("2")) == Tuple(S("0"), S("0.1")) assert divmod(S("2"), 2) == Tuple(S("1"), S("0")) assert divmod(2, S("2")) == Tuple(S("1"), S("0")) assert divmod(S("2"), 1.5) == Tuple(S("1"), S("0.5")) assert divmod(1.5, S("2")) == Tuple(S("0"), S("1.5")) assert divmod(0.3, S("2")) == Tuple(S("0"), S("0.3")) assert divmod(S("3/2"), S("3.5")) == Tuple(S("0"), S("3/2")) assert divmod(S("3.5"), S("3/2")) == Tuple(S("2"), S("0.5")) assert divmod(S("3/2"), S("1/3")) == Tuple(S("4"), Float("1/6")) assert divmod(S("1/3"), S("3/2")) == Tuple(S("0"), S("1/3")) assert divmod(S("3/2"), S("0.1")) == Tuple(S("15"), S("0")) assert divmod(S("0.1"), S("3/2")) == Tuple(S("0"), S("0.1")) assert divmod(S("3/2"), 2) == Tuple(S("0"), S("3/2")) assert divmod(2, S("3/2")) == Tuple(S("1"), S("0.5")) assert divmod(S("3/2"), 1.5) == Tuple(S("1"), S("0")) assert divmod(1.5, S("3/2")) == Tuple(S("1"), S("0")) assert divmod(S("3/2"), 0.3) == Tuple(S("5"), S("0")) assert divmod(0.3, S("3/2")) == Tuple(S("0"), S("0.3")) assert divmod(S("1/3"), S("3.5")) == Tuple(S("0"), S("1/3")) assert divmod(S("3.5"), S("0.1")) == Tuple(S("35"), S("0")) assert divmod(S("0.1"), S("3.5")) == Tuple(S("0"), S("0.1")) assert divmod(S("3.5"), 2) == Tuple(S("1"), S("1.5")) assert divmod(2, S("3.5")) == Tuple(S("0"), S("2")) assert divmod(S("3.5"), 1.5) == Tuple(S("2"), S("0.5")) assert divmod(1.5, S("3.5")) == Tuple(S("0"), S("1.5")) assert divmod(0.3, S("3.5")) == Tuple(S("0"), S("0.3")) assert divmod(S("0.1"), S("1/3")) == Tuple(S("0"), S("0.1")) assert divmod(S("1/3"), 2) == Tuple(S("0"), S("1/3")) assert divmod(2, S("1/3")) == Tuple(S("6"), S("0")) assert divmod(S("1/3"), 1.5) == Tuple(S("0"), S("1/3")) assert divmod(0.3, S("1/3")) == Tuple(S("0"), S("0.3")) assert divmod(S("0.1"), 2) == Tuple(S("0"), S("0.1")) assert divmod(2, S("0.1")) == Tuple(S("20"), S("0")) assert divmod(S("0.1"), 1.5) == Tuple(S("0"), S("0.1")) assert divmod(1.5, S("0.1")) == Tuple(S("15"), S("0")) assert divmod(S("0.1"), 0.3) == Tuple(S("0"), S("0.1")) assert str(divmod(S("2"), 0.3)) == '(6, 0.2)' assert str(divmod(S("3.5"), S("1/3"))) == '(10, 0.166666666666667)' assert str(divmod(S("3.5"), 0.3)) == '(11, 0.2)' assert str(divmod(S("1/3"), S("0.1"))) == '(3, 0.0333333333333333)' assert str(divmod(1.5, S("1/3"))) == '(4, 0.166666666666667)' assert str(divmod(S("1/3"), 0.3)) == '(1, 0.0333333333333333)' assert str(divmod(0.3, S("0.1"))) == '(2, 0.1)' assert divmod(-3, S(2)) == (-2, 1) assert divmod(S(-3), S(2)) == (-2, 1) assert divmod(S(-3), 2) == (-2, 1) def test_igcd(): assert igcd(0, 0) == 0 assert igcd(0, 1) == 1 assert igcd(1, 0) == 1 assert igcd(0, 7) == 7 assert igcd(7, 0) == 7 assert igcd(7, 1) == 1 assert igcd(1, 7) == 1 assert igcd(-1, 0) == 1 assert igcd(0, -1) == 1 assert igcd(-1, -1) == 1 assert igcd(-1, 7) == 1 assert igcd(7, -1) == 1 assert igcd(8, 2) == 2 assert igcd(4, 8) == 4 assert igcd(8, 16) == 8 assert igcd(7, -3) == 1 assert igcd(-7, 3) == 1 assert igcd(-7, -3) == 1 raises(ValueError, lambda: igcd(45.1, 30)) raises(ValueError, lambda: igcd(45, 30.1)) def test_ilcm(): assert ilcm(0, 0) == 0 assert ilcm(1, 0) == 0 assert ilcm(0, 1) == 0 assert ilcm(1, 1) == 1 assert ilcm(2, 1) == 2 assert ilcm(8, 2) == 8 assert ilcm(8, 6) == 24 assert ilcm(8, 7) == 56 raises(ValueError, lambda: ilcm(8.1, 7)) raises(ValueError, lambda: ilcm(8, 7.1)) def test_igcdex(): assert igcdex(2, 3) == (-1, 1, 1) assert igcdex(10, 12) == (-1, 1, 2) assert igcdex(100, 2004) == (-20, 1, 4) def _strictly_equal(a, b): return (a.p, a.q, type(a.p), type(a.q)) == \ (b.p, b.q, type(b.p), type(b.q)) def _test_rational_new(cls): """ Tests that are common between Integer and Rational. """ assert cls(0) is S.Zero assert cls(1) is S.One assert cls(-1) is S.NegativeOne # These look odd, but are similar to int(): assert cls('1') is S.One assert cls(u('-1')) is S.NegativeOne i = Integer(10) assert _strictly_equal(i, cls('10')) assert _strictly_equal(i, cls(u('10'))) assert _strictly_equal(i, cls(long(10))) assert _strictly_equal(i, cls(i)) raises(TypeError, lambda: cls(Symbol('x'))) def test_Integer_new(): """ Test for Integer constructor """ _test_rational_new(Integer) assert _strictly_equal(Integer(0.9), S.Zero) assert _strictly_equal(Integer(10.5), Integer(10)) raises(ValueError, lambda: Integer("10.5")) assert Integer(Rational('1.' + '9'*20)) == 1 def test_Rational_new(): """" Test for Rational constructor """ _test_rational_new(Rational) n1 = Rational(1, 2) assert n1 == Rational(Integer(1), 2) assert n1 == Rational(Integer(1), Integer(2)) assert n1 == Rational(1, Integer(2)) assert n1 == Rational(Rational(1, 2)) assert 1 == Rational(n1, n1) assert Rational(3, 2) == Rational(Rational(1, 2), Rational(1, 3)) assert Rational(3, 1) == Rational(1, Rational(1, 3)) n3_4 = Rational(3, 4) assert Rational('3/4') == n3_4 assert -Rational('-3/4') == n3_4 assert Rational('.76').limit_denominator(4) == n3_4 assert Rational(19, 25).limit_denominator(4) == n3_4 assert Rational('19/25').limit_denominator(4) == n3_4 assert Rational(1.0, 3) == Rational(1, 3) assert Rational(1, 3.0) == Rational(1, 3) assert Rational(Float(0.5)) == Rational(1, 2) assert Rational('1e2/1e-2') == Rational(10000) assert Rational(-1, 0) == S.NegativeInfinity assert Rational(1, 0) == S.Infinity raises(TypeError, lambda: Rational('3**3')) raises(TypeError, lambda: Rational('1/2 + 2/3')) # handle fractions.Fraction instances try: import fractions assert Rational(fractions.Fraction(1, 2)) == Rational(1, 2) except ImportError: pass def test_Number_new(): """" Test for Number constructor """ # Expected behavior on numbers and strings assert Number(1) is S.One assert Number(2).__class__ is Integer assert Number(-622).__class__ is Integer assert Number(5, 3).__class__ is Rational assert Number(5.3).__class__ is Float assert Number('1') is S.One assert Number('2').__class__ is Integer assert Number('-622').__class__ is Integer assert Number('5/3').__class__ is Rational assert Number('5.3').__class__ is Float raises(ValueError, lambda: Number('cos')) raises(TypeError, lambda: Number(cos)) a = Rational(3, 5) assert Number(a) is a # Check idempotence on Numbers def test_Rational_cmp(): n1 = Rational(1, 4) n2 = Rational(1, 3) n3 = Rational(2, 4) n4 = Rational(2, -4) n5 = Rational(0) n6 = Rational(1) n7 = Rational(3) n8 = Rational(-3) assert n8 < n5 assert n5 < n6 assert n6 < n7 assert n8 < n7 assert n7 > n8 assert (n1 + 1)**n2 < 2 assert ((n1 + n6)/n7) < 1 assert n4 < n3 assert n2 < n3 assert n1 < n2 assert n3 > n1 assert not n3 < n1 assert not (Rational(-1) > 0) assert Rational(-1) < 0 def test_Float(): def eq(a, b): t = Float("1.0E-15") return (-t < a - b < t) a = Float(2) ** Float(3) assert eq(a.evalf(), Float(8)) assert eq((pi ** -1).evalf(), Float("0.31830988618379067")) a = Float(2) ** Float(4) assert eq(a.evalf(), Float(16)) assert (S(.3) == S(.5)) is False x_str = Float((0, '13333333333333', -52, 53)) x2_str = Float((0, '26666666666666', -53, 53)) x_hex = Float((0, long(0x13333333333333), -52, 53)) x_dec = Float((0, 5404319552844595, -52, 53)) x2_hex = Float((0, long(0x13333333333333)*2, -53, 53)) assert x_str == x_hex == x_dec == x2_hex == Float(1.2) # x2_str and 1.2 are superficially the same assert str(x2_str) == str(Float(1.2)) # but are different at the mpf level assert Float(1.2)._mpf_ == (0, long(5404319552844595), -52, 53) assert x2_str._mpf_ == (0, long(10808639105689190), -53, 53) assert Float((0, long(0), -123, -1)) == Float('nan') assert Float((0, long(0), -456, -2)) == Float('inf') == Float('+inf') assert Float((1, long(0), -789, -3)) == Float('-inf') raises(ValueError, lambda: Float((0, 7, 1, 3), '')) assert Float('+inf').is_bounded is False assert Float('+inf').is_finite is False assert Float('+inf').is_negative is False assert Float('+inf').is_positive is True assert Float('+inf').is_unbounded is True assert Float('+inf').is_zero is False assert Float('-inf').is_bounded is False assert Float('-inf').is_finite is False assert Float('-inf').is_negative is True assert Float('-inf').is_positive is False assert Float('-inf').is_unbounded is True assert Float('-inf').is_zero is False assert Float('0.0').is_bounded is True assert Float('0.0').is_finite is False assert Float('0.0').is_negative is False assert Float('0.0').is_positive is False assert Float('0.0').is_unbounded is False assert Float('0.0').is_zero is True # do not automatically evalf def teq(a): assert (a.evalf() == a) is False assert (a.evalf() != a) is True assert (a == a.evalf()) is False assert (a != a.evalf()) is True teq(pi) teq(2*pi) teq(cos(0.1, evaluate=False)) i = 12345678901234567890 assert _aresame(Float(12, ''), Float('12', '')) assert _aresame(Float(Integer(i), ''), Float(i, '')) assert _aresame(Float(i, ''), Float(str(i), 20)) assert not _aresame(Float(str(i)), Float(i, '')) # inexact floats (repeating binary = denom not multiple of 2) # cannot have precision greater than 15 assert Float(.125, 22) == .125 assert Float(2.0, 22) == 2 assert float(Float('.12500000000000001', '')) == .125 raises(ValueError, lambda: Float(.12500000000000001, '')) # allow spaces Float('123 456.123 456') == Float('123456.123456') Integer('123 456') == Integer('123456') Rational('123 456.123 456') == Rational('123456.123456') assert Float(' .3e2') == Float('0.3e2') # allow auto precision detection assert Float('.1', '') == Float(.1, 1) assert Float('.125', '') == Float(.125, 3) assert Float('.100', '') == Float(.1, 3) assert Float('2.0', '') == Float('2', 2) raises(ValueError, lambda: Float("12.3d-4", "")) raises(ValueError, lambda: Float(12.3, "")) raises(ValueError, lambda: Float('.')) raises(ValueError, lambda: Float('-.')) zero = Float('0.0') assert Float('-0') == zero assert Float('.0') == zero assert Float('-.0') == zero assert Float('-0.0') == zero assert Float(0.0) == zero assert Float(0) == zero assert Float(0, '') == Float('0', '') assert Float(1) == Float(1.0) assert Float(S.Zero) == zero assert Float(S.One) == Float(1.0) assert Float(decimal.Decimal('0.1'), 3) == Float('.1', 3) def test_Float_eval(): a = Float(3.2) assert (a**2).is_Float def test_Float_issue_2107(): a = Float(0.1, 10) b = Float("0.1", 10) assert a - a == 0 assert a + (-a) == 0 assert S.Zero + a - a == 0 assert S.Zero + a + (-a) == 0 assert b - b == 0 assert b + (-b) == 0 assert S.Zero + b - b == 0 assert S.Zero + b + (-b) == 0 def test_Infinity(): assert oo != 1 assert 1*oo == oo assert 1 != oo assert oo != -oo assert oo != Symbol("x")**3 assert oo + 1 == oo assert 2 + oo == oo assert 3*oo + 2 == oo assert S.Half**oo == 0 assert S.Half**(-oo) == oo assert -oo*3 == -oo assert oo + oo == oo assert -oo + oo*(-5) == -oo assert 1/oo == 0 assert 1/(-oo) == 0 assert 8/oo == 0 assert oo % 2 == nan assert 2 % oo == nan assert oo/oo == nan assert oo/-oo == nan assert -oo/oo == nan assert -oo/-oo == nan assert oo - oo == nan assert oo - -oo == oo assert -oo - oo == -oo assert -oo - -oo == nan assert oo + -oo == nan assert -oo + oo == nan assert oo + oo == oo assert -oo + oo == nan assert oo + -oo == nan assert -oo + -oo == -oo assert oo*oo == oo assert -oo*oo == -oo assert oo*-oo == -oo assert -oo*-oo == oo assert oo/0 == oo assert -oo/0 == -oo assert 0/oo == 0 assert 0/-oo == 0 assert oo*0 == nan assert -oo*0 == nan assert 0*oo == nan assert 0*-oo == nan assert oo + 0 == oo assert -oo + 0 == -oo assert 0 + oo == oo assert 0 + -oo == -oo assert oo - 0 == oo assert -oo - 0 == -oo assert 0 - oo == -oo assert 0 - -oo == oo assert oo/2 == oo assert -oo/2 == -oo assert oo/-2 == -oo assert -oo/-2 == oo assert oo*2 == oo assert -oo*2 == -oo assert oo*-2 == -oo assert 2/oo == 0 assert 2/-oo == 0 assert -2/oo == 0 assert -2/-oo == 0 assert 2*oo == oo assert 2*-oo == -oo assert -2*oo == -oo assert -2*-oo == oo assert 2 + oo == oo assert 2 - oo == -oo assert -2 + oo == oo assert -2 - oo == -oo assert 2 + -oo == -oo assert 2 - -oo == oo assert -2 + -oo == -oo assert -2 - -oo == oo assert S(2) + oo == oo assert S(2) - oo == -oo assert oo/I == -oo*I assert -oo/I == oo*I assert oo*float(1) == Float('inf') and (oo*float(1)).is_Float assert -oo*float(1) == Float('-inf') and (-oo*float(1)).is_Float assert oo/float(1) == Float('inf') and (oo/float(1)).is_Float assert -oo/float(1) == Float('-inf') and (-oo/float(1)).is_Float assert oo*float(-1) == Float('-inf') and (oo*float(-1)).is_Float assert -oo*float(-1) == Float('inf') and (-oo*float(-1)).is_Float assert oo/float(-1) == Float('-inf') and (oo/float(-1)).is_Float assert -oo/float(-1) == Float('inf') and (-oo/float(-1)).is_Float assert oo + float(1) == Float('inf') and (oo + float(1)).is_Float assert -oo + float(1) == Float('-inf') and (-oo + float(1)).is_Float assert oo - float(1) == Float('inf') and (oo - float(1)).is_Float assert -oo - float(1) == Float('-inf') and (-oo - float(1)).is_Float assert float(1)*oo == Float('inf') and (float(1)*oo).is_Float assert float(1)*-oo == Float('-inf') and (float(1)*-oo).is_Float assert float(1)/oo == 0 assert float(1)/-oo == 0 assert float(-1)*oo == Float('-inf') and (float(-1)*oo).is_Float assert float(-1)*-oo == Float('inf') and (float(-1)*-oo).is_Float assert float(-1)/oo == 0 assert float(-1)/-oo == 0 assert float(1) + oo == Float('inf') assert float(1) + -oo == Float('-inf') assert float(1) - oo == Float('-inf') assert float(1) - -oo == Float('inf') assert Float('nan') == nan assert nan*1.0 == nan assert -1.0*nan == nan assert nan*oo == nan assert nan*-oo == nan assert nan/oo == nan assert nan/-oo == nan assert nan + oo == nan assert nan + -oo == nan assert nan - oo == nan assert nan - -oo == nan assert -oo * S.Zero == nan assert oo*nan == nan assert -oo*nan == nan assert oo/nan == nan assert -oo/nan == nan assert oo + nan == nan assert -oo + nan == nan assert oo - nan == nan assert -oo - nan == nan assert S.Zero * oo == nan assert oo.is_Rational is False assert isinstance(oo, Rational) is False assert S.One/oo == 0 assert -S.One/oo == 0 assert S.One/-oo == 0 assert -S.One/-oo == 0 assert S.One*oo == oo assert -S.One*oo == -oo assert S.One*-oo == -oo assert -S.One*-oo == oo assert S.One/nan == nan assert S.One - -oo == oo assert S.One + nan == nan assert S.One - nan == nan assert nan - S.One == nan assert nan/S.One == nan assert -oo - S.One == -oo def test_Infinity_2(): x = Symbol('x') assert oo*x != oo assert oo*(pi - 1) == oo assert oo*(1 - pi) == -oo assert (-oo)*x != -oo assert (-oo)*(pi - 1) == -oo assert (-oo)*(1 - pi) == oo assert (-1)**S.NaN is S.NaN assert oo - Float('inf') is S.NaN assert oo + Float('-inf') is S.NaN assert oo*0 is S.NaN assert oo/Float('inf') is S.NaN assert oo/Float('-inf') is S.NaN assert oo**S.NaN is S.NaN assert -oo + Float('inf') is S.NaN assert -oo - Float('-inf') is S.NaN assert -oo*S.NaN is S.NaN assert -oo*0 is S.NaN assert -oo/Float('inf') is S.NaN assert -oo/Float('-inf') is S.NaN assert -oo/S.NaN is S.NaN assert abs(-oo) == oo assert all((-oo)**i is S.NaN for i in (oo, -oo, S.NaN)) assert (-oo)**3 == -oo assert (-oo)**2 == oo assert abs(S.ComplexInfinity) == oo def test_Mul_Infinity_Zero(): assert 0*Float('inf') == nan assert 0*Float('-inf') == nan assert 0*Float('inf') == nan assert 0*Float('-inf') == nan assert Float('inf')*0 == nan assert Float('-inf')*0 == nan assert Float('inf')*0 == nan assert Float('-inf')*0 == nan assert Float(0)*Float('inf') == nan assert Float(0)*Float('-inf') == nan assert Float(0)*Float('inf') == nan assert Float(0)*Float('-inf') == nan assert Float('inf')*Float(0) == nan assert Float('-inf')*Float(0) == nan assert Float('inf')*Float(0) == nan assert Float('-inf')*Float(0) == nan def test_Div_By_Zero(): assert 1/S(0) == oo assert 1/Float(0) == Float('inf') assert 0/S(0) == nan assert 0/Float(0) == nan assert S(0)/0 == nan assert Float(0)/0 == nan assert -1/S(0) == -oo assert -1/Float(0) == Float('-inf') def test_Infinity_inequations(): assert oo > pi assert not (oo < pi) assert exp(-3) < oo assert Float('+inf') > pi assert not (Float('+inf') < pi) assert exp(-3) < Float('+inf') def test_NaN(): assert nan == nan assert nan != 1 assert 1*nan == nan assert 1 != nan assert nan == -nan assert oo != Symbol("x")**3 assert nan + 1 == nan assert 2 + nan == nan assert 3*nan + 2 == nan assert -nan*3 == nan assert nan + nan == nan assert -nan + nan*(-5) == nan assert 1/nan == nan assert 1/(-nan) == nan assert 8/nan == nan assert not nan > 0 assert not nan < 0 assert not nan >= 0 assert not nan <= 0 assert not 0 < nan assert not 0 > nan assert not 0 <= nan assert not 0 >= nan assert S.One + nan == nan assert S.One - nan == nan assert S.One*nan == nan assert S.One/nan == nan assert nan - S.One == nan assert nan*S.One == nan assert nan + S.One == nan assert nan/S.One == nan assert nan**0 == 1 # as per IEEE 754 assert 1**nan == 1 # as per IEEE 754 def test_special_numbers(): assert isinstance(S.NaN, Number) is True assert isinstance(S.Infinity, Number) is True assert isinstance(S.NegativeInfinity, Number) is True assert S.NaN.is_number is True assert S.Infinity.is_number is True assert S.NegativeInfinity.is_number is True assert isinstance(S.NaN, Rational) is False assert isinstance(S.Infinity, Rational) is False assert isinstance(S.NegativeInfinity, Rational) is False assert S.NaN.is_rational is not True assert S.Infinity.is_rational is not True assert S.NegativeInfinity.is_rational is not True def test_powers(): assert integer_nthroot(1, 2) == (1, True) assert integer_nthroot(1, 5) == (1, True) assert integer_nthroot(2, 1) == (2, True) assert integer_nthroot(2, 2) == (1, False) assert integer_nthroot(2, 5) == (1, False) assert integer_nthroot(4, 2) == (2, True) assert integer_nthroot(123**25, 25) == (123, True) assert integer_nthroot(123**25 + 1, 25) == (123, False) assert integer_nthroot(123**25 - 1, 25) == (122, False) assert integer_nthroot(1, 1) == (1, True) assert integer_nthroot(0, 1) == (0, True) assert integer_nthroot(0, 3) == (0, True) assert integer_nthroot(10000, 1) == (10000, True) assert integer_nthroot(4, 2) == (2, True) assert integer_nthroot(16, 2) == (4, True) assert integer_nthroot(26, 2) == (5, False) assert integer_nthroot(1234567**7, 7) == (1234567, True) assert integer_nthroot(1234567**7 + 1, 7) == (1234567, False) assert integer_nthroot(1234567**7 - 1, 7) == (1234566, False) b = 25**1000 assert integer_nthroot(b, 1000) == (25, True) assert integer_nthroot(b + 1, 1000) == (25, False) assert integer_nthroot(b - 1, 1000) == (24, False) c = 10**400 c2 = c**2 assert integer_nthroot(c2, 2) == (c, True) assert integer_nthroot(c2 + 1, 2) == (c, False) assert integer_nthroot(c2 - 1, 2) == (c - 1, False) assert integer_nthroot(2, 10**10) == (1, False) p, r = integer_nthroot(int(factorial(10000)), 100) assert p % (10**10) == 5322420655 assert not r # Test that this is fast assert integer_nthroot(2, 10**10) == (1, False) def test_integer_nthroot_overflow(): assert integer_nthroot(10**(50*50), 50) == (10**50, True) assert integer_nthroot(10**100000, 10000) == (10**10, True) def test_powers_Integer(): """Test Integer._eval_power""" # check infinity assert S(1) ** S.Infinity == 1 assert S(-1)** S.Infinity == S.NaN assert S(2) ** S.Infinity == S.Infinity assert S(-2)** S.Infinity == S.Infinity + S.Infinity * S.ImaginaryUnit assert S(0) ** S.Infinity == 0 # check Nan assert S(1) ** S.NaN == S.One assert S(-1) ** S.NaN == S.NaN # check for exact roots assert S(-1) ** Rational(6, 5) == - (-1)**(S(1)/5) assert sqrt(S(4)) == 2 assert sqrt(S(-4)) == I * 2 assert S(16) ** Rational(1, 4) == 2 assert S(-16) ** Rational(1, 4) == 2 * (-1)**Rational(1, 4) assert S(9) ** Rational(3, 2) == 27 assert S(-9) ** Rational(3, 2) == -27*I assert S(27) ** Rational(2, 3) == 9 assert S(-27) ** Rational(2, 3) == 9 * (S(-1) ** Rational(2, 3)) assert (-2) ** Rational(-2, 1) == Rational(1, 4) # not exact roots assert sqrt(-3) == I*sqrt(3) assert (3) ** (S(3)/2) == 3 * sqrt(3) assert (-3) ** (S(3)/2) == - 3 * sqrt(-3) assert (-3) ** (S(5)/2) == 9 * I * sqrt(3) assert (-3) ** (S(7)/2) == - I * 27 * sqrt(3) assert (2) ** (S(3)/2) == 2 * sqrt(2) assert (2) ** (S(-3)/2) == sqrt(2) / 4 assert (81) ** (S(2)/3) == 9 * (S(3) ** (S(2)/3)) assert (-81) ** (S(2)/3) == 9 * (S(-3) ** (S(2)/3)) assert (-3) ** Rational(-7, 3) == \ -(-1)**Rational(2, 3)*3**Rational(2, 3)/27 assert (-3) ** Rational(-2, 3) == \ -(-1)**Rational(1, 3)*3**Rational(1, 3)/3 # join roots assert sqrt(6) + sqrt(24) == 3*sqrt(6) assert sqrt(2) * sqrt(3) == sqrt(6) # separate symbols & constansts x = Symbol("x") assert sqrt(49 * x) == 7 * sqrt(x) assert sqrt((3 - sqrt(pi)) ** 2) == 3 - sqrt(pi) # check that it is fast for big numbers assert (2**64 + 1) ** Rational(4, 3) assert (2**64 + 1) ** Rational(17, 25) # negative rational power and negative base assert (-3) ** Rational(-7, 3) == \ -(-1)**Rational(2, 3)*3**Rational(2, 3)/27 assert (-3) ** Rational(-2, 3) == \ -(-1)**Rational(1, 3)*3**Rational(1, 3)/3 assert S(1234).factors() == {617: 1, 2: 1} assert Rational(2*3, 3*5*7).factors() == {2: 1, 5: -1, 7: -1} # test that eval_power factors numbers bigger than # the current limit in factor_trial_division (2**15) from sympy import nextprime n = nextprime(2**15) assert sqrt(n**2) == n assert sqrt(n**3) == n*sqrt(n) assert sqrt(4*n) == 2*sqrt(n) # check that factors of base with powers sharing gcd with power are removed assert (2**4*3)**Rational(1, 6) == 2**Rational(2, 3)*3**Rational(1, 6) assert (2**4*3)**Rational(5, 6) == 8*2**Rational(1, 3)*3**Rational(5, 6) # check that bases sharing a gcd are exptracted assert 2**Rational(1, 3)*3**Rational(1, 4)*6**Rational(1, 5) == \ 2**Rational(8, 15)*3**Rational(9, 20) assert sqrt(8)*24**Rational(1, 3)*6**Rational(1, 5) == \ 4*2**Rational(7, 10)*3**Rational(8, 15) assert sqrt(8)*(-24)**Rational(1, 3)*(-6)**Rational(1, 5) == \ 4*(-3)**Rational(8, 15)*2**Rational(7, 10) assert 2**Rational(1, 3)*2**Rational(8, 9) == 2*2**Rational(2, 9) assert 2**Rational(2, 3)*6**Rational(1, 3) == 2*3**Rational(1, 3) assert 2**Rational(2, 3)*6**Rational(8, 9) == \ 2*2**Rational(5, 9)*3**Rational(8, 9) assert (-2)**Rational(2, S(3))*(-4)**Rational(1, S(3)) == -2*2**Rational(1, 3) assert 3*Pow(3, 2, evaluate=False) == 3**3 assert 3*Pow(3, -1/S(3), evaluate=False) == 3**(2/S(3)) assert (-2)**(1/S(3))*(-3)**(1/S(4))*(-5)**(5/S(6)) == \ -(-1)**Rational(5, 12)*2**Rational(1, 3)*3**Rational(1, 4) * \ 5**Rational(5, 6) assert Integer(-2)**Symbol('', even=True) == \ Integer(2)**Symbol('', even=True) assert (-1)**Float(.5) == 1.0*I def test_powers_Rational(): """Test Rational._eval_power""" # check infinity assert Rational(1, 2) ** S.Infinity == 0 assert Rational(3, 2) ** S.Infinity == S.Infinity assert Rational(-1, 2) ** S.Infinity == 0 assert Rational(-3, 2) ** S.Infinity == \ S.Infinity + S.Infinity * S.ImaginaryUnit # check Nan assert Rational(3, 4) ** S.NaN == S.NaN assert Rational(-2, 3) ** S.NaN == S.NaN # exact roots on numerator assert sqrt(Rational(4, 3)) == 2 * sqrt(3) / 3 assert Rational(4, 3) ** Rational(3, 2) == 8 * sqrt(3) / 9 assert sqrt(Rational(-4, 3)) == I * 2 * sqrt(3) / 3 assert Rational(-4, 3) ** Rational(3, 2) == - I * 8 * sqrt(3) / 9 assert Rational(27, 2) ** Rational(1, 3) == 3 * (2 ** Rational(2, 3)) / 2 assert Rational(5**3, 8**3) ** Rational(4, 3) == Rational(5**4, 8**4) # exact root on denominator assert sqrt(Rational(1, 4)) == Rational(1, 2) assert sqrt(Rational(1, -4)) == I * Rational(1, 2) assert sqrt(Rational(3, 4)) == sqrt(3) / 2 assert sqrt(Rational(3, -4)) == I * sqrt(3) / 2 assert Rational(5, 27) ** Rational(1, 3) == (5 ** Rational(1, 3)) / 3 # not exact roots assert sqrt(Rational(1, 2)) == sqrt(2) / 2 assert sqrt(Rational(-4, 7)) == I * sqrt(Rational(4, 7)) assert Rational(-3, 2)**Rational(-7, 3) == \ -4*(-1)**Rational(2, 3)*2**Rational(1, 3)*3**Rational(2, 3)/27 assert Rational(-3, 2)**Rational(-2, 3) == \ -(-1)**Rational(1, 3)*2**Rational(2, 3)*3**Rational(1, 3)/3 # negative integer power and negative rational base assert Rational(-2, 3) ** Rational(-2, 1) == Rational(9, 4) a = Rational(1, 10) assert a**Float(a, 2) == Float(a, 2)**Float(a, 2) assert Rational(-2, 3)**Symbol('', even=True) == \ Rational(2, 3)**Symbol('', even=True) def test_powers_Float(): assert str((S('-1/10')**S('3/10')).n()) == str(Float(-.1)**(.3)) def test_abs1(): assert Rational(1, 6) != Rational(-1, 6) assert abs(Rational(1, 6)) == abs(Rational(-1, 6)) def test_accept_int(): assert Float(4) == 4 def test_dont_accept_str(): assert Float("0.2") != "0.2" assert not (Float("0.2") == "0.2") def test_int(): a = Rational(5) assert int(a) == 5 a = Rational(9, 10) assert int(a) == int(-a) == 0 assert 1/(-1)**Rational(2, 3) == -(-1)**Rational(1, 3) assert int(pi) == 3 assert int(E) == 2 assert int(GoldenRatio) == 1 def test_long(): a = Rational(5) assert long(a) == 5 a = Rational(9, 10) assert long(a) == long(-a) == 0 a = Integer(2**100) assert long(a) == a assert long(pi) == 3 assert long(E) == 2 assert long(GoldenRatio) == 1 def test_real_bug(): x = Symbol("x") assert str(2.0*x*x) in ["(2.0*x)*x", "2.0*x**2", "2.00000000000000*x**2"] assert str(2.1*x*x) != "(2.0*x)*x" def test_bug_sqrt(): assert ((sqrt(Rational(2)) + 1)*(sqrt(Rational(2)) - 1)).expand() == 1 def test_pi_Pi(): "Test, that pi (instance) is imported, but Pi (class) is not" from sympy import pi with raises(ImportError): from sympy import Pi def test_no_len(): # there should be no len for numbers raises(TypeError, lambda: len(Rational(2))) raises(TypeError, lambda: len(Rational(2, 3))) raises(TypeError, lambda: len(Integer(2))) def test_issue222(): assert sqrt(Rational(1, 5)) == sqrt(Rational(1, 5)) assert 5 * sqrt(Rational(1, 5)) == sqrt(5) def test_issue593(): assert ((-1)**Rational(1, 6)).expand(complex=True) == I/2 + sqrt(3)/2 assert ((-5)**Rational(1, 6)).expand(complex=True) == \ 5**Rational(1, 6)*I/2 + 5**Rational(1, 6)*sqrt(3)/2 assert ((-64)**Rational(1, 6)).expand(complex=True) == I + sqrt(3) def test_issue324(): x = Symbol("x") assert sqrt(x - 1).as_base_exp() == (x - 1, S.Half) assert sqrt(x - 1) != I*sqrt(1 - x) def test_issue350(): x = Symbol("x", real=True) assert sqrt(x**2) == abs(x) assert sqrt(x - 1).subs(x, 5) == 2 def test_Integer_factors(): def F(i): return Integer(i).factors() assert F(1) == {1: 1} assert F(2) == {2: 1} assert F(3) == {3: 1} assert F(4) == {2: 2} assert F(5) == {5: 1} assert F(6) == {2: 1, 3: 1} assert F(7) == {7: 1} assert F(8) == {2: 3} assert F(9) == {3: 2} assert F(10) == {2: 1, 5: 1} assert F(11) == {11: 1} assert F(12) == {2: 2, 3: 1} assert F(13) == {13: 1} assert F(14) == {2: 1, 7: 1} assert F(15) == {3: 1, 5: 1} assert F(16) == {2: 4} assert F(17) == {17: 1} assert F(18) == {2: 1, 3: 2} assert F(19) == {19: 1} assert F(20) == {2: 2, 5: 1} assert F(21) == {3: 1, 7: 1} assert F(22) == {2: 1, 11: 1} assert F(23) == {23: 1} assert F(24) == {2: 3, 3: 1} assert F(25) == {5: 2} assert F(26) == {2: 1, 13: 1} assert F(27) == {3: 3} assert F(28) == {2: 2, 7: 1} assert F(29) == {29: 1} assert F(30) == {2: 1, 3: 1, 5: 1} assert F(31) == {31: 1} assert F(32) == {2: 5} assert F(33) == {3: 1, 11: 1} assert F(34) == {2: 1, 17: 1} assert F(35) == {5: 1, 7: 1} assert F(36) == {2: 2, 3: 2} assert F(37) == {37: 1} assert F(38) == {2: 1, 19: 1} assert F(39) == {3: 1, 13: 1} assert F(40) == {2: 3, 5: 1} assert F(41) == {41: 1} assert F(42) == {2: 1, 3: 1, 7: 1} assert F(43) == {43: 1} assert F(44) == {2: 2, 11: 1} assert F(45) == {3: 2, 5: 1} assert F(46) == {2: 1, 23: 1} assert F(47) == {47: 1} assert F(48) == {2: 4, 3: 1} assert F(49) == {7: 2} assert F(50) == {2: 1, 5: 2} assert F(51) == {3: 1, 17: 1} def test_Rational_factors(): def F(p, q, visual=None): return Rational(p, q).factors(visual=visual) assert F(2, 3) == {2: 1, 3: -1} assert F(2, 9) == {2: 1, 3: -2} assert F(2, 15) == {2: 1, 3: -1, 5: -1} assert F(6, 10) == {3: 1, 5: -1} assert str(F(12, 1, visual=True)) == '2**2*3**1' assert str(F(1, 1, visual=True)) == '1' assert str(F(25, 14, visual=True)) == '5**2/(2*7)' assert str(F(-25, 14*9, visual=True)) == '-5**2/(2*3**2*7)' def test_issue1008(): assert pi*(E + 10) + pi*(-E - 10) != 0 assert pi*(E + 10**10) + pi*(-E - 10**10) != 0 assert pi*(E + 10**20) + pi*(-E - 10**20) != 0 assert pi*(E + 10**80) + pi*(-E - 10**80) != 0 assert (pi*(E + 10) + pi*(-E - 10)).expand() == 0 assert (pi*(E + 10**10) + pi*(-E - 10**10)).expand() == 0 assert (pi*(E + 10**20) + pi*(-E - 10**20)).expand() == 0 assert (pi*(E + 10**80) + pi*(-E - 10**80)).expand() == 0 def test_IntegerInteger(): a = Integer(4) b = Integer(a) assert a == b def test_Rational_gcd_lcm_cofactors(): assert Integer(4).gcd(2) == Integer(2) assert Integer(4).lcm(2) == Integer(4) assert Integer(4).gcd(Integer(2)) == Integer(2) assert Integer(4).lcm(Integer(2)) == Integer(4) assert Integer(4).gcd(3) == Integer(1) assert Integer(4).lcm(3) == Integer(12) assert Integer(4).gcd(Integer(3)) == Integer(1) assert Integer(4).lcm(Integer(3)) == Integer(12) assert Rational(4, 3).gcd(2) == Rational(2, 3) assert Rational(4, 3).lcm(2) == Integer(4) assert Rational(4, 3).gcd(Integer(2)) == Rational(2, 3) assert Rational(4, 3).lcm(Integer(2)) == Integer(4) assert Integer(4).gcd(Rational(2, 9)) == Rational(2, 9) assert Integer(4).lcm(Rational(2, 9)) == Integer(4) assert Rational(4, 3).gcd(Rational(2, 9)) == Rational(2, 9) assert Rational(4, 3).lcm(Rational(2, 9)) == Rational(4, 3) assert Rational(4, 5).gcd(Rational(2, 9)) == Rational(2, 45) assert Rational(4, 5).lcm(Rational(2, 9)) == Integer(4) assert Integer(4).cofactors(2) == (Integer(2), Integer(2), Integer(1)) assert Integer(4).cofactors(Integer(2)) == \ (Integer(2), Integer(2), Integer(1)) assert Integer(4).gcd(Float(2.0)) == S.One assert Integer(4).lcm(Float(2.0)) == Float(8.0) assert Integer(4).cofactors(Float(2.0)) == (S.One, Integer(4), Float(2.0)) assert Rational(1, 2).gcd(Float(2.0)) == S.One assert Rational(1, 2).lcm(Float(2.0)) == Float(1.0) assert Rational(1, 2).cofactors(Float(2.0)) == \ (S.One, Rational(1, 2), Float(2.0)) def test_Float_gcd_lcm_cofactors(): assert Float(2.0).gcd(Integer(4)) == S.One assert Float(2.0).lcm(Integer(4)) == Float(8.0) assert Float(2.0).cofactors(Integer(4)) == (S.One, Float(2.0), Integer(4)) assert Float(2.0).gcd(Rational(1, 2)) == S.One assert Float(2.0).lcm(Rational(1, 2)) == Float(1.0) assert Float(2.0).cofactors(Rational(1, 2)) == \ (S.One, Float(2.0), Rational(1, 2)) def test_issue1512(): assert abs(pi._evalf(50) - 3.14159265358979) < 1e-10 assert abs(E._evalf(50) - 2.71828182845905) < 1e-10 assert abs(Catalan._evalf(50) - 0.915965594177219) < 1e-10 assert abs(EulerGamma._evalf(50) - 0.577215664901533) < 1e-10 assert abs(GoldenRatio._evalf(50) - 1.61803398874989) < 1e-10 x = Symbol("x") assert (pi + x).evalf() == pi.evalf() + x assert (E + x).evalf() == E.evalf() + x assert (Catalan + x).evalf() == Catalan.evalf() + x assert (EulerGamma + x).evalf() == EulerGamma.evalf() + x assert (GoldenRatio + x).evalf() == GoldenRatio.evalf() + x def test_conversion_to_mpmath(): assert mpmath.mpmathify(Integer(1)) == mpmath.mpf(1) assert mpmath.mpmathify(Rational(1, 2)) == mpmath.mpf(0.5) assert mpmath.mpmathify(Float('1.23', 15)) == mpmath.mpf('1.23') def test_relational(): # real x = S(.1) assert (x != cos) is True assert (x == cos) is False # rational x = Rational(1, 3) assert (x != cos) is True assert (x == cos) is False # integer defers to rational so these tests are omitted # number symbol x = pi assert (x != cos) is True assert (x == cos) is False def test_Integer_as_index(): assert 'hello'[Integer(2):] == 'llo' def test_Rational_int(): assert int( Rational(7, 5)) == 1 assert int( Rational(1, 2)) == 0 assert int(-Rational(1, 2)) == 0 assert int(-Rational(7, 5)) == -1 def test_zoo(): b = Symbol('b', bounded=True) nz = Symbol('nz', nonzero=True) p = Symbol('p', positive=True) n = Symbol('n', negative=True) im = Symbol('i', imaginary=True) c = Symbol('c', complex=True) pb = Symbol('pb', positive=True, bounded=True) nb = Symbol('nb', negative=True, bounded=True) imb = Symbol('ib', imaginary=True, bounded=True) for i in [I, S.Infinity, S.NegativeInfinity, S.Zero, S.One, S.Pi, S.Half, S(3), log(3), b, nz, p, n, im, pb, nb, imb, c]: if i.is_bounded and (i.is_real or i.is_imaginary): assert i + zoo is zoo assert i - zoo is zoo assert zoo + i is zoo assert zoo - i is zoo elif i.is_bounded is not False: assert (i + zoo).is_Add assert (i - zoo).is_Add assert (zoo + i).is_Add assert (zoo - i).is_Add else: assert (i + zoo) is S.NaN assert (i - zoo) is S.NaN assert (zoo + i) is S.NaN assert (zoo - i) is S.NaN if i.is_nonzero and (i.is_real or i.is_imaginary): assert i*zoo is zoo assert zoo*i is zoo elif i.is_zero: assert i*zoo is S.NaN assert zoo*i is S.NaN else: assert (i*zoo).is_Mul assert (zoo*i).is_Mul if (1/i).is_nonzero and (i.is_real or i.is_imaginary): assert zoo/i is zoo elif (1/i).is_zero: assert zoo/i is S.NaN else: assert (zoo/i).is_Mul assert (I*oo).is_Mul # allow directed infinity assert zoo + zoo is S.NaN assert zoo * zoo is zoo assert zoo - zoo is S.NaN assert zoo/zoo is S.NaN assert zoo**zoo is S.NaN assert zoo**0 is S.One assert zoo**2 is zoo assert 1/zoo is S.Zero assert Mul.flatten([S(-1), oo, S(0)]) == ([S.NaN], [], None) def test_issue_1023(): x = Symbol('x', nonpositive=True) assert (oo + x).is_Add x = Symbol('x', bounded=True) assert (oo + x).is_Add # x could be imaginary x = Symbol('x', finite=True) assert (oo + x).is_Add # x could be imaginary x = Symbol('x', infinitesimal=True) assert (oo + x).is_Add # x could be imaginary x = Symbol('x', nonnegative=True) assert oo + x == oo x = Symbol('x', bounded=True, real=True) assert oo + x == oo x = Symbol('x', finite=True, real=True) assert oo + x == oo x = Symbol('x', infinitesimal=True, real=True) assert oo + x == oo # similarily for negative infinity x = Symbol('x', nonnegative=True) assert (-oo + x).is_Add x = Symbol('x', bounded=True) assert (-oo + x).is_Add x = Symbol('x', finite=True) assert (-oo + x).is_Add x = Symbol('x', infinitesimal=True) assert (-oo + x).is_Add x = Symbol('x', nonpositive=True) assert -oo + x == -oo x = Symbol('x', bounded=True, real=True) assert -oo + x == -oo x = Symbol('x', finite=True, real=True) assert -oo + x == -oo x = Symbol('x', infinitesimal=True, real=True) assert -oo + x == -oo def test_GoldenRatio_expand(): assert GoldenRatio.expand(func=True) == S.Half + sqrt(5)/2 def test_as_content_primitive(): assert S.Zero.as_content_primitive() == (1, 0) assert S.Half.as_content_primitive() == (S.Half, 1) assert (-S.Half).as_content_primitive() == (S.Half, -1) assert S(3).as_content_primitive() == (3, 1) assert S(3.1).as_content_primitive() == (1, 3.1) @XFAIL def test_hashing_sympy_integers(): # Test for issue #1973 # http://code.google.com/p/sympy/issues/detail?id=1973 assert hash(S(4)) == 4 assert hash(S(4)) == hash(int(4)) def test_issue_1073(): assert int((E**100).round()) == \ 26881171418161354484126255515800135873611119 assert int((pi**100).round()) == \ 51878483143196131920862615246303013562686760680406 assert int((Rational(1)/EulerGamma**100).round()) == \ 734833795660954410469466 @XFAIL def test_mpmath_issues(): from sympy.mpmath.libmp.libmpf import _normalize import sympy.mpmath.libmp as mlib rnd = mlib.round_nearest mpf = (0, long(0), -123, -1, 53, rnd) # nan assert _normalize(mpf, 53) != (0, long(0), 0, 0) mpf = (0, long(0), -456, -2, 53, rnd) # +inf assert _normalize(mpf, 53) != (0, long(0), 0, 0) mpf = (1, long(0), -789, -3, 53, rnd) # -inf assert _normalize(mpf, 53) != (0, long(0), 0, 0) from sympy.mpmath.libmp.libmpf import fnan assert mlib.mpf_eq(fnan, fnan) def test_Catalan_EulerGamma_prec(): n = GoldenRatio f = Float(n.n(), 5) assert f._mpf_ == (0, long(212079), -17, 18) assert f._prec == 20 assert n._as_mpf_val(20) == f._mpf_ n = EulerGamma f = Float(n.n(), 5) assert f._mpf_ == (0, long(302627), -19, 19) assert f._prec == 20 assert n._as_mpf_val(20) == f._mpf_ def test_Float_eq(): assert Float(.12, 3) != Float(.12, 4) assert Float(.12, 3) == .12 assert 0.12 == Float(.12, 3) assert Float('.12', 22) != .12 def test_int_NumberSymbols(): assert [int(i) for i in [pi, EulerGamma, E, GoldenRatio, Catalan]] == \ [3, 0, 2, 1, 0] def test_3541(): from sympy.mpmath.libmp.libmpf import ( _normalize as mpf_normalize, finf, fninf, fzero) # fnan is not included because Float no longer returns fnan, # but otherwise, the same sort of test could apply assert Float(finf).is_zero is False assert Float(fninf).is_zero is False assert bool(Float(0)) is False def test_3250(): assert Float('23.e3', '')._prec == 10 assert Float('23e3', '')._prec == 20 assert Float('23000', '')._prec == 20 assert Float('-23000', '')._prec == 20 def test_mpf_norm(): assert mpf_norm((1, 0, 1, 0), 10) == mpf('0')._mpf_ assert Float._new((1, 0, 1, 0), 10)._mpf_ == mpf('0')._mpf_ def test_latex(): assert latex(pi) == r"\pi" assert latex(E) == r"e" assert latex(GoldenRatio) == r"\phi" assert latex(EulerGamma) == r"\gamma" assert latex(oo) == r"\infty" assert latex(-oo) == r"-\infty" assert latex(zoo) == r"\tilde{\infty}" assert latex(nan) == r"\mathrm{NaN}" assert latex(I) == r"i" sympy-0.7.4.1/sympy/core/tests/test_arit.py0000644000175000017500000013157612253362407021104 0ustar georgeskgeorgeskfrom __future__ import division from sympy import (Symbol, sin, cos, exp, sqrt, Rational, Float, re, pi, sympify, Add, Mul, Pow, Mod, I, log, S, Max, Or, symbols, oo, Integer, sign, im ) from sympy.core.compatibility import long from sympy.utilities.pytest import XFAIL, raises from sympy.utilities.randtest import test_numerically x = Symbol('x') y = Symbol('y') z = Symbol('z') def test_bug1(): assert re(x) != x x.series(x, 0, 1) assert re(x) != x a = Symbol("a") b = Symbol("b", positive=True) c = Symbol("c") def test_Symbol(): e = a*b assert e == a*b assert a*b*b == a*b**2 assert a*b*b + c == c + a*b**2 assert a*b*b - c == -c + a*b**2 def test_arit0(): p = Rational(5) e = a*b assert e == a*b e = a*b + b*a assert e == 2*a*b e = a*b + b*a + a*b + p*b*a assert e == 8*a*b e = a*b + b*a + a*b + p*b*a + a assert e == a + 8*a*b e = a + a assert e == 2*a e = a + b + a assert e == b + 2*a e = a + b*b + a + b*b assert e == 2*a + 2*b**2 e = a + Rational(2) + b*b + a + b*b + p assert e == 7 + 2*a + 2*b**2 e = (a + b*b + a + b*b)*p assert e == 5*(2*a + 2*b**2) e = (a*b*c + c*b*a + b*a*c)*p assert e == 15*a*b*c e = (a*b*c + c*b*a + b*a*c)*p - Rational(15)*a*b*c assert e == Rational(0) e = Rational(50)*(a - a) assert e == Rational(0) e = b*a - b - a*b + b assert e == Rational(0) e = a*b + c**p assert e == a*b + c**5 e = a/b assert e == a*b**(-1) e = a*2*2 assert e == 4*a e = 2 + a*2/2 assert e == 2 + a e = 2 - a - 2 assert e == -a e = 2*a*2 assert e == 4*a e = 2/a/2 assert e == a**(-1) e = 2**a**2 assert e == 2**(a**2) e = -(1 + a) assert e == -1 - a e = Rational(1, 2)*(1 + a) assert e == Rational(1, 2) + a/2 def test_div(): e = a/b assert e == a*b**(-1) e = a/b + c/2 assert e == a*b**(-1) + Rational(1)/2*c e = (1 - b)/(b - 1) assert e == (1 + -b)*((-1) + b)**(-1) def test_pow(): n1 = Rational(1) n2 = Rational(2) n5 = Rational(5) e = a*a assert e == a**2 e = a*a*a assert e == a**3 e = a*a*a*a**Rational(6) assert e == a**9 e = a*a*a*a**Rational(6) - a**Rational(9) assert e == Rational(0) e = a**(b - b) assert e == Rational(1) e = (a - a)**b assert e == Rational(0) e = (a + Rational(1) - a)**b assert e == Rational(1) e = (a + b + c)**n2 assert e == (a + b + c)**2 assert e.expand() == 2*b*c + 2*a*c + 2*a*b + a**2 + c**2 + b**2 e = (a + b)**n2 assert e == (a + b)**2 assert e.expand() == 2*a*b + a**2 + b**2 e = (a + b)**(n1/n2) assert e == sqrt(a + b) assert e.expand() == sqrt(a + b) n = n5**(n1/n2) assert n == sqrt(5) e = n*a*b - n*b*a assert e == Rational(0) e = n*a*b + n*b*a assert e == 2*a*b*sqrt(5) assert e.diff(a) == 2*b*sqrt(5) assert e.diff(a) == 2*b*sqrt(5) e = a/b**2 assert e == a*b**(-2) assert sqrt(2*(1 + sqrt(2))) == (2*(1 + 2**Rational(1, 2)))**Rational(1, 2) x = Symbol('x') y = Symbol('y') assert ((x*y)**3).expand() == y**3 * x**3 assert ((x*y)**-3).expand() == y**-3 * x**-3 assert (x**5*(3*x)**(3)).expand() == 27 * x**8 assert (x**5*(-3*x)**(3)).expand() == -27 * x**8 assert (x**5*(3*x)**(-3)).expand() == Rational(1, 27) * x**2 assert (x**5*(-3*x)**(-3)).expand() == -Rational(1, 27) * x**2 # expand_power_exp assert (x**(y**(x + exp(x + y)) + z)).expand(deep=False) == \ x**z*x**(y**(x + exp(x + y))) assert (x**(y**(x + exp(x + y)) + z)).expand() == \ x**z*x**(y**x*y**(exp(x)*exp(y))) n = Symbol('k', even=False) k = Symbol('k', even=True) assert (-1)**x == (-1)**x assert (-1)**n == (-1)**n assert (-2)**k == 2**k assert (-2*x)**k == (-2*x)**k # we choose not to auto expand this assert (-1)**k == 1 def test_pow2(): # x**(2*y) is always (x**y)**2 but is only (x**2)**y if # x.is_positive or y.is_integer # let x = 1 to see why the following are not true. assert ((-x)**2)**Rational(1, 3) != ((-x)**Rational(1, 3))**2 assert (-x)**Rational(2, 3) != x**Rational(2, 3) assert (-x)**Rational(5, 7) != -x**Rational(5, 7) def test_pow3(): assert sqrt(2)**3 == 2 * sqrt(2) assert sqrt(2)**3 == sqrt(8) def test_pow_E(): assert 2**(y/log(2)) == S.Exp1**y assert 2**(y/log(2)/3) == S.Exp1**(y/3) assert 3**(1/log(-3)) != S.Exp1 assert (3 + 2*I)**(1/(log(-3 - 2*I) + I*pi)) == S.Exp1 assert (4 + 2*I)**(1/(log(-4 - 2*I) + I*pi)) == S.Exp1 assert (3 + 2*I)**(1/(log(-3 - 2*I, 3)/2 + I*pi/log(3)/2)) == 9 assert (3 + 2*I)**(1/(log(3 + 2*I, 3)/2)) == 9 # every time tests are run they will affirm with a different random # value that this identity holds while 1: b = x._random() r, i = b.as_real_imag() if i: break assert test_numerically(b**(1/(log(-b) + sign(i)*I*pi).n()), S.Exp1) def test_pow_issue417(): assert 4**Rational(1, 4) == sqrt(2) def test_pow_im(): for m in (-2, -1, 2): for d in (3, 4, 5): b = m*I for i in range(1, 4*d + 1): e = Rational(i, d) assert (b**e - b.n()**e.n()).n(2, chop=1e-10) == 0 e = Rational(7, 3) assert (2*x*I)**e == 4*2**Rational(1, 3)*(I*x)**e # same as Wolfram Alpha im = symbols('im', imaginary=True) assert (2*im*I)**e == 4*2**Rational(1, 3)*(I*im)**e args = [I, I, I, I, 2] e = Rational(1, 3) ans = 2**e assert Mul(*args, evaluate=False)**e == ans assert Mul(*args)**e == ans args = [I, I, I, 2] e = Rational(1, 3) ans = 2**e*(-I)**e assert Mul(*args, evaluate=False)**e == ans assert Mul(*args)**e == ans args.append(-3) ans = (6*I)**e assert Mul(*args, evaluate=False)**e == ans assert Mul(*args)**e == ans args.append(-1) ans = (-6*I)**e assert Mul(*args, evaluate=False)**e == ans assert Mul(*args)**e == ans args = [I, I, 2] e = Rational(1, 3) ans = (-2)**e assert Mul(*args, evaluate=False)**e == ans assert Mul(*args)**e == ans args.append(-3) ans = (6)**e assert Mul(*args, evaluate=False)**e == ans assert Mul(*args)**e == ans args.append(-1) ans = (-6)**e assert Mul(*args, evaluate=False)**e == ans assert Mul(*args)**e == ans assert Mul(Pow(-1, Rational(3, 2), evaluate=False), I, I) == I assert Mul(I*Pow(I, S.Half, evaluate=False)) == (-1)**Rational(3, 4) def test_real_mul(): assert Float(0) * pi * x == Float(0) assert set((Float(1) * pi * x).args) == set([Float(1), pi, x]) def test_ncmul(): A = Symbol("A", commutative=False) B = Symbol("B", commutative=False) C = Symbol("C", commutative=False) assert A*B != B*A assert A*B*C != C*B*A assert A*b*B*3*C == 3*b*A*B*C assert A*b*B*3*C != 3*b*B*A*C assert A*b*B*3*C == 3*A*B*C*b assert A + B == B + A assert (A + B)*C != C*(A + B) assert C*(A + B)*C != C*C*(A + B) assert A*A == A**2 assert (A + B)*(A + B) == (A + B)**2 assert A**-1 * A == 1 assert A/A == 1 assert A/(A**2) == 1/A assert A/(1 + A) == A/(1 + A) assert set((A + B + 2*(A + B)).args) == \ set([A, B, 2*(A + B)]) def test_ncpow(): x = Symbol('x', commutative=False) y = Symbol('y', commutative=False) z = Symbol('z', commutative=False) a = Symbol('a') b = Symbol('b') c = Symbol('c') assert (x**2)*(y**2) != (y**2)*(x**2) assert (x**-2)*y != y*(x**2) assert 2**x*2**y != 2**(x + y) assert 2**x*2**y*2**z != 2**(x + y + z) assert 2**x*2**(2*x) == 2**(3*x) assert 2**x*2**(2*x)*2**x == 2**(4*x) assert exp(x)*exp(y) != exp(y)*exp(x) assert exp(x)*exp(y)*exp(z) != exp(y)*exp(x)*exp(z) assert exp(x)*exp(y)*exp(z) != exp(x + y + z) assert x**a*x**b != x**(a + b) assert x**a*x**b*x**c != x**(a + b + c) assert x**3*x**4 == x**7 assert x**3*x**4*x**2 == x**9 assert x**a*x**(4*a) == x**(5*a) assert x**a*x**(4*a)*x**a == x**(6*a) def test_powerbug(): x = Symbol("x") assert x**1 != (-x)**1 assert x**2 == (-x)**2 assert x**3 != (-x)**3 assert x**4 == (-x)**4 assert x**5 != (-x)**5 assert x**6 == (-x)**6 assert x**128 == (-x)**128 assert x**129 != (-x)**129 assert (2*x)**2 == (-2*x)**2 def test_Mul_doesnt_expand_exp(): x = Symbol('x') y = Symbol('y') assert exp(x)*exp(y) == exp(x)*exp(y) assert 2**x*2**y == 2**x*2**y assert x**2*x**3 == x**5 assert 2**x*3**x == 6**x assert x**(y)*x**(2*y) == x**(3*y) assert sqrt(2)*sqrt(2) == 2 assert 2**x*2**(2*x) == 2**(3*x) assert sqrt(2)*2**Rational(1, 4)*5**Rational(3, 4) == 10**Rational(3, 4) assert (x**(-log(5)/log(3))*x)/(x*x**( - log(5)/log(3))) == sympify(1) def test_Add_Mul_is_integer(): x = Symbol('x') k = Symbol('k', integer=True) n = Symbol('n', integer=True) assert (2*k).is_integer is True assert (-k).is_integer is True assert (k/3).is_integer is None assert (x*k*n).is_integer is None assert (k + n).is_integer is True assert (k + x).is_integer is None assert (k + n*x).is_integer is None assert (k + n/3).is_integer is None assert ((1 + sqrt(3))*(-sqrt(3) + 1)).is_integer is not False assert (1 + (1 + sqrt(3))*(-sqrt(3) + 1)).is_integer is not False def test_Add_Mul_is_bounded(): x = Symbol('x', real=True, bounded=False) assert sin(x).is_bounded is True assert (x*sin(x)).is_bounded is False assert (1024*sin(x)).is_bounded is True assert (sin(x)*exp(x)).is_bounded is not True assert (sin(x)*cos(x)).is_bounded is True assert (x*sin(x)*exp(x)).is_bounded is not True assert (sin(x) - 67).is_bounded is True assert (sin(x) + exp(x)).is_bounded is not True assert (1 + x).is_bounded is False assert (1 + x**2 + (1 + x)*(1 - x)).is_bounded is None assert (sqrt(2)*(1 + x)).is_bounded is False assert (sqrt(2)*(1 + x)*(1 - x)).is_bounded is False def test_Mul_is_even_odd(): x = Symbol('x', integer=True) y = Symbol('y', integer=True) k = Symbol('k', odd=True) n = Symbol('n', odd=True) m = Symbol('m', even=True) assert (2*x).is_even is True assert (2*x).is_odd is False assert (3*x).is_even is None assert (3*x).is_odd is None assert (k/3).is_integer is None assert (k/3).is_even is None assert (k/3).is_odd is None assert (2*n).is_even is True assert (2*n).is_odd is False assert (2*m).is_even is True assert (2*m).is_odd is False assert (-n).is_even is False assert (-n).is_odd is True assert (k*n).is_even is False assert (k*n).is_odd is True assert (k*m).is_even is True assert (k*m).is_odd is False assert (k*n*m).is_even is True assert (k*n*m).is_odd is False assert (k*m*x).is_even is True assert (k*m*x).is_odd is False # issue 3692: assert (x/2).is_integer is None assert (k/2).is_integer is False assert (m/2).is_integer is True assert (x*y).is_even is None assert (x*x).is_even is None assert (x*(x + k)).is_even is True assert (x*(x + m)).is_even is None assert (x*y*(y + k)).is_even is True assert (x*y*(y + m)).is_even is None assert (x*y).is_odd is None assert (x*x).is_odd is None assert (x*(x + k)).is_odd is False assert (x*(x + m)).is_odd is None assert (x*y*(y + k)).is_odd is False assert (x*y*(y + m)).is_odd is None def test_Mul_is_rational(): x = Symbol('x') n = Symbol('n', integer=True) m = Symbol('m', integer=True) assert (n/m).is_rational is True assert (x/pi).is_rational is None assert (x/n).is_rational is None assert (n/pi).is_rational is False def test_Add_is_rational(): x = Symbol('x') n = Symbol('n', rational=True) m = Symbol('m', rational=True) assert (n + m).is_rational is True assert (x + pi).is_rational is None assert (x + n).is_rational is None assert (n + pi).is_rational is False def test_Add_is_even_odd(): x = Symbol('x', integer=True) k = Symbol('k', odd=True) n = Symbol('n', odd=True) m = Symbol('m', even=True) assert (k + 7).is_even is True assert (k + 7).is_odd is False assert (-k + 7).is_even is True assert (-k + 7).is_odd is False assert (k - 12).is_even is False assert (k - 12).is_odd is True assert (-k - 12).is_even is False assert (-k - 12).is_odd is True assert (k + n).is_even is True assert (k + n).is_odd is False assert (k + m).is_even is False assert (k + m).is_odd is True assert (k + n + m).is_even is True assert (k + n + m).is_odd is False assert (k + n + x + m).is_even is None assert (k + n + x + m).is_odd is None def test_Mul_is_negative_positive(): x = Symbol('x', real=True) y = Symbol('y', real=False) neg = Symbol('neg', negative=True) pos = Symbol('pos', positive=True) nneg = Symbol('nneg', nonnegative=True) npos = Symbol('npos', nonpositive=True) assert neg.is_negative is True assert (-neg).is_negative is False assert (2*neg).is_negative is True assert (2*pos)._eval_is_negative() is False assert (2*pos).is_negative is False assert pos.is_negative is False assert (-pos).is_negative is True assert (2*pos).is_negative is False assert (pos*neg).is_negative is True assert (2*pos*neg).is_negative is True assert (-pos*neg).is_negative is False assert (pos*neg*y).is_negative is False # y.is_real=F; !real -> !neg assert nneg.is_negative is False assert (-nneg).is_negative is None assert (2*nneg).is_negative is False assert npos.is_negative is None assert (-npos).is_negative is False assert (2*npos).is_negative is None assert (nneg*npos).is_negative is None assert (neg*nneg).is_negative is None assert (neg*npos).is_negative is False assert (pos*nneg).is_negative is False assert (pos*npos).is_negative is None assert (npos*neg*nneg).is_negative is False assert (npos*pos*nneg).is_negative is None assert (-npos*neg*nneg).is_negative is None assert (-npos*pos*nneg).is_negative is False assert (17*npos*neg*nneg).is_negative is False assert (17*npos*pos*nneg).is_negative is None assert (neg*npos*pos*nneg).is_negative is False assert (x*neg).is_negative is None assert (nneg*npos*pos*x*neg).is_negative is None assert neg.is_positive is False assert (-neg).is_positive is True assert (2*neg).is_positive is False assert pos.is_positive is True assert (-pos).is_positive is False assert (2*pos).is_positive is True assert (pos*neg).is_positive is False assert (2*pos*neg).is_positive is False assert (-pos*neg).is_positive is True assert (-pos*neg*y).is_positive is False # y.is_real=F; !real -> !neg assert nneg.is_positive is None assert (-nneg).is_positive is False assert (2*nneg).is_positive is None assert npos.is_positive is False assert (-npos).is_positive is None assert (2*npos).is_positive is False assert (nneg*npos).is_positive is False assert (neg*nneg).is_positive is False assert (neg*npos).is_positive is None assert (pos*nneg).is_positive is None assert (pos*npos).is_positive is False assert (npos*neg*nneg).is_positive is None assert (npos*pos*nneg).is_positive is False assert (-npos*neg*nneg).is_positive is False assert (-npos*pos*nneg).is_positive is None assert (17*npos*neg*nneg).is_positive is None assert (17*npos*pos*nneg).is_positive is False assert (neg*npos*pos*nneg).is_positive is None assert (x*neg).is_positive is None assert (nneg*npos*pos*x*neg).is_positive is None def test_Mul_is_negative_positive_2(): a = Symbol('a', nonnegative=True) b = Symbol('b', nonnegative=True) c = Symbol('c', nonpositive=True) d = Symbol('d', nonpositive=True) assert (a*b).is_nonnegative is True assert (a*b).is_negative is False assert (a*b).is_zero is None assert (a*b).is_positive is None assert (c*d).is_nonnegative is True assert (c*d).is_negative is False assert (c*d).is_zero is None assert (c*d).is_positive is None assert (a*c).is_nonpositive is True assert (a*c).is_positive is False assert (a*c).is_zero is None assert (a*c).is_negative is None def test_Mul_is_nonpositive_nonnegative(): x = Symbol('x', real=True) k = Symbol('k', negative=True) n = Symbol('n', positive=True) u = Symbol('u', nonnegative=True) v = Symbol('v', nonpositive=True) assert k.is_nonpositive is True assert (-k).is_nonpositive is False assert (2*k).is_nonpositive is True assert n.is_nonpositive is False assert (-n).is_nonpositive is True assert (2*n).is_nonpositive is False assert (n*k).is_nonpositive is True assert (2*n*k).is_nonpositive is True assert (-n*k).is_nonpositive is False assert u.is_nonpositive is None assert (-u).is_nonpositive is True assert (2*u).is_nonpositive is None assert v.is_nonpositive is True assert (-v).is_nonpositive is None assert (2*v).is_nonpositive is True assert (u*v).is_nonpositive is True assert (k*u).is_nonpositive is True assert (k*v).is_nonpositive is None assert (n*u).is_nonpositive is None assert (n*v).is_nonpositive is True assert (v*k*u).is_nonpositive is None assert (v*n*u).is_nonpositive is True assert (-v*k*u).is_nonpositive is True assert (-v*n*u).is_nonpositive is None assert (17*v*k*u).is_nonpositive is None assert (17*v*n*u).is_nonpositive is True assert (k*v*n*u).is_nonpositive is None assert (x*k).is_nonpositive is None assert (u*v*n*x*k).is_nonpositive is None assert k.is_nonnegative is False assert (-k).is_nonnegative is True assert (2*k).is_nonnegative is False assert n.is_nonnegative is True assert (-n).is_nonnegative is False assert (2*n).is_nonnegative is True assert (n*k).is_nonnegative is False assert (2*n*k).is_nonnegative is False assert (-n*k).is_nonnegative is True assert u.is_nonnegative is True assert (-u).is_nonnegative is None assert (2*u).is_nonnegative is True assert v.is_nonnegative is None assert (-v).is_nonnegative is True assert (2*v).is_nonnegative is None assert (u*v).is_nonnegative is None assert (k*u).is_nonnegative is None assert (k*v).is_nonnegative is True assert (n*u).is_nonnegative is True assert (n*v).is_nonnegative is None assert (v*k*u).is_nonnegative is True assert (v*n*u).is_nonnegative is None assert (-v*k*u).is_nonnegative is None assert (-v*n*u).is_nonnegative is True assert (17*v*k*u).is_nonnegative is True assert (17*v*n*u).is_nonnegative is None assert (k*v*n*u).is_nonnegative is True assert (x*k).is_nonnegative is None assert (u*v*n*x*k).is_nonnegative is None def test_Add_is_negative_positive(): x = Symbol('x', real=True) k = Symbol('k', negative=True) n = Symbol('n', positive=True) u = Symbol('u', nonnegative=True) v = Symbol('v', nonpositive=True) assert (k - 2).is_negative is True assert (k + 17).is_negative is None assert (-k - 5).is_negative is None assert (-k + 123).is_negative is False assert (k - n).is_negative is True assert (k + n).is_negative is None assert (-k - n).is_negative is None assert (-k + n).is_negative is False assert (k - n - 2).is_negative is True assert (k + n + 17).is_negative is None assert (-k - n - 5).is_negative is None assert (-k + n + 123).is_negative is False assert (-2*k + 123*n + 17).is_negative is False assert (k + u).is_negative is None assert (k + v).is_negative is True assert (n + u).is_negative is False assert (n + v).is_negative is None assert (u - v).is_negative is False assert (u + v).is_negative is None assert (-u - v).is_negative is None assert (-u + v).is_negative is None assert (u - v + n + 2).is_negative is False assert (u + v + n + 2).is_negative is None assert (-u - v + n + 2).is_negative is None assert (-u + v + n + 2).is_negative is None assert (k + x).is_negative is None assert (k + x - n).is_negative is None assert (k - 2).is_positive is False assert (k + 17).is_positive is None assert (-k - 5).is_positive is None assert (-k + 123).is_positive is True assert (k - n).is_positive is False assert (k + n).is_positive is None assert (-k - n).is_positive is None assert (-k + n).is_positive is True assert (k - n - 2).is_positive is False assert (k + n + 17).is_positive is None assert (-k - n - 5).is_positive is None assert (-k + n + 123).is_positive is True assert (-2*k + 123*n + 17).is_positive is True assert (k + u).is_positive is None assert (k + v).is_positive is False assert (n + u).is_positive is True assert (n + v).is_positive is None assert (u - v).is_positive is None assert (u + v).is_positive is None assert (-u - v).is_positive is None assert (-u + v).is_positive is False assert (u - v - n - 2).is_positive is None assert (u + v - n - 2).is_positive is None assert (-u - v - n - 2).is_positive is None assert (-u + v - n - 2).is_positive is False assert (n + x).is_positive is None assert (n + x - k).is_positive is None assert (-3 - sqrt(5) + (-sqrt(10)/2 - sqrt(2)/2)**2).is_zero is not False def test_Add_is_nonpositive_nonnegative(): x = Symbol('x', real=True) k = Symbol('k', negative=True) n = Symbol('n', positive=True) u = Symbol('u', nonnegative=True) v = Symbol('v', nonpositive=True) assert (u - 2).is_nonpositive is None assert (u + 17).is_nonpositive is False assert (-u - 5).is_nonpositive is True assert (-u + 123).is_nonpositive is None assert (u - v).is_nonpositive is None assert (u + v).is_nonpositive is None assert (-u - v).is_nonpositive is None assert (-u + v).is_nonpositive is True assert (u - v - 2).is_nonpositive is None assert (u + v + 17).is_nonpositive is None assert (-u - v - 5).is_nonpositive is None assert (-u + v - 123).is_nonpositive is True assert (-2*u + 123*v - 17).is_nonpositive is True assert (k + u).is_nonpositive is None assert (k + v).is_nonpositive is True assert (n + u).is_nonpositive is False assert (n + v).is_nonpositive is None assert (k - n).is_nonpositive is True assert (k + n).is_nonpositive is None assert (-k - n).is_nonpositive is None assert (-k + n).is_nonpositive is False assert (k - n + u + 2).is_nonpositive is None assert (k + n + u + 2).is_nonpositive is None assert (-k - n + u + 2).is_nonpositive is None assert (-k + n + u + 2).is_nonpositive is False assert (u + x).is_nonpositive is None assert (v - x - n).is_nonpositive is None assert (u - 2).is_nonnegative is None assert (u + 17).is_nonnegative is True assert (-u - 5).is_nonnegative is False assert (-u + 123).is_nonnegative is None assert (u - v).is_nonnegative is True assert (u + v).is_nonnegative is None assert (-u - v).is_nonnegative is None assert (-u + v).is_nonnegative is None assert (u - v + 2).is_nonnegative is True assert (u + v + 17).is_nonnegative is None assert (-u - v - 5).is_nonnegative is None assert (-u + v - 123).is_nonnegative is False assert (2*u - 123*v + 17).is_nonnegative is True assert (k + u).is_nonnegative is None assert (k + v).is_nonnegative is False assert (n + u).is_nonnegative is True assert (n + v).is_nonnegative is None assert (k - n).is_nonnegative is False assert (k + n).is_nonnegative is None assert (-k - n).is_nonnegative is None assert (-k + n).is_nonnegative is True assert (k - n - u - 2).is_nonnegative is False assert (k + n - u - 2).is_nonnegative is None assert (-k - n - u - 2).is_nonnegative is None assert (-k + n - u - 2).is_nonnegative is None assert (u - x).is_nonnegative is None assert (v + x + n).is_nonnegative is None def test_Pow_is_integer(): x = Symbol('x') k = Symbol('k', integer=True) n = Symbol('n', integer=True, nonnegative=True) m = Symbol('m', integer=True, positive=True) assert (k**2).is_integer is True assert (k**(-2)).is_integer is False assert (2**k).is_integer is None assert (2**(-k)).is_integer is None assert (2**n).is_integer is True assert (2**(-n)).is_integer is None assert (2**m).is_integer is True assert (2**(-m)).is_integer is False assert (x**2).is_integer is None assert (2**x).is_integer is None assert (k**n).is_integer is True assert (k**(-n)).is_integer is None assert (k**x).is_integer is None assert (x**k).is_integer is None assert (k**(n*m)).is_integer is True assert (k**(-n*m)).is_integer is None assert sqrt(3).is_integer is False assert sqrt(.3).is_integer is False assert Pow(3, 2, evaluate=False).is_integer is True assert Pow(3, 0, evaluate=False).is_integer is True assert Pow(3, -2, evaluate=False).is_integer is False assert Pow(S.Half, 3, evaluate=False).is_integer is False # decided by re-evaluating assert Pow(3, S.Half, evaluate=False).is_integer is False assert Pow(3, S.Half, evaluate=False).is_integer is False assert Pow(4, S.Half, evaluate=False).is_integer is True assert Pow(S.Half, -2, evaluate=False).is_integer is True assert ((-1)**k).is_integer def test_Pow_is_real(): x = Symbol('x', real=True) y = Symbol('y', real=True, positive=True) assert (x**2).is_real is True assert (x**3).is_real is True assert (x**x).is_real is None assert (y**x).is_real is True assert (x**Rational(1, 3)).is_real is None assert (y**Rational(1, 3)).is_real is True assert sqrt(-1 - sqrt(2)).is_real is False i = Symbol('i', imaginary=True) assert (i**i).is_real is None assert (I**i).is_real is None assert ((-I)**i).is_real is None assert (2**i).is_real is None # (2**(pi/log(2) * I)) is real, 2**I is not assert (2**I).is_real is False assert (2**-I).is_real is False assert (i**2).is_real assert (i**3).is_real is False assert (i**x).is_real is None # could be (-I)**(2/3) e = Symbol('e', even=True) o = Symbol('o', odd=True) k = Symbol('k', integer=True) assert (i**e).is_real assert (i**o).is_real is False assert (i**k).is_real is None def test_real_Pow(): k = Symbol('k', integer=True, nonzero=True) assert (k**(I*pi/log(k))).is_real def test_Pow_is_bounded(): x = Symbol('x', real=True) p = Symbol('p', positive=True) n = Symbol('n', negative=True) assert (x**2).is_bounded is None # x could be oo assert (x**x).is_bounded is None # ditto assert (p**x).is_bounded is None # ditto assert (n**x).is_bounded is None # ditto assert (1/S.Pi).is_bounded assert (sin(x)**2).is_bounded is True assert (sin(x)**x).is_bounded is None assert (sin(x)**exp(x)).is_bounded is None assert (1/sin(x)).is_bounded is None # if zero, no, otherwise yes assert (1/exp(x)).is_bounded is None # x could be -oo def test_Pow_is_even_odd(): x = Symbol('x') k = Symbol('k', even=True) n = Symbol('n', odd=True) m = Symbol('m', integer=True, nonnegative=True) p = Symbol('p', integer=True, positive=True) assert ((-1)**n).is_odd assert ((-1)**k).is_odd assert ((-1)**(m - p)).is_odd assert (k**2).is_even is True assert (n**2).is_even is False assert (2**k).is_even is None assert (x**2).is_even is None assert (k**m).is_even is None assert (n**m).is_even is False assert (k**p).is_even is True assert (n**p).is_even is False assert (m**k).is_even is None assert (p**k).is_even is None assert (m**n).is_even is None assert (p**n).is_even is None assert (k**x).is_even is None assert (n**x).is_even is None assert (k**2).is_odd is False assert (n**2).is_odd is True assert (3**k).is_odd is None assert (k**m).is_odd is None assert (n**m).is_odd is True assert (k**p).is_odd is False assert (n**p).is_odd is True assert (m**k).is_odd is None assert (p**k).is_odd is None assert (m**n).is_odd is None assert (p**n).is_odd is None assert (k**x).is_odd is None assert (n**x).is_odd is None def test_Pow_is_negative_positive(): x = Symbol('x', real=True) k = Symbol('k', integer=True, positive=True) n = Symbol('n', even=True) m = Symbol('m', odd=True) z = Symbol('z') assert (2**x).is_positive is True assert ((-2)**x).is_positive is None assert ((-2)**n).is_positive is True assert ((-2)**m).is_positive is False assert (k**2).is_positive is True assert (k**(-2)).is_positive is True assert (k**x).is_positive is True assert ((-k)**x).is_positive is None assert ((-k)**n).is_positive is True assert ((-k)**m).is_positive is False assert (2**x).is_negative is False assert ((-2)**x).is_negative is None assert ((-2)**n).is_negative is False assert ((-2)**m).is_negative is True assert (k**2).is_negative is False assert (k**(-2)).is_negative is False assert (k**x).is_negative is False assert ((-k)**x).is_negative is None assert ((-k)**n).is_negative is False assert ((-k)**m).is_negative is True assert (2**z).is_positive is None assert (2**z).is_negative is None def test_Pow_is_nonpositive_nonnegative(): x = Symbol('x', real=True) k = Symbol('k', integer=True, nonnegative=True) l = Symbol('l', integer=True, positive=True) n = Symbol('n', even=True) m = Symbol('m', odd=True) assert (2**x).is_nonnegative is True assert ((-2)**x).is_nonnegative is None assert ((-2)**n).is_nonnegative is True assert ((-2)**m).is_nonnegative is False assert (k**2).is_nonnegative is True assert (k**(-2)).is_nonnegative is True assert (k**x).is_nonnegative is None # NOTE (0**x).is_real = U assert (l**x).is_nonnegative is True assert (l**x).is_positive is True assert ((-k)**x).is_nonnegative is None assert ((-k)**n).is_nonnegative is True assert ((-k)**m).is_nonnegative is None assert (2**x).is_nonpositive is False assert ((-2)**x).is_nonpositive is None assert ((-2)**n).is_nonpositive is False assert ((-2)**m).is_nonpositive is True assert (k**2).is_nonpositive is None assert (k**(-2)).is_nonpositive is None assert (k**x).is_nonpositive is None assert ((-k)**x).is_nonpositive is None assert ((-k)**n).is_nonpositive is None assert ((-k)**m).is_nonpositive is True def test_Mul_is_imaginary_real(): r = Symbol('r', real=True) i = Symbol('i', imaginary=True) ii = Symbol('ii', imaginary=True) x = Symbol('x') assert I.is_imaginary is True assert I.is_real is False assert (-I).is_imaginary is True assert (-I).is_real is False assert (3*I).is_imaginary is True assert (3*I).is_real is False assert (I*I).is_imaginary is False assert (I*I).is_real is True assert (r*i).is_imaginary is True assert (r*i).is_real is False assert (x*i).is_imaginary is None assert (x*i).is_real is None assert (i*ii).is_imaginary is False assert (i*ii).is_real is True assert (r*i*ii).is_imaginary is False assert (r*i*ii).is_real is True def test_Add_is_comparable(): assert (x + y).is_comparable is False assert (x + 1).is_comparable is False assert (Rational(1, 3) - sqrt(8)).is_comparable is True def test_Mul_is_comparable(): assert (x*y).is_comparable is False assert (x*2).is_comparable is False assert (sqrt(2)*Rational(1, 3)).is_comparable is True def test_Pow_is_comparable(): assert (x**y).is_comparable is False assert (x**2).is_comparable is False assert (sqrt(Rational(1, 3))).is_comparable is True def test_Add_is_positive_2(): e = Rational(1, 3) - sqrt(8) assert e.is_positive is False assert e.is_negative is True e = pi - 1 assert e.is_positive is True assert e.is_negative is False def test_Add_is_irrational(): i = Symbol('i', irrational=True) assert i.is_irrational is True assert i.is_rational is False assert (i + 1).is_irrational is True assert (i + 1).is_rational is False @XFAIL def test_issue432(): class MightyNumeric(tuple): def __rdiv__(self, other): return "something" def __rtruediv__(self, other): return "something" assert sympify(1)/MightyNumeric((1, 2)) == "something" def test_issue432b(): class Foo: def __init__(self): self.field = 1.0 def __mul__(self, other): self.field = self.field * other def __rmul__(self, other): self.field = other * self.field f = Foo() x = Symbol("x") assert f*x == x*f def test_bug3(): a = Symbol("a") b = Symbol("b", positive=True) e = 2*a + b f = b + 2*a assert e == f def test_suppressed_evaluation(): a = Add(0, 3, 2, evaluate=False) b = Mul(1, 3, 2, evaluate=False) c = Pow(3, 2, evaluate=False) assert a != 6 assert a.func is Add assert a.args == (3, 2) assert b != 6 assert b.func is Mul assert b.args == (3, 2) assert c != 9 assert c.func is Pow assert c.args == (3, 2) def test_Add_as_coeff_mul(): # Issue 2425. These should all be (1, self) assert (x + 1).as_coeff_mul() == (1, (x + 1,)) assert (x + 2).as_coeff_mul() == (1, (x + 2,)) assert (x + 3).as_coeff_mul() == (1, (x + 3,)) assert (x - 1).as_coeff_mul() == (1, (x - 1,)) assert (x - 2).as_coeff_mul() == (1, (x - 2,)) assert (x - 3).as_coeff_mul() == (1, (x - 3,)) n = Symbol('n', integer=True) assert (n + 1).as_coeff_mul() == (1, (n + 1,)) assert (n + 2).as_coeff_mul() == (1, (n + 2,)) assert (n + 3).as_coeff_mul() == (1, (n + 3,)) assert (n - 1).as_coeff_mul() == (1, (n - 1,)) assert (n - 2).as_coeff_mul() == (1, (n - 2,)) assert (n - 3).as_coeff_mul() == (1, (n - 3,)) def test_Pow_as_coeff_mul_doesnt_expand(): assert exp(x + y).as_coeff_mul() == (1, (exp(x + y),)) assert exp(x + exp(x + y)) != exp(x + exp(x)*exp(y)) def test_issue415(): assert sqrt(S.Half) * sqrt(6) == 2 * sqrt(3)/2 assert S(1)/2*sqrt(6)*sqrt(2) == sqrt(3) assert sqrt(6)/2*sqrt(2) == sqrt(3) assert sqrt(6)*sqrt(2)/2 == sqrt(3) def test_make_args(): assert Add.make_args(x) == (x,) assert Mul.make_args(x) == (x,) assert Add.make_args(x*y*z) == (x*y*z,) assert Mul.make_args(x*y*z) == (x*y*z).args assert Add.make_args(x + y + z) == (x + y + z).args assert Mul.make_args(x + y + z) == (x + y + z,) assert Add.make_args((x + y)**z) == ((x + y)**z,) assert Mul.make_args((x + y)**z) == ((x + y)**z,) def test_issue2027(): assert (-2)**x*(-3)**x != 6**x i = Symbol('i', integer=1) assert (-2)**i*(-3)**i == 6**i def test_Rational_as_content_primitive(): c, p = S(1), S(0) assert (c*p).as_content_primitive() == (c, p) c, p = S(1)/2, S(1) assert (c*p).as_content_primitive() == (c, p) def test_Add_as_content_primitive(): assert (x + 2).as_content_primitive() == (1, x + 2) assert (3*x + 2).as_content_primitive() == (1, 3*x + 2) assert (3*x + 3).as_content_primitive() == (3, x + 1) assert (3*x + 6).as_content_primitive() == (3, x + 2) assert (3*x + 2*y).as_content_primitive() == (1, 3*x + 2*y) assert (3*x + 3*y).as_content_primitive() == (3, x + y) assert (3*x + 6*y).as_content_primitive() == (3, x + 2*y) assert (3/x + 2*x*y*z**2).as_content_primitive() == (1, 3/x + 2*x*y*z**2) assert (3/x + 3*x*y*z**2).as_content_primitive() == (3, 1/x + x*y*z**2) assert (3/x + 6*x*y*z**2).as_content_primitive() == (3, 1/x + 2*x*y*z**2) assert (2*x/3 + 4*y/9).as_content_primitive() == \ (Rational(2, 9), 3*x + 2*y) assert (2*x/3 + 2.5*y).as_content_primitive() == \ (Rational(1, 3), 2*x + 7.5*y) # the coefficient may sort to a position other than 0 p = 3 + x + y assert (2*p).expand().as_content_primitive() == (2, p) assert (2.0*p).expand().as_content_primitive() == (1, 2.*p) p *= -1 assert (2*p).expand().as_content_primitive() == (2, p) def test_Mul_as_content_primitive(): assert (2*x).as_content_primitive() == (2, x) assert (x*(2 + 2*x)).as_content_primitive() == (2, x*(1 + x)) assert (x*(2 + 2*y)*(3*x + 3)**2).as_content_primitive() == \ (18, x*(1 + y)*(x + 1)**2) assert ((2 + 2*x)**2*(3 + 6*x) + S.Half).as_content_primitive() == \ (S.Half, 24*(x + 1)**2*(2*x + 1) + 1) def test_Pow_as_content_primitive(): assert (x**y).as_content_primitive() == (1, x**y) assert ((2*x + 2)**y).as_content_primitive() == \ (1, (Mul(2, (x + 1), evaluate=False))**y) assert ((2*x + 2)**3).as_content_primitive() == (8, (x + 1)**3) def test_issue2361(): u = Mul(2, (1 + x), evaluate=False) assert (2 + u).args == (2, u) def test_product_irrational(): from sympy import I, pi assert (I*pi).is_irrational is False # The following used to be deduced from the above bug: assert (I*pi).is_positive is False def test_issue_2820(): assert (x/(y*(1 + y))).expand() == x/(y**2 + y) def test_Mod(): assert Mod(x, 1).func is Mod assert pi % pi == S.Zero assert Mod(5, 3) == 2 assert Mod(-5, 3) == 1 assert Mod(5, -3) == -1 assert Mod(-5, -3) == -2 assert type(Mod(3.2, 2, evaluate=False)) == Mod assert 5 % x == Mod(5, x) assert x % 5 == Mod(x, 5) assert x % y == Mod(x, y) assert (x % y).subs({x: 5, y: 3}) == 2 # Float handling point3 = Float(3.3) % 1 assert (x - 3.3) % 1 == Mod(1.*x + 1 - point3, 1) assert Mod(-3.3, 1) == 1 - point3 assert Mod(0.7, 1) == Float(0.7) e = Mod(1.3, 1) point3 = Float._new(Float(.3)._mpf_, 51) assert e == point3 and e.is_Float e = Mod(1.3, .7) point6 = Float._new(Float(.6)._mpf_, 51) assert e == point6 and e.is_Float e = Mod(1.3, Rational(7, 10)) assert e == point6 and e.is_Float e = Mod(Rational(13, 10), 0.7) assert e == point6 and e.is_Float e = Mod(Rational(13, 10), Rational(7, 10)) assert e == .6 and e.is_Rational # check that sign is right r2 = sqrt(2) r3 = sqrt(3) for i in [-r3, -r2, r2, r3]: for j in [-r3, -r2, r2, r3]: assert test_numerically(i % j, i.n() % j.n()) for _x in range(4): for _y in range(9): reps = [(x, _x), (y, _y)] assert Mod(3*x + y, 9).subs(reps) == (3*_x + _y) % 9 # denesting # easy case assert Mod(Mod(x, y), y) == Mod(x, y) # in case someone attempts more denesting for i in [-3, -2, 2, 3]: for j in [-3, -2, 2, 3]: for k in range(3): # print i, j, k assert Mod(Mod(k, i), j) == (k % i) % j # known difference assert Mod(5*sqrt(2), sqrt(5)) == 5*sqrt(2) - 3*sqrt(5) p = symbols('p', positive=True) assert Mod(p + 1, p + 3) == p + 1 n = symbols('n', negative=True) assert Mod(n - 3, n - 1) == -2 assert Mod(n - 2*p, n - p) == -p assert Mod(p - 2*n, p - n) == -n # handling sums assert (x + 3) % 1 == Mod(x, 1) assert (x + 3.0) % 1 == Mod(1.*x, 1) assert (x - S(33)/10) % 1 == Mod(x + S(7)/10, 1) assert str(Mod(.6*x + y, .3*y)) == str(Mod(0.1*y + 0.6*x, 0.3*y)) assert (x + 1) % x == 1 % x assert (x + y) % x == y % x assert (x + y + 2) % x == (y + 2) % x assert (a + 3*x + 1) % (2*x) == Mod(a + x + 1, 2*x) assert (12*x + 18*y) % (3*x) == 3*Mod(6*y, x) # gcd extraction assert (-3*x) % (-2*y) == -Mod(3*x, 2*y) assert (.6*pi) % (.3*x*pi) == 0.3*pi*Mod(2, x) assert (.6*pi) % (.31*x*pi) == pi*Mod(0.6, 0.31*x) assert (6*pi) % (.3*x*pi) == pi*Mod(6, 0.3*x) assert (6*pi) % (.31*x*pi) == pi*Mod(6, 0.31*x) assert (6*pi) % (.42*x*pi) == pi*Mod(6, 0.42*x) assert (12*x) % (2*y) == 2*Mod(6*x, y) assert (12*x) % (3*5*y) == 3*Mod(4*x, 5*y) assert (12*x) % (15*x*y) == 3*x*Mod(4, 5*y) assert (-2*pi) % (3*pi) == pi assert (2*x + 2) % (x + 1) == 0 assert (x*(x + 1)) % (x + 1) == (x + 1)*Mod(x, 1) assert Mod(5.0*x, 0.1*y) == 0.1*Mod(50*x, y) i = Symbol('i', integer=True) assert (3*i*x) % (2*i*y) == i*Mod(3*x, 2*y) def test_issue_2902(): A = Symbol("A", commutative=False) eq = A + A**2 # it doesn't matter whether it's True or False; they should # just all be the same assert ( eq.is_commutative == (eq + 1).is_commutative == (A + 1).is_commutative) B = Symbol("B", commutative=False) # Although commutative terms could cancel we return True # meaning "there are non-commutative symbols; aftersubstitution # that definition can change, e.g. (A*B).subs(B,A**-1) -> 1 assert (sqrt(2)*A).is_commutative is False assert (sqrt(2)*A*B).is_commutative is False def test_polar(): from sympy import polar_lift p = Symbol('p', polar=True) x = Symbol('x') assert p.is_polar assert x.is_polar is None assert S(1).is_polar is None assert (p**x).is_polar is True assert (x**p).is_polar is None assert ((2*p)**x).is_polar is True assert (2*p).is_polar is True assert (-2*p).is_polar is not True assert (polar_lift(-2)*p).is_polar is True q = Symbol('q', polar=True) assert (p*q)**2 == p**2 * q**2 assert (2*q)**2 == 4 * q**2 assert ((p*q)**x).expand() == p**x * q**x def test_issue_2941(): a, b = Pow(1, 2, evaluate=False), S.One assert a != b assert b != a assert not (a == b) assert not (b == a) def test_issue_2983(): assert Max(x, 1) * Max(x, 2) == Max(x, 1) * Max(x, 2) def test_issue_2978(): assert x**2.0/x == x**1.0 assert x/x**2.0 == x**-1.0 assert x*x**2.0 == x**3.0 assert x**1.5*x**2.5 == x**4.0 assert 2**(2.0*x)/2**x == 2**(1.0*x) assert 2**x/2**(2.0*x) == 2**(-1.0*x) assert 2**x*2**(2.0*x) == 2**(3.0*x) assert 2**(1.5*x)*2**(2.5*x) == 2**(4.0*x) def test_mul_flatten_oo(): p = symbols('p', positive=True) n, m = symbols('n,m', negative=True) x_im = symbols('x_im', imaginary=True) assert n*oo == -oo assert n*m*oo == oo assert p*oo == oo assert x_im*oo != I*oo # i could be +/- 3*I -> +/-oo def test_issue_2061_2988_2990_2991(): #2988 assert ((-2*x*y**y)**3.2).n(2) == (2**3.2*(-x*y**y)**3.2).n(2) #2990 A, B, C = symbols('A,B,C', commutative=False) assert (2.*B*C)**3 == 8.0*(B*C)**3 assert (-2.*B*C)**3 == -8.0*(B*C)**3 assert (-2*B*C)**2 == 4*(B*C)**2 #2061 assert sqrt(-1.0*x) == 1.0*sqrt(-x) assert sqrt(1.0*x) == 1.0*sqrt(x) #2991 assert (-2*x*y*A*B)**2 == 4*x**2*y**2*(A*B)**2 def test_float_int(): assert int(float(sqrt(10))) == int(sqrt(10)) assert int(pi**1000) % 10 == 2 assert int(Float('1.123456789012345678901234567890e20', '')) == \ long(112345678901234567890) assert int(Float('1.123456789012345678901234567890e25', '')) == \ long(11234567890123456789012345) # decimal forces float so it's not an exact integer ending in 000000 assert int(Float('1.123456789012345678901234567890e35', '')) == \ 112345678901234567890123456789000192 assert int(Float('123456789012345678901234567890e5', '')) == \ 12345678901234567890123456789000000 assert Integer(Float('1.123456789012345678901234567890e20', '')) == \ 112345678901234567890 assert Integer(Float('1.123456789012345678901234567890e25', '')) == \ 11234567890123456789012345 # decimal forces float so it's not an exact integer ending in 000000 assert Integer(Float('1.123456789012345678901234567890e35', '')) == \ 112345678901234567890123456789000192 assert Integer(Float('123456789012345678901234567890e5', '')) == \ 12345678901234567890123456789000000 assert Float('123000e-2','') == Float('1230.00', '') assert Float('123000e2','') == Float('12300000', '') assert int(1 + Rational('.9999999999999999999999999')) == 1 assert int(pi/1e20) == 0 assert int(1 + pi/1e20) == 1 assert int(Add(1.2, -2, evaluate=False)) == int(1.2 - 2) assert int(Add(1.2, +2, evaluate=False)) == int(1.2 + 2) assert int(Add(1 + Float('.99999999999999999', ''), evaluate=False)) == 1 raises(TypeError, lambda: float(x)) raises(TypeError, lambda: float(sqrt(-1))) assert int(12345678901234567890 + cos(1)**2 + sin(1)**2) == \ 12345678901234567891 def test_issue_3512a(): assert Mul.flatten([3**Rational(1, 3), Pow(-Rational(1, 9), Rational(2, 3), evaluate=False)]) == \ ([Rational(1, 3), (-1)**Rational(2, 3)], [], None) def test_denest_add_mul(): # when working with evaluated expressions make sure they denest eq = x + 1 eq = Add(eq, 2, evaluate=False) eq = Add(eq, 2, evaluate=False) assert Add(*eq.args) == x + 5 eq = x*2 eq = Mul(eq, 2, evaluate=False) eq = Mul(eq, 2, evaluate=False) assert Mul(*eq.args) == 8*x # but don't let them denest unecessarily eq = Mul(-2, x - 2, evaluate=False) assert 2*eq == Mul(-4, x - 2, evaluate=False) assert -eq == Mul(2, x - 2, evaluate=False) sympy-0.7.4.1/sympy/core/tests/test_function.py0000644000175000017500000004676412253362407021776 0ustar georgeskgeorgeskfrom sympy import (Lambda, Symbol, Function, Derivative, Subs, sqrt, log, exp, Rational, Float, sin, cos, acos, diff, I, re, im, E, expand, pi, O, Sum, S, polygamma, loggamma, expint, Tuple, Dummy, Eq, Expr, symbols, nfloat) from sympy.utilities.pytest import XFAIL, raises from sympy.abc import t, w, x, y, z from sympy.core.function import PoleError from sympy.solvers import solve from sympy.utilities.iterables import subsets, variations f, g, h = symbols('f g h', cls=Function) def test_f_expand_complex(): x = Symbol('x', real=True) assert f(x).expand(complex=True) == I*im(f(x)) + re(f(x)) assert exp(x).expand(complex=True) == exp(x) assert exp(I*x).expand(complex=True) == cos(x) + I*sin(x) assert exp(z).expand(complex=True) == cos(im(z))*exp(re(z)) + \ I*sin(im(z))*exp(re(z)) def test_bug1(): e = sqrt(-log(w)) assert e.subs(log(w), -x) == sqrt(x) e = sqrt(-5*log(w)) assert e.subs(log(w), -x) == sqrt(5*x) def test_general_function(): nu = Function('nu') e = nu(x) edx = e.diff(x) edy = e.diff(y) edxdx = e.diff(x).diff(x) edxdy = e.diff(x).diff(y) assert e == nu(x) assert edx != nu(x) assert edx == diff(nu(x), x) assert edy == 0 assert edxdx == diff(diff(nu(x), x), x) assert edxdy == 0 def test_derivative_subs_bug(): e = diff(g(x), x) assert e.subs(g(x), f(x)) != e assert e.subs(g(x), f(x)) == Derivative(f(x), x) assert e.subs(g(x), -f(x)) == Derivative(-f(x), x) assert e.subs(x, y) == Derivative(g(y), y) def test_derivative_subs_self_bug(): d = diff(f(x), x) assert d.subs(d, y) == y def test_derivative_linearity(): assert diff(-f(x), x) == -diff(f(x), x) assert diff(8*f(x), x) == 8*diff(f(x), x) assert diff(8*f(x), x) != 7*diff(f(x), x) assert diff(8*f(x)*x, x) == 8*f(x) + 8*x*diff(f(x), x) assert diff(8*f(x)*y*x, x) == 8*y*f(x) + 8*y*x*diff(f(x), x) def test_derivative_evaluate(): assert Derivative(sin(x), x) != diff(sin(x), x) assert Derivative(sin(x), x).doit() == diff(sin(x), x) assert Derivative(Derivative(f(x), x), x) == diff(f(x), x, x) assert Derivative(sin(x), x, 0) == sin(x) def test_diff_symbols(): assert diff(f(x, y, z), x, y, z) == Derivative(f(x, y, z), x, y, z) assert diff(f(x, y, z), x, x, x) == Derivative(f(x, y, z), x, x, x) assert diff(f(x, y, z), x, 3) == Derivative(f(x, y, z), x, 3) # issue 1929 assert [diff(-z + x/y, sym) for sym in (z, x, y)] == [-1, 1/y, -x/y**2] assert diff(f(x, y, z), x, y, z, 2) == Derivative(f(x, y, z), x, y, z, z) assert diff(f(x, y, z), x, y, z, 2, evaluate=False) == \ Derivative(f(x, y, z), x, y, z, z) assert Derivative(f(x, y, z), x, y, z)._eval_derivative(z) == \ Derivative(f(x, y, z), x, y, z, z) assert Derivative(Derivative(f(x, y, z), x), y)._eval_derivative(z) == \ Derivative(f(x, y, z), x, y, z) def test_Lambda(): e = Lambda(x, x**2) assert e(4) == 16 assert e(x) == x**2 assert e(y) == y**2 assert Lambda(x, x**2) == Lambda(x, x**2) assert Lambda(x, x**2) == Lambda(y, y**2) assert Lambda(x, x**2) != Lambda(y, y**2 + 1) assert Lambda((x, y), x**y) == Lambda((y, x), y**x) assert Lambda((x, y), x**y) != Lambda((x, y), y**x) assert Lambda((x, y), x**y)(x, y) == x**y assert Lambda((x, y), x**y)(3, 3) == 3**3 assert Lambda((x, y), x**y)(x, 3) == x**3 assert Lambda((x, y), x**y)(3, y) == 3**y assert Lambda(x, f(x))(x) == f(x) assert Lambda(x, x**2)(e(x)) == x**4 assert e(e(x)) == x**4 assert Lambda((x, y), x + y).nargs == 2 p = x, y, z, t assert Lambda(p, t*(x + y + z))(*p) == t * (x + y + z) assert Lambda(x, 2*x) + Lambda(y, 2*y) == 2*Lambda(x, 2*x) assert Lambda(x, 2*x) not in [ Lambda(x, x) ] raises(ValueError, lambda: Lambda(1, x)) assert Lambda(x, 1)(1) is S.One def test_IdentityFunction(): assert Lambda(x, x) is Lambda(y, y) is S.IdentityFunction assert Lambda(x, 2*x) is not S.IdentityFunction assert Lambda((x, y), x) is not S.IdentityFunction def test_Lambda_symbols(): assert Lambda(x, 2*x).free_symbols == set() assert Lambda(x, x*y).free_symbols == set([y]) def test_Lambda_arguments(): raises(TypeError, lambda: Lambda(x, 2*x)(x, y)) raises(TypeError, lambda: Lambda((x, y), x + y)(x)) def test_Lambda_equality(): assert Lambda(x, 2*x) == Lambda(y, 2*y) # although variables are casts as Dummies, the expressions # should still compare equal assert Lambda((x, y), 2*x) == Lambda((x, y), 2*x) assert Lambda(x, 2*x) != Lambda((x, y), 2*x) assert Lambda(x, 2*x) != 2*x def test_Subs(): assert Subs(x, x, 0) == Subs(y, y, 0) assert Subs(x, x, 0).subs(x, 1) == Subs(x, x, 1) assert Subs(y, x, 0).subs(y, 1) == Subs(1, x, 0) assert Subs(f(x), x, 0).doit() == f(0) assert Subs(f(x**2), x**2, 0).doit() == f(0) assert Subs(f(x, y, z), (x, y, z), (0, 1, 1)) != \ Subs(f(x, y, z), (x, y, z), (0, 0, 1)) assert Subs(f(x, y), (x, y, z), (0, 1, 1)) == \ Subs(f(x, y), (x, y, z), (0, 1, 2)) assert Subs(f(x, y), (x, y, z), (0, 1, 1)) != \ Subs(f(x, y) + z, (x, y, z), (0, 1, 0)) assert Subs(f(x, y), (x, y), (0, 1)).doit() == f(0, 1) assert Subs(Subs(f(x, y), x, 0), y, 1).doit() == f(0, 1) raises(ValueError, lambda: Subs(f(x, y), (x, y), (0, 0, 1))) raises(ValueError, lambda: Subs(f(x, y), (x, x, y), (0, 0, 1))) assert len(Subs(f(x, y), (x, y), (0, 1)).variables) == 2 assert Subs(f(x, y), (x, y), (0, 1)).point == Tuple(0, 1) assert Subs(f(x), x, 0) == Subs(f(y), y, 0) assert Subs(f(x, y), (x, y), (0, 1)) == Subs(f(x, y), (y, x), (1, 0)) assert Subs(f(x)*y, (x, y), (0, 1)) == Subs(f(y)*x, (y, x), (0, 1)) assert Subs(f(x)*y, (x, y), (1, 1)) == Subs(f(y)*x, (x, y), (1, 1)) assert Subs(f(x), x, 0).subs(x, 1).doit() == f(1) assert Subs(f(x), x, y).subs(y, 0) == Subs(f(x), x, 0) assert Subs(y*f(x), x, y).subs(y, 2) == Subs(2*f(x), x, 2) assert (2 * Subs(f(x), x, 0)).subs(Subs(f(x), x, 0), y) == 2*y assert Subs(f(x), x, 0).free_symbols == set([]) assert Subs(f(x, y), x, z).free_symbols == set([y, z]) assert Subs(f(x).diff(x), x, 0).doit(), Subs(f(x).diff(x), x, 0) assert Subs(1 + f(x).diff(x), x, 0).doit(), 1 + Subs(f(x).diff(x), x, 0) assert Subs(y*f(x, y).diff(x), (x, y), (0, 2)).doit() == \ 2*Subs(Derivative(f(x, 2), x), x, 0) assert Subs(y**2*f(x), x, 0).diff(y) == 2*y*f(0) e = Subs(y**2*f(x), x, y) assert e.diff(y) == e.doit().diff(y) == y**2*Derivative(f(y), y) + 2*y*f(y) assert Subs(f(x), x, 0) + Subs(f(x), x, 0) == 2*Subs(f(x), x, 0) e1 = Subs(z*f(x), x, 1) e2 = Subs(z*f(y), y, 1) assert e1 + e2 == 2*e1 assert e1.__hash__() == e2.__hash__() assert Subs(z*f(x + 1), x, 1) not in [ e1, e2 ] assert Derivative( f(x), x).subs(x, g(x)) == Subs(Derivative(f(x), x), (x,), (g(x),)) assert Subs(f(x)*cos(y) + z, (x, y), (0, pi/3)).n(2) == \ Subs(f(x)*cos(y) + z, (x, y), (0, pi/3)).evalf(2) == \ z + Rational('1/2').n(2)*f(0) @XFAIL def test_Subs2(): # this reflects a limitation of subs(), probably won't fix assert Subs(f(x), x**2, x).doit() == f(sqrt(x)) def test_expand_function(): assert expand(x + y) == x + y assert expand(x + y, complex=True) == I*im(x) + I*im(y) + re(x) + re(y) assert expand((x + y)**11, modulus=11) == x**11 + y**11 def test_function_comparable(): assert sin(x).is_comparable is False assert cos(x).is_comparable is False assert sin(Float('0.1')).is_comparable is True assert cos(Float('0.1')).is_comparable is True assert sin(E).is_comparable is True assert cos(E).is_comparable is True assert sin(Rational(1, 3)).is_comparable is True assert cos(Rational(1, 3)).is_comparable is True @XFAIL def test_function_comparable_infinities(): assert sin(oo).is_comparable is False assert sin(-oo).is_comparable is False assert sin(zoo).is_comparable is False assert sin(nan).is_comparable is False def test_deriv1(): # These all requre derivatives evaluated at a point (issue 1620) to work. # See issue 1525 assert f(2*x).diff(x) == 2*Subs(Derivative(f(x), x), Tuple(x), Tuple(2*x)) assert (f(x)**3).diff(x) == 3*f(x)**2*f(x).diff(x) assert ( f(2*x)**3).diff(x) == 6*f(2*x)**2*Subs(Derivative(f(x), x), Tuple(x), Tuple(2*x)) assert f(2 + x).diff(x) == Subs(Derivative(f(x), x), Tuple(x), Tuple(x + 2)) assert f(2 + 3*x).diff(x) == 3*Subs(Derivative(f(x), x), Tuple(x), Tuple(3*x + 2)) assert f(3*sin(x)).diff(x) == 3*cos(x)*Subs(Derivative(f(x), x), Tuple(x), Tuple(3*sin(x))) def test_deriv2(): assert (x**3).diff(x) == 3*x**2 assert (x**3).diff(x, evaluate=False) != 3*x**2 assert (x**3).diff(x, evaluate=False) == Derivative(x**3, x) assert diff(x**3, x) == 3*x**2 assert diff(x**3, x, evaluate=False) != 3*x**2 assert diff(x**3, x, evaluate=False) == Derivative(x**3, x) def test_func_deriv(): assert f(x).diff(x) == Derivative(f(x), x) # issue 1435 assert f(x, y).diff(x, y) - f(x, y).diff(y, x) == 0 assert Derivative(f(x, y), x, y).args[1:] == (x, y) assert Derivative(f(x, y), y, x).args[1:] == (y, x) assert (Derivative(f(x, y), x, y) - Derivative(f(x, y), y, x)).doit() == 0 def test_suppressed_evaluation(): a = sin(0, evaluate=False) assert a != 0 assert a.func is sin assert a.args == (0,) def test_function_evalf(): def eq(a, b, eps): return abs(a - b) < eps assert eq(sin(1).evalf(15), Float("0.841470984807897"), 1e-13) assert eq( sin(2).evalf(25), Float("0.9092974268256816953960199", 25), 1e-23) assert eq(sin(1 + I).evalf( 15), Float("1.29845758141598") + Float("0.634963914784736")*I, 1e-13) assert eq(exp(1 + I).evalf(15), Float( "1.46869393991588") + Float("2.28735528717884239")*I, 1e-13) assert eq(exp(-0.5 + 1.5*I).evalf(15), Float( "0.0429042815937374") + Float("0.605011292285002")*I, 1e-13) assert eq(log(pi + sqrt(2)*I).evalf( 15), Float("1.23699044022052") + Float("0.422985442737893")*I, 1e-13) assert eq(cos(100).evalf(15), Float("0.86231887228768"), 1e-13) def test_extensibility_eval(): class MyFunc(Function): @classmethod def eval(cls, *args): return (0, 0, 0) assert MyFunc(0) == (0, 0, 0) def test_function_non_commutative(): x = Symbol('x', commutative=False) assert f(x).is_commutative is False assert sin(x).is_commutative is False assert exp(x).is_commutative is False assert log(x).is_commutative is False assert f(x).is_complex is False assert sin(x).is_complex is False assert exp(x).is_complex is False assert log(x).is_complex is False def test_function_complex(): x = Symbol('x', complex=True) assert f(x).is_commutative is True assert sin(x).is_commutative is True assert exp(x).is_commutative is True assert log(x).is_commutative is True assert f(x).is_complex is True assert sin(x).is_complex is True assert exp(x).is_complex is True assert log(x).is_complex is True def test_function__eval_nseries(): n = Symbol('n') assert sin(x)._eval_nseries(x, 2, None) == x + O(x**2) assert sin(x + 1)._eval_nseries(x, 2, None) == x*cos(1) + sin(1) + O(x**2) assert sin(pi*(1 - x))._eval_nseries(x, 2, None) == pi*x + O(x**2) assert acos(1 - x**2)._eval_nseries(x, 2, None) == sqrt(2)*x + O(x**2) assert polygamma(n, x + 1)._eval_nseries(x, 2, None) == \ polygamma(n, 1) + polygamma(n + 1, 1)*x + O(x**2) raises(PoleError, lambda: sin(1/x)._eval_nseries(x, 2, None)) raises(PoleError, lambda: acos(1 - x)._eval_nseries(x, 2, None)) raises(PoleError, lambda: acos(1 + x)._eval_nseries(x, 2, None)) assert loggamma(1/x)._eval_nseries(x, 0, None) == \ log(x)/2 - log(x)/x - 1/x + O(1, x) assert loggamma(log(1/x)).nseries(x, n=1, logx=y) == loggamma(-y) # issue 3626: assert expint(S(3)/2, -x)._eval_nseries(x, 5, None) == \ 2 - 2*sqrt(pi)*sqrt(-x) - 2*x - x**2/3 - x**3/15 - x**4/84 + O(x**5) assert sin(sqrt(x))._eval_nseries(x, 3, None) == \ sqrt(x) - x**(S(3)/2)/6 + x**(S(5)/2)/120 + O(x**3) def test_doit(): n = Symbol('n', integer=True) f = Sum(2 * n * x, (n, 1, 3)) d = Derivative(f, x) assert d.doit() == 12 assert d.doit(deep=False) == Sum(2*n, (n, 1, 3)) def test_evalf_default(): from sympy.functions.special.gamma_functions import polygamma assert type(sin(4.0)) == Float assert type(re(sin(I + 1.0))) == Float assert type(im(sin(I + 1.0))) == Float assert type(sin(4)) == sin assert type(polygamma(2.0, 4.0)) == Float assert type(sin(Rational(1, 4))) == sin def test_issue2300(): args = [x, y, S(2), S.Half] def ok(a): """Return True if the input args for diff are ok""" if not a: return False if a[0].is_Symbol is False: return False s_at = [i for i in range(len(a)) if a[i].is_Symbol] n_at = [i for i in range(len(a)) if not a[i].is_Symbol] # every symbol is followed by symbol or int # every number is followed by a symbol return (all(a[i + 1].is_Symbol or a[i + 1].is_Integer for i in s_at if i + 1 < len(a)) and all(a[i + 1].is_Symbol for i in n_at if i + 1 < len(a))) eq = x**10*y**8 for a in subsets(args): for v in variations(a, len(a)): if ok(v): noraise = eq.diff(*v) else: raises(ValueError, lambda: eq.diff(*v)) def test_derivative_numerically(): from random import random z0 = random() + I*random() assert abs(Derivative(sin(x), x).doit_numerically(z0) - cos(z0)) < 1e-15 def test_fdiff_argument_index_error(): from sympy.core.function import ArgumentIndexError class myfunc(Function): nargs = 1 def fdiff(self, idx): raise ArgumentIndexError mf = myfunc(x) assert mf.diff(x) == Derivative(mf, x) raises(TypeError, lambda: myfunc(x, x)) def test_deriv_wrt_function(): x = f(t) xd = diff(x, t) xdd = diff(xd, t) y = g(t) yd = diff(y, t) assert diff(x, t) == xd assert diff(2 * x + 4, t) == 2 * xd assert diff(2 * x + 4 + y, t) == 2 * xd + yd assert diff(2 * x + 4 + y * x, t) == 2 * xd + x * yd + xd * y assert diff(2 * x + 4 + y * x, x) == 2 + y assert (diff(4 * x**2 + 3 * x + x * y, t) == 3 * xd + x * yd + xd * y + 8 * x * xd) assert (diff(4 * x**2 + 3 * xd + x * y, t) == 3 * xdd + x * yd + xd * y + 8 * x * xd) assert diff(4 * x**2 + 3 * xd + x * y, xd) == 3 assert diff(4 * x**2 + 3 * xd + x * y, xdd) == 0 assert diff(sin(x), t) == xd * cos(x) assert diff(exp(x), t) == xd * exp(x) assert diff(sqrt(x), t) == xd / (2 * sqrt(x)) def test_diff_wrt_value(): assert Expr()._diff_wrt is False assert x._diff_wrt is True assert f(x)._diff_wrt is True assert Derivative(f(x), x)._diff_wrt is True assert Derivative(x**2, x)._diff_wrt is False def test_diff_wrt(): fx = f(x) dfx = diff(f(x), x) ddfx = diff(f(x), x, x) assert diff(sin(fx) + fx**2, fx) == cos(fx) + 2*fx assert diff(sin(dfx) + dfx**2, dfx) == cos(dfx) + 2*dfx assert diff(sin(ddfx) + ddfx**2, ddfx) == cos(ddfx) + 2*ddfx assert diff(fx**2, dfx) == 0 assert diff(fx**2, ddfx) == 0 assert diff(dfx**2, fx) == 0 assert diff(dfx**2, ddfx) == 0 assert diff(ddfx**2, dfx) == 0 assert diff(fx*dfx*ddfx, fx) == dfx*ddfx assert diff(fx*dfx*ddfx, dfx) == fx*ddfx assert diff(fx*dfx*ddfx, ddfx) == fx*dfx assert diff(f(x), x).diff(f(x)) == 0 assert (sin(f(x)) - cos(diff(f(x), x))).diff(f(x)) == cos(f(x)) assert diff(sin(fx), fx, x) == diff(sin(fx), x, fx) # Chain rule cases assert f(g(x)).diff(x) == \ Subs(Derivative(f(x), x), (x,), (g(x),))*Derivative(g(x), x) assert diff(f(g(x), h(x)), x) == \ Subs(Derivative(f(y, h(x)), y), (y,), (g(x),))*Derivative(g(x), x) + \ Subs(Derivative(f(g(x), y), y), (y,), (h(x),))*Derivative(h(x), x) assert f( sin(x)).diff(x) == Subs(Derivative(f(x), x), (x,), (sin(x),))*cos(x) assert diff(f(g(x)), g(x)) == Subs(Derivative(f(x), x), (x,), (g(x),)) def test_diff_wrt_func_subs(): assert f(g(x)).diff(x).subs(g, Lambda(x, 2*x)).doit() == f(2*x).diff(x) def test_diff_wrt_not_allowed(): raises(ValueError, lambda: diff(sin(x**2), x**2)) raises(ValueError, lambda: diff(exp(x*y), x*y)) raises(ValueError, lambda: diff(1 + x, 1 + x)) def test_klein_gordon_lagrangian(): m = Symbol('m') phi = f(x, t) L = -(diff(phi, t)**2 - diff(phi, x)**2 - m**2*phi**2)/2 eqna = Eq( diff(L, phi) - diff(L, diff(phi, x), x) - diff(L, diff(phi, t), t), 0) eqnb = Eq(diff(phi, t, t) - diff(phi, x, x) + m**2*phi, 0) assert eqna == eqnb def test_sho_lagrangian(): m = Symbol('m') k = Symbol('k') x = f(t) L = m*diff(x, t)**2/2 - k*x**2/2 eqna = Eq(diff(L, x), diff(L, diff(x, t), t)) eqnb = Eq(-k*x, m*diff(x, t, t)) assert eqna == eqnb assert diff(L, x, t) == diff(L, t, x) assert diff(L, diff(x, t), t) == m*diff(x, t, 2) assert diff(L, t, diff(x, t)) == -k*x + m*diff(x, t, 2) def test_straight_line(): F = f(x) Fd = F.diff(x) L = sqrt(1 + Fd**2) assert diff(L, F) == 0 assert diff(L, Fd) == Fd/sqrt(1 + Fd**2) def test_sort_variable(): vsort = Derivative._sort_variables assert vsort((x, y, z)) == [x, y, z] assert vsort((h(x), g(x), f(x))) == [f(x), g(x), h(x)] assert vsort((z, y, x, h(x), g(x), f(x))) == [x, y, z, f(x), g(x), h(x)] assert vsort((x, f(x), y, f(y))) == [x, f(x), y, f(y)] assert vsort((y, x, g(x), f(x), z, h(x), y, x)) == \ [x, y, f(x), g(x), z, h(x), x, y] assert vsort((z, y, f(x), x, f(x), g(x))) == [y, z, f(x), x, f(x), g(x)] assert 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] def test_unhandled(): class MyExpr(Expr): def _eval_derivative(self, s): if not s.name.startswith('xi'): return self else: return None expr = MyExpr(x, y, z) assert diff(expr, x, y, f(x), z) == Derivative(expr, f(x), z) assert diff(expr, f(x), x) == Derivative(expr, f(x), x) def test_issue_1612(): x = Symbol("x") assert Symbol('f')(x) == f(x) def test_nfloat(): from sympy.core.basic import _aresame from sympy.polys.rootoftools import RootOf x = Symbol("x") eq = x**(S(4)/3) + 4*x**(S(1)/3)/3 assert _aresame(nfloat(eq), x**(S(4)/3) + (4.0/3)*x**(S(1)/3)) assert _aresame(nfloat(eq, exponent=True), x**(4.0/3) + (4.0/3)*x**(1.0/3)) eq = x**(S(4)/3) + 4*x**(x/3)/3 assert _aresame(nfloat(eq), x**(S(4)/3) + (4.0/3)*x**(x/3)) big = 12345678901234567890 Float_big = Float(big) assert _aresame(nfloat(x**big, exponent=True), x**Float_big) assert _aresame(nfloat(big), Float_big) assert nfloat({x: sqrt(2)}) == {x: nfloat(sqrt(2))} assert nfloat({sqrt(2): x}) == {sqrt(2): x} assert nfloat(cos(x + sqrt(2))) == cos(x + nfloat(sqrt(2))) # issue 3243 f = S('x*lamda + lamda**3*(x/2 + 1/2) + lamda**2 + 1/4') assert not any(a.free_symbols for a in solve(f.subs(x, -0.139))) # issue 3533 assert nfloat(-100000*sqrt(2500000001) + 5000000001) == \ 9.99999999800000e-11 # issue 4023 eq = cos(3*x**4 + y)*RootOf(x**5 + 3*x**3 + 1, 0) assert str(nfloat(eq, exponent=False, n=1)) == '-0.7*cos(3.0*x**4 + y)' sympy-0.7.4.1/sympy/core/tests/test_eval_power.py0000644000175000017500000002510612253362407022277 0ustar georgeskgeorgeskfrom sympy.core import (Rational, Symbol, S, Float, Integer, Number, Pow, Basic, I, nan) from sympy.functions.elementary.miscellaneous import sqrt from sympy.functions.elementary.exponential import exp from sympy.functions.elementary.trigonometric import sin, cos from sympy.series.order import O from sympy.utilities.pytest import XFAIL, slow def test_rational(): a = Rational(1, 5) r = sqrt(5)/5 assert sqrt(a) == r assert 2*sqrt(a) == 2*r r = a*a**Rational(1, 2) assert a**Rational(3, 2) == r assert 2*a**Rational(3, 2) == 2*r r = a**5*a**Rational(2, 3) assert a**Rational(17, 3) == r assert 2 * a**Rational(17, 3) == 2*r def test_large_rational(): e = (Rational(123712**12 - 1, 7) + Rational(1, 7))**Rational(1, 3) assert e == 234232585392159195136 * (Rational(1, 7)**Rational(1, 3)) def test_negative_real(): def feq(a, b): return abs(a - b) < 1E-10 assert feq(S.One / Float(-0.5), -Integer(2)) def test_expand(): x = Symbol('x') assert (2**(-1 - x)).expand() == Rational(1, 2)*2**(-x) def test_issue350(): #test if powers are simplified correctly #see also issue 896 x = Symbol('x') assert ((x**Rational(1, 3))**Rational(2)) == x**Rational(2, 3) assert ( (x**Rational(3))**Rational(2, 5)) == (x**Rational(3))**Rational(2, 5) a = Symbol('a', real=True) b = Symbol('b', real=True) assert (a**2)**b == (abs(a)**b)**2 assert sqrt(1/a) != 1/sqrt(a) # e.g. for a = -1 assert (a**3)**Rational(1, 3) != a assert (x**a)**b != x**(a*b) # e.g. x = -1, a=2, b=1/2 assert (x**.5)**b == x**(.5*b) assert (x**.5)**.5 == x**.25 assert (x**2.5)**.5 != x**1.25 # e.g. for x = 5*I k = Symbol('k', integer=True) m = Symbol('m', integer=True) assert (x**k)**m == x**(k*m) assert Number(5)**Rational(2, 3) == Number(25)**Rational(1, 3) assert (x**.5)**2 == x**1.0 assert (x**2)**k == (x**k)**2 == x**(2*k) a = Symbol('a', positive=True) assert (a**3)**Rational(2, 5) == a**Rational(6, 5) assert (a**2)**b == (a**b)**2 assert (a**Rational(2, 3))**x == (a**(2*x/3)) != (a**x)**Rational(2, 3) def test_issue767(): assert --sqrt(sqrt(5) - 1) == sqrt(sqrt(5) - 1) def test_negative_one(): x = Symbol('x', complex=True) y = Symbol('y', complex=True) assert 1/x**y == x**(-y) def test_issue1263(): neg = Symbol('neg', negative=True) nonneg = Symbol('nonneg', nonnegative=True) any = Symbol('any') num, den = sqrt(1/neg).as_numer_denom() assert num == sqrt(-1) assert den == sqrt(-neg) num, den = sqrt(1/nonneg).as_numer_denom() assert num == 1 assert den == sqrt(nonneg) num, den = sqrt(1/any).as_numer_denom() assert num == sqrt(1/any) assert den == 1 def eqn(num, den, pow): return (num/den)**pow npos = 1 nneg = -1 dpos = 2 - sqrt(3) dneg = 1 - sqrt(3) assert dpos > 0 and dneg < 0 and npos > 0 and nneg < 0 # pos or neg integer eq = eqn(npos, dpos, 2) assert eq.is_Pow and eq.as_numer_denom() == (1, dpos**2) eq = eqn(npos, dneg, 2) assert eq.is_Pow and eq.as_numer_denom() == (1, dneg**2) eq = eqn(nneg, dpos, 2) assert eq.is_Pow and eq.as_numer_denom() == (1, dpos**2) eq = eqn(nneg, dneg, 2) assert eq.is_Pow and eq.as_numer_denom() == (1, dneg**2) eq = eqn(npos, dpos, -2) assert eq.is_Pow and eq.as_numer_denom() == (dpos**2, 1) eq = eqn(npos, dneg, -2) assert eq.is_Pow and eq.as_numer_denom() == (dneg**2, 1) eq = eqn(nneg, dpos, -2) assert eq.is_Pow and eq.as_numer_denom() == (dpos**2, 1) eq = eqn(nneg, dneg, -2) assert eq.is_Pow and eq.as_numer_denom() == (dneg**2, 1) # pos or neg rational pow = S.Half eq = eqn(npos, dpos, pow) assert eq.is_Pow and eq.as_numer_denom() == (npos**pow, dpos**pow) eq = eqn(npos, dneg, pow) assert eq.is_Pow and eq.as_numer_denom() == ((-npos)**pow, (-dneg)**pow) eq = eqn(nneg, dpos, pow) assert not eq.is_Pow or eq.as_numer_denom() == (nneg**pow, dpos**pow) eq = eqn(nneg, dneg, pow) assert eq.is_Pow and eq.as_numer_denom() == ((-nneg)**pow, (-dneg)**pow) eq = eqn(npos, dpos, -pow) assert eq.is_Pow and eq.as_numer_denom() == (dpos**pow, npos**pow) eq = eqn(npos, dneg, -pow) assert eq.is_Pow and eq.as_numer_denom() == ((-dneg)**pow, (-npos)**pow) eq = eqn(nneg, dpos, -pow) assert not eq.is_Pow or eq.as_numer_denom() == (dpos**pow, nneg**pow) eq = eqn(nneg, dneg, -pow) assert eq.is_Pow and eq.as_numer_denom() == ((-dneg)**pow, (-nneg)**pow) # unknown exponent pow = 2*any eq = eqn(npos, dpos, pow) assert eq.is_Pow and eq.as_numer_denom() == (npos**pow, dpos**pow) eq = eqn(npos, dneg, pow) assert eq.is_Pow and eq.as_numer_denom() == ((-npos)**pow, (-dneg)**pow) eq = eqn(nneg, dpos, pow) assert eq.is_Pow and eq.as_numer_denom() == (nneg**pow, dpos**pow) eq = eqn(nneg, dneg, pow) assert eq.is_Pow and eq.as_numer_denom() == ((-nneg)**pow, (-dneg)**pow) eq = eqn(npos, dpos, -pow) assert eq.as_numer_denom() == (dpos**pow, npos**pow) eq = eqn(npos, dneg, -pow) assert eq.is_Pow and eq.as_numer_denom() == ((-dneg)**pow, (-npos)**pow) eq = eqn(nneg, dpos, -pow) assert eq.is_Pow and eq.as_numer_denom() == (dpos**pow, nneg**pow) eq = eqn(nneg, dneg, -pow) assert eq.is_Pow and eq.as_numer_denom() == ((-dneg)**pow, (-nneg)**pow) x = Symbol('x') y = Symbol('y') assert ((1/(1 + x/3))**(-S.One)).as_numer_denom() == (3 + x, 3) notp = Symbol('notp', positive=False) # not positive does not imply real b = ((1 + x/notp)**-2) assert (b**(-y)).as_numer_denom() == (1, b**y) assert (b**(-S.One)).as_numer_denom() == ((notp + x)**2, notp**2) nonp = Symbol('nonp', nonpositive=True) assert (((1 + x/nonp)**-2)**(-S.One)).as_numer_denom() == ((-nonp - x)**2, nonp**2) n = Symbol('n', negative=True) assert (x**n).as_numer_denom() == (1, x**-n) assert sqrt(1/n).as_numer_denom() == (S.ImaginaryUnit, sqrt(-n)) n = Symbol('0 or neg', nonpositive=True) # if x and n are split up without negating each term and n is negative # then the answer might be wrong; if n is 0 it won't matter since # 1/oo and 1/zoo are both zero as is sqrt(0)/sqrt(-x) unless x is also # zero (in which case the negative sign doesn't matter): # 1/sqrt(1/-1) = -I but sqrt(-1)/sqrt(1) = I assert (1/sqrt(x/n)).as_numer_denom() == (sqrt(-n), sqrt(-x)) c = Symbol('c', complex=True) e = sqrt(1/c) assert e.as_numer_denom() == (e, 1) i = Symbol('i', integer=True) assert (((1 + x/y)**i)).as_numer_denom() == ((x + y)**i, y**i) def test_Pow_signs(): """Cf. issues 1496 and 2151""" x = Symbol('x') y = Symbol('y') n = Symbol('n', even=True) assert (3 - y)**2 != (y - 3)**2 assert (3 - y)**n != (y - 3)**n assert (-3 + y - x)**2 != (3 - y + x)**2 assert (y - 3)**3 != -(3 - y)**3 def test_power_with_noncommutative_mul_as_base(): x = Symbol('x', commutative=False) y = Symbol('y', commutative=False) assert not (x*y)**3 == x**3*y**3 assert (2*x*y)**3 == 8*(x*y)**3 def test_zero(): x = Symbol('x') y = Symbol('y') assert 0**x != 0 assert 0**(2*x) == 0**x assert 0**(1.0*x) == 0**x assert 0**(2.0*x) == 0**x assert (0**(2 - x)).as_base_exp() == (0, 2 - x) assert 0**(x - 2) != S.Infinity**(2 - x) assert 0**(2*x*y) == 0**(x*y) assert 0**(-2*x*y) == S.Infinity**(x*y) assert 0**I == nan i = Symbol('i', imaginary=True) assert 0**i == nan def test_pow_as_base_exp(): x = Symbol('x') assert (S.Infinity**(2 - x)).as_base_exp() == (S.Infinity, 2 - x) assert (S.Infinity**(x - 2)).as_base_exp() == (S.Infinity, x - 2) p = S.Half**x assert p.base, p.exp == p.as_base_exp() == (S(2), -x) def test_issue_3001(): x = Symbol('x') y = Symbol('y') assert x**1.0 == x assert x == x**1.0 assert True != x**1.0 assert x**1.0 is not True assert x is not True assert x*y == (x*y)**1.0 assert (x**1.0)**1.0 == x assert (x**1.0)**2.0 == x**2 b = Basic() assert Pow(b, 1.0, evaluate=False) == b # if the following gets distributed as a Mul (x**1.0*y**1.0 then # __eq__ methods could be added to Symbol and Pow to detect the # power-of-1.0 case. assert ((x*y)**1.0).func is Pow def test_issue_3109(): from sympy import root, Rational I = S.ImaginaryUnit assert sqrt(33**(9*I/10)) == -33**(9*I/20) assert root((6*I)**(2*I), 3).as_base_exp()[1] == Rational(1, 3) # != 2*I/3 assert root((6*I)**(I/3), 3).as_base_exp()[1] == I/9 assert sqrt(exp(3*I)) == exp(3*I/2) assert sqrt(-sqrt(3)*(1 + 2*I)) == sqrt(sqrt(3))*sqrt(-1 - 2*I) assert sqrt(exp(5*I)) == -exp(5*I/2) assert root(exp(5*I), 3).exp == Rational(1, 3) def test_issue_3891(): x = Symbol('x') a = Symbol('a') b = Symbol('b') assert (sqrt(a + b*x + x**2)).series(x, 0, 3).removeO() == \ b*x/(2*sqrt(a)) + x**2*(1/(2*sqrt(a)) - \ b**2/(8*a**(S(3)/2))) + sqrt(a) def test_issue_2969(): x = Symbol('x') assert sqrt(sin(x)).series(x, 0, 7) == \ sqrt(x) - x**(S(5)/2)/12 + x**(S(9)/2)/1440 - \ x**(S(13)/2)/24192 + O(x**7) assert sqrt(sin(x)).series(x, 0, 9) == \ sqrt(x) - x**(S(5)/2)/12 + x**(S(9)/2)/1440 - \ x**(S(13)/2)/24192 - 67*x**(S(17)/2)/29030400 + O(x**9) assert sqrt(sin(x**3)).series(x, 0, 19) == \ x**(S(3)/2) - x**(S(15)/2)/12 + x**(S(27)/2)/1440 + O(x**19) assert sqrt(sin(x**3)).series(x, 0, 20) == \ x**(S(3)/2) - x**(S(15)/2)/12 + x**(S(27)/2)/1440 - \ x**(S(39)/2)/24192 + O(x**20) def test_issue_3683(): x = Symbol('x') assert sqrt(sin(x**3)).series(x, 0, 7) == x**(S(3)/2) + O(x**7) assert sqrt(sin(x**4)).series(x, 0, 3) == x**2 + O(x**3) def test_issue_3554(): x = Symbol('x') assert (1 / sqrt(1 + cos(x) * sin(x**2))).series(x, 0, 7) == \ 1 - x**2/2 + 5*x**4/8 - 5*x**6/8 + O(x**7) assert (1 / sqrt(1 + cos(x) * sin(x**2))).series(x, 0, 8) == \ 1 - x**2/2 + 5*x**4/8 - 5*x**6/8 + O(x**8) @slow def test_issue_3554s(): x = Symbol('x') assert (1 / sqrt(1 + cos(x) * sin(x**2))).series(x, 0, 15) == \ 1 - x**2/2 + 5*x**4/8 - 5*x**6/8 + 4039*x**8/5760 - 5393*x**10/6720 + \ 13607537*x**12/14515200 - 532056047*x**14/479001600 + O(x**15) def test_issue_3330(): x = Symbol('x') c = Symbol('c') f = (c**2 + x)**(0.5) assert f.series(x, x0=0, n=1) == (c**2)**0.5 + O(x) assert f.taylor_term(0, x) == (c**2)**0.5 assert f.taylor_term(1, x) == 0.5*x*(c**2)**(-0.5) assert f.taylor_term(2, x) == -0.125*x**2*(c**2)**(-1.5) sympy-0.7.4.1/sympy/core/tests/test_relational.py0000644000175000017500000002130712253362407022265 0ustar georgeskgeorgeskfrom sympy.utilities.pytest import XFAIL, raises from sympy import Symbol, symbols, oo, I, pi, Float, And, Or, Not, Implies, Xor from sympy.core.relational import ( Relational, Equality, Unequality, GreaterThan, LessThan, StrictGreaterThan, StrictLessThan, Rel, Eq, Lt, Le, Gt, Ge, Ne ) x, y, z, t = symbols('x,y,z,t') def test_rel_ne(): assert Relational(x, y, '!=') == Ne(x, y) def test_rel_subs(): e = Relational(x, y, '==') e = e.subs(x, z) assert isinstance(e, Equality) assert e.lhs == z assert e.rhs == y e = Relational(x, y, '>=') e = e.subs(x, z) assert isinstance(e, GreaterThan) assert e.lhs == z assert e.rhs == y e = Relational(x, y, '<=') e = e.subs(x, z) assert isinstance(e, LessThan) assert e.lhs == z assert e.rhs == y e = Relational(x, y, '>') e = e.subs(x, z) assert isinstance(e, StrictGreaterThan) assert e.lhs == z assert e.rhs == y e = Relational(x, y, '<') e = e.subs(x, z) assert isinstance(e, StrictLessThan) assert e.lhs == z assert e.rhs == y e = Eq(x, 0) assert e.subs(x, 0) is True assert e.subs(x, 1) is False def test_wrappers(): e = x + x**2 res = Relational(y, e, '==') assert Rel(y, x + x**2, '==') == res assert Eq(y, x + x**2) == res res = Relational(y, e, '<') assert Lt(y, x + x**2) == res res = Relational(y, e, '<=') assert Le(y, x + x**2) == res res = Relational(y, e, '>') assert Gt(y, x + x**2) == res res = Relational(y, e, '>=') assert Ge(y, x + x**2) == res res = Relational(y, e, '!=') assert Ne(y, x + x**2) == res def test_Eq(): assert Eq(x**2) == Eq(x**2, 0) assert Eq(x**2) != Eq(x**2, 1) def test_rel_Infinity(): assert (oo > oo) is False assert (oo > -oo) is True assert (oo > 1) is True assert (oo < oo) is False assert (oo < -oo) is False assert (oo < 1) is False assert (oo >= oo) is True assert (oo >= -oo) is True assert (oo >= 1) is True assert (oo <= oo) is True assert (oo <= -oo) is False assert (oo <= 1) is False assert (-oo > oo) is False assert (-oo > -oo) is False assert (-oo > 1) is False assert (-oo < oo) is True assert (-oo < -oo) is False assert (-oo < 1) is True assert (-oo >= oo) is False assert (-oo >= -oo) is True assert (-oo >= 1) is False assert (-oo <= oo) is True assert (-oo <= -oo) is True assert (-oo <= 1) is True def test_bool(): assert Eq(0, 0) is True assert Eq(1, 0) is False assert Ne(0, 0) is False assert Ne(1, 0) is True assert Lt(0, 1) is True assert Lt(1, 0) is False assert Le(0, 1) is True assert Le(1, 0) is False assert Le(0, 0) is True assert Gt(1, 0) is True assert Gt(0, 1) is False assert Ge(1, 0) is True assert Ge(0, 1) is False assert Ge(1, 1) is True assert Eq(I, 2) is False assert Ne(I, 2) is True assert Gt(I, 2) not in [True, False] assert Ge(I, 2) not in [True, False] assert Lt(I, 2) not in [True, False] assert Le(I, 2) not in [True, False] a = Float('.000000000000000000001', '') b = Float('.0000000000000000000001', '') assert Eq(pi + a, pi + b) is False def test_rich_cmp(): assert (x < y) == Lt(x, y) assert (x <= y) == Le(x, y) assert (x > y) == Gt(x, y) assert (x >= y) == Ge(x, y) def test_doit(): from sympy import Symbol p = Symbol('p', positive=True) n = Symbol('n', negative=True) np = Symbol('np', nonpositive=True) nn = Symbol('nn', nonnegative=True) assert Gt(p, 0).doit() is True assert Gt(p, 1).doit() == Gt(p, 1) assert Ge(p, 0).doit() is True assert Le(p, 0).doit() is False assert Lt(n, 0).doit() is True assert Le(np, 0).doit() is True assert Gt(nn, 0).doit() == Gt(nn, 0) assert Lt(nn, 0).doit() is False assert Eq(x, 0).doit() == Eq(x, 0) def test_new_relational(): x = Symbol('x') assert Eq(x) == Relational(x, 0) # None ==> Equality assert Eq(x) == Relational(x, 0, '==') assert Eq(x) == Relational(x, 0, 'eq') assert Eq(x) == Equality(x, 0) assert Eq(x, -1) == Relational(x, -1) # None ==> Equality assert Eq(x, -1) == Relational(x, -1, '==') assert Eq(x, -1) == Relational(x, -1, 'eq') assert Eq(x, -1) == Equality(x, -1) assert Eq(x) != Relational(x, 1) # None ==> Equality assert Eq(x) != Relational(x, 1, '==') assert Eq(x) != Relational(x, 1, 'eq') assert Eq(x) != Equality(x, 1) assert Eq(x, -1) != Relational(x, 1) # None ==> Equality assert Eq(x, -1) != Relational(x, 1, '==') assert Eq(x, -1) != Relational(x, 1, 'eq') assert Eq(x, -1) != Equality(x, 1) assert Ne(x, 0) == Relational(x, 0, '!=') assert Ne(x, 0) == Relational(x, 0, '<>') assert Ne(x, 0) == Relational(x, 0, 'ne') assert Ne(x, 0) == Unequality(x, 0) assert Ne(x, 0) != Relational(x, 1, '!=') assert Ne(x, 0) != Relational(x, 1, '<>') assert Ne(x, 0) != Relational(x, 1, 'ne') assert Ne(x, 0) != Unequality(x, 1) assert Ge(x, 0) == Relational(x, 0, '>=') assert Ge(x, 0) == Relational(x, 0, 'ge') assert Ge(x, 0) == GreaterThan(x, 0) assert Ge(x, 1) != Relational(x, 0, '>=') assert Ge(x, 1) != Relational(x, 0, 'ge') assert Ge(x, 1) != GreaterThan(x, 0) assert (x >= 1) == Relational(x, 1, '>=') assert (x >= 1) == Relational(x, 1, 'ge') assert (x >= 1) == GreaterThan(x, 1) assert (x >= 0) != Relational(x, 1, '>=') assert (x >= 0) != Relational(x, 1, 'ge') assert (x >= 0) != GreaterThan(x, 1) assert Le(x, 0) == Relational(x, 0, '<=') assert Le(x, 0) == Relational(x, 0, 'le') assert Le(x, 0) == LessThan(x, 0) assert Le(x, 1) != Relational(x, 0, '<=') assert Le(x, 1) != Relational(x, 0, 'le') assert Le(x, 1) != LessThan(x, 0) assert (x <= 1) == Relational(x, 1, '<=') assert (x <= 1) == Relational(x, 1, 'le') assert (x <= 1) == LessThan(x, 1) assert (x <= 0) != Relational(x, 1, '<=') assert (x <= 0) != Relational(x, 1, 'le') assert (x <= 0) != LessThan(x, 1) assert Gt(x, 0) == Relational(x, 0, '>') assert Gt(x, 0) == Relational(x, 0, 'gt') assert Gt(x, 0) == StrictGreaterThan(x, 0) assert Gt(x, 1) != Relational(x, 0, '>') assert Gt(x, 1) != Relational(x, 0, 'gt') assert Gt(x, 1) != StrictGreaterThan(x, 0) assert (x > 1) == Relational(x, 1, '>') assert (x > 1) == Relational(x, 1, 'gt') assert (x > 1) == StrictGreaterThan(x, 1) assert (x > 0) != Relational(x, 1, '>') assert (x > 0) != Relational(x, 1, 'gt') assert (x > 0) != StrictGreaterThan(x, 1) assert Lt(x, 0) == Relational(x, 0, '<') assert Lt(x, 0) == Relational(x, 0, 'lt') assert Lt(x, 0) == StrictLessThan(x, 0) assert Lt(x, 1) != Relational(x, 0, '<') assert Lt(x, 1) != Relational(x, 0, 'lt') assert Lt(x, 1) != StrictLessThan(x, 0) assert (x < 1) == Relational(x, 1, '<') assert (x < 1) == Relational(x, 1, 'lt') assert (x < 1) == StrictLessThan(x, 1) assert (x < 0) != Relational(x, 1, '<') assert (x < 0) != Relational(x, 1, 'lt') assert (x < 0) != StrictLessThan(x, 1) # finally, some fuzz testing from random import randint from sympy.core.compatibility import unichr for i in range(100): while 1: strtype, length = (unichr, 65535) if randint(0, 1) else (chr, 255) relation_type = strtype( randint(0, length) ) if randint(0, 1): relation_type += strtype( randint(0, length) ) if relation_type not in ('==', 'eq', '!=', '<>', 'ne', '>=', 'ge', '<=', 'le', '>', 'gt', '<', 'lt'): break raises(ValueError, lambda: Relational(x, 1, relation_type)) def test_relational_bool_output(): # http://code.google.com/p/sympy/issues/detail?id=2832 raises(TypeError, lambda: bool(x > 3)) raises(TypeError, lambda: bool(x >= 3)) raises(TypeError, lambda: bool(x < 3)) raises(TypeError, lambda: bool(x <= 3)) raises(TypeError, lambda: bool(Eq(x, 3))) raises(TypeError, lambda: bool(Ne(x, 3))) def test_relational_logic_symbols(): # See issue 3105 assert (x < y) & (z < t) == And(x < y, z < t) assert (x < y) | (z < t) == Or(x < y, z < t) assert ~(x < y) == Not(x < y) assert (x < y) >> (z < t) == Implies(x < y, z < t) assert (x < y) << (z < t) == Implies(z < t, x < y) assert (x < y) ^ (z < t) == Xor(x < y, z < t) assert isinstance((x < y) & (z < t), And) assert isinstance((x < y) | (z < t), Or) assert isinstance(~(x < y), Not) assert isinstance((x < y) >> (z < t), Implies) assert isinstance((x < y) << (z < t), Implies) assert isinstance((x < y) ^ (z < t), (Or, Xor)) sympy-0.7.4.1/sympy/core/tests/test_facts.py0000644000175000017500000002455512253362407021243 0ustar georgeskgeorgeskfrom sympy.core.facts import (deduce_alpha_implications, apply_beta_to_alpha_route, rules_2prereq, FactRules, FactKB) from sympy.core.logic import And, Not from sympy.utilities.pytest import XFAIL, raises T = True F = False U = None def test_deduce_alpha_implications(): def D(i): I = deduce_alpha_implications(i) P = rules_2prereq(dict( ((k, True), set([(v, True) for v in S])) for k, S in I.items())) return I, P # transitivity I, P = D([('a', 'b'), ('b', 'c')]) assert I == {'a': set(['b', 'c']), 'b': set(['c'])} assert P == {'b': set(['a']), 'c': set(['a', 'b'])} # see if the output does not contain repeated implications I, P = D([('a', 'b'), ('b', 'c'), ('b', 'c')]) assert I == {'a': set(['b', 'c']), 'b': set(['c'])} assert P == {'b': set(['a']), 'c': set(['a', 'b'])} # see if it is tolerant to cycles assert D([('a', 'a'), ('a', 'a')]) == ({}, {}) assert D([('a', 'b'), ('b', 'a')]) == ( {'a': set(['b']), 'b': set(['a'])}, {'a': set(['b']), 'b': set(['a'])}) # see if it catches inconsistency raises(ValueError, lambda: D([('a', Not('a'))])) raises(ValueError, lambda: D([('a', 'b'), ('b', Not('a'))])) raises(ValueError, lambda: D([('a', 'b'), ('b', 'c'), ('b', 'na'), ('na', Not('a'))])) # something related to real-world I, P = D([('rat', 'real'), ('int', 'rat')]) assert I == {'int': set(['rat', 'real']), 'rat': set(['real'])} assert P == {'rat': set(['int']), 'real': set(['int', 'rat'])} # TODO move me to appropriate place def test_apply_beta_to_alpha_route(): APPLY = apply_beta_to_alpha_route # indicates empty alpha-chain with attached beta-rule #bidx def Q(bidx): return (set(), [bidx]) # x -> a &(a,b) -> x -- x -> a A = {'x': set(['a'])} B = [(And('a', 'b'), 'x')] assert APPLY(A, B) == {'x': (set(['a']), []), 'a': Q(0), 'b': Q(0)} # x -> a &(a,!x) -> b -- x -> a A = {'x': set(['a'])} B = [(And('a', Not('x')), 'b')] assert APPLY(A, B) == {'x': (set(['a']), []), Not('x'): Q(0), 'a': Q(0)} # x -> a b &(a,b) -> c -- x -> a b c A = {'x': set(['a', 'b'])} B = [(And('a', 'b'), 'c')] assert APPLY(A, B) == \ {'x': (set(['a', 'b', 'c']), []), 'a': Q(0), 'b': Q(0)} # x -> a &(a,b) -> y -- x -> a [#0] A = {'x': set(['a'])} B = [(And('a', 'b'), 'y')] assert APPLY(A, B) == {'x': (set(['a']), [0]), 'a': Q(0), 'b': Q(0)} # x -> a b c &(a,b) -> c -- x -> a b c A = {'x': set(['a', 'b', 'c'])} B = [(And('a', 'b'), 'c')] assert APPLY(A, B) == \ {'x': (set(['a', 'b', 'c']), []), 'a': Q(0), 'b': Q(0)} # x -> a b &(a,b,c) -> y -- x -> a b [#0] A = {'x': set(['a', 'b'])} B = [(And('a', 'b', 'c'), 'y')] assert APPLY(A, B) == \ {'x': (set(['a', 'b']), [0]), 'a': Q(0), 'b': Q(0), 'c': Q(0)} # x -> a b &(a,b) -> c -- x -> a b c d # c -> d c -> d A = {'x': set(['a', 'b']), 'c': set(['d'])} B = [(And('a', 'b'), 'c')] assert APPLY(A, B) == {'x': (set(['a', 'b', 'c', 'd']), []), 'c': (set(['d']), []), 'a': Q(0), 'b': Q(0)} # x -> a b &(a,b) -> c -- x -> a b c d e # c -> d &(c,d) -> e c -> d e A = {'x': set(['a', 'b']), 'c': set(['d'])} B = [(And('a', 'b'), 'c'), (And('c', 'd'), 'e')] assert APPLY(A, B) == {'x': (set(['a', 'b', 'c', 'd', 'e']), []), 'c': (set(['d', 'e']), []), 'a': Q(0), 'b': Q(0), 'd': Q(1)} # x -> a b &(a,y) -> z -- x -> a b y z # &(a,b) -> y A = {'x': set(['a', 'b'])} B = [(And('a', 'y'), 'z'), (And('a', 'b'), 'y')] assert APPLY(A, B) == {'x': (set(['a', 'b', 'y', 'z']), []), 'a': (set(), [0, 1]), 'y': Q(0), 'b': Q(1)} # x -> a b &(a,!b) -> c -- x -> a b A = {'x': set(['a', 'b'])} B = [(And('a', Not('b')), 'c')] assert APPLY(A, B) == \ {'x': (set(['a', 'b']), []), 'a': Q(0), Not('b'): Q(0)} # !x -> !a !b &(!a,b) -> c -- !x -> !a !b A = {Not('x'): set([Not('a'), Not('b')])} B = [(And(Not('a'), 'b'), 'c')] assert APPLY(A, B) == \ {Not('x'): (set([Not('a'), Not('b')]), []), Not('a'): Q(0), 'b': Q(0)} # x -> a b &(b,c) -> !a -- x -> a b A = {'x': set(['a', 'b'])} B = [(And('b', 'c'), Not('a'))] assert APPLY(A, B) == {'x': (set(['a', 'b']), []), 'b': Q(0), 'c': Q(0)} # x -> a b &(a, b) -> c -- x -> a b c p # c -> p a A = {'x': set(['a', 'b']), 'c': set(['p', 'a'])} B = [(And('a', 'b'), 'c')] assert APPLY(A, B) == {'x': (set(['a', 'b', 'c', 'p']), []), 'c': (set(['p', 'a']), []), 'a': Q(0), 'b': Q(0)} def test_FactRules_parse(): f = FactRules('a -> b') assert f.prereq == {'b': set(['a']), 'a': set(['b'])} f = FactRules('a -> !b') assert f.prereq == {'b': set(['a']), 'a': set(['b'])} f = FactRules('!a -> b') assert f.prereq == {'b': set(['a']), 'a': set(['b'])} f = FactRules('!a -> !b') assert f.prereq == {'b': set(['a']), 'a': set(['b'])} f = FactRules('!z == nz') assert f.prereq == {'z': set(['nz']), 'nz': set(['z'])} def test_FactRules_parse2(): raises(ValueError, lambda: FactRules('a -> !a')) def test_FactRules_deduce(): f = FactRules(['a -> b', 'b -> c', 'b -> d', 'c -> e']) def D(facts): kb = FactKB(f) kb.deduce_all_facts(facts) return kb assert D({'a': T}) == {'a': T, 'b': T, 'c': T, 'd': T, 'e': T} assert D({'b': T}) == { 'b': T, 'c': T, 'd': T, 'e': T} assert D({'c': T}) == { 'c': T, 'e': T} assert D({'d': T}) == { 'd': T } assert D({'e': T}) == { 'e': T} assert D({'a': F}) == {'a': F } assert D({'b': F}) == {'a': F, 'b': F } assert D({'c': F}) == {'a': F, 'b': F, 'c': F } assert D({'d': F}) == {'a': F, 'b': F, 'd': F } assert D({'a': U}) == {'a': U} # XXX ok? def test_FactRules_deduce2(): # pos/neg/zero, but the rules are not sufficient to derive all relations f = FactRules(['pos -> !neg', 'pos -> !z']) def D(facts): kb = FactKB(f) kb.deduce_all_facts(facts) return kb assert D({'pos': T}) == {'pos': T, 'neg': F, 'z': F} assert D({'pos': F}) == {'pos': F } assert D({'neg': T}) == {'pos': F, 'neg': T } assert D({'neg': F}) == { 'neg': F } assert D({'z': T}) == {'pos': F, 'z': T} assert D({'z': F}) == { 'z': F} # pos/neg/zero. rules are sufficient to derive all relations f = FactRules(['pos -> !neg', 'neg -> !pos', 'pos -> !z', 'neg -> !z']) assert D({'pos': T}) == {'pos': T, 'neg': F, 'z': F} assert D({'pos': F}) == {'pos': F } assert D({'neg': T}) == {'pos': F, 'neg': T, 'z': F} assert D({'neg': F}) == { 'neg': F } assert D({'z': T}) == {'pos': F, 'neg': F, 'z': T} assert D({'z': F}) == { 'z': F} def test_FactRules_deduce_multiple(): # deduction that involves _several_ starting points f = FactRules(['real == pos | npos']) def D(facts): kb = FactKB(f) kb.deduce_all_facts(facts) return kb assert D({'real': T}) == {'real': T} assert D({'real': F}) == {'real': F, 'pos': F, 'npos': F} assert D({'pos': T}) == {'real': T, 'pos': T} assert D({'npos': T}) == {'real': T, 'npos': T} # --- key tests below --- assert D({'pos': F, 'npos': F}) == {'real': F, 'pos': F, 'npos': F} assert D({'real': T, 'pos': F}) == {'real': T, 'pos': F, 'npos': T} assert D({'real': T, 'npos': F}) == {'real': T, 'pos': T, 'npos': F} assert D({'pos': T, 'npos': F}) == {'real': T, 'pos': T, 'npos': F} assert D({'pos': F, 'npos': T}) == {'real': T, 'pos': F, 'npos': T} def test_FactRules_deduce_multiple2(): f = FactRules(['real == neg | zero | pos']) def D(facts): kb = FactKB(f) kb.deduce_all_facts(facts) return kb assert D({'real': T}) == {'real': T} assert D({'real': F}) == {'real': F, 'neg': F, 'zero': F, 'pos': F} assert D({'neg': T}) == {'real': T, 'neg': T} assert D({'zero': T}) == {'real': T, 'zero': T} assert D({'pos': T}) == {'real': T, 'pos': T} # --- key tests below --- assert D({'neg': F, 'zero': F, 'pos': F}) == {'real': F, 'neg': F, 'zero': F, 'pos': F} assert D({'real': T, 'neg': F}) == {'real': T, 'neg': F} assert D({'real': T, 'zero': F}) == {'real': T, 'zero': F} assert D({'real': T, 'pos': F}) == {'real': T, 'pos': F} assert D({'real': T, 'zero': F, 'pos': F}) == {'real': T, 'neg': T, 'zero': F, 'pos': F} assert D({'real': T, 'neg': F, 'pos': F}) == {'real': T, 'neg': F, 'zero': T, 'pos': F} assert D({'real': T, 'neg': F, 'zero': F }) == {'real': T, 'neg': F, 'zero': F, 'pos': T} assert D({'neg': T, 'zero': F, 'pos': F}) == {'real': T, 'neg': T, 'zero': F, 'pos': F} assert D({'neg': F, 'zero': T, 'pos': F}) == {'real': T, 'neg': F, 'zero': T, 'pos': F} assert D({'neg': F, 'zero': F, 'pos': T}) == {'real': T, 'neg': F, 'zero': F, 'pos': T} def test_FactRules_deduce_base(): # deduction that starts from base f = FactRules(['real == neg | zero | pos', 'neg -> real & !zero & !pos', 'pos -> real & !zero & !neg']) base = FactKB(f) base.deduce_all_facts({'real': T, 'neg': F}) assert base == {'real': T, 'neg': F} base.deduce_all_facts({'zero': F}) assert base == {'real': T, 'neg': F, 'zero': F, 'pos': T} def test_FactRules_deduce_staticext(): # verify that static beta-extensions deduction takes place f = FactRules(['real == neg | zero | pos', 'neg -> real & !zero & !pos', 'pos -> real & !zero & !neg', 'nneg == real & !neg', 'npos == real & !pos']) assert ('npos', True) in f.full_implications[('neg', True)] assert ('nneg', True) in f.full_implications[('pos', True)] assert ('nneg', True) in f.full_implications[('zero', True)] assert ('npos', True) in f.full_implications[('zero', True)] sympy-0.7.4.1/sympy/core/tests/test_truediv.py0000644000175000017500000000147512253362407021621 0ustar georgeskgeorgeskfrom __future__ import division #this module tests that sympy works with true division turned on from sympy import Rational, Symbol, Float def test_truediv(): assert 1/2 != 0 assert Rational(1)/2 != 0 def dotest(s): x = Symbol("x") y = Symbol("y") l = [ Rational(2), Float("1.3"), x, y, pow(x, y)*y, 5, 5.5 ] for x in l: for y in l: s(x, y) return True def test_basic(): def s(a, b): x = a x = +a x = -a x = a + b x = a - b x = a*b x = a/b x = a**b assert dotest(s) def test_ibasic(): def s(a, b): x = a x += b x = a x -= b x = a x *= b x = a x /= b assert dotest(s) sympy-0.7.4.1/sympy/core/tests/test_cache.py0000644000175000017500000000066212253362407021177 0ustar georgeskgeorgeskfrom sympy.core.cache import cacheit def test_cacheit_doc(): @cacheit def testfn(): "test docstring" pass assert testfn.__doc__ == "test docstring" assert testfn.__name__ == "testfn" def test_cacheit_unhashable(): @cacheit def testit(x): return x assert testit(1) == 1 assert testit(1) == 1 a = {} assert testit(a) == {} a[1] = 2 assert testit(a) == {1: 2} sympy-0.7.4.1/sympy/core/tests/test_containers.py0000644000175000017500000001365012253362407022302 0ustar georgeskgeorgeskfrom sympy import Matrix, Tuple, symbols, sympify, Basic, Dict, S, FiniteSet, Integer from sympy.core.containers import tuple_wrapper from sympy.utilities.pytest import raises, XFAIL from sympy.core.compatibility import is_sequence, iterable, u def test_Tuple(): t = (1, 2, 3, 4) st = Tuple(*t) assert set(sympify(t)) == set(st) assert len(t) == len(st) assert set(sympify(t[:2])) == set(st[:2]) assert isinstance(st[:], Tuple) assert st == Tuple(1, 2, 3, 4) assert st.func(*st.args) == st p, q, r, s = symbols('p q r s') t2 = (p, q, r, s) st2 = Tuple(*t2) assert st2.atoms() == set(t2) assert st == st2.subs({p: 1, q: 2, r: 3, s: 4}) # issue 2406 assert all(isinstance(arg, Basic) for arg in st.args) assert Tuple(p, 1).subs(p, 0) == Tuple(0, 1) assert Tuple(p, Tuple(p, 1)).subs(p, 0) == Tuple(0, Tuple(0, 1)) assert Tuple(t2) == Tuple(Tuple(*t2)) assert Tuple.fromiter(t2) == Tuple(*t2) assert Tuple.fromiter(x for x in range(4)) == Tuple(0, 1, 2, 3) assert st2.fromiter(st2.args) == st2 def test_Tuple_contains(): t1, t2 = Tuple(1), Tuple(2) assert t1 in Tuple(1, 2, 3, t1, Tuple(t2)) assert t2 not in Tuple(1, 2, 3, t1, Tuple(t2)) def test_Tuple_concatenation(): assert Tuple(1, 2) + Tuple(3, 4) == Tuple(1, 2, 3, 4) assert (1, 2) + Tuple(3, 4) == Tuple(1, 2, 3, 4) assert Tuple(1, 2) + (3, 4) == Tuple(1, 2, 3, 4) raises(TypeError, lambda: Tuple(1, 2) + 3) raises(TypeError, lambda: 1 + Tuple(2, 3)) #the Tuple case in __radd__ is only reached when a subclass is involved class Tuple2(Tuple): def __radd__(self, other): return Tuple.__radd__(self, other + other) assert Tuple(1, 2) + Tuple2(3, 4) == Tuple(1, 2, 1, 2, 3, 4) assert Tuple2(1, 2) + Tuple(3, 4) == Tuple(1, 2, 3, 4) def test_Tuple_equality(): assert Tuple(1, 2) is not (1, 2) assert (Tuple(1, 2) == (1, 2)) is True assert (Tuple(1, 2) != (1, 2)) is False assert (Tuple(1, 2) == (1, 3)) is False assert (Tuple(1, 2) != (1, 3)) is True assert (Tuple(1, 2) == Tuple(1, 2)) is True assert (Tuple(1, 2) != Tuple(1, 2)) is False assert (Tuple(1, 2) == Tuple(1, 3)) is False assert (Tuple(1, 2) != Tuple(1, 3)) is True def test_Tuple_comparision(): assert (Tuple(1, 3) >= Tuple(-10, 30)) is True assert (Tuple(1, 3) <= Tuple(-10, 30)) is False assert (Tuple(1, 3) >= Tuple(1, 3)) is True assert (Tuple(1, 3) <= Tuple(1, 3)) is True def test_Tuple_tuple_count(): assert Tuple(0, 1, 2, 3).tuple_count(4) == 0 assert Tuple(0, 4, 1, 2, 3).tuple_count(4) == 1 assert Tuple(0, 4, 1, 4, 2, 3).tuple_count(4) == 2 assert Tuple(0, 4, 1, 4, 2, 4, 3).tuple_count(4) == 3 def test_Tuple_index(): assert Tuple(4, 0, 1, 2, 3).index(4) == 0 assert Tuple(0, 4, 1, 2, 3).index(4) == 1 assert Tuple(0, 1, 4, 2, 3).index(4) == 2 assert Tuple(0, 1, 2, 4, 3).index(4) == 3 assert Tuple(0, 1, 2, 3, 4).index(4) == 4 raises(ValueError, lambda: Tuple(0, 1, 2, 3).index(4)) raises(ValueError, lambda: Tuple(4, 0, 1, 2, 3).index(4, 1)) raises(ValueError, lambda: Tuple(0, 1, 2, 3, 4).index(4, 1, 4)) def test_Tuple_mul(): assert Tuple(1, 2, 3)*2 == Tuple(1, 2, 3, 1, 2, 3) assert 2*Tuple(1, 2, 3) == Tuple(1, 2, 3, 1, 2, 3) assert Tuple(1, 2, 3)*Integer(2) == Tuple(1, 2, 3, 1, 2, 3) assert Integer(2)*Tuple(1, 2, 3) == Tuple(1, 2, 3, 1, 2, 3) raises(TypeError, lambda: Tuple(1, 2, 3)*S.Half) raises(TypeError, lambda: S.Half*Tuple(1, 2, 3)) def test_tuple_wrapper(): @tuple_wrapper def wrap_tuples_and_return(*t): return t p = symbols('p') assert wrap_tuples_and_return(p, 1) == (p, 1) assert wrap_tuples_and_return((p, 1)) == (Tuple(p, 1),) assert wrap_tuples_and_return(1, (p, 2), 3) == (1, Tuple(p, 2), 3) def test_iterable_is_sequence(): ordered = [list(), tuple(), Tuple(), Matrix([[]])] unordered = [set()] not_sympy_iterable = [{}, '', u('')] assert all(is_sequence(i) for i in ordered) assert all(not is_sequence(i) for i in unordered) assert all(iterable(i) for i in ordered + unordered) assert all(not iterable(i) for i in not_sympy_iterable) assert all(iterable(i, exclude=None) for i in not_sympy_iterable) def test_Dict(): x, y, z = symbols('x y z') d = Dict({x: 1, y: 2, z: 3}) assert d[x] == 1 assert d[y] == 2 raises(KeyError, lambda: d[2]) assert len(d) == 3 assert set(d.keys()) == set((x, y, z)) assert set(d.values()) == set((S(1), S(2), S(3))) assert d.get(5, 'default') == 'default' assert x in d and z in d and not 5 in d assert d.has(x) and d.has(1) # SymPy Basic .has method # Test input types # input - a python dict # input - items as args - SymPy style assert (Dict({x: 1, y: 2, z: 3}) == Dict((x, 1), (y, 2), (z, 3))) raises(TypeError, lambda: Dict(((x, 1), (y, 2), (z, 3)))) with raises(NotImplementedError): d[5] = 6 # assert immutability assert set( d.items()) == set((Tuple(x, S(1)), Tuple(y, S(2)), Tuple(z, S(3)))) assert set(d) == set([x, y, z]) assert str(d) == '{x: 1, y: 2, z: 3}' assert d.__repr__() == '{x: 1, y: 2, z: 3}' # Test creating a Dict from a Dict. d = Dict({x: 1, y: 2, z: 3}) assert d == Dict(d) def test_issue_2689(): args = [(1, 2), (2, 1)] for o in [Dict, Tuple]: # __eq__ and arg handling if o != Tuple: assert o(*args) == o(*reversed(args)) pair = [o(*args), o(*reversed(args))] assert sorted(pair) == sorted(reversed(pair)) assert set(o(*args)) # doesn't fail @XFAIL def test_issue_2689b(): args = [(1, 2), (2, 1)] for o in [FiniteSet]: # __eq__ and arg handling if o != Tuple: assert o(*args) == o(*reversed(args)) pair = [o(*args), o(*reversed(args))] assert sorted(pair) == sorted(reversed(pair)) assert set(o(*args)) # doesn't fail sympy-0.7.4.1/sympy/core/tests/test_rules.py0000644000175000017500000000053712253362407021267 0ustar georgeskgeorgeskfrom sympy.core.rules import Transform from sympy.utilities.pytest import raises def test_Transform(): add1 = Transform(lambda x: x + 1, lambda x: x % 2 == 1) assert add1[1] == 2 assert (1 in add1) is True assert add1.get(1) == 2 raises(KeyError, lambda: add1[2]) assert (2 in add1) is False assert add1.get(2) is None sympy-0.7.4.1/sympy/core/tests/test_args.py0000644000175000017500000030020412253362407021063 0ustar georgeskgeorgesk"""Test whether all elements of cls.args are instances of Basic. """ # NOTE: keep tests sorted by (module, class name) key. If a class can't # be instantiated, add it here anyway with @SKIP("abstract class) (see # e.g. Function). import os import re import warnings from sympy import Basic, S, symbols, sqrt, sin, oo, Interval, exp from sympy.utilities.pytest import XFAIL, SKIP from sympy.utilities.exceptions import SymPyDeprecationWarning x, y, z = symbols('x,y,z') def test_all_classes_are_tested(): this = os.path.split(__file__)[0] path = os.path.join(this, os.pardir, os.pardir) sympy_path = os.path.abspath(path) prefix = os.path.split(sympy_path)[0] + os.sep re_cls = re.compile("^class ([A-Za-z][A-Za-z0-9_]*)\s*\(", re.MULTILINE) modules = {} # Ignore sympy.statistics import warning warnings.filterwarnings("ignore", message="sympy.statistics has been deprecated since SymPy 0.7.2", category=SymPyDeprecationWarning) for root, dirs, files in os.walk(sympy_path): module = root.replace(prefix, "").replace(os.sep, ".") for file in files: if file.startswith(("_", "test_", "bench_")): continue if not file.endswith(".py"): continue with open(os.path.join(root, file), "r") as f: text = f.read() submodule = module + '.' + file[:-3] names = re_cls.findall(text) if not names: continue try: mod = __import__(submodule, fromlist=names) except ImportError: continue def is_Basic(name): cls = getattr(mod, name) return issubclass(cls, Basic) names = list(filter(is_Basic, names)) if names: modules[submodule] = names ns = globals() failed = [] for module, names in modules.items(): mod = module.replace('.', '__') for name in names: test = 'test_' + mod + '__' + name if test not in ns: failed.append(module + '.' + name) # reset all SymPyDeprecationWarning into errors warnings.simplefilter("error", category=SymPyDeprecationWarning) assert not failed, "Missing classes: %s. Please add tests for these to sympy/core/tests/test_args.py." % ", ".join(failed) def _test_args(obj): return all(isinstance(arg, Basic) for arg in obj.args) def test_sympy__assumptions__assume__AppliedPredicate(): from sympy.assumptions.assume import AppliedPredicate, Predicate assert _test_args(AppliedPredicate(Predicate("test"), 2)) def test_sympy__assumptions__assume__Predicate(): from sympy.assumptions.assume import Predicate assert _test_args(Predicate("test")) @XFAIL def test_sympy__combinatorics__graycode__GrayCode(): from sympy.combinatorics.graycode import GrayCode # an integer is given and returned from GrayCode as the arg assert _test_args(GrayCode(3, start='100')) assert _test_args(GrayCode(3, rank=1)) def test_sympy__combinatorics__subsets__Subset(): from sympy.combinatorics.subsets import Subset assert _test_args(Subset([0, 1], [0, 1, 2, 3])) assert _test_args(Subset(['c', 'd'], ['a', 'b', 'c', 'd'])) @XFAIL def test_sympy__combinatorics__permutations__Permutation(): from sympy.combinatorics.permutations import Permutation assert _test_args(Permutation([0, 1, 2, 3])) @XFAIL def test_sympy__combinatorics__perm_groups__PermutationGroup(): from sympy.combinatorics.permutations import Permutation from sympy.combinatorics.perm_groups import PermutationGroup assert _test_args(PermutationGroup([Permutation([0, 1])])) def test_sympy__combinatorics__polyhedron__Polyhedron(): from sympy.combinatorics.permutations import Permutation from sympy.combinatorics.polyhedron import Polyhedron from sympy.abc import w, x, y, z 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]]), Permutation([[0, 1, 2, 3]])] corners = [w, x, y, z] faces = [(w, x, y), (w, y, z), (w, z, x), (x, y, z)] assert _test_args(Polyhedron(corners, faces, pgroup)) @XFAIL def test_sympy__combinatorics__prufer__Prufer(): from sympy.combinatorics.prufer import Prufer assert _test_args(Prufer([[0, 1], [0, 2], [0, 3]], 4)) def test_sympy__combinatorics__partitions__Partition(): from sympy.combinatorics.partitions import Partition assert _test_args(Partition([[1]])) @XFAIL def test_sympy__combinatorics__partitions__IntegerPartition(): from sympy.combinatorics.partitions import IntegerPartition assert _test_args(IntegerPartition([1])) def test_sympy__concrete__products__Product(): from sympy.concrete.products import Product assert _test_args(Product(x, (x, 0, 10))) assert _test_args(Product(x, (x, 0, y), (y, 0, 10))) @SKIP("abstract Class") def test_sympy__concrete__expr_with_limits__ExprWithLimits(): from sympy.concrete.expr_with_limits import ExprWithLimits assert _test_args(ExprWithLimits(x, (x, 0, 10))) assert _test_args(ExprWithLimits(x*y, (x, 0, 10.),(y,1.,3))) @SKIP("abstract Class") def test_sympy__concrete__expr_with_limits__AddWithLimits(): from sympy.concrete.expr_with_limits import AddWithLimits assert _test_args(AddWithLimits(x, (x, 0, 10))) assert _test_args(AddWithLimits(x*y, (x, 0, 10),(y,1,3))) @SKIP("abstract Class") def test_sympy__concrete__expr_with_intlimits__ExprWithIntLimits(): from sympy.concrete.expr_with_intlimits import ExprWithIntLimits assert _test_args(ExprWithIntLimits(x, (x, 0, 10))) assert _test_args(ExprWithIntLimits(x*y, (x, 0, 10),(y,1,3))) def test_sympy__concrete__summations__Sum(): from sympy.concrete.summations import Sum assert _test_args(Sum(x, (x, 0, 10))) assert _test_args(Sum(x, (x, 0, y), (y, 0, 10))) def test_sympy__core__add__Add(): from sympy.core.add import Add assert _test_args(Add(x, y, z, 2)) def test_sympy__core__basic__Atom(): from sympy.core.basic import Atom assert _test_args(Atom()) def test_sympy__core__basic__Basic(): from sympy.core.basic import Basic assert _test_args(Basic()) def test_sympy__core__containers__Dict(): from sympy.core.containers import Dict assert _test_args(Dict({x: y, y: z})) def test_sympy__core__containers__Tuple(): from sympy.core.containers import Tuple assert _test_args(Tuple(x, y, z, 2)) def test_sympy__core__expr__AtomicExpr(): from sympy.core.expr import AtomicExpr assert _test_args(AtomicExpr()) def test_sympy__core__expr__Expr(): from sympy.core.expr import Expr assert _test_args(Expr()) def test_sympy__core__function__Application(): from sympy.core.function import Application assert _test_args(Application(1, 2, 3)) def test_sympy__core__function__AppliedUndef(): from sympy.core.function import AppliedUndef assert _test_args(AppliedUndef(1, 2, 3)) def test_sympy__core__function__Derivative(): from sympy.core.function import Derivative assert _test_args(Derivative(2, x, y, 3)) @SKIP("abstract class") def test_sympy__core__function__Function(): pass def test_sympy__core__function__Lambda(): from sympy.core.function import Lambda assert _test_args(Lambda((x, y), x + y + z)) def test_sympy__core__function__Subs(): from sympy.core.function import Subs assert _test_args(Subs(x + y, x, 2)) def test_sympy__core__function__WildFunction(): from sympy.core.function import WildFunction assert _test_args(WildFunction('f')) def test_sympy__core__mod__Mod(): from sympy.core.mod import Mod assert _test_args(Mod(x, 2)) def test_sympy__core__mul__Mul(): from sympy.core.mul import Mul assert _test_args(Mul(2, x, y, z)) def test_sympy__core__numbers__Catalan(): from sympy.core.numbers import Catalan assert _test_args(Catalan()) def test_sympy__core__numbers__ComplexInfinity(): from sympy.core.numbers import ComplexInfinity assert _test_args(ComplexInfinity()) def test_sympy__core__numbers__EulerGamma(): from sympy.core.numbers import EulerGamma assert _test_args(EulerGamma()) def test_sympy__core__numbers__Exp1(): from sympy.core.numbers import Exp1 assert _test_args(Exp1()) def test_sympy__core__numbers__Float(): from sympy.core.numbers import Float assert _test_args(Float(1.23)) def test_sympy__core__numbers__GoldenRatio(): from sympy.core.numbers import GoldenRatio assert _test_args(GoldenRatio()) def test_sympy__core__numbers__Half(): from sympy.core.numbers import Half assert _test_args(Half()) def test_sympy__core__numbers__ImaginaryUnit(): from sympy.core.numbers import ImaginaryUnit assert _test_args(ImaginaryUnit()) def test_sympy__core__numbers__Infinity(): from sympy.core.numbers import Infinity assert _test_args(Infinity()) def test_sympy__core__numbers__Integer(): from sympy.core.numbers import Integer assert _test_args(Integer(7)) @SKIP("abstract class") def test_sympy__core__numbers__IntegerConstant(): pass def test_sympy__core__numbers__NaN(): from sympy.core.numbers import NaN assert _test_args(NaN()) def test_sympy__core__numbers__NegativeInfinity(): from sympy.core.numbers import NegativeInfinity assert _test_args(NegativeInfinity()) def test_sympy__core__numbers__NegativeOne(): from sympy.core.numbers import NegativeOne assert _test_args(NegativeOne()) def test_sympy__core__numbers__Number(): from sympy.core.numbers import Number assert _test_args(Number(1, 7)) def test_sympy__core__numbers__NumberSymbol(): from sympy.core.numbers import NumberSymbol assert _test_args(NumberSymbol()) def test_sympy__core__numbers__One(): from sympy.core.numbers import One assert _test_args(One()) def test_sympy__core__numbers__Pi(): from sympy.core.numbers import Pi assert _test_args(Pi()) def test_sympy__core__numbers__Rational(): from sympy.core.numbers import Rational assert _test_args(Rational(1, 7)) def test_sympy__core__numbers__RationalConstant(): from sympy.core.numbers import RationalConstant assert _test_args(RationalConstant()) def test_sympy__core__numbers__Zero(): from sympy.core.numbers import Zero assert _test_args(Zero()) @SKIP("abstract class") def test_sympy__core__operations__AssocOp(): pass @SKIP("abstract class") def test_sympy__core__operations__LatticeOp(): pass def test_sympy__core__power__Pow(): from sympy.core.power import Pow assert _test_args(Pow(x, 2)) def test_sympy__core__relational__Equality(): from sympy.core.relational import Equality assert _test_args(Equality(x, 2)) def test_sympy__core__relational__GreaterThan(): from sympy.core.relational import GreaterThan assert _test_args(GreaterThan(x, 2)) def test_sympy__core__relational__LessThan(): from sympy.core.relational import LessThan assert _test_args(LessThan(x, 2)) @SKIP("abstract class") def test_sympy__core__relational__Relational(): pass def test_sympy__core__relational__StrictGreaterThan(): from sympy.core.relational import StrictGreaterThan assert _test_args(StrictGreaterThan(x, 2)) def test_sympy__core__relational__StrictLessThan(): from sympy.core.relational import StrictLessThan assert _test_args(StrictLessThan(x, 2)) def test_sympy__core__relational__Unequality(): from sympy.core.relational import Unequality assert _test_args(Unequality(x, 2)) def test_sympy__core__sets__EmptySet(): from sympy.core.sets import EmptySet assert _test_args(EmptySet()) def test_sympy__core__sets__UniversalSet(): from sympy.core.sets import UniversalSet assert _test_args(UniversalSet()) def test_sympy__core__sets__FiniteSet(): from sympy.core.sets import FiniteSet assert _test_args(FiniteSet(x, y, z)) @XFAIL def test_sympy__core__sets__Interval(): from sympy.core.sets import Interval assert _test_args(Interval(0, 1)) def test_sympy__core__sets__ProductSet(): from sympy.core.sets import ProductSet, Interval assert _test_args(ProductSet(Interval(0, 1), Interval(0, 1))) @SKIP("does it make sense to test this?") def test_sympy__core__sets__Set(): from sympy.core.sets import Set assert _test_args(Set()) def test_sympy__core__sets__Intersection(): from sympy.core.sets import Intersection, Interval assert _test_args(Intersection(Interval(0, 3), Interval(2, 4), evaluate=False)) def test_sympy__core__sets__Union(): from sympy.core.sets import Union, Interval assert _test_args(Union(Interval(0, 1), Interval(2, 3))) def test_sympy__core__trace__Tr(): from sympy.core.trace import Tr a, b = symbols('a b') assert _test_args(Tr(a + b)) def test_sympy__sets__fancysets__Naturals(): from sympy.sets.fancysets import Naturals assert _test_args(Naturals()) def test_sympy__sets__fancysets__Naturals0(): from sympy.sets.fancysets import Naturals0 assert _test_args(Naturals0()) def test_sympy__sets__fancysets__Integers(): from sympy.sets.fancysets import Integers assert _test_args(Integers()) @XFAIL # This fails for the same reason Interval fails. Not all args are Basic def test_sympy__sets__fancysets__Reals(): from sympy.sets.fancysets import Reals assert _test_args(Reals()) def test_sympy__sets__fancysets__ImageSet(): from sympy.sets.fancysets import ImageSet from sympy import S, Lambda, Symbol x = Symbol('x') assert _test_args(ImageSet(Lambda(x, x**2), S.Naturals)) def test_sympy__sets__fancysets__Range(): from sympy.sets.fancysets import Range assert _test_args(Range(1, 5, 1)) # STATS from sympy.stats.crv_types import NormalDistribution nd = NormalDistribution(0, 1) from sympy.stats.frv_types import DieDistribution die = DieDistribution(6) def test_sympy__stats__crv__ContinuousDomain(): from sympy.stats.crv import ContinuousDomain assert _test_args(ContinuousDomain(set([x]), Interval(-oo, oo))) def test_sympy__stats__crv__SingleContinuousDomain(): from sympy.stats.crv import SingleContinuousDomain assert _test_args(SingleContinuousDomain(x, Interval(-oo, oo))) def test_sympy__stats__crv__ProductContinuousDomain(): from sympy.stats.crv import SingleContinuousDomain, ProductContinuousDomain D = SingleContinuousDomain(x, Interval(-oo, oo)) E = SingleContinuousDomain(y, Interval(0, oo)) assert _test_args(ProductContinuousDomain(D, E)) def test_sympy__stats__crv__ConditionalContinuousDomain(): from sympy.stats.crv import (SingleContinuousDomain, ConditionalContinuousDomain) D = SingleContinuousDomain(x, Interval(-oo, oo)) assert _test_args(ConditionalContinuousDomain(D, x > 0)) def test_sympy__stats__crv__ContinuousPSpace(): from sympy.stats.crv import ContinuousPSpace, SingleContinuousDomain D = SingleContinuousDomain(x, Interval(-oo, oo)) assert _test_args(ContinuousPSpace(D, nd)) def test_sympy__stats__crv__SingleContinuousPSpace(): from sympy.stats.crv import SingleContinuousPSpace assert _test_args(SingleContinuousPSpace(x, nd)) def test_sympy__stats__crv__ProductContinuousPSpace(): from sympy.stats.crv import ProductContinuousPSpace, SingleContinuousPSpace A = SingleContinuousPSpace(x, nd) B = SingleContinuousPSpace(y, nd) assert _test_args(ProductContinuousPSpace(A, B)) @SKIP("abstract class") def test_sympy__stats__crv__SingleContinuousDistribution(): pass def test_sympy__stats__drv__SingleDiscreteDomain(): from sympy.stats.drv import SingleDiscreteDomain assert _test_args(SingleDiscreteDomain(x, S.Naturals)) def test_sympy__stats__drv__SingleDiscretePSpace(): from sympy.stats.drv import SingleDiscretePSpace from sympy.stats.drv_types import PoissonDistribution assert _test_args(SingleDiscretePSpace(x, PoissonDistribution(1))) @SKIP("abstract class") def test_sympy__stats__drv__SingleDiscreteDistribution(): pass def test_sympy__stats__rv__RandomDomain(): from sympy.stats.rv import RandomDomain from sympy.core.sets import FiniteSet assert _test_args(RandomDomain(FiniteSet(x), FiniteSet(1, 2, 3))) def test_sympy__stats__rv__SingleDomain(): from sympy.stats.rv import SingleDomain from sympy.core.sets import FiniteSet assert _test_args(SingleDomain(x, FiniteSet(1, 2, 3))) def test_sympy__stats__rv__ConditionalDomain(): from sympy.stats.rv import ConditionalDomain, RandomDomain from sympy.core.sets import FiniteSet D = RandomDomain(FiniteSet(x), FiniteSet(1, 2)) assert _test_args(ConditionalDomain(D, x > 1)) def test_sympy__stats__rv__PSpace(): from sympy.stats.rv import PSpace, RandomDomain from sympy import Dict, FiniteSet D = RandomDomain(FiniteSet(x), FiniteSet(1, 2, 3, 4, 5, 6)) assert _test_args(PSpace(D, die)) @SKIP("abstract Class") def test_sympy__stats__rv__SinglePSpace(): pass def test_sympy__stats__rv__RandomSymbol(): from sympy.stats.rv import RandomSymbol from sympy.stats.crv import SingleContinuousPSpace A = SingleContinuousPSpace(x, nd) assert _test_args(RandomSymbol(A, x)) def test_sympy__stats__rv__ProductPSpace(): from sympy.stats.rv import ProductPSpace from sympy.stats.crv import SingleContinuousPSpace A = SingleContinuousPSpace(x, nd) B = SingleContinuousPSpace(y, nd) assert _test_args(ProductPSpace(A, B)) def test_sympy__stats__rv__ProductDomain(): from sympy.stats.rv import ProductDomain, SingleDomain D = SingleDomain(x, Interval(-oo, oo)) E = SingleDomain(y, Interval(0, oo)) assert _test_args(ProductDomain(D, E)) def test_sympy__stats__frv_types__DiscreteUniformDistribution(): from sympy.stats.frv_types import DiscreteUniformDistribution from sympy.core.containers import Tuple assert _test_args(DiscreteUniformDistribution(Tuple(*list(range(6))))) def test_sympy__stats__frv_types__DieDistribution(): from sympy.stats.frv_types import DieDistribution assert _test_args(DieDistribution(6)) def test_sympy__stats__frv_types__BernoulliDistribution(): from sympy.stats.frv_types import BernoulliDistribution assert _test_args(BernoulliDistribution(S.Half, 0, 1)) def test_sympy__stats__frv_types__BinomialDistribution(): from sympy.stats.frv_types import BinomialDistribution assert _test_args(BinomialDistribution(5, S.Half, 1, 0)) def test_sympy__stats__frv_types__HypergeometricDistribution(): from sympy.stats.frv_types import HypergeometricDistribution assert _test_args(HypergeometricDistribution(10, 5, 3)) def test_sympy__stats__frv__FiniteDomain(): from sympy.stats.frv import FiniteDomain assert _test_args(FiniteDomain(set([(x, 1), (x, 2)]))) # x can be 1 or 2 def test_sympy__stats__frv__SingleFiniteDomain(): from sympy.stats.frv import SingleFiniteDomain assert _test_args(SingleFiniteDomain(x, set([1, 2]))) # x can be 1 or 2 def test_sympy__stats__frv__ProductFiniteDomain(): from sympy.stats.frv import SingleFiniteDomain, ProductFiniteDomain xd = SingleFiniteDomain(x, set([1, 2])) yd = SingleFiniteDomain(y, set([1, 2])) assert _test_args(ProductFiniteDomain(xd, yd)) def test_sympy__stats__frv__ConditionalFiniteDomain(): from sympy.stats.frv import SingleFiniteDomain, ConditionalFiniteDomain xd = SingleFiniteDomain(x, set([1, 2])) assert _test_args(ConditionalFiniteDomain(xd, x > 1)) def test_sympy__stats__frv__FinitePSpace(): from sympy.stats.frv import FinitePSpace, SingleFiniteDomain xd = SingleFiniteDomain(x, set([1, 2, 3, 4, 5, 6])) p = 1.0/6 xd = SingleFiniteDomain(x, set([1, 2])) assert _test_args(FinitePSpace(xd, {(x, 1): S.Half, (x, 2): S.Half})) def test_sympy__stats__frv__SingleFinitePSpace(): from sympy.stats.frv import SingleFinitePSpace, SingleFiniteDomain from sympy import Symbol assert _test_args(SingleFinitePSpace(Symbol('x'), die)) def test_sympy__stats__frv__ProductFinitePSpace(): from sympy.stats.frv import (SingleFiniteDomain, SingleFinitePSpace, ProductFinitePSpace) from sympy import Symbol xp = SingleFinitePSpace(Symbol('x'), die) yp = SingleFinitePSpace(Symbol('y'), die) assert _test_args(ProductFinitePSpace(xp, yp)) @SKIP("abstract class") def test_sympy__stats__frv__SingleFiniteDistribution(): pass @SKIP("abstract class") def test_sympy__stats__crv__ContinuousDistribution(): pass def test_sympy__stats__frv_types__FiniteDistributionHandmade(): from sympy.stats.frv_types import FiniteDistributionHandmade assert _test_args(FiniteDistributionHandmade({1: 1})) def test_sympy__stats__crv__ContinuousDistributionHandmade(): from sympy.stats.crv import ContinuousDistributionHandmade from sympy import Symbol, Interval assert _test_args(ContinuousDistributionHandmade(Symbol('x'), Interval(0, 2))) def test_sympy__stats__rv__Density(): from sympy.stats.rv import Density from sympy.stats.crv_types import Normal assert _test_args(Density(Normal('x', 0, 1))) def test_sympy__stats__crv_types__ArcsinDistribution(): from sympy.stats.crv_types import ArcsinDistribution assert _test_args(ArcsinDistribution(0, 1)) def test_sympy__stats__crv_types__BeniniDistribution(): from sympy.stats.crv_types import BeniniDistribution assert _test_args(BeniniDistribution(1, 1, 1)) def test_sympy__stats__crv_types__BetaDistribution(): from sympy.stats.crv_types import BetaDistribution assert _test_args(BetaDistribution(1, 1)) def test_sympy__stats__crv_types__BetaPrimeDistribution(): from sympy.stats.crv_types import BetaPrimeDistribution assert _test_args(BetaPrimeDistribution(1, 1)) def test_sympy__stats__crv_types__CauchyDistribution(): from sympy.stats.crv_types import CauchyDistribution assert _test_args(CauchyDistribution(0, 1)) def test_sympy__stats__crv_types__ChiDistribution(): from sympy.stats.crv_types import ChiDistribution assert _test_args(ChiDistribution(1)) def test_sympy__stats__crv_types__ChiNoncentralDistribution(): from sympy.stats.crv_types import ChiNoncentralDistribution assert _test_args(ChiNoncentralDistribution(1,1)) def test_sympy__stats__crv_types__ChiSquaredDistribution(): from sympy.stats.crv_types import ChiSquaredDistribution assert _test_args(ChiSquaredDistribution(1)) def test_sympy__stats__crv_types__DagumDistribution(): from sympy.stats.crv_types import DagumDistribution assert _test_args(DagumDistribution(1, 1, 1)) def test_sympy__stats__crv_types__ExponentialDistribution(): from sympy.stats.crv_types import ExponentialDistribution assert _test_args(ExponentialDistribution(1)) def test_sympy__stats__crv_types__FDistributionDistribution(): from sympy.stats.crv_types import FDistributionDistribution assert _test_args(FDistributionDistribution(1, 1)) def test_sympy__stats__crv_types__FisherZDistribution(): from sympy.stats.crv_types import FisherZDistribution assert _test_args(FisherZDistribution(1, 1)) def test_sympy__stats__crv_types__FrechetDistribution(): from sympy.stats.crv_types import FrechetDistribution assert _test_args(FrechetDistribution(1, 1, 1)) def test_sympy__stats__crv_types__GammaInverseDistribution(): from sympy.stats.crv_types import GammaInverseDistribution assert _test_args(GammaInverseDistribution(1, 1)) def test_sympy__stats__crv_types__GammaDistribution(): from sympy.stats.crv_types import GammaDistribution assert _test_args(GammaDistribution(1, 1)) def test_sympy__stats__crv_types__KumaraswamyDistribution(): from sympy.stats.crv_types import KumaraswamyDistribution assert _test_args(KumaraswamyDistribution(1, 1)) def test_sympy__stats__crv_types__LaplaceDistribution(): from sympy.stats.crv_types import LaplaceDistribution assert _test_args(LaplaceDistribution(0, 1)) def test_sympy__stats__crv_types__LogisticDistribution(): from sympy.stats.crv_types import LogisticDistribution assert _test_args(LogisticDistribution(0, 1)) def test_sympy__stats__crv_types__LogNormalDistribution(): from sympy.stats.crv_types import LogNormalDistribution assert _test_args(LogNormalDistribution(0, 1)) def test_sympy__stats__crv_types__MaxwellDistribution(): from sympy.stats.crv_types import MaxwellDistribution assert _test_args(MaxwellDistribution(1)) def test_sympy__stats__crv_types__NakagamiDistribution(): from sympy.stats.crv_types import NakagamiDistribution assert _test_args(NakagamiDistribution(1, 1)) def test_sympy__stats__crv_types__NormalDistribution(): from sympy.stats.crv_types import NormalDistribution assert _test_args(NormalDistribution(0, 1)) def test_sympy__stats__crv_types__ParetoDistribution(): from sympy.stats.crv_types import ParetoDistribution assert _test_args(ParetoDistribution(1, 1)) def test_sympy__stats__crv_types__QuadraticUDistribution(): from sympy.stats.crv_types import QuadraticUDistribution assert _test_args(QuadraticUDistribution(1, 2)) def test_sympy__stats__crv_types__RaisedCosineDistribution(): from sympy.stats.crv_types import RaisedCosineDistribution assert _test_args(RaisedCosineDistribution(1, 1)) def test_sympy__stats__crv_types__RayleighDistribution(): from sympy.stats.crv_types import RayleighDistribution assert _test_args(RayleighDistribution(1)) def test_sympy__stats__crv_types__StudentTDistribution(): from sympy.stats.crv_types import StudentTDistribution assert _test_args(StudentTDistribution(1)) def test_sympy__stats__crv_types__TriangularDistribution(): from sympy.stats.crv_types import TriangularDistribution assert _test_args(TriangularDistribution(-1, 0, 1)) def test_sympy__stats__crv_types__UniformDistribution(): from sympy.stats.crv_types import UniformDistribution assert _test_args(UniformDistribution(0, 1)) def test_sympy__stats__crv_types__UniformSumDistribution(): from sympy.stats.crv_types import UniformSumDistribution assert _test_args(UniformSumDistribution(1)) def test_sympy__stats__crv_types__VonMisesDistribution(): from sympy.stats.crv_types import VonMisesDistribution assert _test_args(VonMisesDistribution(1, 1)) def test_sympy__stats__crv_types__WeibullDistribution(): from sympy.stats.crv_types import WeibullDistribution assert _test_args(WeibullDistribution(1, 1)) def test_sympy__stats__crv_types__WignerSemicircleDistribution(): from sympy.stats.crv_types import WignerSemicircleDistribution assert _test_args(WignerSemicircleDistribution(1)) def test_sympy__stats__drv_types__PoissonDistribution(): from sympy.stats.drv_types import PoissonDistribution assert _test_args(PoissonDistribution(1)) def test_sympy__stats__drv_types__GeometricDistribution(): from sympy.stats.drv_types import GeometricDistribution assert _test_args(GeometricDistribution(.5)) def test_sympy__core__symbol__Dummy(): from sympy.core.symbol import Dummy assert _test_args(Dummy('t')) def test_sympy__core__symbol__Symbol(): from sympy.core.symbol import Symbol assert _test_args(Symbol('t')) def test_sympy__core__symbol__Wild(): from sympy.core.symbol import Wild assert _test_args(Wild('x', exclude=[x])) @SKIP("abstract class") def test_sympy__functions__combinatorial__factorials__CombinatorialFunction(): pass def test_sympy__functions__combinatorial__factorials__FallingFactorial(): from sympy.functions.combinatorial.factorials import FallingFactorial assert _test_args(FallingFactorial(2, x)) def test_sympy__functions__combinatorial__factorials__MultiFactorial(): from sympy.functions.combinatorial.factorials import MultiFactorial assert _test_args(MultiFactorial(x)) def test_sympy__functions__combinatorial__factorials__RisingFactorial(): from sympy.functions.combinatorial.factorials import RisingFactorial assert _test_args(RisingFactorial(2, x)) def test_sympy__functions__combinatorial__factorials__binomial(): from sympy.functions.combinatorial.factorials import binomial assert _test_args(binomial(2, x)) def test_sympy__functions__combinatorial__factorials__subfactorial(): from sympy.functions.combinatorial.factorials import subfactorial assert _test_args(subfactorial(1)) def test_sympy__functions__combinatorial__factorials__factorial(): from sympy.functions.combinatorial.factorials import factorial assert _test_args(factorial(x)) def test_sympy__functions__combinatorial__factorials__factorial2(): from sympy.functions.combinatorial.factorials import factorial2 assert _test_args(factorial2(x)) def test_sympy__functions__combinatorial__numbers__bell(): from sympy.functions.combinatorial.numbers import bell assert _test_args(bell(x, y)) def test_sympy__functions__combinatorial__numbers__bernoulli(): from sympy.functions.combinatorial.numbers import bernoulli assert _test_args(bernoulli(x)) def test_sympy__functions__combinatorial__numbers__catalan(): from sympy.functions.combinatorial.numbers import catalan assert _test_args(catalan(x)) def test_sympy__functions__combinatorial__numbers__euler(): from sympy.functions.combinatorial.numbers import euler assert _test_args(euler(x)) def test_sympy__functions__combinatorial__numbers__fibonacci(): from sympy.functions.combinatorial.numbers import fibonacci assert _test_args(fibonacci(x)) def test_sympy__functions__combinatorial__numbers__harmonic(): from sympy.functions.combinatorial.numbers import harmonic assert _test_args(harmonic(x, 2)) def test_sympy__functions__combinatorial__numbers__lucas(): from sympy.functions.combinatorial.numbers import lucas assert _test_args(lucas(x)) def test_sympy__functions__elementary__complexes__Abs(): from sympy.functions.elementary.complexes import Abs assert _test_args(Abs(x)) def test_sympy__functions__elementary__complexes__adjoint(): from sympy.functions.elementary.complexes import adjoint assert _test_args(adjoint(x)) def test_sympy__functions__elementary__complexes__arg(): from sympy.functions.elementary.complexes import arg assert _test_args(arg(x)) def test_sympy__functions__elementary__complexes__conjugate(): from sympy.functions.elementary.complexes import conjugate assert _test_args(conjugate(x)) def test_sympy__functions__elementary__complexes__im(): from sympy.functions.elementary.complexes import im assert _test_args(im(x)) def test_sympy__functions__elementary__complexes__re(): from sympy.functions.elementary.complexes import re assert _test_args(re(x)) def test_sympy__functions__elementary__complexes__sign(): from sympy.functions.elementary.complexes import sign assert _test_args(sign(x)) def test_sympy__functions__elementary__complexes__polar_lift(): from sympy.functions.elementary.complexes import polar_lift assert _test_args(polar_lift(x)) def test_sympy__functions__elementary__complexes__periodic_argument(): from sympy.functions.elementary.complexes import periodic_argument assert _test_args(periodic_argument(x, y)) def test_sympy__functions__elementary__complexes__principal_branch(): from sympy.functions.elementary.complexes import principal_branch assert _test_args(principal_branch(x, y)) def test_sympy__functions__elementary__complexes__transpose(): from sympy.functions.elementary.complexes import transpose assert _test_args(transpose(x)) def test_sympy__functions__elementary__exponential__LambertW(): from sympy.functions.elementary.exponential import LambertW assert _test_args(LambertW(2)) @SKIP("abstract class") def test_sympy__functions__elementary__exponential__ExpBase(): pass def test_sympy__functions__elementary__exponential__exp(): from sympy.functions.elementary.exponential import exp assert _test_args(exp(2)) def test_sympy__functions__elementary__exponential__exp_polar(): from sympy.functions.elementary.exponential import exp_polar assert _test_args(exp_polar(2)) def test_sympy__functions__elementary__exponential__log(): from sympy.functions.elementary.exponential import log assert _test_args(log(2)) @SKIP("abstract class") def test_sympy__functions__elementary__hyperbolic__HyperbolicFunction(): pass def test_sympy__functions__elementary__hyperbolic__acosh(): from sympy.functions.elementary.hyperbolic import acosh assert _test_args(acosh(2)) def test_sympy__functions__elementary__hyperbolic__acoth(): from sympy.functions.elementary.hyperbolic import acoth assert _test_args(acoth(2)) def test_sympy__functions__elementary__hyperbolic__asinh(): from sympy.functions.elementary.hyperbolic import asinh assert _test_args(asinh(2)) def test_sympy__functions__elementary__hyperbolic__atanh(): from sympy.functions.elementary.hyperbolic import atanh assert _test_args(atanh(2)) def test_sympy__functions__elementary__hyperbolic__cosh(): from sympy.functions.elementary.hyperbolic import cosh assert _test_args(cosh(2)) def test_sympy__functions__elementary__hyperbolic__coth(): from sympy.functions.elementary.hyperbolic import coth assert _test_args(coth(2)) def test_sympy__functions__elementary__hyperbolic__sinh(): from sympy.functions.elementary.hyperbolic import sinh assert _test_args(sinh(2)) def test_sympy__functions__elementary__hyperbolic__tanh(): from sympy.functions.elementary.hyperbolic import tanh assert _test_args(tanh(2)) @SKIP("does this work at all?") def test_sympy__functions__elementary__integers__RoundFunction(): from sympy.functions.elementary.integers import RoundFunction assert _test_args(RoundFunction()) def test_sympy__functions__elementary__integers__ceiling(): from sympy.functions.elementary.integers import ceiling assert _test_args(ceiling(x)) def test_sympy__functions__elementary__integers__floor(): from sympy.functions.elementary.integers import floor assert _test_args(floor(x)) def test_sympy__functions__elementary__miscellaneous__IdentityFunction(): from sympy.functions.elementary.miscellaneous import IdentityFunction assert _test_args(IdentityFunction()) def test_sympy__functions__elementary__miscellaneous__Max(): from sympy.functions.elementary.miscellaneous import Max assert _test_args(Max(x, 2)) def test_sympy__functions__elementary__miscellaneous__Min(): from sympy.functions.elementary.miscellaneous import Min assert _test_args(Min(x, 2)) @SKIP("abstract class") def test_sympy__functions__elementary__miscellaneous__MinMaxBase(): pass def test_sympy__functions__elementary__piecewise__ExprCondPair(): from sympy.functions.elementary.piecewise import ExprCondPair assert _test_args(ExprCondPair(1, True)) def test_sympy__functions__elementary__piecewise__Piecewise(): from sympy.functions.elementary.piecewise import Piecewise assert _test_args(Piecewise((1, x >= 0), (0, True))) @SKIP("abstract class") def test_sympy__functions__elementary__trigonometric__TrigonometricFunction(): pass @SKIP("abstract class") def test_sympy__functions__elementary__trigonometric__ReciprocalTrigonometricFunction(): pass def test_sympy__functions__elementary__trigonometric__acos(): from sympy.functions.elementary.trigonometric import acos assert _test_args(acos(2)) def test_sympy__functions__elementary__trigonometric__acot(): from sympy.functions.elementary.trigonometric import acot assert _test_args(acot(2)) def test_sympy__functions__elementary__trigonometric__asin(): from sympy.functions.elementary.trigonometric import asin assert _test_args(asin(2)) def test_sympy__functions__elementary__trigonometric__atan(): from sympy.functions.elementary.trigonometric import atan assert _test_args(atan(2)) def test_sympy__functions__elementary__trigonometric__atan2(): from sympy.functions.elementary.trigonometric import atan2 assert _test_args(atan2(2, 3)) def test_sympy__functions__elementary__trigonometric__cos(): from sympy.functions.elementary.trigonometric import cos assert _test_args(cos(2)) def test_sympy__functions__elementary__trigonometric__csc(): from sympy.functions.elementary.trigonometric import csc assert _test_args(csc(2)) def test_sympy__functions__elementary__trigonometric__cot(): from sympy.functions.elementary.trigonometric import cot assert _test_args(cot(2)) def test_sympy__functions__elementary__trigonometric__sin(): assert _test_args(sin(2)) def test_sympy__functions__elementary__trigonometric__sec(): from sympy.functions.elementary.trigonometric import sec assert _test_args(sec(2)) def test_sympy__functions__elementary__trigonometric__tan(): from sympy.functions.elementary.trigonometric import tan assert _test_args(tan(2)) @SKIP("abstract class") def test_sympy__functions__special__bessel__BesselBase(): pass @SKIP("abstract class") def test_sympy__functions__special__bessel__SphericalBesselBase(): pass def test_sympy__functions__special__bessel__besseli(): from sympy.functions.special.bessel import besseli assert _test_args(besseli(x, 1)) def test_sympy__functions__special__bessel__besselj(): from sympy.functions.special.bessel import besselj assert _test_args(besselj(x, 1)) def test_sympy__functions__special__bessel__besselk(): from sympy.functions.special.bessel import besselk assert _test_args(besselk(x, 1)) def test_sympy__functions__special__bessel__bessely(): from sympy.functions.special.bessel import bessely assert _test_args(bessely(x, 1)) def test_sympy__functions__special__bessel__hankel1(): from sympy.functions.special.bessel import hankel1 assert _test_args(hankel1(x, 1)) def test_sympy__functions__special__bessel__hankel2(): from sympy.functions.special.bessel import hankel2 assert _test_args(hankel2(x, 1)) def test_sympy__functions__special__bessel__jn(): from sympy.functions.special.bessel import jn assert _test_args(jn(0, x)) def test_sympy__functions__special__bessel__yn(): from sympy.functions.special.bessel import yn assert _test_args(yn(0, x)) def test_sympy__functions__special__elliptic_integrals__elliptic_k(): from sympy.functions.special.elliptic_integrals import elliptic_k as K assert _test_args(K(x)) def test_sympy__functions__special__elliptic_integrals__elliptic_f(): from sympy.functions.special.elliptic_integrals import elliptic_f as F assert _test_args(F(x, y)) def test_sympy__functions__special__elliptic_integrals__elliptic_e(): from sympy.functions.special.elliptic_integrals import elliptic_e as E assert _test_args(E(x)) assert _test_args(E(x, y)) def test_sympy__functions__special__elliptic_integrals__elliptic_pi(): from sympy.functions.special.elliptic_integrals import elliptic_pi as P assert _test_args(P(x, y)) assert _test_args(P(x, y, z)) def test_sympy__functions__special__delta_functions__DiracDelta(): from sympy.functions.special.delta_functions import DiracDelta assert _test_args(DiracDelta(x, 1)) def test_sympy__functions__special__delta_functions__Heaviside(): from sympy.functions.special.delta_functions import Heaviside assert _test_args(Heaviside(x)) def test_sympy__functions__special__error_functions__erf(): from sympy.functions.special.error_functions import erf assert _test_args(erf(2)) def test_sympy__functions__special__error_functions__erfc(): from sympy.functions.special.error_functions import erfc assert _test_args(erfc(2)) def test_sympy__functions__special__error_functions__erfi(): from sympy.functions.special.error_functions import erfi assert _test_args(erfi(2)) def test_sympy__functions__special__error_functions__erf2(): from sympy.functions.special.error_functions import erf2 assert _test_args(erf2(2, 3)) def test_sympy__functions__special__error_functions__erfinv(): from sympy.functions.special.error_functions import erfinv assert _test_args(erfinv(2)) def test_sympy__functions__special__error_functions__erfcinv(): from sympy.functions.special.error_functions import erfcinv assert _test_args(erfcinv(2)) def test_sympy__functions__special__error_functions__erf2inv(): from sympy.functions.special.error_functions import erf2inv assert _test_args(erf2inv(2, 3)) @SKIP("abstract class") def test_sympy__functions__special__error_functions__FresnelIntegral(): pass def test_sympy__functions__special__error_functions__fresnels(): from sympy.functions.special.error_functions import fresnels assert _test_args(fresnels(2)) def test_sympy__functions__special__error_functions__fresnelc(): from sympy.functions.special.error_functions import fresnelc assert _test_args(fresnelc(2)) def test_sympy__functions__special__error_functions__erfs(): from sympy.functions.special.error_functions import _erfs assert _test_args(_erfs(2)) def test_sympy__functions__special__error_functions__Ei(): from sympy.functions.special.error_functions import Ei assert _test_args(Ei(2)) def test_sympy__functions__special__error_functions__li(): from sympy.functions.special.error_functions import li assert _test_args(li(2)) def test_sympy__functions__special__error_functions__Li(): from sympy.functions.special.error_functions import Li assert _test_args(Li(2)) @SKIP("abstract class") def test_sympy__functions__special__error_functions__TrigonometricIntegral(): pass def test_sympy__functions__special__error_functions__Si(): from sympy.functions.special.error_functions import Si assert _test_args(Si(2)) def test_sympy__functions__special__error_functions__Ci(): from sympy.functions.special.error_functions import Ci assert _test_args(Ci(2)) def test_sympy__functions__special__error_functions__Shi(): from sympy.functions.special.error_functions import Shi assert _test_args(Shi(2)) def test_sympy__functions__special__error_functions__Chi(): from sympy.functions.special.error_functions import Chi assert _test_args(Chi(2)) def test_sympy__functions__special__error_functions__expint(): from sympy.functions.special.error_functions import expint assert _test_args(expint(y, x)) def test_sympy__functions__special__gamma_functions__gamma(): from sympy.functions.special.gamma_functions import gamma assert _test_args(gamma(x)) def test_sympy__functions__special__gamma_functions__loggamma(): from sympy.functions.special.gamma_functions import loggamma assert _test_args(loggamma(2)) def test_sympy__functions__special__gamma_functions__lowergamma(): from sympy.functions.special.gamma_functions import lowergamma assert _test_args(lowergamma(x, 2)) def test_sympy__functions__special__gamma_functions__polygamma(): from sympy.functions.special.gamma_functions import polygamma assert _test_args(polygamma(x, 2)) def test_sympy__functions__special__gamma_functions__uppergamma(): from sympy.functions.special.gamma_functions import uppergamma assert _test_args(uppergamma(x, 2)) @SKIP("abstract class") def test_sympy__functions__special__hyper__TupleParametersBase(): pass @SKIP("abstract class") def test_sympy__functions__special__hyper__TupleArg(): pass def test_sympy__functions__special__hyper__hyper(): from sympy.functions.special.hyper import hyper assert _test_args(hyper([1, 2, 3], [4, 5], x)) def test_sympy__functions__special__hyper__meijerg(): from sympy.functions.special.hyper import meijerg assert _test_args(meijerg([1, 2, 3], [4, 5], [6], [], x)) @SKIP("abstract class") def test_sympy__functions__special__hyper__HyperRep(): pass def test_sympy__functions__special__hyper__HyperRep_power1(): from sympy.functions.special.hyper import HyperRep_power1 assert _test_args(HyperRep_power1(x, y)) def test_sympy__functions__special__hyper__HyperRep_power2(): from sympy.functions.special.hyper import HyperRep_power2 assert _test_args(HyperRep_power2(x, y)) def test_sympy__functions__special__hyper__HyperRep_log1(): from sympy.functions.special.hyper import HyperRep_log1 assert _test_args(HyperRep_log1(x)) def test_sympy__functions__special__hyper__HyperRep_atanh(): from sympy.functions.special.hyper import HyperRep_atanh assert _test_args(HyperRep_atanh(x)) def test_sympy__functions__special__hyper__HyperRep_asin1(): from sympy.functions.special.hyper import HyperRep_asin1 assert _test_args(HyperRep_asin1(x)) def test_sympy__functions__special__hyper__HyperRep_asin2(): from sympy.functions.special.hyper import HyperRep_asin2 assert _test_args(HyperRep_asin2(x)) def test_sympy__functions__special__hyper__HyperRep_sqrts1(): from sympy.functions.special.hyper import HyperRep_sqrts1 assert _test_args(HyperRep_sqrts1(x, y)) def test_sympy__functions__special__hyper__HyperRep_sqrts2(): from sympy.functions.special.hyper import HyperRep_sqrts2 assert _test_args(HyperRep_sqrts2(x, y)) def test_sympy__functions__special__hyper__HyperRep_log2(): from sympy.functions.special.hyper import HyperRep_log2 assert _test_args(HyperRep_log2(x)) def test_sympy__functions__special__hyper__HyperRep_cosasin(): from sympy.functions.special.hyper import HyperRep_cosasin assert _test_args(HyperRep_cosasin(x, y)) def test_sympy__functions__special__hyper__HyperRep_sinasin(): from sympy.functions.special.hyper import HyperRep_sinasin assert _test_args(HyperRep_sinasin(x, y)) @SKIP("abstract class") def test_sympy__functions__special__polynomials__OrthogonalPolynomial(): pass def test_sympy__functions__special__polynomials__jacobi(): from sympy.functions.special.polynomials import jacobi assert _test_args(jacobi(x, 2, 2, 2)) def test_sympy__functions__special__polynomials__gegenbauer(): from sympy.functions.special.polynomials import gegenbauer assert _test_args(gegenbauer(x, 2, 2)) def test_sympy__functions__special__polynomials__chebyshevt(): from sympy.functions.special.polynomials import chebyshevt assert _test_args(chebyshevt(x, 2)) def test_sympy__functions__special__polynomials__chebyshevt_root(): from sympy.functions.special.polynomials import chebyshevt_root assert _test_args(chebyshevt_root(3, 2)) def test_sympy__functions__special__polynomials__chebyshevu(): from sympy.functions.special.polynomials import chebyshevu assert _test_args(chebyshevu(x, 2)) def test_sympy__functions__special__polynomials__chebyshevu_root(): from sympy.functions.special.polynomials import chebyshevu_root assert _test_args(chebyshevu_root(3, 2)) def test_sympy__functions__special__polynomials__hermite(): from sympy.functions.special.polynomials import hermite assert _test_args(hermite(x, 2)) def test_sympy__functions__special__polynomials__legendre(): from sympy.functions.special.polynomials import legendre assert _test_args(legendre(x, 2)) def test_sympy__functions__special__polynomials__assoc_legendre(): from sympy.functions.special.polynomials import assoc_legendre assert _test_args(assoc_legendre(x, 0, y)) def test_sympy__functions__special__polynomials__laguerre(): from sympy.functions.special.polynomials import laguerre assert _test_args(laguerre(x, 2)) def test_sympy__functions__special__polynomials__assoc_laguerre(): from sympy.functions.special.polynomials import assoc_laguerre assert _test_args(assoc_laguerre(x, 0, y)) def test_sympy__functions__special__spherical_harmonics__Ynm(): from sympy.functions.special.spherical_harmonics import Ynm assert _test_args(Ynm(1, 1, x, y)) def test_sympy__functions__special__spherical_harmonics__Znm(): from sympy.functions.special.spherical_harmonics import Znm assert _test_args(Znm(1, 1, x, y)) def test_sympy__functions__special__tensor_functions__LeviCivita(): from sympy.functions.special.tensor_functions import LeviCivita assert _test_args(LeviCivita(x, y, 2)) def test_sympy__functions__special__tensor_functions__KroneckerDelta(): from sympy.functions.special.tensor_functions import KroneckerDelta assert _test_args(KroneckerDelta(x, y)) def test_sympy__functions__special__zeta_functions__dirichlet_eta(): from sympy.functions.special.zeta_functions import dirichlet_eta assert _test_args(dirichlet_eta(x)) def test_sympy__functions__special__zeta_functions__zeta(): from sympy.functions.special.zeta_functions import zeta assert _test_args(zeta(101)) def test_sympy__functions__special__zeta_functions__lerchphi(): from sympy.functions.special.zeta_functions import lerchphi assert _test_args(lerchphi(x, y, z)) def test_sympy__functions__special__zeta_functions__polylog(): from sympy.functions.special.zeta_functions import polylog assert _test_args(polylog(x, y)) def test_sympy__integrals__integrals__Integral(): from sympy.integrals.integrals import Integral assert _test_args(Integral(2, (x, 0, 1))) def test_sympy__integrals__risch__NonElementaryIntegral(): from sympy.integrals.risch import NonElementaryIntegral assert _test_args(NonElementaryIntegral(exp(-x**2), x)) @SKIP("abstract class") def test_sympy__integrals__transforms__IntegralTransform(): pass def test_sympy__integrals__transforms__MellinTransform(): from sympy.integrals.transforms import MellinTransform assert _test_args(MellinTransform(2, x, y)) def test_sympy__integrals__transforms__InverseMellinTransform(): from sympy.integrals.transforms import InverseMellinTransform assert _test_args(InverseMellinTransform(2, x, y, 0, 1)) def test_sympy__integrals__transforms__LaplaceTransform(): from sympy.integrals.transforms import LaplaceTransform assert _test_args(LaplaceTransform(2, x, y)) def test_sympy__integrals__transforms__InverseLaplaceTransform(): from sympy.integrals.transforms import InverseLaplaceTransform assert _test_args(InverseLaplaceTransform(2, x, y, 0)) @SKIP("abstract class") def test_sympy__integrals__transforms__FourierTypeTransform(): pass def test_sympy__integrals__transforms__InverseFourierTransform(): from sympy.integrals.transforms import InverseFourierTransform assert _test_args(InverseFourierTransform(2, x, y)) def test_sympy__integrals__transforms__FourierTransform(): from sympy.integrals.transforms import FourierTransform assert _test_args(FourierTransform(2, x, y)) @SKIP("abstract class") def test_sympy__integrals__transforms__SineCosineTypeTransform(): pass def test_sympy__integrals__transforms__InverseSineTransform(): from sympy.integrals.transforms import InverseSineTransform assert _test_args(InverseSineTransform(2, x, y)) def test_sympy__integrals__transforms__SineTransform(): from sympy.integrals.transforms import SineTransform assert _test_args(SineTransform(2, x, y)) def test_sympy__integrals__transforms__InverseCosineTransform(): from sympy.integrals.transforms import InverseCosineTransform assert _test_args(InverseCosineTransform(2, x, y)) def test_sympy__integrals__transforms__CosineTransform(): from sympy.integrals.transforms import CosineTransform assert _test_args(CosineTransform(2, x, y)) @SKIP("abstract class") def test_sympy__integrals__transforms__HankelTypeTransform(): pass def test_sympy__integrals__transforms__InverseHankelTransform(): from sympy.integrals.transforms import InverseHankelTransform assert _test_args(InverseHankelTransform(2, x, y, 0)) def test_sympy__integrals__transforms__HankelTransform(): from sympy.integrals.transforms import HankelTransform assert _test_args(HankelTransform(2, x, y, 0)) @XFAIL def test_sympy__liealgebras__cartan_type__CartanType_generator(): from sympy.liealgebras.cartan_type import CartanType_generator assert _test_args(CartanType_generator("A2")) @XFAIL def test_sympy__liealgebras__cartan_type__Standard_Cartan(): from sympy.liealgebras.cartan_type import Standard_Cartan assert _test_args(Standard_Cartan("A", 2)) @XFAIL def test_sympy__liealgebras__weyl_group__WeylGroup(): from sympy.liealgebras.weyl_group import WeylGroup assert _test_args(WeylGroup("B4")) @XFAIL def test_sympy__liealgebras__root_system__RootSystem(): from sympy.liealgebras.root_system import RootSyStem assert _test_args(RootSystem("A2")) @XFAIL def test_sympy__liealgebras__type_a__TypeA(): from sympy.liealgebras.type_a import TypeA assert _test_args(TypeA(2)) @XFAIL def test_sympy__liealgebras__type_b__TypeB(): from sympy.liealgebras.type_b import TypeB assert _test_args(TypeB(4)) @XFAIL def test_sympy__liealgebras__type_c__TypeC(): from sympy.liealgebras.type_c import TypeC assert _test_args(TypeC(4)) @XFAIL def test_sympy__liealgebras__type_d__TypeD(): from sympy.liealgebras.type_d import TypeD assert _test_args(TypeD(4)) @XFAIL def test_sympy__liealgebras__type_e__TypeE(): from sympy.liealgebras.type_e import TypeE assert _test_args(TypeE(6)) @XFAIL def test_sympy__liealgebras__type_f__TypeF(): from sympy.liealgebras.type_f import TypeF assert _test_args(TypeF(4)) @XFAIL def test_sympy__liealgebras__type_g__TypeG(): from sympy.liealgebras.type_g import TypeG assert _test_args(TypeG(2)) def test_sympy__logic__boolalg__And(): from sympy.logic.boolalg import And assert _test_args(And(x, y, 2)) @SKIP("abstract class") def test_sympy__logic__boolalg__Boolean(): pass def test_sympy__logic__boolalg__BooleanFunction(): from sympy.logic.boolalg import BooleanFunction assert _test_args(BooleanFunction(1, 2, 3)) @SKIP("abstract class") def test_sympy__logic__boolalg__BooleanAtom(): pass def test_sympy__logic__boolalg__BooleanTrue(): from sympy.logic.boolalg import true assert _test_args(true) def test_sympy__logic__boolalg__BooleanFalse(): from sympy.logic.boolalg import false assert _test_args(false) def test_sympy__logic__boolalg__Equivalent(): from sympy.logic.boolalg import Equivalent assert _test_args(Equivalent(x, 2)) def test_sympy__logic__boolalg__ITE(): from sympy.logic.boolalg import ITE assert _test_args(ITE(x, y, 2)) def test_sympy__logic__boolalg__Implies(): from sympy.logic.boolalg import Implies assert _test_args(Implies(x, y)) def test_sympy__logic__boolalg__Nand(): from sympy.logic.boolalg import Nand assert _test_args(Nand(x, y, 2)) def test_sympy__logic__boolalg__Nor(): from sympy.logic.boolalg import Nor assert _test_args(Nor(x, y)) def test_sympy__logic__boolalg__Not(): from sympy.logic.boolalg import Not assert _test_args(Not(x)) def test_sympy__logic__boolalg__Or(): from sympy.logic.boolalg import Or assert _test_args(Or(x, y)) def test_sympy__logic__boolalg__Xor(): from sympy.logic.boolalg import Xor assert _test_args(Xor(x, y, 2)) def test_sympy__matrices__matrices__DeferredVector(): from sympy.matrices.matrices import DeferredVector assert _test_args(DeferredVector("X")) @SKIP("abstract class") def test_sympy__matrices__expressions__matexpr__MatrixBase(): pass def test_sympy__matrices__immutable__ImmutableMatrix(): from sympy.matrices.immutable import ImmutableMatrix assert _test_args(ImmutableMatrix([[1, 2], [3, 4]])) def test_sympy__matrices__immutable__ImmutableSparseMatrix(): from sympy.matrices.immutable import ImmutableSparseMatrix assert _test_args(ImmutableSparseMatrix([[1, 2], [3, 4]])) def test_sympy__matrices__expressions__slice__MatrixSlice(): from sympy.matrices.expressions.slice import MatrixSlice from sympy.matrices.expressions import MatrixSymbol X = MatrixSymbol('X', 4, 4) assert _test_args(MatrixSlice(X, (0, 2), (0, 2))) def test_sympy__matrices__expressions__blockmatrix__BlockDiagMatrix(): from sympy.matrices.expressions.blockmatrix import BlockDiagMatrix from sympy.matrices.expressions import MatrixSymbol X = MatrixSymbol('X', x, x) Y = MatrixSymbol('Y', y, y) assert _test_args(BlockDiagMatrix(X, Y)) def test_sympy__matrices__expressions__blockmatrix__BlockMatrix(): from sympy.matrices.expressions.blockmatrix import BlockMatrix from sympy.matrices.expressions import MatrixSymbol, ZeroMatrix X = MatrixSymbol('X', x, x) Y = MatrixSymbol('Y', y, y) Z = MatrixSymbol('Z', x, y) O = ZeroMatrix(y, x) assert _test_args(BlockMatrix([[X, Z], [O, Y]])) def test_sympy__matrices__expressions__inverse__Inverse(): from sympy.matrices.expressions.inverse import Inverse from sympy.matrices.expressions import MatrixSymbol assert _test_args(Inverse(MatrixSymbol('A', 3, 3))) def test_sympy__matrices__expressions__matadd__MatAdd(): from sympy.matrices.expressions.matadd import MatAdd from sympy.matrices.expressions import MatrixSymbol X = MatrixSymbol('X', x, y) Y = MatrixSymbol('Y', x, y) assert _test_args(MatAdd(X, Y)) @XFAIL def test_sympy__matrices__expressions__matexpr__Identity(): from sympy.matrices.expressions.matexpr import Identity assert _test_args(Identity(3)) @SKIP("abstract class") def test_sympy__matrices__expressions__matexpr__MatrixExpr(): pass def test_sympy__matrices__expressions__matexpr__MatrixElement(): from sympy.matrices.expressions.matexpr import MatrixSymbol, MatrixElement from sympy import S assert _test_args(MatrixElement(MatrixSymbol('A', 3, 5), S(2), S(3))) @XFAIL def test_sympy__matrices__expressions__matexpr__MatrixSymbol(): from sympy.matrices.expressions.matexpr import MatrixSymbol assert _test_args(MatrixSymbol('A', 3, 5)) @XFAIL def test_sympy__matrices__expressions__matexpr__ZeroMatrix(): from sympy.matrices.expressions.matexpr import ZeroMatrix assert _test_args(ZeroMatrix(3, 5)) def test_sympy__matrices__expressions__matmul__MatMul(): from sympy.matrices.expressions.matmul import MatMul from sympy.matrices.expressions import MatrixSymbol X = MatrixSymbol('X', x, y) Y = MatrixSymbol('Y', y, x) assert _test_args(MatMul(X, Y)) def test_sympy__matrices__expressions__diagonal__DiagonalMatrix(): from sympy.matrices.expressions.diagonal import DiagonalMatrix from sympy.matrices.expressions import MatrixSymbol x = MatrixSymbol('x', 10, 1) assert _test_args(DiagonalMatrix(x)) def test_sympy__matrices__expressions__diagonal__DiagonalOf(): from sympy.matrices.expressions.diagonal import DiagonalOf from sympy.matrices.expressions import MatrixSymbol X = MatrixSymbol('x', 10, 10) assert _test_args(DiagonalOf(X)) def test_sympy__matrices__expressions__hadamard__HadamardProduct(): from sympy.matrices.expressions.hadamard import HadamardProduct from sympy.matrices.expressions import MatrixSymbol X = MatrixSymbol('X', x, y) Y = MatrixSymbol('Y', x, y) assert _test_args(HadamardProduct(X, Y)) def test_sympy__matrices__expressions__matpow__MatPow(): from sympy.matrices.expressions.matpow import MatPow from sympy.matrices.expressions import MatrixSymbol X = MatrixSymbol('X', x, x) assert _test_args(MatPow(X, 2)) def test_sympy__matrices__expressions__transpose__Transpose(): from sympy.matrices.expressions.transpose import Transpose from sympy.matrices.expressions import MatrixSymbol assert _test_args(Transpose(MatrixSymbol('A', 3, 5))) def test_sympy__matrices__expressions__adjoint__Adjoint(): from sympy.matrices.expressions.adjoint import Adjoint from sympy.matrices.expressions import MatrixSymbol assert _test_args(Adjoint(MatrixSymbol('A', 3, 5))) def test_sympy__matrices__expressions__trace__Trace(): from sympy.matrices.expressions.trace import Trace from sympy.matrices.expressions import MatrixSymbol assert _test_args(Trace(MatrixSymbol('A', 3, 3))) def test_sympy__matrices__expressions__determinant__Determinant(): from sympy.matrices.expressions.determinant import Determinant from sympy.matrices.expressions import MatrixSymbol assert _test_args(Determinant(MatrixSymbol('A', 3, 3))) def test_sympy__matrices__expressions__funcmatrix__FunctionMatrix(): from sympy.matrices.expressions.funcmatrix import FunctionMatrix from sympy import Lambda, symbols i, j = symbols('i,j') assert _test_args(FunctionMatrix(3, 3, Lambda((i, j), i - j) )) def test_sympy__matrices__expressions__fourier__DFT(): from sympy.matrices.expressions.fourier import DFT from sympy import S assert _test_args(DFT(S(2))) def test_sympy__matrices__expressions__fourier__IDFT(): from sympy.matrices.expressions.fourier import IDFT from sympy import S assert _test_args(IDFT(S(2))) from sympy.matrices.expressions import MatrixSymbol X = MatrixSymbol('X', 10, 10) def test_sympy__matrices__expressions__factorizations__LofLU(): from sympy.matrices.expressions.factorizations import LofLU assert _test_args(LofLU(X)) def test_sympy__matrices__expressions__factorizations__UofLU(): from sympy.matrices.expressions.factorizations import UofLU assert _test_args(UofLU(X)) def test_sympy__matrices__expressions__factorizations__QofQR(): from sympy.matrices.expressions.factorizations import QofQR assert _test_args(QofQR(X)) def test_sympy__matrices__expressions__factorizations__RofQR(): from sympy.matrices.expressions.factorizations import RofQR assert _test_args(RofQR(X)) def test_sympy__matrices__expressions__factorizations__LofCholesky(): from sympy.matrices.expressions.factorizations import LofCholesky assert _test_args(LofCholesky(X)) def test_sympy__matrices__expressions__factorizations__UofCholesky(): from sympy.matrices.expressions.factorizations import UofCholesky assert _test_args(UofCholesky(X)) def test_sympy__matrices__expressions__factorizations__EigenVectors(): from sympy.matrices.expressions.factorizations import EigenVectors assert _test_args(EigenVectors(X)) def test_sympy__matrices__expressions__factorizations__EigenValues(): from sympy.matrices.expressions.factorizations import EigenValues assert _test_args(EigenValues(X)) def test_sympy__matrices__expressions__factorizations__UofSVD(): from sympy.matrices.expressions.factorizations import UofSVD assert _test_args(UofSVD(X)) def test_sympy__matrices__expressions__factorizations__VofSVD(): from sympy.matrices.expressions.factorizations import VofSVD assert _test_args(VofSVD(X)) def test_sympy__matrices__expressions__factorizations__SofSVD(): from sympy.matrices.expressions.factorizations import SofSVD assert _test_args(SofSVD(X)) @SKIP("abstract class") def test_sympy__matrices__expressions__factorizations__Factorization(): pass def test_sympy__physics__mechanics__essential__CoordinateSym(): from sympy.physics.mechanics import CoordinateSym from sympy.physics.mechanics import ReferenceFrame assert _test_args(CoordinateSym('R_x', ReferenceFrame('R'), 0)) def test_sympy__physics__gaussopt__BeamParameter(): from sympy.physics.gaussopt import BeamParameter assert _test_args(BeamParameter(530e-9, 1, w=1e-3)) def test_sympy__physics__paulialgebra__Pauli(): from sympy.physics.paulialgebra import Pauli assert _test_args(Pauli(1)) def test_sympy__physics__quantum__anticommutator__AntiCommutator(): from sympy.physics.quantum.anticommutator import AntiCommutator assert _test_args(AntiCommutator(x, y)) def test_sympy__physics__quantum__cartesian__PositionBra3D(): from sympy.physics.quantum.cartesian import PositionBra3D assert _test_args(PositionBra3D(x, y, z)) def test_sympy__physics__quantum__cartesian__PositionKet3D(): from sympy.physics.quantum.cartesian import PositionKet3D assert _test_args(PositionKet3D(x, y, z)) def test_sympy__physics__quantum__cartesian__PositionState3D(): from sympy.physics.quantum.cartesian import PositionState3D assert _test_args(PositionState3D(x, y, z)) def test_sympy__physics__quantum__cartesian__PxBra(): from sympy.physics.quantum.cartesian import PxBra assert _test_args(PxBra(x, y, z)) def test_sympy__physics__quantum__cartesian__PxKet(): from sympy.physics.quantum.cartesian import PxKet assert _test_args(PxKet(x, y, z)) def test_sympy__physics__quantum__cartesian__PxOp(): from sympy.physics.quantum.cartesian import PxOp assert _test_args(PxOp(x, y, z)) def test_sympy__physics__quantum__cartesian__XBra(): from sympy.physics.quantum.cartesian import XBra assert _test_args(XBra(x)) def test_sympy__physics__quantum__cartesian__XKet(): from sympy.physics.quantum.cartesian import XKet assert _test_args(XKet(x)) def test_sympy__physics__quantum__cartesian__XOp(): from sympy.physics.quantum.cartesian import XOp assert _test_args(XOp(x)) def test_sympy__physics__quantum__cartesian__YOp(): from sympy.physics.quantum.cartesian import YOp assert _test_args(YOp(x)) def test_sympy__physics__quantum__cartesian__ZOp(): from sympy.physics.quantum.cartesian import ZOp assert _test_args(ZOp(x)) def test_sympy__physics__quantum__cg__CG(): from sympy.physics.quantum.cg import CG from sympy import S assert _test_args(CG(S(3)/2, S(3)/2, S(1)/2, -S(1)/2, 1, 1)) def test_sympy__physics__quantum__cg__Wigner3j(): from sympy.physics.quantum.cg import Wigner3j assert _test_args(Wigner3j(6, 0, 4, 0, 2, 0)) def test_sympy__physics__quantum__cg__Wigner6j(): from sympy.physics.quantum.cg import Wigner6j assert _test_args(Wigner6j(1, 2, 3, 2, 1, 2)) def test_sympy__physics__quantum__cg__Wigner9j(): from sympy.physics.quantum.cg import Wigner9j assert _test_args(Wigner9j(2, 1, 1, S(3)/2, S(1)/2, 1, S(1)/2, S(1)/2, 0)) def test_sympy__physics__quantum__circuitplot__Mz(): from sympy.physics.quantum.circuitplot import Mz assert _test_args(Mz(0)) def test_sympy__physics__quantum__circuitplot__Mx(): from sympy.physics.quantum.circuitplot import Mx assert _test_args(Mx(0)) def test_sympy__physics__quantum__commutator__Commutator(): from sympy.physics.quantum.commutator import Commutator A, B = symbols('A,B', commutative=False) assert _test_args(Commutator(A, B)) def test_sympy__physics__quantum__constants__HBar(): from sympy.physics.quantum.constants import HBar assert _test_args(HBar()) def test_sympy__physics__quantum__dagger__Dagger(): from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.state import Ket assert _test_args(Dagger(Dagger(Ket('psi')))) def test_sympy__physics__quantum__gate__CGate(): from sympy.physics.quantum.gate import CGate, Gate assert _test_args(CGate((0, 1), Gate(2))) def test_sympy__physics__quantum__gate__CGateS(): from sympy.physics.quantum.gate import CGateS, Gate assert _test_args(CGateS((0, 1), Gate(2))) def test_sympy__physics__quantum__gate__CNotGate(): from sympy.physics.quantum.gate import CNotGate assert _test_args(CNotGate(0, 1)) def test_sympy__physics__quantum__gate__Gate(): from sympy.physics.quantum.gate import Gate assert _test_args(Gate(0)) def test_sympy__physics__quantum__gate__HadamardGate(): from sympy.physics.quantum.gate import HadamardGate assert _test_args(HadamardGate(0)) def test_sympy__physics__quantum__gate__IdentityGate(): from sympy.physics.quantum.gate import IdentityGate assert _test_args(IdentityGate(0)) def test_sympy__physics__quantum__gate__OneQubitGate(): from sympy.physics.quantum.gate import OneQubitGate assert _test_args(OneQubitGate(0)) def test_sympy__physics__quantum__gate__PhaseGate(): from sympy.physics.quantum.gate import PhaseGate assert _test_args(PhaseGate(0)) def test_sympy__physics__quantum__gate__SwapGate(): from sympy.physics.quantum.gate import SwapGate assert _test_args(SwapGate(0, 1)) def test_sympy__physics__quantum__gate__TGate(): from sympy.physics.quantum.gate import TGate assert _test_args(TGate(0)) def test_sympy__physics__quantum__gate__TwoQubitGate(): from sympy.physics.quantum.gate import TwoQubitGate assert _test_args(TwoQubitGate(0)) def test_sympy__physics__quantum__gate__UGate(): from sympy.physics.quantum.gate import UGate from sympy.matrices.immutable import ImmutableMatrix from sympy import Integer, Tuple assert _test_args( UGate(Tuple(Integer(1)), ImmutableMatrix([[1, 0], [0, 2]]))) def test_sympy__physics__quantum__gate__XGate(): from sympy.physics.quantum.gate import XGate assert _test_args(XGate(0)) def test_sympy__physics__quantum__gate__YGate(): from sympy.physics.quantum.gate import YGate assert _test_args(YGate(0)) def test_sympy__physics__quantum__gate__ZGate(): from sympy.physics.quantum.gate import ZGate assert _test_args(ZGate(0)) @SKIP("TODO: sympy.physics") def test_sympy__physics__quantum__grover__OracleGate(): from sympy.physics.quantum.grover import OracleGate assert _test_args(OracleGate()) def test_sympy__physics__quantum__grover__WGate(): from sympy.physics.quantum.grover import WGate assert _test_args(WGate(1)) def test_sympy__physics__quantum__hilbert__ComplexSpace(): from sympy.physics.quantum.hilbert import ComplexSpace assert _test_args(ComplexSpace(x)) def test_sympy__physics__quantum__hilbert__DirectSumHilbertSpace(): from sympy.physics.quantum.hilbert import DirectSumHilbertSpace, ComplexSpace, FockSpace c = ComplexSpace(2) f = FockSpace() assert _test_args(DirectSumHilbertSpace(c, f)) def test_sympy__physics__quantum__hilbert__FockSpace(): from sympy.physics.quantum.hilbert import FockSpace assert _test_args(FockSpace()) def test_sympy__physics__quantum__hilbert__HilbertSpace(): from sympy.physics.quantum.hilbert import HilbertSpace assert _test_args(HilbertSpace()) def test_sympy__physics__quantum__hilbert__L2(): from sympy.physics.quantum.hilbert import L2 from sympy import oo, Interval assert _test_args(L2(Interval(0, oo))) def test_sympy__physics__quantum__hilbert__TensorPowerHilbertSpace(): from sympy.physics.quantum.hilbert import TensorPowerHilbertSpace, FockSpace f = FockSpace() assert _test_args(TensorPowerHilbertSpace(f, 2)) def test_sympy__physics__quantum__hilbert__TensorProductHilbertSpace(): from sympy.physics.quantum.hilbert import TensorProductHilbertSpace, FockSpace, ComplexSpace c = ComplexSpace(2) f = FockSpace() assert _test_args(TensorProductHilbertSpace(f, c)) def test_sympy__physics__quantum__innerproduct__InnerProduct(): from sympy.physics.quantum import Bra, Ket, InnerProduct b = Bra('b') k = Ket('k') assert _test_args(InnerProduct(b, k)) def test_sympy__physics__quantum__operator__DifferentialOperator(): from sympy.physics.quantum.operator import DifferentialOperator from sympy import Derivative, Function f = Function('f') assert _test_args(DifferentialOperator(1/x*Derivative(f(x), x), f(x))) def test_sympy__physics__quantum__operator__HermitianOperator(): from sympy.physics.quantum.operator import HermitianOperator assert _test_args(HermitianOperator('H')) def test_sympy__physics__quantum__operator__Operator(): from sympy.physics.quantum.operator import Operator assert _test_args(Operator('A')) def test_sympy__physics__quantum__operator__OuterProduct(): from sympy.physics.quantum.operator import OuterProduct from sympy.physics.quantum import Ket, Bra b = Bra('b') k = Ket('k') assert _test_args(OuterProduct(k, b)) def test_sympy__physics__quantum__operator__UnitaryOperator(): from sympy.physics.quantum.operator import UnitaryOperator assert _test_args(UnitaryOperator('U')) def test_sympy__physics__quantum__piab__PIABBra(): from sympy.physics.quantum.piab import PIABBra assert _test_args(PIABBra('B')) def test_sympy__physics__quantum__piab__PIABHamiltonian(): from sympy.physics.quantum.piab import PIABHamiltonian assert _test_args(PIABHamiltonian('P')) def test_sympy__physics__quantum__piab__PIABKet(): from sympy.physics.quantum.piab import PIABKet assert _test_args(PIABKet('K')) def test_sympy__physics__quantum__qexpr__QExpr(): from sympy.physics.quantum.qexpr import QExpr assert _test_args(QExpr(0)) def test_sympy__physics__quantum__qft__Fourier(): from sympy.physics.quantum.qft import Fourier assert _test_args(Fourier(0, 1)) def test_sympy__physics__quantum__qft__IQFT(): from sympy.physics.quantum.qft import IQFT assert _test_args(IQFT(0, 1)) def test_sympy__physics__quantum__qft__QFT(): from sympy.physics.quantum.qft import QFT assert _test_args(QFT(0, 1)) def test_sympy__physics__quantum__qft__RkGate(): from sympy.physics.quantum.qft import RkGate assert _test_args(RkGate(0, 1)) def test_sympy__physics__quantum__qubit__IntQubit(): from sympy.physics.quantum.qubit import IntQubit assert _test_args(IntQubit(0)) def test_sympy__physics__quantum__qubit__IntQubitBra(): from sympy.physics.quantum.qubit import IntQubitBra assert _test_args(IntQubitBra(0)) def test_sympy__physics__quantum__qubit__IntQubitState(): from sympy.physics.quantum.qubit import IntQubitState, QubitState assert _test_args(IntQubitState(QubitState(0, 1))) def test_sympy__physics__quantum__qubit__Qubit(): from sympy.physics.quantum.qubit import Qubit assert _test_args(Qubit(0, 0, 0)) def test_sympy__physics__quantum__qubit__QubitBra(): from sympy.physics.quantum.qubit import QubitBra assert _test_args(QubitBra('1', 0)) def test_sympy__physics__quantum__qubit__QubitState(): from sympy.physics.quantum.qubit import QubitState assert _test_args(QubitState(0, 1)) def test_sympy__physics__quantum__density__Density(): from sympy.physics.quantum.density import Density from sympy.physics.quantum.state import Ket assert _test_args(Density([Ket(0), 0.5], [Ket(1), 0.5])) @SKIP("TODO: sympy.physics.quantum.shor: Cmod Not Implemented") def test_sympy__physics__quantum__shor__CMod(): from sympy.physics.quantum.shor import CMod assert _test_args(CMod()) def test_sympy__physics__quantum__spin__CoupledSpinState(): from sympy.physics.quantum.spin import CoupledSpinState assert _test_args(CoupledSpinState(1, 0, (1, 1))) assert _test_args(CoupledSpinState(1, 0, (1, S(1)/2, S(1)/2))) assert _test_args(CoupledSpinState( 1, 0, (1, S(1)/2, S(1)/2), ((2, 3, S(1)/2), (1, 2, 1)) )) j, m, j1, j2, j3, j12, x = symbols('j m j1:4 j12 x') assert CoupledSpinState( j, m, (j1, j2, j3)).subs(j2, x) == CoupledSpinState(j, m, (j1, x, j3)) assert CoupledSpinState(j, m, (j1, j2, j3), ((1, 3, j12), (1, 2, j)) ).subs(j12, x) == \ CoupledSpinState(j, m, (j1, j2, j3), ((1, 3, x), (1, 2, j)) ) def test_sympy__physics__quantum__spin__J2Op(): from sympy.physics.quantum.spin import J2Op assert _test_args(J2Op('J')) def test_sympy__physics__quantum__spin__JminusOp(): from sympy.physics.quantum.spin import JminusOp assert _test_args(JminusOp('J')) def test_sympy__physics__quantum__spin__JplusOp(): from sympy.physics.quantum.spin import JplusOp assert _test_args(JplusOp('J')) def test_sympy__physics__quantum__spin__JxBra(): from sympy.physics.quantum.spin import JxBra assert _test_args(JxBra(1, 0)) def test_sympy__physics__quantum__spin__JxBraCoupled(): from sympy.physics.quantum.spin import JxBraCoupled assert _test_args(JxBraCoupled(1, 0, (1, 1))) def test_sympy__physics__quantum__spin__JxKet(): from sympy.physics.quantum.spin import JxKet assert _test_args(JxKet(1, 0)) def test_sympy__physics__quantum__spin__JxKetCoupled(): from sympy.physics.quantum.spin import JxKetCoupled assert _test_args(JxKetCoupled(1, 0, (1, 1))) def test_sympy__physics__quantum__spin__JxOp(): from sympy.physics.quantum.spin import JxOp assert _test_args(JxOp('J')) def test_sympy__physics__quantum__spin__JyBra(): from sympy.physics.quantum.spin import JyBra assert _test_args(JyBra(1, 0)) def test_sympy__physics__quantum__spin__JyBraCoupled(): from sympy.physics.quantum.spin import JyBraCoupled assert _test_args(JyBraCoupled(1, 0, (1, 1))) def test_sympy__physics__quantum__spin__JyKet(): from sympy.physics.quantum.spin import JyKet assert _test_args(JyKet(1, 0)) def test_sympy__physics__quantum__spin__JyKetCoupled(): from sympy.physics.quantum.spin import JyKetCoupled assert _test_args(JyKetCoupled(1, 0, (1, 1))) def test_sympy__physics__quantum__spin__JyOp(): from sympy.physics.quantum.spin import JyOp assert _test_args(JyOp('J')) def test_sympy__physics__quantum__spin__JzBra(): from sympy.physics.quantum.spin import JzBra assert _test_args(JzBra(1, 0)) def test_sympy__physics__quantum__spin__JzBraCoupled(): from sympy.physics.quantum.spin import JzBraCoupled assert _test_args(JzBraCoupled(1, 0, (1, 1))) def test_sympy__physics__quantum__spin__JzKet(): from sympy.physics.quantum.spin import JzKet assert _test_args(JzKet(1, 0)) def test_sympy__physics__quantum__spin__JzKetCoupled(): from sympy.physics.quantum.spin import JzKetCoupled assert _test_args(JzKetCoupled(1, 0, (1, 1))) def test_sympy__physics__quantum__spin__JzOp(): from sympy.physics.quantum.spin import JzOp assert _test_args(JzOp('J')) def test_sympy__physics__quantum__spin__Rotation(): from sympy.physics.quantum.spin import Rotation from sympy import pi assert _test_args(Rotation(pi, 0, pi/2)) def test_sympy__physics__quantum__spin__SpinState(): from sympy.physics.quantum.spin import SpinState assert _test_args(SpinState(1, 0)) def test_sympy__physics__quantum__spin__WignerD(): from sympy.physics.quantum.spin import WignerD assert _test_args(WignerD(0, 1, 2, 3, 4, 5)) def test_sympy__physics__quantum__state__Bra(): from sympy.physics.quantum.state import Bra assert _test_args(Bra(0)) def test_sympy__physics__quantum__state__BraBase(): from sympy.physics.quantum.state import BraBase assert _test_args(BraBase(0)) def test_sympy__physics__quantum__state__Ket(): from sympy.physics.quantum.state import Ket assert _test_args(Ket(0)) def test_sympy__physics__quantum__state__KetBase(): from sympy.physics.quantum.state import KetBase assert _test_args(KetBase(0)) def test_sympy__physics__quantum__state__State(): from sympy.physics.quantum.state import State assert _test_args(State(0)) def test_sympy__physics__quantum__state__StateBase(): from sympy.physics.quantum.state import StateBase assert _test_args(StateBase(0)) def test_sympy__physics__quantum__state__TimeDepBra(): from sympy.physics.quantum.state import TimeDepBra assert _test_args(TimeDepBra('psi', 't')) def test_sympy__physics__quantum__state__TimeDepKet(): from sympy.physics.quantum.state import TimeDepKet assert _test_args(TimeDepKet('psi', 't')) def test_sympy__physics__quantum__state__TimeDepState(): from sympy.physics.quantum.state import TimeDepState assert _test_args(TimeDepState('psi', 't')) def test_sympy__physics__quantum__state__Wavefunction(): from sympy.physics.quantum.state import Wavefunction from sympy.functions import sin from sympy import Piecewise, pi n = 1 L = 1 g = Piecewise((0, x < 0), (0, x > L), (sqrt(2//L)*sin(n*pi*x/L), True)) assert _test_args(Wavefunction(g, x)) def test_sympy__physics__quantum__tensorproduct__TensorProduct(): from sympy.physics.quantum.tensorproduct import TensorProduct assert _test_args(TensorProduct(x, y)) def test_sympy__physics__quantum__identitysearch__GateIdentity(): from sympy.physics.quantum.gate import X from sympy.physics.quantum.identitysearch import GateIdentity assert _test_args(GateIdentity(X(0), X(0))) def test_sympy__physics__quantum__sho1d__SHOOp(): from sympy.physics.quantum.sho1d import SHOOp assert _test_args(SHOOp('a')) def test_sympy__physics__quantum__sho1d__RaisingOp(): from sympy.physics.quantum.sho1d import RaisingOp assert _test_args(RaisingOp('a')) def test_sympy__physics__quantum__sho1d__LoweringOp(): from sympy.physics.quantum.sho1d import LoweringOp assert _test_args(LoweringOp('a')) def test_sympy__physics__quantum__sho1d__NumberOp(): from sympy.physics.quantum.sho1d import NumberOp assert _test_args(NumberOp('N')) def test_sympy__physics__quantum__sho1d__Hamiltonian(): from sympy.physics.quantum.sho1d import Hamiltonian assert _test_args(Hamiltonian('H')) def test_sympy__physics__quantum__sho1d__SHOState(): from sympy.physics.quantum.sho1d import SHOState assert _test_args(SHOState(0)) def test_sympy__physics__quantum__sho1d__SHOKet(): from sympy.physics.quantum.sho1d import SHOKet assert _test_args(SHOKet(0)) def test_sympy__physics__quantum__sho1d__SHOBra(): from sympy.physics.quantum.sho1d import SHOBra assert _test_args(SHOBra(0)) def test_sympy__physics__secondquant__AnnihilateBoson(): from sympy.physics.secondquant import AnnihilateBoson assert _test_args(AnnihilateBoson(0)) def test_sympy__physics__secondquant__AnnihilateFermion(): from sympy.physics.secondquant import AnnihilateFermion assert _test_args(AnnihilateFermion(0)) @SKIP("abstract class") def test_sympy__physics__secondquant__Annihilator(): pass def test_sympy__physics__secondquant__AntiSymmetricTensor(): from sympy.physics.secondquant import AntiSymmetricTensor i, j = symbols('i j', below_fermi=True) a, b = symbols('a b', above_fermi=True) assert _test_args(AntiSymmetricTensor('v', (a, i), (b, j))) def test_sympy__physics__secondquant__BosonState(): from sympy.physics.secondquant import BosonState assert _test_args(BosonState((0, 1))) @SKIP("abstract class") def test_sympy__physics__secondquant__BosonicOperator(): pass def test_sympy__physics__secondquant__Commutator(): from sympy.physics.secondquant import Commutator assert _test_args(Commutator(x, y)) def test_sympy__physics__secondquant__CreateBoson(): from sympy.physics.secondquant import CreateBoson assert _test_args(CreateBoson(0)) def test_sympy__physics__secondquant__CreateFermion(): from sympy.physics.secondquant import CreateFermion assert _test_args(CreateFermion(0)) @SKIP("abstract class") def test_sympy__physics__secondquant__Creator(): pass def test_sympy__physics__secondquant__Dagger(): from sympy.physics.secondquant import Dagger from sympy import I assert _test_args(Dagger(2*I)) def test_sympy__physics__secondquant__FermionState(): from sympy.physics.secondquant import FermionState assert _test_args(FermionState((0, 1))) def test_sympy__physics__secondquant__FermionicOperator(): from sympy.physics.secondquant import FermionicOperator assert _test_args(FermionicOperator(0)) def test_sympy__physics__secondquant__FockState(): from sympy.physics.secondquant import FockState assert _test_args(FockState((0, 1))) def test_sympy__physics__secondquant__FockStateBosonBra(): from sympy.physics.secondquant import FockStateBosonBra assert _test_args(FockStateBosonBra((0, 1))) def test_sympy__physics__secondquant__FockStateBosonKet(): from sympy.physics.secondquant import FockStateBosonKet assert _test_args(FockStateBosonKet((0, 1))) def test_sympy__physics__secondquant__FockStateBra(): from sympy.physics.secondquant import FockStateBra assert _test_args(FockStateBra((0, 1))) def test_sympy__physics__secondquant__FockStateFermionBra(): from sympy.physics.secondquant import FockStateFermionBra assert _test_args(FockStateFermionBra((0, 1))) def test_sympy__physics__secondquant__FockStateFermionKet(): from sympy.physics.secondquant import FockStateFermionKet assert _test_args(FockStateFermionKet((0, 1))) def test_sympy__physics__secondquant__FockStateKet(): from sympy.physics.secondquant import FockStateKet assert _test_args(FockStateKet((0, 1))) def test_sympy__physics__secondquant__InnerProduct(): from sympy.physics.secondquant import InnerProduct from sympy.physics.secondquant import FockStateKet, FockStateBra assert _test_args(InnerProduct(FockStateBra((0, 1)), FockStateKet((0, 1)))) def test_sympy__physics__secondquant__NO(): from sympy.physics.secondquant import NO, F, Fd assert _test_args(NO(Fd(x)*F(y))) def test_sympy__physics__secondquant__PermutationOperator(): from sympy.physics.secondquant import PermutationOperator assert _test_args(PermutationOperator(0, 1)) def test_sympy__physics__secondquant__SqOperator(): from sympy.physics.secondquant import SqOperator assert _test_args(SqOperator(0)) def test_sympy__physics__secondquant__TensorSymbol(): from sympy.physics.secondquant import TensorSymbol assert _test_args(TensorSymbol(x)) def test_sympy__physics__units__Unit(): from sympy.physics.units import Unit assert _test_args(Unit("meter", "m")) def test_sympy__polys__numberfields__AlgebraicNumber(): from sympy.polys.numberfields import AlgebraicNumber assert _test_args(AlgebraicNumber(sqrt(2), [1, 2, 3])) def test_sympy__polys__polytools__GroebnerBasis(): from sympy.polys.polytools import GroebnerBasis assert _test_args(GroebnerBasis([x, y, z], x, y, z)) def test_sympy__polys__polytools__Poly(): from sympy.polys.polytools import Poly assert _test_args(Poly(2, x, y)) def test_sympy__polys__polytools__PurePoly(): from sympy.polys.polytools import PurePoly assert _test_args(PurePoly(2, x, y)) def test_sympy__polys__rootoftools__RootOf(): from sympy.polys.rootoftools import RootOf assert _test_args(RootOf(x**3 + x + 1, 0)) def test_sympy__polys__rootoftools__RootSum(): from sympy.polys.rootoftools import RootSum assert _test_args(RootSum(x**3 + x + 1, sin)) def test_sympy__series__limits__Limit(): from sympy.series.limits import Limit assert _test_args(Limit(x, x, 0, dir='-')) def test_sympy__series__order__Order(): from sympy.series.order import Order assert _test_args(Order(1, x, y)) def test_sympy__simplify__hyperexpand__Hyper_Function(): from sympy.simplify.hyperexpand import Hyper_Function assert _test_args(Hyper_Function([2], [1])) def test_sympy__simplify__hyperexpand__G_Function(): from sympy.simplify.hyperexpand import G_Function assert _test_args(G_Function([2], [1], [], [])) def test_sympy__tensor__indexed__Idx(): from sympy.tensor.indexed import Idx assert _test_args(Idx('test')) assert _test_args(Idx(1, (0, 10))) def test_sympy__tensor__indexed__Indexed(): from sympy.tensor.indexed import Indexed, Idx assert _test_args(Indexed('A', Idx('i'), Idx('j'))) def test_sympy__tensor__indexed__IndexedBase(): from sympy.tensor.indexed import IndexedBase assert _test_args(IndexedBase('A', shape=(x, y))) assert _test_args(IndexedBase('A', 1)) assert _test_args(IndexedBase('A')[0, 1]) @XFAIL def test_sympy__physics__hep__gamma_matrices__GammaMatrixHead(): # This test fails, this class can be reconstructed from the *args # of an instance using `TensorHead(*args)` from sympy.physics.hep.gamma_matrices import GammaMatrixHead, Lorentz from sympy.tensor.tensor import tensor_indices i = tensor_indices('i', Lorentz) assert _test_args(GammaMatrixHead()) def test_sympy__tensor__tensor__TensorIndexType(): from sympy.tensor.tensor import TensorIndexType from sympy import Symbol assert _test_args(TensorIndexType('Lorentz', metric=False)) def test_sympy__tensor__tensor__TensorSymmetry(): from sympy.tensor.tensor import TensorIndexType, tensor_indices, TensorSymmetry, TensorType, get_symmetric_group_sgs assert _test_args(TensorSymmetry(get_symmetric_group_sgs(2))) def test_sympy__tensor__tensor__TensorType(): from sympy.tensor.tensor import TensorIndexType, TensorSymmetry, get_symmetric_group_sgs, TensorType Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') sym = TensorSymmetry(get_symmetric_group_sgs(1)) assert _test_args(TensorType([Lorentz], sym)) def test_sympy__tensor__tensor__TensorHead(): from sympy.tensor.tensor import TensorIndexType, TensorSymmetry, TensorType, get_symmetric_group_sgs, TensorHead Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') sym = TensorSymmetry(get_symmetric_group_sgs(1)) S1 = TensorType([Lorentz], sym) assert _test_args(TensorHead('p', S1, 0)) def test_sympy__tensor__tensor__TensorIndex(): from sympy.tensor.tensor import TensorIndexType, TensorIndex, TensorSymmetry, TensorType, get_symmetric_group_sgs Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') assert _test_args(TensorIndex('i', Lorentz)) @SKIP("abstract class") def test_sympy__tensor__tensor__TensExpr(): pass def test_sympy__tensor__tensor__TensAdd(): from sympy.tensor.tensor import TensorIndexType, TensorSymmetry, TensorType, get_symmetric_group_sgs, tensor_indices, TensAdd Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') a, b = tensor_indices('a,b', Lorentz) sym = TensorSymmetry(get_symmetric_group_sgs(1)) S1 = TensorType([Lorentz], sym) p, q = S1('p,q') t1 = p(a) t2 = q(a) assert _test_args(TensAdd(t1, t2)) def test_sympy__tensor__tensor__TensMul(): from sympy.core import S from sympy.tensor.tensor import TensorIndexType, TensorSymmetry, TensorType, get_symmetric_group_sgs, tensor_indices, TensMul, TIDS Lorentz = TensorIndexType('Lorentz', dummy_fmt='L') a, b = tensor_indices('a,b', Lorentz) sym = TensorSymmetry(get_symmetric_group_sgs(1)) S1 = TensorType([Lorentz], sym) p = S1('p') free, dum = TIDS.free_dum_from_indices(a) assert _test_args(TensMul.from_data(S.One, [p], free, dum)) @XFAIL def test_as_coeff_add(): # the ordering of terms in (3*x, 4*x**2) is system-dependent assert (7, (3*x, 4*x**2)) == (7 + 3*x + 4*x**2).as_coeff_add() def test_sympy__geometry__curve__Curve(): from sympy.geometry.curve import Curve assert _test_args(Curve((x, 1), (x, 0, 1))) def test_sympy__geometry__point__Point(): from sympy.geometry.point import Point assert _test_args(Point(0, 1)) def test_sympy__geometry__ellipse__Ellipse(): from sympy.geometry.ellipse import Ellipse assert _test_args(Ellipse((0, 1), 2, 3)) def test_sympy__geometry__ellipse__Circle(): from sympy.geometry.ellipse import Circle assert _test_args(Circle((0, 1), 2)) def test_sympy__geometry__line__LinearEntity(): from sympy.geometry.line import LinearEntity assert _test_args(LinearEntity((0, 1), (2, 3))) def test_sympy__geometry__line__Line(): from sympy.geometry.line import Line assert _test_args(Line((0, 1), (2, 3))) def test_sympy__geometry__line__Ray(): from sympy.geometry.line import Ray assert _test_args(Ray((0, 1), (2, 3))) def test_sympy__geometry__line__Segment(): from sympy.geometry.line import Segment assert _test_args(Segment((0, 1), (2, 3))) def test_sympy__geometry__polygon__Polygon(): from sympy.geometry.polygon import Polygon assert _test_args(Polygon((0, 1), (2, 3), (4, 5), (6, 7))) def test_sympy__geometry__polygon__RegularPolygon(): from sympy.geometry.polygon import RegularPolygon assert _test_args(RegularPolygon((0, 1), 2, 3, 4)) def test_sympy__geometry__polygon__Triangle(): from sympy.geometry.polygon import Triangle assert _test_args(Triangle((0, 1), (2, 3), (4, 5))) def test_sympy__geometry__entity__GeometryEntity(): from sympy.geometry.entity import GeometryEntity from sympy.geometry.point import Point assert _test_args(GeometryEntity(Point(1, 0), 1)) @XFAIL def test_sympy__diffgeom__diffgeom__Manifold(): from sympy.diffgeom import Manifold assert _test_args(Manifold('name', 3)) @XFAIL def test_sympy__diffgeom__diffgeom__Patch(): from sympy.diffgeom import Manifold, Patch assert _test_args(Patch('name', Manifold('name', 3))) @XFAIL def test_sympy__diffgeom__diffgeom__CoordSystem(): from sympy.diffgeom import Manifold, Patch, CoordSystem assert _test_args(CoordSystem('name', Patch('name', Manifold('name', 3)))) @XFAIL def test_sympy__diffgeom__diffgeom__Point(): from sympy.diffgeom import Manifold, Patch, CoordSystem, Point assert _test_args(Point( CoordSystem('name', Patch('name', Manifold('name', 3))), [x, y])) @XFAIL def test_sympy__diffgeom__diffgeom__BaseScalarField(): from sympy.diffgeom import Manifold, Patch, CoordSystem, BaseScalarField cs = CoordSystem('name', Patch('name', Manifold('name', 3))) assert _test_args(BaseScalarField(cs, 0)) @XFAIL def test_sympy__diffgeom__diffgeom__BaseVectorField(): from sympy.diffgeom import Manifold, Patch, CoordSystem, BaseVectorField cs = CoordSystem('name', Patch('name', Manifold('name', 3))) assert _test_args(BaseVectorField(cs, 0)) def test_sympy__diffgeom__diffgeom__Differential(): from sympy.diffgeom import Manifold, Patch, CoordSystem, BaseScalarField, Differential cs = CoordSystem('name', Patch('name', Manifold('name', 3))) assert _test_args(Differential(BaseScalarField(cs, 0))) def test_sympy__diffgeom__diffgeom__Commutator(): from sympy.diffgeom import Manifold, Patch, CoordSystem, BaseScalarField, BaseVectorField, Commutator cs = CoordSystem('name', Patch('name', Manifold('name', 3))) cs1 = CoordSystem('name1', Patch('name', Manifold('name', 3))) v = BaseVectorField(cs, 0) v1 = BaseVectorField(cs1, 0) assert _test_args(Commutator(v, v1)) def test_sympy__diffgeom__diffgeom__TensorProduct(): from sympy.diffgeom import Manifold, Patch, CoordSystem, BaseScalarField, Differential, TensorProduct cs = CoordSystem('name', Patch('name', Manifold('name', 3))) d = Differential(BaseScalarField(cs, 0)) assert _test_args(TensorProduct(d, d)) def test_sympy__diffgeom__diffgeom__WedgeProduct(): from sympy.diffgeom import Manifold, Patch, CoordSystem, BaseScalarField, Differential, WedgeProduct cs = CoordSystem('name', Patch('name', Manifold('name', 3))) d = Differential(BaseScalarField(cs, 0)) d1 = Differential(BaseScalarField(cs, 1)) assert _test_args(WedgeProduct(d, d1)) def test_sympy__diffgeom__diffgeom__LieDerivative(): from sympy.diffgeom import Manifold, Patch, CoordSystem, BaseScalarField, Differential, BaseVectorField, LieDerivative cs = CoordSystem('name', Patch('name', Manifold('name', 3))) d = Differential(BaseScalarField(cs, 0)) v = BaseVectorField(cs, 0) assert _test_args(LieDerivative(v, d)) @XFAIL def test_sympy__diffgeom__diffgeom__BaseCovarDerivativeOp(): from sympy.diffgeom import Manifold, Patch, CoordSystem, BaseCovarDerivativeOp cs = CoordSystem('name', Patch('name', Manifold('name', 3))) assert _test_args(BaseCovarDerivativeOp(cs, 0, [[[0, ]*3, ]*3, ]*3)) def test_sympy__diffgeom__diffgeom__CovarDerivativeOp(): from sympy.diffgeom import Manifold, Patch, CoordSystem, BaseVectorField, CovarDerivativeOp cs = CoordSystem('name', Patch('name', Manifold('name', 3))) v = BaseVectorField(cs, 0) _test_args(CovarDerivativeOp(v, [[[0, ]*3, ]*3, ]*3)) def test_sympy__categories__baseclasses__Class(): from sympy.categories.baseclasses import Class assert _test_args(Class()) def test_sympy__categories__baseclasses__Object(): from sympy.categories import Object assert _test_args(Object("A")) @XFAIL def test_sympy__categories__baseclasses__Morphism(): from sympy.categories import Object, Morphism assert _test_args(Morphism(Object("A"), Object("B"))) def test_sympy__categories__baseclasses__IdentityMorphism(): from sympy.categories import Object, IdentityMorphism assert _test_args(IdentityMorphism(Object("A"))) def test_sympy__categories__baseclasses__NamedMorphism(): from sympy.categories import Object, NamedMorphism assert _test_args(NamedMorphism(Object("A"), Object("B"), "f")) def test_sympy__categories__baseclasses__CompositeMorphism(): from sympy.categories import Object, NamedMorphism, CompositeMorphism A = Object("A") B = Object("B") C = Object("C") f = NamedMorphism(A, B, "f") g = NamedMorphism(B, C, "g") assert _test_args(CompositeMorphism(f, g)) def test_sympy__categories__baseclasses__Diagram(): from sympy.categories import Object, NamedMorphism, Diagram, Category A = Object("A") B = Object("B") C = Object("C") f = NamedMorphism(A, B, "f") d = Diagram([f]) assert _test_args(d) def test_sympy__categories__baseclasses__Category(): from sympy.categories import Object, NamedMorphism, Diagram, Category A = Object("A") B = Object("B") C = Object("C") f = NamedMorphism(A, B, "f") g = NamedMorphism(B, C, "g") d1 = Diagram([f, g]) d2 = Diagram([f]) K = Category("K", commutative_diagrams=[d1, d2]) assert _test_args(K) def test_sympy__ntheory__factor___totient(): from sympy.ntheory.factor_ import totient k = symbols('k', integer=True) t = totient(k) assert _test_args(t) sympy-0.7.4.1/sympy/core/tests/test_complex.py0000644000175000017500000005117212253362407021605 0ustar georgeskgeorgeskfrom sympy import (S, Symbol, sqrt, I, Integer, Rational, cos, sin, im, re, Abs, exp, sinh, cosh, tan, tanh, conjugate, sign, cot, coth, pi, symbols, expand_complex) from sympy.utilities.pytest import XFAIL def test_complex(): a = Symbol("a", real=True) b = Symbol("b", real=True) e = (a + I*b)*(a - I*b) assert e.expand() == a**2 + b**2 assert sqrt(I) == (-1)**Rational(1, 4) def test_conjugate(): a = Symbol("a", real=True) b = Symbol("b", real=True) c = Symbol("c", imaginary=True) d = Symbol("d", imaginary=True) x = Symbol('x') z = a + I*b + c + I*d zc = a - I*b - c + I*d assert conjugate(z) == zc assert conjugate(exp(z)) == exp(zc) assert conjugate(exp(I*x)) == exp(-I*conjugate(x)) assert conjugate(z**5) == zc**5 assert conjugate(abs(x)) == abs(x) assert conjugate(sign(z)) == sign(zc) assert conjugate(sin(z)) == sin(zc) assert conjugate(cos(z)) == cos(zc) assert conjugate(tan(z)) == tan(zc) assert conjugate(cot(z)) == cot(zc) assert conjugate(sinh(z)) == sinh(zc) assert conjugate(cosh(z)) == cosh(zc) assert conjugate(tanh(z)) == tanh(zc) assert conjugate(coth(z)) == coth(zc) def test_abs1(): a = Symbol("a", real=True) b = Symbol("b", real=True) assert abs(a) == abs(a) assert abs(-a) == abs(a) assert abs(a + I*b) == sqrt(a**2 + b**2) def test_abs2(): a = Symbol("a", real=False) b = Symbol("b", real=False) assert abs(a) != a assert abs(-a) != a assert abs(a + I*b) != sqrt(a**2 + b**2) def test_evalc(): x = Symbol("x", real=True) y = Symbol("y", real=True) z = Symbol("z") assert ((x + I*y)**2).expand(complex=True) == x**2 + 2*I*x*y - y**2 assert expand_complex(z**(2*I)) == (re((re(z) + I*im(z))**(2*I)) + I*im((re(z) + I*im(z))**(2*I))) assert expand_complex( z**(2*I), deep=False) == I*im(z**(2*I)) + re(z**(2*I)) assert exp(I*x) != cos(x) + I*sin(x) assert exp(I*x).expand(complex=True) == cos(x) + I*sin(x) assert exp(I*x + y).expand(complex=True) == exp(y)*cos(x) + I*sin(x)*exp(y) assert sin(I*x).expand(complex=True) == I * sinh(x) assert sin(x + I*y).expand(complex=True) == sin(x)*cosh(y) + \ I * sinh(y) * cos(x) assert cos(I*x).expand(complex=True) == cosh(x) assert cos(x + I*y).expand(complex=True) == cos(x)*cosh(y) - \ I * sinh(y) * sin(x) assert tan(I*x).expand(complex=True) == tanh(x) * I assert tan(x + I*y).expand(complex=True) == ( (sin(x)*cos(x) + I*cosh(y)*sinh(y)) / (cos(x)**2 + sinh(y)**2)).expand() assert sinh(I*x).expand(complex=True) == I * sin(x) assert sinh(x + I*y).expand(complex=True) == sinh(x)*cos(y) + \ I * sin(y) * cosh(x) assert cosh(I*x).expand(complex=True) == cos(x) assert cosh(x + I*y).expand(complex=True) == cosh(x)*cos(y) + \ I * sin(y) * sinh(x) assert tanh(I*x).expand(complex=True) == tan(x) * I assert tanh(x + I*y).expand(complex=True) == ( (sinh(x)*cosh(x) + I*cos(y)*sin(y)) / (sinh(x)**2 + cos(y)**2)).expand() def test_pythoncomplex(): x = Symbol("x") assert 4j*x == 4*x*I assert 4j*x == 4.0*x*I assert 4.1j*x != 4*x*I def test_rootcomplex(): R = Rational assert ((+1 + I)**R(1, 2)).expand( complex=True) == 2**R(1, 4)*cos( pi/8) + 2**R(1, 4)*sin( pi/8)*I assert ((-1 - I)**R(1, 2)).expand( complex=True) == 2**R(1, 4)*cos(3*pi/8) - 2**R(1, 4)*sin(3*pi/8)*I assert (sqrt(-10)*I).as_real_imag() == (-sqrt(10), 0) def test_expand_inverse(): assert (1/(1 + I)).expand(complex=True) == (1 - I)/2 assert ((1 + 2*I)**(-2)).expand(complex=True) == (-3 - 4*I)/25 assert ((1 + I)**(-8)).expand(complex=True) == Rational(1, 16) def test_expand_complex(): assert ((2 + 3*I)**10).expand(complex=True) == -341525 - 145668*I # the following two tests are to ensure the SymPy uses an efficient # algorithm for calculating powers of complex numbers. They should execute # in something like 0.01s. assert ((2 + 3*I)**1000).expand(complex=True) == \ -81079464736246615951519029367296227340216902563389546989376269312984127074385455204551402940331021387412262494620336565547972162814110386834027871072723273110439771695255662375718498785908345629702081336606863762777939617745464755635193139022811989314881997210583159045854968310911252660312523907616129080027594310008539817935736331124833163907518549408018652090650537035647520296539436440394920287688149200763245475036722326561143851304795139005599209239350981457301460233967137708519975586996623552182807311159141501424576682074392689622074945519232029999 + \ 46938745946789557590804551905243206242164799136976022474337918748798900569942573265747576032611189047943842446167719177749107138603040963603119861476016947257034472364028585381714774667326478071264878108114128915685688115488744955550920239128462489496563930809677159214598114273887061533057125164518549173898349061972857446844052995037423459472376202251620778517659247970283904820245958198842631651569984310559418135975795868314764489884749573052997832686979294085577689571149679540256349988338406458116270429842222666345146926395233040564229555893248370000*I assert ((2 + 3*I/4)**1000).expand(complex=True) == \ Integer(1)*37079892761199059751745775382463070250205990218394308874593455293485167797989691280095867197640410033222367257278387021789651672598831503296531725827158233077451476545928116965316544607115843772405184272449644892857783761260737279675075819921259597776770965829089907990486964515784097181964312256560561065607846661496055417619388874421218472707497847700629822858068783288579581649321248495739224020822198695759609598745114438265083593711851665996586461937988748911532242908776883696631067311443171682974330675406616373422505939887984366289623091300746049101284856530270685577940283077888955692921951247230006346681086274961362500646889925803654263491848309446197554307105991537357310209426736453173441104334496173618419659521888945605315751089087820455852582920963561495787655250624781448951403353654348109893478206364632640344111022531861683064175862889459084900614967785405977231549003280842218501570429860550379522498497412180001/114813069527425452423283320117768198402231770208869520047764273682576626139237031385665948631650626991844596463898746277344711896086305533142593135616665318539129989145312280000688779148240044871428926990063486244781615463646388363947317026040466353970904996558162398808944629605623311649536164221970332681344168908984458505602379484807914058900934776500429002716706625830522008132236281291761267883317206598995396418127021779858404042159853183251540889433902091920554957783589672039160081957216630582755380425583726015528348786419432054508915275783882625175435528800822842770817965453762184851149029376 + \ I*421638390580169706973991429333213477486930178424989246669892530737775352519112934278994501272111385966211392610029433824534634841747911783746811994443436271013377059560245191441549885048056920190833693041257216263519792201852046825443439142932464031501882145407459174948712992271510309541474392303461939389368955986650538525895866713074543004916049550090364398070215427272240155060576252568700906004691224321432509053286859100920489253598392100207663785243368195857086816912514025693453058403158416856847185079684216151337200057494966741268925263085619240941610301610538225414050394612058339070756009433535451561664522479191267503989904464718368605684297071150902631208673621618217106272361061676184840810762902463998065947687814692402219182668782278472952758690939877465065070481351343206840649517150634973307937551168752642148704904383991876969408056379195860410677814566225456558230131911142229028179902418223009651437985670625/1793954211366022694113801876840128100034871409513586250746316776290259783425578615401030447369541046747571819748417910583511123376348523955353017744010395602173906080395504375010762174191250701116076984219741972574712741619474818186676828531882286780795390571221287481389759837587864244524002565968286448146002639202882164150037179450123657170327105882819203167448541028601906377066191895183769810676831353109303069033234715310287563158747705988305326397404720186258671215368588625611876280581509852855552819149745718992630449787803625851701801184123166018366180137512856918294030710215034138299203584 assert ((2 + 3*I)**-1000).expand(complex=True) == \ Integer(1)*-81079464736246615951519029367296227340216902563389546989376269312984127074385455204551402940331021387412262494620336565547972162814110386834027871072723273110439771695255662375718498785908345629702081336606863762777939617745464755635193139022811989314881997210583159045854968310911252660312523907616129080027594310008539817935736331124833163907518549408018652090650537035647520296539436440394920287688149200763245475036722326561143851304795139005599209239350981457301460233967137708519975586996623552182807311159141501424576682074392689622074945519232029999/8777125472973511649630750050295188683351430110097915876250894978429797369155961290321829625004920141758416719066805645579710744290541337680113772670040386863849283653078324415471816788604945889094925784900885812724984087843737442111926413818245854362613018058774368703971604921858023116665586358870612944209398056562604561248859926344335598822815885851096698226775053153403320782439987679978321289537645645163767251396759519805603090332694449553371530571613352311006350058217982509738362083094920649452123351717366337410243853659113315547584871655479914439219520157174729130746351059075207407866012574386726064196992865627149566238044625779078186624347183905913357718850537058578084932880569701242598663149911276357125355850792073635533676541250531086757377369962506979378337216411188347761901006460813413505861461267545723590468627854202034450569581626648934062198718362303420281555886394558137408159453103395918783625713213314350531051312551733021627153081075080140680608080529736975658786227362251632725009435866547613598753584705455955419696609282059191031962604169242974038517575645939316377801594539335940001 - Integer(1)*46938745946789557590804551905243206242164799136976022474337918748798900569942573265747576032611189047943842446167719177749107138603040963603119861476016947257034472364028585381714774667326478071264878108114128915685688115488744955550920239128462489496563930809677159214598114273887061533057125164518549173898349061972857446844052995037423459472376202251620778517659247970283904820245958198842631651569984310559418135975795868314764489884749573052997832686979294085577689571149679540256349988338406458116270429842222666345146926395233040564229555893248370000*I/8777125472973511649630750050295188683351430110097915876250894978429797369155961290321829625004920141758416719066805645579710744290541337680113772670040386863849283653078324415471816788604945889094925784900885812724984087843737442111926413818245854362613018058774368703971604921858023116665586358870612944209398056562604561248859926344335598822815885851096698226775053153403320782439987679978321289537645645163767251396759519805603090332694449553371530571613352311006350058217982509738362083094920649452123351717366337410243853659113315547584871655479914439219520157174729130746351059075207407866012574386726064196992865627149566238044625779078186624347183905913357718850537058578084932880569701242598663149911276357125355850792073635533676541250531086757377369962506979378337216411188347761901006460813413505861461267545723590468627854202034450569581626648934062198718362303420281555886394558137408159453103395918783625713213314350531051312551733021627153081075080140680608080529736975658786227362251632725009435866547613598753584705455955419696609282059191031962604169242974038517575645939316377801594539335940001 assert ((2 + 3*I/4)**-1000).expand(complex=True) == \ Integer(1)*4257256305661027385394552848555894604806501409793288342610746813288539790051927148781268212212078237301273165351052934681382567968787279534591114913777456610214738290619922068269909423637926549603264174216950025398244509039145410016404821694746262142525173737175066432954496592560621330313807235750500564940782099283410261748370262433487444897446779072067625787246390824312580440138770014838135245148574339248259670887549732495841810961088930810608893772914812838358159009303794863047635845688453859317690488124382253918725010358589723156019888846606295866740117645571396817375322724096486161308083462637370825829567578309445855481578518239186117686659177284332344643124760453112513611749309168470605289172320376911472635805822082051716625171429727162039621902266619821870482519063133136820085579315127038372190224739238686708451840610064871885616258831386810233957438253532027049148030157164346719204500373766157143311767338973363806106967439378604898250533766359989107510507493549529158818602327525235240510049484816090584478644771183158342479140194633579061295740839490629457435283873180259847394582069479062820225159699506175855369539201399183443253793905149785994830358114153241481884290274629611529758663543080724574566578220908907477622643689220814376054314972190402285121776593824615083669045183404206291739005554569305329760211752815718335731118664756831942466773261465213581616104242113894521054475516019456867271362053692785300826523328020796670205463390909136593859765912483565093461468865534470710132881677639651348709376/2103100954337624833663208713697737151593634525061637972297915388685604042449504336765884978184588688426595940401280828953096857809292320006227881797146858511436638446932833617514351442216409828605662238790280753075176269765767010004889778647709740770757817960711900340755635772183674511158570690702969774966791073165467918123298694584729211212414462628433370481195120564586361368504153395406845170075275051749019600057116719726628746724489572189061061036426955163696859127711110719502594479795200686212257570291758725259007379710596548777812659422174199194837355646482046783616494013289495563083118517507178847555801163089723056310287760875135196081975602765511153122381201303871673391366630940702817360340900568748719988954847590748960761446218262344767250783946365392689256634180417145926390656439421745644011831124277463643383712803287985472471755648426749842410972650924240795946699346613614779460399530274263580007672855851663196114585312432954432654691485867618908420370875753749297487803461900447407917655296784879220450937110470920633595689721819488638484547259978337741496090602390463594556401615298457456112485536498177883358587125449801777718900375736758266215245325999241624148841915093787519330809347240990363802360596034171167818310322276373120180985148650099673289383722502488957717848531612020897298448601714154586319660314294591620415272119454982220034319689607295960162971300417552364254983071768070124456169427638371140064235083443242844616326538396503937972586505546495649094344512270582463639152160238137952390380581401171977159154009407415523525096743009110916334144716516647041176989758534635251844947906038080852185583742296318878233394998111078843229681030277039104786225656992262073797524057992347971177720807155842376332851559276430280477639539393920006008737472164850104411971830120295750221200029811143140323763349636629725073624360001 - Integer(1)*3098214262599218784594285246258841485430681674561917573155883806818465520660668045042109232930382494608383663464454841313154390741655282039877410154577448327874989496074260116195788919037407420625081798124301494353693248757853222257918294662198297114746822817460991242508743651430439120439020484502408313310689912381846149597061657483084652685283853595100434135149479564507015504022249330340259111426799121454516345905101620532787348293877485702600390665276070250119465888154331218827342488849948540687659846652377277250614246402784754153678374932540789808703029043827352976139228402417432199779415751301480406673762521987999573209628597459357964214510139892316208670927074795773830798600837815329291912002136924506221066071242281626618211060464126372574400100990746934953437169840312584285942093951405864225230033279614235191326102697164613004299868695519642598882914862568516635347204441042798206770888274175592401790040170576311989738272102077819127459014286741435419468254146418098278519775722104890854275995510700298782146199325790002255362719776098816136732897323406228294203133323296591166026338391813696715894870956511298793595675308998014158717167429941371979636895553724830981754579086664608880698350866487717403917070872269853194118364230971216854931998642990452908852258008095741042117326241406479532880476938937997238098399302185675832474590293188864060116934035867037219176916416481757918864533515526389079998129329045569609325290897577497835388451456680707076072624629697883854217331728051953671643278797380171857920000*I/2103100954337624833663208713697737151593634525061637972297915388685604042449504336765884978184588688426595940401280828953096857809292320006227881797146858511436638446932833617514351442216409828605662238790280753075176269765767010004889778647709740770757817960711900340755635772183674511158570690702969774966791073165467918123298694584729211212414462628433370481195120564586361368504153395406845170075275051749019600057116719726628746724489572189061061036426955163696859127711110719502594479795200686212257570291758725259007379710596548777812659422174199194837355646482046783616494013289495563083118517507178847555801163089723056310287760875135196081975602765511153122381201303871673391366630940702817360340900568748719988954847590748960761446218262344767250783946365392689256634180417145926390656439421745644011831124277463643383712803287985472471755648426749842410972650924240795946699346613614779460399530274263580007672855851663196114585312432954432654691485867618908420370875753749297487803461900447407917655296784879220450937110470920633595689721819488638484547259978337741496090602390463594556401615298457456112485536498177883358587125449801777718900375736758266215245325999241624148841915093787519330809347240990363802360596034171167818310322276373120180985148650099673289383722502488957717848531612020897298448601714154586319660314294591620415272119454982220034319689607295960162971300417552364254983071768070124456169427638371140064235083443242844616326538396503937972586505546495649094344512270582463639152160238137952390380581401171977159154009407415523525096743009110916334144716516647041176989758534635251844947906038080852185583742296318878233394998111078843229681030277039104786225656992262073797524057992347971177720807155842376332851559276430280477639539393920006008737472164850104411971830120295750221200029811143140323763349636629725073624360001 a = Symbol('a', real=True) b = Symbol('b', real=True) assert exp(a*(2 + I*b)).expand(complex=True) == \ I*exp(2*a)*sin(a*b) + exp(2*a)*cos(a*b) def test_expand(): f = (16 - 2*sqrt(29))**2 assert f.expand() == 372 - 64*sqrt(29) f = (Integer(1)/2 + I/2)**10 assert f.expand() == I/32 f = (Integer(1)/2 + I)**10 assert f.expand() == Integer(237)/1024 - 779*I/256 def test_re_im1652(): x = Symbol('x') assert re(x) == re(conjugate(x)) assert im(x) == - im(conjugate(x)) assert im(x)*re(conjugate(x)) + im(conjugate(x)) * re(x) == 0 def test_issue_1985(): x = Symbol('x') assert ((x + x*I)/(1 + I)).as_real_imag() == (re((x + I*x)/(1 + I) ), im((x + I*x)/(1 + I))) def test_issue_2137(): assert (cos(1 + I)**3).as_real_imag() == (-3*sin(1)**2*sinh(1)**2*cos(1)*cosh(1) + cos(1)**3*cosh(1)**3, -3*cos(1)**2*cosh(1)**2*sin(1)*sinh(1) + sin(1)**3*sinh(1)**3) def test_real_imag(): x, y, z = symbols('x, y, z') X, Y, Z = symbols('X, Y, Z', commutative=False) a = Symbol('a', real=True) assert (2*a*x).as_real_imag() == (2*a*re(x), 2*a*im(x)) # issue 2296: assert (x*x.conjugate()).as_real_imag() == (Abs(x)**2, 0) assert im(x*x.conjugate()) == 0 assert im(x*y.conjugate()*z*y) == im(x*z)*Abs(y)**2 assert im(x*y.conjugate()*x*y) == im(x**2)*Abs(y)**2 assert im(Z*y.conjugate()*X*y) == im(Z*X)*Abs(y)**2 assert im(X*X.conjugate()) == im(X*X.conjugate(), evaluate=False) assert (sin(x)*sin(x).conjugate()).as_real_imag() == \ (Abs(sin(x))**2, 0) # issue 3474: assert (x**2).as_real_imag() == (re(x)**2 - im(x)**2, 2*re(x)*im(x)) # issue 3329: r = Symbol('r', real=True) i = Symbol('i', imaginary=True) assert (i*r*x).as_real_imag() == (I*i*r*im(x), -I*i*r*re(x)) # issue 4007: assert ((1 + I)/(1 - I)).as_real_imag() == (0, 1) assert ((1 + 2*I)*(1 + 3*I)).as_real_imag() == (-5, 5) def test_pow_issue_1724(): e = ((-1)**(S(1)/3)) assert e.conjugate().n() == e.n().conjugate() e = S('-2/3 - (-29/54 + sqrt(93)/18)**(1/3) - 1/(9*(-29/54 + sqrt(93)/18)**(1/3))') assert e.conjugate().n() == e.n().conjugate() e = 2**I assert e.conjugate().n() == e.n().conjugate() def test_issue_2330(): assert sqrt(I).conjugate() != sqrt(I) sympy-0.7.4.1/sympy/core/tests/test_operations.py0000644000175000017500000000175712253362407022325 0ustar georgeskgeorgeskfrom sympy import Integer, S from sympy.core.operations import LatticeOp from sympy.utilities.pytest import raises from sympy.core.sympify import SympifyError # create the simplest possible Lattice class class join(LatticeOp): zero = Integer(0) identity = Integer(1) def test_lattice_simple(): assert join(join(2, 3), 4) == join(2, join(3, 4)) assert join(2, 3) == join(3, 2) assert join(0, 2) == 0 assert join(1, 2) == 2 assert join(2, 2) == 2 assert join(join(2, 3), 4) == join(2, 3, 4) assert join() == 1 assert join(4) == 4 assert join(1, 4, 2, 3, 1, 3, 2) == join(2, 3, 4) def test_lattice_shortcircuit(): raises(SympifyError, lambda: join(object)) assert join(0, object) == 0 def test_lattice_print(): assert str(join(5, 4, 3, 2)) == 'join(2, 3, 4, 5)' def test_lattice_make_args(): assert join.make_args(0) == set([0]) assert join.make_args(1) == set([1]) assert join.make_args(join(2, 3, 4)) == set([S(2), S(3), S(4)]) sympy-0.7.4.1/sympy/core/tests/test_count_ops.py0000644000175000017500000000560412253362407022146 0ustar georgeskgeorgeskfrom sympy import symbols, sin, exp, cos, Derivative, Integral, Basic, \ count_ops, S, And, I, pi, Eq x, y, z = symbols('x,y,z') def test_count_ops_non_visual(): def count(val): return count_ops(val, visual=False) assert count(x) == 0 assert count(x) is not S.Zero assert count(x + y) == 1 assert count(x + y) is not S.One assert count(x + y*x + 2*y) == 4 assert count({x + y: x}) == 1 assert count({x + y: S(2) + x}) is not S.One def test_count_ops_visual(): ADD, MUL, POW, SIN, COS, EXP, AND, D, G = symbols( 'Add Mul Pow sin cos exp And Derivative Integral'.upper()) DIV, SUB, NEG = symbols('DIV SUB NEG') def count(val): return count_ops(val, visual=True) assert count(7) is S.Zero assert count(S(7)) is S.Zero assert count(-1) == NEG assert count(-2) == NEG assert count(S(2)/3) == DIV assert count(pi/3) == DIV assert count(-pi/3) == DIV + NEG assert count(I - 1) == SUB assert count(1 - I) == SUB assert count(1 - 2*I) == SUB + MUL assert count(x) is S.Zero assert count(-x) == NEG assert count(-2*x/3) == NEG + DIV + MUL assert count(1/x) == DIV assert count(1/(x*y)) == DIV + MUL assert count(-1/x) == NEG + DIV assert count(-2/x) == NEG + DIV assert count(x/y) == DIV assert count(-x/y) == NEG + DIV assert count(x**2) == POW assert count(-x**2) == POW + NEG assert count(-2*x**2) == POW + MUL + NEG assert count(x + pi/3) == ADD + DIV assert count(x + S(1)/3) == ADD + DIV assert count(x + y) == ADD assert count(x - y) == SUB assert count(y - x) == SUB assert count(-1/(x - y)) == DIV + NEG + SUB assert count(-1/(y - x)) == DIV + NEG + SUB assert count(1 + x**y) == ADD + POW assert count(1 + x + y) == 2*ADD assert count(1 + x + y + z) == 3*ADD assert count(1 + x**y + 2*x*y + y**2) == 3*ADD + 2*POW + 2*MUL assert count(2*z + y + x + 1) == 3*ADD + MUL assert count(2*z + y**17 + x + 1) == 3*ADD + MUL + POW assert count(2*z + y**17 + x + sin(x)) == 3*ADD + POW + MUL + SIN assert count(2*z + y**17 + x + sin(x**2)) == 3*ADD + MUL + 2*POW + SIN assert count(2*z + y**17 + x + sin( x**2) + exp(cos(x))) == 4*ADD + MUL + 2*POW + EXP + COS + SIN assert count(Derivative(x, x)) == D assert count(Integral(x, x) + 2*x/(1 + x)) == G + DIV + MUL + 2*ADD assert count(Basic()) is S.Zero assert count({x + 1: sin(x)}) == ADD + SIN assert count([x + 1, sin(x) + y, None]) == ADD + SIN + ADD assert count({x + 1: sin(x), y: cos(x) + 1}) == SIN + COS + 2*ADD assert count({}) is S.Zero assert count([x + 1, sin(x)*y, None]) == SIN + ADD + MUL assert count([]) is S.Zero # XXX: These are a bit surprising, only Expr-compatible ops are counted. assert count(And(x, y, z)) == 0 assert count(Basic(x, x + y)) == ADD assert count(Eq(x + y, S(2))) == ADD sympy-0.7.4.1/sympy/core/tests/test_basic.py0000644000175000017500000001122712253362407021214 0ustar georgeskgeorgesk"""This tests sympy/core/basic.py with (ideally) no reference to subclasses of Basic or Atom.""" from sympy.core.basic import Basic, Atom, preorder_traversal from sympy.core.singleton import S, Singleton from sympy.core.symbol import symbols from sympy.core.compatibility import default_sort_key, with_metaclass from sympy import sin, Lambda, Q from sympy.utilities.pytest import raises b1 = Basic() b2 = Basic(b1) b3 = Basic(b2) b21 = Basic(b2, b1) def test_structure(): assert b21.args == (b2, b1) assert tuple(b21.iter_basic_args()) == b21.args assert b21.func(*b21.args) == b21 assert bool(b1) def test_equality(): instances = [b1, b2, b3, b21, Basic(b1, b1, b1), Basic] for i, b_i in enumerate(instances): for j, b_j in enumerate(instances): assert (b_i == b_j) == (i == j) assert (b_i != b_j) == (i != j) assert Basic() != [] assert not(Basic() == []) assert Basic() != 0 assert not(Basic() == 0) def test_matches_basic(): instances = [Basic(b1, b1, b2), Basic(b1, b2, b1), Basic(b2, b1, b1), Basic(b1, b2), Basic(b2, b1), b2, b1] for i, b_i in enumerate(instances): for j, b_j in enumerate(instances): if i == j: assert b_i.matches(b_j) == {} else: assert b_i.matches(b_j) is None assert b1.match(b1) == {} def test_has(): assert b21.has(b1) assert b21.has(b3, b1) assert b21.has(Basic) assert not b1.has(b21, b3) assert not b21.has() def test_subs(): assert b21.subs(b2, b1) == Basic(b1, b1) assert b21.subs(b2, b21) == Basic(b21, b1) assert b3.subs(b2, b1) == b2 assert b21.subs([(b2, b1), (b1, b2)]) == Basic(b2, b2) assert b21.subs({b1: b2, b2: b1}) == Basic(b2, b2) raises(ValueError, lambda: b21.subs('bad arg')) raises(ValueError, lambda: b21.subs(b1, b2, b3)) def test_atoms(): assert b21.atoms() == set() def test_free_symbols_empty(): assert b21.free_symbols == set() def test_doit(): assert b21.doit() == b21 assert b21.doit(deep=False) == b21 def test_S(): assert repr(S) == 'S' def test_xreplace(): assert b21.xreplace({b2: b1}) == Basic(b1, b1) assert b21.xreplace({b2: b21}) == Basic(b21, b1) assert b3.xreplace({b2: b1}) == b2 assert Basic(b1, b2).xreplace({b1: b2, b2: b1}) == Basic(b2, b1) assert Atom(b1).xreplace({b1: b2}) == Atom(b1) assert Atom(b1).xreplace({Atom(b1): b2}) == b2 raises(TypeError, lambda: b1.xreplace()) raises(TypeError, lambda: b1.xreplace([b1, b2])) def test_Singleton(): global instantiated instantiated = 0 class MySingleton(with_metaclass(Singleton, Basic)): def __new__(cls): global instantiated instantiated += 1 return Basic.__new__(cls) assert instantiated == 1 assert MySingleton() is not Basic() assert MySingleton() is MySingleton() assert S.MySingleton is MySingleton() assert instantiated == 1 class MySingleton_sub(MySingleton): pass assert instantiated == 2 assert MySingleton_sub() is not MySingleton() assert MySingleton_sub() is MySingleton_sub() def test_preorder_traversal(): expr = Basic(b21, b3) assert list( preorder_traversal(expr)) == [expr, b21, b2, b1, b1, b3, b2, b1] assert list(preorder_traversal(('abc', ('d', 'ef')))) == [ ('abc', ('d', 'ef')), 'abc', ('d', 'ef'), 'd', 'ef'] result = [] pt = preorder_traversal(expr) for i in pt: result.append(i) if i == b2: pt.skip() assert result == [expr, b21, b2, b1, b3, b2] w, x, y, z = symbols('w:z') expr = z + w*(x + y) assert list(preorder_traversal([expr], keys=default_sort_key)) == \ [[w*(x + y) + z], w*(x + y) + z, z, w*(x + y), w, x + y, x, y] assert list(preorder_traversal((x + y)*z, keys=True)) == \ [z*(x + y), z, x + y, x, y] def test_sorted_args(): x = symbols('x') assert b21._sorted_args == b21.args raises(AttributeError, lambda: x._sorted_args) def test_call(): x, y = symbols('x y') # See the long history of this in issues 1927 and 2006. raises(TypeError, lambda: sin(x)({ x : 1, sin(x) : 2})) raises(TypeError, lambda: sin(x)(1)) # No effect as there are no callables assert sin(x).rcall(1) == sin(x) assert (1 + sin(x)).rcall(1) == 1 + sin(x) # Effect in the pressence of callables l = Lambda(x, 2*x) assert (l + x).rcall(y) == 2*y + x assert (x**l).rcall(2) == x**4 # TODO UndefinedFunction does not subclass Expr #f = Function('f') #assert (2*f)(x) == 2*f(x) assert (Q.real & Q.positive).rcall(x) == Q.real(x) & Q.positive(x) sympy-0.7.4.1/sympy/core/tests/test_expr.py0000644000175000017500000015152112253362407021113 0ustar georgeskgeorgeskfrom __future__ import division from sympy import (Add, Basic, S, Symbol, Wild, Float, Integer, Rational, I, sin, cos, tan, exp, log, nan, oo, sqrt, symbols, Integral, sympify, WildFunction, Poly, Function, Derivative, Number, pi, NumberSymbol, zoo, Piecewise, Mul, Pow, nsimplify, ratsimp, trigsimp, radsimp, powsimp, simplify, together, collect, factorial, apart, combsimp, factor, refine, cancel, Tuple, default_sort_key, DiracDelta, gamma, Dummy, Sum, E, exp_polar, Lambda, expand, diff, O) from sympy.core.function import AppliedUndef from sympy.physics.secondquant import FockState from sympy.physics.units import meter from sympy.core.compatibility import xrange from sympy.utilities.pytest import raises, XFAIL from sympy.abc import a, b, c, n, t, u, x, y, z class DummyNumber(object): """ Minimal implementation of a number that works with SymPy. If one has a Number class (e.g. Sage Integer, or some other custom class) that one wants to work well with SymPy, one has to implement at least the methods of this class DummyNumber, resp. its subclasses I5 and F1_1. Basically, one just needs to implement either __int__() or __float__() and then one needs to make sure that the class works with Python integers and with itself. """ def __radd__(self, a): if isinstance(a, (int, float)): return a + self.number return NotImplemented def __truediv__(a, b): return a.__div__(b) def __rtruediv__(a, b): return a.__rdiv__(b) def __add__(self, a): if isinstance(a, (int, float, DummyNumber)): return self.number + a return NotImplemented def __rsub__(self, a): if isinstance(a, (int, float)): return a - self.number return NotImplemented def __sub__(self, a): if isinstance(a, (int, float, DummyNumber)): return self.number - a return NotImplemented def __rmul__(self, a): if isinstance(a, (int, float)): return a * self.number return NotImplemented def __mul__(self, a): if isinstance(a, (int, float, DummyNumber)): return self.number * a return NotImplemented def __rdiv__(self, a): if isinstance(a, (int, float)): return a / self.number return NotImplemented def __div__(self, a): if isinstance(a, (int, float, DummyNumber)): return self.number / a return NotImplemented def __rpow__(self, a): if isinstance(a, (int, float)): return a ** self.number return NotImplemented def __pow__(self, a): if isinstance(a, (int, float, DummyNumber)): return self.number ** a return NotImplemented def __pos__(self): return self.number def __neg__(self): return - self.number class I5(DummyNumber): number = 5 def __int__(self): return self.number class F1_1(DummyNumber): number = 1.1 def __float__(self): return self.number i5 = I5() f1_1 = F1_1() # basic sympy objects basic_objs = [ Rational(2), Float("1.3"), x, y, pow(x, y)*y, ] # all supported objects all_objs = basic_objs + [ 5, 5.5, i5, f1_1 ] def dotest(s): for x in all_objs: for y in all_objs: s(x, y) return True def test_basic(): def j(a, b): x = a x = +a x = -a x = a + b x = a - b x = a*b x = a/b x = a**b assert dotest(j) def test_ibasic(): def s(a, b): x = a x += b x = a x -= b x = a x *= b x = a x /= b assert dotest(s) def test_relational(): assert (pi < 3) is False assert (pi <= 3) is False assert (pi > 3) is True assert (pi >= 3) is True assert (-pi < 3) is True assert (-pi <= 3) is True assert (-pi > 3) is False assert (-pi >= 3) is False assert (x - 2 < x - 3) is False def test_relational_assumptions(): from sympy import Lt, Gt, Le, Ge m1 = Symbol("m1", nonnegative=False) m2 = Symbol("m2", positive=False) m3 = Symbol("m3", nonpositive=False) m4 = Symbol("m4", negative=False) assert (m1 < 0) == Lt(m1, 0) assert (m2 <= 0) == Le(m2, 0) assert (m3 > 0) == Gt(m3, 0) assert (m4 >= 0) == Ge(m4, 0) m1 = Symbol("m1", nonnegative=False, real=True) m2 = Symbol("m2", positive=False, real=True) m3 = Symbol("m3", nonpositive=False, real=True) m4 = Symbol("m4", negative=False, real=True) assert (m1 < 0) is True assert (m2 <= 0) is True assert (m3 > 0) is True assert (m4 >= 0) is True m1 = Symbol("m1", negative=True) m2 = Symbol("m2", nonpositive=True) m3 = Symbol("m3", positive=True) m4 = Symbol("m4", nonnegative=True) assert (m1 < 0) is True assert (m2 <= 0) is True assert (m3 > 0) is True assert (m4 >= 0) is True m1 = Symbol("m1", negative=False) m2 = Symbol("m2", nonpositive=False) m3 = Symbol("m3", positive=False) m4 = Symbol("m4", nonnegative=False) assert (m1 < 0) is False assert (m2 <= 0) is False assert (m3 > 0) is False assert (m4 >= 0) is False def test_relational_noncommutative(): from sympy import Lt, Gt, Le, Ge A, B = symbols('A,B', commutative=False) assert (A < B) == Lt(A, B) assert (A <= B) == Le(A, B) assert (A > B) == Gt(A, B) assert (A >= B) == Ge(A, B) def test_basic_nostr(): for obj in basic_objs: raises(TypeError, lambda: obj + '1') raises(TypeError, lambda: obj - '1') if obj == 2: assert obj * '1' == '11' else: raises(TypeError, lambda: obj * '1') raises(TypeError, lambda: obj / '1') raises(TypeError, lambda: obj ** '1') def test_series_expansion_for_uniform_order(): assert (1/x + y + x).series(x, 0, 0) == 1/x + O(1, x) assert (1/x + y + x).series(x, 0, 1) == 1/x + y + O(x) assert (1/x + 1 + x).series(x, 0, 0) == 1/x + O(1, x) assert (1/x + 1 + x).series(x, 0, 1) == 1/x + 1 + O(x) assert (1/x + x).series(x, 0, 0) == 1/x + O(1, x) assert (1/x + y + y*x + x).series(x, 0, 0) == 1/x + O(1, x) assert (1/x + y + y*x + x).series(x, 0, 1) == 1/x + y + O(x) def test_leadterm(): assert (3 + 2*x**(log(3)/log(2) - 1)).leadterm(x) == (3, 0) assert (1/x**2 + 1 + x + x**2).leadterm(x)[1] == -2 assert (1/x + 1 + x + x**2).leadterm(x)[1] == -1 assert (x**2 + 1/x).leadterm(x)[1] == -1 assert (1 + x**2).leadterm(x)[1] == 0 assert (x + 1).leadterm(x)[1] == 0 assert (x + x**2).leadterm(x)[1] == 1 assert (x**2).leadterm(x)[1] == 2 def test_as_leading_term(): assert (3 + 2*x**(log(3)/log(2) - 1)).as_leading_term(x) == 3 assert (1/x**2 + 1 + x + x**2).as_leading_term(x) == 1/x**2 assert (1/x + 1 + x + x**2).as_leading_term(x) == 1/x assert (x**2 + 1/x).as_leading_term(x) == 1/x assert (1 + x**2).as_leading_term(x) == 1 assert (x + 1).as_leading_term(x) == 1 assert (x + x**2).as_leading_term(x) == x assert (x**2).as_leading_term(x) == x**2 assert (x + oo).as_leading_term(x) == oo def test_leadterm2(): assert (x*cos(1)*cos(1 + sin(1)) + sin(1 + sin(1))).leadterm(x) == \ (sin(1 + sin(1)), 0) def test_leadterm3(): assert (y + z + x).leadterm(x) == (y + z, 0) def test_as_leading_term2(): assert (x*cos(1)*cos(1 + sin(1)) + sin(1 + sin(1))).as_leading_term(x) == \ sin(1 + sin(1)) def test_as_leading_term3(): assert (2 + pi + x).as_leading_term(x) == 2 + pi assert (2*x + pi*x + x**2).as_leading_term(x) == (2 + pi)*x def test_as_leading_term4(): # see issue 3744 n = Symbol('n', integer=True, positive=True) r = -n**3/(2*n**2 + 4*n + 2) - n**2/(n**2 + 2*n + 1) + \ n**2/(n + 1) - n/(2*n**2 + 4*n + 2) + n/(n*x + x) + 2*n/(n + 1) - \ 1 + 1/(n*x + x) + 1/(n + 1) - 1/x assert r.as_leading_term(x).cancel() == n/2 def test_as_leading_term_stub(): class foo(Function): pass assert foo(1/x).as_leading_term(x) == foo(1/x) assert foo(1).as_leading_term(x) == foo(1) raises(NotImplementedError, lambda: foo(x).as_leading_term(x)) def test_atoms(): assert x.atoms() == set([x]) assert (1 + x).atoms() == set([x, S(1)]) assert (1 + 2*cos(x)).atoms(Symbol) == set([x]) assert (1 + 2*cos(x)).atoms(Symbol, Number) == set([S(1), S(2), x]) assert (2*(x**(y**x))).atoms() == set([S(2), x, y]) assert Rational(1, 2).atoms() == set([S.Half]) assert Rational(1, 2).atoms(Symbol) == set([]) assert sin(oo).atoms(oo) == set([oo]) assert Poly(0, x).atoms() == set([S.Zero]) assert Poly(1, x).atoms() == set([S.One]) assert Poly(x, x).atoms() == set([x]) assert Poly(x, x, y).atoms() == set([x]) assert Poly(x + y, x, y).atoms() == set([x, y]) assert Poly(x + y, x, y, z).atoms() == set([x, y]) assert Poly(x + y*t, x, y, z).atoms() == set([t, x, y]) assert (I*pi).atoms(NumberSymbol) == set([pi]) assert (I*pi).atoms(NumberSymbol, I) == \ (I*pi).atoms(I, NumberSymbol) == set([pi, I]) assert exp(exp(x)).atoms(exp) == set([exp(exp(x)), exp(x)]) assert (1 + x*(2 + y) + exp(3 + z)).atoms(Add) == \ set([1 + x*(2 + y) + exp(3 + z), 2 + y, 3 + z]) # issue 3033 f = Function('f') e = (f(x) + sin(x) + 2) assert e.atoms(AppliedUndef) == \ set([f(x)]) assert e.atoms(AppliedUndef, Function) == \ set([f(x), sin(x)]) assert e.atoms(Function) == \ set([f(x), sin(x)]) assert e.atoms(AppliedUndef, Number) == \ set([f(x), S(2)]) assert e.atoms(Function, Number) == \ set([S(2), sin(x), f(x)]) def test_is_polynomial(): k = Symbol('k', nonnegative=True, integer=True) assert Rational(2).is_polynomial(x, y, z) is True assert (S.Pi).is_polynomial(x, y, z) is True assert x.is_polynomial(x) is True assert x.is_polynomial(y) is True assert (x**2).is_polynomial(x) is True assert (x**2).is_polynomial(y) is True assert (x**(-2)).is_polynomial(x) is False assert (x**(-2)).is_polynomial(y) is True assert (2**x).is_polynomial(x) is False assert (2**x).is_polynomial(y) is True assert (x**k).is_polynomial(x) is False assert (x**k).is_polynomial(k) is False assert (x**x).is_polynomial(x) is False assert (k**k).is_polynomial(k) is False assert (k**x).is_polynomial(k) is False assert (x**(-k)).is_polynomial(x) is False assert ((2*x)**k).is_polynomial(x) is False assert (x**2 + 3*x - 8).is_polynomial(x) is True assert (x**2 + 3*x - 8).is_polynomial(y) is True assert (x**2 + 3*x - 8).is_polynomial() is True assert sqrt(x).is_polynomial(x) is False assert (sqrt(x)**3).is_polynomial(x) is False assert (x**2 + 3*x*sqrt(y) - 8).is_polynomial(x) is True assert (x**2 + 3*x*sqrt(y) - 8).is_polynomial(y) is False assert ((x**2)*(y**2) + x*(y**2) + y*x + exp(2)).is_polynomial() is True assert ((x**2)*(y**2) + x*(y**2) + y*x + exp(x)).is_polynomial() is False assert ( (x**2)*(y**2) + x*(y**2) + y*x + exp(2)).is_polynomial(x, y) is True assert ( (x**2)*(y**2) + x*(y**2) + y*x + exp(x)).is_polynomial(x, y) is False def test_is_rational_function(): assert Integer(1).is_rational_function() is True assert Integer(1).is_rational_function(x) is True assert Rational(17, 54).is_rational_function() is True assert Rational(17, 54).is_rational_function(x) is True assert (12/x).is_rational_function() is True assert (12/x).is_rational_function(x) is True assert (x/y).is_rational_function() is True assert (x/y).is_rational_function(x) is True assert (x/y).is_rational_function(x, y) is True assert (x**2 + 1/x/y).is_rational_function() is True assert (x**2 + 1/x/y).is_rational_function(x) is True assert (x**2 + 1/x/y).is_rational_function(x, y) is True assert (sin(y)/x).is_rational_function() is False assert (sin(y)/x).is_rational_function(y) is False assert (sin(y)/x).is_rational_function(x) is True assert (sin(y)/x).is_rational_function(x, y) is False def test_is_algebraic_expr(): assert sqrt(3).is_algebraic_expr(x) is True assert sqrt(3).is_algebraic_expr() is True eq = ((1 + x**2)/(1 - y**2))**(S(1)/3) assert eq.is_algebraic_expr(x) is True assert eq.is_algebraic_expr(y) is True assert (sqrt(x) + y**(S(2)/3)).is_algebraic_expr(x) is True assert (sqrt(x) + y**(S(2)/3)).is_algebraic_expr(y) is True assert (sqrt(x) + y**(S(2)/3)).is_algebraic_expr() is True assert (cos(y)/sqrt(x)).is_algebraic_expr() is False assert (cos(y)/sqrt(x)).is_algebraic_expr(x) is True assert (cos(y)/sqrt(x)).is_algebraic_expr(y) is False assert (cos(y)/sqrt(x)).is_algebraic_expr(x, y) is False def test_SAGE1(): #see http://code.google.com/p/sympy/issues/detail?id=247 class MyInt: def _sympy_(self): return Integer(5) m = MyInt() e = Rational(2)*m assert e == 10 raises(TypeError, lambda: Rational(2)*MyInt) def test_SAGE2(): class MyInt(object): def __int__(self): return 5 assert sympify(MyInt()) == 5 e = Rational(2)*MyInt() assert e == 10 raises(TypeError, lambda: Rational(2)*MyInt) def test_SAGE3(): class MySymbol: def __rmul__(self, other): return ('mys', other, self) o = MySymbol() e = x*o assert e == ('mys', x, o) def test_len(): e = x*y assert len(e.args) == 2 e = x + y + z assert len(e.args) == 3 def test_doit(): a = Integral(x**2, x) assert isinstance(a.doit(), Integral) is False assert isinstance(a.doit(integrals=True), Integral) is False assert isinstance(a.doit(integrals=False), Integral) is True assert (2*Integral(x, x)).doit() == x**2 def test_attribute_error(): raises(AttributeError, lambda: x.cos()) raises(AttributeError, lambda: x.sin()) raises(AttributeError, lambda: x.exp()) def test_args(): assert (x*y).args in ((x, y), (y, x)) assert (x + y).args in ((x, y), (y, x)) assert (x*y + 1).args in ((x*y, 1), (1, x*y)) assert sin(x*y).args == (x*y,) assert sin(x*y).args[0] == x*y assert (x**y).args == (x, y) assert (x**y).args[0] == x assert (x**y).args[1] == y def test_iter_basic_args(): assert list(sin(x*y).iter_basic_args()) == [x*y] assert list((x**y).iter_basic_args()) == [x, y] def test_noncommutative_expand_issue658(): A, B, C = symbols('A,B,C', commutative=False) assert A*B - B*A != 0 assert (A*(A + B)*B).expand() == A**2*B + A*B**2 assert (A*(A + B + C)*B).expand() == A**2*B + A*B**2 + A*C*B def test_as_numer_denom(): a, b, c = symbols('a, b, c') assert nan.as_numer_denom() == (nan, 1) assert oo.as_numer_denom() == (oo, 1) assert (-oo).as_numer_denom() == (-oo, 1) assert zoo.as_numer_denom() == (zoo, 1) assert (-zoo).as_numer_denom() == (zoo, 1) assert x.as_numer_denom() == (x, 1) assert (1/x).as_numer_denom() == (1, x) assert (x/y).as_numer_denom() == (x, y) assert (x/2).as_numer_denom() == (x, 2) assert (x*y/z).as_numer_denom() == (x*y, z) assert (x/(y*z)).as_numer_denom() == (x, y*z) assert Rational(1, 2).as_numer_denom() == (1, 2) assert (1/y**2).as_numer_denom() == (1, y**2) assert (x/y**2).as_numer_denom() == (x, y**2) assert ((x**2 + 1)/y).as_numer_denom() == (x**2 + 1, y) assert (x*(y + 1)/y**7).as_numer_denom() == (x*(y + 1), y**7) assert (x**-2).as_numer_denom() == (1, x**2) assert (a/x + b/2/x + c/3/x).as_numer_denom() == \ (6*a + 3*b + 2*c, 6*x) assert (a/x + b/2/x + c/3/y).as_numer_denom() == \ (2*c*x + y*(6*a + 3*b), 6*x*y) assert (a/x + b/2/x + c/.5/x).as_numer_denom() == \ (2*a + b + 4.0*c, 2*x) # this should take no more than a few seconds assert int(log(Add(*[Dummy()/i/x for i in xrange(1, 705)] ).as_numer_denom()[1]/x).n(4)) == 705 for i in [S.Infinity, S.NegativeInfinity, S.ComplexInfinity]: assert (i + x/3).as_numer_denom() == \ (x + i, 3) assert (S.Infinity + x/3 + y/4).as_numer_denom() == \ (4*x + 3*y + S.Infinity, 12) assert (oo*x + zoo*y).as_numer_denom() == \ (zoo*y + oo*x, 1) A, B, C = symbols('A,B,C', commutative=False) assert (A*B*C**-1).as_numer_denom() == (A*B*C**-1, 1) assert (A*B*C**-1/x).as_numer_denom() == (A*B*C**-1, x) assert (C**-1*A*B).as_numer_denom() == (C**-1*A*B, 1) assert (C**-1*A*B/x).as_numer_denom() == (C**-1*A*B, x) assert ((A*B*C)**-1).as_numer_denom() == ((A*B*C)**-1, 1) assert ((A*B*C)**-1/x).as_numer_denom() == ((A*B*C)**-1, x) def test_as_independent(): assert (2*x*sin(x) + y + x).as_independent(x) == (y, x + 2*x*sin(x)) assert (2*x*sin(x) + y + x).as_independent(y) == (x + 2*x*sin(x), y) assert (2*x*sin(x) + y + x).as_independent(x, y) == (0, y + x + 2*x*sin(x)) assert (x*sin(x)*cos(y)).as_independent(x) == (cos(y), x*sin(x)) assert (x*sin(x)*cos(y)).as_independent(y) == (x*sin(x), cos(y)) assert (x*sin(x)*cos(y)).as_independent(x, y) == (1, x*sin(x)*cos(y)) assert (sin(x)).as_independent(x) == (1, sin(x)) assert (sin(x)).as_independent(y) == (sin(x), 1) assert (2*sin(x)).as_independent(x) == (2, sin(x)) assert (2*sin(x)).as_independent(y) == (2*sin(x), 1) # issue 1804 = 1766b n1, n2, n3 = symbols('n1 n2 n3', commutative=False) assert (n1 + n1*n2).as_independent(n2) == (n1, n1*n2) assert (n2*n1 + n1*n2).as_independent(n2) == (0, n1*n2 + n2*n1) assert (n1*n2*n1).as_independent(n2) == (n1, n2*n1) assert (n1*n2*n1).as_independent(n1) == (1, n1*n2*n1) assert (3*x).as_independent(x, as_Add=True) == (0, 3*x) assert (3*x).as_independent(x, as_Add=False) == (3, x) assert (3 + x).as_independent(x, as_Add=True) == (3, x) assert (3 + x).as_independent(x, as_Add=False) == (1, 3 + x) # issue 2380 assert (3*x).as_independent(Symbol) == (3, x) # issue 2549 assert (n1*x*y).as_independent(x) == (n1*y, x) assert ((x + n1)*(x - y)).as_independent(x) == (1, (x + n1)*(x - y)) assert ((x + n1)*(x - y)).as_independent(y) == (x + n1, x - y) assert (DiracDelta(x - n1)*DiracDelta(x - y)).as_independent(x) \ == (1, DiracDelta(x - n1)*DiracDelta(x - y)) assert (x*y*n1*n2*n3).as_independent(n2) == (x*y*n1, n2*n3) assert (x*y*n1*n2*n3).as_independent(n1) == (x*y, n1*n2*n3) assert (x*y*n1*n2*n3).as_independent(n3) == (x*y*n1*n2, n3) assert (DiracDelta(x - n1)*DiracDelta(y - n1)*DiracDelta(x - n2)).as_independent(y) == \ (DiracDelta(x - n1)*DiracDelta(x - n2), DiracDelta(y - n1)) # issue 2685 assert (x + Integral(x, (x, 1, 2))).as_independent(x, strict=True) == \ (Integral(x, (x, 1, 2)), x) def test_replace(): f = log(sin(x)) + tan(sin(x**2)) assert f.replace(sin, cos) == log(cos(x)) + tan(cos(x**2)) assert f.replace( sin, lambda a: sin(2*a)) == log(sin(2*x)) + tan(sin(2*x**2)) a = Wild('a') b = Wild('b') assert f.replace(sin(a), cos(a)) == log(cos(x)) + tan(cos(x**2)) assert f.replace( sin(a), lambda a: sin(2*a)) == log(sin(2*x)) + tan(sin(2*x**2)) # test exact assert (2*x).replace(a*x + b, b - a, exact=True) == 2*x assert (2*x).replace(a*x + b, b - a) == 2/x assert (2*x).replace(a*x + b, lambda a, b: b - a, exact=True) == 2*x assert (2*x).replace(a*x + b, lambda a, b: b - a) == 2/x g = 2*sin(x**3) assert g.replace( lambda expr: expr.is_Number, lambda expr: expr**2) == 4*sin(x**9) assert cos(x).replace(cos, sin, map=True) == (sin(x), {cos(x): sin(x)}) assert sin(x).replace(cos, sin) == sin(x) cond, func = lambda x: x.is_Mul, lambda x: 2*x assert (x*y).replace(cond, func, map=True) == (2*x*y, {x*y: 2*x*y}) assert (x*(1 + x*y)).replace(cond, func, map=True) == \ (2*x*(2*x*y + 1), {x*(2*x*y + 1): 2*x*(2*x*y + 1), x*y: 2*x*y}) assert (y*sin(x)).replace(sin, lambda expr: sin(expr)/y, map=True) == \ (sin(x), {sin(x): sin(x)/y}) # if not simultaneous then y*sin(x) -> y*sin(x)/y = sin(x) -> sin(x)/y assert (y*sin(x)).replace(sin, lambda expr: sin(expr)/y, simultaneous=False) == sin(x)/y assert (x**2 + O(x**3)).replace(Pow, lambda b, e: b**e/e) == O(1, x) assert (x**2 + O(x**3)).replace(Pow, lambda b, e: b**e/e, simultaneous=False) == x**2/2 + O(x**3) assert (x*(x*y + 3)).replace(lambda x: x.is_Mul, lambda x: 2 + x) == \ x*(x*y + 5) + 2 e = (x*y + 1)*(2*x*y + 1) + 1 assert e.replace(cond, func, map=True) == ( 2*((2*x*y + 1)*(4*x*y + 1)) + 1, {2*x*y: 4*x*y, x*y: 2*x*y, (2*x*y + 1)*(4*x*y + 1): 2*((2*x*y + 1)*(4*x*y + 1))}) assert x.replace(x, y) == y assert (x + 1).replace(1, 2) == x + 2 def test_find(): expr = (x + y + 2 + sin(3*x)) assert expr.find(lambda u: u.is_Integer) == set([S(2), S(3)]) assert expr.find(lambda u: u.is_Symbol) == set([x, y]) assert expr.find(lambda u: u.is_Integer, group=True) == {S(2): 1, S(3): 1} assert expr.find(lambda u: u.is_Symbol, group=True) == {x: 2, y: 1} assert expr.find(Integer) == set([S(2), S(3)]) assert expr.find(Symbol) == set([x, y]) assert expr.find(Integer, group=True) == {S(2): 1, S(3): 1} assert expr.find(Symbol, group=True) == {x: 2, y: 1} a = Wild('a') expr = sin(sin(x)) + sin(x) + cos(x) + x assert expr.find(lambda u: type(u) is sin) == set([sin(x), sin(sin(x))]) assert expr.find( lambda u: type(u) is sin, group=True) == {sin(x): 2, sin(sin(x)): 1} assert expr.find(sin(a)) == set([sin(x), sin(sin(x))]) assert expr.find(sin(a), group=True) == {sin(x): 2, sin(sin(x)): 1} assert expr.find(sin) == set([sin(x), sin(sin(x))]) assert expr.find(sin, group=True) == {sin(x): 2, sin(sin(x)): 1} def test_count(): expr = (x + y + 2 + sin(3*x)) assert expr.count(lambda u: u.is_Integer) == 2 assert expr.count(lambda u: u.is_Symbol) == 3 assert expr.count(Integer) == 2 assert expr.count(Symbol) == 3 assert expr.count(2) == 1 a = Wild('a') assert expr.count(sin) == 1 assert expr.count(sin(a)) == 1 assert expr.count(lambda u: type(u) is sin) == 1 def test_has_basics(): f = Function('f') g = Function('g') p = Wild('p') assert sin(x).has(x) assert sin(x).has(sin) assert not sin(x).has(y) assert not sin(x).has(cos) assert f(x).has(x) assert f(x).has(f) assert not f(x).has(y) assert not f(x).has(g) assert f(x).diff(x).has(x) assert f(x).diff(x).has(f) assert f(x).diff(x).has(Derivative) assert not f(x).diff(x).has(y) assert not f(x).diff(x).has(g) assert not f(x).diff(x).has(sin) assert (x**2).has(Symbol) assert not (x**2).has(Wild) assert (2*p).has(Wild) assert not x.has() def test_has_multiple(): f = x**2*y + sin(2**t + log(z)) assert f.has(x) assert f.has(y) assert f.has(z) assert f.has(t) assert not f.has(u) assert f.has(x, y, z, t) assert f.has(x, y, z, t, u) i = Integer(4400) assert not i.has(x) assert (i*x**i).has(x) assert not (i*y**i).has(x) assert (i*y**i).has(x, y) assert not (i*y**i).has(x, z) def test_has_piecewise(): f = (x*y + 3/y)**(3 + 2) g = Function('g') h = Function('h') p = Piecewise((g(x), x < -1), (1, x <= 1), (f, True)) assert p.has(x) assert p.has(y) assert not p.has(z) assert p.has(1) assert p.has(3) assert not p.has(4) assert p.has(f) assert p.has(g) assert not p.has(h) def test_has_iterative(): A, B, C = symbols('A,B,C', commutative=False) f = x*gamma(x)*sin(x)*exp(x*y)*A*B*C*cos(x*A*B) assert f.has(x) assert f.has(x*y) assert f.has(x*sin(x)) assert not f.has(x*sin(y)) assert f.has(x*A) assert f.has(x*A*B) assert not f.has(x*A*C) assert f.has(x*A*B*C) assert not f.has(x*A*C*B) assert f.has(x*sin(x)*A*B*C) assert not f.has(x*sin(x)*A*C*B) assert not f.has(x*sin(y)*A*B*C) assert f.has(x*gamma(x)) assert not f.has(x + sin(x)) assert (x & y & z).has(x & z) def test_has_integrals(): f = Integral(x**2 + sin(x*y*z), (x, 0, x + y + z)) assert f.has(x + y) assert f.has(x + z) assert f.has(y + z) assert f.has(x*y) assert f.has(x*z) assert f.has(y*z) assert not f.has(2*x + y) assert not f.has(2*x*y) def test_has_tuple(): f = Function('f') g = Function('g') h = Function('h') assert Tuple(x, y).has(x) assert not Tuple(x, y).has(z) assert Tuple(f(x), g(x)).has(x) assert not Tuple(f(x), g(x)).has(y) assert Tuple(f(x), g(x)).has(f) assert Tuple(f(x), g(x)).has(f(x)) assert not Tuple(f, g).has(x) assert Tuple(f, g).has(f) assert not Tuple(f, g).has(h) assert Tuple(True).has(True) is True # .has(1) will also be True def test_has_units(): from sympy.physics.units import m, s assert (x*m/s).has(x) assert (x*m/s).has(y, z) is False def test_has_polys(): poly = Poly(x**2 + x*y*sin(z), x, y, t) assert poly.has(x) assert poly.has(x, y, z) assert poly.has(x, y, z, t) def test_has_physics(): assert FockState((x, y)).has(x) def test_as_poly_as_expr(): f = x**2 + 2*x*y assert f.as_poly().as_expr() == f assert f.as_poly(x, y).as_expr() == f assert (f + sin(x)).as_poly(x, y) is None p = Poly(f, x, y) assert p.as_poly() == p def test_nonzero(): assert bool(S.Zero) is False assert bool(S.One) is True assert bool(x) is True assert bool(x + y) is True assert bool(x - x) is False assert bool(x*y) is True assert bool(x*1) is True assert bool(x*0) is False def test_is_number(): assert Float(3.14).is_number is True assert Integer(737).is_number is True assert Rational(3, 2).is_number is True assert Rational(8).is_number is True assert x.is_number is False assert (2*x).is_number is False assert (x + y).is_number is False assert log(2).is_number is True assert log(x).is_number is False assert (2 + log(2)).is_number is True assert (8 + log(2)).is_number is True assert (2 + log(x)).is_number is False assert (8 + log(2) + x).is_number is False assert (1 + x**2/x - x).is_number is True assert Tuple(Integer(1)).is_number is False assert Add(2, x).is_number is False assert Mul(3, 4).is_number is True assert Pow(log(2), 2).is_number is True assert oo.is_number is True g = WildFunction('g') assert g.is_number is False assert (2*g).is_number is False assert (x**2).subs(x, 3).is_number is True # test extensibility of .is_number # on subinstances of Basic class A(Basic): pass a = A() assert a.is_number is False def test_as_coeff_add(): assert S(2).as_coeff_add() == (2, ()) assert S(3.0).as_coeff_add() == (0, (S(3.0),)) assert S(-3.0).as_coeff_add() == (0, (S(-3.0),)) assert x.as_coeff_add() == (0, (x,)) assert (x - 1).as_coeff_add() == (-1, (x,)) assert (x + 1).as_coeff_add() == (1, (x,)) assert (x + 2).as_coeff_add() == (2, (x,)) assert (x + y).as_coeff_add(y) == (x, (y,)) assert (3*x).as_coeff_add(y) == (3*x, ()) # don't do expansion e = (x + y)**2 assert e.as_coeff_add(y) == (0, (e,)) def test_as_coeff_mul(): assert S(2).as_coeff_mul() == (2, ()) assert S(3.0).as_coeff_mul() == (1, (S(3.0),)) assert S(-3.0).as_coeff_mul() == (-1, (S(3.0),)) assert x.as_coeff_mul() == (1, (x,)) assert (-x).as_coeff_mul() == (-1, (x,)) assert (2*x).as_coeff_mul() == (2, (x,)) assert (x*y).as_coeff_mul(y) == (x, (y,)) assert (3 + x).as_coeff_mul(y) == (3 + x, ()) # don't do expansion e = exp(x + y) assert e.as_coeff_mul(y) == (1, (e,)) e = 2**(x + y) assert e.as_coeff_mul(y) == (1, (e,)) def test_as_coeff_exponent(): assert (3*x**4).as_coeff_exponent(x) == (3, 4) assert (2*x**3).as_coeff_exponent(x) == (2, 3) assert (4*x**2).as_coeff_exponent(x) == (4, 2) assert (6*x**1).as_coeff_exponent(x) == (6, 1) assert (3*x**0).as_coeff_exponent(x) == (3, 0) assert (2*x**0).as_coeff_exponent(x) == (2, 0) assert (1*x**0).as_coeff_exponent(x) == (1, 0) assert (0*x**0).as_coeff_exponent(x) == (0, 0) assert (-1*x**0).as_coeff_exponent(x) == (-1, 0) assert (-2*x**0).as_coeff_exponent(x) == (-2, 0) assert (2*x**3 + pi*x**3).as_coeff_exponent(x) == (2 + pi, 3) assert (x*log(2)/(2*x + pi*x)).as_coeff_exponent(x) == \ (log(2)/(2 + pi), 0) # 1685 D = Derivative f = Function('f') fx = D(f(x), x) assert fx.as_coeff_exponent(f(x)) == (fx, 0) def test_extractions(): assert ((x*y)**3).extract_multiplicatively(x**2 * y) == x*y**2 assert ((x*y)**3).extract_multiplicatively(x**4 * y) is None assert (2*x).extract_multiplicatively(2) == x assert (2*x).extract_multiplicatively(3) is None assert (2*x).extract_multiplicatively(-1) is None assert (Rational(1, 2)*x).extract_multiplicatively(3) == x/6 assert (sqrt(x)).extract_multiplicatively(x) is None assert (sqrt(x)).extract_multiplicatively(1/x) is None assert ((x*y)**3).extract_additively(1) is None assert (x + 1).extract_additively(x) == 1 assert (x + 1).extract_additively(2*x) is None assert (x + 1).extract_additively(-x) is None assert (-x + 1).extract_additively(2*x) is None assert (2*x + 3).extract_additively(x) == x + 3 assert (2*x + 3).extract_additively(2) == 2*x + 1 assert (2*x + 3).extract_additively(3) == 2*x assert (2*x + 3).extract_additively(-2) is None assert (2*x + 3).extract_additively(3*x) is None assert (2*x + 3).extract_additively(2*x) == 3 assert x.extract_additively(0) == x assert S(2).extract_additively(x) is None assert S(2.).extract_additively(2) == S.Zero assert S(2*x + 3).extract_additively(x + 1) == x + 2 assert S(2*x + 3).extract_additively(y + 1) is None assert S(2*x - 3).extract_additively(x + 1) is None assert S(2*x - 3).extract_additively(y + z) is None assert ((a + 1)*x*4 + y).extract_additively(x).expand() == \ 4*a*x + 3*x + y assert ((a + 1)*x*4 + 3*y).extract_additively(x + 2*y).expand() == \ 4*a*x + 3*x + y assert (y*(x + 1)).extract_additively(x + 1) is None assert ((y + 1)*(x + 1) + 3).extract_additively(x + 1) == \ y*(x + 1) + 3 assert ((x + y)*(x + 1) + x + y + 3).extract_additively(x + y) == \ x*(x + y) + 3 assert (x + y + 2*((x + y)*(x + 1)) + 3).extract_additively((x + y)*(x + 1)) == \ x + y + (x + 1)*(x + y) + 3 assert ((y + 1)*(x + 2*y + 1) + 3).extract_additively(y + 1) == \ (x + 2*y)*(y + 1) + 3 n = Symbol("n", integer=True) assert (Integer(-3)).could_extract_minus_sign() is True assert (-n*x + x).could_extract_minus_sign() != \ (n*x - x).could_extract_minus_sign() assert (x - y).could_extract_minus_sign() != \ (-x + y).could_extract_minus_sign() assert (1 - x - y).could_extract_minus_sign() is True assert (1 - x + y).could_extract_minus_sign() is False assert ((-x - x*y)/y).could_extract_minus_sign() is True assert (-(x + x*y)/y).could_extract_minus_sign() is True assert ((x + x*y)/(-y)).could_extract_minus_sign() is True assert ((x + x*y)/y).could_extract_minus_sign() is False assert (x*(-x - x**3)).could_extract_minus_sign() is True assert ((-x - y)/(x + y)).could_extract_minus_sign() is True # The results of each of these will vary on different machines, e.g. # the first one might be False and the other (then) is true or vice versa, # so both are included. assert ((-x - y)/(x - y)).could_extract_minus_sign() is False or \ ((-x - y)/(y - x)).could_extract_minus_sign() is False assert (x - y).could_extract_minus_sign() is False assert (-x + y).could_extract_minus_sign() is True def test_coeff(): assert (x + 1).coeff(x + 1) == 1 assert (3*x).coeff(0) == 0 assert (z*(1 + x)*x**2).coeff(1 + x) == z*x**2 assert (1 + 2*x*x**(1 + x)).coeff(x*x**(1 + x)) == 2 assert (1 + 2*x**(y + z)).coeff(x**(y + z)) == 2 assert (3 + 2*x + 4*x**2).coeff(1) == 0 assert (3 + 2*x + 4*x**2).coeff(-1) == 0 assert (3 + 2*x + 4*x**2).coeff(x) == 2 assert (3 + 2*x + 4*x**2).coeff(x**2) == 4 assert (3 + 2*x + 4*x**2).coeff(x**3) == 0 assert (-x/8 + x*y).coeff(x) == -S(1)/8 + y assert (-x/8 + x*y).coeff(-x) == S(1)/8 assert (4*x).coeff(2*x) == 0 assert (2*x).coeff(2*x) == 1 assert (-oo*x).coeff(x*oo) == -1 n1, n2 = symbols('n1 n2', commutative=False) assert (n1*n2).coeff(n1) == 1 assert (n1*n2).coeff(n2) == n1 assert (n1*n2 + x*n1).coeff(n1) == 1 # 1*n1*(n2+x) assert (n2*n1 + x*n1).coeff(n1) == n2 + x assert (n2*n1 + x*n1**2).coeff(n1) == n2 assert (n1**x).coeff(n1) == 0 assert (n1*n2 + n2*n1).coeff(n1) == 0 assert (2*(n1 + n2)*n2).coeff(n1 + n2, right=1) == n2 assert (2*(n1 + n2)*n2).coeff(n1 + n2, right=0) == 2 f = Function('f') assert (2*f(x) + 3*f(x).diff(x)).coeff(f(x)) == 2 expr = z*(x + y)**2 expr2 = z*(x + y)**2 + z*(2*x + 2*y)**2 assert expr.coeff(z) == (x + y)**2 assert expr.coeff(x + y) == 0 assert expr2.coeff(z) == (x + y)**2 + (2*x + 2*y)**2 assert (x + y + 3*z).coeff(1) == x + y assert (-x + 2*y).coeff(-1) == x assert (x - 2*y).coeff(-1) == 2*y assert (3 + 2*x + 4*x**2).coeff(1) == 0 assert (-x - 2*y).coeff(2) == -y assert (x + sqrt(2)*x).coeff(sqrt(2)) == x assert (3 + 2*x + 4*x**2).coeff(x) == 2 assert (3 + 2*x + 4*x**2).coeff(x**2) == 4 assert (3 + 2*x + 4*x**2).coeff(x**3) == 0 assert (z*(x + y)**2).coeff((x + y)**2) == z assert (z*(x + y)**2).coeff(x + y) == 0 assert (2 + 2*x + (x + 1)*y).coeff(x + 1) == y assert (x + 2*y + 3).coeff(1) == x assert (x + 2*y + 3).coeff(x, 0) == 2*y + 3 assert (x**2 + 2*y + 3*x).coeff(x**2, 0) == 2*y + 3*x assert x.coeff(0, 0) == 0 assert x.coeff(x, 0) == 0 n, m, o, l = symbols('n m o l', commutative=False) assert n.coeff(n) == 1 assert y.coeff(n) == 0 assert (3*n).coeff(n) == 3 assert (2 + n).coeff(x*m) == 0 assert (2*x*n*m).coeff(x) == 2*n*m assert (2 + n).coeff(x*m*n + y) == 0 assert (2*x*n*m).coeff(3*n) == 0 assert (n*m + m*n*m).coeff(n) == 1 + m assert (n*m + m*n*m).coeff(n, right=True) == m # = (1 + m)*n*m assert (n*m + m*n).coeff(n) == 0 assert (n*m + o*m*n).coeff(m*n) == o assert (n*m + o*m*n).coeff(m*n, right=1) == 1 assert (n*m + n*m*n).coeff(n*m, right=1) == 1 + n # = n*m*(n + 1) def test_coeff2(): r, kappa = symbols('r, kappa') psi = Function("psi") g = 1/r**2 * (2*r*psi(r).diff(r, 1) + r**2 * psi(r).diff(r, 2)) g = g.expand() assert g.coeff((psi(r).diff(r))) == 2/r def test_coeff2_0(): r, kappa = symbols('r, kappa') psi = Function("psi") g = 1/r**2 * (2*r*psi(r).diff(r, 1) + r**2 * psi(r).diff(r, 2)) g = g.expand() assert g.coeff(psi(r).diff(r, 2)) == 1 def test_coeff_expand(): expr = z*(x + y)**2 expr2 = z*(x + y)**2 + z*(2*x + 2*y)**2 assert expr.coeff(z) == (x + y)**2 assert expr2.coeff(z) == (x + y)**2 + (2*x + 2*y)**2 def test_integrate(): assert x.integrate(x) == x**2/2 assert x.integrate((x, 0, 1)) == S(1)/2 def test_as_base_exp(): assert x.as_base_exp() == (x, S.One) assert (x*y*z).as_base_exp() == (x*y*z, S.One) assert (x + y + z).as_base_exp() == (x + y + z, S.One) assert ((x + y)**z).as_base_exp() == (x + y, z) def test_issue1864(): assert hasattr(Mul(x, y), "is_commutative") assert hasattr(Mul(x, y, evaluate=False), "is_commutative") assert hasattr(Pow(x, y), "is_commutative") assert hasattr(Pow(x, y, evaluate=False), "is_commutative") expr = Mul(Pow(2, 2, evaluate=False), 3, evaluate=False) + 1 assert hasattr(expr, "is_commutative") def test_action_verbs(): assert nsimplify((1/(exp(3*pi*x/5) + 1))) == \ (1/(exp(3*pi*x/5) + 1)).nsimplify() assert ratsimp(1/x + 1/y) == (1/x + 1/y).ratsimp() assert trigsimp(log(x), deep=True) == (log(x)).trigsimp(deep=True) assert radsimp(1/(2 + sqrt(2))) == (1/(2 + sqrt(2))).radsimp() assert powsimp(x**y*x**z*y**z, combine='all') == \ (x**y*x**z*y**z).powsimp(combine='all') assert simplify(x**y*x**z*y**z) == (x**y*x**z*y**z).simplify() assert together(1/x + 1/y) == (1/x + 1/y).together() # Not tested because it's deprecated #assert separate((x*(y*z)**3)**2) == ((x*(y*z)**3)**2).separate() assert collect(a*x**2 + b*x**2 + a*x - b*x + c, x) == \ (a*x**2 + b*x**2 + a*x - b*x + c).collect(x) assert apart(y/(y + 2)/(y + 1), y) == (y/(y + 2)/(y + 1)).apart(y) assert combsimp(y/(x + 2)/(x + 1)) == (y/(x + 2)/(x + 1)).combsimp() assert factor(x**2 + 5*x + 6) == (x**2 + 5*x + 6).factor() assert refine(sqrt(x**2)) == sqrt(x**2).refine() assert cancel((x**2 + 5*x + 6)/(x + 2)) == ((x**2 + 5*x + 6)/(x + 2)).cancel() def test_as_powers_dict(): assert x.as_powers_dict() == {x: 1} assert (x**y*z).as_powers_dict() == {x: y, z: 1} assert Mul(2, 2, evaluate=False).as_powers_dict() == {S(2): S(2)} assert (x*y).as_powers_dict()[z] == 0 assert (x + y).as_powers_dict()[z] == 0 def test_as_coefficients_dict(): check = [S(1), x, y, x*y, 1] assert [Add(3*x, 2*x, y, 3).as_coefficients_dict()[i] for i in check] == \ [3, 5, 1, 0, 0] assert [(3*x*y).as_coefficients_dict()[i] for i in check] == \ [0, 0, 0, 3, 0] assert (3.0*x*y).as_coefficients_dict()[3.0*x*y] == 1 def test_args_cnc(): A = symbols('A', commutative=False) assert (x + A).args_cnc() == \ [[], [x + A]] assert (x + a).args_cnc() == \ [[a + x], []] assert (x*a).args_cnc() == \ [[a, x], []] assert (x*y*A*(A + 1)).args_cnc(cset=True) == \ [set([x, y]), [A, 1 + A]] assert Mul(x, x, evaluate=False).args_cnc(cset=True, warn=False) == \ [set([x]), []] assert Mul(x, x**2, evaluate=False).args_cnc(cset=True, warn=False) == \ [set([x, x**2]), []] raises(ValueError, lambda: Mul(x, x, evaluate=False).args_cnc(cset=True)) assert Mul(x, y, x, evaluate=False).args_cnc() == \ [[x, y, x], []] # always split -1 from leading number assert (-1.*x).args_cnc() == [[-1, 1.0, x], []] def test_new_rawargs(): n = Symbol('n', commutative=False) a = x + n assert a.is_commutative is False assert a._new_rawargs(x).is_commutative assert a._new_rawargs(x, y).is_commutative assert a._new_rawargs(x, n).is_commutative is False assert a._new_rawargs(x, y, n).is_commutative is False m = x*n assert m.is_commutative is False assert m._new_rawargs(x).is_commutative assert m._new_rawargs(n).is_commutative is False assert m._new_rawargs(x, y).is_commutative assert m._new_rawargs(x, n).is_commutative is False assert m._new_rawargs(x, y, n).is_commutative is False assert m._new_rawargs(x, n, reeval=False).is_commutative is False assert m._new_rawargs(S.One) is S.One def test_2127(): assert Add(evaluate=False) == 0 assert Mul(evaluate=False) == 1 assert Mul(x + y, evaluate=False).is_Add def test_free_symbols(): # free_symbols should return the free symbols of an object assert S(1).free_symbols == set() assert (x).free_symbols == set([x]) assert Integral(x, (x, 1, y)).free_symbols == set([y]) assert (-Integral(x, (x, 1, y))).free_symbols == set([y]) assert meter.free_symbols == set() assert (meter**x).free_symbols == set([x]) def test_issue2201(): x = Symbol('x', commutative=False) assert x*sqrt(2)/sqrt(6) == x*sqrt(3)/3 def test_as_coeff_Mul(): assert Integer(3).as_coeff_Mul() == (Integer(3), Integer(1)) assert Rational(3, 4).as_coeff_Mul() == (Rational(3, 4), Integer(1)) assert Float(5.0).as_coeff_Mul() == (Float(5.0), Integer(1)) assert (Integer(3)*x).as_coeff_Mul() == (Integer(3), x) assert (Rational(3, 4)*x).as_coeff_Mul() == (Rational(3, 4), x) assert (Float(5.0)*x).as_coeff_Mul() == (Float(5.0), x) assert (Integer(3)*x*y).as_coeff_Mul() == (Integer(3), x*y) assert (Rational(3, 4)*x*y).as_coeff_Mul() == (Rational(3, 4), x*y) assert (Float(5.0)*x*y).as_coeff_Mul() == (Float(5.0), x*y) assert (x).as_coeff_Mul() == (S.One, x) assert (x*y).as_coeff_Mul() == (S.One, x*y) def test_as_coeff_Add(): assert Integer(3).as_coeff_Add() == (Integer(3), Integer(0)) assert Rational(3, 4).as_coeff_Add() == (Rational(3, 4), Integer(0)) assert Float(5.0).as_coeff_Add() == (Float(5.0), Integer(0)) assert (Integer(3) + x).as_coeff_Add() == (Integer(3), x) assert (Rational(3, 4) + x).as_coeff_Add() == (Rational(3, 4), x) assert (Float(5.0) + x).as_coeff_Add() == (Float(5.0), x) assert (Integer(3) + x + y).as_coeff_Add() == (Integer(3), x + y) assert (Rational(3, 4) + x + y).as_coeff_Add() == (Rational(3, 4), x + y) assert (Float(5.0) + x + y).as_coeff_Add() == (Float(5.0), x + y) assert (x).as_coeff_Add() == (S.Zero, x) assert (x*y).as_coeff_Add() == (S.Zero, x*y) def test_expr_sorting(): f, g = symbols('f,g', cls=Function) exprs = [1/x**2, 1/x, sqrt(sqrt(x)), sqrt(x), x, sqrt(x)**3, x**2] assert sorted(exprs, key=default_sort_key) == exprs exprs = [x, 2*x, 2*x**2, 2*x**3, x**n, 2*x**n, sin(x), sin(x)**n, sin(x**2), cos(x), cos(x**2), tan(x)] assert sorted(exprs, key=default_sort_key) == exprs exprs = [x + 1, x**2 + x + 1, x**3 + x**2 + x + 1] assert sorted(exprs, key=default_sort_key) == exprs exprs = [S(4), x - 3*I/2, x + 3*I/2, x - 4*I + 1, x + 4*I + 1] assert sorted(exprs, key=default_sort_key) == exprs exprs = [f(1), f(2), f(3), f(1, 2, 3), g(1), g(2), g(3), g(1, 2, 3)] assert sorted(exprs, key=default_sort_key) == exprs exprs = [f(x), g(x), exp(x), sin(x), cos(x), factorial(x)] assert sorted(exprs, key=default_sort_key) == exprs exprs = [Tuple(x, y), Tuple(x, z), Tuple(x, y, z)] assert sorted(exprs, key=default_sort_key) == exprs exprs = [[3], [1, 2]] assert sorted(exprs, key=default_sort_key) == exprs exprs = [[1, 2], [2, 3]] assert sorted(exprs, key=default_sort_key) == exprs exprs = [[1, 2], [1, 2, 3]] assert sorted(exprs, key=default_sort_key) == exprs exprs = [{x: -y}, {x: y}] assert sorted(exprs, key=default_sort_key) == exprs exprs = [set([1]), set([1, 2])] assert sorted(exprs, key=default_sort_key) == exprs def test_as_ordered_factors(): f, g = symbols('f,g', cls=Function) assert x.as_ordered_factors() == [x] assert (2*x*x**n*sin(x)*cos(x)).as_ordered_factors() \ == [Integer(2), x, x**n, sin(x), cos(x)] args = [f(1), f(2), f(3), f(1, 2, 3), g(1), g(2), g(3), g(1, 2, 3)] expr = Mul(*args) assert expr.as_ordered_factors() == args A, B = symbols('A,B', commutative=False) assert (A*B).as_ordered_factors() == [A, B] assert (B*A).as_ordered_factors() == [B, A] def test_as_ordered_terms(): f, g = symbols('f,g', cls=Function) assert x.as_ordered_terms() == [x] assert (sin(x)**2*cos(x) + sin(x)*cos(x)**2 + 1).as_ordered_terms() \ == [sin(x)**2*cos(x), sin(x)*cos(x)**2, 1] args = [f(1), f(2), f(3), f(1, 2, 3), g(1), g(2), g(3), g(1, 2, 3)] expr = Add(*args) assert expr.as_ordered_terms() == args assert (1 + 4*sqrt(3)*pi*x).as_ordered_terms() == [4*pi*x*sqrt(3), 1] assert ( 2 + 3*I).as_ordered_terms() == [2, 3*I] assert (-2 + 3*I).as_ordered_terms() == [-2, 3*I] assert ( 2 - 3*I).as_ordered_terms() == [2, -3*I] assert (-2 - 3*I).as_ordered_terms() == [-2, -3*I] assert ( 4 + 3*I).as_ordered_terms() == [4, 3*I] assert (-4 + 3*I).as_ordered_terms() == [-4, 3*I] assert ( 4 - 3*I).as_ordered_terms() == [4, -3*I] assert (-4 - 3*I).as_ordered_terms() == [-4, -3*I] f = x**2*y**2 + x*y**4 + y + 2 assert f.as_ordered_terms(order="lex") == [x**2*y**2, x*y**4, y, 2] assert f.as_ordered_terms(order="grlex") == [x*y**4, x**2*y**2, y, 2] assert f.as_ordered_terms(order="rev-lex") == [2, y, x*y**4, x**2*y**2] assert f.as_ordered_terms(order="rev-grlex") == [2, y, x**2*y**2, x*y**4] def test_sort_key_atomic_expr(): from sympy.physics.units import m, s assert sorted([-m, s], key=lambda arg: arg.sort_key()) == [-m, s] def test_issue_1100(): # first subs and limit gives NaN a = x/y assert a._eval_interval(x, 0, oo)._eval_interval(y, oo, 0) is S.NaN # second subs and limit gives NaN assert a._eval_interval(x, 0, oo)._eval_interval(y, 0, oo) is S.NaN # difference gives S.NaN a = x - y assert a._eval_interval(x, 1, oo)._eval_interval(y, oo, 1) is S.NaN raises(ValueError, lambda: x._eval_interval(x, None, None)) def test_primitive(): assert (3*(x + 1)**2).primitive() == (3, (x + 1)**2) assert (6*x + 2).primitive() == (2, 3*x + 1) assert (x/2 + 3).primitive() == (S(1)/2, x + 6) eq = (6*x + 2)*(x/2 + 3) assert eq.primitive()[0] == 1 eq = (2 + 2*x)**2 assert eq.primitive()[0] == 1 assert (4.0*x).primitive() == (1, 4.0*x) assert (4.0*x + y/2).primitive() == (S.Half, 8.0*x + y) assert (-2*x).primitive() == (2, -x) assert Add(5*z/7, 0.5*x, 3*y/2, evaluate=False).primitive() == \ (S(1)/14, 7.0*x + 21*y + 10*z) for i in [S.Infinity, S.NegativeInfinity, S.ComplexInfinity]: assert (i + x/3).primitive() == \ (S(1)/3, i + x) assert (S.Infinity + 2*x/3 + 4*y/7).primitive() == \ (S(1)/21, 14*x + 12*y + oo) assert S.Zero.primitive() == (S.One, S.Zero) def test_issue_2744(): a = 1 + x assert (2*a).extract_multiplicatively(a) == 2 assert (4*a).extract_multiplicatively(2*a) == 2 assert ((3*a)*(2*a)).extract_multiplicatively(a) == 6*a def test_is_constant(): from sympy.solvers.solvers import checksol Sum(x, (x, 1, 10)).is_constant() is True Sum(x, (x, 1, n)).is_constant() is False Sum(x, (x, 1, n)).is_constant(y) is True Sum(x, (x, 1, n)).is_constant(n) is False Sum(x, (x, 1, n)).is_constant(x) is True eq = a*cos(x)**2 + a*sin(x)**2 - a eq.is_constant() is True assert eq.subs({x: pi, a: 2}) == eq.subs({x: pi, a: 3}) == 0 assert x.is_constant() is False assert x.is_constant(y) is True assert checksol(x, x, Sum(x, (x, 1, n))) is False assert checksol(x, x, Sum(x, (x, 1, n))) is False f = Function('f') assert checksol(x, x, f(x)) is False p = symbols('p', positive=True) assert Pow(x, S(0), evaluate=False).is_constant() is True # == 1 assert Pow(S(0), x, evaluate=False).is_constant() is False # == 0 or 1 assert Pow(S(0), p, evaluate=False).is_constant() is True # == 1 assert (2**x).is_constant() is False assert Pow(S(2), S(3), evaluate=False).is_constant() is True z1, z2 = symbols('z1 z2', zero=True) assert (z1 + 2*z2).is_constant() is True assert meter.is_constant() is True assert (3*meter).is_constant() is True assert (x*meter).is_constant() is False def test_equals(): assert (-3 - sqrt(5) + (-sqrt(10)/2 - sqrt(2)/2)**2).equals(0) assert (x**2 - 1).equals((x + 1)*(x - 1)) assert (cos(x)**2 + sin(x)**2).equals(1) assert (a*cos(x)**2 + a*sin(x)**2).equals(a) r = sqrt(2) assert (-1/(r + r*x) + 1/r/(1 + x)).equals(0) assert factorial(x + 1).equals((x + 1)*factorial(x)) assert sqrt(3).equals(2*sqrt(3)) is False assert (sqrt(5)*sqrt(3)).equals(sqrt(3)) is False assert (sqrt(5) + sqrt(3)).equals(0) is False assert (sqrt(5) + pi).equals(0) is False assert meter.equals(0) is False assert (3*meter**2).equals(0) is False eq = -(-1)**(S(3)/4)*6**(S(1)/4) + (-6)**(S(1)/4)*I if eq != 0: # if canonicalization makes this zero, skip the test assert eq.equals(0) assert sqrt(x).equals(0) is False # from integrate(x*sqrt(1+2*x), x); # diff is zero only when assumptions allow i = 2*sqrt(2)*x**(S(5)/2)*(1 + 1/(2*x))**(S(5)/2)/5 + \ 2*sqrt(2)*x**(S(3)/2)*(1 + 1/(2*x))**(S(5)/2)/(-6 - 3/x) ans = sqrt(2*x + 1)*(6*x**2 + x - 1)/15 diff = i - ans assert diff.equals(0) is False assert diff.subs(x, -S.Half/2) == 7*sqrt(2)/120 # there are regions for x for which the expression is True, for # example, when x < -1/2 or x > 0 the expression is zero p = Symbol('p', positive=True) assert diff.subs(x, p).equals(0) is True assert diff.subs(x, -1).equals(0) is True # prove via minimal_polynomial or self-consistency eq = sqrt(1 + sqrt(3)) + sqrt(3 + 3*sqrt(3)) - sqrt(10 + 6*sqrt(3)) assert eq.equals(0) q = 3**Rational(1, 3) + 3 p = expand(q**3)**Rational(1, 3) assert (p - q).equals(0) # issue 3730 # eq = q*x + q/4 + x**4 + x**3 + 2*x**2 - S(1)/3 # z = eq.subs(x, solve(eq, x)[0]) q = symbols('q') z = (q*(-sqrt(-2*(-(q - S(7)/8)**S(2)/8 - S(2197)/13824)**(S(1)/3) - S(13)/12)/2 - sqrt((2*q - S(7)/4)/sqrt(-2*(-(q - S(7)/8)**S(2)/8 - S(2197)/13824)**(S(1)/3) - S(13)/12) + 2*(-(q - S(7)/8)**S(2)/8 - S(2197)/13824)**(S(1)/3) - S(13)/6)/2 - S(1)/4) + q/4 + (-sqrt(-2*(-(q - S(7)/8)**S(2)/8 - S(2197)/13824)**(S(1)/3) - S(13)/12)/2 - sqrt((2*q - S(7)/4)/sqrt(-2*(-(q - S(7)/8)**S(2)/8 - S(2197)/13824)**(S(1)/3) - S(13)/12) + 2*(-(q - S(7)/8)**S(2)/8 - S(2197)/13824)**(S(1)/3) - S(13)/6)/2 - S(1)/4)**4 + (-sqrt(-2*(-(q - S(7)/8)**S(2)/8 - S(2197)/13824)**(S(1)/3) - S(13)/12)/2 - sqrt((2*q - S(7)/4)/sqrt(-2*(-(q - S(7)/8)**S(2)/8 - S(2197)/13824)**(S(1)/3) - S(13)/12) + 2*(-(q - S(7)/8)**S(2)/8 - S(2197)/13824)**(S(1)/3) - S(13)/6)/2 - S(1)/4)**3 + 2*(-sqrt(-2*(-(q - S(7)/8)**S(2)/8 - S(2197)/13824)**(S(1)/3) - S(13)/12)/2 - sqrt((2*q - S(7)/4)/sqrt(-2*(-(q - S(7)/8)**S(2)/8 - S(2197)/13824)**(S(1)/3) - S(13)/12) + 2*(-(q - S(7)/8)**S(2)/8 - S(2197)/13824)**(S(1)/3) - S(13)/6)/2 - S(1)/4)**2 - S(1)/3) assert z.equals(0) def test_random(): from sympy import posify, lucas assert posify(x)[0]._random() is not None assert lucas(n)._random(2, -2, 0, -1, 1) is None def test_round(): from sympy.abc import x assert Float('0.1249999').round(2) == 0.12 d20 = 12345678901234567890 ans = S(d20).round(2) assert ans.is_Float and ans == d20 ans = S(d20).round(-2) assert ans.is_Float and ans == 12345678901234567900 assert S('1/7').round(4) == 0.1429 assert S('.[12345]').round(4) == 0.1235 assert S('.1349').round(2) == 0.13 n = S(12345) ans = n.round() assert ans.is_Float assert ans == n ans = n.round(1) assert ans.is_Float assert ans == n ans = n.round(4) assert ans.is_Float assert ans == n assert n.round(-1) == 12350 r = n.round(-4) assert r == 10000 # in fact, it should equal many values since __eq__ # compares at equal precision assert all(r == i for i in range(9984, 10049)) assert n.round(-5) == 0 assert (pi + sqrt(2)).round(2) == 4.56 assert (10*(pi + sqrt(2))).round(-1) == 50 raises(TypeError, lambda: round(x + 2, 2)) assert S(2.3).round(1) == 2.3 e = S(12.345).round(2) assert e == round(12.345, 2) assert type(e) is Float assert (Float(.3, 3) + 2*pi).round() == 7 assert (Float(.3, 3) + 2*pi*100).round() == 629 assert (Float(.03, 3) + 2*pi/100).round(5) == 0.09283 assert (Float(.03, 3) + 2*pi/100).round(4) == 0.0928 assert (pi + 2*E*I).round() == 3 + 5*I assert S.Zero.round() == 0 a = (Add(1, Float('1.' + '9'*27, ''), evaluate=0)) assert a.round(10) == Float('3.0000000000', '') assert a.round(25) == Float('3.0000000000000000000000000', '') assert a.round(26) == Float('3.00000000000000000000000000', '') assert a.round(27) == Float('2.999999999999999999999999999', '') assert a.round(30) == Float('2.999999999999999999999999999', '') raises(TypeError, lambda: x.round()) # exact magnitude of 10 assert str(S(1).round()) == '1.' assert str(S(100).round()) == '100.' # applied to real and imaginary portions assert (2*pi + E*I).round() == 6 + 3*I assert (2*pi + I/10).round() == 6 assert (pi/10 + 2*I).round() == 2*I # the lhs re and im parts are Float with dps of 2 # and those on the right have dps of 15 so they won't compare # equal unless we use string or compare components (which will # then coerce the floats to the same precision) or re-create # the floats assert str((pi/10 + E*I).round(2)) == '0.31 + 2.72*I' assert (pi/10 + E*I).round(2).as_real_imag() == (0.31, 2.72) assert (pi/10 + E*I).round(2) == Float(0.31, 2) + I*Float(2.72, 3) # issue 3815 assert (I**(I + 3)).round(3) == Float('-0.208', '')*I def test_extract_branch_factor(): assert exp_polar(2.0*I*pi).extract_branch_factor() == (1, 1) def test_identity_removal(): assert Add.make_args(x + 0) == (x,) assert Mul.make_args(x*1) == (x,) def test_float_0(): assert Float(0.0) + 1 == Float(1.0) @XFAIL def test_float_0_fail(): assert Float(0.0)*x == Float(0.0) assert (x + Float(0.0)).is_Add def test_issue_3226(): ans = (b**2 + z**2 - (b*(a + b*t) + z*(c + t*z))**2/( (a + b*t)**2 + (c + t*z)**2))/sqrt((a + b*t)**2 + (c + t*z)**2) e = sqrt((a + b*t)**2 + (c + z*t)**2) assert diff(e, t, 2) == ans e.diff(t, 2) == ans assert diff(e, t, 2, simplify=False) != ans sympy-0.7.4.1/sympy/core/tests/test_priority.py0000644000175000017500000000413612253362407022015 0ustar georgeskgeorgeskfrom sympy import Expr, Symbol from sympy.core.decorators import call_highest_priority class Higher(Expr): _op_priority = 20.0 result = 'high' @call_highest_priority('__rmul__') def __mul__(self, other): return self.result @call_highest_priority('__mul__') def __rmul__(self, other): return self.result @call_highest_priority('__radd__') def __add__(self, other): return self.result @call_highest_priority('__add__') def __radd__(self, other): return self.result @call_highest_priority('__rsub__') def __sub__(self, other): return self.result @call_highest_priority('__sub__') def __rsub__(self, other): return self.result @call_highest_priority('__rpow__') def __pow__(self, other): return self.result @call_highest_priority('__pow__') def __rpow__(self, other): return self.result @call_highest_priority('__rdiv__') def __div__(self, other): return self.result @call_highest_priority('__div__') def __rdiv__(self, other): return self.result __truediv__ = __div__ __rtruediv__ = __rdiv__ class Lower(Higher): _op_priority = 5.0 result = 'low' def test_mul(): x = Symbol('x') h = Higher() l = Lower() assert l*h == h*l == 'high' assert x*h == h*x == 'high' assert l*x == x*l != 'low' def test_add(): x = Symbol('x') h = Higher() l = Lower() assert l + h == h + l == 'high' assert x + h == h + x == 'high' assert l + x == x + l != 'low' def test_sub(): x = Symbol('x') h = Higher() l = Lower() assert l - h == h - l == 'high' assert x - h == h - x == 'high' assert l - x == -(x - l) != 'low' def test_pow(): x = Symbol('x') h = Higher() l = Lower() assert l**h == h**l == 'high' assert x**h == h**x == 'high' assert l**x != 'low' assert x**l != 'low' def test_div(): x = Symbol('x') h = Higher() l = Lower() assert l/h == h/l == 'high' assert x/h == h/x == 'high' assert l/x != 'low' assert x/l != 'low' sympy-0.7.4.1/sympy/core/tests/test_trace.py0000644000175000017500000000542412253362407021233 0ustar georgeskgeorgeskfrom sympy import S, Symbol, symbols, Matrix, Tuple from sympy.core.trace import Tr from sympy.utilities.pytest import raises def test_trace_new(): a, b, c, d, Y = symbols('a b c d Y') A, B, C, D = symbols('A B C D', commutative=False) assert Tr(a + b) == a + b assert Tr(A + B) == Tr(A) + Tr(B) #check trace args not implicitly permuted assert Tr(C*D*A*B).args[0].args == (C, D, A, B) # check for mul and adds assert Tr((a*b) + ( c*d)) == (a*b) + (c*d) # Tr(scalar*A) = scalar*Tr(A) assert Tr(a*A) == a*Tr(A) assert Tr(a*A*B*b) == a*b*Tr(A*B) # since A is symbol and not commutative assert isinstance(Tr(A), Tr) #POW assert Tr(pow(a, b)) == a**b assert isinstance(Tr(pow(A, a)), Tr) #Matrix M = Matrix([[1, 1], [2, 2]]) assert Tr(M) == 3 ##test indices in different forms #no index t = Tr(A) assert t.args[1] == Tuple() #single index t = Tr(A, 0) assert t.args[1] == Tuple(0) #index in a list t = Tr(A, [0]) assert t.args[1] == Tuple(0) t = Tr(A, [0, 1, 2]) assert t.args[1] == Tuple(0, 1, 2) #index is tuple t = Tr(A, (0)) assert t.args[1] == Tuple(0) t = Tr(A, (1, 2)) assert t.args[1] == Tuple(1, 2) #trace indices test t = Tr((A + B), [2]) assert t.args[0].args[1] == Tuple(2) and t.args[1].args[1] == Tuple(2) t = Tr(a*A, [2, 3]) assert t.args[1].args[1] == Tuple(2, 3) #class with trace method defined #to simulate numpy objects class Foo: def trace(self): return 1 assert Tr(Foo()) == 1 #argument test # check for value error, when either/both arguments are not provided raises(ValueError, lambda: Tr()) raises(ValueError, lambda: Tr(A, 1, 2)) def test_trace_doit(): a, b, c, d = symbols('a b c d') A, B, C, D = symbols('A B C D', commutative=False) #TODO: needed while testing reduced density operations, etc. def test_permute(): A, B, C, D, E, F, G = symbols('A B C D E F G', commutative=False) t = Tr(A*B*C*D*E*F*G) assert t.permute(0).args[0].args == (A, B, C, D, E, F, G) assert t.permute(2).args[0].args == (F, G, A, B, C, D, E) assert t.permute(4).args[0].args == (D, E, F, G, A, B, C) assert t.permute(6).args[0].args == (B, C, D, E, F, G, A) assert t.permute(8).args[0].args == t.permute(1).args[0].args assert t.permute(-1).args[0].args == (B, C, D, E, F, G, A) assert t.permute(-3).args[0].args == (D, E, F, G, A, B, C) assert t.permute(-5).args[0].args == (F, G, A, B, C, D, E) assert t.permute(-8).args[0].args == t.permute(-1).args[0].args t = Tr((A + B)*(B*B)*C*D) assert t.permute(2).args[0].args == (C, D, (A + B), (B**2)) t1 = Tr(A*B) t2 = t1.permute(1) assert id(t1) != id(t2) and t1 == t2 sympy-0.7.4.1/sympy/core/tests/test_compatibility.py0000644000175000017500000000113312253362407022777 0ustar georgeskgeorgeskfrom sympy.core.compatibility import default_sort_key, as_int, ordered from sympy.core.singleton import S from sympy.utilities.pytest import raises from sympy.abc import x def test_default_sort_key(): func = lambda x: x assert sorted([func, x, func], key=default_sort_key) == [func, func, x] def test_as_int(): raises(ValueError, lambda : as_int(1.1)) raises(ValueError, lambda : as_int([])) def test_ordered(): # Issue 4111 - this had been failing with python2/3 problems assert(list(ordered([{1:3, 2:4, 9:10}, {1:3}])) == \ [{1: 3}, {1: 3, 2: 4, 9: 10}]) sympy-0.7.4.1/sympy/core/tests/test_sympify.py0000644000175000017500000003475112253362407021642 0ustar georgeskgeorgeskfrom sympy import Symbol, exp, Integer, Float, sin, cos, log, Poly, Lambda, \ Function, I, S, sqrt, srepr, Rational, Tuple, Matrix, Interval, Add, Mul,\ Pow, And, Or, Xor, Not, true, false from sympy.abc import x, y from sympy.core.sympify import sympify, _sympify, SympifyError, kernS from sympy.core.decorators import _sympifyit from sympy.utilities.pytest import XFAIL, raises from sympy.utilities.decorator import conserve_mpmath_dps from sympy.geometry import Point, Line from sympy.functions.combinatorial.factorials import factorial, factorial2 from sympy.abc import _clash, _clash1, _clash2 from sympy.core.compatibility import exec_, HAS_GMPY from sympy import mpmath def test_439(): v = sympify("exp(x)") assert v == exp(x) assert type(v) == type(exp(x)) assert str(type(v)) == str(type(exp(x))) def test_sympify1(): assert sympify("x") == Symbol("x") assert sympify(" x") == Symbol("x") assert sympify(" x ") == Symbol("x") # 1778 n1 = Rational(1, 2) assert sympify('--.5') == n1 assert sympify('-1/2') == -n1 assert sympify('-+--.5') == -n1 assert sympify('-.[3]') == Rational(-1, 3) assert sympify('.[3]') == Rational(1, 3) assert sympify('+.[3]') == Rational(1, 3) assert sympify('+0.[3]*10**-2') == Rational(1, 300) assert sympify('.[052631578947368421]') == Rational(1, 19) assert sympify('.0[526315789473684210]') == Rational(1, 19) assert sympify('.034[56]') == Rational(1711, 49500) # options to make reals into rationals assert sympify('1.22[345]', rational=True) == \ 1 + Rational(22, 100) + Rational(345, 99900) assert sympify('2/2.6', rational=True) == Rational(10, 13) assert sympify('2.6/2', rational=True) == Rational(13, 10) assert sympify('2.6e2/17', rational=True) == Rational(260, 17) assert sympify('2.6e+2/17', rational=True) == Rational(260, 17) assert sympify('2.6e-2/17', rational=True) == Rational(26, 17000) assert sympify('2.1+3/4', rational=True) == \ Rational(21, 10) + Rational(3, 4) assert sympify('2.234456', rational=True) == Rational(279307, 125000) assert sympify('2.234456e23', rational=True) == 223445600000000000000000 assert sympify('2.234456e-23', rational=True) == \ Rational(279307, 12500000000000000000000000000) assert sympify('-2.234456e-23', rational=True) == \ Rational(-279307, 12500000000000000000000000000) assert sympify('12345678901/17', rational=True) == \ Rational(12345678901, 17) assert sympify('1/.3 + x', rational=True) == Rational(10, 3) + x # make sure longs in fractions work assert sympify('222222222222/11111111111') == \ Rational(222222222222, 11111111111) # ... even if they come from repetend notation assert sympify('1/.2[123456789012]') == Rational(333333333333, 70781892967) # ... or from high precision reals assert sympify('.1234567890123456', rational=True) == \ Rational(19290123283179, 156250000000000) def test_sympify_Fraction(): try: import fractions except ImportError: pass else: value = sympify(fractions.Fraction(101, 127)) assert value == Rational(101, 127) and type(value) is Rational def test_sympify_gmpy(): if HAS_GMPY: if HAS_GMPY == 2: import gmpy2 as gmpy elif HAS_GMPY == 1: import gmpy value = sympify(gmpy.mpz(1000001)) assert value == Integer(1000001) and type(value) is Integer value = sympify(gmpy.mpq(101, 127)) assert value == Rational(101, 127) and type(value) is Rational @conserve_mpmath_dps def test_sympify_mpmath(): value = sympify(mpmath.mpf(1.0)) assert value == Float(1.0) and type(value) is Float mpmath.mp.dps = 12 assert sympify( mpmath.pi).epsilon_eq(Float("3.14159265359"), Float("1e-12")) is True assert sympify( mpmath.pi).epsilon_eq(Float("3.14159265359"), Float("1e-13")) is False mpmath.mp.dps = 6 assert sympify( mpmath.pi).epsilon_eq(Float("3.14159"), Float("1e-5")) is True assert sympify( mpmath.pi).epsilon_eq(Float("3.14159"), Float("1e-6")) is False assert sympify(mpmath.mpc(1.0 + 2.0j)) == Float(1.0) + Float(2.0)*I def test_sympify2(): class A: def _sympy_(self): return Symbol("x")**3 a = A() assert _sympify(a) == x**3 assert sympify(a) == x**3 assert a == x**3 def test_sympify3(): assert sympify("x**3") == x**3 assert sympify("x^3") == x**3 assert sympify("1/2") == Integer(1)/2 raises(SympifyError, lambda: _sympify('x**3')) raises(SympifyError, lambda: _sympify('1/2')) def test_sympify_keywords(): raises(SympifyError, lambda: sympify('if')) raises(SympifyError, lambda: sympify('for')) raises(SympifyError, lambda: sympify('while')) raises(SympifyError, lambda: sympify('lambda')) def test_sympify_float(): assert sympify("1e-64") != 0 assert sympify("1e-20000") != 0 def test_sympify_bool(): assert sympify(True) is true assert sympify(False) is false def test_sympyify_iterables(): ans = [Rational(3, 10), Rational(1, 5)] assert sympify(['.3', '.2'], rational=True) == ans assert sympify(set(['.3', '.2']), rational=True) == set(ans) assert sympify(tuple(['.3', '.2']), rational=True) == Tuple(*ans) assert sympify(dict(x=0, y=1)) == {x: 0, y: 1} assert sympify(['1', '2', ['3', '4']]) == [S(1), S(2), [S(3), S(4)]] def test_sympify4(): class A: def _sympy_(self): return Symbol("x") a = A() assert _sympify(a)**3 == x**3 assert sympify(a)**3 == x**3 assert a == x def test_sympify_text(): assert sympify('some') == Symbol('some') assert sympify('core') == Symbol('core') assert sympify('True') is True assert sympify('False') is False assert sympify('Poly') == Poly assert sympify('sin') == sin def test_sympify_function(): assert sympify('factor(x**2-1, x)') == -(1 - x)*(x + 1) assert sympify('sin(pi/2)*cos(pi)') == -Integer(1) def test_sympify_poly(): p = Poly(x**2 + x + 1, x) assert _sympify(p) is p assert sympify(p) is p def test_sympify_factorial(): assert sympify('x!') == factorial(x) assert sympify('(x+1)!') == factorial(x + 1) assert sympify('(1 + y*(x + 1))!') == factorial(1 + y*(x + 1)) assert sympify('(1 + y*(x + 1)!)^2') == (1 + y*factorial(x + 1))**2 assert sympify('y*x!') == y*factorial(x) assert sympify('x!!') == factorial2(x) assert sympify('(x+1)!!') == factorial2(x + 1) assert sympify('(1 + y*(x + 1))!!') == factorial2(1 + y*(x + 1)) assert sympify('(1 + y*(x + 1)!!)^2') == (1 + y*factorial2(x + 1))**2 assert sympify('y*x!!') == y*factorial2(x) assert sympify('factorial2(x)!') == factorial(factorial2(x)) raises(SympifyError, lambda: sympify("+!!")) raises(SympifyError, lambda: sympify(")!!")) raises(SympifyError, lambda: sympify("!")) raises(SympifyError, lambda: sympify("(!)")) raises(SympifyError, lambda: sympify("x!!!")) def test_sage(): # how to effectivelly test for the _sage_() method without having SAGE # installed? assert hasattr(x, "_sage_") assert hasattr(Integer(3), "_sage_") assert hasattr(sin(x), "_sage_") assert hasattr(cos(x), "_sage_") assert hasattr(x**2, "_sage_") assert hasattr(x + y, "_sage_") assert hasattr(exp(x), "_sage_") assert hasattr(log(x), "_sage_") def test_bug496(): assert sympify("a_") == Symbol("a_") assert sympify("_a") == Symbol("_a") @XFAIL def test_lambda(): x = Symbol('x') assert sympify('lambda: 1') == Lambda((), 1) assert sympify('lambda x: 2*x') == Lambda(x, 2*x) assert sympify('lambda x, y: 2*x+y') == Lambda([x, y], 2*x + y) def test_lambda_raises(): with raises(SympifyError): _sympify('lambda: 1') def test_sympify_raises(): raises(SympifyError, lambda: sympify("fx)")) def test__sympify(): x = Symbol('x') f = Function('f') # positive _sympify assert _sympify(x) is x assert _sympify(f) is f assert _sympify(1) == Integer(1) assert _sympify(0.5) == Float("0.5") assert _sympify(1 + 1j) == 1.0 + I*1.0 class A: def _sympy_(self): return Integer(5) a = A() assert _sympify(a) == Integer(5) # negative _sympify raises(SympifyError, lambda: _sympify('1')) raises(SympifyError, lambda: _sympify([1, 2, 3])) def test_sympifyit(): x = Symbol('x') y = Symbol('y') @_sympifyit('b', NotImplemented) def add(a, b): return a + b assert add(x, 1) == x + 1 assert add(x, 0.5) == x + Float('0.5') assert add(x, y) == x + y assert add(x, '1') == NotImplemented @_sympifyit('b') def add_raises(a, b): return a + b assert add_raises(x, 1) == x + 1 assert add_raises(x, 0.5) == x + Float('0.5') assert add_raises(x, y) == x + y raises(SympifyError, lambda: add_raises(x, '1')) def test_int_float(): class F1_1(object): def __float__(self): return 1.1 class F1_1b(object): """ This class is still a float, even though it also implements __int__(). """ def __float__(self): return 1.1 def __int__(self): return 1 class F1_1c(object): """ This class is still a float, because it implements _sympy_() """ def __float__(self): return 1.1 def __int__(self): return 1 def _sympy_(self): return Float(1.1) class I5(object): def __int__(self): return 5 class I5b(object): """ This class implements both __int__() and __float__(), so it will be treated as Float in SymPy. One could change this behavior, by using float(a) == int(a), but deciding that integer-valued floats represent exact numbers is arbitrary and often not correct, so we do not do it. If, in the future, we decide to do it anyway, the tests for I5b need to be changed. """ def __float__(self): return 5.0 def __int__(self): return 5 class I5c(object): """ This class implements both __int__() and __float__(), but also a _sympy_() method, so it will be Integer. """ def __float__(self): return 5.0 def __int__(self): return 5 def _sympy_(self): return Integer(5) i5 = I5() i5b = I5b() i5c = I5c() f1_1 = F1_1() f1_1b = F1_1b() f1_1c = F1_1c() assert sympify(i5) == 5 assert isinstance(sympify(i5), Integer) assert sympify(i5b) == 5 assert isinstance(sympify(i5b), Float) assert sympify(i5c) == 5 assert isinstance(sympify(i5c), Integer) assert abs(sympify(f1_1) - 1.1) < 1e-5 assert abs(sympify(f1_1b) - 1.1) < 1e-5 assert abs(sympify(f1_1c) - 1.1) < 1e-5 assert _sympify(i5) == 5 assert isinstance(_sympify(i5), Integer) assert _sympify(i5b) == 5 assert isinstance(_sympify(i5b), Float) assert _sympify(i5c) == 5 assert isinstance(_sympify(i5c), Integer) assert abs(_sympify(f1_1) - 1.1) < 1e-5 assert abs(_sympify(f1_1b) - 1.1) < 1e-5 assert abs(_sympify(f1_1c) - 1.1) < 1e-5 def test_evaluate_false(): cases = { '2 + 3': Add(2, 3, evaluate=False), '2**2 / 3': Mul(Pow(2, 2, evaluate=False), Pow(3, -1, evaluate=False), evaluate=False), '2 + 3 * 5': Add(2, Mul(3, 5, evaluate=False), evaluate=False), '2 - 3 * 5': Add(2, -Mul(3, 5, evaluate=False), evaluate=False), '1 / 3': Mul(1, Pow(3, -1, evaluate=False), evaluate=False), 'True | False': Or(True, False, evaluate=False), '1 + 2 + 3 + 5*3 + integrate(x)': Add(1, 2, 3, Mul(5, 3, evaluate=False), x**2/2, evaluate=False), '2 * 4 * 6 + 8': Add(Mul(2, 4, 6, evaluate=False), 8, evaluate=False), } for case, result in cases.items(): assert sympify(case, evaluate=False) == result def test_issue1034(): a = sympify('Integer(4)') assert a == Integer(4) assert a.is_Integer def test_issue883(): a = [3, 2.0] assert sympify(a) == [Integer(3), Float(2.0)] assert sympify(tuple(a)) == Tuple(Integer(3), Float(2.0)) assert sympify(set(a)) == set([Integer(3), Float(2.0)]) def test_S_sympify(): assert S(1)/2 == sympify(1)/2 assert (-2)**(S(1)/2) == sqrt(2)*I def test_issue1689(): assert srepr(S(1.0 + 0J)) == srepr(S(1.0)) == srepr(Float(1.0)) def test_issue1699_None(): assert S(None) is None def test_issue3218(): assert sympify("x+\ny") == x + y def test_issue1889_builtins(): C = Symbol('C') vars = {} vars['C'] = C exp1 = sympify('C') assert exp1 == C # Make sure it did not get mixed up with sympy.C exp2 = sympify('C', vars) assert exp2 == C # Make sure it did not get mixed up with sympy.C def test_geometry(): p = sympify(Point(0, 1)) assert p == Point(0, 1) and type(p) == Point L = sympify(Line(p, (1, 0))) assert L == Line((0, 1), (1, 0)) and type(L) == Line def test_kernS(): s = '-1 - 2*(-(-x + 1/x)/(x*(x - 1/x)**2) - 1/(x*(x - 1/x)))' # when 1497 is fixed, this no longer should pass: the expression # should be unchanged assert -1 - 2*(-(-x + 1/x)/(x*(x - 1/x)**2) - 1/(x*(x - 1/x))) == -1 # sympification should not allow the constant to enter a Mul # or else the structure can change dramatically ss = kernS(s) assert ss != -1 and ss.simplify() == -1 s = '-1 - 2*(-(-x + 1/x)/(x*(x - 1/x)**2) - 1/(x*(x - 1/x)))'.replace( 'x', '_kern') ss = kernS(s) assert ss != -1 and ss.simplify() == -1 # issue 3588 assert kernS('Interval(-1,-2 - 4*(-3))') == Interval(-1, 10) assert kernS('_kern') == Symbol('_kern') assert kernS('E**-(x)') == exp(-x) e = 2*(x + y)*y assert kernS(['2*(x + y)*y', ('2*(x + y)*y',)]) == [e, (e,)] assert kernS('-(2*sin(x)**2 + 2*sin(x)*cos(x))*y/2') == \ -y*(2*sin(x)**2 + 2*sin(x)*cos(x))/2 def test_issue_3441_3453(): assert S('[[1/3,2], (2/5,)]') == [[Rational(1, 3), 2], (Rational(2, 5),)] assert S('[[2/6,2], (2/4,)]') == [[Rational(1, 3), 2], (Rational(1, 2),)] assert S('[[[2*(1)]]]') == [[[2]]] assert S('Matrix([2*(1)])') == Matrix([2]) def test_issue_2497(): assert str(S("Q & C", locals=_clash1)) == 'And(C, Q)' assert str(S('pi(x)', locals=_clash2)) == 'pi(x)' assert str(S('pi(C, Q)', locals=_clash)) == 'pi(C, Q)' locals = {} exec_("from sympy.abc import Q, C", locals) assert str(S('C&Q', locals)) == 'And(C, Q)' sympy-0.7.4.1/sympy/core/tests/test_symbol.py0000644000175000017500000002352012253362407021437 0ustar georgeskgeorgeskfrom sympy import (Symbol, Wild, GreaterThan, LessThan, StrictGreaterThan, StrictLessThan, pi, I, Rational, sympify, symbols, Dummy, Function, flatten ) from sympy.core.compatibility import u from sympy.utilities.pytest import raises from sympy.utilities.exceptions import SymPyDeprecationWarning def test_Symbol(): a = Symbol("a") x1 = Symbol("x") x2 = Symbol("x") xdummy1 = Dummy("x") xdummy2 = Dummy("x") assert a != x1 assert a != x2 assert x1 == x2 assert x1 != xdummy1 assert xdummy1 != xdummy2 assert Symbol("x") == Symbol("x") assert Dummy("x") != Dummy("x") d = symbols('d', cls=Dummy) assert isinstance(d, Dummy) c, d = symbols('c,d', cls=Dummy) assert isinstance(c, Dummy) assert isinstance(d, Dummy) raises(TypeError, lambda: Symbol()) def test_Dummy(): assert Dummy() != Dummy() Dummy._count = 0 d1 = Dummy() Dummy._count = 0 assert d1 == Dummy() def test_as_dummy(): x = Symbol('x') x1 = x.as_dummy() assert x1 != x assert x1 != x.as_dummy() x = Symbol('x', commutative=False) x1 = x.as_dummy() assert x1 != x assert x1.is_commutative is False def test_lt_gt(): from sympy import sympify as S x, y = Symbol('x'), Symbol('y') assert (x >= y) == GreaterThan(x, y) assert (x >= 0) == GreaterThan(x, 0) assert (x <= y) == LessThan(x, y) assert (x <= 0) == LessThan(x, 0) assert (0 <= x) == GreaterThan(x, 0) assert (0 >= x) == LessThan(x, 0) assert (S(0) >= x) == GreaterThan(0, x) assert (S(0) <= x) == LessThan(0, x) assert (x > y) == StrictGreaterThan(x, y) assert (x > 0) == StrictGreaterThan(x, 0) assert (x < y) == StrictLessThan(x, y) assert (x < 0) == StrictLessThan(x, 0) assert (0 < x) == StrictGreaterThan(x, 0) assert (0 > x) == StrictLessThan(x, 0) assert (S(0) > x) == StrictGreaterThan(0, x) assert (S(0) < x) == StrictLessThan(0, x) e = x**2 + 4*x + 1 assert (e >= 0) == GreaterThan(e, 0) assert (0 <= e) == GreaterThan(e, 0) assert (e > 0) == StrictGreaterThan(e, 0) assert (0 < e) == StrictGreaterThan(e, 0) assert (e <= 0) == LessThan(e, 0) assert (0 >= e) == LessThan(e, 0) assert (e < 0) == StrictLessThan(e, 0) assert (0 > e) == StrictLessThan(e, 0) assert (S(0) >= e) == GreaterThan(0, e) assert (S(0) <= e) == LessThan(0, e) assert (S(0) < e) == StrictLessThan(0, e) assert (S(0) > e) == StrictGreaterThan(0, e) def test_no_len(): # there should be no len for numbers x = Symbol('x') raises(TypeError, lambda: len(x)) def test_ineq_unequal(): S = sympify x, y, z = symbols('x,y,z') e = ( S(-1) >= x, S(-1) >= y, S(-1) >= z, S(-1) > x, S(-1) > y, S(-1) > z, S(-1) <= x, S(-1) <= y, S(-1) <= z, S(-1) < x, S(-1) < y, S(-1) < z, S(0) >= x, S(0) >= y, S(0) >= z, S(0) > x, S(0) > y, S(0) > z, S(0) <= x, S(0) <= y, S(0) <= z, S(0) < x, S(0) < y, S(0) < z, S('3/7') >= x, S('3/7') >= y, S('3/7') >= z, S('3/7') > x, S('3/7') > y, S('3/7') > z, S('3/7') <= x, S('3/7') <= y, S('3/7') <= z, S('3/7') < x, S('3/7') < y, S('3/7') < z, S(1.5) >= x, S(1.5) >= y, S(1.5) >= z, S(1.5) > x, S(1.5) > y, S(1.5) > z, S(1.5) <= x, S(1.5) <= y, S(1.5) <= z, S(1.5) < x, S(1.5) < y, S(1.5) < z, S(2) >= x, S(2) >= y, S(2) >= z, S(2) > x, S(2) > y, S(2) > z, S(2) <= x, S(2) <= y, S(2) <= z, S(2) < x, S(2) < y, S(2) < z, x >= -1, y >= -1, z >= -1, x > -1, y > -1, z > -1, x <= -1, y <= -1, z <= -1, x < -1, y < -1, z < -1, x >= 0, y >= 0, z >= 0, x > 0, y > 0, z > 0, x <= 0, y <= 0, z <= 0, x < 0, y < 0, z < 0, x >= 1.5, y >= 1.5, z >= 1.5, x > 1.5, y > 1.5, z > 1.5, x <= 1.5, y <= 1.5, z <= 1.5, x < 1.5, y < 1.5, z < 1.5, x >= 2, y >= 2, z >= 2, x > 2, y > 2, z > 2, x <= 2, y <= 2, z <= 2, x < 2, y < 2, z < 2, x >= y, x >= z, y >= x, y >= z, z >= x, z >= y, x > y, x > z, y > x, y > z, z > x, z > y, x <= y, x <= z, y <= x, y <= z, z <= x, z <= y, x < y, x < z, y < x, y < z, z < x, z < y, x - pi >= y + z, y - pi >= x + z, z - pi >= x + y, x - pi > y + z, y - pi > x + z, z - pi > x + y, x - pi <= y + z, y - pi <= x + z, z - pi <= x + y, x - pi < y + z, y - pi < x + z, z - pi < x + y, True, False ) left_e = e[:-1] for i, e1 in enumerate( left_e ): for e2 in e[i + 1:]: assert e1 != e2 def test_Wild_properties(): # these tests only include Atoms x = Symbol("x") y = Symbol("y") p = Symbol("p", positive=True) k = Symbol("k", integer=True) n = Symbol("n", integer=True, positive=True) given_patterns = [ x, y, p, k, -k, n, -n, sympify(-3), sympify(3), pi, Rational(3, 2), I ] integerp = lambda k: k.is_integer positivep = lambda k: k.is_positive symbolp = lambda k: k.is_Symbol realp = lambda k: k.is_real S = Wild("S", properties=[symbolp]) R = Wild("R", properties=[realp]) Y = Wild("Y", exclude=[x, p, k, n]) P = Wild("P", properties=[positivep]) K = Wild("K", properties=[integerp]) N = Wild("N", properties=[positivep, integerp]) given_wildcards = [ S, R, Y, P, K, N ] goodmatch = { S: (x, y, p, k, n), R: (p, k, -k, n, -n, -3, 3, pi, Rational(3, 2)), Y: (y, -3, 3, pi, Rational(3, 2), I ), P: (p, n, 3, pi, Rational(3, 2)), K: (k, -k, n, -n, -3, 3), N: (n, 3)} for A in given_wildcards: for pat in given_patterns: d = pat.match(A) if pat in goodmatch[A]: assert d[A] in goodmatch[A] else: assert d is None def test_symbols(): x = Symbol('x') y = Symbol('y') z = Symbol('z') assert symbols('x') == x assert symbols('x ') == x assert symbols(' x ') == x assert symbols('x,') == (x,) assert symbols('x, ') == (x,) assert symbols('x ,') == (x,) assert symbols('x , y') == (x, y) assert symbols('x,y,z') == (x, y, z) assert symbols('x y z') == (x, y, z) assert symbols('x,y,z,') == (x, y, z) assert symbols('x y z ') == (x, y, z) xyz = Symbol('xyz') abc = Symbol('abc') assert symbols('xyz') == xyz assert symbols('xyz,') == (xyz,) assert symbols('xyz,abc') == (xyz, abc) assert symbols(('xyz',)) == (xyz,) assert symbols(('xyz,',)) == ((xyz,),) assert symbols(('x,y,z,',)) == ((x, y, z),) assert symbols(('xyz', 'abc')) == (xyz, abc) assert symbols(('xyz,abc',)) == ((xyz, abc),) assert symbols(('xyz,abc', 'x,y,z')) == ((xyz, abc), (x, y, z)) assert symbols(('x', 'y', 'z')) == (x, y, z) assert symbols(['x', 'y', 'z']) == [x, y, z] assert symbols(set(['x', 'y', 'z'])) == set([x, y, z]) raises(ValueError, lambda: symbols('')) raises(ValueError, lambda: symbols(',')) raises(ValueError, lambda: symbols('x,,y,,z')) raises(ValueError, lambda: symbols(('x', '', 'y', '', 'z'))) a, b = symbols('x,y', real=True) assert a.is_real and b.is_real x0 = Symbol('x0') x1 = Symbol('x1') x2 = Symbol('x2') y0 = Symbol('y0') y1 = Symbol('y1') assert symbols('x0:0') == () assert symbols('x0:1') == (x0,) assert symbols('x0:2') == (x0, x1) assert symbols('x0:3') == (x0, x1, x2) assert symbols('x:0') == () assert symbols('x:1') == (x0,) assert symbols('x:2') == (x0, x1) assert symbols('x:3') == (x0, x1, x2) assert symbols('x1:1') == () assert symbols('x1:2') == (x1,) assert symbols('x1:3') == (x1, x2) assert symbols('x1:3,x,y,z') == (x1, x2, x, y, z) assert symbols('x:3,y:2') == (x0, x1, x2, y0, y1) assert symbols(('x:3', 'y:2')) == ((x0, x1, x2), (y0, y1)) a = Symbol('a') b = Symbol('b') c = Symbol('c') d = Symbol('d') assert symbols('x:z') == (x, y, z) assert symbols('a:d,x:z') == (a, b, c, d, x, y, z) assert symbols(('a:d', 'x:z')) == ((a, b, c, d), (x, y, z)) aa = Symbol('aa') ab = Symbol('ab') ac = Symbol('ac') ad = Symbol('ad') assert symbols('aa:d') == (aa, ab, ac, ad) assert symbols('aa:d,x:z') == (aa, ab, ac, ad, x, y, z) assert symbols(('aa:d','x:z')) == ((aa, ab, ac, ad), (x, y, z)) # issue 3576 def sym(s): return str(symbols(s)) assert sym('a0:4') == '(a0, a1, a2, a3)' assert sym('a2:4,b1:3') == '(a2, a3, b1, b2)' assert sym('a1(2:4)') == '(a12, a13)' assert sym(('a0:2.0:2')) == '(a0.0, a0.1, a1.0, a1.1)' assert sym(('aa:cz')) == '(aaz, abz, acz)' assert sym('aa:c0:2') == '(aa0, aa1, ab0, ab1, ac0, ac1)' assert sym('aa:ba:b') == '(aaa, aab, aba, abb)' assert sym('a:3b') == '(a0b, a1b, a2b)' assert sym('a-1:3b') == '(a-1b, a-2b)' assert sym('a:2\,:2' + chr(0)) == '(a0,0%s, a0,1%s, a1,0%s, a1,1%s)' % ( (chr(0),)*4) assert sym('x(:a:3)') == '(x(a0), x(a1), x(a2))' assert sym('x(:c):1') == '(xa0, xb0, xc0)' assert sym('x((:a)):3') == '(x(a)0, x(a)1, x(a)2)' assert sym('x(:a:3') == '(x(a0, x(a1, x(a2)' assert sym(':2') == '(0, 1)' assert sym(':b') == '(a, b)' assert sym(':b:2') == '(a0, a1, b0, b1)' assert sym(':2:2') == '(00, 01, 10, 11)' assert sym(':b:b') == '(aa, ab, ba, bb)' raises(ValueError, lambda: symbols(':')) raises(ValueError, lambda: symbols('a:')) raises(ValueError, lambda: symbols('::')) raises(ValueError, lambda: symbols('a::')) raises(ValueError, lambda: symbols(':a:')) raises(ValueError, lambda: symbols('::a')) def test_call(): f = Symbol('f') assert f(2) raises(TypeError, lambda: Wild('x')(1)) def test_unicode(): xu = Symbol(u('x')) x = Symbol('x') assert x == xu raises(TypeError, lambda: Symbol(1)) sympy-0.7.4.1/sympy/core/tests/test_evalf.py0000644000175000017500000003757512253362407021246 0ustar georgeskgeorgeskfrom sympy import (Add, ceiling, cos, E, Eq, exp, factorial, fibonacci, floor, Function, GoldenRatio, I, log, Mul, oo, pi, Pow, Rational, sin, sqrt, sstr, Sum, sympify, S, integrate, atan, product) from sympy.core.evalf import complex_accuracy, PrecisionExhausted, scaled_zero from sympy.core.compatibility import long from sympy.mpmath import inf, ninf, nan from sympy.abc import n, x, y from sympy.mpmath.libmp.libmpf import from_float from sympy.utilities.pytest import raises, XFAIL def NS(e, n=15, **options): return sstr(sympify(e).evalf(n, **options), full_prec=True) def test_evalf_helpers(): assert complex_accuracy((from_float(2.0), None, 35, None)) == 35 assert complex_accuracy((from_float(2.0), from_float(10.0), 35, 100)) == 37 assert complex_accuracy( (from_float(2.0), from_float(1000.0), 35, 100)) == 43 assert complex_accuracy((from_float(2.0), from_float(10.0), 100, 35)) == 35 assert complex_accuracy( (from_float(2.0), from_float(1000.0), 100, 35)) == 35 def test_evalf_basic(): assert NS('pi', 15) == '3.14159265358979' assert NS('2/3', 10) == '0.6666666667' assert NS('355/113-pi', 6) == '2.66764e-7' assert NS('16*atan(1/5)-4*atan(1/239)', 15) == '3.14159265358979' def test_cancellation(): assert NS(Add(pi, Rational(1, 10**1000), -pi, evaluate=False), 15, maxn=1200) == '1.00000000000000e-1000' def test_evalf_powers(): assert NS('pi**(10**20)', 10) == '1.339148777e+49714987269413385435' assert NS(pi**(10**100), 10) == ('4.946362032e+4971498726941338543512682882' '9089887365167832438044244613405349992494711208' '95526746555473864642912223') assert NS('2**(1/10**50)', 15) == '1.00000000000000' assert NS('2**(1/10**50)-1', 15) == '6.93147180559945e-51' # Evaluation of Rump's ill-conditioned polynomial def test_evalf_rump(): a = 1335*y**6/4 + x**2*(11*x**2*y**2 - y**6 - 121*y**4 - 2) + 11*y**8/2 + x/(2*y) assert NS(a, 15, subs={x: 77617, y: 33096}) == '-0.827396059946821' def test_evalf_complex(): assert NS('2*sqrt(pi)*I', 10) == '3.544907702*I' assert NS('3+3*I', 15) == '3.00000000000000 + 3.00000000000000*I' assert NS('E+pi*I', 15) == '2.71828182845905 + 3.14159265358979*I' assert NS('pi * (3+4*I)', 15) == '9.42477796076938 + 12.5663706143592*I' assert NS('I*(2+I)', 15) == '-1.00000000000000 + 2.00000000000000*I' @XFAIL def test_evalf_complex_bug(): assert NS('(pi+E*I)*(E+pi*I)', 15) in ('0.e-15 + 17.25866050002*I', '0.e-17 + 17.25866050002*I', '-0.e-17 + 17.25866050002*I') def test_evalf_complex_powers(): assert NS('(E+pi*I)**100000000000000000') == \ '-3.58896782867793e+61850354284995199 + 4.58581754997159e+61850354284995199*I' # XXX: rewrite if a+a*I simplification introduced in sympy #assert NS('(pi + pi*I)**2') in ('0.e-15 + 19.7392088021787*I', '0.e-16 + 19.7392088021787*I') assert NS('(pi + pi*I)**2', chop=True) == '19.7392088021787*I' assert NS( '(pi + 1/10**8 + pi*I)**2') == '6.2831853e-8 + 19.7392088650106*I' assert NS('(pi + 1/10**12 + pi*I)**2') == '6.283e-12 + 19.7392088021850*I' assert NS('(pi + pi*I)**4', chop=True) == '-389.636364136010' assert NS( '(pi + 1/10**8 + pi*I)**4') == '-389.636366616512 + 2.4805021e-6*I' assert NS('(pi + 1/10**12 + pi*I)**4') == '-389.636364136258 + 2.481e-10*I' assert NS( '(10000*pi + 10000*pi*I)**4', chop=True) == '-3.89636364136010e+18' @XFAIL def test_evalf_complex_powers_bug(): assert NS('(pi + pi*I)**4') == '-389.63636413601 + 0.e-14*I' def test_evalf_exponentiation(): assert NS(sqrt(-pi)) == '1.77245385090552*I' assert NS(Pow(pi*I, Rational( 1, 2), evaluate=False)) == '1.25331413731550 + 1.25331413731550*I' assert NS(pi**I) == '0.413292116101594 + 0.910598499212615*I' assert NS(pi**(E + I/3)) == '20.8438653991931 + 8.36343473930031*I' assert NS((pi + I/3)**(E + I/3)) == '17.2442906093590 + 13.6839376767037*I' assert NS(exp(pi)) == '23.1406926327793' assert NS(exp(pi + E*I)) == '-21.0981542849657 + 9.50576358282422*I' assert NS(pi**pi) == '36.4621596072079' assert NS((-pi)**pi) == '-32.9138577418939 - 15.6897116534332*I' assert NS((-pi)**(-pi)) == '-0.0247567717232697 + 0.0118013091280262*I' # An example from Smith, "Multiple Precision Complex Arithmetic and Functions" def test_evalf_complex_cancellation(): A = Rational('63287/100000') B = Rational('52498/100000') C = Rational('69301/100000') D = Rational('83542/100000') F = Rational('2231321613/2500000000') # XXX: the number of returned mantissa digits in the real part could # change with the implementation. What matters is that the returned digits are # correct; those that are showing now are correct. # >>> ((A+B*I)*(C+D*I)).expand() # 64471/10000000000 + 2231321613*I/2500000000 # >>> 2231321613*4 # 8925286452L assert NS((A + B*I)*(C + D*I), 6) == '6.44710e-6 + 0.892529*I' assert NS((A + B*I)*(C + D*I), 10) == '6.447100000e-6 + 0.8925286452*I' assert NS((A + B*I)*( C + D*I) - F*I, 5) in ('6.4471e-6 + 0.e-14*I', '6.4471e-6 - 0.e-14*I') def test_evalf_logs(): assert NS("log(3+pi*I)", 15) == '1.46877619736226 + 0.808448792630022*I' assert NS("log(pi*I)", 15) == '1.14472988584940 + 1.57079632679490*I' def test_evalf_trig(): assert NS('sin(1)', 15) == '0.841470984807897' assert NS('cos(1)', 15) == '0.540302305868140' assert NS('sin(10**-6)', 15) == '9.99999999999833e-7' assert NS('cos(10**-6)', 15) == '0.999999999999500' assert NS('sin(E*10**100)', 15) == '0.409160531722613' # Some input near roots assert NS(sin(exp(pi*sqrt(163))*pi), 15) == '-2.35596641936785e-12' assert NS(sin(pi*10**100 + Rational(7, 10**5), evaluate=False), 15, maxn=120) == \ '6.99999999428333e-5' assert NS(sin(Rational(7, 10**5), evaluate=False), 15) == \ '6.99999999428333e-5' # Check detection of various false identities def test_evalf_near_integers(): # Binet's formula f = lambda n: ((1 + sqrt(5))**n)/(2**n * sqrt(5)) assert NS(f(5000) - fibonacci(5000), 10, maxn=1500) == '5.156009964e-1046' # Some near-integer identities from # http://mathworld.wolfram.com/AlmostInteger.html assert NS('sin(2017*2**(1/5))', 15) == '-1.00000000000000' assert NS('sin(2017*2**(1/5))', 20) == '-0.99999999999999997857' assert NS('1+sin(2017*2**(1/5))', 15) == '2.14322287389390e-17' assert NS('45 - 613*E/37 + 35/991', 15) == '6.03764498766326e-11' def test_evalf_ramanujan(): assert NS(exp(pi*sqrt(163)) - 640320**3 - 744, 10) == '-7.499274028e-13' # A related identity A = 262537412640768744*exp(-pi*sqrt(163)) B = 196884*exp(-2*pi*sqrt(163)) C = 103378831900730205293632*exp(-3*pi*sqrt(163)) assert NS(1 - A - B + C, 10) == '1.613679005e-59' # Input that for various reasons have failed at some point def test_evalf_bugs(): assert NS(sin(1) + exp(-10**10), 10) == NS(sin(1), 10) assert NS(exp(10**10) + sin(1), 10) == NS(exp(10**10), 10) assert NS('log(1+1/10**50)', 20) == '1.0000000000000000000e-50' assert NS('log(10**100,10)', 10) == '100.0000000' assert NS('log(2)', 10) == '0.6931471806' assert NS( '(sin(x)-x)/x**3', 15, subs={x: '1/10**50'}) == '-0.166666666666667' assert NS(sin(1) + Rational( 1, 10**100)*I, 15) == '0.841470984807897 + 1.00000000000000e-100*I' assert x.evalf() == x assert NS((1 + I)**2*I, 6) == '-2.00000' d = {n: ( -1)**Rational(6, 7), y: (-1)**Rational(4, 7), x: (-1)**Rational(2, 7)} assert NS((x*(1 + y*(1 + n))).subs(d).evalf(), 6) == '0.346011 + 0.433884*I' assert NS(((-I - sqrt(2)*I)**2).evalf()) == '-5.82842712474619' assert NS((1 + I)**2*I, 15) == '-2.00000000000000' #1659 (1/2): assert NS(pi.evalf(69) - pi) == '-4.43863937855894e-71' #1659 (2/2): With the bug present, this still only fails if the # terms are in the order given here. This is not generally the case, # because the order depends on the hashes of the terms. assert NS(20 - 5008329267844*n**25 - 477638700*n**37 - 19*n, subs={n: .01}) == '19.8100000000000' assert NS(((x - 1)*((1 - x))**1000).n() ) == '(-x + 1.00000000000000)**1000*(x - 1.00000000000000)' assert NS((-x).n()) == '-x' assert NS((-2*x).n()) == '-2.00000000000000*x' assert NS((-2*x*y).n()) == '-2.00000000000000*x*y' assert cos(x).n(subs={x: 1+I}) == cos(x).subs(x, 1+I).n() #3561. Also NaN != mpmath.nan # In this order: # 0*nan, 0/nan, 0*inf, 0/inf # 0+nan, 0-nan, 0+inf, 0-inf # >>> n = Some Number # n*nan, n/nan, n*inf, n/inf # n+nan, n-nan, n+inf, n-inf assert (0*sin(oo)).n() == S.Zero assert (0/sin(oo)).n() == S.Zero assert (0*E**(oo)).n() == S.NaN assert (0/E**(oo)).n() == S.Zero assert (0+sin(oo)).n() == S.NaN assert (0-sin(oo)).n() == S.NaN assert (0+E**(oo)).n() == S.Infinity assert (0-E**(oo)).n() == S.NegativeInfinity assert (5*sin(oo)).n() == S.NaN assert (5/sin(oo)).n() == S.NaN assert (5*E**(oo)).n() == S.Infinity assert (5/E**(oo)).n() == S.Zero assert (5+sin(oo)).n() == S.NaN assert (5-sin(oo)).n() == S.NaN assert (5+E**(oo)).n() == S.Infinity assert (5-E**(oo)).n() == S.NegativeInfinity def test_evalf_integer_parts(): a = floor(log(8)/log(2) - exp(-1000), evaluate=False) b = floor(log(8)/log(2), evaluate=False) raises(PrecisionExhausted, lambda: a.evalf()) assert a.evalf(chop=True) == 3 assert a.evalf(maxn=500) == 2 assert b.evalf() == 3 # equals, as a fallback, can still fail but it might succeed as here assert ceiling(10*(sin(1)**2 + cos(1)**2)) == 10 assert int(floor(factorial(50)/E, evaluate=False).evalf(70)) == \ long(11188719610782480504630258070757734324011354208865721592720336800) assert int(ceiling(factorial(50)/E, evaluate=False).evalf(70)) == \ long(11188719610782480504630258070757734324011354208865721592720336801) assert int(floor((GoldenRatio**999 / sqrt(5) + Rational(1, 2))) .evalf(1000)) == fibonacci(999) assert int(floor((GoldenRatio**1000 / sqrt(5) + Rational(1, 2))) .evalf(1000)) == fibonacci(1000) def test_evalf_trig_zero_detection(): a = sin(160*pi, evaluate=False) t = a.evalf(maxn=100) assert abs(t) < 1e-100 assert t._prec < 2 assert a.evalf(chop=True) == 0 raises(PrecisionExhausted, lambda: a.evalf(strict=True)) def test_evalf_sum(): assert Sum(n,(n,1,2)).evalf() == 3. assert Sum(n,(n,1,2)).doit().evalf() == 3. # the next test should return instantly assert Sum(1/n,(n,1,2)).evalf() == 1.5 def test_evalf_divergent_series(): raises(ValueError, lambda: Sum(1/n, (n, 1, oo)).evalf()) raises(ValueError, lambda: Sum(n/(n**2 + 1), (n, 1, oo)).evalf()) raises(ValueError, lambda: Sum((-1)**n, (n, 1, oo)).evalf()) raises(ValueError, lambda: Sum((-1)**n, (n, 1, oo)).evalf()) raises(ValueError, lambda: Sum(n**2, (n, 1, oo)).evalf()) raises(ValueError, lambda: Sum(2**n, (n, 1, oo)).evalf()) raises(ValueError, lambda: Sum((-2)**n, (n, 1, oo)).evalf()) raises(ValueError, lambda: Sum((2*n + 3)/(3*n**2 + 4), (n, 0, oo)).evalf()) raises(ValueError, lambda: Sum((0.5*n**3)/(n**4 + 1), (n, 0, oo)).evalf()) def test_evalf_py_methods(): assert abs(float(pi + 1) - 4.1415926535897932) < 1e-10 assert abs(complex(pi + 1) - 4.1415926535897932) < 1e-10 assert abs( complex(pi + E*I) - (3.1415926535897931 + 2.7182818284590451j)) < 1e-10 raises(TypeError, lambda: float(pi + x)) def test_evalf_power_subs_bugs(): assert (x**2).evalf(subs={x: 0}) == 0 assert sqrt(x).evalf(subs={x: 0}) == 0 assert (x**Rational(2, 3)).evalf(subs={x: 0}) == 0 assert (x**x).evalf(subs={x: 0}) == 1 assert (3**x).evalf(subs={x: 0}) == 1 assert exp(x).evalf(subs={x: 0}) == 1 assert ((2 + I)**x).evalf(subs={x: 0}) == 1 assert (0**x).evalf(subs={x: 0}) == 1 def test_evalf_arguments(): raises(TypeError, lambda: pi.evalf(method="garbage")) def test_implemented_function_evalf(): from sympy.utilities.lambdify import implemented_function f = Function('f') f = implemented_function(f, lambda x: x + 1) assert str(f(x)) == "f(x)" assert str(f(2)) == "f(2)" assert f(2).evalf() == 3 assert f(x).evalf() == f(x) del f._imp_ # XXX: due to caching _imp_ would influence all other tests def test_evaluate_false(): for no in [0, False, None]: assert Add(3, 2, evaluate=no).is_Add assert Mul(3, 2, evaluate=no).is_Mul assert Pow(3, 2, evaluate=no).is_Pow assert Pow(y, 2, evaluate=True) - Pow(y, 2, evaluate=True) == 0 def test_evalf_relational(): assert Eq(x/5, y/10).evalf() == Eq(0.2*x, 0.1*y) def test_issue_2387(): assert not cos(sqrt(0.5 + I)).n().is_Function def test_issue_2387_bug(): from sympy import I, Expr assert abs(Expr._from_mpmath(I._to_mpmath(15), 15) - I) < 1.0e-15 def test_bugs(): from sympy import polar_lift, re assert abs(re((1 + I)**2)) < 1e-15 # anything that evalf's to 0 will do in place of polar_lift assert abs(polar_lift(0)).n() == 0 def test_subs_bugs(): from sympy import besseli assert NS('besseli(-x, y) - besseli(x, y)', subs={x: 3.5, y: 20.0}) == \ '-4.92535585957223e-10' assert NS('Piecewise((x, x>0)) + Piecewise((1-x, x>0))', subs={x: 0.1}) == \ '1.00000000000000' def test_issue_1857_2105(): # 1857 v = S('''(-27*12**(1/3)*sqrt(31)*I + 27*2**(2/3)*3**(1/3)*sqrt(31)*I)/(-2511*2**(2/3)*3**(1/3) + (29*18**(1/3) + 9*2**(1/3)*3**(2/3)*sqrt(31)*I + 87*2**(1/3)*3**(1/6)*I)**2)''') assert NS(v, 1) == '0.e-118 - 0.e-118*I' # 2105 v = S('''-(357587765856 + 18873261792*249**(1/2) + 56619785376*I*83**(1/2) + 108755765856*I*3**(1/2) + 41281887168*6**(1/3)*(1422 + 54*249**(1/2))**(1/3) - 1239810624*6**(1/3)*249**(1/2)*(1422 + 54*249**(1/2))**(1/3) - 3110400000*I*6**(1/3)*83**(1/2)*(1422 + 54*249**(1/2))**(1/3) + 13478400000*I*3**(1/2)*6**(1/3)*(1422 + 54*249**(1/2))**(1/3) + 1274950152*6**(2/3)*(1422 + 54*249**(1/2))**(2/3) + 32347944*6**(2/3)*249**(1/2)*(1422 + 54*249**(1/2))**(2/3) - 1758790152*I*3**(1/2)*6**(2/3)*(1422 + 54*249**(1/2))**(2/3) - 304403832*I*6**(2/3)*83**(1/2)*(1422 + 4*249**(1/2))**(2/3))/(175732658352 + (1106028 + 25596*249**(1/2) + 76788*I*83**(1/2))**2)''') assert NS(v, 5) == '0.077284 + 1.1104*I' assert NS(v, 1) == '0.08 + 1.*I' def test_old_docstring(): a = (E + pi*I)*(E - pi*I) assert NS(a) == '17.2586605000200' assert a.n() == 17.25866050002001 def test_issue_1707(): assert integrate(atan(x)**2, (x, -1, 1)).evalf().round(1) == 0.5 assert atan(0, evaluate=False).n() == 0 def test_evalf_mul(): # sympy should not try to expand this; it should be handled term-wise # in evalf through mpmath assert NS(product(1 + sqrt(n)*I, (n, 1, 500)), 1) == '5.e+567 + 2.e+568*I' def test_scaled_zero(): a, b = (([0], 1, 100, 1), -1) assert scaled_zero(100) == (a, b) assert scaled_zero(a) == (0, 1, 100, 1) a, b = (([1], 1, 100, 1), -1) assert scaled_zero(100, -1) == (a, b) assert scaled_zero(a) == (1, 1, 100, 1) raises(ValueError, lambda: scaled_zero(scaled_zero(100))) raises(ValueError, lambda: scaled_zero(100, 2)) raises(ValueError, lambda: scaled_zero(100, 0)) raises(ValueError, lambda: scaled_zero((1, 5, 1, 3))) def test_chop_value(): for i in range(-27, 28): assert (Pow(10, i)*2).n(chop=10**i) and not (Pow(10, i)).n(chop=10**i) def test_infinities(): assert oo.evalf(chop=True) == inf assert (-oo).evalf(chop=True) == ninf def test_to_mpmath(): assert sqrt(3)._to_mpmath(20)._mpf_ == (0, long(908093), -19, 20) assert S(3.2)._to_mpmath(20)._mpf_ == (0, long(838861), -18, 20) def test_issue_3533_evalf(): add = (-100000*sqrt(2500000001) + 5000000001) assert add.n() == 9.999999998e-11 assert (add*add).n() == 9.999999996e-21 sympy-0.7.4.1/sympy/core/tests/test_sets.py0000644000175000017500000004666312253362407021125 0ustar georgeskgeorgeskfrom sympy import (Symbol, Set, Union, Interval, oo, S, sympify, nan, GreaterThan, LessThan, Max, Min, And, Or, Eq, Ge, Le, Gt, Lt, Float, FiniteSet, Intersection, imageset, I, true, false ) from sympy.mpmath import mpi from sympy.utilities.pytest import raises from sympy.utilities.pytest import raises, XFAIL def test_interval_arguments(): assert Interval(0, oo) == Interval(0, oo, False, True) assert Interval(0, oo).right_open is True assert Interval(-oo, 0) == Interval(-oo, 0, True, False) assert Interval(-oo, 0).left_open is True assert isinstance(Interval(1, 1), FiniteSet) assert Interval(1, 0) == S.EmptySet assert Interval(1, 1).measure == 0 assert Interval(1, 1, False, True) == S.EmptySet assert Interval(1, 1, True, False) == S.EmptySet assert Interval(1, 1, True, True) == S.EmptySet raises(ValueError, lambda: Interval(0, S.ImaginaryUnit)) raises(ValueError, lambda: Interval(0, Symbol('z'))) assert isinstance(Interval(1, Symbol('a', real=True)), Interval) def test_interval_symbolic_end_points(): a = Symbol('a', real=True) assert Union(Interval(0, a), Interval(0, 3)).sup == Max(a, 3) assert Union(Interval(a, 0), Interval(-3, 0)).inf == Min(-3, a) assert Interval(0, a).contains(1) == LessThan(1, a) def test_union(): assert Union(Interval(1, 2), Interval(2, 3)) == Interval(1, 3) assert Union(Interval(1, 2), Interval(2, 3, True)) == Interval(1, 3) assert Union(Interval(1, 3), Interval(2, 4)) == Interval(1, 4) assert Union(Interval(1, 2), Interval(1, 3)) == Interval(1, 3) assert Union(Interval(1, 3), Interval(1, 2)) == Interval(1, 3) assert Union(Interval(1, 3, False, True), Interval(1, 2)) == \ Interval(1, 3, False, True) assert Union(Interval(1, 3), Interval(1, 2, False, True)) == Interval(1, 3) assert Union(Interval(1, 2, True), Interval(1, 3)) == Interval(1, 3) assert Union(Interval(1, 2, True), Interval(1, 3, True)) == \ Interval(1, 3, True) assert Union(Interval(1, 2, True), Interval(1, 3, True, True)) == \ Interval(1, 3, True, True) assert Union(Interval(1, 2, True, True), Interval(1, 3, True)) == \ Interval(1, 3, True) assert Union(Interval(1, 3), Interval(2, 3)) == Interval(1, 3) assert Union(Interval(1, 3, False, True), Interval(2, 3)) == \ Interval(1, 3) assert Union(Interval(1, 2, False, True), Interval(2, 3, True)) != \ Interval(1, 3) assert Union(Interval(1, 2), S.EmptySet) == Interval(1, 2) assert Union(S.EmptySet) == S.EmptySet assert Union(Interval(0, 1), [FiniteSet(1.0/n) for n in range(1, 10)]) == \ Interval(0, 1) assert Interval(1, 2).union(Interval(2, 3)) == \ Interval(1, 2) + Interval(2, 3) assert Interval(1, 2).union(Interval(2, 3)) == Interval(1, 3) assert Union(Set()) == Set() assert FiniteSet(1) + FiniteSet(2) + FiniteSet(3) == FiniteSet(1, 2, 3) assert FiniteSet(['ham']) + FiniteSet(['eggs']) == FiniteSet('ham', 'eggs') assert FiniteSet(1, 2, 3) + S.EmptySet == FiniteSet(1, 2, 3) assert FiniteSet(1, 2, 3) & FiniteSet(2, 3, 4) == FiniteSet(2, 3) assert FiniteSet(1, 2, 3) | FiniteSet(2, 3, 4) == FiniteSet(1, 2, 3, 4) x = Symbol("x") y = Symbol("y") z = Symbol("z") assert S.EmptySet | FiniteSet(x, FiniteSet(y, z)) == \ FiniteSet(x, FiniteSet(y, z)) # Test that Intervals and FiniteSets play nicely assert Interval(1, 3) + FiniteSet(2) == Interval(1, 3) assert Interval(1, 3, True, True) + FiniteSet(3) == \ Interval(1, 3, True, False) X = Interval(1, 3) + FiniteSet(5) Y = Interval(1, 2) + FiniteSet(3) XandY = X.intersect(Y) assert 2 in X and 3 in X and 3 in XandY assert X.subset(XandY) and Y.subset(XandY) raises(TypeError, lambda: Union(1, 2, 3)) assert X.is_iterable is False def test_difference(): assert Interval(1, 3) - Interval(1, 2) == Interval(2, 3, True) assert Interval(1, 3) - Interval(2, 3) == Interval(1, 2, False, True) assert Interval(1, 3, True) - Interval(2, 3) == Interval(1, 2, True, True) assert Interval(1, 3, True) - Interval(2, 3, True) == \ Interval(1, 2, True, False) assert Interval(0, 2) - FiniteSet(1) == \ Union(Interval(0, 1, False, True), Interval(1, 2, True, False)) assert FiniteSet(1, 2, 3) - FiniteSet(2) == FiniteSet(1, 3) assert FiniteSet('ham', 'eggs') - FiniteSet(['eggs']) == FiniteSet(['ham']) assert FiniteSet(1, 2, 3, 4) - Interval(2, 10, True, False) == \ FiniteSet(1, 2) assert FiniteSet(1, 2, 3, 4) - S.EmptySet == FiniteSet(1, 2, 3, 4) assert Union(Interval(0, 2), FiniteSet(2, 3, 4)) - Interval(1, 3) == \ Union(Interval(0, 1, False, True), FiniteSet(4)) def test_complement(): assert Interval(0, 1).complement == \ Union(Interval(-oo, 0, True, True), Interval(1, oo, True, True)) assert Interval(0, 1, True, False).complement == \ Union(Interval(-oo, 0, True, False), Interval(1, oo, True, True)) assert Interval(0, 1, False, True).complement == \ Union(Interval(-oo, 0, True, True), Interval(1, oo, False, True)) assert Interval(0, 1, True, True).complement == \ Union(Interval(-oo, 0, True, False), Interval(1, oo, False, True)) assert -S.EmptySet == S.EmptySet.complement assert ~S.EmptySet == S.EmptySet.complement assert S.EmptySet.complement == S.UniversalSet assert S.UniversalSet.complement == S.EmptySet assert Union(Interval(0, 1), Interval(2, 3)).complement == \ Union(Interval(-oo, 0, True, True), Interval(1, 2, True, True), Interval(3, oo, True, True)) assert FiniteSet(0).complement == Union(Interval(-oo, 0, True, True), Interval(0, oo, True, True)) assert (FiniteSet(5) + Interval(S.NegativeInfinity, 0)).complement == \ Interval(0, 5, True, True) + Interval(5, S.Infinity, True, True) assert FiniteSet(1, 2, 3).complement == \ Interval(S.NegativeInfinity, 1, True, True) + Interval(1, 2, True, True) + \ Interval(2, 3, True, True) + Interval(3, S.Infinity, True, True) X = Interval(1, 3) + FiniteSet(5) assert X.intersect(X.complement) == S.EmptySet square = Interval(0, 1) * Interval(0, 1) notsquare = square.complement assert all(pt in square for pt in [(0, 0), (.5, .5), (1, 0), (1, 1)]) assert not any( pt in notsquare for pt in [(0, 0), (.5, .5), (1, 0), (1, 1)]) assert not any(pt in square for pt in [(-1, 0), (1.5, .5), (10, 10)]) assert all(pt in notsquare for pt in [(-1, 0), (1.5, .5), (10, 10)]) def test_intersect(): x = Symbol('x') assert Interval(0, 2).intersect(Interval(1, 2)) == Interval(1, 2) assert Interval(0, 2).intersect(Interval(1, 2, True)) == \ Interval(1, 2, True) assert Interval(0, 2, True).intersect(Interval(1, 2)) == \ Interval(1, 2, False, False) assert Interval(0, 2, True, True).intersect(Interval(1, 2)) == \ Interval(1, 2, False, True) assert Interval(0, 2).intersect(Union(Interval(0, 1), Interval(2, 3))) == \ Union(Interval(0, 1), Interval(2, 2)) assert FiniteSet(1, 2, x).intersect(FiniteSet(x)) == FiniteSet(x) assert FiniteSet('ham', 'eggs').intersect(FiniteSet(['ham'])) == \ FiniteSet(['ham']) assert FiniteSet(1, 2, 3, 4, 5).intersect(S.EmptySet) == S.EmptySet assert Interval(0, 5).intersect(FiniteSet(1, 3)) == FiniteSet(1, 3) assert Interval(0, 1, True, True).intersect(FiniteSet(1)) == S.EmptySet assert Union(Interval(0, 1), Interval(2, 3)).intersect(Interval(1, 2)) == \ Union(Interval(1, 1), Interval(2, 2)) assert Union(Interval(0, 1), Interval(2, 3)).intersect(Interval(0, 2)) == \ Union(Interval(0, 1), Interval(2, 2)) assert Union(Interval(0, 1), Interval(2, 3)).intersect(Interval(1, 2, True, True)) == \ S.EmptySet assert Union(Interval(0, 1), Interval(2, 3)).intersect(S.EmptySet) == \ S.EmptySet assert Union(Interval(0, 5), FiniteSet(['Ham'])).intersect(FiniteSet(2, 3, 4, 5, 6)) == \ FiniteSet(2, 3, 4, 5) def test_intersection(): # iterable i = Intersection(FiniteSet(1, 2, 3), Interval(2, 5), evaluate=False) assert i.is_iterable assert set(i) == set([S(2), S(3)]) # challenging intervals x = Symbol('x', real=True) i = Intersection(Interval(0, 3), Interval(x, 6)) assert (5 in i) is False raises(TypeError, lambda: 2 in i) # Singleton special cases assert Intersection(Interval(0, 1), S.EmptySet) == S.EmptySet assert Intersection(Interval(0, 1), S.UniversalSet) == Interval(0, 1) # Products line = Interval(0, 5) i = Intersection(line**2, line**3, evaluate=False) assert (2, 2) not in i assert (2, 2, 2) not in i raises(ValueError, lambda: list(i)) def test_interval_subs(): a = Symbol('a', real=True) assert Interval(0, a).subs(a, 2) == Interval(0, 2) assert Interval(a, 0).subs(a, 2) == S.EmptySet def test_interval_to_mpi(): assert Interval(0, 1).to_mpi() == mpi(0, 1) assert Interval(0, 1, True, False).to_mpi() == mpi(0, 1) assert type(Interval(0, 1).to_mpi()) == type(mpi(0, 1)) def test_measure(): a = Symbol('a', real=True) assert Interval(1, 3).measure == 2 assert Interval(0, a).measure == a assert Interval(1, a).measure == a - 1 assert Union(Interval(1, 2), Interval(3, 4)).measure == 2 assert Union(Interval(1, 2), Interval(3, 4), FiniteSet(5, 6, 7)).measure \ == 2 assert FiniteSet(1, 2, oo, a, -oo, -5).measure == 0 assert S.EmptySet.measure == 0 square = Interval(0, 10) * Interval(0, 10) offsetsquare = Interval(5, 15) * Interval(5, 15) band = Interval(-oo, oo) * Interval(2, 4) assert square.measure == offsetsquare.measure == 100 assert (square + offsetsquare).measure == 175 # there is some overlap assert (square - offsetsquare).measure == 75 assert (square * FiniteSet(1, 2, 3)).measure == 0 assert (square.intersect(band)).measure == 20 assert (square + band).measure == oo assert (band * FiniteSet(1, 2, 3)).measure == nan def test_subset(): assert Interval(0, 2).subset(Interval(0, 1)) is True assert Interval(0, 2).subset(Interval(0, 3)) is False assert FiniteSet(1, 2, 3, 4).subset(FiniteSet(1, 2)) assert FiniteSet(1, 2, 3, 4).subset(FiniteSet(4, 5)) is False assert Interval(0, 2).subset(FiniteSet(1)) assert Interval(0, 2, True, True).subset(FiniteSet(1, 2)) is False assert (Interval(0, 2, False, True) + FiniteSet(2, 3)).subset( Interval(1, 2) + FiniteSet(3)) assert Union(Interval(0, 1), Interval(2, 5)).subset(Interval(3, 4)) is True assert Union(Interval(0, 1), Interval(2, 5)).subset(Interval(3, 6)) is False assert Interval(0, 5).subset(FiniteSet(1, 2, 3, 4)) is True assert FiniteSet(1, 2, 3).subset(S.EmptySet) is True assert S.EmptySet.subset(Interval(0, 1)) is False assert S.EmptySet.subset(S.EmptySet) is True raises(ValueError, lambda: S.EmptySet.subset(1)) def test_contains(): assert Interval(0, 2).contains(1) is True assert Interval(0, 2).contains(3) is False assert Interval(0, 2, True, False).contains(0) is False assert Interval(0, 2, True, False).contains(2) is True assert Interval(0, 2, False, True).contains(0) is True assert Interval(0, 2, False, True).contains(2) is False assert Interval(0, 2, True, True).contains(0) is False assert Interval(0, 2, True, True).contains(2) is False assert FiniteSet(1, 2, 3).contains(2) assert FiniteSet(1, 2, Symbol('x')).contains(Symbol('x')) items = [1, 2, S.Infinity, S('ham'), -1.1] fset = FiniteSet(*items) assert all(item in fset for item in items) assert all(fset.contains(item) is True for item in items) assert Union(Interval(0, 1), Interval(2, 5)).contains(3) is True assert Union(Interval(0, 1), Interval(2, 5)).contains(6) is False assert Union(Interval(0, 1), FiniteSet(2, 5)).contains(3) is False assert S.EmptySet.contains(1) is False def test_interval_symbolic(): x = Symbol('x') e = Interval(0, 1) assert e.contains(x) == And(0 <= x, x <= 1) raises(TypeError, lambda: x in e) e = Interval(0, 1, True, True) assert e.contains(x) == And(0 < x, x < 1) def test_union_contains(): x = Symbol('x') i1 = Interval(0, 1) i2 = Interval(2, 3) i3 = Union(i1, i2) raises(TypeError, lambda: x in i3) e = i3.contains(x) assert e == Or(And(0 <= x, x <= 1), And(2 <= x, x <= 3)) assert e.subs(x, -0.5) is false assert e.subs(x, 0.5) is true assert e.subs(x, 1.5) is false assert e.subs(x, 2.5) is true assert e.subs(x, 3.5) is false U = Interval(0, 2, True, True) + Interval(10, oo) + FiniteSet(-1, 2, 5, 6) assert all(el not in U for el in [0, 4, -oo]) assert all(el in U for el in [2, 5, 10]) def test_is_number(): assert Interval(0, 1).is_number is False assert Set().is_number is False def test_Interval_is_left_unbounded(): assert Interval(3, 4).is_left_unbounded is False assert Interval(-oo, 3).is_left_unbounded is True assert Interval(Float("-inf"), 3).is_left_unbounded is True def test_Interval_is_right_unbounded(): assert Interval(3, 4).is_right_unbounded is False assert Interval(3, oo).is_right_unbounded is True assert Interval(3, Float("+inf")).is_right_unbounded is True def test_Interval_as_relational(): x = Symbol('x') assert Interval(-1, 2, False, False).as_relational(x) == \ And(Le(-1, x), Le(x, 2)) assert Interval(-1, 2, True, False).as_relational(x) == \ And(Lt(-1, x), Le(x, 2)) assert Interval(-1, 2, False, True).as_relational(x) == \ And(Le(-1, x), Lt(x, 2)) assert Interval(-1, 2, True, True).as_relational(x) == \ And(Lt(-1, x), Lt(x, 2)) assert Interval(-oo, 2, right_open=False).as_relational(x) == Le(x, 2) assert Interval(-oo, 2, right_open=True).as_relational(x) == Lt(x, 2) assert Interval(-2, oo, left_open=False).as_relational(x) == Ge(x, -2) assert Interval(-2, oo, left_open=True).as_relational(x) == Gt(x, -2) assert Interval(-oo, oo).as_relational(x) is True def test_Finite_as_relational(): x = Symbol('x') y = Symbol('y') assert FiniteSet(1, 2).as_relational(x) == Or(Eq(x, 1), Eq(x, 2)) assert FiniteSet(y, -5).as_relational(x) == Or(Eq(x, y), Eq(x, -5)) def test_Union_as_relational(): x = Symbol('x') assert (Interval(0, 1) + FiniteSet(2)).as_relational(x) == \ Or(And(Le(0, x), Le(x, 1)), Eq(x, 2)) assert (Interval(0, 1, True, True) + FiniteSet(1)).as_relational(x) == \ And(Lt(0, x), Le(x, 1)) def test_Intersection_as_relational(): x = Symbol('x') assert (Intersection(Interval(0, 1), FiniteSet(2), evaluate=False).as_relational(x) == And(And(Le(0, x), Le(x, 1)), Eq(x, 2))) def test_EmptySet_as_relational(): assert S.EmptySet.as_relational(Symbol('x')) is False def test_finite_basic(): x = Symbol('x') A = FiniteSet(1, 2, 3) B = FiniteSet(3, 4, 5) AorB = Union(A, B) AandB = A.intersect(B) assert AorB.subset(A) and AorB.subset(B) assert A.subset(AandB) assert AandB == FiniteSet(3) assert A.inf == 1 and A.sup == 3 assert AorB.inf == 1 and AorB.sup == 5 assert FiniteSet(x, 1, 5).sup == Max(x, 5) assert FiniteSet(x, 1, 5).inf == Min(x, 1) # Ensure a variety of types can exist in a FiniteSet S = FiniteSet((1, 2), Float, A, -5, x, 'eggs', x**2, Interval) def test_product_basic(): H, T = 'H', 'T' unit_line = Interval(0, 1) d6 = FiniteSet(1, 2, 3, 4, 5, 6) d4 = FiniteSet(1, 2, 3, 4) coin = FiniteSet(H, T) square = unit_line * unit_line assert (0, 0) in square assert 0 not in square assert (H, T) in coin ** 2 assert (.5, .5, .5) in square * unit_line assert (H, 3, 3) in coin * d6* d6 HH, TT = sympify(H), sympify(T) assert set(coin**2) == set(((HH, HH), (HH, TT), (TT, HH), (TT, TT))) assert (d6*d6).subset(d4*d4) inf, neginf = S.Infinity, S.NegativeInfinity assert square.complement == Union( Interval(0, 1) * (Interval(neginf, 0, True, True) + Interval(1, inf, True, True)), (Interval(neginf, 0, True, True) + Interval(1, inf, True, True)) * Interval(0, 1), ((Interval(neginf, 0, True, True) + Interval(1, inf, True, True)) * (Interval(neginf, 0, True, True) + Interval(1, inf, True, True)))) assert (Interval(-10, 10)**3).subset(Interval(-5, 5)**3) assert not (Interval(-5, 5)**3).subset(Interval(-10, 10)**3) assert not (Interval(-10, 10)**2).subset(Interval(-5, 5)**3) assert square.subset(Interval(.2, .5)*FiniteSet(.5)) # segment in square def test_real(): x = Symbol('x', real=True) I = Interval(0, 5) J = Interval(10, 20) A = FiniteSet(1, 2, 30, x, S.Pi, S.Infinity) B = FiniteSet(-4, 0) C = FiniteSet(100, S.NegativeInfinity) D = FiniteSet('Ham', 'Eggs') assert all(s.is_real for s in [I, J, A, B, C]) assert not D.is_real assert all((a + b).is_real for a in [I, J, A, B, C] for b in [I, J, A, B, C]) assert not any((a + D).is_real for a in [I, J, A, B, C, D]) assert not (I + A + D).is_real def test_supinf(): x = Symbol('x', real=True) y = Symbol('y', real=True) assert (Interval(0, 1) + FiniteSet(2)).sup == 2 assert (Interval(0, 1) + FiniteSet(2)).inf == 0 assert (Interval(0, 1) + FiniteSet(x)).sup == Max(1, x) assert (Interval(0, 1) + FiniteSet(x)).inf == Min(0, x) assert FiniteSet(5, 1, x).sup == Max(5, x) assert FiniteSet(5, 1, x).inf == Min(1, x) assert FiniteSet(5, 1, x, y).sup == Max(5, x, y) assert FiniteSet(5, 1, x, y).inf == Min(1, x, y) assert FiniteSet(5, 1, x, y, S.Infinity, S.NegativeInfinity).sup == \ S.Infinity assert FiniteSet(5, 1, x, y, S.Infinity, S.NegativeInfinity).inf == \ S.NegativeInfinity assert FiniteSet('Ham', 'Eggs').sup == Max('Ham', 'Eggs') def test_universalset(): U = S.UniversalSet x = Symbol('x') assert U.as_relational(x) is True assert U.union(Interval(2, 4)) == U def test_Interval_free_symbols(): x = Symbol('x', real=True) assert set(Interval(0, x).free_symbols) == set((x,)) def test_image_interval(): x = Symbol('x', real=True) assert imageset(x, 2*x, Interval(-2, 1)) == Interval(-4, 2) assert imageset(x, 2*x, Interval(-2, 1, True, False)) == \ Interval(-4, 2, True, False) assert imageset(x, x**2, Interval(-2, 1, True, False)) == \ Interval(0, 4, False, True) assert imageset(x, x**2, Interval(-2, 1)) == Interval(0, 4) assert imageset(x, x**2, Interval(-2, 1, True, False)) == \ Interval(0, 4, False, True) assert imageset(x, x**2, Interval(-2, 1, True, True)) == \ Interval(0, 4, False, True) def test_image_FiniteSet(): x = Symbol('x', real=True) assert imageset(x, 2*x, FiniteSet(1, 2, 3)) == FiniteSet(2, 4, 6) def test_image_Union(): x = Symbol('x', real=True) assert imageset(x, x**2, Interval(-2, 0) + FiniteSet(1, 2, 3)) == \ (Interval(0, 4) + FiniteSet(9)) def test_image_Intersection(): x = Symbol('x', real=True) y = Symbol('y', real=True) assert imageset(x, x**2, Interval(-2, 0).intersect(Interval(x, y))) == \ Interval(0, 4).intersect(Interval(Min(x**2, y**2), Max(x**2, y**2))) def test_image_EmptySet(): x = Symbol('x', real=True) assert imageset(x, 2*x, S.EmptySet) == S.EmptySet def test_issue_2625(): raises(TypeError, lambda: I in Interval(-oo,oo)) raises(TypeError, lambda: Interval(-oo,oo).contains(I)) raises(TypeError, lambda: I > 2) sympy-0.7.4.1/sympy/core/tests/test_diff.py0000644000175000017500000000535112253362407021044 0ustar georgeskgeorgeskfrom sympy import Symbol, Rational, cos, sin, tan, cot, exp, log, Function, \ Derivative, Expr, symbols, pi, I, S from sympy.utilities.pytest import raises def test_diff(): x, y = symbols('x, y') assert Rational(1, 3).diff(x) is S.Zero assert I.diff(x) is S.Zero assert pi.diff(x) is S.Zero assert x.diff(x, 0) == x assert (x**2).diff(x, 2, x) == 0 assert (x**2).diff(x, y, 0) == 2*x assert (x**2).diff(x, y) == 0 raises(ValueError, lambda: x.diff(1, x)) a = Symbol("a") b = Symbol("b") c = Symbol("c") p = Rational(5) e = a*b + b**p assert e.diff(a) == b assert e.diff(b) == a + 5*b**4 assert e.diff(b).diff(a) == Rational(1) e = a*(b + c) assert e.diff(a) == b + c assert e.diff(b) == a assert e.diff(b).diff(a) == Rational(1) e = c**p assert e.diff(c, 6) == Rational(0) assert e.diff(c, 5) == Rational(120) e = c**Rational(2) assert e.diff(c) == 2*c e = a*b*c assert e.diff(c) == a*b def test_diff2(): n3 = Rational(3) n2 = Rational(2) n6 = Rational(6) x, c = map(Symbol, 'xc') e = n3*(-n2 + x**n2)*cos(x) + x*(-n6 + x**n2)*sin(x) assert e == 3*(-2 + x**2)*cos(x) + x*(-6 + x**2)*sin(x) assert e.diff(x).expand() == x**3*cos(x) e = (x + 1)**3 assert e.diff(x) == 3*(x + 1)**2 e = x*(x + 1)**3 assert e.diff(x) == (x + 1)**3 + 3*x*(x + 1)**2 e = 2*exp(x*x)*x assert e.diff(x) == 2*exp(x**2) + 4*x**2*exp(x**2) def test_diff3(): a, b, c = map(Symbol, 'abc') p = Rational(5) e = a*b + sin(b**p) assert e == a*b + sin(b**5) assert e.diff(a) == b assert e.diff(b) == a + 5*b**4*cos(b**5) e = tan(c) assert e == tan(c) assert e.diff(c) in [cos(c)**(-2), 1 + sin(c)**2/cos(c)**2, 1 + tan(c)**2] e = c*log(c) - c assert e == -c + c*log(c) assert e.diff(c) == log(c) e = log(sin(c)) assert e == log(sin(c)) assert e.diff(c) in [sin(c)**(-1)*cos(c), cot(c)] e = (Rational(2)**a/log(Rational(2))) assert e == 2**a*log(Rational(2))**(-1) assert e.diff(a) == 2**a def test_diff_no_eval_derivative(): class My(Expr): def __new__(cls, x): return Expr.__new__(cls, x) x, y = symbols('x y') # My doesn't have its own _eval_derivative method assert My(x).diff(x).func is Derivative # it doesn't have y so it shouldn't need a method for this case assert My(x).diff(y) == 0 def test_speed(): # this should return in 0.0s. If it takes forever, it's wrong. x = Symbol("x") assert x.diff(x, 10**8) == 0 def test_deriv_noncommutative(): A = Symbol("A", commutative=False) f = Function("f") x = Symbol("x") assert A*f(x)*A == f(x)*A**2 assert A*f(x).diff(x)*A == f(x).diff(x) * A**2 sympy-0.7.4.1/sympy/core/tests/test_wester.py0000644000175000017500000025705612253362407021460 0ustar georgeskgeorgesk""" Tests from Michael Wester's 1999 paper "Review of CAS mathematical capabilities". http://www.math.unm.edu/~wester/cas/book/Wester.pdf See also http://math.unm.edu/~wester/cas_review.html for detailed output of each tested system. """ from sympy import (Rational, symbols, factorial, sqrt, log, exp, oo, zoo, product, binomial, rf, pi, gamma, igcd, factorint, radsimp, combsimp, npartitions, totient, primerange, factor, simplify, gcd, resultant, expand, I, trigsimp, tan, sin, cos, cot, diff, nan, limit, EulerGamma, polygamma, bernoulli, hyper, hyperexpand, besselj, asin, assoc_legendre, Function, re, im, DiracDelta, chebyshevt, legendre_poly, polylog, series, O, atan, sinh, cosh, tanh, floor, ceiling, solve, asinh, acot, csc, sec, LambertW, N, apart, sqrtdenest, factorial2, powdenest, Mul, S, mpmath, ZZ, Poly, expand_func, E, Q, And, Or, Ne, Eq, Le, Lt, ask, refine, AlgebraicNumber, elliptic_e, elliptic_f, powsimp, hessian, wronskian, fibonacci, sign, Lambda, Piecewise, Subs, residue, Derivative, logcombine) from sympy.functions.combinatorial.numbers import stirling from sympy.functions.special.zeta_functions import zeta from sympy.integrals.deltafunctions import deltaintegrate from sympy.utilities.pytest import XFAIL, slow from sympy.utilities.iterables import partitions from sympy.mpmath import mpi, mpc from sympy.matrices import Matrix, GramSchmidt, eye from sympy.matrices.expressions.blockmatrix import BlockMatrix, block_collapse from sympy.matrices.expressions import MatrixSymbol, ZeroMatrix from sympy.galgebra.ga import MV from sympy.physics.quantum import Commutator from sympy.assumptions import assuming from sympy.polys.rings import vring from sympy.polys.fields import vfield from sympy.polys.solvers import solve_lin_sys from sympy.concrete import Sum from sympy.concrete.products import Product from sympy.integrals import integrate from sympy.integrals.transforms import laplace_transform,\ inverse_laplace_transform, LaplaceTransform, fourier_transform,\ mellin_transform from sympy.functions.special.error_functions import erf from sympy.functions.special.delta_functions import Heaviside from sympy.solvers.recurr import rsolve from sympy.solvers.ode import dsolve from sympy.core.relational import Equality R = Rational x, y, z = symbols('x y z') i, j, k, l, m, n = symbols('i j k l m n', integer=True) f = Function('f') g = Function('g') # A. Boolean Logic and Quantifier Elimination # Not implemented. # B. Set Theory # Not implemented. # C. Numbers def test_C1(): assert (factorial(50) == 30414093201713378043612608166064768844377641568960512000000000000) def test_C2(): assert (factorint(factorial(50)) == {2: 47, 3: 22, 5: 12, 7: 8, 11: 4, 13: 3, 17: 2, 19: 2, 23: 2, 29: 1, 31: 1, 37: 1, 41: 1, 43: 1, 47: 1}) def test_C3(): assert (factorial2(10), factorial2(9)) == (3840, 945) # Base conversions; not really implemented by sympy # Whatever. Take credit! def test_C4(): assert 0xABC == 2748 def test_C5(): assert 123 == int('234', 7) def test_C6(): assert int('677', 8) == int('1BF', 16) == 447 def test_C7(): assert log(32768, 8) == 5 def test_C8(): # Modular multiplicative inverse. Would be nice if divmod could do this. assert ZZ.invert(5, 7) == 3 assert ZZ.invert(5, 6) == 5 def test_C9(): assert igcd(igcd(1776, 1554), 5698) == 74 def test_C10(): x = 0 for n in range(2, 11): x += R(1, n) assert x == R(4861, 2520) def test_C11(): assert R(1, 7) == S('0.[142857]') def test_C12(): assert R(7, 11) * R(22, 7) == 2 def test_C13(): test = R(10, 7) * (1 + R(29, 1000)) ** R(1, 3) good = 3 ** R(1, 3) assert test == good def test_C14(): assert sqrtdenest(sqrt(2*sqrt(3) + 4)) == 1 + sqrt(3) def test_C15(): test = sqrtdenest(sqrt(14 + 3*sqrt(3 + 2*sqrt(5 - 12*sqrt(3 - 2*sqrt(2)))))) good = sqrt(2) + 3 assert test == good def test_C16(): test = sqrtdenest(sqrt(10 + 2*sqrt(6) + 2*sqrt(10) + 2*sqrt(15))) good = sqrt(2) + sqrt(3) + sqrt(5) assert test == good def test_C17(): test = radsimp((sqrt(3) + sqrt(2)) / (sqrt(3) - sqrt(2))) good = 5 + 2*sqrt(6) assert test == good def test_C18(): assert simplify((sqrt(-2 + sqrt(-5)) * sqrt(-2 - sqrt(-5))).expand(complex=True)) == 3 @XFAIL def test_C19(): assert radsimp(simplify((90 + 35*sqrt(7)) ** R(1, 3))) == 3 + sqrt(7) @XFAIL def test_C20(): inside = (135 + 78*sqrt(3)) test = simplify((inside**R(2, 3) + 3) * sqrt(3) / inside**R(1, 3)) assert test == 12 @XFAIL def test_C21(): assert simplify((41 + 29*sqrt(2)) ** R(1, 5)) == 1 + sqrt(2) @XFAIL def test_C22(): test = simplify(((6 - 4*sqrt(2))*log(3 - 2*sqrt(2)) + (3 - 2*sqrt(2))*log(17 - 12*sqrt(2)) + 32 - 24*sqrt(2)) / (48*sqrt(2) - 72)) good = sqrt(2)/3 - log(sqrt(2) - 1)/3 assert test == good def test_C23(): assert 2 * oo - 3 == oo @XFAIL def test_C24(): raise NotImplementedError("2**aleph_null == aleph_1") # D. Numerical Analysis def test_D1(): assert 0.0 / sqrt(2) == 0.0 def test_D2(): assert str(exp(-1000000).evalf()) == '3.29683147808856e-434295' def test_D3(): assert exp(pi*sqrt(163)).evalf(50).num.ae(262537412640768744) def test_D4(): assert floor(R(-5, 3)) == -2 assert ceiling(R(-5, 3)) == -1 @XFAIL def test_D5(): raise NotImplementedError("cubic_spline([1, 2, 4, 5], [1, 4, 2, 3], x)(3) == 27/8") @XFAIL def test_D6(): raise NotImplementedError("translate sum(a[i]*x**i, (i,1,n)) to FORTRAN") @XFAIL def test_D7(): raise NotImplementedError("translate sum(a[i]*x**i, (i,1,n)) to C") @XFAIL def test_D8(): # One way is to cheat by converting the sum to a string, # and replacing the '[' and ']' with ''. # E.g., horner(S(str(_).replace('[','').replace(']',''))) raise NotImplementedError("apply Horner's rule to sum(a[i]*x**i, (i,1,5))") @XFAIL def test_D9(): raise NotImplementedError("translate D8 to FORTRAN") @XFAIL def test_D10(): raise NotImplementedError("translate D8 to C") @XFAIL def test_D11(): #Is there a way to use count_ops? raise NotImplementedError("flops(sum(product(f[i][k], (i,1,k)), (k,1,n)))") @XFAIL def test_D12(): assert (mpi(-4, 2) * x + mpi(1, 3)) ** 2 == mpi(-8, 16)*x**2 + mpi(-24, 12)*x + mpi(1, 9) @XFAIL def test_D13(): raise NotImplementedError("discretize a PDE: diff(f(x,t),t) == diff(diff(f(x,t),x),x)") # E. Statistics # See scipy; all of this is numerical. # F. Combinatorial Theory. def test_F1(): assert rf(x, 3) == x*(1 + x)*(2 + x) def test_F2(): assert expand_func(binomial(n, 3)) == n*(n - 1)*(n - 2)/6 @XFAIL def test_F3(): assert combsimp(2**n * factorial(n) * factorial2(2*n - 1)) == factorial(2*n) @XFAIL def test_F4(): assert combsimp((2**n * factorial(n) * product(2*k - 1, (k, 1, n)))) == factorial(2*n) @XFAIL def test_F5(): assert gamma(n + R(1, 2)) / sqrt(pi) / factorial(n) == factorial(2*n)/2**(2*n)/factorial(n)**2 def test_F6(): partTest = [p.copy() for p in partitions(4)] partDesired = [{4: 1}, {1: 1, 3: 1}, {2: 2}, {1: 2, 2:1}, {1: 4}] assert partTest == partDesired def test_F7(): assert npartitions(4) == 5 def test_F8(): assert stirling(5, 2, signed=True) == -50 # if signed, then kind=1 def test_F9(): assert totient(1776) == 576 # G. Number Theory def test_G1(): assert list(primerange(999983, 1000004)) == [999983, 1000003] @XFAIL def test_G2(): raise NotImplementedError("find the primitive root of 191 == 19") @XFAIL def test_G3(): raise NotImplementedError("(a+b)**p mod p == a**p + b**p mod p; p prime") # ... G20 Modular equations and continued fractions are not implemented. # H. Algebra def test_H1(): assert simplify(2*2**n) == simplify(2**(n + 1)) assert powdenest(2*2**n) == simplify(2**(n + 1)) def test_H2(): assert powsimp(4 * 2**n) == 2**(n + 2) def test_H3(): assert (-1)**(n*(n + 1)) == 1 def test_H4(): expr = factor(6*x - 10) assert type(expr) is Mul assert expr.args[0] == 2 assert expr.args[1] == 3*x - 5 p1 = 64*x**34 - 21*x**47 - 126*x**8 - 46*x**5 - 16*x**60 - 81 p2 = 72*x**60 - 25*x**25 - 19*x**23 - 22*x**39 - 83*x**52 + 54*x**10 + 81 q = 34*x**19 - 25*x**16 + 70*x**7 + 20*x**3 - 91*x - 86 def test_H5(): assert gcd(p1, p2, x) == 1 def test_H6(): assert gcd(expand(p1 * q), expand(p2 * q)) == q def test_H7(): p1 = 24*x*y**19*z**8 - 47*x**17*y**5*z**8 + 6*x**15*y**9*z**2 - 3*x**22 + 5 p2 = 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 assert gcd(p1, p2, x, y, z) == 1 def test_H8(): p1 = 24*x*y**19*z**8 - 47*x**17*y**5*z**8 + 6*x**15*y**9*z**2 - 3*x**22 + 5 p2 = 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 q = 11*x**12*y**7*z**13 - 23*x**2*y**8*z**10 + 47*x**17*y**5*z**8 assert gcd(p1 * q, p2 * q, x, y, z) == q def test_H9(): p1 = 2*x**(n + 4) - x**(n + 2) p2 = 4*x**(n + 1) + 3*x**n assert gcd(p1, p2) == x**n def test_H10(): p1 = 3*x**4 + 3*x**3 + x**2 - x - 2 p2 = x**3 - 3*x**2 + x + 5 assert resultant(p1, p2, x) == 0 def test_H11(): assert resultant(p1 * q, p2 * q, x) == 0 def test_H12(): num = x**2 - 4 den = x**2 + 4*x + 4 assert simplify(num/den) == (x - 2)/(x + 2) @XFAIL def test_H13(): assert simplify((exp(x) - 1) / (exp(x/2) + 1)) == exp(x/2) - 1 def test_H14(): p = (x + 1) ** 20 ep = expand(p) assert ep == (1 + 20*x + 190*x**2 + 1140*x**3 + 4845*x**4 + 15504*x**5 + 38760*x**6 + 77520*x**7 + 125970*x**8 + 167960*x**9 + 184756*x**10 + 167960*x**11 + 125970*x**12 + 77520*x**13 + 38760*x**14 + 15504*x**15 + 4845*x**16 + 1140*x**17 + 190*x**18 + 20*x**19 + x**20) dep = diff(ep, x) assert dep == (20 + 380*x + 3420*x**2 + 19380*x**3 + 77520*x**4 + 232560*x**5 + 542640*x**6 + 1007760*x**7 + 1511640*x**8 + 1847560*x**9 + 1847560*x**10 + 1511640*x**11 + 1007760*x**12 + 542640*x**13 + 232560*x**14 + 77520*x**15 + 19380*x**16 + 3420*x**17 + 380*x**18 + 20*x**19) assert factor(dep) == 20*(1 + x)**19 def test_H15(): assert simplify((Mul(*[x - r for r in solve(x**3 + x**2 - 7)]))) == x**3 + x**2 - 7 def test_H16(): assert factor(x**100 - 1) == ((x - 1)*(x + 1)*(x**2 + 1)*(x**4 - x**3 + x**2 - x + 1)*(x**4 + x**3 + x**2 + x + 1)*(x**8 - x**6 + x**4 - x**2 + 1)*(x**20 - x**15 + x**10 - x**5 + 1)*(x**20 + x**15 + x**10 + x**5 + 1)*(x**40 - x**30 + x**20 - x**10 + 1)) @slow def test_H17(): assert simplify(factor(expand(p1 * p2)) - p1*p2) == 0 @XFAIL def test_H18(): # Factor over complex rationals. test = factor(4*x**4 + 8*x**3 + 77*x**2 + 18*x + 53) good = (2*x + 3*I)*(2*x - 3*I)*(x + 1 - 4*I)(x + 1 + 4*I) assert test == good def test_H19(): a = symbols('a') # The idea is to let a**2 == 2, then solve 1/(a-1). Answer is a+1") assert Poly(a - 1).invert(Poly(a**2 - 2)) == a + 1 @XFAIL def test_H20(): raise NotImplementedError("let a**2==2; (x**3 + (a-2)*x**2 - " + "(2*a+3)*x - 3*a) / (x**2-2) = (x**2 - 2*x - 3) / (x-a)") @XFAIL def test_H21(): raise NotImplementedError("evaluate (b+c)**4 assuming b**3==2, c**2==3. \ Answer is 2*b + 8*c + 18*b**2 + 12*b*c + 9") def test_H22(): assert factor(x**4 - 3*x**2 + 1, modulus=5) == (x - 2)**2 * (x + 2)**2 def test_H23(): f = x**11 + x + 1 g = (x**2 + x + 1) * (x**9 - x**8 + x**6 - x**5 + x**3 - x**2 + 1) assert factor(f, modulus=65537) == g def test_H24(): phi = AlgebraicNumber(S.GoldenRatio.expand(func=True), alias='phi') assert factor(x**4 - 3*x**2 + 1, extension=phi) == \ (x - phi)*(x + 1 - phi)*(x - 1 + phi)*(x + phi) @slow def test_H25(): e = (x - 2*y**2 + 3*z**3) ** 20 assert factor(expand(e)) == e @slow def test_H26(): g = expand((sin(x) - 2*cos(y)**2 + 3*tan(z)**3)**20) assert factor(g, expand=False) == (-sin(x) + 2*cos(y)**2 - 3*tan(z)**3)**20 @slow def test_H27(): 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 = -2*z*y**7 \ *(6*x**9*y**9*z**3 + 10*x**7*z**6 + 17*y*x**5*z**12 + 40*y**7) \ *(3*x**22 + 47*x**17*y**5*z**8 - 6*x**15*y**9*z**2 - 24*x*y**19*z**8 - 5) assert factor(expand(f*g)) == h @XFAIL def test_H28(): raise NotImplementedError("expand ((1 - c**2)**5 * (1 - s**2)**5 * " + "(c**2 + s**2)**10) with c**2 + s**2 = 1. Answer is c**10*s**10.") @XFAIL def test_H29(): assert factor(4*x**2 - 21*x*y + 20*y**2, modulus=3) == (x + y)*(x - y) def test_H30(): test = factor(x**3 + y**3, extension=sqrt(-3)) answer = (x + y)*(x + y*(-R(1, 2) - sqrt(3)/2*I))*(x + y*(-R(1, 2) + sqrt(3)/2*I)) assert answer == test def test_H31(): f = (x**2 + 2*x + 3)/(x**3 + 4*x**2 + 5*x + 2) g = 2 / (x + 1)**2 - 2 / (x + 1) + 3 / (x + 2) assert apart(f) == g @XFAIL def test_H32(): # issue 3459 raise NotImplementedError("[A*B*C - (A*B*C)**(-1)]*A*C*B (product \ of a non-commuting product and its inverse)") def test_H33(): A, B, C = symbols('A, B, C', commutatative=False) assert (Commutator(A, Commutator(B, C)) + Commutator(B, Commutator(C, A)) + Commutator(C, Commutator(A, B))).doit().expand() == 0 # I. Trigonometry @XFAIL def test_I1(): assert tan(7*pi/10) == -sqrt(1 + 2/sqrt(5)) @XFAIL def test_I2(): assert sqrt((1 + cos(6))/2) == -cos(3) def test_I3(): assert cos(n*pi) + sin((4*n - 1)*pi/2) == (-1)**n - 1 def test_I4(): assert refine(cos(pi*cos(n*pi)) + sin(pi/2*cos(n*pi)), Q.integer(n)) == (-1)**n - 1 @XFAIL def test_I5(): assert sin((n**5/5 + n**4/2 + n**3/3 - n/30) * pi) == 0 @XFAIL def test_I6(): raise NotImplementedError("assuming -3*pi 0)): solve(log(acos(asin(x**R(2,3) - b) - 1)) + 2, x) == [-b - sin(1 + cos(1/e**2))**R(3/2), b + sin(1 + cos(1/e**2))**R(3/2)] @XFAIL def test_M28(): assert solve(5*x + exp((x - 5)/2) - 8*x**3, x, assume=Q.real(x)) == [-0.784966, -0.016291, 0.802557] def test_M29(): assert solve(abs(x - 1) - 2) == [-1, 3] @XFAIL def test_M30(): assert solve(abs(2*x + 5) - abs(x - 2),x, assume=Q.real(x)) == [-1, -7] @XFAIL def test_M31(): assert solve(1 - abs(x) - max(-x - 2, x - 2),x, assume=Q.real(x)) == [-3/2, 3/2] @XFAIL def test_M32(): assert solve(max(2 - x**2, x)- max(-x, (x**3)/9), assume=Q.real(x)) == [-1, 3] @XFAIL def test_M33(): # Second answer can be written in another form. The second answer is the root of x**3 + 9*x**2 - 18 = 0 in the interval (-2, -1). assert solve(max(2 - x**2, x) - x**3/9, assume=Q.real(x)) == [-3, -1.554894, 3] @XFAIL def test_M34(): z = symbols('z', complex=True) assert solve((1 + I) * z + (2 - I) * conjugate(z) + 3*I, z) == [2 + 3*I] def test_M35(): x, y = symbols('x y', real=True) assert solve((3*x - 2*y - I*y + 3*I).as_real_imag()) == {y: 3, x: 2} @XFAIL def test_M36(): assert solve(f**2 + f - 2, x) == [Eq(f(x), 1), Eq(f(x), -2)] def test_M37(): assert solve([x + y + z - 6, 2*x + y + 2*z - 10, x + 3*y + z - 10 ]) == {x: -z + 4, y: 2} @slow def test_M38(): variabes = vring("k1:50", vfield("a,b,c", ZZ).to_domain()) system = [ -b*k8/a + c*k8/a, -b*k11/a + c*k11/a, -b*k10/a + c*k10/a + k2, -k3 - b*k9/a + c*k9/a, -b*k14/a + c*k14/a, -b*k15/a + c*k15/a, -b*k18/a + c*k18/a - k2, -b*k17/a + c*k17/a, -b*k16/a + c*k16/a + k4, -b*k13/a + c*k13/a - b*k21/a + c*k21/a + b*k5/a - c*k5/a, b*k44/a - c*k44/a, -b*k45/a + c*k45/a, -b*k20/a + c*k20/a, -b*k44/a + c*k44/a, b*k46/a - c*k46/a, b**2*k47/a**2 - 2*b*c*k47/a**2 + c**2*k47/a**2, k3, -k4, -b*k12/a + c*k12/a - a*k6/b + c*k6/b, -b*k19/a + c*k19/a + a*k7/c - b*k7/c, b*k45/a - c*k45/a, -b*k46/a + c*k46/a, -k48 + c*k48/a + c*k48/b - c**2*k48/(a*b), -k49 + b*k49/a + b*k49/c - b**2*k49/(a*c), a*k1/b - c*k1/b, a*k4/b - c*k4/b, a*k3/b - c*k3/b + k9, -k10 + a*k2/b - c*k2/b, a*k7/b - c*k7/b, -k9, k11, b*k12/a - c*k12/a + a*k6/b - c*k6/b, a*k15/b - c*k15/b, k10 + a*k18/b - c*k18/b, -k11 + a*k17/b - c*k17/b, a*k16/b - c*k16/b, -a*k13/b + c*k13/b + a*k21/b - c*k21/b + a*k5/b - c*k5/b, -a*k44/b + c*k44/b, a*k45/b - c*k45/b, a*k14/c - b*k14/c + a*k20/b - c*k20/b, a*k44/b - c*k44/b, -a*k46/b + c*k46/b, -k47 + c*k47/a + c*k47/b - c**2*k47/(a*b), a*k19/b - c*k19/b, -a*k45/b + c*k45/b, a*k46/b - c*k46/b, a**2*k48/b**2 - 2*a*c*k48/b**2 + c**2*k48/b**2, -k49 + a*k49/b + a*k49/c - a**2*k49/(b*c), k16, -k17, -a*k1/c + b*k1/c, -k16 - a*k4/c + b*k4/c, -a*k3/c + b*k3/c, k18 - a*k2/c + b*k2/c, b*k19/a - c*k19/a - a*k7/c + b*k7/c, -a*k6/c + b*k6/c, -a*k8/c + b*k8/c, -a*k11/c + b*k11/c + k17, -a*k10/c + b*k10/c - k18, -a*k9/c + b*k9/c, -a*k14/c + b*k14/c - a*k20/b + c*k20/b, -a*k13/c + b*k13/c + a*k21/c - b*k21/c - a*k5/c + b*k5/c, a*k44/c - b*k44/c, -a*k45/c + b*k45/c, -a*k44/c + b*k44/c, a*k46/c - b*k46/c, -k47 + b*k47/a + b*k47/c - b**2*k47/(a*c), -a*k12/c + b*k12/c, a*k45/c - b*k45/c, -a*k46/c + b*k46/c, -k48 + a*k48/b + a*k48/c - a**2*k48/(b*c), a**2*k49/c**2 - 2*a*b*k49/c**2 + b**2*k49/c**2, k8, k11, -k15, k10 - k18, -k17, k9, -k16, -k29, k14 - k32, -k21 + k23 - k31, -k24 - k30, -k35, k44, -k45, k36, k13 - k23 + k39, -k20 + k38, k25 + k37, b*k26/a - c*k26/a - k34 + k42, -2*k44, k45, k46, b*k47/a - c*k47/a, k41, k44, -k46, -b*k47/a + c*k47/a, k12 + k24, -k19 - k25, -a*k27/b + c*k27/b - k33, k45, -k46, -a*k48/b + c*k48/b, a*k28/c - b*k28/c + k40, -k45, k46, a*k48/b - c*k48/b, a*k49/c - b*k49/c, -a*k49/c + b*k49/c, -k1, -k4, -k3, k15, k18 - k2, k17, k16, k22, k25 - k7, k24 + k30, k21 + k23 - k31, k28, -k44, k45, -k30 - k6, k20 + k32, k27 + b*k33/a - c*k33/a, k44, -k46, -b*k47/a + c*k47/a, -k36, k31 - k39 - k5, -k32 - k38, k19 - k37, k26 - a*k34/b + c*k34/b - k42, k44, -2*k45, k46, a*k48/b - c*k48/b, a*k35/c - b*k35/c - k41, -k44, k46, b*k47/a - c*k47/a, -a*k49/c + b*k49/c, -k40, k45, -k46, -a*k48/b + c*k48/b, a*k49/c - b*k49/c, k1, k4, k3, -k8, -k11, -k10 + k2, -k9, k37 + k7, -k14 - k38, -k22, -k25 - k37, -k24 + k6, -k13 - k23 + k39, -k28 + b*k40/a - c*k40/a, k44, -k45, -k27, -k44, k46, b*k47/a - c*k47/a, k29, k32 + k38, k31 - k39 + k5, -k12 + k30, k35 - a*k41/b + c*k41/b, -k44, k45, -k26 + k34 + a*k42/c - b*k42/c, k44, k45, -2*k46, -b*k47/a + c*k47/a, -a*k48/b + c*k48/b, a*k49/c - b*k49/c, k33, -k45, k46, a*k48/b - c*k48/b, -a*k49/c + b*k49/c ] solution = { k49: 0, k48: 0, k47: 0, k46: 0, k45: 0, k44: 0, k41: 0, k40: 0, k38: 0, k37: 0, k36: 0, k35: 0, k33: 0, k32: 0, k30: 0, k29: 0, k28: 0, k27: 0, k25: 0, k24: 0, k22: 0, k21: 0, k20: 0, k19: 0, k18: 0, k17: 0, k16: 0, k15: 0, k14: 0, k13: 0, k12: 0, k11: 0, k10: 0, k9: 0, k8: 0, k7: 0, k6: 0, k5: 0, k4: 0, k3: 0, k2: 0, k1: 0, k34: b/c*k42, k31: k39, k26: a/c*k42, k23: k39 } assert solve_lin_sys(system, variabes) == solution def test_M39(): x, y, z = symbols('x y z', complex=True) assert solve([x**2*y + 3*y*z - 4, -3*x**2*z + 2*y**2 + 1, 2*y*z**2 - z**2 - 1 ]) ==\ [{y: 1, z: 1, x: -1}, {y: 1, z: 1, x: 1},\ {y: sqrt(2)*I, z: R(1,3) - sqrt(2)*I/3, x: -sqrt(-1 - sqrt(2)*I)},\ {y: sqrt(2)*I, z: R(1,3) - sqrt(2)*I/3, x: sqrt(-1 - sqrt(2)*I)},\ {y: -sqrt(2)*I, z: R(1,3) + sqrt(2)*I/3, x: -sqrt(-1 + sqrt(2)*I)},\ {y: -sqrt(2)*I, z: R(1,3) + sqrt(2)*I/3, x: sqrt(-1 + sqrt(2)*I)}] # N. Inequalities def test_N1(): assert ask(Q.is_true(E**pi > pi**E)) @XFAIL def test_N2(): x = symbols('x', real=True) assert ask(Q.is_true(x**4 - x + 1 > 0)) assert ask(Q.is_true(x**4 - x + 1 > 1)) == False @XFAIL def test_N3(): x = symbols('x', real=True) assert ask(Q.is_true(And(Lt(-1, x), Lt(x, 1))), Q.is_true(abs(x) < 1 )) @XFAIL def test_N4(): x, y = symbols('x y', real=True) assert ask(Q.is_true(2*x**2 > 2*y**2), Q.is_true((x > y) & (y > 0))) @XFAIL def test_N5(): x, y, k = symbols('x y k', real=True) assert ask(Q.is_true(k*x**2 > k*y**2), Q.is_true((x > y) & (y > 0) & (k > 0))) @XFAIL def test_N6(): x, y, k, n = symbols('x y k n', real=True) assert ask(Q.is_true(k*x**n > k*y**n), Q.is_true((x > y) & (y > 0) & (k > 0) & (n > 0))) @XFAIL def test_N7(): x, y = symbols('x y', real=True) assert ask(Q.is_true(y > 0), Q.is_true((x > 1) & (y >= x - 1))) @XFAIL def test_N8(): x, y, z = symbols('x y z', real=True) assert ask(Q.is_true((x == y) & (y == z)), Q.is_true((x >= y) & (y >= z) & (z >= x))) def test_N9(): with assuming(Q.real(x)): assert solve(abs(x - 1) > 2) == Or(x < -1, x > 3) def test_N10(): p = (x - 1)*(x - 2)*(x - 3)*(x - 4)*(x - 5) assert solve(expand(p) < 0, assume=Q.real(x)) == Or( And(Lt(2, x), Lt(x, 3)), And(Lt(4, x), Lt(x, 5)), Lt(x, 1)) def test_N11(): assert solve(6/(x - 3) <= 3, assume=Q.real(x)) == Or(5 <= x, x < 3) @XFAIL def test_N12(): assert solve(sqrt(x) < 2, assume=Q.real(x)) == And(Le(0, x), Lt(x, 4)) @XFAIL def test_N13(): # raises NotImplementedError: can't reduce [sin(x) < 2] assert solve(sin(x) < 2, assume=Q.real(x)) == [] # S.Reals not found @XFAIL def test_N14(): # raises NotImplementedError: can't reduce [sin(x) < 1] assert (solve(sin(x) < 1, assume=Q.real(x)) == Ne(x, pi/2)) @XFAIL def test_N15(): r, t = symbols('r t', real=True) # raises NotImplementedError: only univariate inequalities are supported solve(abs(2*r*(cos(t) - 1) + 1) <= 1, r) @XFAIL def test_N16(): r, t = symbols('r t', real=True) solve((r**2)*((cos(t) - 4)**2)*sin(t)**2 < 9, r) @XFAIL def test_N17(): # raises NotImplementedError: only univariate inequalities are supported assert solve((x + y > 0, x - y < 0)) == (abs(x) < y) def test_O1(): M = Matrix((1 + I, -2, 3*I)) assert sqrt(expand(M.dot(M.H))) == sqrt(15) def test_O2(): assert Matrix((2, 2, -3)).cross(Matrix((1, 3, 1))) == Matrix([[11], [-5], [4]]) @slow def test_O3(): (va, vb, vc, vd) = MV.setup('va vb vc vd') assert (va ^ vb) | (vc ^ vd) == -(va | vc)*(vb | vd) + (va | vd)*(vb | vc) def test_O4(): (ex, ey, ez, grad) = MV.setup('e*x|y|z', metric='[1,1,1]', coords=(x, y, z)) F = ex*(x*y*z) + ey*((x*y*z)**2) + ez*((y**2)*(z**3)) assert (grad^F -(x*z*(2*y**2*z - 1))*ex^ey - x*y*ex^ez + (2*y*z*(-x**2*y + z**2))*ey^ez) == 0 @XFAIL @slow def test_O5(): (_, _, _, grad) = MV.setup('e*x|y|z',metric='[1,1,1]',coords=(x, y, z)) f = MV('f','vector',fct=True) g = MV('g','vector',fct=True) assert grad|(f^g)-g|(grad^f)+f|(grad^g) == 0 #testO8-O9 MISSING!! def test_O10(): L = [Matrix([2, 3, 5]), Matrix([3, 6, 2]), Matrix([8, 3, 6])] assert GramSchmidt(L) == [Matrix([ [2], [3], [5]]), Matrix([ [S(23)/19], [S(63)/19], [S(-47)/19]]), Matrix([ [S(1692)/353], [S(-1551)/706], [S(-423)/706]])] @XFAIL def test_P1(): raise NotImplementedError("Matrix property/function to extract Nth \ diagonal not implemented. See Matlab diag(A,k) \ http://www.mathworks.de/de/help/symbolic/diag.html") def test_P2(): M = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) M.row_del(1) M.col_del(2) assert M == Matrix([[1, 2], [7, 8]]) @XFAIL def test_P3(): A = Matrix([ [11, 12, 13, 14], [21, 22, 23, 24], [31, 32, 33, 34], [41, 42, 43, 44]]) A11 = A[0:3, 1:4] A12 = A[(0, 1, 3), (2, 0, 3)] # unsupported raises exception A21 = A A221 = A[0:2, 2:4] A222 = A[(3, 0), (2, 1)] # unsupported raises exception A22 = BlockMatrix([A221, A222]) B = BlockMatrix([[A11, A12], [A21, A22]]) assert B == Matrix([[12, 13, 14, 13, 11, 14], [22, 22, 24, 23, 21, 24], [32, 33, 34, 43, 41, 44], [11, 12, 13, 14, 13, 14], [21, 22, 23, 24, 23, 24], [31, 32, 33, 34, 43, 42], [41, 42, 43, 44, 13, 12]]) @XFAIL def test_P4(): raise NotImplementedError("Block matrix diagonalization not supported") @XFAIL def test_P5(): M = Matrix([[7, 11], [3, 8]]) # Raises exception % not supported for matrices assert M % 2 == Matrix([[1, 1], [1, 0]]) def test_P5_workaround(): M = Matrix([[7, 11], [3, 8]]) assert M.applyfunc(lambda i: i % 2) == Matrix([[1, 1], [1, 0]]) def test_P6(): M = Matrix([[cos(x), sin(x)], [-sin(x), cos(x)]]) assert M.diff(x, 2) == Matrix([[-cos(x), -sin(x)], [sin(x), -cos(x)]]) def test_P7(): M = Matrix([[x, y]])*( z*Matrix([[1, 3, 5], [2, 4, 6]]) + Matrix([[7, -9, 11], [-8, 10, -12]])) assert M == Matrix([[x*(z + 7) + y*(2*z - 8), x*(3*z - 9) + y*(4*z + 10), x*(5*z + 11) + y*(6*z - 12)]]) @XFAIL def test_P8(): M = Matrix([[1, -2*I], [-3*I, 4]]) assert M.norm(ord=S.Infinity) == 7 # Matrix.norm(ord=inf) not implemented def test_P9(): a, b, c = symbols('a b c', real=True) M = Matrix([[a/(b*c), 1/c, 1/b], [1/c, b/(a*c), 1/a], [1/b, 1/a, c/(a*b)]]) assert factor(M.norm('fro')) == (a**2 + b**2 + c**2)/(abs(a)*abs(b)*abs(c)) @XFAIL def test_P10(): M = Matrix([[1, 2 + 3*I], [f(4 - 5*i), 6]]) # conjugate(f(4 - 5*i)) is not simplified to f(4+5*I) assert M.H == Matrix([[1, f(4 + 5*I)], [2 + 3*I, 6]]) @XFAIL def test_P11(): # raises NotImplementedError("Matrix([[x,y],[1,x*y]]).inv() # not simplifying to extract common factor") assert Matrix([[x, y], [1, x*y]]).inv() == (1/(x**2 - 1))*Matrix([[x, -1], [-1/y, x/y]]) def test_P12(): A11 = MatrixSymbol('A11', n, n) A12 = MatrixSymbol('A12', n, n) A22 = MatrixSymbol('A22', n, n) B = BlockMatrix([[A11, A12], [ZeroMatrix(n, n), A22]]) assert block_collapse(B.I) == BlockMatrix([[A11.I, (-1)*A11.I*A12*A22.I], [ZeroMatrix(n, n), A22.I]]) def test_P13(): M = Matrix([[1, x - 2, x - 3], [x - 1, x**2 - 3*x + 6, x**2 - 3*x - 2], [x - 2, x**2 - 8, 2*(x**2) - 12*x + 14]]) L, U, _ = M.LUdecomposition() assert simplify(L) == Matrix([[1, 0, 0], [x - 1, 1, 0], [x - 2, x - 3, 1]]) assert simplify(U) == Matrix([[1, x - 2, x - 3], [0, 4, x - 5], [0, 0, x - 7]]) def test_P14(): M = Matrix([[1, 2, 3, 1, 3], [3, 2, 1, 1, 7], [0, 2, 4, 1, 1], [1, 1, 1, 1, 4]]) R, _ = M.rref() assert R == Matrix([[1, 0, -1, 0, 2], [0, 1, 2, 0, -1], [0, 0, 0, 1, 3], [0, 0, 0, 0, 0]]) def test_P15(): M = Matrix([[-1, 3, 7, -5], [4, -2, 1, 3], [2, 4, 15, -7]]) assert M.rank() == 2 def test_P16(): M = Matrix([[2*sqrt(2), 8], [6*sqrt(6), 24*sqrt(3)]]) assert M.rank() == 1 @XFAIL def test_P17(): t = symbols('t', real=True) M=Matrix([ [sin(2*t), cos(2*t)], [2*(1 - (cos(t)**2))*cos(t), (1 - 2*(sin(t)**2))*sin(t)]]) assert M.rank() == 1 def test_P18(): M = Matrix([[1, 0, -2, 0], [-2, 1, 0, 3], [-1, 2, -6, 6]]) assert M.nullspace() == [Matrix([[2], [4], [1], [0]]), Matrix([[0], [-3], [0], [1]])] def test_P19(): w = symbols('w') M = Matrix([[1, 1, 1, 1], [w, x, y, z], [w**2, x**2, y**2, z**2], [w**3, x**3, y**3, z**3]]) assert M.det() == (w**3*x**2*y - w**3*x**2*z - w**3*x*y**2 + w**3*x*z**2 + w**3*y**2*z - w**3*y*z**2 - w**2*x**3*y + w**2*x**3*z + w**2*x*y**3 - w**2*x*z**3 - w**2*y**3*z + w**2*y*z**3 + w*x**3*y**2 - w*x**3*z**2 - w*x**2*y**3 + w*x**2*z**3 + w*y**3*z**2 - w*y**2*z**3 - x**3*y**2*z + x**3*y*z**2 + x**2*y**3*z - x**2*y*z**3 - x*y**3*z**2 + x*y**2*z**3 ) @XFAIL def test_P20(): raise NotImplementedError("Matrix minimal polynomial not supported") def test_P21(): M = Matrix([[5, -3, -7], [-2, 1, 2], [2, -3, -4]]) assert M.charpoly(x).as_expr() == x**3 - 2*x**2 - 5*x + 6 @slow def test_P22(): # Wester test calculates eigenvalues for a diagonal matrix of dimension 100 # This currently takes forever with sympy: # M = (2 - x)*eye(100); # assert M.eigenvals() == {-x + 2: 100} # So we will speed-up the test checking only for dimension=12 d = 12 M = (2 - x)*eye(d) assert M.eigenvals() == {-x + 2: d} def test_P23(): M = Matrix([ [2, 1, 0, 0, 0], [1, 2, 1, 0, 0], [0, 1, 2, 1, 0], [0, 0, 1, 2, 1], [0, 0, 0, 1, 2]]) assert M.eigenvals() == { S('1'): 1, S('2'): 1, S('3'): 1, S('sqrt(3) + 2'): 1, S('-sqrt(3) + 2'): 1} def test_P24(): M = Matrix([[611, 196, -192, 407, -8, -52, -49, 29], [196, 899, 113, -192, -71, -43, -8, -44], [-192, 113, 899, 196, 61, 49, 8, 52], [ 407, -192, 196, 611, 8, 44, 59, -23], [ -8, -71, 61, 8, 411, -599, 208, 208], [ -52, -43, 49, 44, -599, 411, 208, 208], [ -49, -8, 8, 59, 208, 208, 99, -911], [ 29, -44, 52, -23, 208, 208, -911, 99]]) assert M.eigenvals() == { S('0'): 1, S('10*sqrt(10405)'): 1, S('100*sqrt(26) + 510'): 1, S('1000'): 2, S('-100*sqrt(26) + 510'): 1, S('-10*sqrt(10405)'): 1, S('1020'): 1} def test_P25(): MF = N(Matrix([[ 611, 196, -192, 407, -8, -52, -49, 29], [ 196, 899, 113, -192, -71, -43, -8, -44], [-192, 113, 899, 196, 61, 49, 8, 52], [ 407, -192, 196, 611, 8, 44, 59, -23], [ -8, -71, 61, 8, 411, -599, 208, 208], [ -52, -43, 49, 44, -599, 411, 208, 208], [ -49, -8, 8, 59, 208, 208, 99, -911], [ 29, -44, 52, -23, 208, 208, -911, 99]])) assert (Matrix(sorted(MF.eigenvals())) - Matrix( [-1020.0490184299969, 0.0, 0.09804864072151699, 1000.0, 1019.9019513592784, 1020.0, 1020.0490184299969])).norm() < 1e-13 def test_P26(): a0, a1, a2, a3, a4 = symbols('a0 a1 a2 a3 a4') M = Matrix([[-a4, -a3, -a2, -a1, -a0, 0, 0, 0, 0], [ 1, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 1, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 1, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 1, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, -1, -1, 0, 0], [ 0, 0, 0, 0, 0, 1, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 1, -1, -1], [ 0, 0, 0, 0, 0, 0, 0, 1, 0]]) assert M.eigenvals() == { S('-1/2 - sqrt(3)*I/2'): 2, S('-1/2 + sqrt(3)*I/2'): 2} def test_P27(): a = symbols('a') M = Matrix([[a, 0, 0, 0, 0], [0, 0, 0, 0, 1], [0, 0, a, 0, 0], [0, 0, 0, a, 0], [0, -2, 0, 0, 2]]) assert M.eigenvects() == [(a, 3, [Matrix([[1], [0], [0], [0], [0]]), Matrix([[0], [0], [1], [0], [0]]), Matrix([[0], [0], [0], [1], [0]])]), (1 - I, 1, [Matrix([[ 0], [-1/(-1 + I)], [ 0], [ 0], [ 1]])]), (1 + I, 1, [Matrix([[ 0], [-1/(-1 - I)], [ 0], [ 0], [ 1]])])] @XFAIL def test_P28(): raise NotImplementedError("Generalized eigenvectors not supported \ https://code.google.com/p/sympy/issues/detail?id=2194") @XFAIL def test_P29(): raise NotImplementedError("Generalized eigenvectors not supported \ https://code.google.com/p/sympy/issues/detail?id=2194") def test_P30(): M = Matrix([[1, 0, 0, 1, -1], [0, 1, -2, 3, -3], [0, 0, -1, 2, -2], [1, -1, 1, 0, 1], [1, -1, 1, -1, 2]]) _, J = M.jordan_form() assert J == Matrix([[-1, 0, 0, 0, 0], [0, 1, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 1, 1], [0, 0, 0, 0, 1]]) @XFAIL def test_P31(): raise NotImplementedError("Smith normal form not implemented") def test_P32(): M = Matrix([[1, -2], [2, 1]]) assert exp(M).rewrite(cos).simplify() == Matrix([[E*cos(2), -E*sin(2)], [E*sin(2), E*cos(2)]]) def test_P33(): w, t = symbols('w t') M = Matrix([[0, 1, 0, 0], [0, 0, 0, 2*w], [0, 0, 0, 1], [0, -2*w, 3*w**2, 0]]) assert exp(M*t).rewrite(cos).expand() == Matrix([ [1, -3*t + 4*sin(t*w)/w, 6*t*w - 6*sin(t*w), -2*cos(t*w)/w + 2/w], [0, 4*cos(t*w) - 3, -6*w*cos(t*w) + 6*w, 2*sin(t*w)], [0, 2*cos(t*w)/w - 2/w, -3*cos(t*w) + 4, sin(t*w)/w], [0, -2*sin(t*w), 3*w*sin(t*w), cos(t*w)]]) @XFAIL def test_P34(): a, b, c = symbols('a b c', real=True) M = Matrix([[a, 1, 0, 0, 0, 0], [0, a, 0, 0, 0, 0], [0, 0, b, 0, 0, 0], [0, 0, 0, c, 1, 0], [0, 0, 0, 0, c, 1], [0, 0, 0, 0, 0, c]]) # raises exception, sin(M) not supported. exp(M*I) also not supported # https://code.google.com/p/sympy/issues/detail?id=3119 assert sin(M) == Matrix([[sin(a), cos(a), 0, 0, 0, 0], [0, sin(a), 0, 0, 0, 0], [0, 0, sin(b), 0, 0, 0], [0, 0, 0, sin(c), cos(c), -sin(c)/2], [0, 0, 0, 0, sin(c), cos(c)], [0, 0, 0, 0, 0, sin(c)]]) @XFAIL def test_P35(): M = pi/2*Matrix([[2, 1, 1], [2, 3, 2], [1, 1, 2]]) # raises exception, sin(M) not supported. exp(M*I) also not supported # https://code.google.com/p/sympy/issues/detail?id=3119 assert sin(M) == eye(3) @XFAIL def test_P36(): M = Matrix([[10, 7], [7, 17]]) assert sqrt(M) == Matrix([[3, 1], [1, 4]]) @XFAIL def test_P37(): M = Matrix([[1, 1, 0], [0, 1, 0], [0, 0, 1]]) #raises NotImplementedError: Implemented only for diagonalizable matrices M**Rational(1, 2) @XFAIL def test_P38(): M=Matrix([[0, 1, 0], [0, 0, 0], [0, 0, 0]]) #raises NotImplementedError: Implemented only for diagonalizable matrices M**Rational(1,2) @XFAIL def test_P39(): ''' M=Matrix([ [1, 1], [2, 2], [3, 3]]) M.SVD() ''' raise NotImplementedError("Singular value decomposition not implemented") def test_P40(): r, t = symbols('r t', real=True) M = Matrix([r*cos(t), r*sin(t)]) assert M.jacobian(Matrix([r, t])) == Matrix([[cos(t), -r*sin(t)], [sin(t), r*cos(t)]]) def test_P41(): r, t = symbols('r t', real=True) assert hessian(r**2*sin(t),(r,t)) == Matrix([[ 2*sin(t), 2*r*cos(t)], [2*r*cos(t), -r**2*sin(t)]]) def test_P42(): assert wronskian([cos(x), sin(x)], x).simplify() == 1 def test_P43(): def __my_jacobian(M, Y): return Matrix([M.diff(v).T for v in Y]).T r, t = symbols('r t', real=True) M = Matrix([r*cos(t), r*sin(t)]) assert __my_jacobian(M,[r,t]) == Matrix([[cos(t), -r*sin(t)], [sin(t), r*cos(t)]]) def test_P44(): def __my_hessian(f, Y): V = Matrix([diff(f, v) for v in Y]) return Matrix([V.T.diff(v) for v in Y]) r, t = symbols('r t', real=True) assert __my_hessian(r**2*sin(t), (r, t)) == Matrix([ [ 2*sin(t), 2*r*cos(t)], [2*r*cos(t), -r**2*sin(t)]]) def test_P45(): def __my_wronskian(Y, v): M = Matrix([Matrix(Y).T.diff(x, n) for n in range(0, len(Y))]) return M.det() assert __my_wronskian([cos(x), sin(x)], x).simplify() == 1 # Q1-Q6 Tensor tests missing @XFAIL def test_R1(): i, n = symbols('i n', integer=True, positive=True) xn = MatrixSymbol('xn', n, 1) Sm = Sum((xn[i, 0] - Sum(xn[j, 0], (j, 0, n - 1))/n)**2, (i, 0, n - 1)) # raises AttributeError: 'str' object has no attribute 'is_Piecewise' Sm.doit() @XFAIL def test_R2(): m, b = symbols('m b', real=True) i, n = symbols('i n', integer=True, positive=True) xn = MatrixSymbol('xn', n, 1) yn = MatrixSymbol('yn', n, 1) f = Sum((yn[i, 0] - m*xn[i, 0] - b)**2, (i, 0, n - 1)) f1 = diff(f, m) f2 = diff(f, b) # raises AttributeError: 'str' object has no attribute 'is_Piecewise' solve((f1, f2), m, b) @XFAIL def test_R3(): n, k = symbols('n k', integer=True, positive=True) sk = ((-1)**k) * (binomial(2*n, k))**2 Sm = Sum(sk, (k, 1, oo)) T = Sm.doit() T2 = T.combsimp() # returns -((-1)**n*factorial(2*n) # - (factorial(n))**2)*exp_polar(-I*pi)/(factorial(n))**2 assert T2 == (-1)**n*binomial(2*n, n) @XFAIL def test_R4(): # Macsyma indefinite sum test case: #(c15) /* Check whether the full Gosper algorithm is implemented # => 1/2^(n + 1) binomial(n, k - 1) */ #closedform(indefsum(binomial(n, k)/2^n - binomial(n + 1, k)/2^(n + 1), k)); #Time= 2690 msecs # (- n + k - 1) binomial(n + 1, k) #(d15) - -------------------------------- # n # 2 2 (n + 1) # #(c16) factcomb(makefact(%)); #Time= 220 msecs # n! #(d16) ---------------- # n # 2 k! 2 (n - k)! # Might be possible after fixing https://github.com/sympy/sympy/pull/1879 raise NotImplementedError("Indefinite sum not supported") @XFAIL def test_R5(): a, b, c, n, k = symbols('a b c n k', integer=True, positive=True) sk = ((-1)**k)*(binomial(a + b, a + k) *binomial(b + c, b + k)*binomial(c + a, c + k)) Sm = Sum(sk, (k, 1, oo)) T = Sm.doit() # hypergeometric series not calculated assert T == factorial(a+b+c)/(factorial(a)*factorial(b)*factorial(c)) @XFAIL def test_R6(): n, k = symbols('n k', integer=True, positive=True) gn = MatrixSymbol('gn', n + 1, 1) Sm = Sum(gn[k, 0] - gn[k - 1, 0], (k, 1, n + 1)) # raises AttributeError: 'str' object has no attribute 'is_Piecewise' assert Sm.doit() == -gn[0, 0] + gn[n + 1, 0] def test_R7(): n, k = symbols('n k', integer=True, positive=True) T = Sum(k**3,(k,1,n)).doit() assert T.factor() == n**2*(n + 1)**2/4 @XFAIL def test_R8(): n, k = symbols('n k', integer=True, positive=True) Sm = Sum(k**2*binomial(n, k), (k, 1, n)) T = Sm.doit() #returns Piecewise function # T.simplify() raisesAttributeError assert T.combsimp() == n*(n + 1)*2**(n - 2) def test_R9(): n, k = symbols('n k', integer=True, positive=True) Sm = Sum(binomial(n, k - 1)/k, (k, 1, n + 1)) assert Sm.doit().simplify() == (2**(n + 1) - 1)/(n + 1) @XFAIL def test_R10(): n, m, r, k = symbols('n m r k', integer=True, positive=True) Sm = Sum(binomial(n, k)*binomial(m, r - k), (k, 0, r)) T = Sm.doit() T2 = T.combsimp().rewrite(factorial) assert T2 == factorial(m + n)/(factorial(r)*factorial(m + n - r)) assert T2 == binomial(m + n, r).rewrite(factorial) # rewrite(binomial) is not working. # https://code.google.com/p/sympy/issues/detail?id=4036 T3 = T2.rewrite(binomial) assert T3 == binomial(m + n, r) @XFAIL def test_R11(): n, k = symbols('n k', integer=True, positive=True) sk = binomial(n, k)*fibonacci(k) Sm = Sum(sk, (k, 0, n)) T = Sm.doit() # Fibonacci simplification not implemented # https://code.google.com/p/sympy/issues/detail?id=4035 assert T == fibonacci(2*n) @XFAIL def test_R12(): n, k = symbols('n k', integer=True, positive=True) Sm = Sum(fibonacci(k)**2, (k, 0, n)) T = Sm.doit() assert T == fibonacci(n)*fibonacci(n + 1) @XFAIL def test_R13(): n, k = symbols('n k', integer=True, positive=True) Sm = Sum(sin(k*x), (k, 1, n)) T = Sm.doit() # Sum is not calculated assert T.simplify() == cot(x/2)/2 - cos(x*(2*n + 1)/2)/(2*sin(x/2)) @XFAIL def test_R14(): n, k = symbols('n k', integer=True, positive=True) Sm = Sum(sin((2*k - 1)*x), (k, 1, n)) T = Sm.doit() # Sum is not calculated assert T.simplify() == sin(n*x)**2/sin(x) @XFAIL def test_R15(): n, k = symbols('n k', integer=True, positive=True) Sm = Sum(binomial(n - k, k), (k, 0, floor(n/2))) T = Sm.doit() # Sum is not calculated assert T.simplify() == fibonacci(n + 1) def test_R16(): k = symbols('k', integer=True, positive=True) Sm = Sum(1/k**2 + 1/k**3, (k, 1, oo)) assert Sm.doit() == zeta(3) + pi**2/6 def test_R17(): k = symbols('k', integer=True, positive=True) assert abs(float(Sum(1/k**2 + 1/k**3, (k, 1, oo))) - 2.8469909700078206) < 1e-15 @XFAIL def test_R18(): k = symbols('k', integer=True, positive=True) Sm = Sum(1/(2**k*k**2), (k, 1, oo)) # returns polylog(2, 1/2), particular value for 1/2 is not known. # https://code.google.com/p/sympy/issues/detail?id=4033 T = Sm.doit() assert T.simplify() == -log(2)**2/2 + pi**2/12 @XFAIL def test_R19(): k = symbols('k', integer=True, positive=True) Sm = Sum(1/((3*k + 1)*(3*k + 2)*(3*k + 3)), (k, 0, oo)) T = Sm.doit() # assert fails, T not simplified assert T.simplify() == -log(3)/4 + sqrt(3)*pi/12 @XFAIL def test_R20(): n, k = symbols('n k', integer=True, positive=True) Sm = Sum(binomial(n, 4*k), (k, 0, oo)) T = Sm.doit() # assert fails, T not simplified assert T.simplify() == 2**(n/2)*cos(pi*n/4)/2 + 2**(n - 1)/2 @XFAIL def test_R21(): k = symbols('k', integer=True, positive=True) Sm = Sum(1/(sqrt(k*(k + 1)) * (sqrt(k) + sqrt(k + 1))), (k, 1, oo)) T = Sm.doit() # Sum not calculated assert T.simplify() == 1 # test_R22 answer not available in Wester samples # Sum(Sum(binomial(n, k)*binomial(n - k, n - 2*k)*x**n*y**(n - 2*k), # (k, 0, floor(n/2))), (n, 0, oo)) with abs(x*y)<1? @XFAIL def test_R23(): n, k = symbols('n k', integer=True, positive=True) Sm = Sum(Sum((factorial(n)/(factorial(k)**2*factorial(n - 2*k)))* (x/y)**k*(x*y)**(n - k), (n, 2*k, oo)), (k, 0, oo)) # Missing how to express constraint abs(x*y)<1? T = Sm.doit() # Sum not calculated assert T == -1/sqrt(x**2*y**2 - 4*x**2 - 2*x*y + 1) def test_R24(): m, k = symbols('m k', integer=True, positive=True) Sm = Sum(Product(k/(2*k - 1), (k, 1, m)), (m, 2, oo)) assert Sm.doit() == pi/2 def test_S1(): k = symbols('k', integer=True, positive=True) Pr = Product(gamma(k/3), (k, 1, 8)) assert Pr.doit().simplify() == 640*sqrt(3)*pi**3/6561 def test_S2(): n, k = symbols('n k', integer=True, positive=True) assert Product(k, (k, 1, n)).doit() == factorial(n) def test_S3(): n, k = symbols('n k', integer=True, positive=True) assert Product(x**k, (k, 1, n)).doit().simplify() == x**(n*(n + 1)/2) def test_S4(): n, k = symbols('n k', integer=True, positive=True) assert Product(1 + 1/k, (k, 1, n -1)).doit().simplify() == n def test_S5(): n, k = symbols('n k', integer=True, positive=True) assert (Product((2*k - 1)/(2*k), (k, 1, n)).doit().combsimp() == factorial(n - Rational(1, 2))/(sqrt(pi)*factorial(n))) @XFAIL def test_S6(): n, k = symbols('n k', integer=True, positive=True) # Product raises Infinite recursion error. # https://code.google.com/p/sympy/issues/detail?id=4034 assert (Product(x**2 -2*x*cos(k*pi/n) + 1, (k, 1, n - 1)).doit().simplify() == (x**(2*n) - 1)/(x**2 - 1)) @XFAIL def test_S7(): k = symbols('k', integer=True, positive=True) Pr = Product((k**3 - 1)/(k**3 + 1), (k, 2, oo)) T = Pr.doit() assert T.simplify() == Rational(2, 3) # T simplifies incorrectly to 0 @XFAIL def test_S8(): k = symbols('k', integer=True, positive=True) Pr = Product(1 - 1/(2*k)**2, (k, 1, oo)) T = Pr.doit() # T = nan https://code.google.com/p/sympy/issues/detail?id=4037 assert T.simplify() == 2/pi @XFAIL def test_S9(): k = symbols('k', integer=True, positive=True) Pr = Product(1 + (-1)**(k + 1)/(2*k - 1), (k, 1, oo)) # Product.doit() raises Infinite recursion error. # https://code.google.com/p/sympy/issues/detail?id=4034 T = Pr.doit() assert T.simplify() == sqrt(2) @XFAIL def test_S10(): k = symbols('k', integer=True, positive=True) Pr = Product((k*(k + 1) + 1 + I)/(k*(k + 1) + 1 - I), (k, 0, oo)) T = Pr.doit() # raises OverflowError # https://code.google.com/p/sympy/issues/detail?id=4038 assert T.simplify() == -1 def test_T1(): assert limit((1 + 1/n)**n, n, oo) == E assert limit((1 - cos(x))/x**2, x, 0) == Rational(1, 2) def test_T2(): assert limit((3**x + 5**x)**(1/x), x, oo) == 5 @XFAIL def test_T3(): assert limit(log(x)/(log(x) + sin(x)), x, oo) == 1 # raises PoleError def test_T4(): assert limit((exp(x*exp(-x)/(exp(-x) + exp(-2*x**2/(x + 1)))) - exp(x))/x, x, oo) == -exp(2) def test_T5(): assert limit(x*log(x)*log(x*exp(x) - x**2)**2/log(log(x**2 + 2*exp(exp(3*x**3*log(x))))), x, oo) == Rational(1, 3) def test_T6(): assert limit(1/n * factorial(n)**(1/n), n, oo) == exp(-1) def test_T7(): limit(1/n * gamma(n + 1)**(1/n), n, oo) def test_T8(): a, z = symbols('a z', real=True, positive=True) assert limit(gamma(z + a)/gamma(z)*exp(-a*log(z)), z, oo) == 1 @XFAIL def test_T9(): z, k = symbols('z k', real=True, positive=True) # raises NotImplementedError: # Don't know how to calculate the mrv of '(1, k)' assert limit(hyper((1, k), (1,), z/k), k, oo) == exp(z) @XFAIL def test_T10(): # raises PoleError should return euler-mascheroni constant limit(zeta(x) - 1/(x - 1), x, 1) @XFAIL def test_T11(): n, k = symbols('n k', integer=True, positive=True) # raises NotImplementedError assert limit(n**x/(x*product((1 + x/k), (k, 1, n))), n, oo) == gamma(x) @XFAIL def test_T12(): x, t = symbols('x t', real=True) # raises PoleError: Don't know how to calculate the # limit(sqrt(pi)*x*erf(x)/(2*(1 - exp(-x**2))), x, 0, dir=+) assert limit(x * integrate(exp(-t**2), (t, 0, x))/(1 - exp(-x**2)), x, 0) == 1 def test_T13(): x = symbols('x', real=True) assert [limit(x/abs(x), x, 0, dir='-'), limit(x/abs(x), x, 0, dir='+')] == [-1, 1] def test_T14(): x = symbols('x', real=True) assert limit(atan(-log(x)), x, 0, dir='+') == pi/2 def test_U1(): x = symbols('x', real=True) assert diff(abs(x), x) == sign(x) def test_U2(): f = Lambda(x, Piecewise((-x, x < 0), (x, x >= 0))) assert diff(f(x), x) == Piecewise((-1, x < 0), (1, x >= 0)) def test_U3(): f = Lambda(x, Piecewise((x**2 - 1, x == 1), (x**3, x != 1))) f1 = Lambda(x, diff(f(x), x)) assert f1(x) == 3*x**2 assert f1(1) == 3 @XFAIL def test_U4(): n = symbols('n', integer=True, positive=True) x = symbols('x', real=True) diff(x**n, x, n) assert diff(x**n, x, n).rewrite(factorial) == factorial(n) @XFAIL def test_U5(): # https://code.google.com/p/sympy/issues/detail?id=3582 # f(g(x)).diff(x,2) returns Derivative(g(x), x)**2*Subs(Derivative( # f(_xi_1), _xi_1, _xi_1), (_xi_1,), (g(x),)) + Derivative(g(x), x, x)* # Subs(Derivative(f(_xi_1), _xi_1), (_xi_1,), (g(x),)) raise NotImplementedError("f(g(t)).diff(t,2) Subs not performed") @XFAIL def test_U6(): h = Function('h') # raises ValueError: Invalid limits given: (y, h(x), g(x)) T = integrate(f(y), y, h(x), g(x)) T.diff(x) @XFAIL def test_U7(): p, t = symbols('p t', real=True) # Exact differential => d(V(P, T)) => dV/dP DP + dV/dT DT # raises ValueError: Since there is more than one variable in the # expression, the variable(s) of differentiation must be supplied to # differentiate f(p,t) diff(f(p, t)) def test_U8(): x, y = symbols('x y', real=True) eq = cos(x*y) + x eq = eq.subs(y, f(x)) # If SymPy had implicit_diff() function this hack could be avoided assert (solve((f(x) - eq).diff(x), f(x).diff(x))[0].subs(f(x), y) == (-y*sin(x*y) + 1)/(x*sin(x*y) + 1)) @XFAIL def test_U9(): # Wester sample case for Maple: # O29 := diff(f(x, y), x) + diff(f(x, y), y); # /d \ /d \ # |-- f(x, y)| + |-- f(x, y)| # \dx / \dy / # # O30 := factor(subs(f(x, y) = g(x^2 + y^2), %)); # 2 2 # 2 D(g)(x + y ) (x + y) x, y = symbols('x y', real=True) su = diff(f(x, y), x) + diff(f(x, y), y) s2 = Subs(su, f(x, y), g(x**2 + y**2)).doit() s3 = s2.doit().factor() # Subs not performed, s3 = 2*(x + y)*Subs(Derivative( # g(_xi_1), _xi_1), (_xi_1,), (x**2 + y**2,)) # Derivative(g(x*2 + y**2), x**2 + y**2) is not valid in SymPy, # and probably will remain that way. You can take derivatives with respect # to other expressions only if they are atomic, like a symbol or a # function. # D operator should be added to SymPy # See https://code.google.com/p/sympy/issues/detail?id=1620. # raises ValueError: Can't differentiate wrt the variable: x**2 + y**2 assert s3 == 2*(x + y)*Derivative(g(x**2 + y**2), x**2 + y**2) @XFAIL def test_U10(): z = symbols('z') # returns wrong value-3/4 . problem seems to come from series expansion assert residue((z**3 + 5)/((z**4 - 1)*(z + 1)), z, -1) == Rational(-9, 4) def test_U11(): (dx, dy, dz) = MV.setup('dx dy dz') # answer is correct, but SymPy doc does not indicate how/if differential # forms are supported assert (2*dx + dz) ^ (3*dx + dy + dz) ^ (dx + dy + 4*dz) == 8*dx ^ dy ^dz @XFAIL def test_U12(): # Wester sample case: # (c41) /* d(3 x^5 dy /\ dz + 5 x y^2 dz /\ dx + 8 z dx /\ dy) # => (15 x^4 + 10 x y + 8) dx /\ dy /\ dz */ # factor(ext_diff(3*x^5 * dy ~ dz + 5*x*y^2 * dz ~ dx + 8*z * dx ~ dy)); # 4 # (d41) (10 x y + 15 x + 8) dx dy dz raise NotImplementedError( "External diff of differential form not supported") @XFAIL def test_U13(): #assert minimize(x**4 - x + 1, x)== -3*2**Rational(1,3)/8 + 1 raise NotImplementedError("minimize() not supported") @XFAIL def test_U14(): #f = 1/(x**2 + y**2 + 1) #assert [minimize(f), maximize(f)] == [0,1] raise NotImplementedError("minimize(), maximize() not supported") @XFAIL def test_U15(): raise NotImplementedError("minimize() not supported and also solve does \ not support multivariate inequalities") @XFAIL def test_U16(): raise NotImplementedError("minimize() not supported in SymPy and also \ solve does not support multivariate inequalities") @XFAIL def test_U17(): raise NotImplementedError("Linear programming, symbolic simplex not \ supported in SymPy") @XFAIL def test_V1(): x = symbols('x', real=True) # integral not calculated # https://code.google.com/p/sympy/issues/detail?id=1113 assert integrate(abs(x), x) == x*abs(x)/2 def test_V2(): assert (integrate(Piecewise((-x, x < 0), (x, x >= 0)), x) == Piecewise((-x**2/2, x < 0), (x**2/2, x >= 0))) def test_V3(): assert integrate(1/(x**3 + 2),x).diff().simplify() == 1/(x**3 + 2) @XFAIL def test_V4(): assert integrate(2**x/sqrt(1 + 4**x), x) == asinh(2**x)/log(2) @XFAIL @slow def test_V5(): # Takes extremely long time # https://code.google.com/p/sympy/issues/detail?id=4050 assert (integrate((3*x - 5)**2/(2*x - 1)**(Rational(7, 2)), x) == (-41 + 80*x - 45*x**2)/(5*(2*x - 1)**Rational(5, 2))) @XFAIL def test_V6(): # returns RootSum(40*_z**2 - 1, Lambda(_i, _i*log(-4*_i + exp(-m*x))))/m assert (integrate(1/(2*exp(m*x) - 5*exp(-m*x)), x) == sqrt(10)*( log(2*exp(m*x) - sqrt(10)) - log(2*exp(m*x) + sqrt(10)))/(20*m)) def test_V7(): r1 = integrate(sinh(x)**4/cosh(x)**2) assert r1.simplify() == -3*x/2 + sinh(x)**3/(2*cosh(x)) + 3*tanh(x)/2 @XFAIL def test_V8_V9(): #Macsyma test case: #(c27) /* This example involves several symbolic parameters # => 1/sqrt(b^2 - a^2) log([sqrt(b^2 - a^2) tan(x/2) + a + b]/ # [sqrt(b^2 - a^2) tan(x/2) - a - b]) (a^2 < b^2) # [Gradshteyn and Ryzhik 2.553(3)] */ #assume(b^2 > a^2)$ #(c28) integrate(1/(a + b*cos(x)), x); #(c29) trigsimp(ratsimp(diff(%, x))); # 1 #(d29) ------------ # b cos(x) + a raise NotImplementedError( "Integrate with assumption not supported") def test_V10(): assert integrate(1/(3 + 3*cos(x) + 4*sin(x)), x) == log(tan(x/2) + 3/4)/4 def test_V11(): # x = symbols('x', real=True) r1 = integrate(1/(4 + 3*cos(x) + 4*sin(x)), x) r2 = factor(r1) assert (logcombine(r2, force=True) == log(((tan(x/2) + 1)/(tan(x/2) + 7))**(1/3))) @XFAIL def test_V12(): r1 = integrate(1/(5 + 3*cos(x) + 4*sin(x)), x) # Correct result in python2.7.4 wrong result in python3.3.1 # https://code.google.com/p/sympy/issues/detail?id=4058 assert r1 == -1/(tan(x/2) + 2) @XFAIL def test_V13(): r1 = integrate(1/(6 + 3*cos(x) + 4*sin(x)), x) # expression not simplified, returns: -sqrt(11)*I*log(tan(x/2) + 4/3 # - sqrt(11)*I/3)/11 + sqrt(11)*I*log(tan(x/2) + 4/3 + sqrt(11)*I/3)/11 assert r1.simplify() == 2*sqrt(11)*atan(sqrt(11)*(3*tan(x/2) + 4)/11)/11 @XFAIL def test_V14(): r1 = integrate(log(abs(x**2 - y**2)), x) # I.simplify() raises AttributeError # https://code.google.com/p/sympy/issues/detail?id=4059 assert (r1.simplify() == x*log(abs(x**2 - y**2)) + y*log(x + y) - y*log(x - y) - 2*x) def test_V15(): r1 = integrate(x*acot(x/y), x) assert simplify(r1 - (x*y + (x**2 + y**2)*acot(x/y))/2) == 0 @XFAIL def test_V16(): # test case in Mathematica syntax: # In[53]:= Integrate[Cos[5*x]*CosIntegral[2*x], x] # CosIntegral[2 x] Sin[5 x] -SinIntegral[3 x] - SinIntegral[7 x] # Out[53]= ------------------------- + ------------------------------------ # 5 10 # cosine Integral function not supported # http://reference.wolfram.com/mathematica/ref/CosIntegral.html raise NotImplementedError("cosine integral function not supported") @XFAIL def test_V17(): r1 = integrate((diff(f(x), x)*g(x) - f(x)*diff(g(x), x))/(f(x)**2 - g(x)**2), x) # integral not calculated assert simplify(r1 - (f(x) - g(x))/(f(x) + g(x))/2) == 0 @XFAIL def test_W1(): # The function has a pole at y. # The integral has a Cauchy principal value of zero but SymPy returns -I*pi # https://code.google.com/p/sympy/issues/detail?id=4060 assert integrate(1/(x - y), (x, y - 1, y + 1)) == 0 @XFAIL def test_W2(): # The function has a pole at y. # The integral is divergent but SymPy returns -2 # https://code.google.com/p/sympy/issues/detail?id=4061 # Test case in Macsyma: # (c6) errcatch(integrate(1/(x - a)^2, x, a - 1, a + 1)); # Integral is divergent assert integrate(1/(x - y)**2, (x, y - 1, y + 1)) == zoo @XFAIL def test_W3(): # integral is not calculated # https://code.google.com/p/sympy/issues/detail?id=4062 assert integrate(sqrt(x + 1/x - 2), (x, 0, 1)) == S(4)/3 @XFAIL def test_W4(): # integral is not calculated assert integrate(sqrt(x + 1/x - 2), (x, 1, 2)) == -2*sqrt(2)/3 + S(4)/3 @XFAIL def test_W5(): # integral is not calculated assert integrate(sqrt(x + 1/x - 2), (x, 0, 2)) == -2*sqrt(2)/3 + S(8)/3 @XFAIL @slow def test_W6(): # integral is not calculated assert integrate(sqrt(2 - 2*cos(2*x))/2, (x, -3*pi/4, -pi/4)) == sqrt(2) def test_W7(): a = symbols('a', real=True, positive=True) r1 = integrate(cos(x)/(x**2 + a**2), (x, -oo, oo)) assert r1.simplify() == pi*exp(-a)/a @XFAIL def test_W8(): # Test case in Mathematica: # In[19]:= Integrate[t^(a - 1)/(1 + t), {t, 0, Infinity}, # Assumptions -> 0 < a < 1] # Out[19]= Pi Csc[a Pi] raise NotImplementedError( "Integrate with assumption 0 < a < 1 not supported") @XFAIL def test_W9(): # Integrand with a residue at infinity => -2 pi [sin(pi/5) + sin(2pi/5)] # (principal value) [Levinson and Redheffer, p. 234] *) r1 = integrate(5*x**3/(1 + x + x**2 + x**3 + x**4), (x, -oo, oo)) r2 = r1.doit() assert r2 == -2*pi*(sqrt(-sqrt(5)/8 + 5/8) + sqrt(sqrt(5)/8 + 5/8)) @XFAIL def test_W10(): # integrate(1/[1 + x + x^2 + ... + x^(2 n)], x = -infinity..infinity) = # 2 pi/(2 n + 1) [1 + cos(pi/[2 n + 1])] csc(2 pi/[2 n + 1]) # [Levinson and Redheffer, p. 255] => 2 pi/5 [1 + cos(pi/5)] csc(2 pi/5) */ r1 = integrate(x/(1 + x + x**2 + x**4), (x, -oo, oo)) r2 = r1.doit() assert r2 == 2*pi*(sqrt(5)/4 + 5/4)*csc(2*pi/5)/5 @XFAIL def test_W11(): # integral not calculated assert (integrate(sqrt(1 - x**2)/(1 + x**2), (x, -1, 1)) == pi*(-1 + sqrt(2))) def test_W12(): p = symbols('p', real=True, positive=True) q = symbols('q', real=True) r1 = integrate(x*exp(-p*x**2 + 2*q*x), (x, -oo, oo)) assert r1.simplify() == sqrt(pi)*q*exp(q**2/p)/p**(3/2) @XFAIL def test_W13(): # Integral not calculated. Expected result is 2*(Euler_mascheroni_constant) r1 = integrate(1/log(x) + 1/(1 - x) - log(log(1/x)), (x, 0, 1)) assert r1 == 2*EulerGamma def test_W14(): assert integrate(sin(x)/x*exp(2*I*x), (x, -oo, oo)) == 0 @XFAIL def test_W15(): # integral not calculated assert integrate(log(gamma(x))*cos(6*pi*x), (x, 0, 1)) == S(1)/12 def test_W16(): assert integrate((1 + x)**3*legendre_poly(1, x)*legendre_poly(2, x), (x, -1, 1)) == S(36)/35 def test_W17(): a, b = symbols('a b', real=True, positive=True) assert integrate(exp(-a*x)*besselj(0, b*x), (x, 0, oo)) == 1/(b*sqrt(a**2/b**2 + 1)) def test_W18(): assert integrate((besselj(1, x)/x)**2, (x, 0, oo)) == 4/(3*pi) @XFAIL def test_W19(): # integrate(cos_int(x)*bessel_j[0](2*sqrt(7*x)), x, 0, inf); # Expected result is cos 7 - 1)/7 [Gradshteyn and Ryzhik 6.782(3)] raise NotImplementedError("cosine integral function not supported") @XFAIL def test_W20(): # integral not calculated assert (integrate(x**2*polylog(3, 1/(x + 1)), (x, 0, 1)) == -pi**2/36 - S(17)/108 + zeta(3)/4 + (-pi**2/2 - 4*log(2) + log(2)**2 + 35/3)*log(2)/9) def test_W21(): assert abs(N(integrate(x**2*polylog(3, 1/(x + 1)), (x, 0, 1))) - 0.210882859565594) < 1e-15 def test_W22(): t, u = symbols('t u', real=True) s = Lambda(x, Piecewise((1, And(x >= 1, x <= 2)), (0, True))) assert (integrate(s(t)*cos(t), (t, 0, u)) == Piecewise((sin(u) - sin(1), And(u <= 2, u >= 1)), (0, u <= 1), (-sin(1) + sin(2), True))) @XFAIL @slow def test_W23(): a, b = symbols('a b', real=True, positive=True) r1 = integrate(integrate(x/(x**2 + y**2), (x, a, b)), (y, -oo, oo)) assert r1.simplify() == pi*(-a + b) # integrate raises RuntimeError: maximum recursion depth exceeded r2 = integrate(integrate(x/(x**2 + y**2), (y, -oo, oo)), (x, a, b)) assert r1 == r2 @XFAIL @slow def test_W24(): x, y = symbols('x y', real=True) r1 = integrate(integrate(sqrt(x**2 + y**2), (x, 0, 1)), (y, 0, 1)) assert (r1 - (sqrt(2) + asinh(1))/3).simplify() == 0 @XFAIL @slow def test_W25(): a, x, y = symbols('a x y', real=True) i1 = integrate(sin(a)*sin(y)/sqrt(1- sin(a)**2*sin(x)**2*sin(y)**2), (x, 0, pi/2)) i2 = integrate(i1, (y, 0, pi/2)) assert (i2 - pi*a/2).simplify() == 0 @XFAIL def test_W26(): x, y = symbols('x y', real=True) # integrate(abs(y - x**2), (y,0,2)) raises ValueError: gamma function pole # https://code.google.com/p/sympy/issues/detail?id=4066 assert integrate(integrate(abs(y - x**2), (y, 0, 2)), (x, -1, 1)) == S(46)/15 def test_W27(): a, b, c = symbols('a b c') assert integrate(integrate(integrate(1, (z, 0, c*(1 - x/a - y/b))), (y, 0, b*(1 - x/a))), (x, 0, a)) == a*b*c/6 def test_X1(): v, c = symbols('v c', real=True) assert (series(1/sqrt(1 - (v/c)**2), v, x0=0, n=8) == 5*v**6/(16*c**6) + 3*v**4/(8*c**4) + v**2/(2*c**2) + 1 + O(v**8)) def test_X2(): v, c = symbols('v c', real=True) s1 = series(1/sqrt(1 - (v/c)**2), v, x0=0, n=8) assert (1/s1**2).series(v, x0=0, n=8) == -v**2/c**2 + 1 + O(v**8) def test_X3(): s1 = (sin(x).series()/cos(x).series()).series() s2 = tan(x).series() assert s2 == x + x**3/3 + 2*x**5/15 + O(x**6) assert s1 == s2 def test_X4(): s1 = log(sin(x)/x).series() assert s1 == -x**2/6 - x**4/180 + O(x**6) assert log(series(sin(x)/x)).series() == s1 @XFAIL def test_X5(): # test case in Mathematica syntax: # In[21]:= (* => [a f'(a d) + g(b d) + integrate(h(c y), y = 0..d)] # + [a^2 f''(a d) + b g'(b d) + h(c d)] (x - d) *) # In[22]:= D[f[a*x], x] + g[b*x] + Integrate[h[c*y], {y, 0, x}] # Out[22]= g[b x] + Integrate[h[c y], {y, 0, x}] + a f'[a x] # In[23]:= Series[%, {x, d, 1}] # Out[23]= (g[b d] + Integrate[h[c y], {y, 0, d}] + a f'[a d]) + # 2 2 # (h[c d] + b g'[b d] + a f''[a d]) (-d + x) + O[-d + x] h = Function('h') a, b, c, d = symbols('a b c d', real=True) # series() raises NotImplementedError: # The _eval_nseries method should be added to to give terms up to O(x**n) at x=0 series(diff(f(a*x), x) + g(b*x) + integrate(h(c*y), (y, 0, x)), x, x0=d, n=2) # assert missing, until exception is removed def test_X6(): # Taylor series of nonscalar objects (noncommutative multiplication) # expected result => (B A - A B) t^2/2 + O(t^3) [Stanly Steinberg] a, b = symbols('a b', commutative=False, scalar=False) assert (series(exp((a + b)*x) - exp(a*x) * exp(b*x), x, x0=0, n=3) == x**2*(-a*b/2 + b*a/2) + O(x**3)) def test_X7(): # => sum( Bernoulli[k]/k! x^(k - 2), k = 1..infinity ) # = 1/x^2 - 1/(2 x) + 1/12 - x^2/720 + x^4/30240 + O(x^6) # [Levinson and Redheffer, p. 173] assert (series(1/(x*(exp(x) - 1)), x, 0, 7) == x**(-2) - 1/(2*x) + S(1)/12 - x**2/720 + x**4/30240 - x**6/1209600 + O(x**7)) @XFAIL def test_X8(): # Puiseux series (terms with fractional degree): # => 1/sqrt(x - 3/2 pi) + (x - 3/2 pi)^(3/2) / 12 + O([x - 3/2 pi]^(7/2)) x = symbols('x', real=True) # raises PoleError: Cannot expand sec(_x + 3*pi/2) around 0 # https://code.google.com/p/sympy/issues/detail?id=4068 series(sqrt(sec(x)), x, x0=pi*3/2, n=4) # assert missing, until exception is removed def test_X9(): assert (series(x**x, x, x0=0, n=4) == 1 + x*log(x) + x**2*log(x)**2/2 + x**3*log(x)**3/6 + O(x**4*log(x)**4)) def test_X10(): z, w = symbols('z w') assert (series(log(sinh(z)) + log(cosh(z + w)), z, x0=0, n=2) == log(cosh(w)) + log(z) + z*sinh(w)/cosh(w) + O(z**2)) def test_X11(): z, w = symbols('z w') assert (series(log(sinh(z) * cosh(z + w)), z, x0=0, n=2) == log(cosh(w)) + log(z) + z*sinh(w)/cosh(w) + O(z**2)) @XFAIL def test_X12(): # Look at the generalized Taylor series around x = 1 # Result => (x - 1)^a/e^b [1 - (a + 2 b) (x - 1) / 2 + O((x - 1)^2)] a, b, x = symbols('a b x', real=True) # series returns O(log(x)**2) # https://code.google.com/p/sympy/issues/detail?id=4069 assert (series(log(x)**a*exp(-b*x), x, x0=1, n=2) == (x - 1)**a/exp(b)*(1 - (a + 2*b)*(x - 1)/2 + O((x - 1)**2))) def test_X13(): assert series(sqrt(2*x**2 + 1), x, x0=oo, n=1) == sqrt(2)*x + O(1/x, (x, oo)) @XFAIL def test_X14(): # Wallis' product => 1/sqrt(pi n) + ... [Knopp, p. 385] assert series(1/2**(2*n)*binomial(2*n, n), n, x==oo, n=1) == 1/(sqrt(pi)*sqrt(n)) + O(1/x, (x, oo)) @XFAIL def test_X15(): # => 0!/x - 1!/x^2 + 2!/x^3 - 3!/x^4 + O(1/x^5) [Knopp, p. 544] x, t = symbols('x t', real=True) # raises RuntimeError: maximum recursion depth exceeded # https://code.google.com/p/sympy/issues/detail?id=4065 e1 = integrate(exp(-t)/t, (t, x, oo)) assert (series(e1, x, x0=oo, n=5) == 6/x**4 + 2/x**3 - 1/x**2 + 1/x + O(x**(-5), (x, oo))) def test_X16(): # Multivariate Taylor series expansion => 1 - (x^2 + 2 x y + y^2)/2 + O(x^4) assert (series(cos(x + y), x + y, x0=0, n=4) == 1 - (x + y)**2/2 + O(x**4 + x**3*y + x**2*y**2 + x*y**3 + y**4, x, y)) @XFAIL def test_X17(): # Power series (compute the general formula) # (c41) powerseries(log(sin(x)/x), x, 0); # /aquarius/data2/opt/local/macsyma_422/library1/trgred.so being loaded. # inf # ==== i1 2 i1 2 i1 # \ (- 1) 2 bern(2 i1) x # (d41) > ------------------------------ # / 2 i1 (2 i1)! # ==== # i1 = 1 raise NotImplementedError("Formal power series not supported") @XFAIL def test_X18(): # Power series (compute the general formula). Maple FPS: # > FormalPowerSeries(exp(-x)*sin(x), x = 0); # infinity # ----- (1/2 k) k # \ 2 sin(3/4 k Pi) x # ) ------------------------- # / k! # ----- raise NotImplementedError("Formal power series not supported") @XFAIL def test_X19(): # (c45) /* Derive an explicit Taylor series solution of y as a function of # x from the following implicit relation: # y = x - 1 + (x - 1)^2/2 + 2/3 (x - 1)^3 + (x - 1)^4 + # 17/10 (x - 1)^5 + ... # */ # x = sin(y) + cos(y); # Time= 0 msecs # (d45) x = sin(y) + cos(y) # # (c46) taylor_revert(%, y, 7); raise NotImplementedError("Solve using series not supported. \ Inverse Taylor series expansion also not supported") @XFAIL def test_X20(): # Pade (rational function) approximation => (2 - x)/(2 + x) # > numapprox[pade](exp(-x), x = 0, [1, 1]); # bytes used=9019816, alloc=3669344, time=13.12 # 1 - 1/2 x # --------- # 1 + 1/2 x # mpmath support numeric Pade approximant but there is # no symbolic implementation in SymPy # http://en.wikipedia.org/wiki/Pad%C3%A9_approximant raise NotImplementedError("Symbolic Pade approximant not supported") @XFAIL def test_X21(): # (c48) /* Fourier series of f(x) of period 2 p over the interval [-p, p] # => - (2 p / pi) sum( (-1)^n sin(n pi x / p) / n, n = 1..infinity ) */ # assume(p > 0)$ # Time= 0 msecs # # (c49) fourier_series(x, x, p); # /aquarius/data2/opt/local/macsyma_422/share/fourier.so being loaded. # (e49) a = 0 # 0 # # (e50) a = 0 # %nn # # %nn # 2 (- 1) p # (e51) b = - ------------ # %nn %pi %nn # # Time= 4540 msecs # inf %nn %pi %nn x # ==== (- 1) sin(---------) # \ p # 2 p > ----------------------- # / %nn # ==== # %nn = 1 # (d51) - ----------------------------------- # %pi raise NotImplementedError("Fourier series not supported") @XFAIL def test_X22(): # (c52) /* => p / 2 # - (2 p / pi^2) sum( [1 - (-1)^n] cos(n pi x / p) / n^2, # n = 1..infinity ) */ # fourier_series(abs(x), x, p); # p # (e52) a = - # 0 2 # # %nn # (2 (- 1) - 2) p # (e53) a = ------------------ # %nn 2 2 # %pi %nn # # (e54) b = 0 # %nn # # Time= 5290 msecs # inf %nn %pi %nn x # ==== (2 (- 1) - 2) cos(---------) # \ p # p > ------------------------------- # / 2 # ==== %nn # %nn = 1 p # (d54) ----------------------------------------- + - # 2 2 # %pi raise NotImplementedError("Fourier series not supported") def test_Y1(): t = symbols('t', real=True, positive=True) w = symbols('w', real=True) s = symbols('s') F, _, _ = laplace_transform(cos((w - 1)*t), t, s) assert F == s/(s**2 + (w - 1)**2) def test_Y2(): t = symbols('t', real=True, positive=True) w = symbols('w', real=True) s = symbols('s') f = inverse_laplace_transform(s/(s**2 + (w - 1)**2), s, t) assert f == cos(t*abs(w - 1)) @XFAIL def test_Y3(): t = symbols('t', real=True, positive=True) w = symbols('w', real=True) s = symbols('s') F, _, _ = laplace_transform(sinh(w*t)*cosh(w*t), t, s) assert F == w/(s**2 - 4*w**2) def test_Y4(): t = symbols('t', real=True, positive=True) s = symbols('s') F, _, _ = laplace_transform(erf(3/sqrt(t)), t, s) assert F == (1 - exp(-6*sqrt(s)))/s @XFAIL def test_Y5_Y6(): # Solve y'' + y = 4 [H(t - 1) - H(t - 2)], y(0) = 1, y'(0) = 0 where H is the # Heaviside (unit step) function (the RHS describes a pulse of magnitude 4 and # duration 1). See David A. Sanchez, Richard C. Allen, Jr. and Walter T. # Kyner, _Differential Equations: An Introduction_, Addison-Wesley Publishing # Company, 1983, p. 211. First, take the Laplace transform of the ODE # => s^2 Y(s) - s + Y(s) = 4/s [e^(-s) - e^(-2 s)] # where Y(s) is the Laplace transform of y(t) t = symbols('t', real=True, positive=True) s = symbols('s') y = Function('y') F, _, _ = laplace_transform(diff(y(t), t, 2) + y(t) - 4*(Heaviside(t - 1) - Heaviside(t - 2)), t, s) # Laplace transform for diff() not calculated # https://code.google.com/p/sympy/issues/detail?id=4077 assert (F == s**2*LaplaceTransform(y(t), t, s) - s + LaplaceTransform(y(t), t, s) - 4*exp(-s)/s + 4*exp(-2*s)/s) # TODO implement second part of test case # Now, solve for Y(s) and then take the inverse Laplace transform # => Y(s) = s/(s^2 + 1) + 4 [1/s - s/(s^2 + 1)] [e^(-s) - e^(-2 s)] # => y(t) = cos t + 4 {[1 - cos(t - 1)] H(t - 1) - [1 - cos(t - 2)] H(t - 2)} @XFAIL def test_Y7(): # What is the Laplace transform of an infinite square wave? # => 1/s + 2 sum( (-1)^n e^(- s n a)/s, n = 1..infinity ) # [Sanchez, Allen and Kyner, p. 213] t = symbols('t', real=True, positive=True) a = symbols('a', real=True) s = symbols('s') F, _, _ = laplace_transform(1 + 2*Sum((-1)**n*Heaviside(t - n*a), (n, 1, oo)), t, s) # returns 2*LaplaceTransform(Sum((-1)**n*Heaviside(-a*n + t), # (n, 1, oo)), t, s) + 1/s # https://code.google.com/p/sympy/issues/detail?id=4078 assert F == 2*Sum((-1)**n*exp(-a*n*s)/s, (n, 1, oo)) + 1/s @XFAIL def test_Y8(): assert fourier_transform(1, x, z) == DiracDelta(z) def test_Y9(): assert (fourier_transform(exp(-9*x**2), x, z) == sqrt(pi)*exp(-pi**2*z**2/9)/3) def test_Y10(): assert (fourier_transform(abs(x)*exp(-3*abs(x)), x, z) == (-8*pi**2*z**2 + 18)/(16*pi**4*z**4 + 72*pi**2*z**2 + 81)) @XFAIL @slow def test_Y11(): # => pi cot(pi s) (0 < Re s < 1) [Gradshteyn and Ryzhik 17.43(5)] x, s = symbols('x s') # raises RuntimeError: maximum recursion depth exceeded # https://code.google.com/p/sympy/issues/detail?id=4082 F, _, _ = mellin_transform(1/(1 - x), x, s) assert F == pi*cot(pi*s) @XFAIL def test_Y12(): # => 2^(s - 4) gamma(s/2)/gamma(4 - s/2) (0 < Re s < 1) # [Gradshteyn and Ryzhik 17.43(16)] x, s = symbols('x s') # returns Wrong value -2**(s - 4)*gamma(s/2 - 3)/gamma(-s/2 + 1) # https://code.google.com/p/sympy/issues/detail?id=4083 F, _, _ = mellin_transform(besselj(3, x)/x**3, x, s) assert F == -2**(s - 4)*gamma(s/2)/gamma(-s/2 + 4) @XFAIL def test_Y13(): # Z[H(t - m T)] => z/[z^m (z - 1)] (H is the Heaviside (unit step) function) z raise NotImplementedError("z-transform not supported") @XFAIL def test_Y14(): # Z[H(t - m T)] => z/[z^m (z - 1)] (H is the Heaviside (unit step) function) raise NotImplementedError("z-transform not supported") def test_Z1(): r = Function('r') assert (rsolve(r(n + 2) - 2*r(n + 1) + r(n) - 2, r(n), {r(0): 1, r(1): m}).simplify() == n**2 + n*(m - 2) + 1) def test_Z2(): r = Function('r') assert (rsolve(r(n) - (5*r(n - 1) - 6*r(n - 2)), r(n), {r(0): 0, r(1): 1}) == -2**n + 3**n) def test_Z3(): # => r(n) = Fibonacci[n + 1] [Cohen, p. 83] r = Function('r') # recurrence solution is correct, Wester expects it to be simplified to # fibonacci(n+1), but that is quite hard assert (rsolve(r(n) - (r(n - 1) + r(n - 2)), r(n), {r(1): 1, r(2): 2}).simplify() == 2**(-n)*((1 + sqrt(5))**n*(sqrt(5) + 5) + (-sqrt(5) + 1)**n*(-sqrt(5) + 5))/10) @XFAIL def test_Z4(): # => [c^(n+1) [c^(n+1) - 2 c - 2] + (n+1) c^2 + 2 c - n] / [(c-1)^3 (c+1)] # [Joan Z. Yu and Robert Israel in sci.math.symbolic] r = Function('r') c = symbols('c') # raises ValueError: Polynomial or rational function expected, # got '(c**2 - c**n)/(c - c**n) s = rsolve(r(n) - ((1 + c - c**(n-1) - c**(n+1))/(1 - c**n)*r(n - 1) - c*(1 - c**(n-2))/(1 - c**(n-1))*r(n - 2) + 1), r(n), {r(1): 1, r(2): (2 + 2*c + c**2)/(1 + c)}) assert (s - (c*(n + 1)*(c*(n + 1) - 2*c - 2) + (n + 1)*c**2 + 2*c - n)/((c-1)**3*(c+1)) == 0) @XFAIL def test_Z5(): # Second order ODE with initial conditions---solve directly # transform: f(t) = sin(2 t)/8 - t cos(2 t)/4 C1, C2 = symbols('C1 C2') # initial conditions not supported, this is a manual workaround # https://code.google.com/p/sympy/issues/detail?id=1621 eq = Derivative(f(x), x, 2) + 4*f(x) - sin(2*x) sol = dsolve(eq, f(x)) f0 = Lambda(x, sol.rhs) assert f0(x) == C2*sin(2*x) + (C1 - x/4)*cos(2*x) f1 = Lambda(x, diff(f0(x), x)) const_dict = solve((f0(0), f1(0))) result = f0(x).subs(C1, const_dict[C1]).subs(C2, const_dict[C2]) assert result == -x*cos(2*x)/4 + sin(2*x)/8 # Result is OK, but ODE solving with initial conditions should be # supported without all this manual work raise NotImplementedError('ODE solving with initial conditions \ not supported') @XFAIL def test_Z6(): # Second order ODE with initial conditions---solve using Laplace # transform: f(t) = sin(2 t)/8 - t cos(2 t)/4 t = symbols('t', real=True, positive=True) s = symbols('s') eq = Derivative(f(t), t, 2) + 4*f(t) - sin(2*t) F, _, _ = laplace_transform(eq, t, s) # Laplace transform for diff() not calculated # https://code.google.com/p/sympy/issues/detail?id=4077 assert (F == s**2*LaplaceTransform(f(t), t, s) + 4*LaplaceTransform(f(t), t, s) - 2/(s**2 + 4)) # rest of test case not implemented sympy-0.7.4.1/sympy/core/tests/test_match.py0000644000175000017500000004046112253362407021231 0ustar georgeskgeorgeskfrom sympy import (abc, Add, cos, Derivative, diff, exp, Float, Function, I, Integer, log, Mul, oo, Poly, Rational, S, sin, sqrt, Symbol, symbols, Wild, pi ) from sympy.utilities.pytest import XFAIL def test_symbol(): x = Symbol('x') a, b, c, p, q = map(Wild, 'abcpq') e = x assert e.match(x) == {} assert e.matches(x) == {} assert e.match(a) == {a: x} e = Rational(5) assert e.match(c) == {c: 5} assert e.match(e) == {} assert e.match(e + 1) is None def test_add(): x, y, a, b, c = map(Symbol, 'xyabc') p, q, r = map(Wild, 'pqr') e = a + b assert e.match(p + b) == {p: a} assert e.match(p + a) == {p: b} e = 1 + b assert e.match(p + b) == {p: 1} e = a + b + c assert e.match(a + p + c) == {p: b} assert e.match(b + p + c) == {p: a} e = a + b + c + x assert e.match(a + p + x + c) == {p: b} assert e.match(b + p + c + x) == {p: a} assert e.match(b) is None assert e.match(b + p) == {p: a + c + x} assert e.match(a + p + c) == {p: b + x} assert e.match(b + p + c) == {p: a + x} e = 4*x + 5 assert e.match(4*x + p) == {p: 5} assert e.match(3*x + p) == {p: x + 5} assert e.match(p*x + 5) == {p: 4} def test_power(): x, y, a, b, c = map(Symbol, 'xyabc') p, q, r = map(Wild, 'pqr') e = (x + y)**a assert e.match(p**q) == {p: x + y, q: a} assert e.match(p**p) is None e = (x + y)**(x + y) assert e.match(p**p) == {p: x + y} assert e.match(p**q) == {p: x + y, q: x + y} e = (2*x)**2 assert e.match(p*q**r) == {p: 4, q: x, r: 2} e = Integer(1) assert e.match(x**p) == {p: 0} def test_match_exclude(): x = Symbol('x') y = Symbol('y') p = Wild("p", exclude=[x, y]) q = Wild("q", exclude=[x, y]) r = Wild("r", exclude=[x, y]) e = 3/(4*x + 5) assert e.match(3/(p*x + q)) == {p: 4, q: 5} e = 3/(4*x + 5) assert e.match(p/(q*x + r)) == {p: 3, q: 4, r: 5} e = 2/(x + 1) assert e.match(p/(q*x + r)) == {p: 2, q: 1, r: 1} e = 1/(x + 1) assert e.match(p/(q*x + r)) == {p: 1, q: 1, r: 1} e = 4*x + 5 assert e.match(p*x + q) == {p: 4, q: 5} e = 4*x + 5*y + 6 assert e.match(p*x + q*y + r) == {p: 4, q: 5, r: 6} def test_mul(): x, y, a, b, c = map(Symbol, 'xyabc') p, q = map(Wild, 'pq') e = 4*x assert e.match(p*x) == {p: 4} assert e.match(p*y) is None assert e.match(e + p*y) == {p: 0} e = a*x*b*c assert e.match(p*x) == {p: a*b*c} assert e.match(c*p*x) == {p: a*b} e = (a + b)*(a + c) assert e.match((p + b)*(p + c)) == {p: a} e = x assert e.match(p*x) == {p: 1} e = exp(x) assert e.match(x**p*exp(x*q)) == {p: 0, q: 1} e = I*Poly(x, x) assert e.match(I*p) == {p: Poly(x, x)} def test_mul_noncommutative(): x, y = symbols('x y') A, B = symbols('A B', commutative=False) u, v = symbols('u v', cls=Wild) w = Wild('w', commutative=False) assert (u*v).matches(x) in ({v: x, u: 1}, {u: x, v: 1}) assert (u*v).matches(x*y) in ({v: y, u: x}, {u: y, v: x}) assert (u*v).matches(A) is None assert (u*v).matches(A*B) is None assert (u*v).matches(x*A) is None assert (u*v).matches(x*y*A) is None assert (u*v).matches(x*A*B) is None assert (u*v).matches(x*y*A*B) is None assert (v*w).matches(x) is None assert (v*w).matches(x*y) is None assert (v*w).matches(A) == {w: A, v: 1} assert (v*w).matches(A*B) == {w: A*B, v: 1} assert (v*w).matches(x*A) == {w: A, v: x} assert (v*w).matches(x*y*A) == {w: A, v: x*y} assert (v*w).matches(x*A*B) == {w: A*B, v: x} assert (v*w).matches(x*y*A*B) == {w: A*B, v: x*y} assert (v*w).matches(-x) is None assert (v*w).matches(-x*y) is None assert (v*w).matches(-A) == {w: A, v: -1} assert (v*w).matches(-A*B) == {w: A*B, v: -1} assert (v*w).matches(-x*A) == {w: A, v: -x} assert (v*w).matches(-x*y*A) == {w: A, v: -x*y} assert (v*w).matches(-x*A*B) == {w: A*B, v: -x} assert (v*w).matches(-x*y*A*B) == {w: A*B, v: -x*y} def test_complex(): a, b, c = map(Symbol, 'abc') x, y = map(Wild, 'xy') assert (1 + I).match(x + I) == {x: 1} assert (a + I).match(x + I) == {x: a} assert (2*I).match(x*I) == {x: 2} assert (a*I).match(x*I) == {x: a} assert (a*I).match(x*y) == {x: I, y: a} assert (2*I).match(x*y) == {x: 2, y: I} #Result is ambiguous, so we need to use Wild's exclude keyword x = Wild('x', exclude=[I]) y = Wild('y', exclude=[I]) assert (a + b*I).match(x + y*I) == {x: a, y: b} def test_functions(): from sympy.core.function import WildFunction x = Symbol('x') g = WildFunction('g') p = Wild('p') q = Wild('q') f = cos(5*x) notf = x assert f.match(p*cos(q*x)) == {p: 1, q: 5} assert f.match(p*g) == {p: 1, g: cos(5*x)} assert notf.match(g) is None @XFAIL def test_functions_X1(): from sympy.core.function import WildFunction x = Symbol('x') g = WildFunction('g') p = Wild('p') q = Wild('q') f = cos(5*x) assert f.match(p*g(q*x)) == {p: 1, g: cos, q: 5} def test_interface(): x, y = map(Symbol, 'xy') p, q = map(Wild, 'pq') assert (x + 1).match(p + 1) == {p: x} assert (x*3).match(p*3) == {p: x} assert (x**3).match(p**3) == {p: x} assert (x*cos(y)).match(p*cos(q)) == {p: x, q: y} assert (x*y).match(p*q) in [{p:x, q:y}, {p:y, q:x}] assert (x + y).match(p + q) in [{p:x, q:y}, {p:y, q:x}] assert (x*y + 1).match(p*q) in [{p:1, q:1 + x*y}, {p:1 + x*y, q:1}] def test_derivative1(): x, y = map(Symbol, 'xy') p, q = map(Wild, 'pq') f = Function('f', nargs=1) fd = Derivative(f(x), x) assert fd.match(p) == {p: fd} assert (fd + 1).match(p + 1) == {p: fd} assert (fd).match(fd) == {} assert (3*fd).match(p*fd) is not None p = Wild("p", exclude=[x]) q = Wild("q", exclude=[x]) assert (3*fd - 1).match(p*fd + q) == {p: 3, q: -1} def test_derivative_bug1(): f = Function("f") x = Symbol("x") a = Wild("a", exclude=[f, x]) b = Wild("b", exclude=[f]) pattern = a * Derivative(f(x), x, x) + b expr = Derivative(f(x), x) + x**2 d1 = {b: x**2} d2 = pattern.xreplace(d1).matches(expr, d1) assert d2 is None def test_derivative2(): f = Function("f") x = Symbol("x") a = Wild("a", exclude=[f, x]) b = Wild("b", exclude=[f]) e = Derivative(f(x), x) assert e.match(Derivative(f(x), x)) == {} assert e.match(Derivative(f(x), x, x)) is None e = Derivative(f(x), x, x) assert e.match(Derivative(f(x), x)) is None assert e.match(Derivative(f(x), x, x)) == {} e = Derivative(f(x), x) + x**2 assert e.match(a*Derivative(f(x), x) + b) == {a: 1, b: x**2} assert e.match(a*Derivative(f(x), x, x) + b) is None e = Derivative(f(x), x, x) + x**2 assert e.match(a*Derivative(f(x), x) + b) is None assert e.match(a*Derivative(f(x), x, x) + b) == {a: 1, b: x**2} def test_match_deriv_bug1(): n = Function('n') l = Function('l') x = Symbol('x') p = Wild('p') e = diff(l(x), x)/x - diff(diff(n(x), x), x)/2 - \ diff(n(x), x)**2/4 + diff(n(x), x)*diff(l(x), x)/4 e = e.subs(n(x), -l(x)).doit() t = x*exp(-l(x)) t2 = t.diff(x, x)/t assert e.match( (p*t2).expand() ) == {p: -Rational(1)/2} def test_match_bug2(): x, y = map(Symbol, 'xy') p, q, r = map(Wild, 'pqr') res = (x + y).match(p + q + r) assert (p + q + r).subs(res) == x + y def test_match_bug3(): x, a, b = map(Symbol, 'xab') p = Wild('p') assert (b*x*exp(a*x)).match(x*exp(p*x)) is None def test_match_bug4(): x = Symbol('x') p = Wild('p') e = x assert e.match(-p*x) == {p: -1} def test_match_bug5(): x = Symbol('x') p = Wild('p') e = -x assert e.match(-p*x) == {p: 1} def test_match_bug6(): x = Symbol('x') p = Wild('p') e = x assert e.match(3*p*x) == {p: Rational(1)/3} def test_behavior1(): x = Symbol('x') p = Wild('p') e = 3*x**2 a = Wild('a', exclude=[x]) assert e.match(a*x) is None assert e.match(p*x) == {p: 3*x} def test_behavior2(): x = Symbol('x') p = Wild('p') e = Rational(6) assert e.match(2*p) == {p: 3} e = 3*x + 3 + 6/x a = Wild('a', exclude=[x]) assert e.expand().match(a*x**2 + a*x + 2*a) is None assert e.expand().match(p*x**2 + p*x + 2*p) == {p: 3/x} def test_match_polynomial(): x = Symbol('x') a = Wild('a', exclude=[x]) b = Wild('b', exclude=[x]) c = Wild('c', exclude=[x]) d = Wild('d', exclude=[x]) eq = 4*x**3 + 3*x**2 + 2*x + 1 pattern = a*x**3 + b*x**2 + c*x + d assert eq.match(pattern) == {a: 4, b: 3, c: 2, d: 1} assert (eq - 3*x**2).match(pattern) == {a: 4, b: 0, c: 2, d: 1} assert (x + sqrt(2) + 3).match(a + b*x + c*x**2) == \ {b: 1, a: sqrt(2) + 3, c: 0} def test_exclude(): x, y, a = map(Symbol, 'xya') p = Wild('p', exclude=[1, x]) q = Wild('q', exclude=[x]) r = Wild('r', exclude=[sin, y]) assert sin(x).match(r) is None assert cos(y).match(r) is None e = 3*x**2 + y*x + a assert e.match(p*x**2 + q*x + r) == {p: 3, q: y, r: a} e = x + 1 assert e.match(x + p) is None assert e.match(p + 1) is None assert e.match(x + 1 + p) == {p: 0} e = cos(x) + 5*sin(y) assert e.match(r) is None assert e.match(cos(y) + r) is None assert e.match(r + p*sin(q)) == {r: cos(x), p: 5, q: y} def test_floats(): a, b = map(Wild, 'ab') e = cos(0.12345, evaluate=False)**2 r = e.match(a*cos(b)**2) assert r == {a: 1, b: Float(0.12345)} def test_Derivative_bug1(): f = Function("f") x = abc.x a = Wild("a", exclude=[f(x)]) b = Wild("b", exclude=[f(x)]) eq = f(x).diff(x) assert eq.match(a*Derivative(f(x), x) + b) == {a: 1, b: 0} def test_match_wild_wild(): p = Wild('p') q = Wild('q') r = Wild('r') assert p.match(q + r) in [ {q: p, r: 0}, {q: 0, r: p} ] assert p.match(q*r) in [ {q: p, r: 1}, {q: 1, r: p} ] p = Wild('p') q = Wild('q', exclude=[p]) r = Wild('r') assert p.match(q + r) == {q: 0, r: p} assert p.match(q*r) == {q: 1, r: p} p = Wild('p') q = Wild('q', exclude=[p]) r = Wild('r', exclude=[p]) assert p.match(q + r) is None assert p.match(q*r) is None def test_combine_inverse(): x, y = symbols("x y") assert Mul._combine_inverse(x*I*y, x*I) == y assert Mul._combine_inverse(x*I*y, y*I) == x assert Mul._combine_inverse(oo*I*y, y*I) == oo assert Mul._combine_inverse(oo*I*y, oo*I) == y assert Add._combine_inverse(oo, oo) == S(0) assert Add._combine_inverse(oo*I, oo*I) == S(0) def test_issue_674(): x = symbols('x') z, phi, r = symbols('z phi r') c, A, B, N = symbols('c A B N', cls=Wild) l = Wild('l', exclude=(0,)) eq = z * sin(2*phi) * r**7 matcher = c * sin(phi*N)**l * r**A * log(r)**B assert eq.match(matcher) == {c: z, l: 1, N: 2, A: 7, B: 0} assert (-eq).match(matcher) == {c: -z, l: 1, N: 2, A: 7, B: 0} assert (x*eq).match(matcher) == {c: x*z, l: 1, N: 2, A: 7, B: 0} assert (-7*x*eq).match(matcher) == {c: -7*x*z, l: 1, N: 2, A: 7, B: 0} matcher = c*sin(phi*N)**l * r**A assert eq.match(matcher) == {c: z, l: 1, N: 2, A: 7} assert (-eq).match(matcher) == {c: -z, l: 1, N: 2, A: 7} assert (x*eq).match(matcher) == {c: x*z, l: 1, N: 2, A: 7} assert (-7*x*eq).match(matcher) == {c: -7*x*z, l: 1, N: 2, A: 7} def test_issue_784(): from sympy.abc import gamma, mu, x f = (-gamma * (x - mu)**2 - log(gamma) + log(2*pi))/2 a, b, c = symbols('a b c', cls=Wild, exclude=(gamma,)) assert f.match(a * log(gamma) + b * gamma + c) == \ {a: -S(1)/2, b: -(mu - x)**2/2, c: log(2*pi)/2} assert f.expand().collect(gamma).match(a * log(gamma) + b * gamma + c) == \ {a: -S(1)/2, b: (-(x - mu)**2/2).expand(), c: (log(2*pi)/2).expand()} def test_issue_1319(): x = Symbol('x') a, b, c = symbols('a b c', cls=Wild, exclude=(x,)) f, g = symbols('f g', cls=Function) eq = diff(g(x)*f(x).diff(x), x) assert eq.match( g(x).diff(x)*f(x).diff(x) + g(x)*f(x).diff(x, x) + c) == {c: 0} assert eq.match(a*g(x).diff( x)*f(x).diff(x) + b*g(x)*f(x).diff(x, x) + c) == {a: 1, b: 1, c: 0} def test_issue_1601(): f = Function('f') x = Symbol('x') a, b = symbols('a b', cls=Wild, exclude=(f(x),)) p = a*f(x) + b eq1 = sin(x) eq2 = f(x) + sin(x) eq3 = f(x) + x + sin(x) eq4 = x + sin(x) assert eq1.match(p) == {a: 0, b: sin(x)} assert eq2.match(p) == {a: 1, b: sin(x)} assert eq3.match(p) == {a: 1, b: x + sin(x)} assert eq4.match(p) == {a: 0, b: x + sin(x)} def test_issue_2069(): a, b, c = symbols('a b c', cls=Wild) x = Symbol('x') f = Function('f') assert x.match(a) == {a: x} assert x.match(a*f(x)**c) == {a: x, c: 0} assert x.match(a*b) == {a: 1, b: x} assert x.match(a*b*f(x)**c) == {a: 1, b: x, c: 0} assert (-x).match(a) == {a: -x} assert (-x).match(a*f(x)**c) == {a: -x, c: 0} assert (-x).match(a*b) == {a: -1, b: x} assert (-x).match(a*b*f(x)**c) == {a: -1, b: x, c: 0} assert (2*x).match(a) == {a: 2*x} assert (2*x).match(a*f(x)**c) == {a: 2*x, c: 0} assert (2*x).match(a*b) == {a: 2, b: x} assert (2*x).match(a*b*f(x)**c) == {a: 2, b: x, c: 0} assert (-2*x).match(a) == {a: -2*x} assert (-2*x).match(a*f(x)**c) == {a: -2*x, c: 0} assert (-2*x).match(a*b) == {a: -2, b: x} assert (-2*x).match(a*b*f(x)**c) == {a: -2, b: x, c: 0} def test_issue_1460(): x = Symbol('x') e = Symbol('e') w = Wild('w', exclude=[x]) y = Wild('y') # this is as it should be assert (3/x).match(w/y) == {w: 3, y: x} assert (3*x).match(w*y) == {w: 3, y: x} assert (x/3).match(y/w) == {w: 3, y: x} assert (3*x).match(y/w) == {w: S(1)/3, y: x} # these could be allowed to fail assert (x/3).match(w/y) == {w: S(1)/3, y: 1/x} assert (3*x).match(w/y) == {w: 3, y: 1/x} assert (3/x).match(w*y) == {w: 3, y: 1/x} # Note that solve will give # multiple roots but match only gives one: # # >>> solve(x**r-y**2,y) # [-x**(r/2), x**(r/2)] r = Symbol('r', rational=True) assert (x**r).match(y**2) == {y: x**(r/2)} assert (x**e).match(y**2) == {y: sqrt(x**e)} # since (x**i = y) -> x = y**(1/i) where i is an integer # the following should also be valid as long as y is not # zero when i is negative. a = Wild('a') e = S(0) assert e.match(a) == {a: e} assert e.match(1/a) is None assert e.match(a**.3) is None e = S(3) assert e.match(1/a) == {a: 1/e} assert e.match(1/a**2) == {a: 1/sqrt(e)} e = pi assert e.match(1/a) == {a: 1/e} assert e.match(1/a**2) == {a: 1/sqrt(e)} assert (-e).match(sqrt(a)) is None assert (-e).match(a**2) == {a: I*sqrt(pi)} def test_issue_1784(): a = Wild('a') x = Symbol('x') e = [i**2 for i in (x - 2, 2 - x)] p = [i**2 for i in (x - a, a- x)] for eq in e: for pat in p: assert eq.match(pat) == {a: 2} def test_issue_1220(): x, y = symbols('x y') p = -x*(S(1)/8 - y) ans = set([S.Zero, y - S(1)/8]) def ok(pat): assert set(p.match(pat).values()) == ans ok(Wild("coeff", exclude=[x])*x + Wild("rest")) ok(Wild("w", exclude=[x])*x + Wild("rest")) ok(Wild("coeff", exclude=[x])*x + Wild("rest")) ok(Wild("w", exclude=[x])*x + Wild("rest")) ok(Wild("e", exclude=[x])*x + Wild("rest")) ok(Wild("ress", exclude=[x])*x + Wild("rest")) ok(Wild("resu", exclude=[x])*x + Wild("rest")) def test_issue_679(): p, c, q = symbols('p c q', cls=Wild) x = Symbol('x') assert (sin(x)**2).match(sin(p)*sin(q)*c) == {q: x, c: 1, p: x} assert (2*sin(x)).match(sin(p) + sin(q) + c) == {q: x, c: 0, p: x} def test_issue_784(): mu, gamma, x = symbols('mu gamma x') f = (- gamma * (x-mu)**2 - log(gamma) + log(2*pi)) / 2 g1 = Wild('g1', exclude=[gamma]) g2 = Wild('g2', exclude=[gamma]) g3 = Wild('g3', exclude=[gamma]) assert f.expand().match(g1 * log(gamma) + g2 * gamma + g3) == \ {g3: log(2)/2 + log(pi)/2, g1: -S(1)/2, g2: -mu**2/2 + mu*x - x**2/2} def test_issue_3004(): x = Symbol('x') a = Wild('a') assert (-I*x*oo).match(I*a*oo) == {a: -x} def test_issue_3539(): a = Wild('a') x = Symbol('x') assert (x - 2).match(a - x) is None assert (6/x).match(a*x) is None assert (6/x**2).match(a/x) == {a: 6/x} sympy-0.7.4.1/sympy/core/tests/test_exprtools.py0000644000175000017500000003203512253362407022172 0ustar georgeskgeorgesk"""Tests for tools for manipulating of large commutative expressions. """ from sympy import (S, Add, sin, Mul, Symbol, oo, Integral, sqrt, Tuple, I, Interval, O, symbols, simplify, collect, Sum, Basic, Dict, root, exp) from sympy.abc import a, b, t, x, y, z from sympy.core.exprtools import (decompose_power, Factors, Term, _gcd_terms, gcd_terms, factor_terms, factor_nc) from sympy.core.mul import _keep_coeff as _keep_coeff from sympy.simplify.cse_opts import sub_pre from sympy.utilities.pytest import raises def test_decompose_power(): assert decompose_power(x) == (x, 1) assert decompose_power(x**2) == (x, 2) assert decompose_power(x**(2*y)) == (x**y, 2) assert decompose_power(x**(2*y/3)) == (x**(y/3), 2) def test_Factors(): assert Factors() == Factors({}) == Factors(S(1)) assert Factors().as_expr() == S.One assert Factors({x: 2, y: 3, sin(x): 4}).as_expr() == x**2*y**3*sin(x)**4 assert Factors(S.Infinity) == Factors({oo: 1}) assert Factors(S.NegativeInfinity) == Factors({oo: 1, -1: 1}) a = Factors({x: 5, y: 3, z: 7}) b = Factors({ y: 4, z: 3, t: 10}) assert a.mul(b) == a*b == Factors({x: 5, y: 7, z: 10, t: 10}) assert a.div(b) == divmod(a, b) == \ (Factors({x: 5, z: 4}), Factors({y: 1, t: 10})) assert a.quo(b) == a/b == Factors({x: 5, z: 4}) assert a.rem(b) == a % b == Factors({y: 1, t: 10}) assert a.pow(3) == a**3 == Factors({x: 15, y: 9, z: 21}) assert b.pow(3) == b**3 == Factors({y: 12, z: 9, t: 30}) assert a.gcd(b) == Factors({y: 3, z: 3}) assert a.lcm(b) == Factors({x: 5, y: 4, z: 7, t: 10}) a = Factors({x: 4, y: 7, t: 7}) b = Factors({z: 1, t: 3}) assert a.normal(b) == (Factors({x: 4, y: 7, t: 4}), Factors({z: 1})) assert Factors(sqrt(2)*x).as_expr() == sqrt(2)*x assert Factors(-I)*I == Factors() assert Factors({S(-1): S(3)})*Factors({S(-1): S(1), I: S(5)}) == \ Factors(I) assert Factors(S(2)**x).div(S(3)**x) == \ (Factors({S(2): x}), Factors({S(3): x})) assert Factors(2**(2*x + 2)).div(S(8)) == \ (Factors({S(2): 2*x + 2}), Factors({S(8): S(1)})) # coverage # /!\ things break if this is not True assert Factors({S(-1): S(3)/2}) == Factors({I: S.One, S(-1): S.One}) assert Factors({I: S(1), S(-1): S(1)/3}).as_expr() == I*(-1)**(S(1)/3) assert Factors(-1.) == Factors({S(-1): S(1), S(1.): 1}) assert Factors(-2.) == Factors({S(-1): S(1), S(2.): 1}) assert Factors((-2.)**x) == Factors({S(-2.): x}) assert Factors(S(-2)) == Factors({S(-1): S(1), S(2): 1}) assert Factors(S.Half) == Factors({S(2): -S.One}) assert Factors(S(3)/2) == Factors({S(3): S.One, S(2): S(-1)}) assert Factors({I: S(1)}) == Factors(I) assert Factors({-1.0: 2, I: 1}) == Factors({S(1.0): 1, I: 1}) assert Factors({S.NegativeOne: -S(3)/2}).as_expr() == I A = symbols('A', commutative=False) assert Factors(2*A**2) == Factors({S(2): 1, A**2: 1}) assert Factors(I) == Factors({I: S.One}) assert Factors(x).normal(S(2)) == (Factors(x), Factors(S(2))) assert Factors(x).normal(S(0)) == (Factors(), Factors(S(0))) raises(ZeroDivisionError, lambda: Factors(x).div(S(0))) assert Factors(x).mul(S(2)) == Factors(2*x) assert Factors(x).mul(S(0)).is_zero assert Factors(x).mul(1/x).is_one assert Factors(x**sqrt(2)**3).as_expr() == x**(2*sqrt(2)) assert Factors(x)**Factors(S(2)) == Factors(x**2) assert Factors(x).gcd(S(0)) == Factors(x) assert Factors(x).lcm(S(0)).is_zero assert Factors(S(0)).div(x) == (Factors(S(0)), Factors()) assert Factors(x).div(x) == (Factors(), Factors()) assert Factors({x: .2})/Factors({x: .2}) == Factors() assert Factors(x) != Factors() assert Factors(S(0)).normal(x) == (Factors(S(0)), Factors()) n, d = x**(2 + y), x**2 f = Factors(n) assert f.div(d) == f.normal(d) == (Factors(x**y), Factors()) assert f.gcd(d) == Factors() d = x**y assert f.div(d) == f.normal(d) == (Factors(x**2), Factors()) assert f.gcd(d) == Factors(d) n = d = 2**x f = Factors(n) assert f.div(d) == f.normal(d) == (Factors(), Factors()) assert f.gcd(d) == Factors(d) n, d = 2**x, 2**y f = Factors(n) assert f.div(d) == f.normal(d) == (Factors({S(2): x}), Factors({S(2): y})) assert f.gcd(d) == Factors() # extraction of constant only n = x**(x + 3) assert Factors(n).normal(x**-3) == (Factors({x: x + 6}), Factors({})) assert Factors(n).normal(x**3) == (Factors({x: x}), Factors({})) assert Factors(n).normal(x**4) == (Factors({x: x}), Factors({x: 1})) assert Factors(n).normal(x**(y - 3)) == \ (Factors({x: x + 6}), Factors({x: y})) assert Factors(n).normal(x**(y + 3)) == (Factors({x: x}), Factors({x: y})) assert Factors(n).normal(x**(y + 4)) == \ (Factors({x: x}), Factors({x: y + 1})) assert Factors(n).div(x**-3) == (Factors({x: x + 6}), Factors({})) assert Factors(n).div(x**3) == (Factors({x: x}), Factors({})) assert Factors(n).div(x**4) == (Factors({x: x}), Factors({x: 1})) assert Factors(n).div(x**(y - 3)) == \ (Factors({x: x + 6}), Factors({x: y})) assert Factors(n).div(x**(y + 3)) == (Factors({x: x}), Factors({x: y})) assert Factors(n).div(x**(y + 4)) == \ (Factors({x: x}), Factors({x: y + 1})) def test_Term(): a = Term(4*x*y**2/z/t**3) b = Term(2*x**3*y**5/t**3) assert a == Term(4, Factors({x: 1, y: 2}), Factors({z: 1, t: 3})) assert b == Term(2, Factors({x: 3, y: 5}), Factors({t: 3})) assert a.as_expr() == 4*x*y**2/z/t**3 assert b.as_expr() == 2*x**3*y**5/t**3 assert a.inv() == \ Term(S(1)/4, Factors({z: 1, t: 3}), Factors({x: 1, y: 2})) assert b.inv() == Term(S(1)/2, Factors({t: 3}), Factors({x: 3, y: 5})) assert a.mul(b) == a*b == \ Term(8, Factors({x: 4, y: 7}), Factors({z: 1, t: 6})) assert a.quo(b) == a/b == Term(2, Factors({}), Factors({x: 2, y: 3, z: 1})) assert a.pow(3) == a**3 == \ Term(64, Factors({x: 3, y: 6}), Factors({z: 3, t: 9})) assert b.pow(3) == b**3 == Term(8, Factors({x: 9, y: 15}), Factors({t: 9})) assert a.pow(-3) == a**(-3) == \ Term(S(1)/64, Factors({z: 3, t: 9}), Factors({x: 3, y: 6})) assert b.pow(-3) == b**(-3) == \ Term(S(1)/8, Factors({t: 9}), Factors({x: 9, y: 15})) assert a.gcd(b) == Term(2, Factors({x: 1, y: 2}), Factors({t: 3})) assert a.lcm(b) == Term(4, Factors({x: 3, y: 5}), Factors({z: 1, t: 3})) a = Term(4*x*y**2/z/t**3) b = Term(2*x**3*y**5*t**7) assert a.mul(b) == Term(8, Factors({x: 4, y: 7, t: 4}), Factors({z: 1})) assert Term((2*x + 2)**3) == Term(8, Factors({x + 1: 3}), Factors({})) assert Term((2*x + 2)*(3*x + 6)**2) == \ Term(18, Factors({x + 1: 1, x + 2: 2}), Factors({})) def test_gcd_terms(): f = 2*(x + 1)*(x + 4)/(5*x**2 + 5) + (2*x + 2)*(x + 5)/(x**2 + 1)/5 + \ (2*x + 2)*(x + 6)/(5*x**2 + 5) assert _gcd_terms(f) == ((S(6)/5)*((1 + x)/(1 + x**2)), 5 + x, 1) assert _gcd_terms(Add.make_args(f)) == \ ((S(6)/5)*((1 + x)/(1 + x**2)), 5 + x, 1) newf = (S(6)/5)*((1 + x)*(5 + x)/(1 + x**2)) assert gcd_terms(f) == newf args = Add.make_args(f) # non-Basic sequences of terms treated as terms of Add assert gcd_terms(list(args)) == newf assert gcd_terms(tuple(args)) == newf assert gcd_terms(set(args)) == newf # but a Basic sequence is treated as a container assert gcd_terms(Tuple(*args)) != newf assert gcd_terms(Basic(Tuple(1, 3*y + 3*x*y), Tuple(1, 3))) == \ Basic((1, 3*y*(x + 1)), (1, 3)) # but we shouldn't change keys of a dictionary or some may be lost assert gcd_terms(Dict((x*(1 + y), 2), (x + x*y, y + x*y))) == \ Dict({x*(y + 1): 2, x + x*y: y*(1 + x)}) assert gcd_terms((2*x + 2)**3 + (2*x + 2)**2) == 4*(x + 1)**2*(2*x + 3) assert gcd_terms(0) == 0 assert gcd_terms(1) == 1 assert gcd_terms(x) == x assert gcd_terms(2 + 2*x) == Mul(2, 1 + x, evaluate=False) arg = x*(2*x + 4*y) garg = 2*x*(x + 2*y) assert gcd_terms(arg) == garg assert gcd_terms(sin(arg)) == sin(garg) # issue 3040-like alpha, alpha1, alpha2, alpha3 = symbols('alpha:4') a = alpha**2 - alpha*x**2 + alpha + x**3 - x*(alpha + 1) rep = (alpha, (1 + sqrt(5))/2 + alpha1*x + alpha2*x**2 + alpha3*x**3) s = (a/(x - alpha)).subs(*rep).series(x, 0, 1) assert simplify(collect(s, x)) == -sqrt(5)/2 - S(3)/2 + O(x) # issue 2818 assert _gcd_terms([S.Zero, S.Zero]) == (0, 0, 1) assert _gcd_terms([2*x + 4]) == (2, x + 2, 1) eq = x/(x + 1/x) assert gcd_terms(eq, fraction=False) == eq def test_factor_terms(): A = Symbol('A', commutative=False) assert factor_terms(9*(x + x*y + 1) + (3*x + 3)**(2 + 2*x)) == \ 9*x*y + 9*x + _keep_coeff(S(3), x + 1)**_keep_coeff(S(2), x + 1) + 9 assert factor_terms(9*(x + x*y + 1) + (3)**(2 + 2*x)) == \ _keep_coeff(S(9), 3**(2*x) + x*y + x + 1) assert factor_terms(3**(2 + 2*x) + a*3**(2 + 2*x)) == \ 9*3**(2*x)*(a + 1) assert factor_terms(x + x*A) == \ x*(1 + A) assert factor_terms(sin(x + x*A)) == \ sin(x*(1 + A)) assert factor_terms((3*x + 3)**((2 + 2*x)/3)) == \ _keep_coeff(S(3), x + 1)**_keep_coeff(S(2)/3, x + 1) assert factor_terms(x + (x*y + x)**(3*x + 3)) == \ x + (x*(y + 1))**_keep_coeff(S(3), x + 1) assert factor_terms(a*(x + x*y) + b*(x*2 + y*x*2)) == \ x*(a + 2*b)*(y + 1) i = Integral(x, (x, 0, oo)) assert factor_terms(i) == i # check radical extraction eq = sqrt(2) + sqrt(10) assert factor_terms(eq) == eq assert factor_terms(eq, radical=True) == sqrt(2)*(1 + sqrt(5)) eq = root(-6, 3) + root(6, 3) assert factor_terms(eq, radical=True) == 6**(S(1)/3)*(1 + (-1)**(S(1)/3)) eq = [x + x*y] ans = [x*(y + 1)] for c in [list, tuple, set]: assert factor_terms(c(eq)) == c(ans) assert factor_terms(Tuple(x + x*y)) == Tuple(x*(y + 1)) assert factor_terms(Interval(0, 1)) == Interval(0, 1) e = 1/sqrt(a/2 + 1) assert factor_terms(e, clear=False) == 1/sqrt(a/2 + 1) assert factor_terms(e, clear=True) == sqrt(2)/sqrt(a + 2) eq = x/(x + 1/x) + 1/(x**2 + 1) assert factor_terms(eq, fraction=False) == eq assert factor_terms(eq, fraction=True) == 1 assert factor_terms((1/(x**3 + x**2) + 2/x**2)*y) == \ y*(2 + 1/(x + 1))/x**2 # if not True, then processesing for this in factor_terms is not necessary assert gcd_terms(-x - y) == -x - y assert factor_terms(-x - y) == Mul(-1, x + y, evaluate=False) # if not True, then "special" processesing in factor_terms is not necessary assert gcd_terms(exp(Mul(-1, x + 1))) == exp(-x - 1) e = exp(-x - 2) + x assert factor_terms(e) == exp(Mul(-1, x + 2, evaluate=False)) + x assert factor_terms(e, sign=False) == e assert factor_terms(exp(-4*x - 2) - x) == -x + exp(Mul(-2, 2*x + 1, evaluate=False)) def test_xreplace(): e = Mul(2, 1 + x, evaluate=False) assert e.xreplace({}) == e assert e.xreplace({y: x}) == e def test_factor_nc(): x, y = symbols('x,y') k = symbols('k', integer=True) n, m, o = symbols('n,m,o', commutative=False) # mul and multinomial expansion is needed from sympy.simplify.simplify import _mexpand e = x*(1 + y)**2 assert _mexpand(e) == x + x*2*y + x*y**2 def factor_nc_test(e): ex = _mexpand(e) assert ex.is_Add f = factor_nc(ex) assert not f.is_Add and _mexpand(f) == ex factor_nc_test(x*(1 + y)) factor_nc_test(n*(x + 1)) factor_nc_test(n*(x + m)) factor_nc_test((x + m)*n) factor_nc_test(n*m*(x*o + n*o*m)*n) s = Sum(x, (x, 1, 2)) factor_nc_test(x*(1 + s)) factor_nc_test(x*(1 + s)*s) factor_nc_test(x*(1 + sin(s))) factor_nc_test((1 + n)**2) factor_nc_test((x + n)*(x + m)*(x + y)) factor_nc_test(x*(n*m + 1)) factor_nc_test(x*(n*m + x)) factor_nc_test(x*(x*n*m + 1)) factor_nc_test(x*n*(x*m + 1)) factor_nc_test(x*(m*n + x*n*m)) factor_nc_test(n*(1 - m)*n**2) factor_nc_test((n + m)**2) factor_nc_test((n - m)*(n + m)**2) factor_nc_test((n + m)**2*(n - m)) factor_nc_test((m - n)*(n + m)**2*(n - m)) assert factor_nc(n*(n + n*m)) == n**2*(1 + m) assert factor_nc(m*(m*n + n*m*n**2)) == m*(m + n*m*n)*n eq = m*sin(n) - sin(n)*m assert factor_nc(eq) == eq # for coverage: from sympy.physics.secondquant import Commutator from sympy import factor eq = 1 + x*Commutator(m, n) assert factor_nc(eq) == eq eq = x*Commutator(m, n) + x*Commutator(m, o)*Commutator(m, n) assert factor(eq) == x*(1 + Commutator(m, o))*Commutator(m, n) # issue 3435 assert (2*n + 2*m).factor() == 2*(n + m) # issue 3602 assert factor_nc(n**k + n**(k + 1)) == n**k*(1 + n) assert factor_nc((m*n)**k + (m*n)**(k + 1)) == (1 + m*n)*(m*n)**k # issue 3819 assert factor_nc(-n*(2*x**2 + 2*x)) == -2*n*x*(x + 1) def test_issue_3261(): a, b = symbols("a b") apb = a + b eq = apb + apb**2*(-2*a - 2*b) assert factor_terms(sub_pre(eq)) == a + b - 2*(a + b)**3 sympy-0.7.4.1/sympy/core/tests/test_subs.py0000644000175000017500000004576512253362407021125 0ustar georgeskgeorgeskfrom sympy import (Symbol, Wild, sin, cos, exp, sqrt, pi, Function, Derivative, abc, Integer, Eq, symbols, Add, I, Float, log, Rational, Lambda, atan2, cse, cot, tan, S, Tuple, Basic, Dict, Piecewise, oo, Mul) from sympy.core.basic import _aresame from sympy.utilities.pytest import XFAIL from sympy.abc import x, y def test_subs(): n3 = Rational(3) e = x e = e.subs(x, n3) assert e == Rational(3) e = 2*x assert e == 2*x e = e.subs(x, n3) assert e == Rational(6) def test_trigonometric(): n3 = Rational(3) e = (sin(x)**2).diff(x) assert e == 2*sin(x)*cos(x) e = e.subs(x, n3) assert e == 2*cos(n3)*sin(n3) e = (sin(x)**2).diff(x) assert e == 2*sin(x)*cos(x) e = e.subs(sin(x), cos(x)) assert e == 2*cos(x)**2 assert exp(pi).subs(exp, sin) == 0 assert cos(exp(pi)).subs(exp, sin) == 1 i = Symbol('i', integer=True) zoo = S.ComplexInfinity assert tan(x).subs(x, pi/2) is zoo assert cot(x).subs(x, pi) is zoo assert cot(i*x).subs(x, pi) is zoo assert tan(i*x).subs(x, pi/2) == tan(i*pi/2) assert tan(i*x).subs(x, pi/2).subs(i, 1) is zoo o = Symbol('o', odd=True) assert tan(o*x).subs(x, pi/2) == tan(o*pi/2) def test_powers(): assert sqrt(1 - sqrt(x)).subs(x, 4) == I assert (sqrt(1 - x**2)**3).subs(x, 2) == - 3*I*sqrt(3) assert (x**Rational(1, 3)).subs(x, 27) == 3 assert (x**Rational(1, 3)).subs(x, -27) == 3*(-1)**Rational(1, 3) assert ((-x)**Rational(1, 3)).subs(x, 27) == 3*(-1)**Rational(1, 3) n = Symbol('n', negative=True) assert (x**n).subs(x, 0) is S.Infinity assert exp(-1).subs(S.Exp1, 0) is S.Infinity assert (x**(4.0*y)).subs(x**(2.0*y), n) == n**2.0 def test_logexppow(): # no eval() x = Symbol('x', real=True) w = Symbol('w') e = (3**(1 + x) + 2**(1 + x))/(3**x + 2**x) assert e.subs(2**x, w) != e assert e.subs(exp(x*log(Rational(2))), w) != e def test_bug(): x1 = Symbol('x1') x2 = Symbol('x2') y = x1*x2 assert y.subs(x1, Float(3.0)) == Float(3.0)*x2 def test_subbug1(): # see that they don't fail (x**x).subs(x, 1) (x**x).subs(x, 1.0) def test_subbug2(): # Ensure this does not cause infinite recursion assert Float(7.7).epsilon_eq(abs(x).subs(x, -7.7)) def test_dict_set(): a, b, c = map(Wild, 'abc') f = 3*cos(4*x) r = f.match(a*cos(b*x)) assert r == {a: 3, b: 4} e = a/b*sin(b*x) assert e.subs(r) == r[a]/r[b]*sin(r[b]*x) assert e.subs(r) == 3*sin(4*x) / 4 s = set(r.items()) assert e.subs(s) == r[a]/r[b]*sin(r[b]*x) assert e.subs(s) == 3*sin(4*x) / 4 assert e.subs(r) == r[a]/r[b]*sin(r[b]*x) assert e.subs(r) == 3*sin(4*x) / 4 assert x.subs(Dict((x, 1))) == 1 def test_dict_ambigous(): # see #467 y = Symbol('y') z = Symbol('z') f = x*exp(x) g = z*exp(z) df = {x: y, exp(x): y} dg = {z: y, exp(z): y} assert f.subs(df) == y**2 assert g.subs(dg) == y**2 # and this is how order can affect the result assert f.subs(x, y).subs(exp(x), y) == y*exp(y) assert f.subs(exp(x), y).subs(x, y) == y**2 # length of args and count_ops are the same so # default_sort_key resolves ordering...if one # doesn't want this result then an unordered # sequence should not be used. e = 1 + x*y assert e.subs({x: y, y: 2}) == 5 # here, there are no obviously clashing keys or values # but the results depend on the order assert exp(x/2 + y).subs(dict([(exp(y + 1), 2), (x, 2)])) == exp(y + 1) def test_deriv_sub_bug3(): y = Symbol('y') f = Function('f') pat = Derivative(f(x), x, x) assert pat.subs(y, y**2) == Derivative(f(x), x, x) assert pat.subs(y, y**2) != Derivative(f(x), x) def test_equality_subs1(): f = Function('f') x = abc.x eq = Eq(f(x)**2, x) res = Eq(Integer(16), x) assert eq.subs(f(x), 4) == res def test_equality_subs2(): f = Function('f') x = abc.x eq = Eq(f(x)**2, 16) assert bool(eq.subs(f(x), 3)) is False assert bool(eq.subs(f(x), 4)) is True def test_issue643(): y = Symbol('y') e = sqrt(x)*exp(y) assert e.subs(sqrt(x), 1) == exp(y) def test_subs_dict1(): x, y = symbols('x y') assert (1 + x*y).subs(x, pi) == 1 + pi*y assert (1 + x*y).subs({x: pi, y: 2}) == 1 + 2*pi c2, c3, q1p, q2p, c1, s1, s2, s3 = symbols('c2 c3 q1p q2p c1 s1 s2 s3') test = (c2**2*q2p*c3 + c1**2*s2**2*q2p*c3 + s1**2*s2**2*q2p*c3 - c1**2*q1p*c2*s3 - s1**2*q1p*c2*s3) assert (test.subs({c1**2: 1 - s1**2, c2**2: 1 - s2**2, c3**3: 1 - s3**2}) == c3*q2p*(1 - s2**2) + c3*q2p*s2**2*(1 - s1**2) - c2*q1p*s3*(1 - s1**2) + c3*q2p*s1**2*s2**2 - c2*q1p*s3*s1**2) def test_mul(): x, y, z, a, b, c = symbols('x y z a b c') A, B, C = symbols('A B C', commutative=0) assert (x*y*z).subs(z*x, y) == y**2 assert (z*x).subs(1/x, z) == z*x assert (x*y/z).subs(1/z, a) == a*x*y assert (x*y/z).subs(x/z, a) == a*y assert (x*y/z).subs(y/z, a) == a*x assert (x*y/z).subs(x/z, 1/a) == y/a assert (x*y/z).subs(x, 1/a) == y/(z*a) assert (2*x*y).subs(5*x*y, z) != 2*z/5 assert (x*y*A).subs(x*y, a) == a*A assert (x**2*y**(3*x/2)).subs(x*y**(x/2), 2) == 4*y**(x/2) assert (x*exp(x*2)).subs(x*exp(x), 2) == 2*exp(x) assert ((x**(2*y))**3).subs(x**y, 2) == 64 assert (x*A*B).subs(x*A, y) == y*B assert (x*y*(1 + x)*(1 + x*y)).subs(x*y, 2) == 6*(1 + x) assert ((1 + A*B)*A*B).subs(A*B, x*A*B) assert (x*a/z).subs(x/z, A) == a*A assert (x**3*A).subs(x**2*A, a) == a*x assert (x**2*A*B).subs(x**2*B, a) == a*A assert (x**2*A*B).subs(x**2*A, a) == a*B assert (b*A**3/(a**3*c**3)).subs(a**4*c**3*A**3/b**4, z) == \ b*A**3/(a**3*c**3) assert (6*x).subs(2*x, y) == 3*y assert (y*exp(3*x/2)).subs(y*exp(x), 2) == 2*exp(x/2) assert (y*exp(3*x/2)).subs(y*exp(x), 2) == 2*exp(x/2) assert (A**2*B*A**2*B*A**2).subs(A*B*A, C) == A*C**2*A assert (x*A**3).subs(x*A, y) == y*A**2 assert (x**2*A**3).subs(x*A, y) == y**2*A assert (x*A**3).subs(x*A, B) == B*A**2 assert (x*A*B*A*exp(x*A*B)).subs(x*A, B) == B**2*A*exp(B*B) assert (x**2*A*B*A*exp(x*A*B)).subs(x*A, B) == B**3*exp(B**2) assert (x**3*A*exp(x*A*B)*A*exp(x*A*B)).subs(x*A, B) == \ x*B*exp(B**2)*B*exp(B**2) assert (x*A*B*C*A*B).subs(x*A*B, C) == C**2*A*B assert (-I*a*b).subs(a*b, 2) == -2*I # issue 3262 assert (-8*I*a).subs(-2*a, 1) == 4*I assert (-I*a).subs(-a, 1) == I # issue 3342 assert (4*x**2).subs(2*x, y) == y**2 assert (2*4*x**2).subs(2*x, y) == 2*y**2 assert (-x**3/9).subs(-x/3, z) == -z**2*x assert (-x**3/9).subs(x/3, z) == -z**2*x assert (-2*x**3/9).subs(x/3, z) == -2*x*z**2 assert (-2*x**3/9).subs(-x/3, z) == -2*x*z**2 assert (-2*x**3/9).subs(-2*x, z) == z*x**2/9 assert (-2*x**3/9).subs(2*x, z) == -z*x**2/9 assert (2*(3*x/5/7)**2).subs(3*x/5, z) == 2*(S(1)/7)**2*z**2 assert (4*x).subs(-2*x, z) == 4*x # try keep subs literal def test_subs_simple(): a = symbols('a', commutative=True) x = symbols('x', commutative=False) assert (2*a).subs(1, 3) == 2*a assert (2*a).subs(2, 3) == 3*a assert (2*a).subs(a, 3) == 6 assert sin(2).subs(1, 3) == sin(2) assert sin(2).subs(2, 3) == sin(3) assert sin(a).subs(a, 3) == sin(3) assert (2*x).subs(1, 3) == 2*x assert (2*x).subs(2, 3) == 3*x assert (2*x).subs(x, 3) == 6 assert sin(x).subs(x, 3) == sin(3) def test_subs_constants(): a, b = symbols('a b', commutative=True) x, y = symbols('x y', commutative=False) assert (a*b).subs(2*a, 1) == a*b assert (1.5*a*b).subs(a, 1) == 1.5*b assert (2*a*b).subs(2*a, 1) == b assert (2*a*b).subs(4*a, 1) == 2*a*b assert (x*y).subs(2*x, 1) == x*y assert (1.5*x*y).subs(x, 1) == 1.5*y assert (2*x*y).subs(2*x, 1) == y assert (2*x*y).subs(4*x, 1) == 2*x*y def test_subs_commutative(): a, b, c, d, K = symbols('a b c d K', commutative=True) assert (a*b).subs(a*b, K) == K assert (a*b*a*b).subs(a*b, K) == K**2 assert (a*a*b*b).subs(a*b, K) == K**2 assert (a*b*c*d).subs(a*b*c, K) == d*K assert (a*b**c).subs(a, K) == K*b**c assert (a*b**c).subs(b, K) == a*K**c assert (a*b**c).subs(c, K) == a*b**K assert (a*b*c*b*a).subs(a*b, K) == c*K**2 assert (a**3*b**2*a).subs(a*b, K) == a**2*K**2 def test_subs_noncommutative(): w, x, y, z, L = symbols('w x y z L', commutative=False) assert (x*y).subs(x*y, L) == L assert (w*y*x).subs(x*y, L) == w*y*x assert (w*x*y*z).subs(x*y, L) == w*L*z assert (x*y*x*y).subs(x*y, L) == L**2 assert (x*x*y).subs(x*y, L) == x*L assert (x*x*y*y).subs(x*y, L) == x*L*y assert (w*x*y).subs(x*y*z, L) == w*x*y assert (x*y**z).subs(x, L) == L*y**z assert (x*y**z).subs(y, L) == x*L**z assert (x*y**z).subs(z, L) == x*y**L assert (w*x*y*z*x*y).subs(x*y*z, L) == w*L*x*y assert (w*x*y*y*w*x*x*y*x*y*y*x*y).subs(x*y, L) == w*L*y*w*x*L**2*y*L def test_subs_basic_funcs(): a, b, c, d, K = symbols('a b c d K', commutative=True) w, x, y, z, L = symbols('w x y z L', commutative=False) assert (x + y).subs(x + y, L) == L assert (x - y).subs(x - y, L) == L assert (x/y).subs(x, L) == L/y assert (x**y).subs(x, L) == L**y assert (x**y).subs(y, L) == x**L assert ((a - c)/b).subs(b, K) == (a - c)/K assert (exp(x*y - z)).subs(x*y, L) == exp(L - z) assert (a*exp(x*y - w*z) + b*exp(x*y + w*z)).subs(z, 0) == \ a*exp(x*y) + b*exp(x*y) assert ((a - b)/(c*d - a*b)).subs(c*d - a*b, K) == (a - b)/K assert (w*exp(a*b - c)*x*y/4).subs(x*y, L) == w*exp(a*b - c)*L/4 def test_subs_wild(): R, S, T, U = symbols('R S T U', cls=Wild) assert (R*S).subs(R*S, T) == T assert (S*R).subs(R*S, T) == T assert (R + S).subs(R + S, T) == T assert (R**S).subs(R, T) == T**S assert (R**S).subs(S, T) == R**T assert (R*S**T).subs(R, U) == U*S**T assert (R*S**T).subs(S, U) == R*U**T assert (R*S**T).subs(T, U) == R*S**U def test_subs_mixed(): a, b, c, d, K = symbols('a b c d K', commutative=True) w, x, y, z, L = symbols('w x y z L', commutative=False) R, S, T, U = symbols('R S T U', cls=Wild) assert (a*x*y).subs(x*y, L) == a*L assert (a*b*x*y*x).subs(x*y, L) == a*b*L*x assert (R*x*y*exp(x*y)).subs(x*y, L) == R*L*exp(L) assert (a*x*y*y*x - x*y*z*exp(a*b)).subs(x*y, L) == a*L*y*x - L*z*exp(a*b) e = c*y*x*y*x**(R*S - a*b) - T*(a*R*b*S) assert e.subs(x*y, L).subs(a*b, K).subs(R*S, U) == \ c*y*L*x**(U - K) - T*(U*K) def test_division(): a, b, c = symbols('a b c', commutative=True) x, y, z = symbols('x y z', commutative=True) assert (1/a).subs(a, c) == 1/c assert (1/a**2).subs(a, c) == 1/c**2 assert (1/a**2).subs(a, -2) == Rational(1, 4) assert (-(1/a**2)).subs(a, -2) == -Rational(1, 4) assert (1/x).subs(x, z) == 1/z assert (1/x**2).subs(x, z) == 1/z**2 assert (1/x**2).subs(x, -2) == Rational(1, 4) assert (-(1/x**2)).subs(x, -2) == -Rational(1, 4) #issue 2261 assert (1/x).subs(x, 0) == 1/S(0) def test_add(): a, b, c, d, x, y, t = symbols('a b c d x y t') assert (a**2 - b - c).subs(a**2 - b, d) in [d - c, a**2 - b - c] assert (a**2 - c).subs(a**2 - c, d) == d assert (a**2 - b - c).subs(a**2 - c, d) in [d - b, a**2 - b - c] assert (a**2 - x - c).subs(a**2 - c, d) in [d - x, a**2 - x - c] assert (a**2 - b - sqrt(a)).subs(a**2 - sqrt(a), c) == c - b assert (a + b + exp(a + b)).subs(a + b, c) == c + exp(c) assert (c + b + exp(c + b)).subs(c + b, a) == a + exp(a) assert (a + b + c + d).subs(b + c, x) == a + d + x assert (a + b + c + d).subs(-b - c, x) == a + d - x assert ((x + 1)*y).subs(x + 1, t) == t*y assert ((-x - 1)*y).subs(x + 1, t) == -t*y assert ((x - 1)*y).subs(x + 1, t) == y*(t - 2) assert ((-x + 1)*y).subs(x + 1, t) == y*(-t + 2) # this should work everytime: e = a**2 - b - c assert e.subs(Add(*e.args[:2]), d) == d + e.args[2] assert e.subs(a**2 - c, d) == d - b # the fallback should recognize when a change has # been made; while .1 == Rational(1, 10) they are not the same # and the change should be made assert (0.1 + a).subs(0.1, Rational(1, 10)) == Rational(1, 10) + a e = (-x*(-y + 1) - y*(y - 1)) ans = (-x*(x) - y*(-x)).expand() assert e.subs(-y + 1, x) == ans def test_subs_issue910(): assert (I*Symbol('a')).subs(1, 2) == I*Symbol('a') def test_functions_subs(): x, y = symbols('x y') f, g = symbols('f g', cls=Function) l = Lambda((x, y), sin(x) + y) assert (g(y, x) + cos(x)).subs(g, l) == sin(y) + x + cos(x) assert (f(x)**2).subs(f, sin) == sin(x)**2 assert (f(x, y)).subs(f, log) == log(x, y) assert (f(x, y)).subs(f, sin) == f(x, y) assert (sin(x) + atan2(x, y)).subs([[atan2, f], [sin, g]]) == \ f(x, y) + g(x) assert (g(f(x + y, x))).subs([[f, l], [g, exp]]) == exp(x + sin(x + y)) def test_derivative_subs(): y = Symbol('y') f = Function('f') assert Derivative(f(x), x).subs(f(x), y) != 0 assert Derivative(f(x), x).subs(f(x), y).subs(y, f(x)) == \ Derivative(f(x), x) # issues 1986, 1938 assert cse(Derivative(f(x), x) + f(x))[1][0].has(Derivative) assert cse(Derivative(f(x, y), x) + Derivative(f(x, y), y))[1][0].has(Derivative) def test_derivative_subs2(): x, y, z = symbols('x y z') f, g = symbols('f g', cls=Function) assert Derivative(f, x, y).subs(Derivative(f, x, y), g) == g assert Derivative(f, y, x).subs(Derivative(f, x, y), g) == g assert Derivative(f, x, y).subs(Derivative(f, x), g) == Derivative(g, y) assert Derivative(f, x, y).subs(Derivative(f, y), g) == Derivative(g, x) assert (Derivative(f(x, y, z), x, y, z).subs( Derivative(f(x, y, z), x, z), g) == Derivative(g, y)) assert (Derivative(f(x, y, z), x, y, z).subs( Derivative(f(x, y, z), z, y), g) == Derivative(g, x)) assert (Derivative(f(x, y, z), x, y, z).subs( Derivative(f(x, y, z), z, y, x), g) == g) def test_derivative_subs3(): x = Symbol('x') dex = Derivative(exp(x), x) assert Derivative(dex, x).subs(dex, exp(x)) == dex assert dex.subs(exp(x), dex) == Derivative(exp(x), x, x) def test_issue2185(): A, B = symbols('A B', commutative=False) assert (x*A).subs(x**2*A, B) == x*A assert (A**2).subs(A**3, B) == A**2 assert (A**6).subs(A**3, B) == B**2 def test_subs_iter(): assert x.subs(reversed([[x, y]])) == y it = iter([[x, y]]) assert x.subs(it) == y assert x.subs(Tuple((x, y))) == y def test_subs_dict(): a, b, c, d, e = symbols('a b c d e') z = symbols('z') assert (2*x + y + z).subs(dict(x=1, y=2)) == 4 + z l = [(sin(x), 2), (x, 1)] assert (sin(x)).subs(l) == \ (sin(x)).subs(dict(l)) == 2 assert sin(x).subs(reversed(l)) == sin(1) expr = sin(2*x) + sqrt(sin(2*x))*cos(2*x)*sin(exp(x)*x) reps = dict([ (sin(2*x), c), (sqrt(sin(2*x)), a), (cos(2*x), b), (exp(x), e), (x, d), ]) assert expr.subs(reps) == c + a*b*sin(d*e) l = [(x, 3), (y, x**2)] assert (x + y).subs(l) == 3 + x**2 assert (x + y).subs(reversed(l)) == 12 # If changes are made to convert lists into dictionaries and do # a dictionary-lookup replacement, these tests will help to catch # some logical errors that might occur l = [(y, z + 2), (1 + z, 5), (z, 2)] assert (y - 1 + 3*x).subs(l) == 5 + 3*x l = [(y, z + 2), (z, 3)] assert (y - 2).subs(l) == 3 def test_no_arith_subs_on_floats(): a, x, y = symbols('a x y') assert (x + 3).subs(x + 3, a) == a assert (x + 3).subs(x + 2, a) == a + 1 assert (x + y + 3).subs(x + 3, a) == a + y assert (x + y + 3).subs(x + 2, a) == a + y + 1 assert (x + 3.0).subs(x + 3.0, a) == a assert (x + 3.0).subs(x + 2.0, a) == x + 3.0 assert (x + y + 3.0).subs(x + 3.0, a) == a + y assert (x + y + 3.0).subs(x + 2.0, a) == x + y + 3.0 def test_issue_2552(): a, b, c, K = symbols('a b c K', commutative=True) x, y, z = symbols('x y z') assert (a/(b*c)).subs(b*c, K) == a/K assert (a/(b**2*c**3)).subs(b*c, K) == a/(c*K**2) assert (1/(x*y)).subs(x*y, 2) == S.Half assert ((1 + x*y)/(x*y)).subs(x*y, 1) == 2 assert (x*y*z).subs(x*y, 2) == 2*z assert ((1 + x*y)/(x*y)/z).subs(x*y, 1) == 2/z def test_issue_2976(): assert Tuple(1, True).subs(1, 2) == Tuple(2, True) def test_issue_2980(): # since x + 2.0 == x + 2 we can't do a simple equality test x = symbols('x') assert _aresame((x + 2.0).subs(2, 3), x + 2.0) assert _aresame((x + 2.0).subs(2.0, 3), x + 3) assert not _aresame(x + 2, x + 2.0) assert not _aresame(Basic(cos, 1), Basic(cos, 1.)) assert _aresame(cos, cos) assert not _aresame(1, S(1)) assert not _aresame(x, symbols('x', positive=True)) def test_issue_1581(): N = Symbol('N') assert N.subs(dict(N=3)) == 3 def test_issue_3059(): assert (x - 1).subs(1, y) == x - y assert (x - 1).subs(-1, y) == x + y assert (x - oo).subs(oo, y) == x - y assert (x - oo).subs(-oo, y) == x + y def test_Function_subs(): from sympy.abc import x, y f, g, h, i = symbols('f g h i', cls=Function) p = Piecewise((g(f(x, y)), x < -1), (g(x), x <= 1)) assert p.subs(g, h) == Piecewise((h(f(x, y)), x < -1), (h(x), x <= 1)) assert (f(y) + g(x)).subs({f: h, g: i}) == i(x) + h(y) def test_simultaneous_subs(): reps = {x: 0, y: 0} assert (x/y).subs(reps) != (y/x).subs(reps) assert (x/y).subs(reps, simultaneous=True) == \ (y/x).subs(reps, simultaneous=True) reps = reps.items() assert (x/y).subs(reps) != (y/x).subs(reps) assert (x/y).subs(reps, simultaneous=True) == \ (y/x).subs(reps, simultaneous=True) def test_issue_3320_3322(): assert (1/(1 + x/y)).subs(x/y, x) == 1/(1 + x) assert (-2*I).subs(2*I, x) == -x assert (-I*x).subs(I*x, x) == -x assert (-3*I*y**4).subs(3*I*y**2, x) == -x*y**2 def test_issue_3460(): assert (-12*x + y).subs(-x, 1) == 12 + y # though this involves cse it generated a failure in Mul._eval_subs x0, x1 = symbols('x0 x1') e = -log(-12*sqrt(2) + 17)/24 - log(-2*sqrt(2) + 3)/12 + sqrt(2)/3 # XXX modify cse so x1 is eliminated and x0 = -sqrt(2)? assert cse(e) == ( [(x0, sqrt(2))], [x0/3 - log(-12*x0 + 17)/24 - log(-2*x0 + 3)/12]) def test_issue_2162(): e = I*x assert exp(e).subs(exp(x), y) == y**I assert (2**e).subs(2**x, y) == y**I eq = (-2)**e assert eq.subs((-2)**x, y) == eq def test_issue_3824(): assert (-2*x*sqrt(2)).subs(2*x, y) == -sqrt(2)*y def test_2arg_hack(): N = Symbol('N', commutative=False) ans = Mul(2, y + 1, evaluate=False) assert (2*x*(y + 1)).subs(x, 1, hack2=True) == ans assert (2*(y + 1 + N)).subs(N, 0, hack2=True) == ans @XFAIL def test_mul2(): """When this fails, remove things labelled "2-arg hack" 1) remove special handling in the fallback of subs that was added in the same commit as this test 2) remove the special handling in Mul.flatten """ assert (2*(x + 1)).is_Mul sympy-0.7.4.1/sympy/core/tests/__init__.py0000644000175000017500000000000012253362407020616 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/core/coreerrors.py0000644000175000017500000000050112253362407020110 0ustar georgeskgeorgesk"""Definitions of common exceptions for :mod:`sympy.core` module. """ from __future__ import print_function, division class BaseCoreError(Exception): """Base class for core related exceptions. """ class NonCommutativeExpression(BaseCoreError): """Raised when expression didn't have commutative property. """ sympy-0.7.4.1/sympy/core/__init__.py0000644000175000017500000000247612253362407017477 0ustar georgeskgeorgesk"""Core module. Provides the basic operations needed in sympy. """ from .sympify import sympify, SympifyError from .cache import cacheit from .basic import Basic, Atom, C, preorder_traversal from .singleton import S from .expr import Expr, AtomicExpr from .symbol import Symbol, Wild, Dummy, symbols, var from .numbers import Number, Float, Rational, Integer, NumberSymbol, \ RealNumber, igcd, ilcm, seterr, E, I, nan, oo, pi, zoo from .power import Pow, integer_nthroot from .mul import Mul, prod from .add import Add from .mod import Mod from .relational import ( Rel, Eq, Ne, Lt, Le, Gt, Ge, Equality, GreaterThan, LessThan, Unequality, StrictGreaterThan, StrictLessThan ) from .multidimensional import vectorize from .function import Lambda, WildFunction, Derivative, diff, FunctionClass, \ Function, Subs, expand, PoleError, count_ops, \ expand_mul, expand_log, expand_func, \ expand_trig, expand_complex, expand_multinomial, nfloat, \ expand_power_base, expand_power_exp from .sets import (Set, Interval, Union, EmptySet, FiniteSet, ProductSet, Intersection, imageset) from .evalf import PrecisionExhausted, N from .containers import Tuple, Dict from .exprtools import gcd_terms, factor_terms, factor_nc # expose singletons Catalan = S.Catalan EulerGamma = S.EulerGamma GoldenRatio = S.GoldenRatio sympy-0.7.4.1/sympy/integrals/0000755000175000017500000000000012253362407016415 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/integrals/rationaltools.py0000644000175000017500000002337312253362407021671 0ustar georgeskgeorgesk"""This module implements tools for integrating rational functions. """ from __future__ import print_function, division from sympy import S, Symbol, symbols, I, log, atan, \ roots, collect, solve, RootSum, Lambda, cancel, Dummy from sympy.polys import Poly, subresultants, resultant, ZZ from sympy.core.compatibility import xrange def ratint(f, x, **flags): """Performs indefinite integration of rational functions. Given a field :math:`K` and a rational function :math:`f = p/q`, where :math:`p` and :math:`q` are polynomials in :math:`K[x]`, returns a function :math:`g` such that :math:`f = g'`. >>> from sympy.integrals.rationaltools import ratint >>> from sympy.abc import x >>> ratint(36/(x**5 - 2*x**4 - 2*x**3 + 4*x**2 + x - 2), x) (12*x + 6)/(x**2 - 1) + 4*log(x - 2) - 4*log(x + 1) References ========== .. [Bro05] M. Bronstein, Symbolic Integration I: Transcendental Functions, Second Edition, Springer-Verlag, 2005, pp. 35-70 See Also ======== sympy.integrals.integrals.Integral.doit ratint_logpart, ratint_ratpart """ if type(f) is not tuple: p, q = f.as_numer_denom() else: p, q = f p, q = Poly(p, x, composite=False, field=True), Poly(q, x, composite=False, field=True) coeff, p, q = p.cancel(q) poly, p = p.div(q) result = poly.integrate(x).as_expr() if p.is_zero: return coeff*result g, h = ratint_ratpart(p, q, x) P, Q = h.as_numer_denom() P = Poly(P, x) Q = Poly(Q, x) q, r = P.div(Q) result += g + q.integrate(x).as_expr() if not r.is_zero: symbol = flags.get('symbol', 't') if not isinstance(symbol, Symbol): t = Dummy(symbol) else: t = symbol.as_dummy() L = ratint_logpart(r, Q, x, t) real = flags.get('real') if real is None: if type(f) is not tuple: atoms = f.atoms() else: p, q = f atoms = p.atoms() | q.atoms() for elt in atoms - set([x]): if not elt.is_real: real = False break else: real = True eps = S(0) if not real: for h, q in L: eps += RootSum( q, Lambda(t, t*log(h.as_expr())), quadratic=True) else: for h, q in L: R = log_to_real(h, q, x, t) if R is not None: eps += R else: eps += RootSum( q, Lambda(t, t*log(h.as_expr())), quadratic=True) result += eps return coeff*result def ratint_ratpart(f, g, x): """ Horowitz-Ostrogradsky algorithm. Given a field K and polynomials f and g in K[x], such that f and g are coprime and deg(f) < deg(g), returns fractions A and B in K(x), such that f/g = A' + B and B has square-free denominator. Examples ======== >>> from sympy.integrals.rationaltools import ratint_ratpart >>> from sympy.abc import x, y >>> from sympy import Poly >>> ratint_ratpart(Poly(1, x, domain='ZZ'), ... Poly(x + 1, x, domain='ZZ'), x) (0, 1/(x + 1)) >>> ratint_ratpart(Poly(1, x, domain='EX'), ... Poly(x**2 + y**2, x, domain='EX'), x) (0, 1/(x**2 + y**2)) >>> ratint_ratpart(Poly(36, x, domain='ZZ'), ... Poly(x**5 - 2*x**4 - 2*x**3 + 4*x**2 + x - 2, x, domain='ZZ'), x) ((12*x + 6)/(x**2 - 1), 12/(x**2 - x - 2)) See Also ======== ratint, ratint_logpart """ f = Poly(f, x) g = Poly(g, x) u, v, _ = g.cofactors(g.diff()) n = u.degree() m = v.degree() A_coeffs = [ Dummy('a' + str(n - i)) for i in xrange(0, n) ] B_coeffs = [ Dummy('b' + str(m - i)) for i in xrange(0, m) ] C_coeffs = A_coeffs + B_coeffs A = Poly(A_coeffs, x, domain=ZZ[C_coeffs]) B = Poly(B_coeffs, x, domain=ZZ[C_coeffs]) H = f - A.diff()*v + A*(u.diff()*v).quo(u) - B*u result = solve(H.coeffs(), C_coeffs) A = A.as_expr().subs(result) B = B.as_expr().subs(result) rat_part = cancel(A/u.as_expr(), x) log_part = cancel(B/v.as_expr(), x) return rat_part, log_part def ratint_logpart(f, g, x, t=None): """ Lazard-Rioboo-Trager algorithm. Given a field K and polynomials f and g in K[x], such that f and g are coprime, deg(f) < deg(g) and g is square-free, returns a list of tuples (s_i, q_i) of polynomials, for i = 1..n, such that s_i in K[t, x] and q_i in K[t], and: ___ ___ d f d \ ` \ ` -- - = -- ) ) a log(s_i(a, x)) dx g dx /__, /__, i=1..n a | q_i(a) = 0 Examples ======== >>> from sympy.integrals.rationaltools import ratint_logpart >>> from sympy.abc import x >>> from sympy import Poly >>> ratint_logpart(Poly(1, x, domain='ZZ'), ... Poly(x**2 + x + 1, x, domain='ZZ'), x) [(Poly(x + 3*_t/2 + 1/2, x, domain='QQ[_t]'), ...Poly(3*_t**2 + 1, _t, domain='ZZ'))] >>> ratint_logpart(Poly(12, x, domain='ZZ'), ... Poly(x**2 - x - 2, x, domain='ZZ'), x) [(Poly(x - 3*_t/8 - 1/2, x, domain='QQ[_t]'), ...Poly(-_t**2 + 16, _t, domain='ZZ'))] See Also ======== ratint, ratint_ratpart """ f, g = Poly(f, x), Poly(g, x) t = t or Dummy('t') a, b = g, f - g.diff()*Poly(t, x) res, R = resultant(a, b, includePRS=True) res = Poly(res, t, composite=False) assert res, "BUG: resultant(%s, %s) can't be zero" % (a, b) R_map, H = {}, [] for r in R: R_map[r.degree()] = r def _include_sign(c, sqf): if (c < 0) is True: h, k = sqf[0] sqf[0] = h*c, k C, res_sqf = res.sqf_list() _include_sign(C, res_sqf) for q, i in res_sqf: _, q = q.primitive() if g.degree() == i: H.append((g, q)) else: h = R_map[i] h_lc = Poly(h.LC(), t, field=True) c, h_lc_sqf = h_lc.sqf_list(all=True) _include_sign(c, h_lc_sqf) for a, j in h_lc_sqf: h = h.quo(Poly(a.gcd(q)**j, x)) inv, coeffs = h_lc.invert(q), [S(1)] for coeff in h.coeffs()[1:]: T = (inv*coeff).rem(q) coeffs.append(T.as_expr()) h = Poly(dict(list(zip(h.monoms(), coeffs))), x) H.append((h, q)) return H def log_to_atan(f, g): """ Convert complex logarithms to real arctangents. Given a real field K and polynomials f and g in K[x], with g != 0, returns a sum h of arctangents of polynomials in K[x], such that: dh d f + I g -- = -- I log( ------- ) dx dx f - I g Examples ======== >>> from sympy.integrals.rationaltools import log_to_atan >>> from sympy.abc import x >>> from sympy import Poly, sqrt, S >>> log_to_atan(Poly(x, x, domain='ZZ'), Poly(1, x, domain='ZZ')) 2*atan(x) >>> log_to_atan(Poly(x + S(1)/2, x, domain='QQ'), ... Poly(sqrt(3)/2, x, domain='EX')) 2*atan(2*sqrt(3)*x/3 + sqrt(3)/3) See Also ======== log_to_real """ if f.degree() < g.degree(): f, g = -g, f f = f.to_field() g = g.to_field() p, q = f.div(g) if q.is_zero: return 2*atan(p.as_expr()) else: s, t, h = g.gcdex(-f) u = (f*s + g*t).quo(h) A = 2*atan(u.as_expr()) return A + log_to_atan(s, t) def log_to_real(h, q, x, t): """ Convert complex logarithms to real functions. Given real field K and polynomials h in K[t,x] and q in K[t], returns real function f such that: ___ df d \ ` -- = -- ) a log(h(a, x)) dx dx /__, a | q(a) = 0 Examples ======== >>> from sympy.integrals.rationaltools import log_to_real >>> from sympy.abc import x, y >>> from sympy import Poly, sqrt, S >>> log_to_real(Poly(x + 3*y/2 + S(1)/2, x, domain='QQ[y]'), ... Poly(3*y**2 + 1, y, domain='ZZ'), x, y) 2*sqrt(3)*atan(2*sqrt(3)*x/3 + sqrt(3)/3)/3 >>> log_to_real(Poly(x**2 - 1, x, domain='ZZ'), ... Poly(-2*y + 1, y, domain='ZZ'), x, y) log(x**2 - 1)/2 See Also ======== log_to_atan """ u, v = symbols('u,v', cls=Dummy) H = h.as_expr().subs({t: u + I*v}).expand() Q = q.as_expr().subs({t: u + I*v}).expand() H_map = collect(H, I, evaluate=False) Q_map = collect(Q, I, evaluate=False) a, b = H_map.get(S(1), S(0)), H_map.get(I, S(0)) c, d = Q_map.get(S(1), S(0)), Q_map.get(I, S(0)) R = Poly(resultant(c, d, v), u) R_u = roots(R, filter='R') if len(R_u) != R.count_roots(): return None result = S(0) for r_u in R_u.keys(): C = Poly(c.subs({u: r_u}), v) R_v = roots(C, filter='R') if len(R_v) != C.count_roots(): return None for r_v in R_v: if not r_v.is_positive: continue D = d.subs({u: r_u, v: r_v}) if D.evalf(chop=True) != 0: continue A = Poly(a.subs({u: r_u, v: r_v}), x) B = Poly(b.subs({u: r_u, v: r_v}), x) AB = (A**2 + B**2).as_expr() result += r_u*log(AB) + r_v*log_to_atan(A, B) R_q = roots(q, filter='R') if len(R_q) != q.count_roots(): return None for r in R_q.keys(): result += r*log(h.as_expr().subs(t, r)) return result sympy-0.7.4.1/sympy/integrals/trigonometry.py0000644000175000017500000002433512253362407021540 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- from __future__ import print_function, division from sympy.core import cacheit, Dummy, Eq, Integer, Rational, S, Wild from sympy.functions import binomial, sin, cos, tan, sec, csc, cot, Piecewise # TODO sin(a*x)*cos(b*x) -> sin((a+b)x) + sin((a-b)x) ? # creating, each time, Wild's and sin/cos/Mul is expensive. Also, our match & # subs are very slow when not cached, and if we create Wild each time, we # effectively block caching. # # so we cache the pattern @cacheit def _pat_sincos(x): a = Wild('a', exclude=[x]) n, m = [Wild(s, exclude=[x], properties=[lambda n: isinstance(n, Integer)]) for s in 'nm'] pat = sin(a*x)**n * cos(a*x)**m return pat, a, n, m _u = Dummy('u') def trigintegrate(f, x, conds='piecewise'): """Integrate f = Mul(trig) over x >>> from sympy import Symbol, sin, cos, tan, sec, csc, cot >>> from sympy.integrals.trigonometry import trigintegrate >>> from sympy.abc import x >>> trigintegrate(sin(x)*cos(x), x) sin(x)**2/2 >>> trigintegrate(sin(x)**2, x) x/2 - sin(x)*cos(x)/2 >>> trigintegrate(tan(x)*sec(x), x) 1/cos(x) >>> trigintegrate(sin(x)*tan(x), x) -log(sin(x) - 1)/2 + log(sin(x) + 1)/2 - sin(x) http://en.wikibooks.org/wiki/Calculus/Integration_techniques See Also ======== sympy.integrals.integrals.Integral.doit sympy.integrals.integrals.Integral """ from sympy.integrals.integrals import integrate pat, a, n, m = _pat_sincos(x) f = f.rewrite('sincos') M = f.match(pat) if M is None: return n, m = M[n], M[m] if n is S.Zero and m is S.Zero: return x zz = x if n is S.Zero else S.Zero a = M[a] if n.is_odd or m.is_odd: u = _u n_, m_ = n.is_odd, m.is_odd # take smallest n or m -- to choose simplest substitution if n_ and m_: n_ = n_ and (n < m) # NB: careful here, one of the m_ = m_ and not (n < m) # conditions *must* be true # n m u=C (n-1)/2 m # S(x) * C(x) dx --> -(1-u^2) * u du if n_: ff = -(1 - u**2)**((n - 1)/2) * u**m uu = cos(a*x) # n m u=S n (m-1)/2 # S(x) * C(x) dx --> u * (1-u^2) du elif m_: ff = u**n * (1 - u**2)**((m - 1)/2) uu = sin(a*x) fi = integrate(ff, u) # XXX cyclic deps fx = fi.subs(u, uu) if conds == 'piecewise': return Piecewise((zz, Eq(a, 0)), (fx / a, True)) return fx / a # n & m are both even # # 2k 2m 2l 2l # we transform S (x) * C (x) into terms with only S (x) or C (x) # # example: # 100 4 100 2 2 100 4 2 # S (x) * C (x) = S (x) * (1-S (x)) = S (x) * (1 + S (x) - 2*S (x)) # # 104 102 100 # = S (x) - 2*S (x) + S (x) # 2k # then S is integrated with recursive formula # take largest n or m -- to choose simplest substitution n_ = (abs(n) > abs(m)) m_ = (abs(m) > abs(n)) res = S.Zero if n_: # 2k 2 k i 2i # C = (1 - S ) = sum(i, (-) * B(k, i) * S ) if m > 0: for i in range(0, m//2 + 1): res += ((-1)**i * binomial(m//2, i) * _sin_pow_integrate(n + 2*i, x)) elif m == 0: res = _sin_pow_integrate(n, x) else: # m < 0 , |n| > |m| # / # | # | m n # | cos (x) sin (x) dx = # | # | #/ # / # | # -1 m+1 n-1 n - 1 | m+2 n-2 # ________ cos (x) sin (x) + _______ | cos (x) sin (x) dx # | # m + 1 m + 1 | # / res = (Rational(-1, m + 1) * cos(x)**(m + 1) * sin(x)**(n - 1) + Rational(n - 1, m + 1) * trigintegrate(cos(x)**(m + 2)*sin(x)**(n - 2), x)) elif m_: # 2k 2 k i 2i # S = (1 - C ) = sum(i, (-) * B(k, i) * C ) if n > 0: # / / # | | # | m n | -m n # | cos (x)*sin (x) dx or | cos (x) * sin (x) dx # | | # / / # # |m| > |n| ; m, n >0 ; m, n belong to Z - {0} # n 2 # sin (x) term is expanded here in terms of cos (x), # and then integrated. # for i in range(0, n//2 + 1): res += ((-1)**i * binomial(n//2, i) * _cos_pow_integrate(m + 2*i, x)) elif n == 0: # / # | # | 1 # | _ _ _ # | m # | cos (x) # / # res = _cos_pow_integrate(m, x) else: # n < 0 , |m| > |n| # / # | # | m n # | cos (x) sin (x) dx = # | # | #/ # / # | # 1 m-1 n+1 m - 1 | m-2 n+2 # _______ cos (x) sin (x) + _______ | cos (x) sin (x) dx # | # n + 1 n + 1 | # / res = (Rational(1, n + 1) * cos(x)**(m - 1)*sin(x)**(n + 1) + Rational(m - 1, n + 1) * trigintegrate(cos(x)**(m - 2)*sin(x)**(n + 2), x)) else: if m == n: ##Substitute sin(2x)/2 for sin(x)cos(x) and then Integrate. res = integrate((Rational(1, 2)*sin(2*x))**m, x) elif (m == -n): if n < 0: # Same as the scheme described above. # the function argument to integrate in the end will # be 1 , this cannot be integrated by trigintegrate. # Hence use sympy.integrals.integrate. res = (Rational(1, n + 1) * cos(x)**(m - 1) * sin(x)**(n + 1) + Rational(m - 1, n + 1) * integrate(cos(x)**(m - 2) * sin(x)**(n + 2), x)) else: res = (Rational(-1, m + 1) * cos(x)**(m + 1) * sin(x)**(n - 1) + Rational(n - 1, m + 1) * integrate(cos(x)**(m + 2)*sin(x)**(n - 2), x)) if conds == 'piecewise': return Piecewise((zz, Eq(a, 0)), (res.subs(x, a*x) / a, True)) return res.subs(x, a*x) / a def _sin_pow_integrate(n, x): if n > 0: if n == 1: #Recursion break return -cos(x) # n > 0 # / / # | | # | n -1 n-1 n - 1 | n-2 # | sin (x) dx = ______ cos (x) sin (x) + _______ | sin (x) dx # | | # | n n | #/ / # # return (Rational(-1, n) * cos(x) * sin(x)**(n - 1) + Rational(n - 1, n) * _sin_pow_integrate(n - 2, x)) if n < 0: if n == -1: ##Make sure this does not come back here again. ##Recursion breaks here or at n==0. return trigintegrate(1/sin(x), x) # n < 0 # / / # | | # | n 1 n+1 n + 2 | n+2 # | sin (x) dx = _______ cos (x) sin (x) + _______ | sin (x) dx # | | # | n + 1 n + 1 | #/ / # return (Rational(1, n + 1) * cos(x) * sin(x)**(n + 1) + Rational(n + 2, n + 1) * _sin_pow_integrate(n + 2, x)) else: #n == 0 #Recursion break. return x def _cos_pow_integrate(n, x): if n > 0: if n == 1: #Recursion break. return sin(x) # n > 0 # / / # | | # | n 1 n-1 n - 1 | n-2 # | sin (x) dx = ______ sin (x) cos (x) + _______ | cos (x) dx # | | # | n n | #/ / # return (Rational(1, n) * sin(x) * cos(x)**(n - 1) + Rational(n - 1, n) * _cos_pow_integrate(n - 2, x)) if n < 0: if n == -1: ##Recursion break return trigintegrate(1/cos(x), x) # n < 0 # / / # | | # | n -1 n+1 n + 2 | n+2 # | cos (x) dx = _______ sin (x) cos (x) + _______ | cos (x) dx # | | # | n + 1 n + 1 | #/ / # return (Rational(-1, n + 1) * sin(x) * cos(x)**(n + 1) + Rational(n + 2, n + 1) * _cos_pow_integrate(n + 2, x)) else: # n == 0 #Recursion Break. return x sympy-0.7.4.1/sympy/integrals/deltafunctions.py0000644000175000017500000001410012253362407022005 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import Mul from sympy.functions import DiracDelta, Heaviside from sympy.solvers import solve from sympy.core.compatibility import default_sort_key def change_mul(node, x): """change_mul(node, x) Rearranges the operands of a product, bringing to front any simple DiracDelta expression. If no simple DiracDelta expression was found, then all the DiracDelta expressions are simplified (using DiracDelta.simplify). Return: (dirac, new node) Where: o dirac is either a simple DiracDelta expression or None (if no simple expression was found); o new node is either a simplified DiracDelta expressions or None (if it could not be simplified). Examples ======== >>> from sympy import DiracDelta, cos >>> from sympy.integrals.deltafunctions import change_mul >>> from sympy.abc import x, y >>> change_mul(x*y*DiracDelta(x)*cos(x), x) (DiracDelta(x), x*y*cos(x)) >>> change_mul(x*y*DiracDelta(x**2 - 1)*cos(x), x) (None, x*y*cos(x)*DiracDelta(x - 1)/2 + x*y*cos(x)*DiracDelta(x + 1)/2) >>> change_mul(x*y*DiracDelta(cos(x))*cos(x), x) (None, None) See Also ======== sympy.functions.special.delta_functions.DiracDelta deltaintegrate """ if not (node.is_Mul or node.is_Pow): return node new_args = [] dirac = None #Sorting is needed so that we consistently collapse the same delta; #However, we must preserve the ordering of non-commutative terms c, nc = node.args_cnc() sorted_args = sorted(c, key=default_sort_key) sorted_args.extend(nc) for arg in sorted_args: if arg.is_Pow and arg.base.func is DiracDelta: new_args.append(arg.func(arg.base, arg.exp - 1)) arg = arg.base if dirac is None and (arg.func is DiracDelta and arg.is_simple(x) and (len(arg.args) <= 1 or arg.args[1] == 0)): dirac = arg else: new_args.append(arg) if not dirac: # there was no simple dirac new_args = [] for arg in sorted_args: if arg.func is DiracDelta: new_args.append(arg.simplify(x)) elif arg.is_Pow and arg.base.func is DiracDelta: new_args.append(arg.func(arg.base.simplify(x), arg.exp)) else: new_args.append(change_mul(arg, x)) if new_args != sorted_args: nnode = Mul(*new_args).expand() else: # if the node didn't change there is nothing to do nnode = None return (None, nnode) return (dirac, Mul(*new_args)) def deltaintegrate(f, x): """ deltaintegrate(f, x) The idea for integration is the following: - If we are dealing with a DiracDelta expression, i.e. DiracDelta(g(x)), we try to simplify it. If we could simplify it, then we integrate the resulting expression. We already know we can integrate a simplified expression, because only simple DiracDelta expressions are involved. If we couldn't simplify it, there are two cases: 1) The expression is a simple expression: we return the integral, taking care if we are dealing with a Derivative or with a proper DiracDelta. 2) The expression is not simple (i.e. DiracDelta(cos(x))): we can do nothing at all. - If the node is a multiplication node having a DiracDelta term: First we expand it. If the expansion did work, the we try to integrate the expansion. If not, we try to extract a simple DiracDelta term, then we have two cases: 1) We have a simple DiracDelta term, so we return the integral. 2) We didn't have a simple term, but we do have an expression with simplified DiracDelta terms, so we integrate this expression. Examples ======== >>> from sympy.abc import x, y, z >>> from sympy.integrals.deltafunctions import deltaintegrate >>> from sympy import sin, cos, DiracDelta, Heaviside >>> deltaintegrate(x*sin(x)*cos(x)*DiracDelta(x - 1), x) sin(1)*cos(1)*Heaviside(x - 1) >>> deltaintegrate(y**2*DiracDelta(x - z)*DiracDelta(y - z), y) z**2*DiracDelta(x - z)*Heaviside(y - z) See Also ======== sympy.functions.special.delta_functions.DiracDelta sympy.integrals.integrals.Integral """ if not f.has(DiracDelta): return None from sympy.integrals import Integral, integrate # g(x) = DiracDelta(h(x)) if f.func == DiracDelta: h = f.simplify(x) if h == f: # can't simplify the expression #FIXME: the second term tells whether is DeltaDirac or Derivative #For integrating derivatives of DiracDelta we need the chain rule if f.is_simple(x): if (len(f.args) <= 1 or f.args[1] == 0): return Heaviside(f.args[0]) else: return (DiracDelta(f.args[0], f.args[1] - 1) / f.args[0].as_poly().LC()) else: # let's try to integrate the simplified expression fh = integrate(h, x) return fh elif f.is_Mul or f.is_Pow: # g(x) = a*b*c*f(DiracDelta(h(x)))*d*e g = f.expand() if f != g: # the expansion worked fh = integrate(g, x) if fh is not None and not isinstance(fh, Integral): return fh else: # no expansion performed, try to extract a simple DiracDelta term dg, rest_mult = change_mul(f, x) if not dg: if rest_mult: fh = integrate(rest_mult, x) return fh else: dg = dg.simplify(x) if dg.is_Mul: # Take out any extracted factors dg, rest_mult_2 = change_mul(dg, x) rest_mult = rest_mult*rest_mult_2 point = solve(dg.args[0], x)[0] return (rest_mult.subs(x, point)*Heaviside(x - point)) return None sympy-0.7.4.1/sympy/integrals/meijerint_doc.py0000644000175000017500000000174012253362407021604 0ustar georgeskgeorgesk""" This module cooks up a docstring when imported. Its only purpose is to be displayed in the sphinx documentation. """ from __future__ import print_function, division from sympy.integrals.meijerint import _create_lookup_table from sympy import latex, Eq, meijerg, Add, Symbol t = {} _create_lookup_table(t) doc = "" for about, category in sorted(t.items()): if about == (): doc += 'Elementary functions:\n\n' else: doc += 'Functions involving ' + ', '.join('`%s`' % latex( list(category[0][0].atoms(func))[0]) for func in about) + ':\n\n' for formula, gs, cond, hint in category: if not isinstance(gs, list): g = Symbol('\\text{generated}') else: g = Add(*[fac*f for (fac, f) in gs]) obj = Eq(formula, g) if cond is True: cond = "" else: cond = ',\\text{ if } %s' % latex(cond) doc += ".. math::\n %s%s\n\n" % (latex(obj), cond) __doc__ = doc sympy-0.7.4.1/sympy/integrals/risch.py0000644000175000017500000017574312253362407020120 0ustar georgeskgeorgesk""" The Risch Algorithm for transcendental function integration. The core algorithms for the Risch algorithm are here. The subproblem algorithms are in the rde.py and prde.py files for the Risch Differential Equation solver and the parametric problems solvers, respectively. All important information concerning the differential extension for an integrand is stored in a DifferentialExtension object, which in the code is usually called DE. Throughout the code and Inside the DifferentialExtension object, the conventions/attribute names are that the base domain is QQ and each differential extension is x, t0, t1, ..., tn-1 = DE.t. DE.x is the variable of integration (Dx == 1), DE.D is a list of the derivatives of x, t1, t2, ..., tn-1 = t, DE.T is the list [x, t1, t2, ..., tn-1], DE.t is the outer-most variable of the differential extension at the given level (the level can be adjusted using DE.increment_level() and DE.decrement_level()), k is the field C(x, t0, ..., tn-2), where C is the constant field. The numerator of a fraction is denoted by a and the denominator by d. If the fraction is named f, fa == numer(f) and fd == denom(f). Fractions are returned as tuples (fa, fd). DE.d and DE.t are used to represent the topmost derivation and extension variable, respectively. The docstring of a function signifies whether an argument is in k[t], in which case it will just return a Poly in t, or in k(t), in which case it will return the fraction (fa, fd). Other variable names probably come from the names used in Bronstein's book. """ from __future__ import print_function, division from sympy import real_roots from sympy.abc import z from sympy.core.function import Lambda from sympy.core.numbers import ilcm, oo from sympy.core.mul import Mul from sympy.core.power import Pow from sympy.core.relational import Eq, Ne from sympy.core.singleton import S from sympy.core.symbol import Symbol, Dummy from sympy.core.compatibility import reduce, ordered, xrange from sympy.integrals.heurisch import _symbols from sympy.functions import (acos, acot, asin, atan, cos, cot, exp, log, Piecewise, sin, tan) from sympy.functions import sinh, cosh, tanh, coth, asinh, acosh , atanh , acoth from sympy.integrals import Integral, integrate from sympy.polys import gcd, cancel, PolynomialError, Poly, reduced, RootSum, DomainError from sympy.utilities.iterables import numbered_symbols def integer_powers(exprs): """ Rewrites a list of expressions as integer multiples of each other. For example, if you have [x, x/2, x**2 + 1, 2*x/3], then you can rewrite this as [(x/6) * 6, (x/6) * 3, (x**2 + 1) * 1, (x/6) * 4]. This is useful in the Risch integration algorithm, where we must write exp(x) + exp(x/2) as (exp(x/2))**2 + exp(x/2), but not as exp(x) + sqrt(exp(x)) (this is because only the transcendental case is implemented and we therefore cannot integrate algebraic extensions). The integer multiples returned by this function for each term are the smallest possible (their content equals 1). Returns a list of tuples where the first element is the base term and the second element is a list of `(item, factor)` terms, where `factor` is the integer multiplicative factor that must multiply the base term to obtain the original item. The easiest way to understand this is to look at an example: >>> from sympy.abc import x >>> from sympy.integrals.risch import integer_powers >>> integer_powers([x, x/2, x**2 + 1, 2*x/3]) [(x/6, [(x, 6), (x/2, 3), (2*x/3, 4)]), (x**2 + 1, [(x**2 + 1, 1)])] We can see how this relates to the example at the beginning of the docstring. It chose x/6 as the first base term. Then, x can be written as (x/2) * 2, so we get (0, 2), and so on. Now only element (x**2 + 1) remains, and there are no other terms that can be written as a rational multiple of that, so we get that it can be written as (x**2 + 1) * 1. """ # Here is the strategy: # First, go through each term and determine if it can be rewritten as a # rational multiple of any of the terms gathered so far. # cancel(a/b).is_Rational is sufficient for this. If it is a multiple, we # add its multiple to the dictionary. terms = {} for term in exprs: for j in terms: a = cancel(term/j) if a.is_Rational: terms[j].append((term, a)) break else: terms[term] = [(term, S(1))] # After we have done this, we have all the like terms together, so we just # need to find a common denominator so that we can get the base term and # integer multiples such that each term can be written as an integer # multiple of the base term, and the content of the integers is 1. newterms = {} for term in terms: common_denom = reduce(ilcm, [i.as_numer_denom()[1] for _, i in terms[term]]) newterm = term/common_denom newmults = [(i, j*common_denom) for i, j in terms[term]] newterms[newterm] = newmults return sorted(iter(newterms.items()), key=lambda item: item[0].sort_key()) class DifferentialExtension(object): """ A container for all the information relating to a differential extension. The attributes of this object are (see also the docstring of __init__): - f: The original (Expr) integrand. - x: The variable of integration. - T: List of variables in the extension. - D: List of derivations in the extension; corresponds to the elements of T. - fa: Poly of the numerator of the integrand. - fd: Poly of the denominator of the integrand. - Tfuncs: Lambda() representations of each element of T (except for x). For back-substitution after integration. - backsubs: A (possibly empty) list of further substitutions to be made on the final integral to make it look more like the integrand. - E_K: List of the positions of the exponential extensions in T. - E_args: The arguments of each of the exponentials in E_K. - L_K: List of the positions of the logarithmic extensions in T. - L_args: The arguments of each of the logarithms in L_K. (See the docstrings of is_deriv_k() and is_log_deriv_k_t_radical() for more information on E_K, E_args, L_K, and L_args) - cases: List of string representations of the cases of T. - t: The top level extension variable, as defined by the current level (see level below). - d: The top level extension derivation, as defined by the current derivation (see level below). - case: The string representation of the case of self.d. (Note that self.T and self.D will always contain the complete extension, regardless of the level. Therefore, you should ALWAYS use DE.t and DE.d instead of DE.T[-1] and DE.D[-1]. If you want to have a list of the derivations or variables only up to the current level, use DE.D[:len(DE.D) + DE.level + 1] and DE.T[:len(DE.T) + DE.level + 1]. Note that, in particular, the derivation() function does this.) The following are also attributes, but will probably not be useful other than in internal use: - newf: Expr form of fa/fd. - level: The number (between -1 and -len(self.T)) such that self.T[self.level] == self.t and self.D[self.level] == self.d. Use the methods self.increment_level() and self.decrement_level() to change the current level. """ # __slots__ is defined mainly so we can iterate over all the attributes # of the class easily (the memory use doesn't matter too much, since we # only create one DifferentialExtension per integration). Also, it's nice # to have a safeguard when debugging. __slots__ = ('f', 'x', 'T', 'D', 'fa', 'fd', 'Tfuncs', 'backsubs', 'E_K', 'E_args', 'L_K', 'L_args', 'cases', 'case', 't', 'd', 'newf', 'level', 'ts') def __init__(self, f=None, x=None, handle_first='log', dummy=True, extension=None, rewrite_complex=False): """ Tries to build a transcendental extension tower from f with respect to x. If it is successful, creates a DifferentialExtension object with, among others, the attributes fa, fd, D, T, Tfuncs, and backsubs such that fa and fd are Polys in T[-1] with rational coefficients in T[:-1], fa/fd == f, and D[i] is a Poly in T[i] with rational coefficients in T[:i] representing the derivative of T[i] for each i from 1 to len(T). Tfuncs is a list of Lambda objects for back replacing the functions after integrating. Lambda() is only used (instead of lambda) to make them easier to test and debug. Note that Tfuncs corresponds to the elements of T, except for T[0] == x, but they should be back-substituted in reverse order. backsubs is a (possibly empty) back-substitution list that should be applied on the completed integral to make it look more like the original integrand. If it is unsuccessful, it raises NotImplementedError. You can also create an object by manually setting the attributes as a dictionary to the extension keyword argument. You must include at least D. Warning, any attribute that is not given will be set to None. The attributes T, t, d, cases, case, x, and level are set automatically and do not need to be given. The functions in the Risch Algorithm will NOT check to see if an attribute is None before using it. This also does not check to see if the extension is valid (non-algebraic) or even if it is self-consistent. Therefore, this should only be used for testing/debugging purposes. """ # XXX: If you need to debug this function, set the break point here if extension: if 'D' not in extension: raise ValueError("At least the key D must be included with " "the extension flag to DifferentialExtension.") for attr in extension: setattr(self, attr, extension[attr]) self._auto_attrs() return elif f is None or x is None: raise ValueError("Either both f and x or a manual extension must " "be given.") from sympy.integrals.prde import is_deriv_k if handle_first not in ['log', 'exp']: raise ValueError("handle_first must be 'log' or 'exp', not %s." % str(handle_first)) # f will be the original function, self.f might change if we reset # (e.g., we pull out a constant from an exponential) self.f = f self.x = x self.reset(dummy=dummy) exp_new_extension, log_new_extension = True, True if rewrite_complex: rewritables = { (sin, cos, cot, tan, sinh, cosh, coth, tanh): exp, (asin, acos, acot, atan): log, } #rewrite the trigonometric components for candidates, rule in rewritables.items(): self.newf = self.newf.rewrite(candidates, rule) else: if any(i.has(x) for i in self.f.atoms(sin, cos, tan, atan, asin, acos)): raise NotImplementedError("Trigonometric extensions are not " "supported (yet!)") def update(seq, atoms, func): s = set(seq) new = atoms - s s = atoms.intersection(s) s.update(list(filter(func, new))) return list(s) exps = set() pows = set() numpows = set() sympows = set() logs = set() symlogs = set() while True: restart = False if self.newf.is_rational_function(*self.T): break if not exp_new_extension and not log_new_extension: # We couldn't find a new extension on the last pass, so I guess # we can't do it. raise NotImplementedError("Couldn't find an elementary " "transcendental extension for %s. Try using a " % str(f) + "manual extension with the extension flag.") # Pre-preparsing. ################# # Get all exp arguments, so we can avoid ahead of time doing # something like t1 = exp(x), t2 = exp(x/2) == sqrt(t1). # Things like sqrt(exp(x)) do not automatically simplify to # exp(x/2), so they will be viewed as algebraic. The easiest way # to handle this is to convert all instances of (a**b)**Rational # to a**(Rational*b) before doing anything else. Note that the # _exp_part code can generate terms of this form, so we do need to # do this at each pass (or else modify it to not do that). ratpows = [i for i in self.newf.atoms(Pow).union(self.newf.atoms(exp)) if (i.base.is_Pow or i.base.func is exp and i.exp.is_Rational)] ratpows_repl = [ (i, i.base.base**(i.exp*i.base.exp)) for i in ratpows] self.backsubs += [(j, i) for i, j in ratpows_repl] self.newf = self.newf.xreplace(dict(ratpows_repl)) # To make the process deterministic, the args are sorted # so that functions with smaller op-counts are processed first. # Ties are broken with the default_sort_key. # XXX Although the method is deterministic no additional work # has been done to guarantee that the simplest solution is # returned and that it would be affected be using different # variables. Though it is possible that this is the case # one should know that it has not been done intentionally so # further improvements may possible. # TODO: This probably doesn't need to be completely recomputed at # each pass. exps = update(exps, self.newf.atoms(exp), lambda i: i.exp.is_rational_function(*self.T) and i.exp.has(*self.T)) pows = update(pows, self.newf.atoms(Pow), lambda i: i.exp.is_rational_function(*self.T) and i.exp.has(*self.T)) numpows = update(numpows, set(pows), lambda i: not i.base.has(*self.T)) sympows = update(sympows, set(pows) - set(numpows), lambda i: i.base.is_rational_function(*self.T) and not i.exp.is_Integer) # The easiest way to deal with non-base E powers is to convert them # into base E, integrate, and then convert back. for i in ordered(pows): old = i new = exp(i.exp*log(i.base)) # If exp is ever changed to automatically reduce exp(x*log(2)) # to 2**x, then this will break. The solution is to not change # exp to do that :) if i in sympows: if i.exp.is_Rational: raise NotImplementedError("Algebraic extensions are " "not supported (%s)." % str(i)) # We can add a**b only if log(a) in the extension, because # a**b == exp(b*log(a)). basea, based = frac_in(i.base, self.t) A = is_deriv_k(basea, based, self) if A is None: # Nonelementary monomial (so far) # TODO: Would there ever be any benefit from just # adding log(base) as a new monomial? # ANSWER: Yes, otherwise we can't integrate x**x (or # rather prove that it has no elementary integral) # without first manually rewriting it as exp(x*log(x)) continue ans, u, const = A newterm = exp(i.exp*(log(const) + u)) # Under the current implementation, exp kills terms # only if they are of the form a*log(x), where a is a # Number. This case should have already been killed by the # above tests. Again, if this changes to kill more than # that, this will break, which maybe is a sign that you # shouldn't be changing that. Actually, if anything, this # auto-simplification should be removed. See # http://groups.google.com/group/sympy/browse_thread/thread/a61d48235f16867f self.newf = self.newf.xreplace({i: newterm}) elif i not in numpows: continue else: # i in numpows newterm = new # TODO: Just put it in self.Tfuncs self.backsubs.append((new, old)) self.newf = self.newf.xreplace({old: newterm}) exps.append(newterm) atoms = self.newf.atoms(log) logs = update(logs, atoms, lambda i: i.args[0].is_rational_function(*self.T) and i.args[0].has(*self.T)) symlogs = update(symlogs, atoms, lambda i: i.has(*self.T) and i.args[0].is_Pow and i.args[0].base.is_rational_function(*self.T) and not i.args[0].exp.is_Integer) # We can handle things like log(x**y) by converting it to y*log(x) # This will fix not only symbolic exponents of the argument, but any # non-Integer exponent, like log(sqrt(x)). The exponent can also # depend on x, like log(x**x). for i in ordered(symlogs): # Unlike in the exponential case above, we do not ever # potentially add new monomials (above we had to add log(a)). # Therefore, there is no need to run any is_deriv functions # here. Just convert log(a**b) to b*log(a) and let # log_new_extension() handle it from there. lbase = log(i.args[0].base) logs.append(lbase) new = i.args[0].exp*lbase self.newf = self.newf.xreplace({i: new}) self.backsubs.append((new, i)) # remove any duplicates logs = list(set(logs)) if handle_first == 'exp' or not log_new_extension: exp_new_extension = self._exp_part(exps, dummy=dummy) if exp_new_extension is None: # reset and restart self.f = self.newf self.reset(dummy=dummy) exp_new_extension = True continue if handle_first == 'log' or not exp_new_extension: log_new_extension = self._log_part(logs, dummy=dummy) self.fa, self.fd = frac_in(self.newf, self.t) self._auto_attrs() return def __getattr__(self, attr): # Avoid AttributeErrors when debugging if attr not in self.__slots__: raise AttributeError("%s has no attribute %s" % (repr(self), repr(attr))) return None def _auto_attrs(self): """ Set attributes that are generated automatically. """ if not self.T: # i.e., when using the extension flag and T isn't given self.T = [i.gen for i in self.D] if not self.x: self.x = self.T[0] self.cases = [get_case(d, t) for d, t in zip(self.D, self.T)] self.level = -1 self.t = self.T[self.level] self.d = self.D[self.level] self.case = self.cases[self.level] def _exp_part(self, exps, dummy=True): """ Try to build an exponential extension. Returns True if there was a new extension, False if there was no new extension but it was able to rewrite the given exponentials in terms of the existing extension, and None if the entire extension building process should be restarted. If the process fails because there is no way around an algebraic extension (e.g., exp(log(x)/2)), it will raise NotImplementedError. """ from sympy.integrals.prde import is_log_deriv_k_t_radical new_extension = False restart = False expargs = [i.exp for i in exps] ip = integer_powers(expargs) for arg, others in ip: # Minimize potential problems with algebraic substitution others.sort(key=lambda i: i[1]) arga, argd = frac_in(arg, self.t) A = is_log_deriv_k_t_radical(arga, argd, self) if A is not None: ans, u, n, const = A # if n is 1 or -1, it's algebraic, but we can handle it if n == -1: # This probably will never happen, because # Rational.as_numer_denom() returns the negative term in # the numerator. But in case that changes, reduce it to # n == 1. n = 1 u **= -1 const *= -1 ans = [(i, -j) for i, j in ans] if n == 1: # Example: exp(x + x**2) over QQ(x, exp(x), exp(x**2)) self.newf = self.newf.xreplace({exp(arg): exp(const)*Mul(*[ u**power for u, power in ans])}) self.newf = self.newf.xreplace(dict([(exp(p*exparg), exp(const*p) * Mul(*[u**power for u, power in ans])) for exparg, p in others])) # TODO: Add something to backsubs to put exp(const*p) # back together. continue else: # Bad news: we have an algebraic radical. But maybe we # could still avoid it by choosing a different extension. # For example, integer_powers() won't handle exp(x/2 + 1) # over QQ(x, exp(x)), but if we pull out the exp(1), it # will. Or maybe we have exp(x + x**2/2), over # QQ(x, exp(x), exp(x**2)), which is exp(x)*sqrt(exp(x**2)), # but if we use QQ(x, exp(x), exp(x**2/2)), then they will # all work. # # So here is what we do: If there is a non-zero const, pull # it out and retry. Also, if len(ans) > 1, then rewrite # exp(arg) as the product of exponentials from ans, and # retry that. If const == 0 and len(ans) == 1, then we # assume that it would have been handled by either # integer_powers() or n == 1 above if it could be handled, # so we give up at that point. For example, you can never # handle exp(log(x)/2) because it equals sqrt(x). if const or len(ans) > 1: rad = Mul(*[term**(power/n) for term, power in ans]) self.newf = self.newf.xreplace(dict((exp(p*exparg), exp(const*p)*rad) for exparg, p in others)) self.newf = self.newf.xreplace(dict(list(zip(reversed(self.T), reversed([f(self.x) for f in self.Tfuncs]))))) restart = True break else: # TODO: give algebraic dependence in error string raise NotImplementedError("Cannot integrate over " "algebraic extensions.") else: arga, argd = frac_in(arg, self.t) darga = (argd*derivation(Poly(arga, self.t), self) - arga*derivation(Poly(argd, self.t), self)) dargd = argd**2 darga, dargd = darga.cancel(dargd, include=True) darg = darga.as_expr()/dargd.as_expr() self.t = next(self.ts) self.T.append(self.t) self.E_args.append(arg) self.E_K.append(len(self.T) - 1) self.D.append(darg.as_poly(self.t, expand=False)*Poly(self.t, self.t, expand=False)) if dummy: i = Dummy("i") else: i = Symbol('i') self.Tfuncs = self.Tfuncs + [Lambda(i, exp(arg.subs(self.x, i)))] self.newf = self.newf.xreplace( dict((exp(exparg), self.t**p) for exparg, p in others)) new_extension = True if restart: return None return new_extension def _log_part(self, logs, dummy=True): """ Try to build a logarithmic extension. Returns True if there was a new extension and False if there was no new extension but it was able to rewrite the given logarithms in terms of the existing extension. Unlike with exponential extensions, there is no way that a logarithm is not transcendental over and cannot be rewritten in terms of an already existing extension in a non-algebraic way, so this function does not ever return None or raise NotImplementedError. """ from sympy.integrals.prde import is_deriv_k new_extension = False logargs = [i.args[0] for i in logs] for arg in ordered(logargs): # The log case is easier, because whenever a logarithm is algebraic # over the base field, it is of the form a1*t1 + ... an*tn + c, # which is a polynomial, so we can just replace it with that. # In other words, we don't have to worry about radicals. arga, argd = frac_in(arg, self.t) A = is_deriv_k(arga, argd, self) if A is not None: ans, u, const = A newterm = log(const) + u self.newf = self.newf.xreplace({log(arg): newterm}) continue else: arga, argd = frac_in(arg, self.t) darga = (argd*derivation(Poly(arga, self.t), self) - arga*derivation(Poly(argd, self.t), self)) dargd = argd**2 darg = darga.as_expr()/dargd.as_expr() self.t = next(self.ts) self.T.append(self.t) self.L_args.append(arg) self.L_K.append(len(self.T) - 1) self.D.append(cancel(darg.as_expr()/arg).as_poly(self.t, expand=False)) if dummy: i = Dummy("i") else: i = Symbol('i') self.Tfuncs = self.Tfuncs + [Lambda(i, log(arg.subs(self.x, i)))] self.newf = self.newf.xreplace({log(arg): self.t}) new_extension = True return new_extension @property def _important_attrs(self): """ Returns some of the more important attributes of self. Used for testing and debugging purposes. The attributes are (fa, fd, D, T, Tfuncs, backsubs, E_K, E_args, L_K, L_args). """ # XXX: This might be easier to read as a dict or something # Maybe a named tuple. return (self.fa, self.fd, self.D, self.T, self.Tfuncs, self.backsubs, self.E_K, self.E_args, self.L_K, self.L_args) # TODO: Implement __repr__ def __str__(self): return str(self._important_attrs) def reset(self, dummy=True): """ Reset self to an initial state. Used by __init__. """ self.t = self.x self.T = [self.x] self.D = [Poly(1, self.x)] self.level = -1 self.L_K, self.E_K, self.L_args, self.E_args = [], [], [], [] if dummy: self.ts = numbered_symbols('t', cls=Dummy) else: # For testing self.ts = numbered_symbols('t') # For various things that we change to make things work that we need to # change back when we are done. self.backsubs = [] self.Tfuncs = [] self.newf = self.f def increment_level(self): """ Increment the level of self. This makes the working differential extension larger. self.level is given relative to the end of the list (-1, -2, etc.), so we don't need do worry about it when building the extension. """ if self.level >= -1: raise ValueError("The level of the differential extension cannot " "be incremented any further.") self.level += 1 self.t = self.T[self.level] self.d = self.D[self.level] self.case = self.cases[self.level] return None def decrement_level(self): """ Decrease the level of self. This makes the working differential extension smaller. self.level is given relative to the end of the list (-1, -2, etc.), so we don't need do worry about it when building the extension. """ if self.level <= -len(self.T): raise ValueError("The level of the differential extension cannot " "be decremented any further.") self.level -= 1 self.t = self.T[self.level] self.d = self.D[self.level] self.case = self.cases[self.level] return None class DecrementLevel(object): """ A context manager for decrementing the level of a DifferentialExtension. """ __slots__ = ('DE',) def __init__(self, DE): self.DE = DE return def __enter__(self): self.DE.decrement_level() def __exit__(self, exc_type, exc_value, traceback): self.DE.increment_level() class NonElementaryIntegralException(Exception): """ Exception used by subroutines within the Risch algorithm to indicate to one another that the function being integrated does not have an elementary integral in the given differential field. """ # TODO: Rewrite algorithms below to use this (?) # TODO: Pass through information about why the integral was nonelementary, # and store that in the resulting NonElementaryIntegral somehow. pass def gcdex_diophantine(a, b, c): """ Extended Euclidean Algorithm, Diophantine version. Given a, b in K[x] and c in (a, b), the ideal generated by a and b, return (s, t) such that s*a + t*b == c and either s == 0 or s.degree() < b.degree(). """ # Extended Euclidean Algorithm (Diophantine Version) pg. 13 # TODO: This should go in densetools.py. # XXX: Bettter name? s, g = a.half_gcdex(b) q = c.exquo(g) # Inexact division means c is not in (a, b) s = q*s if not s.is_zero and b.degree() >= b.degree(): q, s = s.div(b) t = (c - s*a).exquo(b) return (s, t) def frac_in(f, t, **kwargs): """ Returns the tuple (fa, fd), where fa and fd are Polys in t. This is a common idiom in the Risch Algorithm functions, so we abstract it out here. f should be a basic expresion, a Poly, or a tuple (fa, fd), where fa and fd are either basic expressions or Polys, and f == fa/fd. **kwargs are applied to Poly. """ cancel = kwargs.pop('cancel', False) if type(f) is tuple: fa, fd = f f = fa.as_expr()/fd.as_expr() fa, fd = f.as_expr().as_numer_denom() fa, fd = fa.as_poly(t, **kwargs), fd.as_poly(t, **kwargs) if cancel: fa, fd = fa.cancel(fd, include=True) if fa is None or fd is None: raise ValueError("Could not turn %s into a fraction in %s." % (f, t)) return (fa, fd) def as_poly_1t(p, t, z): """ (Hackish) way to convert an element p of K[t, 1/t] to K[t, z]. In other words, z == 1/t will be a dummy variable that Poly can handle better. See issue 2032. Examples ======== >>> from sympy import Symbol, random_poly >>> from sympy.integrals.risch import as_poly_1t >>> from sympy.abc import x, z >>> p1 = random_poly(x, 10, -10, 10) >>> p2 = random_poly(x, 10, -10, 10) >>> p = p1 + p2.subs(x, 1/x) >>> as_poly_1t(p, x, z).as_expr().subs(z, 1/x) == p True """ # TODO: Use this on the final result. That way, we can avoid answers like # (...)*exp(-x). pa, pd = frac_in(p, t, cancel=True) if not pd.is_monomial: # XXX: Is there a better Poly exception that we could raise here? # Either way, if you see this (from the Risch Algorithm) it indicates # a bug. raise PolynomialError("%s is not an element of K[%s, 1/%s]." % (p, t, t)) d = pd.degree(t) one_t_part = pa.slice(0, d + 1) r = pd.degree() - pa.degree() t_part = pa - one_t_part try: t_part = t_part.to_field().exquo(pd) except DomainError as e: # Issue 1851 raise NotImplementedError(e) # Compute the negative degree parts. one_t_part = Poly.from_list(reversed(one_t_part.rep.rep), *one_t_part.gens, domain=one_t_part.domain) if 0 < r < oo: one_t_part *= Poly(t**r, t) one_t_part = one_t_part.replace(t, z) # z will be 1/t if pd.nth(d): one_t_part *= Poly(1/pd.nth(d), z, expand=False) ans = t_part.as_poly(t, z, expand=False) + one_t_part.as_poly(t, z, expand=False) return ans def derivation(p, DE, coefficientD=False, basic=False): """ Computes Dp. Given the derivation D with D = d/dx and p is a polynomial in t over K(x), return Dp. If coefficientD is True, it computes the derivation kD (kappaD), which is defined as kD(sum(ai*Xi**i, (i, 0, n))) == sum(Dai*Xi**i, (i, 1, n)) (Definition 3.2.2, page 80). X in this case is T[-1], so coefficientD computes the derivative just with respect to T[:-1], with T[-1] treated as a constant. If basic=True, the returns a Basic expression. Elements of D can still be instances of Poly. """ if basic: r = 0 else: r = Poly(0, DE.t) t = DE.t if coefficientD: if DE.level <= -len(DE.T): # 'base' case, the answer is 0. return r DE.decrement_level() D = DE.D[:len(DE.D) + DE.level + 1] T = DE.T[:len(DE.T) + DE.level + 1] for d, v in zip(D, T): pv = p.as_poly(v) if pv is None or basic: pv = p.as_expr() if basic: r += d.as_expr()*pv.diff(v) else: r += (d*pv.diff(v)).as_poly(t) if basic: r = cancel(r) if coefficientD: DE.increment_level() return r def get_case(d, t): """ Returns the type of the derivation d. Returns one of {'exp', 'tan', 'base', 'primitive', 'other_linear', 'other_nonlinear'}. """ if not d.has(t): if d.is_one: return 'base' return 'primitive' if d.rem(Poly(t, t)).is_zero: return 'exp' if d.rem(Poly(1 + t**2, t)).is_zero: return 'tan' if d.degree(t) > 1: return 'other_nonlinear' return 'other_linear' def splitfactor(p, DE, coefficientD=False, z=None): """ Splitting factorization. Given a derivation D on k[t] and p in k[t], return (p_n, p_s) in k[t] x k[t] such that p = p_n*p_s, p_s is special, and each square factor of p_n is normal. Page. 100 """ kinv = [1/x for x in DE.T[:DE.level]] if z: kinv.append(z) One = Poly(1, DE.t, domain=p.get_domain()) Dp = derivation(p, DE, coefficientD=coefficientD) # XXX: Is this right? if p.is_zero: return (p, One) if not p.has(DE.t): s = p.as_poly(*kinv).gcd(Dp.as_poly(*kinv)).as_poly(DE.t) n = p.exquo(s) return (n, s) if not Dp.is_zero: h = p.gcd(Dp).to_field() g = p.gcd(p.diff(DE.t)).to_field() s = h.exquo(g) if s.degree(DE.t) == 0: return (p, One) q_split = splitfactor(p.exquo(s), DE, coefficientD=coefficientD) return (q_split[0], q_split[1]*s) else: return (p, One) def splitfactor_sqf(p, DE, coefficientD=False, z=None, basic=False): """ Splitting Square-free Factorization Given a derivation D on k[t] and p in k[t], returns (N1, ..., Nm) and (S1, ..., Sm) in k[t]^m such that p = (N1*N2**2*...*Nm**m)*(S1*S2**2*...*Sm**m) is a splitting factorization of p and the Ni and Si are square-free and coprime. """ # TODO: This algorithm appears to be faster in every case # TODO: Verify this and splitfactor() for multiple extensions kkinv = [1/x for x in DE.T[:DE.level]] + DE.T[:DE.level] if z: kkinv = [z] S = [] N = [] p_sqf = p.sqf_list_include() if p.is_zero: return (((p, 1),), ()) for pi, i in p_sqf: Si = pi.as_poly(*kkinv).gcd(derivation(pi, DE, coefficientD=coefficientD,basic=basic).as_poly(*kkinv)).as_poly(DE.t) pi = Poly(pi, DE.t) Si = Poly(Si, DE.t) Ni = pi.exquo(Si) if not Si.is_one: S.append((Si, i)) if not Ni.is_one: N.append((Ni, i)) return (tuple(N), tuple(S)) def canonical_representation(a, d, DE): """ Canonical Representation. Given a derivation D on k[t] and f = a/d in k(t), return (f_p, f_s, f_n) in k[t] x k(t) x k(t) such that f = f_p + f_s + f_n is the canonical representation of f (f_p is a polynomial, f_s is reduced (has a special denominator), and f_n is simple (has a normal denominator). """ # Make d monic l = Poly(1/d.LC(), DE.t) a, d = a.mul(l), d.mul(l) q, r = a.div(d) dn, ds = splitfactor(d, DE) b, c = gcdex_diophantine(dn.as_poly(DE.t), ds.as_poly(DE.t), r.as_poly(DE.t)) b, c = b.as_poly(DE.t), c.as_poly(DE.t) return (q, (b, ds), (c, dn)) def hermite_reduce(a, d, DE): """ Hermite Reduction - Mack's Linear Version. Given a derivation D on k(t) and f = a/d in k(t), returns g, h, r in k(t) such that f = Dg + h + r, h is simple, and r is reduced. """ # Make d monic l = Poly(1/d.LC(), DE.t) a, d = a.mul(l), d.mul(l) fp, fs, fn = canonical_representation(a, d, DE) a, d = fn l = Poly(1/d.LC(), DE.t) a, d = a.mul(l), d.mul(l) ga = Poly(0, DE.t) gd = Poly(1, DE.t) dd = derivation(d, DE) dm = gcd(d, dd).as_poly(DE.t) ds, r = d.div(dm) while dm.degree(DE.t)>0: ddm = derivation(dm, DE) dm2 = gcd(dm, ddm) dms, r = dm.div(dm2) ds_ddm = ds.mul(ddm) ds_ddm_dm, r = ds_ddm.div(dm) b, c = gcdex_diophantine(-ds_ddm_dm.as_poly(DE.t), dms.as_poly(DE.t), a.as_poly(DE.t)) b, c = b.as_poly(DE.t), c.as_poly(DE.t) db = derivation(b, DE).as_poly(DE.t) ds_dms, r = ds.div(dms) a = c.as_poly(DE.t) - db.mul(ds_dms).as_poly(DE.t) ga = ga*dm + b*gd gd = gd*dm ga, gd = ga.cancel(gd, include=True) dm = dm2 d = ds q, r = a.div(d) ga, gd = ga.cancel(gd, include=True) r, d = r.cancel(d, include=True) rra = q*fs[1] + fp*fs[1] + fs[0] rrd = fs[1] rra, rrd = rra.cancel(rrd, include=True) return ((ga, gd), (r, d), (rra, rrd)) def polynomial_reduce(p, DE): """ Polynomial Reduction. Given a derivation D on k(t) and p in k[t] where t is a nonlinear monomial over k, return q, r in k[t] such that p = Dq + r, and deg(r) < deg_t(Dt). """ q = Poly(0, DE.t) while p.degree(DE.t) >= DE.d.degree(DE.t): m = p.degree(DE.t) - DE.d.degree(DE.t) + 1 q0 = Poly(DE.t**m, DE.t).mul(Poly(p.as_poly(DE.t).LC()/ (m*DE.d.LC()), DE.t)) q += q0 p = p - derivation(q0, DE) return (q, p) def laurent_series(a, d, F, n, DE): """ Contribution of F to the full partial fraction decomposition of A/D Given a field K of characteristic 0 and A,D,F in K[x] with D monic, nonzero, coprime with A, and F the factor of multiplicity n in the square- free factorization of D, return the principal parts of the Laurent series of A/D at all the zeros of F. """ if F.degree()==0: return 0 Z = _symbols('z', n) Z.insert(0, z) delta_a = Poly(0, DE.t) delta_d = Poly(1, DE.t) E = d.quo(F**n) ha, hd = (a, E*Poly(z**n, DE.t)) dF = derivation(F,DE) B, G = gcdex_diophantine(E, F, Poly(1,DE.t)) C, G = gcdex_diophantine(dF, F, Poly(1,DE.t)) # initialization F_store = F V, DE_D_list, H_list= [], [], [] for j in range(0, n): # jth derivative of z would be substituted with dfnth/(j+1) where dfnth =(d^n)f/(dx)^n F_store = derivation(F_store, DE) v = (F_store.as_expr())/(j + 1) V.append(v) DE_D_list.append(Poly(Z[j + 1],Z[j])) DE_new = DifferentialExtension(extension = {'D': DE_D_list}) #a differential indeterminate for j in range(0, n): zEha = Poly(z**(n + j), DE.t)*E**(j + 1)*ha zEhd = hd Pa, Pd = cancel((zEha, zEhd))[1], cancel((zEha, zEhd))[2] Q = Pa.quo(Pd) for i in range(0, j + 1): Q = Q.subs(Z[i], V[i]) Dha = hd*derivation(ha, DE, basic=True) + ha*derivation(hd, DE, basic=True) Dha += hd*derivation(ha, DE_new, basic=True) + ha*derivation(hd, DE_new, basic=True) Dhd = Poly(j + 1, DE.t)*hd**2 ha, hd = Dha, Dhd Ff, Fr = F.div(gcd(F, Q)) F_stara, F_stard = frac_in(Ff, DE.t) if F_stara.degree(DE.t) - F_stard.degree(DE.t) > 0: QBC = Poly(Q, DE.t)*B**(1 + j)*C**(n + j) H = QBC H_list.append(H) H = (QBC*F_stard).rem(F_stara) alphas = real_roots(F_stara) for alpha in list(alphas): delta_a = delta_a*Poly((DE.t - alpha)**(n - j), DE.t) + Poly(H.eval(alpha), DE.t) delta_d = delta_d*Poly((DE.t - alpha)**(n - j), DE.t) return (delta_a, delta_d, H_list) def recognize_derivative(a, d, DE, z=None): """ Compute the squarefree factorization of the denominator of f and for each Di the polynomial H in K[x] (see Theorem 2.7.1), using the LaurentSeries algorithm. Write Di = GiEi where Gj = gcd(Hn, Di) and gcd(Ei,Hn) = 1. Since the residues of f at the roots of Gj are all 0, and the residue of f at a root alpha of Ei is Hi(a) != 0, f is the derivative of a rational function if and only if Ei = 1 for each i, which is equivalent to Di | H[-1] for each i. """ flag =True a, d = a.cancel(d, include=True) q, r = a.div(d) Np, Sp = splitfactor_sqf(d, DE, coefficientD=True, z=z) j = 1 for (s, i) in Sp: delta_a, delta_d, H = laurent_series(r, d, s, j, DE) g = gcd(d, H[-1]).as_poly() if g is not d: flag = False break j = j + 1 return flag def recognize_log_derivative(a, d, DE, z=None): """ There exists a v in K(x)* such that f = dv/v where f a rational function if and only if f can be written as f = A/D where D is squarefree,deg(A) < deg(D), gcd(A, D) = 1, and all the roots of the Rothstein-Trager resultant are integers. In that case, any of the Rothstein-Trager, Lazard-Rioboo-Trager or Czichowski algorithm produces u in K(x) such that du/dx = uf. """ z = z or Dummy('z') a, d = a.cancel(d, include=True) p, a = a.div(d) pz = Poly(z, DE.t) Dd = derivation(d, DE) q = a - pz*Dd r, R = d.resultant(q, includePRS=True) r = Poly(r, z) Np, Sp = splitfactor_sqf(r, DE, coefficientD=True, z=z) for s, i in Sp: # TODO also consider the complex roots # incase we have complex roots it should turn the flag false a = real_roots(s.as_poly(z)) if any(not j.is_Integer for j in a): return False return True def residue_reduce(a, d, DE, z=None, invert=True): """ Lazard-Rioboo-Rothstein-Trager resultant reduction. Given a derivation D on k(t) and f in k(t) simple, return g elementary over k(t) and a Boolean b in {True, False} such that f - Dg in k[t] if b == True or f + h and f + h - Dg do not have an elementary integral over k(t) for any h in k (reduced) if b == False. Returns (G, b), where G is a tuple of tuples of the form (s_i, S_i), such that g = Add(*[RootSum(s_i, lambda z: z*log(S_i(z, t))) for S_i, s_i in G]). f - Dg is the remaining integral, which is elementary only if b == True, and hence the integral of f is elementary only if b == True. f - Dg is not calculated in this function because that would require explicitly calculating the RootSum. Use residue_reduce_derivation(). """ # TODO: Use log_to_atan() from rationaltools.py # If r = residue_reduce(...), then the logarithmic part is given by: # sum([RootSum(a[0].as_poly(z), lambda i: i*log(a[1].as_expr()).subs(z, # i)).subs(t, log(x)) for a in r[0]]) z = z or Dummy('z') a, d = a.cancel(d, include=True) a, d = a.to_field().mul_ground(1/d.LC()), d.to_field().mul_ground(1/d.LC()) kkinv = [1/x for x in DE.T[:DE.level]] + DE.T[:DE.level] if a.is_zero: return ([], True) p, a = a.div(d) pz = Poly(z, DE.t) Dd = derivation(d, DE) q = a - pz*Dd if Dd.degree(DE.t) <= d.degree(DE.t): r, R = d.resultant(q, includePRS=True) else: r, R = q.resultant(d, includePRS=True) R_map, H = {}, [] for i in R: R_map[i.degree()] = i r = Poly(r, z) Np, Sp = splitfactor_sqf(r, DE, coefficientD=True, z=z) for s, i in Sp: if i == d.degree(DE.t): s = Poly(s, z).monic() H.append((s, d)) else: h = R_map.get(i) if h is None: continue h_lc = Poly(h.as_poly(DE.t).LC(), DE.t, field=True) h_lc_sqf = h_lc.sqf_list_include(all=True) for a, j in h_lc_sqf: h = Poly(h, DE.t, field=True).exquo(Poly(gcd(a, s**j, *kkinv), DE.t)) s = Poly(s, z).monic() if invert: h_lc = Poly(h.as_poly(DE.t).LC(), DE.t, field=True, expand=False) inv, coeffs = h_lc.as_poly(z, field=True).invert(s), [S(1)] for coeff in h.coeffs()[1:]: L = reduced(inv*coeff, [s])[1] coeffs.append(L.as_expr()) h = Poly(dict(list(zip(h.monoms(), coeffs))), DE.t) H.append((s, h)) b = all([not cancel(i.as_expr()).has(DE.t, z) for i, _ in Np]) return (H, b) def residue_reduce_to_basic(H, DE, z): """ Converts the tuple returned by residue_reduce() into a Basic expression. """ # TODO: check what Lambda does with RootOf i = Dummy('i') s = list(zip(reversed(DE.T), reversed([f(DE.x) for f in DE.Tfuncs]))) return sum((RootSum(a[0].as_poly(z), Lambda(i, i*log(a[1].as_expr()).subs( {z: i}).subs(s))) for a in H)) def residue_reduce_derivation(H, DE, z): """ Computes the derivation of an expression returned by residue_reduce(). In general, this is a rational function in t, so this returns an as_expr() result. """ # TODO: verify that this is correct for multiple extensions i = Dummy('i') return S(sum((RootSum(a[0].as_poly(z), Lambda(i, i*derivation(a[1], DE).as_expr().subs(z, i)/a[1].as_expr().subs(z, i))) for a in H))) def integrate_primitive_polynomial(p, DE): """ Integration of primitive polynomials. Given a primitive monomial t over k, and p in k[t], return q in k[t], r in k, and a bool b in {True, False} such that r = p - Dq is in k if b is True, or r = p - Dq does not have an elementary integral over k(t) if b is False. """ from sympy.integrals.prde import limited_integrate Zero = Poly(0, DE.t) q = Poly(0, DE.t) if not p.has(DE.t): return (Zero, p, True) while True: if not p.has(DE.t): return (q, p, True) Dta, Dtb = frac_in(DE.d, DE.T[DE.level - 1]) with DecrementLevel(DE): # We had better be integrating the lowest extension (x) # with ratint(). a = p.LC() aa, ad = frac_in(a, DE.t) try: (ba, bd), c = limited_integrate(aa, ad, [(Dta, Dtb)], DE) assert len(c) == 1 except NonElementaryIntegralException: return (q, p, False) m = p.degree(DE.t) q0 = c[0].as_poly(DE.t)*Poly(DE.t**(m + 1)/(m + 1), DE.t) + \ (ba.as_expr()/bd.as_expr()).as_poly(DE.t)*Poly(DE.t**m, DE.t) p = p - derivation(q0, DE) q = q + q0 def integrate_primitive(a, d, DE, z=None): """ Integration of primitive functions. Given a primitive monomial t over k and f in k(t), return g elementary over k(t), i in k(t), and b in {True, False} such that i = f - Dg is in k if b is True or i = f - Dg does not have an elementary integral over k(t) if b is False. This function returns a Basic expression for the first argument. If b is True, the second argument is Basic expression in k to recursively integrate. If b is False, the second argument is an unevaluated Integral, which has been proven to be nonelementary. """ # XXX: a and d must be canceled, or this might return incorrect results z = z or Dummy("z") s = list(zip(reversed(DE.T), reversed([f(DE.x) for f in DE.Tfuncs]))) g1, h, r = hermite_reduce(a, d, DE) g2, b = residue_reduce(h[0], h[1], DE, z=z) if not b: i = cancel(a.as_expr()/d.as_expr() - (g1[1]*derivation(g1[0], DE) - g1[0]*derivation(g1[1], DE)).as_expr()/(g1[1]**2).as_expr() - residue_reduce_derivation(g2, DE, z)) i = NonElementaryIntegral(cancel(i).subs(s), DE.x) return ((g1[0].as_expr()/g1[1].as_expr()).subs(s) + residue_reduce_to_basic(g2, DE, z), i, b) # h - Dg2 + r p = cancel(h[0].as_expr()/h[1].as_expr() - residue_reduce_derivation(g2, DE, z) + r[0].as_expr()/r[1].as_expr()) p = p.as_poly(DE.t) q, i, b = integrate_primitive_polynomial(p, DE) ret = ((g1[0].as_expr()/g1[1].as_expr() + q.as_expr()).subs(s) + residue_reduce_to_basic(g2, DE, z)) if not b: # TODO: This does not do the right thing when b is False i = NonElementaryIntegral(cancel(i.as_expr()).subs(s), DE.x) else: i = cancel(i.as_expr()) return (ret, i, b) def integrate_hyperexponential_polynomial(p, DE, z): """ Integration of hyperexponential polynomials. Given a hyperexponential monomial t over k and p in k[t, 1/t], return q in k[t, 1/t] and a bool b in {True, False} such that p - Dq in k if b is True, or p - Dq does not have an elementary integral over k(t) if b is False. """ from sympy.integrals.rde import rischDE t1 = DE.t dtt = DE.d.exquo(Poly(DE.t, DE.t)) qa = Poly(0, DE.t) qd = Poly(1, DE.t) b = True if p.is_zero: return(qa, qd, b) with DecrementLevel(DE): for i in xrange(-p.degree(z), p.degree(t1) + 1): if not i: continue elif i < 0: # If you get AttributeError: 'NoneType' object has no attribute 'nth' # then this should really not have expand=False # But it shouldn't happen because p is already a Poly in t and z a = p.as_poly(z, expand=False).nth(-i) else: # If you get AttributeError: 'NoneType' object has no attribute 'nth' # then this should really not have expand=False a = p.as_poly(t1, expand=False).nth(i) aa, ad = frac_in(a, DE.t, field=True) aa, ad = aa.cancel(ad, include=True) iDt = Poly(i, t1)*dtt iDta, iDtd = frac_in(iDt, DE.t, field=True) try: va, vd = rischDE(iDta, iDtd, Poly(aa, DE.t), Poly(ad, DE.t), DE) va, vd = frac_in((va, vd), t1) except NonElementaryIntegralException: b = False else: qa = qa*vd + va*Poly(t1**i)*qd qd *= vd return (qa, qd, b) def integrate_hyperexponential(a, d, DE, z=None, conds='piecewise'): """ Integration of hyperexponential functions. Given a hyperexponential monomial t over k and f in k(t), return g elementary over k(t), i in k(t), and a bool b in {True, False} such that i = f - Dg is in k if b is True or i = f - Dg does not have an elementary integral over k(t) if b is False. This function returns a Basic expression for the first argument. If b is True, the second argument is Basic expression in k to recursively integrate. If b is False, the second argument is an unevaluated Integral, which has been proven to be nonelementary. """ # XXX: a and d must be canceled, or this might return incorrect results z = z or Dummy("z") s = list(zip(reversed(DE.T), reversed([f(DE.x) for f in DE.Tfuncs]))) g1, h, r = hermite_reduce(a, d, DE) g2, b = residue_reduce(h[0], h[1], DE, z=z) if not b: i = cancel(a.as_expr()/d.as_expr() - (g1[1]*derivation(g1[0], DE) - g1[0]*derivation(g1[1], DE)).as_expr()/(g1[1]**2).as_expr() - residue_reduce_derivation(g2, DE, z)) i = NonElementaryIntegral(cancel(i.subs(s)), DE.x) return ((g1[0].as_expr()/g1[1].as_expr()).subs(s) + residue_reduce_to_basic(g2, DE, z), i, b) # p should be a polynomial in t and 1/t, because Sirr == k[t, 1/t] # h - Dg2 + r p = cancel(h[0].as_expr()/h[1].as_expr() - residue_reduce_derivation(g2, DE, z) + r[0].as_expr()/r[1].as_expr()) pp = as_poly_1t(p, DE.t, z) qa, qd, b = integrate_hyperexponential_polynomial(pp, DE, z) i = pp.nth(0, 0) ret = ((g1[0].as_expr()/g1[1].as_expr()).subs(s) \ + residue_reduce_to_basic(g2, DE, z)) qas = qa.as_expr().subs(s) qds = qd.as_expr().subs(s) if conds == 'piecewise' and DE.x not in qds.free_symbols: # We have to be careful if the exponent is S.Zero! # XXX: Does qd = 0 always necessarily correspond to the exponential # equaling 1? ret += Piecewise( (integrate((p - i).subs(DE.t, 1).subs(s), DE.x), Eq(qds, 0)), (qas/qds, True) ) else: ret += qas/qds if not b: i = p - (qd*derivation(qa, DE) - qa*derivation(qd, DE)).as_expr()/\ (qd**2).as_expr() i = NonElementaryIntegral(cancel(i).subs(s), DE.x) return (ret, i, b) def integrate_hypertangent_polynomial(p, DE): """ Integration of hypertangent polynomials. Given a differential field k such that sqrt(-1) is not in k, a hypertangent monomial t over k, and p in k[t], return q in k[t] and c in k such that p - Dq - c*D(t**2 + 1)/(t**1 + 1) is in k and p - Dq does not have an elementary integral over k(t) if Dc != 0. """ # XXX: Make sure that sqrt(-1) is not in k. q, r = polynomial_reduce(p, DE) a = DE.d.exquo(Poly(DE.t**2 + 1, DE.t)) c = Poly(r.nth(1)/(2*a.as_expr()), DE.t) return (q, c) def integrate_nonlinear_no_specials(a, d, DE, z=None): """ Integration of nonlinear monomials with no specials. Given a nonlinear monomial t over k such that Sirr ({p in k[t] | p is special, monic, and irreducible}) is empty, and f in k(t), returns g elementary over k(t) and a Boolean b in {True, False} such that f - Dg is in k if b == True, or f - Dg does not have an elementary integral over k(t) if b == False. This function is applicable to all nonlinear extensions, but in the case where it returns b == False, it will only have proven that the integral of f - Dg is nonelementary if Sirr is empty. This function returns a Basic expression. """ # TODO: Integral from k? # TODO: split out nonelementary integral # XXX: a and d must be canceled, or this might not return correct results z = z or Dummy("z") s = list(zip(reversed(DE.T), reversed([f(DE.x) for f in DE.Tfuncs]))) g1, h, r = hermite_reduce(a, d, DE) g2, b = residue_reduce(h[0], h[1], DE, z=z) if not b: return ((g1[0].as_expr()/g1[1].as_expr()).subs(s) + residue_reduce_to_basic(g2, DE, z), b) # Because f has no specials, this should be a polynomial in t, or else # there is a bug. p = cancel(h[0].as_expr()/h[1].as_expr() - residue_reduce_derivation(g2, DE, z).as_expr() + r[0].as_expr()/r[1].as_expr()).as_poly(DE.t) q1, q2 = polynomial_reduce(p, DE) if q2.has(DE.t): b = False else: b = True ret = (cancel(g1[0].as_expr()/g1[1].as_expr() + q1.as_expr()).subs(s) + residue_reduce_to_basic(g2, DE, z)) return (ret, b) class NonElementaryIntegral(Integral): """ Represents a nonelementary Integral. If the result of integrate() is an instance of this class, it is guaranteed to be nonelementary. Note that integrate() by default will try to find any closed-form solution, even in terms of special functions which may themselves not be elementary. To make integrate() only give elementary solutions, or, in the cases where it can prove the integral to be nonelementary, instances of this class, use integrate(risch=True). In this case, integrate() may raise NotImplementedError if it cannot make such a determination. integrate() uses the deterministic Risch algorithm to integrate elementary functions or prove that they have no elementary integral. In some cases, this algorithm can split an integral into an elementary and nonelementary part, so that the result of integrate will be the sum of an elementary expression and a NonElementaryIntegral. Example ======= >>> from sympy import integrate, exp, log, Integral >>> from sympy.abc import x >>> a = integrate(exp(-x**2), x, risch=True) >>> print(a) Integral(exp(-x**2), x) >>> type(a) >>> expr = (2*log(x)**2 - log(x) - x**2)/(log(x)**3 - x**2*log(x)) >>> b = integrate(expr, x, risch=True) >>> print(b) -log(-x + log(x))/2 + log(x + log(x))/2 + Integral(1/log(x), x) >>> type(b.atoms(Integral).pop()) """ # TODO: This is useful in and of itself, because isinstance(result, # NonElementaryIntegral) will tell if the integral has been proven to be # elementary. But should we do more? Perhaps a no-op .doit() if # elementary=True? Or maybe some information on why the integral is # nonelementary. pass def risch_integrate(f, x, extension=None, handle_first='log', separate_integral=False, rewrite_complex=False, conds='piecewise'): r""" The Risch Integration Algorithm. Only transcendental functions are supported. Currently, only exponentials and logarithms are supported, but support for trigonometric functions is forthcoming. If this function returns an unevaluated Integral in the result, it means that it has proven that integral to be nonelementary. Any errors will result in raising NotImplementedError. The unevaluated Integral will be an instance of NonElementaryIntegral, a subclass of Integral. handle_first may be either 'exp' or 'log'. This changes the order in which the extension is built, and may result in a different (but equivalent) solution (for an example of this, see issue 2010). It is also possible that the integral may be computed with one but not the other, because not all cases have been implemented yet. It defaults to 'log' so that the outer extension is exponential when possible, because more of the exponential case has been implemented. If separate_integral is True, the result is returned as a tuple (ans, i), where the integral is ans + i, ans is elementary, and i is either a NonElementaryIntegral or 0. This useful if you want to try further integrating the NonElementaryIntegral part using other algorithms to possibly get a solution in terms of special functions. It is False by default. Examples ======== >>> from sympy.integrals.risch import risch_integrate >>> from sympy import exp, log, pprint >>> from sympy.abc import x First, we try integrating exp(-x**2). Except for a constant factor of 2/sqrt(pi), this is the famous error function. >>> pprint(risch_integrate(exp(-x**2), x)) / | | 2 | -x | e dx | / The unevaluated Integral in the result means that risch_integrate() has proven that exp(-x**2) does not have an elementary anti-derivative. In many cases, risch_integrate() can split out the elementary anti-derivative part from the nonelementary anti-derivative part. For example, >>> pprint(risch_integrate((2*log(x)**2 - log(x) - x**2)/(log(x)**3 - ... x**2*log(x)), x)) / | log(-x + log(x)) log(x + log(x)) | 1 - ---------------- + --------------- + | ------ dx 2 2 | log(x) | / This means that it has proven that the integral of 1/log(x) is nonelementary. This function is also known as the logarithmic integral, and is often denoted as Li(x). risch_integrate() currently only accepts purely transcendental functions with exponentials and logarithms, though note that this can include nested exponentials and logarithms, as well as exponentials with bases other than E. >>> pprint(risch_integrate(exp(x)*exp(exp(x)), x)) / x\ \e / e >>> pprint(risch_integrate(exp(exp(x)), x)) / | | / x\ | \e / | e dx | / >>> pprint(risch_integrate(x*x**x*log(x) + x**x + x*x**x, x)) x x*x >>> pprint(risch_integrate(x**x*log(x), x)) / | | x | x *log(x) dx | / >>> pprint(risch_integrate(-1/(x*log(x)*log(log(x))**2), x)) 1 ----------- log(log(x)) """ f = S(f) DE = extension or DifferentialExtension(f, x, handle_first=handle_first, rewrite_complex=rewrite_complex) fa, fd = DE.fa, DE.fd result = S(0) for case in reversed(DE.cases): if not DE.fa.has(DE.t) and not fd.has(DE.t) and not case == 'base': DE.decrement_level() fa, fd = frac_in((fa, fd), DE.t) continue fa, fd = fa.cancel(fd, include=True) if case == 'exp': ans, i, b = integrate_hyperexponential(fa, fd, DE, conds=conds) elif case == 'primitive': ans, i, b = integrate_primitive(fa, fd, DE) elif case == 'base': # XXX: We can't call ratint() directly here because it doesn't # handle polynomials correctly. ans = integrate(fa.as_expr()/fd.as_expr(), DE.x, risch=False) b = False i = S(0) else: raise NotImplementedError("Only exponential and logarithmic " "extensions are currently supported.") result += ans if b: DE.decrement_level() fa, fd = frac_in(i, DE.t) else: result = result.subs(DE.backsubs) if not i.is_zero: i = NonElementaryIntegral(i.function.subs(DE.backsubs),i.limits) if not separate_integral: result += i return result else: if isinstance(i, NonElementaryIntegral): return (result, i) else: return (result, 0) sympy-0.7.4.1/sympy/integrals/integrals.py0000644000175000017500000014255612253362407020774 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.concrete.expr_with_limits import AddWithLimits from sympy.core.add import Add from sympy.core.basic import Basic, C from sympy.core.compatibility import is_sequence, xrange from sympy.core.containers import Tuple from sympy.core.expr import Expr from sympy.core.function import diff from sympy.core.numbers import oo from sympy.core.relational import Eq from sympy.core.sets import Interval from sympy.core.singleton import S from sympy.core.symbol import (Dummy, Symbol, Wild) from sympy.core.sympify import sympify from sympy.integrals.manualintegrate import manualintegrate from sympy.integrals.trigonometry import trigintegrate from sympy.integrals.deltafunctions import deltaintegrate from sympy.integrals.rationaltools import ratint from sympy.integrals.heurisch import heurisch, heurisch_wrapper from sympy.integrals.meijerint import meijerint_definite, meijerint_indefinite from sympy.utilities import xthreaded, flatten from sympy.utilities.misc import filldedent from sympy.polys import Poly, PolynomialError from sympy.solvers.solvers import solve, posify from sympy.functions import Piecewise, sqrt, sign from sympy.geometry import Curve from sympy.functions.elementary.piecewise import piecewise_fold from sympy.series import limit # TODO get these helper functions into a super class for sum-like # objects: Sum, Product, Integral (issue 3662) class Integral(AddWithLimits): """Represents unevaluated integral.""" __slots__ = ['is_commutative'] def __new__(cls, function, *symbols, **assumptions): """Create an unevaluated integral. Arguments are an integrand followed by one or more limits. If no limits are given and there is only one free symbol in the expression, that symbol will be used, otherwise an error will be raised. >>> from sympy import Integral >>> from sympy.abc import x, y >>> Integral(x) Integral(x, x) >>> Integral(y) Integral(y, y) When limits are provided, they are interpreted as follows (using ``x`` as though it were the variable of integration): (x,) or x - indefinite integral (x, a) - "evaluate at" integral is an abstract antiderivative (x, a, b) - definite integral The ``as_dummy`` method can be used to see which symbols cannot be targeted by subs: those with a preppended underscore cannot be changed with ``subs``. (Also, the integration variables themselves -- the first element of a limit -- can never be changed by subs.) >>> i = Integral(x, x) >>> at = Integral(x, (x, x)) >>> i.as_dummy() Integral(x, x) >>> at.as_dummy() Integral(_x, (_x, x)) """ obj = AddWithLimits.__new__(cls, function, *symbols, **assumptions) return obj def __getnewargs__(self): return (self.function,) + tuple([tuple(xab) for xab in self.limits]) @property def free_symbols(self): """ This method returns the symbols that will exist when the integral is evaluated. This is useful if one is trying to determine whether an integral depends on a certain symbol or not. Examples ======== >>> from sympy import Integral >>> from sympy.abc import x, y >>> Integral(x, (x, y, 1)).free_symbols set([y]) See Also ======== function, limits, variables """ for xab in self.limits: if len(xab) == 3 and xab[1] == xab[2]: return set() return AddWithLimits.free_symbols.fget(self) @property def is_zero(self): """Since Integral doesn't autosimplify it it useful to see if it would simplify to zero or not in a trivial manner, i.e. when the function is 0 or two limits of a definite integral are the same. This is a very naive and quick test, not intended to check for special patterns like Integral(sin(m*x)*cos(n*x), (x, 0, 2*pi)) == 0. Examples ======== >>> from sympy import Integral >>> from sympy.abc import x, y, z >>> Integral(1, (x, 1, 1)).is_zero True >>> Integral(0, (x, y, z)).is_zero True >>> Integral(1, (x, 1, 2)).is_zero False See Also ======== is_number """ if (self.function.is_zero or any(len(xab) == 3 and xab[1] == xab[2] for xab in self.limits)): return True if not self.free_symbols and self.function.is_number: # the integrand is a number and the limits are numerical return False @property def is_number(self): """ Return True if the Integral will result in a number, else False. Integrals 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. Examples ======== >>> from sympy import Integral >>> from sympy.abc import x, y >>> Integral(x).is_number False >>> Integral(x, y).is_number False >>> Integral(x, (y, 1, x)).is_number False >>> Integral(x, (y, 1, 2)).is_number False >>> Integral(x, (y, 1, 1)).is_number True >>> Integral(x, (x, 1, 2)).is_number True >>> Integral(x*y, (x, 1, 2), (y, 1, 3)).is_number True >>> Integral(1, x, (x, 1, 2)).is_number True See Also ======== is_zero """ integrand, limits = self.function, self.limits isyms = integrand.atoms(Symbol) for xab in limits: if len(xab) == 1: isyms.add(xab[0]) continue # it may be removed later elif len(xab) == 3 and xab[1] == xab[2]: # XXX naive equality test return True # integral collapsed if xab[0] in isyms: # take it out of the symbols since it will be replace # with whatever the limits of the integral are isyms.remove(xab[0]) # add in the new symbols for i in xab[1:]: isyms.update(i.free_symbols) # if there are no surviving symbols then the result is a number return len(isyms) == 0 def transform(self, x, u, inverse=False): r""" Performs a change of variables from `x` to `u` using the relationship given by `x` and `u` which will define the transformations `f` and `F` (which are inverses of each other) as follows: 1) If `x` is a Symbol (which is a variable of integration) then `u` will be interpreted as some function, f(u), with inverse F(u). This, in effect, just makes the substitution of x with f(x). 2) If `u` is a Symbol then `x` will be interpreted as some function, F(x), with inverse f(u). This is commonly referred to as u-substitution. The `inverse` option will reverse `x` and `u`. It is a deprecated option since `x` and `u` can just be passed in reverse order. Once f and F have been identified, the transformation is made as follows: .. math:: \int_a^b x \mathrm{d}x \rightarrow \int_{F(a)}^{F(b)} f(x) \frac{\mathrm{d}}{\mathrm{d}x} where `F(x)` is the inverse of `f(x)` and the limits and integrand have been corrected so as to retain the same value after integration. Notes ===== The mappings, F(x) or f(u), must lead to a unique integral. Linear or rational linear expression, `2*x`, `1/x` and `sqrt(x)`, will always work; quadratic expressions like `x**2 - 1` are acceptable as long as the resulting integrand does not depend on the sign of the solutions (see examples). The integral will be returned unchanged if `x` is not a variable of integration. `x` must be (or contain) only one of of the integration variables. If `u` has more than one free symbol then it should be sent as a tuple (`u`, `uvar`) where `uvar` identifies which variable is replacing the integration variable. XXX can it contain another integration variable? Examples ======== >>> from sympy.abc import a, b, c, d, x, u, y >>> from sympy import Integral, S, cos, sqrt >>> i = Integral(x*cos(x**2 - 1), (x, 0, 1)) transform can change the variable of integration >>> i.transform(x, u) Integral(u*cos(u**2 - 1), (u, 0, 1)) transform can perform u-substitution as long as a unique integrand is obtained: >>> i.transform(x**2 - 1, u) Integral(cos(u)/2, (u, -1, 0)) This attempt fails because x = +/-sqrt(u + 1) and the sign does not cancel out of the integrand: >>> Integral(cos(x**2 - 1), (x, 0, 1)).transform(x**2 - 1, u) Traceback (most recent call last): ... ValueError: The mapping between F(x) and f(u) did not give a unique integrand. transform can do a substitution. Here, the previous result is transformed back into the original expression using "u-substitution": >>> ui = _ >>> _.transform(sqrt(u + 1), x) == i True We can accomplish the same with a regular substitution: >>> ui.transform(u, x**2 - 1) == i True If the `x` does not contain a symbol of integration then the integral will be returned unchanged. Integral `i` does not have an integration variable `a` so no change is made: >>> i.transform(a, x) == i True When `u` has more than one free symbol the symbol that is replacing `x` must be identified by passing `u` as a tuple: >>> Integral(x, (x, 0, 1)).transform(x, (u + a, u)) Integral(a + u, (u, -a, -a + 1)) >>> Integral(x, (x, 0, 1)).transform(x, (u + a, a)) Integral(a + u, (a, -u, -u + 1)) See Also ======== variables : Lists the integration variables as_dummy : Replace integration variables with dummy ones """ if inverse: # when this is removed, update the docstring from sympy.utilities.exceptions import SymPyDeprecationWarning SymPyDeprecationWarning( feature="transform(x, f(x), inverse=True)", useinstead="transform(f(x), x)", issue=3380, deprecated_since_version="0.7.2", ).warn() # in the old style x and u contained the same variable so # don't worry about using the old-style feature with the # new style input...but it will still work: # i.transform(x, u).transform(x, u, inverse=True) -> i x, u = u, x d = Dummy('d') xfree = x.free_symbols.intersection(self.variables) if len(xfree) > 1: raise ValueError( 'F(x) can only contain one of: %s' % self.variables) xvar = xfree.pop() if xfree else d if xvar not in self.variables: return self u = sympify(u) if isinstance(u, Expr): ufree = u.free_symbols if len(ufree) != 1: raise ValueError(filldedent(''' When f(u) has more than one free symbol, the one replacing x must be identified: pass f(u) as (f(u), u)''')) uvar = ufree.pop() else: u, uvar = u if uvar not in u.free_symbols: raise ValueError(filldedent(''' Expecting a tuple (expr, symbol) where symbol identified a free symbol in expr, but symbol is not in expr's free symbols.''')) if not isinstance(uvar, Symbol): raise ValueError(filldedent(''' Expecting a tuple (expr, symbol) but didn't get a symbol; got %s''' % uvar)) if x.is_Symbol and u.is_Symbol: return self.xreplace({x: u}) if not x.is_Symbol and not u.is_Symbol: raise ValueError('either x or u must be a symbol') if uvar == xvar: return self.transform(x, u.subs(uvar, d)).xreplace({d: uvar}) if uvar in self.limits: raise ValueError(filldedent(''' u must contain the same variable as in x or a variable that is not already an integration variable''')) if not x.is_Symbol: F = [x.subs(xvar, d)] soln = solve(u - x, xvar, check=False) if not soln: raise ValueError('no solution for solve(F(x) - f(u), x)') f = [fi.subs(uvar, d) for fi in soln] else: f = [u.subs(uvar, d)] pdiff, reps = posify(u - x) puvar = uvar.subs([(v, k) for k, v in reps.items()]) soln = [s.subs(reps) for s in solve(pdiff, puvar)] if not soln: raise ValueError('no solution for solve(F(x) - f(u), u)') F = [fi.subs(xvar, d) for fi in soln] newfuncs = set([(self.function.subs(xvar, fi)*fi.diff(d) ).subs(d, uvar) for fi in f]) if len(newfuncs) > 1: raise ValueError(filldedent(''' The mapping between F(x) and f(u) did not give a unique integrand.''')) newfunc = newfuncs.pop() def _calc_limit_1(F, a, b): """ replace d with a, using subs if possible, otherwise limit where sign of b is considered """ wok = F.subs(d, a) if wok is S.NaN or wok.is_bounded is False and a.is_bounded: return limit(sign(b)*F, d, a) return wok def _calc_limit(a, b): """ replace d with a, using subs if possible, otherwise limit where sign of b is considered """ avals = list(set([_calc_limit_1(Fi, a, b) for Fi in F])) if len(avals) > 1: raise ValueError(filldedent(''' The mapping between F(x) and f(u) did not give a unique limit.''')) return avals[0] newlimits = [] for xab in self.limits: sym = xab[0] if sym == xvar: if len(xab) == 3: a, b = xab[1:] a, b = _calc_limit(a, b), _calc_limit(b, a) if a > b: a, b = b, a newfunc = -newfunc newlimits.append((uvar, a, b)) elif len(xab) == 2: a = _calc_limit(xab[1], 1) newlimits.append((uvar, a)) else: newlimits.append(uvar) else: newlimits.append(xab) return self.func(newfunc, *newlimits) def doit(self, **hints): """ Perform the integration using any hints given. Examples ======== >>> from sympy import Integral >>> from sympy.abc import x, i >>> Integral(x**i, (i, 1, 3)).doit() Piecewise((2, log(x) == 0), (x**3/log(x) - x/log(x), True)) See Also ======== sympy.integrals.trigonometry.trigintegrate sympy.integrals.risch.heurisch sympy.integrals.rationaltools.ratint as_sum : Approximate the integral using a sum """ if not hints.get('integrals', True): return self deep = hints.get('deep', True) meijerg = hints.get('meijerg', None) conds = hints.get('conds', 'piecewise') risch = hints.get('risch', None) manual = hints.get('manual', None) if conds not in ['separate', 'piecewise', 'none']: raise ValueError('conds must be one of "separate", "piecewise", ' '"none", got: %s' % conds) if risch and any(len(xab) > 1 for xab in self.limits): raise ValueError('risch=True is only allowed for indefinite integrals.') # check for the trivial case of equal upper and lower limits if self.is_zero: return S.Zero # now compute and check the function function = self.function if deep: function = function.doit(**hints) if function.is_zero: return S.Zero # There is no trivial answer, so continue undone_limits = [] # ulj = free symbols of any undone limits' upper and lower limits ulj = set() for xab in self.limits: # compute uli, the free symbols in the # Upper and Lower limits of limit I if len(xab) == 1: uli = set(xab[:1]) elif len(xab) == 2: uli = xab[1].free_symbols elif len(xab) == 3: uli = xab[1].free_symbols.union(xab[2].free_symbols) # this integral can be done as long as there is no blocking # limit that has been undone. An undone limit is blocking if # it contains an integration variable that is in this limit's # upper or lower free symbols or vice versa if xab[0] in ulj or any(v[0] in uli for v in undone_limits): undone_limits.append(xab) ulj.update(uli) continue # There are a number of tradeoffs in using the meijer g method. # It can sometimes be a lot faster than other methods, and # sometimes slower. And there are certain types of integrals for # which it is more likely to work than others. # These heuristics are incorporated in deciding what integration # methods to try, in what order. # See the integrate() docstring for details. def try_meijerg(function, xab): ret = None if len(xab) == 3 and meijerg is not False: x, a, b = xab try: res = meijerint_definite(function, x, a, b) except NotImplementedError: from sympy.integrals.meijerint import _debug _debug('NotImplementedError from meijerint_definite') res = None if res is not None: f, cond = res if conds == 'piecewise': ret = Piecewise((f, cond), (self.func(function, (x, a, b)), True)) elif conds == 'separate': if len(self.limits) != 1: raise ValueError('conds=separate not supported in ' 'multiple integrals') ret = f, cond else: ret = f return ret meijerg1 = meijerg if len(xab) == 3 and xab[1].is_real and xab[2].is_real \ and not function.is_Poly and \ (xab[1].has(oo, -oo) or xab[2].has(oo, -oo)): ret = try_meijerg(function, xab) if ret is not None: function = ret continue else: meijerg1 = False # If the special meijerg code did not succeed finding a definite # integral, then the code using meijerint_indefinite will not either # (it might find an antiderivative, but the answer is likely to be # nonsensical). # Thus if we are requested to only use meijer g-function methods, # we give up at this stage. Otherwise we just disable g-function # methods. if meijerg1 is False and meijerg is True: antideriv = None else: antideriv = self._eval_integral( function, xab[0], meijerg=meijerg1, risch=risch, manual=manual, conds=conds) if antideriv is None and meijerg1 is True: ret = try_meijerg(function, xab) if ret is not None: function = ret continue if antideriv is None: undone_limits.append(xab) else: if len(xab) == 1: function = antideriv else: if len(xab) == 3: x, a, b = xab if len(xab) == 2: x, b = xab a = None if deep: if isinstance(a, Basic): a = a.doit(**hints) if isinstance(b, Basic): b = b.doit(**hints) if antideriv.is_Poly: gens = list(antideriv.gens) gens.remove(x) antideriv = antideriv.as_expr() function = antideriv._eval_interval(x, a, b) function = Poly(function, *gens) else: try: function = antideriv._eval_interval(x, a, b) except NotImplementedError: # This can happen if _eval_interval depends in a # complicated way on limits that cannot be computed undone_limits.append(xab) if undone_limits: return self.func(*([function] + undone_limits)) return function def _eval_derivative(self, sym): """Evaluate the derivative of the current Integral object by differentiating under the integral sign [1], using the Fundamental Theorem of Calculus [2] when possible. Whenever an Integral is encountered that is equivalent to zero or has an integrand that is independent of the variable of integration those integrals are performed. All others are returned as Integral instances which can be resolved with doit() (provided they are integrable). References: [1] http://en.wikipedia.org/wiki/Differentiation_under_the_integral_sign [2] http://en.wikipedia.org/wiki/Fundamental_theorem_of_calculus Examples ======== >>> from sympy import Integral >>> from sympy.abc import x, y >>> i = Integral(x + y, y, (y, 1, x)) >>> i.diff(x) Integral(x + y, (y, x)) + Integral(1, y, (y, 1, x)) >>> i.doit().diff(x) == i.diff(x).doit() True >>> i.diff(y) 0 The previous must be true since there is no y in the evaluated integral: >>> i.free_symbols set([x]) >>> i.doit() 2*x**3/3 - x/2 - 1/6 """ # differentiate under the integral sign; we do not # check for regularity conditions (TODO), see issue 1116 # get limits and the function f, limits = self.function, list(self.limits) # the order matters if variables of integration appear in the limits # so work our way in from the outside to the inside. limit = limits.pop(-1) if len(limit) == 3: x, a, b = limit elif len(limit) == 2: x, b = limit a = None else: a = b = None x = limit[0] if limits: # f is the argument to an integral f = self.func(f, *tuple(limits)) # assemble the pieces def _do(f, ab): dab_dsym = diff(ab, sym) if not dab_dsym: return S.Zero if isinstance(f, Integral): limits = [(x, x) if (len(l) == 1 and l[0] == x) else l for l in f.limits] f = self.func(f.function, *limits) return f.subs(x, ab)*dab_dsym rv = 0 if b is not None: rv += _do(f, b) if a is not None: rv -= _do(f, a) if len(limit) == 1 and sym == x: # the dummy variable *is* also the real-world variable arg = f rv += arg else: # the dummy variable might match sym but it's # only a dummy and the actual variable is determined # by the limits, so mask off the variable of integration # while differentiating u = Dummy('u') arg = f.subs(x, u).diff(sym).subs(u, x) rv += self.func(arg, Tuple(x, a, b)) return rv def _eval_integral(self, f, x, meijerg=None, risch=None, manual=None, conds='piecewise'): """ Calculate the anti-derivative to the function f(x). The following algorithms are applied (roughly in this order): 1. Simple heuristics (based on pattern matching and integral table): - most frequently used functions (e.g. polynomials, products of trig functions) 2. Integration of rational functions: - A complete algorithm for integrating rational functions is implemented (the Lazard-Rioboo-Trager algorithm). The algorithm also uses the partial fraction decomposition algorithm implemented in apart() as a preprocessor to make this process faster. Note that the integral of a rational function is always elementary, but in general, it may include a RootSum. 3. Full Risch algorithm: - The Risch algorithm is a complete decision procedure for integrating elementary functions, which means that given any elementary function, it will either compute an elementary antiderivative, or else prove that none exists. Currently, part of transcendental case is implemented, meaning elementary integrals containing exponentials, logarithms, and (soon!) trigonometric functions can be computed. The algebraic case, e.g., functions containing roots, is much more difficult and is not implemented yet. - If the routine fails (because the integrand is not elementary, or because a case is not implemented yet), it continues on to the next algorithms below. If the routine proves that the integrals is nonelementary, it still moves on to the algorithms below, because we might be able to find a closed-form solution in terms of special functions. If risch=True, however, it will stop here. 4. The Meijer G-Function algorithm: - This algorithm works by first rewriting the integrand in terms of very general Meijer G-Function (meijerg in SymPy), integrating it, and then rewriting the result back, if possible. This algorithm is particularly powerful for definite integrals (which is actually part of a different method of Integral), since it can compute closed-form solutions of definite integrals even when no closed-form indefinite integral exists. But it also is capable of computing many indefinite integrals as well. - Another advantage of this method is that it can use some results about the Meijer G-Function to give a result in terms of a Piecewise expression, which allows to express conditionally convergent integrals. - Setting meijerg=True will cause integrate() to use only this method. 5. The "manual integration" algorithm: - This algorithm tries to mimic how a person would find an antiderivative by hand, for example by looking for a substitution or applying integration by parts. This algorithm does not handle as many integrands but can return results in a more familiar form. - Sometimes this algorithm can evaluate parts of an integral; in this case integrate() will try to evaluate the rest of the integrand using the other methods here. - Setting manual=True will cause integrate() to use only this method. 6. The Heuristic Risch algorithm: - This is a heuristic version of the Risch algorithm, meaning that it is not deterministic. This is tried as a last resort because it can be very slow. It is still used because not enough of the full Risch algorithm is implemented, so that there are still some integrals that can only be computed using this method. The goal is to implement enough of the Risch and Meijer G methods so that this can be deleted. """ from sympy.integrals.risch import risch_integrate if risch: try: return risch_integrate(f, x, conds=conds) except NotImplementedError: return None if manual: try: result = manualintegrate(f, x) if result is not None and result.func != Integral: return result except (ValueError, PolynomialError): pass # if it is a poly(x) then let the polynomial integrate itself (fast) # # It is important to make this check first, otherwise the other code # will return a sympy expression instead of a Polynomial. # # see Polynomial for details. if isinstance(f, Poly) and not meijerg: return f.integrate(x) # Piecewise antiderivatives need to call special integrate. if f.func is Piecewise: return f._eval_integral(x) # let's cut it short if `f` does not depend on `x` if not f.has(x): return f*x # try to convert to poly(x) and then integrate if successful (fast) poly = f.as_poly(x) if poly is not None and not meijerg: return poly.integrate().as_expr() if risch is not False: try: result, i = risch_integrate(f, x, separate_integral=True, conds=conds) except NotImplementedError: pass else: if i: # There was a nonelementary integral. Try integrating it. return result + i.doit(risch=False) else: return result # since Integral(f=g1+g2+...) == Integral(g1) + Integral(g2) + ... # we are going to handle Add terms separately, # if `f` is not Add -- we only have one term # Note that in general, this is a bad idea, because Integral(g1) + # Integral(g2) might not be computable, even if Integral(g1 + g2) is. # For example, Integral(x**x + x**x*log(x)). But many heuristics only # work term-wise. So we compute this step last, after trying # risch_integrate. We also try risch_integrate again in this loop, # because maybe the integral is a sum of an elementary part and a # nonelementary part (like erf(x) + exp(x)). risch_integrate() is # quite fast, so this is acceptable. parts = [] args = Add.make_args(f) for g in args: coeff, g = g.as_independent(x) # g(x) = const if g is S.One and not meijerg: parts.append(coeff*x) continue # g(x) = expr + O(x**n) order_term = g.getO() if order_term is not None: h = self._eval_integral(g.removeO(), x) if h is not None: h_order_expr = self._eval_integral(order_term.expr, x) if h_order_expr is not None: h_order_term = order_term.func( h_order_expr, *order_term.variables) parts.append(coeff*(h + h_order_term)) continue # NOTE: if there is O(x**n) and we fail to integrate then there is # no point in trying other methods because they will fail anyway. return None # c # g(x) = (a*x+b) if g.is_Pow and not g.exp.has(x) and not meijerg: a = Wild('a', exclude=[x]) b = Wild('b', exclude=[x]) M = g.base.match(a*x + b) if M is not None: if g.exp == -1: h = C.log(g.base) elif conds != 'piecewise': h = g.base**(g.exp + 1) / (g.exp + 1) else: h1 = C.log(g.base) h2 = g.base**(g.exp + 1) / (g.exp + 1) h = Piecewise((h1, Eq(g.exp, -1)), (h2, True)) parts.append(coeff * h / M[a]) continue # poly(x) # g(x) = ------- # poly(x) if g.is_rational_function(x) and not meijerg: parts.append(coeff * ratint(g, x)) continue if not meijerg: # g(x) = Mul(trig) h = trigintegrate(g, x, conds=conds) if h is not None: parts.append(coeff * h) continue # g(x) has at least a DiracDelta term h = deltaintegrate(g, x) if h is not None: parts.append(coeff * h) continue # Try risch again. if risch is not False: try: h, i = risch_integrate(g, x, separate_integral=True, conds=conds) except NotImplementedError: h = None else: if i: h = h + i.doit(risch=False) parts.append(coeff*h) continue # fall back to heurisch try: if conds == 'piecewise': h = heurisch_wrapper(g, x, hints=[]) else: h = heurisch(g, x, hints=[]) except PolynomialError: # XXX: this exception means there is a bug in the # implementation of heuristic Risch integration # algorithm. h = None else: h = None if meijerg is not False and h is None: # rewrite using G functions try: h = meijerint_indefinite(g, x) except NotImplementedError: from sympy.integrals.meijerint import _debug _debug('NotImplementedError from meijerint_definite') res = None if h is not None: parts.append(coeff * h) continue if h is None and manual is not False: try: result = manualintegrate(g, x) if result is not None and not isinstance(result, Integral): if result.has(Integral): # try to have other algorithms do the integrals # manualintegrate can't handle result = result.func(*[ arg.doit(manual=False) if arg.has(Integral) else arg for arg in result.args ]).expand(multinomial=False, log=False, power_exp=False, power_base=False) if not result.has(Integral): parts.append(coeff * result) continue except (ValueError, PolynomialError): # can't handle some SymPy expressions pass # if we failed maybe it was because we had # a product that could have been expanded, # so let's try an expansion of the whole # thing before giving up; we don't try this # out the outset because there are things # that cannot be solved unless they are # NOT expanded e.g., x**x*(1+log(x)). There # should probably be a checker somewhere in this # routine to look for such cases and try to do # collection on the expressions if they are already # in an expanded form if not h and len(args) == 1: f = f.expand(mul=True, deep=False) if f.is_Add: # Note: risch will be identical on the expanded # expression, but maybe it will be able to pick out parts, # like x*(exp(x) + erf(x)). return self._eval_integral(f, x, meijerg=meijerg, risch=risch, conds=conds) if h is not None: parts.append(coeff * h) else: return None return Add(*parts) def _eval_lseries(self, x, logx): self = self.as_dummy() symb = x for l in self.limits: if x in l[1:]: symb = l[0] break for term in self.function.lseries(symb, logx): yield integrate(term, *self.limits) def _eval_nseries(self, x, n, logx): self = self.as_dummy() symb = x for l in self.limits: if x in l[1:]: symb = l[0] break terms, order = self.function.nseries( x=symb, n=n, logx=logx).as_coeff_add(C.Order) return integrate(terms, *self.limits) + Add(*order)*x def as_sum(self, n, method="midpoint"): """ Approximates the definite integral by a sum. method ... one of: left, right, midpoint, trapezoid These are all basically the rectangle method [1], the only difference is where the function value is taken in each interval to define the rectangle. [1] http://en.wikipedia.org/wiki/Rectangle_method Examples ======== >>> from sympy import sin, sqrt >>> from sympy.abc import x >>> from sympy.integrals import Integral >>> e = Integral(sin(x), (x, 3, 7)) >>> e Integral(sin(x), (x, 3, 7)) For demonstration purposes, this interval will only be split into 2 regions, bounded by [3, 5] and [5, 7]. The left-hand rule uses function evaluations at the left of each interval: >>> e.as_sum(2, 'left') 2*sin(5) + 2*sin(3) The midpoint rule uses evaluations at the center of each interval: >>> e.as_sum(2, 'midpoint') 2*sin(4) + 2*sin(6) The right-hand rule uses function evaluations at the right of each interval: >>> e.as_sum(2, 'right') 2*sin(5) + 2*sin(7) The trapezoid rule uses function evaluations on both sides of the intervals. This is equivalent to taking the average of the left and right hand rule results: >>> e.as_sum(2, 'trapezoid') 2*sin(5) + sin(3) + sin(7) >>> (e.as_sum(2, 'left') + e.as_sum(2, 'right'))/2 == _ True All but the trapexoid method may be used when dealing with a function with a discontinuity. Here, the discontinuity at x = 0 can be avoided by using the midpoint or right-hand method: >>> e = Integral(1/sqrt(x), (x, 0, 1)) >>> e.as_sum(5).n(4) 1.730 >>> e.as_sum(10).n(4) 1.809 >>> e.doit().n(4) # the actual value is 2 2.000 The left- or trapezoid method will encounter the discontinuity and return oo: >>> e.as_sum(5, 'left') oo >>> e.as_sum(5, 'trapezoid') oo See Also ======== Integral.doit : Perform the integration using any hints """ limits = self.limits if len(limits) > 1: raise NotImplementedError( "Multidimensional midpoint rule not implemented yet") else: limit = limits[0] if len(limit) != 3: raise ValueError("Expecting a definite integral.") if n <= 0: raise ValueError("n must be > 0") if n == oo: raise NotImplementedError("Infinite summation not yet implemented") sym, lower_limit, upper_limit = limit dx = (upper_limit - lower_limit)/n if method == 'trapezoid': l = self.function.subs(sym, lower_limit) r = self.function.subs(sym, upper_limit) result = (l + r)/2 for i in range(1, n): x = lower_limit + i*dx result += self.function.subs(sym, x) return result*dx elif method not in ('left', 'right', 'midpoint'): raise NotImplementedError("Unknown method %s" % method) result = 0 for i in range(n): if method == "midpoint": xi = lower_limit + i*dx + dx/2 elif method == "left": xi = lower_limit + i*dx elif method == "right": xi = lower_limit + i*dx + dx result += self.function.subs(sym, xi) return result*dx @xthreaded def integrate(*args, **kwargs): """integrate(f, var, ...) Compute definite or indefinite integral of one or more variables using Risch-Norman algorithm and table lookup. This procedure is able to handle elementary algebraic and transcendental functions and also a huge class of special functions, including Airy, Bessel, Whittaker and Lambert. var can be: - a symbol -- indefinite integration - a tuple (symbol, a) -- indefinite integration with result given with `a` replacing `symbol` - a tuple (symbol, a, b) -- definite integration Several variables can be specified, in which case the result is multiple integration. (If var is omitted and the integrand is univariate, the indefinite integral in that variable will be performed.) Indefinite integrals are returned without terms that are independent of the integration variables. (see examples) Definite improper integrals often entail delicate convergence conditions. Pass conds='piecewise', 'separate' or 'none' to have these returned, respectively, as a Piecewise function, as a separate result (i.e. result will be a tuple), or not at all (default is 'piecewise'). **Strategy** SymPy uses various approaches to definite integration. One method is to find an antiderivative for the integrand, and then use the fundamental theorem of calculus. Various functions are implemented to integrate polynomial, rational and trigonometric functions, and integrands containing DiracDelta terms. SymPy also implements the part of the Risch algorithm, which is a decision procedure for integrating elementary functions, i.e., the algorithm can either find an elementary antiderivative, or prove that one does not exist. There is also a (very successful, albeit somewhat slow) general implementation of the heuristic Risch algorithm. This algorithm will eventually be phased out as more of the full Risch algorithm is implemented. See the docstring of Integral._eval_integral() for more details on computing the antiderivative using algebraic methods. The option risch=True can be used to use only the (full) Risch algorithm. This is useful if you want to know if an elementary function has an elementary antiderivative. If the indefinite Integral returned by this function is an instance of NonElementaryIntegral, that means that the Risch algorithm has proven that integral to be non-elementary. Note that by default, additional methods (such as the Meijer G method outlined below) are tried on these integrals, as they may be expressible in terms of special functions, so if you only care about elementary answers, use risch=True. Also note that an unevaluated Integral returned by this function is not necessarily a NonElementaryIntegral, even with risch=True, as it may just be an indication that the particular part of the Risch algorithm needed to integrate that function is not yet implemented. Another family of strategies comes from re-writing the integrand in terms of so-called Meijer G-functions. Indefinite integrals of a single G-function can always be computed, and the definite integral of a product of two G-functions can be computed from zero to infinity. Various strategies are implemented to rewrite integrands as G-functions, and use this information to compute integrals (see the ``meijerint`` module). The option manual=True can be used to use only an algorithm that tries to mimic integration by hand. This algorithm does not handle as many integrands as the other algorithms implemented but may return results in a more familiar form. The ``manualintegrate`` module has functions that return the steps used (see the module docstring for more information). In general, the algebraic methods work best for computing antiderivatives of (possibly complicated) combinations of elementary functions. The G-function methods work best for computing definite integrals from zero to infinity of moderately complicated combinations of special functions, or indefinite integrals of very simple combinations of special functions. The strategy employed by the integration code is as follows: - If computing a definite integral, and both limits are real, and at least one limit is +- oo, try the G-function method of definite integration first. - Try to find an antiderivative, using all available methods, ordered by performance (that is try fastest method first, slowest last; in particular polynomial integration is tried first, meijer g-functions second to last, and heuristic risch last). - If still not successful, try G-functions irrespective of the limits. The option meijerg=True, False, None can be used to, respectively: always use G-function methods and no others, never use G-function methods, or use all available methods (in order as described above). It defaults to None. Examples ======== >>> from sympy import integrate, log, exp, oo >>> from sympy.abc import a, x, y >>> integrate(x*y, x) x**2*y/2 >>> integrate(log(x), x) x*log(x) - x >>> integrate(log(x), (x, 1, a)) a*log(a) - a + 1 >>> integrate(x) x**2/2 Terms that are independent of x are dropped by indefinite integration: >>> from sympy import sqrt >>> integrate(sqrt(1 + x), (x, 0, x)) 2*(x + 1)**(3/2)/3 - 2/3 >>> integrate(sqrt(1 + x), x) 2*(x + 1)**(3/2)/3 >>> integrate(x*y) Traceback (most recent call last): ... ValueError: specify integration variables to integrate x*y Note that ``integrate(x)`` syntax is meant only for convenience in interactive sessions and should be avoided in library code. >>> integrate(x**a*exp(-x), (x, 0, oo)) # same as conds='piecewise' Piecewise((gamma(a + 1), -re(a) < 1), (Integral(x**a*exp(-x), (x, 0, oo)), True)) >>> integrate(x**a*exp(-x), (x, 0, oo), conds='none') gamma(a + 1) >>> integrate(x**a*exp(-x), (x, 0, oo), conds='separate') (gamma(a + 1), -re(a) < 1) See Also ======== Integral, Integral.doit """ meijerg = kwargs.pop('meijerg', None) conds = kwargs.pop('conds', 'piecewise') risch = kwargs.pop('risch', None) manual = kwargs.pop('manual', None) integral = Integral(*args, **kwargs) if isinstance(integral, Integral): return integral.doit(deep=False, meijerg=meijerg, conds=conds, risch=risch, manual=manual) else: return integral @xthreaded def line_integrate(field, curve, vars): """line_integrate(field, Curve, variables) Compute the line integral. Examples ======== >>> from sympy import Curve, line_integrate, E, ln >>> from sympy.abc import x, y, t >>> C = Curve([E**t + 1, E**t - 1], (t, 0, ln(2))) >>> line_integrate(x + y, C, [x, y]) 3*sqrt(2) See Also ======== integrate, Integral """ F = sympify(field) if not F: raise ValueError( "Expecting function specifying field as first argument.") if not isinstance(curve, Curve): raise ValueError("Expecting Curve entity as second argument.") if not is_sequence(vars): raise ValueError("Expecting ordered iterable for variables.") if len(curve.functions) != len(vars): raise ValueError("Field variable size does not match curve dimension.") if curve.parameter in vars: raise ValueError("Curve parameter clashes with field parameters.") # Calculate derivatives for line parameter functions # F(r) -> F(r(t)) and finally F(r(t)*r'(t)) Ft = F dldt = 0 for i, var in enumerate(vars): _f = curve.functions[i] _dn = diff(_f, curve.parameter) # ...arc length dldt = dldt + (_dn * _dn) Ft = Ft.subs(var, _f) Ft = Ft * sqrt(dldt) integral = Integral(Ft, curve.limits).doit(deep=False) return integral sympy-0.7.4.1/sympy/integrals/prde.py0000644000175000017500000011113712253362407017725 0ustar georgeskgeorgesk""" Algorithms for solving Parametric Risch Differential Equations. The methods used for solving Parametric Risch Differential Equations parallel those for solving Risch Differential Equations. See the outline in the docstring of rde.py for more information. The Parametric Risch Differential Equation problem is, given f, g1, ..., gm in K(t), to determine if there exist y in K(t) and c1, ..., cm in Const(K) such that Dy + f*y == Sum(ci*gi, (i, 1, m)), and to find such y and ci if they exist. For the algorithms here G is a list of tuples of factions of the terms on the right hand side of the equation (i.e., gi in k(t)), and Q is a list of terms on the right hand side of the equation (i.e., qi in k[t]). See the docstring of each function for more information. """ from __future__ import print_function, division from sympy.core import Dummy, ilcm, Add, Mul, Pow, S from sympy.matrices import Matrix, zeros, eye from sympy import im, sqrt, re from sympy.solvers import solve from sympy.polys import Poly, lcm, cancel, sqf_list from sympy.integrals.risch import (gcdex_diophantine, frac_in, derivation, NonElementaryIntegralException, residue_reduce, splitfactor, residue_reduce_derivation, DecrementLevel, recognize_log_derivative) from sympy.integrals.rde import (order_at, order_at_oo, weak_normalizer, bound_degree, spde, solve_poly_rde) from sympy.core.compatibility import reduce, xrange def prde_normal_denom(fa, fd, G, DE): """ Parametric Risch Differential Equation - Normal part of the denominator. Given a derivation D on k[t] and f, g1, ..., gm in k(t) with f weakly normalized with respect to t, return the tuple (a, b, G, h) such that a, h in k[t], b in k, G = [g1, ..., gm] in k(t)^m, and for any solution c1, ..., cm in Const(k) and y in k(t) of Dy + f*y == Sum(ci*gi, (i, 1, m)), q == y*h in k satisfies a*Dq + b*q == Sum(ci*Gi, (i, 1, m)). """ dn, ds = splitfactor(fd, DE) Gas, Gds = list(zip(*G)) gd = reduce(lambda i, j: i.lcm(j), Gds, Poly(1, DE.t)) en, es = splitfactor(gd, DE) p = dn.gcd(en) h = en.gcd(en.diff(DE.t)).quo(p.gcd(p.diff(DE.t))) a = dn*h c = a*h ba = a*fa - dn*derivation(h, DE)*fd ba, bd = ba.cancel(fd, include=True) G = [(c*A).cancel(D, include=True) for A, D in G] return (a, (ba, bd), G, h) def real_imag(ba, bd, gen): """ Helper function, to get the real and imaginary part of a rational function evaluated at sqrt(-1) without actually evaluating it at sqrt(-1) Seperates the even and odd power terms by checking the degree of terms wrt mod 4. Returns a tuple (ba[0], ba[1], bd) where ba[0] is real part of the numerator ba[1] is the imaginary part and bd is the denominator of the rational function. """ bd = bd.as_poly(gen).as_dict() ba = ba.as_poly(gen).as_dict() denom_real = [value if key[0] % 4 == 0 else -value if key[0] % 4 == 2 else 0 for key, value in bd.items()] denom_imag = [value if key[0] % 4 == 1 else -value if key[0] % 4 == 3 else 0 for key, value in bd.items()] bd_real = sum(r for r in denom_real) bd_imag = sum(r for r in denom_imag) num_real = [value if key[0] % 4 == 0 else -value if key[0] % 4 == 2 else 0 for key, value in ba.items()] num_imag = [value if key[0] % 4 == 1 else -value if key[0] % 4 == 3 else 0 for key, value in ba.items()] ba_real = sum(r for r in num_real) ba_imag = sum(r for r in num_imag) ba = ((ba_real*bd_real + ba_imag*bd_imag).as_poly(gen), (ba_imag*bd_real - ba_real*bd_imag).as_poly(gen)) bd = (bd_real*bd_real + bd_imag*bd_imag).as_poly(gen) return (ba[0], ba[1], bd) def prde_special_denom(a, ba, bd, G, DE, case='auto'): """ Parametric Risch Differential Equation - Special part of the denominator. case is on of {'exp', 'tan', 'primitive'} for the hyperexponential, hypertangent, and primitive cases, respectively. For the hyperexponential (resp. hypertangent) case, given a derivation D on k[t] and a in k[t], b in k, and g1, ..., gm in k(t) with Dt/t in k (resp. Dt/(t**2 + 1) in k, sqrt(-1) not in k), a != 0, and gcd(a, t) == 1 (resp. gcd(a, t**2 + 1) == 1), return the tuple (A, B, GG, h) such that A, B, h in k[t], GG = [gg1, ..., ggm] in k(t)^m, and for any solution c1, ..., cm in Const(k) and q in k of a*Dq + b*q == Sum(ci*gi, (i, 1, m)), r == q*h in k[t] satisfies A*Dr + B*r == Sum(ci*ggi, (i, 1, m)). For case == 'primitive', k == k[t], so it returns (a, b, G, 1) in this case. """ # TODO: Merge this with the very similar special_denom() in rde.py if case == 'auto': case = DE.case if case == 'exp': p = Poly(DE.t, DE.t) elif case == 'tan': p = Poly(DE.t**2 + 1, DE.t) elif case in ['primitive', 'base']: B = ba.quo(bd) return (a, B, G, Poly(1, DE.t)) else: raise ValueError("case must be one of {'exp', 'tan', 'primitive', " "'base'}, not %s." % case) nb = order_at(ba, p, DE.t) - order_at(bd, p, DE.t) nc = min([order_at(Ga, p, DE.t) - order_at(Gd, p, DE.t) for Ga, Gd in G]) n = min(0, nc - min(0, nb)) if not nb: # Possible cancellation. if case == 'exp': dcoeff = DE.d.quo(Poly(DE.t, DE.t)) with DecrementLevel(DE): # We are guaranteed to not have problems, # because case != 'base'. alphaa, alphad = frac_in(-ba.eval(0)/bd.eval(0)/a.eval(0), DE.t) etaa, etad = frac_in(dcoeff, DE.t) A = parametric_log_deriv(alphaa, alphad, etaa, etad, DE) if A is not None: a, m, z = A if a == 1: n = min(n, m) elif case == 'tan': dcoeff = DE.d.quo(Poly(DE.t**2 + 1, DE.t)) with DecrementLevel(DE): # We are guaranteed to not have problems, # because case != 'base'. betaa, alphaa, alphad = real_imag(ba, bd*a, DE.t) betad = alphad etaa, etad = frac_in(dcoeff, DE.t) if recognize_log_derivative(2*betaa, betad, DE): A = parametric_log_deriv(alphaa, alphad, etaa, etad, DE) B = parametric_log_deriv(betaa, betad, etaa, etad, DE) if A is not None and B is not None: a, s, z = A if a == 1: n = min(n, s/2) N = max(0, -nb) pN = p**N pn = p**-n # This is 1/h A = a*pN B = ba*pN.quo(bd) + Poly(n, DE.t)*a*derivation(p, DE).quo(p)*pN G = [(Ga*pN*pn).cancel(Gd, include=True) for Ga, Gd in G] h = pn # (a*p**N, (b + n*a*Dp/p)*p**N, g1*p**(N - n), ..., gm*p**(N - n), p**-n) return (A, B, G, h) def prde_linear_constraints(a, b, G, DE): """ Parametric Risch Differential Equation - Generate linear constraints on the constants. Given a derivation D on k[t], a, b, in k[t] with gcd(a, b) == 1, and G = [g1, ..., gm] in k(t)^m, return Q = [q1, ..., qm] in k[t]^m and a matrix M with entries in k(t) such that for any solution c1, ..., cm in Const(k) and p in k[t] of a*Dp + b*p == Sum(ci*gi, (i, 1, m)), (c1, ..., cm) is a solution of Mx == 0, and p and the ci satisfy a*Dp + b*p == Sum(ci*qi, (i, 1, m)). Because M has entries in k(t), and because Matrix doesn't play well with Poly, M will be a Matrix of Basic expressions. """ m = len(G) Gns, Gds = list(zip(*G)) d = reduce(lambda i, j: i.lcm(j), Gds) d = Poly(d, field=True) Q = [(ga*(d).quo(gd)).div(d) for ga, gd in G] if not all([ri.is_zero for _, ri in Q]): N = max([ri.degree(DE.t) for _, ri in Q]) M = Matrix(N + 1, m, lambda i, j: Q[j][1].nth(i)) else: M = Matrix() # No constraints, return the empty matrix. qs, _ = list(zip(*Q)) return (qs, M) def constant_system(A, u, DE): """ Generate a system for the constant solutions. Given a differential field (K, D) with constant field C = Const(K), a Matrix A, and a vector (Matrix) u with coefficients in K, returns the tuple (B, v, s), where B is a Matrix with coefficients in C and v is a vector (Matrix) such that either v has coefficients in C, in which case s is True and the solutions in C of Ax == u are exactly all the solutions of Bx == v, or v has a non-constant coefficient, in which case s is False Ax == u has no constant solution. This algorithm is used both in solving parametric problems and in determining if an element a of K is a derivative of an element of K or the logarithmic derivative of a K-radical using the structure theorem approach. Because Poly does not play well with Matrix yet, this algorithm assumes that all matrix entries are Basic expressions. """ Au = A.row_join(u) Au = Au.rref(simplify=cancel)[0] # Warning: This will NOT return correct results if cancel() cannot reduce # an identically zero expression to 0. The danger is that we might # incorrectly prove that an integral is nonelementary (such as # risch_integrate(exp((sin(x)**2 + cos(x)**2 - 1)*x**2), x). # But this is a limitation in computer algebra in general, and implicit # in the correctness of the Risch Algorithm is the computability of the # constant field (actually, this same correctness problem exists in any # algorithm that uses rref()). # # We therefore limit ourselves to constant fields that are computable # via the cancel() function, in order to prevent a speed bottleneck from # calling some more complex simplification function (rational function # coefficients will fall into this class). Furthermore, (I believe) this # problem will only crop up if the integral explicitly contains an # expression in the constant field that is identically zero, but cannot # be reduced to such by cancel(). Therefore, a careful user can avoid this # problem entirely by being careful with the sorts of expressions that # appear in his integrand in the variables other than the integration # variable (the structure theorems should be able to completely decide these # problems in the integration variable). Au = Au.applyfunc(cancel) A, u = Au[:, :-1], Au[:, -1] for j in range(A.cols): for i in range(A.rows): if A[i, j].has(*DE.T): # This assumes that const(F(t0, ..., tn) == const(K) == F Ri = A[i, :] # Rm+1; m = A.rows Rm1 = Ri.applyfunc(lambda x: derivation(x, DE, basic=True)/ derivation(A[i, j], DE, basic=True)) Rm1 = Rm1.applyfunc(cancel) um1 = cancel(derivation(u[i], DE, basic=True)/ derivation(A[i, j], DE, basic=True)) for s in range(A.rows): # A[s, :] = A[s, :] - A[s, i]*A[:, m+1] Asj = A[s, j] A.row_op(s, lambda r, jj: cancel(r - Asj*Rm1[jj])) # u[s] = u[s] - A[s, j]*u[m+1 u.row_op(s, lambda r, jj: cancel(r - Asj*um1)) A = A.col_join(Rm1) u = u.col_join(Matrix([um1])) return (A, u) def prde_spde(a, b, Q, n, DE): """ Special Polynomial Differential Equation algorithm: Parametric Version. Given a derivation D on k[t], an integer n, and a, b, q1, ..., qm in k[t] with deg(a) > 0 and gcd(a, b) == 1, return (A, B, Q, R, n1), with Qq = [q1, ..., qm] and R = [r1, ..., rm], such that for any solution c1, ..., cm in Const(k) and q in k[t] of degree at most n of a*Dq + b*q == Sum(ci*gi, (i, 1, m)), p = (q - Sum(ci*ri, (i, 1, m)))/a has degree at most n1 and satisfies A*Dp + B*p == Sum(ci*qi, (i, 1, m)) """ R, Z = list(zip(*[gcdex_diophantine(b, a, qi) for qi in Q])) A = a B = b + derivation(a, DE) Qq = [zi - derivation(ri, DE) for ri, zi in zip(R, Z)] R = list(R) n1 = n - a.degree(DE.t) return (A, B, Qq, R, n1) def prde_no_cancel_b_large(b, Q, n, DE): """ Parametric Poly Risch Differential Equation - No cancellation: deg(b) large enough. Given a derivation D on k[t], n in ZZ, and b, q1, ..., qm in k[t] with b != 0 and either D == d/dt or deg(b) > max(0, deg(D) - 1), returns h1, ..., hr in k[r] and a matrix A with coefficients in Const(k) such that if c1, ..., cm in Const(k) and q in k[t] satisfy deg(q) <= n and Dq + b*Q == Sum(ci*qi, (i, 1, m)), then q = Sum(dj*hj, (j, 1, r)), where d1, ..., dr in Const(k) and A*Matrix([[c1, ..., cm, d1, ..., dr]]).T == 0. """ db = b.degree(DE.t) m = len(Q) H = [Poly(0, DE.t)]*m for N in xrange(n, -1, -1): # [n, ..., 0] for i in range(m): si = Q[i].nth(N + db)/b.LC() sitn = Poly(si*DE.t**N, DE.t) H[i] = H[i] + sitn Q[i] = Q[i] - derivation(sitn, DE) - b*sitn if all(qi.is_zero for qi in Q): dc = -1 M = zeros(0, 2) else: dc = max([qi.degree(t) for qi in Q]) M = Matrix(dc + 1, m, lambda i, j: Q[j].nth(i)) A, u = constant_system(M, zeros(dc + 1, 1), DE) c = eye(m) A = A.row_join(zeros(A.rows, m)).col_join(c.row_join(-c)) return (H, A) def prde_no_cancel_b_small(b, Q, n, DE): """ Parametric Poly Risch Differential Equation - No cancellation: deg(b) small enough. Given a derivation D on k[t], n in ZZ, and b, q1, ..., qm in k[t] with deg(b) < deg(D) - 1 and either D == d/dt or deg(D) >= 2, returns h1, ..., hr in k[t] and a matrix A with coefficients in Const(k) such that if c1, ..., cm in Const(k) and q in k[t] satisfy deg(q) <= n and Dq + b*q == Sum(ci*qi, (i, 1, m)) then q = Sum(dj*hj, (j, 1, r)) where d1, ..., dr in Const(k) and A*Matrix([[c1, ..., cm, d1, ..., dr]]).T == 0. """ m = len(Q) H = [Poly(0, DE.t)]*m for N in xrange(n, 0, -1): # [n, ..., 1] for i in range(m): si = Q[i].nth(N + DE.d.degree(DE.t) - 1)/(N*DE.d.LC()) sitn = Poly(si*DE.t**N, DE.t) H[i] = H[i] + sitn Q[i] = Q[i] - derivation(sitn, DE) - b*sitn if b.degree(DE.t) > 0: for i in range(m): si = Poly(Q[i].nth(b.degree(DE.t))/b.LC(), DE.t) H[i] = H[i] + si Q[i] = Q[i] - derivation(si, DE) - b*si if all(qi.is_zero for qi in Q): dc = -1 M = Matrix() else: dc = max([qi.degree(DE.t) for qi in Q]) M = Matrix(dc + 1, m, lambda i, j: Q[j].nth(i)) A, u = constant_system(M, zeros(dc + 1, 1), DE) c = eye(m) A = A.row_join(zeros(A.rows, m)).col_join(c.row_join(-c)) return (H, A) else: # TODO: implement this (requires recursive param_rischDE() call) raise NotImplementedError def param_rischDE(fa, fd, G, DE): """ Solve a Parametric Risch Differential Equation: Dy + f*y == Sum(ci*Gi, (i, 1, m)). """ _, (fa, fd) = weak_normalizer(fa, fd, DE) a, (ba, bd), G, hn = prde_normal_denom(ga, gd, G, DE) A, B, G, hs = prde_special_denom(a, ba, bd, G, DE) g = gcd(A, B) A, B, G = A.quo(g), B.quo(g), [gia.cancel(gid*g, include=True) for gia, gid in G] Q, M = prde_linear_constraints(A, B, G, DE) M, _ = constant_system(M, zeros(M.rows, 1), DE) # Reduce number of constants at this point try: # Similar to rischDE(), we try oo, even though it might lead to # non-termination when there is no solution. At least for prde_spde, # it will always terminate no matter what n is. n = bound_degree(A, B, G, DE, parametric=True) except NotImplementedError: # Useful for debugging: # import warnings # warnings.warn("param_rischDE: Proceeding with n = oo; may cause " # "non-termination.") n = oo A, B, Q, R, n1 = prde_spde(A, B, Q, n, DE) def limited_integrate_reduce(fa, fd, G, DE): """ Simpler version of step 1 & 2 for the limited integration problem. Given a derivation D on k(t) and f, g1, ..., gn in k(t), return (a, b, h, N, g, V) such that a, b, h in k[t], N is a non-negative integer, g in k(t), V == [v1, ..., vm] in k(t)^m, and for any solution v in k(t), c1, ..., cm in C of f == Dv + Sum(ci*wi, (i, 1, m)), p = v*h is in k, and p and the ci satisfy a*Dp + b*p == g + Sum(ci*vi, (i, 1, m)). Furthermore, if S1irr == Sirr, then p is in k[t], and if t is nonlinear or Liouvillian over k, then deg(p) <= N. So that the special part is always computed, this function calls the more general prde_special_denom() automatically if it cannot determine that S1irr == Sirr. Furthermore, it will automatically call bound_degree() when t is linear and non-Liouvillian, which for the transcendental case, implies that Dt == a*t + b with for some a, b in k*. """ dn, ds = splitfactor(fd, DE) E = [splitfactor(gd, DE) for _, gd in G] En, Es = list(zip(*E)) c = reduce(lambda i, j: i.lcm(j), (dn,) + En) # lcm(dn, en1, ..., enm) hn = c.gcd(c.diff(DE.t)) a = hn b = -derivation(hn, DE) N = 0 # These are the cases where we know that S1irr = Sirr, but there could be # others, and this algorithm will need to be extended to handle them. if DE.case in ['base', 'primitive', 'exp', 'tan']: hs = reduce(lambda i, j: i.lcm(j), (ds,) + Es) # lcm(ds, es1, ..., esm) a = hn*hs b = -derivation(hn, DE) - (hn*derivation(hs, DE)).quo(hs) mu = min(order_at_oo(fa, fd, DE.t), min([order_at_oo(ga, gd, DE.t) for ga, gd in G])) # So far, all the above are also nonlinear or Liouvillian, but if this # changes, then this will need to be updated to call bound_degree() # as per the docstring of this function (DE.case == 'other_linear'). N = hn.degree(DE.t) + hs.degree(DE.t) + max(0, 1 - DE.d.degree(DE.t) - mu) else: # TODO: implement this raise NotImplementedError V = [(-a*hn*ga).cancel(gd, include=True) for ga, gd in G] return (a, b, a, N, (a*hn*fa).cancel(fd, include=True), V) def limited_integrate(fa, fd, G, DE): """ Solves the limited integration problem: f = Dv + Sum(ci*wi, (i, 1, n)) """ fa, fd = fa*Poly(1/fd.LC(), DE.t), fd.monic() A, B, h, N, g, V = limited_integrate_reduce(fa, fd, G, DE) V = [g] + V g = A.gcd(B) A, B, V = A.quo(g), B.quo(g), [via.cancel(vid*g, include=True) for via, vid in V] Q, M = prde_linear_constraints(A, B, V, DE) M, _ = constant_system(M, zeros(M.rows, 1), DE) l = M.nullspace() if M == Matrix() or len(l) > 1: # Continue with param_rischDE() raise NotImplementedError("param_rischDE() is required to solve this " "integral.") elif len(l) == 0: raise NonElementaryIntegralException elif len(l) == 1: # The c1 == 1. In this case, we can assume a normal Risch DE if l[0][0].is_zero: raise NonElementaryIntegralException else: l[0] *= 1/l[0][0] C = sum([Poly(i, DE.t)*q for (i, q) in zip(l[0], Q)]) # Custom version of rischDE() that uses the already computed # denominator and degree bound from above. B, C, m, alpha, beta = spde(A, B, C, N, DE) y = solve_poly_rde(B, C, m, DE) return ((alpha*y + beta, h), list(l[0][1:])) else: raise NotImplementedError def parametric_log_deriv_heu(fa, fd, wa, wd, DE, c1=None): """ Parametric logarithmic derivative heuristic. Given a derivation D on k[t], f in k(t), and a hyperexponential monomial theta over k(t), raises either NotImplementedError, in which case the heuristic failed, or returns None, in which case it has proven that no solution exists, or returns a solution (n, m, v) of the equation n*f == Dv/v + m*Dtheta/theta, with v in k(t)* and n, m in ZZ with n != 0. If this heuristic fails, the structure theorem approach will need to be used. The argument w == Dtheta/theta """ # TODO: finish writing this and write tests c1 = c1 or Dummy('c1') p, a = fa.div(fd) q, b = wa.div(wd) B = max(0, derivation(DE.t, DE).degree(DE.t) - 1) C = max(p.degree(DE.t), q.degree(DE.t)) if q.degree(DE.t) > B: eqs = [p.nth(i) - c1*q.nth(i) for i in range(B + 1, C + 1)] s = solve(eqs, c1) if not s or not s[c1].is_Rational: # deg(q) > B, no solution for c. return None N, M = s[c1].as_numer_denom() # N and M are integers N, M = Poly(N, DE.t), Poly(M, DE.t) nfmwa = N*fa*wd - M*wa*fd nfmwd = fd*wd Qv = is_log_deriv_k_t_radical_in_field(N*fa*wd - M*wa*fd, fd*wd, DE, 'auto') if Qv is None: # (N*f - M*w) is not the logarithmic derivative of a k(t)-radical. return None Q, e, v = Qv if e != 1: return None if Q.is_zero or v.is_zero: # Q == 0 or v == 0. return None return (Q*N, Q*M, v) if p.degree(DE.t) > B: return None c = lcm(fd.as_poly(DE.t).LC(), wd.as_poly(DE.t).LC()) l = fd.monic().lcm(wd.monic())*Poly(c, DE.t) ln, ls = splitfactor(l, DE) z = ls*ln.gcd(ln.diff(DE.t)) if not z.has(DE.t): raise NotImplementedError("parametric_log_deriv_heu() " "heuristic failed: z in k.") u1, r1 = (fa*l.quo(fd)).div(z) # (l*f).div(z) u2, r2 = (wa*l.quo(wd)).div(z) # (l*w).div(z) eqs = [r1.nth(i) - c1*r2.nth(i) for i in range(z.degree(DE.t))] s = solve(eqs, c1) if not s or not s[c1].is_Rational: # deg(q) <= B, no solution for c. return None M, N = s[c1].as_numer_denom() nfmwa = N.as_poly(DE.t)*fa*wd - M.as_poly(DE.t)*wa*fd nfmwd = fd*wd Qv = is_log_deriv_k_t_radical_in_field(nfmwa, nfmwd, DE) if Qv is None: # (N*f - M*w) is not the logarithmic derivative of a k(t)-radical. return None Q, v = Qv if Q.is_zero or v.is_zero: # Q == 0 or v == 0. return None return (Q*N, Q*M, v) def parametric_log_deriv(fa, fd, wa, wd, DE): # TODO: Write the full algorithm using the structure theorems. # try: A = parametric_log_deriv_heu(fa, fd, wa, wd, DE) # except NotImplementedError: # Heuristic failed, we have to use the full method. # TODO: This could be implemented more efficiently. # It isn't too worrisome, because the heuristic handles most difficult # cases. return A def is_deriv_k(fa, fd, DE): """ Checks if Df/f is the derivative of an element of k(t). a in k(t) is the derivative of an element of k(t) if there exists b in k(t) such that a = Db. Either returns (ans, u), such that Df/f == Du, or None, which means that Df/f is not the derivative of an element of k(t). ans is a list of tuples such that Add(*[i*j for i, j in ans]) == u. This is useful for seeing exactly which elements of k(t) produce u. This function uses the structure theorem approach, which says that for any f in K, Df/f is the derivative of a element of K if and only if there are ri in QQ such that:: --- --- Dt \ r * Dt + \ r * i Df / i i / i --- = --. --- --- t f i in L i in E i K/C(x) K/C(x) Where C = Const(K), L_K/C(x) = { i in {1, ..., n} such that t_i is transcendental over C(x)(t_1, ..., t_i-1) and Dt_i = Da_i/a_i, for some a_i in C(x)(t_1, ..., t_i-1)* } (i.e., the set of all indices of logarithmic monomials of K over C(x)), and E_K/C(x) = { i in {1, ..., n} such that t_i is transcendental over C(x)(t_1, ..., t_i-1) and Dt_i/t_i = Da_i, for some a_i in C(x)(t_1, ..., t_i-1) } (i.e., the set of all indices of hyperexponential monomials of K over C(x)). If K is an elementary extension over C(x), then the cardinality of L_K/C(x) U E_K/C(x) is exactly the transcendence degree of K over C(x). Furthermore, because Const_D(K) == Const_D(C(x)) == C, deg(Dt_i) == 1 when t_i is in E_K/C(x) and deg(Dt_i) == 0 when t_i is in L_K/C(x), implying in particular that E_K/C(x) and L_K/C(x) are disjoint. The sets L_K/C(x) and E_K/C(x) must, by their nature, be computed recursively using this same function. Therefore, it is required to pass them as indices to D (or T). E_args are the arguments of the hyperexponentials indexed by E_K (i.e., if i is in E_K, then T[i] == exp(E_args[i])). This is needed to compute the final answer u such that Df/f == Du. log(f) will be the same as u up to a additive constant. This is because they will both behave the same as monomials. For example, both log(x) and log(2*x) == log(x) + log(2) satisfy Dt == 1/x, because log(2) is constant. Therefore, the term const is returned. const is such that log(const) + f == u. This is calculated by dividing the arguments of one logarithm from the other. Therefore, it is necessary to pass the arguments of the logarithmic terms in L_args. To handle the case where we are given Df/f, not f, use is_deriv_k_in_field(). """ # Compute Df/f dfa, dfd = fd*(fd*derivation(fa, DE) - fa*derivation(fd, DE)), fd**2*fa dfa, dfd = dfa.cancel(dfd, include=True) # Our assumption here is that each monomial is recursively transcendental if len(DE.L_K) + len(DE.E_K) != len(DE.D) - 1: if [i for i in DE.cases if i == 'tan'] or \ set([i for i in DE.cases if i == 'primitive']) - set(DE.L_K): raise NotImplementedError("Real version of the structure " "theorems with hypertangent support is not yet implemented.") # TODO: What should really be done in this case? raise NotImplementedError("Nonelementary extensions not supported " "in the structure theorems.") E_part = [DE.D[i].quo(Poly(DE.T[i], DE.T[i])).as_expr() for i in DE.E_K] L_part = [DE.D[i].as_expr() for i in DE.L_K] lhs = Matrix([E_part + L_part]) rhs = Matrix([dfa.as_expr()/dfd.as_expr()]) A, u = constant_system(lhs, rhs, DE) if not all(derivation(i, DE, basic=True).is_zero for i in u) or not A: # If the elements of u are not all constant # Note: See comment in constant_system # Also note: derivation(basic=True) calls cancel() return None else: if not all(i.is_Rational for i in u): raise NotImplementedError("Cannot work with non-rational " "coefficients in this case.") else: terms = DE.E_args + [DE.T[i] for i in DE.L_K] ans = list(zip(terms, u)) result = Add(*[Mul(i, j) for i, j in ans]) argterms = [DE.T[i] for i in DE.E_K] + DE.L_args l = [] for i, j in zip(argterms, u): # We need to get around things like sqrt(x**2) != x # and also sqrt(x**2 + 2*x + 1) != x + 1 icoeff, iterms = sqf_list(i) l.append(Mul(*([Pow(icoeff, j)] + [Pow(b, e*j) for b, e in iterms]))) const = cancel(fa.as_expr()/fd.as_expr()/Mul(*l)) return (ans, result, const) def is_log_deriv_k_t_radical(fa, fd, DE, Df=True): """ Checks if Df is the logarithmic derivative of a k(t)-radical. b in k(t) can be written as the logarithmic derivative of a k(t) radical if there exist n in ZZ and u in k(t) with n, u != 0 such that n*b == Du/u. Either returns (ans, u, n, const) or None, which means that Df cannot be written as the logarithmic derivative of a k(t)-radical. ans is a list of tuples such that Mul(*[i**j for i, j in ans]) == u. This is useful for seeing exactly what elements of k(t) produce u. This function uses the structure theorem approach, which says that for any f in K, Df is the logarithmic derivative of a K-radical if and only if there are ri in QQ such that:: --- --- Dt \ r * Dt + \ r * i / i i / i --- = Df. --- --- t i in L i in E i K/C(x) K/C(x) Where C = Const(K), L_K/C(x) = { i in {1, ..., n} such that t_i is transcendental over C(x)(t_1, ..., t_i-1) and Dt_i = Da_i/a_i, for some a_i in C(x)(t_1, ..., t_i-1)* } (i.e., the set of all indices of logarithmic monomials of K over C(x)), and E_K/C(x) = { i in {1, ..., n} such that t_i is transcendental over C(x)(t_1, ..., t_i-1) and Dt_i/t_i = Da_i, for some a_i in C(x)(t_1, ..., t_i-1) } (i.e., the set of all indices of hyperexponential monomials of K over C(x)). If K is an elementary extension over C(x), then the cardinality of L_K/C(x) U E_K/C(x) is exactly the transcendence degree of K over C(x). Furthermore, because Const_D(K) == Const_D(C(x)) == C, deg(Dt_i) == 1 when t_i is in E_K/C(x) and deg(Dt_i) == 0 when t_i is in L_K/C(x), implying in particular that E_K/C(x) and L_K/C(x) are disjoint. The sets L_K/C(x) and E_K/C(x) must, by their nature, be computed recursively using this same function. Therefore, it is required to pass them as indices to D (or T). L_args are the arguments of the logarithms indexed by L_K (i.e., if i is in L_K, then T[i] == log(L_args[i])). This is needed to compute the final answer u such that n*f == Du/u. exp(f) will be the same as u up to a multiplicative constant. This is because they will both behave the same as monomials. For example, both exp(x) and exp(x + 1) == E*exp(x) satisfy Dt == t. Therefore, the term const is returned. const is such that exp(const)*f == u. This is calculated by subtracting the arguments of one exponential from the other. Therefore, it is necessary to pass the arguments of the exponential terms in E_args. To handle the case where we are given Df, not f, use is_log_deriv_k_t_radical_in_field(). """ H = [] if Df: dfa, dfd = (fd*derivation(fa, DE) - fa*derivation(fd, DE)).cancel(fd**2, include=True) else: dfa, dfd = fa, fd # Our assumption here is that each monomial is recursively transcendental if len(DE.L_K) + len(DE.E_K) != len(DE.D) - 1: if [i for i in DE.cases if i == 'tan'] or \ set([i for i in DE.cases if i == 'primitive']) - set(DE.L_K): raise NotImplementedError("Real version of the structure " "theorems with hypertangent support is not yet implemented.") # TODO: What should really be done in this case? raise NotImplementedError("Nonelementary extensions not supported " "in the structure theorems.") E_part = [DE.D[i].quo(Poly(DE.T[i], DE.T[i])).as_expr() for i in DE.E_K] L_part = [DE.D[i].as_expr() for i in DE.L_K] lhs = Matrix([E_part + L_part]) rhs = Matrix([dfa.as_expr()/dfd.as_expr()]) A, u = constant_system(lhs, rhs, DE) if not all(derivation(i, DE, basic=True).is_zero for i in u) or not A: # If the elements of u are not all constant # Note: See comment in constant_system # Also note: derivation(basic=True) calls cancel() return None else: if not all(i.is_Rational for i in u): # TODO: But maybe we can tell if they're not rational, like # log(2)/log(3). Also, there should be an option to continue # anyway, even if the result might potentially be wrong. raise NotImplementedError("Cannot work with non-rational " "coefficients in this case.") else: n = reduce(ilcm, [i.as_numer_denom()[1] for i in u]) u *= n terms = [DE.T[i] for i in DE.E_K] + DE.L_args ans = list(zip(terms, u)) result = Mul(*[Pow(i, j) for i, j in ans]) # exp(f) will be the same as result up to a multiplicative # constant. We now find the log of that constant. argterms = DE.E_args + [DE.T[i] for i in DE.L_K] const = cancel(fa.as_expr()/fd.as_expr() - Add(*[Mul(i, j/n) for i, j in zip(argterms, u)])) return (ans, result, n, const) def is_log_deriv_k_t_radical_in_field(fa, fd, DE, case='auto', z=None): """ Checks if f can be written as the logarithmic derivative of a k(t)-radical. f in k(t) can be written as the logarithmic derivative of a k(t) radical if there exist n in ZZ and u in k(t) with n, u != 0 such that n*f == Du/u. Either returns (n, u) or None, which means that f cannot be written as the logarithmic derivative of a k(t)-radical. case is one of {'primitive', 'exp', 'tan', 'auto'} for the primitive, hyperexponential, and hypertangent cases, respectively. If case it 'auto', it will attempt to determine the type of the derivation automatically. """ fa, fd = fa.cancel(fd, include=True) # f must be simple n, s = splitfactor(fd, DE) if not s.is_one: pass #return None z = z or Dummy('z') H, b = residue_reduce(fa, fd, DE, z=z) if not b: # I will have to verify, but I believe that the answer should be # None in this case. This should never happen for the # functions given when solving the parametric logarithmic # derivative problem when integration elementary functions (see # Bronstein's book, page 255), so most likely this indicates a bug. return None roots = [(i, i.real_roots()) for i, _ in H] if not all(len(j) == i.degree() and all(k.is_Rational for k in j) for i, j in roots): # If f is the logarithmic derivative of a k(t)-radical, then all the # roots of the resultant must be rational numbers. return None # [(a, i), ...], where i*log(a) is a term in the log-part of the integral # of f respolys, residues = list(zip(*roots)) or [[], []] # Note: this might be empty, but everything below should work find in that # case (it should be the same as if it were [[1, 1]]) residueterms = [(H[j][1].subs(z, i), i) for j in xrange(len(H)) for i in residues[j]] # TODO: finish writing this and write tests p = cancel(fa.as_expr()/fd.as_expr() - residue_reduce_derivation(H, DE, z)) p = p.as_poly(DE.t) if p is None: # f - Dg will be in k[t] if f is the logarithmic derivative of a k(t)-radical return None if p.degree(DE.t) >= max(1, DE.d.degree(DE.t)): return None if case == 'auto': case = DE.case if case == 'exp': wa, wd = derivation(DE.t, DE).cancel(Poly(DE.t, DE.t), include=True) with DecrementLevel(DE): pa, pd = frac_in(p, DE.t, cancel=True) wa, wd = frac_in((wa, wd), DE.t) A = parametric_log_deriv(pa, pd, wa, wd, DE) if A is None: return None n, e, u = A u *= DE.t**e # raise NotImplementedError("The hyperexponential case is " # "not yet completely implemented for is_log_deriv_k_t_radical_in_field().") elif case == 'primitive': with DecrementLevel(DE): pa, pd = frac_in(p, DE.t) A = is_log_deriv_k_t_radical_in_field(pa, pd, DE, case='auto') if A is None: return None n, u = A elif case == 'base': # TODO: we can use more efficient residue reduction from ratint() if not fd.is_sqf or fa.degree() >= fd.degree(): # f is the logarithmic derivative in the base case if and only if # f = fa/fd, fd is square-free, deg(fa) < deg(fd), and # gcd(fa, fd) == 1. The last condition is handled by cancel() above. return None # Note: if residueterms = [], returns (1, 1) # f had better be 0 in that case. n = reduce(ilcm, [i.as_numer_denom()[1] for _, i in residueterms], S(1)) u = Mul(*[Pow(i, j*n) for i, j in residueterms]) return (n, u) elif case == 'tan': raise NotImplementedError("The hypertangent case is " "not yet implemented for is_log_deriv_k_t_radical_in_field()") elif case in ['other_linear', 'other_nonlinear']: # XXX: If these are supported by the structure theorems, change to NotImplementedError. raise ValueError("The %s case is not supported in this function." % case) else: raise ValueError("case must be one of {'primitive', 'exp', 'tan', " "'base', 'auto'}, not %s" % case) common_denom = reduce(ilcm, [i.as_numer_denom()[1] for i in [j for _, j in residueterms]] + [n], S(1)) residueterms = [(i, j*common_denom) for i, j in residueterms] m = common_denom//n assert common_denom == n*m # Verify exact division u = cancel(u**m*Mul(*[Pow(i, j) for i, j in residueterms])) return (common_denom, u) sympy-0.7.4.1/sympy/integrals/quadrature.py0000644000175000017500000003434512253362407021155 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import S, Dummy, pi from sympy.functions.combinatorial.factorials import factorial from sympy.functions.elementary.trigonometric import sin, cos from sympy.functions.elementary.miscellaneous import sqrt from sympy.functions.special.gamma_functions import gamma from sympy.polys.orthopolys import legendre_poly, laguerre_poly, hermite_poly, jacobi_poly from sympy.polys.rootoftools import RootOf from sympy.core.compatibility import xrange def gauss_legendre(n, n_digits): r""" Computes the Gauss-Legendre quadrature [1]_ points and weights. The Gauss-Legendre quadrature approximates the integral: .. math:: \int_{-1}^1 f(x)\,dx \approx \sum_{i=1}^n w_i f(x_i) The nodes `x_i` of an order `n` quadrature rule are the roots of `P_n` and the weights `w_i` are given by: .. math:: w_i = \frac{2}{\left(1-x_i^2\right) \left(P'_n(x_i)\right)^2} Parameters ========== n : the order of quadrature n_digits : number of significant digits of the points and weights to return Returns ======= (x, w) : the ``x`` and ``w`` are lists of points and weights as Floats. The points `x_i` and weights `w_i` are returned as ``(x, w)`` tuple of lists. Examples ======== >>> from sympy.integrals.quadrature import gauss_legendre >>> x, w = gauss_legendre(3, 5) >>> x [-0.7746, 0, 0.7746] >>> w [0.55556, 0.88889, 0.55556] >>> x, w = gauss_legendre(4, 5) >>> x [-0.86114, -0.33998, 0.33998, 0.86114] >>> w [0.34786, 0.65215, 0.65215, 0.34786] See Also ======== gauss_laguerre, gauss_gen_laguerre, gauss_hermite, gauss_chebyshev_t, gauss_chebyshev_u, gauss_jacobi References ========== .. [1] http://en.wikipedia.org/wiki/Gaussian_quadrature .. [2] http://people.sc.fsu.edu/~jburkardt/cpp_src/legendre_rule/legendre_rule.html """ x = Dummy("x") p = legendre_poly(n, x, polys=True) pd = p.diff(x) xi = [] w = [] for r in p.real_roots(): if isinstance(r, RootOf): r = r.eval_rational(S(1)/10**(n_digits+2)) xi.append(r.n(n_digits)) w.append((2/((1-r**2) * pd.subs(x, r)**2)).n(n_digits)) return xi, w def gauss_laguerre(n, n_digits): r""" Computes the Gauss-Laguerre quadrature [1]_ points and weights. The Gauss-Laguerre quadrature approximates the integral: .. math:: \int_0^{\infty} e^{-x} f(x)\,dx \approx \sum_{i=1}^n w_i f(x_i) The nodes `x_i` of an order `n` quadrature rule are the roots of `L_n` and the weights `w_i` are given by: .. math:: w_i = \frac{x_i}{(n+1)^2 \left(L_{n+1}(x_i)\right)^2} Parameters ========== n : the order of quadrature n_digits : number of significant digits of the points and weights to return Returns ======= (x, w) : the ``x`` and ``w`` are lists of points and weights as Floats. The points `x_i` and weights `w_i` are returned as ``(x, w)`` tuple of lists. Examples ======== >>> from sympy.integrals.quadrature import gauss_laguerre >>> x, w = gauss_laguerre(3, 5) >>> x [0.41577, 2.2943, 6.2899] >>> w [0.71109, 0.27852, 0.010389] >>> x, w = gauss_laguerre(6, 5) >>> x [0.22285, 1.1889, 2.9927, 5.7751, 9.8375, 15.983] >>> w [0.45896, 0.417, 0.11337, 0.010399, 0.00026102, 8.9855e-7] See Also ======== gauss_legendre, gauss_gen_laguerre, gauss_hermite, gauss_chebyshev_t, gauss_chebyshev_u, gauss_jacobi References ========== .. [1] http://en.wikipedia.org/wiki/Gauss%E2%80%93Laguerre_quadrature .. [2] http://people.sc.fsu.edu/~jburkardt/cpp_src/laguerre_rule/laguerre_rule.html """ x = Dummy("x") p = laguerre_poly(n, x, polys=True) p1 = laguerre_poly(n+1, x, polys=True) xi = [] w = [] for r in p.real_roots(): if isinstance(r, RootOf): r = r.eval_rational(S(1)/10**(n_digits+2)) xi.append(r.n(n_digits)) w.append((r/((n+1)**2 * p1.subs(x, r)**2)).n(n_digits)) return xi, w def gauss_hermite(n, n_digits): r""" Computes the Gauss-Hermite quadrature [1]_ points and weights. The Gauss-Hermite quadrature approximates the integral: .. math:: \int_{-\infty}^{\infty} e^{-x^2} f(x)\,dx \approx \sum_{i=1}^n w_i f(x_i) The nodes `x_i` of an order `n` quadrature rule are the roots of `H_n` and the weights `w_i` are given by: .. math:: w_i = \frac{2^{n-1} n! \sqrt{\pi}}{n^2 \left(H_{n-1}(x_i)\right)^2} Parameters ========== n : the order of quadrature n_digits : number of significant digits of the points and weights to return Returns ======= (x, w) : the ``x`` and ``w`` are lists of points and weights as Floats. The points `x_i` and weights `w_i` are returned as ``(x, w)`` tuple of lists. Examples ======== >>> from sympy.integrals.quadrature import gauss_hermite >>> x, w = gauss_hermite(3, 5) >>> x [-1.2247, 0, 1.2247] >>> w [0.29541, 1.1816, 0.29541] >>> x, w = gauss_hermite(6, 5) >>> x [-2.3506, -1.3358, -0.43608, 0.43608, 1.3358, 2.3506] >>> w [0.00453, 0.15707, 0.72463, 0.72463, 0.15707, 0.00453] See Also ======== gauss_legendre, gauss_laguerre, gauss_gen_laguerre, gauss_chebyshev_t, gauss_chebyshev_u, gauss_jacobi References ========== .. [1] http://en.wikipedia.org/wiki/Gauss-Hermite_Quadrature .. [2] http://people.sc.fsu.edu/~jburkardt/cpp_src/hermite_rule/hermite_rule.html .. [3] http://people.sc.fsu.edu/~jburkardt/cpp_src/gen_hermite_rule/gen_hermite_rule.html """ x = Dummy("x") p = hermite_poly(n, x, polys=True) p1 = hermite_poly(n-1, x, polys=True) xi = [] w = [] for r in p.real_roots(): if isinstance(r, RootOf): r = r.eval_rational(S(1)/10**(n_digits+2)) xi.append(r.n(n_digits)) w.append(((2**(n-1) * factorial(n) * sqrt(pi))/(n**2 * p1.subs(x, r)**2)).n(n_digits)) return xi, w def gauss_gen_laguerre(n, alpha, n_digits): r""" Computes the generalized Gauss-Laguerre quadrature [1]_ points and weights. The generalized Gauss-Laguerre quadrature approximates the integral: .. math:: \int_{0}^\infty x^{\alpha} e^{-x} f(x)\,dx \approx \sum_{i=1}^n w_i f(x_i) The nodes `x_i` of an order `n` quadrature rule are the roots of `L^{\alpha}_n` and the weights `w_i` are given by: .. math:: w_i = \frac{\Gamma(\alpha+n)}{n \Gamma(n) L^{\alpha}_{n-1}(x_i) L^{\alpha+1}_{n-1}(x_i)} Parameters ========== n : the order of quadrature alpha : the exponent of the singularity, `\alpha > -1` n_digits : number of significant digits of the points and weights to return Returns ======= (x, w) : the ``x`` and ``w`` are lists of points and weights as Floats. The points `x_i` and weights `w_i` are returned as ``(x, w)`` tuple of lists. Examples ======== >>> from sympy import S >>> from sympy.integrals.quadrature import gauss_gen_laguerre >>> x, w = gauss_gen_laguerre(3, -S.Half, 5) >>> x [0.19016, 1.7845, 5.5253] >>> w [1.4493, 0.31413, 0.00906] >>> x, w = gauss_gen_laguerre(4, 3*S.Half, 5) >>> x [0.97851, 2.9904, 6.3193, 11.712] >>> w [0.53087, 0.67721, 0.11895, 0.0023152] See Also ======== gauss_legendre, gauss_laguerre, gauss_hermite, gauss_chebyshev_t, gauss_chebyshev_u, gauss_jacobi References ========== .. [1] http://en.wikipedia.org/wiki/Gauss%E2%80%93Laguerre_quadrature .. [2] http://people.sc.fsu.edu/~jburkardt/cpp_src/gen_laguerre_rule/gen_laguerre_rule.html """ x = Dummy("x") p = laguerre_poly(n, x, alpha=alpha, polys=True) p1 = laguerre_poly(n-1, x, alpha=alpha, polys=True) p2 = laguerre_poly(n-1, x, alpha=alpha+1, polys=True) xi = [] w = [] for r in p.real_roots(): if isinstance(r, RootOf): r = r.eval_rational(S(1)/10**(n_digits+2)) xi.append(r.n(n_digits)) w.append((gamma(alpha+n)/(n*gamma(n)*p1.subs(x, r)*p2.subs(x, r))).n(n_digits)) return xi, w def gauss_chebyshev_t(n, n_digits): r""" Computes the Gauss-Chebyshev quadrature [1]_ points and weights of the first kind. The Gauss-Chebyshev quadrature of the first kind approximates the integral: .. math:: \int_{-1}^{1} \frac{1}{\sqrt{1-x^2}} f(x)\,dx \approx \sum_{i=1}^n w_i f(x_i) The nodes `x_i` of an order `n` quadrature rule are the roots of `T_n` and the weights `w_i` are given by: .. math:: w_i = \frac{\pi}{n} Parameters ========== n : the order of quadrature n_digits : number of significant digits of the points and weights to return Returns ======= (x, w) : the ``x`` and ``w`` are lists of points and weights as Floats. The points `x_i` and weights `w_i` are returned as ``(x, w)`` tuple of lists. Examples ======== >>> from sympy import S >>> from sympy.integrals.quadrature import gauss_chebyshev_t >>> x, w = gauss_chebyshev_t(3, 5) >>> x [0.86602, 0, -0.86602] >>> w [1.0472, 1.0472, 1.0472] >>> x, w = gauss_chebyshev_t(6, 5) >>> x [0.96593, 0.70711, 0.25882, -0.25882, -0.70711, -0.96593] >>> w [0.5236, 0.5236, 0.5236, 0.5236, 0.5236, 0.5236] See Also ======== gauss_legendre, gauss_laguerre, gauss_hermite, gauss_gen_laguerre, gauss_chebyshev_u, gauss_jacobi References ========== .. [1] http://en.wikipedia.org/wiki/Chebyshev%E2%80%93Gauss_quadrature .. [2] http://people.sc.fsu.edu/~jburkardt/cpp_src/chebyshev1_rule/chebyshev1_rule.html """ x = Dummy("x") xi = [] w = [] for i in xrange(1,n+1): xi.append((cos((2*i-S.One)/(2*n)*S.Pi)).n(n_digits)) w.append((S.Pi/n).n(n_digits)) return xi, w def gauss_chebyshev_u(n, n_digits): r""" Computes the Gauss-Chebyshev quadrature [1]_ points and weights of the second kind. The Gauss-Chebyshev quadrature of the second kind approximates the integral: .. math:: \int_{-1}^{1} \sqrt{1-x^2} f(x)\,dx \approx \sum_{i=1}^n w_i f(x_i) The nodes `x_i` of an order `n` quadrature rule are the roots of `U_n` and the weights `w_i` are given by: .. math:: w_i = \frac{\pi}{n+1} \sin^2 \left(\frac{i}{n+1}\pi\right) Parameters ========== n : the order of quadrature n_digits : number of significant digits of the points and weights to return Returns ======= (x, w) : the ``x`` and ``w`` are lists of points and weights as Floats. The points `x_i` and weights `w_i` are returned as ``(x, w)`` tuple of lists. Examples ======== >>> from sympy import S >>> from sympy.integrals.quadrature import gauss_chebyshev_u >>> x, w = gauss_chebyshev_u(3, 5) >>> x [0.70711, 0, -0.70711] >>> w [0.3927, 0.7854, 0.3927] >>> x, w = gauss_chebyshev_u(6, 5) >>> x [0.90097, 0.62349, 0.22252, -0.22252, -0.62349, -0.90097] >>> w [0.084489, 0.27433, 0.42658, 0.42658, 0.27433, 0.084489] See Also ======== gauss_legendre, gauss_laguerre, gauss_hermite, gauss_gen_laguerre, gauss_chebyshev_t, gauss_jacobi References ========== .. [1] http://en.wikipedia.org/wiki/Chebyshev%E2%80%93Gauss_quadrature .. [2] http://people.sc.fsu.edu/~jburkardt/cpp_src/chebyshev2_rule/chebyshev2_rule.html """ x = Dummy("x") xi = [] w = [] for i in xrange(1,n+1): xi.append((cos(i/(n+S.One)*S.Pi)).n(n_digits)) w.append((S.Pi/(n+S.One)*sin(i*S.Pi/(n+S.One))**2).n(n_digits)) return xi, w def gauss_jacobi(n, alpha, beta, n_digits): r""" Computes the Gauss-Jacobi quadrature [1]_ points and weights. The Gauss-Jacobi quadrature of the first kind approximates the integral: .. math:: \int_{-1}^1 (1-x)^\alpha (1+x)^\beta f(x)\,dx \approx \sum_{i=1}^n w_i f(x_i) The nodes `x_i` of an order `n` quadrature rule are the roots of `P^{(\alpha,\beta)}_n` and the weights `w_i` are given by: .. math:: w_i = -\frac{2n+\alpha+\beta+2}{n+\alpha+\beta+1}\frac{\Gamma(n+\alpha+1)\Gamma(n+\beta+1)} {\Gamma(n+\alpha+\beta+1)(n+1)!} \frac{2^{\alpha+\beta}}{P'_n(x_i) P^{(\alpha,\beta)}_{n+1}(x_i)} Parameters ========== n : the order of quadrature alpha : the first parameter of the Jacobi Polynomial, `\alpha > -1` beta : the second parameter of the Jacobi Polynomial, `\beta > -1` n_digits : number of significant digits of the points and weights to return Returns ======= (x, w) : the ``x`` and ``w`` are lists of points and weights as Floats. The points `x_i` and weights `w_i` are returned as ``(x, w)`` tuple of lists. Examples ======== >>> from sympy import S >>> from sympy.integrals.quadrature import gauss_jacobi >>> x, w = gauss_jacobi(3, S.Half, -S.Half, 5) >>> x [-0.90097, -0.22252, 0.62349] >>> w [1.7063, 1.0973, 0.33795] >>> x, w = gauss_jacobi(6, 1, 1, 5) >>> x [-0.87174, -0.5917, -0.2093, 0.2093, 0.5917, 0.87174] >>> w [0.050584, 0.22169, 0.39439, 0.39439, 0.22169, 0.050584] See Also ======== gauss_legendre, gauss_laguerre, gauss_hermite, gauss_gen_laguerre, gauss_chebyshev_t, gauss_chebyshev_u References ========== .. [1] http://en.wikipedia.org/wiki/Gauss%E2%80%93Jacobi_quadrature .. [2] http://people.sc.fsu.edu/~jburkardt/cpp_src/jacobi_rule/jacobi_rule.html .. [3] http://people.sc.fsu.edu/~jburkardt/cpp_src/gegenbauer_rule/gegenbauer_rule.html """ x = Dummy("x") p = jacobi_poly(n, alpha, beta, x, polys=True) pd = p.diff(x) pn = jacobi_poly(n+1, alpha, beta, x, polys=True) xi = [] w = [] for r in p.real_roots(): if isinstance(r, RootOf): r = r.eval_rational(S(1)/10**(n_digits+2)) xi.append(r.n(n_digits)) w.append(( - (2*n+alpha+beta+2) / (n+alpha+beta+S.One) * (gamma(n+alpha+1)*gamma(n+beta+1)) / (gamma(n+alpha+beta+S.One)*gamma(n+2)) * 2**(alpha+beta) / (pd.subs(x, r) * pn.subs(x, r)) ).n(n_digits)) return xi, w sympy-0.7.4.1/sympy/integrals/benchmarks/0000755000175000017500000000000012253362407020532 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/integrals/benchmarks/bench_integrate.py0000644000175000017500000000053012253362407024223 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import integrate, Symbol, sin x = Symbol('x') def bench_integrate_sin(): integrate(sin(x), x) def bench_integrate_x1sin(): integrate(x**1*sin(x), x) def bench_integrate_x2sin(): integrate(x**2*sin(x), x) def bench_integrate_x3sin(): integrate(x**3*sin(x), x) sympy-0.7.4.1/sympy/integrals/benchmarks/bench_trigintegrate.py0000644000175000017500000000044212253362407025113 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import Symbol, sin from sympy.integrals.trigonometry import trigintegrate x = Symbol('x') def timeit_trigintegrate_sin3x(): trigintegrate(sin(x)**3, x) def timeit_trigintegrate_x2(): trigintegrate(x**2, x) # -> None sympy-0.7.4.1/sympy/integrals/manualintegrate.py0000644000175000017500000010117512253362407022154 0ustar georgeskgeorgesk"""Integration method that emulates by-hand techniques. This module also provides functionality to get the steps used to evaluate a particular integral, in the ``integral_steps`` function. This will return nested namedtuples representing the integration rules used. The ``manualintegrate`` function computes the integral using those steps given an integrand; given the steps, ``_manualintegrate`` will evaluate them. The integrator can be extended with new heuristics and evaluation techniques. To do so, write a function that accepts an ``IntegralInfo`` object and returns either a namedtuple representing a rule or ``None``. Then, write another function that accepts the namedtuple's fields and returns the antiderivative, and decorate it with ``@evaluates(namedtuple_type)``. """ from __future__ import print_function, division from collections import namedtuple import sympy from sympy.core.compatibility import reduce from sympy.functions.elementary.trigonometric import TrigonometricFunction from sympy.simplify import fraction from sympy.strategies.core import (switch, identity, do_one, null_safe, condition, tryit) def Rule(name, props=""): # GOTCHA: namedtuple class name not considered! def __eq__(self, other): return self.__class__ == other.__class__ and tuple.__eq__(self, other) __neq__ = lambda self, other: not __eq__(self, other) cls = namedtuple(name, props + " context symbol") cls.__eq__ = __eq__ cls.__ne__ = __neq__ return cls ConstantRule = Rule("ConstantRule", "constant") ConstantTimesRule = Rule("ConstantTimesRule", "constant other substep") PowerRule = Rule("PowerRule", "base exp") AddRule = Rule("AddRule", "substeps") URule = Rule("URule", "u_var u_func constant substep") PartsRule = Rule("PartsRule", "u dv v_step second_step") CyclicPartsRule = Rule("CyclicPartsRule", "parts_rules coefficient") TrigRule = Rule("TrigRule", "func arg") ExpRule = Rule("ExpRule", "base exp") LogRule = Rule("LogRule", "func") ArctanRule = Rule("ArctanRule") AlternativeRule = Rule("AlternativeRule", "alternatives") DontKnowRule = Rule("DontKnowRule") DerivativeRule = Rule("DerivativeRule") RewriteRule = Rule("RewriteRule", "rewritten substep") PiecewiseRule = Rule("PiecewiseRule", "subfunctions") IntegralInfo = namedtuple('IntegralInfo', 'integrand symbol') evaluators = {} def evaluates(rule): def _evaluates(func): func.rule = rule evaluators[rule] = func return func return _evaluates def contains_dont_know(rule): if isinstance(rule, DontKnowRule): return True else: for val in rule: if isinstance(val, tuple): if contains_dont_know(val): return True elif isinstance(val, list): if any(contains_dont_know(i) for i in val): return True return False def manual_diff(f, symbol): """Derivative of f in form expected by find_substitutions SymPy's derivatives for some trig functions (like cot) aren't in a form that works well with finding substitutions; this replaces the derivatives for those particular forms with something that works better. """ if f.args: arg = f.args[0] if isinstance(f, sympy.tan): return arg.diff(symbol) * sympy.sec(arg)**2 elif isinstance(f, sympy.cot): return -arg.diff(symbol) * sympy.csc(arg)**2 elif isinstance(f, sympy.sec): return arg.diff(symbol) * sympy.sec(arg) * sympy.tan(arg) elif isinstance(f, sympy.csc): return -arg.diff(symbol) * sympy.csc(arg) * sympy.cot(arg) elif isinstance(f, sympy.Add): return sum([manual_diff(arg, symbol) for arg in f.args]) return f.diff(symbol) # Method based on that on SIN, described in "Symbolic Integration: The # Stormy Decade" def find_substitutions(integrand, symbol, u_var): results = [] def test_subterm(u, u_diff): substituted = integrand / u_diff if symbol not in substituted.free_symbols: # replaced everything already return False substituted = substituted.subs(u, u_var).cancel() if symbol not in substituted.free_symbols: _, denom = integrand.as_numer_denom() _, denom2 = substituted.as_numer_denom() denom2 = denom2.subs(u_var, u) constant = denom/denom2 return (constant, substituted / constant) return False def possible_subterms(term): if any(isinstance(term, cls) for cls in (sympy.sin, sympy.cos, sympy.tan, sympy.asin, sympy.acos, sympy.atan, sympy.exp, sympy.log)): return [term.args[0]] elif isinstance(term, sympy.Mul): r = [] for u in term.args: numer, denom = fraction(u) if numer == 1: r.append(denom) r.extend(possible_subterms(denom)) else: r.append(u) r.extend(possible_subterms(u)) return r elif isinstance(term, sympy.Pow): if term.args[1].is_constant(symbol): return [term.args[0]] elif term.args[0].is_constant(symbol): return [term.args[1]] elif isinstance(term, sympy.Add): return term.args return [] for u in possible_subterms(integrand): if u == symbol: continue u_diff = manual_diff(u, symbol) new_integrand = test_subterm(u, u_diff) if new_integrand is not False: constant, new_integrand = new_integrand substitution = (u, constant, new_integrand) if substitution not in results: results.append(substitution) return results def rewriter(condition, rewrite): """Strategy that rewrites an integrand.""" def _rewriter(integral): integrand, symbol = integral if condition(*integral): rewritten = rewrite(*integral) if rewritten != integrand: substep = integral_steps(rewritten, symbol) if not isinstance(substep, DontKnowRule): return RewriteRule( rewritten, substep, integrand, symbol) return _rewriter def proxy_rewriter(condition, rewrite): """Strategy that rewrites an integrand based on some other criteria.""" def _proxy_rewriter(criteria): criteria, integral = criteria integrand, symbol = integral args = criteria + list(integral) if condition(*args): rewritten = rewrite(*args) if rewritten != integrand: return RewriteRule( rewritten, integral_steps(rewritten, symbol), integrand, symbol) return _proxy_rewriter def multiplexer(conditions): """Apply the rule that matches the condition, else None""" def multiplexer_rl(expr): for key, rule in conditions.items(): if key(expr): return rule(expr) return multiplexer_rl def alternatives(*rules): """Strategy that makes an AlternativeRule out of multiple possible results.""" def _alternatives(integral): alts = [] for rule in rules: result = rule(integral) if (result and not isinstance(result, DontKnowRule) and result != integral and result not in alts): alts.append(result) if len(alts) == 1: return alts[0] elif len(alts) > 1: return AlternativeRule(alts, *integral) return _alternatives def constant_rule(integral): integrand, symbol = integral return ConstantRule(integral.integrand, *integral) def power_rule(integral): integrand, symbol = integral base, exp = integrand.as_base_exp() if symbol not in exp.free_symbols and isinstance(base, sympy.Symbol): if sympy.simplify(exp + 1) == 0: return LogRule(base, integrand, symbol) return PowerRule(base, exp, integrand, symbol) elif symbol not in base.free_symbols and isinstance(exp, sympy.Symbol): rule = ExpRule(base, exp, integrand, symbol) if sympy.ask(~sympy.Q.zero(sympy.log(base))): return rule elif sympy.ask(sympy.Q.zero(sympy.log(base))): return ConstantRule(1, 1, symbol) return PiecewiseRule([ (ConstantRule(1, 1, symbol), sympy.Eq(sympy.log(base), 0)), (rule, True) ], integrand, symbol) def exp_rule(integral): integrand, symbol = integral if isinstance(integrand.args[0], sympy.Symbol): return ExpRule(sympy.E, integrand.args[0], integrand, symbol) def arctan_rule(integral): integrand, symbol = integral base, exp = integrand.as_base_exp() if sympy.simplify(exp + 1) == 0: a = sympy.Wild('a', exclude=[symbol]) b = sympy.Wild('b', exclude=[symbol]) match = base.match(a + b*symbol**2) if match: a, b = match[a], match[b] if ((isinstance(a, sympy.Number) and a < 0) or (isinstance(b, sympy.Number) and b < 0)): return if (sympy.ask(sympy.Q.negative(a) | sympy.Q.negative(b) | sympy.Q.is_true(a <= 0) | sympy.Q.is_true(b <= 0))): return if a != 1 or b != 1: b_condition = b >= 0 u_var = sympy.Dummy("u") rewritten = (sympy.Integer(1) / a) * (base / a) ** (-1) u_func = sympy.sqrt(sympy.sympify(b) / a) * symbol constant = 1 / sympy.sqrt(sympy.sympify(b) / a) substituted = rewritten.subs(u_func, u_var) if a == b: substep = ArctanRule(integrand, symbol) else: subrule = ArctanRule(substituted, u_var) if constant != 1: b_condition = b > 0 subrule = ConstantTimesRule( constant, substituted, subrule, substituted, symbol) substep = URule(u_var, u_func, constant, subrule, integrand, symbol) if a != 1: other = (base / a) ** (-1) substep = ConstantTimesRule( sympy.Integer(1) / a, other, substep, integrand, symbol) return PiecewiseRule([ (substep, sympy.And(a > 0, b_condition)) ], integrand, symbol) return ArctanRule(integrand, symbol) def add_rule(integral): integrand, symbol = integral return AddRule( [integral_steps(g, symbol) for g in integrand.as_ordered_terms()], integrand, symbol) def mul_rule(integral): integrand, symbol = integral args = integrand.args # Constant times function case coeff, f = integrand.as_independent(symbol) if coeff != 1: return ConstantTimesRule( coeff, f, integral_steps(f, symbol), integrand, symbol) def _parts_rule(integrand, symbol): # LIATE rule: # log, inverse trig, algebraic (polynomial), trigonometric, exponential def pull_out_polys(integrand): integrand = integrand.together() polys = [arg for arg in integrand.args if arg.is_polynomial(symbol)] if polys: u = sympy.Mul(*polys) dv = integrand / u return u, dv def pull_out_u(*functions): def pull_out_u_rl(integrand): if any([integrand.has(f) for f in functions]): args = [arg for arg in integrand.args if any(isinstance(arg, cls) for cls in functions)] if args: u = reduce(lambda a,b: a*b, args) dv = integrand / u return u, dv return pull_out_u_rl liate_rules = [pull_out_u(sympy.log), pull_out_u(sympy.atan), pull_out_polys, pull_out_u(sympy.sin, sympy.cos), pull_out_u(sympy.exp)] dummy = sympy.Dummy("temporary") # we can integrate log(x) and atan(x) by setting dv = 1 if isinstance(integrand, sympy.log) or isinstance(integrand, sympy.atan): integrand = dummy * integrand for index, rule in enumerate(liate_rules): result = rule(integrand) if result: u, dv = result # Don't pick u to be a constant if possible if symbol not in u.free_symbols and not u.has(dummy): return u = u.subs(dummy, 1) dv = dv.subs(dummy, 1) for rule in liate_rules[index + 1:]: r = rule(integrand) # make sure dv is amenable to integration if r and r[0].subs(dummy, 1) == dv: du = u.diff(symbol) v_step = integral_steps(dv, symbol) v = _manualintegrate(v_step) return u, dv, v, du, v_step def parts_rule(integral): integrand, symbol = integral constant, integrand = integrand.as_coeff_Mul() result = _parts_rule(integrand, symbol) steps = [] if result: u, dv, v, du, v_step = result steps.append(result) if isinstance(v, sympy.Integral): return while True: if symbol not in (integrand / (v * du)).cancel().free_symbols: coefficient = ((v * du) / integrand).cancel() rule = CyclicPartsRule( [PartsRule(u, dv, v_step, None, None, None) for (u, dv, v, du, v_step) in steps], (-1) ** len(steps) * coefficient, integrand, symbol ) if constant != 1: rule = ConstantTimesRule(constant, integrand, rule, constant * integrand, symbol) return rule result = _parts_rule(v * du, symbol) if result: u, dv, v, du, v_step = result steps.append(result) else: break def make_second_step(steps, integrand): if steps: u, dv, v, du, v_step = steps[0] return PartsRule(u, dv, v_step, make_second_step(steps[1:], v * du), integrand, symbol) else: return integral_steps(integrand, symbol) if steps: u, dv, v, du, v_step = steps[0] rule = PartsRule(u, dv, v_step, make_second_step(steps[1:], v * du), integrand, symbol) if constant != 1: rule = ConstantTimesRule(constant, integrand, rule, constant * integrand, symbol) return rule def trig_rule(integral): integrand, symbol = integral if isinstance(integrand, sympy.sin) or isinstance(integrand, sympy.cos): arg = integrand.args[0] if not isinstance(arg, sympy.Symbol): return # perhaps a substitution can deal with it if isinstance(integrand, sympy.sin): func = 'sin' else: func = 'cos' return TrigRule(func, arg, integrand, symbol) if isinstance(integrand, sympy.tan): rewritten = sympy.sin(*integrand.args) / sympy.cos(*integrand.args) elif isinstance(integrand, sympy.cot): rewritten = sympy.cos(*integrand.args) / sympy.sin(*integrand.args) elif isinstance(integrand, sympy.sec): arg = integrand.args[0] rewritten = ((sympy.sec(arg)**2 + sympy.tan(arg) * sympy.sec(arg)) / (sympy.sec(arg) + sympy.tan(arg))) elif isinstance(integrand, sympy.csc): arg = integrand.args[0] rewritten = ((sympy.csc(arg)**2 + sympy.cot(arg) * sympy.csc(arg)) / (sympy.csc(arg) + sympy.cot(arg))) return RewriteRule( rewritten, integral_steps(rewritten, symbol), integrand, symbol ) def trig_product_rule(integral): integrand, symbol = integral sectan = sympy.sec(symbol) * sympy.tan(symbol) q = integrand / sectan if symbol not in q.free_symbols: rule = TrigRule('sec*tan', symbol, sectan, symbol) if q != 1: rule = ConstantTimesRule(q, sectan, rule, integrand, symbol) return rule csccot = -sympy.csc(symbol) * sympy.cot(symbol) q = integrand / csccot if symbol not in q.free_symbols: rule = TrigRule('csc*cot', symbol, csccot, symbol) if q != 1: rule = ConstantTimesRule(q, csccot, rule, integrand, symbol) return rule @sympy.cacheit def make_wilds(symbol): a = sympy.Wild('a', exclude=[symbol]) b = sympy.Wild('b', exclude=[symbol]) m = sympy.Wild('m', exclude=[symbol], properties=[lambda n: isinstance(n, sympy.Integer)]) n = sympy.Wild('n', exclude=[symbol], properties=[lambda n: isinstance(n, sympy.Integer)]) return a, b, m, n @sympy.cacheit def sincos_pattern(symbol): a, b, m, n = make_wilds(symbol) pattern = sympy.sin(a*symbol)**m * sympy.cos(b*symbol)**n return pattern, a, b, m, n @sympy.cacheit def tansec_pattern(symbol): a, b, m, n = make_wilds(symbol) pattern = sympy.tan(a*symbol)**m * sympy.sec(b*symbol)**n return pattern, a, b, m, n @sympy.cacheit def cotcsc_pattern(symbol): a, b, m, n = make_wilds(symbol) pattern = sympy.cot(a*symbol)**m * sympy.csc(b*symbol)**n return pattern, a, b, m, n def uncurry(func): def uncurry_rl(args): return func(*args) return uncurry_rl def trig_rewriter(rewrite): def trig_rewriter_rl(args): a, b, m, n, integrand, symbol = args rewritten = rewrite(a, b, m, n, integrand, symbol) if rewritten != integrand: return RewriteRule( rewritten, integral_steps(rewritten, symbol), integrand, symbol) return trig_rewriter_rl sincos_botheven_condition = uncurry(lambda a, b, m, n, i, s: m.is_even and n.is_even) sincos_botheven = trig_rewriter( lambda a, b, m, n, i, symbol: ( (((1 - sympy.cos(2*a*symbol)) / 2) ** (m / 2)) * (((1 + sympy.cos(2*b*symbol)) / 2) ** (n / 2)) )) sincos_sinodd_condition = uncurry(lambda a, b, m, n, i, s: m.is_odd and m >= 3) sincos_sinodd = trig_rewriter( lambda a, b, m, n, i, symbol: ( (1 - sympy.cos(a*symbol)**2)**((m - 1) / 2) * sympy.sin(a*symbol) * sympy.cos(b*symbol) ** n)) sincos_cosodd_condition = uncurry(lambda a, b, m, n, i, s: n.is_odd and n >= 3) sincos_cosodd = trig_rewriter( lambda a, b, m, n, i, symbol: ( (1 - sympy.sin(b*symbol)**2)**((n - 1) / 2) * sympy.cos(b*symbol) * sympy.sin(a*symbol) ** m)) tansec_seceven_condition = uncurry(lambda a, b, m, n, i, s: n.is_even and n >= 4) tansec_seceven = trig_rewriter( lambda a, b, m, n, i, symbol: ( (1 + sympy.tan(b*symbol)**2) ** (n/2 - 1) * sympy.sec(b*symbol)**2 * sympy.tan(a*symbol) ** m )) tansec_tanodd_condition = uncurry(lambda a, b, m, n, i, s: m.is_odd) tansec_tanodd = trig_rewriter( lambda a, b, m, n, i, symbol: ( (sympy.sec(a*symbol)**2 - 1) ** ((m - 1) / 2) * sympy.tan(a*symbol) * sympy.sec(b*symbol) ** n )) cotcsc_csceven_condition = uncurry(lambda a, b, m, n, i, s: n.is_even and n >= 4) cotcsc_csceven = trig_rewriter( lambda a, b, m, n, i, symbol: ( (1 + sympy.cot(b*symbol)**2) ** (n/2 - 1) * sympy.csc(b*symbol)**2 * sympy.cot(a*symbol) ** m )) cotcsc_cotodd_condition = uncurry(lambda a, b, m, n, i, s: m.is_odd) cotcsc_cotodd = trig_rewriter( lambda a, b, m, n, i, symbol: ( (sympy.csc(a*symbol)**2 - 1) ** ((m - 1) / 2) * sympy.cot(a*symbol) * sympy.csc(b*symbol) ** n )) def trig_powers_products_rule(integral): integrand, symbol = integral if any(integrand.has(f) for f in (sympy.sin, sympy.cos)): pattern, a, b, m, n = sincos_pattern(symbol) match = integrand.match(pattern) if match: a, b, m, n = match.get(a, 0),match.get(b, 0), match.get(m, 0), match.get(n, 0) return multiplexer({ sincos_botheven_condition: sincos_botheven, sincos_sinodd_condition: sincos_sinodd, sincos_cosodd_condition: sincos_cosodd })((a, b, m, n, integrand, symbol)) integrand = integrand.subs({ 1 / sympy.cos(symbol): sympy.sec(symbol) }) if any(integrand.has(f) for f in (sympy.tan, sympy.sec)): pattern, a, b, m, n = tansec_pattern(symbol) match = integrand.match(pattern) if match: a, b, m, n = match.get(a, 0),match.get(b, 0), match.get(m, 0), match.get(n, 0) return multiplexer({ tansec_tanodd_condition: tansec_tanodd, tansec_seceven_condition: tansec_seceven })((a, b, m, n, integrand, symbol)) integrand = integrand.subs({ 1 / sympy.sin(symbol): sympy.csc(symbol), 1 / sympy.tan(symbol): sympy.cot(symbol), sympy.cos(symbol) / sympy.tan(symbol): sympy.cot(symbol) }) if any(integrand.has(f) for f in (sympy.cot, sympy.csc)): pattern, a, b, m, n = cotcsc_pattern(symbol) match = integrand.match(pattern) if match: a, b, m, n = match.get(a, 0),match.get(b, 0), match.get(m, 0), match.get(n, 0) return multiplexer({ cotcsc_cotodd_condition: cotcsc_cotodd, cotcsc_csceven_condition: cotcsc_csceven })((a, b, m, n, integrand, symbol)) def substitution_rule(integral): integrand, symbol = integral u_var = sympy.Dummy("u") substitutions = find_substitutions(integrand, symbol, u_var) if substitutions: ways = [] for u_func, c, substituted in substitutions: subrule = integral_steps(substituted, u_var) if contains_dont_know(subrule): continue if sympy.simplify(c - 1) != 0: _, denom = c.as_numer_denom() subrule = ConstantTimesRule(c, substituted, subrule, substituted, symbol) if denom.free_symbols: piecewise = [] could_be_zero = [] if isinstance(denom, sympy.Mul): could_be_zero = denom.args else: could_be_zero.append(denom) for expr in could_be_zero: if not sympy.ask(~sympy.Q.zero(expr)): substep = integral_steps(integrand.subs(expr, 0), symbol) if substep: piecewise.append(( substep, sympy.Eq(expr, 0) )) piecewise.append((subrule, True)) subrule = PiecewiseRule(piecewise, substituted, symbol) ways.append(URule(u_var, u_func, c, subrule, integrand, symbol)) if len(ways) > 1: return AlternativeRule(ways, integrand, symbol) elif ways: return ways[0] elif integrand.has(sympy.exp): u_func = sympy.exp(symbol) c = 1 substituted = integrand / u_func.diff(symbol) substituted = substituted.subs(u_func, u_var) if symbol not in substituted.free_symbols: return URule(u_var, u_func, c, integral_steps(substituted, u_var), integrand, symbol) partial_fractions_rule = rewriter( lambda integrand, symbol: integrand.is_rational_function(), lambda integrand, symbol: integrand.apart(symbol)) distribute_expand_rule = rewriter( lambda integrand, symbol: ( all(arg.is_Pow or arg.is_polynomial(symbol) for arg in integrand.args) or isinstance(integrand, sympy.Pow) or isinstance(integrand, sympy.Mul)), lambda integrand, symbol: integrand.expand()) def derivative_rule(integral): variables = integral[0].args[1:] if variables[-1] == integral.symbol: return DerivativeRule(*integral) else: return ConstantRule(integral.integrand, *integral) def fallback_rule(integral): return DontKnowRule(*integral) # Cache is used to break cyclic integrals _integral_cache = {} def integral_steps(integrand, symbol, **options): """Returns the steps needed to compute an integral. This function attempts to mirror what a student would do by hand as closely as possible. SymPy Gamma uses this to provide a step-by-step explanation of an integral. The code it uses to format the results of this function can be found at https://github.com/sympy/sympy_gamma/blob/master/app/logic/intsteps.py. Examples ======== >>> from sympy import exp, sin, cos >>> from sympy.integrals.manualintegrate import integral_steps >>> from sympy.abc import x >>> print(repr(integral_steps(exp(x) / (1 + exp(2 * x)), x))) \ # doctest: +NORMALIZE_WHITESPACE URule(u_var=_u, u_func=exp(x), constant=1, substep=ArctanRule(context=1/(_u**2 + 1), symbol=_u), context=exp(x)/(exp(2*x) + 1), symbol=x) >>> print(repr(integral_steps(sin(x), x))) \ # doctest: +NORMALIZE_WHITESPACE TrigRule(func='sin', arg=x, context=sin(x), symbol=x) >>> print(repr(integral_steps((x**2 + 3)**2 , x))) \ # doctest: +NORMALIZE_WHITESPACE RewriteRule(rewritten=x**4 + 6*x**2 + 9, substep=AddRule(substeps=[PowerRule(base=x, exp=4, context=x**4, symbol=x), ConstantTimesRule(constant=6, other=x**2, substep=PowerRule(base=x, exp=2, context=x**2, symbol=x), context=6*x**2, symbol=x), ConstantRule(constant=9, context=9, symbol=x)], context=x**4 + 6*x**2 + 9, symbol=x), context=(x**2 + 3)**2, symbol=x) Returns ======= rule : namedtuple The first step; most rules have substeps that must also be considered. These substeps can be evaluated using ``manualintegrate`` to obtain a result. """ cachekey = (integrand, symbol) if cachekey in _integral_cache: if _integral_cache[cachekey] is None: # cyclic integral! null_safe will eliminate that path return None else: return _integral_cache[cachekey] else: _integral_cache[cachekey] = None integral = IntegralInfo(integrand, symbol) def key(integral): integrand = integral.integrand if isinstance(integrand, TrigonometricFunction): return TrigonometricFunction elif isinstance(integrand, sympy.Derivative): return sympy.Derivative elif symbol not in integrand.free_symbols: return sympy.Number else: for cls in (sympy.Pow, sympy.Symbol, sympy.exp, sympy.log, sympy.Add, sympy.Mul, sympy.atan): if isinstance(integrand, cls): return cls def integral_is_subclass(*klasses): def _integral_is_subclass(integral): k = key(integral) return k and issubclass(k, klasses) return _integral_is_subclass result = do_one( null_safe(switch(key, { sympy.Pow: do_one(null_safe(power_rule), null_safe(arctan_rule)), sympy.Symbol: power_rule, sympy.exp: exp_rule, sympy.Add: add_rule, sympy.Mul: do_one(null_safe(mul_rule), null_safe(trig_product_rule)), sympy.Derivative: derivative_rule, TrigonometricFunction: trig_rule, sympy.Number: constant_rule })), null_safe( alternatives( substitution_rule, condition( integral_is_subclass(sympy.Mul, sympy.log, sympy.atan), parts_rule), condition( integral_is_subclass(sympy.Mul, sympy.Pow), partial_fractions_rule), condition( integral_is_subclass(sympy.Mul, sympy.Pow), distribute_expand_rule), trig_powers_products_rule ) ), fallback_rule)(integral) del _integral_cache[cachekey] return result @evaluates(ConstantRule) def eval_constant(constant, integrand, symbol): return constant * symbol @evaluates(ConstantTimesRule) def eval_constanttimes(constant, other, substep, integrand, symbol): return constant * _manualintegrate(substep) @evaluates(PowerRule) def eval_power(base, exp, integrand, symbol): return (base ** (exp + 1)) / (exp + 1) @evaluates(ExpRule) def eval_exp(base, exp, integrand, symbol): return integrand / sympy.ln(base) @evaluates(AddRule) def eval_add(substeps, integrand, symbol): return sum(map(_manualintegrate, substeps)) @evaluates(URule) def eval_u(u_var, u_func, constant, substep, integrand, symbol): result = _manualintegrate(substep) return result.subs(u_var, u_func) @evaluates(PartsRule) def eval_parts(u, dv, v_step, second_step, integrand, symbol): v = _manualintegrate(v_step) return u * v - _manualintegrate(second_step) @evaluates(CyclicPartsRule) def eval_cyclicparts(parts_rules, coefficient, integrand, symbol): coefficient = 1 - coefficient result = [] sign = 1 for rule in parts_rules: result.append(sign * rule.u * _manualintegrate(rule.v_step)) sign *= -1 return sympy.Add(*result) / coefficient @evaluates(TrigRule) def eval_trig(func, arg, integrand, symbol): if func == 'sin': return -sympy.cos(arg) elif func == 'cos': return sympy.sin(arg) elif func == 'sec*tan': return sympy.sec(arg) elif func == 'csc*cot': return sympy.csc(arg) @evaluates(LogRule) def eval_log(func, integrand, symbol): return sympy.ln(func) @evaluates(ArctanRule) def eval_arctan(integrand, symbol): return sympy.atan(symbol) @evaluates(AlternativeRule) def eval_alternative(alternatives, integrand, symbol): return _manualintegrate(alternatives[0]) @evaluates(RewriteRule) def eval_rewrite(rewritten, substep, integrand, symbol): return _manualintegrate(substep) @evaluates(PiecewiseRule) def eval_piecewise(substeps, integrand, symbol): return sympy.Piecewise(*[(_manualintegrate(substep), cond) for substep, cond in substeps]) @evaluates(DerivativeRule) def eval_derivativerule(integrand, symbol): # isinstance(integrand, Derivative) should be True if len(integrand.args) == 2: return integrand.args[0] else: return sympy.Derivative(integrand.args[0], *integrand.args[1:-1]) @evaluates(DontKnowRule) def eval_dontknowrule(integrand, symbol): return sympy.Integral(integrand, symbol) def _manualintegrate(rule): evaluator = evaluators.get(rule.__class__) if not evaluator: raise ValueError("Cannot evaluate rule %s" % rule) return evaluator(*rule) def manualintegrate(f, var): """manualintegrate(f, var) Compute indefinite integral of a single variable using an algorithm that resembles what a student would do by hand. Unlike ``integrate``, var can only be a single symbol. Examples ======== >>> from sympy import sin, cos, tan, exp, log, integrate >>> from sympy.integrals.manualintegrate import manualintegrate >>> from sympy.abc import x >>> manualintegrate(1 / x, x) log(x) >>> integrate(1/x) log(x) >>> manualintegrate(log(x), x) x*log(x) - x >>> integrate(log(x)) x*log(x) - x >>> manualintegrate(exp(x) / (1 + exp(2 * x)), x) atan(exp(x)) >>> integrate(exp(x) / (1 + exp(2 * x))) RootSum(4*_z**2 + 1, Lambda(_i, _i*log(2*_i + exp(x)))) >>> manualintegrate(cos(x)**4 * sin(x), x) -cos(x)**5/5 >>> integrate(cos(x)**4 * sin(x), x) -cos(x)**5/5 >>> manualintegrate(cos(x)**4 * sin(x)**3, x) cos(x)**7/7 - cos(x)**5/5 >>> integrate(cos(x)**4 * sin(x)**3, x) cos(x)**7/7 - cos(x)**5/5 >>> manualintegrate(tan(x), x) -log(cos(x)) >>> integrate(tan(x), x) -log(sin(x)**2 - 1)/2 See Also ======== sympy.integrals.integrals.integrate sympy.integrals.integrals.Integral.doit sympy.integrals.integrals.Integral """ return _manualintegrate(integral_steps(f, var)) sympy-0.7.4.1/sympy/integrals/rde.py0000644000175000017500000006246112253362407017552 0ustar georgeskgeorgesk""" Algorithms for solving the Risch differential equation. Given a differential field K of characteristic 0 that is a simple monomial extension of a base field k and f, g in K, the Risch Differential Equation problem is to decide if there exist y in K such that Dy + f*y == g and to find one if there are some. If t is a monomial over k and the coefficients of f and g are in k(t), then y is in k(t), and the outline of the algorithm here is given as: 1. Compute the normal part n of the denominator of y. The problem is then reduced to finding y' in k, where y == y'/n. 2. Compute the special part s of the denominator of y. The problem is then reduced to finding y'' in k[t], where y == y''/(n*s) 3. Bound the degree of y''. 4. Reduce the equation Dy + f*y == g to a similar equation with f, g in k[t]. 5. Find the solutions in k[t] of bounded degree of the reduced equation. See Chapter 6 of "Symbolic Integration I: Transcendental Functions" by Manuel Bronstein. See also the docstring of risch.py. """ from __future__ import print_function, division from operator import mul from sympy.core import oo from sympy.core.compatibility import reduce from sympy.core.symbol import Dummy from sympy.polys import Poly, gcd, ZZ, cancel from sympy.integrals.risch import (gcdex_diophantine, frac_in, derivation, splitfactor, NonElementaryIntegralException, DecrementLevel) # TODO: Add messages to NonElementaryIntegralException errors def order_at(a, p, t): """ Computes the order of a at p, with respect to t. For a, p in k[t], the order of a at p is defined as nu_p(a) = max({n in Z+ such that p**n|a}), where a != 0. If a == 0, nu_p(a) = +oo. To compute the order at a rational function, a/b, use the fact that nu_p(a/b) == nu_p(a) - nu_p(b). """ if a.is_zero: return oo if p == Poly(t, t): return a.as_poly(t).ET()[0][0] # TODO: Can this be done more efficiently? # Perhaps using sqf factorization, binary search with an upper bound of # a.degree(t)//p.degree(t), or some other clever method. n = -1 p1 = Poly(1, t) r = Poly(0, t) while r.is_zero: n += 1 p1 = p1*p r = a.rem(p1) return n def order_at_oo(a, d, t): """ Computes the order of a/d at oo (infinity), with respect to t. For f in k(t), the order or f at oo is defined as deg(d) - deg(a), where f == a/d. """ if a.is_zero: return oo return d.degree(t) - a.degree(t) def weak_normalizer(a, d, DE, z=None): """ Weak normalization. Given a derivation D on k[t] and f == a/d in k(t), return q in k[t] such that f - Dq/q is weakly normalized with respect to t. f in k(t) is said to be "weakly normalized" with respect to t if residue_p(f) is not a positive integer for any normal irreducible p in k[t] such that f is in R_p (Definition 6.1.1). If f has an elementary integral, this is equivalent to no logarithm of integral(f) whose argument depends on t has a positive integer coefficient, where the arguments of the logarithms not in k(t) are in k[t]. Returns (q, f - Dq/q) """ z = z or Dummy('z') dn, ds = splitfactor(d, DE) # Compute d1, where dn == d1*d2**2*...*dn**n is a square-free # factorization of d. g = gcd(dn, dn.diff(DE.t)) d_sqf_part = dn.quo(g) d1 = d_sqf_part.quo(gcd(d_sqf_part, g)) a1, b = gcdex_diophantine(d.quo(d1).as_poly(DE.t), d1.as_poly(DE.t), a.as_poly(DE.t)) r = (a - Poly(z, DE.t)*derivation(d1, DE)).as_poly(DE.t).resultant( d1.as_poly(DE.t)) r = Poly(r, z) if not r.has(z): return (Poly(1, DE.t), (a, d)) N = [i for i in r.real_roots() if i in ZZ and i > 0] q = reduce(mul, [gcd(a - Poly(n, DE.t)*derivation(d1, DE), d1) for n in N], Poly(1, DE.t)) dq = derivation(q, DE) sn = q*a - d*dq sd = q*d sn, sd = sn.cancel(sd, include=True) return (q, (sn, sd)) def normal_denom(fa, fd, ga, gd, DE): """ Normal part of the denominator. Given a derivation D on k[t] and f, g in k(t) with f weakly normalized with respect to t, either raise NonElementaryIntegralException, in which case the equation Dy + f*y == g has no solution in k(t), or the quadruplet (a, b, c, h) such that a, h in k[t], b, c in k, and for any solution y in k(t) of Dy + f*y == g, q = y*h in k satisfies a*Dq + b*q == c. This constitutes step 1 in the outline given in the rde.py docstring. """ dn, ds = splitfactor(fd, DE) en, es = splitfactor(gd, DE) p = dn.gcd(en) h = en.gcd(en.diff(DE.t)).quo(p.gcd(p.diff(DE.t))) a = dn*h c = a*h if c.div(en)[1]: # en does not divide dn*h**2 raise NonElementaryIntegralException ca = c*ga ca, cd = ca.cancel(gd, include=True) ba = a*fa - dn*derivation(h, DE)*fd ba, bd = ba.cancel(fd, include=True) # (dn*h, dn*h*f - dn*Dh, dn*h**2*g, h) return (a, (ba, bd), (ca, cd), h) def special_denom(a, ba, bd, ca, cd, DE, case='auto'): """ Special part of the denominator. case is one of {'exp', 'tan', 'primitive'} for the hyperexponential, hypertangent, and primitive cases, respectively. For the hyperexponential (resp. hypertangent) case, given a derivation D on k[t] and a in k[t], b, c, in k with Dt/t in k (resp. Dt/(t**2 + 1) in k, sqrt(-1) not in k), a != 0, and gcd(a, t) == 1 (resp. gcd(a, t**2 + 1) == 1), return the quadruplet (A, B, C, 1/h) such that A, B, C, h in k[t] and for any solution q in k of a*Dq + b*q == c, r = qh in k[t] satisfies A*Dr + B*r == C. For case == 'primitive', k == k[t], so it returns (a, b, c, 1) in this case. This constitutes step 2 of the outline given in the rde.py docstring. """ from sympy.integrals.prde import parametric_log_deriv # TODO: finish writing this and write tests if case == 'auto': case = DE.case if case == 'exp': p = Poly(DE.t, DE.t) elif case == 'tan': p = Poly(DE.t**2 + 1, DE.t) elif case in ['primitive', 'base']: B = ba.to_field().quo(bd) C = ca.to_field().quo(cd) return (a, B, C, Poly(1, DE.t)) else: raise ValueError("case must be one of {'exp', 'tan', 'primitive', " "'base'}, not %s." % case) # assert a.div(p)[1] nb = order_at(ba, p, DE.t) - order_at(bd, p, DE.t) nc = order_at(ca, p, DE.t) - order_at(cd, p, DE.t) n = min(0, nc - min(0, nb)) if not nb: # Possible cancellation. if case == 'exp': dcoeff = DE.d.quo(Poly(DE.t, DE.t)) with DecrementLevel(DE): # We are guaranteed to not have problems, # because case != 'base'. alphaa, alphad = frac_in(-ba.eval(0)/bd.eval(0)/a.eval(0), DE.t) etaa, etad = frac_in(dcoeff, DE.t) A = parametric_log_deriv(alphaa, alphad, etaa, etad, DE) if A is not None: a, m, z = A if a == 1: n = min(n, m) elif case == 'tan': dcoeff = DE.d.quo(Poly(DE.t**2+1, DE.t)) with DecrementLevel(DE): # We are guaranteed to not have problems, # because case != 'base'. alphaa, alphad = frac_in(im(-ba.eval(sqrt(-1))/bd.eval(sqrt(-1))/a.eval(sqrt(-1))), DE.t) betaa, betad = frac_in(re(-ba.eval(sqrt(-1))/bd.eval(sqrt(-1))/a.eval(sqrt(-1))), DE.t) etaa, etad = frac_in(dcoeff, DE.t) if recognize_log_derivative(2*betaa, betad, DE): A = parametric_log_deriv(alphaa*sqrt(-1)*betad+alphad*betaa, alphad*betad, etaa, etad, DE) if A is not None: a, m, z = A if a == 1: n = min(n, m) N = max(0, -nb, n - nc) pN = p**N pn = p**-n A = a*pN B = ba*pN.quo(bd) + Poly(n, DE.t)*a*derivation(p, DE).quo(p)*pN C = (ca*pN*pn).quo(cd) h = pn # (a*p**N, (b + n*a*Dp/p)*p**N, c*p**(N - n), p**-n) return (A, B, C, h) def bound_degree(a, b, cQ, DE, case='auto', parametric=False): """ Bound on polynomial solutions. Given a derivation D on k[t] and a, b, c in k[t] with a != 0, return n in ZZ such that deg(q) <= n for any solution q in k[t] of a*Dq + b*q == c, when parametric=False, or deg(q) <= n for any solution c1, ..., cm in Const(k) and q in k[t] of a*Dq + b*q == Sum(ci*gi, (i, 1, m)) when parametric=True. For parametric=False, cQ is c, a Poly; for parametric=True, cQ is Q == [q1, ..., qm], a list of Polys. This constitutes step 3 of the outline given in the rde.py docstring. """ from sympy.integrals.prde import (parametric_log_deriv, limited_integrate, is_log_deriv_k_t_radical_in_field) # TODO: finish writing this and write tests if case == 'auto': case = DE.case da = a.degree(DE.t) db = b.degree(DE.t) # The parametric and regular cases are identical, except for this part if parametric: dc = max([i.degree(DE.t) for i in cQ]) else: dc = cQ.degree(DE.t) alpha = cancel(-b.as_poly(DE.t).LC().as_expr()/ a.as_poly(DE.t).LC().as_expr()) if case == 'base': n = max(0, dc - max(db, da - 1)) if db == da - 1 and alpha.is_Integer: n = max(0, alpha, dc - db) elif case == 'primitive': if db > da: n = max(0, dc - db) else: n = max(0, dc - da + 1) etaa, etad = frac_in(DE.d, DE.T[DE.level - 1]) t1 = DE.t with DecrementLevel(DE): alphaa, alphad = frac_in(alpha, DE.t) if db == da - 1: # if alpha == m*Dt + Dz for z in k and m in ZZ: try: (za, zd), m = limited_integrate(alphaa, alphad, [(etaa, etad)], DE) except NonElementaryIntegralException: pass else: assert len(m) == 1 n = max(n, m[0]) elif db == da: # if alpha == Dz/z for z in k*: # beta = -lc(a*Dz + b*z)/(z*lc(a)) # if beta == m*Dt + Dw for w in k and m in ZZ: # n = max(n, m) A = is_log_deriv_k_t_radical_in_field(alphaa, alphad, DE) if A is not None: aa, z = A if aa == 1: beta = -(a*derivation(z, DE).as_poly(t1) + b*z.as_poly(t1)).LC()/(z.as_expr()*a.LC()) betaa, betad = frac_in(beta, DE.t) try: (za, zd), m = limited_integrate(betaa, betad, [(etaa, etad)], DE) except NonElementaryIntegralException: pass else: assert len(m) == 1 n = max(n, m[0]) elif case == 'exp': n = max(0, dc - max(db, da)) if da == db: etaa, etad = frac_in(DE.d.quo(Poly(DE.t, DE.t)), DE.T[DE.level - 1]) with DecrementLevel(DE): alphaa, alphad = frac_in(alpha, DE.t) A = parametric_log_deriv(alphaa, alphad, etaa, etad, DE) if A is not None: # if alpha == m*Dt/t + Dz/z for z in k* and m in ZZ: # n = max(n, m) a, m, z = A if a == 1: n = max(n, m) elif case in ['tan', 'other_nonlinear']: delta = DE.d.degree(DE.t) lam = DE.d.LC() alpha = cancel(alpha/lam) n = max(0, dc - max(da + delta - 1, db)) if db == da + delta - 1 and alpha.is_Integer: n = max(0, alpha, dc - db) else: raise ValueError("case must be one of {'exp', 'tan', 'primitive', " "'other_nonlinear', 'base'}, not %s." % case) return n def spde(a, b, c, n, DE): """ Rothstein's Special Polynomial Differential Equation algorithm. Given a derivation D on k[t], an integer n and a, b, c in k[t] with a != 0, either raise NonElementaryIntegralException, in which case the equation a*Dq + b*q == c has no solution of degree at most n in k[t], or return the tuple (B, C, m, alpha, beta) such that B, C, alpha, beta in k[t], m in ZZ, and any solution q in k[t] of degree at most n of a*Dq + b*q == c must be of the form q == alpha*h + beta, where h in k[t], deg(h) <= m, and Dh + B*h == C. This constitutes step 4 of the outline given in the rde.py docstring. """ zero = Poly(0, DE.t) alpha = Poly(1, DE.t) beta = Poly(0, DE.t) pow_a = 0 while True: if (n < 0) is True: if c.is_zero: return (zero, zero, 0, zero, beta) raise NonElementaryIntegralException g = a.gcd(b) if not c.rem(g).is_zero: # g does not divide c raise NonElementaryIntegralException a, b, c = a.quo(g), b.quo(g), c.quo(g) if a.degree(DE.t) == 0: b = b.to_field().quo(a) c = c.to_field().quo(a) return (b, c, n, alpha, beta) r, z = gcdex_diophantine(b, a, c) b += derivation(a, DE) c = z - derivation(r, DE) n -= a.degree(DE.t) alpha *= a beta += (a**pow_a)*r pow_a += 1 def no_cancel_b_large(b, c, n, DE): """ Poly Risch Differential Equation - No cancellation: deg(b) large enough. Given a derivation D on k[t], n either an integer or +oo, and b, c in k[t] with b != 0 and either D == d/dt or deg(b) > max(0, deg(D) - 1), either raise NonElementaryIntegralException, in which case the equation Dq + b*q == c has no solution of degree at most n in k[t], or a solution q in k[t] of this equation with deg(q) < n. """ q = Poly(0, DE.t) while not c.is_zero: m = c.degree(DE.t) - b.degree(DE.t) if not 0 <= m <= n: # n < 0 or m < 0 or m > n raise NonElementaryIntegralException p = Poly(c.as_poly(DE.t).LC()/b.as_poly(DE.t).LC()*DE.t**m, DE.t, expand=False) q = q + p n = m - 1 c = c - derivation(p, DE) - b*p return q def no_cancel_b_small(b, c, n, DE): """ Poly Risch Differential Equation - No cancellation: deg(b) small enough. Given a derivation D on k[t], n either an integer or +oo, and b, c in k[t] with deg(b) < deg(D) - 1 and either D == d/dt or deg(D) >= 2, either raise NonElementaryIntegralException, in which case the equation Dq + b*q == c has no solution of degree at most n in k[t], or a solution q in k[t] of this equation with deg(q) <= n, or the tuple (h, b0, c0) such that h in k[t], b0, c0, in k, and for any solution q in k[t] of degree at most n of Dq + bq == c, y == q - h is a solution in k of Dy + b0*y == c0. """ q = Poly(0, DE.t) while not c.is_zero: if n == 0: m = 0 else: m = c.degree(DE.t) - DE.d.degree(DE.t) + 1 if not 0 <= m <= n: # n < 0 or m < 0 or m > n raise NonElementaryIntegralException if m > 0: p = Poly(c.as_poly(DE.t).LC()/(m*DE.d.as_poly(DE.t).LC())*DE.t**m, DE.t, expand=False) else: if b.degree(DE.t) != c.degree(DE.t): raise NonElementaryIntegralException if b.degree(DE.t) == 0: return (q, b.as_poly(DE.T[DE.level - 1]), c.as_poly(DE.T[DE.level - 1])) p = Poly(c.as_poly(DE.t).LC()/b.as_poly(DE.t).LC(), DE.t, expand=False) q = q + p n = m - 1 c = c - derivation(p, DE) - b*p return q # TODO: better name for this function def no_cancel_equal(b, c, n, DE): """ Poly Risch Differential Equation - No cancellation: deg(b) == deg(D) - 1 Given a derivation D on k[t] with deg(D) >= 2, n either an integer or +oo, and b, c in k[t] with deg(b) == deg(D) - 1, either raise NonElementaryIntegralException, in which case the equation Dq + b*q == c has no solution of degree at most n in k[t], or a solution q in k[t] of this equation with deg(q) <= n, or the tuple (h, m, C) such that h in k[t], m in ZZ, and C in k[t], and for any solution q in k[t] of degree at most n of Dq + b*q == c, y == q - h is a solution in k[t] of degree at most m of Dy + b*y == C. """ q = Poly(0, DE.t) lc = cancel(-b.as_poly(DE.t).LC()/DE.d.as_poly(DE.t).LC()) if lc.is_Integer and lc.is_positive: M = lc else: M = -1 while not c.is_zero: m = max(M, c.degree(DE.t) - DE.d.degree(DE.t) + 1) if not 0 <= m <= n: # n < 0 or m < 0 or m > n raise NonElementaryIntegralException u = cancel(m*DE.d.as_poly(DE.t).LC() + b.as_poly(DE.t).LC()) if u.is_zero: return (q, m, c) if m > 0: p = Poly(c.as_poly(DE.t).LC()/u*DE.t**m, DE.t, expand=False) else: if c.degree(DE.t) != DE.d.degree(DE.t) - 1: raise NonElementaryIntegralException else: p = c.as_poly(DE.t).LC()/b.as_poly(DE.t).LC() q = q + p n = m - 1 c = c - derivation(p, DE) - b*p return q def cancel_primitive(b, c, n, DE): """ Poly Risch Differential Equation - Cancellation: Primitive case. Given a derivation D on k[t], n either an integer or +oo, b in k, and c in k[t] with Dt in k and b != 0, either raise NonElementaryIntegralException, in which case the equation Dq + b*q == c has no solution of degree at most n in k[t], or a solution q in k[t] of this equation with deg(q) <= n. """ from sympy.integrals.prde import is_log_deriv_k_t_radical_in_field with DecrementLevel(DE): ba, bd = frac_in(b, DE.t) A = is_log_deriv_k_t_radical_in_field(ba, bd, DE) if A is not None: n, z = A if n == 1: # b == Dz/z raise NotImplementedError("is_deriv_in_field() is required to " " solve this problem.") # if z*c == Dp for p in k[t] and deg(p) <= n: # return p/z # else: # raise NonElementaryIntegralException if c.is_zero: return c # return 0 if n < c.degree(DE.t): raise NonElementaryIntegralException q = Poly(0, DE.t) while not c.is_zero: m = c.degree(DE.t) if n < m: raise NonElementaryIntegralException with DecrementLevel(DE): a2a, a2d = frac_in(c.LC(), DE.t) sa, sd = rischDE(ba, bd, a2a, a2d, DE) stm = Poly(sa.as_expr()/sd.as_expr()*DE.t**m, DE.t, expand=False) q += stm n = m - 1 c -= b*stm + derivation(stm, DE) return q def cancel_exp(b, c, n, DE): """ Poly Risch Differential Equation - Cancellation: Hyperexponential case. Given a derivation D on k[t], n either an integer or +oo, b in k, and c in k[t] with Dt/t in k and b != 0, either raise NonElementaryIntegralException, in which case the equation Dq + b*q == c has no solution of degree at most n in k[t], or a solution q in k[t] of this equation with deg(q) <= n. """ from sympy.integrals.prde import parametric_log_deriv eta = DE.d.quo(Poly(DE.t, DE.t)).as_expr() with DecrementLevel(DE): etaa, etad = frac_in(eta, DE.t) ba, bd = frac_in(b, DE.t) A = parametric_log_deriv(ba, bd, etaa, etad, DE) if A is not None: a, m, z = A if a == 1: raise NotImplementedError("is_deriv_in_field() is required to " "solve this problem.") # if c*z*t**m == Dp for p in k and q = p/(z*t**m) in k[t] and # deg(q) <= n: # return q # else: # raise NonElementaryIntegralException if c.is_zero: return c # return 0 if n < c.degree(DE.t): raise NonElementaryIntegralException q = Poly(0, DE.t) while not c.is_zero: m = c.degree(DE.t) if n < m: raise NonElementaryIntegralException # a1 = b + m*Dt/t a1 = b.as_expr() with DecrementLevel(DE): # TODO: Write a dummy function that does this idiom a1a, a1d = frac_in(a1, DE.t) a1a = a1a*etad + etaa*a1d*Poly(m, DE.t) a1d = a1d*etad a2a, a2d = frac_in(c.LC(), DE.t) sa, sd = rischDE(a1a, a1d, a2a, a2d, DE) stm = Poly(sa.as_expr()/sd.as_expr()*DE.t**m, DE.t, expand=False) q += stm n = m - 1 c -= b*stm + derivation(stm, DE) # deg(c) becomes smaller return q def solve_poly_rde(b, cQ, n, DE, parametric=False): """ Solve a Polynomial Risch Differential Equation with degree bound n. This constitutes step 4 of the outline given in the rde.py docstring. For parametric=False, cQ is c, a Poly; for parametric=True, cQ is Q == [q1, ..., qm], a list of Polys. """ from sympy.integrals.prde import (prde_no_cancel_b_large, prde_no_cancel_b_small) # No cancellation if not b.is_zero and (DE.case == 'base' or b.degree(DE.t) > max(0, DE.d.degree(DE.t) - 1)): if parametric: return prde_no_cancel_b_large(b, cQ, n, DE) return no_cancel_b_large(b, cQ, n, DE) elif (b.is_zero or b.degree(DE.t) < DE.d.degree(DE.t) - 1) and \ (DE.case == 'base' or DE.d.degree(DE.t) >= 2): if parametric: return prde_no_cancel_b_small(b, cQ, n, DE) R = no_cancel_b_small(b, cQ, n, DE) if isinstance(R, Poly): return R else: # XXX: Might k be a field? (pg. 209) h, b0, c0 = R with DecrementLevel(DE): b0, c0 = b0.as_poly(DE.t), c0.as_poly(DE.t) assert b0 is not None # See above comment assert c0 is not None y = solve_poly_rde(b0, c0, n, DE).as_poly(DE.t) return h + y elif DE.d.degree(DE.t) >= 2 and b.degree(DE.t) == DE.d.degree(DE.t) - 1 and \ n > -b.as_poly(DE.t).LC()/DE.d.as_poly(DE.t).LC(): # TODO: Is this check necessary, and if so, what should it do if it fails? # b comes from the first element returned from spde() assert b.as_poly(DE.t).LC().is_number if parametric: raise NotImplementedError("prde_no_cancel_b_equal() is not yet " "implemented.") R = no_cancel_equal(b, cQ, n, DE) if isinstance(R, Poly): return R else: h, m, C = R # XXX: Or should it be rischDE()? y = solve_poly_rde(b, C, m, DE) return h + y else: # Cancellation if b.is_zero: raise NotImplementedError("Remaining cases for Poly (P)RDE are " "not yet implemented (is_deriv_in_field() required).") else: if DE.case == 'exp': if parametric: raise NotImplementedError("Parametric RDE cancellation " "hyperexponential case is not yet implemented.") return cancel_exp(b, cQ, n, DE) elif DE.case == 'primitive': if parametric: raise NotImplementedError("Parametric RDE cancellation " "primitive case is not yet implemented.") return cancel_primitive(b, cQ, n, DE) else: raise NotImplementedError("Other Poly (P)RDE cancellation " "cases are not yet implemented (%s)." % case) if parametric: raise NotImplementedError("Remaining cases for Poly PRDE not yet " "implemented.") raise NotImplementedError("Remaining cases for Poly RDE not yet " "implemented.") def rischDE(fa, fd, ga, gd, DE): """ Solve a Risch Differential Equation: Dy + f*y == g. See the outline in the docstring of rde.py for more information about the procedure used. Either raise NonElementaryIntegralException, in which case there is no solution y in the given differential field, or return y in k(t) satisfying Dy + f*y == g, or raise NotImplementedError, in which case, the algorithms necessary to solve the given Risch Differential Equation have not yet been implemented. """ _, (fa, fd) = weak_normalizer(fa, fd, DE) a, (ba, bd), (ca, cd), hn = normal_denom(fa, fd, ga, gd, DE) A, B, C, hs = special_denom(a, ba, bd, ca, cd, DE) try: # Until this is fully implemented, use oo. Note that this will almost # certainly cause non-termination in spde() (unless A == 1), and # *might* lead to non-termination in the next step for a nonelementary # integral (I don't know for certain yet). Fortunately, spde() is # currently written recursively, so this will just give # RuntimeError: maximum recursion depth exceeded. n = bound_degree(A, B, C, DE) except NotImplementedError: # Useful for debugging: # import warnings # warnings.warn("rischDE: Proceeding with n = oo; may cause " # "non-termination.") n = oo B, C, m, alpha, beta = spde(A, B, C, n, DE) y = solve_poly_rde(B, C, m, DE) return (alpha*y + beta, hn*hs) sympy-0.7.4.1/sympy/integrals/heurisch.py0000644000175000017500000004610612253362407020610 0ustar georgeskgeorgeskfrom __future__ import print_function, division from collections import defaultdict from itertools import permutations from sympy.core.add import Add from sympy.core.basic import Basic from sympy.core.mul import Mul from sympy.core.symbol import Symbol, Wild, Dummy from sympy.core.basic import C, sympify from sympy.core.numbers import Rational, I, pi from sympy.core.relational import Eq from sympy.core.singleton import S from sympy.functions import exp, sin, cos, tan, cot, asin, atan from sympy.functions import log, sinh, cosh, tanh, coth, asinh, acosh from sympy.functions import sqrt, erf, erfi, li, Ei from sympy.functions.elementary.piecewise import Piecewise from sympy.logic.boolalg import And from sympy.solvers.solvers import solve, denoms from sympy.utilities.iterables import uniq from sympy.polys import quo, gcd, lcm, factor, cancel, PolynomialError from sympy.polys.monomials import itermonomials from sympy.polys.polyroots import root_factors from sympy.polys.rings import PolyRing from sympy.polys.solvers import solve_lin_sys from sympy.polys.constructor import construct_domain from sympy.core.compatibility import reduce, default_sort_key def components(f, x): """ Returns a set of all functional components of the given expression which includes symbols, function applications and compositions and non-integer powers. Fractional powers are collected with with minimal, positive exponents. >>> from sympy import cos, sin >>> from sympy.abc import x, y >>> from sympy.integrals.heurisch import components >>> components(sin(x)*cos(x)**2, x) set([x, sin(x), cos(x)]) See Also ======== heurisch """ result = set() if x in f.free_symbols: if f.is_Symbol: result.add(f) elif f.is_Function or f.is_Derivative: for g in f.args: result |= components(g, x) result.add(f) elif f.is_Pow: result |= components(f.base, x) if not f.exp.is_Integer: if f.exp.is_Rational: result.add(f.base**Rational(1, f.exp.q)) else: result |= components(f.exp, x) | set([f]) else: for g in f.args: result |= components(g, x) return result # name -> [] of symbols _symbols_cache = {} # NB @cacheit is not convenient here def _symbols(name, n): """get vector of symbols local to this module""" try: lsyms = _symbols_cache[name] except KeyError: lsyms = [] _symbols_cache[name] = lsyms while len(lsyms) < n: lsyms.append( Dummy('%s%i' % (name, len(lsyms))) ) return lsyms[:n] def heurisch_wrapper(f, x, rewrite=False, hints=None, mappings=None, retries=3, degree_offset=0, unnecessary_permutations=None): """ A wrapper around the heurisch integration algorithm. This method takes the result from heurisch and checks for poles in the denominator. For each of these poles, the integral is reevaluated, and the final integration result is given in terms of a Piecewise. Examples ======== >>> from sympy.core import symbols >>> from sympy.functions import cos >>> from sympy.integrals.heurisch import heurisch, heurisch_wrapper >>> n, x = symbols('n x') >>> heurisch(cos(n*x), x) sin(n*x)/n >>> heurisch_wrapper(cos(n*x), x) Piecewise((x, n == 0), (sin(n*x)/n, True)) See Also ======== heurisch """ f = sympify(f) if x not in f.free_symbols: return f*x res = heurisch(f, x, rewrite, hints, mappings, retries, degree_offset, unnecessary_permutations) if not isinstance(res, Basic): return res # We consider each denominator in the expression, and try to find # cases where one or more symbolic denominator might be zero. The # conditions for these cases are stored in the list slns. slns = [] for d in denoms(res): try: slns += solve(d, dict=True, exclude=(x,)) except NotImplementedError: pass if not slns: return res slns = list(uniq(slns)) # Remove the solutions corresponding to poles in the original expression. slns0 = [] for d in denoms(f): try: slns0 += solve(d, dict=True, exclude=(x,)) except NotImplementedError: pass slns = [s for s in slns if s not in slns0] if not slns: return res if len(slns) > 1: eqs = [] for sub_dict in slns: eqs.extend([Eq(key, value) for key, value in sub_dict.items()]) slns = solve(eqs, dict=True, exclude=(x,)) + slns # For each case listed in the list slns, we reevaluate the integral. pairs = [] for sub_dict in slns: expr = heurisch(f.subs(sub_dict), x, rewrite, hints, mappings, retries, degree_offset, unnecessary_permutations) cond = And(*[Eq(key, value) for key, value in sub_dict.items()]) pairs.append((expr, cond)) pairs.append((heurisch(f, x, rewrite, hints, mappings, retries, degree_offset, unnecessary_permutations), True)) return Piecewise(*pairs) def heurisch(f, x, rewrite=False, hints=None, mappings=None, retries=3, degree_offset=0, unnecessary_permutations=None): """ Compute indefinite integral using heuristic Risch algorithm. This is a heuristic approach to indefinite integration in finite terms using the extended heuristic (parallel) Risch algorithm, based on Manuel Bronstein's "Poor Man's Integrator". The algorithm supports various classes of functions including transcendental elementary or special functions like Airy, Bessel, Whittaker and Lambert. Note that this algorithm is not a decision procedure. If it isn't able to compute the antiderivative for a given function, then this is not a proof that such a functions does not exist. One should use recursive Risch algorithm in such case. It's an open question if this algorithm can be made a full decision procedure. This is an internal integrator procedure. You should use toplevel 'integrate' function in most cases, as this procedure needs some preprocessing steps and otherwise may fail. Specification ============= heurisch(f, x, rewrite=False, hints=None) where f : expression x : symbol rewrite -> force rewrite 'f' in terms of 'tan' and 'tanh' hints -> a list of functions that may appear in anti-derivate - hints = None --> no suggestions at all - hints = [ ] --> try to figure out - hints = [f1, ..., fn] --> we know better Examples ======== >>> from sympy import tan >>> from sympy.integrals.heurisch import heurisch >>> from sympy.abc import x, y >>> heurisch(y*tan(x), x) y*log(tan(x)**2 + 1)/2 See Manuel Bronstein's "Poor Man's Integrator": [1] http://www-sop.inria.fr/cafe/Manuel.Bronstein/pmint/index.html For more information on the implemented algorithm refer to: [2] K. Geddes, L. Stefanus, On the Risch-Norman Integration Method and its Implementation in Maple, Proceedings of ISSAC'89, ACM Press, 212-217. [3] J. H. Davenport, On the Parallel Risch Algorithm (I), Proceedings of EUROCAM'82, LNCS 144, Springer, 144-157. [4] J. H. Davenport, On the Parallel Risch Algorithm (III): Use of Tangents, SIGSAM Bulletin 16 (1982), 3-6. [5] J. H. Davenport, B. M. Trager, On the Parallel Risch Algorithm (II), ACM Transactions on Mathematical Software 11 (1985), 356-362. See Also ======== sympy.integrals.integrals.Integral.doit sympy.integrals.integrals.Integral components """ f = sympify(f) if x not in f.free_symbols: return f*x if not f.is_Add: indep, f = f.as_independent(x) else: indep = S.One rewritables = { (sin, cos, cot): tan, (sinh, cosh, coth): tanh, } if rewrite: for candidates, rule in rewritables.items(): f = f.rewrite(candidates, rule) else: for candidates in rewritables.keys(): if f.has(*candidates): break else: rewrite = True terms = components(f, x) if hints is not None: if not hints: a = Wild('a', exclude=[x]) b = Wild('b', exclude=[x]) c = Wild('c', exclude=[x]) for g in set(terms): if g.is_Function: if g.func is li: M = g.args[0].match(a*x**b) if M is not None: terms.add( x*(li(M[a]*x**M[b]) - (M[a]*x**M[b])**(-1/M[b])*Ei((M[b]+1)*log(M[a]*x**M[b])/M[b])) ) #terms.add( x*(li(M[a]*x**M[b]) - (x**M[b])**(-1/M[b])*Ei((M[b]+1)*log(M[a]*x**M[b])/M[b])) ) #terms.add( x*(li(M[a]*x**M[b]) - x*Ei((M[b]+1)*log(M[a]*x**M[b])/M[b])) ) #terms.add( li(M[a]*x**M[b]) - Ei((M[b]+1)*log(M[a]*x**M[b])/M[b]) ) elif g.func is exp: M = g.args[0].match(a*x**2) if M is not None: if M[a].is_positive: terms.add(erfi(sqrt(M[a])*x)) else: # M[a].is_negative or unknown terms.add(erf(sqrt(-M[a])*x)) M = g.args[0].match(a*x**2 + b*x + c) if M is not None: if M[a].is_positive: terms.add(sqrt(pi/4*(-M[a]))*exp(M[c] - M[b]**2/(4*M[a]))* erfi(sqrt(M[a])*x + M[b]/(2*sqrt(M[a])))) elif M[a].is_negative: terms.add(sqrt(pi/4*(-M[a]))*exp(M[c] - M[b]**2/(4*M[a]))* erf(sqrt(-M[a])*x - M[b]/(2*sqrt(-M[a])))) M = g.args[0].match(a*log(x)**2) if M is not None: if M[a].is_positive: terms.add(erfi(sqrt(M[a])*log(x) + 1/(2*sqrt(M[a])))) if M[a].is_negative: terms.add(erf(sqrt(-M[a])*log(x) - 1/(2*sqrt(-M[a])))) elif g.is_Pow: if g.exp.is_Rational and g.exp.q == 2: M = g.base.match(a*x**2 + b) if M is not None and M[b].is_positive: if M[a].is_positive: terms.add(asinh(sqrt(M[a]/M[b])*x)) elif M[a].is_negative: terms.add(asin(sqrt(-M[a]/M[b])*x)) M = g.base.match(a*x**2 - b) if M is not None and M[b].is_positive: if M[a].is_positive: terms.add(acosh(sqrt(M[a]/M[b])*x)) elif M[a].is_negative: terms.add((-M[b]/2*sqrt(-M[a])* atan(sqrt(-M[a])*x/sqrt(M[a]*x**2 - M[b])))) else: terms |= set(hints) for g in set(terms): terms |= components(cancel(g.diff(x)), x) # TODO: caching is significant factor for why permutations work at all. Change this. V = _symbols('x', len(terms)) mapping = dict(list(zip(terms, V))) rev_mapping = {} if unnecessary_permutations is None: unnecessary_permutations = [] for k, v in mapping.items(): rev_mapping[v] = k if mappings is None: # Pre-sort mapping in order of largest to smallest expressions (last is always x). def _sort_key(arg): return default_sort_key(arg[0].as_independent(x)[1]) #optimizing the number of permutations of mappping unnecessary_permutations = [(x, mapping[x])] del mapping[x] mapping = sorted(list(mapping.items()), key=_sort_key, reverse=True) mappings = permutations(mapping) def _substitute(expr): return expr.subs(mapping) for mapping in mappings: mapping = list(mapping) mapping = mapping + unnecessary_permutations diffs = [ _substitute(cancel(g.diff(x))) for g in terms ] denoms = [ g.as_numer_denom()[1] for g in diffs ] if all(h.is_polynomial(*V) for h in denoms) and _substitute(f).is_rational_function(*V): denom = reduce(lambda p, q: lcm(p, q, *V), denoms) break else: if not rewrite: result = heurisch(f, x, rewrite=True, hints=hints, unnecessary_permutations=unnecessary_permutations) if result is not None: return indep*result return None numers = [ cancel(denom*g) for g in diffs ] def _derivation(h): return Add(*[ d * h.diff(v) for d, v in zip(numers, V) ]) def _deflation(p): for y in V: if not p.has(y): continue if _derivation(p) is not S.Zero: c, q = p.as_poly(y).primitive() return _deflation(c)*gcd(q, q.diff(y)).as_expr() else: return p def _splitter(p): for y in V: if not p.has(y): continue if _derivation(y) is not S.Zero: c, q = p.as_poly(y).primitive() q = q.as_expr() h = gcd(q, _derivation(q), y) s = quo(h, gcd(q, q.diff(y), y), y) c_split = _splitter(c) if s.as_poly(y).degree() == 0: return (c_split[0], q * c_split[1]) q_split = _splitter(cancel(q / s)) return (c_split[0]*q_split[0]*s, c_split[1]*q_split[1]) else: return (S.One, p) special = {} for term in terms: if term.is_Function: if term.func is tan: special[1 + _substitute(term)**2] = False elif term.func is tanh: special[1 + _substitute(term)] = False special[1 - _substitute(term)] = False elif term.func is C.LambertW: special[_substitute(term)] = True F = _substitute(f) P, Q = F.as_numer_denom() u_split = _splitter(denom) v_split = _splitter(Q) polys = list(v_split) + [ u_split[0] ] + list(special.keys()) s = u_split[0] * Mul(*[ k for k, v in special.items() if v ]) polified = [ p.as_poly(*V) for p in [s, P, Q] ] if None in polified: return None a, b, c = [ p.total_degree() for p in polified ] poly_denom = (s * v_split[0] * _deflation(v_split[1])).as_expr() def _exponent(g): if g.is_Pow: if g.exp.is_Rational and g.exp.q != 1: if g.exp.p > 0: return g.exp.p + g.exp.q - 1 else: return abs(g.exp.p + g.exp.q) else: return 1 elif not g.is_Atom and g.args: return max([ _exponent(h) for h in g.args ]) else: return 1 A, B = _exponent(f), a + max(b, c) if A > 1 and B > 1: monoms = itermonomials(V, A + B - 1 + degree_offset) else: monoms = itermonomials(V, A + B + degree_offset) poly_coeffs = _symbols('A', len(monoms)) poly_part = Add(*[ poly_coeffs[i]*monomial for i, monomial in enumerate(monoms) ]) reducibles = set() for poly in polys: if poly.has(*V): try: factorization = factor(poly, greedy=True) except PolynomialError: factorization = poly factorization = poly if factorization.is_Mul: reducibles |= set(factorization.args) else: reducibles.add(factorization) def _integrate(field=None): irreducibles = set() for poly in reducibles: for z in poly.atoms(Symbol): if z in V: break else: continue irreducibles |= set(root_factors(poly, z, filter=field)) log_coeffs, log_part = [], [] B = _symbols('B', len(irreducibles)) for i, poly in enumerate(irreducibles): if poly.has(*V): log_coeffs.append(B[i]) log_part.append(log_coeffs[-1] * log(poly)) coeffs = poly_coeffs + log_coeffs # TODO: Currently it's better to use symbolic expressions here instead # of rational functions, because it's simpler and FracElement doesn't # give big speed improvement yet. This is because cancelation is slow # due to slow polynomial GCD algorithms. If this gets improved then # revise this code. candidate = poly_part/poly_denom + Add(*log_part) h = F - _derivation(candidate) / denom raw_numer = h.as_numer_denom()[0] # Rewrite raw_numer as a polynomial in K[coeffs][V] where K is a field # that we have to determine. We can't use simply atoms() because log(3), # sqrt(y) and similar expressions can appear, leading to non-trivial # domains. syms = set(coeffs) | set(V) non_syms = set([]) def find_non_syms(expr): if expr.is_Integer or expr.is_Rational: pass # ignore trivial numbers elif expr in syms: pass # ignore variables elif not expr.has(*syms): non_syms.add(expr) elif expr.is_Add or expr.is_Mul or expr.is_Pow: list(map(find_non_syms, expr.args)) else: # TODO: Non-polynomial expression. This should have been # filtered out at an earlier stage. raise PolynomialError try: find_non_syms(raw_numer) except PolynomialError: return None else: ground, _ = construct_domain(non_syms, field=True) coeff_ring = PolyRing(coeffs, ground) ring = PolyRing(V, coeff_ring) numer = ring.from_expr(raw_numer) solution = solve_lin_sys(numer.coeffs(), coeff_ring) if solution is None: return None else: solution = [ (k.as_expr(), v.as_expr()) for k, v in solution.items() ] return candidate.subs(solution).subs(list(zip(coeffs, [S.Zero]*len(coeffs)))) if not (F.atoms(Symbol) - set(V)): solution = _integrate('Q') if solution is None: solution = _integrate() else: solution = _integrate() if solution is not None: antideriv = solution.subs(rev_mapping) antideriv = cancel(antideriv).expand(force=True) if antideriv.is_Add: antideriv = antideriv.as_independent(x)[1] return indep*antideriv else: if retries >= 0: result = heurisch(f, x, mappings=mappings, rewrite=rewrite, hints=hints, retries=retries - 1, unnecessary_permutations=unnecessary_permutations) if result is not None: return indep*result return None sympy-0.7.4.1/sympy/integrals/meijerint.py0000644000175000017500000021646512253362407020773 0ustar georgeskgeorgesk""" Integrate functions by rewriting them as Meijer G-functions. There are three user-visible functions that can be used by other parts of the sympy library to solve various integration problems: - meijerint_indefinite - meijerint_definite - meijerint_inversion They can be used to compute, respectively, indefinite integrals, definite integrals over intervals of the real line, and inverse laplace-type integrals (from c-I*oo to c+I*oo). See the respective docstrings for details. The main references for this are: [L] Luke, Y. L. (1969), The Special Functions and Their Approximations, Volume 1 [R] 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. [P] A. P. Prudnikov, Yu. A. Brychkov and O. I. Marichev (1990). Integrals and Series: More Special Functions, Vol. 3,. Gordon and Breach Science Publisher """ from __future__ import print_function, division from sympy.core import oo, S, pi, Expr from sympy.core.function import expand, expand_mul, expand_power_base from sympy.core.add import Add from sympy.core.mul import Mul from sympy.core.cache import cacheit from sympy.core.symbol import Dummy, Wild from sympy.simplify import hyperexpand, powdenest from sympy.logic.boolalg import And, Or, BooleanAtom from sympy.functions.special.delta_functions import Heaviside from sympy.functions.elementary.piecewise import Piecewise from sympy.functions.special.hyper import meijerg from sympy.utilities.iterables import multiset_partitions, ordered from sympy.utilities.misc import debug as _debug from sympy.utilities import default_sort_key # keep this at top for easy reference z = Dummy('z') def _create_lookup_table(table): """ Add formulae for the function -> meijerg lookup table. """ def wild(n): return Wild(n, exclude=[z]) p, q, a, b, c = list(map(wild, 'pqabc')) n = Wild('n', properties=[lambda x: x.is_Integer and x > 0]) t = p*z**q def add(formula, an, ap, bm, bq, arg=t, fac=S(1), cond=True, hint=True): table.setdefault(_mytype(formula, z), []).append((formula, [(fac, meijerg(an, ap, bm, bq, arg))], cond, hint)) def addi(formula, inst, cond, hint=True): table.setdefault( _mytype(formula, z), []).append((formula, inst, cond, hint)) def constant(a): return [(a, meijerg([1], [], [], [0], z)), (a, meijerg([], [1], [0], [], z))] table[()] = [(a, constant(a), True, True)] # [P], Section 8. from sympy import unpolarify, Function, Not class IsNonPositiveInteger(Function): nargs = 1 @classmethod def eval(cls, arg): arg = unpolarify(arg) if arg.is_Integer is True: return arg <= 0 # Section 8.4.2 from sympy import (gamma, pi, cos, exp, re, sin, sqrt, sinh, cosh, factorial, log, erf, erfc, erfi, polar_lift) # TODO this needs more polar_lift (c/f entry for exp) add(Heaviside(t - b)*(t - b)**(a - 1), [a], [], [], [0], t/b, gamma(a)*b**(a - 1), And(b > 0)) add(Heaviside(b - t)*(b - t)**(a - 1), [], [a], [0], [], t/b, gamma(a)*b**(a - 1), And(b > 0)) add(Heaviside(z - (b/p)**(1/q))*(t - b)**(a - 1), [a], [], [], [0], t/b, gamma(a)*b**(a - 1), And(b > 0)) add(Heaviside((b/p)**(1/q) - z)*(b - t)**(a - 1), [], [a], [0], [], t/b, gamma(a)*b**(a - 1), And(b > 0)) add((b + t)**(-a), [1 - a], [], [0], [], t/b, b**(-a)/gamma(a), hint=Not(IsNonPositiveInteger(a))) add(abs(b - t)**(-a), [1 - a], [(1 - a)/2], [0], [(1 - a)/2], t/b, pi/(gamma(a)*cos(pi*a/2))*abs(b)**(-a), re(a) < 1) add((t**a - b**a)/(t - b), [0, a], [], [0, a], [], t/b, b**(a - 1)*sin(a*pi)/pi) # 12 def A1(r, sign, nu): return pi**(-S(1)/2)*(-sign*nu/2)**(1 - 2*r) def tmpadd(r, sgn): # XXX the a**2 is bad for matching add((sqrt(a**2 + t) + sgn*a)**b/(a**2 + t)**r, [(1 + b)/2, 1 - 2*r + b/2], [], [(b - sgn*b)/2], [(b + sgn*b)/2], t/a**2, a**(b - 2*r)*A1(r, sgn, b)) tmpadd(0, 1) tmpadd(0, -1) tmpadd(S(1)/2, 1) tmpadd(S(1)/2, -1) # 13 def tmpadd(r, sgn): add((sqrt(a + p*z**q) + sgn*sqrt(p)*z**(q/2))**b/(a + p*z**q)**r, [1 - r + sgn*b/2], [1 - r - sgn*b/2], [0, S(1)/2], [], p*z**q/a, a**(b/2 - r)*A1(r, sgn, b)) tmpadd(0, 1) tmpadd(0, -1) tmpadd(S(1)/2, 1) tmpadd(S(1)/2, -1) # (those after look obscure) # Section 8.4.3 add(exp(polar_lift(-1)*t), [], [], [0], []) # TODO can do sin^n, sinh^n by expansion ... where? # 8.4.4 (hyperbolic functions) add(sinh(t), [], [1], [S(1)/2], [1, 0], t**2/4, pi**(S(3)/2)) add(cosh(t), [], [S(1)/2], [0], [S(1)/2, S(1)/2], t**2/4, pi**(S(3)/2)) # Section 8.4.5 # TODO can do t + a. but can also do by expansion... (XXX not really) add(sin(t), [], [], [S(1)/2], [0], t**2/4, sqrt(pi)) add(cos(t), [], [], [0], [S(1)/2], t**2/4, sqrt(pi)) # Section 8.5.5 def make_log1(subs): N = subs[n] return [((-1)**N*factorial(N), meijerg([], [1]*(N + 1), [0]*(N + 1), [], t))] def make_log2(subs): N = subs[n] return [(factorial(N), meijerg([1]*(N + 1), [], [], [0]*(N + 1), t))] # TODO these only hold for positive p, and can be made more general # but who uses log(x)*Heaviside(a-x) anyway ... # TODO also it would be nice to derive them recursively ... addi(log(t)**n*Heaviside(1 - t), make_log1, True) addi(log(t)**n*Heaviside(t - 1), make_log2, True) def make_log3(subs): return make_log1(subs) + make_log2(subs) addi(log(t)**n, make_log3, True) addi(log(t + a), constant(log(a)) + [(S(1), meijerg([1, 1], [], [1], [0], t/a))], True) addi(log(abs(t - a)), constant(log(abs(a))) + [(pi, meijerg([1, 1], [S(1)/2], [1], [0, S(1)/2], t/a))], True) # TODO log(x)/(x+a) and log(x)/(x-1) can also be done. should they # be derivable? # TODO further formulae in this section seem obscure # Sections 8.4.9-10 # TODO # Section 8.4.11 from sympy import Ei, I, expint, Si, Ci, Shi, Chi, fresnels, fresnelc addi(Ei(t), constant(-I*pi) + [(S(-1), meijerg([], [1], [0, 0], [], t*polar_lift(-1)))], True) # Section 8.4.12 add(Si(t), [1], [], [S(1)/2], [0, 0], t**2/4, sqrt(pi)/2) add(Ci(t), [], [1], [0, 0], [S(1)/2], t**2/4, -sqrt(pi)/2) # Section 8.4.13 add(Shi(t), [S(1)/2], [], [0], [S(-1)/2, S(-1)/2], polar_lift(-1)*t**2/4, t*sqrt(pi)/4) add(Chi(t), [], [S(1)/2, 1], [0, 0], [S(1)/2, S(1)/2], t**2/4, - pi**S('3/2')/2) # generalized exponential integral add(expint(a, t), [], [a], [a - 1, 0], [], t) # Section 8.4.14 add(erf(t), [1], [], [S(1)/2], [0], t**2, 1/sqrt(pi)) # TODO exp(-x)*erf(I*x) does not work add(erfc(t), [], [1], [0, S(1)/2], [], t**2, 1/sqrt(pi)) # This formula for erfi(z) yields a wrong(?) minus sign #add(erfi(t), [1], [], [S(1)/2], [0], -t**2, I/sqrt(pi)) add(erfi(t), [S(1)/2], [], [0], [-S(1)/2], -t**2, t/sqrt(pi)) # Fresnel Integrals add(fresnels(t), [1], [], [S(3)/4], [0, S(1)/4], pi**2*t**4/16, S(1)/2) add(fresnelc(t), [1], [], [S(1)/4], [0, S(3)/4], pi**2*t**4/16, S(1)/2) ##### bessel-type functions ##### from sympy import besselj, bessely, besseli, besselk # Section 8.4.19 add(besselj(a, t), [], [], [a/2], [-a/2], t**2/4) # all of the following are derivable #add(sin(t)*besselj(a, t), [S(1)/4, S(3)/4], [], [(1+a)/2], # [-a/2, a/2, (1-a)/2], t**2, 1/sqrt(2)) #add(cos(t)*besselj(a, t), [S(1)/4, S(3)/4], [], [a/2], # [-a/2, (1+a)/2, (1-a)/2], t**2, 1/sqrt(2)) #add(besselj(a, t)**2, [S(1)/2], [], [a], [-a, 0], t**2, 1/sqrt(pi)) #add(besselj(a, t)*besselj(b, t), [0, S(1)/2], [], [(a + b)/2], # [-(a+b)/2, (a - b)/2, (b - a)/2], t**2, 1/sqrt(pi)) # Section 8.4.20 add(bessely(a, t), [], [-(a + 1)/2], [a/2, -a/2], [-(a + 1)/2], t**2/4) # TODO all of the following should be derivable #add(sin(t)*bessely(a, t), [S(1)/4, S(3)/4], [(1 - a - 1)/2], # [(1 + a)/2, (1 - a)/2], [(1 - a - 1)/2, (1 - 1 - a)/2, (1 - 1 + a)/2], # t**2, 1/sqrt(2)) #add(cos(t)*bessely(a, t), [S(1)/4, S(3)/4], [(0 - a - 1)/2], # [(0 + a)/2, (0 - a)/2], [(0 - a - 1)/2, (1 - 0 - a)/2, (1 - 0 + a)/2], # t**2, 1/sqrt(2)) #add(besselj(a, t)*bessely(b, t), [0, S(1)/2], [(a - b - 1)/2], # [(a + b)/2, (a - b)/2], [(a - b - 1)/2, -(a + b)/2, (b - a)/2], # t**2, 1/sqrt(pi)) #addi(bessely(a, t)**2, # [(2/sqrt(pi), meijerg([], [S(1)/2, S(1)/2 - a], [0, a, -a], # [S(1)/2 - a], t**2)), # (1/sqrt(pi), meijerg([S(1)/2], [], [a], [-a, 0], t**2))], # True) #addi(bessely(a, t)*bessely(b, t), # [(2/sqrt(pi), meijerg([], [0, S(1)/2, (1 - a - b)/2], # [(a + b)/2, (a - b)/2, (b - a)/2, -(a + b)/2], # [(1 - a - b)/2], t**2)), # (1/sqrt(pi), meijerg([0, S(1)/2], [], [(a + b)/2], # [-(a + b)/2, (a - b)/2, (b - a)/2], t**2))], # True) # Section 8.4.21 ? # Section 8.4.22 add(besseli(a, t), [], [(1 + a)/2], [a/2], [-a/2, (1 + a)/2], t**2/4, pi) # TODO many more formulas. should all be derivable # Section 8.4.23 add(besselk(a, t), [], [], [a/2, -a/2], [], t**2/4, S(1)/2) # TODO many more formulas. should all be derivable # Complete elliptic integrals K(z) and E(z) from sympy import elliptic_k, elliptic_e add(elliptic_k(t), [S.Half, S.Half], [], [0], [0], -t, S.Half) add(elliptic_e(t), [S.Half, 3*S.Half], [], [0], [0], -t, -S.Half/2) #################################################################### # First some helper functions. #################################################################### from sympy.utilities.timeutils import timethis timeit = timethis('meijerg') def _mytype(f, x): """ Create a hashable entity describing the type of f. """ if x not in f.free_symbols: return () elif f.is_Function: return (type(f),) else: types = [_mytype(a, x) for a in f.args] res = [] for t in types: res += list(t) res.sort() return tuple(res) class _CoeffExpValueError(ValueError): """ Exception raised by _get_coeff_exp, for internal use only. """ pass def _get_coeff_exp(expr, x): """ When expr is known to be of the form c*x**b, with c and/or b possibly 1, return c, b. >>> from sympy.abc import x, a, b >>> from sympy.integrals.meijerint import _get_coeff_exp >>> _get_coeff_exp(a*x**b, x) (a, b) >>> _get_coeff_exp(x, x) (1, 1) >>> _get_coeff_exp(2*x, x) (2, 1) >>> _get_coeff_exp(x**3, x) (1, 3) """ from sympy import powsimp (c, m) = expand_power_base(powsimp(expr)).as_coeff_mul(x) if not m: return c, S(0) [m] = m if m.is_Pow: if m.base != x: raise _CoeffExpValueError('expr not of form a*x**b') return c, m.exp elif m == x: return c, S(1) else: raise _CoeffExpValueError('expr not of form a*x**b: %s' % expr) def _exponents(expr, x): """ Find the exponents of ``x`` (not including zero) in ``expr``. >>> from sympy.integrals.meijerint import _exponents >>> from sympy.abc import x, y >>> from sympy import sin >>> _exponents(x, x) set([1]) >>> _exponents(x**2, x) set([2]) >>> _exponents(x**2 + x, x) set([1, 2]) >>> _exponents(x**3*sin(x + x**y) + 1/x, x) set([-1, 1, 3, y]) """ def _exponents_(expr, x, res): if expr == x: res.update([1]) return if expr.is_Pow and expr.base == x: res.update([expr.exp]) return for arg in expr.args: _exponents_(arg, x, res) res = set() _exponents_(expr, x, res) return res def _functions(expr, x): """ Find the types of functions in expr, to estimate the complexity. """ from sympy import Function return set(e.func for e in expr.atoms(Function) if x in e.free_symbols) def _find_splitting_points(expr, x): """ Find numbers a such that a linear substitution x --> x+a would (hopefully) simplify expr. >>> from sympy.integrals.meijerint import _find_splitting_points as fsp >>> from sympy import sin >>> from sympy.abc import a, x >>> fsp(x, x) set([0]) >>> fsp((x-1)**3, x) set([1]) >>> fsp(sin(x+3)*x, x) set([-3, 0]) """ from sympy import Tuple p, q = [Wild(n, exclude=[x]) for n in 'pq'] def compute_innermost(expr, res): if not isinstance(expr, Expr): return m = expr.match(p*x + q) if m and m[p] != 0: res.add(-m[q]/m[p]) return if expr.is_Atom: return for arg in expr.args: compute_innermost(arg, res) innermost = set() compute_innermost(expr, innermost) return innermost def _split_mul(f, x): """ Split expression ``f`` into fac, po, g, where fac is a constant factor, po = x**s for some s independent of s, and g is "the rest". >>> from sympy.integrals.meijerint import _split_mul >>> from sympy import sin >>> from sympy.abc import s, x >>> _split_mul((3*x)**s*sin(x**2)*x, x) (3**s, x*x**s, sin(x**2)) """ from sympy import polarify, unpolarify fac = S(1) po = S(1) g = S(1) f = expand_power_base(f) args = Mul.make_args(f) for a in args: if a == x: po *= x elif x not in a.free_symbols: fac *= a else: if a.is_Pow and x not in a.exp.free_symbols: c, t = a.base.as_coeff_mul(x) if t != (x,): c, t = expand_mul(a.base).as_coeff_mul(x) if t == (x,): po *= x**a.exp fac *= unpolarify(polarify(c**a.exp, subs=False)) continue g *= a return fac, po, g def _mul_args(f): """ Return a list ``L`` such that Mul(*L) == f. If f is not a Mul or Pow, L=[f]. If f=g**n for an integer n, L=[g]*n. If f is a Mul, L comes from applying _mul_args to all factors of f. """ args = Mul.make_args(f) gs = [] for g in args: if g.is_Pow and g.exp.is_Integer: n = g.exp base = g.base if n < 0: n = -n base = 1/base gs += [base]*n else: gs.append(g) return gs def _mul_as_two_parts(f): """ Find all the ways to split f into a product of two terms. Return None on failure. Although the order is canonical from multiset_partitions, this is not necessarily the best order to process the terms. For example, if the case of len(gs) == 2 is removed and multiset is allowed to sort the terms, some tests fail. Examples ======== >>> from sympy.integrals.meijerint import _mul_as_two_parts >>> from sympy import sin, exp, ordered >>> from sympy.abc import x >>> list(ordered(_mul_as_two_parts(x*sin(x)*exp(x)))) [(x, exp(x)*sin(x)), (x*exp(x), sin(x)), (x*sin(x), exp(x))] """ gs = _mul_args(f) if len(gs) < 2: return None if len(gs) == 2: return [tuple(gs)] return [(Mul(*x), Mul(*y)) for (x, y) in multiset_partitions(gs, 2)] def _inflate_g(g, n): """ Return C, h such that h is a G function of argument z**n and g = C*h. """ # TODO should this be a method of meijerg? # See: [L, page 150, equation (5)] def inflate(params, n): """ (a1, .., ak) -> (a1/n, (a1+1)/n, ..., (ak + n-1)/n) """ res = [] for a in params: for i in range(n): res.append((a + i)/n) return res v = S(len(g.ap) - len(g.bq)) C = n**(1 + g.nu + v/2) C /= (2*pi)**((n - 1)*g.delta) return C, meijerg(inflate(g.an, n), inflate(g.aother, n), inflate(g.bm, n), inflate(g.bother, n), g.argument**n * n**(n*v)) def _flip_g(g): """ Turn the G function into one of inverse argument (i.e. G(1/x) -> G'(x)) """ # See [L], section 5.2 def tr(l): return [1 - a for a in l] return meijerg(tr(g.bm), tr(g.bother), tr(g.an), tr(g.aother), 1/g.argument) def _inflate_fox_h(g, a): r""" Let d denote the integrand in the definition of the G function ``g``. Consider the function H which is defined in the same way, but with integrand d/Gamma(a*s) (contour conventions as usual). If a is rational, the function H can be written as C*G, for a constant C and a G-function G. This function returns C, G. """ if a < 0: return _inflate_fox_h(_flip_g(g), -a) p = S(a.p) q = S(a.q) # We use the substitution s->qs, i.e. inflate g by q. We are left with an # extra factor of Gamma(p*s), for which we use Gauss' multiplication # theorem. D, g = _inflate_g(g, q) z = g.argument D /= (2*pi)**((1 - p)/2)*p**(-S(1)/2) z /= p**p bs = [(n + 1)/p for n in range(p)] return D, meijerg(g.an, g.aother, g.bm, list(g.bother) + bs, z) _dummies = {} def _dummy(name, token, expr, **kwargs): """ Return a dummy. This will return the same dummy if the same token+name is requested more than once, and it is not already in expr. This is for being cache-friendly. """ d = _dummy_(name, token, **kwargs) if d in expr.free_symbols: return Dummy(name, **kwargs) return d def _dummy_(name, token, **kwargs): """ Return a dummy associated to name and token. Same effect as declaring it globally. """ global _dummies if not (name, token) in _dummies: _dummies[(name, token)] = Dummy(name, **kwargs) return _dummies[(name, token)] def _is_analytic(f, x): """ Check if f(x), when expressed using G functions on the positive reals, will in fact agree with the G functions almost everywhere """ from sympy import Heaviside, Abs return not any(x in expr.free_symbols for expr in f.atoms(Heaviside, Abs)) def _condsimp(cond): """ Do naive simplifications on ``cond``. Note that this routine is completely ad-hoc, simplification rules being added as need arises rather than following any logical pattern. >>> from sympy.integrals.meijerint import _condsimp as simp >>> from sympy import Or, Eq, unbranched_argument as arg, And >>> from sympy.abc import x, y, z >>> simp(Or(x < y, z, Eq(x, y))) Or(x <= y, z) >>> simp(Or(x <= y, And(x < y, z))) x <= y """ from sympy import ( symbols, Wild, Eq, unbranched_argument, exp_polar, pi, I, periodic_argument, oo, polar_lift) from sympy.logic.boolalg import BooleanFunction if not isinstance(cond, BooleanFunction): return cond cond = cond.func(*list(map(_condsimp, cond.args))) change = True p, q, r = symbols('p q r', cls=Wild) rules = [ (Or(p < q, Eq(p, q)), p <= q), # The next two obviously are instances of a general pattern, but it is # easier to spell out the few cases we care about. (And(abs(unbranched_argument(p)) <= pi, abs(unbranched_argument(exp_polar(-2*pi*I)*p)) <= pi), Eq(unbranched_argument(exp_polar(-I*pi)*p), 0)), (And(abs(unbranched_argument(p)) <= pi/2, abs(unbranched_argument(exp_polar(-pi*I)*p)) <= pi/2), Eq(unbranched_argument(exp_polar(-I*pi/2)*p), 0)), (Or(p <= q, And(p < q, r)), p <= q) ] while change: change = False for fro, to in rules: if fro.func != cond.func: continue for n, arg in enumerate(cond.args): if r in fro.args[0].free_symbols: m = arg.match(fro.args[1]) num = 1 else: num = 0 m = arg.match(fro.args[0]) if not m: continue otherargs = [x.subs(m) for x in fro.args[:num] + fro.args[num + 1:]] otherlist = [n] for arg2 in otherargs: for k, arg3 in enumerate(cond.args): if k in otherlist: continue if arg2 == arg3: otherlist += [k] break if arg3.func is And and arg2.args[1] == r and \ arg2.func is And and arg2.args[0] in arg3.args: otherlist += [k] break if arg3.func is And and arg2.args[0] == r and \ arg2.func is And and arg2.args[1] in arg3.args: otherlist += [k] break if len(otherlist) != len(otherargs) + 1: continue newargs = [arg for (k, arg) in enumerate(cond.args) if k not in otherlist] + [to.subs(m)] cond = cond.func(*newargs) change = True break # final tweak def repl_eq(orig): if orig.lhs == 0: expr = orig.rhs elif orig.rhs == 0: expr = orig.lhs else: return orig m = expr.match(unbranched_argument(polar_lift(p)**q)) if not m: if expr.func is periodic_argument and not expr.args[0].is_polar \ and expr.args[1] == oo: return (expr.args[0] > 0) return orig return (m[p] > 0) return cond.replace( lambda expr: expr.is_Relational and expr.rel_op == '==', repl_eq) def _eval_cond(cond): """ Re-evaluate the conditions. """ if isinstance(cond, bool): return cond return _condsimp(cond.doit()) #################################################################### # Now the "backbone" functions to do actual integration. #################################################################### def _my_principal_branch(expr, period, full_pb=False): """ Bring expr nearer to its principal branch by removing superfluous factors. This function does *not* guarantee to yield the principal branch, to avoid introducing opaque principal_branch() objects, unless full_pb=True. """ from sympy import principal_branch res = principal_branch(expr, period) if not full_pb: res = res.replace(principal_branch, lambda x, y: x) return res def _rewrite_saxena_1(fac, po, g, x): """ Rewrite the integral fac*po*g dx, from zero to infinity, as integral fac*G, where G has argument a*x. Note po=x**s. Return fac, G. """ _, s = _get_coeff_exp(po, x) a, b = _get_coeff_exp(g.argument, x) period = g.get_period() a = _my_principal_branch(a, period) # We substitute t = x**b. C = fac/(abs(b)*a**((s + 1)/b - 1)) # Absorb a factor of (at)**((1 + s)/b - 1). def tr(l): return [a + (1 + s)/b - 1 for a in l] return C, meijerg(tr(g.an), tr(g.aother), tr(g.bm), tr(g.bother), a*x) def _check_antecedents_1(g, x, helper=False): """ Return a condition under which the mellin transform of g exists. Any power of x has already been absorbed into the G function, so this is just int_0^\infty g dx. See [L, section 5.6.1]. (Note that s=1.) If ``helper`` is True, only check if the MT exists at infinity, i.e. if int_1^\infty g dx exists. """ # NOTE if you update these conditions, please update the documentation as well from sympy import Eq, Not, ceiling, Ne, re, unbranched_argument as arg delta = g.delta eta, _ = _get_coeff_exp(g.argument, x) m, n, p, q = S([len(g.bm), len(g.an), len(g.ap), len(g.bq)]) xi = m + n - p if p > q: def tr(l): return [1 - x for x in l] return _check_antecedents_1(meijerg(tr(g.bm), tr(g.bother), tr(g.an), tr(g.aother), x/eta), x) tmp = [] for b in g.bm: tmp += [-re(b) < 1] for a in g.an: tmp += [1 < 1 - re(a)] cond_3 = And(*tmp) for b in g.bother: tmp += [-re(b) < 1] for a in g.aother: tmp += [1 < 1 - re(a)] cond_3_star = And(*tmp) cond_4 = (-re(g.nu) + (q + 1 - p)/2 > q - p) def debug(*msg): _debug(*msg) debug('Checking antecedents for 1 function:') debug(' delta=%s, eta=%s, m=%s, n=%s, p=%s, q=%s' % (delta, eta, m, n, p, q)) debug(' ap = %s, %s' % (list(g.an), list(g.aother))) debug(' bq = %s, %s' % (list(g.bm), list(g.bother))) debug(' cond_3=%s, cond_3*=%s, cond_4=%s' % (cond_3, cond_3_star, cond_4)) conds = [] # case 1 case1 = [] tmp1 = [1 <= n, p < q, 1 <= m] tmp2 = [1 <= p, 1 <= m, Eq(q, p + 1), Not(And(Eq(n, 0), Eq(m, p + 1)))] tmp3 = [1 <= p, Eq(q, p)] for k in range(ceiling(delta/2) + 1): tmp3 += [Ne(abs(arg(eta)), (delta - 2*k)*pi)] tmp = [delta > 0, abs(arg(eta)) < delta*pi] extra = [Ne(eta, 0), cond_3] if helper: extra = [] for t in [tmp1, tmp2, tmp3]: case1 += [And(*(t + tmp + extra))] conds += case1 debug(' case 1:', case1) # case 2 extra = [cond_3] if helper: extra = [] case2 = [And(Eq(n, 0), p + 1 <= m, m <= q, abs(arg(eta)) < delta*pi, *extra)] conds += case2 debug(' case 2:', case2) # case 3 extra = [cond_3, cond_4] if helper: extra = [] case3 = [And(p < q, 1 <= m, delta > 0, Eq(abs(arg(eta)), delta*pi), *extra)] case3 += [And(p <= q - 2, Eq(delta, 0), Eq(abs(arg(eta)), 0), *extra)] conds += case3 debug(' case 3:', case3) # TODO altered cases 4-7 # extra case from wofram functions site: # (reproduced verbatim from prudnikov, section 2.24.2) # http://functions.wolfram.com/HypergeometricFunctions/MeijerG/21/02/01/ case_extra = [] case_extra += [Eq(p, q), Eq(delta, 0), Eq(arg(eta), 0), Ne(eta, 0)] if not helper: case_extra += [cond_3] s = [] for a, b in zip(g.ap, g.bq): s += [b - a] case_extra += [re(Add(*s)) < 0] case_extra = And(*case_extra) conds += [case_extra] debug(' extra case:', [case_extra]) case_extra_2 = [And(delta > 0, abs(arg(eta)) < delta*pi)] if not helper: case_extra_2 += [cond_3] case_extra_2 = And(*case_extra_2) conds += [case_extra_2] debug(' second extra case:', [case_extra_2]) # TODO This leaves only one case from the three listed by prudnikov. # Investigate if these indeed cover everything; if so, remove the rest. return Or(*conds) def _int0oo_1(g, x): """ Evaluate int_0^\infty g dx using G functions, assuming the necessary conditions are fulfilled. >>> from sympy.abc import a, b, c, d, x, y >>> from sympy import meijerg >>> from sympy.integrals.meijerint import _int0oo_1 >>> _int0oo_1(meijerg([a], [b], [c], [d], x*y), x) gamma(-a)*gamma(c + 1)/(y*gamma(-d)*gamma(b + 1)) """ # See [L, section 5.6.1]. Note that s=1. from sympy import gamma, combsimp, unpolarify eta, _ = _get_coeff_exp(g.argument, x) res = 1/eta # XXX TODO we should reduce order first for b in g.bm: res *= gamma(b + 1) for a in g.an: res *= gamma(1 - a - 1) for b in g.bother: res /= gamma(1 - b - 1) for a in g.aother: res /= gamma(a + 1) return combsimp(unpolarify(res)) def _rewrite_saxena(fac, po, g1, g2, x, full_pb=False): """ Rewrite the integral fac*po*g1*g2 from 0 to oo in terms of G functions with argument c*x. Return C, f1, f2 such that integral C f1 f2 from 0 to infinity equals integral fac po g1 g2 from 0 to infinity. >>> from sympy.integrals.meijerint import _rewrite_saxena >>> from sympy.abc import s, t, m >>> from sympy import meijerg >>> g1 = meijerg([], [], [0], [], s*t) >>> g2 = meijerg([], [], [m/2], [-m/2], t**2/4) >>> r = _rewrite_saxena(1, t**0, g1, g2, t) >>> r[0] s/(4*sqrt(pi)) >>> r[1] meijerg(((), ()), ((-1/2, 0), ()), s**2*t/4) >>> r[2] meijerg(((), ()), ((m/2,), (-m/2,)), t/4) """ from sympy.core.numbers import ilcm def pb(g): a, b = _get_coeff_exp(g.argument, x) per = g.get_period() return meijerg(g.an, g.aother, g.bm, g.bother, _my_principal_branch(a, per, full_pb)*x**b) _, s = _get_coeff_exp(po, x) _, b1 = _get_coeff_exp(g1.argument, x) _, b2 = _get_coeff_exp(g2.argument, x) if (b1 < 0) is True: b1 = -b1 g1 = _flip_g(g1) if (b2 < 0) is True: b2 = -b2 g2 = _flip_g(g2) if not b1.is_Rational or not b2.is_Rational: return m1, n1 = b1.p, b1.q m2, n2 = b2.p, b2.q tau = ilcm(m1*n2, m2*n1) r1 = tau//(m1*n2) r2 = tau//(m2*n1) C1, g1 = _inflate_g(g1, r1) C2, g2 = _inflate_g(g2, r2) g1 = pb(g1) g2 = pb(g2) fac *= C1*C2 a1, b = _get_coeff_exp(g1.argument, x) a2, _ = _get_coeff_exp(g2.argument, x) # arbitrarily tack on the x**s part to g1 # TODO should we try both? exp = (s + 1)/b - 1 fac = fac/(abs(b) * a1**exp) def tr(l): return [a + exp for a in l] g1 = meijerg(tr(g1.an), tr(g1.aother), tr(g1.bm), tr(g1.bother), a1*x) g2 = meijerg(g2.an, g2.aother, g2.bm, g2.bother, a2*x) return powdenest(fac, polar=True), g1, g2 def _check_antecedents(g1, g2, x): """ Return a condition under which the integral theorem applies. """ from sympy import (re, Eq, Not, Ne, cos, I, exp, ceiling, sin, sign, unpolarify) from sympy import arg as arg_, unbranched_argument as arg # Yes, this is madness. # XXX TODO this is a testing *nightmare* # NOTE if you update these conditions, please update the documentation as well # The following conditions are found in # [P], Section 2.24.1 # # They are also reproduced (verbatim!) at # http://functions.wolfram.com/HypergeometricFunctions/MeijerG/21/02/03/ # # Note: k=l=r=alpha=1 sigma, _ = _get_coeff_exp(g1.argument, x) omega, _ = _get_coeff_exp(g2.argument, x) s, t, u, v = S([len(g1.bm), len(g1.an), len(g1.ap), len(g1.bq)]) m, n, p, q = S([len(g2.bm), len(g2.an), len(g2.ap), len(g2.bq)]) bstar = s + t - (u + v)/2 cstar = m + n - (p + q)/2 rho = g1.nu + (u - v)/2 + 1 mu = g2.nu + (p - q)/2 + 1 phi = q - p - (v - u) eta = 1 - (v - u) - mu - rho psi = (pi*(q - m - n) + abs(arg(omega)))/(q - p) theta = (pi*(v - s - t) + abs(arg(sigma)))/(v - u) lambda_c = (q - p)*abs(omega)**(1/(q - p))*cos(psi) \ + (v - u)*abs(sigma)**(1/(v - u))*cos(theta) def lambda_s0(c1, c2): return c1*(q - p)*abs(omega)**(1/(q - p))*sin(psi) \ + c2*(v - u)*abs(sigma)**(1/(v - u))*sin(theta) lambda_s = Piecewise( ((lambda_s0(+1, +1)*lambda_s0(-1, -1)), And(Eq(arg(sigma), 0), Eq(arg(omega), 0))), (lambda_s0(sign(arg(omega)), +1)*lambda_s0(sign(arg(omega)), -1), And(Eq(arg(sigma), 0), Ne(arg(omega), 0))), (lambda_s0(+1, sign(arg(sigma)))*lambda_s0(-1, sign(arg(sigma))), And(Ne(arg(sigma), 0), Eq(arg(omega), 0))), (lambda_s0(sign(arg(omega)), sign(arg(sigma))), True)) _debug('Checking antecedents:') _debug(' sigma=%s, s=%s, t=%s, u=%s, v=%s, b*=%s, rho=%s' % (sigma, s, t, u, v, bstar, rho)) _debug(' omega=%s, m=%s, n=%s, p=%s, q=%s, c*=%s, mu=%s,' % (omega, m, n, p, q, cstar, mu)) _debug(' phi=%s, eta=%s, psi=%s, theta=%s' % (phi, eta, psi, theta)) c1 = True for g in [g1, g2]: for a in g1.an: for b in g1.bm: diff = a - b if (diff > 0) is True and diff.is_integer: c1 = False tmp = [] for b in g1.bm: for d in g2.bm: tmp += [re(1 + b + d) > 0] c2 = And(*tmp) tmp = [] for a in g1.an: for c in g2.an: tmp += [re(1 + a + c) < 1 + 1] c3 = And(*tmp) tmp = [] for c in g1.an: tmp += [(p - q)*re(1 + c - 1) - re(mu) > -S(3)/2] c4 = And(*tmp) tmp = [] for d in g1.bm: tmp += [(p - q)*re(1 + d) - re(mu) > -S(3)/2] c5 = And(*tmp) tmp = [] for c in g2.an: tmp += [(u - v)*re(1 + c - 1) - re(rho) > -S(3)/2] c6 = And(*tmp) tmp = [] for d in g2.bm: tmp += [(u - v)*re(1 + d) - re(rho) > -S(3)/2] c7 = And(*tmp) c8 = (abs(phi) + 2*re((rho - 1)*(q - p) + (v - u)*(q - p) + (mu - 1)*(v - u)) > 0) c9 = (abs(phi) - 2*re((rho - 1)*(q - p) + (v - u)*(q - p) + (mu - 1)*(v - u)) > 0) c10 = (abs(arg(sigma)) < bstar*pi) c11 = Eq(abs(arg(sigma)), bstar*pi) c12 = (abs(arg(omega)) < cstar*pi) c13 = Eq(abs(arg(omega)), cstar*pi) # The following condition is *not* implemented as stated on the wolfram # function site. In the book of prudnikov there is an additional part # (the And involving re()). However, I only have this book in russian, and # I don't read any russian. The following condition is what other people # have told me it means. # Worryingly, it is different from the condition implemented in REDUCE. # The REDUCE implementation: # https://reduce-algebra.svn.sourceforge.net/svnroot/reduce-algebra/trunk/packages/defint/definta.red # (search for tst14) # The Wolfram alpha version: # http://functions.wolfram.com/HypergeometricFunctions/MeijerG/21/02/03/03/0014/ z0 = exp(-(bstar + cstar)*pi*I) zos = unpolarify(z0*omega/sigma) zso = unpolarify(z0*sigma/omega) if zos == 1/zso: c14 = And(Eq(phi, 0), bstar + cstar <= 1, Or(Ne(zos, 1), re(mu + rho + v - u) < 1, re(mu + rho + q - p) < 1)) else: c14 = And(Eq(phi, 0), bstar - 1 + cstar <= 0, Or(And(Ne(zos, 1), abs(arg_(1 - zos)) < pi), And(re(mu + rho + v - u) < 1, Eq(zos, 1)))) c14_alt = And(Eq(phi, 0), cstar - 1 + bstar <= 0, Or(And(Ne(zso, 1), abs(arg_(1 - zso)) < pi), And(re(mu + rho + q - p) < 1, Eq(zso, 1)))) # Since r=k=l=1, in our case there is c14_alt which is the same as calling # us with (g1, g2) = (g2, g1). The conditions below enumerate all cases # (i.e. we don't have to try arguments reversed by hand), and indeed try # all symmetric cases. (i.e. whenever there is a condition involving c14, # there is also a dual condition which is exactly what we would get when g1, # g2 were interchanged, *but c14 was unaltered*). # Hence the following seems correct: c14 = Or(c14, c14_alt) tmp = [lambda_c > 0, And(Eq(lambda_c, 0), Ne(lambda_s, 0), re(eta) > -1), And(Eq(lambda_c, 0), Eq(lambda_s, 0), re(eta) > 0)] c15 = Or(*tmp) if _eval_cond(lambda_c > 0) != False: c15 = (lambda_c > 0) for cond, i in [(c1, 1), (c2, 2), (c3, 3), (c4, 4), (c5, 5), (c6, 6), (c7, 7), (c8, 8), (c9, 9), (c10, 10), (c11, 11), (c12, 12), (c13, 13), (c14, 14), (c15, 15)]: _debug(' c%s:' % i, cond) # We will return Or(*conds) conds = [] def pr(count): _debug(' case %s:' % count, conds[-1]) conds += [And(m*n*s*t != 0, bstar.is_positive is True, cstar.is_positive is True, c1, c2, c3, c10, c12)] # 1 pr(1) conds += [And(Eq(u, v), Eq(bstar, 0), cstar.is_positive is True, sigma.is_positive is True, re(rho) < 1, c1, c2, c3, c12)] # 2 pr(2) conds += [And(Eq(p, q), Eq(cstar, 0), bstar.is_positive is True, omega.is_positive is True, re(mu) < 1, c1, c2, c3, c10)] # 3 pr(3) conds += [And(Eq(p, q), Eq(u, v), Eq(bstar, 0), Eq(cstar, 0), sigma.is_positive is True, omega.is_positive is True, re(mu) < 1, re(rho) < 1, Ne(sigma, omega), c1, c2, c3)] # 4 pr(4) conds += [And(Eq(p, q), Eq(u, v), Eq(bstar, 0), Eq(cstar, 0), sigma.is_positive is True, omega.is_positive is True, re(mu + rho) < 1, Ne(omega, sigma), c1, c2, c3)] # 5 pr(5) conds += [And(p > q, s.is_positive is True, bstar.is_positive is True, cstar >= 0, c1, c2, c3, c5, c10, c13)] # 6 pr(6) conds += [And(p < q, t.is_positive is True, bstar.is_positive is True, cstar >= 0, c1, c2, c3, c4, c10, c13)] # 7 pr(7) conds += [And(u > v, m.is_positive is True, cstar.is_positive is True, bstar >= 0, c1, c2, c3, c7, c11, c12)] # 8 pr(8) conds += [And(u < v, n.is_positive is True, cstar.is_positive is True, bstar >= 0, c1, c2, c3, c6, c11, c12)] # 9 pr(9) conds += [And(p > q, Eq(u, v), Eq(bstar, 0), cstar >= 0, sigma.is_positive is True, re(rho) < 1, c1, c2, c3, c5, c13)] # 10 pr(10) conds += [And(p < q, Eq(u, v), Eq(bstar, 0), cstar >= 0, sigma.is_positive is True, re(rho) < 1, c1, c2, c3, c4, c13)] # 11 pr(11) conds += [And(Eq(p, q), u > v, bstar >= 0, Eq(cstar, 0), omega.is_positive is True, re(mu) < 1, c1, c2, c3, c7, c11)] # 12 pr(12) conds += [And(Eq(p, q), u < v, bstar >= 0, Eq(cstar, 0), omega.is_positive is True, re(mu) < 1, c1, c2, c3, c6, c11)] # 13 pr(13) conds += [And(p < q, u > v, bstar >= 0, cstar >= 0, c1, c2, c3, c4, c7, c11, c13)] # 14 pr(14) conds += [And(p > q, u < v, bstar >= 0, cstar >= 0, c1, c2, c3, c5, c6, c11, c13)] # 15 pr(15) conds += [And(p > q, u > v, bstar >= 0, cstar >= 0, c1, c2, c3, c5, c7, c8, c11, c13, c14)] # 16 pr(16) conds += [And(p < q, u < v, bstar >= 0, cstar >= 0, c1, c2, c3, c4, c6, c9, c11, c13, c14)] # 17 pr(17) conds += [And(Eq(t, 0), s.is_positive is True, bstar.is_positive is True, phi.is_positive is True, c1, c2, c10)] # 18 pr(18) conds += [And(Eq(s, 0), t.is_positive is True, bstar.is_positive is True, phi.is_negative is True, c1, c3, c10)] # 19 pr(19) conds += [And(Eq(n, 0), m.is_positive is True, cstar.is_positive is True, phi.is_negative is True, c1, c2, c12)] # 20 pr(20) conds += [And(Eq(m, 0), n.is_positive is True, cstar.is_positive is True, phi.is_positive is True, c1, c3, c12)] # 21 pr(21) conds += [And(Eq(s*t, 0), bstar.is_positive is True, cstar.is_positive is True, c1, c2, c3, c10, c12)] # 22 pr(22) conds += [And(Eq(m*n, 0), bstar.is_positive is True, cstar.is_positive is True, c1, c2, c3, c10, c12)] # 23 pr(23) # The following case is from [Luke1969]. As far as I can tell, it is *not* # covered by prudnikov's. # Let G1 and G2 be the two G-functions. Suppose the integral exists from # 0 to a > 0 (this is easy the easy part), that G1 is exponential decay at # infinity, and that the mellin transform of G2 exists. # Then the integral exists. mt1_exists = _check_antecedents_1(g1, x, helper=True) mt2_exists = _check_antecedents_1(g2, x, helper=True) conds += [And(mt2_exists, Eq(t, 0), u < s, bstar.is_positive is True, c10, c1, c2, c3)] pr('E1') conds += [And(mt2_exists, Eq(s, 0), v < t, bstar.is_positive is True, c10, c1, c2, c3)] pr('E2') conds += [And(mt1_exists, Eq(n, 0), p < m, cstar.is_positive is True, c12, c1, c2, c3)] pr('E3') conds += [And(mt1_exists, Eq(m, 0), q < n, cstar.is_positive is True, c12, c1, c2, c3)] pr('E4') # Let's short-circuit if this worked ... # the rest is corner-cases and terrible to read. r = Or(*conds) if _eval_cond(r) != False: return r conds += [And(m + n > p, Eq(t, 0), Eq(phi, 0), s.is_positive is True, bstar.is_positive is True, cstar.is_negative is True, abs(arg(omega)) < (m + n - p + 1)*pi, c1, c2, c10, c14, c15)] # 24 pr(24) conds += [And(m + n > q, Eq(s, 0), Eq(phi, 0), t.is_positive is True, bstar.is_positive is True, cstar.is_negative is True, abs(arg(omega)) < (m + n - q + 1)*pi, c1, c3, c10, c14, c15)] # 25 pr(25) conds += [And(Eq(p, q - 1), Eq(t, 0), Eq(phi, 0), s.is_positive is True, bstar.is_positive is True, cstar >= 0, cstar*pi < abs(arg(omega)), c1, c2, c10, c14, c15)] # 26 pr(26) conds += [And(Eq(p, q + 1), Eq(s, 0), Eq(phi, 0), t.is_positive is True, bstar.is_positive is True, cstar >= 0, cstar*pi < abs(arg(omega)), c1, c3, c10, c14, c15)] # 27 pr(27) conds += [And(p < q - 1, Eq(t, 0), Eq(phi, 0), s.is_positive is True, bstar.is_positive is True, cstar >= 0, cstar*pi < abs(arg(omega)), abs(arg(omega)) < (m + n - p + 1)*pi, c1, c2, c10, c14, c15)] # 28 pr(28) conds += [And( p > q + 1, Eq(s, 0), Eq(phi, 0), t.is_positive is True, bstar.is_positive is True, cstar >= 0, cstar*pi < abs(arg(omega)), abs(arg(omega)) < (m + n - q + 1)*pi, c1, c3, c10, c14, c15)] # 29 pr(29) conds += [And(Eq(n, 0), Eq(phi, 0), s + t > 0, m.is_positive is True, cstar.is_positive is True, bstar.is_negative is True, abs(arg(sigma)) < (s + t - u + 1)*pi, c1, c2, c12, c14, c15)] # 30 pr(30) conds += [And(Eq(m, 0), Eq(phi, 0), s + t > v, n.is_positive is True, cstar.is_positive is True, bstar.is_negative is True, abs(arg(sigma)) < (s + t - v + 1)*pi, c1, c3, c12, c14, c15)] # 31 pr(31) conds += [And(Eq(n, 0), Eq(phi, 0), Eq(u, v - 1), m.is_positive is True, cstar.is_positive is True, bstar >= 0, bstar*pi < abs(arg(sigma)), abs(arg(sigma)) < (bstar + 1)*pi, c1, c2, c12, c14, c15)] # 32 pr(32) conds += [And(Eq(m, 0), Eq(phi, 0), Eq(u, v + 1), n.is_positive is True, cstar.is_positive is True, bstar >= 0, bstar*pi < abs(arg(sigma)), abs(arg(sigma)) < (bstar + 1)*pi, c1, c3, c12, c14, c15)] # 33 pr(33) conds += [And( Eq(n, 0), Eq(phi, 0), u < v - 1, m.is_positive is True, cstar.is_positive is True, bstar >= 0, bstar*pi < abs(arg(sigma)), abs(arg(sigma)) < (s + t - u + 1)*pi, c1, c2, c12, c14, c15)] # 34 pr(34) conds += [And( Eq(m, 0), Eq(phi, 0), u > v + 1, n.is_positive is True, cstar.is_positive is True, bstar >= 0, bstar*pi < abs(arg(sigma)), abs(arg(sigma)) < (s + t - v + 1)*pi, c1, c3, c12, c14, c15)] # 35 pr(35) return Or(*conds) # NOTE An alternative, but as far as I can tell weaker, set of conditions # can be found in [L, section 5.6.2]. def _int0oo(g1, g2, x): """ Express integral from zero to infinity g1*g2 using a G function, assuming the necessary conditions are fulfilled. >>> from sympy.integrals.meijerint import _int0oo >>> from sympy.abc import s, t, m >>> from sympy import meijerg, S >>> g1 = meijerg([], [], [-S(1)/2, 0], [], s**2*t/4) >>> g2 = meijerg([], [], [m/2], [-m/2], t/4) >>> _int0oo(g1, g2, t) 4*meijerg(((1/2, 0), ()), ((m/2,), (-m/2,)), s**(-2))/s**2 """ # See: [L, section 5.6.2, equation (1)] eta, _ = _get_coeff_exp(g1.argument, x) omega, _ = _get_coeff_exp(g2.argument, x) def neg(l): return [-x for x in l] a1 = neg(g1.bm) + list(g2.an) a2 = list(g2.aother) + neg(g1.bother) b1 = neg(g1.an) + list(g2.bm) b2 = list(g2.bother) + neg(g1.aother) return meijerg(a1, a2, b1, b2, omega/eta)/eta def _rewrite_inversion(fac, po, g, x): """ Absorb ``po`` == x**s into g. """ _, s = _get_coeff_exp(po, x) a, b = _get_coeff_exp(g.argument, x) def tr(l): return [t + s/b for t in l] return (powdenest(fac/a**(s/b), polar=True), meijerg(tr(g.an), tr(g.aother), tr(g.bm), tr(g.bother), g.argument)) def _check_antecedents_inversion(g, x): """ Check antecedents for the laplace inversion integral. """ from sympy import re, im, Or, And, Eq, exp, I, Add, nan, Ne _debug('Checking antecedents for inversion:') z = g.argument _, e = _get_coeff_exp(z, x) if e < 0: _debug(' Flipping G.') # We want to assume that argument gets large as |x| -> oo return _check_antecedents_inversion(_flip_g(g), x) def statement_half(a, b, c, z, plus): coeff, exponent = _get_coeff_exp(z, x) a *= exponent b *= coeff**c c *= exponent conds = [] wp = b*exp(I*re(c)*pi/2) wm = b*exp(-I*re(c)*pi/2) if plus: w = wp else: w = wm conds += [And(Or(Eq(b, 0), re(c) <= 0), re(a) <= -1)] conds += [And(Ne(b, 0), Eq(im(c), 0), re(c) > 0, re(w) < 0)] conds += [And(Ne(b, 0), Eq(im(c), 0), re(c) > 0, re(w) <= 0, re(a) <= -1)] return Or(*conds) def statement(a, b, c, z): """ Provide a convergence statement for z**a * exp(b*z**c), c/f sphinx docs. """ return And(statement_half(a, b, c, z, True), statement_half(a, b, c, z, False)) # Notations from [L], section 5.7-10 m, n, p, q = S([len(g.bm), len(g.an), len(g.ap), len(g.bq)]) tau = m + n - p nu = q - m - n rho = (tau - nu)/2 sigma = q - p if sigma == 1: epsilon = S(1)/2 elif sigma > 1: epsilon = 1 else: epsilon = nan theta = ((1 - sigma)/2 + Add(*g.bq) - Add(*g.ap))/sigma delta = g.delta _debug(' m=%s, n=%s, p=%s, q=%s, tau=%s, nu=%s, rho=%s, sigma=%s' % ( m, n, p, q, tau, nu, rho, sigma)) _debug(' epsilon=%s, theta=%s, delta=%s' % (epsilon, theta, delta)) # First check if the computation is valid. if not (g.delta >= e/2 or (p >= 1 and p >= q)): _debug(' Computation not valid for these parameters.') return False # Now check if the inversion integral exists. # Test "condition A" for a in g.an: for b in g.bm: if (a - b).is_integer and a > b: _debug(' Not a valid G function.') return False # There are two cases. If p >= q, we can directly use a slater expansion # like [L], 5.2 (11). Note in particular that the asymptotics of such an # expansion even hold when some of the parameters differ by integers, i.e. # the formula itself would not be valid! (b/c G functions are cts. in their # parameters) # When p < q, we need to use the theorems of [L], 5.10. if p >= q: _debug(' Using asymptotic slater expansion.') return And(*[statement(a - 1, 0, 0, z) for a in g.an]) def E(z): return And(*[statement(a - 1, 0, z) for a in g.an]) def H(z): return statement(theta, -sigma, 1/sigma, z) def Hp(z): return statement_half(theta, -sigma, 1/sigma, z, True) def Hm(z): return statement_half(theta, -sigma, 1/sigma, z, False) # [L], section 5.10 conds = [] # Theorem 1 conds += [And(1 <= n, p < q, 1 <= m, rho*pi - delta >= pi/2, delta > 0, E(z*exp(I*pi*(nu + 1))))] # Theorem 2, statements (2) and (3) conds += [And(p + 1 <= m, m + 1 <= q, delta > 0, delta < pi/2, n == 0, (m - p + 1)*pi - delta >= pi/2, Hp(z*exp(I*pi*(q - m))), Hm(z*exp(-I*pi*(q - m))))] # Theorem 2, statement (5) conds += [And(p < q, m == q, n == 0, delta > 0, (sigma + epsilon)*pi - delta >= pi/2, H(z))] # Theorem 3, statements (6) and (7) conds += [And(Or(And(p <= q - 2, 1 <= tau, tau <= sigma/2), And(p + 1 <= m + n, m + n <= (p + q)/2)), delta > 0, delta < pi/2, (tau + 1)*pi - delta >= pi/2, Hp(z*exp(I*pi*nu)), Hm(z*exp(-I*pi*nu)))] # Theorem 4, statements (10) and (11) conds += [And(p < q, 1 <= m, rho > 0, delta > 0, delta + rho*pi < pi/2, (tau + epsilon)*pi - delta >= pi/2, Hp(z*exp(I*pi*nu)), Hm(z*exp(-I*pi*nu)))] # Trivial case conds += [m == 0] # TODO # Theorem 5 is quite general # Theorem 6 contains special cases for q=p+1 return Or(*conds) def _int_inversion(g, x, t): """ Compute the laplace inversion integral, assuming the formula applies. """ b, a = _get_coeff_exp(g.argument, x) C, g = _inflate_fox_h(meijerg(g.an, g.aother, g.bm, g.bother, b/t**a), -a) return C/t*g #################################################################### # Finally, the real meat. #################################################################### _lookup_table = None @cacheit @timeit def _rewrite_single(f, x, recursive=True): """ Try to rewrite f as a sum of single G functions of the form C*x**s*G(a*x**b), where b is a rational number and C is independent of x. We guarantee that result.argument.as_coeff_mul(x) returns (a, (x**b,)) or (a, ()). Returns a list of tuples (C, s, G) and a condition cond. Returns None on failure. """ from sympy import polarify, unpolarify, oo, zoo, Tuple global _lookup_table if not _lookup_table: _lookup_table = {} _create_lookup_table(_lookup_table) if isinstance(f, meijerg): from sympy import factor coeff, m = factor(f.argument, x).as_coeff_mul(x) if len(m) > 1: return None m = m[0] if m.is_Pow: if m.base != x or not m.exp.is_Rational: return None elif m != x: return None return [(1, 0, meijerg(f.an, f.aother, f.bm, f.bother, coeff*m))], True f_ = f f = f.subs(x, z) t = _mytype(f, z) if t in _lookup_table: l = _lookup_table[t] for formula, terms, cond, hint in l: subs = f.match(formula, old=True) if subs: subs_ = {} for fro, to in subs.items(): subs_[fro] = unpolarify(polarify(to, lift=True), exponents_only=True) subs = subs_ if not isinstance(hint, bool): hint = hint.subs(subs) if hint == False: continue if not isinstance(cond, (bool, BooleanAtom)): cond = unpolarify(cond.subs(subs)) if _eval_cond(cond) == False: continue if not isinstance(terms, list): terms = terms(subs) res = [] for fac, g in terms: r1 = _get_coeff_exp(unpolarify(fac.subs(subs).subs(z, x), exponents_only=True), x) g = g.subs(subs).subs(z, x) # NOTE these substitutions can in principle introduce oo, # zoo and other absurdities. It shouldn't matter, # but better be safe. if Tuple(*(r1 + (g,))).has(oo, zoo, -oo): continue g = meijerg(g.an, g.aother, g.bm, g.bother, unpolarify(g.argument, exponents_only=True)) res.append(r1 + (g,)) if res: return res, cond # try recursive mellin transform if not recursive: return None _debug('Trying recursive mellin transform method.') from sympy.integrals.transforms import (mellin_transform, inverse_mellin_transform, IntegralTransformError, MellinTransformStripError) from sympy import oo, nan, zoo, simplify, cancel def my_imt(F, s, x, strip): """ Calling simplify() all the time is slow and not helpful, since most of the time it only factors things in a way that has to be un-done anyway. But sometimes it can remove apparent poles. """ # XXX should this be in inverse_mellin_transform? try: return inverse_mellin_transform(F, s, x, strip, as_meijerg=True, needeval=True) except MellinTransformStripError: return inverse_mellin_transform( simplify(cancel(expand(F))), s, x, strip, as_meijerg=True, needeval=True) f = f_ s = _dummy('s', 'rewrite-single', f) # to avoid infinite recursion, we have to force the two g functions case def my_integrator(f, x): from sympy import Integral, hyperexpand r = _meijerint_definite_4(f, x, only_double=True) if r is not None: res, cond = r res = _my_unpolarify(hyperexpand(res, rewrite='nonrepsmall')) return Piecewise((res, cond), (Integral(f, (x, 0, oo)), True)) return Integral(f, (x, 0, oo)) try: F, strip, _ = mellin_transform(f, x, s, integrator=my_integrator, simplify=False, needeval=True) g = my_imt(F, s, x, strip) except IntegralTransformError: g = None if g is None: # We try to find an expression by analytic continuation. # (also if the dummy is already in the expression, there is no point in # putting in another one) a = _dummy_('a', 'rewrite-single') if a not in f.free_symbols and _is_analytic(f, x): try: F, strip, _ = mellin_transform(f.subs(x, a*x), x, s, integrator=my_integrator, needeval=True, simplify=False) g = my_imt(F, s, x, strip).subs(a, 1) except IntegralTransformError: g = None if g is None or g.has(oo, nan, zoo): _debug('Recursive mellin transform failed.') return None args = Add.make_args(g) res = [] for f in args: c, m = f.as_coeff_mul(x) if len(m) > 1: raise NotImplementedError('Unexpected form...') g = m[0] a, b = _get_coeff_exp(g.argument, x) res += [(c, 0, meijerg(g.an, g.aother, g.bm, g.bother, unpolarify(polarify( a, lift=True), exponents_only=True) *x**b))] _debug('Recursive mellin transform worked:', g) return res, True def _rewrite1(f, x, recursive=True): """ Try to rewrite f using a (sum of) single G functions with argument a*x**b. Return fac, po, g such that f = fac*po*g, fac is independent of x and po = x**s. Here g is a result from _rewrite_single. Return None on failure. """ fac, po, g = _split_mul(f, x) g = _rewrite_single(g, x, recursive) if g: return fac, po, g[0], g[1] def _rewrite2(f, x): """ Try to rewrite f as a product of two G functions of arguments a*x**b. Return fac, po, g1, g2 such that f = fac*po*g1*g2, where fac is independent of x and po is x**s. Here g1 and g2 are results of _rewrite_single. Returns None on failure. """ fac, po, g = _split_mul(f, x) if any(_rewrite_single(expr, x, False) is None for expr in _mul_args(g)): return None l = _mul_as_two_parts(g) if not l: return None l = list(ordered(l, [ lambda p: max(len(_exponents(p[0], x)), len(_exponents(p[1], x))), lambda p: max(len(_functions(p[0], x)), len(_functions(p[1], x))), lambda p: max(len(_find_splitting_points(p[0], x)), len(_find_splitting_points(p[1], x)))])) for recursive in [False, True]: for fac1, fac2 in l: g1 = _rewrite_single(fac1, x, recursive) g2 = _rewrite_single(fac2, x, recursive) if g1 and g2: cond = And(g1[1], g2[1]) if cond != False: return fac, po, g1[0], g2[0], cond def meijerint_indefinite(f, x): """ Compute an indefinite integral of ``f`` by rewriting it as a G function. >>> from sympy.integrals.meijerint import meijerint_indefinite >>> from sympy import sin >>> from sympy.abc import x >>> meijerint_indefinite(sin(x), x) -cos(x) """ from sympy import hyper, meijerg, count_ops results = [] for a in sorted(_find_splitting_points(f, x) | set([S(0)]), key=default_sort_key): res = _meijerint_indefinite_1(f.subs(x, x + a), x) if res is None: continue results.append(res.subs(x, x - a)) if not res.has(hyper, meijerg): return results[-1] if results: return next(ordered(results)) def _meijerint_indefinite_1(f, x): """ Helper that does not attempt any substitution. """ from sympy import Integral, piecewise_fold _debug('Trying to compute the indefinite integral of', f, 'wrt', x) gs = _rewrite1(f, x) if gs is None: # Note: the code that calls us will do expand() and try again return None fac, po, gl, cond = gs _debug(' could rewrite:', gs) res = S(0) for C, s, g in gl: a, b = _get_coeff_exp(g.argument, x) _, c = _get_coeff_exp(po, x) c += s # we do a substitution t=a*x**b, get integrand fac*t**rho*g fac_ = fac * C / (b*a**((1 + c)/b)) rho = (c + 1)/b - 1 # we now use t**rho*G(params, t) = G(params + rho, t) # [L, page 150, equation (4)] # and integral G(params, t) dt = G(1, params+1, 0, t) # (or a similar expression with 1 and 0 exchanged ... pick the one # which yields a well-defined function) # [R, section 5] # (Note that this dummy will immediately go away again, so we # can safely pass S(1) for ``expr``.) t = _dummy('t', 'meijerint-indefinite', S(1)) def tr(p): return [a + rho + 1 for a in p] if any(b.is_integer and (b <= 0) is True for b in tr(g.bm)): r = -meijerg( tr(g.an), tr(g.aother) + [1], tr(g.bm) + [0], tr(g.bother), t) else: r = meijerg( tr(g.an) + [1], tr(g.aother), tr(g.bm), tr(g.bother) + [0], t) r = hyperexpand(r.subs(t, a*x**b)) # now substitute back # Note: we really do want the powers of x to combine. res += powdenest(fac_*r, polar=True) def _clean(res): """This multiplies out superfluous powers of x we created, and chops off constants: >> _clean(x*(exp(x)/x - 1/x) + 3) exp(x) cancel is used before mul_expand since it is possible for an expression to have an additive constant that doesn't become isolated with simple expansion. Such a situation was identified in issue 3270: >>> from sympy import sqrt, cancel >>> from sympy.abc import x >>> a = sqrt(2*x + 1) >>> bad = (3*x*a**5 + 2*x - a**5 + 1)/a**2 >>> bad.expand().as_independent(x)[0] 0 >>> cancel(bad).expand().as_independent(x)[0] 1 """ from sympy import cancel res = expand_mul(cancel(res), deep=False) return Add._from_args(res.as_coeff_add(x)[1]) res = piecewise_fold(res) if res.is_Piecewise: nargs = [] for expr, cond in res.args: expr = _my_unpolarify(_clean(expr)) nargs += [(expr, cond)] res = Piecewise(*nargs) else: res = _my_unpolarify(_clean(res)) return Piecewise((res, _my_unpolarify(cond)), (Integral(f, x), True)) @timeit def meijerint_definite(f, x, a, b): """ Integrate ``f`` over the interval [``a``, ``b``], by rewriting it as a product of two G functions, or as a single G function. Return res, cond, where cond are convergence conditions. >>> from sympy.integrals.meijerint import meijerint_definite >>> from sympy import exp, oo >>> from sympy.abc import x >>> meijerint_definite(exp(-x**2), x, -oo, oo) (sqrt(pi), True) This function is implemented as a succession of functions meijerint_definite, _meijerint_definite_2, _meijerint_definite_3, _meijerint_definite_4. Each function in the list calls the next one (presumably) several times. This means that calling meijerint_definite can be very costly. """ # This consists of three steps: # 1) Change the integration limits to 0, oo # 2) Rewrite in terms of G functions # 3) Evaluate the integral # # There are usually several ways of doing this, and we want to try all. # This function does (1), calls _meijerint_definite_2 for step (2). from sympy import Integral, arg, exp, I, And, DiracDelta, count_ops _debug('Integrating', f, 'wrt %s from %s to %s.' % (x, a, b)) if f.has(DiracDelta): _debug('Integrand has DiracDelta terms - giving up.') return None f_, x_, a_, b_ = f, x, a, b # Let's use a dummy in case any of the boundaries has x. d = Dummy('x') f = f.subs(x, d) x = d if a == -oo and b != oo: return meijerint_definite(f.subs(x, -x), x, -b, -a) if a == -oo: # Integrating -oo to oo. We need to find a place to split the integral. _debug(' Integrating -oo to +oo.') innermost = _find_splitting_points(f, x) _debug(' Sensible splitting points:', innermost) for c in sorted(innermost, key=default_sort_key, reverse=True) + [S(0)]: _debug(' Trying to split at', c) if not c.is_real: _debug(' Non-real splitting point.') continue res1 = _meijerint_definite_2(f.subs(x, x + c), x) if res1 is None: _debug(' But could not compute first integral.') continue res2 = _meijerint_definite_2(f.subs(x, c - x), x) if res2 is None: _debug(' But could not compute second integral.') continue res1, cond1 = res1 res2, cond2 = res2 cond = _condsimp(And(cond1, cond2)) if cond == False: _debug(' But combined condition is always false.') continue res = res1 + res2 return res, cond return if a == oo: return -meijerint_definite(f, x, b, oo) if (a, b) == (0, oo): # This is a common case - try it directly first. res = _meijerint_definite_2(f, x) if res is not None and not res[0].has(meijerg): return res results = [] if b == oo: for split in _find_splitting_points(f, x): if (a - split >= 0) is True: _debug('Trying x --> x + %s' % split) res = _meijerint_definite_2(f.subs(x, x + split) *Heaviside(x + split - a), x) if res is not None: if res[0].has(meijerg): results.append(res) else: return res f = f.subs(x, x + a) b = b - a a = 0 if b != oo: phi = exp(I*arg(b)) b = abs(b) f = f.subs(x, phi*x) f *= Heaviside(b - x)*phi b = oo _debug('Changed limits to', a, b) _debug('Changed function to', f) res = _meijerint_definite_2(f, x) if res is not None: if res[0].has(meijerg): results.append(res) else: return res if results: return sorted(results, key=lambda x: count_ops(x[0]))[0] def _guess_expansion(f, x): """ Try to guess sensible rewritings for integrand f(x). """ from sympy import expand_trig from sympy.functions.elementary.trigonometric import TrigonometricFunction from sympy.functions.elementary.hyperbolic import HyperbolicFunction res = [(f, 'originial integrand')] expanded = expand_mul(res[-1][0]) if expanded != res[-1][0]: res += [(expanded, 'expand_mul')] expanded = expand(res[-1][0]) if expanded != res[-1][0]: res += [(expanded, 'expand')] if res[-1][0].has(TrigonometricFunction, HyperbolicFunction): expanded = expand_mul(expand_trig(res[-1][0])) if expanded != res[-1][0]: res += [(expanded, 'expand_trig, expand_mul')] return res def _meijerint_definite_2(f, x): """ Try to integrate f dx from zero to infinty. The body of this function computes various 'simplifications' f1, f2, ... of f (e.g. by calling expand_mul(), trigexpand() - see _guess_expansion) and calls _meijerint_definite_3 with each of these in succession. If _meijerint_definite_3 succeedes with any of the simplified functions, returns this result. """ # This function does preparation for (2), calls # _meijerint_definite_3 for (2) and (3) combined. # use a positive dummy - we integrate from 0 to oo dummy = _dummy('x', 'meijerint-definite2', f, positive=True) f = f.subs(x, dummy) x = dummy if f == 0: return S(0), True for g, explanation in _guess_expansion(f, x): _debug('Trying', explanation) res = _meijerint_definite_3(g, x) if res is not None and res[1] != False: return res def _meijerint_definite_3(f, x): """ Try to integrate f dx from zero to infinity. This function calls _meijerint_definite_4 to try to compute the integral. If this fails, it tries using linearity. """ res = _meijerint_definite_4(f, x) if res is not None and res[1] != False: return res if f.is_Add: _debug('Expanding and evaluating all terms.') ress = [_meijerint_definite_4(g, x) for g in f.args] if all(r is not None for r in ress): conds = [] res = S(0) for r, c in ress: res += r conds += [c] c = And(*conds) if c != False: return res, c def _my_unpolarify(f): from sympy import unpolarify return _eval_cond(unpolarify(f)) @timeit def _meijerint_definite_4(f, x, only_double=False): """ Try to integrate f dx from zero to infinity. This function tries to apply the integration theorems found in literature, i.e. it tries to rewrite f as either one or a product of two G-functions. The parameter ``only_double`` is used internally in the recursive algorithm to disable trying to rewrite f as a single G-function. """ # This function does (2) and (3) _debug('Integrating', f) # Try single G function. if not only_double: gs = _rewrite1(f, x, recursive=False) if gs is not None: fac, po, g, cond = gs _debug('Could rewrite as single G function:', fac, po, g) res = S(0) for C, s, f in g: if C == 0: continue C, f = _rewrite_saxena_1(fac*C, po*x**s, f, x) res += C*_int0oo_1(f, x) cond = And(cond, _check_antecedents_1(f, x)) cond = _my_unpolarify(cond) _debug('Result before branch substitutions is:', res) if cond == False: _debug('But cond is always False.') else: return _my_unpolarify(hyperexpand(res)), cond # Try two G functions. gs = _rewrite2(f, x) if gs is not None: for full_pb in [False, True]: fac, po, g1, g2, cond = gs _debug('Could rewrite as two G functions:', fac, po, g1, g2) res = S(0) for C1, s1, f1 in g1: for C2, s2, f2 in g2: r = _rewrite_saxena(fac*C1*C2, po*x**(s1 + s2), f1, f2, x, full_pb) if r is None: _debug('Non-rational exponents.') return C, f1_, f2_ = r _debug('Saxena subst for yielded:', C, f1_, f2_) cond = And(cond, _check_antecedents(f1_, f2_, x)) res += C*_int0oo(f1_, f2_, x) _debug('Result before branch substitutions is:', res) cond = _my_unpolarify(cond) if cond == False: _debug('But cond is always False (full_pb=%s).' % full_pb) else: if only_double: return res, cond return _my_unpolarify(hyperexpand(res)), cond def meijerint_inversion(f, x, t): """ Compute the inverse laplace transform :math:\int_{c+i\infty}^{c-i\infty} f(x) e^{tx) dx, for real c larger than the real part of all singularities of f. Note that ``t`` is always assumed real and positive. Return None if the integral does not exist or could not be evaluated. >>> from sympy.abc import x, t >>> from sympy.integrals.meijerint import meijerint_inversion >>> meijerint_inversion(1/x, x, t) Heaviside(t) """ from sympy import I, Integral, exp, expand, log, Add, Mul, Heaviside f_ = f t_ = t t = Dummy('t', polar=True) # We don't want sqrt(t**2) = abs(t) etc f = f.subs(t_, t) c = Dummy('c') _debug('Laplace-inverting', f) if not _is_analytic(f, x): _debug('But expression is not analytic.') return None # We filter out exponentials here. If we are given an Add this will not # work, but the calling code will take care of that. shift = 0 if f.is_Mul: args = list(f.args) newargs = [] exponentials = [] while args: arg = args.pop() if isinstance(arg, exp): arg2 = expand(arg) if arg2.is_Mul: args += arg2.args continue try: a, b = _get_coeff_exp(arg.args[0], x) except _CoeffExpValueError: b = 0 if b == 1: exponentials.append(a) else: newargs.append(arg) elif arg.is_Pow: arg2 = expand(arg) if arg2.is_Mul: args += arg2.args continue if x not in arg.base.free_symbols: try: a, b = _get_coeff_exp(arg.exp, x) except _CoeffExpValueError: b = 0 if b == 1: exponentials.append(a*log(arg.base)) newargs.append(arg) else: newargs.append(arg) shift = Add(*exponentials) f = Mul(*newargs) gs = _rewrite1(f, x) if gs is not None: fac, po, g, cond = gs _debug('Could rewrite as single G function:', fac, po, g) res = S(0) for C, s, f in g: C, f = _rewrite_inversion(fac*C, po*x**s, f, x) res += C*_int_inversion(f, x, t) cond = And(cond, _check_antecedents_inversion(f, x)) cond = _my_unpolarify(cond) if cond == False: _debug('But cond is always False.') else: _debug('Result before branch substitution:', res) res = _my_unpolarify(hyperexpand(res)) if not res.has(Heaviside): res *= Heaviside(t) res = res.subs(t, t + shift) if not isinstance(cond, bool): cond = cond.subs(t, t + shift) return Piecewise((res.subs(t, t_), cond), (Integral(f_*exp(x*t), (x, c - oo*I, c + oo*I)).subs(t, t_), True)) sympy-0.7.4.1/sympy/integrals/transforms.py0000644000175000017500000016761112253362407021201 0ustar georgeskgeorgesk""" Integral Transforms """ from __future__ import print_function, division from sympy.core import S from sympy.core.compatibility import reduce from sympy.core.function import Function from sympy.core.numbers import oo from sympy.core.symbol import Dummy from sympy.integrals import integrate, Integral from sympy.integrals.meijerint import _dummy from sympy.logic.boolalg import to_cnf, conjuncts, disjuncts, Or, And from sympy.simplify import simplify from sympy.utilities import default_sort_key ########################################################################## # 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 == 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] != 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]) == True or res[1] == False: raise IntegralTransformError( 'Mellin', None, 'no combined convergence.') return res 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 < \operatorname{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 :class:`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(m_n, 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) """ # (This is a separate function because it is moderately complicated, # and I want to doctest it.) # We want to use pi/sin(pi*x) = gamma(x)*gamma(1-x). # But there is one comlication: the gamma functions determine the # inegration contour in the definition of the G-function. Usually # it would not matter if this is slightly shifted, unless this way # we create an undefined function! # So we try to write this in such a way that the gammas are # eminently on the right side of the strip. from sympy import expand_mul, pi, ceiling, gamma, re m, n = m_n 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(key=default_sort_key) ap.sort(key=default_sort_key) bm.sort(key=default_sort_key) bq.sort(key=default_sort_key) 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: try: h = hyperexpand(G) except NotImplementedError as detail: raise IntegralTransformError( 'Inverse Mellin', F, 'Could not calculate integral') 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 == 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)) 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 :class:`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/2 - 1/(2*x))*Heaviside(x - 1) >>> 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 + 1/(2*x))*Heaviside(-x + 1) 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): r""" Naively simplify some conditions occuring in ``expr``, given that `\operatorname{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] != False and x[0] != -oo] if not conds2: conds2 = [x for x in conds if x[1] != 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 == False: raise IntegralTransformError( 'Laplace', None, 'No combined convergence.') return plane, cond 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 < \operatorname{Re}(s)`. This function returns ``(F, a, cond)`` where ``F`` is the Laplace transform of ``f``, `\operatorname{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 :class:`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)*gamma(a + 1)/s, 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)) 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 `\operatorname{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 :class:`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 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 :class:`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 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 :class:`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 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 :class:`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 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 :class:`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 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 :class:`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*exp(-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 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 :class:`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) exp(-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' 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*2**(-m)*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) exp(-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' 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*2**(-m)*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) exp(-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) sympy-0.7.4.1/sympy/integrals/tests/0000755000175000017500000000000012253362407017557 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/integrals/tests/test_lineintegrals.py0000644000175000017500000000044712253362407024035 0ustar georgeskgeorgeskfrom sympy import (symbols, integrate, Integral, diff, sin, cos, pi, E, ln, sympify, Curve, line_integrate, sqrt) s, t, x, y, z = symbols('s,t,x,y,z') def test_lineintegral(): c = Curve([E**t + 1, E**t - 1], (t, 0, ln(2))) assert line_integrate(x + y, c, [x, y]) == 3*sqrt(2) sympy-0.7.4.1/sympy/integrals/tests/test_trigonometry.py0000644000175000017500000000711112253362407023732 0ustar georgeskgeorgeskfrom sympy.core import Eq, Rational, Symbol from sympy.functions import sin, cos, tan, csc, sec, cot, log, Piecewise from sympy.integrals.trigonometry import trigintegrate x = Symbol('x') def test_trigintegrate_odd(): assert trigintegrate(Rational(1), x) == x assert trigintegrate(x, x) is None assert trigintegrate(x**2, x) is None assert trigintegrate(sin(x), x) == -cos(x) assert trigintegrate(cos(x), x) == sin(x) assert trigintegrate(sin(3*x), x) == -cos(3*x)/3 assert trigintegrate(cos(3*x), x) == sin(3*x)/3 y = Symbol('y') assert trigintegrate(sin(y*x), x) == \ Piecewise((0, Eq(y, 0)), (-cos(y*x)/y, True)) assert trigintegrate(cos(y*x), x) == \ Piecewise((x, Eq(y, 0)), (sin(y*x)/y, True)) assert trigintegrate(sin(y*x)**2, x) == \ Piecewise((0, Eq(y, 0)), ((x*y/2 - sin(x*y)*cos(x*y)/2)/y, True)) assert trigintegrate(sin(y*x)*cos(y*x), x) == \ Piecewise((0, Eq(y, 0)), (sin(x*y)**2/(2*y), True)) assert trigintegrate(cos(y*x)**2, x) == \ Piecewise((x, Eq(y, 0)), ((x*y/2 + sin(x*y)*cos(x*y)/2)/y, True)) y = Symbol('y', positive=True) # TODO: remove conds='none' below. For this to work we would have to rule # out (e.g. by trying solve) the condition y = 0, incompatible with # y.is_positive being True. assert trigintegrate(sin(y*x), x, conds='none') == -cos(y*x)/y assert trigintegrate(cos(y*x), x, conds='none') == sin(y*x)/y assert trigintegrate(sin(x)*cos(x), x) == sin(x)**2/2 assert trigintegrate(sin(x)*cos(x)**2, x) == -cos(x)**3/3 assert trigintegrate(sin(x)**2*cos(x), x) == sin(x)**3/3 # check if it selects right function to substitute, # so the result is kept simple assert trigintegrate(sin(x)**7 * cos(x), x) == sin(x)**8/8 assert trigintegrate(sin(x) * cos(x)**7, x) == -cos(x)**8/8 assert trigintegrate(sin(x)**7 * cos(x)**3, x) == \ -sin(x)**10/10 + sin(x)**8/8 assert trigintegrate(sin(x)**3 * cos(x)**7, x) == \ cos(x)**10/10 - cos(x)**8/8 def test_trigintegrate_even(): assert trigintegrate(sin(x)**2, x) == x/2 - cos(x)*sin(x)/2 assert trigintegrate(cos(x)**2, x) == x/2 + cos(x)*sin(x)/2 assert trigintegrate(sin(3*x)**2, x) == x/2 - cos(3*x)*sin(3*x)/6 assert trigintegrate(cos(3*x)**2, x) == x/2 + cos(3*x)*sin(3*x)/6 assert trigintegrate(sin(x)**2 * cos(x)**2, x) == \ x/8 - sin(2*x)*cos(2*x)/16 assert trigintegrate(sin(x)**4 * cos(x)**2, x) == \ x/16 - sin(x) *cos(x)/16 - sin(x)**3*cos(x)/24 + \ sin(x)**5*cos(x)/6 assert trigintegrate(sin(x)**2 * cos(x)**4, x) == \ x/16 + cos(x) *sin(x)/16 + cos(x)**3*sin(x)/24 - \ cos(x)**5*sin(x)/6 assert trigintegrate(sin(x)**(-4), x) == -2*cos(x)/(3*sin(x)) \ - cos(x)/(3*sin(x)**3) assert trigintegrate(cos(x)**(-6), x) == sin(x)/(5*cos(x)**5) \ + 4*sin(x)/(15*cos(x)**3) + 8*sin(x)/(15*cos(x)) def test_trigintegrate_mixed(): assert trigintegrate(sin(x)*sec(x), x) == -log(sin(x)**2 - 1)/2 assert trigintegrate(sin(x)*csc(x), x) == x assert trigintegrate(sin(x)*cot(x), x) == sin(x) assert trigintegrate(cos(x)*sec(x), x) == x assert trigintegrate(cos(x)*csc(x), x) == log(cos(x)**2 - 1)/2 assert trigintegrate(cos(x)*tan(x), x) == -cos(x) assert trigintegrate(cos(x)*cot(x), x) == log(cos(x) - 1)/2 \ - log(cos(x) + 1)/2 + cos(x) def test_trigintegrate_symbolic(): n = Symbol('n', integer=True) assert trigintegrate(cos(x)**n, x) is None assert trigintegrate(sin(x)**n, x) is None assert trigintegrate(cot(x)**n, x) is None sympy-0.7.4.1/sympy/integrals/tests/test_transforms.py0000644000175000017500000007366412253362407023406 0ustar georgeskgeorgeskfrom sympy.integrals.transforms import (mellin_transform, inverse_mellin_transform, laplace_transform, inverse_laplace_transform, fourier_transform, inverse_fourier_transform, sine_transform, inverse_sine_transform, cosine_transform, inverse_cosine_transform, hankel_transform, inverse_hankel_transform, LaplaceTransform, FourierTransform, SineTransform, CosineTransform, InverseLaplaceTransform, InverseFourierTransform, InverseSineTransform, InverseCosineTransform, HankelTransform, InverseHankelTransform) from sympy import ( gamma, exp, oo, Heaviside, symbols, Symbol, re, factorial, pi, cos, S, And, sin, sqrt, I, log, tan, hyperexpand, meijerg, EulerGamma, erf, besselj, bessely, besseli, besselk, exp_polar, polar_lift, unpolarify, Function, expint, expand_mul, combsimp, trigsimp) from sympy.utilities.pytest import XFAIL, slow, skip from sympy.abc import x, s, a, b, c, d nu, beta, rho = symbols('nu beta rho') def test_undefined_function(): from sympy import Function, MellinTransform f = Function('f') assert mellin_transform(f(x), x, s) == MellinTransform(f(x), x, s) assert mellin_transform(f(x) + exp(-x), x, s) == \ (MellinTransform(f(x), x, s) + gamma(s), (0, oo), True) assert laplace_transform(2*f(x), x, s) == 2*LaplaceTransform(f(x), x, s) # TODO test derivative and other rules when implemented def test_free_symbols(): from sympy import Function f = Function('f') assert mellin_transform(f(x), x, s).free_symbols == set([s]) assert mellin_transform(f(x)*a, x, s).free_symbols == set([s, a]) def test_as_integral(): from sympy import Function, Integral f = Function('f') assert mellin_transform(f(x), x, s).rewrite('Integral') == \ Integral(x**(s - 1)*f(x), (x, 0, oo)) assert fourier_transform(f(x), x, s).rewrite('Integral') == \ Integral(f(x)*exp(-2*I*pi*s*x), (x, -oo, oo)) assert laplace_transform(f(x), x, s).rewrite('Integral') == \ Integral(f(x)*exp(-s*x), (x, 0, oo)) assert str(inverse_mellin_transform(f(s), s, x, (a, b)).rewrite('Integral')) \ == "Integral(x**(-s)*f(s), (s, _c - oo*I, _c + oo*I))" assert str(inverse_laplace_transform(f(s), s, x).rewrite('Integral')) == \ "Integral(f(s)*exp(s*x), (s, _c - oo*I, _c + oo*I))" assert inverse_fourier_transform(f(s), s, x).rewrite('Integral') == \ Integral(f(s)*exp(2*I*pi*s*x), (s, -oo, oo)) # NOTE this is stuck in risch because meijerint cannot handle it @slow @XFAIL def test_mellin_transform_fail(): skip("Risch takes forever.") from sympy import Max, Min MT = mellin_transform bpos = symbols('b', positive=True) bneg = symbols('b', negative=True) expr = (sqrt(x + b**2) + b)**a/sqrt(x + b**2) # TODO does not work with bneg, argument wrong. Needs changes to matching. assert MT(expr.subs(b, -bpos), x, s) == \ ((-1)**(a + 1)*2**(a + 2*s)*bpos**(a + 2*s - 1)*gamma(a + s) *gamma(1 - a - 2*s)/gamma(1 - s), (-re(a), -re(a)/2 + S(1)/2), True) expr = (sqrt(x + b**2) + b)**a assert MT(expr.subs(b, -bpos), x, s) == \ ( 2**(a + 2*s)*a*bpos**(a + 2*s)*gamma(-a - 2* s)*gamma(a + s)/gamma(-s + 1), (-re(a), -re(a)/2), True) # Test exponent 1: assert MT(expr.subs({b: -bpos, a: 1}), x, s) == \ (-bpos**(2*s + 1)*gamma(s)*gamma(-s - S(1)/2)/(2*sqrt(pi)), (-1, -S(1)/2), True) def test_mellin_transform(): from sympy import Max, Min, Ne MT = mellin_transform bpos = symbols('b', positive=True) # 8.4.2 assert MT(x**nu*Heaviside(x - 1), x, s) == \ (-1/(nu + s), (-oo, -re(nu)), True) assert MT(x**nu*Heaviside(1 - x), x, s) == \ (1/(nu + s), (-re(nu), oo), True) assert MT((1 - x)**(beta - 1)*Heaviside(1 - x), x, s) == \ (gamma(beta)*gamma(s)/gamma(beta + s), (0, oo), re(-beta) < 0) assert MT((x - 1)**(beta - 1)*Heaviside(x - 1), x, s) == \ (gamma(beta)*gamma(1 - beta - s)/gamma(1 - s), (-oo, -re(beta) + 1), re(-beta) < 0) assert MT((1 + x)**(-rho), x, s) == \ (gamma(s)*gamma(rho - s)/gamma(rho), (0, re(rho)), True) # TODO also the conditions should be simplified assert MT(abs(1 - x)**(-rho), x, s) == ( cos(pi*(rho/2 - s))*gamma(s)*gamma(rho - s)/(cos(pi*rho/2)*gamma(rho)), (0, re(rho)), And(re(rho) - 1 < 0, re(rho) < 1)) mt = MT((1 - x)**(beta - 1)*Heaviside(1 - x) + a*(x - 1)**(beta - 1)*Heaviside(x - 1), x, s) assert mt[1], mt[2] == ((0, -re(beta) + 1), True) assert MT((x**a - b**a)/(x - b), x, s)[0] == \ pi*b**(a + s - 1)*sin(pi*a)/(sin(pi*s)*sin(pi*(a + s))) assert MT((x**a - bpos**a)/(x - bpos), x, s) == \ (pi*bpos**(a + s - 1)*sin(pi*a)/(sin(pi*s)*sin(pi*(a + s))), (Max(-re(a), 0), Min(1 - re(a), 1)), True) expr = (sqrt(x + b**2) + b)**a assert MT(expr.subs(b, bpos), x, s) == \ (-a*(2*bpos)**(a + 2*s)*gamma(s)*gamma(-a - 2*s)/gamma(-a - s + 1), (0, -re(a)/2), True) expr = (sqrt(x + b**2) + b)**a/sqrt(x + b**2) assert MT(expr.subs(b, bpos), x, s) == \ (2**(a + 2*s)*bpos**(a + 2*s - 1)*gamma(s) *gamma(1 - a - 2*s)/gamma(1 - a - s), (0, -re(a)/2 + S(1)/2), True) # 8.4.2 assert MT(exp(-x), x, s) == (gamma(s), (0, oo), True) assert MT(exp(-1/x), x, s) == (gamma(-s), (-oo, 0), True) # 8.4.5 assert MT(log(x)**4*Heaviside(1 - x), x, s) == (24/s**5, (0, oo), True) assert MT(log(x)**3*Heaviside(x - 1), x, s) == (6/s**4, (-oo, 0), True) assert MT(log(x + 1), x, s) == (pi/(s*sin(pi*s)), (-1, 0), True) assert MT(log(1/x + 1), x, s) == (pi/(s*sin(pi*s)), (0, 1), True) assert MT(log(abs(1 - x)), x, s) == (pi/(s*tan(pi*s)), (-1, 0), True) assert MT(log(abs(1 - 1/x)), x, s) == (pi/(s*tan(pi*s)), (0, 1), True) # TODO we cannot currently do these (needs summation of 3F2(-1)) # this also implies that they cannot be written as a single g-function # (although this is possible) mt = MT(log(x)/(x + 1), x, s) assert mt[1:] == ((0, 1), True) assert not hyperexpand(mt[0], allow_hyper=True).has(meijerg) mt = MT(log(x)**2/(x + 1), x, s) assert mt[1:] == ((0, 1), True) assert not hyperexpand(mt[0], allow_hyper=True).has(meijerg) mt = MT(log(x)/(x + 1)**2, x, s) assert mt[1:] == ((0, 2), True) assert not hyperexpand(mt[0], allow_hyper=True).has(meijerg) # 8.4.14 assert MT(erf(sqrt(x)), x, s) == \ (-gamma(s + S(1)/2)/(sqrt(pi)*s), (-S(1)/2, 0), True) def test_mellin_transform_bessel(): from sympy import Max, Min, hyper, meijerg MT = mellin_transform # 8.4.19 assert MT(besselj(a, 2*sqrt(x)), x, s) == \ (gamma(a/2 + s)/gamma(a/2 - s + 1), (-re(a)/2, S(3)/4), True) assert MT(sin(sqrt(x))*besselj(a, sqrt(x)), x, s) == \ (2**a*gamma(-2*s + S(1)/2)*gamma(a/2 + s + S(1)/2)/( gamma(-a/2 - s + 1)*gamma(a - 2*s + 1)), ( -re(a)/2 - S(1)/2, S(1)/4), True) assert MT(cos(sqrt(x))*besselj(a, sqrt(x)), x, s) == \ (2**a*gamma(a/2 + s)*gamma(-2*s + S(1)/2)/( gamma(-a/2 - s + S(1)/2)*gamma(a - 2*s + 1)), ( -re(a)/2, S(1)/4), True) assert MT(besselj(a, sqrt(x))**2, x, s) == \ (gamma(a + s)*gamma(S(1)/2 - s) / (sqrt(pi)*gamma(1 - s)*gamma(1 + a - s)), (-re(a), S(1)/2), True) assert MT(besselj(a, sqrt(x))*besselj(-a, sqrt(x)), x, s) == \ (gamma(s)*gamma(S(1)/2 - s) / (sqrt(pi)*gamma(1 - a - s)*gamma(1 + a - s)), (0, S(1)/2), True) # NOTE: prudnikov gives the strip below as (1/2 - re(a), 1). As far as # I can see this is wrong (since besselj(z) ~ 1/sqrt(z) for z large) assert MT(besselj(a - 1, sqrt(x))*besselj(a, sqrt(x)), x, s) == \ (gamma(1 - s)*gamma(a + s - S(1)/2) / (sqrt(pi)*gamma(S(3)/2 - s)*gamma(a - s + S(1)/2)), (S(1)/2 - re(a), S(1)/2), True) assert MT(besselj(a, sqrt(x))*besselj(b, sqrt(x)), x, s) == \ (4**s*gamma(1 - 2*s)*gamma((a + b)/2 + s) / (gamma(1 - s + (b - a)/2)*gamma(1 - s + (a - b)/2) *gamma( 1 - s + (a + b)/2)), (-(re(a) + re(b))/2, S(1)/2), True) assert MT(besselj(a, sqrt(x))**2 + besselj(-a, sqrt(x))**2, x, s)[1:] == \ ((Max(re(a), -re(a)), S(1)/2), True) # Section 8.4.20 assert MT(bessely(a, 2*sqrt(x)), x, s) == \ (-cos(pi*(a/2 - s))*gamma(s - a/2)*gamma(s + a/2)/pi, (Max(-re(a)/2, re(a)/2), S(3)/4), True) assert MT(sin(sqrt(x))*bessely(a, sqrt(x)), x, s) == \ (-4**s*sin(pi*(a/2 - s))*gamma(S(1)/2 - 2*s) * gamma((1 - a)/2 + s)*gamma((1 + a)/2 + s) / (sqrt(pi)*gamma(1 - s - a/2)*gamma(1 - s + a/2)), (Max(-(re(a) + 1)/2, (re(a) - 1)/2), S(1)/4), True) assert MT(cos(sqrt(x))*bessely(a, sqrt(x)), x, s) == \ (-4**s*cos(pi*(a/2 - s))*gamma(s - a/2)*gamma(s + a/2)*gamma(S(1)/2 - 2*s) / (sqrt(pi)*gamma(S(1)/2 - s - a/2)*gamma(S(1)/2 - s + a/2)), (Max(-re(a)/2, re(a)/2), S(1)/4), True) assert MT(besselj(a, sqrt(x))*bessely(a, sqrt(x)), x, s) == \ (-cos(pi*s)*gamma(s)*gamma(a + s)*gamma(S(1)/2 - s) / (pi**S('3/2')*gamma(1 + a - s)), (Max(-re(a), 0), S(1)/2), True) assert MT(besselj(a, sqrt(x))*bessely(b, sqrt(x)), x, s) == \ (-4**s*cos(pi*(a/2 - b/2 + s))*gamma(1 - 2*s) * gamma(a/2 - b/2 + s)*gamma(a/2 + b/2 + s) / (pi*gamma(a/2 - b/2 - s + 1)*gamma(a/2 + b/2 - s + 1)), (Max((-re(a) + re(b))/2, (-re(a) - re(b))/2), S(1)/2), True) # NOTE bessely(a, sqrt(x))**2 and bessely(a, sqrt(x))*bessely(b, sqrt(x)) # are a mess (no matter what way you look at it ...) assert MT(bessely(a, sqrt(x))**2, x, s)[1:] == \ ((Max(-re(a), 0, re(a)), S(1)/2), True) # Section 8.4.22 # TODO we can't do any of these (delicate cancellation) # Section 8.4.23 assert MT(besselk(a, 2*sqrt(x)), x, s) == \ (gamma( s - a/2)*gamma(s + a/2)/2, (Max(-re(a)/2, re(a)/2), oo), True) assert MT(besselj(a, 2*sqrt(2*sqrt(x)))*besselk( a, 2*sqrt(2*sqrt(x))), x, s) == (4**(-s)*gamma(2*s)* gamma(a/2 + s)/(2*gamma(a/2 - s + 1)), (Max(0, -re(a)/2), oo), True) # TODO bessely(a, x)*besselk(a, x) is a mess assert MT(besseli(a, sqrt(x))*besselk(a, sqrt(x)), x, s) == \ (gamma(s)*gamma( a + s)*gamma(-s + S(1)/2)/(2*sqrt(pi)*gamma(a - s + 1)), (Max(-re(a), 0), S(1)/2), True) assert MT(besseli(b, sqrt(x))*besselk(a, sqrt(x)), x, s) == \ (2**(2*s - 1)*gamma(-2*s + 1)*gamma(-a/2 + b/2 + s)* \ gamma(a/2 + b/2 + s)/(gamma(-a/2 + b/2 - s + 1)* \ gamma(a/2 + b/2 - s + 1)), (Max(-re(a)/2 - re(b)/2, \ re(a)/2 - re(b)/2), S(1)/2), True) # TODO products of besselk are a mess mt = MT(exp(-x/2)*besselk(a, x/2), x, s) mt0 = combsimp((trigsimp(combsimp(mt[0].expand(func=True))))) assert mt0 == 2*pi**(S(3)/2)*cos(pi*s)*gamma(-s + S(1)/2)/( (cos(2*pi*a) - cos(2*pi*s))*gamma(-a - s + 1)*gamma(a - s + 1)) assert mt[1:] == ((Max(-re(a), re(a)), oo), True) # TODO exp(x/2)*besselk(a, x/2) [etc] cannot currently be done # TODO various strange products of special orders def test_expint(): from sympy import E1, expint, Max, re, lerchphi, Symbol, simplify, Si, Ci, Ei aneg = Symbol('a', negative=True) u = Symbol('u', polar=True) assert mellin_transform(E1(x), x, s) == (gamma(s)/s, (0, oo), True) assert inverse_mellin_transform(gamma(s)/s, s, x, (0, oo)).rewrite(expint).expand() == E1(x) assert mellin_transform(expint(a, x), x, s) == \ (gamma(s)/(a + s - 1), (Max(1 - re(a), 0), oo), True) # XXX IMT has hickups with complicated strips ... assert simplify(unpolarify( inverse_mellin_transform(gamma(s)/(aneg + s - 1), s, x, (1 - aneg, oo)).rewrite(expint).expand(func=True))) == \ expint(aneg, x) assert mellin_transform(Si(x), x, s) == \ (-2**s*sqrt(pi)*gamma(s/2 + S(1)/2)/( 2*s*gamma(-s/2 + 1)), (-1, 0), True) assert inverse_mellin_transform(-2**s*sqrt(pi)*gamma((s + 1)/2) /(2*s*gamma(-s/2 + 1)), s, x, (-1, 0)) \ == Si(x) assert mellin_transform(Ci(sqrt(x)), x, s) == \ (-2**(2*s - 1)*sqrt(pi)*gamma(s)/(s*gamma(-s + S(1)/2)), (0, 1), True) assert inverse_mellin_transform( -4**s*sqrt(pi)*gamma(s)/(2*s*gamma(-s + S(1)/2)), s, u, (0, 1)).expand() == Ci(sqrt(u)) # TODO LT of Si, Shi, Chi is a mess ... assert laplace_transform(Ci(x), x, s) == (-log(1 + s**2)/2/s, 0, True) assert laplace_transform(expint(a, x), x, s) == \ (lerchphi(s*polar_lift(-1), 1, a), 0, S(0) < re(a)) assert laplace_transform(expint(1, x), x, s) == (log(s + 1)/s, 0, True) assert laplace_transform(expint(2, x), x, s) == \ ((s - log(s + 1))/s**2, 0, True) assert inverse_laplace_transform(-log(1 + s**2)/2/s, s, u).expand() == \ Heaviside(u)*Ci(u) assert inverse_laplace_transform(log(s + 1)/s, s, x).rewrite(expint) == \ Heaviside(x)*E1(x) assert inverse_laplace_transform((s - log(s + 1))/s**2, s, x).rewrite(expint).expand() == \ (expint(2, x)*Heaviside(x)).rewrite(Ei).rewrite(expint).expand() def test_inverse_mellin_transform(): from sympy import (sin, simplify, expand_func, powsimp, Max, Min, expand, powdenest, powsimp, exp_polar, combsimp, cos, cot) IMT = inverse_mellin_transform assert IMT(gamma(s), s, x, (0, oo)) == exp(-x) assert IMT(gamma(-s), s, x, (-oo, 0)) == exp(-1/x) assert simplify(IMT(s/(2*s**2 - 2), s, x, (2, oo))) == \ (x**2 + 1)*Heaviside(1 - x)/(4*x) # test passing "None" assert IMT(1/(s**2 - 1), s, x, (-1, None)) == \ -x*Heaviside(-x + 1)/2 - Heaviside(x - 1)/(2*x) assert IMT(1/(s**2 - 1), s, x, (None, 1)) == \ -x*Heaviside(-x + 1)/2 - Heaviside(x - 1)/(2*x) # test expansion of sums assert IMT(gamma(s) + gamma(s - 1), s, x, (1, oo)) == (x + 1)*exp(-x)/x # test factorisation of polys r = symbols('r', real=True) assert IMT(1/(s**2 + 1), s, exp(-x), (None, oo) ).subs(x, r).rewrite(sin).simplify() \ == sin(r)*Heaviside(1 - exp(-r)) # test multiplicative substitution _a, _b = symbols('a b', positive=True) assert IMT(_b**(-s/_a)*factorial(s/_a)/s, s, x, (0, oo)) == exp(-_b*x**_a) assert IMT(factorial(_a/_b + s/_b)/(_a + s), s, x, (-_a, oo)) == x**_a*exp(-x**_b) def simp_pows(expr): return simplify(powsimp(expand_mul(expr, deep=False), force=True)).replace(exp_polar, exp) # Now test the inverses of all direct transforms tested above # Section 8.4.2 assert IMT(-1/(nu + s), s, x, (-oo, None)) == x**nu*Heaviside(x - 1) assert IMT(1/(nu + s), s, x, (None, oo)) == x**nu*Heaviside(1 - x) assert simp_pows(IMT(gamma(beta)*gamma(s)/gamma(s + beta), s, x, (0, oo))) \ == (1 - x)**(beta - 1)*Heaviside(1 - x) assert simp_pows(IMT(gamma(beta)*gamma(1 - beta - s)/gamma(1 - s), s, x, (-oo, None))) \ == (x - 1)**(beta - 1)*Heaviside(x - 1) assert simp_pows(IMT(gamma(s)*gamma(rho - s)/gamma(rho), s, x, (0, None))) \ == (1/(x + 1))**rho assert simp_pows(IMT(d**c*d**(s - 1)*sin(pi*c) *gamma(s)*gamma(s + c)*gamma(1 - s)*gamma(1 - s - c)/pi, s, x, (Max(-re(c), 0), Min(1 - re(c), 1)))) \ == (x**c - d**c)/(x - d) assert simplify(IMT(1/sqrt(pi)*(-c/2)*gamma(s)*gamma((1 - c)/2 - s) *gamma(-c/2 - s)/gamma(1 - c - s), s, x, (0, -re(c)/2))) == \ (1 + sqrt(x + 1))**c assert simplify(IMT(2**(a + 2*s)*b**(a + 2*s - 1)*gamma(s)*gamma(1 - a - 2*s) /gamma(1 - a - s), s, x, (0, (-re(a) + 1)/2))) == \ b**(a - 1)*(sqrt(1 + x/b**2) + 1)**(a - 1)*(b**2*sqrt(1 + x/b**2) + b**2 + x)/(b**2 + x) assert simplify(IMT(-2**(c + 2*s)*c*b**(c + 2*s)*gamma(s)*gamma(-c - 2*s) / gamma(-c - s + 1), s, x, (0, -re(c)/2))) == \ b**c*(sqrt(1 + x/b**2) + 1)**c # Section 8.4.5 assert IMT(24/s**5, s, x, (0, oo)) == log(x)**4*Heaviside(1 - x) assert expand(IMT(6/s**4, s, x, (-oo, 0)), force=True) == \ log(x)**3*Heaviside(x - 1) assert IMT(pi/(s*sin(pi*s)), s, x, (-1, 0)) == log(x + 1) assert IMT(pi/(s*sin(pi*s/2)), s, x, (-2, 0)) == log(x**2 + 1) assert IMT(pi/(s*sin(2*pi*s)), s, x, (-S(1)/2, 0)) == log(sqrt(x) + 1) assert IMT(pi/(s*sin(pi*s)), s, x, (0, 1)) == log(1 + 1/x) # TODO def mysimp(expr): from sympy import expand, logcombine, powsimp return expand( powsimp(logcombine(expr, force=True), force=True, deep=True), force=True).replace(exp_polar, exp) assert mysimp(mysimp(IMT(pi/(s*tan(pi*s)), s, x, (-1, 0)))) in [ log(1 - x)*Heaviside(1 - x) + log(x - 1)*Heaviside(x - 1), log(x)*Heaviside(x - 1) + log(1 - 1/x)*Heaviside(x - 1) + log(-x + 1)*Heaviside(-x + 1)] # test passing cot assert mysimp(IMT(pi*cot(pi*s)/s, s, x, (0, 1))) in [ log(1/x - 1)*Heaviside(1 - x) + log(1 - 1/x)*Heaviside(x - 1), -log(x)*Heaviside(-x + 1) + log(1 - 1/x)*Heaviside(x - 1) + log(-x + 1)*Heaviside(-x + 1), ] # 8.4.14 assert IMT(-gamma(s + S(1)/2)/(sqrt(pi)*s), s, x, (-S(1)/2, 0)) == \ erf(sqrt(x)) # 8.4.19 assert simplify(IMT(gamma(a/2 + s)/gamma(a/2 - s + 1), s, x, (-re(a)/2, S(3)/4))) \ == besselj(a, 2*sqrt(x)) assert simplify(IMT(2**a*gamma(S(1)/2 - 2*s)*gamma(s + (a + 1)/2) / (gamma(1 - s - a/2)*gamma(1 - 2*s + a)), s, x, (-(re(a) + 1)/2, S(1)/4))) == \ sin(sqrt(x))*besselj(a, sqrt(x)) assert simplify(IMT(2**a*gamma(a/2 + s)*gamma(S(1)/2 - 2*s) / (gamma(S(1)/2 - s - a/2)*gamma(1 - 2*s + a)), s, x, (-re(a)/2, S(1)/4))) == \ cos(sqrt(x))*besselj(a, sqrt(x)) # TODO this comes out as an amazing mess, but simplifies nicely assert simplify(IMT(gamma(a + s)*gamma(S(1)/2 - s) / (sqrt(pi)*gamma(1 - s)*gamma(1 + a - s)), s, x, (-re(a), S(1)/2))) == \ besselj(a, sqrt(x))**2 assert simplify(IMT(gamma(s)*gamma(S(1)/2 - s) / (sqrt(pi)*gamma(1 - s - a)*gamma(1 + a - s)), s, x, (0, S(1)/2))) == \ besselj(-a, sqrt(x))*besselj(a, sqrt(x)) assert simplify(IMT(4**s*gamma(-2*s + 1)*gamma(a/2 + b/2 + s) / (gamma(-a/2 + b/2 - s + 1)*gamma(a/2 - b/2 - s + 1) *gamma(a/2 + b/2 - s + 1)), s, x, (-(re(a) + re(b))/2, S(1)/2))) == \ besselj(a, sqrt(x))*besselj(b, sqrt(x)) # Section 8.4.20 # TODO this can be further simplified! assert simplify(IMT(-2**(2*s)*cos(pi*a/2 - pi*b/2 + pi*s)*gamma(-2*s + 1) * gamma(a/2 - b/2 + s)*gamma(a/2 + b/2 + s) / (pi*gamma(a/2 - b/2 - s + 1)*gamma(a/2 + b/2 - s + 1)), s, x, (Max(-re(a)/2 - re(b)/2, -re(a)/2 + re(b)/2), S(1)/2))) == \ (cos(pi*b)*besselj(b, sqrt(x)) - besselj(-b, sqrt(x)))*besselj(a, sqrt(x))/sin(pi*b) # TODO more # for coverage assert IMT(pi/cos(pi*s), s, x, (0, S(1)/2)) == sqrt(x)/(x + 1) def test_laplace_transform(): from sympy import (fresnels, fresnelc, hyper) LT = laplace_transform a, b, c, = symbols('a b c', positive=True) t = symbols('t') w = Symbol("w") f = Function("f") # Test unevaluated form assert laplace_transform(f(t), t, w) == LaplaceTransform(f(t), t, w) assert inverse_laplace_transform( f(w), w, t, plane=0) == InverseLaplaceTransform(f(w), w, t, 0) # test a bug spos = symbols('s', positive=True) assert LT(exp(t), t, spos)[:2] == (1/(spos - 1), True) # basic tests from wikipedia assert LT((t - a)**b*exp(-c*(t - a))*Heaviside(t - a), t, s) == \ ((s + c)**(-b - 1)*exp(-a*s)*gamma(b + 1), -c, True) assert LT(t**a, t, s) == (s**(-a - 1)*gamma(a + 1), 0, True) assert LT(Heaviside(t), t, s) == (1/s, 0, True) assert LT(Heaviside(t - a), t, s) == (exp(-a*s)/s, 0, True) assert LT(1 - exp(-a*t), t, s) == (a/(s*(a + s)), 0, True) assert LT((exp(2*t) - 1)*exp(-b - t)*Heaviside(t)/2, t, s, noconds=True) \ == exp(-b)/(s**2 - 1) assert LT(exp(t), t, s)[:2] == (1/(s - 1), 1) assert LT(exp(2*t), t, s)[:2] == (1/(s - 2), 2) assert LT(exp(a*t), t, s)[:2] == (1/(s - a), a) assert LT(log(t/a), t, s) == ((log(a*s) + EulerGamma)/s/-1, 0, True) assert LT(erf(t), t, s) == ((-erf(s/2) + 1)*exp(s**2/4)/s, 0, True) assert LT(sin(a*t), t, s) == (a/(a**2 + s**2), 0, True) assert LT(cos(a*t), t, s) == (s/(a**2 + s**2), 0, True) # TODO would be nice to have these come out better assert LT( exp(-a*t)*sin(b*t), t, s) == (b/(b**2 + (a + s)**2), -a, True) assert LT(exp(-a*t)*cos(b*t), t, s) == \ ((a + s)/(b**2 + (a + s)**2), -a, True) # TODO sinh, cosh have delicate cancellation assert LT(besselj(0, t), t, s) == (1/sqrt(1 + s**2), 0, True) assert LT(besselj(1, t), t, s) == (1 - 1/sqrt(1 + 1/s**2), 0, True) # TODO general order works, but is a *mess* # TODO besseli also works, but is an even greater mess # test a bug in conditions processing # TODO the auxiliary condition should be recognised/simplified assert LT(exp(t)*cos(t), t, s)[:-1] in [ ((s - 1)/(s**2 - 2*s + 2), -oo), ((s - 1)/((s - 1)**2 + 1), -oo), ] # Fresnel functions assert laplace_transform(fresnels(t), t, s) == \ ((-sin(s**2/(2*pi))*fresnels(s/pi) + sin(s**2/(2*pi))/2 - cos(s**2/(2*pi))*fresnelc(s/pi) + cos(s**2/(2*pi))/2)/s, 0, True) assert laplace_transform(fresnelc(t), t, s) == ( (sin(s**2/(2*pi))*fresnelc(s/pi)/s - cos(s**2/(2*pi))*fresnels(s/pi)/s + sqrt(2)*cos(s**2/(2*pi) + pi/4)/(2*s), 0, True)) def test_inverse_laplace_transform(): from sympy import (expand, sinh, cosh, besselj, besseli, exp_polar, unpolarify, simplify, factor_terms) ILT = inverse_laplace_transform a, b, c, = symbols('a b c', positive=True) t = symbols('t') def simp_hyp(expr): return factor_terms(expand_mul(expr)).rewrite(sin) # just test inverses of all of the above assert ILT(1/s, s, t) == Heaviside(t) assert ILT(1/s**2, s, t) == t*Heaviside(t) assert ILT(1/s**5, s, t) == t**4*Heaviside(t)/24 assert ILT(exp(-a*s)/s, s, t) == Heaviside(t - a) assert ILT(exp(-a*s)/(s + b), s, t) == exp(b*(a - t))*Heaviside(-a + t) assert ILT(a/(s**2 + a**2), s, t) == sin(a*t)*Heaviside(t) assert ILT(s/(s**2 + a**2), s, t) == cos(a*t)*Heaviside(t) # TODO is there a way around simp_hyp? assert simp_hyp(ILT(a/(s**2 - a**2), s, t)) == sinh(a*t)*Heaviside(t) assert simp_hyp(ILT(s/(s**2 - a**2), s, t)) == cosh(a*t)*Heaviside(t) assert ILT(a/((s + b)**2 + a**2), s, t) == exp(-b*t)*sin(a*t)*Heaviside(t) assert ILT( (s + b)/((s + b)**2 + a**2), s, t) == exp(-b*t)*cos(a*t)*Heaviside(t) # TODO sinh/cosh shifted come out a mess. also delayed trig is a mess # TODO should this simplify further? assert ILT(exp(-a*s)/s**b, s, t) == \ (t - a)**(b - 1)*Heaviside(t - a)/gamma(b) assert ILT(exp(-a*s)/sqrt(1 + s**2), s, t) == \ Heaviside(t - a)*besselj(0, a - t) # note: besselj(0, x) is even # XXX ILT turns these branch factor into trig functions ... assert simplify(ILT(a**b*(s + sqrt(s**2 - a**2))**(-b)/sqrt(s**2 - a**2), s, t).rewrite(exp)) == \ Heaviside(t)*besseli(b, a*t) assert ILT(a**b*(s + sqrt(s**2 + a**2))**(-b)/sqrt(s**2 + a**2), s, t).rewrite(exp) == \ Heaviside(t)*besselj(b, a*t) assert ILT(1/(s*sqrt(s + 1)), s, t) == Heaviside(t)*erf(sqrt(t)) # TODO can we make erf(t) work? assert ILT(1/(s**2*(s**2 + 1)),s,t) == (t - sin(t))*Heaviside(t) def test_fourier_transform(): from sympy import simplify, expand, expand_complex, factor, expand_trig FT = fourier_transform IFT = inverse_fourier_transform def simp(x): return simplify(expand_trig(expand_complex(expand(x)))) def sinc(x): return sin(pi*x)/(pi*x) k = symbols('k', real=True) f = Function("f") # TODO for this to work with real a, need to expand abs(a*x) to abs(a)*abs(x) a = symbols('a', positive=True) b = symbols('b', positive=True) posk = symbols('posk', positive=True) # Test unevaluated form assert fourier_transform(f(x), x, k) == FourierTransform(f(x), x, k) assert inverse_fourier_transform( f(k), k, x) == InverseFourierTransform(f(k), k, x) # basic examples from wikipedia assert simp(FT(Heaviside(1 - abs(2*a*x)), x, k)) == sinc(k/a)/a # TODO IFT is a *mess* assert simp(FT(Heaviside(1 - abs(a*x))*(1 - abs(a*x)), x, k)) == sinc(k/a)**2/a # TODO IFT assert factor(FT(exp(-a*x)*Heaviside(x), x, k), extension=I) == \ 1/(a + 2*pi*I*k) # NOTE: the ift comes out in pieces assert IFT(1/(a + 2*pi*I*x), x, posk, noconds=False) == (exp(-a*posk), True) assert IFT(1/(a + 2*pi*I*x), x, -posk, noconds=False) == (0, True) assert IFT(1/(a + 2*pi*I*x), x, symbols('k', negative=True), noconds=False) == (0, True) # TODO IFT without factoring comes out as meijer g assert factor(FT(x*exp(-a*x)*Heaviside(x), x, k), extension=I) == \ 1/(a + 2*pi*I*k)**2 assert FT(exp(-a*x)*sin(b*x)*Heaviside(x), x, k) == \ b/(b**2 + (a + 2*I*pi*k)**2) assert FT(exp(-a*x**2), x, k) == sqrt(pi)*exp(-pi**2*k**2/a)/sqrt(a) assert IFT(sqrt(pi/a)*exp(-(pi*k)**2/a), k, x) == exp(-a*x**2) assert FT(exp(-a*abs(x)), x, k) == 2*a/(a**2 + 4*pi**2*k**2) # TODO IFT (comes out as meijer G) # TODO besselj(n, x), n an integer > 0 actually can be done... # TODO are there other common transforms (no distributions!)? def test_sine_transform(): from sympy import sinh, cosh, EulerGamma t = symbols("t") w = symbols("w") a = symbols("a") f = Function("f") # Test unevaluated form assert sine_transform(f(t), t, w) == SineTransform(f(t), t, w) assert inverse_sine_transform( f(w), w, t) == InverseSineTransform(f(w), w, t) assert sine_transform(1/sqrt(t), t, w) == 1/sqrt(w) assert inverse_sine_transform(1/sqrt(w), w, t) == 1/sqrt(t) assert sine_transform( (1/sqrt(t))**3, t, w) == sqrt(w)*gamma(S(1)/4)/(2*gamma(S(5)/4)) assert sine_transform(t**(-a), t, w) == 2**( -a + S(1)/2)*w**(a - 1)*gamma(-a/2 + 1)/gamma((a + 1)/2) assert inverse_sine_transform(2**(-a + S( 1)/2)*w**(a - 1)*gamma(-a/2 + 1)/gamma(a/2 + S(1)/2), w, t) == t**(-a) assert sine_transform( exp(-a*t), t, w) == sqrt(2)*w/(sqrt(pi)*(a**2 + w**2)) assert inverse_sine_transform( sqrt(2)*w/(sqrt(pi)*(a**2 + w**2)), w, t) == exp(-a*t) assert sine_transform( log(t)/t, t, w) == -sqrt(2)*sqrt(pi)*(log(w**2) + 2*EulerGamma)/4 assert sine_transform( t*exp(-a*t**2), t, w) == sqrt(2)*w*exp(-w**2/(4*a))/(4*a**(S(3)/2)) assert inverse_sine_transform( sqrt(2)*w*exp(-w**2/(4*a))/(4*a**(S(3)/2)), w, t) == t*exp(-a*t**2) def test_cosine_transform(): from sympy import sinh, cosh, Si, Ci t = symbols("t") w = symbols("w") a = symbols("a") f = Function("f") # Test unevaluated form assert cosine_transform(f(t), t, w) == CosineTransform(f(t), t, w) assert inverse_cosine_transform( f(w), w, t) == InverseCosineTransform(f(w), w, t) assert cosine_transform(1/sqrt(t), t, w) == 1/sqrt(w) assert inverse_cosine_transform(1/sqrt(w), w, t) == 1/sqrt(t) assert cosine_transform(1/( a**2 + t**2), t, w) == sqrt(2)*sqrt(pi)*exp(-a*w)/(2*a) assert cosine_transform(t**( -a), t, w) == 2**(-a + S(1)/2)*w**(a - 1)*gamma((-a + 1)/2)/gamma(a/2) assert inverse_cosine_transform(2**(-a + S( 1)/2)*w**(a - 1)*gamma(-a/2 + S(1)/2)/gamma(a/2), w, t) == t**(-a) assert cosine_transform( exp(-a*t), t, w) == sqrt(2)*a/(sqrt(pi)*(a**2 + w**2)) assert inverse_cosine_transform( sqrt(2)*a/(sqrt(pi)*(a**2 + w**2)), w, t) == exp(-a*t) assert cosine_transform(exp(-a*sqrt(t))*cos(a*sqrt( t)), t, w) == a*exp(-a**2/(2*w))/(2*w**(S(3)/2)) assert cosine_transform(1/(a + t), t, w) == sqrt(2)*( (-2*Si(a*w) + pi)*sin(a*w)/2 - cos(a*w)*Ci(a*w))/sqrt(pi) assert inverse_cosine_transform(sqrt(2)*meijerg(((S(1)/2, 0), ()), ( (S(1)/2, 0, 0), (S(1)/2,)), a**2*w**2/4)/(2*pi), w, t) == 1/(a + t) assert cosine_transform(1/sqrt(a**2 + t**2), t, w) == sqrt(2)*meijerg( ((S(1)/2,), ()), ((0, 0), (S(1)/2,)), a**2*w**2/4)/(2*sqrt(pi)) assert inverse_cosine_transform(sqrt(2)*meijerg(((S(1)/2,), ()), ((0, 0), (S(1)/2,)), a**2*w**2/4)/(2*sqrt(pi)), w, t) == 1/(t*sqrt(a**2/t**2 + 1)) def test_hankel_transform(): from sympy import sinh, cosh, gamma, sqrt, exp r = Symbol("r") k = Symbol("k") nu = Symbol("nu") m = Symbol("m") a = symbols("a") assert hankel_transform(1/r, r, k, 0) == 1/k assert inverse_hankel_transform(1/k, k, r, 0) == 1/r assert hankel_transform( 1/r**m, r, k, 0) == 2**(-m + 1)*k**(m - 2)*gamma(-m/2 + 1)/gamma(m/2) assert inverse_hankel_transform( 2**(-m + 1)*k**(m - 2)*gamma(-m/2 + 1)/gamma(m/2), k, r, 0) == r**(-m) assert hankel_transform(1/r**m, r, k, nu) == ( 2*2**(-m)*k**(m - 2)*gamma(-m/2 + nu/2 + 1)/gamma(m/2 + nu/2)) assert inverse_hankel_transform(2**(-m + 1)*k**( m - 2)*gamma(-m/2 + nu/2 + 1)/gamma(m/2 + nu/2), k, r, nu) == r**(-m) assert hankel_transform(r**nu*exp(-a*r), r, k, nu) == \ 2**(nu + 1)*a*k**(-nu - 3)*(a**2/k**2 + 1)**(-nu - S( 3)/2)*gamma(nu + S(3)/2)/sqrt(pi) assert inverse_hankel_transform( 2**(nu + 1)*a*k**(-nu - 3)*(a**2/k**2 + 1)**(-nu - S(3)/2)*gamma( nu + S(3)/2)/sqrt(pi), k, r, nu) == r**nu*exp(-a*r) sympy-0.7.4.1/sympy/integrals/tests/test_meijerint.py0000644000175000017500000006473312253362407023173 0ustar georgeskgeorgeskfrom sympy import (meijerg, I, S, integrate, Integral, oo, gamma, hyperexpand, exp, simplify, sqrt, pi, erf, sin, cos, exp_polar, polar_lift, polygamma, hyper, log, expand_func) from sympy.integrals.meijerint import (_rewrite_single, _rewrite1, meijerint_indefinite, _inflate_g, _create_lookup_table, meijerint_definite, meijerint_inversion) from sympy.utilities import default_sort_key from sympy.utilities.randtest import (test_numerically, random_complex_number as randcplx) from sympy.abc import x, y, a, b, c, d, s, t, z def test_rewrite_single(): def t(expr, c, m): e = _rewrite_single(meijerg([a], [b], [c], [d], expr), x) assert e is not None assert isinstance(e[0][0][2], meijerg) assert e[0][0][2].argument.as_coeff_mul(x) == (c, (m,)) def tn(expr): assert _rewrite_single(meijerg([a], [b], [c], [d], expr), x) is None t(x, 1, x) t(x**2, 1, x**2) t(x**2 + y*x**2, y + 1, x**2) tn(x**2 + x) tn(x**y) def u(expr, x): from sympy import Add, exp, exp_polar r = _rewrite_single(expr, x) e = Add(*[res[0]*res[2] for res in r[0]]).replace( exp_polar, exp) # XXX Hack? assert test_numerically(e, expr, x) u(exp(-x)*sin(x), x) # The following has stopped working because hyperexpand changed slightly. # It is probably not worth fixing #u(exp(-x)*sin(x)*cos(x), x) # This one cannot be done numerically, since it comes out as a g-function # of argument 4*pi # NOTE This also tests a bug in inverse mellin transform (which used to # turn exp(4*pi*I*t) into a factor of exp(4*pi*I)**t instead of # exp_polar). #u(exp(x)*sin(x), x) assert _rewrite_single(exp(x)*sin(x), x) == \ ([(-sqrt(2)/(2*sqrt(pi)), 0, meijerg(((-S(1)/2, 0, S(1)/4, S(1)/2, S(3)/4), (1,)), ((), (-S(1)/2, 0)), 64*exp_polar(-4*I*pi)/x**4))], True) def test_rewrite1(): assert _rewrite1(x**3*meijerg([a], [b], [c], [d], x**2 + y*x**2)*5, x) == \ (5, x**3, [(1, 0, meijerg([a], [b], [c], [d], x**2*(y + 1)))], True) def test_meijerint_indefinite_numerically(): def t(fac, arg): g = meijerg([a], [b], [c], [d], arg)*fac subs = {a: randcplx()/10, b: randcplx()/10 + I, c: randcplx(), d: randcplx()} integral = meijerint_indefinite(g, x) assert integral is not None assert test_numerically(g.subs(subs), integral.diff(x).subs(subs), x) t(1, x) t(2, x) t(1, 2*x) t(1, x**2) t(5, x**S('3/2')) t(x**3, x) t(3*x**S('3/2'), 4*x**S('7/3')) def test_inflate(): subs = {a: randcplx()/10, b: randcplx()/10 + I, c: randcplx(), d: randcplx(), y: randcplx()/10} def t(a, b, arg, n): from sympy import Mul m1 = meijerg(a, b, arg) m2 = Mul(*_inflate_g(m1, n)) # NOTE: (the random number)**9 must still be on the principal sheet. # Thus make b&d small to create random numbers of small imaginary part. return test_numerically(m1.subs(subs), m2.subs(subs), x, b=0.1, d=-0.1) assert t([[a], [b]], [[c], [d]], x, 3) assert t([[a, y], [b]], [[c], [d]], x, 3) assert t([[a], [b]], [[c, y], [d]], 2*x**3, 3) def test_recursive(): from sympy import symbols, exp_polar, expand a, b, c = symbols('a b c', positive=True) r = exp(-(x - a)**2)*exp(-(x - b)**2) e = integrate(r, (x, 0, oo), meijerg=True) assert simplify(e.expand()) == ( sqrt(2)*sqrt(pi)*( (erf(sqrt(2)*(a + b)/2) + 1)*exp(-a**2/2 + a*b - b**2/2))/4) e = integrate(exp(-(x - a)**2)*exp(-(x - b)**2)*exp(c*x), (x, 0, oo), meijerg=True) assert simplify(e) == ( sqrt(2)*sqrt(pi)*(erf(sqrt(2)*(2*a + 2*b + c)/4) + 1)*exp(-a**2 - b**2 + (2*a + 2*b + c)**2/8)/4) assert simplify(integrate(exp(-(x - a - b - c)**2), (x, 0, oo), meijerg=True)) == \ sqrt(pi)/2*(1 + erf(a + b + c)) assert simplify(integrate(exp(-(x + a + b + c)**2), (x, 0, oo), meijerg=True)) == \ sqrt(pi)/2*(1 - erf(a + b + c)) def test_meijerint(): from sympy import symbols, expand, arg s, t, mu = symbols('s t mu', real=True) assert integrate(meijerg([], [], [0], [], s*t) *meijerg([], [], [mu/2], [-mu/2], t**2/4), (t, 0, oo)).is_Piecewise s = symbols('s', positive=True) assert integrate(x**s*meijerg([[], []], [[0], []], x), (x, 0, oo)) == \ gamma(s + 1) assert integrate(x**s*meijerg([[], []], [[0], []], x), (x, 0, oo), meijerg=True) == gamma(s + 1) assert isinstance(integrate(x**s*meijerg([[], []], [[0], []], x), (x, 0, oo), meijerg=False), Integral) assert meijerint_indefinite(exp(x), x) == exp(x) # TODO what simplifications should be done automatically? # This tests "extra case" for antecedents_1. a, b = symbols('a b', positive=True) assert simplify(meijerint_definite(x**a, x, 0, b)[0]) == \ b**(a + 1)/(a + 1) # This tests various conditions and expansions: meijerint_definite((x + 1)**3*exp(-x), x, 0, oo) == (16, True) # Again, how about simplifications? sigma, mu = symbols('sigma mu', positive=True) i, c = meijerint_definite(exp(-((x - mu)/(2*sigma))**2), x, 0, oo) assert simplify(i) == sqrt(pi)*sigma*(erf(mu/(2*sigma)) + 1) assert c == True i, _ = meijerint_definite(exp(-mu*x)*exp(sigma*x), x, 0, oo) # TODO it would be nice to test the condition assert simplify(i) == 1/(mu - sigma) # Test substitutions to change limits assert meijerint_definite(exp(x), x, -oo, 2) == (exp(2), True) assert expand(meijerint_definite(exp(x), x, 0, I)[0]) == exp(I) - 1 assert expand(meijerint_definite(exp(-x), x, 0, x)[0]) == \ 1 - exp(-exp(I*arg(x))*abs(x)) # Test -oo to oo assert meijerint_definite(exp(-x**2), x, -oo, oo) == (sqrt(pi), True) assert meijerint_definite(exp(-abs(x)), x, -oo, oo) == (2, True) assert meijerint_definite(exp(-(2*x - 3)**2), x, -oo, oo) == \ (sqrt(pi)/2, True) assert meijerint_definite(exp(-abs(2*x - 3)), x, -oo, oo) == (1, True) assert meijerint_definite(exp(-((x - mu)/sigma)**2/2)/sqrt(2*pi*sigma**2), x, -oo, oo) == (1, True) # Test one of the extra conditions for 2 g-functinos assert meijerint_definite(exp(-x)*sin(x), x, 0, oo) == (S(1)/2, True) # Test a bug def res(n): return (1/(1 + x**2)).diff(x, n).subs(x, 1)*(-1)**n for n in range(6): assert integrate(exp(-x)*sin(x)*x**n, (x, 0, oo), meijerg=True) == \ res(n) # This used to test trigexpand... now it is done by linear substitution assert simplify(integrate(exp(-x)*sin(x + a), (x, 0, oo), meijerg=True) ) == sqrt(2)*sin(a + pi/4)/2 # Test the condition 14 from prudnikov. # (This is besselj*besselj in disguise, to stop the product from being # recognised in the tables.) a, b, s = symbols('a b s') from sympy import And, re assert meijerint_definite(meijerg([], [], [a/2], [-a/2], x/4) *meijerg([], [], [b/2], [-b/2], x/4)*x**(s - 1), x, 0, oo) == \ (4*2**(2*s - 2)*gamma(-2*s + 1)*gamma(a/2 + b/2 + s) /(gamma(-a/2 + b/2 - s + 1)*gamma(a/2 - b/2 - s + 1) *gamma(a/2 + b/2 - s + 1)), And(0 < -2*re(4*s) + 8, 0 < re(a/2 + b/2 + s), re(2*s) < 1)) # test a bug assert integrate(sin(x**a)*sin(x**b), (x, 0, oo), meijerg=True) == \ Integral(sin(x**a)*sin(x**b), (x, 0, oo)) # test better hyperexpand assert integrate(exp(-x**2)*log(x), (x, 0, oo), meijerg=True) == \ (sqrt(pi)*polygamma(0, S(1)/2)/4).expand() # Test hyperexpand bug. from sympy import lowergamma n = symbols('n', integer=True) assert simplify(integrate(exp(-x)*x**n, x, meijerg=True)) == \ lowergamma(n + 1, x) # Test a bug with argument 1/x alpha = symbols('alpha', positive=True) assert meijerint_definite((2 - x)**alpha*sin(alpha/x), x, 0, 2) == \ (sqrt(pi)*alpha*gamma(alpha + 1)*meijerg(((), (alpha/2 + S(1)/2, alpha/2 + 1)), ((0, 0, S(1)/2), (-S(1)/2,)), alpha**S(2)/16)/4, True) # test a bug related to 3016 a, s = symbols('a s', positive=True) assert simplify(integrate(x**s*exp(-a*x**2), (x, -oo, oo))) == \ a**(-s/2 - S(1)/2)*((-1)**s + 1)*gamma(s/2 + S(1)/2)/2 def test_bessel(): from sympy import (besselj, Heaviside, besseli, polar_lift, exp_polar, powdenest) assert simplify(integrate(besselj(a, z)*besselj(b, z)/z, (z, 0, oo), meijerg=True, conds='none')) == \ 2*sin(pi*(a/2 - b/2))/(pi*(a - b)*(a + b)) assert simplify(integrate(besselj(a, z)*besselj(a, z)/z, (z, 0, oo), meijerg=True, conds='none')) == 1/(2*a) # TODO more orthogonality integrals assert simplify(integrate(sin(z*x)*(x**2 - 1)**(-(y + S(1)/2)), (x, 1, oo), meijerg=True, conds='none') *2/((z/2)**y*sqrt(pi)*gamma(S(1)/2 - y))) == \ besselj(y, z) # Werner Rosenheinrich # SOME INDEFINITE INTEGRALS OF BESSEL FUNCTIONS assert integrate(x*besselj(0, x), x, meijerg=True) == x*besselj(1, x) assert integrate(x*besseli(0, x), x, meijerg=True) == x*besseli(1, x) # TODO can do higher powers, but come out as high order ... should they be # reduced to order 0, 1? assert integrate(besselj(1, x), x, meijerg=True) == -besselj(0, x) assert integrate(besselj(1, x)**2/x, x, meijerg=True) == \ -(besselj(0, x)**2 + besselj(1, x)**2)/2 # TODO more besseli when tables are extended or recursive mellin works assert integrate(besselj(0, x)**2/x**2, x, meijerg=True) == \ -2*x*besselj(0, x)**2 - 2*x*besselj(1, x)**2 \ + 2*besselj(0, x)*besselj(1, x) - besselj(0, x)**2/x assert integrate(besselj(0, x)*besselj(1, x), x, meijerg=True) == \ -besselj(0, x)**2/2 assert integrate(x**2*besselj(0, x)*besselj(1, x), x, meijerg=True) == \ x**2*besselj(1, x)**2/2 assert integrate(besselj(0, x)*besselj(1, x)/x, x, meijerg=True) == \ (x*besselj(0, x)**2 + x*besselj(1, x)**2 - besselj(0, x)*besselj(1, x)) # TODO how does besselj(0, a*x)*besselj(0, b*x) work? # TODO how does besselj(0, x)**2*besselj(1, x)**2 work? # TODO sin(x)*besselj(0, x) etc come out a mess # TODO can x*log(x)*besselj(0, x) be done? # TODO how does besselj(1, x)*besselj(0, x+a) work? # TODO more indefinite integrals when struve functions etc are implemented # test a substitution assert integrate(besselj(1, x**2)*x, x, meijerg=True) == \ -besselj(0, x**2)/2 def test_inversion(): from sympy import piecewise_fold, besselj, sqrt, I, sin, cos, Heaviside def inv(f): return piecewise_fold(meijerint_inversion(f, s, t)) assert inv(1/(s**2 + 1)) == sin(t)*Heaviside(t) assert inv(s/(s**2 + 1)) == cos(t)*Heaviside(t) assert inv(exp(-s)/s) == Heaviside(t - 1) assert inv(1/sqrt(1 + s**2)) == besselj(0, t)*Heaviside(t) # Test some antcedents checking. assert meijerint_inversion(sqrt(s)/sqrt(1 + s**2), s, t) is None assert inv(exp(s**2)) is None assert meijerint_inversion(exp(-s**2), s, t) is None def test_lookup_table(): from random import uniform, randrange from sympy import Add, unpolarify, exp_polar, exp from sympy.integrals.meijerint import z as z_dummy table = {} _create_lookup_table(table) for _, l in sorted(table.items()): for formula, terms, cond, hint in sorted(l, key=default_sort_key): subs = {} for a in list(formula.free_symbols) + [z_dummy]: if hasattr(a, 'properties') and a.properties: # these Wilds match positive integers subs[a] = randrange(1, 10) else: subs[a] = uniform(1.5, 2.0) if not isinstance(terms, list): terms = terms(subs) # First test that hyperexpand can do this. expanded = [hyperexpand(g) for (_, g) in terms] assert all(x.is_Piecewise or not x.has(meijerg) for x in expanded) # Now test that the meijer g-function is indeed as advertised. expanded = Add(*[f*x for (f, x) in terms]) a, b = formula.n(subs=subs), expanded.n(subs=subs) r = min(abs(a), abs(b)) if r < 1: assert abs(a - b).n() <= 1e-10 else: assert (abs(a - b)/r).n() <= 1e-10 def test_branch_bug(): from sympy import powdenest, lowergamma # TODO combsimp cannot prove that the factor is unity assert powdenest(integrate(erf(x**3), x, meijerg=True).diff(x), polar=True) == 2*erf(x**3)*gamma(S(2)/3)/3/gamma(S(5)/3) assert integrate(erf(x**3), x, meijerg=True) == \ 2*x*erf(x**3)*gamma(S(2)/3)/(3*gamma(S(5)/3)) \ - 2*gamma(S(2)/3)*lowergamma(S(2)/3, x**6)/(3*sqrt(pi)*gamma(S(5)/3)) def test_linear_subs(): from sympy import besselj assert integrate(sin(x - 1), x, meijerg=True) == -cos(1 - x) assert integrate(besselj(1, x - 1), x, meijerg=True) == -besselj(0, 1 - x) def test_probability(): # various integrals from probability theory from sympy.abc import x, y, z from sympy import symbols, Symbol, Abs, expand_mul, combsimp, powsimp, sin mu1, mu2 = symbols('mu1 mu2', real=True, finite=True, bounded=True) sigma1, sigma2 = symbols('sigma1 sigma2', real=True, finite=True, bounded=True, positive=True) rate = Symbol('lambda', real=True, positive=True, bounded=True) def normal(x, mu, sigma): return 1/sqrt(2*pi*sigma**2)*exp(-(x - mu)**2/2/sigma**2) def exponential(x, rate): return rate*exp(-rate*x) assert integrate(normal(x, mu1, sigma1), (x, -oo, oo), meijerg=True) == 1 assert integrate(x*normal(x, mu1, sigma1), (x, -oo, oo), meijerg=True) == \ mu1 assert integrate(x**2*normal(x, mu1, sigma1), (x, -oo, oo), meijerg=True) \ == mu1**2 + sigma1**2 assert integrate(x**3*normal(x, mu1, sigma1), (x, -oo, oo), meijerg=True) \ == mu1**3 + 3*mu1*sigma1**2 assert integrate(normal(x, mu1, sigma1)*normal(y, mu2, sigma2), (x, -oo, oo), (y, -oo, oo), meijerg=True) == 1 assert integrate(x*normal(x, mu1, sigma1)*normal(y, mu2, sigma2), (x, -oo, oo), (y, -oo, oo), meijerg=True) == mu1 assert integrate(y*normal(x, mu1, sigma1)*normal(y, mu2, sigma2), (x, -oo, oo), (y, -oo, oo), meijerg=True) == mu2 assert integrate(x*y*normal(x, mu1, sigma1)*normal(y, mu2, sigma2), (x, -oo, oo), (y, -oo, oo), meijerg=True) == mu1*mu2 assert integrate((x + y + 1)*normal(x, mu1, sigma1)*normal(y, mu2, sigma2), (x, -oo, oo), (y, -oo, oo), meijerg=True) == 1 + mu1 + mu2 assert integrate((x + y - 1)*normal(x, mu1, sigma1)*normal(y, mu2, sigma2), (x, -oo, oo), (y, -oo, oo), meijerg=True) == \ -1 + mu1 + mu2 i = integrate(x**2*normal(x, mu1, sigma1)*normal(y, mu2, sigma2), (x, -oo, oo), (y, -oo, oo), meijerg=True) assert not i.has(Abs) assert simplify(i) == mu1**2 + sigma1**2 assert integrate(y**2*normal(x, mu1, sigma1)*normal(y, mu2, sigma2), (x, -oo, oo), (y, -oo, oo), meijerg=True) == \ sigma2**2 + mu2**2 assert integrate(exponential(x, rate), (x, 0, oo), meijerg=True) == 1 assert integrate(x*exponential(x, rate), (x, 0, oo), meijerg=True) == \ 1/rate assert integrate(x**2*exponential(x, rate), (x, 0, oo), meijerg=True) == \ 2/rate**2 def E(expr): res1 = integrate(expr*exponential(x, rate)*normal(y, mu1, sigma1), (x, 0, oo), (y, -oo, oo), meijerg=True) res2 = integrate(expr*exponential(x, rate)*normal(y, mu1, sigma1), (y, -oo, oo), (x, 0, oo), meijerg=True) assert expand_mul(res1) == expand_mul(res2) return res1 assert E(1) == 1 assert E(x*y) == mu1/rate assert E(x*y**2) == mu1**2/rate + sigma1**2/rate ans = sigma1**2 + 1/rate**2 assert simplify(E((x + y + 1)**2) - E(x + y + 1)**2) == ans assert simplify(E((x + y - 1)**2) - E(x + y - 1)**2) == ans assert simplify(E((x + y)**2) - E(x + y)**2) == ans # Beta' distribution alpha, beta = symbols('alpha beta', positive=True) betadist = x**(alpha - 1)*(1 + x)**(-alpha - beta)*gamma(alpha + beta) \ /gamma(alpha)/gamma(beta) assert integrate(betadist, (x, 0, oo), meijerg=True) == 1 i = integrate(x*betadist, (x, 0, oo), meijerg=True, conds='separate') assert (combsimp(i[0]), i[1]) == (alpha/(beta - 1), 1 < beta) j = integrate(x**2*betadist, (x, 0, oo), meijerg=True, conds='separate') assert j[1] == (1 < beta - 1) assert combsimp(j[0] - i[0]**2) == (alpha + beta - 1)*alpha \ /(beta - 2)/(beta - 1)**2 # Beta distribution # NOTE: this is evaluated using antiderivatives. It also tests that # meijerint_indefinite returns the simplest possible answer. a, b = symbols('a b', positive=True) betadist = x**(a - 1)*(-x + 1)**(b - 1)*gamma(a + b)/(gamma(a)*gamma(b)) assert simplify(integrate(betadist, (x, 0, 1), meijerg=True)) == 1 assert simplify(integrate(x*betadist, (x, 0, 1), meijerg=True)) == \ a/(a + b) assert simplify(integrate(x**2*betadist, (x, 0, 1), meijerg=True)) == \ a*(a + 1)/(a + b)/(a + b + 1) assert simplify(integrate(x**y*betadist, (x, 0, 1), meijerg=True)) == \ gamma(a + b)*gamma(a + y)/gamma(a)/gamma(a + b + y) # Chi distribution k = Symbol('k', integer=True, positive=True) chi = 2**(1 - k/2)*x**(k - 1)*exp(-x**2/2)/gamma(k/2) assert powsimp(integrate(chi, (x, 0, oo), meijerg=True)) == 1 assert simplify(integrate(x*chi, (x, 0, oo), meijerg=True)) == \ sqrt(2)*gamma((k + 1)/2)/gamma(k/2) assert simplify(integrate(x**2*chi, (x, 0, oo), meijerg=True)) == k # Chi^2 distribution chisquared = 2**(-k/2)/gamma(k/2)*x**(k/2 - 1)*exp(-x/2) assert powsimp(integrate(chisquared, (x, 0, oo), meijerg=True)) == 1 assert simplify(integrate(x*chisquared, (x, 0, oo), meijerg=True)) == k assert simplify(integrate(x**2*chisquared, (x, 0, oo), meijerg=True)) == \ k*(k + 2) assert combsimp(integrate(((x - k)/sqrt(2*k))**3*chisquared, (x, 0, oo), meijerg=True)) == 2*sqrt(2)/sqrt(k) # Dagum distribution a, b, p = symbols('a b p', positive=True) # XXX (x/b)**a does not work dagum = a*p/x*(x/b)**(a*p)/(1 + x**a/b**a)**(p + 1) assert simplify(integrate(dagum, (x, 0, oo), meijerg=True)) == 1 # XXX conditions are a mess arg = x*dagum assert simplify(integrate(arg, (x, 0, oo), meijerg=True, conds='none') ) == a*b*gamma(1 - 1/a)*gamma(p + 1 + 1/a)/( (a*p + 1)*gamma(p)) assert simplify(integrate(x*arg, (x, 0, oo), meijerg=True, conds='none') ) == a*b**2*gamma(1 - 2/a)*gamma(p + 1 + 2/a)/( (a*p + 2)*gamma(p)) # F-distribution d1, d2 = symbols('d1 d2', positive=True) f = sqrt(((d1*x)**d1 * d2**d2)/(d1*x + d2)**(d1 + d2))/x \ /gamma(d1/2)/gamma(d2/2)*gamma((d1 + d2)/2) assert simplify(integrate(f, (x, 0, oo), meijerg=True)) == 1 # TODO conditions are a mess assert simplify(integrate(x*f, (x, 0, oo), meijerg=True, conds='none') ) == d2/(d2 - 2) assert simplify(integrate(x**2*f, (x, 0, oo), meijerg=True, conds='none') ) == d2**2*(d1 + 2)/d1/(d2 - 4)/(d2 - 2) # TODO gamma, rayleigh # inverse gaussian lamda, mu = symbols('lamda mu', positive=True) dist = sqrt(lamda/2/pi)*x**(-S(3)/2)*exp(-lamda*(x - mu)**2/x/2/mu**2) mysimp = lambda expr: simplify(expr.rewrite(exp)) assert mysimp(integrate(dist, (x, 0, oo))) == 1 assert mysimp(integrate(x*dist, (x, 0, oo))) == mu assert mysimp(integrate((x - mu)**2*dist, (x, 0, oo))) == mu**3/lamda assert mysimp(integrate((x - mu)**3*dist, (x, 0, oo))) == 3*mu**5/lamda**2 # Levi c = Symbol('c', positive=True) assert integrate(sqrt(c/2/pi)*exp(-c/2/(x - mu))/(x - mu)**S('3/2'), (x, mu, oo)) == 1 # higher moments oo # log-logistic distn = (beta/alpha)*x**(beta - 1)/alpha**(beta - 1)/ \ (1 + x**beta/alpha**beta)**2 assert simplify(integrate(distn, (x, 0, oo))) == 1 # NOTE the conditions are a mess, but correctly state beta > 1 assert simplify(integrate(x*distn, (x, 0, oo), conds='none')) == \ pi*alpha/beta/sin(pi/beta) # (similar comment for conditions applies) assert simplify(integrate(x**y*distn, (x, 0, oo), conds='none')) == \ pi*alpha**y*y/beta/sin(pi*y/beta) # weibull k = Symbol('k', positive=True) n = Symbol('n', positive=True) distn = k/lamda*(x/lamda)**(k - 1)*exp(-(x/lamda)**k) assert simplify(integrate(distn, (x, 0, oo))) == 1 assert simplify(integrate(x**n*distn, (x, 0, oo))) == \ lamda**n*gamma(1 + n/k) # rice distribution from sympy import besseli nu, sigma = symbols('nu sigma', positive=True) rice = x/sigma**2*exp(-(x**2 + nu**2)/2/sigma**2)*besseli(0, x*nu/sigma**2) assert integrate(rice, (x, 0, oo), meijerg=True) == 1 # can someone verify higher moments? # Laplace distribution mu = Symbol('mu', real=True) b = Symbol('b', positive=True) laplace = exp(-abs(x - mu)/b)/2/b assert integrate(laplace, (x, -oo, oo), meijerg=True) == 1 assert integrate(x*laplace, (x, -oo, oo), meijerg=True) == mu assert integrate(x**2*laplace, (x, -oo, oo), meijerg=True) == \ 2*b**2 + mu**2 # TODO are there other distributions supported on (-oo, oo) that we can do? # misc tests k = Symbol('k', positive=True) assert combsimp(expand_mul(integrate(log(x)*x**(k - 1)*exp(-x)/gamma(k), (x, 0, oo)))) == polygamma(0, k) def test_expint(): """ Test various exponential integrals. """ from sympy import (expint, unpolarify, Symbol, Ci, Si, Shi, Chi, sin, cos, sinh, cosh, Ei) assert simplify(unpolarify(integrate(exp(-z*x)/x**y, (x, 1, oo), meijerg=True, conds='none' ).rewrite(expint).expand(func=True))) == expint(y, z) assert integrate(exp(-z*x)/x, (x, 1, oo), meijerg=True, conds='none').rewrite(expint).expand() == \ expint(1, z) assert integrate(exp(-z*x)/x**2, (x, 1, oo), meijerg=True, conds='none').rewrite(expint).expand() == \ expint(2, z).rewrite(Ei).rewrite(expint) assert integrate(exp(-z*x)/x**3, (x, 1, oo), meijerg=True, conds='none').rewrite(expint).expand() == \ expint(3, z).rewrite(Ei).rewrite(expint).expand() t = Symbol('t', positive=True) assert integrate(-cos(x)/x, (x, t, oo), meijerg=True).expand() == Ci(t) assert integrate(-sin(x)/x, (x, t, oo), meijerg=True).expand() == \ Si(t) - pi/2 assert integrate(sin(x)/x, (x, 0, z), meijerg=True) == Si(z) assert integrate(sinh(x)/x, (x, 0, z), meijerg=True) == Shi(z) assert integrate(exp(-x)/x, x, meijerg=True).expand().rewrite(expint) == \ I*pi - expint(1, x) assert integrate(exp(-x)/x**2, x, meijerg=True).rewrite(expint).expand() \ == expint(1, x) - exp(-x)/x - I*pi u = Symbol('u', polar=True) assert integrate(cos(u)/u, u, meijerg=True).expand().as_independent(u)[1] \ == Ci(u) assert integrate(cosh(u)/u, u, meijerg=True).expand().as_independent(u)[1] \ == Chi(u) assert integrate(expint(1, x), x, meijerg=True ).rewrite(expint).expand() == x*expint(1, x) - exp(-x) assert integrate(expint(2, x), x, meijerg=True ).rewrite(expint).expand() == \ -x**2*expint(1, x)/2 + x*exp(-x)/2 - exp(-x)/2 assert simplify(unpolarify(integrate(expint(y, x), x, meijerg=True).rewrite(expint).expand(func=True))) == \ -expint(y + 1, x) assert integrate(Si(x), x, meijerg=True) == x*Si(x) + cos(x) assert integrate(Ci(u), u, meijerg=True).expand() == u*Ci(u) - sin(u) assert integrate(Shi(x), x, meijerg=True) == x*Shi(x) - cosh(x) assert integrate(Chi(u), u, meijerg=True).expand() == u*Chi(u) - sinh(u) assert integrate(Si(x)*exp(-x), (x, 0, oo), meijerg=True) == pi/4 assert integrate(expint(1, x)*sin(x), (x, 0, oo), meijerg=True) == log(2)/2 def test_messy(): from sympy import (laplace_transform, Si, Ci, Shi, Chi, atan, Piecewise, atanh, acoth, E1, besselj, acosh, asin, Ne, And, re, fourier_transform, sqrt, Abs) assert laplace_transform(Si(x), x, s) == ((-atan(s) + pi/2)/s, 0, True) assert laplace_transform(Shi(x), x, s) == (acoth(s)/s, 1, True) # where should the logs be simplified? assert laplace_transform(Chi(x), x, s) == \ ((log(s**(-2)) - log((s**2 - 1)/s**2))/(2*s), 1, True) # TODO maybe simplify the inequalities? assert laplace_transform(besselj(a, x), x, s)[1:] == \ (0, And(S(0) < re(a/2) + S(1)/2, S(0) < re(a/2) + 1)) # NOTE s < 0 can be done, but argument reduction is not good enough yet assert fourier_transform(besselj(1, x)/x, x, s, noconds=False) == \ (Piecewise((0, 1 < 4*abs(pi**2*s**2)), (2*sqrt(-4*pi**2*s**2 + 1), True)), 0 < s) # TODO FT(besselj(0,x)) - conditions are messy (but for acceptable reasons) # - folding could be better assert integrate(E1(x)*besselj(0, x), (x, 0, oo), meijerg=True) == \ log(1 + sqrt(2)) assert integrate(E1(x)*besselj(1, x), (x, 0, oo), meijerg=True) == \ log(S(1)/2 + sqrt(2)/2) assert integrate(1/x/sqrt(1 - x**2), x, meijerg=True) == \ Piecewise((-acosh(1/x), 1 < abs(x**(-2))), (I*asin(1/x), True)) def test_3023(): assert integrate(exp(-I*x**2), (x, -oo, oo), meijerg=True) == \ -I*sqrt(pi)*exp(I*pi/4) def test_3153(): expr = 1/x/(a + b*x)**(S(1)/3) anti = integrate(expr, x, meijerg=True) assert not expr.has(hyper) # XXX the expression is a mess, but actually upon differentiation and # putting in numerical values seems to work... def test_3249(): assert integrate(exp(I*x)/(1 + x**2), (x, -oo, oo)).simplify().rewrite(exp) \ == pi*exp(-1) def test_fresnel(): from sympy import fresnels, fresnelc assert expand_func(integrate(sin(pi*x**2/2), x)) == fresnels(x) assert expand_func(integrate(cos(pi*x**2/2), x)) == fresnelc(x) def test_3761(): assert meijerint_indefinite(x**x**x, x) is None sympy-0.7.4.1/sympy/integrals/tests/test_manual.py0000644000175000017500000001507412253362407022454 0ustar georgeskgeorgeskfrom sympy import (sin, cos, tan, sec, csc, cot, log, exp, atan, Symbol, Mul, Integral, integrate, pi, Dummy, Derivative, diff, I, sqrt, erf, Piecewise, Eq, Ne, Q, assuming, symbols, And) from sympy.integrals.manualintegrate import manualintegrate, find_substitutions, \ integral_steps, _parts_rule x, y, u, n, a, b = symbols('x y u n a b') def test_find_substitutions(): assert find_substitutions((cot(x)**2 + 1)**2*csc(x)**2*cot(x)**2, x, u) == \ [(cot(x), 1, -u**6 - 2*u**4 - u**2)] assert find_substitutions((sec(x)**2 + tan(x) * sec(x)) / (sec(x) + tan(x)), x, u) == [(sec(x) + tan(x), 1, 1/u)] def test_manualintegrate_polynomials(): assert manualintegrate(y, x) == x*y assert manualintegrate(exp(2), x) == x * exp(2) assert manualintegrate(x**2, x) == x**3 / 3 assert manualintegrate(3 * x**2 + 4 * x**3, x) == x**3 + x**4 assert manualintegrate((x + 2)**3, x) == (x + 2)**4 / 4 assert manualintegrate((3*x + 4)**2, x) == (3*x + 4)**3 / 9 assert manualintegrate((u + 2)**3, u) == (u + 2)**4 / 4 assert manualintegrate((3*u + 4)**2, u) == (3*u + 4)**3 / 9 def test_manualintegrate_exponentials(): assert manualintegrate(exp(2*x), x) == exp(2*x) / 2 assert manualintegrate(2**x, x) == (2 ** x) / log(2) assert manualintegrate(1 / x, x) == log(x) assert manualintegrate(1 / (2*x + 3), x) == log(2*x + 3) / 2 assert manualintegrate(log(x)**2 / x, x) == log(x)**3 / 3 def test_manualintegrate_parts(): assert manualintegrate(exp(x) * sin(x), x) == \ (exp(x) * sin(x)) / 2 - (exp(x) * cos(x)) / 2 assert manualintegrate(2*x*cos(x), x) == 2*x*sin(x) + 2*cos(x) assert manualintegrate(x * log(x), x) == x**2*log(x)/2 - x**2/4 assert manualintegrate(log(x), x) == x * log(x) - x assert manualintegrate((3*x**2 + 5) * exp(x), x) == \ -6*x*exp(x) + (3*x**2 + 5)*exp(x) + 6*exp(x) assert manualintegrate(atan(x), x) == x*atan(x) - log(x**2 + 1)/2 # Make sure _parts_rule doesn't pick u = constant but can pick dv = # constant if necessary, e.g. for integrate(atan(x)) assert _parts_rule(cos(x), x) == None assert _parts_rule(exp(x), x) == None assert _parts_rule(x**2, x) == None result = _parts_rule(atan(x), x) assert result[0] == atan(x) and result[1] == 1 def test_manualintegrate_trigonometry(): assert manualintegrate(sin(x), x) == -cos(x) assert manualintegrate(tan(x), x) == -log(cos(x)) assert manualintegrate(sec(x), x) == log(sec(x) + tan(x)) assert manualintegrate(csc(x), x) == -log(csc(x) + cot(x)) assert manualintegrate(sin(x) * cos(x), x) in [sin(x) ** 2 / 2, -cos(x)**2 / 2] assert manualintegrate(-sec(x) * tan(x), x) == -sec(x) assert manualintegrate(csc(x) * cot(x), x) == -csc(x) def test_manualintegrate_trigpowers(): assert manualintegrate(sin(x)**2 * cos(x), x) == sin(x)**3 / 3 assert manualintegrate(sin(x)**2 * cos(x) **2, x) == \ x / 8 - sin(4*x) / 32 assert manualintegrate(sin(x) * cos(x)**3, x) == -cos(x)**4 / 4 assert manualintegrate(sin(x)**3 * cos(x)**2, x) == \ cos(x)**5 / 5 - cos(x)**3 / 3 assert manualintegrate(tan(x)**3 * sec(x), x) == sec(x)**3/3 - sec(x) assert manualintegrate(tan(x) * sec(x) **2, x) == sec(x)**2/2 assert manualintegrate(cot(x)**5 * csc(x), x) == \ -csc(x)**5/5 + 2*csc(x)**3/3 - csc(x) assert manualintegrate(cot(x)**2 * csc(x)**6, x) == \ -cot(x)**7/7 - 2*cot(x)**5/5 - cot(x)**3/3 def test_manualintegrate_inversetrig(): assert manualintegrate(exp(x) / (1 + exp(2*x)), x) == atan(exp(x)) assert manualintegrate(1 / (4 + 9 * x**2), x) == atan(3 * x/2) / 6 assert manualintegrate(1 / (16 + 16 * x**2), x) == atan(x) / 16 assert manualintegrate(1 / (4 + x**2), x) == atan(x / 2) / 2 assert manualintegrate(1 / (1 + 4 * x**2), x) == atan(2*x) / 2 assert manualintegrate(1/(a + b*x**2), x) == \ Piecewise((atan(x*sqrt(b/a))/(a*sqrt(b/a)), And(a > 0, b > 0))) assert manualintegrate(1/(4 + b*x**2), x) == \ Piecewise((atan(sqrt(b)*x/2)/(2*sqrt(b)), b > 0)) assert manualintegrate(1/(a + 4*x**2), x) == \ Piecewise((atan(2*x*sqrt(1/a))/(2*a*sqrt(1/a)), a > 0)) assert manualintegrate(1/(4 + 4*x**2), x) == atan(x) / 4 def test_manualintegrate_rational(): assert manualintegrate(1/(4 - x**2), x) == -log(x - 2)/4 + log(x + 2)/4 assert manualintegrate(1/(-1 + x**2), x) == log(x - 1)/2 - log(x + 1)/2 def test_manualintegrate_derivative(): assert manualintegrate(pi * Derivative(x**2 + 2*x + 3), x) == \ pi * ((x**2 + 2*x + 3)) assert manualintegrate(Derivative(x**2 + 2*x + 3, y), x) == \ x * Derivative(x**2 + 2*x + 3, y) assert manualintegrate(Derivative(sin(x), x, x, y, x), x) == \ Derivative(sin(x), x, x, y) def test_issue_3700(): r, x, phi = map(Symbol, 'r x phi'.split()) n = Symbol('n', integer=True, positive=True) integrand = (cos(n*(x-phi))*cos(n*x)) limits = (x, -pi, pi) assert manualintegrate(integrand, x).has(Integral) assert r * integrate(integrand.expand(trig=True), limits) / pi == r * cos(n * phi) assert not integrate(integrand, limits).has(Dummy) def test_issue_3796(): assert manualintegrate(diff(exp(x + x**2)), x) == exp(x + x**2) assert integrate(x * exp(x**4), x, risch=False) == -I*sqrt(pi)*erf(I*x**2)/4 def test_manual_true(): assert integrate(exp(x) * sin(x), x, manual=True) == \ (exp(x) * sin(x)) / 2 - (exp(x) * cos(x)) / 2 assert integrate(sin(x) * cos(x), x, manual=True) in \ [sin(x) ** 2 / 2, -cos(x)**2 / 2] def test_issue_3647(): assert manualintegrate(y**x, x) == \ Piecewise((x, Eq(log(y), 0)), (y**x/log(y), True)) assert manualintegrate(y**(n*x), x) == \ Piecewise( (x, Eq(n, 0)), (Piecewise( (n*x, Eq(log(y), 0)), (y**(n*x)/log(y), True))/n, True)) assert manualintegrate(exp(n*x), x) == \ Piecewise((x, Eq(n, 0)), (exp(n*x)/n, True)) with assuming(~Q.zero(log(y))): assert manualintegrate(y**x, x) == y**x/log(y) with assuming(Q.zero(log(y))): assert manualintegrate(y**x, x) == x with assuming(~Q.zero(n)): assert manualintegrate(y**(n*x), x) == \ Piecewise((n*x, Eq(log(y), 0)), (y**(n*x)/log(y), True))/n with assuming(~Q.zero(n) & ~Q.zero(log(y))): assert manualintegrate(y**(n*x), x) == \ y**(n*x)/(n*log(y)) with assuming(Q.negative(a)): assert manualintegrate(1 / (a + b*x**2), x) == \ Integral(1/(a + b*x**2), x) sympy-0.7.4.1/sympy/integrals/tests/test_risch.py0000644000175000017500000010070112253362407022277 0ustar georgeskgeorgesk"""Most of these tests come from the examples in Bronstein's book.""" from sympy import (Poly, I, S, Function, log, symbols, exp, tan, sqrt, Symbol, Lambda, sin, cos, Eq, Piecewise, factor) from sympy.integrals.risch import (gcdex_diophantine, frac_in, as_poly_1t, derivation, splitfactor, splitfactor_sqf, canonical_representation, hermite_reduce, polynomial_reduce, residue_reduce, residue_reduce_to_basic, integrate_primitive, integrate_hyperexponential_polynomial, integrate_hyperexponential, integrate_hypertangent_polynomial, integrate_nonlinear_no_specials, integer_powers, DifferentialExtension, risch_integrate, DecrementLevel, NonElementaryIntegral, recognize_log_derivative, recognize_derivative, laurent_series) from sympy.utilities.pytest import raises from sympy.abc import x, t, nu, z, a, y t0, t1, t2 = symbols('t:3') i = Symbol('i') def test_gcdex_diophantine(): assert gcdex_diophantine(Poly(x**4 - 2*x**3 - 6*x**2 + 12*x + 15), Poly(x**3 + x**2 - 4*x - 4), Poly(x**2 - 1)) == \ (Poly((-x**2 + 4*x - 3)/5), Poly((x**3 - 7*x**2 + 16*x - 10)/5)) def test_frac_in(): assert frac_in(Poly((x + 1)/x*t, t), x) == \ (Poly(t*x + t, x), Poly(x, x)) assert frac_in((x + 1)/x*t, x) == \ (Poly(t*x + t, x), Poly(x, x)) assert frac_in((Poly((x + 1)/x*t, t), Poly(t + 1, t)), x) == \ (Poly(t*x + t, x), Poly((1 + t)*x, x)) raises(ValueError, lambda: frac_in((x + 1)/log(x)*t, x)) assert frac_in(Poly((2 + 2*x + x*(1 + x))/(1 + x)**2, t), x, cancel=True) == \ (Poly(x + 2, x), Poly(x + 1, x)) def test_as_poly_1t(): assert as_poly_1t(2/t + t, t, z) in [ Poly(t + 2*z, t, z), Poly(t + 2*z, z, t)] assert as_poly_1t(2/t + 3/t**2, t, z) in [ Poly(2*z + 3*z**2, t, z), Poly(2*z + 3*z**2, z, t)] assert as_poly_1t(2/((exp(2) + 1)*t), t, z) in [ Poly(2/(exp(2) + 1)*z, t, z), Poly(2/(exp(2) + 1)*z, z, t)] assert as_poly_1t(2/((exp(2) + 1)*t) + t, t, z) in [ Poly(t + 2/(exp(2) + 1)*z, t, z), Poly(t + 2/(exp(2) + 1)*z, z, t)] assert as_poly_1t(S(0), t, z) == Poly(0, t, z) def test_derivation(): p = Poly(4*x**4*t**5 + (-4*x**3 - 4*x**4)*t**4 + (-3*x**2 + 2*x**3)*t**3 + (2*x + 7*x**2 + 2*x**3)*t**2 + (1 - 4*x - 4*x**2)*t - 1 + 2*x, t) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(-t**2 - 3/(2*x)*t + 1/(2*x), t)]}) assert derivation(p, DE) == Poly(-20*x**4*t**6 + (2*x**3 + 16*x**4)*t**5 + (21*x**2 + 12*x**3)*t**4 + (7*x/2 - 25*x**2 - 12*x**3)*t**3 + (-5 - 15*x/2 + 7*x**2)*t**2 - (3 - 8*x - 10*x**2 - 4*x**3)/(2*x)*t + (1 - 4*x**2)/(2*x), t) assert derivation(Poly(1, t), DE) == Poly(0, t) assert derivation(Poly(t, t), DE) == DE.d assert derivation(Poly(t**2 + 1/x*t + (1 - 2*x)/(4*x**2), t), DE) == \ Poly(-2*t**3 - 4/x*t**2 - (5 - 2*x)/(2*x**2)*t - (1 - 2*x)/(2*x**3), t, domain='ZZ(x)') DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1/x, t1), Poly(t, t)]}) assert derivation(Poly(x*t*t1, t), DE) == Poly(t*t1 + x*t*t1 + t, t) assert derivation(Poly(x*t*t1, t), DE, coefficientD=True) == \ Poly((1 + t1)*t, t) DE = DifferentialExtension(extension={'D': [Poly(1, x)]}) assert derivation(Poly(x, x), DE) == Poly(1, x) # Test basic option assert derivation((x + 1)/(x - 1), DE, basic=True) == -2/(1 - 2*x + x**2) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t, t)]}) assert derivation((t + 1)/(t - 1), DE, basic=True) == -2*t/(1 - 2*t + t**2) assert derivation(t + 1, DE, basic=True) == t def test_splitfactor(): p = Poly(4*x**4*t**5 + (-4*x**3 - 4*x**4)*t**4 + (-3*x**2 + 2*x**3)*t**3 + (2*x + 7*x**2 + 2*x**3)*t**2 + (1 - 4*x - 4*x**2)*t - 1 + 2*x, t, field=True) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(-t**2 - 3/(2*x)*t + 1/(2*x), t)]}) assert splitfactor(p, DE) == (Poly(4*x**4*t**3 + (-8*x**3 - 4*x**4)*t**2 + (4*x**2 + 8*x**3)*t - 4*x**2, t), Poly(t**2 + 1/x*t + (1 - 2*x)/(4*x**2), t, domain='ZZ(x)')) assert splitfactor(Poly(x, t), DE) == (Poly(x, t), Poly(1, t)) r = Poly(-4*x**4*z**2 + 4*x**6*z**2 - z*x**3 - 4*x**5*z**3 + 4*x**3*z**3 + x**4 + z*x**5 - x**6, t) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1/x, t)]}) assert splitfactor(r, DE, coefficientD=True) == \ (Poly(x*z - x**2 - z*x**3 + x**4, t), Poly(-x**2 + 4*x**2*z**2, t)) assert splitfactor_sqf(r, DE, coefficientD=True) == \ (((Poly(x*z - x**2 - z*x**3 + x**4, t), 1),), ((Poly(-x**2 + 4*x**2*z**2, t), 1),)) assert splitfactor(Poly(0, t), DE) == (Poly(0, t), Poly(1, t)) assert splitfactor_sqf(Poly(0, t), DE) == (((Poly(0, t), 1),), ()) def test_canonical_representation(): DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1 + t**2, t)]}) assert canonical_representation(Poly(x - t, t), Poly(t**2, t), DE) == \ (Poly(0, t), (Poly(0, t), Poly(1, t)), (Poly(-t + x, t), Poly(t**2, t))) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t**2 + 1, t)]}) assert canonical_representation(Poly(t**5 + t**3 + x**2*t + 1, t), Poly((t**2 + 1)**3, t), DE) == \ (Poly(0, t), (Poly(t**5 + t**3 + x**2*t + 1, t), Poly(t**6 + 3*t**4 + 3*t**2 + 1, t)), (Poly(0, t), Poly(1, t))) def test_hermite_reduce(): DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t**2 + 1, t)]}) assert hermite_reduce(Poly(x - t, t), Poly(t**2, t), DE) == \ ((Poly(-x, t), Poly(t, t)), (Poly(0, t), Poly(1, t)), (Poly(-x, t), Poly(1, t))) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(-t**2 - t/x - (1 - nu**2/x**2), t)]}) assert hermite_reduce( Poly(x**2*t**5 + x*t**4 - nu**2*t**3 - x*(x**2 + 1)*t**2 - (x**2 - nu**2)*t - x**5/4, t), Poly(x**2*t**4 + x**2*(x**2 + 2)*t**2 + x**2 + x**4 + x**6/4, t), DE) == \ ((Poly(-x**2 - 4, t), Poly(4*t**2 + 2*x**2 + 4, t)), (Poly((-2*nu**2 - x**4)*t - (2*x**3 + 2*x), t), Poly(2*x**2*t**2 + x**4 + 2*x**2, t)), (Poly(x*t + 1, t), Poly(x, t))) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1/x, t)]}) a = Poly((-2 + 3*x)*t**3 + (-1 + x)*t**2 + (-4*x + 2*x**2)*t + x**2, t) d = Poly(x*t**6 - 4*x**2*t**5 + 6*x**3*t**4 - 4*x**4*t**3 + x**5*t**2, t) assert hermite_reduce(a, d, DE) == \ ((Poly(3*t**2 + t + 3*x, t), Poly(3*t**4 - 9*x*t**3 + 9*x**2*t**2 - 3*x**3*t, t)), (Poly(0, t), Poly(1, t)), (Poly(0, t), Poly(1, t))) assert hermite_reduce( Poly(-t**2 + 2*t + 2, t), Poly(-x*t**2 + 2*x*t - x, t), DE) == \ ((Poly(3, t), Poly(t - 1, t)), (Poly(0, t), Poly(1, t)), (Poly(1, t), Poly(x, t))) assert hermite_reduce( Poly(-x**2*t**6 + (-1 - 2*x**3 + x**4)*t**3 + (-3 - 3*x**4)*t**2 - 2*x*t - x - 3*x**2, t), Poly(x**4*t**6 - 2*x**2*t**3 + 1, t), DE) == \ ((Poly(x**3*t + x**4 + 1, t), Poly(x**3*t**3 - x, t)), (Poly(0, t), Poly(1, t)), (Poly(-1, t), Poly(x**2, t))) assert hermite_reduce( Poly((-2 + 3*x)*t**3 + (-1 + x)*t**2 + (-4*x + 2*x**2)*t + x**2, t), Poly(x*t**6 - 4*x**2*t**5 + 6*x**3*t**4 - 4*x**4*t**3 + x**5*t**2, t), DE) == \ ((Poly(3*t**2 + t + 3*x, t), Poly(3*t**4 - 9*x*t**3 + 9*x**2*t**2 - 3*x**3*t, t)), (Poly(0, t), Poly(1, t)), (Poly(0, t), Poly(1, t))) def test_polynomial_reduce(): DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1 + t**2, t)]}) assert polynomial_reduce(Poly(1 + x*t + t**2, t), DE) == \ (Poly(t, t), Poly(x*t, t)) assert polynomial_reduce(Poly(0, t), DE) == \ (Poly(0, t), Poly(0, t)) def test_laurent_series(): DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1, t)]}) a = Poly(36, t) d = Poly((t - 2)*(t**2 - 1)**2, t) F = Poly(t**2 - 1, t) n = 2 assert laurent_series(a, d, F, n, DE) == \ (Poly(-3*t**3 + 3*t**2 - 6*t - 8, t), Poly(t**5 + t**4 - 2*t**3 - 2*t**2 + t + 1, t), [Poly(-3*t**3 - 6*t**2, t), Poly(2*t**6 + 6*t**5 - 8*t**3, t)]) def test_recognize_derivative(): DE = DifferentialExtension(extension={'D': [Poly(1, t)]}) a = Poly(36, t) d = Poly((t - 2)*(t**2 - 1)**2, t) assert recognize_derivative(a, d, DE) == False DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1/x, t)]}) a = Poly(2, t) d = Poly(t**2 - 1, t) assert recognize_derivative(a, d, DE) == False assert recognize_derivative(Poly(x*t, t), Poly(1, t), DE) == True DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t**2 + 1, t)]}) assert recognize_derivative(Poly(t, t), Poly(1, t), DE) == True def test_recognize_log_derivative(): a = Poly(2*x**2 + 4*x*t - 2*t - x**2*t, t) d = Poly((2*x + t)*(t + x**2), t) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t, t)]}) assert recognize_log_derivative(a, d, DE, z) == True DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1/x, t)]}) assert recognize_log_derivative(Poly(t + 1, t), Poly(t + x, t), DE) == True assert recognize_log_derivative(Poly(2, t), Poly(t**2 - 1, t), DE) == True DE = DifferentialExtension(extension={'D': [Poly(1, x)]}) assert recognize_log_derivative(Poly(1, x), Poly(x**2 - 2, x), DE) == False assert recognize_log_derivative(Poly(1, x), Poly(x**2 + x, x), DE) == True DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t**2 + 1, t)]}) assert recognize_log_derivative(Poly(1, t), Poly(t**2 - 2, t), DE) == False assert recognize_log_derivative(Poly(1, t), Poly(t**2 + t, t), DE) == False def test_residue_reduce(): a = Poly(2*t**2 - t - x**2, t) d = Poly(t**3 - x**2*t, t) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1/x, t)], 'Tfuncs': [log]}) assert residue_reduce(a, d, DE, z, invert=False) == \ ([(Poly(z**2 - S(1)/4, z), Poly((1 + 3*x*z - 6*z**2 - 2*x**2 + 4*x**2*z**2)*t - x*z + x**2 + 2*x**2*z**2 - 2*z*x**3, t))], False) assert residue_reduce(a, d, DE, z, invert=True) == \ ([(Poly(z**2 - S(1)/4, z), Poly(t + 2*x*z, t))], False) assert residue_reduce(Poly(-2/x, t), Poly(t**2 - 1, t,), DE, z, invert=False) == \ ([(Poly(z**2 - 1, z), Poly(-2*z*t/x - 2/x, t))], True) ans = residue_reduce(Poly(-2/x, t), Poly(t**2 - 1, t), DE, z, invert=True) assert ans == ([(Poly(z**2 - 1, z), Poly(t + z, t))], True) assert residue_reduce_to_basic(ans[0], DE, z) == -log(-1 + log(x)) + log(1 + log(x)) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(-t**2 - t/x - (1 - nu**2/x**2), t)]}) # TODO: Skip or make faster assert residue_reduce(Poly((-2*nu**2 - x**4)/(2*x**2)*t - (1 + x**2)/x, t), Poly(t**2 + 1 + x**2/2, t), DE, z) == \ ([(Poly(z + S(1)/2, z, domain='QQ'), Poly(t**2 + 1 + x**2/2, t, domain='EX'))], True) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1 + t**2, t)]}) assert residue_reduce(Poly(-2*x*t + 1 - x**2, t), Poly(t**2 + 2*x*t + 1 + x**2, t), DE, z) == \ ([(Poly(z**2 + S(1)/4, z), Poly(t + x + 2*z, t))], True) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t, t)]}) assert residue_reduce(Poly(t, t), Poly(t + sqrt(2), t), DE, z) == \ ([(Poly(z - 1, z), Poly(t + sqrt(2), t))], True) def test_integrate_hyperexponential(): # TODO: Add tests for integrate_hyperexponential() from the book a = Poly((1 + 2*t1 + t1**2 + 2*t1**3)*t**2 + (1 + t1**2)*t + 1 + t1**2, t) d = Poly(1, t) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1 + t1**2, t1), Poly(t*(1 + t1**2), t)], 'Tfuncs': [tan, Lambda(i, exp(tan(i)))]}) assert integrate_hyperexponential(a, d, DE) == \ (exp(2*tan(x))*tan(x) + exp(tan(x)), 1 + t1**2, True) # exp(2*tan(x))*tan(x) + tan(x) + exp(tan(x)) a = Poly((t1**3 + (x + 1)*t1**2 + t1 + x + 2)*t, t) assert integrate_hyperexponential(a, d, DE) == \ ((x + tan(x))*exp(tan(x)), 0, True) a = Poly(t, t) d = Poly(1, t) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(2*x*t, t)], 'Tfuncs': [Lambda(i, exp(x**2))]}) assert integrate_hyperexponential(a, d, DE) == \ (0, NonElementaryIntegral(exp(x**2), x), False) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t, t)], 'Tfuncs': [exp]}) assert integrate_hyperexponential(a, d, DE) == (exp(x), 0, True) a = Poly(25*t**6 - 10*t**5 + 7*t**4 - 8*t**3 + 13*t**2 + 2*t - 1, t) d = Poly(25*t**6 + 35*t**4 + 11*t**2 + 1, t) assert integrate_hyperexponential(a, d, DE) == \ (-(11 - 10*exp(x))/(5 + 25*exp(2*x)) + log(1 + exp(2*x)), -1, True) # -(55 - 50*exp(x))/(25 + 125*exp(2*x)) - x + log(1 + exp(2*x)) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t0, t0), Poly(t0*t, t)], 'Tfuncs': [exp, Lambda(i, exp(exp(i)))]}) assert integrate_hyperexponential(Poly(2*t0*t**2, t), Poly(1, t), DE) == (exp(2*exp(x)), 0, True) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t0, t0), Poly(-t0*t, t)], 'Tfuncs': [exp, Lambda(i, exp(-exp(i)))]}) assert integrate_hyperexponential(Poly(-27*exp(9) - 162*t0*exp(9) + 27*x*t0*exp(9), t), Poly((36*exp(18) + x**2*exp(18) - 12*x*exp(18))*t, t), DE) == \ (27*exp(exp(x))/(-6*exp(9) + x*exp(9)), 0, True) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t, t)], 'Tfuncs': [exp]}) assert integrate_hyperexponential(Poly(x**2/2*t, t), Poly(1, t), DE) == \ ((2 - 2*x + x**2)*exp(x)/2, 0, True) assert integrate_hyperexponential(Poly(1 + t, t), Poly(t, t), DE) == \ (-exp(-x), 1, True) # x - exp(-x) assert integrate_hyperexponential(Poly(x, t), Poly(t + 1, t), DE) == \ (0, NonElementaryIntegral(x/(1 + exp(x)), x), False) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1/x, t0), Poly(2*x*t1, t1)], 'Tfuncs': [log, Lambda(i, exp(i**2))]}) elem, nonelem, b = integrate_hyperexponential(Poly((8*x**7 - 12*x**5 + 6*x**3 - x)*t1**4 + (8*t0*x**7 - 8*t0*x**6 - 4*t0*x**5 + 2*t0*x**3 + 2*t0*x**2 - t0*x + 24*x**8 - 36*x**6 - 4*x**5 + 22*x**4 + 4*x**3 - 7*x**2 - x + 1)*t1**3 + (8*t0*x**8 - 4*t0*x**6 - 16*t0*x**5 - 2*t0*x**4 + 12*t0*x**3 + t0*x**2 - 2*t0*x + 24*x**9 - 36*x**7 - 8*x**6 + 22*x**5 + 12*x**4 - 7*x**3 - 6*x**2 + x + 1)*t1**2 + (8*t0*x**8 - 8*t0*x**6 - 16*t0*x**5 + 6*t0*x**4 + 10*t0*x**3 - 2*t0*x**2 - t0*x + 8*x**10 - 12*x**8 - 4*x**7 + 2*x**6 + 12*x**5 + 3*x**4 - 9*x**3 - x**2 + 2*x)*t1 + 8*t0*x**7 - 12*t0*x**6 - 4*t0*x**5 + 8*t0*x**4 - t0*x**2 - 4*x**7 + 4*x**6 + 4*x**5 - 4*x**4 - x**3 + x**2, t1), Poly((8*x**7 - 12*x**5 + 6*x**3 - x)*t1**4 + (24*x**8 + 8*x**7 - 36*x**6 - 12*x**5 + 18*x**4 + 6*x**3 - 3*x**2 - x)*t1**3 + (24*x**9 + 24*x**8 - 36*x**7 - 36*x**6 + 18*x**5 + 18*x**4 - 3*x**3 - 3*x**2)*t1**2 + (8*x**10 + 24*x**9 - 12*x**8 - 36*x**7 + 6*x**6 + 18*x**5 - x**4 - 3*x**3)*t1 + 8*x**10 - 12*x**8 + 6*x**6 - x**4, t1), DE) assert factor(elem) == -((x - 1)*log(x)/((x + exp(x**2))*(2*x**2 - 1))) assert (nonelem, b) == (NonElementaryIntegral(exp(x**2)/(exp(x**2) + 1), x), False) def test_integrate_hyperexponential_polynomial(): # Without proper cancellation within integrate_hyperexponential_polynomial(), # this will take a long time to complete, and will return a complicated # expression p = Poly((-28*x**11*t0 - 6*x**8*t0 + 6*x**9*t0 - 15*x**8*t0**2 + 15*x**7*t0**2 + 84*x**10*t0**2 - 140*x**9*t0**3 - 20*x**6*t0**3 + 20*x**7*t0**3 - 15*x**6*t0**4 + 15*x**5*t0**4 + 140*x**8*t0**4 - 84*x**7*t0**5 - 6*x**4*t0**5 + 6*x**5*t0**5 + x**3*t0**6 - x**4*t0**6 + 28*x**6*t0**6 - 4*x**5*t0**7 + x**9 - x**10 + 4*x**12)/(-8*x**11*t0 + 28*x**10*t0**2 - 56*x**9*t0**3 + 70*x**8*t0**4 - 56*x**7*t0**5 + 28*x**6*t0**6 - 8*x**5*t0**7 + x**4*t0**8 + x**12)*t1**2 + (-28*x**11*t0 - 12*x**8*t0 + 12*x**9*t0 - 30*x**8*t0**2 + 30*x**7*t0**2 + 84*x**10*t0**2 - 140*x**9*t0**3 - 40*x**6*t0**3 + 40*x**7*t0**3 - 30*x**6*t0**4 + 30*x**5*t0**4 + 140*x**8*t0**4 - 84*x**7*t0**5 - 12*x**4*t0**5 + 12*x**5*t0**5 - 2*x**4*t0**6 + 2*x**3*t0**6 + 28*x**6*t0**6 - 4*x**5*t0**7 + 2*x**9 - 2*x**10 + 4*x**12)/(-8*x**11*t0 + 28*x**10*t0**2 - 56*x**9*t0**3 + 70*x**8*t0**4 - 56*x**7*t0**5 + 28*x**6*t0**6 - 8*x**5*t0**7 + x**4*t0**8 + x**12)*t1 + (-2*x**2*t0 + 2*x**3*t0 + x*t0**2 - x**2*t0**2 + x**3 - x**4)/(-4*x**5*t0 + 6*x**4*t0**2 - 4*x**3*t0**3 + x**2*t0**4 + x**6), t1, z, expand=False) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1/x, t0), Poly(2*x*t1, t1)]}) assert integrate_hyperexponential_polynomial(p, DE, z) == ( Poly((x - t0)*t1**2 + (-2*t0 + 2*x)*t1, t1), Poly(-2*x*t0 + x**2 + t0**2, t1), True) DE = DifferentialExtension(extension={'D':[Poly(1, x), Poly(t0, t0)]}) assert integrate_hyperexponential_polynomial(Poly(0, t0), DE, z) == ( Poly(0, t0), Poly(1, t0), True) def test_integrate_hyperexponential_returns_piecewise(): a, b = symbols('a b') DE = DifferentialExtension(a**x, x) assert integrate_hyperexponential(DE.fa, DE.fd, DE) == (Piecewise( (x, Eq(log(a), 0)), (exp(x*log(a))/log(a), True)), 0, True) DE = DifferentialExtension(a**(b*x), x) assert integrate_hyperexponential(DE.fa, DE.fd, DE) == (Piecewise( (x, Eq(b*log(a), 0)), (exp(b*x*log(a))/(b*log(a)), True)), 0, True) DE = DifferentialExtension(exp(a*x), x) assert integrate_hyperexponential(DE.fa, DE.fd, DE) == (Piecewise( (x, Eq(a, 0)), (exp(a*x)/a, True)), 0, True) DE = DifferentialExtension(x*exp(a*x), x) assert integrate_hyperexponential(DE.fa, DE.fd, DE) == (Piecewise( (x**2/2, Eq(a**3, 0)), ((x*a**2 - a)*exp(a*x)/a**3, True)), 0, True) DE = DifferentialExtension(x**2*exp(a*x), x) assert integrate_hyperexponential(DE.fa, DE.fd, DE) == (Piecewise( (x**3/3, Eq(a**6, 0)), ((x**2*a**5 - 2*x*a**4 + 2*a**3)*exp(a*x)/a**6, True)), 0, True) DE = DifferentialExtension(x**y + z, y) assert integrate_hyperexponential(DE.fa, DE.fd, DE) == (Piecewise((y, Eq(log(x), 0)), (exp(log(x)*y)/log(x), True)), z, True) DE = DifferentialExtension(x**y + z + x**(2*y), y) assert integrate_hyperexponential(DE.fa, DE.fd, DE) == (Piecewise((2*y, Eq(2*log(x)**2, 0)), ((exp(2*log(x)*y)*log(x) + 2*exp(log(x)*y)*log(x))/(2*log(x)**2), True)), z, True) # TODO: Add a test where two different parts of the extension use a # Piecewise, like y**x + z**x. def test_integrate_primitive(): DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1/x, t)], 'Tfuncs': [log]}) assert integrate_primitive(Poly(t, t), Poly(1, t), DE) == (x*log(x), -1, True) # (x*log(x) - x, True) assert integrate_primitive(Poly(x, t), Poly(t, t), DE) == (0, NonElementaryIntegral(x/log(x), x), False) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1/x, t1), Poly(1/(x + 1), t2)], 'Tfuncs': [log, Lambda(i, log(i + 1))]}) assert integrate_primitive(Poly(t1, t2), Poly(t2, t2), DE) == \ (0, NonElementaryIntegral(log(x)/log(1 + x), x), False) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1/x, t1), Poly(1/(x*t1), t2)], 'Tfuncs': [log, Lambda(i, log(log(i)))]}) assert integrate_primitive(Poly(t2, t2), Poly(t1, t2), DE) == \ (0, NonElementaryIntegral(log(log(x))/log(x), x), False) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1/x, t0)], 'Tfuncs': [log]}) assert integrate_primitive(Poly(x**2*t0**3 + (3*x**2 + x)*t0**2 + (3*x**2 + 2*x)*t0 + x**2 + x, t0), Poly(x**2*t0**4 + 4*x**2*t0**3 + 6*x**2*t0**2 + 4*x**2*t0 + x**2, t0), DE) == \ (-1/(log(x) + 1), NonElementaryIntegral(1/(log(x) + 1), x), False) def test_integrate_hypertangent_polynomial(): DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t**2 + 1, t)]}) assert integrate_hypertangent_polynomial(Poly(t**2 + x*t + 1, t), DE) == \ (Poly(t, t), Poly(x/2, t)) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(a*(t**2 + 1), t)]}) assert integrate_hypertangent_polynomial(Poly(t**5, t), DE) == \ (Poly(1/(4*a)*t**4 - 1/(2*a)*t**2, t), Poly(1/(2*a), t)) def test_integrate_nonlinear_no_specials(): a, d, = Poly(x**2*t**5 + x*t**4 - nu**2*t**3 - x*(x**2 + 1)*t**2 - (x**2 - nu**2)*t - x**5/4, t), Poly(x**2*t**4 + x**2*(x**2 + 2)*t**2 + x**2 + x**4 + x**6/4, t) # f(x) == phi_nu(x), the logarithmic derivative of J_v, the Bessel function, # which has no specials (see Chapter 5, note 4 of Bronstein's book). f = Function('phi_nu') DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(-t**2 - t/x - (1 - nu**2/x**2), t)], 'Tfuncs': [f]}) assert integrate_nonlinear_no_specials(a, d, DE) == \ (-log(1 + f(x)**2 + x**2/2)/2 - (4 + x**2)/(4 + 2*x**2 + 4*f(x)**2), True) assert integrate_nonlinear_no_specials(Poly(t, t), Poly(1, t), DE) == \ (0, False) def test_integer_powers(): assert integer_powers([x, x/2, x**2 + 1, 2*x/3]) == [ (x/6, [(x, 6), (x/2, 3), (2*x/3, 4)]), (1 + x**2, [(1 + x**2, 1)])] def test_DifferentialExtension_exp(): assert DifferentialExtension(exp(x) + exp(x**2), x, dummy=False)._important_attrs == \ (Poly(t1 + t0, t1), Poly(1, t1), [Poly(1, x,), Poly(t0, t0), Poly(2*x*t1, t1)], [x, t0, t1], [Lambda(i, exp(i)), Lambda(i, exp(i**2))], [], [1, 2], [x, x**2], [], []) assert DifferentialExtension(exp(x) + exp(2*x), x, dummy=False)._important_attrs == \ (Poly(t0**2 + t0, t0), Poly(1, t0), [Poly(1, x), Poly(t0, t0)], [x, t0], [Lambda(i, exp(i))], [], [1], [x], [], []) assert DifferentialExtension(exp(x) + exp(x/2), x, dummy=False)._important_attrs == \ (Poly(t0**2 + t0, t0), Poly(1, t0), [Poly(1, x), Poly(t0/2, t0)], [x, t0], [Lambda(i, exp(i/2))], [], [1], [x/2], [], []) assert DifferentialExtension(exp(x) + exp(x**2) + exp(x + x**2), x, dummy=False)._important_attrs == \ (Poly((1 + t0)*t1 + t0, t1), Poly(1, t1), [Poly(1, x), Poly(t0, t0), Poly(2*x*t1, t1)], [x, t0, t1], [Lambda(i, exp(i)), Lambda(i, exp(i**2))], [], [1, 2], [x, x**2], [], []) assert DifferentialExtension(exp(x) + exp(x**2) + exp(x + x**2 + 1), x, dummy=False)._important_attrs == \ (Poly((1 + S.Exp1*t0)*t1 + t0, t1), Poly(1, t1), [Poly(1, x), Poly(t0, t0), Poly(2*x*t1, t1)], [x, t0, t1], [Lambda(i, exp(i)), Lambda(i, exp(i**2))], [], [1, 2], [x, x**2], [], []) assert DifferentialExtension(exp(x) + exp(x**2) + exp(x/2 + x**2), x, dummy=False)._important_attrs == \ (Poly((t0 + 1)*t1 + t0**2, t1), Poly(1, t1), [Poly(1, x), Poly(t0/2, t0), Poly(2*x*t1, t1)], [x, t0, t1], [Lambda(i, exp(i/2)), Lambda(i, exp(i**2))], [(exp(x/2), sqrt(exp(x)))], [1, 2], [x/2, x**2], [], []) assert DifferentialExtension(exp(x) + exp(x**2) + exp(x/2 + x**2 + 3), x, dummy=False)._important_attrs == \ (Poly((t0*exp(3) + 1)*t1 + t0**2, t1), Poly(1, t1), [Poly(1, x), Poly(t0/2, t0), Poly(2*x*t1, t1)], [x, t0, t1], [Lambda(i, exp(i/2)), Lambda(i, exp(i**2))], [(exp(x/2), sqrt(exp(x)))], [1, 2], [x/2, x**2], [], []) assert DifferentialExtension(sqrt(exp(x)), x, dummy=False)._important_attrs == \ (Poly(t0, t0), Poly(1, t0), [Poly(1, x), Poly(t0/2, t0)], [x, t0], [Lambda(i, exp(i/2))], [(exp(x/2), sqrt(exp(x)))], [1], [x/2], [], []) assert DifferentialExtension(exp(x/2), x, dummy=False)._important_attrs == \ (Poly(t0, t0), Poly(1, t0), [Poly(1, x), Poly(t0/2, t0)], [x, t0], [Lambda(i, exp(i/2))], [], [1], [x/2], [], []) def test_DifferentialExtension_log(): assert DifferentialExtension(log(x)*log(x + 1)*log(2*x**2 + 2*x), x, dummy=False)._important_attrs == \ (Poly(t0*t1**2 + (t0*log(2) + t0**2)*t1, t1), Poly(1, t1), [Poly(1, x), Poly(1/x, t0), Poly(1/(x + 1), t1, expand=False)], [x, t0, t1], [Lambda(i, log(i)), Lambda(i, log(i + 1))], [], [], [], [1, 2], [x, x + 1]) assert DifferentialExtension(x**x*log(x), x, dummy=False)._important_attrs == \ (Poly(t0*t1, t1), Poly(1, t1), [Poly(1, x), Poly(1/x, t0), Poly((1 + t0)*t1, t1)], [x, t0, t1], [Lambda(i, log(i)), Lambda(i, exp(t0*i))], [(exp(x*log(x)), x**x)], [2], [t0*x], [1], [x]) def test_DifferentialExtension_symlog(): assert DifferentialExtension(log(x**x), x, dummy=False)._important_attrs == \ (Poly(x*t0, t0), Poly(1, t0), [Poly(1, x), Poly(1/x, t0)], [x, t0], [Lambda(i, log(i))], [(x*log(x), log(x**x))], [], [], [1], [x]) assert DifferentialExtension(log(x**y), x, dummy=False)._important_attrs == \ (Poly(y*t0, t0), Poly(1, t0), [Poly(1, x), Poly(1/x, t0)], [x, t0], [Lambda(i, log(i))], [(y*log(x), log(x**y))], [], [], [1], [x]) assert DifferentialExtension(log(sqrt(x)), x, dummy=False)._important_attrs == \ (Poly(t0, t0), Poly(2, t0), [Poly(1, x), Poly(1/x, t0)], [x, t0], [Lambda(i, log(i))], [(log(x)/2, log(sqrt(x)))], [], [], [1], [x]) def test_DifferentialExtension_handle_first(): assert DifferentialExtension(exp(x)*log(x), x, handle_first='log', dummy=False)._important_attrs == \ (Poly(t0*t1, t1), Poly(1, t1), [Poly(1, x), Poly(1/x, t0), Poly(t1, t1)], [x, t0, t1], [Lambda(i, log(i)), Lambda(i, exp(i))], [], [2], [x], [1], [x]) assert DifferentialExtension(exp(x)*log(x), x, handle_first='exp', dummy=False)._important_attrs == \ (Poly(t0*t1, t1), Poly(1, t1), [Poly(1, x), Poly(t0, t0), Poly(1/x, t1)], [x, t0, t1], [Lambda(i, exp(i)), Lambda(i, log(i))], [], [1], [x], [2], [x]) # This one must have the log first, regardless of what we set it to # (because the log is inside of the exponential: x**x == exp(x*log(x))) assert DifferentialExtension(-x**x*log(x)**2 + x**x - x**x/x, x, handle_first='exp', dummy=False)._important_attrs == \ DifferentialExtension(-x**x*log(x)**2 + x**x - x**x/x, x, handle_first='log', dummy=False)._important_attrs == \ (Poly((-1 + x - x*t0**2)*t1, t1), Poly(x, t1), [Poly(1, x), Poly(1/x, t0), Poly((1 + t0)*t1, t1)], [x, t0, t1], [Lambda(i, log(i)), Lambda(i, exp(t0*i))], [(exp(x*log(x)), x**x)], [2], [t0*x], [1], [x]) def test_DifferentialExtension_all_attrs(): # Test 'unimportant' attributes DE = DifferentialExtension(exp(x)*log(x), x, dummy=False, handle_first='exp') assert DE.f == exp(x)*log(x) assert DE.newf == t0*t1 assert DE.x == x assert DE.cases == ['base', 'exp', 'primitive'] assert DE.case == 'primitive' assert DE.level == -1 assert DE.t == t1 == DE.T[DE.level] assert DE.d == Poly(1/x, t1) == DE.D[DE.level] raises(ValueError, lambda: DE.increment_level()) DE.decrement_level() assert DE.level == -2 assert DE.t == t0 == DE.T[DE.level] assert DE.d == Poly(t0, t0) == DE.D[DE.level] assert DE.case == 'exp' DE.decrement_level() assert DE.level == -3 assert DE.t == x == DE.T[DE.level] == DE.x assert DE.d == Poly(1, x) == DE.D[DE.level] assert DE.case == 'base' raises(ValueError, lambda: DE.decrement_level()) DE.increment_level() DE.increment_level() assert DE.level == -1 assert DE.t == t1 == DE.T[DE.level] assert DE.d == Poly(1/x, t1) == DE.D[DE.level] assert DE.case == 'primitive' def test_DifferentialExtension_extension_flag(): raises(ValueError, lambda: DifferentialExtension(extension={'T': [x, t]})) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t, t)]}) assert DE._important_attrs == (None, None, [Poly(1, x), Poly(t, t)], [x, t], None, None, None, None, None, None) assert DE.d == Poly(t, t) assert DE.t == t assert DE.level == -1 assert DE.cases == ['base', 'exp'] assert DE.x == x assert DE.case == 'exp' DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t, t)], 'E_K': [1], 'E_args': [x], 'L_K': [], 'L_args': []}) assert DE._important_attrs == (None, None, [Poly(1, x), Poly(t, t)], [x, t], None, None, [1], [x], [], []) raises(ValueError, lambda: DifferentialExtension()) def test_DifferentialExtension_misc(): # Odd ends assert DifferentialExtension(sin(y)*exp(x), x, dummy=False)._important_attrs == \ (Poly(sin(y)*t0, t0, domain='ZZ[sin(y)]'), Poly(1, t0, domain='ZZ'), [Poly(1, x, domain='ZZ'), Poly(t0, t0, domain='ZZ')], [x, t0], [Lambda(i, exp(i))], [], [1], [x], [], []) raises(NotImplementedError, lambda: DifferentialExtension(sin(x), x)) assert DifferentialExtension(10**x, x, dummy=False)._important_attrs == \ (Poly(t0, t0), Poly(1, t0), [Poly(1, x), Poly(log(10)*t0, t0)], [x, t0], [Lambda(i, exp(i*log(10)))], [(exp(x*log(10)), 10**x)], [1], [x*log(10)], [], []) assert DifferentialExtension(log(x) + log(x**2), x, dummy=False)._important_attrs in [ (Poly(3*t0, t0), Poly(2, t0), [Poly(1, x), Poly(2/x, t0)], [x, t0], [Lambda(i, log(i**2))], [], [], [], [1], [x**2]), (Poly(3*t0, t0), Poly(1, t0), [Poly(1, x), Poly(1/x, t0)], [x, t0], [Lambda(i, log(i))], [], [], [], [1], [x])] assert DifferentialExtension(S.Zero, x, dummy=False)._important_attrs == \ (Poly(0, x), Poly(1, x), [Poly(1, x)], [x], [], [], [], [], [], []) def test_DifferentialExtension_Rothstein(): # Rothstein's integral f = (2581284541*exp(x) + 1757211400)/(39916800*exp(3*x) + 119750400*exp(x)**2 + 119750400*exp(x) + 39916800)*exp(1/(exp(x) + 1) - 10*x) assert DifferentialExtension(f, x, dummy=False)._important_attrs == \ (Poly((1757211400 + 2581284541*t0)*t1, t1), Poly(39916800 + 119750400*t0 + 119750400*t0**2 + 39916800*t0**3, t1), [Poly(1, x), Poly(t0, t0), Poly(-(10 + 21*t0 + 10*t0**2)/(1 + 2*t0 + t0**2)*t1, t1, domain='ZZ(t0)')], [x, t0, t1], [Lambda(i, exp(i)), Lambda(i, exp(1/(t0 + 1) - 10*i))], [], [1, 2], [x, 1/(t0 + 1) - 10*x], [], []) class TestingException(Exception): """Dummy Exception class for testing.""" pass def test_DecrementLevel(): DE = DifferentialExtension(x*log(exp(x) + 1), x, dummy=False) assert DE.level == -1 assert DE.t == t1 assert DE.d == Poly(t0/(t0 + 1), t1) assert DE.case == 'primitive' with DecrementLevel(DE): assert DE.level == -2 assert DE.t == t0 assert DE.d == Poly(t0, t0) assert DE.case == 'exp' with DecrementLevel(DE): assert DE.level == -3 assert DE.t == x assert DE.d == Poly(1, x) assert DE.case == 'base' assert DE.level == -2 assert DE.t == t0 assert DE.d == Poly(t0, t0) assert DE.case == 'exp' assert DE.level == -1 assert DE.t == t1 assert DE.d == Poly(t0/(t0 + 1), t1) assert DE.case == 'primitive' # Test that __exit__ is called after an exception correctly try: with DecrementLevel(DE): raise TestingException except TestingException: pass else: raise AssertionError("Did not raise.") assert DE.level == -1 assert DE.t == t1 assert DE.d == Poly(t0/(t0 + 1), t1) assert DE.case == 'primitive' def test_risch_integrate(): assert risch_integrate(t0*exp(x), x) == t0*exp(x) assert risch_integrate(sin(x), x, rewrite_complex=True) == -exp(I*x)/2 - exp(-I*x)/2 # From my GSoC writeup assert risch_integrate((1 + 2*x**2 + x**4 + 2*x**3*exp(2*x**2))/ (x**4*exp(x**2) + 2*x**2*exp(x**2) + exp(x**2)), x) == \ NonElementaryIntegral(exp(-x**2), x) + exp(x**2)/(1 + x**2) assert risch_integrate(0, x) == 0 # These are tested here in addition to in test_DifferentialExtension above # (symlogs) to test that backsubs works correctly. The integrals should be # written in terms of the original logarithms in the integrands. assert risch_integrate(log(x**x), x) == x*log(x**x)/2 - x**2/4 assert risch_integrate(log(x**y), x) == x*log(x**y) - x*y assert risch_integrate(log(sqrt(x)), x) == x*log(sqrt(x)) - x/2 def test_risch_integrate_float(): assert risch_integrate((-60*exp(x) - 19.2*exp(4*x))*exp(4*x), x) == -2.4*exp(8*x) - 12.0*exp(5*x) def test_NonElementaryIntegral(): assert isinstance(risch_integrate(exp(x**2), x), NonElementaryIntegral) assert isinstance(risch_integrate(x**x*log(x), x), NonElementaryIntegral) # Make sure methods of Integral still give back a NonElementaryIntegral assert isinstance(NonElementaryIntegral(x**x*t0, x).subs(t0, log(x)), NonElementaryIntegral) sympy-0.7.4.1/sympy/integrals/tests/test_quadrature.py0000644000175000017500000004706412253362407023360 0ustar georgeskgeorgeskfrom sympy.core import S from sympy.integrals.quadrature import (gauss_legendre, gauss_laguerre, gauss_hermite, gauss_gen_laguerre, gauss_chebyshev_t, gauss_chebyshev_u, gauss_jacobi) def test_legendre(): x, w = gauss_legendre(1, 17) assert [str(r) for r in x] == ['0'] assert [str(r) for r in w] == ['2.0000000000000000'] x, w = gauss_legendre(2, 17) assert [str(r) for r in x] == ['-0.57735026918962576', '0.57735026918962576'] assert [str(r) for r in w] == ['1.0000000000000000', '1.0000000000000000'] x, w = gauss_legendre(3, 17) assert [str(r) for r in x] == ['-0.77459666924148338', '0', '0.77459666924148338'] assert [str(r) for r in w] == ['0.55555555555555556', '0.88888888888888889', '0.55555555555555556'] x, w = gauss_legendre(4, 17) assert [str(r) for r in x] == ['-0.86113631159405258', '-0.33998104358485626', '0.33998104358485626', '0.86113631159405258'] assert [str(r) for r in w] == ['0.34785484513745386', '0.65214515486254614', '0.65214515486254614', '0.34785484513745386'] def test_legendre_precise(): x, w = gauss_legendre(3, 40) assert [str(r) for r in x] == \ ['-0.7745966692414833770358530799564799221666', '0', '0.7745966692414833770358530799564799221666'] assert [str(r) for r in w] == \ ['0.5555555555555555555555555555555555555556', '0.8888888888888888888888888888888888888889', '0.5555555555555555555555555555555555555556'] def test_laguerre(): x, w = gauss_laguerre(1, 17) assert [str(r) for r in x] == ['1.0000000000000000'] assert [str(r) for r in w] == ['1.0000000000000000'] x, w = gauss_laguerre(2, 17) assert [str(r) for r in x] == ['0.58578643762690495', '3.4142135623730950'] assert [str(r) for r in w] == ['0.85355339059327376', '0.14644660940672624'] x, w = gauss_laguerre(3, 17) assert [str(r) for r in x] == [ '0.41577455678347908', '2.2942803602790417', '6.2899450829374792', ] assert [str(r) for r in w] == [ '0.71109300992917302', '0.27851773356924085', '0.010389256501586136', ] x, w = gauss_laguerre(4, 17) assert [str(r) for r in x] == ['0.32254768961939231', '1.7457611011583466', '4.5366202969211280', '9.3950709123011331'] assert [str(r) for r in w] == ['0.60315410434163360', '0.35741869243779969', '0.038887908515005384', '0.00053929470556132745'] x, w = gauss_laguerre(5, 17) assert [str(r) for r in x] == ['0.26356031971814091', '1.4134030591065168', '3.5964257710407221', '7.0858100058588376', '12.640800844275783'] assert [str(r) for r in w] == ['0.52175561058280865', '0.39866681108317593', '0.075942449681707595', '0.0036117586799220485', '2.3369972385776228e-5'] def test_laguerre_precise(): x, w = gauss_laguerre(3, 40) assert [str(r) for r in x] == \ ['0.4157745567834790833115338731282744735466', '2.294280360279041719822050361359593868960', '6.289945082937479196866415765512131657493'] assert [str(r) for r in w] == \ ['0.7110930099291730154495901911425944313094', '0.2785177335692408488014448884567264810349', '0.01038925650158613574896492040067908765572'] def test_hermite(): x, w = gauss_hermite(1, 17) assert [str(r) for r in x] == ['0'] assert [str(r) for r in w] == ['1.7724538509055160'] x, w = gauss_hermite(2, 17) assert [str(r) for r in x] == ['-0.70710678118654752', '0.70710678118654752'] assert [str(r) for r in w] == ['0.88622692545275801', '0.88622692545275801'] x, w = gauss_hermite(3, 17) assert [str(r) for r in x] == [ '-1.2247448713915890', '0', '1.2247448713915890'] assert [str(r) for r in w] == [ '0.29540897515091934', '1.1816359006036774', '0.29540897515091934'] x, w = gauss_hermite(4, 17) assert [str(r) for r in x] == [ '-1.6506801238857846', '-0.52464762327529032', '0.52464762327529032', '1.6506801238857846' ] assert [str(r) for r in w] == [ '0.081312835447245177', '0.80491409000551284', '0.80491409000551284', '0.081312835447245177' ] x, w = gauss_hermite(5, 17) assert [str(r) for r in x] == [ '-2.0201828704560856', '-0.95857246461381851', '0', '0.95857246461381851', '2.0201828704560856' ] assert [str(r) for r in w] == [ '0.019953242059045913', '0.39361932315224116', '0.94530872048294188', '0.39361932315224116', '0.019953242059045913' ] def test_hermite_precise(): x, w = gauss_hermite(3, 40) assert [str(r) for r in x] == [ '-1.224744871391589049098642037352945695983', '0', '1.224744871391589049098642037352945695983' ] assert [str(r) for r in w] == [ '0.2954089751509193378830279138901908637996', '1.181635900603677351532111655560763455198', '0.2954089751509193378830279138901908637996' ] def test_gen_laguerre(): x, w = gauss_gen_laguerre(1, -S.Half, 17) assert [str(r) for r in x] == ['0.50000000000000000'] assert [str(r) for r in w] == ['1.7724538509055160'] x, w = gauss_gen_laguerre(2, -S.Half, 17) assert [str(r) for r in x] == ['0.27525512860841095', '2.7247448713915890'] assert [str(r) for r in w] == ['1.6098281800110257', '0.16262567089449035'] x, w = gauss_gen_laguerre(3, -S.Half, 17) assert [str(r) for r in x] == ['0.19016350919348813', '1.7844927485432516', '5.5253437422632603'] assert [str(r) for r in w] == ['1.4492591904487850', '0.31413464064571329', '0.0090600198110176913'] x, w = gauss_gen_laguerre(4, -S.Half, 17) assert [str(r) for r in x] == ['0.14530352150331709', '1.3390972881263614', '3.9269635013582872', '8.5886356890120343'] assert [str(r) for r in w] ==['1.3222940251164826', '0.41560465162978376', '0.034155966014826951', '0.00039920814442273524'] x, w = gauss_gen_laguerre(5, -S.Half, 17) assert [str(r) for r in x] ==['0.11758132021177814', '1.0745620124369040', '3.0859374437175500', '6.4147297336620305', '11.807189489971737'] assert [str(r) for r in w] ==['1.2217252674706516', '0.48027722216462937', '0.067748788910962126', '0.0026872914935624654', '1.5280865710465241e-5'] x, w = gauss_gen_laguerre(1, 2, 17) assert [str(r) for r in x] ==['3.0000000000000000'] assert [str(r) for r in w] == ['2.0000000000000000'] x, w = gauss_gen_laguerre(2, 2, 17) assert [str(r) for r in x] == ['2.0000000000000000', '6.0000000000000000'] assert [str(r) for r in w] ==['1.5000000000000000', '0.50000000000000000'] x, w = gauss_gen_laguerre(3, 2, 17) assert [str(r) for r in x] ==['1.5173870806774125', '4.3115831337195203', '9.1710297856030672'] assert [str(r) for r in w] ==['1.0374949614904253', '0.90575000470306537', '0.056755033806509347'] x, w = gauss_gen_laguerre(4, 2, 17) assert [str(r) for r in x] ==['1.2267632635003021', '3.4125073586969460', '6.9026926058516134', '12.458036771951139'] assert [str(r) for r in w] ==['0.72552499769865438', '1.0634242919791946', '0.20669613102835355', '0.0043545792937974889'] x, w = gauss_gen_laguerre(5, 2, 17) assert [str(r) for r in x] ==['1.0311091440933816', '2.8372128239538217', '5.6202942725987079', '9.6829098376640271', '15.828473921690062'] assert [str(r) for r in w] == ['0.52091739683509184', '1.0667059331592211', '0.38354972366693113', '0.028564233532974658', '0.00026271280578124935'] def test_gen_laguerre_precise(): x, w = gauss_gen_laguerre(3, -S.Half, 40) assert [str(r) for r in x] ==['0.1901635091934881328718554276203028970878', '1.784492748543251591186722461957367638500', '5.525343742263260275941422110422329464413'] assert [str(r) for r in w] == ['1.449259190448785048183829411195134343108', '0.3141346406457132878326231270167565378246', '0.009060019811017691281714945129254301865020'] x, w = gauss_gen_laguerre(3, 2, 40) assert [str(r) for r in x] == ['1.517387080677412495020323111016672547482', '4.311583133719520302881184669723530562299', '9.171029785603067202098492219259796890218'] assert [str(r) for r in w] ==['1.037494961490425285817554606541269153041', '0.9057500047030653669269785048806009945254', '0.05675503380650934725546688857812985243312'] def test_chebyshev_t(): x, w = gauss_chebyshev_t(1, 17) assert [str(r) for r in x] == ['0'] assert [str(r) for r in w] == ['3.1415926535897932'] x, w = gauss_chebyshev_t(2, 17) assert [str(r) for r in x] == ['0.70710678118654752', '-0.70710678118654752'] assert [str(r) for r in w] == ['1.5707963267948966', '1.5707963267948966'] x, w = gauss_chebyshev_t(3, 17) assert [str(r) for r in x] == ['0.86602540378443865', '0', '-0.86602540378443865'] assert [str(r) for r in w] == ['1.0471975511965977', '1.0471975511965977', '1.0471975511965977'] x, w = gauss_chebyshev_t(4, 17) assert [str(r) for r in x] == ['0.92387953251128676', '0.38268343236508977', '-0.38268343236508977', '-0.92387953251128676'] assert [str(r) for r in w] == ['0.78539816339744831', '0.78539816339744831', '0.78539816339744831', '0.78539816339744831'] x, w = gauss_chebyshev_t(5, 17) assert [str(r) for r in x] == ['0.95105651629515357', '0.58778525229247313', '0', '-0.58778525229247313', '-0.95105651629515357'] assert [str(r) for r in w] == ['0.62831853071795865', '0.62831853071795865', '0.62831853071795865', '0.62831853071795865', '0.62831853071795865'] def test_chebyshev_t_precise(): x, w = gauss_chebyshev_t(3, 40) assert [str(r) for r in x] == [ '0.8660254037844386467637231707529361834714', '0', '-0.8660254037844386467637231707529361834714'] assert [str(r) for r in w] == [ '1.047197551196597746154214461093167628066', '1.047197551196597746154214461093167628066', '1.047197551196597746154214461093167628066'] def test_chebyshev_u(): x, w = gauss_chebyshev_u(1, 17) assert [str(r) for r in x] == ['0'] assert [str(r) for r in w] == ['1.5707963267948966'] x, w = gauss_chebyshev_u(2, 17) assert [str(r) for r in x] == ['0.50000000000000000', '-0.50000000000000000'] assert [str(r) for r in w] == ['0.78539816339744831', '0.78539816339744831'] x, w = gauss_chebyshev_u(3, 17) assert [str(r) for r in x] == ['0.70710678118654752', '0', '-0.70710678118654752'] assert [str(r) for r in w] == ['0.39269908169872415', '0.78539816339744831', '0.39269908169872415'] x, w = gauss_chebyshev_u(4, 17) assert [str(r) for r in x] == ['0.80901699437494742', '0.30901699437494742', '-0.30901699437494742', '-0.80901699437494742'] assert [str(r) for r in w] == ['0.21707871342270599', '0.56831944997474231', '0.56831944997474231', '0.21707871342270599'] x, w = gauss_chebyshev_u(5, 17) assert [str(r) for r in x] == ['0.86602540378443865', '0.50000000000000000', '0', '-0.50000000000000000', '-0.86602540378443865'] assert [str(r) for r in w] == ['0.13089969389957472', '0.39269908169872415', '0.52359877559829887', '0.39269908169872415', '0.13089969389957472'] def test_chebyshev_u_precise(): x, w = gauss_chebyshev_u(3, 40) assert [str(r) for r in x] == [ '0.7071067811865475244008443621048490392848', '0', '-0.7071067811865475244008443621048490392848'] assert [str(r) for r in w] == [ '0.3926990816987241548078304229099378605246', '0.7853981633974483096156608458198757210493', '0.3926990816987241548078304229099378605246'] def test_jacobi(): x, w = gauss_jacobi(1, -S.Half, S.Half, 17) assert [str(r) for r in x] == ['0.50000000000000000'] assert [str(r) for r in w] == ['3.1415926535897932'] x, w = gauss_jacobi(2, -S.Half, S.Half, 17) assert [str(r) for r in x] == ['0.80901699437494742', '-0.30901699437494742'] assert [str(r) for r in w] == ['2.2732777998989693', '0.86831485369082398'] x, w = gauss_jacobi(3, -S.Half, S.Half, 17) assert [str(r) for r in x] == ['-0.62348980185873353', '0.22252093395631440', '0.90096886790241913'] assert [str(r) for r in w] == ['0.33795476356635433', '1.0973322242791115', '1.7063056657443274'] x, w = gauss_jacobi(4, -S.Half, S.Half, 17) assert [str(r) for r in x] == ['-0.76604444311897804', '-0.17364817766693035', '0.50000000000000000', '0.93969262078590838'] assert [str(r) for r in w] == ['0.16333179083642836', '0.57690240318269103', '1.0471975511965977', '1.3541609083740761'] x, w = gauss_jacobi(5, -S.Half, S.Half, 17) assert [str(r) for r in x] == ['-0.84125353283118117', '-0.41541501300188643', '0.14231483827328514', '0.65486073394528506', '0.95949297361449739'] assert [str(r) for r in w] == ['0.090675770007435371', '0.33391416373675607', '0.65248870981926643', '0.94525424081394926', '1.1192597692123861'] x, w = gauss_jacobi(1, 2, 3, 17) assert [str(r) for r in x] == ['0.14285714285714286'] assert [str(r) for r in w] == ['1.0666666666666667'] x, w = gauss_jacobi(2, 2, 3, 17) assert [str(r) for r in x] == ['0.46247529557426437', '-0.24025307335204215'] assert [str(r) for r in w] == ['0.58152042148828007', '0.48514624517838660'] x, w = gauss_jacobi(3, 2, 3, 17) assert [str(r) for r in x] == ['-0.46115870378089762', '0.10438533038323902', '0.62950064612493132'] assert [str(r) for r in w] == ['0.17937613502213266', '0.61595640991147154', '0.27133412173306246'] x, w = gauss_jacobi(4, 2, 3, 17) assert [str(r) for r in x] == ['-0.59903470850824782', '-0.14761105199952565', '0.32554377081188859', '0.72879429738819258'] assert [str(r) for r in w] == ['0.067809641836772187', '0.38956404952032481', '0.47995970868024150', '0.12933326662932816'] x, w = gauss_jacobi(5, 2, 3, 17) assert [str(r) for r in x] == ['-0.69045775012676106', '-0.32651993134900065', '0.082337849552034905', '0.47517887061283164', '0.79279429464422850'] assert [str(r) for r in w] ==['0.027410178066337099', '0.21291786060364828', '0.43908437944395081', '0.32220656547221822', '0.065047683080512268'] def test_jacobi_precise(): x, w = gauss_jacobi(3, -S.Half, S.Half, 40) assert [str(r) for r in x] == [ '-0.6234898018587335305250048840042398106323', '0.2225209339563144042889025644967947594664', '0.9009688679024191262361023195074450511659'] assert [str(r) for r in w] == [ '0.3379547635663543330553835737094171534907', '1.097332224279111467485302294320899710461', '1.706305665744327437921957515249186020246'] x, w = gauss_jacobi(3, 2, 3, 40) assert [str(r) for r in x] == [ '-0.4611587037808976179121958105554375981274', '0.1043853303832390210914918407615869143233', '0.6295006461249313240934312425211234110769'] assert [str(r) for r in w] == [ '0.1793761350221326596137764371503859752628', '0.6159564099114715430909548532229749439714', '0.2713341217330624639619353762933057474325'] sympy-0.7.4.1/sympy/integrals/tests/test_prde.py0000644000175000017500000002525612253362407022134 0ustar georgeskgeorgesk"""Most of these tests come from the examples in Bronstein's book.""" from sympy import Poly, Matrix, S, symbols, I from sympy.integrals.risch import DifferentialExtension from sympy.integrals.prde import (prde_normal_denom, prde_special_denom, prde_linear_constraints, constant_system, prde_spde, prde_no_cancel_b_large, prde_no_cancel_b_small, limited_integrate_reduce, limited_integrate, is_deriv_k, is_log_deriv_k_t_radical, parametric_log_deriv_heu, is_log_deriv_k_t_radical_in_field) from sympy.abc import x, t, n t0, t1, t2, t3, k = symbols('t:4 k') def test_prde_normal_denom(): DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1 + t**2, t)]}) fa = Poly(1, t) fd = Poly(x, t) G = [(Poly(t, t), Poly(1 + t**2, t)), (Poly(1, t), Poly(x + x*t**2, t))] assert prde_normal_denom(fa, fd, G, DE) == \ (Poly(x, t), (Poly(1, t), Poly(1, t)), [(Poly(x*t, t), Poly(t**2 + 1, t)), (Poly(1, t), Poly(t**2 + 1, t))], Poly(1, t)) G = [(Poly(t, t), Poly(t**2 + 2*t + 1, t)), (Poly(x*t, t), Poly(t**2 + 2*t + 1, t)), (Poly(x*t**2, t), Poly(t**2 + 2*t + 1, t))] DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t, t)]}) assert prde_normal_denom(Poly(x, t), Poly(1, t), G, DE) == \ (Poly(t + 1, t), (Poly((-1 + x)*t + x, t), Poly(1, t)), [(Poly(t, t), Poly(1, t)), (Poly(x*t, t), Poly(1, t)), (Poly(x*t**2, t), Poly(1, t))], Poly(t + 1, t)) def test_prde_special_denom(): a = Poly(t + 1, t) ba = Poly(t**2, t) bd = Poly(1, t) G = [(Poly(t, t), Poly(1, t)), (Poly(t**2, t), Poly(1, t)), (Poly(t**3, t), Poly(1, t))] DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t, t)]}) assert prde_special_denom(a, ba, bd, G, DE) == \ (Poly(t + 1, t), Poly(t**2, t), [(Poly(t, t), Poly(1, t)), (Poly(t**2, t), Poly(1, t)), (Poly(t**3, t), Poly(1, t))], Poly(1, t)) G = [(Poly(t, t), Poly(1, t)), (Poly(1, t), Poly(t, t))] assert prde_special_denom(Poly(1, t), Poly(t**2, t), Poly(1, t), G, DE) == \ (Poly(1, t), Poly(t**2 - 1, t), [(Poly(t**2, t), Poly(1, t)), (Poly(1, t), Poly(1, t))], Poly(t, t)) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(-2*x*t0, t0)]}) DE.decrement_level() G = [(Poly(t, t), Poly(t**2, t)), (Poly(2*t, t), Poly(t, t))] assert prde_special_denom(Poly(5*x*t + 1, t), Poly(t**2 + 2*x**3*t, t), Poly(t**3 + 2, t), G, DE) == \ (Poly(5*x*t + 1, t), Poly(0, t), [(Poly(t, t), Poly(t**2, t)), (Poly(2*t, t), Poly(t, t))], Poly(1, x)) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly((t**2 + 1)*2*x, t)]}) G = [(Poly(t + x, t), Poly(t*x, t)), (Poly(2*t, t), Poly(x**2, x))] assert prde_special_denom(Poly(5*x*t + 1, t), Poly(t**2 + 2*x**3*t, t), Poly(t**3, t), G, DE) == \ (Poly(5*x*t + 1, t), Poly(0, t), [(Poly(t + x, t), Poly(x*t, t)), (Poly(2*t, t, x), Poly(x**2, t, x))], Poly(1, t)) assert prde_special_denom(Poly(t + 1, t), Poly(t**2, t), Poly(t**3, t), G, DE) == \ (Poly(t + 1, t), Poly(0, t), [(Poly(t + x, t), Poly(x*t, t)), (Poly(2*t, t, x), Poly(x**2, t, x))], Poly(1, t)) def test_prde_linear_constraints(): DE = DifferentialExtension(extension={'D': [Poly(1, x)]}) G = [(Poly(2*x**3 + 3*x + 1, x), Poly(x**2 - 1, x)), (Poly(1, x), Poly(x - 1, x)), (Poly(1, x), Poly(x + 1, x))] assert prde_linear_constraints(Poly(1, x), Poly(0, x), G, DE) == \ ((Poly(2*x, x), Poly(0, x), Poly(0, x)), Matrix([[1, 1, -1], [5, 1, 1]])) G = [(Poly(t, t), Poly(1, t)), (Poly(t**2, t), Poly(1, t)), (Poly(t**3, t), Poly(1, t))] DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t, t)]}) assert prde_linear_constraints(Poly(t + 1, t), Poly(t**2, t), G, DE) == \ ((Poly(t, t), Poly(t**2, t), Poly(t**3, t)), Matrix()) G = [(Poly(2*x, t), Poly(t, t)), (Poly(-x, t), Poly(t, t))] DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1/x, t)]}) prde_linear_constraints(Poly(1, t), Poly(0, t), G, DE) == \ ((Poly(0, t), Poly(0, t)), Matrix([[2*x, -x]])) def test_constant_system(): A = Matrix([[-(x + 3)/(x - 1), (x + 1)/(x - 1), 1], [-x - 3, x + 1, x - 1], [2*(x + 3)/(x - 1), 0, 0]]) u = Matrix([(x + 1)/(x - 1), x + 1, 0]) DE = DifferentialExtension(extension={'D': [Poly(1, x)]}) assert constant_system(A, u, DE) == \ (Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 0], [0, 0, 1]]), Matrix([0, 1, 0, 0])) def test_prde_spde(): D = [Poly(x, t), Poly(-x*t, t)] DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1/x, t)]}) # TODO: when bound_degree() can handle this, test degree bound from that too assert prde_spde(Poly(t, t), Poly(-1/x, t), D, n, DE) == \ (Poly(t, t), Poly(0, t), [Poly(2*x, t), Poly(-x, t)], [Poly(-x**2, t), Poly(0, t)], n - 1) def test_prde_no_cancel(): # b large DE = DifferentialExtension(extension={'D': [Poly(1, x)]}) assert prde_no_cancel_b_large(Poly(1, x), [Poly(x**2, x), Poly(1, x)], 2, DE) == \ ([Poly(x**2 - 2*x + 2, x), Poly(1, x)], Matrix([[1, 0, -1, 0], [0, 1, 0, -1]])) assert prde_no_cancel_b_large(Poly(1, x), [Poly(x**3, x), Poly(1, x)], 3, DE) == \ ([Poly(x**3 - 3*x**2 + 6*x - 6, x), Poly(1, x)], Matrix([[1, 0, -1, 0], [0, 1, 0, -1]])) # b small # XXX: Is there a better example of a monomial with D.degree() > 2? DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t**3 + 1, t)]}) # My original q was t**4 + t + 1, but this solution implies q == t**4 # (c1 = 4), with some of the ci for the original q equal to 0. G = [Poly(t**6, t), Poly(x*t**5, t), Poly(t**3, t), Poly(x*t**2, t), Poly(1 + x, t)] assert prde_no_cancel_b_small(Poly(x*t, t), G, 4, DE) == \ ([Poly(t**4/4 - x/12*t**3 + x**2/24*t**2 + (-S(11)/12 - x**3/24)*t + x/24, t), Poly(x/3*t**3 - x**2/6*t**2 + (-S(1)/3 + x**3/6)*t - x/6, t), Poly(t, t), Poly(0, t), Poly(0, t)], Matrix([[1, 0, -1, 0, 0, 0, 0, 0, 0, 0], [0, 1, -S(1)/4, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, -1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, -1, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, -1, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, -1, 0], [0, 0, 0, 0, 1, 0, 0, 0, 0, -1]])) # TODO: Add test for deg(b) <= 0 with b small def test_limited_integrate_reduce(): DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1/x, t)]}) assert limited_integrate_reduce(Poly(x, t), Poly(t**2, t), [(Poly(x, t), Poly(t, t))], DE) == \ (Poly(t, t), Poly(-1/x, t), Poly(t, t), 1, (Poly(x, t), Poly(1, t)), [(Poly(-x*t, t), Poly(1, t))]) def test_limited_integrate(): DE = DifferentialExtension(extension={'D': [Poly(1, x)]}) G = [(Poly(x, x), Poly(x + 1, x))] assert limited_integrate(Poly(-(1 + x + 5*x**2 - 3*x**3), x), Poly(1 - x - x**2 + x**3, x), G, DE) == \ ((Poly(x**2 - x + 2, x), Poly(x - 1, x)), [2]) G = [(Poly(1, x), Poly(x, x))] assert limited_integrate(Poly(5*x**2, x), Poly(3, x), G, DE) == \ ((Poly(5*x**3/9, x), Poly(1, x)), [0]) def test_is_log_deriv_k_t_radical(): DE = DifferentialExtension(extension={'D': [Poly(1, x)], 'E_K': [], 'L_K': [], 'E_args': [], 'L_args': []}) assert is_log_deriv_k_t_radical(Poly(2*x, x), Poly(1, x), DE) is None DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(2*t1, t1), Poly(1/x, t2)], 'L_K': [2], 'E_K': [1], 'L_args': [x], 'E_args': [2*x]}) assert is_log_deriv_k_t_radical(Poly(x + t2/2, t2), Poly(1, t2), DE) == \ ([(t1, 1), (x, 1)], t1*x, 2, 0) # TODO: Add more tests DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t0, t0), Poly(1/x, t)], 'L_K': [2], 'E_K': [1], 'L_args': [x], 'E_args': [x]}) assert is_log_deriv_k_t_radical(Poly(x + t/2 + 3, t), Poly(1, t), DE) == \ ([(t0, 2), (x, 1)], x*t0**2, 2, 3) def test_is_deriv_k(): DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1/x, t1), Poly(1/(x + 1), t2)], 'L_K': [1, 2], 'E_K': [], 'L_args': [x, x + 1], 'E_args': []}) assert is_deriv_k(Poly(2*x**2 + 2*x, t2), Poly(1, t2), DE) == \ ([(t1, 1), (t2, 1)], t1 + t2, 2) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1/x, t1), Poly(t2, t2)], 'L_K': [1], 'E_K': [2], 'L_args': [x], 'E_args': [x]}) assert is_deriv_k(Poly(x**2*t2**3, t2), Poly(1, t2), DE) == \ ([(x, 3), (t1, 2)], 2*t1 + 3*x, 1) # TODO: Add more tests, including ones with exponentials DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(2/x, t1)], 'L_K': [1], 'E_K': [], 'L_args': [x**2], 'E_args': []}) assert is_deriv_k(Poly(x, t1), Poly(1, t1), DE) == \ ([(t1, S(1)/2)], t1/2, 1) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(2/(1 + x), t0)], 'L_K': [1], 'E_K': [], 'L_args': [x**2 + 2*x + 1], 'E_args': []}) assert is_deriv_k(Poly(1 + x, t0), Poly(1, t0), DE) == \ ([(t0, S(1)/2)], t0/2, 1) def test_is_log_deriv_k_t_radical_in_field(): # NOTE: any potential constant factor in the second element of the result # doesn't matter, because it cancels in Da/a. DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1/x, t)]}) assert is_log_deriv_k_t_radical_in_field(Poly(5*t + 1, t), Poly(2*t*x, t), DE) == \ (2, t*x**5) assert is_log_deriv_k_t_radical_in_field(Poly(2 + 3*t, t), Poly(5*x*t, t), DE) == \ (5, x**3*t**2) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(-t/x**2, t)]}) assert is_log_deriv_k_t_radical_in_field(Poly(-(1 + 2*t), t), Poly(2*x**2 + 2*x**2*t, t), DE) == \ (2, t + t**2) assert is_log_deriv_k_t_radical_in_field(Poly(-1, t), Poly(x**2, t), DE) == \ (1, t) assert is_log_deriv_k_t_radical_in_field(Poly(1, t), Poly(2*x**2, t), DE) == \ (2, 1/t) def test_parametric_log_deriv(): DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1/x, t)]}) assert parametric_log_deriv_heu(Poly(5*t**2 + t - 6, t), Poly(2*x*t**2, t), Poly(-1, t), Poly(x*t**2, t), DE) == \ (2, 6, t*x**5) sympy-0.7.4.1/sympy/integrals/tests/test_heurisch.py0000644000175000017500000002337512253362407023014 0ustar georgeskgeorgeskfrom sympy import Rational, sqrt, symbols, sin, exp, log, sinh, cosh, cos, pi, \ I, S, erf, tan, asin, asinh, acos, acosh, Function, Derivative, diff, simplify, \ LambertW, Eq, Piecewise, Symbol, Add, ratsimp from sympy.integrals.heurisch import components, heurisch, heurisch_wrapper from sympy.utilities.pytest import XFAIL, skip, slow x, y, z, nu = symbols('x,y,z,nu') f = Function('f') def test_components(): assert components(x*y, x) == set([x]) assert components(1/(x + y), x) == set([x]) assert components(sin(x), x) == set([sin(x), x]) assert components(sin(x)*sqrt(log(x)), x) == \ set([log(x), sin(x), sqrt(log(x)), x]) assert components(x*sin(exp(x)*y), x) == \ set([sin(y*exp(x)), x, exp(x)]) assert components(x**Rational(17, 54)/sqrt(sin(x)), x) == \ set([sin(x), x**Rational(1, 54), sqrt(sin(x)), x]) assert components(f(x), x) == \ set([x, f(x)]) assert components(Derivative(f(x), x), x) == \ set([x, f(x), Derivative(f(x), x)]) assert components(f(x)*diff(f(x), x), x) == \ set([x, f(x), Derivative(f(x), x), Derivative(f(x), x)]) def test_heurisch_polynomials(): assert heurisch(1, x) == x assert heurisch(x, x) == x**2/2 assert heurisch(x**17, x) == x**18/18 def test_heurisch_fractions(): assert heurisch(1/x, x) == log(x) assert heurisch(1/(2 + x), x) == log(x + 2) assert heurisch(1/(x + sin(y)), x) == log(x + sin(y)) # Up to a constant, where C = 5*pi*I/12, Mathematica gives identical # result in the first case. The difference is because sympy changes # signs of expressions without any care. # XXX ^ ^ ^ is this still correct? assert heurisch(5*x**5/( 2*x**6 - 5), x) in [5*log(2*x**6 - 5) / 12, 5*log(-2*x**6 + 5) / 12] assert heurisch(5*x**5/(2*x**6 + 5), x) == 5*log(2*x**6 + 5) / 12 assert heurisch(1/x**2, x) == -1/x assert heurisch(-1/x**5, x) == 1/(4*x**4) def test_heurisch_log(): assert heurisch(log(x), x) == x*log(x) - x assert heurisch(log(3*x), x) == -x + x*log(3) + x*log(x) assert heurisch(log(x**2), x) in [x*log(x**2) - 2*x, 2*x*log(x) - 2*x] def test_heurisch_exp(): assert heurisch(exp(x), x) == exp(x) assert heurisch(exp(-x), x) == -exp(-x) assert heurisch(exp(17*x), x) == exp(17*x) / 17 assert heurisch(x*exp(x), x) == x*exp(x) - exp(x) assert heurisch(x*exp(x**2), x) == exp(x**2) / 2 assert heurisch(exp(-x**2), x) is None assert heurisch(2**x, x) == 2**x/log(2) assert heurisch(x*2**x, x) == x*2**x/log(2) - 2**x*log(2)**(-2) def test_heurisch_trigonometric(): assert heurisch(sin(x), x) == -cos(x) assert heurisch(pi*sin(x) + 1, x) == x - pi*cos(x) assert heurisch(cos(x), x) == sin(x) assert heurisch(tan(x), x) in [ log(1 + tan(x)**2)/2, log(tan(x) + I) + I*x, log(tan(x) - I) - I*x, ] assert heurisch(sin(x)*sin(y), x) == -cos(x)*sin(y) assert heurisch(sin(x)*sin(y), y) == -cos(y)*sin(x) # gives sin(x) in answer when run via setup.py and cos(x) when run via py.test assert heurisch(sin(x)*cos(x), x) in [sin(x)**2 / 2, -cos(x)**2 / 2] assert heurisch(cos(x)/sin(x), x) == log(sin(x)) assert heurisch(x*sin(7*x), x) == sin(7*x) / 49 - x*cos(7*x) / 7 assert heurisch(1/pi/4 * x**2*cos(x), x) == 1/pi/4*(x**2*sin(x) - 2*sin(x) + 2*x*cos(x)) assert heurisch(acos(x/4) * asin(x/4), x) == 2*x - (sqrt(16 - x**2))*asin(x/4) \ + (sqrt(16 - x**2))*acos(x/4) + x*asin(x/4)*acos(x/4) def test_heurisch_hyperbolic(): assert heurisch(sinh(x), x) == cosh(x) assert heurisch(cosh(x), x) == sinh(x) assert heurisch(x*sinh(x), x) == x*cosh(x) - sinh(x) assert heurisch(x*cosh(x), x) == x*sinh(x) - cosh(x) assert heurisch( x*asinh(x/2), x) == x**2*asinh(x/2)/2 + asinh(x/2) - x*sqrt(4 + x**2)/4 def test_heurisch_mixed(): assert heurisch(sin(x)*exp(x), x) == exp(x)*sin(x)/2 - exp(x)*cos(x)/2 def test_heurisch_radicals(): assert heurisch(1/sqrt(x), x) == 2*sqrt(x) assert heurisch(1/sqrt(x)**3, x) == -2/sqrt(x) assert heurisch(sqrt(x)**3, x) == 2*sqrt(x)**5/5 assert heurisch(sin(x)*sqrt(cos(x)), x) == -2*sqrt(cos(x))**3/3 y = Symbol('y') assert heurisch(sin(y*sqrt(x)), x) == 2/y**2*sin(y*sqrt(x)) - \ 2*sqrt(x)*cos(y*sqrt(x))/y assert heurisch_wrapper(sin(y*sqrt(x)), x) == Piecewise( (0, Eq(y, 0)), (-2*sqrt(x)*cos(sqrt(x)*y)/y + 2*sin(sqrt(x)*y)/y**2, True)) y = Symbol('y', positive=True) assert heurisch_wrapper(sin(y*sqrt(x)), x) == 2/y**2*sin(y*sqrt(x)) - \ 2*sqrt(x)*cos(y*sqrt(x))/y def test_heurisch_special(): assert heurisch(erf(x), x) == x*erf(x) + exp(-x**2)/sqrt(pi) assert heurisch(exp(-x**2)*erf(x), x) == sqrt(pi)*erf(x)**2 / 4 def test_heurisch_symbolic_coeffs(): assert heurisch(1/(x + y), x) == log(x + y) assert heurisch(1/(x + sqrt(2)), x) == log(x + sqrt(2)) assert simplify(diff(heurisch(log(x + y + z), y), y)) == log(x + y + z) def test_heurisch_symbolic_coeffs_1130(): y = Symbol('y') assert heurisch_wrapper(1/(x**2 + y), x) == Piecewise( (-1/x, Eq(y, 0)), (-I*log(x - I*sqrt(y))/(2*sqrt(y)) + I*log(x + I*sqrt(y))/(2*sqrt(y)), True)) y = Symbol('y', positive=True) assert heurisch_wrapper(1/(x**2 + y), x) in [I/sqrt(y)*log(x + sqrt(-y))/2 - I/sqrt(y)*log(x - sqrt(-y))/2, I*log(x + I*sqrt(y)) / (2*sqrt(y)) - I*log(x - I*sqrt(y))/(2*sqrt(y))] def test_heurisch_hacking(): assert heurisch(sqrt(1 + 7*x**2), x, hints=[]) == \ x*sqrt(1 + 7*x**2)/2 + sqrt(7)*asinh(sqrt(7)*x)/14 assert heurisch(sqrt(1 - 7*x**2), x, hints=[]) == \ x*sqrt(1 - 7*x**2)/2 + sqrt(7)*asin(sqrt(7)*x)/14 assert heurisch(1/sqrt(1 + 7*x**2), x, hints=[]) == \ sqrt(7)*asinh(sqrt(7)*x)/7 assert heurisch(1/sqrt(1 - 7*x**2), x, hints=[]) == \ sqrt(7)*asin(sqrt(7)*x)/7 assert heurisch(exp(-7*x**2), x, hints=[]) == \ sqrt(7*pi)*erf(sqrt(7)*x)/14 assert heurisch(1/sqrt(9 - 4*x**2), x, hints=[]) == \ asin(2*x/3)/2 assert heurisch(1/sqrt(9 + 4*x**2), x, hints=[]) == \ asinh(2*x/3)/2 def test_heurisch_function(): assert heurisch(f(x), x) is None @XFAIL def test_heurisch_function_derivative(): # TODO: it looks like this used to work just by coincindence and # thanks to sloppy implementation. Investigate why this used to # work at all and if support for this can be restored. df = diff(f(x), x) assert heurisch(f(x)*df, x) == f(x)**2/2 assert heurisch(f(x)**2*df, x) == f(x)**3/3 assert heurisch(df/f(x), x) == log(f(x)) def test_heurisch_wrapper(): f = 1/(y + x) assert heurisch_wrapper(f, x) == log(x + y) f = 1/(y - x) assert heurisch_wrapper(f, x) == -log(x - y) f = 1/((y - x)*(y + x)) assert heurisch_wrapper(f, x) == \ Piecewise((1/x, Eq(y, 0)), (log(x + y)/2/y - log(x - y)/2/y, True)) # issue 3827 f = sqrt(x**2/((y - x)*(y + x))) assert heurisch_wrapper(f, x) == x*sqrt(x**2)*sqrt(1/(-x**2 + y**2)) \ - y**2*sqrt(x**2)*sqrt(1/(-x**2 + y**2))/x def test_issue510(): assert heurisch(1/(x * (1 + log(x)**2)), x) == I*log(log(x) + I)/2 - \ I*log(log(x) - I)/2 ### These are examples from the Poor Man's Integrator ### http://www-sop.inria.fr/cafe/Manuel.Bronstein/pmint/examples/ def test_pmint_rat(): # TODO: heurisch() is off by a constant: -3/4. Possibly different permutation # would give the optimal result? def drop_const(expr, x): if expr.is_Add: return Add(*[ arg for arg in expr.args if arg.has(x) ]) else: return expr f = (x**7 - 24*x**4 - 4*x**2 + 8*x - 8)/(x**8 + 6*x**6 + 12*x**4 + 8*x**2) g = (4 + 8*x**2 + 6*x + 3*x**3)/(x**5 + 4*x**3 + 4*x) + log(x) assert drop_const(ratsimp(heurisch(f, x)), x) == g def test_pmint_trig(): f = (x - tan(x)) / tan(x)**2 + tan(x) g = -x**2/2 - x/tan(x) + log(tan(x)**2 + 1)/2 assert heurisch(f, x) == g @slow # 8 seconds on 3.4 GHz def test_pmint_logexp(): f = (1 + x + x*exp(x))*(x + log(x) + exp(x) - 1)/(x + log(x) + exp(x))**2/x g = log(x**2 + 2*x*exp(x) + 2*x*log(x) + exp(2*x) + 2*exp(x)*log(x) + log(x)**2)/2 + 1/(x + exp(x) + log(x)) # TODO: Optimal solution is g = 1/(x + log(x) + exp(x)) + log(x + log(x) + exp(x)), # but SymPy requires a lot of guidance to properly simplify heurisch() output. assert ratsimp(heurisch(f, x)) == g @slow # 8 seconds on 3.4 GHz @XFAIL # there's a hash dependent failure lurking here def test_pmint_erf(): f = exp(-x**2)*erf(x)/(erf(x)**3 - erf(x)**2 - erf(x) + 1) g = sqrt(pi)*log(erf(x) - 1)/8 - sqrt(pi)*log(erf(x) + 1)/8 - sqrt(pi)/(4*erf(x) - 4) assert ratsimp(heurisch(f, x)) == g def test_pmint_LambertW(): f = LambertW(x) g = x*LambertW(x) - x + x/LambertW(x) assert heurisch(f, x) == g @XFAIL def test_pmint_besselj(): # TODO: in both cases heurisch() gives None. Wrong besselj() derivative? f = besselj(nu + 1, x)/besselj(nu, x) g = nu*log(x) - log(besselj(nu, x)) assert heurisch(f, x) == g f = (nu*besselj(nu, x) - x*besselj(nu + 1, x))/x g = besselj(nu, x) assert heurisch(f, x) == g @slow # 110 seconds on 3.4 GHz def test_pmint_WrightOmega(): def omega(x): return LambertW(exp(x)) f = (1 + omega(x) * (2 + cos(omega(x)) * (x + omega(x))))/(1 + omega(x))/(x + omega(x)) g = log(x + LambertW(exp(x))) + sin(LambertW(exp(x))) assert heurisch(f, x) == g # TODO: convert the rest of PMINT tests: # Airy functions # f = (x - AiryAi(x)*AiryAi(1, x)) / (x**2 - AiryAi(x)**2) # g = Rational(1,2)*ln(x + AiryAi(x)) + Rational(1,2)*ln(x - AiryAi(x)) # f = x**2 * AiryAi(x) # g = -AiryAi(x) + AiryAi(1, x)*x # Whittaker functions # f = WhittakerW(mu + 1, nu, x) / (WhittakerW(mu, nu, x) * x) # g = x/2 - mu*ln(x) - ln(WhittakerW(mu, nu, x)) sympy-0.7.4.1/sympy/integrals/tests/test_failing_integrals.py0000644000175000017500000000737212253362407024662 0ustar georgeskgeorgesk# A collection of failing integrals from the issues. from __future__ import division from sympy import ( integrate, Integral, exp, I, oo, pi, sign, sqrt, Rational, Symbol, sin, cos, tan, S, log, Function, gamma, sinh, ) from sympy.utilities.pytest import XFAIL, SKIP, slow from sympy.abc import x, k, c, y, R, b, h, a, m, A, z, t import signal class TimeOutError(Exception): pass def timeout(signum, frame, time): raise TimeOutError("Timed out after %d seconds" % time) def run_with_timeout(test, time): # Set the signal handler and a 5-second alarm signal.signal(signal.SIGALRM, lambda s, f: timeout(s, f, time)) signal.alarm(time) r = eval(test) signal.alarm(0) # Disable the alarm return r @SKIP("Too slow for @slow") @XFAIL def test_issue_781(): # integrate_hyperexponential(Poly(t*2*(1 - t0**2)*t0*(x**3 + x**2), t), Poly((1 + t0**2)**2*2*(x**2 + x + 1), t), [Poly(1, x), Poly(1 + t0**2, t0), Poly(t, t)], [x, t0, t], [exp, tan]) assert not integrate(exp(x)*cos(2*x)*sin(2*x) * (x**3 + x**2)/(2*(x**2 + x + 1)), x).has(Integral) @XFAIL def test_issue_1113(): assert not integrate(sign(x), x).has(Integral) @XFAIL def test_issue_1135(): assert not integrate(1/sqrt(1 + tan(x)**2)).has(Integral) @XFAIL def test_issue_1227(): assert integrate(((h*(x - R + b))/b)*sqrt(R**2 - x**2), (x, R - b, R)).has(Integral) @XFAIL def test_issue_1392(): assert not integrate(x*sqrt(x**2 + 2*x + 4), x).has(Integral) @XFAIL def test_issue_1393(): assert not integrate(x**2 * sqrt(5 - x**2), x).has(Integral) @XFAIL @slow def test_issue_1412(): # This works, but gives a complicated answer. The correct answer is x - cos(x). # The last one is what Maple gives. It is also quite slow. assert integrate(cos(x)**2 / (1 - sin(x))) in [x - cos(x), 1 - cos(x) + x, -2/(tan((S(1)/2)*x)**2 + 1) + x] @XFAIL def test_issue_1415(): # The correct answer is 2*sin(x) assert not integrate(sin(2*x)/ sin(x)).has(Integral) @XFAIL def test_issue_1426(): # Warning: takes a long time assert not integrate((x**m * (1 - x)**n * (a + b*x + c*x**2))/(1 + x**2), (x, 0, 1)).has(Integral) @XFAIL @slow def test_issue_1441(): # Note, this integral is probably nonelementary assert not integrate( (sin(1/x) - x*exp(x)) / ((-sin(1/x) + x*exp(x))*x + x*sin(1/x)), x).has(Integral) @XFAIL def test_issue_1452(): assert integrate(1/(x*sqrt(1 - x**2)), x).has(Integral) @XFAIL def test_issue_1638a(): # Implementation of Si() assert integrate(sin(x)/x, x).has(Integral) @XFAIL def test_issue_1638b(): assert integrate(sin(x)/x, (x, -oo, oo)) == pi/2 @XFAIL @slow def test_issue_1792(): # Requires the hypergeometric function. assert not integrate(cos(x)**y, x).has(Integral) @XFAIL def test_issue_1796a(): assert not integrate(exp(2*b*x)*exp(-a*x**2), x).has(Integral) @XFAIL def test_issue_1796b(): assert not integrate(exp(2*b*x)*exp(-a*x**2), (x, -oo, 0)).has(Integral) @XFAIL def test_issue_1796c(): assert not integrate(exp(2*b*x)*exp(-a*x**2), (x, -oo, oo)).has(Integral) @XFAIL def test_issue_1796d(): assert not integrate(exp(2*b*x)*exp(-a*x**2), (x, 0, oo)).has(Integral) @XFAIL @slow def test_issue_1842(): assert not integrate(sqrt(1 + sinh(x/20)**2), (x, -25, 25)).has(Integral) @XFAIL @slow def test_issue_1851(): # Problem is with exception assert not integrate((-60*exp(x) - 19.2*exp(4*x))*exp(4*x), x).has(Integral) @XFAIL @slow def test_issue_1869(): assert not integrate(sin(log(x**2))).has(Integral) @XFAIL @slow def test_issue_1893(): # Nonelementary integral. Requires hypergeometric/Meijer-G handling. assert not integrate(log(x) * x**(k - 1) * exp(-x) / gamma(k), (x, 0, oo)).has(Integral) sympy-0.7.4.1/sympy/integrals/tests/test_rationaltools.py0000644000175000017500000000762312253362407024072 0ustar georgeskgeorgeskfrom sympy import S, symbols, I, atan, log, Poly, sqrt, simplify, integrate from sympy.integrals.rationaltools import ratint, ratint_logpart, log_to_atan from sympy.abc import a, b, x, t half = S(1)/2 def test_ratint(): assert ratint(S(0), x) == 0 assert ratint(S(7), x) == 7*x assert ratint(x, x) == x**2/2 assert ratint(2*x, x) == x**2 assert ratint(-2*x, x) == -x**2 assert ratint(8*x**7 + 2*x + 1, x) == x**8 + x**2 + x f = S(1) g = x + 1 assert ratint(f / g, x) == log(x + 1) assert ratint((f, g), x) == log(x + 1) f = x**3 - x g = x - 1 assert ratint(f/g, x) == x**3/3 + x**2/2 f = x g = (x - a)*(x + a) assert ratint(f/g, x) == log(x**2 - a**2)/2 f = S(1) g = x**2 + 1 assert ratint(f/g, x, real=None) == atan(x) assert ratint(f/g, x, real=True) == atan(x) assert ratint(f/g, x, real=False) == I*log(x + I)/2 - I*log(x - I)/2 f = S(36) g = x**5 - 2*x**4 - 2*x**3 + 4*x**2 + x - 2 assert ratint(f/g, x) == \ -4*log(x + 1) + 4*log(x - 2) + (12*x + 6)/(x**2 - 1) f = x**4 - 3*x**2 + 6 g = x**6 - 5*x**4 + 5*x**2 + 4 assert ratint(f/g, x) == \ atan(x) + atan(x**3) + atan(x/2 - 3*x**S(3)/2 + S(1)/2*x**5) f = x**7 - 24*x**4 - 4*x**2 + 8*x - 8 g = x**8 + 6*x**6 + 12*x**4 + 8*x**2 assert ratint(f/g, x) == \ (4 + 6*x + 8*x**2 + 3*x**3)/(4*x + 4*x**3 + x**5) + log(x) assert ratint((x**3*f)/(x*g), x) == \ -(12 - 16*x + 6*x**2 - 14*x**3)/(4 + 4*x**2 + x**4) - \ 5*sqrt(2)*atan(x*sqrt(2)/2) + S(1)/2*x**2 - 3*log(2 + x**2) f = x**5 - x**4 + 4*x**3 + x**2 - x + 5 g = x**4 - 2*x**3 + 5*x**2 - 4*x + 4 assert ratint(f/g, x) == \ x + S(1)/2*x**2 + S(1)/2*log(2 - x + x**2) - (4*x - 9)/(14 - 7*x + 7*x**2) + \ 13*sqrt(7)*atan(-S(1)/7*sqrt(7) + 2*x*sqrt(7)/7)/49 assert ratint(1/(x**2 + x + 1), x) == \ 2*sqrt(3)*atan(sqrt(3)/3 + 2*x*sqrt(3)/3)/3 assert ratint(1/(x**3 + 1), x) == \ -log(1 - x + x**2)/6 + log(1 + x)/3 + sqrt(3)*atan(-sqrt(3) /3 + 2*x*sqrt(3)/3)/3 assert ratint(1/(x**2 + x + 1), x, real=False) == \ -I*3**half*log(half + x - half*I*3**half)/3 + \ I*3**half*log(half + x + half*I*3**half)/3 assert ratint(1/(x**3 + 1), x, real=False) == log(1 + x)/3 + \ (-S(1)/6 + I*3**half/6)*log(-half + x + I*3**half/2) + \ (-S(1)/6 - I*3**half/6)*log(-half + x - I*3**half/2) # Issue 1892 assert ratint(1/(x*(a + b*x)**3), x) == \ (3*a + 2*b*x)/(2*a**4 + 4*a**3*b*x + 2*a**2*b**2*x**2) + ( log(x) - log(a/b + x))/a**3 assert ratint(x/(1 - x**2), x) == -log(x**2 - 1)/2 assert ratint(-x/(1 - x**2), x) == log(x**2 - 1)/2 assert ratint((x/4 - 4/(1 - x)).diff(x), x) == x/4 + 4/(x - 1) ans = atan(x) assert ratint(1/(x**2 + 1), x, symbol=x) == ans assert ratint(1/(x**2 + 1), x, symbol='x') == ans assert ratint(1/(x**2 + 1), x, symbol=a) == ans def test_ratint_logpart(): assert ratint_logpart(x, x**2 - 9, x, t) == \ [(Poly(x**2 - 9, x), Poly(-2*t + 1, t))] assert ratint_logpart(x**2, x**3 - 5, x, t) == \ [(Poly(x**3 - 5, x), Poly(-3*t + 1, t))] def test_issue_2315(): assert ratint(1/(x**2 + 16), x) == atan(x/4)/4 def test_issue_2150(): assert ratint( 1/(x**2 + a**2), x) == (-I*log(-I*a + x)/2 + I*log(I*a + x)/2)/a def test_issue_2718(): a, b, c = symbols('a,b,c', positive=True) assert simplify(ratint(a/(b*c*x**2 + a**2 + b*a), x)) == \ sqrt(a)*atan(sqrt( b)*sqrt(c)*x/(sqrt(a)*sqrt(a + b)))/(sqrt(b)*sqrt(c)*sqrt(a + b)) def test_issue_2882(): u = symbols('u') assert integrate(1/(u**2 + 1)) == atan(u) def test_log_to_atan(): f, g = (Poly(x + S(1)/2, x, domain='QQ'), Poly(sqrt(3)/2, x, domain='EX')) fg_ans = 2*atan(2*sqrt(3)*x/3 + sqrt(3)/3) assert log_to_atan(f, g) == fg_ans assert log_to_atan(g, f) == -fg_ans sympy-0.7.4.1/sympy/integrals/tests/test_deltafunctions.py0000644000175000017500000000626612253362407024224 0ustar georgeskgeorgeskfrom sympy import cos, DiracDelta, Heaviside, Function, pi, S, sin, symbols from sympy.integrals.deltafunctions import change_mul, deltaintegrate f = Function("f") x_1, x_2, x, y, z = symbols("x_1 x_2 x y z") def test_change_mul(): assert change_mul(x, x) == x assert change_mul(x*y, x) == (None, None) assert change_mul(x*y*DiracDelta(x), x) == (DiracDelta(x), x*y) assert change_mul(x*y*DiracDelta(x)*DiracDelta(y), x) == \ (DiracDelta(x), x*y*DiracDelta(y)) assert change_mul(DiracDelta(x)**2, x) == \ (DiracDelta(x), DiracDelta(x)) assert change_mul(y*DiracDelta(x)**2, x) == \ (DiracDelta(x), y*DiracDelta(x)) def test_deltaintegrate(): assert deltaintegrate(x, x) is None assert deltaintegrate(x + DiracDelta(x), x) is None assert deltaintegrate(DiracDelta(x, 0), x) == Heaviside(x) for n in range(10): assert deltaintegrate(DiracDelta(x, n + 1), x) == DiracDelta(x, n) assert deltaintegrate(DiracDelta(x), x) == Heaviside(x) assert deltaintegrate(DiracDelta(-x), x) == Heaviside(x) assert deltaintegrate(DiracDelta(x - y), x) == Heaviside(x - y) assert deltaintegrate(DiracDelta(y - x), x) == Heaviside(x - y) assert deltaintegrate(x*DiracDelta(x), x) == 0 assert deltaintegrate((x - y)*DiracDelta(x - y), x) == 0 assert deltaintegrate(DiracDelta(x)**2, x) == DiracDelta(0)*Heaviside(x) assert deltaintegrate(y*DiracDelta(x)**2, x) == \ y*DiracDelta(0)*Heaviside(x) assert deltaintegrate(DiracDelta(x, 1)**2, x) is None assert deltaintegrate(y*DiracDelta(x, 1)**2, x) is None assert deltaintegrate(DiracDelta(x) * f(x), x) == f(0) * Heaviside(x) assert deltaintegrate(DiracDelta(-x) * f(x), x) == f(0) * Heaviside(x) assert deltaintegrate(DiracDelta(x - 1) * f(x), x) == f(1) * Heaviside(x - 1) assert deltaintegrate(DiracDelta(1 - x) * f(x), x) == f(1) * Heaviside(x - 1) assert deltaintegrate(DiracDelta(x**2 + x - 2), x) == \ Heaviside(x - 1)/3 + Heaviside(x + 2)/3 p = cos(x)*(DiracDelta(x) + DiracDelta(x**2 - 1))*sin(x)*(x - pi) assert deltaintegrate(p, x) - (-pi*(cos(1)*Heaviside(-1 + x)*sin(1)/2 - \ cos(1)*Heaviside(1 + x)*sin(1)/2) + \ cos(1)*Heaviside(1 + x)*sin(1)/2 + \ cos(1)*Heaviside(-1 + x)*sin(1)/2) == 0 p = x_2*DiracDelta(x - x_2)*DiracDelta(x_2 - x_1) assert deltaintegrate(p, x_2) == x*DiracDelta(x - x_1)*Heaviside(x_2 - x) p = x*y**2*z*DiracDelta(y - x)*DiracDelta(y - z)*DiracDelta(x - z) assert deltaintegrate(p, y) == x**3*z*DiracDelta(x - z)**2*Heaviside(y - x) assert deltaintegrate((x + 1)*DiracDelta(2*x), x) == S(1)/2 * Heaviside(x) assert deltaintegrate((x + 1)*DiracDelta(2*x/3 + 4/S(9)), x) == \ S(1)/2 * Heaviside(x + S(2)/3) a, b, c = symbols('a b c', commutative=False) assert deltaintegrate(DiracDelta(x - y)*f(x - b)*f(x - a), x) == \ f(y - b)*f(y - a)*Heaviside(x - y) p = f(x - a)*DiracDelta(x - y)*f(x - c)*f(x - b) assert deltaintegrate(p, x) == f(y - a)*f(y - c)*f(y - b)*Heaviside(x - y) p = DiracDelta(x - z)*f(x - b)*f(x - a)*DiracDelta(x - y) assert deltaintegrate(p, x) == DiracDelta(y - z)*f(y - b)*f(y - a) * \ Heaviside(x - y) sympy-0.7.4.1/sympy/integrals/tests/test_integrals.py0000644000175000017500000010756112253362407023172 0ustar georgeskgeorgeskfrom sympy import ( Abs, acos, acosh, Add, adjoint, asin, asinh, atan, Ci, conjugate, cos, Derivative, diff, DiracDelta, E, exp, erf, erfi, EulerGamma, factor, Function, Heaviside, I, Integral, integrate, Interval, Lambda, LambertW, log, Matrix, O, oo, pi, Piecewise, Poly, Rational, S, simplify, sin, sqrt, sstr, Sum, Symbol, symbols, sympify, terms_gcd, transpose, trigsimp, Tuple, nan, And, Eq, Or ) from sympy.integrals.risch import NonElementaryIntegral from sympy.utilities.pytest import XFAIL, raises, slow from sympy.physics import units x, y, a, t, x_1, x_2, z = symbols('x y a t x_1 x_2 z') n = Symbol('n', integer=True) f = Function('f') def diff_test(i): """Return the set of symbols, s, which were used in testing that i.diff(s) agrees with i.doit().diff(s). If there is an error then the assertion will fail, causing the test to fail.""" syms = i.free_symbols for s in syms: assert (i.diff(s).doit() - i.doit().diff(s)).expand() == 0 return syms def test_improper_integral(): assert integrate(log(x), (x, 0, 1)) == -1 assert integrate(x**(-2), (x, 1, oo)) == 1 def test_constructor(): # this is shared by Sum, so testing Integral's constructor # is equivalent to testing Sum's s1 = Integral(n, n) assert s1.limits == (Tuple(n),) s2 = Integral(n, (n,)) assert s2.limits == (Tuple(n),) s3 = Integral(Sum(x, (x, 1, y))) assert s3.limits == (Tuple(y),) s4 = Integral(n, Tuple(n,)) assert s4.limits == (Tuple(n),) s5 = Integral(n, (n, Interval(1, 2))) assert s5.limits == (Tuple(n, 1, 2),) def test_basics(): assert Integral(0, x) != 0 assert Integral(x, (x, 1, 1)) != 0 assert Integral(oo, x) != oo assert Integral(S.NaN, x) == S.NaN assert diff(Integral(y, y), x) == 0 assert diff(Integral(x, (x, 0, 1)), x) == 0 assert diff(Integral(x, x), x) == x assert diff(Integral(t, (t, 0, x)), x) == x + Integral(0, (t, 0, x)) e = (t + 1)**2 assert diff(integrate(e, (t, 0, x)), x) == \ diff(Integral(e, (t, 0, x)), x).doit().expand() == \ ((1 + x)**2).expand() assert diff(integrate(e, (t, 0, x)), t) == \ diff(Integral(e, (t, 0, x)), t) == 0 assert diff(integrate(e, (t, 0, x)), a) == \ diff(Integral(e, (t, 0, x)), a) == 0 assert diff(integrate(e, t), a) == diff(Integral(e, t), a) == 0 assert integrate(e, (t, a, x)).diff(x) == \ Integral(e, (t, a, x)).diff(x).doit().expand() assert Integral(e, (t, a, x)).diff(x).doit() == ((1 + x)**2) assert integrate(e, (t, x, a)).diff(x).doit() == (-(1 + x)**2).expand() assert integrate(t**2, (t, x, 2*x)).diff(x) == 7*x**2 assert Integral(x, x).atoms() == set([x]) assert Integral(f(x), (x, 0, 1)).atoms() == set([S(0), S(1), x]) assert diff_test(Integral(x, (x, 3*y))) == set([y]) assert diff_test(Integral(x, (a, 3*y))) == set([x, y]) # sum integral of terms assert integrate(y + x + exp(x), x) == x*y + x**2/2 + exp(x) assert Integral(x).is_commutative n = Symbol('n', commutative=False) assert Integral(n + x, x).is_commutative is False def test_basics_multiple(): assert diff_test(Integral(x, (x, 3*x, 5*y), (y, x, 2*x))) == set([x]) assert diff_test(Integral(x, (x, 5*y), (y, x, 2*x))) == set([x]) assert diff_test(Integral(x, (x, 5*y), (y, y, 2*x))) == set([x, y]) assert diff_test(Integral(y, y, x)) == set([x, y]) assert diff_test(Integral(y*x, x, y)) == set([x, y]) assert diff_test(Integral(x + y, y, (y, 1, x))) == set([x]) assert diff_test(Integral(x + y, (x, x, y), (y, y, x))) == set([x, y]) def test_conjugate_transpose(): A, B = symbols("A B", commutative=False) x = Symbol("x", complex=True) p = Integral(A*B, (x,)) assert p.adjoint().doit() == p.doit().adjoint() assert p.conjugate().doit() == p.doit().conjugate() assert p.transpose().doit() == p.doit().transpose() x = Symbol("x", real=True) p = Integral(A*B, (x,)) assert p.adjoint().doit() == p.doit().adjoint() assert p.conjugate().doit() == p.doit().conjugate() assert p.transpose().doit() == p.doit().transpose() def test_integration(): assert integrate(0, (t, 0, x)) == 0 assert integrate(3, (t, 0, x)) == 3*x assert integrate(t, (t, 0, x)) == x**2/2 assert integrate(3*t, (t, 0, x)) == 3*x**2/2 assert integrate(3*t**2, (t, 0, x)) == x**3 assert integrate(1/t, (t, 1, x)) == log(x) assert integrate(-1/t**2, (t, 1, x)) == 1/x - 1 assert integrate(t**2 + 5*t - 8, (t, 0, x)) == x**3/3 + 5*x**2/2 - 8*x assert integrate(x**2, x) == x**3/3 assert integrate((3*t*x)**5, x) == (3*t)**5 * x**6 / 6 b = Symbol("b") c = Symbol("c") assert integrate(a*t, (t, 0, x)) == a*x**2/2 assert integrate(a*t**4, (t, 0, x)) == a*x**5/5 assert integrate(a*t**2 + b*t + c, (t, 0, x)) == a*x**3/3 + b*x**2/2 + c*x def test_multiple_integration(): assert integrate((x**2)*(y**2), (x, 0, 1), (y, -1, 2)) == Rational(1) assert integrate((y**2)*(x**2), x, y) == Rational(1, 9)*(x**3)*(y**3) assert integrate(1/(x + 3)/(1 + x)**3, x) == \ -S(1)/8*log(3 + x) + S(1)/8*log(1 + x) + x/(4 + 8*x + 4*x**2) def test_issue433(): assert integrate(exp(-x), (x, 0, oo)) == 1 def test_issue461(): assert integrate(sqrt(x)**3, x) == 2*sqrt(x)**5/5 assert integrate(sqrt(x), x) == 2*sqrt(x)**3/3 assert integrate(1/sqrt(x)**3, x) == -2/sqrt(x) def test_integrate_poly(): p = Poly(x + x**2*y + y**3, x, y) qx = integrate(p, x) qy = integrate(p, y) assert isinstance(qx, Poly) is True assert isinstance(qy, Poly) is True assert qx.gens == (x, y) assert qy.gens == (x, y) assert qx.as_expr() == x**2/2 + x**3*y/3 + x*y**3 assert qy.as_expr() == x*y + x**2*y**2/2 + y**4/4 def test_integrate_poly_defined(): p = Poly(x + x**2*y + y**3, x, y) Qx = integrate(p, (x, 0, 1)) Qy = integrate(p, (y, 0, pi)) assert isinstance(Qx, Poly) is True assert isinstance(Qy, Poly) is True assert Qx.gens == (y,) assert Qy.gens == (x,) assert Qx.as_expr() == Rational(1, 2) + y/3 + y**3 assert Qy.as_expr() == pi**4/4 + pi*x + pi**2*x**2/2 def test_integrate_omit_var(): y = Symbol('y') assert integrate(x) == x**2/2 raises(ValueError, lambda: integrate(2)) raises(ValueError, lambda: integrate(x*y)) def test_integrate_poly_accurately(): y = Symbol('y') assert integrate(x*sin(y), x) == x**2*sin(y)/2 # when passed to risch_norman, this will be a CPU hog, so this really # checks, that integrated function is recognized as polynomial assert integrate(x**1000*sin(y), x) == x**1001*sin(y)/1001 def test_issue536(): y = Symbol('y') assert integrate(x**2, y) == x**2*y assert integrate(x**2, (y, -1, 1)) == 2*x**2 # works in sympy and py.test but hangs in `setup.py test` def test_integrate_linearterm_pow(): # check integrate((a*x+b)^c, x) -- #400 y = Symbol('y', positive=True) # TODO: Remove conds='none' below, let the assumption take care of it. assert integrate(x**y, x, conds='none') == x**(y + 1)/(y + 1) assert integrate((exp(y)*x + 1/y)**(1 + sin(y)), x, conds='none') == \ exp(-y)*(exp(y)*x + 1/y)**(2 + sin(y)) / (2 + sin(y)) def test_issue519(): assert integrate(pi*sqrt(x), x) == 2*pi*sqrt(x)**3/3 assert integrate(pi*sqrt(x) + E*sqrt(x)**3, x) == \ 2*pi*sqrt(x)**3/3 + 2*E *sqrt(x)**5/5 def test_issue524(): assert integrate(cos((n + 1)*x), x) == Piecewise( (x, Eq(n + 1, 0)), (sin((n + 1)*x)/(n + 1), True)) assert integrate(cos((n - 1)*x), x) == Piecewise( (x, Eq(n - 1, 0)), (sin((n - 1)*x)/(n - 1), True)) assert integrate(cos((n + 1)*x) + cos((n - 1)*x), x) == \ Piecewise((x, Eq(n + 1, 0)), (sin((n + 1)*x)/(n + 1), True)) + \ Piecewise((x, Eq(n - 1, 0)), (sin((n - 1)*x)/(n - 1), True)) def test_issue565(): n = Symbol('n', integer=True, nonzero=True) assert integrate(-1./2 * x * sin(n * pi * x/2), [x, -2, 0]) == \ 2*cos(pi*n)/(pi*n) assert integrate(-Rational(1)/2 * x * sin(n * pi * x/2), [x, -2, 0]) == \ 2*cos(pi*n)/(pi*n) def test_issue580(): # definite integration of rational functions gives wrong answers assert NS(Integral(1/(x**2 - 8*x + 17), (x, 2, 4))) == '1.10714871779409' def test_issue587(): # remove this when fresnel itegrals are implemented from sympy import expand_func, fresnels assert expand_func(integrate(sin(x**2), x)) == \ sqrt(2)*sqrt(pi)*fresnels(sqrt(2)*x/sqrt(pi))/2 def test_integrate_units(): m = units.m s = units.s assert integrate(x * m/s, (x, 1*s, 5*s)) == 12*m*s def test_transcendental_functions(): assert integrate(LambertW(2*x), x) == \ -x + x*LambertW(2*x) + x/LambertW(2*x) def test_issue641(): f = 4*log(x) - 2*log(x)**2 fid = diff(integrate(f, x), x) assert abs(f.subs(x, 42).evalf() - fid.subs(x, 42).evalf()) < 1e-10 def test_issue689(): assert integrate(1/(1 + x**2), x) == atan(x) def test_issue853(): f = sin(x) assert integrate(f, x) == -cos(x) raises(ValueError, lambda: integrate(f, 2*x)) def test_issue1417(): assert integrate(2**x - 2*x, x) == 2**x/log(2) - x**2 def test_matrices(): M = Matrix(2, 2, lambda i, j: (i + j + 1)*sin((i + j + 1)*x)) assert integrate(M, x) == Matrix([ [-cos(x), -cos(2*x)], [-cos(2*x), -cos(3*x)], ]) # issue1012 def test_integrate_functions(): assert integrate(f(x), x) == Integral(f(x), x) assert integrate(f(x), (x, 0, 1)) == Integral(f(x), (x, 0, 1)) assert integrate(f(x)*diff(f(x), x), x) == f(x)**2/2 assert integrate(diff(f(x), x) / f(x), x) == log(f(x)) @XFAIL def test_integrate_derivatives(): assert integrate(Derivative(f(x), x), x) == f(x) assert integrate(Derivative(f(y), y), x) == x*Derivative(f(y), y) def test_transform(): a = Integral(x**2 + 1, (x, -1, 2)) fx = x fy = 3*y + 1 assert a.doit() == a.transform(fx, fy).doit() assert a.transform(fx, fy).transform(fy, fx) == a fx = 3*x + 1 fy = y assert a.transform(fx, fy).transform(fy, fx) == a a = Integral(sin(1/x), (x, 0, 1)) assert a.transform(x, 1/y) == Integral(sin(y)/y**2, (y, 1, oo)) assert a.transform(x, 1/y).transform(y, 1/x) == a a = Integral(exp(-x**2), (x, -oo, oo)) assert a.transform(x, 2*y) == Integral(2*exp(-4*y**2), (y, -oo, oo)) # < 3 arg limit handled properly assert Integral(x, x).transform(x, a*y).doit() == \ Integral(y*a**2, y).doit() _3 = S(3) assert Integral(x, (x, 0, -_3)).transform(x, 1/y).doit() == \ Integral(-1/x**3, (x, -oo, -1/_3)).doit() assert Integral(x, (x, 0, _3)).transform(x, 1/y) == \ Integral(y**(-3), (y, 1/_3, oo)) def test_issue953(): f = S(1)/2*asin(x) + x*sqrt(1 - x**2)/2 assert integrate(cos(asin(x)), x) == f assert integrate(sin(acos(x)), x) == f def NS(e, n=15, **options): return sstr(sympify(e).evalf(n, **options), full_prec=True) def test_evalf_integrals(): assert NS(Integral(x, (x, 2, 5)), 15) == '10.5000000000000' gauss = Integral(exp(-x**2), (x, -oo, oo)) assert NS(gauss, 15) == '1.77245385090552' assert NS(gauss**2 - pi + E*Rational( 1, 10**20), 15) in ('2.71828182845904e-20', '2.71828182845905e-20') # A monster of an integral from http://mathworld.wolfram.com/DefiniteIntegral.html t = Symbol('t') a = 8*sqrt(3)/(1 + 3*t**2) b = 16*sqrt(2)*(3*t + 1)*sqrt(4*t**2 + t + 1)**3 c = (3*t**2 + 1)*(11*t**2 + 2*t + 3)**2 d = sqrt(2)*(249*t**2 + 54*t + 65)/(11*t**2 + 2*t + 3)**2 f = a - b/c - d assert NS(Integral(f, (t, 0, 1)), 50) == \ NS((3*sqrt(2) - 49*pi + 162*atan(sqrt(2)))/12, 50) # http://mathworld.wolfram.com/VardisIntegral.html assert NS(Integral(log(log(1/x))/(1 + x + x**2), (x, 0, 1)), 15) == \ NS('pi/sqrt(3) * log(2*pi**(5/6) / gamma(1/6))', 15) # http://mathworld.wolfram.com/AhmedsIntegral.html assert NS(Integral(atan(sqrt(x**2 + 2))/(sqrt(x**2 + 2)*(x**2 + 1)), (x, 0, 1)), 15) == NS(5*pi**2/96, 15) # http://mathworld.wolfram.com/AbelsIntegral.html assert NS(Integral(x/((exp(pi*x) - exp( -pi*x))*(x**2 + 1)), (x, 0, oo)), 15) == NS('log(2)/2-1/4', 15) # Complex part trimming # http://mathworld.wolfram.com/VardisIntegral.html assert NS(Integral(log(log(sin(x)/cos(x))), (x, pi/4, pi/2)), 15, chop=True) == \ NS('pi/4*log(4*pi**3/gamma(1/4)**4)', 15) # # Endpoints causing trouble (rounding error in integration points -> complex log) assert NS( 2 + Integral(log(2*cos(x/2)), (x, -pi, pi)), 17, chop=True) == NS(2, 17) assert NS( 2 + Integral(log(2*cos(x/2)), (x, -pi, pi)), 20, chop=True) == NS(2, 20) assert NS( 2 + Integral(log(2*cos(x/2)), (x, -pi, pi)), 22, chop=True) == NS(2, 22) # Needs zero handling assert NS(pi - 4*Integral( 'sqrt(1-x**2)', (x, 0, 1)), 15, maxn=30, chop=True) in ('0.0', '0') # Oscillatory quadrature a = Integral(sin(x)/x**2, (x, 1, oo)).evalf(maxn=15) assert 0.49 < a < 0.51 assert NS( Integral(sin(x)/x**2, (x, 1, oo)), quad='osc') == '0.504067061906928' assert NS(Integral( cos(pi*x + 1)/x, (x, -oo, -1)), quad='osc') == '0.276374705640365' # indefinite integrals aren't evaluated assert NS(Integral(x, x)) == 'Integral(x, x)' assert NS(Integral(x, (x, y))) == 'Integral(x, (x, y))' @XFAIL def test_evalf_issue_939(): # http://code.google.com/p/sympy/issues/detail?id=939 # The output form of an integral may differ by a step function between # revisions, making this test a bit useless. This can't be said about # other two tests. For now, all values of this evaluation are used here, # but in future this should be reconsidered. assert NS(integrate(1/(x**5 + 1), x).subs(x, 4), chop=True) in \ ['-0.000976138910649103', '0.965906660135753', '1.93278945918216'] assert NS(Integral(1/(x**5 + 1), (x, 2, 4))) == '0.0144361088886740' assert NS( integrate(1/(x**5 + 1), (x, 2, 4)), chop=True) == '0.0144361088886740' @XFAIL def test_failing_integrals(): #--- # Double integrals not implemented assert NS(Integral( sqrt(x) + x*y, (x, 1, 2), (y, -1, 1)), 15) == '2.43790283299492' # double integral + zero detection assert NS(Integral(sin(x + x*y), (x, -1, 1), (y, -1, 1)), 15) == '0.0' def test_integrate_DiracDelta(): # This is here to check that deltaintegrate is being called, but also # to test definite integrals. More tests are in test_deltafunctions.py assert integrate(DiracDelta(x) * f(x), (x, -oo, oo)) == f(0) assert integrate(DiracDelta(x) * f(x), (x, 0, oo)) == f(0)/2 assert integrate(DiracDelta(x)**2, (x, -oo, oo)) == DiracDelta(0) # issue 1423 assert integrate(integrate((4 - 4*x + x*y - 4*y) * \ DiracDelta(x)*DiracDelta(y - 1), (x, 0, 1)), (y, 0, 1)) == 0 # issue 2630 p = exp(-(x**2 + y**2))/pi assert integrate(p*DiracDelta(x - 10*y), (x, -oo, oo), (y, -oo, oo)) == \ integrate(p*DiracDelta(x - 10*y), (y, -oo, oo), (x, -oo, oo)) == \ integrate(p*DiracDelta(10*x - y), (x, -oo, oo), (y, -oo, oo)) == \ integrate(p*DiracDelta(10*x - y), (y, -oo, oo), (x, -oo, oo)) == \ 1/sqrt(101*pi) @XFAIL def test_integrate_DiracDelta_fails(): # issue 3328 assert integrate(integrate(integrate( DiracDelta(x - y - z), (z, 0, oo)), (y, 0, 1)), (x, 0, 1)) == S(1)/2 def test_integrate_returns_piecewise(): assert integrate(x**y, x) == Piecewise( (log(x), Eq(y, -1)), (x**(y + 1)/(y + 1), True)) assert integrate(x**y, y) == Piecewise( (y, Eq(log(x), 0)), (x**y/log(x), True)) assert integrate(exp(n*x), x) == Piecewise( (x, Eq(n, 0)), (exp(n*x)/n, True)) assert integrate(x*exp(n*x), x) == Piecewise( (x**2/2, Eq(n**3, 0)), ((x*n**2 - n)*exp(n*x)/n**3, True)) assert integrate(x**(n*y), x) == Piecewise( (log(x), Eq(n*y, -1)), (x**(n*y + 1)/(n*y + 1), True)) assert integrate(x**(n*y), y) == Piecewise( (y, Eq(n*log(x), 0)), (x**(n*y)/(n*log(x)), True)) assert integrate(cos(n*x), x) == Piecewise( (x, Eq(n, 0)), (sin(n*x)/n, True)) assert integrate(cos(n*x)**2, x) == Piecewise( (x, Eq(n, 0)), ((n*x/2 + sin(n*x)*cos(n*x)/2)/n, True)) assert integrate(x*cos(n*x), x) == Piecewise( (x**2/2, Eq(n, 0)), (x*sin(n*x)/n + cos(n*x)/n**2, True)) assert integrate(sin(n*x), x) == Piecewise( (0, Eq(n, 0)), (-cos(n*x)/n, True)) assert integrate(sin(n*x)**2, x) == Piecewise( (0, Eq(n, 0)), ((n*x/2 - sin(n*x)*cos(n*x)/2)/n, True)) assert integrate(x*sin(n*x), x) == Piecewise( (0, Eq(n, 0)), (-x*cos(n*x)/n + sin(n*x)/n**2, True)) assert integrate(exp(x*y),(x,0,z)) == Piecewise( \ (z, Eq(y,0)), (exp(y*z)/y - 1/y, True)) def test_subs1(): e = Integral(exp(x - y), x) assert e.subs(y, 3) == Integral(exp(x - 3), x) e = Integral(exp(x - y), (x, 0, 1)) assert e.subs(y, 3) == Integral(exp(x - 3), (x, 0, 1)) f = Lambda(x, exp(-x**2)) conv = Integral(f(x - y)*f(y), (y, -oo, oo)) assert conv.subs({x: 0}) == Integral(exp(-2*y**2), (y, -oo, oo)) def test_subs2(): e = Integral(exp(x - y), x, t) assert e.subs(y, 3) == Integral(exp(x - 3), x, t) e = Integral(exp(x - y), (x, 0, 1), (t, 0, 1)) assert e.subs(y, 3) == Integral(exp(x - 3), (x, 0, 1), (t, 0, 1)) f = Lambda(x, exp(-x**2)) conv = Integral(f(x - y)*f(y), (y, -oo, oo), (t, 0, 1)) assert conv.subs({x: 0}) == Integral(exp(-2*y**2), (y, -oo, oo), (t, 0, 1)) def test_subs3(): e = Integral(exp(x - y), (x, 0, y), (t, y, 1)) assert e.subs(y, 3) == Integral(exp(x - 3), (x, 0, 3), (t, 3, 1)) f = Lambda(x, exp(-x**2)) conv = Integral(f(x - y)*f(y), (y, -oo, oo), (t, x, 1)) assert conv.subs({x: 0}) == Integral(exp(-2*y**2), (y, -oo, oo), (t, 0, 1)) def test_subs4(): e = Integral(exp(x), (x, 0, y), (t, y, 1)) assert e.subs(y, 3) == Integral(exp(x), (x, 0, 3), (t, 3, 1)) f = Lambda(x, exp(-x**2)) conv = Integral(f(y)*f(y), (y, -oo, oo), (t, x, 1)) assert conv.subs({x: 0}) == Integral(exp(-2*y**2), (y, -oo, oo), (t, 0, 1)) def test_subs5(): e = Integral(exp(-x**2), (x, -oo, oo)) assert e.subs(x, 5) == e e = Integral(exp(-x**2 + y), x) assert e.subs(y, 5) == Integral(exp(-x**2 + 5), x) e = Integral(exp(-x**2 + y), (x, x)) assert e.subs(x, 5) == Integral(exp(y - x**2), (x, 5)) assert e.subs(y, 5) == Integral(exp(-x**2 + 5), x) e = Integral(exp(-x**2 + y), (y, -oo, oo), (x, -oo, oo)) assert e.subs(x, 5) == e assert e.subs(y, 5) == e # Test evaluation of antiderivatives e = Integral(exp(-x**2), (x, x)) assert e.subs(x, 5) == Integral(exp(-x**2), (x, 5)) e = Integral(exp(x), x) assert (e.subs(x,1)-e.subs(x,0) - Integral(exp(x),(x,0,1))).doit().is_zero def test_subs6(): a, b = symbols('a b') e = Integral(x*y, (x, f(x), f(y))) assert e.subs(x, 1) == Integral(x*y, (x, f(1), f(y))) assert e.subs(y, 1) == Integral(x, (x, f(x), f(1))) e = Integral(x*y, (x, f(x), f(y)), (y, f(x), f(y))) assert e.subs(x, 1) == Integral(x*y, (x, f(1), f(y)), (y, f(1), f(y))) assert e.subs(y, 1) == Integral(x*y, (x, f(x), f(y)), (y, f(x), f(1))) e = Integral(x*y, (x, f(x), f(a)), (y, f(x), f(a))) assert e.subs(a, 1) == Integral(x*y, (x, f(x), f(1)), (y, f(x), f(1))) def test_subs7(): e = Integral(x, (x, 1, y), (y, 1, 2)) assert e.subs({x: 1, y: 2}) == e e = Integral(sin(x) + sin(y), (x, sin(x), sin(y)), (y, 1, 2)) assert e.subs(sin(y), 1) == e assert e.subs(sin(x), 1) == Integral(sin(x) + sin(y), (x, 1, sin(y)), (y, 1, 2)) def test_expand(): e = Integral(f(x)+f(x**2), (x, 1, y)) assert e.expand() == Integral(f(x), (x, 1, y)) + Integral(f(x**2), (x, 1, y)) def test_integration_variable(): raises(ValueError, lambda: Integral(exp(-x**2), 3)) raises(ValueError, lambda: Integral(exp(-x**2), (3, -oo, oo))) def test_expand_integral(): assert Integral(cos(x**2)*(sin(x**2) + 1), (x, 0, 1)).expand() == \ Integral(cos(x**2)*sin(x**2), (x, 0, 1)) + \ Integral(cos(x**2), (x, 0, 1)) assert Integral(cos(x**2)*(sin(x**2) + 1), x).expand() == \ Integral(cos(x**2)*sin(x**2), x) + \ Integral(cos(x**2), x) def test_as_sum_midpoint1(): e = Integral(sqrt(x**3 + 1), (x, 2, 10)) assert e.as_sum(1, method="midpoint") == 8*sqrt(217) assert e.as_sum(2, method="midpoint") == 4*sqrt(65) + 12*sqrt(57) assert e.as_sum(3, method="midpoint") == 8*sqrt(217)/3 + \ 8*sqrt(3081)/27 + 8*sqrt(52809)/27 assert e.as_sum(4, method="midpoint") == 2*sqrt(730) + \ 4*sqrt(7) + 4*sqrt(86) + 6*sqrt(14) assert abs(e.as_sum(4, method="midpoint").n() - e.n()) < 0.5 e = Integral(sqrt(x**3 + y**3), (x, 2, 10), (y, 0, 10)) raises(NotImplementedError, lambda: e.as_sum(4)) def test_as_sum_midpoint2(): e = Integral((x + y)**2, (x, 0, 1)) assert e.as_sum(1, method="midpoint").expand() == S(1)/4 + y + y**2 assert e.as_sum(2, method="midpoint").expand() == S(5)/16 + y + y**2 assert e.as_sum(3, method="midpoint").expand() == S(35)/108 + y + y**2 assert e.as_sum(4, method="midpoint").expand() == S(21)/64 + y + y**2 def test_as_sum_left(): e = Integral((x + y)**2, (x, 0, 1)) assert e.as_sum(1, method="left").expand() == y**2 assert e.as_sum(2, method="left").expand() == S(1)/8 + y/2 + y**2 assert e.as_sum(3, method="left").expand() == S(5)/27 + 2*y/3 + y**2 assert e.as_sum(4, method="left").expand() == S(7)/32 + 3*y/4 + y**2 def test_as_sum_right(): e = Integral((x + y)**2, (x, 0, 1)) assert e.as_sum(1, method="right").expand() == 1 + 2*y + y**2 assert e.as_sum(2, method="right").expand() == S(5)/8 + 3*y/2 + y**2 assert e.as_sum(3, method="right").expand() == S(14)/27 + 4*y/3 + y**2 assert e.as_sum(4, method="right").expand() == S(15)/32 + 5*y/4 + y**2 def test_as_sum_raises(): e = Integral((x + y)**2, (x, 0, 1)) raises(ValueError, lambda: e.as_sum(-1)) raises(ValueError, lambda: e.as_sum(0)) raises(ValueError, lambda: Integral(x).as_sum(3)) raises(NotImplementedError, lambda: e.as_sum(oo)) raises(NotImplementedError, lambda: e.as_sum(3, method='xxxx2')) def test_nested_doit(): e = Integral(Integral(x, x), x) f = Integral(x, x, x) assert e.doit() == f.doit() def test_issue1566(): # Allow only upper or lower limit evaluation e = Integral(x**2, (x, None, 1)) f = Integral(x**2, (x, 1, None)) assert e.doit() == Rational(1, 3) assert f.doit() == Rational(-1, 3) assert Integral(x*y, (x, None, y)).subs(y, t) == Integral(x*t, (x, None, t)) assert Integral(x*y, (x, y, None)).subs(y, t) == Integral(x*t, (x, t, None)) assert integrate(x**2, (x, None, 1)) == Rational(1, 3) assert integrate(x**2, (x, 1, None)) == Rational(-1, 3) assert integrate("x**2", ("x", "1", None)) == Rational(-1, 3) def test_integral_reconstruct(): e = Integral(x**2, (x, -1, 1)) assert e == Integral(*e.args) def test_doit(): e = Integral(Integral(2*x), (x, 0, 1)) assert e.doit() == Rational(1, 3) assert e.doit(deep=False) == Rational(1, 3) f = Function('f') # doesn't matter if the integral can't be performed assert Integral(f(x), (x, 1, 1)).doit() == 0 # doesn't matter if the limits can't be evaluated assert Integral(0, (x, 1, Integral(f(x), x))).doit() == 0 def test_issue_1785(): assert integrate(sqrt(x)*(1 + x)) == \ Piecewise( (2*sqrt(x)*(x + 1)**2/5 - 2*sqrt(x)*(x + 1)/15 - 4*sqrt(x)/15, Abs(x + 1) > 1), (2*I*sqrt(-x)*(x + 1)**2/5 - 2*I*sqrt(-x)*(x + 1)/15 - 4*I*sqrt(-x)/15, True)) assert integrate(x**x*(1 + log(x))) == x**x def test_is_number(): from sympy.abc import x, y, z from sympy import cos, sin assert Integral(x).is_number is False assert Integral(1, x).is_number is False assert Integral(1, (x, 1)).is_number is True assert Integral(1, (x, 1, 2)).is_number is True assert Integral(1, (x, 1, y)).is_number is False assert Integral(x, y).is_number is False assert Integral(x, (y, 1, x)).is_number is False assert Integral(x, (y, 1, 2)).is_number is False assert Integral(x, (x, 1, 2)).is_number is True assert Integral(x, (y, 1, 1)).is_number is True assert Integral(x*y, (x, 1, 2), (y, 1, 3)).is_number is True assert Integral(x*y, (x, 1, 2), (y, 1, z)).is_number is False assert Integral(x, (x, 1)).is_number is True assert Integral(x, (x, 1, Integral(y, (y, 1, 2)))).is_number is True # it is possible to get a false negative if the integrand is # actually an unsimplified zero, but this is true of is_number in general. assert Integral(sin(x)**2 + cos(x)**2 - 1, x).is_number is False def test_symbols(): from sympy.abc import x, y, z assert Integral(0, x).free_symbols == set() assert Integral(x).free_symbols == set([x]) assert Integral(x, (x, None, y)).free_symbols == set([y]) assert Integral(x, (x, y, None)).free_symbols == set([y]) assert Integral(x, (x, 1, y)).free_symbols == set([y]) assert Integral(x, (x, y, 1)).free_symbols == set([y]) assert Integral(x, (x, x, y)).free_symbols == set([x, y]) assert Integral(x, x, y).free_symbols == set([x, y]) assert Integral(x, (x, 1, 2)).free_symbols == set() assert Integral(x, (y, 1, 2)).free_symbols == set([x]) assert Integral(x, (y, z, z)).free_symbols == set() assert Integral(x, (y, 1, 2), (y, None, None)).free_symbols == set([x, y]) assert Integral(x, (y, 1, 2), (x, 1, y)).free_symbols == set([y]) assert Integral(2, (y, 1, 2), (y, 1, x), (x, 1, 2)).free_symbols == set() assert Integral(2, (y, x, 2), (y, 1, x), (x, 1, 2)).free_symbols == set() assert Integral(2, (x, 1, 2), (y, x, 2), (y, 1, 2)).free_symbols == \ set([x]) def test_is_zero(): from sympy.abc import x, m, n assert Integral(0, (x, 1, x)).is_zero assert Integral(1, (x, 1, 1)).is_zero assert Integral(1, (x, 1, 2)).is_zero is False assert Integral(sin(m*x)*cos(n*x), (x, 0, 2*pi)).is_zero is None def test_series(): from sympy.abc import x i = Integral(cos(x), (x, x)) e = i.lseries(x) assert i.nseries(x, n=8).removeO() == Add(*[next(e) for j in range(4)]) def test_issue_1304(): z = Symbol('z', positive=True) assert integrate(sqrt(x**2 + z**2), x) == \ z**2*asinh(x/z)/2 + x*sqrt(x**2 + z**2)/2 assert integrate(sqrt(x**2 - z**2), x) == \ -z**2*acosh(x/z)/2 + x*sqrt(x**2 - z**2)/2 @XFAIL def test_issue_1304_2(): assert integrate(sqrt(-x**2 - 4), x) == \ -2*atan(x/sqrt(-4 - x**2)) + x*sqrt(-4 - x**2)/2 def test_issue_1001(): R = Symbol('R', positive=True) assert integrate(sqrt(R**2 - x**2), (x, 0, R)) == pi*R**2/4 def test_issue2068(): from sympy.abc import w, x, y, z f = Function('f') assert Integral(Integral(f(x), x), x) == Integral(f(x), x, x) assert Integral(f(x)).args == (f(x), Tuple(x)) assert Integral(Integral(f(x))).args == (f(x), Tuple(x), Tuple(x)) assert Integral(Integral(f(x)), y).args == (f(x), Tuple(x), Tuple(y)) assert Integral(Integral(f(x), z), y).args == (f(x), Tuple(z), Tuple(y)) assert Integral(Integral(Integral(f(x), x), y), z).args == \ (f(x), Tuple(x), Tuple(y), Tuple(z)) assert integrate(Integral(f(x), x), x) == Integral(f(x), x, x) assert integrate(Integral(f(x), y), x) == Integral(y*f(x), x) assert integrate(Integral(f(x), x), y) == Integral(y*f(x), x) assert integrate(Integral(2, x), x) == x**2 assert integrate(Integral(2, x), y) == 2*x*y # don't re-order given limits assert Integral(1, x, y).args != Integral(1, y, x).args # do as many as possibble assert Integral(f(x), y, x, y, x).doit() == Integral(y**2*f(x)/2, x, x) assert Integral(f(x), (x, 1, 2), (w, 1, x), (z, 1, y)).doit() == \ Integral(-f(x) + y*f(x), (x, 1, 2), (w, 1, x)) def test_issue_1791(): z = Symbol('z', positive=True) assert integrate(exp(-log(x)**2), x) == \ sqrt(pi)*exp(S(1)/4)*erf(log(x)-S(1)/2)/2 assert integrate(exp(log(x)**2), x) == \ sqrt(pi)*exp(-S(1)/4)*erfi(log(x)+S(1)/2)/2 assert integrate(exp(-z*log(x)**2), x) == \ sqrt(pi)*exp(1/(4*z))*erf(sqrt(z)*log(x) - 1/(2*sqrt(z)))/(2*sqrt(z)) def test_issue_1277(): n = Symbol('n', integer=True, positive=True) assert simplify(integrate(n*(x**(1/n) - 1), (x, 0, S.Half)) - (n**2 - 2**(1/n)*n**2 - n*2**(1/n))/(2**(1 + 1/n) + n*2**(1 + 1/n))) == 0 @slow def test_issue_1418(): assert integrate((sqrt(x) - x**3)/x**Rational(1, 3), x) == \ 6*x**Rational(7, 6)/7 - 3*x**Rational(11, 3)/11 def test_issue_1428(): k, m = symbols('k m', integer=True) assert integrate(sin(k*x)*sin(m*x), (x, 0, pi)) == Piecewise( (0, And(Eq(k, 0), Eq(m, 0))), (-pi/2, Eq(k, -m)), (pi/2, Eq(k, m)), (0, True)) assert integrate(sin(k*x)*sin(m*x), (x,)) == Piecewise( (0, And(Eq(k, 0), Eq(m, 0))), (-x*sin(m*x)**2/2 - x*cos(m*x)**2/2 + sin(m*x)*cos(m*x)/(2*m), Eq(k, -m)), (x*sin(m*x)**2/2 + x*cos(m*x)**2/2 - sin(m*x)*cos(m*x)/(2*m), Eq(k, m)), (m*sin(k*x)*cos(m*x)/(k**2 - m**2) - k*sin(m*x)*cos(k*x)/(k**2 - m**2), True)) def test_issue_1100(): ypos = Symbol('y', positive=True) # TODO: Remove conds='none' below, let the assumption take care of it. assert integrate(exp(-I*2*pi*ypos*x)*x, (x, -oo, oo), conds='none') == \ Integral(exp(-I*2*pi*ypos*x)*x, (x, -oo, oo)) def test_issue_841(): a, b, c, d = symbols('a:d', positive=True, bounded=True) assert integrate(exp(-x**2 + I*c*x), x) == \ -sqrt(pi)*exp(-c**2/4)*erf(I*c/2 - x)/2 assert integrate(exp(a*x**2 + b*x + c), x) == \ sqrt(pi)*exp(c)*exp(-b**2/(4*a))*erfi(sqrt(a)*x + b/(2*sqrt(a)))/(2*sqrt(a)) def test_issue_2314(): # Note that this is not the same as testing ratint() becuase integrate() # pulls out the coefficient. assert integrate(-a/(a**2 + x**2), x) == I*log(-I*a + x)/2 - I*log(I*a + x)/2 def test_issue_1793a(): A, z = symbols('A z') c = Symbol('c', nonzero=True) P1 = -A*exp(-z) P2 = -A/(c*t)*(sin(x)**2 + cos(y)**2) h1 = -sin(x)**2 - cos(y)**2 h2 = -sin(x)**2 + sin(y)**2 - 1 # there is still some non-deterministic behavior in integrate # or trigsimp which permits one of the following assert integrate(c*(P2 - P1), t) in [ c*(-A*(-h1)*log(c*t)/c + A*t*exp(-z)), c*(-A*(-h2)*log(c*t)/c + A*t*exp(-z)), c*( A* h1 *log(c*t)/c + A*t*exp(-z)), c*( A* h2 *log(c*t)/c + A*t*exp(-z)), (A*c*t - A*(-h1)*log(t)*exp(z))*exp(-z), (A*c*t - A*(-h2)*log(t)*exp(z))*exp(-z), ] def test_issue_1793b(): # Issues relating to issue 1497 are making the actual result of this hard # to test. The answer should be something like # # (-sin(y) + sqrt(-72 + 48*cos(y) - 8*cos(y)**2)/2)*log(x + sqrt(-72 + # 48*cos(y) - 8*cos(y)**2)/(2*(3 - cos(y)))) + (-sin(y) - sqrt(-72 + # 48*cos(y) - 8*cos(y)**2)/2)*log(x - sqrt(-72 + 48*cos(y) - # 8*cos(y)**2)/(2*(3 - cos(y)))) + x**2*sin(y)/2 + 2*x*cos(y) expr = (sin(y)*x**3 + 2*cos(y)*x**2 + 12)/(x**2 + 2) assert trigsimp(factor(integrate(expr, x).diff(x) - expr)) == 0 def test_issue_2079(): assert integrate(sin(x)*f(y, z), (x, 0, pi), (y, 0, pi), (z, 0, pi)) == \ Integral(2*f(y, z), (y, 0, pi), (z, 0, pi)) def test_integrate_series(): f = sin(x).series(x, 0, 10) g = x**2/2 - x**4/24 + x**6/720 - x**8/40320 + x**10/3628800 + O(x**11) assert integrate(f, x) == g assert diff(integrate(f, x), x) == f assert integrate(O(x**5), x) == O(x**6) def test_atom_bug(): from sympy import meijerg from sympy.integrals.heurisch import heurisch assert heurisch(meijerg([], [], [1], [], x), x) is None def test_limit_bug(): z = Symbol('z', nonzero=True) assert integrate(sin(x*y*z), (x, 0, pi), (y, 0, pi)) == \ (log(z**2) + 2*EulerGamma + 2*log(pi))/(2*z) - \ (-log(pi*z) + log(pi**2*z**2)/2 + Ci(pi**2*z))/z + log(pi)/z def test_issue_1604(): g = Function('g') assert integrate(exp(x)*g(x), x).has(Integral) def test_issue_1888(): f = Function('f') assert integrate(f(x).diff(x)**2, x).has(Integral) # The following tests work using meijerint. def test_issue459(): from sympy import Si assert integrate(cos(x*y), (x, -pi/2, pi/2), (y, 0, pi)) == 2*Si(pi**2/2) def test_issue841(): from sympy import expand_mul from sympy.abc import k assert expand_mul(integrate(exp(-x**2)*exp(I*k*x), (x, -oo, oo))) == \ sqrt(pi)*exp(-k**2/4) a, d = symbols('a d', positive=True) assert expand_mul(integrate(exp(-a*x**2 + 2*d*x), (x, -oo, oo))) == \ sqrt(pi)*exp(d**2/a)/sqrt(a) def test_issue1304(): x = Symbol('x', real=True) y = Symbol('y', nonzero=True, real=True) assert integrate(1/(x**2 + y**2)**S('3/2'), x) == \ 1/(y**2*sqrt(1 + y**2/x**2)) def test_issue_1323(): assert integrate(1/sqrt(16 + 4*x**2), x) == asinh(x/2) / 2 def test_issue1394(): from sympy import simplify assert simplify(integrate(x*sqrt(1 + 2*x), x)) == \ sqrt(2*x + 1)*(6*x**2 + x - 1)/15 def test_issue1638(): assert integrate(sin(x)/x, (x, -oo, oo)) == pi assert integrate(sin(x)/x, (x, 0, oo)) == pi/2 def test_issue1893(): from sympy import simplify, expand_func, polygamma, gamma a = Symbol('a', positive=True) assert simplify(expand_func(integrate(exp(-x)*log(x)*x**a, (x, 0, oo)))) == \ (a*polygamma(0, a) + 1)*gamma(a) def test_issue1388(): from sympy import lowergamma, simplify assert simplify(integrate(exp(-x)*x**y, x)) == lowergamma(y + 1, x) @XFAIL def test_issue_1116(): x = Symbol("x") assert integrate(1/(x**2), (x, -1, 1)) == oo def test_issue_1301(): n = Symbol('n', integer=True, positive=True) assert integrate((x**n)*log(x), x) == \ n*x*x**n*log(x)/(n**2 + 2*n + 1) + x*x**n*log(x)/(n**2 + 2*n + 1) - \ x*x**n/(n**2 + 2*n + 1) def test_issue_3154(): # Note: this used to raise NotImplementedError assert integrate((sqrt(1 - x) + sqrt(1 + x))**2/x, x, meijerg=True) == \ Integral((sqrt(-x + 1) + sqrt(x + 1))**2/x, x) def test_issue1054(): assert integrate(1/(1 + x + y + z), (x, 0, 1), (y, 0, 1), (z, 0, 1)) in [ -12*log(3) - 3*log(6)/2 + 3*log(8)/2 + 5*log(2) + 7*log(4), 6*log(2) + 8*log(4) - 27*log(3)/2, 22*log(2) - 27*log(3)/2, -12*log(3) - 3*log(6)/2 + 47*log(2)/2] def test_issue_1227(): R, b, h = symbols('R b h') # It doesn't matter if we can do the integral. Just make sure the result # doesn't contain nan. This is really a test against _eval_interval. assert not integrate(((h*(x - R + b))/b)*sqrt(R**2 - x**2), (x, R - b, R)).has(nan) def test_powers(): assert integrate(2**x + 3**x, x) == 2**x/log(2) + 3**x/log(3) def test_risch_option(): # risch=True only allowed on indefinite integrals raises(ValueError, lambda: integrate(1/log(x), (x, 0, oo), risch=True)) assert integrate(exp(-x**2), x, risch=True) == NonElementaryIntegral(exp(-x**2), x) assert integrate(log(1/x)*y, x, y, risch=True) == y**2*(x*log(1/x)/2 + x/2) assert integrate(erf(x), x, risch=True) == Integral(erf(x), x) # TODO: How to test risch=False? def test_issue_3729(): # TODO: Currently `h' is the result (all three are equivalent). Improve # simplify() to find the form with simplest real coefficients. f = 1/(1.08*x**2 - 4.3) g = 300.0/(324.0*x**2 - 1290.0) h = 0.925925925925926/(1.0*x**2 - 3.98148148148148) assert integrate(f, x).diff(x).simplify().equals(f) is True @XFAIL def test_integrate_Piecewise_rational_over_reals(): f = Piecewise( (0, t - 478.515625*pi < 0), (13.2075145209219*pi/(0.000871222*t + 0.995)**2, t - 478.515625*pi >= 0)) assert integrate(f, (t, 0, oo)) == 15235.9375*pi def test_issue_1704(): x_max = Symbol("x_max") assert integrate(y/pi*exp(-(x_max - x)/cos(a)), x) == \ y*exp((x - x_max)/cos(a))*cos(a)/pi sympy-0.7.4.1/sympy/integrals/tests/test_rde.py0000644000175000017500000001752412253362407021753 0ustar georgeskgeorgesk"""Most of these tests come from the examples in Bronstein's book.""" from sympy import Poly, S, symbols, oo, I from sympy.integrals.risch import (DifferentialExtension, NonElementaryIntegralException) from sympy.integrals.rde import (order_at, order_at_oo, weak_normalizer, normal_denom, special_denom, bound_degree, spde, solve_poly_rde, no_cancel_equal, cancel_primitive, cancel_exp, rischDE) from sympy.utilities.pytest import raises, XFAIL from sympy.abc import x, t, z, n t0, t1, t2, k = symbols('t:3 k') def test_order_at(): a = Poly(t**4, t) b = Poly((t**2 + 1)**3*t, t) p1 = Poly(t, t) p2 = Poly(1 + t**2, t) assert order_at(a, p1, t) == 4 assert order_at(b, p1, t) == 1 assert order_at(a, p2, t) == 0 assert order_at(b, p2, t) == 3 assert order_at(Poly(0, t), Poly(t, t), t) == oo assert order_at_oo(Poly(t**2 - 1, t), Poly(t + 1), t) == \ order_at_oo(Poly(t - 1, t), Poly(1, t), t) == -1 assert order_at_oo(Poly(0, t), Poly(1, t), t) == oo def test_weak_normalizer(): a = Poly((1 + x)*t**5 + 4*t**4 + (-1 - 3*x)*t**3 - 4*t**2 + (-2 + 2*x)*t, t) d = Poly(t**4 - 3*t**2 + 2, t) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t, t)]}) r = weak_normalizer(a, d, DE, z) assert r == (Poly(t**5 - t**4 - 4*t**3 + 4*t**2 + 4*t - 4, t), (Poly((1 + x)*t**2 + x*t, t), Poly(t + 1, t))) assert weak_normalizer(r[1][0], r[1][1], DE) == (Poly(1, t), r[1]) r = weak_normalizer(Poly(1 + t**2), Poly(t**2 - 1, t), DE, z) assert r == (Poly(t**4 - 2*t**2 + 1, t), (Poly(-3*t**2 + 1, t), Poly(t**2 - 1, t))) assert weak_normalizer(r[1][0], r[1][1], DE, z) == (Poly(1, t), r[1]) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1 + t**2)]}) r = weak_normalizer(Poly(1 + t**2), Poly(t, t), DE, z) assert r == (Poly(t, t), (Poly(0, t), Poly(1, t))) assert weak_normalizer(r[1][0], r[1][1], DE, z) == (Poly(1, t), r[1]) def test_normal_denom(): DE = DifferentialExtension(extension={'D': [Poly(1, x)]}) raises(NonElementaryIntegralException, lambda: normal_denom(Poly(1, x), Poly(1, x), Poly(1, x), Poly(x, x), DE)) fa, fd = Poly(t**2 + 1, t), Poly(1, t) ga, gd = Poly(1, t), Poly(t**2, t) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t**2 + 1, t)]}) assert normal_denom(fa, fd, ga, gd, DE) == \ (Poly(t, t), (Poly(t**3 - t**2 + t - 1, t), Poly(1, t)), (Poly(1, t), Poly(1, t)), Poly(t, t)) def test_special_denom(): # TODO: add more tests here DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t, t)]}) assert special_denom(Poly(1, t), Poly(t**2, t), Poly(1, t), Poly(t**2 - 1, t), Poly(t, t), DE) == \ (Poly(1, t), Poly(t**2 - 1, t), Poly(t**2 - 1, t), Poly(t, t)) # assert special_denom(Poly(1, t), Poly(2*x, t), Poly((1 + 2*x)*t, t), DE) == 1 # Issue 841 # Note, this isn't a very good test, because the denominator is just 1, # but at least it tests the exp cancellation case DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(-2*x*t0, t0), Poly(I*k*t1, t1)]}) DE.decrement_level() assert special_denom(Poly(1, t0), Poly(I*k, t0), Poly(1, t0), Poly(t0, t0), Poly(1, t0), DE) == \ (Poly(1, t0), Poly(I*k, t0), Poly(t0, t0), Poly(1, t0)) @XFAIL def test_bound_degree_fail(): # Primitive DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t0/x**2, t0), Poly(1/x, t)]}) assert bound_degree(Poly(t**2, t), Poly(-(1/x**2*t**2 + 1/x), t), Poly((2*x - 1)*t**4 + (t0 + x)/x*t**3 - (t0 + 4*x**2)/2*x*t**2 + x*t, t), DE) == 3 def test_bound_degree(): # Base DE = DifferentialExtension(extension={'D': [Poly(1, x)]}) assert bound_degree(Poly(1, x), Poly(-2*x, x), Poly(1, x), DE) == 0 # Primitive (see above test_bound_degree_fail) # TODO: Add test for when the degree bound becomes larger after limited_integrate # TODO: Add test for db == da - 1 case # Exp # TODO: Add tests # TODO: Add test for when the degree becomes larger after parametric_log_deriv() # Nonlinear DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t**2 + 1, t)]}) assert bound_degree(Poly(t, t), Poly((t - 1)*(t**2 + 1), t), Poly(1, t), DE) == 0 def test_spde(): DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t**2 + 1, t)]}) raises(NonElementaryIntegralException, lambda: spde(Poly(t, t), Poly((t - 1)*(t**2 + 1), t), Poly(1, t), 0, DE)) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t, t)]}) assert spde(Poly(t**2 + x*t*2 + x**2, t), Poly(t**2/x**2 + (2/x - 1)*t, t), Poly(t**2/x**2 + (2/x - 1)*t, t), 0, DE) == \ (Poly(0, t), Poly(0, t), 0, Poly(0, t), Poly(1, t)) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t0/x**2, t0), Poly(1/x, t)]}) assert spde(Poly(t**2, t), Poly(-t**2/x**2 - 1/x, t), Poly((2*x - 1)*t**4 + (t0 + x)/x*t**3 - (t0 + 4*x**2)/(2*x)*t**2 + x*t, t), 3, DE) == \ (Poly(0, t), Poly(0, t), 0, Poly(0, t), Poly(t0*t**2/2 + x**2*t**2 - x**2*t, t)) DE = DifferentialExtension(extension={'D': [Poly(1, x)]}) assert spde(Poly(x**2 + x + 1, x), Poly(-2*x - 1, x), Poly(x**5/2 + 3*x**4/4 + x**3 - x**2 + 1, x), 4, DE) == \ (Poly(0, x), Poly(x/2 - S(1)/4, x), 2, Poly(x**2 + x + 1, x), Poly(5*x/4, x)) assert spde(Poly(x**2 + x + 1, x), Poly(-2*x - 1, x), Poly(x**5/2 + 3*x**4/4 + x**3 - x**2 + 1, x), n, DE) == \ (Poly(0, x), Poly(x/2 - S(1)/4, x), -2 + n, Poly(x**2 + x + 1, x), Poly(5*x/4, x)) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1, t)]}) raises(NonElementaryIntegralException, lambda: spde(Poly((t - 1)*(t**2 + 1)**2, t), Poly((t - 1)*(t**2 + 1), t), Poly(1, t), 0, DE)) def test_solve_poly_rde_no_cancel(): # deg(b) large DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1 + t**2, t)]}) assert solve_poly_rde(Poly(t**2 + 1, t), Poly(t**3 + (x + 1)*t**2 + t + x + 2, t), oo, DE) == Poly(t + x, t) # deg(b) small DE = DifferentialExtension(extension={'D': [Poly(1, x)]}) assert solve_poly_rde(Poly(0, x), Poly(x/2 - S(1)/4, x), oo, DE) == \ Poly(x**2/4 - x/4, x) DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t**2 + 1, t)]}) assert solve_poly_rde(Poly(2, t), Poly(t**2 + 2*t + 3, t), 1, DE) == \ Poly(t + 1, t, x) # deg(b) == deg(D) - 1 DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t**2 + 1, t)]}) assert no_cancel_equal(Poly(1 - t, t), Poly(t**3 + t**2 - 2*x*t - 2*x, t), oo, DE) == \ (Poly(t**2, t), 1, Poly((-2 - 2*x)*t - 2*x, t)) def test_solve_poly_rde_cancel(): # exp DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t, t)]}) assert cancel_exp(Poly(2*x, t), Poly(2*x, t), 0, DE) == \ Poly(1, t) assert cancel_exp(Poly(2*x, t), Poly((1 + 2*x)*t, t), 1, DE) == \ Poly(t, t) # TODO: Add more exp tests, including tests that require is_deriv_in_field() # primitive DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(1/x, t)]}) # If the DecrementLevel context manager is working correctly, this shouldn't # cause any problems with the further tests. raises(NonElementaryIntegralException, lambda: cancel_primitive(Poly(1, t), Poly(t, t), oo, DE)) assert cancel_primitive(Poly(1, t), Poly(t + 1/x, t), 2, DE) == \ Poly(t, t) assert cancel_primitive(Poly(4*x, t), Poly(4*x*t**2 + 2*t/x, t), 3, DE) == \ Poly(t**2, t) # TODO: Add more primitive tests, including tests that require is_deriv_in_field() def test_rischDE(): # TODO: Add more tests for rischDE, including ones from the text DE = DifferentialExtension(extension={'D': [Poly(1, x), Poly(t, t)]}) DE.decrement_level() assert rischDE(Poly(-2*x, x), Poly(1, x), Poly(1 - 2*x - 2*x**2, x), Poly(1, x), DE) == \ (Poly(x + 1, x), Poly(1, x)) sympy-0.7.4.1/sympy/integrals/tests/__init__.py0000644000175000017500000000000012253362407021656 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/integrals/__init__.py0000644000175000017500000000211312253362407020523 0ustar georgeskgeorgesk"""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) sympy-0.7.4.1/sympy/concrete/0000755000175000017500000000000012253362407016227 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/concrete/summations.py0000644000175000017500000005771712253362407021021 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.concrete.expr_with_limits import AddWithLimits from sympy.concrete.expr_with_intlimits import ExprWithIntLimits from sympy.core.basic import C from sympy.core.containers import Tuple from sympy.core.expr import Expr from sympy.core.function import Derivative from sympy.core.relational import Eq from sympy.core.singleton import S from sympy.core.symbol import (Dummy, Wild) from sympy.core.sympify import sympify from sympy.concrete.gosper import gosper_sum from sympy.functions.elementary.piecewise import piecewise_fold, Piecewise from sympy.polys import apart, PolynomialError from sympy.solvers import solve from sympy.core.compatibility import xrange class Sum(AddWithLimits,ExprWithIntLimits): """Represents unevaluated summation. ``Sum`` represents a finite or infinite series, with the first argument being the general form of terms in the series, and the second argument being ``(dummy_variable, start, end)``, with ``dummy_variable`` taking all integer values from ``start`` through ``end``. In accordance with long-standing mathematical convention, the end term is included in the summation. Finite sums =========== For finite sums (and sums with symbolic limits assumed to be finite) we follow the summation convention described by Karr [1], especially definition 3 of section 1.4. The sum: .. math:: \sum_{m \leq i < n} f(i) has *the obvious meaning* for `m < n`, namely: .. math:: \sum_{m \leq i < n} f(i) = f(m) + f(m+1) + \ldots + f(n-2) + f(n-1) with the upper limit value `f(n)` excluded. The sum over an empty set is zero if and only if `m = n`: .. math:: \sum_{m \leq i < n} f(i) = 0 \quad \mathrm{for} \quad m = n Finally, for all other sums over empty sets we assume the following definition: .. math:: \sum_{m \leq i < n} f(i) = - \sum_{n \leq i < m} f(i) \quad \mathrm{for} \quad m > n It is important to note that Karr defines all sums with the upper limit being exclusive. This is in contrast to the usual mathematical notation, but does not affect the summation convention. Indeed we have: .. math:: \sum_{m \leq i < n} f(i) = \sum_{i = m}^{n - 1} f(i) where the difference in notation is intentional to emphasize the meaning, with limits typeset on the top being inclusive. Examples ======== >>> from sympy.abc import i, k, m, n, x >>> from sympy import Sum, factorial, oo >>> Sum(k,(k,1,m)) Sum(k, (k, 1, m)) >>> Sum(k,(k,1,m)).doit() m**2/2 + m/2 >>> Sum(k**2,(k,1,m)) Sum(k**2, (k, 1, m)) >>> Sum(k**2,(k,1,m)).doit() m**3/3 + m**2/2 + m/6 >>> Sum(x**k,(k,0,oo)) Sum(x**k, (k, 0, oo)) >>> Sum(x**k,(k,0,oo)).doit() Piecewise((1/(-x + 1), Abs(x) < 1), (Sum(x**k, (k, 0, oo)), True)) >>> Sum(x**k/factorial(k),(k,0,oo)).doit() exp(x) An example showing that the symbolic result of a summation is still valid for seemingly nonsensical values of the limits. Then the Karr convention allows us to give a perfectly valid interpretation to those sums by interchanging the limits according to the above rules: >>> S = Sum(i, (i,1,n)).doit() >>> S n**2/2 + n/2 >>> S.subs(n, -4) 6 >>> Sum(i, (i, 1, -4)).doit() 6 >>> Sum(-i, (i, -3, 0)).doit() 6 An explicit example of the Karr summation convention: >>> S1 = Sum(i**2, (i, m, m+n-1)).doit() >>> S1 m**2*n + m*n**2 - m*n + n**3/3 - n**2/2 + n/6 >>> S2 = Sum(i**2, (i, m+n, m-1)).doit() >>> S2 -m**2*n - m*n**2 + m*n - n**3/3 + n**2/2 - n/6 >>> S1 + S2 0 >>> S3 = Sum(i, (i, m, m-1)).doit() >>> S3 0 See Also ======== summation Product, product References ========== .. [1] Michael Karr, "Summation in Finite Terms", Journal of the ACM, Volume 28 Issue 2, April 1981, Pages 305-350 http://dl.acm.org/citation.cfm?doid=322248.322255 .. [2] http://en.wikipedia.org/wiki/Summation#Capital-sigma_notation .. [3] http://en.wikipedia.org/wiki/Empty_sum """ __slots__ = ['is_commutative'] def __new__(cls, function, *symbols, **assumptions): obj = AddWithLimits.__new__(cls, function, *symbols, **assumptions) if not hasattr(obj, 'limits'): return obj if any(len(l) != 3 or None in l for l in obj.limits): raise ValueError('Sum requires values for lower and upper bounds.') return obj @property 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 def is_number(self): """ Return True if the Sum will result in a number, else False. Sums are a special case since they contain symbols that can be replaced with numbers. Whether the sum can be done or not in closed form is another issue. But answering whether the final result is a number is not difficult. Examples ======== >>> 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 hints.get('deep', True): f = self.function.doit(**hints) else: f = self.function for n, limit in enumerate(self.limits): i, a, b = limit dif = b - a if dif.is_integer and (dif < 0) is True: a, b = b + 1, a - 1 f = -f newf = eval_sum(f, (i, a, b)) if newf is None: if f == self.function: return self else: return self.func(f, *self.limits[n:]) f = newf if hints.get('deep', True): # eval_sum could return partially unevaluated # result with Piecewise. In this case we won't # doit() recursively. if not isinstance(f, Piecewise): return f.doit(**hints) return f 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 = self.func(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 = self.func(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_simplify(self, ratio, measure): from sympy.simplify.simplify import sum_simplify return sum_simplify(self) def _eval_summation(self, f, x): return None 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] if (a > b) is True: if a - b == 1: return S.Zero,S.Zero a, b = b + 1, a - 1 f = -f s = S.Zero if m: if b.is_Integer and a.is_Integer: m = min(m, b - a + 1) if not eps: for k in range(m): s += f.subs(i, a + k) else: term = f.subs(i, a) if term: test = abs(term.evalf(3)) < eps if isinstance(test, bool): if test is True: return s, abs(term) else: # a symbolic Relational class, can't go further return term, S.Zero s += term for k in range(1, m): term = f.subs(i, a + k) if abs(term.evalf(3)) < eps: return s, abs(term) s += term if b - a + 1 == m: return s, S.Zero 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 xrange(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, simplify=False) return s + iterm, abs(term) def reverse_order(self, *indices): """ Reverse the order of a limit in a Sum. Usage ===== ``reverse_order(self, *indices)`` reverses some limits in the expression ``self`` which can be either a ``Sum`` or a ``Product``. The selectors in the argument ``indices`` specify some indices whose limits get reversed. These selectors are either variable names or numerical indices counted starting from the inner-most limit tuple. Examples ======== >>> from sympy import Sum >>> from sympy.abc import x, y, a, b, c, d >>> Sum(x, (x, 0, 3)).reverse_order(x) Sum(-x, (x, 4, -1)) >>> Sum(x*y, (x, 1, 5), (y, 0, 6)).reverse_order(x, y) Sum(x*y, (x, 6, 0), (y, 7, -1)) >>> Sum(x, (x, a, b)).reverse_order(x) Sum(-x, (x, b + 1, a - 1)) >>> Sum(x, (x, a, b)).reverse_order(0) Sum(-x, (x, b + 1, a - 1)) While one should prefer variable names when specifying which limits to reverse, the index counting notation comes in handy in case there are several symbols with the same name. >>> S = Sum(x**2, (x, a, b), (x, c, d)) >>> S Sum(x**2, (x, a, b), (x, c, d)) >>> S0 = S.reverse_order( 0) >>> S0 Sum(-x**2, (x, b + 1, a - 1), (x, c, d)) >>> S1 = S0.reverse_order( 1) >>> S1 Sum(x**2, (x, b + 1, a - 1), (x, d + 1, c - 1)) Of course we can mix both notations: >>> Sum(x*y, (x, a, b), (y, 2, 5)).reverse_order( x, 1) Sum(x*y, (x, b + 1, a - 1), (y, 6, 1)) >>> Sum(x*y, (x, a, b), (y, 2, 5)).reverse_order( y, x) Sum(x*y, (x, b + 1, a - 1), (y, 6, 1)) See Also ======== index, reorder_limit, reorder References ========== .. [1] Michael Karr, "Summation in Finite Terms", Journal of the ACM, Volume 28 Issue 2, April 1981, Pages 305-350 http://dl.acm.org/citation.cfm?doid=322248.322255 """ l_indices = list(indices) for i, indx in enumerate(l_indices): if not isinstance(indx, int): l_indices[i] = self.index(indx) e = 1 limits = [] for i, limit in enumerate(self.limits): l = limit if i in l_indices: e = -e l = (limit[0], limit[2] + 1 , limit[1] - 1) limits.append(l) return Sum(e * self.function, *limits) 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) See Also ======== Sum Product, product """ 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 xrange(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): from sympy.concrete.delta import deltasummation, _has_simple_delta from sympy.functions import KroneckerDelta (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) if isinstance(f, Piecewise): from sympy.utilities.iterables import flatten if i not in flatten([arg.args[1].free_symbols for arg in f.args]): # Piecewise conditions do not depend on the dummy summation variable, # therefore we can fold: Sum(Piecewise((e, c), ...), limits) # --> Piecewise((Sum(e, limits), c), ...) newargs = [] for arg in f.args: newexpr = eval_sum(arg.expr, limits) if newexpr is None: return None newargs.append((newexpr, arg.cond)) return f.func(*newargs) if f.has(KroneckerDelta) and _has_simple_delta(f, limits[0]): return deltasummation(f, limits) 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 C.Add(*[expr.subs(i, a + j) for j in xrange(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: if (b is S.Infinity and not a is S.NegativeInfinity) or \ (a is S.NegativeInfinity and not b is S.Infinity): return S.Infinity 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)) if not (a.has(S.Infinity, S.NegativeInfinity) or b.has(S.Infinity, S.NegativeInfinity)): # 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: p = (c1**c3).subs(e) q = (c1**c2).subs(e) r = p*(q**a - q**(b + 1))/(1 - q) l = p*(b - a + 1) return Piecewise((l, Eq(q, S.One)), (r, True)) 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, i_a_b): from sympy.logic.boolalg import And i, a, b = i_a_b if (b - a).is_Integer: # We are never going to do better than doing the sum in the obvious way return None old_sum = Sum(f, (i, a, b)) if b != S.Infinity: if a == S.NegativeInfinity: res = _eval_sum_hyper(f.subs(i, -i), i, -b) if res is not None: return Piecewise(res, (old_sum, 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), (old_sum, True)) if a == S.NegativeInfinity: 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), (old_sum, True)) # Now b == oo, a != -oo res = _eval_sum_hyper(f, i, a) if res is not None: r, c = res if c == False: if r.is_number: f = f.subs(i, Dummy('i', integer=True, positive=True) + a) if f.is_positive or f.is_zero: return S.Infinity elif f.is_negative: return S.NegativeInfinity return None return Piecewise(res, (old_sum, True)) sympy-0.7.4.1/sympy/concrete/products.py0000644000175000017500000003404112253362407020446 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core.containers import Tuple from sympy.core.core import C from sympy.core.expr import Expr from sympy.core.mul import Mul from sympy.core.singleton import S from sympy.core.sympify import sympify from sympy.concrete.expr_with_intlimits import ExprWithIntLimits from sympy.functions.elementary.piecewise import piecewise_fold from sympy.polys import quo, roots from sympy.simplify import powsimp from sympy.core.compatibility import xrange class Product(ExprWithIntLimits): r"""Represents unevaluated products. ``Product`` represents a finite or infinite product, with the first argument being the general form of terms in the series, and the second argument being ``(dummy_variable, start, end)``, with ``dummy_variable`` taking all integer values from ``start`` through ``end``. In accordance with long-standing mathematical convention, the end term is included in the product. Finite products =============== For finite products (and products with symbolic limits assumed to be finite) we follow the analogue of the summation convention described by Karr [1], especially definition 3 of section 1.4. The product: .. math:: \prod_{m \leq i < n} f(i) has *the obvious meaning* for `m < n`, namely: .. math:: \prod_{m \leq i < n} f(i) = f(m) f(m+1) \cdot \ldots \cdot f(n-2) f(n-1) with the upper limit value `f(n)` excluded. The product over an empty set is one if and only if `m = n`: .. math:: \prod_{m \leq i < n} f(i) = 1 \quad \mathrm{for} \quad m = n Finally, for all other products over empty sets we assume the following definition: .. math:: \prod_{m \leq i < n} f(i) = \frac{1}{\prod_{n \leq i < m} f(i)} \quad \mathrm{for} \quad m > n It is important to note that above we define all products with the upper limit being exclusive. This is in contrast to the usual mathematical notation, but does not affect the product convention. Indeed we have: .. math:: \prod_{m \leq i < n} f(i) = \prod_{i = m}^{n - 1} f(i) where the difference in notation is intentional to emphasize the meaning, with limits typeset on the top being inclusive. Examples ======== >>> from sympy.abc import a, b, i, k, m, n, x >>> from sympy import Product, factorial, oo >>> Product(k,(k,1,m)) Product(k, (k, 1, m)) >>> Product(k,(k,1,m)).doit() factorial(m) >>> Product(k**2,(k,1,m)) Product(k**2, (k, 1, m)) >>> Product(k**2,(k,1,m)).doit() (factorial(m))**2 Wallis' product for pi: >>> W = Product(2*i/(2*i-1) * 2*i/(2*i+1), (i, 1, oo)) >>> W Product(4*i**2/((2*i - 1)*(2*i + 1)), (i, 1, oo)) Direct computation currently fails: >>> W.doit() nan But we can approach the infinite product by a limit of finite products: >>> from sympy import limit >>> W2 = Product(2*i/(2*i-1)*2*i/(2*i+1), (i, 1, n)) >>> W2 Product(4*i**2/((2*i - 1)*(2*i + 1)), (i, 1, n)) >>> W2e = W2.doit() >>> W2e 2**(-2*n)*4**n*(factorial(n))**2/(RisingFactorial(1/2, n)*RisingFactorial(3/2, n)) >>> limit(W2e, n, oo) pi/2 By the same formula we can compute sin(pi/2): >>> from sympy import pi, gamma, simplify >>> P = pi * x * Product(1 - x**2/k**2,(k,1,n)) >>> P = P.subs(x, pi/2) >>> P pi**2*Product(1 - pi**2/(4*k**2), (k, 1, n))/2 >>> Pe = P.doit() >>> Pe pi**2*RisingFactorial(1 + pi/2, n)*RisingFactorial(-pi/2 + 1, n)/(2*(factorial(n))**2) >>> Pe = Pe.rewrite(gamma) >>> Pe pi**2*gamma(n + 1 + pi/2)*gamma(n - pi/2 + 1)/(2*gamma(1 + pi/2)*gamma(-pi/2 + 1)*gamma(n + 1)**2) >>> Pe = simplify(Pe) >>> Pe sin(pi**2/2)*gamma(n + 1 + pi/2)*gamma(n - pi/2 + 1)/gamma(n + 1)**2 >>> limit(Pe, n, oo) sin(pi**2/2) Products with the lower limit being larger than the upper one: >>> Product(1/i, (i, 6, 1)).doit() 120 >>> Product(i, (i, 2, 5)).doit() 120 The empty product: >>> Product(i, (i, n, n-1)).doit() 1 An example showing that the symbolic result of a product is still valid for seemingly nonsensical values of the limits. Then the Karr convention allows us to give a perfectly valid interpretation to those products by interchanging the limits according to the above rules: >>> P = Product(2, (i, 10, n)).doit() >>> P 2**(n - 9) >>> P.subs(n, 5) 1/16 >>> Product(2, (i, 10, 5)).doit() 1/16 >>> 1/Product(2, (i, 6, 9)).doit() 1/16 An explicit example of the Karr summation convention applied to products: >>> P1 = Product(x, (i, a, b)).doit() >>> P1 x**(-a + b + 1) >>> P2 = Product(x, (i, b+1, a-1)).doit() >>> P2 x**(a - b - 1) >>> simplify(P1 * P2) 1 And another one: >>> P1 = Product(i, (i, b, a)).doit() >>> P1 RisingFactorial(b, a - b + 1) >>> P2 = Product(i, (i, a+1, b-1)).doit() >>> P2 RisingFactorial(a + 1, -a + b - 1) >>> P1 * P2 RisingFactorial(b, a - b + 1)*RisingFactorial(a + 1, -a + b - 1) >>> simplify(P1 * P2) 1 See Also ======== Sum, summation product References ========== .. [1] Michael Karr, "Summation in Finite Terms", Journal of the ACM, Volume 28 Issue 2, April 1981, Pages 305-350 http://dl.acm.org/citation.cfm?doid=322248.322255 .. [2] http://en.wikipedia.org/wiki/Multiplication#Capital_Pi_notation .. [3] http://en.wikipedia.org/wiki/Empty_product """ __slots__ = ['is_commutative'] def __new__(cls, function, *symbols, **assumptions): obj = ExprWithIntLimits.__new__(cls, function, *symbols, **assumptions) return obj @property def term(self): return self._args[0] function = term @property 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]) """ if self.function.is_zero or self.function == 1: return set() return self._free_symbols() @property def is_zero(self): """A Product is zero only if its term is zero. """ return self.term.is_zero @property def is_number(self): """ Return True if the Product will result in a number, else False. Examples ======== >>> 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 = 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 + 1, a - 1 f = 1 / f g = self._eval_product(f, (i, a, b)) if g is None: return self.func(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 self.func(self.function.adjoint(), *self.limits) return None def _eval_conjugate(self): return self.func(self.function.conjugate(), *self.limits) def _eval_product(self, term, limits): from sympy.concrete.delta import deltaproduct, _has_simple_delta from sympy.concrete.summations import summation from sympy.functions import KroneckerDelta (k, a, n) = limits if k not in term.free_symbols: return term**(n - a + 1) if a == n: return term.subs(k, a) if term.has(KroneckerDelta) and _has_simple_delta(term, limits[0]): return deltaproduct(term, limits) dif = n - a if dif.is_Integer: return Mul(*[term.subs(k, a + i) for i in xrange(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 = self.func(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 = self.func(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 self.func(evaluated, limits) else: return f def _eval_simplify(self, ratio, measure): from sympy.simplify.simplify import product_simplify return product_simplify(self) def _eval_transpose(self): if self.is_commutative: return self.func(self.function.transpose(), *self.limits) return None def reverse_order(expr, *indices): """ Reverse the order of a limit in a Product. Usage ===== ``reverse_order(expr, *indices)`` reverses some limits in the expression ``expr`` which can be either a ``Sum`` or a ``Product``. The selectors in the argument ``indices`` specify some indices whose limits get reversed. These selectors are either variable names or numerical indices counted starting from the inner-most limit tuple. Examples ======== >>> from sympy import Product, simplify, RisingFactorial, gamma, Sum >>> from sympy.abc import x, y, a, b, c, d >>> P = Product(x, (x, a, b)) >>> Pr = P.reverse_order(x) >>> Pr Product(1/x, (x, b + 1, a - 1)) >>> Pr = Pr.doit() >>> Pr 1/RisingFactorial(b + 1, a - b - 1) >>> simplify(Pr) gamma(b + 1)/gamma(a) >>> P = P.doit() >>> P RisingFactorial(a, -a + b + 1) >>> simplify(P) gamma(b + 1)/gamma(a) While one should prefer variable names when specifying which limits to reverse, the index counting notation comes in handy in case there are several symbols with the same name. >>> S = Sum(x*y, (x, a, b), (y, c, d)) >>> S Sum(x*y, (x, a, b), (y, c, d)) >>> S0 = S.reverse_order( 0) >>> S0 Sum(-x*y, (x, b + 1, a - 1), (y, c, d)) >>> S1 = S0.reverse_order( 1) >>> S1 Sum(x*y, (x, b + 1, a - 1), (y, d + 1, c - 1)) Of course we can mix both notations: >>> Sum(x*y, (x, a, b), (y, 2, 5)).reverse_order( x, 1) Sum(x*y, (x, b + 1, a - 1), (y, 6, 1)) >>> Sum(x*y, (x, a, b), (y, 2, 5)).reverse_order( y, x) Sum(x*y, (x, b + 1, a - 1), (y, 6, 1)) See Also ======== index, reorder_limit, reorder References ========== .. [1] Michael Karr, "Summation in Finite Terms", Journal of the ACM, Volume 28 Issue 2, April 1981, Pages 305-350 http://dl.acm.org/citation.cfm?doid=322248.322255 """ l_indices = list(indices) for i, indx in enumerate(l_indices): if not isinstance(indx, int): l_indices[i] = expr.index(indx) e = 1 limits = [] for i, limit in enumerate(expr.limits): l = limit if i in l_indices: e = -e l = (limit[0], limit[2] + 1 , limit[1] - 1) limits.append(l) return Product(expr.function ** e, *limits) 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)) factorial(k) >>> product(m, (i, 1, k)) m**k >>> product(i, (i, 1, k), (k, 1, n)) Product(factorial(k), (k, 1, n)) """ prod = Product(*args, **kwargs) if isinstance(prod, Product): return prod.doit(deep=False) else: return prod sympy-0.7.4.1/sympy/concrete/gosper.py0000644000175000017500000001263712253362407020111 0ustar georgeskgeorgesk"""Gosper's algorithm for hypergeometric summation. """ from __future__ import print_function, division from sympy.core import S, Dummy, symbols from sympy.core.compatibility import is_sequence, xrange from sympy.polys import Poly, parallel_poly_from_expr, factor from sympy.solvers import solve from sympy.simplify import hypersimp 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 xrange(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 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() 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)) (-factorial(n) + 2*factorial(2*n + 1))/factorial(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*factorial(n) + factorial(2*n + 1))/(60*factorial(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) if result is S.NaN: try: result = (f*(g + 1)).limit(k, b) - (f*g).limit(k, a) except NotImplementedError: result = None return factor(result) sympy-0.7.4.1/sympy/concrete/delta.py0000644000175000017500000002260312253362407017675 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import Add, Interval, Mul, S, Dummy, symbols from sympy.core.cache import cacheit from sympy.core.compatibility import default_sort_key from sympy.functions import KroneckerDelta, Piecewise, piecewise_fold @cacheit def _expand_delta(expr, index): """ Expand the first Add containing a simple KroneckerDelta. """ if not expr.is_Mul: return expr delta = None func = Add terms = [S(1)] for h in expr.args: if delta is None and h.is_Add and _has_simple_delta(h, index): delta = True func = h.func terms = [terms[0]*t for t in h.args] else: terms = [t*h for t in terms] return func(*terms) @cacheit def _extract_delta(expr, index): """ Extract a simple KroneckerDelta from the expression. Returns the tuple ``(delta, newexpr)`` where: - ``delta`` is a simple KroneckerDelta expression if one was found, or ``None`` if no simple KroneckerDelta expression was found. - ``newexpr`` is a Mul containing the remaining terms; ``expr`` is returned unchanged if no simple KroneckerDelta expression was found. Examples ======== >>> from sympy import KroneckerDelta >>> from sympy.concrete.delta import _extract_delta >>> from sympy.abc import x, y, i, j, k >>> _extract_delta(4*x*y*KroneckerDelta(i, j), i) (KroneckerDelta(i, j), 4*x*y) >>> _extract_delta(4*x*y*KroneckerDelta(i, j), k) (None, 4*x*y*KroneckerDelta(i, j)) See Also ======== sympy.functions.special.tensor_functions.KroneckerDelta deltaproduct deltasummation """ if not _has_simple_delta(expr, index): return (None, expr) if isinstance(expr, KroneckerDelta): return (expr, S(1)) assert expr.is_Mul delta = None terms = [] for arg in expr.args: if delta is None and _is_simple_delta(arg, index): delta = arg else: terms.append(arg) return (delta, expr.func(*terms)) @cacheit def _has_simple_delta(expr, index): """ Returns True if ``expr`` is an expression that contains a KroneckerDelta that is simple in the index ``index``, meaning that this KroneckerDelta is nonzero for a single value of the index ``index``. """ if expr.has(KroneckerDelta): if _is_simple_delta(expr, index): return True if expr.is_Add or expr.is_Mul: for arg in expr.args: if _has_simple_delta(arg, index): return True return False @cacheit def _is_simple_delta(delta, index): """ Returns True if ``delta`` is a KroneckerDelta and is nonzero for a single value of the index ``index``. """ if isinstance(delta, KroneckerDelta) and delta.has(index): p = (delta.args[0] - delta.args[1]).as_poly(index) if p: return p.degree() == 1 return False @cacheit def _remove_multiple_delta(expr): """ Evaluate products of KroneckerDelta's. """ from sympy.solvers import solve if expr.is_Add: return expr.func(*list(map(_remove_multiple_delta, expr.args))) if not expr.is_Mul: return expr eqs = [] newargs = [] for arg in expr.args: if isinstance(arg, KroneckerDelta): eqs.append(arg.args[0] - arg.args[1]) else: newargs.append(arg) if not eqs: return expr solns = solve(eqs, dict=True) if len(solns) == 0: return S.Zero elif len(solns) == 1: for key in solns[0].keys(): newargs.append(KroneckerDelta(key, solns[0][key])) expr2 = expr.func(*newargs) if expr != expr2: return _remove_multiple_delta(expr2) return expr @cacheit def _simplify_delta(expr): """ Rewrite a KroneckerDelta's indices in its simplest form. """ from sympy.solvers import solve if isinstance(expr, KroneckerDelta): try: slns = solve(expr.args[0] - expr.args[1], dict=True) if slns and len(slns) == 1: return Mul(*[KroneckerDelta(*(key, value)) for key, value in slns[0].items()]) except NotImplementedError: pass return expr @cacheit def deltaproduct(f, limit): """ Handle products containing a KroneckerDelta. See Also ======== deltasummation sympy.functions.special.tensor_functions.KroneckerDelta sympy.concrete.products.product """ from sympy.concrete.products import product if ((limit[2] - limit[1]) < 0) is True: return S.One if not f.has(KroneckerDelta): return product(f, limit) if f.is_Add: # Identify the term in the Add that has a simple KroneckerDelta delta = None terms = [] for arg in sorted(f.args, key=default_sort_key): if delta is None and _has_simple_delta(arg, limit[0]): delta = arg else: terms.append(arg) newexpr = f.func(*terms) k = Dummy("kprime", integer=True) if isinstance(limit[1], int) and isinstance(limit[2], int): result = deltaproduct(newexpr, limit) + sum([ deltaproduct(newexpr, (limit[0], limit[1], ik - 1)) * delta.subs(limit[0], ik) * deltaproduct(newexpr, (limit[0], ik + 1, limit[2])) for ik in range(int(limit[1]), int(limit[2] + 1))] ) else: result = deltaproduct(newexpr, limit) + deltasummation( deltaproduct(newexpr, (limit[0], limit[1], k - 1)) * delta.subs(limit[0], k) * deltaproduct(newexpr, (limit[0], k + 1, limit[2])), (k, limit[1], limit[2]), no_piecewise=_has_simple_delta(newexpr, limit[0]) ) return _remove_multiple_delta(result) delta, _ = _extract_delta(f, limit[0]) if not delta: g = _expand_delta(f, limit[0]) if f != g: from sympy import factor try: return factor(deltaproduct(g, limit)) except AssertionError: return deltaproduct(g, limit) return product(f, limit) from sympy import Eq c = Eq(limit[2], limit[1] - 1) return _remove_multiple_delta(f.subs(limit[0], limit[1])*KroneckerDelta(limit[2], limit[1])) + \ S.One*_simplify_delta(KroneckerDelta(limit[2], limit[1] - 1)) @cacheit def deltasummation(f, limit, no_piecewise=False): """ Handle summations containing a KroneckerDelta. The idea for summation is the following: - If we are dealing with a KroneckerDelta expression, i.e. KroneckerDelta(g(x), j), we try to simplify it. If we could simplify it, then we sum the resulting expression. We already know we can sum a simplified expression, because only simple KroneckerDelta expressions are involved. If we couldn't simplify it, there are two cases: 1) The expression is a simple expression: we return the summation, taking care if we are dealing with a Derivative or with a proper KroneckerDelta. 2) The expression is not simple (i.e. KroneckerDelta(cos(x))): we can do nothing at all. - If the expr is a multiplication expr having a KroneckerDelta term: First we expand it. If the expansion did work, then we try to sum the expansion. If not, we try to extract a simple KroneckerDelta term, then we have two cases: 1) We have a simple KroneckerDelta term, so we return the summation. 2) We didn't have a simple term, but we do have an expression with simplified KroneckerDelta terms, so we sum this expression. Examples ======== >>> from sympy import oo >>> from sympy.abc import i, j, k >>> from sympy.concrete.delta import deltasummation >>> from sympy import KroneckerDelta, Piecewise >>> deltasummation(KroneckerDelta(i, k), (k, -oo, oo)) 1 >>> deltasummation(KroneckerDelta(i, k), (k, 0, oo)) Piecewise((1, i >= 0), (0, True)) >>> deltasummation(KroneckerDelta(i, k), (k, 1, 3)) Piecewise((1, And(1 <= i, i <= 3)), (0, True)) >>> deltasummation(k*KroneckerDelta(i, j)*KroneckerDelta(j, k), (k, -oo, oo)) j*KroneckerDelta(i, j) >>> deltasummation(j*KroneckerDelta(i, j), (j, -oo, oo)) i >>> deltasummation(i*KroneckerDelta(i, j), (i, -oo, oo)) j See Also ======== deltaproduct sympy.functions.special.tensor_functions.KroneckerDelta sympy.concrete.sums.summation """ from sympy.concrete.summations import summation from sympy.solvers import solve if ((limit[2] - limit[1]) < 0) is True: return S.Zero if not f.has(KroneckerDelta): return summation(f, limit) x = limit[0] g = _expand_delta(f, x) if g.is_Add: return piecewise_fold( g.func(*[deltasummation(h, limit, no_piecewise) for h in g.args])) # try to extract a simple KroneckerDelta term delta, expr = _extract_delta(g, x) if not delta: return summation(f, limit) solns = solve(delta.args[0] - delta.args[1], x) if len(solns) == 0: return S.Zero elif len(solns) != 1: return Sum(f, limit) value = solns[0] if no_piecewise: return expr.subs(x, value) return Piecewise( (expr.subs(x, value), Interval(*limit[1:3]).as_relational(value)), (S.Zero, True) ) sympy-0.7.4.1/sympy/concrete/expr_with_intlimits.py0000644000175000017500000002324412253362407022713 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.concrete.expr_with_limits import ExprWithLimits from sympy.core.singleton import S from sympy.core.expr import Expr from sympy.core.relational import Eq from sympy.core.sets import Interval from sympy.core.symbol import (Dummy, Wild, Symbol) from sympy.core.sympify import sympify from sympy.core.compatibility import is_sequence, xrange from sympy.core.containers import Tuple from sympy.functions.elementary.piecewise import piecewise_fold, Piecewise from sympy.utilities import flatten class ReorderError(NotImplementedError): """ Exception raised when trying to reorder dependent limits. """ def __init__(self, expr, msg): super(ReorderError, self).__init__( "%s could not be reordered: %s." % (expr, msg)) class ExprWithIntLimits(ExprWithLimits): def change_index(self, var, trafo, newvar=None): """ Change index of a Sum or Product. Perform a linear transformation `x \mapsto a x + b` on the index variable `x`. For `a` the only values allowed are `\pm 1`. A new variable to be used after the change of index can also be specified. Usage ===== ``change_index(expr, var, trafo, newvar=None)`` where ``var`` specifies the index variable `x` to transform. The transformation ``trafo`` must be linear and given in terms of ``var``. If the optional argument ``newvar`` is provided then ``var`` gets replaced by ``newvar`` in the final expression. Examples ======== >>> from sympy import Sum, Product, simplify >>> from sympy.abc import x, y, a, b, c, d, u, v, i, j, k, l >>> S = Sum(x, (x, a, b)) >>> S.doit() -a**2/2 + a/2 + b**2/2 + b/2 >>> Sn = S.change_index(x, x + 1, y) >>> Sn Sum(y - 1, (y, a + 1, b + 1)) >>> Sn.doit() -a**2/2 + a/2 + b**2/2 + b/2 >>> Sn = S.change_index(x, -x, y) >>> Sn Sum(-y, (y, -b, -a)) >>> Sn.doit() -a**2/2 + a/2 + b**2/2 + b/2 >>> Sn = S.change_index(x, x+u) >>> Sn Sum(-u + x, (x, a + u, b + u)) >>> Sn.doit() -a**2/2 - a*u + a/2 + b**2/2 + b*u + b/2 - u*(-a + b + 1) + u >>> simplify(Sn.doit()) -a**2/2 + a/2 + b**2/2 + b/2 >>> Sn = S.change_index(x, -x - u, y) >>> Sn Sum(-u - y, (y, -b - u, -a - u)) >>> Sn.doit() -a**2/2 - a*u + a/2 + b**2/2 + b*u + b/2 - u*(-a + b + 1) + u >>> simplify(Sn.doit()) -a**2/2 + a/2 + b**2/2 + b/2 >>> P = Product(i*j**2, (i, a, b), (j, c, d)) >>> P Product(i*j**2, (i, a, b), (j, c, d)) >>> P2 = P.change_index(i, i+3, k) >>> P2 Product(j**2*(k - 3), (k, a + 3, b + 3), (j, c, d)) >>> P3 = P2.change_index(j, -j, l) >>> P3 Product(l**2*(k - 3), (k, a + 3, b + 3), (l, -d, -c)) When dealing with symbols only, we can make a general linear transformation: >>> Sn = S.change_index(x, u*x+v, y) >>> Sn Sum((-v + y)/u, (y, b*u + v, a*u + v)) >>> Sn.doit() -v*(a*u - b*u + 1)/u + (a**2*u**2/2 + a*u*v + a*u/2 - b**2*u**2/2 - b*u*v + b*u/2 + v)/u >>> simplify(Sn.doit()) a**2*u/2 + a/2 - b**2*u/2 + b/2 However, the last result can be inconsistent with usual summation where the index increment is always 1. This is obvious as we get back the original value only for ``u`` equal +1 or -1. See Also ======== sympy.concrete.simplification.index, sympy.concrete.simplification.reorder_limit, sympy.concrete.simplification.reorder, sympy.concrete.simplification.reverse_order """ if newvar is None: newvar = var limits = [] for limit in self.limits: if limit[0] == var: p = trafo.as_poly(var) if p.degree() != 1: raise ValueError("Index transformation is not linear") alpha = p.coeff_monomial(var) beta = p.coeff_monomial(S.One) if alpha.is_number: if alpha == S.One: limits.append((newvar, alpha*limit[1] + beta, alpha*limit[2] + beta)) elif alpha == S.NegativeOne: limits.append((newvar, alpha*limit[2] + beta, alpha*limit[1] + beta)) else: raise ValueError("Linear transformation results in non-linear summation stepsize") else: # Note that the case of alpha being symbolic can give issues if alpha < 0. limits.append((newvar, alpha*limit[2] + beta, alpha*limit[1] + beta)) else: limits.append(limit) function = self.function.subs(var, (var - beta)/alpha) function = function.subs(var, newvar) return self.func(function, *limits) def index(expr, x): """ Return the index of a dummy variable in the list of limits. Usage ===== ``index(expr, x)`` returns the index of the dummy variable ``x`` in the limits of ``expr``. Note that we start counting with 0 at the inner-most limits tuple. Examples ======== >>> from sympy.abc import x, y, a, b, c, d >>> from sympy import Sum, Product >>> Sum(x*y, (x, a, b), (y, c, d)).index(x) 0 >>> Sum(x*y, (x, a, b), (y, c, d)).index(y) 1 >>> Product(x*y, (x, a, b), (y, c, d)).index(x) 0 >>> Product(x*y, (x, a, b), (y, c, d)).index(y) 1 See Also ======== reorder_limit, reorder, reverse_order """ variables = [limit[0] for limit in expr.limits] if variables.count(x) != 1: raise ValueError(expr, "Number of instances of variable not equal to one") else: return variables.index(x) def reorder(expr, *arg): """ Reorder limits in a expression containing a Sum or a Product. Usage ===== ``expr.reorder(*arg)`` reorders the limits in the expression ``expr`` according to the list of tuples given by ``arg``. These tuples can contain numerical indices or index variable names or involve both. Examples ======== >>> from sympy import Sum, Product >>> from sympy.abc import x, y, z, a, b, c, d, e, f >>> Sum(x*y, (x, a, b), (y, c, d)).reorder((x, y)) Sum(x*y, (y, c, d), (x, a, b)) >>> Sum(x*y*z, (x, a, b), (y, c, d), (z, e, f)).reorder((x, y), (x, z), (y, z)) Sum(x*y*z, (z, e, f), (y, c, d), (x, a, b)) >>> P = Product(x*y*z, (x, a, b), (y, c, d), (z, e, f)) >>> P.reorder((x, y), (x, z), (y, z)) Product(x*y*z, (z, e, f), (y, c, d), (x, a, b)) We can also select the index variables by counting them, starting with the inner-most one: >>> Sum(x**2, (x, a, b), (x, c, d)).reorder((0, 1)) Sum(x**2, (x, c, d), (x, a, b)) And of course we can mix both schemes: >>> Sum(x*y, (x, a, b), (y, c, d)).reorder((y, x)) Sum(x*y, (y, c, d), (x, a, b)) >>> Sum(x*y, (x, a, b), (y, c, d)).reorder((y, 0)) Sum(x*y, (y, c, d), (x, a, b)) See Also ======== reorder_limit, index, reverse_order """ new_expr = expr for r in arg: if len(r) != 2: raise ValueError(r, "Invalid number of arguments") index1 = r[0] index2 = r[1] if not isinstance(r[0], int): index1 = expr.index(r[0]) if not isinstance(r[1], int): index2 = expr.index(r[1]) new_expr = new_expr.reorder_limit(index1, index2) return new_expr def reorder_limit(expr, x, y): """ Interchange two limit tuples of a Sum or Product expression. Usage ===== ``expr.reorder_limit(x, y)`` interchanges two limit tuples. The arguments ``x`` and ``y`` are integers corresponding to the index variables of the two limits which are to be interchanged. The expression ``expr`` has to be either a Sum or a Product. Examples ======== >>> from sympy.abc import x, y, z, a, b, c, d, e, f >>> from sympy import Sum, Product >>> Sum(x*y*z, (x, a, b), (y, c, d), (z, e, f)).reorder_limit(0, 2) Sum(x*y*z, (z, e, f), (y, c, d), (x, a, b)) >>> Sum(x**2, (x, a, b), (x, c, d)).reorder_limit(1, 0) Sum(x**2, (x, c, d), (x, a, b)) >>> Product(x*y*z, (x, a, b), (y, c, d), (z, e, f)).reorder_limit(0, 2) Product(x*y*z, (z, e, f), (y, c, d), (x, a, b)) See Also ======== index, reorder, reverse_order """ var = set([limit[0] for limit in expr.limits]) limit_x = expr.limits[x] limit_y = expr.limits[y] if (len(set(limit_x[1].free_symbols).intersection(var)) == 0 and len(set(limit_x[2].free_symbols).intersection(var)) == 0 and len(set(limit_y[1].free_symbols).intersection(var)) == 0 and len(set(limit_y[2].free_symbols).intersection(var)) == 0): limits = [] for i, limit in enumerate(expr.limits): if i == x: limits.append(limit_y) elif i == y: limits.append(limit_x) else: limits.append(limit) return type(expr)(expr.function, *limits) else: raise ReorderError(expr, "could not interchange the two limits specified") sympy-0.7.4.1/sympy/concrete/expr_with_limits.py0000644000175000017500000003221212253362407022173 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core.basic import C from sympy.core.expr import Expr from sympy.core.relational import Eq from sympy.core.sets import Interval from sympy.core.singleton import S from sympy.core.symbol import (Dummy, Wild, Symbol) from sympy.core.sympify import sympify from sympy.core.compatibility import is_sequence, xrange from sympy.core.containers import Tuple from sympy.functions.elementary.piecewise import piecewise_fold, Piecewise from sympy.utilities import flatten def _process_limits(*symbols): """Process the list of symbols and convert them to canonical limits, storing them as Tuple(symbol, lower, upper). The orientation of the function is also returned when the upper limit is missing so (x, 1, None) becomes (x, None, 1) and the orientation is changed. """ limits = [] orientation = 1 for V in symbols: if isinstance(V, Symbol): limits.append(Tuple(V)) continue elif is_sequence(V, Tuple): V = sympify(flatten(V)) if V[0].is_Symbol: newsymbol = V[0] if len(V) == 2 and isinstance(V[1], Interval): V[1:] = [V[1].start, V[1].end] if len(V) == 3: if V[1] is None and V[2] is not None: nlim = [V[2]] elif V[1] is not None and V[2] is None: orientation *= -1 nlim = [V[1]] elif V[1] is None and V[2] is None: nlim = [] else: nlim = V[1:] limits.append(Tuple(newsymbol, *nlim )) continue elif len(V) == 1 or (len(V) == 2 and V[1] is None): limits.append(Tuple(newsymbol)) continue elif len(V) == 2: limits.append(Tuple(newsymbol, V[1])) continue raise ValueError('Invalid limits given: %s' % str(symbols)) return limits, orientation class ExprWithLimits(Expr): __slots__ = ['is_commutative'] def __new__(cls, function, *symbols, **assumptions): # 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 symbols: limits, orientation = _process_limits(*symbols) else: # symbol not provided -- we can still try to compute a general form free = function.free_symbols if len(free) != 1: raise ValueError( "specify dummy variables for %s" % function) limits, orientation = [Tuple(s) for s in free], 1 # denest any nested calls while cls == type(function): limits = list(function.limits) + limits function = function.function # Only limits with lower and upper bounds are supported; the indefinite form # is not supported if any(len(l) != 3 or None in l for l in limits): raise ValueError('ExprWithLimits requires values for lower and upper bounds.') obj = Expr.__new__(cls, **assumptions) arglist = [function] arglist.extend(limits) obj._args = tuple(arglist) obj.is_commutative = function.is_commutative # limits already checked return obj @property def function(self): """Return the function applied across limits. Examples ======== >>> from sympy import Integral >>> from sympy.abc import x >>> Integral(x**2, (x,)).function x**2 See Also ======== limits, variables, free_symbols """ return self._args[0] @property def limits(self): """Return the limits of expression. Examples ======== >>> from sympy import Integral >>> from sympy.abc import x, i >>> Integral(x**i, (i, 1, 3)).limits ((i, 1, 3),) See Also ======== function, variables, free_symbols """ return self._args[1:] @property def variables(self): """Return a list of the dummy variables >>> from sympy import Sum >>> from sympy.abc import x, i >>> Sum(x**i, (i, 1, 3)).variables [i] See Also ======== function, limits, free_symbols as_dummy : Rename dummy variables transform : Perform mapping on the dummy variable """ return [l[0] for l in self.limits] @property def free_symbols(self): if self.function.is_zero: return set() return self._free_symbols() def as_dummy(self): """ see _as_dummy() for documentation """ return self._as_dummy() def _free_symbols(self): """ This method returns the symbols that will exist when the object is evaluated. This is useful if one is trying to determine whether the objet contains a certain symbol or not. Examples ======== >>> from sympy import Sum >>> from sympy.abc import x, y >>> Sum(x, (x, y, 1)).free_symbols set([y]) """ function, limits = self.function, self.limits if function.is_zero: return set() isyms = function.free_symbols for xab in limits: if len(xab) == 1: isyms.add(xab[0]) continue # 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 def _as_dummy(self): """ Replace instances of the given dummy variables with explicit dummy counterparts to make clear what are dummy variables and what are real-world symbols in an object. Examples ======== >>> from sympy import Integral >>> from sympy.abc import x, y >>> Integral(x, (x, x, y), (y, x, y)).as_dummy() Integral(_x, (_x, x, _y), (_y, x, y)) If the object supperts the "integral at" limit ``(x,)`` it is not treated as a dummy, but the explicit form, ``(x, x)`` of length 2 does treat the variable as a dummy. >>> Integral(x, x).as_dummy() Integral(x, x) >>> Integral(x, (x, x)).as_dummy() Integral(_x, (_x, x)) If there were no dummies in the original expression, then the the symbols which cannot be changed by subs() are clearly seen as those with an underscore prefix. See Also ======== variables : Lists the integration variables transform : Perform mapping on the integration variable """ reps = {} f = self.function limits = list(self.limits) for i in xrange(-1, -len(limits) - 1, -1): xab = list(limits[i]) if len(xab) == 1: continue x = xab[0] xab[0] = x.as_dummy() for j in range(1, len(xab)): xab[j] = xab[j].subs(reps) reps[x] = xab[0] limits[i] = xab f = f.subs(reps) return self.func(f, *limits) def _eval_subs(self, old, new): """ Perform substitutions over non-dummy variables of an expression with limits. Also, can be used to specify point-evaluation of an abstract antiderivative. Examples ======== >>> from sympy import Sum, oo >>> from sympy.abc import s,n >>> Sum(1/n**s, (n, 1, oo)).subs(s, 2) Sum(n**(-2), (n, 1, oo)) >>> from sympy import Integral >>> from sympy.abc import x,a >>> Integral(a*x**2,x).subs(x,4) Integral(a*x**2, (x, 4)) See Also ======== variables : Lists the integration variables transform : Perform mapping on the dummy variable for intgrals change_index : Perform mapping on the sum and product dummy variables """ func, limits = self.function, list(self.limits) # If one of the expressions we are replacing is used as a func index # one of two things happens. # - the old variable first appears as a free variable # so we perform all free substitutions before it becomes # a func index. # - the old variable first appears as a func index, in # which case we ignore. See change_index. # Reorder limits to match standard mathematical practice for scoping limits.reverse() if not isinstance(old, C.Symbol) or \ old.free_symbols.intersection(self.free_symbols): sub_into_func = True for i, xab in enumerate(limits): if 1 == len(xab) and old == xab[0]: xab = (old, old) limits[i] = Tuple(xab[0], *[l._subs(old, new) for l in xab[1:]]) if len(xab[0].free_symbols.intersection(old.free_symbols)) != 0: sub_into_func = False break if isinstance(old,C.AppliedUndef) or isinstance(old,C.UndefinedFunction): sy2 = set(self.variables).intersection(set(new.atoms(Symbol))) sy1 = set(self.variables).intersection(set(old.args)) if not sy2.issubset(sy1): raise ValueError( "substitution can not create dummy dependencies") sub_into_func = True if sub_into_func: func = func.subs(old, new) else: # old is a Symbol and a dummy variable of some limit for i, xab in enumerate(limits): if len(xab) == 3: limits[i] = Tuple(xab[0], *[l._subs(old, new) for l in xab[1:]]) if old == xab[0]: break # simplify redundant limits (x, x) to (x, ) for i, xab in enumerate(limits): if len(xab) == 2 and (xab[0] - xab[1]).is_zero: limits[i] = Tuple(xab[0], ) # Reorder limits back to representation-form limits.reverse() return self.func(func, *limits) class AddWithLimits(ExprWithLimits): r"""Represents unevaluated oriented additions of integer sequences. """ def __new__(cls, function, *symbols, **assumptions): # 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. # # This constructor only differs from ExprWithLimits # in the application of the orientation variable. Perhaps merge? function = piecewise_fold(sympify(function)) if function is S.NaN: return S.NaN if symbols: limits, orientation = _process_limits(*symbols) else: # symbol not provided -- we can still try to compute a general form free = function.free_symbols if len(free) != 1: raise ValueError( "specify dummy variables for %s" % function) limits, orientation = [Tuple(s) for s in free], 1 # denest any nested calls while cls == type(function): limits = list(function.limits) + limits function = function.function obj = Expr.__new__(cls, **assumptions) arglist = [orientation*function] arglist.extend(limits) obj._args = tuple(arglist) obj.is_commutative = function.is_commutative # limits already checked return obj def _eval_adjoint(self): if all([x.is_real for x in flatten(self.limits)]): return self.func(self.function.adjoint(), *self.limits) return None def _eval_conjugate(self): if all([x.is_real for x in flatten(self.limits)]): return self.func(self.function.conjugate(), *self.limits) return None def _eval_transpose(self): if all([x.is_real for x in flatten(self.limits)]): return self.func(self.function.transpose(), *self.limits) return None def _eval_factor(self, **hints): summand = self.function.factor(**hints) keep_inside = [] pull_outside = [] if summand.is_Mul and summand.is_commutative: for i in summand.args: if not i.atoms(C.Symbol).intersection(self.variables): pull_outside.append(i) else: keep_inside.append(i) return C.Mul(*pull_outside) * self.func(C.Mul(*keep_inside), *self.limits) return self def _eval_expand_basic(self, **hints): summand = self.function.expand(**hints) if summand.is_Add and summand.is_commutative: return C.Add(*[ self.func(i, *self.limits) for i in summand.args ]) elif summand != self.function: return self.func(summand, *self.limits) return self sympy-0.7.4.1/sympy/concrete/tests/0000755000175000017500000000000012253362407017371 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/concrete/tests/test_sums_products.py0000644000175000017500000007072312253362407023725 0ustar georgeskgeorgeskfrom sympy import ( Abs, And, binomial, Catalan, cos, Derivative, E, Eq, exp, EulerGamma, factorial, Function, harmonic, I, Integral, KroneckerDelta, log, nan, oo, pi, Piecewise, Product, product, Rational, S, simplify, sqrt, Sum, summation, Symbol, symbols, sympify, zeta, gamma ) from sympy.abc import a, b, c, d, f, k, m, x, y, z from sympy.concrete.summations import telescopic from sympy.utilities.pytest import XFAIL, raises from sympy import simplify n = Symbol('n', integer=True) def test_karr_convention(): # Test the Karr summation convention that we want to hold. # See his paper "Summation in Finite Terms" for a detailed # reasoning why we really want exactly this definition. # The convention is described on page 309 and essentially # in section 1.4, definition 3: # # \sum_{m <= i < n} f(i) 'has the obvious meaning' for m < n # \sum_{m <= i < n} f(i) = 0 for m = n # \sum_{m <= i < n} f(i) = - \sum_{n <= i < m} f(i) for m > n # # It is important to note that he defines all sums with # the upper limit being *exclusive*. # In contrast, sympy and the usual mathematical notation has: # # sum_{i = a}^b f(i) = f(a) + f(a+1) + ... + f(b-1) + f(b) # # with the upper limit *inclusive*. So translating between # the two we find that: # # \sum_{m <= i < n} f(i) = \sum_{i = m}^{n-1} f(i) # # where we intentionally used two different ways to typeset the # sum and its limits. i = Symbol("i", integer=True) k = Symbol("k", integer=True) j = Symbol("j", integer=True) # A simple example with a concrete summand and symbolic limits. # The normal sum: m = k and n = k + j and therefore m < n: m = k n = k + j a = m b = n - 1 S1 = Sum(i**2, (i, a, b)).doit() # The reversed sum: m = k + j and n = k and therefore m > n: m = k + j n = k a = m b = n - 1 S2 = Sum(i**2, (i, a, b)).doit() assert simplify(S1 + S2) == 0 # Test the empty sum: m = k and n = k and therefore m = n: m = k n = k a = m b = n - 1 Sz = Sum(i**2, (i, a, b)).doit() assert Sz == 0 # Another example this time with an unspecified summand and # numeric limits. (We can not do both tests in the same example.) f = Function("f") # The normal sum with m < n: m = 2 n = 11 a = m b = n - 1 S1 = Sum(f(i), (i, a, b)).doit() # The reversed sum with m > n: m = 11 n = 2 a = m b = n - 1 S2 = Sum(f(i), (i, a, b)).doit() assert simplify(S1 + S2) == 0 # Test the empty sum with m = n: m = 5 n = 5 a = m b = n - 1 Sz = Sum(f(i), (i, a, b)).doit() assert Sz == 0 def test_karr_proposition_2a(): # Test Karr, page 309, proposition 2, part a i = Symbol("i", integer=True) u = Symbol("u", integer=True) v = Symbol("v", integer=True) def test_the_sum(m, n): # g g = i**3 + 2*i**2 - 3*i # f = Delta g f = simplify(g.subs(i, i+1) - g) # The sum a = m b = n - 1 S = Sum(f, (i, a, b)).doit() # Test if Sum_{m <= i < n} f(i) = g(n) - g(m) assert simplify(S - (g.subs(i, n) - g.subs(i, m))) == 0 # m < n test_the_sum(u, u+v) # m = n test_the_sum(u, u ) # m > n test_the_sum(u+v, u ) def test_karr_proposition_2b(): # Test Karr, page 309, proposition 2, part b i = Symbol("i", integer=True) u = Symbol("u", integer=True) v = Symbol("v", integer=True) w = Symbol("w", integer=True) def test_the_sum(l, n, m): # Summand s = i**3 # First sum a = l b = n - 1 S1 = Sum(s, (i, a, b)).doit() # Second sum a = l b = m - 1 S2 = Sum(s, (i, a, b)).doit() # Third sum a = m b = n - 1 S3 = Sum(s, (i, a, b)).doit() # Test if S1 = S2 + S3 as required assert S1 - (S2 + S3) == 0 # l < m < n test_the_sum(u, u+v, u+v+w) # l < m = n test_the_sum(u, u+v, u+v ) # l < m > n test_the_sum(u, u+v+w, v ) # l = m < n test_the_sum(u, u, u+v ) # l = m = n test_the_sum(u, u, u ) # l = m > n test_the_sum(u+v, u+v, u ) # l > m < n test_the_sum(u+v, u, u+w ) # l > m = n test_the_sum(u+v, u, u ) # l > m > n test_the_sum(u+v+w, u+v, u ) def test_arithmetic_sums(): assert summation(1, (n, a, b)) == b - a + 1 assert Sum(S.NaN, (n, a, b)) is S.NaN assert Sum(x, (n, a, a)).doit() == x assert Sum(x, (x, a, a)).doit() == a assert Sum(x, (n, 1, a)).doit() == a*x lo, hi = 1, 2 s1 = Sum(n, (n, lo, hi)) s2 = Sum(n, (n, hi, lo)) assert s1 != s2 assert s1.doit() == 3 and s2.doit() == 0 lo, hi = x, x + 1 s1 = Sum(n, (n, lo, hi)) s2 = Sum(n, (n, hi, lo)) assert s1 != s2 assert s1.doit() == 2*x + 1 and s2.doit() == 0 assert Sum(Integral(x, (x, 1, y)) + x, (x, 1, 2)).doit() == \ y**2 + 2 assert summation(1, (n, 1, 10)) == 10 assert summation(2*n, (n, 0, 10**10)) == 100000000010000000000 assert summation(4*n*m, (n, a, 1), (m, 1, d)).expand() == \ 2*d + 2*d**2 + a*d + a*d**2 - d*a**2 - a**2*d**2 assert summation(cos(n), (n, -2, 1)) == cos(-2) + cos(-1) + cos(0) + cos(1) assert summation(cos(n), (n, x, x + 2)) == cos(x) + cos(x + 1) + cos(x + 2) assert isinstance(summation(cos(n), (n, x, x + S.Half)), Sum) assert summation(k, (k, 0, oo)) == oo def test_polynomial_sums(): assert summation(n**2, (n, 3, 8)) == 199 assert summation(n, (n, a, b)) == \ ((a + b)*(b - a + 1)/2).expand() assert summation(n**2, (n, 1, b)) == \ ((2*b**3 + 3*b**2 + b)/6).expand() assert summation(n**3, (n, 1, b)) == \ ((b**4 + 2*b**3 + b**2)/4).expand() assert summation(n**6, (n, 1, b)) == \ ((6*b**7 + 21*b**6 + 21*b**5 - 7*b**3 + b)/42).expand() def test_geometric_sums(): assert summation(pi**n, (n, 0, b)) == (1 - pi**(b + 1)) / (1 - pi) assert summation(2 * 3**n, (n, 0, b)) == 3**(b + 1) - 1 assert summation(Rational(1, 2)**n, (n, 1, oo)) == 1 assert summation(2**n, (n, 0, b)) == 2**(b + 1) - 1 assert summation(2**n, (n, 1, oo)) == oo assert summation(2**(-n), (n, 1, oo)) == 1 assert summation(3**(-n), (n, 4, oo)) == Rational(1, 54) assert summation(2**(-4*n + 3), (n, 1, oo)) == Rational(8, 15) assert summation(2**(n + 1), (n, 1, b)).expand() == 4*(2**b - 1) # issue 3565: assert summation(x**n, (n, 0, oo)) == \ Piecewise((1/(-x + 1), Abs(x) < 1), (Sum(x**n, (n, 0, oo)), True)) assert summation(-2**n, (n, 0, oo)) == -oo assert summation(I**n, (n, 0, oo)) == Sum(I**n, (n, 0, oo)) # issue 3703: assert summation((-1)**(2*x + 2), (x, 0, n)) == n + 1 assert summation((-2)**(2*x + 2), (x, 0, n)) == 4*4**(n + 1)/S(3) - S(4)/3 assert summation((-1)**x, (x, 0, n)) == -(-1)**(n + 1)/S(2) + S(1)/2 assert summation(y**x, (x, a, b)) == \ Piecewise((-a + b + 1, Eq(y, 1)), ((y**a - y**(b + 1))/(-y + 1), True)) assert summation((-2)**(y*x + 2), (x, 0, n)) == \ 4*Piecewise((n + 1, Eq((-2)**y, 1)), ((-(-2)**(y*(n + 1)) + 1)/(-(-2)**y + 1), True)) def test_harmonic_sums(): assert summation(1/k, (k, 0, n)) == Sum(1/k, (k, 0, n)) assert summation(1/k, (k, 1, n)) == harmonic(n) assert summation(n/k, (k, 1, n)) == n*harmonic(n) assert summation(1/k, (k, 5, n)) == harmonic(n) - harmonic(4) def test_composite_sums(): f = Rational(1, 2)*(7 - 6*n + Rational(1, 7)*n**3) s = summation(f, (n, a, b)) assert not isinstance(s, Sum) A = 0 for i in range(-3, 5): A += f.subs(n, i) B = s.subs(a, -3).subs(b, 4) assert A == B def test_hypergeometric_sums(): assert summation( binomial(2*k, k)/4**k, (k, 0, n)) == (1 + 2*n)*binomial(2*n, n)/4**n def test_other_sums(): f = m**2 + m*exp(m) g = 3*exp(S(3)/2)/2 + exp(S(1)/2)/2 - exp(-S(1)/2)/2 - 3*exp(-S(3)/2)/2 + 5 assert summation(f, (m, -S(3)/2, S(3)/2)).expand() == g assert summation(f, (m, -1.5, 1.5)).evalf().epsilon_eq(g.evalf(), 1e-10) fac = factorial def NS(e, n=15, **options): return str(sympify(e).evalf(n, **options)) def test_evalf_fast_series(): # Euler transformed series for sqrt(1+x) assert NS(Sum( fac(2*n + 1)/fac(n)**2/2**(3*n + 1), (n, 0, oo)), 100) == NS(sqrt(2), 100) # Some series for exp(1) estr = NS(E, 100) assert NS(Sum(1/fac(n), (n, 0, oo)), 100) == estr assert NS(1/Sum((1 - 2*n)/fac(2*n), (n, 0, oo)), 100) == estr assert NS(Sum((2*n + 1)/fac(2*n), (n, 0, oo)), 100) == estr assert NS(Sum((4*n + 3)/2**(2*n + 1)/fac(2*n + 1), (n, 0, oo))**2, 100) == estr pistr = NS(pi, 100) # Ramanujan series for pi assert NS(9801/sqrt(8)/Sum(fac( 4*n)*(1103 + 26390*n)/fac(n)**4/396**(4*n), (n, 0, oo)), 100) == pistr assert NS(1/Sum( binomial(2*n, n)**3 * (42*n + 5)/2**(12*n + 4), (n, 0, oo)), 100) == pistr # Machin's formula for pi assert NS(16*Sum((-1)**n/(2*n + 1)/5**(2*n + 1), (n, 0, oo)) - 4*Sum((-1)**n/(2*n + 1)/239**(2*n + 1), (n, 0, oo)), 100) == pistr # Apery's constant astr = NS(zeta(3), 100) P = 126392*n**5 + 412708*n**4 + 531578*n**3 + 336367*n**2 + 104000* \ n + 12463 assert NS(Sum((-1)**n * P / 24 * (fac(2*n + 1)*fac(2*n)*fac( n))**3 / fac(3*n + 2) / fac(4*n + 3)**3, (n, 0, oo)), 100) == astr assert NS(Sum((-1)**n * (205*n**2 + 250*n + 77)/64 * fac(n)**10 / fac(2*n + 1)**5, (n, 0, oo)), 100) == astr def test_evalf_fast_series_issue998(): # Catalan's constant assert NS(Sum((-1)**(n - 1)*2**(8*n)*(40*n**2 - 24*n + 3)*fac(2*n)**3* fac(n)**2/n**3/(2*n - 1)/fac(4*n)**2, (n, 1, oo))/64, 100) == \ NS(Catalan, 100) astr = NS(zeta(3), 100) assert NS(5*Sum( (-1)**(n - 1)*fac(n)**2 / n**3 / fac(2*n), (n, 1, oo))/2, 100) == astr assert NS(Sum((-1)**(n - 1)*(56*n**2 - 32*n + 5) / (2*n - 1)**2 * fac(n - 1) **3 / fac(3*n), (n, 1, oo))/4, 100) == astr def test_evalf_slow_series(): assert NS(Sum((-1)**n / n, (n, 1, oo)), 15) == NS(-log(2), 15) assert NS(Sum((-1)**n / n, (n, 1, oo)), 50) == NS(-log(2), 50) assert NS(Sum(1/n**2, (n, 1, oo)), 15) == NS(pi**2/6, 15) assert NS(Sum(1/n**2, (n, 1, oo)), 100) == NS(pi**2/6, 100) assert NS(Sum(1/n**2, (n, 1, oo)), 500) == NS(pi**2/6, 500) assert NS(Sum((-1)**n / (2*n + 1)**3, (n, 0, oo)), 15) == NS(pi**3/32, 15) assert NS(Sum((-1)**n / (2*n + 1)**3, (n, 0, oo)), 50) == NS(pi**3/32, 50) def test_euler_maclaurin(): # Exact polynomial sums with E-M def check_exact(f, a, b, m, n): A = Sum(f, (k, a, b)) s, e = A.euler_maclaurin(m, n) assert (e == 0) and (s.expand() == A.doit()) check_exact(k**4, a, b, 0, 2) check_exact(k**4 + 2*k, a, b, 1, 2) check_exact(k**4 + k**2, a, b, 1, 5) check_exact(k**5, 2, 6, 1, 2) check_exact(k**5, 2, 6, 1, 3) # Not exact assert Sum(k**6, (k, a, b)).euler_maclaurin(0, 2)[1] != 0 # Numerical test for m, n in [(2, 4), (2, 20), (10, 20), (18, 20)]: A = Sum(1/k**3, (k, 1, oo)) s, e = A.euler_maclaurin(m, n) assert abs((s - zeta(3)).evalf()) < e.evalf() def test_evalf_euler_maclaurin(): assert NS(Sum(1/k**k, (k, 1, oo)), 15) == '1.29128599706266' assert NS(Sum(1/k**k, (k, 1, oo)), 50) == '1.2912859970626635404072825905956005414986193682745' assert NS(Sum(1/k - log(1 + 1/k), (k, 1, oo)), 15) == NS(EulerGamma, 15) assert NS(Sum(1/k - log(1 + 1/k), (k, 1, oo)), 50) == NS(EulerGamma, 50) assert NS(Sum(log(k)/k**2, (k, 1, oo)), 15) == '0.937548254315844' assert NS(Sum(log(k)/k**2, (k, 1, oo)), 50) == '0.93754825431584375370257409456786497789786028861483' assert NS(Sum(1/k, (k, 1000000, 2000000)), 15) == '0.693147930560008' assert NS(Sum(1/k, (k, 1000000, 2000000)), 50) == '0.69314793056000780941723211364567656807940638436025' def test_evalf_symbolic(): f, g = symbols('f g', cls=Function) # issue 3229 expr = Sum(f(x), (x, 1, 3)) + Sum(g(x), (x, 1, 3)) assert expr.evalf() == expr def test_evalf_issue_3273(): assert Sum(0, (k, 1, oo)).evalf() == 0 def test_simple_products(): assert Product(S.NaN, (x, 1, 3)) is S.NaN assert product(S.NaN, (x, 1, 3)) is S.NaN assert Product(x, (n, a, a)).doit() == x assert Product(x, (x, a, a)).doit() == a assert Product(x, (y, 1, a)).doit() == x**a lo, hi = 1, 2 s1 = Product(n, (n, lo, hi)) s2 = Product(n, (n, hi, lo)) assert s1 != s2 # This IS correct according to Karr product convention assert s1.doit() == 2 assert s2.doit() == 1 lo, hi = x, x + 1 s1 = Product(n, (n, lo, hi)) s2 = Product(n, (n, hi, lo)) s3 = 1 / Product(n, (n, hi + 1, lo - 1)) assert s1 != s2 # This IS correct according to Karr product convention assert s1.doit() == x*(x + 1) assert s2.doit() == 1 assert s3.doit() == x*(x + 1) assert Product(Integral(2*x, (x, 1, y)) + 2*x, (x, 1, 2)).doit() == \ (y**2 + 1)*(y**2 + 3) assert product(2, (n, a, b)) == 2**(b - a + 1) assert product(n, (n, 1, b)) == factorial(b) assert product(n**3, (n, 1, b)) == factorial(b)**3 assert product(3**(2 + n), (n, a, b)) \ == 3**(2*(1 - a + b) + b/2 + (b**2)/2 + a/2 - (a**2)/2) assert product(cos(n), (n, 3, 5)) == cos(3)*cos(4)*cos(5) assert product(cos(n), (n, x, x + 2)) == cos(x)*cos(x + 1)*cos(x + 2) assert isinstance(product(cos(n), (n, x, x + S.Half)), Product) # If Product managed to evaluate this one, it most likely got it wrong! assert isinstance(Product(n**n, (n, 1, b)), Product) def test_rational_products(): assert simplify(product(1 + 1/n, (n, a, b))) == (1 + b)/a assert simplify(product(n + 1, (n, a, b))) == gamma(2 + b)/gamma(1 + a) assert simplify(product((n + 1)/(n - 1), (n, a, b))) == b*(1 + b)/(a*(a - 1)) assert simplify(product(n/(n + 1)/(n + 2), (n, a, b))) == \ a*gamma(a + 2)/(b + 1)/gamma(b + 3) assert simplify(product(n*(n + 1)/(n - 1)/(n - 2), (n, a, b))) == \ b**2*(b - 1)*(1 + b)/(a - 1)**2/(a*(a - 2)) def test_wallis_product(): # Wallis product, given in two different forms to ensure that Product # can factor simple rational expressions A = Product(4*n**2 / (4*n**2 - 1), (n, 1, b)) B = Product((2*n)*(2*n)/(2*n - 1)/(2*n + 1), (n, 1, b)) half = Rational(1, 2) R = pi/2 * factorial(b)**2 / factorial(b - half) / factorial(b + half) assert simplify(A.doit()) == R assert simplify(B.doit()) == R # This one should eventually also be doable (Euler's product formula for sin) # assert Product(1+x/n**2, (n, 1, b)) == ... def test_telescopic_sums(): #checks also input 2 of comment 1 issue 1028 assert Sum(1/k - 1/(k + 1), (k, 1, n)).doit() == 1 - 1/(1 + n) f = Function("f") assert Sum( f(k) - f(k + 2), (k, m, n)).doit() == -f(1 + n) - f(2 + n) + f(m) + f(1 + m) assert Sum(cos(k) - cos(k + 3), (k, 1, n)).doit() == -cos(1 + n) - \ cos(2 + n) - cos(3 + n) + cos(1) + cos(2) + cos(3) # dummy variable shouldn't matter assert telescopic(1/m, -m/(1 + m), (m, n - 1, n)) == \ telescopic(1/k, -k/(1 + k), (k, n - 1, n)) assert Sum(1/x/(x - 1), (x, a, b)).doit() == -((a - b - 1)/(b*(a - 1))) def test_sum_reconstruct(): s = Sum(n**2, (n, -1, 1)) assert s == Sum(*s.args) raises(ValueError, lambda: Sum(x, x)) raises(ValueError, lambda: Sum(x, (x, 1))) def test_limit_subs(): for F in (Sum, Product, Integral): assert F(a*exp(a), (a, -2, 2)) == F(a*exp(a), (a, -b, b)).subs(b, 2) assert F(a, (a, F(b, (b, 1, 2)), 4)).subs(F(b, (b, 1, 2)), c) == \ F(a, (a, c, 4)) assert F(x, (x, 1, x + y)).subs(x, 1) == F(x, (x, 1, y + 1)) def test_function_subs(): f = Function("f") S = Sum(x*f(y),(x,0,oo),(y,0,oo)) assert S.subs(f(y),y) == Sum(x*y,(x,0,oo),(y,0,oo)) assert S.subs(f(x),x) == S raises(ValueError, lambda: S.subs(f(y),x+y) ) S = Sum(x*log(y),(x,0,oo),(y,0,oo)) assert S.subs(log(y),y) == S f = Symbol('f') S = Sum(x*f(y),(x,0,oo),(y,0,oo)) assert S.subs(f(y),y) == Sum(x*y,(x,0,oo),(y,0,oo)) def test_equality(): # if this fails remove special handling below raises(ValueError, lambda: Sum(x, x)) r = symbols('x', real=True) for F in (Sum, Product, Integral): try: assert F(x, x) != F(y, y) assert F(x, (x, 1, 2)) != F(x, x) assert F(x, (x, x)) != F(x, x) # or else they print the same assert F(1, x) != F(1, y) except ValueError: pass assert F(a, (x, 1, 2)) != F(a, (x, 1, 3)) assert F(a, (x, 1, 2)) != F(b, (x, 1, 2)) assert F(x, (x, 1, 2)) != F(r, (r, 1, 2)) assert F(1, (x, 1, x)) != F(1, (y, 1, x)) assert F(1, (x, 1, x)) != F(1, (y, 1, y)) # issue 2166 assert Sum(x, (x, 1, x)).subs(x, a) == Sum(x, (x, 1, a)) def test_Sum_doit(): assert Sum(n*Integral(a**2), (n, 0, 2)).doit() == a**3 assert Sum(n*Integral(a**2), (n, 0, 2)).doit(deep=False) == \ 3*Integral(a**2) assert summation(n*Integral(a**2), (n, 0, 2)) == 3*Integral(a**2) # test nested sum evaluation s = Sum( Sum( Sum(2,(z,1,n+1)), (y,x+1,n)), (x,1,n)) assert 0 == (s.doit() - n*(n+1)*(n-1)).factor() assert Sum(Sum(KroneckerDelta(m, n), (m, 1, 3)), (n, 1, 3)).doit() == 3 assert Sum(Sum(KroneckerDelta(k, m), (m, 1, 3)), (n, 1, 3)).doit() == \ 3*Piecewise((1, And(S(1) <= k, k <= 3)), (0, True)) assert Sum(f(n)*Sum(KroneckerDelta(m, n), (m, 0, oo)), (n, 1, 3)).doit() == \ f(1) + f(2) + f(3) assert Sum(f(n)*Sum(KroneckerDelta(m, n), (m, 0, oo)), (n, 1, oo)).doit() == \ Sum(Piecewise((f(n), n >= 0), (0, True)), (n, 1, oo)) l = Symbol('l', integer=True, positive=True) assert Sum(f(l)*Sum(KroneckerDelta(m, l), (m, 0, oo)), (l, 1, oo)).doit() == \ Sum(f(l), (l, 1, oo)) def test_Product_doit(): assert Product(n*Integral(a**2), (n, 1, 3)).doit() == 2 * a**9 / 9 assert Product(n*Integral(a**2), (n, 1, 3)).doit(deep=False) == \ 6*Integral(a**2)**3 assert product(n*Integral(a**2), (n, 1, 3)) == 6*Integral(a**2)**3 def test_Sum_interface(): assert isinstance(Sum(0, (n, 0, 2)), Sum) assert Sum(nan, (n, 0, 2)) is nan assert Sum(nan, (n, 0, oo)) is nan assert Sum(0, (n, 0, 2)).doit() == 0 assert isinstance(Sum(0, (n, 0, oo)), Sum) assert Sum(0, (n, 0, oo)).doit() == 0 raises(ValueError, lambda: Sum(1)) raises(ValueError, lambda: summation(1)) def test_eval_diff(): assert Sum(x, (x, 1, 2)).diff(x) == 0 assert Sum(x*y, (x, 1, 2)).diff(x) == 0 assert Sum(x*y, (y, 1, 2)).diff(x) == Sum(y, (y, 1, 2)) e = Sum(x*y, (x, 1, a)) assert e.diff(a) == Derivative(e, a) assert Sum(x*y, (x, 1, 3), (a, 2, 5)).diff(y) == \ Sum(x*y, (x, 1, 3), (a, 2, 5)).doit().diff(y) == 24 def test_hypersum(): from sympy import simplify, sin, hyper assert simplify(summation(x**n/fac(n), (n, 1, oo))) == -1 + exp(x) assert summation((-1)**n * x**(2*n) / fac(2*n), (n, 0, oo)) == cos(x) assert simplify(summation((-1)**n*x**(2*n + 1) / factorial(2*n + 1), (n, 3, oo))) == -x + sin(x) + x**3/6 - x**5/120 assert summation(1/(n + 2)**3, (n, 1, oo)) == -S(9)/8 + zeta(3) assert summation(1/n**4, (n, 1, oo)) == pi**4/90 s = summation(x**n*n, (n, -oo, 0)) assert s.is_Piecewise assert s.args[0].args[0] == -1/(x*(1 - 1/x)**2) assert s.args[0].args[1] == (abs(1/x) < 1) m = Symbol('n', integer=True, positive=True) assert summation(binomial(m, k), (k, 0, m)) == 2**m def test_issue_1071(): assert summation(1/factorial(k), (k, 0, oo)) == E def test_is_zero(): for func in [Sum, Product]: assert func(0, (x, 1, 1)).is_zero is True assert func(x, (x, 1, 1)).is_zero is None def test_is_commutative(): from sympy.physics.secondquant import NO, F, Fd m = Symbol('m', commutative=False) for f in (Sum, Product, Integral): assert f(z, (z, 1, 1)).is_commutative is True assert f(z*y, (z, 1, 6)).is_commutative is True assert f(m*x, (x, 1, 2)).is_commutative is False assert f(NO(Fd(x)*F(y))*z, (z, 1, 2)).is_commutative is False def test_is_number(): assert Sum(1, (x, 1, 1)).is_number is True assert Sum(1, (x, 1, x)).is_number is False assert Sum(0, (x, y, z)).is_number is True assert Sum(x, (y, 1, 2)).is_number is False assert Sum(x, (y, 1, 1)).is_number is False assert Sum(x, (x, 1, 2)).is_number is True assert Sum(x*y, (x, 1, 2), (y, 1, 3)).is_number is True assert Product(2, (x, 1, 1)).is_number is True assert Product(2, (x, 1, y)).is_number is False assert Product(0, (x, y, z)).is_number is True assert Product(1, (x, y, z)).is_number is True assert Product(x, (y, 1, x)).is_number is False assert Product(x, (y, 1, 2)).is_number is False assert Product(x, (y, 1, 1)).is_number is False assert Product(x, (x, 1, 2)).is_number is True def test_free_symbols(): for func in [Sum, Product]: assert func(1, (x, 1, 2)).free_symbols == set() assert func(0, (x, 1, y)).free_symbols == set() assert func(2, (x, 1, y)).free_symbols == set([y]) assert func(x, (x, 1, 2)).free_symbols == set() assert func(x, (x, 1, y)).free_symbols == set([y]) assert func(x, (y, 1, y)).free_symbols == set([x, y]) assert func(x, (y, 1, 2)).free_symbols == set([x]) assert func(x, (y, 1, 1)).free_symbols == set([x]) assert func(x, (y, 1, z)).free_symbols == set([x, z]) assert func(x, (x, 1, y), (y, 1, 2)).free_symbols == set() assert func(x, (x, 1, y), (y, 1, z)).free_symbols == set([z]) assert func(x, (x, 1, y), (y, 1, y)).free_symbols == set([y]) assert func(x, (y, 1, y), (y, 1, z)).free_symbols == set([x, z]) assert Sum(1, (x, 1, y)).free_symbols == set([y]) assert Product(1, (x, 1, y)).free_symbols == set() def test_conjugate_transpose(): A, B = symbols("A B", commutative=False) p = Sum(A*B**n, (n, 1, 3)) assert p.adjoint().doit() == p.doit().adjoint() assert p.conjugate().doit() == p.doit().conjugate() assert p.transpose().doit() == p.doit().transpose() def test_issue_1072(): assert summation(factorial(2*k + 1)/factorial(2*k), (k, 0, oo)) == oo assert summation(2*k + 1, (k, 0, oo)) == oo def test_issue_3174(): assert Sum(x, (x, 1, n)).n(2, subs={n: 1}) == 1 def test_issue_3175(): assert Sum(x, (x, 1, 0)).doit() == 0 assert NS(Sum(x, (x, 1, 0))) == '0' assert Sum(n, (n, 10, 5)).doit() == -30 assert NS(Sum(n, (n, 10, 5))) == '-30.0000000000000' def test_simplify(): y, t = symbols('y, t') assert simplify(Sum(x*y, (x, n, m), (y, a, k)) + \ Sum(y, (x, n, m), (y, a, k))) == Sum(x*y + y, (x, n, m), (y, a, k)) assert simplify(Sum(x, (x, n, m)) + Sum(x, (x, m + 1, a))) == \ Sum(x, (x, n, a)) assert simplify(Sum(x, (x, k + 1, a)) + Sum(x, (x, n, k))) == \ Sum(x, (x, n, a)) assert simplify(Sum(x, (x, k + 1, a)) + Sum(x + 1, (x, n, k))) == \ Sum(x, (x, k + 1, a)) + Sum(x + 1, (x, n, k)) assert simplify(Sum(x, (x, 0, 3)) * 3 + 3 * Sum(x, (x, 4, 6)) + \ 4 * Sum(z, (z, 0, 1))) == Sum(4*z, (z, 0, 1)) + Sum(3*x, (x, 0, 6)) assert simplify(3*Sum(x**2, (x, a, b)) + Sum(x, (x, a, b))) == \ Sum(3*x**2 + x, (x, a, b)) assert simplify(Sum(x**3, (x, n, k)) * 3 + 3 * Sum(x, (x, n, k)) + \ 4 * y * Sum(z, (z, n, k))) + 1 == \ y*Sum(4*z, (z, n, k)) + Sum(3*x**3 + 3*x, (x, n, k)) + 1 assert simplify(Sum(x, (x, a, b)) + 1 + Sum(x, (x, b + 1, c))) == \ 1 + Sum(x, (x, a, c)) assert simplify(Sum(x, (t, a, b)) + Sum(y, (t, a, b)) + \ Sum(x, (t, b+1, c))) == Sum(x + y, (t, a, b)) + Sum(x, (t, b+1, c)) assert simplify(Sum(x, (t, a, b)) + Sum(x, (t, b+1, c)) + \ Sum(y, (t, a, b))) == Sum(x + y, (t, a, b)) + Sum(x, (t, b+1, c)) assert simplify(Sum(x, (t, a, b)) + 2 * Sum(x, (t, b+1, c))) == \ simplify(Sum(x, (t, a, b)) + Sum(x, (t, b+1, c)) + Sum(x, (t, b+1, c))) assert simplify(Sum(x, (x, a, b))*Sum(x**2, (x, a, b))) == \ Sum(x, (x, a, b)) * Sum(x**2, (x, a, b)) def test_change_index(): b, v = symbols('b, v', integer = True) assert Sum(x, (x, a, b)).change_index(x, x + 1, y) == \ Sum(y - 1, (y, a + 1, b + 1)) assert Sum(x**2, (x, a, b)).change_index( x, x - 1) == \ Sum((x+1)**2, (x, a - 1, b - 1)) assert Sum(x**2, (x, a, b)).change_index( x, -x, y) == \ Sum((-y)**2, (y, -b, -a)) assert Sum(x, (x, a, b)).change_index( x, -x - 1) == \ Sum(-x - 1, (x, -b - 1, -a - 1)) assert Sum(x*y, (x, a, b), (y, c, d)).change_index( x, x - 1, z) == \ Sum((z + 1)*y, (z, a - 1, b - 1), (y, c, d)) assert Sum(x, (x, a, b)).change_index( x, x + v) == \ Sum(-v + x, (x, a + v, b + v)) assert Sum(x, (x, a, b)).change_index( x, -x - v) == \ Sum(-v - x, (x, -b - v, -a - v)) def test_reorder(): b, y, c, d, z = symbols('b, y, c, d, z', integer = True) assert Sum(x*y, (x, a, b), (y, c, d)).reorder((0, 1)) == \ Sum(x*y, (y, c, d), (x, a, b)) assert Sum(x, (x, a, b), (x, c, d)).reorder((0, 1)) == \ Sum(x, (x, c, d), (x, a, b)) assert Sum(x*y + z, (x, a, b), (z, m, n), (y, c, d)).reorder(\ (2, 0), (0, 1)) == Sum(x*y + z, (z, m, n), (y, c, d), (x, a, b)) assert Sum(x*y*z, (x, a, b), (y, c, d), (z, m, n)).reorder(\ (0, 1), (1, 2), (0, 2)) == Sum(x*y*z, (x, a, b), (z, m, n), (y, c, d)) assert Sum(x*y*z, (x, a, b), (y, c, d), (z, m, n)).reorder(\ (x, y), (y, z), (x, z)) == Sum(x*y*z, (x, a, b), (z, m, n), (y, c, d)) assert Sum(x*y, (x, a, b), (y, c, d)).reorder((x, 1)) == \ Sum(x*y, (y, c, d), (x, a, b)) assert Sum(x*y, (x, a, b), (y, c, d)).reorder((y, x)) == \ Sum(x*y, (y, c, d), (x, a, b)) def test_reverse_order(): assert Sum(x, (x, 0, 3)).reverse_order(0) == Sum(-x, (x, 4, -1)) assert Sum(x*y, (x, 1, 5), (y, 0, 6)).reverse_order(0, 1) == \ Sum(x*y, (x, 6, 0), (y, 7, -1)) assert Sum(x, (x, 1, 2)).reverse_order(0) == Sum(-x, (x, 3, 0)) assert Sum(x, (x, 1, 3)).reverse_order(0) == Sum(-x, (x, 4, 0)) assert Sum(x, (x, 1, a)).reverse_order(0) == Sum(-x, (x, a + 1, 0)) assert Sum(x, (x, a, 5)).reverse_order(0) == Sum(-x, (x, 6, a - 1)) assert Sum(x, (x, a + 1, a + 5)).reverse_order(0) == \ Sum(-x, (x, a + 6, a)) assert Sum(x, (x, a + 1, a + 2)).reverse_order(0) == \ Sum(-x, (x, a + 3, a)) assert Sum(x, (x, a + 1, a + 1)).reverse_order(0) == \ Sum(-x, (x, a + 2, a)) assert Sum(x, (x, a, b)).reverse_order(0) == Sum(-x, (x, b + 1, a - 1)) assert Sum(x, (x, a, b)).reverse_order(x) == Sum(-x, (x, b + 1, a - 1)) assert Sum(x*y, (x, a, b), (y, 2, 5)).reverse_order(x, 1) == \ Sum(x*y, (x, b + 1, a - 1), (y, 6, 1)) assert Sum(x*y, (x, a, b), (y, 2, 5)).reverse_order(y, x) == \ Sum(x*y, (x, b + 1, a - 1), (y, 6, 1)) def test_issue_3998(): assert sum(x**n/n for n in range(1, 401)) == summation(x**n/n, (n, 1, 400)) def test_factor_expand_subs(): # test factoring assert Sum(4 * x, (x, 1, y)).factor() == 4 * Sum(x, (x, 1, y)) assert Sum(x * a, (x, 1, y)).factor() == a * Sum(x, (x, 1, y)) assert Sum(4 * x * a, (x, 1, y)).factor() == 4 * a * Sum(x, (x, 1, y)) assert Sum(4 * x * y, (x, 1, y)).factor() == 4 * y * Sum(x, (x, 1, y)) # test expand assert Sum(x+1,(x,1,y)).expand() == Sum(x,(x,1,y)) + Sum(1,(x,1,y)) assert Sum(x+a*x**2,(x,1,y)).expand() == Sum(x,(x,1,y)) + Sum(a*x**2,(x,1,y)) assert Sum(x**(n + 1)*(n + 1), (n, -1, oo)).expand() \ == Sum(x*x**n, (n, -1, oo)) + Sum(n*x*x**n, (n, -1, oo)) assert Sum(x**(n + 1)*(n + 1), (n, -1, oo)).expand(power_exp=False) \ == Sum(n*x**(n+1), (n, -1, oo)) + Sum(x**(n+1), (n, -1, oo)) assert Sum(a*n+a*n**2,(n,0,4)).expand() \ == Sum(a*n,(n,0,4)) + Sum(a*n**2,(n,0,4)) assert Sum(x**a*x**n,(x,0,3)) \ == Sum(x**(a+n),(x,0,3)).expand(power_exp=True) assert Sum(x**(a+n),(x,0,3)) \ == Sum(x**(a+n),(x,0,3)).expand(power_exp=False) # test subs assert Sum(1/(1+a*x**2),(x,0,3)).subs([(a,3)]) == Sum(1/(1+3*x**2),(x,0,3)) assert Sum(x*y,(x,0,y),(y,0,x)).subs([(x,3)]) == Sum(x*y,(x,0,y),(y,0,3)) assert Sum(x,(x,1,10)).subs([(x,y-2)]) == Sum(x,(x,1,10)) assert Sum(1/x,(x,1,10)).subs([(x,(3+n)**3)]) == Sum(1/x,(x,1,10)) assert Sum(1/x,(x,1,10)).subs([(x,3*x-2)]) == Sum(1/x,(x,1,10)) sympy-0.7.4.1/sympy/concrete/tests/test_products.py0000644000175000017500000002617112253362407022654 0ustar georgeskgeorgeskfrom sympy import (symbols, Symbol, product, factorial, rf, sqrt, cos, Function, Product, Rational, Sum, oo) from sympy.utilities.pytest import raises from sympy import simplify a, k, n, m, x = symbols('a,k,n,m,x', integer=True) f = Function('f') def test_karr_convention(): # Test the Karr product convention that we want to hold. # See his paper "Summation in Finite Terms" for a detailed # reasoning why we really want exactly this definition. # The convention is described for sums on page 309 and # essentially in section 1.4, definition 3. For products # we can find in analogy: # # \prod_{m <= i < n} f(i) 'has the obvious meaning' for m < n # \prod_{m <= i < n} f(i) = 0 for m = n # \prod_{m <= i < n} f(i) = 1 / \prod_{n <= i < m} f(i) for m > n # # It is important to note that he defines all products with # the upper limit being *exclusive*. # In contrast, sympy and the usual mathematical notation has: # # prod_{i = a}^b f(i) = f(a) * f(a+1) * ... * f(b-1) * f(b) # # with the upper limit *inclusive*. So translating between # the two we find that: # # \prod_{m <= i < n} f(i) = \prod_{i = m}^{n-1} f(i) # # where we intentionally used two different ways to typeset the # products and its limits. i = Symbol("i", integer=True) k = Symbol("k", integer=True) j = Symbol("j", integer=True) # A simple example with a concrete factors and symbolic limits. # The normal product: m = k and n = k + j and therefore m < n: m = k n = k + j a = m b = n - 1 S1 = Product(i**2, (i, a, b)).doit() # The reversed product: m = k + j and n = k and therefore m > n: m = k + j n = k a = m b = n - 1 S2 = Product(i**2, (i, a, b)).doit() assert simplify(S1 * S2) == 1 # Test the empty product: m = k and n = k and therefore m = n: m = k n = k a = m b = n - 1 Sz = Product(i**2, (i, a, b)).doit() assert Sz == 1 # Another example this time with an unspecified factor and # numeric limits. (We can not do both tests in the same example.) f = Function("f") # The normal product with m < n: m = 2 n = 11 a = m b = n - 1 S1 = Product(f(i), (i, a, b)).doit() # The reversed product with m > n: m = 11 n = 2 a = m b = n - 1 S2 = Product(f(i), (i, a, b)).doit() assert simplify(S1 * S2) == 1 # Test the empty product with m = n: m = 5 n = 5 a = m b = n - 1 Sz = Product(f(i), (i, a, b)).doit() assert Sz == 1 def test_karr_proposition_2a(): # Test Karr, page 309, proposition 2, part a i = Symbol("i", integer=True) u = Symbol("u", integer=True) v = Symbol("v", integer=True) def test_the_product(m, n): # g g = i**3 + 2*i**2 - 3*i # f = Delta g f = simplify(g.subs(i, i+1) / g) # The product a = m b = n - 1 P = Product(f, (i, a, b)).doit() # Test if Product_{m <= i < n} f(i) = g(n) / g(m) assert simplify(P / (g.subs(i, n) / g.subs(i, m))) == 1 # m < n test_the_product(u, u+v) # m = n test_the_product(u, u ) # m > n test_the_product(u+v, u ) def test_karr_proposition_2b(): # Test Karr, page 309, proposition 2, part b i = Symbol("i", integer=True) u = Symbol("u", integer=True) v = Symbol("v", integer=True) w = Symbol("w", integer=True) def test_the_product(l, n, m): # Productmand s = i**3 # First product a = l b = n - 1 S1 = Product(s, (i, a, b)).doit() # Second product a = l b = m - 1 S2 = Product(s, (i, a, b)).doit() # Third product a = m b = n - 1 S3 = Product(s, (i, a, b)).doit() # Test if S1 = S2 * S3 as required assert simplify(S1 / (S2 * S3)) == 1 # l < m < n test_the_product(u, u+v, u+v+w) # l < m = n test_the_product(u, u+v, u+v ) # l < m > n test_the_product(u, u+v+w, v ) # l = m < n test_the_product(u, u, u+v ) # l = m = n test_the_product(u, u, u ) # l = m > n test_the_product(u+v, u+v, u ) # l > m < n test_the_product(u+v, u, u+w ) # l > m = n test_the_product(u+v, u, u ) # l > m > n test_the_product(u+v+w, u+v, u ) def test_simple_products(): assert product(2, (k, a, n)) == 2**(n - a + 1) assert product(k, (k, 1, n)) == factorial(n) assert product(k**3, (k, 1, n)) == factorial(n)**3 assert product(k + 1, (k, 0, n - 1)) == factorial(n) assert product(k + 1, (k, a, n - 1)) == rf(1 + a, n - a) assert product(cos(k), (k, 0, 5)) == cos(1)*cos(2)*cos(3)*cos(4)*cos(5) assert product(cos(k), (k, 3, 5)) == cos(3)*cos(4)*cos(5) assert product(cos(k), (k, 1, Rational(5, 2))) != cos(1)*cos(2) assert isinstance(product(k**k, (k, 1, n)), Product) assert Product(x**k, (k, 1, n)).variables == [k] raises(ValueError, lambda: Product(n)) raises(ValueError, lambda: Product(n, k)) raises(ValueError, lambda: Product(n, k, 1)) raises(ValueError, lambda: Product(n, k, 1, 10)) raises(ValueError, lambda: Product(n, (k, 1))) def test_multiple_products(): assert product(x, (n, 1, k), (k, 1, m)) == x**(m**2/2 + m/2) assert product(f(n), ( n, 1, m), (m, 1, k)) == Product(f(n), (n, 1, m), (m, 1, k)).doit() assert Product(f(n), (m, 1, k), (n, 1, k)).doit() == \ Product(Product(f(n), (m, 1, k)), (n, 1, k)).doit() == \ product(f(n), (m, 1, k), (n, 1, k)) == \ product(product(f(n), (m, 1, k)), (n, 1, k)) == \ Product(f(n)**k, (n, 1, k)) assert Product( x, (x, 1, k), (k, 1, n)).doit() == Product(factorial(k), (k, 1, n)) assert Product(x**k, (n, 1, k), (k, 1, m)).variables == [n, k] def test_rational_products(): assert product(1 + 1/k, (k, 1, n)) == rf(2, n)/factorial(n) def test_special_products(): # Wallis product assert product((4*k)**2 / (4*k**2 - 1), (k, 1, n)) == \ 4**n*factorial(n)**2/rf(Rational(1, 2), n)/rf(Rational(3, 2), n) # Euler's product formula for sin assert product(1 + a/k**2, (k, 1, n)) == \ rf(1 - sqrt(-a), n)*rf(1 + sqrt(-a), n)/factorial(n)**2 def test__eval_product(): from sympy.abc import i, n # 1710 a = Function('a') assert product(2*a(i), (i, 1, n)) == 2**n * Product(a(i), (i, 1, n)) # 1711 assert product(2**i, (i, 1, n)) == 2**(n/2 + n**2/2) def test_product_pow(): # Issue 1718 assert product(2**f(k), (k, 1, n)) == 2**Sum(f(k), (k, 1, n)) assert product(2**(2*f(k)), (k, 1, n)) == 2**Sum(2*f(k), (k, 1, n)) def test_infinite_product(): # Issue 2638 assert isinstance(Product(2**(1/factorial(n)), (n, 0, oo)), Product) def test_conjugate_transpose(): p = Product(x**k, (k, 1, 3)) assert p.adjoint().doit() == p.doit().adjoint() assert p.conjugate().doit() == p.doit().conjugate() assert p.transpose().doit() == p.doit().transpose() A, B = symbols("A B", commutative=False) p = Product(A*B**k, (k, 1, 3)) assert p.adjoint().doit() == p.doit().adjoint() assert p.conjugate().doit() == p.doit().conjugate() assert p.transpose().doit() == p.doit().transpose() def test_simplify(): y, t, b, c = symbols('y, t, b, c', integer = True) assert simplify(Product(x*y, (x, n, m), (y, a, k)) * \ Product(y, (x, n, m), (y, a, k))) == \ Product(x*y**2, (x, n, m), (y, a, k)) assert simplify(3 * y* Product(x, (x, n, m)) * Product(x, (x, m + 1, a))) \ == 3 * y * Product(x, (x, n, a)) assert simplify(Product(x, (x, k + 1, a)) * Product(x, (x, n, k))) == \ Product(x, (x, n, a)) assert simplify(Product(x, (x, k + 1, a)) * Product(x + 1, (x, n, k))) == \ Product(x, (x, k + 1, a)) * Product(x + 1, (x, n, k)) assert simplify(Product(x, (t, a, b)) * Product(y, (t, a, b)) * \ Product(x, (t, b+1, c))) == Product(x*y, (t, a, b)) * \ Product(x, (t, b+1, c)) assert simplify(Product(x, (t, a, b)) * Product(x, (t, b+1, c)) * \ Product(y, (t, a, b))) == Product(x*y, (t, a, b)) * \ Product(x, (t, b+1, c)) def test_change_index(): b, y, c, d, z = symbols('b, y, c, d, z', integer = True) assert Product(x, (x, a, b)).change_index( x, x + 1, y) == \ Product(y - 1, (y, a + 1, b + 1)) assert Product(x**2, (x, a, b)).change_index( x, x - 1) == \ Product((x + 1)**2, (x, a - 1, b - 1)) assert Product(x**2, (x, a, b)).change_index( x, -x, y) == \ Product((-y)**2, (y, -b, -a)) assert Product(x, (x, a, b)).change_index( x, -x - 1) == \ Product(-x - 1, (x, - b - 1, -a - 1)) assert Product(x*y, (x, a, b), (y, c, d)).change_index( x, x - 1, z) == \ Product((z + 1)*y, (z, a - 1, b - 1), (y, c, d)) def test_reorder(): b, y, c, d, z = symbols('b, y, c, d, z', integer = True) assert Product(x*y, (x, a, b), (y, c, d)).reorder((0, 1)) == \ Product(x*y, (y, c, d), (x, a, b)) assert Product(x, (x, a, b), (x, c, d)).reorder((0, 1)) == \ Product(x, (x, c, d), (x, a, b)) assert Product(x*y + z, (x, a, b), (z, m, n), (y, c, d)).reorder(\ (2, 0), (0, 1)) == Product(x*y + z, (z, m, n), (y, c, d), (x, a, b)) assert Product(x*y*z, (x, a, b), (y, c, d), (z, m, n)).reorder(\ (0, 1), (1, 2), (0, 2)) == \ Product(x*y*z, (x, a, b), (z, m, n), (y, c, d)) assert Product(x*y*z, (x, a, b), (y, c, d), (z, m, n)).reorder(\ (x, y), (y, z), (x, z)) == \ Product(x*y*z, (x, a, b), (z, m, n), (y, c, d)) assert Product(x*y, (x, a, b), (y, c, d)).reorder((x, 1)) == \ Product(x*y, (y, c, d), (x, a, b)) assert Product(x*y, (x, a, b), (y, c, d)).reorder((y, x)) == \ Product(x*y, (y, c, d), (x, a, b)) def test_reverse_order(): x, y, a, b, c, d= symbols('x, y, a, b, c, d', integer = True) assert Product(x, (x, 0, 3)).reverse_order(0) == Product(1/x, (x, 4, -1)) assert Product(x*y, (x, 1, 5), (y, 0, 6)).reverse_order(0, 1) == \ Product(x*y, (x, 6, 0), (y, 7, -1)) assert Product(x, (x, 1, 2)).reverse_order(0) == Product(1/x, (x, 3, 0)) assert Product(x, (x, 1, 3)).reverse_order(0) == Product(1/x, (x, 4, 0)) assert Product(x, (x, 1, a)).reverse_order(0) == Product(1/x, (x, a + 1, 0)) assert Product(x, (x, a, 5)).reverse_order(0) == Product(1/x, (x, 6, a - 1)) assert Product(x, (x, a + 1, a + 5)).reverse_order(0) == \ Product(1/x, (x, a + 6, a)) assert Product(x, (x, a + 1, a + 2)).reverse_order(0) == \ Product(1/x, (x, a + 3, a)) assert Product(x, (x, a + 1, a + 1)).reverse_order(0) == \ Product(1/x, (x, a + 2, a)) assert Product(x, (x, a, b)).reverse_order(0) == Product(1/x, (x, b + 1, a - 1)) assert Product(x, (x, a, b)).reverse_order(x) == Product(1/x, (x, b + 1, a - 1)) assert Product(x*y, (x, a, b), (y, 2, 5)).reverse_order(x, 1) == \ Product(x*y, (x, b + 1, a - 1), (y, 6, 1)) assert Product(x*y, (x, a, b), (y, 2, 5)).reverse_order(y, x) == \ Product(x*y, (x, b + 1, a - 1), (y, 6, 1)) sympy-0.7.4.1/sympy/concrete/tests/test_gosper.py0000644000175000017500000001621312253362407022304 0ustar georgeskgeorgesk"""Tests for Gosper's algorithm for hypergeometric summation. """ from sympy import binomial, factorial, gamma, Poly, S, simplify, sqrt, exp, log, Symbol from sympy.abc import a, b, j, k, m, n, r, x from sympy.concrete.gosper import gosper_normal, gosper_sum, gosper_term def test_gosper_normal(): assert gosper_normal(4*n + 5, 2*(4*n + 1)*(2*n + 3), n) == \ (Poly(S(1)/4, n), Poly(n + S(3)/2), Poly(n + S(1)/4)) def test_gosper_term(): assert gosper_term((4*k + 1)*factorial( k)/factorial(2*k + 1), k) == (-k - S(1)/2)/(k + S(1)/4) def test_gosper_sum(): assert gosper_sum(1, (k, 0, n)) == 1 + n assert gosper_sum(k, (k, 0, n)) == n*(1 + n)/2 assert gosper_sum(k**2, (k, 0, n)) == n*(1 + n)*(1 + 2*n)/6 assert gosper_sum(k**3, (k, 0, n)) == n**2*(1 + n)**2/4 assert gosper_sum(2**k, (k, 0, n)) == 2*2**n - 1 assert gosper_sum(factorial(k), (k, 0, n)) is None assert gosper_sum(binomial(n, k), (k, 0, n)) is None assert gosper_sum(factorial(k)/k**2, (k, 0, n)) is None assert gosper_sum((k - 3)*factorial(k), (k, 0, n)) is None assert gosper_sum(k*factorial(k), k) == factorial(k) assert gosper_sum( k*factorial(k), (k, 0, n)) == n*factorial(n) + factorial(n) - 1 assert gosper_sum((-1)**k*binomial(n, k), (k, 0, n)) == 0 assert gosper_sum(( -1)**k*binomial(n, k), (k, 0, m)) == -(-1)**m*(m - n)*binomial(n, m)/n assert gosper_sum((4*k + 1)*factorial(k)/factorial(2*k + 1), (k, 0, n)) == \ (2*factorial(2*n + 1) - factorial(n))/factorial(2*n + 1) # issue 2934: assert gosper_sum( n*(n + a + b)*a**n*b**n/(factorial(n + a)*factorial(n + b)), \ (n, 0, m)) == -a*b*(exp(m*log(a))*exp(m*log(b))*factorial(a)* \ factorial(b) - factorial(a + m)*factorial(b + m))/(factorial(a)* \ factorial(b)*factorial(a + m)*factorial(b + m)) def test_gosper_sum_indefinite(): assert gosper_sum(k, k) == k*(k - 1)/2 assert gosper_sum(k**2, k) == k*(k - 1)*(2*k - 1)/6 assert gosper_sum(1/(k*(k + 1)), k) == -1/k assert gosper_sum(-(27*k**4 + 158*k**3 + 430*k**2 + 678*k + 445)*gamma(2*k + 4)/(3*(3*k + 7)*gamma(3*k + 6)), k) == \ (3*k + 5)*(k**2 + 2*k + 5)*gamma(2*k + 4)/gamma(3*k + 6) def test_gosper_sum_parametric(): assert gosper_sum(binomial(S(1)/2, m - j + 1)*binomial(S(1)/2, m + j), (j, 1, n)) == \ n*(1 + m - n)*(-1 + 2*m + 2*n)*binomial(S(1)/2, 1 + m - n)* \ binomial(S(1)/2, m + n)/(m*(1 + 2*m)) def test_gosper_sum_algebraic(): assert gosper_sum( n**2 + sqrt(2), (n, 0, m)) == (m + 1)*(2*m**2 + m + 6*sqrt(2))/6 def test_gosper_sum_iterated(): f1 = binomial(2*k, k)/4**k f2 = (1 + 2*n)*binomial(2*n, n)/4**n f3 = (1 + 2*n)*(3 + 2*n)*binomial(2*n, n)/(3*4**n) f4 = (1 + 2*n)*(3 + 2*n)*(5 + 2*n)*binomial(2*n, n)/(15*4**n) f5 = (1 + 2*n)*(3 + 2*n)*(5 + 2*n)*(7 + 2*n)*binomial(2*n, n)/(105*4**n) assert gosper_sum(f1, (k, 0, n)) == f2 assert gosper_sum(f2, (n, 0, n)) == f3 assert gosper_sum(f3, (n, 0, n)) == f4 assert gosper_sum(f4, (n, 0, n)) == f5 # the AeqB tests test expressions given in # www.math.upenn.edu/~wilf/AeqB.pdf def test_gosper_sum_AeqB_part1(): f1a = n**4 f1b = n**3*2**n f1c = 1/(n**2 + sqrt(5)*n - 1) f1d = n**4*4**n/binomial(2*n, n) f1e = factorial(3*n)/(factorial(n)*factorial(n + 1)*factorial(n + 2)*27**n) f1f = binomial(2*n, n)**2/((n + 1)*4**(2*n)) f1g = (4*n - 1)*binomial(2*n, n)**2/((2*n - 1)**2*4**(2*n)) f1h = n*factorial(n - S(1)/2)**2/factorial(n + 1)**2 g1a = m*(m + 1)*(2*m + 1)*(3*m**2 + 3*m - 1)/30 g1b = 26 + 2**(m + 1)*(m**3 - 3*m**2 + 9*m - 13) g1c = (m + 1)*(m*(m**2 - 7*m + 3)*sqrt(5) - ( 3*m**3 - 7*m**2 + 19*m - 6))/(2*m**3*sqrt(5) + m**4 + 5*m**2 - 1)/6 g1d = -S(2)/231 + 2*4**m*(m + 1)*(63*m**4 + 112*m**3 + 18*m**2 - 22*m + 3)/(693*binomial(2*m, m)) g1e = -S(9)/2 + (81*m**2 + 261*m + 200)*factorial( 3*m + 2)/(40*27**m*factorial(m)*factorial(m + 1)*factorial(m + 2)) g1f = (2*m + 1)**2*binomial(2*m, m)**2/(4**(2*m)*(m + 1)) g1g = -binomial(2*m, m)**2/4**(2*m) g1h = -(2*m + 1)**2*(3*m + 4)*factorial(m - S(1)/2)**2/factorial(m + 1)**2 g = gosper_sum(f1a, (n, 0, m)) assert g is not None and simplify(g - g1a) == 0 g = gosper_sum(f1b, (n, 0, m)) assert g is not None and simplify(g - g1b) == 0 g = gosper_sum(f1c, (n, 0, m)) assert g is not None and simplify(g - g1c) == 0 g = gosper_sum(f1d, (n, 0, m)) assert g is not None and simplify(g - g1d) == 0 g = gosper_sum(f1e, (n, 0, m)) assert g is not None and simplify(g - g1e) == 0 g = gosper_sum(f1f, (n, 0, m)) assert g is not None and simplify(g - g1f) == 0 g = gosper_sum(f1g, (n, 0, m)) assert g is not None and simplify(g - g1g) == 0 g = gosper_sum(f1h, (n, 0, m)) assert g is not None and simplify(g - g1h) == 0 def test_gosper_sum_AeqB_part2(): f2a = n**2*a**n f2b = (n - r/2)*binomial(r, n) f2c = factorial(n - 1)**2/(factorial(n - x)*factorial(n + x)) g2a = -a*(a + 1)/(a - 1)**3 + a**( m + 1)*(a**2*m**2 - 2*a*m**2 + m**2 - 2*a*m + 2*m + a + 1)/(a - 1)**3 g2b = (m - r)*binomial(r, m)/2 ff = factorial(1 - x)*factorial(1 + x) g2c = 1/ff*( 1 - 1/x**2) + factorial(m)**2/(x**2*factorial(m - x)*factorial(m + x)) g = gosper_sum(f2a, (n, 0, m)) assert g is not None and simplify(g - g2a) == 0 g = gosper_sum(f2b, (n, 0, m)) assert g is not None and simplify(g - g2b) == 0 g = gosper_sum(f2c, (n, 1, m)) assert g is not None and simplify(g - g2c) == 0 def test_gosper_nan(): a = Symbol('a', positive=True) b = Symbol('b', positive=True) n = Symbol('n', integer=True) m = Symbol('m', integer=True) f2d = n*(n + a + b)*a**n*b**n/(factorial(n + a)*factorial(n + b)) g2d = 1/(factorial(a - 1)*factorial( b - 1)) - a**(m + 1)*b**(m + 1)/(factorial(a + m)*factorial(b + m)) g = gosper_sum(f2d, (n, 0, m)) assert simplify(g - g2d) == 0 def test_gosper_sum_AeqB_part3(): f3a = 1/n**4 f3b = (6*n + 3)/(4*n**4 + 8*n**3 + 8*n**2 + 4*n + 3) f3c = 2**n*(n**2 - 2*n - 1)/(n**2*(n + 1)**2) f3d = n**2*4**n/((n + 1)*(n + 2)) f3e = 2**n/(n + 1) f3f = 4*(n - 1)*(n**2 - 2*n - 1)/(n**2*(n + 1)**2*(n - 2)**2*(n - 3)**2) f3g = (n**4 - 14*n**2 - 24*n - 9)*2**n/(n**2*(n + 1)**2*(n + 2)**2* (n + 3)**2) # g3a -> no closed form g3b = m*(m + 2)/(2*m**2 + 4*m + 3) g3c = 2**m/m**2 - 2 g3d = S(2)/3 + 4**(m + 1)*(m - 1)/(m + 2)/3 # g3e -> no closed form g3f = -(-S(1)/16 + 1/((m - 2)**2*(m + 1)**2)) # the AeqB key is wrong g3g = -S(2)/9 + 2**(m + 1)/((m + 1)**2*(m + 3)**2) g = gosper_sum(f3a, (n, 1, m)) assert g is None g = gosper_sum(f3b, (n, 1, m)) assert g is not None and simplify(g - g3b) == 0 g = gosper_sum(f3c, (n, 1, m - 1)) assert g is not None and simplify(g - g3c) == 0 g = gosper_sum(f3d, (n, 1, m)) assert g is not None and simplify(g - g3d) == 0 g = gosper_sum(f3e, (n, 0, m - 1)) assert g is None g = gosper_sum(f3f, (n, 4, m)) assert g is not None and simplify(g - g3f) == 0 g = gosper_sum(f3g, (n, 1, m)) assert g is not None and simplify(g - g3g) == 0 sympy-0.7.4.1/sympy/concrete/tests/test_delta.py0000644000175000017500000005614712253362407022110 0ustar georgeskgeorgeskfrom sympy.concrete import Sum from sympy.concrete.delta import deltaproduct, deltasummation from sympy.core import Eq, S, symbols, oo from sympy.functions import KroneckerDelta, Piecewise, piecewise_fold from sympy.logic import And i, j, k, l, m = symbols("i j k l m", integer=True) x, y = symbols("x y", commutative=False) dp = deltaproduct ds = deltasummation KD = KroneckerDelta def test_deltaproduct_trivial(): assert dp(x, (j, 1, 0)) == 1 assert dp(x, (j, 1, 3)) == x**3 assert dp(x + y, (j, 1, 3)) == (x + y)**3 assert dp(x*y, (j, 1, 3)) == (x*y)**3 assert dp(KD(i, j), (k, 1, 3)) == KD(i, j) assert dp(x*KD(i, j), (k, 1, 3)) == x**3*KD(i, j) assert dp(x*y*KD(i, j), (k, 1, 3)) == (x*y)**3*KD(i, j) def test_deltaproduct_basic(): assert dp(KD(i, j), (j, 1, 3)) == 0 assert dp(KD(i, j), (j, 1, 1)) == KD(i, 1) assert dp(KD(i, j), (j, 2, 2)) == KD(i, 2) assert dp(KD(i, j), (j, 3, 3)) == KD(i, 3) assert dp(KD(i, j), (j, 1, k)) == KD(i, 1)*KD(k, 1) + KD(k, 0) assert dp(KD(i, j), (j, k, 3)) == KD(i, 3)*KD(k, 3) + KD(k, 4) assert dp(KD(i, j), (j, k, l)) == KD(i, l)*KD(k, l) + KD(k, l + 1) def test_deltaproduct_mul_x_kd(): assert dp(x*KD(i, j), (j, 1, 3)) == 0 assert dp(x*KD(i, j), (j, 1, 1)) == x*KD(i, 1) assert dp(x*KD(i, j), (j, 2, 2)) == x*KD(i, 2) assert dp(x*KD(i, j), (j, 3, 3)) == x*KD(i, 3) assert dp(x*KD(i, j), (j, 1, k)) == x*KD(i, 1)*KD(k, 1) + KD(k, 0) assert dp(x*KD(i, j), (j, k, 3)) == x*KD(i, 3)*KD(k, 3) + KD(k, 4) assert dp(x*KD(i, j), (j, k, l)) == x*KD(i, l)*KD(k, l) + KD(k, l + 1) def test_deltaproduct_mul_add_x_y_kd(): assert dp((x + y)*KD(i, j), (j, 1, 3)) == 0 assert dp((x + y)*KD(i, j), (j, 1, 1)) == (x + y)*KD(i, 1) assert dp((x + y)*KD(i, j), (j, 2, 2)) == (x + y)*KD(i, 2) assert dp((x + y)*KD(i, j), (j, 3, 3)) == (x + y)*KD(i, 3) assert dp((x + y)*KD(i, j), (j, 1, k)) == \ (x + y)*KD(i, 1)*KD(k, 1) + KD(k, 0) assert dp((x + y)*KD(i, j), (j, k, 3)) == \ (x + y)*KD(i, 3)*KD(k, 3) + KD(k, 4) assert dp((x + y)*KD(i, j), (j, k, l)) == \ (x + y)*KD(i, l)*KD(k, l) + KD(k, l + 1) def test_deltaproduct_add_kd_kd(): assert dp(KD(i, k) + KD(j, k), (k, 1, 3)) == 0 assert dp(KD(i, k) + KD(j, k), (k, 1, 1)) == KD(i, 1) + KD(j, 1) assert dp(KD(i, k) + KD(j, k), (k, 2, 2)) == KD(i, 2) + KD(j, 2) assert dp(KD(i, k) + KD(j, k), (k, 3, 3)) == KD(i, 3) + KD(j, 3) assert dp(KD(i, k) + KD(j, k), (k, 1, l)) == KD(l, 0) + \ KD(i, 1)*KD(l, 1) + KD(j, 1)*KD(l, 1) + \ KD(i, 1)*KD(j, 2)*KD(l, 2) + KD(j, 1)*KD(i, 2)*KD(l, 2) assert dp(KD(i, k) + KD(j, k), (k, l, 3)) == KD(l, 4) + \ KD(i, 3)*KD(l, 3) + KD(j, 3)*KD(l, 3) + \ KD(i, 2)*KD(j, 3)*KD(l, 2) + KD(i, 3)*KD(j, 2)*KD(l, 2) assert dp(KD(i, k) + KD(j, k), (k, l, m)) == KD(l, m + 1) + \ KD(i, m)*KD(l, m) + KD(j, m)*KD(l, m) + \ KD(i, m)*KD(j, m - 1)*KD(l, m - 1) + KD(i, m - 1)*KD(j, m)*KD(l, m - 1) def test_deltaproduct_mul_x_add_kd_kd(): assert dp(x*(KD(i, k) + KD(j, k)), (k, 1, 3)) == 0 assert dp(x*(KD(i, k) + KD(j, k)), (k, 1, 1)) == x*(KD(i, 1) + KD(j, 1)) assert dp(x*(KD(i, k) + KD(j, k)), (k, 2, 2)) == x*(KD(i, 2) + KD(j, 2)) assert dp(x*(KD(i, k) + KD(j, k)), (k, 3, 3)) == x*(KD(i, 3) + KD(j, 3)) assert dp(x*(KD(i, k) + KD(j, k)), (k, 1, l)) == KD(l, 0) + \ x*KD(i, 1)*KD(l, 1) + x*KD(j, 1)*KD(l, 1) + \ x**2*KD(i, 1)*KD(j, 2)*KD(l, 2) + x**2*KD(j, 1)*KD(i, 2)*KD(l, 2) assert dp(x*(KD(i, k) + KD(j, k)), (k, l, 3)) == KD(l, 4) + \ x*KD(i, 3)*KD(l, 3) + x*KD(j, 3)*KD(l, 3) + \ x**2*KD(i, 2)*KD(j, 3)*KD(l, 2) + x**2*KD(i, 3)*KD(j, 2)*KD(l, 2) assert dp(x*(KD(i, k) + KD(j, k)), (k, l, m)) == KD(l, m + 1) + \ x*KD(i, m)*KD(l, m) + x*KD(j, m)*KD(l, m) + \ x**2*KD(i, m - 1)*KD(j, m)*KD(l, m - 1) + \ x**2*KD(i, m)*KD(j, m - 1)*KD(l, m - 1) def test_deltaproduct_mul_add_x_y_add_kd_kd(): assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, 1, 3)) == 0 assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, 1, 1)) == \ (x + y)*(KD(i, 1) + KD(j, 1)) assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, 2, 2)) == \ (x + y)*(KD(i, 2) + KD(j, 2)) assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, 3, 3)) == \ (x + y)*(KD(i, 3) + KD(j, 3)) assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, 1, l)) == KD(l, 0) + \ (x + y)*KD(i, 1)*KD(l, 1) + (x + y)*KD(j, 1)*KD(l, 1) + \ (x + y)**2*KD(i, 1)*KD(j, 2)*KD(l, 2) + \ (x + y)**2*KD(j, 1)*KD(i, 2)*KD(l, 2) assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, l, 3)) == KD(l, 4) + \ (x + y)*KD(i, 3)*KD(l, 3) + (x + y)*KD(j, 3)*KD(l, 3) + \ (x + y)**2*KD(i, 2)*KD(j, 3)*KD(l, 2) + \ (x + y)**2*KD(i, 3)*KD(j, 2)*KD(l, 2) assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, l, m)) == KD(l, m + 1) + \ (x + y)*KD(i, m)*KD(l, m) + (x + y)*KD(j, m)*KD(l, m) + \ (x + y)**2*KD(i, m - 1)*KD(j, m)*KD(l, m - 1) + \ (x + y)**2*KD(i, m)*KD(j, m - 1)*KD(l, m - 1) def test_deltaproduct_add_mul_x_y_mul_x_kd(): assert dp(x*y + x*KD(i, j), (j, 1, 3)) == (x*y)**3 + \ x*(x*y)**2*KD(i, 1) + (x*y)*x*(x*y)*KD(i, 2) + (x*y)**2*x*KD(i, 3) assert dp(x*y + x*KD(i, j), (j, 1, 1)) == x*y + x*KD(i, 1) assert dp(x*y + x*KD(i, j), (j, 2, 2)) == x*y + x*KD(i, 2) assert dp(x*y + x*KD(i, j), (j, 3, 3)) == x*y + x*KD(i, 3) assert dp(x*y + x*KD(i, j), (j, 1, k)) == \ (x*y)**k + Piecewise( ((x*y)**(i - 1)*x*(x*y)**(k - i), And(S(1) <= i, i <= k)), (0, True) ) assert dp(x*y + x*KD(i, j), (j, k, 3)) == \ (x*y)**(-k + 4) + Piecewise( ((x*y)**(i - k)*x*(x*y)**(3 - i), And(k <= i, i <= 3)), (0, True) ) assert dp(x*y + x*KD(i, j), (j, k, l)) == \ (x*y)**(-k + l + 1) + Piecewise( ((x*y)**(i - k)*x*(x*y)**(l - i), And(k <= i, i <= l)), (0, True) ) def test_deltaproduct_mul_x_add_y_kd(): assert dp(x*(y + KD(i, j)), (j, 1, 3)) == (x*y)**3 + \ x*(x*y)**2*KD(i, 1) + (x*y)*x*(x*y)*KD(i, 2) + (x*y)**2*x*KD(i, 3) assert dp(x*(y + KD(i, j)), (j, 1, 1)) == x*(y + KD(i, 1)) assert dp(x*(y + KD(i, j)), (j, 2, 2)) == x*(y + KD(i, 2)) assert dp(x*(y + KD(i, j)), (j, 3, 3)) == x*(y + KD(i, 3)) assert dp(x*(y + KD(i, j)), (j, 1, k)) == \ (x*y)**k + Piecewise( ((x*y)**(i - 1)*x*(x*y)**(k - i), And(S(1) <= i, i <= k)), (0, True) ) assert dp(x*(y + KD(i, j)), (j, k, 3)) == \ (x*y)**(-k + 4) + Piecewise( ((x*y)**(i - k)*x*(x*y)**(3 - i), And(k <= i, i <= 3)), (0, True) ) assert dp(x*(y + KD(i, j)), (j, k, l)) == \ (x*y)**(-k + l + 1) + Piecewise( ((x*y)**(i - k)*x*(x*y)**(l - i), And(k <= i, i <= l)), (0, True) ) def test_deltaproduct_mul_x_add_y_twokd(): assert dp(x*(y + 2*KD(i, j)), (j, 1, 3)) == (x*y)**3 + \ 2*x*(x*y)**2*KD(i, 1) + 2*x*y*x*x*y*KD(i, 2) + 2*(x*y)**2*x*KD(i, 3) assert dp(x*(y + 2*KD(i, j)), (j, 1, 1)) == x*(y + 2*KD(i, 1)) assert dp(x*(y + 2*KD(i, j)), (j, 2, 2)) == x*(y + 2*KD(i, 2)) assert dp(x*(y + 2*KD(i, j)), (j, 3, 3)) == x*(y + 2*KD(i, 3)) assert dp(x*(y + 2*KD(i, j)), (j, 1, k)) == \ (x*y)**k + Piecewise( (2*(x*y)**(i - 1)*x*(x*y)**(k - i), And(S(1) <= i, i <= k)), (0, True) ) assert dp(x*(y + 2*KD(i, j)), (j, k, 3)) == \ (x*y)**(-k + 4) + Piecewise( (2*(x*y)**(i - k)*x*(x*y)**(3 - i), And(k <= i, i <= 3)), (0, True) ) assert dp(x*(y + 2*KD(i, j)), (j, k, l)) == \ (x*y)**(-k + l + 1) + Piecewise( (2*(x*y)**(i - k)*x*(x*y)**(l - i), And(k <= i, i <= l)), (0, True) ) def test_deltaproduct_mul_add_x_y_add_y_kd(): assert dp((x + y)*(y + KD(i, j)), (j, 1, 3)) == ((x + y)*y)**3 + \ (x + y)*((x + y)*y)**2*KD(i, 1) + \ (x + y)*y*(x + y)**2*y*KD(i, 2) + \ ((x + y)*y)**2*(x + y)*KD(i, 3) assert dp((x + y)*(y + KD(i, j)), (j, 1, 1)) == (x + y)*(y + KD(i, 1)) assert dp((x + y)*(y + KD(i, j)), (j, 2, 2)) == (x + y)*(y + KD(i, 2)) assert dp((x + y)*(y + KD(i, j)), (j, 3, 3)) == (x + y)*(y + KD(i, 3)) assert dp((x + y)*(y + KD(i, j)), (j, 1, k)) == \ ((x + y)*y)**k + Piecewise( (((x + y)*y)**(i - 1)*(x + y)*((x + y)*y)**(k - i), And(S(1) <= i, i <= k)), (0, True) ) assert dp((x + y)*(y + KD(i, j)), (j, k, 3)) == \ ((x + y)*y)**(-k + 4) + Piecewise( (((x + y)*y)**(i - k)*(x + y)*((x + y)*y)**(3 - i), And(k <= i, i <= 3)), (0, True) ) assert dp((x + y)*(y + KD(i, j)), (j, k, l)) == \ ((x + y)*y)**(-k + l + 1) + Piecewise( (((x + y)*y)**(i - k)*(x + y)*((x + y)*y)**(l - i), And(k <= i, i <= l)), (0, True) ) def test_deltaproduct_mul_add_x_kd_add_y_kd(): assert dp((x + KD(i, k))*(y + KD(i, j)), (j, 1, 3)) == \ KD(i, 1)*(KD(i, k) + x)*((KD(i, k) + x)*y)**2 + \ KD(i, 2)*(KD(i, k) + x)*y*(KD(i, k) + x)**2*y + \ KD(i, 3)*((KD(i, k) + x)*y)**2*(KD(i, k) + x) + \ ((KD(i, k) + x)*y)**3 assert dp((x + KD(i, k))*(y + KD(i, j)), (j, 1, 1)) == \ (x + KD(i, k))*(y + KD(i, 1)) assert dp((x + KD(i, k))*(y + KD(i, j)), (j, 2, 2)) == \ (x + KD(i, k))*(y + KD(i, 2)) assert dp((x + KD(i, k))*(y + KD(i, j)), (j, 3, 3)) == \ (x + KD(i, k))*(y + KD(i, 3)) assert dp((x + KD(i, k))*(y + KD(i, j)), (j, 1, k)) == \ ((x + KD(i, k))*y)**k + Piecewise( (((x + KD(i, k))*y)**(i - 1)*(x + KD(i, k))* ((x + KD(i, k))*y)**(-i + k), And(S(1) <= i, i <= k)), (0, True) ) assert dp((x + KD(i, k))*(y + KD(i, j)), (j, k, 3)) == \ ((x + KD(i, k))*y)**(4 - k) + Piecewise( (((x + KD(i, k))*y)**(i - k)*(x + KD(i, k))* ((x + KD(i, k))*y)**(-i + 3), And(k <= i, i <= 3)), (0, True) ) assert dp((x + KD(i, k))*(y + KD(i, j)), (j, k, l)) == \ ((x + KD(i, k))*y)**(-k + l + 1) + Piecewise( (((x + KD(i, k))*y)**(i - k)*(x + KD(i, k))* ((x + KD(i, k))*y)**(-i + l), And(k <= i, i <= l)), (0, True) ) def test_deltasummation_trivial(): assert ds(x, (j, 1, 0)) == 0 assert ds(x, (j, 1, 3)) == 3*x assert ds(x + y, (j, 1, 3)) == 3*(x + y) assert ds(x*y, (j, 1, 3)) == 3*x*y assert ds(KD(i, j), (k, 1, 3)) == 3*KD(i, j) assert ds(x*KD(i, j), (k, 1, 3)) == 3*x*KD(i, j) assert ds(x*y*KD(i, j), (k, 1, 3)) == 3*x*y*KD(i, j) def test_deltasummation_basic_numerical(): n = symbols('n', integer=True, nonzero=True) assert ds(KD(n, 0), (n, 1, 3)) == 0 # return unevaluated, until it gets implemented assert ds(KD(i**2, j**2), (j, -oo, oo)) == \ Sum(KD(i**2, j**2), (j, -oo, oo)) assert Piecewise((KD(i, k), And(S(1) <= i, i <= 3)), (0, True)) == \ ds(KD(i, j)*KD(j, k), (j, 1, 3)) == \ ds(KD(j, k)*KD(i, j), (j, 1, 3)) assert ds(KD(i, k), (k, -oo, oo)) == 1 assert ds(KD(i, k), (k, 0, oo)) == Piecewise((1, i >= 0), (0, True)) assert ds(KD(i, k), (k, 1, 3)) == \ Piecewise((1, And(S(1) <= i, i <= 3)), (0, True)) assert ds(k*KD(i, j)*KD(j, k), (k, -oo, oo)) == j*KD(i, j) assert ds(j*KD(i, j), (j, -oo, oo)) == i assert ds(i*KD(i, j), (i, -oo, oo)) == j assert ds(x, (i, 1, 3)) == 3*x assert ds((i + j)*KD(i, j), (j, -oo, oo)) == 2*i def test_deltasummation_basic_symbolic(): assert ds(KD(i, j), (j, 1, 3)) == \ Piecewise((1, And(S(1) <= i, i <= 3)), (0, True)) assert ds(KD(i, j), (j, 1, 1)) == Piecewise((1, Eq(i, 1)), (0, True)) assert ds(KD(i, j), (j, 2, 2)) == Piecewise((1, Eq(i, 2)), (0, True)) assert ds(KD(i, j), (j, 3, 3)) == Piecewise((1, Eq(i, 3)), (0, True)) assert ds(KD(i, j), (j, 1, k)) == \ Piecewise((1, And(S(1) <= i, i <= k)), (0, True)) assert ds(KD(i, j), (j, k, 3)) == \ Piecewise((1, And(k <= i, i <= 3)), (0, True)) assert ds(KD(i, j), (j, k, l)) == \ Piecewise((1, And(k <= i, i <= l)), (0, True)) def test_deltasummation_mul_x_kd(): assert ds(x*KD(i, j), (j, 1, 3)) == \ Piecewise((x, And(S(1) <= i, i <= 3)), (0, True)) assert ds(x*KD(i, j), (j, 1, 1)) == Piecewise((x, Eq(i, 1)), (0, True)) assert ds(x*KD(i, j), (j, 2, 2)) == Piecewise((x, Eq(i, 2)), (0, True)) assert ds(x*KD(i, j), (j, 3, 3)) == Piecewise((x, Eq(i, 3)), (0, True)) assert ds(x*KD(i, j), (j, 1, k)) == \ Piecewise((x, And(S(1) <= i, i <= k)), (0, True)) assert ds(x*KD(i, j), (j, k, 3)) == \ Piecewise((x, And(k <= i, i <= 3)), (0, True)) assert ds(x*KD(i, j), (j, k, l)) == \ Piecewise((x, And(k <= i, i <= l)), (0, True)) def test_deltasummation_mul_add_x_y_kd(): assert ds((x + y)*KD(i, j), (j, 1, 3)) == \ Piecewise((x + y, And(S(1) <= i, i <= 3)), (0, True)) assert ds((x + y)*KD(i, j), (j, 1, 1)) == \ Piecewise((x + y, Eq(i, 1)), (0, True)) assert ds((x + y)*KD(i, j), (j, 2, 2)) == \ Piecewise((x + y, Eq(i, 2)), (0, True)) assert ds((x + y)*KD(i, j), (j, 3, 3)) == \ Piecewise((x + y, Eq(i, 3)), (0, True)) assert ds((x + y)*KD(i, j), (j, 1, k)) == \ Piecewise((x + y, And(S(1) <= i, i <= k)), (0, True)) assert ds((x + y)*KD(i, j), (j, k, 3)) == \ Piecewise((x + y, And(k <= i, i <= 3)), (0, True)) assert ds((x + y)*KD(i, j), (j, k, l)) == \ Piecewise((x + y, And(k <= i, i <= l)), (0, True)) def test_deltasummation_add_kd_kd(): assert ds(KD(i, k) + KD(j, k), (k, 1, 3)) == piecewise_fold( Piecewise((1, And(S(1) <= i, i <= 3)), (0, True)) + Piecewise((1, And(S(1) <= j, j <= 3)), (0, True))) assert ds(KD(i, k) + KD(j, k), (k, 1, 1)) == piecewise_fold( Piecewise((1, Eq(i, 1)), (0, True)) + Piecewise((1, Eq(j, 1)), (0, True))) assert ds(KD(i, k) + KD(j, k), (k, 2, 2)) == piecewise_fold( Piecewise((1, Eq(i, 2)), (0, True)) + Piecewise((1, Eq(j, 2)), (0, True))) assert ds(KD(i, k) + KD(j, k), (k, 3, 3)) == piecewise_fold( Piecewise((1, Eq(i, 3)), (0, True)) + Piecewise((1, Eq(j, 3)), (0, True))) assert ds(KD(i, k) + KD(j, k), (k, 1, l)) == piecewise_fold( Piecewise((1, And(S(1) <= i, i <= l)), (0, True)) + Piecewise((1, And(S(1) <= j, j <= l)), (0, True))) assert ds(KD(i, k) + KD(j, k), (k, l, 3)) == piecewise_fold( Piecewise((1, And(l <= i, i <= 3)), (0, True)) + Piecewise((1, And(l <= j, j <= 3)), (0, True))) assert ds(KD(i, k) + KD(j, k), (k, l, m)) == piecewise_fold( Piecewise((1, And(l <= i, i <= m)), (0, True)) + Piecewise((1, And(l <= j, j <= m)), (0, True))) def test_deltasummation_add_mul_x_kd_kd(): assert ds(x*KD(i, k) + KD(j, k), (k, 1, 3)) == piecewise_fold( Piecewise((x, And(S(1) <= i, i <= 3)), (0, True)) + Piecewise((1, And(S(1) <= j, j <= 3)), (0, True))) assert ds(x*KD(i, k) + KD(j, k), (k, 1, 1)) == piecewise_fold( Piecewise((x, Eq(i, 1)), (0, True)) + Piecewise((1, Eq(j, 1)), (0, True))) assert ds(x*KD(i, k) + KD(j, k), (k, 2, 2)) == piecewise_fold( Piecewise((x, Eq(i, 2)), (0, True)) + Piecewise((1, Eq(j, 2)), (0, True))) assert ds(x*KD(i, k) + KD(j, k), (k, 3, 3)) == piecewise_fold( Piecewise((x, Eq(i, 3)), (0, True)) + Piecewise((1, Eq(j, 3)), (0, True))) assert ds(x*KD(i, k) + KD(j, k), (k, 1, l)) == piecewise_fold( Piecewise((x, And(S(1) <= i, i <= l)), (0, True)) + Piecewise((1, And(S(1) <= j, j <= l)), (0, True))) assert ds(x*KD(i, k) + KD(j, k), (k, l, 3)) == piecewise_fold( Piecewise((x, And(l <= i, i <= 3)), (0, True)) + Piecewise((1, And(l <= j, j <= 3)), (0, True))) assert ds(x*KD(i, k) + KD(j, k), (k, l, m)) == piecewise_fold( Piecewise((x, And(l <= i, i <= m)), (0, True)) + Piecewise((1, And(l <= j, j <= m)), (0, True))) def test_deltasummation_mul_x_add_kd_kd(): assert ds(x*(KD(i, k) + KD(j, k)), (k, 1, 3)) == piecewise_fold( Piecewise((x, And(S(1) <= i, i <= 3)), (0, True)) + Piecewise((x, And(S(1) <= j, j <= 3)), (0, True))) assert ds(x*(KD(i, k) + KD(j, k)), (k, 1, 1)) == piecewise_fold( Piecewise((x, Eq(i, 1)), (0, True)) + Piecewise((x, Eq(j, 1)), (0, True))) assert ds(x*(KD(i, k) + KD(j, k)), (k, 2, 2)) == piecewise_fold( Piecewise((x, Eq(i, 2)), (0, True)) + Piecewise((x, Eq(j, 2)), (0, True))) assert ds(x*(KD(i, k) + KD(j, k)), (k, 3, 3)) == piecewise_fold( Piecewise((x, Eq(i, 3)), (0, True)) + Piecewise((x, Eq(j, 3)), (0, True))) assert ds(x*(KD(i, k) + KD(j, k)), (k, 1, l)) == piecewise_fold( Piecewise((x, And(S(1) <= i, i <= l)), (0, True)) + Piecewise((x, And(S(1) <= j, j <= l)), (0, True))) assert ds(x*(KD(i, k) + KD(j, k)), (k, l, 3)) == piecewise_fold( Piecewise((x, And(l <= i, i <= 3)), (0, True)) + Piecewise((x, And(l <= j, j <= 3)), (0, True))) assert ds(x*(KD(i, k) + KD(j, k)), (k, l, m)) == piecewise_fold( Piecewise((x, And(l <= i, i <= m)), (0, True)) + Piecewise((x, And(l <= j, j <= m)), (0, True))) def test_deltasummation_mul_add_x_y_add_kd_kd(): assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, 1, 3)) == piecewise_fold( Piecewise((x + y, And(S(1) <= i, i <= 3)), (0, True)) + Piecewise((x + y, And(S(1) <= j, j <= 3)), (0, True))) assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, 1, 1)) == piecewise_fold( Piecewise((x + y, Eq(i, 1)), (0, True)) + Piecewise((x + y, Eq(j, 1)), (0, True))) assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, 2, 2)) == piecewise_fold( Piecewise((x + y, Eq(i, 2)), (0, True)) + Piecewise((x + y, Eq(j, 2)), (0, True))) assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, 3, 3)) == piecewise_fold( Piecewise((x + y, Eq(i, 3)), (0, True)) + Piecewise((x + y, Eq(j, 3)), (0, True))) assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, 1, l)) == piecewise_fold( Piecewise((x + y, And(S(1) <= i, i <= l)), (0, True)) + Piecewise((x + y, And(S(1) <= j, j <= l)), (0, True))) assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, l, 3)) == piecewise_fold( Piecewise((x + y, And(l <= i, i <= 3)), (0, True)) + Piecewise((x + y, And(l <= j, j <= 3)), (0, True))) assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, l, m)) == piecewise_fold( Piecewise((x + y, And(l <= i, i <= m)), (0, True)) + Piecewise((x + y, And(l <= j, j <= m)), (0, True))) def test_deltasummation_add_mul_x_y_mul_x_kd(): assert ds(x*y + x*KD(i, j), (j, 1, 3)) == \ Piecewise((3*x*y + x, And(S(1) <= i, i <= 3)), (3*x*y, True)) assert ds(x*y + x*KD(i, j), (j, 1, 1)) == \ Piecewise((x*y + x, Eq(i, 1)), (x*y, True)) assert ds(x*y + x*KD(i, j), (j, 2, 2)) == \ Piecewise((x*y + x, Eq(i, 2)), (x*y, True)) assert ds(x*y + x*KD(i, j), (j, 3, 3)) == \ Piecewise((x*y + x, Eq(i, 3)), (x*y, True)) assert ds(x*y + x*KD(i, j), (j, 1, k)) == \ Piecewise((k*x*y + x, And(S(1) <= i, i <= k)), (k*x*y, True)) assert ds(x*y + x*KD(i, j), (j, k, 3)) == \ Piecewise(((4 - k)*x*y + x, And(k <= i, i <= 3)), ((4 - k)*x*y, True)) assert ds(x*y + x*KD(i, j), (j, k, l)) == Piecewise( ((l - k + 1)*x*y + x, And(k <= i, i <= l)), ((l - k + 1)*x*y, True)) def test_deltasummation_mul_x_add_y_kd(): assert ds(x*(y + KD(i, j)), (j, 1, 3)) == \ Piecewise((3*x*y + x, And(S(1) <= i, i <= 3)), (3*x*y, True)) assert ds(x*(y + KD(i, j)), (j, 1, 1)) == \ Piecewise((x*y + x, Eq(i, 1)), (x*y, True)) assert ds(x*(y + KD(i, j)), (j, 2, 2)) == \ Piecewise((x*y + x, Eq(i, 2)), (x*y, True)) assert ds(x*(y + KD(i, j)), (j, 3, 3)) == \ Piecewise((x*y + x, Eq(i, 3)), (x*y, True)) assert ds(x*(y + KD(i, j)), (j, 1, k)) == \ Piecewise((k*x*y + x, And(S(1) <= i, i <= k)), (k*x*y, True)) assert ds(x*(y + KD(i, j)), (j, k, 3)) == \ Piecewise(((4 - k)*x*y + x, And(k <= i, i <= 3)), ((4 - k)*x*y, True)) assert ds(x*(y + KD(i, j)), (j, k, l)) == Piecewise( ((l - k + 1)*x*y + x, And(k <= i, i <= l)), ((l - k + 1)*x*y, True)) def test_deltasummation_mul_x_add_y_twokd(): assert ds(x*(y + 2*KD(i, j)), (j, 1, 3)) == \ Piecewise((3*x*y + 2*x, And(S(1) <= i, i <= 3)), (3*x*y, True)) assert ds(x*(y + 2*KD(i, j)), (j, 1, 1)) == \ Piecewise((x*y + 2*x, Eq(i, 1)), (x*y, True)) assert ds(x*(y + 2*KD(i, j)), (j, 2, 2)) == \ Piecewise((x*y + 2*x, Eq(i, 2)), (x*y, True)) assert ds(x*(y + 2*KD(i, j)), (j, 3, 3)) == \ Piecewise((x*y + 2*x, Eq(i, 3)), (x*y, True)) assert ds(x*(y + 2*KD(i, j)), (j, 1, k)) == \ Piecewise((k*x*y + 2*x, And(S(1) <= i, i <= k)), (k*x*y, True)) assert ds(x*(y + 2*KD(i, j)), (j, k, 3)) == Piecewise( ((4 - k)*x*y + 2*x, And(k <= i, i <= 3)), ((4 - k)*x*y, True)) assert ds(x*(y + 2*KD(i, j)), (j, k, l)) == Piecewise( ((l - k + 1)*x*y + 2*x, And(k <= i, i <= l)), ((l - k + 1)*x*y, True)) def test_deltasummation_mul_add_x_y_add_y_kd(): assert ds((x + y)*(y + KD(i, j)), (j, 1, 3)) == Piecewise( (3*(x + y)*y + x + y, And(S(1) <= i, i <= 3)), (3*(x + y)*y, True)) assert ds((x + y)*(y + KD(i, j)), (j, 1, 1)) == \ Piecewise(((x + y)*y + x + y, Eq(i, 1)), ((x + y)*y, True)) assert ds((x + y)*(y + KD(i, j)), (j, 2, 2)) == \ Piecewise(((x + y)*y + x + y, Eq(i, 2)), ((x + y)*y, True)) assert ds((x + y)*(y + KD(i, j)), (j, 3, 3)) == \ Piecewise(((x + y)*y + x + y, Eq(i, 3)), ((x + y)*y, True)) assert ds((x + y)*(y + KD(i, j)), (j, 1, k)) == Piecewise( (k*(x + y)*y + x + y, And(S(1) <= i, i <= k)), (k*(x + y)*y, True)) assert ds((x + y)*(y + KD(i, j)), (j, k, 3)) == Piecewise( ((4 - k)*(x + y)*y + x + y, And(k <= i, i <= 3)), ((4 - k)*(x + y)*y, True)) assert ds((x + y)*(y + KD(i, j)), (j, k, l)) == Piecewise( ((l - k + 1)*(x + y)*y + x + y, And(k <= i, i <= l)), ((l - k + 1)*(x + y)*y, True)) def test_deltasummation_mul_add_x_kd_add_y_kd(): assert ds((x + KD(i, k))*(y + KD(i, j)), (j, 1, 3)) == piecewise_fold( Piecewise((KD(i, k) + x, And(S(1) <= i, i <= 3)), (0, True)) + 3*(KD(i, k) + x)*y) assert ds((x + KD(i, k))*(y + KD(i, j)), (j, 1, 1)) == piecewise_fold( Piecewise((KD(i, k) + x, Eq(i, 1)), (0, True)) + (KD(i, k) + x)*y) assert ds((x + KD(i, k))*(y + KD(i, j)), (j, 2, 2)) == piecewise_fold( Piecewise((KD(i, k) + x, Eq(i, 2)), (0, True)) + (KD(i, k) + x)*y) assert ds((x + KD(i, k))*(y + KD(i, j)), (j, 3, 3)) == piecewise_fold( Piecewise((KD(i, k) + x, Eq(i, 3)), (0, True)) + (KD(i, k) + x)*y) assert ds((x + KD(i, k))*(y + KD(i, j)), (j, 1, k)) == piecewise_fold( Piecewise((KD(i, k) + x, And(S(1) <= i, i <= k)), (0, True)) + k*(KD(i, k) + x)*y) assert ds((x + KD(i, k))*(y + KD(i, j)), (j, k, 3)) == piecewise_fold( Piecewise((KD(i, k) + x, And(k <= i, i <= 3)), (0, True)) + (4 - k)*(KD(i, k) + x)*y) assert ds((x + KD(i, k))*(y + KD(i, j)), (j, k, l)) == piecewise_fold( Piecewise((KD(i, k) + x, And(k <= i, i <= l)), (0, True)) + (l - k + 1)*(KD(i, k) + x)*y) sympy-0.7.4.1/sympy/concrete/tests/__init__.py0000644000175000017500000000000012253362407021470 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/concrete/__init__.py0000644000175000017500000000011612253362407020336 0ustar georgeskgeorgeskfrom .products import product, Product from .summations import summation, Sum sympy-0.7.4.1/sympy/galgebra/0000755000175000017500000000000012253362407016171 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/galgebra/stringarrays.py0000644000175000017500000000630512253362407021277 0ustar georgeskgeorgesk# sympy/galgebra/stringarrays.py """ stringarrays.py are a group of helper functions to convert string input to vector and multivector class function to arrays of SymPy symbols. """ import operator from functools import reduce from itertools import combinations from sympy import S, Symbol, Function def str_array(base, n=None): """ Generate one dimensional (list of strings) or two dimensional (list of list of strings) string array. For one dimensional arrays: - base is string of variable names separated by blanks such as base = 'a b c' which produces the string list ['a','b','c'] or it is a string with no blanks than in conjunction with the integer n generates - str_array('v',n=-3) = ['v_1','v_2','v_3'] str_array('v',n=3) = ['v__1','v__2','v__3']. In the case of LaTeX printing the '_' would give a subscript and the '__' a super script. For two dimensional arrays: - base is string where elements are separated by spaces and rows by commas so that - str_array('a b,c d') = [['a','b'],['c','d']] """ if n is None: if ',' in base: base_array = [] base_split = base.split(',') for base_arg in base_split: base_array.append(list(filter(lambda x: x != '', base_arg.split(' ')))) return base_array else: return base.split(' ') result = [] if isinstance(n, str): if n[0] == '-': for index in n[1:].split(' '): result.append(base + '_' + index) if n[0] == '+': for index in n[1:].split(' '): result.append(base + '__' + index) if n > 0: for i in range(1, n + 1): result.append(base + '__' + str(i)) if n < 0: for i in range(1, -n + 1): result.append(base + '_' + str(i)) return result def symbol_array(base, n=None): """ Generates a string arrary with str_array and replaces each string in array with Symbol of same name. """ symbol_str_lst = str_array(base, n) result = [] for symbol_str in symbol_str_lst: result.append(S(symbol_str)) return tuple(result) def fct_sym_array(str_lst, coords=None): """ Construct list of symbols or functions with names in 'str_lst'. If 'coords' are given (tuple of symbols) function list constructed, otherwise a symbol list is constructed. """ if coords is None: fs_lst = [] for sym_str in str_lst: fs_lst.append(Symbol(sym_str)) else: fs_lst = [] for fct_str in str_lst: fs_lst.append(Function(fct_str)(*coords)) return fs_lst def str_combinations(base, lst, rank=1, mode='_'): """ Construct a list of strings of the form 'base+mode+indexes' where the indexes are formed by converting 'lst' to a list of strings and then forming the 'indexes' by concatenating combinations of elements from 'lst' taken 'rank' at a time. """ str_lst = list(map(lambda x: base + mode + x, map(lambda x: reduce(operator.add, x), combinations(map(lambda x: str(x), lst), rank)))) return str_lst sympy-0.7.4.1/sympy/galgebra/debug.py0000644000175000017500000001007312253362407017632 0ustar georgeskgeorgesk# sympy/galgebra/debug.py from __future__ import print_function from itertools import chain, islice def ostr(obj, dict_mode=False): """ Recursively convert iterated object (list/tuple/dict/set) to string. """ def ostr_rec(obj, dict_mode): global ostr_s if isinstance(obj, tuple): if len(obj) == 0: ostr_s += '(),' else: ostr_s += '(' for obj_i in obj: ostr_rec(obj_i, dict_mode) ostr_s = ostr_s[:-1] + '),' elif isinstance(obj, list): if len(obj) == 0: ostr_s += '[],' else: ostr_s += '[' for obj_i in obj: ostr_rec(obj_i, dict_mode) ostr_s = ostr_s[:-1] + '],' elif isinstance(obj, dict): if dict_mode: ostr_s += '\n' for key in obj.keys(): ostr_rec(key, dict_mode) if ostr_s[-1] == ',': ostr_s = ostr_s[:-1] ostr_s += ' -> ' ostr_rec(obj[key], dict_mode) if ostr_s[-1] == ',': ostr_s = ostr_s[:-1] ostr_s += '\n' else: ostr_s += '{' for key in obj.keys(): ostr_rec(key, dict_mode) if ostr_s[-1] == ',': ostr_s = ostr_s[:-1] ostr_s += ':' ostr_rec(obj[key], dict_mode) ostr_s = ostr_s[:-1] + '} ' elif isinstance(obj, set): tmp_obj = list(obj) ostr_s += '{' for obj_i in tmp_obj: ostr_rec(obj_i, dict_mode) ostr_s = ostr_s[:-1] + '},' else: ostr_s += str(obj) + ',' return global ostr_s ostr_s = '' if isinstance(obj, (tuple, list, dict, set)): ostr_rec(obj, dict_mode) return ostr_s[:-1] else: return str(obj) def oprint(*args, **kwargs): """ Debug printing for iterated (list/tuple/dict/set) objects. args is of form (title1,object1,title2,object2,...) and prints: title1 = object1 title2 = object2 ... If you only wish to print a title set object = None. """ if 'dict_mode' in kwargs: dict_mode = kwargs['dict_mode'] else: dict_mode = False if isinstance(args[0], str) or args[0] is None: titles = list(islice(args, None, None, 2)) objs = tuple(islice(args, 1, None, 2)) if len(args) > 2: if objs[0] is None: n = 0 else: n = len(titles[0]) for (title, obj) in zip(titles[1:], objs[1:]): if obj is not None: if not (dict_mode and isinstance(obj, dict)): n = max(n, len(title)) else: n = len(titles[0]) for (title, obj) in zip(titles, objs): if obj is None: print(title) else: npad = n - len(title) if isinstance(obj, dict): print(title + ':' + ostr(obj, dict_mode)) else: print(title + npad * ' ' + ' = ' + ostr(obj, dict_mode)) else: for arg in args: print(ostr(arg, dict_mode)) return def print_sub_table(title, keys, sdict, blade_rep=True): """ Print substitution dictionary, sdict, according to order of keys in keys """ if title is not None: print(title) for key in keys: print(str(key) + ' = ' + ostr(sdict[key])) return def print_product_table(title, keys, pdict, op='*', blade_rep=True): """ Print product dictionary, pdict, according to order of keys in keys """ if title is not None: print(title) pop = ')' + op + '(' for key1 in keys: for key2 in keys: print('(' + str(key1) + pop + str(key2) + ') = ' + ostr(pdict[(key1, key2)])) return sympy-0.7.4.1/sympy/galgebra/manifold.py0000644000175000017500000002347412253362407020346 0ustar georgeskgeorgesk# sympy/galgebra/manifold.py """ manifold.py defines the Manifold class which allows one to create a vector manifold (manifold defined by vector field of coordinates in embedding vector space) calculate the tangent vectors and derivatives of tangent vectors. Once manifold is created multivector fields can be constructed in the tangent space and all the geometric algebra products and derivatives of the multivector fields calculated. Note that all calculations are done in the embedding space. Future versions of the code will allow manifolds defined purely in terms of a metric. """ from __future__ import print_function from itertools import combinations from os import system import copy from sympy import trigsimp, simplify from sympy.galgebra.ga import MV from sympy.galgebra.debug import oprint from sympy.galgebra.ncutil import linear_expand from sympy.galgebra.printing import find_executable def fct_to_str(fct_names): import sys current_file = open(sys.argv[0], 'r') file_str = current_file.read() current_file.close() if isinstance(fct_names, str): return fct_names fcts_str = '' for fct_name in fct_names: start_def = file_str.find('\ndef ' + fct_name) end_def = file_str.find('\ndef ', start_def + 5) start_class = file_str.find('\nclass ', start_def + 5) end_def = min(end_def, start_class) fcts_str += file_str[start_def:end_def] return fcts_str def VectorComponents(X, basis): (coefs, bases) = linear_expand(X.obj) cdict = {} for (coef, base) in zip(coefs, bases): cdict[str(base)] = coef comp = [] for base in basis: if base in cdict: comp.append(cdict[base]) else: comp.append(0) return comp def FillTemplate(self, template): Nd = 0 var = [] id_old = 0 while True: id_new = template.find('$', id_old + 1) if id_new == -1: break Nd += 1 if Nd % 2 == 0: var.append(template[id_old + 1:id_new]) id_old = id_new var.sort(reverse=True) for v in var: template = template.replace('$' + v + '$', str(eval('self.' + v))) return template class Manifold: def __init__(self, x, coords, debug=False, I=None): """ coords: list of coordinate variables x: vector fuction of coordinate variables (parametric surface) """ self.I = I self.x = x self.coords = coords self.basis = [] self.basis_str = [] self.embedded_basis = [] for u in coords: tv = x.diff(u) self.basis.append(tv) (coefs, bases) = linear_expand(tv.obj) tc = {} for (coef, base) in zip(coefs, bases): str_base = str(base) tc[str_base] = coef if str_base not in self.embedded_basis: self.embedded_basis.append(str_base) self.basis_str.append(tc) self.gij = [] for base1 in self.basis: tmp = [] for base2 in self.basis: tmp.append(simplify(trigsimp((base1 | base2).scalar()))) self.gij.append(tmp) for tv in self.basis_str: for base in self.embedded_basis: if base not in tv: tv[base] = 0 self.dim = len(self.basis) indexes = tuple(range(self.dim)) self.index = [()] for i in indexes: self.index.append(tuple(combinations(indexes, i + 1))) self.index = tuple(self.index) self.MFbasis = [[MV.ONE], self.basis] for igrade in self.index[2:]: grade = [] for iblade in igrade: blade = MV(1, 'scalar') for ibasis in iblade: blade ^= self.basis[ibasis] blade = blade.trigsimp(deep=True, recursive=True) grade.append(blade) self.MFbasis.append(grade) self.E = self.MFbasis[-1][0] self.E_sq = trigsimp((self.E * self.E).scalar(), deep=True, recursive=True) duals = copy.copy(self.MFbasis[-2]) duals.reverse() sgn = 1 self.rbasis = [] for dual in duals: recpv = (sgn * dual * self.E).trigsimp(deep=True, recursive=True) self.rbasis.append(recpv) sgn = -sgn self.dbasis = [] for base in self.basis: dbase = [] for coord in self.coords: d = base.diff(coord).trigsimp(deep=True, recursive=True) dbase.append(d) self.dbasis.append(dbase) self.surface = {} (coefs, bases) = linear_expand(self.x.obj) for (coef, base) in zip(coefs, bases): self.surface[str(base)] = coef self.grad = MV() self.grad.is_grad = True self.grad.blade_rep = True self.grad.igrade = 1 self.grad.rcpr_bases_MV = [] for rbase in self.rbasis: self.grad.rcpr_bases_MV.append(rbase / self.E_sq) self.grad.rcpr_bases_MV = tuple(self.grad.rcpr_bases_MV) self.grad.coords = self.coords self.grad.norm = self.E_sq self.grad.connection = {} if debug: oprint('x', self.x, 'coords', self.coords, 'basis vectors', self.basis, 'index', self.index, 'basis blades', self.MFbasis, 'E', self.E, 'E**2', self.E_sq, '*basis', duals, 'rbasis', self.rbasis, 'basis derivatives', self.dbasis, 'surface', self.surface, 'basis strings', self.basis_str, 'embedding basis', self.embedded_basis, 'metric tensor', self.gij) def Basis(self): return tuple(self.basis) def Grad(self, F): # Intrisic Derivative dF = 0 for (rbase, coord) in zip(self.rbasis, self.coords): dF += rbase * F.diff(coord) dF = dF.simplify() dF = dF / self.E_sq return dF def D(self, F): # Covariant Derivative dF = self.Grad(F) return self.Proj(dF) def S(self, a): # Shape Tensor return def Proj(self, F): PF = (F < self.E) * self.E PF = PF.simplify() PF = PF.trigsimp(deep=True, recursive=True) return (PF / self.E_sq).simplify() def Reject(self, F): return (F - self.Proj(F)).simplify() def DD(self, v, f, opstr=False): mf_comp = [] for e in self.rbasis: mf_comp.append((v | e).scalar() / self.E_sq) result = MV() op = '' for (coord, comp) in zip(self.coords, mf_comp): result += comp * (f.diff(coord)) if opstr: op += '(' + str(comp) + ')D{' + str(coord) + '}+' if opstr: return str(result), op[:-1] return result def Plot2DSurface(self, u_range, v_range, surf=True, grid=True, tan=1.0, scalar_field=None, skip=[1, 1], fct_def=None): plot_template = \ """ from numpy import mgrid,shape,swapaxes,zeros,log,exp,sin,cos,tan $fct_def$ eps = 1.0e-6 u_r = $u_range$ v_r = $v_range$ $coords$ = mgrid[u_r[0]:u_r[1]+eps:(u_r[1]-u_r[0])/float(u_r[2]-1),\\ v_r[0]:v_r[1]+eps:(v_r[1]-v_r[0])/float(v_r[2]-1)] X = $surface$ scal_tan = $tan$ x = X['ex'] y = X['ey'] z = X['ez'] du = $basis_str[0]$ dv = $basis_str[1]$ Zero = zeros(shape(x)) if scal_tan > 0.0: du_x = Zero+du['ex'] du_y = Zero+du['ey'] du_z = Zero+du['ez'] dv_x = Zero+dv['ex'] dv_y = Zero+dv['ey'] dv_z = Zero+dv['ez'] f = $scalar_field$ n = $n$ skip = $skip$ su = skip[0] sv = skip[1] if f[0] != None: dn_x = f[0]*n[0] dn_y = f[0]*n[1] dn_z = f[0]*n[2] from mayavi.mlab import plot3d,quiver3d,mesh,figure figure(bgcolor=(1.0,1.0,1.0)) if $surf$: mesh(x,y,z,colormap="gist_earth") if $grid$: for i in range(shape(u)[0]): plot3d(x[i,],y[i,],z[i,],line_width=1.0,color=(0.0,0.0,0.0),tube_radius=None) xr = swapaxes(x,0,1) yr = swapaxes(y,0,1) zr = swapaxes(z,0,1) for i in range(shape(u)[1]): plot3d(xr[i,],yr[i,],zr[i,],line_width=1.0,color=(0.0,0.0,0.0),tube_radius=None) if scal_tan > 0.0: quiver3d(x[::su,::sv],y[::su,::sv],z[::su,::sv],\\ du_x[::su,::sv],du_y[::su,::sv],du_z[::su,::sv],scale_factor=scal_tan,\\ line_width=1.0,color=(0.0,0.0,0.0),scale_mode='vector',mode='arrow',resolution=16) quiver3d(x[::su,::sv],y[::su,::sv],z[::su,::sv],\\ dv_x[::su,::sv],dv_y[::su,::sv],dv_z[::su,::sv],scale_factor=scal_tan,\\ line_width=1.0,color=(0.0,0.0,0.0),scale_mode='vector',mode='arrow',resolution=16) if f[0] != None: quiver3d(x[::su,::sv],y[::su,::sv],z[::su,::sv],\\ dn_x[::su,::sv],dn_y[::su,::sv],dn_z[::su,::sv],\\ line_width=1.0,color=(0.0,0.0,0.0),scale_mode='none',mode='cone',\\ resolution=16,opacity=0.5) """ if len(self.coords) != 2: return self.skip = skip self.surf = surf self.grid = grid self.tan = tan if fct_def is None: self.fct_def = ' ' else: self.fct_def = fct_to_str(fct_def) self.u_range = u_range self.v_range = v_range self.scalar_field = [scalar_field] print(self.I, '\n', self.basis[0], '\n', self.basis[1]) self.normal = -self.I * (self.basis[0] ^ self.basis[1]) self.n = VectorComponents(self.normal, ['ex', 'ey', 'ez']) msurf = open('manifold_surf.py', 'w') plot_template = FillTemplate(self, plot_template) msurf.write(plot_template) msurf.close() mayavi2 = find_executable('mayavi2') if mayavi2 is None: return system(mayavi2 + ' manifold_surf.py &') return sympy-0.7.4.1/sympy/galgebra/vector.py0000644000175000017500000002412012253362407020044 0ustar georgeskgeorgesk# sympy/galgebra/vector.py """ vector.py is a helper class for the MV class that defines the basis vectors and metric and calulates derivatives of the basis vectors for the MV class. """ import itertools import copy from sympy import Symbol, S, Matrix, trigsimp, diff, expand from sympy.galgebra.printing import GA_Printer from sympy.galgebra.stringarrays import str_array from sympy.galgebra.ncutil import linear_derivation, bilinear_product from sympy.galgebra.debug import oprint def flatten(lst): return list(itertools.chain(*lst)) def TrigSimp(x): return trigsimp(x, recursive=True) class Vector(object): """ Vector class. Setup is done by defining a set of basis vectors in static function 'Bases'. The linear combination of scalar (commutative) sympy quatities and the basis vectors form the vector space. If the number of basis vectors is 'n' the metric tensor is formed as an n by n sympy matrix of scalar symbols and represents the dot products of pairs of basis vectors. """ is_orthogonal = False @staticmethod def setup(base, n=None, metric=None, coords=None, curv=(None, None), debug=False): """ Generate basis of vector space as tuple of vectors and associated metric tensor as Matrix. See str_array(base,n) for usage of base and n and str_array(metric) for usage of metric. To overide elements in the default metric use the character '#' in the metric string. For example if one wishes the diagonal elements of the metric tensor to be zero enter metric = '0 #,# 0'. If the basis vectors are e1 and e2 then the default metric - Vector.metric = ((dot(e1,e1),dot(e1,e2)),dot(e2,e1),dot(e2,e2)) becomes - Vector.metric = ((0,dot(e1,e2)),(dot(e2,e1),0)). The function dot returns a Symbol and is symmetric. The functions 'Bases' calculates the global quantities: - Vector.basis tuple of basis vectors Vector.base_to_index dictionary to convert base to base inded Vector.metric metric tensor represented as a matrix of symbols and numbers """ Vector.is_orthogonal = False Vector.coords = coords Vector.subscripts = [] base_name_lst = base.split(' ') # Define basis vectors if '*' in base: base_lst = base.split('*') base = base_lst[0] Vector.subscripts = base_lst[1].split('|') base_name_lst = [] for subscript in Vector.subscripts: base_name_lst.append(base + '_' + subscript) else: if len(base_name_lst) > 1: Vector.subscripts = [] for base_name in base_name_lst: tmp = base_name.split('_') Vector.subscripts.append(tmp[-1]) elif len(base_name_lst) == 1 and Vector.coords is not None: base_name_lst = [] for coord in Vector.coords: Vector.subscripts.append(str(coord)) base_name_lst.append(base + '_' + str(coord)) else: raise TypeError("'%s' does not define basis vectors" % base) basis = [] base_to_index = {} index = 0 for base_name in base_name_lst: basis_vec = Vector(base_name) basis.append(basis_vec) base_to_index[basis_vec.obj] = index index += 1 Vector.base_to_index = base_to_index Vector.basis = tuple(basis) # define metric tensor default_metric = [] for bv1 in Vector.basis: row = [] for bv2 in Vector.basis: row.append(Vector.basic_dot(bv1, bv2)) default_metric.append(row) Vector.metric = Matrix(default_metric) if metric is not None: if metric[0] == '[' and metric[-1] == ']': Vector.is_orthogonal = True metric_str_lst = metric[1:-1].split(',') Vector.metric = [] for g_ii in metric_str_lst: Vector.metric.append(S(g_ii)) Vector.metric = Matrix(Vector.metric) else: metric_str_lst = flatten(str_array(metric)) for index in range(len(metric_str_lst)): if metric_str_lst[index] != '#': Vector.metric[index] = S(metric_str_lst[index]) Vector.metric_dict = {} # Used to calculate dot product N = range(len(Vector.basis)) if Vector.is_orthogonal: for ii in N: Vector.metric_dict[Vector.basis[ii].obj] = Vector.metric[ii] else: for irow in N: for icol in N: Vector.metric_dict[(Vector.basis[irow].obj, Vector.basis[icol].obj)] = Vector.metric[irow, icol] # calculate tangent vectors and metric for curvilinear basis if curv != (None, None): X = S.Zero for (coef, base) in zip(curv[0], Vector.basis): X += coef * base.obj Vector.tangents = [] for (coord, norm) in zip(Vector.coords, curv[1]): tau = diff(X, coord) tau = trigsimp(tau) tau /= norm tau = expand(tau) Vtau = Vector() Vtau.obj = tau Vector.tangents.append(Vtau) metric = [] for tv1 in Vector.tangents: row = [] for tv2 in Vector.tangents: row.append(tv1 * tv2) metric.append(row) metric = Matrix(metric) metric = metric.applyfunc(TrigSimp) Vector.metric_dict = {} if metric.is_diagonal: Vector.is_orthogonal = True tmp_metric = [] for ii in N: tmp_metric.append(metric[ii, ii]) Vector.metric_dict[Vector.basis[ii].obj] = metric[ii, ii] Vector.metric = Matrix(tmp_metric) else: Vector.is_orthogonal = False Vector.metric = metric for irow in N: for icol in N: Vector.metric_dict[(Vector.basis[irow].obj, Vector.basis[icol].obj)] = Vector.metric[irow, icol] Vector.norm = curv[1] if debug: oprint('Tangent Vectors', Vector.tangents, 'Metric', Vector.metric, 'Metric Dictionary', Vector.metric_dict, 'Normalization', Vector.norm, dict_mode=True) # calculate derivatives of tangent vectors Vector.dtau_dict = None dtau_dict = {} for x in Vector.coords: for (tau, base) in zip(Vector.tangents, Vector.basis): dtau = tau.diff(x).applyfunc(TrigSimp) result = S.Zero for (t, b) in zip(Vector.tangents, Vector.basis): t_dtau = TrigSimp(t * dtau) result += t_dtau * b.obj dtau_dict[(base.obj, x)] = result Vector.dtau_dict = dtau_dict if debug: oprint('Basis Derivatives', Vector.dtau_dict, dict_mode=True) return tuple(Vector.basis) def __init__(self, basis_str=None): if isinstance(basis_str, Vector): self.obj = basis_str else: if basis_str is None or basis_str == '0': self.obj = S(0) else: self.obj = Symbol(basis_str, commutative=False) """ def diff(self, x): (coefs, bases) = linear_expand(self.obj) result = S.Zero for (coef, base) in zip(coefs, bases): result += diff(coef, x) * base return result """ def diff(self, x): Dself = Vector() if isinstance(Vector.dtau_dict, dict): Dself.obj = linear_derivation(self.obj, Vector.Diff, x) else: Dself.obj = diff(self.obj, x) return Dself @staticmethod def basic_dot(v1, v2): """ Dot product of two basis vectors returns a Symbol """ i1 = list(Vector.basis).index(v1) # Python 2.5 i2 = list(Vector.basis).index(v2) # Python 2.5 if i1 < i2: dot_str = '(' + str(Vector.basis[i1]) + '.' + str(Vector.basis[i2]) + ')' else: dot_str = '(' + str(Vector.basis[i2]) + '.' + str(Vector.basis[i1]) + ')' return Symbol(dot_str) @staticmethod def dot(b1, b2): if Vector.is_orthogonal: if b1 != b2: return S.Zero else: return Vector.metric_dict[b1] else: return Vector.metric_dict[(b1, b2)] @staticmethod def Diff(b, x): return Vector.dtau_dict[(b, x)] ######################## Operator Definitions####################### def __str__(self): return GA_Printer().doprint(self) def __mul__(self, v): if not isinstance(v, Vector): self_x_v = Vector() self_x_v.obj = self.obj * v return self_x_v else: result = expand(self.obj * v.obj) result = bilinear_product(result, Vector.dot) return result def __rmul__(self, s): s_x_self = Vector() s_x_self.obj = s * self.obj return s_x_self def __add__(self, v): self_p_v = Vector() self_p_v.obj = self.obj + v.obj return self_p_v def __add_ab__(self, v): self.obj += v.obj return def __sub__(self, v): self_m_v = Vector() self_m_v.obj = self.obj - v.obj return self_m_v def __sub_ab__(self, v): self.obj -= v.obj return def __pos__(self): return self def __neg__(self): n_self = copy.deepcopy(self) n_self.obj = -self.obj return n_self def applyfunc(self, fct): fct_self = Vector() fct_self.obj = fct(self.obj) return fct_self sympy-0.7.4.1/sympy/galgebra/ncutil.py0000644000175000017500000003400312253362407020041 0ustar georgeskgeorgesk# sympy/galgebra/ncutil.py """ ncutil.py contains all the needed utility functions that only depend on SymPy and that are required for the expansion and manipulation of linear combinations of noncommutative SymPy symbols. also contains "half_angle_reduce" which is probably not needed any more due to the improvements in trigsimp. """ from sympy import expand, Mul, Add, Symbol, S, Pow, diff, trigsimp, \ simplify, sin, cos, symbols try: from numpy import matrix numpy_loaded = True except ImportError: numpy_loaded = False ONE_NC = Symbol('ONE', commutative=False) def get_commutative_coef(expr): if isinstance(expr, Mul): (coefs, bases) = expr.args_cnc() return Mul(*coefs) return S.One def half_angle_reduce(expr, theta): s, c = symbols('s c') sub_dict = {sin(theta / 2): s, cos(theta / 2): c} new_expr = expr.subs(sub_dict) sub_dict = {s * c: sin(theta) / 2, s**2: (1 - cos(theta)) / 2, c**2: (1 + cos(theta)) / 2} # print new_expr new_expr = trigsimp(simplify(new_expr.subs(sub_dict)), recursive=True) # print expand(new_expr) return new_expr def linear_expand(expr): """ If a sympy 'Expr' is of the form: expr = expr_0 + expr_1*a_1 + ... + expr_n*a_n where all the a_j are noncommuting symbols in basis then (expr_0, ..., expr_n) and (1, a_1, ..., a_n) are returned. Note that expr_j*a_j does not have to be of that form, but rather can be any Mul with a_j as a factor (it doen not have to be a postmultiplier). expr_0 is the scalar part of the expression. """ expr = expand(expr) if expr.is_commutative: # commutative expr only contains expr_0 return (expr, ), (S.One, ) if isinstance(expr, Mul): # expr only contains one term (coefs, bases) = expr.args_cnc() coefs = Mul(*coefs) bases = bases[0] elif isinstance(expr, Symbol): # term is Symbol coefs = S.One bases = expr elif isinstance(expr, Add): # expr has multiple terms coefs = [] bases = [] for arg in expr.args: term = arg.args_cnc() coef = Mul(*term[0]) base = term[1][0] if base in bases: # increment coefficient of base ibase = list(bases).index(base) # Python 2.5 coefs[ibase] += coef else: # add base to list coefs.append(coef) bases.append(base) else: raise NotImplementedError("linear_expand for type %s" % type(expr)) if not isinstance(coefs, list): # convert single coef to list coefs = [coefs] if not isinstance(bases, list): # convert single base to list bases = [bases] coefs = tuple(coefs) bases = tuple(bases) return coefs, bases def linear_projection(expr, plist=None): """ If a sympy 'Expr' is of the form: expr = expr_0 + expr_1*a_1 + ... + expr_n*a_n where all the a_j are noncommuting symbols in basis then proj(expr) returns the sum of those terms where a_j is in plist """ if expr.is_commutative and plist is None: # return scalar projection return expr expr = expand(expr) if isinstance(expr, Mul): # expr has single term (coefs, bases) = expr.args_cnc() if bases[0] in plist: # vector term to be projected return Mul(*coefs) * bases[0] else: return S.Zero elif isinstance(expr, Symbol): # base vector to be projected if expr in plist: return expr else: return S.Zero elif isinstance(expr, Add): # expr has multiple terms result = S.Zero for arg in expr.args: term = arg.args_cnc() if term[1] == [] and plist is None: # scalar term to be projected result += Mul(*term[0]) elif term[1] != [] and plist is not None and term[1][0] in plist: # vector term to be projected result += Mul(*term[0]) * term[1][0] return result def non_scalar_projection(expr): """ If a sympy 'Expr' is of the form: expr = expr_0*S.One + expr_1*a_1 + ... + expr_n*a_n where all the a_j are noncommuting symbols in basis then proj(expr) returns the sum of those terms where a_j is in plist """ if expr.is_commutative: # return scalar projection return S.Zero expr = expand(expr) if isinstance(expr, Mul): # expr has single term (coefs, bases) = expr.args_cnc() if bases[0] != ONE_NC: # vector term to be projected return Mul(*coefs) * bases[0] else: return S.Zero elif isinstance(expr, Symbol): # base vector to be projected if expr != ONE_NC: return expr else: return S.Zero elif isinstance(expr, Add): # expr has multiple terms result = S.Zero for arg in expr.args: term = arg.args_cnc() if term[1] != ONE_NC: # vector term to be projected result += Mul(*term[0]) * term[1][0] return result def nc_substitue(expr, sub_dict): (coefs, bases) = linear_expand(expr) result = S.Zero for (coef, base) in zip(coefs, bases): if base != 1: result += coef * sub_dict[base] return result def linear_function(expr, fct): """ If a sympy 'Expr' is of the form: expr = expr_0 + expr_1*a_1 + ... + expr_n*a_n where all the a_j are noncommuting symbols in basis then f(expr) = expr_0 + expr_1*f(a_1) + ... + expr_n*f(a_n) is returned """ if expr.is_commutative: return expr expr = expand(expr) if isinstance(expr, Mul): (coefs, bases) = expr.args_cnc() return Mul(*coefs) * fct(bases[0]) elif isinstance(expr, Symbol): return fct(expr) elif isinstance(expr, Add): result = S.Zero for arg in expr.args: term = arg.args_cnc() if term[1] == []: result += Mul(*term[0]) else: result += Mul(*term[0]) * fct(term[1][0]) return result def coef_function(expr, fct): """ If a sympy 'Expr' is of the form: expr = expr_0 + expr_1*a_1 + ... + expr_n*a_n where all the a_j are noncommuting symbols in basis then f(expr) = fct(expr_0) + fct(expr_1)*a_1 + ... + fct(expr_n)*a_n is returned """ expr = expand(expr) if isinstance(expr, Mul): (coefs, bases) = expr.args_cnc() return fct(Mul(*coefs)) * bases[0] elif isinstance(expr, Symbol): if expr.is_commutative: return fct(expr) else: return expr elif isinstance(expr, Add): result = S.Zero for arg in expr.args: term = arg.args_cnc() if term[1] == []: result += fct(Mul(*term[0])) else: result += fct(Mul(*term[0])) * fct(term[1][0]) return result def bilinear_product(expr, fct): """ If a sympy 'Expr' is of the form: expr = expr_ij*a_i*a_j or expr_0 or expr_i*a_i where all the a_i are noncommuting symbols in basis and the expr's are commuting expressions then bilinear_product(expr) = expr_ij*fct(a_i, a_j) bilinear_product(expr_0) = expr_0 bilinear_product(expr_i*a_i) = expr_i*a_i """ def bilinear_term(expr, fct): if expr.is_zero: return expr if isinstance(expr, Mul): # bases in expr (coefs, bases) = expr.args_cnc() coef = Mul(*tuple(coefs)) if isinstance(bases[0], Pow): # base is a_i**2 args = bases[0].args return coef * fct(args[0], args[0]) elif len(bases) == 1: # base is a_i return expr else: # base is a_i*a_j return coef * fct(bases[0], bases[1]) elif isinstance(expr, Pow): # expr is a_i*a_i args = expr.args return fct(args[0], args[0]) elif isinstance(expr, Symbol): return expr else: raise TypeError('!!!!Cannot compute bilinear_product for ' + str(expr) + '!!!!\n') expr = expand(expand(expr)) if not isinstance(expr, Add): return bilinear_term(expr, fct) else: result = S.Zero for term in expr.args: tmp = bilinear_term(term, fct) result += tmp return result def multilinear_product(expr, fct): """ If a sympy 'Expr' is of the form: expr = expr_i1i2...irj*a_i1*a_i2*...*a_ir or expr_0 where all the a_i are noncommuting symbols in basis and the expr's are commuting expressions then multilinear_product(expr) = expr_i1i2...ir*fct(a_i1, a_i2, ..., a_ir) bilinear_product(expr_0) = expr_0 where fct() is defined for r <= n the total number of bases """ if expr.is_commutative: # no bases in expr return expr if isinstance(expr, Mul): # bases in expr (coefs, bases) = expr.args_cnc() if len(coefs) == 0: # expr_ij = 1 coefs = [S.One] coef = Mul(*tuple(coefs)) new_bases = [] for base in bases: if isinstance(base, Pow): args = base.args new_bases += args[1] * [args[0]] else: new_bases.append(base) return coef * fct(new_bases) def bilinear_function(expr, fct): """ If a sympy 'Expr' is of the form: expr = expr_0 + expr_1*a_1 + ... + expr_n*a_n + expr_11*a_1*a_1 + ... + expr_ij*a_i*a_j + ... + expr_nn*a_n*a_n where all the a_j are noncommuting symbols in basis then bilinear_function(expr) = bilinear_product(expr_0) + bilinear_product(expr_1*a_1) + ... + bilinear_product(expr_n*a_n) + bilinear + product(expr_11*a_1*a_1) + ... + bilinear_product(expr_nn*a_n*a_n) """ if expr.is_commutative: return expr expr = expand(expr) if isinstance(expr, (Mul, Pow, Symbol)): # only one additive term return bilinear_product(expr, fct) elif isinstance(expr, Add): # multiple additive terms result = S.Zero for arg in expr.args: result += bilinear_product(arg, fct) return result def multilinear_function(expr, fct): """ If a sympy 'Expr' is of the form summation convention): expr = expr_0 + Sum{0 < r <= n}{expr_i1i2...ir*a_i1*a_i2*...*a_ir} where all the a_j are noncommuting symbols in basis then and the dimension of the basis in n then bilinear_function(expr) = multilinear_product(expr_0) + Sum{0 /dev/null', '&': '&'}, 'win32': {'rm': 'del', 'evince': '', 'null': ' > NUL', '&': ''}, 'darwin': {'rm': 'rm', 'evince': 'open', 'null': ' > /dev/null', '&': '&'}} ColorCode = { 'black': '0;30', 'bright gray': '0;37', 'blue': '0;34', 'white': '1;37', 'green': '0;32', 'bright blue': '1;34', 'cyan': '0;36', 'bright green': '1;32', 'red': '0;31', 'bright cyan': '1;36', 'purple': '0;35', 'bright red': '1;31', 'yellow': '0;33', 'bright purple': '1;35', 'dark gray': '1;30', 'bright yellow': '1;33', 'normal': '0' } InvColorCode = dict(zip(ColorCode.values(), ColorCode.keys())) accepted_latex_functions = ['arcsin', 'arccos', 'arctan', 'sin', 'cos', 'tan', 'theta', 'beta', 'alpha', 'gamma', 'sinh', 'cosh', 'tanh', 'sqrt', 'ln', 'log', 'sec', 'csc', 'cot', 'coth', 're', 'im', 'frac', 'root', 'arg', 'zeta'] 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 class enhance_print: """ A class for color coding the string printing going to a terminal. """ normal = '' base = '' fct = '' deriv = '' bold = '' def __init__(self, base=None, fct=None, deriv=None, on=True, keys=False): if on: if 'win' in sys.platform: if base is None: enhance_print.base = ColorCode['blue'] else: enhance_print.base = ColorCode[base] if fct is None: enhance_print.fct = ColorCode['red'] else: enhance_print.fct = ColorCode[fct] if deriv is None: enhance_print.deriv = ColorCode['cyan'] else: enhance_print.deriv = ColorCode[deriv] enhance_print.normal = '\033[0m' else: if base is None: enhance_print.base = ColorCode['dark gray'] else: enhance_print.base = ColorCode[base] if fct is None: enhance_print.fct = ColorCode['red'] else: enhance_print.fct = ColorCode[fct] if deriv is None: enhance_print.deriv = ColorCode['cyan'] else: enhance_print.deriv = ColorCode[deriv] enhance_print.normal = '\033[0m' if keys: print('Enhanced Printing is on:') print('Base/Blade color is ' + InvColorCode[enhance_print.base]) print('Function color is ' + InvColorCode[enhance_print.fct]) print('Derivative color is ' + InvColorCode[enhance_print.deriv] + '\n') enhance_print.base = '\033[' + enhance_print.base + 'm' enhance_print.fct = '\033[' + enhance_print.fct + 'm' enhance_print.deriv = '\033[' + enhance_print.deriv + 'm' @staticmethod def enhance_base(s): return enhance_print.base + s + enhance_print.normal @staticmethod def enhance_fct(s): return enhance_print.fct + s + enhance_print.normal @staticmethod def enhance_deriv(s): return enhance_print.deriv + s + enhance_print.normal @staticmethod def strip_base(s): new_s = s.replace(enhance_print.base, '') new_s = new_s.replace(enhance_print.normal, '') return new_s class GA_Printer(StrPrinter): """ An enhanced string printer that is galgebra-aware. """ function_names = ('acos', 'acosh', 'acot', 'acoth', 'arg', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceiling', 'conjugate', 'cos', 'cosh', 'cot', 'coth', 'exp', 'floor', 'im', 'log', 're', 'root', 'sin', 'sinh', 'sqrt', 'sign', 'tan', 'tanh') def _print_Function(self, expr): name = expr.func.__name__ if expr.func.nargs is not None: if name in GA_Printer.function_names: return expr.func.__name__ + "(%s)" % self.stringify(expr.args, ", ") return enhance_print.enhance_fct("%s" % (name, )) def _print_Derivative(self, expr): diff_args = list(map(self._print, expr.args)) return enhance_print.enhance_deriv('D{%s}' % (diff_args[1], )) + '%s' % (diff_args[0], ) def _print_MV(self, expr): if expr.obj.is_zero: return '0' else: if expr.print_blades: expr.base_to_blade() ostr = expr.get_normal_order_str() return ostr def _print_Vector(self, expr): if expr.obj.is_zero: return '0' else: ostr = GA_Printer().doprint(expr.obj) ostr = ostr.replace(' ', '') return ostr @staticmethod def _on(): GA_Printer.Basic__str__ = Basic.__str__ Basic.__str__ = lambda self: GA_Printer().doprint(self) return @staticmethod def _off(): Basic.__str__ = GA_Printer.Basic__str__ return def __enter__ (self): GA_Printer._on() return self def __exit__ (self, type, value, traceback): GA_Printer._off() @staticmethod @deprecated(useinstead="with GA_Printer()", issue=4042, deprecated_since_version="0.7.4") def on(): GA_Printer._on() @staticmethod @deprecated(useinstead="with GA_Printer()", issue=4042, deprecated_since_version="0.7.4") def off(): GA_Printer._off() class GA_LatexPrinter(LatexPrinter): r""" An enhanced LaTeX printer that is galgebra-aware. The latex printer is turned on with the function (in ga.py) - Format(Fmode=True,Dmode=True,ipy=False) where Fmode is the function printing mode that surpresses printing arguments, Dmode is the derivative printing mode that does not use fractions, and ipy=True is the Ipython notebook mode that does not redirect the print output. The latex output is post processed and displayed with the function (in GAPrint.py) - xdvi(filename='tmplatex.tex',debug=False) where filename is the name of the tex file one would keep for future inclusion in documents and debug=True would display the tex file immediately. There are three options for printing multivectors in latex. They are acessed with the multivector member function - A.Fmt(self,fmt=1,title=None) where fmt=1, 2, or 3 determines whether the entire multivector A is printed entirely on one line, or one grade is printed per line, or one base is printed per line. If title is not None then the latex string generated is of the form - title+' = '+str(A) where it is assumed that title is a latex math mode string. If title contains '%' it is treated as a pure latex math mode string. If it does not contain '%' then the following character mappings are applied - - 'grad' replaced by '\\bm{\\nabla} ' - '*' replaced by '' - '^' replaced by '\\W ' - '|' replaced by '\\cdot ' - '>' replaced by '\\lfloor ' - '<' replaced by '\\rfloor ' In the case of a print statement of the form - print(title,A) everthing in the title processing still applies except that the multivector formatting is one multivector per line. For print statements of the form - print(title) where no program variables are printed if title contains '#' then title is printed as regular latex line. If title does not contain '#' then title is printed in equation mode. '%' has the same effect in title as in the Fmt() member function. """ preamble = \ """ \\pagestyle{empty} \\usepackage[latin1]{inputenc} \\usepackage{amsmath} \\usepackage{bm} \\usepackage{amsfonts} \\usepackage{amssymb} \\usepackage{amsbsy} \\usepackage{tensor} \\usepackage{listings} \\usepackage{color} \\definecolor{gray}{rgb}{0.95,0.95,0.95} \\setlength{\\parindent}{0pt} \\newcommand{\\bfrac}[2]{\\displaystyle\\frac{#1}{#2}} \\newcommand{\\lp}{\\left (} \\newcommand{\\rp}{\\right )} \\newcommand{\\half}{\\frac{1}{2}} \\newcommand{\\llt}{\\left <} \\newcommand{\\rgt}{\\right >} \\newcommand{\\abs}[1]{\\left |{#1}\\right | } \\newcommand{\\pdiff}[2]{\\bfrac{\\partial {#1}}{\\partial {#2}}} \\newcommand{\\lbrc}{\\left \\{} \\newcommand{\\rbrc}{\\right \\}} \\newcommand{\\W}{\\wedge} \\newcommand{\\prm}[1]{{#1}'} \\newcommand{\\ddt}[1]{\\bfrac{d{#1}}{dt}} \\newcommand{\\R}{\\dagger} \\newcommand{\\deriv}[3]{\\bfrac{d^{#3}#1}{d{#2}^{#3}}} \\newcommand{\\grade}[1]{\\left < {#1} \\right >} \\newcommand{\\f}[2]{{#1}\\lp{#2}\\rp} \\newcommand{\\eval}[2]{\\left . {#1} \\right |_{#2}} \\usepackage{float} \\floatstyle{plain} % optionally change the style of the new float \\newfloat{Code}{H}{myc} \\lstloadlanguages{Python} \\begin{document} """ postscript = '\\end{document}\n' Dmode = False # True - Print derivative contracted Fmode = False # True - Print function contracted latex_flg = False @staticmethod def redirect(ipy=False): GA_LatexPrinter.ipy = ipy GA_LatexPrinter.latex_flg = True GA_LatexPrinter.Basic__str__ = Basic.__str__ GA_LatexPrinter.Matrix__str__ = Matrix.__str__ Basic.__str__ = lambda self: GA_LatexPrinter().doprint(self) Matrix.__str__ = lambda self: GA_LatexPrinter().doprint(self) if not ipy: GA_LatexPrinter.stdout = sys.stdout sys.stdout = StringIO() return @staticmethod def restore(): GA_LatexPrinter.latex_flg = False if not GA_LatexPrinter.ipy: sys.stdout = GA_LatexPrinter.stdout Basic.__str__ = GA_LatexPrinter.Basic__str__ Matrix.__str__ = GA_LatexPrinter.Matrix__str__ return def _print_Pow(self, expr): base = self._print(expr.base) if ('_' in base or '^' in base) and 'cdot' not in base: mode = True else: mode = False # Treat x**Rational(1,n) as special case if expr.exp.is_Rational and abs(expr.exp.p) == 1 and expr.exp.q != 1: expq = expr.exp.q if expq == 2: tex = r"\sqrt{%s}" % base elif self._settings['itex']: tex = r"\root{%d}{%s}" % (expq, base) else: tex = r"\sqrt[%d]{%s}" % (expq, base) if expr.exp.is_negative: return r"\frac{1}{%s}" % tex else: return tex elif self._settings['fold_frac_powers'] \ and expr.exp.is_Rational \ and expr.exp.q != 1: base, p, q = self._print(expr.base), expr.exp.p, expr.exp.q if mode: return r"{\lp %s \rp}^{%s/%s}" % (base, p, q) else: return r"%s^{%s/%s}" % (base, p, q) elif expr.exp.is_Rational and expr.exp.is_negative and expr.base.is_Function: # Things like 1/x return r"\frac{%s}{%s}" % \ (1, self._print(C.Pow(expr.base, -expr.exp))) else: if expr.base.is_Function: return self._print(expr.base, self._print(expr.exp)) else: if expr.is_commutative and expr.exp == -1: """ solves issue 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: if mode: tex = r"{\lp %s \rp}^{%s}" else: tex = r"%s^{%s}" return tex % (self._print(expr.base), self._print(expr.exp)) def _print_Symbol(self, expr): def str_symbol(name_str): (name, supers, subs) = split_super_sub(name_str) # translate name, supers and subs to tex keywords greek = set(['alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta', 'eta', 'theta', 'iota', 'kappa', 'lambda', 'mu', 'nu', 'xi', 'omicron', 'pi', 'rho', 'sigma', 'tau', 'upsilon', 'phi', 'chi', 'psi', 'omega']) greek_translated = {'lamda': 'lambda', 'Lamda': 'Lambda'} other = set(['aleph', 'beth', 'daleth', 'gimel', 'ell', 'eth', 'hbar', 'hslash', 'mho']) def translate(s): tmp = s.lower() if tmp in greek or tmp in other: return "\\" + s if s in greek_translated: return "\\" + greek_translated[s] else: return s name = translate(name) if supers != []: supers = list(map(translate, supers)) if subs != []: subs = list(map(translate, subs)) # glue all items together: if len(supers) > 0: name += "^{%s}" % " ".join(supers) if len(subs) > 0: name += "_{%s}" % " ".join(subs) return name if expr in self._settings['symbol_names']: return self._settings['symbol_names'][expr] name_str = expr.name if '.' in name_str and name_str[0] == '(' and name_str[-1] == ')': name_str = name_str[1:-1] name_lst = name_str.split('.') name_str = r'\lp ' + str_symbol(name_lst[0]) + r'\cdot ' + str_symbol(name_lst[1]) + r'\rp ' return name_str return str_symbol(expr.name) def _print_Function(self, expr, exp=None): func = expr.func.__name__ name = func if hasattr(self, '_print_' + func): return getattr(self, '_print_' + func)(expr, exp) else: args = [str(self._print(arg)) for arg in expr.args] # How inverse trig functions should be displayed, formats are: # abbreviated: asin, full: arcsin, power: sin^-1 inv_trig_style = self._settings['inv_trig_style'] # If we are dealing with a power-style inverse trig function inv_trig_power_case = False # If it is applicable to fold the argument brackets can_fold_brackets = self._settings['fold_func_brackets'] and \ len(args) == 1 and \ not self._needs_function_brackets(expr.args[0]) inv_trig_table = ["asin", "acos", "atan", "acot"] # If the function is an inverse trig function, handle the style if func in inv_trig_table: if inv_trig_style == "abbreviated": func = func elif inv_trig_style == "full": func = "arc" + func[1:] elif inv_trig_style == "power": func = func[1:] inv_trig_power_case = True # Can never fold brackets if we're raised to a power if exp is not None: can_fold_brackets = False if inv_trig_power_case: if func in accepted_latex_functions: name = r"\%s^{-1}" % func else: name = r"\operatorname{%s}^{-1}" % func elif exp is not None: if func in accepted_latex_functions: name = r"\%s^{%s}" % (func, exp) else: name = latex(Symbol(func)) if '_' in func or '^' in func: name = r'{\lp ' + name + r'\rp }^{' + exp + '}' else: name += '^{' + exp + '}' else: if func in accepted_latex_functions: name = r"\%s" % func else: name = latex(Symbol(func)) if exp is not None: if '_' in name or '^' in name: name = r'\lp ' + name + r'\rp^{' + exp + '}' else: name += '^{' + exp + '}' if can_fold_brackets: if func in accepted_latex_functions: # Wrap argument safely to avoid parse-time conflicts # with the function name itself name += r" {%s}" else: if not GA_LatexPrinter.Fmode: name += r"%s" else: if func in accepted_latex_functions or not GA_LatexPrinter.Fmode: name += r"{\left (%s \right )}" if inv_trig_power_case and exp is not None: name += r"^{%s}" % exp if func in accepted_latex_functions or not GA_LatexPrinter.Fmode: if len(args) == 1: return name % args[0] else: return name % ",".join(args) else: return name def _print_Derivative(self, expr): dim = len(expr.variables) imax = 1 if dim == 1: if GA_LatexPrinter.Dmode: tex = r"\partial_{%s}" % self._print(expr.variables[0]) else: tex = r"\frac{\partial}{\partial %s}" % self._print(expr.variables[0]) else: multiplicity, i, tex = [], 1, "" current = expr.variables[0] for symbol in expr.variables[1:]: if symbol == current: i = i + 1 else: multiplicity.append((current, i)) current, i = symbol, 1 else: imax = max(imax, i) multiplicity.append((current, i)) if GA_LatexPrinter.Dmode and imax == 1: tmp = '' dim = 0 for x, i in multiplicity: dim += i tmp += r"%s" % (self._print(x), ) tex = r"\partial^{%s}_{" % (dim, ) + tmp + '}' else: for x, i in multiplicity: if i == 1: tex += r"\partial %s" % self._print(x) else: tex += r"\partial^{%s} %s" % (i, self._print(x)) tex = r"\frac{\partial^{%s}}{%s} " % (dim, tex) if isinstance(expr.expr, C.AssocOp): return r"%s\left(%s\right)" % (tex, self._print(expr.expr)) else: return r"%s %s" % (tex, self._print(expr.expr)) def _print_MV(self, expr): if expr.obj.is_zero: return '0 \n' else: if expr.print_blades: expr.base_to_blade() ostr = expr.get_latex_normal_order_str() return ostr def _print_Matrix(self, expr): lines = [] for line in range(expr. rows): # horrible, should be 'rows' lines.append(" & ".join([self._print(i) for i in expr[line, :]])) ncols = 0 for i in expr[line, :]: ncols += 1 out_str = '\\left [ \\begin{array}{' + ncols * 'c' + '} ' for line in lines[:-1]: out_str += line + ' \\\\ ' out_str += lines[-1] + ' \\end{array}\\right ] ' return out_str def latex(expr, **settings): "Return the LaTeX representation of the given expression." return GA_LatexPrinter(settings).doprint(expr) def print_latex(expr, **settings): "Print the LaTeX representation of the given expression." print(latex(expr, **settings)) def xdvi(filename=None, debug=False, paper=(14, 11)): """ Post processes LaTeX output (see comments below), adds preamble and postscript, generates tex file, inputs file to latex, displays resulting pdf file. """ if GA_LatexPrinter.ipy: GA_LatexPrinter.restore(GA_LatexPrinter.ipy) return sys_cmd = SYS_CMD[sys.platform] latex_str = sys.stdout.getvalue() GA_LatexPrinter.restore() latex_lst = latex_str.split('\n') latex_str = '' lhs = '' code_flg = False for latex_line in latex_lst: if len(latex_line) > 0 and '##' == latex_line[:2]: if code_flg: code_flg = False latex_line = latex_line[2:] else: code_flg = True latex_line = latex_line[2:] elif code_flg: pass elif len(latex_line) > 0 and '#' in latex_line: # Non equation mode output (comment) latex_line = latex_line.replace('#', '') if '%' in latex_line: # Equation mode with no variables to print (comment) latex_line = latex_line.replace('%', '') latex_line = '\\begin{equation*} ' + latex_line + ' \\end{equation*}\n' else: latex_line = latex_line.replace('.', r' \cdot ') # For components of metric tensor if '=' in latex_line: # determing lhs of equation/align eq_index = latex_line.rindex('=') + 1 lhs = latex_line[:eq_index] latex_line = latex_line.replace(lhs, '') if '%' in lhs: # Do not preprocess lhs of equation/align lhs = lhs.replace('%', '') else: # preprocess lhs of equation/align lhs = lhs.replace('|', r'\cdot ') lhs = lhs.replace('^', r'\W ') lhs = lhs.replace('*', ' ') lhs = lhs.replace('grad', r'\bm{\nabla} ') lhs = lhs.replace('<<', r'\rfloor ') lhs = lhs.replace('<', r'\rfloor ') lhs = lhs.replace('>>', r'\lfloor ') lhs = lhs.replace('>', r'\lfloor ') latex_line = lhs + latex_line if r'\begin{align*}' in latex_line: # insert lhs of align environment latex_line = latex_line = latex_line.replace(lhs, '') latex_line = latex_line.replace(r'\begin{align*}', r'\begin{align*} ' + lhs) lhs = '' else: # normal sympy equation latex_line = latex_line.strip() if len(latex_line) > 0: latex_line = '\\begin{equation*} ' + latex_line + ' \\end{equation*}' latex_str += latex_line + '\n' latex_str = latex_str.replace('\n\n', '\n') if debug: print(latex_str) if paper == 'letter': paper_size = \ """ \\documentclass[10pt,fleqn]{report} """ else: paper_size = \ """ \\documentclass[10pt,fleqn]{report} \\usepackage[vcentering]{geometry} """ paper_size += '\\geometry{papersize={' + str(paper[0]) + \ 'in,' + str(paper[1]) + 'in},total={' + str(paper[0] - 1) + \ 'in,' + str(paper[1] - 1) + 'in}}\n' latex_str = paper_size + GA_LatexPrinter.preamble + latex_str + GA_LatexPrinter.postscript if filename is None: pyfilename = sys.argv[0] rootfilename = pyfilename.replace('.py', '') filename = rootfilename + '.tex' print('latex file =', filename) latex_file = open(filename, 'w') latex_file.write(latex_str) latex_file.close() latex_str = None pdflatex = find_executable('pdflatex') print('pdflatex path =', pdflatex) if pdflatex is not None: latex_str = 'pdflatex' else: return if latex_str is not None: if debug: # Display latex excution output for debugging purposes os.system(latex_str + ' ' + filename[:-4]) else: # Works for Linux don't know about Windows os.system(latex_str + ' ' + filename[:-4] + sys_cmd['null']) print_cmd = sys_cmd['evince'] + ' ' + filename[:-4] + '.pdf ' + sys_cmd['&'] print(print_cmd) os.system(print_cmd) raw_input('!!!!Return to continue!!!!\n') if debug: os.system(sys_cmd['rm'] + ' ' + filename[:-4] + '.aux ' + filename[:-4] + '.log') else: os.system(sys_cmd['rm'] + ' ' + filename[:-4] + '.aux ' + filename[:-4] + '.log ' + filename[:-4] + '.tex') return prog_str = '' off_mode = False def Get_Program(off=False): global prog_str, off_mode off_mode = off if off_mode: return prog_file = open(sys.argv[0], 'r') prog_str = prog_file.read() prog_file.close() return def Print_Function(): global prog_str, off_mode if off_mode: return fct_name = str(sys._getframe(1).f_code.co_name) ifct = prog_str.find('def ' + fct_name) iend = prog_str.find('def ', ifct + 4) tmp_str = prog_str[ifct:iend - 1] fct_name = fct_name.replace('_', ' ') if GA_LatexPrinter.latex_flg: print('##\\begin{lstlisting}[language=Python,showspaces=false,' + 'showstringspaces=false,backgroundcolor=\color{gray},frame=single]') print(tmp_str) print('##\\end{lstlisting}') print('#Code Output:') else: print('\n' + 80 * '*') print(tmp_str) print('Code output:\n') return sympy-0.7.4.1/sympy/galgebra/ga.py0000644000175000017500000022761012253362407017142 0ustar georgeskgeorgesk# sympy/galgebra/ga.py """ ga.py implements the symbolic geometric algebra of an n-dimensional vector space with constant metric (future versions will allow for a metric that is a function of coordinates) with an arbitrary set of basis vectors (whether they are orthogonal or not depends on the metric the use inputs). All the products of geometric algebra - geometric outer (wedge) inner (dots) left contraction right contaction and the geometric derivative applied to all products from both sides. For more information on the details of geometric algebra please look at the documentation for the module. """ from __future__ import print_function from functools import reduce from itertools import combinations import copy import operator from sympy import Symbol, Expr, expand, Mul, Add, S, collect, \ Function, simplify, diff, trigsimp, sqrt, Number, \ factor_terms, sin, cos, sinh, cosh from sympy import N as Nsympy from sympy.galgebra.printing import GA_Printer, GA_LatexPrinter, enhance_print, latex from sympy.galgebra.vector import Vector from sympy.galgebra.debug import oprint from sympy.galgebra.stringarrays import fct_sym_array, str_combinations from sympy.galgebra.ncutil import linear_expand, bilinear_product, nc_substitue, \ get_commutative_coef, ONE_NC def diagpq(p, q=0): """ Returns string equivalent metric tensor for signature (p, q). """ n = p + q D = [] for i in range(p): D.append((i*'0 ' +'1 '+ (n-i-1)*'0 ')[:-1]) for i in range(p,n): D.append((i*'0 ' +'-1 '+ (n-i-1)*'0 ')[:-1]) return ','.join(D) def arbitrary_metric(n): """ Returns string equivalent metric tensor for arbitrary signature. """ return ','.join(n*[(n*'# ')[:-1]]) def arbitrary_metric_conformal(n): """ Returns string equivalent metric tensor for arbitrary signature (n+1,1). """ str1 = ','.join(n*[n*'# '+'0 0']) return ','.join([str1, n*'0 '+'1 0', n*'0 '+'0 -1']) def make_coef(self, coef_str): if self.fct: if self.vars is not None: return Function(coef_str)(*self.vars) elif MV.coords is not None: return Function(coef_str)(*MV.coords) else: return Symbol(coef_str) else: return Symbol(coef_str) class MV(object): """ 'MV' class wraps sympy expressions of the form s = s_0 + s_1*b_1 + ... + s_K*b_K where the s_i are real sympy scalars (commutative expressions) and the b_i are non-commutative sympy symbols. For an N-dimensional vector space K = 2**N - 1. The linear combination of scalar (commutative) sympy quatities and the basis multivectors form the multivector space. If the number of basis vectors is 'n' the dimension of the multivector space is 2**n. If the basis of the underlying vector space is (a_1,...,a_n) then the bases of the multivector space are the noncommunicative geometric products of the basis vectors of the form a_i1*a_i2*...*a_ir where i1 < i2 < ... < ir (normal order) and the scalar 1. A multivector space is the vector space with these bases over the sympy scalars. A basic assumption of the geometric product, '*', is that it is associative and that the geometric product of a vector with itself is a scalar. Thus we define for any two vectors - a.b = (a*b + b*a)/2 [1] (D&L 4.7) noting then that a.a = a*a, a.b = b.a, and that a.b is a scalar. The order of the geometric product of any two vectors can be reversed with - b*a = 2*(a.b) - a*b [2] (D&L 4.30) This is all that is required to reduce the geometric product of any number of basis vectors in any order to a linear combination of normal order basis vectors. Note that a dot product for these bases has not yet been defined and when it is the bases will not be orthogonal unless the basis vectors are orthogonal. The outer product of two vectors is defined to be - a^b = (a*b - b*a)/2 [3] (D&L 4.8) This is generalized by the formula a^R_k = (a*R_k + (-1)**k*R_k*a)/2 [4] (D&L 4.38) where R_k is the outer product of k vectors (k-blade) and equation [4] recursively defines the outer product of k + 1 vectors in terms of the linear combination of geometric products of terms with k + 1 and fewer vectors. D&L is "Geometric Algebra for Physicists" by Chris Doran and Anthony Lasenby, Cambridge University Press. """ ##########Methods for products (*,^,|) of orthogonal blades######### """ No multiplication tables (*,^,|) are calculated if the basis vectors are orthogonal. All types of products are calculated on the fly and the basis bases and blades are identical. """ latex_flg = False dot_mode = 's' # 's' - symmetric, 'l' - left contraction, 'r' - right contraction @staticmethod def product_orthogonal_blades(blade1, blade2): blade_index = list(MV.blade_to_index[blade1] + MV.blade_to_index[blade2]) repeats = [] sgn = 1 for i in range(1, len(blade_index)): save = blade_index[i] j = i while j > 0 and blade_index[j - 1] > save: sgn = -sgn blade_index[j] = blade_index[j - 1] j -= 1 blade_index[j] = save if blade_index[j] == blade_index[j - 1]: repeats.append(save) result = S(sgn) for i in repeats: blade_index.remove(i) blade_index.remove(i) result *= MV.metric[i] result *= MV.index_to_blade[tuple(blade_index)] return result @staticmethod def dot_orthogonal_blades(blade1, blade2): index1 = MV.blade_to_index[blade1] index2 = MV.blade_to_index[blade2] index = list(index1 + index2) grade1 = len(index1) grade2 = len(index2) if MV.dot_mode == 's': if grade1 == 0: return S.Zero elif grade2 == 0: return S.Zero else: grade = abs(grade1 - grade2) elif MV.dot_mode == 'l': grade = grade2 - grade1 if grade < 0: return S.Zero if grade1 == 0: return blade2 elif MV.dot_mode == 'r': grade = grade1 - grade2 if grade < 0: return S.Zero if grade2 == 0: return blade1 n = len(index) sgn = 1 result = S.One ordered = False while n > grade: ordered = True i2 = 1 while i2 < n: i1 = i2 - 1 index1 = index[i1] index2 = index[i2] if index1 == index2: n -= 2 if n < grade: return S.Zero result *= MV.metric[index1] index = index[:i1] + index[i2 + 1:] elif index1 > index2: ordered = False index[i1] = index2 index[i2] = index1 sgn = -sgn i2 += 1 else: i2 += 1 if ordered: break if n > grade: return S.Zero else: return sgn * result * MV. index_to_blade[tuple(index)] ######################Multivector Constructors###################### def __init__(self, base=None, mvtype=None, fct=False, blade_rep=False): """ Initialization of multivector X. Inputs are as follows mvtype base result default default Zero multivector 'basisvector' int i ith basis vector 'basisbivector' int i ith basis bivector 'scalar' x scalar of value x 's' 'grade' [A] X.grade(i) = A 's,i' 'vector' [A] X.grade(1) = [A] 's' 'grade2' or 'bivector' [A] X.grade(2) = A 's' 'pseudo' x X.grade(n) = x 's' 'spinor' 's' spinor with coefficients s__indices and name s 'mv' 's' general multivector with s__indices and name s If fct is 'True' and MV.coords is defined in MV.setup then a multivector field of MV.coords is instantiated. Multivector data members are: obj - a sympy expression consisting of a linear combination of sympy scalars and bases/blades. blade_rep - 'True' if 'MV' representation is a blade expansion, 'False' if 'MV' representation is a base expansion. """ def make_scalar(self, base): # make a scalar (grade 0) if isinstance(base, str): if self.fct: self.obj = Function(base)(*MV.coords) * MV.ONE else: self.obj = make_coef(self, base) * MV.ONE else: self.obj = base * MV.ONE self.igrade = 0 self.blade_rep = True return self def make_vector(self, base): # make a vector (grade 1) if isinstance(base, str): if self.fct: base_lst = str_combinations(base, MV.coords, rank=1, mode='__') fct_lst = fct_sym_array(base_lst, MV.coords) self.obj = reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[1])))) else: if MV.coords is not None: base_lst = str_combinations(base, MV.coords, rank=1, mode='__') else: base_lst = str_combinations(base, MV.subscripts, rank=1, mode='__') fct_lst = fct_sym_array(base_lst, None) self.obj = reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[1])))) else: result = S.Zero for (coef, base) in zip(base, MV.blades[1]): result += coef * base self.obj = result self.igrade = 1 self.blade_rep = True return self def make_basisvector(self, base): raise NotImplementedError("Don't know how to compute basis vectors of class %" % self.__class__) def make_basisbivector(self, base): raise NotImplementedError("Don't know how to compute basis bivectors of class %" % self.__class__) def make_grade(self, base): # if base is 'A,n' then make a grade n multivector if isinstance(base, str): base_lst = base.split(',') base = base_lst[0] n = int(base_lst[1]) if self.fct: base_lst = str_combinations(base, MV.coords, rank=n, mode='__') fct_lst = fct_sym_array(base_lst, MV.coords) self.obj = reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[n])))) else: if MV.coords is not None: base_lst = str_combinations(base, MV.coords, rank=n, mode='__') else: base_lst = str_combinations(base, MV.subscripts, rank=n, mode='__') fct_lst = fct_sym_array(base_lst, None) self.obj = reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[n])))) else: raise TypeError('Cannot make_grade for base = %s' % base) self.igrade = n self.blade_rep = True return self def make_grade2(self, base): # grade 2 multivector if isinstance(base, str): if self.fct: base_lst = str_combinations(base, MV.coords, rank=2, mode='__') fct_lst = fct_sym_array(base_lst, MV.coords) self.obj = reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[2])))) else: if MV.coords is not None: base_lst = str_combinations(base, MV.coords, rank=2, mode='__') else: base_lst = str_combinations(base, MV.subscripts, rank=2, mode='__') fct_lst = fct_sym_array(base_lst, None) self.obj = reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[2])))) else: raise TypeError('!!!!Cannot make_grade2 for base = ' + str(base) + '!!!!\n') self.igrade = 2 self.blade_rep = True return self def make_pseudo(self, base): # multivector of grade MV.dim if isinstance(base, str): if self.fct: base_lst = str_combinations(base, MV.coords, rank=MV.dim, mode='__') fct_lst = fct_sym_array(base_lst, MV.coords) self.obj = reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[MV.dim])))) else: if MV.coords is not None: base_lst = str_combinations(base, MV.coords, rank=MV.dim, mode='__') else: base_lst = str_combinations(base, MV.subscripts, rank=MV.dim, mode='__') fct_lst = fct_sym_array(base_lst, None) self.obj = reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[MV.dim])))) else: raise TypeError('!!!!Cannot make_pseudo for base = ' + str(base) + '!!!!\n') self.igrade = MV.dim self.blade_rep = True return self def make_spinor(self, base): # multivector with all even grades if isinstance(base, str): if self.fct: self.obj = Function(base)(*MV.coords) * MV.ONE else: self.obj = Symbol(base) * MV.ONE for rank in range(2, MV.dim1, 2): if self.fct: base_lst = str_combinations(base, MV.coords, rank=rank, mode='__') fct_lst = fct_sym_array(base_lst, MV.coords) self.obj += reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[rank])))) else: if MV.coords is not None: base_lst = str_combinations(base, MV.coords, rank=rank, mode='__') else: base_lst = str_combinations(base, MV.subscripts, rank=rank, mode='__') fct_lst = fct_sym_array(base_lst, None) self.obj += reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[rank])))) else: raise TypeError('Cannot make_mv for base = %s' % base) self.igrade = -1 self.blade_rep = True return self def make_mv(self, base): if isinstance(base, str): if self.fct: self.obj = Function(base)(*MV.coords) * MV.ONE else: self.obj = Symbol(base) * MV.ONE for rank in range(1, MV.dim1): if self.fct: base_lst = str_combinations(base, MV.coords, rank=rank, mode='__') fct_lst = fct_sym_array(base_lst, MV.coords) self.obj += reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[rank])))) else: if MV.coords is not None: base_lst = str_combinations(base, MV.coords, rank=rank, mode='__') else: base_lst = str_combinations(base, MV.subscripts, rank=rank, mode='__') fct_lst = fct_sym_array(base_lst, None) self.obj += reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[rank])))) else: raise TypeError('!!!!Cannot make_mv for base = ' + str(base) + '!!!!\n') self.igrade = -1 self.blade_rep = True return self MVtypes = {'scalar': make_scalar, 'vector': make_vector, 'basisvector': make_basisvector, 'basisbivector': make_basisbivector, 'grade': make_grade, 'grade2': make_grade2, 'bivector': make_grade2, 'pseudo': make_pseudo, 'spinor': make_spinor, 'mv': make_mv} self.fct = fct self.is_base = False self.is_grad = False self.print_blades = MV.print_blades self.fmt = 1 if mvtype is None: if base in (None, S.Zero): # Default is zero multivector self.blade_rep = True self.obj = S.Zero self.igrade = 0 elif isinstance(base, str): # Base or blade basis multivector self.is_base = True if '*' in base: self.blade_rep = False self.igrade = -1 else: if '^' in base: self.blade_rep = True self.igrade = base.count('^') + 1 else: self.blade_rep = blade_rep self.igrade = 1 self.obj = Symbol(base, commutative=False) elif isinstance(base, MV): # Copy constructor self.blade_rep = base.blade_rep self.obj = base.obj self.igrade = base.igrade self.fct = base.fct self.is_base = base.is_base self.is_grad = base.is_grad elif isinstance(base, (Expr, Symbol)): # Gets properties of multivector from Expr if base.is_commutative: self.obj = base * MV.ONE self.blade_rep = True self.igrade = 0 else: if isinstance(base, (Add, Mul)): # Complex expression self = MV.characterize_expression(self, base) elif isinstance(base, Symbol): if not base.is_commutative: if base == MV.ONE: self.obj = base self.blade_rep = True self.igrade = 0 elif base in MV.blades_flat: # basis blade self.obj = base self.blade_rep = True self.igrade = MV.blade_grades[base] elif base in MV.bases_flat: # basis base self.obj = base self.blade_rep = False self.igrade = -1 else: raise ValueError('MV(' + str(base) + ') is not allowed in constructor\n' + 'non-commutative argument is not a base\n') else: # scalar sympy symbol self.obj = base * MV.ONE self.igrade = 0 self.blade_rep = True elif isinstance(base, Number): self.obj = base * MV.ONE self.igrade = 0 self.blade_rep = True else: # Preconfigured multivector types self = MVtypes[mvtype](self, base) def Fmt(self, fmt=1, title=None): self.fmt = fmt if title is not None: print(title + ' = ' + str(self)) return return self def __str__(self): if GA_LatexPrinter.latex_flg: Printer = GA_LatexPrinter else: Printer = GA_Printer self.discover_and_set_grade() self.obj = expand(self.obj).collect(MV.blades_flat) return Printer().doprint(self) ######################## Operator Definitions####################### def coef(self, base): (coefs, bases) = linear_expand(self.obj) if base.obj in bases: icoef = bases.index(base.obj) return coefs[icoef] else: return S.Zero def func(self, fct): (coefs, bases) = linear_expand(self.obj) result = S.Zero for (coef, base) in zip(coefs, bases): result += fct(coef) * base fself = MV(self) fself.obj = result return fself def __eq__(self, mv): if not isinstance(mv, MV): mv = MV(mv) if self.obj == mv.obj: return True else: return False def __neg__(self): # -self nself = MV(self) nself.obj = -self.obj return nself def __pos__(self): # +self return self def __add__(self, b): # self + b if isinstance(b, MV): self.base_to_blade() b.base_to_blade() self_add_b = MV.basic_add(self, b) self_add_b.discover_and_set_grade() else: self_add_b = MV(self) self_add_b.obj = self.obj + b * MV.ONE if self.igrade != 0: self_add_b.igrade = -1 return self_add_b def __radd__(self, b): # b + self b_add_self = MV(self) b_add_self.obj = b * MV.ONE + self.obj if self.igrade != 0: b_add_self.igrade = -1 return b_add_self def __add_ab__(self, b): # self += b selfb = MV() selfb.obj += b.obj return selfb def __sub__(self, b): # self - b if isinstance(b, MV): self.base_to_blade() b.base_to_blade() self_sub_b = MV.basic_sub(self, b) self_sub_b.discover_and_set_grade() else: self_sub_b = MV(self) self_sub_b.obj = self.obj - b * MV.ONE if self.igrade != 0: self_sub_b.igrade = -1 return self_sub_b def __sub_ab__(self, b): # self -= b selfb = MV() selfb.obj -= b.obj return selfb def __rsub__(self, b): # b - self b_sub_self = MV(self) b_sub_self.obj = b * MV.ONE - self.obj if self.igrade != 0: b_sub_self.igrade = -1 return b_sub_self def __mul__(self, b): # self*b if isinstance(b, MV): if self.is_grad: # left geometric derivative result = MV() if self.connection: for (coord, brecp, bnorm) in zip(self.coords, self.rcpr_bases_MV, self.tangent_norm): result += (brecp * b.diff(coord)) / bnorm if b.igrade > 0: result.obj += nc_substitue(b.obj, self.connection['left']) else: for (coord, brecp) in zip(self.coords, self.rcpr_bases_MV): result += brecp * b.diff(coord) return result elif b.is_grad: # right geometric derivative result = MV() if b.connection: for (coord, brecp, bnorm) in zip(b.coords, b.rcpr_bases_MV, b.tangent_norm): result += (self.diff(coord) * brecp) / bnorm if self.igrade > 0: result.obj += nc_substitue(self.obj, b.connection['right']) else: for (coord, brecp) in zip(b.coords, b.rcpr_bases_MV): result += self.diff(coord) * brecp return MV(result) else: if not MV.is_orthogonal: self.blade_to_base() b.blade_to_base() obj = bilinear_product(self.obj * b.obj, MV.geometric_product) self_mul_b = MV(obj) if not MV.is_orthogonal: self_mul_b.igrade = -1 self_mul_b.blade_rep = False self_mul_b.base_to_blade() self_mul_b.discover_and_set_grade() return self_mul_b else: if self.is_grad: result = MV() for (coord, brecp) in zip(self.coords, self.rcpr_bases_MV): result += brecp * diff(b, coord) return result else: self_mul_b = MV(self) self_mul_b.obj *= b return self_mul_b def __mul_ab__(self, b): # self *= b selfb = MV(self) selfb.obj *= b.obj return selfb def __rmul__(self, b): # b * self b_mul_self = MV(self) b_mul_self.obj = b * self.obj return b_mul_self def __div__(self, b): # self / b if not isinstance(b, MV): self_div_b = MV(self) self_div_b.obj = self.obj / b return self_div_b else: raise TypeError('No multivector division for divisor = ' + str(b) + '\n') __truediv__ = __div__ def __or__(self, b): # self | b if isinstance(b, MV): if self.is_grad: # left dot/div (inner) derivative result = MV() if b.igrade == 0: return result if self.connection: for (coord, brecp, bnorm) in zip(self.coords, self.rcpr_bases_MV, self.tangent_norm): result += (brecp | b.diff(coord)) / bnorm result.obj += nc_substitue(b.obj, self.connection['left_dot']) else: for (coord, brecp) in zip(self.coords, self.rcpr_bases_MV): result += brecp | b.diff(coord) return result elif b.is_grad: # right dot/div (inner) derivative result = MV() if b.connection: for (coord, brecp, bnorm) in zip(b.coords, b.rcpr_bases_MV, b.tangent_norm): result += (self.diff(coord) | brecp) / bnorm result.obj += nc_substitue(self.obj, b.connection['right_dot']) else: for (coord, brecp) in zip(b.coords, b.rcpr_bases_MV): result += self.diff(coord) | brecp return MV(result) else: if MV.is_orthogonal: MV.dot_mode = 's' result = bilinear_product(self.obj * b.obj, MV.dot_orthogonal_blades) return MV(result) else: return MV.non_orthogonal_products(self, b, mode='s') else: # dot product returns zero for r.h.s. scalar multiplier return MV() def __ror__(self, b): # b | self b_dot_self = MV() return b_dot_self def __lt__(self, b): # left contraction if isinstance(b, MV): if self.is_grad: # left derivative for left contraction result = MV() if self.connection: for (coord, brecp, bnorm) in zip(self.coords, self.rcpr_bases_MV, self.tangent_norm): result += (brecp < b.diff(coord)) / bnorm result.obj += nc_substitue(b.obj, self.connection['left_dot']) else: for (coord, brecp) in zip(self.coords, self.rcpr_bases_MV): result += brecp < b.diff(coord) return result elif b.is_grad: # right derivative for left contraction result = MV() if b.connection: for (coord, brecp, bnorm) in zip(b.coords, b.rcpr_bases_MV, b.tangent_norm): result += (self.diff(coord) < brecp) / bnorm result.obj += nc_substitue(self.obj, b.connection['right_dot']) else: for (coord, brecp) in zip(b.coords, b.rcpr_bases_MV): result += self.diff(coord) < brecp return MV(result) else: if MV.is_orthogonal: MV.dot_mode = 'l' result = bilinear_product(self.obj * b.obj, MV.dot_orthogonal_blades) return MV(result) else: return MV.non_orthogonal_products(self, b, mode='l') def __gt__(self, b): # right contraction if isinstance(b, MV): if self.is_grad: # left derivative for right contraction result = MV() if self.connection: for (coord, brecp, bnorm) in zip(self.coords, self.rcpr_bases_MV, self.tangent_norm): result += (brecp > b.diff(coord)) / bnorm result.obj += nc_substitue(b.obj, self.connection['left_dot']) else: for (coord, brecp) in zip(self.coords, self.rcpr_bases_MV): result += brecp > b.diff(coord) return result elif b.is_grad: # right derivative for right contraction result = MV() if b.connection: for (coord, brecp, bnorm) in zip(b.coords, b.rcpr_bases_MV, b.tangent_norm): result += (self.diff(coord) > brecp) / bnorm result.obj += nc_substitue(self.obj, b.connection['right_dot']) else: for (coord, brecp) in zip(b.coords, b.rcpr_bases_MV): result += self.diff(coord) > brecp return MV(result) else: if MV.is_orthogonal: MV.dot_mode = 'r' result = bilinear_product(self.obj * b.obj, MV.dot_orthogonal_blades) return MV(result) else: return MV.non_orthogonal_products(self, b, mode='r') def __xor__(self, b): # self ^ b if isinstance(b, MV): if self.is_grad: # left wedge/curl (outer) derivative result = MV() if self.connection: for (coord, brecp, bnorm) in zip(self.coords, self.rcpr_bases_MV, self.tangent_norm): result += (brecp ^ b.diff(coord)) / bnorm result.obj += nc_substitue(b.obj, self.connection['left_wedge']) else: for (coord, brecp) in zip(self.coords, self.rcpr_bases_MV): result += brecp ^ b.diff(coord) return result elif b.is_grad: # right wedge/curl (outer) derivative result = MV() if b.connection: for (coord, brecp, bnorm) in zip(b.coords, b.rcpr_bases_MV, b.tangent_norm): result += (self.diff(coord) ^ brecp) / bnorm result.obj += nc_substitue(self.obj, b.connection['right_wedge']) else: for (coord, brecp) in zip(b.coords, b.rcpr_bases_MV): result += self.diff(coord) ^ brecp return MV(result) else: if MV.is_orthogonal: result = bilinear_product(self.obj * b.obj, MV.wedge_product) return MV(result) else: return MV.non_orthogonal_products(self, b, mode='w') else: if self.is_grad: result = MV() for (coord, brecp) in zip(self.coords, self.rcpr_bases_MV): result += brecp * diff(b, coord) return result else: return self * b def __rxor__(self, b): # b ^ self b_W_self = MV(self) b_W_self.obj = b * self.obj return b_W_self def scalar(self): (coefs, blades) = linear_expand(self.obj) result = S.Zero for (coef, blade) in zip(coefs, blades): if MV.blade_grades[blade] == 0: result += coef return result def set_coef(self, igrade, ibase, value): if self.blade_rep: base = MV.blades[igrade][ibase] else: base = MV.bases[igrade][ibase] (coefs, bases) = linear_expand(self.obj) bases_lst = list(bases) # python 2.5 if base in bases: self.obj += (value - coefs[bases_lst.index(base)]) * base else: self.obj += value * base return def grade(self, igrade=0): if igrade > MV.dim: return MV() if self.igrade > -1: if self.igrade == igrade: return self else: return MV() else: (coefs, blades) = linear_expand(self.obj) result = S.Zero for (coef, blade) in zip(coefs, blades): if MV.blade_grades[blade] == igrade: result += coef * blade self_igrade = MV(result) self_igrade.igrade = igrade return self_igrade def get_grades(self): # grade decomposition of multivector self.base_to_blade() (coefs, bases) = linear_expand(self.obj) grades = {} for (coef, base) in zip(coefs, bases): igrade = MV.blade_grades[base] if igrade in grades: grades[igrade] += coef * base else: grades[igrade] = coef * base for key in grades: # convert sympy expression to multivector grade = MV(grades[key]) grade.blad_rep = True grade.igrade = key grades[key] = grade return grades def discover_and_set_grade(self): self.base_to_blade() (coefs, bases) = linear_expand(self.obj) old_grade = -1 first_flg = True for (coef, base) in zip(coefs, bases): igrade = MV.blade_grades[base] if igrade != old_grade and first_flg: first_flg = False old_grade = igrade elif igrade != old_grade: self.igrade = -1 return self.igrade = old_grade return def get_normal_order_str(self): self.obj = expand(self.obj) if self.blade_rep: self.obj = self.obj.collect(MV.blades_flat1) else: self.obj = self.obj.collect(MV.bases_flat1) terms = zip(*linear_expand(self.obj)) if self.blade_rep: terms = sorted(terms, key=lambda x: MV.blades_flat1_lst.index(x[1])) # Python 2.5 else: terms = sorted(terms, key=lambda x: MV.bases_flat1_lst.index(x[1])) # Python 2.5 o_str = '' first = True if self.fmt == 2: if terms[0][1] == MV.ONE: grade = 0 else: s = str(factor_terms(terms[0][0])) grade = max(s.count('^'), s.count('*')) + 1 for term in terms: if term[1] == MV.ONE: tmp = str(factor_terms(term[0])) else: if isinstance(term[0], Add): tmp = str(factor_terms(term[0])) tmp = '(' + tmp + ')*' + enhance_print.enhance_base(str(term[1])) else: coef_str = str(factor_terms(term[0])) if coef_str == '1': coef_str = '' elif coef_str == '-1': coef_str = '-' else: coef_str += '*' tmp = coef_str + enhance_print.enhance_base(str(term[1])) if first: first = False o_str += tmp else: nl = '' if self.fmt == 2: s = str(term[1]) new_grade = max(s.count('^'), s.count('*')) + 1 if new_grade > grade: nl = '\n' grade = new_grade if tmp[0] == '-': o_str += nl + ' - ' + tmp[1:] else: o_str += nl + ' + ' + tmp if self.fmt == 3: o_str += '\n' if o_str[-1] == '\n': o_str = o_str[:-1] return o_str def get_latex_normal_order_str(self): latex_sep = {'^': r'\W ', '*': ' '} def base_string(base_obj): base_str = GA_LatexPrinter.Basic__str__(base_obj) sep = '^' if '*' in base_str: sep = '*' base_lst = base_str.split(sep) lstr = r'\bm{' + latex(Symbol(base_lst[0])) for base in base_lst[1:]: lstr += latex_sep[sep] + latex(Symbol(base)) lstr += '}' return lstr self.obj = expand(self.obj) if self.blade_rep: self.obj = self.obj.collect(MV.blades_flat) else: self.obj = self.obj.collect(MV.bases_flat) terms = zip(*linear_expand(self.obj)) if self.blade_rep: bgrades = MV.blade_grades terms = sorted(terms, key=lambda x: MV.blades_flat1_lst.index(x[1])) # Python 2.5 else: bgrades = MV.base_grades terms = sorted(terms, key=lambda x: MV.bases_flat1_lst.index(x[1])) # Python 2.5 grades = [] bases = [] old_grade = -1 for term in terms: new_grade = bgrades[term[1]] if old_grade != new_grade: if old_grade > -1: grades.append(bases) bases = [] old_grade = new_grade bases.append(term) if len(bases) > 0: grades.append(bases) o_str = '' grade_strs = [] nbases = 0 for grade in grades: # create [grade[base]] list of base strings base_strs = [] for base in grade: nbases += 1 if base[1] == MV.ONE: base_str = latex(simplify(base[0])) else: if isinstance(base[0], Add): base_str = r'\left ( ' + latex(simplify(base[0])) + r'\right ) ' + base_string(base[1]) else: coef_str = latex(simplify(base[0])) if coef_str == '1': coef_str = '' elif coef_str == '-1': coef_str = '-' base_str = coef_str + base_string(base[1]) if base_str[0] != '-': base_str = '+' + base_str base_strs.append(base_str) grade_strs.append(base_strs) if grade_strs[0][0][0] == '+': grade_strs[0][0] = grade_strs[0][0][1:] o_str = '' ngrades = len(grade_strs) if (self.fmt == 2 and ngrades > 1) or (self.fmt == 3 and nbases > 1): o_str += '\\begin{align*} ' for base_strs in grade_strs: if self.fmt == 2 and ngrades > 1: o_str += ' & ' for base_str in base_strs: if self.fmt == 3 and nbases > 1: o_str += ' & ' + base_str + ' \\\\ ' else: o_str += base_str if self.fmt == 2 and ngrades > 1: o_str += ' \\\\ ' if (self.fmt == 2 and ngrades > 1) or (self.fmt == 3 and nbases > 1): o_str += '\\end{align*} \n' else: pass return o_str @staticmethod def characterize_expression(self, expr): (coefs, bases) = linear_expand(expr) self.obj = expr if not MV.is_orthogonal: if len(set(bases) & MV.bases_set) != 0: self.blade_rep = False self.igrade = -1 else: self.blade_rep = True self.igrade = 0 return self else: self.blade_rep = True self.igrade = -1 if self.blade_rep: self.igrade = MV.blade_grades[bases[0]] for base in bases[1:]: igrade = MV.blade_grades[base] if self.igrade != igrade: self.igrade = -1 break return self return self def db(self): print('(blade_rep,igrade,obj) =', self.blade_rep, self.igrade, self.obj) return ##########################Member Functions########################## def dd(self, v): (coefs, bases) = linear_expand(v.obj) dderiv = MV() for (coef, base) in zip(coefs, bases): dderiv += coef * self.diff(MV.dd_dict[base]) return dderiv def diff(self, var): dself = MV(self) dself.obj = diff(self.obj, var) return dself def simplify(self): (coefs, bases) = linear_expand(self.obj) obj = 0 for (coef, base) in zip(coefs, bases): coef = simplify(coef) obj += coef * base sself = MV(self) sself.obj = obj return sself def trigsimp(self, **kwargs): (coefs, bases) = linear_expand(self.obj) obj = 0 for (coef, base) in zip(coefs, bases): coef = trigsimp(coef, **kwargs) obj += coef * base ts_self = MV(self) ts_self.obj = obj return ts_self def exp(self, alpha=1, norm=0, mode='T'): if self.is_blade(): self_sq = (self * self).scalar() if mode == 'T': if norm == 0: norm = sqrt(-self_sq) return cos(alpha * norm) + sin(alpha * norm) * self / norm else: if norm == 0: norm = sqrt(self_sq) return cosh(alpha * norm) + sinh(alpha * norm) * self / norm else: raise TypeError('!!! ' + str(self) + ' is not a blade in member function "exp" !!!\n') def expand(self): xself = MV(self) xself.obj = expand(self.obj) return xself def factor(self): fself = MV(self) fself.obj = factor_terms(self.obj) return fself def subs(self, x): xsubs = self.obj.subs(x) return MV(xsubs) def collect(self, x): (coefs, bases) = linear_expand(self.obj) result = S.Zero for (coef, base) in zip(coefs, bases): result += collect(coef, x) * base return MV(result) def is_scalar(self): self.discover_and_set_grade() if self.igrade == 0: return True else: return False def is_blade(self): self_sq = self * self if self_sq.is_scalar(): return True return False def dual(self): dself = MV.I * self dself.discover_and_set_grade() return dself def even(self): if self.igrade > -1: if self.igrade % 2 == 0: return self else: return MV() else: (coefs, blades) = linear_expand(self.obj) result = S.Zero for (coef, blade) in zip(coefs, blades): if MV.blade_grades[blade] % 2 == 0: result += coef * blade return MV(result) def odd(self): if self.igrade > -1: if self.igrade % 2 == 1: return self else: return MV() else: (coefs, blades) = linear_expand(self.obj) result = S.Zero for (coef, blade) in zip(coefs, blades): if MV.blade_grades[blade] % 2 == 1: result += coef * blade return MV(result) def norm(self): norm_sq = self * self.rev() norm_sq.discover_and_set_grade() if norm_sq.igrade == 0: norm_sq = norm_sq.scalar() if isinstance(norm_sq, Number): norm = sqrt(abs(norm_sq)) else: norm = sqrt(abs(norm_sq)) return norm else: raise ValueError('In norm self*self.rev() = ' + str(norm_sq) + ' is not a scalar!\n') def norm2(self): norm_sq = self * self.rev() norm_sq.discover_and_set_grade() if norm_sq.igrade == 0: norm_sq = norm_sq.scalar() return norm_sq else: raise ValueError('In norm self*self.rev() = ' + str(norm_sq) + ' is not a scalar!\n') def rev(self): self.base_to_blade() (coefs, bases) = linear_expand(self.obj) result = S.Zero for (coef, base) in zip(coefs, bases): grade = MV.blade_grades[base] if grade < 2: result += coef * base else: sgn_pow = (grade * (grade - 1)) / 2 % 2 if sgn_pow == 1: result -= coef * base else: result += coef * base self_rev = MV() self_rev.obj = result self_rev.igrade = self.igrade self_rev.blade_rep = self.blade_rep self_rev.fct = self.fct self_rev.is_grad = self.is_grad self_rev.print_blades = MV.print_blades self_rev.obj = simplify(self_rev.obj) return self_rev def inv(self): self_rev = self.rev() norm = self * self_rev norm.obj = expand(norm.obj) norm.discover_and_set_grade() if norm.igrade == 0: return self_rev / norm.obj else: raise ValueError('Cannot take inv(A) since A*rev(A) = ' + str(norm) + ' is not a scalar.\n') #######################Reduce Combined Indexes###################### @staticmethod def reduce_basis_loop(blst): """ blst is a list of integers [i_{1},...,i_{r}] representing the geometric product of r basis vectors a_{{i_1}}*...*a_{{i_r}}. reduce_basis_loop searches along the list [i_{1},...,i_{r}] untill it finds i_{j} == i_{j+1} and in this case contracts the list, or if i_{j} > i_{j+1} it revises the list (~i_{j} means remove i_{j} from the list) Case 1: If i_{j} == i_{j+1}, return a_{i_{j}}**2 and [i_{1},..,~i_{j},~i_{j+1},...,i_{r}] Case 2: If i_{j} > i_{j+1}, return a_{i_{j}}.a_{i_{j+1}}, [i_{1},..,~i_{j},~i_{j+1},...,i_{r}], and [i_{1},..,i_{j+1},i_{j},...,i_{r}] """ nblst = len(blst) # number of basis vectors if nblst <= 1: return True # a scalar or vector is already reduced jstep = 1 while jstep < nblst: istep = jstep - 1 if blst[istep] == blst[jstep]: # basis vectorindex is repeated i = blst[istep] # save basis vector index if len(blst) > 2: blst = blst[:istep] + blst[jstep + 1:] # contract blst else: blst = [] if len(blst) <= 1 or jstep == nblst - 1: blst_flg = True # revision of blst is complete else: blst_flg = False # more revision needed return MV.metric[i, i], blst, blst_flg if blst[istep] > blst[jstep]: # blst not in normal order blst1 = blst[:istep] + blst[jstep + 1:] # contract blst a1 = MV.metric2[blst[jstep], blst[istep]] # coef of contraction blst = blst[:istep] + [blst[jstep]] + [blst[istep]] + blst[jstep + 1:] # revise blst if len(blst1) <= 1: blst1_flg = True # revision of blst is complete else: blst1_flg = False # more revision needed return a1, blst1, blst1_flg, blst jstep += 1 return True # revision complete, blst in normal order @staticmethod def reduce_basis(blst): """ Repetitively applies reduce_basis_loop to blst product representation until normal form is realized. """ if blst == []: # blst represents scalar blst_coef = [S.One] blst_expand = [[]] return blst_coef, blst_expand blst_expand = [blst] blst_coef = [S.One] blst_flg = [False] # reduce untill all blst revise flgs are True while not reduce(operator.and_, blst_flg): for i in range(len(blst_flg)): if not blst_flg[i]: # keep revising if revise flg is False tmp = MV.reduce_basis_loop(blst_expand[i]) if isinstance(tmp, bool): blst_flg[i] = tmp # revision of blst_expand[i] complete elif len(tmp) == 3: # blst_expand[i] contracted blst_coef[i] = tmp[0] * blst_coef[i] blst_expand[i] = tmp[1] blst_flg[i] = tmp[2] else: # blst_expand[i] revised blst_coef[i] = -blst_coef[i] # if revision force one more pass in case revision # causes repeated index previous to revised pair of # indexes blst_flg[i] = False blst_expand[i] = tmp[3] blst_coef.append(-blst_coef[i] * tmp[0]) blst_expand.append(tmp[1]) blst_flg.append(tmp[2]) new_blst_coef = [] new_blst_expand = [] for (coef, expand) in zip(blst_coef, blst_expand): if expand in new_blst_expand: i = new_blst_expand.index(expand) new_blst_coef[i] += coef else: new_blst_expand.append(expand) new_blst_coef.append(coef) return new_blst_coef, new_blst_expand ##########################Bases Construction######################## @staticmethod def symbol_product_bases(i1, i2): if i1 == (): if i2 == (): return S.One else: return MV.index_to_base[i2] else: if i2 == (): return MV.index_to_base[i1] index = list(i1 + i2) result = S.Zero (coefs, indexes) = MV.reduce_basis(index) for (coef, index) in zip(coefs, indexes): result += coef * MV.index_to_base[tuple(index)] return result @staticmethod def make_base_blade_symbol(ibase): if len(ibase) == 1: base_str = MV.basis_names[ibase[0]] return Symbol(base_str, commutative=False), base_str, \ Symbol(base_str, commutative=False), base_str else: base_str = '' blade_str = '' for index in ibase: vector_str = MV.basis_names[index] base_str += vector_str + '*' blade_str += vector_str + '^' base_str = base_str[:-1] blade_str = blade_str[:-1] return Symbol(base_str, commutative=False), base_str, \ Symbol(blade_str, commutative=False), blade_str ################Geometric, Wedge, and Dot Products################## @staticmethod def basic_geometric_product(obj1, obj2): """ basic_geometric_product assumes that mv1 and mv2 are both mulitvectors, not scalars and both are in the base and not the blade representation. No multivector flags are checked. This function is used to construct the blades from the bases. """ def mul_table(b1, b2): return MV.base_mul_table[(b1, b2)] obj12 = bilinear_product(obj1 * obj2, mul_table) return obj12 @staticmethod def geometric_product(b1, b2): """ geometric_product(b1, b2) calculates the geometric product of the multivectors b1 and b2 (b1*b2). """ if MV.is_orthogonal: return MV.product_orthogonal_blades(b1, b2) else: result = MV.base_mul_table[(b1, b2)] return result @staticmethod def basic_add(mv1, mv2): """ basic_add assummes that mv1 and mv2 are multivectors both in the base or blade representation. It sets no flags for the output and forms mv1.obj+mv2.obj. It is used to form the base expansion of the blades. """ obj = expand(mv1.obj + mv2.obj) return MV(obj) @staticmethod def basic_sub(mv1, mv2): """ basic_sub assummes that mv1 and mv2 are multivectors both in the base or blade representation. It sets no flags for the output and forms mv1.obj-mv2.obj. It is used to form the base expansion of the blades. """ obj = expand(mv1.obj - mv2.obj) return MV(obj) @staticmethod def dot_product(b1, b2): if MV.is_orthogonal: return MV.dot_orthogonal_blades(b1, b2) else: grade1 = MV.blade_grades[b1] grade2 = MV.blade_grades[b2] if MV.dot_mode == 's': # dot product return MV.blade_dot_table[(b1, b2)] elif MV.dot_mode == 'l': # left contraction grade = grade2 - grade1 elif MV.dot_mode == 'r': # right contraction grade = grade1 - grade2 if grade < 0: return MV() else: return MV.blade_dot_table[(b1, b2)] @staticmethod def wedge_product(b1, b2): i1 = MV.blade_to_index[b1] i2 = MV.blade_to_index[b2] i1_plus_i2 = list(i1 + i2) if len(i1_plus_i2) > MV.dim: return S.Zero (sgn, i1_W_i2) = MV.blade_reduce(i1_plus_i2) if sgn != 0: return sgn * MV.index_to_blade[tuple(i1_W_i2)] else: return S.Zero @staticmethod def blade_reduce(lst): sgn = 1 for i in range(1, len(lst)): save = lst[i] j = i while j > 0 and lst[j - 1] > save: sgn = -sgn lst[j] = lst[j - 1] j -= 1 lst[j] = save if lst[j] == lst[j - 1]: return 0, None return sgn, lst @staticmethod def non_orthogonal_products(mv1, mv2, mode='w'): if isinstance(mv1, MV) and isinstance(mv2, MV): # both sides are mv mv1_grades = mv1.get_grades() mv2_grades = mv2.get_grades() result = MV() for grade1 in mv1_grades: for grade2 in mv2_grades: if mode == 'w': # wedge product grade = grade1 + grade2 elif mode == 's': # dot product if grade1 == 0: grade = -1 elif grade2 == 0: grade = -1 else: grade = abs(grade1 - grade2) elif mode == 'l': # left contraction grade = grade2 - grade1 elif mode == 'r': # right contraction grade = grade1 - grade2 if grade >= 0 and grade <= MV.dim: mv1mv2 = mv1_grades[grade1] * mv2_grades[grade2] mv1mv2_grades = MV(mv1mv2).get_grades() if grade in mv1mv2_grades: result += mv1mv2_grades[grade] return result elif isinstance(mv1, MV): # rhs is sympy scalar if mode == 'w': # wedge product mv1mv2 = MV(mv1) mv1mv2.obj = mv2 * mv1.obj return mv1mv2 else: # dot product or contractions return MV() elif isinstance(mv2, MV): # lhs is sympy scalar mv1mv2 = MV(mv1) mv1mv2.obj = mv2 * mv1.obj return mv1mv2 else: # both sides are sympy scalars if mode == 'w': return MV(mv1 * mv2) else: return MV() ###################Blade Base conversion functions################## def blade_to_base(self): if MV.is_orthogonal: return if self.igrade == 0 or self.igrade == 1: return self if self.blade_rep: self.blade_rep = False self.obj = expand(self.obj) self.obj = self.obj.subs({S.One**2: S.One}) self.obj = simplify(self.obj.subs(MV.blade_expand)) return def base_to_blade(self): if MV.is_orthogonal: return if self.igrade == 0 or self.igrade == 1: return if not self.blade_rep: self.blade_rep = True self.obj = expand(self.obj) self.obj = self.obj.subs(MV.base_expand) self.obj = expand(self.obj) self.obj = simplify(self.obj) return @staticmethod def build_base_blade_arrays(debug): indexes = tuple(range(MV.dim)) MV.index = [()] for i in indexes: MV.index.append(tuple(combinations(indexes, i + 1))) MV.index = tuple(MV.index) # Set up base and blade and index arrays if not MV.is_orthogonal: MV.bases_flat = [] MV.bases = [MV.ONE] MV.base_to_index = {MV.ONE: ()} MV.index_to_base = {(): MV.ONE} MV.base_grades = {MV.ONE: 0} MV.base_grades[S.One] = 0 MV.blades = [MV.ONE] MV.blades_flat = [] MV.blade_grades = {MV.ONE: 0} MV.blade_grades[S.One] = 0 MV.blade_to_index = {MV.ONE: ()} MV.index_to_blade = {(): MV.ONE} ig = 1 # pseudo grade and grade index for igrade in MV.index[1:]: if not MV.is_orthogonal: bases = [] # base symbol array within pseudo grade blades = [] # blade symbol array within grade ib = 0 # base index within grade for ibase in igrade: # build base name string (base_sym, base_str, blade_sym, blade_str) = MV.make_base_blade_symbol(ibase) if not MV.is_orthogonal: bases.append(base_sym) MV.bases_flat.append(base_sym) blades.append(blade_sym) MV.blades_flat.append(blade_sym) base_index = MV.index[ig][ib] # Add to dictionarys relating symbols and indexes if not MV.is_orthogonal: MV.base_to_index[base_sym] = base_index MV.index_to_base[base_index] = base_sym MV.base_grades[base_sym] = ig MV.blade_to_index[blade_sym] = base_index MV.index_to_blade[base_index] = blade_sym MV.blade_grades[blade_sym] = ig ib += 1 ig += 1 if not MV.is_orthogonal: MV.bases.append(tuple(bases)) MV.blades.append(tuple(blades)) if not MV.is_orthogonal: MV.bases = tuple(MV.bases) MV.bases_flat = tuple(MV.bases_flat) MV.bases_flat1 = (MV.ONE, ) + MV.bases_flat MV.bases_set = set(MV.bases_flat[MV.dim:]) MV.bases_flat1_lst = list(MV.bases_flat1) # Python 2.5 MV.blades = tuple(MV.blades) MV.blades_flat = tuple(MV.blades_flat) MV.blades_flat1 = (MV.ONE, ) + MV.blades_flat MV.blades_set = set(MV.blades_flat[MV.dim:]) MV.blades_flat1_lst = list(MV.blades_flat1) # Python 2.5 if debug: if not MV.is_orthogonal: oprint('MV Class Global Objects:', None, 'index(tuple)', MV.index, 'bases(Symbol)', MV.bases, 'base_to_index(Symbol->tuple)', MV.base_to_index, 'index_to_base(tuple->Symbol)', MV.index_to_base, 'bases flat', MV.bases_flat, 'bases set', MV.bases_set, 'blades(Symbol)', MV.blades, 'blade_grades(int)', MV.blade_grades, 'blade_to_index(Symbol->tuple)', MV.blade_to_index, 'index_to_blade(tuple->Symbol)', MV.index_to_blade, 'blades flat', MV.blades_flat, 'blades set', MV.blades_set, dict_mode=True) else: oprint('MV Class Global Objects:', None, 'index(tuple)', MV.index, 'blades(Symbol)', MV.blades, 'blades flat', MV.blades_flat, 'blades set', MV.blades_set, 'blade_grades(int)', MV.blade_grades, 'blade_to_index(Symbol->tuple)', MV.blade_to_index, 'index_to_blade(tuple->Symbol)', MV.index_to_blade, dict_mode=True) return @staticmethod def build_base_mul_table(debug): # Calculate geometric product multiplication table for bases MV.base_mul_table = {(MV.ONE, MV.ONE): MV.ONE} for ig1 in MV.index[1:]: for ib1 in ig1: b1 = MV.index_to_base[ib1] MV.base_mul_table[(MV.ONE, b1)] = b1 MV.base_mul_table[(b1, MV.ONE)] = b1 for ig2 in MV.index[1:]: for ib2 in ig2: b2 = MV.index_to_base[ib2] b1b2 = MV.symbol_product_bases(ib1, ib2) key = (b1, b2) MV.base_mul_table[key] = simplify(b1b2) if debug: oprint('Geometric Product (*) Table for Bases', MV.base_mul_table, dict_mode=True) return @staticmethod def build_base_blade_expansion_tables(debug): # Expand blades in terms of bases MV.blade_expand = {} for (blade, base) in zip(MV.blades[1], MV.bases[1]): MV.blade_expand[blade] = base sgn = -S.One igrade = 2 while igrade <= MV.dim: for ibase in MV.index[igrade]: pre_index = (ibase[0], ) post_index = ibase[1:] a = MV.index_to_blade[pre_index] B = MV.index_to_blade[post_index] B_expand = MV.blade_expand[B] # a^B = (a*B+sgn*B*a)/2 if sgn == 1: result = MV.basic_geometric_product(a, B_expand) + MV.basic_geometric_product(B_expand, a) else: result = MV.basic_geometric_product(a, B_expand) - MV.basic_geometric_product(B_expand, a) MV.blade_expand[MV.index_to_blade[ibase]] = simplify(expand(result / S(2))) igrade += 1 sgn = -sgn if debug: oprint('Blade Expansion Table', MV.blade_expand, dict_mode=True) # Expand bases in terms of blades MV.base_expand = {} for (blade, base) in zip(MV.blades[1], MV.bases[1]): MV.base_expand[base] = blade ig = 2 while ig <= MV.dim: tmp_dict = {} for ib in MV.index[ig]: base = MV.index_to_base[ib] blade = MV.index_to_blade[ib] tmp = MV.blade_expand[blade] tmp = tmp.subs({base: -blade}) tmp = tmp.subs(MV.base_expand) tmp_dict[base] = simplify(expand(-tmp)) MV.base_expand.update(tmp_dict) ig += 1 if debug: oprint('Base Expansion Table', MV.base_expand, dict_mode=True) print('Test Blade Expansion:') for key in MV.blade_expand: test = MV.blade_expand[key].subs(MV.base_expand) print(str(key) + ' = ' + str(test)) print('Test Base Expansion:') for key in MV.base_expand: test = MV.base_expand[key].subs(MV.blade_expand) print(str(key) + ' = ' + str(test)) return @staticmethod def build_reciprocal_basis(debug): MV.I = MV(MV.blades_flat[-1]) MV.rcpr_norm = get_commutative_coef(simplify((MV.I * MV.I).obj)) duals = list(MV.blades_flat[-(MV.dim + 1): -1]) duals.reverse() sgn = 1 MV.rcpr_bases_MV = [] for dual in duals: recpv = sgn * MV(dual) * MV.I MV.rcpr_bases_MV.append(recpv) sgn = -sgn if debug: print('Reciprocal Norm =', MV.rcpr_norm) oprint('Reciprocal Basis', MV.rcpr_bases_MV) if MV.coords is not None: rcpr_bases_MV = [] MV.grad = MV() result = S.Zero for (coef, rbase) in zip(MV.coords, MV.rcpr_bases_MV): nbase_obj = rbase.obj / MV.rcpr_norm term = coef * rbase result += term rcpr_bases_MV.append(MV(nbase_obj)) MV.dd_dict = {} if MV.is_orthogonal: bases = MV.blades[1] else: bases = MV.bases[1] for (coord, base) in zip(MV.coords, bases): MV.dd_dict[base] = coord MV.rcpr_bases_MV = rcpr_bases_MV MV.grad.is_grad = True MV.grad.blade_rep = True MV.grad.igrade = 1 MV.grad.rcpr_bases_MV = tuple(rcpr_bases_MV) MV.grad.coords = MV.coords MV.grad.norm = MV.rcpr_norm MV.grad.connection = {} if debug: print('grad =', MV.grad) oprint('reciprocal bases', MV.rcpr_bases_MV) if debug and MV.coords is not None and not MV.is_orthogonal: print('Reciprocal Vector Test:') for v1 in MV.blades_MV: for (v2, rv2) in zip(MV.blades_MV, MV.rcpr_bases_MV): print(str(v1) + '|Reciprocal(' + str(v2) + ') = ' + str(simplify(expand((v1 | rv2).obj)) / MV.rcpr_norm)) print('I**2 =', MV.rcpr_norm) print('Grad Vector:', MV.grad) return @staticmethod def build_curvilinear_connection(debug): """ Vector.dtau_dict[basis vector symbol,coordinate symbol] = derivative of basis vector as sympy expression """ MV.connection = True MV.tangent_norm = Vector.norm MV.tangent_derivatives_MV = {} rcpr_bases_MV = [] for (rbase, norm) in zip(MV.rcpr_bases_MV, MV.tangent_norm): rcpr_bases_MV.append(rbase / norm) MV.rcpr_bases_MV = rcpr_bases_MV for key in Vector.dtau_dict.keys(): MV.tangent_derivatives_MV[key] = MV(Vector.dtau_dict[key]) if debug: oprint('Tangent Vector Derivatives', MV.tangent_derivatives_MV, dict_mode=True) MV.left_connection = {} MV.right_connection = {} MV.left_wedge_connection = {} MV.right_wedge_connection = {} MV.left_dot_connection = {} MV.right_dot_connection = {} for base in MV.blades[1]: right_result = MV() left_result = MV() for (coord, rblade) in zip(MV.coords, MV.rcpr_bases_MV): left_result += rblade * MV.tangent_derivatives_MV[(base, coord)] right_result += MV.tangent_derivatives_MV[(base, coord)] * rblade left_result.obj = expand(left_result.obj) right_result.obj = expand(right_result.obj) left_result.discover_and_set_grade() right_result.discover_and_set_grade() MV.left_connection[base] = left_result.obj MV.right_connection[base] = right_result.obj MV.left_wedge_connection[base] = left_result.grade(2).obj MV.right_wedge_connection[base] = right_result.grade(2).obj MV.left_dot_connection[base] = left_result.grade(0).obj MV.right_dot_connection[base] = right_result.grade(0).obj for grade in MV.blades[2:]: for blade in grade: index = MV.blade_to_index[blade] N = len(index) left_result = MV() right_result = MV() for (coord, rblade) in zip(MV.coords, MV.rcpr_bases_MV): tmp = MV() for i in range(N): i_pre = index[:i] i_dtan = index[i] i_post = index[i + 1:] base = MV.blades[1][i_dtan] tmp += (MV(MV.index_to_blade[i_pre]) ^ MV.tangent_derivatives_MV[(base, coord)]) ^ MV(MV.index_to_blade[i_post]) left_result += rblade * tmp right_result += tmp * rblade left_result.discover_and_set_grade() right_result.discover_and_set_grade() MV.left_connection[blade] = left_result.obj MV.right_connection[blade] = right_result.obj MV.left_wedge_connection[blade] = left_result.grade(N + 1).obj MV.right_wedge_connection[blade] = right_result.grade(N + 1).obj MV.left_dot_connection[blade] = left_result.grade(abs(N - 1)).obj MV.right_dot_connection[blade] = right_result.grade(abs(N - 1)).obj MV.connection = {'left': MV.left_connection, 'right': MV.right_connection, 'left_wedge': MV.left_wedge_connection, 'right_wedge': MV.right_wedge_connection, 'left_dot': MV.left_dot_connection, 'right_dot': MV.right_dot_connection} MV.grad.connection = MV.connection if debug: oprint('Left Mutlivector Connection', MV.left_connection, 'Right Mutlivector Connection', MV.right_connection, 'Left Wedge Mutlivector Connection', MV.left_wedge_connection, 'Right Wedge Mutlivector Connection', MV.right_wedge_connection, 'Left Dot Mutlivector Connection', MV.left_dot_connection, 'Right Dot Mutlivector Connection', MV.right_dot_connection, dict_mode=True) return @staticmethod def setup(basis, metric=None, coords=None, rframe=False, debug=False, curv=(None, None)): """ MV.setup() creates all the arrays and dictionaries required to construct, multiply, add, and differentiate multivectors in linear and curvilinear coordinate systems. The inputs to MV.setup() are as follows - basis: A string that defines the noncommutative symbols that represent the basis vectors of the underlying vector space of the multivector space. If the string consists of substrings separated by spaces or commas each substring will be the name of the basis vector symbol for example basis='e_x e_y e_z' or basis='i j k'. Another way to enter the basis symbols is to specify a base string with a list of subscript strings. This is done with the following notation so that 'e*x|y|z' is equivalent to 'e_x e_y e_z'. metric: rframe: coords: debug: curv: """ MV.print_blades = False MV.connection = False MV.ONE = ONE_NC MV.basis_vectors = Vector.setup(basis, metric=metric, coords=coords, curv=curv, debug=debug) MV.curv_norm = curv[1] MV.metric = Vector.metric MV.subscripts = Vector.subscripts MV.coords = Vector.coords MV.metric2 = 2 * Vector.metric MV.is_orthogonal = Vector.is_orthogonal MV.basis_names = [] for base in MV.basis_vectors: MV.basis_names.append(str(base)) if debug: oprint('Basis Names', MV.basis_names) MV.dim = len(MV.basis_vectors) MV.dim1 = MV.dim + 1 MV.build_base_blade_arrays(debug) if not MV.is_orthogonal: MV.build_base_mul_table(debug) MV.build_base_blade_expansion_tables(debug) MV.blades_MV = [] for b in MV.blades[1]: mv = MV() mv.obj = b mv.blade_rep = True mv.igrade = 1 MV.blades_MV.append(mv) MV.build_reciprocal_basis(debug) MV.blades_MV = tuple(MV.blades_MV) if curv != (None, None): MV.build_curvilinear_connection(debug) MV.print_blades = True MV.I = MV(MV.blades_flat[-1]) MV.Isq = simplify((MV.I * MV.I).scalar()) MV.Iinv = MV.I / MV.Isq if coords is not None: return MV.blades_MV + (MV.grad, ) else: return MV.blades_MV def Format(Fmode=True, Dmode=True, ipy=False): "Initialize the LaTeX printer with the given mode." GA_LatexPrinter.Dmode = Dmode GA_LatexPrinter.Fmode = Fmode GA_LatexPrinter.ipy = ipy MV.latex_flg = True GA_LatexPrinter.redirect(ipy) return def ga_print_on(): """ Turn on the galgebra-aware string printer. This function is intended for interactive use only. Use with GA_Printer(): xxx instead of ga_print_on() xxx ga_print_off() """ GA_Printer._on() return def ga_print_off(): """ Turn off the galgebra-aware string printer. This function is intended for interactive use only. See ga_print_on for the noninteractive technique. """ GA_Printer._off() return def DD(v, f): if isinstance(f, MV): return f.dd(v) sf = MV(f, 'scalar') return sf.dd(v) def Nga(x, prec=5): if isinstance(x, MV): Px = MV(x) Px.obj = Nsympy(x.obj, prec) return Px else: return Nsympy(x, prec) def Com(A, B): "Commutator of A and B divided by 2." return (A * B - B * A) / S(2) def inv(B): "Invert B if B*B.rev() is scalar." Bnorm = B * B.rev() if Bnorm.is_scalar(): invB = B.rev() / Bnorm.obj return invB else: raise TypeError('Cannot calculate inverse of ' + str(B) + ' since \n' + 'B*Brev() = ' + str(Bnorm) + ' is not a scalar.\n') def proj(B, A): "Project blade A on blade B." result = (A < B) * inv(B) result.trigsimp() return result def rotor(theta, n): n_sq = (n * n).obj if n_sq != S.One: n /= sqrt(n_sq) N = n.dual() R = cos(theta) + sin(theta) * N return R def rot(itheta, A): "Rotate blade A by angle itheta." theta = itheta.norm() i = itheta / theta result = (cos(theta / 2) - i * sin(theta / 2)) * A * (cos(theta / 2) + i * sin(theta / 2)) # result.trigsimp(recursive=True) #trigsimp doesn't work for half angle formulas return result def refl(B, A): "Reflect blade A in blade B." j = B.is_blade() k = A.is_blade() if j > -1 and k > -1: result = (-1)**(j * (k + 1)) * B * A * inv(B) result.trigsimp() return result else: raise ValueError('Can only reflect blades') def dual(M): return M * MV.Iinv def cross(M1, M2): return -MV.I * (M1 ^ M2) def ScalarFunction(TheFunction): return MV() + TheFunction def ReciprocalFrame(basis, mode='norm'): dim = len(basis) indexes = tuple(range(dim)) index = [()] for i in indexes[-2:]: index.append(tuple(combinations(indexes, i + 1))) MFbasis = [] for igrade in index[-2:]: grade = [] for iblade in igrade: blade = MV(1, 'scalar') for ibasis in iblade: blade ^= basis[ibasis] blade = blade.trigsimp(deep=True, recursive=True) grade.append(blade) MFbasis.append(grade) E = MFbasis[-1][0] E_sq = trigsimp((E * E).scalar(), deep=True, recursive=True) duals = copy.copy(MFbasis[-2]) duals.reverse() sgn = 1 rbasis = [] for dual in duals: recpv = (sgn * dual * E).trigsimp(deep=True, recursive=True) rbasis.append(recpv) sgn = -sgn if mode != 'norm': rbasis.append(E_sq) else: for i in range(dim): rbasis[i] = rbasis[i] / E_sq return tuple(rbasis) sympy-0.7.4.1/sympy/galgebra/precedence.py0000644000175000017500000001347012253362407020645 0ustar georgeskgeorgesk# sympy/galgebra/precedence.py """ precedence.py converts a string to a multivector expression where the user can control the precedence of the of the multivector operators so that one does not need to put parenthesis around every multivector operation. The default precedence used (high to low) is <,>, and | have an have the highest precedence, then comes ^, and finally *. """ from __future__ import print_function import re as regrep op_cntrct = regrep.compile(r'(([A-Za-z0-9\_\#]+)(\||<|>)([A-Za-z0-9\_\#]+))') op_wedge = regrep.compile(r'(([A-Za-z0-9\_\#]+)[\^]{1}([A-Za-z0-9\_\#]+)([\^]{1}([A-Za-z0-9\_\#]+))*)') ops = r'[\^\|\<\>]+' ops_search = regrep.compile(r'(\^|\||<|>)+') parse_paren_calls = 0 global_dict = {} op_dict = {} op_lst = [] OPS = {'<>|': r'(([A-Za-z0-9\_\#]+)(\||<|>)([A-Za-z0-9\_\#]+))', '^': r'(([A-Za-z0-9\_\#]+)[\^]{1}([A-Za-z0-9\_\#]+)([\^]{1}([A-Za-z0-9\_\#]+))*)', '*': r'(([A-Za-z0-9\_\#]+)[\*]{1}([A-Za-z0-9\_\#]+)([\*]{1}([A-Za-z0-9\_\#]+))*)'} def define_precedence(gd, op_ord='<>|,^,*'): # Default is Doran and Lasenby convention global global_dict, op_dict, op_lst global_dict = gd op_lst = op_ord.split(',') op_dict = {} for op in op_lst: op_dict[op] = regrep.compile(OPS[op]) return def contains_interval(interval1, interval2): # interval1 inside interval2 if interval1[0] > interval2[0] and interval1[1] < interval2[1]: return True else: return False def parse_paren(line): global parse_paren_calls parse_paren_calls += 1 if ('(' not in line) or (')' not in line): return [[[line]]] level = 0 max_level = 0 ich = 0 paren_lst = [] for ch in line: if ch == '(': level += 1 paren_lst.append([level, ich]) if ch == ')': if level < 1: raise ValueError('Mismathed Parenthesis in: ' + line + '\n') paren_lst.reverse() iparen = 0 for elem in paren_lst: if elem[0] == level: paren_lst[iparen].append(ich) break iparen += 1 paren_lst.reverse() level -= 1 max_level = max(max_level, level) ich += 1 if level != 0: raise ValueError('Mismatched Parenthesis in: ' + line + '\n') if max_level > 0: level_lst = [] for x in range(max_level + 1): level_lst.append([]) for group in paren_lst: level_lst[group[0]].append(group[1:]) ilevel = max_level while ilevel > 1: level = level_lst[ilevel] level_down = level_lst[ilevel - 1] igroup = 0 for group in level: igroup_down = 0 for group_down in level_down: if contains_interval(group, group_down): level_lst[ilevel][igroup].append(igroup_down) igroup_down += 1 igroup += 1 ilevel -= 1 ilevel = 1 for level in level_lst[1:]: igroup = 0 for group in level: token = '#' + str(parse_paren_calls) + '_' + str(ilevel) + '_' + str(igroup) + '#' level_lst[ilevel][igroup].append(line[group[0]:group[1] + 1]) level_lst[ilevel][igroup].append(token) igroup += 1 ilevel += 1 ilevel = 1 for level in level_lst[1:]: igroup = 0 for group in level: group.append(group[-2]) level_lst[ilevel][igroup] = group igroup += 1 ilevel += 1 ilevel = max_level while ilevel > 1: igroup = 0 for group in level_lst[ilevel]: group_down = level_lst[ilevel - 1][group[2]] replace_text = group_down[-1].replace(group[-3], group[-2]) level_lst[ilevel - 1][group[2]][-1] = replace_text igroup += 1 ilevel -= 1 for group in level_lst[1]: line = line.replace(group[2], group[3]) ilevel = 1 level_lst[0] = [[line]] return level_lst def unparse_paren(level_lst): line = level_lst[0][0][0] for level in level_lst[1:]: for group in level: new_string = group[-1] if new_string[:2] == '((' and new_string[-2:] == '))': new_string = new_string[1:-1] line = line.replace(group[-2], new_string) return line def sub_paren(s): string = s.group(0) return '(%s)' % string def add_paren(line, re_exprs): paren_flg = False if (line[0] == '(') and (line[-1] == ')'): paren_flg = True line = line[1:-1] if ('(' in line) or (')' in line): line_levels = parse_paren(line) ilevel = 0 for level in line_levels: igroup = 0 for group in level: group[-1] = regrep.sub(re_exprs, sub_paren, group[-1]) line_levels[ilevel][igroup] = group igroup += 1 ilevel += 1 line = unparse_paren(line_levels) else: line = regrep.sub(re_exprs, sub_paren, line) if paren_flg: line = '(' + line + ')' return line def parse_line(line): global op_lst, op_dict line = line.replace(' ', '') level_lst = parse_paren(line) ilevel = 0 for level in level_lst: igroup = 0 for group in level: string = group[-1] for op in op_lst: string = add_paren(string, op_dict[op]) level_lst[ilevel][igroup][-1] = string igroup += 1 ilevel += 1 line = unparse_paren(level_lst) return line def GAeval(s, pstr=False): seval = parse_line(s) if pstr: print(s) print(seval) return eval(seval, global_dict) sympy-0.7.4.1/sympy/galgebra/tests/0000755000175000017500000000000012253362407017333 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/galgebra/tests/test_ga.py0000644000175000017500000006026712253362407021346 0ustar georgeskgeorgesk# sympy/galgebra/tests/test_ga.py """ The reference D&L is "Geometric Algebra for Physicists" by Doran and Lasenby """ from sympy.core import expand, Rational, S, Symbol, symbols from sympy.functions import sin, cos from sympy.galgebra.ga import MV, Nga, Com from sympy.galgebra.printing import GA_Printer from sympy.matrices import Matrix from sympy.simplify import collect, simplify from sympy.utilities.pytest import XFAIL def F(x, n, nbar): """ Conformal Mapping Function from 3D Euclidean space to 5D conformal space where the images of all maps are null vectors. """ return Rational(1, 2)*((x*x)*n + 2*x - nbar) def make_vector(a, m=3): global n, nbar if isinstance(a, str): sym_str = '' for i in range(m): sym_str += a + str(i + 1) + ' ' sym_lst = list(symbols(sym_str)) sym_lst.append(S.Zero) sym_lst.append(S.Zero) a = MV(sym_lst, 'vector') return F(a, n, nbar) def test_rmul(): """ Test for commutative scalar multiplication. Leftover from when sympy and numpy were not working together and __mul__ and __rmul__ would not give the same answer. """ x, y, z = MV.setup('x y z') a, b, c = symbols('a b c') assert 5*x == x*5 assert Rational(1, 2)*x == x*Rational(1, 2) assert a*x == x*a def test_contraction(): """ Test for inner product and left and right contraction """ e_1, e_2, e_3 = MV.setup('e_1 e_2 e_3', '1 0 0, 0 1 0, 0 0 1') assert ((e_1 ^ e_3) | e_1) == -e_3 assert ((e_1 ^ e_3) > e_1) == -e_3 assert (e_1 | (e_1 ^ e_3)) == e_3 assert (e_1 < (e_1 ^ e_3)) == e_3 assert ((e_1 ^ e_3) < e_1) == 0 assert (e_1 > (e_1 ^ e_3)) == 0 def test_substitution(): e_x, e_y, e_z = MV.setup('e_x e_y e_z', '1 0 0, 0 1 0, 0 0 1') x, y, z = symbols('x y z') X = x*e_x + y*e_y + z*e_z Y = X.subs([(x, 2), (y, 3), (z, 4)]) assert Y == 2*e_x + 3*e_y + 4*e_z def test_vector_extraction(): """ Show that conformal bivector encodes two points. See D&L Section 10.4.1 """ metric = ' 0 -1 #,' + \ '-1 0 #,' + \ ' # # #,' P1, P2, a = MV.setup('P1 P2 a', metric) """ P1 and P2 are null vectors and hence encode points in conformal space. Show that P1 and P2 can be extracted from the bivector B = P1^P2. a is a third vector in the conformal space with a.B not 0. """ B = P1 ^ P2 Bsq = B*B ap = a - (a ^ B)*B Ap = ap + ap*B Am = ap - ap*B P1dota = Symbol('(P1.a)') P2dota = Symbol('(P2.a)') Ap_test = (-2*P2dota)*P1 Am_test = (-2*P1dota)*P2 assert Ap == Ap_test assert Am == Am_test Ap2 = Ap*Ap Am2 = Am*Am assert Ap2 == S.Zero assert Am2 == S.Zero def test_metrics(): """ Test specific metrics (diagpq, arbitrary_metric, arbitrary_metric_conformal) """ from sympy.galgebra.ga import diagpq, arbitrary_metric, arbitrary_metric_conformal metric = diagpq(3) p1, p2, p3 = MV.setup('p1 p2 p3', metric, debug=0) x1, y1, z1 = symbols('x1 y1 z1') x2, y2, z2 = symbols('x2 y2 z2') v1 = x1*p1 + y1*p2 + z1*p3 v2 = x2*p1 + y2*p2 + z2*p3 prod1 = v1*v2 prod2 = (v1|v2) + (v1^v2) diff = prod1 - prod2 assert diff == MV(S.Zero) metric = arbitrary_metric(3) p1, p2, p3 = MV.setup('p1 p2 p3', metric, debug=0) v1 = x1*p1 + y1*p2 + z1*p3 v2 = x2*p1 + y2*p2 + z2*p3 prod1 = v1*v2 prod2 = (v1|v2) + (v1^v2) diff = prod1 - prod2 assert diff == MV(S.Zero) @XFAIL def test_metrics_xfail(): from sympy.galgebra.ga import arbitrary_metric_conformal metric = arbitrary_metric_conformal(3) p1, p2, p3 = MV.setup('p1 p2 p3', metric, debug=0) v1 = x1*p1 + y1*p2 + z1*p3 v2 = x2*p1 + y2*p2 + z2*p3 prod1 = v1*v2 prod2 = (v1|v2) + (v1^v2) diff = prod1 - prod2 assert diff == MV(S.Zero) def test_geometry(): """ Test conformal geometric description of circles, lines, spheres, and planes. """ metric = '1 0 0 0 0,' + \ '0 1 0 0 0,' + \ '0 0 1 0 0,' + \ '0 0 0 0 2,' + \ '0 0 0 2 0' e0, e1, e2, n, nbar = MV.setup('e0 e1 e2 n nbar', metric, debug=0) e = n + nbar #conformal representation of points A = F(e0, n, nbar) # point a = (1,0,0) A = F(a) B = F(e1, n, nbar) # point b = (0,1,0) B = F(b) C = F(-1*e0, n, nbar) # point c = (-1,0,0) C = F(c) D = F(e2, n, nbar) # point d = (0,0,1) D = F(d) x0, x1, x2 = symbols('x0 x1 x2') X = F(MV([x0, x1, x2], 'vector'), n, nbar) Circle = A ^ B ^ C ^ X Line = A ^ B ^ n ^ X Sphere = A ^ B ^ C ^ D ^ X Plane = A ^ B ^ n ^ D ^ X #Circle through a, b, and c Circle_test = -x2*(e0 ^ e1 ^ e2 ^ n) + x2*( e0 ^ e1 ^ e2 ^ nbar) + Rational(1, 2)*(-1 + x0**2 + x1**2 + x2**2)*(e0 ^ e1 ^ n ^ nbar) diff = Circle - Circle_test assert diff == S.Zero #Line through a and b Line_test = -x2*(e0 ^ e1 ^ e2 ^ n) + \ Rational(1, 2)*(-1 + x0 + x1)*(e0 ^ e1 ^ n ^ nbar) + \ (Rational(1, 2)*x2)*(e0 ^ e2 ^ n ^ nbar) + \ (-Rational(1, 2)*x2)*(e1 ^ e2 ^ n ^ nbar) diff = Line - Line_test assert diff == S.Zero #Sphere through a, b, c, and d Sphere_test = Rational(1, 2)*(1 - x0**2 - x1**2 - x2**2)*(e0 ^ e1 ^ e2 ^ n ^ nbar) diff = Sphere - Sphere_test assert diff == S.Zero #Plane through a, b, and d Plane_test = Rational(1, 2)*(1 - x0 - x1 - x2)*(e0 ^ e1 ^ e2 ^ n ^ nbar) diff = Plane - Plane_test assert diff == S.Zero def test_extract_plane_and_line(): """ Show that conformal trivector encodes planes and lines. See D&L section 10.4.2 """ metric = '# # # 0 0,' + \ '# # # 0 0,' + \ '# # # 0 0,' + \ '0 0 0 0 2,' + \ '0 0 0 2 0' p1, p2, p3, n, nbar = MV.setup('p1 p2 p3 n nbar', metric, debug=0) P1 = F(p1, n, nbar) P2 = F(p2, n, nbar) P3 = F(p3, n, nbar) #Line through p1 and p2 L = P1 ^ P2 ^ n delta = (L | n) | nbar delta_test = 2*p1 - 2*p2 diff = delta - delta_test assert diff == S.Zero #Plane through p1, p2, and p3 C = P1 ^ P2 ^ P3 delta = ((C ^ n) | n) | nbar delta_test = 2*(p1 ^ p2) - 2*(p1 ^ p3) + 2*(p2 ^ p3) diff = delta - delta_test assert diff == S.Zero @XFAIL def test_reciprocal_frame(): """ Test of formula for general reciprocal frame of three vectors. Let three independent vectors be e1, e2, and e3. The reciprocal vectors E1, E2, and E3 obey the relations: e_i.E_j = delta_ij*(e1^e2^e3)**2 """ metric = '1 # #,' + \ '# 1 #,' + \ '# # 1,' e1, e2, e3 = MV.setup('e1 e2 e3', metric) E = e1 ^ e2 ^ e3 Esq = (E*E)() Esq_inv = 1/Esq E1 = (e2 ^ e3)*E E2 = (-1)*(e1 ^ e3)*E E3 = (e1 ^ e2)*E w = (E1 | e2) w.collect(MV.g) w = w().expand() w = (E1 | e3) w.collect(MV.g) w = w().expand() assert w == 0 w = (E2 | e1) w.collect(MV.g) w = w().expand() assert w == 0 w = (E2 | e3) w.collect(MV.g) w = w().expand() assert w == 0 w = (E3 | e1) w.collect(MV.g) w = w().expand() assert w == 0 w = (E3 | e2) w.collect(MV.g) w = w().expand() assert w == 0 w = (E1 | e1) w = w().expand() Esq = Esq.expand() assert w/Esq == 1 w = (E2 | e2) w = w().expand() assert w/Esq == 1 w = (E3 | e3) w = w().expand() assert w/Esq == 1 @XFAIL def test_derivative(): coords = x, y, z = symbols('x y z') e_x, e_y, e_z, _ = MV.setup('e', '1 0 0, 0 1 0, 0 0 1', coords=coords) X = x*e_x + y*e_y + z*e_z a = MV('a', 'vector') assert ((X | a).grad()) == a assert ((X*X).grad()) == 2*X assert (X*X*X).grad() == 5*X*X assert X.grad_int() == 3 @XFAIL def test_str(): e_1, e_2, e_3 = MV.setup('e_1 e_2 e_3', '1 0 0, 0 1 0, 0 0 1') X = MV('x') assert str(X) == 'x + x__1*e_1 + x__2*e_2 + x__3*e_3 + x__12*e_1^e_2 + x__13*e_1^e_3 + x__23*e_2^e_3 + x__123**e_1^e_2^e_3' Y = MV('y', 'spinor') assert str(Y) == 'y + y__12*e_1^e_2 + y__13*e_1^e_3 + y__23*e_2^e_3' Z = X + Y assert str(Z) == 'x + y + x__1*e_1 + x__2*e_2 + x__3*e_3 + (x__12 + y__12)*e_1^e_2 + (x__13 + y__13)*e_1^e_3 + (x__23 + y__23)*e_2^e_3 + x__123*e_1^e_2^e_3' assert str(e_1 | e_1) == '1' @XFAIL def test_metric(): MV.setup('e_1 e_2 e_3', '[1,1,1]') assert MV.metric == Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) @XFAIL def test_constructor(): """ Test various multivector constructors """ e_1, e_2, e_3 = MV.setup('e_1 e_2 e_3', '[1,1,1]') assert str(MV('a', 'scalar')) == 'a' assert str(MV('a', 'vector')) == 'a__1*e_1 + a__2*e_2 + a__3*e_3' assert str(MV('a', 'pseudo')) == 'a__123*e_1^e_2^e_3' assert str(MV('a', 'spinor')) == 'a + a__12*e_1^e_2 + a__13*e_1^e_3 + a__23*e_2^e_3' assert str(MV('a')) == 'a + a__1*e_1 + a__2*e_2 + a__3*e_3 + a__12*e_1^e_2 + a__13*e_1^e_3 + a__23*e_2^e_3 + a__123*e_1^e_2^e_3' assert str(MV([2, 'a'], 'grade')) == 'a__12*e_1^e_2 + a__13*e_1^e_3 + a__23*e_2^e_3' assert str(MV('a', 'grade2')) == 'a__12*e_1^e_2 + a__13*e_1^e_3 + a__23*e_2^e_3' def test_basic_multivector_operations(): with GA_Printer(): (ex, ey, ez) = MV.setup('e*x|y|z') A = MV('A', 'mv') assert str(A) == 'A + A__x*e_x + A__y*e_y + A__z*e_z + A__xy*e_x^e_y + A__xz*e_x^e_z + A__yz*e_y^e_z + A__xyz*e_x^e_y^e_z' assert str(A) == 'A + A__x*e_x + A__y*e_y + A__z*e_z + A__xy*e_x^e_y + A__xz*e_x^e_z + A__yz*e_y^e_z + A__xyz*e_x^e_y^e_z' assert str(A) == 'A + A__x*e_x + A__y*e_y + A__z*e_z + A__xy*e_x^e_y + A__xz*e_x^e_z + A__yz*e_y^e_z + A__xyz*e_x^e_y^e_z' X = MV('X', 'vector') Y = MV('Y', 'vector') assert str(X) == 'X__x*e_x + X__y*e_y + X__z*e_z' assert str(Y) == 'Y__x*e_x + Y__y*e_y + Y__z*e_z' assert str((X*Y)) == '(e_x.e_x)*X__x*Y__x + (e_x.e_y)*X__x*Y__y + (e_x.e_y)*X__y*Y__x + (e_x.e_z)*X__x*Y__z + (e_x.e_z)*X__z*Y__x + (e_y.e_y)*X__y*Y__y + (e_y.e_z)*X__y*Y__z + (e_y.e_z)*X__z*Y__y + (e_z.e_z)*X__z*Y__z + (X__x*Y__y - X__y*Y__x)*e_x^e_y + (X__x*Y__z - X__z*Y__x)*e_x^e_z + (X__y*Y__z - X__z*Y__y)*e_y^e_z' assert str((X ^ Y)) == '(X__x*Y__y - X__y*Y__x)*e_x^e_y + (X__x*Y__z - X__z*Y__x)*e_x^e_z + (X__y*Y__z - X__z*Y__y)*e_y^e_z' assert str((X | Y)) == '(e_x.e_x)*X__x*Y__x + (e_x.e_y)*X__x*Y__y + (e_x.e_y)*X__y*Y__x + (e_x.e_z)*X__x*Y__z + (e_x.e_z)*X__z*Y__x + (e_y.e_y)*X__y*Y__y + (e_y.e_z)*X__y*Y__z + (e_y.e_z)*X__z*Y__y + (e_z.e_z)*X__z*Y__z' (ex, ey) = MV.setup('e*x|y') X = MV('X', 'vector') A = MV('A', 'spinor') assert str(X) == 'X__x*e_x + X__y*e_y' assert str(A) == 'A + A__xy*e_x^e_y' assert str((X | A)) == '(-A__xy*((e_x.e_y)*X__x + (e_y.e_y)*X__y))*e_x + (A__xy*((e_x.e_x)*X__x + (e_x.e_y)*X__y))*e_y' assert str((X < A)) == '(-A__xy*((e_x.e_y)*X__x + (e_y.e_y)*X__y))*e_x + (A__xy*((e_x.e_x)*X__x + (e_x.e_y)*X__y))*e_y' assert str((A > X)) == '(A__xy*((e_x.e_y)*X__x + (e_y.e_y)*X__y))*e_x + (-A__xy*((e_x.e_x)*X__x + (e_x.e_y)*X__y))*e_y' (ex, ey) = MV.setup('e*x|y', metric='[1,1]') X = MV('X', 'vector') A = MV('A', 'spinor') assert str(X) == 'X__x*e_x + X__y*e_y' assert str(A) == 'A + A__xy*e_x^e_y' assert str((X*A)) == '(A*X__x - A__xy*X__y)*e_x + (A*X__y + A__xy*X__x)*e_y' assert str((X | A)) == '-A__xy*X__y*e_x + A__xy*X__x*e_y' assert str((X < A)) == '-A__xy*X__y*e_x + A__xy*X__x*e_y' assert str((X > A)) == 'A*X__x*e_x + A*X__y*e_y' assert str((A*X)) == '(A*X__x + A__xy*X__y)*e_x + (A*X__y - A__xy*X__x)*e_y' assert str((A | X)) == 'A__xy*X__y*e_x - A__xy*X__x*e_y' assert str((A < X)) == 'A*X__x*e_x + A*X__y*e_y' assert str((A > X)) == 'A__xy*X__y*e_x - A__xy*X__x*e_y' return def test_check_generalized_BAC_CAB_formulas(): with GA_Printer(): (a, b, c, d, e) = MV.setup('a b c d e') assert str(a | (b*c)) == '-(a.c)*b + (a.b)*c' assert str(a | (b ^ c)) == '-(a.c)*b + (a.b)*c' assert str(a | (b ^ c ^ d)) == '(a.d)*b^c - (a.c)*b^d + (a.b)*c^d' assert str((a | (b ^ c)) + (c | (a ^ b)) + (b | (c ^ a))) == '0' assert str(a*(b ^ c) - b*(a ^ c) + c*(a ^ b)) == '3*a^b^c' assert str(a*(b ^ c ^ d) - b*(a ^ c ^ d) + c*(a ^ b ^ d) - d*(a ^ b ^ c)) == '4*a^b^c^d' assert str((a ^ b) | (c ^ d)) == '-(a.c)*(b.d) + (a.d)*(b.c)' assert str(((a ^ b) | c) | d) == '-(a.c)*(b.d) + (a.d)*(b.c)' assert str(Com(a ^ b, c ^ d)) == '-(b.d)*a^c + (b.c)*a^d + (a.d)*b^c - (a.c)*b^d' assert str((a | (b ^ c)) | (d ^ e)) == '(-(a.b)*(c.e) + (a.c)*(b.e))*d + ((a.b)*(c.d) - (a.c)*(b.d))*e' return def test_derivatives_in_rectangular_coordinates(): with GA_Printer(): X = (x, y, z) = symbols('x y z') (ex, ey, ez, grad) = MV.setup('e_x e_y e_z', metric='[1,1,1]', coords=X) f = MV('f', 'scalar', fct=True) A = MV('A', 'vector', fct=True) B = MV('B', 'grade2', fct=True) C = MV('C', 'mv', fct=True) assert str(f) == 'f' assert str(A) == 'A__x*e_x + A__y*e_y + A__z*e_z' assert str(B) == 'B__xy*e_x^e_y + B__xz*e_x^e_z + B__yz*e_y^e_z' assert str(C) == 'C + C__x*e_x + C__y*e_y + C__z*e_z + C__xy*e_x^e_y + C__xz*e_x^e_z + C__yz*e_y^e_z + C__xyz*e_x^e_y^e_z' assert str(grad*f) == 'D{x}f*e_x + D{y}f*e_y + D{z}f*e_z' assert str(grad | A) == 'D{x}A__x + D{y}A__y + D{z}A__z' assert str(grad*A) == 'D{x}A__x + D{y}A__y + D{z}A__z + (-D{y}A__x + D{x}A__y)*e_x^e_y + (-D{z}A__x + D{x}A__z)*e_x^e_z + (-D{z}A__y + D{y}A__z)*e_y^e_z' assert str(-MV.I*(grad ^ A)) == '(-D{z}A__y + D{y}A__z)*e_x + (D{z}A__x - D{x}A__z)*e_y + (-D{y}A__x + D{x}A__y)*e_z' assert str(grad*B) == '(-(D{y}B__xy + D{z}B__xz))*e_x + (D{x}B__xy - D{z}B__yz)*e_y + (D{x}B__xz + D{y}B__yz)*e_z + (D{z}B__xy - D{y}B__xz + D{x}B__yz)*e_x^e_y^e_z' assert str(grad ^ B) == '(D{z}B__xy - D{y}B__xz + D{x}B__yz)*e_x^e_y^e_z' assert str(grad | B) == '(-(D{y}B__xy + D{z}B__xz))*e_x + (D{x}B__xy - D{z}B__yz)*e_y + (D{x}B__xz + D{y}B__yz)*e_z' assert str(grad < A) == 'D{x}A__x + D{y}A__y + D{z}A__z' assert str(grad > A) == 'D{x}A__x + D{y}A__y + D{z}A__z' assert str(grad < B) == '(-(D{y}B__xy + D{z}B__xz))*e_x + (D{x}B__xy - D{z}B__yz)*e_y + (D{x}B__xz + D{y}B__yz)*e_z' assert str(grad > B) == '0' assert str(grad < C) == 'D{x}C__x + D{y}C__y + D{z}C__z + (-(D{y}C__xy + D{z}C__xz))*e_x + (D{x}C__xy - D{z}C__yz)*e_y + (D{x}C__xz + D{y}C__yz)*e_z + D{z}C__xyz*e_x^e_y - D{y}C__xyz*e_x^e_z + D{x}C__xyz*e_y^e_z' assert str(grad > C) == 'D{x}C__x + D{y}C__y + D{z}C__z + D{x}C*e_x + D{y}C*e_y + D{z}C*e_z' return def test_derivatives_in_spherical_coordinates(): with GA_Printer(): X = (r, th, phi) = symbols('r theta phi') curv = [[r*cos(phi)*sin(th), r*sin(phi)*sin(th), r*cos(th)], [1, r, r*sin(th)]] (er, eth, ephi, grad) = MV.setup('e_r e_theta e_phi', metric='[1,1,1]', coords=X, curv=curv) f = MV('f', 'scalar', fct=True) A = MV('A', 'vector', fct=True) B = MV('B', 'grade2', fct=True) assert str(f) == 'f' assert str(A) == 'A__r*e_r + A__theta*e_theta + A__phi*e_phi' assert str(B) == 'B__rtheta*e_r^e_theta + B__rphi*e_r^e_phi + B__thetaphi*e_theta^e_phi' assert str(grad*f) == 'D{r}f*e_r + D{theta}f/r*e_theta + D{phi}f/(r*sin(theta))*e_phi' assert str(grad | A) == 'D{r}A__r + 2*A__r/r + A__theta*cos(theta)/(r*sin(theta)) + D{theta}A__theta/r + D{phi}A__phi/(r*sin(theta))' assert str(-MV.I*(grad ^ A)) == '((A__phi*cos(theta)/sin(theta) + D{theta}A__phi - D{phi}A__theta/sin(theta))/r)*e_r + (-D{r}A__phi - A__phi/r + D{phi}A__r/(r*sin(theta)))*e_theta + (D{r}A__theta + A__theta/r - D{theta}A__r/r)*e_phi' assert str(grad ^ B) == '(D{r}B__thetaphi - B__rphi*cos(theta)/(r*sin(theta)) + 2*B__thetaphi/r - D{theta}B__rphi/r + D{phi}B__rtheta/(r*sin(theta)))*e_r^e_theta^e_phi' return def test_rounding_numerical_components(): with GA_Printer(): (ex, ey, ez) = MV.setup('e_x e_y e_z', metric='[1,1,1]') X = 1.2*ex + 2.34*ey + 0.555*ez Y = 0.333*ex + 4*ey + 5.3*ez assert str(X) == '1.20000000000000*e_x + 2.34000000000000*e_y + 0.555000000000000*e_z' assert str(Nga(X, 2)) == '1.2*e_x + 2.3*e_y + 0.55*e_z' assert str(X*Y) == '12.7011000000000 + 4.02078000000000*e_x^e_y + 6.17518500000000*e_x^e_z + 10.1820000000000*e_y^e_z' assert str(Nga(X*Y, 2)) == '13. + 4.0*e_x^e_y + 6.2*e_x^e_z + 10.*e_y^e_z' return def test_noneuclidian_distance_calculation(): from sympy import solve, sqrt with GA_Printer(): metric = '0 # #,# 0 #,# # 1' (X, Y, e) = MV.setup('X Y e', metric) assert str((X ^ Y)*(X ^ Y)) == '(X.Y)**2' L = X ^ Y ^ e B = L*e assert str(B) == 'X^Y - (Y.e)*X^e + (X.e)*Y^e' Bsq = B*B assert str(Bsq) == '(X.Y)*((X.Y) - 2*(X.e)*(Y.e))' Bsq = Bsq.scalar() assert str(B) == 'X^Y - (Y.e)*X^e + (X.e)*Y^e' BeBr = B*e*B.rev() assert str(BeBr) == '((X.Y)*(-(X.Y) + 2*(X.e)*(Y.e)))*e' assert str(B*B) == '(X.Y)*((X.Y) - 2*(X.e)*(Y.e))' assert str(L*L) == '(X.Y)*((X.Y) - 2*(X.e)*(Y.e))' (s, c, Binv, M, BigS, BigC, alpha, XdotY, Xdote, Ydote) = symbols('s c (1/B) M S C alpha (X.Y) (X.e) (Y.e)') Bhat = Binv*B R = c + s*Bhat assert str(R) == 'c + (1/B)*s*X^Y - (1/B)*(Y.e)*s*X^e + (1/B)*(X.e)*s*Y^e' Z = R*X*R.rev() Z.obj = expand(Z.obj) Z.obj = Z.obj.collect([Binv, s, c, XdotY]) assert str(Z) == '((1/B)**2*(X.Y)**2*s**2 - 2*(1/B)**2*(X.Y)*(X.e)*(Y.e)*s**2 + 2*(1/B)*(X.Y)*c*s - 2*(1/B)*(X.e)*(Y.e)*c*s + c**2)*X + 2*(1/B)*(X.e)**2*c*s*Y + (2*(1/B)*(X.Y)*(X.e)*s*(-(1/B)*(X.Y)*s + 2*(1/B)*(X.e)*(Y.e)*s - c))*e' W = Z | Y # From this point forward all calculations are with sympy scalars W = W.scalar() assert str(W) == '(1/B)**2*(X.Y)**3*s**2 - 4*(1/B)**2*(X.Y)**2*(X.e)*(Y.e)*s**2 + 4*(1/B)**2*(X.Y)*(X.e)**2*(Y.e)**2*s**2 + 2*(1/B)*(X.Y)**2*c*s - 4*(1/B)*(X.Y)*(X.e)*(Y.e)*c*s + (X.Y)*c**2' W = expand(W) W = simplify(W) W = W.collect([s*Binv]) M = 1/Bsq W = W.subs(Binv**2, M) W = simplify(W) Bmag = sqrt(XdotY**2 - 2*XdotY*Xdote*Ydote) W = W.collect([Binv*c*s, XdotY]) #Double angle substitutions W = W.subs(2*XdotY**2 - 4*XdotY*Xdote*Ydote, 2/(Binv**2)) W = W.subs(2*c*s, BigS) W = W.subs(c**2, (BigC + 1)/2) W = W.subs(s**2, (BigC - 1)/2) W = simplify(W) W = expand(W) W = W.subs(1/Binv, Bmag) assert str(W) == '(X.Y)*C - (X.e)*(Y.e)*C + (X.e)*(Y.e) + S*sqrt((X.Y)**2 - 2*(X.Y)*(X.e)*(Y.e))' Wd = collect(W, [BigC, BigS], exact=True, evaluate=False) Wd_1 = Wd[S.One] Wd_C = Wd[BigC] Wd_S = Wd[BigS] assert str(Wd_1) == '(X.e)*(Y.e)' assert str(Wd_C) == '(X.Y) - (X.e)*(Y.e)' assert str(Wd_S) == 'sqrt((X.Y)**2 - 2*(X.Y)*(X.e)*(Y.e))' assert str(Bmag) == 'sqrt((X.Y)**2 - 2*(X.Y)*(X.e)*(Y.e))' Wd_1 = Wd_1.subs(Bmag, 1/Binv) Wd_C = Wd_C.subs(Bmag, 1/Binv) Wd_S = Wd_S.subs(Bmag, 1/Binv) lhs = Wd_1 + Wd_C*BigC rhs = -Wd_S*BigS lhs = lhs**2 rhs = rhs**2 W = expand(lhs - rhs) W = expand(W.subs(1/Binv**2, Bmag**2)) W = expand(W.subs(BigS**2, BigC**2 - 1)) W = W.collect([BigC, BigC**2], evaluate=False) a = simplify(W[BigC**2]) b = simplify(W[BigC]) c = simplify(W[S.One]) assert str(a) == '(X.e)**2*(Y.e)**2' assert str(b) == '2*(X.e)*(Y.e)*((X.Y) - (X.e)*(Y.e))' assert str(c) == '(X.Y)**2 - 2*(X.Y)*(X.e)*(Y.e) + (X.e)**2*(Y.e)**2' x = Symbol('x') C = solve(a*x**2 + b*x + c, x)[0] assert str(expand(simplify(expand(C)))) == '-(X.Y)/((X.e)*(Y.e)) + 1' return def test_conformal_representations_of_circles_lines_spheres_and_planes(): global n, nbar with GA_Printer(): metric = '1 0 0 0 0,0 1 0 0 0,0 0 1 0 0,0 0 0 0 2,0 0 0 2 0' (e1, e2, e3, n, nbar) = MV.setup('e_1 e_2 e_3 n nbar', metric) e = n + nbar #conformal representation of points A = make_vector(e1) B = make_vector(e2) C = make_vector(-e1) D = make_vector(e3) X = make_vector('x', 3) assert str(A) == 'e_1 + 1/2*n - 1/2*nbar' assert str(B) == 'e_2 + 1/2*n - 1/2*nbar' assert str(C) == '-e_1 + 1/2*n - 1/2*nbar' assert str(D) == 'e_3 + 1/2*n - 1/2*nbar' assert str(X) == 'x1*e_1 + x2*e_2 + x3*e_3 + ((x1**2 + x2**2 + x3**2)/2)*n - 1/2*nbar' assert str((A ^ B ^ C ^ X)) == '-x3*e_1^e_2^e_3^n + x3*e_1^e_2^e_3^nbar + ((x1**2 + x2**2 + x3**2 - 1)/2)*e_1^e_2^n^nbar' assert str((A ^ B ^ n ^ X)) == '-x3*e_1^e_2^e_3^n + ((x1 + x2 - 1)/2)*e_1^e_2^n^nbar + x3/2*e_1^e_3^n^nbar - x3/2*e_2^e_3^n^nbar' assert str((((A ^ B) ^ C) ^ D) ^ X) == '((-x1**2 - x2**2 - x3**2 + 1)/2)*e_1^e_2^e_3^n^nbar' assert str((A ^ B ^ n ^ D ^ X)) == '((-x1 - x2 - x3 + 1)/2)*e_1^e_2^e_3^n^nbar' L = (A ^ B ^ e) ^ X assert str(L) == '-x3*e_1^e_2^e_3^n - x3*e_1^e_2^e_3^nbar + (-x1**2/2 + x1 - x2**2/2 + x2 - x3**2/2 - 1/2)*e_1^e_2^n^nbar + x3*e_1^e_3^n^nbar - x3*e_2^e_3^n^nbar' return def test_properties_of_geometric_objects(): with GA_Printer(): metric = '# # # 0 0,' + \ '# # # 0 0,' + \ '# # # 0 0,' + \ '0 0 0 0 2,' + \ '0 0 0 2 0' (p1, p2, p3, n, nbar) = MV.setup('p1 p2 p3 n nbar', metric) P1 = F(p1, n, nbar) P2 = F(p2, n, nbar) P3 = F(p3, n, nbar) L = P1 ^ P2 ^ n delta = (L | n) | nbar assert str(delta) == '2*p1 - 2*p2' C = P1 ^ P2 ^ P3 delta = ((C ^ n) | n) | nbar assert str(delta) == '2*p1^p2 - 2*p1^p3 + 2*p2^p3' assert str((p2 - p1) ^ (p3 - p1)) == 'p1^p2 - p1^p3 + p2^p3' return def test_extracting_vectors_from_conformal_2_blade(): with GA_Printer(): metric = ' 0 -1 #,' + \ '-1 0 #,' + \ ' # # #,' (P1, P2, a) = MV.setup('P1 P2 a', metric) B = P1 ^ P2 Bsq = B*B assert str(Bsq) == '1' ap = a - (a ^ B)*B assert str(ap) == '-(P2.a)*P1 - (P1.a)*P2' Ap = ap + ap*B Am = ap - ap*B assert str(Ap) == '-2*(P2.a)*P1' assert str(Am) == '-2*(P1.a)*P2' assert str(Ap*Ap) == '0' assert str(Am*Am) == '0' aB = a | B assert str(aB) == '-(P2.a)*P1 + (P1.a)*P2' return def test_reciprocal_frame_test(): with GA_Printer(): metric = '1 # #,' + \ '# 1 #,' + \ '# # 1,' (e1, e2, e3) = MV.setup('e1 e2 e3', metric) E = e1 ^ e2 ^ e3 Esq = (E*E).scalar() assert str(E) == 'e1^e2^e3' assert str(Esq) == '(e1.e2)**2 - 2*(e1.e2)*(e1.e3)*(e2.e3) + (e1.e3)**2 + (e2.e3)**2 - 1' Esq_inv = 1/Esq E1 = (e2 ^ e3)*E E2 = (-1)*(e1 ^ e3)*E E3 = (e1 ^ e2)*E assert str(E1) == '((e2.e3)**2 - 1)*e1 + ((e1.e2) - (e1.e3)*(e2.e3))*e2 + (-(e1.e2)*(e2.e3) + (e1.e3))*e3' assert str(E2) == '((e1.e2) - (e1.e3)*(e2.e3))*e1 + ((e1.e3)**2 - 1)*e2 + (-(e1.e2)*(e1.e3) + (e2.e3))*e3' assert str(E3) == '(-(e1.e2)*(e2.e3) + (e1.e3))*e1 + (-(e1.e2)*(e1.e3) + (e2.e3))*e2 + ((e1.e2)**2 - 1)*e3' w = (E1 | e2) w = w.expand() assert str(w) == '0' w = (E1 | e3) w = w.expand() assert str(w) == '0' w = (E2 | e1) w = w.expand() assert str(w) == '0' w = (E2 | e3) w = w.expand() assert str(w) == '0' w = (E3 | e1) w = w.expand() assert str(w) == '0' w = (E3 | e2) w = w.expand() assert str(w) == '0' w = (E1 | e1) w = (w.expand()).scalar() Esq = expand(Esq) assert str(simplify(w/Esq)) == '1' w = (E2 | e2) w = (w.expand()).scalar() assert str(simplify(w/Esq)) == '1' w = (E3 | e3) w = (w.expand()).scalar() assert str(simplify(w/Esq)) == '1' return sympy-0.7.4.1/sympy/galgebra/tests/__init__.py0000644000175000017500000000000012253362407021432 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/galgebra/__init__.py0000644000175000017500000000070612253362407020305 0ustar georgeskgeorgesk""" A module for geometric algebra """ from sympy.galgebra.ga import MV, Com, DD, Format, Nga, ReciprocalFrame, ScalarFunction, \ cross, dual, ga_print_off, ga_print_on, inv, proj, refl, rot, rotor from sympy.galgebra.debug import oprint, ostr from sympy.galgebra.precedence import define_precedence, GAeval from sympy.galgebra.printing import enhance_print, Get_Program, Print_Function, latex, xdvi from sympy.galgebra.manifold import Manifold sympy-0.7.4.1/sympy/parsing/0000755000175000017500000000000012253362407016070 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/parsing/mathematica.py0000644000175000017500000000302312253362407020715 0ustar georgeskgeorgeskfrom __future__ import print_function, division from re import match from sympy import sympify 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 sympy-0.7.4.1/sympy/parsing/maxima.py0000644000175000017500000000331412253362407017717 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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*:') def parse_maxima(str, globals=None, name_dict={}): str = str.strip() str = str.rstrip('; ') for k, v in 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 sympy-0.7.4.1/sympy/parsing/sympy_tokenize.py0000644000175000017500000004036512253362407021543 0ustar georgeskgeorgesk"""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.""" from __future__ import print_function, division __author__ = 'Ka-Ping Yee ' __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 def group(*choices): return '(' + '|'.join(choices) + ')' def any(*choices): return group(*choices) + '*' 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 = 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 class TokenError(Exception): pass class StopTokenizing(Exception): pass 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))) 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) 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) 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", ("", 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: strstart = (lnum, start) # multiple lines 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 strstart = (lnum, start) 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) sympy-0.7.4.1/sympy/parsing/ast_parser.py0000644000175000017500000000537312253362407020615 0ustar georgeskgeorgesk""" This module implements the functionality to take any Python expression as a string and fix all numbers and other things before evaluating it, thus 1/2 returns Integer(1)/Integer(2) We use the Python ast module for that, which is in python2.6 and later. It is well documented at docs.python.org. Some tips to understand how this works: use dump() to get a nice representation of any node. Then write a string of what you want to get, e.g. "Integer(1)", parse it, dump it and you'll see that you need to do "Call(Name('Integer', Load()), [node], [], None, None)". You don't need to bother with lineno and col_offset, just call fix_missing_locations() before returning the node. """ from __future__ import print_function, division from sympy.core.basic import Basic from sympy.core.compatibility import exec_ from sympy.core.sympify import SympifyError from ast import parse, NodeTransformer, Call, Name, Load, \ fix_missing_locations, Str, Tuple class Transform(NodeTransformer): def __init__(self, local_dict, global_dict): NodeTransformer.__init__(self) self.local_dict = local_dict self.global_dict = global_dict def visit_Num(self, node): if isinstance(node.n, int): return fix_missing_locations(Call(Name('Integer', Load()), [node], [], None, None)) elif isinstance(node.n, float): return fix_missing_locations(Call(Name('Float', Load()), [node], [], None, None)) return node def visit_Name(self, node): if node.id in self.local_dict: return node elif node.id in self.global_dict: name_obj = self.global_dict[node.id] if isinstance(name_obj, (Basic, type)) or callable(name_obj): return node elif node.id in ['True', 'False']: return node return fix_missing_locations(Call(Name('Symbol', Load()), [Str(node.id)], [], None, None)) def visit_Lambda(self, node): args = [self.visit(arg) for arg in node.args.args] body = self.visit(node.body) n = Call(Name('Lambda', Load()), [Tuple(args, Load()), body], [], None, None) return fix_missing_locations(n) def parse_expr(s, local_dict): """ Converts the string "s" to a SymPy expression, in local_dict. It converts all numbers to Integers before feeding it to Python and automatically creates Symbols. """ global_dict = {} exec_('from sympy import *', global_dict) try: a = parse(s.strip(), mode="eval") except SyntaxError: raise SympifyError("Cannot parse %s." % repr(s)) a = Transform(local_dict, global_dict).visit(a) e = compile(a, "", "eval") return eval(e, global_dict, local_dict) sympy-0.7.4.1/sympy/parsing/sympy_parser.py0000644000175000017500000006642712253362407021216 0ustar georgeskgeorgesk"""Transform a string with Python-like source code into SymPy expression. """ from __future__ import print_function, division from .sympy_tokenize import \ generate_tokens, untokenize, TokenError, \ NUMBER, STRING, NAME, OP, ENDMARKER from keyword import iskeyword import ast import re import unicodedata import sympy from sympy.core.compatibility import exec_, StringIO from sympy.core.basic import Basic, C _re_repeated = re.compile(r"^(\d*)\.(\d*)\[(\d+)\]$") 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 character 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 else: try: return not unicodedata.lookup('GREEK SMALL LETTER ' + token) except KeyError: pass if len(token) > 1: return True return False def _token_callable(token, local_dict, global_dict, nextToken=None): """ Predicate for whether a token name represents a callable function. Essentially wraps ``callable``, but looks up the token name in the locals and globals. """ func = local_dict.get(token[1]) if not func: func = global_dict.get(token[1]) return callable(func) and not isinstance(func, sympy.Symbol) 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(recursor): def _inner(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 = recursor(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 return _inner 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 and _token_callable(symbol, local_dict, global_dict): result[-1] = AppliedFunction(symbol, tok) symbol = None else: result.extend(tok) else: symbol = None 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 close parenthesis next to an open parenthesis ("(x+2)(x+3)") - 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, '*')) elif (tok[0] == NAME and not _token_callable(tok, local_dict, global_dict) and nextTok[0] == OP and nextTok[1] == '('): # Constant followed by parenthesis result.append((OP, '*')) elif (tok[0] == NAME and not _token_callable(tok, local_dict, global_dict) and nextTok[0] == NAME and not _token_callable(nextTok, local_dict, global_dict)): # Constant followed by constant result.append((OP, '*')) elif (tok[0] == NAME and not _token_callable(tok, local_dict, global_dict) and (isinstance(nextTok, AppliedFunction) or nextTok[0] == NAME)): # Constant followed by (implicitly applied) function result.append((OP, '*')) if tokens: result.append(tokens[-1]) return result def _implicit_application(tokens, local_dict, global_dict): """Adds parentheses as needed after functions.""" result = [] appendParen = 0 # number of closing parentheses to add skip = 0 # number of tokens to delay before adding a ')' (to # capture **, ^, etc.) exponentSkip = False # skipping tokens before inserting parentheses to # work with function exponentiation for tok, nextTok in zip(tokens, tokens[1:]): result.append(tok) if (tok[0] == NAME and nextTok[0] != OP and nextTok[0] != ENDMARKER): if _token_callable(tok, local_dict, global_dict, nextTok): result.append((OP, '(')) appendParen += 1 # name followed by exponent - function exponentiation elif (tok[0] == NAME and nextTok[0] == OP and nextTok[1] == '**'): if _token_callable(tok, local_dict, global_dict): exponentSkip = True elif exponentSkip: # if the last token added was an applied function (i.e. the # power of the function exponent) OR a multiplication (as # implicit multiplication would have added an extraneous # multiplication) if (isinstance(tok, AppliedFunction) or (tok[0] == OP and tok[1] == '*')): # don't add anything if the next token is a multiplication # or if there's already a parenthesis (if parenthesis, still # stop skipping tokens) if not (nextTok[0] == OP and nextTok[1] == '*'): if not(nextTok[0] == OP and nextTok[1] == '('): result.append((OP, '(')) appendParen += 1 exponentSkip = False elif appendParen: if nextTok[0] == OP and nextTok[1] in ('^', '**', '*'): skip = 1 continue if skip: skip -= 1 continue result.append((OP, ')')) appendParen -= 1 if tokens: result.append(tokens[-1]) if appendParen: result.extend([(OP, ')')] * appendParen) return result def function_exponentiation(tokens, local_dict, global_dict): """Allows functions to be exponentiated, e.g. ``cos**2(x)``. Example: >>> from sympy.parsing.sympy_parser import (parse_expr, ... standard_transformations, function_exponentiation) >>> transformations = standard_transformations + (function_exponentiation,) >>> parse_expr('sin**4(x)', transformations=transformations) sin(x)**4 """ result = [] exponent = [] consuming_exponent = False level = 0 for tok, nextTok in zip(tokens, tokens[1:]): if tok[0] == NAME and nextTok[0] == OP and nextTok[1] == '**': if _token_callable(tok, local_dict, global_dict): consuming_exponent = True elif consuming_exponent: exponent.append(tok) # only want to stop after hitting ) if tok[0] == nextTok[0] == OP and tok[1] == ')' and nextTok[1] == '(': consuming_exponent = False # if implicit multiplication was used, we may have )*( instead if tok[0] == nextTok[0] == OP and tok[1] == '*' and nextTok[1] == '(': consuming_exponent = False del exponent[-1] continue elif exponent and not consuming_exponent: if tok[0] == OP: if tok[1] == '(': level += 1 elif tok[1] == ')': level -= 1 if level == 0: result.append(tok) result.extend(exponent) exponent = [] continue result.append(tok) if tokens: result.append(tokens[-1]) if exponent: result.extend(exponent) return result def split_symbols_custom(predicate): """Creates a transformation that splits symbol names. ``predicate`` should return True if the symbol name is to be split. For instance, to retain the default behavior but avoid splitting certain symbol names, a predicate like this would work: >>> from sympy.parsing.sympy_parser import (parse_expr, _token_splittable, ... standard_transformations, implicit_multiplication, ... split_symbols_custom) >>> def can_split(symbol): ... if symbol not in ('list', 'of', 'unsplittable', 'names'): ... return _token_splittable(symbol) ... return False ... >>> transformation = split_symbols_custom(can_split) >>> parse_expr('unsplittable', transformations=standard_transformations + ... (transformation, implicit_multiplication)) unsplittable """ def _split_symbols(tokens, local_dict, global_dict): result = [] split = False for tok in tokens: if tok[0] == NAME and tok[1] == 'Symbol': split = True elif split and tok[0] == NAME: symbol = tok[1][1:-1] if predicate(symbol): for char in symbol: if char in local_dict or char in global_dict: # Get rid of the call to Symbol del result[-2:] result.extend([(OP, '('), (NAME, "%s" % char), (OP, ')'), (NAME, 'Symbol'), (OP, '(')]) else: result.extend([(NAME, "'%s'" % char), (OP, ')'), (NAME, 'Symbol'), (OP, '(')]) # Delete the last three tokens: get rid of the extraneous # Symbol( we just added, and also get rid of the last ) # because the closing parenthesis of the original Symbol is # still there del result[-3:] split = False continue else: split = False result.append(tok) return result return _split_symbols #: Splits symbol names for implicit multiplication. #: #: Intended to let expressions like ``xyz`` be parsed as ``x*y*z``. Does not #: split Greek character names, so ``theta`` will *not* become #: ``t*h*e*t*a``. Generally this should be used with #: ``implicit_multiplication``. split_symbols = split_symbols_custom(_token_splittable) def implicit_multiplication(result, local_dict, global_dict): """Makes the multiplication operator optional in most cases. Use this before :func:`implicit_application`, otherwise expressions like ``sin 2x`` will be parsed as ``x * sin(2)`` rather than ``sin(2*x)``. Example: >>> from sympy.parsing.sympy_parser import (parse_expr, ... standard_transformations, implicit_multiplication) >>> transformations = standard_transformations + (implicit_multiplication,) >>> parse_expr('3 x y', transformations=transformations) 3*x*y """ # These are interdependent steps, so we don't expose them separately for step in (_group_parentheses(implicit_multiplication), _apply_functions, _implicit_multiplication): result = step(result, local_dict, global_dict) result = _flatten(result) return result def implicit_application(result, local_dict, global_dict): """Makes parentheses optional in some cases for function calls. Use this after :func:`implicit_multiplication`, otherwise expressions like ``sin 2x`` will be parsed as ``x * sin(2)`` rather than ``sin(2*x)``. Example: >>> from sympy.parsing.sympy_parser import (parse_expr, ... standard_transformations, implicit_application) >>> transformations = standard_transformations + (implicit_application,) >>> parse_expr('cot z + csc z', transformations=transformations) cot(z) + csc(z) """ for step in (_group_parentheses(implicit_application), _apply_functions, _implicit_application,): result = step(result, local_dict, global_dict) result = _flatten(result) 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) """ for step in (split_symbols, implicit_multiplication, implicit_application, function_exponentiation): result = step(result, local_dict, global_dict) return result def auto_symbol(tokens, local_dict, global_dict): """Inserts calls to ``Symbol`` for undefined variables.""" result = [] prevTok = (None, None) tokens.append((None, None)) # so zip traverses all tokens for tok, nextTok in zip(tokens, tokens[1:]): tokNum, tokVal = tok nextTokNum, nextTokVal = nextTok if tokNum == NAME: name = tokVal if (name in ['True', 'False', 'None'] or iskeyword(name) or name in local_dict # Don't convert attribute access or (prevTok[0] == OP and prevTok[1] == '.') # Don't convert keyword arguments or (prevTok[0] == OP and prevTok[1] in ('(', ',') and nextTokNum == OP and nextTokVal == '=')): result.append((NAME, name)) continue elif name in global_dict: obj = global_dict[name] if isinstance(obj, (Basic, type)) or callable(obj): result.append((NAME, name)) continue result.extend([ (NAME, 'Symbol'), (OP, '('), (NAME, repr(str(name))), (OP, ')'), ]) else: result.append((tokNum, tokVal)) prevTok = (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``. 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 for :func:`parse_expr`. #: Inserts calls to :class:`Symbol`, :class:`Integer`, and other SymPy #: datatypes and allows the use of standard factorial notation (e.g. ``x!``). standard_transformations = (auto_symbol, auto_number, factorial_notation) def stringify_expr(s, local_dict, global_dict, transformations): """ Converts the string ``s`` to Python code, in ``local_dict`` Generally, ``parse_expr`` should be used. """ 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) return untokenize(tokens) def eval_expr(code, local_dict, global_dict): """ Evaluate Python code generated by ``stringify_expr``. Generally, ``parse_expr`` should be used. """ expr = eval( code, global_dict, local_dict) # take local objects in preference return expr def parse_expr(s, local_dict=None, transformations=standard_transformations, global_dict=None, evaluate=True): """Converts the string ``s`` to a SymPy expression, in ``local_dict`` Parameters ========== s : str The string to parse. local_dict : dict, optional A dictionary of local variables to use when parsing. global_dict : dict, optional A dictionary of global variables. By default, this is initialized with ``from sympy import *``; provide this parameter to override this behavior (for instance, to parse ``"Q & S"``). transformations : tuple, optional A tuple of transformation functions used to modify the tokens of the parsed expression before evaluation. The default transformations convert numeric literals into their SymPy equivalents, convert undefined variables into SymPy symbols, and allow the use of standard mathematical factorial notation (e.g. ``x!``). Examples ======== >>> from sympy.parsing.sympy_parser import parse_expr >>> parse_expr("1/2") 1/2 >>> type(_) >>> from sympy.parsing.sympy_parser import standard_transformations,\\ ... implicit_multiplication_application >>> transformations = (standard_transformations + ... (implicit_multiplication_application,)) >>> parse_expr("2x", transformations=transformations) 2*x See Also ======== stringify_expr, eval_expr, standard_transformations, implicit_multiplication_application """ if local_dict is None: local_dict = {} if global_dict is None: global_dict = {} exec_('from sympy import *', global_dict) code = stringify_expr(s, local_dict, global_dict, transformations) if evaluate is False: code = compile(evaluateFalse(code), '', 'eval') return eval_expr(code, local_dict, global_dict) def evaluateFalse(s): """ Replaces operators with the SymPy equivalent and sets evaluate=False. """ node = ast.parse(s) node = EvaluateFalseTransformer().visit(node) # node is a Module, we want an Expression node = ast.Expression(node.body[0].value) return ast.fix_missing_locations(node) class EvaluateFalseTransformer(ast.NodeTransformer): operators = { ast.Add: 'Add', ast.Mult: 'Mul', ast.Pow: 'Pow', ast.Sub: 'Add', ast.Div: 'Mul', ast.BitOr: 'Or', ast.BitAnd: 'And', ast.BitXor: 'Not', } def flatten(self, args, func): result = [] for arg in args: if isinstance(arg, ast.Call) and arg.func.id == func: result.extend(self.flatten(arg.args, func)) else: result.append(arg) return result def visit_BinOp(self, node): if node.op.__class__ in self.operators: sympy_class = self.operators[node.op.__class__] right = self.visit(node.right) if isinstance(node.op, ast.Sub): right = ast.UnaryOp(op=ast.USub(), operand=right) elif isinstance(node.op, ast.Div): right = ast.Call( func=ast.Name(id='Pow', ctx=ast.Load()), args=[right, ast.UnaryOp(op=ast.USub(), operand=ast.Num(1))], keywords=[ast.keyword(arg='evaluate', value=ast.Name(id='False', ctx=ast.Load()))], starargs=None, kwargs=None ) new_node = ast.Call( func=ast.Name(id=sympy_class, ctx=ast.Load()), args=[self.visit(node.left), right], keywords=[ast.keyword(arg='evaluate', value=ast.Name(id='False', ctx=ast.Load()))], starargs=None, kwargs=None ) if sympy_class in ('Add', 'Mul'): # Denest Add or Mul as appropriate new_node.args = self.flatten(new_node.args, sympy_class) return new_node return node sympy-0.7.4.1/sympy/parsing/tests/0000755000175000017500000000000012253362407017232 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/parsing/tests/test_sympy_parser.py0000644000175000017500000000423112253362407023400 0ustar georgeskgeorgeskfrom sympy.core import Symbol, Float, Rational, Integer, I, Mul, Pow from sympy.functions import exp, factorial, sin from sympy.logic import And from sympy.series import Limit from sympy.utilities.pytest import raises from sympy.parsing.sympy_parser import ( parse_expr, standard_transformations, rationalize, TokenError ) def test_sympy_parser(): x = Symbol('x') inputs = { '2*x': 2 * x, '3.00': Float(3), '22/7': Rational(22, 7), '2+3j': 2 + 3*I, 'exp(x)': exp(x), 'x!': factorial(x), '3.[3]': Rational(10, 3), '10!': 3628800, '-(2)': -Integer(2), '[-1, -2, 3]': [Integer(-1), Integer(-2), Integer(3)], 'Symbol("x").free_symbols': x.free_symbols, "S('S(3).n(n=3)')": 3.00, 'factorint(12, visual=True)': Mul( Pow(2, 2, evaluate=False), Pow(3, 1, evaluate=False), evaluate=False), 'Limit(sin(x), x, 0, dir="-")': Limit(sin(x), x, 0, dir='-'), } for text, result in inputs.items(): assert parse_expr(text) == result def test_rationalize(): inputs = { '0.123': Rational(123, 1000) } transformations = standard_transformations + (rationalize,) for text, result in inputs.items(): assert parse_expr(text, transformations=transformations) == result def test_factorial_fail(): inputs = ['x!!!', 'x!!!!', '(!)'] for text in inputs: try: parse_expr(text) assert False except TokenError: assert True def test_local_dict(): local_dict = { 'my_function': lambda x: x + 2 } inputs = { 'my_function(2)': Integer(4) } for text, result in inputs.items(): assert parse_expr(text, local_dict=local_dict) == result def test_global_dict(): global_dict = { 'Symbol': Symbol } inputs = { 'Q & S': And(Symbol('Q'), Symbol('S')) } for text, result in inputs.items(): assert parse_expr(text, global_dict=global_dict) == result def test_issue_2515_github(): raises(TokenError, lambda: parse_expr('(()')) raises(TokenError, lambda: parse_expr('"""')) sympy-0.7.4.1/sympy/parsing/tests/test_maxima.py0000644000175000017500000000315712253362407022125 0ustar georgeskgeorgeskfrom sympy.parsing.maxima import parse_maxima from sympy import Rational, Abs, Symbol, sin, cos, E, oo, log, factorial from sympy.abc import x n = Symbol('n', integer=True) def test_parser(): assert Abs(parse_maxima('float(1/3)') - 0.333333333) < 10**(-5) assert parse_maxima('13^26') == 91733330193268616658399616009 assert parse_maxima('sin(%pi/2) + cos(%pi/3)') == Rational(3, 2) assert parse_maxima('log(%e)') == 1 def test_injection(): parse_maxima('c: x+1', globals=globals()) assert c == x + 1 parse_maxima('g: sqrt(81)', globals=globals()) assert g == 9 def test_maxima_functions(): assert parse_maxima('expand( (x+1)^2)') == x**2 + 2*x + 1 assert parse_maxima('factor( x**2 + 2*x + 1)') == (x + 1)**2 assert parse_maxima('2*cos(x)^2 + sin(x)^2') == 2*cos(x)**2 + sin(x)**2 assert parse_maxima('trigexpand(sin(2*x)+cos(2*x))') == \ -1 + 2*cos(x)**2 + 2*cos(x)*sin(x) assert parse_maxima('solve(x^2-4,x)') == [-2, 2] assert parse_maxima('limit((1+1/x)^x,x,inf)') == E assert parse_maxima('limit(sqrt(-x)/x,x,0,minus)') == -oo assert parse_maxima('diff(x^x, x)') == x**x*(1 + log(x)) assert parse_maxima('sum(k, k, 1, n)', name_dict=dict( n=Symbol('n', integer=True), k=Symbol('k', integer=True) )) == (n**2 + n)/2 assert parse_maxima('product(k, k, 1, n)', name_dict=dict( n=Symbol('n', integer=True), k=Symbol('k', integer=True) )) == factorial(n) assert parse_maxima('ratsimp((x^2-1)/(x+1))') == x - 1 assert Abs( parse_maxima( 'float(sec(%pi/3) + csc(%pi/3))') - 3.154700538379252) < 10**(-5) sympy-0.7.4.1/sympy/parsing/tests/test_mathematica.py0000644000175000017500000000112512253362407023117 0ustar georgeskgeorgeskfrom sympy.parsing.mathematica import mathematica from sympy import sympify def test_mathematica(): d = {'Sin[x]^2': 'sin(x)**2', '2(x-1)': '2*(x-1)', '3y+8': '3*y+8', 'Arcsin[2x+9(4-x)^2]/x': 'asin(2*x+9*(4-x)**2)/x', 'x+y': 'x+y', '355/113': '355/113', '2.718281828': '2.718281828', 'Sin[12]': 'sin(12)', 'Exp[Log[4]]': 'exp(log(4))', '(x+1)(x+3)': '(x+1)*(x+3)', 'Cos[Arccos[3.6]]': 'cos(acos(3.6))', 'Cos[x]==Sin[y]': 'cos(x)==sin(y)'} for e in d: assert mathematica(e) == sympify(d[e]) sympy-0.7.4.1/sympy/parsing/tests/test_implicit_multiplication_application.py0000644000175000017500000001551012253362407030157 0ustar georgeskgeorgeskimport sympy from sympy.parsing.sympy_parser import ( parse_expr, standard_transformations, convert_xor, implicit_multiplication_application, implicit_multiplication, implicit_application, function_exponentiation, split_symbols, split_symbols_custom, _token_splittable ) from sympy.utilities.pytest import raises def test_implicit_multiplication(): cases = { '5x': '5*x', 'abc': 'a*b*c', '3sin(x)': '3*sin(x)', '(x+1)(x+2)': '(x+1)*(x+2)', '(5 x**2)sin(x)': '(5*x**2)*sin(x)', '2 sin(x) cos(x)': '2*sin(x)*cos(x)', 'pi x': 'pi*x', 'x pi': 'x*pi', 'E x': 'E*x', 'EulerGamma y': 'EulerGamma*y', 'E pi': 'E*pi', 'pi (x + 2)': 'pi*(x+2)', '(x + 2) pi': '(x+2)*pi', 'pi sin(x)': 'pi*sin(x)', } transformations = standard_transformations + (convert_xor,) transformations2 = transformations + (split_symbols, implicit_multiplication) for case in cases: implicit = parse_expr(case, transformations=transformations2) normal = parse_expr(cases[case], transformations=transformations) assert(implicit == normal) application = ['sin x', 'cos 2*x', 'sin cos x'] for case in application: raises(SyntaxError, lambda: parse_expr(case, transformations=transformations2)) raises(TypeError, lambda: parse_expr('sin**2(x)', transformations=transformations2)) def test_implicit_application(): cases = { 'factorial': 'factorial', 'sin x': 'sin(x)', 'tan y**3': 'tan(y**3)', 'cos 2*x': 'cos(2*x)', '(cot)': 'cot', 'sin cos tan x': 'sin(cos(tan(x)))' } transformations = standard_transformations + (convert_xor,) transformations2 = transformations + (implicit_application,) for case in cases: implicit = parse_expr(case, transformations=transformations2) normal = parse_expr(cases[case], transformations=transformations) assert(implicit == normal) multiplication = ['x y', 'x sin x', '2x'] for case in multiplication: raises(SyntaxError, lambda: parse_expr(case, transformations=transformations2)) raises(TypeError, lambda: parse_expr('sin**2(x)', transformations=transformations2)) def test_function_exponentiation(): cases = { 'sin**2(x)': 'sin(x)**2', 'exp^y(z)': 'exp(z)^y', 'sin**2(E^(x))': 'sin(E^(x))**2' } transformations = standard_transformations + (convert_xor,) transformations2 = transformations + (function_exponentiation,) for case in cases: implicit = parse_expr(case, transformations=transformations2) normal = parse_expr(cases[case], transformations=transformations) assert(implicit == normal) other_implicit = ['x y', 'x sin x', '2x', 'sin x', 'cos 2*x', 'sin cos x'] for case in other_implicit: raises(SyntaxError, lambda: parse_expr(case, transformations=transformations2)) assert parse_expr('x**2', local_dict={ 'x': sympy.Symbol('x') }, transformations=transformations2) == parse_expr('x**2') def test_symbol_splitting(): # By default Greek letter names should not be split (lambda is a keyword # so skip it) transformations = standard_transformations + (split_symbols,) greek_letters = ('alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta', 'eta', 'theta', 'iota', 'kappa', 'mu', 'nu', 'xi', 'omicron', 'pi', 'rho', 'sigma', 'tau', 'upsilon', 'phi', 'chi', 'psi', 'omega') for letter in greek_letters: assert(parse_expr(letter, transformations=transformations) == parse_expr(letter)) # Make sure symbol splitting resolves names transformations += (implicit_multiplication,) local_dict = { 'e': sympy.E } cases = { 'xe': 'E*x', 'Iy': 'I*y', 'ee': 'E*E', } for case, expected in cases.items(): assert(parse_expr(case, local_dict=local_dict, transformations=transformations) == parse_expr(expected)) # Make sure custom splitting works def can_split(symbol): if symbol not in ('unsplittable', 'names'): return _token_splittable(symbol) return False transformations = standard_transformations transformations += (split_symbols_custom(can_split), implicit_multiplication) assert(parse_expr('unsplittable', transformations=transformations) == parse_expr('unsplittable')) assert(parse_expr('names', transformations=transformations) == parse_expr('names')) assert(parse_expr('xy', transformations=transformations) == parse_expr('x*y')) for letter in greek_letters: assert(parse_expr(letter, transformations=transformations) == parse_expr(letter)) def test_all_implicit_steps(): cases = { '2x': '2*x', # implicit multiplication 'x y': 'x*y', 'xy': 'x*y', 'sin x': 'sin(x)', # add parentheses '2sin x': '2*sin(x)', 'x y z': 'x*y*z', 'sin(2 * 3x)': 'sin(2 * 3 * x)', 'sin(x) (1 + cos(x))': 'sin(x) * (1 + cos(x))', '(x + 2) sin(x)': '(x + 2) * sin(x)', '(x + 2) sin x': '(x + 2) * sin(x)', 'sin(sin x)': 'sin(sin(x))', 'sin x!': 'sin(factorial(x))', 'sin x!!': 'sin(factorial2(x))', 'factorial': 'factorial', # don't apply a bare function 'x sin x': 'x * sin(x)', # both application and multiplication 'xy sin x': 'x * y * sin(x)', '(x+2)(x+3)': '(x + 2) * (x+3)', 'x**2 + 2xy + y**2': 'x**2 + 2 * x * y + y**2', # split the xy 'pi': 'pi', # don't mess with constants 'None': 'None', 'ln sin x': 'ln(sin(x))', # multiple implicit function applications 'factorial': 'factorial', # don't add parentheses 'sin x**2': 'sin(x**2)', # implicit application to an exponential 'alpha': 'Symbol("alpha")', # don't split Greek letters/subscripts 'x_2': 'Symbol("x_2")', 'sin^2 x**2': 'sin(x**2)**2', # function raised to a power 'sin**3(x)': 'sin(x)**3', '(factorial)': 'factorial', 'tan 3x': 'tan(3*x)', 'sin^2(3*E^(x))': 'sin(3*E**(x))**2', 'sin**2(E^(3x))': 'sin(E**(3*x))**2', 'sin^2 (3x*E^(x))': 'sin(3*x*E^x)**2', 'pi sin x': 'pi*sin(x)', } transformations = standard_transformations + (convert_xor,) transformations2 = transformations + (implicit_multiplication_application,) for case in cases: implicit = parse_expr(case, transformations=transformations2) normal = parse_expr(cases[case], transformations=transformations) assert(implicit == normal) sympy-0.7.4.1/sympy/parsing/tests/__init__.py0000644000175000017500000000000012253362407021331 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/parsing/__init__.py0000644000175000017500000000007612253362407020204 0ustar georgeskgeorgesk"""Used for translating a string into a SymPy expression. """ sympy-0.7.4.1/sympy/plotting/0000755000175000017500000000000012253362407016265 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/plotting/experimental_lambdify.py0000644000175000017500000006033312253362407023210 0ustar georgeskgeorgesk""" rewrite of lambdify - This stuff is not stable at all. It is for internal use in the new plotting module. It may (will! see the Q'n'A in the source) be rewritten. It's completely self contained. Especially it does not use lambdarepr. It does not aim to replace the current lambdify. Most importantly it will never ever support anything else than sympy expressions (no Matrices, dictionaries and so on). """ from __future__ import print_function, division import re from sympy import Symbol, NumberSymbol, I, zoo, oo from sympy.core.compatibility import exec_ # We parse the expression string into a tree that identifies functions. Then # we translate the names of the functions and we translate also some strings # that are not names of functions (all this according to translation # dictionaries). # If the translation goes to another module (like numpy) the # module is imported and 'func' is translated to 'module.func'. # If a function can not be translated, the inner nodes of that part of the # tree are not translated. So if we have Integral(sqrt(x)), sqrt is not # translated to np.sqrt and the Integral does not crash. # A namespace for all this is generated by crawling the (func, args) tree of # the expression. The creation of this namespace involves many ugly # workarounds. # The namespace consists of all the names needed for the sympy expression and # all the name of modules used for translation. Those modules are imported only # as a name (import numpy as np) in order to keep the namespace small and # manageable. # Please, if there is a bug, do not try to fix it here! Rewrite this by using # the method proposed in the last Q'n'A below. That way the new function will # work just as well, be just as simple, but it wont need any new workarounds. # If you insist on fixing it here, look at the workarounds in the function # sympy_expression_namespace and in lambdify. # Q: Why are you not using python abstract syntax tree? # A: Because it is more complicated and not much more powerful in this case. # Q: What if I have Symbol('sin') or g=Function('f')? # A: You will break the algorithm. We should use srepr to defend against this? # The problem with Symbol('sin') is that it will be printed as 'sin'. The # parser will distinguish it from the function 'sin' because functions are # detected thanks to the opening parenthesis, but the lambda expression won't # understand the difference if we have also the sin function. # The solution (complicated) is to use srepr and maybe ast. # The problem with the g=Function('f') is that it will be printed as 'f' but in # the global namespace we have only 'g'. But as the same printer is used in the # constructor of the namespace there will be no problem. # Q: What if some of the printers are not printing as expected? # A: The algorithm wont work. You must use srepr for those cases. But even # srepr may not print well. All problems with printers should be considered # bugs. # Q: What about _imp_ functions? # A: Those are taken care for by evalf. A special case treatment will work # faster but it's not worth the code complexity. # Q: Will ast fix all possible problems? # A: No. You will always have to use some printer. Even srepr may not work in # some cases. But if the printer does not work, that should be considered a # bug. # Q: Is there same way to fix all possible problems? # A: Probably by constructing our strings ourself by traversing the (func, # args) tree and creating the namespace at the same time. That actually sounds # good. from sympy.external import import_module import warnings #TODO debuging output class vectorized_lambdify(object): """ Return a sufficiently smart, vectorized and lambdified function. Returns only reals. This function uses experimental_lambdify to created a lambdified expression ready to be used with numpy. Many of the functions in sympy are not implemented in numpy so in some cases we resort to python cmath or even to evalf. The following translations are tried: only numpy complex - on errors raised by sympy trying to work with ndarray: only python cmath and then vectorize complex128 When using python cmath there is no need for evalf or float/complex because python cmath calls those. This function never tries to mix numpy directly with evalf because numpy does not understand sympy Float. If this is needed one can use the float_wrap_evalf/complex_wrap_evalf options of experimental_lambdify or better one can be explicit about the dtypes that numpy works with. Check numpy bug http://projects.scipy.org/numpy/ticket/1013 to know what types of errors to expect. """ def __init__(self, args, expr): self.args = args self.expr = expr self.lambda_func = experimental_lambdify(args, expr, use_np=True) self.vector_func = self.lambda_func self.failure = False def __call__(self, *args): np = import_module('numpy') np_old_err = np.seterr(invalid='raise') try: temp_args = (np.array(a, dtype=np.complex) for a in args) results = self.vector_func(*temp_args) results = np.ma.masked_where( np.abs(results.imag) > 1e-7 * np.abs(results), results.real, copy=False) except Exception as e: #DEBUG: print 'Error', type(e), e if ((isinstance(e, TypeError) and 'unhashable type: \'numpy.ndarray\'' in str(e)) or (isinstance(e, ValueError) and ('Invalid limits given:' in str(e) or 'negative dimensions are not allowed' in str(e) # XXX or 'sequence too large; must be smaller than 32' in str(e)))): # XXX # Almost all functions were translated to numpy, but some were # left as sympy functions. They recieved an ndarray as an # argument and failed. # sin(ndarray(...)) raises "unhashable type" # Integral(x, (x, 0, ndarray(...))) raises "Invalid limits" # other ugly exceptions that are not well understood (marked with XXX) # TODO: Cleanup the ugly special cases marked with xxx above. # Solution: use cmath and vectorize the final lambda. self.lambda_func = experimental_lambdify( self.args, self.expr, use_python_cmath=True) self.vector_func = np.vectorize( self.lambda_func, otypes=[np.complex]) results = self.vector_func(*args) results = np.ma.masked_where( np.abs(results.imag) > 1e-7 * np.abs(results), results.real, copy=False) else: # Complete failure. One last try with no translations, only # wrapping in complex((...).evalf()) and returning the real # part. if self.failure: raise e else: self.failure = True self.lambda_func = experimental_lambdify( self.args, self.expr, use_evalf=True, complex_wrap_evalf=True) self.vector_func = np.vectorize( self.lambda_func, otypes=[np.complex]) results = self.vector_func(*args) results = np.ma.masked_where( np.abs(results.imag) > 1e-7 * np.abs(results), results.real, copy=False) warnings.warn('The evaluation of the expression is' ' problematic. We are trying a failback method' ' that may still work. Please report this as a bug.') finally: np.seterr(**np_old_err) return results class lambdify(object): """Returns the lambdified function. This function uses experimental_lambdify to create a lambdified expression. It uses cmath to lambdify the expression. If the function is not implemented in python cmath, python cmath calls evalf on those functions. """ def __init__(self, args, expr): self.args = args self.expr = expr self.lambda_func = experimental_lambdify(args, expr, use_evalf=True, use_python_cmath=True) self.failure = False def __call__(self, args): args = complex(args) try: #The result can be sympy.Float. Hence wrap it with complex type. result = complex(self.lambda_func(args)) if abs(result.imag) > 1e-7 * abs(result): return None else: return result.real except Exception as e: # The exceptions raised by sympy, cmath are not consistent and # hence it is not possible to specify all the exceptions that # are to be caught. Presently there are no cases for which the code # reaches this block other than ZeroDivisionError and complex # comparision. Also the exception is caught only once. If the # exception repeats itself, # then it is not caught and the corresponding error is raised. # XXX: Remove catching all exceptions once the plotting module # is heavily tested. if isinstance(e, ZeroDivisionError): return None elif isinstance(e, TypeError) and ('no ordering relation is' ' defined for complex numbers' in str(e)): self.lambda_func = experimental_lambdify(self.args, self.expr, use_evalf=True, use_python_math=True) result = self.lambda_func(args.real) return result else: if self.failure: raise e #Failure #Try wrapping it with complex(..).evalf() self.failure = True self.lambda_func = experimental_lambdify(self.args, self.expr, use_evalf=True, complex_wrap_evalf=True) result = self.lambda_func(args) warnings.warn('The evaluation of the expression is' ' problematic. We are trying a failback method' ' that may still work. Please report this as a bug.') if abs(result.imag) > 1e-7 * abs(result): return None else: return result.real def experimental_lambdify(*args, **kwargs): l = Lambdifier(*args, **kwargs) return l.lambda_func class Lambdifier(object): def __init__(self, args, expr, print_lambda=False, use_evalf=False, float_wrap_evalf=False, complex_wrap_evalf=False, use_np=False, use_python_math=False, use_python_cmath=False, use_interval=False): self.print_lambda = print_lambda self.use_evalf = use_evalf self.float_wrap_evalf = float_wrap_evalf self.complex_wrap_evalf = complex_wrap_evalf self.use_np = use_np self.use_python_math = use_python_math self.use_python_cmath = use_python_cmath self.use_interval = use_interval # Constructing the argument string if not all([isinstance(a, Symbol) for a in args]): raise ValueError('The arguments must be Symbols.') else: argstr = ', '.join([str(a) for a in args]) # Constructing the translation dictionaries and making the translation self.dict_str = self.get_dict_str() self.dict_fun = self.get_dict_fun() exprstr = str(expr) newexpr = self.tree2str_translate(self.str2tree(exprstr)) # Constructing the namespaces namespace = {} namespace.update(self.sympy_atoms_namespace(expr)) namespace.update(self.sympy_expression_namespace(expr)) # XXX Workaround # Ugly workaround because Pow(a,Half) prints as sqrt(a) # and sympy_expression_namespace can not catch it. from sympy import sqrt namespace.update({'sqrt': sqrt}) # End workaround. if use_python_math: namespace.update({'math': __import__('math')}) if use_python_cmath: namespace.update({'cmath': __import__('cmath')}) if use_np: try: namespace.update({'np': __import__('numpy')}) except ImportError: raise ImportError( 'experimental_lambdify failed to import numpy.') if use_interval: namespace.update({'imath': __import__( 'sympy.plotting.intervalmath', fromlist=['intervalmath'])}) namespace.update({'math': __import__('math')}) # Construct the lambda if self.print_lambda: print(newexpr) eval_str = 'lambda %s : ( %s )' % (argstr, newexpr) exec_("from __future__ import division; MYNEWLAMBDA = %s" % eval_str, namespace) self.lambda_func = namespace['MYNEWLAMBDA'] ############################################################################## # Dicts for translating from sympy to other modules ############################################################################## ### # builtins ### # Functions with different names in builtins builtin_functions_different = { 'Min': 'min', 'Max': 'max', 'Abs': 'abs', } # Strings that should be translated builtin_not_functions = { 'I': '1j', 'oo': '1e400', } ### # numpy ### # Functions that are the same in numpy numpy_functions_same = [ 'sin', 'cos', 'tan', 'sinh', 'cosh', 'tanh', 'exp', 'log', 'sqrt', 'floor', 'conjugate', ] # Functions with different names in numpy numpy_functions_different = { "acos": "arccos", "acosh": "arccosh", "arg": "angle", "asin": "arcsin", "asinh": "arcsinh", "atan": "arctan", "atan2": "arctan2", "atanh": "arctanh", "ceiling": "ceil", "im": "imag", "ln": "log", "Max": "amax", "Min": "amin", "re": "real", "Abs": "abs", } # Strings that should be translated numpy_not_functions = { 'pi': 'np.pi', 'oo': 'np.inf', 'E': 'np.e', } ### # python math ### # Functions that are the same in math math_functions_same = [ 'sin', 'cos', 'tan', 'asin', 'acos', 'atan', 'atan2', 'sinh', 'cosh', 'tanh', 'asinh', 'acosh', 'atanh', 'exp', 'log', 'erf', 'sqrt', 'floor', 'factorial', 'gamma', ] # Functions with different names in math math_functions_different = { 'ceiling': 'ceil', 'ln': 'log', 'loggamma': 'lgamma' } # Strings that should be translated math_not_functions = { 'pi': 'math.pi', 'E': 'math.e', } ### # python cmath ### # Functions that are the same in cmath cmath_functions_same = [ 'sin', 'cos', 'tan', 'asin', 'acos', 'atan', 'sinh', 'cosh', 'tanh', 'asinh', 'acosh', 'atanh', 'exp', 'log', 'sqrt', ] # Functions with different names in cmath cmath_functions_different = { 'ln': 'log', 'arg': 'phase', } # Strings that should be translated cmath_not_functions = { 'pi': 'cmath.pi', 'E': 'cmath.e', } ### # intervalmath ### interval_not_functions = { 'pi': 'math.pi', 'E': 'math.e' } interval_functions_same = [ 'sin', 'cos', 'exp', 'tan', 'atan', 'log', 'sqrt', 'cosh', 'sinh', 'tanh', 'floor', 'acos', 'asin', 'acosh', 'asinh', 'atanh', 'Abs', 'And', 'Or' ] interval_functions_different = { 'Min': 'imin', 'Max': 'imax', 'ceiling': 'ceil', } ### # mpmath, etc ### #TODO ### # Create the final ordered tuples of dictionaries ### # For strings def get_dict_str(self): dict_str = dict(self.builtin_not_functions) if self.use_np: dict_str.update(self.numpy_not_functions) if self.use_python_math: dict_str.update(self.math_not_functions) if self.use_python_cmath: dict_str.update(self.cmath_not_functions) if self.use_interval: dict_str.update(self.interval_not_functions) return dict_str # For functions def get_dict_fun(self): dict_fun = dict(self.builtin_functions_different) if self.use_np: for s in self.numpy_functions_same: dict_fun[s] = 'np.' + s for k, v in self.numpy_functions_different.items(): dict_fun[k] = 'np.' + v if self.use_python_math: for s in self.math_functions_same: dict_fun[s] = 'math.' + s for k, v in self.math_functions_different.items(): dict_fun[k] = 'math.' + v if self.use_python_cmath: for s in self.cmath_functions_same: dict_fun[s] = 'cmath.' + s for k, v in self.cmath_functions_different.items(): dict_fun[k] = 'cmath.' + v if self.use_interval: for s in self.interval_functions_same: dict_fun[s] = 'imath.' + s for k, v in self.interval_functions_different.items(): dict_fun[k] = 'imath.' + v return dict_fun ############################################################################## # The translator functions, tree parsers, etc. ############################################################################## def str2tree(self, exprstr): """Converts an expression string to a tree. Functions are represented by ('func_name(', tree_of_arguments). Other expressions are (head_string, mid_tree, tail_str). Expressions that do not contain functions are directly returned. Examples: >>> from sympy.abc import x, y, z >>> from sympy import Integral, sin >>> from sympy.plotting.experimental_lambdify import Lambdifier >>> str2tree = Lambdifier([x], x).str2tree >>> str2tree(str(Integral(x, (x, 1, y)))) ('', ('Integral(', 'x, (x, 1, y)'), ')') >>> str2tree(str(x+y)) 'x + y' >>> str2tree(str(x+y*sin(z)+1)) ('x + y*', ('sin(', 'z'), ') + 1') >>> str2tree('sin(y*(y + 1.1) + (sin(y)))') ('', ('sin(', ('y*(y + 1.1) + (', ('sin(', 'y'), '))')), ')') """ #matches the first 'function_name(' first_par = re.search(r'(\w+\()', exprstr) if first_par is None: return exprstr else: start = first_par.start() end = first_par.end() head = exprstr[:start] func = exprstr[start:end] tail = exprstr[end:] count = 0 for i, c in enumerate(tail): if c == '(': count += 1 elif c == ')': count -= 1 if count == -1: break func_tail = self.str2tree(tail[:i]) tail = self.str2tree(tail[i:]) return (head, (func, func_tail), tail) @classmethod def tree2str(cls, tree): """Converts a tree to string without translations. Examples: >>> from sympy.abc import x, y, z >>> from sympy import Integral, sin >>> from sympy.plotting.experimental_lambdify import Lambdifier >>> str2tree = Lambdifier([x], x).str2tree >>> tree2str = Lambdifier([x], x).tree2str >>> tree2str(str2tree(str(x+y*sin(z)+1))) 'x + y*sin(z) + 1' """ if isinstance(tree, str): return tree else: return ''.join(map(cls.tree2str, tree)) def tree2str_translate(self, tree): """Converts a tree to string with translations. Function names are translated by translate_func. Other strings are translated by translate_str. """ if isinstance(tree, str): return self.translate_str(tree) elif isinstance(tree, tuple) and len(tree) == 2: return self.translate_func(tree[0][:-1], tree[1]) else: return ''.join([self.tree2str_translate(t) for t in tree]) def translate_str(self, estr): """Translate substrings of estr using in order the dictionaries in dict_tuple_str.""" for pattern, repl in self.dict_str.items(): estr = re.sub(pattern, repl, estr) return estr def translate_func(self, func_name, argtree): """Translate function names and the tree of arguments. If the function name is not in the dictionaries of dict_tuple_fun then the function is surrounded by a float((...).evalf()). The use of float is necessary as np.(sympy.Float(..)) raises an error.""" if func_name in self.dict_fun: new_name = self.dict_fun[func_name] argstr = self.tree2str_translate(argtree) return new_name + '(' + argstr else: template = '(%s(%s)).evalf(' if self.use_evalf else '%s(%s' if self.float_wrap_evalf: template = 'float(%s)' % template elif self.complex_wrap_evalf: template = 'complex(%s)' % template return template % (func_name, self.tree2str(argtree)) ############################################################################## # The namespace constructors ############################################################################## @classmethod def sympy_expression_namespace(cls, expr): """Traverses the (func, args) tree of an expression and creates a sympy namespace. All other modules are imported only as a module name. That way the namespace is not poluted and rests quite small. It probably causes much more variable lookups and so it takes more time, but there are no tests on that for the moment.""" if expr is None: return {} else: funcname = str(expr.func) # XXX Workaround # Here we add an ugly workaround because str(func(x)) # is not always the same as str(func). Eg # >>> str(Integral(x)) # "Integral(x)" # >>> str(Integral) # "" # >>> str(sqrt(x)) # "sqrt(x)" # >>> str(sqrt) # "" # >>> str(sin(x)) # "sin(x)" # >>> str(sin) # "sin" # Either one of those can be used but not all at the same time. # The code considers the sin example as the right one. regexlist = [ r'$', # the example Integral r'$', # the example sqrt ] for r in regexlist: m = re.match(r, funcname) if m is not None: funcname = m.groups()[0] # End of the workaround # XXX debug: print funcname args_dict = {} for a in expr.args: if (isinstance(a, Symbol) or isinstance(a, NumberSymbol) or a in [I, zoo, oo]): continue else: args_dict.update(cls.sympy_expression_namespace(a)) args_dict.update({funcname: expr.func}) return args_dict @staticmethod def sympy_atoms_namespace(expr): """For no real reason this function is separated from sympy_expression_namespace. It can be moved to it.""" atoms = expr.atoms(Symbol, NumberSymbol, I, zoo, oo) d = {} for a in atoms: # XXX debug: print 'atom:' + str(a) d[str(a)] = a return d sympy-0.7.4.1/sympy/plotting/textplot.py0000644000175000017500000000372012253362407020524 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import * def textplot(expr, a, b, W=55, H=18): """ Print a crude ASCII art plot of the SymPy expression 'expr' (which should contain a single symbol, e.g. x or something else) over the interval [a, b]. Examples ======== textplot(sin(t)*t, 0, 15) """ free = expr.free_symbols assert len(free) <= 1 x = free.pop() if free else Dummy() f = lambdify([x], expr) a = float(a) b = float(b) # Calculate function values y = [0] * W for x in range(W): try: y[x] = f(a + (b - a)/float(W)*x) except (TypeError, ValueError): y[x] = 0 # Normalize height to screen space ma = max(y) mi = min(y) if ma == mi: if ma: mi, ma = sorted([0, 2*ma]) else: mi, ma = -1, 1 for x in range(W): y[x] = int(float(H)*(y[x] - mi)/(ma - mi)) margin = 7 print for h in range(H - 1, -1, -1): s = [' '] * W for x in range(W): if y[x] == h: if (x == 0 or y[x - 1] == h - 1) and (x == W - 1 or y[x + 1] == h + 1): s[x] = '/' elif (x == 0 or y[x - 1] == h + 1) and (x == W - 1 or y[x + 1] == h - 1): s[x] = '\\' else: s[x] = '.' # Print y values if h == H - 1: prefix = ("%g" % ma).rjust(margin)[:margin] elif h == H//2: prefix = ("%g" % ((mi + ma)/2)).rjust(margin)[:margin] elif h == 0: prefix = ("%g" % mi).rjust(margin)[:margin] else: prefix = " "*margin s = "".join(s) if h == H//2: s = s.replace(" ", "-") print(prefix + " | " + s) # Print x values bottom = " " * (margin + 3) bottom += ("%g" % a).ljust(W//2 - 4) bottom += ("%g" % ((a + b)/2)).ljust(W//2) bottom += "%g" % b print(bottom) sympy-0.7.4.1/sympy/plotting/proxy_pyglet.py0000644000175000017500000000307512253362407021411 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.utilities.exceptions import SymPyDeprecationWarning from .pygletplot import PygletPlot def Plot(*args, **kwargs): """ A temporary proxy for an interface under deprecation. This proxy is the one imported by `from sympy import *`. The Plot class will change in future versions of sympy to use the new plotting module. That new plotting module is already used by the plot() function (lowercase). To write code compatible with future versions of sympy use that function (plot() lowercase). Or if you want to use the old plotting module just import it directly: `from sympy.plotting.pygletplot import PygletPlot` To use Plot from the new plotting module do: `from sympy.plotting.plot import Plot` In future version of sympy you will also be able to use `from sympy.plotting import Plot` but in the current version this will import this proxy object. It's done for backward compatibility. The old plotting module is not deprecated. Only the location will change. The new location is sympy.plotting.pygletplot. """ SymPyDeprecationWarning(value="This interface will change in future " "versions of SymPy. As a precaution use the plot() function " "(lowercase), or use sympy.plotting.pygletplot.PygletPlot to " "continue using Pyglet. See the docstring of this function for " "details.", feature="Plot as an interface to Pyglet", issue=2845, deprecated_since_version="0.7.2" ).warn() return PygletPlot(*args, **kwargs) sympy-0.7.4.1/sympy/plotting/pygletplot/0000755000175000017500000000000012253362407020470 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/plotting/pygletplot/color_scheme.py0000644000175000017500000003030012253362407023500 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import Basic, Symbol, symbols, lambdify from util import interpolate, rinterpolate, create_bounds, update_bounds from sympy.core.compatibility import xrange class ColorGradient(object): colors = [0.4, 0.4, 0.4], [0.9, 0.9, 0.9] intervals = 0.0, 1.0 def __init__(self, *args): if len(args) == 2: self.colors = list(args) self.intervals = [0.0, 1.0] elif len(args) > 0: assert len(args) % 2 == 0 self.colors = [args[i] for i in xrange(1, len(args), 2)] self.intervals = [args[i] for i in xrange(0, len(args), 2)] assert len(self.colors) == len(self.intervals) def copy(self): c = ColorGradient() c.colors = [e[::] for e in self.colors] c.intervals = self.intervals[::] return c def _find_interval(self, v): m = len(self.intervals) i = 0 while i < m - 1 and self.intervals[i] <= v: i += 1 return i def _interpolate_axis(self, axis, v): i = self._find_interval(v) v = rinterpolate(self.intervals[i - 1], self.intervals[i], v) return interpolate(self.colors[i - 1][axis], self.colors[i][axis], v) def __call__(self, r, g, b): c = self._interpolate_axis return c(0, r), c(1, g), c(2, b) default_color_schemes = {} # defined at the bottom of this file class ColorScheme(object): def __init__(self, *args, **kwargs): self.args = args self.f, self.gradient = None, ColorGradient() if len(args) == 1 and not isinstance(args[0], Basic) and callable(args[0]): self.f = args[0] elif len(args) == 1 and isinstance(args[0], str): if args[0] in default_color_schemes: cs = default_color_schemes[args[0]] self.f, self.gradient = cs.f, cs.gradient.copy() else: self.f = lambdify('x,y,z,u,v', args[0]) else: self.f, self.gradient = self._interpret_args(args, kwargs) self._test_color_function() if not isinstance(self.gradient, ColorGradient): raise ValueError("Color gradient not properly initialized. " "(Not a ColorGradient instance.)") def _interpret_args(self, args, kwargs): f, gradient = None, self.gradient atoms, lists = self._sort_args(args) s = self._pop_symbol_list(lists) s = self._fill_in_vars(s) # prepare the error message for lambdification failure f_str = ', '.join(str(fa) for fa in atoms) s_str = (str(sa) for sa in s) s_str = ', '.join(sa for sa in s_str if sa.find('unbound') < 0) f_error = ValueError("Could not interpret arguments " "%s as functions of %s." % (f_str, s_str)) # try to lambdify args if len(atoms) == 1: fv = atoms[0] try: f = lambdify(s, [fv, fv, fv]) except TypeError: raise f_error elif len(atoms) == 3: fr, fg, fb = atoms try: f = lambdify(s, [fr, fg, fb]) except TypeError: raise f_error else: raise ValueError("A ColorScheme must provide 1 or 3 " "functions in x, y, z, u, and/or v.") # try to intrepret any given color information if len(lists) == 0: gargs = [] elif len(lists) == 1: gargs = lists[0] elif len(lists) == 2: try: (r1, g1, b1), (r2, g2, b2) = lists except TypeError: raise ValueError("If two color arguments are given, " "they must be given in the format " "(r1, g1, b1), (r2, g2, b2).") gargs = lists elif len(lists) == 3: try: (r1, r2), (g1, g2), (b1, b2) = lists except: raise ValueError("If three color arguments are given, " "they must be given in the format " "(r1, r2), (g1, g2), (b1, b2). To create " "a multi-step gradient, use the syntax " "[0, colorStart, step1, color1, ..., 1, " "colorEnd].") gargs = [[r1, g1, b1], [r2, g2, b2]] else: raise ValueError("Don't know what to do with collection " "arguments %s." % (', '.join(str(l) for l in lists))) if gargs: try: gradient = ColorGradient(*gargs) except Exception as ex: raise ValueError(("Could not initialize a gradient " "with arguments %s. Inner " "exception: %s") % (gargs, str(ex))) return f, gradient def _pop_symbol_list(self, lists): symbol_lists = [] for l in lists: mark = True for s in l: if s is not None and not isinstance(s, Symbol): mark = False break if mark: lists.remove(l) symbol_lists.append(l) if len(symbol_lists) == 1: return symbol_lists[0] elif len(symbol_lists) == 0: return [] else: raise ValueError("Only one list of Symbols " "can be given for a color scheme.") def _fill_in_vars(self, args): defaults = symbols('x,y,z,u,v') if len(args) == 0: return defaults if not isinstance(args, (tuple, list)): raise v_error if len(args) == 0: return defaults for s in args: if s is not None and not isinstance(s, Symbol): raise v_error # when vars are given explicitly, any vars # not given are marked 'unbound' as to not # be accidentally used in an expression vars = [Symbol('unbound%i' % (i)) for i in range(1, 6)] # interpret as t if len(args) == 1: vars[3] = args[0] # interpret as u,v elif len(args) == 2: if args[0] is not None: vars[3] = args[0] if args[1] is not None: vars[4] = args[1] # interpret as x,y,z elif len(args) >= 3: # allow some of x,y,z to be # left unbound if not given if args[0] is not None: vars[0] = args[0] if args[1] is not None: vars[1] = args[1] if args[2] is not None: vars[2] = args[2] # interpret the rest as t if len(args) >= 4: vars[3] = args[3] # ...or u,v if len(args) >= 5: vars[4] = args[4] return vars def _sort_args(self, args): atoms, lists = [], [] for a in args: if isinstance(a, (tuple, list)): lists.append(a) else: atoms.append(a) return atoms, lists def _test_color_function(self): if not callable(self.f): raise ValueError("Color function is not callable.") try: result = self.f(0, 0, 0, 0, 0) assert len(result) == 3 except TypeError as te: raise ValueError("Color function needs to accept x,y,z,u,v, " "as arguments even if it doesn't use all of them.") except AssertionError as ae: raise ValueError("Color function needs to return 3-tuple r,g,b.") except Exception as ie: pass # color function probably not valid at 0,0,0,0,0 def __call__(self, x, y, z, u, v): try: return self.f(x, y, z, u, v) except Exception as e: #print e return None def apply_to_curve(self, verts, u_set, set_len=None, inc_pos=None): """ Apply this color scheme to a set of vertices over a single independent variable u. """ bounds = create_bounds() cverts = list() if callable(set_len): set_len(len(u_set)*2) # calculate f() = r,g,b for each vert # and find the min and max for r,g,b for _u in xrange(len(u_set)): if verts[_u] is None: cverts.append(None) else: x, y, z = verts[_u] u, v = u_set[_u], None c = self(x, y, z, u, v) if c is not None: c = list(c) update_bounds(bounds, c) cverts.append(c) if callable(inc_pos): inc_pos() # scale and apply gradient for _u in xrange(len(u_set)): if cverts[_u] is not None: for _c in range(3): # scale from [f_min, f_max] to [0,1] cverts[_u][_c] = rinterpolate(bounds[_c][0], bounds[_c][1], cverts[_u][_c]) # apply gradient cverts[_u] = self.gradient(*cverts[_u]) if callable(inc_pos): inc_pos() return cverts def apply_to_surface(self, verts, u_set, v_set, set_len=None, inc_pos=None): """ Apply this color scheme to a set of vertices over two independent variables u and v. """ bounds = create_bounds() cverts = list() if callable(set_len): set_len(len(u_set)*len(v_set)*2) # calculate f() = r,g,b for each vert # and find the min and max for r,g,b for _u in xrange(len(u_set)): column = list() for _v in xrange(len(v_set)): if verts[_u][_v] is None: column.append(None) else: x, y, z = verts[_u][_v] u, v = u_set[_u], v_set[_v] c = self(x, y, z, u, v) if c is not None: c = list(c) update_bounds(bounds, c) column.append(c) if callable(inc_pos): inc_pos() cverts.append(column) # scale and apply gradient for _u in xrange(len(u_set)): for _v in xrange(len(v_set)): if cverts[_u][_v] is not None: # scale from [f_min, f_max] to [0,1] for _c in range(3): cverts[_u][_v][_c] = rinterpolate(bounds[_c][0], bounds[_c][1], cverts[_u][_v][_c]) # apply gradient cverts[_u][_v] = self.gradient(*cverts[_u][_v]) if callable(inc_pos): inc_pos() return cverts def str_base(self): return ", ".join(str(a) for a in self.args) def __repr__(self): return "%s" % (self.str_base()) x, y, z, t, u, v = symbols('x,y,z,t,u,v') default_color_schemes['rainbow'] = ColorScheme(z, y, x) default_color_schemes['zfade'] = ColorScheme(z, (0.4, 0.4, 0.97), (0.97, 0.4, 0.4), (None, None, z)) default_color_schemes['zfade3'] = ColorScheme(z, (None, None, z), [0.00, (0.2, 0.2, 1.0), 0.35, (0.2, 0.8, 0.4), 0.50, (0.3, 0.9, 0.3), 0.65, (0.4, 0.8, 0.2), 1.00, (1.0, 0.2, 0.2)]) default_color_schemes['zfade4'] = ColorScheme(z, (None, None, z), [0.0, (0.3, 0.3, 1.0), 0.30, (0.3, 1.0, 0.3), 0.55, (0.95, 1.0, 0.2), 0.65, (1.0, 0.95, 0.2), 0.85, (1.0, 0.7, 0.2), 1.0, (1.0, 0.3, 0.2)]) sympy-0.7.4.1/sympy/plotting/pygletplot/plot_mode_base.py0000644000175000017500000002643612253362407024031 0ustar georgeskgeorgeskfrom __future__ import print_function, division from pyglet.gl import * from plot_mode import PlotMode from threading import Thread, Event, RLock from color_scheme import ColorScheme from sympy.core import S from sympy.core.compatibility import is_sequence from time import sleep import warnings class PlotModeBase(PlotMode): """ Intended parent class for plotting modes. Provides base functionality in conjunction with its parent, PlotMode. """ ## ## Class-Level Attributes ## """ The following attributes are meant to be set at the class level, and serve as parameters to the plot mode registry (in PlotMode). See plot_modes.py for concrete examples. """ """ i_vars 'x' for Cartesian2D 'xy' for Cartesian3D etc. d_vars 'y' for Cartesian2D 'r' for Polar etc. """ i_vars, d_vars = '', '' """ intervals Default intervals for each i_var, and in the same order. Specified [min, max, steps]. No variable can be given (it is bound later). """ intervals = [] """ aliases A list of strings which can be used to access this mode. 'cartesian' for Cartesian2D and Cartesian3D 'polar' for Polar 'cylindrical', 'polar' for Cylindrical Note that _init_mode chooses the first alias in the list as the mode's primary_alias, which will be displayed to the end user in certain contexts. """ aliases = [] """ is_default Whether to set this mode as the default for arguments passed to PlotMode() containing the same number of d_vars as this mode and at most the same number of i_vars. """ is_default = False """ All of the above attributes are defined in PlotMode. The following ones are specific to PlotModeBase. """ """ A list of the render styles. Do not modify. """ styles = {'wireframe': 1, 'solid': 2, 'both': 3} """ style_override Always use this style if not blank. """ style_override = '' """ default_wireframe_color default_solid_color Can be used when color is None or being calculated. Used by PlotCurve and PlotSurface, but not anywhere in PlotModeBase. """ default_wireframe_color = (0.85, 0.85, 0.85) default_solid_color = (0.6, 0.6, 0.9) default_rot_preset = 'xy' ## ## Instance-Level Attributes ## ## 'Abstract' member functions def _get_evaluator(self): if self.use_lambda_eval: try: e = self._get_lambda_evaluator() return e except: warnings.warn("\nWarning: creating lambda evaluator failed. " "Falling back on sympy subs evaluator.") return self._get_sympy_evaluator() def _get_sympy_evaluator(self): raise NotImplementedError() def _get_lambda_evaluator(self): raise NotImplementedError() def _on_calculate_verts(self): raise NotImplementedError() def _on_calculate_cverts(self): raise NotImplementedError() ## Base member functions def __init__(self, *args, **kwargs): self.verts = [] self.cverts = [] self.bounds = [[S.Infinity, -S.Infinity, 0], [S.Infinity, -S.Infinity, 0], [S.Infinity, -S.Infinity, 0]] self.cbounds = [[S.Infinity, -S.Infinity, 0], [S.Infinity, -S.Infinity, 0], [S.Infinity, -S.Infinity, 0]] self._draw_lock = RLock() self._calculating_verts = Event() self._calculating_cverts = Event() self._calculating_verts_pos = 0.0 self._calculating_verts_len = 0.0 self._calculating_cverts_pos = 0.0 self._calculating_cverts_len = 0.0 self._max_render_stack_size = 3 self._draw_wireframe = [-1] self._draw_solid = [-1] self._style = None self._color = None self.predraw = [] self.postdraw = [] self.use_lambda_eval = self.options.pop('use_sympy_eval', None) is None self.style = self.options.pop('style', '') self.color = self.options.pop('color', 'rainbow') self.bounds_callback = kwargs.pop('bounds_callback', None) self._on_calculate() def synchronized(f): def w(self, *args, **kwargs): self._draw_lock.acquire() try: r = f(self, *args, **kwargs) return r finally: self._draw_lock.release() return w @synchronized def push_wireframe(self, function): """ Push a function which performs gl commands used to build a display list. (The list is built outside of the function) """ assert callable(function) self._draw_wireframe.append(function) if len(self._draw_wireframe) > self._max_render_stack_size: del self._draw_wireframe[1] # leave marker element @synchronized def push_solid(self, function): """ Push a function which performs gl commands used to build a display list. (The list is built outside of the function) """ assert callable(function) self._draw_solid.append(function) if len(self._draw_solid) > self._max_render_stack_size: del self._draw_solid[1] # leave marker element def _create_display_list(self, function): dl = glGenLists(1) glNewList(dl, GL_COMPILE) function() glEndList() return dl def _render_stack_top(self, render_stack): top = render_stack[-1] if top == -1: return -1 # nothing to display elif callable(top): dl = self._create_display_list(top) render_stack[-1] = (dl, top) return dl # display newly added list elif len(top) == 2: if GL_TRUE == glIsList(top[0]): return top[0] # display stored list dl = self._create_display_list(top[1]) render_stack[-1] = (dl, top[1]) return dl # display regenerated list def _draw_solid_display_list(self, dl): glPushAttrib(GL_ENABLE_BIT | GL_POLYGON_BIT) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) glCallList(dl) glPopAttrib() def _draw_wireframe_display_list(self, dl): glPushAttrib(GL_ENABLE_BIT | GL_POLYGON_BIT) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) glEnable(GL_POLYGON_OFFSET_LINE) glPolygonOffset(-0.005, -50.0) glCallList(dl) glPopAttrib() @synchronized def draw(self): for f in self.predraw: if callable(f): f() if self.style_override: style = self.styles[self.style_override] else: style = self.styles[self._style] # Draw solid component if style includes solid if style & 2: dl = self._render_stack_top(self._draw_solid) if dl > 0 and GL_TRUE == glIsList(dl): self._draw_solid_display_list(dl) # Draw wireframe component if style includes wireframe if style & 1: dl = self._render_stack_top(self._draw_wireframe) if dl > 0 and GL_TRUE == glIsList(dl): self._draw_wireframe_display_list(dl) for f in self.postdraw: if callable(f): f() def _on_change_color(self, color): Thread(target=self._calculate_cverts).start() def _on_calculate(self): Thread(target=self._calculate_all).start() def _calculate_all(self): self._calculate_verts() self._calculate_cverts() def _calculate_verts(self): if self._calculating_verts.isSet(): return self._calculating_verts.set() try: self._on_calculate_verts() finally: self._calculating_verts.clear() if callable(self.bounds_callback): self.bounds_callback() def _calculate_cverts(self): if self._calculating_verts.isSet(): return while self._calculating_cverts.isSet(): sleep(0) # wait for previous calculation self._calculating_cverts.set() try: self._on_calculate_cverts() finally: self._calculating_cverts.clear() def _get_calculating_verts(self): return self._calculating_verts.isSet() def _get_calculating_verts_pos(self): return self._calculating_verts_pos def _get_calculating_verts_len(self): return self._calculating_verts_len def _get_calculating_cverts(self): return self._calculating_cverts.isSet() def _get_calculating_cverts_pos(self): return self._calculating_cverts_pos def _get_calculating_cverts_len(self): return self._calculating_cverts_len ## Property handlers def _get_style(self): return self._style @synchronized def _set_style(self, v): if v is None: return if v == '': step_max = 0 for i in self.intervals: if i.v_steps is None: continue step_max = max([step_max, i.v_steps]) v = ['both', 'solid'][step_max > 40] #try: assert v in self.styles if v == self._style: return self._style = v #except Exception as e: #raise RuntimeError(("Style change failed. " # "Reason: %s is not a valid " # "style. Use one of %s.") % # (str(v), ', '.join(self.styles.iterkeys()))) def _get_color(self): return self._color @synchronized def _set_color(self, v): try: if v is not None: if is_sequence(v): v = ColorScheme(*v) else: v = ColorScheme(v) if repr(v) == repr(self._color): return self._on_change_color(v) self._color = v except Exception as e: raise RuntimeError(("Color change failed. " "Reason: %s" % (str(e)))) style = property(_get_style, _set_style) color = property(_get_color, _set_color) calculating_verts = property(_get_calculating_verts) calculating_verts_pos = property(_get_calculating_verts_pos) calculating_verts_len = property(_get_calculating_verts_len) calculating_cverts = property(_get_calculating_cverts) calculating_cverts_pos = property(_get_calculating_cverts_pos) calculating_cverts_len = property(_get_calculating_cverts_len) ## String representations def __str__(self): f = ", ".join(str(d) for d in self.d_vars) o = "'mode=%s'" % (self.primary_alias) return ", ".join([f, o]) def __repr__(self): f = ", ".join(str(d) for d in self.d_vars) i = ", ".join(str(i) for i in self.intervals) d = [('mode', self.primary_alias), ('color', str(self.color)), ('style', str(self.style))] o = "'%s'" % (("; ".join("%s=%s" % (k, v) for k, v in d if v != 'None'))) return ", ".join([f, i, o]) sympy-0.7.4.1/sympy/plotting/pygletplot/plot_curve.py0000644000175000017500000000544512253362407023234 0ustar georgeskgeorgeskfrom __future__ import print_function, division from pyglet.gl import * from plot_mode_base import PlotModeBase from sympy.core import S from sympy.core.compatibility import xrange class PlotCurve(PlotModeBase): style_override = 'wireframe' def _on_calculate_verts(self): self.t_interval = self.intervals[0] self.t_set = list(self.t_interval.frange()) self.bounds = [[S.Infinity, -S.Infinity, 0], [S.Infinity, -S.Infinity, 0], [S.Infinity, -S.Infinity, 0]] evaluate = self._get_evaluator() self._calculating_verts_pos = 0.0 self._calculating_verts_len = float(self.t_interval.v_len) self.verts = list() b = self.bounds for t in self.t_set: try: _e = evaluate(t) # calculate vertex except (NameError, ZeroDivisionError): _e = None if _e is not None: # update bounding box for axis in range(3): b[axis][0] = min([b[axis][0], _e[axis]]) b[axis][1] = max([b[axis][1], _e[axis]]) self.verts.append(_e) self._calculating_verts_pos += 1.0 for axis in range(3): b[axis][2] = b[axis][1] - b[axis][0] if b[axis][2] == 0.0: b[axis][2] = 1.0 self.push_wireframe(self.draw_verts(False)) def _on_calculate_cverts(self): if not self.verts or not self.color: return def set_work_len(n): self._calculating_cverts_len = float(n) def inc_work_pos(): self._calculating_cverts_pos += 1.0 set_work_len(1) self._calculating_cverts_pos = 0 self.cverts = self.color.apply_to_curve(self.verts, self.t_set, set_len=set_work_len, inc_pos=inc_work_pos) self.push_wireframe(self.draw_verts(True)) def calculate_one_cvert(self, t): vert = self.verts[t] return self.color(vert[0], vert[1], vert[2], self.t_set[t], None) def draw_verts(self, use_cverts): def f(): glBegin(GL_LINE_STRIP) for t in xrange(len(self.t_set)): p = self.verts[t] if p is None: glEnd() glBegin(GL_LINE_STRIP) continue if use_cverts: c = self.cverts[t] if c is None: c = (0, 0, 0) glColor3f(*c) else: glColor3f(*self.default_wireframe_color) glVertex3f(*p) glEnd() return f sympy-0.7.4.1/sympy/plotting/pygletplot/plot_axes.py0000644000175000017500000002062612253362407023046 0ustar georgeskgeorgeskfrom __future__ import print_function, division from pyglet.gl import * from pyglet import font from plot_object import PlotObject from util import strided_range, billboard_matrix from util import get_direction_vectors from util import dot_product, vec_sub, vec_mag from sympy.core import S from sympy.core.compatibility import is_sequence class PlotAxes(PlotObject): def __init__(self, *args, **kwargs): # initialize style parameter style = kwargs.pop('style', '').lower() # allow alias kwargs to override style kwarg if kwargs.pop('none', None) is not None: style = 'none' if kwargs.pop('frame', None) is not None: style = 'frame' if kwargs.pop('box', None) is not None: style = 'box' if kwargs.pop('ordinate', None) is not None: style = 'ordinate' if style in ['', 'ordinate']: self._render_object = PlotAxesOrdinate(self) elif style in ['frame', 'box']: self._render_object = PlotAxesFrame(self) elif style in ['none']: self._render_object = None else: raise ValueError(("Unrecognized axes style %s.") % (style)) # initialize stride parameter stride = kwargs.pop('stride', 0.25) try: stride = eval(stride) except TypeError: pass if is_sequence(stride): assert len(stride) == 3 self._stride = stride else: self._stride = [stride, stride, stride] self._tick_length = float(kwargs.pop('tick_length', 0.1)) # setup bounding box and ticks self._origin = [0, 0, 0] self.reset_bounding_box() def flexible_boolean(input, default): if input in [True, False]: return input if input in ['f', 'F', 'false', 'False']: return False if input in ['t', 'T', 'true', 'True']: return True return default # initialize remaining parameters self.visible = flexible_boolean(kwargs.pop('visible', ''), True) self._overlay = flexible_boolean(kwargs.pop('overlay', ''), True) self._colored = flexible_boolean(kwargs.pop('colored', ''), False) self._label_axes = flexible_boolean( kwargs.pop('label_axes', ''), False) self._label_ticks = flexible_boolean( kwargs.pop('label_ticks', ''), True) # setup label font self.font_face = kwargs.pop('font_face', 'Arial') self.font_size = kwargs.pop('font_size', 28) # this is also used to reinit the # font on window close/reopen self.reset_resources() def reset_resources(self): self.label_font = None def reset_bounding_box(self): self._bounding_box = [[None, None], [None, None], [None, None]] self._axis_ticks = [[], [], []] def draw(self): if self._render_object: glPushAttrib(GL_ENABLE_BIT | GL_POLYGON_BIT | GL_DEPTH_BUFFER_BIT) if self._overlay: glDisable(GL_DEPTH_TEST) self._render_object.draw() glPopAttrib() def adjust_bounds(self, child_bounds): b = self._bounding_box c = child_bounds for i in [0, 1, 2]: if abs(c[i][0]) is S.Infinity or abs(c[i][1]) is S.Infinity: continue b[i][0] = [min([b[i][0], c[i][0]]), c[i][0]][b[i][0] is None] b[i][1] = [max([b[i][1], c[i][1]]), c[i][1]][b[i][1] is None] self._recalculate_axis_ticks(i) def _recalculate_axis_ticks(self, axis): b = self._bounding_box if b[axis][0] is None or b[axis][1] is None: self._axis_ticks[axis] = [] else: self._axis_ticks[axis] = strided_range(b[axis][0], b[axis][1], self._stride[axis]) def toggle_visible(self): self.visible = not self.visible def toggle_colors(self): self._colored = not self._colored class PlotAxesBase(PlotObject): def __init__(self, parent_axes): self._p = parent_axes def draw(self): color = [([0.2, 0.1, 0.3], [0.2, 0.1, 0.3], [0.2, 0.1, 0.3]), ([0.9, 0.3, 0.5], [0.5, 1.0, 0.5], [0.3, 0.3, 0.9])][self._p._colored] self.draw_background(color) self.draw_axis(2, color[2]) self.draw_axis(1, color[1]) self.draw_axis(0, color[0]) def draw_background(self, color): pass # optional def draw_axis(self, axis, color): raise NotImplementedError() def draw_text(self, text, position, color, scale=1.0): if len(color) == 3: color = (color[0], color[1], color[2], 1.0) if self._p.label_font is None: self._p.label_font = font.load(self._p.font_face, self._p.font_size, bold=True, italic=False) label = font.Text(self._p.label_font, text, color=color, valign=font.Text.BASELINE, halign=font.Text.CENTER) glPushMatrix() glTranslatef(*position) billboard_matrix() scale_factor = 0.005 * scale glScalef(scale_factor, scale_factor, scale_factor) glColor4f(0, 0, 0, 0) label.draw() glPopMatrix() def draw_line(self, v, color): o = self._p._origin glBegin(GL_LINES) glColor3f(*color) glVertex3f(v[0][0] + o[0], v[0][1] + o[1], v[0][2] + o[2]) glVertex3f(v[1][0] + o[0], v[1][1] + o[1], v[1][2] + o[2]) glEnd() class PlotAxesOrdinate(PlotAxesBase): def __init__(self, parent_axes): super(PlotAxesOrdinate, self).__init__(parent_axes) def draw_axis(self, axis, color): ticks = self._p._axis_ticks[axis] radius = self._p._tick_length / 2.0 if len(ticks) < 2: return # calculate the vector for this axis axis_lines = [[0, 0, 0], [0, 0, 0]] axis_lines[0][axis], axis_lines[1][axis] = ticks[0], ticks[-1] axis_vector = vec_sub(axis_lines[1], axis_lines[0]) # calculate angle to the z direction vector pos_z = get_direction_vectors()[2] d = abs(dot_product(axis_vector, pos_z)) d = d / vec_mag(axis_vector) # don't draw labels if we're looking down the axis labels_visible = abs(d - 1.0) > 0.02 # draw the ticks and labels for tick in ticks: self.draw_tick_line(axis, color, radius, tick, labels_visible) # draw the axis line and labels self.draw_axis_line(axis, color, ticks[0], ticks[-1], labels_visible) def draw_axis_line(self, axis, color, a_min, a_max, labels_visible): axis_line = [[0, 0, 0], [0, 0, 0]] axis_line[0][axis], axis_line[1][axis] = a_min, a_max self.draw_line(axis_line, color) if labels_visible: self.draw_axis_line_labels(axis, color, axis_line) def draw_axis_line_labels(self, axis, color, axis_line): if not self._p._label_axes: return axis_labels = [axis_line[0][::], axis_line[1][::]] axis_labels[0][axis] -= 0.3 axis_labels[1][axis] += 0.3 a_str = ['X', 'Y', 'Z'][axis] self.draw_text("-" + a_str, axis_labels[0], color) self.draw_text("+" + a_str, axis_labels[1], color) def draw_tick_line(self, axis, color, radius, tick, labels_visible): tick_axis = {0: 1, 1: 0, 2: 1}[axis] tick_line = [[0, 0, 0], [0, 0, 0]] tick_line[0][axis] = tick_line[1][axis] = tick tick_line[0][tick_axis], tick_line[1][tick_axis] = -radius, radius self.draw_line(tick_line, color) if labels_visible: self.draw_tick_line_label(axis, color, radius, tick) def draw_tick_line_label(self, axis, color, radius, tick): if not self._p._label_axes: return tick_label_vector = [0, 0, 0] tick_label_vector[axis] = tick tick_label_vector[{0: 1, 1: 0, 2: 1}[axis]] = [-1, 1, 1][ axis] * radius * 3.5 self.draw_text(str(tick), tick_label_vector, color, scale=0.5) class PlotAxesFrame(PlotAxesBase): def __init__(self, parent_axes): super(PlotAxesFrame, self).__init__(parent_axes) def draw_background(self, color): pass def draw_axis(self, axis, color): raise NotImplementedError() sympy-0.7.4.1/sympy/plotting/pygletplot/plot_modes.py0000644000175000017500000001226412253362407023214 0ustar georgeskgeorgeskfrom __future__ import print_function, division from plot_curve import PlotCurve from plot_surface import PlotSurface from sympy import pi, lambdify from sympy.functions import sin, cos from math import sin as p_sin from math import cos as p_cos def float_vec3(f): def inner(*args): v = f(*args) return float(v[0]), float(v[1]), float(v[2]) return inner class Cartesian2D(PlotCurve): i_vars, d_vars = 'x', 'y' intervals = [[-5, 5, 100]] aliases = ['cartesian'] is_default = True def _get_sympy_evaluator(self): fy = self.d_vars[0] x = self.t_interval.v @float_vec3 def e(_x): return (_x, fy.subs(x, _x), 0.0) return e def _get_lambda_evaluator(self): fy = self.d_vars[0] x = self.t_interval.v return lambdify([x], [x, fy, 0.0]) class Cartesian3D(PlotSurface): i_vars, d_vars = 'xy', 'z' intervals = [[-1, 1, 40], [-1, 1, 40]] aliases = ['cartesian', 'monge'] is_default = True def _get_sympy_evaluator(self): fz = self.d_vars[0] x = self.u_interval.v y = self.v_interval.v @float_vec3 def e(_x, _y): return (_x, _y, fz.subs(x, _x).subs(y, _y)) return e def _get_lambda_evaluator(self): fz = self.d_vars[0] x = self.u_interval.v y = self.v_interval.v return lambdify([x, y], [x, y, fz]) class ParametricCurve2D(PlotCurve): i_vars, d_vars = 't', 'xy' intervals = [[0, 2*pi, 100]] aliases = ['parametric'] is_default = True def _get_sympy_evaluator(self): fx, fy = self.d_vars t = self.t_interval.v @float_vec3 def e(_t): return (fx.subs(t, _t), fy.subs(t, _t), 0.0) return e def _get_lambda_evaluator(self): fx, fy = self.d_vars t = self.t_interval.v return lambdify([t], [fx, fy, 0.0]) class ParametricCurve3D(PlotCurve): i_vars, d_vars = 't', 'xyz' intervals = [[0, 2*pi, 100]] aliases = ['parametric'] is_default = True def _get_sympy_evaluator(self): fx, fy, fz = self.d_vars t = self.t_interval.v @float_vec3 def e(_t): return (fx.subs(t, _t), fy.subs(t, _t), fz.subs(t, _t)) return e def _get_lambda_evaluator(self): fx, fy, fz = self.d_vars t = self.t_interval.v return lambdify([t], [fx, fy, fz]) class ParametricSurface(PlotSurface): i_vars, d_vars = 'uv', 'xyz' intervals = [[-1, 1, 40], [-1, 1, 40]] aliases = ['parametric'] is_default = True def _get_sympy_evaluator(self): fx, fy, fz = self.d_vars u = self.u_interval.v v = self.v_interval.v @float_vec3 def e(_u, _v): return (fx.subs(u, _u).subs(v, _v), fy.subs(u, _u).subs(v, _v), fz.subs(u, _u).subs(v, _v)) return e def _get_lambda_evaluator(self): fx, fy, fz = self.d_vars u = self.u_interval.v v = self.v_interval.v return lambdify([u, v], [fx, fy, fz]) class Polar(PlotCurve): i_vars, d_vars = 't', 'r' intervals = [[0, 2*pi, 100]] aliases = ['polar'] is_default = False def _get_sympy_evaluator(self): fr = self.d_vars[0] t = self.t_interval.v def e(_t): _r = float(fr.subs(t, _t)) return (_r*p_cos(_t), _r*p_sin(_t), 0.0) return e def _get_lambda_evaluator(self): fr = self.d_vars[0] t = self.t_interval.v fx, fy = fr*cos(t), fr*sin(t) return lambdify([t], [fx, fy, 0.0]) class Cylindrical(PlotSurface): i_vars, d_vars = 'th', 'r' intervals = [[0, 2*pi, 40], [-1, 1, 20]] aliases = ['cylindrical', 'polar'] is_default = False def _get_sympy_evaluator(self): fr = self.d_vars[0] t = self.u_interval.v h = self.v_interval.v def e(_t, _h): _r = float(fr.subs(t, _t).subs(h, _h)) return (_r*p_cos(_t), _r*p_sin(_t), _h) return e def _get_lambda_evaluator(self): fr = self.d_vars[0] t = self.u_interval.v h = self.v_interval.v fx, fy = fr*cos(t), fr*sin(t) return lambdify([t, h], [fx, fy, h]) class Spherical(PlotSurface): i_vars, d_vars = 'tp', 'r' intervals = [[0, 2*pi, 40], [0, pi, 20]] aliases = ['spherical'] is_default = False def _get_sympy_evaluator(self): fr = self.d_vars[0] t = self.u_interval.v p = self.v_interval.v def e(_t, _p): _r = float(fr.subs(t, _t).subs(p, _p)) return (_r*p_cos(_t)*p_sin(_p), _r*p_sin(_t)*p_sin(_p), _r*p_cos(_p)) return e def _get_lambda_evaluator(self): fr = self.d_vars[0] t = self.u_interval.v p = self.v_interval.v fx = fr * cos(t) * sin(p) fy = fr * sin(t) * sin(p) fz = fr * cos(p) return lambdify([t, p], [fx, fy, fz]) Cartesian2D._register() Cartesian3D._register() ParametricCurve2D._register() ParametricCurve3D._register() ParametricSurface._register() Polar._register() Cylindrical._register() Spherical._register() sympy-0.7.4.1/sympy/plotting/pygletplot/plot_mode.py0000644000175000017500000003361312253362407023032 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import Symbol, sympify from plot_interval import PlotInterval from plot_object import PlotObject from util import parse_option_string from sympy.geometry.entity import GeometryEntity from sympy.core.compatibility import is_sequence class PlotMode(PlotObject): """ Grandparent class for plotting modes. Serves as interface for registration, lookup, and init of modes. To create a new plot mode, inherit from PlotModeBase or one of its children, such as PlotSurface or PlotCurve. """ ## Class-level attributes ## used to register and lookup ## plot modes. See PlotModeBase ## for descriptions and usage. i_vars, d_vars = '', '' intervals = [] aliases = [] is_default = False ## Draw is the only method here which ## is meant to be overridden in child ## classes, and PlotModeBase provides ## a base implementation. def draw(self): raise NotImplementedError() ## Everything else in this file has to ## do with registration and retrieval ## of plot modes. This is where I've ## hidden much of the ugliness of automatic ## plot mode divination... ## Plot mode registry data structures _mode_alias_list = [] _mode_map = { 1: {1: {}, 2: {}}, 2: {1: {}, 2: {}}, 3: {1: {}, 2: {}}, } # [d][i][alias_str]: class _mode_default_map = { 1: {}, 2: {}, 3: {}, } # [d][i]: class _i_var_max, _d_var_max = 2, 3 def __new__(cls, *args, **kwargs): """ This is the function which interprets arguments given to Plot.__init__ and Plot.__setattr__. Returns an initialized instance of the appropriate child class. """ nargs, nkwargs = PlotMode._extract_options(args, kwargs) mode_arg = nkwargs.get('mode', '') # Interpret the arguments d_vars, intervals = PlotMode._interpret_args(nargs) i_vars = PlotMode._find_i_vars(d_vars, intervals) i, d = max([len(i_vars), len(intervals)]), len(d_vars) # Find the appropriate mode subcls = PlotMode._get_mode(mode_arg, i, d) # Create the object o = object.__new__(subcls) # Do some setup for the mode instance o.d_vars = d_vars o._fill_i_vars(i_vars) o._fill_intervals(intervals) o.options = nkwargs return o @staticmethod def _get_mode(mode_arg, i_var_count, d_var_count): """ Tries to return an appropriate mode class. Intended to be called only by __new__. mode_arg Can be a string or a class. If it is a PlotMode subclass, it is simply returned. If it is a string, it can an alias for a mode or an empty string. In the latter case, we try to find a default mode for the i_var_count and d_var_count. i_var_count The number of independent variables needed to evaluate the d_vars. d_var_count The number of dependent variables; usually the number of functions to be evaluated in plotting. For example, a Cartesian function y = f(x) has one i_var (x) and one d_var (y). A parametric form x,y,z = f(u,v), f(u,v), f(u,v) has two two i_vars (u,v) and three d_vars (x,y,z). """ # if the mode_arg is simply a PlotMode class, # check that the mode supports the numbers # of independent and dependent vars, then # return it try: m = None if issubclass(mode_arg, PlotMode): m = mode_arg except TypeError: pass if m: if not m._was_initialized: raise ValueError(("To use unregistered plot mode %s " "you must first call %s._init_mode().") % (m.__name__, m.__name__)) if d_var_count != m.d_var_count: raise ValueError(("%s can only plot functions " "with %i dependent variables.") % (m.__name__, m.d_var_count)) if i_var_count > m.i_var_count: raise ValueError(("%s cannot plot functions " "with more than %i independent " "variables.") % (m.__name__, m.i_var_count)) return m # If it is a string, there are two possibilities. if isinstance(mode_arg, str): i, d = i_var_count, d_var_count if i > PlotMode._i_var_max: raise ValueError(var_count_error(True, True)) if d > PlotMode._d_var_max: raise ValueError(var_count_error(False, True)) # If the string is '', try to find a suitable # default mode if not mode_arg: return PlotMode._get_default_mode(i, d) # Otherwise, interpret the string as a mode # alias (e.g. 'cartesian', 'parametric', etc) else: return PlotMode._get_aliased_mode(mode_arg, i, d) else: raise ValueError("PlotMode argument must be " "a class or a string") @staticmethod def _get_default_mode(i, d, i_vars=-1): if i_vars == -1: i_vars = i try: return PlotMode._mode_default_map[d][i] except TypeError: # Keep looking for modes in higher i var counts # which support the given d var count until we # reach the max i_var count. if i < PlotMode._i_var_max: return PlotMode._get_default_mode(i + 1, d, i_vars) else: raise ValueError(("Couldn't find a default mode " "for %i independent and %i " "dependent variables.") % (i_vars, d)) @staticmethod def _get_aliased_mode(alias, i, d, i_vars=-1): if i_vars == -1: i_vars = i if alias not in PlotMode._mode_alias_list: raise ValueError(("Couldn't find a mode called" " %s. Known modes: %s.") % (alias, ", ".join(PlotMode._mode_alias_list))) try: return PlotMode._mode_map[d][i][alias] except TypeError: # Keep looking for modes in higher i var counts # which support the given d var count and alias # until we reach the max i_var count. if i < PlotMode._i_var_max: return PlotMode._get_aliased_mode(alias, i + 1, d, i_vars) else: raise ValueError(("Couldn't find a %s mode " "for %i independent and %i " "dependent variables.") % (alias, i_vars, d)) @classmethod def _register(cls): """ Called once for each user-usable plot mode. For Cartesian2D, it is invoked after the class definition: Cartesian2D._register() """ name = cls.__name__ #try: cls._init_mode() #except Exception as e: # raise RuntimeError( ("Failed to initialize " # "plot mode %s. Reason: %s") # % (name, (str(e))) ) try: i, d = cls.i_var_count, cls.d_var_count # Add the mode to _mode_map under all # given aliases for a in cls.aliases: if a not in PlotMode._mode_alias_list: # Also track valid aliases, so # we can quickly know when given # an invalid one in _get_mode. PlotMode._mode_alias_list.append(a) PlotMode._mode_map[d][i][a] = cls if cls.is_default: # If this mode was marked as the # default for this d,i combination, # also set that. PlotMode._mode_default_map[d][i] = cls except Exception as e: raise RuntimeError(("Failed to register " "plot mode %s. Reason: %s") % (name, (str(e)))) @classmethod def _init_mode(cls): """ Initializes the plot mode based on the 'mode-specific parameters' above. Only intended to be called by PlotMode._register(). To use a mode without registering it, you can directly call ModeSubclass._init_mode(). """ def symbols_list(symbol_str): return [Symbol(s) for s in symbol_str] # Convert the vars strs into # lists of symbols. cls.i_vars = symbols_list(cls.i_vars) cls.d_vars = symbols_list(cls.d_vars) # Var count is used often, calculate # it once here cls.i_var_count = len(cls.i_vars) cls.d_var_count = len(cls.d_vars) if cls.i_var_count > PlotMode._i_var_max: raise ValueError(var_count_error(True, False)) if cls.d_var_count > PlotMode._d_var_max: raise ValueError(var_count_error(False, False)) # Try to use first alias as primary_alias if len(cls.aliases) > 0: cls.primary_alias = cls.aliases[0] else: cls.primary_alias = cls.__name__ di = cls.intervals if len(di) != cls.i_var_count: raise ValueError("Plot mode must provide a " "default interval for each i_var.") for i in range(cls.i_var_count): # default intervals must be given [min,max,steps] # (no var, but they must be in the same order as i_vars) assert len(di[i]) == 3 # Initialize an incomplete interval, # to later be filled with a var when # the mode is instantiated. di[i] = PlotInterval(None, *di[i]) # To prevent people from using modes # without these required fields set up. cls._was_initialized = True _was_initialized = False ## Initializer Helper Methods @staticmethod def _find_i_vars(functions, intervals): i_vars = [] # First, collect i_vars in the # order they are given in any # intervals. for i in intervals: if i.v is None: continue elif i.v in i_vars: raise ValueError(("Multiple intervals given " "for %s.") % (str(i.v))) i_vars.append(i.v) # Then, find any remaining # i_vars in given functions # (aka d_vars) for f in functions: for a in f.free_symbols: if a not in i_vars: i_vars.append(a) return i_vars def _fill_i_vars(self, i_vars): # copy default i_vars self.i_vars = [Symbol(str(i)) for i in self.i_vars] # replace with given i_vars for i in range(len(i_vars)): self.i_vars[i] = i_vars[i] def _fill_intervals(self, intervals): # copy default intervals self.intervals = [PlotInterval(i) for i in self.intervals] # track i_vars used so far v_used = [] # fill copy of default # intervals with given info for i in range(len(intervals)): self.intervals[i].fill_from(intervals[i]) if self.intervals[i].v is not None: v_used.append(self.intervals[i].v) # Find any orphan intervals and # assign them i_vars for i in range(len(self.intervals)): if self.intervals[i].v is None: u = [v for v in self.i_vars if v not in v_used] assert len(u) != 0 self.intervals[i].v = u[0] v_used.append(u[0]) @staticmethod def _interpret_args(args): interval_wrong_order = "PlotInterval %s was given before any function(s)." interpret_error = "Could not interpret %s as a function or interval." functions, intervals = [], [] if isinstance(args[0], GeometryEntity): for coords in list(args[0].arbitrary_point()): functions.append(coords) intervals.append(PlotInterval.try_parse(args[0].plot_interval())) else: for a in args: i = PlotInterval.try_parse(a) if i is not None: if len(functions) == 0: raise ValueError(interval_wrong_order % (str(i))) else: intervals.append(i) else: if is_sequence(a, include=str): raise ValueError(interpret_error % (str(a))) try: f = sympify(a) functions.append(f) except TypeError: raise ValueError(interpret_error % str(a)) return functions, intervals @staticmethod def _extract_options(args, kwargs): nkwargs, nargs = {}, [] for a in args: if isinstance(a, str): nkwargs = dict(nkwargs, **parse_option_string(a)) else: nargs.append(a) nkwargs = dict(nkwargs, **kwargs) return nargs, nkwargs def var_count_error(is_independent, is_plotting): """ Used to format an error message which differs slightly in 4 places. """ if is_plotting: v = "Plotting" else: v = "Registering plot modes" if is_independent: n, s = PlotMode._i_var_max, "independent" else: n, s = PlotMode._d_var_max, "dependent" return ("%s with more than %i %s variables " "is not supported.") % (v, n, s) sympy-0.7.4.1/sympy/plotting/pygletplot/util.py0000644000175000017500000001061212253362407022017 0ustar georgeskgeorgeskfrom __future__ import print_function, division from pyglet.gl import * from sympy.core import S from sympy.core.compatibility import xrange def get_model_matrix(array_type=c_float, glGetMethod=glGetFloatv): """ Returns the current modelview matrix. """ m = (array_type*16)() glGetMethod(GL_MODELVIEW_MATRIX, m) return m def get_projection_matrix(array_type=c_float, glGetMethod=glGetFloatv): """ Returns the current modelview matrix. """ m = (array_type*16)() glGetMethod(GL_PROJECTION_MATRIX, m) return m def get_viewport(): """ Returns the current viewport. """ m = (c_int*4)() glGetIntegerv(GL_VIEWPORT, m) return m def get_direction_vectors(): m = get_model_matrix() return ((m[0], m[4], m[8]), (m[1], m[5], m[9]), (m[2], m[6], m[10])) def get_view_direction_vectors(): m = get_model_matrix() return ((m[0], m[1], m[2]), (m[4], m[5], m[6]), (m[8], m[9], m[10])) def get_basis_vectors(): return ((1, 0, 0), (0, 1, 0), (0, 0, 1)) def screen_to_model(x, y, z): m = get_model_matrix(c_double, glGetDoublev) p = get_projection_matrix(c_double, glGetDoublev) w = get_viewport() mx, my, mz = c_double(), c_double(), c_double() gluUnProject(x, y, z, m, p, w, mx, my, mz) return float(mx.value), float(my.value), float(mz.value) def model_to_screen(x, y, z): m = get_model_matrix(c_double, glGetDoublev) p = get_projection_matrix(c_double, glGetDoublev) w = get_viewport() mx, my, mz = c_double(), c_double(), c_double() gluProject(x, y, z, m, p, w, mx, my, mz) return float(mx.value), float(my.value), float(mz.value) def vec_subs(a, b): return tuple(a[i] - b[i] for i in xrange(len(a))) def billboard_matrix(): """ Removes rotational components of current matrix so that primitives are always drawn facing the viewer. |1|0|0|x| |0|1|0|x| |0|0|1|x| (x means left unchanged) |x|x|x|x| """ m = get_model_matrix() # XXX: for i in range(11): m[i] = i ? m[0] = 1 m[1] = 0 m[2] = 0 m[4] = 0 m[5] = 1 m[6] = 0 m[8] = 0 m[9] = 0 m[10] = 1 glLoadMatrixf(m) def create_bounds(): return [[S.Infinity, -S.Infinity, 0], [S.Infinity, -S.Infinity, 0], [S.Infinity, -S.Infinity, 0]] def update_bounds(b, v): if v is None: return for axis in range(3): b[axis][0] = min([b[axis][0], v[axis]]) b[axis][1] = max([b[axis][1], v[axis]]) def interpolate(a_min, a_max, a_ratio): return a_min + a_ratio * (a_max - a_min) def rinterpolate(a_min, a_max, a_value): a_range = a_max - a_min if a_range == 0: a_range = 1.0 return (a_value - a_min) / float(a_range) def interpolate_color(color1, color2, ratio): return tuple(interpolate(color1[i], color2[i], ratio) for i in range(3)) def scale_value(v, v_min, v_len): return (v - v_min) / v_len def scale_value_list(flist): v_min, v_max = min(flist), max(flist) v_len = v_max - v_min return list(scale_value(f, v_min, v_len) for f in flist) def strided_range(r_min, r_max, stride, max_steps=50): o_min, o_max = r_min, r_max if abs(r_min - r_max) < 0.001: return [] try: xrange(int(r_min - r_max)) except TypeError: return [] assert r_min < r_max r_min_s = (r_min % stride) r_max_s = stride - (r_max % stride) if abs(r_max_s - stride) < 0.001: r_max_s = 0.0 r_min -= r_min_s r_max += r_max_s r_steps = int((r_max - r_min)/stride) if max_steps and r_steps > max_steps: return strided_range(o_min, o_max, stride*2) return [r_min] + list(r_min + e*stride for e in xrange(1, r_steps + 1)) + [r_max] def parse_option_string(s): if not isinstance(s, str): return None options = {} for token in s.split(';'): pieces = token.split('=') if len(pieces) == 1: option, value = pieces[0], "" elif len(pieces) == 2: option, value = pieces else: raise ValueError("Plot option string '%s' is malformed." % (s)) options[option.strip()] = value.strip() return options def dot_product(v1, v2): return sum(v1[i]*v2[i] for i in range(3)) def vec_sub(v1, v2): return tuple(v1[i] - v2[i] for i in range(3)) def vec_mag(v): return sum(v[i]**2 for i in range(3))**(0.5) sympy-0.7.4.1/sympy/plotting/pygletplot/plot_interval.py0000644000175000017500000001241712253362407023731 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import Symbol, Integer, sympify class PlotInterval(object): """ """ _v, _v_min, _v_max, _v_steps = None, None, None, None def require_all_args(f): def check(self, *args, **kwargs): for g in [self._v, self._v_min, self._v_max, self._v_steps]: if g is None: raise ValueError("PlotInterval is incomplete.") return f(self, *args, **kwargs) return check def __init__(self, *args): if len(args) == 1: if isinstance(args[0], PlotInterval): self.fill_from(args[0]) return elif isinstance(args[0], str): try: args = eval(args[0]) except TypeError: s_eval_error = "Could not interpret string %s." raise ValueError(s_eval_error % (args[0])) elif isinstance(args[0], (tuple, list)): args = args[0] else: raise ValueError("Not an interval.") if not isinstance(args, (tuple, list)) or len(args) > 4: f_error = "PlotInterval must be a tuple or list of length 4 or less." raise ValueError(f_error) args = list(args) if len(args) > 0 and (args[0] is None or isinstance(args[0], Symbol)): self.v = args.pop(0) if len(args) in [2, 3]: self.v_min = args.pop(0) self.v_max = args.pop(0) if len(args) == 1: self.v_steps = args.pop(0) elif len(args) == 1: self.v_steps = args.pop(0) def get_v(self): return self._v def set_v(self, v): if v is None: self._v = None return if not isinstance(v, Symbol): raise ValueError("v must be a sympy Symbol.") self._v = v def get_v_min(self): return self._v_min def set_v_min(self, v_min): if v_min is None: self._v_min = None return try: self._v_min = sympify(v_min) float(self._v_min.evalf()) except TypeError: raise ValueError("v_min could not be interpreted as a number.") def get_v_max(self): return self._v_max def set_v_max(self, v_max): if v_max is None: self._v_max = None return try: self._v_max = sympify(v_max) float(self._v_max.evalf()) except TypeError: raise ValueError("v_max could not be interpreted as a number.") def get_v_steps(self): return self._v_steps def set_v_steps(self, v_steps): if v_steps is None: self._v_steps = None return if isinstance(v_steps, int): v_steps = Integer(v_steps) elif not isinstance(v_steps, Integer): raise ValueError("v_steps must be an int or sympy Integer.") if v_steps <= Integer(0): raise ValueError("v_steps must be positive.") self._v_steps = v_steps @require_all_args def get_v_len(self): return self.v_steps + 1 v = property(get_v, set_v) v_min = property(get_v_min, set_v_min) v_max = property(get_v_max, set_v_max) v_steps = property(get_v_steps, set_v_steps) v_len = property(get_v_len) def fill_from(self, b): if b.v is not None: self.v = b.v if b.v_min is not None: self.v_min = b.v_min if b.v_max is not None: self.v_max = b.v_max if b.v_steps is not None: self.v_steps = b.v_steps @staticmethod def try_parse(*args): """ Returns a PlotInterval if args can be interpreted as such, otherwise None. """ if len(args) == 1 and isinstance(args[0], PlotInterval): return args[0] try: return PlotInterval(*args) except ValueError: return None def _str_base(self): return ",".join([str(self.v), str(self.v_min), str(self.v_max), str(self.v_steps)]) def __repr__(self): """ A string representing the interval in class constructor form. """ return "PlotInterval(%s)" % (self._str_base()) def __str__(self): """ A string representing the interval in list form. """ return "[%s]" % (self._str_base()) @require_all_args def assert_complete(self): pass @require_all_args def vrange(self): """ Yields v_steps+1 sympy numbers ranging from v_min to v_max. """ d = (self.v_max - self.v_min) / self.v_steps for i in xrange(self.v_steps + 1): a = self.v_min + (d * Integer(i)) yield a @require_all_args def vrange2(self): """ Yields v_steps pairs of sympy numbers ranging from (v_min, v_min + step) to (v_max - step, v_max). """ d = (self.v_max - self.v_min) / self.v_steps a = self.v_min + (d * Integer(0)) for i in xrange(self.v_steps): b = self.v_min + (d * Integer(i + 1)) yield a, b a = b def frange(self): for i in self.vrange(): yield float(i.evalf()) sympy-0.7.4.1/sympy/plotting/pygletplot/plot_surface.py0000644000175000017500000000735212253362407023537 0ustar georgeskgeorgeskfrom __future__ import print_function, division from pyglet.gl import * from plot_mode_base import PlotModeBase from sympy.core import S from sympy.core.compatibility import xrange class PlotSurface(PlotModeBase): default_rot_preset = 'perspective' def _on_calculate_verts(self): self.u_interval = self.intervals[0] self.u_set = list(self.u_interval.frange()) self.v_interval = self.intervals[1] self.v_set = list(self.v_interval.frange()) self.bounds = [[S.Infinity, -S.Infinity, 0], [S.Infinity, -S.Infinity, 0], [S.Infinity, -S.Infinity, 0]] evaluate = self._get_evaluator() self._calculating_verts_pos = 0.0 self._calculating_verts_len = float( self.u_interval.v_len*self.v_interval.v_len) verts = list() b = self.bounds for u in self.u_set: column = list() for v in self.v_set: try: _e = evaluate(u, v) # calculate vertex except ZeroDivisionError: _e = None if _e is not None: # update bounding box for axis in range(3): b[axis][0] = min([b[axis][0], _e[axis]]) b[axis][1] = max([b[axis][1], _e[axis]]) column.append(_e) self._calculating_verts_pos += 1.0 verts.append(column) for axis in range(3): b[axis][2] = b[axis][1] - b[axis][0] if b[axis][2] == 0.0: b[axis][2] = 1.0 self.verts = verts self.push_wireframe(self.draw_verts(False, False)) self.push_solid(self.draw_verts(False, True)) def _on_calculate_cverts(self): if not self.verts or not self.color: return def set_work_len(n): self._calculating_cverts_len = float(n) def inc_work_pos(): self._calculating_cverts_pos += 1.0 set_work_len(1) self._calculating_cverts_pos = 0 self.cverts = self.color.apply_to_surface(self.verts, self.u_set, self.v_set, set_len=set_work_len, inc_pos=inc_work_pos) self.push_solid(self.draw_verts(True, True)) def calculate_one_cvert(self, u, v): vert = self.verts[u][v] return self.color(vert[0], vert[1], vert[2], self.u_set[u], self.v_set[v]) def draw_verts(self, use_cverts, use_solid_color): def f(): for u in xrange(1, len(self.u_set)): glBegin(GL_QUAD_STRIP) for v in xrange(len(self.v_set)): pa = self.verts[u - 1][v] pb = self.verts[u][v] if pa is None or pb is None: glEnd() glBegin(GL_QUAD_STRIP) continue if use_cverts: ca = self.cverts[u - 1][v] cb = self.cverts[u][v] if ca is None: ca = (0, 0, 0) if cb is None: cb = (0, 0, 0) else: if use_solid_color: ca = cb = self.default_solid_color else: ca = cb = self.default_wireframe_color glColor3f(*ca) glVertex3f(*pa) glColor3f(*cb) glVertex3f(*pb) glEnd() return f sympy-0.7.4.1/sympy/plotting/pygletplot/managed_window.py0000644000175000017500000000615212253362407024031 0ustar georgeskgeorgeskfrom __future__ import print_function, division from pyglet.gl import * from pyglet.window import Window from pyglet.clock import Clock from threading import Thread, Lock gl_lock = Lock() class ManagedWindow(Window): """ A pyglet window with an event loop which executes automatically in a separate thread. Behavior is added by creating a subclass which overrides setup, update, and/or draw. """ fps_limit = 30 default_win_args = dict(width=600, height=500, vsync=False, resizable=True) def __init__(self, **win_args): """ It is best not to override this function in the child class, unless you need to take additional arguments. Do any OpenGL initialization calls in setup(). """ # check if this is run from the doctester if win_args.get('runfromdoctester', False): return self.win_args = dict(self.default_win_args, **win_args) self.Thread = Thread(target=self.__event_loop__) self.Thread.start() def __event_loop__(self, **win_args): """ The event loop thread function. Do not override or call directly (it is called by __init__). """ gl_lock.acquire() try: try: super(ManagedWindow, self).__init__(**self.win_args) self.switch_to() self.setup() except Exception as e: print("Window initialization failed: %s" % (str(e))) self.has_exit = True finally: gl_lock.release() clock = Clock() clock.set_fps_limit(self.fps_limit) while not self.has_exit: dt = clock.tick() gl_lock.acquire() try: try: self.switch_to() self.dispatch_events() self.clear() self.update(dt) self.draw() self.flip() except Exception as e: print("Uncaught exception in event loop: %s" % str(e)) self.has_exit = True finally: gl_lock.release() super(ManagedWindow, self).close() def close(self): """ Closes the window. """ self.has_exit = True def setup(self): """ Called once before the event loop begins. Override this method in a child class. This is the best place to put things like OpenGL initialization calls. """ pass def update(self, dt): """ Called before draw during each iteration of the event loop. dt is the elapsed time in seconds since the last update. OpenGL rendering calls are best put in draw() rather than here. """ pass def draw(self): """ Called after update during each iteration of the event loop. Put OpenGL rendering calls here. """ pass if __name__ == '__main__': ManagedWindow() sympy-0.7.4.1/sympy/plotting/pygletplot/plot_rotation.py0000644000175000017500000000266412253362407023747 0ustar georgeskgeorgeskfrom __future__ import print_function, division from pyglet.gl import * from math import sqrt as _sqrt, acos as _acos def cross(a, b): return (a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]) def dot(a, b): return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] def mag(a): return _sqrt(a[0]**2 + a[1]**2 + a[2]**2) def norm(a): m = mag(a) return (a[0] / m, a[1] / m, a[2] / m) def get_sphere_mapping(x, y, width, height): x = min([max([x, 0]), width]) y = min([max([y, 0]), height]) sr = _sqrt((width/2)**2 + (height/2)**2) #sr *= 1.5 sx = ((x - width / 2) / sr) sy = ((y - height / 2) / sr) sz = 1.0 - sx**2 - sy**2 if sz > 0.0: sz = _sqrt(sz) return (sx, sy, sz) else: sz = 0 return norm((sx, sy, sz)) rad2deg = 180.0 / 3.141592 def get_spherical_rotatation(p1, p2, width, height, theta_multiplier): v1 = get_sphere_mapping(p1[0], p1[1], width, height) v2 = get_sphere_mapping(p2[0], p2[1], width, height) d = min(max([dot(v1, v2), -1]), 1) if abs(d - 1.0) < 0.000001: return None raxis = norm( cross(v1, v2) ) rtheta = theta_multiplier * rad2deg * _acos(d) #rtheta = 2.0 * rad2deg * _acos(d) glPushMatrix() glLoadIdentity() glRotatef(rtheta, *raxis) mat = (c_float*16)() glGetFloatv(GL_MODELVIEW_MATRIX, mat) glPopMatrix() return mat sympy-0.7.4.1/sympy/plotting/pygletplot/plot_object.py0000644000175000017500000000060312253362407023345 0ustar georgeskgeorgeskfrom __future__ import print_function, division class PlotObject(object): """ Base class for objects which can be displayed in a Plot. """ visible = True def _draw(self): if self.visible: self.draw() def draw(self): """ OpenGL rendering code for the plot object. Override in base class. """ pass sympy-0.7.4.1/sympy/plotting/pygletplot/plot_camera.py0000644000175000017500000000746012253362407023337 0ustar georgeskgeorgeskfrom __future__ import print_function, division from pyglet.gl import * from plot_rotation import get_spherical_rotatation from util import get_model_matrix from util import screen_to_model, model_to_screen from util import vec_subs class PlotCamera(object): min_dist = 0.05 max_dist = 500.0 min_ortho_dist = 100.0 max_ortho_dist = 10000.0 _default_dist = 6.0 _default_ortho_dist = 600.0 rot_presets = { 'xy': (0, 0, 0), 'xz': (-90, 0, 0), 'yz': (0, 90, 0), 'perspective': (-45, 0, -45) } def __init__(self, window, ortho=False): self.window = window self.axes = self.window.plot.axes self.ortho = ortho self.reset() def init_rot_matrix(self): glPushMatrix() glLoadIdentity() self._rot = get_model_matrix() glPopMatrix() def set_rot_preset(self, preset_name): self.init_rot_matrix() try: r = self.rot_presets[preset_name] except AttributeError: raise ValueError( "%s is not a valid rotation preset." % preset_name) try: self.euler_rotate(r[0], 1, 0, 0) self.euler_rotate(r[1], 0, 1, 0) self.euler_rotate(r[2], 0, 0, 1) except AttributeError: pass def reset(self): self._dist = 0.0 self._x, self._y = 0.0, 0.0 self._rot = None if self.ortho: self._dist = self._default_ortho_dist else: self._dist = self._default_dist self.init_rot_matrix() def mult_rot_matrix(self, rot): glPushMatrix() glLoadMatrixf(rot) glMultMatrixf(self._rot) self._rot = get_model_matrix() glPopMatrix() def setup_projection(self): glMatrixMode(GL_PROJECTION) glLoadIdentity() if self.ortho: # yep, this is pseudo ortho (don't tell anyone) gluPerspective( 0.3, float(self.window.width)/float(self.window.height), self.min_ortho_dist - 0.01, self.max_ortho_dist + 0.01) else: gluPerspective( 30.0, float(self.window.width)/float(self.window.height), self.min_dist - 0.01, self.max_dist + 0.01) glMatrixMode(GL_MODELVIEW) def _get_scale(self): return 1.0, 1.0, 1.0 def apply_transformation(self): glLoadIdentity() glTranslatef(self._x, self._y, -self._dist) if self._rot is not None: glMultMatrixf(self._rot) glScalef(*self._get_scale()) def spherical_rotate(self, p1, p2, sensitivity=1.0): mat = get_spherical_rotatation(p1, p2, self.window.width, self.window.height, sensitivity) if mat is not None: self.mult_rot_matrix(mat) def euler_rotate(self, angle, x, y, z): glPushMatrix() glLoadMatrixf(self._rot) glRotatef(angle, x, y, z) self._rot = get_model_matrix() glPopMatrix() def zoom_relative(self, clicks, sensitivity): if self.ortho: dist_d = clicks * sensitivity * 50.0 min_dist = self.min_ortho_dist max_dist = self.max_ortho_dist else: dist_d = clicks * sensitivity min_dist = self.min_dist max_dist = self.max_dist new_dist = (self._dist - dist_d) if (clicks < 0 and new_dist < max_dist) or new_dist > min_dist: self._dist = new_dist def mouse_translate(self, x, y, dx, dy): glPushMatrix() glLoadIdentity() glTranslatef(0, 0, -self._dist) z = model_to_screen(0, 0, 0)[2] d = vec_subs(screen_to_model(x, y, z), screen_to_model(x - dx, y - dy, z)) glPopMatrix() self._x += d[0] self._y += d[1] sympy-0.7.4.1/sympy/plotting/pygletplot/plot_controller.py0000644000175000017500000001546512253362407024276 0ustar georgeskgeorgeskfrom __future__ import print_function, division from pyglet.window import key from pyglet.window.mouse import LEFT, RIGHT, MIDDLE from util import get_direction_vectors, get_basis_vectors class PlotController(object): normal_mouse_sensitivity = 4.0 modified_mouse_sensitivity = 1.0 normal_key_sensitivity = 160.0 modified_key_sensitivity = 40.0 keymap = { key.LEFT: 'left', key.A: 'left', key.NUM_4: 'left', key.RIGHT: 'right', key.D: 'right', key.NUM_6: 'right', key.UP: 'up', key.W: 'up', key.NUM_8: 'up', key.DOWN: 'down', key.S: 'down', key.NUM_2: 'down', key.Z: 'rotate_z_neg', key.NUM_1: 'rotate_z_neg', key.C: 'rotate_z_pos', key.NUM_3: 'rotate_z_pos', key.Q: 'spin_left', key.NUM_7: 'spin_left', key.E: 'spin_right', key.NUM_9: 'spin_right', key.X: 'reset_camera', key.NUM_5: 'reset_camera', key.NUM_ADD: 'zoom_in', key.PAGEUP: 'zoom_in', key.R: 'zoom_in', key.NUM_SUBTRACT: 'zoom_out', key.PAGEDOWN: 'zoom_out', key.F: 'zoom_out', key.RSHIFT: 'modify_sensitivity', key.LSHIFT: 'modify_sensitivity', key.F1: 'rot_preset_xy', key.F2: 'rot_preset_xz', key.F3: 'rot_preset_yz', key.F4: 'rot_preset_perspective', key.F5: 'toggle_axes', key.F6: 'toggle_axe_colors', key.F8: 'save_image' } def __init__(self, window, **kwargs): self.invert_mouse_zoom = kwargs.pop('invert_mouse_zoom', False) self.window = window self.camera = window.camera self.action = { # Rotation around the view Y (up) vector 'left': False, 'right': False, # Rotation around the view X vector 'up': False, 'down': False, # Rotation around the view Z vector 'spin_left': False, 'spin_right': False, # Rotation around the model Z vector 'rotate_z_neg': False, 'rotate_z_pos': False, # Reset to the default rotation 'reset_camera': False, # Performs camera z-translation 'zoom_in': False, 'zoom_out': False, # Use alternative sensitivity (speed) 'modify_sensitivity': False, # Rotation presets 'rot_preset_xy': False, 'rot_preset_xz': False, 'rot_preset_yz': False, 'rot_preset_perspective': False, # axes 'toggle_axes': False, 'toggle_axe_colors': False, # screenshot 'save_image': False } def update(self, dt): z = 0 if self.action['zoom_out']: z -= 1 if self.action['zoom_in']: z += 1 if z != 0: self.camera.zoom_relative(z/10.0, self.get_key_sensitivity()/10.0) dx, dy, dz = 0, 0, 0 if self.action['left']: dx -= 1 if self.action['right']: dx += 1 if self.action['up']: dy -= 1 if self.action['down']: dy += 1 if self.action['spin_left']: dz += 1 if self.action['spin_right']: dz -= 1 if not self.is_2D(): if dx != 0: self.camera.euler_rotate(dx*dt*self.get_key_sensitivity(), *(get_direction_vectors()[1])) if dy != 0: self.camera.euler_rotate(dy*dt*self.get_key_sensitivity(), *(get_direction_vectors()[0])) if dz != 0: self.camera.euler_rotate(dz*dt*self.get_key_sensitivity(), *(get_direction_vectors()[2])) else: self.camera.mouse_translate(0, 0, dx*dt*self.get_key_sensitivity(), -dy*dt*self.get_key_sensitivity()) rz = 0 if self.action['rotate_z_neg'] and not self.is_2D(): rz -= 1 if self.action['rotate_z_pos'] and not self.is_2D(): rz += 1 if rz != 0: self.camera.euler_rotate(rz*dt*self.get_key_sensitivity(), *(get_basis_vectors()[2])) if self.action['reset_camera']: self.camera.reset() if self.action['rot_preset_xy']: self.camera.set_rot_preset('xy') if self.action['rot_preset_xz']: self.camera.set_rot_preset('xz') if self.action['rot_preset_yz']: self.camera.set_rot_preset('yz') if self.action['rot_preset_perspective']: self.camera.set_rot_preset('perspective') if self.action['toggle_axes']: self.action['toggle_axes'] = False self.camera.axes.toggle_visible() if self.action['toggle_axe_colors']: self.action['toggle_axe_colors'] = False self.camera.axes.toggle_colors() if self.action['save_image']: self.action['save_image'] = False self.window.plot.saveimage() return True def get_mouse_sensitivity(self): if self.action['modify_sensitivity']: return self.modified_mouse_sensitivity else: return self.normal_mouse_sensitivity def get_key_sensitivity(self): if self.action['modify_sensitivity']: return self.modified_key_sensitivity else: return self.normal_key_sensitivity def on_key_press(self, symbol, modifiers): if symbol in self.keymap: self.action[self.keymap[symbol]] = True def on_key_release(self, symbol, modifiers): if symbol in self.keymap: self.action[self.keymap[symbol]] = False def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): if buttons & LEFT: if self.is_2D(): self.camera.mouse_translate(x, y, dx, dy) else: self.camera.spherical_rotate((x - dx, y - dy), (x, y), self.get_mouse_sensitivity()) if buttons & MIDDLE: self.camera.zoom_relative([1, -1][self.invert_mouse_zoom]*dy, self.get_mouse_sensitivity()/20.0) if buttons & RIGHT: self.camera.mouse_translate(x, y, dx, dy) def on_mouse_scroll(self, x, y, dx, dy): self.camera.zoom_relative([1, -1][self.invert_mouse_zoom]*dy, self.get_mouse_sensitivity()) def is_2D(self): functions = self.window.plot._functions for i in functions: if len(functions[i].i_vars) > 1 or len(functions[i].d_vars) > 2: return False return True sympy-0.7.4.1/sympy/plotting/pygletplot/plot.py0000644000175000017500000003173212253362407022026 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import Integer from sympy.core.compatibility import is_sequence from threading import RLock # it is sufficient to import "pyglet" here once try: from pyglet.gl import * except ImportError: raise ImportError("pyglet is required for plotting.\n " "visit http://www.pyglet.org/") from plot_object import PlotObject from plot_axes import PlotAxes from plot_window import PlotWindow from plot_mode import PlotMode import plot_modes from time import sleep from os import getcwd, listdir from util import parse_option_string from sympy.geometry.entity import GeometryEntity from sympy.utilities.decorator import doctest_depends_on @doctest_depends_on(modules=('pyglet',)) class PygletPlot(object): """ Plot Examples ============= See examples/advaned/pyglet_plotting.py for many more examples. >>> from sympy.plotting.pygletplot import PygletPlot as Plot >>> from sympy.abc import x, y, z >>> Plot(x*y**3-y*x**3) [0]: -x**3*y + x*y**3, 'mode=cartesian' >>> p = Plot() >>> p[1] = x*y >>> p[1].color = z, (0.4,0.4,0.9), (0.9,0.4,0.4) >>> p = Plot() >>> p[1] = x**2+y**2 >>> p[2] = -x**2-y**2 Variable Intervals ================== The basic format is [var, min, max, steps], but the syntax is flexible and arguments left out are taken from the defaults for the current coordinate mode: >>> Plot(x**2) # implies [x,-5,5,100] [0]: x**2, 'mode=cartesian' >>> Plot(x**2, [], []) # [x,-1,1,40], [y,-1,1,40] [0]: x**2, 'mode=cartesian' >>> Plot(x**2-y**2, [100], [100]) # [x,-1,1,100], [y,-1,1,100] [0]: x**2 - y**2, 'mode=cartesian' >>> Plot(x**2, [x,-13,13,100]) [0]: x**2, 'mode=cartesian' >>> Plot(x**2, [-13,13]) # [x,-13,13,100] [0]: x**2, 'mode=cartesian' >>> Plot(x**2, [x,-13,13]) # [x,-13,13,10] [0]: x**2, 'mode=cartesian' >>> Plot(1*x, [], [x], mode='cylindrical') ... # [unbound_theta,0,2*Pi,40], [x,-1,1,20] [0]: x, 'mode=cartesian' Coordinate Modes ================ Plot supports several curvilinear coordinate modes, and they 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[n] = (function arguments) 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 if one variable is used, and a surface if two are used. Supported mode names by number of variables: 1: parametric, cartesian, polar 2: parametric, cartesian, cylindrical = polar, spherical >>> Plot(1, mode='spherical') # doctest: +SKIP Calculator-like Interface ========================= >>> p = Plot(visible=False) >>> f = x**2 >>> p[1] = f >>> p[2] = f.diff(x) >>> p[3] = f.diff(x).diff(x) # doctest: +SKIP >>> p # doctest: +SKIP [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() Plot Window Keyboard Controls ============================= Screen Rotation: X,Y axis Arrow Keys, A,S,D,W, Numpad 4,6,8,2 Z axis Q,E, Numpad 7,9 Model Rotation: Z axis Z,C, Numpad 1,3 Zoom: R,F, PgUp,PgDn, Numpad +,- Reset Camera: X, Numpad 5 Camera Presets: XY F1 XZ F2 YZ F3 Perspective F4 Sensitivity Modifier: SHIFT Axes Toggle: Visible F5 Colors F6 Close Window: ESCAPE ============================= """ @doctest_depends_on(modules=('pyglet',)) def __init__(self, *fargs, **win_args): """ Positional Arguments ==================== Any given positional arguments are used to initialize a plot function at index 1. In other words... >>> from sympy.plotting.pygletplot import PygletPlot as Plot >>> from sympy.core import Symbol >>> from sympy.abc import x >>> p = Plot(x**2, visible=False) ...is equivalent to... >>> p = Plot(visible=False) >>> p[1] = x**2 Note that in earlier versions of the plotting module, you were able to specify multiple functions in the initializer. This functionality has been dropped in favor of better automatic plot plot_mode detection. Named Arguments =============== axes An option string of the form "key1=value1; key2 = value2" which can use the following options: style = ordinate none OR frame OR box OR ordinate stride = 0.25 val OR (val_x, val_y, val_z) overlay = True (draw on top of plot) True OR False colored = False (False uses Black, True uses colors R,G,B = X,Y,Z) True OR False label_axes = False (display axis names at endpoints) True OR False visible = True (show immediately True OR False The following named arguments are passed as arguments to window initialization: antialiasing = True True OR False ortho = False True OR False invert_mouse_zoom = False True OR False """ self._win_args = win_args self._window = None self._render_lock = RLock() self._functions = {} self._pobjects = [] self._screenshot = ScreenShot(self) axe_options = parse_option_string(win_args.pop('axes', '')) self.axes = PlotAxes(**axe_options) self._pobjects.append(self.axes) self[0] = fargs if win_args.get('visible', True): self.show() ## Window Interfaces def show(self): """ Creates and displays a plot window, or activates it (gives it focus) if it has already been created. """ if self._window and not self._window.has_exit: self._window.activate() else: self._win_args['visible'] = True self.axes.reset_resources() if hasattr(self, '_doctest_depends_on'): self._win_args['runfromdoctester'] = True self._window = PlotWindow(self, **self._win_args) def close(self): """ Closes the plot window. """ if self._window: self._window.close() def saveimage(self, outfile=None, format='', size=(600, 500)): """ Saves a screen capture of the plot window to an image file. If outfile is given, it can either be a path or a file object. Otherwise a png image will be saved to the current working directory. If the format is omitted, it is determined from the filename extension. """ self._screenshot.save(outfile, format, size) ## Function List Interfaces def clear(self): """ Clears the function list of this plot. """ self._render_lock.acquire() self._functions = {} self.adjust_all_bounds() self._render_lock.release() def __getitem__(self, i): """ Returns the function at position i in the function list. """ return self._functions[i] def __setitem__(self, i, args): """ Parses and adds a PlotMode to the function list. """ if not (isinstance(i, (int, Integer)) and i >= 0): raise ValueError("Function index must " "be an integer >= 0.") if isinstance(args, PlotObject): f = args else: if (not is_sequence(args)) or isinstance(args, GeometryEntity): args = [args] if len(args) == 0: return # no arguments given kwargs = dict(bounds_callback=self.adjust_all_bounds) f = PlotMode(*args, **kwargs) if f: self._render_lock.acquire() self._functions[i] = f self._render_lock.release() else: raise ValueError("Failed to parse '%s'." % ', '.join(str(a) for a in args)) def __delitem__(self, i): """ Removes the function in the function list at position i. """ self._render_lock.acquire() del self._functions[i] self.adjust_all_bounds() self._render_lock.release() def firstavailableindex(self): """ Returns the first unused index in the function list. """ i = 0 self._render_lock.acquire() while i in self._functions: i += 1 self._render_lock.release() return i def append(self, *args): """ Parses and adds a PlotMode to the function list at the first available index. """ self.__setitem__(self.firstavailableindex(), args) def __len__(self): """ Returns the number of functions in the function list. """ return len(self._functions) def __iter__(self): """ Allows iteration of the function list. """ return self._functions.itervalues() def __repr__(self): return str(self) def __str__(self): """ Returns a string containing a new-line separated list of the functions in the function list. """ s = "" if len(self._functions) == 0: s += "" else: self._render_lock.acquire() s += "\n".join(["%s[%i]: %s" % ("", i, str(self._functions[i])) for i in self._functions]) self._render_lock.release() return s def adjust_all_bounds(self): self._render_lock.acquire() self.axes.reset_bounding_box() for f in self._functions: self.axes.adjust_bounds(self._functions[f].bounds) self._render_lock.release() def wait_for_calculations(self): sleep(0) self._render_lock.acquire() for f in self._functions: a = self._functions[f]._get_calculating_verts b = self._functions[f]._get_calculating_cverts while a() or b(): sleep(0) self._render_lock.release() class ScreenShot: def __init__(self, plot): self._plot = plot self.screenshot_requested = False self.outfile = None self.format = '' self.invisibleMode = False self.flag = 0 def __nonzero__(self): if self.screenshot_requested: return 1 return 0 __bool__ = __nonzero__ def _execute_saving(self): if self.flag < 3: self.flag += 1 return size_x, size_y = self._plot._window.get_size() size = size_x*size_y*4*sizeof(c_ubyte) image = create_string_buffer(size) glReadPixels(0, 0, size_x, size_y, GL_RGBA, GL_UNSIGNED_BYTE, image) from PIL import Image im = Image.frombuffer('RGBA', (size_x, size_y), image.raw, 'raw', 'RGBA', 0, 1) im.transpose(Image.FLIP_TOP_BOTTOM).save(self.outfile, self.format) self.flag = 0 self.screenshot_requested = False if self.invisibleMode: self._plot._window.close() def save(self, outfile=None, format='', size=(600, 500)): self.outfile = outfile self.format = format self.size = size self.screenshot_requested = True if not self._plot._window or self._plot._window.has_exit: self._plot._win_args['visible'] = False self._plot._win_args['width'] = size[0] self._plot._win_args['height'] = size[1] self._plot.axes.reset_resources() self._plot._window = PlotWindow(self._plot, **self._plot._win_args) self.invisibleMode = True if self.outfile is None: self.outfile = self._create_unique_path() print(self.outfile) def _create_unique_path(self): cwd = getcwd() l = listdir(cwd) path = '' i = 0 while True: if not 'plot_%s.png' % i in l: path = cwd + '/plot_%s.png' % i break i += 1 return path sympy-0.7.4.1/sympy/plotting/pygletplot/plot_window.py0000644000175000017500000001102112253362407023402 0ustar georgeskgeorgeskfrom __future__ import print_function, division from pyglet.gl import * from managed_window import ManagedWindow from plot_camera import PlotCamera from plot_controller import PlotController from time import clock class PlotWindow(ManagedWindow): def __init__(self, plot, **kwargs): """ Named Arguments =============== antialiasing = True True OR False ortho = False True OR False invert_mouse_zoom = False True OR False """ self.plot = plot self.camera = None self._calculating = False self.antialiasing = kwargs.pop('antialiasing', True) self.ortho = kwargs.pop('ortho', False) self.invert_mouse_zoom = kwargs.pop('invert_mouse_zoom', False) self.linewidth = kwargs.pop('linewidth', 1.5) self.title = kwargs.setdefault('caption', "SymPy Plot") self.last_caption_update = 0 self.caption_update_interval = 0.2 self.drawing_first_object = True super(PlotWindow, self).__init__(**kwargs) def setup(self): self.camera = PlotCamera(self, ortho=self.ortho) self.controller = PlotController(self, invert_mouse_zoom=self.invert_mouse_zoom) self.push_handlers(self.controller) glClearColor(1.0, 1.0, 1.0, 0.0) #glClearColor(0.95, 0.95, 0.95, 0.0) glClearDepth(1.0) glDepthFunc(GL_LESS) glEnable(GL_DEPTH_TEST) glEnable(GL_LINE_SMOOTH) glShadeModel(GL_SMOOTH) glLineWidth(self.linewidth) glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) if self.antialiasing: glHint(GL_LINE_SMOOTH_HINT, GL_NICEST) glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST) #glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE) #glHint(GL_POLYGON_SMOOTH_HINT, GL_DONT_CARE) self.camera.setup_projection() def on_resize(self, w, h): super(PlotWindow, self).on_resize(w, h) if self.camera is not None: self.camera.setup_projection() def update(self, dt): self.controller.update(dt) def draw(self): self.plot._render_lock.acquire() self.camera.apply_transformation() calc_verts_pos, calc_verts_len = 0, 0 calc_cverts_pos, calc_cverts_len = 0, 0 should_update_caption = (clock() - self.last_caption_update > self.caption_update_interval) if len(self.plot._functions.values()) == 0: self.drawing_first_object = True for r in self.plot._functions.itervalues(): if self.drawing_first_object: self.camera.set_rot_preset(r.default_rot_preset) self.drawing_first_object = False glPushMatrix() r._draw() glPopMatrix() # might as well do this while we are # iterating and have the lock rather # than locking and iterating twice # per frame: if should_update_caption: try: if r.calculating_verts: calc_verts_pos += r.calculating_verts_pos calc_verts_len += r.calculating_verts_len if r.calculating_cverts: calc_cverts_pos += r.calculating_cverts_pos calc_cverts_len += r.calculating_cverts_len except ValueError: pass for r in self.plot._pobjects: glPushMatrix() r._draw() glPopMatrix() if should_update_caption: self.update_caption(calc_verts_pos, calc_verts_len, calc_cverts_pos, calc_cverts_len) self.last_caption_update = clock() if self.plot._screenshot: self.plot._screenshot._execute_saving() self.plot._render_lock.release() def update_caption(self, calc_verts_pos, calc_verts_len, calc_cverts_pos, calc_cverts_len): caption = self.title if calc_verts_len or calc_cverts_len: caption += " (calculating" if calc_verts_len > 0: p = (calc_verts_pos / calc_verts_len) * 100 caption += " vertices %i%%" % (p) if calc_cverts_len > 0: p = (calc_cverts_pos / calc_cverts_len) * 100 caption += " colors %i%%" % (p) caption += ")" if self.caption != caption: self.set_caption(caption) sympy-0.7.4.1/sympy/plotting/pygletplot/tests/0000755000175000017500000000000012253362407021632 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/plotting/pygletplot/tests/test_plotting.py0000644000175000017500000000537512253362407025115 0ustar georgeskgeorgeskfrom sympy.external.importtools import import_module disabled = False # if pyglet.gl fails to import, e.g. opengl is missing, we disable the tests pyglet_gl = import_module("pyglet.gl", catch=(OSError,)) pyglet_window = import_module("pyglet.window", catch=(OSError,)) if not pyglet_gl or not pyglet_window: disabled = True def setup_module(module): """py.test support""" if module.disabled: import pytest pytest.skip("Pyglet and/or OpenGL are unavailable.") from sympy import symbols, sin, cos x, y, z = symbols('x, y, z') def test_import(): from sympy.plotting.pygletplot import PygletPlot def test_plot_2d(): from sympy.plotting.pygletplot import PygletPlot p = PygletPlot(x, [x, -5, 5, 4], visible=False) p.wait_for_calculations() def test_plot_2d_discontinuous(): from sympy.plotting.pygletplot import PygletPlot p = PygletPlot(1/x, [x, -1, 1, 2], visible=False) p.wait_for_calculations() def test_plot_3d(): from sympy.plotting.pygletplot import PygletPlot p = PygletPlot(x*y, [x, -5, 5, 5], [y, -5, 5, 5], visible=False) p.wait_for_calculations() def test_plot_3d_discontinuous(): from sympy.plotting.pygletplot import PygletPlot p = PygletPlot(1/x, [x, -3, 3, 6], [y, -1, 1, 1], visible=False) p.wait_for_calculations() def test_plot_2d_polar(): from sympy.plotting.pygletplot import PygletPlot p = PygletPlot(1/x, [x, -1, 1, 4], 'mode=polar', visible=False) p.wait_for_calculations() def test_plot_3d_cylinder(): from sympy.plotting.pygletplot import PygletPlot p = PygletPlot( 1/y, [x, 0, 6.282, 4], [y, -1, 1, 4], 'mode=polar;style=solid', visible=False) p.wait_for_calculations() def test_plot_3d_spherical(): from sympy.plotting.pygletplot import PygletPlot p = PygletPlot( 1, [x, 0, 6.282, 4], [y, 0, 3.141, 4], 'mode=spherical;style=wireframe', visible=False) p.wait_for_calculations() def test_plot_2d_parametric(): from sympy.plotting.pygletplot import PygletPlot p = PygletPlot(sin(x), cos(x), [x, 0, 6.282, 4], visible=False) p.wait_for_calculations() def test_plot_3d_parametric(): from sympy.plotting.pygletplot import PygletPlot p = PygletPlot(sin(x), cos(x), x/5.0, [x, 0, 6.282, 4], visible=False) p.wait_for_calculations() def _test_plot_log(): from sympy.plotting.pygletplot import PygletPlot p = PygletPlot(log(x), [x, 0, 6.282, 4], 'mode=polar', visible=False) p.wait_for_calculations() def test_plot_integral(): # Make sure it doesn't treat x as an independent variable from sympy.plotting.pygletplot import PygletPlot from sympy import Integral p = PygletPlot(Integral(z*x, (x, 1, z), (z, 1, y)), visible=False) p.wait_for_calculations() sympy-0.7.4.1/sympy/plotting/pygletplot/tests/__init__.py0000644000175000017500000000000012253362407023731 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/plotting/pygletplot/__init__.py0000644000175000017500000001025212253362407022601 0ustar georgeskgeorgesk"""Plotting module that can plot 2D and 3D functions """ from sympy.utilities.decorator import doctest_depends_on try: @doctest_depends_on(modules=('pyglet',)) def PygletPlot(*args, **kwargs): """ Plot Examples ============= See examples/advanced/pyglet_plotting.py for many more examples. >>> from sympy.plotting.pygletplot import PygletPlot as Plot >>> from sympy import symbols >>> from sympy.abc import x, y, z >>> Plot(x*y**3-y*x**3) [0]: -x**3*y + x*y**3, 'mode=cartesian' >>> p = Plot() >>> p[1] = x*y >>> p[1].color = z, (0.4,0.4,0.9), (0.9,0.4,0.4) >>> p = Plot() >>> p[1] = x**2+y**2 >>> p[2] = -x**2-y**2 Variable Intervals ================== The basic format is [var, min, max, steps], but the syntax is flexible and arguments left out are taken from the defaults for the current coordinate mode: >>> Plot(x**2) # implies [x,-5,5,100] [0]: x**2, 'mode=cartesian' >>> Plot(x**2, [], []) # [x,-1,1,40], [y,-1,1,40] [0]: x**2, 'mode=cartesian' >>> Plot(x**2-y**2, [100], [100]) # [x,-1,1,100], [y,-1,1,100] [0]: x**2 - y**2, 'mode=cartesian' >>> Plot(x**2, [x,-13,13,100]) [0]: x**2, 'mode=cartesian' >>> Plot(x**2, [-13,13]) # [x,-13,13,100] [0]: x**2, 'mode=cartesian' >>> Plot(x**2, [x,-13,13]) # [x,-13,13,100] [0]: x**2, 'mode=cartesian' >>> Plot(1*x, [], [x], mode='cylindrical') ... # [unbound_theta,0,2*Pi,40], [x,-1,1,20] [0]: x, 'mode=cartesian' Coordinate Modes ================ Plot supports several curvilinear coordinate modes, and they 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[n] = (function arguments) 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 if one variable is used, and a surface if two are used. Supported mode names by number of variables: 1: parametric, cartesian, polar 2: parametric, cartesian, cylindrical = polar, spherical >>> Plot(1, mode='spherical') # doctest: +SKIP Calculator-like Interface ========================= >>> p = Plot(visible=False) >>> f = x**2 >>> p[1] = f >>> p[2] = f.diff(x) >>> p[3] = f.diff(x).diff(x) # doctest: +SKIP >>> p # doctest: +SKIP [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() Plot Window Keyboard Controls ============================= Screen Rotation: X,Y axis Arrow Keys, A,S,D,W, Numpad 4,6,8,2 Z axis Q,E, Numpad 7,9 Model Rotation: Z axis Z,C, Numpad 1,3 Zoom: R,F, PgUp,PgDn, Numpad +,- Reset Camera: X, Numpad 5 Camera Presets: XY F1 XZ F2 YZ F3 Perspective F4 Sensitivity Modifier: SHIFT Axes Toggle: Visible F5 Colors F6 Close Window: ESCAPE ============================= """ import plot return plot.PygletPlot(*args, **kwargs) except Exception as e: def PygletPlot(*args, **kwargs): raise e sympy-0.7.4.1/sympy/plotting/intervalmath/0000755000175000017500000000000012253362407020763 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/plotting/intervalmath/lib_interval.py0000644000175000017500000003503112253362407024011 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.plotting.intervalmath import interval from sympy.external import import_module from sympy.core.compatibility import reduce """ The module contains implemented functions for interval arithmetic.""" def Abs(x): if isinstance(x, (int, float)): return interval(abs(x)) elif isinstance(x, interval): if x.start < 0 and x.end > 0: return interval(0, max(abs(x.start), abs(x.end)), is_valid=x.is_valid) else: return interval(abs(x.start), abs(x.end)) else: raise NotImplementedError #Monotonic def exp(x): """evaluates the exponential of an interval""" np = import_module('numpy') if isinstance(x, (int, float)): return interval(np.exp(x), np.exp(x)) elif isinstance(x, interval): return interval(np.exp(x.start), np.exp(x.end), is_valid=x.is_valid) else: raise NotImplementedError #Monotonic def log(x): """evaluates the natural logarithm of an interval""" np = import_module('numpy') if isinstance(x, (int, float)): if x <= 0: return interval(-np.inf, np.inf, is_valid=False) else: return interval(np.log(x)) elif isinstance(x, interval): if not x.is_valid: return interval(-np.inf, np.inf, is_valid=x.is_valid) elif x.end <= 0: return interval(-np.inf, np.inf, is_valid=False) elif x.start <= 0: return interval(-np.inf, np.inf, is_valid=None) return interval(np.log(x.start), np.log(x.end)) else: raise NotImplementedError #Monotonic def log10(x): """evaluates the logarithm to the base 10 of an interval""" np = import_module('numpy') if isinstance(x, (int, float)): if x <= 0: return interval(-np.inf, np.inf, is_valid=False) else: return interval(np.log10(x)) elif isinstance(x, interval): if not x.is_valid: return interval(-np.inf, np.inf, is_valid=x.is_valid) elif x.end <= 0: return interval(-np.inf, np.inf, is_valid=False) elif x.start <= 0: return interval(-np.inf, np.inf, is_valid=None) return interval(np.log10(x.start), np.log10(x.end)) else: raise NotImplementedError #Monotonic def atan(x): """evaluates the tan inverse of an interval""" np = import_module('numpy') if isinstance(x, (int, float)): return interval(np.arctan(x)) elif isinstance(x, interval): start = np.arctan(x.start) end = np.arctan(x.end) return interval(start, end, is_valid=x.is_valid) else: raise NotImplementedError #periodic def sin(x): """evaluates the sine of an interval""" np = import_module('numpy') if isinstance(x, (int, float)): return interval(np.sin(x)) elif isinstance(x, interval): if not x.is_valid: return interval(-1, 1, is_valid=x.is_valid) na, __ = divmod(x.start, np.pi / 2.0) nb, __ = divmod(x.end, np.pi / 2.0) start = min(np.sin(x.start), np.sin(x.end)) end = max(np.sin(x.start), np.sin(x.end)) if nb - na > 4: return interval(-1, 1, is_valid=x.is_valid) elif na == nb: return interval(start, end, is_valid=x.is_valid) else: if (na - 1) // 4 != (nb - 1) // 4: #sin has max end = 1 if (na - 3) // 4 != (nb - 3) // 4: #sin has min start = -1 return interval(start, end) else: raise NotImplementedError #periodic def cos(x): """Evaluates the cos of an interval""" np = import_module('numpy') if isinstance(x, (int, float)): return interval(np.sin(x)) elif isinstance(x, interval): if not (np.isfinite(x.start) and np.isfinite(x.end)): return interval(-1, 1, is_valid=x.is_valid) na, __ = divmod(x.start, np.pi / 2.0) nb, __ = divmod(x.end, np.pi / 2.0) start = min(np.cos(x.start), np.cos(x.end)) end = max(np.cos(x.start), np.cos(x.end)) if nb - na > 4: #differ more than 2*pi return interval(-1, 1, is_valid=x.is_valid) elif na == nb: #in the same quadarant return interval(start, end, is_valid=x.is_valid) else: if (na) // 4 != (nb) // 4: #cos has max end = 1 if (na - 2) // 4 != (nb - 2) // 4: #cos has min start = -1 return interval(start, end, is_valid=x.is_valid) else: raise NotImplementedError def tan(x): """Evaluates the tan of an interval""" return sin(x) / cos(x) #Monotonic def sqrt(x): """Evaluates the square root of an interval""" np = import_module('numpy') if isinstance(x, (int, float)): if x > 0: return interval(np.sqrt(x)) else: return interval(-np.inf, np.inf, is_valid=False) elif isinstance(x, interval): #Outside the domain if x.end < 0: return interval(-np.inf, np.inf, is_valid=False) #Partially outside the domain elif x.start < 0: return interval(-np.inf, np.inf, is_valid=None) else: return interval(np.sqrt(x.start), np.sqrt(x.end), is_valid=x.is_valid) else: raise NotImplementedError def imin(*args): """Evaluates the minimum of a list of intervals""" np = import_module('numpy') if not all(isinstance(arg, (int, float, interval)) for arg in args): return NotImplementedError else: new_args = [a for a in args if isinstance(a, (int, float)) or a.is_valid] if len(new_args) == 0: if all(a.is_valid is False for a in args): return interval(-np.inf, np.inf, is_valid=False) else: return interval(-np.inf, np.inf, is_valid=None) start_array = [a if isinstance(a, (int, float)) else a.start for a in new_args] end_array = [a if isinstance(a, (int, float)) else a.end for a in new_args] return interval(min(start_array), min(end_array)) def imax(*args): """Evaluates the maximum of a list of intervals""" np = import_module('numpy') if not all(isinstance(arg, (int, float, interval)) for arg in args): return NotImplementedError else: new_args = [a for a in args if isinstance(a, (int, float)) or a.is_valid] if len(new_args) == 0: if all(a.is_valid is False for a in args): return interval(-np.inf, np.inf, is_valid=False) else: return interval(-np.inf, np.inf, is_valid=None) start_array = [a if isinstance(a, (int, float)) else a.start for a in new_args] end_array = [a if isinstance(a, (int, float)) else a.end for a in new_args] return interval(max(start_array), max(end_array)) #Monotonic def sinh(x): """Evaluates the hyperbolic sine of an interval""" np = import_module('numpy') if isinstance(x, (int, float)): return interval(np.sinh(x), np.sinh(x)) elif isinstance(x, interval): return interval(np.sinh(x.start), np.sinh(x.end), is_valid=x.is_valid) else: raise NotImplementedError def cosh(x): """Evaluates the hyperbolic cos of an interval""" np = import_module('numpy') if isinstance(x, (int, float)): return interval(np.cosh(x), np.cosh(x)) elif isinstance(x, interval): #both signs if x.start < 0 and x.end > 0: end = max(np.cosh(x.start), np.cosh(x.end)) return interval(1, end, is_valid=x.is_valid) else: #Monotonic start = np.cosh(x.start) end = np.cosh(x.end) return interval(start, end, is_valid=x.is_valid) else: raise NotImplementedError #Monotonic def tanh(x): """Evaluates the hyperbolic tan of an interval""" np = import_module('numpy') if isinstance(x, (int, float)): return interval(np.tanh(x), np.tanh(x)) elif isinstance(x, interval): return interval(np.tanh(x.start), np.tanh(x.end), is_valid=x.is_valid) else: raise NotImplementedError def asin(x): """Evaluates the inverse sine of an interval""" np = import_module('numpy') if isinstance(x, (int, float)): #Outside the domain if abs(x) > 1: return interval(-np.inf, np.inf, is_valid=False) else: return interval(np.arcsin(x), np.arcsin(x)) elif isinstance(x, interval): #Outside the domain if x.is_valid is False or x.start > 1 or x.end < -1: return interval(-np.inf, np.inf, is_valid=False) #Partially outside the domain elif x.start < -1 or x.end > 1: return interval(-np.inf, np.inf, is_valid=None) else: start = np.arcsin(x.start) end = np.arcsin(x.end) return interval(start, end, is_valid=x.is_valid) def acos(x): """Evaluates the inverse cos of an interval""" np = import_module('numpy') if isinstance(x, (int, float)): if abs(x) > 1: #Outside the domain return interval(-np.inf, np.inf, is_valid=False) else: return interval(np.arccos(x), np.arccos(x)) elif isinstance(x, interval): #Outside the domain if x.is_valid is False or x.start > 1 or x.end < -1: return interval(-np.inf, np.inf, is_valid=False) #Partially outside the domain elif x.start < -1 or x.end > 1: return interval(-np.inf, np.inf, is_valid=None) else: start = np.arccos(x.start) end = np.arccos(x.end) return interval(start, end, is_valid=x.is_valid) def ceil(x): """Evaluates the ceiling of an interval""" np = import_module('numpy') if isinstance(x, (int, float)): return interval(np.ceil(x)) elif isinstance(x, interval): if x.is_valid is False: return interval(-np.inf, np.inf, is_valid=False) else: start = np.ceil(x.start) end = np.ceil(x.end) #Continuous over the interval if start == end: return interval(start, end, is_valid=x.is_valid) else: #Not continuous over the interval return interval(start, end, is_valid=None) else: return NotImplementedError def floor(x): """Evaluates the floor of an interval""" np = import_module('numpy') if isinstance(x, (int, float)): return interval(np.floor(x)) elif isinstance(x, interval): if x.is_valid is False: return interval(-np.inf, np.inf, is_valid=False) else: start = np.floor(x.start) end = np.floor(x.end) #continuous over the argument if start == end: return interval(start, end, is_valid=x.is_valid) else: #not continuous over the interval return interval(start, end, is_valid=None) else: return NotImplementedError def acosh(x): """Evaluates the inverse hyperbolic cosine of an interval""" np = import_module('numpy') if isinstance(x, (int, float)): #Outside the domain if x < 1: return interval(-np.inf, np.inf, is_valid=False) else: return interval(np.arccosh(x)) elif isinstance(x, interval): #Outside the domain if x.end < 1: return interval(-np.inf, np.inf, is_valid=False) #Partly outside the domain elif x.start < 1: return interval(-np.inf, np.inf, is_valid=None) else: start = np.arccosh(x.start) end = np.arccosh(x.end) return interval(start, end, is_valid=x.is_valid) else: return NotImplementedError #Monotonic def asinh(x): """Evaluates the inverse hyperbolic sine of an interval""" np = import_module('numpy') if isinstance(x, (int, float)): return interval(np.arcsinh(x)) elif isinstance(x, interval): start = np.arcsinh(x.start) end = np.arcsinh(x.end) return interval(start, end, is_valid=x.is_valid) else: return NotImplementedError def atanh(x): """Evaluates the inverse hyperbolic tangent of an interval""" np = import_module('numpy') if isinstance(x, (int, float)): #Outside the domain if abs(x) >= 1: return interval(-np.inf, np.inf, is_valid=False) else: return interval(np.arctanh(x)) elif isinstance(x, interval): #outside the domain if x.is_valid is False or x.start >= 1 or x.end <= -1: return interval(-np.inf, np.inf, is_valid=False) #partly outside the domain elif x.start <= -1 or x.end >= 1: return interval(-np.inf, np.inf, is_valid=None) else: start = np.arctanh(x.start) end = np.arctanh(x.end) return interval(start, end, is_valid=x.is_valid) else: return NotImplementedError #Three valued logic for interval plotting. def And(*args): """Defines the three valued ``And`` behaviour for a 2-tuple of three valued logic values""" def reduce_and(cmp_intervala, cmp_intervalb): if cmp_intervala[0] is False or cmp_intervalb[0] is False: first = False elif cmp_intervala[0] is None or cmp_intervalb[0] is None: first = None else: first = True if cmp_intervala[1] is False or cmp_intervalb[1] is False: second = False elif cmp_intervala[1] is None or cmp_intervalb[1] is None: second = None else: second = True return (first, second) return reduce(reduce_and, args) def Or(*args): """Defines the three valued ``Or`` behaviour for a 2-tuple of three valued logic values""" def reduce_or(cmp_intervala, cmp_intervalb): if cmp_intervala[0] is True or cmp_intervalb[0] is True: first = True elif cmp_intervala[0] is None or cmp_intervalb[0] is None: first = None else: first = False if cmp_intervala[1] is True or cmp_intervalb[1] is True: second = True elif cmp_intervala[1] is None or cmp_intervalb[1] is None: second = None else: second = False return (first, second) return reduce(reduce_or, args) sympy-0.7.4.1/sympy/plotting/intervalmath/interval_arithmetic.py0000644000175000017500000004011712253362407025375 0ustar georgeskgeorgesk""" Interval Arithmetic for plotting. This module does not implement interval arithmetic accurately and hence cannot be used for purposes other than plotting. If you want to use interval arithmetic, use mpmath's interval arithmetic. The module implements interval arithmetic using numpy and python floating points. The rounding up and down is not handled and hence this is not an accurate implementation of interval arithmetic. The module uses numpy for speed which cannot be achieved with mpmath. """ # Q: Why use numpy? Why not simply use mpmath's interval arithmetic? # A: mpmath's interval arithmetic simulates a floating point unit # and hence is slow, while numpy evaluations are orders of magnitude # faster. # Q: Why create a separate class for intervals? Why not use sympy's # Interval Sets? # A: The functionalities that will be required for plotting is quite # different from what Interval Sets implement. # Q: Why is rounding up and down according to IEEE754 not handled? # A: It is not possible to do it in both numpy and python. An external # library has to used, which defeats the whole purpose i.e., speed. Also # rounding is handled for very few functions in those libraries. # Q Will my plots be affected? # A It will not affect most of the plots. The interval arithmetic # module based suffers the same problems as that of floating point # arithmetic. from __future__ import print_function, division from sympy.external import import_module from sympy.simplify.simplify import nsimplify class interval(object): """ Represents an interval containing floating points as start and end of the interval The is_valid variable tracks whether the interval obtained as the result of the function is in the domain and is continuous. - True: Represents the interval result of a function is continuous and in the domain of the function. - False: The interval argument of the function was not in the domain of the function, hence the is_valid of the result interval is False - None: The function was not continuous over the interval or the function's argument interval is partly in the domain of the function The comparison of two intervals returns a tuple of two 3-valued logic values. The first value determines the comparison as follows: - True: If the comparison is True throughout the intervals. - False: If the comparison is False throughout the intervals. - None: If the comparison is True for some part of the intervals. The second value is determined as follows: - True: If both the intervals in comparison are valid. - False: If at least one of the intervals is False, else - None """ def __init__(self, *args, **kwargs): self.is_valid = kwargs.pop('is_valid', True) if len(args) == 1: if isinstance(args[0], interval): self.start, self.end = args[0].start, args[0].end else: self.start = float(args[0]) self.end = float(args[0]) elif len(args) == 2: if args[0] < args[1]: self.start = float(args[0]) self.end = float(args[1]) else: self.start = float(args[1]) self.end = float(args[0]) else: raise ValueError("interval takes a maximum of two float values " "as arguments") @property def mid(self): return (self.start + self.end) / 2.0 @property def width(self): return self.end - self.start def __repr__(self): return "interval(%f, %f)" % (self.start, self.end) def __str__(self): return "[%f, %f]" % (self.start, self.end) def __lt__(self, other): if isinstance(other, (int, float)): if self.end < other: return (True, self.is_valid) elif self.start > other: return (False, self.is_valid) else: return (None, self.is_valid) elif isinstance(other, interval): if self.is_valid is False or other.is_valid is False: valid = False elif self.is_valid is None or other.is_valid is None: valid = None else: valid = True if self.end < other. start: return (True, valid) if self.start > other.end: return (False, valid) return (None, valid) else: return NotImplemented def __gt__(self, other): if isinstance(other, (int, float)): if self.start > other: return (True, self.is_valid) elif self.end < other: return (False, self.is_valid) else: return (None, self.is_valid) elif isinstance(other, interval): return other.__lt__(self) else: return NotImplemented def __eq__(self, other): if isinstance(other, (int, float)): if self.start == other and self.end == other: return (True, self.is_valid) if other in self: return (None, self.is_valid) else: return (False, self.is_valid) if isinstance(other, interval): if self.is_valid is False or other.is_valid is False: valid = False elif self.is_valid is None or other.is_valid is None: valid = None else: valid = True if self.start == other.start and self.end == other.end: return (True, valid) elif self.__lt__(other)[0] is not None: return (False, valid) else: return (None, valid) else: return NotImplemented def __ne__(self, other): if isinstance(other, (int, float)): if self.start == other and self.end == other: return (False, self.is_valid) if other in self: return (None, self.is_valid) else: return (True, self.is_valid) if isinstance(other, interval): if self.is_valid is False or other.is_valid is False: valid = False elif self.is_valid is None or other.is_valid is None: valid = None else: valid = True if self.start == other.start and self.end == other.end: return (False, valid) if not self.__lt__(other)[0] is None: return (True, valid) return (None, valid) else: return NotImplemented def __le__(self, other): if isinstance(other, (int, float)): if self.end <= other: return (True, self.is_valid) if self.start > other: return (False, self.is_valid) else: return (None, self.is_valid) if isinstance(other, interval): if self.is_valid is False or other.is_valid is False: valid = False elif self.is_valid is None or other.is_valid is None: valid = None else: valid = True if self.end <= other.start: return (True, valid) if self.start > other.end: return (False, valid) return (None, valid) else: return NotImplemented def __ge__(self, other): if isinstance(other, (int, float)): if self.start >= other: return (True, self.is_valid) elif self.end < other: return (False, self.is_valid) else: return (None, self.is_valid) elif isinstance(other, interval): return other.__le__(self) def __add__(self, other): if isinstance(other, (int, float)): if self.is_valid: return interval(self.start + other, self.end + other) else: start = self.start + other end = self.end + other return interval(start, end, is_valid=self.is_valid) elif isinstance(other, interval): start = self.start + other.start end = self.end + other.end if self.is_valid and other.is_valid: return interval(start, end) elif self.is_valid is False or other.is_valid is False: return interval(start, end, is_valid=False) else: return interval(start, end, is_valid=None) else: return NotImplemented __radd__ = __add__ def __sub__(self, other): if isinstance(other, (int, float)): start = self.start - other end = self.end - other return interval(start, end, is_valid=self.is_valid) elif isinstance(other, interval): start = self.start - other.end end = self.end - other.start if self.is_valid and other.is_valid: return interval(self.start - other.end, self.end - other.start) elif self.is_valid is False or other.is_valid is False: return interval(start, end, is_valid=False) else: return interval(start, end, is_valid=None) else: return NotImplemented def __rsub__(self, other): if isinstance(other, (int, float)): start = other - self.end end = other - self.start return interval(start, end, is_valid=self.is_valid) elif isinstance(other, interval): return other.__sub__(self) else: return NotImplemented def __neg__(self): if self.is_valid: return interval(-self.end, -self.start) else: return interval(-self.end, -self.start, is_valid=self.is_valid) def __mul__(self, other): if isinstance(other, interval): if self.is_valid is False or other.is_valid is False: return interval(-float('inf'), float('inf'), is_valid=False) elif self.is_valid is None or other.is_valid is None: return interval(-float('inf'), float('inf'), is_valid=None) else: inters = [] inters.append(self.start * other.start) inters.append(self.end * other.start) inters.append(self.start * other.end) inters.append(self.end * other.end) start = min(inters) end = max(inters) return interval(start, end) elif isinstance(other, (int, float)): return interval(self.start*other, self.end*other, is_valid=self.is_valid) else: return NotImplemented __rmul__ = __mul__ def __contains__(self, other): if isinstance(other, (int, float)): return self.start <= other and self.end >= other else: return self.start <= other.start and other.end <= self.end def __rdiv__(self, other): if isinstance(other, (int, float)): other = interval(other) return other.__div__(self) elif isinstance(other, interval): return other.__div__(self) else: return NotImplemented def __div__(self, other): # Both None and False are handled if not self.is_valid: # Don't divide as the value is not valid return interval(-float('inf'), float('inf'), is_valid=self.is_valid) if isinstance(other, (int, float)): if other == 0: # Divide by zero encountered. valid nowhere return interval(-float('inf'), float('inf'), is_valid=False) else: return interval(self.start / other, self.end / other) elif isinstance(other, interval): if other.is_valid is False or self.is_valid is False: return interval(-float('inf'), float('inf'), is_valid=False) elif other.is_valid is None or self.is_valid is None: return interval(-float('inf'), float('inf'), is_valid=None) else: # denominator contains both signs, i.e. being divided by zero # return the whole real line with is_valid = None if 0 in other: return interval(-float('inf'), float('inf'), is_valid=None) # denominator negative if other.end < 0: self = -self other = -other # denominator positive inters = [] inters.append(self.start / other.start) inters.append(self.end / other.start) inters.append(self.start / other.end) inters.append(self.end / other.end) start = max(inters) end = min(inters) return interval(start, end) else: return NotImplemented __truediv__ = __div__ __rtruediv__ = __rdiv__ def __pow__(self, other): # Implements only power to an integer. from .lib_interval import exp, log if not self.is_valid: return self if isinstance(other, interval): return exp(other * log(self)) elif isinstance(other, (float, int)): if other < 0: return 1 / self.__pow__(abs(other)) else: if int(other) == other: return _pow_int(self, other) else: return _pow_float(self, other) else: return NotImplemented def __rpow__(self, other): if isinstance(other, (float, int)): if not self.is_valid: #Don't do anything return self elif other < 0: if self.width > 0: return interval(-float('inf'), float('inf'), is_valid=False) else: power_rational = nsimplify(self.start) num, denom = power_rational.as_numer_denom() if denom % 2 == 0: return interval(-float('inf'), float('inf'), is_valid=False) else: start = -abs(other)**self.start end = start return interval(start, end) else: return interval(other**self.start, other**self.end) elif isinstance(other, interval): return other.__pow__(self) else: return NotImplemented def __hash__(self): return hash((self.is_valid, self.start, self.end)) def _pow_float(inter, power): """Evaluates an interval raised to a floating point.""" power_rational = nsimplify(power) num, denom = power_rational.as_numer_denom() if num % 2 == 0: start = abs(inter.start)**power end = abs(inter.end)**power if start < 0: ret = interval(0, max(start, end)) else: ret = interval(start, end) return ret elif denom % 2 == 0: if inter.end < 0: return interval(-float('inf'), float('inf'), is_valid=False) elif inter.start < 0: return interval(0, inter.end**power, is_valid=None) else: return interval(inter.start**power, inter.end**power) else: if inter.start < 0: start = -abs(inter.start)**power else: start = inter.start**power if inter.end < 0: end = -abs(inter.end)**power else: end = inter.end**power return interval(start, end, is_valid=inter.is_valid) def _pow_int(inter, power): """Evaluates an interval raised to an integer power""" power = int(power) if power & 1: return interval(inter.start**power, inter.end**power) else: if inter.start < 0 and inter.end > 0: start = 0 end = max(inter.start**power, inter.end**power) return interval(start, end) else: return interval(inter.start**power, inter.end**power) sympy-0.7.4.1/sympy/plotting/intervalmath/tests/0000755000175000017500000000000012253362407022125 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/plotting/intervalmath/tests/test_intervalmath.py0000644000175000017500000002132512253362407026237 0ustar georgeskgeorgeskfrom __future__ import division from sympy.plotting.intervalmath import interval from sympy.utilities.pytest import raises from sympy.external import import_module def test_interval(): assert (interval(1, 1) == interval(1, 1, is_valid=True)) == (True, True) assert (interval(1, 1) == interval(1, 1, is_valid=False)) == (True, False) assert (interval(1, 1) == interval(1, 1, is_valid=None)) == (True, None) assert (interval(1, 1.5) == interval(1, 2)) == (None, True) assert (interval(0, 1) == interval(2, 3)) == (False, True) assert (interval(0, 1) == interval(1, 2)) == (None, True) assert (interval(1, 2) != interval(1, 2)) == (False, True) assert (interval(1, 3) != interval(2, 3)) == (None, True) assert (interval(1, 3) != interval(-5, -3)) == (True, True) assert ( interval(1, 3, is_valid=False) != interval(-5, -3)) == (True, False) assert (interval(1, 3, is_valid=None) != interval(-5, 3)) == (None, None) assert (interval(4, 4) != 4) == (False, True) assert (interval(1, 1) == 1) == (True, True) assert (interval(1, 3, is_valid=False) == interval(1, 3)) == (True, False) assert (interval(1, 3, is_valid=None) == interval(1, 3)) == (True, None) inter = interval(-5, 5) assert (interval(inter) == interval(-5, 5)) == (True, True) assert inter.width == 10 assert 0 in inter assert -5 in inter assert 5 in inter assert interval(0, 3) in inter assert interval(-6, 2) not in inter assert -5.05 not in inter assert 5.3 not in inter interb = interval(-float('inf'), float('inf')) assert 0 in inter assert inter in interb assert interval(0, float('inf')) in interb assert interval(-float('inf'), 5) in interb assert interval(-1e50, 1e50) in interb assert ( -interval(-1, -2, is_valid=False) == interval(1, 2)) == (True, False) raises(ValueError, lambda: interval(1, 2, 3)) def test_interval_add(): assert (interval(1, 2) + interval(2, 3) == interval(3, 5)) == (True, True) assert (1 + interval(1, 2) == interval(2, 3)) == (True, True) assert (interval(1, 2) + 1 == interval(2, 3)) == (True, True) compare = (1 + interval(0, float('inf')) == interval(1, float('inf'))) assert compare == (True, True) a = 1 + interval(2, 5, is_valid=False) assert a.is_valid is False a = 1 + interval(2, 5, is_valid=None) assert a.is_valid is None a = interval(2, 5, is_valid=False) + interval(3, 5, is_valid=None) assert a.is_valid is False a = interval(3, 5) + interval(-1, 1, is_valid=None) assert a.is_valid is None a = interval(2, 5, is_valid=False) + 1 assert a.is_valid is False def test_interval_sub(): assert (interval(1, 2) - interval(1, 5) == interval(-4, 1)) == (True, True) assert (interval(1, 2) - 1 == interval(0, 1)) == (True, True) assert (1 - interval(1, 2) == interval(-1, 0)) == (True, True) a = 1 - interval(1, 2, is_valid=False) assert a.is_valid is False a = interval(1, 4, is_valid=None) - 1 assert a.is_valid is None a = interval(1, 3, is_valid=False) - interval(1, 3) assert a.is_valid is False a = interval(1, 3, is_valid=None) - interval(1, 3) assert a.is_valid is None def test_interval_inequality(): assert (interval(1, 2) < interval(3, 4)) == (True, True) assert (interval(1, 2) < interval(2, 4)) == (None, True) assert (interval(1, 2) < interval(-2, 0)) == (False, True) assert (interval(1, 2) <= interval(2, 4)) == (True, True) assert (interval(1, 2) <= interval(1.5, 6)) == (None, True) assert (interval(2, 3) <= interval(1, 2)) == (None, True) assert (interval(2, 3) <= interval(1, 1.5)) == (False, True) assert ( interval(1, 2, is_valid=False) <= interval(-2, 0)) == (False, False) assert (interval(1, 2, is_valid=None) <= interval(-2, 0)) == (False, None) assert (interval(1, 2) <= 1.5) == (None, True) assert (interval(1, 2) <= 3) == (True, True) assert (interval(1, 2) <= 0) == (False, True) assert (interval(5, 8) > interval(2, 3)) == (True, True) assert (interval(2, 5) > interval(1, 3)) == (None, True) assert (interval(2, 3) > interval(3.1, 5)) == (False, True) assert (interval(3, 5) > 2) == (True, True) assert (interval(3, 5) < 2) == (False, True) assert (interval(1, 5) < 2) == (None, True) assert (interval(1, 5) > 2) == (None, True) assert (interval(0, 1) > 2) == (False, True) assert (interval(1, 2) >= interval(0, 1)) == (True, True) assert (interval(1, 2) >= interval(0, 1.5)) == (None, True) assert (interval(1, 2) >= interval(3, 4)) == (False, True) assert (interval(1, 2) >= 0) == (True, True) assert (interval(1, 2) >= 1.2) == (None, True) assert (interval(1, 2) >= 3) == (False, True) assert (2 > interval(0, 1)) == (True, True) a = interval(-1, 1, is_valid=False) < interval(2, 5, is_valid=None) assert a == (True, False) a = interval(-1, 1, is_valid=None) < interval(2, 5, is_valid=False) assert a == (True, False) a = interval(-1, 1, is_valid=None) < interval(2, 5, is_valid=None) assert a == (True, None) a = interval(-1, 1, is_valid=False) > interval(-5, -2, is_valid=None) assert a == (True, False) a = interval(-1, 1, is_valid=None) > interval(-5, -2, is_valid=False) assert a == (True, False) a = interval(-1, 1, is_valid=None) > interval(-5, -2, is_valid=None) assert a == (True, None) def test_interval_mul(): assert ( interval(1, 5) * interval(2, 10) == interval(2, 50)) == (True, True) a = interval(-1, 1) * interval(2, 10) == interval(-10, 10) assert a == (True, True) a = interval(-1, 1) * interval(-5, 3) == interval(-5, 5) assert a == (True, True) assert (interval(1, 3) * 2 == interval(2, 6)) == (True, True) assert (3 * interval(-1, 2) == interval(-3, 6)) == (True, True) a = 3 * interval(1, 2, is_valid=False) assert a.is_valid is False a = 3 * interval(1, 2, is_valid=None) assert a.is_valid is None a = interval(1, 5, is_valid=False) * interval(1, 2, is_valid=None) assert a.is_valid is False def test_interval_div(): div = interval(1, 2, is_valid=False) / 3 assert div == interval(-float('inf'), float('inf'), is_valid=False) div = interval(1, 2, is_valid=None) / 3 assert div == interval(-float('inf'), float('inf'), is_valid=None) div = 3 / interval(1, 2, is_valid=None) assert div == interval(-float('inf'), float('inf'), is_valid=None) a = interval(1, 2) / 0 assert a.is_valid is False a = interval(0.5, 1) / interval(-1, 0) assert a.is_valid is None a = interval(0, 1) / interval(0, 1) assert a.is_valid is None a = interval(-1, 1) / interval(-1, 1) assert a.is_valid is None a = interval(-1, 2) / interval(0.5, 1) == interval(-2.0, 4.0) assert a == (True, True) a = interval(0, 1) / interval(0.5, 1) == interval(0.0, 2.0) assert a == (True, True) a = interval(-1, 0) / interval(0.5, 1) == interval(-2.0, 0.0) assert a == (True, True) a = interval(-0.5, -0.25) / interval(0.5, 1) == interval(-1.0, -0.25) assert a == (True, True) a = interval(0.5, 1) / interval(0.5, 1) == interval(0.5, 2.0) assert a == (True, True) a = interval(0.5, 4) / interval(0.5, 1) == interval(0.5, 8.0) assert a == (True, True) a = interval(-1, -0.5) / interval(0.5, 1) == interval(-2.0, -0.5) assert a == (True, True) a = interval(-4, -0.5) / interval(0.5, 1) == interval(-8.0, -0.5) assert a == (True, True) a = interval(-1, 2) / interval(-2, -0.5) == interval(-4.0, 2.0) assert a == (True, True) a = interval(0, 1) / interval(-2, -0.5) == interval(-2.0, 0.0) assert a == (True, True) a = interval(-1, 0) / interval(-2, -0.5) == interval(0.0, 2.0) assert a == (True, True) a = interval(-0.5, -0.25) / interval(-2, -0.5) == interval(0.125, 1.0) assert a == (True, True) a = interval(0.5, 1) / interval(-2, -0.5) == interval(-2.0, -0.25) assert a == (True, True) a = interval(0.5, 4) / interval(-2, -0.5) == interval(-8.0, -0.25) assert a == (True, True) a = interval(-1, -0.5) / interval(-2, -0.5) == interval(0.25, 2.0) assert a == (True, True) a = interval(-4, -0.5) / interval(-2, -0.5) == interval(0.25, 8.0) assert a == (True, True) a = interval(-5, 5, is_valid=False) / 2 assert a.is_valid is False def test_hashable(): ''' test that interval objects are hashable. this is required in order to be able to put them into the cache, which appears to be necessary for plotting in py3k. For details, see: https://github.com/sympy/sympy/pull/2101 https://code.google.com/p/sympy/issues/detail?id=3434 ''' hash(interval(1, 1)) hash(interval(1, 1, is_valid=True)) hash(interval(-4, -0.5)) hash(interval(-2, -0.5)) hash(interval(0.25, 8.0)) sympy-0.7.4.1/sympy/plotting/intervalmath/tests/test_interval_functions.py0000644000175000017500000002312712253362407027457 0ustar georgeskgeorgeskfrom __future__ import division from sympy.external import import_module from sympy.plotting.intervalmath import ( Abs, acos, acosh, And, asin, asinh, atan, atanh, ceil, cos, cosh, exp, floor, imax, imin, interval, log, log10, Or, sin, sinh, sqrt, tan, tanh, ) np = import_module('numpy') if not np: disabled = True #requires Numpy. Hence included in interval_functions def test_interval_pow(): a = 2**interval(1, 2) == interval(2, 4) assert a == (True, True) a = interval(1, 2)**interval(1, 2) == interval(1, 4) assert a == (True, True) a = interval(-1, 1)**interval(0.5, 2) assert a.is_valid is None a = interval(-2, -1) ** interval(1, 2) assert a.is_valid is False a = interval(-2, -1) ** (1 / 2) assert a.is_valid is False a = interval(-1, 1)**(1 / 2) assert a.is_valid is None a = interval(-1, 1)**(1 / 3) == interval(-1, 1) assert a == (True, True) a = interval(-1, 1)**2 == interval(0, 1) assert a == (True, True) a = interval(-1, 1) ** (1 / 29) == interval(-1, 1) assert a == (True, True) a = -2**interval(1, 1) == interval(-2, -2) assert a == (True, True) a = interval(1, 2, is_valid=False)**2 assert a.is_valid is False a = (-3)**interval(1, 2) assert a.is_valid is False a = (-4)**interval(0.5, 0.5) assert a.is_valid is False assert ((-3)**interval(1, 1) == interval(-3, -3)) == (True, True) a = interval(8, 64)**(2 / 3) assert abs(a.start - 4) < 1e-10 # eps assert abs(a.end - 16) < 1e-10 a = interval(-8, 64)**(2 / 3) assert abs(a.start - 4) < 1e-10 # eps assert abs(a.end - 16) < 1e-10 def test_exp(): a = exp(interval(-np.inf, 0)) assert a.start == np.exp(-np.inf) assert a.end == np.exp(0) a = exp(interval(1, 2)) assert a.start == np.exp(1) assert a.end == np.exp(2) a = exp(1) assert a.start == np.exp(1) assert a.end == np.exp(1) def test_log(): a = log(interval(1, 2)) assert a.start == 0 assert a.end == np.log(2) a = log(interval(-1, 1)) assert a.is_valid is None a = log(interval(-3, -1)) assert a.is_valid is False a = log(-3) assert a.is_valid is False a = log(2) assert a.start == np.log(2) assert a.end == np.log(2) def test_log10(): a = log10(interval(1, 2)) assert a.start == 0 assert a.end == np.log10(2) a = log10(interval(-1, 1)) assert a.is_valid is None a = log10(interval(-3, -1)) assert a.is_valid is False a = log10(-3) assert a.is_valid is False a = log10(2) assert a.start == np.log10(2) assert a.end == np.log10(2) def test_atan(): a = atan(interval(0, 1)) assert a.start == np.arctan(0) assert a.end == np.arctan(1) a = atan(1) assert a.start == np.arctan(1) assert a.end == np.arctan(1) def test_sin(): a = sin(interval(0, np.pi / 4)) assert a.start == np.sin(0) assert a.end == np.sin(np.pi / 4) a = sin(interval(-np.pi / 4, np.pi / 4)) assert a.start == np.sin(-np.pi / 4) assert a.end == np.sin(np.pi / 4) a = sin(interval(np.pi / 4, 3 * np.pi / 4)) assert a.start == np.sin(np.pi / 4) assert a.end == 1 a = sin(interval(7 * np.pi / 6, 7 * np.pi / 4)) assert a.start == -1 assert a.end == np.sin(7 * np.pi / 6) a = sin(interval(0, 3 * np.pi)) assert a.start == -1 assert a.end == 1 a = sin(interval(np.pi / 3, 7 * np.pi / 4)) assert a.start == -1 assert a.end == 1 a = sin(np.pi / 4) assert a.start == np.sin(np.pi / 4) assert a.end == np.sin(np.pi / 4) a = sin(interval(1, 2, is_valid=False)) assert a.is_valid is False def test_cos(): a = cos(interval(0, np.pi / 4)) assert a.start == np.cos(np.pi / 4) assert a.end == 1 a = cos(interval(-np.pi / 4, np.pi / 4)) assert a.start == np.cos(-np.pi / 4) assert a.end == 1 a = cos(interval(np.pi / 4, 3 * np.pi / 4)) assert a.start == np.cos(3 * np.pi / 4) assert a.end == np.cos(np.pi / 4) a = cos(interval(3 * np.pi / 4, 5 * np.pi / 4)) assert a.start == -1 assert a.end == np.cos(3 * np.pi / 4) a = cos(interval(0, 3 * np.pi)) assert a.start == -1 assert a.end == 1 a = cos(interval(- np.pi / 3, 5 * np.pi / 4)) assert a.start == -1 assert a.end == 1 a = cos(interval(1, 2, is_valid=False)) assert a.is_valid is False def test_tan(): a = tan(interval(0, np.pi / 4)) assert a.start == 0 assert a.end == np.tan(np.pi / 4) a = tan(interval(np.pi / 4, 3 * np.pi / 4)) #discontinuity assert a.is_valid is None def test_sqrt(): a = sqrt(interval(1, 4)) assert a.start == 1 assert a.end == 2 a = sqrt(interval(0.01, 1)) assert a.start == np.sqrt(0.01) assert a.end == 1 a = sqrt(interval(-1, 1)) assert a.is_valid is None a = sqrt(interval(-3, -1)) assert a.is_valid is False a = sqrt(4) assert (a == interval(2, 2)) == (True, True) a = sqrt(-3) assert a.is_valid is False def test_imin(): a = imin(interval(1, 3), interval(2, 5), interval(-1, 3)) assert a.start == -1 assert a.end == 3 a = imin(-2, interval(1, 4)) assert a.start == -2 assert a.end == -2 a = imin(5, interval(3, 4), interval(-2, 2, is_valid=False)) assert a.start == 3 assert a.end == 4 def test_imax(): a = imax(interval(-2, 2), interval(2, 7), interval(-3, 9)) assert a.start == 2 assert a.end == 9 a = imax(8, interval(1, 4)) assert a.start == 8 assert a.end == 8 a = imax(interval(1, 2), interval(3, 4), interval(-2, 2, is_valid=False)) assert a.start == 3 assert a.end == 4 def test_sinh(): a = sinh(interval(-1, 1)) assert a.start == np.sinh(-1) assert a.end == np.sinh(1) a = sinh(1) assert a.start == np.sinh(1) assert a.end == np.sinh(1) def test_cosh(): a = cosh(interval(1, 2)) assert a.start == np.cosh(1) assert a.end == np.cosh(2) a = cosh(interval(-2, -1)) assert a.start == np.cosh(-1) assert a.end == np.cosh(-2) a = cosh(interval(-2, 1)) assert a.start == 1 assert a.end == np.cosh(-2) a = cosh(1) assert a.start == np.cosh(1) assert a.end == np.cosh(1) def test_tanh(): a = tanh(interval(-3, 3)) assert a.start == np.tanh(-3) assert a.end == np.tanh(3) a = tanh(3) assert a.start == np.tanh(3) assert a.end == np.tanh(3) def test_asin(): a = asin(interval(-0.5, 0.5)) assert a.start == np.arcsin(-0.5) assert a.end == np.arcsin(0.5) a = asin(interval(-1.5, 1.5)) assert a.is_valid is None a = asin(interval(-2, -1.5)) assert a.is_valid is False a = asin(interval(0, 2)) assert a.is_valid is None a = asin(interval(2, 5)) assert a.is_valid is False a = asin(0.5) assert a.start == np.arcsin(0.5) assert a.end == np.arcsin(0.5) a = asin(1.5) assert a.is_valid is False def test_acos(): a = acos(interval(-0.5, 0.5)) assert a.start == np.arccos(0.5) assert a.end == np.arccos(-0.5) a = acos(interval(-1.5, 1.5)) assert a.is_valid is None a = acos(interval(-2, -1.5)) assert a.is_valid is False a = acos(interval(0, 2)) assert a.is_valid is None a = acos(interval(2, 5)) assert a.is_valid is False a = acos(0.5) assert a.start == np.arccos(0.5) assert a.end == np.arccos(0.5) a = acos(1.5) assert a.is_valid is False def test_ceil(): a = ceil(interval(0.2, 0.5)) assert a.start == 1 assert a.end == 1 a = ceil(interval(0.5, 1.5)) assert a.start == 1 assert a.end == 2 assert a.is_valid is None a = ceil(interval(-5, 5)) assert a.is_valid is None a = ceil(5.4) assert a.start == 6 assert a.end == 6 def test_floor(): a = floor(interval(0.2, 0.5)) assert a.start == 0 assert a.end == 0 a = floor(interval(0.5, 1.5)) assert a.start == 0 assert a.end == 1 assert a.is_valid is None a = floor(interval(-5, 5)) assert a.is_valid is None a = floor(5.4) assert a.start == 5 assert a.end == 5 def test_asinh(): a = asinh(interval(1, 2)) assert a.start == np.arcsinh(1) assert a.end == np.arcsinh(2) a = asinh(0.5) assert a.start == np.arcsinh(0.5) assert a.end == np.arcsinh(0.5) def test_acosh(): a = acosh(interval(3, 5)) assert a.start == np.arccosh(3) assert a.end == np.arccosh(5) a = acosh(interval(0, 3)) assert a.is_valid is None a = acosh(interval(-3, 0.5)) assert a.is_valid is False a = acosh(0.5) assert a.is_valid is False a = acosh(2) assert a.start == np.arccosh(2) assert a.end == np.arccosh(2) def test_atanh(): a = atanh(interval(-0.5, 0.5)) assert a.start == np.arctanh(-0.5) assert a.end == np.arctanh(0.5) a = atanh(interval(0, 3)) assert a.is_valid is None a = atanh(interval(-3, -2)) assert a.is_valid is False a = atanh(0.5) assert a.start == np.arctanh(0.5) assert a.end == np.arctanh(0.5) a = atanh(1.5) assert a.is_valid is False def test_Abs(): assert (Abs(interval(-0.5, 0.5)) == interval(0, 0.5)) == (True, True) assert (Abs(interval(-3, -2)) == interval(2, 3)) == (True, True) assert (Abs(-3) == interval(3, 3)) == (True, True) def test_And(): args = [(True, True), (True, False), (True, None)] assert And(*args) == (True, False) args = [(False, True), (None, None), (True, True)] assert And(*args) == (False, None) def test_Or(): args = [(True, True), (True, False), (False, None)] assert Or(*args) == (True, True) args = [(None, None), (False, None), (False, False)] assert Or(*args) == (None, None) sympy-0.7.4.1/sympy/plotting/intervalmath/tests/__init__.py0000644000175000017500000000000012253362407024224 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/plotting/intervalmath/__init__.py0000644000175000017500000000040512253362407023073 0ustar georgeskgeorgeskfrom .interval_arithmetic import interval from .lib_interval import (Abs, exp, log, log10, atan, sin, cos, tan, sqrt, imin, imax, sinh, cosh, tanh, acosh, asinh, atanh, asin, acos, atan, ceil, floor, And, Or) sympy-0.7.4.1/sympy/plotting/plot_implicit.py0000644000175000017500000003256012253362407021515 0ustar georgeskgeorgesk"""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 __future__ import print_function, division 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, Dummy from sympy.external import import_module from sympy.logic.boolalg import BooleanFunction from sympy.utilities.decorator import doctest_depends_on import warnings 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 np = import_module('numpy') 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.") np = import_module('numpy') 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' @doctest_depends_on(modules=('matplotlib',)) 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, And >>> x, y = symbols('x y') Without any ranges for the symbols in the expression >>> p1 = plot_implicit(Eq(x**2 + y**2, 5)) With the range for the symbols >>> p2 = plot_implicit(Eq(x**2 + y**2, 3), ... (x, -3, 3), (y, -3, 3)) With depth of recursion as argument. >>> p3 = plot_implicit(Eq(x**2 + y**2, 5), ... (x, -4, 4), (y, -4, 4), depth = 2) 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) 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) Plotting regions. >>> p6 = plot_implicit(y > x**2) Plotting Using boolean conjunctions. >>> p7 = plot_implicit(And(y > x, y > -x)) """ 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 sympy-0.7.4.1/sympy/plotting/plot.py0000644000175000017500000017150312253362407017624 0ustar georgeskgeorgesk"""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 __future__ import print_function, division from inspect import getargspec from itertools import chain from collections import Callable import warnings from sympy import sympify, Expr, Tuple, Dummy from sympy.external import import_module from sympy.utilities.decorator import doctest_depends_on from .experimental_lambdify import (vectorized_lambdify, lambdify) # N.B. # When changing the minimum module version for matplotlib, please change # the same in the `SymPyDocTestFinder`` in `sympy/utilities/runtests.py` # 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 ############################################################################## 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] 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)) 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 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 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): np = import_module('numpy') 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): np = import_module('numpy') 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): np = import_module('numpy') 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) 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))) 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. """ np = import_module('numpy') #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 range(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): np = import_module('numpy') 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) 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): np = import_module('numpy') 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) 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. np = import_module('numpy') 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 range(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 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__() 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): np = import_module('numpy') 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 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): np = import_module('numpy') c = self.surface_color if isinstance(c, 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) 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): np = import_module('numpy') 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)) 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): np = import_module('numpy') 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): np = import_module('numpy') 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 ## don't have to check for the success of importing matplotlib in each case; ## we will only be using this backend if we can successfully import matploblib class MatplotlibBackend(BaseBackend): def __init__(self, parent): super(MatplotlibBackend, self).__init__(parent) are_3D = [s.is_3D for s in self.parent._series] self.matplotlib = import_module('matplotlib', __import__kwargs={'fromlist': ['pyplot', 'cm', 'collections']}, min_module_version='1.1.0', catch=(RuntimeError,)) self.plt = self.matplotlib.pyplot self.cm = self.matplotlib.cm self.LineCollection = self.matplotlib.collections.LineCollection 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 = self.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): ## mpl_toolkits.mplot3d is necessary for ## projection='3d' mpl_toolkits = import_module('mpl_toolkits', __import__kwargs={'fromlist': ['mplot3d']}) self.fig = self.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 = self.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 mpl_toolkits = import_module('mpl_toolkits', __import__kwargs={'fromlist': ['mplot3d']}) art3d = mpl_toolkits.mplot3d.art3d 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=self.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. ListedColormap = self.matplotlib.colors.ListedColormap 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, 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 self.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, 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. mpl_toolkits = import_module('mpl_toolkits', __import__kwargs={'fromlist': ['mplot3d']}) Axes3D = mpl_toolkits.mplot3d.Axes3D 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 self.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: self.plt.show() def save(self, path): self.process_series() self.fig.savefig(path) def close(self): 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): matplotlib = import_module('matplotlib', min_module_version='1.1.0', catch=(RuntimeError,)) 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): np = import_module('numpy') return np.average(np.vstack((array[:-1], array[1:])), 0) def centers_of_faces(array): np = import_module('numpy') 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""" np = import_module('numpy') 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. @doctest_depends_on(modules=('numpy', 'matplotlib',)) def plot(*args, **kwargs): """ Plots a function of a single variable and returns an instance of the ``Plot`` class (also, see the description of the ``show`` keyword argument below). 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 ``plot`` function: ``show``: Boolean. The default value is set to ``True``. Set show to ``False`` and the function will not display the plot. The returned instance of the ``Plot`` class can then be used to save or display the plot by calling the ``save()`` and ``show()`` methods respectively. 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)) Plot object containing: [0]: cartesian line: x**2 for x over (-5.0, 5.0) Multiple plots with single range. >>> plot(x, x**2, x**3, (x, -5, 5)) Plot object containing: [0]: cartesian line: x for x over (-5.0, 5.0) [1]: cartesian line: x**2 for x over (-5.0, 5.0) [2]: cartesian line: x**3 for x over (-5.0, 5.0) Multiple plots with different ranges. >>> plot((x**2, (x, -6, 6)), (x, (x, -5, 5))) Plot object containing: [0]: cartesian line: x**2 for x over (-6.0, 6.0) [1]: cartesian line: x for x over (-5.0, 5.0) No adaptive sampling. >>> plot(x**2, adaptive=False, nb_of_points=400) Plot object containing: [0]: cartesian line: x**2 for x over (-10.0, 10.0) 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 @doctest_depends_on(modules=('numpy', 'matplotlib',)) 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)) Plot object containing: [0]: parametric cartesian line: (cos(u), sin(u)) for u over (-5.0, 5.0) Multiple parametric plot with single range. >>> plot_parametric((cos(u), sin(u)), (u, cos(u))) Plot object containing: [0]: parametric cartesian line: (cos(u), sin(u)) for u over (-10.0, 10.0) [1]: parametric cartesian line: (u, cos(u)) for u over (-10.0, 10.0) Multiple parametric plots. >>> plot_parametric((cos(u), sin(u), (u, -5, 5)), ... (cos(u), u, (u, -5, 5))) Plot object containing: [0]: parametric cartesian line: (cos(u), sin(u)) for u over (-5.0, 5.0) [1]: parametric cartesian line: (cos(u), u) for u over (-5.0, 5.0) 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 @doctest_depends_on(modules=('numpy', 'matplotlib',)) 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)) Plot object containing: [0]: 3D parametric cartesian line: (cos(u), sin(u), u) for u over (-5.0, 5.0) Multiple plots. >>> plot3d_parametric_line((cos(u), sin(u), u, (u, -5, 5)), ... (sin(u), u**2, u, (u, -5, 5))) Plot object containing: [0]: 3D parametric cartesian line: (cos(u), sin(u), u) for u over (-5.0, 5.0) [1]: 3D parametric cartesian line: (sin(u), u**2, u) for u over (-5.0, 5.0) 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 @doctest_depends_on(modules=('numpy', 'matplotlib',)) 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)) Plot object containing: [0]: cartesian surface: x*y for x over (-5.0, 5.0) and y over (-5.0, 5.0) Multiple plots with same range >>> plot3d(x*y, -x*y, (x, -5, 5), (y, -5, 5)) Plot object containing: [0]: cartesian surface: x*y for x over (-5.0, 5.0) and y over (-5.0, 5.0) [1]: cartesian surface: -x*y for x over (-5.0, 5.0) and y over (-5.0, 5.0) Multiple plots with different ranges. >>> plot3d((x**2 + y**2, (x, -5, 5), (y, -5, 5)), ... (x*y, (x, -3, 3), (y, -3, 3))) Plot object containing: [0]: cartesian surface: x**2 + y**2 for x over (-5.0, 5.0) and y over (-5.0, 5.0) [1]: cartesian surface: x*y for x over (-3.0, 3.0) and y over (-3.0, 3.0) 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 @doctest_depends_on(modules=('numpy', 'matplotlib',)) 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)) Plot object containing: [0]: parametric cartesian surface: (cos(u + v), sin(u - v), u - v) for u over (-5.0, 5.0) and v over (-5.0, 5.0) 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 sympy-0.7.4.1/sympy/plotting/tests/0000755000175000017500000000000012253362407017427 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/plotting/tests/test_plot_implicit.py0000644000175000017500000000440712253362407023715 0ustar georgeskgeorgeskfrom sympy import (plot_implicit, cos, Symbol, Eq, sin, re, And, Or, exp, I, tan, pi) from sympy.plotting.plot import unset_show from tempfile import NamedTemporaryFile from sympy.utilities.pytest import skip from sympy.external import import_module #Set plots not to show unset_show() def tmp_file(name=''): return NamedTemporaryFile(suffix='.png').name def plot_and_save(name): x = Symbol('x') y = Symbol('y') z = Symbol('z') #implicit plot tests plot_implicit(Eq(y, cos(x)), (x, -5, 5), (y, -2, 2)).save(tmp_file(name)) plot_implicit(Eq(y**2, x**3 - x), (x, -5, 5), (y, -4, 4)).save(tmp_file(name)) plot_implicit(y > 1 / x, (x, -5, 5), (y, -2, 2)).save(tmp_file(name)) plot_implicit(y < 1 / tan(x), (x, -5, 5), (y, -2, 2)).save(tmp_file(name)) plot_implicit(y >= 2 * sin(x) * cos(x), (x, -5, 5), (y, -2, 2)).save(tmp_file(name)) plot_implicit(y <= x**2, (x, -3, 3), (y, -1, 5)).save(tmp_file(name)) #Test all input args for plot_implicit plot_implicit(Eq(y**2, x**3 - x)).save(tmp_file()) plot_implicit(Eq(y**2, x**3 - x), adaptive=False).save(tmp_file()) plot_implicit(Eq(y**2, x**3 - x), adaptive=False, points=500).save(tmp_file()) plot_implicit(y > x, (x, -5, 5)).save(tmp_file()) plot_implicit(And(y > exp(x), y > x + 2)).save(tmp_file()) plot_implicit(Or(y > x, y > -x)).save(tmp_file()) plot_implicit(x**2 - 1, (x, -5, 5)).save(tmp_file()) plot_implicit(x**2 - 1).save(tmp_file()) plot_implicit(y > x, depth=-5).save(tmp_file()) plot_implicit(y > x, depth=5).save(tmp_file()) plot_implicit(y > cos(x), adaptive=False).save(tmp_file()) plot_implicit(y < cos(x), adaptive=False).save(tmp_file()) plot_implicit(And(y > cos(x), Or(y > x, Eq(y, x)))).save(tmp_file()) plot_implicit(y - cos(pi / x)).save(tmp_file()) #Test plots which cannot be rendered using the adaptive algorithm #TODO: catch the warning. plot_implicit(Eq(y, re(cos(x) + I*sin(x)))).save(tmp_file(name)) def test_matplotlib(): matplotlib = import_module('matplotlib', min_module_version='1.1.0', catch=(RuntimeError,)) if matplotlib: plot_and_save('test') else: skip("Matplotlib not the default backend") sympy-0.7.4.1/sympy/plotting/tests/test_plot.py0000644000175000017500000001656512253362407022033 0ustar georgeskgeorgeskfrom sympy import (pi, sin, cos, Symbol, Integral, summation, sqrt, log, oo, LambertW, I, meijerg, exp_polar, Max) from sympy.plotting import (plot, plot_parametric, plot3d_parametric_line, plot3d, plot3d_parametric_surface) from sympy.plotting.plot import unset_show from sympy.utilities.pytest import skip from sympy.plotting.experimental_lambdify import lambdify from sympy.external import import_module from tempfile import NamedTemporaryFile import warnings import os unset_show() # XXX: We could implement this as a context manager instead # That would need rewriting the plot_and_save() function # entirely class TmpFileManager: tmp_files = [] @classmethod def tmp_file(cls, name=''): cls.tmp_files.append(NamedTemporaryFile(prefix=name, suffix='.png').name) return cls.tmp_files[-1] @classmethod def cleanup(cls): map(os.remove, cls.tmp_files) def plot_and_save(name): tmp_file = TmpFileManager.tmp_file x = Symbol('x') y = Symbol('y') z = Symbol('z') ### # Examples from the 'introduction' notebook ### p = plot(x) p = plot(x*sin(x), x*cos(x)) p.extend(p) p[0].line_color = lambda a: a p[1].line_color = 'b' p.title = 'Big title' p.xlabel = 'the x axis' p[1].label = 'straight line' p.legend = True p.aspect_ratio = (1, 1) p.xlim = (-15, 20) p.save(tmp_file('%s_basic_options_and_colors' % name)) p.extend(plot(x + 1)) p.append(plot(x + 3, x**2)[1]) p.save(tmp_file('%s_plot_extend_append' % name)) p[2] = plot(x**2, (x, -2, 3)) p.save(tmp_file('%s_plot_setitem' % name)) p = plot(sin(x), (x, -2*pi, 4*pi)) p.save(tmp_file('%s_line_explicit' % name)) p = plot(sin(x)) p.save(tmp_file('%s_line_default_range' % name)) p = plot((x**2, (x, -5, 5)), (x**3, (x, -3, 3))) p.save(tmp_file('%s_line_multiple_range' % name)) #parametric 2d plots. #Single plot with default range. plot_parametric(sin(x), cos(x)).save(tmp_file()) #Single plot with range. p = plot_parametric(sin(x), cos(x), (x, -5, 5)) p.save(tmp_file('%s_parametric_range' % name)) #Multiple plots with same range. p = plot_parametric((sin(x), cos(x)), (x, sin(x))) p.save(tmp_file('%s_parametric_multiple' % name)) #Multiple plots with different ranges. p = plot_parametric((sin(x), cos(x), (x, -3, 3)), (x, sin(x), (x, -5, 5))) p.save(tmp_file('%s_parametric_multiple_ranges' % name)) #depth of recursion specified. p = plot_parametric(x, sin(x), depth=13) p.save(tmp_file('%s_recursion_depth' % name)) #No adaptive sampling. p = plot_parametric(cos(x), sin(x), adaptive=False, nb_of_points=500) p.save(tmp_file('%s_adaptive' % name)) #3d parametric plots p = plot3d_parametric_line(sin(x), cos(x), x) p.save(tmp_file('%s_3d_line' % name)) p = plot3d_parametric_line( (sin(x), cos(x), x, (x, -5, 5)), (cos(x), sin(x), x, (x, -3, 3))) p.save(tmp_file('%s_3d_line_multiple' % name)) p = plot3d_parametric_line(sin(x), cos(x), x, nb_of_points=30) p.save(tmp_file('%s_3d_line_points' % name)) # 3d surface single plot. p = plot3d(x * y) p.save(tmp_file('%s_surface' % name)) # Multiple 3D plots with same range. p = plot3d(-x * y, x * y, (x, -5, 5)) p.save(tmp_file('%s_surface_multiple' % name)) # Multiple 3D plots with different ranges. p = plot3d( (x * y, (x, -3, 3), (y, -3, 3)), (-x * y, (x, -3, 3), (y, -3, 3))) p.save(tmp_file('%s_surface_multiple_ranges' % name)) # Single Parametric 3D plot p = plot3d_parametric_surface(sin(x + y), cos(x - y), x - y) p.save(tmp_file('%s_parametric_surface' % name)) # Multiple Parametric 3D plots. p = plot3d_parametric_surface( (x*sin(z), x*cos(z), z, (x, -5, 5), (z, -5, 5)), (sin(x + y), cos(x - y), x - y, (x, -5, 5), (y, -5, 5))) p.save(tmp_file('%s_parametric_surface' % name)) ### # Examples from the 'colors' notebook ### p = plot(sin(x)) p[0].line_color = lambda a: a p.save(tmp_file('%s_colors_line_arity1' % name)) p[0].line_color = lambda a, b: b p.save(tmp_file('%s_colors_line_arity2' % name)) p = plot(x*sin(x), x*cos(x), (x, 0, 10)) p[0].line_color = lambda a: a p.save(tmp_file('%s_colors_param_line_arity1' % name)) p[0].line_color = lambda a, b: a p.save(tmp_file('%s_colors_param_line_arity2a' % name)) p[0].line_color = lambda a, b: b p.save(tmp_file('%s_colors_param_line_arity2b' % name)) p = plot3d_parametric_line(sin(x) + 0.1*sin(x)*cos(7*x), cos(x) + 0.1*cos(x)*cos(7*x), 0.1*sin(7*x), (x, 0, 2*pi)) p[0].line_color = lambda a: sin(4*a) p.save(tmp_file('%s_colors_3d_line_arity1' % name)) p[0].line_color = lambda a, b: b p.save(tmp_file('%s_colors_3d_line_arity2' % name)) p[0].line_color = lambda a, b, c: c p.save(tmp_file('%s_colors_3d_line_arity3' % name)) p = plot3d(sin(x)*y, (x, 0, 6*pi), (y, -5, 5)) p[0].surface_color = lambda a: a p.save(tmp_file('%s_colors_surface_arity1' % name)) p[0].surface_color = lambda a, b: b p.save(tmp_file('%s_colors_surface_arity2' % name)) p[0].surface_color = lambda a, b, c: c p.save(tmp_file('%s_colors_surface_arity3a' % name)) p[0].surface_color = lambda a, b, c: sqrt((a - 3*pi)**2 + b**2) p.save(tmp_file('%s_colors_surface_arity3b' % name)) p = plot3d_parametric_surface(x * cos(4 * y), x * sin(4 * y), y, (x, -1, 1), (y, -1, 1)) p[0].surface_color = lambda a: a p.save(tmp_file('%s_colors_param_surf_arity1' % name)) p[0].surface_color = lambda a, b: a*b p.save(tmp_file('%s_colors_param_surf_arity2' % name)) p[0].surface_color = lambda a, b, c: sqrt(a**2 + b**2 + c**2) p.save(tmp_file('%s_colors_param_surf_arity3' % name)) ### # Examples from the 'advanced' notebook ### i = Integral(log((sin(x)**2 + 1)*sqrt(x**2 + 1)), (x, 0, y)) p = plot(i, (y, 1, 5)) p.save(tmp_file('%s_advanced_integral' % name)) s = summation(1/x**y, (x, 1, oo)) p = plot(s, (y, 2, 10)) p.save(tmp_file('%s_advanced_inf_sum' % name)) p = plot(summation(1/x, (x, 1, y)), (y, 2, 10), show=False) p[0].only_integers = True p[0].steps = True p.save(tmp_file('%s_advanced_fin_sum' % name)) ### # Test expressions that can not be translated to np and generate complex # results. ### plot(sin(x) + I*cos(x)).save(tmp_file()) plot(sqrt(sqrt(-x))).save(tmp_file()) plot(LambertW(x)).save(tmp_file()) plot(sqrt(LambertW(x))).save(tmp_file()) #Characteristic function of a StudentT distribution with nu=10 plot((meijerg(((1 / 2,), ()), ((5, 0, 1 / 2), ()), 5 * x**2 * exp_polar(-I*pi)/2) + meijerg(((1/2,), ()), ((5, 0, 1/2), ()), 5*x**2 * exp_polar(I*pi)/2)) / (48 * pi), (x, 1e-6, 1e-2)).save(tmp_file()) def test_matplotlib(): matplotlib = import_module('matplotlib', min_module_version='1.1.0', catch=(RuntimeError,)) if matplotlib: try: plot_and_save('test') finally: # clean up TmpFileManager.cleanup() else: skip("Matplotlib not the default backend") # Tests for exceptiion handling in experimental_lambdify def test_experimental_lambify(): x = Symbol('x') lambdify([x], Max(x, 5)) assert Max(2, 5) == 5 assert Max(7, 5) == 7 sympy-0.7.4.1/sympy/plotting/tests/__init__.py0000644000175000017500000000000012253362407021526 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/plotting/__init__.py0000644000175000017500000000044112253362407020375 0ustar georgeskgeorgeskfrom .plot import plot_backends from .plot_implicit import plot_implicit from .proxy_pyglet import Plot from .textplot import textplot from .pygletplot import PygletPlot from .plot import (plot, plot_parametric, plot3d, plot3d_parametric_surface, plot3d_parametric_line) sympy-0.7.4.1/sympy/categories/0000755000175000017500000000000012253362407016552 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/categories/baseclasses.py0000644000175000017500000007360312253362407021425 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import (Set, Basic, FiniteSet, EmptySet, Dict, Symbol, Tuple) from sympy.core.compatibility import xrange class Class(Set): r""" The base class for any kind of class in the set-theoretic sense. In axiomatic set theories, everything is a class. A class which can be a member of another class is a set. A class which is not a member of another class is a proper class. The class `\{1, 2\}` is a set; the class of all sets is a proper class. This class is essentially a synonym for :class:`sympy.core.Set`. The goal of this class is to assure easier migration to the eventual proper implementation of set theory. """ is_proper = False class Object(Symbol): """ The base class for any kind of object in an abstract category. While technically any instance of :class:`Basic` will do, this class is the recommended way to create abstract objects in abstract categories. """ class Morphism(Basic): """ The base class for any morphism in an abstract category. In abstract categories, a morphism is an arrow between two category objects. The object where the arrow starts is called the domain, while the object where the arrow ends is called the codomain. Two morphisms between the same pair of objects are considered to be the same morphisms. To distinguish between morphisms between the same objects use :class:`NamedMorphism`. It is prohibited to instantiate this class. Use one of the derived classes instead. See Also ======== IdentityMorphism, NamedMorphism, CompositeMorphism """ def __new__(cls, domain, codomain): raise(NotImplementedError( "Cannot instantiate Morphism. Use derived classes instead.")) @property def domain(self): """ Returns the domain of the morphism. Examples ======== >>> from sympy.categories import Object, NamedMorphism >>> A = Object("A") >>> B = Object("B") >>> f = NamedMorphism(A, B, "f") >>> f.domain Object("A") """ return self.args[0] @property def codomain(self): """ Returns the codomain of the morphism. Examples ======== >>> from sympy.categories import Object, NamedMorphism >>> A = Object("A") >>> B = Object("B") >>> f = NamedMorphism(A, B, "f") >>> f.codomain Object("B") """ return self.args[1] def compose(self, other): r""" Composes self with the supplied morphism. The order of elements in the composition is the usual order, i.e., to construct `g\circ f` use ``g.compose(f)``. Examples ======== >>> from sympy.categories import Object, NamedMorphism >>> A = Object("A") >>> B = Object("B") >>> C = Object("C") >>> f = NamedMorphism(A, B, "f") >>> g = NamedMorphism(B, C, "g") >>> g * f CompositeMorphism((NamedMorphism(Object("A"), Object("B"), "f"), NamedMorphism(Object("B"), Object("C"), "g"))) >>> (g * f).domain Object("A") >>> (g * f).codomain Object("C") """ return CompositeMorphism(other, self) def __mul__(self, other): r""" Composes self with the supplied morphism. The semantics of this operation is given by the following equation: ``g * f == g.compose(f)`` for composable morphisms ``g`` and ``f``. See Also ======== compose """ return self.compose(other) class IdentityMorphism(Morphism): """ Represents an identity morphism. An identity morphism is a morphism with equal domain and codomain, which acts as an identity with respect to composition. Examples ======== >>> from sympy.categories import Object, NamedMorphism, IdentityMorphism >>> A = Object("A") >>> B = Object("B") >>> f = NamedMorphism(A, B, "f") >>> id_A = IdentityMorphism(A) >>> id_B = IdentityMorphism(B) >>> f * id_A == f True >>> id_B * f == f True See Also ======== Morphism """ def __new__(cls, domain): return Basic.__new__(cls, domain, domain) class NamedMorphism(Morphism): """ Represents a morphism which has a name. Names are used to distinguish between morphisms which have the same domain and codomain: two named morphisms are equal if they have the same domains, codomains, and names. Examples ======== >>> from sympy.categories import Object, NamedMorphism >>> A = Object("A") >>> B = Object("B") >>> f = NamedMorphism(A, B, "f") >>> f NamedMorphism(Object("A"), Object("B"), "f") >>> f.name 'f' See Also ======== Morphism """ def __new__(cls, domain, codomain, name): if not name: raise ValueError("Empty morphism names not allowed.") return Basic.__new__(cls, domain, codomain, Symbol(name)) @property def name(self): """ Returns the name of the morphism. Examples ======== >>> from sympy.categories import Object, NamedMorphism >>> A = Object("A") >>> B = Object("B") >>> f = NamedMorphism(A, B, "f") >>> f.name 'f' """ return self.args[2].name class CompositeMorphism(Morphism): r""" Represents a morphism which is a composition of other morphisms. Two composite morphisms are equal if the morphisms they were obtained from (components) are the same and were listed in the same order. The arguments to the constructor for this class should be listed in diagram order: to obtain the composition `g\circ f` from the instances of :class:`Morphism` ``g`` and ``f`` use ``CompositeMorphism(f, g)``. Examples ======== >>> from sympy.categories import Object, NamedMorphism, CompositeMorphism >>> A = Object("A") >>> B = Object("B") >>> C = Object("C") >>> f = NamedMorphism(A, B, "f") >>> g = NamedMorphism(B, C, "g") >>> g * f CompositeMorphism((NamedMorphism(Object("A"), Object("B"), "f"), NamedMorphism(Object("B"), Object("C"), "g"))) >>> CompositeMorphism(f, g) == g * f True """ @staticmethod def _add_morphism(t, morphism): """ Intelligently adds ``morphism`` to tuple ``t``. If ``morphism`` is a composite morphism, its components are added to the tuple. If ``morphism`` is an identity, nothing is added to the tuple. No composability checks are performed. """ if isinstance(morphism, CompositeMorphism): # ``morphism`` is a composite morphism; we have to # denest its components. return t + morphism.components elif isinstance(morphism, IdentityMorphism): # ``morphism`` is an identity. Nothing happens. return t else: return t + Tuple(morphism) def __new__(cls, *components): if components and not isinstance(components[0], Morphism): # Maybe the user has explicitly supplied a list of # morphisms. return CompositeMorphism.__new__(cls, *components[0]) normalised_components = Tuple() # TODO: Fix the unpythonicity. for i in xrange(len(components) - 1): current = components[i] following = components[i + 1] if not isinstance(current, Morphism) or \ not isinstance(following, Morphism): raise TypeError("All components must be morphisms.") if current.codomain != following.domain: raise ValueError("Uncomposable morphisms.") normalised_components = CompositeMorphism._add_morphism( normalised_components, current) # We haven't added the last morphism to the list of normalised # components. Add it now. normalised_components = CompositeMorphism._add_morphism( normalised_components, components[-1]) if not normalised_components: # If ``normalised_components`` is empty, only identities # were supplied. Since they all were composable, they are # all the same identities. return components[0] elif len(normalised_components) == 1: # No sense to construct a whole CompositeMorphism. return normalised_components[0] return Basic.__new__(cls, normalised_components) @property def components(self): """ Returns the components of this composite morphism. Examples ======== >>> from sympy.categories import Object, NamedMorphism >>> A = Object("A") >>> B = Object("B") >>> C = Object("C") >>> f = NamedMorphism(A, B, "f") >>> g = NamedMorphism(B, C, "g") >>> (g * f).components (NamedMorphism(Object("A"), Object("B"), "f"), NamedMorphism(Object("B"), Object("C"), "g")) """ return self.args[0] @property def domain(self): """ Returns the domain of this composite morphism. The domain of the composite morphism is the domain of its first component. Examples ======== >>> from sympy.categories import Object, NamedMorphism >>> A = Object("A") >>> B = Object("B") >>> C = Object("C") >>> f = NamedMorphism(A, B, "f") >>> g = NamedMorphism(B, C, "g") >>> (g * f).domain Object("A") """ return self.components[0].domain @property def codomain(self): """ Returns the codomain of this composite morphism. The codomain of the composite morphism is the codomain of its last component. Examples ======== >>> from sympy.categories import Object, NamedMorphism >>> A = Object("A") >>> B = Object("B") >>> C = Object("C") >>> f = NamedMorphism(A, B, "f") >>> g = NamedMorphism(B, C, "g") >>> (g * f).codomain Object("C") """ return self.components[-1].codomain def flatten(self, new_name): """ Forgets the composite structure of this morphism. If ``new_name`` is not empty, returns a :class:`NamedMorphism` with the supplied name, otherwise returns a :class:`Morphism`. In both cases the domain of the new morphism is the domain of this composite morphism and the codomain of the new morphism is the codomain of this composite morphism. Examples ======== >>> from sympy.categories import Object, NamedMorphism >>> A = Object("A") >>> B = Object("B") >>> C = Object("C") >>> f = NamedMorphism(A, B, "f") >>> g = NamedMorphism(B, C, "g") >>> (g * f).flatten("h") NamedMorphism(Object("A"), Object("C"), "h") """ return NamedMorphism(self.domain, self.codomain, new_name) class Category(Basic): r""" An (abstract) category. A category [JoyOfCats] is a quadruple `\mbox{K} = (O, \hom, id, \circ)` consisting of * a (set-theoretical) class `O`, whose members are called `K`-objects, * for each pair `(A, B)` of `K`-objects, a set `\hom(A, B)` whose members are called `K`-morphisms from `A` to `B`, * for a each `K`-object `A`, a morphism `id:A\rightarrow A`, called the `K`-identity of `A`, * a composition law `\circ` associating with every `K`-morphisms `f:A\rightarrow B` and `g:B\rightarrow C` a `K`-morphism `g\circ f:A\rightarrow C`, called the composite of `f` and `g`. Composition is associative, `K`-identities are identities with respect to composition, and the sets `\hom(A, B)` are pairwise disjoint. This class knows nothing about its objects and morphisms. Concrete cases of (abstract) categories should be implemented as classes derived from this one. Certain instances of :class:`Diagram` can be asserted to be commutative in a :class:`Category` by supplying the argument ``commutative_diagrams`` in the constructor. Examples ======== >>> from sympy.categories import Object, NamedMorphism, Diagram, Category >>> from sympy import FiniteSet >>> A = Object("A") >>> B = Object("B") >>> C = Object("C") >>> f = NamedMorphism(A, B, "f") >>> g = NamedMorphism(B, C, "g") >>> d = Diagram([f, g]) >>> K = Category("K", commutative_diagrams=[d]) >>> K.commutative_diagrams == FiniteSet(d) True See Also ======== Diagram """ def __new__(cls, name, objects=EmptySet(), commutative_diagrams=EmptySet()): if not name: raise ValueError("A Category cannot have an empty name.") new_category = Basic.__new__(cls, Symbol(name), Class(objects), FiniteSet(commutative_diagrams)) return new_category @property def name(self): """ Returns the name of this category. Examples ======== >>> from sympy.categories import Category >>> K = Category("K") >>> K.name 'K' """ return self.args[0].name @property def objects(self): """ Returns the class of objects of this category. Examples ======== >>> from sympy.categories import Object, Category >>> from sympy import FiniteSet >>> A = Object("A") >>> B = Object("B") >>> K = Category("K", FiniteSet(A, B)) >>> K.objects Class({Object("A"), Object("B")}) """ return self.args[1] @property def commutative_diagrams(self): """ Returns the :class:`FiniteSet` of diagrams which are known to be commutative in this category. >>> from sympy.categories import Object, NamedMorphism, Diagram, Category >>> from sympy import FiniteSet >>> A = Object("A") >>> B = Object("B") >>> C = Object("C") >>> f = NamedMorphism(A, B, "f") >>> g = NamedMorphism(B, C, "g") >>> d = Diagram([f, g]) >>> K = Category("K", commutative_diagrams=[d]) >>> K.commutative_diagrams == FiniteSet(d) True """ return self.args[2] def hom(self, A, B): raise NotImplementedError( "hom-sets are not implemented in Category.") def all_morphisms(self): raise NotImplementedError( "Obtaining the class of morphisms is not implemented in Category.") class Diagram(Basic): r""" Represents a diagram in a certain category. Informally, a diagram is a collection of objects of a category and certain morphisms between them. A diagram is still a monoid with respect to morphism composition; i.e., identity morphisms, as well as all composites of morphisms included in the diagram belong to the diagram. For a more formal approach to this notion see [Pare1970]. The components of composite morphisms are also added to the diagram. No properties are assigned to such morphisms by default. A commutative diagram is often accompanied by a statement of the following kind: "if such morphisms with such properties exist, then such morphisms which such properties exist and the diagram is commutative". To represent this, an instance of :class:`Diagram` includes a collection of morphisms which are the premises and another collection of conclusions. ``premises`` and ``conclusions`` associate morphisms belonging to the corresponding categories with the :class:`FiniteSet`'s of their properties. The set of properties of a composite morphism is the intersection of the sets of properties of its components. The domain and codomain of a conclusion morphism should be among the domains and codomains of the morphisms listed as the premises of a diagram. No checks are carried out of whether the supplied object and morphisms do belong to one and the same category. Examples ======== >>> from sympy.categories import Object, NamedMorphism, Diagram >>> from sympy import FiniteSet, pprint, default_sort_key >>> A = Object("A") >>> B = Object("B") >>> C = Object("C") >>> f = NamedMorphism(A, B, "f") >>> g = NamedMorphism(B, C, "g") >>> d = Diagram([f, g]) >>> premises_keys = sorted(d.premises.keys(), key=default_sort_key) >>> pprint(premises_keys, use_unicode=False) [g*f:A-->C, id:A-->A, id:B-->B, id:C-->C, f:A-->B, g:B-->C] >>> pprint(d.premises, use_unicode=False) {g*f:A-->C: EmptySet(), id:A-->A: EmptySet(), id:B-->B: EmptySet(), id:C-->C: EmptySet(), f:A-->B: EmptySet(), g:B-->C: EmptySet()} >>> d = Diagram([f, g], {g * f: "unique"}) >>> pprint(d.conclusions) {g*f:A-->C: {unique}} References ========== [Pare1970] B. Pareigis: Categories and functors. Academic Press, 1970. """ @staticmethod def _set_dict_union(dictionary, key, value): """ If ``key`` is in ``dictionary``, set the new value of ``key`` to be the union between the old value and ``value``. Otherwise, set the value of ``key`` to ``value. Returns ``True`` if the key already was in the dictionary and ``False`` otherwise. """ if key in dictionary: dictionary[key] = dictionary[key] | value return True else: dictionary[key] = value return False @staticmethod def _add_morphism_closure(morphisms, morphism, props, add_identities=True, recurse_composites=True): """ Adds a morphism and its attributes to the supplied dictionary ``morphisms``. If ``add_identities`` is True, also adds the identity morphisms for the domain and the codomain of ``morphism``. """ if not Diagram._set_dict_union(morphisms, morphism, props): # We have just added a new morphism. if isinstance(morphism, IdentityMorphism): if props: # Properties for identity morphisms don't really # make sense, because very much is known about # identity morphisms already, so much that they # are trivial. Having properties for identity # morphisms would only be confusing. raise ValueError( "Instances of IdentityMorphism cannot have properties.") return if add_identities: empty = EmptySet() id_dom = IdentityMorphism(morphism.domain) id_cod = IdentityMorphism(morphism.codomain) Diagram._set_dict_union(morphisms, id_dom, empty) Diagram._set_dict_union(morphisms, id_cod, empty) for existing_morphism, existing_props in list(morphisms.items()): new_props = existing_props & props if morphism.domain == existing_morphism.codomain: left = morphism * existing_morphism Diagram._set_dict_union(morphisms, left, new_props) if morphism.codomain == existing_morphism.domain: right = existing_morphism * morphism Diagram._set_dict_union(morphisms, right, new_props) if isinstance(morphism, CompositeMorphism) and recurse_composites: # This is a composite morphism, add its components as # well. empty = EmptySet() for component in morphism.components: Diagram._add_morphism_closure(morphisms, component, empty, add_identities) def __new__(cls, *args): """ Construct a new instance of Diagram. If no arguments are supplied, an empty diagram is created. If at least an argument is supplied, ``args[0]`` is interpreted as the premises of the diagram. If ``args[0]`` is a list, it is interpreted as a list of :class:`Morphism`'s, in which each :class:`Morphism` has an empty set of properties. If ``args[0]`` is a Python dictionary or a :class:`Dict`, it is interpreted as a dictionary associating to some :class:`Morphism`'s some properties. If at least two arguments are supplied ``args[1]`` is interpreted as the conclusions of the diagram. The type of ``args[1]`` is interpreted in exactly the same way as the type of ``args[0]``. If only one argument is supplied, the diagram has no conclusions. Examples ======== >>> from sympy.categories import Object, NamedMorphism >>> from sympy.categories import IdentityMorphism, Diagram >>> A = Object("A") >>> B = Object("B") >>> C = Object("C") >>> f = NamedMorphism(A, B, "f") >>> g = NamedMorphism(B, C, "g") >>> d = Diagram([f, g]) >>> IdentityMorphism(A) in d.premises.keys() True >>> g * f in d.premises.keys() True >>> d = Diagram([f, g], {g * f: "unique"}) >>> d.conclusions[g * f] {unique} """ premises = {} conclusions = {} # Here we will keep track of the objects which appear in the # premises. objects = EmptySet() if len(args) >= 1: # We've got some premises in the arguments. premises_arg = args[0] if isinstance(premises_arg, list): # The user has supplied a list of morphisms, none of # which have any attributes. empty = EmptySet() for morphism in premises_arg: objects |= FiniteSet(morphism.domain, morphism.codomain) Diagram._add_morphism_closure(premises, morphism, empty) elif isinstance(premises_arg, dict) or isinstance(premises_arg, Dict): # The user has supplied a dictionary of morphisms and # their properties. for morphism, props in premises_arg.items(): objects |= FiniteSet(morphism.domain, morphism.codomain) Diagram._add_morphism_closure( premises, morphism, FiniteSet(props)) if len(args) >= 2: # We also have some conclusions. conclusions_arg = args[1] if isinstance(conclusions_arg, list): # The user has supplied a list of morphisms, none of # which have any attributes. empty = EmptySet() for morphism in conclusions_arg: # Check that no new objects appear in conclusions. if (morphism.domain in objects) and \ (morphism.codomain in objects): # No need to add identities and recurse # composites this time. Diagram._add_morphism_closure( conclusions, morphism, empty, add_identities=False, recurse_composites=False) elif isinstance(conclusions_arg, dict) or \ isinstance(conclusions_arg, Dict): # The user has supplied a dictionary of morphisms and # their properties. for morphism, props in conclusions_arg.items(): # Check that no new objects appear in conclusions. if (morphism.domain in objects) and \ (morphism.codomain in objects): # No need to add identities and recurse # composites this time. Diagram._add_morphism_closure( conclusions, morphism, FiniteSet(props), add_identities=False, recurse_composites=False) return Basic.__new__(cls, Dict(premises), Dict(conclusions), objects) @property def premises(self): """ Returns the premises of this diagram. Examples ======== >>> from sympy.categories import Object, NamedMorphism >>> from sympy.categories import IdentityMorphism, Diagram >>> from sympy import pretty >>> A = Object("A") >>> B = Object("B") >>> f = NamedMorphism(A, B, "f") >>> id_A = IdentityMorphism(A) >>> id_B = IdentityMorphism(B) >>> d = Diagram([f]) >>> print(pretty(d.premises, use_unicode=False)) {id:A-->A: EmptySet(), id:B-->B: EmptySet(), f:A-->B: EmptySet()} """ return self.args[0] @property def conclusions(self): """ Returns the conclusions of this diagram. Examples ======== >>> from sympy.categories import Object, NamedMorphism >>> from sympy.categories import IdentityMorphism, Diagram >>> from sympy import FiniteSet >>> A = Object("A") >>> B = Object("B") >>> C = Object("C") >>> f = NamedMorphism(A, B, "f") >>> g = NamedMorphism(B, C, "g") >>> d = Diagram([f, g]) >>> IdentityMorphism(A) in d.premises.keys() True >>> g * f in d.premises.keys() True >>> d = Diagram([f, g], {g * f: "unique"}) >>> d.conclusions[g * f] == FiniteSet("unique") True """ return self.args[1] @property def objects(self): """ Returns the :class:`FiniteSet` of objects that appear in this diagram. Examples ======== >>> from sympy.categories import Object, NamedMorphism, Diagram >>> A = Object("A") >>> B = Object("B") >>> C = Object("C") >>> f = NamedMorphism(A, B, "f") >>> g = NamedMorphism(B, C, "g") >>> d = Diagram([f, g]) >>> d.objects {Object("A"), Object("B"), Object("C")} """ return self.args[2] def hom(self, A, B): """ Returns a 2-tuple of sets of morphisms between objects A and B: one set of morphisms listed as premises, and the other set of morphisms listed as conclusions. Examples ======== >>> from sympy.categories import Object, NamedMorphism, Diagram >>> from sympy import pretty >>> 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"}) >>> print(pretty(d.hom(A, C), use_unicode=False)) ({g*f:A-->C}, {g*f:A-->C}) See Also ======== Object, Morphism """ premises = EmptySet() conclusions = EmptySet() for morphism in self.premises.keys(): if (morphism.domain == A) and (morphism.codomain == B): premises |= FiniteSet(morphism) for morphism in self.conclusions.keys(): if (morphism.domain == A) and (morphism.codomain == B): conclusions |= FiniteSet(morphism) return (premises, conclusions) def is_subdiagram(self, diagram): """ Checks whether ``diagram`` is a subdiagram of ``self``. Diagram `D'` is a subdiagram of `D` if all premises (conclusions) of `D'` are contained in the premises (conclusions) of `D`. The morphisms contained both in `D'` and `D` should have the same properties for `D'` to be a subdiagram of `D`. Examples ======== >>> from sympy.categories import Object, NamedMorphism, 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"}) >>> d1 = Diagram([f]) >>> d.is_subdiagram(d1) True >>> d1.is_subdiagram(d) False """ premises = all([(m in self.premises) and (diagram.premises[m] == self.premises[m]) for m in diagram.premises]) if not premises: return False conclusions = all([(m in self.conclusions) and (diagram.conclusions[m] == self.conclusions[m]) for m in diagram.conclusions]) # Premises is surely ``True`` here. return conclusions def subdiagram_from_objects(self, objects): """ If ``objects`` is a subset of the objects of ``self``, returns a diagram which has as premises all those premises of ``self`` which have a domains and codomains in ``objects``, likewise for conclusions. Properties are preserved. Examples ======== >>> from sympy.categories import Object, NamedMorphism, Diagram >>> from sympy import FiniteSet >>> A = Object("A") >>> B = Object("B") >>> C = Object("C") >>> f = NamedMorphism(A, B, "f") >>> g = NamedMorphism(B, C, "g") >>> d = Diagram([f, g], {f: "unique", g*f: "veryunique"}) >>> d1 = d.subdiagram_from_objects(FiniteSet(A, B)) >>> d1 == Diagram([f], {f: "unique"}) True """ if not self.objects.subset(objects): raise ValueError( "Supplied objects should all belong to the diagram.") new_premises = {} for morphism, props in self.premises.items(): if (morphism.domain in objects) and (morphism.codomain in objects): new_premises[morphism] = props new_conclusions = {} for morphism, props in self.conclusions.items(): if (morphism.domain in objects) and (morphism.codomain in objects): new_conclusions[morphism] = props return Diagram(new_premises, new_conclusions) sympy-0.7.4.1/sympy/categories/diagram_drawing.py0000644000175000017500000027251312253362407022255 0ustar georgeskgeorgeskr""" 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 __future__ import print_function, division 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, xrange from sympy.printing import latex from sympy.utilities.decorator import doctest_depends_on 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 xrange(width)] for i in xrange(height)] @property def width(self): return self._width @property def height(self): return self._height def __getitem__(self, i_j): """ Returns the element located at in the i-th line and j-th column. """ i, j = i_j return self._array[i][j] def __setitem__(self, i_j, newvalue): """ Sets the element located at in the i-th line and j-th column. """ i, j = i_j 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 xrange(self._width)]) def append_column(self): """ Appends an empty column to the grid. """ self._width += 1 for i in xrange(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 xrange(self._width)]) def prepend_column(self): """ Prepends the grid with an empty column. """ self._width += 1 for i in xrange(self._height): self._array[i].insert(0, None) 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 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 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 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 C D 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 B C D 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 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(premises.items(), 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 xrange(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 xrange(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 xrange(grid.height): for j in xrange(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 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 xrange(top_grid.width)) for i in xrange(top_grid.height)] column_widths = [max(group_size(top_grid[i, j])[1] for i in xrange(top_grid.height)) for j in xrange(top_grid.width)] grid = _GrowableGrid(sum(column_widths), sum(row_heights)) real_row = 0 real_column = 0 for logical_row in xrange(top_grid.height): for logical_column in xrange(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 xrange(local_grid.height): for j in xrange(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 xrange(grid.width): for j in xrange(grid.height): final_grid[i, j] = grid[i, j] start_j = grid.width for i in xrange(remaining_grid.height): for j in xrange(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 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 xrange(current_index)] for o, idx in 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 xrange(g.height): for j in xrange(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 xrange(self._grid.height): for j in xrange(self._grid.width): grid[j, i] = self._grid[i, j] self._grid = grid @property 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 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, i_j): """ 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 = i_j return self._grid[i, j] @property 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) 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) 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 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 xrange(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 xrange(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 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 xrange(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 xrange(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 xrange(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 xrange(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 xrange(start, end + 1) else: return xrange(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 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 xrange(grid.height): for j in xrange(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 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 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 xrange(grid.height): for j in xrange(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) 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) @doctest_depends_on(exe=('latex', 'dvipng'), modules=('pyglet',)) 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) 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",)) sympy-0.7.4.1/sympy/categories/tests/0000755000175000017500000000000012253362407017714 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/categories/tests/test_baseclasses.py0000644000175000017500000001311712253362407023620 0ustar georgeskgeorgeskfrom sympy.categories import (Object, Morphism, IdentityMorphism, NamedMorphism, CompositeMorphism, Diagram, Category) from sympy.categories.baseclasses import Class from sympy.utilities.pytest import XFAIL, raises from sympy import FiniteSet, EmptySet, Dict, Tuple def test_morphisms(): A = Object("A") B = Object("B") C = Object("C") D = Object("D") # Test the base morphism. f = NamedMorphism(A, B, "f") assert f.domain == A assert f.codomain == B assert f == NamedMorphism(A, B, "f") # Test identities. id_A = IdentityMorphism(A) id_B = IdentityMorphism(B) assert id_A.domain == A assert id_A.codomain == A assert id_A == IdentityMorphism(A) assert id_A != id_B # Test named morphisms. g = NamedMorphism(B, C, "g") assert g.name == "g" assert g != f assert g == NamedMorphism(B, C, "g") assert g != NamedMorphism(B, C, "f") # Test composite morphisms. assert f == CompositeMorphism(f) k = g.compose(f) assert k.domain == A assert k.codomain == C assert k.components == Tuple(f, g) assert g * f == k assert CompositeMorphism(f, g) == k assert CompositeMorphism(g * f) == g * f # Test the associativity of composition. h = NamedMorphism(C, D, "h") p = h * g u = h * g * f assert h * k == u assert p * f == u assert CompositeMorphism(f, g, h) == u # Test flattening. u2 = u.flatten("u") assert isinstance(u2, NamedMorphism) assert u2.name == "u" assert u2.domain == A assert u2.codomain == D # Test identities. assert f * id_A == f assert id_B * f == f assert id_A * id_A == id_A assert CompositeMorphism(id_A) == id_A # Test bad compositions. raises(ValueError, lambda: f * g) raises(TypeError, lambda: f.compose(None)) raises(TypeError, lambda: id_A.compose(None)) raises(TypeError, lambda: f * None) raises(TypeError, lambda: id_A * None) raises(TypeError, lambda: CompositeMorphism(f, None, 1)) raises(ValueError, lambda: NamedMorphism(A, B, "")) raises(NotImplementedError, lambda: Morphism(A, B)) def test_diagram(): A = Object("A") B = Object("B") C = Object("C") f = NamedMorphism(A, B, "f") g = NamedMorphism(B, C, "g") id_A = IdentityMorphism(A) id_B = IdentityMorphism(B) empty = EmptySet() # Test the addition of identities. d1 = Diagram([f]) assert d1.objects == FiniteSet(A, B) assert d1.hom(A, B) == (FiniteSet(f), empty) assert d1.hom(A, A) == (FiniteSet(id_A), empty) assert d1.hom(B, B) == (FiniteSet(id_B), empty) assert d1 == Diagram([id_A, f]) assert d1 == Diagram([f, f]) # Test the addition of composites. d2 = Diagram([f, g]) homAC = d2.hom(A, C)[0] assert d2.objects == FiniteSet(A, B, C) assert g * f in d2.premises.keys() assert homAC == FiniteSet(g * f) # Test equality, inequality and hash. d11 = Diagram([f]) assert d1 == d11 assert d1 != d2 assert hash(d1) == hash(d11) d11 = Diagram({f: "unique"}) assert d1 != d11 # Make sure that (re-)adding composites (with new properties) # works as expected. d = Diagram([f, g], {g * f: "unique"}) assert d.conclusions == Dict({g * f: FiniteSet("unique")}) # Check the hom-sets when there are premises and conclusions. assert d.hom(A, C) == (FiniteSet(g * f), FiniteSet(g * f)) d = Diagram([f, g], [g * f]) assert d.hom(A, C) == (FiniteSet(g * f), FiniteSet(g * f)) # Check how the properties of composite morphisms are computed. d = Diagram({f: ["unique", "isomorphism"], g: "unique"}) assert d.premises[g * f] == FiniteSet("unique") # Check that conclusion morphisms with new objects are not allowed. d = Diagram([f], [g]) assert d.conclusions == Dict({}) # Test an empty diagram. d = Diagram() assert d.premises == Dict({}) assert d.conclusions == Dict({}) assert d.objects == empty # Check a SymPy Dict object. d = Diagram(Dict({f: FiniteSet("unique", "isomorphism"), g: "unique"})) assert d.premises[g * f] == FiniteSet("unique") # Check the addition of components of composite morphisms. d = Diagram([g * f]) assert f in d.premises assert g in d.premises # Check subdiagrams. d = Diagram([f, g], {g * f: "unique"}) d1 = Diagram([f]) assert d.is_subdiagram(d1) assert not d1.is_subdiagram(d) d = Diagram([NamedMorphism(B, A, "f'")]) assert not d.is_subdiagram(d1) assert not d1.is_subdiagram(d) d1 = Diagram([f, g], {g * f: ["unique", "something"]}) assert not d.is_subdiagram(d1) assert not d1.is_subdiagram(d) d = Diagram({f: "blooh"}) d1 = Diagram({f: "bleeh"}) assert not d.is_subdiagram(d1) assert not d1.is_subdiagram(d) d = Diagram([f, g], {f: "unique", g * f: "veryunique"}) d1 = d.subdiagram_from_objects(FiniteSet(A, B)) assert d1 == Diagram([f], {f: "unique"}) raises(ValueError, lambda: d.subdiagram_from_objects(FiniteSet(A, Object("D")))) raises(ValueError, lambda: Diagram({IdentityMorphism(A): "unique"})) def test_category(): A = Object("A") B = Object("B") C = Object("C") f = NamedMorphism(A, B, "f") g = NamedMorphism(B, C, "g") d1 = Diagram([f, g]) d2 = Diagram([f]) objects = d1.objects | d2.objects K = Category("K", objects, commutative_diagrams=[d1, d2]) assert K.name == "K" assert K.objects == Class(objects) assert K.commutative_diagrams == FiniteSet(d1, d2) raises(ValueError, lambda: Category("")) sympy-0.7.4.1/sympy/categories/tests/test_drawing.py0000644000175000017500000006571512253362407022776 0ustar georgeskgeorgeskfrom sympy.categories.diagram_drawing import _GrowableGrid, ArrowStringDescription from sympy.categories import (DiagramGrid, Object, NamedMorphism, Diagram, XypicDiagramDrawer, xypic_draw_diagram) from sympy import FiniteSet def test_GrowableGrid(): grid = _GrowableGrid(1, 2) # Check dimensions. assert grid.width == 1 assert grid.height == 2 # Check initialisation of elements. assert grid[0, 0] is None assert grid[1, 0] is None # Check assignment to elements. grid[0, 0] = 1 grid[1, 0] = "two" assert grid[0, 0] == 1 assert grid[1, 0] == "two" # Check appending a row. grid.append_row() assert grid.width == 1 assert grid.height == 3 assert grid[0, 0] == 1 assert grid[1, 0] == "two" assert grid[2, 0] is None # Check appending a column. grid.append_column() assert grid.width == 2 assert grid.height == 3 assert grid[0, 0] == 1 assert grid[1, 0] == "two" assert grid[2, 0] is None assert grid[0, 1] is None assert grid[1, 1] is None assert grid[2, 1] is None grid = _GrowableGrid(1, 2) grid[0, 0] = 1 grid[1, 0] = "two" # Check prepending a row. grid.prepend_row() assert grid.width == 1 assert grid.height == 3 assert grid[0, 0] is None assert grid[1, 0] == 1 assert grid[2, 0] == "two" # Check prepending a column. grid.prepend_column() assert grid.width == 2 assert grid.height == 3 assert grid[0, 0] is None assert grid[1, 0] is None assert grid[2, 0] is None assert grid[0, 1] is None assert grid[1, 1] == 1 assert grid[2, 1] == "two" def test_DiagramGrid(): # Set up some objects and morphisms. A = Object("A") B = Object("B") C = Object("C") D = Object("D") E = Object("E") f = NamedMorphism(A, B, "f") g = NamedMorphism(B, C, "g") h = NamedMorphism(D, A, "h") k = NamedMorphism(D, B, "k") # A one-morphism diagram. d = Diagram([f]) grid = DiagramGrid(d) assert grid.width == 2 assert grid.height == 1 assert grid[0, 0] == A assert grid[0, 1] == B assert grid.morphisms == {f: FiniteSet()} # A triangle. d = Diagram([f, g], {g * f: "unique"}) grid = DiagramGrid(d) assert grid.width == 2 assert grid.height == 2 assert grid[0, 0] == A assert grid[0, 1] == B assert grid[1, 0] == C assert grid[1, 1] is None assert grid.morphisms == {f: FiniteSet(), g: FiniteSet(), g * f: FiniteSet("unique")} # A triangle with a "loop" morphism. l_A = NamedMorphism(A, A, "l_A") d = Diagram([f, g, l_A]) grid = DiagramGrid(d) assert grid.width == 2 assert grid.height == 2 assert grid[0, 0] == A assert grid[0, 1] == B assert grid[1, 0] is None assert grid[1, 1] == C assert grid.morphisms == {f: FiniteSet(), g: FiniteSet(), l_A: FiniteSet()} # A simple diagram. d = Diagram([f, g, h, k]) grid = DiagramGrid(d) assert grid.width == 3 assert grid.height == 2 assert grid[0, 0] == A assert grid[0, 1] == B assert grid[0, 2] == D assert grid[1, 0] is None assert grid[1, 1] == C assert grid[1, 2] is None assert grid.morphisms == {f: FiniteSet(), g: FiniteSet(), h: FiniteSet(), k: FiniteSet()} assert str(grid) == '[[Object("A"), Object("B"), Object("D")], ' \ '[None, Object("C"), None]]' # A chain of morphisms. f = NamedMorphism(A, B, "f") g = NamedMorphism(B, C, "g") h = NamedMorphism(C, D, "h") k = NamedMorphism(D, E, "k") d = Diagram([f, g, h, k]) grid = DiagramGrid(d) assert grid.width == 3 assert grid.height == 3 assert grid[0, 0] == A assert grid[0, 1] == B assert grid[0, 2] is None assert grid[1, 0] is None assert grid[1, 1] == C assert grid[1, 2] == D assert grid[2, 0] is None assert grid[2, 1] is None assert grid[2, 2] == E assert grid.morphisms == {f: FiniteSet(), g: FiniteSet(), h: FiniteSet(), k: FiniteSet()} # A square. f = NamedMorphism(A, B, "f") g = NamedMorphism(B, D, "g") h = NamedMorphism(A, C, "h") k = NamedMorphism(C, D, "k") d = Diagram([f, g, h, k]) grid = DiagramGrid(d) assert grid.width == 2 assert grid.height == 2 assert grid[0, 0] == A assert grid[0, 1] == B assert grid[1, 0] == C assert grid[1, 1] == D assert grid.morphisms == {f: FiniteSet(), g: FiniteSet(), h: FiniteSet(), k: FiniteSet()} # A strange diagram which resulted from a typo when creating a # test for five lemma, but which allowed to stop one extra problem # in the algorithm. A = Object("A") B = Object("B") C = Object("C") D = Object("D") E = Object("E") A_ = Object("A'") B_ = Object("B'") C_ = Object("C'") D_ = Object("D'") E_ = Object("E'") f = NamedMorphism(A, B, "f") g = NamedMorphism(B, C, "g") h = NamedMorphism(C, D, "h") i = NamedMorphism(D, E, "i") # These 4 morphisms should be between primed objects. j = NamedMorphism(A, B, "j") k = NamedMorphism(B, C, "k") l = NamedMorphism(C, D, "l") m = NamedMorphism(D, E, "m") o = NamedMorphism(A, A_, "o") p = NamedMorphism(B, B_, "p") q = NamedMorphism(C, C_, "q") r = NamedMorphism(D, D_, "r") s = NamedMorphism(E, E_, "s") d = Diagram([f, g, h, i, j, k, l, m, o, p, q, r, s]) grid = DiagramGrid(d) assert grid.width == 3 assert grid.height == 4 assert grid[0, 0] is None assert grid[0, 1] == A assert grid[0, 2] == A_ assert grid[1, 0] == C assert grid[1, 1] == B assert grid[1, 2] == B_ assert grid[2, 0] == C_ assert grid[2, 1] == D assert grid[2, 2] == D_ assert grid[3, 0] is None assert grid[3, 1] == E assert grid[3, 2] == E_ morphisms = {} for m in [f, g, h, i, j, k, l, m, o, p, q, r, s]: morphisms[m] = FiniteSet() assert grid.morphisms == morphisms # A cube. A1 = Object("A1") A2 = Object("A2") A3 = Object("A3") A4 = Object("A4") A5 = Object("A5") A6 = Object("A6") A7 = Object("A7") A8 = Object("A8") # The top face of the cube. f1 = NamedMorphism(A1, A2, "f1") f2 = NamedMorphism(A1, A3, "f2") f3 = NamedMorphism(A2, A4, "f3") f4 = NamedMorphism(A3, A4, "f3") # The bottom face of the cube. f5 = NamedMorphism(A5, A6, "f5") f6 = NamedMorphism(A5, A7, "f6") f7 = NamedMorphism(A6, A8, "f7") f8 = NamedMorphism(A7, A8, "f8") # The remaining morphisms. f9 = NamedMorphism(A1, A5, "f9") f10 = NamedMorphism(A2, A6, "f10") f11 = NamedMorphism(A3, A7, "f11") f12 = NamedMorphism(A4, A8, "f11") d = Diagram([f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12]) grid = DiagramGrid(d) assert grid.width == 4 assert grid.height == 3 assert grid[0, 0] is None assert grid[0, 1] == A5 assert grid[0, 2] == A6 assert grid[0, 3] is None assert grid[1, 0] is None assert grid[1, 1] == A1 assert grid[1, 2] == A2 assert grid[1, 3] is None assert grid[2, 0] == A7 assert grid[2, 1] == A3 assert grid[2, 2] == A4 assert grid[2, 3] == A8 morphisms = {} for m in [f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12]: morphisms[m] = FiniteSet() assert grid.morphisms == morphisms # A line diagram. A = Object("A") B = Object("B") C = Object("C") D = Object("D") E = Object("E") f = NamedMorphism(A, B, "f") g = NamedMorphism(B, C, "g") h = NamedMorphism(C, D, "h") i = NamedMorphism(D, E, "i") d = Diagram([f, g, h, i]) grid = DiagramGrid(d, layout="sequential") assert grid.width == 5 assert grid.height == 1 assert grid[0, 0] == A assert grid[0, 1] == B assert grid[0, 2] == C assert grid[0, 3] == D assert grid[0, 4] == E assert grid.morphisms == {f: FiniteSet(), g: FiniteSet(), h: FiniteSet(), i: FiniteSet()} # Test the transposed version. grid = DiagramGrid(d, layout="sequential", transpose=True) assert grid.width == 1 assert grid.height == 5 assert grid[0, 0] == A assert grid[1, 0] == B assert grid[2, 0] == C assert grid[3, 0] == D assert grid[4, 0] == E assert grid.morphisms == {f: FiniteSet(), g: FiniteSet(), h: FiniteSet(), i: FiniteSet()} # A pullback. m1 = NamedMorphism(A, B, "m1") m2 = NamedMorphism(A, C, "m2") s1 = NamedMorphism(B, D, "s1") s2 = NamedMorphism(C, D, "s2") f1 = NamedMorphism(E, B, "f1") f2 = NamedMorphism(E, C, "f2") g = NamedMorphism(E, A, "g") d = Diagram([m1, m2, s1, s2, f1, f2], {g: "unique"}) grid = DiagramGrid(d) assert grid.width == 3 assert grid.height == 2 assert grid[0, 0] == A assert grid[0, 1] == B assert grid[0, 2] == E assert grid[1, 0] == C assert grid[1, 1] == D assert grid[1, 2] is None morphisms = {g: FiniteSet("unique")} for m in [m1, m2, s1, s2, f1, f2]: morphisms[m] = FiniteSet() assert grid.morphisms == morphisms # Test the pullback with sequential layout, just for stress # testing. grid = DiagramGrid(d, layout="sequential") assert grid.width == 5 assert grid.height == 1 assert grid[0, 0] == D assert grid[0, 1] == B assert grid[0, 2] == A assert grid[0, 3] == C assert grid[0, 4] == E assert grid.morphisms == morphisms # Test a pullback with object grouping. grid = DiagramGrid(d, groups=FiniteSet(E, FiniteSet(A, B, C, D))) assert grid.width == 3 assert grid.height == 2 assert grid[0, 0] == E assert grid[0, 1] == A assert grid[0, 2] == B assert grid[1, 0] is None assert grid[1, 1] == C assert grid[1, 2] == D assert grid.morphisms == morphisms # Five lemma, actually. A = Object("A") B = Object("B") C = Object("C") D = Object("D") E = Object("E") A_ = Object("A'") B_ = Object("B'") C_ = Object("C'") D_ = Object("D'") E_ = Object("E'") f = NamedMorphism(A, B, "f") g = NamedMorphism(B, C, "g") h = NamedMorphism(C, D, "h") i = NamedMorphism(D, E, "i") j = NamedMorphism(A_, B_, "j") k = NamedMorphism(B_, C_, "k") l = NamedMorphism(C_, D_, "l") m = NamedMorphism(D_, E_, "m") o = NamedMorphism(A, A_, "o") p = NamedMorphism(B, B_, "p") q = NamedMorphism(C, C_, "q") r = NamedMorphism(D, D_, "r") s = NamedMorphism(E, E_, "s") d = Diagram([f, g, h, i, j, k, l, m, o, p, q, r, s]) grid = DiagramGrid(d) assert grid.width == 5 assert grid.height == 3 assert grid[0, 0] is None assert grid[0, 1] == A assert grid[0, 2] == A_ assert grid[0, 3] is None assert grid[0, 4] is None assert grid[1, 0] == C assert grid[1, 1] == B assert grid[1, 2] == B_ assert grid[1, 3] == C_ assert grid[1, 4] is None assert grid[2, 0] == D assert grid[2, 1] == E assert grid[2, 2] is None assert grid[2, 3] == D_ assert grid[2, 4] == E_ morphisms = {} for m in [f, g, h, i, j, k, l, m, o, p, q, r, s]: morphisms[m] = FiniteSet() assert grid.morphisms == morphisms # Test the five lemma with object grouping. grid = DiagramGrid(d, FiniteSet( FiniteSet(A, B, C, D, E), FiniteSet(A_, B_, C_, D_, E_))) assert grid.width == 6 assert grid.height == 3 assert grid[0, 0] == A assert grid[0, 1] == B assert grid[0, 2] is None assert grid[0, 3] == A_ assert grid[0, 4] == B_ assert grid[0, 5] is None assert grid[1, 0] is None assert grid[1, 1] == C assert grid[1, 2] == D assert grid[1, 3] is None assert grid[1, 4] == C_ assert grid[1, 5] == D_ assert grid[2, 0] is None assert grid[2, 1] is None assert grid[2, 2] == E assert grid[2, 3] is None assert grid[2, 4] is None assert grid[2, 5] == E_ assert grid.morphisms == morphisms # Test the five lemma with object grouping, but mixing containers # to represent groups. grid = DiagramGrid(d, [(A, B, C, D, E), set([A_, B_, C_, D_, E_])]) assert grid.width == 6 assert grid.height == 3 assert grid[0, 0] == A assert grid[0, 1] == B assert grid[0, 2] is None assert grid[0, 3] == A_ assert grid[0, 4] == B_ assert grid[0, 5] is None assert grid[1, 0] is None assert grid[1, 1] == C assert grid[1, 2] == D assert grid[1, 3] is None assert grid[1, 4] == C_ assert grid[1, 5] == D_ assert grid[2, 0] is None assert grid[2, 1] is None assert grid[2, 2] == E assert grid[2, 3] is None assert grid[2, 4] is None assert grid[2, 5] == E_ assert grid.morphisms == morphisms # Test the five lemma with object grouping and hints. grid = DiagramGrid(d, { FiniteSet(A, B, C, D, E): {"layout": "sequential", "transpose": True}, FiniteSet(A_, B_, C_, D_, E_): {"layout": "sequential", "transpose": True}}, transpose=True) assert grid.width == 5 assert grid.height == 2 assert grid[0, 0] == A assert grid[0, 1] == B assert grid[0, 2] == C assert grid[0, 3] == D assert grid[0, 4] == E assert grid[1, 0] == A_ assert grid[1, 1] == B_ assert grid[1, 2] == C_ assert grid[1, 3] == D_ assert grid[1, 4] == E_ assert grid.morphisms == morphisms # A two-triangle disconnected diagram. f = NamedMorphism(A, B, "f") g = NamedMorphism(B, C, "g") f_ = NamedMorphism(A_, B_, "f") g_ = NamedMorphism(B_, C_, "g") d = Diagram([f, g, f_, g_], {g * f: "unique", g_ * f_: "unique"}) grid = DiagramGrid(d) assert grid.width == 4 assert grid.height == 2 assert grid[0, 0] == A assert grid[0, 1] == B assert grid[0, 2] == A_ assert grid[0, 3] == B_ assert grid[1, 0] == C assert grid[1, 1] is None assert grid[1, 2] == C_ assert grid[1, 3] is None assert grid.morphisms == {f: FiniteSet(), g: FiniteSet(), f_: FiniteSet(), g_: FiniteSet(), g * f: FiniteSet("unique"), g_ * f_: FiniteSet("unique")} # A two-morphism disconnected diagram. f = NamedMorphism(A, B, "f") g = NamedMorphism(C, D, "g") d = Diagram([f, g]) grid = DiagramGrid(d) assert grid.width == 4 assert grid.height == 1 assert grid[0, 0] == A assert grid[0, 1] == B assert grid[0, 2] == C assert grid[0, 3] == D assert grid.morphisms == {f: FiniteSet(), g: FiniteSet()} # Test a one-object diagram. f = NamedMorphism(A, A, "f") d = Diagram([f]) grid = DiagramGrid(d) assert grid.width == 1 assert grid.height == 1 assert grid[0, 0] == A # Test a two-object disconnected diagram. g = NamedMorphism(B, B, "g") d = Diagram([f, g]) grid = DiagramGrid(d) assert grid.width == 2 assert grid.height == 1 assert grid[0, 0] == A assert grid[0, 1] == B # Test a diagram in which even growing a pseudopod does not # eventually help. F = Object("F") f1 = NamedMorphism(A, B, "f1") f2 = NamedMorphism(A, C, "f2") f3 = NamedMorphism(A, D, "f3") f4 = NamedMorphism(A, E, "f4") f5 = NamedMorphism(A, A_, "f5") f6 = NamedMorphism(A, B_, "f6") f7 = NamedMorphism(A, C_, "f7") f8 = NamedMorphism(A, D_, "f8") f9 = NamedMorphism(A, E_, "f9") f10 = NamedMorphism(A, F, "f10") d = Diagram([f1, f2, f3, f4, f5, f6, f7, f8, f9, f10]) grid = DiagramGrid(d) assert grid.width == 5 assert grid.height == 3 assert grid[0, 0] == E assert grid[0, 1] == C assert grid[0, 2] == C_ assert grid[0, 3] == E_ assert grid[0, 4] == F assert grid[1, 0] == D assert grid[1, 1] == A assert grid[1, 2] == A_ assert grid[1, 3] is None assert grid[1, 4] is None assert grid[2, 0] == D_ assert grid[2, 1] == B assert grid[2, 2] == B_ assert grid[2, 3] is None assert grid[2, 4] is None morphisms = {} for f in [f1, f2, f3, f4, f5, f6, f7, f8, f9, f10]: morphisms[f] = FiniteSet() assert grid.morphisms == morphisms def test_ArrowStringDescription(): astr = ArrowStringDescription("cm", "", None, "", "", "d", "r", "_", "f") assert str(astr) == "\\ar[dr]_{f}" astr = ArrowStringDescription("cm", "", 12, "", "", "d", "r", "_", "f") assert str(astr) == "\\ar[dr]_{f}" astr = ArrowStringDescription("cm", "^", 12, "", "", "d", "r", "_", "f") assert str(astr) == "\\ar@/^12cm/[dr]_{f}" astr = ArrowStringDescription("cm", "", 12, "r", "", "d", "r", "_", "f") assert str(astr) == "\\ar[dr]_{f}" astr = ArrowStringDescription("cm", "", 12, "r", "u", "d", "r", "_", "f") assert str(astr) == "\\ar@(r,u)[dr]_{f}" astr = ArrowStringDescription("cm", "", 12, "r", "u", "d", "r", "_", "f") assert str(astr) == "\\ar@(r,u)[dr]_{f}" astr = ArrowStringDescription("cm", "", 12, "r", "u", "d", "r", "_", "f") astr.arrow_style = "{-->}" assert str(astr) == "\\ar@(r,u)@{-->}[dr]_{f}" astr = ArrowStringDescription("cm", "_", 12, "", "", "d", "r", "_", "f") astr.arrow_style = "{-->}" assert str(astr) == "\\ar@/_12cm/@{-->}[dr]_{f}" def test_XypicDiagramDrawer_line(): # A linear diagram. A = Object("A") B = Object("B") C = Object("C") D = Object("D") E = Object("E") f = NamedMorphism(A, B, "f") g = NamedMorphism(B, C, "g") h = NamedMorphism(C, D, "h") i = NamedMorphism(D, E, "i") d = Diagram([f, g, h, i]) grid = DiagramGrid(d, layout="sequential") drawer = XypicDiagramDrawer() assert drawer.draw(d, grid) == "\\xymatrix{\n" \ "A \\ar[r]^{f} & B \\ar[r]^{g} & C \\ar[r]^{h} & D \\ar[r]^{i} & E \n" \ "}\n" # The same diagram, transposed. grid = DiagramGrid(d, layout="sequential", transpose=True) drawer = XypicDiagramDrawer() assert drawer.draw(d, grid) == "\\xymatrix{\n" \ "A \\ar[d]^{f} \\\\\n" \ "B \\ar[d]^{g} \\\\\n" \ "C \\ar[d]^{h} \\\\\n" \ "D \\ar[d]^{i} \\\\\n" \ "E \n" \ "}\n" def test_XypicDiagramDrawer_triangle(): # A triangle 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"}) grid = DiagramGrid(d) drawer = XypicDiagramDrawer() assert drawer.draw(d, grid) == "\\xymatrix{\n" \ "A \\ar[d]_{g\\circ f} \\ar[r]^{f} & B \\ar[ld]^{g} \\\\\n" \ "C & \n" \ "}\n" # The same diagram, transposed. grid = DiagramGrid(d, transpose=True) drawer = XypicDiagramDrawer() assert drawer.draw(d, grid) == "\\xymatrix{\n" \ "A \\ar[r]^{g\\circ f} \\ar[d]_{f} & C \\\\\n" \ "B \\ar[ru]_{g} & \n" \ "}\n" # The same diagram, with a masked morphism. assert drawer.draw(d, grid, masked=[g]) == "\\xymatrix{\n" \ "A \\ar[r]^{g\\circ f} \\ar[d]_{f} & C \\\\\n" \ "B & \n" \ "}\n" # The same diagram with a formatter for "unique". def formatter(astr): astr.label = "\\exists !" + astr.label astr.arrow_style = "{-->}" drawer.arrow_formatters["unique"] = formatter assert drawer.draw(d, grid) == "\\xymatrix{\n" \ "A \\ar@{-->}[r]^{\\exists !g\\circ f} \\ar[d]_{f} & C \\\\\n" \ "B \\ar[ru]_{g} & \n" \ "}\n" # The same diagram with a default formatter. def default_formatter(astr): astr.label_displacement = "(0.45)" drawer.default_arrow_formatter = default_formatter assert drawer.draw(d, grid) == "\\xymatrix{\n" \ "A \\ar@{-->}[r]^(0.45){\\exists !g\\circ f} \\ar[d]_(0.45){f} & C \\\\\n" \ "B \\ar[ru]_(0.45){g} & \n" \ "}\n" # A triangle diagram with a lot of morphisms between the same # objects. f1 = NamedMorphism(B, A, "f1") f2 = NamedMorphism(A, B, "f2") g1 = NamedMorphism(C, B, "g1") g2 = NamedMorphism(B, C, "g2") d = Diagram([f, f1, f2, g, g1, g2], {f1 * g1: "unique", g2 * f2: "unique"}) grid = DiagramGrid(d, transpose=True) drawer = XypicDiagramDrawer() assert drawer.draw(d, grid, masked=[f1*g1*g2*f2, g2*f2*f1*g1]) == \ "\\xymatrix{\n" \ "A \\ar[r]^{g_{2}\\circ f_{2}} \\ar[d]_{f} \\ar@/^3mm/[d]^{f_{2}} " \ "& C \\ar@/^3mm/[l]^{f_{1}\\circ g_{1}} \\ar@/^3mm/[ld]^{g_{1}} \\\\\n" \ "B \\ar@/^3mm/[u]^{f_{1}} \\ar[ru]_{g} \\ar@/^3mm/[ru]^{g_{2}} & \n" \ "}\n" def test_XypicDiagramDrawer_cube(): # A cube diagram. A1 = Object("A1") A2 = Object("A2") A3 = Object("A3") A4 = Object("A4") A5 = Object("A5") A6 = Object("A6") A7 = Object("A7") A8 = Object("A8") # The top face of the cube. f1 = NamedMorphism(A1, A2, "f1") f2 = NamedMorphism(A1, A3, "f2") f3 = NamedMorphism(A2, A4, "f3") f4 = NamedMorphism(A3, A4, "f3") # The bottom face of the cube. f5 = NamedMorphism(A5, A6, "f5") f6 = NamedMorphism(A5, A7, "f6") f7 = NamedMorphism(A6, A8, "f7") f8 = NamedMorphism(A7, A8, "f8") # The remaining morphisms. f9 = NamedMorphism(A1, A5, "f9") f10 = NamedMorphism(A2, A6, "f10") f11 = NamedMorphism(A3, A7, "f11") f12 = NamedMorphism(A4, A8, "f11") d = Diagram([f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12]) grid = DiagramGrid(d) drawer = XypicDiagramDrawer() assert drawer.draw(d, grid) == "\\xymatrix{\n" \ "& A_{5} \\ar[r]^{f_{5}} \\ar[ldd]_{f_{6}} & A_{6} \\ar[rdd]^{f_{7}} " \ "& \\\\\n" \ "& A_{1} \\ar[r]^{f_{1}} \\ar[d]^{f_{2}} \\ar[u]^{f_{9}} & A_{2} " \ "\\ar[d]^{f_{3}} \\ar[u]_{f_{10}} & \\\\\n" \ "A_{7} \\ar@/_3mm/[rrr]_{f_{8}} & A_{3} \\ar[r]^{f_{3}} \\ar[l]_{f_{11}} " \ "& A_{4} \\ar[r]^{f_{11}} & A_{8} \n" \ "}\n" # The same diagram, transposed. grid = DiagramGrid(d, transpose=True) drawer = XypicDiagramDrawer() assert drawer.draw(d, grid) == "\\xymatrix{\n" \ "& & A_{7} \\ar@/^3mm/[ddd]^{f_{8}} \\\\\n" \ "A_{5} \\ar[d]_{f_{5}} \\ar[rru]^{f_{6}} & A_{1} \\ar[d]^{f_{1}} " \ "\\ar[r]^{f_{2}} \\ar[l]^{f_{9}} & A_{3} \\ar[d]_{f_{3}} " \ "\\ar[u]^{f_{11}} \\\\\n" \ "A_{6} \\ar[rrd]_{f_{7}} & A_{2} \\ar[r]^{f_{3}} \\ar[l]^{f_{10}} " \ "& A_{4} \\ar[d]_{f_{11}} \\\\\n" \ "& & A_{8} \n" \ "}\n" def test_XypicDiagramDrawer_curved_and_loops(): # A simple diagram, with a curved arrow. A = Object("A") B = Object("B") C = Object("C") D = Object("D") f = NamedMorphism(A, B, "f") g = NamedMorphism(B, C, "g") h = NamedMorphism(D, A, "h") k = NamedMorphism(D, B, "k") d = Diagram([f, g, h, k]) grid = DiagramGrid(d) drawer = XypicDiagramDrawer() assert drawer.draw(d, grid) == "\\xymatrix{\n" \ "A \\ar[r]_{f} & B \\ar[d]^{g} & D \\ar[l]^{k} \\ar@/_3mm/[ll]_{h} \\\\\n" \ "& C & \n" \ "}\n" # The same diagram, transposed. grid = DiagramGrid(d, transpose=True) drawer = XypicDiagramDrawer() assert drawer.draw(d, grid) == "\\xymatrix{\n" \ "A \\ar[d]^{f} & \\\\\n" \ "B \\ar[r]^{g} & C \\\\\n" \ "D \\ar[u]_{k} \\ar@/^3mm/[uu]^{h} & \n" \ "}\n" # The same diagram, larger and rotated. assert drawer.draw(d, grid, diagram_format="@+1cm@dr") == \ "\\xymatrix@+1cm@dr{\n" \ "A \\ar[d]^{f} & \\\\\n" \ "B \\ar[r]^{g} & C \\\\\n" \ "D \\ar[u]_{k} \\ar@/^3mm/[uu]^{h} & \n" \ "}\n" # A simple diagram with three curved arrows. h1 = NamedMorphism(D, A, "h1") h2 = NamedMorphism(A, D, "h2") k = NamedMorphism(D, B, "k") d = Diagram([f, g, h, k, h1, h2]) grid = DiagramGrid(d) drawer = XypicDiagramDrawer() assert drawer.draw(d, grid) == "\\xymatrix{\n" \ "A \\ar[r]_{f} \\ar@/^3mm/[rr]^{h_{2}} & B \\ar[d]^{g} & D \\ar[l]^{k} " \ "\\ar@/_7mm/[ll]_{h} \\ar@/_11mm/[ll]_{h_{1}} \\\\\n" \ "& C & \n" \ "}\n" # The same diagram, transposed. grid = DiagramGrid(d, transpose=True) drawer = XypicDiagramDrawer() assert drawer.draw(d, grid) == "\\xymatrix{\n" \ "A \\ar[d]^{f} \\ar@/_3mm/[dd]_{h_{2}} & \\\\\n" \ "B \\ar[r]^{g} & C \\\\\n" \ "D \\ar[u]_{k} \\ar@/^7mm/[uu]^{h} \\ar@/^11mm/[uu]^{h_{1}} & \n" \ "}\n" # The same diagram, with "loop" morphisms. l_A = NamedMorphism(A, A, "l_A") l_D = NamedMorphism(D, D, "l_D") l_C = NamedMorphism(C, C, "l_C") d = Diagram([f, g, h, k, h1, h2, l_A, l_D, l_C]) grid = DiagramGrid(d) drawer = XypicDiagramDrawer() assert drawer.draw(d, grid) == "\\xymatrix{\n" \ "A \\ar[r]_{f} \\ar@/^3mm/[rr]^{h_{2}} \\ar@(u,l)[]^{l_{A}} " \ "& B \\ar[d]^{g} & D \\ar[l]^{k} \\ar@/_7mm/[ll]_{h} " \ "\\ar@/_11mm/[ll]_{h_{1}} \\ar@(r,u)[]^{l_{D}} \\\\\n" \ "& C \\ar@(l,d)[]^{l_{C}} & \n" \ "}\n" # The same diagram with "loop" morphisms, transposed. grid = DiagramGrid(d, transpose=True) drawer = XypicDiagramDrawer() assert drawer.draw(d, grid) == "\\xymatrix{\n" \ "A \\ar[d]^{f} \\ar@/_3mm/[dd]_{h_{2}} \\ar@(r,u)[]^{l_{A}} & \\\\\n" \ "B \\ar[r]^{g} & C \\ar@(r,u)[]^{l_{C}} \\\\\n" \ "D \\ar[u]_{k} \\ar@/^7mm/[uu]^{h} \\ar@/^11mm/[uu]^{h_{1}} " \ "\\ar@(l,d)[]^{l_{D}} & \n" \ "}\n" # The same diagram with two "loop" morphisms per object. l_A_ = NamedMorphism(A, A, "n_A") l_D_ = NamedMorphism(D, D, "n_D") l_C_ = NamedMorphism(C, C, "n_C") d = Diagram([f, g, h, k, h1, h2, l_A, l_D, l_C, l_A_, l_D_, l_C_]) grid = DiagramGrid(d) drawer = XypicDiagramDrawer() assert drawer.draw(d, grid) == "\\xymatrix{\n" \ "A \\ar[r]_{f} \\ar@/^3mm/[rr]^{h_{2}} \\ar@(u,l)[]^{l_{A}} " \ "\\ar@/^3mm/@(l,d)[]^{n_{A}} & B \\ar[d]^{g} & D \\ar[l]^{k} " \ "\\ar@/_7mm/[ll]_{h} \\ar@/_11mm/[ll]_{h_{1}} \\ar@(r,u)[]^{l_{D}} " \ "\\ar@/^3mm/@(d,r)[]^{n_{D}} \\\\\n" \ "& C \\ar@(l,d)[]^{l_{C}} \\ar@/^3mm/@(d,r)[]^{n_{C}} & \n" \ "}\n" # The same diagram with two "loop" morphisms per object, transposed. grid = DiagramGrid(d, transpose=True) drawer = XypicDiagramDrawer() assert drawer.draw(d, grid) == "\\xymatrix{\n" \ "A \\ar[d]^{f} \\ar@/_3mm/[dd]_{h_{2}} \\ar@(r,u)[]^{l_{A}} " \ "\\ar@/^3mm/@(u,l)[]^{n_{A}} & \\\\\n" \ "B \\ar[r]^{g} & C \\ar@(r,u)[]^{l_{C}} \\ar@/^3mm/@(d,r)[]^{n_{C}} \\\\\n" \ "D \\ar[u]_{k} \\ar@/^7mm/[uu]^{h} \\ar@/^11mm/[uu]^{h_{1}} " \ "\\ar@(l,d)[]^{l_{D}} \\ar@/^3mm/@(d,r)[]^{n_{D}} & \n" \ "}\n" def test_xypic_draw_diagram(): # A linear diagram. A = Object("A") B = Object("B") C = Object("C") D = Object("D") E = Object("E") f = NamedMorphism(A, B, "f") g = NamedMorphism(B, C, "g") h = NamedMorphism(C, D, "h") i = NamedMorphism(D, E, "i") d = Diagram([f, g, h, i]) grid = DiagramGrid(d, layout="sequential") drawer = XypicDiagramDrawer() assert drawer.draw(d, grid) == xypic_draw_diagram(d, layout="sequential") sympy-0.7.4.1/sympy/categories/tests/__init__.py0000644000175000017500000000000012253362407022013 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/categories/__init__.py0000644000175000017500000000140312253362407020661 0ustar georgeskgeorgesk""" 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) sympy-0.7.4.1/sympy/printing/0000755000175000017500000000000012253362407016257 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/printing/pretty/0000755000175000017500000000000012253362407017606 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/printing/pretty/stringpict.py0000644000175000017500000004270212253362407022353 0ustar georgeskgeorgesk"""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 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 __future__ import print_function, division from .pretty_symbology import hobj, vobj, xsym, xobj, pretty_use_unicode from sympy.core.compatibility import u, string_types, xrange 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] def height(self): """The height of the picture in characters.""" return len(self.picture) def width(self): """The width of the picture in characters.""" return len(self.picture[0]) @staticmethod 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, string_types): 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 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) def left(self, *args): """Put pictures (left to right) at left. Returns string, baseline arguments for stringPict. """ return stringPict.next(*(args + (self,))) @staticmethod 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, string_types): 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 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 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 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) 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)) 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) 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) 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 unicode.join(u('\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) 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 OPEN this is an open object: parenthesize if added, multiplied, or powered (example: Piecewise) """ ATOM, FUNC, DIV, POW, MUL, ADD, NEG, OPEN = range(8) def __init__(self, s, baseline=0, binding=0, unicode=None): """Initialize from stringPict and binding power.""" stringPict.__init__(self, s, baseline) self.binding = binding self.unicode = unicode or s # Note: code to handle subtraction is in _print_Add 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()) if num.binding==prettyForm.NEG: num = num.right(" ")[0] 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. """ if len(others) == 0: return self # We aren't actually multiplying... So nothing to do here. 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 xrange(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 # This test was failing to catch a prettyForm.__mul__(prettyForm("-1", 0, 6)) being negative 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 # sin + + (x) b.baseline = a.prettyFunc.baseline + 1 func = stringPict(*a.prettyFunc.right(b)) return prettyForm(*func.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 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)) sympy-0.7.4.1/sympy/printing/pretty/pretty_symbology.py0000644000175000017500000003627512253362407023630 0ustar georgeskgeorgesk"""Symbolic primitives + unicode/ASCII abstraction for pretty.py""" from __future__ import print_function, division import sys import warnings unicode_warnings = '' from sympy.core.compatibility import u, unicode # first, setup unicodedate environment try: import unicodedata def U(name): """unicode character by name or None if not found""" try: u = unicodedata.lookup(name) except KeyError: u = None global unicode_warnings unicode_warnings += 'No \'%s\' in unicodedata\n' % name return u except ImportError: unicode_warnings += 'No unicodedata available\n' U = lambda name: None from sympy.printing.conventions import split_super_sub from sympy.core.alphabets import greeks # prefix conventions when constructing tables # L - LATIN i # G - GREEK beta # D - DIGIT 0 # S - SYMBOL + __all__ = ['greek_unicode', 'sub', 'sup', 'xsym', 'vobj', 'hobj', 'pretty_symbol', 'annotated'] _use_unicode = False def pretty_use_unicode(flag=None): """Set whether pretty-printer should use unicode by default""" global _use_unicode global unicode_warnings if flag is None: return _use_unicode if flag and unicode_warnings: # print warnings (if any) on first unicode usage warnings.warn(unicode_warnings) unicode_warnings = '' use_unicode_prev = _use_unicode _use_unicode = flag return use_unicode_prev 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 symbols.extend(greek_unicode.values()) # and atoms symbols += 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) def xstr(*args): """call str or unicode depending on current mode""" if _use_unicode: return unicode(*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 = list(greeks) # make a copy # deal with Unicode's funny spelling of lambda greek_letters[greek_letters.index('lambda')] = 'lamda' # {} greek letter -> (g,G) greek_unicode = dict([(l, (g(l), G(l))) for l in greek_letters]) greek_unicode = dict((L, g(L)) for L in greek_letters) greek_unicode.update((L[0].upper() + L[1:], G(L)) for L in greek_letters) # aliases greek_unicode['lambda'] = greek_unicode['lamda'] greek_unicode['Lambda'] = greek_unicode['Lamda'] greek_unicode['varsigma'] = u('\u03c2') 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) # Variable modifiers # TODO: Is it worth trying to handle faces with, e.g., 'MATHEMATICAL BOLD CAPITAL A'? # TODO: Make brackets adjust to height of contents modifier_dict = { # Accents 'mathring': lambda s: s+u('\u030A'), 'ddddot': lambda s: s+u('\u0308\u0308'), 'dddot': lambda s: s+u('\u0308\u0307'), 'ddot': lambda s: s+u('\u0308'), 'dot': lambda s: s+u('\u0307'), 'check': lambda s: s+u('\u030C'), 'breve': lambda s: s+u('\u0306'), 'acute': lambda s: s+u('\u0301'), 'grave': lambda s: s+u('\u0300'), 'tilde': lambda s: s+u('\u0303'), 'hat': lambda s: s+u('\u0302'), 'bar': lambda s: s+u('\u0305'), 'vec': lambda s: s+u('\u20D7'), 'prime': lambda s: s+u(' \u030D'), 'prm': lambda s: s+u(' \u030D'), # # Faces -- these are here for some compatibility with latex printing # 'bold': lambda s: s, # 'bm': lambda s: s, # 'cal': lambda s: s, # 'scr': lambda s: s, # 'frak': lambda s: s, # Brackets 'norm': lambda s: u('\u2016')+s+u('\u2016'), 'avg': lambda s: u('\u27E8')+s+u('\u27E9'), 'abs': lambda s: u('\u007C')+s+u('\u007C'), 'mag': lambda s: u('\u007C')+s+u('\u007C'), } # 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 '\' & '/' ? '/': '/', '\\': '\\', } 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 def vobj(symb, height): """Construct vertical object of a given height see: xobj """ return '\n'.join( xobj(symb, height) ) 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')), } 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') } 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 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) def translate(s) : gG = greek_unicode.get(s) if gG is not None: return gG for key in sorted(modifier_dict.keys(), key=lambda k:len(k), reverse=True) : if s.lower().endswith(key) and len(s)>len(key): return modifier_dict[key](translate(s[:-len(key)])) return s name = translate(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 if subs: name += '_'+'_'.join([translate(s) for s in subs]) if sups: name += '__'+'__'.join([translate(s) for s in sups]) return name else: sups_result = ' '.join(pretty_sups) subs_result = ' '.join(pretty_subs) return ''.join([name, sups_result, subs_result]) 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, u('\u250c\u2500\n\u251c\u2500\n\u2575')), 'G': (3, 0, 3, 1, u('\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] sympy-0.7.4.1/sympy/printing/pretty/pretty.py0000644000175000017500000016712712253362407021525 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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.core.compatibility import u, xrange from sympy.printing.printer import Printer from sympy.printing.str import sstr from sympy.printing.conventions import requires_partial 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_unicode, 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 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) _print_RandomSymbol = _print_Symbol 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(u("\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(u(' %s ') % char)) pform = prettyForm(*pform.right(pform_arg)) return pform def _print_And(self, e): if self._use_unicode: return self.__print_Boolean(e, u("\u2227")) else: return self._print_Function(e, sort=True) def _print_Or(self, e): if self._use_unicode: return self.__print_Boolean(e, u("\u2228")) else: return self._print_Function(e, sort=True) def _print_Xor(self, e): if self._use_unicode: return self.__print_Boolean(e, u("\u22bb")) else: return self._print_Function(e, sort=True) def _print_Nand(self, e): if self._use_unicode: return self.__print_Boolean(e, u("\u22bc")) else: return self._print_Function(e, sort=True) def _print_Nor(self, e): if self._use_unicode: return self.__print_Boolean(e, u("\u22bd")) else: return self._print_Function(e, sort=True) def _print_Implies(self, e): if self._use_unicode: return self.__print_Boolean(e, u("\u2192"), sort=False) else: return self._print_Function(e) def _print_Equivalent(self, e): if self._use_unicode: return self.__print_Boolean(e, u("\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 _print_Determinant = _print_Abs 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): if requires_partial(deriv) and self._use_unicode: deriv_symbol = U('PARTIAL DIFFERENTIAL') else: deriv_symbol = r'd' syms = list(reversed(deriv.variables)) x = None for sym, num in group(syms, multiple=False): s = self._print(sym) ds = prettyForm(*s.left(deriv_symbol)) 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(deriv_symbol) 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)) pform.binding = prettyForm.MUL 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)) pform.binding = prettyForm.MUL 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 = u('\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 pretty_func.binding = prettyForm.MUL 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(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(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 prettyF.binding = prettyForm.MUL 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_MatrixElement(self, expr): from sympy.matrices import MatrixSymbol from sympy import Symbol if (isinstance(expr.parent, MatrixSymbol) and expr.i.is_number and expr.j.is_number): return self._print( Symbol(expr.parent.name + '_%d%d'%(expr.i, expr.j))) else: prettyFunc = self._print(expr.parent) prettyIndices = self._print_seq((expr.i, expr.j), delimiter=', ' ).parens(left='[', right=']')[0] pform = prettyForm(binding=prettyForm.FUNC, *stringPict.next(prettyFunc, prettyIndices)) # store pform parts so it can be reassembled e.g. when powered pform.prettyFunc = prettyFunc pform.prettyArgs = prettyIndices return pform def _print_MatrixSlice(self, m): # XXX works only for applied functions prettyFunc = self._print(m.parent) def ppslice(x): x = list(x) if x[2] == 1: del x[2] if x[1] == x[0] + 1: del x[1] if x[0] == 0: x[0] = '' return prettyForm(*self._print_seq(x, delimiter=':')) prettyArgs = self._print_seq((ppslice(m.rowslice), ppslice(m.colslice)), delimiter=', ').parens(left='[', right=']')[0] 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_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(u('\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 == 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 xrange(len_args)]) for j in range(2)] # FIXME: Refactor this code and matrix into some tabular environment. # drawing result D = None for i in xrange(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('{', '')) D.baseline = D.height()//2 D.binding = prettyForm.OPEN 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()) #postioning func_name mid = prettyArgs.height()//2 if mid > 2: prettyFunc.baseline = -mid + 1 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): vars, expr = e.args if self._use_unicode: arrow = u(" \u21a6 ") else: arrow = " -> " if len(vars) == 1: var_form = self._print(vars[0]) else: var_form = self._print(tuple(vars)) return prettyForm(*stringPict.next(var_form, arrow, self._print(expr)), binding=8) def _print_Order(self, expr): pform = self._print(expr.expr) if (expr.point and any(p != S.Zero for p in expr.point)) or \ len(expr.variables) > 1: pform = prettyForm(*pform.right("; ")) if len(expr.variables) > 1: pform = prettyForm(*pform.right(self._print(expr.variables))) elif len(expr.variables): pform = prettyForm(*pform.right(self._print(expr.variables[0]))) if self._use_unicode: pform = prettyForm(*pform.right(u(" \u2192 "))) else: pform = prettyForm(*pform.right(" -> ")) if len(expr.point) > 1: pform = prettyForm(*pform.right(self._print(expr.point))) else: pform = prettyForm(*pform.right(self._print(expr.point[0]))) 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_unicode['Gamma'])) 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_unicode['Gamma'])) 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_unicode['gamma'])) 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_elliptic_e(self, e): pforma0 = self._print(e.args[0]) if len(e.args) == 1: pform = pforma0 else: pforma1 = self._print(e.args[1]) pform = self._hprint_vseparator(pforma0, pforma1) pform = prettyForm(*pform.parens()) pform = prettyForm(*pform.left('E')) return pform def _print_elliptic_k(self, e): pform = self._print(e.args[0]) pform = prettyForm(*pform.parens()) pform = prettyForm(*pform.left('K')) return pform def _print_elliptic_f(self, e): pforma0 = self._print(e.args[0]) pforma1 = self._print(e.args[1]) pform = self._hprint_vseparator(pforma0, pforma1) pform = prettyForm(*pform.parens()) pform = prettyForm(*pform.left('F')) return pform def _print_elliptic_pi(self, e): name = greek_unicode['Pi'] if self._use_unicode else 'Pi' pforma0 = self._print(e.args[0]) pforma1 = self._print(e.args[1]) if len(e.args) == 2: pform = self._hprint_vseparator(pforma0, pforma1) else: pforma2 = self._print(e.args[2]) pforma = self._hprint_vseparator(pforma1, pforma2) pforma = prettyForm(*pforma.left('; ')) pform = prettyForm(*pforma.left(pforma0)) pform = prettyForm(*pform.parens()) pform = prettyForm(*pform.left(name)) 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. """ #TODO: Move this code to prettyForm if index == 0: if pform.height() > 1: pform_neg = '- ' else: pform_neg = '-' else: pform_neg = ' - ' if pform.binding > prettyForm.NEG: p = stringPict(*pform.parens()) else: p = pform p = stringPict.next(pform_neg, p) # Lower the binding to NEG, even if it was higher. Otherwise, it # will print as a + ( - (b)), instead of a - (b). return prettyForm(binding=prettyForm.NEG, *p) 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 xrange(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 xrange(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.simplify.simplify 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(C.Pow(b, -e, evaluate=False)) 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: return prettyForm(str(p), binding=prettyForm.NEG)/prettyForm(str(q)) # Old printing method: #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 = u('\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 = u("\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_ImageSet(self, ts): if self._use_unicode: inn = u("\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(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_PolyRing(self, ring): return prettyForm(sstr(ring)) def _print_FracField(self, field): return prettyForm(sstr(field)) def _print_PolyElement(self, poly): return prettyForm(sstr(poly)) def _print_FracElement(self, frac): return prettyForm(sstr(frac)) 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 = u('\u2124_%d') else: form = 'GF(%d)' return prettyForm(pretty_symbol(form % expr.mod)) def _print_IntegerRing(self, expr): if self._use_unicode: return prettyForm(u('\u2124')) else: return prettyForm('ZZ') def _print_RationalField(self, expr): if self._use_unicode: return prettyForm(u('\u211A')) else: return prettyForm('QQ') def _print_RealField(self, domain): if self._use_unicode: prefix = u('\u211D') else: prefix = 'RR' if domain.has_default_precision: return prettyForm(prefix) else: return self._print(pretty_symbol(prefix + "_" + str(domain.precision))) def _print_ComplexField(self, domain): if self._use_unicode: prefix = u('\u2102') else: prefix = 'CC' if domain.has_default_precision: return prettyForm(prefix) else: return self._print(pretty_symbol(prefix + "_" + str(domain.precision))) def _print_PolynomialRing(self, expr): args = list(expr.symbols) if not expr.order.is_default: order = prettyForm(*prettyForm("order=").right(self._print(expr.order))) args.append(order) pform = self._print_seq(args, '[', ']') pform = prettyForm(*pform.left(self._print(expr.domain))) return pform def _print_FractionField(self, expr): args = list(expr.symbols) if not expr.order.is_default: order = prettyForm(*prettyForm("order=").right(self._print(expr.order))) args.append(order) pform = self._print_seq(args, '(', ')') pform = prettyForm(*pform.left(self._print(expr.domain))) return pform def _print_PolynomialRingBase(self, expr): g = expr.symbols 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.domain))) 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 xrange(grid.width)] for i in xrange(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(u('\u2146 ') + pretty_symbol(string)) else: pform = self._print(field) pform = prettyForm(*pform.parens()) return prettyForm(*pform.left(u("\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 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) 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 : int or None, 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())) sympy-0.7.4.1/sympy/printing/pretty/tests/0000755000175000017500000000000012253362407020750 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/printing/pretty/tests/test_pretty.py0000644000175000017500000027604412253362407023725 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- from sympy import ( Abs, And, Basic, Chi, Ci, Derivative, Dict, Ei, Eq, Equivalent, FF, FiniteSet, Function, Ge, Gt, I, Implies, Integral, KroneckerDelta, Lambda, Le, Limit, Lt, Matrix, Mul, Nand, Ne, Nor, Not, O, Or, Piecewise, Pow, Product, QQ, RR, Rational, Ray, RootOf, RootSum, S, Segment, Shi, Si, Subs, Sum, Symbol, Tuple, Xor, ZZ, atan2, binomial, catalan, ceiling, conjugate, cos, euler, exp, expint, factorial, factorial2, floor, gamma, groebner, hyper, log, lowergamma, meijerg, oo, pi, sin, sqrt, subfactorial, symbols, tan, uppergamma, lex, ilex, grlex, elliptic_k, elliptic_f, elliptic_e, elliptic_pi) from sympy.printing.pretty import pretty as xpretty from sympy.printing.pretty import pprint from sympy.physics.units import joule from sympy.utilities.pytest import raises from sympy.core.trace import Tr from sympy.core.compatibility import u_decode as u a, b, x, y, z, k = symbols('a,b,x,y,z,k') th = Symbol('theta') ph = Symbol('phi') """ Expressions whose pretty-printing is tested here: (A '#' to the right of an expression indicates that its various acceptable orderings are accounted for by the tests.) BASIC EXPRESSIONS: oo (x**2) 1/x y*x**-2 x**Rational(-5,2) (-2)**x Pow(3, 1, evaluate=False) (x**2 + x + 1) # 1-x # 1-2*x # x/y -x/y (x+2)/y # (1+x)*y #3 -5*x/(x+10) # correct placement of negative sign 1 - Rational(3,2)*(x+1) -(-x + 5)*(-x - 2*sqrt(2) + 5) - (-y + 5)*(-y + 5) # Issue 2425 ORDERING: x**2 + x + 1 1 - x 1 - 2*x 2*x**4 + y**2 - x**2 + y**3 RELATIONAL: Eq(x, y) Lt(x, y) Gt(x, y) Le(x, y) Ge(x, y) Ne(x/(y+1), y**2) # RATIONAL NUMBERS: y*x**-2 y**Rational(3,2) * x**Rational(-5,2) sin(x)**3/tan(x)**2 FUNCTIONS (ABS, CONJ, EXP, FUNCTION BRACES, FACTORIAL, FLOOR, CEILING): (2*x + exp(x)) # Abs(x) Abs(x/(x**2+1)) # Abs(1 / (y - Abs(x))) factorial(n) factorial(2*n) subfactorial(n) subfactorial(2*n) factorial(factorial(factorial(n))) factorial(n+1) # conjugate(x) conjugate(f(x+1)) # f(x) f(x, y) f(x/(y+1), y) # sin(x)**2 conjugate(a+b*I) conjugate(exp(a+b*I)) conjugate( f(1 + conjugate(f(x))) ) # f(x/(y+1), y) # denom of first arg floor(1 / (y - floor(x))) ceiling(1 / (y - ceiling(x))) SQRT: sqrt(2) 2**Rational(1,3) 2**Rational(1,1000) sqrt(x**2 + 1) (1 + sqrt(5))**Rational(1,3) 2**(1/x) sqrt(2+pi) (2+(1+x**2)/(2+x))**Rational(1,4)+(1+x**Rational(1,1000))/sqrt(3+x**2) DERIVATIVES: Derivative(log(x), x, evaluate=False) Derivative(log(x), x, evaluate=False) + x # Derivative(log(x) + x**2, x, y, evaluate=False) Derivative(2*x*y, y, x, evaluate=False) + x**2 # beta(alpha).diff(alpha) INTEGRALS: Integral(log(x), x) Integral(x**2, x) Integral((sin(x))**2 / (tan(x))**2) Integral(x**(2**x), x) Integral(x**2, (x,1,2)) Integral(x**2, (x,Rational(1,2),10)) Integral(x**2*y**2, x,y) Integral(x**2, (x, None, 1)) Integral(x**2, (x, 1, None)) Integral(sin(th)/cos(ph), (th,0,pi), (ph, 0, 2*pi)) MATRICES: Matrix([[x**2+1, 1], [y, x+y]]) # Matrix([[x/y, y, th], [0, exp(I*k*ph), 1]]) PIECEWISE: Piecewise((x,x<1),(x**2,True)) SEQUENCES (TUPLES, LISTS, DICTIONARIES): () [] {} (1/x,) [x**2, 1/x, x, y, sin(th)**2/cos(ph)**2] (x**2, 1/x, x, y, sin(th)**2/cos(ph)**2) {x: sin(x)} {1/x: 1/y, x: sin(x)**2} # [x**2] (x**2,) {x**2: 1} LIMITS: Limit(x, x, oo) Limit(x**2, x, 0) Limit(1/x, x, 0) Limit(sin(x)/x, x, 0) UNITS: joule => kg*m**2/s SUBS: Subs(f(x), x, ph**2) Subs(f(x).diff(x), x, 0) Subs(f(x).diff(x)/y, (x, y), (0, Rational(1, 2))) ORDER: O(1) O(1/x) O(x**2 + y**2) """ def pretty(expr, order=None): """ASCII pretty-printing""" return xpretty(expr, order=order, use_unicode=False, wrap_line=False) def upretty(expr, order=None): """Unicode pretty-printing""" return xpretty(expr, order=order, use_unicode=True, wrap_line=False) def test_pretty_ascii_str(): assert pretty( 'xxx' ) == 'xxx' assert pretty( "xxx" ) == 'xxx' assert pretty( 'xxx\'xxx' ) == 'xxx\'xxx' assert pretty( 'xxx"xxx' ) == 'xxx\"xxx' assert pretty( 'xxx\"xxx' ) == 'xxx\"xxx' assert pretty( "xxx'xxx" ) == 'xxx\'xxx' assert pretty( "xxx\'xxx" ) == 'xxx\'xxx' assert pretty( "xxx\"xxx" ) == 'xxx\"xxx' assert pretty( "xxx\"xxx\'xxx" ) == 'xxx"xxx\'xxx' assert pretty( "xxx\nxxx" ) == 'xxx\nxxx' def test_pretty_unicode_str(): assert pretty( u('xxx') ) == u('xxx') assert pretty( u('xxx') ) == u('xxx') assert pretty( u('xxx\'xxx') ) == u('xxx\'xxx') assert pretty( u('xxx"xxx') ) == u('xxx\"xxx') assert pretty( u('xxx\"xxx') ) == u('xxx\"xxx') assert pretty( u("xxx'xxx") ) == u('xxx\'xxx') assert pretty( u("xxx\'xxx") ) == u('xxx\'xxx') assert pretty( u("xxx\"xxx") ) == u('xxx\"xxx') assert pretty( u("xxx\"xxx\'xxx") ) == u('xxx"xxx\'xxx') assert pretty( u("xxx\nxxx") ) == u('xxx\nxxx') def test_upretty_greek(): assert upretty( oo ) == u('∞') assert upretty( Symbol('alpha^+_1') ) == u('α⁺₁') assert upretty( Symbol('beta') ) == u('β') assert upretty(Symbol('lambda')) == u('λ') def test_upretty_multiindex(): assert upretty( Symbol('beta12') ) == u('β₁₂') assert upretty( Symbol('Y00') ) == u('Y₀₀') assert upretty( Symbol('Y_00') ) == u('Y₀₀') assert upretty( Symbol('F^+-') ) == u('F⁺⁻') def test_upretty_sub_super(): assert upretty( Symbol('beta_1_2') ) == u('β₁ ₂') assert upretty( Symbol('beta^1^2') ) == u('β¹ ²') assert upretty( Symbol('beta_1^2') ) == u('β²₁') assert upretty( Symbol('beta_10_20') ) == u('β₁₀ ₂₀') assert upretty( Symbol('beta_ax_gamma^i') ) == u('βⁱₐₓ ᵧ') assert upretty( Symbol("F^1^2_3_4") ) == u('F¹ ²₃ ₄') assert upretty( Symbol("F_1_2^3^4") ) == u('F³ ⁴₁ ₂') assert upretty( Symbol("F_1_2_3_4") ) == u('F₁ ₂ ₃ ₄') assert upretty( Symbol("F^1^2^3^4") ) == u('F¹ ² ³ ⁴') def test_upretty_subs_missingin_24(): assert upretty( Symbol('F_beta') ) == u('Fᵦ') assert upretty( Symbol('F_gamma') ) == u('Fᵧ') assert upretty( Symbol('F_rho') ) == u('Fᵨ') assert upretty( Symbol('F_phi') ) == u('Fᵩ') assert upretty( Symbol('F_chi') ) == u('Fᵪ') assert upretty( Symbol('F_a') ) == u('Fₐ') assert upretty( Symbol('F_e') ) == u('Fₑ') assert upretty( Symbol('F_i') ) == u('Fᵢ') assert upretty( Symbol('F_o') ) == u('Fₒ') assert upretty( Symbol('F_u') ) == u('Fᵤ') assert upretty( Symbol('F_r') ) == u('Fᵣ') assert upretty( Symbol('F_v') ) == u('Fᵥ') assert upretty( Symbol('F_x') ) == u('Fₓ') def test_upretty_modifiers(): # Accents assert upretty( Symbol('Fmathring') ) == u('F̊') assert upretty( Symbol('Fddddot') ) == u('F̈̈') assert upretty( Symbol('Fdddot') ) == u('F̈̇') assert upretty( Symbol('Fddot') ) == u('F̈') assert upretty( Symbol('Fdot') ) == u('Ḟ') assert upretty( Symbol('Fcheck') ) == u('F̌') assert upretty( Symbol('Fbreve') ) == u('F̆') assert upretty( Symbol('Facute') ) == u('F́') assert upretty( Symbol('Fgrave') ) == u('F̀') assert upretty( Symbol('Ftilde') ) == u('F̃') assert upretty( Symbol('Fhat') ) == u('F̂') assert upretty( Symbol('Fbar') ) == u('F̅') assert upretty( Symbol('Fvec') ) == u('F⃗') assert upretty( Symbol('Fprime') ) == u('F ̍') assert upretty( Symbol('Fprm') ) == u('F ̍') # No faces are actually implemented, but test to make sure the modifiers are stripped assert upretty( Symbol('Fbold') ) == u('Fbold') assert upretty( Symbol('Fbm') ) == u('Fbm') assert upretty( Symbol('Fcal') ) == u('Fcal') assert upretty( Symbol('Fscr') ) == u('Fscr') assert upretty( Symbol('Ffrak') ) == u('Ffrak') # Brackets assert upretty( Symbol('Fnorm') ) == u('‖F‖') assert upretty( Symbol('Favg') ) == u('⟨F⟩') assert upretty( Symbol('Fabs') ) == u('|F|') assert upretty( Symbol('Fmag') ) == u('|F|') # Combinations assert upretty( Symbol('xvecdot') ) == u('x⃗̇') assert upretty( Symbol('xDotVec') ) == u('ẋ⃗') assert upretty( Symbol('xHATNorm') ) == u('‖x̂‖') assert upretty( Symbol('xMathring_yCheckPRM__zbreveAbs') ) == u('x̊_y̌ ̍__|z̆|') assert upretty( Symbol('alphadothat_nVECDOT__tTildePrime') ) == u('α̇̂_n⃗̇__t̃ ̍') assert upretty( Symbol('x_dot') ) == u('x_dot') assert upretty( Symbol('x__dot') ) == u('x__dot') def test_pretty_basic(): assert pretty( -Rational(1)/2 ) == '-1/2' assert pretty( -Rational(13)/22 ) == \ """\ -13 \n\ ----\n\ 22 \ """ expr = oo ascii_str = \ """\ oo\ """ ucode_str = \ u("""\ ∞\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = (x**2) ascii_str = \ """\ 2\n\ x \ """ ucode_str = \ u("""\ 2\n\ x \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = 1/x ascii_str = \ """\ 1\n\ -\n\ x\ """ ucode_str = \ u("""\ 1\n\ ─\n\ x\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = y*x**-2 ascii_str = \ """\ y \n\ --\n\ 2\n\ x \ """ ucode_str = \ u("""\ y \n\ ──\n\ 2\n\ x \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = x**Rational(-5, 2) ascii_str = \ """\ 1 \n\ ----\n\ 5/2\n\ x \ """ ucode_str = \ u("""\ 1 \n\ ────\n\ 5/2\n\ x \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = (-2)**x ascii_str = \ """\ x\n\ (-2) \ """ ucode_str = \ u("""\ x\n\ (-2) \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str # See issue 1824 expr = Pow(3, 1, evaluate=False) ascii_str = \ """\ 1\n\ 3 \ """ ucode_str = \ u("""\ 1\n\ 3 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = (x**2 + x + 1) ascii_str_1 = \ """\ 2\n\ 1 + x + x \ """ ascii_str_2 = \ """\ 2 \n\ x + x + 1\ """ ascii_str_3 = \ """\ 2 \n\ x + 1 + x\ """ ucode_str_1 = \ u("""\ 2\n\ 1 + x + x \ """) ucode_str_2 = \ u("""\ 2 \n\ x + x + 1\ """) ucode_str_3 = \ u("""\ 2 \n\ x + 1 + x\ """) assert pretty(expr) in [ascii_str_1, ascii_str_2, ascii_str_3] assert upretty(expr) in [ucode_str_1, ucode_str_2, ucode_str_3] expr = 1 - x ascii_str_1 = \ """\ 1 - x\ """ ascii_str_2 = \ """\ -x + 1\ """ ucode_str_1 = \ u("""\ 1 - x\ """) ucode_str_2 = \ u("""\ -x + 1\ """) assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = 1 - 2*x ascii_str_1 = \ """\ 1 - 2*x\ """ ascii_str_2 = \ """\ -2*x + 1\ """ ucode_str_1 = \ u("""\ 1 - 2⋅x\ """) ucode_str_2 = \ u("""\ -2⋅x + 1\ """) assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = x/y ascii_str = \ """\ x\n\ -\n\ y\ """ ucode_str = \ u("""\ x\n\ ─\n\ y\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = -x/y ascii_str = \ """\ -x \n\ ---\n\ y \ """ ucode_str = \ u("""\ -x \n\ ───\n\ y \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = (x + 2)/y ascii_str_1 = \ """\ 2 + x\n\ -----\n\ y \ """ ascii_str_2 = \ """\ x + 2\n\ -----\n\ y \ """ ucode_str_1 = \ u("""\ 2 + x\n\ ─────\n\ y \ """) ucode_str_2 = \ u("""\ x + 2\n\ ─────\n\ y \ """) assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = (1 + x)*y ascii_str_1 = \ """\ y*(1 + x)\ """ ascii_str_2 = \ """\ (1 + x)*y\ """ ascii_str_3 = \ """\ y*(x + 1)\ """ ucode_str_1 = \ u("""\ y⋅(1 + x)\ """) ucode_str_2 = \ u("""\ (1 + x)⋅y\ """) ucode_str_3 = \ u("""\ y⋅(x + 1)\ """) assert pretty(expr) in [ascii_str_1, ascii_str_2, ascii_str_3] assert upretty(expr) in [ucode_str_1, ucode_str_2, ucode_str_3] # Test for correct placement of the negative sign expr = -5*x/(x + 10) ascii_str_1 = \ """\ -5*x \n\ ------\n\ 10 + x\ """ ascii_str_2 = \ """\ -5*x \n\ ------\n\ x + 10\ """ ucode_str_1 = \ u("""\ -5⋅x \n\ ──────\n\ 10 + x\ """) ucode_str_2 = \ u("""\ -5⋅x \n\ ──────\n\ x + 10\ """) assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = -S(1)/2 - 3*x ascii_str = \ """\ -3*x - 1/2\ """ ucode_str = \ u("""\ -3⋅x - 1/2\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = S(1)/2 - 3*x ascii_str = \ """\ -3*x + 1/2\ """ ucode_str = \ u("""\ -3⋅x + 1/2\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = -S(1)/2 - 3*x/2 ascii_str = \ """\ 3*x 1\n\ - --- - -\n\ 2 2\ """ ucode_str = \ u("""\ 3⋅x 1\n\ - ─── - ─\n\ 2 2\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = S(1)/2 - 3*x/2 ascii_str = \ """\ 3*x 1\n\ - --- + -\n\ 2 2\ """ ucode_str = \ u("""\ 3⋅x 1\n\ - ─── + ─\n\ 2 2\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_negative_fractions(): expr = -x/y ascii_str =\ """\ -x \n\ ---\n\ y \ """ ucode_str =\ u("""\ -x \n\ ───\n\ y \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = -x*z/y ascii_str =\ """\ -x*z \n\ -----\n\ y \ """ ucode_str =\ u("""\ -x⋅z \n\ ─────\n\ y \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = x**2/y ascii_str =\ """\ 2\n\ x \n\ --\n\ y \ """ ucode_str =\ u("""\ 2\n\ x \n\ ──\n\ y \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = -x**2/y ascii_str =\ """\ 2 \n\ -x \n\ ----\n\ y \ """ ucode_str =\ u("""\ 2 \n\ -x \n\ ────\n\ y \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = -x/(y*z) ascii_str =\ """\ -x \n\ ---\n\ y*z\ """ ucode_str =\ u("""\ -x \n\ ───\n\ y⋅z\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = -a/y**2 ascii_str =\ """\ -a \n\ ---\n\ 2\n\ y \ """ ucode_str =\ u("""\ -a \n\ ───\n\ 2\n\ y \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = y**(-a/b) ascii_str =\ """\ -a \n\ ---\n\ b \n\ y \ """ ucode_str =\ u("""\ -a \n\ ───\n\ b \n\ y \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = -1/y**2 ascii_str =\ """\ -1 \n\ ---\n\ 2\n\ y \ """ ucode_str =\ u("""\ -1 \n\ ───\n\ 2\n\ y \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = -10/b**2 ascii_str =\ """\ -10 \n\ ----\n\ 2 \n\ b \ """ ucode_str =\ u("""\ -10 \n\ ────\n\ 2 \n\ b \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Rational(-200, 37) ascii_str =\ """\ -200 \n\ -----\n\ 37 \ """ ucode_str =\ u("""\ -200 \n\ ─────\n\ 37 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_issue_2425(): assert pretty(-(-x + 5)*(-x - 2*sqrt(2) + 5) - (-y + 5)*(-y + 5)) == \ """\ / ___ \\ 2\n\ (x - 5)*\\-x - 2*\\/ 2 + 5/ - (-y + 5) \ """ assert upretty(-(-x + 5)*(-x - 2*sqrt(2) + 5) - (-y + 5)*(-y + 5)) == \ u("""\ ⎛ ___ ⎞ 2\n\ (x - 5)⋅⎝-x - 2⋅╲╱ 2 + 5⎠ - (-y + 5) \ """) def test_pretty_ordering(): assert pretty(x**2 + x + 1, order='lex') == \ """\ 2 \n\ x + x + 1\ """ assert pretty(x**2 + x + 1, order='rev-lex') == \ """\ 2\n\ 1 + x + x \ """ assert pretty(1 - x, order='lex') == '-x + 1' assert pretty(1 - x, order='rev-lex') == '1 - x' assert pretty(1 - 2*x, order='lex') == '-2*x + 1' assert pretty(1 - 2*x, order='rev-lex') == '1 - 2*x' f = 2*x**4 + y**2 - x**2 + y**3 assert pretty(f, order=None) == \ """\ 4 2 3 2\n\ 2*x - x + y + y \ """ assert pretty(f, order='lex') == \ """\ 4 2 3 2\n\ 2*x - x + y + y \ """ assert pretty(f, order='rev-lex') == \ """\ 2 3 2 4\n\ y + y - x + 2*x \ """ expr = x - x**3/6 + x**5/120 + O(x**6) ascii_str = \ """\ 3 5 \n\ x x / 6\\\n\ x - -- + --- + O\\x /\n\ 6 120 \ """ ucode_str = \ u("""\ 3 5 \n\ x x ⎛ 6⎞\n\ x - ── + ─── + O⎝x ⎠\n\ 6 120 \ """) assert pretty(expr, order=None) == ascii_str assert upretty(expr, order=None) == ucode_str assert pretty(expr, order='lex') == ascii_str assert upretty(expr, order='lex') == ucode_str assert pretty(expr, order='rev-lex') == ascii_str assert upretty(expr, order='rev-lex') == ucode_str def test_pretty_relational(): expr = Eq(x, y) ascii_str = \ """\ x = y\ """ ucode_str = \ u("""\ x = y\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Lt(x, y) ascii_str = \ """\ x < y\ """ ucode_str = \ u("""\ x < y\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Gt(x, y) ascii_str = \ """\ x > y\ """ ucode_str = \ u("""\ x > y\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Le(x, y) ascii_str = \ """\ x <= y\ """ ucode_str = \ u("""\ x ≤ y\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Ge(x, y) ascii_str = \ """\ x >= y\ """ ucode_str = \ u("""\ x ≥ y\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Ne(x/(y + 1), y**2) ascii_str_1 = \ """\ x 2\n\ ----- != y \n\ 1 + y \ """ ascii_str_2 = \ """\ x 2\n\ ----- != y \n\ y + 1 \ """ ucode_str_1 = \ u("""\ x 2\n\ ───── ≠ y \n\ 1 + y \ """) ucode_str_2 = \ u("""\ x 2\n\ ───── ≠ y \n\ y + 1 \ """) assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] def test_pretty_rational(): expr = y*x**-2 ascii_str = \ """\ y \n\ --\n\ 2\n\ x \ """ ucode_str = \ u("""\ y \n\ ──\n\ 2\n\ x \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = y**Rational(3, 2) * x**Rational(-5, 2) ascii_str = \ """\ 3/2\n\ y \n\ ----\n\ 5/2\n\ x \ """ ucode_str = \ u("""\ 3/2\n\ y \n\ ────\n\ 5/2\n\ x \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = sin(x)**3/tan(x)**2 ascii_str = \ """\ 3 \n\ sin (x)\n\ -------\n\ 2 \n\ tan (x)\ """ ucode_str = \ u("""\ 3 \n\ sin (x)\n\ ───────\n\ 2 \n\ tan (x)\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_functions(): """Tests for Abs, conjugate, exp, function braces, and factorial.""" expr = (2*x + exp(x)) ascii_str_1 = \ """\ x\n\ 2*x + e \ """ ascii_str_2 = \ """\ x \n\ e + 2*x\ """ ucode_str_1 = \ u("""\ x\n\ 2⋅x + ℯ \ """) ucode_str_2 = \ u("""\ x \n\ ℯ + 2⋅x\ """) assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = Abs(x) ascii_str = \ """\ |x|\ """ ucode_str = \ u("""\ │x│\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Abs(x/(x**2 + 1)) ascii_str_1 = \ """\ | x |\n\ |------|\n\ | 2|\n\ |1 + x |\ """ ascii_str_2 = \ """\ | x |\n\ |------|\n\ | 2 |\n\ |x + 1|\ """ ucode_str_1 = \ u("""\ │ x │\n\ │──────│\n\ │ 2│\n\ │1 + x │\ """) ucode_str_2 = \ u("""\ │ x │\n\ │──────│\n\ │ 2 │\n\ │x + 1│\ """) assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = Abs(1 / (y - Abs(x))) ascii_str = \ """\ | 1 |\n\ |-------|\n\ |y - |x||\ """ ucode_str = \ u("""\ │ 1 │\n\ │───────│\n\ │y - │x││\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str n = Symbol('n', integer=True) expr = factorial(n) ascii_str = \ """\ n!\ """ ucode_str = \ u("""\ n!\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = factorial(2*n) ascii_str = \ """\ (2*n)!\ """ ucode_str = \ u("""\ (2⋅n)!\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = factorial(factorial(factorial(n))) ascii_str = \ """\ ((n!)!)!\ """ ucode_str = \ u("""\ ((n!)!)!\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = factorial(n + 1) ascii_str_1 = \ """\ (1 + n)!\ """ ascii_str_2 = \ """\ (n + 1)!\ """ ucode_str_1 = \ u("""\ (1 + n)!\ """) ucode_str_2 = \ u("""\ (n + 1)!\ """) assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = subfactorial(n) ascii_str = \ """\ !n\ """ ucode_str = \ u("""\ !n\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = subfactorial(2*n) ascii_str = \ """\ !(2*n)\ """ ucode_str = \ u("""\ !(2⋅n)\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str n = Symbol('n', integer=True) expr = factorial2(n) ascii_str = \ """\ n!!\ """ ucode_str = \ u("""\ n!!\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = factorial2(2*n) ascii_str = \ """\ (2*n)!!\ """ ucode_str = \ u("""\ (2⋅n)!!\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = factorial2(factorial2(factorial2(n))) ascii_str = \ """\ ((n!!)!!)!!\ """ ucode_str = \ u("""\ ((n!!)!!)!!\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = factorial2(n + 1) ascii_str_1 = \ """\ (1 + n)!!\ """ ascii_str_2 = \ """\ (n + 1)!!\ """ ucode_str_1 = \ u("""\ (1 + n)!!\ """) ucode_str_2 = \ u("""\ (n + 1)!!\ """) assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = 2*binomial(n, k) ascii_str = \ """\ /n\\\n\ 2*| |\n\ \k/\ """ ucode_str = \ u("""\ ⎛n⎞\n\ 2⋅⎜ ⎟\n\ ⎝k⎠\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = 2*binomial(2*n, k) ascii_str = \ """\ /2*n\\\n\ 2*| |\n\ \ k /\ """ ucode_str = \ u("""\ ⎛2⋅n⎞\n\ 2⋅⎜ ⎟\n\ ⎝ k ⎠\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = 2*binomial(n**2, k) ascii_str = \ """\ / 2\\\n\ |n |\n\ 2*| |\n\ \k /\ """ ucode_str = \ u("""\ ⎛ 2⎞\n\ ⎜n ⎟\n\ 2⋅⎜ ⎟\n\ ⎝k ⎠\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = catalan(n) ascii_str = \ """\ C \n\ n\ """ ucode_str = \ u("""\ C \n\ n\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = conjugate(x) ascii_str = \ """\ _\n\ x\ """ ucode_str = \ u("""\ _\n\ x\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str f = Function('f') expr = conjugate(f(x + 1)) ascii_str_1 = \ """\ ________\n\ f(1 + x)\ """ ascii_str_2 = \ """\ ________\n\ f(x + 1)\ """ ucode_str_1 = \ u("""\ ________\n\ f(1 + x)\ """) ucode_str_2 = \ u("""\ ________\n\ f(x + 1)\ """) assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = f(x) ascii_str = \ """\ f(x)\ """ ucode_str = \ u("""\ f(x)\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = f(x, y) ascii_str = \ """\ f(x, y)\ """ ucode_str = \ u("""\ f(x, y)\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = f(x/(y + 1), y) ascii_str_1 = \ """\ / x \\\n\ f|-----, y|\n\ \\1 + y /\ """ ascii_str_2 = \ """\ / x \\\n\ f|-----, y|\n\ \\y + 1 /\ """ ucode_str_1 = \ u("""\ ⎛ x ⎞\n\ f⎜─────, y⎟\n\ ⎝1 + y ⎠\ """) ucode_str_2 = \ u("""\ ⎛ x ⎞\n\ f⎜─────, y⎟\n\ ⎝y + 1 ⎠\ """) assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = sin(x)**2 ascii_str = \ """\ 2 \n\ sin (x)\ """ ucode_str = \ u("""\ 2 \n\ sin (x)\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = conjugate(a + b*I) ascii_str = \ """\ _ _\n\ a - I*b\ """ ucode_str = \ u("""\ _ _\n\ a - ⅈ⋅b\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = conjugate(exp(a + b*I)) ascii_str = \ """\ _ _\n\ a - I*b\n\ e \ """ ucode_str = \ u("""\ _ _\n\ a - ⅈ⋅b\n\ ℯ \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = conjugate( f(1 + conjugate(f(x))) ) ascii_str_1 = \ """\ ___________\n\ / ____\\\n\ f\\1 + f(x)/\ """ ascii_str_2 = \ """\ ___________\n\ /____ \\\n\ f\\f(x) + 1/\ """ ucode_str_1 = \ u("""\ ___________\n\ ⎛ ____⎞\n\ f⎝1 + f(x)⎠\ """) ucode_str_2 = \ u("""\ ___________\n\ ⎛____ ⎞\n\ f⎝f(x) + 1⎠\ """) assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = f(x/(y + 1), y) ascii_str_1 = \ """\ / x \\\n\ f|-----, y|\n\ \\1 + y /\ """ ascii_str_2 = \ """\ / x \\\n\ f|-----, y|\n\ \\y + 1 /\ """ ucode_str_1 = \ u("""\ ⎛ x ⎞\n\ f⎜─────, y⎟\n\ ⎝1 + y ⎠\ """) ucode_str_2 = \ u("""\ ⎛ x ⎞\n\ f⎜─────, y⎟\n\ ⎝y + 1 ⎠\ """) assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = floor(1 / (y - floor(x))) ascii_str = \ """\ / 1 \\\n\ floor|------------|\n\ \y - floor(x)/\ """ ucode_str = \ u("""\ ⎢ 1 ⎥\n\ ⎢───────⎥\n\ ⎣y - ⌊x⌋⎦\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = ceiling(1 / (y - ceiling(x))) ascii_str = \ """\ / 1 \\\n\ ceiling|--------------|\n\ \y - ceiling(x)/\ """ ucode_str = \ u("""\ ⎡ 1 ⎤\n\ ⎢───────⎥\n\ ⎢y - ⌈x⌉⎥\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = euler(n) ascii_str = \ """\ E \n\ n\ """ ucode_str = \ u("""\ E \n\ n\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = euler(1/(1 + 1/(1 + 1/n))) ascii_str = \ """\ E \n\ 1 \n\ ---------\n\ 1 \n\ 1 + -----\n\ 1\n\ 1 + -\n\ n\ """ ucode_str = \ u("""\ E \n\ 1 \n\ ─────────\n\ 1 \n\ 1 + ─────\n\ 1\n\ 1 + ─\n\ n\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_sqrt(): expr = sqrt(2) ascii_str = \ """\ ___\n\ \/ 2 \ """ ucode_str = \ u("""\ ___\n\ ╲╱ 2 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = 2**Rational(1, 3) ascii_str = \ """\ 3 ___\n\ \/ 2 \ """ ucode_str = \ u("""\ 3 ___\n\ ╲╱ 2 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = 2**Rational(1, 1000) ascii_str = \ """\ 1000___\n\ \/ 2 \ """ ucode_str = \ u("""\ 1000___\n\ ╲╱ 2 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = sqrt(x**2 + 1) ascii_str = \ """\ ________\n\ / 2 \n\ \/ x + 1 \ """ ucode_str = \ u("""\ ________\n\ ╱ 2 \n\ ╲╱ x + 1 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = (1 + sqrt(5))**Rational(1, 3) ascii_str = \ """\ ___________\n\ 3 / ___ \n\ \/ 1 + \/ 5 \ """ ucode_str = \ u("""\ ___________\n\ 3 ╱ ___ \n\ ╲╱ 1 + ╲╱ 5 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = 2**(1/x) ascii_str = \ """\ x ___\n\ \/ 2 \ """ ucode_str = \ u("""\ x ___\n\ ╲╱ 2 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = sqrt(2 + pi) ascii_str = \ """\ ________\n\ \/ 2 + pi \ """ ucode_str = \ u("""\ _______\n\ ╲╱ 2 + π \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = (2 + ( 1 + x**2)/(2 + x))**Rational(1, 4) + (1 + x**Rational(1, 1000))/sqrt(3 + x**2) ascii_str = \ """\ ____________ \n\ / 2 1000___ \n\ / x + 1 \/ x + 1\n\ 4 / 2 + ------ + -----------\n\ \/ x + 2 ________\n\ / 2 \n\ \/ x + 3 \ """ ucode_str = \ u("""\ ____________ \n\ ╱ 2 1000___ \n\ ╱ x + 1 ╲╱ x + 1\n\ 4 ╱ 2 + ────── + ───────────\n\ ╲╱ x + 2 ________\n\ ╱ 2 \n\ ╲╱ x + 3 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_KroneckerDelta(): x, y = symbols("x, y") expr = KroneckerDelta(x, y) ascii_str = \ """\ d \n\ x,y\ """ ucode_str = \ u("""\ δ \n\ x,y\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_product(): n, m, k, l = symbols('n m k l') f = symbols('f', cls=Function) expr = Product(f((n/3)**2), (n, k**2, l)) unicode_str = \ u("""\ l \n\ ┬────────┬ \n\ │ │ ⎛ 2⎞\n\ │ │ ⎜n ⎟\n\ │ │ f⎜──⎟\n\ │ │ ⎝9 ⎠\n\ │ │ \n\ 2 \n\ n = k """) ascii_str = \ """\ l \n\ __________ \n\ | | / 2\\\n\ | | |n |\n\ | | f|--|\n\ | | \9 /\n\ | | \n\ 2 \n\ n = k """ assert pretty(expr) == ascii_str assert upretty(expr) == unicode_str expr = Product(f((n/3)**2), (n, k**2, l), (l, 1, m)) unicode_str = \ u("""\ m l \n\ ┬────────┬ ┬────────┬ \n\ │ │ │ │ ⎛ 2⎞\n\ │ │ │ │ ⎜n ⎟\n\ │ │ │ │ f⎜──⎟\n\ │ │ │ │ ⎝9 ⎠\n\ │ │ │ │ \n\ l = 1 2 \n\ n = k """) ascii_str = \ """\ m l \n\ __________ __________ \n\ | | | | / 2\\\n\ | | | | |n |\n\ | | | | f|--|\n\ | | | | \9 /\n\ | | | | \n\ l = 1 2 \n\ n = k """ assert pretty(expr) == ascii_str assert upretty(expr) == unicode_str def test_pretty_lambda(): # S.IdentityFunction is a special case expr = Lambda(y, y) assert pretty(expr) == "x -> x" assert upretty(expr) == u("x ↦ x") expr = Lambda(x, x+1) assert pretty(expr) == "x -> x + 1" assert upretty(expr) == u("x ↦ x + 1") expr = Lambda(x, x**2) ascii_str = \ """\ 2\n\ x -> x \ """ ucode_str = \ u("""\ 2\n\ x ↦ x \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Lambda(x, x**2)**2 ascii_str = \ """\ 2 / 2\\ \n\ \\x -> x / \ """ ucode_str = \ u("""\ 2 ⎛ 2⎞ \n\ ⎝x ↦ x ⎠ \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Lambda((x, y), x) ascii_str = "(x, y) -> x" ucode_str = u("(x, y) ↦ x") assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Lambda((x, y), x**2) ascii_str = \ """\ 2\n\ (x, y) -> x \ """ ucode_str = \ u("""\ 2\n\ (x, y) ↦ x \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_order(): expr = O(1) ascii_str = \ """\ O(1)\ """ ucode_str = \ u("""\ O(1)\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = O(1/x) ascii_str = \ """\ /1\\\n\ O|-|\n\ \\x/\ """ ucode_str = \ u("""\ ⎛1⎞\n\ O⎜─⎟\n\ ⎝x⎠\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = O(x**2 + y**2) ascii_str = \ """\ / 2 2 \\\n\ O\\x + y ; (x, y) -> (0, 0)/\ """ ucode_str = \ u("""\ ⎛ 2 2 ⎞\n\ O⎝x + y ; (x, y) → (0, 0)⎠\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = O(1, (x, oo)) ascii_str = \ """\ O(1; x -> oo)\ """ ucode_str = \ u("""\ O(1; x → ∞)\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = O(1/x, (x, oo)) ascii_str = \ """\ /1 \\\n\ O|-; x -> oo|\n\ \\x /\ """ ucode_str = \ u("""\ ⎛1 ⎞\n\ O⎜─; x → ∞⎟\n\ ⎝x ⎠\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = O(x**2 + y**2, (x, oo), (y, oo)) ascii_str = \ """\ / 2 2 \\\n\ O\\x + y ; (x, y) -> (oo, oo)/\ """ ucode_str = \ u("""\ ⎛ 2 2 ⎞\n\ O⎝x + y ; (x, y) → (∞, ∞)⎠\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_derivatives(): # Simple expr = Derivative(log(x), x, evaluate=False) ascii_str = \ """\ d \n\ --(log(x))\n\ dx \ """ ucode_str = \ u("""\ d \n\ ──(log(x))\n\ dx \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Derivative(log(x), x, evaluate=False) + x ascii_str_1 = \ """\ d \n\ x + --(log(x))\n\ dx \ """ ascii_str_2 = \ """\ d \n\ --(log(x)) + x\n\ dx \ """ ucode_str_1 = \ u("""\ d \n\ x + ──(log(x))\n\ dx \ """) ucode_str_2 = \ u("""\ d \n\ ──(log(x)) + x\n\ dx \ """) assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] # basic partial derivatives expr = Derivative(log(x + y) + x, x) ascii_str_1 = \ """\ d \n\ --(log(x + y) + x)\n\ dx \ """ ascii_str_2 = \ """\ d \n\ --(x + log(x + y))\n\ dx \ """ ucode_str_1 = \ u("""\ ∂ \n\ ──(log(x + y) + x)\n\ ∂x \ """) ucode_str_2 = \ u("""\ ∂ \n\ ──(x + log(x + y))\n\ ∂x \ """) assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2], upretty(expr) # Multiple symbols expr = Derivative(log(x) + x**2, x, y) ascii_str_1 = \ """\ 2 \n\ d / 2\\\n\ -----\log(x) + x /\n\ dy dx \ """ ascii_str_2 = \ """\ 2 \n\ d / 2 \\\n\ -----\\x + log(x)/\n\ dy dx \ """ ucode_str_1 = \ u("""\ 2 \n\ d ⎛ 2⎞\n\ ─────⎝log(x) + x ⎠\n\ dy dx \ """) ucode_str_2 = \ u("""\ 2 \n\ d ⎛ 2 ⎞\n\ ─────⎝x + log(x)⎠\n\ dy dx \ """) assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = Derivative(2*x*y, y, x) + x**2 ascii_str_1 = \ """\ 2 \n\ d 2\n\ -----(2*x*y) + x \n\ dx dy \ """ ascii_str_2 = \ """\ 2 \n\ 2 d \n\ x + -----(2*x*y)\n\ dx dy \ """ ucode_str_1 = \ u("""\ 2 \n\ ∂ 2\n\ ─────(2⋅x⋅y) + x \n\ ∂x ∂y \ """) ucode_str_2 = \ u("""\ 2 \n\ 2 ∂ \n\ x + ─────(2⋅x⋅y)\n\ ∂x ∂y \ """) assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = Derivative(2*x*y, x, x) ascii_str = \ """\ 2 \n\ d \n\ ---(2*x*y)\n\ 2 \n\ dx \ """ ucode_str = \ u("""\ 2 \n\ ∂ \n\ ───(2⋅x⋅y)\n\ 2 \n\ ∂x \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Derivative(2*x*y, x, 17) ascii_str = \ """\ 17 \n\ d \n\ ----(2*x*y)\n\ 17 \n\ dx \ """ ucode_str = \ u("""\ 17 \n\ ∂ \n\ ────(2⋅x⋅y)\n\ 17 \n\ ∂x \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Derivative(2*x*y, x, x, y) ascii_str = \ """\ 3 \n\ d \n\ ------(2*x*y)\n\ 2 \n\ dy dx \ """ ucode_str = \ u("""\ 3 \n\ ∂ \n\ ──────(2⋅x⋅y)\n\ 2 \n\ ∂y ∂x \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str # Greek letters alpha = Symbol('alpha') beta = Function('beta') expr = beta(alpha).diff(alpha) ascii_str = \ """\ d \n\ ------(beta(alpha))\n\ dalpha \ """ ucode_str = \ u("""\ d \n\ ──(β(α))\n\ dα \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_integrals(): expr = Integral(log(x), x) ascii_str = \ """\ / \n\ | \n\ | log(x) dx\n\ | \n\ / \ """ ucode_str = \ u("""\ ⌠ \n\ ⎮ log(x) dx\n\ ⌡ \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Integral(x**2, x) ascii_str = \ """\ / \n\ | \n\ | 2 \n\ | x dx\n\ | \n\ / \ """ ucode_str = \ u("""\ ⌠ \n\ ⎮ 2 \n\ ⎮ x dx\n\ ⌡ \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Integral((sin(x))**2 / (tan(x))**2) ascii_str = \ """\ / \n\ | \n\ | 2 \n\ | sin (x) \n\ | ------- dx\n\ | 2 \n\ | tan (x) \n\ | \n\ / \ """ ucode_str = \ u("""\ ⌠ \n\ ⎮ 2 \n\ ⎮ sin (x) \n\ ⎮ ─────── dx\n\ ⎮ 2 \n\ ⎮ tan (x) \n\ ⌡ \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Integral(x**(2**x), x) ascii_str = \ """\ / \n\ | \n\ | / x\ \n\ | \\2 / \n\ | x dx\n\ | \n\ / \ """ ucode_str = \ u("""\ ⌠ \n\ ⎮ ⎛ x⎞ \n\ ⎮ ⎝2 ⎠ \n\ ⎮ x dx\n\ ⌡ \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Integral(x**2, (x, 1, 2)) ascii_str = \ """\ 2 \n\ / \n\ | \n\ | 2 \n\ | x dx\n\ | \n\ / \n\ 1 \ """ ucode_str = \ u("""\ 2 \n\ ⌠ \n\ ⎮ 2 \n\ ⎮ x dx\n\ ⌡ \n\ 1 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Integral(x**2, (x, Rational(1, 2), 10)) ascii_str = \ """\ 10 \n\ / \n\ | \n\ | 2 \n\ | x dx\n\ | \n\ / \n\ 1/2 \ """ ucode_str = \ u("""\ 10 \n\ ⌠ \n\ ⎮ 2 \n\ ⎮ x dx\n\ ⌡ \n\ 1/2 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Integral(x**2*y**2, x, y) ascii_str = \ """\ / / \n\ | | \n\ | | 2 2 \n\ | | x *y dx dy\n\ | | \n\ / / \ """ ucode_str = \ u("""\ ⌠ ⌠ \n\ ⎮ ⎮ 2 2 \n\ ⎮ ⎮ x ⋅y dx dy\n\ ⌡ ⌡ \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Integral(sin(th)/cos(ph), (th, 0, pi), (ph, 0, 2*pi)) ascii_str = \ """\ 2*pi pi \n\ / / \n\ | | \n\ | | sin(theta) \n\ | | ---------- d(theta) d(phi)\n\ | | cos(phi) \n\ | | \n\ / / \n\ 0 0 \ """ ucode_str = \ u("""\ 2⋅π π \n\ ⌠ ⌠ \n\ ⎮ ⎮ sin(θ) \n\ ⎮ ⎮ ────── dθ dφ\n\ ⎮ ⎮ cos(φ) \n\ ⌡ ⌡ \n\ 0 0 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_matrix(): # Empty Matrix expr = Matrix() ascii_str = "[]" unicode_str = "[]" assert pretty(expr) == ascii_str assert upretty(expr) == unicode_str expr = Matrix(2, 0, lambda i, j: 0) ascii_str = "[]" unicode_str = "[]" assert pretty(expr) == ascii_str assert upretty(expr) == unicode_str expr = Matrix(0, 2, lambda i, j: 0) ascii_str = "[]" unicode_str = "[]" assert pretty(expr) == ascii_str assert upretty(expr) == unicode_str expr = Matrix([[x**2 + 1, 1], [y, x + y]]) ascii_str_1 = \ """\ [ 2 ] [1 + x 1 ] [ ] [ y x + y]\ """ ascii_str_2 = \ """\ [ 2 ] [x + 1 1 ] [ ] [ y x + y]\ """ ucode_str_1 = \ u("""\ ⎡ 2 ⎤ ⎢1 + x 1 ⎥ ⎢ ⎥ ⎣ y x + y⎦\ """) ucode_str_2 = \ u("""\ ⎡ 2 ⎤ ⎢x + 1 1 ⎥ ⎢ ⎥ ⎣ y x + y⎦\ """) assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = Matrix([[x/y, y, th], [0, exp(I*k*ph), 1]]) ascii_str = \ """\ [x ] [- y theta] [y ] [ ] [ I*k*phi ] [0 e 1 ]\ """ ucode_str = \ u("""\ ⎡x ⎤ ⎢─ y θ⎥ ⎢y ⎥ ⎢ ⎥ ⎢ ⅈ⋅k⋅φ ⎥ ⎣0 ℯ 1⎦\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_Adjoint(): from sympy.matrices import Adjoint, Inverse, MatrixSymbol, Transpose X = MatrixSymbol('X', 2, 2) Y = MatrixSymbol('Y', 2, 2) assert pretty(Adjoint(X)) == " +\nX " assert pretty(Adjoint(X + Y)) == " +\n(X + Y) " assert pretty(Adjoint(X) + Adjoint(Y)) == " + +\nX + Y " assert pretty(Adjoint(X*Y)) == " +\n(X*Y) " assert pretty(Adjoint(Y)*Adjoint(X)) == " + +\nY *X " assert pretty(Adjoint(X**2)) == " +\n/ 2\\ \n\\X / " assert pretty(Adjoint(X)**2) == " 2\n/ +\\ \n\\X / " assert pretty(Adjoint(Inverse(X))) == " +\n/ -1\\ \n\\X / " assert pretty(Inverse(Adjoint(X))) == " -1\n/ +\\ \n\\X / " assert pretty(Adjoint(Transpose(X))) == " +\n/ T\\ \n\\X / " assert pretty(Transpose(Adjoint(X))) == " T\n/ +\\ \n\\X / " assert upretty(Adjoint(X)) == u(" †\nX ") assert upretty(Adjoint(X + Y)) == u(" †\n(X + Y) ") assert upretty(Adjoint(X) + Adjoint(Y)) == u(" † †\nX + Y ") assert upretty(Adjoint(X*Y)) == u(" †\n(X⋅Y) ") assert upretty(Adjoint(Y)*Adjoint(X)) == u(" † †\nY ⋅X ") assert upretty(Adjoint(X**2)) == \ u(" †\n⎛ 2⎞ \n⎝X ⎠ ") assert upretty(Adjoint(X)**2) == \ u(" 2\n⎛ †⎞ \n⎝X ⎠ ") assert upretty(Adjoint(Inverse(X))) == \ u(" †\n⎛ -1⎞ \n⎝X ⎠ ") assert upretty(Inverse(Adjoint(X))) == \ u(" -1\n⎛ †⎞ \n⎝X ⎠ ") assert upretty(Adjoint(Transpose(X))) == \ u(" †\n⎛ T⎞ \n⎝X ⎠ ") assert upretty(Transpose(Adjoint(X))) == \ u(" T\n⎛ †⎞ \n⎝X ⎠ ") def test_pretty_piecewise(): expr = Piecewise((x, x < 1), (x**2, True)) ascii_str = \ """\ /x for x < 1\n\ | \n\ < 2 \n\ |x otherwise\n\ \ \ """ ucode_str = \ u("""\ ⎧x for x < 1\n\ ⎪ \n\ ⎨ 2 \n\ ⎪x otherwise\n\ ⎩ \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = -Piecewise((x, x < 1), (x**2, True)) ascii_str = \ """\ //x for x < 1\\\n\ || |\n\ -|< 2 |\n\ ||x otherwise|\n\ \\\\ /\ """ ucode_str = \ u("""\ ⎛⎧x for x < 1⎞\n\ ⎜⎪ ⎟\n\ -⎜⎨ 2 ⎟\n\ ⎜⎪x otherwise⎟\n\ ⎝⎩ ⎠\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = x + Piecewise((x, x > 0), (y, True)) + Piecewise((x/y, x < 2), (y**2, x > 2), (1, True)) + 1 ascii_str = \ """\ //x \ \n\ ||- for x < 2| \n\ ||y | \n\ //x for x > 0\ || | \n\ x + |< | + |< 2 | + 1\n\ \\\\y otherwise/ ||y for x > 2| \n\ || | \n\ ||1 otherwise| \n\ \\\\ / \ """ ucode_str = \ u("""\ ⎛⎧x ⎞ \n\ ⎜⎪─ for x < 2⎟ \n\ ⎜⎪y ⎟ \n\ ⎛⎧x for x > 0⎞ ⎜⎪ ⎟ \n\ x + ⎜⎨ ⎟ + ⎜⎨ 2 ⎟ + 1\n\ ⎝⎩y otherwise⎠ ⎜⎪y for x > 2⎟ \n\ ⎜⎪ ⎟ \n\ ⎜⎪1 otherwise⎟ \n\ ⎝⎩ ⎠ \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = x - Piecewise((x, x > 0), (y, True)) + Piecewise((x/y, x < 2), (y**2, x > 2), (1, True)) + 1 ascii_str = \ """\ //x \ \n\ ||- for x < 2| \n\ ||y | \n\ //x for x > 0\ || | \n\ x - |< | + |< 2 | + 1\n\ \\\\y otherwise/ ||y for x > 2| \n\ || | \n\ ||1 otherwise| \n\ \\\\ / \ """ ucode_str = \ u("""\ ⎛⎧x ⎞ \n\ ⎜⎪─ for x < 2⎟ \n\ ⎜⎪y ⎟ \n\ ⎛⎧x for x > 0⎞ ⎜⎪ ⎟ \n\ x - ⎜⎨ ⎟ + ⎜⎨ 2 ⎟ + 1\n\ ⎝⎩y otherwise⎠ ⎜⎪y for x > 2⎟ \n\ ⎜⎪ ⎟ \n\ ⎜⎪1 otherwise⎟ \n\ ⎝⎩ ⎠ \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = x*Piecewise((x, x > 0), (y, True)) ascii_str = \ """\ //x for x > 0\\\n\ x*|< |\n\ \\\\y otherwise/\ """ ucode_str = \ u("""\ ⎛⎧x for x > 0⎞\n\ x⋅⎜⎨ ⎟\n\ ⎝⎩y otherwise⎠\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Piecewise((x, x > 0), (y, True))*Piecewise((x/y, x < 2), (y**2, x > 2), (1, True)) ascii_str = \ """\ //x \\\n\ ||- for x < 2|\n\ ||y |\n\ //x for x > 0\ || |\n\ |< |*|< 2 |\n\ \\\\y otherwise/ ||y for x > 2|\n\ || |\n\ ||1 otherwise|\n\ \\\\ /\ """ ucode_str = \ u("""\ ⎛⎧x ⎞\n\ ⎜⎪─ for x < 2⎟\n\ ⎜⎪y ⎟\n\ ⎛⎧x for x > 0⎞ ⎜⎪ ⎟\n\ ⎜⎨ ⎟⋅⎜⎨ 2 ⎟\n\ ⎝⎩y otherwise⎠ ⎜⎪y for x > 2⎟\n\ ⎜⎪ ⎟\n\ ⎜⎪1 otherwise⎟\n\ ⎝⎩ ⎠\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = -Piecewise((x, x > 0), (y, True))*Piecewise((x/y, x < 2), (y**2, x > 2), (1, True)) ascii_str = \ """\ //x \\\n\ ||- for x < 2|\n\ ||y |\n\ //x for x > 0\ || |\n\ -|< |*|< 2 |\n\ \\\\y otherwise/ ||y for x > 2|\n\ || |\n\ ||1 otherwise|\n\ \\\\ /\ """ ucode_str = \ u("""\ ⎛⎧x ⎞\n\ ⎜⎪─ for x < 2⎟\n\ ⎜⎪y ⎟\n\ ⎛⎧x for x > 0⎞ ⎜⎪ ⎟\n\ -⎜⎨ ⎟⋅⎜⎨ 2 ⎟\n\ ⎝⎩y otherwise⎠ ⎜⎪y for x > 2⎟\n\ ⎜⎪ ⎟\n\ ⎜⎪1 otherwise⎟\n\ ⎝⎩ ⎠\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Piecewise((0, Abs(1/y) < 1), (1, Abs(y) < 1), (y*meijerg(((2, 1), ()), ((), (1, 0)), 1/y), True)) ascii_str = \ """\ / |1| \n\ | 0 for |-| < 1\n\ | |y| \n\ | \n\ < 1 for |y| < 1\n\ | \n\ | __0, 2 /2, 1 | 1\ \n\ |y*/__ | | -| otherwise \n\ \ \\_|2, 2 \ 1, 0 | y/ \ """ ucode_str = \ u("""\ ⎧ │1│ \n\ ⎪ 0 for │─│ < 1\n\ ⎪ │y│ \n\ ⎪ \n\ ⎨ 1 for │y│ < 1\n\ ⎪ \n\ ⎪ ╭─╮0, 2 ⎛2, 1 │ 1⎞ \n\ ⎪y⋅│╶┐ ⎜ │ ─⎟ otherwise \n\ ⎩ ╰─╯2, 2 ⎝ 1, 0 │ y⎠ \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str # XXX: We have to use evaluate=False here because Piecewise._eval_power # denests the power. expr = Pow(Piecewise((x, x > 0), (y, True)), 2, evaluate=False) ascii_str = \ """\ 2\n\ //x for x > 0\ \n\ |< | \n\ \\\\y otherwise/ \ """ ucode_str = \ u("""\ 2\n\ ⎛⎧x for x > 0⎞ \n\ ⎜⎨ ⎟ \n\ ⎝⎩y otherwise⎠ \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_seq(): expr = () ascii_str = \ """\ ()\ """ ucode_str = \ u("""\ ()\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = [] ascii_str = \ """\ []\ """ ucode_str = \ u("""\ []\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = {} expr_2 = {} ascii_str = \ """\ {}\ """ ucode_str = \ u("""\ {}\ """) assert pretty(expr) == ascii_str assert pretty(expr_2) == ascii_str assert upretty(expr) == ucode_str assert upretty(expr_2) == ucode_str expr = (1/x,) ascii_str = \ """\ 1 \n\ (-,)\n\ x \ """ ucode_str = \ u("""\ ⎛1 ⎞\n\ ⎜─,⎟\n\ ⎝x ⎠\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = [x**2, 1/x, x, y, sin(th)**2/cos(ph)**2] ascii_str = \ """\ 2 \n\ 2 1 sin (theta) \n\ [x , -, x, y, -----------]\n\ x 2 \n\ cos (phi) \ """ ucode_str = \ u("""\ ⎡ 2 ⎤\n\ ⎢ 2 1 sin (θ)⎥\n\ ⎢x , ─, x, y, ───────⎥\n\ ⎢ x 2 ⎥\n\ ⎣ cos (φ)⎦\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = (x**2, 1/x, x, y, sin(th)**2/cos(ph)**2) ascii_str = \ """\ 2 \n\ 2 1 sin (theta) \n\ (x , -, x, y, -----------)\n\ x 2 \n\ cos (phi) \ """ ucode_str = \ u("""\ ⎛ 2 ⎞\n\ ⎜ 2 1 sin (θ)⎟\n\ ⎜x , ─, x, y, ───────⎟\n\ ⎜ x 2 ⎟\n\ ⎝ cos (φ)⎠\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Tuple(x**2, 1/x, x, y, sin(th)**2/cos(ph)**2) ascii_str = \ """\ 2 \n\ 2 1 sin (theta) \n\ (x , -, x, y, -----------)\n\ x 2 \n\ cos (phi) \ """ ucode_str = \ u("""\ ⎛ 2 ⎞\n\ ⎜ 2 1 sin (θ)⎟\n\ ⎜x , ─, x, y, ───────⎟\n\ ⎜ x 2 ⎟\n\ ⎝ cos (φ)⎠\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = {x: sin(x)} expr_2 = Dict({x: sin(x)}) ascii_str = \ """\ {x: sin(x)}\ """ ucode_str = \ u("""\ {x: sin(x)}\ """) assert pretty(expr) == ascii_str assert pretty(expr_2) == ascii_str assert upretty(expr) == ucode_str assert upretty(expr_2) == ucode_str expr = {1/x: 1/y, x: sin(x)**2} expr_2 = Dict({1/x: 1/y, x: sin(x)**2}) ascii_str = \ """\ 1 1 2 \n\ {-: -, x: sin (x)}\n\ x y \ """ ucode_str = \ u("""\ ⎧1 1 2 ⎫\n\ ⎨─: ─, x: sin (x)⎬\n\ ⎩x y ⎭\ """) assert pretty(expr) == ascii_str assert pretty(expr_2) == ascii_str assert upretty(expr) == ucode_str assert upretty(expr_2) == ucode_str # There used to be a bug with pretty-printing sequences of even height. expr = [x**2] ascii_str = \ """\ 2 \n\ [x ]\ """ ucode_str = \ u("""\ ⎡ 2⎤\n\ ⎣x ⎦\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = (x**2,) ascii_str = \ """\ 2 \n\ (x ,)\ """ ucode_str = \ u("""\ ⎛ 2 ⎞\n\ ⎝x ,⎠\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Tuple(x**2) ascii_str = \ """\ 2 \n\ (x ,)\ """ ucode_str = \ u("""\ ⎛ 2 ⎞\n\ ⎝x ,⎠\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = {x**2: 1} expr_2 = Dict({x**2: 1}) ascii_str = \ """\ 2 \n\ {x : 1}\ """ ucode_str = \ u("""\ ⎧ 2 ⎫\n\ ⎨x : 1⎬\n\ ⎩ ⎭\ """) assert pretty(expr) == ascii_str assert pretty(expr_2) == ascii_str assert upretty(expr) == ucode_str assert upretty(expr_2) == ucode_str def test_any_object_in_sequence(): # Cf. issue 2207 b1 = Basic() b2 = Basic(Basic()) expr = [b2, b1] assert pretty(expr) == "[Basic(Basic()), Basic()]" assert upretty(expr) == u("[Basic(Basic()), Basic()]") expr = set([b2, b1]) assert pretty(expr) == "set([Basic(), Basic(Basic())])" assert upretty(expr) == u("set([Basic(), Basic(Basic())])") expr = {b2: b1, b1: b2} expr2 = Dict({b2: b1, b1: b2}) assert pretty(expr) == "{Basic(): Basic(Basic()), Basic(Basic()): Basic()}" assert pretty( expr2) == "{Basic(): Basic(Basic()), Basic(Basic()): Basic()}" assert upretty( expr) == u("{Basic(): Basic(Basic()), Basic(Basic()): Basic()}") assert upretty( expr2) == u("{Basic(): Basic(Basic()), Basic(Basic()): Basic()}") def test_pretty_sets(): s = FiniteSet assert pretty(s([x*y, x**2])) == \ """\ 2 \n\ {x , x*y}\ """ assert pretty(s(range(1, 6))) == "{1, 2, 3, 4, 5}" assert pretty(s(range(1, 13))) == "{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}" for s in (frozenset, set): assert pretty(s([x*y, x**2])) == \ """\ %s 2 \n\ %s([x , x*y])\ """ % (" " * len(s.__name__), s.__name__) assert pretty(s(range(1, 6))) == "%s([1, 2, 3, 4, 5])" % s.__name__ assert pretty(s(range(1, 13))) == \ "%s([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])" % s.__name__ def test_pretty_limits(): expr = Limit(x, x, oo) ascii_str = \ """\ lim x\n\ x->oo \ """ ucode_str = \ u("""\ lim x\n\ x->∞ \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Limit(x**2, x, 0) ascii_str = \ """\ 2\n\ lim x \n\ x->0 \ """ ucode_str = \ u("""\ 2\n\ lim x \n\ x->0 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Limit(1/x, x, 0) ascii_str = \ """\ 1\n\ lim -\n\ x->0x\ """ ucode_str = \ u("""\ 1\n\ lim ─\n\ x->0x\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Limit(sin(x)/x, x, 0) ascii_str = \ """\ sin(x)\n\ lim ------\n\ x->0 x \ """ ucode_str = \ u("""\ sin(x)\n\ lim ──────\n\ x->0 x \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_RootOf(): expr = RootOf(x**5 + 11*x - 2, 0) ascii_str = \ """\ / 5 \\\n\ RootOf\\x + 11*x - 2, 0/\ """ ucode_str = \ u("""\ ⎛ 5 ⎞\n\ RootOf⎝x + 11⋅x - 2, 0⎠\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_RootSum(): expr = RootSum(x**5 + 11*x - 2, auto=False) ascii_str = \ """\ / 5 \\\n\ RootSum\\x + 11*x - 2/\ """ ucode_str = \ u("""\ ⎛ 5 ⎞\n\ RootSum⎝x + 11⋅x - 2⎠\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = RootSum(x**5 + 11*x - 2, Lambda(z, exp(z))) ascii_str = \ """\ / 5 z\\\n\ RootSum\\x + 11*x - 2, z -> e /\ """ ucode_str = \ u("""\ ⎛ 5 z⎞\n\ RootSum⎝x + 11⋅x - 2, z ↦ ℯ ⎠\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_GroebnerBasis(): expr = groebner([], x, y) ascii_str = \ """\ GroebnerBasis([], x, y, domain=ZZ, order=lex)\ """ ucode_str = \ u("""\ GroebnerBasis([], x, y, domain=ℤ, order=lex)\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str F = [x**2 - 3*y - x + 1, y**2 - 2*x + y - 1] expr = groebner(F, x, y, order='grlex') ascii_str = \ """\ /[ 2 2 ] \\\n\ GroebnerBasis\\[x - x - 3*y + 1, y - 2*x + y - 1], x, y, domain=ZZ, order=grlex/\ """ ucode_str = \ u("""\ ⎛⎡ 2 2 ⎤ ⎞\n\ GroebnerBasis⎝⎣x - x - 3⋅y + 1, y - 2⋅x + y - 1⎦, x, y, domain=ℤ, order=grlex⎠\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = expr.fglm('lex') ascii_str = \ """\ /[ 2 4 3 2 ] \\\n\ GroebnerBasis\\[2*x - y - y + 1, y + 2*y - 3*y - 16*y + 7], x, y, domain=ZZ, order=lex/\ """ ucode_str = \ u("""\ ⎛⎡ 2 4 3 2 ⎤ ⎞\n\ GroebnerBasis⎝⎣2⋅x - y - y + 1, y + 2⋅y - 3⋅y - 16⋅y + 7⎦, x, y, domain=ℤ, order=lex⎠\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_Boolean(): expr = Not(x, evaluate=False) assert pretty(expr) == "Not(x)" assert upretty(expr) == u("¬x") expr = And(x, y) assert pretty(expr) == "And(x, y)" assert upretty(expr) == u("x ∧ y") expr = Or(x, y) assert pretty(expr) == "Or(x, y)" assert upretty(expr) == u("x ∨ y") syms = symbols('a:f') expr = And(*syms) assert pretty(expr) == "And(a, b, c, d, e, f)" assert upretty(expr) == u("a ∧ b ∧ c ∧ d ∧ e ∧ f") expr = Or(*syms) assert pretty(expr) == "Or(a, b, c, d, e, f)" assert upretty(expr) == u("a ∨ b ∨ c ∨ d ∨ e ∨ f") expr = Xor(x, y, evaluate=False) assert pretty(expr) == "Xor(x, y)" assert upretty(expr) == u("x ⊻ y") expr = Nand(x, y, evaluate=False) assert pretty(expr) == "Nand(x, y)" assert upretty(expr) == u("x ⊼ y") expr = Nor(x, y, evaluate=False) assert pretty(expr) == "Nor(x, y)" assert upretty(expr) == u("x ⊽ y") expr = Implies(x, y, evaluate=False) assert pretty(expr) == "Implies(x, y)" assert upretty(expr) == u("x → y") # don't sort args expr = Implies(y, x, evaluate=False) assert pretty(expr) == "Implies(y, x)" assert upretty(expr) == u("y → x") expr = Equivalent(x, y, evaluate=False) assert pretty(expr) == "Equivalent(x, y)" assert upretty(expr) == u("x ≡ y") expr = Equivalent(y, x, evaluate=False) assert pretty(expr) == "Equivalent(x, y)" assert upretty(expr) == u("x ≡ y") def test_pretty_Domain(): expr = FF(23) assert pretty(expr) == "GF(23)" assert upretty(expr) == u("ℤ₂₃") expr = ZZ assert pretty(expr) == "ZZ" assert upretty(expr) == u("ℤ") expr = QQ assert pretty(expr) == "QQ" assert upretty(expr) == u("ℚ") expr = RR assert pretty(expr) == "RR" assert upretty(expr) == u("ℝ") expr = QQ[x] assert pretty(expr) == "QQ[x]" assert upretty(expr) == u("ℚ[x]") expr = QQ[x, y] assert pretty(expr) == "QQ[x, y]" assert upretty(expr) == u("ℚ[x, y]") expr = ZZ.frac_field(x) assert pretty(expr) == "ZZ(x)" assert upretty(expr) == u("ℤ(x)") expr = ZZ.frac_field(x, y) assert pretty(expr) == "ZZ(x, y)" assert upretty(expr) == u("ℤ(x, y)") expr = QQ.poly_ring(x, y, order=grlex) assert pretty(expr) == "QQ[x, y, order=grlex]" assert upretty(expr) == u("ℚ[x, y, order=grlex]") expr = QQ.poly_ring(x, y, order=ilex) assert pretty(expr) == "QQ[x, y, order=ilex]" assert upretty(expr) == u("ℚ[x, y, order=ilex]") def test_pretty_prec(): assert xpretty(S("0.3"), full_prec=True) == "0.300000000000000" assert xpretty(S("0.3"), full_prec="auto") == "0.300000000000000" assert xpretty(S("0.3"), full_prec=False) == "0.3" assert xpretty(S("0.3")*x, full_prec=True, use_unicode=False) in [ "0.300000000000000*x", "x*0.300000000000000" ] assert xpretty(S("0.3")*x, full_prec="auto", use_unicode=False) in [ "0.3*x", "x*0.3" ] assert xpretty(S("0.3")*x, full_prec=False, use_unicode=False) in [ "0.3*x", "x*0.3" ] def test_pprint(): import sys from sympy.core.compatibility import StringIO fd = StringIO() sso = sys.stdout sys.stdout = fd try: pprint(pi, use_unicode=False) finally: sys.stdout = sso assert fd.getvalue() == 'pi\n' def test_pretty_class(): """Test that the printer dispatcher correctly handles classes.""" class C: pass # C has no .__class__ and this was causing problems class D(object): pass assert pretty( C ) == str( C ) assert pretty( D ) == str( D ) def test_pretty_no_wrap_line(): huge_expr = 0 for i in range(20): huge_expr += i*sin(i + x) assert xpretty(huge_expr ).find('\n') != -1 assert xpretty(huge_expr, wrap_line=False).find('\n') == -1 def test_settings(): raises(TypeError, lambda: pretty(S(4), method="garbage")) def test_pretty_sum(): from sympy.abc import x, a, b, k, m, n expr = Sum(k**k, (k, 0, n)) ascii_str = \ """\ n \n\ ___ \n\ \\ ` \n\ \\ k\n\ / k \n\ /__, \n\ k = 0 \ """ ucode_str = \ u("""\ n \n\ ___ \n\ ╲ \n\ ╲ k\n\ ╱ k \n\ ╱ \n\ ‾‾‾ \n\ k = 0 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Sum(k**(Integral(x**n, (x, -oo, oo))), (k, 0, n**n)) ascii_str = \ """\ n \n\ n \n\ ______ \n\ \\ ` \n\ \\ oo \n\ \\ / \n\ \\ | \n\ \\ | n \n\ ) | x dx\n\ / | \n\ / / \n\ / -oo \n\ / k \n\ /_____, \n\ k = 0 \ """ ucode_str = \ u("""\ n \n\ n \n\ ______ \n\ ╲ \n\ ╲ ∞ \n\ ╲ ⌠ \n\ ╲ ⎮ n \n\ ╲ ⎮ x dx\n\ ╱ ⌡ \n\ ╱ -∞ \n\ ╱ k \n\ ╱ \n\ ╱ \n\ ‾‾‾‾‾‾ \n\ k = 0 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Sum(k**( Integral(x**n, (x, -oo, oo))), (k, 0, Integral(x**x, (x, -oo, oo)))) ascii_str = \ """\ oo \n\ / \n\ | \n\ | x \n\ | x dx \n\ | \n\ / \n\ -oo \n\ ______ \n\ \\ ` \n\ \\ oo \n\ \\ / \n\ \\ | \n\ \\ | n \n\ ) | x dx\n\ / | \n\ / / \n\ / -oo \n\ / k \n\ /_____, \n\ k = 0 \ """ ucode_str = \ u("""\ ∞ \n\ ⌠ \n\ ⎮ x \n\ ⎮ x dx \n\ ⌡ \n\ -∞ \n\ ______ \n\ ╲ \n\ ╲ ∞ \n\ ╲ ⌠ \n\ ╲ ⎮ n \n\ ╲ ⎮ x dx\n\ ╱ ⌡ \n\ ╱ -∞ \n\ ╱ k \n\ ╱ \n\ ╱ \n\ ‾‾‾‾‾‾ \n\ k = 0 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Sum(k**(Integral(x**n, (x, -oo, oo))), ( k, x + n + x**2 + n**2 + (x/n) + (1/x), Integral(x**x, (x, -oo, oo)))) ascii_str = \ """\ oo \n\ / \n\ | \n\ | x \n\ | x dx \n\ | \n\ / \n\ -oo \n\ ______ \n\ \ ` \n\ \ oo \n\ \ / \n\ \ | \n\ \ | n \n\ ) | x dx\n\ / | \n\ / / \n\ / -oo \n\ / k \n\ /_____, \n\ 2 2 1 x \n\ k = n + n + x + x + - + - \n\ x n \ """ ucode_str = \ u("""\ ∞ \n\ ⌠ \n\ ⎮ x \n\ ⎮ x dx \n\ ⌡ \n\ -∞ \n\ ______ \n\ ╲ \n\ ╲ ∞ \n\ ╲ ⌠ \n\ ╲ ⎮ n \n\ ╲ ⎮ x dx\n\ ╱ ⌡ \n\ ╱ -∞ \n\ ╱ k \n\ ╱ \n\ ╱ \n\ ‾‾‾‾‾‾ \n\ 2 2 1 x \n\ k = n + n + x + x + ─ + ─ \n\ x n \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Sum(k**( Integral(x**n, (x, -oo, oo))), (k, 0, x + n + x**2 + n**2 + (x/n) + (1/x))) ascii_str = \ """\ 2 2 1 x \n\ n + n + x + x + - + - \n\ x n \n\ ______ \n\ \ ` \n\ \ oo \n\ \ / \n\ \ | \n\ \ | n \n\ ) | x dx\n\ / | \n\ / / \n\ / -oo \n\ / k \n\ /_____, \n\ k = 0 \ """ ucode_str = \ u("""\ 2 2 1 x \n\ n + n + x + x + ─ + ─ \n\ x n \n\ ______ \n\ ╲ \n\ ╲ ∞ \n\ ╲ ⌠ \n\ ╲ ⎮ n \n\ ╲ ⎮ x dx\n\ ╱ ⌡ \n\ ╱ -∞ \n\ ╱ k \n\ ╱ \n\ ╱ \n\ ‾‾‾‾‾‾ \n\ k = 0 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Sum(x, (x, 0, oo)) ascii_str = \ """\ oo \n\ __ \n\ \\ ` \n\ ) x\n\ /_, \n\ x = 0 \ """ ucode_str = \ u("""\ ∞ \n\ ___ \n\ ╲ \n\ ╲ x\n\ ╱ \n\ ╱ \n\ ‾‾‾ \n\ x = 0 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Sum(x**2, (x, 0, oo)) ascii_str = \ u("""\ oo \n\ ___ \n\ \\ ` \n\ \\ 2\n\ / x \n\ /__, \n\ x = 0 \ """) ucode_str = \ u("""\ ∞ \n\ ___ \n\ ╲ \n\ ╲ 2\n\ ╱ x \n\ ╱ \n\ ‾‾‾ \n\ x = 0 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Sum(x/2, (x, 0, oo)) ascii_str = \ """\ oo \n\ ___ \n\ \\ ` \n\ \\ x\n\ ) -\n\ / 2\n\ /__, \n\ x = 0 \ """ ucode_str = \ u("""\ ∞ \n\ ____ \n\ ╲ \n\ ╲ x\n\ ╲ ─\n\ ╱ 2\n\ ╱ \n\ ╱ \n\ ‾‾‾‾ \n\ x = 0 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Sum(x**3/2, (x, 0, oo)) ascii_str = \ """\ oo \n\ ____ \n\ \\ ` \n\ \\ 3\n\ \\ x \n\ / --\n\ / 2 \n\ /___, \n\ x = 0 \ """ ucode_str = \ u("""\ ∞ \n\ ____ \n\ ╲ \n\ ╲ 3\n\ ╲ x \n\ ╱ ──\n\ ╱ 2 \n\ ╱ \n\ ‾‾‾‾ \n\ x = 0 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Sum((x**3*y**(x/2))**n, (x, 0, oo)) ascii_str = \ """\ oo \n\ ____ \n\ \\ ` \n\ \\ n\n\ \\ / x\\ \n\ ) | -| \n\ / | 3 2| \n\ / \\x *y / \n\ /___, \n\ x = 0 \ """ ucode_str = \ u("""\ ∞ \n\ _____ \n\ ╲ \n\ ╲ n\n\ ╲ ⎛ x⎞ \n\ ╲ ⎜ ─⎟ \n\ ╱ ⎜ 3 2⎟ \n\ ╱ ⎝x ⋅y ⎠ \n\ ╱ \n\ ╱ \n\ ‾‾‾‾‾ \n\ x = 0 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Sum(1/x**2, (x, 0, oo)) ascii_str = \ """\ oo \n\ ____ \n\ \\ ` \n\ \\ 1 \n\ \\ --\n\ / 2\n\ / x \n\ /___, \n\ x = 0 \ """ ucode_str = \ u("""\ ∞ \n\ ____ \n\ ╲ \n\ ╲ 1 \n\ ╲ ──\n\ ╱ 2\n\ ╱ x \n\ ╱ \n\ ‾‾‾‾ \n\ x = 0 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Sum(1/y**(a/b), (x, 0, oo)) ascii_str = \ """\ oo \n\ ____ \n\ \\ ` \n\ \\ -a \n\ \\ ---\n\ / b \n\ / y \n\ /___, \n\ x = 0 \ """ ucode_str = \ u("""\ ∞ \n\ ____ \n\ ╲ \n\ ╲ -a \n\ ╲ ───\n\ ╱ b \n\ ╱ y \n\ ╱ \n\ ‾‾‾‾ \n\ x = 0 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Sum(1/y**(a/b), (x, 0, oo), (y, 1, 2)) ascii_str = \ """\ 2 oo \n\ ____ ____ \n\ \\ ` \\ ` \n\ \\ \\ -a\n\ \\ \\ --\n\ / / b \n\ / / y \n\ /___, /___, \n\ y = 1 x = 0 \ """ ucode_str = \ u("""\ 2 ∞ \n\ ____ ____ \n\ ╲ ╲ \n\ ╲ ╲ -a\n\ ╲ ╲ ──\n\ ╱ ╱ b \n\ ╱ ╱ y \n\ ╱ ╱ \n\ ‾‾‾‾ ‾‾‾‾ \n\ y = 1 x = 0 \ """) expr = Sum(1/(1 + 1/( 1 + 1/k)) + 1, (k, 111, 1 + 1/n), (k, 1/(1 + m), oo)) + 1/(1 + 1/k) ascii_str = \ """\ 1 \n\ 1 + - \n\ oo n \n\ _____ _____ \n\ \\ ` \\ ` \n\ \\ \\ / 1 \\ \n\ \\ \\ |1 + ---------| \n\ \\ \\ | 1 | 1 \n\ ) ) | 1 + -----| + -----\n\ / / | 1| 1\n\ / / | 1 + -| 1 + -\n\ / / \\ k/ k\n\ /____, /____, \n\ 1 k = 111 \n\ k = ----- \n\ m + 1 \ """ ucode_str = \ u("""\ 1 \n\ 1 + ─ \n\ ∞ n \n\ ______ ______ \n\ ╲ ╲ \n\ ╲ ╲ ⎛ 1 ⎞ \n\ ╲ ╲ ⎜1 + ─────────⎟ \n\ ╲ ╲ ⎜ 1 ⎟ \n\ ╲ ╲ ⎜ 1 + ─────⎟ 1 \n\ ╱ ╱ ⎜ 1⎟ + ─────\n\ ╱ ╱ ⎜ 1 + ─⎟ 1\n\ ╱ ╱ ⎝ k⎠ 1 + ─\n\ ╱ ╱ k\n\ ╱ ╱ \n\ ‾‾‾‾‾‾ ‾‾‾‾‾‾ \n\ 1 k = 111 \n\ k = ───── \n\ m + 1 \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_units(): expr = joule ascii_str = \ """\ 2\n\ kg*m \n\ -----\n\ 2 \n\ s \ """ unicode_str = \ u("""\ 2\n\ kg⋅m \n\ ─────\n\ 2 \n\ s \ """) assert upretty(expr) == unicode_str assert pretty(expr) == ascii_str def test_pretty_Subs(): f = Function('f') expr = Subs(f(x), x, ph**2) ascii_str = \ """\ (f(x))| 2\n\ |x=phi \ """ unicode_str = \ u("""\ (f(x))│ 2\n\ │x=φ \ """) assert pretty(expr) == ascii_str assert upretty(expr) == unicode_str expr = Subs(f(x).diff(x), x, 0) ascii_str = \ """\ /d \\| \n\ |--(f(x))|| \n\ \\dx /|x=0\ """ unicode_str = \ u("""\ ⎛d ⎞│ \n\ ⎜──(f(x))⎟│ \n\ ⎝dx ⎠│x=0\ """) assert pretty(expr) == ascii_str assert upretty(expr) == unicode_str expr = Subs(f(x).diff(x)/y, (x, y), (0, Rational(1, 2))) ascii_str = \ """\ /d \\| \n\ |--(f(x))|| \n\ |dx || \n\ |--------|| \n\ \\ y /|x=0, y=1/2\ """ unicode_str = \ u("""\ ⎛d ⎞│ \n\ ⎜──(f(x))⎟│ \n\ ⎜dx ⎟│ \n\ ⎜────────⎟│ \n\ ⎝ y ⎠│x=0, y=1/2\ """) assert pretty(expr) == ascii_str assert upretty(expr) == unicode_str def test_gammas(): assert upretty(lowergamma(x, y)) == u("γ(x, y)") assert upretty(uppergamma(x, y)) == u("Γ(x, y)") assert xpretty(gamma(x), use_unicode=True) == u('Γ(x)') def test_hyper(): expr = hyper((), (), z) ucode_str = \ u("""\ ┌─ ⎛ │ ⎞\n\ ├─ ⎜ │ z⎟\n\ 0╵ 0 ⎝ │ ⎠\ """) ascii_str = \ """\ _ \n\ |_ / | \\\n\ | | | z|\n\ 0 0 \\ | /\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = hyper((), (1,), x) ucode_str = \ u("""\ ┌─ ⎛ │ ⎞\n\ ├─ ⎜ │ x⎟\n\ 0╵ 1 ⎝1 │ ⎠\ """) ascii_str = \ """\ _ \n\ |_ / | \\\n\ | | | x|\n\ 0 1 \\1 | /\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = hyper([2], [1], x) ucode_str = \ u("""\ ┌─ ⎛2 │ ⎞\n\ ├─ ⎜ │ x⎟\n\ 1╵ 1 ⎝1 │ ⎠\ """) ascii_str = \ """\ _ \n\ |_ /2 | \\\n\ | | | x|\n\ 1 1 \\1 | /\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = hyper((pi/3, -2*k), (3, 4, 5, -3), x) ucode_str = \ u("""\ ⎛ π │ ⎞\n\ ┌─ ⎜ ─, -2⋅k │ ⎟\n\ ├─ ⎜ 3 │ x⎟\n\ 2╵ 4 ⎜ │ ⎟\n\ ⎝3, 4, 5, -3 │ ⎠\ """) ascii_str = \ """\ \n\ _ / pi | \\\n\ |_ | --, -2*k | |\n\ | | 3 | x|\n\ 2 4 | | |\n\ \\3, 4, 5, -3 | /\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = hyper((pi, S('2/3'), -2*k), (3, 4, 5, -3), x**2) ucode_str = \ u("""\ ┌─ ⎛π, 2/3, -2⋅k │ 2⎞\n\ ├─ ⎜ │ x ⎟\n\ 3╵ 4 ⎝3, 4, 5, -3 │ ⎠\ """) ascii_str = \ """\ _ \n\ |_ /pi, 2/3, -2*k | 2\\\n\ | | | x |\n\ 3 4 \\ 3, 4, 5, -3 | /\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = hyper([1, 2], [3, 4], 1/(1/(1/(1/x + 1) + 1) + 1)) ucode_str = \ u("""\ ⎛ │ 1 ⎞\n\ ⎜ │ ─────────────⎟\n\ ⎜ │ 1 ⎟\n\ ┌─ ⎜1, 2 │ 1 + ─────────⎟\n\ ├─ ⎜ │ 1 ⎟\n\ 2╵ 2 ⎜3, 4 │ 1 + ─────⎟\n\ ⎜ │ 1⎟\n\ ⎜ │ 1 + ─⎟\n\ ⎝ │ x⎠\ """) ascii_str = \ """\ \n\ / | 1 \\\n\ | | -------------|\n\ _ | | 1 |\n\ |_ |1, 2 | 1 + ---------|\n\ | | | 1 |\n\ 2 2 |3, 4 | 1 + -----|\n\ | | 1|\n\ | | 1 + -|\n\ \\ | x/\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_meijerg(): expr = meijerg([pi, pi, x], [1], [0, 1], [1, 2, 3], z) ucode_str = \ u("""\ ╭─╮2, 3 ⎛π, π, x 1 │ ⎞\n\ │╶┐ ⎜ │ z⎟\n\ ╰─╯4, 5 ⎝ 0, 1 1, 2, 3 │ ⎠\ """) ascii_str = \ """\ __2, 3 /pi, pi, x 1 | \\\n\ /__ | | z|\n\ \_|4, 5 \\ 0, 1 1, 2, 3 | /\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = meijerg([1, pi/7], [2, pi, 5], [], [], z**2) ucode_str = \ u("""\ ⎛ π │ ⎞\n\ ╭─╮0, 2 ⎜1, ─ 2, π, 5 │ 2⎟\n\ │╶┐ ⎜ 7 │ z ⎟\n\ ╰─╯5, 0 ⎜ │ ⎟\n\ ⎝ │ ⎠\ """) ascii_str = \ """\ / pi | \\\n\ __0, 2 |1, -- 2, pi, 5 | 2|\n\ /__ | 7 | z |\n\ \_|5, 0 | | |\n\ \\ | /\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str ucode_str = \ u("""\ ╭─╮ 1, 10 ⎛1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1 │ ⎞\n\ │╶┐ ⎜ │ z⎟\n\ ╰─╯11, 2 ⎝ 1 1 │ ⎠\ """) ascii_str = \ """\ __ 1, 10 /1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1 | \\\n\ /__ | | z|\n\ \_|11, 2 \\ 1 1 | /\ """ expr = meijerg([1]*10, [1], [1], [1], z) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = meijerg([1, 2, ], [4, 3], [3], [4, 5], 1/(1/(1/(1/x + 1) + 1) + 1)) ucode_str = \ u("""\ ⎛ │ 1 ⎞\n\ ⎜ │ ─────────────⎟\n\ ⎜ │ 1 ⎟\n\ ╭─╮1, 2 ⎜1, 2 4, 3 │ 1 + ─────────⎟\n\ │╶┐ ⎜ │ 1 ⎟\n\ ╰─╯4, 3 ⎜ 3 4, 5 │ 1 + ─────⎟\n\ ⎜ │ 1⎟\n\ ⎜ │ 1 + ─⎟\n\ ⎝ │ x⎠\ """) ascii_str = \ """\ / | 1 \\\n\ | | -------------|\n\ | | 1 |\n\ __1, 2 |1, 2 4, 3 | 1 + ---------|\n\ /__ | | 1 |\n\ \_|4, 3 | 3 4, 5 | 1 + -----|\n\ | | 1|\n\ | | 1 + -|\n\ \\ | x/\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Integral(expr, x) ucode_str = \ u("""\ ⌠ \n\ ⎮ ⎛ │ 1 ⎞ \n\ ⎮ ⎜ │ ─────────────⎟ \n\ ⎮ ⎜ │ 1 ⎟ \n\ ⎮ ╭─╮1, 2 ⎜1, 2 4, 3 │ 1 + ─────────⎟ \n\ ⎮ │╶┐ ⎜ │ 1 ⎟ dx\n\ ⎮ ╰─╯4, 3 ⎜ 3 4, 5 │ 1 + ─────⎟ \n\ ⎮ ⎜ │ 1⎟ \n\ ⎮ ⎜ │ 1 + ─⎟ \n\ ⎮ ⎝ │ x⎠ \n\ ⌡ \ """) ascii_str = \ """\ / \n\ | \n\ | / | 1 \\ \n\ | | | -------------| \n\ | | | 1 | \n\ | __1, 2 |1, 2 4, 3 | 1 + ---------| \n\ | /__ | | 1 | dx\n\ | \_|4, 3 | 3 4, 5 | 1 + -----| \n\ | | | 1| \n\ | | | 1 + -| \n\ | \\ | x/ \n\ | \n\ / \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_noncommutative(): A, B, C = symbols('A,B,C', commutative=False) expr = A*B*C**-1 ascii_str = \ """\ -1\n\ A*B*C \ """ ucode_str = \ u("""\ -1\n\ A⋅B⋅C \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = C**-1*A*B ascii_str = \ """\ -1 \n\ C *A*B\ """ ucode_str = \ u("""\ -1 \n\ C ⋅A⋅B\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = A*C**-1*B ascii_str = \ """\ -1 \n\ A*C *B\ """ ucode_str = \ u("""\ -1 \n\ A⋅C ⋅B\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = A*C**-1*B/x ascii_str = \ """\ -1 \n\ A*C *B\n\ -------\n\ x \ """ ucode_str = \ u("""\ -1 \n\ A⋅C ⋅B\n\ ───────\n\ x \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_special_functions(): x, y = symbols("x y") # atan2 expr = atan2(y/sqrt(200), sqrt(x)) ascii_str = \ """\ / ___ \\\n\ |\\/ 2 *y ___|\n\ atan2|-------, \\/ x |\n\ \\ 20 /\ """ ucode_str = \ u("""\ ⎛ ___ ⎞\n\ ⎜╲╱ 2 ⋅y ___⎟\n\ atan2⎜───────, ╲╱ x ⎟\n\ ⎝ 20 ⎠\ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_geometry(): e = Segment((0, 1), (0, 2)) assert pretty(e) == 'Segment(Point(0, 1), Point(0, 2))' e = Ray((1, 1), angle=4.05*pi) assert pretty(e) == 'Ray(Point(1, 1), Point(2, tan(pi/20) + 1))' def test_expint(): expr = Ei(x) string = 'Ei(x)' assert pretty(expr) == string assert upretty(expr) == string expr = expint(1, z) ucode_str = u("E₁(z)") ascii_str = "expint(1, z)" assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str assert pretty(Shi(x)) == 'Shi(x)' assert pretty(Si(x)) == 'Si(x)' assert pretty(Ci(x)) == 'Ci(x)' assert pretty(Chi(x)) == 'Chi(x)' assert upretty(Shi(x)) == 'Shi(x)' assert upretty(Si(x)) == 'Si(x)' assert upretty(Ci(x)) == 'Ci(x)' assert upretty(Chi(x)) == 'Chi(x)' def test_elliptic_functions(): ascii_str = \ """\ / 1 \\\n\ K|-----|\n\ \z + 1/\ """ ucode_str = \ u("""\ ⎛ 1 ⎞\n\ K⎜─────⎟\n\ ⎝z + 1⎠\ """) expr = elliptic_k(1/(z + 1)) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str ascii_str = \ """\ / | 1 \\\n\ F|1|-----|\n\ \ |z + 1/\ """ ucode_str = \ u("""\ ⎛ │ 1 ⎞\n\ F⎜1│─────⎟\n\ ⎝ │z + 1⎠\ """) expr = elliptic_f(1, 1/(1 + z)) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str ascii_str = \ """\ / 1 \\\n\ E|-----|\n\ \z + 1/\ """ ucode_str = \ u("""\ ⎛ 1 ⎞\n\ E⎜─────⎟\n\ ⎝z + 1⎠\ """) expr = elliptic_e(1/(z + 1)) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str ascii_str = \ """\ / | 1 \\\n\ E|1|-----|\n\ \ |z + 1/\ """ ucode_str = \ u("""\ ⎛ │ 1 ⎞\n\ E⎜1│─────⎟\n\ ⎝ │z + 1⎠\ """) expr = elliptic_e(1, 1/(1 + z)) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str ascii_str = \ """\ / |4\\\n\ Pi|3|-|\n\ \ |x/\ """ ucode_str = \ u("""\ ⎛ │4⎞\n\ Π⎜3│─⎟\n\ ⎝ │x⎠\ """) expr = elliptic_pi(3, 4/x) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str ascii_str = \ """\ / 4| \\\n\ Pi|3; -|6|\n\ \ x| /\ """ ucode_str = \ u("""\ ⎛ 4│ ⎞\n\ Π⎜3; ─│6⎟\n\ ⎝ x│ ⎠\ """) expr = elliptic_pi(3, 4/x, 6) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_RandomDomain(): from sympy.stats import Normal, Die, Exponential, pspace, where X = Normal('x1', 0, 1) assert upretty(where(X > 0)) == u("Domain: x₁ > 0") D = Die('d1', 6) assert upretty(where(D > 4)) == u('Domain: d₁ = 5 ∨ d₁ = 6') A = Exponential('a', 1) B = Exponential('b', 1) assert upretty(pspace(Tuple(A, B)).domain) == u('Domain: a ≥ 0 ∧ b ≥ 0') def test_PrettyPoly(): F = QQ.frac_field(x, y) R = QQ.poly_ring(x, y) expr = F.convert(x/(x + y)) assert pretty(expr) == "x/(x + y)" assert upretty(expr) == u("x/(x + y)") expr = R.convert(x + y) assert pretty(expr) == "x + y" assert upretty(expr) == u("x + y") def test_issue_3186(): assert pretty(Pow(2, -5, evaluate=False)) == '1 \n--\n 5\n2 ' assert pretty(Pow(x, (1/pi))) == 'pi___\n\\/ x ' def test_issue_3260(): assert pretty(Integral(x**2, x)**2) == \ """\ 2 / / \ \n\ | | | \n\ | | 2 | \n\ | | x dx| \n\ | | | \n\ \/ / \ """ assert upretty(Integral(x**2, x)**2) == \ u("""\ 2 ⎛⌠ ⎞ \n\ ⎜⎮ 2 ⎟ \n\ ⎜⎮ x dx⎟ \n\ ⎝⌡ ⎠ \ """) assert pretty(Sum(x**2, (x, 0, 1))**2) == \ """\ 2 / 1 \\ \n\ | ___ | \n\ | \\ ` | \n\ | \\ 2| \n\ | / x | \n\ | /__, | \n\ \\x = 0 / \ """ assert upretty(Sum(x**2, (x, 0, 1))**2) == \ u("""\ 2 ⎛ 1 ⎞ \n\ ⎜ ___ ⎟ \n\ ⎜ ╲ ⎟ \n\ ⎜ ╲ 2⎟ \n\ ⎜ ╱ x ⎟ \n\ ⎜ ╱ ⎟ \n\ ⎜ ‾‾‾ ⎟ \n\ ⎝x = 0 ⎠ \ """) assert pretty(Product(x**2, (x, 1, 2))**2) == \ """\ 2 / 2 \\ \n\ |______ | \n\ || | 2| \n\ || | x | \n\ || | | \n\ \\x = 1 / \ """ assert upretty(Product(x**2, (x, 1, 2))**2) == \ u("""\ 2 ⎛ 2 ⎞ \n\ ⎜┬────┬ ⎟ \n\ ⎜│ │ 2⎟ \n\ ⎜│ │ x ⎟ \n\ ⎜│ │ ⎟ \n\ ⎝x = 1 ⎠ \ """) f = Function('f') assert pretty(Derivative(f(x), x)**2) == \ """\ 2 /d \\ \n\ |--(f(x))| \n\ \\dx / \ """ assert upretty(Derivative(f(x), x)**2) == \ u("""\ 2 ⎛d ⎞ \n\ ⎜──(f(x))⎟ \n\ ⎝dx ⎠ \ """) def test_issue_3640(): ascii_str = \ """\ 1 \n\ -----\n\ ___\n\ \/ x \ """ ucode_str = \ u("""\ 1 \n\ ─────\n\ ___\n\ ╲╱ x \ """) assert pretty(1/sqrt(x)) == ascii_str assert upretty(1/sqrt(x)) == ucode_str def test_complicated_symbol_unchanged(): for symb_name in ["dexpr2_d1tau", "dexpr2^d1tau"]: assert pretty(Symbol(symb_name)) == symb_name def test_categories(): from sympy.categories import (Object, IdentityMorphism, NamedMorphism, Category, Diagram, DiagramGrid) A1 = Object("A1") A2 = Object("A2") A3 = Object("A3") f1 = NamedMorphism(A1, A2, "f1") f2 = NamedMorphism(A2, A3, "f2") id_A1 = IdentityMorphism(A1) K1 = Category("K1") assert pretty(A1) == "A1" assert upretty(A1) == u("A₁") assert pretty(f1) == "f1:A1-->A2" assert upretty(f1) == u("f₁:A₁——▶A₂") assert pretty(id_A1) == "id:A1-->A1" assert upretty(id_A1) == u("id:A₁——▶A₁") assert pretty(f2*f1) == "f2*f1:A1-->A3" assert upretty(f2*f1) == u("f₂∘f₁:A₁——▶A₃") assert pretty(K1) == "K1" assert upretty(K1) == u("K₁") # Test how diagrams are printed. d = Diagram() assert pretty(d) == "EmptySet()" assert upretty(d) == u("∅") d = Diagram({f1: "unique", f2: S.EmptySet}) assert pretty(d) == "{f2*f1:A1-->A3: EmptySet(), id:A1-->A1: " \ "EmptySet(), id:A2-->A2: EmptySet(), id:A3-->A3: " \ "EmptySet(), f1:A1-->A2: {unique}, f2:A2-->A3: EmptySet()}" assert upretty(d) == u("{f₂∘f₁:A₁——▶A₃: ∅, id:A₁——▶A₁: ∅, " \ "id:A₂——▶A₂: ∅, id:A₃——▶A₃: ∅, f₁:A₁——▶A₂: {unique}, f₂:A₂——▶A₃: ∅}") d = Diagram({f1: "unique", f2: S.EmptySet}, {f2 * f1: "unique"}) assert pretty(d) == "{f2*f1:A1-->A3: EmptySet(), id:A1-->A1: " \ "EmptySet(), id:A2-->A2: EmptySet(), id:A3-->A3: " \ "EmptySet(), f1:A1-->A2: {unique}, f2:A2-->A3: EmptySet()}" \ " ==> {f2*f1:A1-->A3: {unique}}" assert upretty(d) == u("{f₂∘f₁:A₁——▶A₃: ∅, id:A₁——▶A₁: ∅, id:A₂——▶A₂: " \ "∅, id:A₃——▶A₃: ∅, f₁:A₁——▶A₂: {unique}, f₂:A₂——▶A₃: ∅}" \ " ══▶ {f₂∘f₁:A₁——▶A₃: {unique}}") grid = DiagramGrid(d) assert pretty(grid) == "A1 A2\n \nA3 " assert upretty(grid) == u("A₁ A₂\n \nA₃ ") def test_PrettyModules(): R = QQ.old_poly_ring(x, y) F = R.free_module(2) M = F.submodule([x, y], [1, x**2]) ucode_str = \ u("""\ 2\n\ ℚ[x, y] \ """) ascii_str = \ """\ 2\n\ QQ[x, y] \ """ assert upretty(F) == ucode_str assert pretty(F) == ascii_str ucode_str = \ u("""\ ╱ ⎡ 2⎤╲\n\ ╲[x, y], ⎣1, x ⎦╱\ """) ascii_str = \ """\ 2 \n\ <[x, y], [1, x ]>\ """ assert upretty(M) == ucode_str assert pretty(M) == ascii_str I = R.ideal(x**2, y) ucode_str = \ u("""\ ╱ 2 ╲\n\ ╲x , y╱\ """) ascii_str = \ """\ 2 \n\ \ """ assert upretty(I) == ucode_str assert pretty(I) == ascii_str Q = F / M ucode_str = \ u("""\ 2 \n\ ℚ[x, y] \n\ ─────────────────\n\ ╱ ⎡ 2⎤╲\n\ ╲[x, y], ⎣1, x ⎦╱\ """) ascii_str = \ """\ 2 \n\ QQ[x, y] \n\ -----------------\n\ 2 \n\ <[x, y], [1, x ]>\ """ assert upretty(Q) == ucode_str assert pretty(Q) == ascii_str ucode_str = \ u("""\ ╱⎡ 3⎤ ╲\n\ │⎢ x ⎥ ╱ ⎡ 2⎤╲ ╱ ⎡ 2⎤╲│\n\ │⎢1, ──⎥ + ╲[x, y], ⎣1, x ⎦╱, [2, y] + ╲[x, y], ⎣1, x ⎦╱│\n\ ╲⎣ 2 ⎦ ╱\ """) ascii_str = \ """\ 3 \n\ x 2 2 \n\ <[1, --] + <[x, y], [1, x ]>, [2, y] + <[x, y], [1, x ]>>\n\ 2 \ """ def test_QuotientRing(): R = QQ.old_poly_ring(x)/[x**2 + 1] ucode_str = \ u("""\ ℚ[x] \n\ ────────\n\ ╱ 2 ╲\n\ ╲x + 1╱\ """) ascii_str = \ """\ QQ[x] \n\ --------\n\ 2 \n\ \ """ assert upretty(R) == ucode_str assert pretty(R) == ascii_str ucode_str = \ u("""\ ╱ 2 ╲\n\ 1 + ╲x + 1╱\ """) ascii_str = \ """\ 2 \n\ 1 + \ """ assert upretty(R.one) == ucode_str assert pretty(R.one) == ascii_str def test_Homomorphism(): from sympy.polys.agca import homomorphism R = QQ.old_poly_ring(x) expr = homomorphism(R.free_module(1), R.free_module(1), [0]) ucode_str = \ u("""\ 1 1\n\ [0] : ℚ[x] ──> ℚ[x] \ """) ascii_str = \ """\ 1 1\n\ [0] : QQ[x] --> QQ[x] \ """ assert upretty(expr) == ucode_str assert pretty(expr) == ascii_str expr = homomorphism(R.free_module(2), R.free_module(2), [0, 0]) ucode_str = \ u("""\ ⎡0 0⎤ 2 2\n\ ⎢ ⎥ : ℚ[x] ──> ℚ[x] \n\ ⎣0 0⎦ \ """) ascii_str = \ """\ [0 0] 2 2\n\ [ ] : QQ[x] --> QQ[x] \n\ [0 0] \ """ assert upretty(expr) == ucode_str assert pretty(expr) == ascii_str expr = homomorphism(R.free_module(1), R.free_module(1) / [[x]], [0]) ucode_str = \ u("""\ 1\n\ 1 ℚ[x] \n\ [0] : ℚ[x] ──> ─────\n\ <[x]>\ """) ascii_str = \ """\ 1\n\ 1 QQ[x] \n\ [0] : QQ[x] --> ------\n\ <[x]> \ """ assert upretty(expr) == ucode_str assert pretty(expr) == ascii_str def test_Tr(): A, B = symbols('A B', commutative=False) t = Tr(A*B) assert pretty(t) == r'Tr(A*B)' assert upretty(t) == u('Tr(A⋅B)') def test_pretty_Add(): eq = Mul(-2, x - 2, evaluate=False) + 5 assert pretty(eq) == '-2*(x - 2) + 5' sympy-0.7.4.1/sympy/printing/pretty/tests/__init__.py0000644000175000017500000000000012253362407023047 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/printing/pretty/__init__.py0000644000175000017500000000033612253362407021721 0ustar georgeskgeorgesk"""ASCII-ART 2D pretty-printer""" from .pretty import (pretty, pretty_print, pprint, pprint_use_unicode, pprint_try_use_unicode, pager_print) # if unicode output is available -- let's use it pprint_try_use_unicode() sympy-0.7.4.1/sympy/printing/codeprinter.py0000644000175000017500000002322712253362407021155 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import C, Add, Mul, Pow, S from sympy.core.compatibility import default_sort_key from sympy.core.mul import _keep_coeff from sympy.printing.str import StrPrinter from sympy.printing.precedence import precedence class AssignmentError(Exception): """ Raised if an assignment variable for a loop is missing. """ pass class CodePrinter(StrPrinter): """ The base class for code-printing subclasses. """ _operators = { 'and': '&&', 'or': '||', 'not': '!', } 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 from sympy.tensor import get_contraction_structure 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("FIXME: lhs present in rhs,\ this is undefined in CCodePrinter") 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): from sympy.tensor import get_indices, get_contraction_structure 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_And(self, expr): PREC = precedence(expr) return (" %s " % self._operators['and']).join(self.parenthesize(a, PREC) for a in sorted(expr.args, key=default_sort_key)) def _print_Or(self, expr): PREC = precedence(expr) return (" %s " % self._operators['or']).join(self.parenthesize(a, PREC) for a in sorted(expr.args, key=default_sort_key)) def _print_Xor(self, expr): if self._operators.get('xor') is None: return self._print_not_supported(expr) PREC = precedence(expr) return (" %s " % self._operators['xor']).join(self.parenthesize(a, PREC) for a in expr.args) def _print_Equivalent(self, expr): if self._operators.get('equivalent') is None: return self._print_not_supported(expr) PREC = precedence(expr) return (" %s " % self._operators['equivalent']).join(self.parenthesize(a, PREC) for a in expr.args) def _print_Not(self, expr): PREC = precedence(expr) return self._operators['not'] + self.parenthesize(expr.args[0], PREC) 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)) 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_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 sympy-0.7.4.1/sympy/printing/latex.py0000644000175000017500000017724212253362407017763 0ustar georgeskgeorgesk""" A Printer which converts an expression into its LaTeX equivalent. """ from __future__ import print_function, division from sympy.core import S, C, Add, Symbol from sympy.core.function import _coeff_isneg from sympy.core.sympify import SympifyError from sympy.core.alphabets import greeks from sympy.logic.boolalg import true ## sympy.printing imports from .printer import Printer from .conventions import split_super_sub, requires_partial from .precedence import precedence, PRECEDENCE import sympy.mpmath.libmp as mlib from sympy.mpmath.libmp import prec_to_dps from sympy.core.compatibility import default_sort_key, xrange 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', 'sinh', 'cosh', 'tanh', 'sqrt', 'ln', 'log', 'sec', 'csc', 'cot', 'coth', 're', 'im', 'frac', 'root', 'arg', ] tex_greek_dictionary = { 'Alpha': 'A', 'Beta': 'B', 'Epsilon': 'E', 'Zeta': 'Z', 'Eta': 'H', 'Iota': 'I', 'Kappa': 'K', 'Mu': 'M', 'Nu': 'N', 'omicron': 'o', 'Omicron': 'O', 'Rho': 'P', 'Tau': 'T', 'Chi': 'X', 'lamda': r'\lambda', 'Lamda': r'\Lambda', 'khi': r'\chi', 'Khi': r'X', 'varepsilon': r'\varepsilon', 'varkappa': r'\varkappa', 'varphi': r'\varphi', 'varpi': r'\varpi', 'varrho': r'\varrho', 'varsigma': r'\varsigma', 'vartheta': r'\vartheta', } other_symbols = set(['aleph', 'beth', 'daleth', 'gimel', 'ell', 'eth', 'hbar', 'hslash', 'mho', 'wp', ]) # Variable name modifiers modifier_dict = { # Accents 'mathring': lambda s: r'\mathring{'+s+r'}', 'ddddot': lambda s: r'\ddddot{'+s+r'}', 'dddot': lambda s: r'\dddot{'+s+r'}', 'ddot': lambda s: r'\ddot{'+s+r'}', 'dot': lambda s: r'\dot{'+s+r'}', 'check': lambda s: r'\check{'+s+r'}', 'breve': lambda s: r'\breve{'+s+r'}', 'acute': lambda s: r'\acute{'+s+r'}', 'grave': lambda s: r'\grave{'+s+r'}', 'tilde': lambda s: r'\tilde{'+s+r'}', 'hat': lambda s: r'\hat{'+s+r'}', 'bar': lambda s: r'\bar{'+s+r'}', 'vec': lambda s: r'\vec{'+s+r'}', 'prime': lambda s: "{"+s+"}'", 'prm': lambda s: "{"+s+"}'", # Faces 'bold': lambda s: r'\boldsymbol{'+s+r'}', 'bm': lambda s: r'\boldsymbol{'+s+r'}', 'cal': lambda s: r'\mathcal{'+s+r'}', 'scr': lambda s: r'\mathscr{'+s+r'}', 'frak': lambda s: r'\mathfrak{'+s+r'}', # Brackets 'norm': lambda s: r'\left\lVert{'+s+r'}\right\rVert', 'avg': lambda s: r'\left\langle{'+s+r'}\right\rangle', 'abs': lambda s: r'\left\lvert{'+s+r'}\right\rvert', 'mag': lambda s: r'\left\lvert{'+s+r'}\right\rvert', } greek_letters_set = frozenset(greeks) class LatexPrinter(Printer): printmethod = "_latex" _default_settings = { "order": None, "mode": "plain", "itex": False, "fold_frac_powers": False, "fold_func_brackets": False, "fold_short_frac": None, "long_frac_ratio": 2, "mul_symbol": None, "inv_trig_style": "abbreviated", "mat_str": None, "mat_delim": "[", "symbol_names": {}, } def __init__(self, settings=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*'") if self._settings['fold_short_frac'] is None and \ self._settings['mode'] == 'inline': self._settings['fold_short_frac'] = True 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._settings['mul_symbol_latex_numbers'] = \ mul_symbol_table[self._settings['mul_symbol'] or 'dot'] self._delim_dict = {'(': ')', '[': ']'} def parenthesize(self, item, level): if precedence(item) <= level: return r"\left(%s\right)" % self._print(item) else: return self._print(item) 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 _needs_mul_brackets(self, expr, last=False): """ Returns True if the expression needs to be wrapped in brackets when printed as part of a Mul, False otherwise. This is True for Add, but also for some container objects that would not need brackets when appearing last in a Mul, e.g. an Integral. ``last=True`` specifies that this expr is the last to appear in a Mul. """ from sympy import Integral, Piecewise, Product, Sum return expr.is_Add or (not last and any([expr.has(x) for x in (Integral, Piecewise, Product, Sum)])) 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_bool(self, e): return r"\mathrm{%s}" % e _print_BooleanTrue = _print_bool _print_BooleanFalse = _print_bool def _print_NoneType(self, e): return r"\mathrm{%s}" % e 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) # thus we use the number separator separator = self._settings['mul_symbol_latex_numbers'] 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, _ = expr.as_coeff_Mul() if not coeff.is_negative: tex = "" else: expr = -expr tex = "- " from sympy.simplify import fraction numer, denom = fraction(expr, exact=True) separator = self._settings['mul_symbol_latex'] numbersep = self._settings['mul_symbol_latex_numbers'] 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 for i, term in enumerate(args): term_tex = self._print(term) if self._needs_mul_brackets(term, last=(i == len(args) - 1)): term_tex = r"\left(%s\right)" % term_tex if re.search("[0-9][} ]*$", last_term_tex) and \ re.match("[{ ]*[-+0-9]", term_tex): # between two numbers _tex += numbersep elif _tex: _tex += separator _tex += term_tex last_term_tex = term_tex return _tex if denom is S.One: # use the original expression here, since fraction() may have # altered it when producing numer and denom tex += convert(expr) else: snumer = convert(numer) sdenom = convert(denom) ldenom = len(sdenom.split()) ratio = self._settings['long_frac_ratio'] if self._settings['fold_short_frac'] \ and ldenom <= 2 and not "^" in sdenom: # handle short fractions if self._needs_mul_brackets(numer, last=False): tex += r"\left(%s\right) / %s" % (snumer, sdenom) else: tex += r"%s / %s" % (snumer, sdenom) elif len(snumer.split()) > ratio*ldenom: # handle long fractions if self._needs_mul_brackets(numer, last=True): tex += r"\frac{1}{%s}%s\left(%s\right)" \ % (sdenom, separator, snumer) elif numer.is_Mul: # split a long numerator a = S.One b = S.One for x in numer.args: if self._needs_mul_brackets(x, last=False) or \ len(convert(a*x).split()) > ratio*ldenom or \ (b.is_commutative is x.is_commutative is False): b *= x else: a *= x if self._needs_mul_brackets(b, last=True): tex += r"\frac{%s}{%s}%s\left(%s\right)" \ % (convert(a), sdenom, separator, convert(b)) else: tex += r"\frac{%s}{%s}%s%s" \ % (convert(a), sdenom, separator, convert(b)) else: tex += r"\frac{1}{%s}%s%s" % (sdenom, separator, snumer) else: tex += r"\frac{%s}{%s}" % (snumer, sdenom) 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 if expr.base.is_Function: return self._print(expr.base, "%s/%s" % (p, q)) if self._needs_brackets(expr.base): return r"\left(%s\right)^{%s/%s}" % (base, p, q) return r"%s^{%s/%s}" % (base, p, q) elif expr.exp.is_Rational and expr.exp.is_negative and expr.base.is_commutative: # Things like 1/x return self._print_Mul(expr) 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 requires_partial(expr): diff_symbol = r'\partial' else: diff_symbol = r'd' if dim == 1: tex = r"\frac{%s}{%s %s}" % (diff_symbol, diff_symbol, 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"%s %s" % (diff_symbol, self._print(x)) else: tex += r"%s %s^{%s}" % (diff_symbol, self._print(x), i) tex = r"\frac{%s^{%s}}{%s} " % (diff_symbol, 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 _hprint_Function(self, func): ''' Logic to decide how to render a function to latex - if it is a recognized latex name, use the appropriate latex command - if it is a single letter, just use that letter - if it is a longer name, then put \operatorname{} around it and be mindful of undercores in the name ''' func = translate(func) if func in accepted_latex_functions: name = r"\%s" % func elif len(func) == 1 or func.startswith('\\'): name = func else: name = r"\operatorname{%s}" % func.replace("_", r"\_") return name def _print_Function(self, expr, exp=None): ''' Render functions to LaTeX, handling functions that LaTeX knows about e.g., sin, cos, ... by using the proper LaTeX command (\sin, \cos, ...). For single-letter function names, render them as regular LaTeX math symbols. For multi-letter function names that LaTeX does not know about, (e.g., Li, sech) use \operatorname{} so that the function name is rendered in Roman font and LaTeX handles spacing properly. expr is the expression involving the function exp is an exponent ''' 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: name = r'%s^{%s}' % (self._hprint_Function(func), exp) else: name = self._hprint_Function(func) 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_UndefinedFunction(self, expr): return self._hprint_Function(str(expr)) def _print_FunctionClass(self, expr): if hasattr(expr, '_latex_no_arg'): return expr._latex_no_arg(self) return self._hprint_Function(str(expr)) 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"\left\lvert{%s}\right\rvert" % self._print(expr.args[0]) if exp is not None: return r"%s^{%s}" % (tex, exp) else: return tex _print_Determinant = _print_Abs 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_elliptic_k(self, expr, exp=None): tex = r"\left(%s\right)" % self._print(expr.args[0]) if exp is not None: return r"K^{%s}%s" % (exp, tex) else: return r"K%s" % tex def _print_elliptic_f(self, expr, exp=None): tex = r"\left(%s\middle| %s\right)" % \ (self._print(expr.args[0]), self._print(expr.args[1])) if exp is not None: return r"F^{%s}%s" % (exp, tex) else: return r"F%s" % tex def _print_elliptic_e(self, expr, exp=None): if len(expr.args) == 2: tex = r"\left(%s\middle| %s\right)" % \ (self._print(expr.args[0]), self._print(expr.args[1])) else: tex = r"\left(%s\right)" % self._print(expr.args[0]) if exp is not None: return r"E^{%s}%s" % (exp, tex) else: return r"E%s" % tex def _print_elliptic_pi(self, expr, exp=None): if len(expr.args) == 3: tex = r"\left(%s; %s\middle| %s\right)" % \ (self._print(expr.args[0]), self._print(expr.args[1]), \ self._print(expr.args[2])) else: tex = r"\left(%s\middle| %s\right)" % \ (self._print(expr.args[0]), self._print(expr.args[1])) if exp is not None: return r"\Pi^{%s}%s" % (exp, tex) else: return r"\Pi%s" % tex 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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_Ynm(self, expr, exp=None): n, m, theta, phi = map(self._print, expr.args) tex = r"Y_{%s}^{%s}\left(%s,%s\right)" % (n, m, theta, phi) if exp is not None: tex = r"\left(" + tex + r"\right)^{%s}" % (self._print(exp)) return tex def _print_Znm(self, expr, exp=None): n, m, theta, phi = map(self._print, expr.args) tex = r"Z_{%s}^{%s}\left(%s,%s\right)" % (n, m, theta, phi) 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_Order(self, expr): s = self._print(expr.expr) if expr.point and any(p != S.Zero for p in expr.point) or \ len(expr.variables) > 1: s += '; ' if len(expr.variables) > 1: s += self._print(expr.variables) elif len(expr.variables): s += self._print(expr.variables[0]) s += r'\rightarrow' if len(expr.point) > 1: s += self._print(expr.point) else: s += self._print(expr.point[0]) return r"\mathcal{O}\left(%s\right)" % s 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) 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 _print_RandomSymbol = _print_Symbol _print_MatrixSymbol = _print_Symbol 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 == 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, :] ])) mat_str = self._settings['mat_str'] if mat_str is None: if self._settings['mode'] == 'inline': mat_str = 'smallmatrix' else: if (expr.cols <= 10) is True: mat_str = 'matrix' else: mat_str = 'array' out_str = r'\begin{%MATSTR%}%s\end{%MATSTR%}' out_str = out_str.replace('%MATSTR%', mat_str) if mat_str == 'array': out_str = out_str.replace('%s', '{' + 'c'*expr.cols + '}%s') 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_MatrixElement(self, expr): return self._print(expr.parent) + '_{%s, %s}'%(expr.i, expr.j) def _print_MatrixSlice(self, expr): def latexslice(x): x = list(x) if x[2] == 1: del x[2] if x[1] == x[0] + 1: del x[1] if x[0] == 0: x[0] = '' return ':'.join(map(self._print, x)) return (self._print(expr.parent) + r'\left[' + latexslice(expr.rowslice) + ', ' + latexslice(expr.colslice) + r'\right]') 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(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 = map(self._print, expr.args) if all(map(lambda x: x.is_Atom, 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_ImageSet(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_RealField(self, expr): return r"\mathbb{R}" def _print_ComplexField(self, expr): return r"\mathbb{C}" def _print_PolynomialRing(self, expr): domain = self._print(expr.domain) symbols = ", ".join(map(self._print, expr.symbols)) return r"%s\left[%s\right]" % (domain, symbols) def _print_FractionField(self, expr): domain = self._print(expr.domain) symbols = ", ".join(map(self._print, expr.symbols)) return r"%s\left(%s\right)" % (domain, symbols) def _print_PolynomialRingBase(self, expr): domain = self._print(expr.domain) symbols = ", ".join(map(self._print, expr.symbols)) inv = "" if not expr.is_Poly: inv = r"S_<^{-1}" return r"%s%s\left[%s\right]" % (inv, domain, symbols) 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_PolyElement(self, poly): mul_symbol = self._settings['mul_symbol_latex'] return poly.str(self, PRECEDENCE, "{%s}^{%d}", mul_symbol) def _print_FracElement(self, frac): if frac.denom == 1: return self._print(frac.numer) else: numer = self._print(frac.numer) denom = self._print(frac.denom) return r"\frac{%s}{%s}" % (numer, denom) 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 xrange(grid.height): for j in xrange(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'\mathrm{d}%s' % self._print(Symbol(string)) else: return 'd(%s)' % self._print(field) string = self._print(field) return r'\mathrm{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) def _print_totient(self, expr): return r'\phi\left( %s \right)' % self._print(expr.args[0]) def translate(s): r''' Check for a modifier ending the string. If present, convert the modifier to latex and translate the rest recursively. Given a description of a Greek letter or other special character, return the appropriate latex. Let everything else pass as given. >>> from sympy.printing.latex import translate >>> translate('alphahatdotprime') "{\\dot{\\hat{\\alpha}}}'" ''' # Process the rest tex = tex_greek_dictionary.get(s) if tex: return tex elif s.lower() in greek_letters_set or s in other_symbols: return "\\" + s else: # Process modifiers, if any, and recurse for key in sorted(modifier_dict.keys(), key=lambda k:len(k), reverse=True): if s.lower().endswith(key) and len(s)>len(key): return modifier_dict[key](translate(s[:-len(key)])) return s def latex(expr, **settings): r""" Convert the given expression to LaTeX representation. >>> from sympy import latex, pi, sin, asin, Integral, Matrix, Rational >>> from sympy.abc import x, y, mu, r, tau >>> print(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. >>> print(latex((2*mu)**Rational(7,2), mode='plain')) 8 \sqrt{2} \mu^{\frac{7}{2}} >>> print(latex((2*tau)**Rational(7,2), mode='inline')) $8 \sqrt{2} \tau^{\frac{7}{2}}$ >>> print(latex((2*mu)**Rational(7,2), mode='equation*')) \begin{equation*}8 \sqrt{2} \mu^{\frac{7}{2}}\end{equation*} >>> print(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 ``$$ $$``. >>> print(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. >>> print(latex((2*tau)**Rational(7,2), fold_frac_powers=True)) 8 \sqrt{2} \tau^{7/2} fold_func_brackets: Fold function brackets where applicable. >>> print(latex((2*tau)**sin(Rational(7,2)))) \left(2 \tau\right)^{\sin{\left (\frac{7}{2} \right )}} >>> print(latex((2*tau)**sin(Rational(7,2)), fold_func_brackets = True)) \left(2 \tau\right)^{\sin {\frac{7}{2}}} fold_short_frac: Emit "p / q" instead of "\frac{p}{q}" when the denominator is simple enough (at most two terms and no powers). The default value is `True` for inline mode, False otherwise. >>> print(latex(3*x**2/y)) \frac{3 x^{2}}{y} >>> print(latex(3*x**2/y, fold_short_frac=True)) 3 x^{2} / y long_frac_ratio: The allowed ratio of the width of the numerator to the width of the denominator before we start breaking off long fractions. The default value is 2. >>> print(latex(Integral(r, r)/2/pi, long_frac_ratio=2)) \frac{\int r\, dr}{2 \pi} >>> print(latex(Integral(r, r)/2/pi, long_frac_ratio=0)) \frac{1}{2 \pi} \int r\, dr mul_symbol: The symbol to use for multiplication. Can be one of None, "ldot", "dot", or "times". >>> print(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". >>> print(latex(asin(Rational(7,2)))) \operatorname{asin}{\left (\frac{7}{2} \right )} >>> print(latex(asin(Rational(7,2)), inv_trig_style="full")) \arcsin{\left (\frac{7}{2} \right )} >>> print(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", "matrix", "array", etc. Defaults to "smallmatrix" for inline mode, "matrix" for matrices of no more than 10 columns, and "array" otherwise. >>> print(latex(Matrix(2, 1, [x, y]))) \left[\begin{matrix}x\\y\end{matrix}\right] >>> print(latex(Matrix(2, 1, [x, y]), mat_str = "array")) \left[\begin{array}{c}x\\y\end{array}\right] mat_delim: The delimiter to wrap around matrices. Can be one of "[", "(", or the empty string. Defaults to "[". >>> print(latex(Matrix(2, 1, [x, y]), mat_delim="(")) \left(\begin{matrix}x\\y\end{matrix}\right) symbol_names: Dictionary of symbols and the custom strings they should be emitted as. >>> print(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: >>> print(latex([2/x, y], mode='inline')) $\begin{bmatrix}2 / x, & y\end{bmatrix}$ """ return LatexPrinter(settings).doprint(expr) def print_latex(expr, **settings): """Prints LaTeX representation of the given expression.""" print(latex(expr, **settings)) sympy-0.7.4.1/sympy/printing/tableform.py0000644000175000017500000002674112253362407020616 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core.containers import Tuple from types import FunctionType class TableForm(object): """ Create a nice table representation of data. Example:: >>> from sympy import TableForm >>> t = TableForm([[5, 7], [4, 2], [10, 3]]) >>> print(t) 5 7 4 2 10 3 You can use the SymPy's printing system to produce tables in any format (ascii, latex, html, ...). >>> print(t.as_latex()) \\begin{tabular}{l l} $5$ & $7$ \\\\ $4$ & $2$ \\\\ $10$ & $3$ \\\\ \end{tabular} """ def __init__(self, data, **kwarg): """ Creates a TableForm. Parameters: data ... 2D data to be put into the table; data can be given as a Matrix headings ... gives the labels for rows and columns: Can be a single argument that applies to both dimensions: - None ... no labels - "automatic" ... labels are 1, 2, 3, ... Can be a list of labels for rows and columns: The lables for each dimension can be given as None, "automatic", or [l1, l2, ...] e.g. ["automatic", None] will number the rows [default: None] alignments ... alignment of the columns with: - "left" or "<" - "center" or "^" - "right" or ">" When given as a single value, the value is used for all columns. The row headings (if given) will be right justified unless an explicit alignment is given for it and all other columns. [default: "left"] formats ... a list of format strings or functions that accept 3 arguments (entry, row number, col number) and return a string for the table entry. (If a function returns None then the _print method will be used.) wipe_zeros ... Don't show zeros in the table. [default: True] pad ... the string to use to indicate a missing value (e.g. elements that are None or those that are missing from the end of a row (i.e. any row that is shorter than the rest is assumed to have missing values). When None, nothing will be shown for values that are missing from the end of a row; values that are None, however, will be shown. [default: None] Examples ======== >>> from sympy import TableForm, Matrix >>> TableForm([[5, 7], [4, 2], [10, 3]]) 5 7 4 2 10 3 >>> TableForm([list('.'*i) for i in range(1, 4)], headings='automatic') | 1 2 3 --------- 1 | . 2 | . . 3 | . . . >>> TableForm([['.'*(j if not i%2 else 1) for i in range(3)] ... for j in range(4)], alignments='rcl') . . . . .. . .. ... . ... """ from sympy import Symbol, S, Matrix from sympy.core.sympify import SympifyError # We only support 2D data. Check the consistency: if isinstance(data, Matrix): data = data.tolist() _w = len(data[0]) _h = len(data) # fill out any short lines pad = kwarg.get('pad', None) ok_None = False if pad is None: pad = " " ok_None = True pad = Symbol(pad) _w = max(len(line) for line in data) for i, line in enumerate(data): if len(line) != _w: line.extend([pad]*(_w - len(line))) for j, lj in enumerate(line): if lj is None: if not ok_None: lj = pad else: try: lj = S(lj) except SympifyError: lj = Symbol(str(lj)) line[j] = lj data[i] = line _lines = Tuple(*data) headings = kwarg.get("headings", [None, None]) if headings == "automatic": _headings = [range(1, _h + 1), range(1, _w + 1)] else: h1, h2 = headings if h1 == "automatic": h1 = range(1, _h + 1) if h2 == "automatic": h2 = range(1, _w + 1) _headings = [h1, h2] allow = ('l', 'r', 'c') alignments = kwarg.get("alignments", "l") def _std_align(a): a = a.strip().lower() if len(a) > 1: return {'left': 'l', 'right': 'r', 'center': 'c'}.get(a, a) else: return {'<': 'l', '>': 'r', '^': 'c'}.get(a, a) std_align = _std_align(alignments) if std_align in allow: _alignments = [std_align]*_w else: _alignments = [] for a in alignments: std_align = _std_align(a) _alignments.append(std_align) if std_align not in ('l', 'r', 'c'): raise ValueError('alignment "%s" unrecognized' % alignments) if _headings[0] and len(_alignments) == _w + 1: _head_align = _alignments[0] _alignments = _alignments[1:] else: _head_align = 'r' if len(_alignments) != _w: raise ValueError( 'wrong number of alignments: expected %s but got %s' % (_w, len(_alignments))) _column_formats = kwarg.get("formats", [None]*_w) _wipe_zeros = kwarg.get("wipe_zeros", True) self._w = _w self._h = _h self._lines = _lines self._headings = _headings self._head_align = _head_align self._alignments = _alignments self._column_formats = _column_formats self._wipe_zeros = _wipe_zeros def __repr__(self): from .str import sstr return sstr(self, order=None) def __str__(self): from .str import sstr return sstr(self, order=None) def as_matrix(self): """Returns the data of the table in Matrix form. Examples ======== >>> from sympy import TableForm >>> t = TableForm([[5, 7], [4, 2], [10, 3]], headings='automatic') >>> t | 1 2 -------- 1 | 5 7 2 | 4 2 3 | 10 3 >>> t.as_matrix() Matrix([ [ 5, 7], [ 4, 2], [10, 3]]) """ from sympy import Matrix return Matrix(self._lines) def as_str(self): # XXX obsolete ? return str(self) def as_latex(self): from .latex import latex return latex(self) def _sympystr(self, p): """ Returns the string representation of 'self'. Example: >>> from sympy import TableForm >>> t = TableForm([[5, 7], [4, 2], [10, 3]]) >>> s = t.as_str() """ column_widths = [0] * self._w lines = [] for line in self._lines: new_line = [] for i in range(self._w): # Format the item somehow if needed: s = str(line[i]) if self._wipe_zeros and (s == "0"): s = " " w = len(s) if w > column_widths[i]: column_widths[i] = w new_line.append(s) lines.append(new_line) # Check heading: if self._headings[0]: self._headings[0] = [str(x) for x in self._headings[0]] _head_width = max([len(x) for x in self._headings[0]]) if self._headings[1]: new_line = [] for i in range(self._w): # Format the item somehow if needed: s = str(self._headings[1][i]) w = len(s) if w > column_widths[i]: column_widths[i] = w new_line.append(s) self._headings[1] = new_line format_str = [] def _align(align, w): return '%%%s%ss' % ( ("-" if align == "l" else ""), str(w)) format_str = [_align(align, w) for align, w in zip(self._alignments, column_widths)] if self._headings[0]: format_str.insert(0, _align(self._head_align, _head_width)) format_str.insert(1, '|') format_str = ' '.join(format_str) + '\n' s = [] if self._headings[1]: d = self._headings[1] if self._headings[0]: d = [""] + d first_line = format_str % tuple(d) s.append(first_line) s.append("-" * (len(first_line) - 1) + "\n") for i, line in enumerate(lines): d = [l if self._alignments[j] != 'c' else l.center(column_widths[j]) for j, l in enumerate(line)] if self._headings[0]: l = self._headings[0][i] l = (l if self._head_align != 'c' else l.center(_head_width)) d = [l] + d s.append(format_str % tuple(d)) return ''.join(s)[:-1] # don't include trailing newline def _latex(self, printer): """ Returns the string representation of 'self'. """ # Check heading: if self._headings[1]: new_line = [] for i in range(self._w): # Format the item somehow if needed: new_line.append(str(self._headings[1][i])) self._headings[1] = new_line alignments = [] if self._headings[0]: self._headings[0] = [str(x) for x in self._headings[0]] alignments = [self._head_align] alignments.extend(self._alignments) s = r"\begin{tabular}{" + " ".join(alignments) + "}\n" if self._headings[1]: d = self._headings[1] if self._headings[0]: d = [""] + d first_line = " & ".join(d) + r" \\" + "\n" s += first_line s += r"\hline" + "\n" for i, line in enumerate(self._lines): d = [] for j, x in enumerate(line): if self._wipe_zeros and (x in (0, "0")): d.append(" ") continue f = self._column_formats[j] if f: if isinstance(f, FunctionType): v = f(x, i, j) if v is None: v = printer._print(x) else: v = f % x d.append(v) else: v = printer._print(x) d.append("$%s$" % v) if self._headings[0]: d = [self._headings[0][i]] + d s += " & ".join(d) + r" \\" + "\n" s += r"\end{tabular}" return s sympy-0.7.4.1/sympy/printing/theanocode.py0000644000175000017500000001774012253362407020753 0ustar georgeskgeorgeskfrom __future__ import print_function, division import inspect from sympy.utilities import default_sort_key from sympy.external import import_module from sympy.printing.printer import Printer import sympy from functools import partial theano = import_module('theano') if theano: ts = theano.scalar tt = theano.tensor from theano import sandbox from theano.sandbox import linalg as tlinalg mapping = { sympy.Add: tt.add, sympy.Mul: tt.mul, sympy.Abs: tt.abs_, sympy.sign: tt.sgn, sympy.ceiling: tt.ceil, sympy.floor: tt.floor, sympy.log: tt.log, sympy.exp: tt.exp, sympy.sqrt: tt.sqrt, sympy.cos: tt.cos, sympy.acos: tt.arccos, sympy.sin: tt.sin, sympy.asin: tt.arcsin, sympy.tan: tt.tan, sympy.atan: tt.arctan, sympy.atan2: tt.arctan2, sympy.cosh: tt.cosh, sympy.acosh: tt.arccosh, sympy.sinh: tt.sinh, sympy.asinh: tt.arcsinh, sympy.tanh: tt.tanh, sympy.atanh: tt.arctanh, sympy.re: tt.real, sympy.im: tt.imag, sympy.arg: tt.angle, sympy.erf: tt.erf, sympy.gamma: tt.gamma, sympy.loggamma: tt.gammaln, sympy.Pow: tt.pow, sympy.Eq: tt.eq, sympy.StrictGreaterThan: tt.gt, sympy.StrictLessThan: tt.lt, sympy.LessThan: tt.le, sympy.GreaterThan: tt.ge, sympy.Max: tt.maximum, # Sympy accept >2 inputs, Theano only 2 sympy.Min: tt.minimum, # Sympy accept >2 inputs, Theano only 2 # Matrices sympy.MatAdd: tt.Elemwise(ts.add), sympy.HadamardProduct: tt.Elemwise(ts.mul), sympy.Trace: tlinalg.trace, sympy.Determinant : tlinalg.det, sympy.Inverse: tlinalg.matrix_inverse, sympy.Transpose: tt.DimShuffle((False, False), [1, 0]), } class TheanoPrinter(Printer): """ Code printer for Theano computations """ printmethod = "_theano" def __init__(self, *args, **kwargs): self.cache = kwargs.pop('cache', dict()) super(TheanoPrinter, self).__init__(*args, **kwargs) def _print_Symbol(self, s, dtypes={}, broadcastables={}): dtype = dtypes.get(s, 'floatX') broadcastable = broadcastables.get(s, ()) key = (s.name, dtype, broadcastable, type(s)) if key in self.cache: return self.cache[key] else: value = tt.tensor(name=s.name, dtype=dtype, broadcastable=broadcastable) self.cache[key] = value return value def _print_AppliedUndef(self, s, dtypes={}, broadcastables={}): dtype = dtypes.get(s, 'floatX') broadcastable = broadcastables.get(s, ()) name = str(type(s)) + '_' + str(s.args[0]) key = (name, dtype, broadcastable, type(s), s.args) if key in self.cache: return self.cache[key] else: value = tt.tensor(name=name, dtype=dtype, broadcastable=broadcastable) self.cache[key] = value return value def _print_Basic(self, expr, **kwargs): op = mapping[type(expr)] children = [self._print(arg, **kwargs) for arg in expr.args] return op(*children) def _print_Number(self, n, **kwargs): return eval(str(n)) def _print_MatrixSymbol(self, X, dtypes={}, **kwargs): dtype = dtypes.get(X, 'floatX') # shape = [self._print(d, dtypes) for d in X.shape] key = (X.name, dtype, type(X)) if key in self.cache: return self.cache[key] else: value = tt.Tensor(dtype, (False, False))(X.name) self.cache[key] = value return value def _print_DenseMatrix(self, X, **kwargs): try: tt.stacklists except AttributeError: raise NotImplementedError( "Matrix translation not yet supported in this version of Theano") else: return tt.stacklists([[self._print(arg, **kwargs) for arg in L] for L in X.tolist()]) _print_ImmutableMatrix = _print_DenseMatrix def _print_MatMul(self, expr, **kwargs): children = [self._print(arg, **kwargs) for arg in expr.args] result = children[0] for child in children[1:]: result = tt.dot(result, child) return result def _print_MatrixSlice(self, expr, **kwargs): parent = self._print(expr.parent, **kwargs) rowslice = self._print(slice(*expr.rowslice), **kwargs) colslice = self._print(slice(*expr.colslice), **kwargs) return parent[rowslice, colslice] def _print_BlockMatrix(self, expr, **kwargs): nrows, ncols = expr.blocks.shape blocks = [[self._print(expr.blocks[r, c], **kwargs) for c in range(ncols)] for r in range(nrows)] return tt.join(0, *[tt.join(1, *row) for row in blocks]) def _print_slice(self, expr, **kwargs): return slice(*[self._print(i, **kwargs) if isinstance(i, sympy.Basic) else i for i in (expr.start, expr.stop, expr.step)]) def _print_Pi(self, expr, **kwargs): return 3.141592653589793 def _print_Piecewise(self, expr, **kwargs): import numpy as np e, cond = expr.args[0].args if len(expr.args) == 1: return tt.switch(self._print(cond, **kwargs), self._print(e, **kwargs), np.nan) return tt.switch(self._print(cond, **kwargs), self._print(e, **kwargs), self._print(sympy.Piecewise(*expr.args[1:]), **kwargs)) def _print_Rational(self, expr, **kwargs): return tt.true_div(self._print(expr.p, **kwargs), self._print(expr.q, **kwargs)) def _print_Integer(self, expr, **kwargs): return expr.p def _print_factorial(self, expr, **kwargs): return self._print(sympy.gamma(expr.args[0] + 1), **kwargs) def _print_Derivative(self, deriv, **kwargs): rv = self._print(deriv.expr, **kwargs) for var in deriv.variables: var = self._print(var, **kwargs) rv = tt.Rop(rv, var, tt.ones_like(var)) return rv def emptyPrinter(self, expr): return expr def doprint(self, expr, **kwargs): """Returns printer's representation for expr (as a string)""" return self._print(expr, **kwargs) global_cache = {} def theano_code(expr, cache=global_cache, **kwargs): return TheanoPrinter(cache=cache, settings={}).doprint(expr, **kwargs) def dim_handling(inputs, dim=None, dims={}, broadcastables={}, keys=(), **kwargs): """ Handle various input types for dimensions in tensor_wrap See Also: tensor_wrap theano_funciton """ if dim: dims = dict(zip(inputs, [dim]*len(inputs))) if dims: maxdim = max(dims.values()) broadcastables = dict((i, (False,)*dims[i] + (True,)*(maxdim-dims[i])) for i in inputs) return broadcastables def theano_function(inputs, outputs, dtypes={}, cache=None, **kwargs): """ Create Theano function from SymPy expressions """ cache = {} if cache == None else cache broadcastables = dim_handling(inputs, **kwargs) # Remove keyword arguments corresponding to dim_handling dim_names = inspect.getargspec(dim_handling)[0] theano_kwargs = dict((k, v) for k, v in kwargs.items() if k not in dim_names) code = partial(theano_code, cache=cache, dtypes=dtypes, broadcastables=broadcastables) tinputs = list(map(code, inputs)) toutputs = list(map(code, outputs)) toutputs = toutputs[0] if len(toutputs) == 1 else toutputs return theano.function(tinputs, toutputs, **theano_kwargs) sympy-0.7.4.1/sympy/printing/tree.py0000644000175000017500000000410112253362407017564 0ustar georgeskgeorgeskfrom __future__ import print_function, division def pprint_nodes(subtrees): """ 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): """ Returns an information about the "node". This includes class name, string representation and assumptions. """ s = "%s: %s\n" % (node.__class__.__name__, str(node)) if len(node._assumptions) > 0: for a in node._assumptions: s += "%s: %s\n" % (a, node._assumptions[a]) return s def tree(node): """ 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): """ Prints a tree representation of "node". Examples ======== >>> from sympy.printing import print_tree >>> from sympy.abc import x >>> print_tree(x**2) # doctest: +SKIP Pow: x**2 +-Symbol: x | comparable: False +-Integer: 2 real: True nonzero: True comparable: True commutative: True infinitesimal: False unbounded: False noninteger: False zero: False complex: True bounded: True rational: True integer: True imaginary: False finite: True irrational: False See also: tree() """ print(tree(node)) sympy-0.7.4.1/sympy/printing/defaults.py0000644000175000017500000000125412253362407020442 0ustar georgeskgeorgeskfrom __future__ import print_function, division class DefaultPrinting(object): """ The default implementation of printing for SymPy classes. This implements a hack that allows us to print elements of built-in Python containers in a readable way. Natively Python uses ``repr()`` even if ``str()`` was explicitly requested. Mix in this trait into a class to get proper default printing. """ # Note, we always use the default ordering (lex) in __str__ and __repr__, # regardless of the global setting. See issue 2388. def __str__(self): from sympy.printing.str import sstr return sstr(self, order=None) __repr__ = __str__ sympy-0.7.4.1/sympy/printing/conventions.py0000644000175000017500000000455212253362407021204 0ustar georgeskgeorgesk""" A few practical conventions common to all printers. """ from __future__ import print_function, division import re import collections 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 def requires_partial(expr): """Return whether a partial derivative symbol is required for printing This requires checking how many free variables there are, filtering out the ones that are integers. Some expressions don't have free variables. In that case, check its variable list explicitly to get the context of the expression. """ if not isinstance(expr.free_symbols, collections.Iterable): return len(set(expr.variables)) > 1 return sum(not s.is_integer for s in expr.free_symbols) > 1 sympy-0.7.4.1/sympy/printing/fcode.py0000644000175000017500000003740312253362407017720 0ustar georgeskgeorgesk""" 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 __future__ import print_function, division import string from sympy.core import S, C, Add, N from sympy.core.compatibility import string_types from sympy.printing.codeprinter import CodePrinter from sympy.printing.precedence import precedence 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', } _implicit_functions = set([ "sin", "cos", "tan", "asin", "acos", "atan", "atan2", "sinh", "cosh", "tanh", "sqrt", "log", "exp", "erf", "Abs", "sign", "conjugate", ]) _operators = { 'and': '.and.', 'or': '.or.', 'xor': '.neqv.', 'equivalent': '.eqv.', 'not': '.not. ', } _relationals = { '!=': '/=', } def __init__(self, settings=None): CodePrinter.__init__(self, settings) self._init_leading_padding() assign_to = self._settings['assign_to'] if isinstance(assign_to, string_types): 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 = 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 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 = [] from sympy.functions import Piecewise 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 == 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__) eargs = expr.args if name is None: from sympy.functions import conjugate 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_(*eargs)) if expr.func.__name__ not in self._implicit_functions: self._not_supported.add(expr) else: # convert all args to floats eargs = map(N, eargs) return "%s(%s)" % (name, self.stringify(eargs, ", ")) _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("_+-." + string.digits + string.ascii_letters) 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 def indent_code(self, code): """Accepts a string of code or a list of code lines""" if isinstance(code, string_types): 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 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) def print_fcode(expr, **settings): """Prints the Fortran representation of the given expression. See fcode for the meaning of the optional arguments. """ print(fcode(expr, **settings)) sympy-0.7.4.1/sympy/printing/str.py0000644000175000017500000005527312253362407017455 0ustar georgeskgeorgesk""" A Printer for generating readable representation of most sympy classes. """ from __future__ import print_function, division 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.utilities import default_sort_key class StrPrinter(Printer): printmethod = "_sympystr" _default_settings = { "order": None, "full_prec": "auto", } _relationals = dict() 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_BooleanTrue(self, expr): return "True" def _print_BooleanFalse(self, expr): return "False" 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(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_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(self) _print_SparseMatrix = \ _print_MutableSparseMatrix = \ _print_ImmutableSparseMatrix = \ _print_Matrix = \ _print_DenseMatrix = \ _print_MutableDenseMatrix = \ _print_ImmutableMatrix = \ _print_ImmutableDenseMatrix = \ _print_MatrixBase def _print_MatrixElement(self, expr): return self._print(expr.parent) + '[%s, %s]'%(expr.i, expr.j) def _print_MatrixSlice(self, expr): def strslice(x): x = list(x) if x[2] == 1: del x[2] if x[1] == x[0] + 1: del x[1] if x[0] == 0: x[0] = '' return ':'.join(map(self._print, x)) return (self._print(expr.parent) + '[' + strslice(expr.rowslice) + ', ' + strslice(expr.colslice) + ']') 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 = list(map(lambda x: self.parenthesize(x, prec), a)) b_str = list(map(lambda x: self.parenthesize(x, prec), 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 all(p is S.Zero for p in expr.point) or not len(expr.variables): if len(expr.variables) <= 1: return 'O(%s)' % self._print(expr.expr) else: return 'O(%s)' % self.stringify((expr.expr,) + expr.variables, ', ', 0) 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_TensorIndex(self, expr): return expr._print() def _print_TensorHead(self, expr): return expr._print() def _print_TensMul(self, expr): return expr._print() def _print_TensAdd(self, expr): return expr._print() 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_PolyRing(self, ring): return "Polynomial ring in %s over %s with %s order" % \ (", ".join(map(self._print, ring.symbols)), ring.domain, ring.order) def _print_FracField(self, field): return "Rational function field in %s over %s with %s order" % \ (", ".join(map(self._print, field.symbols)), field.domain, field.order) def _print_PolyElement(self, poly): return poly.str(self, PRECEDENCE, "%s**%d", "*") def _print_FracElement(self, frac): if frac.denom == 1: return self._print(frac.numer) else: numer = self.parenthesize(frac.numer, PRECEDENCE["Add"]) denom = self.parenthesize(frac.denom, PRECEDENCE["Atom"]-1) return numer + "/" + denom 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" from sympy.polys.polyerrors import PolynomialError 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): if expr.q == 1: return str(expr.p) else: return "%s/%s" % (expr.p, expr.q) def _print_PythonRational(self, expr): if expr.q == 1: return str(expr.p) else: return "%d/%d" % (expr.p, expr.q) def _print_Fraction(self, expr): if expr.denominator == 1: return str(expr.numerator) else: return "%s/%s" % (expr.numerator, expr.denominator) def _print_mpq(self, expr): if expr.denominator == 1: return str(expr.numerator) else: return "%s/%s" % (expr.numerator, expr.denominator) 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)), self._relationals.get(expr.rel_op) or 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 _print_RandomSymbol = _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) 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 sympy-0.7.4.1/sympy/printing/jscode.py0000644000175000017500000002260112253362407020101 0ustar georgeskgeorgesk""" Javascript code printer The JavascriptCodePrinter converts single sympy expressions into single Javascript expressions, using the functions defined in the Javascript Math object where possible. """ from __future__ import print_function, division from sympy.core import S, C from sympy.printing.codeprinter import CodePrinter from sympy.printing.precedence import precedence from sympy.core.compatibility import string_types # dictionary mapping sympy function to (argument_conditions, Javascript_function). # Used in JavascriptCodePrinter._print_Function(self) known_functions = { } function_translations = { 'Abs': 'Math.abs', 'acos': 'Math.acos', 'asin': 'Math.asin', 'atan': 'Math.atan', 'ceiling': 'Math.ceil', 'cos': 'Math.cos', 'exp': 'Math.exp', 'floor': 'Math.floor', 'log': 'Math.log', 'sin': 'Math.sin', 'tan': 'Math.tan', } class JavascriptCodePrinter(CodePrinter): """"A Printer to convert python expressions to strings of javascript code """ printmethod = '_javascript' _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 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 def doprint(self, expr, assign_to=None): """ Actually format the expression as Javascript code. """ if isinstance(assign_to, string_types): assign_to = C.Symbol(assign_to) elif not isinstance(assign_to, (C.Basic, type(None))): raise TypeError("JavascriptCodePrinter cannot assign to object of type %s" % type(assign_to)) # keep a set of expressions that are not strictly translatable to Javascript # and number constants that must be declared and initialized not_js = 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 == 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_js) > 0: frontlines.append("// Not Javascript:") for expr in sorted(not_js, key=str): frontlines.append("// %s" % repr(expr)) for name, value in sorted(self._number_symbols, key=str): frontlines.append("var %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_js, 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 (var %(varble)s=%(start)s; %(varble)s<%(end)s; %(varble)s++){" for i in indices: # Javascript arrays start at 0 and end at dimension-1 open_lines.append(loopstart % { 'varble': 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/%s' % (self.parenthesize(expr.base, PREC)) elif expr.exp == 0.5: return 'Math.sqrt(%s)' % self._print(expr.base) else: return 'Math.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/%d' % (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(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 "Math.E" def _print_Pi(self, expr): return 'Math.PI' def _print_Infinity(self, expr): return 'Number.POSITIVE_INFINITY' def _print_NegativeInfinity(self, expr): return 'Number.NEGATIVE_INFINITY' 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 == 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_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 expr.func.__name__ in function_translations: tr = function_translations[expr.func.__name__] return "%s(%s)" % (tr, 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) def indent_code(self, code): """Accepts a string of code or a list of code lines""" if isinstance(code, string_types): 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 def jscode(expr, assign_to=None, **settings): """Converts an expr to a string of javascript 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 their string representations. Alternatively the dictionary values can be a list of tuples i.e. [(argument_test, jsfunction_string)]. 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 jscode, symbols, Rational, sin >>> x, tau = symbols(["x", "tau"]) >>> jscode((2*tau)**Rational(7,2)) '8*Math.sqrt(2)*Math.pow(tau, 7/2)' >>> jscode(sin(x), assign_to="s") 's = Math.sin(x);' """ return JavascriptCodePrinter(settings).doprint(expr, assign_to) def print_jscode(expr, **settings): """Prints the Javascript representation of the given expression. See jscode for the meaning of the optional arguments. """ print(jscode(expr, **settings)) sympy-0.7.4.1/sympy/printing/ccode.py0000644000175000017500000002243012253362407017707 0ustar georgeskgeorgesk""" 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 __future__ import print_function, division from sympy.core import S, C from sympy.core.compatibility import string_types from sympy.printing.codeprinter import CodePrinter from sympy.printing.precedence import precedence # 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")], } 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 userfuncs.items(): if not isinstance(v, list): 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 def doprint(self, expr, assign_to=None): """ Actually format the expression as C code. """ if isinstance(assign_to, string_types): 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 == 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.0L/%d.0L' % (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(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 == True: last_line = ": (\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 = "%s" + last_line return code % ": ".join(ecpairs) + " )" 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) def indent_code(self, code): """Accepts a string of code or a list of code lines""" if isinstance(code, string_types): 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 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 their 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, ceiling, Abs >>> x, tau = symbols(["x", "tau"]) >>> ccode((2*tau)**Rational(7,2)) '8*sqrt(2)*pow(tau, 7.0L/2.0L)' >>> ccode(sin(x), assign_to="s") 's = sin(x);' >>> custom_functions = { ... "ceiling": "CEIL", ... "Abs": [(lambda x: not x.is_integer, "fabs"), ... (lambda x: x.is_integer, "ABS")] ... } >>> ccode(Abs(x) + ceiling(x), user_functions=custom_functions) 'fabs(x) + CEIL(x)' """ return CCodePrinter(settings).doprint(expr, assign_to) def print_ccode(expr, **settings): """Prints C representation of the given expression.""" print(ccode(expr, **settings)) sympy-0.7.4.1/sympy/printing/mathml.py0000644000175000017500000003753712253362407020132 0ustar georgeskgeorgesk""" A MathML printer. """ from __future__ import print_function, division from sympy import sympify, S, Mul from sympy.core.function import _coeff_isneg from sympy.core.alphabets import greeks from sympy.core.compatibility import u from .printer import Printer from .pretty.pretty_symbology import greek_unicode from .conventions import split_super_sub, requires_partial 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() 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 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 from sympy.simplify import fraction 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(u("\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 greek_letters = set(greeks) # make a copy def translate(s): if s in greek_unicode: return greek_unicode.get(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') diff_symbol = self.mathml_tag(e) if requires_partial(e): diff_symbol = 'partialdiff' x.appendChild(self.dom.createElement(diff_symbol)) 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 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" % (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 def mathml(expr, **settings): """Returns the MathML representation of expr""" return MathMLPrinter(settings).doprint(expr) def print_mathml(expr, **settings): """ Prints a pretty representation of the MathML code for expr Examples ======== >>> ## >>> from sympy.printing.mathml import print_mathml >>> from sympy.abc import x >>> print_mathml(x+1) #doctest: +NORMALIZE_WHITESPACE x 1 """ s = MathMLPrinter(settings) xml = s._print(sympify(expr)) s.apply_patch() pretty_xml = xml.toprettyxml() s.restore_patch() print(pretty_xml) sympy-0.7.4.1/sympy/printing/python.py0000644000175000017500000000627012253362407020157 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- from __future__ import print_function, division import keyword as kw import sympy from .repr import ReprPrinter from .str import StrPrinter # A list of classes that should be printed using StrPrinter STRPRINT = ("Add", "Infinity", "Integer", "Mul", "NegativeInfinity", "Pow", "Zero") class PythonPrinter(ReprPrinter, StrPrinter): """A printer which converts an expression into its Python interpretation.""" def __init__(self, settings=None): ReprPrinter.__init__(self) StrPrinter.__init__(self, settings) self.symbols = [] self.functions = [] # Create print methods for classes that should use StrPrinter instead # of ReprPrinter. for name in STRPRINT: f_name = "_print_%s" % name f = getattr(StrPrinter, f_name) setattr(PythonPrinter, f_name, f) def _print_Function(self, expr): func = expr.func.__name__ if not hasattr(sympy, func) and not func in self.functions: self.functions.append(func) return StrPrinter._print_Function(self, expr) # procedure (!) for defining symbols which have be defined in print_python() def _print_Symbol(self, expr): symbol = self._str(expr) if symbol not in self.symbols: self.symbols.append(symbol) return StrPrinter._print_Symbol(self, expr) def _print_module(self, expr): raise ValueError('Modules in the expression are unacceptable') def python(expr, **settings): """Return Python interpretation of passed expression (can be passed to the exec() function without any modifications)""" printer = PythonPrinter(settings) exprp = printer.doprint(expr) result = '' # Returning found symbols and functions renamings = {} for symbolname in printer.symbols: newsymbolname = symbolname # Escape symbol names that are reserved python keywords if kw.iskeyword(newsymbolname): while True: newsymbolname += "_" if (newsymbolname not in printer.symbols and newsymbolname not in printer.functions): renamings[sympy.Symbol( symbolname)] = sympy.Symbol(newsymbolname) break result += newsymbolname + ' = Symbol(\'' + symbolname + '\')\n' for functionname in printer.functions: newfunctionname = functionname # Escape function names that are reserved python keywords if kw.iskeyword(newfunctionname): while True: newfunctionname += "_" if (newfunctionname not in printer.symbols and newfunctionname not in printer.functions): renamings[sympy.Function( functionname)] = sympy.Function(newfunctionname) break result += newfunctionname + ' = Function(\'' + functionname + '\')\n' if not len(renamings) == 0: exprp = expr.subs(renamings) result += 'e = ' + printer._str(exprp) return result def print_python(expr, **settings): """Print output of python() function""" print(python(expr, **settings)) sympy-0.7.4.1/sympy/printing/dot.py0000644000175000017500000001476412253362407017433 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import (Basic, Expr, Symbol, Integer, Rational, Float, default_sort_key, Add, Mul) __all__ = ['dotprint'] default_styles = [(Basic, {'color': 'blue', 'shape': 'ellipse'}), (Expr, {'color': 'black'})] sort_classes = (Add, Mul) slotClasses = (Symbol, Integer, Rational, Float) # XXX: Why not just use srepr()? def purestr(x): """ A string that follows obj = type(obj)(*obj.args) exactly """ if not isinstance(x, Basic): return str(x) if type(x) in slotClasses: args = [getattr(x, slot) for slot in x.__slots__] elif type(x) in sort_classes: args = sorted(x.args, key=default_sort_key) else: args = x.args return "%s(%s)"%(type(x).__name__, ', '.join(map(purestr, args))) def styleof(expr, styles=default_styles): """ Merge style dictionaries in order >>> from sympy import Symbol, Basic, Expr >>> from sympy.printing.dot import styleof >>> styles = [(Basic, {'color': 'blue', 'shape': 'ellipse'}), ... (Expr, {'color': 'black'})] >>> styleof(Basic(1), styles) {'color': 'blue', 'shape': 'ellipse'} >>> x = Symbol('x') >>> styleof(x + 1, styles) # this is an Expr {'color': 'black', 'shape': 'ellipse'} """ style = dict() for typ, sty in styles: if isinstance(expr, typ): style.update(sty) return style def attrprint(d, delimiter=', '): """ Print a dictionary of attributes >>> from sympy.printing.dot import attrprint >>> print(attrprint({'color': 'blue', 'shape': 'ellipse'})) "color"="blue", "shape"="ellipse" """ return delimiter.join('"%s"="%s"'%item for item in sorted(d.items())) def dotnode(expr, styles=default_styles, labelfunc=str, pos=(), repeat=True): """ String defining a node >>> from sympy.printing.dot import dotnode >>> from sympy.abc import x >>> print(dotnode(x)) "Symbol(x)_()" ["color"="black", "label"="x", "shape"="ellipse"]; """ style = styleof(expr, styles) if isinstance(expr, Basic) and not expr.is_Atom: label = str(expr.__class__.__name__) else: label = labelfunc(expr) style['label'] = label expr_str = purestr(expr) if repeat: expr_str += '_%s' % str(pos) return '"%s" [%s];' % (expr_str, attrprint(style)) def dotedges(expr, atom=lambda x: not isinstance(x, Basic), pos=(), repeat=True): """ List of strings for all expr->expr.arg pairs See the docstring of dotprint for explanations of the options. >>> from sympy.printing.dot import dotedges >>> from sympy.abc import x >>> for e in dotedges(x+2): ... print(e) "Add(Integer(2), Symbol(x))_()" -> "Integer(2)_(0,)"; "Add(Integer(2), Symbol(x))_()" -> "Symbol(x)_(1,)"; """ if atom(expr): return [] else: # TODO: This is quadratic in complexity (purestr(expr) already # contains [purestr(arg) for arg in expr.args]). expr_str = purestr(expr) arg_strs = [purestr(arg) for arg in expr.args] if repeat: expr_str += '_%s' % str(pos) arg_strs = [arg_str + '_%s' % str(pos + (i,)) for i, arg_str in enumerate(arg_strs)] return ['"%s" -> "%s";'%(expr_str, arg_str) for arg_str in arg_strs] template = \ """digraph{ # Graph style %(graphstyle)s ######### # Nodes # ######### %(nodes)s ######### # Edges # ######### %(edges)s }""" graphstyle = {'rankdir': 'TD', 'ordering': 'out'} def dotprint(expr, styles=default_styles, atom=lambda x: not isinstance(x, Basic), maxdepth=None, repeat=True, labelfunc=str, **kwargs): """ DOT description of a SymPy expression tree Options are ``styles``: Styles for different classes. The default is:: [(Basic, {'color': 'blue', 'shape': 'ellipse'}), (Expr, {'color': 'black'})]`` ``atom``: Function used to determine if an arg is an atom. The default is ``lambda x: not isinstance(x, Basic)``. Another good choice is ``lambda x: not x.args``. ``maxdepth``: The maximum depth. The default is None, meaning no limit. ``repeat``: Whether to different nodes for separate common subexpressions. The default is True. For example, for ``x + x*y`` with ``repeat=True``, it will have two nodes for ``x`` and with ``repeat=False``, it will have one (warning: even if it appears twice in the same object, like Pow(x, x), it will still only appear only once. Hence, with repeat=False, the number of arrows out of an object might not equal the number of args it has). ``labelfunc``: How to label leaf nodes. The default is ``str``. Another good option is ``srepr``. For example with ``str``, the leaf nodes of ``x + 1`` are labeled, ``x`` and ``1``. With ``srepr``, they are labeled ``Symbol('x')`` and ``Integer(1)``. Additional keyword arguments are included as styles for the graph. Examples ======== >>> from sympy.printing.dot import dotprint >>> from sympy.abc import x >>> print(dotprint(x+2)) # doctest: +NORMALIZE_WHITESPACE digraph{ # Graph style "ordering"="out" "rankdir"="TD" ######### # Nodes # ######### "Add(Integer(2), Symbol(x))_()" ["color"="black", "label"="Add", "shape"="ellipse"]; "Integer(2)_(0,)" ["color"="black", "label"="2", "shape"="ellipse"]; "Symbol(x)_(1,)" ["color"="black", "label"="x", "shape"="ellipse"]; ######### # Edges # ######### "Add(Integer(2), Symbol(x))_()" -> "Integer(2)_(0,)"; "Add(Integer(2), Symbol(x))_()" -> "Symbol(x)_(1,)"; } """ # repeat works by adding a signature tuple to the end of each node for its # position in the graph. For example, for expr = Add(x, Pow(x, 2)), the x in the # Pow will have the tuple (1, 0), meaning it is expr.args[1].args[0]. graphstyle.update(kwargs) nodes = [] edges = [] def traverse(e, depth, pos=()): nodes.append(dotnode(e, styles, labelfunc=labelfunc, pos=pos, repeat=repeat)) if maxdepth and depth >= maxdepth: return edges.extend(dotedges(e, atom=atom, pos=pos, repeat=repeat)) [traverse(arg, depth+1, pos + (i,)) for i, arg in enumerate(e.args) if not atom(arg)] traverse(expr, 0) return template%{'graphstyle': attrprint(graphstyle, delimiter='\n'), 'nodes': '\n'.join(nodes), 'edges': '\n'.join(edges)} sympy-0.7.4.1/sympy/printing/lambdarepr.py0000644000175000017500000000473612253362407020754 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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) 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__, str(expr.tolist())) _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) def _print_BooleanTrue(self, expr): return "True" def _print_BooleanFalse(self, expr): return "False" def lambdarepr(expr, **settings): """ Returns a string usable for lambdifying. """ return LambdaPrinter(settings).doprint(expr) sympy-0.7.4.1/sympy/printing/precedence.py0000644000175000017500000000561412253362407020734 0ustar georgeskgeorgesk"""A module providing information about the necessity of brackets""" from __future__ import print_function, division from sympy.core.function import _coeff_isneg # Default precedence values for some basic types PRECEDENCE = { "Lambda": 1, "Xor": 10, "Or": 20, "And": 30, "Relational": 35, "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 = { "Equivalent": PRECEDENCE["Xor"], "Xor": PRECEDENCE["Xor"], "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"] def precedence_PolyElement(item): if item.is_generator: return PRECEDENCE["Atom"] elif item.is_ground: return precedence(item.coeff(1)) elif item.is_term: return PRECEDENCE["Mul"] else: return PRECEDENCE["Add"] def precedence_FracElement(item): if item.denom == 1: return precedence_PolyElement(item.numer) else: return PRECEDENCE["Mul"] PRECEDENCE_FUNCTIONS = { "Integer": precedence_Integer, "Mul": precedence_Mul, "Rational": precedence_Rational, "Float": precedence_Float, "PolyElement": precedence_PolyElement, "FracElement": precedence_FracElement, } 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"] sympy-0.7.4.1/sympy/printing/repr.py0000644000175000017500000001377012253362407017611 0ustar georgeskgeorgesk""" 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 __future__ import print_function, division from sympy.core.function import AppliedUndef from .printer import Printer import sympy.mpmath.libmp as mlib from sympy.mpmath.libmp import prec_to_dps, repr_dps class ReprPrinter(Printer): printmethod = "_sympyrepr" _default_settings = { "order": None } 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]) 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 = 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): if issubclass(expr, AppliedUndef): return 'Function(%r)' % (expr.__name__) else: return 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_BooleanTrue(self, expr): return "S.true" def _print_BooleanFalse(self, expr): return "S.false" def _print_NaN(self, expr): return "nan" 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 = map(self._print, args) return "Mul(%s)" % ", ".join(args) def _print_Rational(self, expr): return 'Rational(%s, %s)' % (self._print(expr.p), self._print(expr.q)) def _print_PythonRational(self, expr): return "%s(%d, %d)" % (expr.__class__.__name__, expr.p, expr.q) 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)) def _print_PolyRing(self, ring): return "%s(%s, %s, %s)" % (ring.__class__.__name__, self._print(ring.symbols), self._print(ring.domain), self._print(ring.order)) def _print_FracField(self, field): return "%s(%s, %s, %s)" % (field.__class__.__name__, self._print(field.symbols), self._print(field.domain), self._print(field.order)) def _print_PolyElement(self, poly): terms = list(poly.terms()) terms.sort(key=poly.ring.order, reverse=True) return "%s(%s, %s)" % (poly.__class__.__name__, self._print(poly.ring), self._print(terms)) def _print_FracElement(self, frac): numer_terms = list(frac.numer.terms()) numer_terms.sort(key=frac.field.order, reverse=True) denom_terms = list(frac.denom.terms()) denom_terms.sort(key=frac.field.order, reverse=True) numer = self._print(numer_terms) denom = self._print(denom_terms) return "%s(%s, %s, %s)" % (frac.__class__.__name__, self._print(frac.field), numer, denom) def srepr(expr, **settings): """return expr in repr form""" return ReprPrinter(settings).doprint(expr) sympy-0.7.4.1/sympy/printing/printer.py0000644000175000017500000002217712253362407020325 0ustar georgeskgeorgesk"""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_ 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 __future__ import print_function, division from sympy import Basic, Add from sympy.core.core import BasicMeta from sympy.core.compatibility import cmp_to_key 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_`` 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 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.") def doprint(self, expr): """Returns printer's representation for expr (as a string)""" return self._str(self._print(expr)) def _print(self, expr, *args, **kwargs): """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, **kwargs) # 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, **kwargs) # 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) sympy-0.7.4.1/sympy/printing/tests/0000755000175000017500000000000012253362407017421 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/printing/tests/test_jscode.py0000644000175000017500000002102112253362407022275 0ustar georgeskgeorgeskfrom sympy.core import pi, oo, symbols, Function, Rational, Integer, GoldenRatio, EulerGamma, Catalan, Lambda, Dummy from sympy.functions import Piecewise, sin, cos, Abs, exp, ceiling, sqrt from sympy.utilities.pytest import raises from sympy.printing.jscode import JavascriptCodePrinter from sympy.utilities.lambdify import implemented_function from sympy.tensor import IndexedBase, Idx # import test from sympy import jscode x, y, z = symbols('x,y,z') g = Function('g') def test_printmethod(): assert jscode(Abs(x)) == "Math.abs(x)" def test_jscode_sqrt(): assert jscode(sqrt(x)) == "Math.sqrt(x)" assert jscode(x**0.5) == "Math.sqrt(x)" assert jscode(sqrt(x)) == "Math.sqrt(x)" def test_jscode_Pow(): assert jscode(x**3) == "Math.pow(x, 3)" assert jscode(x**(y**3)) == "Math.pow(x, Math.pow(y, 3))" assert jscode(1/(g(x)*3.5)**(x - y**x)/(x**2 + y)) == \ "Math.pow(3.5*g(x), -x + Math.pow(y, x))/(Math.pow(x, 2) + y)" assert jscode(x**-1.0) == '1/x' def test_jscode_constants_mathh(): assert jscode(exp(1)) == "Math.E" assert jscode(pi) == "Math.PI" assert jscode(oo) == "Number.POSITIVE_INFINITY" assert jscode(-oo) == "Number.NEGATIVE_INFINITY" def test_jscode_constants_other(): assert jscode( 2*GoldenRatio) == "var GoldenRatio = 1.61803398874989;\n2*GoldenRatio" assert jscode(2*Catalan) == "var Catalan = 0.915965594177219;\n2*Catalan" assert jscode( 2*EulerGamma) == "var EulerGamma = 0.577215664901533;\n2*EulerGamma" def test_jscode_Rational(): assert jscode(Rational(3, 7)) == "3/7" assert jscode(Rational(18, 9)) == "2" assert jscode(Rational(3, -7)) == "-3/7" assert jscode(Rational(-3, -7)) == "3/7" def test_jscode_Integer(): assert jscode(Integer(67)) == "67" assert jscode(Integer(-1)) == "-1" def test_jscode_functions(): assert jscode(sin(x) ** cos(x)) == "Math.pow(Math.sin(x), Math.cos(x))" def test_jscode_inline_function(): x = symbols('x') g = implemented_function('g', Lambda(x, 2*x)) assert jscode(g(x)) == "2*x" g = implemented_function('g', Lambda(x, 2*x/Catalan)) assert jscode(g(x)) == "var Catalan = %s;\n2*x/Catalan" % Catalan.n() A = IndexedBase('A') i = Idx('i', symbols('n', integer=True)) g = implemented_function('g', Lambda(x, x*(1 + x)*(2 + x))) assert jscode(g(A[i]), assign_to=A[i]) == ( "for (var i=0; i "Integer(2)";', '"Add(Integer(2), Symbol(x))" -> "Symbol(x)";' ] assert sorted(dotedges(x + 2, repeat=True)) == [ '"Add(Integer(2), Symbol(x))_()" -> "Integer(2)_(0,)";', '"Add(Integer(2), Symbol(x))_()" -> "Symbol(x)_(1,)";' ] def test_dotprint(): text = dotprint(x+2, repeat=False) assert all(e in text for e in dotedges(x+2, repeat=False)) assert all(n in text for n in map(lambda expr: dotnode(expr, repeat=False), (x, Integer(2), x+2))) assert 'digraph' in text text = dotprint(x+x**2, repeat=False) assert all(e in text for e in dotedges(x+x**2, repeat=False)) assert all(n in text for n in map(lambda expr: dotnode(expr, repeat=False), (x, Integer(2), x**2))) assert 'digraph' in text text = dotprint(x+x**2, repeat=True) assert all(e in text for e in dotedges(x+x**2, repeat=True)) assert all(n in text for n in map(lambda expr: dotnode(expr, pos=()), [x + x**2])) text = dotprint(x**x, repeat=True) assert all(e in text for e in dotedges(x**x, repeat=True)) assert all(n in text for n in [dotnode(x, pos=(0,)), dotnode(x, pos=(1,))]) assert 'digraph' in text def test_dotprint_depth(): text = dotprint(3*x+2, depth=1) assert dotnode(3*x+2) in text assert dotnode(x) not in text def test_Matrix_and_non_basics(): from sympy import MatrixSymbol n = Symbol('n') assert dotprint(MatrixSymbol('X', n, n)) def test_labelfunc(): text = dotprint(x + 2, labelfunc=srepr) assert "Symbol('x')" in text assert "Integer(2)" in text sympy-0.7.4.1/sympy/printing/tests/test_gtk.py0000644000175000017500000000066512253362407021626 0ustar georgeskgeorgeskfrom sympy import print_gtk, sin from sympy.utilities.pytest import XFAIL, raises # this test fails if python-lxml isn't installed. We don't want to depend on # anything with SymPy @XFAIL def test_1(): from sympy.abc import x print_gtk(x**2, start_viewer=False) print_gtk(x**2 + sin(x)/4, start_viewer=False) def test_settings(): from sympy.abc import x raises(TypeError, lambda: print_gtk(x, method="garbage")) sympy-0.7.4.1/sympy/printing/tests/test_python.py0000644000175000017500000001642012253362407022356 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- from sympy import (Symbol, symbols, oo, limit, Rational, Integral, Derivative, log, exp, sqrt, pi, Function, sin, Eq, Ge, Le, Gt, Lt, Ne, Abs, conjugate, I) from sympy.printing.python import python from sympy.utilities.pytest import raises, XFAIL x, y = symbols('x,y') th = Symbol('theta') ph = Symbol('phi') def test_python_basic(): # Simple numbers/symbols assert python(-Rational(1)/2) == "e = Rational(-1, 2)" assert python(-Rational(13)/22) == "e = Rational(-13, 22)" assert python(oo) == "e = oo" # Powers assert python((x**2)) == "x = Symbol(\'x\')\ne = x**2" assert python(1/x) == "x = Symbol('x')\ne = 1/x" assert python(y*x**-2) == "y = Symbol('y')\nx = Symbol('x')\ne = y/x**2" assert python( x**Rational(-5, 2)) == "x = Symbol('x')\ne = x**Rational(-5, 2)" # Sums of terms assert python((x**2 + x + 1)) in [ "x = Symbol('x')\ne = 1 + x + x**2", "x = Symbol('x')\ne = x + x**2 + 1", "x = Symbol('x')\ne = x**2 + x + 1", ] assert python(1 - x) in [ "x = Symbol('x')\ne = 1 - x", "x = Symbol('x')\ne = -x + 1"] assert python(1 - 2*x) in [ "x = Symbol('x')\ne = 1 - 2*x", "x = Symbol('x')\ne = -2*x + 1"] assert python(1 - Rational(3, 2)*y/x) in [ "y = Symbol('y')\nx = Symbol('x')\ne = 1 - 3/2*y/x", "y = Symbol('y')\nx = Symbol('x')\ne = -3/2*y/x + 1", "y = Symbol('y')\nx = Symbol('x')\ne = 1 - 3*y/(2*x)"] # Multiplication assert python(x/y) == "x = Symbol('x')\ny = Symbol('y')\ne = x/y" assert python(-x/y) == "x = Symbol('x')\ny = Symbol('y')\ne = -x/y" assert python((x + 2)/y) in [ "y = Symbol('y')\nx = Symbol('x')\ne = 1/y*(2 + x)", "y = Symbol('y')\nx = Symbol('x')\ne = 1/y*(x + 2)", "x = Symbol('x')\ny = Symbol('y')\ne = 1/y*(2 + x)", "x = Symbol('x')\ny = Symbol('y')\ne = (2 + x)/y", "x = Symbol('x')\ny = Symbol('y')\ne = (x + 2)/y"] assert python((1 + x)*y) in [ "y = Symbol('y')\nx = Symbol('x')\ne = y*(1 + x)", "y = Symbol('y')\nx = Symbol('x')\ne = y*(x + 1)", ] # Check for proper placement of negative sign assert python(-5*x/(x + 10)) == "x = Symbol('x')\ne = -5*x/(x + 10)" assert python(1 - Rational(3, 2)*(x + 1)) in [ "x = Symbol('x')\ne = Rational(-3, 2)*x + Rational(-1, 2)", "x = Symbol('x')\ne = -3*x/2 + Rational(-1, 2)", "x = Symbol('x')\ne = -3*x/2 + Rational(-1, 2)" ] def test_python_keyword_symbol_name_escaping(): # Check for escaping of keywords assert python( 5*Symbol("lambda")) == "lambda_ = Symbol('lambda')\ne = 5*lambda_" assert (python(5*Symbol("lambda") + 7*Symbol("lambda_")) == "lambda__ = Symbol('lambda')\nlambda_ = Symbol('lambda_')\ne = 7*lambda_ + 5*lambda__") assert (python(5*Symbol("for") + Function("for_")(8)) == "for__ = Symbol('for')\nfor_ = Function('for_')\ne = 5*for__ + for_(8)") def test_python_keyword_function_name_escaping(): assert python( 5*Function("for")(8)) == "for_ = Function('for')\ne = 5*for_(8)" def test_python_relational(): assert python(Eq(x, y)) == "x = Symbol('x')\ny = Symbol('y')\ne = x == y" assert python(Ge(x, y)) == "x = Symbol('x')\ny = Symbol('y')\ne = x >= y" assert python(Le(x, y)) == "x = Symbol('x')\ny = Symbol('y')\ne = x <= y" assert python(Gt(x, y)) == "x = Symbol('x')\ny = Symbol('y')\ne = x > y" assert python(Lt(x, y)) == "x = Symbol('x')\ny = Symbol('y')\ne = x < y" assert python(Ne(x/(y + 1), y**2)) in [ "x = Symbol('x')\ny = Symbol('y')\ne = x/(1 + y) != y**2", "x = Symbol('x')\ny = Symbol('y')\ne = x/(y + 1) != y**2"] def test_python_functions(): # Simple assert python((2*x + exp(x))) in "x = Symbol('x')\ne = 2*x + exp(x)" assert python(sqrt(2)) == 'e = sqrt(2)' assert python(2**Rational(1, 3)) == 'e = 2**Rational(1, 3)' assert python(sqrt(2 + pi)) == 'e = sqrt(2 + pi)' assert python((2 + pi)**Rational(1, 3)) == 'e = (2 + pi)**Rational(1, 3)' assert python(2**Rational(1, 4)) == 'e = 2**Rational(1, 4)' assert python(Abs(x)) == "x = Symbol('x')\ne = Abs(x)" assert python( Abs(x/(x**2 + 1))) in ["x = Symbol('x')\ne = Abs(x/(1 + x**2))", "x = Symbol('x')\ne = Abs(x/(x**2 + 1))"] # Univariate/Multivariate functions f = Function('f') assert python(f(x)) == "x = Symbol('x')\nf = Function('f')\ne = f(x)" assert python(f(x, y)) == "x = Symbol('x')\ny = Symbol('y')\nf = Function('f')\ne = f(x, y)" assert python(f(x/(y + 1), y)) in [ "x = Symbol('x')\ny = Symbol('y')\nf = Function('f')\ne = f(x/(1 + y), y)", "x = Symbol('x')\ny = Symbol('y')\nf = Function('f')\ne = f(x/(y + 1), y)"] # Nesting of square roots assert python(sqrt((sqrt(x + 1)) + 1)) in [ "x = Symbol('x')\ne = sqrt(1 + sqrt(1 + x))", "x = Symbol('x')\ne = sqrt(sqrt(x + 1) + 1)"] # Nesting of powers assert python((((x + 1)**Rational(1, 3)) + 1)**Rational(1, 3)) in [ "x = Symbol('x')\ne = (1 + (1 + x)**Rational(1, 3))**Rational(1, 3)", "x = Symbol('x')\ne = ((x + 1)**Rational(1, 3) + 1)**Rational(1, 3)"] # Function powers assert python(sin(x)**2) == "x = Symbol('x')\ne = sin(x)**2" @XFAIL def test_python_functions_conjugates(): a, b = map(Symbol, 'ab') assert python( conjugate(a + b*I) ) == '_ _\na - I*b' assert python( conjugate(exp(a + b*I)) ) == ' _ _\n a - I*b\ne ' def test_python_derivatives(): # Simple f_1 = Derivative(log(x), x, evaluate=False) assert python(f_1) == "x = Symbol('x')\ne = Derivative(log(x), x)" f_2 = Derivative(log(x), x, evaluate=False) + x assert python(f_2) == "x = Symbol('x')\ne = x + Derivative(log(x), x)" # Multiple symbols f_3 = Derivative(log(x) + x**2, x, y, evaluate=False) assert python(f_3) == \ "x = Symbol('x')\ny = Symbol('y')\ne = Derivative(x**2 + log(x), x, y)" f_4 = Derivative(2*x*y, y, x, evaluate=False) + x**2 assert python(f_4) in [ "x = Symbol('x')\ny = Symbol('y')\ne = x**2 + Derivative(2*x*y, y, x)", "x = Symbol('x')\ny = Symbol('y')\ne = Derivative(2*x*y, y, x) + x**2"] def test_python_integrals(): # Simple f_1 = Integral(log(x), x) assert python(f_1) == "x = Symbol('x')\ne = Integral(log(x), x)" f_2 = Integral(x**2, x) assert python(f_2) == "x = Symbol('x')\ne = Integral(x**2, x)" # Double nesting of pow f_3 = Integral(x**(2**x), x) assert python(f_3) == "x = Symbol('x')\ne = Integral(x**(2**x), x)" # Definite integrals f_4 = Integral(x**2, (x, 1, 2)) assert python(f_4) == "x = Symbol('x')\ne = Integral(x**2, (x, 1, 2))" f_5 = Integral(x**2, (x, Rational(1, 2), 10)) assert python( f_5) == "x = Symbol('x')\ne = Integral(x**2, (x, Rational(1, 2), 10))" # Nested integrals f_6 = Integral(x**2*y**2, x, y) assert python(f_6) == "x = Symbol('x')\ny = Symbol('y')\ne = Integral(x**2*y**2, x, y)" # Not implemented yet #def test_python_matrix(): # p = python(Matrix([[x**2+1, 1], [y, x+y]])) # s = '' # assert p == s def test_python_limits(): assert python(limit(x, x, oo)) == 'e = oo' assert python(limit(x**2, x, 0)) == 'e = 0' def test_settings(): raises(TypeError, lambda: python(x, method="garbage")) sympy-0.7.4.1/sympy/printing/tests/test_conventions.py0000644000175000017500000000730412253362407023403 0ustar georgeskgeorgeskfrom sympy import symbols, Derivative, Integral, exp, cos, oo, Function from sympy.functions.special.bessel import besselj from sympy.functions.special.polynomials import legendre from sympy.functions.combinatorial.numbers import bell from sympy.printing.conventions import split_super_sub, requires_partial def test_super_sub(): assert split_super_sub("beta_13_2") == ("beta", [], ["13", "2"]) assert split_super_sub("beta_132_20") == ("beta", [], ["132", "20"]) assert split_super_sub("beta_13") == ("beta", [], ["13"]) assert split_super_sub("x_a_b") == ("x", [], ["a", "b"]) assert split_super_sub("x_1_2_3") == ("x", [], ["1", "2", "3"]) assert split_super_sub("x_a_b1") == ("x", [], ["a", "b1"]) assert split_super_sub("x_a_1") == ("x", [], ["a", "1"]) assert split_super_sub("x_1_a") == ("x", [], ["1", "a"]) assert split_super_sub("x_1^aa") == ("x", ["aa"], ["1"]) assert split_super_sub("x_1__aa") == ("x", ["aa"], ["1"]) assert split_super_sub("x_11^a") == ("x", ["a"], ["11"]) assert split_super_sub("x_11__a") == ("x", ["a"], ["11"]) assert split_super_sub("x_a_b_c_d") == ("x", [], ["a", "b", "c", "d"]) assert split_super_sub("x_a_b^c^d") == ("x", ["c", "d"], ["a", "b"]) assert split_super_sub("x_a_b__c__d") == ("x", ["c", "d"], ["a", "b"]) assert split_super_sub("x_a^b_c^d") == ("x", ["b", "d"], ["a", "c"]) assert split_super_sub("x_a__b_c__d") == ("x", ["b", "d"], ["a", "c"]) assert split_super_sub("x^a^b_c_d") == ("x", ["a", "b"], ["c", "d"]) assert split_super_sub("x__a__b_c_d") == ("x", ["a", "b"], ["c", "d"]) assert split_super_sub("x^a^b^c^d") == ("x", ["a", "b", "c", "d"], []) assert split_super_sub("x__a__b__c__d") == ("x", ["a", "b", "c", "d"], []) assert split_super_sub("alpha_11") == ("alpha", [], ["11"]) assert split_super_sub("alpha_11_11") == ("alpha", [], ["11", "11"]) def test_requires_partial(): x, y, z, t, nu = symbols('x y z t nu') n = symbols('n', integer=True) f = x * y assert requires_partial(Derivative(f, x)) is True assert requires_partial(Derivative(f, y)) is True ## integrating out one of the variables assert requires_partial(Derivative(Integral(exp(-x * y), (x, 0, oo)), y, evaluate=False)) is False ## bessel function with smooth parameter f = besselj(nu, x) assert requires_partial(Derivative(f, x)) is True assert requires_partial(Derivative(f, nu)) is True ## bessel function with integer parameter f = besselj(n, x) assert requires_partial(Derivative(f, x)) is False # this is not really valid (differentiating with respect to an integer) # but there's no reason to use the partial derivative symbol there. make # sure we don't throw an exception here, though assert requires_partial(Derivative(f, n)) is False ## bell polynomial f = bell(n, x) assert requires_partial(Derivative(f, x)) is False # again, invalid assert requires_partial(Derivative(f, n)) is False ## legendre polynomial f = legendre(0, x) assert requires_partial(Derivative(f, x)) is False f = legendre(n, x) assert requires_partial(Derivative(f, x)) is False # again, invalid assert requires_partial(Derivative(f, n)) is False f = x ** n assert requires_partial(Derivative(f, x)) is False assert requires_partial(Derivative(Integral((x*y) ** n * exp(-x * y), (x, 0, oo)), y, evaluate=False)) is False # parametric equation f = (exp(t), cos(t)) g = sum(f) assert requires_partial(Derivative(g, t)) is False # function of unspecified variables f = symbols('f', cls=Function) assert requires_partial(Derivative(f, x)) is False assert requires_partial(Derivative(f, x, y)) is True sympy-0.7.4.1/sympy/printing/tests/test_fcode.py0000644000175000017500000006424612253362407022126 0ustar georgeskgeorgeskfrom sympy import sin, cos, atan2, log, exp, gamma, conjugate, sqrt, \ factorial, Integral, Piecewise, Add, diff, symbols, S, Float, Dummy from sympy import Catalan, EulerGamma, E, GoldenRatio, I, pi from sympy import Function, Rational, Integer, Lambda from sympy.core.relational import Relational from sympy.logic.boolalg import And, Or, Not, Equivalent, Xor from sympy.printing.fcode import fcode, FCodePrinter from sympy.tensor import IndexedBase, Idx from sympy.utilities.lambdify import implemented_function from sympy.utilities.pytest import raises from sympy.core.compatibility import xrange def test_printmethod(): x = symbols('x') class nint(Function): def _fcode(self, printer): return "nint(%s)" % printer._print(self.args[0]) assert fcode(nint(x)) == " nint(x)" def test_fcode_Pow(): x, y = symbols('x,y') n = symbols('n', integer=True) assert fcode(x**3) == " x**3" assert fcode(x**(y**3)) == " x**(y**3)" assert fcode(1/(sin(x)*3.5)**(x - y**x)/(x**2 + y)) == \ " (3.5d0*sin(x))**(-x + y**x)/(x**2 + y)" assert fcode(sqrt(x)) == ' sqrt(x)' assert fcode(sqrt(n)) == ' sqrt(dble(n))' assert fcode(x**0.5) == ' sqrt(x)' assert fcode(sqrt(x)) == ' sqrt(x)' assert fcode(sqrt(10)) == ' sqrt(10.0d0)' assert fcode(x**-1.0) == ' 1.0/x' assert fcode(x**-2.0, assign_to='y', source_format='free', human=True) == 'y = x**(-2.0d0)' # 2823 assert fcode(x**Rational(3, 7)) == ' x**(3.0d0/7.0d0)' def test_fcode_Rational(): x = symbols('x') assert fcode(Rational(3, 7)) == " 3.0d0/7.0d0" assert fcode(Rational(18, 9)) == " 2" assert fcode(Rational(3, -7)) == " -3.0d0/7.0d0" assert fcode(Rational(-3, -7)) == " 3.0d0/7.0d0" assert fcode(x + Rational(3, 7)) == " x + 3.0d0/7.0d0" assert fcode(Rational(3, 7)*x) == " (3.0d0/7.0d0)*x" def test_fcode_Integer(): assert fcode(Integer(67)) == " 67" assert fcode(Integer(-1)) == " -1" def test_fcode_Float(): assert fcode(Float(42.0)) == " 42.0000000000000d0" assert fcode(Float(-1e20)) == " -1.00000000000000d+20" def test_fcode_functions(): x, y = symbols('x,y') assert fcode(sin(x) ** cos(y)) == " sin(x)**cos(y)" #issue 3715 def test_fcode_functions_with_integers(): x= symbols('x') assert fcode(x * log(10)) == " x*log(10.0d0)" assert fcode(x * log(S(10))) == " x*log(10.0d0)" assert fcode(log(S(10))) == " log(10.0d0)" assert fcode(exp(10)) == " exp(10.0d0)" assert fcode(x * log(log(10))) == " x*log(2.30258509299405d0)" assert fcode(x * log(log(S(10)))) == " x*log(2.30258509299405d0)" def test_fcode_NumberSymbol(): p = FCodePrinter() assert fcode(Catalan) == ' parameter (Catalan = 0.915965594177219d0)\n Catalan' assert fcode(EulerGamma) == ' parameter (EulerGamma = 0.577215664901533d0)\n EulerGamma' assert fcode(E) == ' parameter (E = 2.71828182845905d0)\n E' assert fcode(GoldenRatio) == ' parameter (GoldenRatio = 1.61803398874989d0)\n GoldenRatio' assert fcode(pi) == ' parameter (pi = 3.14159265358979d0)\n pi' assert fcode( pi, precision=5) == ' parameter (pi = 3.1416d0)\n pi' assert fcode(Catalan, human=False) == (set( [(Catalan, p._print(Catalan.evalf(15)))]), set([]), ' Catalan') assert fcode(EulerGamma, human=False) == (set([(EulerGamma, p._print( EulerGamma.evalf(15)))]), set([]), ' EulerGamma') assert fcode(E, human=False) == ( set([(E, p._print(E.evalf(15)))]), set([]), ' E') assert fcode(GoldenRatio, human=False) == (set([(GoldenRatio, p._print( GoldenRatio.evalf(15)))]), set([]), ' GoldenRatio') assert fcode(pi, human=False) == ( set([(pi, p._print(pi.evalf(15)))]), set([]), ' pi') assert fcode(pi, precision=5, human=False) == ( set([(pi, p._print(pi.evalf(5)))]), set([]), ' pi') def test_fcode_complex(): assert fcode(I) == " cmplx(0,1)" x = symbols('x') assert fcode(4*I) == " cmplx(0,4)" assert fcode(3 + 4*I) == " cmplx(3,4)" assert fcode(3 + 4*I + x) == " cmplx(3,4) + x" assert fcode(I*x) == " cmplx(0,1)*x" assert fcode(3 + 4*I - x) == " cmplx(3,4) - x" x = symbols('x', imaginary=True) assert fcode(5*x) == " 5*x" assert fcode(I*x) == " cmplx(0,1)*x" assert fcode(3 + x) == " x + 3" def test_implicit(): x, y = symbols('x,y') assert fcode(sin(x)) == " sin(x)" assert fcode(atan2(x, y)) == " atan2(x, y)" assert fcode(conjugate(x)) == " conjg(x)" def test_not_fortran(): x = symbols('x') g = Function('g') assert fcode( gamma(x)) == "C Not Fortran:\nC gamma(x)\n gamma(x)" assert fcode(Integral(sin(x))) == "C Not Fortran:\nC Integral(sin(x), x)\n Integral(sin(x), x)" assert fcode(g(x)) == "C Not Fortran:\nC g(x)\n g(x)" def test_user_functions(): x = symbols('x') assert fcode(sin(x), user_functions={sin: "zsin"}) == " zsin(x)" x = symbols('x') assert fcode( gamma(x), user_functions={gamma: "mygamma"}) == " mygamma(x)" g = Function('g') assert fcode(g(x), user_functions={g: "great"}) == " great(x)" n = symbols('n', integer=True) assert fcode( factorial(n), user_functions={factorial: "fct"}) == " fct(n)" def test_inline_function(): x = symbols('x') g = implemented_function('g', Lambda(x, 2*x)) assert fcode(g(x)) == " 2*x" g = implemented_function('g', Lambda(x, 2*pi/x)) assert fcode(g(x)) == ( " parameter (pi = 3.14159265358979d0)\n" " 2*pi/x" ) A = IndexedBase('A') i = Idx('i', symbols('n', integer=True)) g = implemented_function('g', Lambda(x, x*(1 + x)*(2 + x))) assert fcode(g(A[i]), assign_to=A[i]) == ( " do i = 1, n\n" " A(i) = A(i)*(1 + A(i))*(2 + A(i))\n" " end do" ) def test_assign_to(): x = symbols('x') assert fcode(sin(x), assign_to="s") == " s = sin(x)" def test_line_wrapping(): x, y = symbols('x,y') assert fcode(((x + y)**10).expand(), assign_to="var") == ( " var = x**10 + 10*x**9*y + 45*x**8*y**2 + 120*x**7*y**3 + 210*x**6*\n" " @ y**4 + 252*x**5*y**5 + 210*x**4*y**6 + 120*x**3*y**7 + 45*x**2*y\n" " @ **8 + 10*x*y**9 + y**10" ) e = [x**i for i in range(11)] assert fcode(Add(*e)) == ( " x**10 + x**9 + x**8 + x**7 + x**6 + x**5 + x**4 + x**3 + x**2 + x\n" " @ + 1" ) def test_fcode_precedence(): x, y = symbols("x y") assert fcode(And(x < y, y < x + 1), source_format="free") == \ "x < y .and. y < x + 1" assert fcode(Or(x < y, y < x + 1), source_format="free") == \ "x < y .or. y < x + 1" assert fcode(Xor(x < y, y < x + 1, evaluate=False), source_format="free") == "x < y .neqv. y < x + 1" assert fcode(Equivalent(x < y, y < x + 1), source_format="free") == \ "x < y .eqv. y < x + 1" def test_fcode_Logical(): x, y, z = symbols("x y z") # unary Not assert fcode(Not(x), source_format="free") == ".not. x" # binary And assert fcode(And(x, y), source_format="free") == "x .and. y" assert fcode(And(x, Not(y)), source_format="free") == "x .and. .not. y" assert fcode(And(Not(x), y), source_format="free") == "y .and. .not. x" assert fcode(And(Not(x), Not(y)), source_format="free") == \ ".not. x .and. .not. y" assert fcode(Not(And(x, y), evaluate=False), source_format="free") == \ ".not. (x .and. y)" # binary Or assert fcode(Or(x, y), source_format="free") == "x .or. y" assert fcode(Or(x, Not(y)), source_format="free") == "x .or. .not. y" assert fcode(Or(Not(x), y), source_format="free") == "y .or. .not. x" assert fcode(Or(Not(x), Not(y)), source_format="free") == \ ".not. x .or. .not. y" assert fcode(Not(Or(x, y), evaluate=False), source_format="free") == \ ".not. (x .or. y)" # mixed And/Or assert fcode(And(Or(y, z), x), source_format="free") == "x .and. (y .or. z)" assert fcode(And(Or(z, x), y), source_format="free") == "y .and. (x .or. z)" assert fcode(And(Or(x, y), z), source_format="free") == "z .and. (x .or. y)" assert fcode(Or(And(y, z), x), source_format="free") == "x .or. y .and. z" assert fcode(Or(And(z, x), y), source_format="free") == "y .or. x .and. z" assert fcode(Or(And(x, y), z), source_format="free") == "z .or. x .and. y" # trinary And assert fcode(And(x, y, z), source_format="free") == "x .and. y .and. z" assert fcode(And(x, y, Not(z)), source_format="free") == \ "x .and. y .and. .not. z" assert fcode(And(x, Not(y), z), source_format="free") == \ "x .and. z .and. .not. y" assert fcode(And(Not(x), y, z), source_format="free") == \ "y .and. z .and. .not. x" assert fcode(Not(And(x, y, z), evaluate=False), source_format="free") == \ ".not. (x .and. y .and. z)" # trinary Or assert fcode(Or(x, y, z), source_format="free") == "x .or. y .or. z" assert fcode(Or(x, y, Not(z)), source_format="free") == \ "x .or. y .or. .not. z" assert fcode(Or(x, Not(y), z), source_format="free") == \ "x .or. z .or. .not. y" assert fcode(Or(Not(x), y, z), source_format="free") == \ "y .or. z .or. .not. x" assert fcode(Not(Or(x, y, z), evaluate=False), source_format="free") == \ ".not. (x .or. y .or. z)" def test_fcode_Xlogical(): x, y, z = symbols("x y z") # binary Xor assert fcode(Xor(x, y, evaluate=False), source_format="free") == \ "x .neqv. y" assert fcode(Xor(x, Not(y), evaluate=False), source_format="free") == \ "x .neqv. .not. y" assert fcode(Xor(Not(x), y, evaluate=False), source_format="free") == \ "y .neqv. .not. x" assert fcode(Xor(Not(x), Not(y), evaluate=False), source_format="free") == ".not. x .neqv. .not. y" assert fcode(Not(Xor(x, y, evaluate=False), evaluate=False), source_format="free") == ".not. (x .neqv. y)" # binary Equivalent assert fcode(Equivalent(x, y), source_format="free") == "x .eqv. y" assert fcode(Equivalent(x, Not(y)), source_format="free") == \ "x .eqv. .not. y" assert fcode(Equivalent(Not(x), y), source_format="free") == \ "y .eqv. .not. x" assert fcode(Equivalent(Not(x), Not(y)), source_format="free") == \ ".not. x .eqv. .not. y" assert fcode(Not(Equivalent(x, y), evaluate=False), source_format="free") == ".not. (x .eqv. y)" # mixed And/Equivalent assert fcode(Equivalent(And(y, z), x), source_format="free") == \ "x .eqv. y .and. z" assert fcode(Equivalent(And(z, x), y), source_format="free") == \ "y .eqv. x .and. z" assert fcode(Equivalent(And(x, y), z), source_format="free") == \ "z .eqv. x .and. y" assert fcode(And(Equivalent(y, z), x), source_format="free") == \ "x .and. (y .eqv. z)" assert fcode(And(Equivalent(z, x), y), source_format="free") == \ "y .and. (x .eqv. z)" assert fcode(And(Equivalent(x, y), z), source_format="free") == \ "z .and. (x .eqv. y)" # mixed Or/Equivalent assert fcode(Equivalent(Or(y, z), x), source_format="free") == \ "x .eqv. y .or. z" assert fcode(Equivalent(Or(z, x), y), source_format="free") == \ "y .eqv. x .or. z" assert fcode(Equivalent(Or(x, y), z), source_format="free") == \ "z .eqv. x .or. y" assert fcode(Or(Equivalent(y, z), x), source_format="free") == \ "x .or. (y .eqv. z)" assert fcode(Or(Equivalent(z, x), y), source_format="free") == \ "y .or. (x .eqv. z)" assert fcode(Or(Equivalent(x, y), z), source_format="free") == \ "z .or. (x .eqv. y)" # mixed Xor/Equivalent assert fcode(Equivalent(Xor(y, z, evaluate=False), x), source_format="free") == "x .eqv. (y .neqv. z)" assert fcode(Equivalent(Xor(z, x, evaluate=False), y), source_format="free") == "y .eqv. (x .neqv. z)" assert fcode(Equivalent(Xor(x, y, evaluate=False), z), source_format="free") == "z .eqv. (x .neqv. y)" assert fcode(Xor(Equivalent(y, z), x, evaluate=False), source_format="free") == "x .neqv. (y .eqv. z)" assert fcode(Xor(Equivalent(z, x), y, evaluate=False), source_format="free") == "y .neqv. (x .eqv. z)" assert fcode(Xor(Equivalent(x, y), z, evaluate=False), source_format="free") == "z .neqv. (x .eqv. y)" # mixed And/Xor assert fcode(Xor(And(y, z), x, evaluate=False), source_format="free") == \ "x .neqv. y .and. z" assert fcode(Xor(And(z, x), y, evaluate=False), source_format="free") == \ "y .neqv. x .and. z" assert fcode(Xor(And(x, y), z, evaluate=False), source_format="free") == \ "z .neqv. x .and. y" assert fcode(And(Xor(y, z, evaluate=False), x), source_format="free") == \ "x .and. (y .neqv. z)" assert fcode(And(Xor(z, x, evaluate=False), y), source_format="free") == \ "y .and. (x .neqv. z)" assert fcode(And(Xor(x, y, evaluate=False), z), source_format="free") == \ "z .and. (x .neqv. y)" # mixed Or/Xor assert fcode(Xor(Or(y, z), x, evaluate=False), source_format="free") == \ "x .neqv. y .or. z" assert fcode(Xor(Or(z, x), y, evaluate=False), source_format="free") == \ "y .neqv. x .or. z" assert fcode(Xor(Or(x, y), z, evaluate=False), source_format="free") == \ "z .neqv. x .or. y" assert fcode(Or(Xor(y, z, evaluate=False), x), source_format="free") == \ "x .or. (y .neqv. z)" assert fcode(Or(Xor(z, x, evaluate=False), y), source_format="free") == \ "y .or. (x .neqv. z)" assert fcode(Or(Xor(x, y, evaluate=False), z), source_format="free") == \ "z .or. (x .neqv. y)" # trinary Xor assert fcode(Xor(x, y, z, evaluate=False), source_format="free") == \ "x .neqv. y .neqv. z" assert fcode(Xor(x, y, Not(z), evaluate=False), source_format="free") == \ "x .neqv. y .neqv. .not. z" assert fcode(Xor(x, Not(y), z, evaluate=False), source_format="free") == \ "x .neqv. z .neqv. .not. y" assert fcode(Xor(Not(x), y, z, evaluate=False), source_format="free") == \ "y .neqv. z .neqv. .not. x" def test_fcode_Relational(): x, y = symbols("x y") assert fcode(Relational(x, y, "=="), source_format="free") == "x == y" assert fcode(Relational(x, y, "!="), source_format="free") == "x /= y" assert fcode(Relational(x, y, ">="), source_format="free") == "x >= y" assert fcode(Relational(x, y, "<="), source_format="free") == "x <= y" assert fcode(Relational(x, y, ">"), source_format="free") == "x > y" assert fcode(Relational(x, y, "<"), source_format="free") == "x < y" def test_fcode_Piecewise(): x = symbols('x') code = fcode(Piecewise((x, x < 1), (x**2, True))) expected = ( " if (x < 1) then\n" " x\n" " else\n" " x**2\n" " end if" ) assert code == expected assert fcode(Piecewise((x, x < 1), (x**2, True)), assign_to="var") == ( " if (x < 1) then\n" " var = x\n" " else\n" " var = x**2\n" " end if" ) a = cos(x)/x b = sin(x)/x for i in xrange(10): a = diff(a, x) b = diff(b, x) expected = ( " if (x < 0) then\n" " weird_name = -cos(x)/x + 10*sin(x)/x**2 + 90*cos(x)/x**3 - 720*\n" " @ sin(x)/x**4 - 5040*cos(x)/x**5 + 30240*sin(x)/x**6 + 151200*cos(x\n" " @ )/x**7 - 604800*sin(x)/x**8 - 1814400*cos(x)/x**9 + 3628800*sin(x\n" " @ )/x**10 + 3628800*cos(x)/x**11\n" " else\n" " weird_name = -sin(x)/x - 10*cos(x)/x**2 + 90*sin(x)/x**3 + 720*\n" " @ cos(x)/x**4 - 5040*sin(x)/x**5 - 30240*cos(x)/x**6 + 151200*sin(x\n" " @ )/x**7 + 604800*cos(x)/x**8 - 1814400*sin(x)/x**9 - 3628800*cos(x\n" " @ )/x**10 + 3628800*sin(x)/x**11\n" " end if" ) code = fcode(Piecewise((a, x < 0), (b, True)), assign_to="weird_name") assert code == expected assert fcode(Piecewise((x, x < 1), (x**2, x > 1), (sin(x), True))) == ( " if (x < 1) then\n" " x\n" " else if (x > 1) then\n" " x**2\n" " else\n" " sin(x)\n" " end if" ) assert fcode(Piecewise((x, x < 1), (x**2, x > 1), (sin(x), x > 0))) == ( " if (x < 1) then\n" " x\n" " else if (x > 1) then\n" " x**2\n" " else if (x > 0) then\n" " sin(x)\n" " end if" ) def test_wrap_fortran(): # "########################################################################" printer = FCodePrinter() lines = [ "C This is a long comment on a single line that must be wrapped properly to produce nice output", " this = is + a + long + and + nasty + fortran + statement + that * must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that * must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that * must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that*must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that*must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that*must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that*must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that**must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that**must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that**must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that**must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that**must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement(that)/must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement(that)/must + be + wrapped + properly", ] wrapped_lines = printer._wrap_fortran(lines) expected_lines = [ "C This is a long comment on a single line that must be wrapped", "C properly to produce nice output", " this = is + a + long + and + nasty + fortran + statement + that *", " @ must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that *", " @ must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that", " @ * must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that*", " @ must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that*", " @ must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that", " @ *must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement +", " @ that*must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that**", " @ must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that**", " @ must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that", " @ **must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that", " @ **must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement +", " @ that**must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement(that)/", " @ must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement(that)", " @ /must + be + wrapped + properly", ] for line in wrapped_lines: assert len(line) <= 72 for w, e in zip(wrapped_lines, expected_lines): assert w == e assert len(wrapped_lines) == len(expected_lines) def test_wrap_fortran_keep_d0(): printer = FCodePrinter() lines = [ ' this_variable_is_very_long_because_we_try_to_test_line_break=1.0d0', ' this_variable_is_very_long_because_we_try_to_test_line_break =1.0d0', ' this_variable_is_very_long_because_we_try_to_test_line_break = 1.0d0', ' this_variable_is_very_long_because_we_try_to_test_line_break = 1.0d0', ' this_variable_is_very_long_because_we_try_to_test_line_break = 1.0d0', ' this_variable_is_very_long_because_we_try_to_test_line_break = 10.0d0' ] expected = [ ' this_variable_is_very_long_because_we_try_to_test_line_break=1.0d0', ' this_variable_is_very_long_because_we_try_to_test_line_break =', ' @ 1.0d0', ' this_variable_is_very_long_because_we_try_to_test_line_break =', ' @ 1.0d0', ' this_variable_is_very_long_because_we_try_to_test_line_break =', ' @ 1.0d0', ' this_variable_is_very_long_because_we_try_to_test_line_break =', ' @ 1.0d0', ' this_variable_is_very_long_because_we_try_to_test_line_break =', ' @ 10.0d0' ] assert printer._wrap_fortran(lines) == expected def test_settings(): raises(TypeError, lambda: fcode(S(4), method="garbage")) def test_free_form_code_line(): x, y = symbols('x,y') assert fcode(cos(x) + sin(y), source_format='free') == "sin(y) + cos(x)" def test_free_form_continuation_line(): x, y = symbols('x,y') result = fcode(((cos(x) + sin(y))**(7)).expand(), source_format='free') expected = ( 'sin(y)**7 + 7*sin(y)**6*cos(x) + 21*sin(y)**5*cos(x)**2 + 35*sin(y)**4* &\n' ' cos(x)**3 + 35*sin(y)**3*cos(x)**4 + 21*sin(y)**2*cos(x)**5 + 7* &\n' ' sin(y)*cos(x)**6 + cos(x)**7' ) assert result == expected def test_free_form_comment_line(): printer = FCodePrinter({ 'source_format': 'free'}) lines = [ "! This is a long comment on a single line that must be wrapped properly to produce nice output"] expected = [ '! This is a long comment on a single line that must be wrapped properly', '! to produce nice output'] assert printer._wrap_fortran(lines) == expected def test_loops(): n, m = symbols('n,m', integer=True) A = IndexedBase('A') x = IndexedBase('x') y = IndexedBase('y') i = Idx('i', m) j = Idx('j', n) expected = ( 'do i = 1, m\n' ' y(i) = 0\n' 'end do\n' 'do i = 1, m\n' ' do j = 1, n\n' ' y(i) = %(rhs)s\n' ' end do\n' 'end do' ) code = fcode(A[i, j]*x[j], assign_to=y[i], source_format='free') assert (code == expected % {'rhs': 'y(i) + A(i, j)*x(j)'} or code == expected % {'rhs': 'y(i) + x(j)*A(i, j)'}) def test_dummy_loops(): # the following line could also be # [Dummy(s, integer=True) for s in 'im'] # or [Dummy(integer=True) for s in 'im'] i, m = symbols('i m', integer=True, cls=Dummy) x = IndexedBase('x') y = IndexedBase('y') i = Idx(i, m) expected = ( 'do i_%(icount)i = 1, m_%(mcount)i\n' ' y(i_%(icount)i) = x(i_%(icount)i)\n' 'end do' ) % {'icount': i.label.dummy_index, 'mcount': m.dummy_index} code = fcode(x[i], assign_to=y[i], source_format='free') assert code == expected def test_derived_classes(): class MyFancyFCodePrinter(FCodePrinter): _default_settings = FCodePrinter._default_settings.copy() _default_settings['assign_to'] = "bork" printer = MyFancyFCodePrinter() x = symbols('x') assert printer.doprint(sin(x)) == " bork = sin(x)" def test_indent(): codelines = ( 'subroutine test(a)\n' 'integer :: a, i, j\n' '\n' 'do\n' 'do \n' 'do j = 1, 5\n' 'if (a>b) then\n' 'if(b>0) then\n' 'a = 3\n' 'donot_indent_me = 2\n' 'do_not_indent_me_either = 2\n' 'ifIam_indented_something_went_wrong = 2\n' 'if_I_am_indented_something_went_wrong = 2\n' 'end should not be unindented here\n' 'end if\n' 'endif\n' 'end do\n' 'end do\n' 'enddo\n' 'end subroutine\n' '\n' 'subroutine test2(a)\n' 'integer :: a\n' 'do\n' 'a = a + 1\n' 'end do \n' 'end subroutine\n' ) expected = ( 'subroutine test(a)\n' 'integer :: a, i, j\n' '\n' 'do\n' ' do \n' ' do j = 1, 5\n' ' if (a>b) then\n' ' if(b>0) then\n' ' a = 3\n' ' donot_indent_me = 2\n' ' do_not_indent_me_either = 2\n' ' ifIam_indented_something_went_wrong = 2\n' ' if_I_am_indented_something_went_wrong = 2\n' ' end should not be unindented here\n' ' end if\n' ' endif\n' ' end do\n' ' end do\n' 'enddo\n' 'end subroutine\n' '\n' 'subroutine test2(a)\n' 'integer :: a\n' 'do\n' ' a = a + 1\n' 'end do \n' 'end subroutine\n' ) p = FCodePrinter({'source_format': 'free'}) result = p.indent_code(codelines) assert result == expected sympy-0.7.4.1/sympy/printing/tests/test_theanocode.py0000644000175000017500000002164512253362407023153 0ustar georgeskgeorgeskfrom sympy.external import import_module from sympy.utilities.pytest import raises, SKIP theano = import_module('theano') if theano: import numpy as np ts = theano.scalar tt = theano.tensor xt, yt, zt = [tt.scalar(name, 'floatX') for name in 'xyz'] else: #bin/test will not execute any tests now disabled = True import sympy from sympy import S sy = sympy from sympy.abc import x, y, z, a, b, c from sympy.printing.theanocode import (theano_code, dim_handling, theano_function) def fgraph_of(*exprs): """ Transform SymPy expressions into Theano Computation """ outs = list(map(theano_code, exprs)) ins = theano.gof.graph.inputs(outs) ins, outs = theano.gof.graph.clone(ins, outs) return theano.gof.FunctionGraph(ins, outs) def theano_simplify(fgraph): """ Simplify a Theano Computation """ mode = theano.compile.get_default_mode().excluding("fusion") fgraph = fgraph.clone() mode.optimizer.optimize(fgraph) return fgraph def theq(a, b): """ theano equality """ astr = theano.printing.debugprint(a, file='str') bstr = theano.printing.debugprint(b, file='str') if not astr == bstr: print() print(astr) print(bstr) return astr == bstr def test_symbol(): xt = theano_code(x) assert isinstance(xt, (tt.TensorVariable, ts.ScalarVariable)) assert xt.name == x.name assert theano_code(x, broadcastables={x: (False,)}).broadcastable == (False,) assert theano_code(x, broadcastables={x: (False,)}).name == x.name def test_add(): expr = x + y comp = theano_code(expr) assert comp.owner.op == theano.tensor.add comp = theano_code(expr, broadcastables={x: (False,), y: (False,)}) assert comp.broadcastable == (False,) comp = theano_code(expr, broadcastables={x: (False, True), y: (False, False)}) assert comp.broadcastable == (False, False) def test_trig(): assert theq(theano_code(sympy.sin(x)), tt.sin(xt)) assert theq(theano_code(sympy.tan(x)), tt.tan(xt)) def test_many(): expr = sy.exp(x**2 + sy.cos(y)) * sy.log(2*z) comp = theano_code(expr) expected = tt.exp(xt**2 + tt.cos(yt)) * tt.log(2*zt) # assert theq(comp, expected) def test_dtype(): assert theano_code(x, dtypes={x: 'float32'}).type.dtype == 'float32' assert theano_code(x, dtypes={x: 'float64'}).type.dtype == 'float64' assert theano_code(x+1, dtypes={x: 'float32'}).type.dtype == 'float32' assert theano_code(x+y, dtypes={x: 'float64', y: 'float32'}).type.dtype == 'float64' def test_MatrixSymbol(): X = sympy.MatrixSymbol('X', 4, 5) Xt = theano_code(X) assert isinstance(Xt, tt.TensorVariable) assert Xt.broadcastable == (False, False) def test_MatMul(): X = sympy.MatrixSymbol('X', 4, 4) Y = sympy.MatrixSymbol('X', 4, 4) Z = sympy.MatrixSymbol('X', 4, 4) expr = X*Y*Z assert isinstance(theano_code(expr).owner.op, tt.Dot) def test_Transpose(): X = sympy.MatrixSymbol('X', 4, 4) assert isinstance(theano_code(X.T).owner.op, tt.DimShuffle) def test_MatAdd(): X = sympy.MatrixSymbol('X', 4, 4) Y = sympy.MatrixSymbol('X', 4, 4) Z = sympy.MatrixSymbol('X', 4, 4) expr = X+Y+Z assert isinstance(theano_code(expr).owner.op, tt.Elemwise) def test_symbols_are_created_once(): expr = x**x comp = theano_code(expr) assert theq(comp, xt**xt) def test_dim_handling(): assert dim_handling([x], dim=2) == {x: (False, False)} assert dim_handling([x, y], dims={x: 1, y: 2}) == {x: (False, True), y: (False, False)} assert dim_handling([x], broadcastables={x: (False,)}) == {x: (False,)} def test_Rationals(): assert theq(theano_code(sympy.Integer(2) / 3), tt.true_div(2, 3)) assert theq(theano_code(S.Half), tt.true_div(1, 2)) def test_Integers(): assert theano_code(sympy.Integer(3)) == 3 def test_factorial(): n = sympy.Symbol('n') assert theano_code(sympy.factorial(n)) def test_Derivative(): simp = lambda expr: theano_simplify(fgraph_of(expr)) assert theq(simp(theano_code(sy.Derivative(sy.sin(x), x, evaluate=False))), simp(theano.grad(tt.sin(xt), xt))) def test_theano_function_simple(): f = theano_function([x, y], [x+y]) assert f(2, 3) == 5 def test_theano_function_numpy(): f = theano_function([x, y], [x+y], dim=1, dtypes={x: 'float64', y: 'float64'}) assert np.linalg.norm(f([1, 2], [3, 4]) - np.asarray([4, 6])) < 1e-9 f = theano_function([x, y], [x+y], dtypes={x: 'float64', y: 'float64'}, dim=1) xx = np.arange(3).astype('float64') yy = 2*np.arange(3).astype('float64') assert np.linalg.norm(f(xx, yy) - 3*np.arange(3)) < 1e-9 def test_theano_function_kwargs(): import numpy as np f = theano_function([x, y, z], [x+y], dim=1, on_unused_input='ignore', dtypes={x: 'float64', y: 'float64', z: 'float64'}) assert np.linalg.norm(f([1, 2], [3, 4], [0, 0]) - np.asarray([4, 6])) < 1e-9 f = theano_function([x, y, z], [x+y], dtypes={x: 'float64', y: 'float64', z: 'float64'}, dim=1, on_unused_input='ignore') xx = np.arange(3).astype('float64') yy = 2*np.arange(3).astype('float64') zz = 2*np.arange(3).astype('float64') assert np.linalg.norm(f(xx, yy, zz) - 3*np.arange(3)) < 1e-9 def test_slice(): assert theano_code(slice(1, 2, 3)) == slice(1, 2, 3) assert str(theano_code(slice(1, x, 3), dtypes={x: 'int32'})) ==\ str(slice(1, xt, 3)) def test_MatrixSlice(): n = sympy.Symbol('n', integer=True) X = sympy.MatrixSymbol('X', n, n) Y = X[1:2:3, 4:5:6] Yt = theano_code(Y) assert tuple(Yt.owner.op.idx_list) == (slice(1,2,3), slice(4,5,6)) assert Yt.owner.inputs[0] == theano_code(X) k = sympy.Symbol('k') kt = theano_code(k, dtypes={k: 'int32'}) start, stop, step = 4, k, 2 Y = X[start:stop:step] Yt = theano_code(Y, dtypes={n: 'int32', k: 'int32'}) # assert Yt.owner.op.idx_list[0].stop == kt def test_BlockMatrix(): n = sympy.Symbol('n', integer=True) A = sympy.MatrixSymbol('A', n, n) B = sympy.MatrixSymbol('B', n, n) C = sympy.MatrixSymbol('C', n, n) D = sympy.MatrixSymbol('D', n, n) At, Bt, Ct, Dt = map(theano_code, (A, B, C, D)) Block = sympy.BlockMatrix([[A, B], [C, D]]) Blockt = theano_code(Block) solutions = [tt.join(0, tt.join(1, At, Bt), tt.join(1, Ct, Dt)), tt.join(1, tt.join(0, At, Ct), tt.join(0, Bt, Dt))] assert any(theq(Blockt, solution) for solution in solutions) @SKIP def test_BlockMatrix_Inverse_execution(): k, n = 2, 4 dtype = 'float32' A = sympy.MatrixSymbol('A', n, k) B = sympy.MatrixSymbol('B', n, n) inputs = A, B output = B.I*A cutsizes = {A: [(n//2, n//2), (k//2, k//2)], B: [(n//2, n//2), (n//2, n//2)]} cutinputs = [sympy.blockcut(i, *cutsizes[i]) for i in inputs] cutoutput = output.subs(dict(zip(inputs, cutinputs))) dtypes = dict(zip(inputs, [dtype]*len(inputs))) f = theano_function(inputs, [output], dtypes=dtypes, cache={}) fblocked = theano_function(inputs, [sympy.block_collapse(cutoutput)], dtypes=dtypes, cache={}) ninputs = [np.random.rand(*x.shape).astype(dtype) for x in inputs] ninputs = [np.arange(n*k).reshape(A.shape).astype(dtype), np.eye(n).astype(dtype)] ninputs[1] += np.ones(B.shape)*1e-5 assert np.allclose(f(*ninputs), fblocked(*ninputs), rtol=1e-5) def test_DenseMatrix(): t = sy.Symbol('theta') for MatrixType in [sy.Matrix, sy.ImmutableMatrix]: X = MatrixType([[sy.cos(t), -sy.sin(t)], [sy.sin(t), sy.cos(t)]]) tX = theano_code(X) assert isinstance(tX, tt.TensorVariable) assert tX.owner.op == tt.join def test_AppliedUndef(): t = sy.Symbol('t') f = sy.Function('f') ft = theano_code(f(t)) assert isinstance(ft, tt.TensorVariable) assert ft.name == 'f_t' def test_bad_keyword_args_raise_error(): raises(Exception, lambda : theano_function([x], [x+1], foobar=3)) def test_cache(): sx = sy.Symbol('x') cache = {} tx = theano_code(sx, cache=cache) assert theano_code(sx, cache=cache) is tx assert theano_code(sx, cache={}) is not tx def test_Piecewise(): # A piecewise linear xt, yt = theano_code(x), theano_code(y) expr = sy.Piecewise((0, x<0), (x, x<2), (1, True)) # ___/III result = theano_code(expr) assert result.owner.op == tt.switch expected = tt.switch(xt<0, 0, tt.switch(xt<2, xt, 1)) assert theq(result, expected) expr = sy.Piecewise((x, x < 0)) result = theano_code(expr) expected = tt.switch(xt < 0, xt, np.nan) assert theq(result, expected) def test_Relationals(): xt, yt = theano_code(x), theano_code(y) assert theq(theano_code(x > y), xt > yt) assert theq(theano_code(x < y), xt < yt) assert theq(theano_code(x >= y), xt >= yt) assert theq(theano_code(x <= y), xt <= yt) sympy-0.7.4.1/sympy/printing/tests/test_mathml.py0000644000175000017500000004077712253362407022333 0ustar georgeskgeorgeskfrom sympy import diff, Integral, Limit, sin, Symbol, Integer, Rational, cos, \ tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh, E, I, oo, \ pi, GoldenRatio, EulerGamma, Sum, Eq, Ne, Ge, Lt, Float from sympy.core.compatibility import u from sympy.printing.mathml import mathml, MathMLPrinter from sympy.utilities.pytest import raises x = Symbol('x') y = Symbol('y') mp = MathMLPrinter() def test_printmethod(): assert mp.doprint(1 + x) == 'x1' def test_mathml_core(): mml_1 = mp._print(1 + x) assert mml_1.nodeName == 'apply' nodes = mml_1.childNodes assert len(nodes) == 3 assert nodes[0].nodeName == 'plus' assert nodes[0].hasChildNodes() is False assert nodes[0].nodeValue is None assert nodes[1].nodeName in ['cn', 'ci'] if nodes[1].nodeName == 'cn': assert nodes[1].childNodes[0].nodeValue == '1' assert nodes[2].childNodes[0].nodeValue == 'x' else: assert nodes[1].childNodes[0].nodeValue == 'x' assert nodes[2].childNodes[0].nodeValue == '1' mml_2 = mp._print(x**2) assert mml_2.nodeName == 'apply' nodes = mml_2.childNodes assert nodes[1].childNodes[0].nodeValue == 'x' assert nodes[2].childNodes[0].nodeValue == '2' mml_3 = mp._print(2*x) assert mml_3.nodeName == 'apply' nodes = mml_3.childNodes assert nodes[0].nodeName == 'times' assert nodes[1].childNodes[0].nodeValue == '2' assert nodes[2].childNodes[0].nodeValue == 'x' mml = mp._print(Float(1.0, 2)*x) assert mml.nodeName == 'apply' nodes = mml.childNodes assert nodes[0].nodeName == 'times' assert nodes[1].childNodes[0].nodeValue == '1.0' assert nodes[2].childNodes[0].nodeValue == 'x' def test_mathml_functions(): mml_1 = mp._print(sin(x)) assert mml_1.nodeName == 'apply' assert mml_1.childNodes[0].nodeName == 'sin' assert mml_1.childNodes[1].nodeName == 'ci' mml_2 = mp._print(diff(sin(x), x, evaluate=False)) assert mml_2.nodeName == 'apply' assert mml_2.childNodes[0].nodeName == 'diff' assert mml_2.childNodes[1].nodeName == 'bvar' assert mml_2.childNodes[1].childNodes[ 0].nodeName == 'ci' # below bvar there's x/ci> mml_3 = mp._print(diff(cos(x*y), x, evaluate=False)) assert mml_3.nodeName == 'apply' assert mml_3.childNodes[0].nodeName == 'partialdiff' assert mml_3.childNodes[1].nodeName == 'bvar' assert mml_3.childNodes[1].childNodes[ 0].nodeName == 'ci' # below bvar there's x/ci> def test_mathml_limits(): # XXX No unevaluated limits lim_fun = sin(x)/x mml_1 = mp._print(Limit(lim_fun, x, 0)) assert mml_1.childNodes[0].nodeName == 'limit' assert mml_1.childNodes[1].nodeName == 'bvar' assert mml_1.childNodes[2].nodeName == 'lowlimit' assert mml_1.childNodes[3].toxml() == mp._print(lim_fun).toxml() def test_mathml_integrals(): integrand = x mml_1 = mp._print(Integral(integrand, (x, 0, 1))) assert mml_1.childNodes[0].nodeName == 'int' assert mml_1.childNodes[1].nodeName == 'bvar' assert mml_1.childNodes[2].nodeName == 'lowlimit' assert mml_1.childNodes[3].nodeName == 'uplimit' assert mml_1.childNodes[4].toxml() == mp._print(integrand).toxml() def test_mathml_sums(): summand = x mml_1 = mp._print(Sum(summand, (x, 1, 10))) assert mml_1.childNodes[0].nodeName == 'sum' assert mml_1.childNodes[1].nodeName == 'bvar' assert mml_1.childNodes[2].nodeName == 'lowlimit' assert mml_1.childNodes[3].nodeName == 'uplimit' assert mml_1.childNodes[4].toxml() == mp._print(summand).toxml() def test_mathml_tuples(): mml_1 = mp._print([2]) assert mml_1.nodeName == 'list' assert mml_1.childNodes[0].nodeName == 'cn' assert len(mml_1.childNodes) == 1 mml_2 = mp._print([2, Integer(1)]) assert mml_2.nodeName == 'list' assert mml_2.childNodes[0].nodeName == 'cn' assert mml_2.childNodes[1].nodeName == 'cn' assert len(mml_2.childNodes) == 2 def test_mathml_add(): mml = mp._print(x**5 - x**4 + x) assert mml.childNodes[0].nodeName == 'plus' assert mml.childNodes[1].childNodes[0].nodeName == 'minus' assert mml.childNodes[1].childNodes[1].nodeName == 'apply' def test_mathml_Rational(): mml_1 = mp._print(Rational(1, 1)) """should just return a number""" assert mml_1.nodeName == 'cn' mml_2 = mp._print(Rational(2, 5)) assert mml_2.childNodes[0].nodeName == 'divide' def test_mathml_constants(): mml = mp._print(I) assert mml.nodeName == 'imaginaryi' mml = mp._print(E) assert mml.nodeName == 'exponentiale' mml = mp._print(oo) assert mml.nodeName == 'infinity' mml = mp._print(pi) assert mml.nodeName == 'pi' assert mathml(GoldenRatio) == 'φ' mml = mathml(EulerGamma) assert mml == '' def test_mathml_trig(): mml = mp._print(sin(x)) assert mml.childNodes[0].nodeName == 'sin' mml = mp._print(cos(x)) assert mml.childNodes[0].nodeName == 'cos' mml = mp._print(tan(x)) assert mml.childNodes[0].nodeName == 'tan' mml = mp._print(asin(x)) assert mml.childNodes[0].nodeName == 'arcsin' mml = mp._print(acos(x)) assert mml.childNodes[0].nodeName == 'arccos' mml = mp._print(atan(x)) assert mml.childNodes[0].nodeName == 'arctan' mml = mp._print(sinh(x)) assert mml.childNodes[0].nodeName == 'sinh' mml = mp._print(cosh(x)) assert mml.childNodes[0].nodeName == 'cosh' mml = mp._print(tanh(x)) assert mml.childNodes[0].nodeName == 'tanh' mml = mp._print(asinh(x)) assert mml.childNodes[0].nodeName == 'arcsinh' mml = mp._print(atanh(x)) assert mml.childNodes[0].nodeName == 'arctanh' mml = mp._print(acosh(x)) assert mml.childNodes[0].nodeName == 'arccosh' def test_mathml_relational(): mml_1 = mp._print(Eq(x, 1)) assert mml_1.nodeName == 'apply' assert mml_1.childNodes[0].nodeName == 'eq' assert mml_1.childNodes[1].nodeName == 'ci' assert mml_1.childNodes[1].childNodes[0].nodeValue == 'x' assert mml_1.childNodes[2].nodeName == 'cn' assert mml_1.childNodes[2].childNodes[0].nodeValue == '1' mml_2 = mp._print(Ne(1, x)) assert mml_2.nodeName == 'apply' assert mml_2.childNodes[0].nodeName == 'neq' assert mml_2.childNodes[1].nodeName == 'cn' assert mml_2.childNodes[1].childNodes[0].nodeValue == '1' assert mml_2.childNodes[2].nodeName == 'ci' assert mml_2.childNodes[2].childNodes[0].nodeValue == 'x' mml_3 = mp._print(Ge(1, x)) assert mml_3.nodeName == 'apply' assert mml_3.childNodes[0].nodeName == 'geq' assert mml_3.childNodes[1].nodeName == 'cn' assert mml_3.childNodes[1].childNodes[0].nodeValue == '1' assert mml_3.childNodes[2].nodeName == 'ci' assert mml_3.childNodes[2].childNodes[0].nodeValue == 'x' mml_4 = mp._print(Lt(1, x)) assert mml_4.nodeName == 'apply' assert mml_4.childNodes[0].nodeName == 'lt' assert mml_4.childNodes[1].nodeName == 'cn' assert mml_4.childNodes[1].childNodes[0].nodeValue == '1' assert mml_4.childNodes[2].nodeName == 'ci' assert mml_4.childNodes[2].childNodes[0].nodeValue == 'x' def test_symbol(): mml = mp._print(Symbol("x")) assert mml.nodeName == 'ci' assert mml.childNodes[0].nodeValue == 'x' del mml mml = mp._print(Symbol("x^2")) assert mml.nodeName == 'ci' assert mml.childNodes[0].nodeName == 'mml:msup' assert mml.childNodes[0].childNodes[0].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[0].childNodes[0].nodeValue == 'x' assert mml.childNodes[0].childNodes[1].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[1].childNodes[0].nodeValue == '2' del mml mml = mp._print(Symbol("x__2")) assert mml.nodeName == 'ci' assert mml.childNodes[0].nodeName == 'mml:msup' assert mml.childNodes[0].childNodes[0].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[0].childNodes[0].nodeValue == 'x' assert mml.childNodes[0].childNodes[1].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[1].childNodes[0].nodeValue == '2' del mml mml = mp._print(Symbol("x_2")) assert mml.nodeName == 'ci' assert mml.childNodes[0].nodeName == 'mml:msub' assert mml.childNodes[0].childNodes[0].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[0].childNodes[0].nodeValue == 'x' assert mml.childNodes[0].childNodes[1].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[1].childNodes[0].nodeValue == '2' del mml mml = mp._print(Symbol("x^3_2")) assert mml.nodeName == 'ci' assert mml.childNodes[0].nodeName == 'mml:msubsup' assert mml.childNodes[0].childNodes[0].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[0].childNodes[0].nodeValue == 'x' assert mml.childNodes[0].childNodes[1].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[1].childNodes[0].nodeValue == '2' assert mml.childNodes[0].childNodes[2].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[2].childNodes[0].nodeValue == '3' del mml mml = mp._print(Symbol("x__3_2")) assert mml.nodeName == 'ci' assert mml.childNodes[0].nodeName == 'mml:msubsup' assert mml.childNodes[0].childNodes[0].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[0].childNodes[0].nodeValue == 'x' assert mml.childNodes[0].childNodes[1].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[1].childNodes[0].nodeValue == '2' assert mml.childNodes[0].childNodes[2].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[2].childNodes[0].nodeValue == '3' del mml mml = mp._print(Symbol("x_2_a")) assert mml.nodeName == 'ci' assert mml.childNodes[0].nodeName == 'mml:msub' assert mml.childNodes[0].childNodes[0].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[0].childNodes[0].nodeValue == 'x' assert mml.childNodes[0].childNodes[1].nodeName == 'mml:mrow' assert mml.childNodes[0].childNodes[1].childNodes[0].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[1].childNodes[0].childNodes[ 0].nodeValue == '2' assert mml.childNodes[0].childNodes[1].childNodes[1].nodeName == 'mml:mo' assert mml.childNodes[0].childNodes[1].childNodes[1].childNodes[ 0].nodeValue == ' ' assert mml.childNodes[0].childNodes[1].childNodes[2].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[1].childNodes[2].childNodes[ 0].nodeValue == 'a' del mml mml = mp._print(Symbol("x^2^a")) assert mml.nodeName == 'ci' assert mml.childNodes[0].nodeName == 'mml:msup' assert mml.childNodes[0].childNodes[0].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[0].childNodes[0].nodeValue == 'x' assert mml.childNodes[0].childNodes[1].nodeName == 'mml:mrow' assert mml.childNodes[0].childNodes[1].childNodes[0].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[1].childNodes[0].childNodes[ 0].nodeValue == '2' assert mml.childNodes[0].childNodes[1].childNodes[1].nodeName == 'mml:mo' assert mml.childNodes[0].childNodes[1].childNodes[1].childNodes[ 0].nodeValue == ' ' assert mml.childNodes[0].childNodes[1].childNodes[2].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[1].childNodes[2].childNodes[ 0].nodeValue == 'a' del mml mml = mp._print(Symbol("x__2__a")) assert mml.nodeName == 'ci' assert mml.childNodes[0].nodeName == 'mml:msup' assert mml.childNodes[0].childNodes[0].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[0].childNodes[0].nodeValue == 'x' assert mml.childNodes[0].childNodes[1].nodeName == 'mml:mrow' assert mml.childNodes[0].childNodes[1].childNodes[0].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[1].childNodes[0].childNodes[ 0].nodeValue == '2' assert mml.childNodes[0].childNodes[1].childNodes[1].nodeName == 'mml:mo' assert mml.childNodes[0].childNodes[1].childNodes[1].childNodes[ 0].nodeValue == ' ' assert mml.childNodes[0].childNodes[1].childNodes[2].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[1].childNodes[2].childNodes[ 0].nodeValue == 'a' del mml def test_mathml_greek(): mml = mp._print(Symbol('alpha')) assert mml.nodeName == 'ci' assert mml.childNodes[0].nodeValue == u('\u03b1') assert mp.doprint(Symbol('alpha')) == 'α' assert mp.doprint(Symbol('beta')) == 'β' assert mp.doprint(Symbol('gamma')) == 'γ' assert mp.doprint(Symbol('delta')) == 'δ' assert mp.doprint(Symbol('epsilon')) == 'ε' assert mp.doprint(Symbol('zeta')) == 'ζ' assert mp.doprint(Symbol('eta')) == 'η' assert mp.doprint(Symbol('theta')) == 'θ' assert mp.doprint(Symbol('iota')) == 'ι' assert mp.doprint(Symbol('kappa')) == 'κ' assert mp.doprint(Symbol('lambda')) == 'λ' assert mp.doprint(Symbol('mu')) == 'μ' assert mp.doprint(Symbol('nu')) == 'ν' assert mp.doprint(Symbol('xi')) == 'ξ' assert mp.doprint(Symbol('omicron')) == 'ο' assert mp.doprint(Symbol('pi')) == 'π' assert mp.doprint(Symbol('rho')) == 'ρ' assert mp.doprint(Symbol('varsigma')) == 'ς', mp.doprint(Symbol('varsigma')) assert mp.doprint(Symbol('sigma')) == 'σ' assert mp.doprint(Symbol('tau')) == 'τ' assert mp.doprint(Symbol('upsilon')) == 'υ' assert mp.doprint(Symbol('phi')) == 'φ' assert mp.doprint(Symbol('chi')) == 'χ' assert mp.doprint(Symbol('psi')) == 'ψ' assert mp.doprint(Symbol('omega')) == 'ω' assert mp.doprint(Symbol('Alpha')) == 'Α' assert mp.doprint(Symbol('Beta')) == 'Β' assert mp.doprint(Symbol('Gamma')) == 'Γ' assert mp.doprint(Symbol('Delta')) == 'Δ' assert mp.doprint(Symbol('Epsilon')) == 'Ε' assert mp.doprint(Symbol('Zeta')) == 'Ζ' assert mp.doprint(Symbol('Eta')) == 'Η' assert mp.doprint(Symbol('Theta')) == 'Θ' assert mp.doprint(Symbol('Iota')) == 'Ι' assert mp.doprint(Symbol('Kappa')) == 'Κ' assert mp.doprint(Symbol('Lambda')) == 'Λ' assert mp.doprint(Symbol('Mu')) == 'Μ' assert mp.doprint(Symbol('Nu')) == 'Ν' assert mp.doprint(Symbol('Xi')) == 'Ξ' assert mp.doprint(Symbol('Omicron')) == 'Ο' assert mp.doprint(Symbol('Pi')) == 'Π' assert mp.doprint(Symbol('Rho')) == 'Ρ' assert mp.doprint(Symbol('Sigma')) == 'Σ' assert mp.doprint(Symbol('Tau')) == 'Τ' assert mp.doprint(Symbol('Upsilon')) == 'Υ' assert mp.doprint(Symbol('Phi')) == 'Φ' assert mp.doprint(Symbol('Chi')) == 'Χ' assert mp.doprint(Symbol('Psi')) == 'Ψ' assert mp.doprint(Symbol('Omega')) == 'Ω' def test_mathml_order(): expr = x**3 + x**2*y + 3*x*y**3 + y**4 mp = MathMLPrinter({'order': 'lex'}) mml = mp._print(expr) assert mml.childNodes[1].childNodes[0].nodeName == 'power' assert mml.childNodes[1].childNodes[1].childNodes[0].data == 'x' assert mml.childNodes[1].childNodes[2].childNodes[0].data == '3' assert mml.childNodes[4].childNodes[0].nodeName == 'power' assert mml.childNodes[4].childNodes[1].childNodes[0].data == 'y' assert mml.childNodes[4].childNodes[2].childNodes[0].data == '4' mp = MathMLPrinter({'order': 'rev-lex'}) mml = mp._print(expr) assert mml.childNodes[1].childNodes[0].nodeName == 'power' assert mml.childNodes[1].childNodes[1].childNodes[0].data == 'y' assert mml.childNodes[1].childNodes[2].childNodes[0].data == '4' assert mml.childNodes[4].childNodes[0].nodeName == 'power' assert mml.childNodes[4].childNodes[1].childNodes[0].data == 'x' assert mml.childNodes[4].childNodes[2].childNodes[0].data == '3' def test_settings(): raises(TypeError, lambda: mathml(Symbol("x"), method="garbage")) def test_toprettyxml_hooking(): # test that the patch doesn't influence the behavior of the standard library import xml.dom.minidom doc = xml.dom.minidom.parseString( "x1") prettyxml_old = doc.toprettyxml() mp.apply_patch() mp.restore_patch() assert prettyxml_old == doc.toprettyxml() sympy-0.7.4.1/sympy/printing/tests/test_lambdarepr.py0000644000175000017500000000735012253362407023150 0ustar georgeskgeorgeskfrom sympy import symbols, sin, Matrix, Interval, Piecewise from sympy.utilities.pytest import raises from sympy.printing.lambdarepr import lambdarepr x, y, z = symbols("x,y,z") def test_basic(): assert lambdarepr(x*y) == "x*y" assert lambdarepr(x + y) in ["y + x", "x + y"] assert lambdarepr(x**y) == "x**y" def test_matrix(): A = Matrix([[x, y], [y*x, z**2]]) assert lambdarepr(A) == "MutableDenseMatrix([[x, y], [x*y, z**2]])" def test_piecewise(): # In each case, test eval() the lambdarepr() to make sure there are a # correct number of parentheses. It will give a SyntaxError if there aren't. h = "lambda x: " p = Piecewise((x, True), evaluate=False) l = lambdarepr(p) eval(h + l) assert l == "((x) if (True) else None)" p = Piecewise((x, x < 0)) l = lambdarepr(p) eval(h + l) assert l == "((x) if (x < 0) else None)" p = Piecewise( (1, x < 1), (2, x < 2), (0, True) ) l = lambdarepr(p) eval(h + l) assert l == "((1) if (x < 1) else (((2) if (x < 2) else " \ "(((0) if (True) else None)))))" p = Piecewise( (1, x < 1), (2, x < 2), ) l = lambdarepr(p) eval(h + l) assert l == "((1) if (x < 1) else (((2) if (x < 2) else None)))" p = Piecewise( (x, x < 1), (x**2, Interval(3, 4, True, False).contains(x)), (0, True), ) l = lambdarepr(p) eval(h + l) assert l == "((x) if (x < 1) else (((x**2) if (((x <= 4) and " \ "(x > 3))) else (((0) if (True) else None)))))" p = Piecewise( (x**2, x < 0), (x, Interval(0, 1, False, True).contains(x)), (2 - x, x >= 1), (0, True) ) l = lambdarepr(p) eval(h + l) assert l == "((x**2) if (x < 0) else (((x) if (((x >= 0) and (x < 1))) " \ "else (((-x + 2) if (x >= 1) else (((0) if (True) else None)))))))" p = Piecewise( (x**2, x < 0), (x, Interval(0, 1, False, True).contains(x)), (2 - x, x >= 1), ) l = lambdarepr(p) eval(h + l) assert l == "((x**2) if (x < 0) else (((x) if (((x >= 0) and " \ "(x < 1))) else (((-x + 2) if (x >= 1) else None)))))" p = Piecewise( (1, x >= 1), (2, x >= 2), (3, x >= 3), (4, x >= 4), (5, x >= 5), (6, True) ) l = lambdarepr(p) eval(h + l) assert l == ("((1) if (x >= 1) else (((2) if (x >= 2) else (((3) if " "(x >= 3) else (((4) if (x >= 4) else (((5) if (x >= 5) else (((6) if " "(True) else None)))))))))))") p = Piecewise( (1, x <= 1), (2, x <= 2), (3, x <= 3), (4, x <= 4), (5, x <= 5), (6, True) ) l = lambdarepr(p) eval(h + l) assert l == "((1) if (x <= 1) else (((2) if (x <= 2) else (((3) if " \ "(x <= 3) else (((4) if (x <= 4) else (((5) if (x <= 5) else (((6) if " \ "(True) else None)))))))))))" p = Piecewise( (1, x > 1), (2, x > 2), (3, x > 3), (4, x > 4), (5, x > 5), (6, True) ) l = lambdarepr(p) eval(h + l) assert l == ("((1) if (x > 1) else (((2) if (x > 2) else (((3) if " "(x > 3) else (((4) if (x > 4) else (((5) if (x > 5) else (((6) if " "(True) else None)))))))))))") p = Piecewise( (1, x < 1), (2, x < 2), (3, x < 3), (4, x < 4), (5, x < 5), (6, True) ) l = lambdarepr(p) eval(h + l) assert l == "((1) if (x < 1) else (((2) if (x < 2) else (((3) if " \ "(x < 3) else (((4) if (x < 4) else (((5) if (x < 5) else (((6) if " \ "(True) else None)))))))))))" def test_settings(): raises(TypeError, lambda: lambdarepr(sin(x), method="garbage")) sympy-0.7.4.1/sympy/printing/tests/test_str.py0000644000175000017500000005302512253362407021647 0ustar georgeskgeorgeskfrom __future__ import division from sympy import (Abs, Catalan, cos, Derivative, E, EulerGamma, exp, factorial, factorial2, Function, GoldenRatio, I, Integer, Integral, Interval, Lambda, Limit, Matrix, nan, O, oo, pi, Rational, Float, Rel, S, sin, SparseMatrix, sqrt, summation, Sum, Symbol, symbols, Wild, WildFunction, zeta, zoo, Dummy, Dict, Tuple, FiniteSet, factor, MatrixSymbol, subfactorial, true, false, Equivalent, Xor) from sympy.core import Expr from sympy.physics.units import second, joule from sympy.polys import Poly, RootOf, RootSum, groebner, ring, field, ZZ, QQ, lex, grlex from sympy.geometry import Point, Circle from sympy.utilities.pytest import raises from sympy.printing import sstr, sstrrepr, StrPrinter from sympy.core.trace import Tr x, y, z, w = symbols('x,y,z,w') d = Dummy('d') def test_printmethod(): class R(Abs): def _sympystr(self, printer): return "foo(%s)" % printer._print(self.args[0]) assert sstr(R(x)) == "foo(x)" class R(Abs): def _sympystr(self, printer): return "foo" assert sstr(R(x)) == "foo" def test_Abs(): assert str(Abs(x)) == "Abs(x)" assert str(Abs(Rational(1, 6))) == "1/6" assert str(Abs(Rational(-1, 6))) == "1/6" def test_Add(): assert str(x + y) == "x + y" assert str(x + 1) == "x + 1" assert str(x + x**2) == "x**2 + x" assert str(5 + x + y + x*y + x**2 + y**2) == "x**2 + x*y + x + y**2 + y + 5" assert str(1 + x + x**2/2 + x**3/3) == "x**3/3 + x**2/2 + x + 1" assert str(2*x - 7*x**2 + 2 + 3*y) == "-7*x**2 + 2*x + 3*y + 2" assert str(x - y) == "x - y" assert str(2 - x) == "-x + 2" assert str(x - 2) == "x - 2" assert str(x - y - z - w) == "-w + x - y - z" assert str(x - z*y**2*z*w) == "-w*y**2*z**2 + x" assert str(x - 1*y*x*y) == "-x*y**2 + x" assert str(sin(x).series(x, 0, 15)) == "x - x**3/6 + x**5/120 - x**7/5040 + x**9/362880 - x**11/39916800 + x**13/6227020800 + O(x**15)" def test_Catalan(): assert str(Catalan) == "Catalan" def test_ComplexInfinity(): assert str(zoo) == "zoo" def test_Derivative(): assert str(Derivative(x, y)) == "Derivative(x, y)" assert str(Derivative(x**2, x, evaluate=False)) == "Derivative(x**2, x)" assert str(Derivative( x**2/y, x, y, evaluate=False)) == "Derivative(x**2/y, x, y)" def test_dict(): assert str({1: 1 + x}) == sstr({1: 1 + x}) == "{1: x + 1}" assert str({1: x**2, 2: y*x}) in ("{1: x**2, 2: x*y}", "{2: x*y, 1: x**2}") assert sstr({1: x**2, 2: y*x}) == "{1: x**2, 2: x*y}" def test_Dict(): assert str(Dict({1: 1 + x})) == sstr({1: 1 + x}) == "{1: x + 1}" assert str(Dict({1: x**2, 2: y*x})) in ( "{1: x**2, 2: x*y}", "{2: x*y, 1: x**2}") assert sstr(Dict({1: x**2, 2: y*x})) == "{1: x**2, 2: x*y}" def test_Dummy(): assert str(d) == "_d" assert str(d + x) == "_d + x" def test_EulerGamma(): assert str(EulerGamma) == "EulerGamma" def test_Exp(): assert str(E) == "E" def test_factorial(): n = Symbol('n', integer=True) assert str(factorial(-2)) == "0" assert str(factorial(0)) == "1" assert str(factorial(7)) == "5040" assert str(factorial(n)) == "factorial(n)" assert str(factorial(2*n)) == "factorial(2*n)" assert str(factorial(factorial(n))) == 'factorial(factorial(n))' assert str(factorial(factorial2(n))) == 'factorial(factorial2(n))' assert str(factorial2(factorial(n))) == 'factorial2(factorial(n))' assert str(factorial2(factorial2(n))) == 'factorial2(factorial2(n))' assert str(subfactorial(3)) == "2" assert str(subfactorial(n)) == "subfactorial(n)" assert str(subfactorial(2*n)) == "subfactorial(2*n)" def test_Function(): f = Function('f') fx = f(x) w = WildFunction('w') assert str(f) == "f" assert str(fx) == "f(x)" assert str(w) == "w_" def test_Geometry(): assert sstr(Point(0, 0)) == 'Point(0, 0)' assert sstr(Circle(Point(0, 0), 3)) == 'Circle(Point(0, 0), 3)' # TODO test other Geometry entities def test_GoldenRatio(): assert str(GoldenRatio) == "GoldenRatio" def test_ImaginaryUnit(): assert str(I) == "I" def test_Infinity(): assert str(oo) == "oo" assert str(oo*I) == "oo*I" def test_Integer(): assert str(Integer(-1)) == "-1" assert str(Integer(1)) == "1" assert str(Integer(-3)) == "-3" assert str(Integer(0)) == "0" assert str(Integer(25)) == "25" def test_Integral(): assert str(Integral(sin(x), y)) == "Integral(sin(x), y)" assert str(Integral(sin(x), (y, 0, 1))) == "Integral(sin(x), (y, 0, 1))" def test_Interval(): a = Symbol('a', real=True) assert str(Interval(0, a)) == "[0, a]" assert str(Interval(0, a, False, False)) == "[0, a]" assert str(Interval(0, a, True, False)) == "(0, a]" assert str(Interval(0, a, False, True)) == "[0, a)" assert str(Interval(0, a, True, True)) == "(0, a)" def test_Lambda(): assert str(Lambda(d, d**2)) == "Lambda(_d, _d**2)" def test_Limit(): assert str(Limit(sin(x)/x, x, y)) == "Limit(sin(x)/x, x, y)" assert str(Limit(1/x, x, 0)) == "Limit(1/x, x, 0)" assert str( Limit(sin(x)/x, x, y, dir="-")) == "Limit(sin(x)/x, x, y, dir='-')" def test_list(): assert str([x]) == sstr([x]) == "[x]" assert str([x**2, x*y + 1]) == sstr([x**2, x*y + 1]) == "[x**2, x*y + 1]" assert str([x**2, [y + x]]) == sstr([x**2, [y + x]]) == "[x**2, [x + y]]" def test_Matrix_str(): M = Matrix([[x**+1, 1], [y, x + y]]) assert str(M) == "Matrix([[x, 1], [y, x + y]])" assert sstr(M) == "Matrix([\n[x, 1],\n[y, x + y]])" M = Matrix([[1]]) assert str(M) == sstr(M) == "Matrix([[1]])" M = Matrix([[1, 2]]) assert str(M) == sstr(M) == "Matrix([[1, 2]])" M = Matrix() assert str(M) == sstr(M) == "Matrix(0, 0, [])" M = Matrix(0, 1, lambda i, j: 0) assert str(M) == sstr(M) == "Matrix(0, 1, [])" def test_Mul(): assert str(x/y) == "x/y" assert str(y/x) == "y/x" assert str(x/y/z) == "x/(y*z)" assert str((x + 1)/(y + 2)) == "(x + 1)/(y + 2)" assert str(2*x/3) == '2*x/3' assert str(-2*x/3) == '-2*x/3' class CustomClass1(Expr): is_commutative = True class CustomClass2(Expr): is_commutative = True cc1 = CustomClass1() cc2 = CustomClass2() assert str(Rational(2)*cc1) == '2*CustomClass1()' assert str(cc1*Rational(2)) == '2*CustomClass1()' assert str(cc1*Float("1.5")) == '1.5*CustomClass1()' assert str(cc2*Rational(2)) == '2*CustomClass2()' assert str(cc2*Rational(2)*cc1) == '2*CustomClass1()*CustomClass2()' assert str(cc1*Rational(2)*cc2) == '2*CustomClass1()*CustomClass2()' def test_NaN(): assert str(nan) == "nan" def test_NegativeInfinity(): assert str(-oo) == "-oo" def test_Order(): assert str(O(x)) == "O(x)" assert str(O(x**2)) == "O(x**2)" assert str(O(x*y)) == "O(x*y, x, y)" assert str(O(x, x)) == "O(x)" assert str(O(x, (x, 0))) == "O(x)" assert str(O(x, (x, oo))) == "O(x, (x, oo))" assert str(O(x, x, y)) == "O(x, x, y)" assert str(O(x, x, y)) == "O(x, x, y)" assert str(O(x, (x, oo), (y, oo))) == "O(x, (x, oo), (y, oo))" def test_Permutation_Cycle(): from sympy.combinatorics import Permutation, Cycle # general principle: economically, canonically show all moved elements # and the size of the permutation. for p, s in [ (Cycle(), 'Cycle()'), (Cycle(2), 'Cycle(2)'), (Cycle(2, 1), 'Cycle(1, 2)'), (Cycle(1, 2)(5)(6, 7)(10), 'Cycle(1, 2)(6, 7)(10)'), (Cycle(3, 4)(1, 2)(3, 4), 'Cycle(1, 2)(4)'), ]: assert str(p) == s Permutation.print_cyclic = False for p, s in [ (Permutation([]), 'Permutation([])'), (Permutation([], size=1), 'Permutation([0])'), (Permutation([], size=2), 'Permutation([0, 1])'), (Permutation([], size=10), 'Permutation([], size=10)'), (Permutation([1, 0, 2]), 'Permutation([1, 0, 2])'), (Permutation([1, 0, 2, 3, 4, 5]), 'Permutation([1, 0], size=6)'), (Permutation([1, 0, 2, 3, 4, 5], size=10), 'Permutation([1, 0], size=10)'), ]: assert str(p) == s Permutation.print_cyclic = True for p, s in [ (Permutation([]), 'Permutation()'), (Permutation([], size=1), 'Permutation(0)'), (Permutation([], size=2), 'Permutation(1)'), (Permutation([], size=10), 'Permutation(9)'), (Permutation([1, 0, 2]), 'Permutation(2)(0, 1)'), (Permutation([1, 0, 2, 3, 4, 5]), 'Permutation(5)(0, 1)'), (Permutation([1, 0, 2, 3, 4, 5], size=10), 'Permutation(9)(0, 1)'), (Permutation([0, 1, 3, 2, 4, 5], size=10), 'Permutation(9)(2, 3)'), ]: assert str(p) == s def test_Pi(): assert str(pi) == "pi" def test_Poly(): assert str(Poly(0, x)) == "Poly(0, x, domain='ZZ')" assert str(Poly(1, x)) == "Poly(1, x, domain='ZZ')" assert str(Poly(x, x)) == "Poly(x, x, domain='ZZ')" assert str(Poly(2*x + 1, x)) == "Poly(2*x + 1, x, domain='ZZ')" assert str(Poly(2*x - 1, x)) == "Poly(2*x - 1, x, domain='ZZ')" assert str(Poly(-1, x)) == "Poly(-1, x, domain='ZZ')" assert str(Poly(-x, x)) == "Poly(-x, x, domain='ZZ')" assert str(Poly(-2*x + 1, x)) == "Poly(-2*x + 1, x, domain='ZZ')" assert str(Poly(-2*x - 1, x)) == "Poly(-2*x - 1, x, domain='ZZ')" assert str(Poly(x - 1, x)) == "Poly(x - 1, x, domain='ZZ')" assert str( Poly(x**2 + 1 + y, x)) == "Poly(x**2 + y + 1, x, domain='ZZ[y]')" assert str( Poly(x**2 - 1 + y, x)) == "Poly(x**2 + y - 1, x, domain='ZZ[y]')" assert str(Poly(x**2 + I*x, x)) == "Poly(x**2 + I*x, x, domain='EX')" assert str(Poly(x**2 - I*x, x)) == "Poly(x**2 - I*x, x, domain='EX')" assert str(Poly(-x*y*z + x*y - 1, x, y, z) ) == "Poly(-x*y*z + x*y - 1, x, y, z, domain='ZZ')" assert str(Poly(-w*x**21*y**7*z + (1 + w)*z**3 - 2*x*z + 1, x, y, z)) == \ "Poly(-w*x**21*y**7*z - 2*x*z + (w + 1)*z**3 + 1, x, y, z, domain='ZZ[w]')" assert str(Poly(x**2 + 1, x, modulus=2)) == "Poly(x**2 + 1, x, modulus=2)" assert str(Poly(2*x**2 + 3*x + 4, x, modulus=17)) == "Poly(2*x**2 + 3*x + 4, x, modulus=17)" def test_PolyRing(): assert str(ring("x", ZZ, lex)[0]) == "Polynomial ring in x over ZZ with lex order" assert str(ring("x,y", QQ, grlex)[0]) == "Polynomial ring in x, y over QQ with grlex order" assert str(ring("x,y,z", ZZ["t"], lex)[0]) == "Polynomial ring in x, y, z over ZZ[t] with lex order" def test_FracField(): assert str(field("x", ZZ, lex)[0]) == "Rational function field in x over ZZ with lex order" assert str(field("x,y", QQ, grlex)[0]) == "Rational function field in x, y over QQ with grlex order" assert str(field("x,y,z", ZZ["t"], lex)[0]) == "Rational function field in x, y, z over ZZ[t] with lex order" def test_PolyElement(): Ruv, u,v = ring("u,v", ZZ); Rxyz, x,y,z = ring("x,y,z", Ruv) assert str(x - x) == "0" assert str(x - 1) == "x - 1" assert str(x + 1) == "x + 1" assert str((u**2 + 3*u*v + 1)*x**2*y + u + 1) == "(u**2 + 3*u*v + 1)*x**2*y + u + 1" assert str((u**2 + 3*u*v + 1)*x**2*y + (u + 1)*x) == "(u**2 + 3*u*v + 1)*x**2*y + (u + 1)*x" assert str((u**2 + 3*u*v + 1)*x**2*y + (u + 1)*x + 1) == "(u**2 + 3*u*v + 1)*x**2*y + (u + 1)*x + 1" assert str((-u**2 + 3*u*v - 1)*x**2*y - (u + 1)*x - 1) == "-(u**2 - 3*u*v + 1)*x**2*y - (u + 1)*x - 1" assert str(-(v**2 + v + 1)*x + 3*u*v + 1) == "-(v**2 + v + 1)*x + 3*u*v + 1" assert str(-(v**2 + v + 1)*x - 3*u*v + 1) == "-(v**2 + v + 1)*x - 3*u*v + 1" def test_FracElement(): Fuv, u,v = field("u,v", ZZ); Fxyzt, x,y,z,t = field("x,y,z,t", Fuv) assert str(x - x) == "0" assert str(x - 1) == "x - 1" assert str(x + 1) == "x + 1" assert str(x/3) == "x/3" assert str(x/z) == "x/z" assert str(x*y/z) == "x*y/z" assert str(x/(z*t)) == "x/(z*t)" assert str(x*y/(z*t)) == "x*y/(z*t)" assert str((x - 1)/y) == "(x - 1)/y" assert str((x + 1)/y) == "(x + 1)/y" assert str((-x - 1)/y) == "(-x - 1)/y" assert str((x + 1)/(y*z)) == "(x + 1)/(y*z)" assert str(-y/(x + 1)) == "-y/(x + 1)" assert str(y*z/(x + 1)) == "y*z/(x + 1)" assert str(((u + 1)*x*y + 1)/((v - 1)*z - 1)) == "((u + 1)*x*y + 1)/((v - 1)*z - 1)" assert str(((u + 1)*x*y + 1)/((v - 1)*z - t*u*v - 1)) == "((u + 1)*x*y + 1)/((v - 1)*z - u*v*t - 1)" def test_Pow(): assert str(x**-1) == "1/x" assert str(x**-2) == "x**(-2)" assert str(x**2) == "x**2" assert str((x + y)**-1) == "1/(x + y)" assert str((x + y)**-2) == "(x + y)**(-2)" assert str((x + y)**2) == "(x + y)**2" assert str((x + y)**(1 + x)) == "(x + y)**(x + 1)" assert str(x**Rational(1, 3)) == "x**(1/3)" assert str(1/x**Rational(1, 3)) == "x**(-1/3)" assert str(sqrt(sqrt(x))) == "x**(1/4)" assert str(x**-1.0) == '1/x' def test_sqrt(): assert str(sqrt(x)) == "sqrt(x)" assert str(sqrt(x**2)) == "sqrt(x**2)" assert str(1/sqrt(x)) == "1/sqrt(x)" assert str(1/sqrt(x**2)) == "1/sqrt(x**2)" assert str(y/sqrt(x)) == "y/sqrt(x)" assert str(x**(1/2)) == "x**0.5" assert str(1/x**(1/2)) == "x**(-0.5)" def test_Rational(): n1 = Rational(1, 4) n2 = Rational(1, 3) n3 = Rational(2, 4) n4 = Rational(2, -4) n5 = Rational(0) n7 = Rational(3) n8 = Rational(-3) assert str(n1*n2) == "1/12" assert str(n1*n2) == "1/12" assert str(n3) == "1/2" assert str(n1*n3) == "1/8" assert str(n1 + n3) == "3/4" assert str(n1 + n2) == "7/12" assert str(n1 + n4) == "-1/4" assert str(n4*n4) == "1/4" assert str(n4 + n2) == "-1/6" assert str(n4 + n5) == "-1/2" assert str(n4*n5) == "0" assert str(n3 + n4) == "0" assert str(n1**n7) == "1/64" assert str(n2**n7) == "1/27" assert str(n2**n8) == "27" assert str(n7**n8) == "1/27" assert str(Rational("-25")) == "-25" assert str(Rational("1.25")) == "5/4" assert str(Rational("-2.6e-2")) == "-13/500" assert str(S("25/7")) == "25/7" assert str(S("-123/569")) == "-123/569" assert str(S("0.1[23]", rational=1)) == "61/495" assert str(S("5.1[666]", rational=1)) == "31/6" assert str(S("-5.1[666]", rational=1)) == "-31/6" assert str(S("0.[9]", rational=1)) == "1" assert str(S("-0.[9]", rational=1)) == "-1" assert str(sqrt(Rational(1, 4))) == "1/2" assert str(sqrt(Rational(1, 36))) == "1/6" assert str((123**25) ** Rational(1, 25)) == "123" assert str((123**25 + 1)**Rational(1, 25)) != "123" assert str((123**25 - 1)**Rational(1, 25)) != "123" assert str((123**25 - 1)**Rational(1, 25)) != "122" assert str(sqrt(Rational(81, 36))**3) == "27/8" assert str(1/sqrt(Rational(81, 36))**3) == "8/27" assert str(sqrt(-4)) == str(2*I) assert str(2**Rational(1, 10**10)) == "2**(1/10000000000)" def test_Float(): # NOTE prec is the whole number of decimal digits assert str(Float('1.23', prec=1 + 2)) == '1.23' assert str(Float('1.23456789', prec=1 + 8)) == '1.23456789' assert str( Float('1.234567890123456789', prec=1 + 18)) == '1.234567890123456789' assert str(pi.evalf(1 + 2)) == '3.14' assert str(pi.evalf(1 + 14)) == '3.14159265358979' assert str(pi.evalf(1 + 64)) == ('3.141592653589793238462643383279' '5028841971693993751058209749445923') assert str(pi.round(-1)) == '0.' assert str((pi**400 - (pi**400).round(1)).n(2)) == '-0.e+88' def test_Relational(): assert str(Rel(x, y, "<")) == "x < y" assert str(Rel(x + y, y, "==")) == "x + y == y" def test_RootOf(): assert str(RootOf(x**5 + 2*x - 1, 0)) == "RootOf(x**5 + 2*x - 1, 0)" def test_RootSum(): f = x**5 + 2*x - 1 assert str( RootSum(f, Lambda(z, z), auto=False)) == "RootSum(x**5 + 2*x - 1)" assert str(RootSum(f, Lambda( z, z**2), auto=False)) == "RootSum(x**5 + 2*x - 1, Lambda(z, z**2))" def test_GroebnerBasis(): assert str(groebner( [], x, y)) == "GroebnerBasis([], x, y, domain='ZZ', order='lex')" F = [x**2 - 3*y - x + 1, y**2 - 2*x + y - 1] assert str(groebner(F, order='grlex')) == \ "GroebnerBasis([x**2 - x - 3*y + 1, y**2 - 2*x + y - 1], x, y, domain='ZZ', order='grlex')" assert str(groebner(F, order='lex')) == \ "GroebnerBasis([2*x - y**2 - y + 1, y**4 + 2*y**3 - 3*y**2 - 16*y + 7], x, y, domain='ZZ', order='lex')" def test_set(): assert sstr(set()) == 'set()' assert sstr(frozenset()) == 'frozenset()' assert sstr(set([1, 2, 3])) == 'set([1, 2, 3])' assert sstr( set([1, x, x**2, x**3, x**4])) == 'set([1, x, x**2, x**3, x**4])' def test_SparseMatrix(): M = SparseMatrix([[x**+1, 1], [y, x + y]]) assert str(M) == "Matrix([[x, 1], [y, x + y]])" assert sstr(M) == "Matrix([\n[x, 1],\n[y, x + y]])" def test_Sum(): assert str(summation(cos(3*z), (z, x, y))) == "Sum(cos(3*z), (z, x, y))" assert str(Sum(x*y**2, (x, -2, 2), (y, -5, 5))) == \ "Sum(x*y**2, (x, -2, 2), (y, -5, 5))" def test_Symbol(): assert str(y) == "y" assert str(x) == "x" e = x assert str(e) == "x" def test_tuple(): assert str((x,)) == sstr((x,)) == "(x,)" assert str((x + y, 1 + x)) == sstr((x + y, 1 + x)) == "(x + y, x + 1)" assert str((x + y, ( 1 + x, x**2))) == sstr((x + y, (1 + x, x**2))) == "(x + y, (x + 1, x**2))" def test_Unit(): assert str(second) == "s" assert str(joule) == "kg*m**2/s**2" # issue 2461 def test_wild_str(): # Check expressions containing Wild not causing infinite recursion w = Wild('x') assert str(w + 1) == 'x_ + 1' assert str(exp(2**w) + 5) == 'exp(2**x_) + 5' assert str(3*w + 1) == '3*x_ + 1' assert str(1/w + 1) == '1 + 1/x_' assert str(w**2 + 1) == 'x_**2 + 1' assert str(1/(1 - w)) == '1/(-x_ + 1)' def test_zeta(): assert str(zeta(3)) == "zeta(3)" def test_bug2(): e = x - y a = str(e) b = str(e) assert a == b def test_bug4(): e = -2*sqrt(x) - y/sqrt(x)/2 assert str(e) not in ["(-2)*x**1/2(-1/2)*x**(-1/2)*y", "-2*x**1/2(-1/2)*x**(-1/2)*y", "-2*x**1/2-1/2*x**-1/2*w"] assert str(e) == "-2*sqrt(x) - y/(2*sqrt(x))" def test_issue922(): e = Integral(x, x) + 1 assert str(e) == 'Integral(x, x) + 1' def test_sstrrepr(): assert sstr('abc') == 'abc' assert sstrrepr('abc') == "'abc'" e = ['a', 'b', 'c', x] assert sstr(e) == "[a, b, c, x]" assert sstrrepr(e) == "['a', 'b', 'c', x]" def test_infinity(): assert sstr(oo*I) == "oo*I" def test_full_prec(): assert sstr(S("0.3"), full_prec=True) == "0.300000000000000" assert sstr(S("0.3"), full_prec="auto") == "0.300000000000000" assert sstr(S("0.3"), full_prec=False) == "0.3" assert sstr(S("0.3")*x, full_prec=True) in [ "0.300000000000000*x", "x*0.300000000000000" ] assert sstr(S("0.3")*x, full_prec="auto") in [ "0.3*x", "x*0.3" ] assert sstr(S("0.3")*x, full_prec=False) in [ "0.3*x", "x*0.3" ] def test_noncommutative(): A, B, C = symbols('A,B,C', commutative=False) assert sstr(A*B*C**-1) == "A*B*C**(-1)" assert sstr(C**-1*A*B) == "C**(-1)*A*B" assert sstr(A*C**-1*B) == "A*C**(-1)*B" assert sstr(sqrt(A)) == "sqrt(A)" assert sstr(1/sqrt(A)) == "A**(-1/2)" def test_empty_printer(): str_printer = StrPrinter() assert str_printer.emptyPrinter("foo") == "foo" assert str_printer.emptyPrinter(x*y) == "x*y" assert str_printer.emptyPrinter(32) == "32" def test_settings(): raises(TypeError, lambda: sstr(S(4), method="garbage")) def test_RandomDomain(): from sympy.stats import Normal, Die, Exponential, pspace, where X = Normal('x1', 0, 1) assert str(where(X > 0)) == "Domain: x1 > 0" D = Die('d1', 6) assert str(where(D > 4)) == "Domain: Or(d1 == 5, d1 == 6)" A = Exponential('a', 1) B = Exponential('b', 1) assert str(pspace(Tuple(A, B)).domain) == "Domain: And(a >= 0, b >= 0)" def test_FiniteSet(): assert str(FiniteSet(range(1, 51))) == '{1, 2, 3, ..., 48, 49, 50}' assert str(FiniteSet(range(1, 6))) == '{1, 2, 3, 4, 5}' def test_PrettyPoly(): from sympy.polys.domains import QQ F = QQ.frac_field(x, y) R = QQ[x, y] assert sstr(F.convert(x/(x + y))) == sstr(x/(x + y)) assert sstr(R.convert(x + y)) == sstr(x + y) def test_categories(): from sympy.categories import (Object, NamedMorphism, IdentityMorphism, Category) A = Object("A") B = Object("B") f = NamedMorphism(A, B, "f") id_A = IdentityMorphism(A) K = Category("K") assert str(A) == 'Object("A")' assert str(f) == 'NamedMorphism(Object("A"), Object("B"), "f")' assert str(id_A) == 'IdentityMorphism(Object("A"))' assert str(K) == 'Category("K")' def test_Tr(): A, B = symbols('A B', commutative=False) t = Tr(A*B) assert str(t) == 'Tr(A*B)' def test_issue3288(): assert str(factor(-3.0*z + 3)) == '-3.0*(1.0*z - 1.0)' def test_MatMul_MatAdd(): from sympy import MatrixSymbol assert str(2*(MatrixSymbol("X", 2, 2) + MatrixSymbol("Y", 2, 2))) == \ "2*(X + Y)" def test_MatrixSlice(): from sympy.matrices.expressions import MatrixSymbol assert str(MatrixSymbol('X', 10, 10)[:5, 1:9:2]) == 'X[:5, 1:9:2]' assert str(MatrixSymbol('X', 10, 10)[5, :5:2]) == 'X[5, :5:2]' def test_true_false(): assert str(true) == repr(true) == sstr(true) == "True" assert str(false) == repr(false) == sstr(false) == "False" def test_Equivalent(): assert str(Equivalent(y, x)) == "Equivalent(x, y)" def test_Xor(): assert str(Xor(y, x, evaluate=False)) == "Xor(x, y)" sympy-0.7.4.1/sympy/printing/tests/test_codeprinter.py0000644000175000017500000000075312253362407023355 0ustar georgeskgeorgeskfrom sympy.printing.codeprinter import CodePrinter from sympy.printing.ccode import ccode from sympy.core import C, symbols def setup_test_printer(*args, **kwargs): p = CodePrinter(*args, **kwargs) p._not_supported = set() p._number_symbols = set() return p def test_print_Dummy(): d = C.Dummy('d') p = setup_test_printer() assert p._print_Dummy(d) == "d_%i" % d.dummy_index def test_print_Mul(): x, y = symbols("x y") s = ccode(x ** (-3) * y ** (-2)) sympy-0.7.4.1/sympy/printing/tests/test_ccode.py0000644000175000017500000002216112253362407022111 0ustar georgeskgeorgeskfrom sympy.core import pi, oo, symbols, Function, Rational, Integer, GoldenRatio, EulerGamma, Catalan, Lambda, Dummy from sympy.functions import Piecewise, sin, cos, Abs, exp, ceiling, sqrt from sympy.utilities.pytest import raises from sympy.printing.ccode import CCodePrinter from sympy.utilities.lambdify import implemented_function from sympy.tensor import IndexedBase, Idx # import test from sympy import ccode x, y, z = symbols('x,y,z') g = Function('g') def test_printmethod(): class fabs(Abs): def _ccode(self, printer): return "fabs(%s)" % printer._print(self.args[0]) assert ccode(fabs(x)) == "fabs(x)" def test_ccode_sqrt(): assert ccode(sqrt(x)) == "sqrt(x)" assert ccode(x**0.5) == "sqrt(x)" assert ccode(sqrt(x)) == "sqrt(x)" def test_ccode_Pow(): assert ccode(x**3) == "pow(x, 3)" assert ccode(x**(y**3)) == "pow(x, pow(y, 3))" assert ccode(1/(g(x)*3.5)**(x - y**x)/(x**2 + y)) == \ "pow(3.5*g(x), -x + pow(y, x))/(pow(x, 2) + y)" assert ccode(x**-1.0) == '1.0/x' assert ccode(x**Rational(2, 3)) == 'pow(x, 2.0L/3.0L)' def test_ccode_constants_mathh(): assert ccode(exp(1)) == "M_E" assert ccode(pi) == "M_PI" assert ccode(oo) == "HUGE_VAL" assert ccode(-oo) == "-HUGE_VAL" def test_ccode_constants_other(): assert ccode(2*GoldenRatio) == "double const GoldenRatio = 1.61803398874989;\n2*GoldenRatio" assert ccode( 2*Catalan) == "double const Catalan = 0.915965594177219;\n2*Catalan" assert ccode(2*EulerGamma) == "double const EulerGamma = 0.577215664901533;\n2*EulerGamma" def test_ccode_Rational(): assert ccode(Rational(3, 7)) == "3.0L/7.0L" assert ccode(Rational(18, 9)) == "2" assert ccode(Rational(3, -7)) == "-3.0L/7.0L" assert ccode(Rational(-3, -7)) == "3.0L/7.0L" assert ccode(x + Rational(3, 7)) == "x + 3.0L/7.0L" assert ccode(Rational(3, 7)*x) == "(3.0L/7.0L)*x" def test_ccode_Integer(): assert ccode(Integer(67)) == "67" assert ccode(Integer(-1)) == "-1" def test_ccode_functions(): assert ccode(sin(x) ** cos(x)) == "pow(sin(x), cos(x))" def test_ccode_inline_function(): x = symbols('x') g = implemented_function('g', Lambda(x, 2*x)) assert ccode(g(x)) == "2*x" g = implemented_function('g', Lambda(x, 2*x/Catalan)) assert ccode( g(x)) == "double const Catalan = %s;\n2*x/Catalan" % Catalan.n() A = IndexedBase('A') i = Idx('i', symbols('n', integer=True)) g = implemented_function('g', Lambda(x, x*(1 + x)*(2 + x))) assert ccode(g(A[i]), assign_to=A[i]) == ( "for (int i=0; i> ~y)) == r"\neg (x \Rightarrow \neg y)" assert latex(~x, symbol_names={x: "x_i"}) == r"\neg x_i" assert latex(x & y, symbol_names={x: "x_i", y: "y_i"}) == \ r"x_i \wedge y_i" assert latex(x & y & z, symbol_names={x: "x_i", y: "y_i", z: "z_i"}) == \ r"x_i \wedge y_i \wedge z_i" assert latex(x | y, symbol_names={x: "x_i", y: "y_i"}) == r"x_i \vee y_i" assert latex(x | y | z, symbol_names={x: "x_i", y: "y_i", z: "z_i"}) == \ r"x_i \vee y_i \vee z_i" assert latex((x & y) | z, symbol_names={x: "x_i", y: "y_i", z: "z_i"}) == \ r"z_i \vee \left(x_i \wedge y_i\right)" assert latex(Implies(x, y), symbol_names={x: "x_i", y: "y_i"}) == \ r"x_i \Rightarrow y_i" def test_latex_builtins(): assert latex(True) == r"\mathrm{True}" assert latex(False) == r"\mathrm{False}" assert latex(None) == r"\mathrm{None}" assert latex(true) == r"\mathrm{True}" assert latex(false) == r'\mathrm{False}' def test_latex_Float(): assert latex(Float(1.0e100)) == r"1.0 \cdot 10^{100}" assert latex(Float(1.0e-100)) == r"1.0 \cdot 10^{-100}" assert latex(Float(1.0e-100), mul_symbol="times") == r"1.0 \times 10^{-100}" assert latex(1.0*oo) == r"\infty" assert latex(-1.0*oo) == r"- \infty" def test_latex_symbols(): Gamma, lmbda, rho = symbols('Gamma, lambda, rho') mass, volume = symbols('mass, volume') assert latex(Gamma + lmbda) == r"\Gamma + \lambda" assert latex(Gamma * lmbda) == r"\Gamma \lambda" assert latex(Symbol('q21')) == r"q_{21}" assert latex(Symbol('epsilon0')) == r"\epsilon_{0}" assert latex(Symbol('91')) == r"91" assert latex(Symbol('alpha_new')) == r"\alpha_{new}" assert latex(Symbol('C^orig')) == r"C^{orig}" assert latex(Symbol('x^alpha')) == r"x^{\alpha}" assert latex(Symbol('beta^alpha')) == r"\beta^{\alpha}" assert latex(Symbol('e^Alpha')) == r"e^{A}" @XFAIL def test_latex_symbols_failing(): rho, mass, volume = symbols('rho, mass, volume') assert latex( volume * rho == mass) == r"\rho \mathrm{volume} = \mathrm{mass}" assert latex(volume / mass * rho == 1) == r"\rho \mathrm{volume} {\mathrm{mass}}^{(-1)} = 1" assert latex(mass**3 * volume**3) == r"{\mathrm{mass}}^{3} \cdot {\mathrm{volume}}^{3}" def test_latex_functions(): assert latex(exp(x)) == "e^{x}" assert latex(exp(1) + exp(2)) == "e + e^{2}" f = Function('f') assert latex(f(x)) == r'f{\left (x \right )}' assert latex(f) == r'f' g = Function('g') assert latex(g(x, y)) == r'g{\left (x,y \right )}' assert latex(g) == r'g' h = Function('h') assert latex(h(x, y, z)) == r'h{\left (x,y,z \right )}' assert latex(h) == r'h' Li = Function('Li') assert latex(Li) == r'\operatorname{Li}' assert latex(Li(x)) == r'\operatorname{Li}{\left (x \right )}' beta = Function('beta') # not to be confused with the beta function assert latex(beta(x)) == r"\beta{\left (x \right )}" assert latex(beta) == r"\beta" assert latex(sin(x)) == r"\sin{\left (x \right )}" assert latex(sin(x), fold_func_brackets=True) == r"\sin {x}" assert latex(sin(2*x**2), fold_func_brackets=True) == \ r"\sin {2 x^{2}}" assert latex(sin(x**2), fold_func_brackets=True) == \ r"\sin {x^{2}}" assert latex(asin(x)**2) == r"\operatorname{asin}^{2}{\left (x \right )}" assert latex(asin(x)**2, inv_trig_style="full") == \ r"\arcsin^{2}{\left (x \right )}" assert latex(asin(x)**2, inv_trig_style="power") == \ r"\sin^{-1}{\left (x \right )}^{2}" assert latex(asin(x**2), inv_trig_style="power", fold_func_brackets=True) == \ r"\sin^{-1} {x^{2}}" assert latex(factorial(k)) == r"k!" assert latex(factorial(-k)) == r"\left(- k\right)!" assert latex(subfactorial(k)) == r"!k" assert latex(subfactorial(-k)) == r"!\left(- k\right)" assert latex(factorial2(k)) == r"k!!" assert latex(factorial2(-k)) == r"\left(- k\right)!!" assert latex(binomial(2, k)) == r"{\binom{2}{k}}" assert latex( FallingFactorial(3, k)) == r"{\left(3\right)}_{\left(k\right)}" assert latex(RisingFactorial(3, k)) == r"{\left(3\right)}^{\left(k\right)}" assert latex(floor(x)) == r"\lfloor{x}\rfloor" assert latex(ceiling(x)) == r"\lceil{x}\rceil" assert latex(Min(x, 2, x**3)) == r"\min\left(2, x, x^{3}\right)" assert latex(Min(x, y)**2) == r"\min\left(x, y\right)^{2}" assert latex(Max(x, 2, x**3)) == r"\max\left(2, x, x^{3}\right)" assert latex(Max(x, y)**2) == r"\max\left(x, y\right)^{2}" assert latex(Abs(x)) == r"\left\lvert{x}\right\rvert" assert latex(re(x)) == r"\Re{x}" assert latex(re(x + y)) == r"\Re{x} + \Re{y}" assert latex(im(x)) == r"\Im{x}" assert latex(conjugate(x)) == r"\overline{x}" assert latex(gamma(x)) == r"\Gamma{\left(x \right)}" w = Wild('w') assert latex(gamma(w)) == r"\Gamma{\left(w \right)}" assert latex(Order(x)) == r"\mathcal{O}\left(x\right)" assert latex(Order(x, x)) == r"\mathcal{O}\left(x\right)" assert latex(Order(x, (x, 0))) == r"\mathcal{O}\left(x\right)" assert latex(Order(x, (x, oo))) == r"\mathcal{O}\left(x; x\rightarrow\infty\right)" assert latex(Order(x, x, y)) == r"\mathcal{O}\left(x; \begin{pmatrix}x, & y\end{pmatrix}\rightarrow\begin{pmatrix}0, & 0\end{pmatrix}\right)" assert latex(Order(x, x, y)) == r"\mathcal{O}\left(x; \begin{pmatrix}x, & y\end{pmatrix}\rightarrow\begin{pmatrix}0, & 0\end{pmatrix}\right)" assert latex(Order(x, (x, oo), (y, oo))) == r"\mathcal{O}\left(x; \begin{pmatrix}x, & y\end{pmatrix}\rightarrow\begin{pmatrix}\infty, & \infty\end{pmatrix}\right)" assert latex(lowergamma(x, y)) == r'\gamma\left(x, y\right)' assert latex(uppergamma(x, y)) == r'\Gamma\left(x, y\right)' assert latex(cot(x)) == r'\cot{\left (x \right )}' assert latex(coth(x)) == r'\coth{\left (x \right )}' assert latex(re(x)) == r'\Re{x}' assert latex(im(x)) == r'\Im{x}' assert latex(root(x, y)) == r'x^{\frac{1}{y}}' assert latex(arg(x)) == r'\arg{\left (x \right )}' assert latex(zeta(x)) == r'\zeta\left(x\right)' assert latex(zeta(x)) == r"\zeta\left(x\right)" assert latex(zeta(x)**2) == r"\zeta^{2}\left(x\right)" assert latex(zeta(x, y)) == r"\zeta\left(x, y\right)" assert latex(zeta(x, y)**2) == r"\zeta^{2}\left(x, y\right)" assert latex(dirichlet_eta(x)) == r"\eta\left(x\right)" assert latex(dirichlet_eta(x)**2) == r"\eta^{2}\left(x\right)" assert latex(polylog(x, y)) == r"\operatorname{Li}_{x}\left(y\right)" assert latex( polylog(x, y)**2) == r"\operatorname{Li}_{x}^{2}\left(y\right)" assert latex(lerchphi(x, y, n)) == r"\Phi\left(x, y, n\right)" assert latex(lerchphi(x, y, n)**2) == r"\Phi^{2}\left(x, y, n\right)" assert latex(elliptic_k(z)) == r"K\left(z\right)" assert latex(elliptic_k(z)**2) == r"K^{2}\left(z\right)" assert latex(elliptic_f(x, y)) == r"F\left(x\middle| y\right)" assert latex(elliptic_f(x, y)**2) == r"F^{2}\left(x\middle| y\right)" assert latex(elliptic_e(x, y)) == r"E\left(x\middle| y\right)" assert latex(elliptic_e(x, y)**2) == r"E^{2}\left(x\middle| y\right)" assert latex(elliptic_e(z)) == r"E\left(z\right)" assert latex(elliptic_e(z)**2) == r"E^{2}\left(z\right)" assert latex(elliptic_pi(x, y, z)) == r"\Pi\left(x; y\middle| z\right)" assert latex(elliptic_pi(x, y, z)**2) == \ r"\Pi^{2}\left(x; y\middle| z\right)" assert latex(elliptic_pi(x, y)) == r"\Pi\left(x\middle| y\right)" assert latex(elliptic_pi(x, y)**2) == r"\Pi^{2}\left(x\middle| y\right)" assert latex(Ei(x)) == r'\operatorname{Ei}{\left (x \right )}' assert latex(Ei(x)**2) == r'\operatorname{Ei}^{2}{\left (x \right )}' assert latex(expint(x, y)**2) == r'\operatorname{E}_{x}^{2}\left(y\right)' assert latex(Shi(x)**2) == r'\operatorname{Shi}^{2}{\left (x \right )}' assert latex(Si(x)**2) == r'\operatorname{Si}^{2}{\left (x \right )}' assert latex(Ci(x)**2) == r'\operatorname{Ci}^{2}{\left (x \right )}' assert latex(Chi(x)**2) == r'\operatorname{Chi}^{2}{\left (x \right )}' assert latex(Chi(x)) == r'\operatorname{Chi}{\left (x \right )}' assert latex( jacobi(n, a, b, x)) == r'P_{n}^{\left(a,b\right)}\left(x\right)' assert latex(jacobi(n, a, b, x)**2) == r'\left(P_{n}^{\left(a,b\right)}\left(x\right)\right)^{2}' assert latex( gegenbauer(n, a, x)) == r'C_{n}^{\left(a\right)}\left(x\right)' assert latex(gegenbauer(n, a, x)**2) == r'\left(C_{n}^{\left(a\right)}\left(x\right)\right)^{2}' assert latex(chebyshevt(n, x)) == r'T_{n}\left(x\right)' assert latex( chebyshevt(n, x)**2) == r'\left(T_{n}\left(x\right)\right)^{2}' assert latex(chebyshevu(n, x)) == r'U_{n}\left(x\right)' assert latex( chebyshevu(n, x)**2) == r'\left(U_{n}\left(x\right)\right)^{2}' assert latex(legendre(n, x)) == r'P_{n}\left(x\right)' assert latex(legendre(n, x)**2) == r'\left(P_{n}\left(x\right)\right)^{2}' assert latex( assoc_legendre(n, a, x)) == r'P_{n}^{\left(a\right)}\left(x\right)' assert latex(assoc_legendre(n, a, x)**2) == r'\left(P_{n}^{\left(a\right)}\left(x\right)\right)^{2}' assert latex(laguerre(n, x)) == r'L_{n}\left(x\right)' assert latex(laguerre(n, x)**2) == r'\left(L_{n}\left(x\right)\right)^{2}' assert latex( assoc_laguerre(n, a, x)) == r'L_{n}^{\left(a\right)}\left(x\right)' assert latex(assoc_laguerre(n, a, x)**2) == r'\left(L_{n}^{\left(a\right)}\left(x\right)\right)^{2}' assert latex(hermite(n, x)) == r'H_{n}\left(x\right)' assert latex(hermite(n, x)**2) == r'\left(H_{n}\left(x\right)\right)^{2}' theta = Symbol("theta", real=True) phi = Symbol("phi", real=True) assert latex(Ynm(n,m,theta,phi)) == r'Y_{n}^{m}\left(\theta,\phi\right)' assert latex(Ynm(n, m, theta, phi)**3) == r'\left(Y_{n}^{m}\left(\theta,\phi\right)\right)^{3}' assert latex(Znm(n,m,theta,phi)) == r'Z_{n}^{m}\left(\theta,\phi\right)' assert latex(Znm(n, m, theta, phi)**3) == r'\left(Z_{n}^{m}\left(\theta,\phi\right)\right)^{3}' # Test latex printing of function names with "_" assert latex( polar_lift(0)) == r"\operatorname{polar\_lift}{\left (0 \right )}" assert latex(polar_lift( 0)**3) == r"\operatorname{polar\_lift}^{3}{\left (0 \right )}" assert latex(totient(n)) == r'\phi\left( n \right)' # some unknown function name should get rendered with \operatorname fjlkd = Function('fjlkd') assert latex(fjlkd(x)) == r'\operatorname{fjlkd}{\left (x \right )}' # even when it is referred to without an argument assert latex(fjlkd) == r'\operatorname{fjlkd}' def test_hyper_printing(): from sympy import pi from sympy.abc import x, z assert latex(meijerg(Tuple(pi, pi, x), Tuple(1), (0, 1), Tuple(1, 2, 3/pi), z)) == \ r'{G_{4, 5}^{2, 3}\left(\begin{matrix} \pi, \pi, x & 1 \\0, 1 & 1, 2, \frac{3}{\pi} \end{matrix} \middle| {z} \right)}' assert latex(meijerg(Tuple(), Tuple(1), (0,), Tuple(), z)) == \ r'{G_{1, 1}^{1, 0}\left(\begin{matrix} & 1 \\0 & \end{matrix} \middle| {z} \right)}' assert latex(hyper((x, 2), (3,), z)) == \ r'{{}_{2}F_{1}\left(\begin{matrix} x, 2 ' \ r'\\ 3 \end{matrix}\middle| {z} \right)}' assert latex(hyper(Tuple(), Tuple(1), z)) == \ r'{{}_{0}F_{1}\left(\begin{matrix} ' \ r'\\ 1 \end{matrix}\middle| {z} \right)}' def test_latex_bessel(): from sympy.functions.special.bessel import (besselj, bessely, besseli, besselk, hankel1, hankel2, jn, yn) from sympy.abc import z assert latex(besselj(n, z**2)**k) == r'J^{k}_{n}\left(z^{2}\right)' assert latex(bessely(n, z)) == r'Y_{n}\left(z\right)' assert latex(besseli(n, z)) == r'I_{n}\left(z\right)' assert latex(besselk(n, z)) == r'K_{n}\left(z\right)' assert latex(hankel1(n, z**2)**2) == \ r'\left(H^{(1)}_{n}\left(z^{2}\right)\right)^{2}' assert latex(hankel2(n, z)) == r'H^{(2)}_{n}\left(z\right)' assert latex(jn(n, z)) == r'j_{n}\left(z\right)' assert latex(yn(n, z)) == r'y_{n}\left(z\right)' def test_latex_fresnel(): from sympy.functions.special.error_functions import (fresnels, fresnelc) from sympy.abc import z assert latex(fresnels(z)) == r'S\left(z\right)' assert latex(fresnelc(z)) == r'C\left(z\right)' assert latex(fresnels(z)**2) == r'S^{2}\left(z\right)' assert latex(fresnelc(z)**2) == r'C^{2}\left(z\right)' def test_latex_brackets(): assert latex((-1)**x) == r"\left(-1\right)^{x}" def test_latex_derivatives(): # regular "d" for ordinary derivatives assert latex(diff(x**3, x, evaluate=False)) == \ r"\frac{d}{d x} x^{3}" assert latex(diff(sin(x) + x**2, x, evaluate=False)) == \ r"\frac{d}{d x}\left(x^{2} + \sin{\left (x \right )}\right)" assert latex(diff(diff(sin(x) + x**2, x, evaluate=False), evaluate=False)) == \ r"\frac{d^{2}}{d x^{2}} \left(x^{2} + \sin{\left (x \right )}\right)" assert latex(diff(diff(diff(sin(x) + x**2, x, evaluate=False), evaluate=False), evaluate=False)) == \ r"\frac{d^{3}}{d x^{3}} \left(x^{2} + \sin{\left (x \right )}\right)" # \partial for partial derivatives assert latex(diff(sin(x * y), x, evaluate=False)) == \ r"\frac{\partial}{\partial x} \sin{\left (x y \right )}" assert latex(diff(sin(x * y) + x**2, x, evaluate=False)) == \ r"\frac{\partial}{\partial x}\left(x^{2} + \sin{\left (x y \right )}\right)" assert latex(diff(diff(sin(x*y) + x**2, x, evaluate=False), x, evaluate=False)) == \ r"\frac{\partial^{2}}{\partial x^{2}} \left(x^{2} + \sin{\left (x y \right )}\right)" assert latex(diff(diff(diff(sin(x*y) + x**2, x, evaluate=False), x, evaluate=False), x, evaluate=False)) == \ r"\frac{\partial^{3}}{\partial x^{3}} \left(x^{2} + \sin{\left (x y \right )}\right)" # mixed partial derivatives f = Function("f") assert latex(diff(diff(f(x,y), x, evaluate=False), y, evaluate=False)) == \ r"\frac{\partial^{2}}{\partial x\partial y} " + latex(f(x,y)) assert latex(diff(diff(diff(f(x,y), x, evaluate=False), x, evaluate=False), y, evaluate=False)) == \ r"\frac{\partial^{3}}{\partial x^{2}\partial y} " + latex(f(x,y)) # use ordinary d when one of the variables has been integrated out assert latex(diff(Integral(exp(-x * y), (x, 0, oo)), y, evaluate=False)) == \ r"\frac{d}{d y} \int_{0}^{\infty} e^{- x y}\, dx" def test_latex_subs(): assert latex(Subs(x*y, ( x, y), (1, 2))) == r'\left. x y \right|_{\substack{ x=1\\ y=2 }}' def test_latex_integrals(): assert latex(Integral(log(x), x)) == r"\int \log{\left (x \right )}\, dx" assert latex(Integral(x**2, (x, 0, 1))) == r"\int_{0}^{1} x^{2}\, dx" assert latex(Integral(x**2, (x, 10, 20))) == r"\int_{10}^{20} x^{2}\, dx" assert latex(Integral( y*x**2, (x, 0, 1), y)) == r"\int\int_{0}^{1} x^{2} y\, dx\, dy" assert latex(Integral(y*x**2, (x, 0, 1), y), mode='equation*') \ == r"\begin{equation*}\int\int\limits_{0}^{1} x^{2} y\, dx\, dy\end{equation*}" assert latex(Integral(y*x**2, (x, 0, 1), y), mode='equation*', itex=True) \ == r"$$\int\int_{0}^{1} x^{2} y\, dx\, dy$$" assert latex(Integral(x, (x, 0))) == r"\int^{0} x\, dx" assert latex(Integral(x*y, x, y)) == r"\iint x y\, dx\, dy" assert latex(Integral(x*y*z, x, y, z)) == r"\iiint x y z\, dx\, dy\, dz" assert latex(Integral(x*y*z*t, x, y, z, t)) == \ r"\iiiint t x y z\, dx\, dy\, dz\, dt" assert latex(Integral(x, x, x, x, x, x, x)) == \ r"\int\int\int\int\int\int x\, dx\, dx\, dx\, dx\, dx\, dx" assert latex(Integral(x, x, y, (z, 0, 1))) == \ r"\int_{0}^{1}\int\int x\, dx\, dy\, dz" def test_latex_sets(): for s in (FiniteSet, frozenset, set): assert latex(s([x*y, x**2])) == r"\left\{x^{2}, x y\right\}" assert latex(s(range(1, 6))) == r"\left\{1, 2, 3, 4, 5\right\}" assert latex(s(range(1, 13))) == \ r"\left\{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12\right\}" def test_latex_Range(): assert latex(Range(1, 51)) == \ r'\left\{1, 2, \ldots, 50\right\}' assert latex(Range(1, 4)) == r'\left\{1, 2, 3\right\}' def test_latex_intervals(): a = Symbol('a', real=True) assert latex(Interval(0, 0)) == r"\left\{0\right\}" assert latex(Interval(0, a)) == r"\left[0, a\right]" assert latex(Interval(0, a, False, False)) == r"\left[0, a\right]" assert latex(Interval(0, a, True, False)) == r"\left(0, a\right]" assert latex(Interval(0, a, False, True)) == r"\left[0, a\right)" assert latex(Interval(0, a, True, True)) == r"\left(0, a\right)" def test_latex_emptyset(): assert latex(S.EmptySet) == r"\emptyset" def test_latex_union(): assert latex(Union(Interval(0, 1), Interval(2, 3))) == \ r"\left[0, 1\right] \cup \left[2, 3\right]" assert latex(Union(Interval(1, 1), Interval(2, 2), Interval(3, 4))) == \ r"\left\{1, 2\right\} \cup \left[3, 4\right]" def test_latex_productset(): line = Interval(0, 1) bigline = Interval(0, 10) fset = FiniteSet(1, 2, 3) assert latex(line**2) == r"%s^2" % latex(line) assert latex(line * bigline * fset) == r"%s \times %s \times %s" % ( latex(line), latex(bigline), latex(fset)) def test_latex_Naturals(): assert latex(S.Naturals) == r"\mathbb{N}" assert latex(S.Integers) == r"\mathbb{Z}" def test_latex_ImageSet(): x = Symbol('x') assert latex(ImageSet(Lambda(x, x**2), S.Naturals)) == \ r"\left\{x^{2}\; |\; x \in \mathbb{N}\right\}" def test_latex_sum(): assert latex(Sum(x*y**2, (x, -2, 2), (y, -5, 5))) == \ r"\sum_{\substack{-2 \leq x \leq 2\\-5 \leq y \leq 5}} x y^{2}" assert latex(Sum(x**2, (x, -2, 2))) == \ r"\sum_{x=-2}^{2} x^{2}" assert latex(Sum(x**2 + y, (x, -2, 2))) == \ r"\sum_{x=-2}^{2} \left(x^{2} + y\right)" def test_latex_product(): assert latex(Product(x*y**2, (x, -2, 2), (y, -5, 5))) == \ r"\prod_{\substack{-2 \leq x \leq 2\\-5 \leq y \leq 5}} x y^{2}" assert latex(Product(x**2, (x, -2, 2))) == \ r"\prod_{x=-2}^{2} x^{2}" assert latex(Product(x**2 + y, (x, -2, 2))) == \ r"\prod_{x=-2}^{2} \left(x^{2} + y\right)" def test_latex_limits(): assert latex(Limit(x, x, oo)) == r"\lim_{x \to \infty} x" def test_issue469(): beta = Symbol(r'\beta') y = beta + x assert latex(y) in [r'\beta + x', r'x + \beta'] beta = Symbol(r'beta') y = beta + x assert latex(y) in [r'\beta + x', r'x + \beta'] def test_latex(): assert latex((2*tau)**Rational(7, 2)) == "8 \\sqrt{2} \\tau^{\\frac{7}{2}}" assert latex((2*mu)**Rational(7, 2), mode='equation*') == \ "\\begin{equation*}8 \\sqrt{2} \\mu^{\\frac{7}{2}}\\end{equation*}" assert latex((2*mu)**Rational(7, 2), mode='equation', itex=True) == \ "$$8 \\sqrt{2} \\mu^{\\frac{7}{2}}$$" assert latex([2/x, y]) == "\\begin{bmatrix}\\frac{2}{x}, & y\\end{bmatrix}" def test_latex_dict(): d = {Rational(1): 1, x**2: 2, x: 3, x**3: 4} assert latex(d) == '\\begin{Bmatrix}1 : 1, & x : 3, & x^{2} : 2, & x^{3} : 4\\end{Bmatrix}' D = Dict(d) assert latex(D) == '\\begin{Bmatrix}1 : 1, & x : 3, & x^{2} : 2, & x^{3} : 4\\end{Bmatrix}' def test_latex_rational(): #tests issue 874 assert latex(-Rational(1, 2)) == "- \\frac{1}{2}" assert latex(Rational(-1, 2)) == "- \\frac{1}{2}" assert latex(Rational(1, -2)) == "- \\frac{1}{2}" assert latex(-Rational(-1, 2)) == "\\frac{1}{2}" assert latex(-Rational(1, 2)*x) == "- \\frac{x}{2}" assert latex(-Rational(1, 2)*x + Rational(-2, 3)*y) == \ "- \\frac{x}{2} - \\frac{2 y}{3}" def test_latex_inverse(): #tests issue 1030 assert latex(1/x) == "\\frac{1}{x}" assert latex(1/(x + y)) == "\\frac{1}{x + y}" def test_latex_DiracDelta(): assert latex(DiracDelta(x)) == r"\delta\left(x\right)" assert latex(DiracDelta(x)**2) == r"\left(\delta\left(x\right)\right)^{2}" assert latex(DiracDelta(x, 0)) == r"\delta\left(x\right)" assert latex(DiracDelta(x, 5)) == \ r"\delta^{\left( 5 \right)}\left( x \right)" assert latex(DiracDelta(x, 5)**2) == \ r"\left(\delta^{\left( 5 \right)}\left( x \right)\right)^{2}" def test_latex_Heaviside(): assert latex(Heaviside(x)) == r"\theta\left(x\right)" assert latex(Heaviside(x)**2) == r"\left(\theta\left(x\right)\right)^{2}" def test_latex_KroneckerDelta(): assert latex(KroneckerDelta(x, y)) == r"\delta_{x y}" assert latex(KroneckerDelta(x, y + 1)) == r"\delta_{x, y + 1}" # issue 3479 assert latex(KroneckerDelta(x + 1, y)) == r"\delta_{y, x + 1}" def test_latex_LeviCivita(): assert latex(LeviCivita(x, y, z)) == r"\varepsilon_{x y z}" assert latex(LeviCivita(x, y, z)**2) == r"\left(\varepsilon_{x y z}\right)^{2}" assert latex(LeviCivita(x, y, z + 1)) == r"\varepsilon_{x, y, z + 1}" assert latex(LeviCivita(x, y + 1, z)) == r"\varepsilon_{x, y + 1, z}" assert latex(LeviCivita(x + 1, y, z)) == r"\varepsilon_{x + 1, y, z}" def test_mode(): expr = x + y assert latex(expr) == 'x + y' assert latex(expr, mode='plain') == 'x + y' assert latex(expr, mode='inline') == '$x + y$' assert latex( expr, mode='equation*') == '\\begin{equation*}x + y\\end{equation*}' assert latex( expr, mode='equation') == '\\begin{equation}x + y\\end{equation}' def test_latex_Piecewise(): p = Piecewise((x, x < 1), (x**2, True)) assert latex(p) == "\\begin{cases} x & \\text{for}\: x < 1 \\\\x^{2} &" \ " \\text{otherwise} \\end{cases}" assert latex(p, itex=True) == "\\begin{cases} x & \\text{for}\: x \\lt 1 \\\\x^{2} &" \ " \\text{otherwise} \\end{cases}" p = Piecewise((x, x < 0), (0, x >= 0)) assert latex(p) == "\\begin{cases} x & \\text{for}\\: x < 0 \\\\0 &" \ " \\text{for}\\: x \\geq 0 \\end{cases}" A, B = symbols("A B", commutative=False) p = Piecewise((A**2, Eq(A, B)), (A*B, True)) s = r"\begin{cases} A^{2} & \text{for}\: A = B \\A B & \text{otherwise} \end{cases}" assert latex(p) == s assert latex(A*p) == r"A %s" % s assert latex(p*A) == r"\left(%s\right) A" % s def test_latex_Matrix(): M = Matrix([[1 + x, y], [y, x - 1]]) assert latex(M) == \ r'\left[\begin{matrix}x + 1 & y\\y & x - 1\end{matrix}\right]' assert latex(M, mode='inline') == \ r'$\left[\begin{smallmatrix}x + 1 & y\\' \ r'y & x - 1\end{smallmatrix}\right]$' assert latex(M, mat_str='array') == \ r'\left[\begin{array}{cc}x + 1 & y\\y & x - 1\end{array}\right]' assert latex(M, mat_str='bmatrix') == \ r'\left[\begin{bmatrix}x + 1 & y\\y & x - 1\end{bmatrix}\right]' assert latex(M, mat_delim=None, mat_str='bmatrix') == \ r'\begin{bmatrix}x + 1 & y\\y & x - 1\end{bmatrix}' M2 = Matrix(1, 11, range(11)) assert latex(M2) == \ r'\left[\begin{array}{ccccccccccc}' \ r'0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10\end{array}\right]' def test_latex_mul_symbol(): assert latex(4*4**x, mul_symbol='times') == "4 \\times 4^{x}" assert latex(4*4**x, mul_symbol='dot') == "4 \\cdot 4^{x}" assert latex(4*4**x, mul_symbol='ldot') == "4 \,.\, 4^{x}" assert latex(4*x, mul_symbol='times') == "4 \\times x" assert latex(4*x, mul_symbol='dot') == "4 \\cdot x" assert latex(4*x, mul_symbol='ldot') == "4 \,.\, x" def test_latex_issue1282(): y = 4*4**log(2) assert latex(y) == r'4 \cdot 4^{\log{\left (2 \right )}}' assert latex(1/y) == r'\frac{1}{4 \cdot 4^{\log{\left (2 \right )}}}' def test_latex_issue1477(): assert latex(Symbol("beta_13_2")) == r"\beta_{13 2}" assert latex(Symbol("beta_132_20")) == r"\beta_{132 20}" assert latex(Symbol("beta_13")) == r"\beta_{13}" assert latex(Symbol("x_a_b")) == r"x_{a b}" assert latex(Symbol("x_1_2_3")) == r"x_{1 2 3}" assert latex(Symbol("x_a_b1")) == r"x_{a b1}" assert latex(Symbol("x_a_1")) == r"x_{a 1}" assert latex(Symbol("x_1_a")) == r"x_{1 a}" assert latex(Symbol("x_1^aa")) == r"x^{aa}_{1}" assert latex(Symbol("x_1__aa")) == r"x^{aa}_{1}" assert latex(Symbol("x_11^a")) == r"x^{a}_{11}" assert latex(Symbol("x_11__a")) == r"x^{a}_{11}" assert latex(Symbol("x_a_a_a_a")) == r"x_{a a a a}" assert latex(Symbol("x_a_a^a^a")) == r"x^{a a}_{a a}" assert latex(Symbol("x_a_a__a__a")) == r"x^{a a}_{a a}" assert latex(Symbol("alpha_11")) == r"\alpha_{11}" assert latex(Symbol("alpha_11_11")) == r"\alpha_{11 11}" assert latex(Symbol("alpha_alpha")) == r"\alpha_{\alpha}" assert latex(Symbol("alpha^aleph")) == r"\alpha^{\aleph}" assert latex(Symbol("alpha__aleph")) == r"\alpha^{\aleph}" def test_latex_pow_fraction(): x = Symbol('x') # Testing exp assert 'e^{-x}' in latex(exp(-x)/2).replace(' ', '') # Remove Whitespace # Testing just e^{-x} in case future changes alter behavior of muls or fracs # In particular current output is \frac{1}{2}e^{- x} but perhaps this will # change to \frac{e^{-x}}{2} # Testing general, non-exp, power assert '3^{-x}' in latex(3**-x/2).replace(' ', '') def test_noncommutative(): A, B, C = symbols('A,B,C', commutative=False) assert latex(A*B*C**-1) == "A B C^{-1}" assert latex(C**-1*A*B) == "C^{-1} A B" assert latex(A*C**-1*B) == "A C^{-1} B" def test_latex_order(): expr = x**3 + x**2*y + 3*x*y**3 + y**4 assert latex(expr, order='lex') == "x^{3} + x^{2} y + 3 x y^{3} + y^{4}" assert latex( expr, order='rev-lex') == "y^{4} + 3 x y^{3} + x^{2} y + x^{3}" def test_latex_Lambda(): assert latex(Lambda(x, x + 1)) == \ r"\Lambda {\left (x, x + 1 \right )}" assert latex(Lambda((x, y), x + 1)) == \ r"\Lambda {\left (\begin{pmatrix}x, & y\end{pmatrix}, x + 1 \right )}" def test_latex_PolyElement(): Ruv, u,v = ring("u,v", ZZ); Rxyz, x,y,z = ring("x,y,z", Ruv) assert latex(x - x) == r"0" assert latex(x - 1) == r"x - 1" assert latex(x + 1) == r"x + 1" assert latex((u**2 + 3*u*v + 1)*x**2*y + u + 1) == r"\left({u}^{2} + 3 u v + 1\right) {x}^{2} y + u + 1" assert latex((u**2 + 3*u*v + 1)*x**2*y + (u + 1)*x) == r"\left({u}^{2} + 3 u v + 1\right) {x}^{2} y + \left(u + 1\right) x" assert latex((u**2 + 3*u*v + 1)*x**2*y + (u + 1)*x + 1) == r"\left({u}^{2} + 3 u v + 1\right) {x}^{2} y + \left(u + 1\right) x + 1" assert latex((-u**2 + 3*u*v - 1)*x**2*y - (u + 1)*x - 1) == r"-\left({u}^{2} - 3 u v + 1\right) {x}^{2} y - \left(u + 1\right) x - 1" assert latex(-(v**2 + v + 1)*x + 3*u*v + 1) == r"-\left({v}^{2} + v + 1\right) x + 3 u v + 1" assert latex(-(v**2 + v + 1)*x - 3*u*v + 1) == r"-\left({v}^{2} + v + 1\right) x - 3 u v + 1" def test_latex_FracElement(): Fuv, u,v = field("u,v", ZZ); Fxyzt, x,y,z,t = field("x,y,z,t", Fuv) assert latex(x - x) == r"0" assert latex(x - 1) == r"x - 1" assert latex(x + 1) == r"x + 1" assert latex(x/3) == r"\frac{x}{3}" assert latex(x/z) == r"\frac{x}{z}" assert latex(x*y/z) == r"\frac{x y}{z}" assert latex(x/(z*t)) == r"\frac{x}{z t}" assert latex(x*y/(z*t)) == r"\frac{x y}{z t}" assert latex((x - 1)/y) == r"\frac{x - 1}{y}" assert latex((x + 1)/y) == r"\frac{x + 1}{y}" assert latex((-x - 1)/y) == r"\frac{-x - 1}{y}" assert latex((x + 1)/(y*z)) == r"\frac{x + 1}{y z}" assert latex(-y/(x + 1)) == r"\frac{-y}{x + 1}" assert latex(y*z/(x + 1)) == r"\frac{y z}{x + 1}" assert latex(((u + 1)*x*y + 1)/((v - 1)*z - 1)) == r"\frac{\left(u + 1\right) x y + 1}{\left(v - 1\right) z - 1}" assert latex(((u + 1)*x*y + 1)/((v - 1)*z - t*u*v - 1)) == r"\frac{\left(u + 1\right) x y + 1}{\left(v - 1\right) z - u v t - 1}" def test_latex_Poly(): assert latex(Poly(x**2 + 2 * x, x)) == \ r"\operatorname{Poly}{\left( x^{2} + 2 x, x, domain=\mathbb{Z} \right)}" assert latex(Poly(x/y, x)) == \ r"\operatorname{Poly}{\left( \frac{x}{y}, x, domain=\mathbb{Z}\left(y\right) \right)}" assert latex(Poly(2.0*x + y)) == \ r"\operatorname{Poly}{\left( 2.0 x + 1.0 y, x, y, domain=\mathbb{R} \right)}" def test_latex_RootOf(): assert latex(RootOf(x**5 + x + 3, 0)) == \ r"\operatorname{RootOf} {\left(x^{5} + x + 3, 0\right)}" def test_latex_RootSum(): assert latex(RootSum(x**5 + x + 3, sin)) == \ r"\operatorname{RootSum} {\left(x^{5} + x + 3, \Lambda {\left (x, \sin{\left (x \right )} \right )}\right)}" def test_settings(): raises(TypeError, lambda: latex(x*y, method="garbage")) def test_latex_numbers(): assert latex(catalan(n)) == r"C_{n}" def test_lamda(): assert latex(Symbol('lamda')) == r"\lambda" assert latex(Symbol('Lamda')) == r"\Lambda" def test_custom_symbol_names(): x = Symbol('x') y = Symbol('y') assert latex(x) == "x" assert latex(x, symbol_names={x: "x_i"}) == "x_i" assert latex(x + y, symbol_names={x: "x_i"}) == "x_i + y" assert latex(x**2, symbol_names={x: "x_i"}) == "x_i^{2}" assert latex(x + y, symbol_names={x: "x_i", y: "y_j"}) == "x_i + y_j" def test_matAdd(): from sympy import MatrixSymbol from sympy.printing.latex import LatexPrinter C = MatrixSymbol('C', 5, 5) B = MatrixSymbol('B', 5, 5) l = LatexPrinter() assert l._print_MatAdd(C - 2*B) in ['-2 B + C', 'C -2 B'] assert l._print_MatAdd(C + 2*B) in ['2 B + C', 'C + 2 B'] assert l._print_MatAdd(B - 2*C) in ['B -2 C', '-2 C + B'] assert l._print_MatAdd(B + 2*C) in ['B + 2 C', '2 C + B'] def test_matMul(): from sympy import MatrixSymbol from sympy.printing.latex import LatexPrinter A = MatrixSymbol('A', 5, 5) B = MatrixSymbol('B', 5, 5) x = Symbol('x') l = LatexPrinter() assert l._print_MatMul(2*A) == '2 A' assert l._print_MatMul(2*x*A) == '2 x A' assert l._print_MatMul(-2*A) == '-2 A' assert l._print_MatMul(1.5*A) == '1.5 A' assert l._print_MatMul(sqrt(2)*A) == r'\sqrt{2} A' assert l._print_MatMul(-sqrt(2)*A) == r'- \sqrt{2} A' assert l._print_MatMul(2*sqrt(2)*x*A) == r'2 \sqrt{2} x A' assert l._print_MatMul(-2*A*(A + 2*B)) in [r'-2 A \left(A + 2 B\right)', r'-2 A \left(2 B + A\right)'] def test_latex_MatrixSlice(): from sympy.matrices.expressions import MatrixSymbol assert latex(MatrixSymbol('X', 10, 10)[:5, 1:9:2]) == \ r'X\left[:5, 1:9:2\right]' assert latex(MatrixSymbol('X', 10, 10)[5, :5:2]) == \ r'X\left[5, :5:2\right]' def test_latex_RandomDomain(): from sympy.stats import Normal, Die, Exponential, pspace, where X = Normal('x1', 0, 1) assert latex(where(X > 0)) == "Domain: x_{1} > 0" D = Die('d1', 6) assert latex(where(D > 4)) == r"Domain: d_{1} = 5 \vee d_{1} = 6" A = Exponential('a', 1) B = Exponential('b', 1) assert latex( pspace(Tuple(A, B)).domain) == "Domain: a \geq 0 \wedge b \geq 0" def test_PrettyPoly(): from sympy.polys.domains import QQ F = QQ.frac_field(x, y) R = QQ[x, y] assert latex(F.convert(x/(x + y))) == latex(x/(x + y)) assert latex(R.convert(x + y)) == latex(x + y) def test_integral_transforms(): x = Symbol("x") k = Symbol("k") f = Function("f") a = Symbol("a") b = Symbol("b") assert latex(MellinTransform(f(x), x, k)) == r"\mathcal{M}_{x}\left[f{\left (x \right )}\right]\left(k\right)" assert latex(InverseMellinTransform(f(k), k, x, a, b)) == r"\mathcal{M}^{-1}_{k}\left[f{\left (k \right )}\right]\left(x\right)" assert latex(LaplaceTransform(f(x), x, k)) == r"\mathcal{L}_{x}\left[f{\left (x \right )}\right]\left(k\right)" assert latex(InverseLaplaceTransform(f(k), k, x, (a, b))) == r"\mathcal{L}^{-1}_{k}\left[f{\left (k \right )}\right]\left(x\right)" assert latex(FourierTransform(f(x), x, k)) == r"\mathcal{F}_{x}\left[f{\left (x \right )}\right]\left(k\right)" assert latex(InverseFourierTransform(f(k), k, x)) == r"\mathcal{F}^{-1}_{k}\left[f{\left (k \right )}\right]\left(x\right)" assert latex(CosineTransform(f(x), x, k)) == r"\mathcal{COS}_{x}\left[f{\left (x \right )}\right]\left(k\right)" assert latex(InverseCosineTransform(f(k), k, x)) == r"\mathcal{COS}^{-1}_{k}\left[f{\left (k \right )}\right]\left(x\right)" assert latex(SineTransform(f(x), x, k)) == r"\mathcal{SIN}_{x}\left[f{\left (x \right )}\right]\left(k\right)" assert latex(InverseSineTransform(f(k), k, x)) == r"\mathcal{SIN}^{-1}_{k}\left[f{\left (k \right )}\right]\left(x\right)" def test_PolynomialRingBase(): from sympy.polys.domains import QQ assert latex(QQ.old_poly_ring(x, y)) == r"\mathbb{Q}\left[x, y\right]" assert latex(QQ.old_poly_ring(x, y, order="ilex")) == \ r"S_<^{-1}\mathbb{Q}\left[x, y\right]" def test_categories(): from sympy.categories import (Object, IdentityMorphism, NamedMorphism, Category, Diagram, DiagramGrid) A1 = Object("A1") A2 = Object("A2") A3 = Object("A3") f1 = NamedMorphism(A1, A2, "f1") f2 = NamedMorphism(A2, A3, "f2") id_A1 = IdentityMorphism(A1) K1 = Category("K1") assert latex(A1) == "A_{1}" assert latex(f1) == "f_{1}:A_{1}\\rightarrow A_{2}" assert latex(id_A1) == "id:A_{1}\\rightarrow A_{1}" assert latex(f2*f1) == "f_{2}\\circ f_{1}:A_{1}\\rightarrow A_{3}" assert latex(K1) == "\mathbf{K_{1}}" d = Diagram() assert latex(d) == "\emptyset" d = Diagram({f1: "unique", f2: S.EmptySet}) assert latex(d) == "\\begin{Bmatrix}f_{2}\\circ f_{1}:A_{1}" \ "\\rightarrow A_{3} : \\emptyset, & id:A_{1}\\rightarrow " \ "A_{1} : \\emptyset, & id:A_{2}\\rightarrow A_{2} : " \ "\\emptyset, & id:A_{3}\\rightarrow A_{3} : \\emptyset, " \ "& f_{1}:A_{1}\\rightarrow A_{2} : \\left\\{unique\\right\\}, " \ "& f_{2}:A_{2}\\rightarrow A_{3} : \\emptyset\\end{Bmatrix}" d = Diagram({f1: "unique", f2: S.EmptySet}, {f2 * f1: "unique"}) assert latex(d) == "\\begin{Bmatrix}f_{2}\\circ f_{1}:A_{1}" \ "\\rightarrow A_{3} : \\emptyset, & id:A_{1}\\rightarrow " \ "A_{1} : \\emptyset, & id:A_{2}\\rightarrow A_{2} : " \ "\\emptyset, & id:A_{3}\\rightarrow A_{3} : \\emptyset, " \ "& f_{1}:A_{1}\\rightarrow A_{2} : \\left\\{unique\\right\\}," \ " & f_{2}:A_{2}\\rightarrow A_{3} : \\emptyset\\end{Bmatrix}" \ "\\Longrightarrow \\begin{Bmatrix}f_{2}\\circ f_{1}:A_{1}" \ "\\rightarrow A_{3} : \\left\\{unique\\right\\}\\end{Bmatrix}" # A linear diagram. A = Object("A") B = Object("B") C = Object("C") f = NamedMorphism(A, B, "f") g = NamedMorphism(B, C, "g") d = Diagram([f, g]) grid = DiagramGrid(d) assert latex(grid) == "\\begin{array}{cc}\n" \ "A & B \\\\\n" \ " & C \n" \ "\\end{array}\n" def test_Modules(): from sympy.polys.domains import QQ from sympy.polys.agca import homomorphism R = QQ.old_poly_ring(x, y) F = R.free_module(2) M = F.submodule([x, y], [1, x**2]) assert latex(F) == r"{\mathbb{Q}\left[x, y\right]}^{2}" assert latex(M) == \ r"\left< {\left[ {x},{y} \right]},{\left[ {1},{x^{2}} \right]} \right>" I = R.ideal(x**2, y) assert latex(I) == r"\left< {x^{2}},{y} \right>" Q = F / M assert latex(Q) == r"\frac{{\mathbb{Q}\left[x, y\right]}^{2}}{\left< {\left[ {x},{y} \right]},{\left[ {1},{x^{2}} \right]} \right>}" assert latex(Q.submodule([1, x**3/2], [2, y])) == \ r"\left< {{\left[ {1},{\frac{x^{3}}{2}} \right]} + {\left< {\left[ {x},{y} \right]},{\left[ {1},{x^{2}} \right]} \right>}},{{\left[ {2},{y} \right]} + {\left< {\left[ {x},{y} \right]},{\left[ {1},{x^{2}} \right]} \right>}} \right>" h = homomorphism(QQ.old_poly_ring(x).free_module(2), QQ.old_poly_ring(x).free_module(2), [0, 0]) assert latex(h) == r"{\left[\begin{matrix}0 & 0\\0 & 0\end{matrix}\right]} : {{\mathbb{Q}\left[x\right]}^{2}} \to {{\mathbb{Q}\left[x\right]}^{2}}" def test_QuotientRing(): from sympy.polys.domains import QQ R = QQ.old_poly_ring(x)/[x**2 + 1] assert latex( R) == r"\frac{\mathbb{Q}\left[x\right]}{\left< {x^{2} + 1} \right>}" assert latex(R.one) == r"{1} + {\left< {x^{2} + 1} \right>}" def test_Tr(): #TODO: Handle indices A, B = symbols('A B', commutative=False) t = Tr(A*B) assert latex(t) == r'\mbox{Tr}\left(A B\right)' def test_Adjoint(): from sympy.matrices import MatrixSymbol, Adjoint, Inverse, Transpose X = MatrixSymbol('X', 2, 2) Y = MatrixSymbol('Y', 2, 2) assert latex(Adjoint(X)) == r'X^\dag' assert latex(Adjoint(X + Y)) == r'\left(X + Y\right)^\dag' assert latex(Adjoint(X) + Adjoint(Y)) == r'X^\dag + Y^\dag' assert latex(Adjoint(X*Y)) == r'\left(X Y\right)^\dag' assert latex(Adjoint(Y)*Adjoint(X)) == r'Y^\dag X^\dag' assert latex(Adjoint(X**2)) == r'\left(X^{2}\right)^\dag' assert latex(Adjoint(X)**2) == r'\left(X^\dag\right)^{2}' assert latex(Adjoint(Inverse(X))) == r'\left(X^{-1}\right)^\dag' assert latex(Inverse(Adjoint(X))) == r'\left(X^\dag\right)^{-1}' assert latex(Adjoint(Transpose(X))) == r'\left(X^T\right)^\dag' assert latex(Transpose(Adjoint(X))) == r'\left(X^\dag\right)^T' def test_Hadamard(): from sympy.matrices import MatrixSymbol, HadamardProduct X = MatrixSymbol('X', 2, 2) Y = MatrixSymbol('Y', 2, 2) assert latex(HadamardProduct(X, Y*Y)) == r'X \circ \left(Y Y\right)' assert latex(HadamardProduct(X, Y)*Y) == r'\left(X \circ Y\right) Y' def test_boolean_args_order(): syms = symbols('a:f') expr = And(*syms) assert latex(expr) == 'a \\wedge b \\wedge c \\wedge d \\wedge e \\wedge f' expr = Or(*syms) assert latex(expr) == 'a \\vee b \\vee c \\vee d \\vee e \\vee f' def test_imaginary(): i = sqrt(-1) assert latex(i) == r'i' def test_builtins_without_args(): assert latex(sin) == r'\sin' assert latex(cos) == r'\cos' assert latex(tan) == r'\tan' assert latex(log) == r'\log' assert latex(Ei) == r'\operatorname{Ei}' assert latex(zeta) == r'\zeta' def test_latex_greek_functions(): # bug because capital greeks that have roman equivalents should not use # \Alpha, \Beta, \Eta, etc. s = Function('Alpha') assert latex(s) == r'A' assert latex(s(x)) == r'A{\left (x \right )}' s = Function('Beta') assert latex(s) == r'B' s = Function('Eta') assert latex(s) == r'H' assert latex(s(x)) == r'H{\left (x \right )}' # bug because sympy.core.numbers.Pi is special p = Function('Pi') # assert latex(p(x)) == r'\Pi{\left (x \right )}' assert latex(p) == r'\Pi' # bug because not all greeks are included c = Function('chi') assert latex(c(x)) == r'\chi{\left (x \right )}' assert latex(c) == r'\chi' def test_translate(): s = 'Alpha' assert translate(s) == 'A' s = 'Beta' assert translate(s) == 'B' s = 'Eta' assert translate(s) == 'H' s = 'omicron' assert translate(s) == 'o' s = 'Pi' assert translate(s) == r'\Pi' s = 'pi' assert translate(s) == r'\pi' s = 'LamdaHatDOT' assert translate(s) == r'\dot{\hat{\Lambda}}' def test_other_symbols(): from sympy.printing.latex import other_symbols for s in other_symbols: assert latex(symbols(s)) == "\\"+s def test_modifiers(): # Test each modifier individually in the simplest case (with funny capitalizations) assert latex(symbols("xMathring")) == r"\mathring{x}" assert latex(symbols("xCheck")) == r"\check{x}" assert latex(symbols("xBreve")) == r"\breve{x}" assert latex(symbols("xAcute")) == r"\acute{x}" assert latex(symbols("xGrave")) == r"\grave{x}" assert latex(symbols("xTilde")) == r"\tilde{x}" assert latex(symbols("xPrime")) == r"{x}'" assert latex(symbols("xddDDot")) == r"\ddddot{x}" assert latex(symbols("xDdDot")) == r"\dddot{x}" assert latex(symbols("xDDot")) == r"\ddot{x}" assert latex(symbols("xBold")) == r"\boldsymbol{x}" assert latex(symbols("xnOrM")) == r"\left\lVert{x}\right\rVert" assert latex(symbols("xAVG")) == r"\left\langle{x}\right\rangle" assert latex(symbols("xHat")) == r"\hat{x}" assert latex(symbols("xDot")) == r"\dot{x}" assert latex(symbols("xBar")) == r"\bar{x}" assert latex(symbols("xVec")) == r"\vec{x}" assert latex(symbols("xAbs")) == r"\left\lvert{x}\right\rvert" assert latex(symbols("xMag")) == r"\left\lvert{x}\right\rvert" assert latex(symbols("xPrM")) == r"{x}'" assert latex(symbols("xBM")) == r"\boldsymbol{x}" # Test strings that are *only* the names of modifiers assert latex(symbols("Mathring")) == r"Mathring" assert latex(symbols("Check")) == r"Check" assert latex(symbols("Breve")) == r"Breve" assert latex(symbols("Acute")) == r"Acute" assert latex(symbols("Grave")) == r"Grave" assert latex(symbols("Tilde")) == r"Tilde" assert latex(symbols("Prime")) == r"Prime" assert latex(symbols("DDot")) == r"\dot{D}" assert latex(symbols("Bold")) == r"Bold" assert latex(symbols("NORm")) == r"NORm" assert latex(symbols("AVG")) == r"AVG" assert latex(symbols("Hat")) == r"Hat" assert latex(symbols("Dot")) == r"Dot" assert latex(symbols("Bar")) == r"Bar" assert latex(symbols("Vec")) == r"Vec" assert latex(symbols("Abs")) == r"Abs" assert latex(symbols("Mag")) == r"Mag" assert latex(symbols("PrM")) == r"PrM" assert latex(symbols("BM")) == r"BM" assert latex(symbols("hbar")) == r"\hbar" # Check a few combinations assert latex(symbols("xvecdot")) == r"\dot{\vec{x}}" assert latex(symbols("xDotVec")) == r"\vec{\dot{x}}" assert latex(symbols("xHATNorm")) == r"\left\lVert{\hat{x}}\right\rVert" # Check a couple big, ugly combinations assert latex(symbols('xMathringBm_yCheckPRM__zbreveAbs')) == r"\boldsymbol{\mathring{x}}^{\left\lvert{\breve{z}}\right\rvert}_{{\check{y}}'}" assert latex(symbols('alphadothat_nVECDOT__tTildePrime')) == r"\hat{\dot{\alpha}}^{{\tilde{t}}'}_{\dot{\vec{n}}}" def test_greek_symbols(): assert latex(Symbol('alpha')) == r'\alpha' assert latex(Symbol('beta')) == r'\beta' assert latex(Symbol('gamma')) == r'\gamma' assert latex(Symbol('delta')) == r'\delta' assert latex(Symbol('epsilon')) == r'\epsilon' assert latex(Symbol('zeta')) == r'\zeta' assert latex(Symbol('eta')) == r'\eta' assert latex(Symbol('theta')) == r'\theta' assert latex(Symbol('iota')) == r'\iota' assert latex(Symbol('kappa')) == r'\kappa' assert latex(Symbol('lambda')) == r'\lambda' assert latex(Symbol('mu')) == r'\mu' assert latex(Symbol('nu')) == r'\nu' assert latex(Symbol('xi')) == r'\xi' assert latex(Symbol('omicron')) == r'o' assert latex(Symbol('pi')) == r'\pi' assert latex(Symbol('rho')) == r'\rho' assert latex(Symbol('sigma')) == r'\sigma' assert latex(Symbol('tau')) == r'\tau' assert latex(Symbol('upsilon')) == r'\upsilon' assert latex(Symbol('phi')) == r'\phi' assert latex(Symbol('chi')) == r'\chi' assert latex(Symbol('psi')) == r'\psi' assert latex(Symbol('omega')) == r'\omega' assert latex(Symbol('Alpha')) == r'A' assert latex(Symbol('Beta')) == r'B' assert latex(Symbol('Gamma')) == r'\Gamma' assert latex(Symbol('Delta')) == r'\Delta' assert latex(Symbol('Epsilon')) == r'E' assert latex(Symbol('Zeta')) == r'Z' assert latex(Symbol('Eta')) == r'H' assert latex(Symbol('Theta')) == r'\Theta' assert latex(Symbol('Iota')) == r'I' assert latex(Symbol('Kappa')) == r'K' assert latex(Symbol('Lambda')) == r'\Lambda' assert latex(Symbol('Mu')) == r'M' assert latex(Symbol('Nu')) == r'N' assert latex(Symbol('Xi')) == r'\Xi' assert latex(Symbol('Omicron')) == r'O' assert latex(Symbol('Pi')) == r'\Pi' assert latex(Symbol('Rho')) == r'P' assert latex(Symbol('Sigma')) == r'\Sigma' assert latex(Symbol('Tau')) == r'T' assert latex(Symbol('Upsilon')) == r'\Upsilon' assert latex(Symbol('Phi')) == r'\Phi' assert latex(Symbol('Chi')) == r'X' assert latex(Symbol('Psi')) == r'\Psi' assert latex(Symbol('Omega')) == r'\Omega' assert latex(Symbol('varepsilon')) == r'\varepsilon' assert latex(Symbol('varkappa')) == r'\varkappa' assert latex(Symbol('varphi')) == r'\varphi' assert latex(Symbol('varpi')) == r'\varpi' assert latex(Symbol('varrho')) == r'\varrho' assert latex(Symbol('varsigma')) == r'\varsigma' assert latex(Symbol('vartheta')) == r'\vartheta' @XFAIL def test_builtin_without_args_mismatched_names(): assert latex(CosineTransform) == r'\mathcal{COS}' def test_builtin_no_args(): assert latex(Chi) == r'\operatorname{Chi}' assert latex(gamma) == r'\Gamma' assert latex(KroneckerDelta) == r'\delta' assert latex(DiracDelta) == r'\delta' assert latex(lowergamma) == r'\gamma' def test_issue_3754(): p = Function('Pi') assert latex(p(x)) == r"\Pi{\left (x \right )}" def test_Mul(): e = Mul(-2, x + 1, evaluate=False) assert latex(e) == r'- 2 \left(x + 1\right)' e = Mul(2, x + 1, evaluate=False) assert latex(e) == r'2 \left(x + 1\right)' e = Mul(S.One/2, x + 1, evaluate=False) assert latex(e) == r'\frac{1}{2} \left(x + 1\right)' e = Mul(y, x + 1, evaluate=False) assert latex(e) == r'y \left(x + 1\right)' e = Mul(-y, x + 1, evaluate=False) assert latex(e) == r'- y \left(x + 1\right)' e = Mul(-2, x + 1) assert latex(e) == r'- 2 x - 2' e = Mul(2, x + 1) assert latex(e) == r'2 x + 2' def test_Pow(): e = Pow(2, 2, evaluate=False) assert latex(e) == r'2^{2}' sympy-0.7.4.1/sympy/printing/tests/test_repr.py0000644000175000017500000001117212253362407022004 0ustar georgeskgeorgeskfrom sympy.utilities.pytest import raises from sympy import symbols, Function, Integer, Matrix, Abs, \ Rational, Float, S, WildFunction, ImmutableMatrix, sin, true, false from sympy.core.compatibility import exec_ from sympy.geometry import Point, Ellipse from sympy.printing import srepr from sympy.polys import ring, field, ZZ, QQ, lex, grlex x, y = symbols('x,y') # eval(srepr(expr)) == expr has to succeed in the right environment. The right # environment is the scope of "from sympy import *" for most cases. ENV = {} exec_("from sympy import *", ENV) def sT(expr, string): """ sT := sreprTest Tests that srepr delivers the expected string and that the condition eval(srepr(expr))==expr holds. """ assert srepr(expr) == string assert eval(string, ENV) == expr def test_printmethod(): class R(Abs): def _sympyrepr(self, printer): return "foo(%s)" % printer._print(self.args[0]) assert srepr(R(x)) == "foo(Symbol('x'))" def test_Add(): sT(x + y, "Add(Symbol('x'), Symbol('y'))") assert srepr(x**2 + 1, order='lex') == "Add(Pow(Symbol('x'), Integer(2)), Integer(1))" assert srepr(x**2 + 1, order='old') == "Add(Integer(1), Pow(Symbol('x'), Integer(2)))" def test_Function(): sT(Function("f")(x), "Function('f')(Symbol('x'))") # test unapplied Function sT(Function('f'), "Function('f')") sT(sin(x), "sin(Symbol('x'))") sT(sin, "sin") def test_Geometry(): sT(Point(0, 0), "Point(Integer(0), Integer(0))") sT(Ellipse(Point(0, 0), 5, 1), "Ellipse(Point(Integer(0), Integer(0)), Integer(5), Integer(1))") # TODO more tests def test_Singletons(): sT(S.Catalan, 'Catalan') sT(S.ComplexInfinity, 'zoo') sT(S.EulerGamma, 'EulerGamma') sT(S.Exp1, 'E') sT(S.GoldenRatio, 'GoldenRatio') sT(S.Half, 'Rational(1, 2)') sT(S.ImaginaryUnit, 'I') sT(S.Infinity, 'oo') sT(S.NaN, 'nan') sT(S.NegativeInfinity, '-oo') sT(S.NegativeOne, 'Integer(-1)') sT(S.One, 'Integer(1)') sT(S.Pi, 'pi') sT(S.Zero, 'Integer(0)') def test_Integer(): sT(Integer(4), "Integer(4)") def test_list(): sT([x, Integer(4)], "[Symbol('x'), Integer(4)]") def test_Matrix(): for cls, name in [(Matrix, "MutableDenseMatrix"), (ImmutableMatrix, "ImmutableMatrix")]: sT(cls([[x**+1, 1], [y, x + y]]), "%s([[Symbol('x'), Integer(1)], [Symbol('y'), Add(Symbol('x'), Symbol('y'))]])" % name) sT(cls(), "%s([])" % name) sT(cls([[x**+1, 1], [y, x + y]]), "%s([[Symbol('x'), Integer(1)], [Symbol('y'), Add(Symbol('x'), Symbol('y'))]])" % name) def test_Rational(): sT(Rational(1, 3), "Rational(1, 3)") sT(Rational(-1, 3), "Rational(-1, 3)") def test_Float(): sT(Float('1.23', prec=3), "Float('1.22998', prec=3)") sT(Float('1.23456789', prec=9), "Float('1.23456788994', prec=9)") sT(Float('1.234567890123456789', prec=19), "Float('1.234567890123456789013', prec=19)") sT(Float( '0.60038617995049726', 15), "Float('0.60038617995049726', prec=15)") def test_Symbol(): sT(x, "Symbol('x')") sT(y, "Symbol('y')") def test_tuple(): sT((x,), "(Symbol('x'),)") sT((x, y), "(Symbol('x'), Symbol('y'))") def test_WildFunction(): sT(WildFunction('w'), "WildFunction('w')") def test_settins(): raises(TypeError, lambda: srepr(x, method="garbage")) def test_Mul(): sT(3*x**3*y, "Mul(Integer(3), Pow(Symbol('x'), Integer(3)), Symbol('y'))") assert srepr(3*x**3*y, order='old') == "Mul(Integer(3), Symbol('y'), Pow(Symbol('x'), Integer(3)))" def test_PolyRing(): assert srepr(ring("x", ZZ, lex)[0]) == "PolyRing((Symbol('x'),), ZZ, lex)" assert srepr(ring("x,y", QQ, grlex)[0]) == "PolyRing((Symbol('x'), Symbol('y')), QQ, grlex)" assert srepr(ring("x,y,z", ZZ["t"], lex)[0]) == "PolyRing((Symbol('x'), Symbol('y'), Symbol('z')), ZZ[t], lex)" def test_FracField(): assert srepr(field("x", ZZ, lex)[0]) == "FracField((Symbol('x'),), ZZ, lex)" assert srepr(field("x,y", QQ, grlex)[0]) == "FracField((Symbol('x'), Symbol('y')), QQ, grlex)" assert srepr(field("x,y,z", ZZ["t"], lex)[0]) == "FracField((Symbol('x'), Symbol('y'), Symbol('z')), ZZ[t], lex)" def test_PolyElement(): R, x, y = ring("x,y", ZZ) assert srepr(3*x**2*y + 1) == "PolyElement(PolyRing((Symbol('x'), Symbol('y')), ZZ, lex), [((2, 1), 3), ((0, 0), 1)])" def test_FracElement(): F, x, y = field("x,y", ZZ) assert srepr((3*x**2*y + 1)/(x - y**2)) == "FracElement(FracField((Symbol('x'), Symbol('y')), ZZ, lex), [((2, 1), 3), ((0, 0), 1)], [((1, 0), 1), ((0, 2), -1)])" def test_BooleanAtom(): assert srepr(true) == "S.true" assert srepr(false) == "S.false" sympy-0.7.4.1/sympy/printing/tests/test_precedence.py0000644000175000017500000000531512253362407023133 0ustar georgeskgeorgeskfrom sympy.concrete.products import Product from sympy.concrete.summations import Sum from sympy.core.function import Derivative from sympy.core.numbers import Integer, Rational, Float, oo from sympy.core.relational import Rel from sympy.core.symbol import symbols from sympy.functions import sin from sympy.integrals.integrals import Integral from sympy.series.order import Order from sympy.printing.precedence import precedence, PRECEDENCE x, y = symbols("x,y") def test_Add(): assert precedence(x + y) == PRECEDENCE["Add"] assert precedence(x*y + 1) == PRECEDENCE["Add"] def test_Function(): assert precedence(sin(x)) == PRECEDENCE["Atom"] assert precedence(Derivative(x, y)) == PRECEDENCE["Atom"] def test_Integral(): assert precedence(Integral(x, y)) == PRECEDENCE["Atom"] def test_Mul(): assert precedence(x*y) == PRECEDENCE["Mul"] assert precedence(-x*y) == PRECEDENCE["Add"] def test_Number(): assert precedence(Integer(0)) == PRECEDENCE["Atom"] assert precedence(Integer(1)) == PRECEDENCE["Atom"] assert precedence(Integer(-1)) == PRECEDENCE["Add"] assert precedence(Integer(10)) == PRECEDENCE["Atom"] assert precedence(Rational(5, 2)) == PRECEDENCE["Mul"] assert precedence(Rational(-5, 2)) == PRECEDENCE["Add"] assert precedence(Float(5)) == PRECEDENCE["Atom"] assert precedence(Float(-5)) == PRECEDENCE["Add"] assert precedence(oo) == PRECEDENCE["Atom"] assert precedence(-oo) == PRECEDENCE["Add"] def test_Order(): assert precedence(Order(x)) == PRECEDENCE["Atom"] def test_Pow(): assert precedence(x**y) == PRECEDENCE["Pow"] assert precedence(-x**y) == PRECEDENCE["Add"] assert precedence(x**-y) == PRECEDENCE["Pow"] def test_Product(): assert precedence(Product(x, (x, y, y + 1))) == PRECEDENCE["Atom"] def test_Relational(): assert precedence(Rel(x + y, y, "<")) == PRECEDENCE["Relational"] def test_Sum(): assert precedence(Sum(x, (x, y, y + 1))) == PRECEDENCE["Atom"] def test_Symbol(): assert precedence(x) == PRECEDENCE["Atom"] def test_And_Or(): # precendence relations between logical operators, ... assert precedence(x & y) > precedence(x | y) assert precedence(~y) > precedence(x & y) # ... and with other operators (cfr. other programming languages) assert precedence(x + y) > precedence(x | y) assert precedence(x + y) > precedence(x & y) assert precedence(x*y) > precedence(x | y) assert precedence(x*y) > precedence(x & y) assert precedence(~y) > precedence(x*y) assert precedence(~y) > precedence(x - y) # double checks assert precedence(x & y) == PRECEDENCE["And"] assert precedence(x | y) == PRECEDENCE["Or"] assert precedence(~y) == PRECEDENCE["Not"] sympy-0.7.4.1/sympy/printing/tests/__init__.py0000644000175000017500000000000012253362407021520 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/printing/tests/test_tableform.py0000644000175000017500000001302212253362407023003 0ustar georgeskgeorgesk from sympy import TableForm, S from sympy.printing.latex import latex from sympy.abc import x from sympy.functions.elementary.miscellaneous import sqrt from sympy.functions.elementary.trigonometric import sin from sympy.utilities.pytest import raises from textwrap import dedent def test_TableForm(): s = str(TableForm([["a", "b"], ["c", "d"], ["e", 0]], headings="automatic")) assert s == ( ' | 1 2\n' '-------\n' '1 | a b\n' '2 | c d\n' '3 | e ' ) s = str(TableForm([["a", "b"], ["c", "d"], ["e", 0]], headings="automatic", wipe_zeros=False)) assert s == dedent('''\ | 1 2 ------- 1 | a b 2 | c d 3 | e 0''') s = str(TableForm([[x**2, "b"], ["c", x**2], ["e", "f"]], headings=("automatic", None))) assert s == ( '1 | x**2 b \n' '2 | c x**2\n' '3 | e f ' ) s = str(TableForm([["a", "b"], ["c", "d"], ["e", "f"]], headings=(None, "automatic"))) assert s == dedent('''\ 1 2 --- a b c d e f''') s = str(TableForm([[5, 7], [4, 2], [10, 3]], headings=[["Group A", "Group B", "Group C"], ["y1", "y2"]])) assert s == ( ' | y1 y2\n' '---------------\n' 'Group A | 5 7 \n' 'Group B | 4 2 \n' 'Group C | 10 3 ' ) raises( ValueError, lambda: TableForm( [[5, 7], [4, 2], [10, 3]], headings=[["Group A", "Group B", "Group C"], ["y1", "y2"]], alignments="middle") ) s = str(TableForm([[5, 7], [4, 2], [10, 3]], headings=[["Group A", "Group B", "Group C"], ["y1", "y2"]], alignments="right")) assert s == dedent('''\ | y1 y2 --------------- Group A | 5 7 Group B | 4 2 Group C | 10 3''') # other alignment permutations d = [[1, 100], [100, 1]] s = TableForm(d, headings=(('xxx', 'x'), None), alignments='l') assert str(s) == ( 'xxx | 1 100\n' ' x | 100 1 ' ) s = TableForm(d, headings=(('xxx', 'x'), None), alignments='lr') assert str(s) == dedent('''\ xxx | 1 100 x | 100 1''') s = TableForm(d, headings=(('xxx', 'x'), None), alignments='clr') assert str(s) == dedent('''\ xxx | 1 100 x | 100 1''') s = TableForm(d, headings=(('xxx', 'x'), None)) assert str(s) == ( 'xxx | 1 100\n' ' x | 100 1 ' ) raises(ValueError, lambda: TableForm(d, alignments='clr')) #pad s = str(TableForm([[None, "-", 2], [1]], pad='?')) assert s == dedent('''\ ? - 2 1 ? ?''') def test_TableForm_latex(): s = latex(TableForm([[0, x**3], ["c", S(1)/4], [sqrt(x), sin(x**2)]], wipe_zeros=True, headings=("automatic", "automatic"))) assert s == ( '\\begin{tabular}{r l l}\n' ' & 1 & 2 \\\\\n' '\\hline\n' '1 & & $x^{3}$ \\\\\n' '2 & $c$ & $\\frac{1}{4}$ \\\\\n' '3 & $\\sqrt{x}$ & $\\sin{\\left (x^{2} \\right )}$ \\\\\n' '\\end{tabular}' ) s = latex(TableForm([[0, x**3], ["c", S(1)/4], [sqrt(x), sin(x**2)]], wipe_zeros=True, headings=("automatic", "automatic"), alignments='l')) assert s == ( '\\begin{tabular}{r l l}\n' ' & 1 & 2 \\\\\n' '\\hline\n' '1 & & $x^{3}$ \\\\\n' '2 & $c$ & $\\frac{1}{4}$ \\\\\n' '3 & $\\sqrt{x}$ & $\\sin{\\left (x^{2} \\right )}$ \\\\\n' '\\end{tabular}' ) s = latex(TableForm([[0, x**3], ["c", S(1)/4], [sqrt(x), sin(x**2)]], wipe_zeros=True, headings=("automatic", "automatic"), alignments='l'*3)) assert s == ( '\\begin{tabular}{l l l}\n' ' & 1 & 2 \\\\\n' '\\hline\n' '1 & & $x^{3}$ \\\\\n' '2 & $c$ & $\\frac{1}{4}$ \\\\\n' '3 & $\\sqrt{x}$ & $\\sin{\\left (x^{2} \\right )}$ \\\\\n' '\\end{tabular}' ) s = latex(TableForm([["a", x**3], ["c", S(1)/4], [sqrt(x), sin(x**2)]], headings=("automatic", "automatic"))) assert s == ( '\\begin{tabular}{r l l}\n' ' & 1 & 2 \\\\\n' '\\hline\n' '1 & $a$ & $x^{3}$ \\\\\n' '2 & $c$ & $\\frac{1}{4}$ \\\\\n' '3 & $\\sqrt{x}$ & $\\sin{\\left (x^{2} \\right )}$ \\\\\n' '\\end{tabular}' ) s = latex(TableForm([["a", x**3], ["c", S(1)/4], [sqrt(x), sin(x**2)]], formats=['(%s)', None], headings=("automatic", "automatic"))) assert s == ( '\\begin{tabular}{r l l}\n' ' & 1 & 2 \\\\\n' '\\hline\n' '1 & (a) & $x^{3}$ \\\\\n' '2 & (c) & $\\frac{1}{4}$ \\\\\n' '3 & (sqrt(x)) & $\\sin{\\left (x^{2} \\right )}$ \\\\\n' '\\end{tabular}' ) def neg_in_paren(x, i, j): if i % 2: return ('(%s)' if x < 0 else '%s') % x else: pass # use default print s = latex(TableForm([[-1, 2], [-3, 4]], formats=[neg_in_paren]*2, headings=("automatic", "automatic"))) assert s == ( '\\begin{tabular}{r l l}\n' ' & 1 & 2 \\\\\n' '\\hline\n' '1 & -1 & 2 \\\\\n' '2 & (-3) & 4 \\\\\n' '\\end{tabular}' ) s = latex(TableForm([["a", x**3], ["c", S(1)/4], [sqrt(x), sin(x**2)]])) assert s == ( '\\begin{tabular}{l l}\n' '$a$ & $x^{3}$ \\\\\n' '$c$ & $\\frac{1}{4}$ \\\\\n' '$\\sqrt{x}$ & $\\sin{\\left (x^{2} \\right )}$ \\\\\n' '\\end{tabular}' ) sympy-0.7.4.1/sympy/printing/gtk.py0000644000175000017500000000101012253362407017406 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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. Needs libgtkmathview-bin""" from sympy.utilities.mathml import c2p tmp = tempfile.mktemp() # create a temp file to store the result with open(tmp, 'wb') as file: file.write( c2p(mathml(x), simple=True) ) if start_viewer: os.system("mathmlviewer " + tmp) sympy-0.7.4.1/sympy/printing/preview.py0000644000175000017500000002706312253362407020322 0ustar georgeskgeorgeskfrom __future__ import print_function, division from os.path import join import tempfile import shutil from io import BytesIO try: from subprocess import STDOUT, CalledProcessError from sympy.core.compatibility import check_output except ImportError: pass from sympy.utilities.exceptions import SymPyDeprecationWarning from sympy.utilities.misc import find_executable from .latex import latex from sympy.utilities.decorator import doctest_depends_on @doctest_depends_on(exe=('latex', 'dvipng'), modules=('pyglet',), disable_viewers=('evince', 'gimp', 'superior-dvi-viewer')) def preview(expr, output='png', viewer=None, euler=True, packages=(), filename=None, outputbuffer=None, preamble=None, dvioptions=None, outputTexFile=None, **latex_settings): r""" 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 the available 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') This will choose 'pyglet' by default. To select a different one, do >>> preview(x + y, output='png', viewer='gimp') 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') 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') 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, if 'filename' is unset. However, if it was set, then 'preview' writes the genereted file to this filename instead. There is also support for writing to a BytesIO like object, which needs to be passed to the 'outputbuffer' argument. >>> from io import BytesIO >>> obj = BytesIO() >>> preview(x + y, output='png', viewer='BytesIO', ... outputbuffer=obj) The LaTeX preamble can be customized by setting the 'preamble' keyword argument. This can be used, e.g., to set a different font size, use a custom documentclass or import certain set of LaTeX packages. >>> preamble = "\\documentclass[10pt]{article}\n" \ ... "\\usepackage{amsmath,amsfonts}\\begin{document}" >>> preview(x + y, output='png', preamble=preamble) If the value of 'output' is different from 'dvi' then command line options can be set ('dvioptions' argument) for the execution of the 'dvi'+output conversion tool. These options have to be in the form of a list of strings (see subprocess.Popen). 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}'}) For post-processing the generated TeX File can be written to a file by passing the desired filename to the 'outputTexFile' keyword argument. To write the TeX code to a file named "sample.tex" and run the default png viewer to display the resulting bitmap, do >>> preview(x + y, outputTexFile="sample.tex") """ 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 :) # TODO add candidates for windows to list candidates = { "dvi": [ "evince", "okular", "kdvi", "xdvi" ], "ps": [ "evince", "okular", "gsview", "gv" ], "pdf": [ "evince", "okular", "kpdf", "acroread", "xpdf", "gv" ], } try: for candidate in candidates[output]: path = find_executable(candidate) if path is not None: viewer = path break else: raise SystemError( "No viewers found for '%s' output format." % output) except KeyError: raise SystemError("Invalid output format: %s" % output) else: if viewer == "file": if filename is None: SymPyDeprecationWarning(feature="Using viewer=\"file\" without a " "specified filename", deprecated_since_version="0.7.3", useinstead="viewer=\"file\" and filename=\"desiredname\"", issue=3919).warn() elif viewer == "StringIO": SymPyDeprecationWarning(feature="The preview() viewer StringIO", useinstead="BytesIO", deprecated_since_version="0.7.4", issue=3984).warn() viewer = "BytesIO" if outputbuffer is None: raise ValueError("outputbuffer has to be a BytesIO " "compatible object if viewer=\"StringIO\"") elif viewer == "BytesIO": if outputbuffer is None: raise ValueError("outputbuffer has to be a BytesIO " "compatible object if viewer=\"BytesIO\"") elif viewer not in special and not find_executable(viewer): raise SystemError("Unrecognized viewer: %s" % viewer) if preamble is None: actual_packages = packages + ("amsmath", "amsfonts") if euler: actual_packages += ("euler",) package_includes = "\n" + "\n".join(["\\usepackage{%s}" % p for p in actual_packages]) preamble = r"""\documentclass[12pt]{article} \pagestyle{empty} %s \begin{document} """ % (package_includes) else: if len(packages) > 0: raise ValueError("The \"packages\" keyword must not be set if a " "custom LaTeX preamble was specified") latex_main = preamble + '\n%s\n\n' + r"\end{document}" if isinstance(expr, str): latex_string = expr else: latex_string = latex(expr, mode='inline', **latex_settings) try: workdir = tempfile.mkdtemp() with open(join(workdir, 'texput.tex'), 'w') as fh: fh.write(latex_main % latex_string) if outputTexFile is not None: shutil.copyfile(join(workdir, 'texput.tex'), outputTexFile) if not find_executable('latex'): raise RuntimeError("latex program is not installed") try: check_output(['latex', '-halt-on-error', '-interaction=nonstopmode', 'texput.tex'], cwd=workdir, stderr=STDOUT) except CalledProcessError as e: raise RuntimeError( "'latex' exited abnormally with the following output:\n%s" % e.output) if output != "dvi": defaultoptions = { "ps": [], "pdf": [], "png": ["-T", "tight", "-z", "9", "--truecolor"] } commandend = { "ps": ["-o", "texput.ps", "texput.dvi"], "pdf": ["texput.dvi", "texput.pdf"], "png": ["-o", "texput.png", "texput.dvi"] } cmd = ["dvi" + output] if not find_executable(cmd[0]): raise RuntimeError("%s is not installed" % cmd[0]) try: if dvioptions is not None: cmd.extend(dvioptions) else: cmd.extend(defaultoptions[output]) cmd.extend(commandend[output]) except KeyError: raise SystemError("Invalid output format: %s" % output) try: check_output(cmd, cwd=workdir, stderr=STDOUT) except CalledProcessError as e: raise RuntimeError( "'%s' exited abnormally with the following output:\n%s" % (' '.join(cmd), e.output)) src = "texput.%s" % (output) if viewer == "file": if filename is None: buffer = BytesIO() with open(join(workdir, src), 'rb') as fh: buffer.write(fh.read()) return buffer else: shutil.move(join(workdir,src), filename) elif viewer == "BytesIO": with open(join(workdir, src), 'rb') as fh: outputbuffer.write(fh.read()) elif viewer == "pyglet": try: from pyglet import window, image, gl from pyglet.window import key except ImportError: raise ImportError("pyglet is required for preview.\n visit http://www.pyglet.org/") if output == "png": from pyglet.image.codecs.png import PNGImageDecoder img = image.load(join(workdir, 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: try: check_output([viewer, src], cwd=workdir, stderr=STDOUT) except CalledProcessError as e: raise RuntimeError( "'%s %s' exited abnormally with the following output:\n%s" % (viewer, src, e.output)) finally: try: shutil.rmtree(workdir) # delete directory except OSError as e: if e.errno != 2: # code 2 - no such file or directory raise sympy-0.7.4.1/sympy/printing/__init__.py0000644000175000017500000000106112253362407020366 0ustar georgeskgeorgesk"""Printing subsystem""" from .pretty import pager_print, pretty, pretty_print, pprint, \ pprint_use_unicode, pprint_try_use_unicode from .latex import latex, print_latex from .mathml import mathml, print_mathml from .python import python, print_python from .ccode import ccode, print_ccode from .fcode import fcode, print_fcode from .jscode import jscode, print_jscode from .gtk import print_gtk from .preview import preview from .repr import srepr from .tree import print_tree from .str import StrPrinter, sstr, sstrrepr from .tableform import TableForm sympy-0.7.4.1/sympy/physics/0000755000175000017500000000000012253362407016107 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/physics/hydrogen.py0000644000175000017500000001062112253362407020300 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import factorial, sqrt, exp, S, assoc_laguerre, Float 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) 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) 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 sympy-0.7.4.1/sympy/physics/wigner.py0000644000175000017500000005221412253362407017760 0ustar georgeskgeorgeskr""" 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) Credits and Copyright ~~~~~~~~~~~~~~~~~~~~~ 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 """ from __future__ import print_function, division 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] 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 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 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 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 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 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 sympy-0.7.4.1/sympy/physics/hep/0000755000175000017500000000000012253362407016663 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/physics/hep/gamma_matrices.py0000644000175000017500000007361112253362407022216 0ustar georgeskgeorgeskfrom sympy import S from sympy.tensor.tensor import TensorIndexType, TensorIndex,\ TensMul, TensorHead, tensorsymmetry, TensorType,\ TensAdd, tensor_mul, get_lines from sympy.core.containers import Tuple DiracSpinorIndex = TensorIndexType('DiracSpinorIndex', dim=4, dummy_fmt="S") class _LorentzContainer(object): """ Helper to collect LorentzIndex indices in various dimensions. It collects LorentzIndex TensorIndexType that have been implemented in the code, and stores them in a dict() """ lorentz_types = dict() def __new__(cls, dim=4, eps_dim=None, dummy_fmt="L"): if (dim, eps_dim) in _LorentzContainer.lorentz_types: return _LorentzContainer.lorentz_types[(dim, eps_dim)] new_L = TensorIndexType("LorentzIndex", dim=dim, eps_dim=eps_dim, dummy_fmt=dummy_fmt) _LorentzContainer.lorentz_types[(dim, eps_dim)] = new_L return new_L class GammaMatrixHead(TensorHead): r""" Class to wrap a ``TensorHead`` for gamma matrices. ``dim`` dimension of the gamma matrix. ``eps_dim`` correction for dimensional regularization, use None if not needed. Examples ======== >>> from sympy.physics.hep.gamma_matrices import GammaMatrixHead >>> from sympy.tensor.tensor import tensor_indices >>> G = GammaMatrixHead() >>> i = tensor_indices('i', G.LorentzIndex) >>> G(i) gamma(i, auto_left, -auto_right) Note that there is already an instance of GammaMatrixHead in four dimensions: GammaMatrix, which is simply declare as ``GammaMatrix = GammaMatrixHead()`` >>> from sympy.physics.hep.gamma_matrices import GammaMatrix >>> from sympy.tensor.tensor import tensor_indices >>> i = tensor_indices('i', GammaMatrix.LorentzIndex) >>> GammaMatrix(i) gamma(i, auto_left, -auto_right) To access the metric tensor >>> GammaMatrix.LorentzIndex.metric metric(LorentzIndex,LorentzIndex) """ _gmhd = dict() def __new__(cls, dim=4, eps_dim=4): key = (dim, eps_dim) if key in GammaMatrixHead._gmhd: return GammaMatrixHead._gmhd[key] lorentz = _LorentzContainer(*key) gmh = TensorHead.__new__(cls, "gamma", TensorType(Tuple(lorentz, DiracSpinorIndex, DiracSpinorIndex), tensorsymmetry([1], [1], [1])), comm=2, matrix_behavior=True) GammaMatrixHead._gmhd[key] = gmh gmh.LorentzIndex = lorentz return gmh @staticmethod def extract_type_tens(expression): """ Extract from a ``TensExpr`` all elements of this type. Returns two tensor expressions: * the first contains all ``TensorHead`` of this type. * the second contains all remaining. """ sp = expression.split() # Collect all gamma matrices of the same dimension new_expr = S.One residual_expr = S.One for i in sp: if isinstance(i.args[1][0], GammaMatrixHead): new_expr *= i else: residual_expr *= i return new_expr, residual_expr @staticmethod def simplify_this_type(expression): extracted_expr, residual_expr = GammaMatrixHead.extract_type_tens(expression) res_expr = GammaMatrixHead._simplify_single_line(extracted_expr) return res_expr * residual_expr @staticmethod def simplify_gpgp(ex, sort=True): """ simplify products ``G(i)*p(-i)*G(j)*p(-j) -> p(i)*p(-i)`` Examples ======== >>> from sympy.physics.hep.gamma_matrices import GammaMatrix as G >>> from sympy.tensor.tensor import tensor_indices, tensorhead >>> p, q = tensorhead('p, q', [G.LorentzIndex], [[1]]) >>> i0,i1,i2,i3,i4,i5 = tensor_indices('i0:6', G.LorentzIndex) >>> ps = p(i0)*G(-i0) >>> qs = q(i0)*G(-i0) >>> G.simplify_gpgp(ps*qs*qs) gamma(-L_0, auto_left, -auto_right)*p(L_0)*q(L_1)*q(-L_1) """ def _simplify_gpgp(ex): tids = ex._tids components = tids.components a = [] for i in range(len(components)): if not isinstance(components[i], GammaMatrixHead): continue dum = tids.dum for dx in dum: if dx[2] == i: p_pos1 = dx[3] elif dx[3] == i: p_pos1 = dx[2] else: continue comp1 = components[p_pos1] if comp1.comm == 0 and comp1.rank == 1: a.append((i, p_pos1)) if not a: return ex elim = set() tv = [] hit = True coeff = S.One ta = None while hit: hit = False for i, ai in enumerate(a[:-1]): if ai[0] in elim: continue if ai[0] != a[i + 1][0] - 1: continue if components[ai[1]] != components[a[i + 1][1]]: continue elim.add(ai[0]) elim.add(ai[1]) elim.add(a[i + 1][0]) elim.add(a[i + 1][1]) if not ta: ta = ex.split() mu = TensorIndex('mu', GammaMatrix.LorentzIndex) ind1 = ta[ai[0]].args[-1][1] ind2 = ta[ai[0] + 1].args[-1][2] hit = True if i == 0: coeff = ex.coeff tx = components[ai[1]](mu)*components[ai[1]](-mu) tv.append(tx*DiracSpinorIndex.delta(ind1, ind2)) break if tv: a = [x for j, x in enumerate(ta) if j not in elim] a.extend(tv) t = tensor_mul(*a)*coeff t = t.contract_metric(DiracSpinorIndex.delta) return t else: return ex if sort: ex = ex.sorted_components() while 1: t = _simplify_gpgp(ex) if t != ex: ex = t else: return t @staticmethod def simplify_lines(ex): """ simplify a product of gamma matrices Examples ======== >>> from sympy.physics.hep.gamma_matrices import GammaMatrix, DiracSpinorIndex >>> from sympy.tensor.tensor import tensor_indices >>> i0,i1,i2,i3,i4,i5 = tensor_indices('i0:6', GammaMatrix.LorentzIndex) >>> s0,s1,s2,s3,s4,s5,s6,s7 = tensor_indices('s0:8', DiracSpinorIndex) >>> G = GammaMatrix >>> t = G(i1,s1,-s2)*G(i4,s7,-s6)*G(i2,s2,-s3)*G(i3,s4,-s5)*G(i5,s6,-s7) >>> G.simplify_lines(t) 4*gamma(i3, s4, -s5)*gamma(i1, s1, -S_0)*gamma(i2, S_0, -s3)*metric(i4, i5) """ lines, traces, rest = get_lines(ex, DiracSpinorIndex) a = ex.split() trest = tensor_mul(*[x for i, x in enumerate(a) if i in rest]) tlines = [] for line in lines: first = a[line[0]] last = a[line[-1]] first = [x[0] for x in first.free if x[1] == 1][0] last = [x[0] for x in last.free if x[1] == 2][0] tx = tensor_mul(*[x for i, x in enumerate(a) if i in line]) tx1 = GammaMatrixHead._simplify_single_line(tx) tlines.append(tx1) traces = [GammaMatrix._trace_single_line(tensor_mul(*[x for i, x in enumerate(a) if i in line])) for line in traces] res = tensor_mul(*([trest] + tlines + traces)) return res def gamma_trace(self, t): """ trace of a single line of gamma matrices Examples ======== >>> from sympy.physics.hep.gamma_matrices import GammaMatrix as G >>> from sympy.tensor.tensor import tensor_indices, tensorhead >>> p, q = tensorhead('p, q', [G.LorentzIndex], [[1]]) >>> i0,i1,i2,i3,i4,i5 = tensor_indices('i0:6', G.LorentzIndex) >>> ps = p(i0)*G(-i0) >>> qs = q(i0)*G(-i0) >>> G.gamma_trace(G(i0)*G(i1)) 4*metric(i0, i1) >>> G.gamma_trace(ps*ps) - 4*p(i0)*p(-i0) 0 >>> G.gamma_trace(ps*qs + ps*ps) - 4*p(i0)*p(-i0) - 4*p(i0)*q(-i0) 0 """ #assert any(x == DiracSpinorIndex.auto_right for x, p, c, in t._tids.free) if isinstance(t, TensAdd): res = TensAdd(*[self._trace_single_line(x) for x in t.args]) return res t = self._simplify_single_line(t) res = self._trace_single_line(t) return res @staticmethod def _simplify_single_line(expression): """ Simplify single-line product of gamma matrices. Examples ======== >>> from sympy.physics.hep.gamma_matrices import GammaMatrix as G, DiracSpinorIndex as DS >>> from sympy.tensor.tensor import tensor_indices, tensorhead >>> p = tensorhead('p', [G.LorentzIndex], [[1]]) >>> i0,i1 = tensor_indices('i0:2', G.LorentzIndex) >>> G._simplify_single_line(G(i0)*G(i1)*p(-i1)*G(-i0)) + 2*G(i0)*p(-i0) 0 """ t1, t2 = GammaMatrixHead.extract_type_tens(expression) if t1 != 1: t1 = GammaMatrixHead._kahane_simplify(t1.coeff, t1._tids) res = t1*t2 return res def _trace_single_line(self, t): """ Evaluate the trace of a single gamma matrix line inside a ``TensExpr``. Notes ===== If there are ``DiracSpinorIndex.auto_left`` and ``DiracSpinorIndex.auto_right`` indices trace over them; otherwise traces are not implied (explain) Examples ======== >>> from sympy.physics.hep.gamma_matrices import GammaMatrix as G >>> from sympy.tensor.tensor import tensor_indices, tensorhead >>> p = tensorhead('p', [G.LorentzIndex], [[1]]) >>> i0,i1,i2,i3,i4,i5 = tensor_indices('i0:6', G.LorentzIndex) >>> G._trace_single_line(G(i0)*G(i1)) 4*metric(i0, i1) >>> G._trace_single_line(G(i0)*p(-i0)*G(i1)*p(-i1)) - 4*p(i0)*p(-i0) 0 """ def _trace_single_line1(t): t = t.sorted_components() components = t.components ncomps = len(components) g = self.LorentzIndex.metric sg = DiracSpinorIndex.delta # gamma matirices are in a[i:j] hit = 0 for i in range(ncomps): if isinstance(components[i], GammaMatrixHead): hit = 1 break for j in range(i + hit, ncomps): if not isinstance(components[j], GammaMatrixHead): break else: j = ncomps numG = j - i if numG == 0: spinor_free = [_[0] for _ in t._tids.free if _[0].tensortype is DiracSpinorIndex] tcoeff = t.coeff if spinor_free == [DiracSpinorIndex.auto_left, -DiracSpinorIndex.auto_right]: t = t*DiracSpinorIndex.delta(-DiracSpinorIndex.auto_left, DiracSpinorIndex.auto_right) t = t.contract_metric(sg) return t/tcoeff if tcoeff else t else: return t/tcoeff if tcoeff else t if numG % 2 == 1: return TensMul.from_data(S.Zero, [], [], []) elif numG > 4: t = t.substitute_indices((-DiracSpinorIndex.auto_right, -DiracSpinorIndex.auto_index), (DiracSpinorIndex.auto_left, DiracSpinorIndex.auto_index)) a = t.split() ind1, lind1, rind1 = a[i].args[-1] ind2, lind2, rind2 = a[i + 1].args[-1] aa = a[:i] + a[i + 2:] t1 = tensor_mul(*aa)*g(ind1, ind2)*sg(lind1, rind1)*sg(lind2, rind2) t1 = t1.contract_metric(g) t1 = t1.contract_metric(sg) args = [t1] sign = 1 for k in range(i + 2, j): sign = -sign ind2, lind2, rind2 = a[k].args[-1] aa = a[:i] + a[i + 1:k] + a[k + 1:] t2 = sign*tensor_mul(*aa)*g(ind1, ind2)*sg(lind1, rind1)*sg(lind2, rind2) t2 = t2.contract_metric(g) t2 = t2.contract_metric(sg) t2 = GammaMatrixHead.simplify_gpgp(t2, False) args.append(t2) t3 = TensAdd(*args) #aa = _tensorlist_contract_metric(aa, g(ind1, ind2)) #t3 = t3.canon_bp() t3 = self._trace_single_line(t3) return t3 else: a = t.split() if len(t.components) == 1: if t.components[0] is DiracSpinorIndex.delta: return 4 # FIXME only for D=4 t1 = self._gamma_trace1(*a[i:j]) a2 = a[:i] + a[j:] t2 = tensor_mul(*a2) t3 = t1*t2 if not t3: return t3 t3 = t3.contract_metric(g) return t3 if isinstance(t, TensAdd): a = [x.coeff*_trace_single_line1(x) for x in t.args] return TensAdd(*a) elif isinstance(t, TensMul): r = t.coeff*_trace_single_line1(t) return r else: return t def _gamma_trace1(self, *a): gctr = 4 # FIXME specific for d=4 g = self.LorentzIndex.metric if not a: return gctr n = len(a) if n%2 == 1: #return TensMul.from_data(S.Zero, [], [], []) return S.Zero if n == 2: ind0 = a[0].args[-1][0] ind1 = a[1].args[-1][0] return gctr*g(ind0, ind1) if n == 4: ind0 = a[0].args[-1][0] ind1 = a[1].args[-1][0] ind2 = a[2].args[-1][0] ind3 = a[3].args[-1][0] return gctr*(g(ind0, ind1)*g(ind2, ind3) - \ g(ind0, ind2)*g(ind1, ind3) + g(ind0, ind3)*g(ind1, ind2)) @staticmethod def _kahane_simplify(coeff, tids): r""" This function cancels contracted elements in a product of four dimensional gamma matrices, resulting in an expression equal to the given one, without the contracted gamma matrices. Parameters ========== `coeff` the coefficient of the tensor expression. `tids` TIDS object representing the gamma matrix expression to simplify. Notes ===== If spinor indices are given, the matrices must be given in the order given in the product. Algorithm ========= The idea behind the algorithm is to use some well-known identities, i.e., for contractions enclosing an even number of `\gamma` matrices `\gamma^\mu \gamma_{a_1} \cdots \gamma_{a_{2N}} \gamma_\mu = 2 (\gamma_{a_{2N}} \gamma_{a_1} \cdots \gamma_{a_{2N-1}} + \gamma_{a_{2N-1}} \cdots \gamma_{a_1} \gamma_{a_{2N}} )` for an odd number of `\gamma` matrices `\gamma^\mu \gamma_{a_1} \cdots \gamma_{a_{2N+1}} \gamma_\mu = -2 \gamma_{a_{2N+1}} \gamma_{a_{2N}} \cdots \gamma_{a_{1}}` Instead of repeatedly applying these identities to cancel out all contracted indices, it is possible to recognize the links that would result from such an operation, the problem is thus reduced to a simple rearrangement of free gamma matrices. Examples ======== When using, always remember that the original expression coefficient has to be handled separately >>> from sympy.physics.hep.gamma_matrices import GammaMatrix as G, DiracSpinorIndex as DS >>> from sympy.tensor.tensor import tensor_indices, tensorhead, TensMul, TensAdd >>> i0, i1, i2 = tensor_indices('i0:3', G.LorentzIndex) >>> s0,s1,s2,s3,s4,s5 = tensor_indices('s0:6', DS) >>> ta = G(i0)*G(-i0) >>> G._kahane_simplify(ta.coeff, ta._tids) - 4*DS.delta(DS.auto_left, -DS.auto_right) 0 >>> tb = G(i0)*G(i1)*G(-i0) >>> G._kahane_simplify(tb.coeff, tb._tids) -2*gamma(i1, auto_left, -auto_right) >>> t = G(i0, s0, -s1)*G(-i0,s1,-s2) >>> G._kahane_simplify(t.coeff, t._tids) - 4*DS.delta(s0, -s2) 0 >>> t = G(i0, s0, -s1)*G(-i0,s1,-s0) >>> G._kahane_simplify(t.coeff, t._tids) 16 If there are no contractions, the same expression is returned >>> tc = 3*G(i0)*G(i1) >>> G._kahane_simplify(tc.coeff, tc._tids) 3*gamma(i0, auto_left, -S_0)*gamma(i1, S_0, -auto_right) References ========== [1] Algorithm for Reducing Contracted Products of gamma Matrices, Joseph Kahane, Journal of Mathematical Physics, Vol. 9, No. 10, October 1968. """ for c in tids.components: if not(isinstance(tids.components[0], GammaMatrixHead)): raise ValueError('use only gamma matrices') n = len(tids.components) for p0, p1, c0, c1 in tids.dum: if p0 == 0: continue dc = abs(c0 - c1) if dc not in (1, n - 1): raise ValueError('wrong gamma matrix ordering') free = [_ for _ in tids.free if _[1] == 0] spinor_free = [_ for _ in tids.free if _[1] != 0] if len(spinor_free) == 2: spinor_free.sort(key=lambda x: x[2]) assert spinor_free[0][1] == 1 and spinor_free[-1][1] == 2 assert spinor_free[0][2] == 0 elif spinor_free: raise ValueError('spinor indices do not match') dum = sorted([_ for _ in tids.dum if _[0] == 0 and _[1] == 0]) if len(dum) == 0: # or GammaMatrixHead: # no contractions in `expression`, just return it. return TensMul.from_TIDS(coeff, tids) # find the `first_dum_pos`, i.e. the position of the first contracted # gamma matrix, Kahane's algorithm as described in his paper requires the # gamma matrix expression to start with a contracted gamma matrix, this is # a workaround which ignores possible initial free indices, and re-adds # them later. dum_zip = list(zip(*dum))[2:] first_dum_pos = min(min(dum_zip[0]), min(dum_zip[1])) total_number = len(free) + len(dum)*2 number_of_contractions = len(dum) free_pos = [None]*total_number for i in free: free_pos[i[2]] = i[0] # `index_is_free` is a list of booleans, to identify index position # and whether that index is free or dummy. index_is_free = [False]*total_number for i, indx in enumerate(free): assert indx[1] == 0 index_is_free[indx[2]] = True # `links` is a dictionary containing the graph described in Kahane's paper, # to every key correspond one or two values, representing the linked indices. # All values in `links` are integers, negative numbers are used in the case # where it is necessary to insert gamma matrices between free indices, in # order to make Kahane's algorithm work (see paper). links = dict() for i in range(first_dum_pos, total_number): links[i] = [] # `cum_sign` is a step variable to mark the sign of every index, see paper. cum_sign = -1 # `cum_sign_list` keeps storage for all `cum_sign` (every index). cum_sign_list = [None]*total_number block_free_count = 0 # multiply `resulting_coeff` by the coefficient parameter, the rest # of the algorithm ignores a scalar coefficient. resulting_coeff = S.One * coeff # initialize a lisf of lists of indices. The outer list will contain all # additive tensor expressions, while the inner list will contain the # free indices (rearranged according to the algorithm). resulting_indices = [[]] # start to count the `connected_components`, which together with the number # of contractions, determines a -1 or +1 factor to be multiplied. connected_components = 1 # First loop: here we fill `cum_sign_list`, and draw the links # among consecutive indices (they are stored in `links`). Links among # non-consecutive indices will be drawn later. for i, is_free in enumerate(index_is_free): # if `expression` starts with free indices, they are ignored here; # they are later added as they are to the beginning of all # `resulting_indices` list of lists of indices. if i < first_dum_pos: continue if is_free: block_free_count += 1 # if previous index was free as well, draw an arch in `links`. if block_free_count > 1: links[i - 1].append(i) links[i].append(i - 1) else: # Change the sign of the index (`cum_sign`) if the number of free # indices preceding it is even. cum_sign *= 1 if (block_free_count % 2) else -1 if block_free_count == 0 and i != first_dum_pos: # check if there are two consecutive dummy indices: # in this case create virtual indices with negative position, # these "virtual" indices represent the insertion of two # gamma^0 matrices to separate consecutive dummy indices, as # Kahane's algorithm requires dummy indices to be separated by # free indices. The product of two gamma^0 matrices is unity, # so the new expression being examined is the same as the # original one. if cum_sign == -1: links[-1-i] = [-1-i+1] links[-1-i+1] = [-1-i] if (i - cum_sign) in links: if i != first_dum_pos: links[i].append(i - cum_sign) if block_free_count != 0: if i - cum_sign < len(index_is_free): if index_is_free[i - cum_sign]: links[i - cum_sign].append(i) block_free_count = 0 cum_sign_list[i] = cum_sign # The previous loop has only created links between consecutive free indices, # it is necessary to properly create links among dummy (contracted) indices, # according to the rules described in Kahane's paper. There is only one exception # to Kahane's rules: the negative indices, which handle the case of some # consecutive free indices (Kahane's paper just describes dummy indices # separated by free indices, hinting that free indices can be added without # altering the expression result). for i in dum: assert i[0] == 0 assert i[1] == 0 # get the positions of the two contracted indices: pos1 = i[2] pos2 = i[3] # create Kahane's upper links, i.e. the upper arcs between dummy # (i.e. contracted) indices: links[pos1].append(pos2) links[pos2].append(pos1) # create Kahane's lower links, this corresponds to the arcs below # the line described in the paper: # first we move `pos1` and `pos2` according to the sign of the indices: linkpos1 = pos1 + cum_sign_list[pos1] linkpos2 = pos2 + cum_sign_list[pos2] # otherwise, perform some checks before creating the lower arcs: # make sure we are not exceeding the total number of indices: if linkpos1 >= total_number: continue if linkpos2 >= total_number: continue # make sure we are not below the first dummy index in `expression`: if linkpos1 < first_dum_pos: continue if linkpos2 < first_dum_pos: continue # check if the previous loop created "virtual" indices between dummy # indices, in such a case relink `linkpos1` and `linkpos2`: if (-1-linkpos1) in links: linkpos1 = -1-linkpos1 if (-1-linkpos2) in links: linkpos2 = -1-linkpos2 # move only if not next to free index: if linkpos1 >= 0 and not index_is_free[linkpos1]: linkpos1 = pos1 if linkpos2 >=0 and not index_is_free[linkpos2]: linkpos2 = pos2 # create the lower arcs: if linkpos2 not in links[linkpos1]: links[linkpos1].append(linkpos2) if linkpos1 not in links[linkpos2]: links[linkpos2].append(linkpos1) # This loop starts from the `first_dum_pos` index (first dummy index) # walks through the graph deleting the visited indices from `links`, # it adds a gamma matrix for every free index in encounters, while it # completely ignores dummy indices and virtual indices. pointer = first_dum_pos previous_pointer = 0 while True: if pointer in links: next_ones = links.pop(pointer) else: break if previous_pointer in next_ones: next_ones.remove(previous_pointer) previous_pointer = pointer if next_ones: pointer = next_ones[0] else: break if pointer == previous_pointer: break if pointer >=0 and free_pos[pointer] is not None: for ri in resulting_indices: ri.append(free_pos[pointer]) # The following loop removes the remaining connected components in `links`. # If there are free indices inside a connected component, it gives a # contribution to the resulting expression given by the factor # `gamma_a gamma_b ... gamma_z + gamma_z ... gamma_b gamma_a`, in Kahanes's # paper represented as {gamma_a, gamma_b, ... , gamma_z}, # virtual indices are ignored. The variable `connected_components` is # increased by one for every connected component this loop encounters. # If the connected component has virtual and dummy indices only # (no free indices), it contributes to `resulting_indices` by a factor of two. # The multiplication by two is a result of the # factor {gamma^0, gamma^0} = 2 I, as it appears in Kahane's paper. # Note: curly brackets are meant as in the paper, as a generalized # multi-element anticommutator! while links: connected_components += 1 pointer = min(links.keys()) previous_pointer = pointer # the inner loop erases the visited indices from `links`, and it adds # all free indices to `prepend_indices` list, virtual indices are # ignored. prepend_indices = [] while True: if pointer in links: next_ones = links.pop(pointer) else: break if previous_pointer in next_ones: if len(next_ones) > 1: next_ones.remove(previous_pointer) previous_pointer = pointer if next_ones: pointer = next_ones[0] if pointer >= first_dum_pos and free_pos[pointer] is not None: prepend_indices.insert(0, free_pos[pointer]) # if `prepend_indices` is void, it means there are no free indices # in the loop (and it can be shown that there must be a virtual index), # loops of virtual indices only contribute by a factor of two: if len(prepend_indices) == 0: resulting_coeff *= 2 # otherwise, add the free indices in `prepend_indices` to # the `resulting_indices`: else: expr1 = prepend_indices expr2 = list(reversed(prepend_indices)) resulting_indices = [expri + ri for ri in resulting_indices for expri in (expr1, expr2)] # sign correction, as described in Kahane's paper: resulting_coeff *= -1 if (number_of_contractions - connected_components + 1) % 2 else 1 # power of two factor, as described in Kahane's paper: resulting_coeff *= 2**(number_of_contractions) # If `first_dum_pos` is not zero, it means that there are trailing free gamma # matrices in front of `expression`, so multiply by them: for i in range(0, first_dum_pos): [ri.insert(0, free_pos[i]) for ri in resulting_indices] resulting_expr = S.Zero for i in resulting_indices: temp_expr = S.One for j in i: temp_expr *= GammaMatrix(j) resulting_expr += temp_expr t = resulting_coeff * resulting_expr t1 = None if isinstance(t, TensAdd): t1 = t.args[0] elif isinstance(t, TensMul): t1 = t if t1: spinor_free1 = [_ for _ in t1._tids.free if _[1] != 0] if spinor_free1: if spinor_free: t = t.substitute_indices((DiracSpinorIndex.auto_left, spinor_free[0][0]), (-DiracSpinorIndex.auto_right, spinor_free[-1][0])) else: # FIXME trace t = t*DiracSpinorIndex.delta(DiracSpinorIndex.auto_right, -DiracSpinorIndex.auto_left) t = GammaMatrix.simplify_lines(t) else: if spinor_free: t = t*DiracSpinorIndex.delta(spinor_free[0][0], spinor_free[-1][0]) else: t = t*4 else: if spinor_free: t = t*DiracSpinorIndex.delta(spinor_free[0][0], spinor_free[-1][0]) else: t = t*4 return t GammaMatrix = GammaMatrixHead() sympy-0.7.4.1/sympy/physics/hep/tests/0000755000175000017500000000000012253362407020025 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/physics/hep/tests/test_gamma_matrices.py0000644000175000017500000004221312253362407024411 0ustar georgeskgeorgeskfrom sympy.tensor.tensor import tensor_indices, TensorIndexType, tensorhead, TensorManager, TensMul, TensAdd, get_lines from sympy import simplify, trace from sympy.physics.hep.gamma_matrices import GammaMatrix as G, GammaMatrixHead, DiracSpinorIndex from sympy.utilities.pytest import XFAIL, raises def execute_gamma_simplify_tests_for_function(tfunc, D): """ Perform tests to check if sfunc is able to simplify gamma matrix expressions. Parameters ========== `sfunc` a function to simplify a `TIDS`, shall return the simplified `TIDS`. `D` the number of dimension (in most cases `D=4`). """ mu, nu, rho, sigma = tensor_indices("mu, nu, rho, sigma", G.LorentzIndex) a1, a2, a3, a4, a5, a6 = tensor_indices("a1:7", G.LorentzIndex) mu11, mu12, mu21, mu31, mu32, mu41, mu51, mu52 = tensor_indices("mu11, mu12, mu21, mu31, mu32, mu41, mu51, mu52", G.LorentzIndex) mu61, mu71, mu72 = tensor_indices("mu61, mu71, mu72", G.LorentzIndex) m0, m1, m2, m3, m4, m5, m6 = tensor_indices("m0:7", G.LorentzIndex) def g(xx, yy): return (G(xx)*G(yy) + G(yy)*G(xx))/2 # Some examples taken from Kahane's paper, 4 dim only: if D == 4: t = (G(a1)*G(mu11)*G(a2)*G(mu21)*G(-a1)*G(mu31)*G(-a2)) assert tfunc(t) == -4*G(mu11)*G(mu31)*G(mu21) - 4*G(mu31)*G(mu11)*G(mu21) t = (G(a1)*G(mu11)*G(mu12)*\ G(a2)*G(mu21)*\ G(a3)*G(mu31)*G(mu32)*\ G(a4)*G(mu41)*\ G(-a2)*G(mu51)*G(mu52)*\ G(-a1)*G(mu61)*\ G(-a3)*G(mu71)*G(mu72)*\ G(-a4)) assert tfunc(t) == \ 16*G(mu31)*G(mu32)*G(mu72)*G(mu71)*G(mu11)*G(mu52)*G(mu51)*G(mu12)*G(mu61)*G(mu21)*G(mu41) + 16*G(mu31)*G(mu32)*G(mu72)*G(mu71)*G(mu12)*G(mu51)*G(mu52)*G(mu11)*G(mu61)*G(mu21)*G(mu41) + 16*G(mu71)*G(mu72)*G(mu32)*G(mu31)*G(mu11)*G(mu52)*G(mu51)*G(mu12)*G(mu61)*G(mu21)*G(mu41) + 16*G(mu71)*G(mu72)*G(mu32)*G(mu31)*G(mu12)*G(mu51)*G(mu52)*G(mu11)*G(mu61)*G(mu21)*G(mu41) # Fully G.Lorentz-contracted expressions, these return scalars: def add_delta(ne): return ne * DiracSpinorIndex.delta(DiracSpinorIndex.auto_left, -DiracSpinorIndex.auto_right) t = (G(mu)*G(-mu)) ts = add_delta(D) assert tfunc(t) == ts t = (G(mu)*G(nu)*G(-mu)*G(-nu)) ts = add_delta(2*D - D**2) # -8 assert tfunc(t) == ts t = (G(mu)*G(nu)*G(-nu)*G(-mu)) ts = add_delta(D**2) # 16 assert tfunc(t) == ts t = (G(mu)*G(nu)*G(-rho)*G(-nu)*G(-mu)*G(rho)) ts = add_delta(4*D - 4*D**2 + D**3) # 16 assert tfunc(t) == ts t = (G(mu)*G(nu)*G(rho)*G(-rho)*G(-nu)*G(-mu)) ts = add_delta(D**3) # 64 assert tfunc(t) == ts t = (G(a1)*G(a2)*G(a3)*G(a4)*G(-a3)*G(-a1)*G(-a2)*G(-a4)) ts = add_delta(-8*D + 16*D**2 - 8*D**3 + D**4) # -32 assert tfunc(t) == ts t = (G(-mu)*G(-nu)*G(-rho)*G(-sigma)*G(nu)*G(mu)*G(sigma)*G(rho)) ts = add_delta(-16*D + 24*D**2 - 8*D**3 + D**4) # 64 assert tfunc(t) == ts t = (G(-mu)*G(nu)*G(-rho)*G(sigma)*G(rho)*G(-nu)*G(mu)*G(-sigma)) ts = add_delta(8*D - 12*D**2 + 6*D**3 - D**4) # -32 assert tfunc(t) == ts t = (G(a1)*G(a2)*G(a3)*G(a4)*G(a5)*G(-a3)*G(-a2)*G(-a1)*G(-a5)*G(-a4)) ts = add_delta(64*D - 112*D**2 + 60*D**3 - 12*D**4 + D**5) # 256 assert tfunc(t) == ts t = (G(a1)*G(a2)*G(a3)*G(a4)*G(a5)*G(-a3)*G(-a1)*G(-a2)*G(-a4)*G(-a5)) ts = add_delta(64*D - 120*D**2 + 72*D**3 - 16*D**4 + D**5) # -128 assert tfunc(t) == ts t = (G(a1)*G(a2)*G(a3)*G(a4)*G(a5)*G(a6)*G(-a3)*G(-a2)*G(-a1)*G(-a6)*G(-a5)*G(-a4)) ts = add_delta(416*D - 816*D**2 + 528*D**3 - 144*D**4 + 18*D**5 - D**6) # -128 assert tfunc(t) == ts t = (G(a1)*G(a2)*G(a3)*G(a4)*G(a5)*G(a6)*G(-a2)*G(-a3)*G(-a1)*G(-a6)*G(-a4)*G(-a5)) ts = add_delta(416*D - 848*D**2 + 584*D**3 - 172*D**4 + 22*D**5 - D**6) # -128 assert tfunc(t) == ts # Expressions with free indices: t = (G(mu)*G(nu)*G(rho)*G(sigma)*G(-mu)) assert tfunc(t) == (-2*G(sigma)*G(rho)*G(nu) + (4-D)*G(nu)*G(rho)*G(sigma)) t = (G(mu)*G(nu)*G(-mu)) assert tfunc (t) == (2-D)*G(nu) t = (G(mu)*G(nu)*G(rho)*G(-mu)) assert tfunc(t) == 2*G(nu)*G(rho) + 2*G(rho)*G(nu) - (4-D)*G(nu)*G(rho) t = 2*G(m2)*G(m0)*G(m1)*G(-m0)*G(-m1) st = tfunc(t) assert st == (D*(-2*D + 4))*G(m2) t = G(m2)*G(m0)*G(m1)*G(-m0)*G(-m2) st = tfunc(t) assert st == ((-D + 2)**2)*G(m1) t = G(m0)*G(m1)*G(m2)*G(m3)*G(-m1) st = tfunc(t) assert st == (D - 4)*G(m0)*G(m2)*G(m3) + 4*G(m0)*g(m2, m3) t = G(m0)*G(m1)*G(m2)*G(m3)*G(-m1)*G(-m0) st = tfunc(t) assert st == ((D - 4)**2)*G(m2)*G(m3) + (8*D - 16)*g(m2, m3) t = G(m2)*G(m0)*G(m1)*G(-m2)*G(-m0) st = tfunc(t) assert st == ((-D + 2)*(D - 4) + 4)*G(m1) t = G(m3)*G(m1)*G(m0)*G(m2)*G(-m3)*G(-m0)*G(-m2) st = tfunc(t) assert st == (-4*D + (-D + 2)**2*(D - 4) + 8)*G(m1) t = 2*G(m0)*G(m1)*G(m2)*G(m3)*G(-m0) st = tfunc(t) assert st == ((-2*D + 8)*G(m1)*G(m2)*G(m3) - 4*G(m3)*G(m2)*G(m1)) t = G(m5)*G(m0)*G(m1)*G(m4)*G(m2)*G(-m4)*G(m3)*G(-m0) st = tfunc(t) assert st == (((-D + 2)*(-D + 4))*G(m5)*G(m1)*G(m2)*G(m3) + (2*D - 4)*G(m5)*G(m3)*G(m2)*G(m1)) t = -G(m0)*G(m1)*G(m2)*G(m3)*G(-m0)*G(m4) st = tfunc(t) assert st == ((D - 4)*G(m1)*G(m2)*G(m3)*G(m4) + 2*G(m3)*G(m2)*G(m1)*G(m4)) t = G(-m5)*G(m0)*G(m1)*G(m2)*G(m3)*G(m4)*G(-m0)*G(m5) st = tfunc(t) result1 = ((-D + 4)**2 + 4)*G(m1)*G(m2)*G(m3)*G(m4) +\ (4*D - 16)*G(m3)*G(m2)*G(m1)*G(m4) + (4*D - 16)*G(m4)*G(m1)*G(m2)*G(m3)\ + 4*G(m2)*G(m1)*G(m4)*G(m3) + 4*G(m3)*G(m4)*G(m1)*G(m2) +\ 4*G(m4)*G(m3)*G(m2)*G(m1) # Kahane's algorithm yields this result, which is equivalent to `result1` # in four dimensions, but is not automatically recognized as equal: result2 = 8*G(m1)*G(m2)*G(m3)*G(m4) + 8*G(m4)*G(m3)*G(m2)*G(m1) if D == 4: assert st == (result1) or st == (result2) else: assert st == (result1) # and a few very simple cases, with no contracted indices: t = G(m0) st = tfunc(t) assert st == t t = -7*G(m0) st = tfunc(t) assert st == t t = 224*G(m0)*G(m1)*G(-m2)*G(m3) st = tfunc(t) assert st == t def test_kahane_algorithm(): # Wrap this function to convert to and from TIDS: def tfunc(e): return GammaMatrixHead._simplify_single_line(e) execute_gamma_simplify_tests_for_function(tfunc, D=4) def test_kahane_simplify1(): i0,i1,i2,i3,i4,i5,i6,i7,i8,i9,i10,i11,i12,i13,i14,i15 = tensor_indices('i0:16', G.LorentzIndex) mu, nu, rho, sigma = tensor_indices("mu, nu, rho, sigma", G.LorentzIndex) KD = DiracSpinorIndex.delta sl = DiracSpinorIndex.auto_left sr = DiracSpinorIndex.auto_right D = 4 s0,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13,s14,s15,s16 = \ tensor_indices('s0:17', DiracSpinorIndex) t = DiracSpinorIndex.delta(s0,s1) t = G(i0)*G(i1) r = G._kahane_simplify(t.coeff, t._tids) assert r.equals(t) t = G(i0)*G(i1)*G(-i0) r = G._kahane_simplify(t.coeff, t._tids) assert r.equals(-2*G(i1)) t = G(i0,s0,-s1)*G(i1,s1,-s2)*G(-i0,s2,-s3) r = G._kahane_simplify(t.coeff, t._tids) assert r.equals(-2*G(i1, s0, -s3)) t = G(i0, s0, -s1)*G(i1, s1, -s2) r = G._kahane_simplify(t.coeff, t._tids) assert r.equals(t) t = G(i0, s0, -s1)*G(i1, s1, -s0) r = G._kahane_simplify(t.coeff, t._tids) assert r.equals(t) t = G(i0)*G(-i0) r = G._kahane_simplify(t.coeff, t._tids) assert r.equals(4*KD(sl, -sr)) t = G(i0,s0,-s1)*G(-i0,s1,-s2) r = G._kahane_simplify(t.coeff, t._tids) assert r.equals(4*KD(s0, -s2)) t = G(i0,s0,-s1)*G(-i0,s1,-s0) r = G._kahane_simplify(t.coeff, t._tids) assert r.equals(16) t = G(i0)*G(i1)*G(-i0) r = G._kahane_simplify(t.coeff, t._tids) assert r.equals(-2*G(i1)) t = G(i0)*G(i1)*G(-i0)*G(-i1) r = G._kahane_simplify(t.coeff, t._tids) assert r.equals((2*D - D**2)*KD(sl, -sr)) t = G(i0,s0,-s1)*G(i1,s1,-s2)*G(-i0,s2,-s3)*G(-i1,s3,-s0) r = G._kahane_simplify(t.coeff, t._tids) assert r.equals(4*(2*D - D**2)) t = G(i0,s0,-s1)*G(-i0,s2,-s3)*G(i1,s1,-s2)*G(-i1,s3,-s0) raises(ValueError, lambda: G._kahane_simplify(t.coeff, t._tids)) t = (G(mu)*G(nu)*G(-nu)*G(-mu)) r = G._kahane_simplify(t.coeff, t._tids) assert r.equals(D**2*KD(sl, -sr)) t = (G(mu,s0,-s1)*G(nu,s1,-s2)*G(-nu,s2,-s3)*G(-mu,s3,-s4)) r = G._kahane_simplify(t.coeff, t._tids) assert r.equals(D**2*KD(s0, -s4)) t = (G(mu,s0,-s1)*G(nu,s1,-s2)*G(-nu,s2,-s3)*G(-mu,s3,-s0)) r = G._kahane_simplify(t.coeff, t._tids) assert r.equals(4*D**2) t = (G(mu)*G(nu)*G(-rho)*G(-nu)*G(-mu)*G(rho)) r = G._kahane_simplify(t.coeff, t._tids) assert r.equals((4*D - 4*D**2 + D**3)*KD(sl, -sr)) t = (G(-mu)*G(-nu)*G(-rho)*G(-sigma)*G(nu)*G(mu)*G(sigma)*G(rho)) r = G._kahane_simplify(t.coeff, t._tids) assert r.equals((-16*D + 24*D**2 - 8*D**3 + D**4)*KD(sl, -sr)) t = (G(-mu)*G(nu)*G(-rho)*G(sigma)*G(rho)*G(-nu)*G(mu)*G(-sigma)) r = G._kahane_simplify(t.coeff, t._tids) assert r.equals((8*D - 12*D**2 + 6*D**3 - D**4)*KD(sl, -sr)) # Expressions with free indices: t = (G(mu)*G(nu)*G(rho)*G(sigma)*G(-mu)) r = G._kahane_simplify(t.coeff, t._tids) assert r.equals(-2*G(sigma)*G(rho)*G(nu)) t = (G(mu,s0,-s1)*G(nu,s1,-s2)*G(rho,s2,-s3)*G(sigma,s3,-s4)*G(-mu,s4,-s5)) r = G._kahane_simplify(t.coeff, t._tids) assert r.equals(-2*G(sigma,s0,-s1)*G(rho,s1,-s2)*G(nu,s2,-s5)) def test_gamma_matrix_class(): i, j, k = tensor_indices('i,j,k', G.LorentzIndex) # define another type of TensorHead to see if exprs are correctly handled: A = tensorhead('A', [G.LorentzIndex], [[1]]) t = A(k)*G(i)*G(-i) ts = simplify(t) assert ts == 4*A(k)*DiracSpinorIndex.delta(DiracSpinorIndex.auto_left, -DiracSpinorIndex.auto_right) t = G(i)*A(k)*G(j) ts = simplify(t) assert ts == A(k)*G(i)*G(j) execute_gamma_simplify_tests_for_function(simplify, D=4) def test_gamma_matrix_trace(): gamma_trace = G.gamma_trace g = G.LorentzIndex.metric m0, m1, m2, m3, m4, m5, m6 = tensor_indices('m0:7', G.LorentzIndex) n0, n1, n2, n3, n4, n5 = tensor_indices('n0:6', G.LorentzIndex) # working in D=4 dimensions D = 4 # traces of odd number of gamma matrices are zero: t = G(m0) t1 = gamma_trace(t) assert t1.equals(0) t = G(m0)*G(m1)*G(m2) t1 = gamma_trace(t) assert t1.equals(0) t = G(m0)*G(m1)*G(-m0) t1 = gamma_trace(t) assert t1.equals(0) t = G(m0)*G(m1)*G(m2)*G(m3)*G(m4) t1 = gamma_trace(t) assert t1.equals(0) # traces without internal contractions: t = G(m0)*G(m1) t1 = gamma_trace(t) assert t1 == 4*g(m0, m1) t = G(m0)*G(m1)*G(m2)*G(m3) t1 = gamma_trace(t) t2 = -4*g(m0, m2)*g(m1, m3) + 4*g(m0, m1)*g(m2, m3) + 4*g(m0, m3)*g(m1, m2) st2 = str(t2) assert t1 == t2 t = G(m0)*G(m1)*G(m2)*G(m3)*G(m4)*G(m5) t1 = gamma_trace(t) t2 = t1*g(-m0, -m5) t2 = t2.contract_metric(g) assert t2 == D*gamma_trace(G(m1)*G(m2)*G(m3)*G(m4)) # traces of expressions with internal contractions: t = G(m0)*G(-m0) t1 = gamma_trace(t) assert t1.equals(4*D) t = G(m0)*G(m1)*G(-m0)*G(-m1) t1 = gamma_trace(t) assert t1.equals(8*D - 4*D**2) t = G(m0)*G(m1)*G(m2)*G(m3)*G(m4)*G(-m0) t1 = gamma_trace(t) t2 = (-4*D)*g(m1, m3)*g(m2, m4) + (4*D)*g(m1, m2)*g(m3, m4) + \ (4*D)*g(m1, m4)*g(m2, m3) assert t1.equals(t2) t = G(-m5)*G(m0)*G(m1)*G(m2)*G(m3)*G(m4)*G(-m0)*G(m5) t1 = gamma_trace(t) t2 = (32*D + 4*(-D + 4)**2 - 64)*(g(m1, m2)*g(m3, m4) - \ g(m1, m3)*g(m2, m4) + g(m1, m4)*g(m2, m3)) assert t1.equals(t2) t = G(m0)*G(m1)*G(-m0)*G(m3) t1 = gamma_trace(t) assert t1.equals((-4*D + 8)*g(m1, m3)) # p, q = S1('p,q') # ps = p(m0)*G(-m0) # qs = q(m0)*G(-m0) # t = ps*qs*ps*qs # t1 = gamma_trace(t) # assert t1 == 8*p(m0)*q(-m0)*p(m1)*q(-m1) - 4*p(m0)*p(-m0)*q(m1)*q(-m1) t = G(m0)*G(m1)*G(m2)*G(m3)*G(m4)*G(m5)*G(-m0)*G(-m1)*G(-m2)*G(-m3)*G(-m4)*G(-m5) t1 = gamma_trace(t) assert t1.equals(-4*D**6 + 120*D**5 - 1040*D**4 + 3360*D**3 - 4480*D**2 + 2048*D) t = G(m0)*G(m1)*G(n1)*G(m2)*G(n2)*G(m3)*G(m4)*G(-n2)*G(-n1)*G(-m0)*G(-m1)*G(-m2)*G(-m3)*G(-m4) t1 = gamma_trace(t) tresu = -7168*D + 16768*D**2 - 14400*D**3 + 5920*D**4 - 1232*D**5 + 120*D**6 - 4*D**7 assert t1.equals(tresu) # checked with Mathematica # In[1]:= <>> 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 = 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 = 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) 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 = 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) 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 = 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 = 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) 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 = 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 = 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) 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 = 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 = 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 = 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) sympy-0.7.4.1/sympy/physics/quantum/circuitutils.py0000644000175000017500000003266712253362407022714 0ustar georgeskgeorgesk"""Primitive circuit operations on quantum circuits.""" from __future__ import print_function, division from sympy import Symbol, Integer, Tuple, Mul, sympify, default_sort_key from sympy.utilities import numbered_symbols from sympy.core.compatibility import reduce from sympy.physics.quantum.gate import Gate __all__ = [ 'kmp_table', 'find_subcircuit', 'replace_subcircuit', 'convert_to_symbolic_indices', 'convert_to_real_indices', 'random_reduce', 'random_insert' ] def kmp_table(word): """Build the 'partial match' table of the Knuth-Morris-Pratt algorithm. Note: This is applicable to strings or quantum circuits represented as tuples. """ # Current position in subcircuit pos = 2 # Beginning position of candidate substring that # may reappear later in word cnd = 0 # The 'partial match' table that helps one determine # the next location to start substring search table = list() table.append(-1) table.append(0) while pos < len(word): if word[pos - 1] == word[cnd]: cnd = cnd + 1 table.append(cnd) pos = pos + 1 elif cnd > 0: cnd = table[cnd] else: table.append(0) pos = pos + 1 return table def find_subcircuit(circuit, subcircuit, start=0, end=0): """Finds the subcircuit in circuit, if it exists. If the subcircuit exists, the index of the start of the subcircuit in circuit is returned; otherwise, -1 is returned. The algorithm that is implemented is the Knuth-Morris-Pratt algorithm. Parameters ========== circuit : tuple, Gate or Mul A tuple of Gates or Mul representing a quantum circuit subcircuit : tuple, Gate or Mul A tuple of Gates or Mul to find in circuit start : int The location to start looking for subcircuit. If start is the same or past end, -1 is returned. end : int The last place to look for a subcircuit. If end is less than 1 (one), then the length of circuit is taken to be end. Examples ======== Find the first instance of a subcircuit: >>> from sympy.physics.quantum.circuitutils import find_subcircuit >>> from sympy.physics.quantum.gate import X, Y, Z, H >>> circuit = X(0)*Z(0)*Y(0)*H(0) >>> subcircuit = Z(0)*Y(0) >>> find_subcircuit(circuit, subcircuit) 1 Find the first instance starting at a specific position: >>> find_subcircuit(circuit, subcircuit, start=1) 1 >>> find_subcircuit(circuit, subcircuit, start=2) -1 >>> circuit = circuit*subcircuit >>> find_subcircuit(circuit, subcircuit, start=2) 4 Find the subcircuit within some interval: >>> find_subcircuit(circuit, subcircuit, start=2, end=2) -1 """ if isinstance(circuit, Mul): circuit = circuit.args if isinstance(subcircuit, Mul): subcircuit = subcircuit.args if len(subcircuit) == 0 or len(subcircuit) > len(circuit): return -1 if end < 1: end = len(circuit) # Location in circuit pos = start # Location in the subcircuit index = 0 # 'Partial match' table table = kmp_table(subcircuit) while (pos + index) < end: if subcircuit[index] == circuit[pos + index]: index = index + 1 else: pos = pos + index - table[index] index = table[index] if table[index] > -1 else 0 if index == len(subcircuit): return pos return -1 def replace_subcircuit(circuit, subcircuit, replace=None, pos=0): """Replaces a subcircuit with another subcircuit in circuit, if it exists. If multiple instances of subcircuit exists, the first instance is replaced. The position to being searching from (if different from 0) may be optionally given. If subcircuit can't be found, circuit is returned. Parameters ========== circuit : tuple, Gate or Mul A quantum circuit subcircuit : tuple, Gate or Mul The circuit to be replaced replace : tuple, Gate or Mul The replacement circuit pos : int The location to start search and replace subcircuit, if it exists. This may be used if it is known beforehand that multiple instances exist, and it is desirable to replace a specific instance. If a negative number is given, pos will be defaulted to 0. Examples ======== Find and remove the subcircuit: >>> from sympy.physics.quantum.circuitutils import replace_subcircuit >>> from sympy.physics.quantum.gate import X, Y, Z, H >>> circuit = X(0)*Z(0)*Y(0)*H(0)*X(0)*H(0)*Y(0) >>> subcircuit = Z(0)*Y(0) >>> replace_subcircuit(circuit, subcircuit) (X(0), H(0), X(0), H(0), Y(0)) Remove the subcircuit given a starting search point: >>> replace_subcircuit(circuit, subcircuit, pos=1) (X(0), H(0), X(0), H(0), Y(0)) >>> replace_subcircuit(circuit, subcircuit, pos=2) (X(0), Z(0), Y(0), H(0), X(0), H(0), Y(0)) Replace the subcircuit: >>> replacement = H(0)*Z(0) >>> replace_subcircuit(circuit, subcircuit, replace=replacement) (X(0), H(0), Z(0), H(0), X(0), H(0), Y(0)) """ if pos < 0: pos = 0 if isinstance(circuit, Mul): circuit = circuit.args if isinstance(subcircuit, Mul): subcircuit = subcircuit.args if isinstance(replace, Mul): replace = replace.args elif replace is None: replace = () # Look for the subcircuit starting at pos loc = find_subcircuit(circuit, subcircuit, start=pos) # If subcircuit was found if loc > -1: # Get the gates to the left of subcircuit left = circuit[0:loc] # Get the gates to the right of subcircuit right = circuit[loc + len(subcircuit):len(circuit)] # Recombine the left and right side gates into a circuit circuit = left + replace + right return circuit def _sympify_qubit_map(mapping): new_map = {} for key in mapping: new_map[key] = sympify(mapping[key]) return new_map def convert_to_symbolic_indices(seq, start=None, gen=None, qubit_map=None): """Returns the circuit with symbolic indices and the dictionary mapping symbolic indices to real indices. The mapping is 1 to 1 and onto (bijective). Parameters ========== seq : tuple, Gate/Integer/tuple or Mul A tuple of Gate, Integer, or tuple objects, or a Mul start : Symbol An optional starting symbolic index gen : object An optional numbered symbol generator qubit_map : dict An existing mapping of symbolic indices to real indices All symbolic indices have the format 'i#', where # is some number >= 0. """ if isinstance(seq, Mul): seq = seq.args # A numbered symbol generator index_gen = numbered_symbols(prefix='i', start=-1) cur_ndx = next(index_gen) # keys are symbolic indices; values are real indices ndx_map = {} def create_inverse_map(symb_to_real_map): rev_items = lambda item: tuple([item[1], item[0]]) return dict(map(rev_items, symb_to_real_map.items())) if start is not None: if not isinstance(start, Symbol): msg = 'Expected Symbol for starting index, got %r.' % start raise TypeError(msg) cur_ndx = start if gen is not None: if not isinstance(gen, numbered_symbols().__class__): msg = 'Expected a generator, got %r.' % gen raise TypeError(msg) index_gen = gen if qubit_map is not None: if not isinstance(qubit_map, dict): msg = ('Expected dict for existing map, got ' + '%r.' % qubit_map) raise TypeError(msg) ndx_map = qubit_map ndx_map = _sympify_qubit_map(ndx_map) # keys are real indices; keys are symbolic indices inv_map = create_inverse_map(ndx_map) sym_seq = () for item in seq: # Nested items, so recurse if isinstance(item, Gate): result = convert_to_symbolic_indices(item.args, qubit_map=ndx_map, start=cur_ndx, gen=index_gen) sym_item, new_map, cur_ndx, index_gen = result ndx_map.update(new_map) inv_map = create_inverse_map(ndx_map) elif isinstance(item, tuple) or isinstance(item, Tuple): result = convert_to_symbolic_indices(item, qubit_map=ndx_map, start=cur_ndx, gen=index_gen) sym_item, new_map, cur_ndx, index_gen = result ndx_map.update(new_map) inv_map = create_inverse_map(ndx_map) elif item in inv_map: sym_item = inv_map[item] else: cur_ndx = next(gen) ndx_map[cur_ndx] = item inv_map[item] = cur_ndx sym_item = cur_ndx if isinstance(item, Gate): sym_item = item.__class__(*sym_item) sym_seq = sym_seq + (sym_item,) return sym_seq, ndx_map, cur_ndx, index_gen def convert_to_real_indices(seq, qubit_map): """Returns the circuit with real indices. Parameters ========== seq : tuple, Gate/Integer/tuple or Mul A tuple of Gate, Integer, or tuple objects or a Mul qubit_map : dict A dictionary mapping symbolic indices to real indices. Examples ======== Change the symbolic indices to real integers: >>> from sympy import symbols >>> from sympy.physics.quantum.circuitutils import convert_to_real_indices >>> from sympy.physics.quantum.gate import X, Y, Z, H >>> i0, i1 = symbols('i:2') >>> index_map = {i0 : 0, i1 : 1} >>> convert_to_real_indices(X(i0)*Y(i1)*H(i0)*X(i1), index_map) (X(0), Y(1), H(0), X(1)) """ if isinstance(seq, Mul): seq = seq.args if not isinstance(qubit_map, dict): msg = 'Expected dict for qubit_map, got %r.' % qubit_map raise TypeError(msg) qubit_map = _sympify_qubit_map(qubit_map) real_seq = () for item in seq: # Nested items, so recurse if isinstance(item, Gate): real_item = convert_to_real_indices(item.args, qubit_map) elif isinstance(item, tuple) or isinstance(item, Tuple): real_item = convert_to_real_indices(item, qubit_map) else: real_item = qubit_map[item] if isinstance(item, Gate): real_item = item.__class__(*real_item) real_seq = real_seq + (real_item,) return real_seq def random_reduce(circuit, gate_ids, seed=None): """Shorten the length of a quantum circuit. random_reduce looks for circuit identities in circuit, randomly chooses one to remove, and returns a shorter yet equivalent circuit. If no identities are found, the same circuit is returned. Parameters ========== circuit : Gate tuple of Mul A tuple of Gates representing a quantum circuit gate_ids : list, GateIdentity List of gate identities to find in circuit seed : int or list seed used for _randrange; to override the random selection, provide a list of integers: the elements of gate_ids will be tested in the order given by the list """ from sympy.utilities.randtest import _randrange if not gate_ids: return circuit if isinstance(circuit, Mul): circuit = circuit.args ids = flatten_ids(gate_ids) # Create the random integer generator with the seed randrange = _randrange(seed) # Look for an identity in the circuit while ids: i = randrange(len(ids)) id = ids.pop(i) if find_subcircuit(circuit, id) != -1: break else: # no identity was found return circuit # return circuit with the identity removed return replace_subcircuit(circuit, id) def random_insert(circuit, choices, seed=None): """Insert a circuit into another quantum circuit. random_insert randomly chooses a location in the circuit to insert a randomly selected circuit from amongst the given choices. Parameters ========== circuit : Gate tuple or Mul A tuple or Mul of Gates representing a quantum circuit choices : list Set of circuit choices seed : int or list seed used for _randrange; to override the random selections, give a list two integers, [i, j] where i is the circuit location where choice[j] will be inserted. Notes ===== Indices for insertion should be [0, n] if n is the length of the circuit. """ from sympy.utilities.randtest import _randrange if not choices: return circuit if isinstance(circuit, Mul): circuit = circuit.args # get the location in the circuit and the element to insert from choices randrange = _randrange(seed) loc = randrange(len(circuit) + 1) choice = choices[randrange(len(choices))] circuit = list(circuit) circuit[loc: loc] = choice return tuple(circuit) # Flatten the GateIdentity objects (with gate rules) into one single list def flatten_ids(ids): collapse = lambda acc, an_id: acc + sorted(an_id.equivalent_ids, key=default_sort_key) ids = reduce(collapse, ids, []) ids.sort(key=default_sort_key) return ids sympy-0.7.4.1/sympy/physics/quantum/circuitplot.py0000644000175000017500000003120612253362407022516 0ustar georgeskgeorgesk"""Matplotlib based plotting of quantum circuits. Todo: * Optimize printing of large circuits. * Get this to work with single gates. * Do a better job checking the form of circuits to make sure it is a Mul of Gates. * Get multi-target gates plotting. * Get initial and final states to plot. * Get measurements to plot. Might need to rethink measurement as a gate issue. * Get scale and figsize to be handled in a better way. * Write some tests/examples! """ from __future__ import print_function, division from sympy import Mul from sympy.core.compatibility import u from sympy.external import import_module from sympy.physics.quantum.gate import Gate, OneQubitGate, CGate, CGateS from sympy.core.core import BasicMeta from sympy.core.assumptions import ManagedProperties __all__ = [ 'CircuitPlot', 'circuit_plot', 'labeller', 'Mz', 'Mx', 'CreateOneQubitGate', 'CreateCGate', ] np = import_module('numpy') matplotlib = import_module( 'matplotlib', __import__kwargs={'fromlist': ['pyplot']}, catch=(RuntimeError,)) # This is raised in environments that have no display. if not np or not matplotlib: class CircuitPlot(object): def __init__(*args, **kwargs): raise ImportError('numpy or matplotlib not available.') def circuit_plot(*args, **kwargs): raise ImportError('numpy or matplotlib not available.') else: pyplot = matplotlib.pyplot Line2D = matplotlib.lines.Line2D Circle = matplotlib.patches.Circle #from matplotlib import rc #rc('text',usetex=True) class CircuitPlot(object): """A class for managing a circuit plot.""" scale = 1.0 fontsize = 20.0 linewidth = 1.0 control_radius = 0.05 not_radius = 0.15 swap_delta = 0.05 labels = [] inits = {} label_buffer = 0.5 def __init__(self, c, nqubits, **kwargs): self.circuit = c self.ngates = len(self.circuit.args) self.nqubits = nqubits self.update(kwargs) self._create_grid() self._create_figure() self._plot_wires() self._plot_gates() self._finish() def update(self, kwargs): """Load the kwargs into the instance dict.""" self.__dict__.update(kwargs) def _create_grid(self): """Create the grid of wires.""" scale = self.scale wire_grid = np.arange(0.0, self.nqubits*scale, scale, dtype=float) gate_grid = np.arange(0.0, self.ngates*scale, scale, dtype=float) self._wire_grid = wire_grid self._gate_grid = gate_grid def _create_figure(self): """Create the main matplotlib figure.""" self._figure = pyplot.figure( figsize=(self.ngates*self.scale, self.nqubits*self.scale), facecolor='w', edgecolor='w' ) ax = self._figure.add_subplot( 1, 1, 1, frameon=True ) ax.set_axis_off() offset = 0.5*self.scale ax.set_xlim(self._gate_grid[0] - offset, self._gate_grid[-1] + offset) ax.set_ylim(self._wire_grid[0] - offset, self._wire_grid[-1] + offset) ax.set_aspect('equal') self._axes = ax def _plot_wires(self): """Plot the wires of the circuit diagram.""" xstart = self._gate_grid[0] xstop = self._gate_grid[-1] xdata = (xstart - self.scale, xstop + self.scale) for i in range(self.nqubits): ydata = (self._wire_grid[i], self._wire_grid[i]) line = Line2D( xdata, ydata, color='k', lw=self.linewidth ) self._axes.add_line(line) if self.labels: init_label_buffer = 0 if self.inits.get(self.labels[i]): init_label_buffer = 0.25 self._axes.text( xdata[0]-self.label_buffer-init_label_buffer,ydata[0], render_label(self.labels[i],self.inits), size=self.fontsize, color='k',ha='center',va='center') self._plot_measured_wires() def _plot_measured_wires(self): ismeasured = self._measurements() xstop = self._gate_grid[-1] dy = 0.04 # amount to shift wires when doubled # Plot doubled wires after they are measured for im in ismeasured: xdata = (self._gate_grid[ismeasured[im]],xstop+self.scale) ydata = (self._wire_grid[im]+dy,self._wire_grid[im]+dy) line = Line2D( xdata, ydata, color='k', lw=self.linewidth ) self._axes.add_line(line) # Also double any controlled lines off these wires for i,g in enumerate(self._gates()): if isinstance(g, CGate) or isinstance(g, CGateS): wires = g.controls + g.targets for wire in wires: if wire in ismeasured and \ self._gate_grid[i] > self._gate_grid[ismeasured[wire]]: ydata = min(wires), max(wires) xdata = self._gate_grid[i]-dy, self._gate_grid[i]-dy line = Line2D( xdata, ydata, color='k', lw=self.linewidth ) self._axes.add_line(line) def _gates(self): """Create a list of all gates in the circuit plot.""" gates = [] if isinstance(self.circuit, Mul): for g in reversed(self.circuit.args): if isinstance(g, Gate): gates.append(g) elif isinstance(self.circuit, Gate): gates.append(self.circuit) return gates def _plot_gates(self): """Iterate through the gates and plot each of them.""" for i, gate in enumerate(self._gates()): gate.plot_gate(self, i) def _measurements(self): """Return a dict {i:j} where i is the index of the wire that has been measured, and j is the gate where the wire is measured. """ ismeasured = {} for i,g in enumerate(self._gates()): if getattr(g,'measurement',False): for target in g.targets: if target in ismeasured: if ismeasured[target] > i: ismeasured[target] = i else: ismeasured[target] = i return ismeasured def _finish(self): # Disable clipping to make panning work well for large circuits. for o in self._figure.findobj(): o.set_clip_on(False) def one_qubit_box(self, t, gate_idx, wire_idx): """Draw a box for a single qubit gate.""" x = self._gate_grid[gate_idx] y = self._wire_grid[wire_idx] self._axes.text( x, y, t, color='k', ha='center', va='center', bbox=dict(ec='k', fc='w', fill=True, lw=self.linewidth), size=self.fontsize ) def two_qubit_box(self, t, gate_idx, wire_idx): """Draw a box for a two qubit gate. Doesn't work yet. """ x = self._gate_grid[gate_idx] y = self._wire_grid[wire_idx]+0.5 print(self._gate_grid) print(self._wire_grid) obj = self._axes.text( x, y, t, color='k', ha='center', va='center', bbox=dict(ec='k', fc='w', fill=True, lw=self.linewidth), size=self.fontsize ) def control_line(self, gate_idx, min_wire, max_wire): """Draw a vertical control line.""" xdata = (self._gate_grid[gate_idx], self._gate_grid[gate_idx]) ydata = (self._wire_grid[min_wire], self._wire_grid[max_wire]) line = Line2D( xdata, ydata, color='k', lw=self.linewidth ) self._axes.add_line(line) def control_point(self, gate_idx, wire_idx): """Draw a control point.""" x = self._gate_grid[gate_idx] y = self._wire_grid[wire_idx] radius = self.control_radius c = Circle( (x, y), radius*self.scale, ec='k', fc='k', fill=True, lw=self.linewidth ) self._axes.add_patch(c) def not_point(self, gate_idx, wire_idx): """Draw a NOT gates as the circle with plus in the middle.""" x = self._gate_grid[gate_idx] y = self._wire_grid[wire_idx] radius = self.not_radius c = Circle( (x, y), radius, ec='k', fc='w', fill=False, lw=self.linewidth ) self._axes.add_patch(c) l = Line2D( (x, x), (y - radius, y + radius), color='k', lw=self.linewidth ) self._axes.add_line(l) def swap_point(self, gate_idx, wire_idx): """Draw a swap point as a cross.""" x = self._gate_grid[gate_idx] y = self._wire_grid[wire_idx] d = self.swap_delta l1 = Line2D( (x - d, x + d), (y - d, y + d), color='k', lw=self.linewidth ) l2 = Line2D( (x - d, x + d), (y + d, y - d), color='k', lw=self.linewidth ) self._axes.add_line(l1) self._axes.add_line(l2) def circuit_plot(c, nqubits, **kwargs): """Draw the circuit diagram for the circuit with nqubits. Parameters ========== c : circuit The circuit to plot. Should be a product of Gate instances. nqubits : int The number of qubits to include in the circuit. Must be at least as big as the largest `min_qubits`` of the gates. """ return CircuitPlot(c, nqubits, **kwargs) def render_label(label, inits={}): """Slightly more flexible way to render labels. >>> from sympy.physics.quantum.circuitplot import render_label >>> render_label('q0') '$|q0\\\\rangle$' >>> render_label('q0', {'q0':'0'}) '$|q0\\\\rangle=|0\\\\rangle$' """ init = inits.get(label) if init: return r'$|%s\rangle=|%s\rangle$' % (label, init) return r'$|%s\rangle$' % label def labeller(n, symbol='q'): """Autogenerate labels for wires of quantum circuits. Parameters ========== n : int number of qubits in the circuit symbol : string A character string to precede all gate labels. E.g. 'q_0', 'q_1', etc. >>> from sympy.physics.quantum.circuitplot import labeller >>> labeller(2) ['q_1', 'q_0'] >>> labeller(3,'j') ['j_2', 'j_1', 'j_0'] """ return ['%s_%d' % (symbol,n-i-1) for i in range(n)] class Mz(OneQubitGate): """Mock-up of a z measurement gate. This is in circuitplot rather than gate.py because it's not a real gate, it just draws one. """ measurement = True gate_name='Mz' gate_name_latex=u('M_z') class Mx(OneQubitGate): """Mock-up of an x measurement gate. This is in circuitplot rather than gate.py because it's not a real gate, it just draws one. """ measurement = True gate_name='Mx' gate_name_latex=u('M_x') class CreateOneQubitGate(ManagedProperties): def __new__(mcl, name, latexname=None): if not latexname: latexname = name return BasicMeta.__new__(mcl, name + "Gate", (OneQubitGate,), {'gate_name': name, 'gate_name_latex': latexname}) def CreateCGate(name, latexname=None): """Use a lexical closure to make a controlled gate. """ if not latexname: latexname = name onequbitgate = CreateOneQubitGate(name, latexname) def ControlledGate(ctrls,target): return CGate(tuple(ctrls),onequbitgate(target)) return ControlledGate sympy-0.7.4.1/sympy/physics/quantum/qubit.py0000644000175000017500000005710012253362407021302 0ustar georgeskgeorgesk"""Qubits for quantum computing. Todo: * Finish implementing measurement logic. This should include POVM. * Update docstrings. * Update tests. """ from __future__ import print_function, division import math from sympy import Integer, log, Mul, Add, Pow, conjugate from sympy.core.basic import sympify from sympy.core.compatibility import string_types, xrange 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], string_types): 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)) 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)) 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 xrange(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 xrange(new_size): for j in xrange(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 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(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 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 def _eval_innerproduct_IntQubitBra(self, bra, **hints): return Qubit._eval_innerproduct_QubitBra(self, bra) 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 #----------------------------------------------------------------------------- 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 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>>> 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" ) 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" ) 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 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" ) sympy-0.7.4.1/sympy/physics/quantum/tensorproduct.py0000644000175000017500000002552712253362407023101 0ustar georgeskgeorgesk"""Abstract tensor product.""" from __future__ import print_function, division from sympy import Expr, Add, Mul, Matrix, Pow, sympify from sympy.core.compatibility import u from sympy.core.trace import Tr 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 ) __all__ = [ 'TensorProduct', 'tensor_product_simp' ] #----------------------------------------------------------------------------- # Tensor product #----------------------------------------------------------------------------- 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) Matrix([ [1, 0, 2, 0], [0, 1, 0, 2], [3, 0, 4, 0], [0, 3, 0, 4]]) >>> TensorProduct(m2, m1) Matrix([ [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(u('\u2a02') + u(' '))) 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 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 sympy-0.7.4.1/sympy/physics/quantum/identitysearch.py0000644000175000017500000006621112253362407023200 0ustar georgeskgeorgeskfrom __future__ import print_function, division from collections import deque from random import randint from sympy.external import import_module from sympy import Mul, Basic, Number, Pow, Integer from sympy.matrices import Matrix, eye from sympy.physics.quantum.gate import (Gate, X, Y, Z, H, S, T, CNOT, IdentityGate, gate_simp) from sympy.physics.quantum.represent import represent from sympy.physics.quantum.operator import (UnitaryOperator, HermitianOperator) from sympy.physics.quantum.dagger import Dagger __all__ = [ # Public interfaces 'generate_gate_rules', 'generate_equivalent_ids', 'GateIdentity', 'bfs_identity_search', 'random_identity_search', # "Private" functions 'is_scalar_sparse_matrix', 'is_scalar_nonsparse_matrix', 'is_degenerate', 'is_reducible', ] np = import_module('numpy') scipy = import_module('scipy', __import__kwargs={'fromlist': ['sparse']}) def is_scalar_sparse_matrix(circuit, nqubits, identity_only, eps=1e-11): """Checks if a given scipy.sparse matrix is a scalar matrix. A scalar matrix is such that B = bI, where B is the scalar matrix, b is some scalar multiple, and I is the identity matrix. A scalar matrix would have only the element b along it's main diagonal and zeroes elsewhere. Parameters ========== circuit : Gate tuple Sequence of quantum gates representing a quantum circuit nqubits : int Number of qubits in the circuit identity_only : bool Check for only identity matrices eps : number The tolerance value for zeroing out elements in the matrix. Values in the range [-eps, +eps] will be changed to a zero. """ if not np or not scipy: pass matrix = represent(Mul(*circuit), nqubits=nqubits, format='scipy.sparse') # In some cases, represent returns a 1D scalar value in place # of a multi-dimensional scalar matrix if (isinstance(matrix, int)): return matrix == 1 if identity_only else True # If represent returns a matrix, check if the matrix is diagonal # and if every item along the diagonal is the same else: # Due to floating pointing operations, must zero out # elements that are "very" small in the dense matrix # See parameter for default value. # Get the ndarray version of the dense matrix dense_matrix = matrix.todense().getA() # Since complex values can't be compared, must split # the matrix into real and imaginary components # Find the real values in between -eps and eps bool_real = np.logical_and(dense_matrix.real > -eps, dense_matrix.real < eps) # Find the imaginary values between -eps and eps bool_imag = np.logical_and(dense_matrix.imag > -eps, dense_matrix.imag < eps) # Replaces values between -eps and eps with 0 corrected_real = np.where(bool_real, 0.0, dense_matrix.real) corrected_imag = np.where(bool_imag, 0.0, dense_matrix.imag) # Convert the matrix with real values into imaginary values corrected_imag = corrected_imag * np.complex(1j) # Recombine the real and imaginary components corrected_dense = corrected_real + corrected_imag # Check if it's diagonal row_indices = corrected_dense.nonzero()[0] col_indices = corrected_dense.nonzero()[1] # Check if the rows indices and columns indices are the same # If they match, then matrix only contains elements along diagonal bool_indices = row_indices == col_indices is_diagonal = bool_indices.all() first_element = corrected_dense[0][0] # If the first element is a zero, then can't rescale matrix # and definitely not diagonal if (first_element == 0.0 + 0.0j): return False # The dimensions of the dense matrix should still # be 2^nqubits if there are elements all along the # the main diagonal trace_of_corrected = (corrected_dense/first_element).trace() expected_trace = pow(2, nqubits) has_correct_trace = trace_of_corrected == expected_trace # If only looking for identity matrices # first element must be a 1 real_is_one = abs(first_element.real - 1.0) < eps imag_is_zero = abs(first_element.imag) < eps is_one = real_is_one and imag_is_zero is_identity = is_one if identity_only else True return bool(is_diagonal and has_correct_trace and is_identity) def is_scalar_nonsparse_matrix(circuit, nqubits, identity_only): """Checks if a given circuit, in matrix form, is equivalent to a scalar value. Parameters ========== circuit : Gate tuple Sequence of quantum gates representing a quantum circuit nqubits : int Number of qubits in the circuit identity_only : bool Check for only identity matrices Note: Used in situations when is_scalar_sparse_matrix has bugs """ matrix = represent(Mul(*circuit), nqubits=nqubits) # In some cases, represent returns a 1D scalar value in place # of a multi-dimensional scalar matrix if (isinstance(matrix, Number)): return matrix == 1 if identity_only else True # If represent returns a matrix, check if the matrix is diagonal # and if every item along the diagonal is the same else: # Added up the diagonal elements matrix_trace = matrix.trace() # Divide the trace by the first element in the matrix # if matrix is not required to be the identity matrix adjusted_matrix_trace = (matrix_trace/matrix[0] if not identity_only else matrix_trace) is_identity = matrix[0] == 1.0 if identity_only else True has_correct_trace = adjusted_matrix_trace == pow(2, nqubits) # The matrix is scalar if it's diagonal and the adjusted trace # value is equal to 2^nqubits return bool( matrix.is_diagonal() and has_correct_trace and is_identity) if np and scipy: is_scalar_matrix = is_scalar_sparse_matrix else: is_scalar_matrix = is_scalar_nonsparse_matrix def _get_min_qubits(a_gate): if isinstance(a_gate, Pow): return a_gate.base.min_qubits else: return a_gate.min_qubits def ll_op(left, right): """Perform a LL operation. A LL operation multiplies both left and right circuits with the dagger of the left circuit's leftmost gate, and the dagger is multiplied on the left side of both circuits. If a LL is possible, it returns the new gate rule as a 2-tuple (LHS, RHS), where LHS is the left circuit and and RHS is the right circuit of the new rule. If a LL is not possible, None is returned. Parameters ========== left : Gate tuple The left circuit of a gate rule expression. right : Gate tuple The right circuit of a gate rule expression. Examples ======== Generate a new gate rule using a LL operation: >>> from sympy.physics.quantum.identitysearch import ll_op >>> from sympy.physics.quantum.gate import X, Y, Z >>> x = X(0); y = Y(0); z = Z(0) >>> ll_op((x, y, z), ()) ((Y(0), Z(0)), (X(0),)) >>> ll_op((y, z), (x,)) ((Z(0),), (Y(0), X(0))) """ if (len(left) > 0): ll_gate = left[0] ll_gate_is_unitary = is_scalar_matrix( (Dagger(ll_gate), ll_gate), _get_min_qubits(ll_gate), True) if (len(left) > 0 and ll_gate_is_unitary): # Get the new left side w/o the leftmost gate new_left = left[1:len(left)] # Add the leftmost gate to the left position on the right side new_right = (Dagger(ll_gate),) + right # Return the new gate rule return (new_left, new_right) return None def lr_op(left, right): """Perform a LR operation. A LR operation multiplies both left and right circuits with the dagger of the left circuit's rightmost gate, and the dagger is multiplied on the right side of both circuits. If a LR is possible, it returns the new gate rule as a 2-tuple (LHS, RHS), where LHS is the left circuit and and RHS is the right circuit of the new rule. If a LR is not possible, None is returned. Parameters ========== left : Gate tuple The left circuit of a gate rule expression. right : Gate tuple The right circuit of a gate rule expression. Examples ======== Generate a new gate rule using a LR operation: >>> from sympy.physics.quantum.identitysearch import lr_op >>> from sympy.physics.quantum.gate import X, Y, Z >>> x = X(0); y = Y(0); z = Z(0) >>> lr_op((x, y, z), ()) ((X(0), Y(0)), (Z(0),)) >>> lr_op((x, y), (z,)) ((X(0),), (Z(0), Y(0))) """ if (len(left) > 0): lr_gate = left[len(left) - 1] lr_gate_is_unitary = is_scalar_matrix( (Dagger(lr_gate), lr_gate), _get_min_qubits(lr_gate), True) if (len(left) > 0 and lr_gate_is_unitary): # Get the new left side w/o the rightmost gate new_left = left[0:len(left) - 1] # Add the rightmost gate to the right position on the right side new_right = right + (Dagger(lr_gate),) # Return the new gate rule return (new_left, new_right) return None def rl_op(left, right): """Perform a RL operation. A RL operation multiplies both left and right circuits with the dagger of the right circuit's leftmost gate, and the dagger is multiplied on the left side of both circuits. If a RL is possible, it returns the new gate rule as a 2-tuple (LHS, RHS), where LHS is the left circuit and and RHS is the right circuit of the new rule. If a RL is not possible, None is returned. Parameters ========== left : Gate tuple The left circuit of a gate rule expression. right : Gate tuple The right circuit of a gate rule expression. Examples ======== Generate a new gate rule using a RL operation: >>> from sympy.physics.quantum.identitysearch import rl_op >>> from sympy.physics.quantum.gate import X, Y, Z >>> x = X(0); y = Y(0); z = Z(0) >>> rl_op((x,), (y, z)) ((Y(0), X(0)), (Z(0),)) >>> rl_op((x, y), (z,)) ((Z(0), X(0), Y(0)), ()) """ if (len(right) > 0): rl_gate = right[0] rl_gate_is_unitary = is_scalar_matrix( (Dagger(rl_gate), rl_gate), _get_min_qubits(rl_gate), True) if (len(right) > 0 and rl_gate_is_unitary): # Get the new right side w/o the leftmost gate new_right = right[1:len(right)] # Add the leftmost gate to the left position on the left side new_left = (Dagger(rl_gate),) + left # Return the new gate rule return (new_left, new_right) return None def rr_op(left, right): """Perform a RR operation. A RR operation multiplies both left and right circuits with the dagger of the right circuit's rightmost gate, and the dagger is multiplied on the right side of both circuits. If a RR is possible, it returns the new gate rule as a 2-tuple (LHS, RHS), where LHS is the left circuit and and RHS is the right circuit of the new rule. If a RR is not possible, None is returned. Parameters ========== left : Gate tuple The left circuit of a gate rule expression. right : Gate tuple The right circuit of a gate rule expression. Examples ======== Generate a new gate rule using a RR operation: >>> from sympy.physics.quantum.identitysearch import rr_op >>> from sympy.physics.quantum.gate import X, Y, Z >>> x = X(0); y = Y(0); z = Z(0) >>> rr_op((x, y), (z,)) ((X(0), Y(0), Z(0)), ()) >>> rr_op((x,), (y, z)) ((X(0), Z(0)), (Y(0),)) """ if (len(right) > 0): rr_gate = right[len(right) - 1] rr_gate_is_unitary = is_scalar_matrix( (Dagger(rr_gate), rr_gate), _get_min_qubits(rr_gate), True) if (len(right) > 0 and rr_gate_is_unitary): # Get the new right side w/o the rightmost gate new_right = right[0:len(right) - 1] # Add the rightmost gate to the right position on the right side new_left = left + (Dagger(rr_gate),) # Return the new gate rule return (new_left, new_right) return None def generate_gate_rules(gate_seq, return_as_muls=False): """Returns a set of gate rules. Each gate rules is represented as a 2-tuple of tuples or Muls. An empty tuple represents an arbitrary scalar value. This function uses the four operations (LL, LR, RL, RR) to generate the gate rules. A gate rule is an expression such as ABC = D or AB = CD, where A, B, C, and D are gates. Each value on either side of the equal sign represents a circuit. The four operations allow one to find a set of equivalent circuits from a gate identity. The letters denoting the operation tell the user what activities to perform on each expression. The first letter indicates which side of the equal sign to focus on. The second letter indicates which gate to focus on given the side. Once this information is determined, the inverse of the gate is multiplied on both circuits to create a new gate rule. For example, given the identity, ABCD = 1, a LL operation means look at the left value and multiply both left sides by the inverse of the leftmost gate A. If A is Hermitian, the inverse of A is still A. The resulting new rule is BCD = A. The following is a summary of the four operations. Assume that in the examples, all gates are Hermitian. LL : left circuit, left multiply ABCD = E -> AABCD = AE -> BCD = AE LR : left circuit, right multiply ABCD = E -> ABCDD = ED -> ABC = ED RL : right circuit, left multiply ABC = ED -> EABC = EED -> EABC = D RR : right circuit, right multiply AB = CD -> ABD = CDD -> ABD = C The number of gate rules generated is n*(n+1), where n is the number of gates in the sequence (unproven). Parameters ========== gate_seq : Gate tuple, Mul, or Number A variable length tuple or Mul of Gates whose product is equal to a scalar matrix return_as_muls : bool True to return a set of Muls; False to return a set of tuples Examples ======== Find the gate rules of the current circuit using tuples: >>> from sympy.physics.quantum.identitysearch import generate_gate_rules >>> from sympy.physics.quantum.gate import X, Y, Z >>> x = X(0); y = Y(0); z = Z(0) >>> generate_gate_rules((x, x)) set([((X(0),), (X(0),)), ((X(0), X(0)), ())]) >>> generate_gate_rules((x, y, z)) set([((), (X(0), Z(0), Y(0))), ((), (Y(0), X(0), Z(0))), ((), (Z(0), Y(0), X(0))), ((X(0),), (Z(0), Y(0))), ((Y(0),), (X(0), Z(0))), ((Z(0),), (Y(0), X(0))), ((X(0), Y(0)), (Z(0),)), ((Y(0), Z(0)), (X(0),)), ((Z(0), X(0)), (Y(0),)), ((X(0), Y(0), Z(0)), ()), ((Y(0), Z(0), X(0)), ()), ((Z(0), X(0), Y(0)), ())]) Find the gate rules of the current circuit using Muls: >>> generate_gate_rules(x*x, return_as_muls=True) set([(1, 1)]) >>> generate_gate_rules(x*y*z, return_as_muls=True) set([(1, X(0)*Z(0)*Y(0)), (1, Y(0)*X(0)*Z(0)), (1, Z(0)*Y(0)*X(0)), (X(0)*Y(0), Z(0)), (Y(0)*Z(0), X(0)), (Z(0)*X(0), Y(0)), (X(0)*Y(0)*Z(0), 1), (Y(0)*Z(0)*X(0), 1), (Z(0)*X(0)*Y(0), 1), (X(0), Z(0)*Y(0)), (Y(0), X(0)*Z(0)), (Z(0), Y(0)*X(0))]) """ if isinstance(gate_seq, Number): if return_as_muls: return set([(Integer(1), Integer(1))]) else: return set([((), ())]) elif isinstance(gate_seq, Mul): gate_seq = gate_seq.args # Each item in queue is a 3-tuple: # i) first item is the left side of an equality # ii) second item is the right side of an equality # iii) third item is the number of operations performed # The argument, gate_seq, will start on the left side, and # the right side will be empty, implying the presence of an # identity. queue = deque() # A set of gate rules rules = set() # Maximum number of operations to perform max_ops = len(gate_seq) def process_new_rule(new_rule, ops): if new_rule is not None: new_left, new_right = new_rule if new_rule not in rules and (new_right, new_left) not in rules: rules.add(new_rule) # If haven't reached the max limit on operations if ops + 1 < max_ops: queue.append(new_rule + (ops + 1,)) queue.append((gate_seq, (), 0)) rules.add((gate_seq, ())) while len(queue) > 0: left, right, ops = queue.popleft() # Do a LL new_rule = ll_op(left, right) process_new_rule(new_rule, ops) # Do a LR new_rule = lr_op(left, right) process_new_rule(new_rule, ops) # Do a RL new_rule = rl_op(left, right) process_new_rule(new_rule, ops) # Do a RR new_rule = rr_op(left, right) process_new_rule(new_rule, ops) if return_as_muls: # Convert each rule as tuples into a rule as muls mul_rules = set() for rule in rules: left, right = rule mul_rules.add((Mul(*left), Mul(*right))) rules = mul_rules return rules def generate_equivalent_ids(gate_seq, return_as_muls=False): """Returns a set of equivalent gate identities. A gate identity is a quantum circuit such that the product of the gates in the circuit is equal to a scalar value. For example, XYZ = i, where X, Y, Z are the Pauli gates and i is the imaginary value, is considered a gate identity. This function uses the four operations (LL, LR, RL, RR) to generate the gate rules and, subsequently, to locate equivalent gate identities. Note that all equivalent identities are reachable in n operations from the starting gate identity, where n is the number of gates in the sequence. The max number of gate identities is 2n, where n is the number of gates in the sequence (unproven). Parameters ========== gate_seq : Gate tuple, Mul, or Number A variable length tuple or Mul of Gates whose product is equal to a scalar matrix. return_as_muls: bool True to return as Muls; False to return as tuples Examples ======== Find equivalent gate identities from the current circuit with tuples: >>> from sympy.physics.quantum.identitysearch import generate_equivalent_ids >>> from sympy.physics.quantum.gate import X, Y, Z >>> x = X(0); y = Y(0); z = Z(0) >>> generate_equivalent_ids((x, x)) set([(X(0), X(0))]) >>> generate_equivalent_ids((x, y, z)) set([(X(0), Y(0), Z(0)), (X(0), Z(0), Y(0)), (Y(0), X(0), Z(0)), (Y(0), Z(0), X(0)), (Z(0), X(0), Y(0)), (Z(0), Y(0), X(0))]) Find equivalent gate identities from the current circuit with Muls: >>> generate_equivalent_ids(x*x, return_as_muls=True) set([1]) >>> generate_equivalent_ids(x*y*z, return_as_muls=True) set([X(0)*Y(0)*Z(0), X(0)*Z(0)*Y(0), Y(0)*X(0)*Z(0), Y(0)*Z(0)*X(0), Z(0)*X(0)*Y(0), Z(0)*Y(0)*X(0)]) """ if isinstance(gate_seq, Number): return set([Integer(1)]) elif isinstance(gate_seq, Mul): gate_seq = gate_seq.args # Filter through the gate rules and keep the rules # with an empty tuple either on the left or right side # A set of equivalent gate identities eq_ids = set() gate_rules = generate_gate_rules(gate_seq) for rule in gate_rules: l, r = rule if l == (): eq_ids.add(r) elif r == (): eq_ids.add(l) if return_as_muls: convert_to_mul = lambda id_seq: Mul(*id_seq) eq_ids = set(map(convert_to_mul, eq_ids)) return eq_ids class GateIdentity(Basic): """Wrapper class for circuits that reduce to a scalar value. A gate identity is a quantum circuit such that the product of the gates in the circuit is equal to a scalar value. For example, XYZ = i, where X, Y, Z are the Pauli gates and i is the imaginary value, is considered a gate identity. Parameters ========== args : Gate tuple A variable length tuple of Gates that form an identity. Examples ======== Create a GateIdentity and look at its attributes: >>> from sympy.physics.quantum.identitysearch import GateIdentity >>> from sympy.physics.quantum.gate import X, Y, Z >>> x = X(0); y = Y(0); z = Z(0) >>> an_identity = GateIdentity(x, y, z) >>> an_identity.circuit X(0)*Y(0)*Z(0) >>> an_identity.equivalent_ids set([(X(0), Y(0), Z(0)), (X(0), Z(0), Y(0)), (Y(0), X(0), Z(0)), (Y(0), Z(0), X(0)), (Z(0), X(0), Y(0)), (Z(0), Y(0), X(0))]) """ def __new__(cls, *args): # args should be a tuple - a variable length argument list obj = Basic.__new__(cls, *args) obj._circuit = Mul(*args) obj._rules = generate_gate_rules(args) obj._eq_ids = generate_equivalent_ids(args) return obj @property def circuit(self): return self._circuit @property def gate_rules(self): return self._rules @property def equivalent_ids(self): return self._eq_ids @property def sequence(self): return self.args def __str__(self): """Returns the string of gates in a tuple.""" return str(self.circuit) def is_degenerate(identity_set, gate_identity): """Checks if a gate identity is a permutation of another identity. Parameters ========== identity_set : set A Python set with GateIdentity objects. gate_identity : GateIdentity The GateIdentity to check for existence in the set. Examples ======== Check if the identity is a permutation of another identity: >>> from sympy.physics.quantum.identitysearch import ( ... GateIdentity, is_degenerate) >>> from sympy.physics.quantum.gate import X, Y, Z >>> x = X(0); y = Y(0); z = Z(0) >>> an_identity = GateIdentity(x, y, z) >>> id_set = set([an_identity]) >>> another_id = (y, z, x) >>> is_degenerate(id_set, another_id) True >>> another_id = (x, x) >>> is_degenerate(id_set, another_id) False """ # For now, just iteratively go through the set and check if the current # gate_identity is a permutation of an identity in the set for an_id in identity_set: if (gate_identity in an_id.equivalent_ids): return True return False def is_reducible(circuit, nqubits, begin, end): """Determines if a circuit is reducible by checking if its subcircuits are scalar values. Parameters ========== circuit : Gate tuple A tuple of Gates representing a circuit. The circuit to check if a gate identity is contained in a subcircuit. nqubits : int The number of qubits the circuit operates on. begin : int The leftmost gate in the circuit to include in a subcircuit. end : int The rightmost gate in the circuit to include in a subcircuit. Examples ======== Check if the circuit can be reduced: >>> from sympy.physics.quantum.identitysearch import ( ... GateIdentity, is_reducible) >>> from sympy.physics.quantum.gate import X, Y, Z >>> x = X(0); y = Y(0); z = Z(0) >>> is_reducible((x, y, z), 1, 0, 3) True Check if an interval in the circuit can be reduced: >>> is_reducible((x, y, z), 1, 1, 3) False >>> is_reducible((x, y, y), 1, 1, 3) True """ current_circuit = () # Start from the gate at "end" and go down to almost the gate at "begin" for ndx in reversed(range(begin, end)): next_gate = circuit[ndx] current_circuit = (next_gate,) + current_circuit # If a circuit as a matrix is equivalent to a scalar value if (is_scalar_matrix(current_circuit, nqubits, False)): return True return False def bfs_identity_search(gate_list, nqubits, max_depth=None, identity_only=False): """Constructs a set of gate identities from the list of possible gates. Performs a breadth first search over the space of gate identities. This allows the finding of the shortest gate identities first. Parameters ========== gate_list : list, Gate A list of Gates from which to search for gate identities. nqubits : int The number of qubits the quantum circuit operates on. max_depth : int The longest quantum circuit to construct from gate_list. identity_only : bool True to search for gate identities that reduce to identity; False to search for gate identities that reduce to a scalar. Examples ======== Find a list of gate identities: >>> from sympy.physics.quantum.identitysearch import bfs_identity_search >>> from sympy.physics.quantum.gate import X, Y, Z, H >>> x = X(0); y = Y(0); z = Z(0) >>> bfs_identity_search([x], 1, max_depth=2) set([GateIdentity(X(0), X(0))]) >>> bfs_identity_search([x, y, z], 1) set([GateIdentity(X(0), X(0)), GateIdentity(Y(0), Y(0)), GateIdentity(Z(0), Z(0)), GateIdentity(X(0), Y(0), Z(0))]) Find a list of identities that only equal to 1: >>> bfs_identity_search([x, y, z], 1, identity_only=True) set([GateIdentity(X(0), X(0)), GateIdentity(Y(0), Y(0)), GateIdentity(Z(0), Z(0))]) """ if max_depth is None or max_depth <= 0: max_depth = len(gate_list) id_only = identity_only # Start with an empty sequence (implicitly contains an IdentityGate) queue = deque([()]) # Create an empty set of gate identities ids = set() # Begin searching for gate identities in given space. while (len(queue) > 0): current_circuit = queue.popleft() for next_gate in gate_list: new_circuit = current_circuit + (next_gate,) # Determines if a (strict) subcircuit is a scalar matrix circuit_reducible = is_reducible(new_circuit, nqubits, 1, len(new_circuit)) # In many cases when the matrix is a scalar value, # the evaluated matrix will actually be an integer if (is_scalar_matrix(new_circuit, nqubits, id_only) and not is_degenerate(ids, new_circuit) and not circuit_reducible): ids.add(GateIdentity(*new_circuit)) elif (len(new_circuit) < max_depth and not circuit_reducible): queue.append(new_circuit) return ids def random_identity_search(gate_list, numgates, nqubits): """Randomly selects numgates from gate_list and checks if it is a gate identity. If the circuit is a gate identity, the circuit is returned; Otherwise, None is returned. """ gate_size = len(gate_list) circuit = () for i in range(numgates): next_gate = gate_list[randint(0, gate_size - 1)] circuit = circuit + (next_gate,) is_scalar = is_scalar_matrix(circuit, nqubits, False) return circuit if is_scalar else None sympy-0.7.4.1/sympy/physics/quantum/operator.py0000644000175000017500000003567012253362407022021 0ustar georgeskgeorgesk"""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 __future__ import print_function, division 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 #----------------------------------------------------------------------------- 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') def inverse(self): return self._eval_inverse() inv = inverse def _eval_inverse(self): return self**(-1) 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) 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() 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>>> 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>>> op.hilbert_space H >>> op.ket |k> >>> op.bra >> Dagger(op) |b>>> k*b |k>>> A = Operator('A') >>> A*k*b A*|k>*>> A*(k*b) A*|k>>> 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 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 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 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 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 sympy-0.7.4.1/sympy/physics/quantum/grover.py0000644000175000017500000002250112253362407021457 0ustar georgeskgeorgesk"""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 __future__ import print_function, division from sympy import floor, pi, sqrt, sympify from sympy.core.compatibility import u 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 __all__ = [ 'OracleGate', 'WGate', 'superposition_basis', 'grover_iteration', 'apply_grover' ] 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)]) 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 = u('V') gate_name_latex = u('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 callable(args[1]): 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 def search_function(self): """The unknown function that helps find the sought after qubits.""" return self.label[1] @property 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" ) class WGate(Gate): """General n qubit W Gate in Grover's algorithm. The gate performs the operation ``2|phi> = (tensor product of n Hadamards)*(|0> with n qubits)`` Parameters ========== nqubits : int The number of qubits to operate on """ gate_name = u('W') gate_name_latex = u('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(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 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 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 sympy-0.7.4.1/sympy/physics/quantum/hilbert.py0000644000175000017500000004606012253362407021612 0ustar georgeskgeorgesk"""Hilbert spaces for quantum mechanics. Authors: * Brian Granger * Matt Curry """ from __future__ import print_function, division from sympy import Basic, Interval, oo, sympify from sympy.core.compatibility import u from sympy.printing.pretty.stringpict import prettyForm from sympy.physics.quantum.qexpr import QuantumError from sympy.core.compatibility import reduce __all__ = [ 'HilbertSpaceError', 'HilbertSpace', 'ComplexSpace', 'L2', 'FockSpace' ] #----------------------------------------------------------------------------- # Main objects #----------------------------------------------------------------------------- class HilbertSpaceError(QuantumError): pass #----------------------------------------------------------------------------- # Main objects #----------------------------------------------------------------------------- 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 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 u('H') def _pretty(self, printer, *args): # u = u('\u2108') # script ustr = u('\u0048') return prettyForm(ustr) def _latex(self, printer, *args): return r'\mathcal{H}' 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 ustr = u('\u0043') pform_exp = printer._print(self.dimension, *args) pform_base = prettyForm(ustr) return pform_base**pform_exp def _latex(self, printer, *args): return r'\mathcal{C}^{%s}' % printer._print(self.dimension, *args) 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(u('2')) pform_base = prettyForm(u('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 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 ustr = u('\u0046') return prettyForm(ustr) 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(u(' ') + u('\u2a02') + u(' '))) 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(u(' ') + u('\u2295') + u(' '))) 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(u('\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) sympy-0.7.4.1/sympy/physics/quantum/shor.py0000644000175000017500000001421012253362407021124 0ustar georgeskgeorgesk"""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. """ from __future__ import print_function, division 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 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 def t(self): """Size of 1/2 input register. First 1/2 holds output.""" return self.label[0] @property def a(self): """Base of the controlled mod function.""" return self.label[1] @property 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(range(self.t)): outarray.append((out >> i) & 1) return Qubit(*outarray) 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) 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 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(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 sympy-0.7.4.1/sympy/physics/quantum/state.py0000644000175000017500000007036512253362407021306 0ustar georgeskgeorgesk"""Dirac notation for states.""" from __future__ import print_function, division from sympy import (cacheit, conjugate, Expr, Function, integrate, oo, sqrt, Tuple) from sympy.core.compatibility import u 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 = u("\u27E8") _rbracket_ucode = u("\u27E9") # LIGHT VERTICAL BAR _straight_bracket_ucode = u("\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" 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 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 def dual(self): """Return the dual state of this one.""" return self.dual_class()._new_rawargs(self.hilbert_space, *self.args) @classmethod 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 = u('\u2571'), u('\u2572'), u('\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) 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 , 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) 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)) class State(StateBase): """General abstract quantum state used as a base class for Ket and Bra.""" pass 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 >> k.dual_class() 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 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 >> b.hilbert_space H >>> b.is_commutative False Bra's know about their dual Ket's:: >>> b.dual |psi> >>> b.dual_class() 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*>> b.subs(n,m) >> 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 >> k.dual_class() """ @classmethod def dual_class(self): return TimeDepBra 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 >> b.time t >>> b.label (psi,) >>> b.hilbert_space H >>> b.dual |psi;t> """ @classmethod def dual_class(self): return TimeDepKet 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, positive=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]) 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) is True or (args[ct] > upper) is True: 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 options.keys(): val = options[str(symbol)] expr = expr.subs(symbol, val) return expr.subs(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 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 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 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(zip(self.variables, tuple(limits))) @property 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 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, positive=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 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, positive=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) 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 = symbols('x', real=True) >>> L = symbols('L', positive=True) >>> n = symbols('n', integer=True, positive=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:]) 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) sympy-0.7.4.1/sympy/physics/quantum/qasm.py0000644000175000017500000001423212253362407021116 0ustar georgeskgeorgesk""" qasm.py - Functions to parse a set of qasm commands into a Sympy Circuit. Examples taken from Chuang's page: http://www.media.mit.edu/quanta/qasm2circ/ The code returns a circuit and an associated list of labels. >>> from sympy.physics.quantum.qasm import Qasm >>> q = Qasm('qubit q0', 'qubit q1', 'h q0', 'cnot q0,q1') >>> q.get_circuit() CNOT(1,0)*H(1) >>> q = Qasm('qubit q0', 'qubit q1', 'cnot q0,q1', 'cnot q1,q0', 'cnot q0,q1') >>> q.get_circuit() CNOT(1,0)*CNOT(0,1)*CNOT(1,0) """ __all__ = [ 'Qasm', ] from sympy.physics.quantum.gate import H, CNOT, X, Z, CGate, CGateS, SWAP, S, T,CPHASE from sympy.physics.quantum.circuitplot import Mz def read_qasm(lines): return Qasm(*lines.splitlines()) def read_qasm_file(filename): return Qasm(*open(filename).readlines()) def prod(c): p = 1 for ci in c: p *= ci return p def flip_index(i, n): """Reorder qubit indices from largest to smallest. >>> from sympy.physics.quantum.qasm import flip_index >>> flip_index(0, 2) 1 >>> flip_index(1, 2) 0 """ return n-i-1 def trim(line): """Remove everything following comment # characters in line. >>> from sympy.physics.quantum.qasm import trim >>> trim('nothing happens here') 'nothing happens here' >>> trim('something #happens here') 'something ' """ if not '#' in line: return line return line.split('#')[0] def get_index(target, labels): """Get qubit labels from the rest of the line,and return indices >>> from sympy.physics.quantum.qasm import get_index >>> get_index('q0', ['q0', 'q1']) 1 >>> get_index('q1', ['q0', 'q1']) 0 """ nq = len(labels) return flip_index(labels.index(target), nq) def get_indices(targets, labels): return [get_index(t, labels) for t in targets] def nonblank(args): for line in args: line = trim(line) if line.isspace(): continue yield line return def fullsplit(line): words = line.split() rest = ' '.join(words[1:]) return fixcommand(words[0]), [s.strip() for s in rest.split(',')] def fixcommand(c): """Fix Qasm command names. Remove all of forbidden characters from command c, and replace 'def' with 'qdef'. """ forbidden_characters = ['-'] c = c.lower() for char in forbidden_characters: c = c.replace(char, '') if c == 'def': return 'qdef' return c def stripquotes(s): """Replace explicit quotes in a string. >>> from sympy.physics.quantum.qasm import stripquotes >>> stripquotes("'S'") == 'S' True >>> stripquotes('"S"') == 'S' True >>> stripquotes('S') == 'S' True """ s = s.replace('"', '') # Remove second set of quotes? s = s.replace("'", '') return s class Qasm(object): """Class to form objects from Qasm lines >>> from sympy.physics.quantum.qasm import Qasm >>> q = Qasm('qubit q0', 'qubit q1', 'h q0', 'cnot q0,q1') >>> q.get_circuit() CNOT(1,0)*H(1) >>> q = Qasm('qubit q0', 'qubit q1', 'cnot q0,q1', 'cnot q1,q0', 'cnot q0,q1') >>> q.get_circuit() CNOT(1,0)*CNOT(0,1)*CNOT(1,0) """ def __init__(self, *args, **kwargs): self.defs = {} self.circuit = [] self.labels = [] self.inits = {} self.add(*args) self.kwargs = kwargs def add(self, *lines): for line in nonblank(lines): command, rest = fullsplit(line) if self.defs.get(command): #defs come first, since you can override built-in function = self.defs.get(command) indices = self.indices(rest) if len(indices) == 1: self.circuit.append(function(indices[0])) else: self.circuit.append(function(indices[:-1], indices[-1])) elif hasattr(self, command): function = getattr(self, command) function(*rest) else: print("Function %s not defined. Skipping" % command) def get_circuit(self): return prod(reversed(self.circuit)) def get_labels(self): return list(reversed(self.labels)) def plot(self): from sympy.physics.quantum.circuitplot import CircuitPlot circuit, labels = self.get_circuit(), self.get_labels() CircuitPlot(circuit, len(labels), labels=labels, inits=self.inits) def qubit(self, arg, init=None): self.labels.append(arg) if init: self.inits[arg] = init def indices(self, args): return get_indices(args, self.labels) def index(self, arg): return get_index(arg, self.labels) def nop(self, *args): pass def x(self, arg): self.circuit.append(X(self.index(arg))) def z(self, arg): self.circuit.append(Z(self.index(arg))) def h(self, arg): self.circuit.append(H(self.index(arg))) def s(self, arg): self.circuit.append(S(self.index(arg))) def t(self, arg): self.circuit.append(T(self.index(arg))) def measure(self, arg): self.circuit.append(Mz(self.index(arg))) def cnot(self, a1, a2): self.circuit.append(CNOT(*self.indices([a1, a2]))) def swap(self, a1, a2): self.circuit.append(SWAP(*self.indices([a1, a2]))) def cphase(self, a1, a2): self.circuit.append(CPHASE(*self.indices([a1, a2]))) def toffoli(self, a1, a2, a3): i1, i2, i3 = self.indices([a1, a2, a3]) self.circuit.append(CGateS((i1, i2), X(i3))) def cx(self, a1, a2): fi, fj = self.indices([a1, a2]) self.circuit.append(CGate(fi, X(fj))) def cz(self, a1, a2): fi, fj = self.indices([a1, a2]) self.circuit.append(CGate(fi, Z(fj))) def defbox(self, *args): print("defbox not supported yet. Skipping: ", args) def qdef(self, name, ncontrols, symbol): from sympy.physics.quantum.circuitplot import CreateOneQubitGate, CreateCGate ncontrols = int(ncontrols) command = fixcommand(name) symbol = stripquotes(symbol) if ncontrols > 0: self.defs[command] = CreateCGate(symbol) else: self.defs[command] = CreateOneQubitGate(symbol) sympy-0.7.4.1/sympy/physics/quantum/dagger.py0000644000175000017500000000430212253362407021403 0ustar georgeskgeorgesk"""Hermitian conjugation.""" from __future__ import print_function, division from sympy.core import Expr from sympy.functions.elementary.complexes import adjoint __all__ = [ 'Dagger' ] 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')) >> 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'))) >>> Dagger(OuterProduct(Ket('a'), Bra('b'))) |b>>> 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 Matrix([ [1, I], [2, I]]) >>> Dagger(m) Matrix([ [ 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]) sympy-0.7.4.1/sympy/physics/quantum/density.py0000644000175000017500000002317312253362407021640 0ustar georgeskgeorgeskfrom __future__ import print_function, division from itertools import product from sympy import Tuple, Add, Mul, Matrix, log, expand, sqrt, Rational from sympy.core.trace import Tr from sympy.core.compatibility import u from sympy.printing.pretty.stringpict import prettyForm from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.operator import HermitianOperator, OuterProduct, Operator from sympy.physics.quantum.represent import represent from sympy.physics.quantum.matrixutils import numpy_ndarray, scipy_sparse_matrix, to_numpy from sympy.physics.quantum.tensorproduct import TensorProduct, tensor_product_simp class Density(HermitianOperator): """Density operator for representing mixed states. TODO: Density operator support for Qubits Parameters ========== values : tuples/lists Each tuple/list should be of form (state, prob) or [state,prob] Examples ========= Create a density operator with 2 states represented by Kets. >>> from sympy.physics.quantum.state import Ket >>> from sympy.physics.quantum.density import Density >>> d = Density([Ket(0), 0.5], [Ket(1),0.5]) >>> d 'Density'((|0>, 0.5),(|1>, 0.5)) """ @classmethod def _eval_args(cls, args): # call this to qsympify the args args = super(Density, cls)._eval_args(args) for arg in args: # Check if arg is a tuple if not (isinstance(arg, Tuple) and len(arg) == 2): raise ValueError("Each argument should be of form [state,prob]" " or ( state, prob )") return args def states(self): """Return list of all states. Examples ========= >>> from sympy.physics.quantum.state import Ket >>> from sympy.physics.quantum.density import Density >>> d = Density([Ket(0), 0.5], [Ket(1),0.5]) >>> d.states() (|0>, |1>) """ return Tuple(*[arg[0] for arg in self.args]) def probs(self): """Return list of all probabilities. Examples ========= >>> from sympy.physics.quantum.state import Ket >>> from sympy.physics.quantum.density import Density >>> d = Density([Ket(0), 0.5], [Ket(1),0.5]) >>> d.probs() (0.5, 0.5) """ return Tuple(*[arg[1] for arg in self.args]) def get_state(self, index): """Return specfic state by index. Parameters ========== index : index of state to be returned Examples ========= >>> from sympy.physics.quantum.state import Ket >>> from sympy.physics.quantum.density import Density >>> d = Density([Ket(0), 0.5], [Ket(1),0.5]) >>> d.states()[1] |1> """ state = self.args[index][0] return state def get_prob(self, index): """Return probability of specific state by index. Parameters =========== index : index of states whose probability is returned. Examples ========= >>> from sympy.physics.quantum.state import Ket >>> from sympy.physics.quantum.density import Density >>> d = Density([Ket(0), 0.5], [Ket(1),0.5]) >>> d.probs()[1] 0.500000000000000 """ prob = self.args[index][1] return prob def apply_op(self, op): """op will operate on each individual state. Parameters ========== op : Operator Examples ========= >>> from sympy.physics.quantum.state import Ket >>> from sympy.physics.quantum.density import Density >>> from sympy.physics.quantum.operator import Operator >>> A = Operator('A') >>> d = Density([Ket(0), 0.5], [Ket(1),0.5]) >>> d.apply_op(A) 'Density'((A*|0>, 0.5),(A*|1>, 0.5)) """ new_args = [(op*state, prob) for (state, prob) in self.args] return Density(*new_args) def doit(self, **hints): """Expand the density operator into an outer product format. Examples ========= >>> from sympy.physics.quantum.state import Ket >>> from sympy.physics.quantum.density import Density >>> from sympy.physics.quantum.operator import Operator >>> A = Operator('A') >>> d = Density([Ket(0), 0.5], [Ket(1),0.5]) >>> d.doit() 0.5*|0><0| + 0.5*|1><1| """ terms = [] for (state, prob) in self.args: state = state.expand() # needed to break up (a+b)*c if (isinstance(state, Add)): for arg in product(state.args, repeat=2): terms.append(prob * self._generate_outer_prod(arg[0], arg[1])) else: terms.append(prob * self._generate_outer_prod(state, state)) return Add(*terms) def _generate_outer_prod(self, arg1, arg2): c_part1, nc_part1 = arg1.args_cnc() c_part2, nc_part2 = arg2.args_cnc() if ( len(nc_part1) == 0 or len(nc_part2) == 0 ): raise ValueError('Atleast one-pair of' ' Non-commutative instance required' ' for outer product.') # Muls of Tensor Products should be expanded # before this function is called if (isinstance(nc_part1[0], TensorProduct) and len(nc_part1) == 1 and len(nc_part2) == 1): op = tensor_product_simp(nc_part1[0] * Dagger(nc_part2[0])) else: op = Mul(*nc_part1) * Dagger(Mul(*nc_part2)) return Mul(*c_part1)*Mul(*c_part2)*op def _represent(self, **options): return represent(self.doit(), **options) def _print_operator_name_latex(self, printer, *args): return printer._print(r'\rho', *args) def _print_operator_name_pretty(self, printer, *args): return prettyForm(unichr('\u03C1')) def _eval_trace(self, **kwargs): indices = kwargs.get('indices', []) return Tr(self.doit(), indices).doit() def entropy(self): """ Compute the entropy of a density matrix. Refer to density.entropy() method for examples. """ return entropy(self) def entropy(density): """Compute the entropy of a matrix/density object. This computes -Tr(density*ln(density)) using the eigenvalue decomposition of density, which is given as either a Density instance or a matrix (numpy.ndarray, sympy.Matrix or scipy.sparse). Parameters ========== density : density matrix of type Density, sympy matrix, scipy.sparse or numpy.ndarray Examples: ======== >>> from sympy.physics.quantum.density import Density, entropy >>> from sympy.physics.quantum.represent import represent >>> from sympy.physics.quantum.matrixutils import scipy_sparse_matrix >>> from sympy.physics.quantum.spin import JzKet, Jz >>> from sympy import S, log >>> up = JzKet(S(1)/2,S(1)/2) >>> down = JzKet(S(1)/2,-S(1)/2) >>> d = Density((up,0.5),(down,0.5)) >>> entropy(d) log(2)/2 """ if isinstance(density, Density): density = represent(density) # represent in Matrix if isinstance(density, scipy_sparse_matrix): density = to_numpy(density) if isinstance(density, Matrix): eigvals = density.eigenvals().keys() return expand(-sum(e*log(e) for e in eigvals)) elif isinstance(density, numpy_ndarray): import numpy as np eigvals = np.linalg.eigvals(density) return -np.sum(eigvals*np.log(eigvals)) else: raise ValueError( "numpy.ndarray, scipy.sparse or sympy matrix expected") def fidelity(state1, state2): """ Computes the fidelity between two quantum states (http://en.wikipedia.org/wiki/Fidelity_of_quantum_states) The arguments provided to this function should be a square matrix or a Density object. If it is a square matrix, it is assumed to be diagonalizable. Parameters: ========== state1, state2 : a density matrix or Matrix Examples: ========= >>> from sympy import S, sqrt >>> from sympy.physics.quantum.dagger import Dagger >>> from sympy.physics.quantum.spin import JzKet >>> from sympy.physics.quantum.density import Density, fidelity >>> from sympy.physics.quantum.represent import represent >>> >>> up = JzKet(S(1)/2,S(1)/2) >>> down = JzKet(S(1)/2,-S(1)/2) >>> amp = 1/sqrt(2) >>> updown = (amp * up) + (amp * down) >>> >>> # represent turns Kets into matrices >>> up_dm = represent(up * Dagger(up)) >>> down_dm = represent(down * Dagger(down)) >>> updown_dm = represent(updown * Dagger(updown)) >>> >>> fidelity(up_dm, up_dm) 1 >>> fidelity(up_dm, down_dm) #orthogonal states 0 >>> fidelity(up_dm, updown_dm).evalf().round(3) 0.707 """ state1 = represent(state1) if isinstance(state1, Density) else state1 state2 = represent(state2) if isinstance(state2, Density) else state2 if (not isinstance(state1, Matrix) or not isinstance(state2, Matrix)): raise ValueError("state1 and state2 must be of type Density or Matrix " "received type=%s for state1 and type=%s for state2" % (type(state1), type(state2))) if ( state1.shape != state2.shape and state1.is_square): raise ValueError("The dimensions of both args should be equal and the " "matrix obtained should be a square matrix") sqrt_state1 = state1**Rational(1, 2) return Tr((sqrt_state1 * state2 * sqrt_state1)**Rational(1, 2)).doit() sympy-0.7.4.1/sympy/physics/quantum/qapply.py0000644000175000017500000001441212253362407021463 0ustar georgeskgeorgesk"""Logic for applying operators to states. Todo: * Sometimes the final result needs to be expanded, we should do this by hand. """ from __future__ import print_function, division 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 #----------------------------------------------------------------------------- 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) sympy-0.7.4.1/sympy/physics/quantum/represent.py0000644000175000017500000004300412253362407022163 0ustar georgeskgeorgesk"""Logic for representing operators in state in various bases. TODO: * Get represent working with continuous hilbert spaces. * Document default basis functionality. """ from __future__ import print_function, division 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) 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 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) Matrix([ [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 def rep_innerproduct(expr, **options): """ Returns an innerproduct like representation (e.g. ````) 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) def rep_expectation(expr, **options): """ Returns an ```` 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()) >>> rep_expectation(XOp(), basis=PxKet()) """ 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) def integrate_result(orig_expr, result, **options): """ Returns the result of integrating over any unities ``(|x>>> 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 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 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]) [> 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(unicode(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.') 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 = u('C') gate_name_latex = u('C') # The values this class controls for. control_value = Integer(1) simplify_cgate=False #------------------------------------------------------------------------- # 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 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 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 def targets(self): """A tuple of target qubits.""" return self.gate.targets @property def controls(self): """A tuple of control qubits.""" return tuple(self.label[0]) @property 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) 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) 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(unicode(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): """ Plot the controlled gate. If *simplify_cgate* is true, simplify C-X and C-Z gates into their more familiar forms. """ 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)) if self.simplify_cgate: if self.gate.gate_name == u('X'): self.gate.plot_gate_plus(circ_plot, gate_idx) elif self.gate.gate_name == u('Z'): circ_plot.control_point(gate_idx, self.targets[0]) else: self.gate.plot_gate(circ_plot, gate_idx) else: 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) class CGateS(CGate): """Version of CGate that allows gate simplifications. I.e. cnot looks like an oplus, cphase has dots, etc. """ simplify_cgate=True 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('U') gate_name_latex = u('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 def targets(self): """A tuple of target qubits.""" return tuple(self.label[0]) #------------------------------------------------------------------------- # Gate methods #------------------------------------------------------------------------- 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(unicode(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]) ) 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) class TwoQubitGate(Gate): """A two qubit unitary gate base class.""" nqubits = Integer(2) #----------------------------------------------------------------------------- # Single Qubit Gates #----------------------------------------------------------------------------- class IdentityGate(OneQubitGate): """The single qubit identity gate. Parameters ---------- target : int The target qubit this gate will apply to. Examples -------- """ gate_name = u('1') gate_name_latex = u('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 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 = u('H') gate_name_latex = u('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]) 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 = u('X') gate_name_latex = u('X') def get_target_matrix(self, format='sympy'): return matrix_cache.get_matrix('X', format) def plot_gate(self, circ_plot, gate_idx): OneQubitGate.plot_gate(self,circ_plot,gate_idx) def plot_gate_plus(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) class YGate(HermitianOperator, OneQubitGate): """The single qubit Y gate. Parameters ---------- target : int The target qubit this gate will apply to. Examples -------- """ gate_name = u('Y') gate_name_latex = u('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) class ZGate(HermitianOperator, OneQubitGate): """The single qubit Z gate. Parameters ---------- target : int The target qubit this gate will apply to. Examples -------- """ gate_name = u('Z') gate_name_latex = u('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) 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 = u('S') gate_name_latex = u('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) 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 = u('T') gate_name_latex = u('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 #----------------------------------------------------------------------------- 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 = u('CNOT') simplify_cgate = True #------------------------------------------------------------------------- # 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 def min_qubits(self): """The minimum number of qubits this gate needs to act on.""" return _max(self.label) + 1 @property def targets(self): """A tuple of target qubits.""" return (self.label[1],) @property def controls(self): """A tuple of control qubits.""" return (self.label[0],) @property 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) 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 = u('SWAP') def get_target_matrix(self, format='sympy'): return matrix_cache.get_matrix('SWAP', format) 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 def CPHASE(a,b): return CGateS((a,),Z(b)) #----------------------------------------------------------------------------- # 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. #----------------------------------------------------------------------------- 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 xrange(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 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 xrange(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 #----------------------------------------------------------------------------- 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 = range(nqubits) result = [] for i in xrange(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) sympy-0.7.4.1/sympy/physics/quantum/operatorset.py0000644000175000017500000002245712253362407022534 0ustar georgeskgeorgesk""" 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 __future__ import print_function, division 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()) 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 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(map(lambda x: _make_default(x), 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 sympy-0.7.4.1/sympy/physics/quantum/piab.py0000644000175000017500000000333412253362407021071 0ustar georgeskgeorgesk"""1D quantum particle in a box.""" from __future__ import print_function, division 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' ] 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 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]) 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 sympy-0.7.4.1/sympy/physics/quantum/spin.py0000644000175000017500000021576412253362407021143 0ustar georgeskgeorgesk"""Quantum mechanical angular momemtum.""" from __future__ import print_function, division from sympy import (Add, binomial, cos, exp, Expr, factorial, I, Integer, Mul, pi, Rational, S, sin, simplify, sqrt, Sum, symbols, sympify, Tuple, Dummy) from sympy.core.compatibility import u, unicode 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' % (unicode(self.name), self._coord) def _print_contents_pretty(self, printer, *args): a = stringPict(unicode(self.name)) b = stringPict(self._coord) return self._print_subscript_pretty(a, b) def _print_contents_latex(self, printer, *args): return r'%s_%s' % ((unicode(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(unicode(self.name)) b = prettyForm(u('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)) 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(u('\u211B') + u(' ')) 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 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 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) def _apply_operator_uncoupled(self, state, ket, **options): a = self.alpha b = self.beta g = self.gamma j = ket.j m = ket.m if j.is_number: s = [] size = m_values(j) sz = size[1] for mp in sz: r = Rotation.D(j, m, mp, a, b, g) z = r.doit() s.append(z * state(j, mp)) return Add(*s) else: if options.pop('dummy', True): mp = Dummy('mp') else: mp = symbols('mp') return Sum(Rotation.D(j, m, mp, a, b, g) * state(j, mp), (mp, -j, j)) def _apply_operator_JxKet(self, ket, **options): return self._apply_operator_uncoupled(JxKet, ket, **options) def _apply_operator_JyKet(self, ket, **options): return self._apply_operator_uncoupled(JyKet, ket, **options) def _apply_operator_JzKet(self, ket, **options): return self._apply_operator_uncoupled(JzKet, ket, **options) def _apply_operator_coupled(self, state, ket, **options): a = self.alpha b = self.beta g = self.gamma j = ket.j m = ket.m jn = ket.jn coupling = ket.coupling if j.is_number: s = [] size = m_values(j) sz = size[1] for mp in sz: r = Rotation.D(j, m, mp, a, b, g) z = r.doit() s.append(z * state(j, mp, jn, coupling)) return Add(*s) else: if options.pop('dummy', True): mp = Dummy('mp') else: mp = symbols('mp') return Sum(Rotation.D(j, m, mp, a, b, g) * state( j, mp, jn, coupling), (mp, -j, j)) def _apply_operator_JxKetCoupled(self, ket, **options): return self._apply_operator_coupled(JxKetCoupled, ket, **options) def _apply_operator_JyKetCoupled(self, ket, **options): return self._apply_operator_coupled(JyKetCoupled, ket, **options) def _apply_operator_JzKetCoupled(self, ket, **options): return self._apply_operator_coupled(JzKetCoupled, ket, **options) 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 :: = \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> #then we do qapply() on each each inner product and sum over them. # OR # Inner product of |i>>> 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) Matrix([ [ 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))) Matrix([ [0], [0], [0], [1], [0], [0], [0], [0], [0]]) >>> represent(TensorProduct(JzKet(1,1),JxKet(1,1)), basis=Jz) Matrix([ [ 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) 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)] for i, ji in enumerate(self.jn, start=1): label.append('j%d=%s' % ( i, 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] for i, ji in enumerate(self.jn, start=1): symb = 'j%d' % i 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] for i, ji in enumerate(self.jn, start=1): label.append('j_{%d}=%s' % (i, 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) 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) 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 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) 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 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) Matrix([ [ 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) 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 #----------------------------------------------------------------------------- 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) 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 sympy-0.7.4.1/sympy/physics/quantum/matrixutils.py0000644000175000017500000002400412253362407022540 0ustar georgeskgeorgesk"""Utilities to deal with sympy.Matrix, numpy and scipy.sparse.""" from __future__ import print_function, division from sympy import Matrix, I, Expr, Integer from sympy.matrices import eye, zeros from sympy.external import import_module __all__ = [ 'numpy_ndarray', 'scipy_sparse_matrix', 'sympy_to_numpy', 'sympy_to_scipy_sparse', 'numpy_to_sympy', 'scipy_sparse_to_sympy', 'flatten_scalar', 'matrix_dagger', 'to_sympy', 'to_numpy', 'to_scipy_sparse', 'matrix_tensor_product', 'matrix_zeros' ] # Conditionally define the base classes for numpy and scipy.sparse arrays # for use in isinstance tests. np = import_module('numpy') if not np: class numpy_ndarray(object): pass else: numpy_ndarray = np.ndarray scipy = import_module('scipy', __import__kwargs={'fromlist': ['sparse']}) if not scipy: class scipy_sparse_matrix(object): pass sparse = None else: sparse = scipy.sparse # Try to find spmatrix. if hasattr(sparse, 'base'): # Newer versions have it under scipy.sparse.base. scipy_sparse_matrix = sparse.base.spmatrix elif hasattr(sparse, 'sparse'): # Older versions have it under scipy.sparse.sparse. scipy_sparse_matrix = sparse.sparse.spmatrix def sympy_to_numpy(m, **options): """Convert a sympy Matrix/complex number to a numpy matrix or scalar.""" if not np: raise ImportError dtype = options.get('dtype', 'complex') if isinstance(m, Matrix): return np.matrix(m.tolist(), dtype=dtype) elif isinstance(m, Expr): if m.is_Number or m.is_NumberSymbol or m == I: return complex(m) raise TypeError('Expected Matrix or complex scalar, got: %r' % m) def sympy_to_scipy_sparse(m, **options): """Convert a sympy Matrix/complex number to a numpy matrix or scalar.""" if not np or not sparse: raise ImportError dtype = options.get('dtype', 'complex') if isinstance(m, Matrix): return sparse.csr_matrix(np.matrix(m.tolist(), dtype=dtype)) elif isinstance(m, Expr): if m.is_Number or m.is_NumberSymbol or m == I: return complex(m) raise TypeError('Expected Matrix or complex scalar, got: %r' % m) def scipy_sparse_to_sympy(m, **options): """Convert a scipy.sparse matrix to a sympy matrix.""" return Matrix(m.todense()) def numpy_to_sympy(m, **options): """Convert a numpy matrix to a sympy matrix.""" return Matrix(m) def to_sympy(m, **options): """Convert a numpy/scipy.sparse matrix to a sympy matrix.""" if isinstance(m, Matrix): return m elif isinstance(m, numpy_ndarray): return numpy_to_sympy(m) elif isinstance(m, scipy_sparse_matrix): return scipy_sparse_to_sympy(m) elif isinstance(m, Expr): return m raise TypeError('Expected sympy/numpy/scipy.sparse matrix, got: %r' % m) def to_numpy(m, **options): """Convert a sympy/scipy.sparse matrix to a numpy matrix.""" dtype = options.get('dtype', 'complex') if isinstance(m, (Matrix, Expr)): return sympy_to_numpy(m, dtype=dtype) elif isinstance(m, numpy_ndarray): return m elif isinstance(m, scipy_sparse_matrix): return m.todense() raise TypeError('Expected sympy/numpy/scipy.sparse matrix, got: %r' % m) def to_scipy_sparse(m, **options): """Convert a sympy/numpy matrix to a scipy.sparse matrix.""" dtype = options.get('dtype', 'complex') if isinstance(m, (Matrix, Expr)): return sympy_to_scipy_sparse(m, dtype=dtype) elif isinstance(m, numpy_ndarray): if not sparse: raise ImportError return sparse.csr_matrix(m) elif isinstance(m, scipy_sparse_matrix): return m raise TypeError('Expected sympy/numpy/scipy.sparse matrix, got: %r' % m) def flatten_scalar(e): """Flatten a 1x1 matrix to a scalar, return larger matrices unchanged.""" if isinstance(e, Matrix): if e.shape == (1, 1): e = e[0] if isinstance(e, (numpy_ndarray, scipy_sparse_matrix)): if e.shape == (1, 1): e = complex(e[0, 0]) return e def matrix_dagger(e): """Return the dagger of a sympy/numpy/scipy.sparse matrix.""" if isinstance(e, Matrix): return e.H elif isinstance(e, (numpy_ndarray, scipy_sparse_matrix)): return e.conjugate().transpose() raise TypeError('Expected sympy/numpy/scipy.sparse matrix, got: %r' % e) # TODO: Move this into sympy.matricies. def _sympy_tensor_product(*matrices): """Compute the tensor product of a sequence of sympy Matrices. This is the standard Kronecker product of matrices [1]. Parameters ========== matrices : tuple of Matrix instances The matrices to take the tensor product of. Returns ======= matrix : Matrix The tensor product matrix. Examples ======== >>> from sympy import I, Matrix, symbols >>> from sympy.physics.quantum.matrixutils import _sympy_tensor_product >>> m1 = Matrix([[1,2],[3,4]]) >>> m2 = Matrix([[1,0],[0,1]]) >>> _sympy_tensor_product(m1, m2) Matrix([ [1, 0, 2, 0], [0, 1, 0, 2], [3, 0, 4, 0], [0, 3, 0, 4]]) >>> _sympy_tensor_product(m2, m1) Matrix([ [1, 2, 0, 0], [3, 4, 0, 0], [0, 0, 1, 2], [0, 0, 3, 4]]) References ========== [1] http://en.wikipedia.org/wiki/Kronecker_product """ # Make sure we have a sequence of Matrices if not all(isinstance(m, Matrix) for m in matrices): raise TypeError( 'Sequence of Matrices expected, got: %s' % repr(matrices) ) # Pull out the first element in the product. matrix_expansion = matrices[-1] # Do the tensor product working from right to left. for mat in reversed(matrices[:-1]): rows = mat.rows cols = mat.cols # Go through each row appending tensor product to. # running matrix_expansion. for i in range(rows): start = matrix_expansion*mat[i*cols] # Go through each column joining each item for j in range(cols - 1): start = start.row_join( matrix_expansion*mat[i*cols + j + 1] ) # If this is the first element, make it the start of the # new row. if i == 0: next = start else: next = next.col_join(start) matrix_expansion = next return matrix_expansion def _numpy_tensor_product(*product): """numpy version of tensor product of multiple arguments.""" if not np: raise ImportError answer = product[0] for item in product[1:]: answer = np.kron(answer, item) return answer def _scipy_sparse_tensor_product(*product): """scipy.sparse version of tensor product of multiple arguments.""" if not sparse: raise ImportError answer = product[0] for item in product[1:]: answer = sparse.kron(answer, item) # The final matrices will just be multiplied, so csr is a good final # sparse format. return sparse.csr_matrix(answer) def matrix_tensor_product(*product): """Compute the matrix tensor product of sympy/numpy/scipy.sparse matrices.""" if isinstance(product[0], Matrix): return _sympy_tensor_product(*product) elif isinstance(product[0], numpy_ndarray): return _numpy_tensor_product(*product) elif isinstance(product[0], scipy_sparse_matrix): return _scipy_sparse_tensor_product(*product) def _numpy_eye(n): """numpy version of complex eye.""" if not np: raise ImportError return np.matrix(np.eye(n, dtype='complex')) def _scipy_sparse_eye(n): """scipy.sparse version of complex eye.""" if not sparse: raise ImportError return sparse.eye(n, n, dtype='complex') def matrix_eye(n, **options): """Get the version of eye and tensor_product for a given format.""" format = options.get('format', 'sympy') if format == 'sympy': return eye(n) elif format == 'numpy': return _numpy_eye(n) elif format == 'scipy.sparse': return _scipy_sparse_eye(n) raise NotImplementedError('Invalid format: %r' % format) def _numpy_zeros(m, n, **options): """numpy verson of zeros.""" dtype = options.get('dtype', 'float64') if not np: raise ImportError return np.zeros((m, n), dtype=dtype) def _scipy_sparse_zeros(m, n, **options): """scipy.sparse verson of zeros.""" spmatrix = options.get('spmatrix', 'csr') dtype = options.get('dtype', 'float64') if not sparse: raise ImportError if spmatrix == 'lil': return sparse.lil_matrix((m, n), dtype=dtype) elif spmatrix == 'csr': return sparse.csr_matrix((m, n), dtype=dtype) def matrix_zeros(m, n, **options): """"Get a zeros matrix for a given format.""" format = options.get('format', 'sympy') dtype = options.get('dtype', 'float64') spmatrix = options.get('spmatrix', 'csr') if format == 'sympy': return zeros(m, n) elif format == 'numpy': return _numpy_zeros(m, n, **options) elif format == 'scipy.sparse': return _scipy_sparse_zeros(m, n, **options) raise NotImplementedError('Invaild format: %r' % format) def _numpy_matrix_to_zero(e): """Convert a numpy zero matrix to the zero scalar.""" if not np: raise ImportError test = np.zeros_like(e) if np.allclose(e, test): return 0.0 else: return e def _scipy_sparse_matrix_to_zero(e): """Convert a scipy.sparse zero matrix to the zero scalar.""" if not np: raise ImportError edense = e.todense() test = np.zeros_like(edense) if np.allclose(edense, test): return 0.0 else: return e def matrix_to_zero(e): """Convert a zero matrix to the scalar zero.""" if isinstance(e, Matrix): if zeros(*e.shape) == e: e = Integer(0) elif isinstance(e, numpy_ndarray): e = _numpy_matrix_to_zero(e) elif isinstance(e, scipy_sparse_matrix): e = _scipy_sparse_matrix_to_zero(e) return e sympy-0.7.4.1/sympy/physics/quantum/matrixcache.py0000644000175000017500000000667712253362407022463 0ustar georgeskgeorgesk"""A cache for storing small matrices in multiple formats.""" from __future__ import print_function, division from sympy import Matrix, I, Pow, Rational, exp, pi from sympy.physics.quantum.matrixutils import ( to_sympy, to_numpy, to_scipy_sparse ) class MatrixCache(object): """A cache for small matrices in different formats. This class takes small matrices in the standard ``sympy.Matrix`` format, and then converts these to both ``numpy.matrix`` and ``scipy.sparse.csr_matrix`` matrices. These matrices are then stored for future recovery. """ def __init__(self, dtype='complex'): self._cache = {} self.dtype = dtype def cache_matrix(self, name, m): """Cache a matrix by its name. Parameters ---------- name : str A descriptive name for the matrix, like "identity2". m : list of lists The raw matrix data as a sympy Matrix. """ try: self._sympy_matrix(name, m) except ImportError: pass try: self._numpy_matrix(name, m) except ImportError: pass try: self._scipy_sparse_matrix(name, m) except ImportError: pass def get_matrix(self, name, format): """Get a cached matrix by name and format. Parameters ---------- name : str A descriptive name for the matrix, like "identity2". format : str The format desired ('sympy', 'numpy', 'scipy.sparse') """ m = self._cache.get((name, format)) if m is not None: return m raise NotImplementedError( 'Matrix with name %s and format %s is not available.' % (name, format) ) def _store_matrix(self, name, format, m): self._cache[(name, format)] = m def _sympy_matrix(self, name, m): self._store_matrix(name, 'sympy', to_sympy(m)) def _numpy_matrix(self, name, m): m = to_numpy(m, dtype=self.dtype) self._store_matrix(name, 'numpy', m) def _scipy_sparse_matrix(self, name, m): # TODO: explore different sparse formats. But sparse.kron will use # coo in most cases, so we use that here. m = to_scipy_sparse(m, dtype=self.dtype) self._store_matrix(name, 'scipy.sparse', m) sqrt2_inv = Pow(2, Rational(-1, 2), evaluate=False) # Save the common matrices that we will need matrix_cache = MatrixCache() matrix_cache.cache_matrix('eye2', Matrix([[1, 0], [0, 1]])) matrix_cache.cache_matrix('op11', Matrix([[0, 0], [0, 1]])) # |1><1| matrix_cache.cache_matrix('op00', Matrix([[1, 0], [0, 0]])) # |0><0| matrix_cache.cache_matrix('op10', Matrix([[0, 0], [1, 0]])) # |1><0| matrix_cache.cache_matrix('op01', Matrix([[0, 1], [0, 0]])) # |0><1| matrix_cache.cache_matrix('X', Matrix([[0, 1], [1, 0]])) matrix_cache.cache_matrix('Y', Matrix([[0, -I], [I, 0]])) matrix_cache.cache_matrix('Z', Matrix([[1, 0], [0, -1]])) matrix_cache.cache_matrix('S', Matrix([[1, 0], [0, I]])) matrix_cache.cache_matrix('T', Matrix([[1, 0], [0, exp(I*pi/4)]])) matrix_cache.cache_matrix('H', sqrt2_inv*Matrix([[1, 1], [1, -1]])) matrix_cache.cache_matrix('Hsqrt2', Matrix([[1, 1], [1, -1]])) matrix_cache.cache_matrix( 'SWAP', Matrix([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]])) matrix_cache.cache_matrix('ZX', sqrt2_inv*Matrix([[1, 1], [1, -1]])) matrix_cache.cache_matrix('ZY', Matrix([[I, 0], [0, -I]])) sympy-0.7.4.1/sympy/physics/quantum/innerproduct.py0000644000175000017500000001024512253362407022671 0ustar georgeskgeorgesk"""Symbolic inner product.""" from __future__ import print_function, division 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. 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 >>> ip.bra >> ip.ket |k> In simple products of kets and bras inner products will be automatically identified and created:: >>> b*k But in more complex expressions, there is ambiguity in whether inner or outer products should be created:: >>> k*b*k*b |k>*>> k*(b*k)*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 sympy-0.7.4.1/sympy/physics/quantum/sho1d.py0000644000175000017500000005121512253362407021175 0ustar georgeskgeorgesk"""Simple Harmonic Oscillator 1-Dimension""" from __future__ import print_function, division from sympy import sqrt, I, Symbol, Integer, S from sympy.core.compatibility import u from sympy.physics.quantum.constants import hbar from sympy.physics.quantum.operator import Operator from sympy.physics.quantum.state import Bra, Ket, State from sympy.physics.quantum.qexpr import QExpr from sympy.physics.quantum.cartesian import X, Px from sympy.functions.special.tensor_functions import KroneckerDelta from sympy.physics.quantum.hilbert import ComplexSpace from sympy.physics.quantum.matrixutils import matrix_zeros from sympy.physics.quantum.represent import represent #------------------------------------------------------------------------------ class SHOOp(Operator): """A base class for the SHO Operators. We are limiting the number of arguments to be 1. """ @classmethod def _eval_args(cls, args): args = QExpr._eval_args(args) if len(args) == 1: return args else: raise ValueError("Too many arguments") @classmethod def _eval_hilbert_space(cls, label): return ComplexSpace(S.Infinity) class RaisingOp(SHOOp): """The Raising Operator or a^dagger. When a^dagger acts on a state it raises the state up by one. Taking the adjoint of a^dagger returns 'a', the Lowering Operator. a^dagger can be rewritten in terms of position and momentum. We can represent a^dagger as a matrix, which will be its default basis. Parameters ========== args : tuple The list of numbers or parameters that uniquely specify the operator. Examples ======== Create a Raising Operator and rewrite it in terms of position and momentum, and show that taking its adjoint returns 'a': >>> from sympy.physics.quantum.sho1d import RaisingOp >>> from sympy.physics.quantum import Dagger >>> ad = RaisingOp('a') >>> ad.rewrite('xp').doit() sqrt(2)*(m*omega*X - I*Px)/(2*sqrt(hbar)*sqrt(m*omega)) >>> Dagger(ad) a Taking the commutator of a^dagger with other Operators: >>> from sympy.physics.quantum import Commutator >>> from sympy.physics.quantum.sho1d import RaisingOp, LoweringOp >>> from sympy.physics.quantum.sho1d import NumberOp >>> ad = RaisingOp('a') >>> a = LoweringOp('a') >>> N = NumberOp('N') >>> Commutator(ad, a).doit() -1 >>> Commutator(ad, N).doit() -RaisingOp(a) Apply a^dagger to a state: >>> from sympy.physics.quantum import qapply >>> from sympy.physics.quantum.sho1d import RaisingOp, SHOKet >>> ad = RaisingOp('a') >>> k = SHOKet('k') >>> qapply(ad*k) sqrt(k + 1)*|k + 1> Matrix Representation >>> from sympy.physics.quantum.sho1d import RaisingOp >>> from sympy.physics.quantum.represent import represent >>> ad = RaisingOp('a') >>> represent(ad, basis=N, ndim=4, format='sympy') Matrix([ [0, 0, 0, 0], [1, 0, 0, 0], [0, sqrt(2), 0, 0], [0, 0, sqrt(3), 0]]) """ def _eval_rewrite_as_xp(self, *args): return (Integer(1)/sqrt(Integer(2)*hbar*m*omega))*( Integer(-1)*I*Px + m*omega*X) def _eval_adjoint(self): return LoweringOp(*self.args) def _eval_commutator_LoweringOp(self, other): return Integer(-1) def _eval_commutator_NumberOp(self, other): return Integer(-1)*self def _apply_operator_SHOKet(self, ket): temp = ket.n + Integer(1) return sqrt(temp)*SHOKet(temp) def _represent_default_basis(self, **options): return self._represent_NumberOp(None, **options) def _represent_XOp(self, basis, **options): # This logic is good but the underlying position # representation logic is broken. # temp = self.rewrite('xp').doit() # result = represent(temp, basis=X) # return result raise NotImplementedError('Position representation is not implemented') def _represent_NumberOp(self, basis, **options): ndim_info = options.get('ndim', 4) format = options.get('format','sympy') spmatrix = options.get('spmatrix', 'csr') matrix = matrix_zeros(ndim_info, ndim_info, **options) for i in range(ndim_info - 1): value = sqrt(i + 1) if format == 'scipy.sparse': value = float(value) matrix[i + 1, i] = value if format == 'scipy.sparse': matrix = matrix.tocsr() return matrix #-------------------------------------------------------------------------- # Printing Methods #-------------------------------------------------------------------------- def _print_contents(self, printer, *args): arg0 = printer._print(self.args[0], *args) return '%s(%s)' % (self.__class__.__name__, arg0) def _print_contents_pretty(self, printer, *args): from sympy.printing.pretty.stringpict import prettyForm pform = printer._print(self.args[0], *args) pform = pform**prettyForm(u('\u2020')) return pform def _print_contents_latex(self, printer, *args): arg = printer._print(self.args[0]) return '%s^{\\dag}' % arg class LoweringOp(SHOOp): """The Lowering Operator or 'a'. When 'a' acts on a state it lowers the state up by one. Taking the adjoint of 'a' returns a^dagger, the Raising Operator. 'a' can be rewritten in terms of position and momentum. We can represent 'a' as a matrix, which will be its default basis. Parameters ========== args : tuple The list of numbers or parameters that uniquely specify the operator. Examples ======== Create a Lowering Operator and rewrite it in terms of position and momentum, and show that taking its adjoint returns a^dagger: >>> from sympy.physics.quantum.sho1d import LoweringOp >>> from sympy.physics.quantum import Dagger >>> a = LoweringOp('a') >>> a.rewrite('xp').doit() sqrt(2)*(m*omega*X + I*Px)/(2*sqrt(hbar)*sqrt(m*omega)) >>> Dagger(a) RaisingOp(a) Taking the commutator of 'a' with other Operators: >>> from sympy.physics.quantum import Commutator >>> from sympy.physics.quantum.sho1d import LoweringOp, RaisingOp >>> from sympy.physics.quantum.sho1d import NumberOp >>> a = LoweringOp('a') >>> ad = RaisingOp('a') >>> N = NumberOp('N') >>> Commutator(a, ad).doit() 1 >>> Commutator(a, N).doit() a Apply 'a' to a state: >>> from sympy.physics.quantum import qapply >>> from sympy.physics.quantum.sho1d import LoweringOp, SHOKet >>> a = LoweringOp('a') >>> k = SHOKet('k') >>> qapply(a*k) sqrt(k)*|k - 1> Taking 'a' of the lowest state will return 0: >>> from sympy.physics.quantum import qapply >>> from sympy.physics.quantum.sho1d import LoweringOp, SHOKet >>> a = LoweringOp('a') >>> k = SHOKet(0) >>> qapply(a*k) 0 Matrix Representation >>> from sympy.physics.quantum.sho1d import LoweringOp >>> from sympy.physics.quantum.represent import represent >>> a = LoweringOp('a') >>> represent(a, basis=N, ndim=4, format='sympy') Matrix([ [0, 1, 0, 0], [0, 0, sqrt(2), 0], [0, 0, 0, sqrt(3)], [0, 0, 0, 0]]) """ def _eval_rewrite_as_xp(self, *args): return (Integer(1)/sqrt(Integer(2)*hbar*m*omega))*( I*Px + m*omega*X) def _eval_adjoint(self): return RaisingOp(*self.args) def _eval_commutator_RaisingOp(self, other): return Integer(1) def _eval_commutator_NumberOp(self, other): return Integer(1)*self def _apply_operator_SHOKet(self, ket): temp = ket.n - Integer(1) if ket.n == Integer(0): return Integer(0) else: return sqrt(ket.n)*SHOKet(temp) def _represent_default_basis(self, **options): return self._represent_NumberOp(None, **options) def _represent_XOp(self, basis, **options): # This logic is good but the underlying position # representation logic is broken. # temp = self.rewrite('xp').doit() # result = represent(temp, basis=X) # return result raise NotImplementedError('Position representation is not implemented') def _represent_NumberOp(self, basis, **options): ndim_info = options.get('ndim', 4) format = options.get('format', 'sympy') spmatrix = options.get('spmatrix', 'csr') matrix = matrix_zeros(ndim_info, ndim_info, **options) for i in range(ndim_info - 1): value = sqrt(i + 1) if format == 'scipy.sparse': value = float(value) matrix[i,i + 1] = value if format == 'scipy.sparse': matrix = matrix.tocsr() return matrix class NumberOp(SHOOp): """The Number Operator is simply a^dagger*a It is often useful to write a^dagger*a as simply the Number Operator because the Number Operator commutes with the Hamiltonian. And can be expressed using the Number Operator. Also the Number Operator can be applied to states. We can represent the Number Operator as a matrix, which will be its default basis. Parameters ========== args : tuple The list of numbers or parameters that uniquely specify the operator. Examples ======== Create a Number Operator and rewrite it in terms of the ladder operators, position and momentum operators, and Hamiltonian: >>> from sympy.physics.quantum.sho1d import NumberOp >>> N = NumberOp('N') >>> N.rewrite('a').doit() RaisingOp(a)*a >>> N.rewrite('xp').doit() -1/2 + (m**2*omega**2*X**2 + Px**2)/(2*hbar*m*omega) >>> N.rewrite('H').doit() -1/2 + H/(hbar*omega) Take the Commutator of the Number Operator with other Operators: >>> from sympy.physics.quantum import Commutator >>> from sympy.physics.quantum.sho1d import NumberOp, Hamiltonian >>> from sympy.physics.quantum.sho1d import RaisingOp, LoweringOp >>> N = NumberOp('N') >>> H = Hamiltonian('H') >>> ad = RaisingOp('a') >>> a = LoweringOp('a') >>> Commutator(N,H).doit() 0 >>> Commutator(N,ad).doit() RaisingOp(a) >>> Commutator(N,a).doit() -a Apply the Number Operator to a state: >>> from sympy.physics.quantum import qapply >>> from sympy.physics.quantum.sho1d import NumberOp, SHOKet >>> N = NumberOp('N') >>> k = SHOKet('k') >>> qapply(N*k) k*|k> Matrix Representation >>> from sympy.physics.quantum.sho1d import NumberOp >>> from sympy.physics.quantum.represent import represent >>> N = NumberOp('N') >>> represent(N, basis=N, ndim=4, format='sympy') Matrix([ [0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 2, 0], [0, 0, 0, 3]]) """ def _eval_rewrite_as_a(self, *args): return ad*a def _eval_rewrite_as_xp(self, *args): return (Integer(1)/(Integer(2)*m*hbar*omega))*(Px**2 + ( m*omega*X)**2) - Integer(1)/Integer(2) def _eval_rewrite_as_H(self, *args): return H/(hbar*omega) - Integer(1)/Integer(2) def _apply_operator_SHOKet(self, ket): return ket.n*ket def _eval_commutator_Hamiltonian(self, other): return Integer(0) def _eval_commutator_RaisingOp(self, other): return other def _eval_commutator_LoweringOp(self, other): return Integer(-1)*other def _represent_default_basis(self, **options): return self._represent_NumberOp(None, **options) def _represent_XOp(self, basis, **options): # This logic is good but the underlying position # representation logic is broken. # temp = self.rewrite('xp').doit() # result = represent(temp, basis=X) # return result raise NotImplementedError('Position representation is not implemented') def _represent_NumberOp(self, basis, **options): ndim_info = options.get('ndim', 4) format = options.get('format', 'sympy') spmatrix = options.get('spmatrix', 'csr') matrix = matrix_zeros(ndim_info, ndim_info, **options) for i in range(ndim_info): value = i if format == 'scipy.sparse': value = float(value) matrix[i,i] = value if format == 'scipy.sparse': matrix = matrix.tocsr() return matrix class Hamiltonian(SHOOp): """The Hamiltonian Operator. The Hamiltonian is used to solve the time-independent Schrodinger equation. The Hamiltonian can be expressed using the ladder operators, as well as by position and momentum. We can represent the Hamiltonian Operator as a matrix, which will be its default basis. Parameters ========== args : tuple The list of numbers or parameters that uniquely specify the operator. Examples ======== Create a Hamiltonian Operator and rewrite it in terms of the ladder operators, position and momentum, and the Number Operator: >>> from sympy.physics.quantum.sho1d import Hamiltonian >>> H = Hamiltonian('H') >>> H.rewrite('a').doit() hbar*omega*(1/2 + RaisingOp(a)*a) >>> H.rewrite('xp').doit() (m**2*omega**2*X**2 + Px**2)/(2*m) >>> H.rewrite('N').doit() hbar*omega*(1/2 + N) Take the Commutator of the Hamiltonian and the Number Operator: >>> from sympy.physics.quantum import Commutator >>> from sympy.physics.quantum.sho1d import Hamiltonian, NumberOp >>> H = Hamiltonian('H') >>> N = NumberOp('N') >>> Commutator(H,N).doit() 0 Apply the Hamiltonian Operator to a state: >>> from sympy.physics.quantum import qapply >>> from sympy.physics.quantum.sho1d import Hamiltonian, SHOKet >>> H = Hamiltonian('H') >>> k = SHOKet('k') >>> qapply(H*k) hbar*k*omega*|k> + hbar*omega*|k>/2 Matrix Representation >>> from sympy.physics.quantum.sho1d import Hamiltonian >>> from sympy.physics.quantum.represent import represent >>> H = Hamiltonian('H') >>> represent(H, basis=N, ndim=4, format='sympy') Matrix([ [hbar*omega/2, 0, 0, 0], [ 0, 3*hbar*omega/2, 0, 0], [ 0, 0, 5*hbar*omega/2, 0], [ 0, 0, 0, 7*hbar*omega/2]]) """ def _eval_rewrite_as_a(self, *args): return hbar*omega*(ad*a + Integer(1)/Integer(2)) def _eval_rewrite_as_xp(self, *args): return (Integer(1)/(Integer(2)*m))*(Px**2 + (m*omega*X)**2) def _eval_rewrite_as_N(self, *args): return hbar*omega*(N + Integer(1)/Integer(2)) def _apply_operator_SHOKet(self, ket): return (hbar*omega*(ket.n + Integer(1)/Integer(2)))*ket def _eval_commutator_NumberOp(self, other): return Integer(0) def _represent_default_basis(self, **options): return self._represent_NumberOp(None, **options) def _represent_XOp(self, basis, **options): # This logic is good but the underlying position # representation logic is broken. # temp = self.rewrite('xp').doit() # result = represent(temp, basis=X) # return result raise NotImplementedError('Position representation is not implemented') def _represent_NumberOp(self, basis, **options): ndim_info = options.get('ndim', 4) format = options.get('format', 'sympy') spmatrix = options.get('spmatrix', 'csr') matrix = matrix_zeros(ndim_info, ndim_info, **options) for i in range(ndim_info): value = i + Integer(1)/Integer(2) if format == 'scipy.sparse': value = float(value) matrix[i,i] = value if format == 'scipy.sparse': matrix = matrix.tocsr() return hbar*omega*matrix #------------------------------------------------------------------------------ class SHOState(State): """State class for SHO states""" @classmethod def _eval_hilbert_space(cls, label): return ComplexSpace(S.Infinity) @property def n(self): return self.args[0] class SHOKet(SHOState, Ket): """1D eigenket. Inherits from SHOState and Ket. Parameters ========== args : tuple The list of numbers or parameters that uniquely specify the ket This is usually its quantum numbers or its symbol. Examples ======== Ket's know about their associated bra: >>> from sympy.physics.quantum.sho1d import SHOKet >>> k = SHOKet('k') >>> k.dual >> k.dual_class() Take the Inner Product with a bra: >>> from sympy.physics.quantum import InnerProduct >>> from sympy.physics.quantum.sho1d import SHOKet, SHOBra >>> k = SHOKet('k') >>> b = SHOBra('b') >>> InnerProduct(b,k).doit() KroneckerDelta(b, k) Vector representation of a numerical state ket: >>> from sympy.physics.quantum.sho1d import SHOKet, NumberOp >>> from sympy.physics.quantum.represent import represent >>> k = SHOKet(3) >>> N = NumberOp('N') >>> represent(k, basis=N, ndim=4) Matrix([ [0], [0], [0], [1]]) """ @classmethod def dual_class(self): return SHOBra def _eval_innerproduct_SHOBra(self, bra, **hints): result = KroneckerDelta(self.n, bra.n) return result def _represent_default_basis(self, **options): return self._represent_NumberOp(None, **options) def _represent_NumberOp(self, basis, **options): ndim_info = options.get('ndim', 4) format = options.get('format', 'sympy') options['spmatrix'] = 'lil' vector = matrix_zeros(ndim_info, 1, **options) if isinstance(self.n, Integer): if self.n >= ndim_info: return ValueError("N-Dimension too small") value = Integer(1) if format == 'scipy.sparse': vector[int(self.n), 0] = 1.0 vector = vector.tocsr() elif format == 'numpy': vector[int(self.n), 0] = 1.0 else: vector[self.n, 0] = Integer(1) return vector else: return ValueError("Not Numerical State") class SHOBra(SHOState, Bra): """A time-independent Bra in SHO. Inherits from SHOState and Bra. Parameters ========== args : tuple The list of numbers or parameters that uniquely specify the ket This is usually its quantum numbers or its symbol. Examples ======== Bra's know about their associated ket: >>> from sympy.physics.quantum.sho1d import SHOBra >>> b = SHOBra('b') >>> b.dual |b> >>> b.dual_class() Vector representation of a numerical state bra: >>> from sympy.physics.quantum.sho1d import SHOBra, NumberOp >>> from sympy.physics.quantum.represent import represent >>> b = SHOBra(3) >>> N = NumberOp('N') >>> represent(b, basis=N, ndim=4) Matrix([[0, 0, 0, 1]]) """ @classmethod def dual_class(self): return SHOKet def _represent_default_basis(self, **options): return self._represent_NumberOp(None, **options) def _represent_NumberOp(self, basis, **options): ndim_info = options.get('ndim', 4) format = options.get('format', 'sympy') options['spmatrix'] = 'lil' vector = matrix_zeros(1, ndim_info, **options) if isinstance(self.n, Integer): if self.n >= ndim_info: return ValueError("N-Dimension too small") if format == 'scipy.sparse': vector[0, int(self.n)] = 1.0 vector = vector.tocsr() elif format == 'numpy': vector[0, int(self.n)] = 1.0 else: vector[0, self.n] = Integer(1) return vector else: return ValueError("Not Numerical State") ad = RaisingOp('a') a = LoweringOp('a') H = Hamiltonian('H') N = NumberOp('N') omega = Symbol('omega') m = Symbol('m') sympy-0.7.4.1/sympy/physics/quantum/commutator.py0000644000175000017500000001471312253362407022353 0ustar georgeskgeorgesk"""The commutator: [A,B] = A*B - B*A.""" from __future__ import print_function, division from sympy import S, Expr, Mul, Add from sympy.core.compatibility import u from sympy.printing.pretty.stringpict import prettyForm from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.operator import Operator __all__ = [ 'Commutator' ] #----------------------------------------------------------------------------- # Commutator #----------------------------------------------------------------------------- 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 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(u(','))))) 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]) sympy-0.7.4.1/sympy/physics/quantum/anticommutator.py0000644000175000017500000001047312253362407023226 0ustar georgeskgeorgesk"""The anti-commutator: ``{A,B} = A*B + B*A``.""" from __future__ import print_function, division from sympy import S, Expr, Mul, Integer from sympy.core.compatibility import u 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 #----------------------------------------------------------------------------- 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) 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(u(','))))) 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]) sympy-0.7.4.1/sympy/physics/quantum/constants.py0000644000175000017500000000273412253362407022175 0ustar georgeskgeorgesk"""Constants (like hbar) related to quantum mechanics.""" from __future__ import print_function, division from sympy.core.numbers import NumberSymbol from sympy.core.singleton import Singleton from sympy.core.compatibility import u, with_metaclass from sympy.printing.pretty.stringpict import prettyForm import sympy.mpmath.libmp as mlib #----------------------------------------------------------------------------- # Constants #----------------------------------------------------------------------------- __all__ = [ 'hbar' ] class HBar(with_metaclass(Singleton, NumberSymbol)): """Reduced Plank's constant in numerical and symbolic form [1]_. Examples ======== >>> from sympy.physics.quantum.constants import hbar >>> hbar.evalf() 1.05457162000000e-34 References ========== .. [1] http://en.wikipedia.org/wiki/Planck_constant """ is_real = True is_positive = True is_negative = False is_irrational = True __slots__ = [] def _as_mpf_val(self, prec): return mlib.from_float(1.05457162e-34, prec) def _sympyrepr(self, printer, *args): return 'HBar()' def _sympystr(self, printer, *args): return 'hbar' def _pretty(self, printer, *args): if printer._use_unicode: return prettyForm(u('\u210f')) return prettyForm('hbar') def _latex(self, printer, *args): return r'\hbar' # Create an instance for everyone to use. hbar = HBar() sympy-0.7.4.1/sympy/physics/quantum/qft.py0000644000175000017500000001424112253362407020747 0ustar georgeskgeorgesk"""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 __future__ import print_function, division from sympy import Expr, Matrix, exp, I, pi, Integer, Symbol from sympy.core.compatibility import u 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 #----------------------------------------------------------------------------- class RkGate(OneQubitGate): """This is the R_k gate of the QTF.""" gate_name = u('Rk') gate_name_latex = u('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 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') class QFT(Fourier): """The forward quantum Fourier transform.""" gate_name = u('QFT') gate_name_latex = u('QFT') def decompose(self): """Decomposes QFT into elementary gates.""" start = self.label[0] finish = self.label[1] circuit = 1 for level in reversed(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) class IQFT(Fourier): """The inverse quantum Fourier transform.""" gate_name = u('IQFT') gate_name_latex = u('{QFT^{-1}}') 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(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) sympy-0.7.4.1/sympy/physics/quantum/cartesian.py0000644000175000017500000002102312253362407022122 0ustar georgeskgeorgesk"""Operators and states for 1D cartesian position and momentum. TODO: * Add 3D classes to mappings in operatorset.py """ from __future__ import print_function, division 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 #------------------------------------------------------------------------- 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) 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 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 #------------------------------------------------------------------------- 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 #------------------------------------------------------------------------- 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 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) class XBra(Bra): """1D cartesian position eigenbra.""" @classmethod def default_args(self): return ("x",) @classmethod def dual_class(self): return XKet @property def position(self): """The position of the state.""" return self.label[0] 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 def position_x(self): """ The x coordinate of the state """ return self.label[0] @property def position_y(self): """ The y coordinate of the state """ return self.label[1] @property def position_z(self): """ The z coordinate of the state """ return self.label[2] 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 class PositionBra3D(Bra, PositionState3D): """ 3D cartesian position eigenbra """ @classmethod def dual_class(self): return PositionKet3D #------------------------------------------------------------------------- # Momentum eigenstates #------------------------------------------------------------------------- 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 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) class PxBra(Bra): """1D cartesian momentum eigenbra.""" @classmethod def default_args(self): return ("px",) @classmethod def dual_class(self): return PxKet @property 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 sympy-0.7.4.1/sympy/physics/quantum/tests/0000755000175000017500000000000012253362407020743 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/physics/quantum/tests/test_gate.py0000644000175000017500000002654512253362407023310 0ustar georgeskgeorgeskfrom sympy import exp, symbols, sqrt, I, pi, Mul, Integer, Wild from sympy.matrices import Matrix from sympy.physics.quantum.gate import (XGate, YGate, ZGate, random_circuit, CNOT, IdentityGate, H, X, Y, S, T, Z, SwapGate, gate_simp, gate_sort, CNotGate, TGate, HadamardGate, PhaseGate, UGate, CGate) from sympy.physics.quantum.commutator import Commutator from sympy.physics.quantum.anticommutator import AntiCommutator from sympy.physics.quantum.represent import represent from sympy.physics.quantum.qapply import qapply from sympy.physics.quantum.qubit import Qubit, IntQubit, qubit_to_matrix, \ matrix_to_qubit from sympy.physics.quantum.matrixutils import matrix_to_zero from sympy.physics.quantum.matrixcache import sqrt2_inv from sympy.physics.quantum import Dagger def test_gate(): """Test a basic gate.""" h = HadamardGate(1) assert h.min_qubits == 2 assert h.nqubits == 1 i0 = Wild('i0') i1 = Wild('i1') h0_w1 = HadamardGate(i0) h0_w2 = HadamardGate(i0) h1_w1 = HadamardGate(i1) assert h0_w1 == h0_w2 assert h0_w1 != h1_w1 assert h1_w1 != h0_w2 cnot_10_w1 = CNOT(i1, i0) cnot_10_w2 = CNOT(i1, i0) cnot_01_w1 = CNOT(i0, i1) assert cnot_10_w1 == cnot_10_w2 assert cnot_10_w1 != cnot_01_w1 assert cnot_10_w2 != cnot_01_w1 def test_UGate(): a, b, c, d = symbols('a,b,c,d') uMat = Matrix([[a, b], [c, d]]) # Test basic case where gate exists in 1-qubit space u1 = UGate((0,), uMat) assert represent(u1, nqubits=1) == uMat assert qapply(u1*Qubit('0')) == a*Qubit('0') + c*Qubit('1') assert qapply(u1*Qubit('1')) == b*Qubit('0') + d*Qubit('1') # Test case where gate exists in a larger space u2 = UGate((1,), uMat) u2Rep = represent(u2, nqubits=2) for i in range(4): assert u2Rep*qubit_to_matrix(IntQubit(i, 2)) == \ qubit_to_matrix(qapply(u2*IntQubit(i, 2))) def test_cgate(): """Test the general CGate.""" # Test single control functionality CNOTMatrix = Matrix( [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]) assert represent(CGate(1, XGate(0)), nqubits=2) == CNOTMatrix # Test multiple control bit functionality ToffoliGate = CGate((1, 2), XGate(0)) assert represent(ToffoliGate, nqubits=3) == \ Matrix( [[1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0, 1, 0]]) ToffoliGate = CGate((3, 0), XGate(1)) assert qapply(ToffoliGate*Qubit('1001')) == \ matrix_to_qubit(represent(ToffoliGate*Qubit('1001'), nqubits=4)) assert qapply(ToffoliGate*Qubit('0000')) == \ matrix_to_qubit(represent(ToffoliGate*Qubit('0000'), nqubits=4)) CYGate = CGate(1, YGate(0)) CYGate_matrix = Matrix( ((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 0, -I), (0, 0, I, 0))) # Test 2 qubit controlled-Y gate decompose method. assert represent(CYGate.decompose(), nqubits=2) == CYGate_matrix CZGate = CGate(0, ZGate(1)) CZGate_matrix = Matrix( ((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, -1))) assert qapply(CZGate*Qubit('11')) == -Qubit('11') assert matrix_to_qubit(represent(CZGate*Qubit('11'), nqubits=2)) == \ -Qubit('11') # Test 2 qubit controlled-Z gate decompose method. assert represent(CZGate.decompose(), nqubits=2) == CZGate_matrix CPhaseGate = CGate(0, PhaseGate(1)) assert qapply(CPhaseGate*Qubit('11')) == \ I*Qubit('11') assert matrix_to_qubit(represent(CPhaseGate*Qubit('11'), nqubits=2)) == \ I*Qubit('11') # Test that the dagger, inverse, and power of CGate is evaluated properly assert Dagger(CZGate) == CZGate assert pow(CZGate, 1) == Dagger(CZGate) assert Dagger(CZGate) == CZGate.inverse() assert Dagger(CPhaseGate) != CPhaseGate assert Dagger(CPhaseGate) == CPhaseGate.inverse() assert Dagger(CPhaseGate) == pow(CPhaseGate, -1) assert pow(CPhaseGate, -1) == CPhaseGate.inverse() def test_UGate_CGate_combo(): a, b, c, d = symbols('a,b,c,d') uMat = Matrix([[a, b], [c, d]]) cMat = Matrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, a, b], [0, 0, c, d]]) # Test basic case where gate exists in 1-qubit space. u1 = UGate((0,), uMat) cu1 = CGate(1, u1) assert represent(cu1, nqubits=2) == cMat assert qapply(cu1*Qubit('10')) == a*Qubit('10') + c*Qubit('11') assert qapply(cu1*Qubit('11')) == b*Qubit('10') + d*Qubit('11') assert qapply(cu1*Qubit('01')) == Qubit('01') assert qapply(cu1*Qubit('00')) == Qubit('00') # Test case where gate exists in a larger space. u2 = UGate((1,), uMat) u2Rep = represent(u2, nqubits=2) for i in range(4): assert u2Rep*qubit_to_matrix(IntQubit(i, 2)) == \ qubit_to_matrix(qapply(u2*IntQubit(i, 2))) def test_represent_hadamard(): """Test the representation of the hadamard gate.""" circuit = HadamardGate(0)*Qubit('00') answer = represent(circuit, nqubits=2) # Check that the answers are same to within an epsilon. assert answer == Matrix([sqrt2_inv, sqrt2_inv, 0, 0]) def test_represent_xgate(): """Test the representation of the X gate.""" circuit = XGate(0)*Qubit('00') answer = represent(circuit, nqubits=2) assert Matrix([0, 1, 0, 0]) == answer def test_represent_ygate(): """Test the representation of the Y gate.""" circuit = YGate(0)*Qubit('00') answer = represent(circuit, nqubits=2) assert answer[0] == 0 and answer[1] == I and \ answer[2] == 0 and answer[3] == 0 def test_represent_zgate(): """Test the representation of the Z gate.""" circuit = ZGate(0)*Qubit('00') answer = represent(circuit, nqubits=2) assert Matrix([1, 0, 0, 0]) == answer def test_represent_phasegate(): """Test the representation of the S gate.""" circuit = PhaseGate(0)*Qubit('01') answer = represent(circuit, nqubits=2) assert Matrix([0, I, 0, 0]) == answer def test_represent_tgate(): """Test the representation of the T gate.""" circuit = TGate(0)*Qubit('01') assert Matrix([0, exp(I*pi/4), 0, 0]) == represent(circuit, nqubits=2) def test_compound_gates(): """Test a compound gate representation.""" circuit = YGate(0)*ZGate(0)*XGate(0)*HadamardGate(0)*Qubit('00') answer = represent(circuit, nqubits=2) assert Matrix([I/sqrt(2), I/sqrt(2), 0, 0]) == answer def test_cnot_gate(): """Test the CNOT gate.""" circuit = CNotGate(1, 0) assert represent(circuit, nqubits=2) == \ Matrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]) circuit = circuit*Qubit('111') assert matrix_to_qubit(represent(circuit, nqubits=3)) == \ qapply(circuit) circuit = CNotGate(1, 0) assert Dagger(circuit) == circuit assert Dagger(Dagger(circuit)) == circuit assert circuit*circuit == 1 def test_gate_sort(): """Test gate_sort.""" for g in (X, Y, Z, H, S, T): assert gate_sort(g(2)*g(1)*g(0)) == g(0)*g(1)*g(2) e = gate_sort(X(1)*H(0)**2*CNOT(0, 1)*X(1)*X(0)) assert e == H(0)**2*CNOT(0, 1)*X(0)*X(1)**2 assert gate_sort(Z(0)*X(0)) == -X(0)*Z(0) assert gate_sort(Z(0)*X(0)**2) == X(0)**2*Z(0) assert gate_sort(Y(0)*H(0)) == -H(0)*Y(0) assert gate_sort(Y(0)*X(0)) == -X(0)*Y(0) assert gate_sort(Z(0)*Y(0)) == -Y(0)*Z(0) assert gate_sort(T(0)*S(0)) == S(0)*T(0) assert gate_sort(Z(0)*S(0)) == S(0)*Z(0) assert gate_sort(Z(0)*T(0)) == T(0)*Z(0) assert gate_sort(Z(0)*CNOT(0, 1)) == CNOT(0, 1)*Z(0) assert gate_sort(S(0)*CNOT(0, 1)) == CNOT(0, 1)*S(0) assert gate_sort(T(0)*CNOT(0, 1)) == CNOT(0, 1)*T(0) assert gate_sort(X(1)*CNOT(0, 1)) == CNOT(0, 1)*X(1) # This takes a long time and should only be uncommented once in a while. # nqubits = 5 # ngates = 10 # trials = 10 # for i in range(trials): # c = random_circuit(ngates, nqubits) # assert represent(c, nqubits=nqubits) == \ # represent(gate_sort(c), nqubits=nqubits) def test_gate_simp(): """Test gate_simp.""" e = H(0)*X(1)*H(0)**2*CNOT(0, 1)*X(1)**3*X(0)*Z(3)**2*S(4)**3 assert gate_simp(e) == H(0)*CNOT(0, 1)*S(4)*X(0)*Z(4) assert gate_simp(X(0)*X(0)) == 1 assert gate_simp(Y(0)*Y(0)) == 1 assert gate_simp(Z(0)*Z(0)) == 1 assert gate_simp(H(0)*H(0)) == 1 assert gate_simp(T(0)*T(0)) == S(0) assert gate_simp(S(0)*S(0)) == Z(0) assert gate_simp(Integer(1)) == Integer(1) assert gate_simp(X(0)**2 + Y(0)**2) == Integer(2) def test_swap_gate(): """Test the SWAP gate.""" swap_gate_matrix = Matrix( ((1, 0, 0, 0), (0, 0, 1, 0), (0, 1, 0, 0), (0, 0, 0, 1))) assert represent(SwapGate(1, 0).decompose(), nqubits=2) == swap_gate_matrix assert qapply(SwapGate(1, 3)*Qubit('0010')) == Qubit('1000') nqubits = 4 for i in range(nqubits): for j in range(i): assert represent(SwapGate(i, j), nqubits=nqubits) == \ represent(SwapGate(i, j).decompose(), nqubits=nqubits) def test_one_qubit_commutators(): """Test single qubit gate commutation relations.""" for g1 in (IdentityGate, X, Y, Z, H, T, S): for g2 in (IdentityGate, X, Y, Z, H, T, S): e = Commutator(g1(0), g2(0)) a = matrix_to_zero(represent(e, nqubits=1, format='sympy')) b = matrix_to_zero(represent(e.doit(), nqubits=1, format='sympy')) assert a == b e = Commutator(g1(0), g2(1)) assert e.doit() == 0 def test_one_qubit_anticommutators(): """Test single qubit gate anticommutation relations.""" for g1 in (IdentityGate, X, Y, Z, H): for g2 in (IdentityGate, X, Y, Z, H): e = AntiCommutator(g1(0), g2(0)) a = matrix_to_zero(represent(e, nqubits=1, format='sympy')) b = matrix_to_zero(represent(e.doit(), nqubits=1, format='sympy')) assert a == b e = AntiCommutator(g1(0), g2(1)) a = matrix_to_zero(represent(e, nqubits=2, format='sympy')) b = matrix_to_zero(represent(e.doit(), nqubits=2, format='sympy')) assert a == b def test_cnot_commutators(): """Test commutators of involving CNOT gates.""" assert Commutator(CNOT(0, 1), Z(0)).doit() == 0 assert Commutator(CNOT(0, 1), T(0)).doit() == 0 assert Commutator(CNOT(0, 1), S(0)).doit() == 0 assert Commutator(CNOT(0, 1), X(1)).doit() == 0 assert Commutator(CNOT(0, 1), CNOT(0, 1)).doit() == 0 assert Commutator(CNOT(0, 1), CNOT(0, 2)).doit() == 0 assert Commutator(CNOT(0, 2), CNOT(0, 1)).doit() == 0 assert Commutator(CNOT(1, 2), CNOT(1, 0)).doit() == 0 def test_random_circuit(): c = random_circuit(10, 3) assert isinstance(c, Mul) m = represent(c, nqubits=3) assert m.shape == (8, 8) assert isinstance(m, Matrix) def test_hermitian_XGate(): x = XGate(1, 2) x_dagger = Dagger(x) assert (x == x_dagger) def test_hermitian_YGate(): y = YGate(1, 2) y_dagger = Dagger(y) assert (y == y_dagger) def test_hermitian_ZGate(): z = ZGate(1, 2) z_dagger = Dagger(z) assert (z == z_dagger) def test_unitary_XGate(): x = XGate(1, 2) x_dagger = Dagger(x) assert (x*x_dagger == 1) def test_unitary_YGate(): y = YGate(1, 2) y_dagger = Dagger(y) assert (y*y_dagger == 1) def test_unitary_ZGate(): z = ZGate(1, 2) z_dagger = Dagger(z) assert (z*z_dagger == 1) sympy-0.7.4.1/sympy/physics/quantum/tests/test_piab.py0000644000175000017500000000156112253362407023272 0ustar georgeskgeorgesk"""Tests for piab.py""" from sympy import Interval, pi, S, sin, sqrt, symbols from sympy.functions.special.tensor_functions import KroneckerDelta from sympy.physics.quantum import L2, qapply, hbar, represent from sympy.physics.quantum.piab import PIABHamiltonian, PIABKet, PIABBra, m, L i, j, n, x = symbols('i j n x') def test_H(): assert PIABHamiltonian('H').hilbert_space == \ L2(Interval(S.NegativeInfinity, S.Infinity)) assert qapply(PIABHamiltonian('H')*PIABKet(n)) == \ (n**2*pi**2*hbar**2)/(2*m*L**2)*PIABKet(n) def test_states(): assert PIABKet(n).dual_class() == PIABBra assert PIABKet(n).hilbert_space == \ L2(Interval(S.NegativeInfinity, S.Infinity)) assert represent(PIABKet(n)) == sqrt(2/L)*sin(n*pi*x/L) assert (PIABBra(i)*PIABKet(j)).doit() == KroneckerDelta(i, j) assert PIABBra(n).dual_class() == PIABKet sympy-0.7.4.1/sympy/physics/quantum/tests/test_constants.py0000644000175000017500000000050512253362407024370 0ustar georgeskgeorgeskfrom sympy import Float from sympy.physics.quantum.constants import hbar def test_hbar(): assert hbar.is_commutative is True assert hbar.is_real is True assert hbar.is_positive is True assert hbar.is_negative is False assert hbar.is_irrational is True assert hbar.evalf() == Float(1.05457162e-34) sympy-0.7.4.1/sympy/physics/quantum/tests/test_represent.py0000644000175000017500000001200412253362407024360 0ustar georgeskgeorgeskfrom sympy import Float, I, Integer, Matrix from sympy.external import import_module from sympy.utilities.pytest import skip from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.represent import (represent, rep_innerproduct, rep_expectation, enumerate_states) from sympy.physics.quantum.state import Bra, Ket from sympy.physics.quantum.operator import Operator, OuterProduct from sympy.physics.quantum.tensorproduct import TensorProduct from sympy.physics.quantum.tensorproduct import matrix_tensor_product 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.matrixutils import (numpy_ndarray, scipy_sparse_matrix, to_numpy, to_scipy_sparse, to_sympy) from sympy.physics.quantum.cartesian import XKet, XOp, XBra from sympy.physics.quantum.qapply import qapply from sympy.physics.quantum.operatorset import operators_to_state Amat = Matrix([[1, I], [-I, 1]]) Bmat = Matrix([[1, 2], [3, 4]]) Avec = Matrix([[1], [I]]) class AKet(Ket): @classmethod def dual_class(self): return ABra def _represent_default_basis(self, **options): return self._represent_AOp(None, **options) def _represent_AOp(self, basis, **options): return Avec class ABra(Bra): @classmethod def dual_class(self): return AKet class AOp(Operator): def _represent_default_basis(self, **options): return self._represent_AOp(None, **options) def _represent_AOp(self, basis, **options): return Amat class BOp(Operator): def _represent_default_basis(self, **options): return self._represent_AOp(None, **options) def _represent_AOp(self, basis, **options): return Bmat k = AKet('a') b = ABra('a') A = AOp('A') B = BOp('B') _tests = [ # Bra (b, Dagger(Avec)), (Dagger(b), Avec), # Ket (k, Avec), (Dagger(k), Dagger(Avec)), # Operator (A, Amat), (Dagger(A), Dagger(Amat)), # OuterProduct (OuterProduct(k, b), Avec*Avec.H), # TensorProduct (TensorProduct(A, B), matrix_tensor_product(Amat, Bmat)), # Pow (A**2, Amat**2), # Add/Mul (A*B + 2*A, Amat*Bmat + 2*Amat), # Commutator (Commutator(A, B), Amat*Bmat - Bmat*Amat), # AntiCommutator (AntiCommutator(A, B), Amat*Bmat + Bmat*Amat), # InnerProduct (InnerProduct(b, k), (Avec.H*Avec)[0]) ] def test_format_sympy(): for test in _tests: lhs = represent(test[0], basis=A, format='sympy') rhs = to_sympy(test[1]) assert lhs == rhs def test_scalar_sympy(): assert represent(Integer(1)) == Integer(1) assert represent(Float(1.0)) == Float(1.0) assert represent(1.0 + I) == 1.0 + I np = import_module('numpy') def test_format_numpy(): if not np: skip("numpy not installed.") for test in _tests: lhs = represent(test[0], basis=A, format='numpy') rhs = to_numpy(test[1]) if isinstance(lhs, numpy_ndarray): assert (lhs == rhs).all() else: assert lhs == rhs def test_scalar_numpy(): if not np: skip("numpy not installed.") assert represent(Integer(1), format='numpy') == 1 assert represent(Float(1.0), format='numpy') == 1.0 assert represent(1.0 + I, format='numpy') == 1.0 + 1.0j scipy = import_module('scipy', __import__kwargs={'fromlist': ['sparse']}) def test_format_scipy_sparse(): if not np: skip("numpy not installed.") if not scipy: skip("scipy not installed.") for test in _tests: lhs = represent(test[0], basis=A, format='scipy.sparse') rhs = to_scipy_sparse(test[1]) if isinstance(lhs, scipy_sparse_matrix): assert np.linalg.norm((lhs - rhs).todense()) == 0.0 else: assert lhs == rhs def test_scalar_scipy_sparse(): if not np: skip("numpy not installed.") if not scipy: skip("scipy not installed.") assert represent(Integer(1), format='scipy.sparse') == 1 assert represent(Float(1.0), format='scipy.sparse') == 1.0 assert represent(1.0 + I, format='scipy.sparse') == 1.0 + 1.0j x_ket = XKet('x') x_bra = XBra('x') x_op = XOp('X') def test_innerprod_represent(): assert rep_innerproduct(x_ket) == InnerProduct(XBra("x_1"), x_ket).doit() assert rep_innerproduct(x_bra) == InnerProduct(x_bra, XKet("x_1")).doit() try: rep_innerproduct(x_op) except TypeError: return True def test_operator_represent(): basis_kets = enumerate_states(operators_to_state(x_op), 1, 2) assert rep_expectation( x_op) == qapply(basis_kets[1].dual*x_op*basis_kets[0]) def test_enumerate_states(): test = XKet("foo") assert enumerate_states(test, 1, 1) == [XKet("foo_1")] assert enumerate_states( test, [1, 2, 4]) == [XKet("foo_1"), XKet("foo_2"), XKet("foo_4")] sympy-0.7.4.1/sympy/physics/quantum/tests/test_innerproduct.py0000644000175000017500000000271312253362407025073 0ustar georgeskgeorgeskfrom sympy import I, Integer, latex, pretty from sympy.physics.quantum.innerproduct import InnerProduct from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.state import Bra, Ket, StateBase def test_innerproduct(): k = Ket('k') b = Bra('b') ip = InnerProduct(b, k) assert isinstance(ip, InnerProduct) assert ip.bra == b assert ip.ket == k assert b*k == InnerProduct(b, k) assert k*(b*k)*b == k*InnerProduct(b, k)*b assert InnerProduct(b, k).subs(b, Dagger(k)) == Dagger(k)*k def test_innerproduct_dagger(): k = Ket('k') b = Bra('b') ip = b*k assert Dagger(ip) == Dagger(k)*Dagger(b) class FooState(StateBase): pass class FooKet(Ket, FooState): @classmethod def dual_class(self): return FooBra def _eval_innerproduct_FooBra(self, bra): return Integer(1) def _eval_innerproduct_BarBra(self, bra): return I class FooBra(Bra, FooState): @classmethod def dual_class(self): return FooKet class BarState(StateBase): pass class BarKet(Ket, BarState): @classmethod def dual_class(self): return BarBra class BarBra(Bra, BarState): @classmethod def dual_class(self): return BarKet def test_doit(): f = FooKet('foo') b = BarBra('bar') assert InnerProduct(b, f).doit() == I assert InnerProduct(Dagger(f), Dagger(b)).doit() == -I assert InnerProduct(Dagger(f), f).doit() == Integer(1) sympy-0.7.4.1/sympy/physics/quantum/tests/test_operator.py0000644000175000017500000001257012253362407024214 0ustar georgeskgeorgeskfrom sympy import (Derivative, diff, Function, Integer, Mul, pi, sin, Symbol, symbols) from sympy.physics.quantum.qexpr import QExpr from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.hilbert import HilbertSpace from sympy.physics.quantum.operator import (Operator, UnitaryOperator, HermitianOperator, OuterProduct, DifferentialOperator) from sympy.physics.quantum.state import Ket, Bra, Wavefunction from sympy.physics.quantum.qapply import qapply from sympy.core.trace import Tr from sympy.physics.quantum.spin import JzKet, JzBra class CustomKet(Ket): @classmethod def default_args(self): return ("t",) class CustomOp(HermitianOperator): @classmethod def default_args(self): return ("T",) t_ket = CustomKet() t_op = CustomOp() def test_operator(): A = Operator('A') B = Operator('B') C = Operator('C') assert isinstance(A, Operator) assert isinstance(A, QExpr) assert A.label == (Symbol('A'),) assert A.is_commutative is False assert A.hilbert_space == HilbertSpace() assert A*B != B*A assert (A*(B + C)).expand() == A*B + A*C assert ((A + B)**2).expand() == A**2 + A*B + B*A + B**2 assert t_op.label[0] == Symbol(t_op.default_args()[0]) assert Operator() == Operator("O") def test_operator_inv(): A = Operator('A') assert A*A.inv() == 1 assert A.inv()*A == 1 def test_hermitian(): H = HermitianOperator('H') assert isinstance(H, HermitianOperator) assert isinstance(H, Operator) assert Dagger(H) == H assert H.inv() != H assert H.is_commutative is False assert Dagger(H).is_commutative is False def test_unitary(): U = UnitaryOperator('U') assert isinstance(U, UnitaryOperator) assert isinstance(U, Operator) assert U.inv() == Dagger(U) assert U*Dagger(U) == 1 assert Dagger(U)*U == 1 assert U.is_commutative is False assert Dagger(U).is_commutative is False def test_outer_product(): k = Ket('k') b = Bra('b') op = OuterProduct(k, b) assert isinstance(op, OuterProduct) assert isinstance(op, Operator) assert op.ket == k assert op.bra == b assert op.label == (k, b) assert op.is_commutative is False op = k*b assert isinstance(op, OuterProduct) assert isinstance(op, Operator) assert op.ket == k assert op.bra == b assert op.label == (k, b) assert op.is_commutative is False op = 2*k*b assert op == Mul(Integer(2), k, b) op = 2*(k*b) assert op == Mul(Integer(2), OuterProduct(k, b)) assert Dagger(k*b) == OuterProduct(Dagger(b), Dagger(k)) assert Dagger(k*b).is_commutative is False #test the _eval_trace assert Tr(OuterProduct(JzKet(1, 1), JzBra(1, 1))).doit() == 1 def test_operator_dagger(): A = Operator('A') B = Operator('B') assert Dagger(A*B) == Dagger(B)*Dagger(A) assert Dagger(A + B) == Dagger(A) + Dagger(B) assert Dagger(A**2) == Dagger(A)**2 def test_differential_operator(): x = Symbol('x') f = Function('f') d = DifferentialOperator(Derivative(f(x), x), f(x)) g = Wavefunction(x**2, x) assert qapply(d*g) == Wavefunction(2*x, x) assert d.expr == Derivative(f(x), x) assert d.function == f(x) assert d.variables == (x,) assert diff(d, x) == DifferentialOperator(Derivative(f(x), x, 2), f(x)) d = DifferentialOperator(Derivative(f(x), x, 2), f(x)) g = Wavefunction(x**3, x) assert qapply(d*g) == Wavefunction(6*x, x) assert d.expr == Derivative(f(x), x, 2) assert d.function == f(x) assert d.variables == (x,) assert diff(d, x) == DifferentialOperator(Derivative(f(x), x, 3), f(x)) d = DifferentialOperator(1/x*Derivative(f(x), x), f(x)) assert d.expr == 1/x*Derivative(f(x), x) assert d.function == f(x) assert d.variables == (x,) assert diff(d, x) == \ DifferentialOperator(Derivative(1/x*Derivative(f(x), x), x), f(x)) assert qapply(d*g) == Wavefunction(3*x, x) # 2D cartesian Laplacian y = Symbol('y') d = DifferentialOperator(Derivative(f(x, y), x, 2) + Derivative(f(x, y), y, 2), f(x, y)) w = Wavefunction(x**3*y**2 + y**3*x**2, x, y) assert d.expr == Derivative(f(x, y), x, 2) + Derivative(f(x, y), y, 2) assert d.function == f(x, y) assert d.variables == (x, y) assert diff(d, x) == \ DifferentialOperator(Derivative(d.expr, x), f(x, y)) assert diff(d, y) == \ DifferentialOperator(Derivative(d.expr, y), f(x, y)) assert qapply(d*w) == Wavefunction(2*x**3 + 6*x*y**2 + 6*x**2*y + 2*y**3, x, y) # 2D polar Laplacian (th = theta) r, th = symbols('r th') d = DifferentialOperator(1/r*Derivative(r*Derivative(f(r, th), r), r) + 1/(r**2)*Derivative(f(r, th), th, 2), f(r, th)) w = Wavefunction(r**2*sin(th), r, (th, 0, pi)) assert d.expr == \ 1/r*Derivative(r*Derivative(f(r, th), r), r) + \ 1/(r**2)*Derivative(f(r, th), th, 2) assert d.function == f(r, th) assert d.variables == (r, th) assert diff(d, r) == \ DifferentialOperator(Derivative(d.expr, r), f(r, th)) assert diff(d, th) == \ DifferentialOperator(Derivative(d.expr, th), f(r, th)) assert qapply(d*w) == Wavefunction(3*sin(th), r, (th, 0, pi)) sympy-0.7.4.1/sympy/physics/quantum/tests/test_identitysearch.py0000644000175000017500000004306012253362407025376 0ustar georgeskgeorgeskfrom sympy.external import import_module from sympy import Mul, Integer from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.gate import (X, Y, Z, H, S, T, CNOT, IdentityGate, CGate, PhaseGate, TGate, gate_simp) from sympy.physics.quantum.identitysearch import (generate_gate_rules, generate_equivalent_ids, GateIdentity, bfs_identity_search, random_identity_search, is_scalar_sparse_matrix, is_scalar_nonsparse_matrix, is_degenerate, is_reducible) from sympy.utilities.pytest import skip def create_gate_sequence(qubit=0): gates = (X(qubit), Y(qubit), Z(qubit), H(qubit)) return gates def test_generate_gate_rules_1(): # Test with tuples (x, y, z, h) = create_gate_sequence() ph = PhaseGate(0) cgate_t = CGate(0, TGate(1)) assert generate_gate_rules((x,)) == set([((x,), ())]) gate_rules = set([((x, x), ()), ((x,), (x,))]) assert generate_gate_rules((x, x)) == gate_rules gate_rules = set([((x, y, x), ()), ((y, x, x), ()), ((x, x, y), ()), ((y, x), (x,)), ((x, y), (x,)), ((y,), (x, x))]) assert generate_gate_rules((x, y, x)) == gate_rules gate_rules = set([((x, y, z), ()), ((y, z, x), ()), ((z, x, y), ()), ((), (x, z, y)), ((), (y, x, z)), ((), (z, y, x)), ((x,), (z, y)), ((y, z), (x,)), ((y,), (x, z)), ((z, x), (y,)), ((z,), (y, x)), ((x, y), (z,))]) actual = generate_gate_rules((x, y, z)) assert actual == gate_rules gate_rules = set( [((), (h, z, y, x)), ((), (x, h, z, y)), ((), (y, x, h, z)), ((), (z, y, x, h)), ((h,), (z, y, x)), ((x,), (h, z, y)), ((y,), (x, h, z)), ((z,), (y, x, h)), ((h, x), (z, y)), ((x, y), (h, z)), ((y, z), (x, h)), ((z, h), (y, x)), ((h, x, y), (z,)), ((x, y, z), (h,)), ((y, z, h), (x,)), ((z, h, x), (y,)), ((h, x, y, z), ()), ((x, y, z, h), ()), ((y, z, h, x), ()), ((z, h, x, y), ())]) actual = generate_gate_rules((x, y, z, h)) assert actual == gate_rules gate_rules = set([((), (cgate_t**(-1), ph**(-1), x)), ((), (ph**(-1), x, cgate_t**(-1))), ((), (x, cgate_t**(-1), ph**(-1))), ((cgate_t,), (ph**(-1), x)), ((ph,), (x, cgate_t**(-1))), ((x,), (cgate_t**(-1), ph**(-1))), ((cgate_t, x), (ph**(-1),)), ((ph, cgate_t), (x,)), ((x, ph), (cgate_t**(-1),)), ((cgate_t, x, ph), ()), ((ph, cgate_t, x), ()), ((x, ph, cgate_t), ())]) actual = generate_gate_rules((x, ph, cgate_t)) assert actual == gate_rules gate_rules = set([(Integer(1), cgate_t**(-1)*ph**(-1)*x), (Integer(1), ph**(-1)*x*cgate_t**(-1)), (Integer(1), x*cgate_t**(-1)*ph**(-1)), (cgate_t, ph**(-1)*x), (ph, x*cgate_t**(-1)), (x, cgate_t**(-1)*ph**(-1)), (cgate_t*x, ph**(-1)), (ph*cgate_t, x), (x*ph, cgate_t**(-1)), (cgate_t*x*ph, Integer(1)), (ph*cgate_t*x, Integer(1)), (x*ph*cgate_t, Integer(1))]) actual = generate_gate_rules((x, ph, cgate_t), return_as_muls=True) assert actual == gate_rules def test_generate_gate_rules_2(): # Test with Muls (x, y, z, h) = create_gate_sequence() ph = PhaseGate(0) cgate_t = CGate(0, TGate(1)) # Note: 1 (type int) is not the same as 1 (type One) expected = set([(x, Integer(1))]) assert generate_gate_rules((x,), return_as_muls=True) == expected expected = set([(Integer(1), Integer(1))]) assert generate_gate_rules(x*x, return_as_muls=True) == expected expected = set([((), ())]) assert generate_gate_rules(x*x, return_as_muls=False) == expected gate_rules = set([(x*y*x, Integer(1)), (y, Integer(1)), (y*x, x), (x*y, x)]) assert generate_gate_rules(x*y*x, return_as_muls=True) == gate_rules gate_rules = set([(x*y*z, Integer(1)), (y*z*x, Integer(1)), (z*x*y, Integer(1)), (Integer(1), x*z*y), (Integer(1), y*x*z), (Integer(1), z*y*x), (x, z*y), (y*z, x), (y, x*z), (z*x, y), (z, y*x), (x*y, z)]) actual = generate_gate_rules(x*y*z, return_as_muls=True) assert actual == gate_rules gate_rules = set([(Integer(1), h*z*y*x), (Integer(1), x*h*z*y), (Integer(1), y*x*h*z), (Integer(1), z*y*x*h), (h, z*y*x), (x, h*z*y), (y, x*h*z), (z, y*x*h), (h*x, z*y), (z*h, y*x), (x*y, h*z), (y*z, x*h), (h*x*y, z), (x*y*z, h), (y*z*h, x), (z*h*x, y), (h*x*y*z, Integer(1)), (x*y*z*h, Integer(1)), (y*z*h*x, Integer(1)), (z*h*x*y, Integer(1))]) actual = generate_gate_rules(x*y*z*h, return_as_muls=True) assert actual == gate_rules gate_rules = set([(Integer(1), cgate_t**(-1)*ph**(-1)*x), (Integer(1), ph**(-1)*x*cgate_t**(-1)), (Integer(1), x*cgate_t**(-1)*ph**(-1)), (cgate_t, ph**(-1)*x), (ph, x*cgate_t**(-1)), (x, cgate_t**(-1)*ph**(-1)), (cgate_t*x, ph**(-1)), (ph*cgate_t, x), (x*ph, cgate_t**(-1)), (cgate_t*x*ph, Integer(1)), (ph*cgate_t*x, Integer(1)), (x*ph*cgate_t, Integer(1))]) actual = generate_gate_rules(x*ph*cgate_t, return_as_muls=True) assert actual == gate_rules gate_rules = set([((), (cgate_t**(-1), ph**(-1), x)), ((), (ph**(-1), x, cgate_t**(-1))), ((), (x, cgate_t**(-1), ph**(-1))), ((cgate_t,), (ph**(-1), x)), ((ph,), (x, cgate_t**(-1))), ((x,), (cgate_t**(-1), ph**(-1))), ((cgate_t, x), (ph**(-1),)), ((ph, cgate_t), (x,)), ((x, ph), (cgate_t**(-1),)), ((cgate_t, x, ph), ()), ((ph, cgate_t, x), ()), ((x, ph, cgate_t), ())]) actual = generate_gate_rules(x*ph*cgate_t) assert actual == gate_rules def test_generate_equivalent_ids_1(): # Test with tuples (x, y, z, h) = create_gate_sequence() assert generate_equivalent_ids((x,)) == set([(x,)]) assert generate_equivalent_ids((x, x)) == set([(x, x)]) assert generate_equivalent_ids((x, y)) == set([(x, y), (y, x)]) gate_seq = (x, y, z) gate_ids = set([(x, y, z), (y, z, x), (z, x, y), (z, y, x), (y, x, z), (x, z, y)]) assert generate_equivalent_ids(gate_seq) == gate_ids gate_ids = set([Mul(x, y, z), Mul(y, z, x), Mul(z, x, y), Mul(z, y, x), Mul(y, x, z), Mul(x, z, y)]) assert generate_equivalent_ids(gate_seq, return_as_muls=True) == gate_ids gate_seq = (x, y, z, h) gate_ids = set([(x, y, z, h), (y, z, h, x), (h, x, y, z), (h, z, y, x), (z, y, x, h), (y, x, h, z), (z, h, x, y), (x, h, z, y)]) assert generate_equivalent_ids(gate_seq) == gate_ids gate_seq = (x, y, x, y) gate_ids = set([(x, y, x, y), (y, x, y, x)]) assert generate_equivalent_ids(gate_seq) == gate_ids cgate_y = CGate((1,), y) gate_seq = (y, cgate_y, y, cgate_y) gate_ids = set([(y, cgate_y, y, cgate_y), (cgate_y, y, cgate_y, y)]) assert generate_equivalent_ids(gate_seq) == gate_ids cnot = CNOT(1, 0) cgate_z = CGate((0,), Z(1)) gate_seq = (cnot, h, cgate_z, h) gate_ids = set([(cnot, h, cgate_z, h), (h, cgate_z, h, cnot), (h, cnot, h, cgate_z), (cgate_z, h, cnot, h)]) assert generate_equivalent_ids(gate_seq) == gate_ids def test_generate_equivalent_ids_2(): # Test with Muls (x, y, z, h) = create_gate_sequence() assert generate_equivalent_ids((x,), return_as_muls=True) == set([x]) gate_ids = set([Integer(1)]) assert generate_equivalent_ids(x*x, return_as_muls=True) == gate_ids gate_ids = set([x*y, y*x]) assert generate_equivalent_ids(x*y, return_as_muls=True) == gate_ids gate_ids = set([(x, y), (y, x)]) assert generate_equivalent_ids(x*y) == gate_ids circuit = Mul(*(x, y, z)) gate_ids = set([x*y*z, y*z*x, z*x*y, z*y*x, y*x*z, x*z*y]) assert generate_equivalent_ids(circuit, return_as_muls=True) == gate_ids circuit = Mul(*(x, y, z, h)) gate_ids = set([x*y*z*h, y*z*h*x, h*x*y*z, h*z*y*x, z*y*x*h, y*x*h*z, z*h*x*y, x*h*z*y]) assert generate_equivalent_ids(circuit, return_as_muls=True) == gate_ids circuit = Mul(*(x, y, x, y)) gate_ids = set([x*y*x*y, y*x*y*x]) assert generate_equivalent_ids(circuit, return_as_muls=True) == gate_ids cgate_y = CGate((1,), y) circuit = Mul(*(y, cgate_y, y, cgate_y)) gate_ids = set([y*cgate_y*y*cgate_y, cgate_y*y*cgate_y*y]) assert generate_equivalent_ids(circuit, return_as_muls=True) == gate_ids cnot = CNOT(1, 0) cgate_z = CGate((0,), Z(1)) circuit = Mul(*(cnot, h, cgate_z, h)) gate_ids = set([cnot*h*cgate_z*h, h*cgate_z*h*cnot, h*cnot*h*cgate_z, cgate_z*h*cnot*h]) assert generate_equivalent_ids(circuit, return_as_muls=True) == gate_ids def test_is_scalar_nonsparse_matrix(): numqubits = 2 id_only = False id_gate = (IdentityGate(1),) actual = is_scalar_nonsparse_matrix(id_gate, numqubits, id_only) assert actual is True x0 = X(0) xx_circuit = (x0, x0) actual = is_scalar_nonsparse_matrix(xx_circuit, numqubits, id_only) assert actual is True x1 = X(1) y1 = Y(1) xy_circuit = (x1, y1) actual = is_scalar_nonsparse_matrix(xy_circuit, numqubits, id_only) assert actual is False z1 = Z(1) xyz_circuit = (x1, y1, z1) actual = is_scalar_nonsparse_matrix(xyz_circuit, numqubits, id_only) assert actual is True cnot = CNOT(1, 0) cnot_circuit = (cnot, cnot) actual = is_scalar_nonsparse_matrix(cnot_circuit, numqubits, id_only) assert actual is True h = H(0) hh_circuit = (h, h) actual = is_scalar_nonsparse_matrix(hh_circuit, numqubits, id_only) assert actual is True h1 = H(1) xhzh_circuit = (x1, h1, z1, h1) actual = is_scalar_nonsparse_matrix(xhzh_circuit, numqubits, id_only) assert actual is True id_only = True actual = is_scalar_nonsparse_matrix(xhzh_circuit, numqubits, id_only) assert actual is True actual = is_scalar_nonsparse_matrix(xyz_circuit, numqubits, id_only) assert actual is False actual = is_scalar_nonsparse_matrix(cnot_circuit, numqubits, id_only) assert actual is True actual = is_scalar_nonsparse_matrix(hh_circuit, numqubits, id_only) assert actual is True def test_is_scalar_sparse_matrix(): np = import_module('numpy') if not np: skip("numpy not installed.") scipy = import_module('scipy', __import__kwargs={'fromlist': ['sparse']}) if not scipy: skip("scipy not installed.") numqubits = 2 id_only = False id_gate = (IdentityGate(1),) assert is_scalar_sparse_matrix(id_gate, numqubits, id_only) is True x0 = X(0) xx_circuit = (x0, x0) assert is_scalar_sparse_matrix(xx_circuit, numqubits, id_only) is True x1 = X(1) y1 = Y(1) xy_circuit = (x1, y1) assert is_scalar_sparse_matrix(xy_circuit, numqubits, id_only) is False z1 = Z(1) xyz_circuit = (x1, y1, z1) assert is_scalar_sparse_matrix(xyz_circuit, numqubits, id_only) is True cnot = CNOT(1, 0) cnot_circuit = (cnot, cnot) assert is_scalar_sparse_matrix(cnot_circuit, numqubits, id_only) is True h = H(0) hh_circuit = (h, h) assert is_scalar_sparse_matrix(hh_circuit, numqubits, id_only) is True # NOTE: # The elements of the sparse matrix for the following circuit # is actually 1.0000000000000002+0.0j. h1 = H(1) xhzh_circuit = (x1, h1, z1, h1) assert is_scalar_sparse_matrix(xhzh_circuit, numqubits, id_only) is True id_only = True assert is_scalar_sparse_matrix(xhzh_circuit, numqubits, id_only) is True assert is_scalar_sparse_matrix(xyz_circuit, numqubits, id_only) is False assert is_scalar_sparse_matrix(cnot_circuit, numqubits, id_only) is True assert is_scalar_sparse_matrix(hh_circuit, numqubits, id_only) is True def test_is_degenerate(): (x, y, z, h) = create_gate_sequence() gate_id = GateIdentity(x, y, z) ids = set([gate_id]) another_id = (z, y, x) assert is_degenerate(ids, another_id) is True def test_is_reducible(): nqubits = 2 (x, y, z, h) = create_gate_sequence() circuit = (x, y, y) assert is_reducible(circuit, nqubits, 1, 3) is True circuit = (x, y, x) assert is_reducible(circuit, nqubits, 1, 3) is False circuit = (x, y, y, x) assert is_reducible(circuit, nqubits, 0, 4) is True circuit = (x, y, y, x) assert is_reducible(circuit, nqubits, 1, 3) is True circuit = (x, y, z, y, y) assert is_reducible(circuit, nqubits, 1, 5) is True def test_bfs_identity_search(): assert bfs_identity_search([], 1) == set() (x, y, z, h) = create_gate_sequence() gate_list = [x] id_set = set([GateIdentity(x, x)]) assert bfs_identity_search(gate_list, 1, max_depth=2) == id_set # Set should not contain degenerate quantum circuits gate_list = [x, y, z] id_set = set([GateIdentity(x, x), GateIdentity(y, y), GateIdentity(z, z), GateIdentity(x, y, z)]) assert bfs_identity_search(gate_list, 1) == id_set id_set = set([GateIdentity(x, x), GateIdentity(y, y), GateIdentity(z, z), GateIdentity(x, y, z), GateIdentity(x, y, x, y), GateIdentity(x, z, x, z), GateIdentity(y, z, y, z)]) assert bfs_identity_search(gate_list, 1, max_depth=4) == id_set assert bfs_identity_search(gate_list, 1, max_depth=5) == id_set gate_list = [x, y, z, h] id_set = set([GateIdentity(x, x), GateIdentity(y, y), GateIdentity(z, z), GateIdentity(h, h), GateIdentity(x, y, z), GateIdentity(x, y, x, y), GateIdentity(x, z, x, z), GateIdentity(x, h, z, h), GateIdentity(y, z, y, z), GateIdentity(y, h, y, h)]) assert bfs_identity_search(gate_list, 1) == id_set id_set = set([GateIdentity(x, x), GateIdentity(y, y), GateIdentity(z, z), GateIdentity(h, h)]) assert id_set == bfs_identity_search(gate_list, 1, max_depth=3, identity_only=True) id_set = set([GateIdentity(x, x), GateIdentity(y, y), GateIdentity(z, z), GateIdentity(h, h), GateIdentity(x, y, z), GateIdentity(x, y, x, y), GateIdentity(x, z, x, z), GateIdentity(x, h, z, h), GateIdentity(y, z, y, z), GateIdentity(y, h, y, h), GateIdentity(x, y, h, x, h), GateIdentity(x, z, h, y, h), GateIdentity(y, z, h, z, h)]) assert bfs_identity_search(gate_list, 1, max_depth=5) == id_set id_set = set([GateIdentity(x, x), GateIdentity(y, y), GateIdentity(z, z), GateIdentity(h, h), GateIdentity(x, h, z, h)]) assert id_set == bfs_identity_search(gate_list, 1, max_depth=4, identity_only=True) cnot = CNOT(1, 0) gate_list = [x, cnot] id_set = set([GateIdentity(x, x), GateIdentity(cnot, cnot), GateIdentity(x, cnot, x, cnot)]) assert bfs_identity_search(gate_list, 2, max_depth=4) == id_set cgate_x = CGate((1,), x) gate_list = [x, cgate_x] id_set = set([GateIdentity(x, x), GateIdentity(cgate_x, cgate_x), GateIdentity(x, cgate_x, x, cgate_x)]) assert bfs_identity_search(gate_list, 2, max_depth=4) == id_set cgate_z = CGate((0,), Z(1)) gate_list = [cnot, cgate_z, h] id_set = set([GateIdentity(h, h), GateIdentity(cgate_z, cgate_z), GateIdentity(cnot, cnot), GateIdentity(cnot, h, cgate_z, h)]) assert bfs_identity_search(gate_list, 2, max_depth=4) == id_set s = PhaseGate(0) t = TGate(0) gate_list = [s, t] id_set = set([GateIdentity(s, s, s, s)]) assert bfs_identity_search(gate_list, 1, max_depth=4) == id_set # Throws an error in represent: "exponent must be >= 0" #gate_list = [Dagger(s), t] #id_set = set([GateIdentity(Dagger(s), t, t)]) #assert bfs_identity_search(gate_list, 1, max_depth=3) == id_set sympy-0.7.4.1/sympy/physics/quantum/tests/test_qasm.py0000644000175000017500000000615012253362407023317 0ustar georgeskgeorgeskfrom sympy.physics.quantum.qasm import Qasm, prod, flip_index, trim,\ get_index, nonblank, fullsplit, fixcommand, stripquotes, read_qasm from sympy.physics.quantum.gate import X, Z, H, S, T from sympy.physics.quantum.gate import CNOT, SWAP, CPHASE, CGate, CGateS from sympy.physics.quantum.circuitplot import Mz, CreateOneQubitGate, CreateCGate def test_qasm_readqasm(): qasm_lines = """\ qubit q_0 qubit q_1 h q_0 cnot q_0,q_1 """ q = read_qasm(qasm_lines) assert q.get_circuit() == CNOT(1,0)*H(1) def test_qasm_ex1(): q = Qasm('qubit q0', 'qubit q1', 'h q0', 'cnot q0,q1') assert q.get_circuit() == CNOT(1,0)*H(1) def test_qasm_ex1_methodcalls(): q = Qasm() q.qubit('q_0') q.qubit('q_1') q.h('q_0') q.cnot('q_0', 'q_1') assert q.get_circuit() == CNOT(1,0)*H(1) def test_qasm_swap(): q = Qasm('qubit q0', 'qubit q1', 'cnot q0,q1', 'cnot q1,q0', 'cnot q0,q1') assert q.get_circuit() == CNOT(1,0)*CNOT(0,1)*CNOT(1,0) def test_qasm_ex2(): q = Qasm('qubit q_0', 'qubit q_1', 'qubit q_2', 'h q_1', 'cnot q_1,q_2', 'cnot q_0,q_1', 'h q_0', 'measure q_1', 'measure q_0', 'c-x q_1,q_2', 'c-z q_0,q_2') assert q.get_circuit() == CGate(2,Z(0))*CGate(1,X(0))*Mz(2)*Mz(1)*H(2)*CNOT(2,1)*CNOT(1,0)*H(1) def test_qasm_1q(): for symbol, gate in [('x', X), ('z', Z), ('h', H), ('s', S), ('t', T), ('measure', Mz)]: q = Qasm('qubit q_0', '%s q_0' % symbol) assert q.get_circuit() == gate(0) def test_qasm_2q(): for symbol, gate in [('cnot', CNOT), ('swap', SWAP), ('cphase', CPHASE)]: q = Qasm('qubit q_0', 'qubit q_1', '%s q_0,q_1' % symbol) assert q.get_circuit() == gate(1,0) def test_qasm_3q(): q = Qasm('qubit q0', 'qubit q1', 'qubit q2', 'toffoli q2,q1,q0') assert q.get_circuit() == CGateS((0,1),X(2)) def test_qasm_prod(): assert prod([1, 2, 3]) == 6 assert prod([H(0), X(1)])== H(0)*X(1) def test_qasm_flip_index(): assert flip_index(0, 2) == 1 assert flip_index(1, 2) == 0 def test_qasm_trim(): assert trim('nothing happens here') == 'nothing happens here' assert trim("Something #happens here") == "Something " def test_qasm_get_index(): assert get_index('q0', ['q0', 'q1']) == 1 assert get_index('q1', ['q0', 'q1']) == 0 def test_qasm_nonblank(): assert list(nonblank('abcd')) == list('abcd') assert list(nonblank('abc ')) == list('abc') def test_qasm_fullsplit(): assert fullsplit('g q0,q1,q2, q3') == ('g', ['q0', 'q1', 'q2', 'q3']) def test_qasm_fixcommand(): assert fixcommand('foo') == 'foo' assert fixcommand('def') == 'qdef' def test_qasm_stripquotes(): assert stripquotes("'S'") == 'S' assert stripquotes('"S"') == 'S' assert stripquotes('S') == 'S' def test_qasm_qdef(): # weaker test condition (str) since we don't have access to the actual class q = Qasm("def Q,0,Q",'qubit q0','Q q0') Qgate = CreateOneQubitGate('Q') assert str(q.get_circuit()) == 'Q(0)' q = Qasm("def CQ,1,Q", 'qubit q0', 'qubit q1', 'CQ q0,q1') Qgate = CreateCGate('Q') assert str(q.get_circuit()) == 'C((1),Q(0))' sympy-0.7.4.1/sympy/physics/quantum/tests/test_cartesian.py0000644000175000017500000000742012253362407024330 0ustar georgeskgeorgesk"""Tests for cartesian.py""" from sympy import S, Interval, symbols, I, DiracDelta, exp, sqrt, pi from sympy.physics.quantum import qapply, represent, L2, Dagger from sympy.physics.quantum import Commutator, hbar from sympy.physics.quantum.cartesian import ( XOp, YOp, ZOp, PxOp, X, Y, Z, Px, XKet, XBra, PxKet, PxBra, PositionKet3D, PositionBra3D ) from sympy.physics.quantum.operator import DifferentialOperator x, y, z, x_1, x_2, x_3, y_1, z_1 = symbols('x,y,z,x_1,x_2,x_3,y_1,z_1') px, py, px_1, px_2 = symbols('px py px_1 px_2') def test_x(): assert X.hilbert_space == L2(Interval(S.NegativeInfinity, S.Infinity)) assert Commutator(X, Px).doit() == I*hbar assert qapply(X*XKet(x)) == x*XKet(x) assert XKet(x).dual_class() == XBra assert XBra(x).dual_class() == XKet assert (Dagger(XKet(y))*XKet(x)).doit() == DiracDelta(x - y) assert (PxBra(px)*XKet(x)).doit() == \ exp(-I*x*px/hbar)/sqrt(2*pi*hbar) assert represent(XKet(x)) == DiracDelta(x - x_1) assert represent(XBra(x)) == DiracDelta(-x + x_1) assert XBra(x).position == x assert represent(XOp()*XKet()) == x*DiracDelta(x - x_2) assert represent(XOp()*XKet()*XBra('y')) == \ x*DiracDelta(x - x_3)*DiracDelta(x_1 - y) assert represent(XBra("y")*XKet()) == DiracDelta(x - y) assert represent( XKet()*XBra()) == DiracDelta(x - x_2) * DiracDelta(x_1 - x) rep_p = represent(XOp(), basis=PxOp) assert rep_p == hbar*I*DiracDelta(px_1 - px_2)*DifferentialOperator(px_1) assert rep_p == represent(XOp(), basis=PxOp()) assert rep_p == represent(XOp(), basis=PxKet) assert rep_p == represent(XOp(), basis=PxKet()) assert represent(XOp()*PxKet(), basis=PxKet) == \ hbar*I*DiracDelta(px - px_2)*DifferentialOperator(px) def test_p(): assert Px.hilbert_space == L2(Interval(S.NegativeInfinity, S.Infinity)) assert qapply(Px*PxKet(px)) == px*PxKet(px) assert PxKet(px).dual_class() == PxBra assert PxBra(x).dual_class() == PxKet assert (Dagger(PxKet(py))*PxKet(px)).doit() == DiracDelta(px - py) assert (XBra(x)*PxKet(px)).doit() == \ exp(I*x*px/hbar)/sqrt(2*pi*hbar) assert represent(PxKet(px)) == DiracDelta(px - px_1) rep_x = represent(PxOp(), basis=XOp) assert rep_x == -hbar*I*DiracDelta(x_1 - x_2)*DifferentialOperator(x_1) assert rep_x == represent(PxOp(), basis=XOp()) assert rep_x == represent(PxOp(), basis=XKet) assert rep_x == represent(PxOp(), basis=XKet()) assert represent(PxOp()*XKet(), basis=XKet) == \ -hbar*I*DiracDelta(x - x_2)*DifferentialOperator(x) assert represent(XBra("y")*PxOp()*XKet(), basis=XKet) == \ -hbar*I*DiracDelta(x - y)*DifferentialOperator(x) def test_3dpos(): assert Y.hilbert_space == L2(Interval(S.NegativeInfinity, S.Infinity)) assert Z.hilbert_space == L2(Interval(S.NegativeInfinity, S.Infinity)) test_ket = PositionKet3D(x, y, z) assert qapply(X*test_ket) == x*test_ket assert qapply(Y*test_ket) == y*test_ket assert qapply(Z*test_ket) == z*test_ket assert qapply(X*Y*test_ket) == x*y*test_ket assert qapply(X*Y*Z*test_ket) == x*y*z*test_ket assert qapply(Y*Z*test_ket) == y*z*test_ket assert PositionKet3D() == test_ket assert YOp() == Y assert ZOp() == Z assert PositionKet3D.dual_class() == PositionBra3D assert PositionBra3D.dual_class() == PositionKet3D other_ket = PositionKet3D(x_1, y_1, z_1) assert (Dagger(other_ket)*test_ket).doit() == \ DiracDelta(x - x_1)*DiracDelta(y - y_1)*DiracDelta(z - z_1) assert test_ket.position_x == x assert test_ket.position_y == y assert test_ket.position_z == z assert other_ket.position_x == x_1 assert other_ket.position_y == y_1 assert other_ket.position_z == z_1 # TODO: Add tests for representations sympy-0.7.4.1/sympy/physics/quantum/tests/test_qft.py0000644000175000017500000000323112253362407023145 0ustar georgeskgeorgeskfrom sympy import exp, I, Matrix, pi, sqrt, Symbol from sympy.physics.quantum.qft import QFT, IQFT, RkGate from sympy.physics.quantum.gate import (ZGate, SwapGate, HadamardGate, CGate, PhaseGate, TGate) from sympy.physics.quantum.qubit import Qubit from sympy.physics.quantum.qapply import qapply from sympy.physics.quantum.represent import represent def test_RkGate(): x = Symbol('x') assert RkGate(1, x).k == x assert RkGate(1, x).targets == (1,) assert RkGate(1, 1) == ZGate(1) assert RkGate(2, 2) == PhaseGate(2) assert RkGate(3, 3) == TGate(3) assert represent( RkGate(0, x), nqubits=1) == Matrix([[1, 0], [0, exp(2*I*pi/2**x)]]) def test_quantum_fourier(): assert QFT(0, 3).decompose() == \ SwapGate(0, 2)*HadamardGate(0)*CGate((0,), PhaseGate(1)) * \ HadamardGate(1)*CGate((0,), TGate(2))*CGate((1,), PhaseGate(2)) * \ HadamardGate(2) assert IQFT(0, 3).decompose() == \ HadamardGate(2)*CGate((1,), RkGate(2, -2))*CGate((0,), RkGate(2, -3)) * \ HadamardGate(1)*CGate((0,), RkGate(1, -2))*HadamardGate(0)*SwapGate(0, 2) assert represent(QFT(0, 3), nqubits=3) == \ Matrix([[exp(2*pi*I/8)**(i*j % 8)/sqrt(8) for i in range(8)] for j in range(8)]) assert QFT(0, 4).decompose() # non-trivial decomposition assert qapply(QFT(0, 3).decompose()*Qubit(0, 0, 0)).expand() == qapply( HadamardGate(0)*HadamardGate(1)*HadamardGate(2)*Qubit(0, 0, 0) ).expand() def test_qft_represent(): c = QFT(0, 3) a = represent(c, nqubits=3) b = represent(c.decompose(), nqubits=3) assert a.evalf(prec=10) == b.evalf(prec=10) sympy-0.7.4.1/sympy/physics/quantum/tests/test_commutator.py0000644000175000017500000000344612253362407024555 0ustar georgeskgeorgeskfrom sympy import symbols, Integer from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.commutator import Commutator as Comm from sympy.physics.quantum.operator import Operator a, b, c = symbols('a,b,c') A, B, C, D = symbols('A,B,C,D', commutative=False) def test_commutator(): c = Comm(A, B) assert c.is_commutative is False assert isinstance(c, Comm) assert c.subs(A, C) == Comm(C, B) def test_commutator_identities(): assert Comm(a*A, b*B) == a*b*Comm(A, B) assert Comm(A, A) == 0 assert Comm(a, b) == 0 assert Comm(A, B) == -Comm(B, A) assert Comm(A, B).doit() == A*B - B*A assert Comm(A, B*C).expand(commutator=True) == Comm(A, B)*C + B*Comm(A, C) assert Comm(A*B, C*D).expand(commutator=True) == \ A*C*Comm(B, D) + A*Comm(B, C)*D + C*Comm(A, D)*B + Comm(A, C)*D*B assert Comm(A + B, C + D).expand(commutator=True) == \ Comm(A, C) + Comm(A, D) + Comm(B, C) + Comm(B, D) assert Comm(A, B + C).expand(commutator=True) == Comm(A, B) + Comm(A, C) e = Comm(A, Comm(B, C)) + Comm(B, Comm(C, A)) + Comm(C, Comm(A, B)) assert e.doit().expand() == 0 def test_commutator_dagger(): comm = Comm(A*B, C) assert Dagger(comm).expand(commutator=True) == \ - Comm(Dagger(B), Dagger(C))*Dagger(A) - \ Dagger(B)*Comm(Dagger(A), Dagger(C)) class Foo(Operator): def _eval_commutator_Bar(self, bar): return Integer(0) class Bar(Operator): pass class Tam(Operator): def _eval_commutator_Foo(self, foo): return Integer(1) def test_eval_commutator(): F = Foo('F') B = Bar('B') T = Tam('T') assert Comm(F, B).doit() == 0 assert Comm(B, F).doit() == 0 assert Comm(F, T).doit() == -1 assert Comm(T, F).doit() == 1 assert Comm(B, T).doit() == B*T - T*B sympy-0.7.4.1/sympy/physics/quantum/tests/test_state.py0000644000175000017500000001372612253362407023505 0ustar georgeskgeorgeskfrom sympy import (Add, conjugate, diff, I, Integer, latex, Mul, oo, pi, Pow, pretty, Rational, sin, sqrt, Symbol, symbols, sympify) from sympy.utilities.pytest import raises from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.qexpr import QExpr from sympy.physics.quantum.state import ( Ket, Bra, TimeDepKet, TimeDepBra, KetBase, BraBase, StateBase, Wavefunction ) from sympy.physics.quantum.hilbert import HilbertSpace x, y, t = symbols('x,y,t') class CustomKet(Ket): @classmethod def default_args(self): return ("test",) class CustomKetMultipleLabels(Ket): @classmethod def default_args(self): return ("r", "theta", "phi") class CustomTimeDepKet(TimeDepKet): @classmethod def default_args(self): return ("test", "t") class CustomTimeDepKetMultipleLabels(TimeDepKet): @classmethod def default_args(self): return ("r", "theta", "phi", "t") def test_ket(): k = Ket('0') assert isinstance(k, Ket) assert isinstance(k, KetBase) assert isinstance(k, StateBase) assert isinstance(k, QExpr) assert k.label == (Symbol('0'),) assert k.hilbert_space == HilbertSpace() assert k.is_commutative is False # Make sure this doesn't get converted to the number pi. k = Ket('pi') assert k.label == (Symbol('pi'),) k = Ket(x, y) assert k.label == (x, y) assert k.hilbert_space == HilbertSpace() assert k.is_commutative is False assert k.dual_class() == Bra assert k.dual == Bra(x, y) assert k.subs(x, y) == Ket(y, y) k = CustomKet() assert k == CustomKet("test") k = CustomKetMultipleLabels() assert k == CustomKetMultipleLabels("r", "theta", "phi") assert Ket() == Ket('psi') def test_bra(): b = Bra('0') assert isinstance(b, Bra) assert isinstance(b, BraBase) assert isinstance(b, StateBase) assert isinstance(b, QExpr) assert b.label == (Symbol('0'),) assert b.hilbert_space == HilbertSpace() assert b.is_commutative is False # Make sure this doesn't get converted to the number pi. b = Bra('pi') assert b.label == (Symbol('pi'),) b = Bra(x, y) assert b.label == (x, y) assert b.hilbert_space == HilbertSpace() assert b.is_commutative is False assert b.dual_class() == Ket assert b.dual == Ket(x, y) assert b.subs(x, y) == Bra(y, y) assert Bra() == Bra('psi') def test_ops(): k0 = Ket(0) k1 = Ket(1) k = 2*I*k0 - (x/sqrt(2))*k1 assert k == Add(Mul(2, I, k0), Mul(Rational(-1, 2), x, Pow(2, Rational(1, 2)), k1)) def test_time_dep_ket(): k = TimeDepKet(0, t) assert isinstance(k, TimeDepKet) assert isinstance(k, KetBase) assert isinstance(k, StateBase) assert isinstance(k, QExpr) assert k.label == (Integer(0),) assert k.args == (Integer(0), t) assert k.time == t assert k.dual_class() == TimeDepBra assert k.dual == TimeDepBra(0, t) assert k.subs(t, 2) == TimeDepKet(0, 2) k = TimeDepKet(x, 0.5) assert k.label == (x,) assert k.args == (x, sympify(0.5)) k = CustomTimeDepKet() assert k.label == (Symbol("test"),) assert k.time == Symbol("t") assert k == CustomTimeDepKet("test", "t") k = CustomTimeDepKetMultipleLabels() assert k.label == (Symbol("r"), Symbol("theta"), Symbol("phi")) assert k.time == Symbol("t") assert k == CustomTimeDepKetMultipleLabels("r", "theta", "phi", "t") assert TimeDepKet() == TimeDepKet("psi", "t") def test_time_dep_bra(): b = TimeDepBra(0, t) assert isinstance(b, TimeDepBra) assert isinstance(b, BraBase) assert isinstance(b, StateBase) assert isinstance(b, QExpr) assert b.label == (Integer(0),) assert b.args == (Integer(0), t) assert b.time == t assert b.dual_class() == TimeDepKet assert b.dual == TimeDepKet(0, t) k = TimeDepBra(x, 0.5) assert k.label == (x,) assert k.args == (x, sympify(0.5)) assert TimeDepBra() == TimeDepBra("psi", "t") def test_bra_ket_dagger(): x = symbols('x', complex=True) k = Ket('k') b = Bra('b') assert Dagger(k) == Bra('k') assert Dagger(b) == Ket('b') assert Dagger(k).is_commutative is False k2 = Ket('k2') e = 2*I*k + x*k2 assert Dagger(e) == conjugate(x)*Dagger(k2) - 2*I*Dagger(k) def test_wavefunction(): x, y = symbols('x y', real=True) L = symbols('L', positive=True) n = symbols('n', integer=True, positive=True) f = Wavefunction(x**2, x) p = f.prob() lims = f.limits assert f.is_normalized is False assert f.norm == oo assert f(10) == 100 assert p(10) == 10000 assert lims[x] == (-oo, oo) assert diff(f, x) == Wavefunction(2*x, x) raises(NotImplementedError, lambda: f.normalize()) assert conjugate(f) == Wavefunction(conjugate(f.expr), x) assert conjugate(f) == Dagger(f) g = Wavefunction(x**2*y + y**2*x, (x, 0, 1), (y, 0, 2)) lims_g = g.limits assert lims_g[x] == (0, 1) assert lims_g[y] == (0, 2) assert g.is_normalized is False assert g.norm == sqrt(42)/3 assert g(2, 4) == 0 assert g(1, 1) == 2 assert diff(diff(g, x), y) == Wavefunction(2*x + 2*y, (x, 0, 1), (y, 0, 2)) assert conjugate(g) == Wavefunction(conjugate(g.expr), *g.args[1:]) assert conjugate(g) == Dagger(g) h = Wavefunction(sqrt(5)*x**2, (x, 0, 1)) assert h.is_normalized is True assert h.normalize() == h assert conjugate(h) == Wavefunction(conjugate(h.expr), (x, 0, 1)) assert conjugate(h) == Dagger(h) piab = Wavefunction(sin(n*pi*x/L), (x, 0, L)) assert piab.norm == sqrt(L/2) assert piab(L + 1) == 0 assert piab(0.5) == sin(0.5*n*pi/L) assert piab(0.5, n=1, L=1) == sin(0.5*pi) assert piab.normalize() == \ Wavefunction(sqrt(2)/sqrt(L)*sin(n*pi*x/L), (x, 0, L)) assert conjugate(piab) == Wavefunction(conjugate(piab.expr), (x, 0, L)) assert conjugate(piab) == Dagger(piab) k = Wavefunction(x**2, 'x') assert type(k.variables[0]) == Symbol sympy-0.7.4.1/sympy/physics/quantum/tests/test_anticommutator.py0000644000175000017500000000235612253362407025430 0ustar georgeskgeorgeskfrom sympy import symbols, Integer from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.anticommutator import AntiCommutator as AComm from sympy.physics.quantum.operator import Operator a, b, c = symbols('a,b,c') A, B, C, D = symbols('A,B,C,D', commutative=False) def test_anticommutator(): ac = AComm(A, B) assert isinstance(ac, AComm) assert ac.is_commutative is False assert ac.subs(A, C) == AComm(C, B) def test_commutator_identities(): assert AComm(a*A, b*B) == a*b*AComm(A, B) assert AComm(A, A) == 2*A**2 assert AComm(A, B) == AComm(B, A) assert AComm(a, b) == 2*a*b assert AComm(A, B).doit() == A*B + B*A def test_anticommutator_dagger(): assert Dagger(AComm(A, B)) == AComm(Dagger(A), Dagger(B)) class Foo(Operator): def _eval_anticommutator_Bar(self, bar): return Integer(0) class Bar(Operator): pass class Tam(Operator): def _eval_anticommutator_Foo(self, foo): return Integer(1) def test_eval_commutator(): F = Foo('F') B = Bar('B') T = Tam('T') assert AComm(F, B).doit() == 0 assert AComm(B, F).doit() == 0 assert AComm(F, T).doit() == 1 assert AComm(T, F).doit() == 1 assert AComm(B, T).doit() == B*T + T*B sympy-0.7.4.1/sympy/physics/quantum/tests/test_circuitplot.py0000644000175000017500000000402712253362407024720 0ustar georgeskgeorgeskfrom sympy.physics.quantum.circuitplot import labeller, render_label, Mz, CreateOneQubitGate,\ CreateCGate from sympy.physics.quantum.gate import CNOT, H, X, Z, SWAP, CGate, S, T from sympy.external import import_module from sympy.utilities.pytest import skip mpl = import_module('matplotlib') def test_render_label(): assert render_label('q0') == r'$|q0\rangle$' assert render_label('q0', {'q0': '0'}) == r'$|q0\rangle=|0\rangle$' def test_Mz(): assert str(Mz(0)) == 'Mz(0)' def test_create1(): Qgate = CreateOneQubitGate('Q') assert str(Qgate(0)) == 'Q(0)' def test_createc(): Qgate = CreateCGate('Q') assert str(Qgate([1],0)) == 'C((1),Q(0))' def test_labeller(): """Test the labeller utility""" assert labeller(2) == ['q_1', 'q_0'] assert labeller(3,'j') == ['j_2', 'j_1', 'j_0'] def test_cnot(): """Test a simple cnot circuit. Right now this only makes sure the code doesn't raise an exception, and some simple properties """ if not mpl: skip("matplotlib not installed") else: from sympy.physics.quantum.circuitplot import CircuitPlot c = CircuitPlot(CNOT(1,0),2,labels=labeller(2)) assert c.ngates == 2 assert c.nqubits == 2 assert c.labels == ['q_1', 'q_0'] c = CircuitPlot(CNOT(1,0),2) assert c.ngates == 2 assert c.nqubits == 2 assert c.labels == [] def test_ex1(): if not mpl: skip("matplotlib not installed") else: from sympy.physics.quantum.circuitplot import CircuitPlot c = CircuitPlot(CNOT(1,0)*H(1),2,labels=labeller(2)) assert c.ngates == 2 assert c.nqubits == 2 assert c.labels == ['q_1', 'q_0'] def test_ex4(): if not mpl: skip("matplotlib not installed") else: from sympy.physics.quantum.circuitplot import CircuitPlot c = CircuitPlot(SWAP(0,2)*H(0)* CGate((0,),S(1)) *H(1)*CGate((0,),T(2))\ *CGate((1,),S(2))*H(2),3,labels=labeller(3,'j')) assert c.ngates == 7 assert c.nqubits == 3 assert c.labels == ['j_2', 'j_1', 'j_0'] sympy-0.7.4.1/sympy/physics/quantum/tests/test_matrixutils.py0000644000175000017500000000775412253362407024756 0ustar georgeskgeorgeskfrom sympy import Matrix, zeros, ones, Integer from sympy.physics.quantum.matrixutils import ( to_sympy, to_numpy, to_scipy_sparse, matrix_tensor_product, matrix_to_zero, matrix_zeros, numpy_ndarray, scipy_sparse_matrix ) from sympy.external import import_module from sympy.utilities.pytest import skip m = Matrix([[1, 2], [3, 4]]) def test_sympy_to_sympy(): assert to_sympy(m) == m def test_matrix_to_zero(): assert matrix_to_zero(m) == m assert matrix_to_zero(Matrix([[0, 0], [0, 0]])) == Integer(0) np = import_module('numpy') def test_to_numpy(): if not np: skip("numpy not installed.") result = np.matrix([[1, 2], [3, 4]], dtype='complex') assert (to_numpy(m) == result).all() def test_matrix_tensor_product(): if not np: skip("numpy not installed.") l1 = zeros(4) for i in range(16): l1[i] = 2**i l2 = zeros(4) for i in range(16): l2[i] = i l3 = zeros(2) for i in range(4): l3[i] = i vec = Matrix([1, 2, 3]) #test for Matrix known 4x4 matricies numpyl1 = np.matrix(l1.tolist()) numpyl2 = np.matrix(l2.tolist()) numpy_product = np.kron(numpyl1, numpyl2) args = [l1, l2] sympy_product = matrix_tensor_product(*args) assert numpy_product.tolist() == sympy_product.tolist() numpy_product = np.kron(numpyl2, numpyl1) args = [l2, l1] sympy_product = matrix_tensor_product(*args) assert numpy_product.tolist() == sympy_product.tolist() #test for other known matrix of different dimensions numpyl2 = np.matrix(l3.tolist()) numpy_product = np.kron(numpyl1, numpyl2) args = [l1, l3] sympy_product = matrix_tensor_product(*args) assert numpy_product.tolist() == sympy_product.tolist() numpy_product = np.kron(numpyl2, numpyl1) args = [l3, l1] sympy_product = matrix_tensor_product(*args) assert numpy_product.tolist() == sympy_product.tolist() #test for non square matrix numpyl2 = np.matrix(vec.tolist()) numpy_product = np.kron(numpyl1, numpyl2) args = [l1, vec] sympy_product = matrix_tensor_product(*args) assert numpy_product.tolist() == sympy_product.tolist() numpy_product = np.kron(numpyl2, numpyl1) args = [vec, l1] sympy_product = matrix_tensor_product(*args) assert numpy_product.tolist() == sympy_product.tolist() #test for random matrix with random values that are floats random_matrix1 = np.random.rand(np.random.rand()*5 + 1, np.random.rand()*5 + 1) random_matrix2 = np.random.rand(np.random.rand()*5 + 1, np.random.rand()*5 + 1) numpy_product = np.kron(random_matrix1, random_matrix2) args = [Matrix(random_matrix1.tolist()), Matrix(random_matrix2.tolist())] sympy_product = matrix_tensor_product(*args) assert not (sympy_product - Matrix(numpy_product.tolist())).tolist() > \ (ones(sympy_product.rows, sympy_product.cols)*epsilon).tolist() #test for three matrix kronecker sympy_product = matrix_tensor_product(l1, vec, l2) numpy_product = np.kron(l1, np.kron(vec, l2)) assert numpy_product.tolist() == sympy_product.tolist() scipy = import_module('scipy', __import__kwargs={'fromlist': ['sparse']}) def test_to_scipy_sparse(): if not np: skip("numpy not installed.") if not scipy: skip("scipy not installed.") else: sparse = scipy.sparse result = sparse.csr_matrix([[1, 2], [3, 4]], dtype='complex') assert np.linalg.norm((to_scipy_sparse(m) - result).todense()) == 0.0 epsilon = .000001 def test_matrix_zeros_sympy(): sym = matrix_zeros(4, 4, format='sympy') assert isinstance(sym, Matrix) def test_matrix_zeros_numpy(): if not np: skip("numpy not installed.") num = matrix_zeros(4, 4, format='numpy') assert isinstance(num, numpy_ndarray) def test_matrix_zeros_scipy(): if not np: skip("numpy not installed.") if not scipy: skip("scipy not installed.") sci = matrix_zeros(4, 4, format='scipy.sparse') assert isinstance(sci, scipy_sparse_matrix) sympy-0.7.4.1/sympy/physics/quantum/tests/test_grover.py0000644000175000017500000000633612253362407023670 0ustar georgeskgeorgeskfrom sympy import sqrt from sympy.physics.quantum.qapply import qapply from sympy.physics.quantum.qubit import IntQubit from sympy.physics.quantum.grover import (apply_grover, superposition_basis, OracleGate, grover_iteration, WGate) def return_one_on_two(qubits): return qubits == IntQubit(2, qubits.nqubits) def return_one_on_one(qubits): return qubits == IntQubit(1, qubits.nqubits) def test_superposition_basis(): nbits = 2 first_half_state = IntQubit(0, nbits)/2 + IntQubit(1, nbits)/2 second_half_state = IntQubit(2, nbits)/2 + IntQubit(3, nbits)/2 assert first_half_state + second_half_state == superposition_basis(nbits) nbits = 3 firstq = (1/sqrt(8))*IntQubit(0, nbits) + (1/sqrt(8))*IntQubit(1, nbits) secondq = (1/sqrt(8))*IntQubit(2, nbits) + (1/sqrt(8))*IntQubit(3, nbits) thirdq = (1/sqrt(8))*IntQubit(4, nbits) + (1/sqrt(8))*IntQubit(5, nbits) fourthq = (1/sqrt(8))*IntQubit(6, nbits) + (1/sqrt(8))*IntQubit(7, nbits) assert firstq + secondq + thirdq + fourthq == superposition_basis(nbits) def test_OracleGate(): v = OracleGate(1, lambda qubits: qubits == IntQubit(0)) assert qapply(v*IntQubit(0)) == -IntQubit(0) assert qapply(v*IntQubit(1)) == IntQubit(1) nbits = 2 v = OracleGate(2, return_one_on_two) assert qapply(v*IntQubit(0, nbits)) == IntQubit(0, nbits) assert qapply(v*IntQubit(1, nbits)) == IntQubit(1, nbits) assert qapply(v*IntQubit(2, nbits)) == -IntQubit(2, nbits) assert qapply(v*IntQubit(3, nbits)) == IntQubit(3, nbits) def test_WGate(): nqubits = 2 basis_states = superposition_basis(nqubits) assert qapply(WGate(nqubits)*basis_states) == basis_states expected = ((2/sqrt(pow(2, nqubits)))*basis_states) - IntQubit(1, nqubits) assert qapply(WGate(nqubits)*IntQubit(1, nqubits)) == expected def test_grover_iteration_1(): numqubits = 2 basis_states = superposition_basis(numqubits) v = OracleGate(numqubits, return_one_on_one) expected = IntQubit(1, numqubits) assert qapply(grover_iteration(basis_states, v)) == expected def test_grover_iteration_2(): numqubits = 4 basis_states = superposition_basis(numqubits) v = OracleGate(numqubits, return_one_on_two) # After (pi/4)sqrt(pow(2, n)), IntQubit(2) should have highest prob # In this case, after around pi times (3 or 4) # print '' # print basis_states iterated = grover_iteration(basis_states, v) iterated = qapply(iterated) # print iterated iterated = grover_iteration(iterated, v) iterated = qapply(iterated) # print iterated iterated = grover_iteration(iterated, v) iterated = qapply(iterated) # print iterated # In this case, probability was highest after 3 iterations # Probability of Qubit('0010') was 251/256 (3) vs 781/1024 (4) # Ask about measurement expected = (-13*basis_states)/64 + 264*IntQubit(2, numqubits)/256 assert qapply(expected) == iterated def test_grover(): nqubits = 2 assert apply_grover(return_one_on_one, nqubits) == IntQubit(1, nqubits) nqubits = 4 basis_states = superposition_basis(nqubits) expected = (-13*basis_states)/64 + 264*IntQubit(2, nqubits)/256 assert apply_grover(return_one_on_two, 4) == qapply(expected) sympy-0.7.4.1/sympy/physics/quantum/tests/test_hilbert.py0000644000175000017500000000472112253362407024011 0ustar georgeskgeorgeskfrom sympy.physics.quantum.hilbert import ( HilbertSpace, ComplexSpace, L2, FockSpace, TensorProductHilbertSpace, DirectSumHilbertSpace, TensorPowerHilbertSpace ) from sympy import Interval, oo, Symbol, sstr, srepr def test_hilbert_space(): hs = HilbertSpace() assert isinstance(hs, HilbertSpace) assert sstr(hs) == 'H' assert srepr(hs) == 'HilbertSpace()' def test_complex_space(): c1 = ComplexSpace(2) assert isinstance(c1, ComplexSpace) assert c1.dimension == 2 assert sstr(c1) == 'C(2)' assert srepr(c1) == 'ComplexSpace(Integer(2))' n = Symbol('n') c2 = ComplexSpace(n) assert isinstance(c2, ComplexSpace) assert c2.dimension == n assert sstr(c2) == 'C(n)' assert srepr(c2) == "ComplexSpace(Symbol('n'))" assert c2.subs(n, 2) == ComplexSpace(2) def test_L2(): b1 = L2(Interval(-oo, 1)) assert isinstance(b1, L2) assert b1.dimension == oo assert b1.interval == Interval(-oo, 1) x = Symbol('x', real=True) y = Symbol('y', real=True) b2 = L2(Interval(x, y)) assert b2.dimension == oo assert b2.interval == Interval(x, y) assert b2.subs(x, -1) == L2(Interval(-1, y)) def test_fock_space(): f1 = FockSpace() f2 = FockSpace() assert isinstance(f1, FockSpace) assert f1.dimension == oo assert f1 == f2 def test_tensor_product(): n = Symbol('n') hs1 = ComplexSpace(2) hs2 = ComplexSpace(n) h = hs1*hs2 assert isinstance(h, TensorProductHilbertSpace) assert h.dimension == 2*n assert h.spaces == (hs1, hs2) h = hs2*hs2 assert isinstance(h, TensorPowerHilbertSpace) assert h.base == hs2 assert h.exp == 2 assert h.dimension == n**2 f = FockSpace() h = hs1*hs2*f assert h.dimension == oo def test_tensor_power(): n = Symbol('n') hs1 = ComplexSpace(2) hs2 = ComplexSpace(n) h = hs1**2 assert isinstance(h, TensorPowerHilbertSpace) assert h.base == hs1 assert h.exp == 2 assert h.dimension == 4 h = hs2**3 assert isinstance(h, TensorPowerHilbertSpace) assert h.base == hs2 assert h.exp == 3 assert h.dimension == n**3 def test_direct_sum(): n = Symbol('n') hs1 = ComplexSpace(2) hs2 = ComplexSpace(n) h = hs1 + hs2 assert isinstance(h, DirectSumHilbertSpace) assert h.dimension == 2 + n assert h.spaces == (hs1, hs2) f = FockSpace() h = hs1 + f + hs2 assert h.dimension == oo assert h.spaces == (hs1, f, hs2) sympy-0.7.4.1/sympy/physics/quantum/tests/test_spin.py0000644000175000017500000116165412253362407023343 0ustar georgeskgeorgeskfrom __future__ import division from sympy import cos, exp, expand, I, Matrix, pi, S, sin, sqrt, Sum, symbols from sympy.abc import alpha, beta, gamma, j, m from sympy.physics.quantum import hbar, represent, Commutator, InnerProduct from sympy.physics.quantum.qapply import qapply from sympy.physics.quantum.tensorproduct import TensorProduct from sympy.physics.quantum.cg import CG from sympy.physics.quantum.spin import ( Jx, Jy, Jz, Jplus, Jminus, J2, JxBra, JyBra, JzBra, JxKet, JyKet, JzKet, JxKetCoupled, JyKetCoupled, JzKetCoupled, couple, uncouple, Rotation, WignerD ) from sympy.utilities.pytest import raises j1, j2, j3, j4, m1, m2, m3, m4 = symbols('j1:5 m1:5') j12, j13, j24, j34, j123, j134, mi, mi1, mp = symbols( 'j12 j13 j24 j34 j123 j134 mi mi1 mp') def test_represent_spin_operators(): assert represent(Jx) == hbar*Matrix([[0, 1], [1, 0]])/2 assert represent( Jx, j=1) == hbar*sqrt(2)*Matrix([[0, 1, 0], [1, 0, 1], [0, 1, 0]])/2 assert represent(Jy) == hbar*I*Matrix([[0, -1], [1, 0]])/2 assert represent(Jy, j=1) == hbar*I*sqrt(2)*Matrix([[0, -1, 0], [1, 0, -1], [0, 1, 0]])/2 assert represent(Jz) == hbar*Matrix([[1, 0], [0, -1]])/2 assert represent( Jz, j=1) == hbar*Matrix([[1, 0, 0], [0, 0, 0], [0, 0, -1]]) def test_represent_spin_states(): # Jx basis assert represent(JxKet(S(1)/2, S(1)/2), basis=Jx) == Matrix([1, 0]) assert represent(JxKet(S(1)/2, -S(1)/2), basis=Jx) == Matrix([0, 1]) assert represent(JxKet(1, 1), basis=Jx) == Matrix([1, 0, 0]) assert represent(JxKet(1, 0), basis=Jx) == Matrix([0, 1, 0]) assert represent(JxKet(1, -1), basis=Jx) == Matrix([0, 0, 1]) assert represent( JyKet(S(1)/2, S(1)/2), basis=Jx) == Matrix([exp(-I*pi/4), 0]) assert represent( JyKet(S(1)/2, -S(1)/2), basis=Jx) == Matrix([0, exp(I*pi/4)]) assert represent(JyKet(1, 1), basis=Jx) == Matrix([-I, 0, 0]) assert represent(JyKet(1, 0), basis=Jx) == Matrix([0, 1, 0]) assert represent(JyKet(1, -1), basis=Jx) == Matrix([0, 0, I]) assert represent( JzKet(S(1)/2, S(1)/2), basis=Jx) == sqrt(2)*Matrix([-1, 1])/2 assert represent( JzKet(S(1)/2, -S(1)/2), basis=Jx) == sqrt(2)*Matrix([-1, -1])/2 assert represent(JzKet(1, 1), basis=Jx) == Matrix([1, -sqrt(2), 1])/2 assert represent(JzKet(1, 0), basis=Jx) == sqrt(2)*Matrix([1, 0, -1])/2 assert represent(JzKet(1, -1), basis=Jx) == Matrix([1, sqrt(2), 1])/2 # Jy basis assert represent( JxKet(S(1)/2, S(1)/2), basis=Jy) == Matrix([exp(-3*I*pi/4), 0]) assert represent( JxKet(S(1)/2, -S(1)/2), basis=Jy) == Matrix([0, exp(3*I*pi/4)]) assert represent(JxKet(1, 1), basis=Jy) == Matrix([I, 0, 0]) assert represent(JxKet(1, 0), basis=Jy) == Matrix([0, 1, 0]) assert represent(JxKet(1, -1), basis=Jy) == Matrix([0, 0, -I]) assert represent(JyKet(S(1)/2, S(1)/2), basis=Jy) == Matrix([1, 0]) assert represent(JyKet(S(1)/2, -S(1)/2), basis=Jy) == Matrix([0, 1]) assert represent(JyKet(1, 1), basis=Jy) == Matrix([1, 0, 0]) assert represent(JyKet(1, 0), basis=Jy) == Matrix([0, 1, 0]) assert represent(JyKet(1, -1), basis=Jy) == Matrix([0, 0, 1]) assert represent( JzKet(S(1)/2, S(1)/2), basis=Jy) == sqrt(2)*Matrix([-1, I])/2 assert represent( JzKet(S(1)/2, -S(1)/2), basis=Jy) == sqrt(2)*Matrix([I, -1])/2 assert represent(JzKet(1, 1), basis=Jy) == Matrix([1, -I*sqrt(2), -1])/2 assert represent( JzKet(1, 0), basis=Jy) == Matrix([-sqrt(2)*I, 0, -sqrt(2)*I])/2 assert represent(JzKet(1, -1), basis=Jy) == Matrix([-1, -sqrt(2)*I, 1])/2 # Jz basis assert represent( JxKet(S(1)/2, S(1)/2), basis=Jz) == sqrt(2)*Matrix([1, 1])/2 assert represent( JxKet(S(1)/2, -S(1)/2), basis=Jz) == sqrt(2)*Matrix([-1, 1])/2 assert represent(JxKet(1, 1), basis=Jz) == Matrix([1, sqrt(2), 1])/2 assert represent(JxKet(1, 0), basis=Jz) == sqrt(2)*Matrix([-1, 0, 1])/2 assert represent(JxKet(1, -1), basis=Jz) == Matrix([1, -sqrt(2), 1])/2 assert represent( JyKet(S(1)/2, S(1)/2), basis=Jz) == sqrt(2)*Matrix([-1, -I])/2 assert represent( JyKet(S(1)/2, -S(1)/2), basis=Jz) == sqrt(2)*Matrix([-I, -1])/2 assert represent(JyKet(1, 1), basis=Jz) == Matrix([1, sqrt(2)*I, -1])/2 assert represent(JyKet(1, 0), basis=Jz) == sqrt(2)*Matrix([I, 0, I])/2 assert represent(JyKet(1, -1), basis=Jz) == Matrix([-1, sqrt(2)*I, 1])/2 assert represent(JzKet(S(1)/2, S(1)/2), basis=Jz) == Matrix([1, 0]) assert represent(JzKet(S(1)/2, -S(1)/2), basis=Jz) == Matrix([0, 1]) assert represent(JzKet(1, 1), basis=Jz) == Matrix([1, 0, 0]) assert represent(JzKet(1, 0), basis=Jz) == Matrix([0, 1, 0]) assert represent(JzKet(1, -1), basis=Jz) == Matrix([0, 0, 1]) def test_represent_uncoupled_states(): # Jx basis assert represent(TensorProduct(JxKet(S(1)/2, S(1)/2), JxKet(S(1)/2, S(1)/2)), basis=Jx) == \ Matrix([1, 0, 0, 0]) assert represent(TensorProduct(JxKet(S(1)/2, S(1)/2), JxKet(S(1)/2, -S(1)/2)), basis=Jx) == \ Matrix([0, 1, 0, 0]) assert represent(TensorProduct(JxKet(S(1)/2, -S(1)/2), JxKet(S(1)/2, S(1)/2)), basis=Jx) == \ Matrix([0, 0, 1, 0]) assert represent(TensorProduct(JxKet(S(1)/2, -S(1)/2), JxKet(S(1)/2, -S(1)/2)), basis=Jx) == \ Matrix([0, 0, 0, 1]) assert represent(TensorProduct(JyKet(S(1)/2, S(1)/2), JyKet(S(1)/2, S(1)/2)), basis=Jx) == \ Matrix([-I, 0, 0, 0]) assert represent(TensorProduct(JyKet(S(1)/2, S(1)/2), JyKet(S(1)/2, -S(1)/2)), basis=Jx) == \ Matrix([0, 1, 0, 0]) assert represent(TensorProduct(JyKet(S(1)/2, -S(1)/2), JyKet(S(1)/2, S(1)/2)), basis=Jx) == \ Matrix([0, 0, 1, 0]) assert represent(TensorProduct(JyKet(S(1)/2, -S(1)/2), JyKet(S(1)/2, -S(1)/2)), basis=Jx) == \ Matrix([0, 0, 0, I]) assert represent(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)), basis=Jx) == \ Matrix([S(1)/2, -S(1)/2, -S(1)/2, S(1)/2]) assert represent(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2)), basis=Jx) == \ Matrix([S(1)/2, S(1)/2, -S(1)/2, -S(1)/2]) assert represent(TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2)), basis=Jx) == \ Matrix([S(1)/2, -S(1)/2, S(1)/2, -S(1)/2]) assert represent(TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2)), basis=Jx) == \ Matrix([S(1)/2, S(1)/2, S(1)/2, S(1)/2]) # Jy basis assert represent(TensorProduct(JxKet(S(1)/2, S(1)/2), JxKet(S(1)/2, S(1)/2)), basis=Jy) == \ Matrix([I, 0, 0, 0]) assert represent(TensorProduct(JxKet(S(1)/2, S(1)/2), JxKet(S(1)/2, -S(1)/2)), basis=Jy) == \ Matrix([0, 1, 0, 0]) assert represent(TensorProduct(JxKet(S(1)/2, -S(1)/2), JxKet(S(1)/2, S(1)/2)), basis=Jy) == \ Matrix([0, 0, 1, 0]) assert represent(TensorProduct(JxKet(S(1)/2, -S(1)/2), JxKet(S(1)/2, -S(1)/2)), basis=Jy) == \ Matrix([0, 0, 0, -I]) assert represent(TensorProduct(JyKet(S(1)/2, S(1)/2), JyKet(S(1)/2, S(1)/2)), basis=Jy) == \ Matrix([1, 0, 0, 0]) assert represent(TensorProduct(JyKet(S(1)/2, S(1)/2), JyKet(S(1)/2, -S(1)/2)), basis=Jy) == \ Matrix([0, 1, 0, 0]) assert represent(TensorProduct(JyKet(S(1)/2, -S(1)/2), JyKet(S(1)/2, S(1)/2)), basis=Jy) == \ Matrix([0, 0, 1, 0]) assert represent(TensorProduct(JyKet(S(1)/2, -S(1)/2), JyKet(S(1)/2, -S(1)/2)), basis=Jy) == \ Matrix([0, 0, 0, 1]) assert represent(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)), basis=Jy) == \ Matrix([S(1)/2, -I/2, -I/2, -S(1)/2]) assert represent(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2)), basis=Jy) == \ Matrix([-I/2, S(1)/2, -S(1)/2, -I/2]) assert represent(TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2)), basis=Jy) == \ Matrix([-I/2, -S(1)/2, S(1)/2, -I/2]) assert represent(TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2)), basis=Jy) == \ Matrix([-S(1)/2, -I/2, -I/2, S(1)/2]) # Jz basis assert represent(TensorProduct(JxKet(S(1)/2, S(1)/2), JxKet(S(1)/2, S(1)/2)), basis=Jz) == \ Matrix([S(1)/2, S(1)/2, S(1)/2, S(1)/2]) assert represent(TensorProduct(JxKet(S(1)/2, S(1)/2), JxKet(S(1)/2, -S(1)/2)), basis=Jz) == \ Matrix([-S(1)/2, S(1)/2, -S(1)/2, S(1)/2]) assert represent(TensorProduct(JxKet(S(1)/2, -S(1)/2), JxKet(S(1)/2, S(1)/2)), basis=Jz) == \ Matrix([-S(1)/2, -S(1)/2, S(1)/2, S(1)/2]) assert represent(TensorProduct(JxKet(S(1)/2, -S(1)/2), JxKet(S(1)/2, -S(1)/2)), basis=Jz) == \ Matrix([S(1)/2, -S(1)/2, -S(1)/2, S(1)/2]) assert represent(TensorProduct(JyKet(S(1)/2, S(1)/2), JyKet(S(1)/2, S(1)/2)), basis=Jz) == \ Matrix([S(1)/2, I/2, I/2, -S(1)/2]) assert represent(TensorProduct(JyKet(S(1)/2, S(1)/2), JyKet(S(1)/2, -S(1)/2)), basis=Jz) == \ Matrix([I/2, S(1)/2, -S(1)/2, I/2]) assert represent(TensorProduct(JyKet(S(1)/2, -S(1)/2), JyKet(S(1)/2, S(1)/2)), basis=Jz) == \ Matrix([I/2, -S(1)/2, S(1)/2, I/2]) assert represent(TensorProduct(JyKet(S(1)/2, -S(1)/2), JyKet(S(1)/2, -S(1)/2)), basis=Jz) == \ Matrix([-S(1)/2, I/2, I/2, S(1)/2]) assert represent(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)), basis=Jz) == \ Matrix([1, 0, 0, 0]) assert represent(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2)), basis=Jz) == \ Matrix([0, 1, 0, 0]) assert represent(TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2)), basis=Jz) == \ Matrix([0, 0, 1, 0]) assert represent(TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2)), basis=Jz) == \ Matrix([0, 0, 0, 1]) def test_represent_coupled_states(): # Jx basis assert represent(JxKetCoupled(0, 0, (S(1)/2, S(1)/2)), basis=Jx) == \ Matrix([1, 0, 0, 0]) assert represent(JxKetCoupled(1, 1, (S(1)/2, S(1)/2)), basis=Jx) == \ Matrix([0, 1, 0, 0]) assert represent(JxKetCoupled(1, 0, (S(1)/2, S(1)/2)), basis=Jx) == \ Matrix([0, 0, 1, 0]) assert represent(JxKetCoupled(1, -1, (S(1)/2, S(1)/2)), basis=Jx) == \ Matrix([0, 0, 0, 1]) assert represent(JyKetCoupled(0, 0, (S(1)/2, S(1)/2)), basis=Jx) == \ Matrix([1, 0, 0, 0]) assert represent(JyKetCoupled(1, 1, (S(1)/2, S(1)/2)), basis=Jx) == \ Matrix([0, -I, 0, 0]) assert represent(JyKetCoupled(1, 0, (S(1)/2, S(1)/2)), basis=Jx) == \ Matrix([0, 0, 1, 0]) assert represent(JyKetCoupled(1, -1, (S(1)/2, S(1)/2)), basis=Jx) == \ Matrix([0, 0, 0, I]) assert represent(JzKetCoupled(0, 0, (S(1)/2, S(1)/2)), basis=Jx) == \ Matrix([1, 0, 0, 0]) assert represent(JzKetCoupled(1, 1, (S(1)/2, S(1)/2)), basis=Jx) == \ Matrix([0, S(1)/2, -sqrt(2)/2, S(1)/2]) assert represent(JzKetCoupled(1, 0, (S(1)/2, S(1)/2)), basis=Jx) == \ Matrix([0, sqrt(2)/2, 0, -sqrt(2)/2]) assert represent(JzKetCoupled(1, -1, (S(1)/2, S(1)/2)), basis=Jx) == \ Matrix([0, S(1)/2, sqrt(2)/2, S(1)/2]) # Jy basis assert represent(JxKetCoupled(0, 0, (S(1)/2, S(1)/2)), basis=Jy) == \ Matrix([1, 0, 0, 0]) assert represent(JxKetCoupled(1, 1, (S(1)/2, S(1)/2)), basis=Jy) == \ Matrix([0, I, 0, 0]) assert represent(JxKetCoupled(1, 0, (S(1)/2, S(1)/2)), basis=Jy) == \ Matrix([0, 0, 1, 0]) assert represent(JxKetCoupled(1, -1, (S(1)/2, S(1)/2)), basis=Jy) == \ Matrix([0, 0, 0, -I]) assert represent(JyKetCoupled(0, 0, (S(1)/2, S(1)/2)), basis=Jy) == \ Matrix([1, 0, 0, 0]) assert represent(JyKetCoupled(1, 1, (S(1)/2, S(1)/2)), basis=Jy) == \ Matrix([0, 1, 0, 0]) assert represent(JyKetCoupled(1, 0, (S(1)/2, S(1)/2)), basis=Jy) == \ Matrix([0, 0, 1, 0]) assert represent(JyKetCoupled(1, -1, (S(1)/2, S(1)/2)), basis=Jy) == \ Matrix([0, 0, 0, 1]) assert represent(JzKetCoupled(0, 0, (S(1)/2, S(1)/2)), basis=Jy) == \ Matrix([1, 0, 0, 0]) assert represent(JzKetCoupled(1, 1, (S(1)/2, S(1)/2)), basis=Jy) == \ Matrix([0, S(1)/2, -I*sqrt(2)/2, -S(1)/2]) assert represent(JzKetCoupled(1, 0, (S(1)/2, S(1)/2)), basis=Jy) == \ Matrix([0, -I*sqrt(2)/2, 0, -I*sqrt(2)/2]) assert represent(JzKetCoupled(1, -1, (S(1)/2, S(1)/2)), basis=Jy) == \ Matrix([0, -S(1)/2, -I*sqrt(2)/2, S(1)/2]) # Jz basis assert represent(JxKetCoupled(0, 0, (S(1)/2, S(1)/2)), basis=Jz) == \ Matrix([1, 0, 0, 0]) assert represent(JxKetCoupled(1, 1, (S(1)/2, S(1)/2)), basis=Jz) == \ Matrix([0, S(1)/2, sqrt(2)/2, S(1)/2]) assert represent(JxKetCoupled(1, 0, (S(1)/2, S(1)/2)), basis=Jz) == \ Matrix([0, -sqrt(2)/2, 0, sqrt(2)/2]) assert represent(JxKetCoupled(1, -1, (S(1)/2, S(1)/2)), basis=Jz) == \ Matrix([0, S(1)/2, -sqrt(2)/2, S(1)/2]) assert represent(JyKetCoupled(0, 0, (S(1)/2, S(1)/2)), basis=Jz) == \ Matrix([1, 0, 0, 0]) assert represent(JyKetCoupled(1, 1, (S(1)/2, S(1)/2)), basis=Jz) == \ Matrix([0, S(1)/2, I*sqrt(2)/2, -S(1)/2]) assert represent(JyKetCoupled(1, 0, (S(1)/2, S(1)/2)), basis=Jz) == \ Matrix([0, I*sqrt(2)/2, 0, I*sqrt(2)/2]) assert represent(JyKetCoupled(1, -1, (S(1)/2, S(1)/2)), basis=Jz) == \ Matrix([0, -S(1)/2, I*sqrt(2)/2, S(1)/2]) assert represent(JzKetCoupled(0, 0, (S(1)/2, S(1)/2)), basis=Jz) == \ Matrix([1, 0, 0, 0]) assert represent(JzKetCoupled(1, 1, (S(1)/2, S(1)/2)), basis=Jz) == \ Matrix([0, 1, 0, 0]) assert represent(JzKetCoupled(1, 0, (S(1)/2, S(1)/2)), basis=Jz) == \ Matrix([0, 0, 1, 0]) assert represent(JzKetCoupled(1, -1, (S(1)/2, S(1)/2)), basis=Jz) == \ Matrix([0, 0, 0, 1]) def test_represent_rotation(): assert represent(Rotation(0, pi/2, 0)) == \ Matrix( [[WignerD( S( 1)/2, S( 1)/2, S( 1)/2, 0, pi/2, 0), WignerD( S(1)/2, S(1)/2, -S(1)/2, 0, pi/2, 0)], [WignerD(S(1)/2, -S(1)/2, S(1)/2, 0, pi/2, 0), WignerD(S(1)/2, -S(1)/2, -S(1)/2, 0, pi/2, 0)]]) assert represent(Rotation(0, pi/2, 0), doit=True) == \ Matrix([[sqrt(2)/2, -sqrt(2)/2], [sqrt(2)/2, sqrt(2)/2]]) def test_rewrite_same(): # Rewrite to same basis assert JxBra(1, 1).rewrite('Jx') == JxBra(1, 1) assert JxBra(j, m).rewrite('Jx') == JxBra(j, m) assert JxKet(1, 1).rewrite('Jx') == JxKet(1, 1) assert JxKet(j, m).rewrite('Jx') == JxKet(j, m) def test_rewrite_Bra(): # Numerical assert JxBra(1, 1).rewrite('Jy') == -I*JyBra(1, 1) assert JxBra(1, 0).rewrite('Jy') == JyBra(1, 0) assert JxBra(1, -1).rewrite('Jy') == I*JyBra(1, -1) assert JxBra(1, 1).rewrite( 'Jz') == JzBra(1, 1)/2 + JzBra(1, 0)/sqrt(2) + JzBra(1, -1)/2 assert JxBra( 1, 0).rewrite('Jz') == -sqrt(2)*JzBra(1, 1)/2 + sqrt(2)*JzBra(1, -1)/2 assert JxBra(1, -1).rewrite( 'Jz') == JzBra(1, 1)/2 - JzBra(1, 0)/sqrt(2) + JzBra(1, -1)/2 assert JyBra(1, 1).rewrite('Jx') == I*JxBra(1, 1) assert JyBra(1, 0).rewrite('Jx') == JxBra(1, 0) assert JyBra(1, -1).rewrite('Jx') == -I*JxBra(1, -1) assert JyBra(1, 1).rewrite( 'Jz') == JzBra(1, 1)/2 - sqrt(2)*I*JzBra(1, 0)/2 - JzBra(1, -1)/2 assert JyBra(1, 0).rewrite( 'Jz') == -sqrt(2)*I*JzBra(1, 1)/2 - sqrt(2)*I*JzBra(1, -1)/2 assert JyBra(1, -1).rewrite( 'Jz') == -JzBra(1, 1)/2 - sqrt(2)*I*JzBra(1, 0)/2 + JzBra(1, -1)/2 assert JzBra(1, 1).rewrite( 'Jx') == JxBra(1, 1)/2 - sqrt(2)*JxBra(1, 0)/2 + JxBra(1, -1)/2 assert JzBra( 1, 0).rewrite('Jx') == sqrt(2)*JxBra(1, 1)/2 - sqrt(2)*JxBra(1, -1)/2 assert JzBra(1, -1).rewrite( 'Jx') == JxBra(1, 1)/2 + sqrt(2)*JxBra(1, 0)/2 + JxBra(1, -1)/2 assert JzBra(1, 1).rewrite( 'Jy') == JyBra(1, 1)/2 + sqrt(2)*I*JyBra(1, 0)/2 - JyBra(1, -1)/2 assert JzBra(1, 0).rewrite( 'Jy') == sqrt(2)*I*JyBra(1, 1)/2 + sqrt(2)*I*JyBra(1, -1)/2 assert JzBra(1, -1).rewrite( 'Jy') == -JyBra(1, 1)/2 + sqrt(2)*I*JyBra(1, 0)/2 + JyBra(1, -1)/2 # Symbolic assert JxBra(j, m).rewrite('Jy') == Sum( WignerD(j, mi, m, 3*pi/2, 0, 0) * JyBra(j, mi), (mi, -j, j)) assert JxBra(j, m).rewrite('Jz') == Sum( WignerD(j, mi, m, 0, pi/2, 0) * JzBra(j, mi), (mi, -j, j)) assert JyBra(j, m).rewrite('Jx') == Sum( WignerD(j, mi, m, 0, 0, pi/2) * JxBra(j, mi), (mi, -j, j)) assert JyBra(j, m).rewrite('Jz') == Sum( WignerD(j, mi, m, 3*pi/2, -pi/2, pi/2) * JzBra(j, mi), (mi, -j, j)) assert JzBra(j, m).rewrite('Jx') == Sum( WignerD(j, mi, m, 0, 3*pi/2, 0) * JxBra(j, mi), (mi, -j, j)) assert JzBra(j, m).rewrite('Jy') == Sum( WignerD(j, mi, m, 3*pi/2, pi/2, pi/2) * JyBra(j, mi), (mi, -j, j)) def test_rewrite_Ket(): # Numerical assert JxKet(1, 1).rewrite('Jy') == I*JyKet(1, 1) assert JxKet(1, 0).rewrite('Jy') == JyKet(1, 0) assert JxKet(1, -1).rewrite('Jy') == -I*JyKet(1, -1) assert JxKet(1, 1).rewrite( 'Jz') == JzKet(1, 1)/2 + JzKet(1, 0)/sqrt(2) + JzKet(1, -1)/2 assert JxKet( 1, 0).rewrite('Jz') == -sqrt(2)*JzKet(1, 1)/2 + sqrt(2)*JzKet(1, -1)/2 assert JxKet(1, -1).rewrite( 'Jz') == JzKet(1, 1)/2 - JzKet(1, 0)/sqrt(2) + JzKet(1, -1)/2 assert JyKet(1, 1).rewrite('Jx') == -I*JxKet(1, 1) assert JyKet(1, 0).rewrite('Jx') == JxKet(1, 0) assert JyKet(1, -1).rewrite('Jx') == I*JxKet(1, -1) assert JyKet(1, 1).rewrite( 'Jz') == JzKet(1, 1)/2 + sqrt(2)*I*JzKet(1, 0)/2 - JzKet(1, -1)/2 assert JyKet(1, 0).rewrite( 'Jz') == sqrt(2)*I*JzKet(1, 1)/2 + sqrt(2)*I*JzKet(1, -1)/2 assert JyKet(1, -1).rewrite( 'Jz') == -JzKet(1, 1)/2 + sqrt(2)*I*JzKet(1, 0)/2 + JzKet(1, -1)/2 assert JzKet(1, 1).rewrite( 'Jx') == JxKet(1, 1)/2 - sqrt(2)*JxKet(1, 0)/2 + JxKet(1, -1)/2 assert JzKet( 1, 0).rewrite('Jx') == sqrt(2)*JxKet(1, 1)/2 - sqrt(2)*JxKet(1, -1)/2 assert JzKet(1, -1).rewrite( 'Jx') == JxKet(1, 1)/2 + sqrt(2)*JxKet(1, 0)/2 + JxKet(1, -1)/2 assert JzKet(1, 1).rewrite( 'Jy') == JyKet(1, 1)/2 - sqrt(2)*I*JyKet(1, 0)/2 - JyKet(1, -1)/2 assert JzKet(1, 0).rewrite( 'Jy') == -sqrt(2)*I*JyKet(1, 1)/2 - sqrt(2)*I*JyKet(1, -1)/2 assert JzKet(1, -1).rewrite( 'Jy') == -JyKet(1, 1)/2 - sqrt(2)*I*JyKet(1, 0)/2 + JyKet(1, -1)/2 # Symbolic assert JxKet(j, m).rewrite('Jy') == Sum( WignerD(j, mi, m, 3*pi/2, 0, 0) * JyKet(j, mi), (mi, -j, j)) assert JxKet(j, m).rewrite('Jz') == Sum( WignerD(j, mi, m, 0, pi/2, 0) * JzKet(j, mi), (mi, -j, j)) assert JyKet(j, m).rewrite('Jx') == Sum( WignerD(j, mi, m, 0, 0, pi/2) * JxKet(j, mi), (mi, -j, j)) assert JyKet(j, m).rewrite('Jz') == Sum( WignerD(j, mi, m, 3*pi/2, -pi/2, pi/2) * JzKet(j, mi), (mi, -j, j)) assert JzKet(j, m).rewrite('Jx') == Sum( WignerD(j, mi, m, 0, 3*pi/2, 0) * JxKet(j, mi), (mi, -j, j)) assert JzKet(j, m).rewrite('Jy') == Sum( WignerD(j, mi, m, 3*pi/2, pi/2, pi/2) * JyKet(j, mi), (mi, -j, j)) def test_rewrite_uncoupled_state(): # Numerical assert TensorProduct(JyKet(1, 1), JxKet( 1, 1)).rewrite('Jx') == -I*TensorProduct(JxKet(1, 1), JxKet(1, 1)) assert TensorProduct(JyKet(1, 0), JxKet( 1, 1)).rewrite('Jx') == TensorProduct(JxKet(1, 0), JxKet(1, 1)) assert TensorProduct(JyKet(1, -1), JxKet( 1, 1)).rewrite('Jx') == I*TensorProduct(JxKet(1, -1), JxKet(1, 1)) assert TensorProduct(JzKet(1, 1), JxKet(1, 1)).rewrite('Jx') == \ TensorProduct(JxKet(1, -1), JxKet(1, 1))/2 - sqrt(2)*TensorProduct(JxKet( 1, 0), JxKet(1, 1))/2 + TensorProduct(JxKet(1, 1), JxKet(1, 1))/2 assert TensorProduct(JzKet(1, 0), JxKet(1, 1)).rewrite('Jx') == \ -sqrt(2)*TensorProduct(JxKet(1, -1), JxKet(1, 1))/2 + sqrt( 2)*TensorProduct(JxKet(1, 1), JxKet(1, 1))/2 assert TensorProduct(JzKet(1, -1), JxKet(1, 1)).rewrite('Jx') == \ TensorProduct(JxKet(1, -1), JxKet(1, 1))/2 + sqrt(2)*TensorProduct(JxKet(1, 0), JxKet(1, 1))/2 + TensorProduct(JxKet(1, 1), JxKet(1, 1))/2 assert TensorProduct(JxKet(1, 1), JyKet( 1, 1)).rewrite('Jy') == I*TensorProduct(JyKet(1, 1), JyKet(1, 1)) assert TensorProduct(JxKet(1, 0), JyKet( 1, 1)).rewrite('Jy') == TensorProduct(JyKet(1, 0), JyKet(1, 1)) assert TensorProduct(JxKet(1, -1), JyKet( 1, 1)).rewrite('Jy') == -I*TensorProduct(JyKet(1, -1), JyKet(1, 1)) assert TensorProduct(JzKet(1, 1), JyKet(1, 1)).rewrite('Jy') == \ -TensorProduct(JyKet(1, -1), JyKet(1, 1))/2 - sqrt(2)*I*TensorProduct(JyKet(1, 0), JyKet(1, 1))/2 + TensorProduct(JyKet(1, 1), JyKet(1, 1))/2 assert TensorProduct(JzKet(1, 0), JyKet(1, 1)).rewrite('Jy') == \ -sqrt(2)*I*TensorProduct(JyKet(1, -1), JyKet( 1, 1))/2 - sqrt(2)*I*TensorProduct(JyKet(1, 1), JyKet(1, 1))/2 assert TensorProduct(JzKet(1, -1), JyKet(1, 1)).rewrite('Jy') == \ TensorProduct(JyKet(1, -1), JyKet(1, 1))/2 - sqrt(2)*I*TensorProduct(JyKet(1, 0), JyKet(1, 1))/2 - TensorProduct(JyKet(1, 1), JyKet(1, 1))/2 assert TensorProduct(JxKet(1, 1), JzKet(1, 1)).rewrite('Jz') == \ TensorProduct(JzKet(1, -1), JzKet(1, 1))/2 + sqrt(2)*TensorProduct(JzKet(1, 0), JzKet(1, 1))/2 + TensorProduct(JzKet(1, 1), JzKet(1, 1))/2 assert TensorProduct(JxKet(1, 0), JzKet(1, 1)).rewrite('Jz') == \ sqrt(2)*TensorProduct(JzKet(1, -1), JzKet( 1, 1))/2 - sqrt(2)*TensorProduct(JzKet(1, 1), JzKet(1, 1))/2 assert TensorProduct(JxKet(1, -1), JzKet(1, 1)).rewrite('Jz') == \ TensorProduct(JzKet(1, -1), JzKet(1, 1))/2 - sqrt(2)*TensorProduct(JzKet(1, 0), JzKet(1, 1))/2 + TensorProduct(JzKet(1, 1), JzKet(1, 1))/2 assert TensorProduct(JyKet(1, 1), JzKet(1, 1)).rewrite('Jz') == \ -TensorProduct(JzKet(1, -1), JzKet(1, 1))/2 + sqrt(2)*I*TensorProduct(JzKet(1, 0), JzKet(1, 1))/2 + TensorProduct(JzKet(1, 1), JzKet(1, 1))/2 assert TensorProduct(JyKet(1, 0), JzKet(1, 1)).rewrite('Jz') == \ sqrt(2)*I*TensorProduct(JzKet(1, -1), JzKet( 1, 1))/2 + sqrt(2)*I*TensorProduct(JzKet(1, 1), JzKet(1, 1))/2 assert TensorProduct(JyKet(1, -1), JzKet(1, 1)).rewrite('Jz') == \ TensorProduct(JzKet(1, -1), JzKet(1, 1))/2 + sqrt(2)*I*TensorProduct(JzKet(1, 0), JzKet(1, 1))/2 - TensorProduct(JzKet(1, 1), JzKet(1, 1))/2 # Symbolic assert TensorProduct(JyKet(j1, m1), JxKet(j2, m2)).rewrite('Jy') == \ TensorProduct(JyKet(j1, m1), Sum( WignerD(j2, mi, m2, 3*pi/2, 0, 0) * JyKet(j2, mi), (mi, -j2, j2))) assert TensorProduct(JzKet(j1, m1), JxKet(j2, m2)).rewrite('Jz') == \ TensorProduct(JzKet(j1, m1), Sum( WignerD(j2, mi, m2, 0, pi/2, 0) * JzKet(j2, mi), (mi, -j2, j2))) assert TensorProduct(JxKet(j1, m1), JyKet(j2, m2)).rewrite('Jx') == \ TensorProduct(JxKet(j1, m1), Sum( WignerD(j2, mi, m2, 0, 0, pi/2) * JxKet(j2, mi), (mi, -j2, j2))) assert TensorProduct(JzKet(j1, m1), JyKet(j2, m2)).rewrite('Jz') == \ TensorProduct(JzKet(j1, m1), Sum(WignerD( j2, mi, m2, 3*pi/2, -pi/2, pi/2) * JzKet(j2, mi), (mi, -j2, j2))) assert TensorProduct(JxKet(j1, m1), JzKet(j2, m2)).rewrite('Jx') == \ TensorProduct(JxKet(j1, m1), Sum( WignerD(j2, mi, m2, 0, 3*pi/2, 0) * JxKet(j2, mi), (mi, -j2, j2))) assert TensorProduct(JyKet(j1, m1), JzKet(j2, m2)).rewrite('Jy') == \ TensorProduct(JyKet(j1, m1), Sum(WignerD( j2, mi, m2, 3*pi/2, pi/2, pi/2) * JyKet(j2, mi), (mi, -j2, j2))) def test_rewrite_coupled_state(): # Numerical assert JyKetCoupled(0, 0, (S(1)/2, S(1)/2)).rewrite('Jx') == \ JxKetCoupled(0, 0, (S(1)/2, S(1)/2)) assert JyKetCoupled(1, 1, (S(1)/2, S(1)/2)).rewrite('Jx') == \ -I*JxKetCoupled(1, 1, (S(1)/2, S(1)/2)) assert JyKetCoupled(1, 0, (S(1)/2, S(1)/2)).rewrite('Jx') == \ JxKetCoupled(1, 0, (S(1)/2, S(1)/2)) assert JyKetCoupled(1, -1, (S(1)/2, S(1)/2)).rewrite('Jx') == \ I*JxKetCoupled(1, -1, (S(1)/2, S(1)/2)) assert JzKetCoupled(0, 0, (S(1)/2, S(1)/2)).rewrite('Jx') == \ JxKetCoupled(0, 0, (S(1)/2, S(1)/2)) assert JzKetCoupled(1, 1, (S(1)/2, S(1)/2)).rewrite('Jx') == \ JxKetCoupled(1, 1, (S(1)/2, S(1)/2))/2 - sqrt(2)*JxKetCoupled(1, 0, ( S(1)/2, S(1)/2))/2 + JxKetCoupled(1, -1, (S(1)/2, S(1)/2))/2 assert JzKetCoupled(1, 0, (S(1)/2, S(1)/2)).rewrite('Jx') == \ sqrt(2)*JxKetCoupled(1, 1, (S( 1)/2, S(1)/2))/2 - sqrt(2)*JxKetCoupled(1, -1, (S(1)/2, S(1)/2))/2 assert JzKetCoupled(1, -1, (S(1)/2, S(1)/2)).rewrite('Jx') == \ JxKetCoupled(1, 1, (S(1)/2, S(1)/2))/2 + sqrt(2)*JxKetCoupled(1, 0, ( S(1)/2, S(1)/2))/2 + JxKetCoupled(1, -1, (S(1)/2, S(1)/2))/2 assert JxKetCoupled(0, 0, (S(1)/2, S(1)/2)).rewrite('Jy') == \ JyKetCoupled(0, 0, (S(1)/2, S(1)/2)) assert JxKetCoupled(1, 1, (S(1)/2, S(1)/2)).rewrite('Jy') == \ I*JyKetCoupled(1, 1, (S(1)/2, S(1)/2)) assert JxKetCoupled(1, 0, (S(1)/2, S(1)/2)).rewrite('Jy') == \ JyKetCoupled(1, 0, (S(1)/2, S(1)/2)) assert JxKetCoupled(1, -1, (S(1)/2, S(1)/2)).rewrite('Jy') == \ -I*JyKetCoupled(1, -1, (S(1)/2, S(1)/2)) assert JzKetCoupled(0, 0, (S(1)/2, S(1)/2)).rewrite('Jy') == \ JyKetCoupled(0, 0, (S(1)/2, S(1)/2)) assert JzKetCoupled(1, 1, (S(1)/2, S(1)/2)).rewrite('Jy') == \ JyKetCoupled(1, 1, (S(1)/2, S(1)/2))/2 - I*sqrt(2)*JyKetCoupled(1, 0, ( S(1)/2, S(1)/2))/2 - JyKetCoupled(1, -1, (S(1)/2, S(1)/2))/2 assert JzKetCoupled(1, 0, (S(1)/2, S(1)/2)).rewrite('Jy') == \ -I*sqrt(2)*JyKetCoupled(1, 1, (S(1)/2, S(1)/2))/2 - I*sqrt( 2)*JyKetCoupled(1, -1, (S(1)/2, S(1)/2))/2 assert JzKetCoupled(1, -1, (S(1)/2, S(1)/2)).rewrite('Jy') == \ -JyKetCoupled(1, 1, (S(1)/2, S(1)/2))/2 - I*sqrt(2)*JyKetCoupled(1, 0, (S(1)/2, S(1)/2))/2 + JyKetCoupled(1, -1, (S(1)/2, S(1)/2))/2 assert JxKetCoupled(0, 0, (S(1)/2, S(1)/2)).rewrite('Jz') == \ JzKetCoupled(0, 0, (S(1)/2, S(1)/2)) assert JxKetCoupled(1, 1, (S(1)/2, S(1)/2)).rewrite('Jz') == \ JzKetCoupled(1, 1, (S(1)/2, S(1)/2))/2 + sqrt(2)*JzKetCoupled(1, 0, ( S(1)/2, S(1)/2))/2 + JzKetCoupled(1, -1, (S(1)/2, S(1)/2))/2 assert JxKetCoupled(1, 0, (S(1)/2, S(1)/2)).rewrite('Jz') == \ -sqrt(2)*JzKetCoupled(1, 1, (S( 1)/2, S(1)/2))/2 + sqrt(2)*JzKetCoupled(1, -1, (S(1)/2, S(1)/2))/2 assert JxKetCoupled(1, -1, (S(1)/2, S(1)/2)).rewrite('Jz') == \ JzKetCoupled(1, 1, (S(1)/2, S(1)/2))/2 - sqrt(2)*JzKetCoupled(1, 0, ( S(1)/2, S(1)/2))/2 + JzKetCoupled(1, -1, (S(1)/2, S(1)/2))/2 assert JyKetCoupled(0, 0, (S(1)/2, S(1)/2)).rewrite('Jz') == \ JzKetCoupled(0, 0, (S(1)/2, S(1)/2)) assert JyKetCoupled(1, 1, (S(1)/2, S(1)/2)).rewrite('Jz') == \ JzKetCoupled(1, 1, (S(1)/2, S(1)/2))/2 + I*sqrt(2)*JzKetCoupled(1, 0, ( S(1)/2, S(1)/2))/2 - JzKetCoupled(1, -1, (S(1)/2, S(1)/2))/2 assert JyKetCoupled(1, 0, (S(1)/2, S(1)/2)).rewrite('Jz') == \ I*sqrt(2)*JzKetCoupled(1, 1, (S(1)/2, S(1)/2))/2 + I*sqrt( 2)*JzKetCoupled(1, -1, (S(1)/2, S(1)/2))/2 assert JyKetCoupled(1, -1, (S(1)/2, S(1)/2)).rewrite('Jz') == \ -JzKetCoupled(1, 1, (S(1)/2, S(1)/2))/2 + I*sqrt(2)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2))/2 + JzKetCoupled(1, -1, (S(1)/2, S(1)/2))/2 # Symbolic assert JyKetCoupled(j, m, (j1, j2)).rewrite('Jx') == \ Sum(WignerD(j, mi, m, 0, 0, pi/2) * JxKetCoupled(j, mi, ( j1, j2)), (mi, -j, j)) assert JzKetCoupled(j, m, (j1, j2)).rewrite('Jx') == \ Sum(WignerD(j, mi, m, 0, 3*pi/2, 0) * JxKetCoupled(j, mi, ( j1, j2)), (mi, -j, j)) assert JxKetCoupled(j, m, (j1, j2)).rewrite('Jy') == \ Sum(WignerD(j, mi, m, 3*pi/2, 0, 0) * JyKetCoupled(j, mi, ( j1, j2)), (mi, -j, j)) assert JzKetCoupled(j, m, (j1, j2)).rewrite('Jy') == \ Sum(WignerD(j, mi, m, 3*pi/2, pi/2, pi/2) * JyKetCoupled(j, mi, (j1, j2)), (mi, -j, j)) assert JxKetCoupled(j, m, (j1, j2)).rewrite('Jz') == \ Sum(WignerD(j, mi, m, 0, pi/2, 0) * JzKetCoupled(j, mi, ( j1, j2)), (mi, -j, j)) assert JyKetCoupled(j, m, (j1, j2)).rewrite('Jz') == \ Sum(WignerD(j, mi, m, 3*pi/2, -pi/2, pi/2) * JzKetCoupled( j, mi, (j1, j2)), (mi, -j, j)) def test_innerproducts_of_rewritten_states(): # Numerical assert qapply(JxBra(1, 1)*JxKet(1, 1).rewrite('Jy')).doit() == 1 assert qapply(JxBra(1, 0)*JxKet(1, 0).rewrite('Jy')).doit() == 1 assert qapply(JxBra(1, -1)*JxKet(1, -1).rewrite('Jy')).doit() == 1 assert qapply(JxBra(1, 1)*JxKet(1, 1).rewrite('Jz')).doit() == 1 assert qapply(JxBra(1, 0)*JxKet(1, 0).rewrite('Jz')).doit() == 1 assert qapply(JxBra(1, -1)*JxKet(1, -1).rewrite('Jz')).doit() == 1 assert qapply(JyBra(1, 1)*JyKet(1, 1).rewrite('Jx')).doit() == 1 assert qapply(JyBra(1, 0)*JyKet(1, 0).rewrite('Jx')).doit() == 1 assert qapply(JyBra(1, -1)*JyKet(1, -1).rewrite('Jx')).doit() == 1 assert qapply(JyBra(1, 1)*JyKet(1, 1).rewrite('Jz')).doit() == 1 assert qapply(JyBra(1, 0)*JyKet(1, 0).rewrite('Jz')).doit() == 1 assert qapply(JyBra(1, -1)*JyKet(1, -1).rewrite('Jz')).doit() == 1 assert qapply(JyBra(1, 1)*JyKet(1, 1).rewrite('Jz')).doit() == 1 assert qapply(JyBra(1, 0)*JyKet(1, 0).rewrite('Jz')).doit() == 1 assert qapply(JyBra(1, -1)*JyKet(1, -1).rewrite('Jz')).doit() == 1 assert qapply(JzBra(1, 1)*JzKet(1, 1).rewrite('Jy')).doit() == 1 assert qapply(JzBra(1, 0)*JzKet(1, 0).rewrite('Jy')).doit() == 1 assert qapply(JzBra(1, -1)*JzKet(1, -1).rewrite('Jy')).doit() == 1 assert qapply(JxBra(1, 1)*JxKet(1, 0).rewrite('Jy')).doit() == 0 assert qapply(JxBra(1, 1)*JxKet(1, -1).rewrite('Jy')) == 0 assert qapply(JxBra(1, 1)*JxKet(1, 0).rewrite('Jz')).doit() == 0 assert qapply(JxBra(1, 1)*JxKet(1, -1).rewrite('Jz')) == 0 assert qapply(JyBra(1, 1)*JyKet(1, 0).rewrite('Jx')).doit() == 0 assert qapply(JyBra(1, 1)*JyKet(1, -1).rewrite('Jx')) == 0 assert qapply(JyBra(1, 1)*JyKet(1, 0).rewrite('Jz')).doit() == 0 assert qapply(JyBra(1, 1)*JyKet(1, -1).rewrite('Jz')) == 0 assert qapply(JzBra(1, 1)*JzKet(1, 0).rewrite('Jx')).doit() == 0 assert qapply(JzBra(1, 1)*JzKet(1, -1).rewrite('Jx')) == 0 assert qapply(JzBra(1, 1)*JzKet(1, 0).rewrite('Jy')).doit() == 0 assert qapply(JzBra(1, 1)*JzKet(1, -1).rewrite('Jy')) == 0 assert qapply(JxBra(1, 0)*JxKet(1, 1).rewrite('Jy')) == 0 assert qapply(JxBra(1, 0)*JxKet(1, -1).rewrite('Jy')) == 0 assert qapply(JxBra(1, 0)*JxKet(1, 1).rewrite('Jz')) == 0 assert qapply(JxBra(1, 0)*JxKet(1, -1).rewrite('Jz')) == 0 assert qapply(JyBra(1, 0)*JyKet(1, 1).rewrite('Jx')) == 0 assert qapply(JyBra(1, 0)*JyKet(1, -1).rewrite('Jx')) == 0 assert qapply(JyBra(1, 0)*JyKet(1, 1).rewrite('Jz')) == 0 assert qapply(JyBra(1, 0)*JyKet(1, -1).rewrite('Jz')) == 0 assert qapply(JzBra(1, 0)*JzKet(1, 1).rewrite('Jx')) == 0 assert qapply(JzBra(1, 0)*JzKet(1, -1).rewrite('Jx')) == 0 assert qapply(JzBra(1, 0)*JzKet(1, 1).rewrite('Jy')) == 0 assert qapply(JzBra(1, 0)*JzKet(1, -1).rewrite('Jy')) == 0 assert qapply(JxBra(1, -1)*JxKet(1, 1).rewrite('Jy')) == 0 assert qapply(JxBra(1, -1)*JxKet(1, 0).rewrite('Jy')).doit() == 0 assert qapply(JxBra(1, -1)*JxKet(1, 1).rewrite('Jz')) == 0 assert qapply(JxBra(1, -1)*JxKet(1, 0).rewrite('Jz')).doit() == 0 assert qapply(JyBra(1, -1)*JyKet(1, 1).rewrite('Jx')) == 0 assert qapply(JyBra(1, -1)*JyKet(1, 0).rewrite('Jx')).doit() == 0 assert qapply(JyBra(1, -1)*JyKet(1, 1).rewrite('Jz')) == 0 assert qapply(JyBra(1, -1)*JyKet(1, 0).rewrite('Jz')).doit() == 0 assert qapply(JzBra(1, -1)*JzKet(1, 1).rewrite('Jx')) == 0 assert qapply(JzBra(1, -1)*JzKet(1, 0).rewrite('Jx')).doit() == 0 assert qapply(JzBra(1, -1)*JzKet(1, 1).rewrite('Jy')) == 0 assert qapply(JzBra(1, -1)*JzKet(1, 0).rewrite('Jy')).doit() == 0 def test_uncouple_2_coupled_states(): # j1=1/2, j2=1/2 assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2)) ))) # j1=1/2, j2=1 assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 1)) == \ expand(uncouple( couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 1)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 0)) == \ expand(uncouple( couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 0)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, -1)) == \ expand(uncouple( couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, -1)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, 1)) == \ expand(uncouple( couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, 1)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, 0)) == \ expand(uncouple( couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, 0)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, -1)) == \ expand(uncouple( couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, -1)) ))) # j1=1, j2=1 assert TensorProduct(JzKet(1, 1), JzKet(1, 1)) == \ expand(uncouple(couple( TensorProduct(JzKet(1, 1), JzKet(1, 1)) ))) assert TensorProduct(JzKet(1, 1), JzKet(1, 0)) == \ expand(uncouple(couple( TensorProduct(JzKet(1, 1), JzKet(1, 0)) ))) assert TensorProduct(JzKet(1, 1), JzKet(1, -1)) == \ expand(uncouple(couple( TensorProduct(JzKet(1, 1), JzKet(1, -1)) ))) assert TensorProduct(JzKet(1, 0), JzKet(1, 1)) == \ expand(uncouple(couple( TensorProduct(JzKet(1, 0), JzKet(1, 1)) ))) assert TensorProduct(JzKet(1, 0), JzKet(1, 0)) == \ expand(uncouple(couple( TensorProduct(JzKet(1, 0), JzKet(1, 0)) ))) assert TensorProduct(JzKet(1, 0), JzKet(1, -1)) == \ expand(uncouple(couple( TensorProduct(JzKet(1, 0), JzKet(1, -1)) ))) assert TensorProduct(JzKet(1, -1), JzKet(1, 1)) == \ expand(uncouple(couple( TensorProduct(JzKet(1, -1), JzKet(1, 1)) ))) assert TensorProduct(JzKet(1, -1), JzKet(1, 0)) == \ expand(uncouple(couple( TensorProduct(JzKet(1, -1), JzKet(1, 0)) ))) assert TensorProduct(JzKet(1, -1), JzKet(1, -1)) == \ expand(uncouple(couple( TensorProduct(JzKet(1, -1), JzKet(1, -1)) ))) def test_uncouple_3_coupled_states(): # Default coupling # j1=1/2, j2=1/2, j3=1/2 assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet( S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S( 1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S( 1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S( 1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S( 1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S( 1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S( 1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/ 2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)) ))) # j1=1/2, j2=1, j3=1/2 assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct( JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct( JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct( JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct( JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct( JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct( JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct( JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct( JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct( JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct( JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct( JzKet(S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct( JzKet(S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(S(1)/2, S(-1)/2)) ))) # Coupling j1+j3=j13, j13+j2=j # j1=1/2, j2=1/2, j3=1/2 assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet( S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)), ((1, 3), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet( S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet( S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)), ((1, 3), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet( S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet( S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)), ((1, 3), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet( S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet( S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)), ((1, 3), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet( S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (1, 2)) ))) # j1=1/2, j2=1, j3=1/2 assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S( 1)/2), JzKet(1, 1), JzKet(S(1)/2, S(1)/2)), ((1, 3), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S( 1)/2), JzKet(1, 1), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S( 1)/2), JzKet(1, 0), JzKet(S(1)/2, S(1)/2)), ((1, 3), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S( 1)/2), JzKet(1, 0), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S( 1)/2), JzKet(1, -1), JzKet(S(1)/2, S(1)/2)), ((1, 3), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S( 1)/2), JzKet(1, -1), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S( -1)/2), JzKet(1, 1), JzKet(S(1)/2, S(1)/2)), ((1, 3), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S( -1)/2), JzKet(1, 1), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S( -1)/2), JzKet(1, 0), JzKet(S(1)/2, S(1)/2)), ((1, 3), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S( -1)/2), JzKet(1, 0), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S( -1)/2), JzKet(1, -1), JzKet(S(1)/2, S(1)/2)), ((1, 3), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/ 2), JzKet(1, -1), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (1, 2)) ))) def test_uncouple_4_coupled_states(): # j1=1/2, j2=1/2, j3=1/2, j4=1/2 assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet( S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S( 1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S( 1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S( 1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S( 1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S( 1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S( 1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet( S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S( 1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S( 1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S( 1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S( 1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S( 1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S( 1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)) ))) # j1=1/2, j2=1/2, j3=1, j4=1/2 assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet( S(1)/2, S(1)/2), JzKet(1, -1), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet( S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet( S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet( S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet( S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet( S(1)/2, S(1)/2), JzKet(1, -1), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet( S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet( S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(S(1)/2, S(-1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet( S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(S(1)/2, S(1)/2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet( S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(S(1)/2, S(-1)/2)) ))) # Couple j1+j3=j13, j2+j4=j24, j13+j24=j # j1=1/2, j2=1/2, j3=1/2, j4=1/2 assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (2, 4), (1, 2)) ))) # j1=1/2, j2=1/2, j3=1, j4=1/2 assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(S(1)/2, S(1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(S(1)/2, S(1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(S(1)/2, S(1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(S(1)/2, S(1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(S(1)/2, S(1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(S(1)/2, S(1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(S(1)/2, S(1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(S(1)/2, S(1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(S(1)/2, S(1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(S(1)/2, S(1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(S(1)/2, S(1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(S(1)/2, S(1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(S(1)/2, S(1)/2)), ((1, 3), (2, 4), (1, 2)) ))) assert TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(S(1)/2, S(-1)/2)) == \ expand(uncouple(couple( TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (2, 4), (1, 2)) ))) def test_uncouple_2_coupled_states_numerical(): # j1=1/2, j2=1/2 assert uncouple(JzKetCoupled(0, 0, (S(1)/2, S(1)/2))) == \ sqrt(2)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2))/2 - \ sqrt(2)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2))/2 assert uncouple(JzKetCoupled(1, 1, (S(1)/2, S(1)/2))) == \ TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)) assert uncouple(JzKetCoupled(1, 0, (S(1)/2, S(1)/2))) == \ sqrt(2)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2))/2 + \ sqrt(2)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2))/2 assert uncouple(JzKetCoupled(1, -1, (S(1)/2, S(1)/2))) == \ TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2)) # j1=1, j2=1/2 assert uncouple(JzKetCoupled(S(1)/2, S(1)/2, (1, S(1)/2))) == \ -sqrt(3)*TensorProduct(JzKet(1, 0), JzKet(S(1)/2, S(1)/2))/3 + \ sqrt(6)*TensorProduct(JzKet(1, 1), JzKet(S(1)/2, -S(1)/2))/3 assert uncouple(JzKetCoupled(S(1)/2, -S(1)/2, (1, S(1)/2))) == \ sqrt(3)*TensorProduct(JzKet(1, 0), JzKet(S(1)/2, -S(1)/2))/3 - \ sqrt(6)*TensorProduct(JzKet(1, -1), JzKet(S(1)/2, S(1)/2))/3 assert uncouple(JzKetCoupled(S(3)/2, S(3)/2, (1, S(1)/2))) == \ TensorProduct(JzKet(1, 1), JzKet(S(1)/2, S(1)/2)) assert uncouple(JzKetCoupled(S(3)/2, S(1)/2, (1, S(1)/2))) == \ sqrt(3)*TensorProduct(JzKet(1, 1), JzKet(S(1)/2, -S(1)/2))/3 + \ sqrt(6)*TensorProduct(JzKet(1, 0), JzKet(S(1)/2, S(1)/2))/3 assert uncouple(JzKetCoupled(S(3)/2, -S(1)/2, (1, S(1)/2))) == \ sqrt(6)*TensorProduct(JzKet(1, 0), JzKet(S(1)/2, -S(1)/2))/3 + \ sqrt(3)*TensorProduct(JzKet(1, -1), JzKet(S(1)/2, S(1)/2))/3 assert uncouple(JzKetCoupled(S(3)/2, -S(3)/2, (1, S(1)/2))) == \ TensorProduct(JzKet(1, -1), JzKet(S(1)/2, -S(1)/2)) # j1=1, j2=1 assert uncouple(JzKetCoupled(0, 0, (1, 1))) == \ sqrt(3)*TensorProduct(JzKet(1, 1), JzKet(1, -1))/3 - \ sqrt(3)*TensorProduct(JzKet(1, 0), JzKet(1, 0))/3 + \ sqrt(3)*TensorProduct(JzKet(1, -1), JzKet(1, 1))/3 assert uncouple(JzKetCoupled(1, 1, (1, 1))) == \ sqrt(2)*TensorProduct(JzKet(1, 1), JzKet(1, 0))/2 - \ sqrt(2)*TensorProduct(JzKet(1, 0), JzKet(1, 1))/2 assert uncouple(JzKetCoupled(1, 0, (1, 1))) == \ sqrt(2)*TensorProduct(JzKet(1, 1), JzKet(1, -1))/2 - \ sqrt(2)*TensorProduct(JzKet(1, -1), JzKet(1, 1))/2 assert uncouple(JzKetCoupled(1, -1, (1, 1))) == \ sqrt(2)*TensorProduct(JzKet(1, 0), JzKet(1, -1))/2 - \ sqrt(2)*TensorProduct(JzKet(1, -1), JzKet(1, 0))/2 assert uncouple(JzKetCoupled(2, 2, (1, 1))) == \ TensorProduct(JzKet(1, 1), JzKet(1, 1)) assert uncouple(JzKetCoupled(2, 1, (1, 1))) == \ sqrt(2)*TensorProduct(JzKet(1, 1), JzKet(1, 0))/2 + \ sqrt(2)*TensorProduct(JzKet(1, 0), JzKet(1, 1))/2 assert uncouple(JzKetCoupled(2, 0, (1, 1))) == \ sqrt(6)*TensorProduct(JzKet(1, 1), JzKet(1, -1))/6 + \ sqrt(6)*TensorProduct(JzKet(1, 0), JzKet(1, 0))/3 + \ sqrt(6)*TensorProduct(JzKet(1, -1), JzKet(1, 1))/6 assert uncouple(JzKetCoupled(2, -1, (1, 1))) == \ sqrt(2)*TensorProduct(JzKet(1, 0), JzKet(1, -1))/2 + \ sqrt(2)*TensorProduct(JzKet(1, -1), JzKet(1, 0))/2 assert uncouple(JzKetCoupled(2, -2, (1, 1))) == \ TensorProduct(JzKet(1, -1), JzKet(1, -1)) def test_uncouple_3_coupled_states_numerical(): # Default coupling # j1=1/2, j2=1/2, j3=1/2 assert uncouple(JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2))) == \ TensorProduct(JzKet( S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)) assert uncouple(JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2))) == \ sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2))/3 + \ sqrt(3)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2))/3 + \ sqrt(3)*TensorProduct(JzKet( S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2))/3 assert uncouple(JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2))) == \ sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2))/3 + \ sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2))/3 + \ sqrt(3)*TensorProduct(JzKet( S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2))/3 assert uncouple(JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(1)/2))) == \ TensorProduct(JzKet( S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2)) # j1=1/2, j2=1/2, j3=1 assert uncouple(JzKetCoupled(2, 2, (S(1)/2, S(1)/2, 1))) == \ TensorProduct( JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1)) assert uncouple(JzKetCoupled(2, 1, (S(1)/2, S(1)/2, 1))) == \ TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1))/2 + \ TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1))/2 + \ sqrt(2)*TensorProduct( JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0))/2 assert uncouple(JzKetCoupled(2, 0, (S(1)/2, S(1)/2, 1))) == \ sqrt(6)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1))/6 + \ sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0))/3 + \ sqrt(3)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0))/3 + \ sqrt(6)*TensorProduct( JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1))/6 assert uncouple(JzKetCoupled(2, -1, (S(1)/2, S(1)/2, 1))) == \ sqrt(2)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0))/2 + \ TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1))/2 + \ TensorProduct( JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1))/2 assert uncouple(JzKetCoupled(2, -2, (S(1)/2, S(1)/2, 1))) == \ TensorProduct( JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1)) assert uncouple(JzKetCoupled(1, 1, (S(1)/2, S(1)/2, 1))) == \ -TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1))/2 - \ TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1))/2 + \ sqrt(2)*TensorProduct( JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0))/2 assert uncouple(JzKetCoupled(1, 0, (S(1)/2, S(1)/2, 1))) == \ -sqrt(2)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1))/2 + \ sqrt(2)*TensorProduct( JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1))/2 assert uncouple(JzKetCoupled(1, -1, (S(1)/2, S(1)/2, 1))) == \ -sqrt(2)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0))/2 + \ TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1))/2 + \ TensorProduct( JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1))/2 # j1=1/2, j2=1, j3=1 assert uncouple(JzKetCoupled(S(5)/2, S(5)/2, (S(1)/2, 1, 1))) == \ TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 1)) assert uncouple(JzKetCoupled(S(5)/2, S(3)/2, (S(1)/2, 1, 1))) == \ sqrt(5)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 1))/5 + \ sqrt(10)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 1))/5 + \ sqrt(10)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 0))/5 assert uncouple(JzKetCoupled(S(5)/2, S(1)/2, (S(1)/2, 1, 1))) == \ sqrt(5)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 1))/5 + \ sqrt(5)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 0))/5 + \ sqrt(10)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 1))/10 + \ sqrt(10)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 0))/5 + \ sqrt(10)*TensorProduct( JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, -1))/10 assert uncouple(JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, 1, 1))) == \ sqrt(10)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 1))/10 + \ sqrt(10)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 0))/5 + \ sqrt(10)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, -1))/10 + \ sqrt(5)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 0))/5 + \ sqrt(5)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, -1))/5 assert uncouple(JzKetCoupled(S(5)/2, -S(3)/2, (S(1)/2, 1, 1))) == \ sqrt(10)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 0))/5 + \ sqrt(10)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, -1))/5 + \ sqrt(5)*TensorProduct( JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, -1))/5 assert uncouple(JzKetCoupled(S(5)/2, -S(5)/2, (S(1)/2, 1, 1))) == \ TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, -1)) assert uncouple(JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, 1, 1))) == \ -sqrt(30)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 1))/15 - \ 2*sqrt(15)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 1))/15 + \ sqrt(15)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 0))/5 assert uncouple(JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, 1, 1))) == \ -4*sqrt(5)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 1))/15 + \ sqrt(5)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 0))/15 - \ 2*sqrt(10)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 1))/15 + \ sqrt(10)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 0))/15 + \ sqrt(10)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, -1))/5 assert uncouple(JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, 1, 1))) == \ -sqrt(10)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 1))/5 - \ sqrt(10)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 0))/15 + \ 2*sqrt(10)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, -1))/15 - \ sqrt(5)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 0))/15 + \ 4*sqrt(5)*TensorProduct( JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, -1))/15 assert uncouple(JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, 1, 1))) == \ -sqrt(15)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 0))/5 + \ 2*sqrt(15)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, -1))/15 + \ sqrt(30)*TensorProduct( JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, -1))/15 assert uncouple(JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, 1, 1))) == \ TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 1))/3 - \ TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 0))/3 + \ sqrt(2)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 1))/6 - \ sqrt(2)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 0))/3 + \ sqrt(2)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, -1))/2 assert uncouple(JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, 1, 1))) == \ sqrt(2)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 1))/2 - \ sqrt(2)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 0))/3 + \ sqrt(2)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, -1))/6 - \ TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 0))/3 + \ TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, -1))/3 # j1=1, j2=1, j3=1 assert uncouple(JzKetCoupled(3, 3, (1, 1, 1))) == \ TensorProduct(JzKet(1, 1), JzKet(1, 1), JzKet(1, 1)) assert uncouple(JzKetCoupled(3, 2, (1, 1, 1))) == \ sqrt(3)*TensorProduct(JzKet(1, 0), JzKet(1, 1), JzKet(1, 1))/3 + \ sqrt(3)*TensorProduct(JzKet(1, 1), JzKet(1, 0), JzKet(1, 1))/3 + \ sqrt(3)*TensorProduct(JzKet(1, 1), JzKet(1, 1), JzKet(1, 0))/3 assert uncouple(JzKetCoupled(3, 1, (1, 1, 1))) == \ sqrt(15)*TensorProduct(JzKet(1, -1), JzKet(1, 1), JzKet(1, 1))/15 + \ 2*sqrt(15)*TensorProduct(JzKet(1, 0), JzKet(1, 0), JzKet(1, 1))/15 + \ 2*sqrt(15)*TensorProduct(JzKet(1, 0), JzKet(1, 1), JzKet(1, 0))/15 + \ sqrt(15)*TensorProduct(JzKet(1, 1), JzKet(1, -1), JzKet(1, 1))/15 + \ 2*sqrt(15)*TensorProduct(JzKet(1, 1), JzKet(1, 0), JzKet(1, 0))/15 + \ sqrt(15)*TensorProduct(JzKet(1, 1), JzKet(1, 1), JzKet(1, -1))/15 assert uncouple(JzKetCoupled(3, 0, (1, 1, 1))) == \ sqrt(10)*TensorProduct(JzKet(1, -1), JzKet(1, 0), JzKet(1, 1))/10 + \ sqrt(10)*TensorProduct(JzKet(1, -1), JzKet(1, 1), JzKet(1, 0))/10 + \ sqrt(10)*TensorProduct(JzKet(1, 0), JzKet(1, -1), JzKet(1, 1))/10 + \ sqrt(10)*TensorProduct(JzKet(1, 0), JzKet(1, 0), JzKet(1, 0))/5 + \ sqrt(10)*TensorProduct(JzKet(1, 0), JzKet(1, 1), JzKet(1, -1))/10 + \ sqrt(10)*TensorProduct(JzKet(1, 1), JzKet(1, -1), JzKet(1, 0))/10 + \ sqrt(10)*TensorProduct(JzKet(1, 1), JzKet(1, 0), JzKet(1, -1))/10 assert uncouple(JzKetCoupled(3, -1, (1, 1, 1))) == \ sqrt(15)*TensorProduct(JzKet(1, -1), JzKet(1, -1), JzKet(1, 1))/15 + \ 2*sqrt(15)*TensorProduct(JzKet(1, -1), JzKet(1, 0), JzKet(1, 0))/15 + \ sqrt(15)*TensorProduct(JzKet(1, -1), JzKet(1, 1), JzKet(1, -1))/15 + \ 2*sqrt(15)*TensorProduct(JzKet(1, 0), JzKet(1, -1), JzKet(1, 0))/15 + \ 2*sqrt(15)*TensorProduct(JzKet(1, 0), JzKet(1, 0), JzKet(1, -1))/15 + \ sqrt(15)*TensorProduct(JzKet(1, 1), JzKet(1, -1), JzKet(1, -1))/15 assert uncouple(JzKetCoupled(3, -2, (1, 1, 1))) == \ sqrt(3)*TensorProduct(JzKet(1, -1), JzKet(1, -1), JzKet(1, 0))/3 + \ sqrt(3)*TensorProduct(JzKet(1, -1), JzKet(1, 0), JzKet(1, -1))/3 + \ sqrt(3)*TensorProduct(JzKet(1, 0), JzKet(1, -1), JzKet(1, -1))/3 assert uncouple(JzKetCoupled(3, -3, (1, 1, 1))) == \ TensorProduct(JzKet(1, -1), JzKet(1, -1), JzKet(1, -1)) assert uncouple(JzKetCoupled(2, 2, (1, 1, 1))) == \ -sqrt(6)*TensorProduct(JzKet(1, 0), JzKet(1, 1), JzKet(1, 1))/6 - \ sqrt(6)*TensorProduct(JzKet(1, 1), JzKet(1, 0), JzKet(1, 1))/6 + \ sqrt(6)*TensorProduct(JzKet(1, 1), JzKet(1, 1), JzKet(1, 0))/3 assert uncouple(JzKetCoupled(2, 1, (1, 1, 1))) == \ -sqrt(3)*TensorProduct(JzKet(1, -1), JzKet(1, 1), JzKet(1, 1))/6 - \ sqrt(3)*TensorProduct(JzKet(1, 0), JzKet(1, 0), JzKet(1, 1))/3 + \ sqrt(3)*TensorProduct(JzKet(1, 0), JzKet(1, 1), JzKet(1, 0))/6 - \ sqrt(3)*TensorProduct(JzKet(1, 1), JzKet(1, -1), JzKet(1, 1))/6 + \ sqrt(3)*TensorProduct(JzKet(1, 1), JzKet(1, 0), JzKet(1, 0))/6 + \ sqrt(3)*TensorProduct(JzKet(1, 1), JzKet(1, 1), JzKet(1, -1))/3 assert uncouple(JzKetCoupled(2, 0, (1, 1, 1))) == \ -TensorProduct(JzKet(1, -1), JzKet(1, 0), JzKet(1, 1))/2 - \ TensorProduct(JzKet(1, 0), JzKet(1, -1), JzKet(1, 1))/2 + \ TensorProduct(JzKet(1, 0), JzKet(1, 1), JzKet(1, -1))/2 + \ TensorProduct(JzKet(1, 1), JzKet(1, 0), JzKet(1, -1))/2 assert uncouple(JzKetCoupled(2, -1, (1, 1, 1))) == \ -sqrt(3)*TensorProduct(JzKet(1, -1), JzKet(1, -1), JzKet(1, 1))/3 - \ sqrt(3)*TensorProduct(JzKet(1, -1), JzKet(1, 0), JzKet(1, 0))/6 + \ sqrt(3)*TensorProduct(JzKet(1, -1), JzKet(1, 1), JzKet(1, -1))/6 - \ sqrt(3)*TensorProduct(JzKet(1, 0), JzKet(1, -1), JzKet(1, 0))/6 + \ sqrt(3)*TensorProduct(JzKet(1, 0), JzKet(1, 0), JzKet(1, -1))/3 + \ sqrt(3)*TensorProduct(JzKet(1, 1), JzKet(1, -1), JzKet(1, -1))/6 assert uncouple(JzKetCoupled(2, -2, (1, 1, 1))) == \ -sqrt(6)*TensorProduct(JzKet(1, -1), JzKet(1, -1), JzKet(1, 0))/3 + \ sqrt(6)*TensorProduct(JzKet(1, -1), JzKet(1, 0), JzKet(1, -1))/6 + \ sqrt(6)*TensorProduct(JzKet(1, 0), JzKet(1, -1), JzKet(1, -1))/6 assert uncouple(JzKetCoupled(1, 1, (1, 1, 1))) == \ sqrt(15)*TensorProduct(JzKet(1, -1), JzKet(1, 1), JzKet(1, 1))/30 + \ sqrt(15)*TensorProduct(JzKet(1, 0), JzKet(1, 0), JzKet(1, 1))/15 - \ sqrt(15)*TensorProduct(JzKet(1, 0), JzKet(1, 1), JzKet(1, 0))/10 + \ sqrt(15)*TensorProduct(JzKet(1, 1), JzKet(1, -1), JzKet(1, 1))/30 - \ sqrt(15)*TensorProduct(JzKet(1, 1), JzKet(1, 0), JzKet(1, 0))/10 + \ sqrt(15)*TensorProduct(JzKet(1, 1), JzKet(1, 1), JzKet(1, -1))/5 assert uncouple(JzKetCoupled(1, 0, (1, 1, 1))) == \ sqrt(15)*TensorProduct(JzKet(1, -1), JzKet(1, 0), JzKet(1, 1))/10 - \ sqrt(15)*TensorProduct(JzKet(1, -1), JzKet(1, 1), JzKet(1, 0))/15 + \ sqrt(15)*TensorProduct(JzKet(1, 0), JzKet(1, -1), JzKet(1, 1))/10 - \ 2*sqrt(15)*TensorProduct(JzKet(1, 0), JzKet(1, 0), JzKet(1, 0))/15 + \ sqrt(15)*TensorProduct(JzKet(1, 0), JzKet(1, 1), JzKet(1, -1))/10 - \ sqrt(15)*TensorProduct(JzKet(1, 1), JzKet(1, -1), JzKet(1, 0))/15 + \ sqrt(15)*TensorProduct(JzKet(1, 1), JzKet(1, 0), JzKet(1, -1))/10 assert uncouple(JzKetCoupled(1, -1, (1, 1, 1))) == \ sqrt(15)*TensorProduct(JzKet(1, -1), JzKet(1, -1), JzKet(1, 1))/5 - \ sqrt(15)*TensorProduct(JzKet(1, -1), JzKet(1, 0), JzKet(1, 0))/10 + \ sqrt(15)*TensorProduct(JzKet(1, -1), JzKet(1, 1), JzKet(1, -1))/30 - \ sqrt(15)*TensorProduct(JzKet(1, 0), JzKet(1, -1), JzKet(1, 0))/10 + \ sqrt(15)*TensorProduct(JzKet(1, 0), JzKet(1, 0), JzKet(1, -1))/15 + \ sqrt(15)*TensorProduct(JzKet(1, 1), JzKet(1, -1), JzKet(1, -1))/30 # Defined j13 # j1=1/2, j2=1/2, j3=1, j13=1/2 assert uncouple(JzKetCoupled(1, 1, (S(1)/2, S(1)/2, 1), ((1, 3, S(1)/2), (1, 2, 1)) )) == \ -sqrt(6)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1))/3 + \ sqrt(3)*TensorProduct( JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0))/3 assert uncouple(JzKetCoupled(1, 0, (S(1)/2, S(1)/2, 1), ((1, 3, S(1)/2), (1, 2, 1)) )) == \ -sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1))/3 - \ sqrt(6)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0))/6 + \ sqrt(6)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0))/6 + \ sqrt(3)*TensorProduct( JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1))/3 assert uncouple(JzKetCoupled(1, -1, (S(1)/2, S(1)/2, 1), ((1, 3, S(1)/2), (1, 2, 1)) )) == \ -sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0))/3 + \ sqrt(6)*TensorProduct( JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1))/3 # j1=1/2, j2=1, j3=1, j13=1/2 assert uncouple(JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, 1, 1), ((1, 3, S(1)/2), (1, 2, S(3)/2)))) == \ -sqrt(6)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 1))/3 + \ sqrt(3)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 0))/3 assert uncouple(JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(1)/2), (1, 2, S(3)/2)))) == \ -2*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 1))/3 - \ TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 0))/3 + \ sqrt(2)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 0))/3 + \ sqrt(2)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, -1))/3 assert uncouple(JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(1)/2), (1, 2, S(3)/2)))) == \ -sqrt(2)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 1))/3 - \ sqrt(2)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 0))/3 + \ TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 0))/3 + \ 2*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, -1))/3 assert uncouple(JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, 1, 1), ((1, 3, S(1)/2), (1, 2, S(3)/2)))) == \ -sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 0))/3 + \ sqrt(6)*TensorProduct( JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, -1))/3 # j1=1, j2=1, j3=1, j13=1 assert uncouple(JzKetCoupled(2, 2, (1, 1, 1), ((1, 3, 1), (1, 2, 2)))) == \ -sqrt(2)*TensorProduct(JzKet(1, 0), JzKet(1, 1), JzKet(1, 1))/2 + \ sqrt(2)*TensorProduct(JzKet(1, 1), JzKet(1, 1), JzKet(1, 0))/2 assert uncouple(JzKetCoupled(2, 1, (1, 1, 1), ((1, 3, 1), (1, 2, 2)))) == \ -TensorProduct(JzKet(1, -1), JzKet(1, 1), JzKet(1, 1))/2 - \ TensorProduct(JzKet(1, 0), JzKet(1, 0), JzKet(1, 1))/2 + \ TensorProduct(JzKet(1, 1), JzKet(1, 0), JzKet(1, 0))/2 + \ TensorProduct(JzKet(1, 1), JzKet(1, 1), JzKet(1, -1))/2 assert uncouple(JzKetCoupled(2, 0, (1, 1, 1), ((1, 3, 1), (1, 2, 2)))) == \ -sqrt(3)*TensorProduct(JzKet(1, -1), JzKet(1, 0), JzKet(1, 1))/3 - \ sqrt(3)*TensorProduct(JzKet(1, -1), JzKet(1, 1), JzKet(1, 0))/6 - \ sqrt(3)*TensorProduct(JzKet(1, 0), JzKet(1, -1), JzKet(1, 1))/6 + \ sqrt(3)*TensorProduct(JzKet(1, 0), JzKet(1, 1), JzKet(1, -1))/6 + \ sqrt(3)*TensorProduct(JzKet(1, 1), JzKet(1, -1), JzKet(1, 0))/6 + \ sqrt(3)*TensorProduct(JzKet(1, 1), JzKet(1, 0), JzKet(1, -1))/3 assert uncouple(JzKetCoupled(2, -1, (1, 1, 1), ((1, 3, 1), (1, 2, 2)))) == \ -TensorProduct(JzKet(1, -1), JzKet(1, -1), JzKet(1, 1))/2 - \ TensorProduct(JzKet(1, -1), JzKet(1, 0), JzKet(1, 0))/2 + \ TensorProduct(JzKet(1, 0), JzKet(1, 0), JzKet(1, -1))/2 + \ TensorProduct(JzKet(1, 1), JzKet(1, -1), JzKet(1, -1))/2 assert uncouple(JzKetCoupled(2, -2, (1, 1, 1), ((1, 3, 1), (1, 2, 2)))) == \ -sqrt(2)*TensorProduct(JzKet(1, -1), JzKet(1, -1), JzKet(1, 0))/2 + \ sqrt(2)*TensorProduct(JzKet(1, 0), JzKet(1, -1), JzKet(1, -1))/2 assert uncouple(JzKetCoupled(1, 1, (1, 1, 1), ((1, 3, 1), (1, 2, 1)))) == \ TensorProduct(JzKet(1, -1), JzKet(1, 1), JzKet(1, 1))/2 - \ TensorProduct(JzKet(1, 0), JzKet(1, 0), JzKet(1, 1))/2 + \ TensorProduct(JzKet(1, 1), JzKet(1, 0), JzKet(1, 0))/2 - \ TensorProduct(JzKet(1, 1), JzKet(1, 1), JzKet(1, -1))/2 assert uncouple(JzKetCoupled(1, 0, (1, 1, 1), ((1, 3, 1), (1, 2, 1)))) == \ TensorProduct(JzKet(1, -1), JzKet(1, 1), JzKet(1, 0))/2 - \ TensorProduct(JzKet(1, 0), JzKet(1, -1), JzKet(1, 1))/2 - \ TensorProduct(JzKet(1, 0), JzKet(1, 1), JzKet(1, -1))/2 + \ TensorProduct(JzKet(1, 1), JzKet(1, -1), JzKet(1, 0))/2 assert uncouple(JzKetCoupled(1, -1, (1, 1, 1), ((1, 3, 1), (1, 2, 1)))) == \ -TensorProduct(JzKet(1, -1), JzKet(1, -1), JzKet(1, 1))/2 + \ TensorProduct(JzKet(1, -1), JzKet(1, 0), JzKet(1, 0))/2 - \ TensorProduct(JzKet(1, 0), JzKet(1, 0), JzKet(1, -1))/2 + \ TensorProduct(JzKet(1, 1), JzKet(1, -1), JzKet(1, -1))/2 def test_uncouple_4_coupled_states_numerical(): # j1=1/2, j2=1/2, j3=1, j4=1, default coupling assert uncouple(JzKetCoupled(3, 3, (S(1)/2, S(1)/2, 1, 1))) == \ TensorProduct(JzKet( S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 1)) assert uncouple(JzKetCoupled(3, 2, (S(1)/2, S(1)/2, 1, 1))) == \ sqrt(6)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 1))/6 + \ sqrt(6)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 1))/6 + \ sqrt(3)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 1))/3 + \ sqrt(3)*TensorProduct(JzKet(S( 1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 0))/3 assert uncouple(JzKetCoupled(3, 1, (S(1)/2, S(1)/2, 1, 1))) == \ sqrt(15)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 1))/15 + \ sqrt(30)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 1))/15 + \ sqrt(30)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 0))/15 + \ sqrt(30)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 1))/15 + \ sqrt(30)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 0))/15 + \ sqrt(15)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 1))/15 + \ 2*sqrt(15)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 0))/15 + \ sqrt(15)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, -1))/15 assert uncouple(JzKetCoupled(3, 0, (S(1)/2, S(1)/2, 1, 1))) == \ sqrt(10)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 1))/10 + \ sqrt(10)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 0))/10 + \ sqrt(5)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 1))/10 + \ sqrt(5)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 0))/5 + \ sqrt(5)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, -1))/10 + \ sqrt(5)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 1))/10 + \ sqrt(5)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 0))/5 + \ sqrt(5)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, -1))/10 + \ sqrt(10)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 0))/10 + \ sqrt(10)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, -1))/10 assert uncouple(JzKetCoupled(3, -1, (S(1)/2, S(1)/2, 1, 1))) == \ sqrt(15)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 1))/15 + \ 2*sqrt(15)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 0))/15 + \ sqrt(15)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, -1))/15 + \ sqrt(30)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 0))/15 + \ sqrt(30)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, -1))/15 + \ sqrt(30)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 0))/15 + \ sqrt(30)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, -1))/15 + \ sqrt(15)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, -1))/15 assert uncouple(JzKetCoupled(3, -2, (S(1)/2, S(1)/2, 1, 1))) == \ sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 0))/3 + \ sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, -1))/3 + \ sqrt(6)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, -1))/6 + \ sqrt(6)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, -1))/6 assert uncouple(JzKetCoupled(3, -3, (S(1)/2, S(1)/2, 1, 1))) == \ TensorProduct(JzKet(S(1)/2, -S( 1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, -1)) assert uncouple(JzKetCoupled(2, 2, (S(1)/2, S(1)/2, 1, 1))) == \ -sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 1))/6 - \ sqrt(3)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 1))/6 - \ sqrt(6)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 1))/6 + \ sqrt(6)*TensorProduct(JzKet(S( 1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 0))/3 assert uncouple(JzKetCoupled(2, 1, (S(1)/2, S(1)/2, 1, 1))) == \ -sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 1))/6 - \ sqrt(6)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 1))/6 + \ sqrt(6)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 0))/12 - \ sqrt(6)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 1))/6 + \ sqrt(6)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 0))/12 - \ sqrt(3)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 1))/6 + \ sqrt(3)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 0))/6 + \ sqrt(3)*TensorProduct(JzKet(S( 1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, -1))/3 assert uncouple(JzKetCoupled(2, 0, (S(1)/2, S(1)/2, 1, 1))) == \ -TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 1))/2 - \ sqrt(2)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 1))/4 + \ sqrt(2)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, -1))/4 - \ sqrt(2)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 1))/4 + \ sqrt(2)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, -1))/4 + \ TensorProduct(JzKet(S( 1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, -1))/2 assert uncouple(JzKetCoupled(2, -1, (S(1)/2, S(1)/2, 1, 1))) == \ -sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 1))/3 - \ sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 0))/6 + \ sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, -1))/6 - \ sqrt(6)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 0))/12 + \ sqrt(6)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, -1))/6 - \ sqrt(6)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 0))/12 + \ sqrt(6)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, -1))/6 + \ sqrt(3)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, -1))/6 assert uncouple(JzKetCoupled(2, -2, (S(1)/2, S(1)/2, 1, 1))) == \ -sqrt(6)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 0))/3 + \ sqrt(6)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, -1))/6 + \ sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, -1))/6 + \ sqrt(3)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, -1))/6 assert uncouple(JzKetCoupled(1, 1, (S(1)/2, S(1)/2, 1, 1))) == \ sqrt(15)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 1))/30 + \ sqrt(30)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 1))/30 - \ sqrt(30)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 0))/20 + \ sqrt(30)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 1))/30 - \ sqrt(30)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 0))/20 + \ sqrt(15)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 1))/30 - \ sqrt(15)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 0))/10 + \ sqrt(15)*TensorProduct(JzKet(S( 1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, -1))/5 assert uncouple(JzKetCoupled(1, 0, (S(1)/2, S(1)/2, 1, 1))) == \ sqrt(15)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 1))/10 - \ sqrt(15)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 0))/15 + \ sqrt(30)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 1))/20 - \ sqrt(30)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 0))/15 + \ sqrt(30)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, -1))/20 + \ sqrt(30)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 1))/20 - \ sqrt(30)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 0))/15 + \ sqrt(30)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, -1))/20 - \ sqrt(15)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 0))/15 + \ sqrt(15)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, -1))/10 assert uncouple(JzKetCoupled(1, -1, (S(1)/2, S(1)/2, 1, 1))) == \ sqrt(15)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 1))/5 - \ sqrt(15)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 0))/10 + \ sqrt(15)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, -1))/30 - \ sqrt(30)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 0))/20 + \ sqrt(30)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, -1))/30 - \ sqrt(30)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 0))/20 + \ sqrt(30)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, -1))/30 + \ sqrt(15)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, -1))/30 # j1=1/2, j2=1/2, j3=1, j4=1, j12=1, j34=1 assert uncouple(JzKetCoupled(2, 2, (S(1)/2, S(1)/2, 1, 1), ((1, 2, 1), (3, 4, 1), (1, 3, 2)))) == \ -sqrt(2)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 1))/2 + \ sqrt(2)*TensorProduct(JzKet(S( 1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 0))/2 assert uncouple(JzKetCoupled(2, 1, (S(1)/2, S(1)/2, 1, 1), ((1, 2, 1), (3, 4, 1), (1, 3, 2)))) == \ -sqrt(2)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 1))/4 + \ sqrt(2)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 0))/4 - \ sqrt(2)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 1))/4 + \ sqrt(2)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 0))/4 - \ TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 1))/2 + \ TensorProduct(JzKet(S( 1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, -1))/2 assert uncouple(JzKetCoupled(2, 0, (S(1)/2, S(1)/2, 1, 1), ((1, 2, 1), (3, 4, 1), (1, 3, 2)))) == \ -sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 1))/6 + \ sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 0))/6 - \ sqrt(6)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 1))/6 + \ sqrt(6)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, -1))/6 - \ sqrt(6)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 1))/6 + \ sqrt(6)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, -1))/6 - \ sqrt(3)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 0))/6 + \ sqrt(3)*TensorProduct(JzKet(S( 1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, -1))/6 assert uncouple(JzKetCoupled(2, -1, (S(1)/2, S(1)/2, 1, 1), ((1, 2, 1), (3, 4, 1), (1, 3, 2)))) == \ -TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 1))/2 + \ TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, -1))/2 - \ sqrt(2)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 0))/4 + \ sqrt(2)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, -1))/4 - \ sqrt(2)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 0))/4 + \ sqrt(2)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, -1))/4 assert uncouple(JzKetCoupled(2, -2, (S(1)/2, S(1)/2, 1, 1), ((1, 2, 1), (3, 4, 1), (1, 3, 2)))) == \ -sqrt(2)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 0))/2 + \ sqrt(2)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, -1))/2 assert uncouple(JzKetCoupled(1, 1, (S(1)/2, S(1)/2, 1, 1), ((1, 2, 1), (3, 4, 1), (1, 3, 1)))) == \ sqrt(2)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 1))/4 - \ sqrt(2)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 0))/4 + \ sqrt(2)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 1))/4 - \ sqrt(2)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 0))/4 - \ TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 1))/2 + \ TensorProduct(JzKet(S( 1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, -1))/2 assert uncouple(JzKetCoupled(1, 0, (S(1)/2, S(1)/2, 1, 1), ((1, 2, 1), (3, 4, 1), (1, 3, 1)))) == \ TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 1))/2 - \ TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 0))/2 - \ TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 0))/2 + \ TensorProduct(JzKet(S( 1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, -1))/2 assert uncouple(JzKetCoupled(1, -1, (S(1)/2, S(1)/2, 1, 1), ((1, 2, 1), (3, 4, 1), (1, 3, 1)))) == \ TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 1))/2 - \ TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, -1))/2 - \ sqrt(2)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 0))/4 + \ sqrt(2)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, -1))/4 - \ sqrt(2)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 0))/4 + \ sqrt(2)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, -1))/4 # j1=1/2, j2=1/2, j3=1, j4=1, j12=1, j34=2 assert uncouple(JzKetCoupled(3, 3, (S(1)/2, S(1)/2, 1, 1), ((1, 2, 1), (3, 4, 2), (1, 3, 3)))) == \ TensorProduct(JzKet( S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 1)) assert uncouple(JzKetCoupled(3, 2, (S(1)/2, S(1)/2, 1, 1), ((1, 2, 1), (3, 4, 2), (1, 3, 3)))) == \ sqrt(6)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 1))/6 + \ sqrt(6)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 1))/6 + \ sqrt(3)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 1))/3 + \ sqrt(3)*TensorProduct(JzKet(S( 1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 0))/3 assert uncouple(JzKetCoupled(3, 1, (S(1)/2, S(1)/2, 1, 1), ((1, 2, 1), (3, 4, 2), (1, 3, 3)))) == \ sqrt(15)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 1))/15 + \ sqrt(30)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 1))/15 + \ sqrt(30)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 0))/15 + \ sqrt(30)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 1))/15 + \ sqrt(30)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 0))/15 + \ sqrt(15)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 1))/15 + \ 2*sqrt(15)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 0))/15 + \ sqrt(15)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, -1))/15 assert uncouple(JzKetCoupled(3, 0, (S(1)/2, S(1)/2, 1, 1), ((1, 2, 1), (3, 4, 2), (1, 3, 3)))) == \ sqrt(10)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 1))/10 + \ sqrt(10)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 0))/10 + \ sqrt(5)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 1))/10 + \ sqrt(5)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 0))/5 + \ sqrt(5)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, -1))/10 + \ sqrt(5)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 1))/10 + \ sqrt(5)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 0))/5 + \ sqrt(5)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, -1))/10 + \ sqrt(10)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 0))/10 + \ sqrt(10)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, -1))/10 assert uncouple(JzKetCoupled(3, -1, (S(1)/2, S(1)/2, 1, 1), ((1, 2, 1), (3, 4, 2), (1, 3, 3)))) == \ sqrt(15)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 1))/15 + \ 2*sqrt(15)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 0))/15 + \ sqrt(15)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, -1))/15 + \ sqrt(30)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 0))/15 + \ sqrt(30)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, -1))/15 + \ sqrt(30)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 0))/15 + \ sqrt(30)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, -1))/15 + \ sqrt(15)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, -1))/15 assert uncouple(JzKetCoupled(3, -2, (S(1)/2, S(1)/2, 1, 1), ((1, 2, 1), (3, 4, 2), (1, 3, 3)))) == \ sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 0))/3 + \ sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, -1))/3 + \ sqrt(6)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, -1))/6 + \ sqrt(6)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, -1))/6 assert uncouple(JzKetCoupled(3, -3, (S(1)/2, S(1)/2, 1, 1), ((1, 2, 1), (3, 4, 2), (1, 3, 3)))) == \ TensorProduct(JzKet(S(1)/2, -S( 1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, -1)) assert uncouple(JzKetCoupled(2, 2, (S(1)/2, S(1)/2, 1, 1), ((1, 2, 1), (3, 4, 2), (1, 3, 2)))) == \ -sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 1))/3 - \ sqrt(3)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 1))/3 + \ sqrt(6)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 1))/6 + \ sqrt(6)*TensorProduct(JzKet(S( 1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 0))/6 assert uncouple(JzKetCoupled(2, 1, (S(1)/2, S(1)/2, 1, 1), ((1, 2, 1), (3, 4, 2), (1, 3, 2)))) == \ -sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 1))/3 - \ sqrt(6)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 1))/12 - \ sqrt(6)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 0))/12 - \ sqrt(6)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 1))/12 - \ sqrt(6)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 0))/12 + \ sqrt(3)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 1))/6 + \ sqrt(3)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 0))/3 + \ sqrt(3)*TensorProduct(JzKet(S( 1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, -1))/6 assert uncouple(JzKetCoupled(2, 0, (S(1)/2, S(1)/2, 1, 1), ((1, 2, 1), (3, 4, 2), (1, 3, 2)))) == \ -TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 1))/2 - \ TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 0))/2 + \ TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 0))/2 + \ TensorProduct(JzKet(S( 1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, -1))/2 assert uncouple(JzKetCoupled(2, -1, (S(1)/2, S(1)/2, 1, 1), ((1, 2, 1), (3, 4, 2), (1, 3, 2)))) == \ -sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 1))/6 - \ sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 0))/3 - \ sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, -1))/6 + \ sqrt(6)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 0))/12 + \ sqrt(6)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, -1))/12 + \ sqrt(6)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 0))/12 + \ sqrt(6)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, -1))/12 + \ sqrt(3)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, -1))/3 assert uncouple(JzKetCoupled(2, -2, (S(1)/2, S(1)/2, 1, 1), ((1, 2, 1), (3, 4, 2), (1, 3, 2)))) == \ -sqrt(6)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 0))/6 - \ sqrt(6)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, -1))/6 + \ sqrt(3)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, -1))/3 + \ sqrt(3)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, -1))/3 assert uncouple(JzKetCoupled(1, 1, (S(1)/2, S(1)/2, 1, 1), ((1, 2, 1), (3, 4, 2), (1, 3, 1)))) == \ sqrt(15)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 1))/5 - \ sqrt(30)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 1))/20 - \ sqrt(30)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 0))/20 - \ sqrt(30)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 1))/20 - \ sqrt(30)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 0))/20 + \ sqrt(15)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 1))/30 + \ sqrt(15)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 0))/15 + \ sqrt(15)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, -1))/30 assert uncouple(JzKetCoupled(1, 0, (S(1)/2, S(1)/2, 1, 1), ((1, 2, 1), (3, 4, 2), (1, 3, 1)))) == \ sqrt(15)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 1))/10 + \ sqrt(15)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, 0))/10 - \ sqrt(30)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 1))/30 - \ sqrt(30)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 0))/15 - \ sqrt(30)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, -1))/30 - \ sqrt(30)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 1))/30 - \ sqrt(30)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 0))/15 - \ sqrt(30)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, -1))/30 + \ sqrt(15)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 0))/10 + \ sqrt(15)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, -1))/10 assert uncouple(JzKetCoupled(1, -1, (S(1)/2, S(1)/2, 1, 1), ((1, 2, 1), (3, 4, 2), (1, 3, 1)))) == \ sqrt(15)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 1))/30 + \ sqrt(15)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, 0))/15 + \ sqrt(15)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1), JzKet(1, -1))/30 - \ sqrt(30)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 0))/20 - \ sqrt(30)*TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, -1))/20 - \ sqrt(30)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1), JzKet(1, 0))/20 - \ sqrt(30)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0), JzKet(1, -1))/20 + \ sqrt(15)*TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, -1))/5 def test_uncouple_symbolic(): assert uncouple(JzKetCoupled(j, m, (j1, j2) )) == \ Sum(CG(j1, m1, j2, m2, j, m) * TensorProduct(JzKet(j1, m1), JzKet(j2, m2)), (m1, -j1, j1), (m2, -j2, j2)) assert uncouple(JzKetCoupled(j, m, (j1, j2, j3) )) == \ Sum(CG(j1, m1, j2, m2, j1 + j2, m1 + m2) * CG(j1 + j2, m1 + m2, j3, m3, j, m) * TensorProduct(JzKet(j1, m1), JzKet(j2, m2), JzKet(j3, m3)), (m1, -j1, j1), (m2, -j2, j2), (m3, -j3, j3)) assert uncouple(JzKetCoupled(j, m, (j1, j2, j3), ((1, 3, j13), (1, 2, j)) )) == \ Sum(CG(j1, m1, j3, m3, j13, m1 + m3) * CG(j13, m1 + m3, j2, m2, j, m) * TensorProduct(JzKet(j1, m1), JzKet(j2, m2), JzKet(j3, m3)), (m1, -j1, j1), (m2, -j2, j2), (m3, -j3, j3)) assert uncouple(JzKetCoupled(j, m, (j1, j2, j3, j4) )) == \ Sum(CG(j1, m1, j2, m2, j1 + j2, m1 + m2) * CG(j1 + j2, m1 + m2, j3, m3, j1 + j2 + j3, m1 + m2 + m3) * CG(j1 + j2 + j3, m1 + m2 + m3, j4, m4, j, m) * TensorProduct( JzKet(j1, m1), JzKet(j2, m2), JzKet(j3, m3), JzKet(j4, m4)), (m1, -j1, j1), (m2, -j2, j2), (m3, -j3, j3), (m4, -j4, j4)) assert uncouple(JzKetCoupled(j, m, (j1, j2, j3, j4), ((1, 3, j13), (2, 4, j24), (1, 2, j)) )) == \ Sum(CG(j1, m1, j3, m3, j13, m1 + m3) * CG(j2, m2, j4, m4, j24, m2 + m4) * CG(j13, m1 + m3, j24, m2 + m4, j, m) * TensorProduct( JzKet(j1, m1), JzKet(j2, m2), JzKet(j3, m3), JzKet(j4, m4)), (m1, -j1, j1), (m2, -j2, j2), (m3, -j3, j3), (m4, -j4, j4)) def test_couple_2_states(): # j1=1/2, j2=1/2 assert JzKetCoupled(0, 0, (S(1)/2, S(1)/2)) == \ expand(couple(uncouple( JzKetCoupled(0, 0, (S(1)/2, S(1)/2)) ))) assert JzKetCoupled(1, 1, (S(1)/2, S(1)/2)) == \ expand(couple(uncouple( JzKetCoupled(1, 1, (S(1)/2, S(1)/2)) ))) assert JzKetCoupled(1, 0, (S(1)/2, S(1)/2)) == \ expand(couple(uncouple( JzKetCoupled(1, 0, (S(1)/2, S(1)/2)) ))) assert JzKetCoupled(1, -1, (S(1)/2, S(1)/2)) == \ expand(couple(uncouple( JzKetCoupled(1, -1, (S(1)/2, S(1)/2)) ))) # j1=1, j2=1/2 assert JzKetCoupled(S(1)/2, S(1)/2, (1, S(1)/2)) == \ expand(couple(uncouple( JzKetCoupled(S(1)/2, S(1)/2, (1, S(1)/2)) ))) assert JzKetCoupled(S(1)/2, -S(1)/2, (1, S(1)/2)) == \ expand(couple(uncouple( JzKetCoupled(S(1)/2, -S(1)/2, (1, S(1)/2)) ))) assert JzKetCoupled(S(3)/2, S(3)/2, (1, S(1)/2)) == \ expand(couple(uncouple( JzKetCoupled(S(3)/2, S(3)/2, (1, S(1)/2)) ))) assert JzKetCoupled(S(3)/2, S(1)/2, (1, S(1)/2)) == \ expand(couple(uncouple( JzKetCoupled(S(3)/2, S(1)/2, (1, S(1)/2)) ))) assert JzKetCoupled(S(3)/2, -S(1)/2, (1, S(1)/2)) == \ expand(couple(uncouple( JzKetCoupled(S(3)/2, -S(1)/2, (1, S(1)/2)) ))) assert JzKetCoupled(S(3)/2, -S(3)/2, (1, S(1)/2)) == \ expand(couple(uncouple( JzKetCoupled(S(3)/2, -S(3)/2, (1, S(1)/2)) ))) # j1=1, j2=1 assert JzKetCoupled(0, 0, (1, 1)) == \ expand(couple(uncouple( JzKetCoupled(0, 0, (1, 1)) ))) assert JzKetCoupled(1, 1, (1, 1)) == \ expand(couple(uncouple( JzKetCoupled(1, 1, (1, 1)) ))) assert JzKetCoupled(1, 0, (1, 1)) == \ expand(couple(uncouple( JzKetCoupled(1, 0, (1, 1)) ))) assert JzKetCoupled(1, -1, (1, 1)) == \ expand(couple(uncouple( JzKetCoupled(1, -1, (1, 1)) ))) assert JzKetCoupled(2, 2, (1, 1)) == \ expand(couple(uncouple( JzKetCoupled(2, 2, (1, 1)) ))) assert JzKetCoupled(2, 1, (1, 1)) == \ expand(couple(uncouple( JzKetCoupled(2, 1, (1, 1)) ))) assert JzKetCoupled(2, 0, (1, 1)) == \ expand(couple(uncouple( JzKetCoupled(2, 0, (1, 1)) ))) assert JzKetCoupled(2, -1, (1, 1)) == \ expand(couple(uncouple( JzKetCoupled(2, -1, (1, 1)) ))) assert JzKetCoupled(2, -2, (1, 1)) == \ expand(couple(uncouple( JzKetCoupled(2, -2, (1, 1)) ))) # j1=1/2, j2=3/2 assert JzKetCoupled(1, 1, (S(1)/2, S(3)/2)) == \ expand(couple(uncouple( JzKetCoupled(1, 1, (S(1)/2, S(3)/2)) ))) assert JzKetCoupled(1, 0, (S(1)/2, S(3)/2)) == \ expand(couple(uncouple( JzKetCoupled(1, 0, (S(1)/2, S(3)/2)) ))) assert JzKetCoupled(1, -1, (S(1)/2, S(3)/2)) == \ expand(couple(uncouple( JzKetCoupled(1, -1, (S(1)/2, S(3)/2)) ))) assert JzKetCoupled(2, 2, (S(1)/2, S(3)/2)) == \ expand(couple(uncouple( JzKetCoupled(2, 2, (S(1)/2, S(3)/2)) ))) assert JzKetCoupled(2, 1, (S(1)/2, S(3)/2)) == \ expand(couple(uncouple( JzKetCoupled(2, 1, (S(1)/2, S(3)/2)) ))) assert JzKetCoupled(2, 0, (S(1)/2, S(3)/2)) == \ expand(couple(uncouple( JzKetCoupled(2, 0, (S(1)/2, S(3)/2)) ))) assert JzKetCoupled(2, -1, (S(1)/2, S(3)/2)) == \ expand(couple(uncouple( JzKetCoupled(2, -1, (S(1)/2, S(3)/2)) ))) assert JzKetCoupled(2, -2, (S(1)/2, S(3)/2)) == \ expand(couple(uncouple( JzKetCoupled(2, -2, (S(1)/2, S(3)/2)) ))) def test_couple_3_states(): # Default coupling # j1=1/2, j2=1/2, j3=1/2 assert JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2)) == \ expand(couple(uncouple( JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2)) ))) assert JzKetCoupled(S(1)/2, S(-1)/2, (S(1)/2, S(1)/2, S(1)/2)) == \ expand(couple(uncouple( JzKetCoupled(S(1)/2, S(-1)/2, (S(1)/2, S(1)/2, S(1)/2)) ))) assert JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2)) == \ expand(couple(uncouple( JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2)) ))) assert JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2)) == \ expand(couple(uncouple( JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2)) ))) assert JzKetCoupled(S(3)/2, S(-1)/2, (S(1)/2, S(1)/2, S(1)/2)) == \ expand(couple(uncouple( JzKetCoupled(S(3)/2, S(-1)/2, (S(1)/2, S(1)/2, S(1)/2)) ))) assert JzKetCoupled(S(3)/2, S(-3)/2, (S(1)/2, S(1)/2, S(1)/2)) == \ expand(couple(uncouple( JzKetCoupled(S(3)/2, S(-3)/2, (S(1)/2, S(1)/2, S(1)/2)) ))) # j1=1/2, j2=1/2, j3=1 assert JzKetCoupled(0, 0, (S(1)/2, S(1)/2, 1)) == \ expand(couple(uncouple( JzKetCoupled(0, 0, (S(1)/2, S(1)/2, 1)) ))) assert JzKetCoupled(1, 1, (S(1)/2, S(1)/2, 1)) == \ expand(couple(uncouple( JzKetCoupled(1, 1, (S(1)/2, S(1)/2, 1)) ))) assert JzKetCoupled(1, 0, (S(1)/2, S(1)/2, 1)) == \ expand(couple(uncouple( JzKetCoupled(1, 0, (S(1)/2, S(1)/2, 1)) ))) assert JzKetCoupled(1, -1, (S(1)/2, S(1)/2, 1)) == \ expand(couple(uncouple( JzKetCoupled(1, -1, (S(1)/2, S(1)/2, 1)) ))) assert JzKetCoupled(2, 2, (S(1)/2, S(1)/2, 1)) == \ expand(couple(uncouple( JzKetCoupled(2, 2, (S(1)/2, S(1)/2, 1)) ))) assert JzKetCoupled(2, 1, (S(1)/2, S(1)/2, 1)) == \ expand(couple(uncouple( JzKetCoupled(2, 1, (S(1)/2, S(1)/2, 1)) ))) assert JzKetCoupled(2, 0, (S(1)/2, S(1)/2, 1)) == \ expand(couple(uncouple( JzKetCoupled(2, 0, (S(1)/2, S(1)/2, 1)) ))) assert JzKetCoupled(2, -1, (S(1)/2, S(1)/2, 1)) == \ expand(couple(uncouple( JzKetCoupled(2, -1, (S(1)/2, S(1)/2, 1)) ))) assert JzKetCoupled(2, -2, (S(1)/2, S(1)/2, 1)) == \ expand(couple(uncouple( JzKetCoupled(2, -2, (S(1)/2, S(1)/2, 1)) ))) # Couple j1+j3=j13, j13+j2=j # j1=1/2, j2=1/2, j3=1/2, j13=0 assert JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 3, 0), (1, 2, S(1)/2))) == \ expand(couple(uncouple( JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S( 1)/2, S(1)/2), ((1, 3, 0), (1, 2, S(1)/2))) ), ((1, 3), (1, 2)) )) assert JzKetCoupled(S(1)/2, S(-1)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 3, 0), (1, 2, S(1)/2))) == \ expand(couple(uncouple( JzKetCoupled(S(1)/2, S(-1)/2, (S(1)/2, S( 1)/2, S(1)/2), ((1, 3, 0), (1, 2, S(1)/2))) ), ((1, 3), (1, 2)) )) # j1=1, j2=1/2, j3=1, j13=1 assert JzKetCoupled(S(1)/2, S(1)/2, (1, S(1)/2, 1), ((1, 3, 1), (1, 2, S(1)/2))) == \ expand(couple(uncouple( JzKetCoupled(S(1)/2, S(1)/2, ( 1, S(1)/2, 1), ((1, 3, 1), (1, 2, S(1)/2))) ), ((1, 3), (1, 2)) )) assert JzKetCoupled(S(1)/2, S(-1)/2, (1, S(1)/2, 1), ((1, 3, 1), (1, 2, S(1)/2))) == \ expand(couple(uncouple( JzKetCoupled(S(1)/2, S(-1)/2, ( 1, S(1)/2, 1), ((1, 3, 1), (1, 2, S(1)/2))) ), ((1, 3), (1, 2)) )) assert JzKetCoupled(S(3)/2, S(3)/2, (1, S(1)/2, 1), ((1, 3, 1), (1, 2, S(3)/2))) == \ expand(couple(uncouple( JzKetCoupled(S(3)/2, S(3)/2, ( 1, S(1)/2, 1), ((1, 3, 1), (1, 2, S(3)/2))) ), ((1, 3), (1, 2)) )) assert JzKetCoupled(S(3)/2, S(1)/2, (1, S(1)/2, 1), ((1, 3, 1), (1, 2, S(3)/2))) == \ expand(couple(uncouple( JzKetCoupled(S(3)/2, S(1)/2, ( 1, S(1)/2, 1), ((1, 3, 1), (1, 2, S(3)/2))) ), ((1, 3), (1, 2)) )) assert JzKetCoupled(S(3)/2, S(-1)/2, (1, S(1)/2, 1), ((1, 3, 1), (1, 2, S(3)/2))) == \ expand(couple(uncouple( JzKetCoupled(S(3)/2, S(-1)/2, ( 1, S(1)/2, 1), ((1, 3, 1), (1, 2, S(3)/2))) ), ((1, 3), (1, 2)) )) assert JzKetCoupled(S(3)/2, S(-3)/2, (1, S(1)/2, 1), ((1, 3, 1), (1, 2, S(3)/2))) == \ expand(couple(uncouple( JzKetCoupled(S(3)/2, S(-3)/2, ( 1, S(1)/2, 1), ((1, 3, 1), (1, 2, S(3)/2))) ), ((1, 3), (1, 2)) )) def test_couple_4_states(): # Default coupling # j1=1/2, j2=1/2, j3=1/2, j4=1/2 assert JzKetCoupled(1, 1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2)) == \ expand(couple( uncouple( JzKetCoupled(1, 1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2)) ))) assert JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2)) == \ expand(couple( uncouple( JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2)) ))) assert JzKetCoupled(1, -1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2)) == \ expand(couple(uncouple( JzKetCoupled(1, -1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2)) ))) assert JzKetCoupled(2, 2, (S(1)/2, S(1)/2, S(1)/2, S(1)/2)) == \ expand(couple( uncouple( JzKetCoupled(2, 2, (S(1)/2, S(1)/2, S(1)/2, S(1)/2)) ))) assert JzKetCoupled(2, 1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2)) == \ expand(couple( uncouple( JzKetCoupled(2, 1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2)) ))) assert JzKetCoupled(2, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2)) == \ expand(couple( uncouple( JzKetCoupled(2, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2)) ))) assert JzKetCoupled(2, -1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2)) == \ expand(couple(uncouple( JzKetCoupled(2, -1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2)) ))) assert JzKetCoupled(2, -2, (S(1)/2, S(1)/2, S(1)/2, S(1)/2)) == \ expand(couple(uncouple( JzKetCoupled(2, -2, (S(1)/2, S(1)/2, S(1)/2, S(1)/2)) ))) # j1=1/2, j2=1/2, j3=1/2, j4=1 assert JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1)) == \ expand(couple(uncouple( JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1)) ))) assert JzKetCoupled(S(1)/2, S(-1)/2, (S(1)/2, S(1)/2, S(1)/2, 1)) == \ expand(couple(uncouple( JzKetCoupled(S(1)/2, S(-1)/2, (S(1)/2, S(1)/2, S(1)/2, 1)) ))) assert JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1)) == \ expand(couple(uncouple( JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1)) ))) assert JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1)) == \ expand(couple(uncouple( JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1)) ))) assert JzKetCoupled(S(3)/2, S(-1)/2, (S(1)/2, S(1)/2, S(1)/2, 1)) == \ expand(couple(uncouple( JzKetCoupled(S(3)/2, S(-1)/2, (S(1)/2, S(1)/2, S(1)/2, 1)) ))) assert JzKetCoupled(S(3)/2, S(-3)/2, (S(1)/2, S(1)/2, S(1)/2, 1)) == \ expand(couple(uncouple( JzKetCoupled(S(3)/2, S(-3)/2, (S(1)/2, S(1)/2, S(1)/2, 1)) ))) assert JzKetCoupled(S(5)/2, S(5)/2, (S(1)/2, S(1)/2, S(1)/2, 1)) == \ expand(couple(uncouple( JzKetCoupled(S(5)/2, S(5)/2, (S(1)/2, S(1)/2, S(1)/2, 1)) ))) assert JzKetCoupled(S(5)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1)) == \ expand(couple(uncouple( JzKetCoupled(S(5)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1)) ))) assert JzKetCoupled(S(5)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1)) == \ expand(couple(uncouple( JzKetCoupled(S(5)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1)) ))) assert JzKetCoupled(S(5)/2, S(-1)/2, (S(1)/2, S(1)/2, S(1)/2, 1)) == \ expand(couple(uncouple( JzKetCoupled(S(5)/2, S(-1)/2, (S(1)/2, S(1)/2, S(1)/2, 1)) ))) assert JzKetCoupled(S(5)/2, S(-3)/2, (S(1)/2, S(1)/2, S(1)/2, 1)) == \ expand(couple(uncouple( JzKetCoupled(S(5)/2, S(-3)/2, (S(1)/2, S(1)/2, S(1)/2, 1)) ))) assert JzKetCoupled(S(5)/2, S(-5)/2, (S(1)/2, S(1)/2, S(1)/2, 1)) == \ expand(couple(uncouple( JzKetCoupled(S(5)/2, S(-5)/2, (S(1)/2, S(1)/2, S(1)/2, 1)) ))) # Coupling j1+j3=j13, j2+j4=j24, j13+j24=j # j1=1/2, j2=1/2, j3=1/2, j4=1/2, j13=1, j24=0 assert JzKetCoupled(1, 1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 3, 1), (2, 4, 0), (1, 2, 1)) ) == \ expand(couple(uncouple( JzKetCoupled(1, 1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 3, 1), (2, 4, 0), (1, 2, 1)) ) ), ((1, 3), (2, 4), (1, 2)) )) assert JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 3, 1), (2, 4, 0), (1, 2, 1)) ) == \ expand(couple(uncouple( JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 3, 1), (2, 4, 0), (1, 2, 1)) ) ), ((1, 3), (2, 4), (1, 2)) )) assert JzKetCoupled(1, -1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 3, 1), (2, 4, 0), (1, 2, 1)) ) == \ expand(couple(uncouple( JzKetCoupled(1, -1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 3, 1), (2, 4, 0), (1, 2, 1)) ) ), ((1, 3), (2, 4), (1, 2)) )) # j1=1/2, j2=1/2, j3=1/2, j4=1, j13=1, j24=1/2 assert JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 3, 1), (2, 4, S(1)/2), (1, 2, S(1)/2)) ) == \ expand(couple(uncouple( JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 3, 1), (2, 4, S(1)/2), (1, 2, S(1)/2)) )), ((1, 3), (2, 4), (1, 2)) )) assert JzKetCoupled(S(1)/2, S(-1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 3, 1), (2, 4, S(1)/2), (1, 2, S(1)/2)) ) == \ expand(couple(uncouple( JzKetCoupled(S(1)/2, S(-1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 3, 1), (2, 4, S(1)/2), (1, 2, S(1)/2)) ) ), ((1, 3), (2, 4), (1, 2)) )) assert JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 3, 1), (2, 4, S(1)/2), (1, 2, S(3)/2)) ) == \ expand(couple(uncouple( JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 3, 1), (2, 4, S(1)/2), (1, 2, S(3)/2)) ) ), ((1, 3), (2, 4), (1, 2)) )) assert JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 3, 1), (2, 4, S(1)/2), (1, 2, S(3)/2)) ) == \ expand(couple(uncouple( JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 3, 1), (2, 4, S(1)/2), (1, 2, S(3)/2)) ) ), ((1, 3), (2, 4), (1, 2)) )) assert JzKetCoupled(S(3)/2, S(-1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 3, 1), (2, 4, S(1)/2), (1, 2, S(3)/2)) ) == \ expand(couple(uncouple( JzKetCoupled(S(3)/2, S(-1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 3, 1), (2, 4, S(1)/2), (1, 2, S(3)/2)) ) ), ((1, 3), (2, 4), (1, 2)) )) assert JzKetCoupled(S(3)/2, S(-3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 3, 1), (2, 4, S(1)/2), (1, 2, S(3)/2)) ) == \ expand(couple(uncouple( JzKetCoupled(S(3)/2, S(-3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 3, 1), (2, 4, S(1)/2), (1, 2, S(3)/2)) ) ), ((1, 3), (2, 4), (1, 2)) )) # j1=1/2, j2=1, j3=1/2, j4=1, j13=0, j24=1 assert JzKetCoupled(1, 1, (S(1)/2, 1, S(1)/2, 1), ((1, 3, 0), (2, 4, 1), (1, 2, 1)) ) == \ expand(couple(uncouple( JzKetCoupled(1, 1, (S(1)/2, 1, S(1)/2, 1), ( (1, 3, 0), (2, 4, 1), (1, 2, 1))) ), ((1, 3), (2, 4), (1, 2)) )) assert JzKetCoupled(1, 0, (S(1)/2, 1, S(1)/2, 1), ((1, 3, 0), (2, 4, 1), (1, 2, 1)) ) == \ expand(couple(uncouple( JzKetCoupled(1, 0, (S(1)/2, 1, S(1)/2, 1), ( (1, 3, 0), (2, 4, 1), (1, 2, 1))) ), ((1, 3), (2, 4), (1, 2)) )) assert JzKetCoupled(1, -1, (S(1)/2, 1, S(1)/2, 1), ((1, 3, 0), (2, 4, 1), (1, 2, 1)) ) == \ expand(couple(uncouple( JzKetCoupled(1, -1, (S(1)/2, 1, S(1)/2, 1), ( (1, 3, 0), (2, 4, 1), (1, 2, 1))) ), ((1, 3), (2, 4), (1, 2)) )) # j1=1/2, j2=1, j3=1/2, j4=1, j13=1, j24=1 assert JzKetCoupled(0, 0, (S(1)/2, 1, S(1)/2, 1), ((1, 3, 1), (2, 4, 1), (1, 2, 0)) ) == \ expand(couple(uncouple( JzKetCoupled(0, 0, (S(1)/2, 1, S(1)/2, 1), ( (1, 3, 1), (2, 4, 1), (1, 2, 0))) ), ((1, 3), (2, 4), (1, 2)) )) assert JzKetCoupled(1, 1, (S(1)/2, 1, S(1)/2, 1), ((1, 3, 1), (2, 4, 1), (1, 2, 1)) ) == \ expand(couple(uncouple( JzKetCoupled(1, 1, (S(1)/2, 1, S(1)/2, 1), ( (1, 3, 1), (2, 4, 1), (1, 2, 1))) ), ((1, 3), (2, 4), (1, 2)) )) assert JzKetCoupled(1, 0, (S(1)/2, 1, S(1)/2, 1), ((1, 3, 1), (2, 4, 1), (1, 2, 1)) ) == \ expand(couple(uncouple( JzKetCoupled(1, 0, (S(1)/2, 1, S(1)/2, 1), ( (1, 3, 1), (2, 4, 1), (1, 2, 1))) ), ((1, 3), (2, 4), (1, 2)) )) assert JzKetCoupled(1, -1, (S(1)/2, 1, S(1)/2, 1), ((1, 3, 1), (2, 4, 1), (1, 2, 1)) ) == \ expand(couple(uncouple( JzKetCoupled(1, -1, (S(1)/2, 1, S(1)/2, 1), ( (1, 3, 1), (2, 4, 1), (1, 2, 1))) ), ((1, 3), (2, 4), (1, 2)) )) assert JzKetCoupled(2, 2, (S(1)/2, 1, S(1)/2, 1), ((1, 3, 1), (2, 4, 1), (1, 2, 2)) ) == \ expand(couple(uncouple( JzKetCoupled(2, 2, (S(1)/2, 1, S(1)/2, 1), ( (1, 3, 1), (2, 4, 1), (1, 2, 2))) ), ((1, 3), (2, 4), (1, 2)) )) assert JzKetCoupled(2, 1, (S(1)/2, 1, S(1)/2, 1), ((1, 3, 1), (2, 4, 1), (1, 2, 2)) ) == \ expand(couple(uncouple( JzKetCoupled(2, 1, (S(1)/2, 1, S(1)/2, 1), ( (1, 3, 1), (2, 4, 1), (1, 2, 2))) ), ((1, 3), (2, 4), (1, 2)) )) assert JzKetCoupled(2, 0, (S(1)/2, 1, S(1)/2, 1), ((1, 3, 1), (2, 4, 1), (1, 2, 2)) ) == \ expand(couple(uncouple( JzKetCoupled(2, 0, (S(1)/2, 1, S(1)/2, 1), ( (1, 3, 1), (2, 4, 1), (1, 2, 2))) ), ((1, 3), (2, 4), (1, 2)) )) assert JzKetCoupled(2, -1, (S(1)/2, 1, S(1)/2, 1), ((1, 3, 1), (2, 4, 1), (1, 2, 2)) ) == \ expand(couple(uncouple( JzKetCoupled(2, -1, (S(1)/2, 1, S(1)/2, 1), ( (1, 3, 1), (2, 4, 1), (1, 2, 2))) ), ((1, 3), (2, 4), (1, 2)) )) assert JzKetCoupled(2, -2, (S(1)/2, 1, S(1)/2, 1), ((1, 3, 1), (2, 4, 1), (1, 2, 2)) ) == \ expand(couple(uncouple( JzKetCoupled(2, -2, (S(1)/2, 1, S(1)/2, 1), ( (1, 3, 1), (2, 4, 1), (1, 2, 2))) ), ((1, 3), (2, 4), (1, 2)) )) def test_couple_2_states_numerical(): # j1=1/2, j2=1/2 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2))) == \ JzKetCoupled(1, 1, (S(1)/2, S(1)/2)) assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2))) == \ sqrt(2)*JzKetCoupled(0, 0, (S( 1)/2, S(1)/2))/2 + sqrt(2)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2))/2 assert couple(TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2))) == \ -sqrt(2)*JzKetCoupled(0, 0, (S( 1)/2, S(1)/2))/2 + sqrt(2)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2))/2 assert couple(TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2))) == \ JzKetCoupled(1, -1, (S(1)/2, S(1)/2)) # j1=1, j2=1/2 assert couple(TensorProduct(JzKet(1, 1), JzKet(S(1)/2, S(1)/2))) == \ JzKetCoupled(S(3)/2, S(3)/2, (1, S(1)/2)) assert couple(TensorProduct(JzKet(1, 1), JzKet(S(1)/2, -S(1)/2))) == \ sqrt(6)*JzKetCoupled(S(1)/2, S(1)/2, (1, S(1)/2))/3 + sqrt( 3)*JzKetCoupled(S(3)/2, S(1)/2, (1, S(1)/2))/3 assert couple(TensorProduct(JzKet(1, 0), JzKet(S(1)/2, S(1)/2))) == \ -sqrt(3)*JzKetCoupled(S(1)/2, S(1)/2, (1, S(1)/2))/3 + \ sqrt(6)*JzKetCoupled(S(3)/2, S(1)/2, (1, S(1)/2))/3 assert couple(TensorProduct(JzKet(1, 0), JzKet(S(1)/2, -S(1)/2))) == \ sqrt(3)*JzKetCoupled(S(1)/2, -S(1)/2, (1, S(1)/2))/3 + \ sqrt(6)*JzKetCoupled(S(3)/2, -S(1)/2, (1, S(1)/2))/3 assert couple(TensorProduct(JzKet(1, -1), JzKet(S(1)/2, S(1)/2))) == \ -sqrt(6)*JzKetCoupled(S(1)/2, -S(1)/2, (1, S( 1)/2))/3 + sqrt(3)*JzKetCoupled(S(3)/2, -S(1)/2, (1, S(1)/2))/3 assert couple(TensorProduct(JzKet(1, -1), JzKet(S(1)/2, -S(1)/2))) == \ JzKetCoupled(S(3)/2, -S(3)/2, (1, S(1)/2)) # j1=1, j2=1 assert couple(TensorProduct(JzKet(1, 1), JzKet(1, 1))) == \ JzKetCoupled(2, 2, (1, 1)) assert couple(TensorProduct(JzKet(1, 1), JzKet(1, 0))) == \ sqrt(2)*JzKetCoupled( 1, 1, (1, 1))/2 + sqrt(2)*JzKetCoupled(2, 1, (1, 1))/2 assert couple(TensorProduct(JzKet(1, 1), JzKet(1, -1))) == \ sqrt(3)*JzKetCoupled(0, 0, (1, 1))/3 + sqrt(2)*JzKetCoupled( 1, 0, (1, 1))/2 + sqrt(6)*JzKetCoupled(2, 0, (1, 1))/6 assert couple(TensorProduct(JzKet(1, 0), JzKet(1, 1))) == \ -sqrt(2)*JzKetCoupled( 1, 1, (1, 1))/2 + sqrt(2)*JzKetCoupled(2, 1, (1, 1))/2 assert couple(TensorProduct(JzKet(1, 0), JzKet(1, 0))) == \ -sqrt(3)*JzKetCoupled( 0, 0, (1, 1))/3 + sqrt(6)*JzKetCoupled(2, 0, (1, 1))/3 assert couple(TensorProduct(JzKet(1, 0), JzKet(1, -1))) == \ sqrt(2)*JzKetCoupled( 1, -1, (1, 1))/2 + sqrt(2)*JzKetCoupled(2, -1, (1, 1))/2 assert couple(TensorProduct(JzKet(1, -1), JzKet(1, 1))) == \ sqrt(3)*JzKetCoupled(0, 0, (1, 1))/3 - sqrt(2)*JzKetCoupled( 1, 0, (1, 1))/2 + sqrt(6)*JzKetCoupled(2, 0, (1, 1))/6 assert couple(TensorProduct(JzKet(1, -1), JzKet(1, 0))) == \ -sqrt(2)*JzKetCoupled( 1, -1, (1, 1))/2 + sqrt(2)*JzKetCoupled(2, -1, (1, 1))/2 assert couple(TensorProduct(JzKet(1, -1), JzKet(1, -1))) == \ JzKetCoupled(2, -2, (1, 1)) # j1=3/2, j2=1/2 assert couple(TensorProduct(JzKet(S(3)/2, S(3)/2), JzKet(S(1)/2, S(1)/2))) == \ JzKetCoupled(2, 2, (S(3)/2, S(1)/2)) assert couple(TensorProduct(JzKet(S(3)/2, S(3)/2), JzKet(S(1)/2, -S(1)/2))) == \ sqrt(3)*JzKetCoupled( 1, 1, (S(3)/2, S(1)/2))/2 + JzKetCoupled(2, 1, (S(3)/2, S(1)/2))/2 assert couple(TensorProduct(JzKet(S(3)/2, S(1)/2), JzKet(S(1)/2, S(1)/2))) == \ -JzKetCoupled(1, 1, (S( 3)/2, S(1)/2))/2 + sqrt(3)*JzKetCoupled(2, 1, (S(3)/2, S(1)/2))/2 assert couple(TensorProduct(JzKet(S(3)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2))) == \ sqrt(2)*JzKetCoupled(1, 0, (S( 3)/2, S(1)/2))/2 + sqrt(2)*JzKetCoupled(2, 0, (S(3)/2, S(1)/2))/2 assert couple(TensorProduct(JzKet(S(3)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2))) == \ -sqrt(2)*JzKetCoupled(1, 0, (S( 3)/2, S(1)/2))/2 + sqrt(2)*JzKetCoupled(2, 0, (S(3)/2, S(1)/2))/2 assert couple(TensorProduct(JzKet(S(3)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2))) == \ JzKetCoupled(1, -1, (S( 3)/2, S(1)/2))/2 + sqrt(3)*JzKetCoupled(2, -1, (S(3)/2, S(1)/2))/2 assert couple(TensorProduct(JzKet(S(3)/2, -S(3)/2), JzKet(S(1)/2, S(1)/2))) == \ -sqrt(3)*JzKetCoupled(1, -1, (S(3)/2, S(1)/2))/2 + \ JzKetCoupled(2, -1, (S(3)/2, S(1)/2))/2 assert couple(TensorProduct(JzKet(S(3)/2, -S(3)/2), JzKet(S(1)/2, -S(1)/2))) == \ JzKetCoupled(2, -2, (S(3)/2, S(1)/2)) def test_couple_3_states_numerical(): # Default coupling # j1=1/2,j2=1/2,j3=1/2 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2))) == \ JzKetCoupled(S(3)/2, S( 3)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2)) ) assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2))) == \ sqrt(6)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(1)/2)) )/3 + \ sqrt(3)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/ 2), ((1, 2, 1), (1, 3, S(3)/2)) )/3 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2))) == \ sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (1, 3, S(1)/2)) )/2 - \ sqrt(6)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(1)/2)) )/6 + \ sqrt(3)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/ 2), ((1, 2, 1), (1, 3, S(3)/2)) )/3 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2))) == \ sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (1, 3, S(1)/2)) )/2 + \ sqrt(6)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(1)/2)) )/6 + \ sqrt(3)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1) /2), ((1, 2, 1), (1, 3, S(3)/2)) )/3 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2))) == \ -sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (1, 3, S(1)/2)) )/2 - \ sqrt(6)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(1)/2)) )/6 + \ sqrt(3)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/ 2), ((1, 2, 1), (1, 3, S(3)/2)) )/3 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2))) == \ -sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (1, 3, S(1)/2)) )/2 + \ sqrt(6)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(1)/2)) )/6 + \ sqrt(3)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1) /2), ((1, 2, 1), (1, 3, S(3)/2)) )/3 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2))) == \ -sqrt(6)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(1)/2)) )/3 + \ sqrt(3)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1) /2), ((1, 2, 1), (1, 3, S(3)/2)) )/3 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2))) == \ JzKetCoupled(S(3)/2, -S( 3)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2)) ) # j1=S(1)/2, j2=S(1)/2, j3=1 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1))) == \ JzKetCoupled(2, 2, (S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, 2)) ) assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0))) == \ sqrt(2)*JzKetCoupled(1, 1, (S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, 1)) )/2 + \ sqrt(2)*JzKetCoupled( 2, 1, (S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1))) == \ sqrt(3)*JzKetCoupled(0, 0, (S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, 0)) )/3 + \ sqrt(2)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, 1)) )/2 + \ sqrt(6)*JzKetCoupled( 2, 0, (S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, 2)) )/6 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1))) == \ sqrt(2)*JzKetCoupled(1, 1, (S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, 1)) )/2 - \ JzKetCoupled(1, 1, (S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, 1)) )/2 + \ JzKetCoupled(2, 1, (S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0))) == \ -sqrt(6)*JzKetCoupled(0, 0, (S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, 0)) )/6 + \ sqrt(2)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, 1)) )/2 + \ sqrt(3)*JzKetCoupled( 2, 0, (S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, 2)) )/3 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, -1))) == \ sqrt(2)*JzKetCoupled(1, -1, (S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, 1)) )/2 + \ JzKetCoupled(1, -1, (S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, 1)) )/2 + \ JzKetCoupled(2, -1, (S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1))) == \ -sqrt(2)*JzKetCoupled(1, 1, (S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, 1)) )/2 - \ JzKetCoupled(1, 1, (S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, 1)) )/2 + \ JzKetCoupled(2, 1, (S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0))) == \ -sqrt(6)*JzKetCoupled(0, 0, (S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, 0)) )/6 - \ sqrt(2)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, 1)) )/2 + \ sqrt(3)*JzKetCoupled( 2, 0, (S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, 2)) )/3 assert couple(TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1))) == \ -sqrt(2)*JzKetCoupled(1, -1, (S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, 1)) )/2 + \ JzKetCoupled(1, -1, (S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, 1)) )/2 + \ JzKetCoupled(2, -1, (S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 1))) == \ sqrt(3)*JzKetCoupled(0, 0, (S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, 0)) )/3 - \ sqrt(2)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, 1)) )/2 + \ sqrt(6)*JzKetCoupled( 2, 0, (S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, 2)) )/6 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, 0))) == \ -sqrt(2)*JzKetCoupled(1, -1, (S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, 1)) )/2 + \ sqrt(2)*JzKetCoupled( 2, -1, (S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, -S(1)/2), JzKet(S(1)/2, -S(1)/2), JzKet(1, -1))) == \ JzKetCoupled(2, -2, (S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, 2)) ) # j1=S(1)/2, j2=1, j3=1 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 1))) == \ JzKetCoupled( S(5)/2, S(5)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(5)/2)) ) assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 0))) == \ sqrt(15)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(3)/2)) )/5 + \ sqrt(10)*JzKetCoupled(S( 5)/2, S(3)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, -1))) == \ sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(1)/2)) )/2 + \ sqrt(10)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(3)/2)) )/5 + \ sqrt(10)*JzKetCoupled(S(5)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 1))) == \ sqrt(3)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, 1, 1), ((1, 2, S(1)/2), (1, 3, S(3)/2)) )/3 - \ 2*sqrt(15)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(3)/2)) )/15 + \ sqrt(10)*JzKetCoupled(S( 5)/2, S(3)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 0))) == \ JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(1)/2), (1, 3, S(1)/2)) )/3 - \ sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(1)/2)) )/3 + \ sqrt(2)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(1)/2), (1, 3, S(3)/2)) )/3 + \ sqrt(10)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(3)/2)) )/15 + \ sqrt(10)*JzKetCoupled(S( 5)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, -1))) == \ sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(1)/2), (1, 3, S(1)/2)) )/3 + \ JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(1)/2)) )/3 + \ JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(1)/2), (1, 3, S(3)/2)) )/3 + \ 4*sqrt(5)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 1))) == \ -2*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(1)/2), (1, 3, S(1)/2)) )/3 + \ sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(1)/2)) )/6 + \ sqrt(2)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(1)/2), (1, 3, S(3)/2)) )/3 - \ 2*sqrt(10)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(3)/2)) )/15 + \ sqrt(10)*JzKetCoupled(S(5)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 0))) == \ -sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(1)/2), (1, 3, S(1)/2)) )/3 - \ JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(1)/2)) )/3 + \ 2*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(1)/2), (1, 3, S(3)/2)) )/3 - \ sqrt(5)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, -1))) == \ sqrt(6)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, 1, 1), ((1, 2, S(1)/2), (1, 3, S(3)/2)) )/3 + \ sqrt(30)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, -S(3)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(1, 1))) == \ -sqrt(6)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, 1, 1), ((1, 2, S(1)/2), (1, 3, S(3)/2)) )/3 - \ sqrt(30)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S( 5)/2, S(3)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(1, 0))) == \ -sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(1)/2), (1, 3, S(1)/2)) )/3 - \ JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(1)/2)) )/3 - \ 2*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(1)/2), (1, 3, S(3)/2)) )/3 + \ sqrt(5)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S( 5)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(1, -1))) == \ -2*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(1)/2), (1, 3, S(1)/2)) )/3 + \ sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(1)/2)) )/6 - \ sqrt(2)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(1)/2), (1, 3, S(3)/2)) )/3 + \ 2*sqrt(10)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(3)/2)) )/15 + \ sqrt(10)*JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(1, 1))) == \ sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(1)/2), (1, 3, S(1)/2)) )/3 + \ JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(1)/2)) )/3 - \ JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(1)/2), (1, 3, S(3)/2)) )/3 - \ 4*sqrt(5)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S( 5)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(1, 0))) == \ JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(1)/2), (1, 3, S(1)/2)) )/3 - \ sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(1)/2)) )/3 - \ sqrt(2)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(1)/2), (1, 3, S(3)/2)) )/3 - \ sqrt(10)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(3)/2)) )/15 + \ sqrt(10)*JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(1, -1))) == \ -sqrt(3)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, 1, 1), ((1, 2, S(1)/2), (1, 3, S(3)/2)) )/3 + \ 2*sqrt(15)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(3)/2)) )/15 + \ sqrt(10)*JzKetCoupled(S(5)/2, -S(3)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(1, 1))) == \ sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(1)/2)) )/2 - \ sqrt(10)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(3)/2)) )/5 + \ sqrt(10)*JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(1, 0))) == \ -sqrt(15)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(3)/2)) )/5 + \ sqrt(10)*JzKetCoupled(S(5)/2, -S(3)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(1, -1))) == \ JzKetCoupled(S( 5)/2, -S(5)/2, (S(1)/2, 1, 1), ((1, 2, S(3)/2), (1, 3, S(5)/2)) ) # j1=1, j2=1, j3=1 assert couple(TensorProduct(JzKet(1, 1), JzKet(1, 1), JzKet(1, 1))) == \ JzKetCoupled(3, 3, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) ) assert couple(TensorProduct(JzKet(1, 1), JzKet(1, 1), JzKet(1, 0))) == \ sqrt(6)*JzKetCoupled(2, 2, (1, 1, 1), ((1, 2, 2), (1, 3, 2)) )/3 + \ sqrt(3)*JzKetCoupled(3, 2, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/3 assert couple(TensorProduct(JzKet(1, 1), JzKet(1, 1), JzKet(1, -1))) == \ sqrt(15)*JzKetCoupled(1, 1, (1, 1, 1), ((1, 2, 2), (1, 3, 1)) )/5 + \ sqrt(3)*JzKetCoupled(2, 1, (1, 1, 1), ((1, 2, 2), (1, 3, 2)) )/3 + \ sqrt(15)*JzKetCoupled(3, 1, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/15 assert couple(TensorProduct(JzKet(1, 1), JzKet(1, 0), JzKet(1, 1))) == \ sqrt(2)*JzKetCoupled(2, 2, (1, 1, 1), ((1, 2, 1), (1, 3, 2)) )/2 - \ sqrt(6)*JzKetCoupled(2, 2, (1, 1, 1), ((1, 2, 2), (1, 3, 2)) )/6 + \ sqrt(3)*JzKetCoupled(3, 2, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/3 assert couple(TensorProduct(JzKet(1, 1), JzKet(1, 0), JzKet(1, 0))) == \ JzKetCoupled(1, 1, (1, 1, 1), ((1, 2, 1), (1, 3, 1)) )/2 - \ sqrt(15)*JzKetCoupled(1, 1, (1, 1, 1), ((1, 2, 2), (1, 3, 1)) )/10 + \ JzKetCoupled(2, 1, (1, 1, 1), ((1, 2, 1), (1, 3, 2)) )/2 + \ sqrt(3)*JzKetCoupled(2, 1, (1, 1, 1), ((1, 2, 2), (1, 3, 2)) )/6 + \ 2*sqrt(15)*JzKetCoupled(3, 1, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/15 assert couple(TensorProduct(JzKet(1, 1), JzKet(1, 0), JzKet(1, -1))) == \ sqrt(6)*JzKetCoupled(0, 0, (1, 1, 1), ((1, 2, 1), (1, 3, 0)) )/6 + \ JzKetCoupled(1, 0, (1, 1, 1), ((1, 2, 1), (1, 3, 1)) )/2 + \ sqrt(15)*JzKetCoupled(1, 0, (1, 1, 1), ((1, 2, 2), (1, 3, 1)) )/10 + \ sqrt(3)*JzKetCoupled(2, 0, (1, 1, 1), ((1, 2, 1), (1, 3, 2)) )/6 + \ JzKetCoupled(2, 0, (1, 1, 1), ((1, 2, 2), (1, 3, 2)) )/2 + \ sqrt(10)*JzKetCoupled(3, 0, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/10 assert couple(TensorProduct(JzKet(1, 1), JzKet(1, -1), JzKet(1, 1))) == \ sqrt(3)*JzKetCoupled(1, 1, (1, 1, 1), ((1, 2, 0), (1, 3, 1)) )/3 - \ JzKetCoupled(1, 1, (1, 1, 1), ((1, 2, 1), (1, 3, 1)) )/2 + \ sqrt(15)*JzKetCoupled(1, 1, (1, 1, 1), ((1, 2, 2), (1, 3, 1)) )/30 + \ JzKetCoupled(2, 1, (1, 1, 1), ((1, 2, 1), (1, 3, 2)) )/2 - \ sqrt(3)*JzKetCoupled(2, 1, (1, 1, 1), ((1, 2, 2), (1, 3, 2)) )/6 + \ sqrt(15)*JzKetCoupled(3, 1, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/15 assert couple(TensorProduct(JzKet(1, 1), JzKet(1, -1), JzKet(1, 0))) == \ -sqrt(6)*JzKetCoupled(0, 0, (1, 1, 1), ((1, 2, 1), (1, 3, 0)) )/6 + \ sqrt(3)*JzKetCoupled(1, 0, (1, 1, 1), ((1, 2, 0), (1, 3, 1)) )/3 - \ sqrt(15)*JzKetCoupled(1, 0, (1, 1, 1), ((1, 2, 2), (1, 3, 1)) )/15 + \ sqrt(3)*JzKetCoupled(2, 0, (1, 1, 1), ((1, 2, 1), (1, 3, 2)) )/3 + \ sqrt(10)*JzKetCoupled(3, 0, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/10 assert couple(TensorProduct(JzKet(1, 1), JzKet(1, -1), JzKet(1, -1))) == \ sqrt(3)*JzKetCoupled(1, -1, (1, 1, 1), ((1, 2, 0), (1, 3, 1)) )/3 + \ JzKetCoupled(1, -1, (1, 1, 1), ((1, 2, 1), (1, 3, 1)) )/2 + \ sqrt(15)*JzKetCoupled(1, -1, (1, 1, 1), ((1, 2, 2), (1, 3, 1)) )/30 + \ JzKetCoupled(2, -1, (1, 1, 1), ((1, 2, 1), (1, 3, 2)) )/2 + \ sqrt(3)*JzKetCoupled(2, -1, (1, 1, 1), ((1, 2, 2), (1, 3, 2)) )/6 + \ sqrt(15)*JzKetCoupled(3, -1, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/15 assert couple(TensorProduct(JzKet(1, 0), JzKet(1, 1), JzKet(1, 1))) == \ -sqrt(2)*JzKetCoupled(2, 2, (1, 1, 1), ((1, 2, 1), (1, 3, 2)) )/2 - \ sqrt(6)*JzKetCoupled(2, 2, (1, 1, 1), ((1, 2, 2), (1, 3, 2)) )/6 + \ sqrt(3)*JzKetCoupled(3, 2, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/3 assert couple(TensorProduct(JzKet(1, 0), JzKet(1, 1), JzKet(1, 0))) == \ -JzKetCoupled(1, 1, (1, 1, 1), ((1, 2, 1), (1, 3, 1)) )/2 - \ sqrt(15)*JzKetCoupled(1, 1, (1, 1, 1), ((1, 2, 2), (1, 3, 1)) )/10 - \ JzKetCoupled(2, 1, (1, 1, 1), ((1, 2, 1), (1, 3, 2)) )/2 + \ sqrt(3)*JzKetCoupled(2, 1, (1, 1, 1), ((1, 2, 2), (1, 3, 2)) )/6 + \ 2*sqrt(15)*JzKetCoupled(3, 1, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/15 assert couple(TensorProduct(JzKet(1, 0), JzKet(1, 1), JzKet(1, -1))) == \ -sqrt(6)*JzKetCoupled(0, 0, (1, 1, 1), ((1, 2, 1), (1, 3, 0)) )/6 - \ JzKetCoupled(1, 0, (1, 1, 1), ((1, 2, 1), (1, 3, 1)) )/2 + \ sqrt(15)*JzKetCoupled(1, 0, (1, 1, 1), ((1, 2, 2), (1, 3, 1)) )/10 - \ sqrt(3)*JzKetCoupled(2, 0, (1, 1, 1), ((1, 2, 1), (1, 3, 2)) )/6 + \ JzKetCoupled(2, 0, (1, 1, 1), ((1, 2, 2), (1, 3, 2)) )/2 + \ sqrt(10)*JzKetCoupled(3, 0, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/10 assert couple(TensorProduct(JzKet(1, 0), JzKet(1, 0), JzKet(1, 1))) == \ -sqrt(3)*JzKetCoupled(1, 1, (1, 1, 1), ((1, 2, 0), (1, 3, 1)) )/3 + \ sqrt(15)*JzKetCoupled(1, 1, (1, 1, 1), ((1, 2, 2), (1, 3, 1)) )/15 - \ sqrt(3)*JzKetCoupled(2, 1, (1, 1, 1), ((1, 2, 2), (1, 3, 2)) )/3 + \ 2*sqrt(15)*JzKetCoupled(3, 1, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/15 assert couple(TensorProduct(JzKet(1, 0), JzKet(1, 0), JzKet(1, 0))) == \ -sqrt(3)*JzKetCoupled(1, 0, (1, 1, 1), ((1, 2, 0), (1, 3, 1)) )/3 - \ 2*sqrt(15)*JzKetCoupled(1, 0, (1, 1, 1), ((1, 2, 2), (1, 3, 1)) )/15 + \ sqrt(10)*JzKetCoupled(3, 0, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/5 assert couple(TensorProduct(JzKet(1, 0), JzKet(1, 0), JzKet(1, -1))) == \ -sqrt(3)*JzKetCoupled(1, -1, (1, 1, 1), ((1, 2, 0), (1, 3, 1)) )/3 + \ sqrt(15)*JzKetCoupled(1, -1, (1, 1, 1), ((1, 2, 2), (1, 3, 1)) )/15 + \ sqrt(3)*JzKetCoupled(2, -1, (1, 1, 1), ((1, 2, 2), (1, 3, 2)) )/3 + \ 2*sqrt(15)*JzKetCoupled(3, -1, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/15 assert couple(TensorProduct(JzKet(1, 0), JzKet(1, -1), JzKet(1, 1))) == \ sqrt(6)*JzKetCoupled(0, 0, (1, 1, 1), ((1, 2, 1), (1, 3, 0)) )/6 - \ JzKetCoupled(1, 0, (1, 1, 1), ((1, 2, 1), (1, 3, 1)) )/2 + \ sqrt(15)*JzKetCoupled(1, 0, (1, 1, 1), ((1, 2, 2), (1, 3, 1)) )/10 + \ sqrt(3)*JzKetCoupled(2, 0, (1, 1, 1), ((1, 2, 1), (1, 3, 2)) )/6 - \ JzKetCoupled(2, 0, (1, 1, 1), ((1, 2, 2), (1, 3, 2)) )/2 + \ sqrt(10)*JzKetCoupled(3, 0, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/10 assert couple(TensorProduct(JzKet(1, 0), JzKet(1, -1), JzKet(1, 0))) == \ -JzKetCoupled(1, -1, (1, 1, 1), ((1, 2, 1), (1, 3, 1)) )/2 - \ sqrt(15)*JzKetCoupled(1, -1, (1, 1, 1), ((1, 2, 2), (1, 3, 1)) )/10 + \ JzKetCoupled(2, -1, (1, 1, 1), ((1, 2, 1), (1, 3, 2)) )/2 - \ sqrt(3)*JzKetCoupled(2, -1, (1, 1, 1), ((1, 2, 2), (1, 3, 2)) )/6 + \ 2*sqrt(15)*JzKetCoupled(3, -1, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/15 assert couple(TensorProduct(JzKet(1, 0), JzKet(1, -1), JzKet(1, -1))) == \ sqrt(2)*JzKetCoupled(2, -2, (1, 1, 1), ((1, 2, 1), (1, 3, 2)) )/2 + \ sqrt(6)*JzKetCoupled(2, -2, (1, 1, 1), ((1, 2, 2), (1, 3, 2)) )/6 + \ sqrt(3)*JzKetCoupled(3, -2, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/3 assert couple(TensorProduct(JzKet(1, -1), JzKet(1, 1), JzKet(1, 1))) == \ sqrt(3)*JzKetCoupled(1, 1, (1, 1, 1), ((1, 2, 0), (1, 3, 1)) )/3 + \ JzKetCoupled(1, 1, (1, 1, 1), ((1, 2, 1), (1, 3, 1)) )/2 + \ sqrt(15)*JzKetCoupled(1, 1, (1, 1, 1), ((1, 2, 2), (1, 3, 1)) )/30 - \ JzKetCoupled(2, 1, (1, 1, 1), ((1, 2, 1), (1, 3, 2)) )/2 - \ sqrt(3)*JzKetCoupled(2, 1, (1, 1, 1), ((1, 2, 2), (1, 3, 2)) )/6 + \ sqrt(15)*JzKetCoupled(3, 1, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/15 assert couple(TensorProduct(JzKet(1, -1), JzKet(1, 1), JzKet(1, 0))) == \ sqrt(6)*JzKetCoupled(0, 0, (1, 1, 1), ((1, 2, 1), (1, 3, 0)) )/6 + \ sqrt(3)*JzKetCoupled(1, 0, (1, 1, 1), ((1, 2, 0), (1, 3, 1)) )/3 - \ sqrt(15)*JzKetCoupled(1, 0, (1, 1, 1), ((1, 2, 2), (1, 3, 1)) )/15 - \ sqrt(3)*JzKetCoupled(2, 0, (1, 1, 1), ((1, 2, 1), (1, 3, 2)) )/3 + \ sqrt(10)*JzKetCoupled(3, 0, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/10 assert couple(TensorProduct(JzKet(1, -1), JzKet(1, 1), JzKet(1, -1))) == \ sqrt(3)*JzKetCoupled(1, -1, (1, 1, 1), ((1, 2, 0), (1, 3, 1)) )/3 - \ JzKetCoupled(1, -1, (1, 1, 1), ((1, 2, 1), (1, 3, 1)) )/2 + \ sqrt(15)*JzKetCoupled(1, -1, (1, 1, 1), ((1, 2, 2), (1, 3, 1)) )/30 - \ JzKetCoupled(2, -1, (1, 1, 1), ((1, 2, 1), (1, 3, 2)) )/2 + \ sqrt(3)*JzKetCoupled(2, -1, (1, 1, 1), ((1, 2, 2), (1, 3, 2)) )/6 + \ sqrt(15)*JzKetCoupled(3, -1, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/15 assert couple(TensorProduct(JzKet(1, -1), JzKet(1, 0), JzKet(1, 1))) == \ -sqrt(6)*JzKetCoupled(0, 0, (1, 1, 1), ((1, 2, 1), (1, 3, 0)) )/6 + \ JzKetCoupled(1, 0, (1, 1, 1), ((1, 2, 1), (1, 3, 1)) )/2 + \ sqrt(15)*JzKetCoupled(1, 0, (1, 1, 1), ((1, 2, 2), (1, 3, 1)) )/10 - \ sqrt(3)*JzKetCoupled(2, 0, (1, 1, 1), ((1, 2, 1), (1, 3, 2)) )/6 - \ JzKetCoupled(2, 0, (1, 1, 1), ((1, 2, 2), (1, 3, 2)) )/2 + \ sqrt(10)*JzKetCoupled(3, 0, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/10 assert couple(TensorProduct(JzKet(1, -1), JzKet(1, 0), JzKet(1, 0))) == \ JzKetCoupled(1, -1, (1, 1, 1), ((1, 2, 1), (1, 3, 1)) )/2 - \ sqrt(15)*JzKetCoupled(1, -1, (1, 1, 1), ((1, 2, 2), (1, 3, 1)) )/10 - \ JzKetCoupled(2, -1, (1, 1, 1), ((1, 2, 1), (1, 3, 2)) )/2 - \ sqrt(3)*JzKetCoupled(2, -1, (1, 1, 1), ((1, 2, 2), (1, 3, 2)) )/6 + \ 2*sqrt(15)*JzKetCoupled(3, -1, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/15 assert couple(TensorProduct(JzKet(1, -1), JzKet(1, 0), JzKet(1, -1))) == \ -sqrt(2)*JzKetCoupled(2, -2, (1, 1, 1), ((1, 2, 1), (1, 3, 2)) )/2 + \ sqrt(6)*JzKetCoupled(2, -2, (1, 1, 1), ((1, 2, 2), (1, 3, 2)) )/6 + \ sqrt(3)*JzKetCoupled(3, -2, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/3 assert couple(TensorProduct(JzKet(1, -1), JzKet(1, -1), JzKet(1, 1))) == \ sqrt(15)*JzKetCoupled(1, -1, (1, 1, 1), ((1, 2, 2), (1, 3, 1)) )/5 - \ sqrt(3)*JzKetCoupled(2, -1, (1, 1, 1), ((1, 2, 2), (1, 3, 2)) )/3 + \ sqrt(15)*JzKetCoupled(3, -1, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/15 assert couple(TensorProduct(JzKet(1, -1), JzKet(1, -1), JzKet(1, 0))) == \ -sqrt(6)*JzKetCoupled(2, -2, (1, 1, 1), ((1, 2, 2), (1, 3, 2)) )/3 + \ sqrt(3)*JzKetCoupled(3, -2, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) )/3 assert couple(TensorProduct(JzKet(1, -1), JzKet(1, -1), JzKet(1, -1))) == \ JzKetCoupled(3, -3, (1, 1, 1), ((1, 2, 2), (1, 3, 3)) ) # j1=S(1)/2, j2=S(1)/2, j3=S(3)/2 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(3)/2, S(3)/2))) == \ JzKetCoupled(S(5)/2, S( 5)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(5)/2)) ) assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(3)/2, S(1)/2))) == \ sqrt(10)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(3)/2)) )/5 + \ sqrt(15)*JzKetCoupled(S(5)/2, S(3)/2, (S(1)/2, S(1)/2, S(3) /2), ((1, 2, 1), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(3)/2, S(-1)/2))) == \ sqrt(6)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(1)/2)) )/6 + \ 2*sqrt(30)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(3)/2)) )/15 + \ sqrt(30)*JzKetCoupled(S(5)/2, S( 1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(3)/2, S(-3)/2))) == \ sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(1)/2)) )/2 + \ sqrt(10)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(3)/2)) )/5 + \ sqrt(10)*JzKetCoupled(S(5)/2, -S( 1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(3)/2, S(3)/2))) == \ sqrt(2)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 0), (1, 3, S(3)/2)) )/2 - \ sqrt(30)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(3)/2)) )/10 + \ sqrt(5)*JzKetCoupled(S(5)/2, S(3)/2, (S(1)/2, S(1)/2, S(3)/ 2), ((1, 2, 1), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(3)/2, S(1)/2))) == \ -sqrt(6)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(1)/2)) )/6 + \ sqrt(2)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 0), (1, 3, S(3)/2)) )/2 - \ sqrt(30)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(3)/2)) )/30 + \ sqrt(30)*JzKetCoupled(S(5)/2, S( 1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(3)/2, S(-1)/2))) == \ -sqrt(6)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(1)/2)) )/6 + \ sqrt(2)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 0), (1, 3, S(3)/2)) )/2 + \ sqrt(30)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(3)/2)) )/30 + \ sqrt(30)*JzKetCoupled(S(5)/2, -S( 1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(3)/2, S(-3)/2))) == \ sqrt(2)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 0), (1, 3, S(3)/2)) )/2 + \ sqrt(30)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(3)/2)) )/10 + \ sqrt(5)*JzKetCoupled(S(5)/2, -S(3)/2, (S(1)/2, S(1)/2, S(3) /2), ((1, 2, 1), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(3)/2, S(3)/2))) == \ -sqrt(2)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 0), (1, 3, S(3)/2)) )/2 - \ sqrt(30)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(3)/2)) )/10 + \ sqrt(5)*JzKetCoupled(S(5)/2, S(3)/2, (S(1)/2, S(1)/2, S(3)/ 2), ((1, 2, 1), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(3)/2, S(1)/2))) == \ -sqrt(6)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(1)/2)) )/6 - \ sqrt(2)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 0), (1, 3, S(3)/2)) )/2 - \ sqrt(30)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(3)/2)) )/30 + \ sqrt(30)*JzKetCoupled(S(5)/2, S( 1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(3)/2, S(-1)/2))) == \ -sqrt(6)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(1)/2)) )/6 - \ sqrt(2)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 0), (1, 3, S(3)/2)) )/2 + \ sqrt(30)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(3)/2)) )/30 + \ sqrt(30)*JzKetCoupled(S(5)/2, -S( 1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(3)/2, S(-3)/2))) == \ -sqrt(2)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 0), (1, 3, S(3)/2)) )/2 + \ sqrt(30)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(3)/2)) )/10 + \ sqrt(5)*JzKetCoupled(S(5)/2, -S(3)/2, (S(1)/2, S(1)/2, S(3) /2), ((1, 2, 1), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(3)/2, S(3)/2))) == \ sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(1)/2)) )/2 - \ sqrt(10)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(3)/2)) )/5 + \ sqrt(10)*JzKetCoupled(S(5)/2, S( 1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(3)/2, S(1)/2))) == \ sqrt(6)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(1)/2)) )/6 - \ 2*sqrt(30)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(3)/2)) )/15 + \ sqrt(30)*JzKetCoupled(S(5)/2, -S( 1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(3)/2, S(-1)/2))) == \ -sqrt(10)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(3)/2)) )/5 + \ sqrt(15)*JzKetCoupled(S(5)/2, -S(3)/2, (S(1)/2, S(1)/2, S( 3)/2), ((1, 2, 1), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(3)/2, S(-3)/2))) == \ JzKetCoupled(S(5)/2, -S( 5)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 2, 1), (1, 3, S(5)/2)) ) # Couple j1 to j3 # j1=1/2, j2=1/2, j3=1/2 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)), ((1, 3), (1, 2)) ) == \ JzKetCoupled(S(3)/2, S( 3)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 3, 1), (1, 2, S(3)/2)) ) assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (1, 2)) ) == \ sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 3, 0), (1, 2, S(1)/2)) )/2 - \ sqrt(6)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 3, 1), (1, 2, S(1)/2)) )/6 + \ sqrt(3)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/ 2), ((1, 3, 1), (1, 2, S(3)/2)) )/3 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)), ((1, 3), (1, 2)) ) == \ sqrt(6)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 3, 1), (1, 2, S(1)/2)) )/3 + \ sqrt(3)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/ 2), ((1, 3, 1), (1, 2, S(3)/2)) )/3 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (1, 2)) ) == \ sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 3, 0), (1, 2, S(1)/2)) )/2 + \ sqrt(6)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 3, 1), (1, 2, S(1)/2)) )/6 + \ sqrt(3)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1) /2), ((1, 3, 1), (1, 2, S(3)/2)) )/3 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)), ((1, 3), (1, 2)) ) == \ -sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 3, 0), (1, 2, S(1)/2)) )/2 - \ sqrt(6)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 3, 1), (1, 2, S(1)/2)) )/6 + \ sqrt(3)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/ 2), ((1, 3, 1), (1, 2, S(3)/2)) )/3 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (1, 2)) ) == \ -sqrt(6)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 3, 1), (1, 2, S(1)/2)) )/3 + \ sqrt(3)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1) /2), ((1, 3, 1), (1, 2, S(3)/2)) )/3 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)), ((1, 3), (1, 2)) ) == \ -sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 3, 0), (1, 2, S(1)/2)) )/2 + \ sqrt(6)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 3, 1), (1, 2, S(1)/2)) )/6 + \ sqrt(3)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1) /2), ((1, 3, 1), (1, 2, S(3)/2)) )/3 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)), ((1, 3), (1, 2)) ) == \ JzKetCoupled(S(3)/2, -S( 3)/2, (S(1)/2, S(1)/2, S(1)/2), ((1, 3, 1), (1, 2, S(3)/2)) ) # j1=1/2, j2=1/2, j3=1 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1)), ((1, 3), (1, 2)) ) == \ JzKetCoupled(2, 2, (S(1)/2, S(1)/2, 1), ((1, 3, S(3)/2), (1, 2, 2)) ) assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0)), ((1, 3), (1, 2)) ) == \ sqrt(3)*JzKetCoupled(1, 1, (S(1)/2, S(1)/2, 1), ((1, 3, S(1)/2), (1, 2, 1)) )/3 - \ sqrt(6)*JzKetCoupled(1, 1, (S(1)/2, S(1)/2, 1), ((1, 3, S(3)/2), (1, 2, 1)) )/6 + \ sqrt(2)*JzKetCoupled( 2, 1, (S(1)/2, S(1)/2, 1), ((1, 3, S(3)/2), (1, 2, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1)), ((1, 3), (1, 2)) ) == \ -sqrt(3)*JzKetCoupled(0, 0, (S(1)/2, S(1)/2, 1), ((1, 3, S(1)/2), (1, 2, 0)) )/3 + \ sqrt(3)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, 1), ((1, 3, S(1)/2), (1, 2, 1)) )/3 - \ sqrt(6)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, 1), ((1, 3, S(3)/2), (1, 2, 1)) )/6 + \ sqrt(6)*JzKetCoupled( 2, 0, (S(1)/2, S(1)/2, 1), ((1, 3, S(3)/2), (1, 2, 2)) )/6 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1)), ((1, 3), (1, 2)) ) == \ sqrt(3)*JzKetCoupled(1, 1, (S(1)/2, S(1)/2, 1), ((1, 3, S(3)/2), (1, 2, 1)) )/2 + \ JzKetCoupled(2, 1, (S(1)/2, S(1)/2, 1), ((1, 3, S(3)/2), (1, 2, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0)), ((1, 3), (1, 2)) ) == \ sqrt(6)*JzKetCoupled(0, 0, (S(1)/2, S(1)/2, 1), ((1, 3, S(1)/2), (1, 2, 0)) )/6 + \ sqrt(6)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, 1), ((1, 3, S(1)/2), (1, 2, 1)) )/6 + \ sqrt(3)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, 1), ((1, 3, S(3)/2), (1, 2, 1)) )/3 + \ sqrt(3)*JzKetCoupled( 2, 0, (S(1)/2, S(1)/2, 1), ((1, 3, S(3)/2), (1, 2, 2)) )/3 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, -1)), ((1, 3), (1, 2)) ) == \ sqrt(6)*JzKetCoupled(1, -1, (S(1)/2, S(1)/2, 1), ((1, 3, S(1)/2), (1, 2, 1)) )/3 + \ sqrt(3)*JzKetCoupled(1, -1, (S(1)/2, S(1)/2, 1), ((1, 3, S(3)/2), (1, 2, 1)) )/6 + \ JzKetCoupled( 2, -1, (S(1)/2, S(1)/2, 1), ((1, 3, S(3)/2), (1, 2, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1)), ((1, 3), (1, 2)) ) == \ -sqrt(6)*JzKetCoupled(1, 1, (S(1)/2, S(1)/2, 1), ((1, 3, S(1)/2), (1, 2, 1)) )/3 - \ sqrt(3)*JzKetCoupled(1, 1, (S(1)/2, S(1)/2, 1), ((1, 3, S(3)/2), (1, 2, 1)) )/6 + \ JzKetCoupled(2, 1, (S(1)/2, S(1)/2, 1), ((1, 3, S(3)/2), (1, 2, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0)), ((1, 3), (1, 2)) ) == \ sqrt(6)*JzKetCoupled(0, 0, (S(1)/2, S(1)/2, 1), ((1, 3, S(1)/2), (1, 2, 0)) )/6 - \ sqrt(6)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, 1), ((1, 3, S(1)/2), (1, 2, 1)) )/6 - \ sqrt(3)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, 1), ((1, 3, S(3)/2), (1, 2, 1)) )/3 + \ sqrt(3)*JzKetCoupled( 2, 0, (S(1)/2, S(1)/2, 1), ((1, 3, S(3)/2), (1, 2, 2)) )/3 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1)), ((1, 3), (1, 2)) ) == \ -sqrt(3)*JzKetCoupled(1, -1, (S(1)/2, S(1)/2, 1), ((1, 3, S(3)/2), (1, 2, 1)) )/2 + \ JzKetCoupled( 2, -1, (S(1)/2, S(1)/2, 1), ((1, 3, S(3)/2), (1, 2, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1)), ((1, 3), (1, 2)) ) == \ -sqrt(3)*JzKetCoupled(0, 0, (S(1)/2, S(1)/2, 1), ((1, 3, S(1)/2), (1, 2, 0)) )/3 - \ sqrt(3)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, 1), ((1, 3, S(1)/2), (1, 2, 1)) )/3 + \ sqrt(6)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, 1), ((1, 3, S(3)/2), (1, 2, 1)) )/6 + \ sqrt(6)*JzKetCoupled( 2, 0, (S(1)/2, S(1)/2, 1), ((1, 3, S(3)/2), (1, 2, 2)) )/6 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0)), ((1, 3), (1, 2)) ) == \ -sqrt(3)*JzKetCoupled(1, -1, (S(1)/2, S(1)/2, 1), ((1, 3, S(1)/2), (1, 2, 1)) )/3 + \ sqrt(6)*JzKetCoupled(1, -1, (S(1)/2, S(1)/2, 1), ((1, 3, S(3)/2), (1, 2, 1)) )/6 + \ sqrt(2)*JzKetCoupled( 2, -1, (S(1)/2, S(1)/2, 1), ((1, 3, S(3)/2), (1, 2, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, -1)), ((1, 3), (1, 2)) ) == \ JzKetCoupled(2, -2, (S(1)/2, S(1)/2, 1), ((1, 3, S(3)/2), (1, 2, 2)) ) # j 1=1/2, j 2=1, j 3=1 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 1)), ((1, 3), (1, 2)) ) == \ JzKetCoupled( S(5)/2, S(5)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(5)/2)) ) assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, 0)), ((1, 3), (1, 2)) ) == \ sqrt(3)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, 1, 1), ((1, 3, S(1)/2), (1, 2, S(3)/2)) )/3 - \ 2*sqrt(15)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(3)/2)) )/15 + \ sqrt(10)*JzKetCoupled(S( 5)/2, S(3)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 1), JzKet(1, -1)), ((1, 3), (1, 2)) ) == \ -2*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(1)/2), (1, 2, S(1)/2)) )/3 + \ sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(1)/2)) )/6 + \ sqrt(2)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(1)/2), (1, 2, S(3)/2)) )/3 - \ 2*sqrt(10)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(3)/2)) )/15 + \ sqrt(10)*JzKetCoupled(S(5)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 1)), ((1, 3), (1, 2)) ) == \ sqrt(15)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(3)/2)) )/5 + \ sqrt(10)*JzKetCoupled(S( 5)/2, S(3)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, 0)), ((1, 3), (1, 2)) ) == \ JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(1)/2), (1, 2, S(1)/2)) )/3 - \ sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(1)/2)) )/3 + \ sqrt(2)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(1)/2), (1, 2, S(3)/2)) )/3 + \ sqrt(10)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(3)/2)) )/15 + \ sqrt(10)*JzKetCoupled(S( 5)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, 0), JzKet(1, -1)), ((1, 3), (1, 2)) ) == \ -sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(1)/2), (1, 2, S(1)/2)) )/3 - \ JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(1)/2)) )/3 + \ 2*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(1)/2), (1, 2, S(3)/2)) )/3 - \ sqrt(5)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 1)), ((1, 3), (1, 2)) ) == \ sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(1)/2)) )/2 + \ sqrt(10)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(3)/2)) )/5 + \ sqrt(10)*JzKetCoupled(S(5)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, 0)), ((1, 3), (1, 2)) ) == \ sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(1)/2), (1, 2, S(1)/2)) )/3 + \ JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(1)/2)) )/3 + \ JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(1)/2), (1, 2, S(3)/2)) )/3 + \ 4*sqrt(5)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(1, -1), JzKet(1, -1)), ((1, 3), (1, 2)) ) == \ sqrt(6)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, 1, 1), ((1, 3, S(1)/2), (1, 2, S(3)/2)) )/3 + \ sqrt(30)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, -S(3)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(1, 1)), ((1, 3), (1, 2)) ) == \ -sqrt(6)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, 1, 1), ((1, 3, S(1)/2), (1, 2, S(3)/2)) )/3 - \ sqrt(30)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S( 5)/2, S(3)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(1, 0)), ((1, 3), (1, 2)) ) == \ sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(1)/2), (1, 2, S(1)/2)) )/3 + \ JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(1)/2)) )/3 - \ JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(1)/2), (1, 2, S(3)/2)) )/3 - \ 4*sqrt(5)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S( 5)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, 1), JzKet(1, -1)), ((1, 3), (1, 2)) ) == \ sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(1)/2)) )/2 - \ sqrt(10)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(3)/2)) )/5 + \ sqrt(10)*JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(1, 1)), ((1, 3), (1, 2)) ) == \ -sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(1)/2), (1, 2, S(1)/2)) )/3 - \ JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(1)/2)) )/3 - \ 2*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(1)/2), (1, 2, S(3)/2)) )/3 + \ sqrt(5)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S( 5)/2, S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(1, 0)), ((1, 3), (1, 2)) ) == \ JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(1)/2), (1, 2, S(1)/2)) )/3 - \ sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(1)/2)) )/3 - \ sqrt(2)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(1)/2), (1, 2, S(3)/2)) )/3 - \ sqrt(10)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(3)/2)) )/15 + \ sqrt(10)*JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, 0), JzKet(1, -1)), ((1, 3), (1, 2)) ) == \ -sqrt(15)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(3)/2)) )/5 + \ sqrt(10)*JzKetCoupled(S(5)/2, -S(3)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(1, 1)), ((1, 3), (1, 2)) ) == \ -2*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(1)/2), (1, 2, S(1)/2)) )/3 + \ sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(1)/2)) )/6 - \ sqrt(2)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(1)/2), (1, 2, S(3)/2)) )/3 + \ 2*sqrt(10)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(3)/2)) )/15 + \ sqrt(10)*JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(1, 0)), ((1, 3), (1, 2)) ) == \ -sqrt(3)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, 1, 1), ((1, 3, S(1)/2), (1, 2, S(3)/2)) )/3 + \ 2*sqrt(15)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(3)/2)) )/15 + \ sqrt(10)*JzKetCoupled(S(5)/2, -S(3)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(1, -1), JzKet(1, -1)), ((1, 3), (1, 2)) ) == \ JzKetCoupled(S( 5)/2, -S(5)/2, (S(1)/2, 1, 1), ((1, 3, S(3)/2), (1, 2, S(5)/2)) ) # j1=1, 1, 1 assert couple(TensorProduct(JzKet(1, 1), JzKet(1, 1), JzKet(1, 1)), ((1, 3), (1, 2)) ) == \ JzKetCoupled(3, 3, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) ) assert couple(TensorProduct(JzKet(1, 1), JzKet(1, 1), JzKet(1, 0)), ((1, 3), (1, 2)) ) == \ sqrt(2)*JzKetCoupled(2, 2, (1, 1, 1), ((1, 3, 1), (1, 2, 2)) )/2 - \ sqrt(6)*JzKetCoupled(2, 2, (1, 1, 1), ((1, 3, 2), (1, 2, 2)) )/6 + \ sqrt(3)*JzKetCoupled(3, 2, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/3 assert couple(TensorProduct(JzKet(1, 1), JzKet(1, 1), JzKet(1, -1)), ((1, 3), (1, 2)) ) == \ sqrt(3)*JzKetCoupled(1, 1, (1, 1, 1), ((1, 3, 0), (1, 2, 1)) )/3 - \ JzKetCoupled(1, 1, (1, 1, 1), ((1, 3, 1), (1, 2, 1)) )/2 + \ sqrt(15)*JzKetCoupled(1, 1, (1, 1, 1), ((1, 3, 2), (1, 2, 1)) )/30 + \ JzKetCoupled(2, 1, (1, 1, 1), ((1, 3, 1), (1, 2, 2)) )/2 - \ sqrt(3)*JzKetCoupled(2, 1, (1, 1, 1), ((1, 3, 2), (1, 2, 2)) )/6 + \ sqrt(15)*JzKetCoupled(3, 1, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/15 assert couple(TensorProduct(JzKet(1, 1), JzKet(1, 0), JzKet(1, 1)), ((1, 3), (1, 2)) ) == \ sqrt(6)*JzKetCoupled(2, 2, (1, 1, 1), ((1, 3, 2), (1, 2, 2)) )/3 + \ sqrt(3)*JzKetCoupled(3, 2, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/3 assert couple(TensorProduct(JzKet(1, 1), JzKet(1, 0), JzKet(1, 0)), ((1, 3), (1, 2)) ) == \ JzKetCoupled(1, 1, (1, 1, 1), ((1, 3, 1), (1, 2, 1)) )/2 - \ sqrt(15)*JzKetCoupled(1, 1, (1, 1, 1), ((1, 3, 2), (1, 2, 1)) )/10 + \ JzKetCoupled(2, 1, (1, 1, 1), ((1, 3, 1), (1, 2, 2)) )/2 + \ sqrt(3)*JzKetCoupled(2, 1, (1, 1, 1), ((1, 3, 2), (1, 2, 2)) )/6 + \ 2*sqrt(15)*JzKetCoupled(3, 1, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/15 assert couple(TensorProduct(JzKet(1, 1), JzKet(1, 0), JzKet(1, -1)), ((1, 3), (1, 2)) ) == \ -sqrt(6)*JzKetCoupled(0, 0, (1, 1, 1), ((1, 3, 1), (1, 2, 0)) )/6 + \ sqrt(3)*JzKetCoupled(1, 0, (1, 1, 1), ((1, 3, 0), (1, 2, 1)) )/3 - \ sqrt(15)*JzKetCoupled(1, 0, (1, 1, 1), ((1, 3, 2), (1, 2, 1)) )/15 + \ sqrt(3)*JzKetCoupled(2, 0, (1, 1, 1), ((1, 3, 1), (1, 2, 2)) )/3 + \ sqrt(10)*JzKetCoupled(3, 0, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/10 assert couple(TensorProduct(JzKet(1, 1), JzKet(1, -1), JzKet(1, 1)), ((1, 3), (1, 2)) ) == \ sqrt(15)*JzKetCoupled(1, 1, (1, 1, 1), ((1, 3, 2), (1, 2, 1)) )/5 + \ sqrt(3)*JzKetCoupled(2, 1, (1, 1, 1), ((1, 3, 2), (1, 2, 2)) )/3 + \ sqrt(15)*JzKetCoupled(3, 1, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/15 assert couple(TensorProduct(JzKet(1, 1), JzKet(1, -1), JzKet(1, 0)), ((1, 3), (1, 2)) ) == \ sqrt(6)*JzKetCoupled(0, 0, (1, 1, 1), ((1, 3, 1), (1, 2, 0)) )/6 + \ JzKetCoupled(1, 0, (1, 1, 1), ((1, 3, 1), (1, 2, 1)) )/2 + \ sqrt(15)*JzKetCoupled(1, 0, (1, 1, 1), ((1, 3, 2), (1, 2, 1)) )/10 + \ sqrt(3)*JzKetCoupled(2, 0, (1, 1, 1), ((1, 3, 1), (1, 2, 2)) )/6 + \ JzKetCoupled(2, 0, (1, 1, 1), ((1, 3, 2), (1, 2, 2)) )/2 + \ sqrt(10)*JzKetCoupled(3, 0, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/10 assert couple(TensorProduct(JzKet(1, 1), JzKet(1, -1), JzKet(1, -1)), ((1, 3), (1, 2)) ) == \ sqrt(3)*JzKetCoupled(1, -1, (1, 1, 1), ((1, 3, 0), (1, 2, 1)) )/3 + \ JzKetCoupled(1, -1, (1, 1, 1), ((1, 3, 1), (1, 2, 1)) )/2 + \ sqrt(15)*JzKetCoupled(1, -1, (1, 1, 1), ((1, 3, 2), (1, 2, 1)) )/30 + \ JzKetCoupled(2, -1, (1, 1, 1), ((1, 3, 1), (1, 2, 2)) )/2 + \ sqrt(3)*JzKetCoupled(2, -1, (1, 1, 1), ((1, 3, 2), (1, 2, 2)) )/6 + \ sqrt(15)*JzKetCoupled(3, -1, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/15 assert couple(TensorProduct(JzKet(1, 0), JzKet(1, 1), JzKet(1, 1)), ((1, 3), (1, 2)) ) == \ -sqrt(2)*JzKetCoupled(2, 2, (1, 1, 1), ((1, 3, 1), (1, 2, 2)) )/2 - \ sqrt(6)*JzKetCoupled(2, 2, (1, 1, 1), ((1, 3, 2), (1, 2, 2)) )/6 + \ sqrt(3)*JzKetCoupled(3, 2, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/3 assert couple(TensorProduct(JzKet(1, 0), JzKet(1, 1), JzKet(1, 0)), ((1, 3), (1, 2)) ) == \ -sqrt(3)*JzKetCoupled(1, 1, (1, 1, 1), ((1, 3, 0), (1, 2, 1)) )/3 + \ sqrt(15)*JzKetCoupled(1, 1, (1, 1, 1), ((1, 3, 2), (1, 2, 1)) )/15 - \ sqrt(3)*JzKetCoupled(2, 1, (1, 1, 1), ((1, 3, 2), (1, 2, 2)) )/3 + \ 2*sqrt(15)*JzKetCoupled(3, 1, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/15 assert couple(TensorProduct(JzKet(1, 0), JzKet(1, 1), JzKet(1, -1)), ((1, 3), (1, 2)) ) == \ sqrt(6)*JzKetCoupled(0, 0, (1, 1, 1), ((1, 3, 1), (1, 2, 0)) )/6 - \ JzKetCoupled(1, 0, (1, 1, 1), ((1, 3, 1), (1, 2, 1)) )/2 + \ sqrt(15)*JzKetCoupled(1, 0, (1, 1, 1), ((1, 3, 2), (1, 2, 1)) )/10 + \ sqrt(3)*JzKetCoupled(2, 0, (1, 1, 1), ((1, 3, 1), (1, 2, 2)) )/6 - \ JzKetCoupled(2, 0, (1, 1, 1), ((1, 3, 2), (1, 2, 2)) )/2 + \ sqrt(10)*JzKetCoupled(3, 0, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/10 assert couple(TensorProduct(JzKet(1, 0), JzKet(1, 0), JzKet(1, 1)), ((1, 3), (1, 2)) ) == \ -JzKetCoupled(1, 1, (1, 1, 1), ((1, 3, 1), (1, 2, 1)) )/2 - \ sqrt(15)*JzKetCoupled(1, 1, (1, 1, 1), ((1, 3, 2), (1, 2, 1)) )/10 - \ JzKetCoupled(2, 1, (1, 1, 1), ((1, 3, 1), (1, 2, 2)) )/2 + \ sqrt(3)*JzKetCoupled(2, 1, (1, 1, 1), ((1, 3, 2), (1, 2, 2)) )/6 + \ 2*sqrt(15)*JzKetCoupled(3, 1, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/15 assert couple(TensorProduct(JzKet(1, 0), JzKet(1, 0), JzKet(1, 0)), ((1, 3), (1, 2)) ) == \ -sqrt(3)*JzKetCoupled(1, 0, (1, 1, 1), ((1, 3, 0), (1, 2, 1)) )/3 - \ 2*sqrt(15)*JzKetCoupled(1, 0, (1, 1, 1), ((1, 3, 2), (1, 2, 1)) )/15 + \ sqrt(10)*JzKetCoupled(3, 0, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/5 assert couple(TensorProduct(JzKet(1, 0), JzKet(1, 0), JzKet(1, -1)), ((1, 3), (1, 2)) ) == \ -JzKetCoupled(1, -1, (1, 1, 1), ((1, 3, 1), (1, 2, 1)) )/2 - \ sqrt(15)*JzKetCoupled(1, -1, (1, 1, 1), ((1, 3, 2), (1, 2, 1)) )/10 + \ JzKetCoupled(2, -1, (1, 1, 1), ((1, 3, 1), (1, 2, 2)) )/2 - \ sqrt(3)*JzKetCoupled(2, -1, (1, 1, 1), ((1, 3, 2), (1, 2, 2)) )/6 + \ 2*sqrt(15)*JzKetCoupled(3, -1, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/15 assert couple(TensorProduct(JzKet(1, 0), JzKet(1, -1), JzKet(1, 1)), ((1, 3), (1, 2)) ) == \ -sqrt(6)*JzKetCoupled(0, 0, (1, 1, 1), ((1, 3, 1), (1, 2, 0)) )/6 - \ JzKetCoupled(1, 0, (1, 1, 1), ((1, 3, 1), (1, 2, 1)) )/2 + \ sqrt(15)*JzKetCoupled(1, 0, (1, 1, 1), ((1, 3, 2), (1, 2, 1)) )/10 - \ sqrt(3)*JzKetCoupled(2, 0, (1, 1, 1), ((1, 3, 1), (1, 2, 2)) )/6 + \ JzKetCoupled(2, 0, (1, 1, 1), ((1, 3, 2), (1, 2, 2)) )/2 + \ sqrt(10)*JzKetCoupled(3, 0, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/10 assert couple(TensorProduct(JzKet(1, 0), JzKet(1, -1), JzKet(1, 0)), ((1, 3), (1, 2)) ) == \ -sqrt(3)*JzKetCoupled(1, -1, (1, 1, 1), ((1, 3, 0), (1, 2, 1)) )/3 + \ sqrt(15)*JzKetCoupled(1, -1, (1, 1, 1), ((1, 3, 2), (1, 2, 1)) )/15 + \ sqrt(3)*JzKetCoupled(2, -1, (1, 1, 1), ((1, 3, 2), (1, 2, 2)) )/3 + \ 2*sqrt(15)*JzKetCoupled(3, -1, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/15 assert couple(TensorProduct(JzKet(1, 0), JzKet(1, -1), JzKet(1, -1)), ((1, 3), (1, 2)) ) == \ sqrt(2)*JzKetCoupled(2, -2, (1, 1, 1), ((1, 3, 1), (1, 2, 2)) )/2 + \ sqrt(6)*JzKetCoupled(2, -2, (1, 1, 1), ((1, 3, 2), (1, 2, 2)) )/6 + \ sqrt(3)*JzKetCoupled(3, -2, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/3 assert couple(TensorProduct(JzKet(1, -1), JzKet(1, 1), JzKet(1, 1)), ((1, 3), (1, 2)) ) == \ sqrt(3)*JzKetCoupled(1, 1, (1, 1, 1), ((1, 3, 0), (1, 2, 1)) )/3 + \ JzKetCoupled(1, 1, (1, 1, 1), ((1, 3, 1), (1, 2, 1)) )/2 + \ sqrt(15)*JzKetCoupled(1, 1, (1, 1, 1), ((1, 3, 2), (1, 2, 1)) )/30 - \ JzKetCoupled(2, 1, (1, 1, 1), ((1, 3, 1), (1, 2, 2)) )/2 - \ sqrt(3)*JzKetCoupled(2, 1, (1, 1, 1), ((1, 3, 2), (1, 2, 2)) )/6 + \ sqrt(15)*JzKetCoupled(3, 1, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/15 assert couple(TensorProduct(JzKet(1, -1), JzKet(1, 1), JzKet(1, 0)), ((1, 3), (1, 2)) ) == \ -sqrt(6)*JzKetCoupled(0, 0, (1, 1, 1), ((1, 3, 1), (1, 2, 0)) )/6 + \ JzKetCoupled(1, 0, (1, 1, 1), ((1, 3, 1), (1, 2, 1)) )/2 + \ sqrt(15)*JzKetCoupled(1, 0, (1, 1, 1), ((1, 3, 2), (1, 2, 1)) )/10 - \ sqrt(3)*JzKetCoupled(2, 0, (1, 1, 1), ((1, 3, 1), (1, 2, 2)) )/6 - \ JzKetCoupled(2, 0, (1, 1, 1), ((1, 3, 2), (1, 2, 2)) )/2 + \ sqrt(10)*JzKetCoupled(3, 0, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/10 assert couple(TensorProduct(JzKet(1, -1), JzKet(1, 1), JzKet(1, -1)), ((1, 3), (1, 2)) ) == \ sqrt(15)*JzKetCoupled(1, -1, (1, 1, 1), ((1, 3, 2), (1, 2, 1)) )/5 - \ sqrt(3)*JzKetCoupled(2, -1, (1, 1, 1), ((1, 3, 2), (1, 2, 2)) )/3 + \ sqrt(15)*JzKetCoupled(3, -1, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/15 assert couple(TensorProduct(JzKet(1, -1), JzKet(1, 0), JzKet(1, 1)), ((1, 3), (1, 2)) ) == \ sqrt(6)*JzKetCoupled(0, 0, (1, 1, 1), ((1, 3, 1), (1, 2, 0)) )/6 + \ sqrt(3)*JzKetCoupled(1, 0, (1, 1, 1), ((1, 3, 0), (1, 2, 1)) )/3 - \ sqrt(15)*JzKetCoupled(1, 0, (1, 1, 1), ((1, 3, 2), (1, 2, 1)) )/15 - \ sqrt(3)*JzKetCoupled(2, 0, (1, 1, 1), ((1, 3, 1), (1, 2, 2)) )/3 + \ sqrt(10)*JzKetCoupled(3, 0, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/10 assert couple(TensorProduct(JzKet(1, -1), JzKet(1, 0), JzKet(1, 0)), ((1, 3), (1, 2)) ) == \ JzKetCoupled(1, -1, (1, 1, 1), ((1, 3, 1), (1, 2, 1)) )/2 - \ sqrt(15)*JzKetCoupled(1, -1, (1, 1, 1), ((1, 3, 2), (1, 2, 1)) )/10 - \ JzKetCoupled(2, -1, (1, 1, 1), ((1, 3, 1), (1, 2, 2)) )/2 - \ sqrt(3)*JzKetCoupled(2, -1, (1, 1, 1), ((1, 3, 2), (1, 2, 2)) )/6 + \ 2*sqrt(15)*JzKetCoupled(3, -1, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/15 assert couple(TensorProduct(JzKet(1, -1), JzKet(1, 0), JzKet(1, -1)), ((1, 3), (1, 2)) ) == \ -sqrt(6)*JzKetCoupled(2, -2, (1, 1, 1), ((1, 3, 2), (1, 2, 2)) )/3 + \ sqrt(3)*JzKetCoupled(3, -2, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/3 assert couple(TensorProduct(JzKet(1, -1), JzKet(1, -1), JzKet(1, 1)), ((1, 3), (1, 2)) ) == \ sqrt(3)*JzKetCoupled(1, -1, (1, 1, 1), ((1, 3, 0), (1, 2, 1)) )/3 - \ JzKetCoupled(1, -1, (1, 1, 1), ((1, 3, 1), (1, 2, 1)) )/2 + \ sqrt(15)*JzKetCoupled(1, -1, (1, 1, 1), ((1, 3, 2), (1, 2, 1)) )/30 - \ JzKetCoupled(2, -1, (1, 1, 1), ((1, 3, 1), (1, 2, 2)) )/2 + \ sqrt(3)*JzKetCoupled(2, -1, (1, 1, 1), ((1, 3, 2), (1, 2, 2)) )/6 + \ sqrt(15)*JzKetCoupled(3, -1, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/15 assert couple(TensorProduct(JzKet(1, -1), JzKet(1, -1), JzKet(1, 0)), ((1, 3), (1, 2)) ) == \ -sqrt(2)*JzKetCoupled(2, -2, (1, 1, 1), ((1, 3, 1), (1, 2, 2)) )/2 + \ sqrt(6)*JzKetCoupled(2, -2, (1, 1, 1), ((1, 3, 2), (1, 2, 2)) )/6 + \ sqrt(3)*JzKetCoupled(3, -2, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) )/3 assert couple(TensorProduct(JzKet(1, -1), JzKet(1, -1), JzKet(1, -1)), ((1, 3), (1, 2)) ) == \ JzKetCoupled(3, -3, (1, 1, 1), ((1, 3, 2), (1, 2, 3)) ) # j1=1/2, j2=1/2, j3=3/2 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(3)/2, S(3)/2)), ((1, 3), (1, 2)) ) == \ JzKetCoupled(S(5)/2, S( 5)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 2), (1, 2, S(5)/2)) ) assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(3)/2, S(1)/2)), ((1, 3), (1, 2)) ) == \ JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 1), (1, 2, S(3)/2)) )/2 - \ sqrt(15)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 2), (1, 2, S(3)/2)) )/10 + \ sqrt(15)*JzKetCoupled(S(5)/2, S(3)/2, (S(1)/2, S(1)/2, S(3) /2), ((1, 3, 2), (1, 2, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(3)/2, S(-1)/2)), ((1, 3), (1, 2)) ) == \ -sqrt(6)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 1), (1, 2, S(1)/2)) )/6 + \ sqrt(3)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 1), (1, 2, S(3)/2)) )/3 - \ sqrt(5)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 2), (1, 2, S(3)/2)) )/5 + \ sqrt(30)*JzKetCoupled(S(5)/2, S( 1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 2), (1, 2, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(3)/2, S(-3)/2)), ((1, 3), (1, 2)) ) == \ -sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 1), (1, 2, S(1)/2)) )/2 + \ JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 1), (1, 2, S(3)/2)) )/2 - \ sqrt(15)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 2), (1, 2, S(3)/2)) )/10 + \ sqrt(10)*JzKetCoupled(S(5)/2, -S( 1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 2), (1, 2, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(3)/2, S(3)/2)), ((1, 3), (1, 2)) ) == \ 2*sqrt(5)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 2), (1, 2, S(3)/2)) )/5 + \ sqrt(5)*JzKetCoupled(S(5)/2, S(3)/2, (S(1)/2, S(1)/2, S(3)/ 2), ((1, 3, 2), (1, 2, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(3)/2, S(1)/2)), ((1, 3), (1, 2)) ) == \ sqrt(6)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 1), (1, 2, S(1)/2)) )/6 + \ sqrt(3)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 1), (1, 2, S(3)/2)) )/6 + \ 3*sqrt(5)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 2), (1, 2, S(3)/2)) )/10 + \ sqrt(30)*JzKetCoupled(S(5)/2, S( 1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 2), (1, 2, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(3)/2, S(-1)/2)), ((1, 3), (1, 2)) ) == \ sqrt(6)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 1), (1, 2, S(1)/2)) )/6 + \ sqrt(3)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 1), (1, 2, S(3)/2)) )/3 + \ sqrt(5)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 2), (1, 2, S(3)/2)) )/5 + \ sqrt(30)*JzKetCoupled(S(5)/2, -S( 1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 2), (1, 2, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(3)/2, S(-3)/2)), ((1, 3), (1, 2)) ) == \ sqrt(3)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 1), (1, 2, S(3)/2)) )/2 + \ sqrt(5)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 2), (1, 2, S(3)/2)) )/10 + \ sqrt(5)*JzKetCoupled(S(5)/2, -S(3)/2, (S(1)/2, S(1)/2, S(3) /2), ((1, 3, 2), (1, 2, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(3)/2, S(3)/2)), ((1, 3), (1, 2)) ) == \ -sqrt(3)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 1), (1, 2, S(3)/2)) )/2 - \ sqrt(5)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 2), (1, 2, S(3)/2)) )/10 + \ sqrt(5)*JzKetCoupled(S(5)/2, S(3)/2, (S(1)/2, S(1)/2, S(3)/ 2), ((1, 3, 2), (1, 2, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(3)/2, S(1)/2)), ((1, 3), (1, 2)) ) == \ sqrt(6)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 1), (1, 2, S(1)/2)) )/6 - \ sqrt(3)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 1), (1, 2, S(3)/2)) )/3 - \ sqrt(5)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 2), (1, 2, S(3)/2)) )/5 + \ sqrt(30)*JzKetCoupled(S(5)/2, S( 1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 2), (1, 2, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(3)/2, S(-1)/2)), ((1, 3), (1, 2)) ) == \ sqrt(6)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 1), (1, 2, S(1)/2)) )/6 - \ sqrt(3)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 1), (1, 2, S(3)/2)) )/6 - \ 3*sqrt(5)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 2), (1, 2, S(3)/2)) )/10 + \ sqrt(30)*JzKetCoupled(S(5)/2, -S( 1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 2), (1, 2, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(3)/2, S(-3)/2)), ((1, 3), (1, 2)) ) == \ -2*sqrt(5)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 2), (1, 2, S(3)/2)) )/5 + \ sqrt(5)*JzKetCoupled(S(5)/2, -S(3)/2, (S(1)/2, S(1)/2, S(3) /2), ((1, 3, 2), (1, 2, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(3)/2, S(3)/2)), ((1, 3), (1, 2)) ) == \ -sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 1), (1, 2, S(1)/2)) )/2 - \ JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 1), (1, 2, S(3)/2)) )/2 + \ sqrt(15)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 2), (1, 2, S(3)/2)) )/10 + \ sqrt(10)*JzKetCoupled(S(5)/2, S( 1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 2), (1, 2, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(3)/2, S(1)/2)), ((1, 3), (1, 2)) ) == \ -sqrt(6)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 1), (1, 2, S(1)/2)) )/6 - \ sqrt(3)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 1), (1, 2, S(3)/2)) )/3 + \ sqrt(5)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 2), (1, 2, S(3)/2)) )/5 + \ sqrt(30)*JzKetCoupled(S(5)/2, -S( 1)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 2), (1, 2, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(3)/2, S(-1)/2)), ((1, 3), (1, 2)) ) == \ -JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 1), (1, 2, S(3)/2)) )/2 + \ sqrt(15)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 2), (1, 2, S(3)/2)) )/10 + \ sqrt(15)*JzKetCoupled(S(5)/2, -S(3)/2, (S(1)/2, S(1)/2, S( 3)/2), ((1, 3, 2), (1, 2, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(3)/2, S(-3)/2)), ((1, 3), (1, 2)) ) == \ JzKetCoupled(S(5)/2, -S( 5)/2, (S(1)/2, S(1)/2, S(3)/2), ((1, 3, 2), (1, 2, S(5)/2)) ) def test_couple_4_states_numerical(): # Default coupling # j1=1/2, j2=1/2, j3=1/2, j4=1/2 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2))) == \ JzKetCoupled(2, 2, (S(1)/2, S( 1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 2)) ) assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2))) == \ sqrt(3)*JzKetCoupled(1, 1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 1)) )/2 + \ JzKetCoupled(2, 1, (S(1)/2, S( 1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2))) == \ sqrt(6)*JzKetCoupled(1, 1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, 1)) )/3 - \ sqrt(3)*JzKetCoupled(1, 1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 1)) )/6 + \ JzKetCoupled(2, 1, (S(1)/2, S( 1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2))) == \ sqrt(3)*JzKetCoupled(0, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, 0)) )/3 + \ sqrt(3)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, 1)) )/3 + \ sqrt(6)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 1)) )/6 + \ sqrt(6)*JzKetCoupled(2, 0, (S(1)/2, S( 1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 2)) )/6 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2))) == \ sqrt(2)*JzKetCoupled(1, 1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, 1)) )/2 - \ sqrt(6)*JzKetCoupled(1, 1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, 1)) )/6 - \ sqrt(3)*JzKetCoupled(1, 1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 1)) )/6 + \ JzKetCoupled(2, 1, (S(1)/2, S( 1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2))) == \ JzKetCoupled(0, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, 0)))/2 - \ sqrt(3)*JzKetCoupled(0, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, 0)))/6 + \ JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, 1)))/2 - \ sqrt(3)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, 1)))/6 + \ sqrt(6)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 1)))/6 + \ sqrt(6)*JzKetCoupled(2, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 2)))/6 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2))) == \ -JzKetCoupled(0, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, 0)) )/2 - \ sqrt(3)*JzKetCoupled(0, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, 0)) )/6 + \ JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, 1)) )/2 + \ sqrt(3)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, 1)) )/6 - \ sqrt(6)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 1)) )/6 + \ sqrt(6)*JzKetCoupled(2, 0, (S(1)/2, S( 1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 2)) )/6 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2))) == \ sqrt(2)*JzKetCoupled(1, -1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, 1)) )/2 + \ sqrt(6)*JzKetCoupled(1, -1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, 1)) )/6 + \ sqrt(3)*JzKetCoupled(1, -1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 1)) )/6 + \ JzKetCoupled(2, -1, (S(1)/2, S( 1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2))) == \ -sqrt(2)*JzKetCoupled(1, 1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, 1)) )/2 - \ sqrt(6)*JzKetCoupled(1, 1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, 1)) )/6 - \ sqrt(3)*JzKetCoupled(1, 1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 1)) )/6 + \ JzKetCoupled(2, 1, (S(1)/2, S( 1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2))) == \ -JzKetCoupled(0, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, 0)) )/2 - \ sqrt(3)*JzKetCoupled(0, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, 0)) )/6 - \ JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, 1)) )/2 - \ sqrt(3)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, 1)) )/6 + \ sqrt(6)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 1)) )/6 + \ sqrt(6)*JzKetCoupled(2, 0, (S(1)/2, S( 1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 2)) )/6 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2))) == \ JzKetCoupled(0, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, 0)) )/2 - \ sqrt(3)*JzKetCoupled(0, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, 0)) )/6 - \ JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, 1)) )/2 + \ sqrt(3)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, 1)) )/6 - \ sqrt(6)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 1)) )/6 + \ sqrt(6)*JzKetCoupled(2, 0, (S(1)/2, S( 1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 2)) )/6 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2))) == \ -sqrt(2)*JzKetCoupled(1, -1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, 1)) )/2 + \ sqrt(6)*JzKetCoupled(1, -1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, 1)) )/6 + \ sqrt(3)*JzKetCoupled(1, -1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 1)) )/6 + \ JzKetCoupled(2, -1, (S(1)/2, S( 1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2))) == \ sqrt(3)*JzKetCoupled(0, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, 0)) )/3 - \ sqrt(3)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, 1)) )/3 - \ sqrt(6)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 1)) )/6 + \ sqrt(6)*JzKetCoupled(2, 0, (S(1)/2, S( 1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 2)) )/6 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2))) == \ -sqrt(6)*JzKetCoupled(1, -1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, 1)) )/3 + \ sqrt(3)*JzKetCoupled(1, -1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 1)) )/6 + \ JzKetCoupled(2, -1, (S(1)/2, S( 1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2))) == \ -sqrt(3)*JzKetCoupled(1, -1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 1)) )/2 + \ JzKetCoupled(2, -1, (S(1)/2, S( 1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2))) == \ JzKetCoupled(2, -2, (S(1)/2, S( 1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, 2)) ) # j1=S(1)/2, S(1)/2, S(1)/2, 1 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1))) == \ JzKetCoupled(S(5)/2, S(5)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(5)/2)) ) assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0))) == \ sqrt(15)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(3)/2)) )/5 + \ sqrt(10)*JzKetCoupled(S(5)/2, S(3)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1))) == \ sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(1)/2)) )/2 + \ sqrt(10)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(3)/2)) )/5 + \ sqrt(10)*JzKetCoupled(S(5)/2, S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1))) == \ sqrt(6)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/3 - \ sqrt(30)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, S(3)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0))) == \ sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(1)/2)) )/3 - \ JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(1)/2)) )/3 + \ 2*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/3 + \ sqrt(5)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, -1))) == \ 2*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(1)/2)) )/3 + \ sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(1)/2)) )/6 + \ sqrt(2)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/3 + \ 2*sqrt(10)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(3)/2)) )/15 + \ sqrt(10)*JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1))) == \ sqrt(2)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/2 - \ sqrt(6)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/6 - \ sqrt(30)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, S(3)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0))) == \ sqrt(6)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, S(1)/2)) )/6 - \ sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(1)/2)) )/6 - \ JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(1)/2)) )/3 + \ sqrt(3)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/3 - \ JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/3 + \ sqrt(5)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1))) == \ sqrt(3)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, S(1)/2)) )/3 - \ JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(1)/2)) )/3 + \ sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(1)/2)) )/6 + \ sqrt(6)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/6 - \ sqrt(2)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/6 + \ 2*sqrt(10)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(3)/2)) )/15 + \ sqrt(10)*JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1))) == \ -sqrt(3)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, S(1)/2)) )/3 - \ JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(1)/2)) )/3 + \ sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(1)/2)) )/6 + \ sqrt(6)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/6 + \ sqrt(2)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/6 - \ 2*sqrt(10)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(3)/2)) )/15 + \ sqrt(10)*JzKetCoupled(S(5)/2, S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0))) == \ -sqrt(6)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, S(1)/2)) )/6 - \ sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(1)/2)) )/6 - \ JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(1)/2)) )/3 + \ sqrt(3)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/3 + \ JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/3 - \ sqrt(5)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, -1))) == \ sqrt(2)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/2 + \ sqrt(6)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/6 + \ sqrt(30)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, -S(3)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1))) == \ -sqrt(2)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/2 - \ sqrt(6)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/6 - \ sqrt(30)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, S(3)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0))) == \ -sqrt(6)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, S(1)/2)) )/6 - \ sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(1)/2)) )/6 - \ JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(1)/2)) )/3 - \ sqrt(3)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/3 - \ JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/3 + \ sqrt(5)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1))) == \ -sqrt(3)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, S(1)/2)) )/3 - \ JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(1)/2)) )/3 + \ sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(1)/2)) )/6 - \ sqrt(6)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/6 - \ sqrt(2)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/6 + \ 2*sqrt(10)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(3)/2)) )/15 + \ sqrt(10)*JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1))) == \ sqrt(3)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, S(1)/2)) )/3 - \ JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(1)/2)) )/3 + \ sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(1)/2)) )/6 - \ sqrt(6)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/6 + \ sqrt(2)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/6 - \ 2*sqrt(10)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(3)/2)) )/15 + \ sqrt(10)*JzKetCoupled(S(5)/2, S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0))) == \ sqrt(6)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, S(1)/2)) )/6 - \ sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(1)/2)) )/6 - \ JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(1)/2)) )/3 - \ sqrt(3)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/3 + \ JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/3 - \ sqrt(5)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, -1))) == \ -sqrt(2)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/2 + \ sqrt(6)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/6 + \ sqrt(30)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, -S(3)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1))) == \ 2*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(1)/2)) )/3 + \ sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(1)/2)) )/6 - \ sqrt(2)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/3 - \ 2*sqrt(10)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(3)/2)) )/15 + \ sqrt(10)*JzKetCoupled(S(5)/2, S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0))) == \ sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(1)/2)) )/3 - \ JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(1)/2)) )/3 - \ 2*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/3 - \ sqrt(5)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1))) == \ -sqrt(6)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(1)/2), (1, 4, S(3)/2)) )/3 + \ sqrt(30)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, -S(3)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1))) == \ sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(1)/2)) )/2 - \ sqrt(10)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(3)/2)) )/5 + \ sqrt(10)*JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0))) == \ -sqrt(15)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(3)/2)) )/5 + \ sqrt(10)*JzKetCoupled(S(5)/2, -S(3)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, -1))) == \ JzKetCoupled(S(5)/2, -S(5)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (1, 3, S(3)/2), (1, 4, S(5)/2)) ) # Couple j1 to j2, j3 to j4 # j1=1/2, j2=1/2, j3=1/2, j4=1/2 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)), ((1, 2), (3, 4), (1, 3)) ) == \ JzKetCoupled(2, 2, (S( 1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 2)) ) assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)), ((1, 2), (3, 4), (1, 3)) ) == \ sqrt(2)*JzKetCoupled(1, 1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 0), (1, 3, 1)) )/2 + \ JzKetCoupled(1, 1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 1)) )/2 + \ JzKetCoupled(2, 1, (S(1)/2, S( 1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)), ((1, 2), (3, 4), (1, 3)) ) == \ -sqrt(2)*JzKetCoupled(1, 1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 0), (1, 3, 1)) )/2 + \ JzKetCoupled(1, 1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 1)) )/2 + \ JzKetCoupled(2, 1, (S(1)/2, S( 1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)), ((1, 2), (3, 4), (1, 3)) ) == \ sqrt(3)*JzKetCoupled(0, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 0)) )/3 + \ sqrt(2)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 1)) )/2 + \ sqrt(6)*JzKetCoupled(2, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/ 2), ((1, 2, 1), (3, 4, 1), (1, 3, 2)) )/6 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)), ((1, 2), (3, 4), (1, 3)) ) == \ sqrt(2)*JzKetCoupled(1, 1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (3, 4, 1), (1, 3, 1)) )/2 - \ JzKetCoupled(1, 1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 1)) )/2 + \ JzKetCoupled(2, 1, (S(1)/2, S( 1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)), ((1, 2), (3, 4), (1, 3)) ) == \ JzKetCoupled(0, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (3, 4, 0), (1, 3, 0)) )/2 - \ sqrt(3)*JzKetCoupled(0, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 0)) )/6 + \ JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (3, 4, 1), (1, 3, 1)) )/2 + \ JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 0), (1, 3, 1)) )/2 + \ sqrt(6)*JzKetCoupled(2, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/ 2), ((1, 2, 1), (3, 4, 1), (1, 3, 2)) )/6 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)), ((1, 2), (3, 4), (1, 3)) ) == \ -JzKetCoupled(0, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (3, 4, 0), (1, 3, 0)) )/2 - \ sqrt(3)*JzKetCoupled(0, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 0)) )/6 + \ JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (3, 4, 1), (1, 3, 1)) )/2 - \ JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 0), (1, 3, 1)) )/2 + \ sqrt(6)*JzKetCoupled(2, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/ 2), ((1, 2, 1), (3, 4, 1), (1, 3, 2)) )/6 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)), ((1, 2), (3, 4), (1, 3)) ) == \ sqrt(2)*JzKetCoupled(1, -1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (3, 4, 1), (1, 3, 1)) )/2 + \ JzKetCoupled(1, -1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 1)) )/2 + \ JzKetCoupled(2, -1, (S(1)/2, S( 1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)), ((1, 2), (3, 4), (1, 3)) ) == \ -sqrt(2)*JzKetCoupled(1, 1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (3, 4, 1), (1, 3, 1)) )/2 - \ JzKetCoupled(1, 1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 1)) )/2 + \ JzKetCoupled(2, 1, (S(1)/2, S( 1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)), ((1, 2), (3, 4), (1, 3)) ) == \ -JzKetCoupled(0, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (3, 4, 0), (1, 3, 0)) )/2 - \ sqrt(3)*JzKetCoupled(0, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 0)) )/6 - \ JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (3, 4, 1), (1, 3, 1)) )/2 + \ JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 0), (1, 3, 1)) )/2 + \ sqrt(6)*JzKetCoupled(2, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/ 2), ((1, 2, 1), (3, 4, 1), (1, 3, 2)) )/6 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)), ((1, 2), (3, 4), (1, 3)) ) == \ JzKetCoupled(0, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (3, 4, 0), (1, 3, 0)) )/2 - \ sqrt(3)*JzKetCoupled(0, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 0)) )/6 - \ JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (3, 4, 1), (1, 3, 1)) )/2 - \ JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 0), (1, 3, 1)) )/2 + \ sqrt(6)*JzKetCoupled(2, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/ 2), ((1, 2, 1), (3, 4, 1), (1, 3, 2)) )/6 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)), ((1, 2), (3, 4), (1, 3)) ) == \ -sqrt(2)*JzKetCoupled(1, -1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 0), (3, 4, 1), (1, 3, 1)) )/2 + \ JzKetCoupled(1, -1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 1)) )/2 + \ JzKetCoupled(2, -1, (S(1)/2, S( 1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)), ((1, 2), (3, 4), (1, 3)) ) == \ sqrt(3)*JzKetCoupled(0, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 0)) )/3 - \ sqrt(2)*JzKetCoupled(1, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 1)) )/2 + \ sqrt(6)*JzKetCoupled(2, 0, (S(1)/2, S(1)/2, S(1)/2, S(1)/ 2), ((1, 2, 1), (3, 4, 1), (1, 3, 2)) )/6 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2)), ((1, 2), (3, 4), (1, 3)) ) == \ sqrt(2)*JzKetCoupled(1, -1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 0), (1, 3, 1)) )/2 - \ JzKetCoupled(1, -1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 1)) )/2 + \ JzKetCoupled(2, -1, (S(1)/2, S( 1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2)), ((1, 2), (3, 4), (1, 3)) ) == \ -sqrt(2)*JzKetCoupled(1, -1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 0), (1, 3, 1)) )/2 - \ JzKetCoupled(1, -1, (S(1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 1)) )/2 + \ JzKetCoupled(2, -1, (S(1)/2, S( 1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 2)) )/2 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2)), ((1, 2), (3, 4), (1, 3)) ) == \ JzKetCoupled(2, -2, (S( 1)/2, S(1)/2, S(1)/2, S(1)/2), ((1, 2, 1), (3, 4, 1), (1, 3, 2)) ) # j1=S(1)/2, S(1)/2, S(1)/2, 1 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1)), ((1, 2), (3, 4), (1, 3)) ) == \ JzKetCoupled(S(5)/2, S(5)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(5)/2)) ) assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0)), ((1, 2), (3, 4), (1, 3)) ) == \ sqrt(3)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(3)/2)) )/3 + \ 2*sqrt(15)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/15 + \ sqrt(10)*JzKetCoupled(S(5)/2, S(3)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1)), ((1, 2), (3, 4), (1, 3)) ) == \ 2*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(1)/2)) )/3 + \ sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(1)/2)) )/6 + \ sqrt(2)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(3)/2)) )/3 + \ 2*sqrt(10)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/15 + \ sqrt(10)*JzKetCoupled(S(5)/2, S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1)), ((1, 2), (3, 4), (1, 3)) ) == \ -sqrt(6)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(3)/2)) )/3 + \ sqrt(30)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, S(3)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0)), ((1, 2), (3, 4), (1, 3)) ) == \ -sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(1)/2)) )/3 + \ JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(1)/2)) )/3 - \ JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(3)/2)) )/3 + \ 4*sqrt(5)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, -1)), ((1, 2), (3, 4), (1, 3)) ) == \ sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(1)/2)) )/2 + \ sqrt(10)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/5 + \ sqrt(10)*JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1)), ((1, 2), (3, 4), (1, 3)) ) == \ sqrt(2)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/2 - \ sqrt(30)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/10 + \ sqrt(5)*JzKetCoupled(S(5)/2, S(3)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0)), ((1, 2), (3, 4), (1, 3)) ) == \ sqrt(6)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (3, 4, S(1)/2), (1, 3, S(1)/2)) )/6 - \ sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(1)/2)) )/6 - \ JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(1)/2)) )/3 + \ sqrt(3)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/3 + \ JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(3)/2)) )/3 - \ sqrt(5)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1)), ((1, 2), (3, 4), (1, 3)) ) == \ sqrt(3)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (3, 4, S(1)/2), (1, 3, S(1)/2)) )/3 + \ JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(1)/2)) )/3 - \ sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(1)/2)) )/6 + \ sqrt(6)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/6 + \ sqrt(2)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(3)/2)) )/3 + \ sqrt(10)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/30 + \ sqrt(10)*JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1)), ((1, 2), (3, 4), (1, 3)) ) == \ -sqrt(3)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (3, 4, S(1)/2), (1, 3, S(1)/2)) )/3 + \ JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(1)/2)) )/3 - \ sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(1)/2)) )/6 + \ sqrt(6)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/6 - \ sqrt(2)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(3)/2)) )/3 - \ sqrt(10)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/30 + \ sqrt(10)*JzKetCoupled(S(5)/2, S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0)), ((1, 2), (3, 4), (1, 3)) ) == \ -sqrt(6)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (3, 4, S(1)/2), (1, 3, S(1)/2)) )/6 - \ sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(1)/2)) )/6 - \ JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(1)/2)) )/3 + \ sqrt(3)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/3 - \ JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(3)/2)) )/3 + \ sqrt(5)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, -1)), ((1, 2), (3, 4), (1, 3)) ) == \ sqrt(2)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/2 + \ sqrt(30)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/10 + \ sqrt(5)*JzKetCoupled(S(5)/2, -S(3)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1)), ((1, 2), (3, 4), (1, 3)) ) == \ -sqrt(2)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/2 - \ sqrt(30)*JzKetCoupled(S(3)/2, S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/10 + \ sqrt(5)*JzKetCoupled(S(5)/2, S(3)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0)), ((1, 2), (3, 4), (1, 3)) ) == \ -sqrt(6)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (3, 4, S(1)/2), (1, 3, S(1)/2)) )/6 - \ sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(1)/2)) )/6 - \ JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(1)/2)) )/3 - \ sqrt(3)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/3 + \ JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(3)/2)) )/3 - \ sqrt(5)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1)), ((1, 2), (3, 4), (1, 3)) ) == \ -sqrt(3)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (3, 4, S(1)/2), (1, 3, S(1)/2)) )/3 + \ JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(1)/2)) )/3 - \ sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(1)/2)) )/6 - \ sqrt(6)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/6 + \ sqrt(2)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(3)/2)) )/3 + \ sqrt(10)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/30 + \ sqrt(10)*JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1)), ((1, 2), (3, 4), (1, 3)) ) == \ sqrt(3)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (3, 4, S(1)/2), (1, 3, S(1)/2)) )/3 + \ JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(1)/2)) )/3 - \ sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(1)/2)) )/6 - \ sqrt(6)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/6 - \ sqrt(2)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(3)/2)) )/3 - \ sqrt(10)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/30 + \ sqrt(10)*JzKetCoupled(S(5)/2, S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0)), ((1, 2), (3, 4), (1, 3)) ) == \ sqrt(6)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (3, 4, S(1)/2), (1, 3, S(1)/2)) )/6 - \ sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(1)/2)) )/6 - \ JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(1)/2)) )/3 - \ sqrt(3)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/3 - \ JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(3)/2)) )/3 + \ sqrt(5)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, -1)), ((1, 2), (3, 4), (1, 3)) ) == \ -sqrt(2)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 0), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/2 + \ sqrt(30)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/10 + \ sqrt(5)*JzKetCoupled(S(5)/2, -S(3)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 1)), ((1, 2), (3, 4), (1, 3)) ) == \ sqrt(2)*JzKetCoupled(S(1)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(1)/2)) )/2 - \ sqrt(10)*JzKetCoupled(S(3)/2, S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/5 + \ sqrt(10)*JzKetCoupled(S(5)/2, S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, 0)), ((1, 2), (3, 4), (1, 3)) ) == \ -sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(1)/2)) )/3 + \ JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(1)/2)) )/3 + \ JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(3)/2)) )/3 - \ 4*sqrt(5)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(1)/2), JzKet(1, -1)), ((1, 2), (3, 4), (1, 3)) ) == \ sqrt(6)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(3)/2)) )/3 - \ sqrt(30)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/15 + \ sqrt(5)*JzKetCoupled(S(5)/2, -S(3)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 1)), ((1, 2), (3, 4), (1, 3)) ) == \ 2*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(1)/2)) )/3 + \ sqrt(2)*JzKetCoupled(S(1)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(1)/2)) )/6 - \ sqrt(2)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(3)/2)) )/3 - \ 2*sqrt(10)*JzKetCoupled(S(3)/2, -S(1)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/15 + \ sqrt(10)*JzKetCoupled(S(5)/2, -S(1)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(5)/2)) )/10 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, 0)), ((1, 2), (3, 4), (1, 3)) ) == \ -sqrt(3)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(1)/2), (1, 3, S(3)/2)) )/3 - \ 2*sqrt(15)*JzKetCoupled(S(3)/2, -S(3)/2, (S(1)/2, S(1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(3)/2)) )/15 + \ sqrt(10)*JzKetCoupled(S(5)/2, -S(3)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(5)/2)) )/5 assert couple(TensorProduct(JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(S(1)/2, S(-1)/2), JzKet(1, -1)), ((1, 2), (3, 4), (1, 3)) ) == \ JzKetCoupled(S(5)/2, -S(5)/2, (S(1)/2, S( 1)/2, S(1)/2, 1), ((1, 2, 1), (3, 4, S(3)/2), (1, 3, S(5)/2)) ) def test_couple_symbolic(): assert couple(TensorProduct(JzKet(j1, m1), JzKet(j2, m2))) == \ Sum(CG(j1, m1, j2, m2, j, m1 + m2) * JzKetCoupled(j, m1 + m2, ( j1, j2)), (j, m1 + m2, j1 + j2)) assert couple(TensorProduct(JzKet(j1, m1), JzKet(j2, m2), JzKet(j3, m3))) == \ Sum(CG(j1, m1, j2, m2, j12, m1 + m2) * CG(j12, m1 + m2, j3, m3, j, m1 + m2 + m3) * JzKetCoupled(j, m1 + m2 + m3, (j1, j2, j3), ((1, 2, j12), (1, 3, j)) ), (j12, m1 + m2, j1 + j2), (j, m1 + m2 + m3, j12 + j3)) assert couple(TensorProduct(JzKet(j1, m1), JzKet(j2, m2), JzKet(j3, m3)), ((1, 3), (1, 2)) ) == \ Sum(CG(j1, m1, j3, m3, j13, m1 + m3) * CG(j13, m1 + m3, j2, m2, j, m1 + m2 + m3) * JzKetCoupled(j, m1 + m2 + m3, (j1, j2, j3), ((1, 3, j13), (1, 2, j)) ), (j13, m1 + m3, j1 + j3), (j, m1 + m2 + m3, j13 + j2)) assert couple(TensorProduct(JzKet(j1, m1), JzKet(j2, m2), JzKet(j3, m3), JzKet(j4, m4))) == \ Sum(CG(j1, m1, j2, m2, j12, m1 + m2) * CG(j12, m1 + m2, j3, m3, j123, m1 + m2 + m3) * CG(j123, m1 + m2 + m3, j4, m4, j, m1 + m2 + m3 + m4) * JzKetCoupled(j, m1 + m2 + m3 + m4, ( j1, j2, j3, j4), ((1, 2, j12), (1, 3, j123), (1, 4, j)) ), (j12, m1 + m2, j1 + j2), (j123, m1 + m2 + m3, j12 + j3), (j, m1 + m2 + m3 + m4, j123 + j4)) assert couple(TensorProduct(JzKet(j1, m1), JzKet(j2, m2), JzKet(j3, m3), JzKet(j4, m4)), ((1, 2), (3, 4), (1, 3)) ) == \ Sum(CG(j1, m1, j2, m2, j12, m1 + m2) * CG(j3, m3, j4, m4, j34, m3 + m4) * CG(j12, m1 + m2, j34, m3 + m4, j, m1 + m2 + m3 + m4) * JzKetCoupled(j, m1 + m2 + m3 + m4, ( j1, j2, j3, j4), ((1, 2, j12), (3, 4, j34), (1, 3, j)) ), (j12, m1 + m2, j1 + j2), (j34, m3 + m4, j3 + j4), (j, m1 + m2 + m3 + m4, j12 + j34)) assert couple(TensorProduct(JzKet(j1, m1), JzKet(j2, m2), JzKet(j3, m3), JzKet(j4, m4)), ((1, 3), (1, 4), (1, 2)) ) == \ Sum(CG(j1, m1, j3, m3, j13, m1 + m3) * CG(j13, m1 + m3, j4, m4, j134, m1 + m3 + m4) * CG(j134, m1 + m3 + m4, j2, m2, j, m1 + m2 + m3 + m4) * JzKetCoupled(j, m1 + m2 + m3 + m4, ( j1, j2, j3, j4), ((1, 3, j13), (1, 4, j134), (1, 2, j)) ), (j13, m1 + m3, j1 + j3), (j134, m1 + m3 + m4, j13 + j4), (j, m1 + m2 + m3 + m4, j134 + j2)) def test_innerproduct(): assert InnerProduct(JzBra(1, 1), JzKet(1, 1)).doit() == 1 assert InnerProduct( JzBra(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2)).doit() == 0 assert InnerProduct(JzBra(j, m), JzKet(j, m)).doit() == 1 assert InnerProduct(JzBra(1, 0), JyKet(1, 1)).doit() == I/sqrt(2) assert InnerProduct( JxBra(S(1)/2, S(1)/2), JzKet(S(1)/2, S(1)/2)).doit() == -sqrt(2)/2 assert InnerProduct(JyBra(1, 1), JzKet(1, 1)).doit() == S(1)/2 assert InnerProduct(JxBra(1, -1), JyKet(1, 1)).doit() == 0 def test_rotation_small_d(): # Symbolic tests # j = 1/2 assert Rotation.d(S(1)/2, S(1)/2, S(1)/2, beta).doit() == cos(beta/2) assert Rotation.d(S(1)/2, S(1)/2, -S(1)/2, beta).doit() == -sin(beta/2) assert Rotation.d(S(1)/2, -S(1)/2, S(1)/2, beta).doit() == sin(beta/2) assert Rotation.d(S(1)/2, -S(1)/2, -S(1)/2, beta).doit() == cos(beta/2) # j = 1 assert Rotation.d(1, 1, 1, beta).doit() == (1 + cos(beta))/2 assert Rotation.d(1, 1, 0, beta).doit() == -sin(beta)/sqrt(2) assert Rotation.d(1, 1, -1, beta).doit() == (1 - cos(beta))/2 assert Rotation.d(1, 0, 1, beta).doit() == sin(beta)/sqrt(2) assert Rotation.d(1, 0, 0, beta).doit() == cos(beta) assert Rotation.d(1, 0, -1, beta).doit() == -sin(beta)/sqrt(2) assert Rotation.d(1, -1, 1, beta).doit() == (1 - cos(beta))/2 assert Rotation.d(1, -1, 0, beta).doit() == sin(beta)/sqrt(2) assert Rotation.d(1, -1, -1, beta).doit() == (1 + cos(beta))/2 # j = 3/2 assert Rotation.d(S( 3)/2, S(3)/2, S(3)/2, beta).doit() == (3*cos(beta/2) + cos(3*beta/2))/4 assert Rotation.d(S(3)/2, S( 3)/2, S(1)/2, beta).doit() == -sqrt(3)*(sin(beta/2) + sin(3*beta/2))/4 assert Rotation.d(S(3)/2, S( 3)/2, -S(1)/2, beta).doit() == sqrt(3)*(cos(beta/2) - cos(3*beta/2))/4 assert Rotation.d(S(3)/2, S( 3)/2, -S(3)/2, beta).doit() == (-3*sin(beta/2) + sin(3*beta/2))/4 assert Rotation.d(S(3)/2, S( 1)/2, S(3)/2, beta).doit() == sqrt(3)*(sin(beta/2) + sin(3*beta/2))/4 assert Rotation.d(S( 3)/2, S(1)/2, S(1)/2, beta).doit() == (cos(beta/2) + 3*cos(3*beta/2))/4 assert Rotation.d(S( 3)/2, S(1)/2, -S(1)/2, beta).doit() == (sin(beta/2) - 3*sin(3*beta/2))/4 assert Rotation.d(S(3)/2, S( 1)/2, -S(3)/2, beta).doit() == sqrt(3)*(cos(beta/2) - cos(3*beta/2))/4 assert Rotation.d(S(3)/2, -S( 1)/2, S(3)/2, beta).doit() == sqrt(3)*(cos(beta/2) - cos(3*beta/2))/4 assert Rotation.d(S(3)/2, -S( 1)/2, S(1)/2, beta).doit() == (-sin(beta/2) + 3*sin(3*beta/2))/4 assert Rotation.d(S(3)/2, -S( 1)/2, -S(1)/2, beta).doit() == (cos(beta/2) + 3*cos(3*beta/2))/4 assert Rotation.d(S(3)/2, -S( 1)/2, -S(3)/2, beta).doit() == -sqrt(3)*(sin(beta/2) + sin(3*beta/2))/4 assert Rotation.d(S( 3)/2, -S(3)/2, S(3)/2, beta).doit() == (3*sin(beta/2) - sin(3*beta/2))/4 assert Rotation.d(S(3)/2, -S( 3)/2, S(1)/2, beta).doit() == sqrt(3)*(cos(beta/2) - cos(3*beta/2))/4 assert Rotation.d(S(3)/2, -S( 3)/2, -S(1)/2, beta).doit() == sqrt(3)*(sin(beta/2) + sin(3*beta/2))/4 assert Rotation.d(S(3)/2, -S( 3)/2, -S(3)/2, beta).doit() == (3*cos(beta/2) + cos(3*beta/2))/4 # j = 2 assert Rotation.d(2, 2, 2, beta).doit() == (3 + 4*cos(beta) + cos(2*beta))/8 assert Rotation.d(2, 2, 1, beta).doit() == -((cos(beta) + 1)*sin(beta))/2 assert Rotation.d(2, 2, 0, beta).doit() == sqrt(6)*sin(beta)**2/4 assert Rotation.d(2, 2, -1, beta).doit() == (cos(beta) - 1)*sin(beta)/2 assert Rotation.d(2, 2, -2, beta).doit() == (3 - 4*cos(beta) + cos(2*beta))/8 assert Rotation.d(2, 1, 2, beta).doit() == (cos(beta) + 1)*sin(beta)/2 assert Rotation.d(2, 1, 1, beta).doit() == (cos(beta) + cos(2*beta))/2 assert Rotation.d(2, 1, 0, beta).doit() == -sqrt(6)*sin(2*beta)/4 assert Rotation.d(2, 1, -1, beta).doit() == (cos(beta) - cos(2*beta))/2 assert Rotation.d(2, 1, -2, beta).doit() == (cos(beta) - 1)*sin(beta)/2 assert Rotation.d(2, 0, 2, beta).doit() == sqrt(6)*sin(beta)**2/4 assert Rotation.d(2, 0, 1, beta).doit() == sqrt(6)*sin(2*beta)/4 assert Rotation.d(2, 0, 0, beta).doit() == (1 + 3*cos(2*beta))/4 assert Rotation.d(2, 0, -1, beta).doit() == -sqrt(6)*sin(2*beta)/4 assert Rotation.d(2, 0, -2, beta).doit() == sqrt(6)*sin(beta)**2/4 assert Rotation.d(2, -1, 2, beta).doit() == (2*sin(beta) - sin(2*beta))/4 assert Rotation.d(2, -1, 1, beta).doit() == (cos(beta) - cos(2*beta))/2 assert Rotation.d(2, -1, 0, beta).doit() == sqrt(6)*sin(2*beta)/4 assert Rotation.d(2, -1, -1, beta).doit() == (cos(beta) + cos(2*beta))/2 assert Rotation.d(2, -1, -2, beta).doit() == -((cos(beta) + 1)*sin(beta))/2 assert Rotation.d(2, -2, 2, beta).doit() == (3 - 4*cos(beta) + cos(2*beta))/8 assert Rotation.d(2, -2, 1, beta).doit() == (2*sin(beta) - sin(2*beta))/4 assert Rotation.d(2, -2, 0, beta).doit() == sqrt(6)*sin(beta)**2/4 assert Rotation.d(2, -2, -1, beta).doit() == (cos(beta) + 1)*sin(beta)/2 assert Rotation.d(2, -2, -2, beta).doit() == (3 + 4*cos(beta) + cos(2*beta))/8 # Numerical tests # j = 1/2 assert Rotation.d(S(1)/2, S(1)/2, S(1)/2, pi/2).doit() == sqrt(2)/2 assert Rotation.d(S(1)/2, S(1)/2, -S(1)/2, pi/2).doit() == -sqrt(2)/2 assert Rotation.d(S(1)/2, -S(1)/2, S(1)/2, pi/2).doit() == sqrt(2)/2 assert Rotation.d(S(1)/2, -S(1)/2, -S(1)/2, pi/2).doit() == sqrt(2)/2 # j = 1 assert Rotation.d(1, 1, 1, pi/2).doit() == 1/2 assert Rotation.d(1, 1, 0, pi/2).doit() == -sqrt(2)/2 assert Rotation.d(1, 1, -1, pi/2).doit() == 1/2 assert Rotation.d(1, 0, 1, pi/2).doit() == sqrt(2)/2 assert Rotation.d(1, 0, 0, pi/2).doit() == 0 assert Rotation.d(1, 0, -1, pi/2).doit() == -sqrt(2)/2 assert Rotation.d(1, -1, 1, pi/2).doit() == 1/2 assert Rotation.d(1, -1, 0, pi/2).doit() == sqrt(2)/2 assert Rotation.d(1, -1, -1, pi/2).doit() == 1/2 # j = 3/2 assert Rotation.d(S(3)/2, S(3)/2, S(3)/2, pi/2).doit() == sqrt(2)/4 assert Rotation.d(S(3)/2, S(3)/2, S(1)/2, pi/2).doit() == -sqrt(6)/4 assert Rotation.d(S(3)/2, S(3)/2, -S(1)/2, pi/2).doit() == sqrt(6)/4 assert Rotation.d(S(3)/2, S(3)/2, -S(3)/2, pi/2).doit() == -sqrt(2)/4 assert Rotation.d(S(3)/2, S(1)/2, S(3)/2, pi/2).doit() == sqrt(6)/4 assert Rotation.d(S(3)/2, S(1)/2, S(1)/2, pi/2).doit() == -sqrt(2)/4 assert Rotation.d(S(3)/2, S(1)/2, -S(1)/2, pi/2).doit() == -sqrt(2)/4 assert Rotation.d(S(3)/2, S(1)/2, -S(3)/2, pi/2).doit() == sqrt(6)/4 assert Rotation.d(S(3)/2, -S(1)/2, S(3)/2, pi/2).doit() == sqrt(6)/4 assert Rotation.d(S(3)/2, -S(1)/2, S(1)/2, pi/2).doit() == sqrt(2)/4 assert Rotation.d(S(3)/2, -S(1)/2, -S(1)/2, pi/2).doit() == -sqrt(2)/4 assert Rotation.d(S(3)/2, -S(1)/2, -S(3)/2, pi/2).doit() == -sqrt(6)/4 assert Rotation.d(S(3)/2, -S(3)/2, S(3)/2, pi/2).doit() == sqrt(2)/4 assert Rotation.d(S(3)/2, -S(3)/2, S(1)/2, pi/2).doit() == sqrt(6)/4 assert Rotation.d(S(3)/2, -S(3)/2, -S(1)/2, pi/2).doit() == sqrt(6)/4 assert Rotation.d(S(3)/2, -S(3)/2, -S(3)/2, pi/2).doit() == sqrt(2)/4 # j = 2 assert Rotation.d(2, 2, 2, pi/2).doit() == 1/4 assert Rotation.d(2, 2, 1, pi/2).doit() == -1/2 assert Rotation.d(2, 2, 0, pi/2).doit() == sqrt(6)/4 assert Rotation.d(2, 2, -1, pi/2).doit() == -1/2 assert Rotation.d(2, 2, -2, pi/2).doit() == 1/4 assert Rotation.d(2, 1, 2, pi/2).doit() == 1/2 assert Rotation.d(2, 1, 1, pi/2).doit() == -1/2 assert Rotation.d(2, 1, 0, pi/2).doit() == 0 assert Rotation.d(2, 1, -1, pi/2).doit() == 1/2 assert Rotation.d(2, 1, -2, pi/2).doit() == -1/2 assert Rotation.d(2, 0, 2, pi/2).doit() == sqrt(6)/4 assert Rotation.d(2, 0, 1, pi/2).doit() == 0 assert Rotation.d(2, 0, 0, pi/2).doit() == -1/2 assert Rotation.d(2, 0, -1, pi/2).doit() == 0 assert Rotation.d(2, 0, -2, pi/2).doit() == sqrt(6)/4 assert Rotation.d(2, -1, 2, pi/2).doit() == 1/2 assert Rotation.d(2, -1, 1, pi/2).doit() == 1/2 assert Rotation.d(2, -1, 0, pi/2).doit() == 0 assert Rotation.d(2, -1, -1, pi/2).doit() == -1/2 assert Rotation.d(2, -1, -2, pi/2).doit() == -1/2 assert Rotation.d(2, -2, 2, pi/2).doit() == 1/4 assert Rotation.d(2, -2, 1, pi/2).doit() == 1/2 assert Rotation.d(2, -2, 0, pi/2).doit() == sqrt(6)/4 assert Rotation.d(2, -2, -1, pi/2).doit() == 1/2 assert Rotation.d(2, -2, -2, pi/2).doit() == 1/4 def test_rotation_d(): # Symbolic tests # j = 1/2 assert Rotation.D(S(1)/2, S(1)/2, S(1)/2, alpha, beta, gamma).doit() == \ cos(beta/2)*exp(-I*alpha/2)*exp(-I*gamma/2) assert Rotation.D(S(1)/2, S(1)/2, -S(1)/2, alpha, beta, gamma).doit() == \ -sin(beta/2)*exp(-I*alpha/2)*exp(I*gamma/2) assert Rotation.D(S(1)/2, -S(1)/2, S(1)/2, alpha, beta, gamma).doit() == \ sin(beta/2)*exp(I*alpha/2)*exp(-I*gamma/2) assert Rotation.D(S(1)/2, -S(1)/2, -S(1)/2, alpha, beta, gamma).doit() == \ cos(beta/2)*exp(I*alpha/2)*exp(I*gamma/2) # j = 1 assert Rotation.D(1, 1, 1, alpha, beta, gamma).doit() == \ (1 + cos(beta))/2*exp(-I*alpha)*exp(-I*gamma) assert Rotation.D(1, 1, 0, alpha, beta, gamma).doit() == -sin( beta)/sqrt(2)*exp(-I*alpha) assert Rotation.D(1, 1, -1, alpha, beta, gamma).doit() == \ (1 - cos(beta))/2*exp(-I*alpha)*exp(I*gamma) assert Rotation.D(1, 0, 1, alpha, beta, gamma).doit() == \ sin(beta)/sqrt(2)*exp(-I*gamma) assert Rotation.D(1, 0, 0, alpha, beta, gamma).doit() == cos(beta) assert Rotation.D(1, 0, -1, alpha, beta, gamma).doit() == \ -sin(beta)/sqrt(2)*exp(I*gamma) assert Rotation.D(1, -1, 1, alpha, beta, gamma).doit() == \ (1 - cos(beta))/2*exp(I*alpha)*exp(-I*gamma) assert Rotation.D(1, -1, 0, alpha, beta, gamma).doit() == \ sin(beta)/sqrt(2)*exp(I*alpha) assert Rotation.D(1, -1, -1, alpha, beta, gamma).doit() == \ (1 + cos(beta))/2*exp(I*alpha)*exp(I*gamma) # j = 3/2 assert Rotation.D(S(3)/2, S(3)/2, S(3)/2, alpha, beta, gamma).doit() == \ (3*cos(beta/2) + cos(3*beta/2))/4*exp(-3*I*alpha/2)*exp(-3*I*gamma/2) assert Rotation.D(S(3)/2, S(3)/2, S(1)/2, alpha, beta, gamma).doit() == \ -sqrt(3)*(sin(beta/2) + sin(3*beta/2))/4*exp(-3*I*alpha/2)*exp(-I*gamma/2) assert Rotation.D(S(3)/2, S(3)/2, -S(1)/2, alpha, beta, gamma).doit() == \ sqrt(3)*(cos(beta/2) - cos(3*beta/2))/4*exp(-3*I*alpha/2)*exp(I*gamma/2) assert Rotation.D(S(3)/2, S(3)/2, -S(3)/2, alpha, beta, gamma).doit() == \ (-3*sin(beta/2) + sin(3*beta/2))/4*exp(-3*I*alpha/2)*exp(3*I*gamma/2) assert Rotation.D(S(3)/2, S(1)/2, S(3)/2, alpha, beta, gamma).doit() == \ sqrt(3)*(sin(beta/2) + sin(3*beta/2))/4*exp(-I*alpha/2)*exp(-3*I*gamma/2) assert Rotation.D(S(3)/2, S(1)/2, S(1)/2, alpha, beta, gamma).doit() == \ (cos(beta/2) + 3*cos(3*beta/2))/4*exp(-I*alpha/2)*exp(-I*gamma/2) assert Rotation.D(S(3)/2, S(1)/2, -S(1)/2, alpha, beta, gamma).doit() == \ (sin(beta/2) - 3*sin(3*beta/2))/4*exp(-I*alpha/2)*exp(I*gamma/2) assert Rotation.D(S(3)/2, S(1)/2, -S(3)/2, alpha, beta, gamma).doit() == \ sqrt(3)*(cos(beta/2) - cos(3*beta/2))/4*exp(-I*alpha/2)*exp(3*I*gamma/2) assert Rotation.D(S(3)/2, -S(1)/2, S(3)/2, alpha, beta, gamma).doit() == \ sqrt(3)*(cos(beta/2) - cos(3*beta/2))/4*exp(I*alpha/2)*exp(-3*I*gamma/2) assert Rotation.D(S(3)/2, -S(1)/2, S(1)/2, alpha, beta, gamma).doit() == \ (-sin(beta/2) + 3*sin(3*beta/2))/4*exp(I*alpha/2)*exp(-I*gamma/2) assert Rotation.D(S(3)/2, -S(1)/2, -S(1)/2, alpha, beta, gamma).doit() == \ (cos(beta/2) + 3*cos(3*beta/2))/4*exp(I*alpha/2)*exp(I*gamma/2) assert Rotation.D(S(3)/2, -S(1)/2, -S(3)/2, alpha, beta, gamma).doit() == \ -sqrt(3)*(sin(beta/2) + sin(3*beta/2))/4*exp(I*alpha/2)*exp(3*I*gamma/2) assert Rotation.D(S(3)/2, -S(3)/2, S(3)/2, alpha, beta, gamma).doit() == \ (3*sin(beta/2) - sin(3*beta/2))/4*exp(3*I*alpha/2)*exp(-3*I*gamma/2) assert Rotation.D(S(3)/2, -S(3)/2, S(1)/2, alpha, beta, gamma).doit() == \ sqrt(3)*(cos(beta/2) - cos(3*beta/2))/4*exp(3*I*alpha/2)*exp(-I*gamma/2) assert Rotation.D(S(3)/2, -S(3)/2, -S(1)/2, alpha, beta, gamma).doit() == \ sqrt(3)*(sin(beta/2) + sin(3*beta/2))/4*exp(3*I*alpha/2)*exp(I*gamma/2) assert Rotation.D(S(3)/2, -S(3)/2, -S(3)/2, alpha, beta, gamma).doit() == \ (3*cos(beta/2) + cos(3*beta/2))/4*exp(3*I*alpha/2)*exp(3*I*gamma/2) # j = 2 assert Rotation.D(2, 2, 2, alpha, beta, gamma).doit() == \ (3 + 4*cos(beta) + cos(2*beta))/8*exp(-2*I*alpha)*exp(-2*I*gamma) assert Rotation.D(2, 2, 1, alpha, beta, gamma).doit() == \ -((cos(beta) + 1)*exp(-2*I*alpha)*exp(-I*gamma)*sin(beta))/2 assert Rotation.D(2, 2, 0, alpha, beta, gamma).doit() == \ sqrt(6)*sin(beta)**2/4*exp(-2*I*alpha) assert Rotation.D(2, 2, -1, alpha, beta, gamma).doit() == \ (cos(beta) - 1)*sin(beta)/2*exp(-2*I*alpha)*exp(I*gamma) assert Rotation.D(2, 2, -2, alpha, beta, gamma).doit() == \ (3 - 4*cos(beta) + cos(2*beta))/8*exp(-2*I*alpha)*exp(2*I*gamma) assert Rotation.D(2, 1, 2, alpha, beta, gamma).doit() == \ (cos(beta) + 1)*sin(beta)/2*exp(-I*alpha)*exp(-2*I*gamma) assert Rotation.D(2, 1, 1, alpha, beta, gamma).doit() == \ (cos(beta) + cos(2*beta))/2*exp(-I*alpha)*exp(-I*gamma) assert Rotation.D(2, 1, 0, alpha, beta, gamma).doit() == -sqrt(6)* \ sin(2*beta)/4*exp(-I*alpha) assert Rotation.D(2, 1, -1, alpha, beta, gamma).doit() == \ (cos(beta) - cos(2*beta))/2*exp(-I*alpha)*exp(I*gamma) assert Rotation.D(2, 1, -2, alpha, beta, gamma).doit() == \ (cos(beta) - 1)*sin(beta)/2*exp(-I*alpha)*exp(2*I*gamma) assert Rotation.D(2, 0, 2, alpha, beta, gamma).doit() == \ sqrt(6)*sin(beta)**2/4*exp(-2*I*gamma) assert Rotation.D(2, 0, 1, alpha, beta, gamma).doit() == sqrt(6)* \ sin(2*beta)/4*exp(-I*gamma) assert Rotation.D( 2, 0, 0, alpha, beta, gamma).doit() == (1 + 3*cos(2*beta))/4 assert Rotation.D(2, 0, -1, alpha, beta, gamma).doit() == -sqrt(6)* \ sin(2*beta)/4*exp(I*gamma) assert Rotation.D(2, 0, -2, alpha, beta, gamma).doit() == \ sqrt(6)*sin(beta)**2/4*exp(2*I*gamma) assert Rotation.D(2, -1, 2, alpha, beta, gamma).doit() == \ (2*sin(beta) - sin(2*beta))/4*exp(I*alpha)*exp(-2*I*gamma) assert Rotation.D(2, -1, 1, alpha, beta, gamma).doit() == \ (cos(beta) - cos(2*beta))/2*exp(I*alpha)*exp(-I*gamma) assert Rotation.D(2, -1, 0, alpha, beta, gamma).doit() == sqrt(6)* \ sin(2*beta)/4*exp(I*alpha) assert Rotation.D(2, -1, -1, alpha, beta, gamma).doit() == \ (cos(beta) + cos(2*beta))/2*exp(I*alpha)*exp(I*gamma) assert Rotation.D(2, -1, -2, alpha, beta, gamma).doit() == \ -((cos(beta) + 1)*sin(beta))/2*exp(I*alpha)*exp(2*I*gamma) assert Rotation.D(2, -2, 2, alpha, beta, gamma).doit() == \ (3 - 4*cos(beta) + cos(2*beta))/8*exp(2*I*alpha)*exp(-2*I*gamma) assert Rotation.D(2, -2, 1, alpha, beta, gamma).doit() == \ (2*sin(beta) - sin(2*beta))/4*exp(2*I*alpha)*exp(-I*gamma) assert Rotation.D(2, -2, 0, alpha, beta, gamma).doit() == \ sqrt(6)*sin(beta)**2/4*exp(2*I*alpha) assert Rotation.D(2, -2, -1, alpha, beta, gamma).doit() == \ (cos(beta) + 1)*sin(beta)/2*exp(2*I*alpha)*exp(I*gamma) assert Rotation.D(2, -2, -2, alpha, beta, gamma).doit() == \ (3 + 4*cos(beta) + cos(2*beta))/8*exp(2*I*alpha)*exp(2*I*gamma) # Numerical tests # j = 1/2 assert Rotation.D( S(1)/2, S(1)/2, S(1)/2, pi/2, pi/2, pi/2).doit() == -I*sqrt(2)/2 assert Rotation.D( S(1)/2, S(1)/2, -S(1)/2, pi/2, pi/2, pi/2).doit() == -sqrt(2)/2 assert Rotation.D( S(1)/2, -S(1)/2, S(1)/2, pi/2, pi/2, pi/2).doit() == sqrt(2)/2 assert Rotation.D( S(1)/2, -S(1)/2, -S(1)/2, pi/2, pi/2, pi/2).doit() == I*sqrt(2)/2 # j = 1 assert Rotation.D(1, 1, 1, pi/2, pi/2, pi/2).doit() == -1/2 assert Rotation.D(1, 1, 0, pi/2, pi/2, pi/2).doit() == I*sqrt(2)/2 assert Rotation.D(1, 1, -1, pi/2, pi/2, pi/2).doit() == 1/2 assert Rotation.D(1, 0, 1, pi/2, pi/2, pi/2).doit() == -I*sqrt(2)/2 assert Rotation.D(1, 0, 0, pi/2, pi/2, pi/2).doit() == 0 assert Rotation.D(1, 0, -1, pi/2, pi/2, pi/2).doit() == -I*sqrt(2)/2 assert Rotation.D(1, -1, 1, pi/2, pi/2, pi/2).doit() == 1/2 assert Rotation.D(1, -1, 0, pi/2, pi/2, pi/2).doit() == I*sqrt(2)/2 assert Rotation.D(1, -1, -1, pi/2, pi/2, pi/2).doit() == -1/2 # j = 3/2 assert Rotation.D( S(3)/2, S(3)/2, S(3)/2, pi/2, pi/2, pi/2).doit() == I*sqrt(2)/4 assert Rotation.D( S(3)/2, S(3)/2, S(1)/2, pi/2, pi/2, pi/2).doit() == sqrt(6)/4 assert Rotation.D( S(3)/2, S(3)/2, -S(1)/2, pi/2, pi/2, pi/2).doit() == -I*sqrt(6)/4 assert Rotation.D( S(3)/2, S(3)/2, -S(3)/2, pi/2, pi/2, pi/2).doit() == -sqrt(2)/4 assert Rotation.D( S(3)/2, S(1)/2, S(3)/2, pi/2, pi/2, pi/2).doit() == -sqrt(6)/4 assert Rotation.D( S(3)/2, S(1)/2, S(1)/2, pi/2, pi/2, pi/2).doit() == I*sqrt(2)/4 assert Rotation.D( S(3)/2, S(1)/2, -S(1)/2, pi/2, pi/2, pi/2).doit() == -sqrt(2)/4 assert Rotation.D( S(3)/2, S(1)/2, -S(3)/2, pi/2, pi/2, pi/2).doit() == I*sqrt(6)/4 assert Rotation.D( S(3)/2, -S(1)/2, S(3)/2, pi/2, pi/2, pi/2).doit() == -I*sqrt(6)/4 assert Rotation.D( S(3)/2, -S(1)/2, S(1)/2, pi/2, pi/2, pi/2).doit() == sqrt(2)/4 assert Rotation.D( S(3)/2, -S(1)/2, -S(1)/2, pi/2, pi/2, pi/2).doit() == -I*sqrt(2)/4 assert Rotation.D( S(3)/2, -S(1)/2, -S(3)/2, pi/2, pi/2, pi/2).doit() == sqrt(6)/4 assert Rotation.D( S(3)/2, -S(3)/2, S(3)/2, pi/2, pi/2, pi/2).doit() == sqrt(2)/4 assert Rotation.D( S(3)/2, -S(3)/2, S(1)/2, pi/2, pi/2, pi/2).doit() == I*sqrt(6)/4 assert Rotation.D( S(3)/2, -S(3)/2, -S(1)/2, pi/2, pi/2, pi/2).doit() == -sqrt(6)/4 assert Rotation.D( S(3)/2, -S(3)/2, -S(3)/2, pi/2, pi/2, pi/2).doit() == -I*sqrt(2)/4 # j = 2 assert Rotation.D(2, 2, 2, pi/2, pi/2, pi/2).doit() == 1/4 assert Rotation.D(2, 2, 1, pi/2, pi/2, pi/2).doit() == -I/2 assert Rotation.D(2, 2, 0, pi/2, pi/2, pi/2).doit() == -sqrt(6)/4 assert Rotation.D(2, 2, -1, pi/2, pi/2, pi/2).doit() == I/2 assert Rotation.D(2, 2, -2, pi/2, pi/2, pi/2).doit() == 1/4 assert Rotation.D(2, 1, 2, pi/2, pi/2, pi/2).doit() == I/2 assert Rotation.D(2, 1, 1, pi/2, pi/2, pi/2).doit() == 1/2 assert Rotation.D(2, 1, 0, pi/2, pi/2, pi/2).doit() == 0 assert Rotation.D(2, 1, -1, pi/2, pi/2, pi/2).doit() == 1/2 assert Rotation.D(2, 1, -2, pi/2, pi/2, pi/2).doit() == -I/2 assert Rotation.D(2, 0, 2, pi/2, pi/2, pi/2).doit() == -sqrt(6)/4 assert Rotation.D(2, 0, 1, pi/2, pi/2, pi/2).doit() == 0 assert Rotation.D(2, 0, 0, pi/2, pi/2, pi/2).doit() == -1/2 assert Rotation.D(2, 0, -1, pi/2, pi/2, pi/2).doit() == 0 assert Rotation.D(2, 0, -2, pi/2, pi/2, pi/2).doit() == -sqrt(6)/4 assert Rotation.D(2, -1, 2, pi/2, pi/2, pi/2).doit() == -I/2 assert Rotation.D(2, -1, 1, pi/2, pi/2, pi/2).doit() == 1/2 assert Rotation.D(2, -1, 0, pi/2, pi/2, pi/2).doit() == 0 assert Rotation.D(2, -1, -1, pi/2, pi/2, pi/2).doit() == 1/2 assert Rotation.D(2, -1, -2, pi/2, pi/2, pi/2).doit() == I/2 assert Rotation.D(2, -2, 2, pi/2, pi/2, pi/2).doit() == 1/4 assert Rotation.D(2, -2, 1, pi/2, pi/2, pi/2).doit() == I/2 assert Rotation.D(2, -2, 0, pi/2, pi/2, pi/2).doit() == -sqrt(6)/4 assert Rotation.D(2, -2, -1, pi/2, pi/2, pi/2).doit() == -I/2 assert Rotation.D(2, -2, -2, pi/2, pi/2, pi/2).doit() == 1/4 def test_wignerd(): assert Rotation.D( j, m, mp, alpha, beta, gamma) == WignerD(j, m, mp, alpha, beta, gamma) assert Rotation.d(j, m, mp, beta) == WignerD(j, m, mp, 0, beta, 0) def test_jplus(): assert Commutator(Jplus, Jminus).doit() == 2*hbar*Jz assert Jplus.matrix_element(1, 1, 1, 1) == 0 assert Jplus.rewrite('xyz') == Jx + I*Jy # Normal operators, normal states # Numerical assert qapply(Jplus*JxKet(1, 1)) == \ -hbar*sqrt(2)*JxKet(1, 0)/2 + hbar*JxKet(1, 1) assert qapply(Jplus*JyKet(1, 1)) == \ hbar*sqrt(2)*JyKet(1, 0)/2 + I*hbar*JyKet(1, 1) assert qapply(Jplus*JzKet(1, 1)) == 0 # Symbolic assert qapply(Jplus*JxKet(j, m)) == \ Sum(hbar * sqrt(-mi**2 - mi + j**2 + j) * WignerD(j, mi, m, 0, pi/2, 0) * Sum(WignerD(j, mi1, mi + 1, 0, 3*pi/2, 0) * JxKet(j, mi1), (mi1, -j, j)), (mi, -j, j)) assert qapply(Jplus*JyKet(j, m)) == \ Sum(hbar * sqrt(j**2 + j - mi**2 - mi) * WignerD(j, mi, m, 3*pi/2, -pi/2, pi/2) * Sum(WignerD(j, mi1, mi + 1, 3*pi/2, pi/2, pi/2) * JyKet(j, mi1), (mi1, -j, j)), (mi, -j, j)) assert qapply(Jplus*JzKet(j, m)) == \ hbar*sqrt(j**2 + j - m**2 - m)*JzKet(j, m + 1) # Normal operators, coupled states # Numerical assert qapply(Jplus*JxKetCoupled(1, 1, (1, 1))) == -hbar*sqrt(2) * \ JxKetCoupled(1, 0, (1, 1))/2 + hbar*JxKetCoupled(1, 1, (1, 1)) assert qapply(Jplus*JyKetCoupled(1, 1, (1, 1))) == hbar*sqrt(2) * \ JyKetCoupled(1, 0, (1, 1))/2 + I*hbar*JyKetCoupled(1, 1, (1, 1)) assert qapply(Jplus*JzKet(1, 1)) == 0 # Symbolic assert qapply(Jplus*JxKetCoupled(j, m, (j1, j2))) == \ Sum(hbar * sqrt(-mi**2 - mi + j**2 + j) * WignerD(j, mi, m, 0, pi/2, 0) * Sum( WignerD( j, mi1, mi + 1, 0, 3*pi/2, 0) * JxKetCoupled(j, mi1, (j1, j2)), (mi1, -j, j)), (mi, -j, j)) assert qapply(Jplus*JyKetCoupled(j, m, (j1, j2))) == \ Sum(hbar * sqrt(j**2 + j - mi**2 - mi) * WignerD(j, mi, m, 3*pi/2, -pi/2, pi/2) * Sum( WignerD(j, mi1, mi + 1, 3*pi/2, pi/2, pi/2) * JyKetCoupled(j, mi1, (j1, j2)), (mi1, -j, j)), (mi, -j, j)) assert qapply(Jplus*JzKetCoupled(j, m, (j1, j2))) == \ hbar*sqrt(j**2 + j - m**2 - m)*JzKetCoupled(j, m + 1, (j1, j2)) # Uncoupled operators, uncoupled states # Numerical assert qapply(TensorProduct(Jplus, 1)*TensorProduct(JxKet(1, 1), JxKet(1, -1))) == \ -hbar*sqrt(2)*TensorProduct(JxKet(1, 0), JxKet(1, -1))/2 + \ hbar*TensorProduct(JxKet(1, 1), JxKet(1, -1)) assert qapply(TensorProduct(1, Jplus)*TensorProduct(JxKet(1, 1), JxKet(1, -1))) == \ -hbar*TensorProduct(JxKet(1, 1), JxKet(1, -1)) + \ hbar*sqrt(2)*TensorProduct(JxKet(1, 1), JxKet(1, 0))/2 assert qapply(TensorProduct(Jplus, 1)*TensorProduct(JyKet(1, 1), JyKet(1, -1))) == \ hbar*sqrt(2)*TensorProduct(JyKet(1, 0), JyKet(1, -1))/2 + \ hbar*I*TensorProduct(JyKet(1, 1), JyKet(1, -1)) assert qapply(TensorProduct(1, Jplus)*TensorProduct(JyKet(1, 1), JyKet(1, -1))) == \ -hbar*I*TensorProduct(JyKet(1, 1), JyKet(1, -1)) + \ hbar*sqrt(2)*TensorProduct(JyKet(1, 1), JyKet(1, 0))/2 assert qapply( TensorProduct(Jplus, 1)*TensorProduct(JzKet(1, 1), JzKet(1, -1))) == 0 assert qapply(TensorProduct(1, Jplus)*TensorProduct(JzKet(1, 1), JzKet(1, -1))) == \ hbar*sqrt(2)*TensorProduct(JzKet(1, 1), JzKet(1, 0)) # Symbolic assert qapply(TensorProduct(Jplus, 1)*TensorProduct(JxKet(j1, m1), JxKet(j2, m2))) == \ TensorProduct(Sum(hbar * sqrt(-mi**2 - mi + j1**2 + j1) * WignerD(j1, mi, m1, 0, pi/2, 0) * Sum(WignerD(j1, mi1, mi + 1, 0, 3*pi/2, 0) * JxKet(j1, mi1), (mi1, -j1, j1)), (mi, -j1, j1)), JxKet(j2, m2)) assert qapply(TensorProduct(1, Jplus)*TensorProduct(JxKet(j1, m1), JxKet(j2, m2))) == \ TensorProduct(JxKet(j1, m1), Sum(hbar * sqrt(-mi**2 - mi + j2**2 + j2) * WignerD(j2, mi, m2, 0, pi/2, 0) * Sum(WignerD(j2, mi1, mi + 1, 0, 3*pi/2, 0) * JxKet(j2, mi1), (mi1, -j2, j2)), (mi, -j2, j2))) assert qapply(TensorProduct(Jplus, 1)*TensorProduct(JyKet(j1, m1), JyKet(j2, m2))) == \ TensorProduct(Sum(hbar * sqrt(j1**2 + j1 - mi**2 - mi) * WignerD(j1, mi, m1, 3*pi/2, -pi/2, pi/2) * Sum(WignerD(j1, mi1, mi + 1, 3*pi/2, pi/2, pi/2) * JyKet(j1, mi1), (mi1, -j1, j1)), (mi, -j1, j1)), JyKet(j2, m2)) assert qapply(TensorProduct(1, Jplus)*TensorProduct(JyKet(j1, m1), JyKet(j2, m2))) == \ TensorProduct(JyKet(j1, m1), Sum(hbar * sqrt(j2**2 + j2 - mi**2 - mi) * WignerD(j2, mi, m2, 3*pi/2, -pi/2, pi/2) * Sum(WignerD(j2, mi1, mi + 1, 3*pi/2, pi/2, pi/2) * JyKet(j2, mi1), (mi1, -j2, j2)), (mi, -j2, j2))) assert qapply(TensorProduct(Jplus, 1)*TensorProduct(JzKet(j1, m1), JzKet(j2, m2))) == \ hbar*sqrt( j1**2 + j1 - m1**2 - m1)*TensorProduct(JzKet(j1, m1 + 1), JzKet(j2, m2)) assert qapply(TensorProduct(1, Jplus)*TensorProduct(JzKet(j1, m1), JzKet(j2, m2))) == \ hbar*sqrt( j2**2 + j2 - m2**2 - m2)*TensorProduct(JzKet(j1, m1), JzKet(j2, m2 + 1)) def test_jminus(): assert qapply(Jminus*JzKet(1, -1)) == 0 assert Jminus.matrix_element(1, 0, 1, 1) == sqrt(2)*hbar assert Jminus.rewrite('xyz') == Jx - I*Jy # Normal operators, normal states # Numerical assert qapply(Jminus*JxKet(1, 1)) == \ hbar*sqrt(2)*JxKet(1, 0)/2 + hbar*JxKet(1, 1) assert qapply(Jminus*JyKet(1, 1)) == \ hbar*sqrt(2)*JyKet(1, 0)/2 - hbar*I*JyKet(1, 1) assert qapply(Jminus*JzKet(1, 1)) == sqrt(2)*hbar*JzKet(1, 0) # Symbolic assert qapply(Jminus*JxKet(j, m)) == \ Sum(hbar*sqrt(j**2 + j - mi**2 + mi)*WignerD(j, mi, m, 0, pi/2, 0) * Sum(WignerD(j, mi1, mi - 1, 0, 3*pi/2, 0)*JxKet(j, mi1), (mi1, -j, j)), (mi, -j, j)) assert qapply(Jminus*JyKet(j, m)) == \ Sum(hbar*sqrt(j**2 + j - mi**2 + mi)*WignerD(j, mi, m, 3*pi/2, -pi/2, pi/2) * Sum(WignerD(j, mi1, mi - 1, 3*pi/2, pi/2, pi/2)*JyKet(j, mi1), (mi1, -j, j)), (mi, -j, j)) assert qapply(Jminus*JzKet(j, m)) == \ hbar*sqrt(j**2 + j - m**2 + m)*JzKet(j, m - 1) # Normal operators, coupled states # Numerical assert qapply(Jminus*JxKetCoupled(1, 1, (1, 1))) == \ hbar*sqrt(2)*JxKetCoupled(1, 0, (1, 1))/2 + \ hbar*JxKetCoupled(1, 1, (1, 1)) assert qapply(Jminus*JyKetCoupled(1, 1, (1, 1))) == \ hbar*sqrt(2)*JyKetCoupled(1, 0, (1, 1))/2 - \ hbar*I*JyKetCoupled(1, 1, (1, 1)) assert qapply(Jminus*JzKetCoupled(1, 1, (1, 1))) == \ sqrt(2)*hbar*JzKetCoupled(1, 0, (1, 1)) # Symbolic assert qapply(Jminus*JxKetCoupled(j, m, (j1, j2))) == \ Sum(hbar*sqrt(j**2 + j - mi**2 + mi)*WignerD(j, mi, m, 0, pi/2, 0) * Sum(WignerD(j, mi1, mi - 1, 0, 3*pi/2, 0)*JxKetCoupled(j, mi1, (j1, j2)), (mi1, -j, j)), (mi, -j, j)) assert qapply(Jminus*JyKetCoupled(j, m, (j1, j2))) == \ Sum(hbar*sqrt(j**2 + j - mi**2 + mi)*WignerD(j, mi, m, 3*pi/2, -pi/2, pi/2) * Sum( WignerD(j, mi1, mi - 1, 3*pi/2, pi/2, pi/2)* JyKetCoupled(j, mi1, (j1, j2)), (mi1, -j, j)), (mi, -j, j)) assert qapply(Jminus*JzKetCoupled(j, m, (j1, j2))) == \ hbar*sqrt(j**2 + j - m**2 + m)*JzKetCoupled(j, m - 1, (j1, j2)) # Uncoupled operators, uncoupled states # Numerical assert qapply(TensorProduct(Jminus, 1)*TensorProduct(JxKet(1, 1), JxKet(1, -1))) == \ hbar*sqrt(2)*TensorProduct(JxKet(1, 0), JxKet(1, -1))/2 + \ hbar*TensorProduct(JxKet(1, 1), JxKet(1, -1)) assert qapply(TensorProduct(1, Jminus)*TensorProduct(JxKet(1, 1), JxKet(1, -1))) == \ -hbar*TensorProduct(JxKet(1, 1), JxKet(1, -1)) - \ hbar*sqrt(2)*TensorProduct(JxKet(1, 1), JxKet(1, 0))/2 assert qapply(TensorProduct(Jminus, 1)*TensorProduct(JyKet(1, 1), JyKet(1, -1))) == \ hbar*sqrt(2)*TensorProduct(JyKet(1, 0), JyKet(1, -1))/2 - \ hbar*I*TensorProduct(JyKet(1, 1), JyKet(1, -1)) assert qapply(TensorProduct(1, Jminus)*TensorProduct(JyKet(1, 1), JyKet(1, -1))) == \ hbar*I*TensorProduct(JyKet(1, 1), JyKet(1, -1)) + \ hbar*sqrt(2)*TensorProduct(JyKet(1, 1), JyKet(1, 0))/2 assert qapply(TensorProduct(Jminus, 1)*TensorProduct(JzKet(1, 1), JzKet(1, -1))) == \ sqrt(2)*hbar*TensorProduct(JzKet(1, 0), JzKet(1, -1)) assert qapply(TensorProduct( 1, Jminus)*TensorProduct(JzKet(1, 1), JzKet(1, -1))) == 0 # Symbolic assert qapply(TensorProduct(Jminus, 1)*TensorProduct(JxKet(j1, m1), JxKet(j2, m2))) == \ TensorProduct(Sum(hbar*sqrt(j1**2 + j1 - mi**2 + mi)*WignerD(j1, mi, m1, 0, pi/2, 0) * Sum(WignerD(j1, mi1, mi - 1, 0, 3*pi/2, 0)*JxKet(j1, mi1), (mi1, -j1, j1)), (mi, -j1, j1)), JxKet(j2, m2)) assert qapply(TensorProduct(1, Jminus)*TensorProduct(JxKet(j1, m1), JxKet(j2, m2))) == \ TensorProduct(JxKet(j1, m1), Sum(hbar*sqrt(j2**2 + j2 - mi**2 + mi)*WignerD(j2, mi, m2, 0, pi/2, 0) * Sum(WignerD(j2, mi1, mi - 1, 0, 3*pi/2, 0)*JxKet(j2, mi1), (mi1, -j2, j2)), (mi, -j2, j2))) assert qapply(TensorProduct(Jminus, 1)*TensorProduct(JyKet(j1, m1), JyKet(j2, m2))) == \ TensorProduct(Sum(hbar*sqrt(j1**2 + j1 - mi**2 + mi)*WignerD(j1, mi, m1, 3*pi/2, -pi/2, pi/2) * Sum(WignerD(j1, mi1, mi - 1, 3*pi/2, pi/2, pi/2)*JyKet(j1, mi1), (mi1, -j1, j1)), (mi, -j1, j1)), JyKet(j2, m2)) assert qapply(TensorProduct(1, Jminus)*TensorProduct(JyKet(j1, m1), JyKet(j2, m2))) == \ TensorProduct(JyKet(j1, m1), Sum(hbar*sqrt(j2**2 + j2 - mi**2 + mi)*WignerD(j2, mi, m2, 3*pi/2, -pi/2, pi/2) * Sum(WignerD(j2, mi1, mi - 1, 3*pi/2, pi/2, pi/2)*JyKet(j2, mi1), (mi1, -j2, j2)), (mi, -j2, j2))) assert qapply(TensorProduct(Jminus, 1)*TensorProduct(JzKet(j1, m1), JzKet(j2, m2))) == \ hbar*sqrt( j1**2 + j1 - m1**2 + m1)*TensorProduct(JzKet(j1, m1 - 1), JzKet(j2, m2)) assert qapply(TensorProduct(1, Jminus)*TensorProduct(JzKet(j1, m1), JzKet(j2, m2))) == \ hbar*sqrt( j2**2 + j2 - m2**2 + m2)*TensorProduct(JzKet(j1, m1), JzKet(j2, m2 - 1)) def test_j2(): assert Commutator(J2, Jz).doit() == 0 assert J2.matrix_element(1, 1, 1, 1) == 2*hbar**2 # Normal operators, normal states # Numerical assert qapply(J2*JxKet(1, 1)) == 2*hbar**2*JxKet(1, 1) assert qapply(J2*JyKet(1, 1)) == 2*hbar**2*JyKet(1, 1) assert qapply(J2*JzKet(1, 1)) == 2*hbar**2*JzKet(1, 1) # Symbolic assert qapply(J2*JxKet(j, m)) == \ hbar**2*j**2*JxKet(j, m) + hbar**2*j*JxKet(j, m) assert qapply(J2*JyKet(j, m)) == \ hbar**2*j**2*JyKet(j, m) + hbar**2*j*JyKet(j, m) assert qapply(J2*JzKet(j, m)) == \ hbar**2*j**2*JzKet(j, m) + hbar**2*j*JzKet(j, m) # Normal operators, coupled states # Numerical assert qapply(J2*JxKetCoupled(1, 1, (1, 1))) == \ 2*hbar**2*JxKetCoupled(1, 1, (1, 1)) assert qapply(J2*JyKetCoupled(1, 1, (1, 1))) == \ 2*hbar**2*JyKetCoupled(1, 1, (1, 1)) assert qapply(J2*JzKetCoupled(1, 1, (1, 1))) == \ 2*hbar**2*JzKetCoupled(1, 1, (1, 1)) # Symbolic assert qapply(J2*JxKetCoupled(j, m, (j1, j2))) == \ hbar**2*j**2*JxKetCoupled(j, m, (j1, j2)) + \ hbar**2*j*JxKetCoupled(j, m, (j1, j2)) assert qapply(J2*JyKetCoupled(j, m, (j1, j2))) == \ hbar**2*j**2*JyKetCoupled(j, m, (j1, j2)) + \ hbar**2*j*JyKetCoupled(j, m, (j1, j2)) assert qapply(J2*JzKetCoupled(j, m, (j1, j2))) == \ hbar**2*j**2*JzKetCoupled(j, m, (j1, j2)) + \ hbar**2*j*JzKetCoupled(j, m, (j1, j2)) # Uncoupled operators, uncoupled states # Numerical assert qapply(TensorProduct(J2, 1)*TensorProduct(JxKet(1, 1), JxKet(1, -1))) == \ 2*hbar**2*TensorProduct(JxKet(1, 1), JxKet(1, -1)) assert qapply(TensorProduct(1, J2)*TensorProduct(JxKet(1, 1), JxKet(1, -1))) == \ 2*hbar**2*TensorProduct(JxKet(1, 1), JxKet(1, -1)) assert qapply(TensorProduct(J2, 1)*TensorProduct(JyKet(1, 1), JyKet(1, -1))) == \ 2*hbar**2*TensorProduct(JyKet(1, 1), JyKet(1, -1)) assert qapply(TensorProduct(1, J2)*TensorProduct(JyKet(1, 1), JyKet(1, -1))) == \ 2*hbar**2*TensorProduct(JyKet(1, 1), JyKet(1, -1)) assert qapply(TensorProduct(J2, 1)*TensorProduct(JzKet(1, 1), JzKet(1, -1))) == \ 2*hbar**2*TensorProduct(JzKet(1, 1), JzKet(1, -1)) assert qapply(TensorProduct(1, J2)*TensorProduct(JzKet(1, 1), JzKet(1, -1))) == \ 2*hbar**2*TensorProduct(JzKet(1, 1), JzKet(1, -1)) # Symbolic assert qapply(TensorProduct(J2, 1)*TensorProduct(JxKet(j1, m1), JxKet(j2, m2))) == \ hbar**2*j1**2*TensorProduct(JxKet(j1, m1), JxKet(j2, m2)) + \ hbar**2*j1*TensorProduct(JxKet(j1, m1), JxKet(j2, m2)) assert qapply(TensorProduct(1, J2)*TensorProduct(JxKet(j1, m1), JxKet(j2, m2))) == \ hbar**2*j2**2*TensorProduct(JxKet(j1, m1), JxKet(j2, m2)) + \ hbar**2*j2*TensorProduct(JxKet(j1, m1), JxKet(j2, m2)) assert qapply(TensorProduct(J2, 1)*TensorProduct(JyKet(j1, m1), JyKet(j2, m2))) == \ hbar**2*j1**2*TensorProduct(JyKet(j1, m1), JyKet(j2, m2)) + \ hbar**2*j1*TensorProduct(JyKet(j1, m1), JyKet(j2, m2)) assert qapply(TensorProduct(1, J2)*TensorProduct(JyKet(j1, m1), JyKet(j2, m2))) == \ hbar**2*j2**2*TensorProduct(JyKet(j1, m1), JyKet(j2, m2)) + \ hbar**2*j2*TensorProduct(JyKet(j1, m1), JyKet(j2, m2)) assert qapply(TensorProduct(J2, 1)*TensorProduct(JzKet(j1, m1), JzKet(j2, m2))) == \ hbar**2*j1**2*TensorProduct(JzKet(j1, m1), JzKet(j2, m2)) + \ hbar**2*j1*TensorProduct(JzKet(j1, m1), JzKet(j2, m2)) assert qapply(TensorProduct(1, J2)*TensorProduct(JzKet(j1, m1), JzKet(j2, m2))) == \ hbar**2*j2**2*TensorProduct(JzKet(j1, m1), JzKet(j2, m2)) + \ hbar**2*j2*TensorProduct(JzKet(j1, m1), JzKet(j2, m2)) def test_jx(): assert Commutator(Jx, Jz).doit() == -I*hbar*Jy assert Jx.rewrite('plusminus') == (Jminus + Jplus)/2 assert represent(Jx, basis=Jz, j=1) == ( represent(Jplus, basis=Jz, j=1) + represent(Jminus, basis=Jz, j=1))/2 # Normal operators, normal states # Numerical assert qapply(Jx*JxKet(1, 1)) == hbar*JxKet(1, 1) assert qapply(Jx*JyKet(1, 1)) == hbar*JyKet(1, 1) assert qapply(Jx*JzKet(1, 1)) == sqrt(2)*hbar*JzKet(1, 0)/2 # Symbolic assert qapply(Jx*JxKet(j, m)) == hbar*m*JxKet(j, m) assert qapply(Jx*JyKet(j, m)) == \ Sum(hbar*mi*WignerD(j, mi, m, 0, 0, pi/2)*Sum(WignerD(j, mi1, mi, 3*pi/2, 0, 0)*JyKet(j, mi1), (mi1, -j, j)), (mi, -j, j)) assert qapply(Jx*JzKet(j, m)) == \ hbar*sqrt(j**2 + j - m**2 - m)*JzKet(j, m + 1)/2 + hbar*sqrt(j**2 + j - m**2 + m)*JzKet(j, m - 1)/2 # Normal operators, coupled states # Numerical assert qapply(Jx*JxKetCoupled(1, 1, (1, 1))) == \ hbar*JxKetCoupled(1, 1, (1, 1)) assert qapply(Jx*JyKetCoupled(1, 1, (1, 1))) == \ hbar*JyKetCoupled(1, 1, (1, 1)) assert qapply(Jx*JzKetCoupled(1, 1, (1, 1))) == \ sqrt(2)*hbar*JzKetCoupled(1, 0, (1, 1))/2 # Symbolic assert qapply(Jx*JxKetCoupled(j, m, (j1, j2))) == \ hbar*m*JxKetCoupled(j, m, (j1, j2)) assert qapply(Jx*JyKetCoupled(j, m, (j1, j2))) == \ Sum(hbar*mi*WignerD(j, mi, m, 0, 0, pi/2)*Sum(WignerD(j, mi1, mi, 3*pi/2, 0, 0)*JyKetCoupled(j, mi1, (j1, j2)), (mi1, -j, j)), (mi, -j, j)) assert qapply(Jx*JzKetCoupled(j, m, (j1, j2))) == \ hbar*sqrt(j**2 + j - m**2 - m)*JzKetCoupled(j, m + 1, (j1, j2))/2 + \ hbar*sqrt(j**2 + j - m**2 + m)*JzKetCoupled(j, m - 1, (j1, j2))/2 # Normal operators, uncoupled states # Numerical assert qapply(Jx*TensorProduct(JxKet(1, 1), JxKet(1, 1))) == \ 2*hbar*TensorProduct(JxKet(1, 1), JxKet(1, 1)) assert qapply(Jx*TensorProduct(JyKet(1, 1), JyKet(1, 1))) == \ hbar*TensorProduct(JyKet(1, 1), JyKet(1, 1)) + \ hbar*TensorProduct(JyKet(1, 1), JyKet(1, 1)) assert qapply(Jx*TensorProduct(JzKet(1, 1), JzKet(1, 1))) == \ sqrt(2)*hbar*TensorProduct(JzKet(1, 1), JzKet(1, 0))/2 + \ sqrt(2)*hbar*TensorProduct(JzKet(1, 0), JzKet(1, 1))/2 assert qapply(Jx*TensorProduct(JxKet(1, 1), JxKet(1, -1))) == 0 # Symbolic assert qapply(Jx*TensorProduct(JxKet(j1, m1), JxKet(j2, m2))) == \ hbar*m1*TensorProduct(JxKet(j1, m1), JxKet(j2, m2)) + \ hbar*m2*TensorProduct(JxKet(j1, m1), JxKet(j2, m2)) assert qapply(Jx*TensorProduct(JyKet(j1, m1), JyKet(j2, m2))) == \ TensorProduct(Sum(hbar*mi*WignerD(j1, mi, m1, 0, 0, pi/2)*Sum(WignerD(j1, mi1, mi, 3*pi/2, 0, 0)*JyKet(j1, mi1), (mi1, -j1, j1)), (mi, -j1, j1)), JyKet(j2, m2)) + \ TensorProduct(JyKet(j1, m1), Sum(hbar*mi*WignerD(j2, mi, m2, 0, 0, pi/2)*Sum(WignerD(j2, mi1, mi, 3*pi/2, 0, 0)*JyKet(j2, mi1), (mi1, -j2, j2)), (mi, -j2, j2))) assert qapply(Jx*TensorProduct(JzKet(j1, m1), JzKet(j2, m2))) == \ hbar*sqrt(j1**2 + j1 - m1**2 - m1)*TensorProduct(JzKet(j1, m1 + 1), JzKet(j2, m2))/2 + \ hbar*sqrt(j1**2 + j1 - m1**2 + m1)*TensorProduct(JzKet(j1, m1 - 1), JzKet(j2, m2))/2 + \ hbar*sqrt(j2**2 + j2 - m2**2 - m2)*TensorProduct(JzKet(j1, m1), JzKet(j2, m2 + 1))/2 + \ hbar*sqrt( j2**2 + j2 - m2**2 + m2)*TensorProduct(JzKet(j1, m1), JzKet(j2, m2 - 1))/2 # Uncoupled operators, uncoupled states # Numerical assert qapply(TensorProduct(Jx, 1)*TensorProduct(JxKet(1, 1), JxKet(1, -1))) == \ hbar*TensorProduct(JxKet(1, 1), JxKet(1, -1)) assert qapply(TensorProduct(1, Jx)*TensorProduct(JxKet(1, 1), JxKet(1, -1))) == \ -hbar*TensorProduct(JxKet(1, 1), JxKet(1, -1)) assert qapply(TensorProduct(Jx, 1)*TensorProduct(JyKet(1, 1), JyKet(1, -1))) == \ hbar*TensorProduct(JyKet(1, 1), JyKet(1, -1)) assert qapply(TensorProduct(1, Jx)*TensorProduct(JyKet(1, 1), JyKet(1, -1))) == \ -hbar*TensorProduct(JyKet(1, 1), JyKet(1, -1)) assert qapply(TensorProduct(Jx, 1)*TensorProduct(JzKet(1, 1), JzKet(1, -1))) == \ hbar*sqrt(2)*TensorProduct(JzKet(1, 0), JzKet(1, -1))/2 assert qapply(TensorProduct(1, Jx)*TensorProduct(JzKet(1, 1), JzKet(1, -1))) == \ hbar*sqrt(2)*TensorProduct(JzKet(1, 1), JzKet(1, 0))/2 # Symbolic assert qapply(TensorProduct(Jx, 1)*TensorProduct(JxKet(j1, m1), JxKet(j2, m2))) == \ hbar*m1*TensorProduct(JxKet(j1, m1), JxKet(j2, m2)) assert qapply(TensorProduct(1, Jx)*TensorProduct(JxKet(j1, m1), JxKet(j2, m2))) == \ hbar*m2*TensorProduct(JxKet(j1, m1), JxKet(j2, m2)) assert qapply(TensorProduct(Jx, 1)*TensorProduct(JyKet(j1, m1), JyKet(j2, m2))) == \ TensorProduct(Sum(hbar*mi*WignerD(j1, mi, m1, 0, 0, pi/2) * Sum(WignerD(j1, mi1, mi, 3*pi/2, 0, 0)*JyKet(j1, mi1), (mi1, -j1, j1)), (mi, -j1, j1)), JyKet(j2, m2)) assert qapply(TensorProduct(1, Jx)*TensorProduct(JyKet(j1, m1), JyKet(j2, m2))) == \ TensorProduct(JyKet(j1, m1), Sum(hbar*mi*WignerD(j2, mi, m2, 0, 0, pi/2) * Sum(WignerD(j2, mi1, mi, 3*pi/2, 0, 0)*JyKet(j2, mi1), (mi1, -j2, j2)), (mi, -j2, j2))) assert qapply(TensorProduct(Jx, 1)*TensorProduct(JzKet(j1, m1), JzKet(j2, m2))) == \ hbar*sqrt(j1**2 + j1 - m1**2 - m1)*TensorProduct(JzKet(j1, m1 + 1), JzKet(j2, m2))/2 + \ hbar*sqrt( j1**2 + j1 - m1**2 + m1)*TensorProduct(JzKet(j1, m1 - 1), JzKet(j2, m2))/2 assert qapply(TensorProduct(1, Jx)*TensorProduct(JzKet(j1, m1), JzKet(j2, m2))) == \ hbar*sqrt(j2**2 + j2 - m2**2 - m2)*TensorProduct(JzKet(j1, m1), JzKet(j2, m2 + 1))/2 + \ hbar*sqrt( j2**2 + j2 - m2**2 + m2)*TensorProduct(JzKet(j1, m1), JzKet(j2, m2 - 1))/2 def test_jy(): assert Commutator(Jy, Jz).doit() == I*hbar*Jx assert Jy.rewrite('plusminus') == (Jplus - Jminus)/(2*I) assert represent(Jy, basis=Jz) == ( represent(Jplus, basis=Jz) - represent(Jminus, basis=Jz))/(2*I) # Normal operators, normal states # Numerical assert qapply(Jy*JxKet(1, 1)) == hbar*JxKet(1, 1) assert qapply(Jy*JyKet(1, 1)) == hbar*JyKet(1, 1) assert qapply(Jy*JzKet(1, 1)) == sqrt(2)*hbar*I*JzKet(1, 0)/2 # Symbolic assert qapply(Jy*JxKet(j, m)) == \ Sum(hbar*mi*WignerD(j, mi, m, 3*pi/2, 0, 0)*Sum(WignerD( j, mi1, mi, 0, 0, pi/2)*JxKet(j, mi1), (mi1, -j, j)), (mi, -j, j)) assert qapply(Jy*JyKet(j, m)) == hbar*m*JyKet(j, m) assert qapply(Jy*JzKet(j, m)) == \ -hbar*I*sqrt(j**2 + j - m**2 - m)*JzKet( j, m + 1)/2 + hbar*I*sqrt(j**2 + j - m**2 + m)*JzKet(j, m - 1)/2 # Normal operators, coupled states # Numerical assert qapply(Jy*JxKetCoupled(1, 1, (1, 1))) == \ hbar*JxKetCoupled(1, 1, (1, 1)) assert qapply(Jy*JyKetCoupled(1, 1, (1, 1))) == \ hbar*JyKetCoupled(1, 1, (1, 1)) assert qapply(Jy*JzKetCoupled(1, 1, (1, 1))) == \ sqrt(2)*hbar*I*JzKetCoupled(1, 0, (1, 1))/2 # Symbolic assert qapply(Jy*JxKetCoupled(j, m, (j1, j2))) == \ Sum(hbar*mi*WignerD(j, mi, m, 3*pi/2, 0, 0)*Sum(WignerD(j, mi1, mi, 0, 0, pi/2)*JxKetCoupled(j, mi1, (j1, j2)), (mi1, -j, j)), (mi, -j, j)) assert qapply(Jy*JyKetCoupled(j, m, (j1, j2))) == \ hbar*m*JyKetCoupled(j, m, (j1, j2)) assert qapply(Jy*JzKetCoupled(j, m, (j1, j2))) == \ -hbar*I*sqrt(j**2 + j - m**2 - m)*JzKetCoupled(j, m + 1, (j1, j2))/2 + \ hbar*I*sqrt(j**2 + j - m**2 + m)*JzKetCoupled(j, m - 1, (j1, j2))/2 # Normal operators, uncoupled states # Numerical assert qapply(Jy*TensorProduct(JxKet(1, 1), JxKet(1, 1))) == \ hbar*TensorProduct(JxKet(1, 1), JxKet(1, 1)) + \ hbar*TensorProduct(JxKet(1, 1), JxKet(1, 1)) assert qapply(Jy*TensorProduct(JyKet(1, 1), JyKet(1, 1))) == \ 2*hbar*TensorProduct(JyKet(1, 1), JyKet(1, 1)) assert qapply(Jy*TensorProduct(JzKet(1, 1), JzKet(1, 1))) == \ sqrt(2)*hbar*I*TensorProduct(JzKet(1, 1), JzKet(1, 0))/2 + \ sqrt(2)*hbar*I*TensorProduct(JzKet(1, 0), JzKet(1, 1))/2 assert qapply(Jy*TensorProduct(JyKet(1, 1), JyKet(1, -1))) == 0 # Symbolic assert qapply(Jy*TensorProduct(JxKet(j1, m1), JxKet(j2, m2))) == \ TensorProduct(JxKet(j1, m1), Sum(hbar*mi*WignerD(j2, mi, m2, 3*pi/2, 0, 0)*Sum(WignerD(j2, mi1, mi, 0, 0, pi/2)*JxKet(j2, mi1), (mi1, -j2, j2)), (mi, -j2, j2))) + \ TensorProduct(Sum(hbar*mi*WignerD(j1, mi, m1, 3*pi/2, 0, 0)*Sum(WignerD(j1, mi1, mi, 0, 0, pi/2)*JxKet(j1, mi1), (mi1, -j1, j1)), (mi, -j1, j1)), JxKet(j2, m2)) assert qapply(Jy*TensorProduct(JyKet(j1, m1), JyKet(j2, m2))) == \ hbar*m1*TensorProduct(JyKet(j1, m1), JyKet( j2, m2)) + hbar*m2*TensorProduct(JyKet(j1, m1), JyKet(j2, m2)) assert qapply(Jy*TensorProduct(JzKet(j1, m1), JzKet(j2, m2))) == \ -hbar*I*sqrt(j1**2 + j1 - m1**2 - m1)*TensorProduct(JzKet(j1, m1 + 1), JzKet(j2, m2))/2 + \ hbar*I*sqrt(j1**2 + j1 - m1**2 + m1)*TensorProduct(JzKet(j1, m1 - 1), JzKet(j2, m2))/2 + \ -hbar*I*sqrt(j2**2 + j2 - m2**2 - m2)*TensorProduct(JzKet(j1, m1), JzKet(j2, m2 + 1))/2 + \ hbar*I*sqrt( j2**2 + j2 - m2**2 + m2)*TensorProduct(JzKet(j1, m1), JzKet(j2, m2 - 1))/2 # Uncoupled operators, uncoupled states # Numerical assert qapply(TensorProduct(Jy, 1)*TensorProduct(JxKet(1, 1), JxKet(1, -1))) == \ hbar*TensorProduct(JxKet(1, 1), JxKet(1, -1)) assert qapply(TensorProduct(1, Jy)*TensorProduct(JxKet(1, 1), JxKet(1, -1))) == \ -hbar*TensorProduct(JxKet(1, 1), JxKet(1, -1)) assert qapply(TensorProduct(Jy, 1)*TensorProduct(JyKet(1, 1), JyKet(1, -1))) == \ hbar*TensorProduct(JyKet(1, 1), JyKet(1, -1)) assert qapply(TensorProduct(1, Jy)*TensorProduct(JyKet(1, 1), JyKet(1, -1))) == \ -hbar*TensorProduct(JyKet(1, 1), JyKet(1, -1)) assert qapply(TensorProduct(Jy, 1)*TensorProduct(JzKet(1, 1), JzKet(1, -1))) == \ hbar*sqrt(2)*I*TensorProduct(JzKet(1, 0), JzKet(1, -1))/2 assert qapply(TensorProduct(1, Jy)*TensorProduct(JzKet(1, 1), JzKet(1, -1))) == \ -hbar*sqrt(2)*I*TensorProduct(JzKet(1, 1), JzKet(1, 0))/2 # Symbolic assert qapply(TensorProduct(Jy, 1)*TensorProduct(JxKet(j1, m1), JxKet(j2, m2))) == \ TensorProduct(Sum(hbar*mi*WignerD(j1, mi, m1, 3*pi/2, 0, 0) * Sum(WignerD(j1, mi1, mi, 0, 0, pi/2)*JxKet(j1, mi1), (mi1, -j1, j1)), (mi, -j1, j1)), JxKet(j2, m2)) assert qapply(TensorProduct(1, Jy)*TensorProduct(JxKet(j1, m1), JxKet(j2, m2))) == \ TensorProduct(JxKet(j1, m1), Sum(hbar*mi*WignerD(j2, mi, m2, 3*pi/2, 0, 0) * Sum(WignerD(j2, mi1, mi, 0, 0, pi/2)*JxKet(j2, mi1), (mi1, -j2, j2)), (mi, -j2, j2))) assert qapply(TensorProduct(Jy, 1)*TensorProduct(JyKet(j1, m1), JyKet(j2, m2))) == \ hbar*m1*TensorProduct(JyKet(j1, m1), JyKet(j2, m2)) assert qapply(TensorProduct(1, Jy)*TensorProduct(JyKet(j1, m1), JyKet(j2, m2))) == \ hbar*m2*TensorProduct(JyKet(j1, m1), JyKet(j2, m2)) assert qapply(TensorProduct(Jy, 1)*TensorProduct(JzKet(j1, m1), JzKet(j2, m2))) == \ -hbar*I*sqrt(j1**2 + j1 - m1**2 - m1)*TensorProduct(JzKet(j1, m1 + 1), JzKet(j2, m2))/2 + \ hbar*I*sqrt( j1**2 + j1 - m1**2 + m1)*TensorProduct(JzKet(j1, m1 - 1), JzKet(j2, m2))/2 assert qapply(TensorProduct(1, Jy)*TensorProduct(JzKet(j1, m1), JzKet(j2, m2))) == \ -hbar*I*sqrt(j2**2 + j2 - m2**2 - m2)*TensorProduct(JzKet(j1, m1), JzKet(j2, m2 + 1))/2 + \ hbar*I*sqrt( j2**2 + j2 - m2**2 + m2)*TensorProduct(JzKet(j1, m1), JzKet(j2, m2 - 1))/2 def test_jz(): assert Commutator(Jz, Jminus).doit() == -hbar*Jminus # Normal operators, normal states # Numerical assert qapply(Jz*JxKet(1, 1)) == -sqrt(2)*hbar*JxKet(1, 0)/2 assert qapply(Jz*JyKet(1, 1)) == -sqrt(2)*hbar*I*JyKet(1, 0)/2 assert qapply(Jz*JzKet(2, 1)) == hbar*JzKet(2, 1) # Symbolic assert qapply(Jz*JxKet(j, m)) == \ Sum(hbar*mi*WignerD(j, mi, m, 0, pi/2, 0)*Sum(WignerD(j, mi1, mi, 0, 3*pi/2, 0)*JxKet(j, mi1), (mi1, -j, j)), (mi, -j, j)) assert qapply(Jz*JyKet(j, m)) == \ Sum(hbar*mi*WignerD(j, mi, m, 3*pi/2, -pi/2, pi/2)*Sum(WignerD(j, mi1, mi, 3*pi/2, pi/2, pi/2)*JyKet(j, mi1), (mi1, -j, j)), (mi, -j, j)) assert qapply(Jz*JzKet(j, m)) == hbar*m*JzKet(j, m) # Normal operators, coupled states # Numerical assert qapply(Jz*JxKetCoupled(1, 1, (1, 1))) == \ -sqrt(2)*hbar*JxKetCoupled(1, 0, (1, 1))/2 assert qapply(Jz*JyKetCoupled(1, 1, (1, 1))) == \ -sqrt(2)*hbar*I*JyKetCoupled(1, 0, (1, 1))/2 assert qapply(Jz*JzKetCoupled(1, 1, (1, 1))) == \ hbar*JzKetCoupled(1, 1, (1, 1)) # Symbolic assert qapply(Jz*JxKetCoupled(j, m, (j1, j2))) == \ Sum(hbar*mi*WignerD(j, mi, m, 0, pi/2, 0)*Sum(WignerD(j, mi1, mi, 0, 3*pi/2, 0)*JxKetCoupled(j, mi1, (j1, j2)), (mi1, -j, j)), (mi, -j, j)) assert qapply(Jz*JyKetCoupled(j, m, (j1, j2))) == \ Sum(hbar*mi*WignerD(j, mi, m, 3*pi/2, -pi/2, pi/2)*Sum(WignerD(j, mi1, mi, 3*pi/2, pi/2, pi/2)*JyKetCoupled(j, mi1, (j1, j2)), (mi1, -j, j)), (mi, -j, j)) assert qapply(Jz*JzKetCoupled(j, m, (j1, j2))) == \ hbar*m*JzKetCoupled(j, m, (j1, j2)) # Normal operators, uncoupled states # Numerical assert qapply(Jz*TensorProduct(JxKet(1, 1), JxKet(1, 1))) == \ -sqrt(2)*hbar*TensorProduct(JxKet(1, 1), JxKet(1, 0))/2 - \ sqrt(2)*hbar*TensorProduct(JxKet(1, 0), JxKet(1, 1))/2 assert qapply(Jz*TensorProduct(JyKet(1, 1), JyKet(1, 1))) == \ -sqrt(2)*hbar*I*TensorProduct(JyKet(1, 1), JyKet(1, 0))/2 - \ sqrt(2)*hbar*I*TensorProduct(JyKet(1, 0), JyKet(1, 1))/2 assert qapply(Jz*TensorProduct(JzKet(1, 1), JzKet(1, 1))) == \ 2*hbar*TensorProduct(JzKet(1, 1), JzKet(1, 1)) assert qapply(Jz*TensorProduct(JzKet(1, 1), JzKet(1, -1))) == 0 # Symbolic assert qapply(Jz*TensorProduct(JxKet(j1, m1), JxKet(j2, m2))) == \ TensorProduct(JxKet(j1, m1), Sum(hbar*mi*WignerD(j2, mi, m2, 0, pi/2, 0)*Sum(WignerD(j2, mi1, mi, 0, 3*pi/2, 0)*JxKet(j2, mi1), (mi1, -j2, j2)), (mi, -j2, j2))) + \ TensorProduct(Sum(hbar*mi*WignerD(j1, mi, m1, 0, pi/2, 0)*Sum(WignerD(j1, mi1, mi, 0, 3*pi/2, 0)*JxKet(j1, mi1), (mi1, -j1, j1)), (mi, -j1, j1)), JxKet(j2, m2)) assert qapply(Jz*TensorProduct(JyKet(j1, m1), JyKet(j2, m2))) == \ TensorProduct(JyKet(j1, m1), Sum(hbar*mi*WignerD(j2, mi, m2, 3*pi/2, -pi/2, pi/2)*Sum(WignerD(j2, mi1, mi, 3*pi/2, pi/2, pi/2)*JyKet(j2, mi1), (mi1, -j2, j2)), (mi, -j2, j2))) + \ TensorProduct(Sum(hbar*mi*WignerD(j1, mi, m1, 3*pi/2, -pi/2, pi/2)*Sum(WignerD(j1, mi1, mi, 3*pi/2, pi/2, pi/2)*JyKet(j1, mi1), (mi1, -j1, j1)), (mi, -j1, j1)), JyKet(j2, m2)) assert qapply(Jz*TensorProduct(JzKet(j1, m1), JzKet(j2, m2))) == \ hbar*m1*TensorProduct(JzKet(j1, m1), JzKet( j2, m2)) + hbar*m2*TensorProduct(JzKet(j1, m1), JzKet(j2, m2)) # Uncoupled Operators # Numerical assert qapply(TensorProduct(Jz, 1)*TensorProduct(JxKet(1, 1), JxKet(1, -1))) == \ -sqrt(2)*hbar*TensorProduct(JxKet(1, 0), JxKet(1, -1))/2 assert qapply(TensorProduct(1, Jz)*TensorProduct(JxKet(1, 1), JxKet(1, -1))) == \ -sqrt(2)*hbar*TensorProduct(JxKet(1, 1), JxKet(1, 0))/2 assert qapply(TensorProduct(Jz, 1)*TensorProduct(JyKet(1, 1), JyKet(1, -1))) == \ -sqrt(2)*I*hbar*TensorProduct(JyKet(1, 0), JyKet(1, -1))/2 assert qapply(TensorProduct(1, Jz)*TensorProduct(JyKet(1, 1), JyKet(1, -1))) == \ sqrt(2)*I*hbar*TensorProduct(JyKet(1, 1), JyKet(1, 0))/2 assert qapply(TensorProduct(Jz, 1)*TensorProduct(JzKet(1, 1), JzKet(1, -1))) == \ hbar*TensorProduct(JzKet(1, 1), JzKet(1, -1)) assert qapply(TensorProduct(1, Jz)*TensorProduct(JzKet(1, 1), JzKet(1, -1))) == \ -hbar*TensorProduct(JzKet(1, 1), JzKet(1, -1)) # Symbolic assert qapply(TensorProduct(Jz, 1)*TensorProduct(JxKet(j1, m1), JxKet(j2, m2))) == \ TensorProduct(Sum(hbar*mi*WignerD(j1, mi, m1, 0, pi/2, 0)*Sum(WignerD(j1, mi1, mi, 0, 3*pi/2, 0)*JxKet(j1, mi1), (mi1, -j1, j1)), (mi, -j1, j1)), JxKet(j2, m2)) assert qapply(TensorProduct(1, Jz)*TensorProduct(JxKet(j1, m1), JxKet(j2, m2))) == \ TensorProduct(JxKet(j1, m1), Sum(hbar*mi*WignerD(j2, mi, m2, 0, pi/2, 0)*Sum(WignerD(j2, mi1, mi, 0, 3*pi/2, 0)*JxKet(j2, mi1), (mi1, -j2, j2)), (mi, -j2, j2))) assert qapply(TensorProduct(Jz, 1)*TensorProduct(JyKet(j1, m1), JyKet(j2, m2))) == \ TensorProduct(Sum(hbar*mi*WignerD(j1, mi, m1, 3*pi/2, -pi/2, pi/2)*Sum(WignerD(j1, mi1, mi, 3*pi/2, pi/2, pi/2)*JyKet(j1, mi1), (mi1, -j1, j1)), (mi, -j1, j1)), JyKet(j2, m2)) assert qapply(TensorProduct(1, Jz)*TensorProduct(JyKet(j1, m1), JyKet(j2, m2))) == \ TensorProduct(JyKet(j1, m1), Sum(hbar*mi*WignerD(j2, mi, m2, 3*pi/2, -pi/2, pi/2)*Sum(WignerD(j2, mi1, mi, 3*pi/2, pi/2, pi/2)*JyKet(j2, mi1), (mi1, -j2, j2)), (mi, -j2, j2))) assert qapply(TensorProduct(Jz, 1)*TensorProduct(JzKet(j1, m1), JzKet(j2, m2))) == \ hbar*m1*TensorProduct(JzKet(j1, m1), JzKet(j2, m2)) assert qapply(TensorProduct(1, Jz)*TensorProduct(JzKet(j1, m1), JzKet(j2, m2))) == \ hbar*m2*TensorProduct(JzKet(j1, m1), JzKet(j2, m2)) def test_rotation(): a, b, g = symbols('a b g') j, m = symbols('j m') #Uncoupled answ = [JxKet(1,-1)/2 - sqrt(2)*JxKet(1,0)/2 + JxKet(1,1)/2 , JyKet(1,-1)/2 - sqrt(2)*JyKet(1,0)/2 + JyKet(1,1)/2 , JzKet(1,-1)/2 - sqrt(2)*JzKet(1,0)/2 + JzKet(1,1)/2] fun = [state(1, 1) for state in (JxKet, JyKet, JzKet)] for state in fun: got = qapply(Rotation(0, pi/2, 0)*state) assert got in answ answ.remove(got) assert not answ arg = Rotation(a, b, g)*fun[0] assert qapply(arg) == (-exp(-I*a)*exp(I*g)*cos(b)*JxKet(1,-1)/2 + exp(-I*a)*exp(I*g)*JxKet(1,-1)/2 - sqrt(2)*exp(-I*a)*sin(b)*JxKet(1,0)/2 + exp(-I*a)*exp(-I*g)*cos(b)*JxKet(1,1)/2 + exp(-I*a)*exp(-I*g)*JxKet(1,1)/2) #dummy effective assert str(qapply(Rotation(a, b, g)*JzKet(j, m), dummy=False)) == str( qapply(Rotation(a, b, g)*JzKet(j, m), dummy=True)).replace('_','') #Coupled ans = [JxKetCoupled(1,-1,(1,1))/2 - sqrt(2)*JxKetCoupled(1,0,(1,1))/2 + JxKetCoupled(1,1,(1,1))/2 , JyKetCoupled(1,-1,(1,1))/2 - sqrt(2)*JyKetCoupled(1,0,(1,1))/2 + JyKetCoupled(1,1,(1,1))/2 , JzKetCoupled(1,-1,(1,1))/2 - sqrt(2)*JzKetCoupled(1,0,(1,1))/2 + JzKetCoupled(1,1,(1,1))/2] fun = [state(1, 1, (1,1)) for state in (JxKetCoupled, JyKetCoupled, JzKetCoupled)] for state in fun: got = qapply(Rotation(0, pi/2, 0)*state) assert got in ans ans.remove(got) assert not ans arg = Rotation(a, b, g)*fun[0] assert qapply(arg) == ( -exp(-I*a)*exp(I*g)*cos(b)*JxKetCoupled(1,-1,(1,1))/2 + exp(-I*a)*exp(I*g)*JxKetCoupled(1,-1,(1,1))/2 - sqrt(2)*exp(-I*a)*sin(b)*JxKetCoupled(1,0,(1,1))/2 + exp(-I*a)*exp(-I*g)*cos(b)*JxKetCoupled(1,1,(1,1))/2 + exp(-I*a)*exp(-I*g)*JxKetCoupled(1,1,(1,1))/2) #dummy effective assert str(qapply(Rotation(a,b,g)*JzKetCoupled(j,m,(j1,j2)), dummy=False)) == str( qapply(Rotation(a,b,g)*JzKetCoupled(j,m,(j1,j2)), dummy=True)).replace('_','') def test_jzket(): j, m = symbols('j m') # j not integer or half integer raises(ValueError, lambda: JzKet(S(2)/3, -S(1)/3)) raises(ValueError, lambda: JzKet(S(2)/3, m)) # j < 0 raises(ValueError, lambda: JzKet(-1, 1)) raises(ValueError, lambda: JzKet(-1, m)) # m not integer or half integer raises(ValueError, lambda: JzKet(j, -S(1)/3)) # abs(m) > j raises(ValueError, lambda: JzKet(1, 2)) raises(ValueError, lambda: JzKet(1, -2)) # j-m not integer raises(ValueError, lambda: JzKet(1, S(1)/2)) def test_jzketcoupled(): j, m = symbols('j m') # j not integer or half integer raises(ValueError, lambda: JzKetCoupled(S(2)/3, -S(1)/3, (1,))) raises(ValueError, lambda: JzKetCoupled(S(2)/3, m, (1,))) # j < 0 raises(ValueError, lambda: JzKetCoupled(-1, 1, (1,))) raises(ValueError, lambda: JzKetCoupled(-1, m, (1,))) # m not integer or half integer raises(ValueError, lambda: JzKetCoupled(j, -S(1)/3, (1,))) # abs(m) > j raises(ValueError, lambda: JzKetCoupled(1, 2, (1,))) raises(ValueError, lambda: JzKetCoupled(1, -2, (1,))) # j-m not integer raises(ValueError, lambda: JzKetCoupled(1, S(1)/2, (1,))) # checks types on coupling scheme raises(TypeError, lambda: JzKetCoupled(1, 1, 1)) raises(TypeError, lambda: JzKetCoupled(1, 1, (1,), 1)) raises(TypeError, lambda: JzKetCoupled(1, 1, (1, 1), (1,))) raises(TypeError, lambda: JzKetCoupled(1, 1, (1, 1, 1), (1, 2, 1), (1, 3, 1))) # checks length of coupling terms raises(ValueError, lambda: JzKetCoupled(1, 1, (1,), ((1, 2, 1),))) raises(ValueError, lambda: JzKetCoupled(1, 1, (1, 1), ((1, 2),))) # all jn are integer or half-integer raises(ValueError, lambda: JzKetCoupled(1, 1, (S(1)/3, S(2)/3))) # indicies in coupling scheme must be integers raises(ValueError, lambda: JzKetCoupled(1, 1, (1, 1), ((S(1)/2, 1, 2),) )) raises(ValueError, lambda: JzKetCoupled(1, 1, (1, 1), ((1, S(1)/2, 2),) )) # indicies out of range raises(ValueError, lambda: JzKetCoupled(1, 1, (1, 1), ((0, 2, 1),) )) raises(ValueError, lambda: JzKetCoupled(1, 1, (1, 1), ((3, 2, 1),) )) raises(ValueError, lambda: JzKetCoupled(1, 1, (1, 1), ((1, 0, 1),) )) raises(ValueError, lambda: JzKetCoupled(1, 1, (1, 1), ((1, 3, 1),) )) # all j values in coupling scheme must by integer or half-integer raises(ValueError, lambda: JzKetCoupled(1, 1, (1, 1, 1), ((1, 2, S( 4)/3), (1, 3, 1)) )) # each coupling must satisfy |j1-j2| <= j3 <= j1+j2 raises(ValueError, lambda: JzKetCoupled(1, 1, (1, 5))) raises(ValueError, lambda: JzKetCoupled(5, 1, (1, 1))) # final j of coupling must be j of the state raises(ValueError, lambda: JzKetCoupled(1, 1, (1, 1), ((1, 2, 2),) )) sympy-0.7.4.1/sympy/physics/quantum/tests/test_qapply.py0000644000175000017500000000630612253362407023667 0ustar georgeskgeorgeskfrom sympy import I, Integer, sqrt, symbols, Matrix from sympy.physics.quantum.anticommutator import AntiCommutator from sympy.physics.quantum.commutator import Commutator from sympy.physics.quantum.constants import hbar from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.gate import H from sympy.physics.quantum.operator import Operator, UnitaryOperator from sympy.physics.quantum.qapply import qapply from sympy.physics.quantum.qubit import Qubit from sympy.physics.quantum.spin import Jx, Jy, Jz, Jplus, Jminus, J2, JzKet from sympy.physics.quantum.state import Ket from sympy.physics.quantum.density import Density from sympy.physics.quantum.qubit import Qubit from sympy.physics.quantum.gate import UGate j, jp, m, mp = symbols("j j' m m'") z = JzKet(1, 0) po = JzKet(1, 1) mo = JzKet(1, -1) A = Operator('A') class Foo(Operator): def _apply_operator_JzKet(self, ket, **options): return ket def test_basic(): assert qapply(Jz*po) == hbar*po assert qapply(Jx*z) == hbar*po/sqrt(2) + hbar*mo/sqrt(2) assert qapply((Jplus + Jminus)*z/sqrt(2)) == hbar*po + hbar*mo assert qapply(Jz*(po + mo)) == hbar*po - hbar*mo assert qapply(Jz*po + Jz*mo) == hbar*po - hbar*mo assert qapply(Jminus*Jminus*po) == 2*hbar**2*mo assert qapply(Jplus**2*mo) == 2*hbar**2*po assert qapply(Jplus**2*Jminus**2*po) == 4*hbar**4*po def test_extra(): extra = z.dual*A*z assert qapply(Jz*po*extra) == hbar*po*extra assert qapply(Jx*z*extra) == (hbar*po/sqrt(2) + hbar*mo/sqrt(2))*extra assert qapply( (Jplus + Jminus)*z/sqrt(2)*extra) == hbar*po*extra + hbar*mo*extra assert qapply(Jz*(po + mo)*extra) == hbar*po*extra - hbar*mo*extra assert qapply(Jz*po*extra + Jz*mo*extra) == hbar*po*extra - hbar*mo*extra assert qapply(Jminus*Jminus*po*extra) == 2*hbar**2*mo*extra assert qapply(Jplus**2*mo*extra) == 2*hbar**2*po*extra assert qapply(Jplus**2*Jminus**2*po*extra) == 4*hbar**4*po*extra def test_innerproduct(): assert qapply(po.dual*Jz*po, ip_doit=False) == hbar*(po.dual*po) assert qapply(po.dual*Jz*po) == hbar def test_zero(): assert qapply(0) == 0 assert qapply(Integer(0)) == 0 def test_commutator(): assert qapply(Commutator(Jx, Jy)*Jz*po) == I*hbar**3*po assert qapply(Commutator(J2, Jz)*Jz*po) == 0 assert qapply(Commutator(Jz, Foo('F'))*po) == 0 assert qapply(Commutator(Foo('F'), Jz)*po) == 0 def test_anticommutator(): assert qapply(AntiCommutator(Jz, Foo('F'))*po) == 2*hbar*po assert qapply(AntiCommutator(Foo('F'), Jz)*po) == 2*hbar*po def test_outerproduct(): e = Jz*(mo*po.dual)*Jz*po assert qapply(e) == -hbar**2*mo assert qapply(e, ip_doit=False) == -hbar**2*(po.dual*po)*mo assert qapply(e).doit() == -hbar**2*mo def test_dagger(): lhs = Dagger(Qubit(0))*Dagger(H(0)) rhs = Dagger(Qubit(1))/sqrt(2) + Dagger(Qubit(0))/sqrt(2) assert qapply(lhs, dagger=True) == rhs def test_issue2974(): x, y = symbols('x y', commutative=False) A = Ket(x, y) B = Operator('B') assert qapply(A) == A assert qapply(A.dual*B) == A.dual*B def test_density(): d = Density([Jz*mo, 0.5], [Jz*po, 0.5]) assert qapply(d) == Density([-hbar*mo, 0.5], [hbar*po, 0.5]) sympy-0.7.4.1/sympy/physics/quantum/tests/test_operatorset.py0000644000175000017500000000517512253362407024733 0ustar georgeskgeorgeskfrom sympy.physics.quantum.operatorset import ( operators_to_state, state_to_operators ) from sympy.physics.quantum.cartesian import ( XOp, XKet, PxOp, PxKet, XBra, PxBra ) from sympy.physics.quantum.state import Ket, Bra from sympy.physics.quantum.operator import Operator from sympy.physics.quantum.spin import ( JxKet, JyKet, JzKet, JxBra, JyBra, JzBra, JxOp, JyOp, JzOp, J2Op ) from sympy.utilities.pytest import raises from sympy.utilities.pytest import XFAIL @XFAIL def test_spin(): assert operators_to_state(set([J2Op, JxOp])) == JxKet() assert operators_to_state(set([J2Op, JyOp])) == JyKet() assert operators_to_state(set([J2Op, JzOp])) == JzKet() assert operators_to_state(set([J2Op(), JxOp()])) == JxKet() assert operators_to_state(set([J2Op(), JyOp()])) == JyKet() assert operators_to_state(set([J2Op(), JzOp()])) == JzKet() assert state_to_operators(JxKet) == set([J2Op(), JxOp()]) assert state_to_operators(JyKet) == set([J2Op(), JyOp()]) assert state_to_operators(JzKet) == set([J2Op(), JzOp()]) assert state_to_operators(JxBra) == set([J2Op(), JxOp()]) assert state_to_operators(JyBra) == set([J2Op(), JyOp()]) assert state_to_operators(JzBra) == set([J2Op(), JzOp()]) assert state_to_operators(JxKet()) == set([J2Op(), JxOp()]) assert state_to_operators(JyKet()) == set([J2Op(), JyOp()]) assert state_to_operators(JzKet()) == set([J2Op(), JzOp()]) assert state_to_operators(JxBra()) == set([J2Op(), JxOp()]) assert state_to_operators(JyBra()) == set([J2Op(), JyOp()]) assert state_to_operators(JzBra()) == set([J2Op(), JzOp()]) def test_op_to_state(): assert operators_to_state(XOp) == XKet() assert operators_to_state(PxOp) == PxKet() assert operators_to_state(Operator) == Ket() assert state_to_operators(operators_to_state(XOp("Q"))) == XOp("Q") assert state_to_operators(operators_to_state(XOp())) == XOp() raises(NotImplementedError, lambda: operators_to_state(XKet)) def test_state_to_op(): assert state_to_operators(XKet) == XOp() assert state_to_operators(PxKet) == PxOp() assert state_to_operators(XBra) == XOp() assert state_to_operators(PxBra) == PxOp() assert state_to_operators(Ket) == Operator() assert state_to_operators(Bra) == Operator() assert operators_to_state(state_to_operators(XKet("test"))) == XKet("test") assert operators_to_state(state_to_operators(XBra("test"))) == XKet("test") assert operators_to_state(state_to_operators(XKet())) == XKet() assert operators_to_state(state_to_operators(XBra())) == XKet() raises(NotImplementedError, lambda: state_to_operators(XOp)) sympy-0.7.4.1/sympy/physics/quantum/tests/test_tensorproduct.py0000644000175000017500000000725012253362407025273 0ustar georgeskgeorgeskfrom sympy import I, symbols, Matrix from sympy.physics.quantum.commutator import Commutator as Comm from sympy.physics.quantum.tensorproduct import TensorProduct from sympy.physics.quantum.tensorproduct import TensorProduct as TP from sympy.physics.quantum.tensorproduct import tensor_product_simp from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.qubit import Qubit, QubitBra from sympy.physics.quantum.operator import OuterProduct from sympy.physics.quantum.density import Density from sympy.core.trace import Tr A, B, C = symbols('A,B,C', commutative=False) x = symbols('x') mat1 = Matrix([[1, 2*I], [1 + I, 3]]) mat2 = Matrix([[2*I, 3], [4*I, 2]]) def test_tensor_product_dagger(): assert Dagger(TensorProduct(I*A, B)) == \ -I*TensorProduct(Dagger(A), Dagger(B)) assert Dagger(TensorProduct(mat1, mat2)) == \ TensorProduct(Dagger(mat1), Dagger(mat2)) def test_tensor_product_abstract(): assert TP(x*A, 2*B) == x*2*TP(A, B) assert TP(A, B) != TP(B, A) assert TP(A, B).is_commutative is False assert isinstance(TP(A, B), TP) assert TP(A, B).subs(A, C) == TP(C, B) def test_tensor_product_expand(): assert TP(A + B, B + C).expand(tensorproduct=True) == \ TP(A, B) + TP(A, C) + TP(B, B) + TP(B, C) def test_tensor_product_commutator(): assert TP(Comm(A, B), C).doit().expand(tensorproduct=True) == \ TP(A*B, C) - TP(B*A, C) assert Comm(TP(A, B), TP(B, C)).doit() == \ TP(A, B)*TP(B, C) - TP(B, C)*TP(A, B) def test_tensor_product_simp(): assert tensor_product_simp(TP(A, B)*TP(B, C)) == TP(A*B, B*C) def test_issue_2824(): # most of the issue regarding sympification of args has been handled # and is tested internally by the use of args_cnc through the quantum # module, but the following is a test from the issue that used to raise. assert TensorProduct(1, Qubit('1')*Qubit('1').dual) == \ TensorProduct(1, OuterProduct(Qubit(1), QubitBra(1))) def test_eval_trace(): # This test includes tests with dependencies between TensorProducts #and density operators. Since, the test is more to test the behavior of #TensorProducts it remains here A, B, C, D, E, F = symbols('A B C D E F', commutative=False) # Density with simple tensor products as args t = TensorProduct(A, B) d = Density([t, 1.0]) tr = Tr(d) assert tr.doit() == 1.0*Tr(A*Dagger(A))*Tr(B*Dagger(B)) ## partial trace with simple tensor products as args t = TensorProduct(A, B, C) d = Density([t, 1.0]) tr = Tr(d, [1]) assert tr.doit() == 1.0*A*Dagger(A)*Tr(B*Dagger(B))*C*Dagger(C) tr = Tr(d, [0, 2]) assert tr.doit() == 1.0*Tr(A*Dagger(A))*B*Dagger(B)*Tr(C*Dagger(C)) # Density with multiple Tensorproducts as states t2 = TensorProduct(A, B) t3 = TensorProduct(C, D) d = Density([t2, 0.5], [t3, 0.5]) t = Tr(d) assert t.doit() == (0.5*Tr(A*Dagger(A))*Tr(B*Dagger(B)) + 0.5*Tr(C*Dagger(C))*Tr(D*Dagger(D))) t = Tr(d, [0]) assert t.doit() == (0.5*Tr(A*Dagger(A))*B*Dagger(B) + 0.5*Tr(C*Dagger(C))*D*Dagger(D)) #Density with mixed states d = Density([t2 + t3, 1.0]) t = Tr(d) assert t.doit() == ( 1.0*Tr(A*Dagger(A))*Tr(B*Dagger(B)) + 1.0*Tr(A*Dagger(C))*Tr(B*Dagger(D)) + 1.0*Tr(C*Dagger(A))*Tr(D*Dagger(B)) + 1.0*Tr(C*Dagger(C))*Tr(D*Dagger(D))) t = Tr(d, [1] ) assert t.doit() == ( 1.0*A*Dagger(A)*Tr(B*Dagger(B)) + 1.0*A*Dagger(C)*Tr(B*Dagger(D)) + 1.0*C*Dagger(A)*Tr(D*Dagger(B)) + 1.0*C*Dagger(C)*Tr(D*Dagger(D))) sympy-0.7.4.1/sympy/physics/quantum/tests/test_dagger.py0000644000175000017500000000275012253362407023611 0ustar georgeskgeorgeskfrom sympy import I, Matrix, symbols, conjugate, Expr, Integer from sympy.physics.quantum.dagger import adjoint, Dagger from sympy.external import import_module from sympy.utilities.pytest import skip def test_scalars(): x = symbols('x', complex=True) assert Dagger(x) == conjugate(x) assert Dagger(I*x) == -I*conjugate(x) i = symbols('i', real=True) assert Dagger(i) == i p = symbols('p') assert isinstance(Dagger(p), adjoint) i = Integer(3) assert Dagger(i) == i A = symbols('A', commutative=False) assert Dagger(A).is_commutative is False def test_matrix(): x = symbols('x') m = Matrix([[I, x*I], [2, 4]]) assert Dagger(m) == m.H class Foo(Expr): def _eval_adjoint(self): return I def test_eval_adjoint(): f = Foo() d = Dagger(f) assert d == I np = import_module('numpy') def test_numpy_dagger(): if not np: skip("numpy not installed.") a = np.matrix([[1.0, 2.0j], [-1.0j, 2.0]]) adag = a.copy().transpose().conjugate() assert (Dagger(a) == adag).all() scipy = import_module('scipy', __import__kwargs={'fromlist': ['sparse']}) def test_scipy_sparse_dagger(): if not np: skip("numpy not installed.") if not scipy: skip("scipy not installed.") else: sparse = scipy.sparse a = sparse.csr_matrix([[1.0 + 0.0j, 2.0j], [-1.0j, 2.0 + 0.0j]]) adag = a.copy().transpose().conjugate() assert np.linalg.norm((Dagger(a) - adag).todense()) == 0.0 sympy-0.7.4.1/sympy/physics/quantum/tests/test_sho1d.py0000644000175000017500000001077112253362407023400 0ustar georgeskgeorgesk"""Tests for sho1d.py""" from sympy import Integer, Symbol, sqrt, I, S from sympy.physics.quantum import Dagger from sympy.physics.quantum.constants import hbar from sympy.physics.quantum import Commutator from sympy.physics.quantum.qapply import qapply from sympy.physics.quantum.innerproduct import InnerProduct from sympy.physics.quantum.cartesian import X, Px from sympy.functions.special.tensor_functions import KroneckerDelta from sympy.physics.quantum.hilbert import ComplexSpace from sympy.physics.quantum.represent import represent from sympy.external import import_module from sympy.utilities.pytest import skip from sympy.physics.quantum.sho1d import (RaisingOp, LoweringOp, SHOKet, SHOBra, Hamiltonian, NumberOp) ad = RaisingOp('a') a = LoweringOp('a') k = SHOKet('k') kz = SHOKet(0) kf = SHOKet(1) k3 = SHOKet(3) b = SHOBra('b') b3 = SHOBra(3) H = Hamiltonian('H') N = NumberOp('N') omega = Symbol('omega') m = Symbol('m') ndim = Integer(4) np = import_module('numpy') scipy = import_module('scipy', __import__kwargs={'fromlist': ['sparse']}) ad_rep_sympy = represent(ad, basis=N, ndim=4, format='sympy') a_rep = represent(a, basis=N, ndim=4, format='sympy') N_rep = represent(N, basis=N, ndim=4, format='sympy') H_rep = represent(H, basis=N, ndim=4, format='sympy') k3_rep = represent(k3, basis=N, ndim=4, format='sympy') b3_rep = represent(b3, basis=N, ndim=4, format='sympy') def test_RaisingOp(): assert Dagger(ad) == a assert Commutator(ad, a).doit() == Integer(-1) assert Commutator(ad, N).doit() == Integer(-1)*ad assert qapply(ad*k) == (sqrt(k.n + 1)*SHOKet(k.n + 1)).expand() assert qapply(ad*kz) == (sqrt(kz.n + 1)*SHOKet(kz.n + 1)).expand() assert qapply(ad*kf) == (sqrt(kf.n + 1)*SHOKet(kf.n + 1)).expand() assert ad.rewrite('xp').doit() == \ (Integer(1)/sqrt(Integer(2)*hbar*m*omega))*(Integer(-1)*I*Px + m*omega*X) assert ad.hilbert_space == ComplexSpace(S.Infinity) for i in range(ndim - 1): assert ad_rep_sympy[i + 1,i] == sqrt(i + 1) if not np: skip("numpy not installed.") ad_rep_numpy = represent(ad, basis=N, ndim=4, format='numpy') for i in range(ndim - 1): assert ad_rep_numpy[i + 1,i] == float(sqrt(i + 1)) if not np: skip("numpy not installed.") if not scipy: skip("scipy not installed.") else: sparse = scipy.sparse ad_rep_scipy = represent(ad, basis=N, ndim=4, format='scipy.sparse', spmatrix='lil') for i in range(ndim - 1): assert ad_rep_scipy[i + 1,i] == float(sqrt(i + 1)) assert ad_rep_numpy.dtype == 'float64' assert ad_rep_scipy.dtype == 'float64' def test_LoweringOp(): assert Dagger(a) == ad assert Commutator(a, ad).doit() == Integer(1) assert Commutator(a, N).doit() == a assert qapply(a*k) == (sqrt(k.n)*SHOKet(k.n-Integer(1))).expand() assert qapply(a*kz) == Integer(0) assert qapply(a*kf) == (sqrt(kf.n)*SHOKet(kf.n-Integer(1))).expand() assert a.rewrite('xp').doit() == \ (Integer(1)/sqrt(Integer(2)*hbar*m*omega))*(I*Px + m*omega*X) for i in range(ndim - 1): assert a_rep[i,i + 1] == sqrt(i + 1) def test_NumberOp(): assert Commutator(N, ad).doit() == ad assert Commutator(N, a).doit() == Integer(-1)*a assert Commutator(N, H).doit() == Integer(0) assert qapply(N*k) == (k.n*k).expand() assert N.rewrite('a').doit() == ad*a assert N.rewrite('xp').doit() == (Integer(1)/(Integer(2)*m*hbar*omega))*( Px**2 + (m*omega*X)**2) - Integer(1)/Integer(2) assert N.rewrite('H').doit() == H/(hbar*omega) - Integer(1)/Integer(2) for i in range(ndim): assert N_rep[i,i] == i assert N_rep == ad_rep_sympy*a_rep def test_Hamiltonian(): assert Commutator(H, N).doit() == Integer(0) assert qapply(H*k) == ((hbar*omega*(k.n + Integer(1)/Integer(2)))*k).expand() assert H.rewrite('a').doit() == hbar*omega*(ad*a + Integer(1)/Integer(2)) assert H.rewrite('xp').doit() == \ (Integer(1)/(Integer(2)*m))*(Px**2 + (m*omega*X)**2) assert H.rewrite('N').doit() == hbar*omega*(N + Integer(1)/Integer(2)) for i in range(ndim): assert H_rep[i,i] == hbar*omega*(i + Integer(1)/Integer(2)) def test_SHOKet(): assert SHOKet('k').dual_class() == SHOBra assert SHOBra('b').dual_class() == SHOKet assert InnerProduct(b,k).doit() == KroneckerDelta(k.n, b.n) assert k.hilbert_space == ComplexSpace(S.Infinity) assert k3_rep[k3.n, 0] == Integer(1) assert b3_rep[0, b3.n] == Integer(1) sympy-0.7.4.1/sympy/physics/quantum/tests/test_cg.py0000644000175000017500000002050212253362407022744 0ustar georgeskgeorgeskfrom __future__ import division from sympy import S, sqrt, Sum, symbols from sympy.physics.quantum.cg import Wigner3j, Wigner6j, Wigner9j, CG, cg_simp from sympy.functions.special.tensor_functions import KroneckerDelta def test_cg_simp_add(): j, m1, m1p, m2, m2p = symbols('j m1 m1p m2 m2p') # Test Varshalovich 8.7.1 Eq 1 a = CG(S(1)/2, S(1)/2, 0, 0, S(1)/2, S(1)/2) b = CG(S(1)/2, -S(1)/2, 0, 0, S(1)/2, -S(1)/2) c = CG(1, 1, 0, 0, 1, 1) d = CG(1, 0, 0, 0, 1, 0) e = CG(1, -1, 0, 0, 1, -1) assert cg_simp(a + b) == 2 assert cg_simp(c + d + e) == 3 assert cg_simp(a + b + c + d + e) == 5 assert cg_simp(a + b + c) == 2 + c assert cg_simp(2*a + b) == 2 + a assert cg_simp(2*c + d + e) == 3 + c assert cg_simp(5*a + 5*b) == 10 assert cg_simp(5*c + 5*d + 5*e) == 15 assert cg_simp(-a - b) == -2 assert cg_simp(-c - d - e) == -3 assert cg_simp(-6*a - 6*b) == -12 assert cg_simp(-4*c - 4*d - 4*e) == -12 a = CG(S(1)/2, S(1)/2, j, 0, S(1)/2, S(1)/2) b = CG(S(1)/2, -S(1)/2, j, 0, S(1)/2, -S(1)/2) c = CG(1, 1, j, 0, 1, 1) d = CG(1, 0, j, 0, 1, 0) e = CG(1, -1, j, 0, 1, -1) assert cg_simp(a + b) == 2*KroneckerDelta(j, 0) assert cg_simp(c + d + e) == 3*KroneckerDelta(j, 0) assert cg_simp(a + b + c + d + e) == 5*KroneckerDelta(j, 0) assert cg_simp(a + b + c) == 2*KroneckerDelta(j, 0) + c assert cg_simp(2*a + b) == 2*KroneckerDelta(j, 0) + a assert cg_simp(2*c + d + e) == 3*KroneckerDelta(j, 0) + c assert cg_simp(5*a + 5*b) == 10*KroneckerDelta(j, 0) assert cg_simp(5*c + 5*d + 5*e) == 15*KroneckerDelta(j, 0) assert cg_simp(-a - b) == -2*KroneckerDelta(j, 0) assert cg_simp(-c - d - e) == -3*KroneckerDelta(j, 0) assert cg_simp(-6*a - 6*b) == -12*KroneckerDelta(j, 0) assert cg_simp(-4*c - 4*d - 4*e) == -12*KroneckerDelta(j, 0) # Test Varshalovich 8.7.1 Eq 2 a = CG(S(1)/2, S(1)/2, S(1)/2, -S(1)/2, 0, 0) b = CG(S(1)/2, -S(1)/2, S(1)/2, S(1)/2, 0, 0) c = CG(1, 1, 1, -1, 0, 0) d = CG(1, 0, 1, 0, 0, 0) e = CG(1, -1, 1, 1, 0, 0) assert cg_simp(a - b) == sqrt(2) assert cg_simp(c - d + e) == sqrt(3) assert cg_simp(a - b + c - d + e) == sqrt(2) + sqrt(3) assert cg_simp(a - b + c) == sqrt(2) + c assert cg_simp(2*a - b) == sqrt(2) + a assert cg_simp(2*c - d + e) == sqrt(3) + c assert cg_simp(5*a - 5*b) == 5*sqrt(2) assert cg_simp(5*c - 5*d + 5*e) == 5*sqrt(3) assert cg_simp(-a + b) == -sqrt(2) assert cg_simp(-c + d - e) == -sqrt(3) assert cg_simp(-6*a + 6*b) == -6*sqrt(2) assert cg_simp(-4*c + 4*d - 4*e) == -4*sqrt(3) a = CG(S(1)/2, S(1)/2, S(1)/2, -S(1)/2, j, 0) b = CG(S(1)/2, -S(1)/2, S(1)/2, S(1)/2, j, 0) c = CG(1, 1, 1, -1, j, 0) d = CG(1, 0, 1, 0, j, 0) e = CG(1, -1, 1, 1, j, 0) assert cg_simp(a - b) == sqrt(2)*KroneckerDelta(j, 0) assert cg_simp(c - d + e) == sqrt(3)*KroneckerDelta(j, 0) assert cg_simp(a - b + c - d + e) == sqrt( 2)*KroneckerDelta(j, 0) + sqrt(3)*KroneckerDelta(j, 0) assert cg_simp(a - b + c) == sqrt(2)*KroneckerDelta(j, 0) + c assert cg_simp(2*a - b) == sqrt(2)*KroneckerDelta(j, 0) + a assert cg_simp(2*c - d + e) == sqrt(3)*KroneckerDelta(j, 0) + c assert cg_simp(5*a - 5*b) == 5*sqrt(2)*KroneckerDelta(j, 0) assert cg_simp(5*c - 5*d + 5*e) == 5*sqrt(3)*KroneckerDelta(j, 0) assert cg_simp(-a + b) == -sqrt(2)*KroneckerDelta(j, 0) assert cg_simp(-c + d - e) == -sqrt(3)*KroneckerDelta(j, 0) assert cg_simp(-6*a + 6*b) == -6*sqrt(2)*KroneckerDelta(j, 0) assert cg_simp(-4*c + 4*d - 4*e) == -4*sqrt(3)*KroneckerDelta(j, 0) # Test Varshalovich 8.7.2 Eq 9 # alpha=alphap,beta=betap case # numerical a = CG(S(1)/2, S(1)/2, S(1)/2, -S(1)/2, 1, 0)**2 b = CG(S(1)/2, S(1)/2, S(1)/2, -S(1)/2, 0, 0)**2 c = CG(1, 0, 1, 1, 1, 1)**2 d = CG(1, 0, 1, 1, 2, 1)**2 assert cg_simp(a + b) == 1 assert cg_simp(c + d) == 1 assert cg_simp(a + b + c + d) == 2 assert cg_simp(4*a + 4*b) == 4 assert cg_simp(4*c + 4*d) == 4 assert cg_simp(5*a + 3*b) == 3 + 2*a assert cg_simp(5*c + 3*d) == 3 + 2*c assert cg_simp(-a - b) == -1 assert cg_simp(-c - d) == -1 # symbolic a = CG(S(1)/2, m1, S(1)/2, m2, 1, 1)**2 b = CG(S(1)/2, m1, S(1)/2, m2, 1, 0)**2 c = CG(S(1)/2, m1, S(1)/2, m2, 1, -1)**2 d = CG(S(1)/2, m1, S(1)/2, m2, 0, 0)**2 assert cg_simp(a + b + c + d) == 1 assert cg_simp(4*a + 4*b + 4*c + 4*d) == 4 assert cg_simp(3*a + 5*b + 3*c + 4*d) == 3 + 2*b + d assert cg_simp(-a - b - c - d) == -1 a = CG(1, m1, 1, m2, 2, 2)**2 b = CG(1, m1, 1, m2, 2, 1)**2 c = CG(1, m1, 1, m2, 2, 0)**2 d = CG(1, m1, 1, m2, 2, -1)**2 e = CG(1, m1, 1, m2, 2, -2)**2 f = CG(1, m1, 1, m2, 1, 1)**2 g = CG(1, m1, 1, m2, 1, 0)**2 h = CG(1, m1, 1, m2, 1, -1)**2 i = CG(1, m1, 1, m2, 0, 0)**2 assert cg_simp(a + b + c + d + e + f + g + h + i) == 1 assert cg_simp(4*(a + b + c + d + e + f + g + h + i)) == 4 assert cg_simp(a + b + 2*c + d + 4*e + f + g + h + i) == 1 + c + 3*e assert cg_simp(-a - b - c - d - e - f - g - h - i) == -1 # alpha!=alphap or beta!=betap case # numerical a = CG(S(1)/2, S( 1)/2, S(1)/2, -S(1)/2, 1, 0)*CG(S(1)/2, -S(1)/2, S(1)/2, S(1)/2, 1, 0) b = CG(S(1)/2, S( 1)/2, S(1)/2, -S(1)/2, 0, 0)*CG(S(1)/2, -S(1)/2, S(1)/2, S(1)/2, 0, 0) c = CG(1, 1, 1, 0, 2, 1)*CG(1, 0, 1, 1, 2, 1) d = CG(1, 1, 1, 0, 1, 1)*CG(1, 0, 1, 1, 1, 1) assert cg_simp(a + b) == 0 assert cg_simp(c + d) == 0 # symbolic a = CG(S(1)/2, m1, S(1)/2, m2, 1, 1)*CG(S(1)/2, m1p, S(1)/2, m2p, 1, 1) b = CG(S(1)/2, m1, S(1)/2, m2, 1, 0)*CG(S(1)/2, m1p, S(1)/2, m2p, 1, 0) c = CG(S(1)/2, m1, S(1)/2, m2, 1, -1)*CG(S(1)/2, m1p, S(1)/2, m2p, 1, -1) d = CG(S(1)/2, m1, S(1)/2, m2, 0, 0)*CG(S(1)/2, m1p, S(1)/2, m2p, 0, 0) assert cg_simp(a + b + c + d) == KroneckerDelta(m1, m1p)*KroneckerDelta(m2, m2p) a = CG(1, m1, 1, m2, 2, 2)*CG(1, m1p, 1, m2p, 2, 2) b = CG(1, m1, 1, m2, 2, 1)*CG(1, m1p, 1, m2p, 2, 1) c = CG(1, m1, 1, m2, 2, 0)*CG(1, m1p, 1, m2p, 2, 0) d = CG(1, m1, 1, m2, 2, -1)*CG(1, m1p, 1, m2p, 2, -1) e = CG(1, m1, 1, m2, 2, -2)*CG(1, m1p, 1, m2p, 2, -2) f = CG(1, m1, 1, m2, 1, 1)*CG(1, m1p, 1, m2p, 1, 1) g = CG(1, m1, 1, m2, 1, 0)*CG(1, m1p, 1, m2p, 1, 0) h = CG(1, m1, 1, m2, 1, -1)*CG(1, m1p, 1, m2p, 1, -1) i = CG(1, m1, 1, m2, 0, 0)*CG(1, m1p, 1, m2p, 0, 0) assert cg_simp( a + b + c + d + e + f + g + h + i) == KroneckerDelta(m1, m1p)*KroneckerDelta(m2, m2p) def test_cg_simp_sum(): x, a, b, c, cp, alpha, beta, gamma, gammap = symbols( 'x a b c cp alpha beta gamma gammap') # Varshalovich 8.7.1 Eq 1 assert cg_simp(x * Sum(CG(a, alpha, b, 0, a, alpha), (alpha, -a, a) )) == x*(2*a + 1)*KroneckerDelta(b, 0) assert cg_simp(x * Sum(CG(a, alpha, b, 0, a, alpha), (alpha, -a, a)) + CG(1, 0, 1, 0, 1, 0)) == x*(2*a + 1)*KroneckerDelta(b, 0) + CG(1, 0, 1, 0, 1, 0) assert cg_simp(2 * Sum(CG(1, alpha, 0, 0, 1, alpha), (alpha, -1, 1))) == 6 # Varshalovich 8.7.1 Eq 2 assert cg_simp(x*Sum((-1)**(a - alpha) * CG(a, alpha, a, -alpha, c, 0), (alpha, -a, a))) == x*sqrt(2*a + 1)*KroneckerDelta(c, 0) assert cg_simp(3*Sum((-1)**(2 - alpha) * CG( 2, alpha, 2, -alpha, 0, 0), (alpha, -2, 2))) == 3*sqrt(5) # Varshalovich 8.7.2 Eq 4 assert cg_simp(Sum(CG(a, alpha, b, beta, c, gamma)*CG(a, alpha, b, beta, cp, gammap), (alpha, -a, a), (beta, -b, b))) == KroneckerDelta(c, cp)*KroneckerDelta(gamma, gammap) assert cg_simp(Sum(CG(a, alpha, b, beta, c, gamma)*CG(a, alpha, b, beta, c, gammap), (alpha, -a, a), (beta, -b, b))) == KroneckerDelta(gamma, gammap) assert cg_simp(Sum(CG(a, alpha, b, beta, c, gamma)*CG(a, alpha, b, beta, cp, gamma), (alpha, -a, a), (beta, -b, b))) == KroneckerDelta(c, cp) assert cg_simp(Sum(CG( a, alpha, b, beta, c, gamma)**2, (alpha, -a, a), (beta, -b, b))) == 1 assert cg_simp(Sum(CG(2, alpha, 1, beta, 2, gamma)*CG(2, alpha, 1, beta, 2, gammap), (alpha, -2, 2), (beta, -1, 1))) == KroneckerDelta(gamma, gammap) def test_doit(): assert Wigner3j(1/2, -1/2, 1/2, 1/2, 0, 0).doit() == -sqrt(2)/2 assert Wigner6j(1, 2, 3, 2, 1, 2).doit() == sqrt(21)/105 assert Wigner9j( 2, 1, 1, S(3)/2, S(1)/2, 1, S(1)/2, S(1)/2, 0).doit() == sqrt(2)/12 assert CG(1/2, 1/2, 1/2, -1/2, 1, 0).doit() == sqrt(2)/2 sympy-0.7.4.1/sympy/physics/quantum/tests/test_qubit.py0000644000175000017500000001735512253362407023513 0ustar georgeskgeorgeskimport random from sympy import Integer, Matrix, Rational, sqrt, symbols from sympy.physics.quantum.qubit import (measure_all, measure_partial, matrix_to_qubit, matrix_to_density, qubit_to_matrix, IntQubit, IntQubitBra, QubitBra) from sympy.physics.quantum.gate import (HadamardGate, CNOT, XGate, YGate, ZGate, PhaseGate) from sympy.physics.quantum.qapply import qapply from sympy.physics.quantum.represent import represent from sympy.physics.quantum.shor import Qubit from sympy.utilities.pytest import raises from sympy.physics.quantum.density import Density from sympy.core.trace import Tr x, y = symbols('x,y') epsilon = .000001 def test_Qubit(): array = [0, 0, 1, 1, 0] qb = Qubit('00110') assert qb.flip(0) == Qubit('00111') assert qb.flip(1) == Qubit('00100') assert qb.flip(4) == Qubit('10110') assert qb.dimension == 5 for i in range(5): assert qb[i] == array[4 - i] assert len(qb) == 5 qb = Qubit('110') def test_QubitBra(): assert Qubit(0).dual_class() == QubitBra assert QubitBra(0).dual_class() == Qubit assert represent(Qubit(1, 1, 0), nqubits=3).H == \ represent(QubitBra(1, 1, 0), nqubits=3) assert Qubit( 0, 1)._eval_innerproduct_QubitBra(QubitBra(1, 0)) == Integer(0) assert Qubit( 0, 1)._eval_innerproduct_QubitBra(QubitBra(0, 1)) == Integer(1) def test_IntQubit(): assert IntQubit(8).as_int() == 8 assert IntQubit(8).qubit_values == (1, 0, 0, 0) assert IntQubit(7, 4).qubit_values == (0, 1, 1, 1) assert IntQubit(3) == IntQubit(3, 2) #test Dual Classes assert IntQubit(3).dual_class() == IntQubitBra assert IntQubitBra(3).dual_class() == IntQubit assert IntQubit(5)._eval_innerproduct_IntQubitBra(IntQubitBra(5)) == Integer(1) assert IntQubit(4)._eval_innerproduct_IntQubitBra(IntQubitBra(5)) == Integer(0) raises(ValueError, lambda: IntQubit(4, 1)) def test_superposition_of_states(): assert qapply(CNOT(0, 1)*HadamardGate(0)*(1/sqrt(2)*Qubit('01') + 1/sqrt(2)*Qubit('10'))).expand() == (Qubit('01')/2 + Qubit('00')/2 - Qubit('11')/2 + Qubit('10')/2) assert matrix_to_qubit(represent(CNOT(0, 1)*HadamardGate(0) *(1/sqrt(2)*Qubit('01') + 1/sqrt(2)*Qubit('10')), nqubits=2)) == \ (Qubit('01')/2 + Qubit('00')/2 - Qubit('11')/2 + Qubit('10')/2) #test apply methods def test_apply_represent_equality(): gates = [HadamardGate(int(3*random.random())), XGate(int(3*random.random())), ZGate(int(3*random.random())), YGate(int(3*random.random())), ZGate(int(3*random.random())), PhaseGate(int(3*random.random()))] circuit = Qubit(int(random.random()*2), int(random.random()*2), int(random.random()*2), int(random.random()*2), int(random.random()*2), int(random.random()*2)) for i in range(int(random.random()*6)): circuit = gates[int(random.random()*6)]*circuit mat = represent(circuit, nqubits=6) states = qapply(circuit) state_rep = matrix_to_qubit(mat) states = states.expand() state_rep = state_rep.expand() assert state_rep == states def test_matrix_to_qubits(): assert matrix_to_qubit( Matrix([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])) == \ Qubit(0, 0, 0, 0) assert qubit_to_matrix(Qubit(0, 0, 0, 0)) == \ Matrix([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) assert matrix_to_qubit(sqrt(2)*2*Matrix([1, 1, 1, 1, 1, 1, 1, 1])) == \ (2*sqrt(2)*(Qubit(0, 0, 0) + Qubit(0, 0, 1) + Qubit(0, 1, 0) + Qubit(0, 1, 1) + Qubit(1, 0, 0) + Qubit(1, 0, 1) + Qubit(1, 1, 0) + Qubit(1, 1, 1))).expand() assert qubit_to_matrix(2*sqrt(2)*(Qubit(0, 0, 0) + Qubit(0, 0, 1) + Qubit(0, 1, 0) + Qubit(0, 1, 1) + Qubit(1, 0, 0) + Qubit(1, 0, 1) + Qubit(1, 1, 0) + Qubit(1, 1, 1))) == \ sqrt(2)*2*Matrix([1, 1, 1, 1, 1, 1, 1, 1]) def test_measure_normalize(): a, b = symbols('a b') state = a*Qubit('110') + b*Qubit('111') assert measure_partial(state, (0,), normalize=False) == \ [(a*Qubit('110'), a*a.conjugate()), (b*Qubit('111'), b*b.conjugate())] assert measure_all(state, normalize=False) == \ [(Qubit('110'), a*a.conjugate()), (Qubit('111'), b*b.conjugate())] def test_measure_partial(): #Basic test of collapse of entangled two qubits (Bell States) state = Qubit('01') + Qubit('10') assert measure_partial(state, (0,)) == \ [(Qubit('10'), Rational(1, 2)), (Qubit('01'), Rational(1, 2))] assert measure_partial(state, (0,)) == \ measure_partial(state, (1,))[::-1] #Test of more complex collapse and probability calculation state1 = sqrt(2)/sqrt(3)*Qubit('00001') + 1/sqrt(3)*Qubit('11111') assert measure_partial(state1, (0,)) == \ [(sqrt(2)/sqrt(3)*Qubit('00001') + 1/sqrt(3)*Qubit('11111'), 1)] assert measure_partial(state1, (1, 2)) == measure_partial(state1, (3, 4)) assert measure_partial(state1, (1, 2, 3)) == \ [(Qubit('00001'), Rational(2, 3)), (Qubit('11111'), Rational(1, 3))] #test of measuring multiple bits at once state2 = Qubit('1111') + Qubit('1101') + Qubit('1011') + Qubit('1000') assert measure_partial(state2, (0, 1, 3)) == \ [(Qubit('1000'), Rational(1, 4)), (Qubit('1101'), Rational(1, 4)), (Qubit('1011')/sqrt(2) + Qubit('1111')/sqrt(2), Rational(1, 2))] assert measure_partial(state2, (0,)) == \ [(Qubit('1000'), Rational(1, 4)), (Qubit('1111')/sqrt(3) + Qubit('1101')/sqrt(3) + Qubit('1011')/sqrt(3), Rational(3, 4))] def test_measure_all(): assert measure_all(Qubit('11')) == [(Qubit('11'), 1)] state = Qubit('11') + Qubit('10') assert measure_all(state) == [(Qubit('10'), Rational(1, 2)), (Qubit('11'), Rational(1, 2))] state2 = Qubit('11')/sqrt(5) + 2*Qubit('00')/sqrt(5) assert measure_all(state2) == \ [(Qubit('00'), Rational(4, 5)), (Qubit('11'), Rational(1, 5))] def test_eval_trace(): q1 = Qubit('10110') q2 = Qubit('01010') d = Density([q1, 0.6], [q2, 0.4]) t = Tr(d) assert t.doit() == 1 # extreme bits t = Tr(d, 0) assert t.doit() == (0.4*Density([Qubit('0101'), 1]) + 0.6*Density([Qubit('1011'), 1])) t = Tr(d, 4) assert t.doit() == (0.4*Density([Qubit('1010'), 1]) + 0.6*Density([Qubit('0110'), 1])) # index somewhere in between t = Tr(d, 2) assert t.doit() == (0.4*Density([Qubit('0110'), 1]) + 0.6*Density([Qubit('1010'), 1])) #trace all indices t = Tr(d, [0, 1, 2, 3, 4]) assert t.doit() == 1 # trace some indices, initialized in # non-canonical order t = Tr(d, [2, 1, 3]) assert t.doit() == (0.4*Density([Qubit('00'), 1]) + 0.6*Density([Qubit('10'), 1])) # mixed states q = (1/sqrt(2)) * (Qubit('00') + Qubit('11')) d = Density( [q, 1.0] ) t = Tr(d, 0) assert t.doit() == (0.5*Density([Qubit('0'), 1]) + 0.5*Density([Qubit('1'), 1])) def test_matrix_to_density(): mat = Matrix([[0, 0], [0, 1]]) assert matrix_to_density(mat) == Density([Qubit('1'), 1]) mat = Matrix([[1, 0], [0, 0]]) assert matrix_to_density(mat) == Density([Qubit('0'), 1]) mat = Matrix([[0, 0], [0, 0]]) assert matrix_to_density(mat) == 0 mat = Matrix([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]]) assert matrix_to_density(mat) == Density([Qubit('10'), 1]) mat = Matrix([[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]) assert matrix_to_density(mat) == Density([Qubit('00'), 1]) sympy-0.7.4.1/sympy/physics/quantum/tests/test_density.py0000644000175000017500000002274012253362407024040 0ustar georgeskgeorgeskfrom sympy import pprint, latex, symbols, S, log from sympy.matrices import Matrix from sympy.core.trace import Tr from sympy.external import import_module from sympy.physics.quantum.density import Density, entropy, fidelity from sympy.physics.quantum.state import Ket, Bra, TimeDepKet from sympy.physics.quantum.qubit import Qubit from sympy.physics.quantum.qapply import qapply from sympy.physics.quantum.gate import HadamardGate from sympy.physics.quantum.represent import represent from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.cartesian import XKet, PxKet, PxOp, XOp from sympy.physics.quantum.spin import JzKet, Jz from sympy.physics.quantum.operator import OuterProduct from sympy.functions import sqrt from sympy.utilities.pytest import raises from sympy.physics.quantum.matrixutils import scipy_sparse_matrix from sympy.physics.quantum.tensorproduct import TensorProduct def test_eval_args(): # check instance created assert isinstance(Density([Ket(0), 0.5], [Ket(1), 0.5]), Density) assert isinstance(Density([Qubit('00'), 1/sqrt(2)], [Qubit('11'), 1/sqrt(2)]), Density) #test if Qubit object type preserved d = Density([Qubit('00'), 1/sqrt(2)], [Qubit('11'), 1/sqrt(2)]) for (state, prob) in d.args: assert isinstance(state, Qubit) # check for value error, when prob is not provided raises(ValueError, lambda: Density([Ket(0)], [Ket(1)])) def test_doit(): x, y = symbols('x y') A, B, C, D, E, F = symbols('A B C D E F', commutative=False) d = Density([XKet(), 0.5], [PxKet(), 0.5]) assert (0.5*(PxKet()*Dagger(PxKet())) + 0.5*(XKet()*Dagger(XKet()))) == d.doit() # check for kets with expr in them d_with_sym = Density([XKet(x*y), 0.5], [PxKet(x*y), 0.5]) assert (0.5*(PxKet(x*y)*Dagger(PxKet(x*y))) + 0.5*(XKet(x*y)*Dagger(XKet(x*y)))) == d_with_sym.doit() d = Density([(A + B)*C, 1.0]) assert d.doit() == (1.0*A*C*Dagger(C)*Dagger(A) + 1.0*A*C*Dagger(C)*Dagger(B) + 1.0*B*C*Dagger(C)*Dagger(A) + 1.0*B*C*Dagger(C)*Dagger(B)) # With TensorProducts as args # Density with simple tensor products as args t = TensorProduct(A, B, C) d = Density([t, 1.0]) assert d.doit() == \ 1.0 * TensorProduct(A*Dagger(A), B*Dagger(B), C*Dagger(C)) # Density with multiple Tensorproducts as states t2 = TensorProduct(A, B) t3 = TensorProduct(C, D) d = Density([t2, 0.5], [t3, 0.5]) assert d.doit() == (0.5 * TensorProduct(A*Dagger(A), B*Dagger(B)) + 0.5 * TensorProduct(C*Dagger(C), D*Dagger(D))) #Density with mixed states d = Density([t2 + t3, 1.0]) assert d.doit() == (1.0 * TensorProduct(A*Dagger(A), B*Dagger(B)) + 1.0 * TensorProduct(A*Dagger(C), B*Dagger(D)) + 1.0 * TensorProduct(C*Dagger(A), D*Dagger(B)) + 1.0 * TensorProduct(C*Dagger(C), D*Dagger(D))) #Density operators with spin states tp1 = TensorProduct(JzKet(1, 1), JzKet(1, -1)) d = Density([tp1, 1]) # full trace t = Tr(d) assert t.doit() == 1 #Partial trace on density operators with spin states t = Tr(d, [0]) assert t.doit() == JzKet(1, -1) * Dagger(JzKet(1, -1)) t = Tr(d, [1]) assert t.doit() == JzKet(1, 1) * Dagger(JzKet(1, 1)) # with another spin state tp2 = TensorProduct(JzKet(S(1)/2, S(1)/2), JzKet(S(1)/2, -S(1)/2)) d = Density([tp2, 1]) #full trace t = Tr(d) assert t.doit() == 1 #Partial trace on density operators with spin states t = Tr(d, [0]) assert t.doit() == JzKet(S(1)/2, -S(1)/2) * Dagger(JzKet(S(1)/2, -S(1)/2)) t = Tr(d, [1]) assert t.doit() == JzKet(S(1)/2, S(1)/2) * Dagger(JzKet(S(1)/2, S(1)/2)) def test_apply_op(): d = Density([Ket(0), 0.5], [Ket(1), 0.5]) assert d.apply_op(XOp()) == Density([XOp()*Ket(0), 0.5], [XOp()*Ket(1), 0.5]) def test_represent(): x, y = symbols('x y') d = Density([XKet(), 0.5], [PxKet(), 0.5]) assert (represent(0.5*(PxKet()*Dagger(PxKet()))) + represent(0.5*(XKet()*Dagger(XKet())))) == represent(d) # check for kets with expr in them d_with_sym = Density([XKet(x*y), 0.5], [PxKet(x*y), 0.5]) assert (represent(0.5*(PxKet(x*y)*Dagger(PxKet(x*y)))) + represent(0.5*(XKet(x*y)*Dagger(XKet(x*y))))) == \ represent(d_with_sym) # check when given explicit basis assert (represent(0.5*(XKet()*Dagger(XKet())), basis=PxOp()) + represent(0.5*(PxKet()*Dagger(PxKet())), basis=PxOp())) == \ represent(d, basis=PxOp()) def test_states(): d = Density([Ket(0), 0.5], [Ket(1), 0.5]) states = d.states() assert states[0] == Ket(0) and states[1] == Ket(1) def test_probs(): d = Density([Ket(0), .75], [Ket(1), 0.25]) probs = d.probs() assert probs[0] == 0.75 and probs[1] == 0.25 #probs can be symbols x, y = symbols('x y') d = Density([Ket(0), x], [Ket(1), y]) probs = d.probs() assert probs[0] == x and probs[1] == y def test_get_state(): x, y = symbols('x y') d = Density([Ket(0), x], [Ket(1), y]) states = (d.get_state(0), d.get_state(1)) assert states[0] == Ket(0) and states[1] == Ket(1) def test_get_prob(): x, y = symbols('x y') d = Density([Ket(0), x], [Ket(1), y]) probs = (d.get_prob(0), d.get_prob(1)) assert probs[0] == x and probs[1] == y def test_entropy(): up = JzKet(S(1)/2, S(1)/2) down = JzKet(S(1)/2, -S(1)/2) d = Density((up, 0.5), (down, 0.5)) # test for density object ent = entropy(d) assert entropy(d) == 0.5*log(2) assert d.entropy() == 0.5*log(2) np = import_module('numpy', min_module_version='1.4.0') if np: #do this test only if 'numpy' is available on test machine np_mat = represent(d, format='numpy') ent = entropy(np_mat) assert isinstance(np_mat, np.matrixlib.defmatrix.matrix) assert ent.real == 0.69314718055994529 assert ent.imag == 0 scipy = import_module('scipy', __import__kwargs={'fromlist': ['sparse']}) if scipy and np: #do this test only if numpy and scipy are available mat = represent(d, format="scipy.sparse") assert isinstance(mat, scipy_sparse_matrix) assert ent.real == 0.69314718055994529 assert ent.imag == 0 def test_eval_trace(): up = JzKet(S(1)/2, S(1)/2) down = JzKet(S(1)/2, -S(1)/2) d = Density((up, 0.5), (down, 0.5)) t = Tr(d) assert t.doit() == 1 #test dummy time dependent states class TestTimeDepKet(TimeDepKet): def _eval_trace(self, bra, **options): return 1 x, t = symbols('x t') k1 = TestTimeDepKet(0, 0.5) k2 = TestTimeDepKet(0, 1) d = Density([k1, 0.5], [k2, 0.5]) assert d.doit() == (0.5 * OuterProduct(k1, k1.dual) + 0.5 * OuterProduct(k2, k2.dual)) t = Tr(d) assert t.doit() == 1 def test_fidelity(): #test with kets up = JzKet(S(1)/2, S(1)/2) down = JzKet(S(1)/2, -S(1)/2) updown = (S(1)/sqrt(2))*up + (S(1)/sqrt(2))*down #check with matrices up_dm = represent(up * Dagger(up)) down_dm = represent(down * Dagger(down)) updown_dm = represent(updown * Dagger(updown)) assert abs(fidelity(up_dm, up_dm) - 1) < 1e-3 assert fidelity(up_dm, down_dm) < 1e-3 assert abs(fidelity(up_dm, updown_dm) - (S(1)/sqrt(2))) < 1e-3 assert abs(fidelity(updown_dm, down_dm) - (S(1)/sqrt(2))) < 1e-3 #check with density up_dm = Density([up, 1.0]) down_dm = Density([down, 1.0]) updown_dm = Density([updown, 1.0]) assert abs(fidelity(up_dm, up_dm) - 1) < 1e-3 assert abs(fidelity(up_dm, down_dm)) < 1e-3 assert abs(fidelity(up_dm, updown_dm) - (S(1)/sqrt(2))) < 1e-3 assert abs(fidelity(updown_dm, down_dm) - (S(1)/sqrt(2))) < 1e-3 #check mixed states with density updown2 = (sqrt(3)/2)*up + (S(1)/2)*down d1 = Density([updown, 0.25], [updown2, 0.75]) d2 = Density([updown, 0.75], [updown2, 0.25]) assert abs(fidelity(d1, d2) - 0.991) < 1e-3 assert abs(fidelity(d2, d1) - fidelity(d1, d2)) < 1e-3 #using qubits/density(pure states) state1 = Qubit('0') state2 = Qubit('1') state3 = (S(1)/sqrt(2))*state1 + (S(1)/sqrt(2))*state2 state4 = (sqrt(S(2)/3))*state1 + (S(1)/sqrt(3))*state2 state1_dm = Density([state1, 1]) state2_dm = Density([state2, 1]) state3_dm = Density([state3, 1]) assert fidelity(state1_dm, state1_dm) == 1 assert fidelity(state1_dm, state2_dm) == 0 assert abs(fidelity(state1_dm, state3_dm) - 1/sqrt(2)) < 1e-3 assert abs(fidelity(state3_dm, state2_dm) - 1/sqrt(2)) < 1e-3 #using qubits/density(mixed states) d1 = Density([state3, 0.70], [state4, 0.30]) d2 = Density([state3, 0.20], [state4, 0.80]) assert abs(fidelity(d1, d1) - 1) < 1e-3 assert abs(fidelity(d1, d2) - 0.996) < 1e-3 assert abs(fidelity(d1, d2) - fidelity(d2, d1)) < 1e-3 #TODO: test for invalid arguments # non-square matrix mat1 = [[0, 0], [0, 0], [0, 0]] mat2 = [[0, 0], [0, 0]] raises(ValueError, lambda: fidelity(mat1, mat2)) # unequal dimensions mat1 = [[0, 0], [0, 0]] mat2 = [[0, 0, 0], [0, 0, 0], [0, 0, 0]] raises(ValueError, lambda: fidelity(mat1, mat2)) # unsupported data-type x, y = 1, 2 # random values that is not a matrix raises(ValueError, lambda: fidelity(x, y)) sympy-0.7.4.1/sympy/physics/quantum/tests/test_printing.py0000644000175000017500000007244112253362407024216 0ustar georgeskgeorgesk# -*- encoding: utf-8 -*- """ TODO: * Address Issue 2251, printing of spin states """ from sympy.physics.quantum.anticommutator import AntiCommutator from sympy.physics.quantum.cg import CG, Wigner3j, Wigner6j, Wigner9j from sympy.physics.quantum.commutator import Commutator from sympy.physics.quantum.constants import hbar from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.gate import CGate, CNotGate, IdentityGate, UGate, XGate from sympy.physics.quantum.hilbert import ComplexSpace, FockSpace, HilbertSpace, L2 from sympy.physics.quantum.innerproduct import InnerProduct from sympy.physics.quantum.operator import Operator, OuterProduct, DifferentialOperator from sympy.physics.quantum.qexpr import QExpr from sympy.physics.quantum.qubit import Qubit, IntQubit from sympy.physics.quantum.spin import Jz, J2, JzBra, JzBraCoupled, JzKet, JzKetCoupled, Rotation, WignerD from sympy.physics.quantum.state import Bra, Ket, TimeDepBra, TimeDepKet from sympy.physics.quantum.tensorproduct import TensorProduct from sympy.physics.quantum.sho1d import RaisingOp from sympy import Derivative, Function, Interval, Matrix, Pow, S, symbols, Symbol, oo from sympy.core.compatibility import exec_ from sympy.utilities.pytest import XFAIL # Imports used in srepr strings from sympy.physics.quantum.constants import HBar from sympy.physics.quantum.hilbert import DirectSumHilbertSpace, TensorProductHilbertSpace, TensorPowerHilbertSpace from sympy.physics.quantum.spin import JzOp, J2Op from sympy import Add, Integer, Mul, Rational, Tuple from sympy.printing import srepr from sympy.printing.pretty import pretty as xpretty from sympy.printing.latex import latex from sympy.core.compatibility import u_decode as u MutableDenseMatrix = Matrix ENV = {} exec_("from sympy import *", ENV) def sT(expr, string): """ sT := sreprTest from sympy/printing/tests/test_repr.py """ assert srepr(expr) == string assert eval(string) == expr def pretty(expr): """ASCII pretty-printing""" return xpretty(expr, use_unicode=False, wrap_line=False) def upretty(expr): """Unicode pretty-printing""" return xpretty(expr, use_unicode=True, wrap_line=False) def test_anticommutator(): A = Operator('A') B = Operator('B') ac = AntiCommutator(A, B) ac_tall = AntiCommutator(A**2, B) assert str(ac) == '{A,B}' assert pretty(ac) == '{A,B}' assert upretty(ac) == u('{A,B}') assert latex(ac) == r'\left\{A,B\right\}' sT(ac, "AntiCommutator(Operator(Symbol('A')),Operator(Symbol('B')))") assert str(ac_tall) == '{A**2,B}' ascii_str = \ """\ / 2 \\\n\ \n\ \\ /\ """ ucode_str = \ u("""\ ⎧ 2 ⎫\n\ ⎨A ,B⎬\n\ ⎩ ⎭\ """) assert pretty(ac_tall) == ascii_str assert upretty(ac_tall) == ucode_str assert latex(ac_tall) == r'\left\{\left(A\right)^{2},B\right\}' sT(ac_tall, "AntiCommutator(Pow(Operator(Symbol('A')), Integer(2)),Operator(Symbol('B')))") def test_cg(): cg = CG(1, 2, 3, 4, 5, 6) wigner3j = Wigner3j(1, 2, 3, 4, 5, 6) wigner6j = Wigner6j(1, 2, 3, 4, 5, 6) wigner9j = Wigner9j(1, 2, 3, 4, 5, 6, 7, 8, 9) assert str(cg) == 'CG(1, 2, 3, 4, 5, 6)' ascii_str = \ """\ 5,6 \n\ C \n\ 1,2,3,4\ """ ucode_str = \ u("""\ 5,6 \n\ C \n\ 1,2,3,4\ """) assert pretty(cg) == ascii_str assert upretty(cg) == ucode_str assert latex(cg) == r'C^{5,6}_{1,2,3,4}' sT(cg, "CG(Integer(1), Integer(2), Integer(3), Integer(4), Integer(5), Integer(6))") assert str(wigner3j) == 'Wigner3j(1, 2, 3, 4, 5, 6)' ascii_str = \ """\ /1 3 5\\\n\ | |\n\ \\2 4 6/\ """ ucode_str = \ u("""\ ⎛1 3 5⎞\n\ ⎜ ⎟\n\ ⎝2 4 6⎠\ """) assert pretty(wigner3j) == ascii_str assert upretty(wigner3j) == ucode_str assert latex(wigner3j) == \ r'\left(\begin{array}{ccc} 1 & 3 & 5 \\ 2 & 4 & 6 \end{array}\right)' sT(wigner3j, "Wigner3j(Integer(1), Integer(2), Integer(3), Integer(4), Integer(5), Integer(6))") assert str(wigner6j) == 'Wigner6j(1, 2, 3, 4, 5, 6)' ascii_str = \ """\ /1 2 3\\\n\ < >\n\ \\4 5 6/\ """ ucode_str = \ u("""\ ⎧1 2 3⎫\n\ ⎨ ⎬\n\ ⎩4 5 6⎭\ """) assert pretty(wigner6j) == ascii_str assert upretty(wigner6j) == ucode_str assert latex(wigner6j) == \ r'\left\{\begin{array}{ccc} 1 & 2 & 3 \\ 4 & 5 & 6 \end{array}\right\}' sT(wigner6j, "Wigner6j(Integer(1), Integer(2), Integer(3), Integer(4), Integer(5), Integer(6))") assert str(wigner9j) == 'Wigner9j(1, 2, 3, 4, 5, 6, 7, 8, 9)' ascii_str = \ """\ /1 2 3\\\n\ | |\n\ <4 5 6>\n\ | |\n\ \\7 8 9/\ """ ucode_str = \ u("""\ ⎧1 2 3⎫\n\ ⎪ ⎪\n\ ⎨4 5 6⎬\n\ ⎪ ⎪\n\ ⎩7 8 9⎭\ """) assert pretty(wigner9j) == ascii_str assert upretty(wigner9j) == ucode_str assert latex(wigner9j) == \ r'\left\{\begin{array}{ccc} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{array}\right\}' sT(wigner9j, "Wigner9j(Integer(1), Integer(2), Integer(3), Integer(4), Integer(5), Integer(6), Integer(7), Integer(8), Integer(9))") def test_commutator(): A = Operator('A') B = Operator('B') c = Commutator(A, B) c_tall = Commutator(A**2, B) assert str(c) == '[A,B]' assert pretty(c) == '[A,B]' assert upretty(c) == u('[A,B]') assert latex(c) == r'\left[A,B\right]' sT(c, "Commutator(Operator(Symbol('A')),Operator(Symbol('B')))") assert str(c_tall) == '[A**2,B]' ascii_str = \ """\ [ 2 ]\n\ [A ,B]\ """ ucode_str = \ u("""\ ⎡ 2 ⎤\n\ ⎣A ,B⎦\ """) assert pretty(c_tall) == ascii_str assert upretty(c_tall) == ucode_str assert latex(c_tall) == r'\left[\left(A\right)^{2},B\right]' sT(c_tall, "Commutator(Pow(Operator(Symbol('A')), Integer(2)),Operator(Symbol('B')))") def test_constants(): assert str(hbar) == 'hbar' assert pretty(hbar) == 'hbar' assert upretty(hbar) == u('ℏ') assert latex(hbar) == r'\hbar' sT(hbar, "HBar()") def test_dagger(): x = symbols('x') expr = Dagger(x) assert str(expr) == 'Dagger(x)' ascii_str = \ """\ +\n\ x \ """ ucode_str = \ u("""\ †\n\ x \ """) assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str assert latex(expr) == r'x^{\dag}' sT(expr, "Dagger(Symbol('x'))") @XFAIL def test_gate_failing(): a, b, c, d = symbols('a,b,c,d') uMat = Matrix([[a, b], [c, d]]) g = UGate((0,), uMat) assert str(g) == 'U(0)' def test_gate(): a, b, c, d = symbols('a,b,c,d') uMat = Matrix([[a, b], [c, d]]) q = Qubit(1, 0, 1, 0, 1) g1 = IdentityGate(2) g2 = CGate((3, 0), XGate(1)) g3 = CNotGate(1, 0) g4 = UGate((0,), uMat) assert str(g1) == '1(2)' assert pretty(g1) == '1 \n 2' assert upretty(g1) == u('1 \n 2') assert latex(g1) == r'1_{2}' sT(g1, "IdentityGate(Integer(2))") assert str(g1*q) == '1(2)*|10101>' ascii_str = \ """\ 1 *|10101>\n\ 2 \ """ ucode_str = \ u("""\ 1 ⋅❘10101⟩\n\ 2 \ """) assert pretty(g1*q) == ascii_str assert upretty(g1*q) == ucode_str assert latex(g1*q) == r'1_{2} {\left|10101\right\rangle }' sT(g1*q, "Mul(IdentityGate(Integer(2)), Qubit(Integer(1),Integer(0),Integer(1),Integer(0),Integer(1)))") assert str(g2) == 'C((3,0),X(1))' ascii_str = \ """\ C /X \\\n\ 3,0\\ 1/\ """ ucode_str = \ u("""\ C ⎛X ⎞\n\ 3,0⎝ 1⎠\ """) assert pretty(g2) == ascii_str assert upretty(g2) == ucode_str assert latex(g2) == r'C_{3,0}{\left(X_{1}\right)}' sT(g2, "CGate(Tuple(Integer(3), Integer(0)),XGate(Integer(1)))") assert str(g3) == 'CNOT(1,0)' ascii_str = \ """\ CNOT \n\ 1,0\ """ ucode_str = \ u("""\ CNOT \n\ 1,0\ """) assert pretty(g3) == ascii_str assert upretty(g3) == ucode_str assert latex(g3) == r'CNOT_{1,0}' sT(g3, "CNotGate(Integer(1),Integer(0))") # str(g4) Fails #assert str(g4) == '' ascii_str = \ """\ U \n\ 0\ """ ucode_str = \ u("""\ U \n\ 0\ """) assert pretty(g4) == ascii_str assert upretty(g4) == ucode_str assert latex(g4) == r'U_{0}' sT(g4, "UGate(Tuple(Integer(0)),MutableDenseMatrix([[Symbol('a'), Symbol('b')], [Symbol('c'), Symbol('d')]]))") def test_hilbert(): h1 = HilbertSpace() h2 = ComplexSpace(2) h3 = FockSpace() h4 = L2(Interval(0, oo)) assert str(h1) == 'H' assert pretty(h1) == 'H' assert upretty(h1) == u('H') assert latex(h1) == r'\mathcal{H}' sT(h1, "HilbertSpace()") assert str(h2) == 'C(2)' ascii_str = \ """\ 2\n\ C \ """ ucode_str = \ u("""\ 2\n\ C \ """) assert pretty(h2) == ascii_str assert upretty(h2) == ucode_str assert latex(h2) == r'\mathcal{C}^{2}' sT(h2, "ComplexSpace(Integer(2))") assert str(h3) == 'F' assert pretty(h3) == 'F' assert upretty(h3) == u('F') assert latex(h3) == r'\mathcal{F}' sT(h3, "FockSpace()") assert str(h4) == 'L2([0, oo))' ascii_str = \ """\ 2\n\ L \ """ ucode_str = \ u("""\ 2\n\ L \ """) assert pretty(h4) == ascii_str assert upretty(h4) == ucode_str assert latex(h4) == r'{\mathcal{L}^2}\left( \left[0, \infty\right) \right)' sT(h4, "L2(Interval(Integer(0), oo, False, True))") assert str(h1 + h2) == 'H+C(2)' ascii_str = \ """\ 2\n\ H + C \ """ ucode_str = \ u("""\ 2\n\ H ⊕ C \ """) assert pretty(h1 + h2) == ascii_str assert upretty(h1 + h2) == ucode_str assert latex(h1 + h2) sT(h1 + h2, "DirectSumHilbertSpace(HilbertSpace(),ComplexSpace(Integer(2)))") assert str(h1*h2) == "H*C(2)" ascii_str = \ """\ 2\n\ H x C \ """ ucode_str = \ u("""\ 2\n\ H ⨂ C \ """) assert pretty(h1*h2) == ascii_str assert upretty(h1*h2) == ucode_str assert latex(h1*h2) sT(h1*h2, "TensorProductHilbertSpace(HilbertSpace(),ComplexSpace(Integer(2)))") assert str(h1**2) == 'H**2' ascii_str = \ """\ x2\n\ H \ """ ucode_str = \ u("""\ ⨂2\n\ H \ """) assert pretty(h1**2) == ascii_str assert upretty(h1**2) == ucode_str assert latex(h1**2) == r'{\mathcal{H}}^{\otimes 2}' sT(h1**2, "TensorPowerHilbertSpace(HilbertSpace(),Integer(2))") def test_innerproduct(): x = symbols('x') ip1 = InnerProduct(Bra(), Ket()) ip2 = InnerProduct(TimeDepBra(), TimeDepKet()) ip3 = InnerProduct(JzBra(1, 1), JzKet(1, 1)) ip4 = InnerProduct(JzBraCoupled(1, 1, (1, 1)), JzKetCoupled(1, 1, (1, 1))) ip_tall1 = InnerProduct(Bra(x/2), Ket(x/2)) ip_tall2 = InnerProduct(Bra(x), Ket(x/2)) ip_tall3 = InnerProduct(Bra(x/2), Ket(x)) assert str(ip1) == '' assert pretty(ip1) == '' assert upretty(ip1) == u('⟨ψ❘ψ⟩') assert latex( ip1) == r'\left\langle \psi \right. {\left|\psi\right\rangle }' sT(ip1, "InnerProduct(Bra(Symbol('psi')),Ket(Symbol('psi')))") assert str(ip2) == '' assert pretty(ip2) == '' assert upretty(ip2) == u('⟨ψ;t❘ψ;t⟩') assert latex(ip2) == \ r'\left\langle \psi;t \right. {\left|\psi;t\right\rangle }' sT(ip2, "InnerProduct(TimeDepBra(Symbol('psi'),Symbol('t')),TimeDepKet(Symbol('psi'),Symbol('t')))") assert str(ip3) == "<1,1|1,1>" assert pretty(ip3) == '<1,1|1,1>' assert upretty(ip3) == u('⟨1,1❘1,1⟩') assert latex(ip3) == r'\left\langle 1,1 \right. {\left|1,1\right\rangle }' sT(ip3, "InnerProduct(JzBra(Integer(1),Integer(1)),JzKet(Integer(1),Integer(1)))") assert str(ip4) == "<1,1,j1=1,j2=1|1,1,j1=1,j2=1>" assert pretty(ip4) == '<1,1,j1=1,j2=1|1,1,j1=1,j2=1>' assert upretty(ip4) == u('⟨1,1,j₁=1,j₂=1❘1,1,j₁=1,j₂=1⟩') assert latex(ip4) == \ r'\left\langle 1,1,j_{1}=1,j_{2}=1 \right. {\left|1,1,j_{1}=1,j_{2}=1\right\rangle }' sT(ip4, "InnerProduct(JzBraCoupled(Integer(1),Integer(1),Tuple(Integer(1), Integer(1)),Tuple(Tuple(Integer(1), Integer(2), Integer(1)))),JzKetCoupled(Integer(1),Integer(1),Tuple(Integer(1), Integer(1)),Tuple(Tuple(Integer(1), Integer(2), Integer(1)))))") assert str(ip_tall1) == '' ascii_str = \ """\ / | \\ \n\ / x|x \\\n\ \\ -|- /\n\ \\2|2/ \ """ ucode_str = \ u("""\ ╱ │ ╲ \n\ ╱ x│x ╲\n\ ╲ ─│─ ╱\n\ ╲2│2╱ \ """) assert pretty(ip_tall1) == ascii_str assert upretty(ip_tall1) == ucode_str assert latex(ip_tall1) == \ r'\left\langle \frac{x}{2} \right. {\left|\frac{x}{2}\right\rangle }' sT(ip_tall1, "InnerProduct(Bra(Mul(Rational(1, 2), Symbol('x'))),Ket(Mul(Rational(1, 2), Symbol('x'))))") assert str(ip_tall2) == '' ascii_str = \ """\ / | \\ \n\ / |x \\\n\ \\ x|- /\n\ \\ |2/ \ """ ucode_str = \ u("""\ ╱ │ ╲ \n\ ╱ │x ╲\n\ ╲ x│─ ╱\n\ ╲ │2╱ \ """) assert pretty(ip_tall2) == ascii_str assert upretty(ip_tall2) == ucode_str assert latex(ip_tall2) == \ r'\left\langle x \right. {\left|\frac{x}{2}\right\rangle }' sT(ip_tall2, "InnerProduct(Bra(Symbol('x')),Ket(Mul(Rational(1, 2), Symbol('x'))))") assert str(ip_tall3) == '' ascii_str = \ """\ / | \\ \n\ / x| \\\n\ \\ -|x /\n\ \\2| / \ """ ucode_str = \ u("""\ ╱ │ ╲ \n\ ╱ x│ ╲\n\ ╲ ─│x ╱\n\ ╲2│ ╱ \ """) assert pretty(ip_tall3) == ascii_str assert upretty(ip_tall3) == ucode_str assert latex(ip_tall3) == \ r'\left\langle \frac{x}{2} \right. {\left|x\right\rangle }' sT(ip_tall3, "InnerProduct(Bra(Mul(Rational(1, 2), Symbol('x'))),Ket(Symbol('x')))") def test_operator(): a = Operator('A') b = Operator('B', Symbol('t'), S(1)/2) inv = a.inv() f = Function('f') x = symbols('x') d = DifferentialOperator(Derivative(f(x), x), f(x)) op = OuterProduct(Ket(), Bra()) assert str(a) == 'A' assert pretty(a) == 'A' assert upretty(a) == u('A') assert latex(a) == 'A' sT(a, "Operator(Symbol('A'))") assert str(inv) == 'A**(-1)' ascii_str = \ """\ -1\n\ A \ """ ucode_str = \ u("""\ -1\n\ A \ """) assert pretty(inv) == ascii_str assert upretty(inv) == ucode_str assert latex(inv) == r'\left(A\right)^{-1}' sT(inv, "Pow(Operator(Symbol('A')), Integer(-1))") assert str(d) == 'DifferentialOperator(Derivative(f(x), x),f(x))' ascii_str = \ """\ /d \\\n\ DifferentialOperator|--(f(x)),f(x)|\n\ \dx /\ """ ucode_str = \ u("""\ ⎛d ⎞\n\ DifferentialOperator⎜──(f(x)),f(x)⎟\n\ ⎝dx ⎠\ """) assert pretty(d) == ascii_str assert upretty(d) == ucode_str assert latex(d) == \ r'DifferentialOperator\left(\frac{d}{d x} f{\left (x \right )},f{\left (x \right )}\right)' sT(d, "DifferentialOperator(Derivative(Function('f')(Symbol('x')), Symbol('x')),Function('f')(Symbol('x')))") assert str(b) == 'Operator(B,t,1/2)' assert pretty(b) == 'Operator(B,t,1/2)' assert upretty(b) == u('Operator(B,t,1/2)') assert latex(b) == r'Operator\left(B,t,\frac{1}{2}\right)' sT(b, "Operator(Symbol('B'),Symbol('t'),Rational(1, 2))") assert str(op) == '|psi>' assert pretty(q1) == '|0101>' assert upretty(q1) == u('❘0101⟩') assert latex(q1) == r'{\left|0101\right\rangle }' sT(q1, "Qubit(Integer(0),Integer(1),Integer(0),Integer(1))") assert str(q2) == '|8>' assert pretty(q2) == '|8>' assert upretty(q2) == u('❘8⟩') assert latex(q2) == r'{\left|8\right\rangle }' sT(q2, "IntQubit(8)") def test_spin(): lz = JzOp('L') ket = JzKet(1, 0) bra = JzBra(1, 0) cket = JzKetCoupled(1, 0, (1, 2)) cbra = JzBraCoupled(1, 0, (1, 2)) cket_big = JzKetCoupled(1, 0, (1, 2, 3)) cbra_big = JzBraCoupled(1, 0, (1, 2, 3)) rot = Rotation(1, 2, 3) bigd = WignerD(1, 2, 3, 4, 5, 6) smalld = WignerD(1, 2, 3, 0, 4, 0) assert str(lz) == 'Lz' ascii_str = \ """\ L \n\ z\ """ ucode_str = \ u("""\ L \n\ z\ """) assert pretty(lz) == ascii_str assert upretty(lz) == ucode_str assert latex(lz) == 'L_z' sT(lz, "JzOp(Symbol('L'))") assert str(J2) == 'J2' ascii_str = \ """\ 2\n\ J \ """ ucode_str = \ u("""\ 2\n\ J \ """) assert pretty(J2) == ascii_str assert upretty(J2) == ucode_str assert latex(J2) == r'J^2' sT(J2, "J2Op(Symbol('J'))") assert str(Jz) == 'Jz' ascii_str = \ """\ J \n\ z\ """ ucode_str = \ u("""\ J \n\ z\ """) assert pretty(Jz) == ascii_str assert upretty(Jz) == ucode_str assert latex(Jz) == 'J_z' sT(Jz, "JzOp(Symbol('J'))") assert str(ket) == '|1,0>' assert pretty(ket) == '|1,0>' assert upretty(ket) == u('❘1,0⟩') assert latex(ket) == r'{\left|1,0\right\rangle }' sT(ket, "JzKet(Integer(1),Integer(0))") assert str(bra) == '<1,0|' assert pretty(bra) == '<1,0|' assert upretty(bra) == u('⟨1,0❘') assert latex(bra) == r'{\left\langle 1,0\right|}' sT(bra, "JzBra(Integer(1),Integer(0))") assert str(cket) == '|1,0,j1=1,j2=2>' assert pretty(cket) == '|1,0,j1=1,j2=2>' assert upretty(cket) == u('❘1,0,j₁=1,j₂=2⟩') assert latex(cket) == r'{\left|1,0,j_{1}=1,j_{2}=2\right\rangle }' sT(cket, "JzKetCoupled(Integer(1),Integer(0),Tuple(Integer(1), Integer(2)),Tuple(Tuple(Integer(1), Integer(2), Integer(1))))") assert str(cbra) == '<1,0,j1=1,j2=2|' assert pretty(cbra) == '<1,0,j1=1,j2=2|' assert upretty(cbra) == u('⟨1,0,j₁=1,j₂=2❘') assert latex(cbra) == r'{\left\langle 1,0,j_{1}=1,j_{2}=2\right|}' sT(cbra, "JzBraCoupled(Integer(1),Integer(0),Tuple(Integer(1), Integer(2)),Tuple(Tuple(Integer(1), Integer(2), Integer(1))))") assert str(cket_big) == '|1,0,j1=1,j2=2,j3=3,j(1,2)=3>' # TODO: Fix non-unicode pretty printing # i.e. j1,2 -> j(1,2) assert pretty(cket_big) == '|1,0,j1=1,j2=2,j3=3,j1,2=3>' assert upretty(cket_big) == u('❘1,0,j₁=1,j₂=2,j₃=3,j₁,₂=3⟩') assert latex(cket_big) == \ r'{\left|1,0,j_{1}=1,j_{2}=2,j_{3}=3,j_{1,2}=3\right\rangle }' sT(cket_big, "JzKetCoupled(Integer(1),Integer(0),Tuple(Integer(1), Integer(2), Integer(3)),Tuple(Tuple(Integer(1), Integer(2), Integer(3)), Tuple(Integer(1), Integer(3), Integer(1))))") assert str(cbra_big) == '<1,0,j1=1,j2=2,j3=3,j(1,2)=3|' assert pretty(cbra_big) == u('<1,0,j1=1,j2=2,j3=3,j1,2=3|') assert upretty(cbra_big) == u('⟨1,0,j₁=1,j₂=2,j₃=3,j₁,₂=3❘') assert latex(cbra_big) == \ r'{\left\langle 1,0,j_{1}=1,j_{2}=2,j_{3}=3,j_{1,2}=3\right|}' sT(cbra_big, "JzBraCoupled(Integer(1),Integer(0),Tuple(Integer(1), Integer(2), Integer(3)),Tuple(Tuple(Integer(1), Integer(2), Integer(3)), Tuple(Integer(1), Integer(3), Integer(1))))") assert str(rot) == 'R(1,2,3)' assert pretty(rot) == 'R (1,2,3)' assert upretty(rot) == u('ℛ (1,2,3)') assert latex(rot) == r'\mathcal{R}\left(1,2,3\right)' sT(rot, "Rotation(Integer(1),Integer(2),Integer(3))") assert str(bigd) == 'WignerD(1, 2, 3, 4, 5, 6)' ascii_str = \ """\ 1 \n\ D (4,5,6)\n\ 2,3 \ """ ucode_str = \ u("""\ 1 \n\ D (4,5,6)\n\ 2,3 \ """) assert pretty(bigd) == ascii_str assert upretty(bigd) == ucode_str assert latex(bigd) == r'D^{1}_{2,3}\left(4,5,6\right)' sT(bigd, "WignerD(Integer(1), Integer(2), Integer(3), Integer(4), Integer(5), Integer(6))") assert str(smalld) == 'WignerD(1, 2, 3, 0, 4, 0)' ascii_str = \ """\ 1 \n\ d (4)\n\ 2,3 \ """ ucode_str = \ u("""\ 1 \n\ d (4)\n\ 2,3 \ """) assert pretty(smalld) == ascii_str assert upretty(smalld) == ucode_str assert latex(smalld) == r'd^{1}_{2,3}\left(4\right)' sT(smalld, "WignerD(Integer(1), Integer(2), Integer(3), Integer(0), Integer(4), Integer(0))") def test_state(): x = symbols('x') bra = Bra() ket = Ket() bra_tall = Bra(x/2) ket_tall = Ket(x/2) tbra = TimeDepBra() tket = TimeDepKet() assert str(bra) == '' assert pretty(ket) == '|psi>' assert upretty(ket) == u('❘ψ⟩') assert latex(ket) == r'{\left|\psi\right\rangle }' sT(ket, "Ket(Symbol('psi'))") assert str(bra_tall) == '' ascii_str = \ """\ | \\ \n\ |x \\\n\ |- /\n\ |2/ \ """ ucode_str = \ u("""\ │ ╲ \n\ │x ╲\n\ │─ ╱\n\ │2╱ \ """) assert pretty(ket_tall) == ascii_str assert upretty(ket_tall) == ucode_str assert latex(ket_tall) == r'{\left|\frac{x}{2}\right\rangle }' sT(ket_tall, "Ket(Mul(Rational(1, 2), Symbol('x')))") assert str(tbra) == '' assert pretty(tket) == '|psi;t>' assert upretty(tket) == u('❘ψ;t⟩') assert latex(tket) == r'{\left|\psi;t\right\rangle }' sT(tket, "TimeDepKet(Symbol('psi'),Symbol('t'))") def test_tensorproduct(): tp = TensorProduct(JzKet(1, 1), JzKet(1, 0)) assert str(tp) == '|1,1>x|1,0>' assert pretty(tp) == '|1,1>x |1,0>' assert upretty(tp) == u('❘1,1⟩⨂ ❘1,0⟩') assert latex(tp) == \ r'{{\left|1,1\right\rangle }}\otimes {{\left|1,0\right\rangle }}' sT(tp, "TensorProduct(JzKet(Integer(1),Integer(1)), JzKet(Integer(1),Integer(0)))") def test_big_expr(): f = Function('f') x = symbols('x') e1 = Dagger(AntiCommutator(Operator('A') + Operator('B'), Pow(DifferentialOperator(Derivative(f(x), x), f(x)), 3))*TensorProduct(Jz**2, Operator('A') + Operator('B')))*(JzBra(1, 0) + JzBra(1, 1))*(JzKet(0, 0) + JzKet(1, -1)) e2 = Commutator(Jz**2, Operator('A') + Operator('B'))*AntiCommutator(Dagger(Operator('C')*Operator('D')), Operator('E').inv()**2)*Dagger(Commutator(Jz, J2)) e3 = Wigner3j(1, 2, 3, 4, 5, 6)*TensorProduct(Commutator(Operator('A') + Dagger(Operator('B')), Operator('C') + Operator('D')), Jz - J2)*Dagger(OuterProduct(Dagger(JzBra(1, 1)), JzBra(1, 0)))*TensorProduct(JzKetCoupled(1, 1, (1, 1)) + JzKetCoupled(1, 0, (1, 1)), JzKetCoupled(1, -1, (1, 1))) e4 = (ComplexSpace(1)*ComplexSpace(2) + FockSpace()**2)*(L2(Interval( 0, oo)) + HilbertSpace()) assert str(e1) == '(Jz**2)x(Dagger(A) + Dagger(B))*{Dagger(DifferentialOperator(Derivative(f(x), x),f(x)))**3,Dagger(A) + Dagger(B)}*(<1,0| + <1,1|)*(|0,0> + |1,-1>)' ascii_str = \ """\ / 3 \\ \n\ |/ +\\ | \n\ 2 / + +\\ <| /d \\ | + +> \n\ /J \\ x \\A + B /*||DifferentialOperator|--(f(x)),f(x)| | ,A + B |*(<1,0| + <1,1|)*(|0,0> + |1,-1>)\n\ \\ z/ \\\\ \dx / / / \ """ ucode_str = \ u("""\ ⎧ 3 ⎫ \n\ ⎪⎛ †⎞ ⎪ \n\ 2 ⎛ † †⎞ ⎨⎜ ⎛d ⎞ ⎟ † †⎬ \n\ ⎛J ⎞ ⨂ ⎝A + B ⎠⋅⎪⎜DifferentialOperator⎜──(f(x)),f(x)⎟ ⎟ ,A + B ⎪⋅(⟨1,0❘ + ⟨1,1❘)⋅(❘0,0⟩ + ❘1,-1⟩)\n\ ⎝ z⎠ ⎩⎝ ⎝dx ⎠ ⎠ ⎭ \ """) assert pretty(e1) == ascii_str assert upretty(e1) == ucode_str assert latex(e1) == \ r'{\left(J_z\right)^{2}}\otimes \left({A^{\dag} + B^{\dag}}\right) \left\{\left(DifferentialOperator\left(\frac{d}{d x} f{\left (x \right )},f{\left (x \right )}\right)^{\dag}\right)^{3},A^{\dag} + B^{\dag}\right\} \left({\left\langle 1,0\right|} + {\left\langle 1,1\right|}\right) \left({\left|0,0\right\rangle } + {\left|1,-1\right\rangle }\right)' sT(e1, "Mul(TensorProduct(Pow(JzOp(Symbol('J')), Integer(2)), Add(Dagger(Operator(Symbol('A'))), Dagger(Operator(Symbol('B'))))), AntiCommutator(Pow(Dagger(DifferentialOperator(Derivative(Function('f')(Symbol('x')), Symbol('x')),Function('f')(Symbol('x')))), Integer(3)),Add(Dagger(Operator(Symbol('A'))), Dagger(Operator(Symbol('B'))))), Add(JzBra(Integer(1),Integer(0)), JzBra(Integer(1),Integer(1))), Add(JzKet(Integer(0),Integer(0)), JzKet(Integer(1),Integer(-1))))") assert str(e2) == '[Jz**2,A + B]*{E**(-2),Dagger(D)*Dagger(C)}*[J2,Jz]' ascii_str = \ """\ [ 2 ] / -2 + +\\ [ 2 ]\n\ [/J \\ ,A + B]**[J ,J ]\n\ [\\ z/ ] \\ / [ z]\ """ ucode_str = \ u("""\ ⎡ 2 ⎤ ⎧ -2 † †⎫ ⎡ 2 ⎤\n\ ⎢⎛J ⎞ ,A + B⎥⋅⎨E ,D ⋅C ⎬⋅⎢J ,J ⎥\n\ ⎣⎝ z⎠ ⎦ ⎩ ⎭ ⎣ z⎦\ """) assert pretty(e2) == ascii_str assert upretty(e2) == ucode_str assert latex(e2) == \ r'\left[\left(J_z\right)^{2},A + B\right] \left\{\left(E\right)^{-2},D^{\dag} C^{\dag}\right\} \left[J^2,J_z\right]' sT(e2, "Mul(Commutator(Pow(JzOp(Symbol('J')), Integer(2)),Add(Operator(Symbol('A')), Operator(Symbol('B')))), AntiCommutator(Pow(Operator(Symbol('E')), Integer(-2)),Mul(Dagger(Operator(Symbol('D'))), Dagger(Operator(Symbol('C'))))), Commutator(J2Op(Symbol('J')),JzOp(Symbol('J'))))") assert str(e3) == \ "Wigner3j(1, 2, 3, 4, 5, 6)*[Dagger(B) + A,C + D]x(-J2 + Jz)*|1,0><1,1|*(|1,0,j1=1,j2=1> + |1,1,j1=1,j2=1>)x|1,-1,j1=1,j2=1>" ascii_str = \ """\ [ + ] / 2 \\ \n\ /1 3 5\\*[B + A,C + D]x |- J + J |*|1,0><1,1|*(|1,0,j1=1,j2=1> + |1,1,j1=1,j2=1>)x |1,-1,j1=1,j2=1>\n\ | | \\ z/ \n\ \\2 4 6/ \ """ ucode_str = \ u("""\ ⎡ † ⎤ ⎛ 2 ⎞ \n\ ⎛1 3 5⎞⋅⎣B + A,C + D⎦⨂ ⎜- J + J ⎟⋅❘1,0⟩⟨1,1❘⋅(❘1,0,j₁=1,j₂=1⟩ + ❘1,1,j₁=1,j₂=1⟩)⨂ ❘1,-1,j₁=1,j₂=1⟩\n\ ⎜ ⎟ ⎝ z⎠ \n\ ⎝2 4 6⎠ \ """) assert pretty(e3) == ascii_str assert upretty(e3) == ucode_str assert latex(e3) == \ r'\left(\begin{array}{ccc} 1 & 3 & 5 \\ 2 & 4 & 6 \end{array}\right) {\left[B^{\dag} + A,C + D\right]}\otimes \left({- J^2 + J_z}\right) {\left|1,0\right\rangle }{\left\langle 1,1\right|} \left({{\left|1,0,j_{1}=1,j_{2}=1\right\rangle } + {\left|1,1,j_{1}=1,j_{2}=1\right\rangle }}\right)\otimes {{\left|1,-1,j_{1}=1,j_{2}=1\right\rangle }}' sT(e3, "Mul(Wigner3j(Integer(1), Integer(2), Integer(3), Integer(4), Integer(5), Integer(6)), TensorProduct(Commutator(Add(Dagger(Operator(Symbol('B'))), Operator(Symbol('A'))),Add(Operator(Symbol('C')), Operator(Symbol('D')))), Add(Mul(Integer(-1), J2Op(Symbol('J'))), JzOp(Symbol('J')))), OuterProduct(JzKet(Integer(1),Integer(0)),JzBra(Integer(1),Integer(1))), TensorProduct(Add(JzKetCoupled(Integer(1),Integer(0),Tuple(Integer(1), Integer(1)),Tuple(Tuple(Integer(1), Integer(2), Integer(1)))), JzKetCoupled(Integer(1),Integer(1),Tuple(Integer(1), Integer(1)),Tuple(Tuple(Integer(1), Integer(2), Integer(1))))), JzKetCoupled(Integer(1),Integer(-1),Tuple(Integer(1), Integer(1)),Tuple(Tuple(Integer(1), Integer(2), Integer(1))))))") assert str(e4) == '(C(1)*C(2)+F**2)*(L2([0, oo))+H)' ascii_str = \ """\ // 1 2\\ x2\\ / 2 \\\n\ \\\\C x C / + F / x \L + H/\ """ ucode_str = \ u("""\ ⎛⎛ 1 2⎞ ⨂2⎞ ⎛ 2 ⎞\n\ ⎝⎝C ⨂ C ⎠ ⊕ F ⎠ ⨂ ⎝L ⊕ H⎠\ """) assert pretty(e4) == ascii_str assert upretty(e4) == ucode_str assert latex(e4) == \ r'\left(\left(\mathcal{C}^{1}\otimes \mathcal{C}^{2}\right)\oplus {\mathcal{F}}^{\otimes 2}\right)\otimes \left({\mathcal{L}^2}\left( \left[0, \infty\right) \right)\oplus \mathcal{H}\right)' sT(e4, "TensorProductHilbertSpace((DirectSumHilbertSpace(TensorProductHilbertSpace(ComplexSpace(Integer(1)),ComplexSpace(Integer(2))),TensorPowerHilbertSpace(FockSpace(),Integer(2)))),(DirectSumHilbertSpace(L2(Interval(Integer(0), oo, False, True)),HilbertSpace())))") def _test_sho1d(): ad = RaisingOp('a') assert pretty(ad) == u(' \u2020\na ') assert latex(ad) == 'a^{\\dag}' sympy-0.7.4.1/sympy/physics/quantum/tests/test_qexpr.py0000644000175000017500000000235412253362407023517 0ustar georgeskgeorgeskfrom sympy import Symbol, Integer from sympy.physics.quantum.qexpr import QExpr, _qsympify_sequence from sympy.physics.quantum.hilbert import HilbertSpace from sympy.core.containers import Tuple x = Symbol('x') y = Symbol('y') def test_qexpr_new(): q = QExpr(0) assert q.label == (0,) assert q.hilbert_space == HilbertSpace() assert q.is_commutative is False q = QExpr(0, 1) assert q.label == (Integer(0), Integer(1)) q = QExpr._new_rawargs(HilbertSpace(), Integer(0), Integer(1)) assert q.label == (Integer(0), Integer(1)) assert q.hilbert_space == HilbertSpace() def test_qexpr_commutative(): q1 = QExpr(x) q2 = QExpr(y) assert q1.is_commutative is False assert q2.is_commutative is False assert q1*q2 != q2*q1 q = QExpr._new_rawargs(0, 1, HilbertSpace()) assert q.is_commutative is False def test_qexpr_subs(): q1 = QExpr(x, y) assert q1.subs(x, y) == QExpr(y, y) assert q1.subs({x: 1, y: 2}) == QExpr(1, 2) def test_qsympify(): assert _qsympify_sequence([[1, 2], [1, 3]]) == (Tuple(1, 2), Tuple(1, 3)) assert _qsympify_sequence(([1, 2, [3, 4, [2, ]], 1], 3)) == \ (Tuple(1, 2, Tuple(3, 4, Tuple(2,)), 1), 3) assert _qsympify_sequence((1,)) == (1,) sympy-0.7.4.1/sympy/physics/quantum/tests/test_shor.py0000644000175000017500000000160012253362407023324 0ustar georgeskgeorgeskfrom sympy.utilities.pytest import XFAIL from sympy.physics.quantum.qapply import qapply from sympy.physics.quantum.qubit import Qubit from sympy.physics.quantum.shor import ( CMod, continued_fraction, getr ) @XFAIL def test_CMod(): assert qapply(CMod(4, 2, 2)*Qubit(0, 0, 1, 0, 0, 0, 0, 0)) == \ Qubit(0, 0, 1, 0, 0, 0, 0, 0) assert qapply(CMod(5, 5, 7)*Qubit(0, 0, 1, 0, 0, 0, 0, 0, 0, 0)) == \ Qubit(0, 0, 1, 0, 0, 0, 0, 0, 1, 0) assert qapply(CMod(3, 2, 3)*Qubit(0, 1, 0, 0, 0, 0)) == \ Qubit(0, 1, 0, 0, 0, 1) def test_continued_frac(): assert continued_fraction(3245, 10000) == [0, 3, 12, 4, 13] assert continued_fraction(1932, 2568) == [0, 1, 3, 26, 2] assert continued_fraction(6589, 2569) == [2, 1, 1, 3, 2, 1, 3, 1, 23] assert getr(513, 1024, 10) == 2 assert getr(169, 1024, 11) == 6 assert getr(314, 4096, 16) == 13 sympy-0.7.4.1/sympy/physics/quantum/tests/test_circuitutils.py0000644000175000017500000003143512253362407025105 0ustar georgeskgeorgeskfrom sympy import Symbol, Integer, Mul from sympy.utilities import numbered_symbols from sympy.physics.quantum.gate import (X, Y, Z, H, S, T, CNOT, CGate) from sympy.physics.quantum.identitysearch import bfs_identity_search from sympy.physics.quantum.circuitutils import (kmp_table, find_subcircuit, replace_subcircuit, convert_to_symbolic_indices, convert_to_real_indices, random_reduce, random_insert, flatten_ids) def create_gate_sequence(qubit=0): gates = (X(qubit), Y(qubit), Z(qubit), H(qubit)) return gates def test_kmp_table(): word = ('a', 'b', 'c', 'd', 'a', 'b', 'd') expected_table = [-1, 0, 0, 0, 0, 1, 2] assert expected_table == kmp_table(word) word = ('P', 'A', 'R', 'T', 'I', 'C', 'I', 'P', 'A', 'T', 'E', ' ', 'I', 'N', ' ', 'P', 'A', 'R', 'A', 'C', 'H', 'U', 'T', 'E') expected_table = [-1, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0, 0, 0, 0] assert expected_table == kmp_table(word) x = X(0) y = Y(0) z = Z(0) h = H(0) word = (x, y, y, x, z) expected_table = [-1, 0, 0, 0, 1] assert expected_table == kmp_table(word) word = (x, x, y, h, z) expected_table = [-1, 0, 1, 0, 0] assert expected_table == kmp_table(word) def test_find_subcircuit(): x = X(0) y = Y(0) z = Z(0) h = H(0) x1 = X(1) y1 = Y(1) i0 = Symbol('i0') x_i0 = X(i0) y_i0 = Y(i0) z_i0 = Z(i0) h_i0 = H(i0) circuit = (x, y, z) assert find_subcircuit(circuit, (x,)) == 0 assert find_subcircuit(circuit, (x1,)) == -1 assert find_subcircuit(circuit, (y,)) == 1 assert find_subcircuit(circuit, (h,)) == -1 assert find_subcircuit(circuit, Mul(x, h)) == -1 assert find_subcircuit(circuit, Mul(x, y, z)) == 0 assert find_subcircuit(circuit, Mul(y, z)) == 1 assert find_subcircuit(Mul(*circuit), (x, y, z, h)) == -1 assert find_subcircuit(Mul(*circuit), (z, y, x)) == -1 assert find_subcircuit(circuit, (x,), start=2, end=1) == -1 circuit = (x, y, x, y, z) assert find_subcircuit(Mul(*circuit), Mul(x, y, z)) == 2 assert find_subcircuit(circuit, (x,), start=1) == 2 assert find_subcircuit(circuit, (x, y), start=1, end=2) == -1 assert find_subcircuit(Mul(*circuit), (x, y), start=1, end=3) == -1 assert find_subcircuit(circuit, (x, y), start=1, end=4) == 2 assert find_subcircuit(circuit, (x, y), start=2, end=4) == 2 circuit = (x, y, z, x1, x, y, z, h, x, y, x1, x, y, z, h, y1, h) assert find_subcircuit(circuit, (x, y, z, h, y1)) == 11 circuit = (x, y, x_i0, y_i0, z_i0, z) assert find_subcircuit(circuit, (x_i0, y_i0, z_i0)) == 2 circuit = (x_i0, y_i0, z_i0, x_i0, y_i0, h_i0) subcircuit = (x_i0, y_i0, z_i0) result = find_subcircuit(circuit, subcircuit) assert result == 0 def test_replace_subcircuit(): x = X(0) y = Y(0) z = Z(0) h = H(0) cnot = CNOT(1, 0) cgate_z = CGate((0,), Z(1)) # Standard cases circuit = (z, y, x, x) remove = (z, y, x) assert replace_subcircuit(circuit, Mul(*remove)) == (x,) assert replace_subcircuit(circuit, remove + (x,)) == () assert replace_subcircuit(circuit, remove, pos=1) == circuit assert replace_subcircuit(circuit, remove, pos=0) == (x,) assert replace_subcircuit(circuit, (x, x), pos=2) == (z, y) assert replace_subcircuit(circuit, (h,)) == circuit circuit = (x, y, x, y, z) remove = (x, y, z) assert replace_subcircuit(Mul(*circuit), Mul(*remove)) == (x, y) remove = (x, y, x, y) assert replace_subcircuit(circuit, remove) == (z,) circuit = (x, h, cgate_z, h, cnot) remove = (x, h, cgate_z) assert replace_subcircuit(circuit, Mul(*remove), pos=-1) == (h, cnot) assert replace_subcircuit(circuit, remove, pos=1) == circuit remove = (h, h) assert replace_subcircuit(circuit, remove) == circuit remove = (h, cgate_z, h, cnot) assert replace_subcircuit(circuit, remove) == (x,) replace = (h, x) actual = replace_subcircuit(circuit, remove, replace=replace) assert actual == (x, h, x) circuit = (x, y, h, x, y, z) remove = (x, y) replace = (cnot, cgate_z) actual = replace_subcircuit(circuit, remove, replace=Mul(*replace)) assert actual == (cnot, cgate_z, h, x, y, z) actual = replace_subcircuit(circuit, remove, replace=replace, pos=1) assert actual == (x, y, h, cnot, cgate_z, z) def test_convert_to_symbolic_indices(): (x, y, z, h) = create_gate_sequence() i0 = Symbol('i0') exp_map = {i0: Integer(0)} actual, act_map, sndx, gen = convert_to_symbolic_indices((x,)) assert actual == (X(i0),) assert act_map == exp_map expected = (X(i0), Y(i0), Z(i0), H(i0)) exp_map = {i0: Integer(0)} actual, act_map, sndx, gen = convert_to_symbolic_indices((x, y, z, h)) assert actual == expected assert exp_map == act_map (x1, y1, z1, h1) = create_gate_sequence(1) i1 = Symbol('i1') expected = (X(i0), Y(i0), Z(i0), H(i0)) exp_map = {i0: Integer(1)} actual, act_map, sndx, gen = convert_to_symbolic_indices((x1, y1, z1, h1)) assert actual == expected assert act_map == exp_map expected = (X(i0), Y(i0), Z(i0), H(i0), X(i1), Y(i1), Z(i1), H(i1)) exp_map = {i0: Integer(0), i1: Integer(1)} actual, act_map, sndx, gen = convert_to_symbolic_indices((x, y, z, h, x1, y1, z1, h1)) assert actual == expected assert act_map == exp_map exp_map = {i0: Integer(1), i1: Integer(0)} actual, act_map, sndx, gen = convert_to_symbolic_indices(Mul(x1, y1, z1, h1, x, y, z, h)) assert actual == expected assert act_map == exp_map expected = (X(i0), X(i1), Y(i0), Y(i1), Z(i0), Z(i1), H(i0), H(i1)) exp_map = {i0: Integer(0), i1: Integer(1)} actual, act_map, sndx, gen = convert_to_symbolic_indices(Mul(x, x1, y, y1, z, z1, h, h1)) assert actual == expected assert act_map == exp_map exp_map = {i0: Integer(1), i1: Integer(0)} actual, act_map, sndx, gen = convert_to_symbolic_indices((x1, x, y1, y, z1, z, h1, h)) assert actual == expected assert act_map == exp_map cnot_10 = CNOT(1, 0) cnot_01 = CNOT(0, 1) cgate_z_10 = CGate(1, Z(0)) cgate_z_01 = CGate(0, Z(1)) expected = (X(i0), X(i1), Y(i0), Y(i1), Z(i0), Z(i1), H(i0), H(i1), CNOT(i1, i0), CNOT(i0, i1), CGate(i1, Z(i0)), CGate(i0, Z(i1))) exp_map = {i0: Integer(0), i1: Integer(1)} args = (x, x1, y, y1, z, z1, h, h1, cnot_10, cnot_01, cgate_z_10, cgate_z_01) actual, act_map, sndx, gen = convert_to_symbolic_indices(args) assert actual == expected assert act_map == exp_map args = (x1, x, y1, y, z1, z, h1, h, cnot_10, cnot_01, cgate_z_10, cgate_z_01) expected = (X(i0), X(i1), Y(i0), Y(i1), Z(i0), Z(i1), H(i0), H(i1), CNOT(i0, i1), CNOT(i1, i0), CGate(i0, Z(i1)), CGate(i1, Z(i0))) exp_map = {i0: Integer(1), i1: Integer(0)} actual, act_map, sndx, gen = convert_to_symbolic_indices(args) assert actual == expected assert act_map == exp_map args = (cnot_10, h, cgate_z_01, h) expected = (CNOT(i0, i1), H(i1), CGate(i1, Z(i0)), H(i1)) exp_map = {i0: Integer(1), i1: Integer(0)} actual, act_map, sndx, gen = convert_to_symbolic_indices(args) assert actual == expected assert act_map == exp_map args = (cnot_01, h1, cgate_z_10, h1) exp_map = {i0: Integer(0), i1: Integer(1)} actual, act_map, sndx, gen = convert_to_symbolic_indices(args) assert actual == expected assert act_map == exp_map args = (cnot_10, h1, cgate_z_01, h1) expected = (CNOT(i0, i1), H(i0), CGate(i1, Z(i0)), H(i0)) exp_map = {i0: Integer(1), i1: Integer(0)} actual, act_map, sndx, gen = convert_to_symbolic_indices(args) assert actual == expected assert act_map == exp_map i2 = Symbol('i2') ccgate_z = CGate(0, CGate(1, Z(2))) ccgate_x = CGate(1, CGate(2, X(0))) args = (ccgate_z, ccgate_x) expected = (CGate(i0, CGate(i1, Z(i2))), CGate(i1, CGate(i2, X(i0)))) exp_map = {i0: Integer(0), i1: Integer(1), i2: Integer(2)} actual, act_map, sndx, gen = convert_to_symbolic_indices(args) assert actual == expected assert act_map == exp_map ndx_map = {i0: Integer(0)} index_gen = numbered_symbols(prefix='i', start=1) actual, act_map, sndx, gen = convert_to_symbolic_indices(args, qubit_map=ndx_map, start=i0, gen=index_gen) assert actual == expected assert act_map == exp_map i3 = Symbol('i3') cgate_x0_c321 = CGate((3, 2, 1), X(0)) exp_map = {i0: Integer(3), i1: Integer(2), i2: Integer(1), i3: Integer(0)} expected = (CGate((i0, i1, i2), X(i3)),) args = (cgate_x0_c321,) actual, act_map, sndx, gen = convert_to_symbolic_indices(args) assert actual == expected assert act_map == exp_map def test_convert_to_real_indices(): i0 = Symbol('i0') i1 = Symbol('i1') (x, y, z, h) = create_gate_sequence() x_i0 = X(i0) y_i0 = Y(i0) z_i0 = Z(i0) qubit_map = {i0: 0} args = (z_i0, y_i0, x_i0) expected = (z, y, x) actual = convert_to_real_indices(args, qubit_map) assert actual == expected cnot_10 = CNOT(1, 0) cnot_01 = CNOT(0, 1) cgate_z_10 = CGate(1, Z(0)) cgate_z_01 = CGate(0, Z(1)) cnot_i1_i0 = CNOT(i1, i0) cnot_i0_i1 = CNOT(i0, i1) cgate_z_i1_i0 = CGate(i1, Z(i0)) qubit_map = {i0: 0, i1: 1} args = (cnot_i1_i0,) expected = (cnot_10,) actual = convert_to_real_indices(args, qubit_map) assert actual == expected args = (cgate_z_i1_i0,) expected = (cgate_z_10,) actual = convert_to_real_indices(args, qubit_map) assert actual == expected args = (cnot_i0_i1,) expected = (cnot_01,) actual = convert_to_real_indices(args, qubit_map) assert actual == expected qubit_map = {i0: 1, i1: 0} args = (cgate_z_i1_i0,) expected = (cgate_z_01,) actual = convert_to_real_indices(args, qubit_map) assert actual == expected i2 = Symbol('i2') ccgate_z = CGate(i0, CGate(i1, Z(i2))) ccgate_x = CGate(i1, CGate(i2, X(i0))) qubit_map = {i0: 0, i1: 1, i2: 2} args = (ccgate_z, ccgate_x) expected = (CGate(0, CGate(1, Z(2))), CGate(1, CGate(2, X(0)))) actual = convert_to_real_indices(Mul(*args), qubit_map) assert actual == expected qubit_map = {i0: 1, i2: 0, i1: 2} args = (ccgate_x, ccgate_z) expected = (CGate(2, CGate(0, X(1))), CGate(1, CGate(2, Z(0)))) actual = convert_to_real_indices(args, qubit_map) assert actual == expected def test_random_reduce(): x = X(0) y = Y(0) z = Z(0) h = H(0) cnot = CNOT(1, 0) cgate_z = CGate((0,), Z(1)) gate_list = [x, y, z] ids = list(bfs_identity_search(gate_list, 1, max_depth=4)) circuit = (x, y, h, z, cnot) assert random_reduce(circuit, []) == circuit assert random_reduce(circuit, ids) == circuit seq = [2, 11, 9, 3, 5] circuit = (x, y, z, x, y, h) assert random_reduce(circuit, ids, seed=seq) == (x, y, h) circuit = (x, x, y, y, z, z) assert random_reduce(circuit, ids, seed=seq) == (x, x, y, y) seq = [14, 13, 0] assert random_reduce(circuit, ids, seed=seq) == (y, y, z, z) gate_list = [x, y, z, h, cnot, cgate_z] ids = list(bfs_identity_search(gate_list, 2, max_depth=4)) seq = [25] circuit = (x, y, z, y, h, y, h, cgate_z, h, cnot) expected = (x, y, z, cgate_z, h, cnot) assert random_reduce(circuit, ids, seed=seq) == expected circuit = Mul(*circuit) assert random_reduce(circuit, ids, seed=seq) == expected def test_random_insert(): x = X(0) y = Y(0) z = Z(0) h = H(0) cnot = CNOT(1, 0) cgate_z = CGate((0,), Z(1)) choices = [(x, x)] circuit = (y, y) loc, choice = 0, 0 actual = random_insert(circuit, choices, seed=[loc, choice]) assert actual == (x, x, y, y) circuit = (x, y, z, h) choices = [(h, h), (x, y, z)] expected = (x, x, y, z, y, z, h) loc, choice = 1, 1 actual = random_insert(circuit, choices, seed=[loc, choice]) assert actual == expected gate_list = [x, y, z, h, cnot, cgate_z] ids = list(bfs_identity_search(gate_list, 2, max_depth=4)) eq_ids = flatten_ids(ids) circuit = (x, y, h, cnot, cgate_z) expected = (x, z, x, z, x, y, h, cnot, cgate_z) loc, choice = 1, 30 actual = random_insert(circuit, eq_ids, seed=[loc, choice]) assert actual == expected circuit = Mul(*circuit) actual = random_insert(circuit, eq_ids, seed=[loc, choice]) assert actual == expected sympy-0.7.4.1/sympy/physics/quantum/tests/__init__.py0000644000175000017500000000000012253362407023042 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/physics/quantum/__init__.py0000644000175000017500000000273512253362407021721 0ustar georgeskgeorgesk__all__ = [] # The following pattern is used below for importing sub-modules: # # 1. "from foo import *". This imports all the names from foo.__all__ into # this module. But, this does not put those names into the __all__ of # this module. This enables "from sympy.physics.quantum import State" to # work. # 2. "import foo; __all__.extend(foo.__all__)". This adds all the names in # foo.__all__ to the __all__ of this module. The names in __all__ # determine which names are imported when # "from sympy.physics.quantum import *" is done. from . import anticommutator from .anticommutator import * __all__.extend(anticommutator.__all__) from .qapply import __all__ as qap_all from .qapply import * __all__.extend(qap_all) from . import commutator from .commutator import * __all__.extend(commutator.__all__) from . import dagger from .dagger import * __all__.extend(dagger.__all__) from . import hilbert from .hilbert import * __all__.extend(hilbert.__all__) from . import innerproduct from .innerproduct import * __all__.extend(innerproduct.__all__) from . import operator from .operator import * __all__.extend(operator.__all__) from .represent import __all__ as rep_all from .represent import * __all__.extend(rep_all) from . import state from .state import * __all__.extend(state.__all__) from . import tensorproduct from .tensorproduct import * __all__.extend(tensorproduct.__all__) from . import constants from .constants import * __all__.extend(constants.__all__) sympy-0.7.4.1/sympy/physics/quantum/qexpr.py0000644000175000017500000003506612253362407021324 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy import Expr, sympify, Symbol, Matrix from sympy.printing.pretty.stringpict import prettyForm from sympy.core.containers import Tuple from sympy.core.compatibility import is_sequence, string_types, u from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.matrixutils import ( numpy_ndarray, scipy_sparse_matrix, to_sympy, to_numpy, to_scipy_sparse ) __all__ = [ 'QuantumError', 'QExpr' ] #----------------------------------------------------------------------------- # Error handling #----------------------------------------------------------------------------- class QuantumError(Exception): pass def _qsympify_sequence(seq): """Convert elements of a sequence to standard form. This is like sympify, but it performs special logic for arguments passed to QExpr. The following conversions are done: * (list, tuple, Tuple) => _qsympify_sequence each element and convert sequence to a Tuple. * basestring => Symbol * Matrix => Matrix * other => sympify Strings are passed to Symbol, not sympify to make sure that variables like 'pi' are kept as Symbols, not the SymPy built-in number subclasses. Examples ======== >>> from sympy.physics.quantum.qexpr import _qsympify_sequence >>> _qsympify_sequence((1,2,[3,4,[1,]])) (1, 2, (3, 4, (1,))) """ return tuple(__qsympify_sequence_helper(seq)) def __qsympify_sequence_helper(seq): """ Helper function for _qsympify_sequence This function does the actual work. """ #base case. If not a list, do Sympification if not is_sequence(seq): if isinstance(seq, Matrix): return seq elif isinstance(seq, string_types): return Symbol(seq) else: return sympify(seq) # base condition, when seq is QExpr and also # is iterable. if isinstance(seq, QExpr): return seq #if list, recurse on each item in the list result = [__qsympify_sequence_helper(item) for item in seq] return Tuple(*result) #----------------------------------------------------------------------------- # Basic Quantum Expression from which all objects descend #----------------------------------------------------------------------------- class QExpr(Expr): """A base class for all quantum object like operators and states.""" # In sympy, slots are for instance attributes that are computed # dynamically by the __new__ method. They are not part of args, but they # derive from args. # The Hilbert space a quantum Object belongs to. __slots__ = ['hilbert_space'] is_commutative = False # The separator used in printing the label. _label_separator = u('') is_commutative = False def __new__(cls, *args, **old_assumptions): """Construct a new quantum object. Parameters ========== args : tuple The list of numbers or parameters that uniquely specify the quantum object. For a state, this will be its symbol or its set of quantum numbers. Examples ======== >>> from sympy.physics.quantum.qexpr import QExpr >>> q = QExpr(0) >>> q 0 >>> q.label (0,) >>> q.hilbert_space H >>> q.args (0,) >>> q.is_commutative False """ # First compute args and call Expr.__new__ to create the instance args = cls._eval_args(args) if len(args) == 0: args = cls._eval_args(tuple(cls.default_args())) inst = Expr.__new__(cls, *args, **old_assumptions) # Now set the slots on the instance inst.hilbert_space = cls._eval_hilbert_space(args) return inst @classmethod def _new_rawargs(cls, hilbert_space, *args, **old_assumptions): """Create new instance of this class with hilbert_space and args. This is used to bypass the more complex logic in the ``__new__`` method in cases where you already have the exact ``hilbert_space`` and ``args``. This should be used when you are positive these arguments are valid, in their final, proper form and want to optimize the creation of the object. """ obj = Expr.__new__(cls, *args, **old_assumptions) obj.hilbert_space = hilbert_space return obj #------------------------------------------------------------------------- # Properties #------------------------------------------------------------------------- @property def label(self): """The label is the unique set of identifiers for the object. Usually, this will include all of the information about the state *except* the time (in the case of time-dependent objects). This must be a tuple, rather than a Tuple. """ if len(self.args) == 0: # If there is no label specified, return the default return self._eval_args(list(self.default_args())) else: return self.args @property def is_symbolic(self): return True @classmethod def default_args(self): """If no arguments are specified, then this will return a default set of arguments to be run through the constructor. NOTE: Any classes that override this MUST return a tuple of arguments. Should be overidden by subclasses to specify the default arguments for kets and operators """ raise NotImplementedError("No default arguments for this class!") #------------------------------------------------------------------------- # _eval_* methods #------------------------------------------------------------------------- def _eval_adjoint(self): obj = Expr._eval_adjoint(self) if obj is None: obj = Expr.__new__(Dagger, self) if isinstance(obj, QExpr): obj.hilbert_space = self.hilbert_space return obj @classmethod def _eval_args(cls, args): """Process the args passed to the __new__ method. This simply runs args through _qsympify_sequence. """ return _qsympify_sequence(args) @classmethod def _eval_hilbert_space(cls, args): """Compute the Hilbert space instance from the args. """ from sympy.physics.quantum.hilbert import HilbertSpace return HilbertSpace() #------------------------------------------------------------------------- # Printing #------------------------------------------------------------------------- # Utilities for printing: these operate on raw sympy objects def _print_sequence(self, seq, sep, printer, *args): result = [] for item in seq: result.append(printer._print(item, *args)) return sep.join(result) def _print_sequence_pretty(self, seq, sep, printer, *args): pform = printer._print(seq[0], *args) for item in seq[1:]: pform = prettyForm(*pform.right((sep))) pform = prettyForm(*pform.right((printer._print(item, *args)))) return pform # Utilities for printing: these operate prettyForm objects def _print_subscript_pretty(self, a, b): top = prettyForm(*b.left(' '*a.width())) bot = prettyForm(*a.right(' '*b.width())) return prettyForm(binding=prettyForm.POW, *bot.below(top)) def _print_superscript_pretty(self, a, b): return a**b def _print_parens_pretty(self, pform, left='(', right=')'): return prettyForm(*pform.parens(left=left, right=right)) # Printing of labels (i.e. args) def _print_label(self, printer, *args): """Prints the label of the QExpr This method prints self.label, using self._label_separator to separate the elements. This method should not be overridden, instead, override _print_contents to change printing behavior. """ return self._print_sequence( self.label, self._label_separator, printer, *args ) def _print_label_repr(self, printer, *args): return self._print_sequence( self.label, ',', printer, *args ) def _print_label_pretty(self, printer, *args): return self._print_sequence_pretty( self.label, self._label_separator, printer, *args ) def _print_label_latex(self, printer, *args): return self._print_sequence( self.label, self._label_separator, printer, *args ) # Printing of contents (default to label) def _print_contents(self, printer, *args): """Printer for contents of QExpr Handles the printing of any unique identifying contents of a QExpr to print as its contents, such as any variables or quantum numbers. The default is to print the label, which is almost always the args. This should not include printing of any brackets or parenteses. """ return self._print_label(printer, *args) def _print_contents_pretty(self, printer, *args): return self._print_label_pretty(printer, *args) def _print_contents_latex(self, printer, *args): return self._print_label_latex(printer, *args) # Main printing methods def _sympystr(self, printer, *args): """Default printing behavior of QExpr objects Handles the default printing of a QExpr. To add other things to the printing of the object, such as an operator name to operators or brackets to states, the class should override the _print/_pretty/_latex functions directly and make calls to _print_contents where appropriate. This allows things like InnerProduct to easily control its printing the printing of contents. """ return self._print_contents(printer, *args) def _sympyrepr(self, printer, *args): classname = self.__class__.__name__ label = self._print_label_repr(printer, *args) return '%s(%s)' % (classname, label) def _pretty(self, printer, *args): pform = self._print_contents_pretty(printer, *args) return pform def _latex(self, printer, *args): return self._print_contents_latex(printer, *args) #------------------------------------------------------------------------- # Methods from Basic and Expr #------------------------------------------------------------------------- def doit(self, **kw_args): return self def _eval_rewrite(self, pattern, rule, **hints): # TODO: Make Basic.rewrite get the rule using the class name rather # than str(). See L1072 of basic.py. # This will call self.rule(*self.args) for rewriting. 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, pattern): if hasattr(self, rule): rewritten = getattr(self, rule)(*args, **hints) if rewritten is not None: return rewritten return self #------------------------------------------------------------------------- # Represent #------------------------------------------------------------------------- def _represent_default_basis(self, **options): raise NotImplementedError('This object does not have a default basis') def _represent(self, **options): """Represent this object in a given basis. This method dispatches to the actual methods that perform the representation. Subclases of QExpr should define various methods to determine how the object will be represented in various bases. The format of these methods is:: def _represent_BasisName(self, basis, **options): Thus to define how a quantum object is represented in the basis of the operator Position, you would define:: def _represent_Position(self, basis, **options): Usually, basis object will be instances of Operator subclasses, but there is a chance we will relax this in the future to accomodate other types of basis sets that are not associated with an operator. If the ``format`` option is given it can be ("sympy", "numpy", "scipy.sparse"). This will ensure that any matrices that result from representing the object are returned in the appropriate matrix format. Parameters ========== basis : Operator The Operator whose basis functions will be used as the basis for representation. options : dict A dictionary of key/value pairs that give options and hints for the representation, such as the number of basis functions to be used. """ basis = options.pop('basis', None) if basis is None: result = self._represent_default_basis(**options) else: result = dispatch_method(self, '_represent', basis, **options) # If we get a matrix representation, convert it to the right format. format = options.get('format', 'sympy') result = self._format_represent(result, format) return result def _format_represent(self, result, format): if format == 'sympy' and not isinstance(result, Matrix): return to_sympy(result) elif format == 'numpy' and not isinstance(result, numpy_ndarray): return to_numpy(result) elif format == 'scipy.sparse' and \ not isinstance(result, scipy_sparse_matrix): return to_scipy_sparse(result) return result def split_commutative_parts(e): """Split into commutative and non-commutative parts.""" c_part, nc_part = e.args_cnc() c_part = list(c_part) return c_part, nc_part def split_qexpr_parts(e): """Split an expression into Expr and noncommutative QExpr parts.""" expr_part = [] qexpr_part = [] for arg in e.args: if not isinstance(arg, QExpr): expr_part.append(arg) else: qexpr_part.append(arg) return expr_part, qexpr_part def dispatch_method(self, basename, arg, **options): """Dispatch a method to the proper handlers.""" method_name = '%s_%s' % (basename, arg.__class__.__name__) if hasattr(self, method_name): f = getattr(self, method_name) # This can raise and we will allow it to propagate. result = f(arg, **options) if result is not None: return result raise NotImplementedError( "%s.%s can't handle: %r" % (self.__class__.__name__, basename, arg) ) sympy-0.7.4.1/sympy/physics/sho.py0000644000175000017500000000466212253362407017262 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import S, pi, Rational from sympy.functions import assoc_laguerre, sqrt, exp, factorial, factorial2 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)/factorial2(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 = 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) 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 sympy-0.7.4.1/sympy/physics/mechanics/0000755000175000017500000000000012253362407020041 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/physics/mechanics/functions.py0000644000175000017500000011333112253362407022425 0ustar georgeskgeorgeskfrom __future__ import print_function, division __all__ = ['cross', 'dot', 'express', 'time_derivative', 'outer', 'inertia', 'mechanics_printing', 'mprint', 'msprint', 'mpprint', 'mlatex', 'kinematic_equations', 'inertia_of_point_mass', 'get_motion_params', 'partial_velocity', 'linear_momentum', 'angular_momentum', 'kinetic_energy', 'potential_energy', 'Lagrangian'] from sympy.physics.mechanics.essential import (Vector, Dyadic, ReferenceFrame, CoordinateSym, MechanicsStrPrinter, MechanicsPrettyPrinter, MechanicsLatexPrinter, dynamicsymbols, _check_frame, _check_vector) 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, solve, diff, sin, cos, Matrix, Symbol, integrate, \ trigsimp from sympy.core.basic import S 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__ 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__ def express(expr, frame, frame2=None, variables=False): """ Global function for 'express' functionality. Re-expresses a Vector, scalar(sympyfiable) or Dyadic in given frame. Refer to the local methods of Vector and Dyadic for details. If 'variables' is True, then the coordinate variables (CoordinateSym instances) of other frames present in the vector/scalar field or dyadic expression are also substituted in terms of the base scalars of this frame. Parameters ========== expr : Vector/Dyadic/scalar(sympyfiable) The expression to re-express in ReferenceFrame 'frame' frame: ReferenceFrame The reference frame to express expr in frame2 : ReferenceFrame The other frame required for re-expression(only for Dyadic expr) variables : boolean Specifies whether to substitute the coordinate variables present in expr, in terms of those of frame 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) >>> from sympy.physics.mechanics import express >>> express(d, B, N) cos(q)*(B.x|N.x) - sin(q)*(B.y|N.x) >>> express(B.x, N) cos(q)*N.x + sin(q)*N.y >>> express(N[0], B, variables=True) B_x*cos(q(t)) - B_y*sin(q(t)) """ _check_frame(frame) if expr == 0: return S(0) if isinstance(expr, Vector): #Given expr is a Vector if variables: #If variables attribute is True, substitute #the coordinate variables in the Vector frame_list = [x[-1] for x in expr.args] subs_dict = {} for f in frame_list: subs_dict.update(f.variable_map(frame)) expr = expr.subs(subs_dict) #Re-express in this frame outvec = Vector([]) for i, v in enumerate(expr.args): if v[1] != frame: temp = frame.dcm(v[1]) * v[0] if Vector.simp: temp = temp.applyfunc(lambda x: \ trigsimp(x, method='fu')) outvec += Vector([(temp, frame)]) else: outvec += Vector([v]) return outvec if isinstance(expr, Dyadic): if frame2 is None: frame2 = frame _check_frame(frame2) ol = Dyadic(0) for i, v in enumerate(expr.args): ol += express(v[0], frame, variables=variables) * \ (express(v[1], frame, variables=variables) | \ express(v[2], frame2, variables=variables)) return ol else: if variables: #Given expr is a scalar field frame_set = set([]) expr = sympify(expr) #Subsitute all the coordinate variables for x in expr.atoms(): if isinstance(x, CoordinateSym)and x.frame != frame: frame_set.add(x.frame) subs_dict = {} for f in frame_set: subs_dict.update(f.variable_map(frame)) return expr.subs(subs_dict) return expr def time_derivative(expr, frame, order=1): """ Calculate the time derivative of a vector/scalar field function or dyadic expression in given frame. References ========== http://en.wikipedia.org/wiki/Rotating_reference_frame#Time_derivatives_in_the_two_frames Parameters ========== expr : Vector/Dyadic/sympifyable The expression whose time derivative is to be calculated frame : ReferenceFrame The reference frame to calculate the time derivative in order : integer The order of the derivative to be calculated 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) >>> from sympy.physics.mechanics import time_derivative >>> time_derivative(v, N) u1'*N.x >>> time_derivative(u1*A[0], N) N_x*Derivative(u1(t), t) >>> B = N.orientnew('B', 'Axis', [u1, N.z]) >>> from sympy.physics.mechanics import outer >>> d = outer(N.x, N.x) >>> time_derivative(d, B) - u1'*(N.y|N.x) - u1'*(N.x|N.y) """ t = dynamicsymbols._t _check_frame(frame) if order == 0: return expr if order%1 != 0 or order < 0: raise ValueError("Unsupported value of order entered") if isinstance(expr, Vector): outvec = Vector(0) for i, v in enumerate(expr.args): if v[1] == frame: outvec += Vector([(express(v[0], frame, \ variables=True).diff(t), frame)]) else: outvec += time_derivative(Vector([v]), v[1]) + \ (v[1].ang_vel_in(frame) ^ Vector([v])) return time_derivative(outvec, frame, order - 1) if isinstance(expr, Dyadic): ol = Dyadic(0) for i, v in enumerate(expr.args): ol += (v[0].diff(t) * (v[1] | v[2])) ol += (v[0] * (time_derivative(v[1], frame) | v[2])) ol += (v[0] * (v[1] | time_derivative(v[2], frame))) return time_derivative(ol, frame, order - 1) else: return diff(express(expr, frame, variables=True), t, order) def outer(vec1, vec2): """Outer product 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.outer.__doc__ 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 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)) 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 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 """ outstr = msprint(expr, **settings) from sympy.core.compatibility import builtins if (outstr != 'None'): builtins._ = outstr print(outstr) def msprint(expr, **settings): r"""Function for displaying expressions generated in mechanics. Returns the output of mprint() as a string. Parameters ========== expr : valid sympy object SymPy expression to print settings : args Same as print for SymPy Examples ======== >>> from sympy.physics.mechanics import msprint, dynamicsymbols >>> u1, u2 = dynamicsymbols('u1 u2') >>> u2d = dynamicsymbols('u2', level=1) >>> print("%s = %s" % (u1, u2 + u2d)) u1(t) = u2(t) + Derivative(u2(t), t) >>> print("%s = %s" % (msprint(u1), msprint(u2 + u2d))) u1 = u2 + u2' """ pr = MechanicsStrPrinter(settings) return pr.doprint(expr) 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)) 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) 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') def get_motion_params(frame, **kwargs): """ Returns the three motion parameters - (acceleration, velocity, and position) as vectorial functions of time in the given frame. If a higher order differential function is provided, the lower order functions are used as boundary conditions. For example, given the acceleration, the velocity and position parameters are taken as boundary conditions. The values of time at which the boundary conditions are specified are taken from timevalue1(for position boundary condition) and timevalue2(for velocity boundary condition). If any of the boundary conditions are not provided, they are taken to be zero by default (zero vectors, in case of vectorial inputs). If the boundary conditions are also functions of time, they are converted to constants by substituting the time values in the dynamicsymbols._t time Symbol. This function can also be used for calculating rotational motion parameters. Have a look at the Parameters and Examples for more clarity. Parameters ========== frame : ReferenceFrame The frame to express the motion parameters in acceleration : Vector Acceleration of the object/frame as a function of time velocity : Vector Velocity as function of time or as boundary condition of velocity at time = timevalue1 position : Vector Velocity as function of time or as boundary condition of velocity at time = timevalue1 timevalue1 : sympyfiable Value of time for position boundary condition timevalue2 : sympyfiable Value of time for velocity boundary condition Examples ======== >>> from sympy.physics.mechanics import ReferenceFrame, get_motion_params, dynamicsymbols >>> from sympy import symbols >>> R = ReferenceFrame('R') >>> v1, v2, v3 = dynamicsymbols('v1 v2 v3') >>> v = v1*R.x + v2*R.y + v3*R.z >>> get_motion_params(R, position = v) (v1''*R.x + v2''*R.y + v3''*R.z, v1'*R.x + v2'*R.y + v3'*R.z, v1*R.x + v2*R.y + v3*R.z) >>> a, b, c = symbols('a b c') >>> v = a*R.x + b*R.y + c*R.z >>> get_motion_params(R, velocity = v) (0, a*R.x + b*R.y + c*R.z, a*t*R.x + b*t*R.y + c*t*R.z) >>> parameters = get_motion_params(R, acceleration = v) >>> parameters[1] a*t*R.x + b*t*R.y + c*t*R.z >>> parameters[2] a*t**2/2*R.x + b*t**2/2*R.y + c*t**2/2*R.z """ ##Helper functions def _process_vector_differential(vectdiff, condition, \ variable, ordinate, frame): """ Helper function for get_motion methods. Finds derivative of vectdiff wrt variable, and its integral using the specified boundary condition at value of variable = ordinate. Returns a tuple of - (derivative, function and integral) wrt vectdiff """ #Make sure boundary condition is independent of 'variable' if condition != 0: condition = express(condition, frame, variables=True) #Special case of vectdiff == 0 if vectdiff == Vector(0): return (0, 0, condition) #Express vectdiff completely in condition's frame to give vectdiff1 vectdiff1 = express(vectdiff, frame) #Find derivative of vectdiff vectdiff2 = time_derivative(vectdiff, frame) #Integrate and use boundary condition vectdiff0 = Vector(0) lims = (variable, ordinate, variable) for dim in frame: function1 = vectdiff1.dot(dim) abscissa = dim.dot(condition).subs({variable : ordinate}) # Indefinite integral of 'function1' wrt 'variable', using # the given initial condition (ordinate, abscissa). vectdiff0 += (integrate(function1, lims) + abscissa) * dim #Return tuple return (vectdiff2, vectdiff, vectdiff0) ##Function body _check_frame(frame) #Decide mode of operation based on user's input if 'acceleration' in kwargs: mode = 2 elif 'velocity' in kwargs: mode = 1 else: mode = 0 #All the possible parameters in kwargs #Not all are required for every case #If not specified, set to default values(may or may not be used in #calculations) conditions = ['acceleration', 'velocity', 'position', 'timevalue', 'timevalue1', 'timevalue2'] for i, x in enumerate(conditions): if x not in kwargs: if i < 3: kwargs[x] = Vector(0) else: kwargs[x] = S(0) elif i < 3: _check_vector(kwargs[x]) else: kwargs[x] = sympify(kwargs[x]) if mode == 2: vel = _process_vector_differential(kwargs['acceleration'], kwargs['velocity'], dynamicsymbols._t, kwargs['timevalue2'], frame)[2] pos = _process_vector_differential(vel, kwargs['position'], dynamicsymbols._t, kwargs['timevalue1'], frame)[2] return (kwargs['acceleration'], vel, pos) elif mode == 1: return _process_vector_differential(kwargs['velocity'], kwargs['position'], dynamicsymbols._t, kwargs['timevalue1'], frame) else: vel = time_derivative(kwargs['position'], frame) acc = time_derivative(vel, frame) return (acc, vel, kwargs['position']) 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 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 = Vector(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 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 = Vector(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 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 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 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) sympy-0.7.4.1/sympy/physics/mechanics/point.py0000644000175000017500000003313012253362407021544 0ustar georgeskgeorgeskfrom __future__ import print_function, division __all__ = ['Point'] from sympy.physics.mechanics.essential import _check_frame, _check_vector from sympy.physics.mechanics.essential import Vector 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 = 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) 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 >>> from sympy.physics.mechanics import Vector, 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) 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) 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 Vector(0) return self._acc_dict[frame] 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') if value == 0: value = Vector(0) value = _check_vector(value) p = Point(name) p.set_pos(self, value) self.set_pos(p, -value) return p 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 = Vector(0) plist = self._pdict_list(otherpoint, 0) for i in range(len(plist) - 1): outvec += plist[i]._pos_dict[plist[i + 1]] return outvec 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 """ if value == 0: value = Vector(0) value = _check_vector(value) _check_frame(frame) self._acc_dict.update({frame: value}) 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 """ if value == 0: value = Vector(0) value = _check_vector(value) self._check_point(otherpoint) self._pos_dict.update({otherpoint: value}) otherpoint._pos_dict.update({self: -value}) 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 """ if value == 0: value = Vector(0) value = _check_vector(value) _check_frame(frame) self._vel_dict.update({frame: value}) 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 >>> from sympy.physics.mechanics import Vector, 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) 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) 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] sympy-0.7.4.1/sympy/physics/mechanics/particle.py0000644000175000017500000001346312253362407022225 0ustar georgeskgeorgeskfrom __future__ import print_function, division __all__ = ['Particle'] from sympy import sympify from sympy.physics.mechanics.point import Point 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__ 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) 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) 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) 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)) 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)) 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 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 sympy-0.7.4.1/sympy/physics/mechanics/lagrange.py0000644000175000017500000003015012253362407022172 0ustar georgeskgeorgeskfrom __future__ import print_function, division __all__ = ['LagrangesMethod'] from sympy import diff, zeros, Matrix, eye, sympify from sympy.physics.mechanics import (dynamicsymbols, ReferenceFrame, Point) 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()) Matrix([[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()) Matrix([[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 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(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 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 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 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(zip(qdd, [0] * len(qdd))) if self.coneqs is not None: lam = self.lam_vec lamzero = dict(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 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) 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) sympy-0.7.4.1/sympy/physics/mechanics/rigidbody.py0000644000175000017500000002165712253362407022402 0ustar georgeskgeorgeskfrom __future__ import print_function, division __all__ = ['RigidBody'] from sympy import sympify from sympy.physics.mechanics.point import Point from sympy.physics.mechanics.essential import ReferenceFrame, Dyadic 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] # 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 # I_S/S* = I_S/O - I_S*/O from sympy.physics.mechanics.functions import inertia_of_point_mass I_Ss_O = inertia_of_point_mass(self.mass, self.masscenter.pos_from(I[1]), self.frame) self._central_inertia = I[0] - I_Ss_O inertia = property(get_inertia, set_inertia) @property def central_inertia(self): """The body's central inertia dyadic.""" return self._central_inertia 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) 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.central_inertia & self.frame.ang_vel_in(frame)) + (point.vel(frame) ^ -self.masscenter.pos_from(point)) * self.mass) 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 are typically defined with respect to an inertial frame but any relevant frame in which the velocities are 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.central_inertia & 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 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 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 sympy-0.7.4.1/sympy/physics/mechanics/essential.py0000644000175000017500000021347212253362407022413 0ustar georgeskgeorgeskfrom __future__ import print_function, division __all__ = ['ReferenceFrame', 'Vector', 'Dyadic', 'dynamicsymbols', 'MechanicsStrPrinter', 'MechanicsPrettyPrinter', 'MechanicsLatexPrinter', 'CoordinateSym'] from sympy import ( Symbol, sin, cos, eye, trigsimp, diff, sqrt, sympify, expand, zeros, Derivative, Function, symbols, Add, solve, S, ImmutableMatrix as Matrix) from sympy.core import C from sympy.core.compatibility import reduce, u, string_types 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 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 unless creating a zero dyadic. zd = Dyadic(0) 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 = [] if inlist == 0: inlist = [] 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(0) 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(0) 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 """ if other == 0: other = Dyadic(0) 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 unicode(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(u(" + ") + mpp.doprint(ar[i][1]) + u("\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(u(" - ") + mpp.doprint(ar[i][1]) + u("\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 = u("(%s)") % arg_str if arg_str.startswith(u("-")): arg_str = arg_str[1:] str_start = u(" - ") else: str_start = u(" + ") ol.append(str_start + arg_str + u(" ") + mpp.doprint(ar[i][1]) + u("\u2a02 ") + mpp.doprint(ar[i][2])) outstr = u("").join(ol) if outstr.startswith(u(" + ")): 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(0) 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(0) 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(0) 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__ 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. Calls the global express function 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) """ from sympy.physics.mechanics import express return express(self, frame1, frame2) 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], Dyadic(0)) def dt(self, frame): """Take the time derivative of this Dyadic in a frame. This function calls the global time_derivative method 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) """ from sympy.physics.mechanics import time_derivative return time_derivative(self, frame) def simplify(self): """Returns a simplified Dyadic.""" out = Dyadic(0) for v in self.args: out += Dyadic([(v[0].simplify(), v[1], v[2])]) return out 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], Dyadic(0)) dot = __and__ cross = __xor__ class CoordinateSym(Symbol): """ A coordinate symbol/base scalar associated wrt a Reference Frame. Ideally, users should not instantiate this class. Instances of this class must only be accessed through the corresponding frame as 'frame[index]'. CoordinateSyms having the same frame and index parameters are equal (even though they may be instantiated separately). Parameters ========== name : string The display name of the CoordinateSym frame : ReferenceFrame The reference frame this base scalar belongs to index : 0, 1 or 2 The index of the dimension denoted by this coordinate variable Examples ======== >>> from sympy.physics.mechanics import ReferenceFrame, CoordinateSym >>> A = ReferenceFrame('A') >>> A[1] A_y >>> type(A[0]) >>> a_y = CoordinateSym('a_y', A, 1) >>> a_y == A[1] True """ def __new__(cls, name, frame, index): obj = super(CoordinateSym, cls).__new__(cls, name) _check_frame(frame) if index not in range(0, 3): raise ValueError("Invalid index specified") obj._id = (frame, index) return obj @property def frame(self): return self._id[0] def __eq__(self, other): #Check if the other object is a CoordinateSym of the same frame #and same index if isinstance(other, CoordinateSym): if other._id == self._id: return True return False def __ne__(self, other): return not self.__eq__(other) def __hash__(self): return tuple((self._id[0].__hash__(), self._id[1])).__hash__() 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, variables=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', indices=('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, string_types): 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, string_types): raise TypeError('Indices must be strings') self.str_vecs = [(name + '[\'' + indices[0] + '\']'), (name + '[\'' + indices[1] + '\']'), (name + '[\'' + indices[2] + '\']')] self.pretty_vecs = [(u("\033[94m\033[1m") + name.lower() + u("_") + indices[0] + u("\033[0;0m\x1b[0;0m")), (u("\033[94m\033[1m") + name.lower() + u("_") + indices[1] + u("\033[0;0m\x1b[0;0m")), (u("\033[94m\033[1m") + name.lower() + u("_") + indices[2] + u("\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 = [(u("\033[94m\033[1m") + name.lower() + u("_x\033[0;0m\x1b[0;0m")), (u("\033[94m\033[1m") + name.lower() + u("_y\033[0;0m\x1b[0;0m")), (u("\033[94m\033[1m") + name.lower() + u("_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, string_types): raise TypeError('Latex entries must be strings') self.latex_vecs = latexs self.name = name self._var_dict = {} #The _dcm_dict dictionary will only store the dcms of parent-child #relationships. The _dcm_cache dictionary will work as the dcm #cache. self._dcm_dict = {} self._dcm_cache = {} 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)]) #Associate coordinate symbols wrt this frame if variables is not None: if not isinstance(variables, (tuple, list)): raise TypeError('Supply the variable names as a list/tuple') if len(variables) != 3: raise ValueError('Supply 3 variable names') for i in variables: if not isinstance(i, string_types): raise TypeError('Variable names must be strings') else: variables = [name + '_x', name + '_y', name + '_z'] self.varlist = (CoordinateSym(variables[0], self, 0), \ CoordinateSym(variables[1], self, 1), \ CoordinateSym(variables[2], self, 2)) def __getitem__(self, ind): """ Returns basis vector for the provided index, if the index is a string. If the index is a number, returns the coordinate variable correspon- -ding to that index. """ if not isinstance(ind, str): if ind < 3: return self.varlist[ind] else: raise ValueError("Invalid index provided") 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 = 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)]) def variable_map(self, otherframe): """ Returns a dictionary which expresses the coordinate variables of this frame in terms of the variables of otherframe. If Vector.simp is True, returns a simplified version of the mapped values. Else, returns them without simplification. Simplification of the expressions may take time. Parameters ========== otherframe : ReferenceFrame The other frame to map the variables to Examples ======== >>> from sympy.physics.mechanics import ReferenceFrame, dynamicsymbols >>> A = ReferenceFrame('A') >>> q = dynamicsymbols('q') >>> B = A.orientnew('B', 'Axis', [q, A.z]) >>> A.variable_map(B) {A_x: B_x*cos(q(t)) - B_y*sin(q(t)), A_y: B_x*sin(q(t)) + B_y*cos(q(t)), A_z: B_z} """ _check_frame(otherframe) if (otherframe, Vector.simp) in self._var_dict: return self._var_dict[(otherframe, Vector.simp)] else: vars_matrix = self.dcm(otherframe) * Matrix(otherframe.varlist) mapping = {} for i, x in enumerate(self): if Vector.simp: mapping[self.varlist[i]] = trigsimp(vars_matrix[i], method='fu') else: mapping[self.varlist[i]] = vars_matrix[i] self._var_dict[(otherframe, Vector.simp)] = mapping return mapping 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) 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(0) for i in range(len(flist) - 1): outvec += flist[i]._ang_vel_dict[flist[i + 1]] return outvec 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) Matrix([ [1, 0, 0], [0, cos(q1), -sin(q1)], [0, sin(q1), cos(q1)]]) """ _check_frame(otherframe) #Check if the dcm wrt that frame has already been calculated if otherframe in self._dcm_cache: return self._dcm_cache[otherframe] flist = self._dict_list(otherframe, 0) outdcm = eye(3) for i in range(len(flist) - 1): outdcm = outdcm * flist[i]._dcm_dict[flist[i + 1]] #After calculation, store the dcm in dcm cache for faster #future retrieval self._dcm_cache[otherframe] = outdcm otherframe._dcm_cache[self] = outdcm.T return outdcm def orient(self, parent, rot_type, amounts, rot_order=''): """Defines the orientation of this frame relative to a parent frame. 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. Supported types are 'Body', 'Space', 'Quaternion', and 'Axis'. See examples for correct usage. 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') #Reset the _dcm_cache of this frame, and remove it from the _dcm_caches #of the frames it is linked to. Also remove it from the _dcm_dict of #its parent frames = self._dcm_cache.keys() for frame in frames: if frame in self._dcm_dict: del frame._dcm_dict[self] del frame._dcm_cache[self] #Add the dcm relationship to _dcm_dict self._dcm_dict = self._dlist[0] = {} self._dcm_dict.update({parent: parent_orient.T}) parent._dcm_dict.update({self: parent_orient}) #Also update the dcm cache after resetting it self._dcm_cache = {} self._dcm_cache.update({parent: parent_orient.T}) parent._dcm_cache.update({self: parent_orient}) 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}) self._var_dict = {} def orientnew(self, newname, rot_type, amounts, rot_order='', variables=None, 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, variables, indices, latexs) newframe.orient(self, rot_type, amounts, rot_order) return newframe orientnew.__doc__ += orient.__doc__ 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 """ if value == 0: value = Vector(0) value = _check_vector(value) _check_frame(otherframe) self._ang_acc_dict.update({otherframe: value}) otherframe._ang_acc_dict.update({self: -value}) 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 """ if value == 0: value = Vector(0) value = _check_vector(value) _check_frame(otherframe) self._ang_vel_dict.update({otherframe: value}) otherframe._ang_vel_dict.update({self: -value}) @property def x(self): """The basis Vector for the ReferenceFrame, in the x direction. """ return self._x @property def y(self): """The basis Vector for the ReferenceFrame, in the y direction. """ return self._y @property def z(self): """The basis Vector for the ReferenceFrame, in the z direction. """ return self._z 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. The only exception is to create a zero vector: zv = Vector(0) """ self.args = [] if inlist == 0: inlist = [] 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: 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. """ if other == 0: other = Vector(0) 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(0) 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 unicode(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(u(" + ") + 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(u(" - ") + 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 = u("(%s)") % arg_str if arg_str[0] == u("-"): arg_str = arg_str[1:] str_start = u(" - ") else: str_start = u(" + ") ol.append(str_start + arg_str + '*' + ar[i][1].pretty_vecs[j]) outstr = u("").join(ol) if outstr.startswith(u(" + ")): 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(0) 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 Vector(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(0) 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__ def dot(self, other): return self & other dot.__doc__ = __and__.__doc__ def cross(self, other): return self ^ other cross.__doc__ = __xor__.__doc__ def outer(self, other): return self | other outer.__doc__ = __or__.__doc__ 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 = Vector(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 def express(self, otherframe, variables=False): """ Returns a Vector equivalent to this one, expressed in otherframe. Uses the global express method. Parameters ========== otherframe : ReferenceFrame The frame for this Vector to be described in variables : boolean If True, the coordinate symbols(if present) in this Vector are re-expressed in terms otherframe 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 """ from sympy.physics.mechanics import express return express(self, otherframe, variables=variables) def doit(self, **hints): """Calls .doit() on each term in the Vector""" ov = Vector(0) for i, v in enumerate(self.args): ov += Vector([(v[0].applyfunc(lambda x: x.doit(**hints)), v[1])]) return ov def dt(self, otherframe): """ Returns a Vector which is the time derivative of the self Vector, taken in frame otherframe. Calls the global time_derivative method Parameters ========== otherframe : ReferenceFrame The frame to calculate the time derivative in """ from sympy.physics.mechanics import time_derivative return time_derivative(self, otherframe) def simplify(self): """Returns a simplified Vector.""" outvec = Vector(0) for i in self.args: outvec += Vector([(i[0].simplify(), i[1])]) return outvec 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 = Vector(0) for i, v in enumerate(self.args): ov += Vector([(v[0].subs(*args, **kwargs), v[1])]) return ov def magnitude(self): """Returns the magnitude (Euclidean norm) of self.""" return sqrt(self & self) 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"" if exp: sup += r"^{%s}" % self._print(exp) 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 = u("") elif dots == 1: dots = u("\u0307") elif dots == 2: dots = u("\u0308") elif dots == 3: dots = u("\u20db") elif dots == 4: dots = u("\u20dc") uni_subs = [u("\u2080"), u("\u2081"), u("\u2082"), u("\u2083"), u("\u2084"), u("\u2085"), u("\u2086"), u("\u2087"), u("\u2088"), u("\u2089"), u("\u208a"), u("\u208b"), u("\u208c"), u("\u208d"), u("\u208e"), u("\u208f"), u("\u2090"), u("\u2091"), u("\u2092"), u("\u2093"), u("\u2094"), u("\u2095"), u("\u2096"), u("\u2097"), u("\u2098"), u("\u2099"), u("\u209a"), u("\u209b"), u("\u209c"), u("\u209d"), u("\u209e"), u("\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 class MechanicsTypeError(TypeError): def __init__(self, other, type_str): super(MechanicsTypeError, self).__init__("Expected an instance of %s, " "instead received an object '%s' of type %s." % ( type_str, other, type(other))) def _check_dyadic(other): if not isinstance(other, Dyadic): raise TypeError('A Dyadic must be supplied') return other def _check_frame(other): if not isinstance(other, ReferenceFrame): raise MechanicsTypeError(other, "ReferenceFrame") def _check_vector(other): if not isinstance(other, Vector): raise TypeError('A Vector must be supplied') return other 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 = '\'' sympy-0.7.4.1/sympy/physics/mechanics/kane.py0000644000175000017500000010740012253362407021333 0ustar georgeskgeorgeskfrom __future__ import print_function, division __all__ = ['KanesMethod'] from sympy import Symbol, zeros, Matrix, diff, solve_linear_system_LU, eye from sympy.core.compatibility import reduce 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) 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 Matrix([[(-c*u(t) - k*q(t))/m]]) >>> KM.linearize()[0] Matrix([ [ 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 ___KDEqError = AttributeError('Create an instance of KanesMethod with' + 'kinematic differential equations to use' + 'this method.') 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 and Map self._k_d = None self._f_d = None self._k_kqdot = None self._k_ku = None self._f_k = None self._qdot_u_map = 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) + str(r1 * i))) temp2 = Matrix(r2, c2, lambda i, j: Symbol('y' + str(j) + str(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 coordinates 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[:, :] C = self._f_nh[:, 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[:, :p] ml1 = B[:, p:o] self._Ars = - self._mat_inv_mul(ml1, mr1) def _partial_velocity(self, vlist, ulist, frame): """Returns the list of partial velocities, replacing qdot's in the velocity list if necessary. """ if self._qdot_u_map is None: raise ___KDEqError v = [vel.subs(self._qdot_u_map) for vel in vlist] return partial_velocity(v, ulist, frame) def kindiffdict(self): """Returns the qdot's in a dictionary. """ if self._qdot_u_map is None: raise ___KDEqError return self._qdot_u_map 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) kdeqs = Matrix(kdeqs) 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)) self._qdot_u_map = solve_linear_system_LU(Matrix([self._k_kqdot.T, -(self._k_ku * Matrix(self._u) + self._f_k).T]).T, self._qdot) self._k_ku = self._mat_inv_mul(k_kqdot, k_ku).subs(uaz) self._f_k = self._mat_inv_mul(k_kqdot, f_k).subs(uaz) 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 = self._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)))) # dictionary of qdot's to u's qdots = dict(list(zip(list(self._qdot_u_map.keys()), list(self._qdot_u_map.values())))) for k, v in list(qdots.items()): qdots[k.diff(t)] = v.diff(t) 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 += [self._partial_velocity([v.masscenter.vel(N), v.frame.ang_vel_in(N)], u, N)] elif isinstance(v, Particle): partials += [self._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() vel = v.masscenter.vel(N).subs(uaz).doit() acc = v.masscenter.acc(N).subs(udotzero).subs(uaz).doit() inertial_force = (M.diff(t) * vel + M * acc) omega = v.frame.ang_vel_in(N).subs(uaz).doit() I = v.central_inertia.subs(uaz).doit() inertial_torque = ((I.dt(v.frame) & omega).subs(uaz).doit() + (I & v.frame.ang_acc_in(N)).subs(udotzero).subs(uaz).doit() + (omega ^ (I & omega)).subs(uaz).doit()) for j in range(o): tmp_vel = partials[i][0][j].subs(uaz).doit() tmp_ang = (I & partials[i][1][j].subs(uaz).doit()) for k in range(o): # translational MM[j, k] += M * (tmp_vel & partials[i][0][k]) # rotational MM[j, k] += (tmp_ang & partials[i][1][k]) nonMM[j] += inertial_force & partials[i][0][j] nonMM[j] += inertial_torque & partials[i][1][j] if isinstance(v, Particle): M = v.mass.subs(uaz).doit() vel = v.point.vel(N).subs(uaz).doit() acc = v.point.acc(N).subs(udotzero).subs(uaz).doit() inertial_force = (M.diff(t) * vel + M * acc) for j in range(o): temp = partials[i][0][j].subs(uaz).doit() for k in range(o): MM[j, k] += M * (temp & partials[i][0][k]) nonMM[j] += inertial_force & partials[i][0][j] # Negate FRSTAR since Kane defines the inertia forces/torques # to be negative and we didn't do so above. MM = MM.subs(qdots).subs(uaz).doit() nonMM = nonMM.subs(qdots).subs(udotzero).subs(uadz).subs(uaz).doit() 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 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 __KDEqError fr = self._form_fr(FL) frstar = self._form_frstar(BL) if self._uaux != []: if self._udep == []: km = KanesMethod(self._inertial, self._q, self._uaux, u_auxiliary=self._uaux) else: km = KanesMethod(self._inertial, self._q, self._uaux, u_auxiliary=self._uaux, u_dependent=self._udep, velocity_constraints=(self._k_nh * Matrix(self._u) + self._f_nh)) km._qdot_u_map = self._qdot_u_map self._km = km 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) 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)) 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]) sympy-0.7.4.1/sympy/physics/mechanics/tests/0000755000175000017500000000000012253362407021203 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/physics/mechanics/tests/test_point.py0000644000175000017500000000674012253362407023754 0ustar georgeskgeorgeskfrom sympy.physics.mechanics import dynamicsymbols, Point, ReferenceFrame def test_point_v1pt_theorys(): q, q2 = dynamicsymbols('q q2') qd, q2d = dynamicsymbols('q q2', 1) qdd, q2dd = dynamicsymbols('q q2', 2) N = ReferenceFrame('N') B = ReferenceFrame('B') B.set_ang_vel(N, qd * B.z) O = Point('O') P = O.locatenew('P', B.x) P.set_vel(B, 0) O.set_vel(N, 0) assert P.v1pt_theory(O, N, B) == qd * B.y O.set_vel(N, N.x) assert P.v1pt_theory(O, N, B) == N.x + qd * B.y P.set_vel(B, B.z) assert P.v1pt_theory(O, N, B) == B.z + N.x + qd * B.y def test_point_a1pt_theorys(): q, q2 = dynamicsymbols('q q2') qd, q2d = dynamicsymbols('q q2', 1) qdd, q2dd = dynamicsymbols('q q2', 2) N = ReferenceFrame('N') B = ReferenceFrame('B') B.set_ang_vel(N, qd * B.z) O = Point('O') P = O.locatenew('P', B.x) P.set_vel(B, 0) O.set_vel(N, 0) assert P.a1pt_theory(O, N, B) == -(qd**2) * B.x + qdd * B.y P.set_vel(B, q2d * B.z) assert P.a1pt_theory(O, N, B) == -(qd**2) * B.x + qdd * B.y + q2dd * B.z O.set_vel(N, q2d * B.x) assert P.a1pt_theory(O, N, B) == ((q2dd - qd**2) * B.x + (q2d * qd + qdd) * B.y + q2dd * B.z) def test_point_v2pt_theorys(): 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', 0) O.set_vel(N, 0) assert P.v2pt_theory(O, N, B) == 0 P = O.locatenew('P', B.x) assert P.v2pt_theory(O, N, B) == (qd * B.z ^ B.x) O.set_vel(N, N.x) assert P.v2pt_theory(O, N, B) == N.x + qd * B.y def test_point_a2pt_theorys(): q = dynamicsymbols('q') qd = dynamicsymbols('q', 1) qdd = dynamicsymbols('q', 2) N = ReferenceFrame('N') B = N.orientnew('B', 'Axis', [q, N.z]) O = Point('O') P = O.locatenew('P', 0) O.set_vel(N, 0) assert P.a2pt_theory(O, N, B) == 0 P.set_pos(O, B.x) assert P.a2pt_theory(O, N, B) == (-qd**2) * B.x + (qdd) * B.y def test_point_funcs(): q, q2 = dynamicsymbols('q q2') qd, q2d = dynamicsymbols('q q2', 1) qdd, q2dd = dynamicsymbols('q q2', 2) N = ReferenceFrame('N') B = ReferenceFrame('B') B.set_ang_vel(N, 5 * B.y) O = Point('O') P = O.locatenew('P', q * B.x) assert P.pos_from(O) == q * B.x P.set_vel(B, qd * B.x + q2d * B.y) assert P.vel(B) == qd * B.x + q2d * B.y O.set_vel(N, 0) assert O.vel(N) == 0 assert P.a1pt_theory(O, N, B) == ((-25 * q + qdd) * B.x + (q2dd) * B.y + (-10 * qd) * B.z) 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) assert O.vel(N) == 5 * N.x assert P.a2pt_theory(O, N, B) == (-10 * qd**2) * B.x + (10 * qdd) * B.y 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) assert P.v1pt_theory(O, N, B) == qd * B.x + q2d * B.y - 5 * q * B.z def test_point_pos(): q = dynamicsymbols('q') N = ReferenceFrame('N') B = N.orientnew('B', 'Axis', [q, N.z]) O = Point('O') P = O.locatenew('P', 10 * N.x + 5 * B.x) assert P.pos_from(O) == 10 * N.x + 5 * B.x Q = P.locatenew('Q', 10 * N.y + 5 * B.y) assert Q.pos_from(P) == 10 * N.y + 5 * B.y assert Q.pos_from(O) == 10 * N.x + 10 * N.y + 5 * B.x + 5 * B.y assert O.pos_from(Q) == -10 * N.x - 10 * N.y - 5 * B.x - 5 * B.y sympy-0.7.4.1/sympy/physics/mechanics/tests/test_functions.py0000644000175000017500000005327112253362407024634 0ustar georgeskgeorgeskfrom sympy import S, Integral, sin, cos, pi, sqrt, symbols from sympy.physics.mechanics import (Dyadic, Particle, Point, ReferenceFrame, RigidBody, Vector) from sympy.physics.mechanics import (angular_momentum, cross, dot, dynamicsymbols, express, time_derivative, inertia, inertia_of_point_mass, kinematic_equations, kinetic_energy, linear_momentum, outer, partial_velocity, potential_energy, get_motion_params) from sympy.utilities.pytest import raises Vector.simp = True q1, q2, q3, q4, q5 = symbols('q1 q2 q3 q4 q5') N = ReferenceFrame('N') A = N.orientnew('A', 'Axis', [q1, N.z]) B = A.orientnew('B', 'Axis', [q2, A.x]) C = B.orientnew('C', 'Axis', [q3, B.y]) def test_dot(): assert dot(A.x, A.x) == 1 assert dot(A.x, A.y) == 0 assert dot(A.x, A.z) == 0 assert dot(A.y, A.x) == 0 assert dot(A.y, A.y) == 1 assert dot(A.y, A.z) == 0 assert dot(A.z, A.x) == 0 assert dot(A.z, A.y) == 0 assert dot(A.z, A.z) == 1 def test_dot_different_frames(): assert dot(N.x, A.x) == cos(q1) assert dot(N.x, A.y) == -sin(q1) assert dot(N.x, A.z) == 0 assert dot(N.y, A.x) == sin(q1) assert dot(N.y, A.y) == cos(q1) assert dot(N.y, A.z) == 0 assert dot(N.z, A.x) == 0 assert dot(N.z, A.y) == 0 assert dot(N.z, A.z) == 1 assert dot(N.x, A.x + A.y) == sqrt(2)*cos(q1 + pi/4) == dot(A.x + A.y, N.x) assert dot(A.x, C.x) == cos(q3) assert dot(A.x, C.y) == 0 assert dot(A.x, C.z) == sin(q3) assert dot(A.y, C.x) == sin(q2)*sin(q3) assert dot(A.y, C.y) == cos(q2) assert dot(A.y, C.z) == -sin(q2)*cos(q3) assert dot(A.z, C.x) == -cos(q2)*sin(q3) assert dot(A.z, C.y) == sin(q2) assert dot(A.z, C.z) == cos(q2)*cos(q3) def test_cross(): assert cross(A.x, A.x) == 0 assert cross(A.x, A.y) == A.z assert cross(A.x, A.z) == -A.y assert cross(A.y, A.x) == -A.z assert cross(A.y, A.y) == 0 assert cross(A.y, A.z) == A.x assert cross(A.z, A.x) == A.y assert cross(A.z, A.y) == -A.x assert cross(A.z, A.z) == 0 def test_cross_different_frames(): assert cross(N.x, A.x) == sin(q1)*A.z assert cross(N.x, A.y) == cos(q1)*A.z assert cross(N.x, A.z) == -sin(q1)*A.x - cos(q1)*A.y assert cross(N.y, A.x) == -cos(q1)*A.z assert cross(N.y, A.y) == sin(q1)*A.z assert cross(N.y, A.z) == cos(q1)*A.x - sin(q1)*A.y assert cross(N.z, A.x) == A.y assert cross(N.z, A.y) == -A.x assert cross(N.z, A.z) == 0 assert cross(N.x, A.x) == sin(q1)*A.z assert cross(N.x, A.y) == cos(q1)*A.z assert cross(N.x, A.x + A.y) == sin(q1)*A.z + cos(q1)*A.z assert cross(A.x + A.y, N.x) == -sin(q1)*A.z - cos(q1)*A.z assert cross(A.x, C.x) == sin(q3)*C.y assert cross(A.x, C.y) == -sin(q3)*C.x + cos(q3)*C.z assert cross(A.x, C.z) == -cos(q3)*C.y assert cross(C.x, A.x) == -sin(q3)*C.y assert cross(C.y, A.x) == sin(q3)*C.x - cos(q3)*C.z assert cross(C.z, A.x) == cos(q3)*C.y def test_operator_match(): """Test that the output of dot, cross, outer functions match operator behavior. """ A = ReferenceFrame('A') v = A.x + A.y d = v | v zerov = Vector(0) zerod = Dyadic(0) # dot products assert d & d == dot(d, d) assert d & zerod == dot(d, zerod) assert zerod & d == dot(zerod, d) assert d & v == dot(d, v) assert v & d == dot(v, d) assert d & zerov == dot(d, zerov) assert zerov & d == dot(zerov, d) raises(TypeError, lambda: dot(d, S(0))) raises(TypeError, lambda: dot(S(0), d)) raises(TypeError, lambda: dot(d, 0)) raises(TypeError, lambda: dot(0, d)) assert v & v == dot(v, v) assert v & zerov == dot(v, zerov) assert zerov & v == dot(zerov, v) raises(TypeError, lambda: dot(v, S(0))) raises(TypeError, lambda: dot(S(0), v)) raises(TypeError, lambda: dot(v, 0)) raises(TypeError, lambda: dot(0, v)) # cross products raises(TypeError, lambda: cross(d, d)) raises(TypeError, lambda: cross(d, zerod)) raises(TypeError, lambda: cross(zerod, d)) assert d ^ v == cross(d, v) assert v ^ d == cross(v, d) assert d ^ zerov == cross(d, zerov) assert zerov ^ d == cross(zerov, d) assert zerov ^ d == cross(zerov, d) raises(TypeError, lambda: cross(d, S(0))) raises(TypeError, lambda: cross(S(0), d)) raises(TypeError, lambda: cross(d, 0)) raises(TypeError, lambda: cross(0, d)) assert v ^ v == cross(v, v) assert v ^ zerov == cross(v, zerov) assert zerov ^ v == cross(zerov, v) raises(TypeError, lambda: cross(v, S(0))) raises(TypeError, lambda: cross(S(0), v)) raises(TypeError, lambda: cross(v, 0)) raises(TypeError, lambda: cross(0, v)) # outer products raises(TypeError, lambda: outer(d, d)) raises(TypeError, lambda: outer(d, zerod)) raises(TypeError, lambda: outer(zerod, d)) raises(TypeError, lambda: outer(d, v)) raises(TypeError, lambda: outer(v, d)) raises(TypeError, lambda: outer(d, zerov)) raises(TypeError, lambda: outer(zerov, d)) raises(TypeError, lambda: outer(zerov, d)) raises(TypeError, lambda: outer(d, S(0))) raises(TypeError, lambda: outer(S(0), d)) raises(TypeError, lambda: outer(d, 0)) raises(TypeError, lambda: outer(0, d)) assert v | v == outer(v, v) assert v | zerov == outer(v, zerov) assert zerov | v == outer(zerov, v) raises(TypeError, lambda: outer(v, S(0))) raises(TypeError, lambda: outer(S(0), v)) raises(TypeError, lambda: outer(v, 0)) raises(TypeError, lambda: outer(0, v)) def test_express(): assert express(A.x, C) == cos(q3)*C.x + sin(q3)*C.z assert express(A.y, C) == sin(q2)*sin(q3)*C.x + cos(q2)*C.y - \ sin(q2)*cos(q3)*C.z assert express(A.z, C) == -sin(q3)*cos(q2)*C.x + sin(q2)*C.y + \ cos(q2)*cos(q3)*C.z assert express(A.x, N) == cos(q1)*N.x + sin(q1)*N.y assert express(A.y, N) == -sin(q1)*N.x + cos(q1)*N.y assert express(A.z, N) == N.z assert express(A.x, A) == A.x assert express(A.y, A) == A.y assert express(A.z, A) == A.z assert express(A.x, B) == B.x assert express(A.y, B) == cos(q2)*B.y - sin(q2)*B.z assert express(A.z, B) == sin(q2)*B.y + cos(q2)*B.z assert express(A.x, C) == cos(q3)*C.x + sin(q3)*C.z assert express(A.y, C) == sin(q2)*sin(q3)*C.x + cos(q2)*C.y - \ sin(q2)*cos(q3)*C.z assert express(A.z, C) == -sin(q3)*cos(q2)*C.x + sin(q2)*C.y + \ cos(q2)*cos(q3)*C.z # Check to make sure UnitVectors get converted properly assert express(N.x, N) == N.x assert express(N.y, N) == N.y assert express(N.z, N) == N.z assert express(N.x, A) == (cos(q1)*A.x - sin(q1)*A.y) assert express(N.y, A) == (sin(q1)*A.x + cos(q1)*A.y) assert express(N.z, A) == A.z assert express(N.x, B) == (cos(q1)*B.x - sin(q1)*cos(q2)*B.y + sin(q1)*sin(q2)*B.z) assert express(N.y, B) == (sin(q1)*B.x + cos(q1)*cos(q2)*B.y - sin(q2)*cos(q1)*B.z) assert express(N.z, B) == (sin(q2)*B.y + cos(q2)*B.z) assert express(N.x, C) == ( (cos(q1)*cos(q3) - sin(q1)*sin(q2)*sin(q3))*C.x - sin(q1)*cos(q2)*C.y + (sin(q3)*cos(q1) + sin(q1)*sin(q2)*cos(q3))*C.z) assert express(N.y, C) == ( (sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1))*C.x + cos(q1)*cos(q2)*C.y + (sin(q1)*sin(q3) - sin(q2)*cos(q1)*cos(q3))*C.z) assert express(N.z, C) == (-sin(q3)*cos(q2)*C.x + sin(q2)*C.y + cos(q2)*cos(q3)*C.z) assert express(A.x, N) == (cos(q1)*N.x + sin(q1)*N.y) assert express(A.y, N) == (-sin(q1)*N.x + cos(q1)*N.y) assert express(A.z, N) == N.z assert express(A.x, A) == A.x assert express(A.y, A) == A.y assert express(A.z, A) == A.z assert express(A.x, B) == B.x assert express(A.y, B) == (cos(q2)*B.y - sin(q2)*B.z) assert express(A.z, B) == (sin(q2)*B.y + cos(q2)*B.z) assert express(A.x, C) == (cos(q3)*C.x + sin(q3)*C.z) assert express(A.y, C) == (sin(q2)*sin(q3)*C.x + cos(q2)*C.y - sin(q2)*cos(q3)*C.z) assert express(A.z, C) == (-sin(q3)*cos(q2)*C.x + sin(q2)*C.y + cos(q2)*cos(q3)*C.z) assert express(B.x, N) == (cos(q1)*N.x + sin(q1)*N.y) assert express(B.y, N) == (-sin(q1)*cos(q2)*N.x + cos(q1)*cos(q2)*N.y + sin(q2)*N.z) assert express(B.z, N) == (sin(q1)*sin(q2)*N.x - sin(q2)*cos(q1)*N.y + cos(q2)*N.z) assert express(B.x, A) == A.x assert express(B.y, A) == (cos(q2)*A.y + sin(q2)*A.z) assert express(B.z, A) == (-sin(q2)*A.y + cos(q2)*A.z) assert express(B.x, B) == B.x assert express(B.y, B) == B.y assert express(B.z, B) == B.z assert express(B.x, C) == (cos(q3)*C.x + sin(q3)*C.z) assert express(B.y, C) == C.y assert express(B.z, C) == (-sin(q3)*C.x + cos(q3)*C.z) assert express(C.x, N) == ( (cos(q1)*cos(q3) - sin(q1)*sin(q2)*sin(q3))*N.x + (sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1))*N.y - sin(q3)*cos(q2)*N.z) assert express(C.y, N) == ( -sin(q1)*cos(q2)*N.x + cos(q1)*cos(q2)*N.y + sin(q2)*N.z) assert express(C.z, N) == ( (sin(q3)*cos(q1) + sin(q1)*sin(q2)*cos(q3))*N.x + (sin(q1)*sin(q3) - sin(q2)*cos(q1)*cos(q3))*N.y + cos(q2)*cos(q3)*N.z) assert express(C.x, A) == (cos(q3)*A.x + sin(q2)*sin(q3)*A.y - sin(q3)*cos(q2)*A.z) assert express(C.y, A) == (cos(q2)*A.y + sin(q2)*A.z) assert express(C.z, A) == (sin(q3)*A.x - sin(q2)*cos(q3)*A.y + cos(q2)*cos(q3)*A.z) assert express(C.x, B) == (cos(q3)*B.x - sin(q3)*B.z) assert express(C.y, B) == B.y assert express(C.z, B) == (sin(q3)*B.x + cos(q3)*B.z) assert express(C.x, C) == C.x assert express(C.y, C) == C.y assert express(C.z, C) == C.z == (C.z) # Check to make sure Vectors get converted back to UnitVectors assert N.x == express((cos(q1)*A.x - sin(q1)*A.y), N) assert N.y == express((sin(q1)*A.x + cos(q1)*A.y), N) assert N.x == express((cos(q1)*B.x - sin(q1)*cos(q2)*B.y + sin(q1)*sin(q2)*B.z), N) assert N.y == express((sin(q1)*B.x + cos(q1)*cos(q2)*B.y - sin(q2)*cos(q1)*B.z), N) assert N.z == express((sin(q2)*B.y + cos(q2)*B.z), N) """ These don't really test our code, they instead test the auto simplification (or lack thereof) of SymPy. assert N.x == express(( (cos(q1)*cos(q3)-sin(q1)*sin(q2)*sin(q3))*C.x - sin(q1)*cos(q2)*C.y + (sin(q3)*cos(q1)+sin(q1)*sin(q2)*cos(q3))*C.z), N) assert N.y == express(( (sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1))*C.x + cos(q1)*cos(q2)*C.y + (sin(q1)*sin(q3) - sin(q2)*cos(q1)*cos(q3))*C.z), N) assert N.z == express((-sin(q3)*cos(q2)*C.x + sin(q2)*C.y + cos(q2)*cos(q3)*C.z), N) """ assert A.x == express((cos(q1)*N.x + sin(q1)*N.y), A) assert A.y == express((-sin(q1)*N.x + cos(q1)*N.y), A) assert A.y == express((cos(q2)*B.y - sin(q2)*B.z), A) assert A.z == express((sin(q2)*B.y + cos(q2)*B.z), A) assert A.x == express((cos(q3)*C.x + sin(q3)*C.z), A) # Tripsimp messes up here too. #print express((sin(q2)*sin(q3)*C.x + cos(q2)*C.y - # sin(q2)*cos(q3)*C.z), A) assert A.y == express((sin(q2)*sin(q3)*C.x + cos(q2)*C.y - sin(q2)*cos(q3)*C.z), A) assert A.z == express((-sin(q3)*cos(q2)*C.x + sin(q2)*C.y + cos(q2)*cos(q3)*C.z), A) assert B.x == express((cos(q1)*N.x + sin(q1)*N.y), B) assert B.y == express((-sin(q1)*cos(q2)*N.x + cos(q1)*cos(q2)*N.y + sin(q2)*N.z), B) assert B.z == express((sin(q1)*sin(q2)*N.x - sin(q2)*cos(q1)*N.y + cos(q2)*N.z), B) assert B.y == express((cos(q2)*A.y + sin(q2)*A.z), B) assert B.z == express((-sin(q2)*A.y + cos(q2)*A.z), B) assert B.x == express((cos(q3)*C.x + sin(q3)*C.z), B) assert B.z == express((-sin(q3)*C.x + cos(q3)*C.z), B) """ assert C.x == express(( (cos(q1)*cos(q3)-sin(q1)*sin(q2)*sin(q3))*N.x + (sin(q1)*cos(q3)+sin(q2)*sin(q3)*cos(q1))*N.y - sin(q3)*cos(q2)*N.z), C) assert C.y == express(( -sin(q1)*cos(q2)*N.x + cos(q1)*cos(q2)*N.y + sin(q2)*N.z), C) assert C.z == express(( (sin(q3)*cos(q1)+sin(q1)*sin(q2)*cos(q3))*N.x + (sin(q1)*sin(q3)-sin(q2)*cos(q1)*cos(q3))*N.y + cos(q2)*cos(q3)*N.z), C) """ assert C.x == express((cos(q3)*A.x + sin(q2)*sin(q3)*A.y - sin(q3)*cos(q2)*A.z), C) assert C.y == express((cos(q2)*A.y + sin(q2)*A.z), C) assert C.z == express((sin(q3)*A.x - sin(q2)*cos(q3)*A.y + cos(q2)*cos(q3)*A.z), C) assert C.x == express((cos(q3)*B.x - sin(q3)*B.z), C) assert C.z == express((sin(q3)*B.x + cos(q3)*B.z), C) def test_time_derivative(): #The use of time_derivative for calculations pertaining to scalar #fields has been tested in test_coordinate_vars in test_essential.py A = ReferenceFrame('A') q = dynamicsymbols('q') qd = dynamicsymbols('q', 1) B = A.orientnew('B', 'Axis', [q, A.z]) d = A.x | A.x assert time_derivative(d, B) == (-qd) * (A.y | A.x) + \ (-qd) * (A.x | A.y) d1 = A.x | B.y assert time_derivative(d1, A) == - qd*(A.x|B.x) assert time_derivative(d1, B) == - qd*(A.y|B.y) d2 = A.x | B.x assert time_derivative(d2, A) == qd*(A.x|B.y) assert time_derivative(d2, B) == - qd*(A.y|B.x) d3 = A.x | B.z assert time_derivative(d3, A) == 0 assert time_derivative(d3, B) == - qd*(A.y|B.z) q1, q2, q3, q4 = dynamicsymbols('q1 q2 q3 q4') q1d, q2d, q3d, q4d = dynamicsymbols('q1 q2 q3 q4', 1) q1dd, q2dd, q3dd, q4dd = dynamicsymbols('q1 q2 q3 q4', 2) C = B.orientnew('C', 'Axis', [q4, B.x]) v1 = q1 * A.z v2 = q2*A.x + q3*B.y v3 = q1*A.x + q2*A.y + q3*A.z assert time_derivative(B.x, C) == 0 assert time_derivative(B.y, C) == - q4d*B.z assert time_derivative(B.z, C) == q4d*B.y assert time_derivative(v1, B) == q1d*A.z assert time_derivative(v1, C) == - q1*sin(q)*q4d*A.x + \ q1*cos(q)*q4d*A.y + q1d*A.z assert time_derivative(v2, A) == q2d*A.x - q3*qd*B.x + q3d*B.y assert time_derivative(v2, C) == q2d*A.x - q2*qd*A.y + \ q2*sin(q)*q4d*A.z + q3d*B.y - q3*q4d*B.z assert time_derivative(v3, B) == (q2*qd + q1d)*A.x + \ (-q1*qd + q2d)*A.y + q3d*A.z assert time_derivative(d, C) == - qd*(A.y|A.x) + \ sin(q)*q4d*(A.z|A.x) - qd*(A.x|A.y) + sin(q)*q4d*(A.x|A.z) def test_get_motion_methods(): #Initialization t = dynamicsymbols._t s1, s2, s3 = symbols('s1 s2 s3') S1, S2, S3 = symbols('S1 S2 S3') S4, S5, S6 = symbols('S4 S5 S6') t1, t2 = symbols('t1 t2') a, b, c = dynamicsymbols('a b c') ad, bd, cd = dynamicsymbols('a b c', 1) a2d, b2d, c2d = dynamicsymbols('a b c', 2) v0 = S1*N.x + S2*N.y + S3*N.z v01 = S4*N.x + S5*N.y + S6*N.z v1 = s1*N.x + s2*N.y + s3*N.z v2 = a*N.x + b*N.y + c*N.z v2d = ad*N.x + bd*N.y + cd*N.z v2dd = a2d*N.x + b2d*N.y + c2d*N.z #Test position parameter assert get_motion_params(frame = N) == (0, 0, 0) assert get_motion_params(N, position=v1) == (0, 0, v1) assert get_motion_params(N, position=v2) == (v2dd, v2d, v2) #Test velocity parameter assert get_motion_params(N, velocity=v1) == (0, v1, v1 * t) assert get_motion_params(N, velocity=v1, position=v0, timevalue1=t1) == \ (0, v1, v0 + v1*(t - t1)) answer = get_motion_params(N, velocity=v1, position=v2, timevalue1=t1) answer_expected = (0, v1, v1*t - v1*t1 + v2.subs(t, t1)) assert answer == answer_expected answer = get_motion_params(N, velocity=v2, position=v0, timevalue1=t1) integral_vector = Integral(a, (t, t1, t))*N.x + Integral(b, (t, t1, t))*N.y \ + Integral(c, (t, t1, t))*N.z answer_expected = (v2d, v2, v0 + integral_vector) assert answer == answer_expected #Test acceleration parameter assert get_motion_params(N, acceleration=v1) == (v1, v1 * t, v1 * t**2/2) assert get_motion_params(N, acceleration=v1, velocity=v0, position=v2, timevalue1=t1, timevalue2=t2) == \ (v1, (v0 + v1*t - v1*t2), -v0*t1 + v1*t**2/2 + v1*t2*t1 - \ v1*t1**2/2 + t*(v0 - v1*t2) + \ v2.subs(t, t1)) assert get_motion_params(N, acceleration=v1, velocity=v0, position=v01, timevalue1=t1, timevalue2=t2) == \ (v1, v0 + v1*t - v1*t2, -v0*t1 + v01 + v1*t**2/2 + \ v1*t2*t1 - v1*t1**2/2 + \ t*(v0 - v1*t2)) answer = get_motion_params(N, acceleration=a*N.x, velocity=S1*N.x, position=S2*N.x, timevalue1=t1, timevalue2=t2) i1 = Integral(a, (t, t2, t)) answer_expected = (a*N.x, (S1 + i1)*N.x, \ (S2 + Integral(S1 + i1, (t, t1, t)))*N.x) assert answer == answer_expected def test_inertia(): N = ReferenceFrame('N') ixx, iyy, izz = symbols('ixx iyy izz') ixy, iyz, izx = symbols('ixy iyz izx') assert inertia(N, ixx, iyy, izz) == (ixx * (N.x | N.x) + iyy * (N.y | N.y) + izz * (N.z | N.z)) assert inertia(N, 0, 0, 0) == 0 * (N.x | N.x) assert inertia(N, ixx, iyy, izz, ixy, iyz, izx) == (ixx * (N.x | N.x) + ixy * (N.x | N.y) + izx * (N.x | N.z) + ixy * (N.y | N.x) + iyy * (N.y | N.y) + iyz * (N.y | N.z) + izx * (N.z | N.x) + iyz * (N.z | N.y) + izz * (N.z | N.z)) def test_kin_eqs(): q0, q1, q2, q3 = dynamicsymbols('q0 q1 q2 q3') q0d, q1d, q2d, q3d = dynamicsymbols('q0 q1 q2 q3', 1) u1, u2, u3 = dynamicsymbols('u1 u2 u3') kds = kinematic_equations([u1, u2, u3], [q0, q1, q2, q3], 'quaternion') assert kds == [-0.5 * q0 * u1 - 0.5 * q2 * u3 + 0.5 * q3 * u2 + q1d, -0.5 * q0 * u2 + 0.5 * q1 * u3 - 0.5 * q3 * u1 + q2d, -0.5 * q0 * u3 - 0.5 * q1 * u2 + 0.5 * q2 * u1 + q3d, 0.5 * q1 * u1 + 0.5 * q2 * u2 + 0.5 * q3 * u3 + q0d] def test_inertia_of_point_mass(): r, s, t, m = symbols('r s t m') N = ReferenceFrame('N') px = r * N.x I = inertia_of_point_mass(m, px, N) assert I == m * r**2 * (N.y | N.y) + m * r**2 * (N.z | N.z) py = s * N.y I = inertia_of_point_mass(m, py, N) assert I == m * s**2 * (N.x | N.x) + m * s**2 * (N.z | N.z) pz = t * N.z I = inertia_of_point_mass(m, pz, N) assert I == m * t**2 * (N.x | N.x) + m * t**2 * (N.y | N.y) p = px + py + pz I = inertia_of_point_mass(m, p, N) assert I == (m * (s**2 + t**2) * (N.x | N.x) - m * r * s * (N.x | N.y) - m * r * t * (N.x | N.z) - m * r * s * (N.y | N.x) + m * (r**2 + t**2) * (N.y | N.y) - m * s * t * (N.y | N.z) - m * r * t * (N.z | N.x) - m * s * t * (N.z | N.y) + m * (r**2 + s**2) * (N.z | N.z)) def test_partial_velocity(): q1, q2, q3, u1, u2, u3 = dynamicsymbols('q1 q2 q3 u1 u2 u3') u4, u5 = dynamicsymbols('u4, u5') r = symbols('r') 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) C = Point('C') C.set_vel(N, u4 * L.x + u5 * (Y.z ^ L.x)) Dmc = C.locatenew('Dmc', r * L.z) Dmc.v2pt_theory(C, N, R) vel_list = [Dmc.vel(N), C.vel(N), R.ang_vel_in(N)] u_list = [u1, u2, u3, u4, u5] assert (partial_velocity(vel_list, u_list, N) == [[- r*L.y, r*L.x, 0, L.x, cos(q2)*L.y - sin(q2)*L.z], [0, 0, 0, L.x, cos(q2)*L.y - sin(q2)*L.z], [L.x, L.y, L.z, 0, 0]]) def test_linear_momentum(): N = ReferenceFrame('N') Ac = Point('Ac') Ac.set_vel(N, 25 * N.y) I = outer(N.x, N.x) A = RigidBody('A', Ac, N, 20, (I, Ac)) P = Point('P') Pa = Particle('Pa', P, 1) Pa.point.set_vel(N, 10 * N.x) assert linear_momentum(N, A, Pa) == 10 * N.x + 500 * N.y def test_angular_momentum_and_linear_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) Pa = Particle('Pa', P, m) I = outer(N.z, N.z) A = RigidBody('A', Ac, a, M, (I, Ac)) assert linear_momentum( N, A, Pa) == 2 * m * q1d* l1 * N.y + M * l1 * q1d * N.y assert angular_momentum( O, N, A, Pa) == 4 * m * q1d * l1**2 * N.z + q1d * N.z def test_kinetic_energy(): m, M, l1 = symbols('m M l1') 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)) assert 0 == kinetic_energy(N, Pa, A) - (M*l1**2*omega**2/2 + 2*l1**2*m*omega**2 + omega**2/2) def test_potential_energy(): 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)) Pa.set_potential_energy(m * g * h) A.set_potential_energy(M * g * H) assert potential_energy(A, Pa) == m * g * h + M * g * H sympy-0.7.4.1/sympy/physics/mechanics/tests/test_particle.py0000644000175000017500000000255712253362407024430 0ustar georgeskgeorgeskfrom sympy import symbols from sympy.physics.mechanics import Point, Particle, ReferenceFrame def test_particle(): m, m2, v1, v2, v3, r, g, h = symbols('m m2 v1 v2 v3 r g h') P = Point('P') P2 = Point('P2') p = Particle('pa', P, m) assert p.mass == m assert p.point == P # Test the mass setter p.mass = m2 assert p.mass == m2 # Test the point setter p.point = P2 assert p.point == P2 # Test the linear momentum function N = ReferenceFrame('N') O = Point('O') P2.set_pos(O, r * N.y) P2.set_vel(N, v1 * N.x) assert p.linear_momentum(N) == m2 * v1 * N.x assert p.angular_momentum(O, N) == -m2 * r *v1 * N.z P2.set_vel(N, v2 * N.y) assert p.linear_momentum(N) == m2 * v2 * N.y assert p.angular_momentum(O, N) == 0 P2.set_vel(N, v3 * N.z) assert p.linear_momentum(N) == m2 * v3 * N.z assert p.angular_momentum(O, N) == m2 * r * v3 * N.x P2.set_vel(N, v1 * N.x + v2 * N.y + v3 * N.z) assert p.linear_momentum(N) == m2 * (v1 * N.x + v2 * N.y + v3 * N.z) assert p.angular_momentum(O, N) == m2 * r * (v3 * N.x - v1 * N.z) p.set_potential_energy(m * g * h) assert p.potential_energy == m * g * h # TODO make the result not be system-dependent assert p.kinetic_energy( N) in [m2*(v1**2 + v2**2 + v3**2)/2, m2 * v1**2 / 2 + m2 * v2**2 / 2 + m2 * v3**2 / 2] sympy-0.7.4.1/sympy/physics/mechanics/tests/test_essential.py0000644000175000017500000004217612253362407024615 0ustar georgeskgeorgeskfrom sympy import cos, Matrix, sin, symbols, pi, S, Function, zeros from sympy.abc import x, y, z from sympy.physics.mechanics import Vector, ReferenceFrame, dot, dynamicsymbols, \ express, time_derivative from sympy.physics.mechanics import Dyadic, CoordinateSym, express from sympy.physics.mechanics.essential import MechanicsLatexPrinter from sympy.utilities.pytest import raises Vector.simp = True A = ReferenceFrame('A') def test_dyadic(): d1 = A.x | A.x d2 = A.y | A.y d3 = A.x | A.y assert d1 * 0 == 0 assert d1 != 0 assert d1 * 2 == 2 * A.x | A.x assert d1 / 2. == 0.5 * d1 assert d1 & (0 * d1) == 0 assert d1 & d2 == 0 assert d1 & A.x == A.x assert d1 ^ A.x == 0 assert d1 ^ A.y == A.x | A.z assert d1 ^ A.z == - A.x | A.y assert d2 ^ A.x == - A.y | A.z assert A.x ^ d1 == 0 assert A.y ^ d1 == - A.z | A.x assert A.z ^ d1 == A.y | A.x assert A.x & d1 == A.x assert A.y & d1 == 0 assert A.y & d2 == A.y assert d1 & d3 == A.x | A.y assert d3 & d1 == 0 assert d1.dt(A) == 0 q = dynamicsymbols('q') qd = dynamicsymbols('q', 1) B = A.orientnew('B', 'Axis', [q, A.z]) assert d1.express(B) == d1.express(B, B) assert d1.express(B) == ((cos(q)**2) * (B.x | B.x) + (-sin(q) * cos(q)) * (B.x | B.y) + (-sin(q) * cos(q)) * (B.y | B.x) + (sin(q)**2) * (B.y | B.y)) assert d1.express(B, A) == (cos(q)) * (B.x | A.x) + (-sin(q)) * (B.y | A.x) assert d1.express(A, B) == (cos(q)) * (A.x | B.x) + (-sin(q)) * (A.x | B.y) assert d1.dt(B) == (-qd) * (A.y | A.x) + (-qd) * (A.x | A.y) def test_coordinate_vars(): """Tests the coordinate variables functionality""" assert CoordinateSym('Ax', A, 0) == A[0] assert CoordinateSym('Ax', A, 1) == A[1] assert CoordinateSym('Ax', A, 2) == A[2] q = dynamicsymbols('q') qd = dynamicsymbols('q', 1) assert isinstance(A[0], CoordinateSym) and \ isinstance(A[0], CoordinateSym) and \ isinstance(A[0], CoordinateSym) assert A.variable_map(A) == {A[0]:A[0], A[1]:A[1], A[2]:A[2]} assert A[0].frame == A B = A.orientnew('B', 'Axis', [q, A.z]) assert B.variable_map(A) == {B[2]: A[2], B[1]: -A[0]*sin(q) + A[1]*cos(q), B[0]: A[0]*cos(q) + A[1]*sin(q)} assert A.variable_map(B) == {A[0]: B[0]*cos(q) - B[1]*sin(q), A[1]: B[0]*sin(q) + B[1]*cos(q), A[2]: B[2]} assert time_derivative(B[0], A) == -A[0]*sin(q)*qd + A[1]*cos(q)*qd assert time_derivative(B[1], A) == -A[0]*cos(q)*qd - A[1]*sin(q)*qd assert time_derivative(B[2], A) == 0 assert express(B[0], A, variables=True) == A[0]*cos(q) + A[1]*sin(q) assert express(B[1], A, variables=True) == -A[0]*sin(q) + A[1]*cos(q) assert express(B[2], A, variables=True) == A[2] assert time_derivative(A[0]*A.x + A[1]*A.y + A[2]*A.z, B) == A[1]*qd*A.x - A[0]*qd*A.y assert time_derivative(B[0]*B.x + B[1]*B.y + B[2]*B.z, A) == - B[1]*qd*B.x + B[0]*qd*B.y assert express(B[0]*B[1]*B[2], A, variables=True) == \ A[2]*(-A[0]*sin(q) + A[1]*cos(q))*(A[0]*cos(q) + A[1]*sin(q)) assert (time_derivative(B[0]*B[1]*B[2], A) - (A[2]*(-A[0]**2*cos(2*q) - 2*A[0]*A[1]*sin(2*q) + A[1]**2*cos(2*q))*qd)).trigsimp() == 0 assert express(B[0]*B.x + B[1]*B.y + B[2]*B.z, A) == \ (B[0]*cos(q) - B[1]*sin(q))*A.x + (B[0]*sin(q) + \ B[1]*cos(q))*A.y + B[2]*A.z assert express(B[0]*B.x + B[1]*B.y + B[2]*B.z, A, variables=True) == \ A[0]*A.x + A[1]*A.y + A[2]*A.z assert express(A[0]*A.x + A[1]*A.y + A[2]*A.z, B) == \ (A[0]*cos(q) + A[1]*sin(q))*B.x + \ (-A[0]*sin(q) + A[1]*cos(q))*B.y + A[2]*B.z assert express(A[0]*A.x + A[1]*A.y + A[2]*A.z, B, variables=True) == \ B[0]*B.x + B[1]*B.y + B[2]*B.z N = B.orientnew('N', 'Axis', [-q, B.z]) assert N.variable_map(A) == {N[0]: A[0], N[2]: A[2], N[1]: A[1]} C = A.orientnew('C', 'Axis', [q, A.x + A.y + A.z]) mapping = A.variable_map(C) assert mapping[A[0]] == 2*C[0]*cos(q)/3 + C[0]/3 - 2*C[1]*sin(q + pi/6)/3 +\ C[1]/3 - 2*C[2]*cos(q + pi/3)/3 + C[2]/3 assert mapping[A[1]] == -2*C[0]*cos(q + pi/3)/3 + \ C[0]/3 + 2*C[1]*cos(q)/3 + C[1]/3 - 2*C[2]*sin(q + pi/6)/3 + C[2]/3 assert mapping[A[2]] == -2*C[0]*sin(q + pi/6)/3 + C[0]/3 - \ 2*C[1]*cos(q + pi/3)/3 + C[1]/3 + 2*C[2]*cos(q)/3 + C[2]/3 def test_ang_vel(): q1, q2, q3, q4 = dynamicsymbols('q1 q2 q3 q4') q1d, q2d, q3d, q4d = dynamicsymbols('q1 q2 q3 q4', 1) N = ReferenceFrame('N') A = N.orientnew('A', 'Axis', [q1, N.z]) B = A.orientnew('B', 'Axis', [q2, A.x]) C = B.orientnew('C', 'Axis', [q3, B.y]) D = N.orientnew('D', 'Axis', [q4, N.y]) u1, u2, u3 = dynamicsymbols('u1 u2 u3') assert A.ang_vel_in(N) == (q1d)*A.z assert B.ang_vel_in(N) == (q2d)*B.x + (q1d)*A.z assert C.ang_vel_in(N) == (q3d)*C.y + (q2d)*B.x + (q1d)*A.z A2 = N.orientnew('A2', 'Axis', [q4, N.y]) assert N.ang_vel_in(N) == 0 assert N.ang_vel_in(A) == -q1d*N.z assert N.ang_vel_in(B) == -q1d*A.z - q2d*B.x assert N.ang_vel_in(C) == -q1d*A.z - q2d*B.x - q3d*B.y assert N.ang_vel_in(A2) == -q4d*N.y assert A.ang_vel_in(N) == q1d*N.z assert A.ang_vel_in(A) == 0 assert A.ang_vel_in(B) == - q2d*B.x assert A.ang_vel_in(C) == - q2d*B.x - q3d*B.y assert A.ang_vel_in(A2) == q1d*N.z - q4d*N.y assert B.ang_vel_in(N) == q1d*A.z + q2d*A.x assert B.ang_vel_in(A) == q2d*A.x assert B.ang_vel_in(B) == 0 assert B.ang_vel_in(C) == -q3d*B.y assert B.ang_vel_in(A2) == q1d*A.z + q2d*A.x - q4d*N.y assert C.ang_vel_in(N) == q1d*A.z + q2d*A.x + q3d*B.y assert C.ang_vel_in(A) == q2d*A.x + q3d*C.y assert C.ang_vel_in(B) == q3d*B.y assert C.ang_vel_in(C) == 0 assert C.ang_vel_in(A2) == q1d*A.z + q2d*A.x + q3d*B.y - q4d*N.y assert A2.ang_vel_in(N) == q4d*A2.y assert A2.ang_vel_in(A) == q4d*A2.y - q1d*N.z assert A2.ang_vel_in(B) == q4d*N.y - q1d*A.z - q2d*A.x assert A2.ang_vel_in(C) == q4d*N.y - q1d*A.z - q2d*A.x - q3d*B.y assert A2.ang_vel_in(A2) == 0 C.set_ang_vel(N, u1*C.x + u2*C.y + u3*C.z) assert C.ang_vel_in(N) == (u1)*C.x + (u2)*C.y + (u3)*C.z assert N.ang_vel_in(C) == (-u1)*C.x + (-u2)*C.y + (-u3)*C.z assert C.ang_vel_in(D) == (u1)*C.x + (u2)*C.y + (u3)*C.z + (-q4d)*D.y assert D.ang_vel_in(C) == (-u1)*C.x + (-u2)*C.y + (-u3)*C.z + (q4d)*D.y q0 = dynamicsymbols('q0') q0d = dynamicsymbols('q0', 1) E = N.orientnew('E', 'Quaternion', (q0, q1, q2, q3)) assert E.ang_vel_in(N) == ( 2 * (q1d * q0 + q2d * q3 - q3d * q2 - q0d * q1) * E.x + 2 * (q2d * q0 + q3d * q1 - q1d * q3 - q0d * q2) * E.y + 2 * (q3d * q0 + q1d * q2 - q2d * q1 - q0d * q3) * E.z) F = N.orientnew('F', 'Body', (q1, q2, q3), '313') assert F.ang_vel_in(N) == ((sin(q2)*sin(q3)*q1d + cos(q3)*q2d)*F.x + (sin(q2)*cos(q3)*q1d - sin(q3)*q2d)*F.y + (cos(q2)*q1d + q3d)*F.z) G = N.orientnew('G', 'Axis', (q1, N.x + N.y)) assert G.ang_vel_in(N) == q1d * (N.x + N.y).normalize() assert N.ang_vel_in(G) == -q1d * (N.x + N.y).normalize() def test_dcm(): q1, q2, q3, q4 = dynamicsymbols('q1 q2 q3 q4') N = ReferenceFrame('N') A = N.orientnew('A', 'Axis', [q1, N.z]) B = A.orientnew('B', 'Axis', [q2, A.x]) C = B.orientnew('C', 'Axis', [q3, B.y]) D = N.orientnew('D', 'Axis', [q4, N.y]) E = N.orientnew('E', 'Space', [q1, q2, q3], '123') assert N.dcm(C) == Matrix([ [- sin(q1) * sin(q2) * sin(q3) + cos(q1) * cos(q3), - sin(q1) * cos(q2), sin(q1) * sin(q2) * cos(q3) + sin(q3) * cos(q1)], [sin(q1) * cos(q3) + sin(q2) * sin(q3) * cos(q1), cos(q1) * cos(q2), sin(q1) * sin(q3) - sin(q2) * cos(q1) * cos(q3)], [- sin(q3) * cos(q2), sin(q2), cos(q2) * cos(q3)]]) # This is a little touchy. Is it ok to use simplify in assert? test_mat = D.dcm(C) - Matrix( [[cos(q1) * cos(q3) * cos(q4) - sin(q3) * (- sin(q4) * cos(q2) + sin(q1) * sin(q2) * cos(q4)), - sin(q2) * sin(q4) - sin(q1) * cos(q2) * cos(q4), sin(q3) * cos(q1) * cos(q4) + cos(q3) * (- sin(q4) * cos(q2) + sin(q1) * sin(q2) * cos(q4))], [sin(q1) * cos(q3) + sin(q2) * sin(q3) * cos(q1), cos(q1) * cos(q2), sin(q1) * sin(q3) - sin(q2) * cos(q1) * cos(q3)], [sin(q4) * cos(q1) * cos(q3) - sin(q3) * (cos(q2) * cos(q4) + sin(q1) * sin(q2) * sin(q4)), sin(q2) * cos(q4) - sin(q1) * sin(q4) * cos(q2), sin(q3) * sin(q4) * cos(q1) + cos(q3) * (cos(q2) * cos(q4) + sin(q1) * sin(q2) * sin(q4))]]) assert test_mat.expand() == zeros(3, 3) assert E.dcm(N) == Matrix( [[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)]]) def test_Vector(): assert A.x != A.y assert A.y != A.z assert A.z != A.x v1 = x*A.x + y*A.y + z*A.z v2 = x**2*A.x + y**2*A.y + z**2*A.z v3 = v1 + v2 v4 = v1 - v2 assert isinstance(v1, Vector) assert dot(v1, A.x) == x assert dot(v1, A.y) == y assert dot(v1, A.z) == z assert isinstance(v2, Vector) assert dot(v2, A.x) == x**2 assert dot(v2, A.y) == y**2 assert dot(v2, A.z) == z**2 assert isinstance(v3, Vector) # We probably shouldn't be using simplify in dot... assert dot(v3, A.x) == x**2 + x assert dot(v3, A.y) == y**2 + y assert dot(v3, A.z) == z**2 + z assert isinstance(v4, Vector) # We probably shouldn't be using simplify in dot... assert dot(v4, A.x) == x - x**2 assert dot(v4, A.y) == y - y**2 assert dot(v4, A.z) == z - z**2 def test_Vector_diffs(): q1, q2, q3, q4 = dynamicsymbols('q1 q2 q3 q4') q1d, q2d, q3d, q4d = dynamicsymbols('q1 q2 q3 q4', 1) q1dd, q2dd, q3dd, q4dd = dynamicsymbols('q1 q2 q3 q4', 2) N = ReferenceFrame('N') A = N.orientnew('A', 'Axis', [q3, N.z]) B = A.orientnew('B', 'Axis', [q2, A.x]) v1 = q2 * A.x + q3 * N.y v2 = q3 * B.x + v1 v3 = v1.dt(B) v4 = v2.dt(B) v5 = q1*A.x + q2*A.y + q3*A.z assert v1.dt(N) == q2d * A.x + q2 * q3d * A.y + q3d * N.y assert v1.dt(A) == q2d * A.x + q3 * q3d * N.x + q3d * N.y assert v1.dt(B) == (q2d * A.x + q3 * q3d * N.x + q3d *\ N.y - q3 * cos(q3) * q2d * N.z) assert v2.dt(N) == (q2d * A.x + (q2 + q3) * q3d * A.y + q3d * B.x + q3d * N.y) assert v2.dt(A) == q2d * A.x + q3d * B.x + q3 * q3d * N.x + q3d * N.y assert v2.dt(B) == (q2d * A.x + q3d * B.x + q3 * q3d * N.x + q3d * N.y - q3 * cos(q3) * q2d * N.z) assert v3.dt(N) == (q2dd * A.x + q2d * q3d * A.y + (q3d**2 + q3 * q3dd) * N.x + q3dd * N.y + (q3 * sin(q3) * q2d * q3d - cos(q3) * q2d * q3d - q3 * cos(q3) * q2dd) * N.z) assert v3.dt(A) == (q2dd * A.x + (2 * q3d**2 + q3 * q3dd) * N.x + (q3dd - q3 * q3d**2) * N.y + (q3 * sin(q3) * q2d * q3d - cos(q3) * q2d * q3d - q3 * cos(q3) * q2dd) * N.z) assert v3.dt(B) == (q2dd * A.x - q3 * cos(q3) * q2d**2 * A.y + (2 * q3d**2 + q3 * q3dd) * N.x + (q3dd - q3 * q3d**2) * N.y + (2 * q3 * sin(q3) * q2d * q3d - 2 * cos(q3) * q2d * q3d - q3 * cos(q3) * q2dd) * N.z) assert v4.dt(N) == (q2dd * A.x + q3d * (q2d + q3d) * A.y + q3dd * B.x + (q3d**2 + q3 * q3dd) * N.x + q3dd * N.y + (q3 * sin(q3) * q2d * q3d - cos(q3) * q2d * q3d - q3 * cos(q3) * q2dd) * N.z) assert v4.dt(A) == (q2dd * A.x + q3dd * B.x + (2 * q3d**2 + q3 * q3dd) * N.x + (q3dd - q3 * q3d**2) * N.y + (q3 * sin(q3) * q2d * q3d - cos(q3) * q2d * q3d - q3 * cos(q3) * q2dd) * N.z) assert v4.dt(B) == (q2dd * A.x - q3 * cos(q3) * q2d**2 * A.y + q3dd * B.x + (2 * q3d**2 + q3 * q3dd) * N.x + (q3dd - q3 * q3d**2) * N.y + (2 * q3 * sin(q3) * q2d * q3d - 2 * cos(q3) * q2d * q3d - q3 * cos(q3) * q2dd) * N.z) assert v5.dt(B) == q1d*A.x + (q3*q2d + q2d)*A.y + (-q2*q2d + q3d)*A.z assert v5.dt(A) == q1d*A.x + q2d*A.y + q3d*A.z assert v5.dt(N) == (-q2*q3d + q1d)*A.x + (q1*q3d + q2d)*A.y + q3d*A.z assert v3.diff(q1d, N) == 0 assert v3.diff(q2d, N) == A.x - q3 * cos(q3) * N.z assert v3.diff(q3d, N) == q3 * N.x + N.y assert v3.diff(q1d, A) == 0 assert v3.diff(q2d, A) == A.x - q3 * cos(q3) * N.z assert v3.diff(q3d, A) == q3 * N.x + N.y assert v3.diff(q1d, B) == 0 assert v3.diff(q2d, B) == A.x - q3 * cos(q3) * N.z assert v3.diff(q3d, B) == q3 * N.x + N.y assert v4.diff(q1d, N) == 0 assert v4.diff(q2d, N) == A.x - q3 * cos(q3) * N.z assert v4.diff(q3d, N) == B.x + q3 * N.x + N.y assert v4.diff(q1d, A) == 0 assert v4.diff(q2d, A) == A.x - q3 * cos(q3) * N.z assert v4.diff(q3d, A) == B.x + q3 * N.x + N.y assert v4.diff(q1d, B) == 0 assert v4.diff(q2d, B) == A.x - q3 * cos(q3) * N.z assert v4.diff(q3d, B) == B.x + q3 * N.x + N.y def test_vector_simplify(): x, y, z, k, n, m, w, f, s, A = symbols('x, y, z, k, n, m, w, f, s, A') N = ReferenceFrame('N') test1 = (1 / x + 1 / y) * N.x assert (test1 & N.x) != (x + y) / (x * y) test1 = test1.simplify() assert (test1 & N.x) == (x + y) / (x * y) test2 = (A**2 * s**4 / (4 * pi * k * m**3)) * N.x test2 = test2.simplify() assert (test2 & N.x) == (A**2 * s**4 / (4 * pi * k * m**3)) test3 = ((4 + 4 * x - 2 * (2 + 2 * x)) / (2 + 2 * x)) * N.x test3 = test3.simplify() assert (test3 & N.x) == 0 test4 = ((-4 * x * y**2 - 2 * y**3 - 2 * x**2 * y) / (x + y)**2) * N.x test4 = test4.simplify() assert (test4 & N.x) == -2 * y def test_dyadic_simplify(): x, y, z, k, n, m, w, f, s, A = symbols('x, y, z, k, n, m, w, f, s, A') N = ReferenceFrame('N') dy = N.x | N.x test1 = (1 / x + 1 / y) * dy assert (N.x & test1 & N.x) != (x + y) / (x * y) test1 = test1.simplify() assert (N.x & test1 & N.x) == (x + y) / (x * y) test2 = (A**2 * s**4 / (4 * pi * k * m**3)) * dy test2 = test2.simplify() assert (N.x & test2 & N.x) == (A**2 * s**4 / (4 * pi * k * m**3)) test3 = ((4 + 4 * x - 2 * (2 + 2 * x)) / (2 + 2 * x)) * dy test3 = test3.simplify() assert (N.x & test3 & N.x) == 0 test4 = ((-4 * x * y**2 - 2 * y**3 - 2 * x**2 * y) / (x + y)**2) * dy test4 = test4.simplify() assert (N.x & test4 & N.x) == -2 * y def test_latex_printer(): r = Function('r')('t') assert MechanicsLatexPrinter().doprint(r**2) == "r^{2}" def test_output_type(): A = ReferenceFrame('A') v = A.x + A.y d = v | v zerov = Vector(0) zerod = Dyadic(0) # dot products assert isinstance(d & d, Dyadic) assert isinstance(d & zerod, Dyadic) assert isinstance(zerod & d, Dyadic) assert isinstance(d & v, Vector) assert isinstance(v & d, Vector) assert isinstance(d & zerov, Vector) assert isinstance(zerov & d, Vector) raises(TypeError, lambda: d & S(0)) raises(TypeError, lambda: S(0) & d) raises(TypeError, lambda: d & 0) raises(TypeError, lambda: 0 & d) assert not isinstance(v & v, (Vector, Dyadic)) assert not isinstance(v & zerov, (Vector, Dyadic)) assert not isinstance(zerov & v, (Vector, Dyadic)) raises(TypeError, lambda: v & S(0)) raises(TypeError, lambda: S(0) & v) raises(TypeError, lambda: v & 0) raises(TypeError, lambda: 0 & v) # cross products raises(TypeError, lambda: d ^ d) raises(TypeError, lambda: d ^ zerod) raises(TypeError, lambda: zerod ^ d) assert isinstance(d ^ v, Dyadic) assert isinstance(v ^ d, Dyadic) assert isinstance(d ^ zerov, Dyadic) assert isinstance(zerov ^ d, Dyadic) assert isinstance(zerov ^ d, Dyadic) raises(TypeError, lambda: d ^ S(0)) raises(TypeError, lambda: S(0) ^ d) raises(TypeError, lambda: d ^ 0) raises(TypeError, lambda: 0 ^ d) assert isinstance(v ^ v, Vector) assert isinstance(v ^ zerov, Vector) assert isinstance(zerov ^ v, Vector) raises(TypeError, lambda: v ^ S(0)) raises(TypeError, lambda: S(0) ^ v) raises(TypeError, lambda: v ^ 0) raises(TypeError, lambda: 0 ^ v) # outer products raises(TypeError, lambda: d | d) raises(TypeError, lambda: d | zerod) raises(TypeError, lambda: zerod | d) raises(TypeError, lambda: d | v) raises(TypeError, lambda: v | d) raises(TypeError, lambda: d | zerov) raises(TypeError, lambda: zerov | d) raises(TypeError, lambda: zerov | d) raises(TypeError, lambda: d | S(0)) raises(TypeError, lambda: S(0) | d) raises(TypeError, lambda: d | 0) raises(TypeError, lambda: 0 | d) assert isinstance(v | v, Dyadic) assert isinstance(v | zerov, Dyadic) assert isinstance(zerov | v, Dyadic) raises(TypeError, lambda: v | S(0)) raises(TypeError, lambda: S(0) | v) raises(TypeError, lambda: v | 0) raises(TypeError, lambda: 0 | v) sympy-0.7.4.1/sympy/physics/mechanics/tests/test_kane.py0000644000175000017500000002443312253362407023540 0ustar georgeskgeorgeskfrom sympy import cos, expand, Matrix, sin, symbols, tan, sqrt, S from sympy.physics.mechanics import (dynamicsymbols, ReferenceFrame, Point, RigidBody, KanesMethod, inertia, Particle, dot) def test_one_dof(): # This is for a 1 dof spring-mass-damper case. # It is described in more detail in the KanesMethod docstring. 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) kd = [qd - u] FL = [(P, (-k * q - c * u) * N.x)] pa = Particle('pa', P, m) BL = [pa] KM = KanesMethod(N, [q], [u], kd) KM.kanes_equations(FL, BL) MM = KM.mass_matrix forcing = KM.forcing rhs = MM.inv() * forcing assert expand(rhs[0]) == expand(-(q * k + u * c) / m) assert KM.linearize() == \ (Matrix([[0, 1], [-k, -c]]), Matrix([]), Matrix([])) def test_two_dof(): # This is for a 2 d.o.f., 2 particle spring-mass-damper. # The first coordinate is the displacement of the first particle, and the # second is the relative displacement between the first and second # particles. Speeds are defined as the time derivatives of the particles. q1, q2, u1, u2 = dynamicsymbols('q1 q2 u1 u2') q1d, q2d, u1d, u2d = dynamicsymbols('q1 q2 u1 u2', 1) m, c1, c2, k1, k2 = symbols('m c1 c2 k1 k2') N = ReferenceFrame('N') P1 = Point('P1') P2 = Point('P2') P1.set_vel(N, u1 * N.x) P2.set_vel(N, (u1 + u2) * N.x) kd = [q1d - u1, q2d - u2] # Now we create the list of forces, then assign properties to each # particle, then create a list of all particles. FL = [(P1, (-k1 * q1 - c1 * u1 + k2 * q2 + c2 * u2) * N.x), (P2, (-k2 * q2 - c2 * u2) * N.x)] pa1 = Particle('pa1', P1, m) pa2 = Particle('pa2', P2, m) BL = [pa1, pa2] # Finally we create the KanesMethod object, specify the inertial frame, # pass relevant information, and form Fr & Fr*. Then we calculate the mass # matrix and forcing terms, and finally solve for the udots. KM = KanesMethod(N, q_ind=[q1, q2], u_ind=[u1, u2], kd_eqs=kd) KM.kanes_equations(FL, BL) MM = KM.mass_matrix forcing = KM.forcing rhs = MM.inv() * forcing assert expand(rhs[0]) == expand((-k1 * q1 - c1 * u1 + k2 * q2 + c2 * u2)/m) assert expand(rhs[1]) == expand((k1 * q1 + c1 * u1 - 2 * k2 * q2 - 2 * c2 * u2) / m) def test_pend(): q, u = dynamicsymbols('q u') qd, ud = dynamicsymbols('q u', 1) m, l, g = symbols('m l g') N = ReferenceFrame('N') P = Point('P') P.set_vel(N, -l * u * sin(q) * N.x + l * u * cos(q) * N.y) kd = [qd - u] FL = [(P, m * g * N.x)] pa = Particle('pa', P, m) BL = [pa] KM = KanesMethod(N, [q], [u], kd) KM.kanes_equations(FL, BL) MM = KM.mass_matrix forcing = KM.forcing rhs = MM.inv() * forcing rhs.simplify() assert expand(rhs[0]) == expand(-g / l * sin(q)) def test_rolling_disc(): # Rolling Disc Example # Here the rolling disc 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). 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') # 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]) w_R_N_qd = R.ang_vel_in(N) R.set_ang_vel(N, u1 * L.x + u2 * L.y + u3 * L.z) # 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) # This is a simple way to form the inertia dyadic. I = inertia(L, m / 4 * r**2, m / 2 * r**2, m / 4 * r**2) # Kinematic differential equations; how the generalized coordinate time # derivatives relate to generalized speeds. kd = [dot(R.ang_vel_in(N) - w_R_N_qd, uv) for uv in L] # Creation of the force list; it is the gravitational force at the mass # center 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 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) 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() assert rhs.expand() == Matrix([(6*u2*u3*r - u3**2*r*tan(q2) + 4*g*sin(q2))/(5*r), -2*u1*u3/3, u1*(-2*u2 + u3*tan(q2))]).expand() # This code tests our output vs. benchmark values. When r=g=m=1, the # critical speed (where all eigenvalues of the linearized equations are 0) # is 1 / sqrt(3) for the upright case. forcing_full = KM.forcing_full forcing_full.simplify() rhs_full = KM.mass_matrix_full.inv() * forcing_full rhs_jac = rhs_full.jacobian([q1, q2, q3, u1, u2, u3]) rhs_jac_subbed = rhs_jac.subs({r: 1, g: 1, m: 1}) rhs_jac_upright = rhs_jac_subbed.subs({q1: 0, q2: 0, q3: 0, u1: 0, u3: 0}) assert rhs_jac_upright.subs(u2, 1 / sqrt(3)).eigenvals() == {S(0): 6} def test_aux(): # Same as above, except we have 2 auxiliary speeds for the ground contact # point, which is known to be zero. In one case, we go through then # substitute the aux. speeds in at the end (they are zero, as well as their # derivative), in the other case, we use the built-in auxiliary speed part # of KanesMethod. The equations from each should be the same. 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) u4, u5, f1, f2 = dynamicsymbols('u4, u5, f1, f2') u4d, u5d = dynamicsymbols('u4, u5', 1) r, m, g = symbols('r m g') 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]) w_R_N_qd = R.ang_vel_in(N) R.set_ang_vel(N, u1 * L.x + u2 * L.y + u3 * L.z) C = Point('C') C.set_vel(N, u4 * L.x + u5 * (Y.z ^ L.x)) Dmc = C.locatenew('Dmc', r * L.z) Dmc.v2pt_theory(C, N, R) Dmc.a2pt_theory(C, N, R) I = inertia(L, m / 4 * r**2, m / 2 * r**2, m / 4 * r**2) kd = [dot(R.ang_vel_in(N) - w_R_N_qd, uv) for uv in L] ForceList = [(Dmc, - m * g * Y.z), (C, f1 * L.x + f2 * (Y.z ^ L.x))] BodyD = RigidBody('BodyD', Dmc, R, m, (I, Dmc)) BodyList = [BodyD] KM = KanesMethod(N, q_ind=[q1, q2, q3], u_ind=[u1, u2, u3, u4, u5], kd_eqs=kd) (fr, frstar) = KM.kanes_equations(ForceList, BodyList) fr = fr.subs({u4d: 0, u5d: 0}).subs({u4: 0, u5: 0}) frstar = frstar.subs({u4d: 0, u5d: 0}).subs({u4: 0, u5: 0}) KM2 = KanesMethod(N, q_ind=[q1, q2, q3], u_ind=[u1, u2, u3], kd_eqs=kd, u_auxiliary=[u4, u5]) (fr2, frstar2) = KM2.kanes_equations(ForceList, BodyList) fr2 = fr2.subs({u4d: 0, u5d: 0}).subs({u4: 0, u5: 0}) frstar2 = frstar2.subs({u4d: 0, u5d: 0}).subs({u4: 0, u5: 0}) frstar.simplify() frstar2.simplify() assert (fr - fr2).expand() == Matrix([0, 0, 0, 0, 0]) assert (frstar - frstar2).expand() == Matrix([0, 0, 0, 0, 0]) def test_parallel_axis(): # This is for a 2 dof inverted pendulum on a cart. # This tests the parallel axis code in KanesMethod. The inertia of the # pendulum is defined about the hinge, not about the center of mass. # Defining the constants and knowns of the system gravity = symbols('g') k, ls = symbols('k ls') a, mA, mC = symbols('a mA mC') F = dynamicsymbols('F') Ix, Iy, Iz = symbols('Ix Iy Iz') # Declaring the Generalized coordinates and speeds q1, q2 = dynamicsymbols('q1 q2') q1d, q2d = dynamicsymbols('q1 q2', 1) u1, u2 = dynamicsymbols('u1 u2') u1d, u2d = dynamicsymbols('u1 u2', 1) # Creating reference frames N = ReferenceFrame('N') A = ReferenceFrame('A') A.orient(N, 'Axis', [-q2, N.z]) A.set_ang_vel(N, -u2 * N.z) # Origin of Newtonian reference frame O = Point('O') # Creating and Locating the positions of the cart, C, and the # center of mass of the pendulum, A C = O.locatenew('C', q1 * N.x) Ao = C.locatenew('Ao', a * A.y) # Defining velocities of the points O.set_vel(N, 0) C.set_vel(N, u1 * N.x) Ao.v2pt_theory(C, N, A) Cart = Particle('Cart', C, mC) Pendulum = RigidBody('Pendulum', Ao, A, mA, (inertia(A, Ix, Iy, Iz), C)) # kinematical differential equations kindiffs = [q1d - u1, q2d - u2] bodyList = [Cart, Pendulum] forceList = [(Ao, -N.y * gravity * mA), (C, -N.y * gravity * mC), (C, -N.x * k * (q1 - ls)), (C, N.x * F)] km = KanesMethod(N, [q1, q2], [u1, u2], kindiffs) (fr, frstar) = km.kanes_equations(forceList, bodyList) mm = km.mass_matrix_full assert mm[3, 3] == Iz sympy-0.7.4.1/sympy/physics/mechanics/tests/test_rigidbody.py0000644000175000017500000000453112253362407024573 0ustar georgeskgeorgeskfrom sympy import symbols from sympy.physics.mechanics import Point, ReferenceFrame, Dyadic, RigidBody from sympy.physics.mechanics import dynamicsymbols, outer from sympy.physics.mechanics import inertia_of_point_mass def test_rigidbody(): m, m2, v1, v2, v3, omega = symbols('m m2 v1 v2 v3 omega') A = ReferenceFrame('A') A2 = ReferenceFrame('A2') P = Point('P') P2 = Point('P2') I = Dyadic(0) I2 = Dyadic(0) B = RigidBody('B', P, A, m, (I, P)) assert B.mass == m assert B.frame == A assert B.masscenter == P assert B.inertia == (I, B.masscenter) B.mass = m2 B.frame = A2 B.masscenter = P2 B.inertia = (I2, B.masscenter) assert B.mass == m2 assert B.frame == A2 assert B.masscenter == P2 assert B.inertia == (I2, B.masscenter) assert B.masscenter == P2 assert B.inertia == (I2, B.masscenter) # Testing linear momentum function assuming A2 is the inertial frame N = ReferenceFrame('N') P2.set_vel(N, v1 * N.x + v2 * N.y + v3 * N.z) assert B.linear_momentum(N) == m2 * (v1 * N.x + v2 * N.y + v3 * N.z) def test_rigidbody2(): M, v, r, omega, g, h = dynamicsymbols('M v r omega g h') N = ReferenceFrame('N') b = ReferenceFrame('b') b.set_ang_vel(N, omega * b.x) P = Point('P') I = outer(b.x, b.x) Inertia_tuple = (I, P) B = RigidBody('B', P, b, M, Inertia_tuple) P.set_vel(N, v * b.x) assert B.angular_momentum(P, N) == omega * b.x O = Point('O') O.set_vel(N, v * b.x) P.set_pos(O, r * b.y) assert B.angular_momentum(O, N) == omega * b.x - M*v*r*b.z B.set_potential_energy(M * g * h) assert B.potential_energy == M * g * h assert B.kinetic_energy(N) == (omega**2 + M * v**2) / 2 def test_rigidbody3(): q1, q2, q3, q4 = dynamicsymbols('q1:5') p1, p2, p3 = symbols('p1:4') m = symbols('m') A = ReferenceFrame('A') B = A.orientnew('B', 'axis', [q1, A.x]) O = Point('O') O.set_vel(A, q2*A.x + q3*A.y + q4*A.z) P = O.locatenew('P', p1*B.x + p2*B.y + p3*B.z) I = outer(B.x, B.x) rb1 = RigidBody('rb1', P, B, m, (I, P)) # I_S/O = I_S/S* + I_S*/O rb2 = RigidBody('rb2', P, B, m, (I + inertia_of_point_mass(m, P.pos_from(O), B), O)) assert rb1.central_inertia == rb2.central_inertia assert rb1.angular_momentum(O, A) == rb2.angular_momentum(O, A) sympy-0.7.4.1/sympy/physics/mechanics/tests/test_lagrange.py0000644000175000017500000002035512253362407024401 0ustar georgeskgeorgeskfrom sympy.physics.mechanics import (dynamicsymbols, ReferenceFrame, Point, RigidBody, LagrangesMethod, Particle, kinetic_energy, dynamicsymbols, inertia, potential_energy, Lagrangian) from sympy import symbols, pi, sin, cos, tan, simplify, expand, Function, \ Derivative def test_disc_on_an_incline_plane(): # Disc rolling on an inclined plane # First the generalized coordinates are created. The mass center of the # disc is located from top vertex of the inclined plane by the generalized # coordinate 'y'. The orientation of the disc is defined by the angle # 'theta'. The mass of the disc is 'm' and its radius is 'R'. The length of # the inclined path is 'l', the angle of inclination is 'alpha'. 'g' is the # gravitational constant. y, theta = dynamicsymbols('y theta') yd, thetad = dynamicsymbols('y theta', 1) m, g, R, l, alpha = symbols('m g R l alpha') # Next, we create the inertial reference frame 'N'. A reference frame 'A' # is attached to the inclined plane. Finally a frame is created which is attached to the disk. N = ReferenceFrame('N') A = N.orientnew('A', 'Axis', [pi/2 - alpha, N.z]) B = A.orientnew('B', 'Axis', [-theta, A.z]) # Creating the disc 'D'; we create the point that represents the mass # center of the disc and set its velocity. The inertia dyadic of the disc # is created. Finally, we create the disc. Do = Point('Do') Do.set_vel(N, yd * A.x) I = m * R**2 / 2 * B.z | B.z D = RigidBody('D', Do, B, m, (I, Do)) # To construct the Lagrangian, 'L', of the disc, we determine its kinetic # and potential energies, T and U, respectively. L is defined as the # difference between T and U. D.set_potential_energy(m * g * (l - y) * sin(alpha)) L = Lagrangian(N, D) # We then create the list of generalized coordinates and constraint # equations. The constraint arises due to the disc rolling without slip on # on the inclined path. Also, the constraint is holonomic but we supply the # differentiated holonomic equation as the 'LagrangesMethod' class requires # that. We then invoke the 'LagrangesMethod' class and supply it the # necessary arguments and generate the equations of motion. The'rhs' method # solves for the q_double_dots (i.e. the second derivative with respect to # time of the generalized coordinates and the lagrange multiplers. q = [y, theta] coneq = [yd - R * thetad] m = LagrangesMethod(L, q, coneq) m.form_lagranges_equations() assert m.rhs()[2] == 2*g*sin(alpha)/3 def test_simp_pen(): # This tests that the equations generated by LagrangesMethod are identical # to those obtained by hand calculations. The system under consideration is # the simple pendulum. # We begin by creating the generalized coordinates as per the requirements # of LagrangesMethod. Also we created the associate symbols # that characterize the system: 'm' is the mass of the bob, l is the length # of the massless rigid rod connecting the bob to a point O fixed in the # inertial frame. q, u = dynamicsymbols('q u') qd, ud = dynamicsymbols('q u ', 1) l, m, g = symbols('l m g') # We then create the inertial frame and a frame attached to the massless # string following which we define the inertial angular velocity of the # string. N = ReferenceFrame('N') A = N.orientnew('A', 'Axis', [q, N.z]) A.set_ang_vel(N, qd * N.z) # Next, we create the point O and fix it in the inertial frame. We then # locate the point P to which the bob is attached. Its corresponding # velocity is then determined by the 'two point formula'. O = Point('O') O.set_vel(N, 0) P = O.locatenew('P', l * A.x) P.v2pt_theory(O, N, A) # The 'Particle' which represents the bob is then created and its # Lagrangian generated. Pa = Particle('Pa', P, m) Pa.set_potential_energy(- m * g * l * cos(q)) L = Lagrangian(N, Pa) # The 'LagrangesMethod' class is invoked to obtain equations of motion. lm = LagrangesMethod(L, [q]) lm.form_lagranges_equations() RHS = lm.rhs() assert RHS[1] == -g*sin(q)/l def test_dub_pen(): # The system considered is the double pendulum. Like in the # test of the simple pendulum above, we begin by creating the generalized # coordinates and the simple generalized speeds and accelerations which # will be used later. Following this we create frames and points necessary # for the kinematics. The procedure isn't explicitly explained as this is # similar to the simple pendulum. Also this is documented on the pydy.org # website. q1, q2 = dynamicsymbols('q1 q2') q1d, q2d = dynamicsymbols('q1 q2', 1) q1dd, q2dd = dynamicsymbols('q1 q2', 2) u1, u2 = dynamicsymbols('u1 u2') u1d, u2d = dynamicsymbols('u1 u2', 1) l, m, g = symbols('l m g') N = ReferenceFrame('N') A = N.orientnew('A', 'Axis', [q1, N.z]) B = N.orientnew('B', 'Axis', [q2, N.z]) A.set_ang_vel(N, q1d * A.z) B.set_ang_vel(N, q2d * A.z) O = Point('O') P = O.locatenew('P', l * A.x) R = P.locatenew('R', l * B.x) O.set_vel(N, 0) P.v2pt_theory(O, N, A) R.v2pt_theory(P, N, B) ParP = Particle('ParP', P, m) ParR = Particle('ParR', R, m) ParP.set_potential_energy(- m * g * l * cos(q1)) ParR.set_potential_energy(- m * g * l * cos(q1) - m * g * l * cos(q2)) L = Lagrangian(N, ParP, ParR) lm = LagrangesMethod(L, [q1, q2]) lm.form_lagranges_equations() assert simplify(l*m*(2*g*sin(q1) + l*sin(q1)*sin(q2)*q2dd + l*sin(q1)*cos(q2)*q2d**2 - l*sin(q2)*cos(q1)*q2d**2 + l*cos(q1)*cos(q2)*q2dd + 2*l*q1dd) - lm.eom[0]) == 0 assert simplify(l*m*(g*sin(q2) + l*sin(q1)*sin(q2)*q1dd - l*sin(q1)*cos(q2)*q1d**2 + l*sin(q2)*cos(q1)*q1d**2 + l*cos(q1)*cos(q2)*q1dd + l*q2dd) - lm.eom[1]) == 0 def test_rolling_disc(): # Rolling Disc Example # 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 need to describe this system, along with the # disc's mass and radius, and the local gravity. 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) # Forming the inertia dyadic. I = inertia(L, m / 4 * r**2, m / 2 * r**2, m / 4 * r**2) BodyD = RigidBody('BodyD', Dmc, R, m, (I, Dmc)) # Finally we form the equations of motion, using the same steps we did # before. Supply the Lagrangian, the generalized speeds. BodyD.set_potential_energy(- m * g * r * cos(q2)) Lag = Lagrangian(N, BodyD) q = [q1, q2, q3] q1 = Function('q1') q2 = Function('q2') q3 = Function('q3') l = LagrangesMethod(Lag, q) l.form_lagranges_equations() RHS = l.rhs() RHS.simplify() t = symbols('t') assert (l.mass_matrix[3:6] == [0, 5*m*r**2/4, 0]) assert RHS[4].simplify() == ( (-8*g*sin(q2(t)) + r*(5*sin(2*q2(t))*Derivative(q1(t), t) + 12*cos(q2(t))*Derivative(q3(t), t))*Derivative(q1(t), t))/(10*r)) assert RHS[5] == (-5*cos(q2(t))*Derivative(q1(t), t) + 6*tan(q2(t) )*Derivative(q3(t), t) + 4*Derivative(q1(t), t)/cos(q2(t)) )*Derivative(q2(t), t) sympy-0.7.4.1/sympy/physics/mechanics/tests/test_kane3.py0000644000175000017500000003360312253362407023622 0ustar georgeskgeorgeskfrom sympy import evalf, symbols, zeros, pi, sin, cos, sqrt, acos, Matrix from sympy.physics.mechanics import (ReferenceFrame, dynamicsymbols, inertia, KanesMethod, RigidBody, Point, dot) from sympy.utilities.pytest import slow @slow def test_bicycle(): # Code to get equations of motion for a bicycle modeled as in: # J.P Meijaard, 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 (2007) 463, 1955-1982 # doi: 10.1098/rspa.2007.1857 # 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 aide debugging. # Declare Coordinates & Speeds # Simple definitions for qdots - qd = u # 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) # Declare System's Parameters 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 mass center -> frame mass center + # frame/fork connection -> fork mass center + front wheel mass center -> # 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 previously defined points, using the 2 - point # theorem (written out by hand here). Accelerations again are calculated # automatically when first needed. WR_cont.set_vel(N, 0) WR_mc.v2pt_theory(WR_cont, N, WR) Steer.v2pt_theory(WR_mc, N, Frame) Frame_mc.v2pt_theory(WR_mc, N, Frame) Fork_mc.v2pt_theory(Steer, N, Fork) WF_mc.v2pt_theory(Steer, N, Fork) WF_cont.v2pt_theory(WF_mc, N, WF) # Sets the inertias of each body. Uses the inertia frame to construct the # inertia dyadics. Wheel inertias are only defined by principle 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) # 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 lineraization process. conlist_coord = [WF_cont.pos_from(WR_cont) & Y.z] # The force list; each body has the appropriate gravitational force applied # at its mass center. 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, as are the speeds. The kinematic # differential equation are also entered here. Here the dependent speeds # are specified, in the same order they were provided in earlier, along # with the non-holonomic constraints. The dependent coordinate is also # provided, with the holonomic constraint. Again, this is only provided # for the linearization process. KM = KanesMethod(N, q_ind=[q1, q2, q5], q_dependent=[q4], configuration_constraints=conlist_coord, u_ind=[u2, u3, u5], u_dependent=[u1, u4, u6], velocity_constraints=conlist_speed, kd_eqs=kd) (fr, frstar) = KM.kanes_equations(FL, BL) # This is the start of entering in the numerical values from the benchmark # paper to validate the eigen values of the linearized equations from this # model to the reference eigen values. 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 = symbols('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} # 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 # centripital 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. forcing_lin = KM.linearize()[0] # 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.mass_matrix_full MM_full_s = MM_full.subs(val_dict) forcing_lin_s = forcing_lin.subs(KM.kindiffdict()).subs(val_dict) MM_full_s = MM_full_s.evalf() forcing_lin_s = forcing_lin_s.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. Amat = MM_full_s.inv() * forcing_lin_s A = Amat.extract([1, 2, 4, 6], [1, 2, 3, 5]) # Precomputed for comparison Res = Matrix([[ 0, 0, 1.0, 0], [ 0, 0, 0, 1.0], [9.48977444677355, -0.891197738059089*v**2 - 0.571523173729245, -0.105522449805691*v, -0.330515398992311*v], [11.7194768719633, -1.97171508499972*v**2 + 30.9087533932407, 3.67680523332152*v, -3.08486552743311*v]]) # Actual eigenvalue comparison eps = 1.e-12 for i in range(6): error = Res.subs(v, i) - A.subs(v, i) assert all(abs(x) < eps for x in error) sympy-0.7.4.1/sympy/physics/mechanics/tests/test_kane2.py0000644000175000017500000004115512253362407023622 0ustar georgeskgeorgeskfrom sympy import cos, expand, Matrix, Poly, simplify, sin, solve, sqrt from sympy import symbols, tan, trigsimp, zeros from sympy.physics.mechanics import (cross, dot, dynamicsymbols, KanesMethod, inertia, inertia_of_point_mass, Particle, Point, ReferenceFrame, RigidBody) def test_aux_dep(): # This test is about rolling disc dynamics, comparing the results found # with KanesMethod to those found when deriving the equations "manually" # with SymPy. # The terms Fr, Fr*, and Fr*_steady are all compared between the two # methods. Here, Fr*_steady refers to the generalized inertia forces for an # equilibrium configuration. # Note: comparing to the test of test_rolling_disc() in test_kane.py, this # test also tests auxiliary speeds and configuration and motion constraints #, seen in the generalized dependent coordinates q[3], and depend speeds # u[3], u[4] and u[5]. # First, mannual derivation of Fr, Fr_star, Fr_star_steady. # Symbols for time and constant parameters. # Symbols for contact forces: Fx, Fy, Fz. t, r, m, g, I, J = symbols('t r m g I J') Fx, Fy, Fz = symbols('Fx Fy Fz') # Configuration variables and their time derivatives: # q[0] -- yaw # q[1] -- lean # q[2] -- spin # q[3] -- dot(-r*B.z, A.z) -- distance from ground plane to disc center in # A.z direction # Generalized speeds and their time derivatives: # u[0] -- disc angular velocity component, disc fixed x direction # u[1] -- disc angular velocity component, disc fixed y direction # u[2] -- disc angular velocity component, disc fixed z direction # u[3] -- disc velocity component, A.x direction # u[4] -- disc velocity component, A.y direction # u[5] -- disc velocity component, A.z direction # Auxiliary generalized speeds: # ua[0] -- contact point auxiliary generalized speed, A.x direction # ua[1] -- contact point auxiliary generalized speed, A.y direction # ua[2] -- contact point auxiliary generalized speed, A.z direction q = dynamicsymbols('q:4') qd = [qi.diff(t) for qi in q] u = dynamicsymbols('u:6') ud = [ui.diff(t) for ui in u] #ud_zero = {udi : 0 for udi in ud} ud_zero = dict(zip(ud, [0.]*len(ud))) ua = dynamicsymbols('ua:3') #ua_zero = {uai : 0 for uai in ua} ua_zero = dict(zip(ua, [0.]*len(ua))) # Reference frames: # Yaw intermediate frame: A. # Lean intermediate frame: B. # Disc fixed frame: C. N = ReferenceFrame('N') A = N.orientnew('A', 'Axis', [q[0], N.z]) B = A.orientnew('B', 'Axis', [q[1], A.x]) C = B.orientnew('C', 'Axis', [q[2], B.y]) # Angular velocity and angular acceleration of disc fixed frame # u[0], u[1] and u[2] are generalized independent speeds. C.set_ang_vel(N, u[0]*B.x + u[1]*B.y + u[2]*B.z) C.set_ang_acc(N, C.ang_vel_in(N).diff(t, B) + cross(B.ang_vel_in(N), C.ang_vel_in(N))) # Velocity and acceleration of points: # Disc-ground contact point: P. # Center of disc: O, defined from point P with depend coordinate: q[3] # u[3], u[4] and u[5] are generalized dependent speeds. P = Point('P') P.set_vel(N, ua[0]*A.x + ua[1]*A.y + ua[2]*A.z) O = P.locatenew('O', q[3]*A.z + r*sin(q[1])*A.y) O.set_vel(N, u[3]*A.x + u[4]*A.y + u[5]*A.z) O.set_acc(N, O.vel(N).diff(t, A) + cross(A.ang_vel_in(N), O.vel(N))) # Kinematic differential equations: # Two equalities: one is w_c_n_qd = C.ang_vel_in(N) in three coordinates # directions of B, for qd0, qd1 and qd2. # the other is v_o_n_qd = O.vel(N) in A.z direction for qd3. # Then, solve for dq/dt's in terms of u's: qd_kd. w_c_n_qd = qd[0]*A.z + qd[1]*B.x + qd[2]*B.y v_o_n_qd = O.pos_from(P).diff(t, A) + cross(A.ang_vel_in(N), O.pos_from(P)) kindiffs = Matrix([dot(w_c_n_qd - C.ang_vel_in(N), uv) for uv in B] + [dot(v_o_n_qd - O.vel(N), A.z)]) qd_kd = solve(kindiffs, qd) # Values of generalized speeds during a steady turn for later substitution # into the Fr_star_steady. steady_conditions = solve(kindiffs.subs({qd[1] : 0, qd[3] : 0}), u) steady_conditions.update({qd[1] : 0, qd[3] : 0}) # Partial angular velocities and velocities. partial_w_C = [C.ang_vel_in(N).diff(ui, N) for ui in u + ua] partial_v_O = [O.vel(N).diff(ui, N) for ui in u + ua] partial_v_P = [P.vel(N).diff(ui, N) for ui in u + ua] # Configuration constraint: f_c, the projection of radius r in A.z direction # is q[3]. # Velocity constraints: f_v, for u3, u4 and u5. # Acceleration constraints: f_a. f_c = Matrix([dot(-r*B.z, A.z) - q[3]]) f_v = Matrix([dot(O.vel(N) - (P.vel(N) + cross(C.ang_vel_in(N), O.pos_from(P))), ai).expand() for ai in A]) v_o_n = cross(C.ang_vel_in(N), O.pos_from(P)) a_o_n = v_o_n.diff(t, A) + cross(A.ang_vel_in(N), v_o_n) f_a = Matrix([dot(O.acc(N) - a_o_n, ai) for ai in A]) # Solve for constraint equations in the form of # u_dependent = A_rs * [u_i; u_aux]. # First, obtain constraint coefficient matrix: M_v * [u; ua] = 0; # Second, taking u[0], u[1], u[2] as independent, # taking u[3], u[4], u[5] as dependent, # rearranging the matrix of M_v to be A_rs for u_dependent. # Third, u_aux ==0 for u_dep, and resulting dictionary of u_dep_dict. M_v = zeros(3, 9) for i in range(3): for j, ui in enumerate(u + ua): M_v[i, j] = f_v[i].diff(ui) M_v_i = M_v[:, :3] M_v_d = M_v[:, 3:6] M_v_aux = M_v[:, 6:] M_v_i_aux = M_v_i.row_join(M_v_aux) A_rs = - M_v_d.inv() * M_v_i_aux u_dep = A_rs[:, :3] * Matrix(u[:3]) u_dep_dict = dict(zip(u[3:], u_dep)) #u_dep_dict = {udi : u_depi[0] for udi, u_depi in zip(u[3:], u_dep.tolist())} # Active forces: F_O acting on point O; F_P acting on point P. # Generalized active forces (unconstrained): Fr_u = F_point * pv_point. F_O = m*g*A.z F_P = Fx * A.x + Fy * A.y + Fz * A.z Fr_u = Matrix([dot(F_O, pv_o) + dot(F_P, pv_p) for pv_o, pv_p in zip(partial_v_O, partial_v_P)]) # Inertia force: R_star_O. # Inertia of disc: I_C_O, where J is a inertia component about principal axis. # Inertia torque: T_star_C. # Generalized inertia forces (unconstrained): Fr_star_u. R_star_O = -m*O.acc(N) I_C_O = inertia(B, I, J, I) T_star_C = -(dot(I_C_O, C.ang_acc_in(N)) \ + cross(C.ang_vel_in(N), dot(I_C_O, C.ang_vel_in(N)))) Fr_star_u = Matrix([dot(R_star_O, pv) + dot(T_star_C, pav) for pv, pav in zip(partial_v_O, partial_w_C)]) # Form nonholonomic Fr: Fr_c, and nonholonomic Fr_star: Fr_star_c. # Also, nonholonomic Fr_star in steady turning condition: Fr_star_steady. Fr_c = Fr_u[:3, :].col_join(Fr_u[6:, :]) + A_rs.T * Fr_u[3:6, :] Fr_star_c = Fr_star_u[:3, :].col_join(Fr_star_u[6:, :])\ + A_rs.T * Fr_star_u[3:6, :] Fr_star_steady = Fr_star_c.subs(ud_zero).subs(u_dep_dict)\ .subs(steady_conditions).subs({q[3]: -r*cos(q[1])}).expand() # Second, using KaneMethod in mechanics for fr, frstar and frstar_steady. # Rigid Bodies: disc, with inertia I_C_O. iner_tuple = (I_C_O, O) disc = RigidBody('disc', O, C, m, iner_tuple) bodyList = [disc] # Generalized forces: Gravity: F_o; Auxiliary forces: F_p. F_o = (O, F_O) F_p = (P, F_P) forceList = [F_o, F_p] # KanesMethod. kane = KanesMethod( N, q_ind= q[:3], u_ind= u[:3], kd_eqs=kindiffs, q_dependent=q[3:], configuration_constraints = f_c, u_dependent=u[3:], velocity_constraints= f_v, u_auxiliary=ua ) # fr, frstar, frstar_steady and kdd(kinematic differential equations). (fr, frstar)= kane.kanes_equations(forceList, bodyList) frstar_steady = frstar.subs(ud_zero).subs(u_dep_dict).subs(steady_conditions)\ .subs({q[3]: -r*cos(q[1])}).expand() kdd = kane.kindiffdict() assert Matrix(Fr_c).expand() == fr.expand() assert Matrix(Fr_star_c.subs(kdd)).expand() == frstar.expand() assert (simplify(Matrix(Fr_star_steady).expand()) == simplify(frstar_steady.expand())) def test_mat_inv_mul(): # Just a quick test to check that KanesMethod._mat_inv_mul works as # intended. Uses SymPy generated primes as matrix entries, so each entry in # each matrix should be symbolic and unique, allowing proper comparison. # Checks _mat_inv_mul against Matrix.inv / Matrix.__mul__. from sympy import Matrix, prime from sympy.physics.mechanics import ReferenceFrame, KanesMethod # Just need to create an instance of KanesMethod to get to _mat_inv_mul mat_inv_mul = KanesMethod(ReferenceFrame('N'), [1], [1])._mat_inv_mul # going to form 3 matrices # 1 n x n # different n x n # 1 n x 2n n = 3 m1 = Matrix(n, n, lambda i, j: prime(i * n + j + 2)) m2 = Matrix(n, n, lambda i, j: prime(i * n + j + 5)) m3 = Matrix(n, n, lambda i, j: prime(i + j * n + 2)) assert mat_inv_mul(m1, m2) == m1.inv() * m2 assert mat_inv_mul(m1, m3) == m1.inv() * m3 def test_non_central_inertia(): # This tests that the calculation of Fr* does not depend the point # about which the inertia of a rigid body is defined. This test solves # exercises 8.12, 8.17 from Kane 1985. # Declare symbols q1, q2, q3 = dynamicsymbols('q1:4') q1d, q2d, q3d = dynamicsymbols('q1:4', level=1) u1, u2, u3, u4, u5 = dynamicsymbols('u1:6') u_prime, R, M, g, e, f, theta = symbols('u\' R, M, g, e, f, theta') a, b, mA, mB, IA, J, K, t = symbols('a b mA mB IA J K t') Q1, Q2, Q3 = symbols('Q1, Q2 Q3') IA22, IA23, IA33 = symbols('IA22 IA23 IA33') # Reference Frames F = ReferenceFrame('F') P = F.orientnew('P', 'axis', [-theta, F.y]) A = P.orientnew('A', 'axis', [q1, P.x]) A.set_ang_vel(F, u1*A.x + u3*A.z) # define frames for wheels B = A.orientnew('B', 'axis', [q2, A.z]) C = A.orientnew('C', 'axis', [q3, A.z]) B.set_ang_vel(A, u4 * A.z) C.set_ang_vel(A, u5 * A.z) # define points D, S*, Q on frame A and their velocities pD = Point('D') pD.set_vel(A, 0) # u3 will not change v_D_F since wheels are still assumed to roll without slip. pD.set_vel(F, u2 * A.y) pS_star = pD.locatenew('S*', e*A.y) pQ = pD.locatenew('Q', f*A.y - R*A.x) for p in [pS_star, pQ]: p.v2pt_theory(pD, F, A) # masscenters of bodies A, B, C pA_star = pD.locatenew('A*', a*A.y) pB_star = pD.locatenew('B*', b*A.z) pC_star = pD.locatenew('C*', -b*A.z) for p in [pA_star, pB_star, pC_star]: p.v2pt_theory(pD, F, A) # points of B, C touching the plane P pB_hat = pB_star.locatenew('B^', -R*A.x) pC_hat = pC_star.locatenew('C^', -R*A.x) pB_hat.v2pt_theory(pB_star, F, B) pC_hat.v2pt_theory(pC_star, F, C) # the velocities of B^, C^ are zero since B, C are assumed to roll without slip #kde = [dot(p.vel(F), b) for b in A for p in [pB_hat, pC_hat]] kde = [q1d - u1, q2d - u4, q3d - u5] vc = [dot(p.vel(F), A.y) for p in [pB_hat, pC_hat]] # inertias of bodies A, B, C # IA22, IA23, IA33 are not specified in the problem statement, but are # necessary to define an inertia object. Although the values of # IA22, IA23, IA33 are not known in terms of the variables given in the # problem statement, they do not appear in the general inertia terms. inertia_A = inertia(A, IA, IA22, IA33, 0, IA23, 0) inertia_B = inertia(B, K, K, J) inertia_C = inertia(C, K, K, J) # define the rigid bodies A, B, C rbA = RigidBody('rbA', pA_star, A, mA, (inertia_A, pA_star)) rbB = RigidBody('rbB', pB_star, B, mB, (inertia_B, pB_star)) rbC = RigidBody('rbC', pC_star, C, mB, (inertia_C, pC_star)) km = KanesMethod(F, q_ind=[q1, q2, q3], u_ind=[u1, u2], kd_eqs=kde, u_dependent=[u4, u5], velocity_constraints=vc, u_auxiliary=[u3]) forces = [(pS_star, -M*g*F.x), (pQ, Q1*A.x + Q2*A.y + Q3*A.z)] bodies = [rbA, rbB, rbC] fr, fr_star = km.kanes_equations(forces, bodies) vc_map = solve(vc, [u4, u5]) # KanesMethod returns the negative of Fr, Fr* as defined in Kane1985. fr_star_expected = Matrix([ -(IA + 2*J*b**2/R**2 + 2*K + mA*a**2 + 2*mB*b**2) * u1.diff(t) - mA*a*u1*u2, -(mA + 2*mB +2*J/R**2) * u2.diff(t) + mA*a*u1**2, 0]) assert (trigsimp(fr_star.subs(vc_map).subs(u3, 0)).doit().expand() == fr_star_expected.expand()) # define inertias of rigid bodies A, B, C about point D # I_S/O = I_S/S* + I_S*/O bodies2 = [] for rb, I_star in zip([rbA, rbB, rbC], [inertia_A, inertia_B, inertia_C]): I = I_star + inertia_of_point_mass(rb.mass, rb.masscenter.pos_from(pD), rb.frame) bodies2.append(RigidBody('', rb.masscenter, rb.frame, rb.mass, (I, pD))) fr2, fr_star2 = km.kanes_equations(forces, bodies2) assert (trigsimp(fr_star2.subs(vc_map).subs(u3, 0)).doit().expand() == fr_star_expected.expand()) def test_sub_qdot(): # This test solves exercises 8.12, 8.17 from Kane 1985 and defines # some velocities in terms of q, qdot. ## --- Declare symbols --- q1, q2, q3 = dynamicsymbols('q1:4') q1d, q2d, q3d = dynamicsymbols('q1:4', level=1) u1, u2, u3 = dynamicsymbols('u1:4') u_prime, R, M, g, e, f, theta = symbols('u\' R, M, g, e, f, theta') a, b, mA, mB, IA, J, K, t = symbols('a b mA mB IA J K t') IA22, IA23, IA33 = symbols('IA22 IA23 IA33') Q1, Q2, Q3 = symbols('Q1 Q2 Q3') # --- Reference Frames --- F = ReferenceFrame('F') P = F.orientnew('P', 'axis', [-theta, F.y]) A = P.orientnew('A', 'axis', [q1, P.x]) A.set_ang_vel(F, u1*A.x + u3*A.z) # define frames for wheels B = A.orientnew('B', 'axis', [q2, A.z]) C = A.orientnew('C', 'axis', [q3, A.z]) ## --- define points D, S*, Q on frame A and their velocities --- pD = Point('D') pD.set_vel(A, 0) # u3 will not change v_D_F since wheels are still assumed to roll w/o slip pD.set_vel(F, u2 * A.y) pS_star = pD.locatenew('S*', e*A.y) pQ = pD.locatenew('Q', f*A.y - R*A.x) # masscenters of bodies A, B, C pA_star = pD.locatenew('A*', a*A.y) pB_star = pD.locatenew('B*', b*A.z) pC_star = pD.locatenew('C*', -b*A.z) for p in [pS_star, pQ, pA_star, pB_star, pC_star]: p.v2pt_theory(pD, F, A) # points of B, C touching the plane P pB_hat = pB_star.locatenew('B^', -R*A.x) pC_hat = pC_star.locatenew('C^', -R*A.x) pB_hat.v2pt_theory(pB_star, F, B) pC_hat.v2pt_theory(pC_star, F, C) # --- relate qdot, u --- # the velocities of B^, C^ are zero since B, C are assumed to roll w/o slip kde = [dot(p.vel(F), A.y) for p in [pB_hat, pC_hat]] kde += [u1 - q1d] kde_map = solve(kde, [q1d, q2d, q3d]) for k, v in list(kde_map.items()): kde_map[k.diff(t)] = v.diff(t) # inertias of bodies A, B, C # IA22, IA23, IA33 are not specified in the problem statement, but are # necessary to define an inertia object. Although the values of # IA22, IA23, IA33 are not known in terms of the variables given in the # problem statement, they do not appear in the general inertia terms. inertia_A = inertia(A, IA, IA22, IA33, 0, IA23, 0) inertia_B = inertia(B, K, K, J) inertia_C = inertia(C, K, K, J) # define the rigid bodies A, B, C rbA = RigidBody('rbA', pA_star, A, mA, (inertia_A, pA_star)) rbB = RigidBody('rbB', pB_star, B, mB, (inertia_B, pB_star)) rbC = RigidBody('rbC', pC_star, C, mB, (inertia_C, pC_star)) ## --- use kanes method --- km = KanesMethod(F, [q1, q2, q3], [u1, u2], kd_eqs=kde, u_auxiliary=[u3]) forces = [(pS_star, -M*g*F.x), (pQ, Q1*A.x + Q2*A.y + Q3*A.z)] bodies = [rbA, rbB, rbC] # Q2 = -u_prime * u2 * Q1 / sqrt(u2**2 + f**2 * u1**2) # -u_prime * R * u2 / sqrt(u2**2 + f**2 * u1**2) = R / Q1 * Q2 fr_expected = Matrix([ f*Q3 + M*g*e*sin(theta)*cos(q1), Q2 + M*g*sin(theta)*sin(q1), e*M*g*cos(theta) - Q1*f - Q2*R]) #Q1 * (f - u_prime * R * u2 / sqrt(u2**2 + f**2 * u1**2)))]) fr_star_expected = Matrix([ -(IA + 2*J*b**2/R**2 + 2*K + mA*a**2 + 2*mB*b**2) * u1.diff(t) - mA*a*u1*u2, -(mA + 2*mB +2*J/R**2) * u2.diff(t) + mA*a*u1**2, 0]) fr, fr_star = km.kanes_equations(forces, bodies) assert (fr.expand() == fr_expected.expand()) assert (trigsimp(fr_star).expand() == fr_star_expected.expand()) sympy-0.7.4.1/sympy/physics/mechanics/tests/__init__.py0000644000175000017500000000000012253362407023302 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/physics/mechanics/__init__.py0000644000175000017500000000214212253362407022151 0ustar georgeskgeorgesk__all__ = [] # The following pattern is used below for importing sub-modules: # # 1. "from foo import *". This imports all the names from foo.__all__ into # this module. But, this does not put those names into the __all__ of # this module. This enables "from sympy.physics.mechanics import kinematics" to # work. # 2. "import foo; __all__.extend(foo.__all__)". This adds all the names in # foo.__all__ to the __all__ of this module. The names in __all__ # determine which names are imported when # "from sympy.physics.mechanics import *" is done. from . import kane from .kane import * __all__.extend(kane.__all__) from . import rigidbody from .rigidbody import * __all__.extend(rigidbody.__all__) from . import functions from .functions import * __all__.extend(functions.__all__) from . import particle from .particle import * __all__.extend(particle.__all__) from . import point from .point import * __all__.extend(point.__all__) from . import essential from .essential import * __all__.extend(essential.__all__) from . import lagrange from .lagrange import * __all__.extend(lagrange.__all__) sympy-0.7.4.1/sympy/physics/paulialgebra.py0000644000175000017500000000743412253362407021121 0ustar georgeskgeorgesk""" 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. References ~~~~~~~~~~ .. [1] http://en.wikipedia.org/wiki/Pauli_matrices """ from __future__ import print_function, division 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) 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) sympy-0.7.4.1/sympy/physics/secondquant.py0000644000175000017500000026110012253362407021005 0ustar georgeskgeorgesk""" Second quantization operators and states for bosons. This follow the formulation of Fetter and Welecka, "Quantum Theory of Many-Particle Systems." """ from __future__ import print_function, division 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, xrange 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 __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 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 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 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 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 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 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 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 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) 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 class CreateBoson(BosonicOperator, Creator): """ Bosonic creation operator. """ op_symbol = 'b+' def _dagger_(self): return AnnihilateBoson(self.state) 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 class AnnihilateFermion(FermionicOperator, Annihilator): """ Fermionic annihilation operator. """ op_symbol = 'f' def _dagger_(self): return CreateFermion(self.state) 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 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 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 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 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 class CreateFermion(FermionicOperator, Creator): """ Fermionic creation operator. """ op_symbol = 'f+' def _dagger_(self): return AnnihilateFermion(self.state) 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 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 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 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 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 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]) class FockStateKet(FockState): """ Representation of a ket. """ lbracket = '|' rbracket = '>' 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) 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) 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 ), they are converted to instances of InnerProduct. This does not currently work on double inner products like, . 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 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) 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 ````. """ 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 def bra(self): """Returns the bra part of the state""" return self.args[0] @property 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__() 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) Matrix([ [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 class BosonicBasis(object): """ Base class for a basis set of bosonic Fock states. """ pass 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) 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) 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) 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) 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) 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() 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(i, p)*CreateFermion(q) + KroneckerDelta(i, q)*CreateFermion(p) """ is_commutative = False nargs = 2 @classmethod 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) 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]) 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 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 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 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) 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() >>> list(no.iter_q_creators()) [0, 1] >>> list(no.iter_q_annihilators()) [3, 2] """ ops = self.args[0].args iter = xrange(len(ops) - 1, -1, -1) for i in iter: if ops[i].is_q_annihilator: yield i else: break 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() >>> list(no.iter_q_creators()) [0, 1] >>> list(no.iter_q_annihilators()) [3, 2] """ ops = self.args[0].args iter = xrange(0, len(ops)) for i in iter: if ops[i].is_q_creator: yield i else: break 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 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(_i, q)*KroneckerDelta(p, q) >>> contraction(F(p),Fd(q)) KroneckerDelta(_a, q)*KroneckerDelta(p, q) 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) 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 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 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 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 = sorted(map(sympify, (i, j)), key=default_sort_key) obj = Basic.__new__(cls, i, j) return obj 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 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] return min(a, b, key=lambda x: default_sort_key(_get_indices(x, ind))) 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 sympy-0.7.4.1/sympy/physics/units.py0000644000175000017500000001776512253362407017643 0ustar georgeskgeorgesk""" 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 231 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 __future__ import print_function, division from sympy import Rational, pi from sympy.core import AtomicExpr 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 = Rational(231, 4) * 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 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 sympy-0.7.4.1/sympy/physics/gaussopt.py0000644000175000017500000004633312253362407020337 0ustar georgeskgeorgesk# -*- 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 __future__ import print_function, division 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 ### 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 Matrix([ [1, 2], [3, 4]]) >>> RayTransferMatrix(Matrix([[1, 2], [3, 4]])) Matrix([ [1, 2], [3, 4]]) >>> mat.A 1 >>> f = Symbol('f') >>> lens = ThinLens(f) >>> lens Matrix([ [ 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 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 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 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 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] 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) Matrix([ [1, d], [0, 1]]) """ def __new__(cls, d): return RayTransferMatrix.__new__(cls, 1, d, 0, 1) 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) Matrix([ [1, 0], [0, n1/n2]]) """ def __new__(cls, n1, n2): n1, n2 = map(sympify, (n1, n2)) return RayTransferMatrix.__new__(cls, 1, 0, 0, n1/n2) 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) Matrix([ [ 1, 0], [(n1 - n2)/(R*n2), n1/n2]]) """ def __new__(cls, R, n1, n2): R, n1, n2 = map(sympify, (R, n1, n2)) return RayTransferMatrix.__new__(cls, 1, 0, (n1 - n2)/R/n2, n1/n2) class FlatMirror(RayTransferMatrix): """ Ray Transfer Matrix for reflection. See Also ======== RayTransferMatrix Examples ======== >>> from sympy.physics.gaussopt import FlatMirror >>> FlatMirror() Matrix([ [1, 0], [0, 1]]) """ def __new__(cls): return RayTransferMatrix.__new__(cls, 1, 0, 0, 1) 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) Matrix([ [ 1, 0], [-2/R, 1]]) """ def __new__(cls, R): R = sympify(R) return RayTransferMatrix.__new__(cls, 1, 0, -2/R, 1) 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) Matrix([ [ 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 ### 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) Matrix([ [ h], [angle]]) >>> FreeSpace(d)*GeometricRay(h, angle) Matrix([ [angle*d + h], [ angle]]) >>> GeometricRay( Matrix( ((h,), (angle,)) ) ) Matrix([ [ 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 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 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 ### 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 = 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 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 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 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 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 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 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 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 ### 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 = map(sympify, (w, wavelen)) return w**2*pi/wavelen 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 = map(sympify, (z_r, wavelen)) return sqrt(z_r/pi*wavelen) 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 = 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) 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 = map(sympify, (a, f)) return -geometric_conj_ab(a, -f) geometric_conj_bf = geometric_conj_af 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 = 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) 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 = 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 sympy-0.7.4.1/sympy/physics/qho_1d.py0000644000175000017500000000316212253362407017636 0ustar georgeskgeorgeskfrom __future__ import print_function, division from sympy.core import S, pi, Rational from sympy.functions import hermite, sqrt, exp, factorial from sympy.physics.quantum.constants import hbar 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 = 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) 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)) sympy-0.7.4.1/sympy/physics/tests/0000755000175000017500000000000012253362407017251 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/physics/tests/test_clebsch_gordan.py0000644000175000017500000000556112253362407023626 0ustar georgeskgeorgeskfrom sympy import S, sqrt, pi from sympy.physics.wigner import clebsch_gordan, wigner_9j, wigner_6j, gaunt from sympy.core.numbers import Rational # Todo: more tests should be added from: # http://en.wikipedia.org/wiki/Table_of_Clebsch-Gordan_coefficients def test_clebsch_gordan_docs(): assert clebsch_gordan(S(3)/2, S(1)/2, 2, S(3)/2, S(1)/2, 2) == 1 assert clebsch_gordan(S(3)/2, S(1)/2, 1, S(3)/2, -S(1)/2, 1) == sqrt(3)/2 assert clebsch_gordan(S(3)/2, S(1)/2, 1, -S(1)/2, S(1)/2, 0) == -sqrt(2)/2 def test_clebsch_gordan1(): j_1 = S(1)/2 j_2 = S(1)/2 m = 1 j = 1 m_1 = S(1)/2 m_2 = S(1)/2 assert clebsch_gordan(j_1, j_2, j, m_1, m_2, m) == 1 j_1 = S(1)/2 j_2 = S(1)/2 m = 0 j = 1 m_1 = S(1)/2 m_2 = S(1)/2 assert clebsch_gordan(j_1, j_2, j, m_1, m_2, m) == 0 j_1 = S(1)/2 j_2 = S(1)/2 m = 0 j = 1 m_1 = S(1)/2 m_2 = -S(1)/2 assert clebsch_gordan(j_1, j_2, j, m_1, m_2, m) == sqrt(2)/2 j_1 = S(1)/2 j_2 = S(1)/2 m = 0 j = 0 m_1 = S(1)/2 m_2 = -S(1)/2 assert clebsch_gordan(j_1, j_2, j, m_1, m_2, m) == sqrt(2)/2 j_1 = S(1)/2 j_2 = S(1)/2 m = 0 j = 1 m_1 = -S(1)/2 m_2 = S(1)/2 assert clebsch_gordan(j_1, j_2, j, m_1, m_2, m) == sqrt(2)/2 j_1 = S(1)/2 j_2 = S(1)/2 m = 0 j = 0 m_1 = -S(1)/2 m_2 = S(1)/2 assert clebsch_gordan(j_1, j_2, j, m_1, m_2, m) == -sqrt(2)/2 def test_clebsch_gordan2(): j_1 = S(1) j_2 = S(1)/2 m = S(3)/2 j = S(3)/2 m_1 = 1 m_2 = S(1)/2 assert clebsch_gordan(j_1, j_2, j, m_1, m_2, m) == 1 j_1 = S(1) j_2 = S(1)/2 m = S(1)/2 j = S(3)/2 m_1 = 1 m_2 = -S(1)/2 assert clebsch_gordan(j_1, j_2, j, m_1, m_2, m) == 1/sqrt(3) j_1 = S(1) j_2 = S(1)/2 m = S(1)/2 j = S(1)/2 m_1 = 1 m_2 = -S(1)/2 assert clebsch_gordan(j_1, j_2, j, m_1, m_2, m) == sqrt(2)/sqrt(3) j_1 = S(1) j_2 = S(1)/2 m = S(1)/2 j = S(1)/2 m_1 = 0 m_2 = S(1)/2 assert clebsch_gordan(j_1, j_2, j, m_1, m_2, m) == -1/sqrt(3) j_1 = S(1) j_2 = S(1)/2 m = S(1)/2 j = S(3)/2 m_1 = 0 m_2 = S(1)/2 assert clebsch_gordan(j_1, j_2, j, m_1, m_2, m) == sqrt(2)/sqrt(3) def test_wigner(): def tn(a, b): return abs((a - b).n(64) < S('1e-64')) assert tn(wigner_9j(1, 1, 1, 1, 1, 1, 1, 1, 0, prec=64), S(1)/18) assert wigner_9j(3, 3, 2, 3, 3, 2, 3, 3, 2) == 3221*sqrt( 70)/(246960*sqrt(105)) - 365/(3528*sqrt(70)*sqrt(105)) assert wigner_6j(5, 5, 5, 5, 5, 5) == Rational(1, 52) assert tn(wigner_6j(8, 8, 8, 8, 8, 8, prec=64), -S(12219)/965770) def test_gaunt(): def tn(a, b): return abs((a - b).n(64) < S('1e-64')) assert gaunt(1, 0, 1, 1, 0, -1) == -1/(2*sqrt(pi)) assert tn(gaunt( 10, 10, 12, 9, 3, -12, prec=64), (-S(98)/62031) * sqrt(6279)/sqrt(pi)) sympy-0.7.4.1/sympy/physics/tests/test_gaussopt.py0000644000175000017500000000726212253362407022536 0ustar georgeskgeorgeskfrom sympy import atan2, factor, Float, I, Matrix, N, oo, pi, sqrt, symbols from sympy.physics.gaussopt import (BeamParameter, CurvedMirror, CurvedRefraction, FlatMirror, FlatRefraction, FreeSpace, GeometricRay, RayTransferMatrix, ThinLens, conjugate_gauss_beams, gaussian_conj, geometric_conj_ab, geometric_conj_af, geometric_conj_bf, rayleigh2waist, waist2rayleigh) def streq(a, b): return str(a) == str(b) def test_gauss_opt(): mat = RayTransferMatrix(1, 2, 3, 4) assert mat == Matrix([[1, 2], [3, 4]]) assert mat == RayTransferMatrix( Matrix([[1, 2], [3, 4]]) ) assert [mat.A, mat.B, mat.C, mat.D] == [1, 2, 3, 4] d, f, h, n1, n2, R = symbols('d f h n1 n2 R') lens = ThinLens(f) assert lens == Matrix([[ 1, 0], [-1/f, 1]]) assert lens.C == -1/f assert FreeSpace(d) == Matrix([[ 1, d], [0, 1]]) assert FlatRefraction(n1, n2) == Matrix([[1, 0], [0, n1/n2]]) assert CurvedRefraction( R, n1, n2) == Matrix([[1, 0], [(n1 - n2)/(R*n2), n1/n2]]) assert FlatMirror() == Matrix([[1, 0], [0, 1]]) assert CurvedMirror(R) == Matrix([[ 1, 0], [-2/R, 1]]) assert ThinLens(f) == Matrix([[ 1, 0], [-1/f, 1]]) mul = CurvedMirror(R)*FreeSpace(d) mul_mat = Matrix([[ 1, 0], [-2/R, 1]])*Matrix([[ 1, d], [0, 1]]) assert mul.A == mul_mat[0, 0] assert mul.B == mul_mat[0, 1] assert mul.C == mul_mat[1, 0] assert mul.D == mul_mat[1, 1] angle = symbols('angle') assert GeometricRay(h, angle) == Matrix([[ h], [angle]]) assert FreeSpace( d)*GeometricRay(h, angle) == Matrix([[angle*d + h], [angle]]) assert GeometricRay( Matrix( ((h,), (angle,)) ) ) == Matrix([[h], [angle]]) assert (FreeSpace(d)*GeometricRay(h, angle)).height == angle*d + h assert (FreeSpace(d)*GeometricRay(h, angle)).angle == angle p = BeamParameter(530e-9, 1, w=1e-3) assert streq(p.q, 1 + 1.88679245283019*I*pi) assert streq(N(p.q), 1.0 + 5.92753330865999*I) assert streq(N(p.w_0), Float(0.00100000000000000)) assert streq(N(p.z_r), Float(5.92753330865999)) fs = FreeSpace(10) p1 = fs*p assert streq(N(p.w), Float(0.00101413072159615)) assert streq(N(p1.w), Float(0.00210803120913829)) w, wavelen = symbols('w wavelen') assert waist2rayleigh(w, wavelen) == pi*w**2/wavelen z_r, wavelen = symbols('z_r wavelen') assert rayleigh2waist(z_r, wavelen) == sqrt(wavelen*z_r)/sqrt(pi) a, b, f = symbols('a b f') assert geometric_conj_ab(a, b) == a*b/(a + b) assert geometric_conj_af(a, f) == a*f/(a - f) assert geometric_conj_bf(b, f) == b*f/(b - f) assert geometric_conj_ab(oo, b) == b assert geometric_conj_ab(a, oo) == a s_in, z_r_in, f = symbols('s_in z_r_in f') assert gaussian_conj( s_in, z_r_in, f)[0] == 1/(-1/(s_in + z_r_in**2/(-f + s_in)) + 1/f) assert gaussian_conj( s_in, z_r_in, f)[1] == z_r_in/(1 - s_in**2/f**2 + z_r_in**2/f**2) assert gaussian_conj( s_in, z_r_in, f)[2] == 1/sqrt(1 - s_in**2/f**2 + z_r_in**2/f**2) l, w_i, w_o, f = symbols('l w_i w_o f') assert 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) assert 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 assert conjugate_gauss_beams(l, w_i, w_o, f=f)[2] == f z, l, w = symbols('z l r', positive=True) p = BeamParameter(l, z, w=w) assert p.radius == z*(l**2*z**2/(pi**2*w**4) + 1) assert p.w == w*sqrt(l**2*z**2/(pi**2*w**4) + 1) assert p.w_0 == w assert p.divergence == l/(pi*w) assert p.gouy == atan2(z, pi*w**2/l) assert p.waist_approximation_limit == 2*l/pi sympy-0.7.4.1/sympy/physics/tests/test_qho_1d.py0000644000175000017500000000222412253362407022035 0ustar georgeskgeorgeskfrom sympy import exp, integrate, oo, Rational, pi, S, simplify, sqrt from sympy.abc import omega, m, x from sympy.physics.qho_1d import psi_n, E_n from sympy.physics.quantum.constants import hbar nu = m * omega / hbar def test_wavefunction(): Psi = { 0: (nu/pi)**(S(1)/4) * exp(-nu * x**2 /2), 1: (nu/pi)**(S(1)/4) * sqrt(2*nu) * x * exp(-nu * x**2 /2), 2: (nu/pi)**(S(1)/4) * (2 * nu * x**2 - 1)/sqrt(2) * exp(-nu * x**2 /2), 3: (nu/pi)**(S(1)/4) * sqrt(nu/3) * (2 * nu * x**3 - 3 * x) * exp(-nu * x**2 /2) } for n in Psi: assert simplify(psi_n(n, x, m, omega) - Psi[n]) == 0 def test_norm(n=1): # Maximum "n" which is tested: for i in range(n + 1): assert integrate(psi_n(i, x, 1, 1)**2, (x, -oo, oo)) == 1 def test_orthogonality(n=1): # Maximum "n" which is tested: for i in range(n + 1): for j in range(i + 1, n + 1): assert integrate( psi_n(i, x, 1, 1)*psi_n(j, x, 1, 1), (x, -oo, oo)) == 0 def test_energies(n=1): # Maximum "n" which is tested: for i in range(n + 1): assert E_n(i, omega) == hbar * omega * (i + Rational(1, 2)) sympy-0.7.4.1/sympy/physics/tests/test_secondquant.py0000644000175000017500000012750012253362407023213 0ustar georgeskgeorgeskfrom sympy.physics.secondquant import ( Dagger, Bd, VarBosonicBasis, BBra, B, BKet, FixedBosonicBasis, matrix_rep, apply_operators, InnerProduct, Commutator, KroneckerDelta, AnnihilateBoson, CreateBoson, BosonicOperator, F, Fd, FKet, BosonState, CreateFermion, AnnihilateFermion, evaluate_deltas, AntiSymmetricTensor, contraction, NO, wicks, PermutationOperator, simplify_index_permutations, _sort_anticommuting_fermions, _get_ordered_dummies, substitute_dummies ) from sympy import (Dummy, expand, Function, I, Rational, simplify, sqrt, Sum, Symbol, symbols) from sympy.utilities.pytest import XFAIL def test_PermutationOperator(): p, q, r, s = symbols('p,q,r,s') f, g, h, i = map(Function, 'fghi') P = PermutationOperator assert P(p, q).get_permuted(f(p)*g(q)) == -f(q)*g(p) assert P(p, q).get_permuted(f(p, q)) == -f(q, p) assert P(p, q).get_permuted(f(p)) == f(p) expr = (f(p)*g(q)*h(r)*i(s) - f(q)*g(p)*h(r)*i(s) - f(p)*g(q)*h(s)*i(r) + f(q)*g(p)*h(s)*i(r)) perms = [P(p, q), P(r, s)] assert (simplify_index_permutations(expr, perms) == P(p, q)*P(r, s)*f(p)*g(q)*h(r)*i(s)) def test_index_permutations_with_dummies(): a, b, c, d = symbols('a b c d') p, q, r, s = symbols('p q r s', cls=Dummy) f, g = map(Function, 'fg') P = PermutationOperator # No dummy substitution necessary expr = f(a, b, p, q) - f(b, a, p, q) assert simplify_index_permutations( expr, [P(a, b)]) == P(a, b)*f(a, b, p, q) # Cases where dummy substitution is needed expected = P(a, b)*substitute_dummies(f(a, b, p, q)) expr = f(a, b, p, q) - f(b, a, q, p) result = simplify_index_permutations(expr, [P(a, b)]) assert expected == substitute_dummies(result) expr = f(a, b, q, p) - f(b, a, p, q) result = simplify_index_permutations(expr, [P(a, b)]) assert expected == substitute_dummies(result) # A case where nothing can be done expr = f(a, b, q, p) - g(b, a, p, q) result = simplify_index_permutations(expr, [P(a, b)]) assert expr == result def test_dagger(): i, j, n, m = symbols('i,j,n,m') assert Dagger(1) == 1 assert Dagger(1.0) == 1.0 assert Dagger(2*I) == -2*I assert Dagger(Rational(1, 2)*I/3.0) == -Rational(1, 2)*I/3.0 assert Dagger(BKet([n])) == BBra([n]) assert Dagger(B(0)) == Bd(0) assert Dagger(Bd(0)) == B(0) assert Dagger(B(n)) == Bd(n) assert Dagger(Bd(n)) == B(n) assert Dagger(B(0) + B(1)) == Bd(0) + Bd(1) assert Dagger(n*m) == Dagger(n)*Dagger(m) # n, m commute assert Dagger(B(n)*B(m)) == Bd(m)*Bd(n) assert Dagger(B(n)**10) == Dagger(B(n))**10 def test_operator(): i, j = symbols('i,j') o = BosonicOperator(i) assert o.state == i assert o.is_symbolic o = BosonicOperator(1) assert o.state == 1 assert not o.is_symbolic def test_create(): i, j, n, m = symbols('i,j,n,m') o = Bd(i) assert isinstance(o, CreateBoson) o = o.subs(i, j) assert o.atoms(Symbol) == set([j]) o = Bd(0) assert o.apply_operator(BKet([n])) == sqrt(n + 1)*BKet([n + 1]) o = Bd(n) assert o.apply_operator(BKet([n])) == o*BKet([n]) def test_annihilate(): i, j, n, m = symbols('i,j,n,m') o = B(i) assert isinstance(o, AnnihilateBoson) o = o.subs(i, j) assert o.atoms(Symbol) == set([j]) o = B(0) assert o.apply_operator(BKet([n])) == sqrt(n)*BKet([n - 1]) o = B(n) assert o.apply_operator(BKet([n])) == o*BKet([n]) def test_basic_state(): i, j, n, m = symbols('i,j,n,m') s = BosonState([0, 1, 2, 3, 4]) assert len(s) == 5 assert s.args[0] == tuple(range(5)) assert s.up(0) == BosonState([1, 1, 2, 3, 4]) assert s.down(4) == BosonState([0, 1, 2, 3, 3]) for i in range(5): assert s.up(i).down(i) == s assert s.down(0) == 0 for i in range(5): assert s[i] == i s = BosonState([n, m]) assert s.down(0) == BosonState([n - 1, m]) assert s.up(0) == BosonState([n + 1, m]) @XFAIL def test_move1(): i, j = symbols('i,j') A, C = symbols('A,C', cls=Function) o = A(i)*C(j) # This almost works, but has a minus sign wrong assert move(o, 0, 1) == KroneckerDelta(i, j) + C(j)*A(i) @XFAIL def test_move2(): i, j = symbols('i,j') A, C = symbols('A,C', cls=Function) o = C(j)*A(i) # This almost works, but has a minus sign wrong assert move(o, 0, 1) == -KroneckerDelta(i, j) + A(i)*C(j) def test_basic_apply(): n = symbols("n") e = B(0)*BKet([n]) assert apply_operators(e) == sqrt(n)*BKet([n - 1]) e = Bd(0)*BKet([n]) assert apply_operators(e) == sqrt(n + 1)*BKet([n + 1]) def test_complex_apply(): n, m = symbols("n,m") o = Bd(0)*B(0)*Bd(1)*B(0) e = apply_operators(o*BKet([n, m])) answer = sqrt(n)*sqrt(m + 1)*(-1 + n)*BKet([-1 + n, 1 + m]) assert expand(e) == expand(answer) def test_number_operator(): n = symbols("n") o = Bd(0)*B(0) e = apply_operators(o*BKet([n])) assert e == n*BKet([n]) def test_inner_product(): i, j, k, l = symbols('i,j,k,l') s1 = BBra([0]) s2 = BKet([1]) assert InnerProduct(s1, Dagger(s1)) == 1 assert InnerProduct(s1, s2) == 0 s1 = BBra([i, j]) s2 = BKet([k, l]) r = InnerProduct(s1, s2) assert r == KroneckerDelta(i, k)*KroneckerDelta(j, l) def test_symbolic_matrix_elements(): n, m = symbols('n,m') s1 = BBra([n]) s2 = BKet([m]) o = B(0) e = apply_operators(s1*o*s2) assert e == sqrt(m)*KroneckerDelta(n, m - 1) def test_matrix_elements(): b = VarBosonicBasis(5) o = B(0) m = matrix_rep(o, b) for i in range(4): assert m[i, i + 1] == sqrt(i + 1) o = Bd(0) m = matrix_rep(o, b) for i in range(4): assert m[i + 1, i] == sqrt(i + 1) def test_sho(): n, m = symbols('n,m') h_n = Bd(n)*B(n)*(n + Rational(1, 2)) H = Sum(h_n, (n, 0, 5)) o = H.doit(deep=False) b = FixedBosonicBasis(2, 6) m = matrix_rep(o, b) # We need to double check these energy values to make sure that they # are correct and have the proper degeneracies! diag = [1, 2, 3, 3, 4, 5, 4, 5, 6, 7, 5, 6, 7, 8, 9, 6, 7, 8, 9, 10, 11] for i in range(len(diag)): assert diag[i] == m[i, i] def test_commutation(): n, m = symbols("n,m", above_fermi=True) c = Commutator(B(0), Bd(0)) assert c == 1 c = Commutator(Bd(0), B(0)) assert c == -1 c = Commutator(B(n), Bd(0)) assert c == KroneckerDelta(n, 0) c = Commutator(B(0), Bd(0)) e = simplify(apply_operators(c*BKet([n]))) assert e == BKet([n]) c = Commutator(B(0), B(1)) e = simplify(apply_operators(c*BKet([n, m]))) assert e == 0 c = Commutator(F(m), Fd(m)) assert c == +1 - 2*NO(Fd(m)*F(m)) c = Commutator(Fd(m), F(m)) assert c == -1 + 2*NO(Fd(m)*F(m)) C = Commutator X, Y, Z = symbols('X,Y,Z', commutative=False) assert C(C(X, Y), Z) != 0 assert C(C(X, Z), Y) != 0 assert C(Y, C(X, Z)) != 0 i, j, k, l = symbols('i,j,k,l', below_fermi=True) a, b, c, d = symbols('a,b,c,d', above_fermi=True) p, q, r, s = symbols('p,q,r,s') D = KroneckerDelta assert C(Fd(a), F(i)) == -2*NO(F(i)*Fd(a)) assert C(Fd(j), NO(Fd(a)*F(i))).doit(wicks=True) == -D(j, i)*Fd(a) assert C(Fd(a)*F(i), Fd(b)*F(j)).doit(wicks=True) == 0 def test_create_f(): i, j, n, m = symbols('i,j,n,m') o = Fd(i) assert isinstance(o, CreateFermion) o = o.subs(i, j) assert o.atoms(Symbol) == set([j]) o = Fd(1) assert o.apply_operator(FKet([n])) == FKet([1, n]) assert o.apply_operator(FKet([n])) == -FKet([n, 1]) o = Fd(n) assert o.apply_operator(FKet([])) == FKet([n]) vacuum = FKet([], fermi_level=4) assert vacuum == FKet([], fermi_level=4) i, j, k, l = symbols('i,j,k,l', below_fermi=True) a, b, c, d = symbols('a,b,c,d', above_fermi=True) p, q, r, s = symbols('p,q,r,s') assert Fd(i).apply_operator(FKet([i, j, k], 4)) == FKet([j, k], 4) assert Fd(a).apply_operator(FKet([i, b, k], 4)) == FKet([a, i, b, k], 4) def test_annihilate_f(): i, j, n, m = symbols('i,j,n,m') o = F(i) assert isinstance(o, AnnihilateFermion) o = o.subs(i, j) assert o.atoms(Symbol) == set([j]) o = F(1) assert o.apply_operator(FKet([1, n])) == FKet([n]) assert o.apply_operator(FKet([n, 1])) == -FKet([n]) o = F(n) assert o.apply_operator(FKet([n])) == FKet([]) i, j, k, l = symbols('i,j,k,l', below_fermi=True) a, b, c, d = symbols('a,b,c,d', above_fermi=True) p, q, r, s = symbols('p,q,r,s') assert F(i).apply_operator(FKet([i, j, k], 4)) == 0 assert F(a).apply_operator(FKet([i, b, k], 4)) == 0 assert F(l).apply_operator(FKet([i, j, k], 3)) == 0 assert F(l).apply_operator(FKet([i, j, k], 4)) == FKet([l, i, j, k], 4) def test_create_b(): i, j, n, m = symbols('i,j,n,m') o = Bd(i) assert isinstance(o, CreateBoson) o = o.subs(i, j) assert o.atoms(Symbol) == set([j]) o = Bd(0) assert o.apply_operator(BKet([n])) == sqrt(n + 1)*BKet([n + 1]) o = Bd(n) assert o.apply_operator(BKet([n])) == o*BKet([n]) def test_annihilate_b(): i, j, n, m = symbols('i,j,n,m') o = B(i) assert isinstance(o, AnnihilateBoson) o = o.subs(i, j) assert o.atoms(Symbol) == set([j]) o = B(0) def test_wicks(): p, q, r, s = symbols('p,q,r,s', above_fermi=True) # Testing for particles only str = F(p)*Fd(q) assert wicks(str) == NO(F(p)*Fd(q)) + KroneckerDelta(p, q) str = Fd(p)*F(q) assert wicks(str) == NO(Fd(p)*F(q)) str = F(p)*Fd(q)*F(r)*Fd(s) nstr = wicks(str) fasit = NO( KroneckerDelta(p, q)*KroneckerDelta(r, s) + KroneckerDelta(p, q)*AnnihilateFermion(r)*CreateFermion(s) + KroneckerDelta(r, s)*AnnihilateFermion(p)*CreateFermion(q) - KroneckerDelta(p, s)*AnnihilateFermion(r)*CreateFermion(q) - AnnihilateFermion(p)*AnnihilateFermion(r)*CreateFermion(q)*CreateFermion(s)) assert nstr == fasit assert (p*q*nstr).expand() == wicks(p*q*str) assert (nstr*p*q*2).expand() == wicks(str*p*q*2) # Testing CC equations particles and holes i, j, k, l = symbols('i j k l', below_fermi=True, cls=Dummy) a, b, c, d = symbols('a b c d', above_fermi=True, cls=Dummy) p, q, r, s = symbols('p q r s', cls=Dummy) assert (wicks(F(a)*NO(F(i)*F(j))*Fd(b)) == NO(F(a)*F(i)*F(j)*Fd(b)) + KroneckerDelta(a, b)*NO(F(i)*F(j))) assert (wicks(F(a)*NO(F(i)*F(j)*F(k))*Fd(b)) == NO(F(a)*F(i)*F(j)*F(k)*Fd(b)) - KroneckerDelta(a, b)*NO(F(i)*F(j)*F(k))) expr = wicks(Fd(i)*NO(Fd(j)*F(k))*F(l)) assert (expr == -KroneckerDelta(i, k)*NO(Fd(j)*F(l)) - KroneckerDelta(j, l)*NO(Fd(i)*F(k)) - KroneckerDelta(i, k)*KroneckerDelta(j, l) + KroneckerDelta(i, l)*NO(Fd(j)*F(k)) + NO(Fd(i)*Fd(j)*F(k)*F(l))) expr = wicks(F(a)*NO(F(b)*Fd(c))*Fd(d)) assert (expr == -KroneckerDelta(a, c)*NO(F(b)*Fd(d)) - KroneckerDelta(b, d)*NO(F(a)*Fd(c)) - KroneckerDelta(a, c)*KroneckerDelta(b, d) + KroneckerDelta(a, d)*NO(F(b)*Fd(c)) + NO(F(a)*F(b)*Fd(c)*Fd(d))) def test_NO(): i, j, k, l = symbols('i j k l', below_fermi=True) a, b, c, d = symbols('a b c d', above_fermi=True) p, q, r, s = symbols('p q r s', cls=Dummy) assert (NO(Fd(p)*F(q) + Fd(a)*F(b)) == NO(Fd(p)*F(q)) + NO(Fd(a)*F(b))) assert (NO(Fd(i)*NO(F(j)*Fd(a))) == NO(Fd(i)*F(j)*Fd(a))) assert NO(1) == 1 assert NO(i) == i assert (NO(Fd(a)*Fd(b)*(F(c) + F(d))) == NO(Fd(a)*Fd(b)*F(c)) + NO(Fd(a)*Fd(b)*F(d))) assert NO(Fd(a)*F(b))._remove_brackets() == Fd(a)*F(b) assert NO(F(j)*Fd(i))._remove_brackets() == F(j)*Fd(i) assert (NO(Fd(p)*F(q)).subs(Fd(p), Fd(a) + Fd(i)) == NO(Fd(a)*F(q)) + NO(Fd(i)*F(q))) assert (NO(Fd(p)*F(q)).subs(F(q), F(a) + F(i)) == NO(Fd(p)*F(a)) + NO(Fd(p)*F(i))) expr = NO(Fd(p)*F(q))._remove_brackets() assert wicks(expr) == NO(expr) assert NO(Fd(a)*F(b)) == - NO(F(b)*Fd(a)) no = NO(Fd(a)*F(i)*F(b)*Fd(j)) l1 = [ ind for ind in no.iter_q_creators() ] assert l1 == [0, 1] l2 = [ ind for ind in no.iter_q_annihilators() ] assert l2 == [3, 2] def test_sorting(): i, j = symbols('i,j', below_fermi=True) a, b = symbols('a,b', above_fermi=True) p, q = symbols('p,q') # p, q assert _sort_anticommuting_fermions([Fd(p), F(q)]) == ([Fd(p), F(q)], 0) assert _sort_anticommuting_fermions([F(p), Fd(q)]) == ([Fd(q), F(p)], 1) # i, p assert _sort_anticommuting_fermions([F(p), Fd(i)]) == ([F(p), Fd(i)], 0) assert _sort_anticommuting_fermions([Fd(i), F(p)]) == ([F(p), Fd(i)], 1) assert _sort_anticommuting_fermions([Fd(p), Fd(i)]) == ([Fd(p), Fd(i)], 0) assert _sort_anticommuting_fermions([Fd(i), Fd(p)]) == ([Fd(p), Fd(i)], 1) assert _sort_anticommuting_fermions([F(p), F(i)]) == ([F(i), F(p)], 1) assert _sort_anticommuting_fermions([F(i), F(p)]) == ([F(i), F(p)], 0) assert _sort_anticommuting_fermions([Fd(p), F(i)]) == ([F(i), Fd(p)], 1) assert _sort_anticommuting_fermions([F(i), Fd(p)]) == ([F(i), Fd(p)], 0) # a, p assert _sort_anticommuting_fermions([F(p), Fd(a)]) == ([Fd(a), F(p)], 1) assert _sort_anticommuting_fermions([Fd(a), F(p)]) == ([Fd(a), F(p)], 0) assert _sort_anticommuting_fermions([Fd(p), Fd(a)]) == ([Fd(a), Fd(p)], 1) assert _sort_anticommuting_fermions([Fd(a), Fd(p)]) == ([Fd(a), Fd(p)], 0) assert _sort_anticommuting_fermions([F(p), F(a)]) == ([F(p), F(a)], 0) assert _sort_anticommuting_fermions([F(a), F(p)]) == ([F(p), F(a)], 1) assert _sort_anticommuting_fermions([Fd(p), F(a)]) == ([Fd(p), F(a)], 0) assert _sort_anticommuting_fermions([F(a), Fd(p)]) == ([Fd(p), F(a)], 1) # i, a assert _sort_anticommuting_fermions([F(i), Fd(j)]) == ([F(i), Fd(j)], 0) assert _sort_anticommuting_fermions([Fd(j), F(i)]) == ([F(i), Fd(j)], 1) assert _sort_anticommuting_fermions([Fd(a), Fd(i)]) == ([Fd(a), Fd(i)], 0) assert _sort_anticommuting_fermions([Fd(i), Fd(a)]) == ([Fd(a), Fd(i)], 1) assert _sort_anticommuting_fermions([F(a), F(i)]) == ([F(i), F(a)], 1) assert _sort_anticommuting_fermions([F(i), F(a)]) == ([F(i), F(a)], 0) def test_contraction(): i, j, k, l = symbols('i,j,k,l', below_fermi=True) a, b, c, d = symbols('a,b,c,d', above_fermi=True) p, q, r, s = symbols('p,q,r,s') assert contraction(Fd(i), F(j)) == KroneckerDelta(i, j) assert contraction(F(a), Fd(b)) == KroneckerDelta(a, b) assert contraction(F(a), Fd(i)) == 0 assert contraction(Fd(a), F(i)) == 0 assert contraction(F(i), Fd(a)) == 0 assert contraction(Fd(i), F(a)) == 0 assert contraction(Fd(i), F(p)) == KroneckerDelta(i, p) restr = evaluate_deltas(contraction(Fd(p), F(q))) assert restr.is_only_below_fermi restr = evaluate_deltas(contraction(F(p), Fd(q))) assert restr.is_only_above_fermi def test_Tensors(): i, j, k, l = symbols('i j k l', below_fermi=True, cls=Dummy) a, b, c, d = symbols('a b c d', above_fermi=True, cls=Dummy) p, q, r, s = symbols('p q r s') AT = AntiSymmetricTensor assert AT('t', (a, b), (i, j)) == -AT('t', (b, a), (i, j)) assert AT('t', (a, b), (i, j)) == AT('t', (b, a), (j, i)) assert AT('t', (a, b), (i, j)) == -AT('t', (a, b), (j, i)) assert AT('t', (a, a), (i, j)) == 0 assert AT('t', (a, b), (i, i)) == 0 assert AT('t', (a, b, c), (i, j)) == -AT('t', (b, a, c), (i, j)) assert AT('t', (a, b, c), (i, j, k)) == AT('t', (b, a, c), (i, k, j)) tabij = AT('t', (a, b), (i, j)) assert tabij.has(a) assert tabij.has(b) assert tabij.has(i) assert tabij.has(j) assert tabij.subs(b, c) == AT('t', (a, c), (i, j)) assert (2*tabij).subs(i, c) == 2*AT('t', (a, b), (c, j)) assert AT('t', (a, a), (i, j)).subs(a, b) == AT('t', (b, b), (i, j)) assert AT('t', (a, i), (a, j)).subs(a, b) == AT('t', (b, i), (b, j)) def test_fully_contracted(): i, j, k, l = symbols('i j k l', below_fermi=True) a, b, c, d = symbols('a b c d', above_fermi=True) p, q, r, s = symbols('p q r s', cls=Dummy) Fock = (AntiSymmetricTensor('f', (p,), (q,))* NO(Fd(p)*F(q))) V = (AntiSymmetricTensor('v', (p, q), (r, s))* NO(Fd(p)*Fd(q)*F(s)*F(r)))/4 Fai = wicks(NO(Fd(i)*F(a))*Fock, keep_only_fully_contracted=True, simplify_kronecker_deltas=True) assert Fai == AntiSymmetricTensor('f', (a,), (i,)) Vabij = wicks(NO(Fd(i)*Fd(j)*F(b)*F(a))*V, keep_only_fully_contracted=True, simplify_kronecker_deltas=True) assert Vabij == AntiSymmetricTensor('v', (a, b), (i, j)) def test_substitute_dummies_without_dummies(): i, j = symbols('i,j') assert substitute_dummies(att(i, j) + 2) == att(i, j) + 2 assert substitute_dummies(att(i, j) + 1) == att(i, j) + 1 def test_substitute_dummies_NO_operator(): i, j = symbols('i j', cls=Dummy) assert substitute_dummies(att(i, j)*NO(Fd(i)*F(j)) - att(j, i)*NO(Fd(j)*F(i))) == 0 def test_substitute_dummies_SQ_operator(): i, j = symbols('i j', cls=Dummy) assert substitute_dummies(att(i, j)*Fd(i)*F(j) - att(j, i)*Fd(j)*F(i)) == 0 def test_substitute_dummies_new_indices(): 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') assert substitute_dummies(f(i, a, p) - f(j, b, q), new_indices=True) == 0 def test_substitute_dummies_substitution_order(): i, j, k, l = symbols('i j k l', below_fermi=True, cls=Dummy) f = Function('f') from sympy.utilities.iterables import variations for permut in variations([i, j, k, l], 4): assert substitute_dummies(f(*permut) - f(i, j, k, l)) == 0 def test_dummy_order_inner_outer_lines_VT1T1T1(): ii = symbols('i', below_fermi=True) aa = symbols('a', above_fermi=True) k, l = symbols('k l', below_fermi=True, cls=Dummy) c, d = symbols('c d', above_fermi=True, cls=Dummy) v = Function('v') t = Function('t') dums = _get_ordered_dummies # Coupled-Cluster T1 terms with V*T1*T1*T1 # t^{a}_{k} t^{c}_{i} t^{d}_{l} v^{lk}_{dc} exprs = [ # permut v and t <=> swapping internal lines, equivalent # irrespective of symmetries in v v(k, l, c, d)*t(c, ii)*t(d, l)*t(aa, k), v(l, k, c, d)*t(c, ii)*t(d, k)*t(aa, l), v(k, l, d, c)*t(d, ii)*t(c, l)*t(aa, k), v(l, k, d, c)*t(d, ii)*t(c, k)*t(aa, l), ] for permut in exprs[1:]: assert dums(exprs[0]) != dums(permut) assert substitute_dummies(exprs[0]) == substitute_dummies(permut) def test_dummy_order_inner_outer_lines_VT1T1T1T1(): ii, jj = symbols('i j', below_fermi=True) aa, bb = symbols('a b', above_fermi=True) k, l = symbols('k l', below_fermi=True, cls=Dummy) c, d = symbols('c d', above_fermi=True, cls=Dummy) v = Function('v') t = Function('t') dums = _get_ordered_dummies # Coupled-Cluster T2 terms with V*T1*T1*T1*T1 exprs = [ # permut t <=> swapping external lines, not equivalent # except if v has certain symmetries. v(k, l, c, d)*t(c, ii)*t(d, jj)*t(aa, k)*t(bb, l), v(k, l, c, d)*t(c, jj)*t(d, ii)*t(aa, k)*t(bb, l), v(k, l, c, d)*t(c, ii)*t(d, jj)*t(bb, k)*t(aa, l), v(k, l, c, d)*t(c, jj)*t(d, ii)*t(bb, k)*t(aa, l), ] for permut in exprs[1:]: assert dums(exprs[0]) != dums(permut) assert substitute_dummies(exprs[0]) != substitute_dummies(permut) exprs = [ # permut v <=> swapping external lines, not equivalent # except if v has certain symmetries. # # Note that in contrast to above, these permutations have identical # dummy order. That is because the proximity to external indices # has higher influence on the canonical dummy ordering than the # position of a dummy on the factors. In fact, the terms here are # similar in structure as the result of the dummy substitions above. v(k, l, c, d)*t(c, ii)*t(d, jj)*t(aa, k)*t(bb, l), v(l, k, c, d)*t(c, ii)*t(d, jj)*t(aa, k)*t(bb, l), v(k, l, d, c)*t(c, ii)*t(d, jj)*t(aa, k)*t(bb, l), v(l, k, d, c)*t(c, ii)*t(d, jj)*t(aa, k)*t(bb, l), ] for permut in exprs[1:]: assert dums(exprs[0]) == dums(permut) assert substitute_dummies(exprs[0]) != substitute_dummies(permut) exprs = [ # permut t and v <=> swapping internal lines, equivalent. # Canonical dummy order is different, and a consistent # substitution reveals the equivalence. v(k, l, c, d)*t(c, ii)*t(d, jj)*t(aa, k)*t(bb, l), v(k, l, d, c)*t(c, jj)*t(d, ii)*t(aa, k)*t(bb, l), v(l, k, c, d)*t(c, ii)*t(d, jj)*t(bb, k)*t(aa, l), v(l, k, d, c)*t(c, jj)*t(d, ii)*t(bb, k)*t(aa, l), ] for permut in exprs[1:]: assert dums(exprs[0]) != dums(permut) assert substitute_dummies(exprs[0]) == substitute_dummies(permut) def test_equivalent_internal_lines_VT1T1(): i, j, k, l = symbols('i j k l', below_fermi=True, cls=Dummy) a, b, c, d = symbols('a b c d', above_fermi=True, cls=Dummy) v = Function('v') t = Function('t') dums = _get_ordered_dummies exprs = [ # permute v. Different dummy order. Not equivalent. v(i, j, a, b)*t(a, i)*t(b, j), v(j, i, a, b)*t(a, i)*t(b, j), v(i, j, b, a)*t(a, i)*t(b, j), ] for permut in exprs[1:]: assert dums(exprs[0]) != dums(permut) assert substitute_dummies(exprs[0]) != substitute_dummies(permut) exprs = [ # permute v. Different dummy order. Equivalent v(i, j, a, b)*t(a, i)*t(b, j), v(j, i, b, a)*t(a, i)*t(b, j), ] for permut in exprs[1:]: assert dums(exprs[0]) != dums(permut) assert substitute_dummies(exprs[0]) == substitute_dummies(permut) exprs = [ # permute t. Same dummy order, not equivalent. v(i, j, a, b)*t(a, i)*t(b, j), v(i, j, a, b)*t(b, i)*t(a, j), ] for permut in exprs[1:]: assert dums(exprs[0]) == dums(permut) assert substitute_dummies(exprs[0]) != substitute_dummies(permut) exprs = [ # permute v and t. Different dummy order, equivalent v(i, j, a, b)*t(a, i)*t(b, j), v(j, i, a, b)*t(a, j)*t(b, i), v(i, j, b, a)*t(b, i)*t(a, j), v(j, i, b, a)*t(b, j)*t(a, i), ] for permut in exprs[1:]: assert dums(exprs[0]) != dums(permut) assert substitute_dummies(exprs[0]) == substitute_dummies(permut) def test_equivalent_internal_lines_VT2conjT2(): # this diagram requires special handling in TCE i, j, k, l, m, n = symbols('i j k l m n', below_fermi=True, cls=Dummy) a, b, c, d, e, f = symbols('a b c d e f', above_fermi=True, cls=Dummy) p1, p2, p3, p4 = symbols('p1 p2 p3 p4', above_fermi=True, cls=Dummy) h1, h2, h3, h4 = symbols('h1 h2 h3 h4', below_fermi=True, cls=Dummy) from sympy.utilities.iterables import variations v = Function('v') t = Function('t') dums = _get_ordered_dummies # v(abcd)t(abij)t(ijcd) template = v(p1, p2, p3, p4)*t(p1, p2, i, j)*t(i, j, p3, p4) permutator = variations([a, b, c, d], 4) base = template.subs(zip([p1, p2, p3, p4], next(permutator))) for permut in permutator: subslist = zip([p1, p2, p3, p4], permut) expr = template.subs(subslist) assert dums(base) != dums(expr) assert substitute_dummies(expr) == substitute_dummies(base) template = v(p1, p2, p3, p4)*t(p1, p2, j, i)*t(j, i, p3, p4) permutator = variations([a, b, c, d], 4) base = template.subs(zip([p1, p2, p3, p4], next(permutator))) for permut in permutator: subslist = zip([p1, p2, p3, p4], permut) expr = template.subs(subslist) assert dums(base) != dums(expr) assert substitute_dummies(expr) == substitute_dummies(base) # v(abcd)t(abij)t(jicd) template = v(p1, p2, p3, p4)*t(p1, p2, i, j)*t(j, i, p3, p4) permutator = variations([a, b, c, d], 4) base = template.subs(zip([p1, p2, p3, p4], next(permutator))) for permut in permutator: subslist = zip([p1, p2, p3, p4], permut) expr = template.subs(subslist) assert dums(base) != dums(expr) assert substitute_dummies(expr) == substitute_dummies(base) template = v(p1, p2, p3, p4)*t(p1, p2, j, i)*t(i, j, p3, p4) permutator = variations([a, b, c, d], 4) base = template.subs(zip([p1, p2, p3, p4], next(permutator))) for permut in permutator: subslist = zip([p1, p2, p3, p4], permut) expr = template.subs(subslist) assert dums(base) != dums(expr) assert substitute_dummies(expr) == substitute_dummies(base) def test_equivalent_internal_lines_VT2conjT2_ambiguous_order(): # These diagrams invokes _determine_ambiguous() because the # dummies can not be ordered unambiguously by the key alone i, j, k, l, m, n = symbols('i j k l m n', below_fermi=True, cls=Dummy) a, b, c, d, e, f = symbols('a b c d e f', above_fermi=True, cls=Dummy) p1, p2, p3, p4 = symbols('p1 p2 p3 p4', above_fermi=True, cls=Dummy) h1, h2, h3, h4 = symbols('h1 h2 h3 h4', below_fermi=True, cls=Dummy) from sympy.utilities.iterables import variations v = Function('v') t = Function('t') dums = _get_ordered_dummies # v(abcd)t(abij)t(cdij) template = v(p1, p2, p3, p4)*t(p1, p2, i, j)*t(p3, p4, i, j) permutator = variations([a, b, c, d], 4) base = template.subs(zip([p1, p2, p3, p4], next(permutator))) for permut in permutator: subslist = zip([p1, p2, p3, p4], permut) expr = template.subs(subslist) assert dums(base) != dums(expr) assert substitute_dummies(expr) == substitute_dummies(base) template = v(p1, p2, p3, p4)*t(p1, p2, j, i)*t(p3, p4, i, j) permutator = variations([a, b, c, d], 4) base = template.subs(zip([p1, p2, p3, p4], next(permutator))) for permut in permutator: subslist = zip([p1, p2, p3, p4], permut) expr = template.subs(subslist) assert dums(base) != dums(expr) assert substitute_dummies(expr) == substitute_dummies(base) def test_equivalent_internal_lines_VT2(): i, j, k, l = symbols('i j k l', below_fermi=True, cls=Dummy) a, b, c, d = symbols('a b c d', above_fermi=True, cls=Dummy) v = Function('v') t = Function('t') dums = _get_ordered_dummies exprs = [ # permute v. Same dummy order, not equivalent. # # This test show that the dummy order may not be sensitive to all # index permutations. The following expressions have identical # structure as the resulting terms from of the dummy subsitutions # in the test above. Here, all expressions have the same dummy # order, so they cannot be simplified by means of dummy # substitution. In order to simplify further, it is necessary to # exploit symmetries in the objects, for instance if t or v is # antisymmetric. v(i, j, a, b)*t(a, b, i, j), v(j, i, a, b)*t(a, b, i, j), v(i, j, b, a)*t(a, b, i, j), v(j, i, b, a)*t(a, b, i, j), ] for permut in exprs[1:]: assert dums(exprs[0]) == dums(permut) assert substitute_dummies(exprs[0]) != substitute_dummies(permut) exprs = [ # permute t. v(i, j, a, b)*t(a, b, i, j), v(i, j, a, b)*t(b, a, i, j), v(i, j, a, b)*t(a, b, j, i), v(i, j, a, b)*t(b, a, j, i), ] for permut in exprs[1:]: assert dums(exprs[0]) != dums(permut) assert substitute_dummies(exprs[0]) != substitute_dummies(permut) exprs = [ # permute v and t. Relabelling of dummies should be equivalent. v(i, j, a, b)*t(a, b, i, j), v(j, i, a, b)*t(a, b, j, i), v(i, j, b, a)*t(b, a, i, j), v(j, i, b, a)*t(b, a, j, i), ] for permut in exprs[1:]: assert dums(exprs[0]) != dums(permut) assert substitute_dummies(exprs[0]) == substitute_dummies(permut) def test_internal_external_VT2T2(): ii, jj = symbols('i j', below_fermi=True) aa, bb = symbols('a b', above_fermi=True) k, l = symbols('k l', below_fermi=True, cls=Dummy) c, d = symbols('c d', above_fermi=True, cls=Dummy) v = Function('v') t = Function('t') dums = _get_ordered_dummies exprs = [ v(k, l, c, d)*t(aa, c, ii, k)*t(bb, d, jj, l), v(l, k, c, d)*t(aa, c, ii, l)*t(bb, d, jj, k), v(k, l, d, c)*t(aa, d, ii, k)*t(bb, c, jj, l), v(l, k, d, c)*t(aa, d, ii, l)*t(bb, c, jj, k), ] for permut in exprs[1:]: assert dums(exprs[0]) != dums(permut) assert substitute_dummies(exprs[0]) == substitute_dummies(permut) exprs = [ v(k, l, c, d)*t(aa, c, ii, k)*t(d, bb, jj, l), v(l, k, c, d)*t(aa, c, ii, l)*t(d, bb, jj, k), v(k, l, d, c)*t(aa, d, ii, k)*t(c, bb, jj, l), v(l, k, d, c)*t(aa, d, ii, l)*t(c, bb, jj, k), ] for permut in exprs[1:]: assert dums(exprs[0]) != dums(permut) assert substitute_dummies(exprs[0]) == substitute_dummies(permut) exprs = [ v(k, l, c, d)*t(c, aa, ii, k)*t(bb, d, jj, l), v(l, k, c, d)*t(c, aa, ii, l)*t(bb, d, jj, k), v(k, l, d, c)*t(d, aa, ii, k)*t(bb, c, jj, l), v(l, k, d, c)*t(d, aa, ii, l)*t(bb, c, jj, k), ] for permut in exprs[1:]: assert dums(exprs[0]) != dums(permut) assert substitute_dummies(exprs[0]) == substitute_dummies(permut) def test_internal_external_pqrs(): ii, jj = symbols('i j') aa, bb = symbols('a b') k, l = symbols('k l', cls=Dummy) c, d = symbols('c d', cls=Dummy) v = Function('v') t = Function('t') dums = _get_ordered_dummies exprs = [ v(k, l, c, d)*t(aa, c, ii, k)*t(bb, d, jj, l), v(l, k, c, d)*t(aa, c, ii, l)*t(bb, d, jj, k), v(k, l, d, c)*t(aa, d, ii, k)*t(bb, c, jj, l), v(l, k, d, c)*t(aa, d, ii, l)*t(bb, c, jj, k), ] for permut in exprs[1:]: assert dums(exprs[0]) != dums(permut) assert substitute_dummies(exprs[0]) == substitute_dummies(permut) def test_dummy_order_well_defined(): aa, bb = symbols('a b', above_fermi=True) k, l, m = symbols('k l m', below_fermi=True, cls=Dummy) c, d = symbols('c d', above_fermi=True, cls=Dummy) p, q = symbols('p q', cls=Dummy) A = Function('A') B = Function('B') C = Function('C') dums = _get_ordered_dummies # We go through all key components in the order of increasing priority, # and consider only fully orderable expressions. Non-orderable expressions # are tested elsewhere. # pos in first factor determines sort order assert dums(A(k, l)*B(l, k)) == [k, l] assert dums(A(l, k)*B(l, k)) == [l, k] assert dums(A(k, l)*B(k, l)) == [k, l] assert dums(A(l, k)*B(k, l)) == [l, k] # factors involving the index assert dums(A(k, l)*B(l, m)*C(k, m)) == [l, k, m] assert dums(A(k, l)*B(l, m)*C(m, k)) == [l, k, m] assert dums(A(l, k)*B(l, m)*C(k, m)) == [l, k, m] assert dums(A(l, k)*B(l, m)*C(m, k)) == [l, k, m] assert dums(A(k, l)*B(m, l)*C(k, m)) == [l, k, m] assert dums(A(k, l)*B(m, l)*C(m, k)) == [l, k, m] assert dums(A(l, k)*B(m, l)*C(k, m)) == [l, k, m] assert dums(A(l, k)*B(m, l)*C(m, k)) == [l, k, m] # same, but with factor order determined by non-dummies assert dums(A(k, aa, l)*A(l, bb, m)*A(bb, k, m)) == [l, k, m] assert dums(A(k, aa, l)*A(l, bb, m)*A(bb, m, k)) == [l, k, m] assert dums(A(k, aa, l)*A(m, bb, l)*A(bb, k, m)) == [l, k, m] assert dums(A(k, aa, l)*A(m, bb, l)*A(bb, m, k)) == [l, k, m] assert dums(A(l, aa, k)*A(l, bb, m)*A(bb, k, m)) == [l, k, m] assert dums(A(l, aa, k)*A(l, bb, m)*A(bb, m, k)) == [l, k, m] assert dums(A(l, aa, k)*A(m, bb, l)*A(bb, k, m)) == [l, k, m] assert dums(A(l, aa, k)*A(m, bb, l)*A(bb, m, k)) == [l, k, m] # index range assert dums(A(p, c, k)*B(p, c, k)) == [k, c, p] assert dums(A(p, k, c)*B(p, c, k)) == [k, c, p] assert dums(A(c, k, p)*B(p, c, k)) == [k, c, p] assert dums(A(c, p, k)*B(p, c, k)) == [k, c, p] assert dums(A(k, c, p)*B(p, c, k)) == [k, c, p] assert dums(A(k, p, c)*B(p, c, k)) == [k, c, p] assert dums(B(p, c, k)*A(p, c, k)) == [k, c, p] assert dums(B(p, k, c)*A(p, c, k)) == [k, c, p] assert dums(B(c, k, p)*A(p, c, k)) == [k, c, p] assert dums(B(c, p, k)*A(p, c, k)) == [k, c, p] assert dums(B(k, c, p)*A(p, c, k)) == [k, c, p] assert dums(B(k, p, c)*A(p, c, k)) == [k, c, p] def test_dummy_order_ambiguous(): aa, bb = symbols('a b', above_fermi=True) i, j, k, l, m = symbols('i j k l m', below_fermi=True, cls=Dummy) a, b, c, d, e = symbols('a b c d e', above_fermi=True, cls=Dummy) p, q = symbols('p q', cls=Dummy) p1, p2, p3, p4 = symbols('p1 p2 p3 p4', above_fermi=True, cls=Dummy) p5, p6, p7, p8 = symbols('p5 p6 p7 p8', above_fermi=True, cls=Dummy) h1, h2, h3, h4 = symbols('h1 h2 h3 h4', below_fermi=True, cls=Dummy) h5, h6, h7, h8 = symbols('h5 h6 h7 h8', below_fermi=True, cls=Dummy) A = Function('A') B = Function('B') from sympy.utilities.iterables import variations # A*A*A*A*B -- ordering of p5 and p4 is used to figure out the rest template = A(p1, p2)*A(p4, p1)*A(p2, p3)*A(p3, p5)*B(p5, p4) permutator = variations([a, b, c, d, e], 5) base = template.subs(zip([p1, p2, p3, p4, p5], next(permutator))) for permut in permutator: subslist = zip([p1, p2, p3, p4, p5], permut) expr = template.subs(subslist) assert substitute_dummies(expr) == substitute_dummies(base) # A*A*A*A*A -- an arbitrary index is assigned and the rest are figured out template = A(p1, p2)*A(p4, p1)*A(p2, p3)*A(p3, p5)*A(p5, p4) permutator = variations([a, b, c, d, e], 5) base = template.subs(zip([p1, p2, p3, p4, p5], next(permutator))) for permut in permutator: subslist = zip([p1, p2, p3, p4, p5], permut) expr = template.subs(subslist) assert substitute_dummies(expr) == substitute_dummies(base) # A*A*A -- ordering of p5 and p4 is used to figure out the rest template = A(p1, p2, p4, p1)*A(p2, p3, p3, p5)*A(p5, p4) permutator = variations([a, b, c, d, e], 5) base = template.subs(zip([p1, p2, p3, p4, p5], next(permutator))) for permut in permutator: subslist = zip([p1, p2, p3, p4, p5], permut) expr = template.subs(subslist) assert substitute_dummies(expr) == substitute_dummies(base) def atv(*args): return AntiSymmetricTensor('v', args[:2], args[2:] ) def att(*args): if len(args) == 4: return AntiSymmetricTensor('t', args[:2], args[2:] ) elif len(args) == 2: return AntiSymmetricTensor('t', (args[0],), (args[1],)) def test_dummy_order_inner_outer_lines_VT1T1T1_AT(): ii = symbols('i', below_fermi=True) aa = symbols('a', above_fermi=True) k, l = symbols('k l', below_fermi=True, cls=Dummy) c, d = symbols('c d', above_fermi=True, cls=Dummy) # Coupled-Cluster T1 terms with V*T1*T1*T1 # t^{a}_{k} t^{c}_{i} t^{d}_{l} v^{lk}_{dc} exprs = [ # permut v and t <=> swapping internal lines, equivalent # irrespective of symmetries in v atv(k, l, c, d)*att(c, ii)*att(d, l)*att(aa, k), atv(l, k, c, d)*att(c, ii)*att(d, k)*att(aa, l), atv(k, l, d, c)*att(d, ii)*att(c, l)*att(aa, k), atv(l, k, d, c)*att(d, ii)*att(c, k)*att(aa, l), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) == substitute_dummies(permut) def test_dummy_order_inner_outer_lines_VT1T1T1T1_AT(): ii, jj = symbols('i j', below_fermi=True) aa, bb = symbols('a b', above_fermi=True) k, l = symbols('k l', below_fermi=True, cls=Dummy) c, d = symbols('c d', above_fermi=True, cls=Dummy) # Coupled-Cluster T2 terms with V*T1*T1*T1*T1 # non-equivalent substitutions (change of sign) exprs = [ # permut t <=> swapping external lines atv(k, l, c, d)*att(c, ii)*att(d, jj)*att(aa, k)*att(bb, l), atv(k, l, c, d)*att(c, jj)*att(d, ii)*att(aa, k)*att(bb, l), atv(k, l, c, d)*att(c, ii)*att(d, jj)*att(bb, k)*att(aa, l), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) == -substitute_dummies(permut) # equivalent substitutions exprs = [ atv(k, l, c, d)*att(c, ii)*att(d, jj)*att(aa, k)*att(bb, l), # permut t <=> swapping external lines atv(k, l, c, d)*att(c, jj)*att(d, ii)*att(bb, k)*att(aa, l), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) == substitute_dummies(permut) def test_equivalent_internal_lines_VT1T1_AT(): i, j, k, l = symbols('i j k l', below_fermi=True, cls=Dummy) a, b, c, d = symbols('a b c d', above_fermi=True, cls=Dummy) exprs = [ # permute v. Different dummy order. Not equivalent. atv(i, j, a, b)*att(a, i)*att(b, j), atv(j, i, a, b)*att(a, i)*att(b, j), atv(i, j, b, a)*att(a, i)*att(b, j), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) != substitute_dummies(permut) exprs = [ # permute v. Different dummy order. Equivalent atv(i, j, a, b)*att(a, i)*att(b, j), atv(j, i, b, a)*att(a, i)*att(b, j), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) == substitute_dummies(permut) exprs = [ # permute t. Same dummy order, not equivalent. atv(i, j, a, b)*att(a, i)*att(b, j), atv(i, j, a, b)*att(b, i)*att(a, j), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) != substitute_dummies(permut) exprs = [ # permute v and t. Different dummy order, equivalent atv(i, j, a, b)*att(a, i)*att(b, j), atv(j, i, a, b)*att(a, j)*att(b, i), atv(i, j, b, a)*att(b, i)*att(a, j), atv(j, i, b, a)*att(b, j)*att(a, i), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) == substitute_dummies(permut) def test_equivalent_internal_lines_VT2conjT2_AT(): # this diagram requires special handling in TCE i, j, k, l, m, n = symbols('i j k l m n', below_fermi=True, cls=Dummy) a, b, c, d, e, f = symbols('a b c d e f', above_fermi=True, cls=Dummy) p1, p2, p3, p4 = symbols('p1 p2 p3 p4', above_fermi=True, cls=Dummy) h1, h2, h3, h4 = symbols('h1 h2 h3 h4', below_fermi=True, cls=Dummy) from sympy.utilities.iterables import variations # atv(abcd)att(abij)att(ijcd) template = atv(p1, p2, p3, p4)*att(p1, p2, i, j)*att(i, j, p3, p4) permutator = variations([a, b, c, d], 4) base = template.subs(zip([p1, p2, p3, p4], next(permutator))) for permut in permutator: subslist = zip([p1, p2, p3, p4], permut) expr = template.subs(subslist) assert substitute_dummies(expr) == substitute_dummies(base) template = atv(p1, p2, p3, p4)*att(p1, p2, j, i)*att(j, i, p3, p4) permutator = variations([a, b, c, d], 4) base = template.subs(zip([p1, p2, p3, p4], next(permutator))) for permut in permutator: subslist = zip([p1, p2, p3, p4], permut) expr = template.subs(subslist) assert substitute_dummies(expr) == substitute_dummies(base) # atv(abcd)att(abij)att(jicd) template = atv(p1, p2, p3, p4)*att(p1, p2, i, j)*att(j, i, p3, p4) permutator = variations([a, b, c, d], 4) base = template.subs(zip([p1, p2, p3, p4], next(permutator))) for permut in permutator: subslist = zip([p1, p2, p3, p4], permut) expr = template.subs(subslist) assert substitute_dummies(expr) == substitute_dummies(base) template = atv(p1, p2, p3, p4)*att(p1, p2, j, i)*att(i, j, p3, p4) permutator = variations([a, b, c, d], 4) base = template.subs(zip([p1, p2, p3, p4], next(permutator))) for permut in permutator: subslist = zip([p1, p2, p3, p4], permut) expr = template.subs(subslist) assert substitute_dummies(expr) == substitute_dummies(base) def test_equivalent_internal_lines_VT2conjT2_ambiguous_order_AT(): # These diagrams invokes _determine_ambiguous() because the # dummies can not be ordered unambiguously by the key alone i, j, k, l, m, n = symbols('i j k l m n', below_fermi=True, cls=Dummy) a, b, c, d, e, f = symbols('a b c d e f', above_fermi=True, cls=Dummy) p1, p2, p3, p4 = symbols('p1 p2 p3 p4', above_fermi=True, cls=Dummy) h1, h2, h3, h4 = symbols('h1 h2 h3 h4', below_fermi=True, cls=Dummy) from sympy.utilities.iterables import variations # atv(abcd)att(abij)att(cdij) template = atv(p1, p2, p3, p4)*att(p1, p2, i, j)*att(p3, p4, i, j) permutator = variations([a, b, c, d], 4) base = template.subs(zip([p1, p2, p3, p4], next(permutator))) for permut in permutator: subslist = zip([p1, p2, p3, p4], permut) expr = template.subs(subslist) assert substitute_dummies(expr) == substitute_dummies(base) template = atv(p1, p2, p3, p4)*att(p1, p2, j, i)*att(p3, p4, i, j) permutator = variations([a, b, c, d], 4) base = template.subs(zip([p1, p2, p3, p4], next(permutator))) for permut in permutator: subslist = zip([p1, p2, p3, p4], permut) expr = template.subs(subslist) assert substitute_dummies(expr) == substitute_dummies(base) def test_equivalent_internal_lines_VT2_AT(): i, j, k, l = symbols('i j k l', below_fermi=True, cls=Dummy) a, b, c, d = symbols('a b c d', above_fermi=True, cls=Dummy) exprs = [ # permute v. Same dummy order, not equivalent. atv(i, j, a, b)*att(a, b, i, j), atv(j, i, a, b)*att(a, b, i, j), atv(i, j, b, a)*att(a, b, i, j), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) != substitute_dummies(permut) exprs = [ # permute t. atv(i, j, a, b)*att(a, b, i, j), atv(i, j, a, b)*att(b, a, i, j), atv(i, j, a, b)*att(a, b, j, i), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) != substitute_dummies(permut) exprs = [ # permute v and t. Relabelling of dummies should be equivalent. atv(i, j, a, b)*att(a, b, i, j), atv(j, i, a, b)*att(a, b, j, i), atv(i, j, b, a)*att(b, a, i, j), atv(j, i, b, a)*att(b, a, j, i), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) == substitute_dummies(permut) def test_internal_external_VT2T2_AT(): ii, jj = symbols('i j', below_fermi=True) aa, bb = symbols('a b', above_fermi=True) k, l = symbols('k l', below_fermi=True, cls=Dummy) c, d = symbols('c d', above_fermi=True, cls=Dummy) exprs = [ atv(k, l, c, d)*att(aa, c, ii, k)*att(bb, d, jj, l), atv(l, k, c, d)*att(aa, c, ii, l)*att(bb, d, jj, k), atv(k, l, d, c)*att(aa, d, ii, k)*att(bb, c, jj, l), atv(l, k, d, c)*att(aa, d, ii, l)*att(bb, c, jj, k), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) == substitute_dummies(permut) exprs = [ atv(k, l, c, d)*att(aa, c, ii, k)*att(d, bb, jj, l), atv(l, k, c, d)*att(aa, c, ii, l)*att(d, bb, jj, k), atv(k, l, d, c)*att(aa, d, ii, k)*att(c, bb, jj, l), atv(l, k, d, c)*att(aa, d, ii, l)*att(c, bb, jj, k), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) == substitute_dummies(permut) exprs = [ atv(k, l, c, d)*att(c, aa, ii, k)*att(bb, d, jj, l), atv(l, k, c, d)*att(c, aa, ii, l)*att(bb, d, jj, k), atv(k, l, d, c)*att(d, aa, ii, k)*att(bb, c, jj, l), atv(l, k, d, c)*att(d, aa, ii, l)*att(bb, c, jj, k), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) == substitute_dummies(permut) def test_internal_external_pqrs_AT(): ii, jj = symbols('i j') aa, bb = symbols('a b') k, l = symbols('k l', cls=Dummy) c, d = symbols('c d', cls=Dummy) exprs = [ atv(k, l, c, d)*att(aa, c, ii, k)*att(bb, d, jj, l), atv(l, k, c, d)*att(aa, c, ii, l)*att(bb, d, jj, k), atv(k, l, d, c)*att(aa, d, ii, k)*att(bb, c, jj, l), atv(l, k, d, c)*att(aa, d, ii, l)*att(bb, c, jj, k), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) == substitute_dummies(permut) sympy-0.7.4.1/sympy/physics/tests/test_physics_matrices.py0000644000175000017500000000424412253362407024237 0ustar georgeskgeorgeskfrom sympy.physics.matrices import msigma, mgamma, minkowski_tensor, pat_matrix from sympy import zeros, eye, I, Matrix def test_parallel_axis_theorem(): # This tests the parallel axis theorem matrix by comparing to test # matrices. # First case, 1 in all directions. mat1 = Matrix(((2, -1, -1), (-1, 2, -1), (-1, -1, 2))) assert pat_matrix(1, 1, 1, 1) == mat1 assert pat_matrix(2, 1, 1, 1) == 2*mat1 # Second case, 1 in x, 0 in all others mat2 = Matrix(((0, 0, 0), (0, 1, 0), (0, 0, 1))) assert pat_matrix(1, 1, 0, 0) == mat2 assert pat_matrix(2, 1, 0, 0) == 2*mat2 # Third case, 1 in y, 0 in all others mat3 = Matrix(((1, 0, 0), (0, 0, 0), (0, 0, 1))) assert pat_matrix(1, 0, 1, 0) == mat3 assert pat_matrix(2, 0, 1, 0) == 2*mat3 # Fourth case, 1 in z, 0 in all others mat4 = Matrix(((1, 0, 0), (0, 1, 0), (0, 0, 0))) assert pat_matrix(1, 0, 0, 1) == mat4 assert pat_matrix(2, 0, 0, 1) == 2*mat4 def test_Pauli(): #this and the following test are testing both Pauli and Dirac matrices #and also that the general Matrix class works correctly in a real world #situation sigma1 = msigma(1) sigma2 = msigma(2) sigma3 = msigma(3) assert sigma1 == sigma1 assert sigma1 != sigma2 # sigma*I -> I*sigma (see #354) assert sigma1*sigma2 == sigma3*I assert sigma3*sigma1 == sigma2*I assert sigma2*sigma3 == sigma1*I assert sigma1*sigma1 == eye(2) assert sigma2*sigma2 == eye(2) assert sigma3*sigma3 == eye(2) assert sigma1*2*sigma1 == 2*eye(2) assert sigma1*sigma3*sigma1 == -sigma3 def test_Dirac(): gamma0 = mgamma(0) gamma1 = mgamma(1) gamma2 = mgamma(2) gamma3 = mgamma(3) gamma5 = mgamma(5) # gamma*I -> I*gamma (see #354) assert gamma5 == gamma0 * gamma1 * gamma2 * gamma3 * I assert gamma1 * gamma2 + gamma2 * gamma1 == zeros(4) assert gamma0 * gamma0 == eye(4) * minkowski_tensor[0, 0] assert gamma2 * gamma2 != eye(4) * minkowski_tensor[0, 0] assert gamma2 * gamma2 == eye(4) * minkowski_tensor[2, 2] assert mgamma(5, True) == \ mgamma(0, True)*mgamma(1, True)*mgamma(2, True)*mgamma(3, True)*I sympy-0.7.4.1/sympy/physics/tests/test_sho.py0000644000175000017500000000124312253362407021453 0ustar georgeskgeorgeskfrom sympy.core import symbols, Rational, Function, diff from sympy.physics.sho import R_nl, E_nl from sympy import simplify def test_sho_R_nl(): omega, r = symbols('omega r') l = symbols('l', integer=True) u = Function('u') # check that it obeys the Schrodinger equation for n in range(5): schreq = ( -diff(u(r), r, 2)/2 + ((l*(l + 1))/(2*r**2) + omega**2*r**2/2 - E_nl(n, l, omega))*u(r) ) result = schreq.subs(u(r), r*R_nl(n, l, omega/2, r)) assert simplify(result.doit()) == 0 def test_energy(): n, l, hw = symbols('n l hw') assert simplify(E_nl(n, l, hw) - (2*n + l + Rational(3, 2))*hw) == 0 sympy-0.7.4.1/sympy/physics/tests/test_hydrogen.py0000644000175000017500000001004312253362407022477 0ustar georgeskgeorgeskfrom sympy import exp, integrate, oo, S, simplify, sqrt, symbols from sympy.physics.hydrogen import R_nl, E_nl, E_nl_dirac from sympy.utilities.pytest import raises n, r, Z = symbols('n r Z') def feq(a, b, max_relative_error=1e-12, max_absolute_error=1e-12): a = float(a) b = float(b) # if the numbers are close enough (absolutely), then they are equal if abs(a - b) < max_absolute_error: return True # if not, they can still be equal if their relative error is small if abs(b) > abs(a): relative_error = abs((a - b)/b) else: relative_error = abs((a - b)/a) return relative_error <= max_relative_error def test_wavefunction(): a = 1/Z R = { (1, 0): 2*sqrt(1/a**3) * exp(-r/a), (2, 0): sqrt(1/(2*a**3)) * exp(-r/(2*a)) * (1 - r/(2*a)), (2, 1): S(1)/2 * sqrt(1/(6*a**3)) * exp(-r/(2*a)) * r/a, (3, 0): S(2)/3 * sqrt(1/(3*a**3)) * exp(-r/(3*a)) * (1 - 2*r/(3*a) + S(2)/27 * (r/a)**2), (3, 1): S(4)/27 * sqrt(2/(3*a**3)) * exp(-r/(3*a)) * (1 - r/(6*a)) * r/a, (3, 2): S(2)/81 * sqrt(2/(15*a**3)) * exp(-r/(3*a)) * (r/a)**2, (4, 0): S(1)/4 * sqrt(1/a**3) * exp(-r/(4*a)) * (1 - 3*r/(4*a) + S(1)/8 * (r/a)**2 - S(1)/192 * (r/a)**3), (4, 1): S(1)/16 * sqrt(5/(3*a**3)) * exp(-r/(4*a)) * (1 - r/(4*a) + S(1)/80 * (r/a)**2) * (r/a), (4, 2): S(1)/64 * sqrt(1/(5*a**3)) * exp(-r/(4*a)) * (1 - r/(12*a)) * (r/a)**2, (4, 3): S(1)/768 * sqrt(1/(35*a**3)) * exp(-r/(4*a)) * (r/a)**3, } for n, l in R: assert simplify(R_nl(n, l, r, Z) - R[(n, l)]) == 0 def test_norm(): # Maximum "n" which is tested: n_max = 2 # you can test any n and it works, but it's slow, so it's commented out: #n_max = 4 for n in range(n_max + 1): for l in range(n): assert integrate(R_nl(n, l, r)**2 * r**2, (r, 0, oo)) == 1 def test_hydrogen_energies(): assert E_nl(n, Z) == -Z**2/(2*n**2) assert E_nl(n) == -1/(2*n**2) assert E_nl(1, 47) == -S(47)**2/(2*1**2) assert E_nl(2, 47) == -S(47)**2/(2*2**2) assert E_nl(1) == -S(1)/(2*1**2) assert E_nl(2) == -S(1)/(2*2**2) assert E_nl(3) == -S(1)/(2*3**2) assert E_nl(4) == -S(1)/(2*4**2) assert E_nl(100) == -S(1)/(2*100**2) raises(ValueError, lambda: E_nl(0)) def test_hydrogen_energies_relat(): # First test exact formulas for small "c" so that we get nice expressions: assert E_nl_dirac(2, 0, Z=1, c=1) == 1/sqrt(2) - 1 assert simplify(E_nl_dirac(2, 0, Z=1, c=2) - ( (8*sqrt(3) + 16) / sqrt(16*sqrt(3) + 32) - 4)) == 0 assert simplify(E_nl_dirac(2, 0, Z=1, c=3) - ( (54*sqrt(2) + 81) / sqrt(108*sqrt(2) + 162) - 9)) == 0 # Now test for almost the correct speed of light, without floating point # numbers: assert simplify(E_nl_dirac(2, 0, Z=1, c=137) - ( (352275361 + 10285412 * sqrt(1173)) / sqrt(704550722 + 20570824 * sqrt(1173)) - 18769)) == 0 assert simplify(E_nl_dirac(2, 0, Z=82, c=137) - ( (352275361 + 2571353 * sqrt(12045)) / sqrt(704550722 + 5142706*sqrt(12045)) - 18769)) == 0 # Test using exact speed of light, and compare against the nonrelativistic # energies: for n in range(1, 5): for l in range(n): assert feq(E_nl_dirac(n, l), E_nl(n), 1e-5, 1e-5) if l > 0: assert feq(E_nl_dirac(n, l, False), E_nl(n), 1e-5, 1e-5) Z = 2 for n in range(1, 5): for l in range(n): assert feq(E_nl_dirac(n, l, Z=Z), E_nl(n, Z), 1e-4, 1e-4) if l > 0: assert feq(E_nl_dirac(n, l, False, Z), E_nl(n, Z), 1e-4, 1e-4) Z = 3 for n in range(1, 5): for l in range(n): assert feq(E_nl_dirac(n, l, Z=Z), E_nl(n, Z), 1e-3, 1e-3) if l > 0: assert feq(E_nl_dirac(n, l, False, Z), E_nl(n, Z), 1e-3, 1e-3) # Test the exceptions: raises(ValueError, lambda: E_nl_dirac(0, 0)) raises(ValueError, lambda: E_nl_dirac(1, -1)) raises(ValueError, lambda: E_nl_dirac(1, 0, False)) sympy-0.7.4.1/sympy/physics/tests/test_units.py0000644000175000017500000000204612253362407022026 0ustar georgeskgeorgeskfrom sympy import integrate, Rational, sqrt, Symbol from sympy.physics.units import (au, amu, charge, day, find_unit, foot, km, m, meter, minute, s, speed_of_light, grams, quart, inch) def test_units(): assert (5*m/s * day) / km == 432 assert foot / meter == Rational('0.3048') # amu is a pure mass so mass/mass gives a number, not an amount (mol) assert str(grams/(amu).n(2)) == '6.0e+23' # Light from the sun needs about 8.3 minutes to reach earth t = (1*au / speed_of_light).evalf() / minute assert abs(t - 8.31) < 0.1 assert sqrt(m**2) == m assert (sqrt(m))**2 == m t = Symbol('t') assert integrate(t*m/s, (t, 1*s, 5*s)) == 12*m*s assert (t * m/s).integrate((t, 1*s, 5*s)) == 12*m*s def test_issue_quart(): assert 4*quart/inch**3 == 231 def test_issue_2466(): assert (m < s).is_Relational def test_find_unit(): assert find_unit('charge') == ['charge'] assert find_unit(charge) == ['C', 'charge', 'coulomb', 'coulombs'] sympy-0.7.4.1/sympy/physics/tests/test_paulialgebra.py0000644000175000017500000000165512253362407023321 0ustar georgeskgeorgeskfrom sympy import I from sympy.physics.paulialgebra import Pauli from sympy.utilities.pytest import XFAIL sigma1 = Pauli(1) sigma2 = Pauli(2) sigma3 = Pauli(3) def test_Pauli(): from sympy.physics.paulialgebra import evaluate_pauli_product assert sigma1 == sigma1 assert sigma1 != sigma2 assert sigma1*sigma2 == I*sigma3 assert sigma3*sigma1 == I*sigma2 assert sigma2*sigma3 == I*sigma1 assert sigma1*sigma1 == 1 assert sigma2*sigma2 == 1 assert sigma3*sigma3 == 1 assert evaluate_pauli_product(I*sigma2*sigma3) == -sigma1 assert sigma1**0 == 1 assert sigma1**1 == sigma1 assert sigma1**2 == 1 assert sigma1**3 == sigma1 assert sigma1**4 == 1 assert sigma3**2 == 1 assert sigma1*2*sigma1 == 2 # Check bug 3372 assert evaluate_pauli_product(-I*4*sigma1*sigma2) == 4*sigma3 @XFAIL def test_Pauli_should_work(): assert sigma1*sigma3*sigma1 == -sigma3 sympy-0.7.4.1/sympy/physics/tests/__init__.py0000644000175000017500000000000012253362407021350 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/physics/__init__.py0000644000175000017500000000020412253362407020214 0ustar georgeskgeorgesk""" A module that helps solving problems in physics """ from . import units from .matrices import mgamma, msigma, minkowski_tensor sympy-0.7.4.1/sympy/physics/matrices.py0000644000175000017500000000640412253362407020274 0ustar georgeskgeorgesk"""Known matrices related to physics""" from __future__ import print_function, division from sympy import Matrix, I def msigma(i): """Returns a Pauli matrix sigma_i. i=1,2,3 References ========== .. [1] http://en.wikipedia.org/wiki/Pauli_matrices Examples ======== >>> from sympy.physics.matrices import msigma >>> msigma(1) Matrix([ [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) 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) Matrix([ [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) Matrix([ [ 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) 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 References ========== .. [1] http://en.wikipedia.org/wiki/Gamma_matrices Examples ======== >>> from sympy.physics.matrices import mgamma >>> mgamma(1) Matrix([ [ 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) )) sympy-0.7.4.1/sympy/simplify/0000755000175000017500000000000012253362407016261 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/simplify/epathtools.py0000644000175000017500000002401212253362407021014 0ustar georgeskgeorgesk"""Tools for manipulation of expressions using paths. """ from __future__ import print_function, division from sympy.core.compatibility import xrange from sympy.core import Basic 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 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 = range(*span.indices(len(args))) else: indices = [span] else: indices = xrange(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) 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 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) sympy-0.7.4.1/sympy/simplify/cse_main.py0000644000175000017500000003512012253362407020412 0ustar georgeskgeorgesk""" Tools for doing common subexpression elimination. """ from __future__ import print_function, division import difflib from sympy.core import Basic, Mul, Add, Pow, sympify, Tuple from sympy.core.singleton import S from sympy.core.basic import preorder_traversal from sympy.core.function import _coeff_isneg from sympy.core.exprtools import factor_terms from sympy.core.compatibility import iterable, xrange 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. basic_optimizations = [(cse_opts.sub_pre, cse_opts.sub_post), (factor_terms, None)] # 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((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 opt_cse(exprs, order='canonical'): """Find optimization opportunities in Adds, Muls, Pows and negative coefficient Muls Parameters ---------- exprs : list of sympy expressions The expressions to optimize. order : string, 'none' or 'canonical' The order by which Mul and Add arguments are processed. For large expressions where speed is a concern, use the setting order='none'. Returns ------- opt_subs : dictionary of expression substitutions The expression substitutions which can be useful to optimize CSE. Examples -------- >>> from sympy.simplify.cse_main import opt_cse >>> from sympy.abc import x >>> opt_subs = opt_cse([x**-2]) >>> print(opt_subs) {x**(-2): 1/(x**2)} """ from sympy.matrices import Matrix opt_subs = dict() adds = set() muls = set() seen_subexp = set() def _find_opts(expr): if expr.is_Atom: return if iterable(expr): list(map(_find_opts, expr)) return if expr in seen_subexp: return expr seen_subexp.add(expr) list(map(_find_opts, expr.args)) if _coeff_isneg(expr): neg_expr = -expr if not neg_expr.is_Atom: opt_subs[expr] = Mul(S.NegativeOne, neg_expr, evaluate=False) seen_subexp.add(neg_expr) expr = neg_expr if expr.is_Mul: muls.add(expr) elif expr.is_Add: adds.add(expr) elif expr.is_Pow: if _coeff_isneg(expr.exp): opt_subs[expr] = Pow(Pow(expr.base, -expr.exp), S.NegativeOne, evaluate=False) for e in exprs: if isinstance(e, Basic): _find_opts(e) ## Process Adds and commutative Muls def _match_common_args(Func, funcs): if order != 'none': funcs = list(ordered(funcs)) else: funcs = sorted(funcs, key=lambda x: len(x.args)) func_args = [set(e.args) for e in funcs] for i in xrange(len(func_args)): for j in xrange(i + 1, len(func_args)): com_args = func_args[i].intersection(func_args[j]) if len(com_args) > 1: com_func = Func(*com_args) # for all sets, replace the common symbols by the function # over them, to allow recursive matches diff_i = func_args[i].difference(com_args) func_args[i] = diff_i | set([com_func]) if diff_i: opt_subs[funcs[i]] = Func(Func(*diff_i), com_func, evaluate=False) diff_j = func_args[j].difference(com_args) func_args[j] = diff_j | set([com_func]) opt_subs[funcs[j]] = Func(Func(*diff_j), com_func, evaluate=False) for k in xrange(j + 1, len(func_args)): if not com_args.difference(func_args[k]): diff_k = func_args[k].difference(com_args) func_args[k] = diff_k | set([com_func]) opt_subs[funcs[k]] = Func(Func(*diff_k), com_func, evaluate=False) # split muls into commutative comutative_muls = set() for m in muls: c, nc = m.args_cnc(cset=True) if c: c_mul = Mul(*c) if nc: opt_subs[m] = Mul(c_mul, Mul(*nc), evaluate=False) if len(c) > 1: comutative_muls.add(c_mul) _match_common_args(Add, adds) _match_common_args(Mul, comutative_muls) return opt_subs def tree_cse(exprs, symbols, opt_subs=None, order='canonical'): """Perform raw CSE on expression tree, taking opt_subs into account. Parameters ========== exprs : list of sympy expressions The expressions to reduce. symbols : infinite iterator yielding unique Symbols The symbols used to label the common subexpressions which are pulled out. opt_subs : dictionary of expression substitutions The expressions to be substituted before any CSE action is performed. order : string, 'none' or 'canonical' The order by which Mul and Add arguments are processed. For large expressions where speed is a concern, use the setting order='none'. """ from sympy.matrices import Matrix if opt_subs is None: opt_subs = dict() ## Find repeated sub-expressions to_eliminate = set() seen_subexp = set() def _find_repeated(expr): if expr.is_Atom: return if iterable(expr): args = expr else: if expr in seen_subexp: to_eliminate.add(expr) return seen_subexp.add(expr) if expr in opt_subs: expr = opt_subs[expr] args = expr.args list(map(_find_repeated, args)) for e in exprs: if isinstance(e, Basic): _find_repeated(e) ## Rebuild tree replacements = [] subs = dict() def _rebuild(expr): if expr.is_Atom: return expr if iterable(expr): new_args = [_rebuild(arg) for arg in expr] return expr.func(*new_args) if expr in subs: return subs[expr] orig_expr = expr if expr in opt_subs: expr = opt_subs[expr] # If enabled, parse Muls and Adds arguments by order to ensure # replacement order independent from hashes if order != 'none': if expr.is_Mul: c, nc = expr.args_cnc() args = list(ordered(c)) + nc elif expr.is_Add: args = list(ordered(expr.args)) else: args = expr.args else: args = expr.args new_args = list(map(_rebuild, args)) if new_args != args: new_expr = expr.func(*new_args) else: new_expr = expr if orig_expr in to_eliminate: sym = next(symbols) subs[orig_expr] = sym replacements.append((sym, new_expr)) return sym else: return new_expr reduced_exprs = [] for e in exprs: if isinstance(e, Basic): reduced_e = _rebuild(e) else: reduced_e = e reduced_exprs.append(reduced_e) return replacements, reduced_exprs def cse(exprs, symbols=None, optimizations=None, postprocess=None, order='canonical'): """ 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 The (preprocessor, postprocessor) pairs of external optimization functions. Optionally 'basic' can be passed for a set of predefined basic optimizations. Such 'basic' optimizations were used by default in old implementation, however they can be really slow on larger expressions. Now, no pre or post optimizations are made by default. 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 order : string, 'none' or 'canonical' The order by which Mul and Add arguments are processed. If set to 'canonical', arguments will be canonically ordered. If set to 'none', ordering will be faster but dependent on expressions hashes, thus machine dependent and variable. For large expressions where speed is a concern, use the setting order='none'. 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) if optimizations is None: optimizations = list() elif optimizations == 'basic': optimizations = basic_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 other optimization opportunities. opt_subs = opt_cse(reduced_exprs, order) # Main CSE algorithm. replacements, reduced_exprs = tree_cse(reduced_exprs, symbols, opt_subs, order) # 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] 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) sympy-0.7.4.1/sympy/simplify/cse_opts.py0000644000175000017500000000260112253362407020451 0ustar georgeskgeorgesk""" Optimizations of the expression tree representation for better CSE opportunities. """ from __future__ import print_function, division from sympy.core import Add, Basic, Expr, Mul from sympy.core.basic import preorder_traversal from sympy.core.singleton import S from sympy.utilities.iterables import default_sort_key def sub_pre(e): """ Replace y - x with -(x - y) if -1 can be extracted from y - x. """ reps = [a for a in e.atoms(Add) if a.could_extract_minus_sign()] # make it canonical reps.sort(key=default_sort_key) e = e.subs([(a, Mul._from_args([S.NegativeOne, -a])) for a in reps]) # repeat again for persisting Adds but mark these with a leading 1, -1 # e.g. y - x -> 1*-1*(x - y) if isinstance(e, Basic): negs = {} for a in sorted(e.atoms(Add), key=default_sort_key): if a in reps or a.could_extract_minus_sign(): negs[a] = Mul._from_args([S.One, S.NegativeOne, -a]) e = e.xreplace(negs) return e def sub_post(e): """ Replace 1*-1*x with -x. """ replacements = [] for node in preorder_traversal(e): if isinstance(node, Mul) and \ node.args[0] is S.One and node.args[1] is S.NegativeOne: replacements.append((node, -Mul._from_args(node.args[2:]))) for node, replacement in replacements: e = e.xreplace({node: replacement}) return e sympy-0.7.4.1/sympy/simplify/traversaltools.py0000644000175000017500000000165612253362407021727 0ustar georgeskgeorgesk"""Tools for applying functions to specified parts of expressions. """ from __future__ import print_function, division from sympy.core import sympify 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) sympy-0.7.4.1/sympy/simplify/fu.py0000644000175000017500000017356312253362407017264 0ustar georgeskgeorgesk""" Implementation of the trigsimp algorithm by Fu et al. The idea behind the ``fu`` algorithm is to use a sequence of rules, applied in what is heuristically known to be a smart order, to select a simpler expression that is equivalent to the input. There are transform rules in which a single rule is applied to the expression tree. The following are just mnemonic in nature; see the docstrings for examples. TR0 - simplify expression TR1 - sec-csc to cos-sin TR2 - tan-cot to sin-cos ratio TR2i - sin-cos ratio to tan TR3 - angle canonicalization TR4 - functions at special angles TR5 - powers of sin to powers of cos TR6 - powers of cos to powers of sin TR7 - reduce cos power (increase angle) TR8 - expand products of sin-cos to sums TR9 - contract sums of sin-cos to products TR10 - separate sin-cos arguments TR10i - collect sin-cos arguments TR11 - reduce double angles TR12 - separate tan arguments TR12i - collect tan arguments TR13 - expand product of tan-cot TRmorrie - prod(cos(x*2**i), (i, 0, k - 1)) -> sin(2**k*x)/(2**k*sin(x)) TR14 - factored powers of sin or cos to cos or sin power TR15 - negative powers of sin to cot power TR16 - negative powers of cos to tan power TR22 - tan-cot powers to negative powers of sec-csc functions TR111 - negative sin-cos-tan powers to csc-sec-cot There are 4 combination transforms (CTR1 - CTR4) in which a sequence of transformations are applied and the simplest expression is selected from a few options. Finally, there are the 2 rule lists (RL1 and RL2), which apply a sequence of transformations and combined transformations, and the ``fu`` algorithm itself, which applies rules and rule lists and selects the best expressions. There is also a function ``L`` which counts the number of trigonometric funcions that appear in the expression. Other than TR0, re-writing of expressions is not done by the transformations. e.g. TR10i finds pairs of terms in a sum that are in the form like ``cos(x)*cos(y) + sin(x)*sin(y)``. Such expression are targeted in a bottom-up traversal of the expression, but no manipulation to make them appear is attempted. For example, Set-up for examples below: >>> from sympy.simplify.fu import fu, L, TR9, TR10i, TR11 >>> from sympy import factor, sin, cos, powsimp >>> from sympy.abc import x, y, z, a >>> from time import time >>> eq = cos(x + y)/cos(x) >>> TR10i(eq.expand(trig=True)) -sin(x)*sin(y)/cos(x) + cos(y) If the expression is put in "normal" form (with a common denominator) then the transformation is successful: >>> TR10i(_.normal()) cos(x + y)/cos(x) TR11's behavior is similar. It rewrites double angles as smaller angles but doesn't do any simplification of the result. >>> TR11(sin(2)**a*cos(1)**(-a), 1) (2*sin(1)*cos(1))**a*cos(1)**(-a) >>> powsimp(_) (2*sin(1))**a The temptation is to try make these TR rules "smarter" but that should really be done at a higher level; the TR rules should try maintain the "do one thing well" principle. There is one exception, however. In TR10i and TR9 terms are recognized even when they are each multiplied by a common factor: >>> fu(a*cos(x)*cos(y) + a*sin(x)*sin(y)) a*cos(x - y) Factoring with ``factor_terms`` is used but it it "JIT"-like, being delayed until it is deemed necessary. Furthermore, if the factoring does not help with the simplification, it is not retained, so ``a*cos(x)*cos(y) + a*sin(x)*sin(z)`` does not become the factored (but unsimplified in the trigonometric sense) expression: >>> fu(a*cos(x)*cos(y) + a*sin(x)*sin(z)) a*sin(x)*sin(z) + a*cos(x)*cos(y) In some cases factoring might be a good idea, but the user is left to make that decision. For example: >>> expr=((15*sin(2*x) + 19*sin(x + y) + 17*sin(x + z) + 19*cos(x - z) + ... 25)*(20*sin(2*x) + 15*sin(x + y) + sin(y + z) + 14*cos(x - z) + ... 14*cos(y - z))*(9*sin(2*y) + 12*sin(y + z) + 10*cos(x - y) + 2*cos(y - ... z) + 18)).expand(trig=True).expand() In the expanded state, there are nearly 1000 trig functions: >>> L(expr) 932 If the expression where factored first, this would take time but the resulting expression would be transformed very quickly: >>> def clock(f, n=2): ... t=time(); f(); return round(time()-t, n) ... >>> clock(lambda: factor(expr)) # doctest: +SKIP 0.86 >>> clock(lambda: TR10i(expr), 3) # doctest: +SKIP 0.016 If the unexpanded expression is used, the transformation takes longer but not as long as it took to factor it and then transform it: >>> clock(lambda: TR10i(expr), 2) # doctest: +SKIP 0.28 So neither expansion nor factoring is used in ``TR10i``: if the expression is already factored (or partially factored) then expansion with ``trig=True`` would destroy what is already known and take longer; if the expression is expanded, factoring may take longer than simply applying the transformation itself. Although the algorithms should be canonical, always giving the same result, they may not yield the best result. This, in general, is the nature of simplification where searching all possible transformation paths is very expensive. Here is a simple example. There are 6 terms in the following sum: >>> expr = (sin(x)**2*cos(y)*cos(z) + sin(x)*sin(y)*cos(x)*cos(z) + ... sin(x)*sin(z)*cos(x)*cos(y) + sin(y)*sin(z)*cos(x)**2 + sin(y)*sin(z) + ... cos(y)*cos(z)) >>> args = expr.args Serendipitously, fu gives the best result: >>> fu(expr) 3*cos(y - z)/2 - cos(2*x + y + z)/2 But if different terms were combined, a less-optimal result might be obtained, requiring some additional work to get better simplification, but still less than optimal. The following shows an alternative form of ``expr`` that resists optimal simplification once a given step is taken since it leads to a dead end: >>> TR9(-cos(x)**2*cos(y + z) + 3*cos(y - z)/2 + ... cos(y + z)/2 + cos(-2*x + y + z)/4 - cos(2*x + y + z)/4) sin(2*x)*sin(y + z)/2 - cos(x)**2*cos(y + z) + 3*cos(y - z)/2 + cos(y + z)/2 Here is a smaller expression that exhibits the same behavior: >>> a = sin(x)*sin(z)*cos(x)*cos(y) + sin(x)*sin(y)*cos(x)*cos(z) >>> TR10i(a) sin(x)*sin(y + z)*cos(x) >>> newa = _ >>> TR10i(expr - a) # this combines two more of the remaining terms sin(x)**2*cos(y)*cos(z) + sin(y)*sin(z)*cos(x)**2 + cos(y - z) >>> TR10i(_ + newa) == _ + newa # but now there is no more simplification True Without getting lucky or trying all possible pairings of arguments, the final result may be less than optimal and impossible to find without better heuristics or brute force trial of all possibilities. Notes ===== This work was started by Dimitar Vlahovski at the Technological School "Electronic systems" (30.11.2011). References ========== http://rfdz.ph-noe.ac.at/fileadmin/Mathematik_Uploads/ACDCA/ DESTIME2006/DES_contribs/Fu/simplification.pdf http://www.sosmath.com/trig/Trig5/trig5/pdf/pdf.html gives a formula sheet. """ from __future__ import print_function, division from collections import defaultdict from itertools import combinations from sympy.simplify.simplify import (simplify, powsimp, ratsimp, combsimp, _mexpand, bottom_up) from sympy.core.sympify import sympify from sympy.functions.elementary.trigonometric import ( cos, sin, tan, cot, sec, csc, sqrt) from sympy.functions.elementary.hyperbolic import cosh, sinh, tanh, coth from sympy.core.compatibility import ordered from sympy.core.core import C from sympy.core.mul import Mul from sympy.core.power import Pow from sympy.core.function import expand_mul, count_ops from sympy.core.add import Add from sympy.core.symbol import Dummy from sympy.core.exprtools import Factors, gcd_terms from sympy.core.rules import Transform from sympy.core.basic import S from sympy.core.numbers import Integer, pi, I from sympy.strategies.tree import greedy from sympy.strategies.core import identity, debug from sympy.polys.polytools import factor from sympy.ntheory.factor_ import perfect_power from sympy import SYMPY_DEBUG # ================== Fu-like tools =========================== def TR0(rv): """Simplification of rational polynomials, trying to simplify the expression, e.g. combine things like 3*x + 2*x, etc.... """ # although it would be nice to use cancel, it doesn't work # with noncommutatives return rv.normal().factor().expand() def TR1(rv): """Replace sec, csc with 1/cos, 1/sin Examples ======== >>> from sympy.simplify.fu import TR1, sec, csc >>> from sympy.abc import x >>> TR1(2*csc(x) + sec(x)) 1/cos(x) + 2/sin(x) """ def f(rv): if rv.func is sec: a = rv.args[0] return S.One/cos(a) elif rv.func is csc: a = rv.args[0] return S.One/sin(a) return rv return bottom_up(rv, f) def TR2(rv): """Replace tan and cot with sin/cos and cos/sin Examples ======== >>> from sympy.simplify.fu import TR2 >>> from sympy.abc import x >>> from sympy import tan, cot, sin, cos >>> TR2(tan(x)) sin(x)/cos(x) >>> TR2(cot(x)) cos(x)/sin(x) >>> TR2(tan(tan(x) - sin(x)/cos(x))) 0 """ def f(rv): if rv.func is tan: a = rv.args[0] return sin(a)/cos(a) elif rv.func is cot: a = rv.args[0] return cos(a)/sin(a) return rv return bottom_up(rv, f) def TR2i(rv, half=False): """Converts ratios involving sin and cos as follows:: sin(x)/cos(x) -> tan(x) sin(x)/(cos(x) + 1) -> tan(x/2) if half=True Examples ======== >>> from sympy.simplify.fu import TR2i >>> from sympy.abc import x, a >>> from sympy import sin, cos >>> TR2i(sin(x)/cos(x)) tan(x) Powers of the numerator and denominator are also recognized >>> TR2i(sin(x)**2/(cos(x) + 1)**2, half=True) tan(x/2)**2 The transformation does not take place unless assumptions allow (i.e. the base must be positive or the exponent must be an integer for both numerator and denominator) >>> TR2i(sin(x)**a/(cos(x) + 1)**a) (cos(x) + 1)**(-a)*sin(x)**a """ def f(rv): if not rv.is_Mul: return rv n, d = rv.as_numer_denom() if n.is_Atom or d.is_Atom: return rv def ok(k, e): # initial filtering of factors return ( (e.is_integer or k.is_positive) and ( k.func in (sin, cos) or (half and k.is_Add and len(k.args) >= 2 and any(any(ai.func is cos or ai.is_Pow and ai.base is cos for ai in Mul.make_args(a)) for a in k.args)))) n = n.as_powers_dict() ndone = [(k, n.pop(k)) for k in list(n.keys()) if not ok(k, n[k])] if not n: return rv d = d.as_powers_dict() ddone = [(k, d.pop(k)) for k in list(d.keys()) if not ok(k, d[k])] if not d: return rv # factoring if necessary def factorize(d, ddone): newk = [] for k in d: if k.is_Add and len(k.args) > 2: knew = factor(k) if half else factor_terms(k) if knew != k: newk.append((k, knew)) if newk: for i, (k, knew) in enumerate(newk): del d[k] newk[i] = knew newk = Mul(*newk).as_powers_dict() for k in newk: if ok(k, d[k]): d[k] += newk[k] else: ddone.append((k, d[k])) del newk factorize(n, ndone) factorize(d, ddone) # joining t = [] for k in n: if k.func is sin: a = cos(k.args[0], evaluate=False) if a in d and d[a] == n[k]: t.append(tan(k.args[0])**n[k]) n[k] = d[a] = None elif half: a1 = 1 + a if a1 in d and d[a1] == n[k]: t.append((tan(k.args[0]/2))**n[k]) n[k] = d[a1] = None elif k.func is cos: a = sin(k.args[0], evaluate=False) if a in d and d[a] == n[k]: t.append(tan(k.args[0])**-n[k]) n[k] = d[a] = None elif half and k.is_Add and k.args[0] is S.One and \ k.args[1].func is cos: a = sin(k.args[1].args[0], evaluate=False) if a in d and d[a] == n[k] and (d[a].is_integer or \ a.is_positive): t.append(tan(a.args[0]/2)**-n[k]) n[k] = d[a] = None if t: rv = Mul(*(t + [b**e for b, e in n.items() if e]))/\ Mul(*[b**e for b, e in d.items() if e]) rv *= Mul(*[b**e for b, e in ndone])/Mul(*[b**e for b, e in ddone]) return rv return bottom_up(rv, f) def TR3(rv): """Induced formula: example sin(-a) = -sin(a) Examples ======== >>> from sympy.simplify.fu import TR3 >>> from sympy.abc import x, y >>> from sympy import pi >>> from sympy import cos >>> TR3(cos(y - x*(y - x))) cos(x*(x - y) + y) >>> cos(pi/2 + x) -sin(x) >>> cos(30*pi/2 + x) -cos(x) """ from sympy.simplify.simplify import signsimp # Negative argument (already automatic for funcs like sin(-x) -> -sin(x) # but more complicated expressions can use it, too). Also, trig angles # between pi/4 and pi/2 are not reduced to an angle between 0 and pi/4. # The following are automatically handled: # Argument of type: pi/2 +/- angle # Argument of type: pi +/- angle # Argument of type : 2k*pi +/- angle def f(rv): if not isinstance(rv, C.TrigonometricFunction): return rv rv = rv.func(signsimp(rv.args[0])) if (rv.args[0] - S.Pi/4).is_positive is (S.Pi/2 - rv.args[0]).is_positive is True: fmap = {cos: sin, sin: cos, tan: cot, cot: tan, sec: csc, csc: sec} rv = fmap[rv.func](S.Pi/2 - rv.args[0]) return rv return bottom_up(rv, f) def TR4(rv): """Identify values of special angles. a= 0 pi/6 pi/4 pi/3 pi/2 ---------------------------------------------------- cos(a) 0 1/2 sqrt(2)/2 sqrt(3)/2 1 sin(a) 1 sqrt(3)/2 sqrt(2)/2 1/2 0 tan(a) 0 sqt(3)/3 1 sqrt(3) -- Examples ======== >>> from sympy.simplify.fu import TR4 >>> from sympy import pi >>> from sympy import cos, sin, tan, cot >>> for s in (0, pi/6, pi/4, pi/3, pi/2): ... print('%s %s %s %s' % (cos(s), sin(s), tan(s), cot(s))) ... 1 0 0 zoo sqrt(3)/2 1/2 sqrt(3)/3 sqrt(3) sqrt(2)/2 sqrt(2)/2 1 1 1/2 sqrt(3)/2 sqrt(3) sqrt(3)/3 0 1 zoo 0 """ # special values at 0, pi/6, pi/4, pi/3, pi/2 already handled return rv def _TR56(rv, f, g, h, max, pow): """Helper for TR5 and TR6 to replace f**2 with h(g**2) Options ======= max : controls size of exponent that can appear on f e.g. if max=4 then f**4 will be changed to h(g**2)**2. pow : controls whether the exponent must be a perfect power of 2 e.g. if pow=True (and max >= 6) then f**6 will not be changed but f**8 will be changed to h(g**2)**4 >>> from sympy.simplify.fu import _TR56 as T >>> from sympy.abc import x >>> from sympy import sin, cos >>> h = lambda x: 1 - x >>> T(sin(x)**3, sin, cos, h, 4, False) sin(x)**3 >>> T(sin(x)**6, sin, cos, h, 6, False) (-cos(x)**2 + 1)**3 >>> T(sin(x)**6, sin, cos, h, 6, True) sin(x)**6 >>> T(sin(x)**8, sin, cos, h, 10, True) (-cos(x)**2 + 1)**4 """ def _f(rv): # I'm not sure if this transformation should target all even powers # or only those expressible as powers of 2. Also, should it only # make the changes in powers that appear in sums -- making an isolated # change is not going to allow a simplification as far as I can tell. if not (rv.is_Pow and rv.base.func == f): return rv if (rv.exp < 0) is True: return rv if (rv.exp > max) is True: return rv if rv.exp == 2: return h(g(rv.base.args[0])**2) else: if rv.exp == 4: e = 2 elif not pow: if rv.exp % 2: return rv e = rv.exp//2 else: p = perfect_power(rv.exp) if not p: return rv e = rv.exp//2 return h(g(rv.base.args[0])**2)**e return bottom_up(rv, _f) def TR5(rv, max=4, pow=False): """Replacement of sin**2 with 1 - cos(x)**2. See _TR56 docstring for advanced use of ``max`` and ``pow``. Examples ======== >>> from sympy.simplify.fu import TR5 >>> from sympy.abc import x >>> from sympy import sin >>> TR5(sin(x)**2) -cos(x)**2 + 1 >>> TR5(sin(x)**-2) # unchanged sin(x)**(-2) >>> TR5(sin(x)**4) (-cos(x)**2 + 1)**2 """ return _TR56(rv, sin, cos, lambda x: 1 - x, max=max, pow=pow) def TR6(rv, max=4, pow=False): """Replacement of cos**2 with 1 - sin(x)**2. See _TR56 docstring for advanced use of ``max`` and ``pow``. Examples ======== >>> from sympy.simplify.fu import TR6 >>> from sympy.abc import x >>> from sympy import cos >>> TR6(cos(x)**2) -sin(x)**2 + 1 >>> TR6(cos(x)**-2) #unchanged cos(x)**(-2) >>> TR6(cos(x)**4) (-sin(x)**2 + 1)**2 """ return _TR56(rv, cos, sin, lambda x: 1 - x, max=max, pow=pow) def TR7(rv): """Lowering the degree of cos(x)**2 Examples ======== >>> from sympy.simplify.fu import TR7 >>> from sympy.abc import x >>> from sympy import cos >>> TR7(cos(x)**2) cos(2*x)/2 + 1/2 >>> TR7(cos(x)**2 + 1) cos(2*x)/2 + 3/2 """ def f(rv): if not (rv.is_Pow and rv.base.func == cos and rv.exp == 2): return rv return (1 + cos(2*rv.base.args[0]))/2 return bottom_up(rv, f) def TR8(rv, first=True): """Converting products of ``cos`` and/or ``sin`` to a sum or difference of ``cos`` and or ``sin`` terms. Examples ======== >>> from sympy.simplify.fu import TR8, TR7 >>> from sympy import cos, sin >>> TR8(cos(2)*cos(3)) cos(5)/2 + cos(1)/2 >>> TR8(cos(2)*sin(3)) sin(5)/2 + sin(1)/2 >>> TR8(sin(2)*sin(3)) -cos(5)/2 + cos(1)/2 """ def f(rv): if not ( rv.is_Mul or rv.is_Pow and rv.base.func in (cos, sin) and (rv.exp.is_integer or rv.base.is_positive)): return rv if first: n, d = [expand_mul(i) for i in rv.as_numer_denom()] newn = TR8(n, first=False) newd = TR8(d, first=False) if newn != n or newd != d: rv = gcd_terms(newn/newd) if rv.is_Mul and rv.args[0].is_Rational and \ len(rv.args) == 2 and rv.args[1].is_Add: rv = Mul(*rv.as_coeff_Mul()) return rv args = {cos: [], sin: [], None: []} for a in ordered(Mul.make_args(rv)): if a.func in (cos, sin): args[a.func].append(a.args[0]) elif (a.is_Pow and a.exp.is_Integer and a.exp > 0 and \ a.base.func in (cos, sin)): # XXX this is ok but pathological expression could be handled # more efficiently as in TRmorrie args[a.base.func].extend([a.base.args[0]]*a.exp) else: args[None].append(a) c = args[cos] s = args[sin] if not (c and s or len(c) > 1 or len(s) > 1): return rv args = args[None] n = min(len(c), len(s)) for i in range(n): a1 = s.pop() a2 = c.pop() args.append((sin(a1 + a2) + sin(a1 - a2))/2) while len(c) > 1: a1 = c.pop() a2 = c.pop() args.append((cos(a1 + a2) + cos(a1 - a2))/2) if c: args.append(cos(c.pop())) while len(s) > 1: a1 = s.pop() a2 = s.pop() args.append((-cos(a1 + a2) + cos(a1 - a2))/2) if s: args.append(sin(s.pop())) return TR8(expand_mul(Mul(*args))) return bottom_up(rv, f) def TR9(rv): """Sum of ``cos`` or ``sin`` terms as a product of ``cos`` or ``sin``. Examples ======== >>> from sympy.simplify.fu import TR9 >>> from sympy import cos, sin >>> TR9(cos(1) + cos(2)) 2*cos(1/2)*cos(3/2) >>> TR9(cos(1) + 2*sin(1) + 2*sin(2)) cos(1) + 4*sin(3/2)*cos(1/2) If no change is made by TR9, no re-arrangement of the expression will be made. For example, though factoring of common term is attempted, if the factored expression wasn't changed, the original expression will be returned: >>> TR9(cos(3) + cos(3)*cos(2)) cos(3) + cos(2)*cos(3) """ def f(rv): if not rv.is_Add: return rv def do(rv, first=True): # cos(a)+/-cos(b) can be combined into a product of cosines and # sin(a)+/-sin(b) can be combined into a product of cosine and # sine. # # If there are more than two args, the pairs which "work" will # have a gcd extractable and the remaining two terms will have # the above structure -- all pairs must be checked to find the # ones that work. args that don't have a common set of symbols # are skipped since this doesn't lead to a simpler formula and # also has the arbitrariness of combining, for example, the x # and y term instead of the y and z term in something like # cos(x) + cos(y) + cos(z). if not rv.is_Add: return rv args = list(ordered(rv.args)) if len(args) != 2: hit = False for i in range(len(args)): ai = args[i] if ai is None: continue for j in range(i + 1, len(args)): aj = args[j] if aj is None: continue was = ai + aj new = do(was) if new != was: args[i] = new # update in place args[j] = None hit = True break # go to next i if hit: rv = Add(*[_f for _f in args if _f]) if rv.is_Add: rv = do(rv) return rv # two-arg Add split = trig_split(*args) if not split: return rv gcd, n1, n2, a, b, iscos = split # application of rule if possible if iscos: if n1 == n2: return gcd*n1*2*cos((a + b)/2)*cos((a - b)/2) if n1 < 0: a, b = b, a return -2*gcd*sin((a + b)/2)*sin((a - b)/2) else: if n1 == n2: return gcd*n1*2*sin((a + b)/2)*cos((a - b)/2) if n1 < 0: a, b = b, a return 2*gcd*cos((a + b)/2)*sin((a - b)/2) return process_common_addends(rv, do) # DON'T sift by free symbols return bottom_up(rv, f) def TR10(rv, first=True): """Separate sums in ``cos`` and ``sin``. Examples ======== >>> from sympy.simplify.fu import TR10 >>> from sympy.abc import a, b, c >>> from sympy import cos, sin >>> TR10(cos(a + b)) -sin(a)*sin(b) + cos(a)*cos(b) >>> TR10(sin(a + b)) sin(a)*cos(b) + sin(b)*cos(a) >>> TR10(sin(a + b + c)) (-sin(a)*sin(b) + cos(a)*cos(b))*sin(c) + \ (sin(a)*cos(b) + sin(b)*cos(a))*cos(c) """ def f(rv): if not rv.func in (cos, sin): return rv f = rv.func arg = rv.args[0] if arg.is_Add: if first: args = list(ordered(arg.args)) else: args = list(arg.args) a = args.pop() b = Add._from_args(args) if b.is_Add: if f == sin: return sin(a)*TR10(cos(b), first=False) + \ cos(a)*TR10(sin(b), first=False) else: return cos(a)*TR10(cos(b), first=False) - \ sin(a)*TR10(sin(b), first=False) else: if f == sin: return sin(a)*cos(b) + cos(a)*sin(b) else: return cos(a)*cos(b) - sin(a)*sin(b) return rv return bottom_up(rv, f) def TR10i(rv): """Sum of products to function of sum. Examples ======== >>> from sympy.simplify.fu import TR10i >>> from sympy import cos, sin, pi, Add, Mul, sqrt, Symbol >>> from sympy.abc import x, y >>> TR10i(cos(1)*cos(3) + sin(1)*sin(3)) cos(2) >>> TR10i(cos(1)*sin(3) + sin(1)*cos(3) + cos(3)) cos(3) + sin(4) >>> TR10i(sqrt(2)*cos(x)*x + sqrt(6)*sin(x)*x) 2*sqrt(2)*x*sin(x + pi/6) """ global _ROOT2, _ROOT3, _invROOT3 if _ROOT2 is None: _roots() def f(rv): if not rv.is_Add: return rv def do(rv, first=True): # args which can be expressed as A*(cos(a)*cos(b)+/-sin(a)*sin(b)) # or B*(cos(a)*sin(b)+/-cos(b)*sin(a)) can be combined into # A*f(a+/-b) where f is either sin or cos. # # If there are more than two args, the pairs which "work" will have # a gcd extractable and the remaining two terms will have the above # structure -- all pairs must be checked to find the ones that # work. if not rv.is_Add: return rv args = list(ordered(rv.args)) if len(args) != 2: hit = False for i in range(len(args)): ai = args[i] if ai is None: continue for j in range(i + 1, len(args)): aj = args[j] if aj is None: continue was = ai + aj new = do(was) if new != was: args[i] = new # update in place args[j] = None hit = True break # go to next i if hit: rv = Add(*[_f for _f in args if _f]) if rv.is_Add: rv = do(rv) return rv # two-arg Add split = trig_split(*args, two=True) if not split: return rv gcd, n1, n2, a, b, same = split # identify and get c1 to be cos then apply rule if possible if same: # coscos, sinsin gcd = n1*gcd if n1 == n2: return gcd*cos(a - b) return gcd*cos(a + b) else: #cossin, cossin gcd = n1*gcd if n1 == n2: return gcd*sin(a + b) return gcd*sin(b - a) rv = process_common_addends( rv, do, lambda x: tuple(ordered(x.free_symbols))) # need to check for inducible pairs in ratio of sqrt(3):1 that # appeared in different lists when sorting by coefficient while rv.is_Add: byrad = defaultdict(list) for a in rv.args: hit = 0 if a.is_Mul: for ai in a.args: if ai.is_Pow and ai.exp is S.Half and \ ai.base.is_Integer: byrad[ai].append(a) hit = 1 break if not hit: byrad[S.One].append(a) # no need to check all pairs -- just check for the onees # that have the right ratio args = [] for a in byrad: for b in [_ROOT3*a, _invROOT3]: if b in byrad: for i in range(len(byrad[a])): if byrad[a][i] is None: continue for j in range(len(byrad[b])): if byrad[b][j] is None: continue was = Add(byrad[a][i] + byrad[b][j]) new = do(was) if new != was: args.append(new) byrad[a][i] = None byrad[b][j] = None break if args: rv = Add(*(args + [Add(*[_f for _f in v if _f]) for v in byrad.values()])) else: rv = do(rv) # final pass to resolve any new inducible pairs break return rv return bottom_up(rv, f) def TR11(rv, base=None): """Function of double angle to product. The ``base`` argument can be used to indicate what is the un-doubled argument, e.g. if 3*pi/7 is the base then cosine and sine functions with argument 6*pi/7 will be replaced. Examples ======== >>> from sympy.simplify.fu import TR11 >>> from sympy import cos, sin, pi >>> from sympy.abc import x >>> TR11(sin(2*x)) 2*sin(x)*cos(x) >>> TR11(cos(2*x)) -sin(x)**2 + cos(x)**2 >>> TR11(sin(4*x)) 4*(-sin(x)**2 + cos(x)**2)*sin(x)*cos(x) >>> TR11(sin(4*x/3)) 4*(-sin(x/3)**2 + cos(x/3)**2)*sin(x/3)*cos(x/3) If the arguments are simply integers, no change is made unless a base is provided: >>> TR11(cos(2)) cos(2) >>> TR11(cos(4), 2) -sin(2)**2 + cos(2)**2 There is a subtle issue here in that autosimplification will convert some higher angles to lower angles >>> cos(6*pi/7) + cos(3*pi/7) -cos(pi/7) + cos(3*pi/7) The 6*pi/7 angle is now pi/7 but can be targeted with TR11 by supplying the 3*pi/7 base: >>> TR11(_, 3*pi/7) -sin(3*pi/7)**2 + cos(3*pi/7)**2 + cos(3*pi/7) """ def f(rv): if not rv.func in (cos, sin): return rv if base: f = rv.func t = f(base*2) co = S.One if t.is_Mul: co, t = t.as_coeff_Mul() if not t.func in (cos, sin): return rv if rv.args[0] == t.args[0]: c = cos(base) s = sin(base) if f is cos: return (c**2 - s**2)/co else: return 2*c*s/co return rv elif not rv.args[0].is_Number: # make a change if the leading coefficient's numerator is # divisible by 2 c, m = rv.args[0].as_coeff_Mul(rational=True) if c.p % 2 == 0: arg = c.p//2*m/c.q c = TR11(cos(arg)) s = TR11(sin(arg)) if rv.func == sin: rv = 2*s*c else: rv = c**2 - s**2 return rv return bottom_up(rv, f) def TR12(rv, first=True): """Separate sums in ``tan``. Examples ======== >>> from sympy.simplify.fu import TR12 >>> from sympy.abc import x, y >>> from sympy import tan >>> from sympy.simplify.fu import TR12 >>> TR12(tan(x + y)) (tan(x) + tan(y))/(-tan(x)*tan(y) + 1) """ def f(rv): if not rv.func == tan: return rv arg = rv.args[0] if arg.is_Add: if first: args = list(ordered(arg.args)) else: args = list(arg.args) a = args.pop() b = Add._from_args(args) if b.is_Add: tb = TR12(tan(b), first=False) else: tb = tan(b) return (tan(a) + tb)/(1 - tan(a)*tb) return rv return bottom_up(rv, f) def TR12i(rv): """Combine tan arguments as (tan(y) + tan(x))/(tan(x)*tan(y) - 1) -> -tan(x + y) Examples ======== >>> from sympy.simplify.fu import TR12i >>> from sympy import tan >>> from sympy.abc import a, b, c >>> ta, tb, tc = [tan(i) for i in (a, b, c)] >>> TR12i((ta + tb)/(-ta*tb + 1)) tan(a + b) >>> TR12i((ta + tb)/(ta*tb - 1)) -tan(a + b) >>> TR12i((-ta - tb)/(ta*tb - 1)) tan(a + b) >>> eq = (ta + tb)/(-ta*tb + 1)**2*(-3*ta - 3*tc)/(2*(ta*tc - 1)) >>> TR12i(eq.expand()) -3*tan(a + b)*tan(a + c)/(2*(tan(a) + tan(b) - 1)) """ from sympy import factor, fraction, factor_terms def f(rv): if not (rv.is_Add or rv.is_Mul or rv.is_Pow): return rv n, d = rv.as_numer_denom() if not d.args or not n.args: return rv dok = {} def ok(di): m = as_f_sign_1(di) if m: g, f, s = m if s is S.NegativeOne and f.is_Mul and len(f.args) == 2 and \ all(fi.func is tan for fi in f.args): return g, f dargs = list(Mul.make_args(d)) for i, di in enumerate(dargs): m = ok(di) if m: g, t = m s = Add(*[_.args[0] for _ in t.args]) dok[s] = S.One dargs[i] = g continue if di.is_Add: di = factor(di) if di.is_Mul: dargs.extend(di.args) dargs[i] = S.One elif di.is_Pow and (di.exp.is_integer or di.base.is_positive): m = ok(di.base) if m: g, t = m s = Add(*[_.args[0] for _ in t.args]) dok[s] = di.exp dargs[i] = g**di.exp else: di = factor(di) if di.is_Mul: dargs.extend(di.args) dargs[i] = S.One if not dok: return rv def ok(ni): if ni.is_Add and len(ni.args) == 2: a, b = ni.args if a.func is tan and b.func is tan: return a, b nargs = list(Mul.make_args(factor_terms(n))) hit = False for i, ni in enumerate(nargs): m = ok(ni) if not m: m = ok(-ni) if m: nargs[i] = S.NegativeOne else: if ni.is_Add: ni = factor(ni) if ni.is_Mul: nargs.extend(ni.args) nargs[i] = S.One continue elif ni.is_Pow and ( ni.exp.is_integer or ni.base.is_positive): m = ok(ni.base) if m: nargs[i] = S.One else: ni = factor(ni) if ni.is_Mul: nargs.extend(ni.args) nargs[i] = S.One continue else: continue else: nargs[i] = S.One hit = True s = Add(*[_.args[0] for _ in m]) ed = dok[s] newed = ed.extract_additively(S.One) if newed is not None: if newed: dok[s] = newed else: dok.pop(s) nargs[i] *= -tan(s) if hit: rv = Mul(*nargs)/Mul(*dargs)/Mul(*[(Add(*[ tan(a) for a in i.args]) - 1)**e for i, e in dok.items()]) return rv return bottom_up(rv, f) def TR13(rv): """Change products of ``tan`` or ``cot``. Examples ======== >>> from sympy.simplify.fu import TR13 >>> from sympy import tan, cot, cos >>> TR13(tan(3)*tan(2)) -tan(2)/tan(5) - tan(3)/tan(5) + 1 >>> TR13(cot(3)*cot(2)) cot(2)*cot(5) + 1 + cot(3)*cot(5) """ def f(rv): if not rv.is_Mul: return rv # XXX handle products of powers? or let power-reducing handle it? args = {tan: [], cot: [], None: []} for a in ordered(Mul.make_args(rv)): if a.func in (tan, cot): args[a.func].append(a.args[0]) else: args[None].append(a) t = args[tan] c = args[cot] if len(t) < 2 and len(c) < 2: return rv args = args[None] while len(t) > 1: t1 = t.pop() t2 = t.pop() args.append(1 - (tan(t1)/tan(t1 + t2) + tan(t2)/tan(t1 + t2))) if t: args.append(tan(t.pop())) while len(c) > 1: t1 = c.pop() t2 = c.pop() args.append(1 + cot(t1)*cot(t1 + t2) + cot(t2)*cot(t1 + t2)) if c: args.append(cot(c.pop())) return Mul(*args) return bottom_up(rv, f) def TRmorrie(rv): """Returns cos(x)*cos(2*x)*...*cos(2**(k-1)*x) -> sin(2**k*x)/(2**k*sin(x)) Examples ======== >>> from sympy.simplify.fu import TRmorrie, TR8, TR3 >>> from sympy.abc import x >>> from sympy import Mul, cos, pi >>> TRmorrie(cos(x)*cos(2*x)) sin(4*x)/(4*sin(x)) >>> TRmorrie(7*Mul(*[cos(x) for x in range(10)])) 7*sin(12)*sin(16)*cos(5)*cos(7)*cos(9)/(64*sin(1)*sin(3)) Sometimes autosimplification will cause a power to be not recognized. e.g. in the following, cos(4*pi/7) automatically simplifies to -cos(3*pi/7) so only 2 of the 3 terms are recognized: >>> TRmorrie(cos(pi/7)*cos(2*pi/7)*cos(4*pi/7)) -sin(3*pi/7)*cos(3*pi/7)/(4*sin(pi/7)) A touch by TR8 resolves the expression to a Rational >>> TR8(_) -1/8 In this case, if eq is unsimplified, the answer is obtained directly: >>> eq = cos(pi/9)*cos(2*pi/9)*cos(3*pi/9)*cos(4*pi/9) >>> TRmorrie(eq) 1/16 But if angles are made canonical with TR3 then the answer is not simplified without further work: >>> TR3(eq) sin(pi/18)*cos(pi/9)*cos(2*pi/9)/2 >>> TRmorrie(_) sin(pi/18)*sin(4*pi/9)/(8*sin(pi/9)) >>> TR8(_) cos(7*pi/18)/(16*sin(pi/9)) >>> TR3(_) 1/16 The original expression would have resolve to 1/16 directly with TR8, however: >>> TR8(eq) 1/16 References ========== http://en.wikipedia.org/wiki/Morrie%27s_law """ def f(rv): if not rv.is_Mul: return rv args = defaultdict(list) coss = {} other = [] for c in rv.args: b, e = c.as_base_exp() if e.is_Integer and b.func is cos: co, a = b.args[0].as_coeff_Mul() args[a].append(co) coss[b] = e else: other.append(c) new = [] for a in args: c = args[a] c.sort() no = [] while c: k = 0 cc = ci = c[0] while cc in c: k += 1 cc *= 2 if k > 1: newarg = sin(2**k*ci*a)/2**k/sin(ci*a) # see how many times this can be taken take = None ccs = [] for i in range(k): cc /= 2 key = cos(a*cc, evaluate=False) ccs.append(cc) take = min(coss[key], take or coss[key]) # update exponent counts for i in range(k): cc = ccs.pop() key = cos(a*cc, evaluate=False) coss[key] -= take if not coss[key]: c.remove(cc) new.append(newarg**take) else: no.append(c.pop(0)) c[:] = no if new: rv = Mul(*(new + other + [ cos(k*a, evaluate=False) for a in args for k in args[a]])) return rv return bottom_up(rv, f) def TR14(rv, first=True): """Convert factored powers of sin and cos identities into simpler expressions. Examples ======== >>> from sympy.simplify.fu import TR14 >>> from sympy.abc import x, y >>> from sympy import cos, sin >>> TR14((cos(x) - 1)*(cos(x) + 1)) -sin(x)**2 >>> TR14((sin(x) - 1)*(sin(x) + 1)) -cos(x)**2 >>> p1 = (cos(x) + 1)*(cos(x) - 1) >>> p2 = (cos(y) - 1)*2*(cos(y) + 1) >>> p3 = (3*(cos(y) - 1))*(3*(cos(y) + 1)) >>> TR14(p1*p2*p3*(x - 1)) -18*(x - 1)*sin(x)**2*sin(y)**4 """ def f(rv): if not rv.is_Mul: return rv if first: # sort them by location in numerator and denominator # so the code below can just deal with positive exponents n, d = rv.as_numer_denom() if d is not S.One: newn = TR14(n, first=False) newd = TR14(d, first=False) if newn != n or newd != d: rv = newn/newd return rv other = [] process = [] for a in rv.args: if a.is_Pow: b, e = a.as_base_exp() if not (e.is_integer or b.is_positive): other.append(a) continue a = b else: e = S.One m = as_f_sign_1(a) if not m or m[1].func not in (cos, sin): if e is S.One: other.append(a) else: other.append(a**e) continue g, f, si = m process.append((g, e.is_Number, e, f, si, a)) # sort them to get like terms next to each other process = list(ordered(process)) # keep track of whether there was any change nother = len(other) # access keys keys = (g, t, e, f, si, a) = list(range(6)) while process: A = process.pop(0) if process: B = process[0] if A[e].is_Number and B[e].is_Number: # both exponents are numbers if A[f] == B[f]: if A[si] != B[si]: B = process.pop(0) take = min(A[e], B[e]) # reinsert any remainder # the B will likely sort after A so check it first if B[e] != take: rem = [B[i] for i in keys] rem[e] -= take process.insert(0, rem) elif A[e] != take: rem = [A[i] for i in keys] rem[e] -= take process.insert(0, rem) if A[f].func is cos: t = sin else: t = cos other.append((-A[g]*B[g]*t(A[f].args[0])**2)**take) continue elif A[e] == B[e]: # both exponents are equal symbols if A[f] == B[f]: if A[si] != B[si]: B = process.pop(0) take = A[e] if A[f].func is cos: t = sin else: t = cos other.append((-A[g]*B[g]*t(A[f].args[0])**2)**take) continue # either we are done or neither condition above applied other.append(A[a]**A[e]) if len(other) != nother: rv = Mul(*other) return rv return bottom_up(rv, f) def TR15(rv, max=4, pow=False): """Convert sin(x)*-2 to 1 + cot(x)**2. See _TR56 docstring for advanced use of ``max`` and ``pow``. Examples ======== >>> from sympy.simplify.fu import TR15 >>> from sympy.abc import x >>> from sympy import cos, sin >>> TR15(1 - 1/sin(x)**2) -cot(x)**2 """ def f(rv): if not (isinstance(rv, Pow) and rv.base.func is sin): return rv ia = 1/rv a = _TR56(ia, sin, cot, lambda x: 1 + x, max=max, pow=pow) if a != ia: rv = a return rv return bottom_up(rv, f) def TR16(rv, max=4, pow=False): """Convert cos(x)*-2 to 1 + tan(x)**2. See _TR56 docstring for advanced use of ``max`` and ``pow``. Examples ======== >>> from sympy.simplify.fu import TR16 >>> from sympy.abc import x >>> from sympy import cos, sin >>> TR16(1 - 1/cos(x)**2) -tan(x)**2 """ def f(rv): if not (isinstance(rv, Pow) and rv.base.func is cos): return rv ia = 1/rv a = _TR56(ia, cos, tan, lambda x: 1 + x, max=max, pow=pow) if a != ia: rv = a return rv return bottom_up(rv, f) def TR111(rv): """Convert f(x)**-i to g(x)**i where either ``i`` is an integer or the base is positive and f, g are: tan, cot; sin, csc; or cos, sec. Examples ======== >>> from sympy.simplify.fu import TR111 >>> from sympy.abc import x >>> from sympy import tan >>> TR111(1 - 1/tan(x)**2) -cot(x)**2 + 1 """ def f(rv): if not ( isinstance(rv, Pow) and (rv.base.is_positive or rv.exp.is_integer and rv.exp.is_negative)): return rv if rv.base.func is tan: return cot(rv.base.args[0])**-rv.exp elif rv.base.func is sin: return csc(rv.base.args[0])**-rv.exp elif rv.base.func is cos: return sec(rv.base.args[0])**-rv.exp return rv return bottom_up(rv, f) def TR22(rv, max=4, pow=False): """Convert tan(x)**2 to sec(x)**2 - 1 and cot(x)**2 to csc(x)**2 - 1. See _TR56 docstring for advanced use of ``max`` and ``pow``. Examples ======== >>> from sympy.simplify.fu import TR22 >>> from sympy.abc import x >>> from sympy import tan, cot >>> TR22(1 + tan(x)**2) sec(x)**2 >>> TR22(1 + cot(x)**2) csc(x)**2 """ def f(rv): if not (isinstance(rv, Pow) and rv.base.func in (cot, tan)): return rv rv = _TR56(rv, tan, sec, lambda x: x - 1, max=max, pow=pow) rv = _TR56(rv, cot, csc, lambda x: x - 1, max=max, pow=pow) return rv return bottom_up(rv, f) def L(rv): """Return count of trigonometric functions in expression. Examples ======== >>> from sympy.simplify.fu import L >>> from sympy.abc import x >>> from sympy import cos, sin >>> L(cos(x)+sin(x)) 2 """ return S(rv.count(C.TrigonometricFunction)) # ============== end of basic Fu-like tools ===================== if SYMPY_DEBUG: (TR0, TR1, TR2, TR3, TR4, TR5, TR6, TR7, TR8, TR9, TR10, TR11, TR12, TR13, TR2i, TRmorrie, TR14, TR15, TR16, TR12i, TR111, TR22 )= list(map(debug, (TR0, TR1, TR2, TR3, TR4, TR5, TR6, TR7, TR8, TR9, TR10, TR11, TR12, TR13, TR2i, TRmorrie, TR14, TR15, TR16, TR12i, TR111, TR22))) # tuples are chains -- (f, g) -> lambda x: g(f(x)) # lists are choices -- [f, g] -> lambda x: min(f(x), g(x), key=objective) CTR1 = [(TR5, TR0), (TR6, TR0), identity] CTR2 = (TR11, [(TR5, TR0), (TR6, TR0), TR0]) CTR3 = [(TRmorrie, TR8, TR0), (TRmorrie, TR8, TR10i, TR0), identity] CTR4 = [(TR4, TR10i), identity] RL1 = (TR4, TR3, TR4, TR12, TR4, TR13, TR4, TR0) # XXX it's a little unclear how this one is to be implemented # see Fu paper of reference, page 7. What is the Union symbol refering to? # The diagram shows all these as one chain of transformations, but the # text refers to them being applied independently. Also, a break # if L starts to increase has not been implemented. RL2 = [ (TR4, TR3, TR10, TR4, TR3, TR11), (TR5, TR7, TR11, TR4), (CTR3, CTR1, TR9, CTR2, TR4, TR9, TR9, CTR4), identity, ] def fu(rv, measure=lambda x: (L(x), x.count_ops())): """Attempt to simplify expression by using transformation rules given in the algorithm by Fu et al. :func:`fu` will try to minimize the objective function ``measure``. By default this first minimizes the number of trig terms and then minimizes the number of total operations. Examples ======== >>> from sympy.simplify.fu import fu >>> from sympy import cos, sin, tan, pi, S, sqrt >>> from sympy.abc import x, y, a, b >>> fu(sin(50)**2 + cos(50)**2 + sin(pi/6)) 3/2 >>> fu(sqrt(6)*cos(x) + sqrt(2)*sin(x)) 2*sqrt(2)*sin(x + pi/3) CTR1 example >>> eq = sin(x)**4 - cos(y)**2 + sin(y)**2 + 2*cos(x)**2 >>> fu(eq) cos(x)**4 - 2*cos(y)**2 + 2 CTR2 example >>> fu(S.Half - cos(2*x)/2) sin(x)**2 CTR3 example >>> fu(sin(a)*(cos(b) - sin(b)) + cos(a)*(sin(b) + cos(b))) sqrt(2)*sin(a + b + pi/4) CTR4 example >>> fu(sqrt(3)*cos(x)/2 + sin(x)/2) sin(x + pi/3) Example 1 >>> fu(1-sin(2*x)**2/4-sin(y)**2-cos(x)**4) -cos(x)**2 + cos(y)**2 Example 2 >>> fu(cos(4*pi/9)) sin(pi/18) >>> fu(cos(pi/9)*cos(2*pi/9)*cos(3*pi/9)*cos(4*pi/9)) 1/16 Example 3 >>> fu(tan(7*pi/18)+tan(5*pi/18)-sqrt(3)*tan(5*pi/18)*tan(7*pi/18)) -sqrt(3) Objective function example >>> fu(sin(x)/cos(x)) # default objective function tan(x) >>> fu(sin(x)/cos(x), measure=lambda x: -x.count_ops()) # maximize op count sin(x)/cos(x) References ========== http://rfdz.ph-noe.ac.at/fileadmin/Mathematik_Uploads/ACDCA/ DESTIME2006/DES_contribs/Fu/simplification.pdf """ fRL1 = greedy(RL1, measure) fRL2 = greedy(RL2, measure) was = rv rv = sympify(rv) rv = TR1(rv) if rv.has(tan, cot): rv1 = fRL1(rv) if (measure(rv1) < measure(rv)): rv = rv1 if rv.has(tan, cot): rv = TR2(rv) if rv.has(sin, cos): rv1 = fRL2(rv) rv2 = TR8(TRmorrie(rv1)) rv = min([was, rv, rv1, rv2], key=measure) return min(TR2i(rv), rv, key=measure) def process_common_addends(rv, do, key2=None, key1=True): """Apply ``do`` to addends of ``rv`` that (if key1=True) share at least a common absolute value of their coefficient and the value of ``key2`` when applied to the argument. If ``key1`` is False ``key2`` must be supplied and will be the only key applied. """ # collect by absolute value of coefficient and key2 absc = defaultdict(list) if key1: for a in rv.args: c, a = a.as_coeff_Mul() if c < 0: c = -c a = -a # put the sign on `a` absc[(c, key2(a) if key2 else 1)].append(a) elif key2: for a in rv.args: absc[(S.One, key2(a))].append(a) else: raise ValueError('must have at least one key') args = [] hit = False for k in absc: v = absc[k] c, _ = k if len(v) > 1: e = Add(*v, evaluate=False) new = do(e) if new != e: e = new hit = True args.append(c*e) else: args.append(c*v[0]) if hit: rv = Add(*args) return rv fufuncs = ''' TR0 TR1 TR2 TR3 TR4 TR5 TR6 TR7 TR8 TR9 TR10 TR10i TR11 TR12 TR13 L TR2i TRmorrie TR12i TR14 TR15 TR16 TR111 TR22'''.split() FU = dict(list(zip(fufuncs, list(map(locals().get, fufuncs))))) def _roots(): global _ROOT2, _ROOT3, _invROOT3 _ROOT2, _ROOT3 = sqrt(2), sqrt(3) _invROOT3 = 1/_ROOT3 _ROOT2 = None def trig_split(a, b, two=False): """Return the gcd, s1, s2, a1, a2, bool where If two is False (default) then:: a + b = gcd*(s1*f(a1) + s2*f(a2)) where f = cos if bool else sin else: if bool, a + b was +/- cos(a1)*cos(a2) +/- sin(a1)*sin(a2) and equals n1*gcd*cos(a - b) if n1 == n2 else n1*gcd*cos(a + b) else a + b was +/- cos(a1)*sin(a2) +/- sin(a1)*cos(a2) and equals n1*gcd*sin(a + b) if n1 = n2 else n1*gcd*sin(b - a) Examples ======== >>> from sympy.simplify.fu import trig_split >>> from sympy.abc import x, y, z >>> from sympy import cos, sin, sqrt >>> trig_split(cos(x), cos(y)) (1, 1, 1, x, y, True) >>> trig_split(2*cos(x), -2*cos(y)) (2, 1, -1, x, y, True) >>> trig_split(cos(x)*sin(y), cos(y)*sin(y)) (sin(y), 1, 1, x, y, True) >>> trig_split(cos(x), -sqrt(3)*sin(x), two=True) (2, 1, -1, x, pi/6, False) >>> trig_split(cos(x), sin(x), two=True) (sqrt(2), 1, 1, x, pi/4, False) >>> trig_split(cos(x), -sin(x), two=True) (sqrt(2), 1, -1, x, pi/4, False) >>> trig_split(sqrt(2)*cos(x), -sqrt(6)*sin(x), two=True) (2*sqrt(2), 1, -1, x, pi/6, False) >>> trig_split(-sqrt(6)*cos(x), -sqrt(2)*sin(x), two=True) (-2*sqrt(2), 1, 1, x, pi/3, False) >>> trig_split(cos(x)/sqrt(6), sin(x)/sqrt(2), two=True) (sqrt(6)/3, 1, 1, x, pi/6, False) >>> trig_split(-sqrt(6)*cos(x)*sin(y), -sqrt(2)*sin(x)*sin(y), two=True) (-2*sqrt(2)*sin(y), 1, 1, x, pi/3, False) >>> trig_split(cos(x), sin(x)) >>> trig_split(cos(x), sin(z)) >>> trig_split(2*cos(x), -sin(x)) >>> trig_split(cos(x), -sqrt(3)*sin(x)) >>> trig_split(cos(x)*cos(y), sin(x)*sin(z)) >>> trig_split(cos(x)*cos(y), sin(x)*sin(y)) >>> trig_split(-sqrt(6)*cos(x), sqrt(2)*sin(x)*sin(y), two=True) """ global _ROOT2, _ROOT3, _invROOT3 if _ROOT2 is None: _roots() a, b = [Factors(i) for i in (a, b)] ua, ub = a.normal(b) gcd = a.gcd(b).as_expr() n1 = n2 = 1 if S.NegativeOne in ua.factors: ua = ua.quo(S.NegativeOne) n1 = -n1 elif S.NegativeOne in ub.factors: ub = ub.quo(S.NegativeOne) n2 = -n2 a, b = [i.as_expr() for i in (ua, ub)] def pow_cos_sin(a, two): """Return ``a`` as a tuple (r, c, s) such that ``a = (r or 1)*(c or 1)*(s or 1)``. Three arguments are returned (radical, c-factor, s-factor) as long as the conditions set by ``two`` are met; otherwise None is returned. If ``two`` is True there will be one or two non-None values in the tuple: c and s or c and r or s and r or s or c with c being a cosine function (if possible) else a sine, and s being a sine function (if possible) else oosine. If ``two`` is False then there will only be a c or s term in the tuple. ``two`` also require that either two cos and/or sin be present (with the condition that if the functions are the same the arguments are different or vice versa) or that a single cosine or a single sine be present with an optional radical. If the above conditions dictated by ``two`` are not met then None is returned. """ c = s = None co = S.One if a.is_Mul: co, a = a.as_coeff_Mul() if len(a.args) > 2 or not two: return None if a.is_Mul: args = list(a.args) else: args = [a] a = args.pop(0) if a.func is cos: c = a elif a.func is sin: s = a elif a.is_Pow and a.exp is S.Half: # autoeval doesn't allow -1/2 co *= a else: return None if args: b = args[0] if b.func is cos: if c: s = b else: c = b elif b.func is sin: if s: c = b else: s = b elif b.is_Pow and b.exp is S.Half: co *= b else: return None return co if co is not S.One else None, c, s elif a.func is cos: c = a elif a.func is sin: s = a if c is None and s is None: return co = co if co is not S.One else None return co, c, s # get the parts m = pow_cos_sin(a, two) if m is None: return coa, ca, sa = m m = pow_cos_sin(b, two) if m is None: return cob, cb, sb = m # check them if (not ca) and cb or ca and ca.func is sin: coa, ca, sa, cob, cb, sb = cob, cb, sb, coa, ca, sa n1, n2 = n2, n1 if not two: # need cos(x) and cos(y) or sin(x) and sin(y) c = ca or sa s = cb or sb if c.func is not s.func: return None return gcd, n1, n2, c.args[0], s.args[0], c.func is cos else: if not coa and not cob: if (ca and cb and sa and sb): if not ((ca.func is sa.func) is (cb.func is sb.func)): return args = set([j.args for j in (ca, sa)]) if not all(i.args in args for i in (cb, sb)): return return gcd, n1, n2, ca.args[0], sa.args[0], ca.func is sa.func if ca and sa or cb and sb or \ two and (ca is None and sa is None or cb is None and sb is None): return c = ca or sa s = cb or sb if c.args != s.args: return if not coa: coa = S.One if not cob: cob = S.One if coa is cob: gcd *= _ROOT2 return gcd, n1, n2, c.args[0], pi/4, False elif coa/cob == _ROOT3: gcd *= 2*cob return gcd, n1, n2, c.args[0], pi/3, False elif coa/cob == _invROOT3: gcd *= 2*coa return gcd, n1, n2, c.args[0], pi/6, False def as_f_sign_1(e): """If ``e`` is a sum that can be written as ``g*(a + s)`` where ``s`` is ``+/-1``, return ``g``, ``a``, and ``s`` where ``a`` does not have a leading negative coefficient. Examples ======== >>> from sympy.simplify.fu import as_f_sign_1 >>> from sympy.abc import x >>> as_f_sign_1(x + 1) (1, x, 1) >>> as_f_sign_1(x - 1) (1, x, -1) >>> as_f_sign_1(-x + 1) (-1, x, -1) >>> as_f_sign_1(-x - 1) (-1, x, 1) >>> as_f_sign_1(2*x + 2) (2, x, 1) """ if not e.is_Add or len(e.args) != 2: return # exact match a, b = e.args if a in (S.NegativeOne, S.One): g = S.One if b.is_Mul and b.args[0].is_Number and b.args[0] < 0: a, b = -a, -b g = -g return g, b, a # gcd match a, b = [Factors(i) for i in e.args] ua, ub = a.normal(b) gcd = a.gcd(b).as_expr() if S.NegativeOne in ua.factors: ua = ua.quo(S.NegativeOne) n1 = -1 n2 = 1 elif S.NegativeOne in ub.factors: ub = ub.quo(S.NegativeOne) n1 = 1 n2 = -1 else: n1 = n2 = 1 a, b = [i.as_expr() for i in (ua, ub)] if a is S.One: a, b = b, a n1, n2 = n2, n1 if n1 == -1: gcd = -gcd n2 = -n2 if b is S.One: return gcd, a, n2 def _osborne(e): """Replace all hyperbolic functions with trig functions using the Osborne rule. References ========== http://en.wikipedia.org/wiki/Hyperbolic_function """ def f(rv): if not isinstance(rv, C.HyperbolicFunction): return rv if rv.func is sinh: return I*sin(rv.args[0]) elif rv.func is cosh: return cos(rv.args[0]) elif rv.func is tanh: return I*tan(rv.args[0]) elif rv.func is coth: return cot(rv.args[0])/I else: raise NotImplementedError('unhandled %s' % rv.func) return bottom_up(e, f) def _osbornei(e): """Replace all trig functions with hyperbolic functions using the Osborne rule. References ========== http://en.wikipedia.org/wiki/Hyperbolic_function """ def f(rv): if not isinstance(rv, C.TrigonometricFunction): return rv if rv.func is sin: return sinh(rv.args[0])/I elif rv.func is cos: return cosh(rv.args[0]) elif rv.func is tan: return tanh(rv.args[0])/I elif rv.func is cot: return coth(rv.args[0])*I elif rv.func is sec: return 1/cosh(rv.args[0]) elif rv.func is csc: return I/sinh(rv.args[0]) else: raise NotImplementedError('unhandled %s' % rv.func) return bottom_up(e, f) def hyper_as_trig(rv): """Return an expression containing hyperbolic functions in terms of trigonometric functions. Any trigonometric functions initially present are replaced with Dummy symbols and the function to undo the masking and the conversion back to hyperbolics is also returned. It should always be true that:: t, f = hyper_as_trig(expr) expr == f(t) Examples ======== >>> from sympy.simplify.fu import hyper_as_trig, fu >>> from sympy.abc import x >>> from sympy import cosh, sinh >>> eq = sinh(x)**2 + cosh(x)**2 >>> t, f = hyper_as_trig(eq) >>> f(fu(t)) cosh(2*x) References ========== http://en.wikipedia.org/wiki/Hyperbolic_function """ from sympy.simplify.simplify import signsimp # mask of trig functions trigs = rv.atoms(C.TrigonometricFunction) reps = [(t, Dummy()) for t in trigs] masked = rv.xreplace(dict(reps)) # get inversion substitutions in place reps = [(v, k) for k, v in reps] return _osborne(masked), lambda x: signsimp( _osbornei(x).xreplace(dict(reps))) sympy-0.7.4.1/sympy/simplify/simplify.py0000644000175000017500000052430712253362407020502 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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, count_ops, Expr, factor_terms, expand_multinomial, FunctionClass, expand_power_base, symbols, igcd, expand_power_exp, expand_log) from sympy.core.add import _unevaluated_Add from sympy.core.cache import cacheit from sympy.core.compatibility import iterable, reduce, default_sort_key, ordered, xrange from sympy.core.exprtools import Factors, gcd_terms from sympy.core.numbers import Float, Number, I 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, sin, cos, tan, cot, sinh, cosh, tanh, coth, piecewise_fold, Piecewise) from sympy.functions.elementary.exponential import ExpBase from sympy.functions.elementary.integers import ceiling from sympy.utilities.iterables import flatten, has_variety, sift 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.ntheory.factor_ import multiplicity from sympy.polys import (Poly, together, reduced, cancel, factor, ComputationFailed, lcm, gcd) import sympy.mpmath as mpmath def _mexpand(expr): return expand_mul(expand_multinomial(expr)) 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 def separate(expr, deep=False, force=False): """ Deprecated wrapper for ``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) 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. See Also ======== collect_const, collect_sqrt, rcollect """ 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 expr.func(*[ 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 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) See Also ======== collect, collect_const, collect_sqrt """ 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 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 = expr.func(*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, 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 ret.items(): ret[k] = Mul(*v) return ret 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 >>> eq = (x + y**5 + y)/(x - y) >>> ratsimpmodprime(eq, [x*y**5 - x - y], x, y, order='lex') (x**2 + x*y + x + y)/(x**2 - x*y) If ``polynomial`` is False, the algorithm computes a rational simplification which minimizes the sum of the total degrees of the numerator and the denominator. If ``polynomial`` is True, this function just brings numerator and denominator into a canonical form. This is much faster, but has potentially worse results. 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.monomials import monomial_div from sympy.core.compatibility import combinations_with_replacement from sympy.utilities.misc import debug quick = args.pop('quick', True) polynomial = args.pop('polynomial', False) debug('ratsimpmodprime', expr) # 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: 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:]] tested = set() def staircase(n): """ Compute all monomials with degree less than ``n`` that are not divisible by any element of ``leading_monomials``. """ if n == 0: return [1] S = [] for mi in combinations_with_replacement(xrange(len(opt.gens)), n): m = [0]*len(opt.gens) for i in mi: m[i] += 1 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] + staircase(n - 1) def _ratsimpmodprime(a, b, allsol, 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. As an extension, if quick=False, we look at all possible degrees such that the total degree is less than *or equal to* the best current solution. We retain a list of all solutions of minimal degree, and try to find the best one at the end. """ c, d = a, b steps = 0 maxdeg = a.total_degree() + b.total_degree() if quick: bound = maxdeg - 1 else: bound = maxdeg while N + D <= bound: if (N, D) in tested: break tested.add((N, D)) M1 = staircase(N) M2 = staircase(D) debug('%s / %s: %s, %s' % (N, D, M1, M2)) Cs = symbols("c:%d" % len(M1), cls=Dummy) Ds = symbols("d:%d" % len(M2), cls=Dummy) ng = Cs + Ds c_hat = Poly( sum([Cs[i] * M1[i] for i in xrange(len(M1))]), opt.gens + ng) d_hat = Poly( sum([Ds[i] * M2[i] for i in xrange(len(M2))]), opt.gens + ng) r = reduced(a * d_hat - b * c_hat, G, opt.gens + ng, order=opt.order, polys=True)[1] S = Poly(r, gens=opt.gens).coeffs() sol = solve(S, Cs + Ds, particular=True, quick=True) 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) if d == 0: raise ValueError('Ideal not prime?') allsol.append((c_hat, d_hat, S, Cs + Ds)) if N + D != maxdeg: allsol = [allsol[-1]] break steps += 1 N += 1 D += 1 if steps > 0: c, d, allsol = _ratsimpmodprime(c, d, allsol, N, D - steps) c, d, allsol = _ratsimpmodprime(c, d, allsol, N - steps, D) return c, d, allsol # 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] if polynomial: return (num/denom).cancel() c, d, allsol = _ratsimpmodprime( Poly(num, opt.gens), Poly(denom, opt.gens), []) if not quick and allsol: debug('Looking for best minimal solution. Got: %s' % len(allsol)) newsol = [] for c_hat, d_hat, S, ng in allsol: sol = solve(S, ng, particular=True, quick=False) newsol.append((c_hat.subs(sol), d_hat.subs(sol))) c, d = min(newsol, key=lambda x: len(x[0].terms()) + len(x[1].terms())) if not domain.has_Field: cn, c = c.clear_denoms(convert=True) dn, d = d.clear_denoms(convert=True) r = Rational(cn, dn) return (c*r.q)/(d*r.p) def trigsimp_groebner(expr, hints=[], quick=False, order="grlex", polynomial=False): """ Simplify trigonometric expressions using a groebner basis algorithm. This routine takes a fraction involving trigonometric or hyperbolic expressions, and tries to simplify it. The primary metric is the total degree. Some attempts are made to choose the simplest possible expression of the minimal degree, but this is non-rigorous, and also very slow (see the ``quick=True`` option). If ``polynomial`` is set to True, instead of simplifying numerator and denominator together, this function just brings numerator and denominator into a canonical form. This is much faster, but has potentially worse results. However, if the input is a polynomial, then the result is guaranteed to be an equivalent polynomial of minimal degree. The most important option is hints. Its entries can be any of the following: - a natural number - a function - an iterable of the form (func, var1, var2, ...) - anything else, interpreted as a generator A number is used to indicate that the search space should be increased. A function is used to indicate that said function is likely to occur in a simplified expression. An iterable is used indicate that func(var1 + var2 + ...) is likely to occur in a simplified . An additional generator also indicates that it is likely to occur. (See examples below). This routine carries out various computationally intensive algorithms. The option ``quick=True`` can be used to suppress one particularly slow step (at the expense of potentially more complicated results, but never at the expense of increased total degree). Examples ======== >>> from sympy.abc import x, y >>> from sympy import sin, tan, cos, sinh, cosh, tanh >>> from sympy.simplify.simplify import trigsimp_groebner Suppose you want to simplify ``sin(x)*cos(x)``. Naively, nothing happens: >>> ex = sin(x)*cos(x) >>> trigsimp_groebner(ex) sin(x)*cos(x) This is because ``trigsimp_groebner`` only looks for a simplification involving just ``sin(x)`` and ``cos(x)``. You can tell it to also try ``2*x`` by passing ``hints=[2]``: >>> trigsimp_groebner(ex, hints=[2]) sin(2*x)/2 >>> trigsimp_groebner(sin(x)**2 - cos(x)**2, hints=[2]) -cos(2*x) Increasing the search space this way can quickly become expensive. A much faster way is to give a specific expression that is likely to occur: >>> trigsimp_groebner(ex, hints=[sin(2*x)]) sin(2*x)/2 Hyperbolic expressions are similarly supported: >>> trigsimp_groebner(sinh(2*x)/sinh(x)) 2*cosh(x) Note how no hints had to be passed, since the expression already involved ``2*x``. The tangent function is also supported. You can either pass ``tan`` in the hints, to indicate that than should be tried whenever cosine or sine are, or you can pass a specific generator: >>> trigsimp_groebner(sin(x)/cos(x), hints=[tan]) tan(x) >>> trigsimp_groebner(sinh(x)/cosh(x), hints=[tanh(x)]) tanh(x) Finally, you can use the iterable form to suggest that angle sum formulae should be tried: >>> ex = (tan(x) + tan(y))/(1 - tan(x)*tan(y)) >>> trigsimp_groebner(ex, hints=[(tan, x, y)]) tan(x + y) """ # TODO # - preprocess by replacing everything by funcs we can handle # - optionally use cot instead of tan # - more intelligent hinting. # For example, if the ideal is small, and we have sin(x), sin(y), # add sin(x + y) automatically... ? # - algebraic numbers ... # - expressions of lowest degree are not distinguished properly # e.g. 1 - sin(x)**2 # - we could try to order the generators intelligently, so as to influence # which monomials appear in the quotient basis # THEORY # ------ # Ratsimpmodprime above can be used to "simplify" a rational function # modulo a prime ideal. "Simplify" mainly means finding an equivalent # expression of lower total degree. # # We intend to use this to simplify trigonometric functions. To do that, # we need to decide (a) which ring to use, and (b) modulo which ideal to # simplify. In practice, (a) means settling on a list of "generators" # a, b, c, ..., such that the fraction we want to simplify is a rational # function in a, b, c, ..., with coefficients in ZZ (integers). # (2) means that we have to decide what relations to impose on the # generators. There are two practical problems: # (1) The ideal has to be *prime* (a technical term). # (2) The relations have to be polynomials in the generators. # # We typically have two kinds of generators: # - trigonometric expressions, like sin(x), cos(5*x), etc # - "everything else", like gamma(x), pi, etc. # # Since this function is trigsimp, we will concentrate on what to do with # trigonometric expressions. We can also simplify hyperbolic expressions, # but the extensions should be clear. # # One crucial point is that all *other* generators really should behave # like indeterminates. In particular if (say) "I" is one of them, then # in fact I**2 + 1 = 0 and we may and will compute non-sensical # expressions. However, we can work with a dummy and add the relation # I**2 + 1 = 0 to our ideal, then substitute back in the end. # # Now regarding trigonometric generators. We split them into groups, # according to the argument of the trigonometric functions. We want to # organise this in such a way that most trigonometric identities apply in # the same group. For example, given sin(x), cos(2*x) and cos(y), we would # group as [sin(x), cos(2*x)] and [cos(y)]. # # Our prime ideal will be built in three steps: # (1) For each group, compute a "geometrically prime" ideal of relations. # Geometrically prime means that it generates a prime ideal in # CC[gens], not just ZZ[gens]. # (2) Take the union of all the generators of the ideals for all groups. # By the geometric primality condition, this is still prime. # (3) Add further inter-group relations which preserve primality. # # Step (1) works as follows. We will isolate common factors in the # argument, so that all our generators are of the form sin(n*x), cos(n*x) # or tan(n*x), with n an integer. Suppose first there are no tan terms. # The ideal [sin(x)**2 + cos(x)**2 - 1] is geometrically prime, since # X**2 + Y**2 - 1 is irreducible over CC. # Now, if we have a generator sin(n*x), than we can, using trig identities, # express sin(n*x) as a polynomial in sin(x) and cos(x). We can add this # relation to the ideal, preserving geometric primality, since the quotient # ring is unchanged. # Thus we have treated all sin and cos terms. # For tan(n*x), we add a relation tan(n*x)*cos(n*x) - sin(n*x) = 0. # (This requires of course that we already have relations for cos(n*x) and # sin(n*x).) It is not obvious, but it seems that this preserves geometric # primality. # XXX A real proof would be nice. HELP! # Sketch that is a prime ideal of # CC[S, C, T]: # - it suffices to show that the projective closure in CP**3 is # irreducible # - using the half-angle substitutions, we can express sin(x), tan(x), # cos(x) as rational functions in tan(x/2) # - from this, we get a rational map from CP**1 to our curve # - this is a morphism, hence the curve is prime # # Step (2) is trivial. # # Step (3) works by adding selected relations of the form # sin(x + y) - sin(x)*cos(y) - sin(y)*cos(x), etc. Geometric primality is # preserved by the same argument as before. from sympy.utilities.misc import debug from sympy import symbols from sympy.polys import parallel_poly_from_expr, groebner, ZZ from sympy.polys.polyerrors import PolificationFailed sin, cos, tan = C.sin, C.cos, C.tan sinh, cosh, tanh = C.sinh, C.cosh, C.tanh def parse_hints(hints): """Split hints into (n, funcs, iterables, gens).""" n = 1 funcs, iterables, gens = [], [], [] for e in hints: if isinstance(e, (int, Integer)): n = e elif isinstance(e, FunctionClass): funcs.append(e) elif iterable(e): iterables.append((e[0], e[1:])) # XXX sin(x+2y)? # Note: we go through polys so e.g. # sin(-x) -> -sin(x) -> sin(x) gens.extend(parallel_poly_from_expr( [e[0](x) for x in e[1:]] + [e[0](Add(*e[1:]))])[1].gens) else: gens.append(e) return n, funcs, iterables, gens def build_ideal(x, terms): """ Build generators for our ideal. Terms is an iterable with elements of the form (fn, coeff), indicating that we have a generator fn(coeff*x). If any of the terms is trigonometric, sin(x) and cos(x) are guaranteed to appear in terms. Similarly for hyperbolic functions. For tan(n*x), sin(n*x) and cos(n*x) are guaranteed. """ gens = [] I = [] y = Dummy('y') for fn, coeff in terms: for c, s, t, rel in ( [cos, sin, tan, cos(x)**2 + sin(x)**2 - 1], [cosh, sinh, tanh, cosh(x)**2 - sinh(x)**2 - 1]): if coeff == 1 and fn in [c, s]: I.append(rel) elif fn == t: I.append(t(coeff*x)*c(coeff*x) - s(coeff*x)) elif fn in [c, s]: cn = fn(coeff*y).expand(trig=True).subs(y, x) I.append(fn(coeff*x) - cn) return list(set(I)) def analyse_gens(gens, hints): """ Analyse the generators ``gens``, using the hints ``hints``. The meaning of ``hints`` is described in the main docstring. Return a new list of generators, and also the ideal we should work with. """ # First parse the hints n, funcs, iterables, extragens = parse_hints(hints) debug('n=%s' % n, 'funcs:', funcs, 'iterables:', iterables, 'extragens:', extragens) # We just add the extragens to gens and analyse them as before gens = list(gens) gens.extend(extragens) # remove duplicates funcs = list(set(funcs)) iterables = list(set(iterables)) gens = list(set(gens)) # all the functions we can do anything with allfuncs = set([sin, cos, tan, sinh, cosh, tanh]) # sin(3*x) -> ((3, x), sin) trigterms = [(g.args[0].as_coeff_mul(), g.func) for g in gens if g.func in allfuncs] # Our list of new generators - start with anything that we cannot # work with (i.e. is not a trigonometric term) freegens = [g for g in gens if g.func not in allfuncs] newgens = [] trigdict = {} for (coeff, var), fn in trigterms: trigdict.setdefault(var, []).append((coeff, fn)) res = [] # the ideal for key, val in trigdict.items(): # We have now assembeled a dictionary. Its keys are common # arguments in trigonometric expressions, and values are lists of # pairs (fn, coeff). x0, (fn, coeff) in trigdict means that we # need to deal with fn(coeff*x0). We take the rational gcd of the # coeffs, call it ``gcd``. We then use x = x0/gcd as "base symbol", # all other arguments are integral multiples thereof. # We will build an ideal which works with sin(x), cos(x). # If hint tan is provided, also work with tan(x). Moreover, if # n > 1, also work with sin(k*x) for k <= n, and similarly for cos # (and tan if the hint is provided). Finally, any generators which # the ideal does not work with but we need to accomodate (either # because it was in expr or because it was provided as a hint) # we also build into the ideal. # This selection process is expressed in the list ``terms``. # build_ideal then generates the actual relations in our ideal, # from this list. fns = [x[1] for x in val] val = [x[0] for x in val] gcd = reduce(igcd, val) terms = [(fn, v/gcd) for (fn, v) in zip(fns, val)] fs = set(funcs + fns) for c, s, t in ([cos, sin, tan], [cosh, sinh, tanh]): if any(x in fs for x in (c, s, t)): fs.add(c) fs.add(s) for fn in fs: for k in range(1, n + 1): terms.append((fn, k)) extra = [] for fn, v in terms: if fn == tan: extra.append((sin, v)) extra.append((cos, v)) if fn in [sin, cos] and tan in fs: extra.append((tan, v)) if fn == tanh: extra.append((sinh, v)) extra.append((cosh, v)) if fn in [sinh, cosh] and tanh in fs: extra.append((tanh, v)) terms.extend(extra) x = gcd*Mul(*key) r = build_ideal(x, terms) res.extend(r) newgens.extend(set(fn(v*x) for fn, v in terms)) # Add generators for compound expressions from iterables for fn, args in iterables: if fn == tan: # Tan expressions are recovered from sin and cos. iterables.extend([(sin, args), (cos, args)]) elif fn == tanh: # Tanh expressions are recovered from sihn and cosh. iterables.extend([(sinh, args), (cosh, args)]) else: dummys = symbols('d:%i' % len(args), cls=Dummy) expr = fn( Add(*dummys)).expand(trig=True).subs(list(zip(dummys, args))) res.append(fn(Add(*args)) - expr) if myI in gens: res.append(myI**2 + 1) freegens.remove(myI) newgens.append(myI) return res, freegens, newgens myI = Dummy('I') expr = expr.subs(S.ImaginaryUnit, myI) subs = [(myI, S.ImaginaryUnit)] num, denom = cancel(expr).as_numer_denom() try: (pnum, pdenom), opt = parallel_poly_from_expr([num, denom]) except PolificationFailed: return expr debug('initial gens:', opt.gens) ideal, freegens, gens = analyse_gens(opt.gens, hints) debug('ideal:', ideal) debug('new gens:', gens, " -- len", len(gens)) debug('free gens:', freegens, " -- len", len(gens)) # NOTE we force the domain to be ZZ to stop polys from injecting generators # (which is usually a sign of a bug in the way we build the ideal) if not gens: return expr G = groebner(ideal, order=order, gens=gens, domain=ZZ) debug('groebner basis:', list(G), " -- len", len(G)) # If our fraction is a polynomial in the free generators, simplify all # coefficients separately: if freegens and pdenom.has_only_gens(*set(gens).intersection(pdenom.gens)): num = Poly(num, gens=gens+freegens).eject(*gens) res = [] for monom, coeff in num.terms(): ourgens = set(parallel_poly_from_expr([coeff, denom])[1].gens) # We compute the transitive closure of all generators that can # be reached from our generators through relations in the ideal. changed = True while changed: changed = False for p in ideal: p = Poly(p) if not ourgens.issuperset(p.gens) and \ not p.has_only_gens(*set(p.gens).difference(ourgens)): changed = True ourgens.update(p.exclude().gens) # NOTE preserve order! realgens = [x for x in gens if x in ourgens] # The generators of the ideal have now been (implicitely) split # into two groups: those involving ourgens and those that don't. # Since we took the transitive closure above, these two groups # live in subgrings generated by a *disjoint* set of variables. # Any sensible groebner basis algorithm will preserve this disjoint # structure (i.e. the elements of the groebner basis can be split # similarly), and and the two subsets of the groebner basis then # form groebner bases by themselves. (For the smaller generating # sets, of course.) ourG = [g.as_expr() for g in G.polys if g.has_only_gens(*ourgens.intersection(g.gens))] res.append(Mul(*[a**b for a, b in zip(freegens, monom)]) * \ ratsimpmodprime(coeff/denom, ourG, order=order, gens=realgens, quick=quick, domain=ZZ, polynomial=polynomial).subs(subs)) return Add(*res) # NOTE The following is simpler and has less assumptions on the # groebner basis algorithm. If the above turns out to be broken, # use this. return Add(*[Mul(*[a**b for a, b in zip(freegens, monom)]) * \ ratsimpmodprime(coeff/denom, list(G), order=order, gens=gens, quick=quick, domain=ZZ) for monom, coeff in num.terms()]) else: return ratsimpmodprime( expr, list(G), order=order, gens=freegens+gens, quick=quick, domain=ZZ, polynomial=polynomial).subs(subs) _trigs = (C.TrigonometricFunction, C.HyperbolicFunction) def trigsimp(expr, **opts): """ reduces expression by using known trig identities Notes ===== method: - Determine the method to use. Valid choices are 'matching' (default), 'groebner', 'combined', and 'fu'. If 'matching', simplify the expression recursively by targeting common patterns. If 'groebner', apply an experimental groebner basis algorithm. In this case further options are forwarded to ``trigsimp_groebner``, please refer to its docstring. If 'combined', first run the groebner basis algorithm with small default parameters, then run the 'matching' algorithm. 'fu' runs the collection of trigonometric transformations described by Fu, et al. (see the `fu` docstring). Examples ======== >>> from sympy import trigsimp, sin, cos, log >>> from sympy.abc import x, y >>> e = 2*sin(x)**2 + 2*cos(x)**2 >>> trigsimp(e) 2 Simplification occurs wherever trigonometric functions are located. >>> trigsimp(log(e)) log(2) Using `method="groebner"` (or `"combined"`) might lead to greater simplification. The old trigsimp routine can be accessed as with method 'old'. >>> from sympy import coth, tanh >>> t = 3*tanh(x)**7 - 2/coth(x)**7 >>> trigsimp(t, method='old') == t True >>> trigsimp(t) tanh(x)**7 """ from sympy.simplify.fu import fu expr = sympify(expr) old = opts.pop('old', False) if not old: opts.pop('deep', None) recursive = opts.pop('recursive', None) method = opts.pop('method', 'matching') else: method = 'old' def groebnersimp(ex, **opts): def traverse(e): if e.is_Atom: return e args = [traverse(x) for x in e.args] if e.is_Function or e.is_Pow: args = [trigsimp_groebner(x, **opts) for x in args] return e.func(*args) return trigsimp_groebner(traverse(ex), **opts) trigsimpfunc = { 'fu': (lambda x: fu(x)), 'matching': (lambda x: futrig(x)), 'groebner': (lambda x: groebnersimp(x, **opts)), 'combined': (lambda x: futrig(groebnersimp(x, polynomial=True, hints=[2, tan]))), 'old': lambda x: trigsimp_old(x, **opts), }[method] return trigsimpfunc(expr) 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, if non-zero, the terms of the Add will be returned, else the expression itself will be returned as a single term. If ``evaluate`` is True, the expression with any collected terms will be returned. 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(3)*a, sqrt(5)*b, sqrt(2)*(a + b)), 3) >>> collect_sqrt(a*sqrt(2) + b, evaluate=False) ((b, sqrt(2)*a), 1) >>> collect_sqrt(a + b, evaluate=False) ((a + b,), 0) See Also ======== collect, collect_const, rcollect """ # this step will help to standardize any complex arguments # of sqrts 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) # we only want radicals, so exclude Number handling; in this case # d will be evaluated d = collect_const(expr, *vars, Numbers=False) hit = expr != d if not evaluate: nrad = 0 # make the evaluated args canonical args = list(ordered(Add.make_args(d))) for i, m in enumerate(args): c, nc = m.args_cnc() for ci in c: # XXX should this be restricted to ci.is_number as above? if ci.is_Pow and ci.exp.is_Rational and ci.exp.q == 2 or \ ci is S.ImaginaryUnit: nrad += 1 break args[i] *= coeff if not (hit or nrad): args = [Add(*args)] return tuple(args), nrad return coeff*d def collect_const(expr, *vars, **kwargs): """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. Although any Number can also be targeted, if this is not desired set ``Numbers=False`` and no Float or Rational will be collected. Examples ======== >>> from sympy import sqrt >>> from sympy.abc import a, s, x, y, z >>> 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) The collection is sign-sensitive, giving higher precedence to the unsigned values: >>> collect_const(x - y - z) x - (y + z) >>> collect_const(-y - z) -(y + z) >>> collect_const(2*x - 2*y - 2*z, 2) 2*(x - y - z) >>> collect_const(2*x - 2*y - 2*z, -2) 2*x - 2*(y + z) See Also ======== collect, collect_sqrt, rcollect """ if not expr.is_Add: return expr recurse = False Numbers = kwargs.get('Numbers', True) if not vars: recurse = True vars = set() for a in expr.args: for m in Mul.make_args(a): if m.is_number: vars.add(m) else: vars = sympify(vars) if not Numbers: vars = [v for v in vars if not v.is_Number] vars = list(ordered(vars)) for v in vars: terms = defaultdict(list) Fv = Factors(v) for m in Add.make_args(expr): f = Factors(m) q, r = f.div(Fv) if r.is_one: # only accept this as a true factor if # it didn't change an exponent from an Integer # to a non-Integer, e.g. 2/sqrt(2) -> sqrt(2) # -- we aren't looking for this sort of change fwas = f.factors.copy() fnow = q.factors if not any(k in fwas and fwas[k].is_Integer and not fnow[k].is_Integer for k in fnow): terms[v].append(q.as_expr()) continue terms[S.One].append(m) args = [] hit = False uneval = False for k in ordered(terms): v = terms[k] if k is S.One: args.extend(v) continue if len(v) > 1: v = Add(*v) hit = True if recurse and v != expr: vars.append(v) else: v = v[0] # be careful not to let uneval become True unless # it must be because it's going to be more expensive # to rebuild the expression as an unevaluated one if Numbers and k.is_Number and v.is_Add: args.append(_keep_coeff(k, v, sign=True)) uneval = True else: args.append(k*v) if hit: if uneval: expr = _unevaluated_Add(*args) else: 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 _is_sum_surds(p): args = p.args if p.is_Add else [p] for y in args: if not ((y**2).is_Rational and y.is_real): return False return True def _nthroot_solve(p, n, prec): """ helper function for ``nthroot`` It denests ``p**Rational(1, n)`` using its minimal polynomial """ from sympy.polys.numberfields import _minimal_polynomial_sq from sympy.solvers import solve while n % 2 == 0: p = sqrtdenest(sqrt(p)) n = n // 2 if n == 1: return p pn = p**Rational(1, n) x = Symbol('x') f = _minimal_polynomial_sq(p, n, x) if f is None: return None sols = solve(f, x) for sol in sols: if abs(sol - pn).n() < 1./10**prec: sol = sqrtdenest(sol) if _mexpand(sol**n) == p: return sol def nthroot(expr, n, max_len=4, prec=15): """ compute a real nth-root of a sum of surds Parameters ========== expr : sum of surds n : integer max_len : maximum number of surds passed as constants to ``nsimplify`` Algorithm ========= First ``nsimplify`` is used to get a candidate root; if it is not a root the minimal polynomial is computed; the answer is one of its roots. Examples ======== >>> from sympy.simplify.simplify import nthroot >>> from sympy import Rational, sqrt >>> nthroot(90 + 34*sqrt(7), 3) sqrt(7) + 3 """ from sympy.simplify.sqrtdenest import sqrt_depth, is_algebraic expr = sympify(expr) n = sympify(n) p = expr**Rational(1, n) if not n.is_integer: return p if not _is_sum_surds(expr): return p surds = [] coeff_muls = [x.as_coeff_Mul() for x in expr.args] for x, y in coeff_muls: if not x.is_rational: return p if y is S.One: continue if not (y.is_Pow and y.exp == S.Half and y.base.is_integer): return p surds.append(y) surds.sort() surds = surds[:max_len] if expr < 0 and n % 2 == 1: p = (-expr)**Rational(1, n) a = nsimplify(p, constants=surds) res = a if _mexpand(a**n) == _mexpand(-expr) else p return -res a = nsimplify(p, constants=surds) if _mexpand(a) is not _mexpand(p) and _mexpand(a**n) == _mexpand(expr): return _mexpand(a) expr = _nthroot_solve(expr, n, prec) if expr is None: return p return expr 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) 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 then the expression is returned unchanged. Examples ======== >>> from sympy import radsimp, sqrt, Symbol, denom, pprint, I >>> from sympy import factor_terms, fraction, signsimp >>> from sympy.simplify.simplify import collect_sqrt >>> 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 = map(Symbol, 'xy') >>> e = ((2 + 2*sqrt(2))*x + (2 + sqrt(8))*y)/(2 + sqrt(2)) >>> radsimp(e) sqrt(2)*(x + y) No simplification beyond removal of the gcd is done. One might want to polish the result a little, however, by collecting square root terms: >>> r2 = sqrt(2) >>> r5 = sqrt(5) >>> ans = radsimp(1/(y*r2 + x*r2 + a*r5 + b*r5)); pprint(ans) ___ ___ ___ ___ \/ 5 *a + \/ 5 *b - \/ 2 *x - \/ 2 *y ------------------------------------------ 2 2 2 2 5*a + 10*a*b + 5*b - 2*x - 4*x*y - 2*y >>> n, d = fraction(ans) >>> pprint(factor_terms(signsimp(collect_sqrt(n))/d, radical=True)) ___ ___ \/ 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 or there is no denominator, the original expression will be returned. >>> radsimp(sqrt(2)*x + sqrt(2)) sqrt(2)*x + sqrt(2) 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)) """ from sympy.core.mul import _unevaluated_Mul as _umul from sympy.core.exprtools import Factors syms = symbols("a:d A:D") def _num(rterms): # return the multiplier that will simplify the expression described # by rterms [(sqrt arg, coeff), ... ] a, b, c, d, A, B, C, D = syms if len(rterms) == 2: reps = dict(list(zip([A, a, B, b], [j for i in rterms for j in i]))) return ( sqrt(A)*a - sqrt(B)*b).xreplace(reps) if len(rterms) == 3: reps = dict(list(zip([A, a, B, b, C, c], [j for i in rterms for j in i]))) return ( (sqrt(A)*a + sqrt(B)*b - sqrt(C)*c)*(2*sqrt(A)*sqrt(B)*a*b - A*a**2 - B*b**2 + C*c**2)).xreplace(reps) elif len(rterms) == 4: reps = dict(list(zip([A, a, B, b, C, c, D, d], [j for i in rterms for j in i]))) return ((sqrt(A)*a + sqrt(B)*b - sqrt(C)*c - sqrt(D)*d)*(2*sqrt(A)*sqrt(B)*a*b - A*a**2 - B*b**2 - 2*sqrt(C)*sqrt(D)*c*d + C*c**2 + D*d**2)*(-8*sqrt(A)*sqrt(B)*sqrt(C)*sqrt(D)*a*b*c*d + A**2*a**4 - 2*A*B*a**2*b**2 - 2*A*C*a**2*c**2 - 2*A*D*a**2*d**2 + B**2*b**4 - 2*B*C*b**2*c**2 - 2*B*D*b**2*d**2 + C**2*c**4 - 2*C*D*c**2*d**2 + D**2*d**4)).xreplace(reps) elif len(rterms) == 1: return sqrt(rterms[0][0]) else: raise NotImplementedError def handle(expr): if expr.is_Atom: return expr n, d = fraction(expr) if d.is_Atom: # n can't be an Atom since expr is not an Atom n = n.func(*[handle(a) for a in n.args]) return _umul(n, 1/d) elif n is not S.One: return Mul(n, handle(1/d)) elif d.is_Mul: return _umul(*[handle(1/d) for d in d.args]) if not symbolic and d.free_symbols: return expr if d.is_Pow and d.exp.is_Rational and d.exp.q == 2: d2 = sqrtdenest(sqrt(d.base))**d.exp.p if d2 != d: return handle(1/d2) elif d.is_Pow and (d.exp.is_integer or d.base.is_positive): # (1/d**i) = (1/d)**i return handle(1/d.base)**d.exp if not (d.is_Add or d.is_Pow and d.exp.is_Rational and d.exp.q == 2): return 1/d.func(*[handle(a) for a in d.args]) # handle 1/d treating d as an Add (though it may not be) keep = True # keep changes that are made # flatten it and collect radicals after checking for special # conditions d = _mexpand(d) # did it change? if d.is_Atom: return 1/d # is it a number that might be handled easily? if d.is_number: _d = nsimplify(d) if _d.is_Number and _d.equals(d): return 1/_d while True: # collect similar terms collected = defaultdict(list) for m in Add.make_args(d): # d might have become non-Add p2 = [] other = [] for i in Mul.make_args(m): if i.is_Pow and (i.exp is S.Half or i.exp.is_Rational and i.exp.q != 1 and log(i.exp.q, 2).is_Integer): p2.append(i.base if i.exp is S.Half else i.base**(2*i.exp)) elif i is S.ImaginaryUnit: p2.append(S.NegativeOne) else: other.append(i) collected[tuple(ordered(p2))].append(Mul(*other)) rterms = list(ordered(list(collected.items()))) rterms = [(Mul(*i), Add(*j)) for i, j in rterms] nrad = len(rterms) - (1 if rterms[0][0] is S.One else 0) if nrad < 1: break elif nrad > max_terms: # there may have been invalid operations leading to this point # so don't keep changes, e.g. this expression is troublesome # in collecting terms so as not to raise the issue of 2834: # r = sqrt(sqrt(5) + 5) # eq = 1/(sqrt(5)*r + 2*sqrt(5)*sqrt(-sqrt(5) + 5) + 5*r) keep = False break if len(rterms) > 4: # in general, only 4 terms can be removed with repeated squaring # but other considerations can guide selection of radical terms # so that radicals are removed if all([x.is_Integer and (y**2).is_Rational for x, y in rterms]): nd, d = rad_rationalize(S.One, Add._from_args( [sqrt(x)*y for x, y in rterms])) n *= nd else: # is there anything else that might be attempted? keep = False break num = powsimp(_num(rterms)) n *= num d *= num d = _mexpand(d) if d.is_Atom: break if not keep: return expr return _umul(n, 1/d) coeff, expr = expr.as_coeff_Add() expr = expr.normal() old = fraction(expr) n, d = fraction(handle(expr)) if old != (n, d): if not d.is_Atom: was = (n, d) n = signsimp(n, evaluate=False) d = signsimp(d, evaluate=False) u = Factors(_umul(n, 1/d)) u = _umul(*[k**v for k, v in u.factors.items()]) n, d = fraction(u) if old == (n, d): n, d = was n = expand_mul(n) if d.is_Number or d.is_Add: n2, d2 = fraction(gcd_terms(_umul(n, 1/d))) if d2.is_Number or (d2.count_ops() <= d.count_ops()): n, d = [signsimp(i) for i in (n2, d2)] if n.is_Mul and n.args[0].is_Number: n = n.func(*n.args) return coeff + _umul(n, 1/d) 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 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) if isinstance(arg, Expr) else arg 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 = expand_log(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)) 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') 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 = [] coeff = S.One for term in expr.args: if term.is_Rational: coeff *= term continue if term.is_Pow: term = _denest_pow(term) 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 ordered(iter(c_powers.items())): # allow 2**x/4 -> 2**(x - 2); don't do this when b and e are # Numbers since autoevaluation will undo it, e.g. # 2**(1/3)/4 -> 2**(1/3 - 2) -> 2**(1/3)/4 assert 2**(S(1)/3 - 2) == 2**(S(1)/3)/4 if (b and b.is_Number and not all(ei.is_Number for ei in e) and \ coeff is not S.One and b not in (S.One, S.NegativeOne)): m = multiplicity(abs(b), abs(coeff)) if m: e.append(m) coeff /= b**m c_powers[b] = Add(*e) if coeff is not S.One: if coeff in c_powers: c_powers[coeff] += S.One else: c_powers[coeff] = S.One # convert to plain dictionary c_powers = dict(c_powers) # 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 # check for base and negated base pairs be = list(c_powers.items()) _n = S.NegativeOne for i, (b, e) in enumerate(be): if ((-b).is_Symbol or b.is_Add) and -b in c_powers: if (b.is_positive in (0, 1) or e.is_integer): c_powers[-b] += c_powers.pop(b) if _n in c_powers: c_powers[_n] += e else: c_powers[_n] = 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 expo. and it's current value 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 xrange(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 xrange(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 rad's 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 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 = expr.func(*(newexpr + [Pow(b, e) for b, e in c_powers.items()])) if combine == 'exp': return expr.func(newexpr, expr.func(*nc_part)) else: return recurse(expr.func(*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(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 xrange(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 = expr.func(*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 = expr.func(*(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 expr.func(*(c_part + nc_part)) else: raise ValueError("combine must be one of ('all', 'exp', 'base').") 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 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') 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 xrange(n): result *= a + i return result elif n < 0: for i in xrange(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, level=0): """ Simplify products of gamma functions further. """ if expr.is_Atom: return expr def gamma_rat(x): # helper to simplify ratios of gammas was = x.count(gamma) xx = x.replace(gamma, lambda n: rf(1, (n - 1).expand() ).replace(rf, lambda a, b: gamma(a + b)/gamma(a))) if xx.count(gamma) < was: x = xx return x def gamma_factor(x): # return True if there is a gamma factor in shallow args if x.func is gamma: return True if x.is_Add or x.is_Mul: return any(gamma_factor(xi) for xi in x.args) if x.is_Pow and (x.exp.is_integer or x.base.is_positive): return gamma_factor(x.base) return False # recursion step if level == 0: expr = expr.func(*[rule_gamma(x, level + 1) for x in expr.args]) level += 1 if not expr.is_Mul: return expr # non-commutative step if level == 1: args, nc = expr.args_cnc() if not args: return expr if nc: return rule_gamma(Mul._from_args(args), level + 1)*Mul._from_args(nc) level += 1 # pure gamma handling, not factor absorbtion if level == 2: sifted = sift(expr.args, gamma_factor) gamma_ind = Mul(*sifted.pop(False, [])) d = Mul(*sifted.pop(True, [])) assert not sifted nd, dd = d.as_numer_denom() for ipass in range(2): args = list(ordered(Mul.make_args(nd))) for i, ni in enumerate(args): if ni.is_Add: ni, dd = Add(*[ rule_gamma(gamma_rat(a/dd), level + 1) for a in ni.args] ).as_numer_denom() args[i] = ni if not dd.has(gamma): break nd = Mul(*args) if ipass == 0 and not gamma_factor(nd): break nd, dd = dd, nd # now process in reversed order expr = gamma_ind*nd/dd if not (expr.is_Mul and (gamma_factor(dd) or gamma_factor(nd))): return expr level += 1 # iteration until constant if level == 3: while True: was = expr expr = rule_gamma(expr, 4) if expr == was: return expr numer_gammas = [] denom_gammas = [] numer_others = [] denom_others = [] def explicate(p): if p is S.One: return None, [] b, e = p.as_base_exp() if e.is_Integer: if b.func is gamma: return True, [b.args[0]]*e else: return False, [b]*e else: return False, [p] newargs = list(ordered(expr.args)) while newargs: n, d = newargs.pop().as_numer_denom() isg, l = explicate(n) if isg: numer_gammas.extend(l) elif isg is False: numer_others.extend(l) isg, l = explicate(d) if isg: denom_gammas.extend(l) elif isg is False: denom_others.extend(l) # =========== level 2 work: pure gamma manipulation ========= # 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 gammas by using the duplication # theorem to cancel an upper and lower: gamma(2*s)/gamma(s) = # 2**(2*s + 1)/(4*sqrt(pi))*gamma(s + 1/2). Although this could # be done with higher argument ratios like gamma(3*x)/gamma(x), # this would not reduce the number of gammas as in this case. 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 xrange(n): no.append(2*y + k) elif n < 0: for k in xrange(-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 reduce the number of gamma factors by applying the # multiplication theorem (used when n gammas with args differing # by 1/n mod 1 are encountered). # # run of 2 with args differing by 1/2 # # >>> combsimp(gamma(x)*gamma(x+S.Half)) # 2*sqrt(2)*2**(-2*x - 1/2)*sqrt(pi)*gamma(2*x) # # run of 3 args differing by 1/3 (mod 1) # # >>> combsimp(gamma(x)*gamma(x+S(1)/3)*gamma(x+S(2)/3)) # 6*3**(-3*x - 1/2)*pi*gamma(3*x) # >>> combsimp(gamma(x)*gamma(x+S(1)/3)*gamma(x+S(5)/3)) # 2*3**(-3*x - 1/2)*pi*(3*x + 2)*gamma(3*x) # 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) # =========== level >= 2 work: factor absorbtion ========= if level >= 2: # Try to absorb factors into the gammas: x*gamma(x) -> gamma(x + 1) # and gamma(x)/(x - 1) -> gamma(x - 1) # This code (in particular repeated calls to find_fuzzy) can be very # slow. def find_fuzzy(l, x): if not l: return 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(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 # =========== rebuild expr ================================== 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) was = factor(expr) expr = rule_gamma(was) if expr != was: expr = factor(expr) return 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 e.func(*[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 def simplify(expr, ratio=1.7, measure=count_ops, fu=False): """ 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 >>> trigsimp(a) (x**2 + x)/x >>> cancel(_) 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(a) + 1)) >>> count_ops(g) 8 >>> count_ops(h) 5 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)) 2*LOG + MUL + POW + SUB >>> 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) 14 >>> 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. """ from sympy.simplify.hyperexpand import hyperexpand from sympy.functions.special.bessel import BesselBase original_expr = expr = signsimp(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 from sympy import Sum, Product if not isinstance(expr, Basic) or not expr.args: # XXX: temporary hack return expr if not isinstance(expr, (Add, Mul, Pow, ExpBase)): return expr.func(*[simplify(x, ratio=ratio, measure=measure, fu=fu) for x in expr.args]) # 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) expr = bottom_up(expr, lambda w: w.normal()) expr = Mul(*powsimp(expr).as_content_primitive()) _e = cancel(expr) expr1 = shorter(_e, _mexpand(_e).cancel()) # issue 3730 expr2 = shorter(together(expr, deep=True), together(expr1, deep=True)) if ratio is S.Infinity: expr = expr2 else: expr = shorter(expr2, expr1, expr) if not isinstance(expr, Basic): # XXX: temporary hack return expr expr = factor_terms(expr, sign=False) # hyperexpand automatically only works on hypergeometric terms expr = hyperexpand(expr) expr = piecewise_fold(expr) if expr.has(BesselBase): expr = besselsimp(expr) if expr.has(C.TrigonometricFunction) and not fu 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) if expr.has(Sum): expr = sum_simplify(expr) if expr.has(Product): expr = product_simplify(expr) short = shorter(powsimp(expr, combine='exp', deep=True), powsimp(expr), expr) short = shorter(short, factor_terms(short), expand_power_exp(expand_mul(short))) if short.has(C.TrigonometricFunction, C.HyperbolicFunction, C.ExpBase): short = exptrigsimp(short, simplify=False) # 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 = short.xreplace(hollow_mul) 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 = fraction(expr) if d != 0: expr = signsimp(-n/(-d)) if measure(expr) > ratio*measure(original_expr): expr = original_expr return expr def _real_to_rational(expr, tolerance=None): """ 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 = {} reduce_num = None if tolerance is not None and tolerance < 1: reduce_num = ceiling(1/tolerance) for float in p.atoms(C.Float): key = float if reduce_num is not None: r = Rational(float).limit_denominator(reduce_num) elif (tolerance is not None and tolerance >= 1 and float.is_Integer is False): r = Rational(tolerance*round(float/tolerance) ).limit_denominator(int(tolerance)) else: r = nsimplify(float, rational=False) # e.g. log(3).n() -> log(3) instead of a Rational if not r.is_Rational: if float < 0: float = -float d = Pow(10, int((mpmath.log(float)/mpmath.log(10)))) r = -Rational(str(float/d))*d elif float > 0: d = Pow(10, int((mpmath.log(float)/mpmath.log(10)))) r = Rational(str(float/d))*d else: r = Integer(0) reps[key] = r return p.subs(reps, simultaneous=True) 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, tolerance) # SymPy's default tolerance for Rationals is 15; other numbers may have # lower tolerances set, so use them to pick the largest tolerance if None # was given if tolerance is None: tolerance = 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() # safety check to make sure that this evaluated to a number if not (re.is_Number and im.is_Number): 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_bounded 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) def logcombine(expr, force=False): """ Takes logarithms and combines them using the following rules: - log(x) + log(y) == log(x*y) if both are not negative - a*log(x) == log(x**a) if x is positive and a is real If ``force`` is True then the assumptions above will be assumed to hold if there is no assumption already in place on a quantity. For example, if ``a`` is imaginary or the argument negative, force will not perform a combination but if ``a`` is a symbol with no assumptions the change will take place. Examples ======== >>> from sympy import Symbol, symbols, log, logcombine, I >>> 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) The transformation is limited to factors and/or terms that contain logs, so the result depends on the initial state of expansion: >>> eq = (2 + 3*I)*log(x) >>> logcombine(eq, force=True) == eq True >>> logcombine(eq.expand(), force=True) log(x**2) + I*log(x**3) See Also ======== posify: replace all symbols with symbols having positive assumptions """ def f(rv): if not (rv.is_Add or rv.is_Mul): return rv def gooda(a): # bool to tell whether the leading ``a`` in ``a*log(x)`` # could appear as log(x**a) return (a is not S.NegativeOne and # -1 *could* go, but we disallow (a.is_real or force and a.is_real is not False)) def goodlog(l): # bool to tell whether log ``l``'s argument can combine with others a = l.args[0] return a.is_positive or force and a.is_nonpositive is not False other = [] logs = [] log1 = defaultdict(list) for a in Add.make_args(rv): if a.func is log and goodlog(a): log1[()].append(([], a)) elif not a.is_Mul: other.append(a) else: ot = [] co = [] lo = [] for ai in a.args: if ai.is_Rational and ai < 0: ot.append(S.NegativeOne) co.append(-ai) elif ai.func is log and goodlog(ai): lo.append(ai) elif gooda(ai): co.append(ai) else: ot.append(ai) if len(lo) > 1: logs.append((ot, co, lo)) elif lo: log1[tuple(ot)].append((co, lo[0])) else: other.append(a) # if there is only one log at each coefficient and none have # an exponent to place inside the log then there is nothing to do if not logs and all(len(log1[k]) == 1 and log1[k][0] == [] for k in log1): return rv # collapse multi-logs as far as possible in a canonical way # TODO: see if x*log(a)+x*log(a)*log(b) -> x*log(a)*(1+log(b))? # -- in this case, it's unambiguous, but if it were were a log(c) in # each term then it's arbitrary whether they are grouped by log(a) or # by log(c). So for now, just leave this alone; it's probably better to # let the user decide for o, e, l in logs: l = list(ordered(l)) e = log(l.pop(0).args[0]**Mul(*e)) while l: li = l.pop(0) e = log(li.args[0]**e) c, l = Mul(*o), e if l.func is log: # it should be, but check to be sure log1[(c,)].append(([], l)) else: other.append(c*l) # logs that have the same coefficient can multiply for k in list(log1.keys()): log1[Mul(*k)] = log(logcombine(Mul(*[ l.args[0]**Mul(*c) for c, l in log1.pop(k)]), force=force)) # logs that have oppositely signed coefficients can divide for k in ordered(list(log1.keys())): if not k in log1: # already popped as -k continue if -k in log1: # figure out which has the minus sign; the one with # more op counts should be the one num, den = k, -k if num.count_ops() > den.count_ops(): num, den = den, num other.append(num*log(log1.pop(num).args[0]/log1.pop(den).args[0])) else: other.append(k*log1.pop(k)) return Add(*other) return bottom_up(expr, f) def bottom_up(rv, F, atoms=False, nonbasic=False): """Apply ``F`` to all expressions in an expression tree from the bottom up. If ``atoms`` is True, apply ``F`` even if there are no args; if ``nonbasic`` is True, try to apply ``F`` to non-Basic objects. """ try: if rv.args: args = tuple([bottom_up(a, F, atoms, nonbasic) for a in rv.args]) if args != rv.args: rv = rv.func(*args) rv = F(rv) elif atoms: rv = F(rv) except AttributeError: if nonbasic: try: rv = F(rv) except TypeError: pass return rv 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. Then, functions of half-integer order are rewritten using trigonometric functions and functions of integer order (> 1) are rewritten using functions of low order. Finally, if the expression was changed, compute factorization of the result with factor(). >>> 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)) >>> besselsimp(z*besseli(0, z) + z*(besseli(2, z))/2 + besseli(1, z)) 3*z*besseli(0, z)/2 """ from sympy import besselj, bessely, besseli, besselk, jn, I, pi, Dummy # TODO # - 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 orig_expr = expr 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 exptrigsimp(trigsimp(unpolarify( fro(nu, z0).rewrite(besselj).rewrite(jn).expand( func=True)).subs(z0, z))) elif nu.is_Integer and nu > 1: return fro(nu, z).expand(func=True) return fro(nu, z) return repl expr = expr.replace(besselj, expander(besselj)) expr = expr.replace(bessely, expander(bessely)) expr = expr.replace(besseli, expander(besseli)) expr = expr.replace(besselk, expander(besselk)) if expr != orig_expr: expr = expr.factor() return expr def exptrigsimp(expr, simplify=True): """ Simplifies exponential / trigonometric / hyperbolic functions. When ``simplify`` is True (default) the expression obtained after the simplification step will be then be passed through simplify to precondition it so the final transformations will be applied. Examples ======== >>> from sympy import exptrigsimp, exp, cosh, sinh >>> from sympy.abc import z >>> exptrigsimp(exp(z) + exp(-z)) 2*cosh(z) >>> exptrigsimp(cosh(z) - sinh(z)) exp(-z) """ from sympy.simplify.fu import hyper_as_trig, TR2i def exp_trig(e): # select the better of e, and e rewritten in terms of exp or trig # functions choices = [e] if e.has(*_trigs): choices.append(e.rewrite(exp)) choices.append(e.rewrite(cos)) return min(*choices, key=count_ops) newexpr = bottom_up(expr, exp_trig) if simplify: newexpr = newexpr.simplify() # conversion from exp to hyperbolic ex = newexpr.atoms(exp, S.Exp1) ex = [ei for ei in ex if 1/ei not in ex] ## sinh and cosh for ei in ex: e2 = ei**-2 if e2 in ex: a = e2.args[0]/2 newexpr = newexpr.subs((e2 + 1)*ei, 2*cosh(a)) newexpr = newexpr.subs((e2 - 1)*ei, 2*sinh(a)) ## exp ratios to tan and tanh for ei in ex: n, d = ei - 1, ei + 1 et = n/d etinv = d/n # not 1/et or else recursion errors arise a = ei.args[0] if ei.func is exp else S.One if a.is_Mul or a is S.ImaginaryUnit: c = a.as_coefficient(I) if c: t = S.ImaginaryUnit*tan(c/2) newexpr = newexpr.subs(etinv, 1/t) newexpr = newexpr.subs(et, t) continue t = tanh(a/2) newexpr = newexpr.subs(etinv, 1/t) newexpr = newexpr.subs(et, t) # sin/cos and sinh/cosh ratios to tan and tanh, respectively if newexpr.has(C.HyperbolicFunction): e, f = hyper_as_trig(newexpr) newexpr = f(TR2i(e)) if newexpr.has(C.TrigonometricFunction): newexpr = TR2i(newexpr) # can we ever generate an I where there was none previously? if not (newexpr.has(I) and not expr.has(I)): expr = newexpr return expr def _is_Expr(e): """_eapply helper to tell whether ``e`` and all its args are Exprs.""" if not isinstance(e, Expr): return False return all(_is_Expr(i) for i in e.args) def _eapply(func, e, cond=None): """Apply ``func`` to ``e`` if all args are Exprs else only apply it to those args that *are* Exprs.""" if not isinstance(e, Expr): return e if _is_Expr(e) or not e.args: return func(e) return e.func(*[ _eapply(func, ei) if (cond is None or cond(ei)) else ei for ei in e.args]) def futrig(e, **kwargs): """Return simplified ``e`` using Fu-like transformations. This is not the "Fu" algorithm. This is called by default from ``trigsimp``. By default, hyperbolics subexpressions will be simplified, but this can be disabled by setting ``hyper=False``. Examples ======== >>> from sympy import trigsimp, tan, sinh, tanh >>> from sympy.simplify.simplify import futrig >>> from sympy.abc import x >>> trigsimp(1/tan(x)**2) tan(x)**(-2) >>> futrig(sinh(x)/tanh(x)) cosh(x) """ from sympy.simplify.fu import hyper_as_trig e = sympify(e) if not isinstance(e, Basic): return e if not e.args: return e old = e e = bottom_up(e, lambda x: _futrig(x, **kwargs)) if kwargs.pop('hyper', True) and e.has(C.HyperbolicFunction): e, f = hyper_as_trig(e) e = f(_futrig(e)) if e != old and e.is_Mul and e.args[0].is_Rational: # redistribute leading coeff on 2-arg Add e = Mul(*e.as_coeff_Mul()) return e def _futrig(e, **kwargs): """Helper for futrig.""" from sympy.strategies.tree import greedy from sympy.strategies.core import identity from sympy.simplify.fu import ( TR1, TR2, TR3, TR2i, TR14, TR5, TR10, L, TR10i, TR8, TR6, TR15, TR16, TR111, TR5, TRmorrie, TR11, TR14, TR22, TR12) from sympy.core.compatibility import ordered, _nodes if not e.has(C.TrigonometricFunction): return e if e.is_Mul: coeff, e = e.as_independent(C.TrigonometricFunction) else: coeff = S.One Lops = lambda x: (L(x), x.count_ops(), _nodes(x), len(x.args), x.is_Add) trigs = lambda x: x.has(C.TrigonometricFunction) tree = [identity, ( TR3, # canonical angles TR1, # sec-csc -> cos-sin TR12, # expand tan of sum lambda x: _eapply(factor, x, trigs), TR2, # tan-cot -> sin-cos [identity, lambda x: _eapply(_mexpand, x, trigs)], TR2i, # sin-cos ratio -> tan lambda x: _eapply(lambda i: factor(i.normal()), x, trigs), TR14, # factored identities TR5, # sin-pow -> cos_pow TR10, # sin-cos of sums -> sin-cos prod TR11, TR6, # reduce double angles and rewrite cos pows lambda x: _eapply(factor, x, trigs), TR14, # factored powers of identities [identity, lambda x: _eapply(_mexpand, x, trigs)], TRmorrie, TR10i, # sin-cos products > sin-cos of sums [identity, TR8], # sin-cos products -> sin-cos of sums [identity, lambda x: TR2i(TR2(x))], # tan -> sin-cos -> tan [ lambda x: _eapply(expand_mul, TR5(x), trigs), lambda x: _eapply( expand_mul, TR15(x), trigs)], # pos/neg powers of sin [ lambda x: _eapply(expand_mul, TR6(x), trigs), lambda x: _eapply( expand_mul, TR16(x), trigs)], # pos/neg powers of cos TR111, # tan, sin, cos to neg power -> cot, csc, sec [identity, TR2i], # sin-cos ratio to tan [identity, lambda x: _eapply( expand_mul, TR22(x), trigs)], # tan-cot to sec-csc TR1, TR2, TR2i, [identity, lambda x: _eapply( factor_terms, TR12(x), trigs)], # expand tan of sum )] e = greedy(tree, objective=Lops)(e) return coeff*e def sum_simplify(s): """Main function for Sum simplification""" from sympy.concrete.summations import Sum terms = Add.make_args(s) s_t = [] # Sum Terms o_t = [] # Other Terms for term in terms: if isinstance(term, Mul): constant = 1 other = 1 s = 0 n_sum_terms = 0 for j in range(len(term.args)): if isinstance(term.args[j], Sum): s = term.args[j] n_sum_terms = n_sum_terms + 1 elif term.args[j].is_number == True: constant = constant * term.args[j] else: other = other * term.args[j] if other == 1 and n_sum_terms == 1: # Insert the constant inside the Sum s_t.append(Sum(constant * s.function, *s.limits)) elif other != 1 and n_sum_terms == 1: o_t.append(other * Sum(constant * s.function, *s.limits)) else: o_t.append(term) elif isinstance(term, Sum): s_t.append(term) else: o_t.append(term) used = [False] * len(s_t) for method in range(2): for i, s_term1 in enumerate(s_t): if not used[i]: for j, s_term2 in enumerate(s_t): if not used[j] and i != j: if isinstance(sum_add(s_term1, s_term2, method), Sum): s_t[i] = sum_add(s_term1, s_term2, method) used[j] = True result = Add(*o_t) for i, s_term in enumerate(s_t): if not used[i]: result = Add(result, s_term) return result def sum_add(self, other, method=0): """Helper function for Sum simplification""" from sympy.concrete.summations import Sum if type(self) == type(other): if method == 0: if self.limits == other.limits: return Sum(self.function + other.function, *self.limits) elif method == 1: if simplify(self.function - other.function) == 0: if len(self.limits) == len(other.limits) == 1: i = self.limits[0][0] x1 = self.limits[0][1] y1 = self.limits[0][2] j = other.limits[0][0] x2 = other.limits[0][1] y2 = other.limits[0][2] if i == j: if x2 == y1 + 1: return Sum(self.function, (i, x1, y2)) elif x1 == y2 + 1: return Sum(self.function, (i, x2, y1)) return Add(self, other) def product_simplify(s): """Main function for Product simplification""" from sympy.concrete.products import Product terms = Mul.make_args(s) p_t = [] # Product Terms o_t = [] # Other Terms for term in terms: if isinstance(term, Product): p_t.append(term) else: o_t.append(term) used = [False] * len(p_t) for method in range(2): for i, p_term1 in enumerate(p_t): if not used[i]: for j, p_term2 in enumerate(p_t): if not used[j] and i != j: if isinstance(product_mul(p_term1, p_term2, method), Product): p_t[i] = product_mul(p_term1, p_term2, method) used[j] = True result = Mul(*o_t) for i, p_term in enumerate(p_t): if not used[i]: result = Mul(result, p_term) return result def product_mul(self, other, method=0): """Helper function for Product simplification""" from sympy.concrete.products import Product if type(self) == type(other): if method == 0: if self.limits == other.limits: return Product(self.function * other.function, *self.limits) elif method == 1: if simplify(self.function - other.function) == 0: if len(self.limits) == len(other.limits) == 1: i = self.limits[0][0] x1 = self.limits[0][1] y1 = self.limits[0][2] j = other.limits[0][0] x2 = other.limits[0][1] y2 = other.limits[0][2] if i == j: if x2 == y1 + 1: return Product(self.function, (i, x1, y2)) elif x1 == y2 + 1: return Product(self.function, (i, x2, y1)) return Mul(self, other) #-------------------- the old trigsimp routines --------------------- _trigs = (C.TrigonometricFunction, C.HyperbolicFunction) def trigsimp_old(expr, **opts): """ 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) method: - Determine the method to use. Valid choices are 'matching' (default), 'groebner', 'combined', 'fu' and 'futrig'. If 'matching', simplify the expression recursively by pattern matching. If 'groebner', apply an experimental groebner basis algorithm. In this case further options are forwarded to ``trigsimp_groebner``, please refer to its docstring. If 'combined', first run the groebner basis algorithm with small default parameters, then run the 'matching' algorithm. 'fu' runs the collection of trigonometric transformations described by Fu, et al. (see the `fu` docstring) while `futrig` runs a subset of Fu-transforms that mimic the behavior of `trigsimp`. compare: - show input and output from `trigsimp` and `futrig` when different, but returns the `trigsimp` value. Examples ======== >>> from sympy import trigsimp, sin, cos, log, cosh, sinh, tan, cot >>> from sympy.abc import x, y >>> e = 2*sin(x)**2 + 2*cos(x)**2 >>> trigsimp(e, old=True) 2 >>> trigsimp(log(e), old=True) log(2*sin(x)**2 + 2*cos(x)**2) >>> trigsimp(log(e), deep=True, old=True) log(2) Using `method="groebner"` (or `"combined"`) can sometimes lead to a lot more simplification: >>> e = (-sin(x) + 1)/cos(x) + cos(x)/(-sin(x) + 1) >>> trigsimp(e, old=True) (-sin(x) + 1)/cos(x) - cos(x)/(sin(x) - 1) >>> trigsimp(e, method="groebner", old=True) 2/cos(x) >>> trigsimp(1/cot(x)**2, compare=True, old=True) futrig: tan(x)**2 cot(x)**(-2) """ from sympy import tan from sympy.simplify.fu import fu old = expr first = opts.pop('first', True) if first: if not expr.has(*_trigs): return expr trigsyms = set.union(*[t.free_symbols for t in expr.atoms(*_trigs)]) if len(trigsyms) > 1: d = separatevars(expr) if d.is_Mul: d = separatevars(d, dict=True) or d if isinstance(d, dict): expr = 1 for k, v in d.items(): # remove hollow factoring was = v v = expand_mul(v) opts['first'] = False vnew = trigsimp(v, **opts) if vnew == v: vnew = was expr *= vnew old = expr else: if d.is_Add: for s in trigsyms: r, e = expr.as_independent(s) if r: opts['first'] = False expr = r + trigsimp(e, **opts) if not expr.is_Add: break old = expr recursive = opts.pop('recursive', False) deep = opts.pop('deep', False) method = opts.pop('method', 'matching') def groebnersimp(ex, deep, **opts): def traverse(e): if e.is_Atom: return e args = [traverse(x) for x in e.args] if e.is_Function or e.is_Pow: args = [trigsimp_groebner(x, **opts) for x in args] return e.func(*args) if deep: ex = traverse(ex) return trigsimp_groebner(ex, **opts) trigsimpfunc = { 'matching': (lambda x, d: _trigsimp(x, d)), 'groebner': (lambda x, d: groebnersimp(x, d, **opts)), 'combined': (lambda x, d: _trigsimp(groebnersimp(x, d, polynomial=True, hints=[2, tan]), d)) }[method] if recursive: w, g = cse(expr) g = trigsimpfunc(g[0], deep) for sub in reversed(w): g = g.subs(sub[0], sub[1]) g = trigsimpfunc(g, deep) result = g else: result = trigsimpfunc(expr, deep) if opts.get('compare', False): f = futrig(old) if f != result: print('\tfutrig:', f) return result def _dotrig(a, b): """Helper to tell whether ``a`` and ``b`` have the same sorts of symbols in them -- no need to test hyperbolic patterns against expressions that have no hyperbolics in them.""" return a.func == b.func and ( a.has(C.TrigonometricFunction) and b.has(C.TrigonometricFunction) or a.has(C.HyperbolicFunction) and b.has(C.HyperbolicFunction)) _trigpat = None def _trigpats(): global _trigpat a, b, c = symbols('a b c', cls=Wild) d = Wild('d', commutative=False) # for the simplifications like sinh/cosh -> tanh: # DO NOT REORDER THE FIRST 14 since these are assumed to be in this # order in _match_div_rewrite. matchers_division = ( (a*sin(b)**c/cos(b)**c, a*tan(b)**c, sin(b), cos(b)), (a*tan(b)**c*cos(b)**c, a*sin(b)**c, sin(b), cos(b)), (a*cot(b)**c*sin(b)**c, a*cos(b)**c, sin(b), cos(b)), (a*tan(b)**c/sin(b)**c, a/cos(b)**c, sin(b), cos(b)), (a*cot(b)**c/cos(b)**c, a/sin(b)**c, sin(b), cos(b)), (a*cot(b)**c*tan(b)**c, a, sin(b), cos(b)), (a*(cos(b) + 1)**c*(cos(b) - 1)**c, a*(-sin(b)**2)**c, cos(b) + 1, cos(b) - 1), (a*(sin(b) + 1)**c*(sin(b) - 1)**c, a*(-cos(b)**2)**c, sin(b) + 1, sin(b) - 1), (a*sinh(b)**c/cosh(b)**c, a*tanh(b)**c, S.One, S.One), (a*tanh(b)**c*cosh(b)**c, a*sinh(b)**c, S.One, S.One), (a*coth(b)**c*sinh(b)**c, a*cosh(b)**c, S.One, S.One), (a*tanh(b)**c/sinh(b)**c, a/cosh(b)**c, S.One, S.One), (a*coth(b)**c/cosh(b)**c, a/sinh(b)**c, S.One, S.One), (a*coth(b)**c*tanh(b)**c, a, S.One, S.One), (c*(tanh(a) + tanh(b))/(1 + tanh(a)*tanh(b)), tanh(a + b)*c, S.One, S.One), ) matchers_add = ( (c*sin(a)*cos(b) + c*cos(a)*sin(b) + d, sin(a + b)*c + d), (c*cos(a)*cos(b) - c*sin(a)*sin(b) + d, cos(a + b)*c + d), (c*sin(a)*cos(b) - c*cos(a)*sin(b) + d, sin(a - b)*c + d), (c*cos(a)*cos(b) + c*sin(a)*sin(b) + d, cos(a - b)*c + d), (c*sinh(a)*cosh(b) + c*sinh(b)*cosh(a) + d, sinh(a + b)*c + d), (c*cosh(a)*cosh(b) + c*sinh(a)*sinh(b) + d, cosh(a + b)*c + 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)))), ) # Reduce any lingering artifacts, 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), ) _trigpat = (a, b, c, d, matchers_division, matchers_add, matchers_identity, artifacts) return _trigpat def _replace_mul_fpowxgpow(expr, f, g, rexp, h, rexph): """Helper for _match_div_rewrite. Replace f(b_)**c_*g(b_)**(rexp(c_)) with h(b)**rexph(c) if f(b_) and g(b_) are both positive or if c_ is an integer. """ # assert expr.is_Mul and expr.is_commutative and f != g fargs = defaultdict(int) gargs = defaultdict(int) args = [] for x in expr.args: if x.is_Pow or x.func in (f, g): b, e = x.as_base_exp() if b.is_positive or e.is_integer: if b.func == f: fargs[b.args[0]] += e continue elif b.func == g: gargs[b.args[0]] += e continue args.append(x) common = set(fargs) & set(gargs) hit = False while common: key = common.pop() fe = fargs.pop(key) ge = gargs.pop(key) if fe == rexp(ge): args.append(h(key)**rexph(fe)) hit = True else: fargs[key] = fe gargs[key] = ge if not hit: return expr while fargs: key, e = fargs.popitem() args.append(f(key)**e) while gargs: key, e = gargs.popitem() args.append(g(key)**e) return Mul(*args) _idn = lambda x: x _midn = lambda x: -x _one = lambda x: S.One def _match_div_rewrite(expr, i): """helper for __trigsimp""" if i == 0: expr = _replace_mul_fpowxgpow(expr, sin, cos, _midn, tan, _idn) elif i == 1: expr = _replace_mul_fpowxgpow(expr, tan, cos, _idn, sin, _idn) elif i == 2: expr = _replace_mul_fpowxgpow(expr, cot, sin, _idn, cos, _idn) elif i == 3: expr = _replace_mul_fpowxgpow(expr, tan, sin, _midn, cos, _midn) elif i == 4: expr = _replace_mul_fpowxgpow(expr, cot, cos, _midn, sin, _midn) elif i == 5: expr = _replace_mul_fpowxgpow(expr, cot, tan, _idn, _one, _idn) # i in (6, 7) is skipped elif i == 8: expr = _replace_mul_fpowxgpow(expr, sinh, cosh, _midn, tanh, _idn) elif i == 9: expr = _replace_mul_fpowxgpow(expr, tanh, cosh, _idn, sinh, _idn) elif i == 10: expr = _replace_mul_fpowxgpow(expr, coth, sinh, _idn, cosh, _idn) elif i == 11: expr = _replace_mul_fpowxgpow(expr, tanh, sinh, _midn, cosh, _midn) elif i == 12: expr = _replace_mul_fpowxgpow(expr, coth, cosh, _midn, sinh, _midn) elif i == 13: expr = _replace_mul_fpowxgpow(expr, coth, tanh, _idn, _one, _idn) else: return None return expr def _trigsimp(expr, deep=False): # protect the cache from non-trig patterns; we only allow # trig patterns to enter the cache if expr.has(*_trigs): return __trigsimp(expr, deep) return expr @cacheit def __trigsimp(expr, deep=False): """recursive helper for trigsimp""" from sympy.simplify.fu import TR10i if _trigpat is None: _trigpats() a, b, c, d, matchers_division, matchers_add, \ matchers_identity, artifacts = _trigpat if expr.is_Mul: # do some simplifications like sin/cos -> tan: if not expr.is_commutative: com, nc = expr.args_cnc() expr = _trigsimp(Mul._from_args(com), deep)*Mul._from_args(nc) else: for i, (pattern, simp, ok1, ok2) in enumerate(matchers_division): if not _dotrig(expr, pattern): continue newexpr = _match_div_rewrite(expr, i) if newexpr is not None: if newexpr != expr: expr = newexpr break else: continue # use SymPy matching instead res = expr.match(pattern) if res and res.get(c, 0): if not res[c].is_integer: ok = ok1.subs(res) if not ok.is_positive: continue ok = ok2.subs(res) if not ok.is_positive: continue # if "a" contains any of trig or hyperbolic funcs with # argument "b" then skip the simplification if any(w.args[0] == res[b] for w in res[a].atoms( C.TrigonometricFunction, C.HyperbolicFunction)): continue # simplify and finish: expr = simp.subs(res) break # process below if expr.is_Add: args = [] for term in expr.args: if not term.is_commutative: com, nc = term.args_cnc() nc = Mul._from_args(nc) term = Mul._from_args(com) else: nc = S.One 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*nc) if args != expr.args: expr = Add(*args) expr = min(expr, expand(expr), key=count_ops) if expr.is_Add: for pattern, result in matchers_add: if not _dotrig(expr, pattern): continue expr = TR10i(expr) if expr.has(C.HyperbolicFunction): res = expr.match(pattern) # if "d" contains any trig or hyperbolic funcs with # argument "a" or "b" then skip the simplification; # this isn't perfect -- see tests if res is None or not (a in res and b in res) or any( w.args[0] in (res[a], res[b]) for w in res[d].atoms( C.TrigonometricFunction, C.HyperbolicFunction)): continue expr = result.subs(res) break # 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 not _dotrig(expr, pattern): continue # 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) was = None while m and was != expr: was = expr if m[a_t] == 0 or \ -m[a_t] in m[c].args or m[a_t] + m[c] == 0: break if d in m and m[a_t]*m[d] + m[c] == 0: break expr = result.subs(m) m = expr.match(pattern) m.setdefault(c, S.Zero) elif expr.is_Mul or expr.is_Pow or deep and expr.args: expr = expr.func(*[_trigsimp(a, deep) for a in expr.args]) try: if not expr.has(*_trigs): raise TypeError e = expr.atoms(exp) new = expr.rewrite(exp, deep=deep) if new == e: raise TypeError fnew = factor(new) if fnew != new: new = sorted([new, factor(new)], key=count_ops)[0] # if all exp that were introduced disappeared then accept it if not (new.atoms(exp) - e): expr = new except TypeError: pass return expr #------------------- end of old trigsimp routines -------------------- sympy-0.7.4.1/sympy/simplify/hyperexpand_doc.py0000644000175000017500000000074112253362407022011 0ustar georgeskgeorgesk""" This module cooks up a docstring when imported. Its only purpose is to be displayed in the sphinx documentation. """ from __future__ import print_function, division from sympy.simplify.hyperexpand import FormulaCollection from sympy import latex, Eq, hyper c = FormulaCollection() doc = "" for f in c.formulae: obj = Eq(hyper(f.func.ap, f.func.bq, f.z), f.closed_form.rewrite('nonrepsmall')) doc += ".. math::\n %s\n" % latex(obj) __doc__ = doc sympy-0.7.4.1/sympy/simplify/sqrtdenest.py0000644000175000017500000004736012253362407021041 0ustar georgeskgeorgeskfrom __future__ import print_function, division 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], key=default_sort_key) 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 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, key=default_sort_key) 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 = 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 . 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 = list(filter(None, [_sqrt_match(expr) for expr in nested])) 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 sympy-0.7.4.1/sympy/simplify/hyperexpand.py0000644000175000017500000024141712253362407021173 0ustar georgeskgeorgesk""" 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 __future__ import print_function, division from collections import defaultdict from itertools import product from sympy import SYMPY_DEBUG from sympy.core import (S, Dummy, symbols, sympify, Tuple, expand, I, pi, Mul, EulerGamma, oo, zoo, expand_func, Add, nan, Expr) from sympy.core.mod import Mod from sympy.core.compatibility import default_sort_key, xrange from sympy.utilities.iterables import sift from sympy.functions import (exp, sqrt, root, log, lowergamma, cos, besseli, gamma, uppergamma, expint, erf, sin, besselj, Ei, Ci, Si, Shi, sinh, cosh, Chi, fresnels, fresnelc, polar_lift, exp_polar, floor, ceiling, rf, factorial, lerchphi, Piecewise, re, elliptic_k, elliptic_e) from sympy.functions.special.hyper import (hyper, HyperRep_atanh, HyperRep_power1, HyperRep_power2, HyperRep_log1, HyperRep_asin1, HyperRep_asin2, HyperRep_sqrts1, HyperRep_sqrts2, HyperRep_log2, HyperRep_cosasin, HyperRep_sinasin, meijerg) from sympy.simplify import powdenest, simplify, polarify, unpolarify from sympy.polys import poly, Poly from sympy.series import residue # leave add formulae at the top for easy reference def add_formulae(formulae): """ Create our knowledge base. """ from sympy.matrices import Matrix a, b, c, z = symbols('a b c, z', cls=Dummy) def add(ap, bq, res): func = Hyper_Function(ap, bq) formulae.append(Formula(func, z, res, (a, b, c))) def addb(ap, bq, B, C, M): func = Hyper_Function(ap, bq) formulae.append(Formula(func, z, None, (a, b, c), B, C, M)) # Luke, Y. L. (1969), The Special Functions and Their Approximations, # Volume 1, section 6.2 # 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]])) # Complete elliptic integrals K(z) and E(z), both a 2F1 function #add((S.Half, S.Half), (S.One, ), 2*elliptic_k(z)/pi) #add((-S.Half, S.Half), (S.One, ), 2*elliptic_e(z)/pi) addb([S.Half, S.Half], [S.One], Matrix([elliptic_k(z), elliptic_e(z)]), Matrix([[2/pi, 0]]), Matrix([[-S.Half, -1/(2*z-2)], [-S.Half, S.Half]])) addb([-S.Half, S.Half], [S.One], Matrix([elliptic_k(z), elliptic_e(z)]), Matrix([[0, 2/pi]]), Matrix([[-S.Half, -1/(2*z-2)], [-S.Half, S.Half]])) # 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]])) # 3F3 # This is rule: http://functions.wolfram.com/07.31.03.0134.01 # Initial reason to add it was a nice solution for # integrate(erf(a*z)/z**2, z) and same for erfc and erfi. # Basic rule # add([1, 1, a], [2, 2, a+1], (a/(z*(a-1)**2)) * # (1 - (-z)**(1-a) * (gamma(a) - uppergamma(a,-z)) # - (a-1) * (EulerGamma + uppergamma(0,-z) + log(-z)) # - exp(z))) # Manually tuned rule addb([1, 1, a], [2, 2, a+1], Matrix([a*(log(-z) + expint(1, -z) + EulerGamma)/(z*(a**2 - 2*a + 1)), a*(-z)**(-a)*(gamma(a) - uppergamma(a, -z))/(a - 1)**2, a*exp(z)/(a**2 - 2*a + 1), a/(z*(a**2 - 2*a + 1))]), Matrix([[1-a, 1, -1/z, 1]]), Matrix([[-1,0,-1/z,1], [0,-a,1,0], [0,0,z,0], [0,0,0,-1]])) def add_meijerg_formulae(formulae): from sympy.matrices import Matrix 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(func): x = func.an[0] y, z = func.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}, G_Function([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(func): """http://functions.wolfram.com/07.34.03.0984.01""" x = func.an[0] u, v, w = func.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}, G_Function([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``. """ 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() _mod1 = lambda x: Mod(x, 1) class Hyper_Function(Expr): """ A generalized hypergeometric function. """ def __new__(cls, ap, bq): obj = super(Hyper_Function, cls).__new__(cls) obj.ap = Tuple(*list(map(expand, ap))) obj.bq = Tuple(*list(map(expand, bq))) return obj @property def args(self): return (self.ap, self.bq) @property def sizes(self): return (len(self.ap), len(self.bq)) @property def gamma(self): """ Number of upper parameters that are negative integers This is a transformation invariant. """ return sum(bool(x.is_integer and x.is_negative) for x in self.ap) def _hashable_content(self): return super(Hyper_Function, self)._hashable_content() + (self.ap, self.bq) def __call__(self, arg): return hyper(self.ap, self.bq, arg) def build_invariants(self): """ Compute the invariant vector. 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 Hyper_Function >>> 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: >>> Hyper_Function(ap, bq).build_invariants() (1, ((0, 1), (1/3, 1), (1/2, 2)), ((0, 2),)) """ abuckets, bbuckets = sift(self.ap, _mod1), sift(self.bq, _mod1) def tr(bucket): bucket = list(bucket.items()) if not any(isinstance(x[0], Mod) for x in bucket): bucket.sort(key=lambda x: default_sort_key(x[0])) bucket = tuple([(mod, len(values)) for mod, values in bucket if values]) return bucket return (self.gamma, tr(abuckets), tr(bbuckets)) def difficulty(self, func): """ Estimate how many steps it takes to reach ``func`` from self. Return -1 if impossible. """ if self.gamma != func.gamma: return -1 oabuckets, obbuckets, abuckets, bbuckets = [sift(params, _mod1) for params in (self.ap, self.bq, func.ap, func.bq)] 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 def _is_suitable_origin(self): """ Decide if ``self`` is a suitable origin. A function is a suitable origin iff: * none of the ai equals bj + n, with n a non-negative integer * none of the ai is zero * none of the bj is a non-positive integer Note that this gives meaningful results only when none of the indices are symbolic. """ for a in self.ap: for b in self.bq: if (a - b).is_integer and (a < b) is False: return False for a in self.ap: if a == 0: return False for b in self.bq: if b.is_integer and b.is_nonpositive: return False return True class G_Function(Expr): """ A Meijer G-function. """ def __new__(cls, an, ap, bm, bq): obj = super(G_Function, cls).__new__(cls) obj.an = Tuple(*list(map(expand, an))) obj.ap = Tuple(*list(map(expand, ap))) obj.bm = Tuple(*list(map(expand, bm))) obj.bq = Tuple(*list(map(expand, bq))) return obj @property def args(self): return (self.an, self.ap, self.bm, self.bq) def _hashable_content(self): return super(G_Function, self)._hashable_content() + self.args def __call__(self, z): return meijerg(self.an, self.ap, self.bm, self.bq, z) 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 G_Function >>> from sympy.abc import y >>> from sympy import S >>> a, b = [1, 3, 2, S(3)/2], [1 + y, y, 2, y + 3] >>> G_Function(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]}) """ 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)) # 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 - func, the function - B, C, M (see _compute_basis) >>> from sympy.abc import a, b, z >>> from sympy.simplify.hyperexpand import Formula, Hyper_Function >>> func = Hyper_Function((a/2, a/3 + b, (1+a)/2), (a, b, (a+b)/7)) >>> f = Formula(func, z, None, [a, b]) """ 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.func.ap] bfactors = [_x + b - 1 for b in self.func.bq] expr = _x*Mul(*bfactors) - self.z*Mul(*afactors) poly = Poly(expr, _x) n = poly.degree() - 1 b = [closed_form] for _ in xrange(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, func, z, res, symbols, B=None, C=None, M=None): z = sympify(z) res = sympify(res) symbols = [x for x in sympify(symbols) if func.has(x)] self.z = z self.symbols = symbols self.B = B self.C = C self.M = M self.func = func # 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, func): """ Find substitutions of the free symbols that match ``func``. Return the substitution dictionaries as a list. Note that the returned instantiations need not actually match, or be valid! """ from sympy.solvers import solve ap = func.ap bq = func.bq if len(ap) != len(self.func.ap) or len(bq) != len(self.func.bq): raise TypeError('Cannot instantiate other number of parameters') symbol_values = [] for a in self.symbols: if a in self.func.ap.args: symbol_values.append(ap) elif a in self.func.bq.args: symbol_values.append(bq) else: raise ValueError("At least one of the parameters of the " "formula must be equal to %s" % (a,)) base_repl = [dict(list(zip(self.symbols, values))) for values in product(*symbol_values)] abuckets, bbuckets = [sift(params, _mod1) for params in [ap, bq]] a_inv, b_inv = [dict((a, len(vals)) for a, vals in bucket.items()) for bucket in [abuckets, bbuckets]] critical_values = [[0] for _ in self.symbols] result = [] _n = Dummy() for repl in base_repl: symb_a, symb_b = [sift(params, lambda x: _mod1(x.xreplace(repl))) for params in [self.func.ap, self.func.bq]] for bucket, obucket in [(abuckets, symb_a), (bbuckets, symb_b)]: 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]): break for a, vals in zip(self.symbols, critical_values): if repl[a].free_symbols: continue exprs = [expr for expr in obucket[mod] if expr.has(a)] repl0 = repl.copy() repl0[a] += _n for expr in exprs: for target in bucket[mod]: n0, = solve(expr.xreplace(repl0) - target, _n) assert not n0.free_symbols vals.append(n0) else: values = [] for a, vals in zip(self.symbols, critical_values): a0 = repl[a] min_ = floor(min(vals)) max_ = ceiling(max(vals)) values.append([a0 + n for n in range(min_, max_ + 1)]) result.extend(dict(list(zip(self.symbols, l))) for l in product(*values)) return result 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.func.sizes if len(f.symbols) > 0: self.symbolic_formulae.setdefault(sizes, []).append(f) else: inv = f.func.build_invariants() self.concrete_formulae.setdefault(sizes, {})[inv] = f def lookup_origin(self, func): """ Given the suitable target ``func``, try to find an origin in our knowledge base. >>> from sympy.simplify.hyperexpand import (FormulaCollection, ... Hyper_Function) >>> f = FormulaCollection() >>> f.lookup_origin(Hyper_Function((), ())).closed_form exp(_z) >>> f.lookup_origin(Hyper_Function([1], ())).closed_form HyperRep_power1(-1, _z) >>> from sympy import S >>> i = Hyper_Function([S('1/4'), S('3/4 + 4')], [S.Half]) >>> f.lookup_origin(i).closed_form HyperRep_sqrts1(-1/4, _z) """ inv = func.build_invariants() sizes = func.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]: repls = f.find_instantiations(func) for repl in repls: func2 = f.func.xreplace(repl) if not func2._is_suitable_origin(): continue diff = func2.difficulty(func) if diff == -1: continue possible.append((diff, repl, f, func2)) # find the nearest origin possible.sort(key=lambda x: x[0]) for _, repl, f, func2 in possible: f2 = Formula(func2, f.z, None, [], f.B.subs(repl), f.C.subs(repl), f.M.subs(repl)) if not any(e.has(S.NaN, oo, -oo, zoo) for e in [f2.B, f2.M, f2.C]): return f2 else: return None 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 - func, the function - 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.func = G_Function(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, func): """ Try to instantiate the current formula to (almost) match func. This uses the _matcher passed on init. """ if func.signature != self.func.signature: return None res = self._matcher(func) if res is not None: subs, newfunc = res return MeijerFormula(newfunc.an, newfunc.ap, newfunc.bm, newfunc.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): formulae = [] add_meijerg_formulae(formulae) self.formulae = defaultdict(list) for formula in formulae: self.formulae[formula.func.signature].append(formula) self.formulae = dict(self.formulae) def lookup_origin(self, func): """ Try to find a formula that matches func. """ if not func.signature in self.formulae: return None for formula in self.formulae[func.signature]: res = formula.try_instantiate(func) 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 '' % (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 '' % (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 '' % (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 '' % (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 '' % (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 '' % (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 '' % (-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 '' % (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 '' % (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 '' % (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 '' % (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 '' % (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 not n.is_Integer or n < 0: 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 xrange(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. """ b = sympify(b) a = sympify(a) n = b - a if n.is_negative or not n.is_Integer: return None self = Operator.__new__(cls) p = S(1) for k in xrange(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 '' % \ (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 xrange(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(func): """ Given the hypergeometric function ``func``, find a sequence of operators to reduces order as much as possible. Return (newfunc, [operators]), where applying the operators to the hypergeometric function newfunc yields func. Examples ======== >>> from sympy.simplify.hyperexpand import reduce_order, Hyper_Function >>> reduce_order(Hyper_Function((1, 2), (3, 4))) (Hyper_Function((1, 2), (3, 4)), []) >>> reduce_order(Hyper_Function((1,), (1,))) (Hyper_Function((), ()), []) >>> reduce_order(Hyper_Function((2, 4), (3, 3))) (Hyper_Function((2,), (3,)), []) """ nap, nbq, operators = _reduce_order(func.ap, func.bq, ReduceOrder, default_sort_key) return Hyper_Function(Tuple(*nap), Tuple(*nbq)), operators def reduce_order_meijer(func): """ Given the Meijer G function parameters, ``func``, find a sequence of operators that reduces order as much as possible. Return newfunc, [operators]. Examples ======== >>> from sympy.simplify.hyperexpand import (reduce_order_meijer, ... G_Function) >>> reduce_order_meijer(G_Function([3, 4], [5, 6], [3, 4], [1, 2]))[0] G_Function((4, 3), (5, 6), (3, 4), (2, 1)) >>> reduce_order_meijer(G_Function([3, 4], [5, 6], [3, 4], [1, 8]))[0] G_Function((3,), (5, 6), (3, 4), (1,)) >>> reduce_order_meijer(G_Function([3, 4], [5, 6], [7, 5], [1, 5]))[0] G_Function((3,), (), (), (1,)) >>> reduce_order_meijer(G_Function([3, 4], [5, 6], [7, 5], [5, 3]))[0] G_Function((), (), (), ()) """ nan, nbq, ops1 = _reduce_order(func.an, func.bq, ReduceOrder.meijer_plus, lambda x: default_sort_key(-x)) nbm, nap, ops2 = _reduce_order(func.bm, func.ap, ReduceOrder.meijer_minus, default_sort_key) return G_Function(nan, nap, nbm, nbq), ops1 + ops2 def make_derivative_operator(M, z): """ Create a derivative operator, to be passed to Operator.apply. """ 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(target, origin, z): """ Devise a plan (consisting of shift and un-shift operators) to be applied to the hypergeometric function ``target`` to yield ``origin``. Returns a list of operators. >>> from sympy.simplify.hyperexpand import devise_plan, Hyper_Function >>> from sympy.abc import z Nothing to do: >>> devise_plan(Hyper_Function((1, 2), ()), Hyper_Function((1, 2), ()), z) [] >>> devise_plan(Hyper_Function((), (1, 2)), Hyper_Function((), (1, 2)), z) [] Very simple plans: >>> devise_plan(Hyper_Function((2,), ()), Hyper_Function((1,), ()), z) [] >>> devise_plan(Hyper_Function((), (2,)), Hyper_Function((), (1,)), z) [] Several buckets: >>> from sympy import S >>> devise_plan(Hyper_Function((1, S.Half), ()), ... Hyper_Function((2, S('3/2')), ()), z) #doctest: +NORMALIZE_WHITESPACE [, ] A slightly more complicated plan: >>> devise_plan(Hyper_Function((1, 3), ()), Hyper_Function((2, 2), ()), z) [, ] Another more complicated plan: (note that the ap have to be shifted first!) >>> devise_plan(Hyper_Function((1, -1), (2,)), Hyper_Function((3, -2), (4,)), z) [, , , , ] """ abuckets, bbuckets, nabuckets, nbbuckets = [sift(params, _mod1) for params in (target.ap, target.bq, origin.ap, origin.bq)] 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' % (target, origin)) ops = [] def do_shifts(fro, to, inc, dec): ops = [] for i in xrange(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' % (target, origin)) 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(func, z): """ Try to recognise a hypergeometric sum that starts from k > 0. """ abuckets, bbuckets = sift(func.ap, _mod1), sift(func.bq, _mod1) if 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(func.ap) nap.remove(r) nbq = list(func.bq) nbq.remove(k) k -= 1 nap = [x - k for x in nap] nbq = [x - k for x in nbq] ops = [] for n in xrange(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 xrange(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 Hyper_Function(nap, nbq), ops, -p def try_polynomial(func, z): """ Recognise polynomial cases. Returns None if not such a case. Requires order to be fully reduced. """ abuckets, bbuckets = sift(func.ap, _mod1), sift(func.bq, _mod1) a0 = abuckets[S(0)] b0 = bbuckets[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 func.ap: fac *= a + n for b in func.bq: fac /= b + n res += fac return res def try_lerchphi(func): """ Try to find an expression for Hyper_Function ``func`` 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.matrices import Matrix, zeros from sympy.polys import apart # 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 = sift(func.ap, _mod1), sift(func.bq, _mod1) paired = {} for key, value in abuckets.items(): if key != 0 and not key in bbuckets: return None bvalue = bbuckets[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 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 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 coeffs.items(): C[trans[b]] = Add(*c) M = zeros(len(B)) for b, l in deriv.items(): for c, b2 in l: M[trans[b], trans[b2]] = c return Formula(func, z, None, [], B, C, M) def build_hypergeometric_formula(func): """ Create a formula object representing the hypergeometric function ``func``. """ # 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, Matrix, eye z = Dummy('z') if func.ap: afactors = [_x + a for a in func.ap] bfactors = [_x + b - 1 for b in func.bq] expr = _x*Mul(*bfactors) - z*Mul(*afactors) poly = Poly(expr, _x) n = poly.degree() basis = [] M = zeros(n) for k in xrange(n): a = func.ap[0] + k basis += [hyper([a] + list(func.ap[1:]), func.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 xrange(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(func, z, None, [], B, C, M) else: # Since there are no `ap`, none of the `bq` can be non-positive # integers. basis = [] bq = list(func.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(*func.bq) for k in range(1, n): M[k, k - 1] = func.bq[k - 1] M[k, k] = -func.bq[k - 1] return Formula(func, 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. p, q = len(ap), len(bq) z_ = z z = unpolarify(z) if z == 0: return S.One 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 def _hyperexpand(func, z, ops0=[], z0=Dummy('z0'), premult=1, prem=0, rewrite='default'): """ Try to find an expression for the hypergeometric function ``func``. 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. """ if z is S.Zero: return S.One 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 ', func) # First reduce order as much as possible. func, ops = reduce_order(func) if ops: debug(' Reduced order to', func) else: debug(' Could not reduce order.') # Now try polynomial cases res = try_polynomial(func, 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(func, z0) if res is not None: func, nops, p = res debug(' Recognised shifted sum, reduced order to', func) 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(func.ap), len(func.bq)) == (2, 1): f = build_hypergeometric_formula(func) 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 formula = _collection.lookup_origin(func) # Now try a lerch phi formula if formula is None: formula = try_lerchphi(func) if formula is None: debug(' Could not find an origin.', 'Will return answer in terms of ' 'simpler hypergeometric functions.') formula = build_hypergeometric_formula(func) debug(' Found an origin:', formula.closed_form, formula.func) # We need to find the operators that convert formula into func. ops += devise_plan(func, formula.func, z0) # Now carry out the plan. r = carryout_plan(formula, ops) + p return powdenest(r, polar=True).replace(hyper, hyperexpand_special) def devise_plan_meijer(fro, to, z): """ Find operators to convert G-function ``fro`` into G-function ``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, ... G_Function) >>> from sympy.abc import z Empty plan: >>> devise_plan_meijer(G_Function([1], [2], [3], [4]), ... G_Function([1], [2], [3], [4]), z) [] Very simple plans: >>> devise_plan_meijer(G_Function([0], [], [], []), ... G_Function([1], [], [], []), z) [] >>> devise_plan_meijer(G_Function([0], [], [], []), ... G_Function([-1], [], [], []), z) [] >>> devise_plan_meijer(G_Function([], [1], [], []), ... G_Function([], [2], [], []), z) [] Slightly more complicated plans: >>> devise_plan_meijer(G_Function([0], [], [], []), ... G_Function([2], [], [], []), z) [, ] >>> devise_plan_meijer(G_Function([0], [], [0], []), ... G_Function([-1], [], [1], []), z) [, ] Order matters: >>> devise_plan_meijer(G_Function([0], [], [0], []), ... G_Function([1], [], [1], []), z) [, ] """ # 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(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 def _meijergexpand(func, z0, allow_hyper=False, rewrite='default'): """ Try to find an expression for the Meijer G function specified by the G_Function ``func``. If ``allow_hyper`` is True, then returning an expression in terms of hypergeometric functions is allowed. Currently this just does slater's theorem. """ global _meijercollection if _meijercollection is None: _meijercollection = MeijerFormulaCollection() if rewrite == 'default': rewrite = None func0 = func debug('Try to expand meijer G function corresponding to', func) # We will play games with analytic continuation - rather use a fresh symbol z = Dummy('z') func, ops = reduce_order_meijer(func) if ops: debug(' Reduced order to', func) else: debug(' Could not reduce order.') # Try to find a direct formula f = _meijercollection.lookup_origin(func) if f is not None: debug(' Found a Meijer G formula:', f.func) ops += devise_plan_meijer(f.func, func, 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): # zfinal is the value that will eventually be substituted for z. # We pass it to _hyperexpand to improve performance. func = G_Function(an, bm, ap, bq) _, pbm, pap, _ = func.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(Hyper_Function(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(Hyper_Function(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(func.an, func.bm, func.ap, func.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(func.bm), tr(func.an), tr(func.bq), tr(func.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 = func(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): 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), (func0(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), (func0(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 func0(z0) 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 """ f = sympify(f) def do_replace(ap, bq, z): r = _hyperexpand(Hyper_Function(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(G_Function(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) sympy-0.7.4.1/sympy/simplify/tests/0000755000175000017500000000000012253362407017423 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/simplify/tests/test_epathtools.py0000644000175000017500000000660412253362407023224 0ustar georgeskgeorgesk"""Tests for tools for manipulation of expressions using paths. """ from sympy.simplify.epathtools import epath, EPath from sympy.utilities.pytest import raises from sympy import sin, cos, E from sympy.abc import x, y, z, t def test_epath_select(): expr = [((x, 1, t), 2), ((3, y, 4), z)] assert epath("/*", expr) == [((x, 1, t), 2), ((3, y, 4), z)] assert epath("/*/*", expr) == [(x, 1, t), 2, (3, y, 4), z] assert epath("/*/*/*", expr) == [x, 1, t, 3, y, 4] assert epath("/*/*/*/*", expr) == [] assert epath("/[:]", expr) == [((x, 1, t), 2), ((3, y, 4), z)] assert epath("/[:]/[:]", expr) == [(x, 1, t), 2, (3, y, 4), z] assert epath("/[:]/[:]/[:]", expr) == [x, 1, t, 3, y, 4] assert epath("/[:]/[:]/[:]/[:]", expr) == [] assert epath("/*/[:]", expr) == [(x, 1, t), 2, (3, y, 4), z] assert epath("/*/[0]", expr) == [(x, 1, t), (3, y, 4)] assert epath("/*/[1]", expr) == [2, z] assert epath("/*/[2]", expr) == [] assert epath("/*/int", expr) == [2] assert epath("/*/Symbol", expr) == [z] assert epath("/*/tuple", expr) == [(x, 1, t), (3, y, 4)] assert epath("/*/__iter__?", expr) == [(x, 1, t), (3, y, 4)] assert epath("/*/int|tuple", expr) == [(x, 1, t), 2, (3, y, 4)] assert epath("/*/Symbol|tuple", expr) == [(x, 1, t), (3, y, 4), z] assert epath("/*/int|Symbol|tuple", expr) == [(x, 1, t), 2, (3, y, 4), z] assert epath("/*/int|__iter__?", expr) == [(x, 1, t), 2, (3, y, 4)] assert epath("/*/Symbol|__iter__?", expr) == [(x, 1, t), (3, y, 4), z] assert epath( "/*/int|Symbol|__iter__?", expr) == [(x, 1, t), 2, (3, y, 4), z] assert epath("/*/[0]/int", expr) == [1, 3, 4] assert epath("/*/[0]/Symbol", expr) == [x, t, y] assert epath("/*/[0]/int[1:]", expr) == [1, 4] assert epath("/*/[0]/Symbol[1:]", expr) == [t, y] assert epath("/Symbol", x + y + z + 1) == [x, y, z] assert epath("/*/*/Symbol", t + sin(x + 1) + cos(x + y + E)) == [x, x, y] def test_epath_apply(): expr = [((x, 1, t), 2), ((3, y, 4), z)] func = lambda expr: expr**2 assert epath("/*", expr, list) == [[(x, 1, t), 2], [(3, y, 4), z]] assert epath("/*/[0]", expr, list) == [([x, 1, t], 2), ([3, y, 4], z)] assert epath("/*/[1]", expr, func) == [((x, 1, t), 4), ((3, y, 4), z**2)] assert epath("/*/[2]", expr, list) == expr assert epath("/*/[0]/int", expr, func) == [((x, 1, t), 2), ((9, y, 16), z)] assert epath("/*/[0]/Symbol", expr, func) == [((x**2, 1, t**2), 2), ((3, y**2, 4), z)] assert epath( "/*/[0]/int[1:]", expr, func) == [((x, 1, t), 2), ((3, y, 16), z)] assert epath("/*/[0]/Symbol[1:]", expr, func) == [((x, 1, t**2), 2), ((3, y**2, 4), z)] assert epath("/Symbol", x + y + z + 1, func) == x**2 + y**2 + z**2 + 1 assert epath("/*/*/Symbol", t + sin(x + 1) + cos(x + y + E), func) == \ t + sin(x**2 + 1) + cos(x**2 + y**2 + E) def test_EPath(): assert EPath("/*/[0]")._path == "/*/[0]" assert EPath(EPath("/*/[0]"))._path == "/*/[0]" assert isinstance(epath("/*/[0]"), EPath) is True assert repr(EPath("/*/[0]")) == "EPath('/*/[0]')" raises(ValueError, lambda: EPath("")) raises(ValueError, lambda: EPath("/")) raises(ValueError, lambda: EPath("/|x")) raises(ValueError, lambda: EPath("/[")) raises(ValueError, lambda: EPath("/[0]%")) raises(NotImplementedError, lambda: EPath("Symbol")) sympy-0.7.4.1/sympy/simplify/tests/test_function.py0000644000175000017500000000414512253362407022665 0ustar georgeskgeorgesk""" Unit tests for Hyper_Function""" from sympy.core import symbols, Dummy, Tuple, S from sympy.functions import hyper from sympy.simplify.hyperexpand import Hyper_Function def test_attrs(): a, b = symbols('a, b', cls=Dummy) f = Hyper_Function([2, a], [b]) assert f.ap == Tuple(2, a) assert f.bq == Tuple(b) assert f.args == (Tuple(2, a), Tuple(b)) assert f.sizes == (2, 1) def test_call(): a, b, x = symbols('a, b, x', cls=Dummy) f = Hyper_Function([2, a], [b]) assert f(x) == hyper([2, a], [b], x) def test_has(): a, b, c = symbols('a, b, c', cls=Dummy) f = Hyper_Function([2, -a], [b]) assert f.has(a) assert f.has(Tuple(b)) assert not f.has(c) def test_eq(): assert Hyper_Function([1], []) == Hyper_Function([1], []) assert (Hyper_Function([1], []) != Hyper_Function([1], [])) is False assert Hyper_Function([1], []) != Hyper_Function([2], []) assert Hyper_Function([1], []) != Hyper_Function([1, 2], []) assert Hyper_Function([1], []) != Hyper_Function([1], [2]) def test_gamma(): assert Hyper_Function([2, 3], [-1]).gamma == 0 assert Hyper_Function([-2, -3], [-1]).gamma == 2 n = Dummy(integer=True) assert Hyper_Function([-1, n, 1], []).gamma == 1 assert Hyper_Function([-1, -n, 1], []).gamma == 1 p = Dummy(integer=True, positive=True) assert Hyper_Function([-1, p, 1], []).gamma == 1 assert Hyper_Function([-1, -p, 1], []).gamma == 2 def test_suitable_origin(): assert Hyper_Function((S(1)/2,), (S(3)/2,))._is_suitable_origin() is True assert Hyper_Function((S(1)/2,), (S(1)/2,))._is_suitable_origin() is False assert Hyper_Function((S(1)/2,), (-S(1)/2,))._is_suitable_origin() is False assert Hyper_Function((S(1)/2,), (0,))._is_suitable_origin() is False assert Hyper_Function((S(1)/2,), (-1, 1,))._is_suitable_origin() is False assert Hyper_Function((S(1)/2, 0), (1,))._is_suitable_origin() is False assert Hyper_Function((S(1)/2, 1), (2, -S(2)/3))._is_suitable_origin() is True assert Hyper_Function((S(1)/2, 1), (2, -S(2)/3, S(3)/2))._is_suitable_origin() is True sympy-0.7.4.1/sympy/simplify/tests/test_rewrite.py0000644000175000017500000000172012253362407022515 0ustar georgeskgeorgeskfrom sympy import (sin, cos, exp, cot, sqrt, S, I, E, pi, symbols, Function, Matrix, Eq, RootSum, Lambda) from sympy.integrals import integrate x, y, z, n = symbols('x,y,z,n') def test_has(): assert cot(x).has(x) assert cot(x).has(cot) assert not cot(x).has(sin) assert sin(x).has(x) assert sin(x).has(sin) assert not sin(x).has(cot) def test_sin_exp_rewrite(): assert sin(x).rewrite(sin, exp) == -I/2*(exp(I*x) - exp(-I*x)) assert sin(x).rewrite(sin, exp).rewrite(exp, sin) == sin(x) assert cos(x).rewrite(cos, exp).rewrite(exp, cos) == cos(x) assert (sin(5*y) - sin( 2*x)).rewrite(sin, exp).rewrite(exp, sin) == sin(5*y) - sin(2*x) assert sin(x + y).rewrite(sin, exp).rewrite(exp, sin) == sin(x + y) assert cos(x + y).rewrite(cos, exp).rewrite(exp, cos) == cos(x + y) # This next test currently passes... not clear whether it should or not? assert cos(x).rewrite(cos, exp).rewrite(exp, sin) == cos(x) sympy-0.7.4.1/sympy/simplify/tests/test_hyperexpand.py0000644000175000017500000011073612253362407023373 0ustar georgeskgeorgeskfrom random import randrange from sympy.simplify.hyperexpand import (ShiftA, ShiftB, UnShiftA, UnShiftB, MeijerShiftA, MeijerShiftB, MeijerShiftC, MeijerShiftD, MeijerUnShiftA, MeijerUnShiftB, MeijerUnShiftC, MeijerUnShiftD, ReduceOrder, reduce_order, apply_operators, devise_plan, make_derivative_operator, Formula, hyperexpand, Hyper_Function, G_Function, reduce_order_meijer, build_hypergeometric_formula) from sympy import hyper, I, S, meijerg, Piecewise, exp_polar from sympy.utilities.pytest import raises from sympy.abc import z, a, b, c from sympy.utilities.randtest import test_numerically as tn from sympy.utilities.pytest import XFAIL, skip, slow from sympy import (cos, sin, log, exp, asin, lowergamma, atanh, besseli, gamma, sqrt, pi, erf, exp_polar) def test_branch_bug(): assert hyperexpand(hyper((-S(1)/3, S(1)/2), (S(2)/3, S(3)/2), -z)) == \ -z**S('1/3')*lowergamma(exp_polar(I*pi)/3, z)/5 \ + sqrt(pi)*erf(sqrt(z))/(5*sqrt(z)) assert hyperexpand(meijerg([S(7)/6, 1], [], [S(2)/3], [S(1)/6, 0], z)) == \ 2*z**S('2/3')*(2*sqrt(pi)*erf(sqrt(z))/sqrt(z) - 2*lowergamma( S(2)/3, z)/z**S('2/3'))*gamma(S(2)/3)/gamma(S(5)/3) def test_hyperexpand(): # Luke, Y. L. (1969), The Special Functions and Their Approximations, # Volume 1, section 6.2 assert hyperexpand(hyper([], [], z)) == exp(z) assert hyperexpand(hyper([1, 1], [2], -z)*z) == log(1 + z) assert hyperexpand(hyper([], [S.Half], -z**2/4)) == cos(z) assert hyperexpand(z*hyper([], [S('3/2')], -z**2/4)) == sin(z) assert hyperexpand(hyper([S('1/2'), S('1/2')], [S('3/2')], z**2)*z) \ == asin(z) def can_do(ap, bq, numerical=True, div=1, lowerplane=False): from sympy import exp_polar, exp r = hyperexpand(hyper(ap, bq, z)) if r.has(hyper): return False if not numerical: return True repl = {} for n, a in enumerate(r.free_symbols - set([z])): repl[a] = randcplx(n)/div [a, b, c, d] = [2, -1, 3, 1] if lowerplane: [a, b, c, d] = [2, -2, 3, -1] return tn( hyper(ap, bq, z).subs(repl), r.replace(exp_polar, exp).subs(repl), z, a=a, b=b, c=c, d=d) def test_roach(): # Kelly B. Roach. Meijer G Function Representations. # Section "Gallery" assert can_do([S(1)/2], [S(9)/2]) assert can_do([], [1, S(5)/2, 4]) assert can_do([-S.Half, 1, 2], [3, 4]) assert can_do([S(1)/3], [-S(2)/3, -S(1)/2, S(1)/2, 1]) assert can_do([-S(3)/2, -S(1)/2], [-S(5)/2, 1]) assert can_do([-S(3)/2, ], [-S(1)/2, S(1)/2]) # shine-integral assert can_do([-S(3)/2, -S(1)/2], [2]) # elliptic integrals @XFAIL def test_roach_fail(): assert can_do([-S(1)/2, 1], [S(1)/4, S(1)/2, S(3)/4]) # PFDD assert can_do([S(3)/2], [S(5)/2, 5]) # struve function assert can_do([-S(1)/2, S(1)/2, 1], [S(3)/2, S(5)/2]) # polylog, pfdd assert can_do([1, 2, 3], [S(1)/2, 4]) # XXX ? assert can_do([S(1)/2], [-S(1)/3, -S(1)/2, -S(2)/3]) # PFDD ? # For the long table tests, see end of file def test_polynomial(): from sympy import oo assert hyperexpand(hyper([], [-1], z)) == oo assert hyperexpand(hyper([-2], [-1], z)) == oo assert hyperexpand(hyper([0, 0], [-1], z)) == 1 assert can_do([-5, -2, randcplx(), randcplx()], [-10, randcplx()]) def test_hyperexpand_bases(): assert hyperexpand(hyper([2], [a], z)) == \ a + z**(-a + 1)*(-a**2 + 3*a + z*(a - 1) - 2)*exp(z)* \ lowergamma(a - 1, z) - 1 # TODO [a+1, a-S.Half], [2*a] assert hyperexpand(hyper([1, 2], [3], z)) == -2/z - 2*log(-z + 1)/z**2 assert hyperexpand(hyper([S.Half, 2], [S(3)/2], z)) == \ -1/(2*z - 2) + atanh(sqrt(z))/sqrt(z)/2 assert hyperexpand(hyper([S(1)/2, S(1)/2], [S(5)/2], z)) == \ (-3*z + 3)/4/(z*sqrt(-z + 1)) \ + (6*z - 3)*asin(sqrt(z))/(4*z**(S(3)/2)) assert hyperexpand(hyper([1, 2], [S(3)/2], z)) == -1/(2*z - 2) \ - asin(sqrt(z))/(sqrt(z)*(2*z - 2)*sqrt(-z + 1)) assert hyperexpand(hyper([-S.Half - 1, 1, 2], [S.Half, 3], z)) == \ sqrt(z)*(6*z/7 - S(6)/5)*atanh(sqrt(z)) \ + (-30*z**2 + 32*z - 6)/35/z - 6*log(-z + 1)/(35*z**2) assert hyperexpand(hyper([1 + S.Half, 1, 1], [2, 2], z)) == \ -4*log(sqrt(-z + 1)/2 + S(1)/2)/z # TODO hyperexpand(hyper([a], [2*a + 1], z)) # TODO [S.Half, a], [S(3)/2, a+1] assert hyperexpand(hyper([2], [b, 1], z)) == \ z**(-b/2 + S(1)/2)*besseli(b - 1, 2*sqrt(z))*gamma(b) \ + z**(-b/2 + 1)*besseli(b, 2*sqrt(z))*gamma(b) # TODO [a], [a - S.Half, 2*a] def test_hyperexpand_parametric(): assert hyperexpand(hyper([a, S(1)/2 + a], [S(1)/2], z)) \ == (1 + sqrt(z))**(-2*a)/2 + (1 - sqrt(z))**(-2*a)/2 assert hyperexpand(hyper([a, -S(1)/2 + a], [2*a], z)) \ == 2**(2*a - 1)*((-z + 1)**(S(1)/2) + 1)**(-2*a + 1) def test_shifted_sum(): from sympy import simplify assert simplify(hyperexpand(z**4*hyper([2], [3, S('3/2')], -z**2))) \ == z*sin(2*z) + (-z**2 + S.Half)*cos(2*z) - S.Half def _randrat(): """ Steer clear of integers. """ return S(randrange(25) + 10)/50 def randcplx(offset=-1): """ Polys is not good with real coefficients. """ return _randrat() + I*_randrat() + I*(1 + offset) def test_formulae(): from sympy.simplify.hyperexpand import FormulaCollection formulae = FormulaCollection().formulae for formula in formulae: h = formula.func(formula.z) rep = {} for n, sym in enumerate(formula.symbols): rep[sym] = randcplx(n) # NOTE hyperexpand returns truly branched functions. We know we are # on the main sheet, but numerical evaluation can still go wrong # (e.g. if exp_polar cannot be evalf'd). # Just replace all exp_polar by exp, this usually works. # first test if the closed-form is actually correct h = h.subs(rep) closed_form = formula.closed_form.subs(rep).rewrite('nonrepsmall') z = formula.z assert tn(h, closed_form.replace(exp_polar, exp), z) # now test the computed matrix cl = (formula.C * formula.B)[0].subs(rep).rewrite('nonrepsmall') assert tn(closed_form.replace( exp_polar, exp), cl.replace(exp_polar, exp), z) deriv1 = z*formula.B.applyfunc(lambda t: t.rewrite( 'nonrepsmall')).diff(z) deriv2 = formula.M * formula.B for d1, d2 in zip(deriv1, deriv2): assert tn(d1.subs(rep).replace(exp_polar, exp), d2.subs(rep).rewrite('nonrepsmall').replace(exp_polar, exp), z) def test_meijerg_formulae(): from sympy.simplify.hyperexpand import MeijerFormulaCollection formulae = MeijerFormulaCollection().formulae for sig in formulae: for formula in formulae[sig]: g = meijerg(formula.func.an, formula.func.ap, formula.func.bm, formula.func.bq, formula.z) rep = {} for sym in formula.symbols: rep[sym] = randcplx() # first test if the closed-form is actually correct g = g.subs(rep) closed_form = formula.closed_form.subs(rep) z = formula.z assert tn(g, closed_form, z) #print closed_form # now test the computed matrix cl = (formula.C * formula.B)[0].subs(rep) assert tn(closed_form, cl, z) deriv1 = z*formula.B.diff(z) deriv2 = formula.M * formula.B for d1, d2 in zip(deriv1, deriv2): assert tn(d1.subs(rep), d2.subs(rep), z) def op(f): return z*f.diff(z) def test_plan(): assert devise_plan(Hyper_Function([0], ()), Hyper_Function([0], ()), z) == [] with raises(ValueError): devise_plan(Hyper_Function([1], ()), Hyper_Function((), ()), z) with raises(ValueError): devise_plan(Hyper_Function([2], [1]), Hyper_Function([2], [2]), z) with raises(ValueError): devise_plan(Hyper_Function([2], []), Hyper_Function([S("1/2")], []), z) # We cannot use pi/(10000 + n) because polys is insanely slow. a1, a2, b1 = map(lambda n: randcplx(n), range(3)) b1 += 2*I h = hyper([a1, a2], [b1], z) h2 = hyper((a1 + 1, a2), [b1], z) assert tn(apply_operators(h, devise_plan(Hyper_Function((a1 + 1, a2), [b1]), Hyper_Function((a1, a2), [b1]), z), op), h2, z) h2 = hyper((a1 + 1, a2 - 1), [b1], z) assert tn(apply_operators(h, devise_plan(Hyper_Function((a1 + 1, a2 - 1), [b1]), Hyper_Function((a1, a2), [b1]), z), op), h2, z) def test_plan_derivatives(): a1, a2, a3 = 1, 2, S('1/2') b1, b2 = 3, S('5/2') h = Hyper_Function((a1, a2, a3), (b1, b2)) h2 = Hyper_Function((a1 + 1, a2 + 1, a3 + 2), (b1 + 1, b2 + 1)) ops = devise_plan(h2, h, z) f = Formula(h, z, h(z), []) deriv = make_derivative_operator(f.M, z) assert tn((apply_operators(f.C, ops, deriv)*f.B)[0], h2(z), z) h2 = Hyper_Function((a1, a2 - 1, a3 - 2), (b1 - 1, b2 - 1)) ops = devise_plan(h2, h, z) assert tn((apply_operators(f.C, ops, deriv)*f.B)[0], h2(z), z) def test_reduction_operators(): a1, a2, b1 = map(lambda n: randcplx(n), range(3)) h = hyper([a1], [b1], z) assert ReduceOrder(2, 0) is None assert ReduceOrder(2, -1) is None assert ReduceOrder(1, S('1/2')) is None h2 = hyper((a1, a2), (b1, a2), z) assert tn(ReduceOrder(a2, a2).apply(h, op), h2, z) h2 = hyper((a1, a2 + 1), (b1, a2), z) assert tn(ReduceOrder(a2 + 1, a2).apply(h, op), h2, z) h2 = hyper((a2 + 4, a1), (b1, a2), z) assert tn(ReduceOrder(a2 + 4, a2).apply(h, op), h2, z) # test several step order reduction ap = (a2 + 4, a1, b1 + 1) bq = (a2, b1, b1) func, ops = reduce_order(Hyper_Function(ap, bq)) assert func.ap == (a1,) assert func.bq == (b1,) assert tn(apply_operators(h, ops, op), hyper(ap, bq, z), z) def test_shift_operators(): a1, a2, b1, b2, b3 = map(lambda n: randcplx(n), range(5)) h = hyper((a1, a2), (b1, b2, b3), z) raises(ValueError, lambda: ShiftA(0)) raises(ValueError, lambda: ShiftB(1)) assert tn(ShiftA(a1).apply(h, op), hyper((a1 + 1, a2), (b1, b2, b3), z), z) assert tn(ShiftA(a2).apply(h, op), hyper((a1, a2 + 1), (b1, b2, b3), z), z) assert tn(ShiftB(b1).apply(h, op), hyper((a1, a2), (b1 - 1, b2, b3), z), z) assert tn(ShiftB(b2).apply(h, op), hyper((a1, a2), (b1, b2 - 1, b3), z), z) assert tn(ShiftB(b3).apply(h, op), hyper((a1, a2), (b1, b2, b3 - 1), z), z) def test_ushift_operators(): a1, a2, b1, b2, b3 = map(lambda n: randcplx(n), range(5)) h = hyper((a1, a2), (b1, b2, b3), z) raises(ValueError, lambda: UnShiftA((1,), (), 0, z)) raises(ValueError, lambda: UnShiftB((), (-1,), 0, z)) raises(ValueError, lambda: UnShiftA((1,), (0, -1, 1), 0, z)) raises(ValueError, lambda: UnShiftB((0, 1), (1,), 0, z)) s = UnShiftA((a1, a2), (b1, b2, b3), 0, z) assert tn(s.apply(h, op), hyper((a1 - 1, a2), (b1, b2, b3), z), z) s = UnShiftA((a1, a2), (b1, b2, b3), 1, z) assert tn(s.apply(h, op), hyper((a1, a2 - 1), (b1, b2, b3), z), z) s = UnShiftB((a1, a2), (b1, b2, b3), 0, z) assert tn(s.apply(h, op), hyper((a1, a2), (b1 + 1, b2, b3), z), z) s = UnShiftB((a1, a2), (b1, b2, b3), 1, z) assert tn(s.apply(h, op), hyper((a1, a2), (b1, b2 + 1, b3), z), z) s = UnShiftB((a1, a2), (b1, b2, b3), 2, z) assert tn(s.apply(h, op), hyper((a1, a2), (b1, b2, b3 + 1), z), z) def can_do_meijer(a1, a2, b1, b2, numeric=True): """ This helper function tries to hyperexpand() the meijer g-function corresponding to the parameters a1, a2, b1, b2. It returns False if this expansion still contains g-functions. If numeric is True, it also tests the so-obtained formula numerically (at random values) and returns False if the test fails. Else it returns True. """ from sympy import unpolarify, expand r = hyperexpand(meijerg(a1, a2, b1, b2, z)) if r.has(meijerg): return False # NOTE hyperexpand() returns a truly branched function, whereas numerical # evaluation only works on the main branch. Since we are evaluating on # the main branch, this should not be a problem, but expressions like # exp_polar(I*pi/2*x)**a are evaluated incorrectly. We thus have to get # rid of them. The expand heuristically does this... r = unpolarify(expand(r, force=True, power_base=True, power_exp=False, mul=False, log=False, multinomial=False, basic=False)) if not numeric: return True repl = {} for n, a in enumerate(meijerg(a1, a2, b1, b2, z).free_symbols - set([z])): repl[a] = randcplx(n) return tn(meijerg(a1, a2, b1, b2, z).subs(repl), r.subs(repl), z) def test_meijerg_expand(): from sympy import combsimp, simplify # from mpmath docs assert hyperexpand(meijerg([[], []], [[0], []], -z)) == exp(z) assert hyperexpand(meijerg([[1, 1], []], [[1], [0]], z)) == \ log(z + 1) assert hyperexpand(meijerg([[1, 1], []], [[1], [1]], z)) == \ z/(z + 1) assert hyperexpand(meijerg([[], []], [[S(1)/2], [0]], (z/2)**2)) \ == sin(z)/sqrt(pi) assert hyperexpand(meijerg([[], []], [[0], [S(1)/2]], (z/2)**2)) \ == cos(z)/sqrt(pi) assert can_do_meijer([], [a], [a - 1, a - S.Half], []) assert can_do_meijer([], [], [a/2], [-a/2], False) # branches... assert can_do_meijer([a], [b], [a], [b, a - 1]) # wikipedia assert hyperexpand(meijerg([1], [], [], [0], z)) == \ Piecewise((0, abs(z) < 1), (1, abs(1/z) < 1), (meijerg([1], [], [], [0], z), True)) assert hyperexpand(meijerg([], [1], [0], [], z)) == \ Piecewise((1, abs(z) < 1), (0, abs(1/z) < 1), (meijerg([], [1], [0], [], z), True)) # The Special Functions and their Approximations assert can_do_meijer([], [], [a + b/2], [a, a - b/2, a + S.Half]) assert can_do_meijer( [], [], [a], [b], False) # branches only agree for small z assert can_do_meijer([], [S.Half], [a], [-a]) assert can_do_meijer([], [], [a, b], []) assert can_do_meijer([], [], [a, b], []) assert can_do_meijer([], [], [a, a + S.Half], [b, b + S.Half]) assert can_do_meijer([], [], [a, -a], [0, S.Half], False) # dito assert can_do_meijer([], [], [a, a + S.Half, b, b + S.Half], []) assert can_do_meijer([S.Half], [], [0], [a, -a]) assert can_do_meijer([S.Half], [], [a], [0, -a], False) # dito assert can_do_meijer([], [a - S.Half], [a, b], [a - S.Half], False) assert can_do_meijer([], [a + S.Half], [a + b, a - b, a], [], False) assert can_do_meijer([a + S.Half], [], [b, 2*a - b, a], [], False) # This for example is actually zero. assert can_do_meijer([], [], [], [a, b]) # Testing a bug: assert hyperexpand(meijerg([0, 2], [], [], [-1, 1], z)) == \ Piecewise((0, abs(z) < 1), (z/2 - 1/(2*z), abs(1/z) < 1), (meijerg([0, 2], [], [], [-1, 1], z), True)) # Test that the simplest possible answer is returned: assert combsimp(simplify(hyperexpand( meijerg([1], [1 - a], [-a/2, -a/2 + S(1)/2], [], 1/z)))) == \ -2*sqrt(pi)*(sqrt(z + 1) + 1)**a/a # Test that hyper is returned assert hyperexpand(meijerg([1], [], [a], [0, 0], z)) == hyper( (a,), (a + 1, a + 1), z*exp_polar(I*pi))*z**a*gamma(a)/gamma(a + 1)**2 def test_meijerg_lookup(): from sympy import uppergamma, Si, Ci assert hyperexpand(meijerg([a], [], [b, a], [], z)) == \ z**b*exp(z)*gamma(-a + b + 1)*uppergamma(a - b, z) assert hyperexpand(meijerg([0], [], [0, 0], [], z)) == \ exp(z)*uppergamma(0, z) assert can_do_meijer([a], [], [b, a + 1], []) assert can_do_meijer([a], [], [b + 2, a], []) assert can_do_meijer([a], [], [b - 2, a], []) assert hyperexpand(meijerg([a], [], [a, a, a - S(1)/2], [], z)) == \ -sqrt(pi)*z**(a - S(1)/2)*(2*cos(2*sqrt(z))*(Si(2*sqrt(z)) - pi/2) - 2*sin(2*sqrt(z))*Ci(2*sqrt(z))) == \ hyperexpand(meijerg([a], [], [a, a - S(1)/2, a], [], z)) == \ hyperexpand(meijerg([a], [], [a - S(1)/2, a, a], [], z)) assert can_do_meijer([a - 1], [], [a + 2, a - S(3)/2, a + 1], []) @XFAIL def test_meijerg_expand_fail(): # These basically test hyper([], [1/2 - a, 1/2 + 1, 1/2], z), # which is *very* messy. But since the meijer g actually yields a # sum of bessel functions, things can sometimes be simplified a lot and # are then put into tables... assert can_do_meijer([], [], [a + S.Half], [a, a - b/2, a + b/2]) assert can_do_meijer([], [], [0, S.Half], [a, -a]) assert can_do_meijer([], [], [3*a - S.Half, a, -a - S.Half], [a - S.Half]) assert can_do_meijer([], [], [0, a - S.Half, -a - S.Half], [S.Half]) assert can_do_meijer([], [], [a, b + S(1)/2, b], [2*b - a]) assert can_do_meijer([], [], [a, b + S(1)/2, b, 2*b - a]) assert can_do_meijer([S.Half], [], [-a, a], [0]) def test_meijerg(): # carefully set up the parameters. # NOTE: this used to fail sometimes. I believe it is fixed, but if you # hit an inexplicable test failure here, please let me know the seed. a1, a2 = map(lambda n: randcplx() - 5*I - n*I, range(2)) b1, b2 = map(lambda n: randcplx() + 5*I + n*I, range(2)) b3, b4, b5, a3, a4, a5 = map(lambda n: randcplx(), range(6)) g = meijerg([a1], [a3, a4], [b1], [b3, b4], z) assert ReduceOrder.meijer_minus(3, 4) is None assert ReduceOrder.meijer_plus(4, 3) is None g2 = meijerg([a1, a2], [a3, a4], [b1], [b3, b4, a2], z) assert tn(ReduceOrder.meijer_plus(a2, a2).apply(g, op), g2, z) g2 = meijerg([a1, a2], [a3, a4], [b1], [b3, b4, a2 + 1], z) assert tn(ReduceOrder.meijer_plus(a2, a2 + 1).apply(g, op), g2, z) g2 = meijerg([a1, a2 - 1], [a3, a4], [b1], [b3, b4, a2 + 2], z) assert tn(ReduceOrder.meijer_plus(a2 - 1, a2 + 2).apply(g, op), g2, z) g2 = meijerg([a1], [a3, a4, b2 - 1], [b1, b2 + 2], [b3, b4], z) assert tn(ReduceOrder.meijer_minus( b2 + 2, b2 - 1).apply(g, op), g2, z, tol=1e-6) # test several-step reduction an = [a1, a2] bq = [b3, b4, a2 + 1] ap = [a3, a4, b2 - 1] bm = [b1, b2 + 1] niq, ops = reduce_order_meijer(G_Function(an, ap, bm, bq)) assert niq.an == (a1,) assert set(niq.ap) == set([a3, a4]) assert niq.bm == (b1,) assert set(niq.bq) == set([b3, b4]) assert tn(apply_operators(g, ops, op), meijerg(an, ap, bm, bq, z), z) def test_meijerg_shift_operators(): # carefully set up the parameters. XXX this still fails sometimes a1, a2, a3, a4, a5, b1, b2, b3, b4, b5 = \ map(lambda n: randcplx(n), range(10)) g = meijerg([a1], [a3, a4], [b1], [b3, b4], z) assert tn(MeijerShiftA(b1).apply(g, op), meijerg([a1], [a3, a4], [b1 + 1], [b3, b4], z), z) assert tn(MeijerShiftB(a1).apply(g, op), meijerg([a1 - 1], [a3, a4], [b1], [b3, b4], z), z) assert tn(MeijerShiftC(b3).apply(g, op), meijerg([a1], [a3, a4], [b1], [b3 + 1, b4], z), z) assert tn(MeijerShiftD(a3).apply(g, op), meijerg([a1], [a3 - 1, a4], [b1], [b3, b4], z), z) s = MeijerUnShiftA([a1], [a3, a4], [b1], [b3, b4], 0, z) assert tn( s.apply(g, op), meijerg([a1], [a3, a4], [b1 - 1], [b3, b4], z), z) s = MeijerUnShiftC([a1], [a3, a4], [b1], [b3, b4], 0, z) assert tn( s.apply(g, op), meijerg([a1], [a3, a4], [b1], [b3 - 1, b4], z), z) s = MeijerUnShiftB([a1], [a3, a4], [b1], [b3, b4], 0, z) assert tn( s.apply(g, op), meijerg([a1 + 1], [a3, a4], [b1], [b3, b4], z), z) s = MeijerUnShiftD([a1], [a3, a4], [b1], [b3, b4], 0, z) assert tn( s.apply(g, op), meijerg([a1], [a3 + 1, a4], [b1], [b3, b4], z), z) def test_meijerg_confluence(): def t(m, a, b): from sympy import sympify, Piecewise a, b = sympify([a, b]) m_ = m m = hyperexpand(m) if not m == Piecewise((a, abs(z) < 1), (b, abs(1/z) < 1), (m_, True)): return False if not (m.args[0].args[0] == a and m.args[1].args[0] == b): return False z0 = randcplx()/10 if abs(m.subs(z, z0).n() - a.subs(z, z0).n()).n() > 1e-10: return False if abs(m.subs(z, 1/z0).n() - b.subs(z, 1/z0).n()).n() > 1e-10: return False return True assert t(meijerg([], [1, 1], [0, 0], [], z), -log(z), 0) assert t(meijerg( [], [3, 1], [0, 0], [], z), -z**2/4 + z - log(z)/2 - S(3)/4, 0) assert t(meijerg([], [3, 1], [-1, 0], [], z), z**2/12 - z/2 + log(z)/2 + S(1)/4 + 1/(6*z), 0) assert t(meijerg([], [1, 1, 1, 1], [0, 0, 0, 0], [], z), -log(z)**3/6, 0) assert t(meijerg([1, 1], [], [], [0, 0], z), 0, -log(1/z)) assert t(meijerg([1, 1], [2, 2], [1, 1], [0, 0], z), -z*log(z) + 2*z, -log(1/z) + 2) assert t(meijerg([S(1)/2], [1, 1], [0, 0], [S(3)/2], z), log(z)/2 - 1, 0) def u(an, ap, bm, bq): m = meijerg(an, ap, bm, bq, z) m2 = hyperexpand(m, allow_hyper=True) if m2.has(meijerg) and not (m2.is_Piecewise and len(m2.args) == 3): return False return tn(m, m2, z) assert u([], [1], [0, 0], []) assert u([1, 1], [], [], [0]) assert u([1, 1], [2, 2, 5], [1, 1, 6], [0, 0]) assert u([1, 1], [2, 2, 5], [1, 1, 6], [0]) def test_lerchphi(): from sympy import combsimp, exp_polar, polylog, log, lerchphi assert hyperexpand(hyper([1, a], [a + 1], z)/a) == lerchphi(z, 1, a) assert hyperexpand( hyper([1, a, a], [a + 1, a + 1], z)/a**2) == lerchphi(z, 2, a) assert hyperexpand(hyper([1, a, a, a], [a + 1, a + 1, a + 1], z)/a**3) == \ lerchphi(z, 3, a) assert hyperexpand(hyper([1] + [a]*10, [a + 1]*10, z)/a**10) == \ lerchphi(z, 10, a) assert combsimp(hyperexpand(meijerg([0, 1 - a], [], [0], [-a], exp_polar(-I*pi)*z))) == lerchphi(z, 1, a) assert combsimp(hyperexpand(meijerg([0, 1 - a, 1 - a], [], [0], [-a, -a], exp_polar(-I*pi)*z))) == lerchphi(z, 2, a) assert combsimp(hyperexpand(meijerg([0, 1 - a, 1 - a, 1 - a], [], [0], [-a, -a, -a], exp_polar(-I*pi)*z))) == lerchphi(z, 3, a) assert hyperexpand(z*hyper([1, 1], [2], z)) == -log(1 + -z) assert hyperexpand(z*hyper([1, 1, 1], [2, 2], z)) == polylog(2, z) assert hyperexpand(z*hyper([1, 1, 1, 1], [2, 2, 2], z)) == polylog(3, z) assert hyperexpand(hyper([1, a, 1 + S(1)/2], [a + 1, S(1)/2], z)) == \ -2*a/(z - 1) + (-2*a**2 + a)*lerchphi(z, 1, a) # Now numerical tests. These make sure reductions etc are carried out # correctly # a rational function (polylog at negative integer order) assert can_do([2, 2, 2], [1, 1]) # NOTE these contain log(1-x) etc ... better make sure we have |z| < 1 # reduction of order for polylog assert can_do([1, 1, 1, b + 5], [2, 2, b], div=10) # reduction of order for lerchphi # XXX lerchphi in mpmath is flaky assert can_do( [1, a, a, a, b + 5], [a + 1, a + 1, a + 1, b], numerical=False) # test a bug from sympy import Abs assert hyperexpand(hyper([S(1)/2, S(1)/2, S(1)/2, 1], [S(3)/2, S(3)/2, S(3)/2], S(1)/4)) == \ Abs(-polylog(3, exp_polar(I*pi)/2) + polylog(3, S(1)/2)) def test_partial_simp(): # First test that hypergeometric function formulae work. a, b, c, d, e = map(lambda _: randcplx(), range(5)) for func in [Hyper_Function([a, b, c], [d, e]), Hyper_Function([], [a, b, c, d, e])]: f = build_hypergeometric_formula(func) z = f.z assert f.closed_form == func(z) deriv1 = f.B.diff(z)*z deriv2 = f.M*f.B for func1, func2 in zip(deriv1, deriv2): assert tn(func1, func2, z) # Now test that formulae are partially simplified. from sympy.abc import a, b, z assert hyperexpand(hyper([3, a], [1, b], z)) == \ (-a*b/2 + a*z/2 + 2*a)*hyper([a + 1], [b], z) \ + (a*b/2 - 2*a + 1)*hyper([a], [b], z) assert tn( hyperexpand(hyper([3, d], [1, e], z)), hyper([3, d], [1, e], z), z) assert hyperexpand(hyper([3], [1, a, b], z)) == \ hyper((), (a, b), z) \ + z*hyper((), (a + 1, b), z)/(2*a) \ - z*(b - 4)*hyper((), (a + 1, b + 1), z)/(2*a*b) assert tn( hyperexpand(hyper([3], [1, d, e], z)), hyper([3], [1, d, e], z), z) def test_hyperexpand_special(): assert hyperexpand(hyper([a, b], [c], 1)) == \ gamma(c)*gamma(c - a - b)/gamma(c - a)/gamma(c - b) assert hyperexpand(hyper([a, b], [1 + a - b], -1)) == \ gamma(1 + a/2)*gamma(1 + a - b)/gamma(1 + a)/gamma(1 + a/2 - b) assert hyperexpand(hyper([a, b], [1 + b - a], -1)) == \ gamma(1 + b/2)*gamma(1 + b - a)/gamma(1 + b)/gamma(1 + b/2 - a) assert hyperexpand(meijerg([1 - z - a/2], [1 - z + a/2], [b/2], [-b/2], 1)) == \ gamma(1 - 2*z)*gamma(z + a/2 + b/2)/gamma(1 - z + a/2 - b/2) \ /gamma(1 - z - a/2 + b/2)/gamma(1 - z + a/2 + b/2) assert hyperexpand(hyper([a], [b], 0)) == 1 assert hyper([a], [b], 0) != 0 def test_Mod1_behavior(): from sympy import Symbol, simplify, lowergamma n = Symbol('n', integer=True) # Note: this should not hang. assert simplify(hyperexpand(meijerg([1], [], [n + 1], [0], z))) == \ lowergamma(n + 1, z) @slow def test_prudnikov_misc(): assert can_do([1, (3 + I)/2, (3 - I)/2], [S(3)/2, 2]) assert can_do([S.Half, a - 1], [S(3)/2, a + 1], lowerplane=True) assert can_do([], [b + 1]) assert can_do([a], [a - 1, b + 1]) assert can_do([a], [a - S.Half, 2*a]) assert can_do([a], [a - S.Half, 2*a + 1]) assert can_do([a], [a - S.Half, 2*a - 1]) assert can_do([a], [a + S.Half, 2*a]) assert can_do([a], [a + S.Half, 2*a + 1]) assert can_do([a], [a + S.Half, 2*a - 1]) assert can_do([S.Half], [b, 2 - b]) assert can_do([S.Half], [b, 3 - b]) assert can_do([1], [2, b]) assert can_do([a, a + S.Half], [2*a, b, 2*a - b + 1]) assert can_do([a, a + S.Half], [S.Half, 2*a, 2*a + S.Half]) assert can_do([a], [a + 1], lowerplane=True) # lowergamma @slow def test_prudnikov_1(): # A. P. Prudnikov, Yu. A. Brychkov and O. I. Marichev (1990). # Integrals and Series: More Special Functions, Vol. 3,. # Gordon and Breach Science Publisher # 7.3.1 assert can_do([a, -a], [S.Half]) assert can_do([a, 1 - a], [S.Half]) assert can_do([a, 1 - a], [S(3)/2]) assert can_do([a, 2 - a], [S.Half]) assert can_do([a, 2 - a], [S(3)/2]) assert can_do([a, 2 - a], [S(3)/2]) assert can_do([a, a + S(1)/2], [2*a - 1]) assert can_do([a, a + S(1)/2], [2*a]) assert can_do([a, a + S(1)/2], [2*a + 1]) assert can_do([a, a + S(1)/2], [S(1)/2]) assert can_do([a, a + S(1)/2], [S(3)/2]) assert can_do([a, a/2 + 1], [a/2]) assert can_do([1, b], [2]) assert can_do([1, b], [b + 1], numerical=False) # Lerch Phi # NOTE: branches are complicated for |z| > 1 assert can_do([a], [2*a]) assert can_do([a], [2*a + 1]) assert can_do([a], [2*a - 1]) @slow def test_prudnikov_2(): h = S.Half assert can_do([-h, -h], [h]) assert can_do([-h, h], [3*h]) assert can_do([-h, h], [5*h]) assert can_do([-h, h], [7*h]) assert can_do([-h, 1], [h]) for p in [-h, h]: for n in [-h, h, 1, 3*h, 2, 5*h, 3, 7*h, 4]: for m in [-h, h, 3*h, 5*h, 7*h]: assert can_do([p, n], [m]) for n in [1, 2, 3, 4]: for m in [1, 2, 3, 4]: assert can_do([p, n], [m]) @slow def test_prudnikov_3(): h = S.Half assert can_do([S(1)/4, S(3)/4], [h]) assert can_do([S(1)/4, S(3)/4], [3*h]) assert can_do([S(1)/3, S(2)/3], [3*h]) assert can_do([S(3)/4, S(5)/4], [h]) assert can_do([S(3)/4, S(5)/4], [3*h]) for p in [1, 2, 3, 4]: for n in [-h, h, 1, 3*h, 2, 5*h, 3, 7*h, 4, 9*h]: for m in [1, 3*h, 2, 5*h, 3, 7*h, 4]: assert can_do([p, m], [n]) @slow def test_prudnikov_4(): h = S.Half for p in [3*h, 5*h, 7*h]: for n in [-h, h, 3*h, 5*h, 7*h]: for m in [3*h, 2, 5*h, 3, 7*h, 4]: assert can_do([p, m], [n]) for n in [1, 2, 3, 4]: for m in [2, 3, 4]: assert can_do([p, m], [n]) @slow def test_prudnikov_5(): h = S.Half for p in [1, 2, 3]: for q in range(p, 4): for r in [1, 2, 3]: for s in range(r, 4): assert can_do([-h, p, q], [r, s]) for p in [h, 1, 3*h, 2, 5*h, 3]: for q in [h, 3*h, 5*h]: for r in [h, 3*h, 5*h]: for s in [h, 3*h, 5*h]: if s <= q and s <= r: assert can_do([-h, p, q], [r, s]) for p in [h, 1, 3*h, 2, 5*h, 3]: for q in [1, 2, 3]: for r in [h, 3*h, 5*h]: for s in [1, 2, 3]: assert can_do([-h, p, q], [r, s]) @slow def test_prudnikov_6(): h = S.Half for m in [3*h, 5*h]: for n in [1, 2, 3]: for q in [h, 1, 2]: for p in [1, 2, 3]: assert can_do([h, q, p], [m, n]) for q in [1, 2, 3]: for p in [3*h, 5*h]: assert can_do([h, q, p], [m, n]) for q in [1, 2]: for p in [1, 2, 3]: for m in [1, 2, 3]: for n in [1, 2, 3]: assert can_do([h, q, p], [m, n]) assert can_do([h, h, 5*h], [3*h, 3*h]) assert can_do([h, 1, 5*h], [3*h, 3*h]) assert can_do([h, 2, 2], [1, 3]) # pages 435 to 457 contain more PFDD and stuff like this @slow def test_prudnikov_7(): assert can_do([3], [6]) h = S.Half for n in [h, 3*h, 5*h, 7*h]: assert can_do([-h], [n]) for m in [-h, h, 1, 3*h, 2, 5*h, 3, 7*h, 4]: # HERE for n in [-h, h, 3*h, 5*h, 7*h, 1, 2, 3, 4]: assert can_do([m], [n]) @slow def test_prudnikov_8(): h = S.Half # 7.12.2 for a in [1, 2, 3]: for b in [1, 2, 3]: for c in range(1, a + 1): for d in [h, 1, 3*h, 2, 5*h, 3]: assert can_do([a, b], [c, d]) for b in [3*h, 5*h]: for c in [h, 1, 3*h, 2, 5*h, 3]: for d in [1, 2, 3]: assert can_do([a, b], [c, d]) for a in [-h, h, 3*h, 5*h]: for b in [1, 2, 3]: for c in [h, 1, 3*h, 2, 5*h, 3]: for d in [1, 2, 3]: assert can_do([a, b], [c, d]) for b in [h, 3*h, 5*h]: for c in [h, 3*h, 5*h, 3]: for d in [h, 1, 3*h, 2, 5*h, 3]: if c <= b: assert can_do([a, b], [c, d]) @slow def test_prudnikov_9(): # 7.13.1 [we have a general formula ... so this is a bit pointless] for i in range(9): assert can_do([], [(S(i) + 1)/2]) for i in range(5): assert can_do([], [-(2*S(i) + 1)/2]) @slow def test_prudnikov_10(): # 7.14.2 h = S.Half for p in [-h, h, 1, 3*h, 2, 5*h, 3, 7*h, 4]: for m in [1, 2, 3, 4]: for n in range(m, 5): assert can_do([p], [m, n]) for p in [1, 2, 3, 4]: for n in [h, 3*h, 5*h, 7*h]: for m in [1, 2, 3, 4]: assert can_do([p], [n, m]) for p in [3*h, 5*h, 7*h]: for m in [h, 1, 2, 5*h, 3, 7*h, 4]: assert can_do([p], [h, m]) assert can_do([p], [3*h, m]) for m in [h, 1, 2, 5*h, 3, 7*h, 4]: assert can_do([7*h], [5*h, m]) assert can_do([-S(1)/2], [S(1)/2, S(1)/2]) # shine-integral shi @slow def test_prudnikov_11(): # 7.15 assert can_do([a, a + S.Half], [2*a, b, 2*a - b]) assert can_do([a, a + S.Half], [S(3)/2, 2*a, 2*a - S(1)/2]) assert can_do([S(1)/4, S(3)/4], [S(1)/2, S(1)/2, 1]) assert can_do([S(5)/4, S(3)/4], [S(3)/2, S(1)/2, 2]) assert can_do([S(5)/4, S(3)/4], [S(3)/2, S(3)/2, 1]) assert can_do([S(5)/4, S(7)/4], [S(3)/2, S(5)/2, 2]) assert can_do([1, 1], [S(3)/2, 2, 2]) # cosh-integral chi @slow def test_prudnikov_12(): # 7.16 assert can_do( [], [a, a + S.Half, 2*a], False) # branches only agree for some z! assert can_do([], [a, a + S.Half, 2*a + 1], False) # dito assert can_do([], [S.Half, a, a + S.Half]) assert can_do([], [S(3)/2, a, a + S.Half]) assert can_do([], [S(1)/4, S(1)/2, S(3)/4]) assert can_do([], [S(1)/2, S(1)/2, 1]) assert can_do([], [S(1)/2, S(3)/2, 1]) assert can_do([], [S(3)/4, S(3)/2, S(5)/4]) assert can_do([], [1, 1, S(3)/2]) assert can_do([], [1, 2, S(3)/2]) assert can_do([], [1, S(3)/2, S(3)/2]) assert can_do([], [S(5)/4, S(3)/2, S(7)/4]) assert can_do([], [2, S(3)/2, S(3)/2]) def test_prudnikov_2F1(): h = S.Half # Elliptic integrals for p in [-h, h]: for m in [h, 3*h, 5*h, 7*h]: for n in [1, 2, 3, 4]: assert can_do([p, m], [n]) @XFAIL def test_prudnikov_fail_2F1(): assert can_do([a, b], [b + 1]) # incomplete beta function assert can_do([-1, b], [c]) # Poly. also -2, -3 etc # TODO polys # Legendre functions: assert can_do([a, b], [a + b + S.Half]) assert can_do([a, b], [a + b - S.Half]) assert can_do([a, b], [a + b + S(3)/2]) assert can_do([a, b], [(a + b + 1)/2]) assert can_do([a, b], [(a + b)/2 + 1]) assert can_do([a, b], [a - b + 1]) assert can_do([a, b], [a - b + 2]) assert can_do([a, b], [2*b]) assert can_do([a, b], [S.Half]) assert can_do([a, b], [S(3)/2]) assert can_do([a, 1 - a], [c]) assert can_do([a, 2 - a], [c]) assert can_do([a, 3 - a], [c]) assert can_do([a, a + S(1)/2], [c]) assert can_do([1, b], [c]) assert can_do([1, b], [S(3)/2]) assert can_do([S(1)/4, S(3)/4], [1]) # PFDD o = S(1) assert can_do([o/8, 1], [o/8*9]) assert can_do([o/6, 1], [o/6*7]) assert can_do([o/6, 1], [o/6*13]) assert can_do([o/5, 1], [o/5*6]) assert can_do([o/5, 1], [o/5*11]) assert can_do([o/4, 1], [o/4*5]) assert can_do([o/4, 1], [o/4*9]) assert can_do([o/3, 1], [o/3*4]) assert can_do([o/3, 1], [o/3*7]) assert can_do([o/8*3, 1], [o/8*11]) assert can_do([o/5*2, 1], [o/5*7]) assert can_do([o/5*2, 1], [o/5*12]) assert can_do([o/5*3, 1], [o/5*8]) assert can_do([o/5*3, 1], [o/5*13]) assert can_do([o/8*5, 1], [o/8*13]) assert can_do([o/4*3, 1], [o/4*7]) assert can_do([o/4*3, 1], [o/4*11]) assert can_do([o/3*2, 1], [o/3*5]) assert can_do([o/3*2, 1], [o/3*8]) assert can_do([o/5*4, 1], [o/5*9]) assert can_do([o/5*4, 1], [o/5*14]) assert can_do([o/6*5, 1], [o/6*11]) assert can_do([o/6*5, 1], [o/6*17]) assert can_do([o/8*7, 1], [o/8*15]) @XFAIL def test_prudnikov_fail_3F2(): assert can_do([a, a + S(1)/3, a + S(2)/3], [S(1)/3, S(2)/3]) assert can_do([a, a + S(1)/3, a + S(2)/3], [S(2)/3, S(4)/3]) assert can_do([a, a + S(1)/3, a + S(2)/3], [S(4)/3, S(5)/3]) # page 421 assert can_do([a, a + S(1)/3, a + S(2)/3], [3*a/2, (3*a + 1)/2]) # pages 422 ... assert can_do([-S.Half, S.Half, S.Half], [1, 1]) # elliptic integrals assert can_do([-S.Half, S.Half, 1], [S(3)/2, S(3)/2]) # TODO LOTS more # PFDD assert can_do([S(1)/8, S(3)/8, 1], [S(9)/8, S(11)/8]) assert can_do([S(1)/8, S(5)/8, 1], [S(9)/8, S(13)/8]) assert can_do([S(1)/8, S(7)/8, 1], [S(9)/8, S(15)/8]) assert can_do([S(1)/6, S(1)/3, 1], [S(7)/6, S(4)/3]) assert can_do([S(1)/6, S(2)/3, 1], [S(7)/6, S(5)/3]) assert can_do([S(1)/6, S(2)/3, 1], [S(5)/3, S(13)/6]) assert can_do([S.Half, 1, 1], [S(1)/4, S(3)/4]) # LOTS more @XFAIL def test_prudnikov_fail_other(): # 7.11.2 # 7.12.1 assert can_do([1, a], [b, 1 - 2*a + b]) # ??? # 7.14.2 assert can_do([-S(1)/2], [S(1)/2, 1]) # struve assert can_do([1], [S(1)/2, S(1)/2]) # struve assert can_do([S(1)/4], [S(1)/2, S(5)/4]) # PFDD assert can_do([S(3)/4], [S(3)/2, S(7)/4]) # PFDD assert can_do([1], [S(1)/4, S(3)/4]) # PFDD assert can_do([1], [S(3)/4, S(5)/4]) # PFDD assert can_do([1], [S(5)/4, S(7)/4]) # PFDD # TODO LOTS more # 7.15.2 assert can_do([S(1)/2, 1], [S(3)/4, S(5)/4, S(3)/2]) # PFDD assert can_do([S(1)/2, 1], [S(7)/4, S(5)/4, S(3)/2]) # PFDD # 7.16.1 assert can_do([], [S(1)/3, S(2/3)]) # PFDD assert can_do([], [S(2)/3, S(4/3)]) # PFDD assert can_do([], [S(5)/3, S(4/3)]) # PFDD # XXX this does not *evaluate* right?? assert can_do([], [a, a + S.Half, 2*a - 1]) def test_bug(): h = hyper([-1, 1], [z], -1) assert hyperexpand(h) == (z + 1)/z sympy-0.7.4.1/sympy/simplify/tests/test_cse.py0000644000175000017500000002150612253362407021612 0ustar georgeskgeorgeskimport itertools from sympy import (Add, Pow, Symbol, exp, sqrt, symbols, sympify, cse, Matrix, S, cos, sin, Eq, Function, Tuple, RootOf) from sympy.simplify.cse_opts import sub_pre, sub_post from sympy.functions.special.hyper import meijerg from sympy.simplify import cse_main, cse_opts from sympy.utilities.pytest import XFAIL w, x, y, z = symbols('w,x,y,z') x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12 = symbols('x:13') def test_numbered_symbols(): ns = cse_main.numbered_symbols(prefix='y') assert list(itertools.islice( ns, 0, 10)) == [Symbol('y%s' % i) for i in range(0, 10)] ns = cse_main.numbered_symbols(prefix='y') assert list(itertools.islice( ns, 10, 20)) == [Symbol('y%s' % i) for i in range(10, 20)] ns = cse_main.numbered_symbols() assert list(itertools.islice( ns, 0, 10)) == [Symbol('x%s' % i) for i in range(0, 10)] # Dummy "optimization" functions for testing. def opt1(expr): return expr + y def opt2(expr): return expr*z def test_preprocess_for_cse(): assert cse_main.preprocess_for_cse(x, [(opt1, None)]) == x + y assert cse_main.preprocess_for_cse(x, [(None, opt1)]) == x assert cse_main.preprocess_for_cse(x, [(None, None)]) == x assert cse_main.preprocess_for_cse(x, [(opt1, opt2)]) == x + y assert cse_main.preprocess_for_cse( x, [(opt1, None), (opt2, None)]) == (x + y)*z def test_postprocess_for_cse(): assert cse_main.postprocess_for_cse(x, [(opt1, None)]) == x assert cse_main.postprocess_for_cse(x, [(None, opt1)]) == x + y assert cse_main.postprocess_for_cse(x, [(None, None)]) == x assert cse_main.postprocess_for_cse(x, [(opt1, opt2)]) == x*z # Note the reverse order of application. assert cse_main.postprocess_for_cse( x, [(None, opt1), (None, opt2)]) == x*z + y def test_cse_single(): # Simple substitution. e = Add(Pow(x + y, 2), sqrt(x + y)) substs, reduced = cse([e]) assert substs == [(x0, x + y)] assert reduced == [sqrt(x0) + x0**2] def test_cse_single2(): # Simple substitution, test for being able to pass the expression directly e = Add(Pow(x + y, 2), sqrt(x + y)) substs, reduced = cse(e) assert substs == [(x0, x + y)] assert reduced == [sqrt(x0) + x0**2] assert isinstance(cse(Matrix([[1]]))[1][0], Matrix) def test_cse_not_possible(): # No substitution possible. e = Add(x, y) substs, reduced = cse([e]) assert substs == [] assert reduced == [x + y] # issue 3230 eq = (meijerg((1, 2), (y, 4), (5,), [], x) + meijerg((1, 3), (y, 4), (5,), [], x)) assert cse(eq) == ([], [eq]) def test_nested_substitution(): # Substitution within a substitution. e = Add(Pow(w*x + y, 2), sqrt(w*x + y)) substs, reduced = cse([e]) assert substs == [(x0, w*x + y)] assert reduced == [sqrt(x0) + x0**2] def test_subtraction_opt(): # Make sure subtraction is optimized. e = (x - y)*(z - y) + exp((x - y)*(z - y)) substs, reduced = cse( [e], optimizations=[(cse_opts.sub_pre, cse_opts.sub_post)]) assert substs == [(x0, (x - y)*(y - z))] assert reduced == [-x0 + exp(-x0)] e = -(x - y)*(z - y) + exp(-(x - y)*(z - y)) substs, reduced = cse( [e], optimizations=[(cse_opts.sub_pre, cse_opts.sub_post)]) assert substs == [(x0, (x - y)*(y - z))] assert reduced == [x0 + exp(x0)] # issue 978 n = -1 + 1/x e = n/x/(-n)**2 - 1/n/x assert cse(e, optimizations=[(cse_opts.sub_pre, cse_opts.sub_post)]) == \ ([], [0]) def test_multiple_expressions(): e1 = (x + y)*z e2 = (x + y)*w substs, reduced = cse([e1, e2]) assert substs == [(x0, x + y)] assert reduced == [x0*z, x0*w] l = [w*x*y + z, w*y] substs, reduced = cse(l) rsubsts, _ = cse(reversed(l)) assert substs == rsubsts assert reduced == [z + x*x0, x0] l = [w*x*y, w*x*y + z, w*y] substs, reduced = cse(l) rsubsts, _ = cse(reversed(l)) assert substs == rsubsts assert reduced == [x1, x1 + z, x0] l = [(x - z)*(y - z), x - z, y - z] substs, reduced = cse(l) rsubsts, _ = cse(reversed(l)) assert substs == [(x0, -z), (x1, x + x0), (x2, x0 + y)] assert rsubsts == [(x0, -z), (x1, x0 + y), (x2, x + x0)] assert reduced == [x1*x2, x1, x2] l = [w*y + w + x + y + z, w*x*y] assert cse(l) == ([(x0, w*y)], [w + x + x0 + y + z, x*x0]) assert cse([x + y, x + y + z]) == ([(x0, x + y)], [x0, z + x0]) assert cse([x + y, x + z]) == ([], [x + y, x + z]) assert cse([x*y, z + x*y, x*y*z + 3]) == \ ([(x0, x*y)], [x0, z + x0, 3 + x0*z]) @XFAIL # CSE of non-commutative Mul terms is disabled def test_non_commutative_cse(): A, B, C = symbols('A B C', commutative=False) l = [A*B*C, A*C] assert cse(l) == ([], l) l = [A*B*C, A*B] assert cse(l) == ([(x0, A*B)], [x0*C, x0]) # Test if CSE of non-commutative Mul terms is disabled def test_bypass_non_commutatives(): A, B, C = symbols('A B C', commutative=False) l = [A*B*C, A*C] assert cse(l) == ([], l) l = [A*B*C, A*B] assert cse(l) == ([], l) l = [B*C, A*B*C] assert cse(l) == ([], l) @XFAIL # CSE fails when replacing non-commutative sub-expressions def test_non_commutative_order(): A, B, C = symbols('A B C', commutative=False) x0 = symbols('x0', commutative=False) l = [B+C, A*(B+C)] assert cse(l) == ([(x0, B+C)], [x0, A*x0]) @XFAIL def test_powers(): assert cse(x*y**2 + x*y) == ([(x0, x*y)], [x0*y + x0]) def test_issues_1399(): assert cse(w/(x - y) + z/(y - x), optimizations='basic') == \ ([], [(w - z)/(x - y)]) def test_issue_921(): assert cse(x**5 + x**4 + x**3 + x**2, optimizations='basic') \ == ([(x0, x**2)], [x0*(x**3 + x + x0 + 1)]) def test_issue_1104(): assert cse(sin(x**x)/x**x) == ([(x0, x**x)], [sin(x0)/x0]) def test_issue_3164(): e = Eq(x*(-x + 1) + x*(x - 1), 0) assert cse(e, optimizations='basic') == ([], [True]) def test_dont_cse_tuples(): from sympy import Subs f = Function("f") g = Function("g") name_val, (expr,) = cse( Subs(f(x, y), (x, y), (0, 1)) + Subs(g(x, y), (x, y), (0, 1))) assert name_val == [] assert expr == (Subs(f(x, y), (x, y), (0, 1)) + Subs(g(x, y), (x, y), (0, 1))) name_val, (expr,) = cse( Subs(f(x, y), (x, y), (0, x + y)) + Subs(g(x, y), (x, y), (0, x + y))) assert name_val == [(x0, x + y)] assert expr == Subs(f(x, y), (x, y), (0, x0)) + \ Subs(g(x, y), (x, y), (0, x0)) def test_pow_invpow(): assert cse(1/x**2 + x**2) == \ ([(x0, x**2)], [x0 + 1/x0]) assert cse(x**2 + (1 + 1/x**2)/x**2) == \ ([(x0, x**2), (x1, 1/x0)], [x0 + x1*(x1 + 1)]) assert cse(1/x**2 + (1 + 1/x**2)*x**2) == \ ([(x0, x**2), (x1, 1/x0)], [x0*(x1 + 1) + x1]) assert cse(cos(1/x**2) + sin(1/x**2)) == \ ([(x0, x**(-2))], [sin(x0) + cos(x0)]) assert cse(cos(x**2) + sin(x**2)) == \ ([(x0, x**2)], [sin(x0) + cos(x0)]) assert cse(y/(2 + x**2) + z/x**2/y) == \ ([(x0, x**2)], [y/(x0 + 2) + z/(x0*y)]) assert cse(exp(x**2) + x**2*cos(1/x**2)) == \ ([(x0, x**2)], [x0*cos(1/x0) + exp(x0)]) assert cse((1 + 1/x**2)/x**2) == \ ([(x0, x**(-2))], [x0*(x0 + 1)]) assert cse(x**(2*y) + x**(-2*y)) == \ ([(x0, x**(2*y))], [x0 + 1/x0]) def test_postprocess(): eq = (x + 1 + exp((x + 1)/(y + 1)) + cos(y + 1)) assert cse([eq, Eq(x, z + 1), z - 2, (z + 1)*(x + 1)], postprocess=cse_main.cse_separate) == \ [[(x1, y + 1), (x2, z + 1), (x, x2), (x0, x + 1)], [x0 + exp(x0/x1) + cos(x1), z - 2, x0*x2]] def test_issue1400(): # previously, this gave 16 constants from sympy.abc import a, b B = Function('B') G = Function('G') t = Tuple(* (a, a + S(1)/2, 2*a, b, 2*a - b + 1, (sqrt(z)/2)**(-2*a + 1)*B(2*a - b, sqrt(z))*B(b - 1, sqrt(z))*G(b)*G(2*a - b + 1), sqrt(z)*(sqrt(z)/2)**(-2*a + 1)*B(b, sqrt(z))*B(2*a - b, sqrt(z))*G(b)*G(2*a - b + 1), sqrt(z)*(sqrt(z)/2)**(-2*a + 1)*B(b - 1, sqrt(z))*B(2*a - b + 1, sqrt(z))*G(b)*G(2*a - b + 1), (sqrt(z)/2)**(-2*a + 1)*B(b, sqrt(z))*B(2*a - b + 1, sqrt(z))*G(b)*G(2*a - b + 1), 1, 0, S(1)/2, z/2, -b + 1, -2*a + b, -2*a)) c = cse(t) ans = ( [(x0, 2*a), (x1, -b), (x2, x1 + 1), (x3, x0 + x2), (x4, sqrt(z)), (x5, B(x0 + x1, x4)), (x6, G(b)), (x7, G(x3)), (x8, -x0), (x9, (x4/2)**(x8 + 1)), (x10, x6*x7*x9*B(b - 1, x4)), (x11, x6*x7*x9*B(b, x4)), (x12, B(x3, x4))], [(a, a + S(1)/2, x0, b, x3, x10*x5, x11*x4*x5, x10*x12*x4, x11*x12, 1, 0, S(1)/2, z/2, x2, b + x8, x8)]) assert ans == c def test_issue_3070(): r = RootOf(x**6 - 4*x**5 - 2, 1) assert cse(r) == ([], [r]) # and a check that the right thing is done with the new # mechanism assert sub_post(sub_pre((-x - y)*z - x - y)) == -z*(x + y) - x - y sympy-0.7.4.1/sympy/simplify/tests/test_sqrtdenest.py0000644000175000017500000001447212253362407023240 0ustar georgeskgeorgeskfrom sympy import sqrt, root, S, Symbol, sqrtdenest, Integral, cos from sympy.simplify.sqrtdenest import _subsets as subsets r2, r3, r5, r6, r7, r10, r15, r29 = [sqrt(x) for x in [2, 3, 5, 6, 7, 10, 15, 29]] def test_sqrtdenest(): d = {sqrt(5 + 2 * r6): r2 + r3, sqrt(5. + 2 * r6): sqrt(5. + 2 * r6), sqrt(5. + 4*sqrt(5 + 2 * r6)): sqrt(5.0 + 4*r2 + 4*r3), sqrt(r2): sqrt(r2), sqrt(5 + r7): sqrt(5 + r7), sqrt(3 + sqrt(5 + 2*r7)): 3*r2*(5 + 2*r7)**(S(1)/4)/(2*sqrt(6 + 3*r7)) + r2*sqrt(6 + 3*r7)/(2*(5 + 2*r7)**(S(1)/4)), sqrt(3 + 2*r3): 3**(S(3)/4)*(r6/2 + 3*r2/2)/3} for i in d: assert sqrtdenest(i) == d[i] def test_sqrtdenest2(): assert sqrtdenest(sqrt(16 - 2*r29 + 2*sqrt(55 - 10*r29))) == \ r5 + sqrt(11 - 2*r29) e = sqrt(-r5 + sqrt(-2*r29 + 2*sqrt(-10*r29 + 55) + 16)) assert sqrtdenest(e) == root(-2*r29 + 11, 4) r = sqrt(1 + r7) assert sqrtdenest(sqrt(1 + r)) == sqrt(1 + r) e = sqrt(((1 + sqrt(1 + 2*sqrt(3 + r2 + r5)))**2).expand()) assert sqrtdenest(e) == 1 + sqrt(1 + 2*sqrt(r2 + r5 + 3)) assert sqrtdenest(sqrt(5*r3 + 6*r2)) == \ sqrt(2)*root(3, 4) + root(3, 4)**3 assert sqrtdenest(sqrt(((1 + r5 + sqrt(1 + r3))**2).expand())) == \ 1 + r5 + sqrt(1 + r3) assert sqrtdenest(sqrt(((1 + r5 + r7 + sqrt(1 + r3))**2).expand())) == \ 1 + sqrt(1 + r3) + r5 + r7 e = sqrt(((1 + cos(2) + cos(3) + sqrt(1 + r3))**2).expand()) assert sqrtdenest(e) == cos(3) + cos(2) + 1 + sqrt(1 + r3) e = sqrt(-2*r10 + 2*r2*sqrt(-2*r10 + 11) + 14) assert sqrtdenest(e) == sqrt(-2*r10 - 2*r2 + 4*r5 + 14) # check that the result is not more complicated than the input z = sqrt(-2*r29 + cos(2) + 2*sqrt(-10*r29 + 55) + 16) assert sqrtdenest(z) == z assert sqrtdenest(sqrt(r6 + sqrt(15))) == sqrt(r6 + sqrt(15)) z = sqrt(15 - 2*sqrt(31) + 2*sqrt(55 - 10*r29)) assert sqrtdenest(z) == z def test_sqrtdenest_rec(): assert sqrtdenest(sqrt(-4*sqrt(14) - 2*r6 + 4*sqrt(21) + 33)) == \ -r2 + r3 + 2*r7 assert sqrtdenest(sqrt(-28*r7 - 14*r5 + 4*sqrt(35) + 82)) == \ -7 + r5 + 2*r7 assert sqrtdenest(sqrt(6*r2/11 + 2*sqrt(22)/11 + 6*sqrt(11)/11 + 2)) == \ sqrt(11)*(r2 + 3 + sqrt(11))/11 assert sqrtdenest(sqrt(468*r3 + 3024*r2 + 2912*r6 + 19735)) == \ 9*r3 + 26 + 56*r6 z = sqrt(-490*r3 - 98*sqrt(115) - 98*sqrt(345) - 2107) assert sqrtdenest(z) == sqrt(-1)*(7*r5 + 7*r15 + 7*sqrt(23)) z = sqrt(-4*sqrt(14) - 2*r6 + 4*sqrt(21) + 34) assert sqrtdenest(z) == z assert sqrtdenest(sqrt(-8*r2 - 2*r5 + 18)) == -r10 + 1 + r2 + r5 assert sqrtdenest(sqrt(8*r2 + 2*r5 - 18)) == \ sqrt(-1)*(-r10 + 1 + r2 + r5) assert sqrtdenest(sqrt(8*r2/3 + 14*r5/3 + S(154)/9)) == \ -r10/3 + r2 + r5 + 3 assert sqrtdenest(sqrt(sqrt(2*r6 + 5) + sqrt(2*r7 + 8))) == \ sqrt(1 + r2 + r3 + r7) assert sqrtdenest(sqrt(4*r15 + 8*r5 + 12*r3 + 24)) == 1 + r3 + r5 + r15 w = 1 + r2 + r3 + r5 + r7 assert sqrtdenest(sqrt((w**2).expand())) == w z = sqrt((w**2).expand() + 1) assert sqrtdenest(z) == z z = sqrt(2*r10 + 6*r2 + 4*r5 + 12 + 10*r15 + 30*r3) assert sqrtdenest(z) == z def test_issue3142(): z = sqrt( -320 + 32*sqrt(5) + 64*r15) assert sqrtdenest(z) == z def test_sqrtdenest3(): z = sqrt(13 - 2*r10 + 2*r2*sqrt(-2*r10 + 11)) assert sqrtdenest(z) == -1 + r2 + r10 assert sqrtdenest(z, max_iter=1) == -1 + sqrt(2) + sqrt(10) n = sqrt(2*r6/7 + 2*r7/7 + 2*sqrt(42)/7 + 2) d = sqrt(16 - 2*r29 + 2*sqrt(55 - 10*r29)) assert sqrtdenest(n/d).equals( r7*(1 + r6 + r7)/(7*(sqrt(-2*r29 + 11) + r5))) z = sqrt(sqrt(r2 + 2) + 2) assert sqrtdenest(z) == z assert sqrtdenest(sqrt(-2*r10 + 4*r2*sqrt(-2*r10 + 11) + 20)) == \ sqrt(-2*r10 - 4*r2 + 8*r5 + 20) assert sqrtdenest(sqrt((112 + 70*r2) + (46 + 34*r2)*r5)) == \ r10 + 5 + 4*r2 + 3*r5 z = sqrt(5 + sqrt(2*r6 + 5)*sqrt(-2*r29 + 2*sqrt(-10*r29 + 55) + 16)) r = sqrt(-2*r29 + 11) assert sqrtdenest(z) == sqrt(r2*r + r3*r + r10 + r15 + 5) def test_sqrtdenest4(): # see Denest_en.pdf in http://code.google.com/p/sympy/issues/detail?id=93 z = sqrt(8 - r2*sqrt(5 - r5) - sqrt(3)*(1 + r5)) z1 = sqrtdenest(z) c = sqrt(-r5 + 5) z1 = ((-r15*c - r3*c + c + r5*c - r6 - r2 + r10 + sqrt(30))/4).expand() assert sqrtdenest(z) == z1 z = sqrt(2*r2*sqrt(r2 + 2) + 5*r2 + 4*sqrt(r2 + 2) + 8) assert sqrtdenest(z) == r2 + sqrt(r2 + 2) + 2 w = 2 + r2 + r3 + (1 + r3)*sqrt(2 + r2 + 5*r3) z = sqrt((w**2).expand()) assert sqrtdenest(z) == w.expand() def test_sqrt_symbolic_denest(): x = Symbol('x') z = sqrt(((1 + sqrt(sqrt(2 + x) + 3))**2).expand()) assert sqrtdenest(z) == sqrt((1 + sqrt(sqrt(2 + x) + 3))**2) z = sqrt(((1 + sqrt(sqrt(2 + cos(1)) + 3))**2).expand()) assert sqrtdenest(z) == 1 + sqrt(sqrt(2 + cos(1)) + 3) z = ((1 + cos(2))**4 + 1).expand() assert sqrtdenest(z) == z z = sqrt(((1 + sqrt(sqrt(2 + cos(3*x)) + 3))**2 + 1).expand()) assert sqrtdenest(z) == z c = cos(3) c2 = c**2 assert sqrtdenest(sqrt(2*sqrt(1 + r3)*c + c2 + 1 + r3*c2)) == \ -1 - sqrt(1 + r3)*c ra = sqrt(1 + r3) z = sqrt(20*ra*sqrt(3 + 3*r3) + 12*r3*ra*sqrt(3 + 3*r3) + 64*r3 + 112) assert sqrtdenest(z) == z def test_issue_2758(): from sympy.abc import x, y z = sqrt(1/(4*r3 + 7) + 1) ans = (r2 + r6)/(r3 + 2) assert sqrtdenest(z) == ans assert sqrtdenest(1 + z) == 1 + ans assert sqrtdenest(Integral(z + 1, (x, 1, 2))) == \ Integral(1 + ans, (x, 1, 2)) assert sqrtdenest(x + sqrt(y)) == x + sqrt(y) ans = (r2 + r6)/(r3 + 2) assert sqrtdenest(z) == ans assert sqrtdenest(1 + z) == 1 + ans assert sqrtdenest(Integral(z + 1, (x, 1, 2))) == \ Integral(1 + ans, (x, 1, 2)) assert sqrtdenest(x + sqrt(y)) == x + sqrt(y) def test_subsets(): assert subsets(1) == [[1]] assert subsets(4) == [ [1, 0, 0, 0], [0, 1, 0, 0], [1, 1, 0, 0], [0, 0, 1, 0], [1, 0, 1, 0], [0, 1, 1, 0], [1, 1, 1, 0], [0, 0, 0, 1], [1, 0, 0, 1], [0, 1, 0, 1], [1, 1, 0, 1], [0, 0, 1, 1], [1, 0, 1, 1], [0, 1, 1, 1], [1, 1, 1, 1]] def test_issue_2554(): assert sqrtdenest( sqrt(2 + sqrt(2 + sqrt(2)))) == sqrt(2 + sqrt(2 + sqrt(2))) sympy-0.7.4.1/sympy/simplify/tests/test_simplify.py0000644000175000017500000021133112253362407022671 0ustar georgeskgeorgeskfrom sympy import ( acos, Add, atan, besselsimp, binomial, collect, collect_const, combsimp, cos, cosh, cot, coth, count_ops, Derivative, diff, Dummy, E, Eq, erf, exp, exp_polar, expand, exptrigsimp, factor, factorial, FallingFactorial, Float, fraction, Function, gamma, GoldenRatio, hyper, hyper, hypersimp, I, Integer, Integral, integrate, log, logcombine, Matrix, Mul, nsimplify, O, oo, pi, Piecewise, polar_lift, polarify, posify, powdenest, powsimp, radsimp, Rational, ratsimp, ratsimpmodprime, rcollect, RisingFactorial, root, S, separatevars, signsimp, simplify, sin, sinh, solve, sqrt, Subs, Symbol, symbols, sympify, tan, tanh, trigsimp, Wild, Basic, ordered, expand_multinomial, denom) from sympy.core.mul import _keep_coeff from sympy.simplify.simplify import ( collect_sqrt, fraction_expand, _unevaluated_Add, nthroot) from sympy.utilities.pytest import XFAIL, slow from sympy.abc import x, y, z, t, a, b, c, d, e, f, g, h, i, k def test_ratsimp(): f, g = 1/x + 1/y, (x + y)/(x*y) assert f != g and ratsimp(f) == g f, g = 1/(1 + 1/x), 1 - 1/(x + 1) assert f != g and ratsimp(f) == g f, g = x/(x + y) + y/(x + y), 1 assert f != g and ratsimp(f) == g f, g = -x - y - y**2/(x + y) + x**2/(x + y), -2*y assert f != g and ratsimp(f) == g f = (a*c*x*y + a*c*z - b*d*x*y - b*d*z - b*t*x*y - b*t*x - b*t*z + e*x)/(x*y + z) G = [a*c - b*d - b*t + (-b*t*x + e*x)/(x*y + z), a*c - b*d - b*t - ( b*t*x - e*x)/(x*y + z)] assert f != g and ratsimp(f) in G A = sqrt(pi) B = log(erf(x) - 1) C = log(erf(x) + 1) D = 8 - 8*erf(x) f = A*B/D - A*C/D + A*C*erf(x)/D - A*B*erf(x)/D + 2*A/D assert ratsimp(f) == A*B/8 - A*C/8 - A/(4*erf(x) - 4) def test_ratsimpmodprime(): a = y**5 + x + y b = x - y F = [x*y**5 - x - y] assert ratsimpmodprime(a/b, F, x, y, order='lex') == \ (x**2 + x*y + x + y) / (x**2 - x*y) a = x + y**2 - 2 b = x + y**2 - y - 1 F = [x*y - 1] assert ratsimpmodprime(a/b, F, x, y, order='lex') == \ (1 + y - x)/(y - x) a = 5*x**3 + 21*x**2 + 4*x*y + 23*x + 12*y + 15 b = 7*x**3 - y*x**2 + 31*x**2 + 2*x*y + 15*y + 37*x + 21 F = [x**2 + y**2 - 1] assert ratsimpmodprime(a/b, F, x, y, order='lex') == \ (1 + 5*y - 5*x)/(8*y - 6*x) a = x*y - x - 2*y + 4 b = x + y**2 - 2*y F = [x - 2, y - 3] assert ratsimpmodprime(a/b, F, x, y, order='lex') == \ Rational(2, 5) # Test a bug where denominators would be dropped assert ratsimpmodprime(x, [y - 2*x], order='lex') == \ y/2 def test_trigsimp1(): x, y = symbols('x,y') assert trigsimp(1 - sin(x)**2) == cos(x)**2 assert trigsimp(1 - cos(x)**2) == sin(x)**2 assert trigsimp(sin(x)**2 + cos(x)**2) == 1 assert trigsimp(1 + tan(x)**2) == 1/cos(x)**2 assert trigsimp(1/cos(x)**2 - 1) == tan(x)**2 assert trigsimp(1/cos(x)**2 - tan(x)**2) == 1 assert trigsimp(1 + cot(x)**2) == 1/sin(x)**2 assert trigsimp(1/sin(x)**2 - 1) == 1/tan(x)**2 assert trigsimp(1/sin(x)**2 - cot(x)**2) == 1 assert trigsimp(5*cos(x)**2 + 5*sin(x)**2) == 5 assert trigsimp(5*cos(x/2)**2 + 2*sin(x/2)**2) == 3*cos(x)/2 + S(7)/2 assert trigsimp(sin(x)/cos(x)) == tan(x) assert trigsimp(2*tan(x)*cos(x)) == 2*sin(x) assert trigsimp(cot(x)**3*sin(x)**3) == cos(x)**3 assert trigsimp(y*tan(x)**2/sin(x)**2) == y/cos(x)**2 assert trigsimp(cot(x)/cos(x)) == 1/sin(x) assert trigsimp(sin(x + y) + sin(x - y)) == 2*sin(x)*cos(y) assert trigsimp(sin(x + y) - sin(x - y)) == 2*sin(y)*cos(x) assert trigsimp(cos(x + y) + cos(x - y)) == 2*cos(x)*cos(y) assert trigsimp(cos(x + y) - cos(x - y)) == -2*sin(x)*sin(y) assert ratsimp(trigsimp(tan(x + y) - tan(x)/(1 - tan(x)*tan(y)))) == \ sin(y)/(-sin(y)*tan(x) + cos(y)) # -tan(y)/(tan(x)*tan(y) - 1) assert trigsimp(sinh(x + y) + sinh(x - y)) == 2*sinh(x)*cosh(y) assert trigsimp(sinh(x + y) - sinh(x - y)) == 2*sinh(y)*cosh(x) assert trigsimp(cosh(x + y) + cosh(x - y)) == 2*cosh(x)*cosh(y) assert trigsimp(cosh(x + y) - cosh(x - y)) == 2*sinh(x)*sinh(y) assert ratsimp(trigsimp(tanh(x + y) - tanh(x)/(1 + tanh(x)*tanh(y)))) == \ sinh(y)/(sinh(y)*tanh(x) + cosh(y)) assert trigsimp(cos(0.12345)**2 + sin(0.12345)**2) == 1 e = 2*sin(x)**2 + 2*cos(x)**2 assert trigsimp(log(e)) == log(2) def test_trigsimp1a(): assert trigsimp(sin(2)**2*cos(3)*exp(2)/cos(2)**2) == tan(2)**2*cos(3)*exp(2) assert trigsimp(tan(2)**2*cos(3)*exp(2)*cos(2)**2) == sin(2)**2*cos(3)*exp(2) assert trigsimp(cot(2)*cos(3)*exp(2)*sin(2)) == cos(3)*exp(2)*cos(2) assert trigsimp(tan(2)*cos(3)*exp(2)/sin(2)) == cos(3)*exp(2)/cos(2) assert trigsimp(cot(2)*cos(3)*exp(2)/cos(2)) == cos(3)*exp(2)/sin(2) assert trigsimp(cot(2)*cos(3)*exp(2)*tan(2)) == cos(3)*exp(2) assert trigsimp(sinh(2)*cos(3)*exp(2)/cosh(2)) == tanh(2)*cos(3)*exp(2) assert trigsimp(tanh(2)*cos(3)*exp(2)*cosh(2)) == sinh(2)*cos(3)*exp(2) assert trigsimp(coth(2)*cos(3)*exp(2)*sinh(2)) == cosh(2)*cos(3)*exp(2) assert trigsimp(tanh(2)*cos(3)*exp(2)/sinh(2)) == cos(3)*exp(2)/cosh(2) assert trigsimp(coth(2)*cos(3)*exp(2)/cosh(2)) == cos(3)*exp(2)/sinh(2) assert trigsimp(coth(2)*cos(3)*exp(2)*tanh(2)) == cos(3)*exp(2) def test_trigsimp2(): x, y = symbols('x,y') assert trigsimp(cos(x)**2*sin(y)**2 + cos(x)**2*cos(y)**2 + sin(x)**2, recursive=True) == 1 assert trigsimp(sin(x)**2*sin(y)**2 + sin(x)**2*cos(y)**2 + cos(x)**2, recursive=True) == 1 assert trigsimp( Subs(x, x, sin(y)**2 + cos(y)**2)) == Subs(x, x, 1) def test_issue1274(): x = Symbol("x") assert abs(trigsimp(2.0*sin(x)**2 + 2.0*cos(x)**2) - 2.0) < 1e-10 def test_trigsimp3(): x, y = symbols('x,y') assert trigsimp(sin(x)/cos(x)) == tan(x) assert trigsimp(sin(x)**2/cos(x)**2) == tan(x)**2 assert trigsimp(sin(x)**3/cos(x)**3) == tan(x)**3 assert trigsimp(sin(x)**10/cos(x)**10) == tan(x)**10 assert trigsimp(cos(x)/sin(x)) == 1/tan(x) assert trigsimp(cos(x)**2/sin(x)**2) == 1/tan(x)**2 assert trigsimp(cos(x)**10/sin(x)**10) == 1/tan(x)**10 assert trigsimp(tan(x)) == trigsimp(sin(x)/cos(x)) def test_1562(): a, x, y = symbols('a x y') eq = -4*sin(x)**4 + 4*cos(x)**4 - 8*cos(x)**2 assert trigsimp(eq) == -4 n = sin(x)**6 + 4*sin(x)**4*cos(x)**2 + 5*sin(x)**2*cos(x)**4 + 2*cos(x)**6 d = -sin(x)**2 - 2*cos(x)**2 assert simplify(n/d) == -1 assert trigsimp(-2*cos(x)**2 + cos(x)**4 - sin(x)**4) == -1 eq = (- sin(x)**3/4)*cos(x) + (cos(x)**3/4)*sin(x) - sin(2*x)*cos(2*x)/8 assert trigsimp(eq) == 0 def test_1395(): a, b = symbols('a b') eq = sin(a)**2*sin(b)**2 + cos(a)**2*cos(b)**2*tan(a)**2 + cos(a)**2 assert trigsimp(eq) == 1 def test_2849(): a, x, y = symbols('a x y') assert trigsimp(diff(integrate(cos(x)/sin(x)**7, x), x)) == \ cos(x)/sin(x)**7 def test_1676(): a, x, y = symbols('a x y') assert trigsimp(sin(x)*cos(y)+cos(x)*sin(y)) == sin(x + y) assert trigsimp(sin(x)*cos(y)+cos(x)*sin(y)+3) == sin(x + y) + 3 def test_1181(): a, x, y = symbols('a x y') assert trigsimp(cos(x)**2 + cos(y)**2*sin(x)**2 + sin(y)**2*sin(x)**2) == 1 assert trigsimp(a**2*sin(x)**2 + a**2*cos(y)**2*cos(x)**2 + a**2*cos(x)**2*sin(y)**2) == a**2 assert trigsimp(a**2*cos(y)**2*sin(x)**2 + a**2*sin(y)**2*sin(x)**2) == a**2*sin(x)**2 def test_111(): eqs = (sin(2)*cos(3) + sin(3)*cos(2), -sin(2)*sin(3) + cos(2)*cos(3), sin(2)*cos(3) - sin(3)*cos(2), sin(2)*sin(3) + cos(2)*cos(3), sin(2)*sin(3) + cos(2)*cos(3) + cos(2), sinh(2)*cosh(3) + sinh(3)*cosh(2), sinh(2)*sinh(3) + cosh(2)*cosh(3), ) assert [trigsimp(e) for e in eqs] == [ sin(5), cos(5), -sin(1), cos(1), cos(1) + cos(2), sinh(5), cosh(5), ] def test_trigsimp_issues(): a, x, y = symbols('a x y') # 1526 - factor_terms works, too assert trigsimp(sin(x)**3 + cos(x)**2*sin(x)) == sin(x) # issue 2849 assert trigsimp(diff(integrate(cos(x)/sin(x)**3, x), x)) == \ cos(x)/sin(x)**3 assert trigsimp(diff(integrate(sin(x)/cos(x)**3, x), x)) == \ sin(x)/cos(x)**3 # check integer exponents e = sin(x)**y/cos(x)**y assert trigsimp(e) == e assert trigsimp(e.subs(y, 2)) == tan(x)**2 assert trigsimp(e.subs(x, 1)) == tan(1)**y # check for multiple patterns assert (cos(x)**2/sin(x)**2*cos(y)**2/sin(y)**2).trigsimp() == \ 1/tan(x)**2/tan(y)**2 assert trigsimp(cos(x)/sin(x)*cos(x+y)/sin(x+y)) == \ 1/(tan(x)*tan(x + y)) eq = cos(2)*(cos(3) + 1)**2/(cos(3) - 1)**2 assert trigsimp(eq) == eq.factor() # factor makes denom (-1 + cos(3))**2 assert trigsimp(cos(2)*(cos(3) + 1)**2*(cos(3) - 1)**2) == \ cos(2)*sin(3)**4 # issue 3690; this generates an expression that formerly caused # trigsimp to hang assert cot(x).equals(tan(x)) is False # nan or the unchanged expression is ok, but not sin(1) z = cos(x)**2 + sin(x)**2 - 1 z1 = tan(x)**2 - 1/cot(x)**2 n = (1 + z1/z) assert trigsimp(sin(n)) != sin(1) eq = x*(n - 1) - x*n assert trigsimp(eq) is S.NaN assert trigsimp(eq, recursive=True) is S.NaN assert trigsimp(1).is_Integer assert trigsimp(-sin(x)**4 - 2*sin(x)**2*cos(x)**2 - cos(x)**4) == -1 def test_trigsimp_issue_2515(): x = Symbol('x') assert trigsimp(x*cos(x)*tan(x)) == x*sin(x) assert trigsimp(-sin(x) + cos(x)*tan(x)) == 0 def test_issue_3826(): assert trigsimp(tan(2*x).expand(trig=True)) == tan(2*x) def test_trigsimp_issue_4032(): n = Symbol('n', integer=True, positive=True) assert trigsimp(2**(n/2)*cos(pi*n/4)/2 + 2**(n - 1)/2) == \ 2**(n/2)*cos(pi*n/4)/2 + 2**n/4 def test_trigsimp_noncommutative(): x, y = symbols('x,y') A, B = symbols('A,B', commutative=False) assert trigsimp(A - A*sin(x)**2) == A*cos(x)**2 assert trigsimp(A - A*cos(x)**2) == A*sin(x)**2 assert trigsimp(A*sin(x)**2 + A*cos(x)**2) == A assert trigsimp(A + A*tan(x)**2) == A/cos(x)**2 assert trigsimp(A/cos(x)**2 - A) == A*tan(x)**2 assert trigsimp(A/cos(x)**2 - A*tan(x)**2) == A assert trigsimp(A + A*cot(x)**2) == A/sin(x)**2 assert trigsimp(A/sin(x)**2 - A) == A/tan(x)**2 assert trigsimp(A/sin(x)**2 - A*cot(x)**2) == A assert trigsimp(y*A*cos(x)**2 + y*A*sin(x)**2) == y*A assert trigsimp(A*sin(x)/cos(x)) == A*tan(x) assert trigsimp(A*tan(x)*cos(x)) == A*sin(x) assert trigsimp(A*cot(x)**3*sin(x)**3) == A*cos(x)**3 assert trigsimp(y*A*tan(x)**2/sin(x)**2) == y*A/cos(x)**2 assert trigsimp(A*cot(x)/cos(x)) == A/sin(x) assert trigsimp(A*sin(x + y) + A*sin(x - y)) == 2*A*sin(x)*cos(y) assert trigsimp(A*sin(x + y) - A*sin(x - y)) == 2*A*sin(y)*cos(x) assert trigsimp(A*cos(x + y) + A*cos(x - y)) == 2*A*cos(x)*cos(y) assert trigsimp(A*cos(x + y) - A*cos(x - y)) == -2*A*sin(x)*sin(y) assert trigsimp(A*sinh(x + y) + A*sinh(x - y)) == 2*A*sinh(x)*cosh(y) assert trigsimp(A*sinh(x + y) - A*sinh(x - y)) == 2*A*sinh(y)*cosh(x) assert trigsimp(A*cosh(x + y) + A*cosh(x - y)) == 2*A*cosh(x)*cosh(y) assert trigsimp(A*cosh(x + y) - A*cosh(x - y)) == 2*A*sinh(x)*sinh(y) assert trigsimp(A*cos(0.12345)**2 + A*sin(0.12345)**2) == 1.0*A def test_hyperbolic_simp(): x, y = symbols('x,y') assert trigsimp(sinh(x)**2 + 1) == cosh(x)**2 assert trigsimp(cosh(x)**2 - 1) == sinh(x)**2 assert trigsimp(cosh(x)**2 - sinh(x)**2) == 1 assert trigsimp(1 - tanh(x)**2) == 1/cosh(x)**2 assert trigsimp(1 - 1/cosh(x)**2) == tanh(x)**2 assert trigsimp(tanh(x)**2 + 1/cosh(x)**2) == 1 assert trigsimp(coth(x)**2 - 1) == 1/sinh(x)**2 assert trigsimp(1/sinh(x)**2 + 1) == 1/tanh(x)**2 assert trigsimp(coth(x)**2 - 1/sinh(x)**2) == 1 assert trigsimp(5*cosh(x)**2 - 5*sinh(x)**2) == 5 assert trigsimp(5*cosh(x/2)**2 - 2*sinh(x/2)**2) == 3*cosh(x)/2 + S(7)/2 assert trigsimp(sinh(x)/cosh(x)) == tanh(x) assert trigsimp(tanh(x)) == trigsimp(sinh(x)/cosh(x)) assert trigsimp(cosh(x)/sinh(x)) == 1/tanh(x) assert trigsimp(2*tanh(x)*cosh(x)) == 2*sinh(x) assert trigsimp(coth(x)**3*sinh(x)**3) == cosh(x)**3 assert trigsimp(y*tanh(x)**2/sinh(x)**2) == y/cosh(x)**2 assert trigsimp(coth(x)/cosh(x)) == 1/sinh(x) e = 2*cosh(x)**2 - 2*sinh(x)**2 assert trigsimp(log(e)) == log(2) assert trigsimp(cosh(x)**2*cosh(y)**2 - cosh(x)**2*sinh(y)**2 - sinh(x)**2, recursive=True) == 1 assert trigsimp(sinh(x)**2*sinh(y)**2 - sinh(x)**2*cosh(y)**2 + cosh(x)**2, recursive=True) == 1 assert abs(trigsimp(2.0*cosh(x)**2 - 2.0*sinh(x)**2) - 2.0) < 1e-10 assert trigsimp(sinh(x)**2/cosh(x)**2) == tanh(x)**2 assert trigsimp(sinh(x)**3/cosh(x)**3) == tanh(x)**3 assert trigsimp(sinh(x)**10/cosh(x)**10) == tanh(x)**10 assert trigsimp(cosh(x)**3/sinh(x)**3) == 1/tanh(x)**3 assert trigsimp(cosh(x)/sinh(x)) == 1/tanh(x) assert trigsimp(cosh(x)**2/sinh(x)**2) == 1/tanh(x)**2 assert trigsimp(cosh(x)**10/sinh(x)**10) == 1/tanh(x)**10 assert trigsimp(x*cosh(x)*tanh(x)) == x*sinh(x) assert trigsimp(-sinh(x) + cosh(x)*tanh(x)) == 0 assert tan(x) != 1/cot(x) # cot doesn't auto-simplify assert trigsimp(tan(x) - 1/cot(x)) == 0 assert trigsimp(3*tanh(x)**7 - 2/coth(x)**7) == tanh(x)**7 def test_trigsimp_groebner(): from sympy.simplify.simplify import trigsimp_groebner c = cos(x) s = sin(x) ex = (4*s*c + 12*s + 5*c**3 + 21*c**2 + 23*c + 15)/( -s*c**2 + 2*s*c + 15*s + 7*c**3 + 31*c**2 + 37*c + 21) resnum = (5*s - 5*c + 1) resdenom = (8*s - 6*c) results = [resnum/resdenom, (-resnum)/(-resdenom)] assert trigsimp_groebner(ex) in results assert trigsimp_groebner(s/c, hints=[tan]) == tan(x) assert trigsimp((-s + 1)/c + c/(-s + 1), method='groebner') == 2/c assert trigsimp((-s + 1)/c + c/(-s + 1), method='groebner', polynomial=True) == 2/c # Test quick=False works assert trigsimp_groebner(ex, hints=[2]) in results # test "I" assert trigsimp_groebner(sin(I*x)/cos(I*x), hints=[tanh]) == I*tanh(x) # test hyperbolic / sums assert trigsimp_groebner((tanh(x)+tanh(y))/(1+tanh(x)*tanh(y)), hints=[(tanh, x, y)]) == tanh(x + y) @XFAIL def test_factorial_simplify(): # There are more tests in test_factorials.py. These are just to # ensure that simplify() calls factorial_simplify correctly from sympy.specfun.factorials import factorial x = Symbol('x') assert simplify(factorial(x)/x) == factorial(x - 1) assert simplify(factorial(factorial(x))) == factorial(factorial(x)) def test_simplify_expr(): x, y, z, k, n, m, w, f, s, A = symbols('x,y,z,k,n,m,w,f,s,A') assert all(simplify(tmp) == tmp for tmp in [I, E, oo, x, -x, -oo, -E, -I]) e = 1/x + 1/y assert e != (x + y)/(x*y) assert simplify(e) == (x + y)/(x*y) e = A**2*s**4/(4*pi*k*m**3) assert simplify(e) == e e = (4 + 4*x - 2*(2 + 2*x))/(2 + 2*x) assert simplify(e) == 0 e = (-4*x*y**2 - 2*y**3 - 2*x**2*y)/(x + y)**2 assert simplify(e) == -2*y e = -x - y - (x + y)**(-1)*y**2 + (x + y)**(-1)*x**2 assert simplify(e) == -2*y e = (x + x*y)/x assert simplify(e) == 1 + y e = (f(x) + y*f(x))/f(x) assert simplify(e) == 1 + y e = (2 * (1/n - cos(n * pi)/n))/pi assert simplify(e) == (-cos(pi*n) + 1)/(pi*n)*2 e = integrate(1/(x**3 + 1), x).diff(x) assert simplify(e) == 1/(x**3 + 1) e = integrate(x/(x**2 + 3*x + 1), x).diff(x) assert simplify(e) == x/(x**2 + 3*x + 1) A = Matrix([[2*k - m*w**2, -k], [-k, k - m*w**2]]).inv() assert simplify((A*Matrix([0, f]))[1]) == \ -f*(2*k - m*w**2)/(k**2 - (k - m*w**2)*(2*k - m*w**2)) f = -x + y/(z + t) + z*x/(z + t) + z*a/(z + t) + t*x/(z + t) assert simplify(f) == (y + a*z)/(z + t) A, B = symbols('A,B', commutative=False) assert simplify(A*B - B*A) == A*B - B*A assert simplify(A/(1 + y/x)) == x*A/(x + y) assert simplify(A*(1/x + 1/y)) == A/x + A/y #(x + y)*A/(x*y) assert simplify(log(2) + log(3)) == log(6) assert simplify(log(2*x) - log(2)) == log(x) assert simplify(hyper([], [], x)) == exp(x) def test_issue_458(): f_1 = x*a + y*b + z*c - 1 f_2 = x*d + y*e + z*f - 1 f_3 = x*g + y*h + z*i - 1 solutions = solve([f_1, f_2, f_3], x, y, z, simplify=False) assert simplify(solutions[y]) == \ (a*i + c*d + f*g - a*f - c*g - d*i)/ \ (a*e*i + b*f*g + c*d*h - a*f*h - b*d*i - c*e*g) def test_simplify_other(): assert simplify(sin(x)**2 + cos(x)**2) == 1 assert simplify(gamma(x + 1)/gamma(x)) == x assert simplify(sin(x)**2 + cos(x)**2 + factorial(x)/gamma(x)) == 1 + x assert simplify( Eq(sin(x)**2 + cos(x)**2, factorial(x)/gamma(x))) == Eq(1, x) nc = symbols('nc', commutative=False) assert simplify(x + x*nc) == x*(1 + nc) # issue 3024 # f = exp(-I*(k*sqrt(t) + x/(2*sqrt(t)))**2) # ans = integrate(f, (k, -oo, oo), conds='none') ans = I*(-pi*x*exp(-3*I*pi/4 + I*x**2/(4*t))*erf(x*exp(-3*I*pi/4)/ (2*sqrt(t)))/(2*sqrt(t)) + pi*x*exp(-3*I*pi/4 + I*x**2/(4*t))/ (2*sqrt(t)))*exp(-I*x**2/(4*t))/(sqrt(pi)*x) - I*sqrt(pi) * \ (-erf(x*exp(I*pi/4)/(2*sqrt(t))) + 1)*exp(I*pi/4)/(2*sqrt(t)) assert simplify(ans) == -(-1)**(S(3)/4)*sqrt(pi)/sqrt(t) # issue 3271 assert simplify(2**(2 + x)/4) == 2**x def test_simplify_complex(): cosAsExp = cos(x)._eval_rewrite_as_exp(x) tanAsExp = tan(x)._eval_rewrite_as_exp(x) assert simplify(cosAsExp*tanAsExp).expand() == ( sin(x))._eval_rewrite_as_exp(x).expand() # issue 1242 def test_simplify_ratio(): # roots of x**3-3*x+5 roots = ['(1/2 - sqrt(3)*I/2)*(sqrt(21)/2 + 5/2)**(1/3) + 1/((1/2 - ' 'sqrt(3)*I/2)*(sqrt(21)/2 + 5/2)**(1/3))', '1/((1/2 + sqrt(3)*I/2)*(sqrt(21)/2 + 5/2)**(1/3)) + ' '(1/2 + sqrt(3)*I/2)*(sqrt(21)/2 + 5/2)**(1/3)', '-(sqrt(21)/2 + 5/2)**(1/3) - 1/(sqrt(21)/2 + 5/2)**(1/3)'] for r in roots: r = S(r) assert count_ops(simplify(r, ratio=1)) <= count_ops(r) # If ratio=oo, simplify() is always applied: assert simplify(r, ratio=oo) is not r def test_simplify_measure(): measure1 = lambda expr: len(str(expr)) measure2 = lambda expr: -count_ops(expr) # Return the most complicated result expr = (x + 1)/(x + sin(x)**2 + cos(x)**2) assert measure1(simplify(expr, measure=measure1)) <= measure1(expr) assert measure2(simplify(expr, measure=measure2)) <= measure2(expr) def test_simplify_issue_1308(): assert simplify(exp(-Rational(1, 2)) + exp(-Rational(3, 2))) == \ (1 + E)*exp(-Rational(3, 2)) def test_issue_2553(): assert simplify(E + exp(-E)) == exp(-E) + E n = symbols('n', commutative=False) assert simplify(n + n**(-n)) == n + n**(-n) def test_simplify_fail1(): x = Symbol('x') y = Symbol('y') e = (x + y)**2/(-4*x*y**2 - 2*y**3 - 2*x**2*y) assert simplify(e) == 1 / (-2*y) def test_fraction(): x, y, z = map(Symbol, 'xyz') A = Symbol('A', commutative=False) assert fraction(Rational(1, 2)) == (1, 2) assert fraction(x) == (x, 1) assert fraction(1/x) == (1, x) assert fraction(x/y) == (x, y) assert fraction(x/2) == (x, 2) assert fraction(x*y/z) == (x*y, z) assert fraction(x/(y*z)) == (x, y*z) assert fraction(1/y**2) == (1, y**2) assert fraction(x/y**2) == (x, y**2) assert fraction((x**2 + 1)/y) == (x**2 + 1, y) assert fraction(x*(y + 1)/y**7) == (x*(y + 1), y**7) assert fraction(exp(-x), exact=True) == (exp(-x), 1) assert fraction(x*A/y) == (x*A, y) assert fraction(x*A**-1/y) == (x*A**-1, y) n = symbols('n', negative=True) assert fraction(exp(n)) == (1, exp(-n)) assert fraction(exp(-n)) == (exp(-n), 1) def test_powsimp(): x, y, z, n = symbols('x,y,z,n') f = Function('f') assert powsimp( 4**x * 2**(-x) * 2**(-x) ) == 1 assert powsimp( (-4)**x * (-2)**(-x) * 2**(-x) ) == 1 assert powsimp( f(4**x * 2**(-x) * 2**(-x)) ) == f(4**x * 2**(-x) * 2**(-x)) assert powsimp( f(4**x * 2**(-x) * 2**(-x)), deep=True ) == f(1) assert exp(x)*exp(y) == exp(x)*exp(y) assert powsimp(exp(x)*exp(y)) == exp(x + y) assert powsimp(exp(x)*exp(y)*2**x*2**y) == (2*E)**(x + y) assert powsimp(exp(x)*exp(y)*2**x*2**y, combine='exp') == \ exp(x + y)*2**(x + y) assert powsimp(exp(x)*exp(y)*exp(2)*sin(x) + sin(y) + 2**x*2**y) == \ exp(2 + x + y)*sin(x) + sin(y) + 2**(x + y) assert powsimp(sin(exp(x)*exp(y))) == sin(exp(x)*exp(y)) assert powsimp(sin(exp(x)*exp(y)), deep=True) == sin(exp(x + y)) assert powsimp(x**2*x**y) == x**(2 + y) # This should remain factored, because 'exp' with deep=True is supposed # to act like old automatic exponent combining. assert powsimp((1 + E*exp(E))*exp(-E), combine='exp', deep=True) == \ (1 + exp(1 + E))*exp(-E) assert powsimp((1 + E*exp(E))*exp(-E), deep=True) == \ (1 + exp(1 + E))*exp(-E) assert powsimp((1 + E*exp(E))*exp(-E)) == (1 + exp(1 + E))*exp(-E) assert powsimp((1 + E*exp(E))*exp(-E), combine='exp') == \ (1 + exp(1 + E))*exp(-E) assert powsimp((1 + E*exp(E))*exp(-E), combine='base') == \ (1 + E*exp(E))*exp(-E) x, y = symbols('x,y', nonnegative=True) n = Symbol('n', real=True) assert powsimp(y**n * (y/x)**(-n)) == x**n assert powsimp(x**(x**(x*y)*y**(x*y))*y**(x**(x*y)*y**(x*y)), deep=True) \ == (x*y)**(x*y)**(x*y) assert powsimp(2**(2**(2*x)*x), deep=False) == 2**(2**(2*x)*x) assert powsimp(2**(2**(2*x)*x), deep=True) == 2**(x*4**x) assert powsimp( exp(-x + exp(-x)*exp(-x*log(x))), deep=False, combine='exp') == \ exp(-x + exp(-x)*exp(-x*log(x))) assert powsimp( exp(-x + exp(-x)*exp(-x*log(x))), deep=False, combine='exp') == \ exp(-x + exp(-x)*exp(-x*log(x))) assert powsimp((x + y)/(3*z), deep=False, combine='exp') == (x + y)/(3*z) assert powsimp((x/3 + y/3)/z, deep=True, combine='exp') == (x/3 + y/3)/z assert powsimp(exp(x)/(1 + exp(x)*exp(y)), deep=True) == \ exp(x)/(1 + exp(x + y)) assert powsimp(x*y**(z**x*z**y), deep=True) == x*y**(z**(x + y)) assert powsimp((z**x*z**y)**x, deep=True) == (z**(x + y))**x assert powsimp(x*(z**x*z**y)**x, deep=True) == x*(z**(x + y))**x p = symbols('p', positive=True) assert powsimp((1/x)**log(2)/x) == (1/x)**(1 + log(2)) assert powsimp((1/p)**log(2)/p) == p**(-1 - log(2)) # coefficient of exponent can only be simplified for positive bases assert powsimp(2**(2*x)) == 4**x assert powsimp((-1)**(2*x)) == (-1)**(2*x) i = symbols('i', integer=True) assert powsimp((-1)**(2*i)) == 1 assert powsimp((-1)**(-x)) != (-1)**x # could be 1/((-1)**x), but is not # force=True overrides assumptions assert powsimp((-1)**(2*x), force=True) == 1 # rational exponents allow combining of negative terms w, n, m = symbols('w n m', negative=True) e = i/a # not a rational exponent if `a` is unknown ex = w**e*n**e*m**e assert powsimp(ex) == m**(i/a)*n**(i/a)*w**(i/a) e = i/3 ex = w**e*n**e*m**e assert powsimp(ex) == (-1)**i*(-m*n*w)**(i/3) e = (3 + i)/i ex = w**e*n**e*m**e assert powsimp(ex) == (-1)**(3*e)*(-m*n*w)**e eq = x**(2*a/3) # eq != (x**a)**(2/3) (try x = -1 and a = 3 to see) assert powsimp(eq).exp == eq.exp == 2*a/3 # powdenest goes the other direction assert powsimp(2**(2*x)) == 4**x assert powsimp(exp(p/2)) == exp(p/2) # issue 3269 eq = Mul(*[sqrt(Dummy(imaginary=True)) for i in range(3)]) assert powsimp(eq) == eq and eq.is_Mul def test_issue_3268(): z = -5*sqrt(2)/(2*sqrt(2*sqrt(29) + 29)) + sqrt(-sqrt(29)/29 + S(1)/2) assert Mul(*[powsimp(a) for a in Mul.make_args(z.normal())]) == 0 assert powsimp(z.normal()) == 0 assert simplify(z) == 0 assert powsimp(sqrt(2 + sqrt(3))*sqrt(2 - sqrt(3)) + 1) == 2 assert powsimp(z) != 0 def test_powsimp_negated_base(): assert powsimp((-x + y)/sqrt(x - y)) == -sqrt(x - y) assert powsimp((-x + y)*(-z + y)/sqrt(x - y)/sqrt(z - y)) == sqrt(x - y)*sqrt(z - y) p = symbols('p', positive=True) assert powsimp((-p)**a/p**a) == (-1)**a n = symbols('n', negative=True) assert powsimp((-n)**a/n**a) == (-1)**a # if x is 0 then the lhs is 0**a*oo**a which is not (-1)**a assert powsimp((-x)**a/x**a) != (-1)**a def test_issue_3341(): assert powsimp(16*2**a*8**b) == 2**(a + 3*b + 4) def test_powsimp_polar(): from sympy import polar_lift, exp_polar x, y, z = symbols('x y z') p, q, r = symbols('p q r', polar=True) assert (polar_lift(-1))**(2*x) == exp_polar(2*pi*I*x) assert powsimp(p**x * q**x) == (p*q)**x assert p**x * (1/p)**x == 1 assert (1/p)**x == p**(-x) assert exp_polar(x)*exp_polar(y) == exp_polar(x)*exp_polar(y) assert powsimp(exp_polar(x)*exp_polar(y)) == exp_polar(x + y) assert powsimp(exp_polar(x)*exp_polar(y)*p**x*p**y) == \ (p*exp_polar(1))**(x + y) assert powsimp(exp_polar(x)*exp_polar(y)*p**x*p**y, combine='exp') == \ exp_polar(x + y)*p**(x + y) assert powsimp( exp_polar(x)*exp_polar(y)*exp_polar(2)*sin(x) + sin(y) + p**x*p**y) \ == p**(x + y) + sin(x)*exp_polar(2 + x + y) + sin(y) assert powsimp(sin(exp_polar(x)*exp_polar(y))) == \ sin(exp_polar(x)*exp_polar(y)) assert powsimp(sin(exp_polar(x)*exp_polar(y)), deep=True) == \ sin(exp_polar(x + y)) def test_powsimp_nc(): x, y, z = symbols('x,y,z') A, B, C = symbols('A B C', commutative=False) assert powsimp(A**x*A**y, combine='all') == A**(x + y) assert powsimp(A**x*A**y, combine='base') == A**x*A**y assert powsimp(A**x*A**y, combine='exp') == A**(x + y) assert powsimp(A**x*B**x, combine='all') == (A*B)**x assert powsimp(A**x*B**x, combine='base') == (A*B)**x assert powsimp(A**x*B**x, combine='exp') == A**x*B**x assert powsimp(B**x*A**x, combine='all') == (B*A)**x assert powsimp(B**x*A**x, combine='base') == (B*A)**x assert powsimp(B**x*A**x, combine='exp') == B**x*A**x assert powsimp(A**x*A**y*A**z, combine='all') == A**(x + y + z) assert powsimp(A**x*A**y*A**z, combine='base') == A**x*A**y*A**z assert powsimp(A**x*A**y*A**z, combine='exp') == A**(x + y + z) assert powsimp(A**x*B**x*C**x, combine='all') == (A*B*C)**x assert powsimp(A**x*B**x*C**x, combine='base') == (A*B*C)**x assert powsimp(A**x*B**x*C**x, combine='exp') == A**x*B**x*C**x assert powsimp(B**x*A**x*C**x, combine='all') == (B*A*C)**x assert powsimp(B**x*A**x*C**x, combine='base') == (B*A*C)**x assert powsimp(B**x*A**x*C**x, combine='exp') == B**x*A**x*C**x def test_nthroot(): assert nthroot(90 + 34*sqrt(7), 3) == sqrt(7) + 3 q = 1 + sqrt(2) - 2*sqrt(3) + sqrt(6) + sqrt(7) assert nthroot(expand_multinomial(q**3), 3) == q assert nthroot(41 + 29*sqrt(2), 5) == 1 + sqrt(2) assert nthroot(-41 - 29*sqrt(2), 5) == -1 - sqrt(2) expr = 1320*sqrt(10) + 4216 + 2576*sqrt(6) + 1640*sqrt(15) assert nthroot(expr, 5) == 1 + sqrt(6) + sqrt(15) q = 1 + sqrt(2) + sqrt(3) + sqrt(5) assert expand_multinomial(nthroot(expand_multinomial(q**5), 5)) == q q = 1 + sqrt(2) + 7*sqrt(6) + 2*sqrt(10) assert nthroot(expand_multinomial(q**5), 5, 8) == q q = 1 + sqrt(2) - 2*sqrt(3) + 1171*sqrt(6) assert nthroot(expand_multinomial(q**3), 3) == q assert nthroot(expand_multinomial(q**6), 6) == q @slow def test_nthroot1(): q = 1 + sqrt(2) + sqrt(3) + S(1)/10**20 p = expand_multinomial(q**5) assert nthroot(p, 5) == q q = 1 + sqrt(2) + sqrt(3) + S(1)/10**30 p = expand_multinomial(q**5) assert nthroot(p, 5) == p**Rational(1, 5) def test_collect_1(): """Collect with respect to a Symbol""" x, y, z, n = symbols('x,y,z,n') assert collect( x + y*x, x ) == x * (1 + y) assert collect( x + x**2, x ) == x + x**2 assert collect( x**2 + y*x**2, x ) == (x**2)*(1 + y) assert collect( x**2 + y*x, x ) == x*y + x**2 assert collect( 2*x**2 + y*x**2 + 3*x*y, [x] ) == x**2*(2 + y) + 3*x*y assert collect( 2*x**2 + y*x**2 + 3*x*y, [y] ) == 2*x**2 + y*(x**2 + 3*x) assert collect( ((1 + y + x)**4).expand(), x) == ((1 + y)**4).expand() + \ x*(4*(1 + y)**3).expand() + x**2*(6*(1 + y)**2).expand() + \ x**3*(4*(1 + y)).expand() + x**4 # symbols can be given as any iterable expr = x + y assert collect(expr, expr.free_symbols) == expr def test_collect_2(): """Collect with respect to a sum""" a, b, x = symbols('a,b,x') assert collect(a*(cos(x) + sin(x)) + b*(cos(x) + sin(x)), sin(x) + cos(x)) == (a + b)*(cos(x) + sin(x)) def test_collect_3(): """Collect with respect to a product""" a, b, c = symbols('a,b,c') f = Function('f') x, y, z, n = symbols('x,y,z,n') assert collect(-x/8 + x*y, -x) == x*(y - S(1)/8) assert collect( 1 + x*(y**2), x*y ) == 1 + x*(y**2) assert collect( x*y + a*x*y, x*y) == x*y*(1 + a) assert collect( 1 + x*y + a*x*y, x*y) == 1 + x*y*(1 + a) assert collect(a*x*f(x) + b*(x*f(x)), x*f(x)) == x*(a + b)*f(x) assert collect(a*x*log(x) + b*(x*log(x)), x*log(x)) == x*(a + b)*log(x) assert collect(a*x**2*log(x)**2 + b*(x*log(x))**2, x*log(x)) == \ x**2*log(x)**2*(a + b) # with respect to a product of three symbols assert collect(y*x*z + a*x*y*z, x*y*z) == (1 + a)*x*y*z def test_collect_4(): """Collect with respect to a power""" a, b, c, x = symbols('a,b,c,x') assert collect(a*x**c + b*x**c, x**c) == x**c*(a + b) # issue 2997: 2 stays with c (unless c is integer or x is positive0 assert collect(a*x**(2*c) + b*x**(2*c), x**c) == x**(2*c)*(a + b) def test_collect_5(): """Collect with respect to a tuple""" a, x, y, z, n = symbols('a,x,y,z,n') assert collect(x**2*y**4 + z*(x*y**2)**2 + z + a*z, [x*y**2, z]) in [ z*(1 + a + x**2*y**4) + x**2*y**4, z*(1 + a) + x**2*y**4*(1 + z) ] assert collect((1 + (x + y) + (x + y)**2).expand(), [x, y]) == 1 + y + x*(1 + 2*y) + x**2 + y**2 def test_collect_D(): D = Derivative f = Function('f') x, a, b = symbols('x,a,b') fx = D(f(x), x) fxx = D(f(x), x, x) assert collect(a*fx + b*fx, fx) == (a + b)*fx assert collect(a*D(fx, x) + b*D(fx, x), fx) == (a + b)*D(fx, x) assert collect(a*fxx + b*fxx, fx) == (a + b)*D(fx, x) # 1685 assert collect(5*f(x) + 3*fx, fx) == 5*f(x) + 3*fx assert collect(f(x) + f(x)*diff(f(x), x) + x*diff(f(x), x)*f(x), f(x).diff(x)) == \ (x*f(x) + f(x))*D(f(x), x) + f(x) assert collect(f(x) + f(x)*diff(f(x), x) + x*diff(f(x), x)*f(x), f(x).diff(x), exact=True) == \ (x*f(x) + f(x))*D(f(x), x) + f(x) assert collect(1/f(x) + 1/f(x)*diff(f(x), x) + x*diff(f(x), x)/f(x), f(x).diff(x), exact=True) == \ (1/f(x) + x/f(x))*D(f(x), x) + 1/f(x) @XFAIL def test_collect_issues(): D = Derivative f = Function('f') e = (1 + x*D(f(x), x) + D(f(x), x))/f(x) assert collect(e.expand(), f(x).diff(x)) != e def test_collect_D_0(): D = Derivative f = Function('f') x, a, b = symbols('x,a,b') fxx = D(f(x), x, x) # collect does not distinguish nested derivatives, so it returns # -- (a + b)*D(D(f, x), x) assert collect(a*fxx + b*fxx, fxx) == (a + b)*fxx def test_collect_Wild(): """Collect with respect to functions with Wild argument""" a, b, x, y = symbols('a b x y') f = Function('f') w1 = Wild('.1') w2 = Wild('.2') assert collect(f(x) + a*f(x), f(w1)) == (1 + a)*f(x) assert collect(f(x, y) + a*f(x, y), f(w1)) == f(x, y) + a*f(x, y) assert collect(f(x, y) + a*f(x, y), f(w1, w2)) == (1 + a)*f(x, y) assert collect(f(x, y) + a*f(x, y), f(w1, w1)) == f(x, y) + a*f(x, y) assert collect(f(x, x) + a*f(x, x), f(w1, w1)) == (1 + a)*f(x, x) assert collect(a*(x + 1)**y + (x + 1)**y, w1**y) == (1 + a)*(x + 1)**y assert collect(a*(x + 1)**y + (x + 1)**y, w1**b) == \ a*(x + 1)**y + (x + 1)**y assert collect(a*(x + 1)**y + (x + 1)**y, (x + 1)**w2) == \ (1 + a)*(x + 1)**y assert collect(a*(x + 1)**y + (x + 1)**y, w1**w2) == (1 + a)*(x + 1)**y def test_collect_func(): f = ((x + a + 1)**3).expand() assert collect(f, x) == a**3 + 3*a**2 + 3*a + x**3 + x**2*(3*a + 3) + \ x*(3*a**2 + 6*a + 3) + 1 assert collect(f, x, factor) == x**3 + 3*x**2*(a + 1) + 3*x*(a + 1)**2 + \ (a + 1)**3 assert collect(f, x, evaluate=False) == { S.One: a**3 + 3*a**2 + 3*a + 1, x: 3*a**2 + 6*a + 3, x**2: 3*a + 3, x**3: 1 } @XFAIL def test_collect_func_xfail(): # XXX: this test will pass when automatic constant distribution is removed (#1497) assert collect(f, x, factor, evaluate=False) == {S.One: (a + 1)**3, x: 3*(a + 1)**2, x**2: 3*(a + 1), x**3: 1} def test_collect_order(): a, b, x, t = symbols('a,b,x,t') assert collect(t + t*x + t*x**2 + O(x**3), t) == t*(1 + x + x**2 + O(x**3)) assert collect(t + t*x + x**2 + O(x**3), t) == \ t*(1 + x + O(x**3)) + x**2 + O(x**3) f = a*x + b*x + c*x**2 + d*x**2 + O(x**3) g = x*(a + b) + x**2*(c + d) + O(x**3) assert collect(f, x) == g assert collect(f, x, distribute_order_term=False) == g f = sin(a + b).series(b, 0, 10) assert collect(f, [sin(a), cos(a)]) == \ sin(a)*cos(b).series(b, 0, 10) + cos(a)*sin(b).series(b, 0, 10) assert collect(f, [sin(a), cos(a)], distribute_order_term=False) == \ sin(a)*cos(b).series(b, 0, 10).removeO() + \ cos(a)*sin(b).series(b, 0, 10).removeO() + O(b**10) def test_rcollect(): assert rcollect((x**2*y + x*y + x + y)/(x + y), y) == \ (x + y*(1 + x + x**2))/(x + y) assert rcollect(sqrt(-((x + 1)*(y + 1))), z) == sqrt(-((x + 1)*(y + 1))) def test_separatevars(): x, y, z, n = symbols('x,y,z,n') assert separatevars(2*n*x*z + 2*x*y*z) == 2*x*z*(n + y) assert separatevars(x*z + x*y*z) == x*z*(1 + y) assert separatevars(pi*x*z + pi*x*y*z) == pi*x*z*(1 + y) assert separatevars(x*y**2*sin(x) + x*sin(x)*sin(y)) == \ x*(sin(y) + y**2)*sin(x) assert separatevars(x*exp(x + y) + x*exp(x)) == x*(1 + exp(y))*exp(x) assert separatevars((x*(y + 1))**z).is_Pow # != x**z*(1 + y)**z assert separatevars(1 + x + y + x*y) == (x + 1)*(y + 1) assert separatevars(y/pi*exp(-(z - x)/cos(n))) == \ y*exp(x/cos(n))*exp(-z/cos(n))/pi assert separatevars((x + y)*(x - y) + y**2 + 2*x + 1) == (x + 1)**2 # 1759 p = Symbol('p', positive=True) assert separatevars(sqrt(p**2 + x*p**2)) == p*sqrt(1 + x) assert separatevars(sqrt(y*(p**2 + x*p**2))) == p*sqrt(y*(1 + x)) assert separatevars(sqrt(y*(p**2 + x*p**2)), force=True) == \ p*sqrt(y)*sqrt(1 + x) # 1766 assert separatevars(sqrt(x*y)).is_Pow assert separatevars(sqrt(x*y), force=True) == sqrt(x)*sqrt(y) # 1858 # any type sequence for symbols is fine assert separatevars(((2*x + 2)*y), dict=True, symbols=()) == \ {'coeff': 1, x: 2*x + 2, y: y} # separable assert separatevars(((2*x + 2)*y), dict=True, symbols=[x]) == \ {'coeff': y, x: 2*x + 2} assert separatevars(((2*x + 2)*y), dict=True, symbols=[]) == \ {'coeff': 1, x: 2*x + 2, y: y} assert separatevars(((2*x + 2)*y), dict=True) == \ {'coeff': 1, x: 2*x + 2, y: y} assert separatevars(((2*x + 2)*y), dict=True, symbols=None) == \ {'coeff': y*(2*x + 2)} # not separable assert separatevars(3, dict=True) is None assert separatevars(2*x + y, dict=True, symbols=()) is None assert separatevars(2*x + y, dict=True) is None assert separatevars(2*x + y, dict=True, symbols=None) == {'coeff': 2*x + y} # 1709 n, m = symbols('n,m', commutative=False) assert separatevars(m + n*m) == (1 + n)*m assert separatevars(x + x*n) == x*(1 + n) # 1811 f = Function('f') assert separatevars(f(x) + x*f(x)) == f(x) + x*f(x) # a noncommutable object present eq = x*(1 + hyper((), (), y*z)) assert separatevars(eq) == eq def test_separatevars_advanced_factor(): x, y, z = symbols('x,y,z') assert separatevars(1 + log(x)*log(y) + log(x) + log(y)) == \ (log(x) + 1)*(log(y) + 1) assert separatevars(1 + x - log(z) - x*log(z) - exp(y)*log(z) - x*exp(y)*log(z) + x*exp(y) + exp(y)) == \ -((x + 1)*(log(z) - 1)*(exp(y) + 1)) x, y = symbols('x,y', positive=True) assert separatevars(1 + log(x**log(y)) + log(x*y)) == \ (log(x) + 1)*(log(y) + 1) def test_hypersimp(): n, k = symbols('n,k', integer=True) assert hypersimp(factorial(k), k) == k + 1 assert hypersimp(factorial(k**2), k) is None assert hypersimp(1/factorial(k), k) == 1/(k + 1) assert hypersimp(2**k/factorial(k)**2, k) == 2/(k + 1)**2 assert hypersimp(binomial(n, k), k) == (n - k)/(k + 1) assert hypersimp(binomial(n + 1, k), k) == (n - k + 1)/(k + 1) term = (4*k + 1)*factorial(k)/factorial(2*k + 1) assert hypersimp(term, k) == (S(1)/2)*((4*k + 5)/(3 + 14*k + 8*k**2)) term = 1/((2*k - 1)*factorial(2*k + 1)) assert hypersimp(term, k) == (k - S(1)/2)/((k + 1)*(2*k + 1)*(2*k + 3)) term = binomial(n, k)*(-1)**k/factorial(k) assert hypersimp(term, k) == (k - n)/(k + 1)**2 def test_nsimplify(): x = Symbol("x") assert nsimplify(0) == 0 assert nsimplify(-1) == -1 assert nsimplify(1) == 1 assert nsimplify(1 + x) == 1 + x assert nsimplify(2.7) == Rational(27, 10) assert nsimplify(1 - GoldenRatio) == (1 - sqrt(5))/2 assert nsimplify((1 + sqrt(5))/4, [GoldenRatio]) == GoldenRatio/2 assert nsimplify(2/GoldenRatio, [GoldenRatio]) == 2*GoldenRatio - 2 assert nsimplify(exp(5*pi*I/3, evaluate=False)) == \ sympify('1/2 - sqrt(3)*I/2') assert nsimplify(sin(3*pi/5, evaluate=False)) == \ sympify('sqrt(sqrt(5)/8 + 5/8)') assert nsimplify(sqrt(atan('1', evaluate=False))*(2 + I), [pi]) == \ sqrt(pi) + sqrt(pi)/2*I assert nsimplify(2 + exp(2*atan('1/4')*I)) == sympify('49/17 + 8*I/17') assert nsimplify(pi, tolerance=0.01) == Rational(22, 7) assert nsimplify(pi, tolerance=0.001) == Rational(355, 113) assert nsimplify(0.33333, tolerance=1e-4) == Rational(1, 3) assert nsimplify(2.0**(1/3.), tolerance=0.001) == Rational(635, 504) assert nsimplify(2.0**(1/3.), tolerance=0.001, full=True) == \ 2**Rational(1, 3) assert nsimplify(x + .5, rational=True) == Rational(1, 2) + x assert nsimplify(1/.3 + x, rational=True) == Rational(10, 3) + x assert nsimplify(log(3).n(), rational=True) == \ sympify('109861228866811/100000000000000') assert nsimplify(Float(0.272198261287950), [pi, log(2)]) == pi*log(2)/8 assert nsimplify(Float(0.272198261287950).n(3), [pi, log(2)]) == \ -pi/4 - log(2) + S(7)/4 assert nsimplify(x/7.0) == x/7 assert nsimplify(pi/1e2) == pi/100 assert nsimplify(pi/1e2, rational=False) == pi/100.0 assert nsimplify(pi/1e-7) == 10000000*pi assert not nsimplify( factor(-3.0*z**2*(z**2)**(-2.5) + 3*(z**2)**(-1.5))).atoms(Float) e = x**0.0 assert e.is_Pow and nsimplify(x**0.0) == 1 assert nsimplify(3.333333, tolerance=0.1, rational=True) == Rational(10, 3) assert nsimplify(3.333333, tolerance=0.01, rational=True) == Rational(10, 3) assert nsimplify(3.666666, tolerance=0.1, rational=True) == Rational(11, 3) assert nsimplify(3.666666, tolerance=0.01, rational=True) == Rational(11, 3) assert nsimplify(33, tolerance=10, rational=True) == Rational(33) assert nsimplify(33.33, tolerance=10, rational=True) == Rational(30) assert nsimplify(37.76, tolerance=10, rational=True) == Rational(40) assert nsimplify(-203.1) == -S(2031)/10 assert nsimplify(.2, tolerance=0) == S.One/5 assert nsimplify(-.2, tolerance=0) == -S.One/5 assert nsimplify(.2222, tolerance=0) == S(1111)/5000 assert nsimplify(-.2222, tolerance=0) == -S(1111)/5000 def test_extract_minus_sign(): x = Symbol("x") y = Symbol("y") a = Symbol("a") b = Symbol("b") assert simplify(-x/-y) == x/y assert simplify(-x/y) == -x/y assert simplify(x/y) == x/y assert simplify(x/-y) == -x/y assert simplify(-x/0) == -oo*x assert simplify(S(-5)/0) == -oo assert simplify(-a*x/(-y - b)) == a*x/(b + y) def test_diff(): x = Symbol("x") y = Symbol("y") f = Function("f") g = Function("g") assert simplify(g(x).diff(x)*f(x).diff(x) - f(x).diff(x)*g(x).diff(x)) == 0 assert simplify(2*f(x)*f(x).diff(x) - diff(f(x)**2, x)) == 0 assert simplify(diff(1/f(x), x) + f(x).diff(x)/f(x)**2) == 0 assert simplify(f(x).diff(x, y) - f(x).diff(y, x)) == 0 def test_logcombine_1(): x, y = symbols("x,y") a = Symbol("a") z, w = symbols("z,w", positive=True) b = Symbol("b", real=True) assert logcombine(log(x) + 2*log(y)) == log(x) + 2*log(y) assert logcombine(log(x) + 2*log(y), force=True) == log(x*y**2) assert logcombine(a*log(w) + log(z)) == a*log(w) + log(z) assert logcombine(b*log(z) + b*log(x)) == log(z**b) + b*log(x) assert logcombine(b*log(z) - log(w)) == log(z**b/w) assert logcombine(log(x)*log(z)) == log(x)*log(z) assert logcombine(log(w)*log(x)) == log(w)*log(x) assert logcombine(cos(-2*log(z) + b*log(w))) in [cos(log(w**b/z**2)), cos(log(z**2/w**b))] assert logcombine(log(log(x) - log(y)) - log(z), force=True) == \ log(log(x/y)/z) assert logcombine((2 + I)*log(x), force=True) == (2 + I)*log(x) assert logcombine((x**2 + log(x) - log(y))/(x*y), force=True) == \ (x**2 + log(x/y))/(x*y) # the following could also give log(z*x**log(y**2)), what we # are testing is that a canonical result is obtained assert logcombine(log(x)*2*log(y) + log(z), force=True) == \ log(z*y**log(x**2)) assert logcombine((x*y + sqrt(x**4 + y**4) + log(x) - log(y))/(pi*x**Rational(2, 3)* sqrt(y)**3), force=True) == ( x*y + sqrt(x**4 + y**4) + log(x/y))/(pi*x**(S(2)/3)*y**(S(3)/2)) assert logcombine(gamma(-log(x/y))*acos(-log(x/y)), force=True) == \ acos(-log(x/y))*gamma(-log(x/y)) assert logcombine(2*log(z)*log(w)*log(x) + log(z) + log(w)) == \ log(z**log(w**2))*log(x) + log(w*z) assert logcombine(3*log(w) + 3*log(z)) == log(w**3*z**3) assert logcombine(x*(y + 1) + log(2) + log(3)) == x*(y + 1) + log(6) assert logcombine((x + y)*log(w) + (-x - y)*log(3)) == (x + y)*log(w/3) def test_logcombine_complex_coeff(): i = Integral((sin(x**2) + cos(x**3))/x, x) assert logcombine(i, force=True) == i assert logcombine(i + 2*log(x), force=True) == \ i + log(x**2) def test_posify(): from sympy.abc import x assert str(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) # when it is corrected, posify will allow the change to be made. The # force=True option can do so as well when it is implemented. eq, rep = posify(1/x) assert log(eq).expand().subs(rep) == -log(x) assert str(posify([x, 1 + x])) == '([_x, _x + 1], {_x: x})' x = symbols('x') p = symbols('p', positive=True) n = symbols('n', negative=True) orig = [x, n, p] modified, reps = posify(orig) assert str(modified) == '[_x, n, p]' assert [w.subs(reps) for w in modified] == orig def test_powdenest(): from sympy import powdenest from sympy.abc import x, y, z, a, b p, q = symbols('p q', positive=True) i, j = symbols('i,j', integer=True) assert powdenest(x) == x assert powdenest(x + 2*(x**(2*a/3))**(3*x)) == (x + 2*(x**(2*a/3))**(3*x)) assert powdenest((exp(2*a/3))**(3*x)) # -X-> (exp(a/3))**(6*x) assert powdenest((x**(2*a/3))**(3*x)) == ((x**(2*a/3))**(3*x)) assert powdenest(exp(3*x*log(2))) == 2**(3*x) assert powdenest(sqrt(p**2)) == p i, j = symbols('i,j', integer=True) eq = p**(2*i)*q**(4*i) assert powdenest(eq) == (p*q**2)**(2*i) # -X-> (x**x)**i*(x**x)**j == x**(x*(i + j)) assert powdenest((x**x)**(i + j)) assert powdenest(exp(3*y*log(x))) == x**(3*y) assert powdenest(exp(y*(log(a) + log(b)))) == (a*b)**y assert powdenest(exp(3*(log(a) + log(b)))) == a**3*b**3 assert powdenest(((x**(2*i))**(3*y))**x) == ((x**(2*i))**(3*y))**x assert powdenest(((x**(2*i))**(3*y))**x, force=True) == x**(6*i*x*y) assert powdenest(((x**(2*a/3))**(3*y/i))**x) == \ (((x**(2*a/3))**(3*y/i))**x) assert powdenest((x**(2*i)*y**(4*i))**z, force=True) == (x*y**2)**(2*i*z) assert powdenest((p**(2*i)*q**(4*i))**j) == (p*q**2)**(2*i*j) assert powdenest(((p**(2*a))**(3*y))**x) == p**(6*a*x*y) e = ((x**2*y**4)**a)**(x*y) assert powdenest(e) == e e = (((x**2*y**4)**a)**(x*y))**3 assert powdenest(e) == ((x**2*y**4)**a)**(3*x*y) assert powdenest((((x**2*y**4)**a)**(x*y)), force=True) == \ (x*y**2)**(2*a*x*y) assert powdenest((((x**2*y**4)**a)**(x*y))**3, force=True) == \ (x*y**2)**(6*a*x*y) assert powdenest((x**2*y**6)**i) != (x*y**3)**(2*i) x, y = symbols('x,y', positive=True) assert powdenest((x**2*y**6)**i) == (x*y**3)**(2*i) assert powdenest((x**(2*i/3)*y**(i/2))**(2*i)) == (x**(S(4)/3)*y)**(i**2) assert powdenest(sqrt(x**(2*i)*y**(6*i))) == (x*y**3)**i assert powdenest(4**x) == 2**(2*x) assert powdenest((4**x)**y) == 2**(2*x*y) assert powdenest(4**x*y) == 2**(2*x)*y def test_powdenest_polar(): x, y, z = symbols('x y z', polar=True) a, b, c = symbols('a b c') assert powdenest((x*y*z)**a) == x**a*y**a*z**a assert powdenest((x**a*y**b)**c) == x**(a*c)*y**(b*c) assert powdenest(((x**a)**b*y**c)**c) == x**(a*b*c)*y**(c**2) def test_issue_2706(): arg = ((gamma(x)*hyper((), (), x))*pi)**2 assert powdenest(arg) == (pi*gamma(x)*hyper((), (), x))**2 assert arg.is_positive is None def test_issue_1095(): # simplify should call cancel from sympy.abc import x, y f = Function('f') assert simplify((4*x + 6*f(y))/(2*x + 3*f(y))) == 2 @XFAIL def test_simplify_float_vs_integer(): # Test for issue 1374: # http://code.google.com/p/sympy/issues/detail?id=1374 assert simplify(x**2.0 - x**2) == 0 assert simplify(x**2 - x**2.0) == 0 def test_combsimp(): from sympy.abc import n, k assert combsimp(factorial(n)) == factorial(n) assert combsimp(binomial(n, k)) == binomial(n, k) assert combsimp(factorial(n)/factorial(n - 3)) == n*(-1 + n)*(-2 + n) assert combsimp(binomial(n + 1, k + 1)/binomial(n, k)) == (1 + n)/(1 + k) assert combsimp(binomial(3*n + 4, n + 1)/binomial(3*n + 1, n)) == \ S(3)/2*((3*n + 2)*(3*n + 4)/((n + 1)*(2*n + 3))) assert combsimp(factorial(n)**2/factorial(n - 3)) == \ factorial(n)*n*(-1 + n)*(-2 + n) assert combsimp(factorial(n)*binomial(n + 1, k + 1)/binomial(n, k)) == \ factorial(n)*(1 + n)/(1 + k) assert combsimp(binomial(n - 1, k)) == -((-n + k)*binomial(n, k))/n assert combsimp(binomial(n + 2, k + S(1)/2)) == 4*((n + 1)*(n + 2) * binomial(n, k + S(1)/2))/((2*k - 2*n - 1)*(2*k - 2*n - 3)) assert combsimp(binomial(n + 2, k + 2.0)) == \ -((1.0*n + 2.0)*binomial(n + 1.0, k + 2.0))/(k - n) # coverage tests assert combsimp(factorial(n*(1 + n) - n**2 - n)) == 1 assert combsimp(binomial(n + k - 2, n)) == \ k*(k - 1)*binomial(n + k, n)/((n + k)*(n + k - 1)) i = Symbol('i', integer=True) e = gamma(i + 3) assert combsimp(e) == e e = gamma(exp(i)) assert combsimp(e) == e e = gamma(n + S(1)/3)*gamma(n + S(2)/3) assert combsimp(e) == e assert combsimp(gamma(4*n + S(1)/2)/gamma(2*n - S(3)/4)) == \ 2**(4*n - S(5)/2)*(8*n - 3)*gamma(2*n + S(3)/4)/sqrt(pi) assert combsimp(6*FallingFactorial(-4, n)/factorial(n)) == \ (-1)**n*(n + 1)*(n + 2)*(n + 3) assert combsimp(6*FallingFactorial(-4, n - 1)/factorial(n - 1)) == \ (-1)**(n - 1)*n*(n + 1)*(n + 2) assert combsimp(6*FallingFactorial(-4, n - 3)/factorial(n - 3)) == \ (-1)**(n - 3)*n*(n - 1)*(n - 2) assert combsimp(6*FallingFactorial(-4, -n - 1)/factorial(-n - 1)) == \ -(-1)**(-n - 1)*n*(n - 1)*(n - 2) assert combsimp(6*RisingFactorial(4, n)/factorial(n)) == \ (n + 1)*(n + 2)*(n + 3) assert combsimp(6*RisingFactorial(4, n - 1)/factorial(n - 1)) == \ n*(n + 1)*(n + 2) assert combsimp(6*RisingFactorial(4, n - 3)/factorial(n - 3)) == \ n*(n - 1)*(n - 2) assert combsimp(6*RisingFactorial(4, -n - 1)/factorial(-n - 1)) == \ -n*(n - 1)*(n - 2) def test_issue_2516(): aA, Re, a, b, D = symbols('aA Re a b D') e = ((D**3*a + b*aA**3)/Re).expand() assert collect(e, [aA**3/Re, a]) == e def test_issue_2629(): b = x*sqrt(y) a = sqrt(b) c = sqrt(sqrt(x)*y) assert powsimp(a*b) == sqrt(b)**3 assert powsimp(a*b**2*sqrt(y)) == sqrt(y)*a**5 assert powsimp(a*x**2*c**3*y) == c**3*a**5 assert powsimp(a*x*c**3*y**2) == c**7*a assert powsimp(x*c**3*y**2) == c**7 assert powsimp(x*c**3*y) == x*y*c**3 assert powsimp(sqrt(x)*c**3*y) == c**5 assert powsimp(sqrt(x)*a**3*sqrt(y)) == sqrt(x)*sqrt(y)*a**3 assert powsimp(Mul(sqrt(x)*c**3*sqrt(y), y, evaluate=False)) == \ sqrt(x)*sqrt(y)**3*c**3 assert powsimp(a**2*a*x**2*y) == a**7 # symbolic powers work, too b = x**y*y a = b*sqrt(b) assert a.is_Mul is True assert powsimp(a) == sqrt(b)**3 # as does exp a = x*exp(2*y/3) assert powsimp(a*sqrt(a)) == sqrt(a)**3 assert powsimp(a**2*sqrt(a)) == sqrt(a)**5 assert powsimp(a**2*sqrt(sqrt(a))) == sqrt(sqrt(a))**9 def test_as_content_primitive(): # although the _as_content_primitive methods do not alter the underlying structure, # the as_content_primitive function will touch up the expression and join # bases that would otherwise have not been joined. assert ((x*(2 + 2*x)*(3*x + 3)**2)).as_content_primitive() == \ (18, x*(x + 1)**3) assert (2 + 2*x + 2*y*(3 + 3*y)).as_content_primitive() == \ (2, x + 3*y*(y + 1) + 1) assert ((2 + 6*x)**2).as_content_primitive() == \ (4, (3*x + 1)**2) assert ((2 + 6*x)**(2*y)).as_content_primitive() == \ (1, (_keep_coeff(S(2), (3*x + 1)))**(2*y)) assert (5 + 10*x + 2*y*(3 + 3*y)).as_content_primitive() == \ (1, 10*x + 6*y*(y + 1) + 5) assert ((5*(x*(1 + y)) + 2*x*(3 + 3*y))).as_content_primitive() == \ (11, x*(y + 1)) assert ((5*(x*(1 + y)) + 2*x*(3 + 3*y))**2).as_content_primitive() == \ (121, x**2*(y + 1)**2) assert (y**2).as_content_primitive() == \ (1, y**2) assert (S.Infinity).as_content_primitive() == (1, oo) eq = x**(2 + y) assert (eq).as_content_primitive() == (1, eq) assert (S.Half**(2 + x)).as_content_primitive() == (S(1)/4, 2**-x) assert ((-S.Half)**(2 + x)).as_content_primitive() == \ (S(1)/4, (-S.Half)**x) assert ((-S.Half)**(2 + x)).as_content_primitive() == \ (S(1)/4, (-S.Half)**x) assert (4**((1 + y)/2)).as_content_primitive() == (2, 4**(y/2)) assert (3**((1 + y)/2)).as_content_primitive() == \ (1, 3**(Mul(S(1)/2, 1 + y, evaluate=False))) assert (5**(S(3)/4)).as_content_primitive() == (1, 5**(S(3)/4)) assert (5**(S(7)/4)).as_content_primitive() == (5, 5**(S(3)/4)) assert Add(5*z/7, 0.5*x, 3*y/2, evaluate=False).as_content_primitive() == \ (S(1)/14, 7.0*x + 21*y + 10*z) assert (2**(S(3)/4) + 2**(S(1)/4)*sqrt(3)).as_content_primitive(radical=True) == \ (1, 2**(S(1)/4)*(sqrt(2) + sqrt(3))) def test_radsimp(): r2 = sqrt(2) r3 = sqrt(3) r5 = sqrt(5) r7 = sqrt(7) assert fraction(radsimp(1/r2)) == (sqrt(2), 2) assert radsimp(1/(1 + r2)) == \ -1 + sqrt(2) assert radsimp(1/(r2 + r3)) == \ -sqrt(2) + sqrt(3) assert fraction(radsimp(1/(1 + r2 + r3))) == \ (-sqrt(6) + sqrt(2) + 2, 4) assert fraction(radsimp(1/(r2 + r3 + r5))) == \ (-sqrt(30) + 2*sqrt(3) + 3*sqrt(2), 12) assert fraction(radsimp(1/(1 + r2 + r3 + r5))) == ( (-34*sqrt(10) - 26*sqrt(15) - 55*sqrt(3) - 61*sqrt(2) + 14*sqrt(30) + 93 + 46*sqrt(6) + 53*sqrt(5), 71)) assert fraction(radsimp(1/(r2 + r3 + r5 + r7))) == ( (-50*sqrt(42) - 133*sqrt(5) - 34*sqrt(70) - 145*sqrt(3) + 22*sqrt(105) + 185*sqrt(2) + 62*sqrt(30) + 135*sqrt(7), 215)) z = radsimp(1/(1 + r2/3 + r3/5 + r5 + r7)) assert len((3616791619821680643598*z).args) == 16 assert radsimp(1/z) == 1/z assert radsimp(1/z, max_terms=20).expand() == 1 + r2/3 + r3/5 + r5 + r7 assert radsimp(1/(r2*3)) == \ sqrt(2)/6 assert radsimp(1/(r2*a + r3 + r5 + r7)) == ( (8*sqrt(2)*a**7 - 8*sqrt(7)*a**6 - 8*sqrt(5)*a**6 - 8*sqrt(3)*a**6 - 180*sqrt(2)*a**5 + 8*sqrt(30)*a**5 + 8*sqrt(42)*a**5 + 8*sqrt(70)*a**5 - 24*sqrt(105)*a**4 + 84*sqrt(3)*a**4 + 100*sqrt(5)*a**4 + 116*sqrt(7)*a**4 - 72*sqrt(70)*a**3 - 40*sqrt(42)*a**3 - 8*sqrt(30)*a**3 + 782*sqrt(2)*a**3 - 462*sqrt(3)*a**2 - 302*sqrt(7)*a**2 - 254*sqrt(5)*a**2 + 120*sqrt(105)*a**2 - 795*sqrt(2)*a - 62*sqrt(30)*a + 82*sqrt(42)*a + 98*sqrt(70)*a - 118*sqrt(105) + 59*sqrt(7) + 295*sqrt(5) + 531*sqrt(3))/(16*a**8 - 480*a**6 + 3128*a**4 - 6360*a**2 + 3481)) assert radsimp(1/(r2*a + r2*b + r3 + r7)) == ( (sqrt(2)*a*(a + b)**2 - 5*sqrt(2)*a + sqrt(42)*a + sqrt(2)*b*(a + b)**2 - 5*sqrt(2)*b + sqrt(42)*b - sqrt(7)*(a + b)**2 - sqrt(3)*(a + b)**2 - 2*sqrt(3) + 2*sqrt(7))/(2*a**4 + 8*a**3*b + 12*a**2*b**2 - 20*a**2 + 8*a*b**3 - 40*a*b + 2*b**4 - 20*b**2 + 8)) assert radsimp(1/(r2*a + r2*b + r2*c + r2*d)) == \ sqrt(2)/(2*a + 2*b + 2*c + 2*d) assert radsimp(1/(1 + r2*a + r2*b + r2*c + r2*d)) == ( (sqrt(2)*a + sqrt(2)*b + sqrt(2)*c + sqrt(2)*d - 1)/(2*a**2 + 4*a*b + 4*a*c + 4*a*d + 2*b**2 + 4*b*c + 4*b*d + 2*c**2 + 4*c*d + 2*d**2 - 1)) assert radsimp((y**2 - x)/(y - sqrt(x))) == \ sqrt(x) + y assert radsimp(-(y**2 - x)/(y - sqrt(x))) == \ -(sqrt(x) + y) assert radsimp(1/(1 - I + a*I)) == \ (-I*a + 1 + I)/(a**2 - 2*a + 2) assert radsimp(1/((-x + y)*(x - sqrt(y)))) == \ (-x - sqrt(y))/((x - y)*(x**2 - y)) e = (3 + 3*sqrt(2))*x*(3*x - 3*sqrt(y)) assert radsimp(e) == x*(3 + 3*sqrt(2))*(3*x - 3*sqrt(y)) assert radsimp(1/e) == ( (-9*x + 9*sqrt(2)*x - 9*sqrt(y) + 9*sqrt(2)*sqrt(y))/(9*x*(9*x**2 - 9*y))) assert radsimp(1 + 1/(1 + sqrt(3))) == \ Mul(S.Half, -1 + sqrt(3), evaluate=False) + 1 A = symbols("A", commutative=False) assert radsimp(x**2 + sqrt(2)*x**2 - sqrt(2)*x*A) == \ x**2 + sqrt(2)*x**2 - sqrt(2)*x*A assert radsimp(1/sqrt(5 + 2 * sqrt(6))) == -sqrt(2) + sqrt(3) assert radsimp(1/sqrt(5 + 2 * sqrt(6))**3) == -(-sqrt(3) + sqrt(2))**3 # issue 3433 assert fraction(radsimp(1/sqrt(x))) == (sqrt(x), x) assert fraction(radsimp(1/sqrt(2*x + 3))) == (sqrt(2*x + 3), 2*x + 3) assert fraction(radsimp(1/sqrt(2*(x + 3)))) == (sqrt(2*x + 6), 2*x + 6) # issue 2895 e = S('-(2 + 2*sqrt(2) + 4*2**(1/4))/' '(1 + 2**(3/4) + 3*2**(1/4) + 3*sqrt(2))') assert radsimp(e).expand() == -2*2**(S(3)/4) - 2*2**(S(1)/4) + 2 + 2*sqrt(2) # issue 2887 (modifications to radimp didn't initially recognize this so # the test is included here) assert radsimp(1/(-sqrt(5)/2 - S(1)/2 + (-sqrt(5)/2 - S(1)/2)**2)) == 1 # from issue 2835 eq = ( (-240*sqrt(2)*sqrt(sqrt(5) + 5)*sqrt(8*sqrt(5) + 40) - 360*sqrt(2)*sqrt(-8*sqrt(5) + 40)*sqrt(-sqrt(5) + 5) - 120*sqrt(10)*sqrt(-8*sqrt(5) + 40)*sqrt(-sqrt(5) + 5) + 120*sqrt(2)*sqrt(-sqrt(5) + 5)*sqrt(8*sqrt(5) + 40) + 120*sqrt(2)*sqrt(-8*sqrt(5) + 40)*sqrt(sqrt(5) + 5) + 120*sqrt(10)*sqrt(-sqrt(5) + 5)*sqrt(8*sqrt(5) + 40) + 120*sqrt(10)*sqrt(-8*sqrt(5) + 40)*sqrt(sqrt(5) + 5))/(-36000 - 7200*sqrt(5) + (12*sqrt(10)*sqrt(sqrt(5) + 5) + 24*sqrt(10)*sqrt(-sqrt(5) + 5))**2)) assert radsimp(eq) is S.NaN # it's 0/0 # work with normal form e = 1/sqrt(sqrt(7)/7 + 2*sqrt(2) + 3*sqrt(3) + 5*sqrt(5)) + 3 assert radsimp(e) == ( -sqrt(sqrt(7) + 14*sqrt(2) + 21*sqrt(3) + 35*sqrt(5))*(-11654899*sqrt(35) - 1577436*sqrt(210) - 1278438*sqrt(15) - 1346996*sqrt(10) + 1635060*sqrt(6) + 5709765 + 7539830*sqrt(14) + 8291415*sqrt(21))/1300423175 + 3) # obey power rules base = sqrt(3) - sqrt(2) assert radsimp(1/base**3) == (sqrt(3) + sqrt(2))**3 assert radsimp(1/(-base)**3) == -(sqrt(2) + sqrt(3))**3 assert radsimp(1/(-base)**x) == (-base)**(-x) assert radsimp(1/base**x) == (sqrt(2) + sqrt(3))**x assert radsimp(root(1/(-1 - sqrt(2)), -x)) == (-1)**(-1/x)*(1 + sqrt(2))**(1/x) # recurse e = cos(1/(1 + sqrt(2))) assert radsimp(e) == cos(-sqrt(2) + 1) assert radsimp(e/2) == cos(-sqrt(2) + 1)/2 assert radsimp(1/e) == 1/cos(-sqrt(2) + 1) assert radsimp(2/e) == 2/cos(-sqrt(2) + 1) # test that symbolic denominators are not processed r = 1 + sqrt(2) assert radsimp(x/r, symbolic=False) == -x*(-sqrt(2) + 1) assert radsimp(x/(y + r), symbolic=False) == x/(y + 1 + sqrt(2)) assert radsimp(x/(y + r)/r, symbolic=False) == \ -x*(-sqrt(2) + 1)/(y + 1 + sqrt(2)) def test_radsimp_issue_3214(): c, p = symbols('c p', positive=True) s = sqrt(c**2 - p**2) b = (c + I*p - s)/(c + I*p + s) assert radsimp(b) == -I*(c + I*p - sqrt(c**2 - p**2))**2/(2*c*p) def test_collect_const(): # coverage not provided by above tests assert collect_const(2*sqrt(3) + 4*a*sqrt(5)) == \ 2*(2*sqrt(5)*a + sqrt(3)) # let the primitive reabsorb assert collect_const(2*sqrt(3) + 4*a*sqrt(5), sqrt(3)) == \ 2*sqrt(3) + 4*a*sqrt(5) assert collect_const(sqrt(2)*(1 + sqrt(2)) + sqrt(3) + x*sqrt(2)) == \ sqrt(2)*(x + 1 + sqrt(2)) + sqrt(3) # issue 2191 assert collect_const(2*x + 2*y + 1, 2) == \ collect_const(2*x + 2*y + 1) == \ Add(S(1), Mul(2, x + y, evaluate=False), evaluate=False) assert collect_const(-y - z) == Mul(-1, y + z, evaluate=False) assert collect_const(2*x - 2*y - 2*z, 2) == \ Mul(2, x - y - z, evaluate=False) assert collect_const(2*x - 2*y - 2*z, -2) == \ _unevaluated_Add(2*x, Mul(-2, y + z, evaluate=False)) # this is why the content_primitive is used eq = (sqrt(15 + 5*sqrt(2))*x + sqrt(3 + sqrt(2))*y)*2 assert collect_sqrt(eq + 2) == \ 2*sqrt(sqrt(2) + 3)*(sqrt(5)*x + y) + 2 def test_issue2834(): from sympy import Polygon, RegularPolygon, denom x = Polygon(*RegularPolygon((0, 0), 1, 5).vertices).centroid.x assert abs(denom(x).n()) > 1e-12 assert abs(denom(radsimp(x))) > 1e-12 # in case simplify didn't handle it def test_fraction_expand(): eq = (x + y)*y/x assert eq.expand(frac=True) == fraction_expand(eq) == (x*y + y**2)/x assert eq.expand() == y + y**2/x def test_combsimp_gamma(): from sympy.abc import x, y R = Rational assert combsimp(gamma(x)) == gamma(x) assert combsimp(gamma(x + 1)/x) == gamma(x) assert combsimp(gamma(x)/(x - 1)) == gamma(x - 1) assert combsimp(x*gamma(x)) == gamma(x + 1) assert combsimp((x + 1)*gamma(x + 1)) == gamma(x + 2) assert combsimp(gamma(x + y)*(x + y)) == gamma(x + y + 1) assert combsimp(x/gamma(x + 1)) == 1/gamma(x) assert combsimp((x + 1)**2/gamma(x + 2)) == (x + 1)/gamma(x + 1) assert combsimp(x*gamma(x) + gamma(x + 3)/(x + 2)) == \ (x + 2)*gamma(x + 1) assert combsimp(gamma(2*x)*x) == gamma(2*x + 1)/2 assert combsimp(gamma(2*x)/(x - S(1)/2)) == 2*gamma(2*x - 1) assert combsimp(gamma(x)*gamma(1 - x)) == pi/sin(pi*x) assert combsimp(gamma(x)*gamma(-x)) == -pi/(x*sin(pi*x)) assert combsimp(1/gamma(x + 3)/gamma(1 - x)) == \ sin(pi*x)/(pi*x*(x + 1)*(x + 2)) assert powsimp(combsimp( gamma(x)*gamma(x + S(1)/2)*gamma(y)/gamma(x + y))) == \ 2**(-2*x + 1)*sqrt(pi)*gamma(2*x)*gamma(y)/gamma(x + y) assert combsimp(1/gamma(x)/gamma(x - S(1)/3)/gamma(x + S(1)/3)) == \ 3**(3*x - S(3)/2)/(2*pi*gamma(3*x - 1)) assert simplify( gamma(S(1)/2 + x/2)*gamma(1 + x/2)/gamma(1 + x)/sqrt(pi)*2**x) == 1 assert combsimp(gamma(S(-1)/4)*gamma(S(-3)/4)) == 16*sqrt(2)*pi/3 assert powsimp(combsimp(gamma(2*x)/gamma(x))) == \ 2**(2*x - 1)*gamma(x + S(1)/2)/sqrt(pi) # issue 3693 e = (-gamma(k)*gamma(k + 2) + gamma(k + 1)**2)/gamma(k)**2 assert combsimp(e) == -k assert combsimp(1/e) == -1/k e = (gamma(x) + gamma(x + 1))/gamma(x) assert combsimp(e) == x + 1 assert combsimp(1/e) == 1/(x + 1) e = (gamma(x) + gamma(x + 2))*(gamma(x - 1) + gamma(x))/gamma(x) assert combsimp(e) == (x**2 + x + 1)*gamma(x + 1)/(x - 1) e = (-gamma(k)*gamma(k + 2) + gamma(k + 1)**2)/gamma(k)**2 assert combsimp(e**2) == k**2 assert combsimp(e**2/gamma(k + 1)) == k/gamma(k) a = R(1, 2) + R(1, 3) b = a + R(1, 3) assert combsimp(gamma(2*k)/gamma(k)*gamma(k + a)*gamma(k + b)) 3*2**(2*k + 1)*3**(-3*k - 2)*sqrt(pi)*gamma(3*k + R(3, 2))/2 A, B = symbols('A B', commutative=False) assert combsimp(e*B*A) == combsimp(e)*B*A # check iteration assert combsimp(gamma(2*k)/gamma(k)*gamma(-k - R(1, 2))) == ( -2**(2*k + 1)*sqrt(pi)/(2*((2*k + 1)*cos(pi*k)))) assert combsimp( gamma(k)*gamma(k + R(1, 3))*gamma(k + R(2, 3))/gamma(3*k/2)) == ( 3*2**(3*k + 1)*3**(-3*k - S.Half)*sqrt(pi)*gamma(3*k/2 + S.Half)/2) def test_polarify(): from sympy import polar_lift, polarify x = Symbol('x') z = Symbol('z', polar=True) f = Function('f') ES = {} assert polarify(-1) == (polar_lift(-1), ES) assert polarify(1 + I) == (polar_lift(1 + I), ES) assert polarify(exp(x), subs=False) == exp(x) assert polarify(1 + x, subs=False) == 1 + x assert polarify(f(I) + x, subs=False) == f(polar_lift(I)) + x assert polarify(x, lift=True) == polar_lift(x) assert polarify(z, lift=True) == z assert polarify(f(x), lift=True) == f(polar_lift(x)) assert polarify(1 + x, lift=True) == polar_lift(1 + x) assert polarify(1 + f(x), lift=True) == polar_lift(1 + f(polar_lift(x))) newex, subs = polarify(f(x) + z) assert newex.subs(subs) == f(x) + z mu = Symbol("mu") sigma = Symbol("sigma", positive=True) # Make sure polarify(lift=True) doesn't try to lift the integration # variable assert polarify( Integral(sqrt(2)*x*exp(-(-mu + x)**2/(2*sigma**2))/(2*sqrt(pi)*sigma), (x, -oo, oo)), lift=True) == Integral(sqrt(2)*(sigma*exp_polar(0))**exp_polar(I*pi)* exp((sigma*exp_polar(0))**(2*exp_polar(I*pi))*exp_polar(I*pi)*polar_lift(-mu + x)** (2*exp_polar(0))/2)*exp_polar(0)*polar_lift(x)/(2*sqrt(pi)), (x, -oo, oo)) def test_unpolarify(): from sympy import (exp_polar, polar_lift, exp, unpolarify, sin, principal_branch) from sympy import gamma, erf, sin, tanh, uppergamma, Eq, Ne from sympy.abc import x p = exp_polar(7*I) + 1 u = exp(7*I) + 1 assert unpolarify(1) == 1 assert unpolarify(p) == u assert unpolarify(p**2) == u**2 assert unpolarify(p**x) == p**x assert unpolarify(p*x) == u*x assert unpolarify(p + x) == u + x assert unpolarify(sqrt(sin(p))) == sqrt(sin(u)) # Test reduction to principal branch 2*pi. t = principal_branch(x, 2*pi) assert unpolarify(t) == x assert unpolarify(sqrt(t)) == sqrt(t) # Test exponents_only. assert unpolarify(p**p, exponents_only=True) == p**u assert unpolarify(uppergamma(x, p**p)) == uppergamma(x, p**u) # Test functions. assert unpolarify(sin(p)) == sin(u) assert unpolarify(tanh(p)) == tanh(u) assert unpolarify(gamma(p)) == gamma(u) assert unpolarify(erf(p)) == erf(u) assert unpolarify(uppergamma(x, p)) == uppergamma(x, p) assert unpolarify(uppergamma(sin(p), sin(p + exp_polar(0)))) == \ uppergamma(sin(u), sin(u + 1)) assert unpolarify(uppergamma(polar_lift(0), 2*exp_polar(0))) == \ uppergamma(0, 2) assert unpolarify(Eq(p, 0)) == Eq(u, 0) assert unpolarify(Ne(p, 0)) == Ne(u, 0) assert unpolarify(polar_lift(x) > 0) == (x > 0) # Test bools assert unpolarify(True) is True def test_issue_2998(): assert collect(a*y**(2.0*x) + b*y**(2.0*x), y**x) == y**(2.0*x)*(a + b) assert collect(a*2**(2.0*x) + b*2**(2.0*x), 2**x) == 2**(2.0*x)*(a + b) def test_signsimp(): e = x*(-x + 1) + x*(x - 1) assert signsimp(Eq(e, 0)) is True def test_besselsimp(): from sympy import besselj, besseli, besselk, bessely, jn, yn, exp_polar, cosh, cosine_transform assert besselsimp(exp(-I*pi*y/2)*besseli(y, z*exp_polar(I*pi/2))) == \ besselj(y, z) assert besselsimp(exp(-I*pi*a/2)*besseli(a, 2*sqrt(x)*exp_polar(I*pi/2))) == \ besselj(a, 2*sqrt(x)) assert besselsimp(sqrt(2)*sqrt(pi)*x**(S(1)/4)*exp(I*pi/4)*exp(-I*pi*a/2) * besseli(-S(1)/2, sqrt(x)*exp_polar(I*pi/2)) * besseli(a, sqrt(x)*exp_polar(I*pi/2))/2) == \ besselj(a, sqrt(x)) * cos(sqrt(x)) assert besselsimp(besseli(S(-1)/2, z)) == \ sqrt(2)*cosh(z)/(sqrt(pi)*sqrt(z)) assert besselsimp(besseli(a, z*exp_polar(-I*pi/2))) == \ exp(-I*pi*a/2)*besselj(a, z) assert cosine_transform(1/t*sin(a/t), t, y) == \ sqrt(2)*sqrt(pi)*besselj(0, 2*sqrt(a)*sqrt(y))/2 def test_Piecewise(): e1 = x*(x + y) - y*(x + y) e2 = sin(x)**2 + cos(x)**2 e3 = expand((x + y)*y/x) s1 = simplify(e1) s2 = simplify(e2) s3 = simplify(e3) assert simplify(Piecewise((e1, x < e2), (e3, True))) == \ Piecewise((s1, x < s2), (s3, True)) # trigsimp tries not to touch non-trig containing args assert trigsimp(Piecewise((e1, e3 < e2), (e3, True))) == \ Piecewise((e1, e3 < s2), (e3, True)) def test_polymorphism(): class A(Basic): def _eval_simplify(x, **kwargs): return 1 a = A(5, 2) assert simplify(a) == 1 def test_issue_from_PR1599(): n1, n2, n3, n4 = symbols('n1 n2 n3 n4', negative=True) assert simplify(I*sqrt(n1)) == -sqrt(-n1) assert (powsimp(sqrt(n1)*sqrt(n2)*sqrt(n3)) == -I*sqrt(-n1)*sqrt(-n2)*sqrt(-n3)) assert (powsimp(root(n1, 3)*root(n2, 3)*root(n3, 3)*root(n4, 3)) == -(-1)**(S(1)/3)* (-n1)**(S(1)/3)*(-n2)**(S(1)/3)*(-n3)**(S(1)/3)*(-n4)**(S(1)/3)) def test_3712(): eq = (x + 2*y)*(2*x + 2) assert simplify(eq) == (x + 1)*(x + 2*y)*2 # reject the 2-arg Mul -- these are a headache for test writing assert simplify(eq.expand()) == \ 2*x**2 + 4*x*y + 2*x + 4*y @XFAIL def test_3712_fail(): # from doc/src/modules/physics/mechanics/examples.rst, the current `eq` # at Line 576 (in different variables) was formerly the equivalent and # shorter expression given below...it would be nice to get the short one # back again xp, y, x, z = symbols('xp, y, x, z') eq = 4*(-19*sin(x)*y + 5*sin(3*x)*y + 15*cos(2*x)*z - 21*z)*xp/(9*cos(x) - 5*cos(3*x)) assert trigsimp(eq) == -2*(2*cos(x)*tan(x)*y + 3*z)*xp/cos(x) def test_3821(): e = [cos(x) + I*sin(x), cos(x) - I*sin(x), cosh(x) - sinh(x), cosh(x) + sinh(x)] ok = [exp(I*x), exp(-I*x), exp(-x), exp(x)] # wrap in f to show that the change happens wherever ei occurs f = Function('f') assert [simplify(f(ei)).args[0] for ei in e] == ok def test_3902(): from sympy.abc import r, R assert simplify(-(r*Piecewise((4*pi/3, r <= R), (-8*pi*R**3/(3*r**3), True)) + 2*Piecewise((4*pi*r/3, r <= R), (4*pi*R**3/(3*r**2), True)))/(4*pi*r)) == \ Piecewise((-1, r <= R), (0, True)) def test_exptrigsimp(): def valid(a, b): from sympy.utilities.randtest import test_numerically as tn if not (tn(a, b) and a == b): return False return True assert exptrigsimp(exp(x) + exp(-x)) == 2*cosh(x) assert exptrigsimp(exp(x) - exp(-x)) == 2*sinh(x) e = [cos(x) + I*sin(x), cos(x) - I*sin(x), cosh(x) - sinh(x), cosh(x) + sinh(x)] ok = [exp(I*x), exp(-I*x), exp(-x), exp(x)] assert all(valid(i, j) for i, j in zip( [exptrigsimp(ei) for ei in e], ok)) ue = [cos(x) + sin(x), cos(x) - sin(x), cosh(x) + I*sinh(x), cosh(x) - I*sinh(x)] assert [exptrigsimp(ei) == ei for ei in ue] res = [] ok = [y*tanh(1), 1/(y*tanh(1)), I*y*tan(1), -I/(y*tan(1)), y*tanh(x), 1/(y*tanh(x)), I*y*tan(x), -I/(y*tan(x)), y*tanh(1 + I), 1/(y*tanh(1 + I))] for a in (1, I, x, I*x, 1 + I): w = exp(a) eq = y*(w - 1/w)/(w + 1/w) s = simplify(eq) assert s == exptrigsimp(eq) res.append(s) sinv = simplify(1/eq) assert sinv == exptrigsimp(1/eq) res.append(sinv) assert all(valid(i, j) for i, j in zip(res, ok)) for a in range(1, 3): w = exp(a) e = w + 1/w s = simplify(e) assert s == exptrigsimp(e) assert valid(s, 2*cosh(a)) e = w - 1/w s = simplify(e) assert s == exptrigsimp(e) assert valid(s, 2*sinh(a)) sympy-0.7.4.1/sympy/simplify/tests/test_fu.py0000644000175000017500000003705312253362407021456 0ustar georgeskgeorgeskfrom sympy import ( Add, Mul, S, Symbol, cos, cot, csc, pi, I, sec, sin, sqrt, tan, root, powsimp, symbols, sinh, cosh, tanh, coth) from sympy.simplify.fu import ( L, TR1, TR10, TR10i, TR11, TR12, TR12i, TR13, TR14, TR15, TR16, TR111, TR2, TR2i, TR3, TR5, TR6, TR7, TR8, TR9, TRmorrie, _TR56 as T, hyper_as_trig, csc, fu, process_common_addends, sec, trig_split, as_f_sign_1) from sympy.utilities.randtest import test_numerically from sympy.abc import a, b, c, x, y, z def test_TR1(): assert TR1(2*csc(x) + sec(x)) == 1/cos(x) + 2/sin(x) def test_TR2(): assert TR2(tan(x)) == sin(x)/cos(x) assert TR2(cot(x)) == cos(x)/sin(x) assert TR2(tan(tan(x) - sin(x)/cos(x))) == 0 def test_TR2i(): # just a reminder that ratios of powers only simplify if both # numerator and denominator satisfy the condition that each # has a positive base or an integer exponent; e.g. the following, # at y=-1, x=1/2 gives sqrt(2)*I != -sqrt(2)*I assert powsimp(2**x/y**x) != (2/y)**x assert TR2i(sin(x)/cos(x)) == tan(x) assert TR2i(sin(x)*sin(y)/cos(x)) == tan(x)*sin(y) assert TR2i(1/(sin(x)/cos(x))) == 1/tan(x) assert TR2i(1/(sin(x)*sin(y)/cos(x))) == 1/tan(x)/sin(y) assert TR2i(sin(1)/(cos(1) + 1), half=True) == tan(S.Half) assert TR2i(sin(2)/(cos(2) + 1), half=True) == tan(1) assert TR2i(sin(4)/(cos(4) + 1), half=True) == tan(2) assert TR2i(sin(5)/(cos(5) + 1), half=True) == tan(5*S.Half) assert TR2i((cos(1) + 1)/sin(1), half=True) == 1/tan(S.Half) assert TR2i((cos(2) + 1)/sin(2), half=True) == 1/tan(1) assert TR2i((cos(4) + 1)/sin(4), half=True) == 1/tan(2) assert TR2i((cos(5) + 1)/sin(5), half=True) == 1/tan(5*S.Half) assert TR2i((cos(1) + 1)**(-a)*sin(1)**a, half=True) == tan(S.Half)**a assert TR2i((cos(2) + 1)**(-a)*sin(2)**a, half=True) == tan(1)**a assert TR2i((cos(4) + 1)**(-a)*sin(4)**a, half=True) == (cos(4) + 1)**(-a)*sin(4)**a assert TR2i((cos(5) + 1)**(-a)*sin(5)**a, half=True) == (cos(5) + 1)**(-a)*sin(5)**a assert TR2i((cos(1) + 1)**a*sin(1)**(-a), half=True) == tan(S.Half)**(-a) assert TR2i((cos(2) + 1)**a*sin(2)**(-a), half=True) == tan(1)**(-a) assert TR2i((cos(4) + 1)**a*sin(4)**(-a), half=True) == (cos(4) + 1)**a*sin(4)**(-a) assert TR2i((cos(5) + 1)**a*sin(5)**(-a), half=True) == (cos(5) + 1)**a*sin(5)**(-a) i = symbols('i', integer=True) assert TR2i(((cos(5) + 1)**i*sin(5)**(-i)), half=True) == tan(5*S.Half)**(-i) assert TR2i(1/((cos(5) + 1)**i*sin(5)**(-i)), half=True) == tan(5*S.Half)**i def test_TR3(): assert TR3(cos(y - x*(y - x))) == cos(x*(x - y) + y) assert cos(pi/2 + x) == -sin(x) assert cos(30*pi/2 + x) == -cos(x) for f in (cos, sin, tan, cot, csc, sec): i = f(3*pi/7) j = TR3(i) assert test_numerically(i, j) and i.func != j.func def test__TR56(): h = lambda x: 1 - x assert T(sin(x)**3, sin, cos, h, 4, False) == sin(x)**3 assert T(sin(x)**10, sin, cos, h, 4, False) == sin(x)**10 assert T(sin(x)**6, sin, cos, h, 6, False) == (-cos(x)**2 + 1)**3 assert T(sin(x)**6, sin, cos, h, 6, True) == sin(x)**6 assert T(sin(x)**8, sin, cos, h, 10, True) == (-cos(x)**2 + 1)**4 def test_TR5(): assert TR5(sin(x)**2) == -cos(x)**2 + 1 assert TR5(sin(x)**-2) == sin(x)**(-2) assert TR5(sin(x)**4) == (-cos(x)**2 + 1)**2 def test_TR6(): assert TR6(cos(x)**2) == -sin(x)**2 + 1 assert TR6(cos(x)**-2) == cos(x)**(-2) assert TR6(cos(x)**4) == (-sin(x)**2 + 1)**2 def test_TR7(): assert TR7(cos(x)**2) == cos(2*x)/2 + S(1)/2 assert TR7(cos(x)**2 + 1) == cos(2*x)/2 + S(3)/2 def test_TR8(): assert TR8(cos(2)*cos(3)) == cos(5)/2 + cos(1)/2 assert TR8(cos(2)*sin(3)) == sin(5)/2 + sin(1)/2 assert TR8(sin(2)*sin(3)) == -cos(5)/2 + cos(1)/2 assert TR8(sin(1)*sin(2)*sin(3)) == sin(4)/4 - sin(6)/4 + sin(2)/4 assert TR8(cos(2)*cos(3)*cos(4)*cos(5)) == \ cos(4)/4 + cos(10)/8 + cos(2)/8 + cos(8)/8 + cos(14)/8 + \ cos(6)/8 + S(1)/8 assert TR8(cos(2)*cos(3)*cos(4)*cos(5)*cos(6)) == \ cos(10)/8 + cos(4)/8 + 3*cos(2)/16 + cos(16)/16 + cos(8)/8 + \ cos(14)/16 + cos(20)/16 + cos(12)/16 + S(1)/16 + cos(6)/8 assert TR8(sin(3*pi/7)**2*cos(3*pi/7)**2/(16*sin(pi/7)**2)) == S(1)/64 def test_TR9(): a = S(1)/2 b = 3*a assert TR9(a) == a assert TR9(cos(1) + cos(2)) == 2*cos(a)*cos(b) assert TR9(cos(1) - cos(2)) == 2*sin(a)*sin(b) assert TR9(sin(1) - sin(2)) == -2*sin(a)*cos(b) assert TR9(sin(1) + sin(2)) == 2*sin(b)*cos(a) assert TR9(cos(1) + 2*sin(1) + 2*sin(2)) == cos(1) + 4*sin(b)*cos(a) assert TR9(cos(4) + cos(2) + 2*cos(1)*cos(3)) == 4*cos(1)*cos(3) assert TR9((cos(4) + cos(2))/cos(3)/2 + cos(3)) == 2*cos(1)*cos(2) assert TR9(cos(3) + cos(4) + cos(5) + cos(6)) == \ 4*cos(S(1)/2)*cos(1)*cos(S(9)/2) assert TR9(cos(3) + cos(3)*cos(2)) == cos(3) + cos(2)*cos(3) assert TR9(-cos(y) + cos(x*y)) == -2*sin(x*y/2 - y/2)*sin(x*y/2 + y/2) assert TR9(-sin(y) + sin(x*y)) == 2*sin(x*y/2 - y/2)*cos(x*y/2 + y/2) c = cos(x) s = sin(x) for si in ((1, 1), (1, -1), (-1, 1), (-1, -1)): for a in ((c, s), (s, c), (cos(x), cos(x*y)), (sin(x), sin(x*y))): args = zip(si, a) ex = Add(*[Mul(*ai) for ai in args]) t = TR9(ex) assert not (a[0].func == a[1].func and ( not test_numerically(ex, t.expand(trig=True)) or t.is_Add) or a[1].func != a[0].func and ex != t) def test_TR10(): assert TR10(cos(a + b)) == -sin(a)*sin(b) + cos(a)*cos(b) assert TR10(sin(a + b)) == sin(a)*cos(b) + sin(b)*cos(a) assert TR10(sin(a + b + c)) == \ (-sin(a)*sin(b) + cos(a)*cos(b))*sin(c) + \ (sin(a)*cos(b) + sin(b)*cos(a))*cos(c) assert TR10(cos(a + b + c)) == \ (-sin(a)*sin(b) + cos(a)*cos(b))*cos(c) - \ (sin(a)*cos(b) + sin(b)*cos(a))*sin(c) def test_TR10i(): assert TR10i(cos(1)*cos(3) + sin(1)*sin(3)) == cos(2) assert TR10i(cos(1)*cos(3) - sin(1)*sin(3)) == cos(4) assert TR10i(cos(1)*sin(3) - sin(1)*cos(3)) == sin(2) assert TR10i(cos(1)*sin(3) + sin(1)*cos(3)) == sin(4) assert TR10i(cos(1)*sin(3) + sin(1)*cos(3) + 7) == sin(4) + 7 assert TR10i(cos(1)*sin(3) + sin(1)*cos(3) + cos(3)) == cos(3) + sin(4) assert TR10i(2*cos(1)*sin(3) + 2*sin(1)*cos(3) + cos(3)) == \ 2*sin(4) + cos(3) assert TR10i(cos(2)*cos(3) + sin(2)*(cos(1)*sin(2) + cos(2)*sin(1))) == \ cos(1) eq = (cos(2)*cos(3) + sin(2)*( cos(1)*sin(2) + cos(2)*sin(1)))*cos(5) + sin(1)*sin(5) assert TR10i(eq) == TR10i(eq.expand()) == cos(4) assert TR10i(sqrt(2)*cos(x)*x + sqrt(6)*sin(x)*x) == \ 2*sqrt(2)*x*sin(x + pi/6) assert TR10i(cos(x)/sqrt(6) + sin(x)/sqrt(2) + cos(x)/sqrt(6)/3 + sin(x)/sqrt(2)/3) == 4*sqrt(6)*sin(x + pi/6)/9 assert TR10i(cos(x)/sqrt(6) + sin(x)/sqrt(2) + cos(y)/sqrt(6)/3 + sin(y)/sqrt(2)/3) == \ sqrt(6)*sin(x + pi/6)/3 + sqrt(6)*sin(y + pi/6)/9 assert TR10i(cos(x) + sqrt(3)*sin(x) + 2*sqrt(3)*cos(x + pi/6)) == 4*cos(x) assert TR10i(cos(x) + sqrt(3)*sin(x) + 2*sqrt(3)*cos(x + pi/6) + 4*sin(x)) == 4*sqrt(2)*sin(x + pi/4) assert TR10i(cos(2)*sin(3) + sin(2)*cos(4)) == \ sin(2)*cos(4) + sin(3)*cos(2) A = Symbol('A', commutative=False) assert TR10i(sqrt(2)*cos(x)*A + sqrt(6)*sin(x)*A) == \ 2*sqrt(2)*sin(x + pi/6)*A c = cos(x) s = sin(x) h = sin(y) r = cos(y) for si in ((1, 1), (1, -1), (-1, 1), (-1, -1)): for a in ((c*r, s*h), (c*h, s*r)): # explicit 2-args args = zip(si, a) ex = Add(*[Mul(*ai) for ai in args]) t = TR10i(ex) assert not (ex - t.expand(trig=True) or t.is_Add) c = cos(x) s = sin(x) h = sin(pi/6) r = cos(pi/6) for si in ((1, 1), (1, -1), (-1, 1), (-1, -1)): for a in ((c*r, s*h), (c*h, s*r)): # induced args = zip(si, a) ex = Add(*[Mul(*ai) for ai in args]) t = TR10i(ex) assert not (ex - t.expand(trig=True) or t.is_Add) def test_TR11(): assert TR11(sin(2*x)) == 2*sin(x)*cos(x) assert TR11(sin(4*x)) == 4*((-sin(x)**2 + cos(x)**2)*sin(x)*cos(x)) assert TR11(sin(4*x/3)) == \ 4*((-sin(x/3)**2 + cos(x/3)**2)*sin(x/3)*cos(x/3)) assert TR11(cos(2*x)) == -sin(x)**2 + cos(x)**2 assert TR11(cos(4*x)) == \ (-sin(x)**2 + cos(x)**2)**2 - 4*sin(x)**2*cos(x)**2 assert TR11(cos(2)) == cos(2) assert TR11(cos(3*pi/7), 2*pi/7) == -cos(2*pi/7)**2 + sin(2*pi/7)**2 assert TR11(cos(4), 2) == -sin(2)**2 + cos(2)**2 assert TR11(cos(6), 2) == cos(6) assert TR11(sin(x)/cos(x/2), x/2) == 2*sin(x/2) def test_TR12(): assert TR12(tan(x + y)) == (tan(x) + tan(y))/(-tan(x)*tan(y) + 1) assert TR12(tan(x + y + z)) ==\ (tan(z) + (tan(x) + tan(y))/(-tan(x)*tan(y) + 1))/( 1 - (tan(x) + tan(y))*tan(z)/(-tan(x)*tan(y) + 1)) assert TR12(tan(x*y)) == tan(x*y) def test_TR13(): assert TR13(tan(3)*tan(2)) == -tan(2)/tan(5) - tan(3)/tan(5) + 1 assert TR13(cot(3)*cot(2)) == 1 + cot(3)*cot(5) + cot(2)*cot(5) assert TR13(tan(1)*tan(2)*tan(3)) == \ (-tan(2)/tan(5) - tan(3)/tan(5) + 1)*tan(1) assert TR13(tan(1)*tan(2)*cot(3)) == \ (-tan(2)/tan(3) + 1 - tan(1)/tan(3))*cot(3) def test_L(): assert L(cos(x) + sin(x)) == 2 def test_fu(): assert fu(sin(50)**2 + cos(50)**2 + sin(pi/6)) == S(3)/2 assert fu(sqrt(6)*cos(x) + sqrt(2)*sin(x)) == 2*sqrt(2)*sin(x + pi/3) eq = sin(x)**4 - cos(y)**2 + sin(y)**2 + 2*cos(x)**2 assert fu(eq) == cos(x)**4 - 2*cos(y)**2 + 2 assert fu(S.Half - cos(2*x)/2) == sin(x)**2 assert fu(sin(a)*(cos(b) - sin(b)) + cos(a)*(sin(b) + cos(b))) == \ sqrt(2)*sin(a + b + pi/4) assert fu(sqrt(3)*cos(x)/2 + sin(x)/2) == sin(x + pi/3) assert fu(1 - sin(2*x)**2/4 - sin(y)**2 - cos(x)**4) == \ -cos(x)**2 + cos(y)**2 assert fu(cos(4*pi/9)) == sin(pi/18) assert fu(cos(pi/9)*cos(2*pi/9)*cos(3*pi/9)*cos(4*pi/9)) == S(1)/16 assert fu( tan(7*pi/18) + tan(5*pi/18) - sqrt(3)*tan(5*pi/18)*tan(7*pi/18)) == \ -sqrt(3) assert fu(tan(1)*tan(2)) == tan(1)*tan(2) expr = Mul(*[cos(2**i) for i in range(10)]) assert fu(expr) == sin(1024)/(1024*sin(1)) def test_objective(): assert fu(sin(x)/cos(x), measure=lambda x: x.count_ops()) == \ tan(x) assert fu(sin(x)/cos(x), measure=lambda x: -x.count_ops()) == \ sin(x)/cos(x) def test_process_common_addends(): # this tests that the args are not evaluated as they are given to do # and that key2 works when key1 is False do = lambda x: Add(*[i**(i%2) for i in x.args]) process_common_addends(Add(*[1, 2, 3, 4], evaluate=False), do, key2=lambda x: x%2, key1=False) == 1**1 + 3**1 + 2**0 + 4**0 def test_trig_split(): assert trig_split(cos(x), cos(y)) == (1, 1, 1, x, y, True) assert trig_split(2*cos(x), -2*cos(y)) == (2, 1, -1, x, y, True) assert trig_split(cos(x)*sin(y), cos(y)*sin(y)) == \ (sin(y), 1, 1, x, y, True) assert trig_split(cos(x), -sqrt(3)*sin(x), two=True) == \ (2, 1, -1, x, pi/6, False) assert trig_split(cos(x), sin(x), two=True) == \ (sqrt(2), 1, 1, x, pi/4, False) assert trig_split(cos(x), -sin(x), two=True) == \ (sqrt(2), 1, -1, x, pi/4, False) assert trig_split(sqrt(2)*cos(x), -sqrt(6)*sin(x), two=True) == \ (2*sqrt(2), 1, -1, x, pi/6, False) assert trig_split(-sqrt(6)*cos(x), -sqrt(2)*sin(x), two=True) == \ (-2*sqrt(2), 1, 1, x, pi/3, False) assert trig_split(cos(x)/sqrt(6), sin(x)/sqrt(2), two=True) == \ (sqrt(6)/3, 1, 1, x, pi/6, False) assert trig_split(-sqrt(6)*cos(x)*sin(y), -sqrt(2)*sin(x)*sin(y), two=True) == \ (-2*sqrt(2)*sin(y), 1, 1, x, pi/3, False) assert trig_split(cos(x), sin(x)) is None assert trig_split(cos(x), sin(z)) is None assert trig_split(2*cos(x), -sin(x)) is None assert trig_split(cos(x), -sqrt(3)*sin(x)) is None assert trig_split(cos(x)*cos(y), sin(x)*sin(z)) is None assert trig_split(cos(x)*cos(y), sin(x)*sin(y)) is None assert trig_split(-sqrt(6)*cos(x), sqrt(2)*sin(x)*sin(y), two=True) is \ None assert trig_split(sqrt(3)*sqrt(x), cos(3), two=True) is None assert trig_split(sqrt(3)*root(x, 3), sin(3)*cos(2), two=True) is None assert trig_split(cos(5)*cos(6), cos(7)*sin(5), two=True) is None def test_TRmorrie(): assert TRmorrie(7*Mul(*[cos(i) for i in range(10)])) == \ 7*sin(12)*sin(16)*cos(5)*cos(7)*cos(9)/(64*sin(1)*sin(3)) assert TRmorrie(x) == x assert TRmorrie(2*x) == 2*x e = cos(pi/7)*cos(2*pi/7)*cos(4*pi/7) assert TR8(TRmorrie(e)) == -S(1)/8 e = Mul(*[cos(2**i*pi/17) for i in range(1, 17)]) assert TR8(TR3(TRmorrie(e))) == S(1)/65536 def test_hyper_as_trig(): from sympy.simplify.fu import _osborne, _osbornei eq = sinh(x)**2 + cosh(x)**2 t, f = hyper_as_trig(eq) assert f(fu(t)) == cosh(2*x) assert _osborne(cosh(x)) == cos(x) assert _osborne(sinh(x)) == I*sin(x) assert _osborne(tanh(x)) == I*tan(x) assert _osborne(coth(x)) == cot(x)/I assert _osbornei(cos(x)) == cosh(x) assert _osbornei(sin(x)) == sinh(x)/I assert _osbornei(tan(x)) == tanh(x)/I assert _osbornei(cot(x)) == coth(x)*I assert _osbornei(sec(x)) == 1/cosh(x) assert _osbornei(csc(x)) == I/sinh(x) def test_TR12i(): ta, tb, tc = [tan(i) for i in (a, b, c)] assert TR12i((ta + tb)/(-ta*tb + 1)) == tan(a + b) assert TR12i((ta + tb)/(ta*tb - 1)) == -tan(a + b) assert TR12i((-ta - tb)/(ta*tb - 1)) == tan(a + b) eq = (ta + tb)/(-ta*tb + 1)**2*(-3*ta - 3*tc)/(2*(ta*tc - 1)) assert TR12i(eq.expand()) == \ -3*tan(a + b)*tan(a + c)/(tan(a) + tan(b) - 1)/2 assert TR12i(tan(x)/sin(x)) == tan(x)/sin(x) eq = (ta + cos(2))/(-ta*tb + 1) assert TR12i(eq) == eq eq = (ta + tb + 2)**2/(-ta*tb + 1) assert TR12i(eq) == eq eq = ta/(-ta*tb + 1) assert TR12i(eq) == eq eq = (((ta + tb)*(a + 1)).expand())**2/(ta*tb - 1) assert TR12i(eq) == -(a + 1)**2*tan(a + b) def test_TR14(): eq = (cos(x) - 1)*(cos(x) + 1) ans = -sin(x)**2 assert TR14(eq) == ans assert TR14(1/eq) == 1/ans assert TR14((cos(x) - 1)**2*(cos(x) + 1)**2) == ans**2 assert TR14((cos(x) - 1)**2*(cos(x) + 1)**3) == ans**2*(cos(x) + 1) assert TR14((cos(x) - 1)**3*(cos(x) + 1)**2) == ans**2*(cos(x) - 1) eq = (cos(x) - 1)**y*(cos(x) + 1)**y assert TR14(eq) == eq eq = (cos(x) - 2)**y*(cos(x) + 1) assert TR14(eq) == eq eq = (tan(x) - 2)**2*(cos(x) + 1) assert TR14(eq) == eq i = symbols('i', integer=True) assert TR14((cos(x) - 1)**i*(cos(x) + 1)**i) == ans**i assert TR14((sin(x) - 1)**i*(sin(x) + 1)**i) == (-cos(x)**2)**i # could use extraction in this case eq = (cos(x) - 1)**(i + 1)*(cos(x) + 1)**i assert TR14(eq) in [(cos(x) - 1)*ans**i, eq] assert TR14((sin(x) - 1)*(sin(x) + 1)) == -cos(x)**2 p1 = (cos(x) + 1)*(cos(x) - 1) p2 = (cos(y) - 1)*2*(cos(y) + 1) p3 = (3*(cos(y) - 1))*(3*(cos(y) + 1)) assert TR14(p1*p2*p3*(x - 1)) == -18*((x - 1)*sin(x)**2*sin(y)**4) def test_TR15_16_17(): assert TR15(1 - 1/sin(x)**2) == -cot(x)**2 assert TR16(1 - 1/cos(x)**2) == -tan(x)**2 assert TR111(1 - 1/tan(x)**2) == 1 - cot(x)**2 def test_as_f_sign_1(): assert as_f_sign_1(x + 1) == (1, x, 1) assert as_f_sign_1(x - 1) == (1, x, -1) assert as_f_sign_1(-x + 1) == (-1, x, -1) assert as_f_sign_1(-x - 1) == (-1, x, 1) assert as_f_sign_1(2*x + 2) == (2, x, 1) assert as_f_sign_1(x*y - y) == (y, x, -1) assert as_f_sign_1(-x*y + y) == (-y, x, -1) sympy-0.7.4.1/sympy/simplify/tests/test_traversaltools.py0000644000175000017500000000155312253362407024124 0ustar georgeskgeorgesk"""Tools for applying functions to specified parts of expressions. """ from sympy.simplify.traversaltools import use from sympy import expand, factor, I from sympy.abc import x, y def test_use(): assert use(0, expand) == 0 f = (x + y)**2*x + 1 assert use(f, expand, level=0) == x**3 + 2*x**2*y + x*y**2 + + 1 assert use(f, expand, level=1) == x**3 + 2*x**2*y + x*y**2 + + 1 assert use(f, expand, level=2) == 1 + x*(2*x*y + x**2 + y**2) assert use(f, expand, level=3) == (x + y)**2*x + 1 f = (x**2 + 1)**2 - 1 kwargs = {'gaussian': True} assert use(f, factor, level=0, kwargs=kwargs) == x**2*(x**2 + 2) assert use(f, factor, level=1, kwargs=kwargs) == (x + I)**2*(x - I)**2 - 1 assert use(f, factor, level=2, kwargs=kwargs) == (x + I)**2*(x - I)**2 - 1 assert use(f, factor, level=3, kwargs=kwargs) == (x**2 + 1)**2 - 1 sympy-0.7.4.1/sympy/simplify/tests/__init__.py0000644000175000017500000000000012253362407021522 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/simplify/__init__.py0000644000175000017500000000131312253362407020370 0ustar georgeskgeorgesk"""The module helps converting SymPy expressions into shorter forms of them. for example: the expression E**(pi*I) will be converted into -1 the expression (x+x)**2 will be converted into 4*x**2 """ from .simplify import (collect, rcollect, separate, radsimp, ratsimp, fraction, simplify, trigsimp, powsimp, combsimp, hypersimp, hypersimilar, nsimplify, logcombine, separatevars, numer, denom, powdenest, posify, polarify, unpolarify, collect_const, signsimp, besselsimp, ratsimpmodprime, exptrigsimp) from .fu import FU, fu from .sqrtdenest import sqrtdenest from .cse_main import cse from .traversaltools import use from .epathtools import epath, EPath from .hyperexpand import hyperexpand sympy-0.7.4.1/sympy/utilities/0000755000175000017500000000000012253362407016440 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/utilities/exceptions.py0000644000175000017500000001305512253362407021177 0ustar georgeskgeorgesk""" General SymPy exceptions and warnings. """ from __future__ import print_function, division import warnings from sympy.utilities.misc import filldedent class SymPyDeprecationWarning(DeprecationWarning): r"""A warning for deprecated features of SymPy. This class is expected to be used with the warnings.warn function (note that one has to explicitly turn on deprecation warnings): >>> import warnings >>> from sympy.utilities.exceptions import SymPyDeprecationWarning >>> warnings.simplefilter( ... "always", SymPyDeprecationWarning) >>> warnings.warn( ... "Don't do this, it's deprecated", ... SymPyDeprecationWarning) #doctest:+SKIP __main__:1: SymPyDeprecationWarning: "Don't do this, it's deprecated" The recommended way to use this class is, however, is by calling the warn method after constructing the message: >>> SymPyDeprecationWarning("Don't do this, it's deprecated.").warn() #doctest:+SKIP __main__:1: SymPyDeprecationWarning: Don't do this, it's deprecated. warning (see_above, SymPyDeprecationWarning) To provide additional information, create an instance of this class in this way: >>> SymPyDeprecationWarning( ... feature="Such and such", ... last_supported_version="1.2.3", ... useinstead="this other feature") Such and such has been deprecated. It will be last supported in SymPy version 1.2.3. Use this other feature instead. Note that the text in ``feature`` begins a sentence, so if it begins with a plain English word, the first letter of that word should be capitalized. Either (or both) of the arguments ``last_supported_version`` and ``useinstead`` can be omitted. In this case the corresponding sentence will not be shown: >>> SymPyDeprecationWarning(feature="Such and such", ... useinstead="this other feature") Such and such has been deprecated. Use this other feature instead. You can still provide the argument value. If it is a string, it will be appended to the end of the message: >>> SymPyDeprecationWarning( ... feature="Such and such", ... useinstead="this other feature", ... value="Contact the developers for further information.") Such and such has been deprecated. Use this other feature instead. Contact the developers for further information. If, however, the argument value does not hold a string, a string representation of the object will be appended to the message: >>> SymPyDeprecationWarning( ... feature="Such and such", ... useinstead="this other feature", ... value=[1,2,3]) Such and such has been deprecated. Use this other feature instead. ([1, 2, 3]) To associate an issue with a deprecation, use the ``issue`` flag. >>> SymPyDeprecationWarning( ... feature="Old feature", ... useinstead="new feature", ... issue=2142) Old feature has been deprecated. Use new feature instead. See http://code.google.com/p/sympy/issues/detail?id=2142 for more info. Every formal deprecation should have an associated issue in the Google Code issue tracker. All such issues should have the DeprecationRemoval tag. Additionally, each formal deprecation should mark the first release for which it was deprecated. Use the ``deprecated_since_version`` flag for this. >>> SymPyDeprecationWarning( ... feature="Old feature", ... useinstead="new feature", ... deprecated_since_version="0.7.2") Old feature has been deprecated since SymPy 0.7.2. Use new feature instead. Note that it may be necessary to go back through all the deprecations before a release to make sure that the version number is correct. So just use what you believe will be the next release number (this usually means bumping the minor number by one). To mark a function as deprecated, you can use the decorator @deprecated. See Also ======== sympy.core.decorators.deprecated """ def __init__(self, value=None, feature=None, last_supported_version=None, useinstead=None, issue=None, deprecated_since_version=None): self.fullMessage = "" if feature: if deprecated_since_version: self.fullMessage = "%s has been deprecated since SymPy %s. " % \ (feature, deprecated_since_version) else: self.fullMessage = "%s has been deprecated. " % feature if last_supported_version: self.fullMessage += ("It will be last supported in SymPy " "version %s. ") % last_supported_version if useinstead: self.fullMessage += "Use %s instead. " % useinstead if issue: self.fullMessage += ("See " "http://code.google.com/p/sympy/issues/detail?id=%d for more " "info. ") % issue if value: if not isinstance(value, str): value = "(%s)" % repr(value) value = " " + value else: value = "" self.fullMessage += value def __str__(self): return '\n%s\n' % filldedent(self.fullMessage) def warn(self, stacklevel=2): see_above = self.fullMessage # the next line is what the user would see after the error is printed # if stacklevel was set to 1. If you are writting a wrapper around this, # increase the stacklevel accordingly. warnings.warn(see_above, SymPyDeprecationWarning, stacklevel=stacklevel) sympy-0.7.4.1/sympy/utilities/misc.py0000644000175000017500000001430612253362407017751 0ustar georgeskgeorgesk"""Miscellaneous stuff that doesn't really fit anywhere else.""" from __future__ import print_function, division import sys import os from textwrap import fill, dedent from sympy.core.compatibility import get_function_name # 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) 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 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" # XXX: PyPy doesn't support hash randomization HASH_RANDOMIZATION = getattr(sys.flags, 'hash_randomization', False) _debug_tmp = [] _debug_iter = 0 def debug_decorator(func): """If SYMPY_DEBUG is True, it will print a nice execution tree with arguments and results of all decorated functions, else do nothing. """ from sympy import SYMPY_DEBUG if not SYMPY_DEBUG: return func def maketree(f, *args, **kw): global _debug_tmp global _debug_iter oldtmp = _debug_tmp _debug_tmp = [] _debug_iter += 1 def tree(subtrees): 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) # If there is a bug and the algorithm enters an infinite loop, enable the # following lines. It will print the names and parameters of all major functions # that are called, *before* they are called #from sympy.core.compatibility import reduce #print("%s%s %s%s" % (_debug_iter, reduce(lambda x, y: x + y, \ # map(lambda x: '-', range(1, 2 + _debug_iter))), get_function_name(f), args)) r = f(*args, **kw) _debug_iter -= 1 s = "%s%s = %s\n" % (get_function_name(f), args, r) if _debug_tmp != []: s += tree(_debug_tmp) _debug_tmp = oldtmp _debug_tmp.append(s) if _debug_iter == 0: print((_debug_tmp[0])) _debug_tmp = [] return r def decorated(*args, **kwargs): return maketree(func, *args, **kwargs) return decorated def debug(*args): """ Print ``*args`` if SYMPY_DEBUG is True, else do nothing. """ from sympy import SYMPY_DEBUG if SYMPY_DEBUG: print(*args, file=sys.stderr) 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 sympy-0.7.4.1/sympy/utilities/source.py0000644000175000017500000000257212253362407020320 0ustar georgeskgeorgesk""" This module adds several functions for interactive source code inspection. """ from __future__ import print_function, division import inspect def source(object): """ Prints the source code of a given object. """ print('In file: %s' % inspect.getsourcefile(object)) print(inspect.getsource(object)) 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 callable(lookup_view): raise AttributeError( "'%s.%s' is not a callable." % (mod_name, func_name)) return lookup_view 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:] sympy-0.7.4.1/sympy/utilities/compilef.py0000644000175000017500000004401312253362407020612 0ustar georgeskgeorgesk# needs access to libtcc and math.h # TODO: *get tcc errors (currently something like 'Unknown error 3217941984', # this makes debugging painful) # *currently the compiled function accepts too many arguments silently # *implement multi-dimensional functions for frange # *list comprehension syntax for frange? # *configuration of path to libtcc.so # *add gcc support again (easier to set up than tcc) # *fix compiler warnings # heavily inspired by http://www.cs.tut.fi/~ask/cinpy/ """ Experimental module for compiling functions to machine code. Can also be used to generate C code from SymPy expressions. Depends on libtcc. This code is experimental. It may have severe bugs. Due to the use of C, it's able to crash your Python interpreter/debugger with obscure error messages. 64 bit floats (double) are used. Overview ======== clambdify: compile a function to machine code (only useful for big functions) frange: evaluate a function on a range of numbers using machine code cexpr: translate a Python expression to a C expression genfcode: generate C code from a lambda string evanonarray: evaluate a function on an array using machine code Performance =========== Python functions using the math module are *quite* fast. For simple functions they are faster than functions compiled to machine code. So you should test to see whether lambdify is fast enough for you. Iterating is slow in Python (it's probably the biggest bottle neck). frange allows you to iterate using machine code. This can result in huge speedups. You might want to use NumPy: http://numpy.org/ For simple functions it's faster, but for big ones frange can be several times more efficient. You should experiment to see which solution is best for your application. You can run the included benchmarks to see the real performance on your machine. Configuration ============= You will probably need to compile libtcc on your own. Get the sources of tcc: http://bellard.org/tcc/ Currently it only works for a recent development version. So you might want to run the following commands (you have to use your own paths of course): $ cvs -z3 -d:pserver:anonymous@cvs.savannah.nongnu.org:/sources/tinycc co tinycc $ cd tinycc $ ./configure $ make $ gcc -shared -Wl,-soname,libtcc.so -o libtcc.so libtcc.o $ cd sympy/utilities/ $ ln -s tinycc/libtcc.so # or change libtccpath in compilef.py You might try to run libtcc_test. If something went wrong there will be bad low level Python errors probably crashing the interpreter. The error output will be printed to stdout or stderr, which might be different to your Python shell. Make sure that this module knows the path to libtcc. If everything went right, all the tests will pass. Run this file to do so and to see the results of some benchmarks. """ from __future__ import print_function, division import ctypes from sympy import Symbol, cse, sympify from sympy.utilities.lambdify import lambdastr as getlambdastr from sympy.external import import_module libtccpath = './libtcc.so' dps = 17 # decimal places of float precision # load libtcc TODO: better Windows support try: libtcc = ctypes.cdll.LoadLibrary(libtccpath) except OSError: libtcc = None if not libtcc: raise ImportError('Could not load libtcc') def __getLeftRight(expr, index, oplength=1, stopchar='+-'): """ Gets the expressions to the left and right of an operator. >>> __getLeftRight('1/(g(x)*3.5)**(x - a**x)/(x**2 + a)', 12, ... oplength=2, stopchar='+-*/') ('(g(x)*3.5)', '(x - a**x)') """ # assumes correct syntax # TODO: never repeat yourself # get left expression left = '' openbraces = 0 for char in reversed(expr[:index]): if char == ' ': # skip whitespaces but keep them left = char + left continue elif char == ')': openbraces += 1 left = char + left elif char == '(': if not openbraces: # happens when operator is in braces break openbraces -= 1 left = char + left elif char in stopchar: if openbraces: left = char + left continue else: break else: left = char + left # get right expression right = '' openbraces = 0 for char in expr[index + oplength:]: if char == ' ': # skip whitespaces but keep them right += char continue elif char == '(': openbraces += 1 right += char elif char == ')': if not openbraces: # happens when operator is in braces break openbraces -= 1 right += char elif char in stopchar: if openbraces: right += char continue else: break else: right += char return (left, right) def cexpr(pyexpr): """ Python math expression string -> C expression string """ # TODO: better spacing # replace 'a**b' with 'pow(a, b)' while True: index = pyexpr.find('**') if index != -1: left, right = __getLeftRight(pyexpr, index, 2, '+-*/') pyexpr = pyexpr.replace(left + '**' + right, ' pow(%s, %s) ' % (left.lstrip(), right.rstrip())) else: break # TODO: convert 'x**n' to 'x*x*...*x' # TODO: avoid integer division return pyexpr def _gentmpvars(): """ Generate symbols tmp1, tmp2, ... infinitely. """ i = 0 while True: i += 1 yield Symbol('tmp' + str(i)) def genfcode(lambdastr, use_cse=False): """ Python lambda string -> C function code Optionally cse() is used to eliminate common subexpressions. """ # TODO: verify lambda string # interpret lambda string varstr, fstr = lambdastr.split(': ') varstr = varstr.lstrip('lambda ') # generate C variable string cvars = varstr.split(',') cvarstr = '' for v in cvars: cvarstr += 'double %s, ' % v cvarstr = cvarstr.rstrip(', ') # convert function string to C syntax if not use_cse: cfstr = '' finalexpr = cexpr(fstr) else: # eliminate common subexpressions subs, finalexpr = cse(sympify(fstr), _gentmpvars()) assert len(finalexpr) == 1 vardec = '' cfstr = '' for symbol, expr in subs: vardec += ' double %s;\n' % symbol.name cfstr += ' %s = %s;\n' % ( symbol.name, cexpr(str(expr.evalf(dps)))) cfstr = vardec + cfstr finalexpr = cexpr(str(finalexpr[0].evalf(dps))) # generate C code code = """ inline double f(%s) { %s return %s; } """ % (cvarstr, cfstr, finalexpr) return code def __run(cmd): """ Checks the exit code of a ran command. """ if not cmd == 0: raise RuntimeError('could not run libtcc command') def _compile(code, argcount=None, fname='f', fprototype=None): """ C code with function -> compiled function Supports all standard C math functions, pi and e. Function is assumed to get and return 'double' only. Uses libtcc. """ # returned type and all arguments are double if fprototype: fprototype = ctypes.CFUNCTYPE(*fprototype) else: assert argcount, 'need argcount if no prototype is specified' fprototype = ctypes.CFUNCTYPE(*[ctypes.c_double]*(argcount + 1)) # see libtcc.h for API documentation tccstate = libtcc.tcc_new() __run(libtcc.tcc_set_output_type(tccstate, 0)) # output to memory ##print libtcc.tcc_add_library_path(tccstate, mathh) # could be dropped __run(libtcc.tcc_add_library(tccstate, 'm')) # use math.h FIXME: Windows # compile string __run(libtcc.tcc_compile_string(tccstate, code)) __run(libtcc.tcc_relocate(tccstate)) # fails if link error # create C variable to get result symbol = ctypes.c_long() __run(libtcc.tcc_get_symbol(tccstate, ctypes.byref(symbol), fname)) # return reference to C function return fprototype(symbol.value) # expr needs to work with lambdastr def clambdify(args, expr, **kwargs): """ SymPy expression -> compiled function Supports all standard C math functions, pi and e. >>> from sympy import sqrt >>> from sympy.abc import x, y >>> cf = clambdify((x,y), sqrt(x*y)) >>> cf(0.5, 4) 1.4142135623730951 """ # convert function to lambda string s = getlambdastr(args, expr.evalf(21)) # generate code code = """ # include # define pi M_PI # define e M_E %s """ % genfcode(s, **kwargs) # compile code return _compile(code, len(args)) def frange(*args, **kwargs): """ frange(lambdastr, [start,] stop[, step]) -> ctypes double array Evaluates function on range using machine code. Currently only one-dimensional functions are supported. For simple functions it's somewhat slower than NumPy. For big functions it can be several times faster. lambdastr has the same restrictions as in clambdify. >>> frange('lambda x: sqrt(x)', 1, 4) # doctest: +ELLIPSIS <__main__.c_double_Array_3 object at ...> >>> for i in _: ... print(i) ... 1.0 1.41421356237 1.73205080757 """ if len(args) > 4: raise TypeError('expected at most 4 arguments, got %i' % len(args)) if len(args) < 2: raise TypeError('expected at least 2 argument, got %i' % len(args)) # interpret arguments lambdastr = args[0] start = 0 step = 1 if len(args) == 2: stop = args[1] elif len(args) >= 3: start = args[1] stop = args[2] if len(args) == 4: step = args[3] assert start + step != start, \ 'step is too small and would cause an infinite loop' # determine length of resulting array # TODO: do this better length = stop - start if length % step == 0: length = length/step - 1 # exclude last one else: length = length//step if step > 0: if start < stop: length += 1 # include first one else: if start > stop: length += 1 # include first one if length < 0: length = 0 assert length == int(length) length = int(length) # create array a = (ctypes.c_double * length)() # generate code vardef = 'double* MAX; double x = %f;' % start loopbody = '*result = f(x); x += %f;' % step code = """ # include # define pi M_PI # define e M_E %s void evalonrange(double *result, int n) { %s for (MAX = result + n; result < MAX; result++) { %s } } """ % (genfcode(lambdastr, **kwargs), vardef, loopbody) # compile and run evalonrange = _compile(code, fname='evalonrange', fprototype=[None, ctypes.c_void_p, ctypes.c_int]) evalonrange(ctypes.byref(a), ctypes.c_int(length)) # return ctypes array with results return a def evalonarray(lambdastr, array, length=None, **kwargs): """ Evaluates a function on an array using machine code. array can be a numpy array, a ctypes array or a pointer to an array. In the latter case, the correct length must be specified. array will be overwritten! Make a copy before to avoid this. """ # interpret arguments if hasattr(array, 'ctypes'): # numpy array pointer = array.ctypes.get_as_parameter() length = len(array) elif isinstance(array, ctypes.Array): # ctypes array pointer = ctypes.byref(array) length = len(array) elif isinstance(array, ctypes.c_void_p): # ctypes pointer FIXME pointer = array assert isinstance(length, int) and not length < 0 else: raise ValueError('array type not recognized') # generate code code = """ # include # define pi M_PI # define e M_E %s void evalonarray(double *array, int length) { double* MAX; for (MAX = array + length; array < MAX; array++) { *array = f(*array); } } """ % genfcode(lambdastr, **kwargs) # compile an run on array run = _compile(code, fname='evalonarray', fprototype=[None, ctypes.c_void_p, ctypes.c_int]) run(pointer, length) ######### # TESTS # ######### from sympy import sqrt, pi, lambdify from math import exp as _exp, cos as _cos, sin as _sin def test_cexpr(): expr = '1/(g(x)*3.5)**(x - a**x)/(x**2 + a)' assert cexpr(expr).replace(' ', '') == \ '1/pow((g(x)*3.5),(x-pow(a,x)))/(pow(x,2)+a)' def test_clambdify(): x = Symbol('x') y = Symbol('y') z = Symbol('z') f1 = sqrt(x*y) pf1 = lambdify((x, y), f1, 'math') cf1 = clambdify((x, y), f1) for i in xrange(10): assert cf1(i, 10 - i) == pf1(i, 10 - i) f2 = (x - y) / z * pi pf2 = lambdify((x, y, z), f2, 'math') cf2 = clambdify((x, y, z), f2) assert round(pf2(1, 2, 3), 14) == round(cf2(1, 2, 3), 14) # FIXME: slight difference in precision def test_frange(): fstr = 'lambda x: _exp(x)*_cos(x)**x' f = eval(fstr) a = frange(fstr, 30, 168, 3) args = range(30, 168, 3) assert len(a) == len(args) for i in xrange(len(a)): assert a[i] == f(args[i]) assert len(frange('lambda x: x', 0, -10000)) == 0 assert len(frange('lambda x: x', -1, -1, 0.0001)) == 0 a = frange('lambda x: x', -5, 5, 0.1) b = range(-50, 50) assert len(a) == len(b) for i in xrange(len(a)): assert int(round(a[i]*10)) == b[i] a = frange('lambda x: x', 17, -9, -3) b = range(17, -9, -3) assert len(a) == len(b) for i in xrange(len(a)): assert a[i] == b[i] a = frange('lambda x: x', 2.7, -3.1, -1.01) b = range(270, -310, -101) assert len(a) == len(b) for i in xrange(len(a)): assert int(round(a[i]*100)) == b[i] assert frange('lambda x: x', 0.2, 0.1, -0.1)[0] == 0.2 assert len(frange('lambda x: x', 0)) == 0 assert len(frange('lambda x: x', 1000, -1)) == 0 assert len(frange('lambda x: x', -1.23, 3.21, -0.0000001)) == 0 try: frange() assert False except TypeError: pass try: frange(1, 2, 3, 4, 5) assert False except TypeError: pass def test_evalonarray_ctypes(): a = frange('lambda x: x', 10) evalonarray('lambda x: _sin(x)', a) for i, j in enumerate(a): assert _sin(i) == j # TODO: test for ctypes pointers ## evalonarray('lambda x: asin(x)', ctypes.byref(a), len(a)) ## for i, j in enumerater(a): ## print j def test_evalonarray_numpy(): numpy = import_module('numpy') a = numpy.arange(10, dtype=float) evalonarray('lambda x: x + 1', a) for i, j in enumerate(a): assert float(i + 1) == j def test_use_cse(): args = ('lambda x: sqrt(x + 1)**sqrt(x + 1)', 1, 10) a = frange(*args) kwargs = {} kwargs['use_cse'] = True b = frange(*args, **kwargs) assert len(a) == len(b) for i in xrange(len(a)): assert a[i] == b[i] def benchmark(): """ Run some benchmarks for clambdify and frange. NumPy and Psyco are used as reference if available. """ from time import time from timeit import Timer def fbenchmark(f, var=[Symbol('x')]): """ Do some benchmarks with f using clambdify, lambdify and psyco. """ global cf, pf, psyf start = time() cf = clambdify(var, f) print('compile time (including sympy overhead): %f s' % ( time() - start)) pf = lambdify(var, f, 'math') psyf = None psyco = import_module('psyco') if psyco: psyf = lambdify(var, f, 'math') psyco.bind(psyf) code = '''for x in (i/1000. for i in range(1000)): f(%s)''' % ('x,'*len(var)).rstrip(',') t1 = Timer(code, 'from __main__ import cf as f') t2 = Timer(code, 'from __main__ import pf as f') if psyf: t3 = Timer(code, 'from __main__ import psyf as f') else: t3 = None print('for x = (0, 1, 2, ..., 999)/1000') print('20 times in 3 runs') print('compiled: %.4f %.4f %.4f' % tuple(t1.repeat(3, 20))) print('Python lambda: %.4f %.4f %.4f' % tuple(t2.repeat(3, 20))) if t3: print('Psyco lambda: %.4f %.4f %.4f' % tuple(t3.repeat(3, 20))) print('big function:') from sympy import _exp, _sin, _cos, pi, lambdify x = Symbol('x') ## f1 = diff(_exp(x)**2 - _sin(x)**pi, x) \ ## * x**12-2*x**3+2*_exp(x**2)-3*x**7+4*_exp(123+x-x**5+2*x**4) \ ## * ((x + pi)**5).expand() f1 = 2*_exp(x**2) + x**12*(-pi*_sin(x)**((-1) + pi)*_cos(x) + 2*_exp(2*x)) \ + 4*(10*pi**3*x**2 + 10*pi**2*x**3 + 5*pi*x**4 + 5*x*pi**4 + pi**5 + x**5)*_exp(123 + x + 2*x**4 - x**5) - 2*x**3 - 3*x**7 fbenchmark(f1) print() print('simple function:') y = Symbol('y') f2 = sqrt(x*y) + x*5 fbenchmark(f2, [x, y]) times = 100000 fstr = '_exp(_sin(_exp(-x**2)) + sqrt(pi)*_cos(x**5/(x**3-x**2+pi*x)))' print print('frange with f(x) =') print(fstr) print('for x=1, ..., %i' % times) print('in 3 runs including full compile time') t4 = Timer("frange('lambda x: %s', 0, %i)" % (fstr, times), 'from __main__ import frange') numpy = import_module('numpy') print('frange: %.4f %.4f %.4f' % tuple(t4.repeat(3, 1))) if numpy: t5 = Timer('x = arange(%i); result = %s' % (times, fstr), 'from numpy import arange, sqrt, exp, sin, cos, exp, pi') print('numpy: %.4f %.4f %.4f' % tuple(t5.repeat(3, 1))) # TODO: integration into fbenchmark if __name__ == '__main__': if __debug__: print('Running tests...',) numpy = import_module('numpy') test_cexpr() test_clambdify() test_frange() test_evalonarray_ctypes() if numpy: test_evalonarray_numpy() test_use_cse() import doctest doctest.testmod() print('OK') print print('Running benchmark...') benchmark() sympy-0.7.4.1/sympy/utilities/benchmarking.py0000644000175000017500000001452312253362407021447 0ustar georgeskgeorgesk"""benchmarking through py.test""" from __future__ import print_function, division import py from py.__.test.item import Item from py.__.test.terminal.terminal import TerminalSession from math import ceil as _ceil, floor as _floor, log10 import timeit from inspect import getsource from sympy.core.compatibility import exec_ # from IPython.Magic.magic_timeit #units = ["s", "ms", "\xc2\xb5s", "ns"] units = ["s", "ms", "us", "ns"] scaling = [1, 1e3, 1e6, 1e9] unitn = dict((s, i) for i, s in enumerate(units)) precision = 3 # like py.test Directory but scan for 'bench_.py' class Directory(py.test.collect.Directory): def filefilter(self, path): b = path.purebasename ext = path.ext return b.startswith('bench_') and ext == '.py' # like py.test Module but scane for 'bench_' and 'timeit_' class Module(py.test.collect.Module): def funcnamefilter(self, name): return name.startswith('bench_') or name.startswith('timeit_') # Function level benchmarking driver class Timer(timeit.Timer): def __init__(self, stmt, setup='pass', timer=timeit.default_timer, globals=globals()): # copy of timeit.Timer.__init__ # similarity index 95% self.timer = timer stmt = timeit.reindent(stmt, 8) setup = timeit.reindent(setup, 4) src = timeit.template % {'stmt': stmt, 'setup': setup} self.src = src # Save for traceback display code = compile(src, timeit.dummy_src_name, "exec") ns = {} #exec code in globals(), ns -- original timeit code exec_(code, globals, ns) # -- we use caller-provided globals instead self.inner = ns["inner"] class Function(py.__.test.item.Function): def __init__(self, *args, **kw): super(Function, self).__init__(*args, **kw) self.benchtime = None self.benchtitle = None def execute(self, target, *args): # get func source without first 'def func(...):' line src = getsource(target) src = '\n'.join( src.splitlines()[1:] ) # extract benchmark title if target.func_doc is not None: self.benchtitle = target.func_doc else: self.benchtitle = src.splitlines()[0].strip() # XXX we ignore args timer = Timer(src, globals=target.func_globals) if self.name.startswith('timeit_'): # from IPython.Magic.magic_timeit repeat = 3 number = 1 for i in range(1, 10): t = timer.timeit(number) if t >= 0.2: number *= (0.2 / t) number = int(_ceil(number)) break if t <= 0.02: # we are not close enough to that 0.2s number *= 10 else: # since we are very close to be > 0.2s we'd better adjust number # so that timing time is not too high number *= (0.2 / t) number = int(_ceil(number)) break self.benchtime = min(timer.repeat(repeat, number)) / number # 'bench_' else: self.benchtime = timer.timeit(1) class BenchSession(TerminalSession): def header(self, colitems): #self.out.sep("-", "benchmarking starts") super(BenchSession, self).header(colitems) def footer(self, colitems): super(BenchSession, self).footer(colitems) #self.out.sep("-", "benchmarking ends") self.out.write('\n') self.print_bench_results() def print_bench_results(self): self.out.write('==============================\n') self.out.write(' *** BENCHMARKING RESULTS *** \n') self.out.write('==============================\n') self.out.write('\n') # benchname, time, benchtitle results = [] for item, outcome in self._memo: if isinstance(item, Item): best = item.benchtime if best is None: # skipped or failed benchmarks tstr = '---' else: # from IPython.Magic.magic_timeit if best > 0.0: order = min(-int(_floor(log10(best)) // 3), 3) else: order = 3 tstr = "%.*g %s" % ( precision, best * scaling[order], units[order]) results.append( [item.name, tstr, item.benchtitle] ) # dot/unit align second column # FIXME simpler? this is crappy -- shame on me... wm = [0]*len(units) we = [0]*len(units) for s in results: tstr = s[1] n, u = tstr.split() # unit n un = unitn[u] try: m, e = n.split('.') except ValueError: m, e = n, '' wm[un] = max(len(m), wm[un]) we[un] = max(len(e), we[un]) for s in results: tstr = s[1] n, u = tstr.split() un = unitn[u] try: m, e = n.split('.') except ValueError: m, e = n, '' m = m.rjust(wm[un]) e = e.ljust(we[un]) if e.strip(): n = '.'.join((m, e)) else: n = ' '.join((m, e)) # let's put the number into the right place txt = '' for i in range(len(units)): if i == un: txt += n else: txt += ' '*(wm[i] + we[i] + 1) s[1] = '%s %s' % (txt, u) # align all columns besides the last one for i in range(2): w = max(len(s[i]) for s in results) for s in results: s[i] = s[i].ljust(w) # show results for s in results: self.out.write('%s | %s | %s\n' % tuple(s)) def main(args=None): # hook our Directory/Module/Function as defaults from py.__.test import defaultconftest defaultconftest.Directory = Directory defaultconftest.Module = Module defaultconftest.Function = Function # hook BenchSession as py.test session config = py.test.config config._getsessionclass = lambda: BenchSession py.test.cmdline.main(args) sympy-0.7.4.1/sympy/utilities/autowrap.py0000644000175000017500000003774012253362407020667 0ustar georgeskgeorgesk"""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) >>> binary_callable(1, 2) -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) >>> 2*f(x, y) + y y + 2*f(x, y) >>> (2*f(x, y) + y).evalf(2, subs={x: 1, y:2}) 0.e-110 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. """ from __future__ import print_function, division _doctest_depends_on = { 'exe': ('f2py', 'gfortran'), 'modules': ('numpy',)} import sys import os import shutil import tempfile from subprocess import STDOUT, CalledProcessError from sympy.core.compatibility import check_output from sympy.utilities.codegen import ( get_code_generator, Routine, OutputArgument, InOutArgument, CodeGenArgumentListError, Result ) from sympy.utilities.lambdify import implemented_function from sympy.utilities.decorator import doctest_depends_on from sympy import C class CodeWrapError(Exception): pass 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) try: retoutput = check_output(command, stderr=STDOUT) except CalledProcessError as e: raise CodeWrapError( "Error while executing command: %s. Command output is:\n%s" % ( " ".join(command), e.output)) if not self.quiet: print(retoutput) 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 = filter(lambda x: not isinstance( x, OutputArgument), routine.arguments) 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]) }, end="", file=f) def _process_files(self, routine): return @classmethod def _get_wrapped_function(cls, mod): return mod.autofunc 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 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) # 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)) 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()] @doctest_depends_on(exe=('f2py', 'gfortran'), modules=('numpy',)) 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 (, , ). 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) >>> binary_func(1, 4, 2) -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) @doctest_depends_on (exe=('f2py', 'gfortran'), modules=('numpy',)) 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 >>> from sympy.utilities.autowrap import binary_function >>> expr = ((x - y)**(25)).expand() >>> f = binary_function('f', expr) >>> type(f) >>> 2*f(x, y) 2*f(x, y) >>> f(x, y).evalf(2, subs={x: 1, y: 2}) -1.0 """ binary = autowrap(expr, **kwargs) return implemented_function(symfunc, binary) @doctest_depends_on (exe=('f2py', 'gfortran'), modules=('numpy',)) 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 >>> import numpy as np >>> f = ufuncify([x, y], y + x**2) >>> f([1, 2, 3], 2) [ 3. 6. 11.] >>> a = f(np.arange(5), 3) >>> isinstance(a, np.ndarray) True >>> print a [ 3. 4. 7. 12. 19.] """ 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) sympy-0.7.4.1/sympy/utilities/magic.py0000644000175000017500000000070112253362407020070 0ustar georgeskgeorgesk"""Functions that involve magic. """ from __future__ import print_function, division def pollute(names, objects): """Pollute the global namespace with symbols -> objects mapping. """ from inspect import currentframe frame = currentframe().f_back.f_back try: for name, obj in zip(names, objects): frame.f_globals[name] = obj finally: del frame # break cyclic dependencies as stated in inspect docs sympy-0.7.4.1/sympy/utilities/pytest.py0000644000175000017500000001107212253362407020343 0ustar georgeskgeorgesk"""py.test hacks to support XFAIL/XPASS""" from __future__ import print_function, division import sys import functools from sympy.core.compatibility import get_function_name 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: 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``. ``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 >>> with raises(ZeroDivisionError): ... n = 1/0 >>> with raises(ZeroDivisionError): ... 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): ... 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): ... 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 callable(code): 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: message = str(e) if message != "Timeout": raise XFail(get_function_name(func)) else: raise Skipped("Timeout") raise XPass(get_function_name(func)) 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 def SKIP(reason): def skipping(func): @functools.wraps(func) def inner(*args, **kwargs): skip(reason) return inner return skipping sympy-0.7.4.1/sympy/utilities/runtests.py0000644000175000017500000023046012253362407020706 0ustar georgeskgeorgesk""" 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 """ from __future__ import print_function, division import os import sys import platform 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 random import subprocess import signal import stat from sympy.core.cache import clear_cache from sympy.core.compatibility import exec_, PY3, get_function_code, string_types from sympy.utilities.misc import find_executable from sympy.external import import_module from sympy.utilities.exceptions import SymPyDeprecationWarning IS_WINDOWS = (os.name == 'nt') class Skipped(Exception): pass import __future__ # add more flags ?? future_flags = __future__.division.compiler_flag 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 not PY3: if isinstance(s, unicode): 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 PY3 and IS_WINDOWS: DocTestRunner.report_failure = _report_failure 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 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 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 \ get_function_code(object).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) 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 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 PY3): # 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) 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 The ``split`` option can be passed to split the test run into parts. The split currently only splits the test files, though this may change in the future. ``split`` should be a string of the form 'a/b', which will run part ``a`` of ``b``. For instance, to run the first half of the test suite: >>> sympy.test(split='1/2') # 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) enhance_asserts = kwargs.get("enhance_asserts", False) split = kwargs.get('split', None) r = PyTestReporter(verbose=verbose, tb=tb, colors=colors, force_colors=force_colors, split=split) 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 # Show deprecation warnings import warnings warnings.simplefilter("error", SymPyDeprecationWarning) test_files = t.get_test_files('sympy') if len(paths) == 0: matched = 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 if split: matched = split_list(matched, split) t._testfiles.extend(matched) return int(not t.test(sort=sort, timeout=timeout, slow=slow, enhance_asserts=enhance_asserts)) 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 ``split`` option can be passed to split the test run into parts. The split currently only splits the test files, though this may change in the future. ``split`` should be a string of the form 'a/b', which will run part ``a`` of ``b``. Note that the regular doctests and the Sphinx doctests are split independently. For instance, to run the first half of the test suite: >>> sympy.doctest(split='1/2') # 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", []) split = kwargs.get('split', None) 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/statistics", # prints a deprecation "doc/src/modules/statistics.rst", # warning (the module is deprecated) "sympy/utilities/compilef.py" # needs tcc ]) if import_module('numpy') is None: blacklist.extend([ "sympy/plotting/experimental_lambdify.py", "sympy/plotting/plot_implicit.py", "examples/advanced/autowrap_integrators.py", "examples/advanced/autowrap_ufuncify.py", "examples/intermediate/sample.py", "examples/intermediate/mplot2d.py", "examples/intermediate/mplot3d.py", "doc/src/modules/numeric-computation.rst" ]) else: if import_module('matplotlib') is None: blacklist.extend([ "examples/intermediate/mplot2d.py", "examples/intermediate/mplot3d.py" ]) else: # don't display matplotlib windows from sympy.plotting.plot import unset_show unset_show() if import_module('pyglet') is None: blacklist.extend(["sympy/plotting/pygletplot"]) if import_module('theano') is None: blacklist.extend(["doc/src/modules/numeric-computation.rst"]) # disabled because of doctest failures in asmeurer's bot blacklist.extend([ "sympy/utilities/autowrap.py", "examples/advanced/autowrap_integrators.py", "examples/advanced/autowrap_ufuncify.py" ]) # pytest = import_module('pytest') # py = import_module('py') # if py is None or pytest is None: # blacklist.extend([ # "sympy/conftest.py", # "sympy/utilities/benchmarking.py" # ]) # blacklist these modules until issue 1741 is resolved blacklist.extend([ "sympy/conftest.py", "sympy/utilities/benchmarking.py" ]) 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 # Show deprecation warnings import warnings warnings.simplefilter("error", SymPyDeprecationWarning) r = PyTestReporter(verbose, split=split) 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: matched = 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 if split: matched = split_list(matched, split) 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 if split: matched = split_list(matched, split) 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) sp = re.compile(r'([0-9]+)/([1-9][0-9]*)') def split_list(l, split): """ Splits a list into part a of b split should be a string of the form 'a/b'. For instance, '1/3' would give the split one of three. If the length of the list is not divisible by the number of splits, the last split will have more items. >>> from sympy.utilities.runtests import split_list >>> a = list(range(10)) >>> split_list(a, '1/3') [0, 1, 2] >>> split_list(a, '2/3') [3, 4, 5] >>> split_list(a, '3/3') [6, 7, 8, 9] """ m = sp.match(split) if not m: raise ValueError("split must be a string of the form a/b where a and b are ints") i, t = map(int, m.groups()) return l[(i - 1)*len(l)//t:i*len(l)//t] from collections import namedtuple SymPyTestResults = namedtuple('TestResults', 'failed attempted') 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 PY3: 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) runner._checker = SymPyOutputChecker() # Read the file, convert it to a test, and run it. test = parser.get_doctest(text, globs, name, filename, 0) runner.run(test, compileflags=future_flags) 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=None): 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 if seed is not None else random.random() def test(self, sort=False, timeout=False, slow=False, enhance_asserts=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, enhance_asserts) except KeyboardInterrupt: print(" interrupted by user") self._reporter.finish() raise return self._reporter.finish() def _enhance_asserts(self, source): from ast import (NodeTransformer, Compare, Name, Store, Load, Tuple, Assign, BinOp, Str, Mod, Assert, parse, fix_missing_locations) ops = {"Eq": '==', "NotEq": '!=', "Lt": '<', "LtE": '<=', "Gt": '>', "GtE": '>=', "Is": 'is', "IsNot": 'is not', "In": 'in', "NotIn": 'not in'} class Transform(NodeTransformer): def visit_Assert(self, stmt): if isinstance(stmt.test, Compare): compare = stmt.test values = [compare.left] + compare.comparators names = [ "_%s" % i for i, _ in enumerate(values) ] names_store = [ Name(n, Store()) for n in names ] names_load = [ Name(n, Load()) for n in names ] target = Tuple(names_store, Store()) value = Tuple(values, Load()) assign = Assign([target], value) new_compare = Compare(names_load[0], compare.ops, names_load[1:]) msg_format = "\n%s " + "\n%s ".join([ ops[op.__class__.__name__] for op in compare.ops ]) + "\n%s" msg = BinOp(Str(msg_format), Mod(), Tuple(names_load, Load())) test = Assert(new_compare, msg, lineno=stmt.lineno, col_offset=stmt.col_offset) return [assign, test] else: return stmt tree = parse(source) new_tree = Transform().visit(tree) return fix_missing_locations(new_tree) def test_file(self, filename, sort=True, timeout=False, slow=False, enhance_asserts=False): funcs = [] try: clear_cache() self._count += 1 gl = {'__file__': filename} random.seed(self._seed) try: if PY3: open_file = lambda: open(filename, encoding="utf8") else: open_file = lambda: open(filename) with open_file() as f: source = f.read() if enhance_asserts: try: source = self._enhance_asserts(source) except ImportError: pass code = compile(source, filename, "exec") exec_(code, 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 not disabled: # 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 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 except Exception: self._reporter.entering_filename(filename, len(funcs)) raise 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 sorted([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 sympy.core.compatibility 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 # check if there are external dependencies which need to be met if '_doctest_depends_on' in test.globs: if not self._process_dependencies(test.globs['_doctest_depends_on']): self._reporter.test_skip() continue runner = SymPyDocTestRunner(optionflags=pdoctest.ELLIPSIS | pdoctest.NORMALIZE_WHITESPACE | pdoctest.IGNORE_EXCEPTION_DETAIL) runner._checker = SymPyOutputChecker() 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 test.globs['print_function'] = print_function try: f, t = runner.run(test, compileflags=future_flags, 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] def _process_dependencies(self, deps): """ Returns ``False`` if some dependencies are not met and the test should be skipped otherwise returns ``True``. """ executables = deps.get('exe', None) moduledeps = deps.get('modules', None) viewers = deps.get('disable_viewers', None) pyglet = deps.get('pyglet', None) # print deps if executables is not None: for ex in executables: found = find_executable(ex) # print "EXE %s found %s" %(ex, found) if found is None: return False if moduledeps is not None: for extmod in moduledeps: if extmod == 'matplotlib': matplotlib = import_module( 'matplotlib', __import__kwargs={'fromlist': ['pyplot', 'cm', 'collections']}, min_module_version='1.0.0', catch=(RuntimeError,)) if matplotlib is not None: pass # print "EXTMODULE matplotlib version %s found" % \ # matplotlib.__version__ else: # print "EXTMODULE matplotlib > 1.0.0 not found" return False else: # TODO min version support mod = import_module(extmod) if mod is not None: version = "unknown" if hasattr(mod, '__version__'): version = mod.__version__ # print "EXTMODULE %s version %s found" %(extmod, version) else: # print "EXTMODULE %s not found" %(extmod) return False if viewers is not None: import tempfile tempdir = tempfile.mkdtemp() os.environ['PATH'] = '%s:%s' % (tempdir, os.environ['PATH']) if PY3: vw = '#!/usr/bin/env python3\n' \ 'import sys\n' \ 'if len(sys.argv) <= 1:\n' \ ' exit("wrong number of args")\n' else: vw = '#!/usr/bin/env python\n' \ 'import sys\n' \ 'if len(sys.argv) <= 1:\n' \ ' exit("wrong number of args")\n' for viewer in viewers: with open(os.path.join(tempdir, viewer), 'w') as fh: fh.write(vw) # make the file executable os.chmod(os.path.join(tempdir, viewer), stat.S_IREAD | stat.S_IWRITE | stat.S_IXUSR) if pyglet: # monkey-patch pyglet s.t. it does not open a window during # doctesting import pyglet class DummyWindow(object): def __init__(self, *args, **kwargs): self.has_exit=True self.width = 600 self.height = 400 def set_vsync(self, x): pass def switch_to(self): pass def push_handlers(self, x): pass def close(self): pass pyglet.window.Window = DummyWindow return True 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) if not self._recurse: return # Look for tests in a module's contained objects. if inspect.ismodule(obj): for rawname, val in obj.__dict__.items(): # Recurse to functions & classes. if inspect.isfunction(val) or inspect.isclass(val): # Make sure we don't run doctests functions or classes # from different modules if val.__module__ != module.__name__: continue assert self._from_module(module, val), \ "%s is not in module %s (rawname %s)" % (val, module, rawname) 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. for valname, val in getattr(obj, '__test__', {}).items(): if not isinstance(valname, string_types): 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, string_types)): 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): for valname, val in 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)): # Make sure we don't run doctests functions or classes # from different modules if isinstance(val, property): if val.fget.__module__ != module.__name__: continue else: if val.__module__ != module.__name__: continue assert self._from_module(module, val), \ "%s is not in module %s (valname %s)" % (val, module, valname) 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. """ lineno = None # Extract the object's docstring. If it doesn't have one, # then return None (no test for this object). if isinstance(obj, string_types): # obj is a string in the case for objects in the polys package. # Note that source_lines is a binary string (compiled polys # modules), which can't be handled by _find_lineno so determine # the line number here. docstring = obj matches = re.findall("line \d+", name) assert len(matches) == 1, \ "string '%s' does not contain lineno " % name # NOTE: this is not the exact linenumber but its better than no # lineno ;) lineno = int(matches[0][5:]) else: try: if obj.__doc__ is None: docstring = '' else: docstring = obj.__doc__ if not isinstance(docstring, string_types): docstring = str(docstring) except (TypeError, AttributeError): docstring = '' # Don't bother if the docstring is empty. if self._exclude_empty and not docstring: return None # check that properties have a docstring because _find_lineno # assumes it if isinstance(obj, property): if obj.fget.__doc__ is None: return None # Find the docstring's location in the file. if lineno is None: # handling of properties is not implemented in _find_lineno so do # it here tobj = obj if not isinstance(obj, property) else obj.fget lineno = self._find_lineno(tobj, source_lines) assert lineno is not 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] if hasattr(obj, '_doctest_depends_on'): globs['_doctest_depends_on'] = obj._doctest_depends_on else: globs['_doctest_depends_on'] = {} return self._parser.get_doctest(docstring, globs, name, filename, lineno) 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. """ 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: test.globs['print_function'] = print_function 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 class SymPyOutputChecker(pdoctest.OutputChecker): """ Compared to the OutputChecker from the stdlib our OutputChecker class supports numerical comparison of floats occuring in the output of the doctest examples """ def __init__(self): # NOTE OutputChecker is an old-style class with no __init__ method, # so we can't call the base class version of __init__ here got_floats = r'(\d+\.\d*|\.\d+)' # floats in the 'want' string may contain ellipses want_floats = got_floats + r'(\.{3})?' front_sep = r'\s|\+|\-|\*|,' back_sep = front_sep + r'|j|e' fbeg = r'^%s(?=%s|$)' % (got_floats, back_sep) fmidend = r'(?<=%s)%s(?=%s|$)' % (front_sep, got_floats, back_sep) self.num_got_rgx = re.compile(r'(%s|%s)' %(fbeg, fmidend)) fbeg = r'^%s(?=%s|$)' % (want_floats, back_sep) fmidend = r'(?<=%s)%s(?=%s|$)' % (front_sep, want_floats, back_sep) self.num_want_rgx = re.compile(r'(%s|%s)' %(fbeg, fmidend)) def check_output(self, want, got, optionflags): """ Return True iff the actual output from an example (`got`) matches the expected output (`want`). These strings are always considered to match if they are identical; but depending on what option flags the test runner is using, several non-exact match types are also possible. See the documentation for `TestRunner` for more information about option flags. """ # Handle the common case first, for efficiency: # if they're string-identical, always return true. if got == want: return True # TODO parse integers as well ? # Parse floats and compare them. If some of the parsed floats contain # ellipses, skip the comparison. matches = self.num_got_rgx.finditer(got) numbers_got = [match.group(1) for match in matches] # list of strs matches = self.num_want_rgx.finditer(want) numbers_want = [match.group(1) for match in matches] # list of strs if len(numbers_got) != len(numbers_want): return False if len(numbers_got) > 0: nw_ = [] for ng, nw in zip(numbers_got, numbers_want): if '...' in nw: nw_.append(ng) continue else: nw_.append(nw) if abs(float(ng)-float(nw)) > 1e-5: return False got = self.num_got_rgx.sub(r'%s', got) got = got % tuple(nw_) # can be used as a special sequence to signify a # blank line, unless the DONT_ACCEPT_BLANKLINE flag is used. if not (optionflags & pdoctest.DONT_ACCEPT_BLANKLINE): # Replace in want with a blank line. want = re.sub('(?m)^%s\s*?$' % re.escape(pdoctest.BLANKLINE_MARKER), '', want) # If a line in got contains only spaces, then remove the # spaces. got = re.sub('(?m)^\s*?$', '', got) if got == want: return True # This flag causes doctest to ignore any differences in the # contents of whitespace strings. Note that this can be used # in conjunction with the ELLIPSIS flag. if optionflags & pdoctest.NORMALIZE_WHITESPACE: got = ' '.join(got.split()) want = ' '.join(want.split()) if got == want: return True # The ELLIPSIS flag says to let the sequence "..." in `want` # match any substring in `got`. if optionflags & pdoctest.ELLIPSIS: if pdoctest._ellipsis_match(want, got): return True # We didn't find any match; return false. return False class Reporter(object): """ Parent class for all reporters. """ pass 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, split=None): 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 self._split = split # 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 PY3: 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\d+);" re_osx = r"(?P\d+)\s*columns;" re_solaris = r"columns\s+=\s+(?P\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 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 PY3 and IS_WINDOWS: text = text.encode('raw_unicode_escape').decode('utf8', 'ignore') elif PY3 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 implementation = platform.python_implementation() if implementation == 'PyPy': implementation += " %s.%s.%s-%s-%s" % sys.pypy_version_info self.write("executable: %s (%s) [%s]\n" % (executable, python_version, implementation)) 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.core.compatibility import GROUND_TYPES, HAS_GMPY version = '' if GROUND_TYPES =='gmpy': if HAS_GMPY == 1: import gmpy elif HAS_GMPY == 2: import gmpy2 as gmpy version = gmpy.version() self.write("ground types: %s %s\n" % (GROUND_TYPES, version)) 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") or '0' if HASH_RANDOMIZATION and (hash_seed == "random" or int(hash_seed)): self.write("on (PYTHONHASHSEED=%s)\n" % hash_seed) else: self.write("off\n") if self._split: self.write("split: %s\n" % self._split) 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): 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=None): char = "s" self._skipped += 1 if v is not None: message = str(v) 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") if v is not None: 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() sympy-0.7.4.1/sympy/utilities/timeutils.py0000644000175000017500000000400012253362407021023 0ustar georgeskgeorgesk"""Simple tools for timing functions' execution, when IPython is not available. """ from __future__ import print_function, division import timeit import math from sympy.core.compatibility import u _scales = [1e0, 1e3, 1e6, 1e9] _units = [u('s'), u('ms'), u('\u03bcs'), u('ns')] def timed(func, setup="pass", limit=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 elif limit is not None and number >= limit: 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.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 sympy-0.7.4.1/sympy/utilities/decorator.py0000644000175000017500000001337512253362407021005 0ustar georgeskgeorgesk"""Useful utility decorators. """ from __future__ import print_function, division import sys import types import inspect from sympy.core.decorators import wraps from sympy.core.compatibility import class_types, get_function_globals, get_function_name, iterable 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): try: return expr.__class__([func(f, *args, **kwargs) for f in expr]) except TypeError: return 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 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) 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) 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 class no_attrs_in_subclass(object): """Don't 'inherit' certain attributes from a base class >>> from sympy.utilities.decorator import no_attrs_in_subclass >>> class A(object): ... x = 'test' >>> A.x = no_attrs_in_subclass(A, A.x) >>> class B(A): ... pass >>> hasattr(A, 'x') True >>> hasattr(B, 'x') False """ def __init__(self, cls, f): self.cls = cls self.f = f def __get__(self, instance, owner=None): if owner == self.cls: if hasattr(self.f, '__get__'): return self.f.__get__(instance, owner) return self.f raise AttributeError def doctest_depends_on(exe=None, modules=None, disable_viewers=None): """Adds metadata about the depenencies which need to be met for doctesting the docstrings of the decorated objects.""" pyglet = False if modules is not None and 'pyglet' in modules: pyglet = True def depends_on_deco(fn): fn._doctest_depends_on = dict(exe=exe, modules=modules, disable_viewers=disable_viewers, pyglet=pyglet) # once we drop py2.5 support and use class decorators this evaluates # to True if inspect.isclass(fn): fn._doctest_depdends_on = no_attrs_in_subclass(fn, fn._doctest_depends_on) return fn return depends_on_deco def public(obj): """ Append ``obj``'s name to global ``__all__`` variable (call site). By using this decorator on functions or classes you achieve the same goal as by filling ``__all__`` variables manually, you just don't have to repeat your self (object's name). You also know if object is public at definition site, not at some random location (where ``__all__`` was set). Note that in multiple decorator setup, in almost all cases, ``@public`` decorator must be applied before any other decorators, because it relies on the pointer to object's global namespace. If you apply other decorators first, ``@public`` may end up modifying wrong namespace. Example:: >>> from sympy.utilities.decorator import public >>> __all__ Traceback (most recent call last): ... NameError: name '__all__' is not defined >>> @public ... def some_function(): ... pass >>> __all__ ['some_function'] """ if isinstance(obj, types.FunctionType): ns = get_function_globals(obj) name = get_function_name(obj) elif isinstance(obj, (type(type), class_types)): ns = sys.modules[obj.__module__].__dict__ name = obj.__name__ else: raise TypeError("expected a function or a class, got %s" % obj) if "__all__" not in ns: ns["__all__"] = [name] else: ns["__all__"].append(name) return obj sympy-0.7.4.1/sympy/utilities/codegen.py0000644000175000017500000010434312253362407020423 0ustar georgeskgeorgesk""" 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 - ... """ from __future__ import print_function, division import os from sympy import __version__ as sympy_version from sympy.core import Symbol, S, Expr, Tuple, Equality, Function from sympy.core.compatibility import is_sequence, StringIO, string_types 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 # 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 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 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 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") } 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)) 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) 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 # 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 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 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' """ class CCodeGen(CodeGen): """ Generator for C code The .write() method inherited from CodeGen will output a code file and an inteface file, .c and .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 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 \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"] 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__ 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] class FCodeGen(CodeGen): """ Generator for Fortran 95 code The .write() method inherited from CodeGen will output a code file and an inteface file, .f90 and .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"] 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) 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__ 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 # 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.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) #include "test.h" #include double f(double x, double y, double z) { return x + y*z; } >>> print(h_name) test.h >>> print(c_header) #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], string_types): # 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) sympy-0.7.4.1/sympy/utilities/lambdify.py0000644000175000017500000003764212253362407020615 0ustar georgeskgeorgesk""" This module provides convenient functions to transform sympy expressions to lambda functions which can be used to calculate numerical values very fast. """ from __future__ import print_function, division from sympy.external import import_module from sympy.core.compatibility import exec_, is_sequence, iterable, string_types 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 = { "elliptic_k": "ellipk", "elliptic_f": "ellipf", "elliptic_e": "ellipe", "elliptic_pi": "ellippi", "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", "ImmutableMatrix": "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", "ImmutableMatrix": "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] 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. The default behavior is to substitute all arguments in the provided expression with dummy symbols. This allows for applied functions (e.g. f(t)) to be supplied as arguments. Call the function with dummify=False if dummy substitution is unwanted. If you want to view the lambdified function or provide "sympy" as the module, you should probably set dummify=False. 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) Matrix([[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. module_provided = True if modules is None: module_provided = False # 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, dummify=True) 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) def lambdastr(args, expr, printer=None, dummify=False): """ 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])' """ # Transforming everything to strings. from sympy.matrices import DeferredVector from sympy import Dummy, sympify, Symbol, Function 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 def sub_args(args, dummies_dict): if isinstance(args, str): return args elif isinstance(args, DeferredVector): return str(args) elif iterable(args): flatten = lambda *n: (e for a in n for e in (flatten(*a) if iterable(a) else (a,))) dummies = flatten([sub_args(a, dummies_dict) for a in args]) return ",".join(str(a) for a in dummies) else: if isinstance(args, Function): dummies = Dummy() dummies_dict.update({args : dummies}) return str(dummies) else: return str(args) def sub_expr(expr, dummies_dict): try: expr = sympify(expr).xreplace(dummies_dict) except: if isinstance(expr, DeferredVector): pass elif isinstance(expr, dict): k = [sub_expr(sympify(a), dummies_dict) for a in expr.keys()] v = [sub_expr(sympify(a), dummies_dict) for a in expr.values()] expr = dict(zip(k, v)) elif isinstance(expr, tuple): expr = tuple(sub_expr(sympify(a), dummies_dict) for a in expr) elif isinstance(expr, list): expr = [sub_expr(sympify(a), dummies_dict) for a in expr] return expr # Transform args dummies_dict = {} if dummify: args = sub_args(args, dummies_dict) else: if isinstance(args, str): pass elif iterable(args, exclude=DeferredVector): args = ",".join(str(a) for a in args) # Transform expr if dummify: if isinstance(expr, str): pass else: expr = sub_expr(expr, dummies_dict) expr = lambdarepr(expr) 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 >>> 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 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 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 >>> 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, string_types): 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 sympy-0.7.4.1/sympy/utilities/mathml/0000755000175000017500000000000012253362407017722 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/utilities/mathml/data/0000755000175000017500000000000012253362407020633 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/utilities/mathml/data/simple_mmlctop.xsl0000644000175000017500000033740112253362407024417 0ustar georgeskgeorgesk e - + &#x2062; &#x2148; - + &#x2062; &#x2148; Polar &#x2062; Polar &#x2062; &#x2061; [ ] -1 &#x03BB; &#x2061; &#x2218; &#x2218; id id domain codomain image &#x2061; { if otherwise &#x230A; &#x230B; e ! max min max min | - - - + &#x2062; gcd lcm gcd lcm &#x2061; &#x2227; &#x2061; &#x2228; &#x2061; &#x22BB; &#x2061; &#x00AC; &#x2061; &#x00AC; &#x2061; &#x2200; : , &#x2203; : , &#x00AF; &#x211C; &#x2111; &#x2061; &#x230A; &#x2308; &#x230B; &#x2309; &#x2260; &#x2248; &#x2223; &#x2198; &#x2197; &#x2192; &#x21D2; &#x2208; &#x2209; &#x2284; &#x2288; &#x2286; &#x2282; &#x2265; &#x2264; &#x2261; ln ln log log log log d d d d &#x2032; &#x2145; &#x2202; &#x2202; &#x2202; &#x2202; &#x2202; &#x2202; &#x2207; 2 &#x2061; | &#x222A; &#x2229; \ &#x00D7; &#x2211; &#x220F; = &#x2211; &#x220F; &#x2211; &#x220F; &#x222B; &#x222B; &#x222B; &#x222B; &#x222B; d lim &#x2192; &#x03C3; &#x03C3; 2 median mode det T &#x00D7; &#x22C5; &#x2297; &#x2124; &#x211D; &#x211A; &#x2115; &#x2102; &#x2119; e &#x2148; NaN true false &#x2205; &#x03C0; &#x213D; &#x221E; sympy-0.7.4.1/sympy/utilities/mathml/data/mmltex.xsl0000644000175000017500000041415512253362407022703 0ustar georgeskgeorgesk $ $ + i / _{} e^{i } E \mathrm{} ( , ) () \left( \left[ , \right) \right] \left\{\right\} ^{(-1)} \mathrm{lambda}\: .\: \circ \mathrm{id} \mathop{\mathrm{ }} \begin{cases} \end{cases} & \text{if $ $} \\ & \text{otherwise} \left\lfloor\frac{ }{ }\right\rfloor ! \left( \frac{ }{ } \right) \ \{ , , \} - - ( - + ) ^{ } \mod ( \times ) \sqrt [ ] { } \gcd \land \lor \mathop{\mathrm{xor}} \neg \implies \ , \colon \left| \right| \overline{} \Re \Im \lfloor \rfloor \lceil \rceil = \neq > < \ge \le \equiv \approx | \int _{ } ^{ } \,d ^\prime \frac{ d^{ } }{d ^{ } d }{d } } D_{ , } \frac{\partial^{ + + } }{ \partial ^{ } } , \mathop{\mathrm{div}} \nabla^2 \{\} \left[\right] \colon , \cup \cap \in \notin \subseteq \subset \nsubseteq \not\subset \setminus | | \times ^{ } \sum \prod _{ = } ^{ } \lim_{ } \to \searrow \nearrow \rightarrow \to \ \ \mathrm{ \,} \mathrm{ } e^{} \lg \log_{ } \langle , \rangle \sigma \sigma( )^2 \langle ^{ }\rangle _{ } \left(\begin{array}{c} \\ \end{array}\right) \begin{pmatrix} \end{pmatrix} & \\ \det \begin{vmatrix} \end{vmatrix} ^T _{ , } \dot \mathbb{Z} \mathbb{R} \mathbb{Q} \mathbb{N} \mathbb{C} \mathbb{P} e i NaN \mbox{true} \mbox{false} \emptyset \pi \gamma \infty ( ) ( ) \multicolumn{ }{c}{ } & \hfill \hfill & \\ \begin{array}{ | | } \hline \\ \hline \end{array} \overline{ } \overbrace{ } \underline{ } \underbrace{ } _{ }^{ } \underset{ }{\overset{ }{ }} \overline{ } \overbrace{ } ^{ } \stackrel{ }{ } \underline{ } \underbrace{ } _{ } \underset{ }{ } { }_{ }^{ } { }^{ } { }_{ } {}_{ } {}^{ } {} _{ } ^{ } {} _{ } ^{ } \genfrac{}{}{ ex .05ex .2ex }{}{ \frac{ \hfill \hfill }{ \hfill \hfill } \sqrt[ ]{ } exception 25: \text{exception 25:} \sqrt{ } \left \ \left( , \right \ \right) \phantom{ } \overline{ \hspace{.2em}|} \sqrt{ } \overline{) } \colorbox[rgb]{ }{$ \textcolor[rgb]{ }{ } $} \mathrm{ } \text{ } \phantom{\rule [- ] { 0ex }{ 0ex }} " " \colorbox[rgb]{ }{$ \textcolor[rgb]{ }{ \mathrm{ \mathbf{ \mathit{ \mathbit{ \mathbb{ { \mathcal{ \mathsc{ \mathfrak{ \mathsf{ \mathbsf{ \mathsfit{ \mathbsfit{ \mathtt{ { } } $} , , , , 0,1,1 0,0,0 0,0,1 1,0,1 .5,.5,.5 0,.5,0 0,1,0 .5,0,0 0,0,.5 .5,.5,0 .5,0,.5 1,0,0 .75,.75,.75 0,.5,.5 1,1,1 1,1,0 Exception at color template Exception at Hex2Decimal template sympy-0.7.4.1/sympy/utilities/mathml/data/mmlctop.xsl0000644000175000017500000033741412253362407023052 0ustar georgeskgeorgesk e - + &#x2062; &#x2148; - + &#x2062; &#x2148; Polar &#x2062; Polar &#x2062; &#x2061; [ ] -1 &#x03BB; &#x2061; &#x2218; &#x2218; id id domain codomain image &#x2061; { if otherwise &#x230A; &#x230B; &#x2147; ! max min max min | - - - + &#x2062; gcd lcm gcd lcm &#x2061; &#x2227; &#x2061; &#x2228; &#x2061; &#x22BB; &#x2061; &#x00AC; &#x2061; &#x00AC; &#x2061; &#x2200; : , &#x2203; : , &#x00AF; &#x211C; &#x2111; &#x2061; &#x230A; &#x2308; &#x230B; &#x2309; &#x2260; &#x2248; &#x2223; &#x2198; &#x2197; &#x2192; &#x21D2; &#x2208; &#x2209; &#x2284; &#x2288; &#x2286; &#x2282; &#x2265; &#x2264; &#x2261; ln ln log log log log &#x2146; &#x2146; &#x2146; &#x2146; &#x2032; &#x2145; &#x2202; &#x2202; &#x2202; &#x2202; &#x2202; &#x2202; &#x2207; 2 &#x2061; | &#x222A; &#x2229; \ &#x00D7; &#x2211; &#x220F; = &#x2211; &#x220F; &#x2211; &#x220F; &#x222B; &#x222B; &#x222B; &#x222B; &#x222B; &#x2146; lim &#x2192; &#x03C3; &#x03C3; 2 median mode det T &#x00D7; &#x22C5; &#x2297; &#x2124; &#x211D; &#x211A; &#x2115; &#x2102; &#x2119; &#x2147; &#x2148; NaN true false &#x2205; &#x03C0; &#x213D; &#x221E; sympy-0.7.4.1/sympy/utilities/mathml/__init__.py0000644000175000017500000000375612253362407022046 0ustar georgeskgeorgesk"""Module with some functions for MathML, like transforming MathML content in MathML presentation. To use this module, you will need lxml. """ from sympy.utilities.pkgdata import get_resource from sympy.utilities.decorator import doctest_depends_on import xml.dom.minidom def add_mathml_headers(s): return """""" + s + "" @doctest_depends_on(modules=('lxml',)) def apply_xsl(mml, xsl): """Apply a xsl to a MathML string @param mml: a string with MathML code @param xsl: a string representing a path to a xsl (xml stylesheet) file. This file name is relative to the PYTHONPATH >>> from sympy.utilities.mathml import apply_xsl >>> xsl = 'mathml/data/simple_mmlctop.xsl' >>> mml = ' a b ' >>> res = apply_xsl(mml,xsl) >>> ''.join(res.splitlines()) ' a + b' """ from lxml import etree s = etree.XML(get_resource(xsl).read()) transform = etree.XSLT(s) doc = etree.XML(mml) result = transform(doc) s = str(result) return s @doctest_depends_on(modules=('lxml',)) def c2p(mml, simple=False): """Transforms a document in MathML content (like the one that sympy produces) in one document in MathML presentation, more suitable for printing, and more widely accepted >>> from sympy.utilities.mathml import c2p >>> mml = ' 2 ' >>> c2p(mml,simple=True) != c2p(mml,simple=False) True """ if not mml.startswith(' 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 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 >>> 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 isinstance(z, Symbol) 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) 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 >>> 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) 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') sympy-0.7.4.1/sympy/utilities/iterables.py0000644000175000017500000015754412253362407021004 0ustar georgeskgeorgeskfrom __future__ import print_function, division from collections import defaultdict from itertools import combinations, permutations, product, product as cartes import random from operator import gt from sympy.core.decorators import deprecated from sympy.core import Basic, C # this is the logical location of these functions from sympy.core.compatibility import ( as_int, combinations_with_replacement, default_sort_key, is_sequence, iterable, ordered, xrange ) 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 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 xrange(n)))) 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) 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 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 >>> 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) 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 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 = raw_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) 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 >>> 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)) 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 """ 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 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 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 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 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' """ from sympy.core.compatibility import StringIO import sys stdout = sys.stdout sys.stdout = file = StringIO() try: func() finally: sys.stdout = stdout return file.getvalue() 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(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 def take(iter, n): """Return ``n`` items from ``iter`` iterator. """ return [ value for _, value in zip(xrange(n), iter) ] def dict_merge(*dicts): """Merge dictionaries into a single dictionary. """ merged = {} for dict in dicts: merged.update(dict) return merged 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 xrange(len(seqs))): break else: i += 1 return seqs[0][:i] 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 xrange(len(seqs))): break else: i -= 1 if i == -1: return [] else: return seqs[0][i + 1:] 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 xrange(n): yield seq[:i + 1] 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 xrange(n): yield seq[n - i - 1:] 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 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] def rotate_right(x, y): """ Right 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] 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 itertools 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 def multiset_permutations(m, size=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 """ 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): if size < 1: yield [] 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('%s %s %s' % (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 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 = 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. If one is counting partitions then it is better to use the ``nT`` function. See Also ======== partitions sympy.combinatorics.partitions.Partition sympy.combinatorics.partitions.IntegerPartition sympy.functions.combinatorial.numbers.nT """ 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] 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 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 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(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)) def has_variety(seq): """Return True if there are any different elements in ``seq``. Examples ======== >>> from sympy.utilities.iterables import has_variety >>> 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 def uniq(seq, result=None): """ Yield unique elements from ``seq`` as an iterator. The second parameter ``result`` is used internally; it is not necessary to pass anything for this. 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]] """ try: seen = set() result = result or [] for i, s in enumerate(seq): if not (s in seen or seen.add(s)): yield s except TypeError: if s not in result: yield s result.append(s) if hasattr(seq, '__getitem__'): for s in uniq(seq[i + 1:], result): yield s else: for s in uniq(seq, result): yield s def generate_bell(n): """ Generates the bell permutations of length ``n``. Examples ======== >>> from itertools import permutations >>> from sympy.utilities.iterables import generate_bell >>> from sympy import zeros, Matrix 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(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 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 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 = 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") def unrestricted_necklace(n, k): """Wrapper to necklaces to return a free (unrestricted) necklace.""" return necklaces(n, k, free=True) 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)) def bracelets(n, k): """Wrapper to necklaces to return a free (unrestricted) necklace.""" return necklaces(n, k, free=True) 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 xrange(n - 1, 0, -1): if P[p] != 0: target = P[p] - 1 for q in xrange(p - 1, 0, -1): if P[q] == target: break offset = p - q for i in xrange(p, n + 1): P[i] = P[i - offset] break else: break 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, key=default_sort_key) if is_set: i = seq.index(small) if not directed: n = len(seq) p = (i + 1) % n m = (i - 1) % n if default_sort_key(seq[p]) > default_sort_key(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) 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 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: >>> from __future__ import print_function >>> 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 = %s' % ordered) ... for p in kbins(list(range(3)), 2, ordered=ordered): ... print(' %s' % 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 == 1: 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) sympy-0.7.4.1/sympy/utilities/pkgdata.py0000644000175000017500000000347712253362407020440 0ustar georgeskgeorgesk""" 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 open(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). """ from __future__ import print_function, division import sys import os from sympy.core.compatibility import cStringIO as StringIO 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,AttributeError): pass else: return StringIO(data) return open(os.path.normpath(path), 'rb') sympy-0.7.4.1/sympy/utilities/tests/0000755000175000017500000000000012253362407017602 5ustar georgeskgeorgesksympy-0.7.4.1/sympy/utilities/tests/test_pytest.py0000644000175000017500000000264212253362407022547 0ustar georgeskgeorgeskfrom sympy.utilities.pytest import raises # Test callables def test_expected_exception_is_silent_callable(): def f(): raise ValueError() raises(ValueError, f) def test_lack_of_exception_triggers_AssertionError_callable(): try: raises(Exception, lambda: 1 + 1) assert False except AssertionError as e: assert str(e) == "DID NOT RAISE" def test_unexpected_exception_is_passed_through_callable(): def f(): raise ValueError("some error message") try: raises(TypeError, f) assert False except ValueError as e: assert str(e) == "some error message" # Test with statement def test_expected_exception_is_silent_with(): with raises(ValueError): raise ValueError() def test_lack_of_exception_triggers_AssertionError_with(): try: with raises(Exception): 1 + 1 assert False except AssertionError as e: assert str(e) == "DID NOT RAISE" def test_unexpected_exception_is_passed_through_with(): try: with raises(TypeError): raise ValueError("some error message") assert False except ValueError as e: assert str(e) == "some error message" # Now we can use raises() instead of try/catch # to test that a specific exception class is raised def test_second_argument_should_be_callable_or_string(): raises(TypeError, lambda: raises("irrelevant", 42)) sympy-0.7.4.1/sympy/utilities/tests/diagnose_imports.py0000755000175000017500000002303412253362407023527 0ustar georgeskgeorgesk#!/usr/bin/env python """ Import diagnostics. Run bin/diagnose_imports.py --help for details. """ from __future__ import print_function if __name__ == "__main__": import sys import inspect from sympy.core.compatibility import builtins import optparse from os.path import abspath, dirname, join, normpath this_file = abspath(__file__) sympy_dir = join(dirname(this_file), '..', '..', '..') sympy_dir = normpath(sympy_dir) sys.path.insert(0, sympy_dir) option_parser = optparse.OptionParser( usage= "Usage: %prog option [options]\n" "\n" "Import analysis for imports between SymPy modules.") option_group = optparse.OptionGroup( option_parser, 'Analysis options', 'Options that define what to do. Exactly one of these must be given.') option_group.add_option( '--problems', help= 'Print all import problems, that is: ' 'If an import pulls in a package instead of a module ' '(e.g. sympy.core instead of sympy.core.add); ' # see ##PACKAGE## 'if it imports a symbol that is already present; ' # see ##DUPLICATE## 'if it imports a symbol ' 'from somewhere other than the defining module.', # see ##ORIGIN## action='count') option_group.add_option( '--origins', help= 'For each imported symbol in each module, ' 'print the module that defined it. ' '(This is useful for import refactoring.)', action='count') option_parser.add_option_group(option_group) option_group = optparse.OptionGroup( option_parser, 'Sort options', 'These options define the sort order for output lines. ' 'At most one of these options is allowed. ' 'Unsorted output will reflect the order in which imports happened.') option_group.add_option( '--by-importer', help='Sort output lines by name of importing module.', action='count') option_group.add_option( '--by-origin', help='Sort output lines by name of imported module.', action='count') option_parser.add_option_group(option_group) (options, args) = option_parser.parse_args() if args: option_parser.error( 'Unexpected arguments %s (try %s --help)' % (args, sys.argv[0])) if options.problems > 1: option_parser.error('--problems must not be given more than once.') if options.origins > 1: option_parser.error('--origins must not be given more than once.') if options.by_importer > 1: option_parser.error('--by-importer must not be given more than once.') if options.by_origin > 1: option_parser.error('--by-origin must not be given more than once.') options.problems = options.problems == 1 options.origins = options.origins == 1 options.by_importer = options.by_importer == 1 options.by_origin = options.by_origin == 1 if not options.problems and not options.origins: option_parser.error( 'At least one of --problems and --origins is required') if options.problems and options.origins: option_parser.error( 'At most one of --problems and --origins is allowed') if options.by_importer and options.by_origin: option_parser.error( 'At most one of --by-importer and --by-origin is allowed') options.by_process = not options.by_importer and not options.by_origin builtin_import = builtins.__import__ class Definition(object): """Information about a symbol's definition.""" def __init__(self, name, value, definer): self.name = name self.value = value self.definer = definer def __hash__(self): return hash(self.name) def __eq__(self, other): return self.name == other.name and self.value == other.value def __ne__(self, other): return not (self == other) def __repr__(self): return 'Definition(%s, ..., %s)' % ( repr(self.name), repr(self.definer)) symbol_definers = {} # Maps each function/variable to name of module to define it def in_module(a, b): """Is a the same module as or a submodule of b?""" return a == b or a != None and b != None and a.startswith(b + '.') def relevant(module): """Is module relevant for import checking? Only imports between relevant modules will be checked.""" return in_module(module, 'sympy') and not in_module(module, 'sympy.mpmath') sorted_messages = [] def msg(msg, *args): global options, sorted_messages if options.by_process: print(msg % args) else: sorted_messages.append(msg % args) def tracking_import(module, globals=globals(), locals=[], fromlist=None, level=-1): """__import__ wrapper - does not change imports at all, but tracks them. Default order is implemented by doing output directly. All other orders are implemented by collecting output information into a sorted list that will be emitted after all imports are processed. Indirect imports can only occur after the requested symbol has been imported directly (because the indirect import would not have a module to pick the symbol up from). So this code detects indirect imports by checking whether the symbol in question was already imported. Keeps the semantics of __import__ unchanged.""" global options, symbol_definers caller_frame = inspect.getframeinfo(sys._getframe(1)) importer_filename = caller_frame.filename importer_module = globals['__name__'] if importer_filename == caller_frame.filename: importer_reference = '%s line %s' % ( importer_filename, str(caller_frame.lineno)) else: importer_reference = importer_filename result = builtin_import(module, globals, locals, fromlist, level) importee_module = result.__name__ # We're only interested if importer and importee are in SymPy if relevant(importer_module) and relevant(importee_module): for symbol in result.__dict__.iterkeys(): definition = Definition( symbol, result.__dict__[symbol], importer_module) if not symbol_definers.has_key(definition): symbol_definers[definition] = importee_module if hasattr(result, '__path__'): ##PACKAGE## # The existence of __path__ is documented in the tutorial on modules. # Python 3.3 documents this in http://docs.python.org/3.3/reference/import.html if options.by_origin: msg('Error: %s (a package) is imported by %s', module, importer_reference) else: msg('Error: %s contains package import %s', importer_reference, module) if fromlist != None: symbol_list = fromlist if '*' in symbol_list: if (importer_filename.endswith('__init__.py') or importer_filename.endswith('__init__.pyc') or importer_filename.endswith('__init__.pyo')): # We do not check starred imports inside __init__ # That's the normal "please copy over its imports to my namespace" symbol_list = [] else: symbol_list = result.__dict__.iterkeys() for symbol in symbol_list: if not symbol in result.__dict__: if options.by_origin: msg('Error: %s.%s is not defined (yet), but %s tries to import it', importee_module, symbol, importer_reference) else: msg('Error: %s tries to import %s.%s, which did not define it (yet)', importer_reference, importee_module, symbol) else: definition = Definition( symbol, result.__dict__[symbol], importer_module) symbol_definer = symbol_definers[definition] if symbol_definer == importee_module: ##DUPLICATE## if options.by_origin: msg('Error: %s.%s is imported again into %s', importee_module, symbol, importer_reference) else: msg('Error: %s imports %s.%s again', importer_reference, importee_module, symbol) else: ##ORIGIN## if options.by_origin: msg('Error: %s.%s is imported by %s, which should import %s.%s instead', importee_module, symbol, importer_reference, symbol_definer, symbol) else: msg('Error: %s imports %s.%s but should import %s.%s instead', importer_reference, importee_module, symbol, symbol_definer, symbol) return result builtins.__import__ = tracking_import __import__('sympy') sorted_messages.sort() for message in sorted_messages: print(message) sympy-0.7.4.1/sympy/utilities/tests/test_module_imports.py0000755000175000017500000000273412253362407024266 0ustar georgeskgeorgesk""" Checks that SymPy does not contain indirect imports. An indirect import is importing a symbol from a module that itself imported the symbol from elsewhere. Such a constellation makes it harder to diagnose inter-module dependencies and import order problems, and is therefore strongly discouraged. (Indirect imports from end-user code is fine and in fact a best practice.) Implementation note: Forcing Python into actually unloading already-imported submodules is a tricky and partly undocumented process. To avoid these issues, the actual diagnostic code is in bin/diagnose_imports, which is run as a separate, pristine Python process. """ from __future__ import print_function import subprocess import sys from os.path import abspath, dirname, join, normpath import inspect from sympy.utilities.pytest import XFAIL @XFAIL def test_module_imports_are_direct(): my_filename = abspath(inspect.getfile(inspect.currentframe())) my_dirname = dirname(my_filename) diagnose_imports_filename = join(my_dirname, 'diagnose_imports.py') diagnose_imports_filename = normpath(diagnose_imports_filename) process = subprocess.Popen( [ sys.executable, normpath(diagnose_imports_filename), '--problems', '--by-importer' ], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=-1) output, _ = process.communicate() assert output == '', "There are import problems:\n" + output.decode() sympy-0.7.4.1/sympy/utilities/tests/test_decorator.py0000644000175000017500000000234512253362407023201 0ustar georgeskgeorgeskfrom sympy.utilities.decorator import threaded, xthreaded from sympy import Eq, Matrix from sympy.abc import x, y from sympy.core.decorators import wraps def test_threaded(): @threaded def function(expr, *args): return 2*expr + sum(args) assert function(Matrix([[x, y], [1, x]]), 1, 2) == \ Matrix([[2*x + 3, 2*y + 3], [5, 2*x + 3]]) assert function(Eq(x, y), 1, 2) == Eq(2*x + 3, 2*y + 3) assert function([x, y], 1, 2) == [2*x + 3, 2*y + 3] assert function((x, y), 1, 2) == (2*x + 3, 2*y + 3) assert function(set([x, y]), 1, 2) == set([2*x + 3, 2*y + 3]) @threaded def function(expr, n): return expr**n assert function(x + y, 2) == x**2 + y**2 assert function(x, 2) == x**2 def test_xthreaded(): @xthreaded def function(expr, n): return expr**n assert function(x + y, 2) == (x + y)**2 def test_wraps(): def my_func(x): """My function. """ my_func.is_my_func = True new_my_func = threaded(my_func) new_my_func = wraps(my_func)(new_my_func) assert new_my_func.__name__ == 'my_func' assert new_my_func.__doc__ == 'My function. ' assert hasattr(new_my_func, 'is_my_func') assert new_my_func.is_my_func is True sympy-0.7.4.1/sympy/utilities/tests/test_autowrap.py0000644000175000017500000001005312253362407023054 0ustar georgeskgeorgesk# Tests that require installed backends go into # sympy/test_external/test_autowrap import os import tempfile import shutil from sympy.utilities.autowrap import autowrap, binary_function, CythonCodeWrapper, \ ufuncify from sympy.utilities.codegen import Routine, CCodeGen, CodeGenArgumentListError from sympy.utilities.pytest import raises from sympy.core import symbols, Eq from sympy.core.compatibility import StringIO def get_string(dump_fn, routines, prefix="file", header=False, empty=False): """Wrapper for dump_fn. dump_fn writes its results to a stream object and this wrapper returns the contents of that stream as a string. This auxiliary function is used by many tests below. The header and the empty lines are not generator to facilitate the testing of the output. """ output = StringIO() dump_fn(routines, output, prefix, header, empty) source = output.getvalue() output.close() return source def test_cython_wrapper_scalar_function(): x, y, z = symbols('x,y,z') expr = (x + y)*z routine = Routine("test", expr) code_gen = CythonCodeWrapper(CCodeGen()) source = get_string(code_gen.dump_pyx, [routine]) expected = ( 'cdef extern from "file.h":\n' ' double test(double x, double y, double z)\n' 'def test_c(double x, double y, double z):\n' ' return test(x, y, z)\n' ) assert source == expected def test_cython_wrapper_outarg(): from sympy import Equality x, y, z = symbols('x,y,z') code_gen = CythonCodeWrapper(CCodeGen()) routine = Routine("test", Equality(z, x + y)) source = get_string(code_gen.dump_pyx, [routine]) expected = ( 'cdef extern from "file.h":\n' ' void test(double x, double y, double &z)\n' 'def test_c(double x, double y):\n' ' cdef double z\n' ' test(x, y, z)\n' ' return z\n' ) assert source == expected def test_cython_wrapper_inoutarg(): from sympy import Equality x, y, z = symbols('x,y,z') code_gen = CythonCodeWrapper(CCodeGen()) routine = Routine("test", Equality(z, x + y + z)) source = get_string(code_gen.dump_pyx, [routine]) expected = ( 'cdef extern from "file.h":\n' ' void test(double x, double y, double &z)\n' 'def test_c(double x, double y, double z):\n' ' test(x, y, z)\n' ' return z\n' ) assert source == expected def test_autowrap_dummy(): x, y, z = symbols('x y z') # Uses DummyWrapper to test that codegen works as expected f = autowrap(x + y, backend='dummy') assert f() == str(x + y) assert f.args == "x, y" assert f.returns == "nameless" f = autowrap(Eq(z, x + y), backend='dummy') assert f() == str(x + y) assert f.args == "x, y" assert f.returns == "z" f = autowrap(Eq(z, x + y + z), backend='dummy') assert f() == str(x + y + z) assert f.args == "x, y, z" assert f.returns == "z" def test_autowrap_args(): x, y, z = symbols('x y z') raises(CodeGenArgumentListError, lambda: autowrap(Eq(z, x + y), backend='dummy', args=[x])) f = autowrap(Eq(z, x + y), backend='dummy', args=[y, x]) assert f() == str(x + y) assert f.args == "y, x" assert f.returns == "z" raises(CodeGenArgumentListError, lambda: autowrap(Eq(z, x + y + z), backend='dummy', args=[x, y])) f = autowrap(Eq(z, x + y + z), backend='dummy', args=[y, x, z]) assert f() == str(x + y + z) assert f.args == "y, x, z" assert f.returns == "z" def test_autowrap_store_files(): x, y = symbols('x y') tmp = tempfile.mkdtemp() try: f = autowrap(x + y, backend='dummy', tempdir=tmp) assert f() == str(x + y) assert os.access(tmp, os.F_OK) finally: shutil.rmtree(tmp) def test_binary_function(): x, y = symbols('x y') f = binary_function('f', x + y, backend='dummy') assert f._imp_() == str(x + y) def test_ufuncify(): x, y = symbols('x y') f = ufuncify((x, y), x + y, backend='dummy') assert f() == "f(_x[_i], y)" sympy-0.7.4.1/sympy/utilities/tests/test_timeutils.py0000644000175000017500000000052012253362407023227 0ustar georgeskgeorgesk"""Tests for simple tools for timing functions' execution. """ import sys from sympy.utilities.timeutils import timed def test_timed(): result = timed(lambda: 1 + 1, limit=100000) assert result[0] == 100000 and result[3] == "ns" result = timed("1 + 1", limit=100000) assert result[0] == 100000 and result[3] == "ns" sympy-0.7.4.1/sympy/utilities/tests/test_pickling.py0000644000175000017500000005106312253362407023020 0ustar georgeskgeorgeskimport copy import pickle import warnings import sys from sympy.utilities.pytest import XFAIL from sympy.core.basic import Atom, Basic from sympy.core.core import BasicMeta, BasicType, ClassRegistry from sympy.core.singleton import SingletonRegistry from sympy.core.symbol import Dummy, Symbol, Wild from sympy.core.numbers import (E, I, pi, oo, zoo, nan, Integer, Number, NumberSymbol, Rational, Float) from sympy.core.relational import (Equality, GreaterThan, LessThan, Relational, StrictGreaterThan, StrictLessThan, Unequality) from sympy.core.add import Add from sympy.core.mul import Mul from sympy.core.power import Pow from sympy.core.function import Derivative, Function, FunctionClass, Lambda, \ WildFunction from sympy.core.sets import Interval from sympy.core.multidimensional import vectorize from sympy.functions import exp #from sympy.core.ast_parser import SymPyParser, SymPyTransformer from sympy.core.compatibility import HAS_GMPY, PY3 from sympy.utilities.exceptions import SymPyDeprecationWarning from sympy import symbols, S excluded_attrs = set(['_assumptions', '_mhash']) def check(a, exclude=[], check_attr=True): """ Check that pickling and copying round-trips. """ # Python 2.6+ warns about BasicException.message, for example. warnings.filterwarnings("ignore", category=DeprecationWarning) protocols = [0, 1, 2, copy.copy, copy.deepcopy] # Python 2.x doesn't support the third pickling protocol if PY3: protocols.extend([3]) for protocol in protocols: if protocol in exclude: continue if callable(protocol): if isinstance(a, BasicType): # Classes can't be copied, but that's okay. return b = protocol(a) else: b = pickle.loads(pickle.dumps(a, protocol)) d1 = dir(a) d2 = dir(b) assert set(d1) == set(d2) if not check_attr: continue def c(a, b, d): for i in d: if not hasattr(a, i) or i in excluded_attrs: continue attr = getattr(a, i) if not hasattr(attr, "__call__"): assert hasattr(b, i), i assert getattr(b, i) == attr, "%s != %s" % (getattr(b, i), attr) c(a, b, d1) c(b, a, d2) # reset filters warnings.simplefilter("default", category=DeprecationWarning) warnings.simplefilter("error", category=SymPyDeprecationWarning) #================== core ========================= def test_core_basic(): for c in (Atom, Atom(), Basic, Basic(), # XXX: dynamically created types are not picklable # BasicMeta, BasicMeta("test", (), {}), # BasicType, BasicType("test", (), {}), ClassRegistry, ClassRegistry(), SingletonRegistry, SingletonRegistry()): check(c) def test_core_symbol(): # make the Symbol a unique name that doesn't class with any other # testing variable in this file since after this test the symbol # having the same name will be cached as noncommutative for c in (Dummy, Dummy("x", commutative=False), Symbol, Symbol("_issue_3130", commutative=False), Wild, Wild("x")): check(c) def test_core_numbers(): for c in (Integer(2), Rational(2, 3), Float("1.2")): check(c) def test_core_relational(): x = Symbol("x") y = Symbol("y") for c in (Equality, Equality(x, y), GreaterThan, GreaterThan(x, y), LessThan, LessThan(x, y), Relational, Relational(x, y), StrictGreaterThan, StrictGreaterThan(x, y), StrictLessThan, StrictLessThan(x, y), Unequality, Unequality(x, y)): check(c) def test_core_add(): x = Symbol("x") for c in (Add, Add(x, 4)): check(c) def test_core_mul(): x = Symbol("x") for c in (Mul, Mul(x, 4)): check(c) def test_core_power(): x = Symbol("x") for c in (Pow, Pow(x, 4)): check(c) def test_core_function(): x = Symbol("x") for f in (Derivative, Derivative(x), Function, FunctionClass, Lambda, WildFunction): check(f) @XFAIL def test_core_dynamicfunctions(): # This fails because f is assumed to be a class at sympy.basic.function.f f = Function("f") check(f) def test_core_interval(): for c in (Interval, Interval(0, 2)): check(c) def test_core_multidimensional(): for c in (vectorize, vectorize(0)): check(c) def test_Singletons(): protocols = [0, 1, 2] if PY3: protocols.extend([3]) copiers = [copy.copy, copy.deepcopy] copiers += [lambda x: pickle.loads(pickle.dumps(x, proto)) for proto in protocols] for obj in (Integer(-1), Integer(0), Integer(1), Rational(1, 2), pi, E, I, oo, -oo, zoo, nan, S.GoldenRatio, S.EulerGamma, S.Catalan, S.EmptySet, S.IdentityFunction): for func in copiers: assert func(obj) is obj #================== functions =================== from sympy.functions import (Piecewise, lowergamma, acosh, chebyshevu, chebyshevt, ln, chebyshevt_root, binomial, legendre, Heaviside, factorial, bernoulli, coth, tanh, assoc_legendre, sign, arg, asin, DiracDelta, re, rf, Abs, uppergamma, binomial, sinh, Ynm, cos, cot, acos, acot, gamma, bell, hermite, harmonic, LambertW, zeta, log, factorial, asinh, acoth, Znm, cosh, dirichlet_eta, Eijk, loggamma, erf, ceiling, im, fibonacci, conjugate, tan, chebyshevu_root, floor, atanh, sqrt, RisingFactorial, sin, atan, ff, FallingFactorial, lucas, atan2, polygamma, exp) def test_functions(): one_var = (acosh, ln, Heaviside, factorial, bernoulli, coth, tanh, sign, arg, asin, DiracDelta, re, Abs, sinh, cos, cot, acos, acot, gamma, bell, harmonic, LambertW, zeta, log, factorial, asinh, acoth, cosh, dirichlet_eta, loggamma, erf, ceiling, im, fibonacci, conjugate, tan, floor, atanh, sin, atan, lucas, exp) two_var = (rf, ff, lowergamma, chebyshevu, chebyshevt, binomial, atan2, polygamma, hermite, legendre, uppergamma) x, y, z = symbols("x,y,z") others = (chebyshevt_root, chebyshevu_root, Eijk(x, y, z), Piecewise( (0, x < -1), (x**2, x <= 1), (x**3, True)), assoc_legendre) for cls in one_var: check(cls) c = cls(x) check(c) for cls in two_var: check(cls) c = cls(x, y) check(c) for cls in others: check(cls) #================== geometry ==================== from sympy.geometry.entity import GeometryEntity from sympy.geometry.point import Point from sympy.geometry.ellipse import Circle, Ellipse from sympy.geometry.line import Line, LinearEntity, Ray, Segment from sympy.geometry.polygon import Polygon, RegularPolygon, Triangle def test_geometry(): p1 = Point(1, 2) p2 = Point(2, 3) p3 = Point(0, 0) p4 = Point(0, 1) for c in ( GeometryEntity, GeometryEntity(), Point, p1, Circle, Circle(p1, 2), Ellipse, Ellipse(p1, 3, 4), Line, Line(p1, p2), LinearEntity, LinearEntity(p1, p2), Ray, Ray(p1, p2), Segment, Segment(p1, p2), Polygon, Polygon(p1, p2, p3, p4), RegularPolygon, RegularPolygon(p1, 4, 5), Triangle, Triangle(p1, p2, p3)): check(c, check_attr=False) #================== integrals ==================== from sympy.integrals.integrals import Integral def test_integrals(): x = Symbol("x") for c in (Integral, Integral(x)): check(c) #==================== logic ===================== from sympy.core.logic import Logic def test_logic(): for c in (Logic, Logic(1)): check(c) #================== matrices ==================== from sympy.matrices import Matrix, SparseMatrix def test_matrices(): for c in (Matrix, Matrix([1, 2, 3]), SparseMatrix, SparseMatrix([[1, 2], [3, 4]])): check(c) #================== ntheory ===================== from sympy.ntheory.generate import Sieve def test_ntheory(): for c in (Sieve, Sieve()): check(c) #================== physics ===================== from sympy.physics.paulialgebra import Pauli from sympy.physics.units import Unit def test_physics(): for c in (Unit, Unit("meter", "m"), Pauli, Pauli(1)): check(c) #================== plotting ==================== # XXX: These tests are not complete, so XFAIL them @XFAIL def test_plotting(): from sympy.plotting.color_scheme import ColorGradient, ColorScheme from sympy.plotting.managed_window import ManagedWindow from sympy.plotting.plot import Plot, ScreenShot from sympy.plotting.plot_axes import PlotAxes, PlotAxesBase, PlotAxesFrame, PlotAxesOrdinate from sympy.plotting.plot_camera import PlotCamera from sympy.plotting.plot_controller import PlotController from sympy.plotting.plot_curve import PlotCurve from sympy.plotting.plot_interval import PlotInterval from sympy.plotting.plot_mode import PlotMode from sympy.plotting.plot_modes import Cartesian2D, Cartesian3D, Cylindrical, \ ParametricCurve2D, ParametricCurve3D, ParametricSurface, Polar, Spherical from sympy.plotting.plot_object import PlotObject from sympy.plotting.plot_surface import PlotSurface from sympy.plotting.plot_window import PlotWindow for c in ( ColorGradient, ColorGradient(0.2, 0.4), ColorScheme, ManagedWindow, ManagedWindow, Plot, ScreenShot, PlotAxes, PlotAxesBase, PlotAxesFrame, PlotAxesOrdinate, PlotCamera, PlotController, PlotCurve, PlotInterval, PlotMode, Cartesian2D, Cartesian3D, Cylindrical, ParametricCurve2D, ParametricCurve3D, ParametricSurface, Polar, Spherical, PlotObject, PlotSurface, PlotWindow): check(c) @XFAIL def test_plotting2(): from sympy.plotting.color_scheme import ColorGradient, ColorScheme from sympy.plotting.managed_window import ManagedWindow from sympy.plotting.plot import Plot, ScreenShot from sympy.plotting.plot_axes import PlotAxes, PlotAxesBase, PlotAxesFrame, PlotAxesOrdinate from sympy.plotting.plot_camera import PlotCamera from sympy.plotting.plot_controller import PlotController from sympy.plotting.plot_curve import PlotCurve from sympy.plotting.plot_interval import PlotInterval from sympy.plotting.plot_mode import PlotMode from sympy.plotting.plot_modes import Cartesian2D, Cartesian3D, Cylindrical, \ ParametricCurve2D, ParametricCurve3D, ParametricSurface, Polar, Spherical from sympy.plotting.plot_object import PlotObject from sympy.plotting.plot_surface import PlotSurface from sympy.plotting.plot_window import PlotWindow check(ColorScheme("rainbow")) check(Plot(1, visible=False)) check(PlotAxes()) #================== polys ======================= from sympy import Poly, ZZ, QQ, lex def test_pickling_polys_polytools(): from sympy.polys.polytools import Poly, PurePoly, GroebnerBasis x = Symbol('x') for c in (Poly, Poly(x, x)): check(c) for c in (PurePoly, PurePoly(x)): check(c) # TODO: fix pickling of Options class (see GroebnerBasis._options) # for c in (GroebnerBasis, GroebnerBasis([x**2 - 1], x, order=lex)): # check(c) def test_pickling_polys_polyclasses(): from sympy.polys.polyclasses import DMP, DMF, ANP for c in (DMP, DMP([[ZZ(1)], [ZZ(2)], [ZZ(3)]], ZZ)): check(c) for c in (DMF, DMF(([ZZ(1), ZZ(2)], [ZZ(1), ZZ(3)]), ZZ)): check(c) for c in (ANP, ANP([QQ(1), QQ(2)], [QQ(1), QQ(2), QQ(3)], QQ)): check(c) @XFAIL def test_pickling_polys_rings(): # NOTE: can't use protocols < 2 because we have to execute __new__ to # make sure caching of rings works properly. from sympy.polys.rings import PolyRing ring = PolyRing("x,y,z", ZZ, lex) for c in (PolyRing, ring): check(c, exclude=[0, 1]) for c in (ring.dtype, ring.one): check(c, exclude=[0, 1], check_attr=False) # TODO: Py3k def test_pickling_polys_fields(): # NOTE: can't use protocols < 2 because we have to execute __new__ to # make sure caching of fields works properly. from sympy.polys.fields import FracField field = FracField("x,y,z", ZZ, lex) # TODO: AssertionError: assert id(obj) not in self.memo # for c in (FracField, field): # check(c, exclude=[0, 1]) # TODO: AssertionError: assert id(obj) not in self.memo # for c in (field.dtype, field.one): # check(c, exclude=[0, 1]) def test_pickling_polys_elements(): from sympy.polys.domains.pythonrational import PythonRational from sympy.polys.domains.pythonfinitefield import PythonFiniteField from sympy.polys.domains.mpelements import MPContext for c in (PythonRational, PythonRational(1, 7)): check(c) gf = PythonFiniteField(17) # TODO: fix pickling of ModularInteger # for c in (gf.dtype, gf(5)): # check(c) mp = MPContext() # TODO: fix pickling of RealElement # for c in (mp.mpf, mp.mpf(1.0)): # check(c) # TODO: fix pickling of ComplexElement # for c in (mp.mpc, mp.mpc(1.0, -1.5)): # check(c) def test_pickling_polys_domains(): from sympy.polys.domains.pythonfinitefield import PythonFiniteField from sympy.polys.domains.pythonintegerring import PythonIntegerRing from sympy.polys.domains.pythonrationalfield import PythonRationalField # TODO: fix pickling of ModularInteger # for c in (PythonFiniteField, PythonFiniteField(17)): # check(c) for c in (PythonIntegerRing, PythonIntegerRing()): check(c) for c in (PythonRationalField, PythonRationalField()): check(c) if HAS_GMPY: from sympy.polys.domains.gmpyfinitefield import GMPYFiniteField from sympy.polys.domains.gmpyintegerring import GMPYIntegerRing from sympy.polys.domains.gmpyrationalfield import GMPYRationalField # TODO: fix pickling of ModularInteger # for c in (GMPYFiniteField, GMPYFiniteField(17)): # check(c) for c in (GMPYIntegerRing, GMPYIntegerRing()): check(c) for c in (GMPYRationalField, GMPYRationalField()): check(c) from sympy.polys.domains.realfield import RealField from sympy.polys.domains.complexfield import ComplexField from sympy.polys.domains.algebraicfield import AlgebraicField from sympy.polys.domains.polynomialring import PolynomialRing from sympy.polys.domains.fractionfield import FractionField from sympy.polys.domains.expressiondomain import ExpressionDomain # TODO: fix pickling of RealElement # for c in (RealField, RealField(100)): # check(c) # TODO: fix pickling of ComplexElement # for c in (ComplexField, ComplexField(100)): # check(c) for c in (AlgebraicField, AlgebraicField(QQ, sqrt(3))): check(c) # TODO: AssertionError # for c in (PolynomialRing, PolynomialRing(ZZ, "x,y,z")): # check(c) # TODO: AttributeError: 'PolyElement' object has no attribute 'ring' # for c in (FractionField, FractionField(ZZ, "x,y,z")): # check(c) for c in (ExpressionDomain, ExpressionDomain()): check(c) def test_pickling_polys_numberfields(): from sympy.polys.numberfields import AlgebraicNumber for c in (AlgebraicNumber, AlgebraicNumber(sqrt(3))): check(c) def test_pickling_polys_orderings(): from sympy.polys.orderings import (LexOrder, GradedLexOrder, ReversedGradedLexOrder, ProductOrder, InverseOrder) for c in (LexOrder, LexOrder()): check(c) for c in (GradedLexOrder, GradedLexOrder()): check(c) for c in (ReversedGradedLexOrder, ReversedGradedLexOrder()): check(c) # TODO: Argh, Python is so naive. No lambdas nor inner function support in # pickling module. Maybe someone could figure out what to do with this. # # for c in (ProductOrder, ProductOrder((LexOrder(), lambda m: m[:2]), # (GradedLexOrder(), lambda m: m[2:]))): # check(c) for c in (InverseOrder, InverseOrder(LexOrder())): check(c) def test_pickling_polys_monomials(): from sympy.polys.monomials import MonomialOps, Monomial x, y, z = symbols("x,y,z") for c in (MonomialOps, MonomialOps(3)): check(c) for c in (Monomial, Monomial((1, 2, 3), (x, y, z))): check(c) def test_pickling_polys_errors(): from sympy.polys.polyerrors import (ExactQuotientFailed, OperationNotSupported, HeuristicGCDFailed, HomomorphismFailed, IsomorphismFailed, ExtraneousFactors, EvaluationFailed, RefinementFailed, CoercionFailed, NotInvertible, NotReversible, NotAlgebraic, DomainError, PolynomialError, UnificationFailed, GeneratorsError, GeneratorsNeeded, ComputationFailed, UnivariatePolynomialError, MultivariatePolynomialError, PolificationFailed, OptionError, FlagError) x = Symbol('x') # TODO: TypeError: __init__() takes at least 3 arguments (1 given) # for c in (ExactQuotientFailed, ExactQuotientFailed(x, 3*x, ZZ)): # check(c) # TODO: TypeError: can't pickle instancemethod objects # for c in (OperationNotSupported, OperationNotSupported(Poly(x), Poly.gcd)): # check(c) for c in (HeuristicGCDFailed, HeuristicGCDFailed()): check(c) for c in (HomomorphismFailed, HomomorphismFailed()): check(c) for c in (IsomorphismFailed, IsomorphismFailed()): check(c) for c in (ExtraneousFactors, ExtraneousFactors()): check(c) for c in (EvaluationFailed, EvaluationFailed()): check(c) for c in (RefinementFailed, RefinementFailed()): check(c) for c in (CoercionFailed, CoercionFailed()): check(c) for c in (NotInvertible, NotInvertible()): check(c) for c in (NotReversible, NotReversible()): check(c) for c in (NotAlgebraic, NotAlgebraic()): check(c) for c in (DomainError, DomainError()): check(c) for c in (PolynomialError, PolynomialError()): check(c) for c in (UnificationFailed, UnificationFailed()): check(c) for c in (GeneratorsError, GeneratorsError()): check(c) for c in (GeneratorsNeeded, GeneratorsNeeded()): check(c) # TODO: PicklingError: Can't pickle at 0x38578c0>: it's not found as __main__. # for c in (ComputationFailed, ComputationFailed(lambda t: t, 3, None)): # check(c) for c in (UnivariatePolynomialError, UnivariatePolynomialError()): check(c) for c in (MultivariatePolynomialError, MultivariatePolynomialError()): check(c) # TODO: TypeError: __init__() takes at least 3 arguments (1 given) # for c in (PolificationFailed, PolificationFailed({}, x, x, False)): # check(c) for c in (OptionError, OptionError()): check(c) for c in (FlagError, FlagError()): check(c) def test_pickling_polys_options(): from sympy.polys.polyoptions import Options # TODO: fix pickling of `symbols' flag # for c in (Options, Options((), dict(domain='ZZ', polys=False))): # check(c) # TODO: def test_pickling_polys_rootisolation(): # RealInterval # ComplexInterval def test_pickling_polys_rootoftools(): from sympy.polys.rootoftools import RootOf, RootSum x = Symbol('x') f = x**3 + x + 3 for c in (RootOf, RootOf(f, 0)): check(c) for c in (RootSum, RootSum(f, exp)): check(c) #================== printing ==================== from sympy.printing.latex import LatexPrinter from sympy.printing.mathml import MathMLPrinter from sympy.printing.pretty.pretty import PrettyPrinter from sympy.printing.pretty.stringpict import prettyForm, stringPict from sympy.printing.printer import Printer from sympy.printing.python import PythonPrinter def test_printing(): for c in (LatexPrinter, LatexPrinter(), MathMLPrinter, PrettyPrinter, prettyForm, stringPict, stringPict("a"), Printer, Printer(), PythonPrinter, PythonPrinter()): check(c) @XFAIL def test_printing1(): check(MathMLPrinter()) @XFAIL def test_printing2(): check(PrettyPrinter()) #================== series ====================== from sympy.series.limits import Limit from sympy.series.order import Order def test_series(): e = Symbol("e") x = Symbol("x") for c in (Limit, Limit(e, x, 1), Order, Order(e)): check(c) #================== concrete ================== from sympy.concrete.products import Product from sympy.concrete.summations import Sum def test_concrete(): x = Symbol("x") for c in (Product, Product(x, (x, 2, 4)), Sum, Sum(x, (x, 2, 4))): check(c) sympy-0.7.4.1/sympy/utilities/tests/test_codegen.py0000644000175000017500000011176212253362407022627 0ustar georgeskgeorgeskfrom sympy.core import symbols, Eq, pi, Catalan, Lambda, Dummy from sympy.core.compatibility import StringIO from sympy import erf from sympy.utilities.codegen import (CCodeGen, Routine, InputArgument, CodeGenError, FCodeGen, codegen, CodeGenArgumentListError, OutputArgument, InOutArgument) from sympy.utilities.pytest import raises from sympy.utilities.lambdify import implemented_function # import test: #FIXME: Fails due to circular import in with core # from sympy import codegen def get_string(dump_fn, routines, prefix="file", header=False, empty=False): """Wrapper for dump_fn. dump_fn writes its results to a stream object and this wrapper returns the contents of that stream as a string. This auxiliary function is used by many tests below. The header and the empty lines are not generator to facilitate the testing of the output. """ output = StringIO() dump_fn(routines, output, prefix, header, empty) source = output.getvalue() output.close() return source def test_Routine_argument_order(): a, x, y, z = symbols('a x y z') expr = (x + y)*z raises(CodeGenArgumentListError, lambda: Routine("test", expr, argument_sequence=[z, x])) raises(CodeGenArgumentListError, lambda: Routine("test", Eq(a, expr), argument_sequence=[z, x, y])) r = Routine('test', Eq(a, expr), argument_sequence=[z, x, a, y]) assert [ arg.name for arg in r.arguments ] == [z, x, a, y] assert [ type(arg) for arg in r.arguments ] == [ InputArgument, InputArgument, OutputArgument, InputArgument ] r = Routine('test', Eq(z, expr), argument_sequence=[z, x, y]) assert [ type(arg) for arg in r.arguments ] == [ InOutArgument, InputArgument, InputArgument ] from sympy.tensor import IndexedBase, Idx A, B = map(IndexedBase, ['A', 'B']) m = symbols('m', integer=True) i = Idx('i', m) r = Routine('test', Eq(A[i], B[i]), argument_sequence=[B, A, m]) assert [ arg.name for arg in r.arguments ] == [B.label, A.label, m] def test_empty_c_code(): code_gen = CCodeGen() source = get_string(code_gen.dump_c, []) assert source == "#include \"file.h\"\n#include \n" def test_empty_c_code_with_comment(): code_gen = CCodeGen() source = get_string(code_gen.dump_c, [], header=True) assert source[:82] == ( "/******************************************************************************\n *" ) # " Code generated with sympy 0.7.2-git " assert source[158:] == ( "*\n" " * *\n" " * See http://www.sympy.org/ for more information. *\n" " * *\n" " * This file is part of 'project' *\n" " ******************************************************************************/\n" "#include \"file.h\"\n" "#include \n" ) def test_empty_c_header(): code_gen = CCodeGen() source = get_string(code_gen.dump_h, []) assert source == "#ifndef PROJECT__FILE__H\n#define PROJECT__FILE__H\n#endif\n" def test_simple_c_code(): x, y, z = symbols('x,y,z') expr = (x + y)*z routine = Routine("test", expr) code_gen = CCodeGen() source = get_string(code_gen.dump_c, [routine]) expected = ( "#include \"file.h\"\n" "#include \n" "double test(double x, double y, double z) {\n" " return z*(x + y);\n" "}\n" ) assert source == expected def test_numbersymbol_c_code(): routine = Routine("test", pi**Catalan) code_gen = CCodeGen() source = get_string(code_gen.dump_c, [routine]) expected = ( "#include \"file.h\"\n" "#include \n" "double test() {\n" " double const Catalan = 0.915965594177219;\n" " return pow(M_PI, Catalan);\n" "}\n" ) assert source == expected def test_c_code_argument_order(): x, y, z = symbols('x,y,z') expr = x + y routine = Routine("test", expr, argument_sequence=[z, x, y]) code_gen = CCodeGen() source = get_string(code_gen.dump_c, [routine]) expected = ( "#include \"file.h\"\n" "#include \n" "double test(double z, double x, double y) {\n" " return x + y;\n" "}\n" ) assert source == expected def test_simple_c_header(): x, y, z = symbols('x,y,z') expr = (x + y)*z routine = Routine("test", expr) code_gen = CCodeGen() source = get_string(code_gen.dump_h, [routine]) expected = ( "#ifndef PROJECT__FILE__H\n" "#define PROJECT__FILE__H\n" "double test(double x, double y, double z);\n" "#endif\n" ) assert source == expected def test_simple_c_codegen(): x, y, z = symbols('x,y,z') expr = (x + y)*z result = codegen(("test", expr), "C", "file", header=False, empty=False) expected = [ ("file.c", "#include \"file.h\"\n" "#include \n" "double test(double x, double y, double z) {\n" " return z*(x + y);\n" "}\n"), ("file.h", "#ifndef PROJECT__FILE__H\n" "#define PROJECT__FILE__H\n" "double test(double x, double y, double z);\n" "#endif\n") ] assert result == expected def test_multiple_results_c(): x, y, z = symbols('x,y,z') expr1 = (x + y)*z expr2 = (x - y)*z routine = Routine( "test", [expr1, expr2] ) code_gen = CCodeGen() raises(CodeGenError, lambda: get_string(code_gen.dump_h, [routine])) def test_no_results_c(): raises(ValueError, lambda: Routine("test", [])) def test_ansi_math1_codegen(): # not included: log10 from sympy import (acos, asin, atan, ceiling, cos, cosh, floor, log, ln, sin, sinh, sqrt, tan, tanh, Abs) x = symbols('x') name_expr = [ ("test_fabs", Abs(x)), ("test_acos", acos(x)), ("test_asin", asin(x)), ("test_atan", atan(x)), ("test_ceil", ceiling(x)), ("test_cos", cos(x)), ("test_cosh", cosh(x)), ("test_floor", floor(x)), ("test_log", log(x)), ("test_ln", ln(x)), ("test_sin", sin(x)), ("test_sinh", sinh(x)), ("test_sqrt", sqrt(x)), ("test_tan", tan(x)), ("test_tanh", tanh(x)), ] result = codegen(name_expr, "C", "file", header=False, empty=False) assert result[0][0] == "file.c" assert result[0][1] == ( '#include "file.h"\n#include \n' 'double test_fabs(double x) {\n return fabs(x);\n}\n' 'double test_acos(double x) {\n return acos(x);\n}\n' 'double test_asin(double x) {\n return asin(x);\n}\n' 'double test_atan(double x) {\n return atan(x);\n}\n' 'double test_ceil(double x) {\n return ceil(x);\n}\n' 'double test_cos(double x) {\n return cos(x);\n}\n' 'double test_cosh(double x) {\n return cosh(x);\n}\n' 'double test_floor(double x) {\n return floor(x);\n}\n' 'double test_log(double x) {\n return log(x);\n}\n' 'double test_ln(double x) {\n return log(x);\n}\n' 'double test_sin(double x) {\n return sin(x);\n}\n' 'double test_sinh(double x) {\n return sinh(x);\n}\n' 'double test_sqrt(double x) {\n return sqrt(x);\n}\n' 'double test_tan(double x) {\n return tan(x);\n}\n' 'double test_tanh(double x) {\n return tanh(x);\n}\n' ) assert result[1][0] == "file.h" assert result[1][1] == ( '#ifndef PROJECT__FILE__H\n#define PROJECT__FILE__H\n' 'double test_fabs(double x);\ndouble test_acos(double x);\n' 'double test_asin(double x);\ndouble test_atan(double x);\n' 'double test_ceil(double x);\ndouble test_cos(double x);\n' 'double test_cosh(double x);\ndouble test_floor(double x);\n' 'double test_log(double x);\ndouble test_ln(double x);\n' 'double test_sin(double x);\ndouble test_sinh(double x);\n' 'double test_sqrt(double x);\ndouble test_tan(double x);\n' 'double test_tanh(double x);\n#endif\n' ) def test_ansi_math2_codegen(): # not included: frexp, ldexp, modf, fmod from sympy import atan2 x, y = symbols('x,y') name_expr = [ ("test_atan2", atan2(x, y)), ("test_pow", x**y), ] result = codegen(name_expr, "C", "file", header=False, empty=False) assert result[0][0] == "file.c" assert result[0][1] == ( '#include "file.h"\n#include \n' 'double test_atan2(double x, double y) {\n return atan2(x, y);\n}\n' 'double test_pow(double x, double y) {\n return pow(x, y);\n}\n' ) assert result[1][0] == "file.h" assert result[1][1] == ( '#ifndef PROJECT__FILE__H\n#define PROJECT__FILE__H\n' 'double test_atan2(double x, double y);\n' 'double test_pow(double x, double y);\n' '#endif\n' ) def test_complicated_codegen(): from sympy import sin, cos, tan x, y, z = symbols('x,y,z') name_expr = [ ("test1", ((sin(x) + cos(y) + tan(z))**7).expand()), ("test2", cos(cos(cos(cos(cos(cos(cos(cos(x + y + z))))))))), ] result = codegen(name_expr, "C", "file", header=False, empty=False) assert result[0][0] == "file.c" assert result[0][1] == ( '#include "file.h"\n#include \n' 'double test1(double x, double y, double z) {\n' ' return ' 'pow(sin(x), 7) + ' '7*pow(sin(x), 6)*cos(y) + ' '7*pow(sin(x), 6)*tan(z) + ' '21*pow(sin(x), 5)*pow(cos(y), 2) + ' '42*pow(sin(x), 5)*cos(y)*tan(z) + ' '21*pow(sin(x), 5)*pow(tan(z), 2) + ' '35*pow(sin(x), 4)*pow(cos(y), 3) + ' '105*pow(sin(x), 4)*pow(cos(y), 2)*tan(z) + ' '105*pow(sin(x), 4)*cos(y)*pow(tan(z), 2) + ' '35*pow(sin(x), 4)*pow(tan(z), 3) + ' '35*pow(sin(x), 3)*pow(cos(y), 4) + ' '140*pow(sin(x), 3)*pow(cos(y), 3)*tan(z) + ' '210*pow(sin(x), 3)*pow(cos(y), 2)*pow(tan(z), 2) + ' '140*pow(sin(x), 3)*cos(y)*pow(tan(z), 3) + ' '35*pow(sin(x), 3)*pow(tan(z), 4) + ' '21*pow(sin(x), 2)*pow(cos(y), 5) + ' '105*pow(sin(x), 2)*pow(cos(y), 4)*tan(z) + ' '210*pow(sin(x), 2)*pow(cos(y), 3)*pow(tan(z), 2) + ' '210*pow(sin(x), 2)*pow(cos(y), 2)*pow(tan(z), 3) + ' '105*pow(sin(x), 2)*cos(y)*pow(tan(z), 4) + ' '21*pow(sin(x), 2)*pow(tan(z), 5) + ' '7*sin(x)*pow(cos(y), 6) + ' '42*sin(x)*pow(cos(y), 5)*tan(z) + ' '105*sin(x)*pow(cos(y), 4)*pow(tan(z), 2) + ' '140*sin(x)*pow(cos(y), 3)*pow(tan(z), 3) + ' '105*sin(x)*pow(cos(y), 2)*pow(tan(z), 4) + ' '42*sin(x)*cos(y)*pow(tan(z), 5) + ' '7*sin(x)*pow(tan(z), 6) + ' 'pow(cos(y), 7) + ' '7*pow(cos(y), 6)*tan(z) + ' '21*pow(cos(y), 5)*pow(tan(z), 2) + ' '35*pow(cos(y), 4)*pow(tan(z), 3) + ' '35*pow(cos(y), 3)*pow(tan(z), 4) + ' '21*pow(cos(y), 2)*pow(tan(z), 5) + ' '7*cos(y)*pow(tan(z), 6) + ' 'pow(tan(z), 7);\n' '}\n' 'double test2(double x, double y, double z) {\n' ' return cos(cos(cos(cos(cos(cos(cos(cos(x + y + z))))))));\n' '}\n' ) assert result[1][0] == "file.h" assert result[1][1] == ( '#ifndef PROJECT__FILE__H\n' '#define PROJECT__FILE__H\n' 'double test1(double x, double y, double z);\n' 'double test2(double x, double y, double z);\n' '#endif\n' ) def test_loops_c(): from sympy.tensor import IndexedBase, Idx from sympy import symbols n, m = symbols('n m', integer=True) A = IndexedBase('A') x = IndexedBase('x') y = IndexedBase('y') i = Idx('i', m) j = Idx('j', n) (f1, code), (f2, interface) = codegen( ('matrix_vector', Eq(y[i], A[i, j]*x[j])), "C", "file", header=False, empty=False) assert f1 == 'file.c' expected = ( '#include "file.h"\n' '#include \n' 'void matrix_vector(double *A, int m, int n, double *x, double *y) {\n' ' for (int i=0; i\n' 'void test_dummies(int m_%(mno)i, double *x, double *y) {\n' ' for (int i_%(ino)i=0; i_%(ino)i\n' 'void matrix_vector(double *A, int m, int n, int o, int p, double *x, double *y) {\n' ' for (int i=o; i<%(upperi)s; i++){\n' ' y[i] = 0;\n' ' }\n' ' for (int i=o; i<%(upperi)s; i++){\n' ' for (int j=0; j\n' 'double foo(double x, double &y) {\n' ' y = sin(x);\n' ' return cos(x);\n' '}\n' ) assert result[0][1] == expected def test_empty_f_code(): code_gen = FCodeGen() source = get_string(code_gen.dump_f95, []) assert source == "" def test_empty_f_code_with_header(): code_gen = FCodeGen() source = get_string(code_gen.dump_f95, [], header=True) assert source[:82] == ( "!******************************************************************************\n!*" ) # " Code generated with sympy 0.7.2-git " assert source[158:] == ( "*\n" "!* *\n" "!* See http://www.sympy.org/ for more information. *\n" "!* *\n" "!* This file is part of 'project' *\n" "!******************************************************************************\n" ) def test_empty_f_header(): code_gen = FCodeGen() source = get_string(code_gen.dump_h, []) assert source == "" def test_simple_f_code(): x, y, z = symbols('x,y,z') expr = (x + y)*z routine = Routine("test", expr) code_gen = FCodeGen() source = get_string(code_gen.dump_f95, [routine]) expected = ( "REAL*8 function test(x, y, z)\n" "implicit none\n" "REAL*8, intent(in) :: x\n" "REAL*8, intent(in) :: y\n" "REAL*8, intent(in) :: z\n" "test = z*(x + y)\n" "end function\n" ) assert source == expected def test_numbersymbol_f_code(): routine = Routine("test", pi**Catalan) code_gen = FCodeGen() source = get_string(code_gen.dump_f95, [routine]) expected = ( "REAL*8 function test()\n" "implicit none\n" "REAL*8, parameter :: Catalan = 0.915965594177219d0\n" "REAL*8, parameter :: pi = 3.14159265358979d0\n" "test = pi**Catalan\n" "end function\n" ) assert source == expected def test_erf_f_code(): x = symbols('x') routine = Routine("test", erf(x) - erf(-2 * x)) code_gen = FCodeGen() source = get_string(code_gen.dump_f95, [routine]) expected = ( "REAL*8 function test(x)\n" "implicit none\n" "REAL*8, intent(in) :: x\n" "test = erf(x) + erf(2.0d0*x)\n" "end function\n" ) assert source == expected, source def test_f_code_argument_order(): x, y, z = symbols('x,y,z') expr = x + y routine = Routine("test", expr, argument_sequence=[z, x, y]) code_gen = FCodeGen() source = get_string(code_gen.dump_f95, [routine]) expected = ( "REAL*8 function test(z, x, y)\n" "implicit none\n" "REAL*8, intent(in) :: z\n" "REAL*8, intent(in) :: x\n" "REAL*8, intent(in) :: y\n" "test = x + y\n" "end function\n" ) assert source == expected def test_simple_f_header(): x, y, z = symbols('x,y,z') expr = (x + y)*z routine = Routine("test", expr) code_gen = FCodeGen() source = get_string(code_gen.dump_h, [routine]) expected = ( "interface\n" "REAL*8 function test(x, y, z)\n" "implicit none\n" "REAL*8, intent(in) :: x\n" "REAL*8, intent(in) :: y\n" "REAL*8, intent(in) :: z\n" "end function\n" "end interface\n" ) assert source == expected def test_simple_f_codegen(): x, y, z = symbols('x,y,z') expr = (x + y)*z result = codegen( ("test", expr), "F95", "file", header=False, empty=False) expected = [ ("file.f90", "REAL*8 function test(x, y, z)\n" "implicit none\n" "REAL*8, intent(in) :: x\n" "REAL*8, intent(in) :: y\n" "REAL*8, intent(in) :: z\n" "test = z*(x + y)\n" "end function\n"), ("file.h", "interface\n" "REAL*8 function test(x, y, z)\n" "implicit none\n" "REAL*8, intent(in) :: x\n" "REAL*8, intent(in) :: y\n" "REAL*8, intent(in) :: z\n" "end function\n" "end interface\n") ] assert result == expected def test_multiple_results_f(): x, y, z = symbols('x,y,z') expr1 = (x + y)*z expr2 = (x - y)*z routine = Routine( "test", [expr1, expr2] ) code_gen = FCodeGen() raises(CodeGenError, lambda: get_string(code_gen.dump_h, [routine])) def test_no_results_f(): raises(ValueError, lambda: Routine("test", [])) def test_intrinsic_math_codegen(): # not included: log10 from sympy import (acos, asin, atan, ceiling, cos, cosh, floor, log, ln, sin, sinh, sqrt, tan, tanh, Abs) x = symbols('x') name_expr = [ ("test_abs", Abs(x)), ("test_acos", acos(x)), ("test_asin", asin(x)), ("test_atan", atan(x)), # ("test_ceil", ceiling(x)), ("test_cos", cos(x)), ("test_cosh", cosh(x)), # ("test_floor", floor(x)), ("test_log", log(x)), ("test_ln", ln(x)), ("test_sin", sin(x)), ("test_sinh", sinh(x)), ("test_sqrt", sqrt(x)), ("test_tan", tan(x)), ("test_tanh", tanh(x)), ] result = codegen(name_expr, "F95", "file", header=False, empty=False) assert result[0][0] == "file.f90" expected = ( 'REAL*8 function test_abs(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_abs = Abs(x)\n' 'end function\n' 'REAL*8 function test_acos(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_acos = acos(x)\n' 'end function\n' 'REAL*8 function test_asin(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_asin = asin(x)\n' 'end function\n' 'REAL*8 function test_atan(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_atan = atan(x)\n' 'end function\n' 'REAL*8 function test_cos(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_cos = cos(x)\n' 'end function\n' 'REAL*8 function test_cosh(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_cosh = cosh(x)\n' 'end function\n' 'REAL*8 function test_log(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_log = log(x)\n' 'end function\n' 'REAL*8 function test_ln(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_ln = log(x)\n' 'end function\n' 'REAL*8 function test_sin(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_sin = sin(x)\n' 'end function\n' 'REAL*8 function test_sinh(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_sinh = sinh(x)\n' 'end function\n' 'REAL*8 function test_sqrt(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_sqrt = sqrt(x)\n' 'end function\n' 'REAL*8 function test_tan(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_tan = tan(x)\n' 'end function\n' 'REAL*8 function test_tanh(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_tanh = tanh(x)\n' 'end function\n' ) assert result[0][1] == expected assert result[1][0] == "file.h" expected = ( 'interface\n' 'REAL*8 function test_abs(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_acos(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_asin(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_atan(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_cos(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_cosh(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_log(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_ln(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_sin(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_sinh(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_sqrt(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_tan(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_tanh(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' ) assert result[1][1] == expected def test_intrinsic_math2_codegen(): # not included: frexp, ldexp, modf, fmod from sympy import atan2 x, y = symbols('x,y') name_expr = [ ("test_atan2", atan2(x, y)), ("test_pow", x**y), ] result = codegen(name_expr, "F95", "file", header=False, empty=False) assert result[0][0] == "file.f90" expected = ( 'REAL*8 function test_atan2(x, y)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'REAL*8, intent(in) :: y\n' 'test_atan2 = atan2(x, y)\n' 'end function\n' 'REAL*8 function test_pow(x, y)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'REAL*8, intent(in) :: y\n' 'test_pow = x**y\n' 'end function\n' ) assert result[0][1] == expected assert result[1][0] == "file.h" expected = ( 'interface\n' 'REAL*8 function test_atan2(x, y)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'REAL*8, intent(in) :: y\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_pow(x, y)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'REAL*8, intent(in) :: y\n' 'end function\n' 'end interface\n' ) assert result[1][1] == expected def test_complicated_codegen_f95(): from sympy import sin, cos, tan x, y, z = symbols('x,y,z') name_expr = [ ("test1", ((sin(x) + cos(y) + tan(z))**7).expand()), ("test2", cos(cos(cos(cos(cos(cos(cos(cos(x + y + z))))))))), ] result = codegen(name_expr, "F95", "file", header=False, empty=False) assert result[0][0] == "file.f90" expected = ( 'REAL*8 function test1(x, y, z)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'REAL*8, intent(in) :: y\n' 'REAL*8, intent(in) :: z\n' 'test1 = sin(x)**7 + 7*sin(x)**6*cos(y) + 7*sin(x)**6*tan(z) + 21*sin(x) &\n' ' **5*cos(y)**2 + 42*sin(x)**5*cos(y)*tan(z) + 21*sin(x)**5*tan(z) &\n' ' **2 + 35*sin(x)**4*cos(y)**3 + 105*sin(x)**4*cos(y)**2*tan(z) + &\n' ' 105*sin(x)**4*cos(y)*tan(z)**2 + 35*sin(x)**4*tan(z)**3 + 35*sin( &\n' ' x)**3*cos(y)**4 + 140*sin(x)**3*cos(y)**3*tan(z) + 210*sin(x)**3* &\n' ' cos(y)**2*tan(z)**2 + 140*sin(x)**3*cos(y)*tan(z)**3 + 35*sin(x) &\n' ' **3*tan(z)**4 + 21*sin(x)**2*cos(y)**5 + 105*sin(x)**2*cos(y)**4* &\n' ' tan(z) + 210*sin(x)**2*cos(y)**3*tan(z)**2 + 210*sin(x)**2*cos(y) &\n' ' **2*tan(z)**3 + 105*sin(x)**2*cos(y)*tan(z)**4 + 21*sin(x)**2*tan &\n' ' (z)**5 + 7*sin(x)*cos(y)**6 + 42*sin(x)*cos(y)**5*tan(z) + 105* &\n' ' sin(x)*cos(y)**4*tan(z)**2 + 140*sin(x)*cos(y)**3*tan(z)**3 + 105 &\n' ' *sin(x)*cos(y)**2*tan(z)**4 + 42*sin(x)*cos(y)*tan(z)**5 + 7*sin( &\n' ' x)*tan(z)**6 + cos(y)**7 + 7*cos(y)**6*tan(z) + 21*cos(y)**5*tan( &\n' ' z)**2 + 35*cos(y)**4*tan(z)**3 + 35*cos(y)**3*tan(z)**4 + 21*cos( &\n' ' y)**2*tan(z)**5 + 7*cos(y)*tan(z)**6 + tan(z)**7\n' 'end function\n' 'REAL*8 function test2(x, y, z)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'REAL*8, intent(in) :: y\n' 'REAL*8, intent(in) :: z\n' 'test2 = cos(cos(cos(cos(cos(cos(cos(cos(x + y + z))))))))\n' 'end function\n' ) assert result[0][1] == expected assert result[1][0] == "file.h" expected = ( 'interface\n' 'REAL*8 function test1(x, y, z)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'REAL*8, intent(in) :: y\n' 'REAL*8, intent(in) :: z\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test2(x, y, z)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'REAL*8, intent(in) :: y\n' 'REAL*8, intent(in) :: z\n' 'end function\n' 'end interface\n' ) assert result[1][1] == expected def test_loops(): from sympy.tensor import IndexedBase, Idx from sympy import symbols n, m = symbols('n,m', integer=True) A, x, y = map(IndexedBase, 'Axy') i = Idx('i', m) j = Idx('j', n) (f1, code), (f2, interface) = codegen( ('matrix_vector', Eq(y[i], A[i, j]*x[j])), "F95", "file", header=False, empty=False) assert f1 == 'file.f90' expected = ( 'subroutine matrix_vector(A, m, n, x, y)\n' 'implicit none\n' 'INTEGER*4, intent(in) :: m\n' 'INTEGER*4, intent(in) :: n\n' 'REAL*8, intent(in), dimension(1:m, 1:n) :: A\n' 'REAL*8, intent(in), dimension(1:n) :: x\n' 'REAL*8, intent(out), dimension(1:m) :: y\n' 'INTEGER*4 :: i\n' 'INTEGER*4 :: j\n' 'do i = 1, m\n' ' y(i) = 0\n' 'end do\n' 'do i = 1, m\n' ' do j = 1, n\n' ' y(i) = y(i) + %(rhs)s\n' ' end do\n' 'end do\n' 'end subroutine\n' ) % {'rhs': 'A(i, j)*x(j)'} assert expected == code assert f2 == 'file.h' assert interface == ( 'interface\n' 'subroutine matrix_vector(A, m, n, x, y)\n' 'implicit none\n' 'INTEGER*4, intent(in) :: m\n' 'INTEGER*4, intent(in) :: n\n' 'REAL*8, intent(in), dimension(1:m, 1:n) :: A\n' 'REAL*8, intent(in), dimension(1:n) :: x\n' 'REAL*8, intent(out), dimension(1:m) :: y\n' 'end subroutine\n' 'end interface\n' ) def test_dummy_loops_f95(): from sympy.tensor import IndexedBase, Idx # the following line could also be # [Dummy(s, integer=True) for s in 'im'] # or [Dummy(integer=True) for s in 'im'] i, m = symbols('i m', integer=True, cls=Dummy) x = IndexedBase('x') y = IndexedBase('y') i = Idx(i, m) expected = ( 'subroutine test_dummies(m_%(mcount)i, x, y)\n' 'implicit none\n' 'INTEGER*4, intent(in) :: m_%(mcount)i\n' 'REAL*8, intent(in), dimension(1:m_%(mcount)i) :: x\n' 'REAL*8, intent(out), dimension(1:m_%(mcount)i) :: y\n' 'INTEGER*4 :: i_%(icount)i\n' 'do i_%(icount)i = 1, m_%(mcount)i\n' ' y(i_%(icount)i) = x(i_%(icount)i)\n' 'end do\n' 'end subroutine\n' ) % {'icount': i.label.dummy_index, 'mcount': m.dummy_index} r = Routine('test_dummies', Eq(y[i], x[i])) c = FCodeGen() code = get_string(c.dump_f95, [r]) assert code == expected def test_loops_InOut(): from sympy.tensor import IndexedBase, Idx from sympy import symbols i, j, n, m = symbols('i,j,n,m', integer=True) A, x, y = symbols('A,x,y') A = IndexedBase(A)[Idx(i, m), Idx(j, n)] x = IndexedBase(x)[Idx(j, n)] y = IndexedBase(y)[Idx(i, m)] (f1, code), (f2, interface) = codegen( ('matrix_vector', Eq(y, y + A*x)), "F95", "file", header=False, empty=False) assert f1 == 'file.f90' expected = ( 'subroutine matrix_vector(A, m, n, x, y)\n' 'implicit none\n' 'INTEGER*4, intent(in) :: m\n' 'INTEGER*4, intent(in) :: n\n' 'REAL*8, intent(in), dimension(1:m, 1:n) :: A\n' 'REAL*8, intent(in), dimension(1:n) :: x\n' 'REAL*8, intent(inout), dimension(1:m) :: y\n' 'INTEGER*4 :: i\n' 'INTEGER*4 :: j\n' 'do i = 1, m\n' ' do j = 1, n\n' ' y(i) = y(i) + %(rhs)s\n' ' end do\n' 'end do\n' 'end subroutine\n' ) assert (code == expected % {'rhs': 'A(i, j)*x(j)'} or code == expected % {'rhs': 'x(j)*A(i, j)'}) assert f2 == 'file.h' assert interface == ( 'interface\n' 'subroutine matrix_vector(A, m, n, x, y)\n' 'implicit none\n' 'INTEGER*4, intent(in) :: m\n' 'INTEGER*4, intent(in) :: n\n' 'REAL*8, intent(in), dimension(1:m, 1:n) :: A\n' 'REAL*8, intent(in), dimension(1:n) :: x\n' 'REAL*8, intent(inout), dimension(1:m) :: y\n' 'end subroutine\n' 'end interface\n' ) def test_partial_loops_f(): # check that loop boundaries are determined by Idx, and array strides # determined by shape of IndexedBase object. from sympy.tensor import IndexedBase, Idx from sympy import symbols n, m, o, p = symbols('n m o p', integer=True) A = IndexedBase('A', shape=(m, p)) x = IndexedBase('x') y = IndexedBase('y') i = Idx('i', (o, m - 5)) # Note: bounds are inclusive j = Idx('j', n) # dimension n corresponds to bounds (0, n - 1) (f1, code), (f2, interface) = codegen( ('matrix_vector', Eq(y[i], A[i, j]*x[j])), "F95", "file", header=False, empty=False) expected = ( 'subroutine matrix_vector(A, m, n, o, p, x, y)\n' 'implicit none\n' 'INTEGER*4, intent(in) :: m\n' 'INTEGER*4, intent(in) :: n\n' 'INTEGER*4, intent(in) :: o\n' 'INTEGER*4, intent(in) :: p\n' 'REAL*8, intent(in), dimension(1:m, 1:p) :: A\n' 'REAL*8, intent(in), dimension(1:n) :: x\n' 'REAL*8, intent(out), dimension(1:%(iup-ilow)s) :: y\n' 'INTEGER*4 :: i\n' 'INTEGER*4 :: j\n' 'do i = %(ilow)s, %(iup)s\n' ' y(i) = 0\n' 'end do\n' 'do i = %(ilow)s, %(iup)s\n' ' do j = 1, n\n' ' y(i) = y(i) + %(rhs)s\n' ' end do\n' 'end do\n' 'end subroutine\n' ) % { 'rhs': 'A(i, j)*x(j)', 'iup': str(m - 4), 'ilow': str(1 + o), 'iup-ilow': str(m - 4 - o) } assert expected == code def test_output_arg_f(): from sympy import sin, cos, Equality x, y, z = symbols("x,y,z") r = Routine("foo", [Equality(y, sin(x)), cos(x)]) c = FCodeGen() result = c.write([r], "test", header=False, empty=False) assert result[0][0] == "test.f90" assert result[0][1] == ( 'REAL*8 function foo(x, y)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'REAL*8, intent(out) :: y\n' 'y = sin(x)\n' 'foo = cos(x)\n' 'end function\n' ) def test_inline_function(): from sympy.tensor import IndexedBase, Idx from sympy import symbols n, m = symbols('n m', integer=True) A, x, y = map(IndexedBase, 'Axy') i = Idx('i', m) p = FCodeGen() func = implemented_function('func', Lambda(n, n*(n + 1))) routine = Routine('test_inline', Eq(y[i], func(x[i]))) code = get_string(p.dump_f95, [routine]) expected = ( 'subroutine test_inline(m, x, y)\n' 'implicit none\n' 'INTEGER*4, intent(in) :: m\n' 'REAL*8, intent(in), dimension(1:m) :: x\n' 'REAL*8, intent(out), dimension(1:m) :: y\n' 'INTEGER*4 :: i\n' 'do i = 1, m\n' ' y(i) = x(i)*(1 + x(i))\n' 'end do\n' 'end subroutine\n' ) assert code == expected def test_check_case(): x, X = symbols('x,X') raises(CodeGenError, lambda: codegen(('test', x*X), 'f95', 'prefix')) def test_check_case_false_positive(): # The upper case/lower case exception should not be triggered by SymPy # objects that differ only because of assumptions. (It may be useful to # have a check for that as well, but here we only want to test against # false positives with respect to case checking.) x1 = symbols('x') x2 = symbols('x', my_assumption=True) try: codegen(('test', x1*x2), 'f95', 'prefix') except CodeGenError as e: if e.args[0].startswith("Fortran ignores case."): raise AssertionError("This exception should not be raised!") sympy-0.7.4.1/sympy/utilities/tests/test_source.py0000644000175000017500000000044112253362407022512 0ustar georgeskgeorgeskfrom sympy.utilities.source import get_mod_func, get_class def test_get_mod_func(): assert get_mod_func( 'sympy.core.basic.Basic') == ('sympy.core.basic', 'Basic') def test_get_class(): _basic = get_class('sympy.core.basic.Basic') assert _basic.__name__ == 'Basic' sympy-0.7.4.1/sympy/utilities/tests/test_code_quality.py0000644000175000017500000002622712253362407023706 0ustar georgeskgeorgeskfrom os import walk, sep, pardir from os.path import split, join, abspath, exists, isfile from glob import glob import re import random from sympy.core.compatibility import PY3 # System path separator (usually slash or backslash) to be # used with excluded files, e.g. # exclude = set([ # "%(sep)smpmath%(sep)s" % sepd, # ]) sepd = {"sep": sep} # path and sympy_path SYMPY_PATH = abspath(join(split(__file__)[0], pardir, pardir)) # go to sympy/ assert exists(SYMPY_PATH) TOP_PATH = abspath(join(SYMPY_PATH, pardir)) BIN_PATH = join(TOP_PATH, "bin") EXAMPLES_PATH = join(TOP_PATH, "examples") # Error messages message_space = "File contains trailing whitespace: %s, line %s." message_implicit = "File contains an implicit import: %s, line %s." message_tabs = "File contains tabs instead of spaces: %s, line %s." message_carriage = "File contains carriage returns at end of line: %s, line %s" message_str_raise = "File contains string exception: %s, line %s" message_gen_raise = "File contains generic exception: %s, line %s" message_old_raise = "File contains old-style raise statement: %s, line %s, \"%s\"" message_eof = "File does not end with a newline: %s, line %s" message_multi_eof = "File ends with more than 1 newline: %s, line %s" message_test_suite_def = "Function should start with 'test_' or '_': %s, line %s" implicit_test_re = re.compile(r'^\s*(>>> )?(\.\.\. )?from .* import .*\*') str_raise_re = re.compile( r'^\s*(>>> )?(\.\.\. )?raise(\s+(\'|\")|\s*(\(\s*)+(\'|\"))') gen_raise_re = re.compile( r'^\s*(>>> )?(\.\.\. )?raise(\s+Exception|\s*(\(\s*)+Exception)') old_raise_re = re.compile(r'^\s*(>>> )?(\.\.\. )?raise((\s*\(\s*)|\s+)\w+\s*,') test_suite_def_re = re.compile(r'^def\s+(?!(_|test))[^(]*\(\s*\)\s*:$') test_file_re = re.compile(r'.*test_.*\.py$') def tab_in_leading(s): """Returns True if there are tabs in the leading whitespace of a line, including the whitespace of docstring code samples.""" n = len(s) - len(s.lstrip()) if not s[n:n + 3] in ['...', '>>>']: check = s[:n] else: smore = s[n + 3:] check = s[:n] + smore[:len(smore) - len(smore.lstrip())] return not (check.expandtabs() == check) def check_directory_tree(base_path, file_check, exclusions=set(), pattern="*.py"): """ Checks all files in the directory tree (with base_path as starting point) with the file_check function provided, skipping files that contain any of the strings in the set provided by exclusions. """ if not base_path: return for root, dirs, files in walk(base_path): check_files(glob(join(root, pattern)), file_check, exclusions) def check_files(files, file_check, exclusions=set(), pattern=None): """ Checks all files with the file_check function provided, skipping files that contain any of the strings in the set provided by exclusions. """ if not files: return for fname in files: if not exists(fname) or not isfile(fname): continue if any(ex in fname for ex in exclusions): continue if pattern is None or re.match(pattern, fname): file_check(fname) def test_files(): """ This test tests all files in sympy and checks that: o no lines contains a trailing whitespace o no lines end with \r\n o no line uses tabs instead of spaces o that the file ends with a single newline o there are no general or string exceptions o there are no old style raise statements o name of arg-less test suite functions start with _ or test_ """ def test(fname): if PY3: with open(fname, "rt", encoding="utf8") as test_file: test_this_file(fname, test_file) else: with open(fname, "rt") as test_file: test_this_file(fname, test_file) def test_this_file(fname, test_file): line = None # to flag the case where there were no lines in file for idx, line in enumerate(test_file): if test_file_re.match(fname) and test_suite_def_re.match(line): assert False, message_test_suite_def % (fname, idx + 1) if line.endswith(" \n") or line.endswith("\t\n"): assert False, message_space % (fname, idx + 1) if line.endswith("\r\n"): assert False, message_carriage % (fname, idx + 1) if tab_in_leading(line): assert False, message_tabs % (fname, idx + 1) if str_raise_re.search(line): assert False, message_str_raise % (fname, idx + 1) if gen_raise_re.search(line): assert False, message_gen_raise % (fname, idx + 1) if (implicit_test_re.search(line) and not filter(lambda ex: ex in fname, import_exclude)): assert False, message_implicit % (fname, idx + 1) result = old_raise_re.search(line) if result is not None: assert False, message_old_raise % ( fname, idx + 1, result.group(2)) if line is not None: if line == '\n' and idx > 0: assert False, message_multi_eof % (fname, idx + 1) elif not line.endswith('\n'): # eof newline check assert False, message_eof % (fname, idx + 1) # Files to test at top level top_level_files = [join(TOP_PATH, file) for file in [ "build.py", "setup.py", "setupegg.py", ]] # Files to exclude from all tests exclude = set([ "%(sep)smpmath%(sep)s" % sepd, ]) # Files to exclude from the implicit import test import_exclude = set([ # glob imports are allowed in top-level __init__.py: "%(sep)ssympy%(sep)s__init__.py" % sepd, # these __init__.py should be fixed: # XXX: not really, they use useful import pattern (DRY) "%(sep)smechanics%(sep)s__init__.py" % sepd, "%(sep)squantum%(sep)s__init__.py" % sepd, "%(sep)spolys%(sep)s__init__.py" % sepd, "%(sep)spolys%(sep)sdomains%(sep)s__init__.py" % sepd, # interactive sympy executes ``from sympy import *``: "%(sep)sinteractive%(sep)ssession.py" % sepd, # isympy executes ``from sympy import *``: "%(sep)sbin%(sep)sisympy" % sepd, # these two are import timing tests: "%(sep)sbin%(sep)ssympy_time.py" % sepd, "%(sep)sbin%(sep)ssympy_time_cache.py" % sepd, # Taken from Python stdlib: "%(sep)sparsing%(sep)ssympy_tokenize.py" % sepd, # these two should be fixed: "%(sep)splotting%(sep)spygletplot%(sep)s" % sepd, "%(sep)splotting%(sep)stextplot.py" % sepd, ]) check_files(top_level_files, test) check_directory_tree(BIN_PATH, test, set(["~", ".pyc", ".sh"]), "*") check_directory_tree(SYMPY_PATH, test, exclude) check_directory_tree(EXAMPLES_PATH, test, exclude) def _with_space(c): # return c with a random amount of leading space return random.randint(0, 10)*' ' + c def test_raise_statement_regular_expression(): candidates_ok = [ "some text # raise Exception, 'text'", "raise ValueError('text') # raise Exception, 'text'", "raise ValueError('text')", "raise ValueError", "raise ValueError('text')", "raise ValueError('text') #,", # Talking about an exception in a docstring ''''"""This function will raise ValueError, except when it doesn't"""''', "raise (ValueError('text')", ] str_candidates_fail = [ "raise 'exception'", "raise 'Exception'", 'raise "exception"', 'raise "Exception"', "raise 'ValueError'", ] gen_candidates_fail = [ "raise Exception('text') # raise Exception, 'text'", "raise Exception('text')", "raise Exception", "raise Exception('text')", "raise Exception('text') #,", "raise Exception, 'text'", "raise Exception, 'text' # raise Exception('text')", "raise Exception, 'text' # raise Exception, 'text'", ">>> raise Exception, 'text'", ">>> raise Exception, 'text' # raise Exception('text')", ">>> raise Exception, 'text' # raise Exception, 'text'", ] old_candidates_fail = [ "raise Exception, 'text'", "raise Exception, 'text' # raise Exception('text')", "raise Exception, 'text' # raise Exception, 'text'", ">>> raise Exception, 'text'", ">>> raise Exception, 'text' # raise Exception('text')", ">>> raise Exception, 'text' # raise Exception, 'text'", "raise ValueError, 'text'", "raise ValueError, 'text' # raise Exception('text')", "raise ValueError, 'text' # raise Exception, 'text'", ">>> raise ValueError, 'text'", ">>> raise ValueError, 'text' # raise Exception('text')", ">>> raise ValueError, 'text' # raise Exception, 'text'", "raise(ValueError,", "raise (ValueError,", "raise( ValueError,", "raise ( ValueError,", "raise(ValueError ,", "raise (ValueError ,", "raise( ValueError ,", "raise ( ValueError ,", ] for c in candidates_ok: assert str_raise_re.search(_with_space(c)) is None, c assert gen_raise_re.search(_with_space(c)) is None, c assert old_raise_re.search(_with_space(c)) is None, c for c in str_candidates_fail: assert str_raise_re.search(_with_space(c)) is not None, c for c in gen_candidates_fail: assert gen_raise_re.search(_with_space(c)) is not None, c for c in old_candidates_fail: assert old_raise_re.search(_with_space(c)) is not None, c def test_implicit_imports_regular_expression(): candidates_ok = [ "from sympy import something", ">>> from sympy import something", "from sympy.somewhere import something", ">>> from sympy.somewhere import something", "import sympy", ">>> import sympy", "import sympy.something.something", "... import sympy", "... import sympy.something.something", "... from sympy import something", "... from sympy.somewhere import something", ">> from sympy import *", # To allow 'fake' docstrings "# from sympy import *", "some text # from sympy import *", ] candidates_fail = [ "from sympy import *", ">>> from sympy import *", "from sympy.somewhere import *", ">>> from sympy.somewhere import *", "... from sympy import *", "... from sympy.somewhere import *", ] for c in candidates_ok: assert implicit_test_re.search(_with_space(c)) is None, c for c in candidates_fail: assert implicit_test_re.search(_with_space(c)) is not None, c def test_test_suite_defs(): candidates_ok = [ " def foo():\n", "def foo(arg):\n", "def _foo():\n", "def test_foo():\n", ] candidates_fail = [ "def foo():\n", "def foo() :\n", "def foo( ):\n", "def foo():\n", ] for c in candidates_ok: assert test_suite_def_re.search(c) is None, c for c in candidates_fail: assert test_suite_def_re.search(c) is not None, c sympy-0.7.4.1/sympy/utilities/tests/test_iterables.py0000644000175000017500000005703212253362407023174 0ustar georgeskgeorgeskfrom textwrap import dedent from sympy import ( symbols, Integral, Tuple, Dummy, Basic, default_sort_key, Matrix, factorial, true) from sympy.combinatorics import RGS_enum, RGS_unrank, Permutation from sympy.utilities.iterables import ( _partition, _set_partitions, binary_partitions, bracelets, capture, cartes, common_prefix, common_suffix, dict_merge, flatten, generate_bell, generate_derangements, generate_involutions, generate_oriented_forest, group, has_dups, kbins, minlex, multiset, multiset_combinations, multiset_partitions, multiset_permutations, necklaces, numbered_symbols, ordered, partitions, permutations, postfixes, postorder_traversal, prefixes, reshape, rotate_left, rotate_right, runs, sift, subsets, take, topological_sort, unflatten, uniq, variations) from sympy.core.singleton import S from sympy.functions.elementary.piecewise import Piecewise, ExprCondPair from sympy.utilities.pytest import raises w, x, y, z = symbols('w,x,y,z') def test_postorder_traversal(): expr = z + w*(x + y) expected = [z, w, x, y, x + y, w*(x + y), w*(x + y) + z] assert list(postorder_traversal(expr, keys=default_sort_key)) == expected assert list(postorder_traversal(expr, keys=True)) == expected expr = Piecewise((x, x < 1), (x**2, True)) expected = [ x, 1, x, x < 1, ExprCondPair(x, x < 1), 2, x, x**2, true, ExprCondPair(x**2, True), Piecewise((x, x < 1), (x**2, True)) ] assert list(postorder_traversal(expr, keys=default_sort_key)) == expected assert list(postorder_traversal( [expr], keys=default_sort_key)) == expected + [[expr]] assert list(postorder_traversal(Integral(x**2, (x, 0, 1)), keys=default_sort_key)) == [ 2, x, x**2, 0, 1, x, Tuple(x, 0, 1), Integral(x**2, Tuple(x, 0, 1)) ] assert list(postorder_traversal(('abc', ('d', 'ef')))) == [ 'abc', 'd', 'ef', ('d', 'ef'), ('abc', ('d', 'ef'))] def test_flatten(): assert flatten((1, (1,))) == [1, 1] assert flatten((x, (x,))) == [x, x] ls = [[(-2, -1), (1, 2)], [(0, 0)]] assert flatten(ls, levels=0) == ls assert flatten(ls, levels=1) == [(-2, -1), (1, 2), (0, 0)] assert flatten(ls, levels=2) == [-2, -1, 1, 2, 0, 0] assert flatten(ls, levels=3) == [-2, -1, 1, 2, 0, 0] raises(ValueError, lambda: flatten(ls, levels=-1)) class MyOp(Basic): pass assert flatten([MyOp(x, y), z]) == [MyOp(x, y), z] assert flatten([MyOp(x, y), z], cls=MyOp) == [x, y, z] assert flatten(set([1, 11, 2])) == list(set([1, 11, 2])) def test_group(): assert group([]) == [] assert group([], multiple=False) == [] assert group([1]) == [[1]] assert group([1], multiple=False) == [(1, 1)] assert group([1, 1]) == [[1, 1]] assert group([1, 1], multiple=False) == [(1, 2)] assert group([1, 1, 1]) == [[1, 1, 1]] assert group([1, 1, 1], multiple=False) == [(1, 3)] assert group([1, 2, 1]) == [[1], [2], [1]] assert group([1, 2, 1], multiple=False) == [(1, 1), (2, 1), (1, 1)] assert group([1, 1, 2, 2, 2, 1, 3, 3]) == [[1, 1], [2, 2, 2], [1], [3, 3]] assert group([1, 1, 2, 2, 2, 1, 3, 3], multiple=False) == [(1, 2), (2, 3), (1, 1), (3, 2)] def test_subsets(): # combinations assert list(subsets([1, 2, 3], 0)) == [()] assert list(subsets([1, 2, 3], 1)) == [(1,), (2,), (3,)] assert list(subsets([1, 2, 3], 2)) == [(1, 2), (1, 3), (2, 3)] assert list(subsets([1, 2, 3], 3)) == [(1, 2, 3)] l = list(range(4)) assert list(subsets(l, 0, repetition=True)) == [()] assert list(subsets(l, 1, repetition=True)) == [(0,), (1,), (2,), (3,)] assert list(subsets(l, 2, repetition=True)) == [(0, 0), (0, 1), (0, 2), (0, 3), (1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)] assert list(subsets(l, 3, repetition=True)) == [(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 0, 3), (0, 1, 1), (0, 1, 2), (0, 1, 3), (0, 2, 2), (0, 2, 3), (0, 3, 3), (1, 1, 1), (1, 1, 2), (1, 1, 3), (1, 2, 2), (1, 2, 3), (1, 3, 3), (2, 2, 2), (2, 2, 3), (2, 3, 3), (3, 3, 3)] assert len(list(subsets(l, 4, repetition=True))) == 35 assert list(subsets(l[:2], 3, repetition=False)) == [] assert list(subsets(l[:2], 3, repetition=True)) == [(0, 0, 0), (0, 0, 1), (0, 1, 1), (1, 1, 1)] assert list(subsets([1, 2], repetition=True)) == \ [(), (1,), (2,), (1, 1), (1, 2), (2, 2)] assert list(subsets([1, 2], repetition=False)) == \ [(), (1,), (2,), (1, 2)] assert list(subsets([1, 2, 3], 2)) == \ [(1, 2), (1, 3), (2, 3)] assert list(subsets([1, 2, 3], 2, repetition=True)) == \ [(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)] def test_variations(): # permutations l = list(range(4)) assert list(variations(l, 0, repetition=False)) == [()] assert list(variations(l, 1, repetition=False)) == [(0,), (1,), (2,), (3,)] assert list(variations(l, 2, repetition=False)) == [(0, 1), (0, 2), (0, 3), (1, 0), (1, 2), (1, 3), (2, 0), (2, 1), (2, 3), (3, 0), (3, 1), (3, 2)] assert list(variations(l, 3, repetition=False)) == [(0, 1, 2), (0, 1, 3), (0, 2, 1), (0, 2, 3), (0, 3, 1), (0, 3, 2), (1, 0, 2), (1, 0, 3), (1, 2, 0), (1, 2, 3), (1, 3, 0), (1, 3, 2), (2, 0, 1), (2, 0, 3), (2, 1, 0), (2, 1, 3), (2, 3, 0), (2, 3, 1), (3, 0, 1), (3, 0, 2), (3, 1, 0), (3, 1, 2), (3, 2, 0), (3, 2, 1)] assert list(variations(l, 0, repetition=True)) == [()] assert list(variations(l, 1, repetition=True)) == [(0,), (1,), (2,), (3,)] assert list(variations(l, 2, repetition=True)) == [(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2), (2, 3), (3, 0), (3, 1), (3, 2), (3, 3)] assert len(list(variations(l, 3, repetition=True))) == 64 assert len(list(variations(l, 4, repetition=True))) == 256 assert list(variations(l[:2], 3, repetition=False)) == [] assert list(variations(l[:2], 3, repetition=True)) == [ (0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1) ] def test_cartes(): assert list(cartes([1, 2], [3, 4, 5])) == \ [(1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5)] assert list(cartes()) == [()] assert list(cartes('a')) == [('a',)] assert list(cartes('a', repeat=2)) == [('a', 'a')] assert list(cartes(list(range(2)))) == [(0,), (1,)] def test_numbered_symbols(): s = numbered_symbols(cls=Dummy) assert isinstance(next(s), Dummy) def test_sift(): assert sift(list(range(5)), lambda _: _ % 2) == {1: [1, 3], 0: [0, 2, 4]} assert sift([x, y], lambda _: _.has(x)) == {False: [y], True: [x]} assert sift([S.One], lambda _: _.has(x)) == {False: [1]} def test_take(): X = numbered_symbols() assert take(X, 5) == list(symbols('x0:5')) assert take(X, 5) == list(symbols('x5:10')) assert take([1, 2, 3, 4, 5], 5) == [1, 2, 3, 4, 5] def test_dict_merge(): assert dict_merge({}, {1: x, y: z}) == {1: x, y: z} assert dict_merge({1: x, y: z}, {}) == {1: x, y: z} assert dict_merge({2: z}, {1: x, y: z}) == {1: x, 2: z, y: z} assert dict_merge({1: x, y: z}, {2: z}) == {1: x, 2: z, y: z} assert dict_merge({1: y, 2: z}, {1: x, y: z}) == {1: x, 2: z, y: z} assert dict_merge({1: x, y: z}, {1: y, 2: z}) == {1: y, 2: z, y: z} def test_prefixes(): assert list(prefixes([])) == [] assert list(prefixes([1])) == [[1]] assert list(prefixes([1, 2])) == [[1], [1, 2]] assert list(prefixes([1, 2, 3, 4, 5])) == \ [[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5]] def test_postfixes(): assert list(postfixes([])) == [] assert list(postfixes([1])) == [[1]] assert list(postfixes([1, 2])) == [[2], [1, 2]] assert list(postfixes([1, 2, 3, 4, 5])) == \ [[5], [4, 5], [3, 4, 5], [2, 3, 4, 5], [1, 2, 3, 4, 5]] def test_topological_sort(): 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)] assert topological_sort((V, E)) == [3, 5, 7, 8, 11, 2, 9, 10] assert topological_sort((V, E), key=lambda v: -v) == \ [7, 5, 11, 3, 10, 8, 9, 2] raises(ValueError, lambda: topological_sort((V, E + [(10, 7)]))) def test_rotate(): A = [0, 1, 2, 3, 4] assert rotate_left(A, 2) == [2, 3, 4, 0, 1] assert rotate_right(A, 1) == [4, 0, 1, 2, 3] A = [] B = rotate_right(A, 1) assert B == [] B.append(1) assert A == [] B = rotate_left(A, 1) assert B == [] B.append(1) assert A == [] def test_multiset_partitions(): A = [0, 1, 2, 3, 4] assert list(multiset_partitions(A, 5)) == [[[0], [1], [2], [3], [4]]] assert len(list(multiset_partitions(A, 4))) == 10 assert len(list(multiset_partitions(A, 3))) == 25 assert list(multiset_partitions([1, 1, 1, 2, 2], 2)) == [ [[1, 1, 1, 2], [2]], [[1, 1, 1], [2, 2]], [[1, 1, 2, 2], [1]], [[1, 1, 2], [1, 2]], [[1, 1], [1, 2, 2]]] assert list(multiset_partitions([1, 1, 2, 2], 2)) == [ [[1, 1, 2], [2]], [[1, 1], [2, 2]], [[1, 2, 2], [1]], [[1, 2], [1, 2]]] assert 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]]] assert list(multiset_partitions([1, 2, 2], 2)) == [ [[1, 2], [2]], [[1], [2, 2]]] assert list(multiset_partitions(3)) == [ [[0, 1, 2]], [[0, 1], [2]], [[0, 2], [1]], [[0], [1, 2]], [[0], [1], [2]]] assert list(multiset_partitions(3, 2)) == [ [[0, 1], [2]], [[0, 2], [1]], [[0], [1, 2]]] assert list(multiset_partitions([1] * 3, 2)) == [[[1], [1, 1]]] assert list(multiset_partitions([1] * 3)) == [ [[1, 1, 1]], [[1], [1, 1]], [[1], [1], [1]]] a = [3, 2, 1] assert list(multiset_partitions(a)) == \ list(multiset_partitions(sorted(a))) assert list(multiset_partitions(a, 5)) == [] assert list(multiset_partitions(a, 1)) == [[[1, 2, 3]]] assert list(multiset_partitions(a + [4], 5)) == [] assert list(multiset_partitions(a + [4], 1)) == [[[1, 2, 3, 4]]] assert list(multiset_partitions(2, 5)) == [] assert list(multiset_partitions(2, 1)) == [[[0, 1]]] assert list(multiset_partitions('a')) == [[['a']]] assert list(multiset_partitions('a', 2)) == [] assert list(multiset_partitions('ab')) == [[['a', 'b']], [['a'], ['b']]] assert list(multiset_partitions('ab', 1)) == [[['a', 'b']]] assert list(multiset_partitions('aaa', 1)) == [['aaa']] assert list(multiset_partitions([1, 1], 1)) == [[[1, 1]]] def test_multiset_combinations(): ans = ['iii', 'iim', 'iip', 'iis', 'imp', 'ims', 'ipp', 'ips', 'iss', 'mpp', 'mps', 'mss', 'pps', 'pss', 'sss'] assert [''.join(i) for i in list(multiset_combinations('mississippi', 3))] == ans M = multiset('mississippi') assert [''.join(i) for i in list(multiset_combinations(M, 3))] == ans assert [''.join(i) for i in multiset_combinations(M, 30)] == [] assert list(multiset_combinations([[1], [2, 3]], 2)) == [[[1], [2, 3]]] assert len(list(multiset_combinations('a', 3))) == 0 assert len(list(multiset_combinations('a', 0))) == 1 assert list(multiset_combinations('abc', 1)) == [['a'], ['b'], ['c']] def test_multiset_permutations(): ans = ['abby', 'abyb', 'aybb', 'baby', 'bayb', 'bbay', 'bbya', 'byab', 'byba', 'yabb', 'ybab', 'ybba'] assert [''.join(i) for i in multiset_permutations('baby')] == ans assert [''.join(i) for i in multiset_permutations(multiset('baby'))] == ans assert list(multiset_permutations([0, 0, 0], 2)) == [[0, 0]] assert list(multiset_permutations([0, 2, 1], 2)) == [ [0, 1], [0, 2], [1, 0], [1, 2], [2, 0], [2, 1]] assert len(list(multiset_permutations('a', 0))) == 1 assert len(list(multiset_permutations('a', 3))) == 0 def test(): for i in range(1, 7): print(i) for p in multiset_permutations([0, 0, 1, 0, 1], i): print(p) assert capture(lambda: test()) == dedent('''\ 1 [0] [1] 2 [0, 0] [0, 1] [1, 0] [1, 1] 3 [0, 0, 0] [0, 0, 1] [0, 1, 0] [0, 1, 1] [1, 0, 0] [1, 0, 1] [1, 1, 0] 4 [0, 0, 0, 1] [0, 0, 1, 0] [0, 0, 1, 1] [0, 1, 0, 0] [0, 1, 0, 1] [0, 1, 1, 0] [1, 0, 0, 0] [1, 0, 0, 1] [1, 0, 1, 0] [1, 1, 0, 0] 5 [0, 0, 0, 1, 1] [0, 0, 1, 0, 1] [0, 0, 1, 1, 0] [0, 1, 0, 0, 1] [0, 1, 0, 1, 0] [0, 1, 1, 0, 0] [1, 0, 0, 0, 1] [1, 0, 0, 1, 0] [1, 0, 1, 0, 0] [1, 1, 0, 0, 0] 6\n''') def test_partitions(): assert [p.copy() for p in partitions(6, k=2)] == [ {2: 3}, {1: 2, 2: 2}, {1: 4, 2: 1}, {1: 6}] assert [p.copy() for p in partitions(6, k=3)] == [ {3: 2}, {1: 1, 2: 1, 3: 1}, {1: 3, 3: 1}, {2: 3}, {1: 2, 2: 2}, {1: 4, 2: 1}, {1: 6}] assert [p.copy() for p in partitions(6, k=2, m=2)] == [] assert [p.copy() for p in partitions(8, k=4, m=3)] == [ {4: 2}, {1: 1, 3: 1, 4: 1}, {2: 2, 4: 1}, {2: 1, 3: 2}] == [ i.copy() for i in partitions(8, k=4, m=3) if all(k <= 4 for k in i) and sum(i.values()) <=3] assert [p.copy() for p in partitions(S(3), m=2)] == [ {3: 1}, {1: 1, 2: 1}] assert [i.copy() for i in partitions(4, k=3)] == [ {1: 1, 3: 1}, {2: 2}, {1: 2, 2: 1}, {1: 4}] == [ i.copy() for i in partitions(4) if all(k <= 3 for k in i)] raises(ValueError, lambda: list(partitions(3, 0))) # Consistency check on output of _partitions and RGS_unrank. # This provides a sanity test on both routines. Also verifies that # the total number of partitions is the same in each case. # (from pkrathmann2) for n in range(2, 6): i = 0 for m, q in _set_partitions(n): assert q == RGS_unrank(i, n) i = i+1 assert i == RGS_enum(n) def test_binary_partitions(): assert [i[:] for i in binary_partitions(10)] == [[8, 2], [8, 1, 1], [4, 4, 2], [4, 4, 1, 1], [4, 2, 2, 2], [4, 2, 2, 1, 1], [4, 2, 1, 1, 1, 1], [4, 1, 1, 1, 1, 1, 1], [2, 2, 2, 2, 2], [2, 2, 2, 2, 1, 1], [2, 2, 2, 1, 1, 1, 1], [2, 2, 1, 1, 1, 1, 1, 1], [2, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]] assert len([j[:] for j in binary_partitions(16)]) == 36 def test_bell_perm(): assert [len(list(generate_bell(i))) for i in range(1, 7)] == [ factorial(i) for i in range(1, 7)] assert list(generate_bell(3)) == [ (0, 1, 2), (1, 0, 2), (1, 2, 0), (2, 1, 0), (2, 0, 1), (0, 2, 1)] def test_involutions(): lengths = [1, 2, 4, 10, 26, 76] for n, N in enumerate(lengths): i = list(generate_involutions(n + 1)) assert len(i) == N assert len(set([Permutation(j)**2 for j in i])) == 1 def test_derangements(): assert len(list(generate_derangements(list(range(6))))) == 265 assert ''.join(''.join(i) for i in generate_derangements('abcde')) == ( 'badecbaecdbcaedbcdeabceadbdaecbdeacbdecabeacdbedacbedcacabedcadebcaebd' 'cdaebcdbeacdeabcdebaceabdcebadcedabcedbadabecdaebcdaecbdcaebdcbeadceab' 'dcebadeabcdeacbdebacdebcaeabcdeadbceadcbecabdecbadecdabecdbaedabcedacb' 'edbacedbca') assert 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]] assert list(generate_derangements([0, 1, 2, 2])) == [ [2, 2, 0, 1], [2, 2, 1, 0]] def test_necklaces(): def count(n, k, f): return len(list(necklaces(n, k, f))) m = [] for i in range(1, 8): m.append(( i, count(i, 2, 0), count(i, 2, 1), count(i, 3, 1))) assert Matrix(m) == Matrix([ [1, 2, 2, 3], [2, 3, 3, 6], [3, 4, 4, 10], [4, 6, 6, 21], [5, 8, 8, 39], [6, 14, 13, 92], [7, 20, 18, 198]]) def test_generate_oriented_forest(): assert list(generate_oriented_forest(5)) == [[0, 1, 2, 3, 4], [0, 1, 2, 3, 3], [0, 1, 2, 3, 2], [0, 1, 2, 3, 1], [0, 1, 2, 3, 0], [0, 1, 2, 2, 2], [0, 1, 2, 2, 1], [0, 1, 2, 2, 0], [0, 1, 2, 1, 2], [0, 1, 2, 1, 1], [0, 1, 2, 1, 0], [0, 1, 2, 0, 1], [0, 1, 2, 0, 0], [0, 1, 1, 1, 1], [0, 1, 1, 1, 0], [0, 1, 1, 0, 1], [0, 1, 1, 0, 0], [0, 1, 0, 1, 0], [0, 1, 0, 0, 0], [0, 0, 0, 0, 0]] assert len(list(generate_oriented_forest(10))) == 1842 def test_unflatten(): r = list(range(10)) assert unflatten(r) == list(zip(r[::2], r[1::2])) assert unflatten(r, 5) == [tuple(r[:5]), tuple(r[5:])] raises(ValueError, lambda: unflatten(list(range(10)), 3)) raises(ValueError, lambda: unflatten(list(range(10)), -2)) def test_common_prefix_suffix(): assert common_prefix([], [1]) == [] assert common_prefix(list(range(3))) == [0, 1, 2] assert common_prefix(list(range(3)), list(range(4))) == [0, 1, 2] assert common_prefix([1, 2, 3], [1, 2, 5]) == [1, 2] assert common_prefix([1, 2, 3], [1, 3, 5]) == [1] assert common_suffix([], [1]) == [] assert common_suffix(list(range(3))) == [0, 1, 2] assert common_suffix(list(range(3)), list(range(3))) == [0, 1, 2] assert common_suffix(list(range(3)), list(range(4))) == [] assert common_suffix([1, 2, 3], [9, 2, 3]) == [2, 3] assert common_suffix([1, 2, 3], [9, 7, 3]) == [3] def test_minlex(): assert minlex([1, 2, 0]) == (0, 1, 2) assert minlex((1, 2, 0)) == (0, 1, 2) assert minlex((1, 0, 2)) == (0, 2, 1) assert minlex((1, 0, 2), directed=False) == (0, 1, 2) assert minlex('aba') == 'aab' def test_ordered(): assert list(ordered((x, y), hash, default=False)) in [[x, y], [y, x]] assert list(ordered((x, y), hash, default=False)) == \ list(ordered((y, x), hash, default=False)) assert list(ordered((x, y))) == [x, y] seq, keys = [[[1, 2, 1], [0, 3, 1], [1, 1, 3], [2], [1]], (lambda x: len(x), lambda x: sum(x))] assert list(ordered(seq, keys, default=False, warn=False)) == \ [[1], [2], [1, 2, 1], [0, 3, 1], [1, 1, 3]] raises(ValueError, lambda: list(ordered(seq, keys, default=False, warn=True))) def test_runs(): assert runs([]) == [] assert runs([1]) == [[1]] assert runs([1, 1]) == [[1], [1]] assert runs([1, 1, 2]) == [[1], [1, 2]] assert runs([1, 2, 1]) == [[1, 2], [1]] assert runs([2, 1, 1]) == [[2], [1], [1]] from operator import lt assert runs([2, 1, 1], lt) == [[2, 1], [1]] def test_reshape(): seq = list(range(1, 9)) assert reshape(seq, [4]) == \ [[1, 2, 3, 4], [5, 6, 7, 8]] assert reshape(seq, (4,)) == \ [(1, 2, 3, 4), (5, 6, 7, 8)] assert reshape(seq, (2, 2)) == \ [(1, 2, 3, 4), (5, 6, 7, 8)] assert reshape(seq, (2, [2])) == \ [(1, 2, [3, 4]), (5, 6, [7, 8])] assert reshape(seq, ((2,), [2])) == \ [((1, 2), [3, 4]), ((5, 6), [7, 8])] assert reshape(seq, (1, [2], 1)) == \ [(1, [2, 3], 4), (5, [6, 7], 8)] assert reshape(tuple(seq), ([[1], 1, (2,)],)) == \ (([[1], 2, (3, 4)],), ([[5], 6, (7, 8)],)) assert reshape(tuple(seq), ([1], 1, (2,))) == \ (([1], 2, (3, 4)), ([5], 6, (7, 8))) assert reshape(list(range(12)), [2, [3], set([2]), (1, (3,), 1)]) == \ [[0, 1, [2, 3, 4], set([5, 6]), (7, (8, 9, 10), 11)]] def test_uniq(): assert list(uniq(p.copy() for p in partitions(4))) == \ [{4: 1}, {1: 1, 3: 1}, {2: 2}, {1: 2, 2: 1}, {1: 4}] assert list(uniq(x % 2 for x in range(5))) == [0, 1] assert list(uniq('a')) == ['a'] assert list(uniq('ababc')) == list('abc') assert list(uniq([[1], [2, 1], [1]])) == [[1], [2, 1]] assert list(uniq(permutations(i for i in [[1], 2, 2]))) == \ [([1], 2, 2), (2, [1], 2), (2, 2, [1])] assert list(uniq([2, 3, 2, 4, [2], [1], [2], [3], [1]])) == \ [2, 3, 4, [2], [1], [3]] def test_kbins(): assert len(list(kbins('1123', 2, ordered=1))) == 24 assert len(list(kbins('1123', 2, ordered=11))) == 36 assert len(list(kbins('1123', 2, ordered=10))) == 10 assert len(list(kbins('1123', 2, ordered=0))) == 5 assert len(list(kbins('1123', 2, ordered=None))) == 3 def test(): for ordered in [None, 0, 1, 10, 11]: print('ordered =', ordered) for p in kbins([0, 0, 1], 2, ordered=ordered): print(' ', p) assert capture(lambda : test()) == dedent('''\ ordered = None [[0], [0, 1]] [[0, 0], [1]] ordered = 0 [[0, 0], [1]] [[0, 1], [0]] ordered = 1 [[0], [0, 1]] [[0], [1, 0]] [[1], [0, 0]] ordered = 10 [[0, 0], [1]] [[1], [0, 0]] [[0, 1], [0]] [[0], [0, 1]] ordered = 11 [[0], [0, 1]] [[0, 0], [1]] [[0], [1, 0]] [[0, 1], [0]] [[1], [0, 0]] [[1, 0], [0]]\n''') def test(): for ordered in [None, 0, 1, 10, 11]: print('ordered =', ordered) for p in kbins(list(range(3)), 2, ordered=ordered): print(' ', p) assert capture(lambda : test()) == dedent('''\ 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]]\n''') def test_has_dups(): assert has_dups(set()) is False assert has_dups(list(range(3))) is False assert has_dups([1, 2, 1]) is True def test__partition(): assert _partition('abcde', [1, 0, 1, 2, 0]) == [ ['b', 'e'], ['a', 'c'], ['d']] assert _partition('abcde', [1, 0, 1, 2, 0], 3) == [ ['b', 'e'], ['a', 'c'], ['d']] output = (3, [1, 0, 1, 2, 0]) assert _partition('abcde', *output) == [['b', 'e'], ['a', 'c'], ['d']] sympy-0.7.4.1/sympy/utilities/tests/test_lambdify.py0000644000175000017500000002750612253362407023014 0ustar georgeskgeorgeskfrom sympy.utilities.pytest import XFAIL, raises from sympy import ( symbols, lambdify, sqrt, sin, cos, pi, atan, Rational, Float, Matrix, Lambda, exp, Integral, oo, I, Abs, Function, true, false) from sympy.printing.lambdarepr import LambdaPrinter from sympy import mpmath from sympy.utilities.lambdify import implemented_function from sympy.utilities.pytest import skip from sympy.utilities.decorator import conserve_mpmath_dps from sympy.external import import_module import math import sympy MutableDenseMatrix = Matrix numpy = import_module('numpy') x, y, z = symbols('x,y,z') #================== Test different arguments ======================= def test_no_args(): f = lambdify([], 1) raises(TypeError, lambda: f(-1)) assert f() == 1 def test_single_arg(): f = lambdify(x, 2*x) assert f(1) == 2 def test_list_args(): f = lambdify([x, y], x + y) assert f(1, 2) == 3 def test_str_args(): f = lambdify('x,y,z', 'z,y,x') assert f(3, 2, 1) == (1, 2, 3) assert f(1.0, 2.0, 3.0) == (3.0, 2.0, 1.0) # make sure correct number of args required raises(TypeError, lambda: f(0)) def test_own_namespace(): myfunc = lambda x: 1 f = lambdify(x, sin(x), {"sin": myfunc}) assert f(0.1) == 1 assert f(100) == 1 def test_own_module(): f = lambdify(x, sin(x), math) assert f(0) == 0.0 f = lambdify(x, sympy.ceiling(x), math) raises(NameError, lambda: f(4.5)) def test_bad_args(): # no vargs given raises(TypeError, lambda: lambdify(1)) # same with vector exprs raises(TypeError, lambda: lambdify([1, 2])) def test_atoms(): # Non-Symbol atoms should not be pulled out from the expression namespace f = lambdify(x, pi + x, {"pi": 3.14}) assert f(0) == 3.14 f = lambdify(x, I + x, {"I": 1j}) assert f(1) == 1 + 1j #================== Test different modules ========================= # high precision output of sin(0.2*pi) is used to detect if precision is lost unwanted @conserve_mpmath_dps def test_sympy_lambda(): mpmath.mp.dps = 50 sin02 = mpmath.mpf("0.19866933079506121545941262711838975037020672954020") f = lambdify(x, sin(x), "sympy") assert f(x) == sin(x) prec = 1e-15 assert -prec < f(Rational(1, 5)).evalf() - Float(str(sin02)) < prec # arctan is in numpy module and should not be available raises(NameError, lambda: lambdify(x, arctan(x), "sympy")) @conserve_mpmath_dps def test_math_lambda(): mpmath.mp.dps = 50 sin02 = mpmath.mpf("0.19866933079506121545941262711838975037020672954020") f = lambdify(x, sin(x), "math") prec = 1e-15 assert -prec < f(0.2) - sin02 < prec raises(ValueError, lambda: f(x)) # if this succeeds, it can't be a python math function @conserve_mpmath_dps def test_mpmath_lambda(): mpmath.mp.dps = 50 sin02 = mpmath.mpf("0.19866933079506121545941262711838975037020672954020") f = lambdify(x, sin(x), "mpmath") prec = 1e-49 # mpmath precision is around 50 decimal places assert -prec < f(mpmath.mpf("0.2")) - sin02 < prec raises(TypeError, lambda: f(x)) # if this succeeds, it can't be a mpmath function @conserve_mpmath_dps @XFAIL def test_number_precision(): mpmath.mp.dps = 50 sin02 = mpmath.mpf("0.19866933079506121545941262711838975037020672954020") f = lambdify(x, sin02, "mpmath") prec = 1e-49 # mpmath precision is around 50 decimal places assert -prec < f(0) - sin02 < prec #================== Test Translations ============================== # We can only check if all translated functions are valid. It has to be checked # by hand if they are complete. def test_math_transl(): from sympy.utilities.lambdify import MATH_TRANSLATIONS for sym, mat in MATH_TRANSLATIONS.items(): assert sym in sympy.__dict__ assert mat in math.__dict__ def test_mpmath_transl(): from sympy.utilities.lambdify import MPMATH_TRANSLATIONS for sym, mat in MPMATH_TRANSLATIONS.items(): assert sym in sympy.__dict__ or sym == 'Matrix' assert mat in mpmath.__dict__ def test_numpy_transl(): if not numpy: skip("numpy not installed.") from sympy.utilities.lambdify import NUMPY_TRANSLATIONS for sym, nump in NUMPY_TRANSLATIONS.items(): assert sym in sympy.__dict__ assert nump in numpy.__dict__ def test_numpy_translation_abs(): if not numpy: skip("numpy not installed.") f = lambdify(x, Abs(x), "numpy") assert f(-1) == 1 assert f(1) == 1 #================== Test some functions ============================ def test_exponentiation(): f = lambdify(x, x**2) assert f(-1) == 1 assert f(0) == 0 assert f(1) == 1 assert f(-2) == 4 assert f(2) == 4 assert f(2.5) == 6.25 def test_sqrt(): f = lambdify(x, sqrt(x)) assert f(0) == 0.0 assert f(1) == 1.0 assert f(4) == 2.0 assert abs(f(2) - 1.414) < 0.001 assert f(6.25) == 2.5 def test_trig(): f = lambdify([x], [cos(x), sin(x)]) d = f(pi) prec = 1e-11 assert -prec < d[0] + 1 < prec assert -prec < d[1] < prec d = f(3.14159) prec = 1e-5 assert -prec < d[0] + 1 < prec assert -prec < d[1] < prec #================== Test vectors =================================== def test_vector_simple(): f = lambdify((x, y, z), (z, y, x)) assert f(3, 2, 1) == (1, 2, 3) assert f(1.0, 2.0, 3.0) == (3.0, 2.0, 1.0) # make sure correct number of args required raises(TypeError, lambda: f(0)) def test_vector_discontinuous(): f = lambdify(x, (-1/x, 1/x)) raises(ZeroDivisionError, lambda: f(0)) assert f(1) == (-1.0, 1.0) assert f(2) == (-0.5, 0.5) assert f(-2) == (0.5, -0.5) def test_trig_symbolic(): f = lambdify([x], [cos(x), sin(x)]) d = f(pi) assert abs(d[0] + 1) < 0.0001 assert abs(d[1] - 0) < 0.0001 def test_trig_float(): f = lambdify([x], [cos(x), sin(x)]) d = f(3.14159) assert abs(d[0] + 1) < 0.0001 assert abs(d[1] - 0) < 0.0001 def test_docs(): f = lambdify(x, x**2) assert f(2) == 4 f = lambdify([x, y, z], [z, y, x]) assert f(1, 2, 3) == [3, 2, 1] f = lambdify(x, sqrt(x)) assert f(4) == 2.0 f = lambdify((x, y), sin(x*y)**2) assert f(0, 5) == 0 def test_math(): f = lambdify((x, y), sin(x), modules="math") assert f(0, 5) == 0 def test_sin(): f = lambdify(x, sin(x)**2) assert isinstance(f(2), float) f = lambdify(x, sin(x)**2, modules="math") assert isinstance(f(2), float) def test_matrix(): A = Matrix([[x, x*y], [sin(z) + 4, x**z]]) sol = Matrix([[1, 2], [sin(3) + 4, 1]]) f = lambdify((x, y, z), A, modules="sympy") assert f(1, 2, 3) == sol f = lambdify((x, y, z), (A, [A]), modules="sympy") assert f(1, 2, 3) == (sol, [sol]) J = Matrix((x, x + y)).jacobian((x, y)) v = Matrix((x, y)) sol = Matrix([[1, 0], [1, 1]]) assert lambdify(v, J, modules='sympy')(1, 2) == sol assert lambdify(v.T, J, modules='sympy')(1, 2) == sol def test_integral(): f = Lambda(x, exp(-x**2)) l = lambdify(x, Integral(f(x), (x, -oo, oo)), modules="sympy") assert l(x) == Integral(exp(-x**2), (x, -oo, oo)) #================== Test symbolic ================================== def test_sym_single_arg(): f = lambdify(x, x * y) assert f(z) == z * y def test_sym_list_args(): f = lambdify([x, y], x + y + z) assert f(1, 2) == 3 + z def test_sym_integral(): f = Lambda(x, exp(-x**2)) l = lambdify(x, Integral(f(x), (x, -oo, oo)), modules="sympy") assert l(y).doit() == sqrt(pi) def test_namespace_order(): # lambdify had a bug, such that module dictionaries or cached module # dictionaries would pull earlier namespaces into themselves. # Because the module dictionaries form the namespace of the # generated lambda, this meant that the behavior of a previously # generated lambda function could change as a result of later calls # to lambdify. n1 = {'f': lambda x: 'first f'} n2 = {'f': lambda x: 'second f', 'g': lambda x: 'function g'} f = sympy.Function('f') g = sympy.Function('g') if1 = lambdify(x, f(x), modules=(n1, "sympy")) assert if1(1) == 'first f' if2 = lambdify(x, g(x), modules=(n2, "sympy")) # previously gave 'second f' assert if1(1) == 'first f' def test_imps(): # Here we check if the default returned functions are anonymous - in # the sense that we can have more than one function with the same name f = implemented_function('f', lambda x: 2*x) g = implemented_function('f', lambda x: math.sqrt(x)) l1 = lambdify(x, f(x)) l2 = lambdify(x, g(x)) assert str(f(x)) == str(g(x)) assert l1(3) == 6 assert l2(3) == math.sqrt(3) # check that we can pass in a Function as input func = sympy.Function('myfunc') assert not hasattr(func, '_imp_') my_f = implemented_function(func, lambda x: 2*x) assert hasattr(func, '_imp_') # Error for functions with same name and different implementation f2 = implemented_function("f", lambda x: x + 101) raises(ValueError, lambda: lambdify(x, f(f2(x)))) def test_imps_wrong_args(): raises(ValueError, lambda: implemented_function(sin, lambda x: x)) def test_lambdify_imps(): # Test lambdify with implemented functions # first test basic (sympy) lambdify f = sympy.cos assert lambdify(x, f(x))(0) == 1 assert lambdify(x, 1 + f(x))(0) == 2 assert lambdify((x, y), y + f(x))(0, 1) == 2 # make an implemented function and test f = implemented_function("f", lambda x: x + 100) assert lambdify(x, f(x))(0) == 100 assert lambdify(x, 1 + f(x))(0) == 101 assert lambdify((x, y), y + f(x))(0, 1) == 101 # Can also handle tuples, lists, dicts as expressions lam = lambdify(x, (f(x), x)) assert lam(3) == (103, 3) lam = lambdify(x, [f(x), x]) assert lam(3) == [103, 3] lam = lambdify(x, [f(x), (f(x), x)]) assert lam(3) == [103, (103, 3)] lam = lambdify(x, {f(x): x}) assert lam(3) == {103: 3} lam = lambdify(x, {f(x): x}) assert lam(3) == {103: 3} lam = lambdify(x, {x: f(x)}) assert lam(3) == {3: 103} # Check that imp preferred to other namespaces by default d = {'f': lambda x: x + 99} lam = lambdify(x, f(x), d) assert lam(3) == 103 # Unless flag passed lam = lambdify(x, f(x), d, use_imps=False) assert lam(3) == 102 def test_dummification(): t = symbols('t') F = Function('F') G = Function('G') some_expr = 2 * F(t)**2 / G(t) lam = lambdify((F(t), G(t)), some_expr) assert lam(3, 9) == 2 lam = lambdify(sin(t), 2 * sin(t)**2) assert lam(F(t)) == 2 * F(t)**2 raises(SyntaxError, lambda: lambdify(F(t) * G(t), F(t) * G(t) + 5)) raises(SyntaxError, lambda: lambdify(2 * F(t), 2 * F(t) + 5)) raises(SyntaxError, lambda: lambdify(2 * F(t), 4 * F(t) + 5)) #================== Test special printers ========================== def test_special_printers(): 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 intervalrepr(expr): return IntervalPrinter().doprint(expr) expr = sympy.sqrt(sympy.sqrt(2) + sympy.sqrt(3)) + sympy.S(1)/2 func0 = lambdify((), expr, modules="mpmath", printer=intervalrepr) func1 = lambdify((), expr, modules="mpmath", printer=IntervalPrinter) func2 = lambdify((), expr, modules="mpmath", printer=IntervalPrinter()) mpi = type(mpmath.mpi(1, 2)) assert isinstance(func0(), mpi) assert isinstance(func1(), mpi) assert isinstance(func2(), mpi) def test_true_false(): # We want exact is comparison here, not just == assert lambdify([], true)() is True assert lambdify([], false)() is False sympy-0.7.4.1/sympy/utilities/tests/__init__.py0000644000175000017500000000000012253362407021701 0ustar georgeskgeorgesksympy-0.7.4.1/sympy/utilities/__init__.py0000644000175000017500000000105212253362407020547 0ustar georgeskgeorgesk"""This module contains some general purpose utilities that are used across SymPy. """ from .iterables import (flatten, group, take, subsets, variations, numbered_symbols, cartes, capture, dict_merge, postorder_traversal, interactive_traversal, prefixes, postfixes, sift, topological_sort, unflatten, has_dups, has_variety, reshape, default_sort_key, ordered) from .lambdify import lambdify from .source import source from .decorator import threaded, xthreaded, public from .runtests import test, doctest from .timeutils import timed sympy-0.7.4.1/sympy/__init__.py0000644000175000017500000000361712253362407016545 0ustar georgeskgeorgesk""" 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/""" from __future__ import absolute_import, print_function __version__ = "0.7.4.1" import sys if sys.version_info[0] == 2 and sys.version_info[1] < 6: raise ImportError("Python Version 2.6 or above is required for SymPy.") else: # Python 3 pass # Here we can also check for specific Python 3 versions, if needed del sys def __sympy_debug(): # helper function so we don't import os globally import os return eval(os.getenv('SYMPY_DEBUG', 'False')) SYMPY_DEBUG = __sympy_debug() from .core import * from .logic import * from .assumptions import * from .polys import * from .series import * from .functions import * from .ntheory import * from .concrete import * from .simplify import * from .sets import * from .solvers import * from .matrices import * from .geometry import * from .utilities import * from .integrals import * from .tensor import * from .parsing import * # 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 sympy-0.7.4.1/README.rst0000644000175000017500000001513212253362407014735 0ustar georgeskgeorgeskSymPy ===== A Python library for symbolic mathematics. http://sympy.org/ See the AUTHORS file for the list of authors. And many more people helped on the SymPy mailing list, reported bugs, helped organize SymPy's participation in the Google Summer of Code, the Google Highly Open Participation Contest, Google Code-In, wrote and blogged about SymPy... License: New BSD License (see the LICENSE file for details) covers all files in the sympy repository unless stated otherwise. Our mailing list is at https://groups.google.com/forum/?fromgroups#!forum/sympy. We are also on IRC (`#sympy on Freenode `_, note, this channel is `logged `_). Feel free to ask us anything there. We have a very welcoming and helpful community. Download -------- Get the latest version of SymPy from https://pypi.python.org/pypi/sympy/ To get the git version do :: $ git clone git://github.com/sympy/sympy.git For other options (tarballs, debs, etc.), see See http://docs.sympy.org/dev/install.html. Documentation and usage ----------------------- Everything is at: http://docs.sympy.org/ You can generate everything at the above site in your local copy of SymPy by:: $ cd doc $ make html Then the docs will be in `_build/html`. If you don't want to read that, here is a short usage: From this directory, start python and:: >>> from sympy import Symbol, cos >>> x = Symbol('x') >>> e = 1/cos(x) >>> print e.series(x, 0, 10) 1 + (1/2)*x**2 + (5/24)*x**4 + (61/720)*x**6 + (277/8064)*x**8 + O(x**10) SymPy also comes with a console that is a simple wrapper around the classic python console (or IPython when available) that loads the sympy namespace and executes some common commands for you. To start it, issue:: $ bin/isympy from this directory if SymPy is not installed or simply:: $ isympy if SymPy is installed. Installation ------------ To install SymPy, simply run:: $ python setup.py install If you install it system-wide, you may need to prefix the previous command with ``sudo``:: $ sudo python setup.py install See http://docs.sympy.org/dev/install.html for more information. Tests ----- To execute all tests, run:: $./setup.py test in the current directory. For more fine-grained running of tests or doctest, use ``bin/test`` or respectively ``bin/doctest``. The master branch is automatically tested by Travis CI, the results can be seen here: .. image:: https://secure.travis-ci.org/sympy/sympy.png?branch=master :target: http://travis-ci.org/sympy/sympy To test pull requests, use `sympy-bot `_. Usage in Python 3 ----------------- SymPy also supports Python 3. If you want to install the latest version in Python 3, get the Python 3 tarball from https://pypi.python.org/pypi/sympy/ To install the SymPy for Python 3, simply run the above commands with a Python 3 interpreter. Clean ----- To clean everything (thus getting the same tree as in the repository):: $ ./setup.py clean You can also clean things with git using:: $ git clean -Xdf which will clear everything ignored by ``.gitignore``, and:: $ git clean -df to clear all untracked files. You can revert the most recent changes in git with:: $ git reset --hard WARNING: The above commands will all clear changes you may have made, and you will lose them forever. Be sure to check things with ``git status``, ``git diff``, ``git clean -Xn`` and ``git clean -n`` before doing any of those. Bugs ---- Our issue tracker is at https://code.google.com/p/sympy/issues/list. Please report any bugs that you find. Or, even better, fork the repository on GitHub and create a pull request. We welcome all changes, big or small, and we will help you make the pull request if you are new to git (just ask on our mailing list or IRC). Brief History ------------- SymPy was started by Ondřej Čertík in 2005, he wrote some code during the summer, then he wrote some more code during the summer 2006. In February 2007, Fabian Pedregosa joined the project and helped fixed many things, contributed documentation and made it alive again. 5 students (Mateusz Paprocki, Brian Jorgensen, Jason Gedge, Robert Schwarz and Chris Wu) improved SymPy incredibly during the summer 2007 as part of the Google Summer of Code. Pearu Peterson joined the development during the summer 2007 and he has made SymPy much more competitive by rewriting the core from scratch, that has made it from 10x to 100x faster. Jurjen N.E. Bos has contributed pretty printing and other patches. Fredrik Johansson has wrote mpmath and contributed a lot of patches. SymPy has participated in every Google Summer of Code since 2007. You can see https://github.com/sympy/sympy/wiki#google-summer-of-code for full details. Each year has improved SymPy by bounds. Most of SymPy's development has come from Google Summer of Code students. In 2011, Ondřej Čertík stepped down as lead developer, with Aaron Meurer, who also started as a Google Summer of Code student, taking his place. Ondřej Čertík is still active in the community, but is too busy with work and family to play a lead development role Since then, a lot more people have joined the development and some people have also left. You can see the full list in doc/src/aboutus.rst, or online at: http://docs.sympy.org/dev/aboutus.html#sympy-development-team The git history goes back to 2007, when development moved from svn to hg. To see the history before that point, look at http://github.com/sympy/sympy-old. You can use git to see the biggest developers. The command:: $ git shortlog -ns will show each developer, sorted by commits to the project. The command:: $ git shortlog -ns --since="1 year" will show the top developers from the last year. Citation -------- To cite SymPy in publications use:: SymPy Development Team (2013). SymPy: Python library for symbolic mathematics URL http://www.sympy.org. A BibTeX entry for LaTeX users is:: @Manual{, title = {SymPy: Python library for symbolic mathematics}, author = {{SymPy Development Team}}, year = {2013}, url = {http://www.sympy.org}, } SymPy is BSD licensed, so you are free to use it whatever you like, be it academic, commercial, creating forks or derivatives, as long as you copy the BSD statement if you redistribute it (see the LICENSE file for details). That said, although not required by the SymPy license, if it is convenient for you, please cite SymPy when using it in your work and also consider contributing all your changes back, so that we can incorporate it and all of us will benefit in the end. sympy-0.7.4.1/.gitignore0000644000175000017500000000260712253362407015241 0ustar georgeskgeorgesk# This file tells git what files to ignore (e.g., you won't see them as # untracked with "git status"). Add anything to it that can be cleared # without any worry (e.g., by "git clean -Xdf"), because it can be # regenerated. Lines beginning with # are comments. You can also ignore # files on a per-repository basis by modifying the core.excludesfile # configuration option (see "git help config"). If you need to make git # track a file that is ignored for some reason, you have to use # "git add -f". See "git help gitignore" for more information. # Regular Python bytecode file *.pyc # Optimized Python bytecode file *.pyo # Vim's swap files *.sw[op] # Generated files from Jython *$py.class # Generated C files in the polys directory /sympy/polys/*.c # Generated dynamic libraries in the polys directory /sympy/polys/*.so # File generated by setup.py using MANIFEST.in MANIFEST # Generated by ctags (used to improve autocompletion in vim) tags my/ # Files generated by setup.py dist/ build/ # Files generated by setupegg.py sympy.egg-info/ # Tox files tox.ini .tox/ # Coverage files (./bin/coverage_report.py) .coverage covhtml/ # Built doc files (cd doc; make html) doc/_build/ doc/sphinx/ # pdf files generated from svg files (cd doc; make latex) doc/src/modules/physics/mechanics/*.pdf # Mac OS X Junk .DS_Store # Backup files *~ # Temp output of sympy/printing/preview.py: sample.tex sympy-0.7.4.1/release/0000755000175000017500000000000012253362407014664 5ustar georgeskgeorgesksympy-0.7.4.1/release/Vagrantfile0000644000175000017500000000046012253362407017051 0ustar georgeskgeorgesk# -*- mode: ruby -*- # vi: set ft=ruby : Vagrant::Config.run do |config| #config.vm.box = "precise64" #config.vm.box_url = "http://files.vagrantup.com/precise64.box" config.vm.box = "precise32" config.vm.box_url = "http://files.vagrantup.com/precise32.box" config.ssh.forward_agent = true end sympy-0.7.4.1/release/README.md0000644000175000017500000001321412253362407016144 0ustar georgeskgeorgesk# Prepare the VM First execute: vagrant up fab vagrant prepare which will prepare the VM (install packages, cache sympy repository, etc.). You only need to execute this once. It will take a while if you have never run it before, because it has to download a lot of stuff. # Release First, make sure that you have done the following things - Create a release branch. Usually this branch is the same name as the release (e.g., "0.7.3"), although no naming convention is enforced on it. - Change the version in the release branch in sympy/__init__.py. If you want to do a release candidate, change it to something like 0.7.3.rc1. - Change the version in master. This way, any additional changes made in master will be shown as coming from the right place. The master release should be like "0.7.3-git". - Push the release branch up to origin, and make a pull request for it against master. It is important to create a new branch because that lets master continue as normal. The fab script will automatically checkout the release branch from origin, which is why you need to push it (it determines what the release branch by just looking at what branch you have checked out locally, so make sure you are on the release branch when you release). It is important to change the version number because it uses that in naming the tarballs it creates. If you want to test the release process without pushing a branch to the official repo, you can push a branch to your fork and use `fab vagrant release:fork='username'`, where `username` is your GitHub username. Note that once you do the actual release, you should do it in a branch in the official GitHub repo. **NOTE**: If your fork does not have all the tags of the official repo, then the code that finds the previous version will not work correctly. Hence, you may see things like more authors in the authors list than you should. To remedy this, be sure to do `git fetch origin --tags` and `git push github --tags`. Once you have done these things, execute: fab vagrant release this create release tarballs and put them all into a new "release" directory of the current directory. # Testing things The full test suite is not run by fabric, because we leave that to Travis. However, there are things that need to be tested specific to the release. Most of these things are done automatically by the release command (like testing that the tarball can be installed), but one thing must be tested manually, because it has to be inspected by hand, namely, making sure that the tarballs contain everything, and don't contain any junk files. Run fab vagrant show_files:arg to show the files in the tarball, where `arg` is `source` or `html`. You'll probably want to pipe the output of this into `less`, so that you can inspect it. You should also open the pdf and make sure that it has built correctly, and open the html docs and make sure that they have built correctly. # Tagging the release Once you have made the final release files that you plan to upload, be sure that everything is committed, and that the most recent git HEAD is indeed the same one that was used to build the files (you can always do `fab vagrant release` again if you are not sure). Then tag the release with the command git tag sympy-VERSION -a where you should replace `VERSION` with the version (which should be `x.y.z`, or `x.y.z.rcn` for the `n`th release candidate. It is very important to follow the tag naming conventions. The `-a` will cause it to prompt for a tag commit message. Just write something like "SymPy VERSION release". Then, push up the tag, with git push origin sympy-VERSION Note, once a tag is pushed, that's it. It can't be changed. If you need to change the tag, you must bump the release number. So double check that everything is right before pushing. # Uploading Before you release, you need to push the tag up, as described above. Release candidates should only be uploaded to GitHub only. fab vagrant GitHub_release This will create the release on GitHub for the tag, and upload the files to it. Do not upload release candidates to PyPI, as `pip` and `easy_install` will pick them up if you do. This will prompt you for a username and password the first time you call it. After that, it will prompt you to generate a token file. If you don't save the token to a file, you will need to pass it in as an argument. Releasing is only supported via OAuth, so using a token is required. You (obviously) need push access to create a GitHub release. If you want to test this before doing it, use fab vagrant GitHub_release:draft=True This will make the release not visible until you go to the web interface and publish it. You can also set the `user` and `repo` flags to test against a different GitHub repo. For final releases, you should upload to both GitHub and PyPI. The command fab vagrant upload will do both of these. You will need admin access to the SymPy PyPI project. Note that if either of these commands fails for some reason, you will very likely need to go into the web interface and clean some things up before you can upload again. # Other You can run all the SymPy tests by running: fab vagrant test_sympy To get the md5 sums of all the files, use fab md5 To list the files in the tarball use fab vagrant show_files:arg where `arg` is `source` or `html`, for the Python sources and the html docs, respectively. Note that the source code is already checked automatically against the files in git and a whitelist. You can obtain all available commands by: fab -l # Restarting from scratch Run vagrant destroy You can also delete the releases that it has built rm -rf release sympy-0.7.4.1/release/.gitignore0000644000175000017500000000002112253362407016645 0ustar georgeskgeorgesk.vagrant release sympy-0.7.4.1/release/fabfile.py0000644000175000017500000013402612253362407016634 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- """ Fab file for releasing Please read the README in this directory. Guide for this file =================== Vagrant is a tool that gives us a reproducible VM, and fabric is a tool that we use to run commands on that VM. Each function in this file should be run as fab vagrant func Even those functions that do not use vagrant must be run this way, because of the vagrant configuration at the bottom of this file. Any function that should be made avaiable from the command line needs to have the @task decorator. Save any files that should be reset between runs somewhere in the repos directory, so that the remove_userspace() function will clear it. It's best to do a complete vagrant destroy before a full release, but that takes a while, so the remove_userspace() ensures that things are mostly reset for testing. Do not enforce any naming conventions on the release branch. By tradition, the name of the release branch is the same as the version being released (like 0.7.3), but this is not required. Use get_sympy_version() and get_sympy_short_version() to get the SymPy version (the SymPy __version__ *must* be changed in __init__.py for this to work). """ from __future__ import print_function from collections import defaultdict, OrderedDict from contextlib import contextmanager from fabric.api import env, local, run, sudo, cd, hide, task from fabric.contrib.files import exists from fabric.colors import blue, red, green from fabric.utils import error, warn try: # Only works in newer versions of fabric env.colorize_errors = True except AttributeError: pass try: import requests from requests.auth import HTTPBasicAuth from requests_oauthlib import OAuth2 except ImportError: warn("requests and requests-oauthlib must be installed to upload to GitHub") requests = False import unicodedata import json from getpass import getpass import os import stat import sys import time import ConfigParser try: # https://pypi.python.org/pypi/fabric-virtualenv/ from fabvenv import virtualenv, make_virtualenv # Note, according to fabvenv docs, always use an absolute path with # virtualenv(). except ImportError: error("fabvenv is required. See https://pypi.python.org/pypi/fabric-virtualenv/") # Note, it's actually good practice to use absolute paths # everywhere. Otherwise, you will get surprising results if you call one # function from another, because your current working directory will be # whatever it was in the calling function, not ~. Also, due to what should # probably be considered a bug, ~ is not treated as an absolute path. You have # to explicitly write out /home/vagrant/ env.use_ssh_config = True def full_path_split(path): """ Function to do a full split on a path. """ # Based on http://stackoverflow.com/a/13505966/161801 rest, tail = os.path.split(path) if not rest or rest == os.path.sep: return (tail,) return full_path_split(rest) + (tail,) @contextmanager def use_venv(pyversion): """ Change make_virtualenv to use a given cmd pyversion should be '2' or '3' """ pyversion = str(pyversion) if pyversion == '2': yield elif pyversion == '3': oldvenv = env.virtualenv env.virtualenv = 'virtualenv -p /usr/bin/python3' yield env.virtualenv = oldvenv else: raise ValueError("pyversion must be one of '2' or '3', not %s" % pyversion) @task def prepare(): """ Setup the VM This only needs to be run once. It downloads all the necessary software, and a git cache. To reset this, use vagrant destroy and vagrant up. Note, this may take a while to finish, depending on your internet connection speed. """ prepare_apt() checkout_cache() @task def prepare_apt(): """ Download software from apt Note, on a slower internet connection, this will take a while to finish, because it has to download many packages, include latex and all its dependencies. """ sudo("apt-get -qq update") sudo("apt-get -y install git python3 make python-virtualenv zip python-dev") # Needed to build the docs sudo("apt-get -y install graphviz inkscape texlive texlive-xetex texlive-fonts-recommended texlive-latex-extra") # Our Ubuntu is too old to include Python 3.3 sudo("apt-get -y install python-software-properties") sudo("add-apt-repository -y ppa:fkrull/deadsnakes") sudo("apt-get -y update") sudo("apt-get -y install python3.3") @task def remove_userspace(): """ Deletes (!) the SymPy changes. Use with great care. This should be run between runs to reset everything. """ run("rm -rf repos") if os.path.exists("release"): error("release directory already exists locally. Remove it to continue.") @task def checkout_cache(): """ Checkout a cache of SymPy This should only be run once. The cache is use as a --reference for git clone. This makes deleting and recreating the SymPy a la remove_userspace() and gitrepos() and clone very fast. """ run("rm -rf sympy-cache.git") run("git clone --bare https://github.com/sympy/sympy.git sympy-cache.git") @task def gitrepos(branch=None, fork='sympy'): """ Clone the repo fab vagrant prepare (namely, checkout_cache()) must be run first. By default, the branch checked out is the same one as the one checked out locally. The master branch is not allowed--use a release branch (see the README). No naming convention is put on the release branch. To test the release, create a branch in your fork, and set the fork option. """ with cd("/home/vagrant"): if not exists("sympy-cache.git"): error("Run fab vagrant prepare first") if not branch: # Use the current branch (of this git repo, not the one in Vagrant) branch = local("git rev-parse --abbrev-ref HEAD", capture=True) if branch == "master": raise Exception("Cannot release from master") run("mkdir -p repos") with cd("/home/vagrant/repos"): run("git clone --reference ../sympy-cache.git https://github.com/{fork}/sympy.git".format(fork=fork)) with cd("/home/vagrant/repos/sympy"): run("git checkout -t origin/%s" % branch) @task def get_sympy_version(version_cache=[]): """ Get the full version of SymPy being released (like 0.7.3.rc1) """ if version_cache: return version_cache[0] if not exists("/home/vagrant/repos/sympy"): gitrepos() with cd("/home/vagrant/repos/sympy"): version = run('python -c "import sympy;print(sympy.__version__)"') assert '\n' not in version assert ' ' not in version assert '\t' not in version version_cache.append(version) return version @task def get_sympy_short_version(): """ Get the short version of SymPy being released, not including any rc tags (like 0.7.3) """ version = get_sympy_version() parts = version.split('.') non_rc_parts = [i for i in parts if i.isdigit()] return '.'.join(non_rc_parts) # Remove any rc tags @task def test_sympy(): """ Run the SymPy test suite """ with cd("/home/vagrant/repos/sympy"): run("./setup.py test") @task def test_tarball(release='2'): """ Test that the tarball can be unpacked and installed, and that sympy imports in the install. """ if release not in {'2', '3'}: # TODO: Add win32 raise ValueError("release must be one of '2', '3', not %s" % release) venv = "/home/vagrant/repos/test-{release}-virtualenv".format(release=release) tarball_formatter_dict = tarball_formatter() with use_venv(release): make_virtualenv(venv) with virtualenv(venv): run("cp /vagrant/release/{source} releasetar.tar".format(**tarball_formatter_dict)) run("tar xvf releasetar.tar") with cd("/home/vagrant/{source-orig-notar}".format(**tarball_formatter_dict)): run("python setup.py install") run('python -c "import sympy; print(sympy.__version__)"') @task def release(branch=None, fork='sympy'): """ Perform all the steps required for the release, except uploading In particular, it builds all the release files, and puts them in the release/ directory in the same directory as this one. At the end, it prints some things that need to be pasted into various places as part of the release. To test the release, push a branch to your fork on GitHub and set the fork option to your username. """ remove_userspace() gitrepos(branch, fork) # This has to be run locally because it itself uses fabric. I split it out # into a separate script so that it can be used without vagrant. local("../bin/mailmap_update.py") source_tarball() build_docs() copy_release_files() test_tarball('2') test_tarball('3') compare_tar_against_git() print_authors() @task def source_tarball(): """ Build the source tarball """ with cd("/home/vagrant/repos/sympy"): run("git clean -dfx") run("./setup.py clean") run("./setup.py sdist --keep-temp") run("./setup.py bdist_wininst") run("mv dist/{win32-orig} dist/{win32}".format(**tarball_formatter())) @task def build_docs(): """ Build the html and pdf docs """ with cd("/home/vagrant/repos/sympy"): run("mkdir -p dist") venv = "/home/vagrant/docs-virtualenv" make_virtualenv(venv, dependencies=['sphinx==1.1.3', 'numpy']) with virtualenv(venv): with cd("/home/vagrant/repos/sympy/doc"): run("make clean") run("make html-errors") with cd("/home/vagrant/repos/sympy/doc/_build"): run("mv html {html-nozip}".format(**tarball_formatter())) run("zip -9lr {html} {html-nozip}".format(**tarball_formatter())) run("cp {html} ../../dist/".format(**tarball_formatter())) run("make clean") run("make latex") with cd("/home/vagrant/repos/sympy/doc/_build/latex"): run("make") run("cp {pdf-orig} ../../../dist/{pdf}".format(**tarball_formatter())) @task def copy_release_files(): """ Move the release files from the VM to release/ locally """ with cd("/home/vagrant/repos/sympy"): run("mkdir -p /vagrant/release") run("cp dist/* /vagrant/release/") @task def show_files(file, print_=True): """ Show the contents of a tarball. The current options for file are source: The source tarball win: The Python 2 Windows installer (Not yet implemented!) html: The html docs zip Note, this runs locally, not in vagrant. """ # TODO: Test the unarchived name. See # https://code.google.com/p/sympy/issues/detail?id=3988. if file == 'source': ret = local("tar tf release/{source}".format(**tarball_formatter()), capture=True) elif file == 'win': # TODO: Windows raise NotImplementedError("Windows installers") elif file == 'html': ret = local("unzip -l release/{html}".format(**tarball_formatter()), capture=True) else: raise ValueError(file + " is not valid") if print_: print(ret) return ret # If a file does not end up in the tarball that should, add it to setup.py if # it is Python, or MANIFEST.in if it is not. (There is a command at the top # of setup.py to gather all the things that should be there). # TODO: Also check that this whitelist isn't growning out of date from files # removed from git. # TODO: Address the "why?" comments below. # Files that are in git that should not be in the tarball git_whitelist = { # Git specific dotfiles '.gitattributes', '.gitignore', '.mailmap', # Travis '.travis.yml', # This is the file you should edit if not enough ends up in the tarball 'MANIFEST.in', # Experimental Cythonization support. Not for production 'Makefile', # Nothing from bin/ should be shipped unless we intend to install it. Most # of this stuff is for development anyway. To run the tests from the # tarball, use setup.py test, or import sympy and run sympy.test() or # sympy.doctest(). 'bin/adapt_paths.py', 'bin/ask_update.py', 'bin/coverage_doctest.py', 'bin/coverage_report.py', 'bin/diagnose_imports', 'bin/doctest', 'bin/generate_test_list.py', 'bin/get_sympy.py', 'bin/py.bench', 'bin/mailmap_update.py', 'bin/strip_whitespace', 'bin/sympy_time.py', 'bin/sympy_time_cache.py', 'bin/test', 'bin/test_import', 'bin/test_import.py', 'bin/test_isolated', 'bin/test_travis.sh', # This is also related to Cythonization 'build.py', # The notebooks are not ready for shipping yet. They need to be cleaned # up, and preferrably doctested. See also # https://code.google.com/p/sympy/issues/detail?id=2940. 'examples/advanced/identitysearch_example.ipynb', 'examples/beginner/plot_advanced.ipynb', 'examples/beginner/plot_colors.ipynb', 'examples/beginner/plot_discont.ipynb', 'examples/beginner/plot_gallery.ipynb', 'examples/beginner/plot_intro.ipynb', 'examples/intermediate/limit_examples_advanced.ipynb', 'examples/intermediate/schwarzschild.ipynb', 'examples/notebooks/density.ipynb', 'examples/notebooks/fidelity.ipynb', 'examples/notebooks/fresnel_integrals.ipynb', 'examples/notebooks/qubits.ipynb', 'examples/notebooks/sho1d_example.ipynb', 'examples/notebooks/spin.ipynb', 'examples/notebooks/trace.ipynb', # This stuff :) 'release/.gitignore', 'release/README.md', 'release/Vagrantfile', 'release/fabfile.py', # This is just a distribute version of setup.py. Used mainly for setup.py # develop, which we don't care about in the release tarball 'setupegg.py', # We don't ship the benchmarks (why?) 'sympy/benchmarks/bench_meijerint.py', 'sympy/benchmarks/bench_symbench.py', 'sympy/core/benchmarks/bench_arit.py', 'sympy/core/benchmarks/bench_assumptions.py', 'sympy/core/benchmarks/bench_basic.py', 'sympy/core/benchmarks/bench_expand.py', 'sympy/core/benchmarks/bench_numbers.py', 'sympy/core/benchmarks/bench_sympify.py', 'sympy/functions/elementary/benchmarks/bench_exp.py', 'sympy/functions/special/benchmarks/bench_special.py', # More benchmarks 'sympy/integrals/benchmarks/bench_integrate.py', 'sympy/integrals/benchmarks/bench_trigintegrate.py', 'sympy/logic/benchmarks/input/10.cnf', 'sympy/logic/benchmarks/input/100.cnf', 'sympy/logic/benchmarks/input/105.cnf', 'sympy/logic/benchmarks/input/110.cnf', 'sympy/logic/benchmarks/input/115.cnf', 'sympy/logic/benchmarks/input/120.cnf', 'sympy/logic/benchmarks/input/125.cnf', 'sympy/logic/benchmarks/input/130.cnf', 'sympy/logic/benchmarks/input/135.cnf', 'sympy/logic/benchmarks/input/140.cnf', 'sympy/logic/benchmarks/input/145.cnf', 'sympy/logic/benchmarks/input/15.cnf', 'sympy/logic/benchmarks/input/150.cnf', 'sympy/logic/benchmarks/input/20.cnf', 'sympy/logic/benchmarks/input/25.cnf', 'sympy/logic/benchmarks/input/30.cnf', 'sympy/logic/benchmarks/input/35.cnf', 'sympy/logic/benchmarks/input/40.cnf', 'sympy/logic/benchmarks/input/45.cnf', 'sympy/logic/benchmarks/input/50.cnf', 'sympy/logic/benchmarks/input/55.cnf', 'sympy/logic/benchmarks/input/60.cnf', 'sympy/logic/benchmarks/input/65.cnf', 'sympy/logic/benchmarks/input/70.cnf', 'sympy/logic/benchmarks/input/75.cnf', 'sympy/logic/benchmarks/input/80.cnf', 'sympy/logic/benchmarks/input/85.cnf', 'sympy/logic/benchmarks/input/90.cnf', 'sympy/logic/benchmarks/input/95.cnf', 'sympy/logic/benchmarks/run-solvers.py', 'sympy/logic/benchmarks/test-solver.py', 'sympy/matrices/benchmarks/bench_matrix.py', # More benchmarks... 'sympy/polys/benchmarks/__init__.py', 'sympy/polys/benchmarks/bench_galoispolys.py', 'sympy/polys/benchmarks/bench_groebnertools.py', 'sympy/polys/benchmarks/bench_solvers.py', 'sympy/series/benchmarks/bench_limit.py', 'sympy/solvers/benchmarks/bench_solvers.py', # Example on how to use tox to test Sympy. For development. 'tox.ini.sample', } # Files that should be in the tarball should not be in git tarball_whitelist = { "PKG-INFO", # Generated by setup.py. Contains metadata for PyPI. } @task def compare_tar_against_git(): """ Compare the contents of the tarball against git ls-files """ with hide("commands"): with cd("/home/vagrant/repos/sympy"): git_lsfiles = set([i.strip() for i in run("git ls-files").split("\n")]) tar_output_orig = set(show_files('source', print_=False).split("\n")) tar_output = set() for file in tar_output_orig: # The tar files are like sympy-0.7.3/sympy/__init__.py, and the git # files are like sympy/__init__.py. split_path = full_path_split(file) if split_path[-1]: # Exclude directories, as git ls-files does not include them tar_output.add(os.path.join(*split_path[1:])) # print tar_output # print git_lsfiles fail = False print() print(blue("Files in the tarball from git that should not be there:", bold=True)) print() for line in sorted(tar_output.intersection(git_whitelist)): fail = True print(line) print() print(blue("Files in git but not in the tarball:", bold=True)) print() for line in sorted(git_lsfiles - tar_output - git_whitelist): fail = True print(line) print() print(blue("Files in the tarball but not in git:", bold=True)) print() for line in sorted(tar_output - git_lsfiles - tarball_whitelist): fail = True print(line) if fail: error("Non-whitelisted files found or not found in the tarball") @task def md5(file='*', print_=True): """ Print the md5 sums of the release files """ out = local("md5sum release/" + file, capture=True) # Remove the release/ part for printing. Useful for copy-pasting into the # release notes. out = [i.split() for i in out.strip().split('\n')] out = '\n'.join(["%s\t%s" % (i, os.path.split(j)[1]) for i, j in out]) if print_: print(out) return out descriptions = OrderedDict([ ('source', "The SymPy source installer.",), ('win32', "Python Windows 32-bit installer.",), ('html', '''Html documentation for the Python 2 version. This is the same as the online documentation.''',), ('pdf', '''Pdf version of the html documentation.''',), ]) @task def size(file='*', print_=True): """ Print the sizes of the release files """ out = local("du -h release/" + file, capture=True) out = [i.split() for i in out.strip().split('\n')] out = '\n'.join(["%s\t%s" % (i, os.path.split(j)[1]) for i, j in out]) if print_: print(out) return out @task def table(): """ Make an html table of the downloads. This is for pasting into the GitHub releases page. See GitHub_release(). """ # TODO: Add the file size tarball_formatter_dict = tarball_formatter() shortversion = get_sympy_short_version() tarball_formatter_dict['version'] = shortversion md5s = [i.split('\t') for i in md5(print_=False).split('\n')] md5s_dict = {name: md5 for md5, name in md5s} sizes = [i.split('\t') for i in size(print_=False).split('\n')] sizes_dict = {name: size for size, name in sizes} table = [] # http://docs.python.org/2/library/contextlib.html#contextlib.contextmanager. Not # recommended as a real way to generate html, but it works better than # anything else I've tried. @contextmanager def tag(name): table.append("<%s>" % name) yield table.append("" % name) with tag('table'): with tag('tr'): for headname in ["Filename", "Description", "size", "md5"]: with tag("th"): table.append(headname) for key in descriptions: name = get_tarball_name(key) with tag('tr'): with tag('td'): # code renders better than tt or pre with tag('code'): table.append(name) with tag('td'): table.append(descriptions[key].format(**tarball_formatter_dict)) with tag('td'): table.append(sizes_dict[name]) with tag('td'): table.append(md5s_dict[name]) out = ' '.join(table) return out @task def get_tarball_name(file): """ Get the name of a tarball file should be one of source-orig: The original name of the source tarball source-orig-notar: The name of the untarred directory source: The source tarball (after renaming) win32-orig: The original name of the win32 installer win32: The name of the win32 installer (after renaming) html: The name of the html zip html-nozip: The name of the html, without ".zip" pdf-orig: The original name of the pdf file pdf: The name of the pdf file (after renaming) """ version = get_sympy_version() doctypename = defaultdict(str, {'html': 'zip', 'pdf': 'pdf'}) winos = defaultdict(str, {'win32': 'win32', 'win32-orig': 'linux-i686'}) if file in {'source-orig', 'source'}: name = 'sympy-{version}.tar.gz' elif file == 'source-orig-notar': name = "sympy-{version}" elif file in {'win32', 'win32-orig'}: name = "sympy-{version}.{wintype}.exe" elif file in {'html', 'pdf', 'html-nozip'}: name = "sympy-docs-{type}-{version}" if file == 'html-nozip': # zip files keep the name of the original zipped directory. See # https://code.google.com/p/sympy/issues/detail?id=3988. file = 'html' else: name += ".{extension}" elif file == 'pdf-orig': name = "sympy-{version}.pdf" else: raise ValueError(file + " is not a recognized argument") ret = name.format(version=version, type=file, extension=doctypename[file], wintype=winos[file]) return ret tarball_name_types = { 'source-orig', 'source-orig-notar', 'source', 'win32-orig', 'win32', 'html', 'html-nozip', 'pdf-orig', 'pdf', } # This has to be a function, because you cannot call any function here at # import time (before the vagrant() function is run). def tarball_formatter(): return {name: get_tarball_name(name) for name in tarball_name_types} @task def get_previous_version_tag(): """ Get the version of the previous release """ # We try, probably too hard, to portably get the number of the previous # release of SymPy. Our strategy is to look at the git tags. The # following assumptions are made about the git tags: # - The only tags are for releases # - The tags are given the consistent naming: # sympy-major.minor.micro[.rcnumber] # (e.g., sympy-0.7.2 or sympy-0.7.2.rc1) # In particular, it goes back in the tag history and finds the most recent # tag that doesn't contain the current short version number as a substring. shortversion = get_sympy_short_version() curcommit = "HEAD" with cd("/home/vagrant/repos/sympy"): while True: curtag = run("git describe --abbrev=0 --tags " + curcommit).strip() if shortversion in curtag: # If the tagged commit is a merge commit, we cannot be sure # that it will go back in the right direction. This almost # never happens, so just error parents = local("git rev-list --parents -n 1 " + curtag, capture=True).strip().split() # rev-list prints the current commit and then all its parents assert len(parents) == 2, curtag curcommit = curtag + "^" # The parent of the tagged commit else: print(blue("Using {tag} as the tag for the previous " "release.".format(tag=curtag), bold=True)) return curtag error("Could not find the tag for the previous release.") @task def get_authors(): """ Get the list of authors since the previous release Returns the list in alphabetical order by last name. Authors who contributed for the first time for this release will have a star appended to the end of their names. Note: it's a good idea to use ./bin/mailmap_update.py (from the base sympy directory) to make AUTHORS and .mailmap up-to-date first before using this. fab vagrant release does this automatically. """ def lastnamekey(name): """ Sort key to sort by last name Note, we decided to sort based on the last name, because that way is fair. We used to sort by commit count or line number count, but that bumps up people who made lots of maintenance changes like updating mpmath or moving some files around. """ # Note, this will do the wrong thing for people who have multi-word # last names, but there are also people with middle initials. I don't # know of a perfect way to handle everyone. Feel free to fix up the # list by hand. # Note, you must call unicode() *before* lower, or else it won't # lowercase non-ASCII characters like Č -> č text = unicode(name.strip().split()[-1], encoding='utf-8').lower() # Convert things like Čertík to Certik return unicodedata.normalize('NFKD', text).encode('ascii', 'ignore') old_release_tag = get_previous_version_tag() with cd("/home/vagrant/repos/sympy"), hide('commands'): releaseauthors = set(run('git --no-pager log {tag}.. --format="%aN"'.format(tag=old_release_tag)).strip().split('\n')) priorauthors = set(run('git --no-pager log {tag} --format="%aN"'.format(tag=old_release_tag)).strip().split('\n')) releaseauthors = {name.strip() for name in releaseauthors if name.strip()} priorauthors = {name.strip() for name in priorauthors if name.strip()} newauthors = releaseauthors - priorauthors starred_newauthors = {name + "*" for name in newauthors} authors = releaseauthors - newauthors | starred_newauthors return (sorted(authors, key=lastnamekey), len(releaseauthors), len(newauthors)) @task def print_authors(): """ Print authors text to put at the bottom of the release notes """ authors, authorcount, newauthorcount = get_authors() print(blue("Here are the authors to put at the bottom of the release " "notes.", bold=True)) print() print("""## Authors The following people contributed at least one patch to this release (names are given in alphabetical order by last name). A total of {authorcount} people contributed to this release. People with a * by their names contributed a patch for the first time for this release; {newauthorcount} people contributed for the first time for this release. Thanks to everyone who contributed to this release! """.format(authorcount=authorcount, newauthorcount=newauthorcount)) for name in authors: print("- " + name) print() @task def check_tag_exists(): """ Check if the tag for this release has been uploaded yet. """ version = get_sympy_version() tag = 'sympy-' + version with cd("/home/vagrant/repos/sympy"): all_tags = run("git ls-remote --tags origin") return tag in all_tags # ------------------------------------------------ # Updating websites @task def update_websites(): """ Update various websites owned by SymPy. So far, supports the docs and sympy.org """ update_docs() update_sympy_org() def get_location(location): """ Read/save a location from the configuration file. """ locations_file = os.path.expanduser('~/.sympy/sympy-locations') config = ConfigParser.SafeConfigParser() config.read(locations_file) the_location = config.has_option("Locations", location) and config.get("Locations", location) if not the_location: the_location = raw_input("Where is the SymPy {location} directory? ".format(location=location)) if not config.has_section("Locations"): config.add_section("Locations") config.set("Locations", location, the_location) save = raw_input("Save this to file [yes]? ") if save.lower().strip() in ['', 'y', 'yes']: print("saving to ", locations_file) with open(locations_file, 'w') as f: config.write(f) else: print("Reading {location} location from config".format(location=location)) return os.path.abspath(os.path.expanduser(the_location)) @task def update_docs(docs_location=None): """ Update the docs hosted at docs.sympy.org """ docs_location = docs_location or get_location("docs") print("Docs location:", docs_location) # Check that the docs directory is clean local("cd {docs_location} && git diff --exit-code > /dev/null".format(docs_location=docs_location)) local("cd {docs_location} && git diff --cached --exit-code > /dev/null".format(docs_location=docs_location)) # See the README of the docs repo. We have to remove the old redirects, # move in the new docs, and create redirects. current_version = get_sympy_version() previous_version = get_previous_version_tag().lstrip('sympy-') print("Removing redirects from previous version") local("cd {docs_location} && rm -r {previous_version}".format(docs_location=docs_location, previous_version=previous_version)) print("Moving previous latest docs to old version") local("cd {docs_location} && mv latest {previous_version}".format(docs_location=docs_location, previous_version=previous_version)) print("Unzipping docs into repo") release_dir = os.path.abspath(os.path.expanduser(os.path.join(os.path.curdir, 'release'))) docs_zip = os.path.abspath(os.path.join(release_dir, get_tarball_name('html'))) local("cd {docs_location} && unzip {docs_zip} > /dev/null".format(docs_location=docs_location, docs_zip=docs_zip)) local("cd {docs_location} && mv {docs_zip_name} {version}".format(docs_location=docs_location, docs_zip_name=get_tarball_name("html-nozip"), version=current_version)) print("Writing new version to releases.txt") with open(os.path.join(docs_location, "releases.txt"), 'a') as f: f.write("{version}:SymPy {version}".format(version=current_version)) print("Generating indexes") local("cd {docs_location} && ./generate_indexes.py".format(docs_location=docs_location)) local("cd {docs_location} && mv {version} latest".format(docs_location=docs_location, version=current_version)) print("Generating redirects") local("cd {docs_location} && ./generate_redirects.py latest {version} ".format(docs_location=docs_location, version=current_version)) print("Committing") local("cd {docs_location} && git add -A {version} latest".format(docs_location=docs_location, version=current_version)) local("cd {docs_location} && git commit -a -m \'Updating docs to {version}\'".format(docs_location=docs_location, version=current_version)) print("Pushing") local("cd {docs_location} && git push origin".format(docs_location=docs_location)) @task def update_sympy_org(website_location=None): """ Update sympy.org This just means adding an entry to the news section. """ website_location = website_location or get_location("sympy.github.com") # Check that the website directory is clean local("cd {website_location} && git diff --exit-code > /dev/null".format(website_location=website_location)) local("cd {website_location} && git diff --cached --exit-code > /dev/null".format(website_location=website_location)) release_date = time.gmtime(os.path.getctime(os.path.join("release", tarball_formatter()['source']))) release_year = str(release_date.tm_year) release_month = str(release_date.tm_mon) release_day = str(release_date.tm_mday) version = get_sympy_version() with open(os.path.join(website_location, "templates", "index.html"), 'r') as f: lines = f.read().split('\n') # We could try to use some html parser, but this way is easier try: news = lines.index(r"

{% trans %}News{% endtrans %}

") except ValueError: error("index.html format not as expected") lines.insert(news + 2, # There is a

after the news line. Put it # after that. r""" {{ datetime(""" + release_year + """, """ + release_month + """, """ + release_day + """) }} {% trans v='""" + version + """' %}Version {{ v }} released{% endtrans %} ({% trans %}changes{% endtrans %})

""") with open(os.path.join(website_location, "templates", "index.html"), 'w') as f: print("Updating index.html template") f.write('\n'.join(lines)) print("Generating website pages") local("cd {website_location} && ./generate".format(website_location=website_location)) print("Committing") local("cd {website_location} && git commit -a -m \'Add {version} to the news\'".format(website_location=website_location, version=version)) print("Pushing") local("cd {website_location} && git push origin".format(website_location=website_location)) # ------------------------------------------------ # Uploading @task def upload(): """ Upload the files everywhere (PyPI and GitHub) """ distutils_check() GitHub_release() pypi_register() pypi_upload() test_pypi(2) test_pypi(3) @task def distutils_check(): """ Runs setup.py check """ with cd("/home/vagrant/repos/sympy"): run("python setup.py check") run("python3 setup.py check") @task def pypi_register(): """ Register a release with PyPI This should only be done for the final release. You need PyPI authentication to do this. """ with cd("/home/vagrant/repos/sympy"): run("python setup.py register") @task def pypi_upload(): """ Upload files to PyPI. You will need to enter a password. """ with cd("/home/vagrant/repos/sympy"): # See http://stackoverflow.com/a/17657183/161801 run("python setup.py sdist --dry-run upload") @task def test_pypi(release='2'): """ Test that the sympy can be pip installed, and that sympy imports in the install. """ # This function is similar to test_tarball() version = get_sympy_version() if release not in {'2', '3'}: # TODO: Add win32 raise ValueError("release must be one of '2', '3', not %s" % release) venv = "/home/vagrant/repos/test-{release}-pip-virtualenv".format(release=release) with use_venv(release): make_virtualenv(venv) with virtualenv(venv): run("pip install sympy") run('python -c "import sympy; assert sympy.__version__ == \'{version}\'"'.format(version=version)) @task def GitHub_release_text(): """ Generate text to put in the GitHub release Markdown box """ shortversion = get_sympy_short_version() htmltable = table() out = """\ See https://github.com/sympy/sympy/wiki/release-notes-for-{shortversion} for the release notes. {htmltable} **Note**: Do not download the `Source code (zip)` or the `Source code (tar.gz)` files below. """ out = out.format(shortversion=shortversion, htmltable=htmltable) print(blue("Here are the release notes to copy into the GitHub release " "Markdown form:", bold=True)) print() print(out) return out @task def GitHub_release(username=None, user='sympy', token=None, token_file_path="~/.sympy/release-token", repo='sympy', draft=False): """ Upload the release files to GitHub. The tag must be pushed up first. You can test on another repo by changing user and repo. """ if not requests: error("requests and requests-oauthlib must be installed to upload to GitHub") release_text = GitHub_release_text() version = get_sympy_version() short_version = get_sympy_short_version() tag = 'sympy-' + version prerelease = short_version != version urls = URLs(user=user, repo=repo) if not username: username = raw_input("GitHub username: ") token = load_token_file(token_file_path) if not token: username, password, token = GitHub_authenticate(urls, username, token) # If the tag in question is not pushed up yet, then GitHub will just # create it off of master automatically, which is not what we want. We # could make it create it off the release branch, but even then, we would # not be sure that the correct commit is tagged. So we require that the # tag exist first. if not check_tag_exists(): error("The tag for this version has not been pushed yet. Cannot upload the release.") # See http://developer.github.com/v3/repos/releases/#create-a-release # First, create the release post = {} post['tag_name'] = tag post['name'] = "SymPy " + version post['body'] = release_text post['draft'] = draft post['prerelease'] = prerelease print("Creating release for tag", tag, end=' ') result = query_GitHub(urls.releases_url, username, password=None, token=token, data=json.dumps(post)).json() release_id = result['id'] print(green("Done")) # Then, upload all the files to it. for key in descriptions: tarball = get_tarball_name(key) params = {} params['name'] = tarball if tarball.endswith('gz'): headers = {'Content-Type':'application/gzip'} elif tarball.endswith('pdf'): headers = {'Content-Type':'application/pdf'} elif tarball.endswith('zip'): headers = {'Content-Type':'application/zip'} else: headers = {'Content-Type':'application/octet-stream'} print("Uploading", tarball, end=' ') sys.stdout.flush() with open(os.path.join("release", tarball), 'rb') as f: result = query_GitHub(urls.release_uploads_url % release_id, username, password=None, token=token, data=f, params=params, headers=headers).json() print(green("Done")) # TODO: download the files and check that they have the right md5 sum def GitHub_check_authentication(urls, username, password, token): """ Checks that username & password is valid. """ query_GitHub(urls.api_url, username, password, token) def GitHub_authenticate(urls, username, token=None): _login_message = """\ Enter your GitHub username & password or press ^C to quit. The password will be kept as a Python variable as long as this script is running and https to authenticate with GitHub, otherwise not saved anywhere else:\ """ if username: print("> Authenticating as %s" % username) else: print(_login_message) username = raw_input("Username: ") authenticated = False if token: print("> Authenticating using token") try: GitHub_check_authentication(urls, username, None, token) except AuthenticationFailed: print("> Authentication failed") else: print("> OK") password = None authenticated = True while not authenticated: password = getpass("Password: ") try: print("> Checking username and password ...") GitHub_check_authentication(urls, username, password, None) except AuthenticationFailed: print("> Authentication failed") else: print("> OK.") authenticated = True if password: generate = raw_input("> Generate API token? [Y/n] ") if generate.lower() in ["y", "ye", "yes", ""]: name = raw_input("> Name of token on GitHub? [SymPy Release] ") if name == "": name = "SymPy Release" token = generate_token(urls, username, password, name=name) print("Your token is", token) print("Use this token from now on as GitHub_release:token=" + token + ",username=" + username) print(red("DO NOT share this token with anyone")) save = raw_input("Do you want to save this token to a file [yes]? ") if save.lower().strip() in ['y', 'yes', 'ye', '']: save_token_file(token) return username, password, token def generate_token(urls, username, password, OTP=None, name="SymPy Release"): enc_data = json.dumps( { "scopes": ["public_repo"], "note": name } ) url = urls.authorize_url rep = query_GitHub(url, username=username, password=password, data=enc_data).json() return rep["token"] def save_token_file(token): token_file = raw_input("> Enter token file location [~/.sympy/release-token] ") token_file = token_file or "~/.sympy/release-token" token_file_expand = os.path.expanduser(token_file) token_file_expand = os.path.abspath(token_file_expand) token_folder, _ = os.path.split(token_file_expand) try: if not os.path.isdir(token_folder): os.mkdir(token_folder, 0o700) with open(token_file_expand, 'w') as f: f.write(token + '\n') os.chmod(token_file_expand, stat.S_IREAD | stat.S_IWRITE) except OSError as e: print("> Unable to create folder for token file: ", e) return except IOError as e: print("> Unable to save token file: ", e) return return token_file def load_token_file(path="~/.sympy/release-token"): print("> Using token file %s" % path) path = os.path.expanduser(path) path = os.path.abspath(path) if os.path.isfile(path): try: with open(path) as f: token = f.readline() except IOError: print("> Unable to read token file") return else: print("> Token file does not exist") return return token.strip() class URLs(object): """ This class contains URLs and templates which used in requests to GitHub API """ def __init__(self, user="sympy", repo="sympy", api_url="https://api.github.com", authorize_url="https://api.github.com/authorizations", uploads_url='https://uploads.github.com', main_url='https://github.com'): """Generates all URLs and templates""" self.user = user self.repo = repo self.api_url = api_url self.authorize_url = authorize_url self.uploads_url = uploads_url self.main_url = main_url self.pull_list_url = api_url + "/repos" + "/" + user + "/" + repo + "/pulls" self.issue_list_url = api_url + "/repos/" + user + "/" + repo + "/issues" self.releases_url = api_url + "/repos/" + user + "/" + repo + "/releases" self.single_issue_template = self.issue_list_url + "/%d" self.single_pull_template = self.pull_list_url + "/%d" self.user_info_template = api_url + "/users/%s" self.user_repos_template = api_url + "/users/%s/repos" self.issue_comment_template = (api_url + "/repos" + "/" + user + "/" + repo + "/issues/%d" + "/comments") self.release_uploads_url = (uploads_url + "/repos/" + user + "/" + repo + "/releases/%d" + "/assets") self.release_download_url = (main_url + "/" + user + "/" + repo + "/releases/download/%s/%s") class AuthenticationFailed(Exception): pass def query_GitHub(url, username=None, password=None, token=None, data=None, OTP=None, headers=None, params=None, files=None): """ Query GitHub API. In case of a multipage result, DOES NOT query the next page. """ headers = headers or {} if OTP: headers['X-GitHub-OTP'] = OTP if token: auth = OAuth2(client_id=username, token=dict(access_token=token, token_type='bearer')) else: auth = HTTPBasicAuth(username, password) if data: r = requests.post(url, auth=auth, data=data, headers=headers, params=params, files=files) else: r = requests.get(url, auth=auth, headers=headers, params=params, stream=True) if r.status_code == 401: two_factor = r.headers.get('X-GitHub-OTP') if two_factor: print("A two-factor authentication code is required:", two_factor.split(';')[1].strip()) OTP = raw_input("Authentication code: ") return query_GitHub(url, username=username, password=password, token=token, data=data, OTP=OTP) raise AuthenticationFailed("invalid username or password") r.raise_for_status() return r # ------------------------------------------------ # Vagrant related configuration @task def vagrant(): """ Run commands using vagrant """ vc = get_vagrant_config() # change from the default user to 'vagrant' env.user = vc['User'] # connect to the port-forwarded ssh env.hosts = ['%s:%s' % (vc['HostName'], vc['Port'])] # use vagrant ssh key env.key_filename = vc['IdentityFile'].strip('"') # Forward the agent if specified: env.forward_agent = vc.get('ForwardAgent', 'no') == 'yes' def get_vagrant_config(): """ Parses vagrant configuration and returns it as dict of ssh parameters and their values """ result = local('vagrant ssh-config', capture=True) conf = {} for line in iter(result.splitlines()): parts = line.split() conf[parts[0]] = ' '.join(parts[1:]) return conf @task def restart_network(): """ Do this if the VM won't connect to the internet. """ run("sudo /etc/init.d/networking restart") # --------------------------------------- # Just a simple testing command: @task def uname(): """ Get the uname in Vagrant. Useful for testing that Vagrant works. """ run('uname -a') sympy-0.7.4.1/doc/0000755000175000017500000000000012253362407014011 5ustar georgeskgeorgesksympy-0.7.4.1/doc/cheatsheet/0000755000175000017500000000000012253362407016126 5ustar georgeskgeorgesksympy-0.7.4.1/doc/cheatsheet/cheatsheet.tex0000644000175000017500000002112612253362407020767 0ustar georgeskgeorgesk\documentclass{article} \usepackage[landscape]{geometry} \usepackage{url} \usepackage{multicol} \usepackage{amsmath} \usepackage{amsfonts} \advance\topmargin-.8in \advance\textheight2in \advance\textwidth3in \advance\oddsidemargin-1.5in \advance\evensidemargin-1.5in \parindent0pt \parskip2pt \newcommand{\hr}{\centerline{\rule{3.5in}{1pt}}} \usepackage{tikz} \usetikzlibrary{shapes,snakes} \usepackage{amsmath,amssymb} \begin{document} \begin{center}\huge{\textbf{SymPy Cheatsheet (http://sympy.org)}}\end{center} \begin{multicols*}{3} \tikzstyle{mybox} = [draw=black, fill=white, very thick, rectangle, rounded corners, inner sep=10pt, inner ysep=10pt] \tikzstyle{fancytitle} =[fill=black, text=white, font=\bfseries] \begin{tikzpicture} \node [mybox] (box){% \begin{minipage}{0.3\textwidth} \begin{center}\small{\begin{tabular}{l r} %Sympy packages:& \verb|from sympy import some_package |\\ Sympy help: & \verb|help(function)|\\ Declare symbol:& \verb|x = Symbol('x')|\\ Substitution:& \verb|expr.subs(old, new)|\\ Numerical evaluation:& \verb|expr.evalf()|\\ Expanding:& \verb|expr.expand()|\\ Common denominator:& \verb|ratsimp(expr)|\\ Simplify expression:&\verb|simplify(expr)|\\ \end{tabular}}\end{center} \end{minipage} }; \node[fancytitle, right=10pt] at (box.north west) {Basics}; \end{tikzpicture} \begin{tikzpicture} \node [mybox] (box){% \begin{minipage}{0.075\textwidth} \begin{center}\small{\begin{tabular}{l r} $\pi$: &\verb|pi|\\ $e$: &\verb|E|\\ $\infty$:&\verb|oo|\\ $i$:&\verb|I| \end{tabular}}\end{center} \end{minipage} }; \node[fancytitle, right=10pt] at (box.north west) {Constants}; \end{tikzpicture} \begin{tikzpicture} \node [mybox] (box){% \begin{minipage}{0.195\textwidth} \begin{center}\small{\begin{tabular}{l r} Integers ($\mathbb{Z}$): &\verb|Integer(x)|\\ Rationals ($\mathbb{Q}$): &\verb|Rational(p, q)|\\ Reals ($\mathbb{R}$): &\verb|Float(x)|\\ \\ \end{tabular}}\end{center} \end{minipage} }; \node[fancytitle, right=10pt] at (box.north west) {Numbers types}; \end{tikzpicture} \begin{tikzpicture} \node [mybox] (box){% \begin{minipage}{0.3\textwidth} \begin{center}\small{\begin{tabular}{l r} Trigonometric: &\verb|sin cos tan cot|\\ Cyclometric: &\verb|asin acos atan acot|\\ Hyperbolic: &\verb|sinh cosh tanh coth|\\ Area hyperbolic: &\verb|asinh acosh atanh acoth|\\ Exponential: &\verb|exp(x)|\\ Square root: &\verb|sqrt(x)|\\ Logarithm ($\log_ba$): &\verb|log(a, b)|\\ Natural logarithm: &\verb|log(a)|\\ Gamma ($\Gamma(x)$): &\verb|gamma(x)|\\ Absolute value: &\verb|abs(x)|\\ \end{tabular}}\end{center} \end{minipage} }; \node[fancytitle, right=10pt] at (box.north west) {Basic funtions}; \end{tikzpicture} \begin{tikzpicture} \node [mybox] (box){% \begin{minipage}{0.3\textwidth} \begin{center}\small{\begin{tabular}{l r} $\displaystyle\lim_{x\to a} f(x)$: &\verb|limit(f, x, a)|\\ $\displaystyle\lim_{x\to a_-} f(x)$: &\verb|limit(f, x, a, dir='-')|\\ $\displaystyle\lim_{x\to a_+} f(x)$: &\verb|limit(f, x, a, dir='+')|\\ $\frac{d}{dx}f(x)$: &\verb|diff(f, x)|\\ $\frac{\partial}{\partial x} f(x,y)$: &\verb|diff(f, x)|\\ $\int f(x) \, dx:$& \verb|integrate(f, x)|\\ $\int_a^b f(x) \, dx:$& \verb|integrate(f, (x, a, b))|\\ Taylor series (at $a$, deg $n$)& \verb|f.series(x, a, n)|\\ \end{tabular}}\end{center} \end{minipage} }; \node[fancytitle, right=10pt] at (box.north west) {Calculus}; \end{tikzpicture} \begin{tikzpicture} \node [mybox] (box){% \begin{minipage}{0.3\textwidth} \begin{center}\small{\begin{tabular}{l r} Equation $f(x)=0$: &\verb|solve(f, x)|\\ System of equations: &\verb|solve([f, g], [x, y])|\\ Differential equation: &\verb|dsolve(equation, f(x))|\\ \end{tabular}}\end{center} \end{minipage} }; \node[fancytitle, right=10pt] at (box.north west) {Equations}; \end{tikzpicture} \begin{tikzpicture} \node [mybox] (box){% \begin{minipage}{0.3\textwidth} \begin{center}\small{\begin{tabular}{l r} Points: & \verb|a = Point(xcoord, ycoord)|\\ Lines: &\verb|l = Line(pointA, pointB)|\\ Circles: &\verb|c = Circle(center, radius)|\\ Triangles: &\verb|t = Triangle(a, b, c)|\\ Area: &\verb|object.area|\\ Intersection: &\verb|intersection(a, b)|\\ Checking tangency: &\verb|c.is_tangent(l)|\\ \end{tabular}}\end{center} \end{minipage} }; \node[fancytitle, right=10pt] at (box.north west) {Geometry}; \end{tikzpicture} \begin{tikzpicture} \node [mybox] (box){% \begin{minipage}{0.3\textwidth} \begin{center}\small{\begin{tabular}{l r} Plot: &\verb|Plot(f, [a, b])|\\ Zoom: $+/-$: &\verb|R/F or PgUp/PgDn or Numpad +/-|\\ Rotate X,Y axis: &\verb|Arrow Keys or WASD|\\ Rotate Z axis: &\verb| Q and E or Numpad 7 and 9|\\ View XY: &\verb|F1|\\ View XZ: &\verb|F2|\\ View YZ: &\verb|F3|\\ View Perspective: &\verb|F4|\\ Axes Visibility: &\verb|F5|\\ Axes Colors: &\verb|F6|\\ Screenshot: &\verb|F8|\\ Exit plot: &\verb|ESC|\\ \end{tabular}}\end{center} \end{minipage} }; \node[fancytitle, right=10pt] at (box.north west) {Plotting}; \end{tikzpicture} \begin{tikzpicture} \node [mybox] (box){% \begin{minipage}{0.3\textwidth} \begin{center}\small{\begin{tabular}{l r} Factorial ($n!$): &\verb|factorial(n)|\\ Binomial coefficient $n\choose k$: &\verb|binomial(n, k)|\\ Sum ($\sum_{n=a}^b expr$): &\verb|summation(expr, (n, a, b))|\\ Product ($\prod_{n=a}^b expr$): &\verb|product(expr, (n, a, b))|\\ \end{tabular}}\end{center} \end{minipage} }; \node[fancytitle, right=10pt] at (box.north west) {Discrete math}; \end{tikzpicture} \begin{tikzpicture} \node [mybox] (box){% \begin{minipage}{0.3\textwidth} \begin{center}\small{\begin{tabular}{l r} Matrix definition: &\verb|m = Matrix([[a, b], [c, d]])|\\ Determinant: &\verb|m.det()|\\ Inverse: &\verb|m.inv()|\\ Identity matrix $n\times n$: &\verb|eye(n)|\\ Zero matrix $n\times n$: &\verb|zeros(n)|\\ Ones matrix $n\times n$: &\verb|ones(n)|\\ \end{tabular}}\end{center} \end{minipage} }; \node[fancytitle, right=10pt] at (box.north west) {Linear algebra}; \end{tikzpicture} \begin{tikzpicture} \node [mybox] (box){% \begin{minipage}{0.3\textwidth} \begin{center}\small{\begin{tabular}{l r} \LaTeX{} print: &\verb|print latex()|\\ Python print: &\verb|print python()|\\ Pretty print: &\verb|pprint()|\\ \end{tabular}}\end{center} \end{minipage} }; \node[fancytitle, right=10pt] at (box.north west) {Printing}; \end{tikzpicture} \begin{tikzpicture} \node [mybox] (box){% \begin{minipage}{0.3\textwidth} \small{ Find 100 digits of $\pi^e$:\\ \verb|(pi**E).n(100)|\\ \\ Expand $(x+y)^2(x-y)(x^2+y)$:\\ \verb|((x + y)**2 * (x - y) * (x**2 + y)).expand()|\\ \\ Simplify $\displaystyle\frac{1}{x} + \frac{x\sin x -1}{x^2-1}$:\\ \verb|simplify((1/x) + (x * sin(x) - 1)/(x**2 - 1))|\\ \\ Check if line passing through points $(0,1)$ and $(1,1)$\\ is tangent to circle with center at $(5,5)$ and radius $3$:\\ \verb|Circle(Point(5,5), 3).is_tangent(|\\\verb|Line(Point(0,1), Point(1,1)))|\\ \\ Find roots of $x^4-4x^3+2x^2-x=0$:\\ \verb|solve(x**4 - 4*x**3 + 2*x**2 - x, x)|\\ \\ Solve the equations system: $x+y=4$, $xy=3$:\\ \verb|solve([x + y - 4, x*y - 3], [x, y])|\\ \\ Calculate limit of the sequence $\sqrt[n]{n}$:\\ \verb|limit(n**(1/n), n, oo)|\\ \\ Calculate left-sided limit of the function $\frac{|x|}{x}$ in 0:\\ \verb|limit(abs(x)/x, x, 0, dir='-')|\\ \\ Calculate the sum $\sum_{n=0}^{100} n^2$:\\ \verb|summation(n**2, (n, 0, 100))|\\ \\ Calculate the sum $\sum_{n=0}^{\infty} \frac{1}{n^2}$:\\ \verb|summation(1/n**2, (n, 0, oo))|\\ \\ Calculate the integral $\int \cos^3 x \, dx$:\\ \verb|integrate(cos(x)**3, x)|\\ \\ Calculate the integral $\int_1^{\infty} \frac{dx}{x^2}$:\\ \verb|integrate(1/x**2, (x, 1, oo))|\\ \\ Find 10 terms of series expansion of $\frac{1}{1-2x}$ at $0$:\\ \verb|(1/(1 - 2*x)).series(x, 0, 10)|\\ \\ Solve the differential equation $f^{''}(x)+9f(x)=1$:\\ \verb|dsolve(f(x).diff(x, x) + 9*f(x) - 1, f(x))|\\ } \end{minipage} }; \node[fancytitle, right=10pt] at (box.north west) {Examples}; \end{tikzpicture} %\begin{tikzpicture} %\node [mybox] (box){% % \begin{minipage}{0.3\textwidth} % \begin{center}\small{\begin{tabular}{l r} % % \end{tabular}}\end{center} % \end{minipage} %}; %\node[fancytitle, right=10pt] at (box.north west) {Numbers types}; %\end{tikzpicture} %\begin{tikzpicture} %\node [mybox] (box){% % \begin{minipage}{0.3\textwidth} % \begin{center}\small{\begin{tabular}{l r} % % \end{tabular}}\end{center} % \end{minipage} %}; %\node[fancytitle, right=10pt] at (box.north west) {Numbers types}; %\end{tikzpicture} \end{multicols*} \end{document} sympy-0.7.4.1/doc/logo/0000755000175000017500000000000012253362407014751 5ustar georgeskgeorgesksympy-0.7.4.1/doc/logo/SymPy-Favicon.ico0000644000175000017500000001412612253362407020115 0ustar georgeskgeorgeskPNG  IHDR@@iqbKGDC pHYs ` `%  vpAg@@`IDATx{dW?yzfd񂱗 k@Zv&(e?l)J-A" wlf=3=~wWWuU׻u'؀a-_[::9%mr;pK77[p;pK77[lLNAe^G\n]VԷD]]Dd'Id)$eHeD2b`incYm0AM!C'S8tK'Xk1,oޣ0p=q|/Uyg֌̡}Ňgr-3F8le)=B|s=[j;UҁXމI0;5X)OG>ەU M#?2B6W j [5Z;;] "mN]:O {ʰo_Biv;HE5Ool7rWTO@ c"` ˄C;u~v&Cr<ێN01Q$I2mT#Ubum ̞8 t֗ql,k]t+Ó~HUF]Y T)Hb}*$8<-l]i6huL<ӓLfK; a Ӫ8C I$L86Mnt ;;Y >[˫6kUv/ D7_: *LR(Bu:%y-Jݝ?c)= P36jgrHhYtll֐ =~{]&n_ү*:a";Z^`}e !a:u d(ɕ ;%.Uyg٬԰}ti=S1rL])er\pXH +,wt$NcV+2am^:$1N` H'0b*D0TkmJFII&Y,D*Aslrå 5B3 q;ӉU_ aOJv`"PɄj RcXϡ&e oGˆN c : j+ \HX)HW_YxPAF@i0LUSyR\;CPx^;ffig531mį1l7E!E8 ==`6`ism230MMU (\Yd {gtL|KK=!~td^N';{(YյEmuOnG>2VV(6ЅH: &SхKci>/wx'?GHMJDa ,L(kVf~];u*C ! R}sz"f?׼@OiTTk] 6d{9Θ0hv_E-AS,,3$cFL1d6E$xC(0M. Pʫ5r+ $2)C9f%i]af38zDvm6tϳ|rtU"0$$;rw!HPTE/?4Hb~% 9!^E02cl64Igt LSSL.c<-ߢ5W.6Kgx; 6>ϽG21Yd` s<0 NihdJk+L} p $FL0R19U@Akk\Z[UQdjo&YݠS'= 7AAMֈRnzOskzB*a)di-#j R:Z\W4# !1US,eael"C=dRd1)tRC7FU$+dlpẂtRf/K|O{m9D\ߧlIȿ 045tME͗Y$ vz\HB""8> f[\!딙O3̩.wOO|̗.w'80f'Z61:T:f[ud&NF}KWw(& t.ݖ "LG.T12c`5$cQe8ZsdL>YKoGWjtvEA𚲯5>q>/cQ]6%CԾ`"q}:]VϥK$`hHZXJ.O!5"J|3//r-d }jE(Ba&+f @SV S@*N`  xAD vK)!w̞=XKkl h5{:6ö>w#SW '}|Ѓګx_{bXN( YYѬD 4A?Vߕoɟy}s}Ǟee-BTȏH40²]] qbL1q{3dqB'r5:uvݹHiTxURl<j?S~N|_u]:ZB!R%81?Xw@ζg!5 PռJ&{h2>xG1DIU {f|Dm#7p^Ú!:Qrs]d;\2[ч~n[/'a0Ž \Gd1TM!#!P:^h#, u7Q^~eZz6)9W18!U&USj U@(! = "Sz,DQmq쟻37ku0dN4=Fw1*r"R0P@QQWBlU)v=K7d'/[_%݁/S"s(zTXtSoftwarexML.MMLTp,J-/.V`'6IENDB`sympy-0.7.4.1/doc/logo/SymPy-500x500-Logo-No-Transparency-No-Text.png0000644000175000017500000045232412253362407024704 0ustar georgeskgeorgeskPNG  IHDRߊsBIT|d pHYs ` `% tEXtSoftwareMacromedia Fireworks 8hx!prVWxZ TTgL^MWu:ëԣzPa)*.HQܗAqC׸(qKT-fINzdg3w_AcQ` ?àUP55PS5nzKJjMh}/))q׸KjtUv]n{nrr¿tJ\.r7\A +>*..@/_~|ʕJ+xђo-h넹 ae^rEL^Xt/w{_sf?++]by7eI# 4f(DGB~{k[OCnlk`5}2oÀka9H \znn8kB0}Q #TBAW.@gKGAT|ϢWQI1f ? |gvЮGVϪEw[__=K|l^ y6qY9a:]PcSu. CQdP轌4LCp`&|4ӿ7 &E QeQW1#$ / DQ=oQ;<>8AQᨰ $YmMIV2IT6A"~N|9)$JMCCv$6I05_QT{ZAaq\pHyp(ڊ(@T[Q0*$< QUqrO4=lWtduh ;)@ a)vbSFCH m" GmP;?oh©/ILq)Zz](gBirFh TF1)&xf5 {yyo>:g7FB(*.R][&YA0r!/ $VjMV|8e;>oiUB{:$]4蚆PP2g  +($dΝ=h7k0ހ%V0д|)=^#( :Ħ˪͊\ Jʣ8`۹ېjx_\hsfEjFO]1M8auϽ+P\h6rFΎ%R Y݀O g|"*E[C,h3h6,7ǀ3ւE", uA(+旌kWFOO~=gb*SQ;:b Җr͂.13 °5<^d,7-. _YS4PB]90)'Pk^\@%vS|Άד}C@*!:SP)8TU+F-8{MM,~vgCC!D6GQ3S%-DzG`Ŝ„D!BaB9hs]AycIڵ>vmuGЄc=KěyAkj{5dE|OP.ʆēoBڍz|.醲̈́iKۊc,+Qa vͭ_07ьfenR @UMz6{4# I ?6yt6t(g=c NTMv Eau'zb0PQ21ZC;gNID_ #)jֈ(rzFޏ Sz-E䶘\X4KZY-`f*3oi&*`?,]Q/~<6lITe#fDEH@u >G&6KS#C5RD[a6FZ_杽o?Uj,x2DYvFM6klIs]}zbt?̂GcL=m< "ٴ' lTx֔3w t&Α-} ֌`tj@kE&+"_Oޘ !*SK}Ǹ!/ 97Ҫ˰`ߙ?}'FX$qm 6]z$d|;n}Yw s! (D?Hv83iV͌K8?/\*oܫq+=}"XEڜ68 cz2<&{j:mi8%oN~^#΀EG| Pi)ׄd`{*PhX$R}t|Ab߄yd,<;Wj7aLo*7g@{}6~*Җ'o|\:"pz=[RM8bڲ1Dlb_w4^|Avf}vHVڪ]{Ln.j*x+ 8=~U~~]QQUuh`; FbXD:*9vΜb?%B7q' &2ls393/rw(J,{ @pod4%p7A( ].bt9K@"5/* zL'FH(E{/\ [7 J ;tiݒΝhg͆yG #H<5NlYQb1]1Fch6Ut́Ᏻ:A vf*${&B)7:1 _duZ(h! ]ɩ >}^d'~UJN$vxx޵,>yL*(+FP" ިUW,ɨ%JVІO8׉gDD(ڴԐ$6=7~f|Movd|T /}p|}.L;ve 8Ğ~:ș ɛt r{G|߼Owɠ&8Jpˇ|1d^i7A33!" ww[ }oM@xiG4^۽8wAfY>V w!z<KBѱFc|@DG':;Q76^7G}V[aGGq 0`$XZA]\]3!hwl,?bvցлl o%н:Z-]gQ׷"y-`k|s/vڕGg;Ga 6H9;J3ݐXt5;ږҮnԳ rH{0;#..+ڡ֖?„jpt%}01 dޮC1rp/?3whv֧=vc  0+oư2{΁mצB\Hp߫?\ÀoxΈivîW@SS-ftasAZw͚=^7~q1U蘜%Wv*LB;{a҃q zWQo̳}aw_F>̨㿸_AMaV]V}Cqc;{rt! V m7AJhǞ}/L9bu _\*!q= 0'nB P'S5֮'OHmkBF)3mkTSx][sFdƱ%˷d+/ڧdp'(8$yq as#K2sƍH*h n\:~{?nvFcm ,V}wr=[;jcKU'G=c0&{gnǻmӻ+ϔ)]UnMNal퇰Z(J ^*IqڇwwSЙ6Fcs{|<anw6Cb;oNmҷf'lc zcWuY#zlq<^ƩixƩA&laIl^%(Wl(Ce0/BG(О.+h36':9GĨ-s H;@ja:<8f 59t2B&CȜtۿ2va)n69ǰ/з;؟g.Sl :R{%%m00z10_r0@.3R>\E|nD3_)W`j|jFaTS>lՌak ID܅#Ww ci8:еr9jg$b ?`@n"ae TT|"Bl8R=PI {4@/3L/VkTFϾ[p+E&tdexj }tۻ |z4i&RBttO@5J6}PB2 ԔGM h^B7 XN?O26$$sKD%J4OExfΥrOFp0F6f8rRԭLˏ=tðvڰ0516BЂ|%[:Kl(D1Qd R(()вZ6CfC }a(i譸o99Xg(AQETL 2 -<PHrųflKLp:)eF:[Q\H+yrp#Cc84g'x*f ]͘JG%guI_N=#O+Nqqv,{A7 *FeШ ATlLysٗ,2BCȚ)zω^u`Le0 &W71ܤrDVP.⊕q4kXdAqàr#RO4}"c^#-1"i1GM GV:IfŮ8Tl+d#֩5gH&=Ei& =U$9x>s/8%% ,1 ַS2m9VnY0ɃB  RmЉR+aqt>PFg,1qI\ D0Č{tOuWm#\iʘ\At!69"fs"LP]quÀ8# Vb WScN'Hqv:($R7Rt!)}&$ž- ii20)z)~Qx7Cxo)(7E`R;(a|?qfEEp pmpH@=)y9F1ȿOP#415e)8r],Tpjh͗RrKXK{Gy\R||sVW+3k ʐ5܁GXCG#IbXif~nHY8CaKO,p0B@8EK5I:(Lhu{a\],S%1yekZRd8g>h\E9mYAIc\lHwdz@ D832y_8ҐN`,{ޣH9oH AGpM =+h.\0,ah1tK@hKP԰Y@ ㄩs5AK6zm>|($W ٠$E-]&a"L#^l`+̿vܿƕva7:GG1L&02Df,Lti CY1<2L=Gyv΋:6QX̑NÔ`\Ȏ8 0Pf5S¢/{+QT=q{oo!dkXטа`Oxf[.T7XƗٗN Ժ%&*d\)bNy $C}Gx? JcZh4Fʼn"IEK$|lt68Ax@nوI LGȴ|FP[*d[󉊤10Lދٳ9hgXŰ`^w·I97ͽ>N3gW='3Ro:P"XK/h:dAҁ):Oa-%?]'` F#(O @6DD%!UºVC>(zr bg:Žh+`bf./BO<D"[gWe#r IZtA`/#8۔(!%K<h3`\Q«I ҩȇ6+K;xlB6]3yRK&1G=%t*R6+MQ-%

2h1gM.[It2b%:Ke5i"oRBU4QK KvY ΄Yd9՞4G*,clXǶxe}AD) /??['5cಡEm67#͌}e44|,~}X`mwE^ `Y٤UcҪUҚEZMBdHe,elrJZeKkon_l=nMNWɩ6 6|Tj=ld6d6 ra R&!ְV& S^xO>[֩QWN?NkZ޾7Z|':G')h\j{dj`Jqo?p<+ ڞ>l A$E.J#uGuTJ1}j_PPY҈S%#Yo# iډn|Ȳy jcJa KZU  UU4sSkjb ٚm܁*S ]5<5ՂkzM맡 MߴRPݡji&/=*kU)+҅G&S-ȪB+*$UN >Dz]uz1IU ?S8@a_[(0ܔlݳĚFw4UzjE`lhi2" (4F]gU<*_x$i[΄M;~h X*p u-Ih?6%UQR:>7fcH0QQ3z(L s\vޏ{-_ ~`II!ý<3/ {5geW$kض738=85iB#sڣH&_ŮqV^KٕGP8KjnKxGOcEф#51*z7(Kz^w,#pon$U^Eɛ>z䖊'nx"1܏pGJ?X^[*#cq =l;o I9[ol'v A>^pw95هM/qǖaIS)H7 }$Iλ 0bglћ/o`Ĥ/0}k\:%UYa#;?~#s9[8KMHegi&gh?'*nqP'ߦjFDzY_2IޑD.%y wb芡+b m< Co&Z+8芣+.rtPvAW\s;gg9qt[sCW ]1tE~@3t!x;RP؟p8슳+ή8(gK#]1tCW ]1tafh 문nڣ+vعbbgs.WYyW1tCW zʻt^m]GW]q+sˮعb犝+v~<]~cwCW ]1tЏ篼Kg8슳+~=]Wʻ+bW5@72\CeuXb|qa4>L_]oή^|:AfFۀ=#ku/LVt"隳]Ck k[Գg\0g4ۚ:aj$oT/*G勮/*֦=Dq~/-*AGGyڇW1lIo`]DS0qtQ]`D QCmx J0CSL7)[0z9x9nŴ`EɂDŽg׆+K7cUL^_&d%i`^/hOWДoK8;Ԅ #D#$KK;dC`_+֛e7#~k~T)чx0jk9b&ɷ׈5@Lyr1֩ zw|DnDT|pBVZYMϒ:JWN-ImQynh1UȾu! ~Y7pt02I2' zV)]ٌ)m.N<W-oCj9b>tus^'P,ݍ䞋XpӶc n# XF2ޥSKҳxQMy䊚bK" !\KڵHV`v-#+Y JTY*+J<!} 6`VIߏuUF%^`i@W-ʨȑ&{kXƲ2}gTOH43qT }3Z  =mo=0`OloA]sx#}<&Ui'kypwGڈ'C<#Xk./߅*?O쵐zOWC?X_RA|g!.ϑ#N"PBH!;oCmO>M>e55kF#tWoRHVY&thɪ4!{.xd7}\ O=tX>_kEU1K}8E8eI> [ʯ >yEZ:ȠΟñkU1>7קcݦ٦>btŢ?";.ogfhqLOyu5^>Ʒ.Gb k,+Xʢ V*jm֥{$4)2u7T.W%=l5˭E%~ =1[3?:2ƣ/sAž# و%[ݴRtNpp)V=y*YI*XvڣJҏk@ >@5( myH"` dCh.wWK)"ok^rϚ'a`o 7XX8x!U EGauaT,gbaz 4taxRFo~9@a(iX>3R HZ6h !1֐VH\&Evp]fذ|ϖ(m34[ơ@k vN;$G mkBSx]N0Æ WM- 2*5X|DZ=fLc(F? hҶ BLrGpodgd/e>f 9]WA\\Lf[xilϣTs1H QlHاEt(SAZ_Y X~mkBT~xW眙c̦ @ !"}`}&q>$c74'u:ng뜸tf:''3'=iuk χ߱Ab{GBOhtAKP. ] Wd(,8k X &e 2X*P٬E@ 6n 0 oį;?:apw_=3J VY )kEkA͇R G)nFH=gŧ~u:}/Ԭ+]ѱ6A*o(=Ep0=./dNϴ)/䂀 <zYlKPjcA%9r|*aFh=Ylx</.C <@h+;&K*,6B/FsChl bl{ūq['V>,A-@E%z JbV' ek?HJ :J`aF&&zqnjԟ#kT'suaHoDBl./ilC>JME1?4Zؼ#ΎĻ+O6{)5@bu2h[~, =rhR zMs1Ҹ^iLNS"O_}qFuPa56!wC&@$3 b14ņ^Aie0{ lö]ܚurz4oCP䁔}$m=h QQw@]S}0ThR ?P`v~Pbbǰpe牕8fGZak֦%Fcc¹ }kHIN~iB >k=4t0 ؈L'2ON h3hckR=Zƭ?Wb{&'^||*w(%_b'_7(i\DؠodžTޠank¿͌.gGwbϫh\n]-*0oӣx53Bn2ڴ<'>Y*#>(ʕ2U+k cBO|vgKp&ha5M[f:hK|@3BP s 5yϗx z/>oe(2xVM!wAU(ho,b!2^RWD?=Y.h 4)K tv5s{)žccY} ~͔DŇK|N p,hapPh#, 9%_ GYg7p|{*rn ܨBUs&'fg8j`9|?h4}R&ж0/t)йtTXP;RӇ6=B0]P]`ܢc‚Щ|Y`h+IxY S+:S=Y_I />|BNhe9a}8':`E9Y}_sJ7}y(iZ e0vOIguDCkZ xѽV+:7+땬vb6N?̃G]]9Ed'Q;v~Y $Ĺ%n9*jh,ex? _7)}?D V=fD}Nm6'g\>">c1 RDj/ Vϧw(UM!Rb4(wiP@]V:?}.$4$37'>lYMy)7/<_i S{Ԕ-#LM? hvh/T5 ">1O81!}>h%qg2O y( pō>gz~^l>?ž9Cs'z/:P:]#1<3&QGh>ؙm |kMxgkM>4{Ć ,WTCRd ΨDvKEPoMY&֊fҞѝ #l| MJ8/ <;CļaߠΡz&-_[ҺRC!kV>/W8[b;%G} ĄX]]'-vn4 =D2IW8JڃR [N_?_-G~Ǘ&~a6mQBkcv:@{hSgP;.<^_Jk!ؙxMƳǔCgkDw]mGsƄ>Z=Ӯ!x*6.P]9Xl< ҺbՇ:pәE޵O8E{0kcH!. Ok)NrB~!ΕZs+ҡYf6_$Zϥ_}eﵛOƞ?[= c4tՒ-'JׯR !zyҚαE+QJpF4ʅ @u7/{;}laƔ,ODZ3iDd+#'9dkH5s>B e޾crT|~^T@gڨפjՂتu97'Ǩ\kM !zt7n:prOZqa)~eו,oLs)[>Wc\yΎD?B/̫{M™'4 ,MdTc(_5E'Wk]r|}i}oyzsAF!PAE9լ/}/YRٹb5PO>x=OҪVY$~Wkko2=u7{B? k%?pW-[\y3;EϬPxAs) sy$?߽wwWC#F6#D{{6 Gr}= ~W=2CzB#4ۦ]yfY6M3Bw }\_*tI:|SMCw{+usWvzz4{3/?sgG}Vv}hy੽AF74Ӻ޻%He^ ,^k~bd{ M=|t=+p8p8p8p8p8p8p8p8p8p8p8p8p8p8p8p8p8d9{ mkBT4 xWWYi>ӾԒ{0 $@! # fvL>ϟH"3MSi*~7F Na&ᩮp5ffUb/+\pʼnW?ʞ/Ό$­lZa߇/SJ?73E~٥!_V[M=.HީZ­ٰ 2 }!3k{_$X_hFsR{ 9Q.@ImN^X֞5{jRRjڋEdz > ϗ@uKz鄘 ۓK?|7\(*'kϗ1o.kj]! >)'k Vǿ7zy?wHv',kpFu?4xzCzд[:%kAJ;x'xAtlsc8=_msN,d~4 3z8hzZ`|ȬA/ȵSGz*l܄ kJf4.B9| *׹5˓λX"LZ/:}VwA^[Ƙ{齞sհxx*ϒẂ?^(%QPf_xI]nl'Dqb4]Y[YjeF`M?>0ߊaϹL0~ W Q7 8+낈47Th, 5&Rk˟Y0{ukaw!^P<A׀{E辛kV=}z󉽟/ 5ﯶ:6l?_.v-( FI/!/UBaUH.C~7Opܓ*1{k/^ `T鄡YAn 2Am7p3>-nd.ocɂ/{oEb%NPg+&+0\$'G]S3nqkSg9l%Of} 4Ś!p9Iڎݿhڼ[vZ3̐]O;Mߘ?IcFZbns^lǜk8t;ˤm?_^AX!ego@?M߸I ugh5o?Oq%9B>nc`G̿9L{Il3}=MDr*晘?ۡɄN^ ~Ȗ]x,eTBXHHu; q&??Z#SY-S&?:8XF|ml,ϗra~d,0_ƾ]<Жs=͇Og5HGu8rΈ7DI,/$`N-gOY-ls  _EJ)_٘afI0<;R ;"9ޘQce wH q<70E_xhs`/,+sM"aW@8jaO?:Of[?Ix dㅍ-F 1]ʭ<{`|W%S1l)I'X1F^ءt`$XKJyrg{4Nquy!?-~ wdO 7ǘ[ #(b)cF#|3ɟ AaRm7_\}*s'?d>2r~odPB3t3L礉0.#ƿOzs$x:1\3rr(2)瀩,}F!k_ò$aJpceI| lˮZw u O6($X,SuIb^vO`ݗTs#Ugֲ?J=T%s>_ d*oS"#4{H}fc>քsssMT?T2 3_3 OҠ~2Px&_1E> U8V˲H8[p{eG3. Z3-_*x,h i~NjPN~+!d;kˍ?<_̆'O p3:?R7{CI^p{H!O?M1:@*&ad\_'Ic- >ֈf+UTP5" _BKgψ~ ψ0>)=xQ&8@wCk̍TSc{qu|Ÿ&i$<-0Y/k:TŅ/IqY6qWY^."@b7:rN{ ‡?4I\?r柵DP"d@H'R"|15i:7ELlIORQ2F}U/ڤۀq=,*)@\ڧn~baޒRo[SH_q%Vd>vLl)(ck0k 7 ʮgˑPws 7Sk%k\y Qn?(?$Z}e>jWBugM'd~`?5V.MT#)m?JΔ=I9:  e,Y9_2K((wGJ?,EoLd Qf<#}AZH`mDH3w4+ʉ_9&?;|'Tn)G\C=>_lXscJ}jq M {Y#ݙy3~B д #i>RG凴` H\7~_*ke$uAΤ@'M?Uk~pe4Lb1(hgXߨis=#/BAv(>="r(]S#,WUkノ%xjYQ~@5   4}^P/o ch{AƢ/ƿcx ^?3?-b,h Ѹ8m3 GcC { yz8@fJKv?vp.]Ov9cp1/Ke(!6!=".OKXlw9O!uS.̟7"bf 5J!nk5Ps :bå</!7l;,}  9%aIާNs(}΁]6 x=CGkXN9p 3tr w8M`-|Q:gܸf6`sĈg 6>\>6Eg͸GiiH9S9;.aw06A 6>Cd7gFk A dc.,XT3f=v8 JG;pٵ3*B S$$/ɃoΔ5w5Pl=e0oF @: (Ead^K2CaN{%xP♉9##SwdzJNǹg>u}lö#fBp@XyܣЖӗ{JNs icWƟ);OzZ6&2kzFRr"ϳ̱0oKxe@Z>=6%,Aq^FƻRud1$wЃ^7nHE 6{ΛYbeXH_P5wM9}cgX!o' W_ kR"#B齣)s".8K4]BVZ }r%fr=`4x";<^oQ.D?j:1Z1">[LvVe3k VbrDžIplߗ1(6 XnwjOZ68K&tYOJ _29!޸t[tO~XG4,H`yjD5-0M%:SF>a~~XyQnp9`bMcIslٌ `_s$)]e}dlW z NAl-Xj?Kƀbp3j\oM_+:%#Kef Ηu y3~,vW|SM gʹCTWϨ{㦄u Gsu=cfK{aui;{ !VףQ Wh3 K^j?'ale3\+$G?[69&uY#ϋrPS&+ʖcoc}>[$i[$L;Íy\k5QWSіYس@sT<(gU1g,$y_XLp!zTc1[kwF5?E>Mf)cɳr|YHSO*ep ‘mN)y~;J 81ˬ9@5Oc&%-i?bO8g⹂1>N#psl{ 07߿Ǚw|Ά$| jvI<{iyJSJ68W(K pt#?D#=E(לg*/ɷ}jĒZHI_ P<8L?kXJ=¸B[ }x~ u~DY6%fI|69O7h(<瀴0>Δ|eo ׉~N<$sn _Rz*䀱x9@igAv%z6svѿU X 'lQ$Q"*>+D1g T3'u'$~x"Ω@R Hݏ\a'E4GϮzZGy?J!a"%1ƭ 19?qIK:]8\:O7Z\BHOK:U+ӓG_=Wx4ɿy;vB͸yj$8J]ţ*yS|Xh=PSSoQC7S k0kR'gYu }tݏu}o)5@d!kf&CWG)>5]3}6Z<| vXJetMw3OwS6_CG0/%~ X513U,7tr?i_>X?/;jg3_x9c'v?pz-K'XKG?'"3"Y ^o7H:jvv>}d.F?g۬n[N:ݿ ɞaGQYa!^ꎎs̍_XA_rF[r>A+_pxځe8׿OOOCzǏ}yϹc)ߐ=!/H|W*~]r;rT˼4YJ}i̻!R)eyݏsnts(v.ŋ`ooAAA֬¿>ʿh<}`>^Lhy7:p?[&}obl·Z.=ECku M%8}CF1']VjiV/#ɴJw:p|`04&[}.['M-YE﮶Q=9GUc>3D''iuJ>mpYY{g9YƲ:l5ngbRŞO~Qkh;Ks L$<,N\\}{>>\?%!stKsTy\,熲 70GPr(Iug/<oiNdae7d_F71l-90<A@5};tP*V vąU80'ph]`hapFݟ O[^0mz@< =CH'.z( w^ʃAoB¶Yŀ"q'@a/A .A0;F$BѤ74,[е{Z,~cWpu#&FlpYt=ƻaꀏrҀp -T^Pğ oC@$ڠz6SWulxJp"ZCLrO]Waв⿽"# b]&Mo@"vC洗`,P2 UP{=n9I,Iʿ /%kRwI/2M&}PHP1GQMyC^=O!^!Z> z+|8{_oV`B[?Dl pQD84D=pהW Qi\ )Ӻ#=A:5!"Ԭ]I<(] >1 }ʓo%$g41\&i_11;CO8&iBcZ\P/kY"9-<5b"Ddz-itDY#vո4p*n bq^82I/`UEpφ+~3\WItpv8֓X?`=ya$Ak俓%ĸBeavX<}!³ʙzD]P0!A$I mX/[sqx7sUһ ^S2XZ"sM fHIvD9քX ސ=xm`[G~ӹIч6X:˽7ijjs'mp`( 'YIf#wYϋ"Ioͯ2G|@uKtpپ:@q+팽:z9'}b8Pɚ ~EN9#nN?KPYhLok ܘ_N WJ$d_\8/'ѧe&# cL1w ])3/K?תDF1xYwk('d0{DKdCu"_@=>e!܋dq?T @Axo43b]y5` ~v eFv@՝t.U<U{doO8,?{OolI(Ib :'NEiC$gi]!^ X:k{lC^Px1*.eCH4^)PDc<KkKO'xȌu`aHkS@?4ֆY|!mR',u!b#\H7 ^( O82$_ #RM#F]!5{:/_Y8T!5uah 98UPD٢cK*sE?}ŔHO1Cg%ఄt?dJ] 0r6K5gl):o%)ԏeڸ3I=,{!!{+Pt0 μ?Nk%0uWJ@~O! $(j뚡l9wo{T Kֽoy[LLK ;t8~z#E5P^,;EX ;GQTk 0qɿ yC4gO{ƔH]q/ ߄C!@й•@.*zFb{K Mv qFv`t Ά)OXk"C,1Br$X, A`3SNHV{GsATaG`wͩ(5#wo+5 <2}d, R<8c%􊖵W]r"':WfVJ !L\9B{Iܞ0Y(`}s3Hn_L3*xnUvp'xA]`W1R*RCr@ցzu 싀"@{9T3@`EBě>Y>.$G|Pwl (=g7l -DC'Yn$=U)s_IgČJ~Bݸ7iq+tꡮ\W oջ׊lB,т X+(00y׸b=z7bz@@(7QP ǬQfx' ӈނ->S2+X?[x^d7ۍ^ {K(cξ*66-$oQFwo,=X߳%x&%ߵ^ O !7H [ 4ikl%9uBy_S]l fJw/i5%Lư8{p% ))(L^o:W{w*]{}K2H,P. q q^?2A~@?aWW#p=/4 <xy 54O Y^sԾR)ҳY~G'IN)*+\C^u)w#ٞ gwwu lp`&p,YFRxoy mw=٭8o$M6y&ptshW A&kBa7}ZgARhA 9Ķm[ =9")t!{};*"߽~!Wub~w}4'(5WT&R & \=L߀ނP͈Ë:A^xx[".ޛ :L{kLuPstL$&BXN׏\:wj]O<[zg ظ]^p^H|!w>citDG`~e}sDg8̺1"փN~߀^k4&H֛#҈tF`uWM`x/.ZaH୼v%7{ixg9,ϯ?"bn=3ixA ܝ<ֺu[–-[(🝝 ; s 8O%P^zhڗ[hW.E\̆l8ܞ]R U1PJ~~V~Vdf?{396UVN` pHLXTK3m05ˡao"ACkq?{(LL ŸF8]$NuVSwg@2"]!<`wUZp&tBkE8;?քw}]{Wsjij+Zky/͗U>Hֆw~Φi7jǝfгq_\dw>Z7gc'U>h߉2Q؂Wc~VE/3̮ NB|/'kc31|$ n / (fZZqq[ϦAZXG׫U^Dd-D&՟)xl(~Rw"C' m ¾zU;w}mjSE01,]=:Mub5u6 MWeޕ뿍wשͷ=W.[wRD_-3s\kzѥNSg2TZTο%X7=x<+M|FjG5(gľ{?Km4.*õ ZώmxTkjÆ]jC /7mx:7&[6< L=hy2sfcr5p)_u$Iwe]PBQkWKTYlM5[ UoKܟT?8>^)+^;6"~ jJmt+5zK Sg/W\g4Fz vg [WU?gϬͱ$g75}oCDGMhnGhc<-5l;L5wAL>#(wP68ەn׍8fz1 gP/j?.-oN1iHpALo{L3%G\ɡ^g"?>m贈;|ړ foBﳞ_+gL%(TYO VT073y${jz`^?W'ܙHPF)p-Ο(]$K{B;!{b5{5N)jx?mU^3 "ڛ3M{'Ŀ]l@_xi3! 4O3<6͜1kp࣭MԃwВ>O?3yDoL'm͞s06&CfA(}XonɮCߛio.{>ՃO#gC $6ǿ\|!8ܒ`_7>v3l\7qj5YaĿö;Dxp lуo$d@y90 ~`n,mkBT?mxYPW3IL^f&3&5UIJȾ/ 5 jdQY\@M .ohɘ$Fb&Ij|jk/4tn>^w9{s( BP( BP( BP( BP( BP( BP( BP( WwxOIa7]113.8a⁖­M12Eǫ6 m\G6-aGR[#SO%Nۃbyk&lːƺ0^殚—wo3W=޴m cä3rڃxA';2t Y-+?Xo&mKe^ "'iϵA2W#[pyvIU?=,  C{Hq5/196Xۀ6(dtBqMhC'ʺ䋲IBaE)Jg? q6$8[(.jg~;>$mK`2G"Pt G!hx>,BVw$b?Y`} *QjNQe憝!x6ļ燤XAoNQѨ?ڴ>] egyFy,E{Hj1T9//8+y=CPN?%@^w{GIkoIOcꟷsALs@+1srG9j+m`84#1X_ B$~"&\=ھۤXP+Pˁ6?h.>9#|/w?i-p,gr4C/':&Gd NE84́c7Fbk˜Q'IYP=waXSu?[hL$x Ԥ|n}9i3ƎT7o5Ψ@x L&c VLچ̼/q0彶AXgsRj'`Gڎ^øo@?D˿Gu %mKsd:&큭9cr?ͯmf[˲HYud:?&mOsS~> q #Go]O;GqerI+Dcl7eJH@Їʖzٽ3DJ]Z⿔&!J% ` k>5 OmS~ yxkK]8{S>XH1\gtx,P}4$B1 ?owi: r ue^ket} UW׀y(B$!E`P-Mh2}V{.J=<:֑kBLӽDM8CPr%7 p]-ӆ‘M8߂M@_\\VC%Ҷ6E? C`A6 z)bf$OX>+FABF#n\=ÎIܔ@]ՕpԄ؁Q>z r8DƂ|)i WCMD_ }~ ۘ{˅])TC__d8*zjM|C.A/nImsԟSwXu){GֲyOl#xU4^4ZYzSrtLN!vJ?iy&|c~CN߳y j II@\`d^LaC#=aǙc!!q/On}"P!BBܯy8ab@|:sµ \zI7`l  {Pw8!/֭+ܱX%-GImleH%Y{3u݃}r,Шpעb>0;z'OwTW7\ s&Ub|ElK؋Þ-ŽíYO6TK±F3x&&fvsLJg 꿉,`|,S~%i=/'vgkl!C#]H]GZˁ52)e\&|{l?BG0 -I'2<-G&}N'550|ں_*TT`so`ox͛xѭ[Xx{w>U] y脭Pm/ݾpx\?cpFxۊO|;^7_|xϠǎV++/JM[6/:;0{R'#O9Gx-W=H6UZ<߉b<\]Hf&D1Ϛ=B;QǚO/H'HT-[H'Z6lY?ԃI'pӓ6]Z=QP7k?;2BP( BP( BB" mkBTWx흍8 FSHI!)$FRHnw HYx3ꇤsaaaaxIǏ'U{o_ھgW9 o'GW {>~Jlo߾)*/N\ϱov[iZ_ձaJΝ/:6O- 92b?Tlk%?_21B sY5>:>c=1Ow y^- ڶ,XzusM#גU]>H_yYv!ۉ_mi Rus]Xm_g)YY)m]y,m z1aaaxEߓGקo/Y\k6xjgH|yu.\aæM&wk#ϐ$?]Mo\Ⱦ,/ڥQ@~6s?)}, l gX #vQg Bٙ^uのuhm?}{].~}v_J;xogJY]޳@.)oqC?}>@Xߘ'-(W? źvƔOʙRv[K?[A}?-wmՑ}g\=c}M ggg DŽ-B^k_g?F? v0||؎=ǧHPgs/hؑI t~{n^}ZyD5XWvO)"c0vY Z|~_%/,p\ɹyΰZ/;/xs_9?Pܯ5ݻ\[y|č8gʱL{? 0 0 _k3>z_\S |<)b|7aaaxn.ta?l^Cvkؽ#~e)3<3^kdlc&jK+o"e<.ʞ`^(3zu l+6v<ï k7]/lc[`On}򚄫 G뎱zt^v2)?;Wmr5ocIz?Ozx{&!ez."ѯ 1Gg{+ҏlw<=}GݽFƨ^)zIpG K֜{{e G12ۭqiumf>.}~a? 0 0 [u+7Svq֭y΅ ?ނ}XwŶv?ߩDZۓ-q/?߳=<~#>Fk"qzrQo 9r,nY[;o:)@-`ק-7({߯S@µK9֠ɸ>:n3 _[_*mtcmC>qSL=<6;ǫsaaa{xˌ\ފpx?0׋#5zяc]x^l򼠕(f:~٣^lin59W~\;?vn6erUbS~v^U O7O(|;+SG4|?f*?rW~2oNٟS9~daևmH6mX[J~s.ym4ٶO|Bd/b5ɿyU? 0 0 0 0 0 0 0.P~*1@G\⟿KrKXs2(ߥ纎J8'>X@▼QQbqwx b)_K|v 1M6kee-2Ǜ59?K^E~9ϱQﱮYF8N?~;:=J<-tĒyNAgC \NXKs)'^Kg\~2}6}Գ)n]Or^j~"{p29w6/.z-v:+M{WJYZ굢`% Ҥl9ힶկ#OUz+U?;sd~vND7*.Y+v:ye;8}~|+ÑޅN9}{Bƞ#txխsXɿkSV/uJ=o G<ջL'L:D]6jfgLz/+ؽ[{rCMYq~[{yy czA;w9zszWHVax3 %1mkBTax}W\izcc홞 s9s Y $IDrέy w]]A {;[[[MϞ=_>B7^CCC L9'T@u~,+ ȇ}PS}ZYmoo7t駟6o6'O\"畄,/QEP[]&uzzz6ݸq?oK?|azz:kk$8oi, PYLM9{69bTKK ۦVCM&uE03q}>yo^Nhk=,vuwq=8zWCAb2 M(sJYxtfÑY gkllt$??|_3ve$8>n,j* a^]V˹?8DtwA]:I3\<;/LtAZv8D' 4d.r;p  +p ;`c^[`g,`F}(A]39Gwl|fQ'nYx,-6Cg/9S i_ ~NAcFs?~]Vݏ9;sy~l.]hCPݔ!^Ws6^'[$'LHޭ~8yBXm4d1^^XX@_hڬ ^iXX8 -~G0gkJ^/b}"m73  [)nH2tc1d%fTfU` QGl -ĐFBaM Zmmmk]?|{rY]#Nz{9.yCΑᇑ0F:,v{X"A#wUq&Z&T.4M@#ADϸCP8 SNPA;k1^߿]Rr>ɑrItљs oPzɋP:%8GN#&_Tn%b'`y<0fsz#&Dqޞ0(JQdwvF{`װ {y9sdꚳhmTW5@W(0GC m ͻu>0 Jg ^H|kA08.?b0kG!a bOZBDNAs0y)o>&^+aT+ubTџ:&:0ey;`yw.cu^t3Gmry"k*so@I_js"7hU*G oϜ9yȑk5$^9 )5]y| fU|]R)6mrNmyӎDCh5W|aEO(857c:sg YK/?~\MMU}62yZ_v[St\h5j7 Bcu=q;IoEѰ{2!yb0up꜃.L>b&?4rBN翷w!xo61A{^[y5Ko:s F hV+mk^ WΩkvn2Oݧ`Rs@^ac+mb11"]1N|w $GBp{EЙ.BMTZsp.V a[lJ6S ~  քk<|P:^c, ~kKxM|fDjym4O,/5>QQ/9|ˍ@y:nQt91yGD1?os?@eTv+~1fn[+jåpzi hb&ⰵC }g"2|M?Od^ ?|t9@d{ <N7Z&38!ǬvQʖ XjkI6&^3`M-޻[U?ZXk>o$,$9g>Q9S"69Qf0mһ\ gdI/"Gy k\:8iZ8"A!_fCoBԜQ^h~ ݠ7 E3:H- 4F߿M ӉsbVm'B~#] *Rd)((-Zr#ܗP+BQVg.7$Sc6WÚY` Ğ3.Z:Uf`>>8"rꃭg+b5Zx3 L~`-=3¸:Z&S4 Y<]ĠjN|2sɧܔr*k|^Uqo^>'j#`h ;#/^ r=|cʄA*cO)՞QZe jbq9}]xǸ(^smFts) y9zww (JT(\$_5EQ/ g'ܟ.y~"k vBXv.=G='ud|뢂X(+IL8޼7R hH1i/V_p~^  E@bNQ-&=E9㽿1x2PlZrnkHuzjZF]q sa \#qn{h 幇Y2~͐95w=X=s51Ct[t[Pwlf|W yZzd,.3-^rinSVŽK6ABRcϵ&/N#*?ķGHrϯZ}&׭:/[vk9$[xC<GR{uG᧯ז}W2{k%fC߇=VQo LoܼYCkr`q:[Cz'31{Ur;O=Q;V}0Lkϣ?o⣞w"ޫ=k66P7޸;'tN 2&{c9 {Z}ݰ|ވ5`Hqu4 S}8 b,cV6Ru;LI|*Ҹ㱹F NƷN;}4W_Nw.3m.fCW>?sN7p>n3JGt}cDg(7r&'L㉍RbSd]pn(|z}lj D^xt_ DbVeuWwEyNg8d{ $șʹBw HI=^G`|bvùֆ>*B`+nio}=;:._K'W=fvw=`~;"~ R !_g1T>:8'Q;!%أJNZg*\ߴj9ј7~ְ7kj#ﯟLn8Sp{ fzrks|o)3PlsS(R<>ǭVmҊ_'abO/ruʆGOn8k_';ayRGX1&܂;*:6*=kiz}/F= }zS{f7|~nnKSJsO\'s}{ռL@kq>R]8SiV:C=es^?8m0`q΍ҘT{dό: 4О:|g^s7|s__|~ҏ~=ZQ6>,"~v&*n36WaΕg UOu<-~ =Kµ*&~6Lz&8 o9y5ǜ}sY~]V;Ѹ`^H>=)wIq>$Yrx߿'w1 xYܣSm{Qw\:B[L<{yp*jܧǒ}=+;38_~Ome?訕^ۭB'XmM7s̯}%Czh̅Ve80ޖ\#<|E>3&~>.ꪟg:qR{ZCd{Oy|+ #>@!'Cm?8Bgy0?^Tpnwn%l̘6X=ܷ {9.}8CV],t`Z5]m7Fnx~Py¼.}FP.Gw~ͣLݿye>{s~&+$J ?3v_?35ߗOMwĘ\5}]l|owńBjJ44(-Ε< ΁L/{E){EG?eNqke՟cgs/-poOƎڏ;~~=[`.DHJ$(ޛ ][G8O;pV>憴G{Kd,/Y ~jmiB`B>y1EY0/cCE+8`Ӷ{|Iޮ ]|'>+s^^r/yo. }u;Wu"ϣo|u}\hKV)!_ޯWpw.,09Ҷ7bݏ=}s"H+ǵu 1$*p֓{uekF?:\XyE9ͅ"cOl~wo.n,@}E2dG<‡<}_9l܁x:o'\@D5$xpأ^17 ~i ΎP~OK9FˣGY[=q<!2O8zXvޢHw;O8%Dv%.+ܳm ; ;Uѓ_y0~= P{*gY'n^Y~zB3i˚ 6˚-cZsS9Lǻ8>pO>[r⬞~ftގŚgX={I)DLJ] 2k%H ZqoEO.տ{1 eLyw{.JQa8Ckܩ SBN<3ؓ-ѡnj<O5r$.OW?8`=6!rm} Ymc'l5b/2S ;]!vco+mtFs/I~ /?q?~P]n2Ԗ{ۑUu?zt>yxtȯ.ɎLm/Kغ@OvaahOwIʨݣ NW߾ƀٻLJ0S-ҙvg iM)lqZͿ\| s; 4+vC^_nhdT6pt3 /Ksgi5=w\3֡7Js}#=V#w)xw=_~qg/?7N>ZfvL`gf^XO97V :!1gC>0d]U{6;Uo+sw&om:oBhO" v3e*0{'_?Wt>Kߓw;-@ce2d< V_u?s5zCP`$'F@uY:̌P?CE~LVϾP?W5C.~ɺ/Ԏ7쇎 #ԛ Nг^͊c`iLĹ9;ӻӵE9ϯjy"yxT""$[cb'Ο~R6MXу吔䟿~y.GG朏W&t wPߩ |t_d'g'+W *֖?]>}Y_ƻ \轊Rc y9-1}~!Z_+_`ډkJ^uy<?g} ]#5SvR.qK=+Of8oӊ}2;|rw Yuh kS}=N}!>^)8@y}$fц W:/y ;QvB"-l^.- @FG.pyV/'\5gL[WųP6׵;31Q!j=|n: !inz+;WD[,؁,p{Y V;:@ 4ъ1aa?!@V0c~9?2v^~@8l0oE~.`&gz+b@~wh+v?Zo߳{_OE:`ro}j0FxGyG(f"]e@@>usSs-֬FN0kUmH.{$#<^.FWe I#(hY@}@~ ;qGud5ҀӅa 2-`>j_~V5@8dްϵ}.2#oY =#<@ x~ZApN~*V1d$:I =qJv+ L6CQ;ʿ:?gpTlpû֒C Xy(sĴlyG R4ߛ>Y@ޑM~;+:t: Ҩ;oMጏO+¨=M~K+`Fn+`_ִ_]9/G?#s۔[@OCh1լaAlBn'sK?;d&:BfxH< z8cIvgH>dG_D#I'bMBp5tw[PO `EdZ}3%@ D,+Atwq3 _??};z^ J`T6%_Hd," C-k uTg\!_s>b/ q'P_1at0E/zC2qrK:K<w0Sa/7>g~cƞphٓ [/ q"2j D/BOR[!m+DوvπCv@ w.&h觜BxgeL9AP9c9@Dr8|8'}#p&b,1H[4\mq5?=3c! 7;=dA6.'_i:oІEsGg3D:@UML7Us`*kp? ١. ==bWxW97Z䩲74o b!Qb uIϘ| ?6`!uԑ揬V0DcaD5|')wfq"ǿu&:`=qgw ;C9s=aBTG wKưas " "- |\wOq0Ҟ-*~6:`@,)ptj )/ARN)=Gڞ^s* pI4]HKRrθט }5g;E;>λr,8JY>DʰV[* ?BP WrD|J!,(s73\VZ8I7h@/zExP;~lz@b(-zP}wW{&ӽWx&vICsغ/b PO-:(yK np#g9:⫥3@ >]d&~.EOzݑ~ZC @W\^7\`j:H,@7"Dj @θ+Jq]HN~I1J_2$< >ۙV.z6U?TGg՚mkBTexSyŽۺ{E 2*9 9HCCR1$# P1]DQqw]u_x8'0COOQfzy~ MIm@X xgXj0wXES1^Jk zm'Il[[=z\s.x7_ẀUB1Kxs42=0xۉRi-'<@LNrAc6Fvda??,ŬړIY1 ό'cEWsOR%h3/ː\͎v$ɛwXq5){_Åg)K΁<\T YR&v\`׳6p3 CcXP!UQ5'Tyf.]MNHY[/0)tft盓_kWAIpKiFLxﴞh{YND?"O@ۣ$hsQt'*@Lސ/sa6zƈk]rkG3i>8o(/-` Lg1*4XOɿ?$uaPz7 f"#li.c RޥSgA&hyP6Ho| ]G{ B#|_˙"f osl?߬R>PZ碡a*P}!'#+)Oؽމr\xs̟1X߸| _HE&2 Pr#[3cFq RUOԏ9XQ||=׆>?򜍘_2 ;.{PuWl;2:Kx|,@YS#?dSU3 X]^:̀*׿*yÕS,t>!TM3CvwOoz|PpV Pd]ĠaU-$ΐCL?$<Q!DCIF>f>6 `{ntL{Qp~4$yA?.?QBhuZI@5/΀'+/g_}F/0?ymyC+GdN%43R@xJ#'<3TOJ$f/b׵*މջI{FKNK:nCP|nhwqh8t^er/9Sef?/М!=w]}|?R+g92Ц"2Ƚ"1ǡJ3=QХ ^l=B d(U>GݍH8@pւljx97}e4Q6k3$ٷȂ>!_jG=*mY+Gr2$jKT?{AdOf2l~YڡQ8A?8L֓N;H9A֖[O"(^BEy`*0+ \$6$Lv"A=`u6sT_|,nK[)먰84Φ_ x6XW5xUAX Dq-ݞ{Gc#xuzWM_ܧ 1<g.+Þh,NE@0NVܫrCW 3h=ҢJF\F5emkfC62@^WCᏉ[F̤q[,^^[?cFJ (sO@ ,BDe d%5"Yq?Vvfmdy'sOBo;4pD1 6 =j$Z2 vKwk_5_AQxm? #{6d*2qr)ҠǖOTjy=v|}ÃMeʀ8ꁾg:b+{z>NEpD2 ΁89ŸUXM Q`1d9&}5]ְlyuY]`#TޫԽK7@Jd?Q /]Ǖ*s63iׁwFk>H##οtOP̹GU_B9p-}.s4)eVPF3*w*8j;PTSBWp ,Kͱ˝~b yWX+9Ngq K8w"sJΥ|WT9w+%]2aҟak-M"f-K2-%se Q CmZlb¶m~9๭fit6e~z_s>7}#,KN=v(s`鵆-U֜M6-%, οs7 gœ=NvL&iσ!{P/",cTr]'?+ղ]fj X{Yp6g1f9t{%us΁VMx̻A{u4^߰{13/ Nd%9@}I_VC}T2ԏPf]?.3 _$ L?Os2D) *mJvb3Y9DGD9ҲHJ+ϧCׁX2׷ZRsYv*.}؂هw)cs-p`])w訍kL-~$ߥc{XF_9Oga$9St^mS[9 pR%pY)p e9,sUV>< 8-OƊ`wve0~e'\nMS_?K>a֑u>olAK?.כ8_߱ph KbO? p οa7l8 t8sbJ]sٿAkX}"zs{"|vt?vA},UArhAX־fl6rYNqI^܍sOa%30 {}g/Y/m x0c iǺe%|/V}xZ^?Hs`և9tGx8:_Y| f_}| ZJo-c* %mkBTxO[7m&MI͓ ?0y7!Đ )y$@k$M!i6-yd/iԩUi-ZMVm/|}ϵ %,{9k]JmN^Vcsj(SruKj*NUB٤ksB&e5$ru/Tls +"*սU{BE=]YJҽ?v::`-,SF?@s{Y;ZT]r{Cڮ"rO~єepI>2mCw3'5*[?vhϟ \3~L |_Q{H=*ӆ=*Ugꬬ,S;!\Kuլ=3N_w$l){GW{gOД b YwN"saeO)5Ky~ eiߓy( `T?5I s箛>b'ݵ&wrO|YGxއ\TWbn0y}U4\6O7_pꭉ_,w~sZoMtD]I]qeOLckYKu0Oy4{7$}+8 {u:FHnRc <>^mNz4߶ROZ[T&rO3T羸ˢ%@ٰ:.wJk@͙yk pvr뼒?>{{8ZX;YTyOkJd}R cU >-}?fq}v^H4P5Cu_:R0z !{ mT?YDzOhNׄ'3{g gfo ջ[SjTW{k?UZ/0?(}4I;(J#=]d}8'qP\Bj>ψ ے$٭}ŊY'/s2TP0*= Z+s%ĜBM+?N ߣ}9V[:i߿#u! 7*!s pxuDP_{un]'5;D{zjw6 bϚENK˫ {z/o׭Uyǟ5 6_xrO9OOX [ڡr<:}]~~3Y{5mPu5|״HݗK=<~-72 MP/aL6}kv~> yF>Zo\XS~zc }{}sjwz9H>_}* 9\*{_uk. Nʎkha`5ެ|cYu,"ydpM%{0#^,.齼 7wճX6g!cL Yҗu83HȸKM)gtd~Ys2W>qm{q(ā|.ϓ:!Qtt/"O\'Afo{]9]%=`v9oE}:?x/c˽?igr֛PxPܘG~z\ '@s9R>nJwW-h[1]J?Y bۛ!G7Uo/g=Ky'!ͿCP}4g4#gH-oyr6Snq.e5id3[sZL.˪q?z:Η|2$HVnN/if\b=v0yU|H{9Ak>sux-7dŃ+ⷯ9o &Z;7Tqh_ [Vꦧ`)X=Wϩɳ8T=h9wJHڽk]cg}Dɹ#\q+<;|'Y_cަ/O5 ptMǁgg.;o4?{Sy_֟_|!х<KֿAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA W[mkBTx흍) q ĉ8D^>׻gI@XjjgiЃ`0 `0 ?ϟ|:seQ3|ӧO|:2|.};7eGFO6_Qv]T]^ˮg{>pjzkuo{yye?{-x/ D:3D&򈼹e^Hyi#/OGzϪ߯_~ :sMe#M3Y#=2 QЙ[\s=E8}E>GȩT ڲTg-}VfoSVwzV}./>~!?U1<#}=F[ ~QڋBN..+푹^edLo+[\-k dW(}6q$#?z6Bөi?L7!3O_Q}Пuo[=tkȋM!'}/Ƈdr2_Cﲨ: `0 :8o=+8-4}۞cĥXdq{bUq©ήm!ƶg*ΪU\z[GA=^+ru{LV U?)V>ғ)x|Yҁgi\yi^cUo*= !TY?rfgWsʽVn*VX#=Fϫ+[F~yH\L~[O҇h5ݵTow|Sfӟ+);F;:x )/OS yUo2e)Ve3'wgGg=J^`0  ľu kU,Ksؑ5nY,bXw{ w&3QהNQev ]ƷgcH˞i{A3I8hwduwUIWq8I>+@pQşGcZ\ƪUߝ]/:3d;ɫ:gB9R|GW~w2;fzt|+i5nΟgZY|<1NyŬ|E7k?z/k><=Α}N΅>uWydʬdz `0 *\?W8GY:Dgcg< 2+'W6qn؟{ru"wU쏘~c#T?+y{Q,,^qF/Xv8.֩g3}ȸOP ~n%hUG4(_sn|W}Tg&x^c,Fѭ+ <#+}/Uw8BRh_|33!mr\7U9m({ѝpvew[xG]߱?g;,nҽow8]וb?OV=Z_#ve?vN_WrYLo;1g9pV^G~>[_vNOS3 `0Q[ veO\k^8֔v<Zbz\Opbn$~}oz3ј mK vU]^iNWA#x딫jt q :E= z%օq)CcYEqyRG-+u (K\hP'*^ء^q=m=y|Kvūe\rȊ4={W1;=ݷxp;o@>ȘT\Ԏ+C=*ɫ|GJOCW]x1.ﵠ9_Eб Vq)v(ʑ}[GwǺ{-oSdו_˞׃2;iT&w*w:g׭SOsj%Z[~_˯d֮+w]7 `0]kIu+eL]ւoA^;=GR?v쯱;<y o$N1紈=:ߥPVu< <&3KyC/4r)i=*/|Ύ^]QNН1qGw>ù{ ?Kv:A}E:_n+{u=rq͓̳]>>d}+|L01`0 leg:׺񶊝`W,3O?]\9P~[kOWiGc~)-<w.3q}'vuw$Vnv(r52S;Wk_Kϔ8B/hEՠ'9w?K;x:x<|@cϽVyc@ۖSw8Bq]=2lBe6V}eR( VeZT4ade2ޒ+nYBTqSߔ<[&=f[|szP)G}{Zׅ3n7jpWwftEw[ǽ;`l? `0 `0 `{~i`oLy>uoi\qK|}7Svu9G쯿c¾#>,jow{ՆݲL=mW2u_8دjo?kD߱mw>#}E:OۡO;y`$jwmkBTxWWaݴvӍva{z:Svڅԍ0hh4njj ˣXo|S[[[VFB;OY6ߔΦwii9 Y$P/~sﭯ3<<|L;h65'KH ;rG4uU)7Uhh{OOOZWWb\ij갹D]2훝8y*S4P)vkӡ ,ζhn>r^;j}^x9Y鐥H\Ibnn飥kvkrL4u~Wk6Ԫ G jו؟.r^;044tyMf<[!*5ޫno9˱1Ok0_]4yD^V$/nJ ˢh+Nt: gp JA&_Iw {ec@<ecP8mWi+6mG@$9M|^sɫ` 2RK~&ucAA"!#-ns"G%5_]C/D_yNCnskѴ׍ s%6=@p^³PR srN$ 8~.şX{Ah3zVw{zד̅&<뱿o'&^P=A1"HIedFx SwKV U9AA O I<>'k{% Dcu<C`_0? ۞_npҳ tK@A͉g3n [ߙ |1HA9tNỉWϖ׍n9xNč-ucWٝ׍^o3'R@ҜO7sb[ω9ZCqNucO;'n8ʜnLKOs"ٜnds" ؟F Ɗ]- 777777777777777777773A{=7 @}<-]Ӝk0ٓ`qWKY'E [a~TQ:fI 6Pgrtzop $X?RVKWs*'h+|֟?~O4_؟&ba߆_8|K?[]liJI\>V{rBunźHnrv}Yknz@C3jc2gln$sb\?v8}^7V?ѺCh+`*?qK9O'Ƚ؟.B{X!D]^7V؟؟ĐC~o}2 V;[wnxt19Tf۽?]y`}o?[ݼ7A&U6eAG8tC[͓sODWMu8#ao_3nkmׁ(kVdymkBT6x횉m0]HI!)$FR?6c>>~sm+vuՑνYu8uN?WP>1JsWiV_uKEϸ/rˆ_gKW]ױEYcl,[TYHT}xL#}A GV7^}>iҞ-i;}LJX&TP3T#ߨgJl e'=?͘ona|7>?ǐU%;/mN/IfQփz{G}?v✽3X~j{zTAO^ʰ>?sy|G)Pஶڬn[mmHOnFQ_CSS<ˋ;:}.VT_,=<:;_={=wQѠZ3n-0Pr!ߡwcσϿZ+#XfRce(1iC L]j)Yv ZđрyLg?^8Vb蝖WwQkW/? pQ424lcCH2G o+AR +:x5LᑧhM\Ud׷U->7o ޷q8(i;, pm$f+-<D3?C6ARQq;16Q f,`Yip};yQ[ ~#FK L_EcHN+/C8!<]=z3pF]wN} )VpH=eW}m/)hƱ:akFxo9~Ëe ` FĘXVf ǼQ9X@|8"tpy7S~i˚Cdm&XCCMP 8}ˉ7VÚ135zzE€X/H=!ŴGrݏP޻>Vr39[ PfE_7O(s[\_K֤e|Qpf¤QCC'K)#2i`9JS=}U'c+:c,㥈 ՞c\=3÷7wVc fpkՄq ;@"\CMGvY} ԜTuqzΞ_RG<7{$C4 b'wݐ>` \=yg9X6uß/f,4`<!|DyQU[u:9Nܠ)+q&juC,e> !˜dEv~̽Vg7U :g#i/ "FML9,kI) +pa:soт7E:C?C8]BͥixKfl6kWaߧעB=z-x s^zԈ9swڣҾ~ w&0Eh7Q^@a$M[( o5ZEg?#喓0 ^hF~^~, '?,H\eujf@Zrf5SA^澒# 3a{}Ў׀QAXdk+BGzLzYEy7W/8so۵6|{Lr3)oN {"1W`&7[i;2&j8 ,sDnU4!Ӕ5Ay#y~I>A{KeL{4 ˟f}w12eߴ ~@Z83낈R3˽?qE]y}ړ06W_-(@A3$z?L*S .Ea}dv6Sς7B ˛[8@{U-|lut>n)}b.+M:v ٗ/QA{q_Z{&JԄIn. ̑ˠ\.=}A䵫X Ji} !-B=a5X[̘VLִ=[uhm{:4EJT:F(\g ;nqsZL+AvR5 !=|="̑Ɛj#HHpBT3lkc_W& t\!S,ўL;n7О@ti8u2)Yʸ(IFMeZs1SuxІWOݛ1+뇫Ʒ/poCM h*DcTh9닙L|q߼ƣ6WrNDqwBa#LA!(7@l5#f̌נ~3f.zކ0(Йk+a>о3Mƣݟ11Tw$~q^!ʋ?³įZ 5.÷/׽&]ӫg}BS d"0Z;pr$iUxգE;oA+b@gI_7Fw)p_Y@}FOBZ tV@^-66}GpZ=ڻNtY4[OZ8v~U[ֈ1S4ާg&,Z žVʞeD1n@ŻgX;OvE/Gp<ƚSp[j-\.`8_ƇcRۗC:ʚaꪹm蹨9K_f~0mZ1^mCJܪX1zr='yk尵6`8Tro7/Kn/jp{=V ZꏹXӄou&^@NN\@͐uQq׍16O9Fw5˟݃|/lOtF.%X_I~gpMuOG߻yCk`cIg!;h-RQT|s!j,{! %qRe,pYeB4^xMk%V"2( ցñC;zƊa{?3jB 0/zɬXs2Vp=kyd7eNYsWch@QQ Гh_B=*ygYC}W%?P5\[-Ƕ~fn=EtxHe뻪ñHɳ;vjF\F9t]C1ߟ!E<{y3sꯋ^ X9ػ:݄ˍE(.LCQA*̬h8m(W:7j՘jOu19R/,5)=Fy*|jts?Q?~OC> ^@57"7;p?][ݧ9z_S?|={7^ҒL"+#1Q!xe 0=Ɲm k3EfE-߻1\eR"oק7uaCh}@X&?LN:[5o#ײATc땒}=0bvuuA72ّ֔4קFܤAҁOUP2V,VkYmn61 {mz!<̇;+1ܝq. N4Ա?|/|vΘΦQբ/I~\sJNo)zb\ ˓1=/F)d5ܳ]5Ů}*X[ʅ_?|0|}@Aץ,?gνG}ſ`׻PZ. z4XH)Ԗ:{O>O^8_P!e^_z{倰^3?O}Zav}]V-́֞I@lg?*#l/ոgfW5@ȸ/P赎|]"_oE¾eP_93zM9H2_xi4_;yb/FWUl'uyv<,j"J7;V|s)->c~០g+E4Rj=ZU_$_//./ 7Q 9uHV5؃~lE~}]^igl9wqdh,nJ&_Hu_uy'819`>Mϻa+_- +׷?!6#L5#JsWp[̇ }}߹+: ^y$z@ӎ][ziiiD΋j9:,Ex3bW|Nq^S|}xw6=㟾/Ht&GwiY{0_0_? ']̅ F8JQ}=3uO7C'sCO(O4I_se:şX}.[> K+Ơ=wa _֊ͻWб}fDo`Y!uz.V'o=wFi_~FT}H⻆*_B~jA`9 rY&ו r_|, \-񿙯0_P.P#Y 7Дȿz/J T ߈8 l} *_W&?3׫?Vu WO8$4QWO 3wgF|׫B'L][{j_I yej~_~@ſAAsc6"DUa`<(WJ>pg^> ?ޯW'~*.GO!*qa5 3ɳY3Syg]EP]Sݧe'=rݧe4}KW1ѧ:7)/ ToCY' $oC,wLy_e࿵:Ff nfoS7uvn2s_厩~Ӕsl8U+ €jI.+{^|QSu5'~d`7磸>ϲyYk Җkenw8i7s1ee9t5&|P^ǘWϸh~뇫-qqr~dvw8@>#_q%*mkBTx}+(H,"H$"#X$,QԈZs>U{ ..T}6ڳ-F`p]k߅~b  О$wݓٱ|sCoA+q3lOx@(0a+? T,_7s\Ϙ^Bl1)C+k(FyN"8dPC_9>O0&l4Im+nwGrŰ)/tihf ѸX>E)<,6s45zb?J\<OM%O#(76:= ӋYAƒH Ls6MXBcX&ǘJte. 3.je(??Lj=%wZizFTx$kP8Em jAOހ>~؆B9 ֤8UKCvjbL Cy ;mj P. DkwUE€3ܨ8xUJs\ɟ+;}sFQ(KIXݛƨ 1 +KdX];Jģcx$D׷X`i @l̏rnm$^9΄zBGϞQ=nfkDe; <a>,⢞jk0B[p($Ǡp4 nq`XƓ vϵ.xHnorJ5Hu뇗 f a[Z:>36[g RL؍?( &w.7C#~B{] UW 71jk~ecGrD.=K@WDZM0倐0\xvqNZ ># BE )&yA}t?B Ym(WIpɱ |2+\2 )l8tl@Z.Be񅋍RSƃm>dIl'N adĢG3%#)?$s _5=YBR#-k"qGP-e"f%֩-ϓ378M9ϊ,_*n;HEBƱcl~ ˝[/sagIE2,z1t:kLș壋G){7ond{@rP>kwk׽ #kXfyEAB9uM4P=_lgW؇N#_nGpp ,ZUu6ȓVӰ0EK7*|]{75F\ԶzQz! uH>upT٣o3P)[^6` -d&*=%fY<^ط`_6|h3ء>2 Pq7ώ ,NsjF=B` 큳CiU)R鐏@LҮǧmb<2FHRqùFXi䎲OmGA}:*u f:@ʫRH.66jcGOpO- 6HKJU:Jǃv,3DZEƮqq7p?ȌK%ȧ$;?Qr6pP7`a^=R_)m>D3#£ _' Iɭu͋C-Rne㯄ssL<ȭ/R)|Lt_1Lk=rr 4/gEr~PnB[\g[{gYvRW' {Fem1{ wL;7&$xc0 n&u@5sCCձm8Heft x{q(aтa?Q%l4ςxmWI׆GC1kQ3iJh,KRO`ʲ4)%b6B8\pe;u)ko)#WSncRx{[sXv195_0Kՙ7>Tp5ٴl3S"؝LX睫[5m Q="u}pϘ*xbՉ#iM+@Z! Ϯ~jYݬ$?5mtu] %@݅:4h8ۃtu3; ΑO1A/r R*5i&j#Y2:$Z(ad@>'z L뇶6Z8|`6"X1_z' F-я?X^ A:?1;h/KVB' vOnFS ƤQ{=kh7MwXQp\v͓O/. N3HKRlK"q^Wh1wt h@3e6N|I;y?8t[[! $,ήLe"z%IކAkRl!3u8ځy?_W)AbCO!rza5Sn֗#<43y6"R߃CQ&>[# BHǽ{vekOTlq(UH͵h ݔ8,@tՂL{p/*L"d_y k,4 G̖bD>,.ok"D;|7[.DCA#ilϟI֬Dq]+eE _-- ڰc^Lq1~CCC9gNH8BkhJ#Z-`VoMa 9r$պZ-hkh ?C$ ^tď9d(8P݅]ڶw[wl;dn׆oKd Hބ(DInI M_(5)6H/Y1 QRk,nXHʉ?>df&6^EJmt{CCc`0ʅv5x<\9Yc}106"״!֏9dl:' 1H"z'7QqɌ#KR./CVgQȬ\ `?d1yuM6Ƶ8ZX]8^pwQE &1frRKi$GݜЕh3'{;;~FK37ku<pdʎ+C RMzƏ7)nҀ lEGyl:̑IoBS%|ЕsTulebA}Aʹ10A{KʘӺtjdLI=r PRg_LbR Şl?␔)![Fo wi&k^CV(t@pW2{hxHGRn͉eCbxԉ6GQd27\ثdS=\Ff*0ۣOP5(rZߙxQZ>~GAeN-jY7Ҿn;n?ӹ"Px}/NW:݊&׾:x" ꭥу;R펔 c䛅љElmG§a= h¨BG_uYnZ쫭FYs U"zM&:Gnu.DX5Xn;}ԫ%XO?~2&Frjj8 yA*W I9/ub)Zl: s 85J>~iI3Yԕ;:#hELם[ROd^GA˩f~Y!En0~/A Km>^WYq"<цF*c:xw|͞w%ehRgd9̕v3v Dgh>>?3hYDkgC(ʹƒԕSԜ| 2Q94(?OGQ34 fccPopTYaW(>@tX4`LGٞpɄaŰl\[9c26U M6f,'C4i?W~psϠ?kAKrŵk@I|>^xs?\`,D̒5W^w DMXf_8<%|8_왉pP1Wlm߃f?4:́_Ԕv M;k:p_sj؎qw]$F}y ,b'N=o0, ~M YR46+!}@~ujctCP.Y(x׎z?70WXFܣo3z0c8RGg0 TU򄽻w"/4֏CQ`[{Ocn]+{{ N!33+5]qpj' r9FDȬ)~: 9Gmx2-?sraG"yvUpa;Ră A\& ?#n 0eed~oq嶭!!DzP^H)>oȑ.ļԶ=Hy7S-M ?8ycߧq|#5"2Б lm#UeΤVbM͘jAc7Z ]> 4gb s 2WRsKg6 's8qzTT[R[w)I95xWj #!nN+zPڔ KgTE,?{^RDݥ=Ru^zîc&D'i74SJߔ&HUG[crͦ<׿~4}څh;lpAZ%XZ;tQ?yk1+Ƴu6[ Dc4Ɯ*dB#!}e>samhG3c^8u9󼵕⸈߂UyB;f "Yi=D =4&|C3g]~WgjhSIXU"1A5Fr4{AljwTt6</N \Rta| i>T.Wo>>xϯY{緷m,J{gg}v~)]s!?wXGFl!7U|Cnfﳅ:.@mq%臔Ru?.:aBֺE#Gg'yXDuSWNJD)21ѵVagWPqȒ s?¶@g")s\T{f3go^w:^"{d#!φt},nyWFKv„X4|VB~,˘_&fjp/WԍwaO H 3I`u1ͤ+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_Wݚw) IDATxyxky,8`HbIՕ%ٖlvbyi6I4f23INt?ftLLI2OTNKĎ륉hEdZK܅$vW${)A%~qB!͍'@!ӣN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t BHN!t^!3+++t:]^ZZmۈFJ{}n{9sρƆ'NktRcDQzFGGsw !tBΡ^xa*N_c߬ ɲ .w\ !tBΉW_}uUU?wXA6/FcK?kmo+5۳k(W0pt_k׎R !tBP6t:}\.qDZqll´ .@9¡H>_rINnܸn3m?32ه6wύNH sss۷oOwf61Ğ6mRӳ󘞙3t#mlޕӊ s xvq=s[l2񹻼mcu~^eA 2*  j@-oޱ9}#Ko]='}sCt d:px6mfcIL'p{a ^BUmNzx 1C|Їp@WZb<<!DM {/ne6V8kё~HWffc \H!8q`Xtۆi[-mm!P=i˶Q,hbQC~Cֺږ5g¨}7w=x}ygҌ: =S333'i3SdGO" RS_7}OtNq/⡣[j|p]lƉ9PDmxunD<i-ǁa[X-1݂0Ѿ :oXꖅiRձP(jΦcw=w}gIףN޴ۻl3f֢h3Ob ܸo|y|ס#H"#2b !/<9^ ^A8p2jm] =#:u=ưOh<ѹ%_Q1ux;-szݰlݚ5,7apUTGG?FnYPMif?wsl΁jX]bv:ǟ Gg>C?\|!F+W\Z^^Gn3Fs^l.Y|ϣTv|>Q>{Q$* $\` AIB#A7LX@]PF"ݴ&F5w;eB&ᡘqx8X\*E= vtGח20WG| F:n޾q2 >w&.nUB<-.D$QQ *nW9P?q%P>w~K?}%Ҷ|o!=7==JGZ  &FO!?ZT/ >FZ] H\"ހ"GVFcx$%~OUÕ5m9p[n!V2DO2aonTlێrn4TM^j]܅_vIHb՜͵rlgwY83*鍗EI Յ5d>L)xmwڃ>u]m;Bms4l{'Y !w>qP3 TtkU,QMDs }O|_#?wɫzAM:'fffzLn+޲L0>XX8{_%N9(fii)fig?l,T!g{3^X輔~B^EA=GB@jZ֪t7N*E}}Эmve(TPoSgE gWum;(Tj(U-GxMg "7{`>sϴ|>>H d^poeĶZv88vJqT kkU\. B-/sû؏]O|'s:2fVVqwKR6&ǒjS(_*yţ7Ga@tTFa?|"HMn1%QY>47`ΞB)cCp^iи"\;OnnNM{T,DOpN3- iòm7|G坔aZXϕ&Q;.2$b"ދńN @l\J UQZ-nwL3WXZ9vZ> D$yM8DeaI:2j;4e?XA~qͶ4By"AѠ}f`TE]YAn Gؗܲ[R8{+~4TjuuNH">g/pJ7F%qP4J]íݶ%@4!ZxD˯ժB~tnVӡ G ) biPkM&m#_Md Y=m"fc7=Au▁▎rфZ1wR-R(0HY ""<~|[yc9[CS HPīj, `TAB7-006毕`g{BOw3ޗ886go6a0M[^quoʷ~~?:omIGn߾ݗN/o33vx63olMq-٦&*j8r:Jq+Rk^OiڨMJOF;eހ%,@ < Ɲm_3Lfnڝw5I}ݨ$庆痑M-<ǡ?DnEǍrXLx^yw?s{?ڽ>:ikqq14===u֭mf ޺ll8$̎㯿~?v&>F\q wW5-*{1ku4L BG# b7PP_G'p?01ww29ca{s?inĎ30ýmdžm۰mow=zvCPzfYXq JzE" 4 uq"b6y d#Fڦk|]EYoNk\(;%rFB!tۆa[0,/Tk7?W~LO𜣀ޥn߾7===5;;;v6&Rxbe~f˯ϠnN @@D(Zz5\eCo8kuTч(67oglb~̂zIqI2B^?dUx(dyσyHOA(G8G +*gR@Y*䱶lu<HCH #V&66̆yfk fɳ‹ !1Q4}nXMdz^ŢP^Q"F>.-'O떉M+UZyĞ\|?{ O,,,􅓷cr,x&^{B^A0lld3ai:`0gwX,d="68fYZJλ|KثP*cqvibO"4 !yɇImZVב*5]l+q a'(s8(Et+%n(U4i+ll ~g@CTRڮ5SȪUufsу'?}'w~Q@bgmfAc Obt(Q6v}fexPC?ĴC =sӫYlܑج`:8GǑ9AQ`rSv2SCaCg@/EnP<b(b(TE YB<S}Po3tW_z7;(zDG<5`8rXNW1 6R#:~^v+^}&VV/Uq8%li0- ւ1nWx @0ݙ%GX([,[5 d :m >}.11r9{9믏NOOOp6hIL&ϬN*ooޥJx\G{itllj 0frp8iUL,ެBo=v^ vߜG "A@쌾EcN7rsܙܳP/l"{K =;k&v\X'μ{EiZ[╫74Vsl`mp| g[==rRU?-1 rs`w WW)Uq<֯i% wsk_WWzšui;rXQ/Ngx!tۛ aख़eMAu TWl&Wxu6}Q_H }ȉw#B=艆pI Uat"#D,5D?"եy,8&R;SD g0}c WnZn+k0ؒ{CMݹ$F1 ݥu1-/.4l1˘}u/=/(O?UUB:|Pڶ 8GM%11DlNffm1&Cb`H+r$."o5`RkkTk;}eA-33 XV5!B>Ҭ\ŁP]u{4;U[WqS><w|: m871Dbnd=Ĺmk+X]j5ByxTq{6雋|חU꬘/O| c h0r\LR/__;}wsb;OOO' >|^ZevƓH%ή̲,l ET[,q7ps~X車~;6𷹈Ȕ`Y6jU(F?V-AX5|\f@܃>2AAD! |S>xQBvyapyau7FV9v0sbB ;V)!"f8 eY*~8<땡(ƒSc:U)PTГH2MldV]]Fvu;dT#EtMK-֖Zd!$-{;֫xi2am1Hxw7[0;ؠEyBlfkUlWlB~?|{ٯ:AN1ǶQoA׼*_T˲71}cC}!kQ),@!2jlbcm8RC#M:KȻC;+d%}`.KH<p}#ym??t ^ p7V2SZ]m,v#j/9p`yu@(ۇÈN+XXbaixj槱k;k6T!I<:z{D$mu77W(b0D8njp%pGx3밌_sfO{4B?'~W/ɲ Wi}  bx0> Faia|h=L@زF=F UP cxD p6-kUTBvuX[u鉡/Bo"h,I:577=W\y,ނ(zDbн`ٶյ<旲XXʢ\nݷ& 72X.jQ_c9!xTtVDC۷A6㭼pxgj7=%J t^qNQ@?'"Ѝs UȞ21<؇ة+ݵzE5*} ~YOTV3|GA off6dW1C@=. V\F*}2f[6*IOYԬ=Agww7_Si+eb2t8bԆ18yoersXYf&V@0&SG۶(WOUj@IO05ًx2Wq'~xA8=el5dkGmjYEpZ_4sNo7\ /6_?t_Dϡ{J x>/caqKYd7SţS>&F9eZ,O pnTpѓ Cc.JlCE[~Nd܋dK;jjtb'5H_rY z}(`#C'OGzc#@VhY|ط3:|;Wi]@Z-۪-hދ8ĢpDaÁ <7n֦/QH>wbb|p_kQT " 10j Ylg>d0ZZ`}uy`=8zbq"=Z. {ܜN*}qíJ?m*/?J?EUk0|Dtp06N,1&<A@"^[ܾ>VAusO~>ӿ3x1w.r<ڧ ||_C'O^/zr1N`xm "=<﹩P M̷nzr1Vdg& lXMۛ,v~M}䯿]ܾܬiUڮx}P|H lf6edV訦R*R*."zIG9w{NoYN/fU; 2 [L7%(wQ#.[۲1*yV[$iYVVV011s6lI?o.>"hлTSj>ڍJU 7.'}J3.m{7֚W[ۍ$ЗLmxd?b~<4tla}ekf2ˋ,/U A9$5^nqw7\ {>kK]-[ao^1{u#x(c2~@L=^F.| IDAT7[.[)q˸e Ab`aձ WPiF3,Y\H bKM$;xeXvZ"> bjcҏCT*Ls-d\x>6٫GRSڬjyX@ pb'{zS[OO|?1ɹA-#}x>6ps-RZ^V|/ Oͯop}32,CyAIڙ/h12mոaY;#Nܺ\f%?Ǐ>?#Obd802_ Wlϸ_?rD,"h$G8w.z}u +Ȯ.w+XEޝ6I;megJ cjt㐸BGN;N,Bo{2qA8Miq }^ U1227?w}MY}`Yd2̓ 2a Cd! !؀la6[-ɺ?z,ݮT~\.$IDERijT}Yg4M>+pdqЯ6CKRƕ+25p`nSM}ۯ{C:y4#Tnx:^*HiXllBҀ ʢq亪k];~{7h99>yAYiqgY"(=Sy&\bqIm*=-bH2!vx*}3fW*E}ݮ(]"o|9|q޽htb.pNȴ6Qz≿oyOsW467Bw65_,}m5[CVh}ӄ< !Ӵ@NP"LcfIwwKSf3wĩާ[\Awi QFnj~~sMRͪڡVd d8ھVH/1LƦ2de{*^^{,5=Zԭ5X FE<r"HF*4FgS@9lL\ G+ DBmW*K,m6wLQ!zR['yVTtSY" Pcmǃ mH] ]<K o~JocsepxbcmpF ]WݞW5..(52%a$ < ဏ~l~ܤ!: Mʥ"RŹBoyݤfQ!A*Z6>>HOo{Osn Z^GuӴ9lwmPT2gT=(vWF9~;k^y{r"t]^kѹ"5xOqZBJ˔EͲebY'S芅PV/2mÉF>6|K!%Rѝt=<4$O}CC׶UP*kk/˭SELJoH(~oOU9I[`<| k*t\ʨv=Vx;h6?Ϗ&_wgbc˄+.ƣO6=}xŪv|u/`fV_m0I L-銅} #/@G"±nb8M'Pfk|^e03|DbUqL^q)+r*ɝI`tc{lSz~[=(JHHlWZtmil #=Y~w?t]q{O4E$}kڂ8`Z6Nͣ vRcUZ6BT H0%¾Jux!1EQ6k\wx 78L!<|#,jN#\wҷ{oS؋?zL0@IZR?S?MM0?=sL]< t&zopxڠTҘn/VlJ3rҷO;JObƊ]/aWLe+,OXJʆA):mmr0O6uȟo?ygps˄+.kX?WDLd~jβkw ~$Nj-X.eh٫% C*V @<ѶG~G18zqX^ҩwɅ9{{B᪸w I_=$sՂ R\J+JuHO?RY - *-jERjd#TJ2~LW]v_j8ʄ{1(2{888ȎEMDkuJKR}om;X+I+~CRP/P0=A>^v /TQ*py.~IIo_%5Z>JŴ,ffƎK;J?IM_*җ']{\\vpEr~twVqH5a˶-|7%nQr ?WtW]vtRB*j["2Ӷ>D6"7 Fg`6t|N%O0%  #->PGoLiW\j>2M&Ǚ wCBbb2>̜Jy/?]!XRUR~sWFǁ\߆e3`Z[ %3H<>7{k ր+.;ι¨8zi~ĺf OޣlGNЭz,4%E& C~N?Grf֝}OgOHjn*eYN8$noaW\+}re7~5qٷ3r%ОrJ-L1wՋ_ {"n Lċ>ӽwM{}peyEZe68(v%t5P XQYM ګ/^G``E<A ddQH03FdU:uvqhjI&X4'A%Lc% Gw_eO~Mw/`y1,Ixue6ySC5,JM q2)%:\8O}zS27tU\Awq~paᘿCFy=8Zu@rU^qrBXmb&AE<EQ*F5+OwlI<@wWCtuF6=fx}~al&0sSrͣ޺swtUZXƶu.(J[3˘jJ?vJ w,ۈ"[Hlp],[0Ȉ)$X z+< 2vu ^; \*TYFW!S٣k&XKKk7sj;">LlVilFU5TUC [U_JfYJfys>J0GFT,"}. le"Ʌ9>}30tvYJ昘Zd|rr! TU@UR 1wLE #[ȷu3ꢈ%B$ {=I^Z^|_}oy2. ةH_G|^rLE,4*S\+4ʋ7Cwz9<܁a,&UeUq@ t,҄A^\Œ /N‹SȲ@_'#C E?a,LQfGGWb[ ӲYf|j6RlH~!7~sMϾ/L}2ŝT/0 IA4v_7T<2t{A~ka,/(TᠲB2%yG!8o(,-%dI۪_7WBK& 2PQZgY&ll6 &ei9˓OߑvYJf_KNdVƎ;o~Pgb/8}Nglja$IL$f8FJۻPe5rWcQk_pȁ{}cLf &{QGL6:)jRgՅdl`a *EQM̡w?XمQǡX,R,VR^wԼ$1P7à Lͷ4jܚv 4y%}_w10#X`k4ڊerXD$bN(p9hT}6]8t3) Mϛ0#U(QPӺ6Eiac+z;#,m/xdbQ?m'd Ʋ>ebHJ[TSà /ՊOUҭf=n*e'n=qG8د 6K*MKv<7dUAY-\Aw1&M/@=8ڟ3X(XJ,M6 }bIEWQ#9`!܋i.NIԝIv30йaj~1T*hn*obM6}nJ oH+iQd0Bu97->pbi!K$KQVt5%^ ߣplfJ-Bu3"!&V@ClIof=]>z}TsS u(TN+cGTLOwo}7k%"~yJx,ԙ5x<7/*e6SyY5k g(D{t%#;!m֘n`m8":~ni3hS!"ݱ_apB>s+4dz>G*v|7r?ǟ~_| gzg8}/^;HX!U"ꭢl~L,)k\D.[9|~݉(=Qb6\ E "B7jLۢe xe2y-l\%I DϿwTq\t7_ؘ8m"pd4ʑu<孧kżJZU71xo|6ǾXL"#wE mdM3B>o{7[ l+8jYgjb%dY;BW"BW"WJ)o1Bsmf"XO(gGM BD##KW=>Νj, B#mۡP.}65_VMk#5o;~hY{n*e7 V<%b^F^%~(-Q72ZRVK@Aڈ+8NY!KgY ?C\Av)V1M4 siA {:-fX<bhR׾!l-5 H{V}h -" kXz-|2LM-kd%-_>ޅ^.j̔3H|ma"-QbJoFԻv-r$3X'j{_C&Zn-qҩTg|tDNDĂ bi kSFh$!TU\jt[Wv,ǡdC7,R"};7d=|#Ťʳ+FpS.G~'fn+UR*O$G稏+_lL_m׾ {u^p英0:kER,_XZww$BIhrJCEHmҞj<_)$EoFCg5QwMTEuHtxJjcu &:>O˵Ǐ|#}~?M]k6K,.! eJBWp q@衴 IDAT7_߻k+#=ZtdvzeDQ$dy)n6ߤeM,|RB0j۳6c:AdUDh=Tcvfim繸log?|k|c8=7ؐ͞D* 2a sE.|oޫWЯ1?]KGmL'Yˠ {t‡8]J^%(wۂciveVT*3dngTy}o{7{ݟ۶jBY^\&б"qk/.$\.a7>=ٴ|jNn\A9гc/%ZSR2L"JI4x+e}GmSS!cV+BuDLh21=Ԡa HLj}7 z7fMΖI-_hm68𖬇ؙg~_O k=<%ΪըРf_..&AqU-VZzAsm'rqZRMbrOr4of3&lscMJ9t?T\Aqᬇr/r-,ˮYiזjcŴI(Vj/FSAz$%t&T+#q7F9 7T%wu7-& \;s 2 @٢pfp`W~ӵdz06uqtrf|drz|dbf|drf|dznjP׵{鐞PIOl._WW~끷OMU'>ѣm_hgp8"v8b8a_4Trm]kv|r Kh$朼 'gf~*S'=S?M"u;|ß7e^ŷ}(yzxpPyliuӿ/ǻu"1'YS%rP-ǡ'ZV;*AEX._'o-"/&^M jYݒPriz~c_z׿~~+O;>`/ħ>ٟ_sÇ?v4w{l7BE_s28Wӊ";]pEJ9k⸥rmz SJ}0D0e9n|9Ejy:.Y,S5ceΔ 5}mیےx}@sv;_;_7?|W5+Z=n赯}_|Vٖ>?/-q-Ov>|6vyO8)K$ȑ7bB9ڭp Ֆ#IhL/Pʱaq\A)~YlL9KVאDP "72Tqݲ9aP@o3eΟ?x>>{Az  Kf&q8eJ!L@duY`W⸜,{OVv|]wݔKCO_leK8Ӑv_|vMVKu>Bn. Ȣh4L-Zyv [P\HS*k:P_.ŲΗ9>?(O㾔I'wtIٸ\~N纑nuZZ^rtmqPzz%2jq;^˞"?[ NDYZl,"tHϺ=o""Dcy.itR:E,KEz0)rKAE+I2VTl!5E-6 0yO '8:nF:7zMջ & C.#9a(n±S\\^ ؆QeWݓ\Awi`@ϖC7 y*9t>ǁX RUBHi.15Nb;(ux?SY$%^F!_%n0x[ԅ2[.u:3Sg+HUCW j.}':bJ$! 5Q*XwW]v鈇H\.>YE 躅Yx<=oinYsJ<[(0Y^gXg+c~Y#H*e!J'=vtUnU(a?o2ST"yOA?ܳE_, xa-ý\7u+jhV+]\u{+.=[_!_4_XtA[ot3|ʤOALN*!Cr&sYF"Qq=7a $T"ab:Q'%\pdB $PGmY}#L=QFYP&y^ m;ͦMϏ@G0}x^1ęrPxLsqq#t+ሟ|=Xre( x@z0J)H~Yưm&9F"QDA j$ŦF|ARjö2iUmXQG4N+uʺi`^^#/RΘ,_(ӱmTP@U)Z>975H_7erZ+~ \\Vw=F"dj,]Ǐ? KKFm=6#K"e#Wu'`JgWQ8L䲌Fcx$ ͲDY.4_z+ beKN'CMaԴ)v݁&6Vz[^?&0n܌FvZ#;c@*o "C]r`}Ά-ݢT}]3H/.;^+.-IJ"V]BHΩG/U =6tӶe"%d&'6L3D^|LJUA"PdʦINѬ!wֶ >>n܌NvFkj~=m36"_{..W,c>O}?4z񞗷)reӷѴ͋zŒOpADwVRH%+TkE.zڎTdbeݴ,*tc\EYaCHP4 8aہ]&Evql4^/*eFfJ#=n[ g*U$/ vmkTXWp+$I--cmӛJfL'jj*{3:UsWDQ`hsgf6}P_ j$3 P, :@: )H@~(dLsJfY,tx$ |@ezHQ0lW"(QMZ C(>jW&iX^*_밟ÕϤOV~)zӲ[d|no@e|"~ oK;9۱ea1vq٫{[ ɾ..cnG y$Vy 69Pq[uJkrL.k i)*m#"T.z [bQDEa Y *LB,pT]@Edmǡl5 tb7H 8Z;+gL2+K߃O29'%sx"Uj}Yvq\1w&ROƑ+If 36xmUv/:XدX0x4b^ԂM!mhooE~|d4 A0 GEyEU"T(R2̦U T5g;JoR*LsEd^V(-'42*S^}T3x$Qd/~ ӵfv;TqjJVT#w'n~apElkc165iRS锊B TQdRKAvRNy"">FDD|޶EA (֋ɗ cN@M!p4Ni";ul 3\ˏ|x$đFz:WAADX@8mYRY.N^SsqQ.Yۯ2ⶣ-R'>|+Rw9ttv/N쥈#30҆ ll9 m̮zaL. > %Fn3XjO1 [qS(:eKq%b Q°m,& TmEA +57miP4*Ts+u+WJNd5\G{G,~!:؎xMf%Q32\\Ko7q B@s[ 9.طWw=r+\ޟ`z">iowSr5iwA,;(q`aD@YH z W&'_2Mbn2CXO\/D}25qEQ eZ&s["/"QY-ǩ{Ѩd~5_m+MIeӶHH2K)\yp,Bjqrˎ)+L }0|$ݳp[p[{`n&cLϯ60jI>ɶ 3D";^EVLfT$f/eD-)"^{eyKh&ݑ9 I#IDj: F5ͪYuoIxDwGsp󱷗i]\$."R񮱉M\|1A9xƗOM'n~jp`/9г^dy-6!Um,r6Vt4faDos:}+"HZ?@\$шBбq)ui`R`pW &}ڊ(xgw}vB衵/=W[-y}p7[n︧5ȁM'™EZ[SS?E^1k yYV8T÷(JtZHc36h*"1Ax% K QDAd$QHoeQlu V X_4˪+= t q%OwӲ?+q;bA)VB_$Җ{իzO?~O?ԟx+Z7ƂttH/oUe;X$ 9r>IS %u@ն=t>۾ط>{}_盯{ލ F^>c\\Ĩ=E IDAT4!49mc:TB2c@RA(K<\X4b,4!|TneyY/ݗΨ_I[k"aⴒ-!EzDkB;Ӽ$HO,.|֞{[Ϝ>~kB6O:B$=(-\\Vٵ=(Py7^uUa_6r_ڪqD|*Gp,F0%Pj`E֙80_; R{<( &:HJd :T*'Yz!<+q(""}0]1DA ideL PӿY9T|RHY70-Z IUd~/ai9NePju>T#>#>,a|3%j e B#%R2 $"]u2>Y\ْʅej%@FtB,4²l,F3LrգHǣt뷼*++2L!)Bs(,Xb⦭p,/NˏeveĹqbx.+=\tHh %Sdj2eALVrI_ :+mKҝ/O_:O)+RQq(.vƸ\6$4,cY6qs5 }#;+qtBMtD_ٓ(2^EƣH}>zB!5c]((>J{\%&S, ?Wc]xӶ ~/Zp'$N%oRuNyJ@dgb$Kx>##3WuUjW=q\Y3f14@F7%(JhHq)iɝٙ3=m}Jo"Þ8̬: ]aND8>Dzny̽ڦvsZ?O~gOUOg|ǂ>f(G.I^sW{nF MP%"ɨ:P,~EIFp'pÐ$An 7mpBuj!kW:]u^wTpV]?pY.=GECӼsy'dCݥw'9BFΝFܔH9;58LV,+n+UI! HIf3`9O c %:Y0֛^Q%U7~yovJ1˱Rϋ}'ݐLa A;h [ cRY {{D<=U-O_~S2{尼1> YwΑ v!}vﻱ)LۑGH[[7!0:ADOK_,p"g=VtXڡ1v&Nbn޾A'.9̜ڕE|ox( Ӽm.zt YqPv6V3 I)M/x0j=hTlsHu ˖;tr~% g~FEt?X\mpvz1A"I=Ev^O'].3_*qy^o쵹%?ϼ&:ҮMk7T!I9ޝΡ (B>CKxqyxq}{#23nN!33|ߎYbߋȲęsS|p;ŜZZߴ.,bP|Awj;LH,q :!Lu#LZY]9ٴz]u;7PL$Fs5PБH)"!"DaCkkϜ9NLYIE#ԘiYLZF˧#t/8ݛ̷[,wI0Fx☪Ru]TYR5lUԡc]m-f\ƌ'OK2xJW]%-|f) „b1Q٬z Eaʶm8[cn"[}r ZɿOSO=7ߚ'XS_] cdYA!P5l~m̘M,(vW{GڊE!9N4`OhÃ$u`A,ܛ6W%iQ6-b |O+B?np3ϏSg*ܺB3XguBm;ݿbM~ZV$!*0aaԴiuOiI{#?2qB$amc2y;ִ]=JZO+^eIRU,U,IDIc=e O͸q"0`:/ l怘MEŴU (4|&lsgM`?] xKhw)<:onoea9U?:uH1cq(g} `0 4;h^s˒:oGX$89u' LA*Lɰ( M?!! ?l4]InO_l8K$+rTYZm̑=SOXP,Kzs-])Ψ*7 g{j7G:B2SQYo94<CuȅgGD-VߘDnW<23L0(p€f]lUidߺ\ȊĩrL?asGMr^NDsu~ҧ*v}am+k#i9AFxua !X zs6 Tq#wTLea *<'Qkx̱2TUUP$yk FB΅$IsG97]mD5MGfxklv(U2$ZmK @D b@YSS6$χԗ#S*v=upb3n9xqj}k,I&+:~T$! OЯSW/"Vd4vkI;0e|5mGT3Y}N}vpmzo}y}^Y߰e36e F?Idb8T'8|t8I!B̍:M:+AJR&27]ut&]PTmǏb(B֚RXdH@VnQ`EoƱ,̥33chRmw5Bi9a\ݕ;!օ3?צ\f) ]a/L_ء땾Y%9x%d4mW Y7 jcz$Q"貂lWV؃нO)8`V6 .Keokn'jLEoT^urc`E|E)Ͼ~!3ΐ/zGQoG_Z^A$ 'gvcC9ˣDYᠱ"-QJ-VemjONwa,+AKoy>jԜ7VkRyۨm֚dhWΞfq:䙋'+p(itCb?D*9]_[]Ce*3{ơ.[I]ZhvcҢޖHZlOZP:)r$F^Q]sk68x5g" bg *EV+2;kZOSe^q%K@[%@wTm wwĂrH}9'U%tG7 l!В% Qr绢o `(j.^:2: Ds o3D!74Zg*ryVB.)'1uNv֛?yC{O-< 0 4Ns\*3׉1c={&m /zn`m _-|vODZ707.{zcgw^22WoOLrNUmi9e;a8ߦ4aR(]vX N('Cky¸U`iIGjW E7'|ӪvSIP&ȉF"]jY IyjAtrXy,wT=*~ɭ7Sag\_Xw>"NNˡ0Q\I|r1Ł0|'Zt`Ødo6i)"!DB,B/ruk e P;*tE)N[Xxx|fb2VMΟ.r|WkNX3R-Ϭ'ıLw֫n'2ebĂjDs5°er* Hr$!J !y_H %@ ]Gu;(,3mgX65ϣ{-fqX|?pJ_q&+UONT`\v\!GZ#b[=CDYHnWM;u;6s[5NI"0B-,$#HdTŃ8{~zdAq7t:`3Tg*|Vc}[Yi$H5u17ݭ ~'PlI%7bev׮(G>w1$Ի;-:!$Qp'Tm*5C,]&'KeˢlY48)li഻n;(0xM~=aS>h:x8j$)OcCޝo" m:ޢ,^*MECd̨PH*^C҂C]Se>BM1l;tZ{b}Y7kMɞ@['d$j'-3AZՈV5B3%relh AORӾq]m"҆; ԛwVpi݁3z7jڟn*}%? 2O$s&"eB]|xrr$a&Nw#iwUU:Ǎ_[y9J%prԦ ,Q{bx)Mf&$\ỲA=_Ud>q4"NvpBm')Š&(Aల!F 3^o{χᅨu-^#Pv+n[Xto5Yp='Ɯ?4ml;awz!}M09_,QaHib}N>oV0 lo-` dӡT9onj9 Co)qf"XmILtPH"ކ'egwHa(Yn+r׳[Ր%l3*)ɚ8ѧYsO[UtstRU[],%nnjN;DK1@%K U2E3;ͮ΍cE?8&cZ,mۅƙK1/i䲺6ԏȒĴh,w"I0%ݗ[lۺI,UZB-|HHT,ݾ(/ȉs&Oj_nT77e 3n8fA9fN&8DZǘ>H8ݖɗUee}ªdJlM,4.Eu/NRUrjtw9q̍K$Xƾt_ywB9s=RJBҮB@jds` ,6 lwvA'&)R%Pȋ|};lG& Uϥ[g Fw /[Nx`Ҳ3p6_`}W$Nw;\UkNݛs~>p'׸(n!s-: QDcEYSTY&@'a6Q>EFNj ! X d iaSg+\`qϏg~IEYmMұ ,owݞuف86v$4F(][F'.{k*bZzfBD[C3I- RL(;r@(Xsr3iZ4|S2=׮Hry;j^oLEesT4f7^70 s:F˶iqw. cC@iz : IDAT)E槿>˒ąripmS`'E]ĔWoqB:N+:hw#;UeBΰh`w{%$$ahA L6R^4 tEF'fAUx߬:؆jǖֺvܱ9tu~M\SuTYE;>d:aJP#4]2ҁ+ d8#bi?!xMS8yz[7Vٛ97ӏ@Ud~#:tg +ku;Zʞ@^q^(bVW\֫N`;퓆I; H*wG5$ 9(bbIv0M&-;u'jC+8#+tratr.(̵Z;mUlfCԍ_B_l/.Se=WPRa_9f 0D}(H *="=UjSuPuUH#I(qV\psUNP=<53Rf'6;1qaı^i}2Y\N;pvt*K(3+ݰLbGIJCu6eJͳ{QčF)3q[:N9t}7fȲDlQg*g Z>،q:vѷJ*O}›v5u |LO9Q{︪(BFńݟRn {[tVMV]N4P$f|V5"0 \[ryv cfĉll qǿRN=mg?8ujaunj9 &tCٻY o co[7IӃHr0}xB%^5PmӖ3>"E t-v`jh7y>ł|?}aNheۢ`ܬ5{+7$+#az$v;lLFhNHؚ qJa*ܥ*; ML' !W׈ℒaߍ <}zFىlV2lZQD%O39_n2'?6qky<Os[Fa8!z4?[PS;PD\q n+rs,7_|9{~ffb2ZU[clGe.<5=I6m^ͯkK1C[%rff#y0L$a($ YF4t7$%tmsRmk"חD]5ߣaݣ aV^{JTx&z;sTM/ޭ";Vzac $KBCP& u?Uv1?9tØilc_I+ hE!:@.oQX(f8C3>GO?mo7u.d SMY< ;P  ѝ^]sbaMg*cϣ]?(o\kYr櫍>VS"grYc䃇Q #ŌٻAĭoyW}gU,I<s?|\?}uI?=Xiy8K$Kh¾/v,ڬ){Lf,3Oc%"?>ZUU|EYQX<{|sR P bʳcjs-nw-gCXo9 }nR_ G? f ^ZIG{LМ'Q i`$IHUI/[oVHݜ︡(̦G~M.̼ou\bZ4M "jJ4wcCIH.k}^[!Q~֖=VDyClRR; MF}UY!>ـ[]s!xa0QRvrSX{d/2^d8[|7$Ix>s'8IquN_Q ޜm |۷ZZ1 ]VoVkm;%$K>8GNԴ$CM2!T0o0S7 *ZzWݛsi]R} 3|pkzuܘ19ԭt\죣T# !0h6y AdT2كJ_E_Î%nL/n./o.D xܷDQoe{?{ߙE:*,sX.Fv'{M(K$VgTaQBjȲmw ,[=H\ )p;jeMŲLx iDulY"%B&_cgNmAnj(mZ&2y2[N˯pVJ$R tG6#H@EQ 'ΝL}cƤq$r]؍Ce )*vߴM"yݦ6S؉f*L$!HHd(|;j txdwޚ 3>q $I|8Ÿ|-moi" 9[KmgsI@ J&X li;+3l^:bӌ4c [|R]Ec8Qt{O!%"a0aZdbGStԥw:ٚ.+Lu/} ny}_{_Tq' jL.!F]0wj{E혏&G?-q2P𷋽*J, 肝H>0# ]QC?bb00,Km!F[{򖟽ݢ^e`Ă W|`ǔ;Pޝq' Yu;}k!kZ}Q\AzR]zysjȜL?{M.wu.mZTʱua2]PQɒBGCEAd<7UF-Ȑ7mNHk+Mn*I3gsEU="LgmӜ)(мǨk r[ %= jZ,-8[}f.($L͋"<HR-[~IUb N=f,ycZ&\7LZ=c:Nv8gtm:~̘p+Yx / /G >I3ZA"#ld6H G&UύF K2C~yh/N2_ 3v*,dyU8Iyjkb-m=ӭtݵ%מİ|gܺ6 ӻD:azxqD{?{dCwTIQe9K.DQ;0WV\+o@LȎXs0uK;8@5dsodLB3F*ȶ2xBzEd59cϸfЅ_E8$W=Q2_c<<7w^~ 5ΕqL{ݟpדܻͿuQǂںG :ʈv-my\003ȒWc0HA*vz7SUP- dY҆>&mv~ҲzZy1bZK/﮳ze :q1ܘq14a! ͆Oc*e~u}jkUUɏnz>9_^U:YꊲUL®>-?mDF]v701=MKA=GYB-{L3ȖFA]W^$ON0Uc[+Q RihSQ:maTY&^ >Swz2iuu :´ GyS|/ަt(ܽgIՖZ0v.CL]T01cq`A7ZU/O~֚k㥕X*` e(ʽqlEż͡WO>vo`6x?5:`.s{E$A2`Gx~펴wpF;' uJ%ys}!$Sgw_F#elhGYFHV aP=&vT7=DrTT Efu4Y&LC] :]O}L6MtYQ8yH;f =>Q*G_7կ|w,-8C,u&kGc /nܮi~zG@$]ϝ[@iVXB@!hB%|a!O!IP93\3}-tG]$B R5m!mG[u3=kL.Q0tV:w]K_uhL?Y\)b_ glbnj/?()"go9HŸ+,VY^n3g5/~"kmly69IA4zVfM;S mg˭% $$#͹'`lLLdC͵ISI$J9VrvLt%IX&rw n\AKȱɡ;)&+^.]R$. B޾~K{LOt'>}rfjs(Nq ?*C~Kk1 2RV3 aXA)q3x$G`z7_{[WoW= ēLA }ڡG'MbPw3ݝ 0aePXhz!+LJIz%v3DK]-a, 26{AHNӑ%<ko#I";(X ^(<tr9U>z&)b>p}/ ZUO~N2D䅟 /gUQw\גD7|U :Bk׻ᇦ|C BUʆJV }ZϰYP7ʮ"Z&L badT 'K pU\e Et9| RdaZϣa@%\%}n GJ<)SNiWz⫭V9s|gӘJЇ1Q*G{~ϿyL6ws`K $kcU2?^qWޝnjj/~oϿ~_I{V$[A.u},Cs r%~/\rIaVulUgУI&"TW]:)wj !'UExHա2uO@uc²*轂7n[guZdP1c>}s_^O/g_/9"ԡ}7[2<]<3ϳ_q]a0C2ifi>nmgq+/c冧J9Щ:t1ߎ$ffQ25mgMsJgLZoݏ#׮k_zn_3f^ xOⳟx|_~ʻ컳u(rYCRw>rymZup)<̽az7o?G:FTvd5f$5C=n {rj4IKIܶS(L9? ʽ^s~ei}.>yv۔6_I•k+8 <2m婘9Koڕ@%;f!`m% &`}>tfI0u S}gF0虚& ߧ0NlW26 x.nva.}]p9qdo~?_&wH[{ƼpkLBwc_{ossN Y Jt^ceEgYycd>~{~|9sr\ cڽJ2D%mV& faۉ. Ϛۿ#V֎B8CQQvVS F@{KsČswW^/}Gÿ7~7>cNw?kx4^W,.7h\t]CUT$IeH,KH(1C2uL+4BI2V$Zm0!B0&Iq@օ@ m%&*[q-˶5N>fk9 ؚWlUmA l%}!m}KsD.^z/7Mؓ$k7R/T :s[+&Sۜ=JEȲ(d2_g bOƭejQa_+u1!ަRJB4!÷[!ODZASl eyfA*"!v xt@ad1'_x?g7?ç?g>xgv&]k3w>躎mۘi(rmPۜ9=ů|)~+׾>src۟@v{j5AiGuia4RB WцWK=ѿGM wߟ1cA.ԇ}8YY3w{m% |,=s8=ů}9>&K>dKo}W7?R[^(֮;d,g?Ov=Sv M>#жVvNxv!$wVA͔&" g_4f0Ƃ~1Ug^c !o~0|A,~]b9Z&(WSvop} U+G@0J}pP^7]`lb{o|jesVa[Ƒ[#Rki 습(>a/nNC:"zgXy}WCOl۶ {'B:@'-N3s_T&WTUM7 n? V'&c! `DkC=Z=Fu[O^A19sM^BՂ 8F5m`۹2lxO;-@[վ:{|&׆|V&kR.Yxn֮7޲ ϚۃU;{v5tZmz="]ږ6ZK.&ưt߲X#J@8_u@''cߞxhl"{GViE/ӆ:޸=0-,u! "_`yd3};!^Ȓ~ztB 3Q)o څ=o Bu;BS#KEhBA\7Qf]TbC8UTq|,*p{+0 116@jRzBVw 3qCk%ǟ<1E6ZLc@9_phu#΃kSk;═f;Ǟߡk NXx5AnRz)/4wy܍m^W'X"_-Tf䊰{$1%24EC>eSq侃=5BnV\879?ys3yp{/\`n&p-BEqa {Mo\F\',,^;+! ̢;Π YѺ(n^}eʘ>3iy30 %Xj۵+li5^ucWRJgKPB *L*`R`<O ?v}\{8 ʪ8R7?/dFyV5BB"XBf/!9x #c) N=Rr7L 5QZWoBن 0,Fkcnb=)qMTP/aWj_,V1Jt,s_{2y'+ S:vlRS0#o;n>t^F:*%%{FFBHKy).%=<JHkVE~pk!%[D P6ߧu:sqz [n5p".]X*X #cЍNEqtSp+ieʕuu`eMe)UHס`hQ4Xw\BN 7O;HhԳ[^M 1?c tU̯.=<~1}ÿzy-b%Db5\@Dfm"5D0 h}7+"Yth^!׽~h~PEBj[,i0wv]c]͡ѡլk5؎ФxmÅ"|h=4W06Л(kRro^OzP j8vax$Cx+9;3 i?wIڅvaӀ*_kn|lHcL*PmA?xshM_+ZPe_ Z! trY +9A bq45.ۃc{(,PD"k[vҮ="+-_=k E6ޛeF39 >Os;Uk錖jj1?_D{kWrz\s!!Ճ >GZwTkh6B<'}9zAdKX/`qб켓ڍuY,:ޒ{ؔB6" fPI6pJ+ZV["(t*@;*˫,ѩ; AoOQ^g}GzuHB lH7b@ov9:R@a+o:H;X*G* `U,8W@.[s{(#=k*~lvc\H Omvp xHs)tiQHػo{8X/ 3_q{e)k7n-$ kj8/\C@@Z] ƤJ>š4w g(I MW115AH)˖0̻[?Lŵ!݉*^J@ ś:Wj!ZBY7_WZ%MrcHwv+ݯEN:bah8$ݲ,+߸W UUk?͡=붞%TFH:>:Ѕ\ o_vW^?WdH_- tғD2D2:{&S mzygLa`5ruCo[oiK5 1KYDVSQ-3%(/q,/鳐ϺEN0u3]{!@U[WaFW Fo/ EL:T hh0#2,xHVs̅P;<Ҵ/ll" t!-qs,P.];[hE]XNU$c&JƳ,uqj sۜ\}xuKӉƯ~4bb{b+-f0 OG22L f3k .7R "B   MSpR*wS+5:SR[VJy\)J>|w|/x" mbjj255u{=O=Ν]BOhP!~94;6&ui^TO b|[vs¼m8}LaH!ЫRudϛ/Si}T 箼5tM191L/0}i뮻=wywy7xܺ\s(з#G?ry8s?;wn>۶h- n_4 `KkgFf uJ nއ[$9ݭŰʼcvmhQ6ŒgUJuO>wѣGUC~ 8p<:33wܹyEh(͗p+ׯ;4߸%|0r,li%w9jƆq'] #vp+Dr(0BĩsYKF${R=zG>vw?E~NNN{NJA*͇0L S{1մ%.Lk]fn2sH,@'ij/\ 0[-u.ZH bNI T_]pb[.jt)}ѣQ)tB~۴HS1Loi~KN[l]֍]kWB[. kd3S)?Z\n}yB ]נiTJ'kAGK󳳳sT‡߮4?Wڪ];48V{cÃk|'΅/9\r;vo_~syܫ?ك>2<~TNt^}jbbšXgMi *^b0D*nbjߥ~:.mKc {w;ԁ#cv>q=aW:C5bpR:H;D/y5>(a40&;4oG'~T*\[Dj\>Imk!jsBJ\a|Inf|*өN6ZiO}%́p.\>'Ν`"aL a 5D7z'SILZ{̅[1g+3λaW/\4"xq f@J?cs2r2N\߽O!| EcDqчWe"w%+.\~ *M ф 3^Hb 0i.'iH,GK!gHNڒ/Ws &cdL*pZGR 2F/wa'%`*1wOt`:[?냟q,Cֆ.VCk" 8HP )a|Xy^ xׇ]vJFDG4iˆh+ss877 1 IqCubvtq> }۞5=Α-@&.^ "]{ld,XPtxƲV=믻tF:QUoO<_xwnB Q\!{@RufIfREzo0z>CrY%|ׇU-rQD@0z/1_s?Y.ީ8.4ܒ#nm+3욌"]&=3 톥V] 5Q-d.ЮRn|iA?mW#[t7;9ẃxu EBYڸ NU) ѤXӃC9$`x9R/mm(JP{+s38;79#!3D-ǟ|r]3|NiRj6a`Sü^Sh|,%sw]جFv{_/==(!@Nw[~^:Pul=W-eIӍa߂ FV>{_Z]/ zP[+{k+5]bS?ͻ1LPi~drx'Bvw`&y@=K7~TAo?LJgEel tҵݓ{} '4]An^pM9E Un qqMw{e%jʹ9M]xUSPm<ܳͽō bj" *ͯۅEunp־-ГzHXA;-?A}[Ua1(Vc?w= !@NzrzҌJ!;S](-8(e|RhHJE:#a=9v7o"gy(e=XPA0H< pq VSi{\|cx'B$Uu/[[}-Rc-ລ<(,pm UL1 t(IcoϜ:~_M #\)*-w S=94!9n 9n@ )+}~d=nnMnm3Wг`4ǡ}Cb(Mvs͗•\_ =͸~]&J*cQ* n[uݭ DdM>W]wfkUM4 p]4w*#KЌDR") |[,o7l[_"gj&MȨ\3,8wGHa0wp{T>qx$|~!Yz^oo>pI8UR.?V|nT??5 !k¶ҍǫ 9H:a9ӿڙJ ƀ$R" Tg Mk5 kyЭ2BvBmi]e3v9Lv=Pu{NϿ~U 84q |~K\KHbm.@pf3Xm>!:̽{Vm=.q=c7ol/~7{Pʝl?cm<`p_ S9EeQZ'FLbH8ڼdTUA,b@Sۯۍ[cbMCDz[wuUKk])Tɋ8}qslzlp_Xa,_€&PeL*fJ(+Y~I\3衫kXn&'7<|/ G#tGogOxy*nF\*xE zDyZU6ԙ Ć4Ćjj0n|8e평q|cL&o|wڅLbxpm9 Y41;o1[Q0qkGՋ!1X /s (,ps)HѶo,m3W6t~;)F#t)2EZ啃0Đ-dW],Nd Md-mfKU {RK2z.Wh@2 ݷ=)*ų8y..}p>15ETETx49丁1v(EaǜZe"-SP$vu.۾X0ӈo?x+ B:4|VwϜ:ƥk|((әU˹܇GTah&jee $8$|u/N"д5Cq+ƃSHN( FFR(US X%ՒPT`=3c:Fnb`t"_"[./k\P"0c.6fj!Z~'~ڷ|GN6[~o}㗖*H p p˗ B6:_޷gPAdp3a=xm+РH4V[ O% ܶ6:Л{k]w-j!Dޕ#&F;1}۔{{ ]v텟_@W4fҵR0?Wmv-+/%?|9:2ӧ>g]?@0>CybK E],u hj4G #||![TTC0"A\3$' $'} zl^lREVhTt$44`U}V4P maԍ#^BNԧ>g?g~ *CecpJ^]=oLQ C"L*e0z_Z9V"a5Lޜ$"i cyE~?9W<6J<(~7<;ldW?w߻~0>`x,䀁rJ+u XvT-wM wD Qs,^m^;WM7U [QەU\T66u9f.WK@_XY ~5o Bց\?WJ DtE!=VVʪpae4 0I~m/Cj~k]^qa6>ȁ2xH~ |׋^ڔ"d(Us_y}xٟ`^ C X<[@JR\Ýӝ)P UQ7i\Y̸C9@j\2;[yubnu@'W׿< RiBxVf4RJp) e m5@.hfTAisиFUZVʯlRK )$` j{*е:$tID4a9s ( 04 @U%⡒߼ —XWXԛ#d(ɶާEmϹ4!"HK.NV}.̭R ;- N[( Եnx{F(,Tlw=@:҉(b'a9*Jn^cnұ޼>߻%}#~:6{7LH D!0<a NYY]a0"W\j|ƂcaU|^g}UElm* 0{*=,W;b8g~!!kGNŹ>Z& <@@цC?@:Qm<{2sZ&|ua20ƐJH t7 PUEcP5@QW1*T$cVE2 'pwÌo] \Y yގ羄"U 1 s_`n gQko2G>=7҆!l{wsQMĂR FA %-poI96(Е( % UUh avJ3 9m==?z=M7|ժa= ߭o *"PEX WgOU06*cu 0JɃݐ!d Pkƒ+/*+exVNH4fmdp}3~ޥxsCM_%>~10( EKJk8qߏnvb#S* P?C񡽻u#l1 trMyׇ/|_MjO(}㱘h4(rO wLN<p<  M+ ^–1@ _N(^kM[l5r]e Fcݿ@hV*fѹZF4lM3cS5!W :_y7~#[|Eu"r膂dBJpJ w/UgvHVnz^R[(noƧJ>5̗T+pB9eV YxZAV}Q^+sRfIѴYk[U< ?>vkǽ]0_0tE|@(\N^hY 3''??y[9" l,j2(VQw9wѡ[U4]5%|g6$?j{8RS񎏵XMF̟\jmz:GD5B>"XX*z(=^[C۝=Q˳_v.bvF5:bq Zݶ0)$y*̻7}a4.[ރΠI=؋.й"uxK"||_ ot\ 桤OCƩ `,nY{ D袐wV߆D$|7xw+|Gr@'}33å``xADY.P4HU T2UKp]f@ULvuZ({a{dO[֖p栬_h s @ FV^(p.Q,8(;] -/ƏF7e~|'d@'}_?7{3~SwbAxcUitDcZCiW٢"–v " HD[^9o.2+p./=c4E-mPCc˯v/p=!s[Բ ގN|=oq۝ϭ (Ɏ]0>WO_<|0k#kYPWOUۗB^тpw,**|{UQ2a~fud֞z'KxrxǨj\jEB- |w P3O__Jn>F "HEY xP!D%k8SS Zي* 6xu|=\+d{+ ޼:0jDb%h o_Wۃr@'_z{k6 a+W x0DL/C:.p-? =I;2u5.Py<pkoS`<@e`qIDATUvDCo=' GNHŹC=x}⑻9`%+Am^!Ar G2g1hР>Pl ,UcO5o,Bit[Gٓ?!?1h+J|F\|_ xn!jh\ȶeu0׾+^}Nj^'#OPң̼_Ï=x7нJ==:>)Pkg+)  ( g h*ek/F.&őP*2ty#ǯ|?ꗿ[twl{脬ǿuOy =0?(3[Bq·-ϫ/yg1 !( @\F{{o}ER{{~׃-rRm"jZw*@Č|^/?ww>g& 6Q=G |n_cA';2@V`+6Z=oއ/)!}-40=~#ǟ;vn{ӇV76An^36j|y ^ͻn~Ó7{'ENUv'vxl6{ Y/`ծ*bFvᣏ{ZE'mhv~&v™.9p™g/90}́b}oH{8s膛SWG\C2E܅ /\7dG مQǵ#J7޿ ~ 7xBH tBųK]w\}OPL&'ŁD]Siz"!*@'BvB!5@'B:!( !>@N! tB!PB!}BBH@'B:!( !>@N! tB!PB!}BBH@'B:!( !>@N! tB!PB!}BBH@'B:!( !>@N! tB!PB!}BBH@'B:!( !>@N! tB!PB!}BBH@'B:!( !>@N! tB!PB!}BBH@'B:!( !>@N! tB!PB!}BBH@'B:!( !>@N! tB!PB!}BBH@'B:!( !>@N! tB!PB!}BBH@'B:!( !>@N! tB!PB!}BBH@'B:!( !>@N! tB!PB!}BBH@'B:!?M¼#OIENDB`sympy-0.7.4.1/doc/logo/SymPy-500x500-Logo -No-Text.png0000644000175000017500000050670112253362407021722 0ustar georgeskgeorgeskPNG  IHDRߊsBIT|d pHYs ` `% tEXtSoftwareMacromedia Fireworks 8hx prVWx[M$e^kzZo>i6(?BbDO^ !!] c$xPzpЃ'1L QIC,tDoOWwUP3lwٜ?')_l0fʏ))lͧ<93tMS9΂)_ 44|Ͽxۿ8'?ps~~GG'??|,Hn}?^NՏh}o*{sOpwC_q_sp2޺! 3S $?wj{OkW>S}d *!<}{]ëd%+YJVd%8U g0CH_޴ SLØ1Zn0)0QD&#y]UWZ[TeSwJ) xM=xHS(R*TH.$WVZ#La|#%4̣^DĠNyN:o" $Y! D6a7GLp] $_93L+o@}U ]HH&Qڼs><RpZM-*l\q3ݕ:0WTfx?U On vBo IH-0`iӎo0s Ad*GпX cmRӰr"fJ(h˼Ẽ&}epAݒW tWЦߩMJ/Q,0 Zd/)ֲxŀ.YZ ᔸy:=~m֋T}!j  ڙ)R /2ӧ#ϻcmHcN΃`0;H`ǡ3M5Dlc~ݯ<z`bp)7ky`) FbȠ7{\DS` a9볧hN|~bD*+Npu>>mP[K#]:'ABA/q+q(-[Gy9WywCrĺoeJ?ۅ y liSv8x P),1EDh+XbƇw pd-ˎPHBcdeYmqpف(d D#NbYHPf ǶҘ?m+ߡ %{Mrw4*35_BV0nd{6tX5h@.pQ? P6Tpv7ً&` *tl5`Ƙ,+  s4\WLzU#٧aB0 b̶CޔVٍOu^߿|a7QHaALVzcX-HY\WA2gZBqC AӸC wdhЮ xi@Zw̳y@n` m##m6K_?ZƸۏZs!R**r2t_4z*I Hp~ -N/Qmɰ٤*&-@]a,%:ކ"y~rf%DZx-Wۯ(Yٱ$}bO}<8ɇ=؂Y"^w^Tl.+GL?-qdak7|_22ape#H0L< &]®\z32;c=yLI{6X'4[,oF8ӀvN;qFcޏx #;G ܹٽ;d4&&|9wE/1/ "Q&eq|o$sO*X :CjbYn_d\[!q{/鷣}찰ۡ$ʻmg<~#~B^wb [mknzەd%+YJVd%+YJV>JwMǽg~O}%+_Mm\*$HmkBF)3>mkTSx][sFdƱ%dka_TONQEI]8$yqjkcKVsNwE D Nsst͸7ؽ?N`h7Wl򰛎]؇05 }LJ&N~n#-"ڕvEÓc_Q-Z/d2螆 xg:JNblOv/ l`w0Acx@vG `Dv[o@s؇cΈ5GC&]m'+}=5;S/=hӃ&lLp3Q 6 ZŷE3\R6ccng-Q gphX=XM1Cǵ>>."B-md !!d3ɰ3>D!8e1Ҏ&>@ eHPacP;|{?+~j99|?hj42-7[l!I|HBj KˈcWoНAE dI^.N EUv@FXҭC3:CE[D9M'R;jV2C]@^֪/WϖTFϾ_p-ESRRQ|{ga?ܙ!^o)@c6DD/Ì5W)e3+}F M'`@id|VO(qb E4eq0h br^s R&<2`]6LYicH,'Eө #gc2j25fg62Ѓ|&_HnVL(EXbȦǣJJѲLZ.Ceh̔0-2AfJ2F1}"h9ۢh3IatY2>G+5ϵd{r`BL)G`頒/MXI+բgZ1`ȆagzvɘUeV֠\e,XF)J]*כd'_*Nqp u6n89 b=<*)VjfjrP#T_y99&.z cF+a > 5eD?9` Jrl(Dn%[]~kEqͰE[B(E8L>` f5Tp1ʕJ#&b #HUc5juf 4|N^C1 *)^KזѯU S3@E(/ӬzϝK*Γ/\}EE-"2d@ a5|>aIbܸi 7 N/L7,2 .F (SY߂' V Rfp7ZmV>9 Ȁ<̯6+)%E4 $,MhUoJZS 1y(dմl|:F aps:&%%UXmdz @KDXYbQXSZ9#*ocN3goz<7Hi|*ز'*hdlj ^?];|!%A\A;T }8ٕJ|7S,,q\<ױ vf4˜?P*HiOArMf`7$pr8[Th!>&لl.,Qf"ZzEX#ޚLs:aR*M]6%"P׈,^Z-q )3irSe^q I{3ʤ|9>k by79lcC=ޠK  56zzeS1W,UF_f_tńAo'ד]{N&&C4W`+locK&lܰ臤`\APմ-6r69&#ܐ-f#sB(ZeوmH?rVٲXL%U&JnD2spb# ]dD1_SQUQ1g EF;M)=&_iMS?%;l orx?idvVӶȥlBǞ8BKCJ.f/(i_1p5rfUfR2 23)-*/v75EB>EQlVVEFGbT*/6Z-&e_e3ܩv{&ݻ8ElvFaƞO/զ-ڴe1٘-„/il2ÄgVҎњS{68NCz9 ŸS|\Y|BLO&[mGNhF.+6vSXx:˗%hí[dj`Kqخo<)6zYJo$I0g~p_M>QzbKcIzXRLY)%tWB %sЊ}U0DAPuq F|f-Kt_P?QK%:Z 3 uQZM?T5([>׭;SsQY7ѢFZ@+iY75O)L݊Jn~P5; T t ))Zw9W 2UG&ǐWv[fw%ŅDh!v_Lf~Jdzμ#U[\@# ʽo5K3ҝ{I R5O׺g9Zڟ@dJ6vqVP/~38eAMXJul8 j&}oOOo%eΘ핚\gǽ^f˭HCZԌ7ʈ${>'Ю'{͸~H{TLȏS8YczV#Rz ?aNM?eN{$'y avpNJI i5 عҌY쑚NSg=.tS3z^7,#ptٟ^j2*垼OkpLi_DetrCˏmu 5S>c =@ CGv-mRC%='v2B9璿 .;9saKeqOBoh$_3O͗W`1UǗ%Lߚ)?z YYs[3w~@[i ,"wv!=4.άM::k? kW% 1N0Ϳ~9r'vWb:);opvg7-C 2t-C+3u 9Кrt-Gr2GwѢ1mݲs-;q [疡[ne薡UڽC3AċO)hO8rv-g g 2t-C ƭZ:nڣ[vnٹeb2ʻjvϕw-C 2t_>C~]5Cʻ[n9/on~c+Zvnٹe疝|vʻ5Xy2t-C 3WU3zk9얳[9+Ge薡[ngۯ6sHeoe =}Qbkíe| }͆-q5̯Aoή_S3G818bJ&2ݤ1o%)(4IA V_*XJ \~l8~qmhZr$Vt9i;k?ϊ4p^/h<:JMہޤWƃW#0N _gwzHwC]5)% ]*jrJ|ǴFD .?Ђ|8c9q>#?[9+ii|h;ʸjeM"sp7ZX}Lr,fC{JA,hLq -'cs9Ϡw(x}W)sxx֟A3UUҒYց}-Qϐ"KyF!vm|;O5wE5!ye&XޕGa$ Z]RU}L4$V.>KzށlekWyJ,1=5|k72˘Y[̾%e_`?h92a#-+UQA85_-Xz2Qu#/}O-!_bgc'1*n"]Ђ]@j2/sF*\ϭeuU6%z|[F6gg[ HeF]' 1E|r !e~A-Ϋׅof#̞ mc\u$@UzNxT+IYdQm%4bQ6׉hⱏlmW+J=ILn3&K!Ƚr-w4'{!`{?֣JgV3"U:T.yF" ٢x ay\s+~?XmU^Q^]3X?m3, , 7 ~hyw^x|:p,Gbazx4O^)GCv0:FW hK'=1h@3qsݦؾï;M6 l{x

钴+hƏxg_mwot65!ǻ2wvj?#x?q~ڣن4\ | 54Z!BIdr߆Iw 6'DDFaEU^3. TEDNjrv( "\GUHDUEBD("m/"XUU!B~a\DHp*iv~r*m$" Raq*H"F"Ұ MU~#Qa)ɰ2T,#0=AjDFp*lt 7UF2ϰrUò+dTy CPщTe8dxA*8UʬW+Ti$#APՋ[^ptᜌHՍ[~#alBFm}Ff}F3/d'F2>4TvOA/}v 9'v@عi:si"# 2UF2C6h4ɈHso&EhvQ|Mϰ U1H]?KX՟;f#efc5*>RUpFv Yϳ7eȹ$]eٹraN,Ut?nYY4P;ZQ_/B6 uS\d(U_gMݛ9+ )b[6%?8;3$l#-N4q:ܦY#f﷬,mkBSx]N0Æ WM- 2*5X|DZ=fLc(F? hҶ BLrGpodgd/e>f 9]WA\\Lf[xilϣTs1H QlHاEt(SAZ_Y XmmkBT~rxݡJa]q cb X ,C U󄧿+|u8;U=ߛ[=| Io@B_Oo@GGGzLo c@iz,џ/ ?NПP$ _>Yz??ǷG @њ[M}VA:Y@mkBT~xݱ `Qhhj $Z  W!1M~K7mQZiצmviz?2 ? ޛGGG7?^& ϏGGGGGAz????74k >QuimkBT~~xݡJQa/EAt-baip? 6AP֌+bv* N:vk5jjmצmm71Jo@'" _.7?Jo ,`G^_N?7?????Omצmצmצm֎n vПPvz? k[fz^z???ท@Jӿ6?hmkBT~x-JQab:&ʸ'jE,ÀlP q#p^pJUSߚߛp@zހ?|757x-fAk__[[' '\7 GGG?_}ހs7??????? OO(M&<#YXrmkBT~x!J`Q"*X(V4-0QA ܀b ds wz= _}tscA5Mncr@iz? Ouz?O _]7^7??>Oo@GGGGܸ*7MnwӿQzG'˭//6& C_M/c`l<4wmkBT~ܚx-NCQ% !iB,yiRQנ00$! 1(\#`&QnqX~';vڍG5jjmצm>`G џޥ7W]z?sׯG//џۓ' k~@b3Ko _7????3`mצ?.G \mkBT~bxݡmBaaF!) 5 !L +t&t::Lqyuǜ0jQSӿ4kӿ6kӿӧ7?ۤ7yY7Ko@GGJӿ6kkez????3P<`kz?????w wiz???? Qbp7mkBTx-JQa?h"lNqcm\Ab\;9{{?pi55Kӿ6kӿv:Io yހO8џ@GGGz;Io *`G/_?_7?7O lS_Az?zJo@GAz+ kn5`Zzvz???=;Ho ?_TqwZY)>mkBTx흇WTY9ٙA2%$79HPD%*ID 9a<;{6ͻ{55 9sfsoݪUu@m[ֶmOӆ Çɓl.^OrFm~Zmbb̥:QvlmX~,֒8Ǐ@iqoϻW‚Z}}=2y!02gkmMԮ_˼X+Φ8gsңpyo%av'x1+!2G5l |r~ 7mKX_p .1PU6sJMk333ZkTvm+:,#kbvI\m y3PqӃTGn.im:.&kbof낤9B aǡ0<_7 l70^[ZZRknn`^D1Z LKp szJ3GSjq}pdN 0Gn t9=(X2~?ALvkw&ч9 VJ37t nkp;40G~Itv*.)Gg?,|k===Db⵺B1p2,<*YjBcHNp74+/c`iT^.3No0^{@V"U00syU"=Y( +q0204NL;fl m7J?^|6:: Z>V#jxM^ϟ,smuvй&H.B\} ^2g4OSyLC d [A9$ 6˻k_4TWW-^;Nk=`OtjFY}wѾ\%==7qoDi0*ÝѹQkȼh pFB?={VNU8^i? 5>Pq/o}4sT;m~ΎՈ&V~Ѹ)VwJduBnR$dw^W7^c2pV?LkMMMk 񚐦&]k.KZCu7~G~Y;F~j[AOuhZ9Q&Pj NҢpnuk-%plHxT=xS)c3B^ޯ_D)dB_s9>ӋOhmIݻPR$7^o^A|=o";d_ű#Du NI3GavZoVVrz;xxMYǜ真z֞G=$LЧ_G!]OsyEK+SClm~Iڵkk׎azn.ïDk|Yx~zF)1߾dY{8 xs|?x!6w87SOÞga߫-//=^k(KkBzrOCC'4ps2\,,Az2=c3^yQ3}P~') z?sZI>9uww;^'`0]^AG=a bu76DZg:QvT]q+w/tdO@eGŔ`|:J3F` %;%Quɧc#J'_'woǏ5VϢ@6=Z=^åh+$Oqjp?3ràⲋL^s)r: {iC#H=fKuA#j w%2L622xmT9s/`=u*wbcz:N&{t2g6Or`6Gu`OĚ?ko޼Ygv{+ 5-seW w.W]=~x=߿KS!k,2=eN:Aj|d1x(6-H+#z^Uq_^˛2^#^:R~ e_b9xv޾v}ꠅ̞#ԕy\mU2J〺RZccl"s:ZUf9seq=ISNc~v'^Zk6pq?x6꽠=;A[x֭[.*T-9"4(9\oQsX MFV/Ne܃Aˍ㵻wuuu9^k-+SM*ޅ.πs'$\`!{&q2;B@B`(ܛivHt*F_~rgyB}w<0@c|zoQY4sq6SǚkE쯼Jr^;YO9:>^;;+բ!..233+w㵱Zxv_WO.Sp9K)b}ɭ뭼q>}W?G`{E{vX5*f_y:(^{a?**J8EGe;~ rtV=mm}e[kCBdc|u  4ʟm'af"q L {W"!-535ŹQbpR%K|y2l 5%sZ󉟍3ޜ﮾j>\[Wk0Qhځz?ko"\f.4sp6pdC?. Pƻ0/9:0־ԟBDgt<vRk.z}}vn~ \`>~aK!'hp|g(^#Ȓ? g xOa= "ڍÝ"#%#`HO޵̘;_O|O qY/v-,H_ Gv1p4{Ww {T~޼w%dvdIz~6˝?++#8kQ$R"!'+ k13B1YȤ^ +5%Ѿ#yv'|䈷>jrc/nҔ%}.qoCZa'Ԟc|g (W+NA dAQN8dPAx(y-?'.uX='~;n燊J9Us9FeKRXcWvW=s_OX"}bC^?j-^V  )^Q_^۠9<φi*X <ۙ ԥ5l>m\v}=~C"};DP bGa e➗%$^l}{38cTvN.5Lo 1$Doi8ZtzAcL=Q5}8/ggAia,6ҟZȆqΉxssbO%t~l}E+se1{c}nk8H Ȝs5Yj=ǻҺu0WDBgK̝Z9N,[T9@B3t[cO<#t_G?AlVP m/(yu\ ka;+U|$|FNω_}1J}~XD5[1AM87s CPY߼aU9dW8G^Pyz|Hx3?{2+X)v{} lzk)s9'r 1xN;'W[Q8F̔/;`!X>2J]D|| CI{_?K 1D1L[8Ixa~-ȳZNL]Mκ[{KjĪ(/>!>ns"X5@ =_!hTְfo,tF5@k5)_OۑsZR@?X}>nl=ωzD/iJIk)^=LO1uтdr:Ggl6s" ;'ek{pi1a2!QYZ%')WH=[ۓ=]Yq>)t^B9N8pgiCOg~s)=k|z\gB|!>MT}_{oelɟN?s5?IW8CqgO ]pX.p7HK?D s;멅z+=˙_rU˺!6&3a5e+fP'}KU$y;@x~Hw+nWwƇ;k '_y >b =F6-֣-w4|v;i/tMG7_}?5LVw 8'`VZˉzB~]{(#EFS6ޏfD~>Δb WkpnEK ,V7/r'`siE4TϹ=8}~™JX+Z+t(z2&[lH W1[Msn:TGbğ{~G?U{ ^m7L%>Ss0{~~r7HX> 3]=X{j=}?gY=/|dv8SS,{MnzZs=sb /TWWZj쟻1Yow ȶ]Ocf]нΜā>^zֻC@36Z5$Չ{l=-˟ٿh/ COMRC+vwVp>=1u^8%Nt[Hm iNmr7HMT?^o){FZS gs9Ľo'b }ϔoIOi CoT`Ϧٯg ,-rv'H`g\!~46å7S9m-EOV4܎hh)oB g٪hiʮFz`l fDq< zWmo?+ya Q3s{PvvZe b&E\ M ԘzBrSY!O(lay6KߌN}p$ 1V_B< Eg~s ,~{_w@T{ nLBCU<#TxoMdӥ#r~"tQ%ptݯ7P}+F4?@CQ<='5wzroܟQ~3"N5f9;/9I{糳q^;g}H:>>'Seg3}f}a G~k-v/O-A"w9/Sã\s?'^YgĖ8zNlZJ}`)FMsN^3IE$wT7zz~ f̌o!Ftg؋`K=N *ezxۢO $x{Q_Ž9|l_z̝>{Z>es@XuzT"z5,?w1QYlu>3]qpaO{{Ǘ{Il1=XV]_PD50e#_/Fċ"ovTe f)Dϯk{~D+4˟-7P q!&q?{7I\gR ~؀n9 Dǖ#t_NrOݠK}~$g}-fαz?,7Sȿ(y|6KP~:p;_Š};Y7Q\+f(7 WU6#T_ q-J 2{aHuj/bzչ/8#h=6ԥl@\aV<2z4}l=HX3|@vl?Iyڼ5rAYIp{R`4 n!>fVb޸oe ?}J8DYs$i c{$.&gnzHz`jc*V1~R{lade޻ߘ*J\J evxg7D),Fg@#<gTw/y߻ 2!*ܬw>Ӡ=4}ЧoVH~,AgֆpyΕ{K^#Ms?;7Mg ʌ5 V|3֓b3MpS< >bV珺җ3b>\ͽ ~&4!?0u`/(Yjm0_o''^8Vh :KstL9li»ߵhOw?7n/ܑ"?vyN|k£P ַJBM`;»n&;i#`z@wW}ΪY*zuE >W]Fv@MJ }q{}k@OΌ.1gDD гO )5q^|}[A{!!"?FY\ˌk@dσ `4̚9DR^gF^4k|;udH}_ 3k Z]"mRŒ! )Y7?H}Z9)V4WW%fF?<-&{bׁ+t IJSn4C2"j'iJ+ |_y^qP1eÀ]َ-.ls_j0_Ns_VbCXo>0fl gznCőD8{tD<ecRl,Lk6P}Rr\v7손 cοD5`W"|gbs.~oGH!ᛁ?[xsl]̨s"*y?TTE?x*kp&c"v5P,gQ/!tأpgȘ-$dzǂ;ݛɌ_Sp43,1Mw\ցYKLͅ\{aM(\ Ԉ]S}%2sCS ="xf`/g)cbzmf:xJgZEQ֪ 8FـQro[XǬ/_'(?3Q-ʡ9b8[ҵ7u/ |]0 7l6J!MuXoK\E؀rL·'GՌECPuJ*Mk p#k@jT"4cE?l nW;Rҕy`)o/v^i_nfa8V=}2kgJ,fe eR>5/)WIgT= ]Nك ?$µkQT0BlQJԇ򷱔uvgJu' grƬ9OMq3'uYx.0s&L91_jlmFl+?Yo#7M_9t(Ǹ пG_gE3+CyτSɴ=C!+%tA@.wO_QsIgIπ6kmez0)_/(0G|4YA8/׎*NRNF.Da+&R?s+]tb  3嘸{ ) 22H^$jҒ1kZS~R`X33/Z9%UK[lstM8kB7;lw(IAdckY-bf#{JV-]0v77mײuP|џ({d?=/?I&j53n1o*ZT>ew#,WK 8Il_cs+E׼NN@фmO5?n+X^ᚐ?;63d{(:@3.93"vM)#~F|8z sP>]`ώ$Q~,ң;t ^:` c.^tV!g |Pw&F./4Xw. 2? ѽƐo Qt\W-("GZ5 ؏5HU&r_GV'mkBT!x}Uw鵶e13%uleY`ٲZ3@&&$9׹;\dfKVVUNY[z7 ObL I{ocj?+y0>MNaMFx+nI[|B詏`uG>$IDy -<"jPZQLg پp;vV?:|<*9Řf0CXh{ u_Zl8 oip&ɂ[e,"8\IS.,.$9z^ WaVp5Y’\L8hS|rQ_v ˌ<̘)JM44|nqYȟ஽l]D\jSx*?3yH)daRlSZȆ@1k4ՎF nW:<|_ŰLzaP5[Pˢ͝@h: 0M"݆̆h}7- A6 !1^L-_|zH:áy/j | 7qlu>_E2`MY|'}>etf/3x<i:@^( M4c|c}&3cC%zx2Xu{"}? vVT2rp6`=ړw^GAfD:sz;AӵQ{)7w18;Miq(2B2c TǑ=*$?ꀹ2oH\s#E}-Ǽ-A&*rǣtb4YcR\1c #3KǣFǓ*D>WGA;\Y#m+UGAvT܌}=/lʠaN{+-Y11 wNx1U=<72dq_ aFّ~*T7)\,ФSe~"|9/wq;WXtvCv!<6s ?fNce/ =d<&5p+ʀ{Uɶ*Y?{}xmЇ`J.X*{+po֘,hC΀̿a+ L$9 %9R+Wh`eۄ-z"}9.V?y|b="R:Q&Va` 6kaL /c^PNr25 n#;g|RZ-":/g`D>1'\CO{Ko7gD߱/xhq\& lEb,cH:= tug ,d`E #ߒ 3:9KP Uk&/F"2g:1n$֪sx?(ڌEy0$t}[{ϛ5-Yۑw'-@̴ P8Spc& ,"дږ wïbυnSb]eF_L"3һ r9Lj&uuBcG3`q?ڏb3<$#2%2 FX*32Q9ui^rg5|z[~Η+N tn଑eeX-11 #7&b1D'#Ax:Oe$_HFh ^@@I't&*Ő s }+!+d=I6ZHgDd&T?́P('?!A/"b<%#sx}'?삊@+wdWWkQǫoKsM^ڀ2qb[0u7أ1|<ߟmP(k#>6A6eZ(3,NT[-cm| =v}#Rƣ>m,b/ecwO!^ڶBcL9k\"66b= Xsc?OVV)gB?؃I&p̜! &9A5;`m[\emm0OCM2*y31`;K`{ǝ\ uO>ag>)o)x ՎWQ0M?fk+裮R1y$xzQü oI+eyv"Ф3$Ϙ2 >T̹pS VQC֌X3S.|Ȗ7c끈q`B/lLAoȚr<x. hUw܋yçz0ƻ<֢leh2=r&Yo<#FOG=g 4v9U%/~ZW0X50CxrB1Eb ,?kÞߐ!欢a/\R0G#"ﺃouh }W͖/ưb;l ?uT9k+'_ Zk h1ل+6/Ę5Yp>|FGy?gF}+_ ?_nFx#Up~Z)aMoh-.#o(X `g4|l9r:7_tߒ #{e0{W~8gh~!`|ZV1 {X*Z#Y\{Aάo/csNek>Bs5OydX} _@/&ՙ=1>DKͫWac۴&|"|]ivetuC'OB/`]82LjV#eP<}CVT0JbX[d-ěfl tB0Y>\*{jt.B ;Xtx1CB>{\IaPO=r57(z&N?5 /~ |_ae!br1 kr!XֶX 6kde~ol;BX?Bc={ s?RALFGtMelGCZ>/8KcRӮlZ\j l2^D9h8׌ij'YnΓ,9=b\#` Zf 8F? 3?m))I}H KHVu.~| _ox+uW\˗/AAvCqJ#(nTh4CW.;ۙ =ݞ]\e)>^+?+2SG0{]dw 9(3|j{ax >XZef!h`rO Oth4aaa:V2GmT.r!d)< \Cgc r NV;R:'5 >G!0ESX-= 𰧀a' ?Q'Y)@qi'8Cl>Z 9 "HKTXmpw @?h[埫_j A֓/4]f\D@(Յ) Wݲi9ĆA%T|gde ׺v@K%%7[I&2-Gtߌ}#^_^R1D0N\}|>:`D@p̭//CNkr#I'Xod@ut0e1;_GnV wVZߤB6~XwBb#G"dfJ#L#0?#92?l=k } Y@- m9cG UJ_~ Cmy/γ 3P#qe -a>("1A}!z@({ eQ6l*}v{l<7s%#Og 3sFTĀH_as_;5R\Й?L?ng p?Oy5sZx~(ΈP(žz7 |Ú?μ2RٿV[VdX&`ߥbbOEq뿕RS]nc }3&'3@iƞs P|fs\h{7T͐<3/f=Q,m;~O\-񏻫h?39T' ^ :-g9YQx9hpGqYgh^xQ?m=Y=ڒg?㾸o=91B8ڦ_!OyKC@N竛?KgHfUy9S?)k9#l}cbu`?-&?AT4Zj;.3<%3{V+m@F}4|sNl˜M;w;?A?)įi{,e村{,ɳyq|W%6kV(Lw9)V5 C?}\/(R^/-5S;9䔅I@)WͲIsB&BaN8;4?^ Zg'ǒv O 8YRk@l>c܏8#U|op9?inn"P2;on4 ]<,0hg?YNOjlӝoI ]6nP{Ahflǒ| pd}]B/<ؘ†b ^ [hg4cCk*?nn>2!q8wMc##>e瀑? ;N]cd.5Ϥs~-_8n=VE륚i;#zE8PQ'~H cq `! uPϿ-F c}DaUxL 0s]ƽgc~Ol.IM}uJ* ܫe p?B#-B"u? |[#9_'c~/@Q$zZV ¸@B>Ԭ>О}X#p/xoklD\tnH#5׀.J ?`>t>x[p:ƁʿÙ]?5eTX,25`n^ ~Yǯ^ lݭ`k=wJA.@4'r6-{I_Tnv!+hܺ=s[B.Ac' -ş_z#?q@iAϷLc?r@n>~'H;9?aId|#Ooku?惍ƽ"y,?#g'Jo ֽP)s.-f>6Un)]|~68 ”!~?s^o̳d1XǽyRSD=9b8Z]l=-7kP^L<t>l@kC 02jan~s/Hr3}h轭#G$ݟjUqx`+^`?9F\mj+(?{\b4Yo=xP@?뉷UǾmj;ٽ\?Rߎ5`Q ԔD?}26Ca R;Lo!yJ3]=6rs?+\åK .ϛ2lO10#ݑb|=1C-5Y#>6$#b]0??>W>&*>:ノ6|$$ I<$O /ΕJ/ 8Ӝ=Cz?DS :Prxg>\׿o~3Z:$(p/7P%kcO8Buk1ƿ,w>0b= y1 =1WWcc=cJH(Qs5߆؏'r @[oAٻ=C䬹%|!}%u~wDݳ[$]G`?!b9֙F|qOc]Ze7jN_ UܳL><.Osu^ 9%}~<ӱـ'>磄;_WG&roΜT ` ~an; u0sahz?!:>|3t_S·$a_X>WKo}?7}GΦ'i?e< 2!RǢ!i"ӡGD=cuP;_??j+m&{mkBTACx=JAQ*6cF!ڙJ7l,l,\;w=/<006+5jjmצmmw@sހOgހOmz??Q߫N7?{;Jo@'<`Iz txq@oaצmצmpq@a+]/` ӿֻHo oހOG6kJի--mkBTRx1 !A`_ h8k~-Qncl%zk[vG~?3Dݠ mkBTWx흍8 FSHI!)$FRHnw HYx3ꇤsaaaaxIǏ'U{o_ھgW9 o'GW {>~Jlo߾)*/N\ϱov[iZ_ձaJΝ/:6O- 92b?Tlk%?_21B sY5>:>c=1Ow y^- ڶ,XzusM#גU]>H_yYv!ۉ_mi Rus]Xm_g)YY)m]y,m z1aaaxEߓGקo/Y\k6xjgH|yu.\aæM&wk#ϐ$?]Mo\Ⱦ,/ڥQ@~6s?)}, l gX #vQg Bٙ^uのuhm?}{].~}v_J;xogJY]޳@.)oqC?}>@Xߘ'-(W? źvƔOʙRv[K?[A}?-wmՑ}g\=c}M ggg DŽ-B^k_g?F? v0||؎=ǧHPgs/hؑI t~{n^}ZyD5XWvO)"c0vY Z|~_%/,p\ɹyΰZ/;/xs_9?Pܯ5ݻ\[y|č8gʱL{? 0 0 _k3>z_\S |<)b|7aaaxn.ta?l^Cvkؽ#~e)3<3^kdlc&jK+o"e<.ʞ`^(3zu l+6v<ï k7]/lc[`On}򚄫 G뎱zt^v2)?;Wmr5ocIz?Ozx{&!ez."ѯ 1Gg{+ҏlw<=}GݽFƨ^)zIpG K֜{{e G12ۭqiumf>.}~a? 0 0 [u+7Svq֭y΅ ?ނ}XwŶv?ߩDZۓ-q/?߳=<~#>Fk"qzrQo 9r,nY[;o:)@-`ק-7({߯S@µK9֠ɸ>:n3 _[_*mtcmC>qSL=<6;ǫsaaa{xˌ\ފpx?0׋#5zяc]x^l򼠕(f:~٣^lin59W~\;?vn6erUbS~v^U O7O(|;+SG4|?f*?rW~2oNٟS9~daևmH6mX[J~s.ym4ٶO|Bd/b5ɿyU? 0 0 0 0 0 0 0.P~*1@G\⟿KrKXs2(ߥ纎J8'>X@▼QQbqwx b)_K|v 1M6kee-2Ǜ59?K^E~9ϱQﱮYF8N?~;:=J<-tĒyNAgC \NXKs)'^Kg\~2}6}Գ)n]Or^j~"{p29w6/.z-v:+M{WJYZ굢`% Ҥl9ힶկ#OUz+U?;sd~vND7*.Y+v:ye;8}~|+ÑޅN9}{Bƞ#txխsXɿkSV/uJ=o G<ջL'L:D]6jfgLz/+ؽ[{rCMYq~[{yy czA;w9zszWHVax3 % mkBT]px}[TYS}/YL$Ifr9(A@ 9 "I2sFTLl}gw<ӳ~7`P@a={ww>U{a||5558{-<\?XpRQTT$  o߾~7Ǭ믿 7nhK;''!Kϟ޽{'<wsyvV28deXȖ\~'Ē!q8ODιtD#3-ٙ Buu099)/bS%@fF2T~",nN'={'E?+=Z>|?( NuiX !\=&_~?~Q/? / [?._{>'`R8 :S:0w>c]i^ҀvAX[YHp=M[{6`_0<"X K(Ÿ Aq^Bx,ĦG BJ~Q,T 5xpeɋG?YueOѽ D%9+zg 7z.8x 3#3iBeS>,_<%שbb_?246 A160sӅfX`s`/xD8Åasb=s'{2S} ?g4j~碢wSx{Mr^{̒Q}U~m8o>m8w l'v4n7*UpӄK^ւzJ gC.s͐4M'k6A0 0ى#;znBn 47cњ,u,v nBr^PV%\yQ~SXh݈^ۡ{ 2]D4D|>Rƌy9͑7i(iS7PrmP-*ء=2TsXеt rv3_q\DS0Bl>¿Tn,ly,cFt1a7G.Mshwos>u!N7A857w 'Ǎ:nK&(bk]Fe3@d>ڇvN.Bh<yWkhOΑ\0FdaxсUD{jDŽ.! C(/nݛwObz9#x5/dCH8G:CH6@:C0Ӂ# f+׿ BZ 4&_-{勑>|Tt,C;˼jM!pNxkR㥹Ҽi h~&ߴi%?n nc!>t/qBDzCkE{b`(BeSdOHQLyiF/Au,.5?&np!8.q,zӺ-g Oy)K)&Yx|G]' #堨2ɓ,=o1u`"rcd/au//Ƌ3o| ۠`9Ö f칏BxA3('& Fr9iE K_5#p/׆E.wP@<32 ӧyd!͝ܦ.7O۫?!l:4]f]gqb8+Nԩu%]{^[5r.y,|ז 92GO5HZLLe%kN1*{ XDРުq?i>`?CGV/C+:[ϓ]:D֜Bɇ_ $O1oZ[[6m? `S FkùF U{aW <5rHV%-ӄ}^8UkN_óqڮ?{Ym1a>Y\-ʍ-j\kv¡Y]{>:5FLkS~v߲ZLu.oDcTR=_^khPzV#'6FyFשǹ.ՀQ*' w6`O- {T/a'd]chR̀bbF'li!I~za)GxY߷Wޭa{pnhD5m ;Yov Y*N:VSHFmߋ79tpGXݯ IWrpیΛe3ܶ@w=νJzQ| d^ڼf׶Wj9TOU]O6 vGMPeވV  f SI0$6` %@~]|GUx;Liq˦1M汢pArR\ :O5h˘Uc}0se@ ?mqaۡM֛`>Nӱ6g<ƦclĕDLMt,yL ɸoUvM9i<ϽR ys5TQMJT3WPuCtN+ɔnM/3<=m0 jp- V)ܜ38o汾>OxX쁱`6.avp41<}Lg?KZ#8a!sDT3bGBl Xt ]C~F8h :7@h2ހ=@]/v܊#`L A݋L3H1Hk|pἭ q5az` m0d߿l}7BŊX5M~6Vj3#V),&Y}гIDh0 5o|}臓IH?@d#;-9 C~V8 #PXT%l Z՜Xz !Q^@l!!S!~bS8q/Lf1cS>_uLU`bu4s}S9p.FmE[rpi \{myy|O:pc$]!I Qp:ks-\Y}(IӒx^nIuЊ'/ml~Ͻ5Y词ĭ؎dG9A|:qgmK c0{ ϿnWx~~z nKY} Fpr)UG>@j(mcޥËGF^v,ϋrb|4רּ=gErk8ߖBt<1ޞbk<;k\gVʚy8G*6_ !w/c!:rI+Q/|y5TY>;l—T\eQe9@>=)yDP]Ή>w{xmUl *endj?wmR c1f4&"ʃk% _&s]1]rK?ƍz4!s?{c( Bc4-(LEPol6⟱ZO\-5wqi8-G/OX[YrNozW}\:*UrcͭՅSDz='ٛkjucK")!puq9>b./CǦ{^21H#xأ՝k|f`Fd,qǂ{Y|Kby+X*Oa~&w+e6zO{Yp% q2u}!mm䊒p 3Φ/]d^?'|ko_mOz.qJ׋3077-⢽Py =a kndE̍4&K|Et~V u/t7'\35PO輍!O /&\BWELށ^C>\lWH+uj]cQ\3 d-5\$򞝒wڂjjMꯊruV+tazt]_.4esSl_O@Sߛˏd3E!=O+6w,ggH=_T瑾ә=W)tz?y{:_5%W2>#JIy] IO.SWYB@{0H:#^i37cSXry %ǰz/ ]V zIUOBrU|NNߜoKP iSmf5yܾÛu%uݑ?wݢ]/u+V׃WOו^t]@~u=fm뺒ᴮQ.`-+_ot=~麒xJv]s_R}׸ˊϘ \B#uySf0s]$\(s|`1uzuuy L% =:4*u]:˘:ӛ5H VϰӟW+nHQYg?uO}T?h?/|g{d: { kkt=]q<=BYw939r}߈,x`J+JS." k:ã7Q]uWǔH kH׳Xt݊tJ]_xЅ~麝C(R4bu0tlx:|eu=+=Fs9"Fu6KWAK # t6'OH甐 Ŀ$<cAHI:{,J]_H9 ?oyq07WϚt=DM0t_9f-]דD?7GPq*"]ag+ƿ$:KլEȰ+\ˆ/Jl| 6\y({.#YJWdyV<:bc+l ʳE'}ω\ }PpOh}JypOh}JWrE'U#ؕursO+?[}BAcy^ A]lw~>f!79qL}bc_{_`)}E^+XwMWT.J}_];S>w&Rq& _xO\ue@kycZ_tLw@Pdx&Xy 4F Y"N˲g>qB^%j $آ,CWk):.)uXcд޼ [`祇DUYΥ<謏үe[uhc;v=8j(j(,ԍ6S1l|PA֖Yp"EϨ.{åd/Lbv+4 7]Q(- \[Ư]Vxi #sصNELRh `ᦅc?TcD1f5L9_Oυ4= 3B~ע' 6k{kVHjxGW9[0V{YgWH{VMD?3&g]\mkBTaPxݡm`ю,I!ӦTUT0.@w&lA0uLw73 g55Kӿ6kӿ7Lo@BUzgzoGGGsMހtHo 0Oo@GGGGC8`)џ'Ϗ RTqHALmkBTiKxݡQQJ  AfŶ@ GBH_AK'&b8&3m֨__a@uހoOUz2`6џ/??7 m~/GGGG8pzހtM*xHk͝\mkBTnxݱiaRE0bg!","hR V6"d7)FMMM>7=MПP,`2`"`GzGGG4kӿ97?????_7?ߍ`Oo@GGGG:?<7KiP Vr :FmkBTx?xݡJQa/*IYp0V\""Z &k ˪Q슈½{_s8pJoyFMMMyMo@Bfg ?79No c~ހ,8`?_[f _ џxտv27y]X;xLo@'a#`G { & k{M 7?????_TZ6 FmkBT3xݡqaSl P@TQu6a3]#k*>o8Ox)fAk__[;>V _]z&`g@Oo@GGJӿ6kk"џGGGGGz<n' ٿ7Giצ?!5mkBTxݡJ`]L*",-UD1Ȋ+4L0N7`VۂŴ+N~by?xG;5Vz??sހOrހO> ^]7{@,џ@篻g'~@*YS0`MM?7?xKo@G6ܿ77}af?Ys?Jӿ6kJ =7YdmkBT}xݱiQQGUbPFH@ZV"CBv MS-)N^RSѿjM_[7?}~Mo@Bez???w@, oGGGGGny7Ho ؿYПPfހOO ?ۯqz??wހܨ_7 F<mkBTx-Jaa"*Ij A0QA&܀ Ap }¹}kuPUToo@~z?ПPϟG?x;Ho@'"`4 | ?7w1{ӿ7{ӿ7{ӿ7{wk loF& Ok? '_U RmkBTx!nBaQA P  -tM ׁN~Vb8&3mZk/MMޥ7?ܤ7?ߏ o@OiNGGGG*`2џgiz????=t(MY/ծRmkBTxݡm`юmB0a0 , C|Eulp;|>55Kӿ6kӿ6^ Ow@o@m@@}ހmHo s7?????/?џ'- ϋQ@xjlgEmkBTx흍) q ĉ8D^>׻gI@XjjgiЃ`0 `0 ?ϟ|:seQ3|ӧO|:2|.};7eGFO6_Qv]T]^ˮg{>pjzkuo{yye?{-x/ D:3D&򈼹e^Hyi#/OGzϪ߯_~ :sMe#M3Y#=2 QЙ[\s=E8}E>GȩT ڲTg-}VfoSVwzV}./>~!?U1<#}=F[ ~QڋBN..+푹^edLo+[\-k dW(}6q$#?z6Bөi?L7!3O_Q}Пuo[=tkȋM!'}/Ƈdr2_Cﲨ: `0 :8o=+8-4}۞cĥXdq{bUq©ήm!ƶg*ΪU\z[GA=^+ru{LV U?)V>ғ)x|Yҁgi\yi^cUo*= !TY?rfgWsʽVn*VX#=Fϫ+[F~yH\L~[O҇h5ݵTow|Sfӟ+);F;:x )/OS yUo2e)Ve3'wgGg=J^`0  ľu kU,Ksؑ5nY,bXw{ w&3QהNQev ]ƷgcH˞i{A3I8hwduwUIWq8I>+@pQşGcZ\ƪUߝ]/:3d;ɫ:gB9R|GW~w2;fzt|+i5nΟgZY|<1NyŬ|E7k?z/k><=Α}N΅>uWydʬdz `0 *\?W8GY:Dgcg< 2+'W6qn؟{ru"wU쏘~c#T?+y{Q,,^qF/Xv8.֩g3}ȸOP ~n%hUG4(_sn|W}Tg&x^c,Fѭ+ <#+}/Uw8BRh_|33!mr\7U9m({ѝpvew[xG]߱?g;,nҽow8]וb?OV=Z_#ve?vN_WrYLo;1g9pV^G~>[_vNOS3 `0Q[ veO\k^8֔v<Zbz\Opbn$~}oz3ј mK vU]^iNWA#x딫jt q :E= z%օq)CcYEqyRG-+u (K\hP'*^ء^q=m=y|Kvūe\rȊ4={W1;=ݷxp;o@>ȘT\Ԏ+C=*ɫ|GJOCW]x1.ﵠ9_Eб Vq)v(ʑ}[GwǺ{-oSdו_˞׃2;iT&w*w:g׭SOsj%Z[~_˯d֮+w]7 `0]kIu+eL]ւoA^;=GR?v쯱;<y o$N1紈=:ߥPVu< <&3KyC/4r)i=*/|Ύ^]QNН1qGw>ù{ ?Kv:A}E:_n+{u=rq͓̳]>>d}+|L01`0 leg:׺񶊝`W,3O?]\9P~[kOWiGc~)-<w.3q}'vuw$Vnv(r52S;Wk_Kϔ8B/hEՠ'9w?K;x:x<|@cϽVyc@ۖSw8Bq]=2lBe6V}eR( VeZT4ade2ޒ+nYBTqSߔ<[&=f[|szP)G}{Zׅ3n7jpWwftEw[ǽ;`l? `0 `0 `{~i`oLy>uoi\qK|}7Svu9G쯿c¾#>,jow{ՆݲL=mW2u_8دjo?kD߱mw>#}E:OۡO;y`$jwmkBTxݯJPa/E f*&(Z&LYIE,lVAl 5½{_i ٙ55Kӿ6kӿvހOd7`'g@wހLqz{ ߽7?g _l72>Ho ؿxހ?7y____[[^Ko ؿ{ހOy'` ӿ9Oo eހL)`mUbJݶymkBTxݱ*pa"td1IVIl,eHLnբ\dsww oŅ17/v :Jo@B߿O9Oo@B>Ko@',`yz?Ǐ A _7MnwӿؾJo zހOd@d@l=џK5Qy]mkBTxݡmQaFATTt]-z\B7?ɛ\sb=147m>ަ7?o? N TӿSz????3Qe@Yb7Mnwӿq@Lo@GGGg7IRPmkBTx!aabTŠ)b(-!ݦys''@\ڠ[Ho i@Zހ/_Fzu(RDmkBTsxݱJP.%"$Z5¦@Ƃ hn ! x 3<8Ziצmn _M7??W ?_79Lo@Gvc@<`Oamr@t5No@GGGgG ^{U__[ua daz'GGGgQ_@#ԟ{ mkBTxoǿr8ر8s:;})MPh(`P H@0 b&&q FFg87N|Cry~?yn9'OZJ PR3*U" TCn KAM'Tj*5 nNp5{WǯgO}?Uds&ξY Y kK4`joH6W2=UdԃR F썆Z@]06;vÝG7^ >|1j n1Jca,V|TIptk3j ]`K ߋAp4փ?lH5\9 Vã6?O?~:PV4'_a $h 7̧\zZ`҂٘z\0<{rW ugk/4`KM a]Niry57z`Ce.DЦ"7t>8?.½ `i szMRcs{ͦ^7k{C;"O\ϸU3g APYQq/ Rys,ټ!dz0xc!bZt]Ր+Oj;t`ijjC鋡$ohVޓbf݃Ok<0vm <0| cD4u|6[*GW"o0oH󆹽f_lYͼyöݬ g08r\[T_sSBR=N3yCbwvBpupyBK.q;n(|/Sk` 5%BȘ_ܽaF(cWaзO r7 c/_8ϭ/hk/]k!?M"oy΅ U ސ Z|-|W\DXcV1 oK 쌕uP:M\΢EԞ#:w0of`Dsncof8\0B{&?uS-k`gg_QL]s֕ 1ݽ783Ϭ {ۦ֋4dn`g8eUu (1By ̍,!+߳H|?T`RdcX\x7$ij "U{А&6\Z5L &A :p1G?@ZE}g A8-&83;4jhHʛJ?ʺBbAf9\D:oǼAi*=FK:z`M6h?X[&MCQNHFV3ޡ3s]CUzcTs⾠DŽ}}{6^TzH9Z.ǠJǐ=.KLȱN!Y-fq50/:@4L ư<Ë)fN:7ڠic%,+@`_0-]Bmk]UwvIJ?rI{y,T@%! _4 5jKbfoE4qWM'8W"pneUlJc!3|juw{ BS/pĵ#KRrr@-`_yڀom -LW6pw9?}˂/9,zc^:0 <)y,L ڤ=;@O͏~CI!`]`:t|]zI;v58{*2ܗ^b!'DYNX 6њX}0_X )I\P"9%=T=414{%#d?wz7_lAPLX3x:C/+}LMf՞r.%Y/gμ'}^pl5A-x}pdX=TM{m@+̥Ra塢S#E3^|owܘxwq-Tp Fqt2M6Zcr} d?ߟ i.4Ztx<[д]ӹAYVS-QϤ)x#ǹ.g1/ A>}'Ĵ,HIK~./2}[8SL: 7!]?`ĒvL1_`-q "Gj>^eд G=6MCgRypGkIւ' wLDbu>:"8CFBsX?p/~۲w<}R{ +wڰk*ٟ?1 !=99|er߿or:q77'7-H׃ϓƗ_Sܾ|AE˗w{gT7 s!ιo{n`U.:{=M޺5 e|)'6ݸPW08rϞzZBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAem-ƆymkBT6x횉m0]HI!)$FR?6c>>~sm+vuՑνYu8uN?WP>1JsWiV_uKEϸ/rˆ_gKW]ױEYcl,[TYHT}xL#}A GV7^}>iҞ-i;}LJX&TP3T#ߨgJl e'=?͘ona|7>?ǐU%;/mN/IfQփz{G}?v✽3X~j{zTAO^ʰ>?sy|G)P7yހtNo Jo@'xހlџ3~7?]7??Z xҿ6ZZN頿MUf`jmkBTxݯJaa/e"|1V\,fQ 2X]ZZL˒MfN^ O?SvV):u+vzwg  >/No &џ~_7L}7??Y7?~>Lo@'ހO G^znz??RAzOgMzIz߻Oo@GGG?OR5?P?{ymkBTxݡJaa/ ƆI&+ (. V/`1-N>b{y_G5jjmצm{77?. 7Oo@G6`GG盛'Կ{Ko@GGGgV ӿ6kkqz~z??sAz/ ,`_Jӿ6*]SmkBTxi`Qqqqq ! COMlS$I$I$I$I$I$IqO9˵wOo]ss,Խu˲<^0 tc}av=7_{V9z}ٿ^s_y~?}ez۶@wE՟k$I$I$I@? R*f}mkBTx+JPQbIr 6-[lzA &'"8ds 8 a3' c : MnwG ? |7?sހOqz ?]7zހ=7?????ӿ7Mnw7{ O6ПPf,6Vg Ol+ӕSMnxD.MmkBTxwSםQvwyd˒%kwٖ[lc`pBRB503s=i̴MLN4i5~}nce!s~{~1vp8@U e^XÁo} 8^T@Eo]@::¤l/K5u_1AͼN2SjqfQ 7׸)twT4kKA'\W3]~Eݯq? v-YrOW U ?7?2t-KPw\T^{ ?N]d%>#,}t+dL-OkwMLgM %?yP:W l7s_ g!xAY'gOCP 9$K Jޥuߓ"LNg??G过7rͯ>8 mZwM' CrOhz~eh{vٽ */o@?V<'@S]4y\/ZtFcZP_{c4CQuORk W@Tt$wot ~9\; uF޽L`Uu_D!fyfa#_O=wwaّ }Kz,337,[ {B!g|~KwoO`$9'TfBQ5^+vI竤O5Bh g1WSto=j~G0c)8qlZ;hWݿa׼ʃ0p3^8rE?aJGjAw(_}N}5¹o=J}q.HJPBB}\#ʠp2^gWׁj >C8{ۂuQ/]CP^ z{~@sܫ[Q󾒾W}5E?ʡ|%@7'c{~B;PgN9Lեi8hGv?48& v2_8{_v* =iܟX(x1%ΞFmoOޜsCEuVYj7:5ˮJAB Ż3Z¾{{_.JN@w} +iG:+Mع{OCM`O.q3r:.> ZM¹>Ol~86@:T-16N<I9|E&X{}^>MkaUn>Wb>G5xo|˹dq!h{6A|!8/WUlߴA (+;RiX2!<$XCo t}}5wO;Vt_ZZ{/R1LE1)oYtdz|_gG w놘;|!b vW:g9"s )A{4>&}}+fg5nhe tR=Y` +wBB3|ߊsdV%Bz1L{nZ>ˡ\b}u&`+*(SG)X9ǭTU vo'澤Iǽ{?*bLH1[`wFk/_jE#+OkI9XlnR cP0z_^뽇{r/h θk mkakzh:. ^4n\wkv{rwp_fZE=CeqS]kZ 5^稸 ܇X_=!޸QOqٱ'!m${y\#Vm$%f:{0 v`ggÄ6,-|Mz93Z7}͔Q{_bs5jn:Hz%!}5yIZQ>X{3OyY<z$]px.z=a&GZm^};ڷddާX6SxO E+{>^ڟ*:Junuol~ڵd |=!24@1٠sB $⽨9݅!%!p(#ig+&տU4dz~fOo5o9w<JƢ &B54MԾ-7 =_)ȵ8L܇0n%(M`z+)v]k-57ycs ]IJ;SN1rzY׾u+NOs-u ߖ yʄ蜑J9?HhhdKGsVgd8ӂY\Nw 5rz\SҕQzݰr2;V$Ƨ9S@|=*YK81ߛ y}9uV*^1|]6đKA%hlSYL5؜O#S'}x-֪/r:=|5({3gJ IO<+xrF%L%dz$R whn_?[N1$C?۵+{ڿ ٿYx׾Nqc*,t/O'SE5 n(w žA9FC'=r/ 3Wɢkw/ܱ;~jVþ/ca i!YX$wq8~ɼwc!f+Oc+d_M:f˱ scc˿'{7.r?p}Y?<~{ܿw%nw-ȎY4`+w3W-o- 9ֿ+5+gqsC\ρV{|-3NOF^}?/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVB. mkBTxݱ*aqbD -GNI$-daTJ,&b QV;y]۟_SƛёR u*WM_7V'?~[Io@B ?~7٦7s@0nz7 _7Ύh:qn.3<T*NOO}}ҿ^<L&8:: Κ}M=#HL&\.7p\OAprrߍ$ ߘژS4JzCmkBT"xݡ QEQJ `6b5 i.~@'&<1GL55Kӿ6kӿ] _'6| oހ4kӿvznџwuz?????<_TMx% rmkBTx!JQabĤLPP)) P%Mdl7wr] O?nco3Ơпz;Io " ^7]7s@]z???3q?q7q@8Qzg ϟf _>6/ӿ7mџ?~mkBT xݡJaa/E9 YdA,a .xc lfz'W1Ox 8mwZiצm& 7?ݥ7?{ހ,`Ez'Կ{=Lo %.;,[pO"%dƾ(]mk``Yq ?Ҳ{oNQ<;?I9 Se0=d[ [_MN/_ }͢4OT0h~`Yzl!BJwsbJ.!N?SΛiN?ȅ#w3y}Ƌ1P'BKm+̑,(KzN!~}&[5uat} Fj?;+L$y2k7 =3ȟym/|azYy#N$)/,.kt&I䋯k=dck=)* ɪ!8HvN\h.6\B7c o¶kPs&v@aL(3 \e&^NNR"C2{n4I̋ 9a$6KN2LӂW .R.=}(4GnȯN} Lh-ePth&RSᰨM Qn[YI. NqcCU< TU2X}Gl-Wo:;!l L (;H"t_߭ ciG}#w3z¶cw#uG G rgo<E; DVHgGB0d,A`8볇Jqk!΋ ZŨ愜v! ;!~;LfggˋAl;Zg{Z뗓$4QוjG@#Z0Rvp~.)k#s<:%) ݾ 1 ? $s@r<'vB%_0E\ 2r̈́Uk.Fg6GY{tGh㯫bV t%ūHG?_r؟ݟ*8{ '\h  ^03mvi2 7l0l Eyusܟ`Ew0֛O|-ZBzѿz0)g&k'_0B'@'H\ st|aV+d| y}xmO Ϣ9@=y?ƇO07A>H-joUX(;*Etw3D ^ x7e2Dpܽ3 ޻bv:7PalIb8I7OφS9Mͷ 8mTGi^an%پgk6M+^zcDN 8bjE0ms~4lZS8I:LjvpF>fMbTV_}L>;X}>Ơ_Л)lЌG_-}]h}!Et3(O׉XX[9 khgXZ}b=e-%P{|6iwBfͣpNhiUa2ĤGk^1ilMXsٓZjA0^qMhWkի1UR~BS V%@pv}9k.aҖGp37|㋰g>h^zt NE)w#|AbK)sc `LbS7,|ӳƞ4g-D=+4wk敽PW$g*0v>st2ySՃa߱VK i:Z`j,bEs!4W 8&0YjYo G`g2u,}vAKμIygҾ* 4n_+4{}yn]]a޺ZUJN7)ZcdNZ藱QK ;ӹK\<{vU,ť*ɀxQh>&~aoS\ΙhRZ](8MʲP&GpWb rەX?7Hd5K VU!dX+ܝʹ ʟ˯$Add$'HJR }+`IX7%< ڌw`&fOs:!Z͝5R(b琸? APoz3JETA)sM!y$:dD]=yf\?x9أo W07I3oj_0/w-bB~OxFqJĹ^sSaM!;\? cD2?`nQk[T 8뙫{]JX Am׍g]}J>vowW1il's@۾mM6!m@RbfL \=rk%3#MO/> ?gAhW/629!:ˁ<@d lĿ@"bYVNzpuS]  (yS@=3 4Wh=vi?˂Wʛxgs> a!`(ePdWº;d"w t}z3It,YnX>Mcr>`9>Kg mQXzs炪Jkۉ}z(g0k40<Q8WG]y.֚Ί{٨^\RKi:as} bj04TϏ%E,?H~.u.|0i{:8Ƣp,9<ԂB6g:sNUN|uloKGg9G{p\e잷s[ҸY!m1 |-j3!Жq/f rvQ<Orq[sv [;VԶrŞ@+An>ڀ)g4vv3aIE|uעF]x(M!(T*m!}]O șLgxuw F91Z?Sr x c{1uqܳwgf8'5c7]v{ 8}SD޳FMQAa <gObV#={? ^Q︉9OLZs/k~%g(Ƈ:3f"(Fw=jik߾ϛ}?ACu>. .nw?7gymggl3.(de`{Z%'Y=F\Gqz/Õ(@9;͋ xCVل_ l0> hwQ2!L^|Gp/}v1{֨q g?8xѲ>Y.ɪ˿p߹6:`P7>#ۑ(փe5s9-ϻ;-4n5}s_ Q.9Wj-9~_ {w`k'ߞ#uLtϧ{½8i3[@a;q\;wm{R0j׿s!{x y:;DZאsi-![a1au:}'!*w}~jܧ j}ii+>>;0~\\;ګ>J; =1kpR]-~y3\%+U$.;<Es~tvm3^gsZ[ Io3~/]] \>D6o$$b)x]:_8 x=r"cIBfP?u١thh4T*ɵ::E~xDVУi,֡|÷bwM?ߒ3yy"qgO'ǏPP+/5iD.WUid A}a"q--ZOǑn65iWDH.\(GT^{"#y`^r -_Nɓ>((((((|K?%WRmkBTxݭJp]l2?Ғ,6eAa&T &o@EVLk^w* OW3֨__W' _7?Ko@'<`Yz?ϟ />f Oez?????t~@MMM=7?nzd3` vzmP/c>xmkBTxݱ*pa"tIdQG) &7jQbm*x7֧Kc : Mnw{g[ _7?uހ~Ho@B>;Lo@'<`Ez? AK_7MnwӿؽIo zހOd@\bc2[Ko {ހ?Z>amkBTx!JQa3("1(`hQ D\ǀEfd *'<-7[{͍ҿ5{ӿ7{>/ _ Oo@GGG&`k@yzQzoGG Oe? ?{>Io oMMMj8No t@j47?Mo p8Ho@GGG/ߛ@'XߞmkBTIxݭJaab!* /[+Vg<b,`1 <(;W Sۋ Tuӿn׭\o7Ko@B? L ?O џ9=Mo 0`'쭛ހOu;`&`~zIz???3?*Jj'`Jz??3zzݻ[õGGGGf@/Uӿn'|H`{CmkBTx][T89j#JҔޫ{W@iQ @"OԨ9ܜ]2ϓ'-3޳03{fMg}Y]@{SS'mK'shk4+l h?K6.h5Xf,e] k*mdEU﵁c.P5(4*#cp{/B`:B|bB:_fT8C" rt JߏÞ+G >IfMD^fip$>S0D#G39o*򅈍k4O׀UF w#^MX PߟMy.phMdCrNG8pw!NȀg)4 6LzF1 T@0t( :AUvK&} זQ(0'{A8 ~8W`'] =Sor7X ?}!R%Xb]9c-s4ÅSc̿ Aޠl@q Bd;ayNW: R6k>V4 F2Vkw#(̞ 'k8ˋ"ΌZ@sQفPA08Hj!1qJj/D{,/ dF;(偔hG9'sMmsT"&%Zh ւ~s̛<ql/bq-d{+i !Ʌ؅քPV n~S뵁j\~']DsMpl{ / '✔G$'E!7Lg׀}-P:EtpJ:@TORC(! V=Gd:R Z(i 5Ս k:|.H .ԾXLZy>CbǭgJ 2R'Hp%R#Q`n;HA)?ՆY2|!_iLsw%h`- l0}~DJGсvL8PqBFoi4 T=cNF<*;`|3e~M#Mi`NRA` 3EJ[!o$?`F <( "Y,©Fpȫfp4=S X%p gķGP|5 iS2BIL1R L'׶prrG|Qs kGw'j@y`Rx{Gޥ*rvl"jwLf 4 Z3i^?h3s G2a>"p0ԈL0 hd?=)f[,{D!l:x?i!h! `s탠?6?V8+AcPE$/Di]6H wt UDg93WH=UI Mf kp:#Xs )ni]+2 D k$?S>Oh~PK"}ICeT&(X7D*ރ+9o p]]}{SH,~]%?#.{Ɠy s$'j;4%ǎx&P[~ ݓ օ<´I|7{[>0W P`wGd=ysQ?y:ݥ_W? Z+-p%\E(3=\@S\ G= ']mBg F?י;ŬGv'Q;'<ΧOgբs`#zX+bPf9e#wӛ p; *uu1b3ҩH9 ?2>Wlo2x=^4nWD4oI ~QiKqTr??=Δ;-]ה1nOSwMmC&zN\t1qW$}0sXM*0z%δDxN@w>}+QROa=5|ݭ+(KL=r9pIg4ç*ϹՄs?9%/U)Ӭ߿?twuB{S}E̍WRsy=niſg*wLP&?7'9]6;Vٰ-ϦROp=?'^1'h{U7|da X]o)q4y&3SA8/PwEs?wu <'_?ި{5Ž*Pw=Z~q]EDЦ>X-cYnSjIrMЁ4yT'8<âƳR<.!w?Bo>׍9IQ@o!5 s N,oDb݀x IMywŔg755RDwH~TqS_Alt@k'q a,%DzKȗ_<&%ם!y.[N-X_P-5;%n .'z˫[N*S_[>#U;Z%03^̛Zb 8] ۡ%r:۩۽e15@]{aͰ@wPOM6fńzK<"υ{ K ٌZl1<$b677gǨ޽]fl͹jc&'Ho9aog_va3Q8x z=9"OoߚMOOñcyBOL`[|)_of6??#O, 5x5L V-+C_KybP-1_:剟L+O+y.ʿ6Dὼ"2 +?ʣykB(+'y =aj%8C&g`Z'>y"&#=6FCJ?lMXFȿ<AWG ħ@Ee,5fBbx'nskqXz;?n-[BTD@sCz9P[-&'D\Yzy+s/<`VE2X |nO%-Cc P+N"ZHl0` +`称C0w'ߪz N@];`PyBt{V9,=>ka/> V:?l>_ևYCLF2z'2EymkBTKxݯJqa/e*",-Uے  jjx Қ| o8Ox z^kѨހOW O?~nПP}z7uހts@fހO:W ?+LMMMd=No xހO`@@$џR@% 0JSmkBTx!aabj0"&Am YĀmcUpokQSӿ4kӿ6kkQzyz-o?{= Oiצmހtu77?????AvހtM*xSE͵mkBTHx-JQa`b h1| m `vN`L2Y|Oxpxm`sFMmkހO NLo@Bџ ~7? OyzWӋGGGG,`___[Ko ހp@Jڠ[Ko f=`nހ8q0_jӿ6dBףhi]mkBTѴx!na.M&#&:L YIX$Agog^{Zk/MM ~W 6 _?CMLo w7????3b0Mo kހ_?_]z????#O QTq~Cz}I*mkBTx}+(H,"H$"#X$,QԈZs>U{ ..T}6ڳ-F`p]k߅~b  О$wݓٱ|sCoA+q3lOx@(0a+? T,_7s\Ϙ^Bl1)C+k(FyN"8dPC_9>O0&l4Im+nwGrŰ)/tihf ѸX>E)<,6s45zb?J\<OM%O#(76:= ӋYAƒH Ls6MXBcX&ǘJte. 3.je(??Lj=%wZizFTx$kP8Em jAOހ>~؆B9 ֤8UKCvjbL Cy ;mj P. DkwUE€3ܨ8xUJs\ɟ+;}sFQ(KIXݛƨ 1 +KdX];Jģcx$D׷X`i @l̏rnm$^9΄zBGϞQ=nfkDe; <a>,⢞jk0B[p($Ǡp4 nq`XƓ vϵ.xHnorJ5Hu뇗 f a[Z:>36[g RL؍?( &w.7C#~B{] UW 71jk~ecGrD.=K@WDZM0倐0\xvqNZ ># BE )&yA}t?B Ym(WIpɱ |2+\2 )l8tl@Z.Be񅋍RSƃm>dIl'N adĢG3%#)?$s _5=YBR#-k"qGP-e"f%֩-ϓ378M9ϊ,_*n;HEBƱcl~ ˝[/sagIE2,z1t:kLș壋G){7ond{@rP>kwk׽ #kXfyEAB9uM4P=_lgW؇N#_nGpp ,ZUu6ȓVӰ0EK7*|]{75F\ԶzQz! uH>upT٣o3P)[^6` -d&*=%fY<^ط`_6|h3ء>2 Pq7ώ ,NsjF=B` 큳CiU)R鐏@LҮǧmb<2FHRqùFXi䎲OmGA}:*u f:@ʫRH.66jcGOpO- 6HKJU:Jǃv,3DZEƮqq7p?ȌK%ȧ$;?Qr6pP7`a^=R_)m>D3#£ _' Iɭu͋C-Rne㯄ssL<ȭ/R)|Lt_1Lk=rr 4/gEr~PnB[\g[{gYvRW' {Fem1{ wL;7&$xc0 n&u@5sCCձm8Heft x{q(aтa?Q%l4ςxmWI׆GC1kQ3iJh,KRO`ʲ4)%b6B8\pe;u)ko)#WSncRx{[sXv195_0Kՙ7>Tp5ٴl3S"؝LX睫[5m Q="u}pϘ*xbՉ#iM+@Z! Ϯ~jYݬ$?5mtu] %@݅:4h8ۃtu3; ΑO1A/r R*5i&j#Y2:$Z(ad@>'z L뇶6Z8|`6"X1_z' F-я?X^ A:?1;h/KVB' vOnFS ƤQ{=kh7MwXQp\v͓O/. N3HKRlK"q^Wh1wt h@3e6N|I;y?8t[[! $,ήLe"z%IކAkRl!3u8ځy?_W)AbCO!rza5Sn֗#<43y6"R߃CQ&>[# BHǽ{vekOTlq(UH͵h ݔ8,@tՂL{p/*L"d_y k,4 G̖bD>,.ok"D;|7[.DCA#ilϟI֬Dq]+eE _-- ڰc^Lq1~CCC9gNH8BkhJ#Z-`VoMa 9r$պZ-hkh ?C$ ^tď9d(8P݅]ڶw[wl;dn׆oKd Hބ(DInI M_(5)6H/Y1 QRk,nXHʉ?>df&6^EJmt{CCc`0ʅv5x<\9Yc}106"״!֏9dl:' 1H"z'7QqɌ#KR./CVgQȬ\ `?d1yuM6Ƶ8ZX]8^pwQE &1frRKi$GݜЕh3'{;;~FK37ku<pdʎ+C RMzƏ7)nҀ lEGyl:̑IoBS%|ЕsTulebA}Aʹ10A{KʘӺtjdLI=r PRg_LbR Şl?␔)![Fo wi&k^CV(t@pW2{hxHGRn͉eCbxԉ6GQd27\ثdS=\Ff*0ۣOP5(rZߙxQZ>~GAeN-jY7Ҿn;n?ӹ"Px}/NW:݊&׾:x" ꭥу;R펔 c䛅љElmG§a= h¨BG_uYnZ쫭FYs U"zM&:Gnu.DX5Xn;}ԫ%XO?~2&Frjj8 yA*W I9/ub)Zl: s 85J>~iI3Yԕ;:#hELם[ROd^GA˩f~Y!En0~/A Km>^WYq"<цF*c:xw|͞w%ehRgd9̕v3v Dgh>>?3hYDkgC(ʹƒԕSԜ| 2Q94(?OGQ34 fccPopTYaW(>@tX4`LGٞpɄaŰl\[9c26U M6f,'C4i?W~psϠ?kAKrŵk@I|>^xs?\`,D̒5W^w DMXf_8<%|8_왉pP1Wlm߃f?4:́_Ԕv M;k:p_sj؎qw]$F}y ,b'N=o0, ~M YR46+!}@~ujctCP.Y(x׎z?70WXFܣo3z0c8RGg0 TU򄽻w"/4֏CQ`[{Ocn]+{{ N!33+5]qpj' r9FDȬ)~: 9Gmx2-?sraG"yvUpa;Ră A\& ?#n 0eed~oq嶭!!DzP^H)>oȑ.ļԶ=Hy7S-M ?8ycߧq|#5"2Б lm#UeΤVbM͘jAc7Z ]> 4gb s 2WRsKg6 's8qzTT[R[w)I95xWj #!nN+zPڔ KgTE,?{^RDݥ=Ru^zîc&D'i74SJߔ&HUG[crͦ<׿~4}څh;lpAZ%XZ;tQ?yk1+Ƴu6[ Dc4Ɯ*dB#!}e>samhG3c^8u9󼵕⸈߂UyB;f "Yi=D =4&|C3g]~WgjhSIXU"1A5Fr4{AljwTt6</N \Rta| i>T.Wo>>xϯY{緷m,J{gg}v~)]s!?wXGFl!7U|Cnfﳅ:.@mq%臔Ru?.:aBֺE#Gg'yXDuSWNJD)21ѵVagWPqȒ s?¶@g")s\T{f3go^w:^"{d#!φt},nyWFKv„X4|VB~,˘_&fjp/WԍwaO H 3I`u1ͤ+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_Wݚw)UmkBTxݡmQюmB  @`$uMIA#H@PMKw7(PuӿneOo s7?4 y.?.џxПPu@8Ho@GGGG?џ7 OCTM@ ^4̔en]mkBTxݡmBa4h( I4XNрjgOGLv#%CZ5y- M_Ic=*G iL2gYh}=9q: 0Gykf(Ǐ);J%J~WJp t|=33Qw9(U6/*Vӑ2wт/r$a3jԀ̽ޠq\Fd% ?]nƥnaM!C''#-p>'OX&?`0{-悓Lq^1#Ygs"}rȿ_o,am* EIi#?*u~_z\f'y[]O,$ r2w4ga]16i3_#ҳd /q2;dt&>]*L)8_?/ęqm9ՀG91 ,?eu;:s>wXow%ng w~a˹j2kw/diP?ya[t:&4sm6/gWu3;h&t,ȿ챝k|v^C4tol5KLGTt~5;ţk|K[1x} ocv$ $/`B"4NnFZ;sԡmwLFɿ|Ay±XT~US%6#eh3ռ9h{­^Oh܉HT|Ud~!U#:V=s =RqΕ_v/:˩{A*vZŵ$ȝ#ο+_^ _Hh.*.hgCOru/%T,^3s \?ȿ#]i#9u:UH`gݏ9b[bKzYVXg]UXSՎ@ BdkS\gNZEi] #ʆ>ma޹Y5/$YT`Yh]|&8 Ćjݫy;}: _%V îptFϵW( B( #%~`/5>#C<J?_gھNe,s`VS}c8ԉ_jBZV>iKyNJDjy󠌣;4-$c Y6{<տԘr ?CjX_O/'>Fn117pY(_ܜP/kZzv+aerH~G;φ 6#TIPsR䖥΂Ώ)LP$@zBf2%QXil9AWO>5iT9kX̽;k~Ɣ֘?!Mfs@}vW C_/!ɯ $%NB2_#!ɯ%&                        )w"klmkBT^x1Jqa⪄jh"z OM~B ot^k__)џ@Kz=<7?7???wxMo w@gހOo9C(`sހ\uz?^ܦ7 ]7?????g4kzmkBT1xݡJ`a/e(C.FM&„i6ՠ4,,&W^y?xz55Kӿ6kӿvހO0`W? Ho@'s@azo _v72Jo ؿ?>Ho |ހ߽rS_ۭ{w ?_9]No @h#Yp?Jӿ6keP3YmkBT^x!nBaQ A Z[W%;Y9|n̴pZiצmֺ<`, ]ПPe@'џ'?~w)MJo ؿ;|7?????=_{@{ހOǟ(MS^ImkBTxݡaab F)$))|؜L^G+\~)O55Kӿ6kӿ6Y {n  އ_sz???qMIo@GGGG:꿿./4No@GGGG:?[4 .X|~mkBTpxݡJaa/E &ú$V&'Z F` V`_3xUl'Ox6mo֨___[7?:Io " _ݦ7?W /џ ܤ7>Mo@Bgo]z/ Ϛ.?7????kp@q/џ@t'`޵~@~7Lo@GGgmP?ƺ IDATxytdu'*VM6IqId-,k,3^fbg&>3q$s2'edĉeq2ؑWMırc٦$4EJn6 Q(Wz{xRU@ant~xBPu BÍ{@!N! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN!A!7kkkʊ8b|2&''!>(y<-B@{qniiəÝ;w\0:V3ހyE05d7®T4Y ߙfZ^p030<<.]Gy%<  G0;;[n^wEQX3 eq]Bqgaj]ǵq v%LMMQ!! 脜\.j3T**ԝ}Al{w !:!PZ#l6۱Wu 86p8@"TS)Tm;[;Xnbu3|rrRrZږDj$-DBяE#f"NvwE8!3 hۭWl3uH3SiLO =̗JX氺.gau'{coF±#(ILLabt#$~EÀ:!G0M@63ntTLM13@,60p󋸻F<'pC&5S$GyP@'qYܹs5^̦'RfX`vaw1[w={>d_n$(Ҵ9ܼymt̬\[Y3Si 63q0w1 $(D'0"/0p"^dDﺽ8Ab^˜uK2/6?c;L²ؖ,ہcmuvڀx75uqߏ[#ϏN5$G0|=y(\.תPVn3Jcl$PWÛW7Pm CqPaP $s71c`=kIs]K.lDži9tFnYh,hGBlB05m9Gd\~=cOK@,:Q@';BU~6LO19$ XZ{ o^wϽœ1@ DS bI`D/@8w>a a H">[|aaZmCU^q]؎qizBjV1Q-Ъ ͆Ys-WH8aO\S kr(j ⛛]̚AKYf(z޼zߺz;rO &D " B!$Bc-_g8$##Aw`t~ } .\7w\b-_BX8=iOxdˆճ ñar@i¦rN(Yo>Տ+dO^~O_y,$QߔP@'}h֭[j6[浙e03$ 7߹ߛ=B6%$ #5G,@Ȃp3cVjtV(p~㺸E~ԃL`;.lٻt]XDž:~kn ¶bV7Q/XlN_Y8Jun#{cK8w]mpX v`:6Lǁi;]iuEN;؎mC,J: [:v65T wLhy f|I/?2tsTEDSiTͷ^ǝSc@(#c`J#ē>D>Q˕[:1} M걝Tu];c% S80 s; ;wqR mahX5ۛ9zr1=rW}'=NJͬco7mfRlz*cxm|oy(!: 1b0b!3@w7߾z9sC]OVg+ե:by Wt`-_n`9ވ>G7G0uӄf(W l.QZonaK>OOi|ONN*mnZm6 FyR_}M_GjT)AD{8EEB^7,]g!:A0b h&,v߱xcѶO6KO ac7_Eԅv?Vٵ\OG1:iͯݲѰ- KuYꖉaak :*I__4zi'P@'^Ph . ޡ,34&ƓDoڭ;?KU=vd N{AA 8=\0l+ a n[Ntێbn50K. 8./{d{#rеch&e[r59FcxEP\Z-mҶ2SpK,޴><3vqq}mŚh:N+ao7uQ7MT 5,W-Ts #/~Ч/Px0+ qA ޥ tU^ߍ sqy(evRq]UF WՠO9Ʊ=2~3??Q缈y({*ͶjQ%~dH3i6̺)+o9 b 2OKUA@hu!"+)ʑX.^qJY\b BpdNΕ 0yrvj0|d8.BZoA]8B{Ͳmq+=wZec3_aX[zo]dTYtrA V @l<9Bħzr@0Q3MtZ &-`Zi?^~{^Ͼ E>ȃC|>ߪP/j3 `PQ֏N 2~dC {_g IQY90z91EEDM:Jo-a[%;5q;6tˆب ߾]4"Mkh{#9v=@@Q8}myin. <ϼKvEP5 fs9A*߽q*}Pޖ] hyf:0t߾#As- U@;]E0T_ {dIaz%|W ߋP|"EJ7o,bz*6㸮/7G~7ڨ xy!c, EnPӰyp5u!5F`"4V̂>XQEf^,hUZ͆VP0-͵sJw< HdpڼT׫`͔U^ee1W@Zd,8 sDX=0_$?$Nm e{p/ ,(b<'vٳO> ^Gtuu!$# 4M;Y\`z > bz2Ƀmfi_ qڑ %L"@b/D~uQ_sv*5,wX+ ^W^y-`p7`t8Qt]-bhӭ5**Ɍalx1q}?4ͭc-7W]ŚrCُͤA3 Dvfggt6P z_KJ> #Jc!#"FQYF?ةnY(6(z۩8(4xga j]va  @NccRUVxE+~L~4 AP"x P:,K,5=ouQL b3_FqrPXQ7_U(xfwoį(<|AjDx(!;ߔnZXϕ;%ȵUY#x79M?|UZ[K$q _3p;y,ϝxAyO?B=]P@']̬}AsX Si 7 0RW"H07'.0Ϗܽa[تQ=C|\VP*Mh@Wqg똷л| 88cnce>i\nNY\0ӻ%/(aj򂾨.x膅byu# `d0u$/qsݣ2HhՕur-Z~]R8\N@xhu wJN뷫Gmwa G؏}'0?:9 bvv7o<ͬ; <8'>줾/V`<E#P+T}]ӛmcKlik+R6vmoR4Q[( XFW!@ڇ~2c0 6E C}"\3s: h}f w)0x=,@iz_L/&B?&jôaD$Ȣ\# 7(mS7rxݶ[$"+٫u˸N:sJ+>>'06<BN+~631 `f232;r!P8 n_q<6gb*UI8PEalfq0r:J4[ ,A9o3P)8&A- XՅ:Q^'9l/2b"|^D(a 3ֺ>n| ޔnW:nW{ df:=iV//ZA=oA+Z ֙=p ͓5,@ (,6uKc>O ϶+ Srfu6d3iL(eͿVyēi3ø;58Fx]<a:pfӽlfk3v>yC״ۊ5񍥮oyTQ0 'خ0ہn|C;0*LCG( P{ bZ\ \33l__DD|e؂C<Ʊnl D(@Z&vrY4?'#ʀC0PTPV`j< LI=/#p(/JXP:?d^I"^~dk:nY(44TnY7B{QW"a<\p +5}{x~ÿ_Fn,Nf6dx5 |5-.tΣauN @&DTW;Cz\Jsu]CwV8[EvI;rn8Wst)ea___tox$6!xy,W  )|Pl,ace ;=%YFjx1ةT66v>7y^x!2 6 6@ r Gl"3a2*T(68o%/sZa[)hXw3+ϱǿzp]˘;C$f&ȜSY'wno~>C8601\X|$BHڛ/5su] mP.vځtƎuɌFR0qwƹrf\Wk/:þ #Oc ,y#lQ$ H&cHUT ]26V]]ț1D 1Guz+M,odFmzO*P|5zq\,o[ى;  !|s`H5n屲Sj=q]liZk=uuQ2tT~,"CٸN 矒.F/w@H,y<$Iߧ""@,h$Ф3]N.WP.{~?D,o4}3/#_*uiecpF횖7 dwm't'vn$*PE^ybCCQ۶Տ{' Hj׵Z9;_+}]%g#>?]`B;;;JR6 G1ALO11(^7? o~.ɏxF!H(_3=^=`^o _sa<Xt=.c=\WoF.h\e$1$CQgx"CD~LN ad8h4Y:"7v67'[FZi6`۽AD"3R#cP}S,2n]Ƶ_YACI c>d>i7tۥtc/'b!t7OQYvt 4?bciI Yz %:S뜆y}?3>h(?$rWLmfSiLCy1ŎsOHkGC!gs*{#u]r]pHPems6goRbm▁'2_zJ2BٷTU!TEF2(2qwko%!$̌ X¶e! Ffj~xv;n "t*ǐ~ҏ:hg,DZ q5UceP5M;O(2" 25DVEhƗwPW#84 i^S BJcz"}nmfBMw?v}*9㙟LBq=s֝%uUv]0pPcةYe.*]LGjaV F߉{c6p_s "D2<;GiECK#>=9kjG/l|Hf #;;o66^\X'+jk=14 Qٲl]ksXgUA%# ` 8|vwGR)M1À"v. r Z5 x~~jܶ|6pho7n3L5Hd@$6x/egx7`݂fxk~`[amDQaհ^cz|xB&@<=[ K"E@,'p>ޜ@/$2È " ٕe,bsmV=a0i@N]71{6XހiƖ<&'{w$p\(1Rԣ3pɅnX( ^\p' _w; ?ywnn<#CU^h1f6=Tm3J:c QN?hϣNn/-??q$.(j͑5`Zw7[}mC+~3M˷+Xծ&Br¥Y h|(?S[O38j{]e^LT+tbW4@"ua֗z{ap5>Lj,d1{kWo vʽcsw˜7~.0 w.h [KXyg~[?s󾢀~|wggg[OB,Wl*LlKXYFҾbrh/g?g?[m ΨxxshSQH0@l mc^jACε  Ev'YkkKqŇ4ةF ߯BDeĕi$vwuL\z 3$4trʮcg3 CF( J#A"3Hl]˞V7pzq|'%(jo{onv 1`Rci (I da;X* &Bmf]v1q0GyllO _fN]>Q@@~~ɽq= Rsx^ZˋȮ.#_<'A"3|寽W;ms;9c Hk4gyX E\]Zo{[u gvns׫Ъtоc .,b \D<9>U(~>U tڡcq$Num[ȭy%OPܢ> OL!3:T瓌|c F0jڷ G:4r .nliخkBFh bF(Kwvȑ?x1,A'#PЏģﻸ6ֱ*jSػpni~m,euqGC48s&+_bi%-l抭 ^Ѫ8fS a9$" t7  PtצZ3-h"k," W_u] 8Ʀ( ~?|>y4$F16H|¢R~ƳV-֥g?˿V2 ƀ,c$@ H0ꖉrN3|nfvOcP84WC9o@ \9p;af0&CN*K"TU0qӲvQeW5zoi]с Si)'>@(|ѾVݷmR#H BQV87 X\ai%JsߺnX(Xbtv;AsO΋qHvCHc dΘwۛYl,Zz[gG'W,1Ddi/Zi%]j }^?~np|_i<Fb'22"'_@7y9\l.}¯@"=lKa{-_biJ*v̄9RBR0Y􋸵1C S\ok1D-{{_5mU漀B9PRNۀGxWp:_7^T/+-\YeoavY~ĩ3W6{Z 0i{URd"a&Us`F;R)lwj(vHW7uuӶ)1iU]$"㕏dϏ{?U*X_lgl ˆg c096~?8㸸P"jrFF;;SianwRc# Ģ'O!<$fibsmKȮ,ev7gd@>+-7*yQoiXV~<|~,٥w.{H RfD _l}\(0t]i0t @QTHYQ#bDz^ fS3% E#14D b<^E]<bQ#\~d>Vg՚fCMxeۚkn.ϼY2g8Kka8,r/\CCq@~ j03殺_}+앏|T}+}.OqyKmlqf +lK[RRLV˭Լn,oB0b|$2[Q$ƽTε.loꆅ[t sH?;*uE  Cn3.r{bt ȉӓ#'_<Ý:PFCBQ|zCC^CR~ >TpppP8?ߋ4tdWW۶3/ Pk ,-oaq%V{*\Gt|*=cz"Sftaf9]0 a]/vR uϖ^ u,'66 t҇tvKͻZU[yU, T*XµKE#C=}j>:@t0Ǟ :6Vk𕎅jyoAszenuܣy?㐈n!8o+~sM^C ,~1=5r7WQV!"^اU^Ca{ 7ekVBUz@(DD $с ]WT|g+U51Kס*5v{4pQq8qm'g}{{B JAUNR@@?ֿSEmo7OOs(=5oXXl>5|w x-C>ADq(ۚMò|९ `Evf–ݶiH㉑i(b}ϒ(`p0o#_ǝ{ZsP|juUqce [=4T%T˥Z(b0Ffl yJdlTrk;0;Uc*W "a{R{<5rS-kNz0ulo{Emcmm ӭ1nߘ1`a\ۋ+a ߟU>Ֆi~Ssds|@h'L_} Fl"qm&a{+jsʝ*כӑ8.gObW'Թvk}eջwzN;`QәU#B"=X lbkc,DvueǑ 2ۃ/T'>38}5*?Bѓ4ܯiUP=j+Y8FkY\=t.蘂ظ6j_7 O 鄊tKCU޻Ye4͞éyYQ5X}8FGPHo]8` WvG¾V~h؛l4,\}%n<ĉJ~>ԇ15}O?kܭKU~*DID"30# JH !5<7֐[_ o碰Ca+2cyI =hF[[oJu(|^x:q[ %萚 vWX7z05ǍmS,Z,Ay6nvkދW˰vX` =2 IDATԉyⳟyG)!k(l4L-cmq 'AdYƧxsћ+\[En}qtOX[\q<":xڠ'a#>U0>噳OB 9 p"8QE|X°51}*́aBP(b||r<#>Xݎ po@D ?6=,¿_?/~O> [b~ kj`@Hcq|oWs3͓|7az oY4;x˛Ks6ءZ"WJ?{o&}~~g}}]3˒wx!`B$@H!$B qBcK%K#4W׾?NwuUWUo==9=󨧺=}}./qEv!؈GIu]Y]#$I-45K:U4>`aF I.''<{ma121yS?㭤^7#<rRܭBPHn_K*Rã$PrBuI\NXmzKh&4q.<_5쇸'$ 9䞍:/uHw_.Wpdj4@>pNum*z&a2ސm H8{Mv(/ 6UWɮxힱ0t)fZ`(Ltf޾ = jUAfd"}1&^8ԟYϕ&ulۡtJL`dHe'zul{7oDpht̰I*Xe$h$%ꢦ6ݐU'-}?ŭt1 3,^ZΩ׫!IObm鼺emTVMsϕ+s{KwFFTTeHl{l;U==;,76W9J:BJC7Lo<"E„B0P2Jh*X3$8vXXZoTJ?KHbܭ@ mw9 Y)rᱝtۦfDJdÄG%Ļ*핡7XFzKo{MQ M(<`?؞_-0sFUn &Qh/#:XC].R}JtX_[a}mI%)b$ʶ@__w}c:"FۜLCZ)SutM5LĵmiȲ$+ȲL $Dē}[*ju+}TzXc*=̉%RA HF ro\9Dw wBHpI^mYЛH^@xslOgkGhYwZW+,Iyz+M)\i@nzqlJ<D'=qE$1>NJt5$vuSJ(PRO9<[^J㌖tbG+IDbJبvPB=H }gćZk+O۽t#u`\&1BV^tnu 5m, Bx$D4ܺi<ܢ-: .EgZzh~QGQnA*n9>=J.O˯7~!r JJ[k/;ԁ^?|;!qUmxP5'ߞg/7K%q|A92ͭ^l?hQ 7t\dnLi\lۥZ7 "!T ub8.&E9@O<@9;W0$346mz3zTzUPR%K9.(Y_}F`Ou=73*u[$G n63yއݟĭt#K?bռg^7L^^X;Li&s+yR1.s.D8OqUʔK*"FZ(r遭mVXYㅧL"mȮ"=T=9ۘ|whrQ6fFne.WMݣt P̝ݏHѶCJO󓟺dzW|A9\4x(!J7ąvxqn.1.k 턤m'mD4$H¨W^)KQ7_}y # M08:Now0?Tp{T~(]|F\zuKMP6Vr9/:(itu]qq\.5ˢn.Y{_{Գ~~K|[>zˏk7|A9vIm0lx)WVb4f;K1]_* ѽhNMdS[Ca0'Ϝo|.ϳ47,un\c5f`hlbVә[*=_J 3yK@ ?tck$t vjzMK0nyYMiP1 ޗq 2Hgۇ:oc#pdHx\ЈsUX.ɖ[6lNFv8\Ħfl4t"DltfUOO8fNu]Wl|]%Dve}H,d談o%RmmTzTkD{Ky9'O?o+.D\/ZvtZIԊ:z k?ۈɷ$Mi[e4]oau}e,,n)~LJY>ĥW_lK.P/.:ni1l;-?J$YpNTg^!\ ^tY$f6ƶr$W!fSq,M p6.>k*K'εΓc¨69iF8DaRKq\'f>(dltU$5jMga)G@UH&#QX?lGVưz糫;fl]tEQw`x"0y۶)gY_]fea0=q;@vyϮ$Ν:_yϴ| '%7V/ BtNjyXUMg"n 3:ŅpޭtC]{M.SM,EunՄ+P( WL: oWbbZU*݆ZZ$ F2,,QdH`>R}t,Ks3,nY0ean8Cc z{%~*_:q@A,lEw^]YCLZkNCFp'ͺV'ձ%{>XZ)BF|ۿx _}/=QPBgum;f-B-qU6ܯ\m3\C`9eA$I"PQU3xJ?K%01O_|0g1q]Ybivi*Qo˱.;0('S:NTkV6:VZ/%ҏ1w/~ bAplҺAOfjŀfA 욄k261vuVLQ?!^R/7?]^ ϡR*)R]i* ) ޺7\oof+m/+[eJ4wd$B!Zj8hx®ĢzײEֲE5cLgN *$xTJEo" ٕ9p 8eK̭2=Jv}T녏}_yA% 4%7A!%ސ1|XeV%£6KݯVkAY!pN}Vzm}/_c:Z_}]O u{"Tvml7B3E޼cK˨t4ىLa5)WMlSw01 ǶY]FDBJ4^yuW^CQdFz01|,̽p0M9*"#e,,3=RbDJ猟Jy!+ϷckҨB~++d-> $`񴼹xS6ZŠJO(6AW_}^W|r0|?I\ja{⺒MAX}FH Fຐkte^u]Yρ,+bQ"0P:?˲7VM21xyU 0:y@D5&-,溦U.tEM{^;jyL803qli}R_v&j3 jD:LvADvL  RoDpYٳQ ϡaY6J,|Ȓh8ÁBE+b Ŕ啖ǂa!@q;'o!77ͅ,VubZgbbH$B4!4֋y%5PֲE7DS!~*Mgrq8L>k;p8F|7Ml6eYD"W`d{PQ7H!=^k7^NՇtCcnqd'p%4t)[3J QNmш™{yztթ+5 Ӌ]ץZRz`0{j^eb aYWgZwo*>{#N~Op6Hej" zϣoX&WUdY&*괪z& fmrݘy _}^/t'w?D_\FEkO:T奶1{U;[h(a"!7>qT˯YRn{Jͩ }KW'sWݩ'Oloz=ůmuX֨kNݜd}'O㠵ϻWDz;_u}oV ϡ1x1F{poL/`;.خ|=jͶ ^SŲ,JGH;ܩ$+Y^scL6Nj~Sbc?RgN91|G෉Ju!@ϮG2"59J!b&X%:P+FY|Q49|AX[)HFQ0] @H$P02+ B`h$I4\w# ` E{-l/@ef~j~~KoF*;f[j.6U.|ko箕='0<4. 5mJePp[jkf+[Ǧu K՚J#/q?|뽉œ Jz2$F;C\F2u3lz0^ E9Ϳ"&OfX[S[s W//HҟIHE.ؕr(;7>YFިtąrX;@]iQw ô xNz-kY7Rs=}p(/_/MpܴFnZC IO=g2<>紛%2[5鼠|VVW|AGdR,vQhT+W6}qdYR1;\ ziƣi2\N$Y_uw[_э] YTL_wo\LN(w{/~|;FzBpXHZ!V(]vn{lC@4~ouɓf bq~u$I"e}auߥeͰmBU#J4_Yq6Ao5Ll=\sNi{ů04ku8`(!{VU= cpKUE%G w+ ~OO]l>_af:R]r0 b9N+yJ' z7@p  \y}Vg܊ܥ\zDxy;~}Wi%dn}\ J O+Sޟ[#dz UM[\ϟWvS&F]!; IDATy<5"NjE"Z)R8ƞA5k\ƈTRavBmȲ̇|]w ˻Ga%ҶLNj7#`L2JΕ?̡۷# ]H*#?qTq\Dv,5k+⭛f$A:$Aas%x/muZM ge ?ƍė_yϸ ӡMQL뷢}*ɑ MȚӶ1lׅ٧J~|362q;d|AK<5 ^?m;IPg[V0YܛI''Nsr4;R$z33\!wdMլI5kb2#Lj3rv=7zS\'RG;>V ]J_&A,ޱ/}tKvLR֢whEDUl!d,6QU%(o3)kͭ{rH"%J | >-+ٖWDbN{Isۛ|ZI{-E1=wif\BKU/U 1pXpZbj:fS ,:/#nS\zagjTriSu[w(ۤGWdWMꭤETm_UCa^@$tx $|[I' rv]^\r>f;. N<^;J)CJצ<{.[>GG" qZw[,,3 _r.֛F~ZCRɑ J`MnZ7}ց@hK_rR<M ]PWk{;ٜfNcJ'5r-bΧ[sf= 0X @TU_'*JrZegzH[QV|:S.r & LDݧϝ$ɌL062gV,4,L30Ҝkvq,F~upD'?rxSrϋS:^s_bLʥ}u]y,aܕ%tFtWjֽyimA7ГaPL$TsO^>arrVClqc"`4k^텽&ZtD}}qu/]ȋ_k8}d'~/~ؘF8ęBir:j i,vwھ2HBbdp1MZʥ+//=gր]-X4.C?li>%nۺ?|xG~5G/w9$1>WڕqU0K(*Q+;ǭkDdD̕hiv%(GlNIәH%"XCP_w0}uj_b s p7>&Лw3SOa#O|3~xӣo%vg=<#?"_hcۼO/ӌ=YRO 9|Aat+]hwu]fn4ZdK ,ر8b-R,jIYCLUI Yo@35wgJ9P8^TTdܖؖK_^̮ 5O F;8uôl.و kzпW/W:~>pI@Pax,Lv'o r1t!kCܽ8Ԗmz;QUmDB Em;u41 ݴq6$IPeB0 ȒDb(ߝ ][×/NSN'' w-ir~$K:"|ӯ ̈+^{W["@<~d O,̮ii'BARF*#*ԛ D6U+&';Ǖt#be*IAIu)$D"XM0`"dX@m\eXTպ0;[hhhd@L_ex^ TvZ{97ޏ$=};Cݹy 11{ߋ^,z{>1#l? 00zaJ붥7\Ud]`i`ZŰ 6ZId2 V+żgPḫ&G<ywjx˥Z7W_?͟,;Os {34#8'uͬ@nO Y^5xTJuVz'^3qs\yXsTV6 :}0.2'Nt3"ub)W^IAve*尸V$ ћ! \J,^â\ss 3hHTз "uz{K`O0}|n)J$˾R{#}w;X䩁} z$RI(з%`gRY yM#Hخld"$ז1-WL CI E]lFUcrKkm+ v0= 'ߑ; ֯ɾQ[??L-ZO @4FX!RAw4G||F綠?A<\ڛ[*fe[f pk_Kv.KQ:BL@ + 0[.1H" AYɖ;WID(JNc:P״6D2}\nP7L{  0$:%:TU0Wlh1 CV)m-4 Ft^|A^z~zOM(e;Ț8M=ci=Jw\RYFm%I+ 8)v$oKI)^dR1E1T'FA+kucOL80HG⸎KyŤS).Xi?"$1>ɑAN 0m"8*jM *qIvyDOX9&~&[yt%3" R>4 6]1!QrkxKcn9ӥ"')TIfY,˔c(DC2$(4 u"BDU[%@=C5(l1r\ 1 1`Q.5). c;SL-'_0>ljN gL lR*F1_%t!8v"!Mh fHn{ fXD2,IMBҺP0m,3R *A+ aEA,JfY(NC*^7oN;S ?]+Z7Ľ8S3 e^_@Ud81p>Cpgnqkkhsqӂ~*ޭ{O  37>h/7-w řBp7R.5;uݦT4H${4bfcݲm&[Hc\EY &eq]$15@<q!Qv\lv]lŰ,GWJ* P)g iXXƂWE>N g81a|@{ʶXu Jnm>>3Grgt O8M7lg.$?ڕ];>JɆ,)[iDc*,(-J&s)k>w2ƅ'Nk_yz籽gfjY ƇS,iZS}\uq6iv>uSGًg$"Z !(;eVkUz"ITً]%!*"ooE5o|8V=@ $Mr -mNTex4Fa"H Y/V쎳ZtAQVXm Z!H;וUl>9MAbk|k@ 2N;q,!e5z,@D Dq6LP>ܣQ|CׅҢNnJ#7Q]_Yr\__'gNIRco}|n'uv ~S'Έ0>YQU"; oo }{W蕴E& s TK::i~Sԟg~3g݁v/N<\<|xeiFd7^UV\6Vg"Dc*S/7B YLRb1Z"{\d #J$13֗BQǘԿ8\rZJ0lQ T piڹ`6U:-ddtYх ˶yu|üESa cJ3;ƣ8v݄~kD9{`Lf8~MȗgG_8+OO9'N K IDAT7v|PA7'\[L*N]WWjaj IvY!(p\F]3ᗫR1U%PM Y&Dt<_Rc/*4Sռ,GE6mcwTth떗7ۇ臯&n Y yjK][\E^]d7?Y+3צ)Z*n|n?n*,K$$b>_,S1-ö eU>&}» .TVIW8ѐ[?"LEڋmD@H4H xrkT NzR,vc=K$3 }#;=N.eaalITPe7}V). шfVUҺeqc5ZmΠ\ò1O-0mWr% uI $+dPx#oQ1 ʦA}jDb#h?W!?5?s/7=}|bob/>;r䂾)J}q`"N)Xlq5RMT ez6WmLi"?>'oV#Z}ҧ>L0>:ɉc0yrW^{/NPmk8z2|dwH.#v8; yv+H<7建{zκYww p8gDIPȐB!=H/C HHB % {۽5;3ޖzU]f'bbcAs-nG!k m7}Jnr$Le2fΣ8 C+IT;~,Zm ,Ȃ unlޛ_RrbO1e L8yt|~)om,{]pTm?WMK|#>Xm}ih``NMw gi*z?-D'3?xJ "t|+zfb.|Ƽ]6nwi.9 ΄QȽ%>~~1| N>suwE1Ш[\,!k֮Z|F:nyDQLeij#ޱ; {nx5iwlk29E%i'(AJа]2M.MmP# ž&;' xMץ .WK\T7:,:v /Zt"K=gu $#uyU& w9 O_2}#mi-_/zn拟|k%c֗X]X! w2Qwfy}\!ϹOrG<J҂sqaR{km۽oP$EJ{*$k!~@7ܵz. dd0aqҨt] 6J O|)Zr~;t$I w?X8Ux)޼zo7ƲȕrEUS12EsC;L +"Qk *y2ћ,vڬv-NA0xmSmdQĐLYPk]W[#zXF>%PHUlyPq5XIĘd0z4Ib40M0kn"OlosMfFGSOͰ7k gc4k }jKDa(L_4QӃ UU`@'[.nP 84=1i_'] n*{85ppvz"rZ$lt)bE[۱1rabD7cE,; ? A}+H^KVrQDsi{M_ YƐʈ@ESU9c=,Fy,e@=,o4Еۂ% L( fu]MI3C76M?e0vEg'~"?eng?v>mE:ǭ- דeG\>bD?NUm;+ o\`]ku~SuQHDz3#͚xl %A4x"E9{a3gմ(2b"grdQ䃍+Gtb͚zʯ, c,%bI^omt!)BE>~퉾YE}k54Lmt\4iBο?$bVf{Fq\SS>,5<:w`s)+9L9v( HgMu([o~SL aյk-|Gg#Bǔ*c#1ql-ݶ{ܮOaU,xVU(20c*<#A;pL߯32TuVӐq{u XfBdFx5I".^`6"SS% 1Ai;3e$ڨo (!Dܧ׬Qw퀉 YI0 cj>ՀLad8gӉM#%jq€{.yQ$ E(GgH?KS~u\oY\(#q:Gsx~ZKکL Ӥ:lvtjJ<g_r4՚/ʧ_kO sڦ-1 8h֛-&!յjjpԮsñ/->fPh_ң Ԧ$(r+LmQ{M7xbɁju8.nXkL)c5F#YU!*'ifV B@8 gΎ.xZg嘩s}bwj;)}΅С\1fw1D 2ɓvHҷUh!k{΢04i H*J(EC/p G u~T{me8[WEA'awd-v( 6w/_*򙏠B ՋK={Ճ ֛ỽvskDQ乩}Ĉp" xEd6P8ZSN#_6x 6 5lIc&~0P%∘05Jo;.uV_p:"3]sv,;e9IaҕIW[x,l'Bm\~-iQxv͵f CAYE>2Q*Ǎ=gԗ}kqN#cZKkn."ԢMEik飍jS ;w\eQDm8E հ# 94 W5FǴ=ut`;ev]Y|}͜^*:qCb7E.2o\=(T?1"=fs(RFGTq^ow޴x4.}r'f]1a`v>885gDjs>dt(ȍy"Ьwz U=+ɖdNӁ7=g*Ʉc@@ lEQ{ExIB(^UDT"o!aˌg`ΐe򔍀N;u]νcɭ?QKFU^~~~ Fm<=Qn±l*S>#JvWxDtmcWv};Hw |,m}xڶˏgyr|.]J*c\(s{v vloFUx~jW;udmB~bҘN^vq:Ncc';&OlmցZ PD5IB'|˪vK%ADwD^EH! ~Ŕ̬Eԝ۽_#)\);]+FH< s?l1VB?*$- cEUg*z#/fp~0#1lDKUI #rܡ#cCuY"gmaP6 ZKvpS$A`:,Yčq/$Fnz?F??3>I~DMS%h_{ײWDaHy:8}NLO::Bc ZMռ$lw뒂$A-," ]@dx:WmPe0Nֱr|vAݸw4CaXI>1ƽz]Fc"Q-(anym3~j 24) O8%(l qXug'hk,QU䗾DpֻtCyP:XixEyWYqﶈS6a_MU>Y Sj D."r_#ʉK7G=v:׻6mn}D{A8*NkFK9Sy|:ţĥ+z,|0;`{R"®HRA^=/dybmK fd}wj>ur)jUI (qkk.KEqgͩ/ZrO6nԹjR0'~5+>)i(i:O>+9^ zu~̔,aҵ*c{^#FǎЛ ۋ`>FFy24~,P8[v+ Ah̡Jb-+@rXmu|&d:Vg-sʳgyoј̚{=%cD7i;XtbIPԆryo%#D(gc?L]!cC0.vmrR d6 ?. 0if(j:]+5. 9tIfsS*/i잯`||Ӫذ66?5bQ9Kn΋J (ZgKq Șh<QdM?n9ʠ8lMCe2ݵG.>17g5INDU^8SZ{W(k!Aqp<$5vۣ\13+^Ls- B"f^:$Bw_"ÑaӍ|V$me5S;`{\4*c$q!yt7 tYbI˚3_*F; NI[xaą'/uf&nTG8 '+ѩt+w]VyW|:">ʖ fG1]giSul{=M Vz&gΖ8 d񜎮 weS3nQ/ףV ~Je ?) bՀ;.3?rǥ] ^l:4|a?KW^N5'i.KV,n CV,vT;ʕbabz[w i1Gb>8AWÃWyo[Rn(sF8xVz=Csva˭6 Gǿ AGsš(\Т.K\-{F E+i|m݀6C4DqV#d̛6+>_L]SnlyX8p8r2걮*nwӨsVMDoۺETm;:KNZ& ɪݞAR,Wcu^jYKͭUcͷE=n8bq9VN&80wm6<a)/de {;xq!0h8^5K) uOޅA23W-3%E~rsʭڞyu!e-"̋Y8få E982.͂[w/fXXm8ROjbSC:|}7_ETco/)h~^ 1l;4q9$R["Ȣ* }(t}XPT!Ye 07lCF8,[㑂WsƙY;[ ^x,$Z!٭>hڷRtbՐ0+šM)@tt7VYzc t"=Qp 5mGW׮NN r^`-a2⦬Eq ܽ6v7zAHHQP38GՏ${b[-A |4= 'xNDPIՔM1>ނnf4&./}n\Ds^,|?'oOQ6 ƽzko'&!aF ʊG0=NǧM}B&#cf#^T4LE#c( BnD׊ȕde]14>-WVLe2{s{y (i[6?Y Sd6fs̷[=eNod9 G4nb~Z9$i41+ ׎ilz7B^4 6K u;98N-d=:Czaȝfbqhl{.u79=/Q&=A{49zf;zQ5yKd_K?vm8s1Ik#ű]4Qv~9L[>ܘU$x=_F1V+YYxϥwVe^4tSW&_~|6}&z3/OOp\bj"#!|r=CzemƊ?i0lZj=D?# AwȅZή6IHj$q.g\FNyz]));G,'RX"m[UQFNocamCЈ>9%=HU,e!OlRgR+EO=f߲.DJiFc\|Jn|WNp6 a:.Hpζ}feilD;/鐿Gb` .J̺mlMۍQ{pkecoK>i}!}*g;KRJ+eЗfiT̍џ }(s7N("(Gէ)L&{_YU޻' ;C6MX4CBc9-A>f\ґ_dWgxb*aEK70h<-Q0ͤ0cZf^0!cNqvS~ju& ln͵ _$4;OXG2kY^}pIψ:SeXyܿtf#VAT)Nkq 5^>Ʃna" 5:!B ?UJ}cGB@GGeXGb""%H7ߍ!k}+ڝQί类gz͌Ƶgw#W.U?{Njz,dȅBJ\#y1RbE 2 6Exdy㘇n+ L9#wzDA̤_P@w; ˘nqc3+|O&n-TQԜ-Puza]{?"\uS쵔=cܮ/P_O#ON5>mτaWw,R( M 0# Ui31_Z?(Ͽty0TsW?Ɵ}]jrۏ&K\*9_ȲԲ Z! !@V$<@8wY9XnD2CeHBjiȺ3i1UK0p{睔5$"k]a}'4 0dl"c zuJTHяg8`K엗,6i4% !w\AΘN#>N縮r띔xYr]!"gg&юLEE泼zv'E!F ä!'?\Ev;>o2wfc;rd7Vsl}5c9^j߁)+dͼ+Ș?kUDQ8]t;YVcیO1"oAPq @EQ UUq`R Z8nb=DQ[ ܶ6>¥ʩ]ÕKLoEfr?~A`"k25i..59ML;tH !*Sq rtokjHBr[%Kص< 6w]Ntٯ.EZOxF1c &bD鰙zvJת{o6VRe i,ȲL}u+ ~Ĉ- tt tX#]Fecͦanz8 @LYl|}kvW)9;1d|Ӌc Gyԑ Y^ƙ ĝ8.7eeGĝn}l:z=Rm] S"]ߧ"nϞ] Y&ͤȢHYzg]ßAjt-6>3yn]V~.%cZGqiݔ6SVn{B,1uJg+Yh; Oncjwdt{aHP g }0:@Q zFF}ncm:0f.CJ\T0v#qlAڌ~>}}qjk*lH҃qm'EWLy0]JE+|ozGv;, P44{_[ݦc2̲|]\^Tx}֖||7f3]Sh*JEs񣈆0 YҎpV$I2.笪"~ԿrFG~?jdsTA%su#FT#ow/};IuBV,rY}k{@:չ̃QS3sSMcwzHqk'qZlE@8Vӣ)4ynjjq"<0>,}s4o|= MDJIJquJI'O~0>_(oD.zSfbZ( MYV .m.z8emapYdrvu<" <p#$A tߛ.I?(M=t[p4PX^Ơ݈؍(^H^Y!#EQo8U(p]==͓OL߻bCICeʚLYmߡX;anqڔt fdt ,% {+&Ɩֱu8PpIv+)+{{T2.WwDA:@Iױ;1e* 6j:EI4{7_)|m&sk33'1NDsov؄u笄G+w[ ϼp>^顩2g/ޠRb'oJs\$Aϔx*?ɹLj [Q"W#>Vq ,/Jam%"r逑͘@ ;MҖaʁُ"˖Tw䳽1MaF8![AϷ|mFڌ gϜ4K q7$ZD=46B`b<7Q0):f \̖2(bZch;NB'u=q]w*pntiAU?MɢH}`%j_6t_f$)?.=k3¯Wb.ڋ<-;6Q͇Tb_bAwo |̥+Sʽ֏ K_Xa”U&.C ~X<lZēS>< ƯʗӓS?i4If\r%_|f$8+>vT)grR$cIGdV Lro0h!=Y3&<7 l~3ba !o_מ~!jN"^ߨ=~HY^0$+Ͽ`XO&+yW^ᙧΐh(}@$WƴLֱ-aQA5bV-R3P\4UftƋrHD/f8;QtrVJ71 澵$`MBA <{/?GG|mFG%LOMW0&Z߳WMta{nq~S*|o]'짯Q)g1 Ȩ̤$SF!.cpnv8uZu'JnA@W Y'K,115UehAOgE4ضߝy-5f.돢~ bډ>hǿ%Zarlz|3ZH6=Gڗ_gf. %J„؅4zᲺݞo0p(}c5d(Jq? )G*RV)F1ᆇK=tSo5sSA1oO_?ï~E^qIL,QDAgz$ &]wѵd-#hW?♆t7 ql5":@l{Q@ǸϏFQS<"{#QL%s sE_zx]?L&sǮ_8\1rY*V]DL ~@jD񼐥v7tz&<voDv75IQO^^rE#NGk0__—_'?֮.|̓ק>k]SvukW'hwuյ˫MmUUp!,a*  I*i 1qX1Q8펃aDqD)1 EQe-$(0iv='k{emK?,,u Hm$[Iǐ(ʑiĈ }wC(Q37-*_|]rY\VA(HD&MAui4:̮Ro {Lb wKpF (kz?qks8yvxiwM:|#f9@Ͳ zpt٨/}M#FH?$6a"v}UUQ՝Ԯ$I':(I!or4t-,n0;j9;A ^kujD ;׷:0 IDAT}٥<5}(4=wCv?JI :ddS:(`X{ή-xy>g}M#FĨ!ڌP)O|ؗҗ s}?/܋P!5~P?X^0֮c2ƋϜ/y3.vc]ruVmaJ CvpQRvv1ASF?bQ DeW^ȇ}qooun {Tp![F޷MbH}Di2#v+)r Q|'\iĈA}D_fnШ*sLCc7.SH X.Q_[pWߛvo1@hR;51H^0s{uc?x[HEDMKjȺp>3s߭ O~98B$JwܓG<ތ}D0֮ƙz'A֡kݐqkx蛖r}Nl~ow`aIKrW3Rq|}M#F b$#z,\~>^cqk}{wVhWW$IKOjIxN9cz^ M=cDP(}1fgF8,#A WS&֮QkF""nZ8y/!m|qC&sUg| nDĶxAOj?f{]yđm?gΒ/cww `PkSx1y)vq)E^tàYnG!1.z'E.qF>Hn.o~h@TsU_:F0ha+BP#Z>;J9C֌AyT ׊ƹ+Zt9(j8?rX\BIϖɠ:^ *A(])SBE#α=><8\+V`CtZs+*2e[P{~;rqh4\\:'v)E-G`GH/ kX[zsW.ʥuhL3YLf`{nEq~^Gݭ xuA x\.HXLP6_Qk6twBAENwNHb [X].au$fݙ_]@J"]YmsPLLaǛ6zzG%Ur#/i]\h3vxJ]Zn3%QT ~s15$@J=Ң8( p A UZF~rRkݴ[H5Biеq]/N @Jx"b2z`ImW?t g(I08 (P,԰λۍ?L[ݏ*^)@I5Mл]pkD]-[Vd#]k~JaQcJcr*_y.[)\ڽW MЍg!`N 3SDOuFRmw͏y[/dLJQW  %!s\s=FQ?cm\4z{q"RBHpe]55ks -!%}q@"2(L#S8td RH`K͟oke]S_+7bý(yH6`F*-bX\Ñ!rfyc_:e7CdW-q+eQ8[h86r@<1*с8G:aZo?k]BV 7/SunGRBA˂~\kmAlXFOv:[߾mλ-q}{%xr@w?`2DviJ0un !P7l6̝cm]rD'xŒVAFs"FKkd1sڎ `k{ѐ ː, q_JT69n{hGss(u%n4QsXb9tY&2 TDs:2w`44ŒXcu VVZ1h@'{lZݶO*$RP )QT.7⻳]3\ +6:xsQӮB&RX2'V5McʵV^Qrgvw!Pcg/UQK`q.5Ö8o/h)C'ө?P<69{T‚VFn#۸:HJaݶTna*թI^H|s^z!ĕ*.\.;ux(1!.7pu9kK\,W󯻅FI)۶%#,Cg7αVH4|v⼂L>^s`"sQ99 2,U7~s bgnAU ܚ˗\K.N."ӱ8fcƸ98ffݖ8z>r(,WKͷ9HeC_J>"S1,ny!ՈOQ}dS1LzW=ϥT[%ىDb{.\.9p(t# HC*a l N.V tcn&v`J{%y^]zcvq3.͏Oe#G9;93(7l Tij$S:px.׵] >[!q^o]e }c.\@P|Vдx f|R~WPj|fVY>-q@~5>–ZnB gQ.[| riKѨm no9_,NBIqqH>@@ SנW5Aߩ^]no+}w:g-gfڷmKTQ)=Ͳykpĉ8(~?0* pΟ?W*\ùnM°&jBE48t|Kanqs#n;#/o Vg_캜qp\T$=ީua DkJ9jzoWz0`e`a~k.^.•o&vlj'}d2qwvplS4x8>QYfDYJ˼9hmj婙G[:(ŰkovkkQ>i͋*Uk﨔n.vɓȍ}3MǏǡbVRo]5 |=%܅Xۙ;% tW\EW' G^Μ/Ru!b<Ͳ'Oɓ{ ؇' c XXXC=Mi~5Ac;%RÝ}˯CRfKđe<)^xĔ+"碥RT=R@og=xB]ב >eq1vI*(o`{Zcq>-ͷn;h݋1whn;wm%2F6K`c~ ]2]155]R:,ͯ}KJzW\5ph`~"@njb{˧O}wK3_Z_[#OO"mS)ba~~}K0z^)h ¥8&Rid…us3]gAD1pёʽ 籌ޯ]h tW\uwǮ 8߯/\Nv0li^%V+mL6),LL"Li~܈gfOT*\^YGaw]>uBes]*C̛o(.m_ow_cɞ@?`)/u9>JX)g1Jaab Tpt6#3g/E^~=ρrv ][f {^@?z?4XXŰ81+SJ8QCӨ@eXp :ٴQϤ2_) k}/`XFPwY3K`&&0m,b{ީ4?$3Vz}ͷMggCqw@Jz>S`tjvlzyѹ+V3=_}}C6!â@']^{s_L3R k VDrSMM^C7tXݛRⱧ|ߑGBbX 6px>f'҈o그; B(V@mw1ӠJ{$ҿwɧz TS3Kxb\$Q8g - mzJ^inl6毓d:ԙ (׺O:Gbj~ puv@zsi/`OVs{~ϝ2$ tI4ƿ 2Ksaji-_. ?Gp>9nj-X i{. 8cJg€\.8Qi3;BeGaI*,SwN+mK;^Ђ8rPfG>~ >1lG@ \gBe==lAx N9] H%?S€8 KGþgUqAI]!D$-fXi VZCh8zKR@ %/]uX* x KX-py$_mLbJ/!%>܆[biRjv]őoSLqð8Zuek;v5r=Qһߏβ𢪬Ѩ8R)4np3<4!5kr(0܋|; "W8<,ч[=u%[Y"t&{?49G&4}gZ,w]['8)mJ)40 `oYZ %Bp~'ڛqoxC]'!;AN[G:'ѨHfW-;5+$sa߅X9zwѻ] 7V+)!dw}K z 7H>kλOđY]HQi~,x[E󘘆&W UbR*\9@ =& Wy<{އNBvhF IDAT { elLGq Q,j]BrBf 6,ԜԜȎѻ5R̀bsmsgpvy _{4ޏඛ&8 l|!/=cxۛ~pGȨ(~G߁7SxyYLQiv= ihz#`"RuL4xF/^ifs823{o;ێM`~ff n kZwhz!DnjTn':au9jA<[·%G,5Z+ ٮX/c 5G:ܻo7ދ@3v=pNl2 ylޱs $7ƯH\R73׮tb5q̮M2ugjWajUbv@)aF?jUzw9f2'nqm!{x![~Q@x&3nJ~-H!05vBt2 ơk C887Y0D8Q^A9fhhUp k:9ܲ07ۏ;M=+u<}2^bN`< 4q,ceF>uT1Ҹ'^AՊe)B]G`t7$tXpSFZ1wC|E> t4M#!_${P/I.60{J+6 oPxdb0b|i5\VWo`ԑlܻ] >&v5V™'Oa"Ʊiw!|&F;|\]+ty1;X;Yx9c2*=HQBy=ې}7k#e{߶Kѫ:}wqBtIvu_ \zq&'5ܚn#b9yҖn%ca1j3SƑI2Ppbk>emkMS&ҩ&p xUGpHN/BBT ,.Q6P`aĩ!O>g3pO'j_)|}̩]y+&c鞫ۥPr󅧱*Xf}_ !B߉< g.DOD-0)/ee$H/N'be6ܑcbIӓY((Tv))$쪋FE paؐԬ[>n(U(TP5Gۈi&2V"rU@6D/mϲwĻdO5׾Z0>C:x7Ga440Ơd>|^  rXwb;pηM6m|®ͅy nh>1}Њq̂[c>q(F`H[ sU;,6OOΰOsz ^(ɞ ۿ}3m515ҩu8/ ~~:2)L``n2k?ULB‡d>$z. @uClaP]JetLdF;YcߔӇ kpZ×)gh:LMGL7^X]i|^m}g? _ !;GN >SmO[H@\/=Ģ.:tK 4jyL*'Q@m(c`g\\roY5q>w5`߬J +: :ӆrV={pWA_zu-`Fg}\W?G>ALp'1uUm!5vso"f2_ G+wV$;r9XNGz\?N%5=!,_o 揅s ]:>>Vl錉*[u^4= wh wLqk,~s^ \WknmC3̸ԱU_5n}wVm|>|xO!;AN^6/?}[[mHqLLƐɘX?WFZ AK@ )!:=4h!o6{ӸfXIa~7 U %µ5}v^zMofC"{[(˪aO~H1,G,cf& ,?_W( R&R9ya3λVʏJI/UI%! J)0Ys9c5CXo\@z|O!@'F[ b&9`0LNƐɚpVN5F0܇]N9i =fp>gUuv8~?c\*Dlk/x+zY^,;#!Ϟ?BFENP1!c LK]vrNlZ> ǃ;o,< <3ڶ_%'LmGvv;pM3 ) tGg_̉ HTĄM稭X;S a ֻNqΐ[%¹.KH #{ 9rA; I)UZ0o99&sgP}'痱_*qH1lDv"m.TA۪롵Mc:\*h`JCXȗPL4٦0=o9 +yBZ ~ċv%|7|[ \i,<g1]@beLMߺd#'D^@'Z^o#EU&•J G Ox R}0yuS; ܞលteF#°BെKN@1hW6}ӊX'@8RNdvj#fw;'s죿q]/\_zam˷@,6n5i/T5Q^<ޕRsLBWM_8¿  Q%x kws iߕJAZ\)`|;oy- BLe=?m<#Cvt?( R7/ɭ}㉄LB<p/QREei?pEwx AZ A[k&}s p;c p[e * j)6m}^n=p]őm4m+pBI S⡐wF)yٶ5Q{| #B^._"_E S"6s092Y m\.UV#[w^ SO,^ڣHdϢF,dc57kr 9>|_b}͆c(7ZN͏cjbz&d@'7]?b$LsflSiVKکx(]Qr!}ՉnF)STl2k3Arb/j`qGS (] [ViWv2ѫ( myu GV]e jY=zh`Ie-K we{Y T4x᜻*wo w ۲4G_5އPk%rH ;~O+/u]%̈ V+>%'_>Ke}ŃFN~ݏ|90e}D@"CoB䢲܀Svy{ `%`{8=^t~ƀS%`ݮ !!#_d>H>U&s]kTVP8n701݄9Ff~g[Ul Ia-ksQ3.u9V &b[/P]Tʃv=ϋ=|~-$/q<>GkeAƺWsOH$ z[i|;@uFuFÅ} 1 [,wif~+yҳߛLLvgG+܃9+ h}j>{·ιr]7[8rᮔ:9x~_}}J ]}ij4XqֵMIz  d !tai[1꾃g \YФ\4 %3%ϡ& mhy9?.*EN3Nq|*!٢c{m XLG,!7W;R:ܒp6r 559ÑfjwOHϗpeF'*Tvnf_UQiۿgp@jckA]+M oy>* Wm ca9 И6gA75{=im #~P} } Qա#\Z]AH77WI|!:9N=m;gQoP*ֻ40M J* 6*K 5RȰkXai$&^0 UώX-e[wNg柡ӛV^!O_nBn`4/~ 4xe2H+\9]34M&un̓SA858#i3 uꐧ= oo nKh sDw~7/ Qau}?(om;WFu\6 )\Gn.\χPW Ӯ(h5Lͱд=:!O?-<̹Cn>7?1:n7Lx|nB$|w|$|Y5Q0y],e.M?}hR'FDNȐ_Wx _֫-h:<[38g<|A֩JE|[*Bȡ*ry_y/{ۛ~o|JޱqBNH)O'W2N}~ 7%u(p7zنy~?@a@N.*x'}+TnSZ}77C_9bV}ބW?@%uB@'dH)SS_dzƳO!_\mw `Q/aa<ѱ hem9 OӧgаGk{Lfp` Ivկ>$䀠@'e$ąKSxŧ܋峰mVD mϱ5%6V\7k^>cu(!^Q(8,]: ΢RPtr8n>z+n9zzzūa5!lHtVֱ^Xza훮rܯ$譸q~S/!@@'B~ZK!QB!cBB@'B:!2( !1@N! tB!d PB!cBB@'B:!2( !1@N! tB!d PB!cBB@'B:!2( !1@N! tB!d PB!cBB@'B:!2( !1@N! tB!d PB!cBB@'B:!sRIDAT2( !1@N! tB!d PB!cBB@'B:!2( !1@N! tB!d PB!cBB@'B:!2( !1@N! tB!d PB!cBB@'B:!2( !1@N! tB!d PB!cBB@'B:!2( !1@N! tB!d PB!cBB@'B:!2( !1@N! tB!d PB!cBB@'B:!2( !1@N! tB!d PB!cBBT+}{IENDB`sympy-0.7.4.1/doc/logo/LogoInfo.txt0000644000175000017500000000057612253362407017236 0ustar georgeskgeorgeskI created these logo's and permit their free use on the same terms as the rest of SymPy's code and documentation. Fredrik Johansson ---------------- This directory only contains the updated logo. All original files can be downloaded from: http://sympy.googlecode.com/svn/materials/logo/ ---------------- Font Used : CMU Serif Favicon Generator Used : http://www.favicon.co.uk/sympy-0.7.4.1/doc/logo/SymPy-NoTail-NoText.png0000644000175000017500000042222412253362407021151 0ustar georgeskgeorgeskPNG  IHDRĉsBIT|d pHYs ` `% tEXtSoftwareMacromedia Fireworks 8hxprVWxZ]zܐlsq.aXA=̰qvقM1ڂ_1]@PD?M/?sﹿs溺_?㺼Ws>_^^]^\]\\^_Ŝs]\^.?j)^Dk|>^Fsh~6b]D~}^Fz\T:GyG*m&=A\69]ؖ$eѯ9Y4M]5\XKz?յ==+,~g${MOϲ2Tyk(GZdz7tX;:RGX/wK9w+/,"VUFW@Ɔc(m 6{Ggm|+h߹9Ԛ.uKcITzBo`|V(Vc>8 >73![b.#CqPN$!t'jdŇǕH<>R l:\2OA Lip)J5n!%$cdFL2NkEmNR=_5!+3OpxO %G#(ei̼9fĺ{D͡I>k2߁n(܄G8aȠJ-2! gv3րu"?j>=/[(D2"\28׍WyC\e%>fK8kimLr .{8sm4rv C=A>Ҟ ?f8ZKCa[ð3]`7"t"#@,o#5LVE4@{e>]xA f6,d!oH]MHCU=%; 6^<=T<$'50j`1Bl`zlgO?#" JPOUc?z,H'qY7v:`q=>'?3cƍ.oȝB@VQ:xitUxoS%` uz S |XB:(6 -K_Hz֎FjdsY\s7t@&e9{EihZr|c{!vR`} H@I izZ6:h?s'k+)zO&5$X86}QusC^|]%Y@+4Y:HpH=Hޱ?o( Xfl3da2&hNG諥[H1W:Nu ,ϴjP(%ȀTkQeN,M9ןA3)ۏZCB6@tpm_jM ۈ=]wZ|(rKjK'-ܸG,Ж)O=z}՟G?_>E] 41qeणH'%!)ޮ;e*?9`zȗ9A{/Xa*yY:·g) @HSq>2?ꉘ" )J4AB>~.O, w"6Ծs@@Gzs% z*$ G]qhҨ$5/{s)'EP)`;ӱАM38ƉT>loz> 9u2O|P(WKaT bشx lgDNMtOba&m;6߈@fVeH5B20,jK_CHR2m`Di|Veer|~&.'C L=u`st3a@|){ǿΏUg ?,tf?56Ew߽DtAObۡO o%dc&eC_iD@H8 M#%I_HCRՊ? hπd"@LD kS>FYIN9 "Cc4U;jf\^uj,6=!() aQ!b0Ȃ&]I#7&ENZfG\'P[k96|~e!wiW†[E_r>ZweiqA9~?}-0 [FNPWMsΞedS?e/a鍢!'ƚL:E'qVwFyi/`]в;NTiX2Qfb?{J! R٠vD8B[8֔Wf,NR_pd$zlC!BK:.KT}ca*tqwB] j?, ءͧ2#q :X2)HOCE&r˫߱GyPjp~'*"P2bAk[ՓF@~8(W@ i?W:4zf/?Gmb߻ᔳ2 c+iȿo?)D`S+SyOٛ4B: o:g_]k.OH>4$2Tg ]/GوyFyϼM'orlfٗ߀n5,ÕJ(-(<'W̻x?ٜi ?x O\Z` /ǧg =lq.4 fO%hDJ;Cѧ^$ Ag>I]lY?o޼ٌrr {~GaS{LMGta[U 0BDp0ŗAVF:/ood+)&[NU[|zw`N;;o%(g 142 !rYE-a߉Mw]po < |J΋ Wyw}w^~t^.XQe􀛵}]s.{jcbSM 0H7;ѭowډY'u?AF"t#tz0{=WYΞL6\t׷Q.eX*n@pڟ=ZN 3#0hOs\A+?{?hh _s3x9k?@r@!|߯<񘞏u[K̑Ӟ~|׷o\!o|}ۖYգZVr}U3EV.ᶷ)W#d}VSbcy[֪zX?A!HmkBF)3mkTSx][sƒFrc[oVa_TO(b]e@yb>%^v @`0X 0{i@G/[7Nog2ՂoP npvO +^}n0?[{pÛi vS2TmmÐO ZzAKS:T v{o@:-QqiZV9`WY'Gz8QԠ}M:^}bPс::+ ,B eKنWJW*1L-q ꮔq.6&f*ب9xK 2xq~-2 }^vO5˅C1swϠ\FP-Px8paBSt )hZ˝mK w[88->LtqKk86;kXjykx[vfa1ڇnP{~[(z26AgM5iJs]K#V?! t 6 speYQyTʠQ4*FeШyA$,&}MY[=Ԧ6/!'}8=ĩT?̱o!4Z !!d2L|(Ċo+p y'lᗏTԚJ}x,| ,(G _9?p@_W #c# ȉ-USUoUu-hU+ĿMU3biC02Լ<"^*n6wS-v6^Y CGCK58%wwU1Bc08劦jRLd(?/E:GVԮrK+SG@ȡ@u(lMT|gV }M/(V+X&#cdFNJGKJv57H@7 P?O22HI#- ? / ?JC|?R,/ع R90E ppcha1 =;)Ɇ=tðvڸ)KW;d24ډFkysW'ʄrpFsBLIͦ9[Z{%9yHl28ɳ.5LP+#*AT\FYD 962أ 偖l,:kg9T4eis$9Y8F&C)CP"nE 0 %(&z)7P vj˽[`C0#i\ePEWr6ctvH+W&'Q%&#J%]̹]qxaQq)Osp3,n fpj8IN M'm>]?e] ]l\ {[c:-az+ cT+cD ɐ4<$$RZ 51rF9sYw cޟjj j4j"Z/ nG.;(9~I `kgZ|PyB-/)>i>Kbe>Y1Ov!6Nrȗ:B๐XS.E q27ЇlXLr?1ŸܢRiN\O0DЛ@_'FE^CAt :G][d{pu'߃afډo6IxTz<&'S9=Vh贱WAYTTوlTˬ q gO3W֘Ik\Kk= `W! kpv;ǔ 0m͍J}aDw69tZǚ-?z4w3%He!q@78l)FSm՝'YkZaC-YkGHѿde#:u4)?iQ~{ÿ+5xckhգP ԙ)![SUnѽeJDw[cІDz ~1P]7G>T'+♹0/I KWTpPAUbPN[<;s&v)r(;gyJf mlh,TV;T- 5qDbWJ+t*#lkcHU3-ͷ]Ëן)oJ9O$қk53yC \@wZ3Vh{gakgڢѝm{uj_A^ߵ"0Nst<9+p%Qh]A(Z#OgIڶʔ9~hNӱL(":$ٔD͒"YFqӾvbc2ozi=3gu+Qmz~X:ڄv=*?+ &c~hYV3gWWTz0#|5c(~٫/C:{q$Ç}%(+Jt^w™+6o?TiPh* |1'<6!԰YԲ5oA׍9+{' sN蜆b U˦eгP6qo'Iλ0gl.ad{*M.ƪhFiu]#;[8OHegfigl'G~=¼xMLL25aLY_rZDli-y'j1]iCh5C ]3t5Cfh>g{f皝kvٹf, vQvݨٹf皝kv.-s]!(皡kf蚡2} ?O՜]sv5gל][ %]st5G]ͅ9Z:ky7U5C ]3z1qw.2jf蚡}^<.*f皝kvg33jvٹf皝}v^ڇQGиko7 QqU}ԾzbmHaɄw5 x֣29]sq ~f$*OM.~ YgE(ok_0_Z0xL|#%kAjCc/8=/\%gk8lweuOq=6ޜVԒijajtC,yQ|P?gFnoB:j.9d;}9Aif 5k4p33e,M v|sj kCH%ni=MbϴgE qW!ΛrcZ{h`߆L[ZBo~WQf~7;]:;B3KM!ƍ}e {ByH{c%#!{pU|rqIO1nq˸ YX[Q.=(MR/r@TBgS'@Ѿ~}5x[5›Jx?SkWOhk+lq֡ Ru#5^ 'jF0*'wq7CRڟb-QBH!{cèmI(4}Q4(suGgi&l҄1hnc佋gI-)A*8BFd [0>" HpG#&O儇[4\z^ܓX% U=&he2h'3( !1(rM!O+A{'qYy.=3=ϳ.(W<ʡ=gš([n934k Ur -2I[kI19rF^u,̲,{>$m,fʭmv 54)TH`YaϑR&5Y?b.zѺK1_?ɓ嘤RbC U-ɠ0;->! -A!(-~lUo>AR>L><[5c;$SGp.Ӻ{6ROsOf>lnvwhY@EF0"A֯I`ZЅ!E* ]}j+,V ?R+G=>|ĘqyHԀ$zC1{'9ۊI6;lwNed%lpX21s=e& cU&o5CNM'ړp/N'|K{3)=!T6~4@ 6(myH{Ej^nv[Qi]kފjoSsM5;-V IeB|&\_8&hp:f"2qw>ߣRsgP@Aw{p3mN#փҿӍ8'bm?ڧ5p &XvM`2ՂNMMz=Vϊ3,Yf:70p;/oQ}^ozpA{2m#ӧଳ gݛihsâ{Hw&q]‹t; CV񦷷wv>\f -x^T ~6ZXVt .:o]B9>_Wꠏ`XYq'N rاo#`pwL*)k98ώ=Y{.9 (y3_PBc*Pv=hP7{`뾦qPyt ' r&=Ág6d|&hp6a* C;xx|>8ڋt1V l; -ԙSB' ElEl+ؕ-UreGUm.ye.9|К!6}Fq>c?(jV08Aq]uc=&9?t RLR S|D<ٚu: iX`׾NPчo V2ڷ{M_2÷}b ;:i?D tmkBSx]N0Æ WM- 2*5X|DZ=fLc(F? hҶ BLrGpodgd/e>f 9]WA\\Lf[xilϣTs1H QlHاEt(SAZ_Y X)>mkBTx흇WTY9ٙA2%$79HPD%*ID 9a<;{6ͻ{55 9sfsoݪUu@m[ֶmOӆ Çɓl.^OrFm~Zmbb̥:QvlmX~,֒8Ǐ@iqoϻW‚Z}}=2y!02gkmMԮ_˼X+Φ8gsңpyo%av'x1+!2G5l |r~ 7mKX_p .1PU6sJMk333ZkTvm+:,#kbvI\m y3PqӃTGn.im:.&kbof낤9B aǡ0<_7 l70^[ZZRknn`^D1Z LKp szJ3GSjq}pdN 0Gn t9=(X2~?ALvkw&ч9 VJ37t nkp;40G~Itv*.)Gg?,|k===Db⵺B1p2,<*YjBcHNp74+/c`iT^.3No0^{@V"U00syU"=Y( +q0204NL;fl m7J?^|6:: Z>V#jxM^ϟ,smuvй&H.B\} ^2g4OSyLC d [A9$ 6˻k_4TWW-^;Nk=`OtjFY}wѾ\%==7qoDi0*ÝѹQkȼh pFB?={VNU8^i? 5>Pq/o}4sT;m~ΎՈ&V~Ѹ)VwJduBnR$dw^W7^c2pV?LkMMMk 񚐦&]k.KZCu7~G~Y;F~j[AOuhZ9Q&Pj NҢpnuk-%plHxT=xS)c3B^ޯ_D)dB_s9>ӋOhmIݻPR$7^o^A|=o";d_ű#Du NI3GavZoVVrz;xxMYǜ真z֞G=$LЧ_G!]OsyEK+SClm~Iڵkk׎azn.ïDk|Yx~zF)1߾dY{8 xs|?x!6w87SOÞga߫-//=^k(KkBzrOCC'4ps2\,,Az2=c3^yQ3}P~') z?sZI>9uww;^'`0]^AG=a bu76DZg:QvT]q+w/tdO@eGŔ`|:J3F` %;%Quɧc#J'_'woǏ5VϢ@6=Z=^åh+$Oqjp?3ràⲋL^s)r: {iC#H=fKuA#j w%2L622xmT9s/`=u*wbcz:N&{t2g6Or`6Gu`OĚ?ko޼Ygv{+ 5-seW w.W]=~x=߿KS!k,2=eN:Aj|d1x(6-H+#z^Uq_^˛2^#^:R~ e_b9xv޾v}ꠅ̞#ԕy\mU2J〺RZccl"s:ZUf9seq=ISNc~v'^Zk6pq?x6꽠=;A[x֭[.*T-9"4(9\oQsX MFV/Ne܃Aˍ㵻wuuu9^k-+SM*ޅ.πs'$\`!{&q2;B@B`(ܛivHt*F_~rgyB}w<0@c|zoQY4sq6SǚkE쯼Jr^;YO9:>^;;+բ!..233+w㵱Zxv_WO.Sp9K)b}ɭ뭼q>}W?G`{E{vX5*f_y:(^{a?**J8EGe;~ rtV=mm}e[kCBdc|u  4ʟm'af"q L {W"!-535ŹQbpR%K|y2l 5%sZ󉟍3ޜ﮾j>\[Wk0Qhځz?ko"\f.4sp6pdC?. Pƻ0/9:0־ԟBDgt<vRk.z}}vn~ \`>~aK!'hp|g(^#Ȓ? g xOa= "ڍÝ"#%#`HO޵̘;_O|O qY/v-,H_ Gv1p4{Ww {T~޼w%dvdIz~6˝?++#8kQ$R"!'+ k13B1YȤ^ +5%Ѿ#yv'|䈷>jrc/nҔ%}.qoCZa'Ԟc|g (W+NA dAQN8dPAx(y-?'.uX='~;n燊J9Us9FeKRXcWvW=s_OX"}bC^?j-^V  )^Q_^۠9<φi*X <ۙ ԥ5l>m\v}=~C"};DP bGa e➗%$^l}{38cTvN.5Lo 1$Doi8ZtzAcL=Q5}8/ggAia,6ҟZȆqΉxssbO%t~l}E+se1{c}nk8H Ȝs5Yj=ǻҺu0WDBgK̝Z9N,[T9@B3t[cO<#t_G?AlVP m/(yu\ ka;+U|$|FNω_}1J}~XD5[1AM87s CPY߼aU9dW8G^Pyz|Hx3?{2+X)v{} lzk)s9'r 1xN;'W[Q8F̔/;`!X>2J]D|| CI{_?K 1D1L[8Ixa~-ȳZNL]Mκ[{KjĪ(/>!>ns"X5@ =_!hTְfo,tF5@k5)_OۑsZR@?X}>nl=ωzD/iJIk)^=LO1uтdr:Ggl6s" ;'ek{pi1a2!QYZ%')WH=[ۓ=]Yq>)t^B9N8pgiCOg~s)=k|z\gB|!>MT}_{oelɟN?s5?IW8CqgO ]pX.p7HK?D s;멅z+=˙_rU˺!6&3a5e+fP'}KU$y;@x~Hw+nWwƇ;k '_y >b =F6-֣-w4|v;i/tMG7_}?5LVw 8'`VZˉzB~]{(#EFS6ޏfD~>Δb WkpnEK ,V7/r'`siE4TϹ=8}~™JX+Z+t(z2&[lH W1[Msn:TGbğ{~G?U{ ^m7L%>Ss0{~~r7HX> 3]=X{j=}?gY=/|dv8SS,{MnzZs=sb /TWWZj쟻1Yow ȶ]Ocf]нΜā>^zֻC@36Z5$Չ{l=-˟ٿh/ COMRC+vwVp>=1u^8%Nt[Hm iNmr7HMT?^o){FZS gs9Ľo'b }ϔoIOi CoT`Ϧٯg ,-rv'H`g\!~46å7S9m-EOV4܎hh)oB g٪hiʮFz`l fDq< zWmo?+ya Q3s{PvvZe b&E\ M ԘzBrSY!O(lay6KߌN}p$ 1V_B< Eg~s ,~{_w@T{ nLBCU<#TxoMdӥ#r~"tQ%ptݯ7P}+F4?@CQ<='5wzroܟQ~3"N5f9;/9I{糳q^;g}H:>>'Seg3}f}a G~k-v/O-A"w9/Sã\s?'^YgĖ8zNlZJ}`)FMsN^3IE$wT7zz~ f̌o!Ftg؋`K=N *ezxۢO $x{Q_Ž9|l_z̝>{Z>es@XuzT"z5,?w1QYlu>3]qpaO{{Ǘ{Il1=XV]_PD50e#_/Fċ"ovTe f)Dϯk{~D+4˟-7P q!&q?{7I\gR ~؀n9 Dǖ#t_NrOݠK}~$g}-fαz?,7Sȿ(y|6KP~:p;_Š};Y7Q\+f(7 WU6#T_ q-J 2{aHuj/bzչ/8#h=6ԥl@\aV<2z4}l=HX3|@vl?Iyڼ5rAYIp{R`4 n!>fVb޸oe ?}J8DYs$i c{$.&gnzHz`jc*V1~R{lade޻ߘ*J\J evxg7D),Fg@#<gTw/y߻ 2!*ܬw>Ӡ=4}ЧoVH~,AgֆpyΕ{K^#Ms?;7Mg ʌ5 V|3֓b3MpS< >bV珺җ3b>\ͽ ~&4!?0u`/(Yjm0_o''^8Vh :KstL9li»ߵhOw?7n/ܑ"?vyN|k£P ַJBM`;»n&;i#`z@wW}ΪY*zuE >W]Fv@MJ }q{}k@OΌ.1gDD гO )5q^|}[A{!!"?FY\ˌk@dσ `4̚9DR^gF^4k|;udH}_ 3k Z]"mRŒ! )Y7?H}Z9)V4WW%fF?<-&{bׁ+t IJSn4C2"j'iJ+ |_y^qP1eÀ]َ-.ls_j0_Ns_VbCXo>0fl gznCőD8{tD<ecRl,Lk6P}Rr\v7손 cοD5`W"|gbs.~oGH!ᛁ?[xsl]̨s"*y?TTE?x*kp&c"v5P,gQ/!tأpgȘ-$dzǂ;ݛɌ_Sp43,1Mw\ցYKLͅ\{aM(\ Ԉ]S}%2sCS ="xf`/g)cbzmf:xJgZEQ֪ 8FـQro[XǬ/_'(?3Q-ʡ9b8[ҵ7u/ |]0 7l6J!MuXoK\E؀rL·'GՌECPuJ*Mk p#k@jT"4cE?l nW;Rҕy`)o/v^i_nfa8V=}2kgJ,fe eR>5/)WIgT= ]Nك ?$µkQT0BlQJԇ򷱔uvgJu' grƬ9OMq3'uYx.0s&L91_jlmFl+?Yo#7M_9t(Ǹ пG_gE3+CyτSɴ=C!+%tA@.wO_QsIgIπ6kmez0)_/(0G|4YA8/׎*NRNF.Da+&R?s+]tb  3嘸{ ) 22H^$jҒ1kZS~R`X33/Z9%UK[lstM8kB7;lw(IAdckY-bf#{JV-]0v77mײuP|џ({d?=/?I&j53n1o*ZT>ew#,WK 8Il_cs+E׼NN@фmO5?n+X^ᚐ?;63d{(:@3.93"vM)#~F|8z sP>]`ώ$Q~,ң;t ^:` c.^tV!g |Pw&F./4Xw. 2? ѽƐo Qt\W-("GZ5 ؏5HU&r_GV'mkBT!x}Uw鵶e13%uleY`ٲZ3@&&$9׹;\dfKVVUNY[z7 ObL I{ocj?+y0>MNaMFx+nI[|B詏`uG>$IDy -<"jPZQLg پp;vV?:|<*9Řf0CXh{ u_Zl8 oip&ɂ[e,"8\IS.,.$9z^ WaVp5Y’\L8hS|rQ_v ˌ<̘)JM44|nqYȟ஽l]D\jSx*?3yH)daRlSZȆ@1k4ՎF nW:<|_ŰLzaP5[Pˢ͝@h: 0M"݆̆h}7- A6 !1^L-_|zH:áy/j | 7qlu>_E2`MY|'}>etf/3x<i:@^( M4c|c}&3cC%zx2Xu{"}? vVT2rp6`=ړw^GAfD:sz;AӵQ{)7w18;Miq(2B2c TǑ=*$?ꀹ2oH\s#E}-Ǽ-A&*rǣtb4YcR\1c #3KǣFǓ*D>WGA;\Y#m+UGAvT܌}=/lʠaN{+-Y11 wNx1U=<72dq_ aFّ~*T7)\,ФSe~"|9/wq;WXtvCv!<6s ?fNce/ =d<&5p+ʀ{Uɶ*Y?{}xmЇ`J.X*{+po֘,hC΀̿a+ L$9 %9R+Wh`eۄ-z"}9.V?y|b="R:Q&Va` 6kaL /c^PNr25 n#;g|RZ-":/g`D>1'\CO{Ko7gD߱/xhq\& lEb,cH:= tug ,d`E #ߒ 3:9KP Uk&/F"2g:1n$֪sx?(ڌEy0$t}[{ϛ5-Yۑw'-@̴ P8Spc& ,"дږ wïbυnSb]eF_L"3һ r9Lj&uuBcG3`q?ڏb3<$#2%2 FX*32Q9ui^rg5|z[~Η+N tn଑eeX-11 #7&b1D'#Ax:Oe$_HFh ^@@I't&*Ő s }+!+d=I6ZHgDd&T?́P('?!A/"b<%#sx}'?삊@+wdWWkQǫoKsM^ڀ2qb[0u7أ1|<ߟmP(k#>6A6eZ(3,NT[-cm| =v}#Rƣ>m,b/ecwO!^ڶBcL9k\"66b= Xsc?OVV)gB?؃I&p̜! &9A5;`m[\emm0OCM2*y31`;K`{ǝ\ uO>ag>)o)x ՎWQ0M?fk+裮R1y$xzQü oI+eyv"Ф3$Ϙ2 >T̹pS VQC֌X3S.|Ȗ7c끈q`B/lLAoȚr<x. hUw܋yçz0ƻ<֢leh2=r&Yo<#FOG=g 4v9U%/~ZW0X50CxrB1Eb ,?kÞߐ!欢a/\R0G#"ﺃouh }W͖/ưb;l ?uT9k+'_ Zk h1ل+6/Ę5Yp>|FGy?gF}+_ ?_nFx#Up~Z)aMoh-.#o(X `g4|l9r:7_tߒ #{e0{W~8gh~!`|ZV1 {X*Z#Y\{Aάo/csNek>Bs5OydX} _@/&ՙ=1>DKͫWac۴&|"|]ivetuC'OB/`]82LjV#eP<}CVT0JbX[d-ěfl tB0Y>\*{jt.B ;Xtx1CB>{\IaPO=r57(z&N?5 /~ |_ae!br1 kr!XֶX 6kde~ol;BX?Bc={ s?RALFGtMelGCZ>/8KcRӮlZ\j l2^D9h8׌ij'YnΓ,9=b\#` Zf 8F? 3?m))I}H KHVu.~| _ox+uW\˗/AAvCqJ#(nTh4CW.;ۙ =ݞ]\e)>^+?+2SG0{]dw 9(3|j{ax >XZef!h`rO Oth4aaa:V2GmT.r!d)< \Cgc r NV;R:'5 >G!0ESX-= 𰧀a' ?Q'Y)@qi'8Cl>Z 9 "HKTXmpw @?h[埫_j A֓/4]f\D@(Յ) Wݲi9ĆA%T|gde ׺v@K%%7[I&2-Gtߌ}#^_^R1D0N\}|>:`D@p̭//CNkr#I'Xod@ut0e1;_GnV wVZߤB6~XwBb#G"dfJ#L#0?#92?l=k } Y@- m9cG UJ_~ Cmy/γ 3P#qe -a>("1A}!z@({ eQ6l*}v{l<7s%#Og 3sFTĀH_as_;5R\Й?L?ng p?Oy5sZx~(ΈP(žz7 |Ú?μ2RٿV[VdX&`ߥbbOEq뿕RS]nc }3&'3@iƞs P|fs\h{7T͐<3/f=Q,m;~O\-񏻫h?39T' ^ :-g9YQx9hpGqYgh^xQ?m=Y=ڒg?㾸o=91B8ڦ_!OyKC@N竛?KgHfUy9S?)k9#l}cbu`?-&?AT4Zj;.3<%3{V+m@F}4|sNl˜M;w;?A?)įi{,e村{,ɳyq|W%6kV(Lw9)V5 C?}\/(R^/-5S;9䔅I@)WͲIsB&BaN8;4?^ Zg'ǒv O 8YRk@l>c܏8#U|op9?inn"P2;on4 ]<,0hg?YNOjlӝoI ]6nP{Ahflǒ| pd}]B/<ؘ†b ^ [hg4cCk*?nn>2!q8wMc##>e瀑? ;N]cd.5Ϥs~-_8n=VE륚i;#zE8PQ'~H cq `! uPϿ-F c}DaUxL 0s]ƽgc~Ol.IM}uJ* ܫe p?B#-B"u? |[#9_'c~/@Q$zZV ¸@B>Ԭ>О}X#p/xoklD\tnH#5׀.J ?`>t>x[p:ƁʿÙ]?5eTX,25`n^ ~Yǯ^ lݭ`k=wJA.@4'r6-{I_Tnv!+hܺ=s[B.Ac' -ş_z#?q@iAϷLc?r@n>~'H;9?aId|#Ooku?惍ƽ"y,?#g'Jo ֽP)s.-f>6Un)]|~68 ”!~?s^o̳d1XǽyRSD=9b8Z]l=-7kP^L<t>l@kC 02jan~s/Hr3}h轭#G$ݟjUqx`+^`?9F\mj+(?{\b4Yo=xP@?뉷UǾmj;ٽ\?Rߎ5`Q ԔD?}26Ca R;Lo!yJ3]=6rs?+\åK .ϛ2lO10#ݑb|=1C-5Y#>6$#b]0??>W>&*>:ノ6|$$ I<$O /ΕJ/ 8Ӝ=Cz?DS :Prxg>\׿o~3Z:$(p/7P%kcO8Buk1ƿ,w>0b= y1 =1WWcc=cJH(Qs5߆؏'r @[oAٻ=C䬹%|!}%u~wDݳ[$]G`?!b9֙F|qOc]Ze7jN_ UܳL><.Osu^ 9%}~<ӱـ'>磄;_WG&roΜT ` ~an; u0sahz?!:>|3t_S·$a_X>WKo}?7}GΦ'i?e< 2!RǢ!i"ӡGD=cuP;_??j+m&{mkBTRx1 !A`_ h8k~-Qncl%zk[vG~?3Dݠ mkBTWx흍8 FSHI!)$FRHnw HYx3ꇤsaaaaxIǏ'U{o_ھgW9 o'GW {>~Jlo߾)*/N\ϱov[iZ_ձaJΝ/:6O- 92b?Tlk%?_21B sY5>:>c=1Ow y^- ڶ,XzusM#גU]>H_yYv!ۉ_mi Rus]Xm_g)YY)m]y,m z1aaaxEߓGקo/Y\k6xjgH|yu.\aæM&wk#ϐ$?]Mo\Ⱦ,/ڥQ@~6s?)}, l gX #vQg Bٙ^uのuhm?}{].~}v_J;xogJY]޳@.)oqC?}>@Xߘ'-(W? źvƔOʙRv[K?[A}?-wmՑ}g\=c}M ggg DŽ-B^k_g?F? v0||؎=ǧHPgs/hؑI t~{n^}ZyD5XWvO)"c0vY Z|~_%/,p\ɹyΰZ/;/xs_9?Pܯ5ݻ\[y|č8gʱL{? 0 0 _k3>z_\S |<)b|7aaaxn.ta?l^Cvkؽ#~e)3<3^kdlc&jK+o"e<.ʞ`^(3zu l+6v<ï k7]/lc[`On}򚄫 G뎱zt^v2)?;Wmr5ocIz?Ozx{&!ez."ѯ 1Gg{+ҏlw<=}GݽFƨ^)zIpG K֜{{e G12ۭqiumf>.}~a? 0 0 [u+7Svq֭y΅ ?ނ}XwŶv?ߩDZۓ-q/?߳=<~#>Fk"qzrQo 9r,nY[;o:)@-`ק-7({߯S@µK9֠ɸ>:n3 _[_*mtcmC>qSL=<6;ǫsaaa{xˌ\ފpx?0׋#5zяc]x^l򼠕(f:~٣^lin59W~\;?vn6erUbS~v^U O7O(|;+SG4|?f*?rW~2oNٟS9~daևmH6mX[J~s.ym4ٶO|Bd/b5ɿyU? 0 0 0 0 0 0 0.P~*1@G\⟿KrKXs2(ߥ纎J8'>X@▼QQbqwx b)_K|v 1M6kee-2Ǜ59?K^E~9ϱQﱮYF8N?~;:=J<-tĒyNAgC \NXKs)'^Kg\~2}6}Գ)n]Or^j~"{p29w6/.z-v:+M{WJYZ굢`% Ҥl9ힶկ#OUz+U?;sd~vND7*.Y+v:ye;8}~|+ÑޅN9}{Bƞ#txխsXɿkSV/uJ=o G<ջL'L:D]6jfgLz/+ؽ[{rCMYq~[{yy czA;w9zszWHVax3 % mkBT]px}[TYS}/YL$Ifr9(A@ 9 "I2sFTLl}gw<ӳ~7`P@a={ww>U{a||5558{-<\?XpRQTT$  o߾~7Ǭ믿 7nhK;''!Kϟ޽{'<wsyvV28deXȖ\~'Ē!q8ODιtD#3-ٙ Buu099)/bS%@fF2T~",nN'={'E?+=Z>|?( NuiX !\=&_~?~Q/? / [?._{>'`R8 :S:0w>c]i^ҀvAX[YHp=M[{6`_0<"X K(Ÿ Aq^Bx,ĦG BJ~Q,T 5xpeɋG?YueOѽ D%9+zg 7z.8x 3#3iBeS>,_<%שbb_?246 A160sӅfX`s`/xD8Åasb=s'{2S} ?g4j~碢wSx{Mr^{̒Q}U~m8o>m8w l'v4n7*UpӄK^ւzJ gC.s͐4M'k6A0 0ى#;znBn 47cњ,u,v nBr^PV%\yQ~SXh݈^ۡ{ 2]D4D|>Rƌy9͑7i(iS7PrmP-*ء=2TsXеt rv3_q\DS0Bl>¿Tn,ly,cFt1a7G.Mshwos>u!N7A857w 'Ǎ:nK&(bk]Fe3@d>ڇvN.Bh<yWkhOΑ\0FdaxсUD{jDŽ.! C(/nݛwObz9#x5/dCH8G:CH6@:C0Ӂ# f+׿ BZ 4&_-{勑>|Tt,C;˼jM!pNxkR㥹Ҽi h~&ߴi%?n nc!>t/qBDzCkE{b`(BeSdOHQLyiF/Au,.5?&np!8.q,zӺ-g Oy)K)&Yx|G]' #堨2ɓ,=o1u`"rcd/au//Ƌ3o| ۠`9Ö f칏BxA3('& Fr9iE K_5#p/׆E.wP@<32 ӧyd!͝ܦ.7O۫?!l:4]f]gqb8+Nԩu%]{^[5r.y,|ז 92GO5HZLLe%kN1*{ XDРުq?i>`?CGV/C+:[ϓ]:D֜Bɇ_ $O1oZ[[6m? `S FkùF U{aW <5rHV%-ӄ}^8UkN_óqڮ?{Ym1a>Y\-ʍ-j\kv¡Y]{>:5FLkS~v߲ZLu.oDcTR=_^khPzV#'6FyFשǹ.ՀQ*' w6`O- {T/a'd]chR̀bbF'li!I~za)GxY߷Wޭa{pnhD5m ;Yov Y*N:VSHFmߋ79tpGXݯ IWrpیΛe3ܶ@w=νJzQ| d^ڼf׶Wj9TOU]O6 vGMPeވV  f SI0$6` %@~]|GUx;Liq˦1M汢pArR\ :O5h˘Uc}0se@ ?mqaۡM֛`>Nӱ6g<ƦclĕDLMt,yL ɸoUvM9i<ϽR ys5TQMJT3WPuCtN+ɔnM/3<=m0 jp- V)ܜ38o汾>OxX쁱`6.avp41<}Lg?KZ#8a!sDT3bGBl Xt ]C~F8h :7@h2ހ=@]/v܊#`L A݋L3H1Hk|pἭ q5az` m0d߿l}7BŊX5M~6Vj3#V),&Y}гIDh0 5o|}臓IH?@d#;-9 C~V8 #PXT%l Z՜Xz !Q^@l!!S!~bS8q/Lf1cS>_uLU`bu4s}S9p.FmE[rpi \{myy|O:pc$]!I Qp:ks-\Y}(IӒx^nIuЊ'/ml~Ͻ5Y词ĭ؎dG9A|:qgmK c0{ ϿnWx~~z nKY} Fpr)UG>@j(mcޥËGF^v,ϋrb|4רּ=gErk8ߖBt<1ޞbk<;k\gVʚy8G*6_ !w/c!:rI+Q/|y5TY>;l—T\eQe9@>=)yDP]Ή>w{xmUl *endj?wmR c1f4&"ʃk% _&s]1]rK?ƍz4!s?{c( Bc4-(LEPol6⟱ZO\-5wqi8-G/OX[YrNozW}\:*UrcͭՅSDz='ٛkjucK")!puq9>b./CǦ{^21H#xأ՝k|f`Fd,qǂ{Y|Kby+X*Oa~&w+e6zO{Yp% q2u}!mm䊒p 3Φ/]d^?'|ko_mOz.qJ׋3077-⢽Py =a kndE̍4&K|Et~V u/t7'\35PO輍!O /&\BWELށ^C>\lWH+uj]cQ\3 d-5\$򞝒wڂjjMꯊruV+tazt]_.4esSl_O@Sߛˏd3E!=O+6w,ggH=_T瑾ә=W)tz?y{:_5%W2>#JIy] IO.SWYB@{0H:#^i37cSXry %ǰz/ ]V zIUOBrU|NNߜoKP iSmf5yܾÛu%uݑ?wݢ]/u+V׃WOו^t]@~u=fm뺒ᴮQ.`-+_ot=~麒xJv]s_R}׸ˊϘ \B#uySf0s]$\(s|`1uzuuy L% =:4*u]:˘:ӛ5H VϰӟW+nHQYg?uO}T?h?/|g{d: { kkt=]q<=BYw939r}߈,x`J+JS." k:ã7Q]uWǔH kH׳Xt݊tJ]_xЅ~麝C(R4bu0tlx:|eu=+=Fs9"Fu6KWAK # t6'OH甐 Ŀ$<cAHI:{,J]_H9 ?oyq07WϚt=DM0t_9f-]דD?7GPq*"]ag+ƿ$:KլEȰ+\ˆ/Jl| 6\y({.#YJWdyV<:bc+l ʳE'}ω\ }PpOh}JypOh}JWrE'U#ؕursO+?[}BAcy^ A]lw~>f!79qL}bc_{_`)}E^+XwMWT.J}_];S>w&Rq& _xO\ue@kycZ_tLw@Pdx&Xy 4F Y"N˲g>qB^%j $آ,CWk):.)uXcд޼ [`祇DUYΥ<謏үe[uhc;v=8j(j(,ԍ6S1l|PA֖Yp"EϨ.{åd/Lbv+4 7]Q(- \[Ư]Vxi #sصNELRh `ᦅc?TcD1f5L9_Oυ4= 3B~ע' 6k{kVHjxGW9[0V{YgWH{VMD?3&g]#mkBTtxA @1/ P0}TAw6z?6mo6mo6mo6mo6mo6mo6mo6mo6m '8hmkBTx흍) q ĉ8D^>׻gI@XjjgiЃ`0 `0 ?ϟ|:seQ3|ӧO|:2|.};7eGFO6_Qv]T]^ˮg{>pjzkuo{yye?{-x/ D:3D&򈼹e^Hyi#/OGzϪ߯_~ :sMe#M3Y#=2 QЙ[\s=E8}E>GȩT ڲTg-}VfoSVwzV}./>~!?U1<#}=F[ ~QڋBN..+푹^edLo+[\-k dW(}6q$#?z6Bөi?L7!3O_Q}Пuo[=tkȋM!'}/Ƈdr2_Cﲨ: `0 :8o=+8-4}۞cĥXdq{bUq©ήm!ƶg*ΪU\z[GA=^+ru{LV U?)V>ғ)x|Yҁgi\yi^cUo*= !TY?rfgWsʽVn*VX#=Fϫ+[F~yH\L~[O҇h5ݵTow|Sfӟ+);F;:x )/OS yUo2e)Ve3'wgGg=J^`0  ľu kU,Ksؑ5nY,bXw{ w&3QהNQev ]ƷgcH˞i{A3I8hwduwUIWq8I>+@pQşGcZ\ƪUߝ]/:3d;ɫ:gB9R|GW~w2;fzt|+i5nΟgZY|<1NyŬ|E7k?z/k><=Α}N΅>uWydʬdz `0 *\?W8GY:Dgcg< 2+'W6qn؟{ru"wU쏘~c#T?+y{Q,,^qF/Xv8.֩g3}ȸOP ~n%hUG4(_sn|W}Tg&x^c,Fѭ+ <#+}/Uw8BRh_|33!mr\7U9m({ѝpvew[xG]߱?g;,nҽow8]וb?OV=Z_#ve?vN_WrYLo;1g9pV^G~>[_vNOS3 `0Q[ veO\k^8֔v<Zbz\Opbn$~}oz3ј mK vU]^iNWA#x딫jt q :E= z%օq)CcYEqyRG-+u (K\hP'*^ء^q=m=y|Kvūe\rȊ4={W1;=ݷxp;o@>ȘT\Ԏ+C=*ɫ|GJOCW]x1.ﵠ9_Eб Vq)v(ʑ}[GwǺ{-oSdו_˞׃2;iT&w*w:g׭SOsj%Z[~_˯d֮+w]7 `0]kIu+eL]ւoA^;=GR?v쯱;<y o$N1紈=:ߥPVu< <&3KyC/4r)i=*/|Ύ^]QNН1qGw>ù{ ?Kv:A}E:_n+{u=rq͓̳]>>d}+|L01`0 leg:׺񶊝`W,3O?]\9P~[kOWiGc~)-<w.3q}'vuw$Vnv(r52S;Wk_Kϔ8B/hEՠ'9w?K;x:x<|@cϽVyc@ۖSw8Bq]=2lBe6V}eR( VeZT4ade2ޒ+nYBTqSߔ<[&=f[|szP)G}{Zׅ3n7jpWwftEw[ǽ;`l? `0 `0 `{~i`oLy>uoi\qK|}7Svu9G쯿c¾#>,jow{ՆݲL=mW2u_8دjo?kD߱mw>#}E:OۡO;y`$jw mkBTxoǿr8ر8s:;})MPh(`P H@0 b&&q FFg87N|Cry~?yn9'OZJ PR3*U" TCn KAM'Tj*5 nNp5{WǯgO}?Uds&ξY Y kK4`joH6W2=UdԃR F썆Z@]06;vÝG7^ >|1j n1Jca,V|TIptk3j ]`K ߋAp4փ?lH5\9 Vã6?O?~:PV4'_a $h 7̧\zZ`҂٘z\0<{rW ugk/4`KM a]Niry57z`Ce.DЦ"7t>8?.½ `i szMRcs{ͦ^7k{C;"O\ϸU3g APYQq/ Rys,ټ!dz0xc!bZt]Ր+Oj;t`ijjC鋡$ohVޓbf݃Ok<0vm <0| cD4u|6[*GW"o0oH󆹽f_lYͼyöݬ g08r\[T_sSBR=N3yCbwvBpupyBK.q;n(|/Sk` 5%BȘ_ܽaF(cWaзO r7 c/_8ϭ/hk/]k!?M"oy΅ U ސ Z|-|W\DXcV1 oK 쌕uP:M\΢EԞ#:w0of`Dsncof8\0B{&?uS-k`gg_QL]s֕ 1ݽ783Ϭ {ۦ֋4dn`g8eUu (1By ̍,!+߳H|?T`RdcX\x7$ij "U{А&6\Z5L &A :p1G?@ZE}g A8-&83;4jhHʛJ?ʺBbAf9\D:oǼAi*=FK:z`M6h?X[&MCQNHFV3ޡ3s]CUzcTs⾠DŽ}}{6^TzH9Z.ǠJǐ=.KLȱN!Y-fq50/:@4L ư<Ë)fN:7ڠic%,+@`_0-]Bmk]UwvIJ?rI{y,T@%! _4 5jKbfoE4qWM'8W"pneUlJc!3|juw{ BS/pĵ#KRrr@-`_yڀom -LW6pw9?}˂/9,zc^:0 <)y,L ڤ=;@O͏~CI!`]`:t|]zI;v58{*2ܗ^b!'DYNX 6њX}0_X )I\P"9%=T=414{%#d?wz7_lAPLX3x:C/+}LMf՞r.%Y/gμ'}^pl5A-x}pdX=TM{m@+̥Ra塢S#E3^|owܘxwq-Tp Fqt2M6Zcr} d?ߟ i.4Ztx<[д]ӹAYVS-QϤ)x#ǹ.g1/ A>}'Ĵ,HIK~./2}[8SL: 7!]?`ĒvL1_`-q "Gj>^eд G=6MCgRypGkIւ' wLDbu>:"8CFBsX?p/~۲w<}R{ +wڰk*ٟ?1 !=99|er߿or:q77'7-H׃ϓƗ_Sܾ|AE˗w{gT7 s!ιo{n`U.:{=M޺5 e|)'6ݸPW08rϞzZBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAem-ƆymkBT6x횉m0]HI!)$FR?6c>>~sm+vuՑνYu8uN?WP>1JsWiV_uKEϸ/rˆ_gKW]ױEYcl,[TYHT}xL#}A GV7^}>iҞ-i;}LJX&TP3T#ߨgJl e'=?͘ona|7>?ǐU%;/mN/IfQփz{G}?v✽3X~j{zTAO^ʰ>?sy|G)P#,}t+dL-OkwMLgM %?yP:W l7s_ g!xAY'gOCP 9$K Jޥuߓ"LNg??G过7rͯ>8 mZwM' CrOhz~eh{vٽ */o@?V<'@S]4y\/ZtFcZP_{c4CQuORk W@Tt$wot ~9\; uF޽L`Uu_D!fyfa#_O=wwaّ }Kz,337,[ {B!g|~KwoO`$9'TfBQ5^+vI竤O5Bh g1WSto=j~G0c)8qlZ;hWݿa׼ʃ0p3^8rE?aJGjAw(_}N}5¹o=J}q.HJPBB}\#ʠp2^gWׁj >C8{ۂuQ/]CP^ z{~@sܫ[Q󾒾W}5E?ʡ|%@7'c{~B;PgN9Lեi8hGv?48& v2_8{_v* =iܟX(x1%ΞFmoOޜsCEuVYj7:5ˮJAB Ż3Z¾{{_.JN@w} +iG:+Mع{OCM`O.q3r:.> ZM¹>Ol~86@:T-16N<I9|E&X{}^>MkaUn>Wb>G5xo|˹dq!h{6A|!8/WUlߴA (+;RiX2!<$XCo t}}5wO;Vt_ZZ{/R1LE1)oYtdz|_gG w놘;|!b vW:g9"s )A{4>&}}+fg5nhe tR=Y` +wBB3|ߊsdV%Bz1L{nZ>ˡ\b}u&`+*(SG)X9ǭTU vo'澤Iǽ{?*bLH1[`wFk/_jE#+OkI9XlnR cP0z_^뽇{r/h θk mkakzh:. ^4n\wkv{rwp_fZE=CeqS]kZ 5^稸 ܇X_=!޸QOqٱ'!m${y\#Vm$%f:{0 v`ggÄ6,-|Mz93Z7}͔Q{_bs5jn:Hz%!}5yIZQ>X{3OyY<z$]px.z=a&GZm^};ڷddާX6SxO E+{>^ڟ*:Junuol~ڵd |=!24@1٠sB $⽨9݅!%!p(#ig+&տU4dz~fOo5o9w<JƢ &B54MԾ-7 =_)ȵ8L܇0n%(M`z+)v]k-57ycs ]IJ;SN1rzY׾u+NOs-u ߖ yʄ蜑J9?HhhdKGsVgd8ӂY\Nw 5rz\SҕQzݰr2;V$Ƨ9S@|=*YK81ߛ y}9uV*^1|]6đKA%hlSYL5؜O#S'}x-֪/r:=|5({3gJ IO<+xrF%L%dz$R whn_?[N1$C?۵+{ڿ ٿYx׾Nqc*,t/O'SE5 n(w žA9FC'=r/ 3Wɢkw/ܱ;~jVþ/ca i!YX$wq8~ɼwc!f+Oc+d_M:f˱ scc˿'{7.r?p}Y?<~{ܿw%nw-ȎY4`+w3W-o- 9ֿ+5+gqsC\ρV{|-3NOF^}?/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVB. jmkBTxNQDDꦚ Mm٘)n"D6D-"R8'88]56ރ缓w?&Y/Qf{003nOP6k{m]-M=_C[fXߵwbٱ{%6H~G/K~o+3@ȭE.;LUl_P絊߿n~O@ہ2l~m驭-v_ꝏ^ |_mFmFmFmFmFmFmFmFmFmFmFmFm-S0K(鸝vgW_CIL}3BIHņ=vu]m{['2g~ ggOPn׺磿\=[s5yP{{?词词词词词词词词词词词词词词词词词词词词词词词词词词nHt=;é[C69DW^ԜRҀJ>Ύh:qn.3<T*NOO}}ҿ^<L&8:: Κ}M=#HL&\.7p\OAprrߍ$ ߘژS4JzmkBTxWTǟ?ޕ] 3;JHQ;H &vņbDcbI޴~~ppf9ӘỲa۳ D%J(QD%J(QD%J(QD%J(QD%Tn)99p<{E$SJHZ~<@n}z~Z&ʆuDBg[Vw-Hg*r5+(ײITAݼ,ڂTZEjpUe&ŽH |gp02}+&Q&~= }/@@>%.;,[pO"%dƾ(]mk``Yq ?Ҳ{oNQ<;?I9 Se0=d[ [_MN/_ }͢4OT0h~`Yzl!BJwsbJ.!N?SΛiN?ȅ#w3y}Ƌ1P'BKm+̑,(KzN!~}&[5uat} Fj?;+L$y2k7 =3ȟym/|azYy#N$)/,.kt&I䋯k=dck=)* ɪ!8HvN\h.6\B7c o¶kPs&v@aL(3 \e&^NNR"C2{n4I̋ 9a$6KN2LӂW .R.=}(4GnȯN} Lh-ePth&RSᰨM Qn[YI. NqcCU< TU2X}Gl-Wo:;!l L (;H"t_߭ ciG}#w3z¶cw#uG G rgo<E; DVHgGB0d,A`8볇Jqk!΋ ZŨ愜v! ;!~;LfggˋAl;Zg{Z뗓$4QוjG@#Z0Rvp~.)k#s<:%) ݾ 1 ? $s@r<'vB%_0E\ 2r̈́Uk.Fg6GY{tGh㯫bV t%ūHG?_r؟ݟ*8{ '\h  ^03mvi2 7l0l Eyusܟ`Ew0֛O|-ZBzѿz0)g&k'_0B'@'H\ st|aV+d| y}xmO Ϣ9@=y?ƇO07A>H-joUX(;*Etw3D ^ x7e2Dpܽ3 ޻bv:7PalIb8I7OφS9Mͷ 8mTGi^an%پgk6M+^zcDN 8bjE0ms~4lZS8I:LjvpF>fMbTV_}L>;X}>Ơ_Л)lЌG_-}]h}!Et3(O׉XX[9 khgXZ}b=e-%P{|6iwBfͣpNhiUa2ĤGk^1ilMXsٓZjA0^qMhWkի1UR~BS V%@pv}9k.aҖGp37|㋰g>h^zt NE)w#|AbK)sc `LbS7,|ӳƞ4g-D=+4wk敽PW$g*0v>st2ySՃa߱VK i:Z`j,bEs!4W 8&0YjYo G`g2u,}vAKμIygҾ* 4n_+4{}yn]]a޺ZUJN7)ZcdNZ藱QK ;ӹK\<{vU,ť*ɀxQh>&~aoS\ΙhRZ](8MʲP&GpWb rەX?7Hd5K VU!dX+ܝʹ ʟ˯$Add$'HJR }+`IX7%< ڌw`&fOs:!Z͝5R(b琸? APoz3JETA)sM!y$:dD]=yf\?x9أo W07I3oj_0/w-bB~OxFqJĹ^sSaM!;\? cD2?`nQk[T 8뙫{]JX Am׍g]}J>vowW1il's@۾mM6!m@RbfL \=rk%3#MO/> ?gAhW/629!:ˁ<@d lĿ@"bYVNzpuS]  (yS@=3 4Wh=vi?˂Wʛxgs> a!`(ePdWº;d"w t}z3It,YnX>Mcr>`9>Kg mQXzs炪Jkۉ}z(g0k40<Q8WG]y.֚Ί{٨^\RKi:as} bj04TϏ%E,?H~.u.|0i{:8Ƣp,9<ԂB6g:sNUN|uloKGg9G{p\e잷s[ҸY!m1 |-j3!Жq/f rvQ<Orq[sv [;VԶrŞ@+An>ڀ)g4vv3aIE|uעF]x(M!(T*m!}]O șLgxuw F91Z?Sr x c{1uqܳwgf8'5c7]v{ 8}SD޳FMQAa <gObV#={? ^Q︉9OLZs/k~%g(Ƈ:3f"(Fw=jik߾ϛ}?ACu>. .nw?7gymggl3.(de`{Z%'Y=F\Gqz/Õ(@9;͋ xCVل_ l0> hwQ2!L^|Gp/}v1{֨q g?8xѲ>Y.ɪ˿p߹6:`P7>#ۑ(փe5s9-ϻ;-4n5}s_ Q.9Wj-9~_ {w`k'ߞ#uLtϧ{½8i3[@a;q\;wm{R0j׿s!{x y:;DZאsi-![a1au:}'!*w}~jܧ j}ii+>>;0~\\;ګ>J; =1kpR]-~y3\%+U$.;<Es~tvm3^gsZ[ Io3~/]] \>D6o$$b)x]:_8 x=r"cIBfP?u١thh4T*ɵ::E~xDVУi,֡|÷bwM?ߒ3yy"qgO'ǏPP+/5iD.WUid A}a"q--ZOǑn65iWDH.\(GT^{"#y`^r -_Nɓ>((((((|K?%WRCmkBTx][T89j#JҔޫ{W@iQ @"OԨ9ܜ]2ϓ'-3޳03{fMg}Y]@{SS'mK'shk4+l h?K6.h5Xf,e] k*mdEU﵁c.P5(4*#cp{/B`:B|bB:_fT8C" rt JߏÞ+G >IfMD^fip$>S0D#G39o*򅈍k4O׀UF w#^MX PߟMy.phMdCrNG8pw!NȀg)4 6LzF1 T@0t( :AUvK&} זQ(0'{A8 ~8W`'] =Sor7X ?}!R%Xb]9c-s4ÅSc̿ Aޠl@q Bd;ayNW: R6k>V4 F2Vkw#(̞ 'k8ˋ"ΌZ@sQفPA08Hj!1qJj/D{,/ dF;(偔hG9'sMmsT"&%Zh ւ~s̛<ql/bq-d{+i !Ʌ؅քPV n~S뵁j\~']DsMpl{ / '✔G$'E!7Lg׀}-P:EtpJ:@TORC(! V=Gd:R Z(i 5Ս k:|.H .ԾXLZy>CbǭgJ 2R'Hp%R#Q`n;HA)?ՆY2|!_iLsw%h`- l0}~DJGсvL8PqBFoi4 T=cNF<*;`|3e~M#Mi`NRA` 3EJ[!o$?`F <( "Y,©Fpȫfp4=S X%p gķGP|5 iS2BIL1R L'׶prrG|Qs kGw'j@y`Rx{Gޥ*rvl"jwLf 4 Z3i^?h3s G2a>"p0ԈL0 hd?=)f[,{D!l:x?i!h! `s탠?6?V8+AcPE$/Di]6H wt UDg93WH=UI Mf kp:#Xs )ni]+2 D k$?S>Oh~PK"}ICeT&(X7D*ރ+9o p]]}{SH,~]%?#.{Ɠy s$'j;4%ǎx&P[~ ݓ օ<´I|7{[>0W P`wGd=ysQ?y:ݥ_W? Z+-p%\E(3=\@S\ G= ']mBg F?י;ŬGv'Q;'<ΧOgբs`#zX+bPf9e#wӛ p; *uu1b3ҩH9 ?2>Wlo2x=^4nWD4oI ~QiKqTr??=Δ;-]ה1nOSwMmC&zN\t1qW$}0sXM*0z%δDxN@w>}+QROa=5|ݭ+(KL=r9pIg4ç*ϹՄs?9%/U)Ӭ߿?twuB{S}E̍WRsy=niſg*wLP&?7'9]6;Vٰ-ϦROp=?'^1'h{U7|da X]o)q4y&3SA8/PwEs?wu <'_?ި{5Ž*Pw=Z~q]EDЦ>X-cYnSjIrMЁ4yT'8<âƳR<.!w?Bo>׍9IQ@o!5 s N,oDb݀x IMywŔg755RDwH~TqS_Alt@k'q a,%DzKȗ_<&%ם!y.[N-X_P-5;%n .'z˫[N*S_[>#U;Z%03^̛Zb 8] ۡ%r:۩۽e15@]{aͰ@wPOM6fńzK<"υ{ K ٌZl1<$b677gǨ޽]fl͹jc&'Ho9aog_va3Q8x z=9"OoߚMOOñcyBOL`[|)_of6??#O, 5x5L V-+C_KybP-1_:剟L+O+y.ʿ6Dὼ"2 +?ʣykB(+'y =aj%8C&g`Z'>y"&#=6FCJ?lMXFȿ<AWG ħ@Ee,5fBbx'nskqXz;?n-[BTD@sCz9P[-&'D\Yzy+s/<`VE2X |nO%-Cc P+N"ZHl0` +`称C0w'ߪz N@];`PyBt{V9,=>ka/> V:?l>_ևYCLF2z'2E*mkBTx}+(H,"H$"#X$,QԈZs>U{ ..T}6ڳ-F`p]k߅~b  О$wݓٱ|sCoA+q3lOx@(0a+? T,_7s\Ϙ^Bl1)C+k(FyN"8dPC_9>O0&l4Im+nwGrŰ)/tihf ѸX>E)<,6s45zb?J\<OM%O#(76:= ӋYAƒH Ls6MXBcX&ǘJte. 3.je(??Lj=%wZizFTx$kP8Em jAOހ>~؆B9 ֤8UKCvjbL Cy ;mj P. DkwUE€3ܨ8xUJs\ɟ+;}sFQ(KIXݛƨ 1 +KdX];Jģcx$D׷X`i @l̏rnm$^9΄zBGϞQ=nfkDe; <a>,⢞jk0B[p($Ǡp4 nq`XƓ vϵ.xHnorJ5Hu뇗 f a[Z:>36[g RL؍?( &w.7C#~B{] UW 71jk~ecGrD.=K@WDZM0倐0\xvqNZ ># BE )&yA}t?B Ym(WIpɱ |2+\2 )l8tl@Z.Be񅋍RSƃm>dIl'N adĢG3%#)?$s _5=YBR#-k"qGP-e"f%֩-ϓ378M9ϊ,_*n;HEBƱcl~ ˝[/sagIE2,z1t:kLș壋G){7ond{@rP>kwk׽ #kXfyEAB9uM4P=_lgW؇N#_nGpp ,ZUu6ȓVӰ0EK7*|]{75F\ԶzQz! uH>upT٣o3P)[^6` -d&*=%fY<^ط`_6|h3ء>2 Pq7ώ ,NsjF=B` 큳CiU)R鐏@LҮǧmb<2FHRqùFXi䎲OmGA}:*u f:@ʫRH.66jcGOpO- 6HKJU:Jǃv,3DZEƮqq7p?ȌK%ȧ$;?Qr6pP7`a^=R_)m>D3#£ _' Iɭu͋C-Rne㯄ssL<ȭ/R)|Lt_1Lk=rr 4/gEr~PnB[\g[{gYvRW' {Fem1{ wL;7&$xc0 n&u@5sCCձm8Heft x{q(aтa?Q%l4ςxmWI׆GC1kQ3iJh,KRO`ʲ4)%b6B8\pe;u)ko)#WSncRx{[sXv195_0Kՙ7>Tp5ٴl3S"؝LX睫[5m Q="u}pϘ*xbՉ#iM+@Z! Ϯ~jYݬ$?5mtu] %@݅:4h8ۃtu3; ΑO1A/r R*5i&j#Y2:$Z(ad@>'z L뇶6Z8|`6"X1_z' F-я?X^ A:?1;h/KVB' vOnFS ƤQ{=kh7MwXQp\v͓O/. N3HKRlK"q^Wh1wt h@3e6N|I;y?8t[[! $,ήLe"z%IކAkRl!3u8ځy?_W)AbCO!rza5Sn֗#<43y6"R߃CQ&>[# BHǽ{vekOTlq(UH͵h ݔ8,@tՂL{p/*L"d_y k,4 G̖bD>,.ok"D;|7[.DCA#ilϟI֬Dq]+eE _-- ڰc^Lq1~CCC9gNH8BkhJ#Z-`VoMa 9r$պZ-hkh ?C$ ^tď9d(8P݅]ڶw[wl;dn׆oKd Hބ(DInI M_(5)6H/Y1 QRk,nXHʉ?>df&6^EJmt{CCc`0ʅv5x<\9Yc}106"״!֏9dl:' 1H"z'7QqɌ#KR./CVgQȬ\ `?d1yuM6Ƶ8ZX]8^pwQE &1frRKi$GݜЕh3'{;;~FK37ku<pdʎ+C RMzƏ7)nҀ lEGyl:̑IoBS%|ЕsTulebA}Aʹ10A{KʘӺtjdLI=r PRg_LbR Şl?␔)![Fo wi&k^CV(t@pW2{hxHGRn͉eCbxԉ6GQd27\ثdS=\Ff*0ۣOP5(rZߙxQZ>~GAeN-jY7Ҿn;n?ӹ"Px}/NW:݊&׾:x" ꭥу;R펔 c䛅љElmG§a= h¨BG_uYnZ쫭FYs U"zM&:Gnu.DX5Xn;}ԫ%XO?~2&Frjj8 yA*W I9/ub)Zl: s 85J>~iI3Yԕ;:#hELם[ROd^GA˩f~Y!En0~/A Km>^WYq"<цF*c:xw|͞w%ehRgd9̕v3v Dgh>>?3hYDkgC(ʹƒԕSԜ| 2Q94(?OGQ34 fccPopTYaW(>@tX4`LGٞpɄaŰl\[9c26U M6f,'C4i?W~psϠ?kAKrŵk@I|>^xs?\`,D̒5W^w DMXf_8<%|8_왉pP1Wlm߃f?4:́_Ԕv M;k:p_sj؎qw]$F}y ,b'N=o0, ~M YR46+!}@~ujctCP.Y(x׎z?70WXFܣo3z0c8RGg0 TU򄽻w"/4֏CQ`[{Ocn]+{{ N!33+5]qpj' r9FDȬ)~: 9Gmx2-?sraG"yvUpa;Ră A\& ?#n 0eed~oq嶭!!DzP^H)>oȑ.ļԶ=Hy7S-M ?8ycߧq|#5"2Б lm#UeΤVbM͘jAc7Z ]> 4gb s 2WRsKg6 's8qzTT[R[w)I95xWj #!nN+zPڔ KgTE,?{^RDݥ=Ru^zîc&D'i74SJߔ&HUG[crͦ<׿~4}څh;lpAZ%XZ;tQ?yk1+Ƴu6[ Dc4Ɯ*dB#!}e>samhG3c^8u9󼵕⸈߂UyB;f "Yi=D =4&|C3g]~WgjhSIXU"1A5Fr4{AljwTt6</N \Rta| i>T.Wo>>xϯY{緷m,J{gg}v~)]s!?wXGFl!7U|Cnfﳅ:.@mq%臔Ru?.:aBֺE#Gg'yXDuSWNJD)21ѵVagWPqȒ s?¶@g")s\T{f3go^w:^"{d#!φt},nyWFKv„X4|VB~,˘_&fjp/WԍwaO H 3I`u1ͤ+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_Wݚw)mkBTVx1JqLHe ,ž uX6 {{+*M>2S<ޯYZzz|ln?n&f_?w1 4V7}n? lxu>}Կn`oӿn`oӿn`{oӿM6oӿMsmѿM6<ߦmߦmߦmߦmߦmߦmߦmߦmߦmhӿM6o[_Zz+sw?372zsOe3 IDATxwdgz|7TթR0  .").I%K摬#Y>uc8I)SKBlZƕERE &$ X =ӹCxա`3OT'lEzpq1W\\\\s\!pqqqyq1W\\\\s\!pqqqyq1W\\\\s\!pqqqyq1W\\\\s\!pqqqyq1W\\\\s\!pqqqyq1W\\\\s\!pqqqyq1W\\\\s\!pqqqyq1W\\\\s\!pqqqyq1W\\\\s}.6ͱl[E|2SSSm?cpydnn9 B/̈˗/3>>$K*癝enn/=Bn{i<ҥKB`wevv{٦iDz6k`% I8 yB#˗/O e|H,KZ\\sh4c[F3SöAR{LOOi8 mۨ((KUQ;\\\N+Pr\qz3"`8Tk $/ps~;,.)תuF40-ЏiiP:!{ꪪꊬ*4/ՖhxؽMUUJ(&s~}ED#}3'..\!Q.[3t:o[ ,e68<Ć\N2=ܦef%F͝,Bl@6_ [(R,W0}o E#\ GOrqyظBSո}vÑvO;~A.L'J=YIgX8ӛ[F.{!BoF#D#}LN396x<˃G]=-}pߵ{Zf?1=TP[,3{ [Yce#CV/$!J &FqbblP3rqyPB`Yr[ۓ=g&{+=f.p"z17ɱO4S .W2+++qMa6޲{5l3΅$IiZK+ܘ\s_z/#&$$E YH $չn6 #xc]ll۹p?vwia6icaa`ncac[`6 _ZXƣ{&G>216)&FN #In>|p!dZRtj$1dYP*'k{7ToGRސ' Kh!@D%V T|AOIȲ$$,B7km؇.-lLF7,5zդV1U jejS4tejaԻxpx<^.<),O_y/=K>,(< lsz3Ix< k|;y{ܘw!@(qPPԃ?H$Jr6ԵNSmxdͣhG/Gc6eaXժAbP)K ER^Z2hTME5g(?HrT +JpaeWVPe'g/[:5뎪HNz0,gy\MnYTtS(5X_QXkPXoKh)>y |g]Q Alll&}Rv{vCȲN}M~k`u3i_i#!栯PUslaqgzHZ!-0D!U;>e||v1F$4|D]QQkx(6efW3=wbh+P7 I4&1mT SilWZ:°V^_'sHB\!B6myl}Tɉ8Չ_u7޹ pȰgx IBF lM<*~0Iɖ\_NN2tיS(fyȩ01M4g&1pdDtU@K96 f®HY4+%Ծ{r$@lGYXtlѶQdΌw GM05a+iR1 *NEv-1ӟCSy<ԋ(|Hp`vY2잱(RLO%i7rwdk_&E0jDZm+Q;ǏeI" 26ţiR7M͙j4l\ƭ-CG@O"Ms[F>&=qdKU2;=q2Ѳ>?5GHO2-^8 ѥ2% a'pO5LaP5t*yЩ6&dI’lR;evj7Oܥ?/>>],+++ތ}!.L9IPpϋ_}7(=7`pOˠχWnw(Bz {4t%%I09p`0Aa\_L[gX!T*ow %C$ណ8,r8˦/cARĞemo]A7[7a)[X<]:kjb6VZ@%BUeJ l[UrS;Ɔ'>GG]Ν;wcaas13tB̗}NZD!qOJOc@@!ycZݥu?O$(}aꦁqtXJjCon Xt+r RCQ|[UbBе:{-DADk ѪmTCbeaZ^Pr}/<'cϜ,V7ȕnYU\<NJ GmSuJ+e/uz܂(|O sCG!0M0gKw{hm3NH`1/YfPմ8~@U|[7 YX=rl־Y~Ce2OKeiYUB&i津J|^PyVuywI Qd)&$$7/Zj-ײcp*P4~$R˺{&BaݲNaLtuˢhY+3=OzLprj"??J.ضJYP{9b8Fwc+̝垎UH=$Es>!s 2b1luEAmxz,AsVzrCtjሂN:]f,K'!ͧ?9^yU4Ԏ?\Tvϑ3G'?T9: x%OH=@8Wx @W;0[T%~G=6<`6o]f3Lvg-aRLd)}a8juBZ}oh†hX}LyilFI@s?p=m? *uʵ مUQyUW$ɩ_F{k[Rm4HdC]SU.&IDwGFbq`6b?&}Cn?^&>?ǫ/7\NZ"7o<3c8l+;¯ѫɧLAN>M#8i̠Os1T \F [rw`R۶F1*UÙ&k{^b< ˲M*%j٠Z2M%rEG7LL%HHH< UdYx$Gh2^_Rk^s~Tc,7CC@dKxp!%[ W*2IF}H!U鴟TcĿnu P4{#;O,Ⅷ?§>9>'H %H5sZf}cP0Ll0~ߟQ'j@w϶;AO?̔~g/Uy޸vf>G0j+*`w,C~?},-&WxK:׶샆N0P>z]';@$1mmԭpLφ"x%<^ Ov~^MF u!WU,Il,uh!YsY xezZ/ &mUeolS-O8#͟ G& 껡7HgXM^emc rݽo*1"ܟn=gggY\\<3v^YX]?udK_fW*SI"zr^# WQh$ʹ,]fm{^sK4<5yٍ 0VMo7(Gx+ V^P'(j2 >Ef%Cő!%0@2&FBA4,B\J0jY:  P±"ebY־X٭Ƨ|QLN1?O|k")!mefgg}volO03d8y?~ٯtH`QWb$1 ط#"1KflV*6lFP;0YzqUKNqGoE /*w   ͍j4!FCmbcOugKvr°s8&_DA(2Z wA07>[Y(xx8`#ITfV ^[^PqWòh׶HFbLbp'wvX]:)#HrT/%s?DC6z$ N3;;͛7{6vOEbf*vϓ{x_*ZD*p 4}iYPhW GBCoN*˲)t;\WT4f4&Fsз@'Bj}b!bO tRNrZDAk _߯ ӯu: Ĵ-TYƣʄ^Uq*wt,7EC@Uښ*5]\P7VX4H3TipwvW)|t>#p΍&ۭ|Oc`xx S)&cg[E!C8҇ϕqOj\A?Zx$Pk1ilW?u S?֔jlô(d3 9Ƚq=x9]1%D1:R\zA]`χ *G -pZ m OQձAlIRm]b"jΠ5F QEa@P(잛{0dvϣH,!rI'Jp3˿U&>f약ٍ,}a! 8,ۦa(D4X/MLBV;$YWaq!OU?zŃFHu% fי^|/TJS& $ Y4/[L4mI*K,̲zFZ^X`6/%$9+J(!WNt`3N.vYVWWd0"%̞HSB|~4e)Uj Ә-_S-|W%k٩ՎPއްH/XW Xf3.5N=?ATlT?@\zTo@`:Bb>OXjZ'wa8X/%l0eqڼ{BWՖ0hZDF7c7m@N<dzm],:zFm ;*[/lfVo(5 IDAT2a1=qAݿ3/磻?!h46tXg_oYX]o~zMWse~bדiެ_PU,3٦.Nz`Pu:F4iMJ-TNX 9GQ>+(Ȳt%D;u@QdUǣ*2Bۙ4j`ïI`^h (7dEohOB ou$PUJ@Rv>~Y&=*^vZb=tT7 vf^;*ILD"`Cn: roQُ|/7܄`9;;ݻwOoJpa*El(vYWY]R]W3O,/HCi{+0tskvJ,}m&[QڷO6tiU.,̖H/Vj#m'sJ^?D"~2,M@%YFe^@G$$  5~ w.ƂB."ˋloEů*guqަmkNa6ucNK*$OkVvڻeV)agsW_}/Ùm;Sej"΅$s{vb-tH P! lb3?~펏?,0a^>6¶ݕuYvz"nlW;{ʲ`4w`*fu͖\TnOL$!_5G!##N$^Ǚѫ U!L |}އNjl.HzeF3}!q۶ai-rz4Kki=;?)J??O˲YiuwUw;*2^yϑt15DP^OVvբ [u UtZB|XO kbY^>2DqճTBj=;beaxjd}o;,c0FB [=mo=ֲm6V۶7*ղwl@HYi Q/p6te<G,G21@hmL"kˋoH4E!>fOPdi)MN>!2%t JAz6Iq*%"WՖI§TeEV%WTT*d* ~kNFZWֺ> RB\_p'E%&9:Nbt۹<yo7_^!'vM"~O h*Vʗ*UQ$~B*_uVq[I++sZe;N_QzǰL6*e7*zsHe܎~p Lv$ s{77w/uFG͋*4/x?fN NWǰй7{sQ&%}ަaY[i C3`34APew;Jُɧ4ܱ0- DD\~$!O9\Iי=+^Y&yz|ځutD!ƯoS^A/23qy&j_[[;s?t=j[ޠ9zoYD7, bRm8eN_)0EJ`VF3!b~ rB/E%$ QfJ(gjr$C}O,򭯿N|tAOKOΛK(d2='W M bo]ŷo̱f3}=eKAF^!)盗<]Bc*|gEG.66;*B[eFM|(!~[zxnU.-- uIL_|WQB<'j$"lM[+VcGr^lװ, !1f.yӉۋK:μ'4%Tn l*|{ijsn& q 6KTkMEE;a d#[>d*93~vׁȊB !a:bV !9f0#_,U֖X_^RS:L(mɞ^SR4v& lzKr' 0蹯:!7xP""KL#;j%}-[?[7a/}ɞm]*CX9$8?il^Yd}yrUm|xW?}g~N۶qk}s >|{P`I텖MJ>iܹ^Vn$GA`pf|x=*):C<`һu [kL^![<罰;5{{KG0TKf^K:" 2vgWDY32[U_ޤޞ@ ~'"ɟIbe~?σt!?cCLH&ΰidV"$}4iRc &R=N_{n ?U kD$qNX5 ,ln,oV٪T 5Ac<8xض%yK4(xu5,H{_ I˥_ܾ@m ;kLJLE(`Wbbɒh4+/]er"t|䶷[°yB%A(e 'k5,OO]]Tjaj&˛,n^ J\L._il4jyƐG>9@8B kn{\NNny|m6∔{;'ğ'zxlӨRB-$!(Vt=mBHCH1d[[i֗Z^ #-Qo~@>$͙U%h4Ӷe[|JMs{-TZ;]D$"L 32׎e` > eYlgdVY_Y"u¿  K4mI 7;K͐f2- \|j3}W!Pq{/aĴ+x86w'|qŬ kFArg[h m\/3iF^%zNxL'֚D ~ \4e+s{>=:󢧳Tg"\qx޻r}!$0fiׅml&%ͷo 1>#;5UQU'pVտ% @ap+ӾSg+D$E5_Fm۬mw}^se:*d$xrrF.xGO}PW wnmQ4?`QUUʔ/$D F"QCx;2zNzeL_&yzd,.m!)T$L33wlhWth"tl?]xs_;ԌЛYuAmM7ʪ`ᎉ׾|yvSMגq?ɸ3 BmrnV{b\*21vR`W{Z"?k1|&Z {:'֊{ϓ%XIWb9"Hͭ#儂Vd'' l |afzoQ)PTW>#] T+e[2lmʤY-Kl>0K0xb`8K#sҦ9k'e89 Rlh~wWV"xU"IY$5܏QX}kkl6($ |Mc[: ־]upXXk9g^8D]HswB2>v38<ηﭴ&)_Q[Rx$NE[*f[0!NeZʦ&: ڻoݣ* F3?:5kka: }| ,d+fsm*٭͞r6BR!*$IR\SZ! +\M@^t{! 'tc[6/r#'/fp~f{A߸FA;i\ͿO^dG2,JofuZެY8BjAӅ?TލEBblt!vuBr6j@~{™JR5^M~.O(k|?TϽ[Վ=J<߿zTb1#RG̊!12Nbdz*U6V)u!ՎMv3Cv3Íw$<`"c?Zze!ؾ/Nt/#Wx!^~n˂[wBjk5wz!ņ"Ny`ic{ڎo\C ;ʰD:X ˢT4[+^}ѥ0D?D }*cG#Bad73]7tVY]w~8%LvfxbpbklY[Ao]Sa6 wY]K >;⃤ZkӘrB>'+*XQg^\Fg_c6 l tH`#a]%xvLLLP(eH`2\ o*{ _O; 9%]${![Λw !&1>c|l?η_\VH(zoV*l՜Fs ^x7zh5c"_&.[~??6Jsk/혛!7W #{m2lf/HK S3GN Kd6;3;h^PsZȯ3~dK4+cPgb_x\Ժǥ zv]nTL>DB~>&!rཛ9n˟>4{w^o{.I 8dMl =cv^X,hPrV>x"}?E2q`"`"Igi6kKzm4;in|mUe(r*i#[8;$Jp156VYYhU,~)iYڤT!T37sBrۇ@.`V˄Kχ{Z뵞ZD1_V IDATbVԁ G!HeF^ ̓R۶_q^G?uc?#'WxA^~np_)ZRRR$_nGU  t7Ph#mNl,淗v6=Zg%?YbCg<"W{j=֖c:P"PrX2EngzHMW^ecuF O5>HFV#9 >9bABm$R>2iqb`6z ~UOıPmW d|dLDoȋ!֯)^zӽS ~$ .Dt!3Uw!ur!إ=[n";|{!UjI<xOP\yg^>0s*3Wb&;3i6@/4<̵]T*I/wb/0&6Ð2p<@2$& [Xb /m{w*ϙ?J%T뭻ܧ=%~}&Ks3,-&"b0c 9iNrccM f>*FݥzTr稈ΜXҝ׾<>?5"ozы Ϻ8U">rGIyAR&__u˂_g*Dù0M pxJ+7zKHK]WB"IJS jWة;F"nX[8A4PS"PNp䁯(%%kuRJqrHTޣ 4yl@(& "؎MRDbI<#dNWTXZl5v| aRԜ}cY[02zcui86GA$ST{8q9{#A1K_;ח\?ٿw9{=k. d%TJE*$ODI$b}wU#vNW/TZc|r1Nw$ $Fy7_|[whHu7^/=S&1^&Z-l98WUA8p s&L|Dcӿ('//薅Dbt[ۊ$A@Q-T5Lnl&eO+I!U!  w  '7:+KĢ7RXFG !+ :WsX|7~'W. fJ&˝d8^sA N!8Iݍ? owwDpuarwv4bT}h,l',]4ztݺO]B&Ci6$k SgJjLZz&` R/O-"JQF&"z WWƮ%Aʒ|D,G~~{9cgv,/TK6SA7Ȇ#܌A[#Mf?Gzz>aV@l^ZZav&=5ZL*F"V'T1B̒{ZjD\j i &.kK -->K"5"]'moVZzx$3'89=pkb<|M?M/8>JI3k<ݣ"{{|s✉c T??*[v|pax(!ٖpaYq=^XXlh!nknP:DrҚHwODh"I4ڦV.QVP=f^ϢjCcLL1<>z@\cqyUh^!aC<ՙd`wqRtU5xf7 HDx>>ptwx+ )5ѵF|OGx}"+Bơf@ "5}C߅ŕ2b![wT$Z( s[M~u9Vz,dUn\EsíAs+$b90rK ~J>,&]ᦶ/.D}MŶY:N!91d/wU¯c&Ʀ|h { k@EVKUN]n@0 ``Ie\ b`vnΙc܁yTE}p$3Lif vL]>>>F1rm(A iCQ<_1;=* DEFxNA U_Zj6VZШU~/^@er㌌R$vA@w벴m\osv`_Y뿅ODž _MTl ѫ2edȏ"m\c^-$<;ҵ_/?#܂Rwƽ=I A4<-k$Ļ4Z7Įs7C>tT TbQݿhd!mcZqv7#SzmuO oyۏ| \]픙\PNUnr<33LZ#x{ۣFv۹&JUI&#QXG9$-7f-{6l%\xYVȎ284B9Rw''@8 }7D]FÝZZ{WbQ@mT/)Q[@4j6SGUE: e;l+l+X"$'5!5cOb: ,̱?t:Ba\nxIǃq@_]'~}RՠVrp]IڛϣKw^ف4NqEޓX1:הB'~W7}籜!v ȡD*uj;r~o/r[|a[8UUP%hPk>|$Yo.4lMd˒< 3uSgyVXYVv2 FZ xwCa,-ouAy sG16<_6/Ħ8!>MZk`D]=T͹Hwf-l$B:bS }/}3ǒ8/R)5çp$%Bev$,D& {NlKRԴ^]c(%DB!<0L DqxF5ȗȗҳWFBLMd16F>.\Gz=J9p#1 3'&i!}Tl+͉]7{6K>>M=0< @]zSc]$<szALQ$\4J\SNdN_봯\"k{_)$Fz,Rm B"QL.`wrsF>tT㱞7,T6eX纬oED"!4Unz |yYdz2DR,CrGm+:_.346@eiiم  u{H>xs^xَ_ ׏vU©Q 2heLO:, !ɧ]D}"h+MwDSv7b@MlAϊs,26al8ChQ0)W-L+xil@db(Dn`(Hv0dܑ%$EQo?FsiS|dId0x?ӗ|5||?/c  }YܛИQ'JI.m)*!FySx—|H'qY/׺@,iH$]A! 1hKUti۹A#01| N,@hu9nDh4B$!Se66|k"!2%HӾ/xWg.^};#żV~m|>8D"'/`$o3T6-`00MzտzM`a3?)&DQ`,d>=*ΔJT5n #y*T[EQ => AȢ3 pR+3X;&Di$16:d#JHqܦO`{[`V9&Cv0W]΃n0rZXw3;oMkz$IRN]Nr DU}E7'ť^ʤ(Ԍnæ.tARʪL*&yc^ӜvukeT@.!Ԓ&rd3Gu#X/WMw_cGwXOxl9S7Lcv 9 :Z=9Ay"[)%F[<Ώ# #>x A|Sa:!I"Dgӈ(¹SIΝ & ^\\|8 i"zK9s2͉ |~~7 zuU?ȡŏ.erP"mX, ;K Vܙ$R6$[+pP"OUdžxyiI nPQZ! 1"K,%ʄʌFhKHY&hH>Ʈ|JPv?ʏ̏t>.uUq1;UU`GIߙ [)n9\BHF%y> |`5{lO ;֭5 U'"`w+ Ncel*F$R$.P{\@gatIHe.^= sA׫/qx['oUsy68#:ZYpx.z7IU mAl[>zZwn4T)7\B(%%IcG]40v]"M{s<@eBdJUEH@<D;x2ܓ|qnd)cN&9wr4_%yx ZONm5v%>Ə\- ⧻w}~Ku_$5yTs2Ne2|"Z{{ ;#0t 6elL.A&sN uCFznQ$ ,תXh,q,Hk}7$IO{/Hol6HMhL!Ib[wA. y]Aջ#i,혶6WepEV?yDRv"k+EA 5 $依+q* X۪Y-cJR0BFaSC`*@m^ۥR5*\ZbnN}mnR*7XY-ZOP螯AAU5~_'>"jCiY'=b5qc ]|ԝQ롗J9WLvo *}bFP%١$\D*^[իݣviSJRr(rb]DbSs}geJuFm' )Az4ݬ}ɧ[p(O/Q?^Õ Y9$>bdvswQE!ގ͕wXyˉ ȍX[}G5z`*iҙ8$Rk=& A{~P&tX6$%dFepXcsu Eʹ%fV! K~ssw~zM}mC~?|{$cb  HZ&V Ȼw$A`cKwuD#1_c9mOaYˋ,/n""(,οO庄dy>(lɇb;F,zۅ^}q$<8"o+ J Ek %2Bx@&8n-!:m㑌o a2MGyY[)a幄q}Z^[[*͆|irh30~(mo:'P/ˬm:kT\Kq󸬉2&Ae©"2$7Fӿ1߳[}۲zӧ,$qO\ !ɘP;+B*XG\U[9'7,E@hYM,UjL'| GG4Hͼm ~#ʎJ>Bq@=lEas$;6bPT^4gj G#?"_m#T:Fj Jx<2G|^}[."Hhy]GF8Z8xn mCj Ɠi;SUQ{?!n]>g_~9lOmQ\珢ĨJrL#1JR*X+k316um>5s_~,h%|PBWqZ vRhGR L#au&~X6}+5v7{\J`0rARC7DHMz}]89uSon*/{oxQ(nYS@=oS۬ll՘Dz:1C{X/:\bau{ID{>(D%{0g{T㽛M|p"-Rh*I$ u* aYF O_ez2D~-q|s-O$Cg{̰N$b o:*ղ0pYgZW|{ Qa\V/YPg`:1RھVFaNNum&SŇU@|ktΟSC\xn_g@bnvp6%`nq*,YC(coJAj]zZ2szo66Kؖr>mcS驷MJ/28,K,,uA).+G۟j~':W©|Sm'A:9ĥ ~<ڳؖ3V0m @ec"햙[ͲȆ{g#jATD)=(FOGXؠ7SXt_Bz7>B2ⱇే8ċ煋K_U<Ͻey>,ď,؏OL'sS47`gyir)Bz]d\%4v}FS2\;*HyIl צyw7hb+;WPmNS-)qq]iW: 8 8=Nck^¥g/|?/lh\};aWO}[%Ei|?.J;f9>aڮ>}nJ${l>jXQaϤqͲ; &ңI#($Ue""cM%! ߗߖY|oW7KJ'Y߬`/a4_EյW&Y}z?{Y~^xqE@2-}jjW… ຮP,( K:5@*}SoܓO _G%GqGܑ=ϣT vѱ^>J~\D21u޵W@Fhx`;..Ѓ}=./m\8w.Z goG'$I"r98 MCȃ| _N=7yGC&3:fq.w\lƉ LA{D<ͷ}'ҊA`"`\"Mö,v客(*RsҙB$ "'-ϔظ g81]=F=d eS(y$28WG!~'K]?NʕHA$;Obi`kd*t"5l"(JWL+J5ۦdvI"ϥlYT"Lٜ-YSxa:hhd5&q]4+Zf ~_gx'yIMf<^D~u$>?$HLM1wb\i}HxOĞϿ㵈o $T |Z^HԷX{LEʼn5KMeQd2 ,)v'\3_+/'5!μ}%[|sȯ%'?/Ņ b@&Mnt\M}y;JHFw<"nkPDU7l<z% Zy]'Xr"ٹ 0O0W)+ z$h]X(Ly$J\WX~ޕj\\GSd9=cgGy`:x6u_ UjXv  #J"M 4w f>#ͱ 3 "CdsI6^hH@X'1C2j" dJޕ麬7 Ez d<fبU4I`,Bfy>t,(k"ޒbqV/YyӣLKŠ;V97tDʅ.SSirCTJ+ض*dSo أQv' "0DdAl`3kUE%}gZTjQ(QE!KsG.0E}dQ$P,ʦH~jt\awj$VjTbIP|&5K~7~sAn8E$hoj-" R*eX\ r cRD2"4j}m2g`,S0 \C D`eŴ0ݠ. aUL1Q 1Q Q۔L*K%UH;z3,W_Upbt[F iaWFcDPTX٠\,I;59.`pM$tt*mgQ[x;HT`.s2'I$Qj[̗N0߽iP$JݱYn3b 2aYp*8^Kt>c9-CIPRf d҂Iq821؎ˍ5n,UI$2pb4ljÙ# L"Nac%}qqW( LqҾ`y9m)ַX0$Iyі )9(5]FPU1E?41CTb8N+$!MpCB dΆɜ ޓYsbRz̮3Χ!KsvbsSdZwv Gi}377ڴ>nJ"0s}gDL 76Ui{<`.cJ$7:`>k2v`Uˢ8,φ iDQDE&!x,Ds1\|MZ>\ȝ;|fzɡ$8za~5jO}0g'R8=>KG`:w~gp$LLec3qf70Ķ꡺kk6%$5a^X0XLa'WJ$Yd iUAzUu#,H(R\<`g@"H"Bd'9ۑCö]V *DƞMyl%mzחV'>҉ƘlW:?cA 7:D<'.Bw w5 {O7Hr!$ =L炚_o QB!L?@Hmib.pG1|O:"$u Gl"X$#H Y̎Qwlve*ۑ @,6 b(--ʋ&VDTi%cL$E|λ ScK`nlh}qpWԉs}ul -$6yu/k}X[m0:DLrҴCd>`daqy]',$CGue DEQZGbH "IU#)8夭a\U/:M&9 n1W0We&7 LܽHT松>]O+K{ M-Ey6>|i^urx,80'  JNx2N3yBh >^dt hEls#ɻS0 B7:w a]ȉc~ vݖ hut9^Gx698$-QW#nԛplC^#Dy$6W2&x⁓gʦ>BD5PK38vyaK47=5Vet88{mrq֊x;<JAMAJ QT 1&I HȞ"J]$ !b]B~I>:u[2Rp$S!N%-_n~q`Rp\fxifL*yϝ8>_pA2e`0Fqsy¥efT 0fag.{RDQ$bq9xtW2%@Bz:mR}\\L\$QzΎd8zX|5}mA-  cM$t(hγ=E Uzm&Z_l~8X4/U}O~yz },>w!k{>f4ҍu춨@5]1rpN{o4(u )ѰH6Fm^LeũS4MF1Bִz>Re^5:L$:;E F 6C(4veR=875[R /6ؼ(PM sy4o|<فHKZJ1_@$rcC(wǽ{3qry[8j/-c|\$"l%MhNamN6!SyCPo2  3:eZf(Cmv+$$LdR@42/h?7hZQ[6r2'M5aH;оM2NHyMgRѪ yuNGsrlh'ݧc9.`YVSsWMQh(P,FD Hw5BU'pcF{4*][3 r\,/ lٵaL.R$$3 7)eQ-C2(c=8gLlwN,_~E9?=~ޛXYX"-ئ'W9">9/2)QAf0>LY,W79 'R*O&W=FNk hE]$s)*(=c.V"g5qU%v%'|| E<˂D̰u⥅u#.),#,mQ. [G35/5ݽforj|{ nGZg9/D_]J=B'<="o>P*֙JEw y V,>XK:lk" j`=ZRJM]:-8ss+̬L !Y&idFbqn"y>V-z"FBE%@<߃Q@kuCߪLr7$P8L&\ʦI4PF%ߐd,}^yŗ9<ٻ.8+lo2[X#s+k{4-=yϿ= 4Frsἓ%,绚v"7gqܪlB$b0v]jmni7kxC4Ktʆ, hY,/p6-f+}fX4X!5s k"2g=%QE"(^ Kr3~Gzc "BBH$T i}PRIi`hlDc1Ybv /^'It}Uj+04#3:g:f9ku2C]g[6Wgw-'L>Ќjn#:ˍшRʼn,7=FU$:3 WZj@:(LH m9#ӻ2]Vj Dυ9J!3!RZNJ 4 \M]gSבEeŠ=Zfuܐ;H ?ŋ G IDATNaHHV B1UHKä́qcȶԭIH\$:Me+Ȝ 9&Eg#|¯3= %新zHVF]\(%纈8ZXkNX$ǽ{&Xgph.JB / n<8$rma!6|d-ٶRPP8EDF&=Ut8G^o(HB ,#/)8G2Zb! aY&,+ˈyYUb8ENAT/A]YIf!_f('tߊrDp<ږBL("3pl6uiY}9&}*Wjk+ +\?kz6(zJuǭ/$˴)n& t=Wi"PT 3>̗n`4I+:(,#((r5_l-l4s=6볲\'5*yZ%5,ǒ?؂y>Z#&fl!$uʆ&uv=()^T+ \ݶo8;udQ$i4 ˢfYFdH\a8|~CߕHDI`q0V/{4{>{2זVw@rq3X>z@WؖîdcyȦ$pV g͂#K/H$j_pHJrDΐhJ ¾/|%;/h3EW3޻ ( 侜H,\Qy"*q2o{k7榊cj/-U~@ѿC-&\f3" tK77Zx=/|bQt &+/Xˍ:WvYm6;%-c\jYll[T,Zs<~A2)w0jў:t)gXJcC'H,LLTjlPx& ,*U{GzG'ʤ)23;3vA|ʥr7$tuiģ"./G`4.-Ͱ6yl\R Ud>iDWfT9XP%Ԓ7 FT+ږZB1N*33T6\j9zBT etM0` ~'Q"貂WV2dgα5$8)gdH$ n.8욝>/dQ_/RGV?xͯ{*v|oy \`fpwuB_^"../G]iKze C@=ϧ ^/J>]`L8;vJaij_PtYآq5Y% 渝˓3F ;,1O=[YCmTUK7BPEe Q5O!$:?5U͗@.k1t^/w%tXkaԶ]~j~ߢY&=+^pl6-ժ|VqVMqߡ{AmFɎ9(BGfIN~ׄv=j&CJWU3出_m4vj =~gz ӒH%˾]t:6Vۤ0W@ IG\\΅!W0-ř$UwI]Њu7@7ޢ^ݰcJ՜PUn,#0)r3#Q>a33;{2HK ".&f[&MS_Rϭm͝jD*'0g2Z }?=zA*e QJw;FB&=QN% 'Ewc YAWtE>/:n@gR$h"+ݾu9 ]O$)T-m>ĺ6}Ip gf122u⯧L>NC:ZΤŢa<"T v:É} Uፅ"3ݟJ=@ Fck-ϱ }JfHE؞G6Z-VM;MpCA'kjRL\kq%B<\T@bEXLwGs]2xo3ss[o?\#tm*S\q8W`&^Co^%שͰI-# wMW[X0YظoS%C CB`&k&Nt+X˻s;J}8>mJMoeGG]?1B"Dh|:T=7s#gn!G2="F |S[蔦@Eq-N#7挩_+ nD2)R$v%Ffl@Lb4YPT En+%0uՔKzVdIb>$g(Tn  :~C3yVq|\:돍QL8wG;&2FK*yR@?ʑՊjN哞#<69v?r9"ča 5?vY5K5lwm704Ą)XkvXyRQ#M,;*p5aqLl4WRm'x'x<4"Ω!'vșcČv1Uͅ"x UEj5QӋloul Yzd[len̦SQ'wDi R-e'߷m83Ӊ5L ,LK[E,ժܯ-/eQF}J b}dY"ntFtGD95KW hcA OF.a̧yHb0TV Pb*@lVWwc{'6]V?XdwաrgoeI宐^c͓zvH󌏂(\dI*Lb"|Yy7p5~_D]a,92"⅝P6 YZ_Fioj ="3Jm?/J)Ө;8NHڳЪԷ]N@Ihȴ~A#7e2i`/!$Wia;{^]L6A:8.SL,C]>S,Y 'ɾl/^Q E h8=*0m.>5:MofQԓFM({9wUC\Vd5^o~EcNQZ ݅v!G6fR.y]y &&Wit.$SDBj|v#!*8M;%ܕ-..?4F6#~q08qrz,}@2xB,<,'vHP0'ڐ'˪E>N}{VB `vL Iɹ Aw\]!#;KE2!?&4>]2y)4V,q`w.ͧNI]qCvݡI|&l.50=f/e;ɁzJzUop+|í2[&m l }L6nH+"ڈG?\؈ʡ(O1s\1Αq㱾Vʵ"_{ E?}0g l5,}bge[|Ԫ6M2Nk2O ](JaJL,%_D<1KN:ʦI1,I\"1ֶV5*api&-3׋9{Ḫr=eCEIMj:F}D*Gh'4LgX*_TUE\lεGk%6y~^!8kN3!pj`쬃0Vas;&f;"bIj!(ZmGqlӌ*"B 4[8ko^Ce֯|D#V70nN8@j;,7j:T. `&f.#KcQlK6AT84/py.G.8ʝI쇂6ZEt\>]Q*qm?AgDR*RZG܈qg‡?^~V 7A { aZ8癙Mަ*wxzQeۅϒ tږZ9zȨ!UG;% 64%aHh$i=AHND{ 3.[&kՃ?e*mp=E(b>oY :D!!1c3/KS9?狕fC,0Oh$ht&N$Krb`?͗ߺK"~i^Λܝ .ȝnH+>g@lJ\ЪaE:ܸg:}rvkL&^B{1ZEIztږC; r!\bg;s3kwptR y۲GVJyf ;/R Å1_|]1H}3=$I|M_}l&>l0 UN!ǻK,eRc%9tvO[o)FmNfs yiWyA@gz*kvt"B%k |"9$e鋛?H{͎! k$Inv##B`Uw\x a6*lmvweYoL:l׹}s|湞N@"!P|2T6B@岵fuIb=: x`ƵN'`ÆK:A.!BP` ?/h% s\jÇ<uY/|N*o䳡͹7OWK3'+L2~eRGQBe2)-qCeʦM2dd[ Um֞6\o|>vg]lʃBjG-1Zvh7r2qwm k$TmHZef3G~KedY&7sh+0[RRmR4:2sn n'KֵcM1;.y_ko3K'+rw k[Z.C|> !x rK[sՕ&[mZMȺ>fgSVYYǮe="Uk8:k e8^N6mWj| RXMV .̆tƔd/l;O& 2,ٙKG/΍!Z>x` |&,krsa6Vlox'&!q/+_{b!u"hBSsK~*p3F]Y+Ih7&O^ AܕnƇ㍁$I+\EC{/>Gn8?[xA@ѮKvӐ YzW|icŁ QH(ceCeqҼwNABy0;ⷾ>mIL %1p+]V\8<1[ esˋy毽_o0_̜YOBՙgfH1FIAF{HnT' TEac&8ZöAX,ds*T,LA$3H+_۳٭[+hp@$7,<ǫX\zo7YϞ~Ic(*2E$gHiI4=W[C-98NT̑O*K,/j!DWա:s_NN۾G۴O lm)IgwO|-B!z޿8?ME$1eH? Mnꊂ&#g!l|0[h{͖nl,EDD//!U<~5>vxs ~/<|T?zJ`3/N]Vg( ?/wEvwE3jWPYw)^ I1]#OމGuJ? nd$QG]1Fqm{6|/Ǻ \O>/!tI /Y෾>wy%RIdI"o$.2K I>{g^\7+Trǡ< ms ;֋jȤ us$^AD$^CO&HHHH|2q~g?oqͫ$ gI(Ȓ++CvKL@|:Ҷcx?MQ @fi8G (W1/Dhh.J,/Wl4?XjlR #;~L&PQ-&$S+XkQKPހ|wMźCMC\hV"Ic2IC_vW4ZQ b μ!h,>>w^Y$~$$jbܽ=@eQ.5ܮSo躆!9IPUx\G$dYB$E&T xL'7?Y"JSAXCe:. @!]PH1@Dd[|к7~7ji\~9;MBq _5؆ }@j:=T1k}x%DL 'OB}Y\{%$ Tt*+Ӯ|yȲ($+oXe;6Z'+TkM2"LjJH\{Wt1ugz('Z Ll&s@7ѹF:)+;``K9U6U W 7: }? Ž"ɠLJA@x;br& .;ۣű7ϩD&Av: ͘|v晝5'0 :DC;p##ê&ocz1s<".._j< !זH~-ӣCA (=Wr;_W/ %a Wލҏ| >2BWgU=_RψC{Ì +m?c e;:&"FL4BCFH )8|?_?8%)~ /""QY zg˱$5۟on9t*.5NMcVu 3ˤˤFVM)-"Tz.؎1WjW}̇?~Z8˻701@`_!!Y=\gӓ :w N?{z.-i<.Glջ߅I.!6!֘WXg$$z:;u$ 2$Ź,,ԋ;U}#" G?yӵ:uϜFwQy]ƴ݉A]Zb,'3@R%){i~W<9Tv!w'B@ڦ^m  Q/TZs>^T~jT\|Wh'}mu;{I}͟i&>g({A@xiE=+<[ԫ''~<}R?x7kN~fԵXdB6YF><| Nqs`9jqm`c`r0!K`AW[/$4K ysˏN볹^as,KgR]oa.{d)Ju|/IZ8=y­a)i 6A@L7p@rJ/PgxYmKs{@HtcR=vgа@d"9uC೵ꇳZE$F(e>cii:|gFvR::"3\rKř㝊Gֱ/ vxAPlhb/˰Ltmri!hAY`USDĩ91i۬<.򸄦fgRfRS7F'pѱ(,4QF f訊7zϨD]? .G%!Y఼Z+|u qhG/|j H/[U+pIH膆a躆ؖm4;޳ 8IaAd"9uCpw6)-džG.2QhFyv,?kh(,)cjΰy'Q {Sžlss+ <unE>r$/QhZe <~[ 4F ЀM wh}<X*jx#!oU d#$\w1;6;uօ]FьhcY_oHb^t]?Kn+1з_z,+MWz4))92#Ѝ%CxtP}Տz$#N3cz$BB_Liuu굓>+(=yfT@?!pa+IMlyq{ups{Lld!{G\,Τ!$JǸqkv r3TE##$5];k+K}4nʻwlHt6[܃<$wCPf@Oz Ǝ0ҕKW ~@FȊߓ72GQjxlb6БB_cY J6vwR%H|o! p94uwNN┦F=e堤 8P\!xURe fA@HzDH<} @`);c7,y0D BAKSo]8y^Lf0O'of2IZ'>ԑщ{{džr|AY'7i$GCC%\^ =4uwƄщ'>e g L6<ޏ[IP%QuPA8 dcp~z<@d"8Y(M ѐeX93dVIxLf:Pӻw:lФ4 􄼚5@kg?b[)MF"8r TÃ#$,IRlW1%t!1;xJ8QE(V5 b?46 m:jUg{5JSͽR/G0 JG$*Is4Uf2}:XnidBGypB `X?}:Yly/~OD@Vb37 Mf*|s1sܔ"+2ŹW8TMS(!B-˲L*>!@ᘮ4p%̞ {4C̖v]N{D~ a(*$W0EK$ޭtX^Zcg3/$*t檖FҐ\-|5 ^}cy|˕"3_]i^PTc;Ȳ<ŹSbaA<po(Ve|WIF&BXF J޽{[bGo8L#$K.q%~~jB)fRx@ij"u4M.4b8\ȏ +C #}ީ2>"vm{!ѹFwJ.7}hҽ{ŧB`q.Ag !mooɓ!$xxBHַK #tͣ`$ r'g! lu_q{!=9h/ D j I cCH!nN6|4N>&4X&;tql 8Zi$I\uXa Ax~ QI͝~nؾzkxfj!w/ D rjsG !y~.vydIr)_R~Tܸ0!@%Vwiv̡L IDAT;g!X=B^`RG0|"iQ'b"E}tҟ|!#vvOO/83C&BH/~]x:w'8`Б8hcIQ'b]zTTۥjE.($K!8g UUeyP,ݛ[XÏx4t\O(dOVMJ&Qۜij#$(7#?w+GBʤ2RW~\Bgk-<@W5.,l03nig! ! Tܨx&r> Q\|9~@`"]U0t U|V胏3]$7oΑ;b}7ϿOɞSެ#2"a$t)lsY)Sv,IfƁrjB| ?<+$Y_~y_ _w\:y[ލHtL264sx)N0$doE bJΌ!wg 6ؓ&||\ۣSd E%ubIU9 !(7 >[]!r,g$(a ޾ l@V2 W.?{,G?mC^NM|HuB>/C!k1g'~(}@?ܫ =:U/Y{>Td$YBՔaH}_66|tlDBzNg$>|L5Y'Q8^l֙ϧI 9^zz]hąLEQo_;73XAK|wqV* |W`V\̊w}쎋HȲ+FBsMariY(3]Ð3˰g) !ImW?7]i<8@@U4u0 O!}#JG3e.-MOxo~xȳ|O +hzIAY/xvA xkH{!FFO.0oa!ci!uGTBf/ϻ:Nx^BB"*bHBƓL|V Z~5]׵`$ޞ\p {o}6\;iV]=:v; !SPtdQ [>fìy1 ղBIzL#6cAJǶyœ-dIb6=0 rʩ3ً zY-ӏvހTC[aP^) V7paY riv,҉nb,Opf^xkLJ3i~;>=D8@t|\GpZ$=t V]z fêyu=ћٴRոN,5 JzW"vBR,pu1" w37c\ RE{ I%!r/cXJe6AP^?Z꿿

SDyR'R)&5(M[kB5٨ح"TQG_)f3 $۱ZCzaRku!cDI!$Nsۯn=5@o}B\@<R] OwhY)SJ=k+[k4h!8obcoT0$~Uդ,,6+&ww@*WlJ) W)ZUmkI,[RT84yC'߰V[5b$[M i~sSC;clbfn8UN΅\ (7*WAC3g\sy_}SĒ.#ȜQYGefjs7VB|">`1x~)g}*\0ЪZgrq!'">Cos@2S׼3sSZJA y4A0RE$ZZZ;.IouIoJ.X-ƓFSjmAJpHiȗq1~C 214{&hpO"'GWSVt=)ulJ+|BOHqQt|P(:k$=ᒞE h!-@dn;r \ģǏ`l`[F\k[΍ҁS>ApaVvoV A2AYj'AfҲ*a8+`-WHqoDf 7)O(  }B D-ZI E\-T!lUaHb+2s}QR)&lW_2yIOe#<+|voZ׶ \q< ׶(BsJt;kl`+=cY Cd{l1qv- _ȓ]})-WWoD^AБ'*$ UGNR ,Kb[%,8 F4RNmշze9,ǢW!7O=ŷdt;?tƥ ECG2QtV.M~b[/Id$iJZ̆2EXHkԐ+f 2{k~ N8_>.GbPxbAx{WRk[+Q*CD9E N6D°تn;-rtz|TsՅx%L%OK ij'x$y*aݦߎ ~N tpBJ ;IcĂt~.d!@=ZM-k^fJ ~ KhE]<*^۟)q=kkAQ[.%spJk*ֶbIj)#jrhi(0xɸm5v,E ۢh\W[h-nUy޻t]<}vttV=c3=5cON3zl,ĕhtG siPz0l@V/hr9`!ۺKJxa/IĆ@'q27?|x{-\<[fpn"2Ԝ'yVpb;&:Z@ %OuV.G ~WB%m7sI%<\c|~Á: >Vȶm jqU?^M"=2cHZsb.v_q [rB0OR橧 m/Xi&7oc]<5d{#Zz`dhW+Мxpn"xac,\;6BKZCB P_5NƱyEUf6R6m'R.h4RmjLrJ)_%C%9اSF/L0w{O|LT CMnAr%6LMv/" ;{oo~[{C9!(Kq}?_zp$=N:ef>~vX!󾃹 e l4~egU \Nk}RW)4c1z M!.#ݛذ/ЩLY:cIBmPLKB[7o>x A>n4A|ed$ΉTr^0ml$6R;XEh@Z81ѐBQG mt`%^18e,Ya@)a#)xMz¥k4`71in |^T ||p9XRHײ.b WbgzJL;|yѫ7 @RC6I S%5B(wQ;; %NPT+TP3Ғ Oƣ[_ꟈ>;s??0_"H-RcRd땘3{ў}faS9`~K|֏KoMa 8joҭA6+QU%.14Ft-,v"yE@%lٝ&>h.u8K!>^yVT*!NjmSDt,RZG,NbfB||ϖpD22 y"rtz]yƷò#Q:Qw38|3juJY1kWb7aڸJ92wܧZ\hc5 r;捛~,C"KFu!Fh_ql,^q9T6TfKؤ1 ݤ10qn++EG9}ĉcfk:j0{sn:)c0~q^^7 w}|AtgBBZ5!!%%(&N"YEA)/wJj+%{~矷[|@2y6C=='r~3x'$͖- 2<^oBZjQ3(8 m RDZH4&qhh񂨽QiE5Zi)6; %q }0Aj aH*8kؖĶ5N)fObDid=83jij1r/kʹBHq修Jdqt0-/>gmz^`XIT\O،qc*S9lջPQxMD^ɸr Pxd 6kA.s˧}_(NVf=RoV71BLNqƃX*JA 04q$LH:4,lWƣB]燔*ծ",I_"V\p[h Tɹ5lAZ{Wx R8ouTίjYN$hQ%bēQmTPq|6AƯj4~E}Nu֚2P5g[FW? w '#+_tDŢ.CraLH4t '%UDxB[D 'ao K0:Ǖ/;X L. }-L:A `w#0PLMXBfNSi[Y=i8!hA_˷:]-HH)yeB/<%i_%lI$L%hS@QV<`D?<5=OI>5Wҷ!,.R;,[EN>y `4ٿi ŢLyh$:fݳoS`#p/~G>>f33O\NA-}vX+e*ᕢ(dW0/yX%⪁O KT9|Yhm:P3;Snk#]m-[o";q  9>W{?'JITj? }Syr'J~vЫ!հtBʂ r%O}Q' ̥(Mm;?cZligio'=E4n ɖd²|#3W鬝sEtǶs|m>a#!萟cnm T$LJbD'97* Ñ5mW#+gi .~NX<]6Mo[:F"E8ռx4vsX;|_1;SRX/KẇvsѮ~!Rȗoe*)SkLb֒(XumA5$7Y"?U"m`5dcĚQB(*m7,W3䟴B)5nb `bYY)VuZk9lnVEay{>E= F6>s-LmGQ@"aL9$SvúJ4ldB5+R XB,$B A4KX=-%$8.9%B0[4qTzO9)5j+Ji9luvȲ6^µ XIDAT#pb`o77S'Z DvaR鸒d!kW&KxE0Ԅ^ضh nXRQnŲS ^ߪ_'j]$U &(/ Ljr Ur 5a @"xxKMn0ll2J+~|n呃b5@ś|$6ɤC"i7r@~L~DT\ڒ-B@Ž!E4MQE4 +[%"+v`0•OS)w pŇo;ݝ #'{_ڭu>ڊDA'mu-qX&|<+@ĬeQ'n[Odqѯʫ1, qʉ]#}(JLk/..>RH^?o~[M*pFcp|۾pVQS^h6o;x&NJ/=VPfx`K DzRqv<}GyOm|Ez[mX:bH!. XV?aQB쓛,Q.!!m',l.v-_9Jb$2X j|_{!Ȏ&:D@UX9E&+nwm`8!8M|oRA;oBZDEa'l\B+M1S&wDBY>ic.%9tzWCWnIL૆n?]`+ H2|76N3FN3U!ory@+Pi-aRYD\-xTr^9xڑM9m/)j F hj=}CwJmp+oM-nz1Bp1=;ŝy;G @N$ ,), wRj%X/Wz>V(t Xt#AV4VA#[Fſ{_61Y3 x_q/o{olh^~Tl[`;(,DYlYO TEP@{/u폨Y"eQMܸx/7knv #g 3suu;LZa@ "o,#!CdѠuϵ&4A0dU# @=W^zWrs_B_jÙBx~6z", g"R?ퟋo*^W^i ,F2syu}QZU~Bծk'>X\<97rӍW]kR?s#JÏ>đ'QQ-QO'Z,~-^ہ D9{ (_(/jwK<W_~ UuݽoK`u{gjfGqC>zǏC Mҟ{v^g.*a2W0B`XH #LM3e63lf澪WtؽBv＀{v^w `qΤ~`0 Cc`0z#!0 `08F 1B`0 =`q Cc`0z#!0 `08F 1B`0 =`q Cc`0z#!0 `08F 1B`0 =`q Cc`0z#!0 `08F 1B`0 =`q Cc`0z#,Q8IENDB`sympy-0.7.4.1/doc/logo/SymPy-500x500-Logo-No-Transparency.png0000644000175000017500000052727212253362407023415 0ustar georgeskgeorgeskPNG  IHDRߊsBIT|d pHYs ` `% tEXtSoftwareMacromedia Fireworks 8hx 9prVWxZkv8=*EiBF-jSx+1 ^tJm!3'g( ,~$_=e{orwnrpïnnܾaïnno7͆mn7kfqF܌6|qh$;#Fζ?DFיJ2mu$3q$8t7wO[褧:\y׶e;/2-%>Db hn<=f~\Ezb|j#xZΩmں纥@%pyCGE8?IlV$&]jw뺅,rQь;{6>̮x;\s]M#_L!$@6] t59(s^Yt_yd=ZK9cH=?Ǔ/SQTED4 19r|OS&ڦvUZhe(c-$hW 2d*Рo?$!ln*3 &j*Be&it<9곩 ,( U L͔PȦl3UP2$ʷeKxT'9MɄ)R{jGojo>U`fMF`s~-*/rev9W)_/W9 .:O&y('z61JY[}̎A.@?|M HYHx]?9MA @?Ɏu1HS`r4M  :@7XqҌ|QoLha9>U 閪i23OB jj} ?pڀ1!f2&u4M=Gi[+ ; ¤9B(Tasj k<;|N !OhQcΊYiGxџ>V$~Vo@%u1(u\ bLl p^W8Ua e ><GWM/tFC4>?0+$824CE?VHEiƋ <侉 &gby2lA.h&i|(E ćU_Bb۠@JO  邰xHߪ&6QrE4Ka]kB DfˆzƳYQl]CքOe`{A;Ut1%?̒3Ewk>EHie-쫌[3E4 ƹ c%|)]a/ ̀lA}$Epꔺ!ڏt ' QKh,M$$s1B1c/ ( {xAqvLZq:t@qhC0lA._|+3,G@ |K++#L@a%dbğ!* óbkPҮBeb9N"HA42P%佁>\'m= fˁiU>kW+,4’QsdHKB)T$Qt}0i, @eNweҍ0X0^3;xŮ7NDc?h A ",m2穝QdBC .aÜDƶ%*0 ?+#Qy"~vy?Q . l?^mW"Y"X&ºY[V`5Ė:E]:HIy9&@5r'&3K/. ;߼ V s9YOWӝt~d;vRD >[d҈ 18vLX\ʸC ^`( lP vs>^TxҜ_F-_Z9Tq~pѢq -G _U9f=__4lH_J]?= >?Ρ_7Wp@#/(.)n i^o*4ׄ唠EVeB%ꆗHzK\Ni ji׸ʠBCpXR`7 c}J;[@tWOҋżJD=}|,.gRd ҿ= FBPH؝Z_/.{G_Z0\uQMh8ؕڧv8H5;n}\=x#+/- ?d:PNrY:?А7pu}E!Kr΃`0z毃<p pk`D%,8D[,rr+9qoL: |!ԟ5V+J|/&bp1WvYʴ~(h%߅8BuKaGy>3վ8.ʴT6z濰;S'NOB>ߢн(yNCHWcNZ`-SLyG/|fh ?y=vA6Ů}u?v܎qn?vn|HmkBF)3 rmkTSx]YsH۶|wo>"66= <(CRm_8mM]=^ffUX*W2+^O ~E{vnAMI}tx^A>Q=Wl pL?m &tl]}`Z`t#`… MݪCӓ@`iuh,[w]he{FBC%?TH9&FYLS"}͚oί\YW`X>˕塶ʒ% =C1r>edp&ѽY°"W8FS||`"k$Q2NSNQB*g>HMHᘷLtj!/%Ϊ ^UOq(K'rM|n|H Fhk~RTh/Y%"xpj jڜ iOp]ƒh-: ΰ1cf`" q{oRa(ܘHq -֓~(_Q.r{YC-0E֑Y'Sc2id^Krc)5r~Av@K/?j#W4O|OԖغY$H`MyO( Z"E>wƇ,^@KȄ3E'_XY^o¥izR6?itm68 =i gEEE|> T=w'Ƌ{EYk;ZY˂s1YK'3 `+_:zؖ(hX,7T2l3`-m:Q>-tsui #ZqS -tJbv' ioҙU/p Q\ r,E 2;7ЇX̠f?0¿rIƜ@k'}V%]_y>؅2zHl g bI;4g2͙h܈9<4sD(},42=/!(q잓=uIg][#jbDOΆLwew~Rt1',Qik5F1_R\Uh1'(DkZ&*}UBF'\YeG=b1eLnEt.99U~I;43Ź=ç}GxOSQʋӧO_1iD==}3s~!G&M9ţ{ݍnjew@aypbb0muZm->!xLTl_L[Ӗjsmuir[qDl=)ݺS==LGhWj=Ţtb: Vg0kZ0tIŴ1mEj+٥"8=8.qer;yN/ N'kz٥šMcD/..pUW߮?%oZ~"E;;a? 8*̪] {]QCz -9a+uFaUU[zU%-!whŶ*_N;᫺UNxf-KtWP?D޺tI1A.eU5 젪u.kםک9U+HֺwMn;8]Z g3 ]52dh*TȌMUۋUBuZGRFݿ+xƍEqu0.rϴnR9O?$]ճ܊`)5\@w3.(:7Y&^Ewjܔls)ቝ*S$z׆zKS3"# )]A(G {llѳ;glaݫQI}$$k:cS{I3f{%%2YFq߻3+<"iV2c߅S11k=2' / ~b`y΁<3?(_neImGP8=!Ϝh3ӧ$#Ykg+{uT8wDf~H.18:;OV8=ijRŊ~rWxJΓeD69!f~ %o 铩FsR urI'." EO3p?O+=~(C{mEUѕ3?ᶐ~:΢#;[=-I TsߡO wQ9;pUȰE-4E\Hoa}.3Ug1U%L_V?jYi33wG[oqh ߗyD™#zF/$[,;ʂ7L<y@*. ~1M0˿+g}9r'~Wb:)zv“w-;ܲs-;Ku0e+2t-C 2,C+`0{C-C 2t %} wV9Zne薡ڸEa6ӚxFrv-gr4gY5k9薣[n9Zst ʻiZvnٹe疝׋\y7We疝[vng盯f\y2t-C 3WgXy2t-C 3WgXy2t-C 3WU3zk9얳[9+9nW޵rt-G}w=FvnM+cgѣ\u~B"څ9{DzNcSӫYo6nBɞQȘzck{ƭm*㷹Z[yoygt ^6)ax}elx*,.6]8 E뙋+θ CN~WObrtkJmp6y7>dFqsO5~V-u#8HL,Ӂ|c'r7aO1TL>dvGprӽط+@6E|ɖq >KuQo`21.x&_-5g$]->KY-EJXW_nzS_-^0O_w-l+.G1 ({3)օ52lVy뺧x su&F> !_ 5;\f^eRٷge=]] $nS\rvv~BDkM0}⚢0ώtWysFRVd2D> ` hۧ2)Խ"k\nHlPýgQxKH}}~JcM1&>#n"UyV#[y#Ʊ]Di]]7{uqE?1Ƨf#{h4 cv}]l\z9[;ů2um-,^g5@՜J9˜LZ!W؟+b;̟7:+{R*gK*)(*eg#wZC>}Eܜ@'06eO=a0VIz62mddm5b<+y4?:ѱqMg匪 Դ;_ONraM2ݦ[ۊf+yS{]u ;Us}\R{~q)M>:_"ߏuuZ~z5К4;,wY1G+'XU\:t1LUB9[ =k/\L+Fŵ?Wv+]<٦8b1u~?e٣E>qNyGW\K{- hX֟lQ!폙#VBháxcCmɘ-L:5h.bR|Q,;3'5k C-8pWPׇ6Lw} ~y2M|V643ϡg쎛Z3kVS7WھL:h;{KʽYVS섞 {6um]EVߋ,~%*: kef -ϼU{ߺrﯖhaLuR6RhBs]\ŭ(uPȪYe=3[YH~ž#EysgN6?uy%`tt$<`tM5#R}SFmo|/{#SK>nm<؜Su6jbhQ }<,J9vu@I8vv G=\ހc{=2jކٌFބnScnն_{| .wCn8x_-I۴8dX6lf/ֺ\;Y!zfNOyXvǶaumfr<'o#<@/с vm;~/o[[&ĄI[W>=rY-֕"ʆcQ l 215'G6CWtFpA:>2uY͸xxDeOQ(_!&wS'8=0L=aVBܐ)0:yupٴЁg_76zk7cf=l%B<&"b?C.4. OVHj:aF"th-YZ#j:\DǕ f^W*5pBYF4$&SeEMDDщߔa5 XYv# }YF"0 (4Upp:P*'Hے OwjkZLMV>\KYDedbezaVȨtʰpXprz-eWZ'qHFd!rl7fa"8il7F2tP6j?bY0f2 WUHnd%by'/-&frNsݔ DF[%dX|eڍdx0h2lpnvɰ5KMuxn:d^ Jwe6"E=xn~Hnn/DFli^ =H,Vce6#S> TeeU(HV.WV}x&80di$q ჆)-LG%-}DÖHF"/1YF2Ms[9rE0n˜ MjdFP;]Ov&XK0B;læpFogHnG4q<{τdrEmkBSx]N0Æ WM- 2*5X|DZ=fLc(F? hҶ BLrGpodgd/e>f 9]WA\\Lf[xilϣTs1H QlHاEt(SAZ_Y XmmkBT~rxݡJa]q cb X ,C U󄧿+|u8;U=ߛ[=| Io@B_Oo@GGGzLo c@iz,џ/ ?NПP$ _>Yz??ǷG @њ[M}VA:Y@mkBT~xݱ `Qhhj $Z  W!1M~K7mQZiצmviz?2 ? ޛGGG7?^& ϏGGGGGAz????74k >QuimkBT~~xݡJQa/EAt-baip? 6AP֌+bv* N:vk5jjmצmm71Jo@'" _.7?Jo ,`G^_N?7?????Omצmצmצm֎n vПPvz? k[fz^z???ท@Jӿ6?hmkBT~x-JQab:&ʸ'jE,ÀlP q#p^pJUSߚߛp@zހ?|757x-fAk__[[' '\7 GGG?_}ހs7??????? OO(M&<#YXrmkBT~x!J`Q"*X(V4-0QA ܀b ds wz= _}tscA5Mncr@iz? Ouz?O _]7^7??>Oo@GGGGܸ*7MnwӿQzG'˭//6& C_M/c`l<4wmkBT~ܚx-NCQ% !iB,yiRQנ00$! 1(\#`&QnqX~';vڍG5jjmצm>`G џޥ7W]z?sׯG//џۓ' k~@b3Ko _7????3`mצ?.G \mkBT~bxݡmBaaF!) 5 !L +t&t::Lqyuǜ0jQSӿ4kӿ6kӿӧ7?ۤ7yY7Ko@GGJӿ6kkez????3P<`kz?????w wiz???? Qbp7mkBTx-JQa?h"lNqcm\Ab\;9{{?pi55Kӿ6kӿv:Io yހO8џ@GGGz;Io *`G/_?_7?7O lS_Az?zJo@GAz+ kn5`Zzvz???=;Ho ?_TqwZY)>mkBTx흇WTY9ٙA2%$79HPD%*ID 9a<;{6ͻ{55 9sfsoݪUu@m[ֶmOӆ Çɓl.^OrFm~Zmbb̥:QvlmX~,֒8Ǐ@iqoϻW‚Z}}=2y!02gkmMԮ_˼X+Φ8gsңpyo%av'x1+!2G5l |r~ 7mKX_p .1PU6sJMk333ZkTvm+:,#kbvI\m y3PqӃTGn.im:.&kbof낤9B aǡ0<_7 l70^[ZZRknn`^D1Z LKp szJ3GSjq}pdN 0Gn t9=(X2~?ALvkw&ч9 VJ37t nkp;40G~Itv*.)Gg?,|k===Db⵺B1p2,<*YjBcHNp74+/c`iT^.3No0^{@V"U00syU"=Y( +q0204NL;fl m7J?^|6:: Z>V#jxM^ϟ,smuvй&H.B\} ^2g4OSyLC d [A9$ 6˻k_4TWW-^;Nk=`OtjFY}wѾ\%==7qoDi0*ÝѹQkȼh pFB?={VNU8^i? 5>Pq/o}4sT;m~ΎՈ&V~Ѹ)VwJduBnR$dw^W7^c2pV?LkMMMk 񚐦&]k.KZCu7~G~Y;F~j[AOuhZ9Q&Pj NҢpnuk-%plHxT=xS)c3B^ޯ_D)dB_s9>ӋOhmIݻPR$7^o^A|=o";d_ű#Du NI3GavZoVVrz;xxMYǜ真z֞G=$LЧ_G!]OsyEK+SClm~Iڵkk׎azn.ïDk|Yx~zF)1߾dY{8 xs|?x!6w87SOÞga߫-//=^k(KkBzrOCC'4ps2\,,Az2=c3^yQ3}P~') z?sZI>9uww;^'`0]^AG=a bu76DZg:QvT]q+w/tdO@eGŔ`|:J3F` %;%Quɧc#J'_'woǏ5VϢ@6=Z=^åh+$Oqjp?3ràⲋL^s)r: {iC#H=fKuA#j w%2L622xmT9s/`=u*wbcz:N&{t2g6Or`6Gu`OĚ?ko޼Ygv{+ 5-seW w.W]=~x=߿KS!k,2=eN:Aj|d1x(6-H+#z^Uq_^˛2^#^:R~ e_b9xv޾v}ꠅ̞#ԕy\mU2J〺RZccl"s:ZUf9seq=ISNc~v'^Zk6pq?x6꽠=;A[x֭[.*T-9"4(9\oQsX MFV/Ne܃Aˍ㵻wuuu9^k-+SM*ޅ.πs'$\`!{&q2;B@B`(ܛivHt*F_~rgyB}w<0@c|zoQY4sq6SǚkE쯼Jr^;YO9:>^;;+բ!..233+w㵱Zxv_WO.Sp9K)b}ɭ뭼q>}W?G`{E{vX5*f_y:(^{a?**J8EGe;~ rtV=mm}e[kCBdc|u  4ʟm'af"q L {W"!-535ŹQbpR%K|y2l 5%sZ󉟍3ޜ﮾j>\[Wk0Qhځz?ko"\f.4sp6pdC?. Pƻ0/9:0־ԟBDgt<vRk.z}}vn~ \`>~aK!'hp|g(^#Ȓ? g xOa= "ڍÝ"#%#`HO޵̘;_O|O qY/v-,H_ Gv1p4{Ww {T~޼w%dvdIz~6˝?++#8kQ$R"!'+ k13B1YȤ^ +5%Ѿ#yv'|䈷>jrc/nҔ%}.qoCZa'Ԟc|g (W+NA dAQN8dPAx(y-?'.uX='~;n燊J9Us9FeKRXcWvW=s_OX"}bC^?j-^V  )^Q_^۠9<φi*X <ۙ ԥ5l>m\v}=~C"};DP bGa e➗%$^l}{38cTvN.5Lo 1$Doi8ZtzAcL=Q5}8/ggAia,6ҟZȆqΉxssbO%t~l}E+se1{c}nk8H Ȝs5Yj=ǻҺu0WDBgK̝Z9N,[T9@B3t[cO<#t_G?AlVP m/(yu\ ka;+U|$|FNω_}1J}~XD5[1AM87s CPY߼aU9dW8G^Pyz|Hx3?{2+X)v{} lzk)s9'r 1xN;'W[Q8F̔/;`!X>2J]D|| CI{_?K 1D1L[8Ixa~-ȳZNL]Mκ[{KjĪ(/>!>ns"X5@ =_!hTְfo,tF5@k5)_OۑsZR@?X}>nl=ωzD/iJIk)^=LO1uтdr:Ggl6s" ;'ek{pi1a2!QYZ%')WH=[ۓ=]Yq>)t^B9N8pgiCOg~s)=k|z\gB|!>MT}_{oelɟN?s5?IW8CqgO ]pX.p7HK?D s;멅z+=˙_rU˺!6&3a5e+fP'}KU$y;@x~Hw+nWwƇ;k '_y >b =F6-֣-w4|v;i/tMG7_}?5LVw 8'`VZˉzB~]{(#EFS6ޏfD~>Δb WkpnEK ,V7/r'`siE4TϹ=8}~™JX+Z+t(z2&[lH W1[Msn:TGbğ{~G?U{ ^m7L%>Ss0{~~r7HX> 3]=X{j=}?gY=/|dv8SS,{MnzZs=sb /TWWZj쟻1Yow ȶ]Ocf]нΜā>^zֻC@36Z5$Չ{l=-˟ٿh/ COMRC+vwVp>=1u^8%Nt[Hm iNmr7HMT?^o){FZS gs9Ľo'b }ϔoIOi CoT`Ϧٯg ,-rv'H`g\!~46å7S9m-EOV4܎hh)oB g٪hiʮFz`l fDq< zWmo?+ya Q3s{PvvZe b&E\ M ԘzBrSY!O(lay6KߌN}p$ 1V_B< Eg~s ,~{_w@T{ nLBCU<#TxoMdӥ#r~"tQ%ptݯ7P}+F4?@CQ<='5wzroܟQ~3"N5f9;/9I{糳q^;g}H:>>'Seg3}f}a G~k-v/O-A"w9/Sã\s?'^YgĖ8zNlZJ}`)FMsN^3IE$wT7zz~ f̌o!Ftg؋`K=N *ezxۢO $x{Q_Ž9|l_z̝>{Z>es@XuzT"z5,?w1QYlu>3]qpaO{{Ǘ{Il1=XV]_PD50e#_/Fċ"ovTe f)Dϯk{~D+4˟-7P q!&q?{7I\gR ~؀n9 Dǖ#t_NrOݠK}~$g}-fαz?,7Sȿ(y|6KP~:p;_Š};Y7Q\+f(7 WU6#T_ q-J 2{aHuj/bzչ/8#h=6ԥl@\aV<2z4}l=HX3|@vl?Iyڼ5rAYIp{R`4 n!>fVb޸oe ?}J8DYs$i c{$.&gnzHz`jc*V1~R{lade޻ߘ*J\J evxg7D),Fg@#<gTw/y߻ 2!*ܬw>Ӡ=4}ЧoVH~,AgֆpyΕ{K^#Ms?;7Mg ʌ5 V|3֓b3MpS< >bV珺җ3b>\ͽ ~&4!?0u`/(Yjm0_o''^8Vh :KstL9li»ߵhOw?7n/ܑ"?vyN|k£P ַJBM`;»n&;i#`z@wW}ΪY*zuE >W]Fv@MJ }q{}k@OΌ.1gDD гO )5q^|}[A{!!"?FY\ˌk@dσ `4̚9DR^gF^4k|;udH}_ 3k Z]"mRŒ! )Y7?H}Z9)V4WW%fF?<-&{bׁ+t IJSn4C2"j'iJ+ |_y^qP1eÀ]َ-.ls_j0_Ns_VbCXo>0fl gznCőD8{tD<ecRl,Lk6P}Rr\v7손 cοD5`W"|gbs.~oGH!ᛁ?[xsl]̨s"*y?TTE?x*kp&c"v5P,gQ/!tأpgȘ-$dzǂ;ݛɌ_Sp43,1Mw\ցYKLͅ\{aM(\ Ԉ]S}%2sCS ="xf`/g)cbzmf:xJgZEQ֪ 8FـQro[XǬ/_'(?3Q-ʡ9b8[ҵ7u/ |]0 7l6J!MuXoK\E؀rL·'GՌECPuJ*Mk p#k@jT"4cE?l nW;Rҕy`)o/v^i_nfa8V=}2kgJ,fe eR>5/)WIgT= ]Nك ?$µkQT0BlQJԇ򷱔uvgJu' grƬ9OMq3'uYx.0s&L91_jlmFl+?Yo#7M_9t(Ǹ пG_gE3+CyτSɴ=C!+%tA@.wO_QsIgIπ6kmez0)_/(0G|4YA8/׎*NRNF.Da+&R?s+]tb  3嘸{ ) 22H^$jҒ1kZS~R`X33/Z9%UK[lstM8kB7;lw(IAdckY-bf#{JV-]0v77mײuP|џ({d?=/?I&j53n1o*ZT>ew#,WK 8Il_cs+E׼NN@фmO5?n+X^ᚐ?;63d{(:@3.93"vM)#~F|8z sP>]`ώ$Q~,ң;t ^:` c.^tV!g |Pw&F./4Xw. 2? ѽƐo Qt\W-("GZ5 ؏5HU&r_GV'mkBT!x}Uw鵶e13%uleY`ٲZ3@&&$9׹;\dfKVVUNY[z7 ObL I{ocj?+y0>MNaMFx+nI[|B詏`uG>$IDy -<"jPZQLg پp;vV?:|<*9Řf0CXh{ u_Zl8 oip&ɂ[e,"8\IS.,.$9z^ WaVp5Y’\L8hS|rQ_v ˌ<̘)JM44|nqYȟ஽l]D\jSx*?3yH)daRlSZȆ@1k4ՎF nW:<|_ŰLzaP5[Pˢ͝@h: 0M"݆̆h}7- A6 !1^L-_|zH:áy/j | 7qlu>_E2`MY|'}>etf/3x<i:@^( M4c|c}&3cC%zx2Xu{"}? vVT2rp6`=ړw^GAfD:sz;AӵQ{)7w18;Miq(2B2c TǑ=*$?ꀹ2oH\s#E}-Ǽ-A&*rǣtb4YcR\1c #3KǣFǓ*D>WGA;\Y#m+UGAvT܌}=/lʠaN{+-Y11 wNx1U=<72dq_ aFّ~*T7)\,ФSe~"|9/wq;WXtvCv!<6s ?fNce/ =d<&5p+ʀ{Uɶ*Y?{}xmЇ`J.X*{+po֘,hC΀̿a+ L$9 %9R+Wh`eۄ-z"}9.V?y|b="R:Q&Va` 6kaL /c^PNr25 n#;g|RZ-":/g`D>1'\CO{Ko7gD߱/xhq\& lEb,cH:= tug ,d`E #ߒ 3:9KP Uk&/F"2g:1n$֪sx?(ڌEy0$t}[{ϛ5-Yۑw'-@̴ P8Spc& ,"дږ wïbυnSb]eF_L"3һ r9Lj&uuBcG3`q?ڏb3<$#2%2 FX*32Q9ui^rg5|z[~Η+N tn଑eeX-11 #7&b1D'#Ax:Oe$_HFh ^@@I't&*Ő s }+!+d=I6ZHgDd&T?́P('?!A/"b<%#sx}'?삊@+wdWWkQǫoKsM^ڀ2qb[0u7أ1|<ߟmP(k#>6A6eZ(3,NT[-cm| =v}#Rƣ>m,b/ecwO!^ڶBcL9k\"66b= Xsc?OVV)gB?؃I&p̜! &9A5;`m[\emm0OCM2*y31`;K`{ǝ\ uO>ag>)o)x ՎWQ0M?fk+裮R1y$xzQü oI+eyv"Ф3$Ϙ2 >T̹pS VQC֌X3S.|Ȗ7c끈q`B/lLAoȚr<x. hUw܋yçz0ƻ<֢leh2=r&Yo<#FOG=g 4v9U%/~ZW0X50CxrB1Eb ,?kÞߐ!欢a/\R0G#"ﺃouh }W͖/ưb;l ?uT9k+'_ Zk h1ل+6/Ę5Yp>|FGy?gF}+_ ?_nFx#Up~Z)aMoh-.#o(X `g4|l9r:7_tߒ #{e0{W~8gh~!`|ZV1 {X*Z#Y\{Aάo/csNek>Bs5OydX} _@/&ՙ=1>DKͫWac۴&|"|]ivetuC'OB/`]82LjV#eP<}CVT0JbX[d-ěfl tB0Y>\*{jt.B ;Xtx1CB>{\IaPO=r57(z&N?5 /~ |_ae!br1 kr!XֶX 6kde~ol;BX?Bc={ s?RALFGtMelGCZ>/8KcRӮlZ\j l2^D9h8׌ij'YnΓ,9=b\#` Zf 8F? 3?m))I}H KHVu.~| _ox+uW\˗/AAvCqJ#(nTh4CW.;ۙ =ݞ]\e)>^+?+2SG0{]dw 9(3|j{ax >XZef!h`rO Oth4aaa:V2GmT.r!d)< \Cgc r NV;R:'5 >G!0ESX-= 𰧀a' ?Q'Y)@qi'8Cl>Z 9 "HKTXmpw @?h[埫_j A֓/4]f\D@(Յ) Wݲi9ĆA%T|gde ׺v@K%%7[I&2-Gtߌ}#^_^R1D0N\}|>:`D@p̭//CNkr#I'Xod@ut0e1;_GnV wVZߤB6~XwBb#G"dfJ#L#0?#92?l=k } Y@- m9cG UJ_~ Cmy/γ 3P#qe -a>("1A}!z@({ eQ6l*}v{l<7s%#Og 3sFTĀH_as_;5R\Й?L?ng p?Oy5sZx~(ΈP(žz7 |Ú?μ2RٿV[VdX&`ߥbbOEq뿕RS]nc }3&'3@iƞs P|fs\h{7T͐<3/f=Q,m;~O\-񏻫h?39T' ^ :-g9YQx9hpGqYgh^xQ?m=Y=ڒg?㾸o=91B8ڦ_!OyKC@N竛?KgHfUy9S?)k9#l}cbu`?-&?AT4Zj;.3<%3{V+m@F}4|sNl˜M;w;?A?)įi{,e村{,ɳyq|W%6kV(Lw9)V5 C?}\/(R^/-5S;9䔅I@)WͲIsB&BaN8;4?^ Zg'ǒv O 8YRk@l>c܏8#U|op9?inn"P2;on4 ]<,0hg?YNOjlӝoI ]6nP{Ahflǒ| pd}]B/<ؘ†b ^ [hg4cCk*?nn>2!q8wMc##>e瀑? ;N]cd.5Ϥs~-_8n=VE륚i;#zE8PQ'~H cq `! uPϿ-F c}DaUxL 0s]ƽgc~Ol.IM}uJ* ܫe p?B#-B"u? |[#9_'c~/@Q$zZV ¸@B>Ԭ>О}X#p/xoklD\tnH#5׀.J ?`>t>x[p:ƁʿÙ]?5eTX,25`n^ ~Yǯ^ lݭ`k=wJA.@4'r6-{I_Tnv!+hܺ=s[B.Ac' -ş_z#?q@iAϷLc?r@n>~'H;9?aId|#Ooku?惍ƽ"y,?#g'Jo ֽP)s.-f>6Un)]|~68 ”!~?s^o̳d1XǽyRSD=9b8Z]l=-7kP^L<t>l@kC 02jan~s/Hr3}h轭#G$ݟjUqx`+^`?9F\mj+(?{\b4Yo=xP@?뉷UǾmj;ٽ\?Rߎ5`Q ԔD?}26Ca R;Lo!yJ3]=6rs?+\åK .ϛ2lO10#ݑb|=1C-5Y#>6$#b]0??>W>&*>:ノ6|$$ I<$O /ΕJ/ 8Ӝ=Cz?DS :Prxg>\׿o~3Z:$(p/7P%kcO8Buk1ƿ,w>0b= y1 =1WWcc=cJH(Qs5߆؏'r @[oAٻ=C䬹%|!}%u~wDݳ[$]G`?!b9֙F|qOc]Ze7jN_ UܳL><.Osu^ 9%}~<ӱـ'>磄;_WG&roΜT ` ~an; u0sahz?!:>|3t_S·$a_X>WKo}?7}GΦ'i?e< 2!RǢ!i"ӡGD=cuP;_??j+m&{mkBTACx=JAQ*6cF!ڙJ7l,l,\;w=/<006+5jjmצmmw@sހOgހOmz??Q߫N7?{;Jo@'<`Iz txq@oaצmצmpq@a+]/` ӿֻHo oހOG6kJի--mkBTE8xk0@P HJX HJHX H$Af/\lϏȽ#н^p.yB^Vm7 W]8<浫/ӼSiu9q<ޢqkV9W—R?= ]aKxoܹo 1r%C5&S<}Z;֐mT[rZ}*ȳ%~ddr-ss,ֵν7"[C%,oTbۭko}}{Dov{;G~KSܖxV>ܿ)\kY{9y*Vǿ\`*D^֨sbN/[ש#S3MBȧ7_m?iȥijo=ӖxG uv/hnka[E=2V5߇ym?M#2j~\⿮9@Q࿝=w_{~!^ ZV|'4ޔ+E{x؁99pꖿߖLf!^9o5{yۙy?PP<"޳_V-mlߺ'5<ʲctqoޱ@[\VB1Kƻb\Gg#ſ'S!S$^x3/ݬk{u>P#{s=v8v w}:hDz5Ўs¾ %0vKmkBTK#x[u0E !  !! !a BdUg֍GI8[mmn[雦9NᛛW;w.w>hoY{uD[xKKMN߂vp$ow;5wbCÜq/4}98ǎ6G.*XEGXig δ*kɷ RUh%&ro-*ߘTߣoҿ9??N?5WU9`ҿ}ҿf' >/Y:~)ϵySno L491kCrk-[_5|5?uB&a~xm}Fǣ퓟gٯ]Kuu&Fq{\_ȵ9?7zv?GH< 9;}`_o?޵8֙˺'pӽ@(9~,vi'}?=l#m6u|~¿c?ߺo @9<"9r0e 6S1,OZ!\S6+m@kզm W#y19#ߗ3Wm.Ld?YS^m??-V|=H.yzuFr镹\V rюNn4kC:8C d#gӊ칎F[+>밁|7jDj旣d41_ϰ #'/o%ھ6dh׆a9=6ӽ~'6D>hKb"#;s[㱕 HLs%c),,Gh|8k<`?doV_k<`_k<`{r='{wrA^Yùy/k^_sk>|9W/9;~Jlo߾)*/N\ϱov[iZ_ձaJΝ/:6O- 92b?Tlk%?_21B sY5>:>c=1Ow y^- ڶ,XzusM#גU]>H_yYv!ۉ_mi Rus]Xm_g)YY)m]y,m z1aaaxEߓGקo/Y\k6xjgH|yu.\aæM&wk#ϐ$?]Mo\Ⱦ,/ڥQ@~6s?)}, l gX #vQg Bٙ^uのuhm?}{].~}v_J;xogJY]޳@.)oqC?}>@Xߘ'-(W? źvƔOʙRv[K?[A}?-wmՑ}g\=c}M ggg DŽ-B^k_g?F? v0||؎=ǧHPgs/hؑI t~{n^}ZyD5XWvO)"c0vY Z|~_%/,p\ɹyΰZ/;/xs_9?Pܯ5ݻ\[y|č8gʱL{? 0 0 _k3>z_\S |<)b|7aaaxn.ta?l^Cvkؽ#~e)3<3^kdlc&jK+o"e<.ʞ`^(3zu l+6v<ï k7]/lc[`On}򚄫 G뎱zt^v2)?;Wmr5ocIz?Ozx{&!ez."ѯ 1Gg{+ҏlw<=}GݽFƨ^)zIpG K֜{{e G12ۭqiumf>.}~a? 0 0 [u+7Svq֭y΅ ?ނ}XwŶv?ߩDZۓ-q/?߳=<~#>Fk"qzrQo 9r,nY[;o:)@-`ק-7({߯S@µK9֠ɸ>:n3 _[_*mtcmC>qSL=<6;ǫsaaa{xˌ\ފpx?0׋#5zяc]x^l򼠕(f:~٣^lin59W~\;?vn6erUbS~v^U O7O(|;+SG4|?f*?rW~2oNٟS9~daևmH6mX[J~s.ym4ٶO|Bd/b5ɿyU? 0 0 0 0 0 0 0.P~*1@G\⟿KrKXs2(ߥ纎J8'>X@▼QQbqwx b)_K|v 1M6kee-2Ǜ59?K^E~9ϱQﱮYF8N?~;:=J<-tĒyNAgC \NXKs)'^Kg\~2}6}Գ)n]Or^j~"{p29w6/.z-v:+M{WJYZ굢`% Ҥl9ힶկ#OUz+U?;sd~vND7*.Y+v:ye;8}~|+ÑޅN9}{Bƞ#txխsXɿkSV/uJ=o G<ջL'L:D]6jfgLz/+ؽ[{rCMYq~[{yy czA;w9zszWHVax3 % mkBT]px}[TYS}/YL$Ifr9(A@ 9 "I2sFTLl}gw<ӳ~7`P@a={ww>U{a||5558{-<\?XpRQTT$  o߾~7Ǭ믿 7nhK;''!Kϟ޽{'<wsyvV28deXȖ\~'Ē!q8ODιtD#3-ٙ Buu099)/bS%@fF2T~",nN'={'E?+=Z>|?( NuiX !\=&_~?~Q/? / [?._{>'`R8 :S:0w>c]i^ҀvAX[YHp=M[{6`_0<"X K(Ÿ Aq^Bx,ĦG BJ~Q,T 5xpeɋG?YueOѽ D%9+zg 7z.8x 3#3iBeS>,_<%שbb_?246 A160sӅfX`s`/xD8Åasb=s'{2S} ?g4j~碢wSx{Mr^{̒Q}U~m8o>m8w l'v4n7*UpӄK^ւzJ gC.s͐4M'k6A0 0ى#;znBn 47cњ,u,v nBr^PV%\yQ~SXh݈^ۡ{ 2]D4D|>Rƌy9͑7i(iS7PrmP-*ء=2TsXеt rv3_q\DS0Bl>¿Tn,ly,cFt1a7G.Mshwos>u!N7A857w 'Ǎ:nK&(bk]Fe3@d>ڇvN.Bh<yWkhOΑ\0FdaxсUD{jDŽ.! C(/nݛwObz9#x5/dCH8G:CH6@:C0Ӂ# f+׿ BZ 4&_-{勑>|Tt,C;˼jM!pNxkR㥹Ҽi h~&ߴi%?n nc!>t/qBDzCkE{b`(BeSdOHQLyiF/Au,.5?&np!8.q,zӺ-g Oy)K)&Yx|G]' #堨2ɓ,=o1u`"rcd/au//Ƌ3o| ۠`9Ö f칏BxA3('& Fr9iE K_5#p/׆E.wP@<32 ӧyd!͝ܦ.7O۫?!l:4]f]gqb8+Nԩu%]{^[5r.y,|ז 92GO5HZLLe%kN1*{ XDРުq?i>`?CGV/C+:[ϓ]:D֜Bɇ_ $O1oZ[[6m? `S FkùF U{aW <5rHV%-ӄ}^8UkN_óqڮ?{Ym1a>Y\-ʍ-j\kv¡Y]{>:5FLkS~v߲ZLu.oDcTR=_^khPzV#'6FyFשǹ.ՀQ*' w6`O- {T/a'd]chR̀bbF'li!I~za)GxY߷Wޭa{pnhD5m ;Yov Y*N:VSHFmߋ79tpGXݯ IWrpیΛe3ܶ@w=νJzQ| d^ڼf׶Wj9TOU]O6 vGMPeވV  f SI0$6` %@~]|GUx;Liq˦1M汢pArR\ :O5h˘Uc}0se@ ?mqaۡM֛`>Nӱ6g<ƦclĕDLMt,yL ɸoUvM9i<ϽR ys5TQMJT3WPuCtN+ɔnM/3<=m0 jp- V)ܜ38o汾>OxX쁱`6.avp41<}Lg?KZ#8a!sDT3bGBl Xt ]C~F8h :7@h2ހ=@]/v܊#`L A݋L3H1Hk|pἭ q5az` m0d߿l}7BŊX5M~6Vj3#V),&Y}гIDh0 5o|}臓IH?@d#;-9 C~V8 #PXT%l Z՜Xz !Q^@l!!S!~bS8q/Lf1cS>_uLU`bu4s}S9p.FmE[rpi \{myy|O:pc$]!I Qp:ks-\Y}(IӒx^nIuЊ'/ml~Ͻ5Y词ĭ؎dG9A|:qgmK c0{ ϿnWx~~z nKY} Fpr)UG>@j(mcޥËGF^v,ϋrb|4רּ=gErk8ߖBt<1ޞbk<;k\gVʚy8G*6_ !w/c!:rI+Q/|y5TY>;l—T\eQe9@>=)yDP]Ή>w{xmUl *endj?wmR c1f4&"ʃk% _&s]1]rK?ƍz4!s?{c( Bc4-(LEPol6⟱ZO\-5wqi8-G/OX[YrNozW}\:*UrcͭՅSDz='ٛkjucK")!puq9>b./CǦ{^21H#xأ՝k|f`Fd,qǂ{Y|Kby+X*Oa~&w+e6zO{Yp% q2u}!mm䊒p 3Φ/]d^?'|ko_mOz.qJ׋3077-⢽Py =a kndE̍4&K|Et~V u/t7'\35PO輍!O /&\BWELށ^C>\lWH+uj]cQ\3 d-5\$򞝒wڂjjMꯊruV+tazt]_.4esSl_O@Sߛˏd3E!=O+6w,ggH=_T瑾ә=W)tz?y{:_5%W2>#JIy] IO.SWYB@{0H:#^i37cSXry %ǰz/ ]V zIUOBrU|NNߜoKP iSmf5yܾÛu%uݑ?wݢ]/u+V׃WOו^t]@~u=fm뺒ᴮQ.`-+_ot=~麒xJv]s_R}׸ˊϘ \B#uySf0s]$\(s|`1uzuuy L% =:4*u]:˘:ӛ5H VϰӟW+nHQYg?uO}T?h?/|g{d: { kkt=]q<=BYw939r}߈,x`J+JS." k:ã7Q]uWǔH kH׳Xt݊tJ]_xЅ~麝C(R4bu0tlx:|eu=+=Fs9"Fu6KWAK # t6'OH甐 Ŀ$<cAHI:{,J]_H9 ?oyq07WϚt=DM0t_9f-]דD?7GPq*"]ag+ƿ$:KլEȰ+\ˆ/Jl| 6\y({.#YJWdyV<:bc+l ʳE'}ω\ }PpOh}JypOh}JWrE'U#ؕursO+?[}BAcy^ A]lw~>f!79qL}bc_{_`)}E^+XwMWT.J}_];S>w&Rq& _xO\ue@kycZ_tLw@Pdx&Xy 4F Y"N˲g>qB^%j $آ,CWk):.)uXcд޼ [`祇DUYΥ<謏үe[uhc;v=8j(j(,ԍ6S1l|PA֖Yp"EϨ.{åd/Lbv+4 7]Q(- \[Ư]Vxi #sصNELRh `ᦅc?TcD1f5L9_Oυ4= 3B~ע' 6k{kVHjxGW9[0V{YgWH{VMD?3&g]\mkBTaPxݡm`ю,I!ӦTUT0.@w&lA0uLw73 g55Kӿ6kӿ7Lo@BUzgzoGGGsMހtHo 0Oo@GGGGC8`)џ'Ϗ RTqHALmkBTiKxݡQQJ  AfŶ@ GBH_AK'&b8&3m֨__a@uހoOUz2`6џ/??7 m~/GGGG8pzހtM*xHk͝\mkBTnxݱiaRE0bg!","hR V6"d7)FMMM>7=MПP,`2`"`GzGGG4kӿ97?????_7?ߍ`Oo@GGGG:?<7KiP Vr :FmkBTx'xm* P HH$THH}ʺd5;f:g+WP)RJ0//FSb8zcOemo-x w6h/]}Hf],t2ӏ}.WY& =ZgHbݷԑrm Hetzx]Z fXu i۴@l^we!4윹}ΌS$~ظࡠcy''fR}sׄ?/<6I鬄>Ic1@Ng#[9 #HPKK??|!lZH,׮IPq~uI_rzn"m3rI/=xB6ŀ綏b7>1 K˴n9!!7,nj5V2s|I q/7։ohORJ :?MAV!;Oݿ9JƇNYwuk9-~V6Q=Z(#R>c.}&R9r+uרؿp@(_rϻVBwup$ʞp@ˌxlxWy"LDxlp1x< @-U&ioЉ2+Z;T#ggZ3 u y`]>;\됓rϼ'U>fkY_ \N 0LƟ s=1.Dv&BT쑶IKSXOlM7M9?ϩclTHǸǩauE15vR(PP!meK/JۤמzQ[x`Sm+9>FwH/G;S<jslskTyc}_ ԥkX-X}$mXJ͹BocC7<'9ؾO*| .J(JQJ>s1C$K5}:awZVY˵\ s3k7_~YcmOi_k+?|,׷  U׹Ћwmxɽ]ڧWH=uLпY2+礓w/!q9,:LbiԲWI {?` Rk9c^|;Q T ;3Qs9#/1`v &?3i _sQ?4y Wqc 9g86:w>P}G y+)mkBTx?xݡJQa/*IYp0V\""Z &k ˪Q슈½{_s8pJoyFMMMyMo@Bfg ?79No c~ހ,8`?_[f _ џxտv27y]X;xLo@'a#`G { & k{M 7?????_TZ6 FmkBT3xݡqaSl P@TQu6a3]#k*>o8Ox)fAk__[;>V _]z&`g@Oo@GGJӿ6kk"џGGGGGz<n' ٿ7Giצ?!5mkBTxݡJ`]L*",-UD1Ȋ+4L0N7`VۂŴ+N~by?xG;5Vz??sހOrހO> ^]7{@,џ@篻g'~@*YS0`MM?7?xKo@G6ܿ77}af?Ys?Jӿ6kJ =7YdmkBT}xݱiQQGUbPFH@ZV"CBv MS-)N^RSѿjM_[7?}~Mo@Bez???w@, oGGGGGny7Ho ؿYПPfހOO ?ۯqz??wހܨ_7 F<mkBTx-Jaa"*Ij A0QA&܀ Ap }¹}kuPUToo@~z?ПPϟG?x;Ho@'"`4 | ?7w1{ӿ7{ӿ7{ӿ7{wk loF& Ok? '_U RmkBTx!nBaQA P  -tM ׁN~Vb8&3mZk/MMޥ7?ܤ7?ߏ o@OiNGGGG*`2џgiz????=t(MY/ծRmkBTxݡm`юmB0a0 , C|Eulp;|>55Kӿ6kӿ6^ Ow@o@m@@}ހmHo s7?????/?џ'- ϋQ@xjlgEmkBTx흍) q ĉ8D^>׻gI@XjjgiЃ`0 `0 ?ϟ|:seQ3|ӧO|:2|.};7eGFO6_Qv]T]^ˮg{>pjzkuo{yye?{-x/ D:3D&򈼹e^Hyi#/OGzϪ߯_~ :sMe#M3Y#=2 QЙ[\s=E8}E>GȩT ڲTg-}VfoSVwzV}./>~!?U1<#}=F[ ~QڋBN..+푹^edLo+[\-k dW(}6q$#?z6Bөi?L7!3O_Q}Пuo[=tkȋM!'}/Ƈdr2_Cﲨ: `0 :8o=+8-4}۞cĥXdq{bUq©ήm!ƶg*ΪU\z[GA=^+ru{LV U?)V>ғ)x|Yҁgi\yi^cUo*= !TY?rfgWsʽVn*VX#=Fϫ+[F~yH\L~[O҇h5ݵTow|Sfӟ+);F;:x )/OS yUo2e)Ve3'wgGg=J^`0  ľu kU,Ksؑ5nY,bXw{ w&3QהNQev ]ƷgcH˞i{A3I8hwduwUIWq8I>+@pQşGcZ\ƪUߝ]/:3d;ɫ:gB9R|GW~w2;fzt|+i5nΟgZY|<1NyŬ|E7k?z/k><=Α}N΅>uWydʬdz `0 *\?W8GY:Dgcg< 2+'W6qn؟{ru"wU쏘~c#T?+y{Q,,^qF/Xv8.֩g3}ȸOP ~n%hUG4(_sn|W}Tg&x^c,Fѭ+ <#+}/Uw8BRh_|33!mr\7U9m({ѝpvew[xG]߱?g;,nҽow8]וb?OV=Z_#ve?vN_WrYLo;1g9pV^G~>[_vNOS3 `0Q[ veO\k^8֔v<Zbz\Opbn$~}oz3ј mK vU]^iNWA#x딫jt q :E= z%օq)CcYEqyRG-+u (K\hP'*^ء^q=m=y|Kvūe\rȊ4={W1;=ݷxp;o@>ȘT\Ԏ+C=*ɫ|GJOCW]x1.ﵠ9_Eб Vq)v(ʑ}[GwǺ{-oSdו_˞׃2;iT&w*w:g׭SOsj%Z[~_˯d֮+w]7 `0]kIu+eL]ւoA^;=GR?v쯱;<y o$N1紈=:ߥPVu< <&3KyC/4r)i=*/|Ύ^]QNН1qGw>ù{ ?Kv:A}E:_n+{u=rq͓̳]>>d}+|L01`0 leg:׺񶊝`W,3O?]\9P~[kOWiGc~)-<w.3q}'vuw$Vnv(r52S;Wk_Kϔ8B/hEՠ'9w?K;x:x<|@cϽVyc@ۖSw8Bq]=2lBe6V}eR( VeZT4ade2ޒ+nYBTqSߔ<[&=f[|szP)G}{Zׅ3n7jpWwftEw[ǽ;`l? `0 `0 `{~i`oLy>uoi\qK|}7Svu9G쯿c¾#>,jow{ՆݲL=mW2u_8دjo?kD߱mw>#}E:OۡO;y`$jwmkBTxݯJPa/E f*&(Z&LYIE,lVAl 5½{_i ٙ55Kӿ6kӿvހOd7`'g@wހLqz{ ߽7?g _l72>Ho ؿxހ?7y____[[^Ko ؿ{ހOy'` ӿ9Oo eހL)`mUbJݶymkBTxݱ*pa"td1IVIl,eHLnբ\dsww oŅ17/v :Jo@B߿O9Oo@B>Ko@',`yz?Ǐ A _7MnwӿؾJo zހOd@d@l=џK5Qy]mkBTxݡmQaFATTt]-z\B7?ɛ\sb=147m>ަ7?o? N TӿSz????3Qe@Yb7Mnwӿq@Lo@GGGg7IRPmkBTx!aabTŠ)b(-!ݦys''@\ڠ[Ho i@Zހ/_Fzu(RDmkBTsxݱJP.%"$Z5¦@Ƃ hn ! x 3<8Ziצmn _M7??W ?_79Lo@Gvc@<`Oamr@t5No@GGGgG ^{U__[ua daz'GGGgQ_@#ԟ{ mkBTxoǿr8ر8s:;})MPh(`P H@0 b&&q FFg87N|Cry~?yn9'OZJ PR3*U" TCn KAM'Tj*5 nNp5{WǯgO}?Uds&ξY Y kK4`joH6W2=UdԃR F썆Z@]06;vÝG7^ >|1j n1Jca,V|TIptk3j ]`K ߋAp4փ?lH5\9 Vã6?O?~:PV4'_a $h 7̧\zZ`҂٘z\0<{rW ugk/4`KM a]Niry57z`Ce.DЦ"7t>8?.½ `i szMRcs{ͦ^7k{C;"O\ϸU3g APYQq/ Rys,ټ!dz0xc!bZt]Ր+Oj;t`ijjC鋡$ohVޓbf݃Ok<0vm <0| cD4u|6[*GW"o0oH󆹽f_lYͼyöݬ g08r\[T_sSBR=N3yCbwvBpupyBK.q;n(|/Sk` 5%BȘ_ܽaF(cWaзO r7 c/_8ϭ/hk/]k!?M"oy΅ U ސ Z|-|W\DXcV1 oK 쌕uP:M\΢EԞ#:w0of`Dsncof8\0B{&?uS-k`gg_QL]s֕ 1ݽ783Ϭ {ۦ֋4dn`g8eUu (1By ̍,!+߳H|?T`RdcX\x7$ij "U{А&6\Z5L &A :p1G?@ZE}g A8-&83;4jhHʛJ?ʺBbAf9\D:oǼAi*=FK:z`M6h?X[&MCQNHFV3ޡ3s]CUzcTs⾠DŽ}}{6^TzH9Z.ǠJǐ=.KLȱN!Y-fq50/:@4L ư<Ë)fN:7ڠic%,+@`_0-]Bmk]UwvIJ?rI{y,T@%! _4 5jKbfoE4qWM'8W"pneUlJc!3|juw{ BS/pĵ#KRrr@-`_yڀom -LW6pw9?}˂/9,zc^:0 <)y,L ڤ=;@O͏~CI!`]`:t|]zI;v58{*2ܗ^b!'DYNX 6њX}0_X )I\P"9%=T=414{%#d?wz7_lAPLX3x:C/+}LMf՞r.%Y/gμ'}^pl5A-x}pdX=TM{m@+̥Ra塢S#E3^|owܘxwq-Tp Fqt2M6Zcr} d?ߟ i.4Ztx<[д]ӹAYVS-QϤ)x#ǹ.g1/ A>}'Ĵ,HIK~./2}[8SL: 7!]?`ĒvL1_`-q "Gj>^eд G=6MCgRypGkIւ' wLDbu>:"8CFBsX?p/~۲w<}R{ +wڰk*ٟ?1 !=99|er߿or:q77'7-H׃ϓƗ_Sܾ|AE˗w{gT7 s!ιo{n`U.:{=M޺5 e|)'6ݸPW08rϞzZBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAem-ƆymkBT6x횉m0]HI!)$FR?6c>>~sm+vuՑνYu8uN?WP>1JsWiV_uKEϸ/rˆ_gKW]ױEYcl,[TYHT}xL#}A GV7^}>iҞ-i;}LJX&TP3T#ߨgJl e'=?͘ona|7>?ǐU%;/mN/IfQփz{G}?v✽3X~j{zTAO^ʰ>?sy|G)P7yހtNo Jo@'xހlџ3~7?]7??Z xҿ6ZZN頿MUf`jmkBTxݯJaa/e"|1V\,fQ 2X]ZZL˒MfN^ O?SvV):u+vzwg  >/No &џ~_7L}7??Y7?~>Lo@'ހO G^znz??RAzOgMzIz߻Oo@GGG?OR5?P?{ymkBTxݡJaa/ ƆI&+ (. V/`1-N>b{y_G5jjmצm{77?. 7Oo@G6`GG盛'Կ{Ko@GGGgV ӿ6kkqz~z??sAz/ ,`_Jӿ6*]SmkBTxi`Qqqqq ! COMlS$I$I$I$I$I$IqO9˵wOo]ss,Խu˲<^0 tc}av=7_{V9z}ٿ^s_y~?}ez۶@wE՟k$I$I$I@? R*f}mkBTx+JPQbIr 6-[lzA &'"8ds 8 a3' c : MnwG ? |7?sހOqz ?]7zހ=7?????ӿ7Mnw7{ O6ПPf,6Vg Ol+ӕSMnxD.MmkBTxwSםQvwyd˒%kwٖ[lc`pBRB503s=i̴MLN4i5~}nce!s~{~1vp8@U e^XÁo} 8^T@Eo]@::¤l/K5u_1AͼN2SjqfQ 7׸)twT4kKA'\W3]~Eݯq? v-YrOW U ?7?2t-KPw\T^{ ?N]d%>#,}t+dL-OkwMLgM %?yP:W l7s_ g!xAY'gOCP 9$K Jޥuߓ"LNg??G过7rͯ>8 mZwM' CrOhz~eh{vٽ */o@?V<'@S]4y\/ZtFcZP_{c4CQuORk W@Tt$wot ~9\; uF޽L`Uu_D!fyfa#_O=wwaّ }Kz,337,[ {B!g|~KwoO`$9'TfBQ5^+vI竤O5Bh g1WSto=j~G0c)8qlZ;hWݿa׼ʃ0p3^8rE?aJGjAw(_}N}5¹o=J}q.HJPBB}\#ʠp2^gWׁj >C8{ۂuQ/]CP^ z{~@sܫ[Q󾒾W}5E?ʡ|%@7'c{~B;PgN9Lեi8hGv?48& v2_8{_v* =iܟX(x1%ΞFmoOޜsCEuVYj7:5ˮJAB Ż3Z¾{{_.JN@w} +iG:+Mع{OCM`O.q3r:.> ZM¹>Ol~86@:T-16N<I9|E&X{}^>MkaUn>Wb>G5xo|˹dq!h{6A|!8/WUlߴA (+;RiX2!<$XCo t}}5wO;Vt_ZZ{/R1LE1)oYtdz|_gG w놘;|!b vW:g9"s )A{4>&}}+fg5nhe tR=Y` +wBB3|ߊsdV%Bz1L{nZ>ˡ\b}u&`+*(SG)X9ǭTU vo'澤Iǽ{?*bLH1[`wFk/_jE#+OkI9XlnR cP0z_^뽇{r/h θk mkakzh:. ^4n\wkv{rwp_fZE=CeqS]kZ 5^稸 ܇X_=!޸QOqٱ'!m${y\#Vm$%f:{0 v`ggÄ6,-|Mz93Z7}͔Q{_bs5jn:Hz%!}5yIZQ>X{3OyY<z$]px.z=a&GZm^};ڷddާX6SxO E+{>^ڟ*:Junuol~ڵd |=!24@1٠sB $⽨9݅!%!p(#ig+&տU4dz~fOo5o9w<JƢ &B54MԾ-7 =_)ȵ8L܇0n%(M`z+)v]k-57ycs ]IJ;SN1rzY׾u+NOs-u ߖ yʄ蜑J9?HhhdKGsVgd8ӂY\Nw 5rz\SҕQzݰr2;V$Ƨ9S@|=*YK81ߛ y}9uV*^1|]6đKA%hlSYL5؜O#S'}x-֪/r:=|5({3gJ IO<+xrF%L%dz$R whn_?[N1$C?۵+{ڿ ٿYx׾Nqc*,t/O'SE5 n(w žA9FC'=r/ 3Wɢkw/ܱ;~jVþ/ca i!YX$wq8~ɼwc!f+Oc+d_M:f˱ scc˿'{7.r?p}Y?<~{ܿw%nw-ȎY4`+w3W-o- 9ֿ+5+gqsC\ρV{|-3NOF^}?/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVB. mkBTxݱ*aqbD -GNI$-daTJ,&b QV;y]۟_SƛёR u*WM_7V'?~[Io@B ?~7٦7s@0nz7 _7Ύh:qn.3<T*NOO}}ҿ^<L&8:: Κ}M=#HL&\.7p\OAprrߍ$ ߘژS4JzCmkBT"xݡ QEQJ `6b5 i.~@'&<1GL55Kӿ6kӿ] _'6| oހ4kӿvznџwuz?????<_TMx% rmkBTx!JQabĤLPP)) P%Mdl7wr] O?nco3Ơпz;Io " ^7]7s@]z???3q?q7q@8Qzg ϟf _>6/ӿ7mџ?~mkBT xݡJaa/E9 YdA,a .xc lfz'W1Ox 8mwZiצm& 7?ݥ7?{ހ,`Ez'Կ{=Lo %.;,[pO"%dƾ(]mk``Yq ?Ҳ{oNQ<;?I9 Se0=d[ [_MN/_ }͢4OT0h~`Yzl!BJwsbJ.!N?SΛiN?ȅ#w3y}Ƌ1P'BKm+̑,(KzN!~}&[5uat} Fj?;+L$y2k7 =3ȟym/|azYy#N$)/,.kt&I䋯k=dck=)* ɪ!8HvN\h.6\B7c o¶kPs&v@aL(3 \e&^NNR"C2{n4I̋ 9a$6KN2LӂW .R.=}(4GnȯN} Lh-ePth&RSᰨM Qn[YI. NqcCU< TU2X}Gl-Wo:;!l L (;H"t_߭ ciG}#w3z¶cw#uG G rgo<E; DVHgGB0d,A`8볇Jqk!΋ ZŨ愜v! ;!~;LfggˋAl;Zg{Z뗓$4QוjG@#Z0Rvp~.)k#s<:%) ݾ 1 ? $s@r<'vB%_0E\ 2r̈́Uk.Fg6GY{tGh㯫bV t%ūHG?_r؟ݟ*8{ '\h  ^03mvi2 7l0l Eyusܟ`Ew0֛O|-ZBzѿz0)g&k'_0B'@'H\ st|aV+d| y}xmO Ϣ9@=y?ƇO07A>H-joUX(;*Etw3D ^ x7e2Dpܽ3 ޻bv:7PalIb8I7OφS9Mͷ 8mTGi^an%پgk6M+^zcDN 8bjE0ms~4lZS8I:LjvpF>fMbTV_}L>;X}>Ơ_Л)lЌG_-}]h}!Et3(O׉XX[9 khgXZ}b=e-%P{|6iwBfͣpNhiUa2ĤGk^1ilMXsٓZjA0^qMhWkի1UR~BS V%@pv}9k.aҖGp37|㋰g>h^zt NE)w#|AbK)sc `LbS7,|ӳƞ4g-D=+4wk敽PW$g*0v>st2ySՃa߱VK i:Z`j,bEs!4W 8&0YjYo G`g2u,}vAKμIygҾ* 4n_+4{}yn]]a޺ZUJN7)ZcdNZ藱QK ;ӹK\<{vU,ť*ɀxQh>&~aoS\ΙhRZ](8MʲP&GpWb rەX?7Hd5K VU!dX+ܝʹ ʟ˯$Add$'HJR }+`IX7%< ڌw`&fOs:!Z͝5R(b琸? APoz3JETA)sM!y$:dD]=yf\?x9أo W07I3oj_0/w-bB~OxFqJĹ^sSaM!;\? cD2?`nQk[T 8뙫{]JX Am׍g]}J>vowW1il's@۾mM6!m@RbfL \=rk%3#MO/> ?gAhW/629!:ˁ<@d lĿ@"bYVNzpuS]  (yS@=3 4Wh=vi?˂Wʛxgs> a!`(ePdWº;d"w t}z3It,YnX>Mcr>`9>Kg mQXzs炪Jkۉ}z(g0k40<Q8WG]y.֚Ί{٨^\RKi:as} bj04TϏ%E,?H~.u.|0i{:8Ƣp,9<ԂB6g:sNUN|uloKGg9G{p\e잷s[ҸY!m1 |-j3!Жq/f rvQ<Orq[sv [;VԶrŞ@+An>ڀ)g4vv3aIE|uעF]x(M!(T*m!}]O șLgxuw F91Z?Sr x c{1uqܳwgf8'5c7]v{ 8}SD޳FMQAa <gObV#={? ^Q︉9OLZs/k~%g(Ƈ:3f"(Fw=jik߾ϛ}?ACu>. .nw?7gymggl3.(de`{Z%'Y=F\Gqz/Õ(@9;͋ xCVل_ l0> hwQ2!L^|Gp/}v1{֨q g?8xѲ>Y.ɪ˿p߹6:`P7>#ۑ(փe5s9-ϻ;-4n5}s_ Q.9Wj-9~_ {w`k'ߞ#uLtϧ{½8i3[@a;q\;wm{R0j׿s!{x y:;DZאsi-![a1au:}'!*w}~jܧ j}ii+>>;0~\\;ګ>J; =1kpR]-~y3\%+U$.;<Es~tvm3^gsZ[ Io3~/]] \>D6o$$b)x]:_8 x=r"cIBfP?u١thh4T*ɵ::E~xDVУi,֡|÷bwM?ߒ3yy"qgO'ǏPP+/5iD.WUid A}a"q--ZOǑn65iWDH.\(GT^{"#y`^r -_Nɓ>((((((|K?%WRmkBTxݭJp]l2?Ғ,6eAa&T &o@EVLk^w* OW3֨__W' _7?Ko@'<`Yz?ϟ />f Oez?????t~@MMM=7?nzd3` vzmP/c>xmkBTxݱ*pa"tIdQG) &7jQbm*x7֧Kc : Mnw{g[ _7?uހ~Ho@B>;Lo@'<`Ez? AK_7MnwӿؽIo zހOd@\bc2[Ko {ހ?Z>amkBTx!JQa3("1(`hQ D\ǀEfd *'<-7[{͍ҿ5{ӿ7{>/ _ Oo@GGG&`k@yzQzoGG Oe? ?{>Io oMMMj8No t@j47?Mo p8Ho@GGG/ߛ@'XߞmkBTIxݭJaab!* /[+Vg<b,`1 <(;W Sۋ Tuӿn׭\o7Ko@B? L ?O џ9=Mo 0`'쭛ހOu;`&`~zIz???3?*Jj'`Jz??3zzݻ[õGGGGf@/Uӿn'|H`{CmkBTx][T89j#JҔޫ{W@iQ @"OԨ9ܜ]2ϓ'-3޳03{fMg}Y]@{SS'mK'shk4+l h?K6.h5Xf,e] k*mdEU﵁c.P5(4*#cp{/B`:B|bB:_fT8C" rt JߏÞ+G >IfMD^fip$>S0D#G39o*򅈍k4O׀UF w#^MX PߟMy.phMdCrNG8pw!NȀg)4 6LzF1 T@0t( :AUvK&} זQ(0'{A8 ~8W`'] =Sor7X ?}!R%Xb]9c-s4ÅSc̿ Aޠl@q Bd;ayNW: R6k>V4 F2Vkw#(̞ 'k8ˋ"ΌZ@sQفPA08Hj!1qJj/D{,/ dF;(偔hG9'sMmsT"&%Zh ւ~s̛<ql/bq-d{+i !Ʌ؅քPV n~S뵁j\~']DsMpl{ / '✔G$'E!7Lg׀}-P:EtpJ:@TORC(! V=Gd:R Z(i 5Ս k:|.H .ԾXLZy>CbǭgJ 2R'Hp%R#Q`n;HA)?ՆY2|!_iLsw%h`- l0}~DJGсvL8PqBFoi4 T=cNF<*;`|3e~M#Mi`NRA` 3EJ[!o$?`F <( "Y,©Fpȫfp4=S X%p gķGP|5 iS2BIL1R L'׶prrG|Qs kGw'j@y`Rx{Gޥ*rvl"jwLf 4 Z3i^?h3s G2a>"p0ԈL0 hd?=)f[,{D!l:x?i!h! `s탠?6?V8+AcPE$/Di]6H wt UDg93WH=UI Mf kp:#Xs )ni]+2 D k$?S>Oh~PK"}ICeT&(X7D*ރ+9o p]]}{SH,~]%?#.{Ɠy s$'j;4%ǎx&P[~ ݓ օ<´I|7{[>0W P`wGd=ysQ?y:ݥ_W? Z+-p%\E(3=\@S\ G= ']mBg F?י;ŬGv'Q;'<ΧOgբs`#zX+bPf9e#wӛ p; *uu1b3ҩH9 ?2>Wlo2x=^4nWD4oI ~QiKqTr??=Δ;-]ה1nOSwMmC&zN\t1qW$}0sXM*0z%δDxN@w>}+QROa=5|ݭ+(KL=r9pIg4ç*ϹՄs?9%/U)Ӭ߿?twuB{S}E̍WRsy=niſg*wLP&?7'9]6;Vٰ-ϦROp=?'^1'h{U7|da X]o)q4y&3SA8/PwEs?wu <'_?ި{5Ž*Pw=Z~q]EDЦ>X-cYnSjIrMЁ4yT'8<âƳR<.!w?Bo>׍9IQ@o!5 s N,oDb݀x IMywŔg755RDwH~TqS_Alt@k'q a,%DzKȗ_<&%ם!y.[N-X_P-5;%n .'z˫[N*S_[>#U;Z%03^̛Zb 8] ۡ%r:۩۽e15@]{aͰ@wPOM6fńzK<"υ{ K ٌZl1<$b677gǨ޽]fl͹jc&'Ho9aog_va3Q8x z=9"OoߚMOOñcyBOL`[|)_of6??#O, 5x5L V-+C_KybP-1_:剟L+O+y.ʿ6Dὼ"2 +?ʣykB(+'y =aj%8C&g`Z'>y"&#=6FCJ?lMXFȿ<AWG ħ@Ee,5fBbx'nskqXz;?n-[BTD@sCz9P[-&'D\Yzy+s/<`VE2X |nO%-Cc P+N"ZHl0` +`称C0w'ߪz N@];`PyBt{V9,=>ka/> V:?l>_ևYCLF2z'2EymkBTKxݯJqa/e*",-Uے  jjx Қ| o8Ox z^kѨހOW O?~nПP}z7uހts@fހO:W ?+LMMMd=No xހO`@@$џR@% 0JSmkBTx!aabj0"&Am YĀmcUpokQSӿ4kӿ6kkQzyz-o?{= Oiצmހtu77?????AvހtM*xSE͵mkBTHx-JQa`b h1| m `vN`L2Y|Oxpxm`sFMmkހO NLo@Bџ ~7? OyzWӋGGGG,`___[Ko ހp@Jڠ[Ko f=`nހ8q0_jӿ6dBףhi]mkBTѴx!na.M&#&:L YIX$Agog^{Zk/MM ~W 6 _?CMLo w7????3b0Mo kހ_?_]z????#O QTq~Cz}I*mkBTx}+(H,"H$"#X$,QԈZs>U{ ..T}6ڳ-F`p]k߅~b  О$wݓٱ|sCoA+q3lOx@(0a+? T,_7s\Ϙ^Bl1)C+k(FyN"8dPC_9>O0&l4Im+nwGrŰ)/tihf ѸX>E)<,6s45zb?J\<OM%O#(76:= ӋYAƒH Ls6MXBcX&ǘJte. 3.je(??Lj=%wZizFTx$kP8Em jAOހ>~؆B9 ֤8UKCvjbL Cy ;mj P. DkwUE€3ܨ8xUJs\ɟ+;}sFQ(KIXݛƨ 1 +KdX];Jģcx$D׷X`i @l̏rnm$^9΄zBGϞQ=nfkDe; <a>,⢞jk0B[p($Ǡp4 nq`XƓ vϵ.xHnorJ5Hu뇗 f a[Z:>36[g RL؍?( &w.7C#~B{] UW 71jk~ecGrD.=K@WDZM0倐0\xvqNZ ># BE )&yA}t?B Ym(WIpɱ |2+\2 )l8tl@Z.Be񅋍RSƃm>dIl'N adĢG3%#)?$s _5=YBR#-k"qGP-e"f%֩-ϓ378M9ϊ,_*n;HEBƱcl~ ˝[/sagIE2,z1t:kLș壋G){7ond{@rP>kwk׽ #kXfyEAB9uM4P=_lgW؇N#_nGpp ,ZUu6ȓVӰ0EK7*|]{75F\ԶzQz! uH>upT٣o3P)[^6` -d&*=%fY<^ط`_6|h3ء>2 Pq7ώ ,NsjF=B` 큳CiU)R鐏@LҮǧmb<2FHRqùFXi䎲OmGA}:*u f:@ʫRH.66jcGOpO- 6HKJU:Jǃv,3DZEƮqq7p?ȌK%ȧ$;?Qr6pP7`a^=R_)m>D3#£ _' Iɭu͋C-Rne㯄ssL<ȭ/R)|Lt_1Lk=rr 4/gEr~PnB[\g[{gYvRW' {Fem1{ wL;7&$xc0 n&u@5sCCձm8Heft x{q(aтa?Q%l4ςxmWI׆GC1kQ3iJh,KRO`ʲ4)%b6B8\pe;u)ko)#WSncRx{[sXv195_0Kՙ7>Tp5ٴl3S"؝LX睫[5m Q="u}pϘ*xbՉ#iM+@Z! Ϯ~jYݬ$?5mtu] %@݅:4h8ۃtu3; ΑO1A/r R*5i&j#Y2:$Z(ad@>'z L뇶6Z8|`6"X1_z' F-я?X^ A:?1;h/KVB' vOnFS ƤQ{=kh7MwXQp\v͓O/. N3HKRlK"q^Wh1wt h@3e6N|I;y?8t[[! $,ήLe"z%IކAkRl!3u8ځy?_W)AbCO!rza5Sn֗#<43y6"R߃CQ&>[# BHǽ{vekOTlq(UH͵h ݔ8,@tՂL{p/*L"d_y k,4 G̖bD>,.ok"D;|7[.DCA#ilϟI֬Dq]+eE _-- ڰc^Lq1~CCC9gNH8BkhJ#Z-`VoMa 9r$պZ-hkh ?C$ ^tď9d(8P݅]ڶw[wl;dn׆oKd Hބ(DInI M_(5)6H/Y1 QRk,nXHʉ?>df&6^EJmt{CCc`0ʅv5x<\9Yc}106"״!֏9dl:' 1H"z'7QqɌ#KR./CVgQȬ\ `?d1yuM6Ƶ8ZX]8^pwQE &1frRKi$GݜЕh3'{;;~FK37ku<pdʎ+C RMzƏ7)nҀ lEGyl:̑IoBS%|ЕsTulebA}Aʹ10A{KʘӺtjdLI=r PRg_LbR Şl?␔)![Fo wi&k^CV(t@pW2{hxHGRn͉eCbxԉ6GQd27\ثdS=\Ff*0ۣOP5(rZߙxQZ>~GAeN-jY7Ҿn;n?ӹ"Px}/NW:݊&׾:x" ꭥу;R펔 c䛅љElmG§a= h¨BG_uYnZ쫭FYs U"zM&:Gnu.DX5Xn;}ԫ%XO?~2&Frjj8 yA*W I9/ub)Zl: s 85J>~iI3Yԕ;:#hELם[ROd^GA˩f~Y!En0~/A Km>^WYq"<цF*c:xw|͞w%ehRgd9̕v3v Dgh>>?3hYDkgC(ʹƒԕSԜ| 2Q94(?OGQ34 fccPopTYaW(>@tX4`LGٞpɄaŰl\[9c26U M6f,'C4i?W~psϠ?kAKrŵk@I|>^xs?\`,D̒5W^w DMXf_8<%|8_왉pP1Wlm߃f?4:́_Ԕv M;k:p_sj؎qw]$F}y ,b'N=o0, ~M YR46+!}@~ujctCP.Y(x׎z?70WXFܣo3z0c8RGg0 TU򄽻w"/4֏CQ`[{Ocn]+{{ N!33+5]qpj' r9FDȬ)~: 9Gmx2-?sraG"yvUpa;Ră A\& ?#n 0eed~oq嶭!!DzP^H)>oȑ.ļԶ=Hy7S-M ?8ycߧq|#5"2Б lm#UeΤVbM͘jAc7Z ]> 4gb s 2WRsKg6 's8qzTT[R[w)I95xWj #!nN+zPڔ KgTE,?{^RDݥ=Ru^zîc&D'i74SJߔ&HUG[crͦ<׿~4}څh;lpAZ%XZ;tQ?yk1+Ƴu6[ Dc4Ɯ*dB#!}e>samhG3c^8u9󼵕⸈߂UyB;f "Yi=D =4&|C3g]~WgjhSIXU"1A5Fr4{AljwTt6</N \Rta| i>T.Wo>>xϯY{緷m,J{gg}v~)]s!?wXGFl!7U|Cnfﳅ:.@mq%臔Ru?.:aBֺE#Gg'yXDuSWNJD)21ѵVagWPqȒ s?¶@g")s\T{f3go^w:^"{d#!φt},nyWFKv„X4|VB~,˘_&fjp/WԍwaO H 3I`u1ͤ+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_Wݚw)UmkBTxݡmQюmB  @`$uMIA#H@PMKw7(PuӿneOo s7?4 y.?.џxПPu@8Ho@GGGG?џ7 OCTM@ ^4̔en]mkBTxݡmBa4h( I4XNрjgOGLv#%CZ5y- M_Ic=*G iL2gYh}=9q: 0Gykf(Ǐ);J%J~WJp t|=33Qw9(U6/*Vӑ2wт/r$a3jԀ̽ޠq\Fd% ?]nƥnaM!C''#-p>'OX&?`0{-悓Lq^1#Ygs"}rȿ_o,am* EIi#?*u~_z\f'y[]O,$ r2w4ga]16i3_#ҳd /q2;dt&>]*L)8_?/ęqm9ՀG91 ,?eu;:s>wXow%ng w~a˹j2kw/diP?ya[t:&4sm6/gWu3;h&t,ȿ챝k|v^C4tol5KLGTt~5;ţk|K[1x} ocv$ $/`B"4NnFZ;sԡmwLFɿ|Ay±XT~US%6#eh3ռ9h{­^Oh܉HT|Ud~!U#:V=s =RqΕ_v/:˩{A*vZŵ$ȝ#ο+_^ _Hh.*.hgCOru/%T,^3s \?ȿ#]i#9u:UH`gݏ9b[bKzYVXg]UXSՎ@ BdkS\gNZEi] #ʆ>ma޹Y5/$YT`Yh]|&8 Ćjݫy;}: _%V îptFϵW( B( #%~`/5>#C<J?_gھNe,s`VS}c8ԉ_jBZV>iKyNJDjy󠌣;4-$c Y6{<տԘr ?CjX_O/'>Fn117pY(_ܜP/kZzv+aerH~G;φ 6#TIPsR䖥΂Ώ)LP$@zBf2%QXil9AWO>5iT9kX̽;k~Ɣ֘?!Mfs@}vW C_/!ɯ $%NB2_#!ɯ%&                        )w"klmkBT^x1Jqa⪄jh"z OM~B ot^k__)џ@Kz=<7?7???wxMo w@gހOo9C(`sހ\uz?^ܦ7 ]7?????g4kzmkBT1xݡJ`a/e(C.FM&„i6ՠ4,,&W^y?xz55Kӿ6kӿvހO0`W? Ho@'s@azo _v72Jo ؿ?>Ho |ހ߽rS_ۭ{w ?_9]No @h#Yp?Jӿ6keP3YmkBT^x!nBaQ A Z[W%;Y9|n̴pZiצmֺ<`, ]ПPe@'џ'?~w)MJo ؿ;|7?????=_{@{ހOǟ(MS^ImkBTxݡaab F)$))|؜L^G+\~)O55Kӿ6kӿ6Y {n  އ_sz???qMIo@GGGG:꿿./4No@GGGG:?[4 .X|~mkBTpxݡJaa/E &ú$V&'Z F` V`_3xUl'Ox6mo֨___[7?:Io " _ݦ7?W /џ ܤ7>Mo@Bgo]z/ Ϛ.?7????kp@q/џ@t'`޵~@~7Lo@GGgmP?ƺ IDATxycy|w.v,o=+g!gHHI-%ٲeǎ;q8'y9(qbɔeDKuDICq gȞzPz U]]]=s0 \ uH$Dx<H$DrHAH$ ]"H$ tD"H.R%D"HAH$ ]"H$ tD"H.R%D"HAH$ ]"H$ tD"H.R%D"HAH$ ]"H$ tD"H.R%D"HAH$ ]"H$ tD"H.R%D"HAH$ ]"H$ tD"H.R%D"HAH$ ]"H$ tD"H.R%D"HAH$ ]"H$ tD"H.R%D"HAH$ ]"H$ tD"H.R%D"HAH$ ]"H$ tD"H.R%D"h$Io077Ғ8}}}իLMM>cH$]`nn9 B̈W2>>D~D Dr277VuBm{a<ʕ+<ÕH$)9\.s X[[kQ.k8vp(P($x ^J<B"(H~&EG Dr Loϯm;-,gΒ- d"rkH, >?F$s#"!Bٻ޹yn[Z?#9NMlG'Ǧb_VۓoK$-ƍnxffpn6S 63q;k x; ݧhU@T]SP= G4-s6`ۺ,/c;XcXm98Xcضm8k47u~GѩMM~i(\й !O@W0BN ^PPUE4ט!Phm[Ux`4uqm\lŴ5zզVU-jeju)ZT &Yu><</Wg>}yb$CKwd N15XX]yׯ;<\0">XҠ/az44EAStEA>06L bxt!z-aXhP3-lAT ]#3}r4b;PZT+EԠ\4)M%F,;TsK$Ig63=y C},.y_P*vE|}}ͬ%]lf]Y ]Wv&_{o\{ͭ39NE:X 7:Gǫ'bvj=* hT4(f8z ; q]ee;O:Wv}4h*Cb!iLǡؘCfQn߬]Sԩn[4xʳ٫/_ģ{JwHA\Xj7odnn.6-IӴ qi*ǫo~u7ߛ=D6#  7h`^M;YA)bwcTA0B+.O$x^d坥4KN+>a/خu]uq\çqmj$ݬKשnT[z ^x%_>y ) iܹsclfզwL4/ooACM^QuA0!2!9 1 2kZ0aQUWf]M?d-שaKwtMᅉaW& Aٮi4˱icc:_]RkP3M\ {pvjMݲd7lW)n4lT-ٮMqU>W"gR%=0??,w9gxL13"RTw^:Ou\B@xKA~_׏}no뺧gBՍSsoXTD{+zGSyyz]]ӱY٢0پ($!#Mۦf,Rz\N1ݠZ[P“*wʚ.y,q]@763g_763Of63bd!o߸|ߡ|W!6a1DAP?:LWZSֳpw[tY3o"贼s)"iecv\?b|!+l]h,9 ƨf`:ӤjJ +WkVgO &ŧ^,z3}L^"Qxtp{4,o^l]RN ,Qc)}n? 76X^_WsTv?bٕLON meS-궍}u]E24,6*l.UZ^Lԫ擯~z^fK ]rf^l6+^'lfC\J197׈߹y_?o]qFCdLS~"^/."QTvϣz{l;KiI3p}CNw]A?3#Ϣӽ,n([xD0(*bbj8J{婑 Cmd<Ȁ*|xfkv]jE%U:VMۦbYTLl߫[c? O||3/IqHAOvlfd2Q.M 1=g4g-~K'[UOI> q/j(<u($"AxB.аmM5sl6뒫Ը{08CgрD_OrRv73ٵ̽05GUx'ݰݚ?[Iv3a*eJ O߰mE2X0IWGqeoٞQ8}>~~/y$7R%,b!.M5B=/sf;o}5~_P*|LB@l ti?1ڞ A%0-nmPk((ñ= -ha6,߇x[QU1;S}!pO}r})i<+<1B{V.o]F7{ 7Q[~W8<7]v& < Yjt!d,&|%6T-NI?6~6#Ԅ zkz5u^\ҰXXpma2en9CaJvJwi~+34ջV{`~Mַ G%FS֠@ASD3Ԯ^۝r6slʈVQGxTe2x 2Lǡf7DtJ2wɓ-S+}$'~@ .˻6z*kÃxQ`h51ob|Ƶn8pev]Rq]`m̝k9se"^凿?vE$)J:޵fK68Rlf׏o"o` }R fݪD}qd(7w_:h@sԩ+yjJ['7h'"G6% D]]ƲmLvjONi٬oi4,Y}s] >Lb&Į{5 8KrAaR6M:mI:]YV)8, ī/7yW1gS{@萂.9sw3sܩlf#Cşg~RUH>`F$b^MW Qذ㺬KqwmX?aps?ǥ-cS&KQmD }Ql{39v=. i%*Uͭ&~(Uk f뜦^Sf>EmU Ҽ;7TC?9zyjj{˩T,RAQ8{w]rdiܟlQaic?^!>GG?ͫ/7$EJ.R%gBXƍe3KcLܳ|~_>zV. LW#hމ6f$'_2 U"WQ7Ȗ]?uu]!>O~`TTˬ $]DKNMZ=ͬm=ABL:h3r3o^{)3N*ݦ OУurqVm2YovND u%RW^ <^WSP1*`GzO:MGUe;}v UsxezZ_4: ]esʝs~sjt}O3>2ٺL0>2Uftf2VחYM/Ҽ|!}#} l`~F DfggYXX8,aY̯?9Tǯ2'*iB uļ^#*EVШ Ml![jv׉Q pECSS7[-VbޢZvCߒf*&zU ?hb:n<\j4LGcNj`ݶ)6 궍lJU@Plՙ?6 ɦuq]Ronvha|4>s~?drtL"#]r,ms=掰"fofNk6K@OSy'T>~l3.ѡPU"S)S9]ץblj \e}@ZODܲ*EJ9VKDX lޜ?jQ #bUr!i7j3LQt}\wድ:/T,0ƷY;yޣ>"~s~cS)蒎ܺu'mfSIfS N'՗+`QOEjh(D@[ׄ vj CXl<[8TӴl3f@ՒE>kR63܏ D{z}b]ݿOms;9aza|_DÈh*F)tQoXl{쬫kx5D8@Lޣ(tVݣ"H,tmiB{̖߱Kj•8RXsmVojwnI?~GpKNƍGZ"f*cL9fvR~O]܈h<uPxRԜ ]Û fZ88ӮZM/vRkUh;9:K1gRܶ(Xg0?hx,ZXO{b}^{ }Bo6) %> 7twnRi6렫*]%7ZS[rv7T%$'Wbg|ݶwģARJ[nMN /~{~_d|dQι@ lf sij8}xס&">N& |_߁ ?( #pע0^.oFmLBCjlv(ld3 9~ntUD޶:%QKzA숽pPT]7ߧ1" !knk]޽d#>MfvqiffRFn'S~qrʶE5gQZ Z/ dklܬ

g+9ٴ}\>P(666Ng3Kĸ4b6H//j &R p#˿U'>f약ن*}a(Йc^|/ڬ(͂0Plܦ(mͤ)uSn֣wfm^jzFl֪=OE5gPC0 ,3GOӺ.])nm8Mz~^ET :zjӱjlj^K5J֢N3ZhEFhր0"F)FQY{e@H?])#vlfܗTj\{Y}yrWT2 Mc,Ƴ7|؎KP"WR5&z:\vv|Ź} b(PO IDAT{ٽZVZN=pAT\R?;?,ͭC.E4)ryN˲#EoVG7Lx%փ} _w8T4{$y[ vM3|N^k󸻮K`VlIqmUV]>*tK{C hv3l3sqVfvii3uF+wx_^ߗ+|y0Hf B5b;-halתfU$ۨDDZY+:_9a^(-{~ x\5ME4TUivaSDع{@T4]ã躆ǣk*B:[4j`ǷIwt/x|4 "R VohOCIoυu4E!ua1X|xU CU{t^<5_,*F{HnYlB{(+0 tlLؗ^*R3>j.c3ݻM%45D|0vRVy2ɯ}5sm `("fE |H;atpPhK^؛}zylBȵӢT] <^*xlkXjk1e($jOkfVeV*a3_/ ,..277w63R ͬo ԕ' 62iYb>쭗ʹ.F~_u٪UTx6[jNf*M1kroJ̅\ؠr{q(Bv;?-x [pDTr߇?>0}^g}eˋ4zy !'I'uYfq5Rzŵ4i==)Nߤē~|?OqY\ftT^A^%+Mh6jCdVP?:_'C~YbY{|S| !~}'/?}Gvے:dyQSC'_E׾b>j`pmu\juu~|^ղ͍l}HV(5U?H8ӂ삦x<:xc;HA bvv9|َ!`f:X]??'_bcPPK0VxP}=b^  `b*c*ŮeUVޫilE);= K4Ljt8>ryn-,r"ܸÝ%rFNf($3 hV7̗7"quE%k죢* U^ëjDN.1[5D~,fR&_o~?aV;UUO^~#gv| ) Ba+~_6I35?q2:lZb:X8oZjBs` 뢸:FlI JNeNF g#Inx*>K"ǥ1`6Lyܽ1wP $%=_^a۲Ȭ. |3h@+4?A`e[{7BnŽRpcE;vlE]sA[zULdWU^j2e$].Q5[;-@Q#SVw⫫ 􅙙N13:3ك\?d3ޥTy/%JOb :lT*خ@Wglu\/pKQ-TJ74Z7'Gؠ92o]'359xؙqxkQ-]I !14῿w7Y[l{v3sbLJG͖eso!ÛXNߢSny1mκ,eaZe~Qg vJPeY9!1?O-#|Mw+-..f u3;keb>G|hI}˿o|n?7H8<9BD[My4y2Yհn(T22w37Y_<~Eto/f(g0;u-G0%޾yuV60ޠwE_}p$MQfBsTG]HngYVu_xgſ/S|qC 9;77A>_ 8&163)gk3+s›UAArd$Gzt"3Ip1`eES[3Å5Jfm6u4]oYvqҬJE+8GbݙV5R<ei! eR*9i!}f0YwVY[ZRsqP$>Hʕ蓮ȵ7__b[2_3lB4 Hv۵r{_ͳFo/})uM!{ڵMJ̶m6VH//Hb11<ʫ~MuOps~K>~6ݛB "҅"xk~WFc;>\nSVj$֕wGゆSzt&Ƈxݩ#'t@8dQg+a;f#zCD(TMc0">4J|hh_?]Ӟ2`m)~QGNK˗}wo}w^zY{(@<xy JvX(Upͬ rۘfF{]4OxbYGsOOׯG0!} /=?sOOZ.8Ozy v!W}ȩ7_/ݮz/$ޠ"Sl|g}Xw9-{ZuD𛝭rrՒE]Q;V08:H.jU a_!B' * ~O]"}-$KRdGmYlWɬ.Y]!Ig^G|h|3:o--tk$HsD^XZ[1{{G>y1#, !Q~ÇG;xAWUDPY632fƷyZ{%WGko}7r~5a~/^OِG`|4 [-<gf ͡ںov{pP@x F;èX&K"NKo78}A se jb@rND';z=:>>SBgcY; 2ˬ.B[{! g05@2E_<{lnn GN<ۯK7Y{ ]%9:Frd wIi㰺e~)RboްJ,oY_cwX?pa6*6''8:޳N[>H~_b(9r;HA?G|_tWWn.0;5jF  3< !Hģ21LzFzyV/( no(b^XM>PJyPro; Va1s,GfxXiQu#N~|$\NO8B>~9#s4["Hxd^@{>o~Dw~VJ&Abw.d|,}]as=uWȮ~y, ht^/@v]䲻ִ|rKf{B6W/-N;FSU&;Y祿?gξ!8ledVX[^$ygDFZjV4 PFc$̱ \|0^F(ss.{^ÂZY-f1DݶbWU49ImS/ڼsiFɗI3b{,e3S5084Bbhб8mbbPz._Pvz! 2<VE~&8g&1WèB)=^ ޛ zFyЬK6qo7?z>ȣJ%xA^y~qq+eJ\VV=40-Ks^@h~|4N_`(̥Sb&+K-/^Ze fȋ!4o@nXgt\+mZ*#4UaPi-!p|-Nsmþ W&NTMT~rNJ/Qv^RUhse:-( (-e_66zYVsy W* *kaq)TvvsJf=yR5O^U%Jфp[*]eTlC$#+SIWh۶f\B{b?LbQ>=pY[ee.Mj 1 *JR@ >| H`$J0!{1uK-c6O2sU#~;[daq |PzB6|(=9`fرKfCyba n<#K:Z]* J۪` r_O걵IAI%Yaм뺔evګ}^޽@X s}]`b@@' JoNDsy"_^VڱG~TE!B15nf*888 *JmoN3~j<5<@dB'?o][^R*:|$GF< T+e63l̤qV-K.=OOODrH0Թ9PF3+~CA/éPIP:dwwn'k@)J[Xc777fhF26|:K77W ~3x/)7MiηBQN7~M0B2t­E!j{nVT[ojzm _߹VF?AEvaa7 <3:{Q}Go~Z9 k|?o,mILfmFO9 BR![]u)'8wS7 /fX^씅^tt> ˽<5rRU-kw'Y:(Jfm۬033{c ($B#'W/ ?#"y8U)|ʻ7]%i>L04-jj} m+q5Ym縖KŞ=Ksכ& W&Nb7v΢XkL|6n$GL uW)J<5L<5Se6\_c}e՞+YIzy"oA&D +=JE mͳ _czPI(>LC\`uȥ +}ڧkj%k;5h-uOnw & J"?o9wO!R'>RfTxF f-м0[YC*^_}AF7vP`䥽+;smlU*e*j=z5^'"}\~O}xϏLqk-[! $ZW?s]:e2+Klod]W~SNv d6:5JX]BW#9df*vXMm?2Ex IDAT5{3v9[|v@ԺK4\oe =ܨؼ/muޅ~&g͗+q^ޝ]Zk)Z^Us d[ݽ ˝&rN>|"1}>A*yddR![.2loHs;BuCh#[6'L^2dVY_YbyNG8\KAmյf J.I%dž҇\yRcltśeiz*z,;P L䯃87s_e 8g}wZzjόwmCLl0!\_ $I !\$F xyLQꔺ<~?|_߹#lK,C}z.>zӓ,Nۣ#mYM-pC${2lwWT &xp^5}*Fáa2.-'h$ĻMV"g.Ǚ:V-0 IZP8BLv޾,=,j* ;2xS(>&]NJtU ΄uS(PR8gN9b<מr-rÉu+ϔHD۷-uP1E$4 !Ğ nCڲSvoc =:䞻$_~c?3?%N)9_n֖La 8zKKp=mӰ]G ο{էз~䖟np$M\(aO<s9;4b\$r} w8DžۃFq)pu(C*6[}$ݳ*D#:詳xRӝ\o[^$ȋO?I4h{|÷ZYжC*T7#3Ɗҏc?vU UVmjEz\mě'uk2~oh0:jA8zyX8 1$1Rh{yxx(^UϢD2'˫r{oz8[+~nW 'I%ΗF<=̅ 70^8j)~nԫ_˗ Nh<ݮ؎ZRzCܟ|V\z其_ Ki0Ő[ÎPޒD/ómoˢʵ_wRNsA8t  b8"1t0Dd"y qskEnD,p1~Y]o8eOR֩}ECS1RH9{-Op}pm0Һ7GC83S$= c}CHi^/S3+/!?{䯶upCqWvwe<(}JۙxVeS_G۟M <&wÙuzsudh0-cic.Q{V׍=Uqg=-dnP/TT*J:#Gxs;VdEi;({!iFaݫGQT"L5m6Dqk9VX2-N<͙ B VgVY] RwO_/C>|"%7ޠ/ ||NjyDU0,w"n$i|g?屇L$ޭ"C۴%Z]Z؞ݦ O1 2t7z²kh*ݺjj$ FG/Pdh`>ҽ}\x ,nBmc8c$Cc K̃Tk^u\pBP-8,Z]m[.򝆌\O5A^oH?3e[oyw>i9A8t^q,ң 4}m+f-BMqUݯ<0[{LgJ 'ma~~UfJ/jTvH'W=gT^~YN(VRzUƮ" 3 BO˛?Unh%/C=t/~O=57.wczϣ.qv/n(_[瞯`(jޠT11LaZdY!F56lYw?[7X?cUUl7ju9l~!m*R)JWdތ~lJ!~Ͷc͂ru<]JcAY[O+) 2E !_qU#>:4מ|Ӹg/ CöVJՎ,|OCȒh:ÁBvD' Œ+4cx]NǷЛ ӛB6˹xItsRDTBA4%PZk%VJHkCth0-k,Nou5qN=0 }6uݘF  T'wa/MZwu],jbǘ{ֺ[hi ThXx>\N||Jm)5ﴦ_jG,5_龀^N ןKX)ƽ6I^^S@"H ?/"<Bv-][ lcS#|mjm*B`nLEOljg&=*B45÷Wߠ#5?UCHWw}meOYG ?BqZ!3fɑmQjxw#ڼKhSYy݅\w,̮-< *|W/HџML:.J([n左ܭ.>К,#WGoB9}oC]E 9Gݲ;i95FoV| NRGG$SW? &N~J'?%2' 3F sSU"m龠wVֶ~Y^>ێZUVՙ)gȲDn`uطK/緧m75(T- UQw#է;bmiSԽ-~0wtZwQ=*}a޵|&:}[zNwW/=_87vleruьB$QNBpb/:bѸxwNnA˘85p Ao4mX[C$ҽ1V˘v}]ZL!( uTUZV%{{n7Eo(%$}9_[yg?}k-\("NʾgHB4jN$MwŅ#=CwAH$#'ɭv7/2=cy!:wJC]镼AL Zn 'oV B^HByǷۯ5젗 SǕD$YB$ʨ/Eeu\^ze߅L84AT2 SiRl_tRmZ{lXQ$ uIhZsO HªQE DsD|ʩLj簺d!6dYNow#ݰ ʢIeqK牀pB&G}bP;=zMi_^3yJb4?~U=|A Ig{b >.븮2noF td(D@d(DA?b("s$/=ܰ@7A:'R Ų]"M>ʇ?QnL__z|?.kۿSIkFB$G4ɛ5`:3_-3S?7~ё!]A{j;_c)??|a-7)”R#!ZLu%.F!?SA[Wf 4/zO!͞M]Pj4{ 1q搕nz.Q+3?nmuT(DQt]FLUY_'3*Jq"۳m*zD:[QVrcݩ*L^}L ŵ"i 0FdFG筏[o-L`f~)秘bnq3uGaZ0}h$*~oc͇niOz L w1BO p<yi̕E AtWj5yyuAГi2_l4d{O^>erJNGlgg6UUnZDXڨSSJ6c-@v(!?ϕOvOj ʺx:I6v\ :}p]W'%>px7Coc~JLM>==ͼ卯 5wȏxҧ>O1wsH9HdC3/t=}L]_vvxfˑ,:#VK"z㪦Iö(* ۦn[D5UdȒHwWō~Igx›y}&'z"7E},غAa@ ZZo0E< b~drkM 8~On;bQ#{ $Jzʥ:u7Rʚz#rl_7O_oqS$S4'].Pn/]_ۃRUgf@7u"3wFv\j } U~yiZtӗ!;Ec$ONͲZW1U%$>6(Uu\#'))!H3Kj,P:#e˳|hra"=EjJv׾d3d(W,US'~]@kw=00 =pHAeZ.eaBb5}8󘫔9ASUۘ<j lá7`踞"IGTLa yAS[8# xds_Ɍ=d$ #0y\S9s)ra6|d:I2 ^oֈ =TOLBو.+:aEn%Í L0B!?2/,d'o73Z!/TLh[,%u,(hrc8Bz^,z܆D{¦,bIoX%+[zD=QY|+u32/O=ܒ$~YoW 7-x3}p`o3WA8&NKУJ0, MЅ\)}3JަgHE m\uF*<tT YX&3 }1)^!Yf fTMg=Q"n۔MݶQ(Tnyú9AFO%֧2wdSZҜAp4-ksK\[@UdF89pс>Cpgmu] < 荛pqӂ~*{K 7>w3tW0݋LMHzJ[a8K&ɔ(@wlmǡMo81۾(KP5FͲ&!  M 6q=<ôm#ܫ)~p)g 3޲n/scޯWd>Ng99el@{X $B~u 3Grg My Z4QTx\ec3EYXIB$4 !JFo$raQ$"A2ڳߺ$QU%UHnYD{UF՜EihY \_7$abwwB}]AvxD:A4u7fofrU 9+G7Oo oɼ4^fVw|\b}m. 4eb cn_3qªe9 qD"آ5X{da>0:2$c.璉GIh~1{Բ$QE%ll!V ?/LJsfm |W_|/~FRqzR c"F 4.K^̃9܎ܴKdO'ɞnr|b%DvᓟKOfO:؟Z3%%{H 7y,/+ )T붍YPSI'5DdT8hCKr\$T$񦯼8莍no>+DJ x^SkBwi~MQ)f nlePfP&($ɬ}D:I~e$ 8*?YEO<N<}߲G4{X4sKi uV4E7,9bq6Dwggx$ZFw,kd.L*qU# PVpD4!,2ɖN;um)m7{,IM#i@ w]Q|ݲU#= <(/'u:Emsu5y{? vXo'MNg8}xuȃ#}; z<BQ6 -6\vqR[&Y.ˋuc,\5Т2[Y$c7]rue> #y*#j[\\aSax_.}ڪ$j 6Ur] FwHαqܽ%!Hh~<5ˤ~@7HHoIaTgtkﯳZ(Sg|(˃&xݩ1¡ݽn- UܼLXk 'z'~ wkX5o~͏51zzv~ϩ,/\YZzYkz"lP\>6X0PUxBƳ5N=VY"ۓ`9_[v=r!$p\YۜP'?haӨyk߅q!Y&$d¾IMq?JHIORY2Y\g:V}oUX)i_s纝KHE#2 #Ԍ*+NrIK5QbqE w2!_S6FWdTJ hq$!KC}i$!(QEA;ƴuUV5͚YSHUHDB$"fARhyRI|3a2acZ+u z a;lyu΍ .pjd`z.3צ)g;gp'pNq ^#c"qs}XM̸??.>~+rNoLmn6LN$Z(řrS$h,u"$S3 MuY!H|NC7M/ժCaJHSM Er}_Vc/*uQ,GE8.bX6yTLX?߇_쇯nYMyJK][<^Y`7ם;Y+W(۽kan?nʌ$&>_mmP54lӱeU9!sҿ zTWIWƁ?ٔ?*,CڋmD@h,Dfgxr{LN~R .,uk8.=0 RYڶ#;}Ny`6icIkBHUTX8@[/x]>łxG(4*.CgB(5!uf-qu*-=a:zRO'4~xx 8{QJD~ _\]?g'ž?UDqOy_nl=>h%eO4+[6UˤfMw=N=bQYZ'wNiAg/:LL d~uC9z϶DP,Ha+xӵ R6q]l2RxxoxUZ$ aneaE!j$B!CwuyqvikhHB4 j ޞ j,84={ξD([V==Bq.Cr4I )$SKaþo+/ K#l-)&)rHp3ޫ *@=O&[u-DTt4HIdFߧy2O?gKotg<uKi߫c?q,s6>$I0᜸"zI ;Ѽ8Exaԗؽ"3@ IEda$K.Z.Z<2^'b6oi,~D%.^w>nz2G1gQno:-r"S~Av5LHyt : ljx. %LZY]9ٲz]wz7LǂGs5PБH)$&$ 06]^8KDI#ԘiYLZfˣy8#LK-pyVNcBw>瞼a|gZ5)_XVfU!NcƜ+dK̏MvD}KU1s#-_nv-!a:3e AHh )i">]ĩ,a6'^d`dKϞ[?'7lQRy2jN>l qB̜ɠ(ZD0yN'7y2/N EaҲ)Ɓ)j/N]QrMߎG5ZCK2*V8>O w+Ēyx .4UJ$?!aí4q%O?W?G6ެ5X_wnסYkPP_DEȲsAE!P5l~m̘--09Ew;n]Eh+xpTj\D=eI&dH$[.W%iQ6-"=GB5.+Z#ig]n^ HRFR9^([kY'P㘰~S6n».IP/1ѷ&di{Ǵ}eIRU,U,Iq̭k=e Ρ׏ͨjKRaF|S-H]r&c:[ bF[7V3€QWL[DGwԳOSvNE:ǭ- דeG\>f NUk<0+ o\`]ĕ&겤$Q$yeg$b5nUT$aQ2,bbZ^@{S ?j4]I̭ؗ9_-ulfb)$'x&Ae>بordWoS~eQ$X^R,Kzk#S(Ψ*׷xO=5jH[QN#]uGDTpq޼Cumcv]?@g#Reb,cͱvyo;J t?u2 0 #AgD=bus.VuÊy$o@'R!?Sܽ~)< o&&(VBqgP$m|N*fLM٨Z"q$-4VCS*vHRsM'6ƍBnWۻeIP4Yԁs" lp~TYH}>-ߣ-jI,ig2l8WJ}1Ϳlи;`/|^z걑R۴e36e f]?ObbQQ]& gO:<:[07SRAuv6O6%LFS^Y ߨ9]jNO!9PT=:Fa"%֚RXdwH@V$l*AHHCǢ4 S ŻqhV$izB޳r6:5kQ@]BύX\PXd;CAm)”Jq.+y]'iZ:~-氳%lD6, _Y ${3U8y^m[X:{J,IͤV}6ޞn2~BC[nrkq_O!K={0ƛxvskq}̘p"0t"2+hCrԩR7nsS~b?ae0 "FFmc~N;`n̖#vYkN,o*{Pte/^=˷~p Èv%kKZ4 .s}FYÐ"#I1P&=FyqƑ\ O'4tJ|f\el5EۚXrGx1Nz(ێq"QO]DȒD^7}tt`;!NlZ;o4sz9֪O} i\~2us,S9l̘4Nؚ ϡ0Bs[_(m z {YLYF0 W6BZH֜.WgR`gaTWFSe==kf'yC2Vw\b֓FxH{nDe0Fq$h4V̬L~R%[R9M0Vc%n -.a1;}KCQxE}x#yM/ l)[!KvLH{%U\~']- ?Wxag3XY0 @4z.nס2SAp3ܷ-aaLv?/|nEF 17oxhn'f|~ҕ2ʅ2o!Lju*@HF/tM,/v(Mev|6d'ꉍ cwc:Rkx!(oYni"kk>A?Oت㘺tjU+ueM53 ^+ski_'OYD'&'XO] EU$g*zc-fp~('1lDKuEBHt]5m@wU2RSǼN]dQ-\(z,hԻ`i*/Lr}JoWYDLwjURPt#Ak=b2I܄r* 8c:JD=M!H1%A]I:]3T,R-f2Rsz>p?%o|pŵ*S;g7R }uz 9:|1*0S:]f#yRb@3D'dn ^/ƻS]lI%7beO7jWC,I{RI}CG,%wRզRQ3DBܳmr$Q,ʖE:.^"IfLn'qcGK5͍Wv+ag|J?aȊBR։ustt,cZMռ$mw"ɘa#*"=@U$Tzڠ`=BM9l7vpFÌ=1,qujdWFD2q-XPph|ʓ D jHJx:tjDŽ)i.WdiSU͈B$;aH;^ִdጮ#&Χ[c" e۟L~qN ."LI²MH# "TU7fQ9wi>(϶NzaѫO;Yx2㶦hLZK=DZ (&.]>UES. mJy/vERz<8mK]V{hiMfrb=k~Q5=^vn ¾5EП66˽ $aV9h\)( HIK@0y411^U0 lo`,dˆ^G2u;fIp|֛cdz'zR޲L[T#5"'ݪ,I4.ӏPLL֤}˜ejYޛg46zm`)m% su;nHdP(#nS_ PuLQ!ST0ϱL\RÈ FbQDE7;Yn ̵W^~L&յ~E$ EdMM+ĹlSQYvj~sN|QǏ">~~iQ0lX3=%6EUB%4#ڳ8qI$TLM4i>,a)*g0I\6#c*MZܣ#RO'J΃ț:/p}YNZDXɦzͥ)M#/h4BdU"SH+SdPDJ3<^58okn2f*\бz % XJI0O2f( ryZڀNITZ}izՐy,_6) z ?n0- +;|1GsQ(z1zHrcJ@WDsG2́۬D,NN yE6M2\,0y8s9o禋LLLc3*\0w.xR bVWz,w PЮ{Mv5$G{B(荆=u9Vn<::tp ^aƺ;sXlYx4pY$E=˭;MxBeӶHKc1sJ+ w ][Sn8sF?!gۣ^]]@rBo?Iќ W<3+S%/F n0'rz!wԪ.YA>so:,pi`Mk@.k()p*nǺ)κf΍Z5`mT:KNZX6 ɪݾAQR,M^FHY/Wo!fݴ; p̘r"LxQ(v1< P(]ݖɗUesªdNlM/5. Of*9MR5zɻp=_fp pXpdc_ȏ߼3B$uscg^] A[2QV:pzBh6<-lN#_0дcŖMLuqn޺d1j#$UJ;ЙD+}],fJ8:T]g{LH bW0 &-H3a( :>wE/oguj~x/~e+c ɋk79юx,QDsc禬 , ٸ5ABn7ɒ#B&KlaS *|_pgs(_v{dQ5kg*ܪ5Y%u Qqmq,h5}ZM;Q(ž V>LAAT*,kf͘&gZA'@-|yx~`c_tIMhy%31?vE8˳Qw6ITT;>'LE_m^{Ͼ|klXq%TU3氜JkapԵ0 s=~w{;8ܳs:nu,Ed}#n+.V}sQsw~=EyFuYx\`ܬ54IabQN-z݀^7@ ELvo滍ueBΰh{`'IZ ]]B֤ AhzR,{@uhxnbciPVS܋(5}nTjZxW?򵥍8=g,cNO"Ek*/{GL/AjK*C!@oX! b\Bƣ4s&[;޽SYTEpdQdz`AkZKȫQV"#~C Ƒ>ixh3=@ !IPLޤ! 6+r dҲS#X{=ھl&;t}ڮGnNd 3 l}AhBMVU. ̵}n^F5^a\{/c ćHOBE槁D,N!碨˨7XIb*G ;~#Y\rbg:|{JMUIV]n7Z{z/"*<⶛(4͆G&i'ߍ.tOr$3#cf~1Y̜؞0Y:ۦlZuv7 k6>C3_mPR_#-cys,KLdl.U؛ prP`hry}QURykX"g+W5EU½;82fQ9v|EW]< LoF磤C4 ޞjҝ6\qс´;ݾ)Caj9{4C;ֶ?qa24;ń_RdM[:Ngy˝-6^8݃P$fp[bv5$ 8wk}TH#!k}jKzD"iaMAcG9G eI/Q[~>]QZJ =;=1ٜ]?tX -DW{t;AaI@^QB,J`safB$ ★Nu:AC7xZs ZG!U޺3]Bl6G6Eԋl6}%?ϗS_ONujaqnj9&_tIS7Ŷ[&+*IizJ] ^1Ct[e䌇HQ]LY]m_]X_8Ͽ{6AJ$4mQ0 n[{;7݉2qVF0N@$lLFhG.Hؚ? ㈸  r%|Y4U,CCGUb/3̞4|AŔ shAԻ?[ 3d6fsm,c˦(yfn-'|c#_W:cycNc5mQWݾhWZJWPSWZ\QN;p{aۉķ_<\2sO31>o*x hv2hԷ5[Uͩ#yE(BE\_\NKdf3ԏ{%GF.͏y <ô8>d9K L4^b~Z9$i41 %46}!Q#h#b+O4'>$?EyySڙ OM0Քã⵵9^Td7{u|GuЩE,K}Y^?lXm94}U(O#?F\p 3g!՝oysV}W,I\9-=*UBj=|?ܤϥ>V2ˡ}7֏%m?W;uNr)o|LWw$I泼|v(N9}9BiHsH4K̷Oc>o9{0Y8K޿L;ݾiQI޿g+{K]LO,րڦ d2*gU tEŏʾ&obgX 67a7@R{ "j17(i|-YβcZ!;PR x۩ YvX6 mn͵- }PDdYMIOcrfn3ClƀLf˰vs?ҙ)ǷZ$R $+S;6!H@ag/KnjITdrRSڿe!zFMic)E*0 &=”yX1F#=,]^;cԳǻIħ^y+*៽M㎴[= CUx\B!rJ;$_ B-L|F6c0kEZ-S>cN#ٷ( cnGpzLg9Y=us}0mgw+l %ӢG̏Dg!8^me7HЅx=s_ߋjKx)-:P⽑q/Xwz}ѵ I~}(\~zR(<ĹM5d.>}C7&BW|9%Nҝ^ ZP93HƜ,cA?,l,t4TdI& u"ڍzHVȊ y8攊4,!pΐϾx|"ch'6Pdsӓrv6Dާ'J~g07 CU8WTg%d_HLlڻ+;ZI~Z{-Hum/@(OZx{QHg?{fCwT72esxϦ~oajt` -:>0}n]C[Ay}`HzTkBeyba?{hduqǏ"GM#~?)L[I(k.͆G.S((#f*:2󸁙몊,I}=La߬w0UUJڡ@%,mxcҶ4vOZU Qb?ZuͮzkHFm :q3ܘq94 E1Ga*eKR02kZ=Fv=M.o'Ϋ:=, uEnA"GG[ѹY{Mq76lLP[Xl$lVP2F}B P\o/7鹘kQAul)e 2duή0,u`˟/ƿ\FkO3ܞPhȨKØ}$b ŒW>2F_nsX5UU=ŋϝO-O|N\>K,-קz4^vyMGqmv'(̓]-`Vtf}HzXUL&ݏ"ھn9}3YcG9T J w>Qgv2eh7Z˗ &^>HYYN.Ͷ[][ 3qFZscAjLLdS뭍II$J9r6G&<?Gq&٧-& uq$W7S}nfjs"7>*ŨؗpTzHHU(>)&n+%8k{D'x%~9UO! v%<ӀN g@iEI|&1k=̦dBd͵Y2.fh&N{3<-h-&p,Xdrvu#K<p#I"tߛ(X>(<tR̅Y$\ǥ<5I<1''?7<ˍa_+I/%/gUQOvv\zX뷇|U ?Ek{sOcSwopw*eCldDL;piϰP7J! mJ&bzY}9d2C˅ ]#K pue E$IdZت:O%r%}A cL3xSm#/s\]V^KNc<)pչW2_ӯ:'=snpx"@1цOx%!q]njj<…{xEk?sJ9e)qQ$ns>S42% " ~oErZX b7Y^ڼG9DH3<#A(9&iK\(lDVQe qsrx`)1cNGV?_:'3$C0"@"5CxG#=a}-$ vZ &s?WSC"9d.da2FMAˎ@[ ,,tR9b禋XL1]nqYS穳SǰiܾQ7U l2xz W7r V =7o7s}?1_~?;C-zv]D+3AVKFv Yu+NT)gsR$cIG3TV LroH4RGRվcbtᑯg̘0Ca;=;ᅯzjG@ DWQx@o\^0j ϿxXK+y~k/gX]]5WU/~)*,]Vx= #^Ar_nz|3ZH6= O+_{箞%1Z,I + Sf/b"P76͆Jo{V] WrF5drW'L2(}̽`,?oL(RцOpG?pSuk'k׏b⋟_ꋼrEXޏHDr%?GI>JV Ѷ0FЮ3n f(l, !x^QS<$C_cA'sw6X[#2v(O>o_$_U. 5:vUI&{Fk\ߏXZBKg³G-iwk_PԾd+%/Nߝq>yS_y77~뻋CY44z|qֻX^ml;躆FۥIPUґ$ Y$ EQ0tаLLF"N,c c\קq 0B "?(1dU{}dOEey{[%rَƹMt{m?#,}VUڻIڽ>d+QXW^>5scA{ {Ǽ\M~tlk\$5t~dU0DeE!1_x& 0h4:ͯRo  Lb0 .Bb y?|p&B} aʹ>ӑV3 vYER$ľ^7);=6-&C_Ә11M߿H=x7vxDut}'(ʉB!oS\0$ ] Yj;C^kujL;׷zeGyfQhA$~(PdI"uh:t$YžP绻^Ȼ7<{k3 ƅΏOIG})Y[ipgc% {}Vs2W:Oe>L2{`{qA7bޙ.8[i;*71G^Wki̘a>oJ(Q/ywk׉ug]][{mW\}ݧU}.}T\Tݤ5B;*i4GcGLUyŏԗ_'lKoMUBo H}Di2#vk)r3 Q|(vCpknH4f0Ƃ>f sШ jslK.S[($KD.Q_[p+L;Ov?@K IDAT Pgz]ZƌXǤRupuc>h[IeTHjȦq>3wؓ 5@*Ƽ>go,"{{ҋ¾fQrƸ08fQ  m!CS3E]Wuojð@U>ʓۿünWa%a|='vRFJ=Hi鏎Ш7f0֯ ۿ~dƻt;]!j]G4]Ex5B87;T X&t ^-8u^e/d A4ggv ZVE (%BB>>89;C`q1ϷoK UXرG"r5g>ym"=S}{aL&:}kZ_U~p.+䵰1FI߉AEB!ͧcq:>vMŬ6Y_m"I/fLLdzS?7T!}S[hkpga^hUo\|J'1BD*2V8^pmQG{ λ}yW-Y~-K,ꅰ! 9TB4ZߘPC.e{PBX%cdY;nyvuzf~=9}Oz) pɝ3,{|m| )Fnh@&U@&U[A8̼-ȱ u$tr(2}ݸY$4`:~tI>T;,Jpv&$@ :ozӴP*SbpΛȤ7$X]|+o2~m;[iXx'pcg+ݰ-Hߤ87&Q<л[v,}t;*vKJx8wWJܞAZ;D7~JIon'u; Ib\|d`'[ sӮs:3#]dvAFJ@!0!3!5-&28;8-}W{!CnHO!nkU#4s8Bl"8f☈mso^P ;뙌E P(fL `NFk8b+ HM0-yE-}?[\n؇,K\3sP~KJ%W}i%1+gf\&PhLshV4n0\(hMcMS zS `uzmܛ HO9X`]l US0=T6nbN ކ^i$w{{ډ_1`@"K0s6,m\GX:T2!A6jev?xӲ4^!j~x-(3h :q {Hb*p$-"Pi*,Q) Ee}y(*'ڹSI8D(+gD㱙>T5! :͂^)ZՈiFީQ At )a#nbw;>s+]D K^aDBc[㺜%@e10Ȩ m4PJc4S^N N tȨ/wLJ(˽;p5Hfs-`%0b'iXy6 R&-TM9SU;sw}yF3a,͇ q(# 3f:vɌh8`/s-#nIMR/ nqzk9aalF=`YrظM$$ce .=fr:#=$ +AA7󆒵Hrv]t)&W {Vo@0PrIK08G8b~jµNu\EЛ.W) G3iDDhnz8At/B8`i>3 a!,av.ٹ8ރĩviB!;8ޗ$ x:"3 j*%0Jue" x4D{CMsQsқ{"6nfq3b_ N$cc ƀXV,,,M[Y(q$v{{zE$I[ܡscXO1`7 =P )$ᾥYd%ˇL+DK\= _Za~&t]"c~i=ĝV } b^OT[_e;=z\A&t8Ib4{orV{ }z14iM0bqa {El`V .Gy<.^HSN$c|Al W, w 7Io+^-CIdsp.0==`{1?K.!쾱q2!A?F0ưE|$׼ Z@ҹi,5ą>q37 08xmv@l"KƢz;G~.2%ѡg썊 X^^f?0 OHЏ1Cuτ\uחĝ6Ri~?3sgǵ̻-nl yMc~owiu\k9*PP^ r@~Bhv랮yM\;EqY8D} 8Fbz^~u z=] YF}ܥ}fz@{\D@cXXX‚kI 6yf-ڍ&1#,ۉu'5+:8$1s+?N( ?w^+q^r!~ԠzBtbu[y_N~$0cqrS5Ұz.!~T*GXj>}q4V(wb\"n^%5h>.3\P!A?eqk/9` wiӸzmLFX&Bar#T;8aR 5 }Phqr|-1rÄ5_}/||D$oMc:C8L5?,+qP$rCA,wwrE_vv-,#42zV/A'[J-tCx=]M@NRuOD'$1nq5 B7ͭ;X38v;5"цa+s׽wC{}oCsa4EF@Sʖ|sfG!:X (&n!A'Z;=_xHn`+I P45M{۵;EcOLb!1D$xpR揎w׾j303?}%j/Z%COjkF7Mde*: AMD$pS>bgVkSK$ A'Z'~g&,n2b)I "!h,rYzsAU|")5DlݸJd ,[D06 JwSΗ)Ͳ %h8Duy3FYxmX us_!~ A'Zx#b;o() L z@>]c *!  '7Xӱ>'hHLO"1=BEU.mq =һunZJۦus1:Mu;bYJM&A0M%O1|H W4^U.>XZ?B3&)d,I * ɵBgfiڡ k~DDbڛBxsôp{/0w[L~1D0.ZOuZ`oJ]BN"2~_*o&Fм-rL,C4PJЋ;`*E IMF(@ 5>Kl@b ӱ [''1Bg5,0L/~w|'Kn*@UDJGW~yu%h>gOD'7,[ঀ0X@vD "!~&,]1QJ(gLpK870Q.ԀP4@Hs l{ ^p ph/$8=GOאt8@l7oK:_DEwi30h$"`BJ2*r ֶ^:>{:y]J7^0{?k_/$'\z??îίJIQ6;J)9֕hB1DfU{,*y RڄQ n@r\ZPE(T3 l6$03? IqS51m{/cz B2Y1@{UD( RkR@XF,l' 7)j~whHЉ /HmgQ.\- jf!ebfb21^J(M2&%^e&J2$) iFml}7n&7#Z5nv"?",/~AL2!j\LHgQRvP Pp.Ѯo|ԔOj(!A'| $x7Dvb 9Iʼ VuC$PLrݗ1DT{(9 dzU%rld9P)㝝- 3v97EW_n*Ӳ] HMiBXnm 6<ż5CB,K ё]{]qI@N" o2BVep7hZ.-X@$(-0 8!#8Xz/{5^pF@!];*6#jЉO9_k~Hlſ}8.*jȼx`ȩ 9vAtH4_ CpW$~ A'|sv|EAR;9XY z@h^W?Gޗ`!ơ H& RlH(y5Kď4s"0Gr+rƄ2#t,܁p\R4% H\)PP"W2K2HXANN;^ aarKSy[35s_/#IewjnIj% /  ̞ , KM`;8h"1t|+$D|~xl&Ԁ ɠ7[\]SH0 @% 8Pəf2f IDATa\W&7ZXC\[[%u_~}qCJk 2>o#8iF7[-tYI(1ǢyڑꍳHemz=A :51|=MƛBV$̜M`7]4w7caVq֟I@0 W0 ,f뽡D) (![$RI\6bBb 8%׼C//}uVD.uoo8 T>stܲ]*6z'﻾#^!A'z"Aw#$av>| m,v"B1uk/(A  z/gػYi<95ĠTd+ƛ[+_\:e@L :6-\҄uFR(A E Ez8{zA4"LBXwnOk'pnv ]pͿ~}_:6v`5)=h"'p^[K˭#?zZ>'N$@Xgϰ_9 h1@-l^0JZXB0ڛ .al1}RAhҎ%'2QYp;c7":n'@<$_^޳aCkfW7[(ݐdpC ԣ) dj2u)MA:-f-(j.Q(vʢŻcx!ap<4= ln.{&4/0D/PQ. ;E.؆i,%I ,AeY$! eޫyKwԗ%Rʎ˪bd&nxw/&5/x{#ko]<%1̽7& 7  "+l/(ۈ ͳM(]_*ɞ.w8oM)ɶ!aCN )wtco;z @b8úɹ B *4'F8{ɜocWKf_(LZ߳Źwx-ucVMc;]4\n#_l0S#d!`&,dr;sMagOڻvAwؓդa,x۾;xU1XHЉ/ꗰþ!@p䙎g|3rVoIJsPW T õyIp.P((&A ܘ1}h/{.zw/{Ч9l&MT,$&qy|x!14A~oN*Ym73'z+1%b'^@("d9Jje_%洔۷\]Ha*k-[m/<] c hAcwJbC#N_ |ͯ~Ȭ) aȦ+t˄n?GV- CS`L`LpfxƋc z;Mm Tݍd0&603@XjrG)WA1WiXd`]3s*f㽐,K X&a ;E];7DI'~{1HЉR,7_:bgE=mNP1 .5hV 2cPT{涏 ̀!e]}ް)PخrzIjcb#ΘX.4,r'1OPT3gڄaĢw1s1wQRMrȤtV9b0Bw?jgPþ3S?]1*HЉc&>k?/G 0=(guWf2`.A$TB $Ak a3m- j׺.w-jι]BWȘ> b&F;9ulb{f0 [ YJ 4YAP:tm/ >$yC_'}?!!A'F_D]T0F~w-.xsHb`P4@ _V;`U'4GqpL)m}%Y*WO5|2sL4D#GG@arW!RvmO".?sbl A'FUQY0)a~!_G%g4XeSxf$ծI:mW3O@D{+`BAlA;?Iw ?n7`Z`ΟEd_y]A :1r~_Բ߃ ִb9njֺJvȊ-Z' j*BIcgLI3EYcB2zd*WJY w T ru [ cPjYS?3c?t8@(9-!8bf.ap~yzV52\FQ@HA,OVp_;!~ A'7?_nˎ#*ar* dY׭,a ޵38=ݙɐ% vY qͰDwy?*Eml~,;0=*=~]B841(HЉ#X*HÇЊ2Xq aa).BNZaF3cN+n\#A I-"neU\pXB@p! 11(U<$Uo_Dhb<a9i,$1 MQEv%Lc{m _Cavp{zl+$0S2LM1Py=he_mq649* 4}M"I *dU»><{1a"+P;ր HDCk!PZ(d,RVv n$8x?ž[kGBN .֤dLOd2켞Qj?TjA zg˛Gò<6fՆȤŲtԶjGM@40]Q9(7[bYCQ?@tb~cxղC VBHTT*,cʹ-> E{};pݰP,W;!a[>Ȣܡ]nKz6^fTy%vd*nba~泐|'~!A'4k ~dž9kB]јdEB~70*v;/  ۱Q'™ǝ4 Y0t,<(4D yo㎘=mvzo b A'Ɩ/_W~{;57 X <Ci[Y%!+~wd@$AVI12a;91z 3Q<=giiqKpX: Q[$dpf([ɱ]l;ԚEb췞,sA tbr~g7|Py΄m&&Mh$SCnKF/XmK$R,KY0Y Uڊ{b2GnC7̭Eiezx+I<Yhjy;߀tr &szؖLxb`T {%O}b>b˟Żȱ( A'׿KO>R 8n阊xQL1YFzѶڇyB`fU"ܛH>o @0"EU|^l<[56*cmg e94guL|Cʻ>V½ v}^z(b>?>gqr_A86dYe ZVTaB!ۅk[9dǘzaX*Vxx*r5 }Ew 0,p[՟#S)QD1<ެ钲l.3 a:eetrJ-gg81QAN;^g~cFa`EءΨx t$oP#[wrO Zuzar%+) ;&)nn'&e*U reLh[L+\(pu/g0=91.ǒbsJXITј@ՑG1]7xK':?k< Y_]b?sL_A)ײ=l!`MS*"nlB^@&SA:Y]*(g9l{7>; 8ln>o2s=*A%OH,zDN׹;61t i]bnYp3 nH[=b'?g2[-cZ.H+ PRS#'>Mىc :q4W3XhtuefBvNnܹG7,w uvS*beomTN$`@Vi*yd*A )<N薁QuaX0tA 5H(Z9qz!A't-ɮZM&uB\na+=Ǡ@. @* yVt蒮I1( ^dYrn &ĻMarz{Y8e[RAu$Q :A( Y ;%$Ib$@rحSp)`F.}#" k y=$ :A bU, f.uV*egUH t"][@v+c;"NH H b$UN +uILf?2ZA"NANGL_" k#DY`nfl}W!'@Nc@E UKsv9ۻ  :A#*Cě F :A?pkpCNA'qʉ% GH @NA't 8A   N$Aq A'  :AA>?c [¹]<*4Btk,X.Xx @_!> 2' \B\buzvA!$_+c+>W? :1 cG} .W!? cs{@Svom5+nsQ& :1 cD-O\1 HGT(g`[ !6|InbkFN c"d`{B}a].۲DR$-}2.xwIЉapLen A 1u3VWFYdHЉ|a_|6_z,lq̈́κn]%8 Hǐ6B b~TѵA' :qT8!xsB-+IM؞AAtˌ'kpV) H;'OmkB<ۃԉ!B !.8v\S!B#}pNn/8'$=;Ā w]B狋ϑ}3o&7TOßDGņ =1]^D$Ӱu-Gdow"A%G}U}'B>^&8Wnh/A J'8A r#oT\@]N򜏧>4l"@>>[ J'|8吠Nl-8 8:F'cκ=EtbXz|lؐl4n8@> Ab"8^_QD^8 ) :1H)OÂA?R  $׃v gFM'H'N($ǃ#̌%  K  xUGC)_ztߋQw~$N $ch>;d 3~]}" 8#U/M' tb(~jDԅBBQKزϧf@ !6B5*ڷRUkG0i%˨Hv~EKW^-Unh\ ^F%&^+i=OYbX>yqʸdcmpn$йNcfi4=Ѧr>/[ vLHIDAT؟vuq;j !ֻ\2!<^?5I>qan_BuZV~z}mWWHLj.*q1 hc%z~tzO:!/vT=q68`tJ 8W5gu[U󌱗qwψ_;6QsϯUE%fezqYM7. ǵZM.JO7#+Me#Ջ lk\\a~w;ɝA_“E)>*g{Fk"ۘ`Cö.u=V$F{\㒏5V랿v4=~a M6Ъ~;}O!v|{(%tk:[?hDzk]oޠNF{)zak]6=/]Z{;<ώq<+94^ {aQ ա)v]h>_K㡣h1 xC\bk^:1s !Vz]96_`Uvf`]?IlO"Xhn3ս >]uټ "1b`t>:a0ŽyN6^oLDSӘq6s=K cph*DePPtѸ_|{;-7AAekLj 2b^m3;,Q~Yw}4}Tp_~N ~ /򬣲T[W0J5,cc_ D64K1V BZ[2:[̓91K]}{|cuOfaސ2 ~ʼnUŽVTVzqø b<~/s A?T[uWϓOIRl3a'lx=bѴkݤ*k$DUP/_8Ӹ+6x=c`Aƈ#Fw';56z1"H ]FkɵvC¨fg04:Ev]ANR'Y+ߋp 8xYГodq U> Ac!N#zKS=AdЮz mSou!.s !A At/zQCĩjhq Xx)KkA>\B-GP:V-e J^ ,i+u)Tu AW]JW :ծtځ娎̌r ANWG"w1b㱡\x8SW{ N<=F |P*X8YĩiO&cHǏa{H}&C@0SȚcOKP}L^xl}ky<:\8uxJ 1dH5]s3:uH~ !AOt 8oa17!f㱖 M}(TuL!AOVơkFaux|z&CD&R xτAB6fSOB͓q4@ z>c^:$ Yw~V8/c{v6:l u&Ca&/kIPcHǛգҔ$SQ :tNHQuP@><D rN<W FG ^ haC>#:choXCסW]m u|p sؤPxclmwgZ0X!~aL8>&R@>lQ.x1ve','S| <;&^õQD;Sevu`^<ݡ_kuxCPP3qPrd3^NLqpqbֿG`z=d}ZvsB~?(@qf#ȜchKcOp6a]i[E /խgLzq.zYkm7<)m.nS}7z clnG|-̢|s. F.$&׭i|1f;.c-%g cy)ks cb7mm@>pmGB peU%Z]q.72ƺYO?!6c/q25naet6&ܧ%E 4 xaKp'FP e'N$A%!gB~N7˰+` z5k\yB^(sk$u~⠤1_m\=$8LUk]~~ \W7Gv0q`mvq } B3>ɽ^d,Zݧ$SRq U}B:qlp)[T?Kh*[,w8 E]xbIb~2!A'|kGuq )\ıkB 'JmP ,tPoaP2AC :1TcN g%nXF-GScɦNCV q\`]{=K4/q&|=Y .AXcWޖ9`ٯ픪}/N8dC ׮Ή~a7jGFh݌nG>>q!A'?*]q q_碹zQ1o(E\P`u`ut׵t5qcn!z߻4aVYİp-u:'N Ө9YtbXwx|A'_R !V>"8wx9T4f'tbXx yk^B<"s mNźMO :1^n|TJDv|?ѴyaWv>4'a JJ@*$6r`& }^z&k:kɘammig{F͏No~Pfi<\0%p o˫K!iy>p^%>z^r#jlq|]Fagt3Ѷ+ݵÏkU| Jq*SOþW(tm\vQa<y#W34D a(WY7m<6R OJZ()!k)A$ "X{ \y>[h] N3;~|{1Ǹq\5u\.5= T`(u1~ >9/X]XiMX0D!') JIVHὅ6a ǀWBa/@kNb3a$Z mKv8xt4~yV+*_dkliJi̕*[fdh61YE3/JffjН1F3P&Ԙ LUUZjޙn ?y8S).:n;um['JH>ZP'`>D9ZzXD8 HR E}IJj)gTp~!Ccڟր}nJ @'јĦYj FYUE.M]?$>NPD1%@DZhpJ HQ\JH(Ey8nB78ēDJua@= (D*%Hqv&4ѱ~\ٰ *`y @]W}' !j?}1"+D X;)b$#TuUAN?I&q2/'sÓ+uZAknG/QX* $>'z7WVsX@ <g@ ݃ `lP:De u-˓tJ`Ut/*5|vK$Ժ|ΙZ==x6Gx$"FP(i a;@LLh3x =]ʁL$pt  z?&sVWTxDHU Y\ ! !#,* .ɿtuH|FZ}z:%SOC HLE)Sr I ._%Cqq\Էtl*hyp7Q8,սO!^VKuWIzD<({ P u]B >=s׀ "TJy R%> BF | ż3/X1CVrͺ:1s@_pbA"1KasH8k:3mf9@@Nw[O(JpGiAsdp|uZ,+G)_bFOCE B 4fO¤V lV: ԺDSдٱq4@ux+Ir8xXsY#8ec߰>{( CJ B-ɄQF7O\Q~K[d<{I0TY' A,I؞Χ&GP6O&2 ͕ȟZjw?[+ 5%ȡ9g$PW( 3D4 +} MX ;D`V*?g\*(/Jﻏ D\% [D1H hB*RA`1(c/`\_^a Rv *eH ?Hh FY 6Ol!q' q 5!}IUZIZ;MX(A)>z Mx_z}PRNf;P 7R?jBgǩ0@Qjߘ?2zMo?neƧToj?{?E֍| $$D_Tl9#/8%ƃJ40NJR1deד$# ?+Ë? v+'-Tv@BוƓ{R2)8]sⁿ?O"dzp7`|"%꜂}e(bU/mӼR.3 kxS2n(Мq.ʯg(ɚb zn]{ޞÜ;{yOias* `y;^~"\p-`:}%UPRcp=X`y1 !/|q/q\Zp;.&h ~JtteM;GQGwW,9{F-8⦹pg-*V+_g"2h %ه>=Q^ b)U*1=A=o/܅Wg N~v ~w GI*wZcrttvΡ is潁ZH*jUpU/=E ѡV}_w])W}.G^i7sf T|oQ:BI"gF˭J8|&ge~rAȑm>E-}ƿ #u~s, v D:^| h 0$WL^a4\d??? !3jR}>'B.V=5,tow*Bnuk>Q|f+Ŀk.g=H)t%w_6LzL *Ȗ?1aua!*Аe>Z_odQ|ح$n[bk"]moM7-.sط7>3}~ۦ ou⾅XYtycg{ns돳~駵1NtǸu7?yy=~FXHmkBF)3mkTSx][w8L:s3/>gϞ} -˲ݑm(w~ɡ(1tn[OV} EMeq!~UPWyo;kǐ^x j%Ԭp> `/ #X _8]̕X@*LA|M rgʿćp=C&lnL=%Hv=*(=ӧWpDzRr.ټm\Kw+5h2)CFVv%| >}S. gN̴bf8f9Eyyʴa3-Fb)ll|acL<a8;Z fyp 3J,nLhV^4M+BS4o m r\&!}!x)>0meڤtJs^qc\?'uC5@ lf?Kc#jNt6Tse4熣k~'.!acd.u 97TA2hTʠQ4jEh9)%+IPrV@ϥ(uiK(MI_9NO8qjѝ"MBCȺx;Sߤ3td1<`;UhsouKxs}pp6;m2|(,o4K 4B:Cd !MP>MWB yt_#\_1RE+Rks(qYyi0v50r0wA>P]n`Lؚ0[QhFNlE:VMWZWםjvx^ ; K03-6@Z*C/ҽOfs7kpb<`h0r;'9>"nYX|$>NiFiaTi*(jFіrC+jt%i)ף:^6j&V3+RW,J=11+ENJGr@JvY$ FhP'*$$s#o-sK,KOKt:A\)g@SW}:eedgG+%z1ǖΠcVNVbLvLWr<&cD+q[hP6yuL!'Wj4 .Թq/l;jsL}Ga}n"r0f8&1n!G#31\dzOxB89N9DLmS.q[|tܚyCۮ;vG{ i-(z+`2X ՌDрO3\bI^{oQT<޹;&^inLccj#z.Yv3J^;&oL">_~o)"zRyo>鱷/LdiQ <іErۊ#I$Hӂ$g2əHKgd YNb*C!O,%uIo=22><- 4hY e$5]F/)lt%HT^BhMkDZGOH/􎯤~…%+qZZb9a0ۍ, `ȩ34t+~Ġ"G -rلll&|M{&\\?͹LZFZ ) N9N }>Sik^SK#~tھ>lL؟d.@*7K/aK0j[M?D?\ӊrk\;F3[q 8*dEuyԐ-'lB) iڪ:p/|U"j~!6E'ƪyy? ;k¼ 3.]1Fd);ᠡmX.*=Dn&=CG{hw>KZeJ mnic^tZ rX,*ق0VtNRyI~"' <]\XB=+x :{ )- ;d>Q^wv#&Uir0V&'?[AGGt|FDPӥszEY< ޢXF?>zW\<eB`Z4RPr'bIC(;QHK0 O5 0t C7 ][d{e ;7ܰsΕY,y;|]9;{] ;7ܰ7yWݸw C7 0t>C|]>Coλn8oo.v] G7p7ym{'\QS}Z;339+abMK[kE *; xK1r@kk3mWMjo] \Ii ^ҳ\0j6{h4.]Hި8+v}Q9_t3}Qqm쑨Cx=zɢEept1h8 h1R{,6I YK(/Y%z2)&J&2} :' S/'A~mwM|P6Д$N gYK;=n@4SNiLJ˵N@~zC-1P7dIiz ۉgZWy qW&z}{h`ƤL[۠ZJo1wQf~;]P:[B3Sf>#+D*V-]y^,߿&_xEG4ujHl^LbogO[Pp)/~ƞebQKO[vXUגRN'hQhC'J!̘#b#]r{G?pw8'6%&چV9M ʏy? jId65\Ј=[H#]=gt?&>J^k(QQkZ֞N-58!DzD-B> ʞN\쉴2 ]yk֣WdQM>[eK^-oiw7𻶘nR?+3/i0w~aI[5UB_WzG_QDz; mD}AA$W q G];ji} ">sxbxZW=Sa_h5֪*6r&iHO.Nc)?16;p/^U.&R' h>CjhҖZ~QˣD*['X$o{hJ2VkI*ГIx]amd[f\5B+P3ZڎJu[ޏ$zA5Y:d=q0$}7B9J)o܍ċDɤ!M ᐽ6dWL(h]Doԡ^yFvO;wd5aG ݕ&!?cK]\=Ok |Hyx-%0h߂INJEZ#? =e6%bƸoS/׶Z&-Yʯ 7Am~29ӎ#:O͍Acm= 4C?͠ϡg2 iHI>etN{u?{hOLX[^$Q+(t 2I[i1W9rGV7u,eYv>$;mko$pN[|NhjI >+Y$ۋ5[ݼ,QNT-{1<+YNI**Sl'yd}> vŞGV2Dr=+Կw%k[MT@(V$;sd9y26Yn#sׂZvs?'C˺}RғMЅ y-~m.)RNPY1f~]b_9TWl㎃OGF$Quxq/6C7UMi3?ɡVOiŠf1esu#4/9gkޏm!YZ&3aC`g)dԎ85%iMvƽxpO.nRzXC\l~;iZumQd-3>fj^jO6ﭨ~ZW|ڦb7]ڙ:owScuOoh]Ozp9 G=t x ,3q=* ;q1;r۟̓ &=MaosZqyxOAOssݓ;0Qh6ްKECw3LYz0q^]_<)Fl &f@QYo57 a?dЧ'c&HhΠzggt)q6S( xS5ye_OX2CL~v:v=rb0wXbeI%GXho<~CU.};FSSJóm4AMLκ;t@ 9퐒%KT!Ax]LG;}jnǎB1T8:Rka y>>ww6dcK5(4=k\ވy|Ec!4}H7lH߰MF~b÷hb 6t4@m.z`3mkBSx]N0Æ WM- 2*5X|DZ=fLc(F? hҶ BLrGpodgd/e>f 9]WA\\Lf[xilϣTs1H QlHاEt(SAZ_Y X)>mkBTx흇WTY9ٙA2%$79HPD%*ID 9a<;{6ͻ{55 9sfsoݪUu@m[ֶmOӆ Çɓl.^OrFm~Zmbb̥:QvlmX~,֒8Ǐ@iqoϻW‚Z}}=2y!02gkmMԮ_˼X+Φ8gsңpyo%av'x1+!2G5l |r~ 7mKX_p .1PU6sJMk333ZkTvm+:,#kbvI\m y3PqӃTGn.im:.&kbof낤9B aǡ0<_7 l70^[ZZRknn`^D1Z LKp szJ3GSjq}pdN 0Gn t9=(X2~?ALvkw&ч9 VJ37t nkp;40G~Itv*.)Gg?,|k===Db⵺B1p2,<*YjBcHNp74+/c`iT^.3No0^{@V"U00syU"=Y( +q0204NL;fl m7J?^|6:: Z>V#jxM^ϟ,smuvй&H.B\} ^2g4OSyLC d [A9$ 6˻k_4TWW-^;Nk=`OtjFY}wѾ\%==7qoDi0*ÝѹQkȼh pFB?={VNU8^i? 5>Pq/o}4sT;m~ΎՈ&V~Ѹ)VwJduBnR$dw^W7^c2pV?LkMMMk 񚐦&]k.KZCu7~G~Y;F~j[AOuhZ9Q&Pj NҢpnuk-%plHxT=xS)c3B^ޯ_D)dB_s9>ӋOhmIݻPR$7^o^A|=o";d_ű#Du NI3GavZoVVrz;xxMYǜ真z֞G=$LЧ_G!]OsyEK+SClm~Iڵkk׎azn.ïDk|Yx~zF)1߾dY{8 xs|?x!6w87SOÞga߫-//=^k(KkBzrOCC'4ps2\,,Az2=c3^yQ3}P~') z?sZI>9uww;^'`0]^AG=a bu76DZg:QvT]q+w/tdO@eGŔ`|:J3F` %;%Quɧc#J'_'woǏ5VϢ@6=Z=^åh+$Oqjp?3ràⲋL^s)r: {iC#H=fKuA#j w%2L622xmT9s/`=u*wbcz:N&{t2g6Or`6Gu`OĚ?ko޼Ygv{+ 5-seW w.W]=~x=߿KS!k,2=eN:Aj|d1x(6-H+#z^Uq_^˛2^#^:R~ e_b9xv޾v}ꠅ̞#ԕy\mU2J〺RZccl"s:ZUf9seq=ISNc~v'^Zk6pq?x6꽠=;A[x֭[.*T-9"4(9\oQsX MFV/Ne܃Aˍ㵻wuuu9^k-+SM*ޅ.πs'$\`!{&q2;B@B`(ܛivHt*F_~rgyB}w<0@c|zoQY4sq6SǚkE쯼Jr^;YO9:>^;;+բ!..233+w㵱Zxv_WO.Sp9K)b}ɭ뭼q>}W?G`{E{vX5*f_y:(^{a?**J8EGe;~ rtV=mm}e[kCBdc|u  4ʟm'af"q L {W"!-535ŹQbpR%K|y2l 5%sZ󉟍3ޜ﮾j>\[Wk0Qhځz?ko"\f.4sp6pdC?. Pƻ0/9:0־ԟBDgt<vRk.z}}vn~ \`>~aK!'hp|g(^#Ȓ? g xOa= "ڍÝ"#%#`HO޵̘;_O|O qY/v-,H_ Gv1p4{Ww {T~޼w%dvdIz~6˝?++#8kQ$R"!'+ k13B1YȤ^ +5%Ѿ#yv'|䈷>jrc/nҔ%}.qoCZa'Ԟc|g (W+NA dAQN8dPAx(y-?'.uX='~;n燊J9Us9FeKRXcWvW=s_OX"}bC^?j-^V  )^Q_^۠9<φi*X <ۙ ԥ5l>m\v}=~C"};DP bGa e➗%$^l}{38cTvN.5Lo 1$Doi8ZtzAcL=Q5}8/ggAia,6ҟZȆqΉxssbO%t~l}E+se1{c}nk8H Ȝs5Yj=ǻҺu0WDBgK̝Z9N,[T9@B3t[cO<#t_G?AlVP m/(yu\ ka;+U|$|FNω_}1J}~XD5[1AM87s CPY߼aU9dW8G^Pyz|Hx3?{2+X)v{} lzk)s9'r 1xN;'W[Q8F̔/;`!X>2J]D|| CI{_?K 1D1L[8Ixa~-ȳZNL]Mκ[{KjĪ(/>!>ns"X5@ =_!hTְfo,tF5@k5)_OۑsZR@?X}>nl=ωzD/iJIk)^=LO1uтdr:Ggl6s" ;'ek{pi1a2!QYZ%')WH=[ۓ=]Yq>)t^B9N8pgiCOg~s)=k|z\gB|!>MT}_{oelɟN?s5?IW8CqgO ]pX.p7HK?D s;멅z+=˙_rU˺!6&3a5e+fP'}KU$y;@x~Hw+nWwƇ;k '_y >b =F6-֣-w4|v;i/tMG7_}?5LVw 8'`VZˉzB~]{(#EFS6ޏfD~>Δb WkpnEK ,V7/r'`siE4TϹ=8}~™JX+Z+t(z2&[lH W1[Msn:TGbğ{~G?U{ ^m7L%>Ss0{~~r7HX> 3]=X{j=}?gY=/|dv8SS,{MnzZs=sb /TWWZj쟻1Yow ȶ]Ocf]нΜā>^zֻC@36Z5$Չ{l=-˟ٿh/ COMRC+vwVp>=1u^8%Nt[Hm iNmr7HMT?^o){FZS gs9Ľo'b }ϔoIOi CoT`Ϧٯg ,-rv'H`g\!~46å7S9m-EOV4܎hh)oB g٪hiʮFz`l fDq< zWmo?+ya Q3s{PvvZe b&E\ M ԘzBrSY!O(lay6KߌN}p$ 1V_B< Eg~s ,~{_w@T{ nLBCU<#TxoMdӥ#r~"tQ%ptݯ7P}+F4?@CQ<='5wzroܟQ~3"N5f9;/9I{糳q^;g}H:>>'Seg3}f}a G~k-v/O-A"w9/Sã\s?'^YgĖ8zNlZJ}`)FMsN^3IE$wT7zz~ f̌o!Ftg؋`K=N *ezxۢO $x{Q_Ž9|l_z̝>{Z>es@XuzT"z5,?w1QYlu>3]qpaO{{Ǘ{Il1=XV]_PD50e#_/Fċ"ovTe f)Dϯk{~D+4˟-7P q!&q?{7I\gR ~؀n9 Dǖ#t_NrOݠK}~$g}-fαz?,7Sȿ(y|6KP~:p;_Š};Y7Q\+f(7 WU6#T_ q-J 2{aHuj/bzչ/8#h=6ԥl@\aV<2z4}l=HX3|@vl?Iyڼ5rAYIp{R`4 n!>fVb޸oe ?}J8DYs$i c{$.&gnzHz`jc*V1~R{lade޻ߘ*J\J evxg7D),Fg@#<gTw/y߻ 2!*ܬw>Ӡ=4}ЧoVH~,AgֆpyΕ{K^#Ms?;7Mg ʌ5 V|3֓b3MpS< >bV珺җ3b>\ͽ ~&4!?0u`/(Yjm0_o''^8Vh :KstL9li»ߵhOw?7n/ܑ"?vyN|k£P ַJBM`;»n&;i#`z@wW}ΪY*zuE >W]Fv@MJ }q{}k@OΌ.1gDD гO )5q^|}[A{!!"?FY\ˌk@dσ `4̚9DR^gF^4k|;udH}_ 3k Z]"mRŒ! )Y7?H}Z9)V4WW%fF?<-&{bׁ+t IJSn4C2"j'iJ+ |_y^qP1eÀ]َ-.ls_j0_Ns_VbCXo>0fl gznCőD8{tD<ecRl,Lk6P}Rr\v7손 cοD5`W"|gbs.~oGH!ᛁ?[xsl]̨s"*y?TTE?x*kp&c"v5P,gQ/!tأpgȘ-$dzǂ;ݛɌ_Sp43,1Mw\ցYKLͅ\{aM(\ Ԉ]S}%2sCS ="xf`/g)cbzmf:xJgZEQ֪ 8FـQro[XǬ/_'(?3Q-ʡ9b8[ҵ7u/ |]0 7l6J!MuXoK\E؀rL·'GՌECPuJ*Mk p#k@jT"4cE?l nW;Rҕy`)o/v^i_nfa8V=}2kgJ,fe eR>5/)WIgT= ]Nك ?$µkQT0BlQJԇ򷱔uvgJu' grƬ9OMq3'uYx.0s&L91_jlmFl+?Yo#7M_9t(Ǹ пG_gE3+CyτSɴ=C!+%tA@.wO_QsIgIπ6kmez0)_/(0G|4YA8/׎*NRNF.Da+&R?s+]tb  3嘸{ ) 22H^$jҒ1kZS~R`X33/Z9%UK[lstM8kB7;lw(IAdckY-bf#{JV-]0v77mײuP|џ({d?=/?I&j53n1o*ZT>ew#,WK 8Il_cs+E׼NN@фmO5?n+X^ᚐ?;63d{(:@3.93"vM)#~F|8z sP>]`ώ$Q~,ң;t ^:` c.^tV!g |Pw&F./4Xw. 2? ѽƐo Qt\W-("GZ5 ؏5HU&r_GV'mkBT!x}Uw鵶e13%uleY`ٲZ3@&&$9׹;\dfKVVUNY[z7 ObL I{ocj?+y0>MNaMFx+nI[|B詏`uG>$IDy -<"jPZQLg پp;vV?:|<*9Řf0CXh{ u_Zl8 oip&ɂ[e,"8\IS.,.$9z^ WaVp5Y’\L8hS|rQ_v ˌ<̘)JM44|nqYȟ஽l]D\jSx*?3yH)daRlSZȆ@1k4ՎF nW:<|_ŰLzaP5[Pˢ͝@h: 0M"݆̆h}7- A6 !1^L-_|zH:áy/j | 7qlu>_E2`MY|'}>etf/3x<i:@^( M4c|c}&3cC%zx2Xu{"}? vVT2rp6`=ړw^GAfD:sz;AӵQ{)7w18;Miq(2B2c TǑ=*$?ꀹ2oH\s#E}-Ǽ-A&*rǣtb4YcR\1c #3KǣFǓ*D>WGA;\Y#m+UGAvT܌}=/lʠaN{+-Y11 wNx1U=<72dq_ aFّ~*T7)\,ФSe~"|9/wq;WXtvCv!<6s ?fNce/ =d<&5p+ʀ{Uɶ*Y?{}xmЇ`J.X*{+po֘,hC΀̿a+ L$9 %9R+Wh`eۄ-z"}9.V?y|b="R:Q&Va` 6kaL /c^PNr25 n#;g|RZ-":/g`D>1'\CO{Ko7gD߱/xhq\& lEb,cH:= tug ,d`E #ߒ 3:9KP Uk&/F"2g:1n$֪sx?(ڌEy0$t}[{ϛ5-Yۑw'-@̴ P8Spc& ,"дږ wïbυnSb]eF_L"3һ r9Lj&uuBcG3`q?ڏb3<$#2%2 FX*32Q9ui^rg5|z[~Η+N tn଑eeX-11 #7&b1D'#Ax:Oe$_HFh ^@@I't&*Ő s }+!+d=I6ZHgDd&T?́P('?!A/"b<%#sx}'?삊@+wdWWkQǫoKsM^ڀ2qb[0u7أ1|<ߟmP(k#>6A6eZ(3,NT[-cm| =v}#Rƣ>m,b/ecwO!^ڶBcL9k\"66b= Xsc?OVV)gB?؃I&p̜! &9A5;`m[\emm0OCM2*y31`;K`{ǝ\ uO>ag>)o)x ՎWQ0M?fk+裮R1y$xzQü oI+eyv"Ф3$Ϙ2 >T̹pS VQC֌X3S.|Ȗ7c끈q`B/lLAoȚr<x. hUw܋yçz0ƻ<֢leh2=r&Yo<#FOG=g 4v9U%/~ZW0X50CxrB1Eb ,?kÞߐ!欢a/\R0G#"ﺃouh }W͖/ưb;l ?uT9k+'_ Zk h1ل+6/Ę5Yp>|FGy?gF}+_ ?_nFx#Up~Z)aMoh-.#o(X `g4|l9r:7_tߒ #{e0{W~8gh~!`|ZV1 {X*Z#Y\{Aάo/csNek>Bs5OydX} _@/&ՙ=1>DKͫWac۴&|"|]ivetuC'OB/`]82LjV#eP<}CVT0JbX[d-ěfl tB0Y>\*{jt.B ;Xtx1CB>{\IaPO=r57(z&N?5 /~ |_ae!br1 kr!XֶX 6kde~ol;BX?Bc={ s?RALFGtMelGCZ>/8KcRӮlZ\j l2^D9h8׌ij'YnΓ,9=b\#` Zf 8F? 3?m))I}H KHVu.~| _ox+uW\˗/AAvCqJ#(nTh4CW.;ۙ =ݞ]\e)>^+?+2SG0{]dw 9(3|j{ax >XZef!h`rO Oth4aaa:V2GmT.r!d)< \Cgc r NV;R:'5 >G!0ESX-= 𰧀a' ?Q'Y)@qi'8Cl>Z 9 "HKTXmpw @?h[埫_j A֓/4]f\D@(Յ) Wݲi9ĆA%T|gde ׺v@K%%7[I&2-Gtߌ}#^_^R1D0N\}|>:`D@p̭//CNkr#I'Xod@ut0e1;_GnV wVZߤB6~XwBb#G"dfJ#L#0?#92?l=k } Y@- m9cG UJ_~ Cmy/γ 3P#qe -a>("1A}!z@({ eQ6l*}v{l<7s%#Og 3sFTĀH_as_;5R\Й?L?ng p?Oy5sZx~(ΈP(žz7 |Ú?μ2RٿV[VdX&`ߥbbOEq뿕RS]nc }3&'3@iƞs P|fs\h{7T͐<3/f=Q,m;~O\-񏻫h?39T' ^ :-g9YQx9hpGqYgh^xQ?m=Y=ڒg?㾸o=91B8ڦ_!OyKC@N竛?KgHfUy9S?)k9#l}cbu`?-&?AT4Zj;.3<%3{V+m@F}4|sNl˜M;w;?A?)įi{,e村{,ɳyq|W%6kV(Lw9)V5 C?}\/(R^/-5S;9䔅I@)WͲIsB&BaN8;4?^ Zg'ǒv O 8YRk@l>c܏8#U|op9?inn"P2;on4 ]<,0hg?YNOjlӝoI ]6nP{Ahflǒ| pd}]B/<ؘ†b ^ [hg4cCk*?nn>2!q8wMc##>e瀑? ;N]cd.5Ϥs~-_8n=VE륚i;#zE8PQ'~H cq `! uPϿ-F c}DaUxL 0s]ƽgc~Ol.IM}uJ* ܫe p?B#-B"u? |[#9_'c~/@Q$zZV ¸@B>Ԭ>О}X#p/xoklD\tnH#5׀.J ?`>t>x[p:ƁʿÙ]?5eTX,25`n^ ~Yǯ^ lݭ`k=wJA.@4'r6-{I_Tnv!+hܺ=s[B.Ac' -ş_z#?q@iAϷLc?r@n>~'H;9?aId|#Ooku?惍ƽ"y,?#g'Jo ֽP)s.-f>6Un)]|~68 ”!~?s^o̳d1XǽyRSD=9b8Z]l=-7kP^L<t>l@kC 02jan~s/Hr3}h轭#G$ݟjUqx`+^`?9F\mj+(?{\b4Yo=xP@?뉷UǾmj;ٽ\?Rߎ5`Q ԔD?}26Ca R;Lo!yJ3]=6rs?+\åK .ϛ2lO10#ݑb|=1C-5Y#>6$#b]0??>W>&*>:ノ6|$$ I<$O /ΕJ/ 8Ӝ=Cz?DS :Prxg>\׿o~3Z:$(p/7P%kcO8Buk1ƿ,w>0b= y1 =1WWcc=cJH(Qs5߆؏'r @[oAٻ=C䬹%|!}%u~wDݳ[$]G`?!b9֙F|qOc]Ze7jN_ UܳL><.Osu^ 9%}~<ӱـ'>磄;_WG&roΜT ` ~an; u0sahz?!:>|3t_S·$a_X>WKo}?7}GΦ'i?e< 2!RǢ!i"ӡGD=cuP;_??j+m&{mkBTE8xk0@P HJX HJHX H$Af/\lϏȽ#н^p.yB^Vm7 W]8<浫/ӼSiu9q<ޢqkV9W—R?= ]aKxoܹo 1r%C5&S<}Z;֐mT[rZ}*ȳ%~ddr-ss,ֵν7"[C%,oTbۭko}}{Dov{;G~KSܖxV>ܿ)\kY{9y*Vǿ\`*D^֨sbN/[ש#S3MBȧ7_m?iȥijo=ӖxG uv/hnka[E=2V5߇ym?M#2j~\⿮9@Q࿝=w_{~!^ ZV|'4ޔ+E{x؁99pꖿߖLf!^9o5{yۙy?PP<"޳_V-mlߺ'5<ʲctqoޱ@[\VB1Kƻb\Gg#ſ'S!S$^x3/ݬk{u>P#{s=v8v w}:hDz5Ўs¾ %0vKmkBTK#x[u0E !  !! !a BdUg֍GI8[mmn[雦9NᛛW;w.w>hoY{uD[xKKMN߂vp$ow;5wbCÜq/4}98ǎ6G.*XEGXig δ*kɷ RUh%&ro-*ߘTߣoҿ9??N?5WU9`ҿ}ҿf' >/Y:~)ϵySno L491kCrk-[_5|5?uB&a~xm}Fǣ퓟gٯ]Kuu&Fq{\_ȵ9?7zv?GH< 9;}`_o?޵8֙˺'pӽ@(9~,vi'}?=l#m6u|~¿c?ߺo @9<"9r0e 6S1,OZ!\S6+m@kզm W#y19#ߗ3Wm.Ld?YS^m??-V|=H.yzuFr镹\V rюNn4kC:8C d#gӊ칎F[+>밁|7jDj旣d41_ϰ #'/o%ھ6dh׆a9=6ӽ~'6D>hKb"#;s[㱕 HLs%c),,Gh|8k<`?doV_k<`_k<`{r='{wrA^Yùy/k^_sk>|9W/9;~Jlo߾)*/N\ϱov[iZ_ձaJΝ/:6O- 92b?Tlk%?_21B sY5>:>c=1Ow y^- ڶ,XzusM#גU]>H_yYv!ۉ_mi Rus]Xm_g)YY)m]y,m z1aaaxEߓGקo/Y\k6xjgH|yu.\aæM&wk#ϐ$?]Mo\Ⱦ,/ڥQ@~6s?)}, l gX #vQg Bٙ^uのuhm?}{].~}v_J;xogJY]޳@.)oqC?}>@Xߘ'-(W? źvƔOʙRv[K?[A}?-wmՑ}g\=c}M ggg DŽ-B^k_g?F? v0||؎=ǧHPgs/hؑI t~{n^}ZyD5XWvO)"c0vY Z|~_%/,p\ɹyΰZ/;/xs_9?Pܯ5ݻ\[y|č8gʱL{? 0 0 _k3>z_\S |<)b|7aaaxn.ta?l^Cvkؽ#~e)3<3^kdlc&jK+o"e<.ʞ`^(3zu l+6v<ï k7]/lc[`On}򚄫 G뎱zt^v2)?;Wmr5ocIz?Ozx{&!ez."ѯ 1Gg{+ҏlw<=}GݽFƨ^)zIpG K֜{{e G12ۭqiumf>.}~a? 0 0 [u+7Svq֭y΅ ?ނ}XwŶv?ߩDZۓ-q/?߳=<~#>Fk"qzrQo 9r,nY[;o:)@-`ק-7({߯S@µK9֠ɸ>:n3 _[_*mtcmC>qSL=<6;ǫsaaa{xˌ\ފpx?0׋#5zяc]x^l򼠕(f:~٣^lin59W~\;?vn6erUbS~v^U O7O(|;+SG4|?f*?rW~2oNٟS9~daևmH6mX[J~s.ym4ٶO|Bd/b5ɿyU? 0 0 0 0 0 0 0.P~*1@G\⟿KrKXs2(ߥ纎J8'>X@▼QQbqwx b)_K|v 1M6kee-2Ǜ59?K^E~9ϱQﱮYF8N?~;:=J<-tĒyNAgC \NXKs)'^Kg\~2}6}Գ)n]Or^j~"{p29w6/.z-v:+M{WJYZ굢`% Ҥl9ힶկ#OUz+U?;sd~vND7*.Y+v:ye;8}~|+ÑޅN9}{Bƞ#txխsXɿkSV/uJ=o G<ջL'L:D]6jfgLz/+ؽ[{rCMYq~[{yy czA;w9zszWHVax3 % mkBT]px}[TYS}/YL$Ifr9(A@ 9 "I2sFTLl}gw<ӳ~7`P@a={ww>U{a||5558{-<\?XpRQTT$  o߾~7Ǭ믿 7nhK;''!Kϟ޽{'<wsyvV28deXȖ\~'Ē!q8ODιtD#3-ٙ Buu099)/bS%@fF2T~",nN'={'E?+=Z>|?( NuiX !\=&_~?~Q/? / [?._{>'`R8 :S:0w>c]i^ҀvAX[YHp=M[{6`_0<"X K(Ÿ Aq^Bx,ĦG BJ~Q,T 5xpeɋG?YueOѽ D%9+zg 7z.8x 3#3iBeS>,_<%שbb_?246 A160sӅfX`s`/xD8Åasb=s'{2S} ?g4j~碢wSx{Mr^{̒Q}U~m8o>m8w l'v4n7*UpӄK^ւzJ gC.s͐4M'k6A0 0ى#;znBn 47cњ,u,v nBr^PV%\yQ~SXh݈^ۡ{ 2]D4D|>Rƌy9͑7i(iS7PrmP-*ء=2TsXеt rv3_q\DS0Bl>¿Tn,ly,cFt1a7G.Mshwos>u!N7A857w 'Ǎ:nK&(bk]Fe3@d>ڇvN.Bh<yWkhOΑ\0FdaxсUD{jDŽ.! C(/nݛwObz9#x5/dCH8G:CH6@:C0Ӂ# f+׿ BZ 4&_-{勑>|Tt,C;˼jM!pNxkR㥹Ҽi h~&ߴi%?n nc!>t/qBDzCkE{b`(BeSdOHQLyiF/Au,.5?&np!8.q,zӺ-g Oy)K)&Yx|G]' #堨2ɓ,=o1u`"rcd/au//Ƌ3o| ۠`9Ö f칏BxA3('& Fr9iE K_5#p/׆E.wP@<32 ӧyd!͝ܦ.7O۫?!l:4]f]gqb8+Nԩu%]{^[5r.y,|ז 92GO5HZLLe%kN1*{ XDРުq?i>`?CGV/C+:[ϓ]:D֜Bɇ_ $O1oZ[[6m? `S FkùF U{aW <5rHV%-ӄ}^8UkN_óqڮ?{Ym1a>Y\-ʍ-j\kv¡Y]{>:5FLkS~v߲ZLu.oDcTR=_^khPzV#'6FyFשǹ.ՀQ*' w6`O- {T/a'd]chR̀bbF'li!I~za)GxY߷Wޭa{pnhD5m ;Yov Y*N:VSHFmߋ79tpGXݯ IWrpیΛe3ܶ@w=νJzQ| d^ڼf׶Wj9TOU]O6 vGMPeވV  f SI0$6` %@~]|GUx;Liq˦1M汢pArR\ :O5h˘Uc}0se@ ?mqaۡM֛`>Nӱ6g<ƦclĕDLMt,yL ɸoUvM9i<ϽR ys5TQMJT3WPuCtN+ɔnM/3<=m0 jp- V)ܜ38o汾>OxX쁱`6.avp41<}Lg?KZ#8a!sDT3bGBl Xt ]C~F8h :7@h2ހ=@]/v܊#`L A݋L3H1Hk|pἭ q5az` m0d߿l}7BŊX5M~6Vj3#V),&Y}гIDh0 5o|}臓IH?@d#;-9 C~V8 #PXT%l Z՜Xz !Q^@l!!S!~bS8q/Lf1cS>_uLU`bu4s}S9p.FmE[rpi \{myy|O:pc$]!I Qp:ks-\Y}(IӒx^nIuЊ'/ml~Ͻ5Y词ĭ؎dG9A|:qgmK c0{ ϿnWx~~z nKY} Fpr)UG>@j(mcޥËGF^v,ϋrb|4רּ=gErk8ߖBt<1ޞbk<;k\gVʚy8G*6_ !w/c!:rI+Q/|y5TY>;l—T\eQe9@>=)yDP]Ή>w{xmUl *endj?wmR c1f4&"ʃk% _&s]1]rK?ƍz4!s?{c( Bc4-(LEPol6⟱ZO\-5wqi8-G/OX[YrNozW}\:*UrcͭՅSDz='ٛkjucK")!puq9>b./CǦ{^21H#xأ՝k|f`Fd,qǂ{Y|Kby+X*Oa~&w+e6zO{Yp% q2u}!mm䊒p 3Φ/]d^?'|ko_mOz.qJ׋3077-⢽Py =a kndE̍4&K|Et~V u/t7'\35PO輍!O /&\BWELށ^C>\lWH+uj]cQ\3 d-5\$򞝒wڂjjMꯊruV+tazt]_.4esSl_O@Sߛˏd3E!=O+6w,ggH=_T瑾ә=W)tz?y{:_5%W2>#JIy] IO.SWYB@{0H:#^i37cSXry %ǰz/ ]V zIUOBrU|NNߜoKP iSmf5yܾÛu%uݑ?wݢ]/u+V׃WOו^t]@~u=fm뺒ᴮQ.`-+_ot=~麒xJv]s_R}׸ˊϘ \B#uySf0s]$\(s|`1uzuuy L% =:4*u]:˘:ӛ5H VϰӟW+nHQYg?uO}T?h?/|g{d: { kkt=]q<=BYw939r}߈,x`J+JS." k:ã7Q]uWǔH kH׳Xt݊tJ]_xЅ~麝C(R4bu0tlx:|eu=+=Fs9"Fu6KWAK # t6'OH甐 Ŀ$<cAHI:{,J]_H9 ?oyq07WϚt=DM0t_9f-]דD?7GPq*"]ag+ƿ$:KլEȰ+\ˆ/Jl| 6\y({.#YJWdyV<:bc+l ʳE'}ω\ }PpOh}JypOh}JWrE'U#ؕursO+?[}BAcy^ A]lw~>f!79qL}bc_{_`)}E^+XwMWT.J}_];S>w&Rq& _xO\ue@kycZ_tLw@Pdx&Xy 4F Y"N˲g>qB^%j $آ,CWk):.)uXcд޼ [`祇DUYΥ<謏үe[uhc;v=8j(j(,ԍ6S1l|PA֖Yp"EϨ.{åd/Lbv+4 7]Q(- \[Ư]Vxi #sصNELRh `ᦅc?TcD1f5L9_Oυ4= 3B~ע' 6k{kVHjxGW9[0V{YgWH{VMD?3&g]#mkBTtxA @1/ P0}TAw6z?6mo6mo6mo6mo6mo6mo6mo6mo6m '8hmkBTx'xm* P HH$THH}ʺd5;f:g+WP)RJ0//FSb8zcOemo-x w6h/]}Hf],t2ӏ}.WY& =ZgHbݷԑrm Hetzx]Z fXu i۴@l^we!4윹}ΌS$~ظࡠcy''fR}sׄ?/<6I鬄>Ic1@Ng#[9 #HPKK??|!lZH,׮IPq~uI_rzn"m3rI/=xB6ŀ綏b7>1 K˴n9!!7,nj5V2s|I q/7։ohORJ :?MAV!;Oݿ9JƇNYwuk9-~V6Q=Z(#R>c.}&R9r+uרؿp@(_rϻVBwup$ʞp@ˌxlxWy"LDxlp1x< @-U&ioЉ2+Z;T#ggZ3 u y`]>;\됓rϼ'U>fkY_ \N 0LƟ s=1.Dv&BT쑶IKSXOlM7M9?ϩclTHǸǩauE15vR(PP!meK/JۤמzQ[x`Sm+9>FwH/G;S<jslskTyc}_ ԥkX-X}$mXJ͹BocC7<'9ؾO*| .J(JQJ>s1C$K5}:awZVY˵\ s3k7_~YcmOi_k+?|,׷  U׹Ћwmxɽ]ڧWH=uLпY2+礓w/!q9,:LbiԲWI {?` Rk9c^|;Q T ;3Qs9#/1`v &?3i _sQ?4y Wqc 9g86:w>P}G y+)mkBTx흍) q ĉ8D^>׻gI@XjjgiЃ`0 `0 ?ϟ|:seQ3|ӧO|:2|.};7eGFO6_Qv]T]^ˮg{>pjzkuo{yye?{-x/ D:3D&򈼹e^Hyi#/OGzϪ߯_~ :sMe#M3Y#=2 QЙ[\s=E8}E>GȩT ڲTg-}VfoSVwzV}./>~!?U1<#}=F[ ~QڋBN..+푹^edLo+[\-k dW(}6q$#?z6Bөi?L7!3O_Q}Пuo[=tkȋM!'}/Ƈdr2_Cﲨ: `0 :8o=+8-4}۞cĥXdq{bUq©ήm!ƶg*ΪU\z[GA=^+ru{LV U?)V>ғ)x|Yҁgi\yi^cUo*= !TY?rfgWsʽVn*VX#=Fϫ+[F~yH\L~[O҇h5ݵTow|Sfӟ+);F;:x )/OS yUo2e)Ve3'wgGg=J^`0  ľu kU,Ksؑ5nY,bXw{ w&3QהNQev ]ƷgcH˞i{A3I8hwduwUIWq8I>+@pQşGcZ\ƪUߝ]/:3d;ɫ:gB9R|GW~w2;fzt|+i5nΟgZY|<1NyŬ|E7k?z/k><=Α}N΅>uWydʬdz `0 *\?W8GY:Dgcg< 2+'W6qn؟{ru"wU쏘~c#T?+y{Q,,^qF/Xv8.֩g3}ȸOP ~n%hUG4(_sn|W}Tg&x^c,Fѭ+ <#+}/Uw8BRh_|33!mr\7U9m({ѝpvew[xG]߱?g;,nҽow8]וb?OV=Z_#ve?vN_WrYLo;1g9pV^G~>[_vNOS3 `0Q[ veO\k^8֔v<Zbz\Opbn$~}oz3ј mK vU]^iNWA#x딫jt q :E= z%օq)CcYEqyRG-+u (K\hP'*^ء^q=m=y|Kvūe\rȊ4={W1;=ݷxp;o@>ȘT\Ԏ+C=*ɫ|GJOCW]x1.ﵠ9_Eб Vq)v(ʑ}[GwǺ{-oSdו_˞׃2;iT&w*w:g׭SOsj%Z[~_˯d֮+w]7 `0]kIu+eL]ւoA^;=GR?v쯱;<y o$N1紈=:ߥPVu< <&3KyC/4r)i=*/|Ύ^]QNН1qGw>ù{ ?Kv:A}E:_n+{u=rq͓̳]>>d}+|L01`0 leg:׺񶊝`W,3O?]\9P~[kOWiGc~)-<w.3q}'vuw$Vnv(r52S;Wk_Kϔ8B/hEՠ'9w?K;x:x<|@cϽVyc@ۖSw8Bq]=2lBe6V}eR( VeZT4ade2ޒ+nYBTqSߔ<[&=f[|szP)G}{Zׅ3n7jpWwftEw[ǽ;`l? `0 `0 `{~i`oLy>uoi\qK|}7Svu9G쯿c¾#>,jow{ՆݲL=mW2u_8دjo?kD߱mw>#}E:OۡO;y`$jw mkBTxoǿr8ر8s:;})MPh(`P H@0 b&&q FFg87N|Cry~?yn9'OZJ PR3*U" TCn KAM'Tj*5 nNp5{WǯgO}?Uds&ξY Y kK4`joH6W2=UdԃR F썆Z@]06;vÝG7^ >|1j n1Jca,V|TIptk3j ]`K ߋAp4փ?lH5\9 Vã6?O?~:PV4'_a $h 7̧\zZ`҂٘z\0<{rW ugk/4`KM a]Niry57z`Ce.DЦ"7t>8?.½ `i szMRcs{ͦ^7k{C;"O\ϸU3g APYQq/ Rys,ټ!dz0xc!bZt]Ր+Oj;t`ijjC鋡$ohVޓbf݃Ok<0vm <0| cD4u|6[*GW"o0oH󆹽f_lYͼyöݬ g08r\[T_sSBR=N3yCbwvBpupyBK.q;n(|/Sk` 5%BȘ_ܽaF(cWaзO r7 c/_8ϭ/hk/]k!?M"oy΅ U ސ Z|-|W\DXcV1 oK 쌕uP:M\΢EԞ#:w0of`Dsncof8\0B{&?uS-k`gg_QL]s֕ 1ݽ783Ϭ {ۦ֋4dn`g8eUu (1By ̍,!+߳H|?T`RdcX\x7$ij "U{А&6\Z5L &A :p1G?@ZE}g A8-&83;4jhHʛJ?ʺBbAf9\D:oǼAi*=FK:z`M6h?X[&MCQNHFV3ޡ3s]CUzcTs⾠DŽ}}{6^TzH9Z.ǠJǐ=.KLȱN!Y-fq50/:@4L ư<Ë)fN:7ڠic%,+@`_0-]Bmk]UwvIJ?rI{y,T@%! _4 5jKbfoE4qWM'8W"pneUlJc!3|juw{ BS/pĵ#KRrr@-`_yڀom -LW6pw9?}˂/9,zc^:0 <)y,L ڤ=;@O͏~CI!`]`:t|]zI;v58{*2ܗ^b!'DYNX 6њX}0_X )I\P"9%=T=414{%#d?wz7_lAPLX3x:C/+}LMf՞r.%Y/gμ'}^pl5A-x}pdX=TM{m@+̥Ra塢S#E3^|owܘxwq-Tp Fqt2M6Zcr} d?ߟ i.4Ztx<[д]ӹAYVS-QϤ)x#ǹ.g1/ A>}'Ĵ,HIK~./2}[8SL: 7!]?`ĒvL1_`-q "Gj>^eд G=6MCgRypGkIւ' wLDbu>:"8CFBsX?p/~۲w<}R{ +wڰk*ٟ?1 !=99|er߿or:q77'7-H׃ϓƗ_Sܾ|AE˗w{gT7 s!ιo{n`U.:{=M޺5 e|)'6ݸPW08rϞzZBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAem-ƆymkBT6x횉m0]HI!)$FR?6c>>~sm+vuՑνYu8uN?WP>1JsWiV_uKEϸ/rˆ_gKW]ױEYcl,[TYHT}xL#}A GV7^}>iҞ-i;}LJX&TP3T#ߨgJl e'=?͘ona|7>?ǐU%;/mN/IfQփz{G}?v✽3X~j{zTAO^ʰ>?sy|G)P#,}t+dL-OkwMLgM %?yP:W l7s_ g!xAY'gOCP 9$K Jޥuߓ"LNg??G过7rͯ>8 mZwM' CrOhz~eh{vٽ */o@?V<'@S]4y\/ZtFcZP_{c4CQuORk W@Tt$wot ~9\; uF޽L`Uu_D!fyfa#_O=wwaّ }Kz,337,[ {B!g|~KwoO`$9'TfBQ5^+vI竤O5Bh g1WSto=j~G0c)8qlZ;hWݿa׼ʃ0p3^8rE?aJGjAw(_}N}5¹o=J}q.HJPBB}\#ʠp2^gWׁj >C8{ۂuQ/]CP^ z{~@sܫ[Q󾒾W}5E?ʡ|%@7'c{~B;PgN9Lեi8hGv?48& v2_8{_v* =iܟX(x1%ΞFmoOޜsCEuVYj7:5ˮJAB Ż3Z¾{{_.JN@w} +iG:+Mع{OCM`O.q3r:.> ZM¹>Ol~86@:T-16N<I9|E&X{}^>MkaUn>Wb>G5xo|˹dq!h{6A|!8/WUlߴA (+;RiX2!<$XCo t}}5wO;Vt_ZZ{/R1LE1)oYtdz|_gG w놘;|!b vW:g9"s )A{4>&}}+fg5nhe tR=Y` +wBB3|ߊsdV%Bz1L{nZ>ˡ\b}u&`+*(SG)X9ǭTU vo'澤Iǽ{?*bLH1[`wFk/_jE#+OkI9XlnR cP0z_^뽇{r/h θk mkakzh:. ^4n\wkv{rwp_fZE=CeqS]kZ 5^稸 ܇X_=!޸QOqٱ'!m${y\#Vm$%f:{0 v`ggÄ6,-|Mz93Z7}͔Q{_bs5jn:Hz%!}5yIZQ>X{3OyY<z$]px.z=a&GZm^};ڷddާX6SxO E+{>^ڟ*:Junuol~ڵd |=!24@1٠sB $⽨9݅!%!p(#ig+&տU4dz~fOo5o9w<JƢ &B54MԾ-7 =_)ȵ8L܇0n%(M`z+)v]k-57ycs ]IJ;SN1rzY׾u+NOs-u ߖ yʄ蜑J9?HhhdKGsVgd8ӂY\Nw 5rz\SҕQzݰr2;V$Ƨ9S@|=*YK81ߛ y}9uV*^1|]6đKA%hlSYL5؜O#S'}x-֪/r:=|5({3gJ IO<+xrF%L%dz$R whn_?[N1$C?۵+{ڿ ٿYx׾Nqc*,t/O'SE5 n(w žA9FC'=r/ 3Wɢkw/ܱ;~jVþ/ca i!YX$wq8~ɼwc!f+Oc+d_M:f˱ scc˿'{7.r?p}Y?<~{ܿw%nw-ȎY4`+w3W-o- 9ֿ+5+gqsC\ρV{|-3NOF^}?/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVB. jmkBTxNQDDꦚ Mm٘)n"D6D-"R8'88]56ރ缓w?&Y/Qf{003nOP6k{m]-M=_C[fXߵwbٱ{%6H~G/K~o+3@ȭE.;LUl_P絊߿n~O@ہ2l~m驭-v_ꝏ^ |_mFmFmFmFmFmFmFmFmFmFmFmFm-S0K(鸝vgW_CIL}3BIHņ=vu]m{['2g~ ggOPn׺磿\=[s5yP{{?词词词词词词词词词词词词词词词词词词词词词词词词词词nHt=;é[C69DW^ԜRҀJ>Ύh:qn.3<T*NOO}}ҿ^<L&8:: Κ}M=#HL&\.7p\OAprrߍ$ ߘژS4JzmkBTxWTǟ?ޕ] 3;JHQ;H &vņbDcbI޴~~ppf9ӘỲa۳ D%J(QD%J(QD%J(QD%J(QD%Tn)99p<{E$SJHZ~<@n}z~Z&ʆuDBg[Vw-Hg*r5+(ײITAݼ,ڂTZEjpUe&ŽH |gp02}+&Q&~= }/@@>%.;,[pO"%dƾ(]mk``Yq ?Ҳ{oNQ<;?I9 Se0=d[ [_MN/_ }͢4OT0h~`Yzl!BJwsbJ.!N?SΛiN?ȅ#w3y}Ƌ1P'BKm+̑,(KzN!~}&[5uat} Fj?;+L$y2k7 =3ȟym/|azYy#N$)/,.kt&I䋯k=dck=)* ɪ!8HvN\h.6\B7c o¶kPs&v@aL(3 \e&^NNR"C2{n4I̋ 9a$6KN2LӂW .R.=}(4GnȯN} Lh-ePth&RSᰨM Qn[YI. NqcCU< TU2X}Gl-Wo:;!l L (;H"t_߭ ciG}#w3z¶cw#uG G rgo<E; DVHgGB0d,A`8볇Jqk!΋ ZŨ愜v! ;!~;LfggˋAl;Zg{Z뗓$4QוjG@#Z0Rvp~.)k#s<:%) ݾ 1 ? $s@r<'vB%_0E\ 2r̈́Uk.Fg6GY{tGh㯫bV t%ūHG?_r؟ݟ*8{ '\h  ^03mvi2 7l0l Eyusܟ`Ew0֛O|-ZBzѿz0)g&k'_0B'@'H\ st|aV+d| y}xmO Ϣ9@=y?ƇO07A>H-joUX(;*Etw3D ^ x7e2Dpܽ3 ޻bv:7PalIb8I7OφS9Mͷ 8mTGi^an%پgk6M+^zcDN 8bjE0ms~4lZS8I:LjvpF>fMbTV_}L>;X}>Ơ_Л)lЌG_-}]h}!Et3(O׉XX[9 khgXZ}b=e-%P{|6iwBfͣpNhiUa2ĤGk^1ilMXsٓZjA0^qMhWkի1UR~BS V%@pv}9k.aҖGp37|㋰g>h^zt NE)w#|AbK)sc `LbS7,|ӳƞ4g-D=+4wk敽PW$g*0v>st2ySՃa߱VK i:Z`j,bEs!4W 8&0YjYo G`g2u,}vAKμIygҾ* 4n_+4{}yn]]a޺ZUJN7)ZcdNZ藱QK ;ӹK\<{vU,ť*ɀxQh>&~aoS\ΙhRZ](8MʲP&GpWb rەX?7Hd5K VU!dX+ܝʹ ʟ˯$Add$'HJR }+`IX7%< ڌw`&fOs:!Z͝5R(b琸? APoz3JETA)sM!y$:dD]=yf\?x9أo W07I3oj_0/w-bB~OxFqJĹ^sSaM!;\? cD2?`nQk[T 8뙫{]JX Am׍g]}J>vowW1il's@۾mM6!m@RbfL \=rk%3#MO/> ?gAhW/629!:ˁ<@d lĿ@"bYVNzpuS]  (yS@=3 4Wh=vi?˂Wʛxgs> a!`(ePdWº;d"w t}z3It,YnX>Mcr>`9>Kg mQXzs炪Jkۉ}z(g0k40<Q8WG]y.֚Ί{٨^\RKi:as} bj04TϏ%E,?H~.u.|0i{:8Ƣp,9<ԂB6g:sNUN|uloKGg9G{p\e잷s[ҸY!m1 |-j3!Жq/f rvQ<Orq[sv [;VԶrŞ@+An>ڀ)g4vv3aIE|uעF]x(M!(T*m!}]O șLgxuw F91Z?Sr x c{1uqܳwgf8'5c7]v{ 8}SD޳FMQAa <gObV#={? ^Q︉9OLZs/k~%g(Ƈ:3f"(Fw=jik߾ϛ}?ACu>. .nw?7gymggl3.(de`{Z%'Y=F\Gqz/Õ(@9;͋ xCVل_ l0> hwQ2!L^|Gp/}v1{֨q g?8xѲ>Y.ɪ˿p߹6:`P7>#ۑ(փe5s9-ϻ;-4n5}s_ Q.9Wj-9~_ {w`k'ߞ#uLtϧ{½8i3[@a;q\;wm{R0j׿s!{x y:;DZאsi-![a1au:}'!*w}~jܧ j}ii+>>;0~\\;ګ>J; =1kpR]-~y3\%+U$.;<Es~tvm3^gsZ[ Io3~/]] \>D6o$$b)x]:_8 x=r"cIBfP?u١thh4T*ɵ::E~xDVУi,֡|÷bwM?ߒ3yy"qgO'ǏPP+/5iD.WUid A}a"q--ZOǑn65iWDH.\(GT^{"#y`^r -_Nɓ>((((((|K?%WRCmkBTx][T89j#JҔޫ{W@iQ @"OԨ9ܜ]2ϓ'-3޳03{fMg}Y]@{SS'mK'shk4+l h?K6.h5Xf,e] k*mdEU﵁c.P5(4*#cp{/B`:B|bB:_fT8C" rt JߏÞ+G >IfMD^fip$>S0D#G39o*򅈍k4O׀UF w#^MX PߟMy.phMdCrNG8pw!NȀg)4 6LzF1 T@0t( :AUvK&} זQ(0'{A8 ~8W`'] =Sor7X ?}!R%Xb]9c-s4ÅSc̿ Aޠl@q Bd;ayNW: R6k>V4 F2Vkw#(̞ 'k8ˋ"ΌZ@sQفPA08Hj!1qJj/D{,/ dF;(偔hG9'sMmsT"&%Zh ւ~s̛<ql/bq-d{+i !Ʌ؅քPV n~S뵁j\~']DsMpl{ / '✔G$'E!7Lg׀}-P:EtpJ:@TORC(! V=Gd:R Z(i 5Ս k:|.H .ԾXLZy>CbǭgJ 2R'Hp%R#Q`n;HA)?ՆY2|!_iLsw%h`- l0}~DJGсvL8PqBFoi4 T=cNF<*;`|3e~M#Mi`NRA` 3EJ[!o$?`F <( "Y,©Fpȫfp4=S X%p gķGP|5 iS2BIL1R L'׶prrG|Qs kGw'j@y`Rx{Gޥ*rvl"jwLf 4 Z3i^?h3s G2a>"p0ԈL0 hd?=)f[,{D!l:x?i!h! `s탠?6?V8+AcPE$/Di]6H wt UDg93WH=UI Mf kp:#Xs )ni]+2 D k$?S>Oh~PK"}ICeT&(X7D*ރ+9o p]]}{SH,~]%?#.{Ɠy s$'j;4%ǎx&P[~ ݓ օ<´I|7{[>0W P`wGd=ysQ?y:ݥ_W? Z+-p%\E(3=\@S\ G= ']mBg F?י;ŬGv'Q;'<ΧOgբs`#zX+bPf9e#wӛ p; *uu1b3ҩH9 ?2>Wlo2x=^4nWD4oI ~QiKqTr??=Δ;-]ה1nOSwMmC&zN\t1qW$}0sXM*0z%δDxN@w>}+QROa=5|ݭ+(KL=r9pIg4ç*ϹՄs?9%/U)Ӭ߿?twuB{S}E̍WRsy=niſg*wLP&?7'9]6;Vٰ-ϦROp=?'^1'h{U7|da X]o)q4y&3SA8/PwEs?wu <'_?ި{5Ž*Pw=Z~q]EDЦ>X-cYnSjIrMЁ4yT'8<âƳR<.!w?Bo>׍9IQ@o!5 s N,oDb݀x IMywŔg755RDwH~TqS_Alt@k'q a,%DzKȗ_<&%ם!y.[N-X_P-5;%n .'z˫[N*S_[>#U;Z%03^̛Zb 8] ۡ%r:۩۽e15@]{aͰ@wPOM6fńzK<"υ{ K ٌZl1<$b677gǨ޽]fl͹jc&'Ho9aog_va3Q8x z=9"OoߚMOOñcyBOL`[|)_of6??#O, 5x5L V-+C_KybP-1_:剟L+O+y.ʿ6Dὼ"2 +?ʣykB(+'y =aj%8C&g`Z'>y"&#=6FCJ?lMXFȿ<AWG ħ@Ee,5fBbx'nskqXz;?n-[BTD@sCz9P[-&'D\Yzy+s/<`VE2X |nO%-Cc P+N"ZHl0` +`称C0w'ߪz N@];`PyBt{V9,=>ka/> V:?l>_ևYCLF2z'2E*mkBTx}+(H,"H$"#X$,QԈZs>U{ ..T}6ڳ-F`p]k߅~b  О$wݓٱ|sCoA+q3lOx@(0a+? T,_7s\Ϙ^Bl1)C+k(FyN"8dPC_9>O0&l4Im+nwGrŰ)/tihf ѸX>E)<,6s45zb?J\<OM%O#(76:= ӋYAƒH Ls6MXBcX&ǘJte. 3.je(??Lj=%wZizFTx$kP8Em jAOހ>~؆B9 ֤8UKCvjbL Cy ;mj P. DkwUE€3ܨ8xUJs\ɟ+;}sFQ(KIXݛƨ 1 +KdX];Jģcx$D׷X`i @l̏rnm$^9΄zBGϞQ=nfkDe; <a>,⢞jk0B[p($Ǡp4 nq`XƓ vϵ.xHnorJ5Hu뇗 f a[Z:>36[g RL؍?( &w.7C#~B{] UW 71jk~ecGrD.=K@WDZM0倐0\xvqNZ ># BE )&yA}t?B Ym(WIpɱ |2+\2 )l8tl@Z.Be񅋍RSƃm>dIl'N adĢG3%#)?$s _5=YBR#-k"qGP-e"f%֩-ϓ378M9ϊ,_*n;HEBƱcl~ ˝[/sagIE2,z1t:kLș壋G){7ond{@rP>kwk׽ #kXfyEAB9uM4P=_lgW؇N#_nGpp ,ZUu6ȓVӰ0EK7*|]{75F\ԶzQz! uH>upT٣o3P)[^6` -d&*=%fY<^ط`_6|h3ء>2 Pq7ώ ,NsjF=B` 큳CiU)R鐏@LҮǧmb<2FHRqùFXi䎲OmGA}:*u f:@ʫRH.66jcGOpO- 6HKJU:Jǃv,3DZEƮqq7p?ȌK%ȧ$;?Qr6pP7`a^=R_)m>D3#£ _' Iɭu͋C-Rne㯄ssL<ȭ/R)|Lt_1Lk=rr 4/gEr~PnB[\g[{gYvRW' {Fem1{ wL;7&$xc0 n&u@5sCCձm8Heft x{q(aтa?Q%l4ςxmWI׆GC1kQ3iJh,KRO`ʲ4)%b6B8\pe;u)ko)#WSncRx{[sXv195_0Kՙ7>Tp5ٴl3S"؝LX睫[5m Q="u}pϘ*xbՉ#iM+@Z! Ϯ~jYݬ$?5mtu] %@݅:4h8ۃtu3; ΑO1A/r R*5i&j#Y2:$Z(ad@>'z L뇶6Z8|`6"X1_z' F-я?X^ A:?1;h/KVB' vOnFS ƤQ{=kh7MwXQp\v͓O/. N3HKRlK"q^Wh1wt h@3e6N|I;y?8t[[! $,ήLe"z%IކAkRl!3u8ځy?_W)AbCO!rza5Sn֗#<43y6"R߃CQ&>[# BHǽ{vekOTlq(UH͵h ݔ8,@tՂL{p/*L"d_y k,4 G̖bD>,.ok"D;|7[.DCA#ilϟI֬Dq]+eE _-- ڰc^Lq1~CCC9gNH8BkhJ#Z-`VoMa 9r$պZ-hkh ?C$ ^tď9d(8P݅]ڶw[wl;dn׆oKd Hބ(DInI M_(5)6H/Y1 QRk,nXHʉ?>df&6^EJmt{CCc`0ʅv5x<\9Yc}106"״!֏9dl:' 1H"z'7QqɌ#KR./CVgQȬ\ `?d1yuM6Ƶ8ZX]8^pwQE &1frRKi$GݜЕh3'{;;~FK37ku<pdʎ+C RMzƏ7)nҀ lEGyl:̑IoBS%|ЕsTulebA}Aʹ10A{KʘӺtjdLI=r PRg_LbR Şl?␔)![Fo wi&k^CV(t@pW2{hxHGRn͉eCbxԉ6GQd27\ثdS=\Ff*0ۣOP5(rZߙxQZ>~GAeN-jY7Ҿn;n?ӹ"Px}/NW:݊&׾:x" ꭥу;R펔 c䛅љElmG§a= h¨BG_uYnZ쫭FYs U"zM&:Gnu.DX5Xn;}ԫ%XO?~2&Frjj8 yA*W I9/ub)Zl: s 85J>~iI3Yԕ;:#hELם[ROd^GA˩f~Y!En0~/A Km>^WYq"<цF*c:xw|͞w%ehRgd9̕v3v Dgh>>?3hYDkgC(ʹƒԕSԜ| 2Q94(?OGQ34 fccPopTYaW(>@tX4`LGٞpɄaŰl\[9c26U M6f,'C4i?W~psϠ?kAKrŵk@I|>^xs?\`,D̒5W^w DMXf_8<%|8_왉pP1Wlm߃f?4:́_Ԕv M;k:p_sj؎qw]$F}y ,b'N=o0, ~M YR46+!}@~ujctCP.Y(x׎z?70WXFܣo3z0c8RGg0 TU򄽻w"/4֏CQ`[{Ocn]+{{ N!33+5]qpj' r9FDȬ)~: 9Gmx2-?sraG"yvUpa;Ră A\& ?#n 0eed~oq嶭!!DzP^H)>oȑ.ļԶ=Hy7S-M ?8ycߧq|#5"2Б lm#UeΤVbM͘jAc7Z ]> 4gb s 2WRsKg6 's8qzTT[R[w)I95xWj #!nN+zPڔ KgTE,?{^RDݥ=Ru^zîc&D'i74SJߔ&HUG[crͦ<׿~4}څh;lpAZ%XZ;tQ?yk1+Ƴu6[ Dc4Ɯ*dB#!}e>samhG3c^8u9󼵕⸈߂UyB;f "Yi=D =4&|C3g]~WgjhSIXU"1A5Fr4{AljwTt6</N \Rta| i>T.Wo>>xϯY{緷m,J{gg}v~)]s!?wXGFl!7U|Cnfﳅ:.@mq%臔Ru?.:aBֺE#Gg'yXDuSWNJD)21ѵVagWPqȒ s?¶@g")s\T{f3go^w:^"{d#!φt},nyWFKv„X4|VB~,˘_&fjp/WԍwaO H 3I`u1ͤ+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_Wݚw)mkBTVx1JqLHe ,ž uX6 {{+*M>2S<ޯYZzz|ln?n&f_?w1 4V7}n? lxu>}Կn`oӿn`oӿn`{oӿM6oӿMsmѿM6<ߦmߦmߦmߦmߦmߦmߦmߦmߦmhӿM6o[_Zz+sw?372zsOe3 IDATxycux؁ZվtOwOҳpwK(ɖ-;vtq9Y|vDltX2eYcIbIXG4 gȞ^kBCUkf4Ww{mCβl[E__z*wzx=<,_.UUW^ell InCO=r0;;vY!| i-XC8YnC8~Mz3rHr#h"WSOz衇'˲Ekvvze2Zn%$Ms15dx̉\ s:;BwH!͹D$`xxX\r˗/iyn=#X]]eff{ٕJq62ZWU(& %(f;"xpBRECWj-nF5թ 4`z2A0pj~'c%fe#\7n60ڸz؃/f8j8!:cbdIG&{ᢣG>0~mYkjZdK moyf_Yce#MZ}$HB"S_qibd)Ϩ.:zeY,..233Ãl]o5tc3lfyE*#;N'F>KHSL2E|XYYavvw6QGmTN %kiY-pgn٬on=i EP="U$UBVܷP\±y[6\ [8Aݸ2- 2m 4,, mL4ll ,6e\ߏkbd#S Rd|d/d#tөV,Om4`l$,K%{y6oݾCh3I2.+|!_PP&!pr"H4Yxd6j۶][ؘnXԪIlRTKbR0(uj%ba:z|p\~Z<{yvgEP$ UیTu*uJ%h.Bz:iS(Sשi*2h˶1- òT *erɠTS*s:Ab,*Yz4#♫=wS롇B|.(bp666:XkZͻxmu6[粟,EU*F$U4[ODJ՝Zk KD8"I8ƶwƲm,fu'G*[Ĵ}8ϲ۹ds?$%],ꖉnZT:*tʎA5g^W^/qG=|# jʽ{eee5pI.M&pwn{3bЂ ĄA MUp+ʉ# "feXF&ѐMB}p}8}'D˶"W>^~P_ ec޵mc4n[a[m>fT:LVTlFeG1YxٗG_|W_8S}zG>O3771C8ӓ .M%xo}[|퍷(-dU%]}F|^Ei+\[q lpf?YOmml3TE!;,ǽ ]MF7MꖅaM 2- ݴOO6P֩: K>(=E4\f :mʎ^=\St ы_%G|,baaNeh.&LO%G~ͷ׿ɃSL0t`̋_UQaq7nO̮mjC^Fzn;kuޙ_=s]+S5%e6e-0ަR1(B$a&4&UàXQ!QHɯ(tג8HB_c/ןzCc#۶tnkuncv5S !x}~.zNo;9 BI${/Tw+BtWێF7A5ӠfTMibmaP6tu2[j gs g^~ <􍞃&zd8LC-NmᴱF'4`b:=Q3maPmL0%$4)e]gg;U5L狁Aϼyn9a=33C:>5:d8Ye:<\>Y.啉=%~G4K8n6g*U\T*u6Ejz|,IB~Fø6&5ӤXM˶ɖ[>I\V\xa"վLBzM Cd@ ~qQi|׈6NJՃ CO&àb 8j]Ҩk% KkK%6YR<̻p?qSN_ صF̰y5/ҤcjE;~k[_&b}">D]".B¶m~/*֧-,{.5e5ɱfGLUX~PdkBvzjGи?g,"z :<`vv}0=T@X+׿E-%o@ϣѯyZVڊ\nn^=xwiMt܉Hi`d5[m3Rmk4v6`{N^ahr0yvkCJBP뤶`#IhGY{ }kqôBo C"ڞ)cΜ,۫dK5_fZ3Ju I۲mʺN^g}{yUN+ROi_Q^yc=Yt4X uUXZh8&Ģ6S(A<įq2e|Jڮ z4jlTd?ӇG}3?O|Ħ%H>C 9+ A#UIOv^,fP䭇+TM@eCvM2&5ˤDA!{ǡZΗVȦi;3t,65 Kj1ƵQ,)Yε"P,(UFwoEV ^$$7Mnfr~h"ׇm59GUn' jZ6 :zmTd6b?&u݅n&>?k|/ANzx|ВOPݻgFǣLMYm|7~_<:xk!m4b^߁[x ߇aUdJnnm6F]b8adݳ-M`Y6ՒIhR)T&IhP*膉i;!  7MK­I4 &4E!]d!9B:CXF$Vd\&{,C$r5eG4kk(_N7vȮ|,g?§>9>'&KWr@쑿 *T*Fl`jm/dɃnPT__[GnLLtO** w,^/c˶jdk5jk[{V[Cjf>ۮ VkVTKhR#KV܁^ QzEmlmU{7TJ=i2??hI8ކ5z5$x|_1 OzM.*A\|mۦlTulfc'Oy fZ0,rމf#3B Ls")M0c6phcᴷFOřJ0l> ?|_ߑįi$179RGT5 6eF4('SR?JE7LVٮVA.SѩFNcapF{{>4~s.pP5`1TϹivc'0@6Bw.G',R=;g+Ky " w\Ouk8*2{5,v-$qe8T r}gţ_ 'Fğ?#{\{IRp# icVdA'm>)~oG )\Ue$l`Hc(qMJ|`S12*\o![M!S1gWQQ8UBPϘT y_`NAL ľJ:DXbtZY-5HۧSqTj:Jnb,Re^ 8҈v'iQc_s O@ޏxߐO>oZ777OgE4`裐ZYo>R I%Ls7˿֑x?dսU,C}Z@u-ۦn(D4X/SMtLZ=qPΗY\Qяܸ8JR DBڏ4BVT4ՍF*IrC%!>p$!u~vҦm;tZsժT*ej*u%ʠq} $'DH@̅?̡KFQ&X]mK"jTru$SS: J!rƠ50j56$Q!hhDB85vZHA 9d;tdnwKK^u~ 2qg=33Ꙭї&S]ո^c6s޽τI>@4ШKl"rbNEwoj5jǥs;A[͗)d$Il$R?ٙ{DFH#\Twz,u,\R!հ~B_`K@I\bjڅ~! WUdjs{)n8]Qtؼ}*nYj5rjK muvJm?*ئ )RHe%7KϽb‘O^ovnŏSl4X#mjO΂Յ9שU*eKŎ'4F@U(1ަﭢNr ک:z_#tu­Lg-TNXY RpDlRT^?>Ȩ,KNkI4^K" .UEU\.U)l7NV;k>&7RPGP E*'W!.BlI§Ub)PieMT^<8e: IDATQ+jBU; Mۻ*IB`C1\7Djw$Ǿ+& kÇFOƹ4$:jyU*Y]xbMW;Ghr'$"^$Qfv-N Jv?-۶Ԫ{:Um;|T0X)Z9-TI5Bao׏hw)˥6&YPdYqU|>pO y5^ϹS ˋ//ur#4cP`=ۤ6۩m £}}n'jU'/;$sDhEū*x':idDsض[;\9Hi4تTX{;Ecfݸ(=1m%fgg`Vqi2A}L^Fo_ iN??v&`k/hc:}5-fZ9ЙflW*m;F˲`$9pb,dtglVϝt RB*&$I4!oBD.KUb}$>^JuZ%֗I,Q2B#12F0wmR,6XZObi=莓U9,fpu#"tilU)v:jVu]A?p7U`TPJGс%]~=x䳾SpѬbeaXrD6+ȿm;L< ƗTB=7wX5k6Jm5 Via?2{|lKܝ_9斗)q&%O5tr%jƸ+D_K'iA,^!U eIJfںN y`2(l)R,W^عE##|>߬95z*x;s#nAنxFA,չ2:~d/6r1tM4G4Gs1 ͍*x47XGJNWg so Aե2":4D,9B0|j1L $ V<7N`祷w;{_]%SRӻss퇐g}}4ضaZ$hCHq٢3^*?+ܲLXsvy۴B*:R|Ν_ߦo_dz#'s%JҬY[[;5z/TĹYJ o=2݀~| +uE<znXfi$<1KFlj)FhtD=q-~䜅T>;RTb~B3QU/C%kN&xJ"G%&&Aٝ-֗"l6-5O3OFU[bo;wfYIۦ]N]#/~1om7:Ȏە |g$>llvR~'Ɲ9`\? wXx?Lo3448t*kt(5ц7W\hrᾁ3?k_,xOL8?@pcA':oͭ[[^Oz|yL.xA۩{Y ,u9} H`.Msg.12y4괺A &!f0>z QVZ^bmye.k\$Ib l?؝cVә[H1swwc}gխut;)헙ѫЕмё,T$ D(ܖk\oxz|'tL! g"gggeߏqjM,K>==`z*At|х\JaYBãN_&><*/.ho0hC ܈l|giJYP7M*$,/R򘊊v7RF[Gٍr"gB{=N&nXgO>z:5&\cU|Izv:N:fjVH(Ȋ` W,Ktc箏۪R*@je*fa`"ɕg_ >5 ^_eॿ헑`2nF9uTf.ت8]GM&K_:rsQp&җd߾.Qyz\(J{ 2>%C:@,4H:"$5) ē]N._[t{ mChx(v[1 y,l,oT*W0ye6) ,Q'o;]ԏs61Οd|0IsN0k׍~RF2:2>[6ݰ.ٽGu9mJN.yEoTc!sLm ʆrՠMڮfsaKlN"E+HHh/(bbp ݃cxLO󑗞&8EE)' jIY"QI qIZlmNEUJE} GU]ĆFE-oYk,.)l+y6`/$gڧe|^ն%X4 3;j*%]=$IMO '3-+k%57L@…,#}:!ٽ`?OVc!e99&bfK԰z ӺB*6Jָ2H!יO^nҏ6&mmcxQfjB6(Cb$h!2nMCQT,-oxm(d%}MDM`[_n$m%0%>~''/ e\Avym6gEg?cʹ{躅$vֶם<72h9۲Hд[w 0$m$qN"61XAnV5mlij4&nuQPfkV9rB.~0,318Hf^0_33,b;" O41ܰt'(|Y\jiٶ eZ e2\920nOCԇSNITk)hi0bV5 #/9䃍Q,ղ8LVN%_%|W{qnpy" ) np>FCsgtP,Y_Z ZWqj_zAMHQ$q)$ӑH˅<ņs?b;W"F٨2w|X/#s>ac7@22dtj]]*}Fc]?ÌR<Jh'!Ē $Jpe&Ki ţlDPiKJB@f(b>R~7%]?h~cʫ?H0d!V$.7A 4[2ZZ$7F϶B,L$nyl]>s9u d)*zcWo bY6wyeiDWU*۝幺n07bn>u@9<\zY ]gcuERKT;h7W_ z}мߣ%l$4Y+ZNA!,/c5$8ðH-[]]TQ[eϕ(ᵏ>&ncPgԫjJzBQ44̓Kpk@(|!V_ eto^<>CCDx|#Gvr9H^ږeHzZsCŒAd94pI2! 5#9J@$TYp;|~|(/*Q'>F> uD|05{K|w~xnI\qu 7ܾr}ar9r\CuϹmۤ6262{GDI&"'q+$CΉ4nlmPKԊ["sp2F2R>Ӳ}ݲhmb.B~~lBFjqDP 7hȉ%>WuIJMpijjDo;(x|~0P(D0&<0"CH,i<+DgN& iқrZL&w1=DX\i%tl"W^8tzmc[P^@&ʪ`/}'G?^?e"%sV۝9۶)J\hs7n. nY֝ETUadhs((nL\f}ekx׌zb>_D>i\+ +KѾp/]FEr,#HȍRrdY ';>>0=5rw훔EUO}HWJDfkV[ݙ+"R!1q1B mr( A^g.~6崥MǝvV9f('Rh|wW{GGL<EZX}kkL&(q<U}[:@>>־Su𼱾j _:cyxƉ9:snp!Å<7fl204wWZ ի!R.Ij;pR(]HXs7K'& P6͖AgFJ\@?Cn_>p72M)6VH*W(sl2K 92~G+-,YYFo;G9[p{fa8m̿WGN^|#;QO\fk1*-]`Aڷ~&?j\_g~GD$~zaDy.] N)ϥ3 yal4 oS~}^55z[QR,t]D!}Nēr59~BMg/=o\o2 8QɎ`I&&x0:[luGCI,ZY^'28HrtKON’cHovi崓%y̓NNQ= ×|#'#5&ӣ*/a;~m~ZY8h. :9?1>ꥳi'[j5HZ6y,Ͽ׵1Ͳm҃!IATP?DCU'#2ACxUX{?W?qyx$u>{\L(Fal$ ݷ_m|__sd(Wu~SmfullUFu fo=XMġMc:':Ъ:nd}y}n@Ad ֈu_o6 R+WL/$?F&i6Io/p/esw4B|d㰖*k?%H<݆ }=w@ u.&oTK7!z|~yT6}7˽幙En,}$E?[㑕ⱁLחe`c=ZOG)'  KsY[甆߶mv6Slo*SџH9o0ε_X]fea۲,|K1Mw&R9mͱrZ2G+9! YW2 "\U{3C@g~o$wѿIנy$2/XAڶmKgNO{Wrc`!Ipe:ĕ{.p<=*:W,E[Td}qeb .pqϼrf9 L_elNp"nk5VY]tronC`h(hDZ!ѡ!cMѼ^2㗯q7w ,,9ӛWѦiriW"d9D`c isH.vB>w* &FBKXUQkqUGyx<``F>lf3` l $hiur#cǍȈ\+kOˈ; 3ܔ rr|bu1ZC/v/^]x׾_V!vD"2O?ǻRɞ 4OQe"(t-S8DzM0}D$4n{ln(J bDy: ICF G]eRUiԪiXm{UI%IPUp,F8%Omjqiir }O9-̉$@@ [m>HE'y.4H4l N$~{ޛI>AoM/xAN<ӎܯᗹY*܀TTt[rmAv+ڑmG[0QFպ$ Tx$D4). qzBDdި7꬯,FxJqcet6مuvX|Ƿ|7W. f J&˝8^p{-@Ȇo{'qEij$/Fswܖu}iՅ 6ݽ8ٳQ"ASﳰY⵵(IdGfdr!G3nn7{.o. JKN;,$߷M>y;+Kw}ɠZv A6f'mx-$4F.t7/_<ȓd׻w;'6||۳gs<[ݲyeiY\` 0l֊dR1> HΓq,a Fj0Kj0y4jUr )JI̥޴{][$cv pm)s&Ǝ=R-?oWoz>4s]my ,+8K ́[>Y'ݓDQ$HM$a<Kԫ5em'zQ5IF&D_{뱸99mb4CvXG^|F:6٠]9xj 8x8{c4ݱ=t H ot_|o? u>4qe-sӞ?ldW[fxO1@bqccIf8 œh4L&ˁY^rZf */{?m6 &%7}qm/p֝@3%Wm*EͲmH3ӏI+>4񥗻NíEY-UW;u-W@H'8%qޘIݧt ;g,;ȼGL<3jUR,xɯ_[gD$oQvd IUDJX`c9\i"ii[? |Z:{6 N^oxa- 3ΪY4g_fi|xՅBI%N`zX[0h$@!bHRg=&KST!({h vz @fh7ҨU[DFK~",gd<"p\Q1ig 崛_|BKo㏊qzuɫl Qy,k?VBzog^geO|bEg])^hAԳ-kI!%堍v0+ TVdLkVL!AqΤԝ/xɷ..%։Nm;]Rۂ(^ b-c-iz5۩* " ϲ66DQ`b,JYDV̐p0 ՅyVX[Zӏq:!8j0`7eh<$A鸈/_{(?kq5\G> 8G11lF i6 >/sct)ROo;&r}LRF-H6ͮmԄo 88TUAQipdd3[[dL9ԙsxG~m9VfUzGk},`phUO^7̠z~M^#i$ԗ(Ɔ'xۛ!|o?'D<קi1^k`Ҍ쒈Up" R9v"lDHCl6t oOt'훿;o|TCP| #;v@YNuKhw1EEdr(qM;9&Ӻkcso;>zJdc&iTv4+lSu$ճǧc|B:qvjXlMu l,s]7ĢQ"*~zxdYbldS#sD3<ƙGö-֖ 39>uYZdvairrZ"x=t/x'K>aLq+jW%m >/#6ZΔi#20Ҙ|:E>_';^6ޑO{L/B>)jb|zV,Vdc| EIjaZTM4MLd$E4'nP8yeLMfYSSGzl/FziVdId0x?ӗ5|Ϸ??T*71'$C ;NL\$bؐJr\ةpY<;pq&qY/׺n%Xv(ҐDht C{_TE:nCr;OR<!\za-%Zn =5 kmf2>X乣b#_fI87-dFrڽ7}[9W:޺?"Q[ Ow^mqH$B:@삄6|-v ؃ʦE:rɧZWo&;uv/xga3)DQ`,d>=֔JT5n #y*T/DQ =>dshDt<#\\L֎ɭz i<'I25ej"Gnp4?Οɐx>U|?y^8]"fӖ^#IvS.kl${5sT֣_'+SZ-xԥ. 0<`C&w`GvUI%DB2ox<[ΔtRj}?vmozi+/~yƬC8us[AC]&堜=#c6X#wPIh7kݸgcL"AEBk%r\XUxxlW֑K!B2g?$b!v<@Ws9/$"{2!2#sD,rZ8`A_N{1G? :`B͸K[ࣤLg slj~eZU'J%'|fF{ou9D|1q-w=z:ky>' i[,m0,K fdr 2:wP2R"hT4L,fVŲlED4EFSediD/ȁBv@wMЁ9pxK"r7}9$I >[,[&4ߒ$1zEKf^3|W@P=(=D(&r=Ci; >]o%o *e}bFPʥ%١$\D*uqW{F/jd]v{BԬxćzXxTp]ߧa;D g.R3܉@KpdPf7x).t7B_N! 3?KY¬I1x2tY3ǁ o+ }w}v-'j G4r#)֖w?9z͠^3dyt&$&v s7 MF&emH\^HfT56W @Lk_o&[\&;/eu6ԆB uM}9t$ie9,/n(lnTy}ʬ-%$˻JDQvewI~8Hlt^e"?'R1vqڇ>8x)~?|??~ŕ}%߃ٵaR@DiHZ!< I{Vo7roNB]O>DL6A~w#aysyVJBwg/ \Bȸy--D%jG&Jv4r L_ڮxhL:q}x `>! "{yۿ? k 0*.Fť8y2&)e©팖2$7F泿9߳-G;m70}jZkJn]]Xxаjk8 b&$˨yU: z*Aq٪&SJaA$I|=-7XZ?Q]@ QVV=Jc/mEVv"/P;'wOO*#5T< Xiy>BaCцIE> M#눂@B(GH3Q!'Vwu=l UQ?‡n]>g_}9tOm\珢ĨJrL#1J6,X+k#cblw'`0/<{Xєzьmx(8v"jGR  L>#au&~X6 }+5v7\J`0vARC7DH}]89uS=Tj~yx/P(nYN=oS۬՘Dz:ؓ1CF$~b"2|e_5yۉ{|2xxϾ²Nó=*y hc8EpC$BD,߄Iezfo/Vu1]r(}9$\ee~Z͵:%xۿwT g[iY\Y]GUsYXgbOHMhu1WtWtk7+>: _G=C>~.0{ϳe"t1kzT7]cWfmPt!tVҡ0y(eR>z_+ vKy=K]giw^C\e N&dFY޹>n-$~ ?q"-Bj ͷg ,ӈQ1(nxU¬Amiۇ}_kro  ~EEo~FӴz-^S/:ҽߞv='iSUB>)V8D_*AS0V78hM0lz{x]Q-W| jJ%lF9E6DQbbl)鷷k˕3 7_e~qYfY\Y-pUS3(]p IDAT#w>uOvl/Ss!._\8c}7Ŷi=FzT6 nv,lwM6aD?HrbtK yaz> L'c?~ApPقG Dyyv%^"/]z_*fy-+=|Q~33wD͏rO=u')Os y:>>Mn8E(X/' %Zx>f.uwP5$l7y6hb>WPmNS-Aqq]inxSpc'DAdlx w ^嗄/>˟/l`9bѸ.>eo;W|[pi}?]%'?_qanqIBpD7~=y+{Ϛ|׻x~Hx?3w gϒ:}5'N#i_>r[`|*]*H}԰@ž5Bwl²84HƭT5ʖ$7U|= 7s9qujtj|vFUZړq@>|<g/H\{+$Sw}{xM=ص?G^n|]O:Oљ#ܓ#R[Rkd&Ҿ D@8p4D<~P|0 idɸ'ߖ$U"U⅕ ϼHŏg˴X_>ujjŋ qis4!&O-FЏ%-XQ}>D;<57'/_t7> WtDDj<~K%C,l\;BL Vdf-z&iƒibE\TIf۔L.I\$㹔- QJ$)0],שԍ]}<0 `t4`28M.\lޕX3~叾os퉓<$&dơ$$>?$HLM1wc\i졏|DxO-y{VvU0s Rn~E>]tw%Xol[ױ8keˢd XX+RN<=_ۇr`~x;8o]._C~叾W.-,gz2irC`5}q(n=@`8zasxUtF}[M6OK>V- YU9LtP'mBjP4wx,oIB b\=f_0ss Gx(MgϦApJ vaDIwdL죏 90ބ)u˞6!%X?xr,!bЎT+T U֨LDMeQg(S& 4F%8YDM(:!KȷPxO.8ʚȩx}ՋuV^ K |R& q~:CSYN "MBĩ)4!* ulAQY~L<\ÐOH۲zK" |gZTjQ(QE!KW.0E}dQ$P,ʦH~jt\awoj$VNjTboHPbОQ5ˋɁ(Bm;7ujd#-/*`Y.iAS\uM^"m.+c X՘NPwۮ˕ՠ9,˨Ra`. 0 2(6: ͗j5c;Ͳ V^ZvH˲]^YՙGFxh*CniƶQ:}ّQ?O |"aj4mBq|s;?RXh٠1ZpQPݓE $5 N$ۈfxkI"`P$0(U3|=%iȫtz'ɷ%9$U:kͣyrja'&FbG;@G"0MOVJY J;7o<@ JӃه⊽gTJaY<IDAj䫽+HiPc{aYh]pD%jJwC-\*a$Ku 7 *+s&AA* #Y;PlRA;SGo{Z"!8d.\euߐ辩xYq\p5Y| G?3W)s2B$LeZa2dKq3ҊiR'$ GcC2J/ ` :#*Sq|ϧfSY2)/-覨g`铟EE&3.VF8l˴ɯHK=8LL+ D4ĢZ| &TO# J"W󘭔9L" aZjtM@& ;Fe {D䮘4dž!lc>i}t7 $FT#*_yIeɢdzn'\cfyu+*2CNqb4w2-{|j*+e}>qo"`\K6 )A[ ~Kz~,e\D2$TmRI\F7ZMD;5mFfaY&,CŲ0ً`ǛD3 ьcCIDEx״lk\[\@%&2qb4P\;${z<ϣ/ߤ!m}%`d|WW1C8|@AD,)+ 5|X=KlH~ aq\abH0h(@\Q)*uۦjYx(@LQ*yO80Jʄ2CI[(/LƑv\n,qc)%\9N)gԨȷcu G꣏W//{ɑ*!XmkM>łA4 I̓E?^VHA麬5 ¨?@ SlkmA3D*rW>0K Ao:o{,Hlw2kn@FUs\ٕufW,riNrnjL@<ӹ?-̾6Cxscn}G>fOihf*mlvA9f^'7gMLjYTuE$5p@EQi ȂHTQq<u$EZrݛZL"w>B|%R &V9󕗈Gœ 0.N9譆}}I>,21eęYD4O۪>njL]ٔTԀQ]bExjN&&$@4cr ,M2`SEEvm۠=oFGtc'pJ&y]`=ش)Ι KyjjCyu$Qdz$ǹQMi:xF=[G*K27MXZ+F[_xqX0XLa'R$Yd iUAzUu#l(R\< A@ & E!a.E" cOZަ` ldzחVʧU҉Ƙl:c|A 7:D<'.Bwzi}qloGUe&Y{jgYrU@Z[Rtn%{)2R2M,%:IB},` T$TQ<\#WB(DdHD;6u;c2H UedDMʋE~x+Tj|Q%cL$E|.."ScK`nlh}q%9876[S(mқ#>^xt`t,/&zkT}H Ɇԕ% NXIB d( 2|,p gΑIEFR ~W[DT"nP^4)ΛC7:\\BHH(09rz S wz%}q,'V`eia)W mT*yx!4s}Vꌎ(8""barnXkITM 3>[wpn_?Cv gNiRޣ04Sp_;O,!@܁Bۻ# U<jcmh&jD"QII'lM<NC^"$QD_j[xeG'84pʤQDEUHa:<O/#F\ Hixk궈4obO?"yEFr<~vם$u &>' JNxefA=x-C~A65ν!$bantâJE-PD hut9&DDc"}MH-MvZ>(m|@mj86!m/hq QF.D]vTtfWZD7rnr':ɹ;ߏگ>}O>6%y3ѳDsx `0k{g4uG5]1r@7NY $)J8"3bG#HJ0$V&(Vޛ鸬8uh4FHښ\GBFށdyGj8He'}"$5]Qh/bpr&ؚ_t)sl^#&EK<~v^8Ov?nVR%..} fētp/^^n3rL IlED\d7aч:\hLaDPC (*L(Tkz(ʬjh l"(23I! %$"˨wP?(\`RcVGlk$)4E&ֈV'(bmu҃ >t/7(j.^ssvr>vcC{?>y_=SQsswpU}x`ĩ!^̞ƹt] K81jq~*,E h}&T`LH7!`2J,Q6 kW)n^#) *32$h86ld,Dyap}}DddY\,t]\u=LۡJUEb4d i6 FfeNߨfٸH5XX߷|^_eF3y^wz ƻ0wurӋ^c]T-݅|C)"QF}asKяW89fL.zؒz3+L9̸@& |r\LfEUd4EFU$C5p9b[VtW+6WKÍ1lPr|qò]f l53IB8dB`(ܔjEնkDDƞ1Ds5ݽT;/y!^rl6>+(>9ī/5)Of0>LY,W7 r~wT4q]L6LyAz֐݉@&ѰK \f+ebp4JxGOXլ$[RX\U}C B|# dADVD"M6Z$|yal*@˛m'O&">5ˢf[T-V\=o\ |wi\95>7IX~^֜ د cw<8@ciЇO_(L;|_~*/^O=ӏA2f a5;X;Φn>}I>$21սͩ#gy鵕VZn0!J"S(B[HlN@鲲Tcx$x.YTI-r}q<䣨2cgh^WuRQ1ؾGQdQjڜDGhz]geNj@#5jyUKjX&%:`FLQBH)T Mt_QSWT3YpfE4|n[T,e"qh }W%z$VXX|k\[ZVr}y6oZǶlVM˫@6'>n wW6cDH0ju=*Γx6l#"X>{I};ٙټ{nw/ aQ$EB$@)ɱփb\~U.?IvU.?lI%fYIB {ݝzs8:3p>Upz|/}҈Mb=Aya#ʞ?w\ߧlk|Z>bѠf[S;0=RijjR6 j.I$zkuIMg9nnd|L,2K/YN&GHq~!;Hv̒b?_4M2"8AjߞQ*:k+C2mj\Ycw{vȥ[8Rzd=6s(//i|zPk;Mٝ|WPudT q-wbu6֦ǰ%|pf2yqD, r<ېNԛTzVSjgԺqDԲ)6YG;msd{n@ G\._Bs@rpfFBߕ2ﳸ4Ls@g¤##oBD\ףzF#i<:Ј ۃk{ iɘr5v676qHKq]$wmPVӇzO&%z9f G h ^|LJa.kytU]|oJz#G 4U̘M6ք{iIaz{&%Ӡ88<,89ijjQLZ m:/qUc5V6G.97WUngs$SCIk*pCnϰ3\FǗtPI"p|^L'ܾѲ;S'4חXuQ>'㥆Ąrhb(jCK$.WI)R |}_`>Ӥsaxm"(<'|ڎOyeI‰ؠ芌t.j 8tYf1S1M*0VZMc0(ɬF쟗uv%~~率t.fT&ET!N B.09fI'ؖ>Q^_*0׷RGP? e{I65N>Ghnc4/(!u۝nVAMŴh9. o}y v J#̊,Ic1ds,'?(r2l6ڻ"ёKڭR/oi P|˧HgFYu#I7T~NPdXI'{nMv![O/(l51)C{dc5)"}!0{vAݦjٝ ÓzSplTl6ThHnf2,%CWVT[,Tyosٱ~\_}̜ I"vMiUU:B(>]LW߸|ls|F.͝|gH@=QjOۢxu';:fgfhcM;VNκ܊m+ۻN[v2)4ZM8:=NGn!LO4kݮ\H5 $ Bnn}VIofg??j!`&V\anI k>],,eI$4j m'XհSU>;,\@:p ZMv%F&nM{ߧB"M*?G?h?e)e"^O,p<4"KDDH f' ;/$OD9hh(j2ETQo5jA%g#uU=w^㏎p֐2+a[E?L) ''QׯJ@+zcH\)lo5ho5vmAuZ#jU[Y&]{` t<_t"-vAEͲp|-<6k*U˜jDk4#:c1nd2:5"_sHC:[.[CBC(>}\Ya bThd+먪RDté'X7sf̖N]Ab6-%ߝI [pܦvZ&M\[ ,cJC}s;%$5;Jo&yCLfMɟŅ P4#(r Ӈ$I\5B&/\XYwفiFhl5(̩κ>u vTlcI:Չ I9m f9 LDSBH)Dc:F ˥ 9íqNd-uRcRP-jUŹ&"(HgH\+ĵtB<8H/Dp6!Ġ a]2+(2n6ɽyě[7ƕ)љ <'^lpߞ={ND{,G.#UB69>.Mu1 V괴 #4g⁵NR7u>-lY )T\t0M)KWS@DA scP<-Bon!^/`첹~#G(U$QݲVDlьYX tD7'{|lAF@2 a3MSX6D!VWLAg7ЛW 'JmlhQR6.xG946%JX[ q;CI\:khYA5487+$,Z%彍]د5hY@*p-YwB\UDR 6d }B.0 lmN]̵mWҋWP?'kK7ZW=Qx.ed䡌 Z"IU.tKQ7p$hB&FG,1M\؞)۔ B LwZB<>0ڲl6JU5ubx3,K%(d7T ۍכLhRi6YT^V!nsYV 樚*xnlH,N"r;7^&>mJs'i֊]AA͆n:GF۽"d 2j.(8n4J؈yŹKY ~23xG 8hjO[5r} 7/v٩?h̍LT@[ZYM/Yxׂ۰]?m 40' g oMlx]8_yL:6˅9^*:édzA34kyQAVA\@h38Ph׼B$ADWg\[#"p|f/NkqTk<*O/.%>r;юJ23( N3u+SΟNV ^bgFr3x"􆊯q,ۿ.ˋY4uz o-/ v#~$8fӡxfc~f>ĵs ,hJ@IX̧qeՅ,T|$Ͷl c;Qi.+Tu>7w3͟:j%X||_`ZGR!!gn^ͧ*շyɇQQe;,-ΓN=HPӧ-wX4\ۢ^'65 B$"FBd" rQөIy %Ar*2<:(w?V,!)""GkK} H/.Xz/E ?}S>{,ec4DcM+ P|&"/d~a7/Ͷ3yJsD(rӊ>qB#M ǾL!Yd3fepqb{kU[-vJ5쾍 ۢbuNQ@ʹA~}})Qoxפal*g>7Y6d_̑, k>Q+׃?\<33!7&0UUW߼1 6d$'b\zd Ԓu.@/,;56XYԘ-GT}.ZӖKDyT?- Տs 7lk4S MoQF}cgD4%ˢDYF;|B.P|&$hcg?/zؔm\֗Y,$I,J:9X:n R 0 U-vwln4.sε;rnؗo841ݎ!T?IھgT%mD!Q+(,%D~);4`M F˰r 4ۣ#ݍ]Q\H(BY^ͳ#;USv27ri^^`!tR:sBN];sι_ .[=hϒa<:I8W'sCo8I$ev-Ӧjܖ#df!>o;w+ҫ{<G[>D_Y=kHWo7ɤc "*gygetrsIx{h>JGGw܉3׮{}aѸHȰz\3#Ys}3ھ92ژ#dw]ӫ}O 4w­odFTJUF$ B` IU;l~ P|ư]fwx*A%^{DiI(̧o;'*ȥywe"v?#PuC;nV.(M[ *eܮVgs΄S3(Щ#46PQD'g!Z[T#2w!XΣ5ܵRrDaJ%ҹL`($$P|FnYxk}B*}擪*K? g_"|M0Yf%wف AHne[: UٺT+ۛ vZ49GԪzl~Y]pcBtڱ+P6 ?ipqUXŠ2s`1<;S*eܙ` x3S0 ټCOB >HW̫&b $dxe!|,6FE?0K2/YaUa5޸R`9J}Cj3Ъ׊րa.6[hhڐ#kZ6dʕ;ڎC9@h,ϥٗN爏SUZow`w}\ z"C>"aql%stg<)]cLjsY(_ٗhV'!k$u4Q5,f?gדN ԗ='MjUTZ'Q&at'B41 몊,IÄiۨLo`ݶ e6:9ѬvbLcuv^j-J7$ RtTF #.6P.5\U^}kr7+/^!ƺ|/.ƕsi2'raA=:nZ2+ZbkQј]s;;~qӠя+ tSL>B\y_qUԁ ,XͰzhZʘO!#cj?y_JOӠ /_yBj&YH$.%^YȳNN *\Ff{Aq= 8xdQٛ|B$˓ll5xP2pѰm,LO22ޢ숶kjܵ/5Z ct.Cfnu#!!б#W,^y2s8k򍯽@*9;´ȒD6f.[ |B"6qgeA Mѩj9l5(Ǖw]Cj0m1B-ߧj jvlbQzE1hCeOFR}pcdEa1G2P|O?0(/~|9.;U^t驸atN{W`WDbqYSIv fZkj(nDL.=Pö1VR=fkW5S;y)I~hk{Iҁ-\? ^|vJ썶yfse~ߤO^pymk` Zl IDAT$1I+,huZ}_P.uV=k#$I FGnRQ2xk8r]^peԤt2BdBڍH,<(pq }pKa-<gK4py`8K2_R +b,T 4quRh' c㹎ONk»Zѥ;Zbt2I z'w=gKi;i4d)Ih* 1jKp>+ז)\Y Ms[\<ձܶZG)2\D/-slThH/>ntY%QGx§4;ܺ: ) ?!#4ev\L6_sd]LaԔC9PPDGHyn@dz/YM1iG'*Crdo;+aᎨ-ɒR>=C p%W2:sLṼ\A$(}[ ~NwHe\Aá?$x>q0ⷿeϿ)B9ѧ(,/d3I#hH2M&`{htBTX]}].ɨ΋+ 8Z?i2HgLJݲ='8\j6?xBBt 7n/> .[e/b!Ċ qUg1Nz[h ]q"$|A`5`\j|*c?\2OY,$i^) eA}_,a'yn;G;˲ko==9?IS,̧6|j 87tOUI*aD!dHy2̶=vv!p9_#ZW E!`[$TFeODQQ$)p BpZ]bF3\"ϴ&?y//L=}9?i,EMRAAZ@5T;MwJ*b:$!$(J(шEuIQMi#@xnP|H`Ўɖ@+zyu~yFdSc(z/L/DOja3O\U{Gzk#d!8:?ѱϹDu-\h+*dq28ׯ-bEZ:y(>`DZC-xi/}v|[&7-Kufv͚z(dJ밷dzgO-~kL}L!!x& *ۛGC.I7д0C|R/x_w{w%л a焦}ԲӂٰpzmVDPA7rejYy4m>>gٵ 9#旮r"p5rhS/Oz{RNRo؞9k[Ȓ4nOPӁ/n׀mTk#YxGGm>G9!gc%i/j\KQܳ%[E@/'zA pZ; \q,Gk{3SH0)YbjyIB^}sBΈtMr"#p5978%@m1wef`>c'O5~yc 3#>r/Fמo,X^tjQEjbnDjk/'/6eF_#D?u?gk,,={9rvk4+/+7UgD6; ̣Yֿ3)A)qi7=}?1Mͭ2gB|>`eM$cB7FgBB# vsn"?9r .jFsҽy}]z2؂\Z3q̋/X7i3nWГߍF l|>>/L;+I2v'u'5? gV\K~KApӁvr V||(.,eY6R;)1$u/{wc)ǘz3}Mm`8bȆ~v}>o&a<:?ݡ~} O#m嚮"a y?F>,,d9BeZطis++J6{a5.A*a!d!6.vPA:/ B` 慄<+쌰97|?zv YlO¨g#GW^&"=c㋏~ڮ{j axNB@ðxxPدu> M'Pl]B]~#{BBᩋ|L19xAÃlBbDU+zO$"wN K?`sv $\zdrg.vǍsPytP°8'x{G4M%W7 I /=Ԯ:+-2>tT[ǝqaPUZ|qxLv;AUKxw*e(~^m6qacm n|1#2@{Du= 3) ۣ*gpn[lC>~*dV}`w;k4@&m8"M_~.v{x*<'LtPm|{wDS P/:kHȤ<5Od=x)#d'*ZC!b>ƾMBP,7t}$"^EĐ/98rG^u`.,?uTc|f?&QX0!}_Š3>̒v&JEh]oEi>)!mFS[4mNw?pvToILys"L_F:ƇLS!>|9>i uGDӆ\>ۛ7֊-0 dIpua6z/J||_PmtOcֽ 1w9@4-|[qd;⹀2{ jVpfp^ Cs7鰽y"/),d_H4V`a0Fdc5<7zR*6^Wna 24:WNʾNM/R4 hǍ @T"toHȬ<Ө!th!Cm܆a_<Gԑ]]3-$IR ]vߞ0-g`A^UDzoc122T|He Hwㅄ->OsqݲxTdQMWϧ%%'nul k06a3D#:"xs:\II(N8Et\C{Bu"w&8A,f!!Ajm zz>~`0( 'xWR>jpXqTL==τ3>htxlnr޴[PZY&/z;DQ8.XP^h=KuEEIu{̉pBDȧcj$t:pg8Mv&$;]la,4|Hg9Ia{cznsAu|#Kg?#~2l;/.c-ujT:6 gh3g(@AU'9 J_n0'MxB$†nbnvx9,(,B(>V#5iE>Mi2tKԸ؞SmdiZGРiy XZFs,-BP)79<ԉo6 GDdZ@w |1sc@3<^gsݛQ>'+(~XӴ~$n$Ib.b./lŭcxRPU;˳̰>H|\<\"_ +og]zCٜ:R\n$ɝ {6>n3ݐgg^|#+W\{>g;GVd:`$6t+ QbF?5\—zM}!PӉpL/ѵs'XTL%\O7r@Abfig|#i1@,>h̬2xFwWI˧\yT;nlvDBbrQ,mtzbQxRPgY`-T!bq=iŧFkD>9n"IĨ6V+`L{e8vg1ܳq>y@WrA36ܓtȲL4>}ǰq-t\wc+5<$!I)$,acUJ'6cL#DÛ QMetqqnOƧQ|r1p6dȒ|6Ai^Oh*;MCl;mV|7hVjNnAKSSv BfᲲgy1N4lϟ< ma3+H%~|\ \m(\> J#䖪m{Bpdq3OXMpN&ڝkUccC..T|\93]j`#fH F/ /R|*T` h;F@C4q|xTcq.83,ꁳ > {NhαޫO]8JX]dzY޹T(U->_!UY^ga> ɊLaBe.[ӔƙB)F~eY&IM 15VD \tqh9d5,PP;C۬oXߪrxs$IZ UKAb3zG6' F/6ꨊb!ܤ.yO%;ؖ,,,/RX^8!9?2fIJ*9!2/. J6yZݍ/ |VMrȧ{@rDtZˋC~%>oN\>+J%E-.SǵxN!N=鹹lT\OQ*vڸfh~^Sn資͞vYu ]bv;3Kےr>Dl" Lr=,IӪl5ΕNd2o;ݻwg~s׾5QץuqATUZd! LϕR3SU3q?k=Ӎi|KDvi`=t7ȲD<ӝn4k>w7ϔ5Ѹ4aVӮ_.ܽ{7t~t:믿믿mkkk |qsgI)R!֩=Yڸ#g;J bkzWgR2,J8' ZepB2X7һͶ{z~o0ް8,\4MxWw}{Q(f~ u۷osm>'QQwzNV=FGssmv9*v"Yv<1B~}xeG>*k!UrN"hfc:7J6;yƍҽ{tZPc,/Y^>6`Wgzܣ݃@[;NKP^N]rs]ug|| MSQU|> tZȴusL ;hsp=|',cx"N6>Y }OڭjYutHEG;a:-<<n$$hCsosTQoytm%Sd;M g;=,c[6?]$I\}m M$.->t=0[_=FA i!!>LAȒD!J.ϕXI=kx3z W%_upDm ܞx㚏/[SyNOF>?W$-L\Ot3Iz{|w@x=V9Vyo!d+ K?OtYXi64|A"e97Ǖ= fc8c\Pd$YBՔ%emBuu> Bɲ0sY6Ԛ.1f{cڳ7gZ\gb.Er274GN;Wޙ@CBEQo_?pj ǩ/LbAPR*KQ.b6R0=T.G?٣r\sbADWT1Ycy' {4+l6y<3pea_G B%NRoYA%ɬDȬFlYs1*.f>b)2ZD%iHb aaG#Өh)ee)y<(^2[[mPEUD+xTbj^_ 9`tݫX$ޚBB3%>U޿I ˰qLtUQqHtaZ>ɬK$ Z*_0*.Fi{@.fB>NQX*UO;ږ>kȒ|:s*Fחs˙IdX>3F=Q/ZSD5DD9Z.O%w}R5M1u$1, `*箵H"k>Z}}au?Y|(}~Ԣ]7}.*tj{g$U:GǴ#a`U>^xYްͮc ͚"lŒ8Xvݵ00X $sLwWwuy#ʪ|2~{B}q䱘PRB1kBea\Q%;SD\7[c+ε+|okvn:trHc ַ^7^6l)%bQ m ?)Ml[]fݕvU>w}*2J6c:N!k21]TܵMl0! 9k2\Q%N:Fҙ%\ȋ3b:3L<8}1nTqecmɴ}iaH <إGyvNlnTdj{=!^wZڧB.#+>7¯7y~VL$غޱ)'4ꛋYrސLhS=!<cx ?2u<1*MvnC#<} O_—-zsE Ǐrǩ9NHsB:٫7"v@[xE~bd24K;jYLDBު 4 -tPt~͛xObK-c #7ϲqy®U~75Lv2I#!:gx@)kEYEIiS1%tbxeortƵ8w.f{[|!o{ .Uo2A'.m[{,F씫kZQ׼u䉛Bc!>B>ӿpI< #7rc}r!v5rVI1❹`Z*{o֝0HM%W6ɅgfR=ĭ'ryJepq'Bޚ]cRBR;d7|w"~q$ZhS(a, {}7IM=>n@<ܶ\7\bB#5gh#1u" **X{sEVFU14A<#5`GBO^yg2?7+n{Pm>)=K~PWڍ 5ylH ds*bk3hefTaAzĔ7~)06p Om Gء88Vi)`%3:Fք 5k5["=WZEn,[ź .onOO?t:Ft3/#3-ڷ)3Tcg D,ECVֻRs Ӛ_鰳]?z8N᳏~˼h2&lnAcKkE|Ԙ35YE۞UMڨtDb"U_ffa&Xp=5x ֯`;Lǹe%}É.>]qz<峹Q wM1xN|*,b#gA4 y* f!HN'Bґ85$)AWU4gŝ7WeQ]júkR 2/ln'ssܵ0/iҩ8-LO^~U-M)nytܨ~ↁ seYbv=F||=p{Il\>VU KO~x黚G6q??L".f)3GynSs=+&,ovDl&1$lq) elv6b/~LaF}>Οmw{_>gJʺdP7_K&BRBdz+[O8DZ1M1ޜ"-/UUºkKGdr@x%#t,d9x \MJVU:"5ȡ$ll|ehحs{2y,Ƒ8r:շvdll[sݳvL'n4d]͓nw.Y־BeԐ/y˗ŽciLݔdj๯^"(Y&f =@GskDX+#LLm'P!>UmYrvKVi#&޺˴)dKvK^Ѓ$FLͳh \~&sINu[YѬS4)#|jg0M8jq;rO}TNJ|,֯J>oP)8J &c2 U~)Lar nO-MmEKqܰosX;)9m05gvXZ2%/ݠt,,w A҈#dq횬_ . `l:Uy~}?}?҅oPtH)ا>UdO{-wvpPha\8 w`z2ՓA8x±:;w4]4.3ݿhԗnXxj\KYb%c!oqjpnp׮_"wLQ4a>q58~b] _A)kVVh:5BqkU"ʹVp-ĄKg9zYȼQ"ټlR.>Q,\k~L`g4Gę^~('{/(>qX8l=ݷ$rK5W1 Hcg1^)q$ 7OOhq}xJB *&\wE\B񜑄6_ػ]^2R@`HN8r,4^ ʹ}׊ؔmk M )pNioXA)I~ض X!ZNblo?um>'S+Pt%>-/G^[uI=L=1m~srά[*ĖRӕx%])h'wFQeÀ.eO>_݈8V\8&6[fɮv_\rЈi)Z ] X"\ KpDΥxߎsoa[ئ* ̡;ItG1*an.=[)[X"8a'Бtr& Vz94{Ho+}Q(z_?Anl^?ھ+৹P%>=dgw4p&ӳ R^ijtz>Ε4(Y!xɿL,L0]^]ᩐ3.Sַ^?nXapzb!Z2ڋN8/xG(K]{BLiHXqMNI\Vŝ2vgJ8w-mQ 3.[/]mcjJ肗&oTLb{=m#ax'TtY+Yw'BDiLLBO(Z[l_eFu͕HsDĝ)tٺRvEK4N&&>hٶ,֋τyqg;d;TSMGJ͖dJ٪?YWV|CPCϐI LT }Ot_HUٹ'{-Uvj v30TS8XZ[VZM83'{|[,aϒ;Yϐ3Խ> R1ΔZb ,>>$idp4\!r@*e0JiKGϔع]rj"˱)ZM1"4Լ5>Zp6p_Fa˩`Ҽ3~ajwM͕9$Sf;Sj:]VP£-0KIX\#N$FUtmع3mlOpIDAT]t$8qG>Y8rTvv͠9 s0 ({Nxfے;ђV,12(Q&J|(a"p9i/i 6HcF{,Xd d% Rhr)#&ܬ7GSLܞY KQ7MLbs&&Bk*Bx)L xp=x\'I RI*ۀt$[7vI9gX9ah }?u,i}p3d˅hh2Ј91ו(!nI)^ϛm%f':kB7(F%>CҶUk$ޣѲ#L$S:ɤAWdvɼ)X]St.ġ$Xo=:Mb) 톥;gRVY;q@$<()["H Q`s5Oneئ["[T 5wP)o#s+L(P-1MlcV00HцHs}ǡApؠgȉ*D2&p s4"F:ɔA<#InΕ<]vt7 ‡ɗ!=7PMB@\=TMm޿n}>ŸgDxUhd\$fb$4A2<qJe;e4b)[WFe$g3;H0M-mt4qĈԦ0A9y%c@KBmG0FkC%`y3ܴrcCzZ9ZN6t1bnh^m M&4y7=AIJ$崼c$h%kt(Q(Q34WTb4LuD+JWPP4F"-댆 dzn< s"*J|%FAts aIՊY B B:J|-ӑ ";+.x"#,fss(BW7)*E5眤p@TKwnFx(QRPBP%>=+rADF,J| BwRBP(%> B;J| Bw( (Q( EQP(GP(}GBP(BPc8!f3c{V\2)e6e`UJI bPu<<1Xm9ଔB H)U1B1VxV2."p8f !+WBA!)j)F|i𖋸.5kW=!,YBحTBaqi{nR|<8GlZB礔kuCR_SC">lzζ;N|gUŵXZ>}QmIS^0ZKU c%>RfgƬ׆_.2Ծ(Q:X(xW lsnQ-F|s I[gvm"W3rmsp=bp"GZK)eƻP?{9RRsy3zvFJ"\އ;@k)1/RT1x'lڝG'xj Esy>h4ye)R6T(Z|@,P4A zKA6Th~3וbiEkuHo[2F]|O,Q(}K=샢Gx@{5)Cjݯ~5l5J|y9Me c`E 3 FY|Nƅ~u}bX@[ϐ1MW E4.Ps>C(jF!Dŧ`um+Ƞei#dPt?qeif8NOBz9Qܐ1jv8P4';M]OCȊj3h8Ou9BJ|Q(kj"@RU)WpNP XmtzrEL4;* GM3_5Py|)WeԼB`o03gX?[Zv)a1ΩkviXࣸQp+كvCB6شFpAY/{ڇJZ2T>hWJX3W4xVIH)W[lo7l\=z, U,F|_l-"\iֶK4N{vݶth#16p# ;xߣbV;m3`oA?qo*_ŭ{j}ˬ_#{VjI4?ۯ6q>PC/`@t맖{nn} zk>Sb1pVoUX%\K{5q>Ǣ,VqGb}sy\-%N >,ƙm,޿T[]#K5wߝ`W][n~bfiwyyA+1G*5xnbУvW׭ 9e=/:ּ/m΄-܉r#\C? OWoT"=~ p_Χ ')~Y& nDZ7^ ٶd#asO BB, !+^gxp x\;l!<<R' BF7KRʅ.~yYKm,5y[׾[n]Z,%k289z6e 8;%q*&-N׭*_;4Dy6DIa!k65ʭu,P-s;V湺jU coy^ьBg7{ ]:Q|+]_\Ha_d{R=X|fKpzD&<0 H)ygx3n&o)omVl_O ]nK})ga#A^;COHϲt#9DߚןFtc}zMDҘNky{e^8 BO,%\VX~BrS(9ԵS"\\n }]dxDmu=E6՗ѼJ;|h|f"0_=|ϗG@XZSkg3OԺУħK;Vl_nuȭ TPހ&]nsħY0BӨxm?>X=,ΦPa)/P Hbǹ"ݠSz2Gm,CDぷ'lи.յcde[en4~hA/o%sqJ[VBupX0Cu܁56zU גJQ(fJےrW3{>p,-^ ٺf;J~fV(d)⮔˭Qjȶ7Z yKyؾQYAg,Te\ga6v;-4Fn ԵOY0QOACuMGyablY vo&n̸O? aEe-dj^ >XbTYim}">ZC2z*B)SRו؈ 5b]X >Pje%d[]uq]]8Av@xC?&ۗ*OT^b62ū34:i &+V*P)޿p^Q|=9:B,Yg_ R *8 !Qc_0.qpi|AsM۴^Dxrߨ #e{l=Yx8΁eNz, r7* s? =Xs{YnB\҇N@!SrW7a7ScBݾU yVՉFxǰYr~;`!dh͕&okAv< BJ9>oށ+g/k[㭕id<"\d»ٮQm)e_F^jogp"x w@M {(mګ+\5y5S1Y kX$wS9k>ޣb`ݪnO#Y8Wr:~4hgߪ߱=9c:~ cnԸow\~3Bbs= X(gז- .y@,^[sCg_Oc#25!]Ӹ"(+Ի!fp{/ !ZiQR5!yA{r.к( 2>KoCmӸ= ^E[Q9CKyYaů Ed:/ ,zBTӛE\Yp?fyǧ%5Bnoym;*eDfpGy?o"FQGqOsz[KRʅuF2kTo~Ќ#\B=o}K ug\*Z+s|j}BPwȃvo(h7E0K Exq-G^)Pn1ƋߋRʞS(F ]{I[DY> n%>Cb˺m4^]W @q_͵nB%nPn!CqF[YeRC^!bdB%86z«m,!h=Ee? g;Bq7ʹjC;v&RkªxM16xj^JPV !nJ|p- W+}}^vP8xnfR!Ă./<)WuPnCMՕV/.\"nm#b"QV)+[|6`Z] )E8a<,+WBβֈ³>J|_Q=VNw74~ *"]sXQ~ IENDB`sympy-0.7.4.1/doc/logo/SymPy-Favicon-WithoutTail.ico0000644000175000017500000001267512253362407022377 0ustar georgeskgeorgeskPNG  IHDR@@iqbKGDC pHYs ` `%  vpAg@@`IDATxi\ys{{ggzVC vAH )"MɔLJ7)+$=R~I9q*J"Nq%Jvdɱh+D;@̾޾ (R[utW~|,BJOmF7;nwh7ZnʗPJzxXMw(o=ťG(hHlNJִj7EKv4'T< @黮.nuLmzbjY.+]YR?q{ѱ̽5im}>cI&F2$a.4̀9)̞͉KOФQs uV[޳f]R.PBLeg];~`*{O/(-7 EY%/c\<[gBw/<X@ A4fIJ{K!==\L~pp b_<ޗ>Zq`)ϝ_(z-"TRDE< 7QCN4#qEۧuΟ*BtFLu|˫߼nhT/}<ww 2 y4W8o#*gJ,nVX_pu %3Z(P38dz:D xqY^"V.'uCխ(썒Fh7mVjMz=J:0؜:/}c3f_?x2oNul$ʥ2pqo݊o| VlV7wtrh|[cj7M2<2D$v\^Q9"ΐN!=D~lϟ?w)A1 Cpɇt}x`꿚~ WOؐKׄeN('Bů}1uo2C?ŅSM=jqn=2@Vv RJ,ӤZޢVd|M `yą5VVK.لc/r\l8LD a*UJgbKT޳ph4mZ@'Td>ɏ0\pq.}ESQke%z;"H3Y_/c]A.3RџOrqĜ:3hI %%m:W $gcbH) oma:i]%!&)4&DTjE2umTD2 Fյ*mav;*ŁLzHEJ$lk'~ ψ"@ 04~PDf:`hikzģ: sr kseE*(,c{Y[#5T ã(FJ*//YƱL}躆c8Wͫ@?f|r*1fۡS  𜀩fΚ{|aٻjBlE0ͦB}èOihR%IEMed"N.4K%J2gNKߗ'|I,euiY (A&ȄI\ǧ8`短Ķjm/2TvL[g16O2đ}yn9VR/qʦM2꓈ vMɦF BAkhYH "P~fu3_ȌF %*NWň) i\m l 8e֫=$M!eg~ghoT\P̓HD/mpn=wC 0{%ē @8ȅϔF?=~jTfPX-G\ڲD[ kUaF9}F8]rG9@?_#]MӋzj}Sd1rٴRb!Wy@2z 7*_ K<<&!Nt߾x.wq.֟}b z?q< CNRp8pC"ʋ.>Mwt vk٨  D?d2p凷.7w=ڡ[ yJbCY&R,\l3l ȑ. Nv=bgz|8JhFz\*d Qq+%*#mS=9L>^ 2*ãk W߃J/Mi ҕm]=srG%~am* )g8qG=c|g|y/%ߨc$##U޵-!J{,|q(.OrGu"hZT*P5O^/UX] Ifh=t\X @6~uz34m R AZ8V$!\"Plw80\d2QHsCyd%11 YtZZ7t[;ǃft(;(oQT Pŏ'!`_PPQi %~EyjiP nM /z hT|#&YaiMI nD;$9UdYgG*JG`h?$x(B^hOIY pETZ6RH% -J:EJxs\/1xLJ6hX H&i`II<,6;O4q3UZoavM1X-eVx(W"Rı}yKpK3ضǑ=i'ܱK>L60Grvl%ZTa#i S?6hH `TIpCN|ư=,&];"M+x++E !#. x]VUf*{z?0}HXgDϾp:YC'4[j0Vzlhl,w68o9 ͣT 69RS.WR >6Zp3)VU>Iaם& !B҉9ƷOX.+;w 0>JX"Z=Tu|8հ9sEitMX@BXH)cK~dR- ]$8+})^Ō+#!ǶWW%?1MPH[D.wM928/r2|FQҡ0\Kk33y:<'ϡ;_U5 zk9 GBh!!q|M2 !Hg~>.rVFCԢRjH6 !!]G( BRO7p?$B /ǛJݻjEH%TRlbKaR-(o5i;MSyvBD0e!+ H3/E%D=|6qmg.[{֝;]їfb CesA2kUU)mTLaxh,ZFxTtMP`="VBEay楥[}V_]'fb>~q(0ԟ$D)2؎?rCACA`?|uE>teG ̆RY^p?3K':Rʫ} ]?~3O$9\1NUK }޹* dǟ9ǓAJ O&'h(l-wo̟]5 QZ?[{߿n+m^4q5TqCS=Xt2B<ZN#| xi=/dIw. . u+xN7D>_pq_q߫|<=<j|wOPΣOؿ'Fp̒9NzFA.)^3'5up߹#Yij4ڄT#ɬ jVFsTiTC!;F(mFweH=~+|7EDVhzT}&fWN\{;nwh7Zn~O^gr_(zTXtSoftwarexML.MMLTp,J-/.V`'6IENDB`sympy-0.7.4.1/doc/logo/SymPy-500x500-Logo.png0000644000175000017500000052711412253362407020367 0ustar georgeskgeorgeskPNG  IHDRߊsBIT|d pHYs ` `% tEXtSoftwareMacromedia Fireworks 8hx prVWxZkz%R `P 1" t&QerEh3]@?;{PۉJDs蟣~xǧxxw#͖S|_-x7k~Lݭ'ݯk~FY][[v d?$'9INrn$יJ2mu$3[jpn~\pmW#QÍw]WvUYi9/t5-@:^UiKRλ{̠J;eE ¥LU!IlV$6[tr.b^ьvv0:ܮxh;Sn:Г/zQPt .v| M0lJZcs<Q3.q&uA>OfXHb:&.dSk2 ȢYTexr8܆*kwo]&QRx;VzA*Q@Y+@.;aFpt6MPh~x~LSC4 ˲YN4d:\TeH!aI($do'fa S' o5R86 W\Ai [`Lg Ui[ȕEs|_|^2tLb?A_(fEn:U] MIYHxC?M  <`( :.V^CU۶N$`%*K3I[Lz>u(}+9g9A:-UY eT? nѨi))k@AXP~3MY:Phs.X֣tt ç2iN*j5p?0>' -%X0'3*,$ihj);%^WX;&@VHo@%&:otD Hl`-p^&ܿ2s"GL,Jc6F3Fz}1&Zc\C*ԆnJ"׆3j҈g-6x!}c'A^&C(WϯU2wA[X&i|,Obs! 59(/2H֢vpDm$,~_nsDzQ-XUQP J?ߋ/.,`?:>H1d](ZWuޭl~5 0֖Q2eWWo.L(҂q,d,BT}, ܀j?.IBSvY?inhG/[0 THQc=M(`̂_ޤexn8`4iCreP^_xV9 ]ɚ;zYrY6e /[5~( -6_@肹XlgމRD6 )@?S߿< ߿b \o1UF_Tޟg##dӱ*"n3_ FlSvZ֭jZ@9g d{l@&'pKe2Vcŕ 4~~ zn&RR h/E)t\?=+>5«4s&f2sKؿDh9?Q= >.?Ρ-[ߴ7p33ϩ(~ ^d*~a$OrҺBhJ2iyDwJk8|γ4 f -7oTMwm(=; A߱o!8pApjZVU8GU ]0RB_fQŻ 6*p7au7L{m.W]T2KN\hDt>nc$/ydus[^ ֯uP,ܵxcH# j 77Tvd)>/9 cf~|(A[s4^+J#egA%(An@Np2⍩W<șqEy!juͿB?-k pn&.+Zٖ^~4G !0~=~\t=}q*}FiqSb~<؟"66= <(CRm_8mM]=^ffUX*W2+^O ~E{vnAMI}tx^A>Q=Wl pL?m &tl]}`Z`t#`… MݪCӓ@`iuh,[w]he{FBC%?TH9&FYLS"}͚oί\YW`X>˕塶ʒ% =C1r>edp&ѽY°"W8FS||`"k$Q2NSNQB*g>HMHᘷLtj!/%Ϊ ^UOq(K'rM|n|H Fhk~RTh/Y%"xpj jڜ iOp]ƒh-: ΰ1cf`" q{oRa(ܘHq -֓~(_Q.r{YC-0E֑Y'Sc2id^Krc)5r~Av@K/?j#W4O|OԖغY$H`MyO( Z"E>wƇ,^@KȄ3E'_XY^o¥izR6?itm68 =i gEEE|> T=w'Ƌ{EYk;ZY˂s1YK'3 `+_:zؖ(hX,7T2l3`-m:Q>-tsui #ZqS -tJbv' ioҙU/p Q\ r,E 2;7ЇX̠f?0¿rIƜ@k'}V%]_y>؅2zHl g bI;4g2͙h܈9<4sD(},42=/!(q잓=uIg][#jbDOΆLwew~Rt1',Qik5F1_R\Uh1'(DkZ&*}UBF'\YeG=b1eLnEt.99U~I;43Ź=ç}GxOSQʋӧO_1iD==}3s~!G&M9ţ{ݍnjew@aypbb0muZm->!xLTl_L[Ӗjsmuir[qDl=)ݺS==LGhWj=Ţtb: Vg0kZ0tIŴ1mEj+٥"8=8.qer;yN/ N'kz٥šMcD/..pUW߮?%oZ~"E;;a? 8*̪] {]QCz -9a+uFaUU[zU%-!whŶ*_N;᫺UNxf-KtWP?D޺tI1A.eU5 젪u.kםک9U+HֺwMn;8]Z g3 ]52dh*TȌMUۋUBuZGRFݿ+xƍEqu0.rϴnR9O?$]ճ܊`)5\@w3.(:7Y&^Ewjܔls)ቝ*S$z׆zKS3"# )]A(G {llѳ;glaݫQI}$$k:cS{I3f{%%2YFq߻3+<"iV2c߅S11k=2' / ~b`y΁<3?(_neImGP8=!Ϝh3ӧ$#Ykg+{uT8wDf~H.18:;OV8=ijRŊ~rWxJΓeD69!f~ %o 铩FsR urI'." EO3p?O+=~(C{mEUѕ3?ᶐ~:΢#;[=-I TsߡO wQ9;pUȰE-4E\Hoa}.3Ug1U%L_V?jYi33wG[oqh ߗyD™#zF/$[,;ʂ7L<y@*. ~1M0˿+g}9r'~Wb:)zv“w-;ܲs-;Ku0e+2t-C 2,C+`0{C-C 2t %} wV9Zne薡ڸEa6ӚxFrv-gr4gY5k9薣[n9Zst ʻiZvnٹe疝׋\y7We疝[vng盯f\y2t-C 3WgXy2t-C 3WgXy2t-C 3WU3zk9얳[9+9nW޵rt-G}w=FvnM+cgѣ\u~B"څ9{DzNcSӫYo6nBɞQȘzck{ƭm*㷹Z[yoygt ^6)ax}elx*,.6]8 E뙋+θ CN~WObrtkJmp6y7>dFqsO5~V-u#8HL,Ӂ|c'r7aO1TL>dvGprӽط+@6E|ɖq >KuQo`21.x&_-5g$]->KY-EJXW_nzS_-^0O_w-l+.G1 ({3)օ52lVy뺧x su&F> !_ 5;\f^eRٷge=]] $nS\rvv~BDkM0}⚢0ώtWysFRVd2D> ` hۧ2)Խ"k\nHlPýgQxKH}}~JcM1&>#n"UyV#[y#Ʊ]Di]]7{uqE?1Ƨf#{h4 cv}]l\z9[;ů2um-,^g5@՜J9˜LZ!W؟+b;̟7:+{R*gK*)(*eg#wZC>}Eܜ@'06eO=a0VIz62mddm5b<+y4?:ѱqMg匪 Դ;_ONraM2ݦ[ۊf+yS{]u ;Us}\R{~q)M>:_"ߏuuZ~z5К4;,wY1G+'XU\:t1LUB9[ =k/\L+Fŵ?Wv+]<٦8b1u~?e٣E>qNyGW\K{- hX֟lQ!폙#VBháxcCmɘ-L:5h.bR|Q,;3'5k C-8pWPׇ6Lw} ~y2M|V643ϡg쎛Z3kVS7WھL:h;{KʽYVS섞 {6um]EVߋ,~%*: kef -ϼU{ߺrﯖhaLuR6RhBs]\ŭ(uPȪYe=3[YH~ž#EysgN6?uy%`tt$<`tM5#R}SFmo|/{#SK>nm<؜Su6jbhQ }<,J9vu@I8vv G=\ހc{=2jކٌFބnScnն_{| .wCn8x_-I۴8dX6lf/ֺ\;Y!zfNOyXvǶaumfr<'o#<@/с vm;~/o[[&ĄI[W>=rY-֕"ʆcQ l 215'G6CWtFpA:>2uY͸xxDeOQ(_!&wS'8=0L=aVBܐ)0:yupٴЁg_76zk7cf=l%B<&"b?C.4. OVHj:aF"th-YZ#j:\DǕ f^W*5pBYF4$&SeEMDDщߔa5 XYv# }YF"0 (4Upp:P*'Hے OwjkZLMV>\KYDedbezaVȨtʰpXprz-eWZ'qHFd!rl7fa"8il7F2tP6j?bY0f2 WUHnd%by'/-&frNsݔ DF[%dX|eڍdx0h2lpnvɰ5KMuxn:d^ Jwe6"E=xn~Hnn/DFli^ =H,Vce6#S> TeeU(HV.WV}x&80di$q ჆)-LG%-}DÖHF"/1YF2Ms[9rE0n˜ MjdFP;]Ov&XK0B;læpFogHnG4q<{τdrEmkBSx]N0Æ WM- 2*5X|DZ=fLc(F? hҶ BLrGpodgd/e>f 9]WA\\Lf[xilϣTs1H QlHاEt(SAZ_Y XmmkBT~rxݡJa]q cb X ,C U󄧿+|u8;U=ߛ[=| Io@B_Oo@GGGzLo c@iz,џ/ ?NПP$ _>Yz??ǷG @њ[M}VA:Y@mkBT~xݱ `Qhhj $Z  W!1M~K7mQZiצmviz?2 ? ޛGGG7?^& ϏGGGGGAz????74k >QuimkBT~~xݡJQa/EAt-baip? 6AP֌+bv* N:vk5jjmצmm71Jo@'" _.7?Jo ,`G^_N?7?????Omצmצmצm֎n vПPvz? k[fz^z???ท@Jӿ6?hmkBT~x-JQab:&ʸ'jE,ÀlP q#p^pJUSߚߛp@zހ?|757x-fAk__[[' '\7 GGG?_}ހs7??????? OO(M&<#YXrmkBT~x!J`Q"*X(V4-0QA ܀b ds wz= _}tscA5Mncr@iz? Ouz?O _]7^7??>Oo@GGGGܸ*7MnwӿQzG'˭//6& C_M/c`l<4wmkBT~ܚx-NCQ% !iB,yiRQנ00$! 1(\#`&QnqX~';vڍG5jjmצm>`G џޥ7W]z?sׯG//џۓ' k~@b3Ko _7????3`mצ?.G \mkBT~bxݡmBaaF!) 5 !L +t&t::Lqyuǜ0jQSӿ4kӿ6kӿӧ7?ۤ7yY7Ko@GGJӿ6kkez????3P<`kz?????w wiz???? Qbp7mkBTx-JQa?h"lNqcm\Ab\;9{{?pi55Kӿ6kӿv:Io yހO8џ@GGGz;Io *`G/_?_7?7O lS_Az?zJo@GAz+ kn5`Zzvz???=;Ho ?_TqwZY)>mkBTx흇WTY9ٙA2%$79HPD%*ID 9a<;{6ͻ{55 9sfsoݪUu@m[ֶmOӆ Çɓl.^OrFm~Zmbb̥:QvlmX~,֒8Ǐ@iqoϻW‚Z}}=2y!02gkmMԮ_˼X+Φ8gsңpyo%av'x1+!2G5l |r~ 7mKX_p .1PU6sJMk333ZkTvm+:,#kbvI\m y3PqӃTGn.im:.&kbof낤9B aǡ0<_7 l70^[ZZRknn`^D1Z LKp szJ3GSjq}pdN 0Gn t9=(X2~?ALvkw&ч9 VJ37t nkp;40G~Itv*.)Gg?,|k===Db⵺B1p2,<*YjBcHNp74+/c`iT^.3No0^{@V"U00syU"=Y( +q0204NL;fl m7J?^|6:: Z>V#jxM^ϟ,smuvй&H.B\} ^2g4OSyLC d [A9$ 6˻k_4TWW-^;Nk=`OtjFY}wѾ\%==7qoDi0*ÝѹQkȼh pFB?={VNU8^i? 5>Pq/o}4sT;m~ΎՈ&V~Ѹ)VwJduBnR$dw^W7^c2pV?LkMMMk 񚐦&]k.KZCu7~G~Y;F~j[AOuhZ9Q&Pj NҢpnuk-%plHxT=xS)c3B^ޯ_D)dB_s9>ӋOhmIݻPR$7^o^A|=o";d_ű#Du NI3GavZoVVrz;xxMYǜ真z֞G=$LЧ_G!]OsyEK+SClm~Iڵkk׎azn.ïDk|Yx~zF)1߾dY{8 xs|?x!6w87SOÞga߫-//=^k(KkBzrOCC'4ps2\,,Az2=c3^yQ3}P~') z?sZI>9uww;^'`0]^AG=a bu76DZg:QvT]q+w/tdO@eGŔ`|:J3F` %;%Quɧc#J'_'woǏ5VϢ@6=Z=^åh+$Oqjp?3ràⲋL^s)r: {iC#H=fKuA#j w%2L622xmT9s/`=u*wbcz:N&{t2g6Or`6Gu`OĚ?ko޼Ygv{+ 5-seW w.W]=~x=߿KS!k,2=eN:Aj|d1x(6-H+#z^Uq_^˛2^#^:R~ e_b9xv޾v}ꠅ̞#ԕy\mU2J〺RZccl"s:ZUf9seq=ISNc~v'^Zk6pq?x6꽠=;A[x֭[.*T-9"4(9\oQsX MFV/Ne܃Aˍ㵻wuuu9^k-+SM*ޅ.πs'$\`!{&q2;B@B`(ܛivHt*F_~rgyB}w<0@c|zoQY4sq6SǚkE쯼Jr^;YO9:>^;;+բ!..233+w㵱Zxv_WO.Sp9K)b}ɭ뭼q>}W?G`{E{vX5*f_y:(^{a?**J8EGe;~ rtV=mm}e[kCBdc|u  4ʟm'af"q L {W"!-535ŹQbpR%K|y2l 5%sZ󉟍3ޜ﮾j>\[Wk0Qhځz?ko"\f.4sp6pdC?. Pƻ0/9:0־ԟBDgt<vRk.z}}vn~ \`>~aK!'hp|g(^#Ȓ? g xOa= "ڍÝ"#%#`HO޵̘;_O|O qY/v-,H_ Gv1p4{Ww {T~޼w%dvdIz~6˝?++#8kQ$R"!'+ k13B1YȤ^ +5%Ѿ#yv'|䈷>jrc/nҔ%}.qoCZa'Ԟc|g (W+NA dAQN8dPAx(y-?'.uX='~;n燊J9Us9FeKRXcWvW=s_OX"}bC^?j-^V  )^Q_^۠9<φi*X <ۙ ԥ5l>m\v}=~C"};DP bGa e➗%$^l}{38cTvN.5Lo 1$Doi8ZtzAcL=Q5}8/ggAia,6ҟZȆqΉxssbO%t~l}E+se1{c}nk8H Ȝs5Yj=ǻҺu0WDBgK̝Z9N,[T9@B3t[cO<#t_G?AlVP m/(yu\ ka;+U|$|FNω_}1J}~XD5[1AM87s CPY߼aU9dW8G^Pyz|Hx3?{2+X)v{} lzk)s9'r 1xN;'W[Q8F̔/;`!X>2J]D|| CI{_?K 1D1L[8Ixa~-ȳZNL]Mκ[{KjĪ(/>!>ns"X5@ =_!hTְfo,tF5@k5)_OۑsZR@?X}>nl=ωzD/iJIk)^=LO1uтdr:Ggl6s" ;'ek{pi1a2!QYZ%')WH=[ۓ=]Yq>)t^B9N8pgiCOg~s)=k|z\gB|!>MT}_{oelɟN?s5?IW8CqgO ]pX.p7HK?D s;멅z+=˙_rU˺!6&3a5e+fP'}KU$y;@x~Hw+nWwƇ;k '_y >b =F6-֣-w4|v;i/tMG7_}?5LVw 8'`VZˉzB~]{(#EFS6ޏfD~>Δb WkpnEK ,V7/r'`siE4TϹ=8}~™JX+Z+t(z2&[lH W1[Msn:TGbğ{~G?U{ ^m7L%>Ss0{~~r7HX> 3]=X{j=}?gY=/|dv8SS,{MnzZs=sb /TWWZj쟻1Yow ȶ]Ocf]нΜā>^zֻC@36Z5$Չ{l=-˟ٿh/ COMRC+vwVp>=1u^8%Nt[Hm iNmr7HMT?^o){FZS gs9Ľo'b }ϔoIOi CoT`Ϧٯg ,-rv'H`g\!~46å7S9m-EOV4܎hh)oB g٪hiʮFz`l fDq< zWmo?+ya Q3s{PvvZe b&E\ M ԘzBrSY!O(lay6KߌN}p$ 1V_B< Eg~s ,~{_w@T{ nLBCU<#TxoMdӥ#r~"tQ%ptݯ7P}+F4?@CQ<='5wzroܟQ~3"N5f9;/9I{糳q^;g}H:>>'Seg3}f}a G~k-v/O-A"w9/Sã\s?'^YgĖ8zNlZJ}`)FMsN^3IE$wT7zz~ f̌o!Ftg؋`K=N *ezxۢO $x{Q_Ž9|l_z̝>{Z>es@XuzT"z5,?w1QYlu>3]qpaO{{Ǘ{Il1=XV]_PD50e#_/Fċ"ovTe f)Dϯk{~D+4˟-7P q!&q?{7I\gR ~؀n9 Dǖ#t_NrOݠK}~$g}-fαz?,7Sȿ(y|6KP~:p;_Š};Y7Q\+f(7 WU6#T_ q-J 2{aHuj/bzչ/8#h=6ԥl@\aV<2z4}l=HX3|@vl?Iyڼ5rAYIp{R`4 n!>fVb޸oe ?}J8DYs$i c{$.&gnzHz`jc*V1~R{lade޻ߘ*J\J evxg7D),Fg@#<gTw/y߻ 2!*ܬw>Ӡ=4}ЧoVH~,AgֆpyΕ{K^#Ms?;7Mg ʌ5 V|3֓b3MpS< >bV珺җ3b>\ͽ ~&4!?0u`/(Yjm0_o''^8Vh :KstL9li»ߵhOw?7n/ܑ"?vyN|k£P ַJBM`;»n&;i#`z@wW}ΪY*zuE >W]Fv@MJ }q{}k@OΌ.1gDD гO )5q^|}[A{!!"?FY\ˌk@dσ `4̚9DR^gF^4k|;udH}_ 3k Z]"mRŒ! )Y7?H}Z9)V4WW%fF?<-&{bׁ+t IJSn4C2"j'iJ+ |_y^qP1eÀ]َ-.ls_j0_Ns_VbCXo>0fl gznCőD8{tD<ecRl,Lk6P}Rr\v7손 cοD5`W"|gbs.~oGH!ᛁ?[xsl]̨s"*y?TTE?x*kp&c"v5P,gQ/!tأpgȘ-$dzǂ;ݛɌ_Sp43,1Mw\ցYKLͅ\{aM(\ Ԉ]S}%2sCS ="xf`/g)cbzmf:xJgZEQ֪ 8FـQro[XǬ/_'(?3Q-ʡ9b8[ҵ7u/ |]0 7l6J!MuXoK\E؀rL·'GՌECPuJ*Mk p#k@jT"4cE?l nW;Rҕy`)o/v^i_nfa8V=}2kgJ,fe eR>5/)WIgT= ]Nك ?$µkQT0BlQJԇ򷱔uvgJu' grƬ9OMq3'uYx.0s&L91_jlmFl+?Yo#7M_9t(Ǹ пG_gE3+CyτSɴ=C!+%tA@.wO_QsIgIπ6kmez0)_/(0G|4YA8/׎*NRNF.Da+&R?s+]tb  3嘸{ ) 22H^$jҒ1kZS~R`X33/Z9%UK[lstM8kB7;lw(IAdckY-bf#{JV-]0v77mײuP|џ({d?=/?I&j53n1o*ZT>ew#,WK 8Il_cs+E׼NN@фmO5?n+X^ᚐ?;63d{(:@3.93"vM)#~F|8z sP>]`ώ$Q~,ң;t ^:` c.^tV!g |Pw&F./4Xw. 2? ѽƐo Qt\W-("GZ5 ؏5HU&r_GV'mkBT!x}Uw鵶e13%uleY`ٲZ3@&&$9׹;\dfKVVUNY[z7 ObL I{ocj?+y0>MNaMFx+nI[|B詏`uG>$IDy -<"jPZQLg پp;vV?:|<*9Řf0CXh{ u_Zl8 oip&ɂ[e,"8\IS.,.$9z^ WaVp5Y’\L8hS|rQ_v ˌ<̘)JM44|nqYȟ஽l]D\jSx*?3yH)daRlSZȆ@1k4ՎF nW:<|_ŰLzaP5[Pˢ͝@h: 0M"݆̆h}7- A6 !1^L-_|zH:áy/j | 7qlu>_E2`MY|'}>etf/3x<i:@^( M4c|c}&3cC%zx2Xu{"}? vVT2rp6`=ړw^GAfD:sz;AӵQ{)7w18;Miq(2B2c TǑ=*$?ꀹ2oH\s#E}-Ǽ-A&*rǣtb4YcR\1c #3KǣFǓ*D>WGA;\Y#m+UGAvT܌}=/lʠaN{+-Y11 wNx1U=<72dq_ aFّ~*T7)\,ФSe~"|9/wq;WXtvCv!<6s ?fNce/ =d<&5p+ʀ{Uɶ*Y?{}xmЇ`J.X*{+po֘,hC΀̿a+ L$9 %9R+Wh`eۄ-z"}9.V?y|b="R:Q&Va` 6kaL /c^PNr25 n#;g|RZ-":/g`D>1'\CO{Ko7gD߱/xhq\& lEb,cH:= tug ,d`E #ߒ 3:9KP Uk&/F"2g:1n$֪sx?(ڌEy0$t}[{ϛ5-Yۑw'-@̴ P8Spc& ,"дږ wïbυnSb]eF_L"3һ r9Lj&uuBcG3`q?ڏb3<$#2%2 FX*32Q9ui^rg5|z[~Η+N tn଑eeX-11 #7&b1D'#Ax:Oe$_HFh ^@@I't&*Ő s }+!+d=I6ZHgDd&T?́P('?!A/"b<%#sx}'?삊@+wdWWkQǫoKsM^ڀ2qb[0u7أ1|<ߟmP(k#>6A6eZ(3,NT[-cm| =v}#Rƣ>m,b/ecwO!^ڶBcL9k\"66b= Xsc?OVV)gB?؃I&p̜! &9A5;`m[\emm0OCM2*y31`;K`{ǝ\ uO>ag>)o)x ՎWQ0M?fk+裮R1y$xzQü oI+eyv"Ф3$Ϙ2 >T̹pS VQC֌X3S.|Ȗ7c끈q`B/lLAoȚr<x. hUw܋yçz0ƻ<֢leh2=r&Yo<#FOG=g 4v9U%/~ZW0X50CxrB1Eb ,?kÞߐ!欢a/\R0G#"ﺃouh }W͖/ưb;l ?uT9k+'_ Zk h1ل+6/Ę5Yp>|FGy?gF}+_ ?_nFx#Up~Z)aMoh-.#o(X `g4|l9r:7_tߒ #{e0{W~8gh~!`|ZV1 {X*Z#Y\{Aάo/csNek>Bs5OydX} _@/&ՙ=1>DKͫWac۴&|"|]ivetuC'OB/`]82LjV#eP<}CVT0JbX[d-ěfl tB0Y>\*{jt.B ;Xtx1CB>{\IaPO=r57(z&N?5 /~ |_ae!br1 kr!XֶX 6kde~ol;BX?Bc={ s?RALFGtMelGCZ>/8KcRӮlZ\j l2^D9h8׌ij'YnΓ,9=b\#` Zf 8F? 3?m))I}H KHVu.~| _ox+uW\˗/AAvCqJ#(nTh4CW.;ۙ =ݞ]\e)>^+?+2SG0{]dw 9(3|j{ax >XZef!h`rO Oth4aaa:V2GmT.r!d)< \Cgc r NV;R:'5 >G!0ESX-= 𰧀a' ?Q'Y)@qi'8Cl>Z 9 "HKTXmpw @?h[埫_j A֓/4]f\D@(Յ) Wݲi9ĆA%T|gde ׺v@K%%7[I&2-Gtߌ}#^_^R1D0N\}|>:`D@p̭//CNkr#I'Xod@ut0e1;_GnV wVZߤB6~XwBb#G"dfJ#L#0?#92?l=k } Y@- m9cG UJ_~ Cmy/γ 3P#qe -a>("1A}!z@({ eQ6l*}v{l<7s%#Og 3sFTĀH_as_;5R\Й?L?ng p?Oy5sZx~(ΈP(žz7 |Ú?μ2RٿV[VdX&`ߥbbOEq뿕RS]nc }3&'3@iƞs P|fs\h{7T͐<3/f=Q,m;~O\-񏻫h?39T' ^ :-g9YQx9hpGqYgh^xQ?m=Y=ڒg?㾸o=91B8ڦ_!OyKC@N竛?KgHfUy9S?)k9#l}cbu`?-&?AT4Zj;.3<%3{V+m@F}4|sNl˜M;w;?A?)įi{,e村{,ɳyq|W%6kV(Lw9)V5 C?}\/(R^/-5S;9䔅I@)WͲIsB&BaN8;4?^ Zg'ǒv O 8YRk@l>c܏8#U|op9?inn"P2;on4 ]<,0hg?YNOjlӝoI ]6nP{Ahflǒ| pd}]B/<ؘ†b ^ [hg4cCk*?nn>2!q8wMc##>e瀑? ;N]cd.5Ϥs~-_8n=VE륚i;#zE8PQ'~H cq `! uPϿ-F c}DaUxL 0s]ƽgc~Ol.IM}uJ* ܫe p?B#-B"u? |[#9_'c~/@Q$zZV ¸@B>Ԭ>О}X#p/xoklD\tnH#5׀.J ?`>t>x[p:ƁʿÙ]?5eTX,25`n^ ~Yǯ^ lݭ`k=wJA.@4'r6-{I_Tnv!+hܺ=s[B.Ac' -ş_z#?q@iAϷLc?r@n>~'H;9?aId|#Ooku?惍ƽ"y,?#g'Jo ֽP)s.-f>6Un)]|~68 ”!~?s^o̳d1XǽyRSD=9b8Z]l=-7kP^L<t>l@kC 02jan~s/Hr3}h轭#G$ݟjUqx`+^`?9F\mj+(?{\b4Yo=xP@?뉷UǾmj;ٽ\?Rߎ5`Q ԔD?}26Ca R;Lo!yJ3]=6rs?+\åK .ϛ2lO10#ݑb|=1C-5Y#>6$#b]0??>W>&*>:ノ6|$$ I<$O /ΕJ/ 8Ӝ=Cz?DS :Prxg>\׿o~3Z:$(p/7P%kcO8Buk1ƿ,w>0b= y1 =1WWcc=cJH(Qs5߆؏'r @[oAٻ=C䬹%|!}%u~wDݳ[$]G`?!b9֙F|qOc]Ze7jN_ UܳL><.Osu^ 9%}~<ӱـ'>磄;_WG&roΜT ` ~an; u0sahz?!:>|3t_S·$a_X>WKo}?7}GΦ'i?e< 2!RǢ!i"ӡGD=cuP;_??j+m&{mkBTACx=JAQ*6cF!ڙJ7l,l,\;w=/<006+5jjmצmmw@sހOgހOmz??Q߫N7?{;Jo@'<`Iz txq@oaצmצmpq@a+]/` ӿֻHo oހOG6kJի--mkBTE8xk0@P HJX HJHX H$Af/\lϏȽ#н^p.yB^Vm7 W]8<浫/ӼSiu9q<ޢqkV9W—R?= ]aKxoܹo 1r%C5&S<}Z;֐mT[rZ}*ȳ%~ddr-ss,ֵν7"[C%,oTbۭko}}{Dov{;G~KSܖxV>ܿ)\kY{9y*Vǿ\`*D^֨sbN/[ש#S3MBȧ7_m?iȥijo=ӖxG uv/hnka[E=2V5߇ym?M#2j~\⿮9@Q࿝=w_{~!^ ZV|'4ޔ+E{x؁99pꖿߖLf!^9o5{yۙy?PP<"޳_V-mlߺ'5<ʲctqoޱ@[\VB1Kƻb\Gg#ſ'S!S$^x3/ݬk{u>P#{s=v8v w}:hDz5Ўs¾ %0vKmkBTK#x[u0E !  !! !a BdUg֍GI8[mmn[雦9NᛛW;w.w>hoY{uD[xKKMN߂vp$ow;5wbCÜq/4}98ǎ6G.*XEGXig δ*kɷ RUh%&ro-*ߘTߣoҿ9??N?5WU9`ҿ}ҿf' >/Y:~)ϵySno L491kCrk-[_5|5?uB&a~xm}Fǣ퓟gٯ]Kuu&Fq{\_ȵ9?7zv?GH< 9;}`_o?޵8֙˺'pӽ@(9~,vi'}?=l#m6u|~¿c?ߺo @9<"9r0e 6S1,OZ!\S6+m@kզm W#y19#ߗ3Wm.Ld?YS^m??-V|=H.yzuFr镹\V rюNn4kC:8C d#gӊ칎F[+>밁|7jDj旣d41_ϰ #'/o%ھ6dh׆a9=6ӽ~'6D>hKb"#;s[㱕 HLs%c),,Gh|8k<`?doV_k<`_k<`{r='{wrA^Yùy/k^_sk>|9W/9;~Jlo߾)*/N\ϱov[iZ_ձaJΝ/:6O- 92b?Tlk%?_21B sY5>:>c=1Ow y^- ڶ,XzusM#גU]>H_yYv!ۉ_mi Rus]Xm_g)YY)m]y,m z1aaaxEߓGקo/Y\k6xjgH|yu.\aæM&wk#ϐ$?]Mo\Ⱦ,/ڥQ@~6s?)}, l gX #vQg Bٙ^uのuhm?}{].~}v_J;xogJY]޳@.)oqC?}>@Xߘ'-(W? źvƔOʙRv[K?[A}?-wmՑ}g\=c}M ggg DŽ-B^k_g?F? v0||؎=ǧHPgs/hؑI t~{n^}ZyD5XWvO)"c0vY Z|~_%/,p\ɹyΰZ/;/xs_9?Pܯ5ݻ\[y|č8gʱL{? 0 0 _k3>z_\S |<)b|7aaaxn.ta?l^Cvkؽ#~e)3<3^kdlc&jK+o"e<.ʞ`^(3zu l+6v<ï k7]/lc[`On}򚄫 G뎱zt^v2)?;Wmr5ocIz?Ozx{&!ez."ѯ 1Gg{+ҏlw<=}GݽFƨ^)zIpG K֜{{e G12ۭqiumf>.}~a? 0 0 [u+7Svq֭y΅ ?ނ}XwŶv?ߩDZۓ-q/?߳=<~#>Fk"qzrQo 9r,nY[;o:)@-`ק-7({߯S@µK9֠ɸ>:n3 _[_*mtcmC>qSL=<6;ǫsaaa{xˌ\ފpx?0׋#5zяc]x^l򼠕(f:~٣^lin59W~\;?vn6erUbS~v^U O7O(|;+SG4|?f*?rW~2oNٟS9~daևmH6mX[J~s.ym4ٶO|Bd/b5ɿyU? 0 0 0 0 0 0 0.P~*1@G\⟿KrKXs2(ߥ纎J8'>X@▼QQbqwx b)_K|v 1M6kee-2Ǜ59?K^E~9ϱQﱮYF8N?~;:=J<-tĒyNAgC \NXKs)'^Kg\~2}6}Գ)n]Or^j~"{p29w6/.z-v:+M{WJYZ굢`% Ҥl9ힶկ#OUz+U?;sd~vND7*.Y+v:ye;8}~|+ÑޅN9}{Bƞ#txխsXɿkSV/uJ=o G<ջL'L:D]6jfgLz/+ؽ[{rCMYq~[{yy czA;w9zszWHVax3 % mkBT]px}[TYS}/YL$Ifr9(A@ 9 "I2sFTLl}gw<ӳ~7`P@a={ww>U{a||5558{-<\?XpRQTT$  o߾~7Ǭ믿 7nhK;''!Kϟ޽{'<wsyvV28deXȖ\~'Ē!q8ODιtD#3-ٙ Buu099)/bS%@fF2T~",nN'={'E?+=Z>|?( NuiX !\=&_~?~Q/? / [?._{>'`R8 :S:0w>c]i^ҀvAX[YHp=M[{6`_0<"X K(Ÿ Aq^Bx,ĦG BJ~Q,T 5xpeɋG?YueOѽ D%9+zg 7z.8x 3#3iBeS>,_<%שbb_?246 A160sӅfX`s`/xD8Åasb=s'{2S} ?g4j~碢wSx{Mr^{̒Q}U~m8o>m8w l'v4n7*UpӄK^ւzJ gC.s͐4M'k6A0 0ى#;znBn 47cњ,u,v nBr^PV%\yQ~SXh݈^ۡ{ 2]D4D|>Rƌy9͑7i(iS7PrmP-*ء=2TsXеt rv3_q\DS0Bl>¿Tn,ly,cFt1a7G.Mshwos>u!N7A857w 'Ǎ:nK&(bk]Fe3@d>ڇvN.Bh<yWkhOΑ\0FdaxсUD{jDŽ.! C(/nݛwObz9#x5/dCH8G:CH6@:C0Ӂ# f+׿ BZ 4&_-{勑>|Tt,C;˼jM!pNxkR㥹Ҽi h~&ߴi%?n nc!>t/qBDzCkE{b`(BeSdOHQLyiF/Au,.5?&np!8.q,zӺ-g Oy)K)&Yx|G]' #堨2ɓ,=o1u`"rcd/au//Ƌ3o| ۠`9Ö f칏BxA3('& Fr9iE K_5#p/׆E.wP@<32 ӧyd!͝ܦ.7O۫?!l:4]f]gqb8+Nԩu%]{^[5r.y,|ז 92GO5HZLLe%kN1*{ XDРުq?i>`?CGV/C+:[ϓ]:D֜Bɇ_ $O1oZ[[6m? `S FkùF U{aW <5rHV%-ӄ}^8UkN_óqڮ?{Ym1a>Y\-ʍ-j\kv¡Y]{>:5FLkS~v߲ZLu.oDcTR=_^khPzV#'6FyFשǹ.ՀQ*' w6`O- {T/a'd]chR̀bbF'li!I~za)GxY߷Wޭa{pnhD5m ;Yov Y*N:VSHFmߋ79tpGXݯ IWrpیΛe3ܶ@w=νJzQ| d^ڼf׶Wj9TOU]O6 vGMPeވV  f SI0$6` %@~]|GUx;Liq˦1M汢pArR\ :O5h˘Uc}0se@ ?mqaۡM֛`>Nӱ6g<ƦclĕDLMt,yL ɸoUvM9i<ϽR ys5TQMJT3WPuCtN+ɔnM/3<=m0 jp- V)ܜ38o汾>OxX쁱`6.avp41<}Lg?KZ#8a!sDT3bGBl Xt ]C~F8h :7@h2ހ=@]/v܊#`L A݋L3H1Hk|pἭ q5az` m0d߿l}7BŊX5M~6Vj3#V),&Y}гIDh0 5o|}臓IH?@d#;-9 C~V8 #PXT%l Z՜Xz !Q^@l!!S!~bS8q/Lf1cS>_uLU`bu4s}S9p.FmE[rpi \{myy|O:pc$]!I Qp:ks-\Y}(IӒx^nIuЊ'/ml~Ͻ5Y词ĭ؎dG9A|:qgmK c0{ ϿnWx~~z nKY} Fpr)UG>@j(mcޥËGF^v,ϋrb|4רּ=gErk8ߖBt<1ޞbk<;k\gVʚy8G*6_ !w/c!:rI+Q/|y5TY>;l—T\eQe9@>=)yDP]Ή>w{xmUl *endj?wmR c1f4&"ʃk% _&s]1]rK?ƍz4!s?{c( Bc4-(LEPol6⟱ZO\-5wqi8-G/OX[YrNozW}\:*UrcͭՅSDz='ٛkjucK")!puq9>b./CǦ{^21H#xأ՝k|f`Fd,qǂ{Y|Kby+X*Oa~&w+e6zO{Yp% q2u}!mm䊒p 3Φ/]d^?'|ko_mOz.qJ׋3077-⢽Py =a kndE̍4&K|Et~V u/t7'\35PO輍!O /&\BWELށ^C>\lWH+uj]cQ\3 d-5\$򞝒wڂjjMꯊruV+tazt]_.4esSl_O@Sߛˏd3E!=O+6w,ggH=_T瑾ә=W)tz?y{:_5%W2>#JIy] IO.SWYB@{0H:#^i37cSXry %ǰz/ ]V zIUOBrU|NNߜoKP iSmf5yܾÛu%uݑ?wݢ]/u+V׃WOו^t]@~u=fm뺒ᴮQ.`-+_ot=~麒xJv]s_R}׸ˊϘ \B#uySf0s]$\(s|`1uzuuy L% =:4*u]:˘:ӛ5H VϰӟW+nHQYg?uO}T?h?/|g{d: { kkt=]q<=BYw939r}߈,x`J+JS." k:ã7Q]uWǔH kH׳Xt݊tJ]_xЅ~麝C(R4bu0tlx:|eu=+=Fs9"Fu6KWAK # t6'OH甐 Ŀ$<cAHI:{,J]_H9 ?oyq07WϚt=DM0t_9f-]דD?7GPq*"]ag+ƿ$:KլEȰ+\ˆ/Jl| 6\y({.#YJWdyV<:bc+l ʳE'}ω\ }PpOh}JypOh}JWrE'U#ؕursO+?[}BAcy^ A]lw~>f!79qL}bc_{_`)}E^+XwMWT.J}_];S>w&Rq& _xO\ue@kycZ_tLw@Pdx&Xy 4F Y"N˲g>qB^%j $آ,CWk):.)uXcд޼ [`祇DUYΥ<謏үe[uhc;v=8j(j(,ԍ6S1l|PA֖Yp"EϨ.{åd/Lbv+4 7]Q(- \[Ư]Vxi #sصNELRh `ᦅc?TcD1f5L9_Oυ4= 3B~ע' 6k{kVHjxGW9[0V{YgWH{VMD?3&g]\mkBTaPxݡm`ю,I!ӦTUT0.@w&lA0uLw73 g55Kӿ6kӿ7Lo@BUzgzoGGGsMހtHo 0Oo@GGGGC8`)џ'Ϗ RTqHALmkBTiKxݡQQJ  AfŶ@ GBH_AK'&b8&3m֨__a@uހoOUz2`6џ/??7 m~/GGGG8pzހtM*xHk͝\mkBTnxݱiaRE0bg!","hR V6"d7)FMMM>7=MПP,`2`"`GzGGG4kӿ97?????_7?ߍ`Oo@GGGG:?<7KiP Vr :FmkBTx'xm* P HH$THH}ʺd5;f:g+WP)RJ0//FSb8zcOemo-x w6h/]}Hf],t2ӏ}.WY& =ZgHbݷԑrm Hetzx]Z fXu i۴@l^we!4윹}ΌS$~ظࡠcy''fR}sׄ?/<6I鬄>Ic1@Ng#[9 #HPKK??|!lZH,׮IPq~uI_rzn"m3rI/=xB6ŀ綏b7>1 K˴n9!!7,nj5V2s|I q/7։ohORJ :?MAV!;Oݿ9JƇNYwuk9-~V6Q=Z(#R>c.}&R9r+uרؿp@(_rϻVBwup$ʞp@ˌxlxWy"LDxlp1x< @-U&ioЉ2+Z;T#ggZ3 u y`]>;\됓rϼ'U>fkY_ \N 0LƟ s=1.Dv&BT쑶IKSXOlM7M9?ϩclTHǸǩauE15vR(PP!meK/JۤמzQ[x`Sm+9>FwH/G;S<jslskTyc}_ ԥkX-X}$mXJ͹BocC7<'9ؾO*| .J(JQJ>s1C$K5}:awZVY˵\ s3k7_~YcmOi_k+?|,׷  U׹Ћwmxɽ]ڧWH=uLпY2+礓w/!q9,:LbiԲWI {?` Rk9c^|;Q T ;3Qs9#/1`v &?3i _sQ?4y Wqc 9g86:w>P}G y+)mkBTx?xݡJQa/*IYp0V\""Z &k ˪Q슈½{_s8pJoyFMMMyMo@Bfg ?79No c~ހ,8`?_[f _ џxտv27y]X;xLo@'a#`G { & k{M 7?????_TZ6 FmkBT3xݡqaSl P@TQu6a3]#k*>o8Ox)fAk__[;>V _]z&`g@Oo@GGJӿ6kk"џGGGGGz<n' ٿ7Giצ?!5mkBTxݡJ`]L*",-UD1Ȋ+4L0N7`VۂŴ+N~by?xG;5Vz??sހOrހO> ^]7{@,џ@篻g'~@*YS0`MM?7?xKo@G6ܿ77}af?Ys?Jӿ6kJ =7YdmkBT}xݱiQQGUbPFH@ZV"CBv MS-)N^RSѿjM_[7?}~Mo@Bez???w@, oGGGGGny7Ho ؿYПPfހOO ?ۯqz??wހܨ_7 F<mkBTx-Jaa"*Ij A0QA&܀ Ap }¹}kuPUToo@~z?ПPϟG?x;Ho@'"`4 | ?7w1{ӿ7{ӿ7{ӿ7{wk loF& Ok? '_U RmkBTx!nBaQA P  -tM ׁN~Vb8&3mZk/MMޥ7?ܤ7?ߏ o@OiNGGGG*`2џgiz????=t(MY/ծRmkBTxݡm`юmB0a0 , C|Eulp;|>55Kӿ6kӿ6^ Ow@o@m@@}ހmHo s7?????/?џ'- ϋQ@xjlgEmkBTx흍) q ĉ8D^>׻gI@XjjgiЃ`0 `0 ?ϟ|:seQ3|ӧO|:2|.};7eGFO6_Qv]T]^ˮg{>pjzkuo{yye?{-x/ D:3D&򈼹e^Hyi#/OGzϪ߯_~ :sMe#M3Y#=2 QЙ[\s=E8}E>GȩT ڲTg-}VfoSVwzV}./>~!?U1<#}=F[ ~QڋBN..+푹^edLo+[\-k dW(}6q$#?z6Bөi?L7!3O_Q}Пuo[=tkȋM!'}/Ƈdr2_Cﲨ: `0 :8o=+8-4}۞cĥXdq{bUq©ήm!ƶg*ΪU\z[GA=^+ru{LV U?)V>ғ)x|Yҁgi\yi^cUo*= !TY?rfgWsʽVn*VX#=Fϫ+[F~yH\L~[O҇h5ݵTow|Sfӟ+);F;:x )/OS yUo2e)Ve3'wgGg=J^`0  ľu kU,Ksؑ5nY,bXw{ w&3QהNQev ]ƷgcH˞i{A3I8hwduwUIWq8I>+@pQşGcZ\ƪUߝ]/:3d;ɫ:gB9R|GW~w2;fzt|+i5nΟgZY|<1NyŬ|E7k?z/k><=Α}N΅>uWydʬdz `0 *\?W8GY:Dgcg< 2+'W6qn؟{ru"wU쏘~c#T?+y{Q,,^qF/Xv8.֩g3}ȸOP ~n%hUG4(_sn|W}Tg&x^c,Fѭ+ <#+}/Uw8BRh_|33!mr\7U9m({ѝpvew[xG]߱?g;,nҽow8]וb?OV=Z_#ve?vN_WrYLo;1g9pV^G~>[_vNOS3 `0Q[ veO\k^8֔v<Zbz\Opbn$~}oz3ј mK vU]^iNWA#x딫jt q :E= z%օq)CcYEqyRG-+u (K\hP'*^ء^q=m=y|Kvūe\rȊ4={W1;=ݷxp;o@>ȘT\Ԏ+C=*ɫ|GJOCW]x1.ﵠ9_Eб Vq)v(ʑ}[GwǺ{-oSdו_˞׃2;iT&w*w:g׭SOsj%Z[~_˯d֮+w]7 `0]kIu+eL]ւoA^;=GR?v쯱;<y o$N1紈=:ߥPVu< <&3KyC/4r)i=*/|Ύ^]QNН1qGw>ù{ ?Kv:A}E:_n+{u=rq͓̳]>>d}+|L01`0 leg:׺񶊝`W,3O?]\9P~[kOWiGc~)-<w.3q}'vuw$Vnv(r52S;Wk_Kϔ8B/hEՠ'9w?K;x:x<|@cϽVyc@ۖSw8Bq]=2lBe6V}eR( VeZT4ade2ޒ+nYBTqSߔ<[&=f[|szP)G}{Zׅ3n7jpWwftEw[ǽ;`l? `0 `0 `{~i`oLy>uoi\qK|}7Svu9G쯿c¾#>,jow{ՆݲL=mW2u_8دjo?kD߱mw>#}E:OۡO;y`$jwmkBTxݯJPa/E f*&(Z&LYIE,lVAl 5½{_i ٙ55Kӿ6kӿvހOd7`'g@wހLqz{ ߽7?g _l72>Ho ؿxހ?7y____[[^Ko ؿ{ހOy'` ӿ9Oo eހL)`mUbJݶymkBTxݱ*pa"td1IVIl,eHLnբ\dsww oŅ17/v :Jo@B߿O9Oo@B>Ko@',`yz?Ǐ A _7MnwӿؾJo zހOd@d@l=џK5Qy]mkBTxݡmQaFATTt]-z\B7?ɛ\sb=147m>ަ7?o? N TӿSz????3Qe@Yb7Mnwӿq@Lo@GGGg7IRPmkBTx!aabTŠ)b(-!ݦys''@\ڠ[Ho i@Zހ/_Fzu(RDmkBTsxݱJP.%"$Z5¦@Ƃ hn ! x 3<8Ziצmn _M7??W ?_79Lo@Gvc@<`Oamr@t5No@GGGgG ^{U__[ua daz'GGGgQ_@#ԟ{ mkBTxoǿr8ر8s:;})MPh(`P H@0 b&&q FFg87N|Cry~?yn9'OZJ PR3*U" TCn KAM'Tj*5 nNp5{WǯgO}?Uds&ξY Y kK4`joH6W2=UdԃR F썆Z@]06;vÝG7^ >|1j n1Jca,V|TIptk3j ]`K ߋAp4փ?lH5\9 Vã6?O?~:PV4'_a $h 7̧\zZ`҂٘z\0<{rW ugk/4`KM a]Niry57z`Ce.DЦ"7t>8?.½ `i szMRcs{ͦ^7k{C;"O\ϸU3g APYQq/ Rys,ټ!dz0xc!bZt]Ր+Oj;t`ijjC鋡$ohVޓbf݃Ok<0vm <0| cD4u|6[*GW"o0oH󆹽f_lYͼyöݬ g08r\[T_sSBR=N3yCbwvBpupyBK.q;n(|/Sk` 5%BȘ_ܽaF(cWaзO r7 c/_8ϭ/hk/]k!?M"oy΅ U ސ Z|-|W\DXcV1 oK 쌕uP:M\΢EԞ#:w0of`Dsncof8\0B{&?uS-k`gg_QL]s֕ 1ݽ783Ϭ {ۦ֋4dn`g8eUu (1By ̍,!+߳H|?T`RdcX\x7$ij "U{А&6\Z5L &A :p1G?@ZE}g A8-&83;4jhHʛJ?ʺBbAf9\D:oǼAi*=FK:z`M6h?X[&MCQNHFV3ޡ3s]CUzcTs⾠DŽ}}{6^TzH9Z.ǠJǐ=.KLȱN!Y-fq50/:@4L ư<Ë)fN:7ڠic%,+@`_0-]Bmk]UwvIJ?rI{y,T@%! _4 5jKbfoE4qWM'8W"pneUlJc!3|juw{ BS/pĵ#KRrr@-`_yڀom -LW6pw9?}˂/9,zc^:0 <)y,L ڤ=;@O͏~CI!`]`:t|]zI;v58{*2ܗ^b!'DYNX 6њX}0_X )I\P"9%=T=414{%#d?wz7_lAPLX3x:C/+}LMf՞r.%Y/gμ'}^pl5A-x}pdX=TM{m@+̥Ra塢S#E3^|owܘxwq-Tp Fqt2M6Zcr} d?ߟ i.4Ztx<[д]ӹAYVS-QϤ)x#ǹ.g1/ A>}'Ĵ,HIK~./2}[8SL: 7!]?`ĒvL1_`-q "Gj>^eд G=6MCgRypGkIւ' wLDbu>:"8CFBsX?p/~۲w<}R{ +wڰk*ٟ?1 !=99|er߿or:q77'7-H׃ϓƗ_Sܾ|AE˗w{gT7 s!ιo{n`U.:{=M޺5 e|)'6ݸPW08rϞzZBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAem-ƆymkBT6x횉m0]HI!)$FR?6c>>~sm+vuՑνYu8uN?WP>1JsWiV_uKEϸ/rˆ_gKW]ױEYcl,[TYHT}xL#}A GV7^}>iҞ-i;}LJX&TP3T#ߨgJl e'=?͘ona|7>?ǐU%;/mN/IfQփz{G}?v✽3X~j{zTAO^ʰ>?sy|G)P7yހtNo Jo@'xހlџ3~7?]7??Z xҿ6ZZN頿MUf`jmkBTxݯJaa/e"|1V\,fQ 2X]ZZL˒MfN^ O?SvV):u+vzwg  >/No &џ~_7L}7??Y7?~>Lo@'ހO G^znz??RAzOgMzIz߻Oo@GGG?OR5?P?{ymkBTxݡJaa/ ƆI&+ (. V/`1-N>b{y_G5jjmצm{77?. 7Oo@G6`GG盛'Կ{Ko@GGGgV ӿ6kkqz~z??sAz/ ,`_Jӿ6*]SmkBTxi`Qqqqq ! COMlS$I$I$I$I$I$IqO9˵wOo]ss,Խu˲<^0 tc}av=7_{V9z}ٿ^s_y~?}ez۶@wE՟k$I$I$I@? R*f}mkBTx+JPQbIr 6-[lzA &'"8ds 8 a3' c : MnwG ? |7?sހOqz ?]7zހ=7?????ӿ7Mnw7{ O6ПPf,6Vg Ol+ӕSMnxD.MmkBTxwSםQvwyd˒%kwٖ[lc`pBRB503s=i̴MLN4i5~}nce!s~{~1vp8@U e^XÁo} 8^T@Eo]@::¤l/K5u_1AͼN2SjqfQ 7׸)twT4kKA'\W3]~Eݯq? v-YrOW U ?7?2t-KPw\T^{ ?N]d%>#,}t+dL-OkwMLgM %?yP:W l7s_ g!xAY'gOCP 9$K Jޥuߓ"LNg??G过7rͯ>8 mZwM' CrOhz~eh{vٽ */o@?V<'@S]4y\/ZtFcZP_{c4CQuORk W@Tt$wot ~9\; uF޽L`Uu_D!fyfa#_O=wwaّ }Kz,337,[ {B!g|~KwoO`$9'TfBQ5^+vI竤O5Bh g1WSto=j~G0c)8qlZ;hWݿa׼ʃ0p3^8rE?aJGjAw(_}N}5¹o=J}q.HJPBB}\#ʠp2^gWׁj >C8{ۂuQ/]CP^ z{~@sܫ[Q󾒾W}5E?ʡ|%@7'c{~B;PgN9Lեi8hGv?48& v2_8{_v* =iܟX(x1%ΞFmoOޜsCEuVYj7:5ˮJAB Ż3Z¾{{_.JN@w} +iG:+Mع{OCM`O.q3r:.> ZM¹>Ol~86@:T-16N<I9|E&X{}^>MkaUn>Wb>G5xo|˹dq!h{6A|!8/WUlߴA (+;RiX2!<$XCo t}}5wO;Vt_ZZ{/R1LE1)oYtdz|_gG w놘;|!b vW:g9"s )A{4>&}}+fg5nhe tR=Y` +wBB3|ߊsdV%Bz1L{nZ>ˡ\b}u&`+*(SG)X9ǭTU vo'澤Iǽ{?*bLH1[`wFk/_jE#+OkI9XlnR cP0z_^뽇{r/h θk mkakzh:. ^4n\wkv{rwp_fZE=CeqS]kZ 5^稸 ܇X_=!޸QOqٱ'!m${y\#Vm$%f:{0 v`ggÄ6,-|Mz93Z7}͔Q{_bs5jn:Hz%!}5yIZQ>X{3OyY<z$]px.z=a&GZm^};ڷddާX6SxO E+{>^ڟ*:Junuol~ڵd |=!24@1٠sB $⽨9݅!%!p(#ig+&տU4dz~fOo5o9w<JƢ &B54MԾ-7 =_)ȵ8L܇0n%(M`z+)v]k-57ycs ]IJ;SN1rzY׾u+NOs-u ߖ yʄ蜑J9?HhhdKGsVgd8ӂY\Nw 5rz\SҕQzݰr2;V$Ƨ9S@|=*YK81ߛ y}9uV*^1|]6đKA%hlSYL5؜O#S'}x-֪/r:=|5({3gJ IO<+xrF%L%dz$R whn_?[N1$C?۵+{ڿ ٿYx׾Nqc*,t/O'SE5 n(w žA9FC'=r/ 3Wɢkw/ܱ;~jVþ/ca i!YX$wq8~ɼwc!f+Oc+d_M:f˱ scc˿'{7.r?p}Y?<~{ܿw%nw-ȎY4`+w3W-o- 9ֿ+5+gqsC\ρV{|-3NOF^}?/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVB. mkBTxݱ*aqbD -GNI$-daTJ,&b QV;y]۟_SƛёR u*WM_7V'?~[Io@B ?~7٦7s@0nz7 _7Ύh:qn.3<T*NOO}}ҿ^<L&8:: Κ}M=#HL&\.7p\OAprrߍ$ ߘژS4JzCmkBT"xݡ QEQJ `6b5 i.~@'&<1GL55Kӿ6kӿ] _'6| oހ4kӿvznџwuz?????<_TMx% rmkBTx!JQabĤLPP)) P%Mdl7wr] O?nco3Ơпz;Io " ^7]7s@]z???3q?q7q@8Qzg ϟf _>6/ӿ7mџ?~mkBT xݡJaa/E9 YdA,a .xc lfz'W1Ox 8mwZiצm& 7?ݥ7?{ހ,`Ez'Կ{=Lo %.;,[pO"%dƾ(]mk``Yq ?Ҳ{oNQ<;?I9 Se0=d[ [_MN/_ }͢4OT0h~`Yzl!BJwsbJ.!N?SΛiN?ȅ#w3y}Ƌ1P'BKm+̑,(KzN!~}&[5uat} Fj?;+L$y2k7 =3ȟym/|azYy#N$)/,.kt&I䋯k=dck=)* ɪ!8HvN\h.6\B7c o¶kPs&v@aL(3 \e&^NNR"C2{n4I̋ 9a$6KN2LӂW .R.=}(4GnȯN} Lh-ePth&RSᰨM Qn[YI. NqcCU< TU2X}Gl-Wo:;!l L (;H"t_߭ ciG}#w3z¶cw#uG G rgo<E; DVHgGB0d,A`8볇Jqk!΋ ZŨ愜v! ;!~;LfggˋAl;Zg{Z뗓$4QוjG@#Z0Rvp~.)k#s<:%) ݾ 1 ? $s@r<'vB%_0E\ 2r̈́Uk.Fg6GY{tGh㯫bV t%ūHG?_r؟ݟ*8{ '\h  ^03mvi2 7l0l Eyusܟ`Ew0֛O|-ZBzѿz0)g&k'_0B'@'H\ st|aV+d| y}xmO Ϣ9@=y?ƇO07A>H-joUX(;*Etw3D ^ x7e2Dpܽ3 ޻bv:7PalIb8I7OφS9Mͷ 8mTGi^an%پgk6M+^zcDN 8bjE0ms~4lZS8I:LjvpF>fMbTV_}L>;X}>Ơ_Л)lЌG_-}]h}!Et3(O׉XX[9 khgXZ}b=e-%P{|6iwBfͣpNhiUa2ĤGk^1ilMXsٓZjA0^qMhWkի1UR~BS V%@pv}9k.aҖGp37|㋰g>h^zt NE)w#|AbK)sc `LbS7,|ӳƞ4g-D=+4wk敽PW$g*0v>st2ySՃa߱VK i:Z`j,bEs!4W 8&0YjYo G`g2u,}vAKμIygҾ* 4n_+4{}yn]]a޺ZUJN7)ZcdNZ藱QK ;ӹK\<{vU,ť*ɀxQh>&~aoS\ΙhRZ](8MʲP&GpWb rەX?7Hd5K VU!dX+ܝʹ ʟ˯$Add$'HJR }+`IX7%< ڌw`&fOs:!Z͝5R(b琸? APoz3JETA)sM!y$:dD]=yf\?x9أo W07I3oj_0/w-bB~OxFqJĹ^sSaM!;\? cD2?`nQk[T 8뙫{]JX Am׍g]}J>vowW1il's@۾mM6!m@RbfL \=rk%3#MO/> ?gAhW/629!:ˁ<@d lĿ@"bYVNzpuS]  (yS@=3 4Wh=vi?˂Wʛxgs> a!`(ePdWº;d"w t}z3It,YnX>Mcr>`9>Kg mQXzs炪Jkۉ}z(g0k40<Q8WG]y.֚Ί{٨^\RKi:as} bj04TϏ%E,?H~.u.|0i{:8Ƣp,9<ԂB6g:sNUN|uloKGg9G{p\e잷s[ҸY!m1 |-j3!Жq/f rvQ<Orq[sv [;VԶrŞ@+An>ڀ)g4vv3aIE|uעF]x(M!(T*m!}]O șLgxuw F91Z?Sr x c{1uqܳwgf8'5c7]v{ 8}SD޳FMQAa <gObV#={? ^Q︉9OLZs/k~%g(Ƈ:3f"(Fw=jik߾ϛ}?ACu>. .nw?7gymggl3.(de`{Z%'Y=F\Gqz/Õ(@9;͋ xCVل_ l0> hwQ2!L^|Gp/}v1{֨q g?8xѲ>Y.ɪ˿p߹6:`P7>#ۑ(փe5s9-ϻ;-4n5}s_ Q.9Wj-9~_ {w`k'ߞ#uLtϧ{½8i3[@a;q\;wm{R0j׿s!{x y:;DZאsi-![a1au:}'!*w}~jܧ j}ii+>>;0~\\;ګ>J; =1kpR]-~y3\%+U$.;<Es~tvm3^gsZ[ Io3~/]] \>D6o$$b)x]:_8 x=r"cIBfP?u١thh4T*ɵ::E~xDVУi,֡|÷bwM?ߒ3yy"qgO'ǏPP+/5iD.WUid A}a"q--ZOǑn65iWDH.\(GT^{"#y`^r -_Nɓ>((((((|K?%WRmkBTxݭJp]l2?Ғ,6eAa&T &o@EVLk^w* OW3֨__W' _7?Ko@'<`Yz?ϟ />f Oez?????t~@MMM=7?nzd3` vzmP/c>xmkBTxݱ*pa"tIdQG) &7jQbm*x7֧Kc : Mnw{g[ _7?uހ~Ho@B>;Lo@'<`Ez? AK_7MnwӿؽIo zހOd@\bc2[Ko {ހ?Z>amkBTx!JQa3("1(`hQ D\ǀEfd *'<-7[{͍ҿ5{ӿ7{>/ _ Oo@GGG&`k@yzQzoGG Oe? ?{>Io oMMMj8No t@j47?Mo p8Ho@GGG/ߛ@'XߞmkBTIxݭJaab!* /[+Vg<b,`1 <(;W Sۋ Tuӿn׭\o7Ko@B? L ?O џ9=Mo 0`'쭛ހOu;`&`~zIz???3?*Jj'`Jz??3zzݻ[õGGGGf@/Uӿn'|H`{CmkBTx][T89j#JҔޫ{W@iQ @"OԨ9ܜ]2ϓ'-3޳03{fMg}Y]@{SS'mK'shk4+l h?K6.h5Xf,e] k*mdEU﵁c.P5(4*#cp{/B`:B|bB:_fT8C" rt JߏÞ+G >IfMD^fip$>S0D#G39o*򅈍k4O׀UF w#^MX PߟMy.phMdCrNG8pw!NȀg)4 6LzF1 T@0t( :AUvK&} זQ(0'{A8 ~8W`'] =Sor7X ?}!R%Xb]9c-s4ÅSc̿ Aޠl@q Bd;ayNW: R6k>V4 F2Vkw#(̞ 'k8ˋ"ΌZ@sQفPA08Hj!1qJj/D{,/ dF;(偔hG9'sMmsT"&%Zh ւ~s̛<ql/bq-d{+i !Ʌ؅քPV n~S뵁j\~']DsMpl{ / '✔G$'E!7Lg׀}-P:EtpJ:@TORC(! V=Gd:R Z(i 5Ս k:|.H .ԾXLZy>CbǭgJ 2R'Hp%R#Q`n;HA)?ՆY2|!_iLsw%h`- l0}~DJGсvL8PqBFoi4 T=cNF<*;`|3e~M#Mi`NRA` 3EJ[!o$?`F <( "Y,©Fpȫfp4=S X%p gķGP|5 iS2BIL1R L'׶prrG|Qs kGw'j@y`Rx{Gޥ*rvl"jwLf 4 Z3i^?h3s G2a>"p0ԈL0 hd?=)f[,{D!l:x?i!h! `s탠?6?V8+AcPE$/Di]6H wt UDg93WH=UI Mf kp:#Xs )ni]+2 D k$?S>Oh~PK"}ICeT&(X7D*ރ+9o p]]}{SH,~]%?#.{Ɠy s$'j;4%ǎx&P[~ ݓ օ<´I|7{[>0W P`wGd=ysQ?y:ݥ_W? Z+-p%\E(3=\@S\ G= ']mBg F?י;ŬGv'Q;'<ΧOgբs`#zX+bPf9e#wӛ p; *uu1b3ҩH9 ?2>Wlo2x=^4nWD4oI ~QiKqTr??=Δ;-]ה1nOSwMmC&zN\t1qW$}0sXM*0z%δDxN@w>}+QROa=5|ݭ+(KL=r9pIg4ç*ϹՄs?9%/U)Ӭ߿?twuB{S}E̍WRsy=niſg*wLP&?7'9]6;Vٰ-ϦROp=?'^1'h{U7|da X]o)q4y&3SA8/PwEs?wu <'_?ި{5Ž*Pw=Z~q]EDЦ>X-cYnSjIrMЁ4yT'8<âƳR<.!w?Bo>׍9IQ@o!5 s N,oDb݀x IMywŔg755RDwH~TqS_Alt@k'q a,%DzKȗ_<&%ם!y.[N-X_P-5;%n .'z˫[N*S_[>#U;Z%03^̛Zb 8] ۡ%r:۩۽e15@]{aͰ@wPOM6fńzK<"υ{ K ٌZl1<$b677gǨ޽]fl͹jc&'Ho9aog_va3Q8x z=9"OoߚMOOñcyBOL`[|)_of6??#O, 5x5L V-+C_KybP-1_:剟L+O+y.ʿ6Dὼ"2 +?ʣykB(+'y =aj%8C&g`Z'>y"&#=6FCJ?lMXFȿ<AWG ħ@Ee,5fBbx'nskqXz;?n-[BTD@sCz9P[-&'D\Yzy+s/<`VE2X |nO%-Cc P+N"ZHl0` +`称C0w'ߪz N@];`PyBt{V9,=>ka/> V:?l>_ևYCLF2z'2EymkBTKxݯJqa/e*",-Uے  jjx Қ| o8Ox z^kѨހOW O?~nПP}z7uހts@fހO:W ?+LMMMd=No xހO`@@$џR@% 0JSmkBTx!aabj0"&Am YĀmcUpokQSӿ4kӿ6kkQzyz-o?{= Oiצmހtu77?????AvހtM*xSE͵mkBTHx-JQa`b h1| m `vN`L2Y|Oxpxm`sFMmkހO NLo@Bџ ~7? OyzWӋGGGG,`___[Ko ހp@Jڠ[Ko f=`nހ8q0_jӿ6dBףhi]mkBTѴx!na.M&#&:L YIX$Agog^{Zk/MM ~W 6 _?CMLo w7????3b0Mo kހ_?_]z????#O QTq~Cz}I*mkBTx}+(H,"H$"#X$,QԈZs>U{ ..T}6ڳ-F`p]k߅~b  О$wݓٱ|sCoA+q3lOx@(0a+? T,_7s\Ϙ^Bl1)C+k(FyN"8dPC_9>O0&l4Im+nwGrŰ)/tihf ѸX>E)<,6s45zb?J\<OM%O#(76:= ӋYAƒH Ls6MXBcX&ǘJte. 3.je(??Lj=%wZizFTx$kP8Em jAOހ>~؆B9 ֤8UKCvjbL Cy ;mj P. DkwUE€3ܨ8xUJs\ɟ+;}sFQ(KIXݛƨ 1 +KdX];Jģcx$D׷X`i @l̏rnm$^9΄zBGϞQ=nfkDe; <a>,⢞jk0B[p($Ǡp4 nq`XƓ vϵ.xHnorJ5Hu뇗 f a[Z:>36[g RL؍?( &w.7C#~B{] UW 71jk~ecGrD.=K@WDZM0倐0\xvqNZ ># BE )&yA}t?B Ym(WIpɱ |2+\2 )l8tl@Z.Be񅋍RSƃm>dIl'N adĢG3%#)?$s _5=YBR#-k"qGP-e"f%֩-ϓ378M9ϊ,_*n;HEBƱcl~ ˝[/sagIE2,z1t:kLș壋G){7ond{@rP>kwk׽ #kXfyEAB9uM4P=_lgW؇N#_nGpp ,ZUu6ȓVӰ0EK7*|]{75F\ԶzQz! uH>upT٣o3P)[^6` -d&*=%fY<^ط`_6|h3ء>2 Pq7ώ ,NsjF=B` 큳CiU)R鐏@LҮǧmb<2FHRqùFXi䎲OmGA}:*u f:@ʫRH.66jcGOpO- 6HKJU:Jǃv,3DZEƮqq7p?ȌK%ȧ$;?Qr6pP7`a^=R_)m>D3#£ _' Iɭu͋C-Rne㯄ssL<ȭ/R)|Lt_1Lk=rr 4/gEr~PnB[\g[{gYvRW' {Fem1{ wL;7&$xc0 n&u@5sCCձm8Heft x{q(aтa?Q%l4ςxmWI׆GC1kQ3iJh,KRO`ʲ4)%b6B8\pe;u)ko)#WSncRx{[sXv195_0Kՙ7>Tp5ٴl3S"؝LX睫[5m Q="u}pϘ*xbՉ#iM+@Z! Ϯ~jYݬ$?5mtu] %@݅:4h8ۃtu3; ΑO1A/r R*5i&j#Y2:$Z(ad@>'z L뇶6Z8|`6"X1_z' F-я?X^ A:?1;h/KVB' vOnFS ƤQ{=kh7MwXQp\v͓O/. N3HKRlK"q^Wh1wt h@3e6N|I;y?8t[[! $,ήLe"z%IކAkRl!3u8ځy?_W)AbCO!rza5Sn֗#<43y6"R߃CQ&>[# BHǽ{vekOTlq(UH͵h ݔ8,@tՂL{p/*L"d_y k,4 G̖bD>,.ok"D;|7[.DCA#ilϟI֬Dq]+eE _-- ڰc^Lq1~CCC9gNH8BkhJ#Z-`VoMa 9r$պZ-hkh ?C$ ^tď9d(8P݅]ڶw[wl;dn׆oKd Hބ(DInI M_(5)6H/Y1 QRk,nXHʉ?>df&6^EJmt{CCc`0ʅv5x<\9Yc}106"״!֏9dl:' 1H"z'7QqɌ#KR./CVgQȬ\ `?d1yuM6Ƶ8ZX]8^pwQE &1frRKi$GݜЕh3'{;;~FK37ku<pdʎ+C RMzƏ7)nҀ lEGyl:̑IoBS%|ЕsTulebA}Aʹ10A{KʘӺtjdLI=r PRg_LbR Şl?␔)![Fo wi&k^CV(t@pW2{hxHGRn͉eCbxԉ6GQd27\ثdS=\Ff*0ۣOP5(rZߙxQZ>~GAeN-jY7Ҿn;n?ӹ"Px}/NW:݊&׾:x" ꭥу;R펔 c䛅љElmG§a= h¨BG_uYnZ쫭FYs U"zM&:Gnu.DX5Xn;}ԫ%XO?~2&Frjj8 yA*W I9/ub)Zl: s 85J>~iI3Yԕ;:#hELם[ROd^GA˩f~Y!En0~/A Km>^WYq"<цF*c:xw|͞w%ehRgd9̕v3v Dgh>>?3hYDkgC(ʹƒԕSԜ| 2Q94(?OGQ34 fccPopTYaW(>@tX4`LGٞpɄaŰl\[9c26U M6f,'C4i?W~psϠ?kAKrŵk@I|>^xs?\`,D̒5W^w DMXf_8<%|8_왉pP1Wlm߃f?4:́_Ԕv M;k:p_sj؎qw]$F}y ,b'N=o0, ~M YR46+!}@~ujctCP.Y(x׎z?70WXFܣo3z0c8RGg0 TU򄽻w"/4֏CQ`[{Ocn]+{{ N!33+5]qpj' r9FDȬ)~: 9Gmx2-?sraG"yvUpa;Ră A\& ?#n 0eed~oq嶭!!DzP^H)>oȑ.ļԶ=Hy7S-M ?8ycߧq|#5"2Б lm#UeΤVbM͘jAc7Z ]> 4gb s 2WRsKg6 's8qzTT[R[w)I95xWj #!nN+zPڔ KgTE,?{^RDݥ=Ru^zîc&D'i74SJߔ&HUG[crͦ<׿~4}څh;lpAZ%XZ;tQ?yk1+Ƴu6[ Dc4Ɯ*dB#!}e>samhG3c^8u9󼵕⸈߂UyB;f "Yi=D =4&|C3g]~WgjhSIXU"1A5Fr4{AljwTt6</N \Rta| i>T.Wo>>xϯY{緷m,J{gg}v~)]s!?wXGFl!7U|Cnfﳅ:.@mq%臔Ru?.:aBֺE#Gg'yXDuSWNJD)21ѵVagWPqȒ s?¶@g")s\T{f3go^w:^"{d#!φt},nyWFKv„X4|VB~,˘_&fjp/WԍwaO H 3I`u1ͤ+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_Wݚw)UmkBTxݡmQюmB  @`$uMIA#H@PMKw7(PuӿneOo s7?4 y.?.џxПPu@8Ho@GGGG?џ7 OCTM@ ^4̔en]mkBTxݡmBa4h( I4XNрjgOGLv#%CZ5y- M_Ic=*G iL2gYh}=9q: 0Gykf(Ǐ);J%J~WJp t|=33Qw9(U6/*Vӑ2wт/r$a3jԀ̽ޠq\Fd% ?]nƥnaM!C''#-p>'OX&?`0{-悓Lq^1#Ygs"}rȿ_o,am* EIi#?*u~_z\f'y[]O,$ r2w4ga]16i3_#ҳd /q2;dt&>]*L)8_?/ęqm9ՀG91 ,?eu;:s>wXow%ng w~a˹j2kw/diP?ya[t:&4sm6/gWu3;h&t,ȿ챝k|v^C4tol5KLGTt~5;ţk|K[1x} ocv$ $/`B"4NnFZ;sԡmwLFɿ|Ay±XT~US%6#eh3ռ9h{­^Oh܉HT|Ud~!U#:V=s =RqΕ_v/:˩{A*vZŵ$ȝ#ο+_^ _Hh.*.hgCOru/%T,^3s \?ȿ#]i#9u:UH`gݏ9b[bKzYVXg]UXSՎ@ BdkS\gNZEi] #ʆ>ma޹Y5/$YT`Yh]|&8 Ćjݫy;}: _%V îptFϵW( B( #%~`/5>#C<J?_gھNe,s`VS}c8ԉ_jBZV>iKyNJDjy󠌣;4-$c Y6{<տԘr ?CjX_O/'>Fn117pY(_ܜP/kZzv+aerH~G;φ 6#TIPsR䖥΂Ώ)LP$@zBf2%QXil9AWO>5iT9kX̽;k~Ɣ֘?!Mfs@}vW C_/!ɯ $%NB2_#!ɯ%&                        )w"klmkBT^x1Jqa⪄jh"z OM~B ot^k__)џ@Kz=<7?7???wxMo w@gހOo9C(`sހ\uz?^ܦ7 ]7?????g4kzmkBT1xݡJ`a/e(C.FM&„i6ՠ4,,&W^y?xz55Kӿ6kӿvހO0`W? Ho@'s@azo _v72Jo ؿ?>Ho |ހ߽rS_ۭ{w ?_9]No @h#Yp?Jӿ6keP3YmkBT^x!nBaQ A Z[W%;Y9|n̴pZiצmֺ<`, ]ПPe@'џ'?~w)MJo ؿ;|7?????=_{@{ހOǟ(MS^ImkBTxݡaab F)$))|؜L^G+\~)O55Kӿ6kӿ6Y {n  އ_sz???qMIo@GGGG:꿿./4No@GGGG:?[4 .X|~mkBTpxݡJaa/E &ú$V&'Z F` V`_3xUl'Ox6mo֨___[7?:Io " _ݦ7?W /џ ܤ7>Mo@Bgo]z/ Ϛ.?7????kp@q/џ@t'`޵~@~7Lo@GGgmP?ƺ IDATxycy|w.v,o=+g!gHHI-%ٲeǎ;q8'y9(qbɔeDKuDICq gȞzPz U]]]=s0 \ uH$Dx<H$DrHAH$ ]"H$ tD"H.R%D"HAH$ ]"H$ tD"H.R%D"HAH$ ]"H$ tD"H.R%D"HAH$ ]"H$ tD"H.R%D"HAH$ ]"H$ tD"H.R%D"HAH$ ]"H$ tD"H.R%D"HAH$ ]"H$ tD"H.R%D"HAH$ ]"H$ tD"H.R%D"HAH$ ]"H$ tD"H.R%D"HAH$ ]"H$ tD"H.R%D"h$Io077Ғ8}}}իLMM>cH$]`nn9 B̈W2>>D~D Dr277VuBm{a<ʕ+<ÕH$)9\.s X[[kQ.k8vp(P($x ^J<B"(H~&EG Dr Loϯm;-,gΒ- d"rkH, >?F$s#"!Bٻ޹yn[Z?#9NMlG'Ǧb_VۓoK$-ƍnxffpn6S 63q;k x; ݧhU@T]SP= G4-s6`ۺ,/c;XcXm98Xcضm8k47u~GѩMM~i(\й !O@W0BN ^PPUE4ט!Phm[Ux`4uqm\lŴ5zզVU-jeju)ZT &Yu><</Wg>}yb$CKwd N15XX]yׯ;<\0">XҠ/az44EAStEA>06L bxt!z-aXhP3-lAT ]#3}r4b;PZT+EԠ\4)M%F,;TsK$Ig63=y C},.y_P*vE|}}ͬ%]lf]Y ]Wv&_{o\{ͭ39NE:X 7:Gǫ'bvj=* hT4(f8z ; q]ee;O:Wv}4h*Cb!iLǡؘCfQn߬]Sԩn[4xʳ٫/_ģ{JwHA\Xj7odnn.6-IӴ qi*ǫo~u7ߛ=D6#  7h`^M;YA)bwcTA0B+.O$x^d坥4KN+>a/خu]uq\çqmj$ݬKשnT[z ^x%_>y ) iܹsclfզwL4/ooACM^QuA0!2!9 1 2kZ0aQUWf]M?d-שaKwtMᅉaW& Aٮi4˱icc:_]RkP3M\ {pvjMݲd7lW)n4lT-ٮMqU>W"gR%=0??,w9gxL13"RTw^:Ou\B@xKA~_׏}no뺧gBՍSsoXTD{+zGSyyz]]ӱY٢0پ($!#Mۦf,Rz\N1ݠZ[P“*wʚ.y,q]@763g_763Of63bd!o߸|ߡ|W!6a1DAP?:LWZSֳpw[tY3o"贼s)"iecv\?b|!+l]h,9 ƨf`:ӤjJ +WkVgO &ŧ^,z3}L^"Qxtp{4,o^l]RN ,Qc)}n? 76X^_WsTv?bٕLON meS-궍}u]E24,6*l.UZ^Lԫ擯~z^fK ]rf^l6+^'lfC\J197׈߹y_?o]qFCdLS~"^/."QTvϣz{l;KiI3p}CNw]A?3#Ϣӽ,n([xD0(*bbj8J{婑 Cmd<Ȁ*|xfkv]jE%U:VMۦbYTLl߫[c? O||3/IqHAOvlfd2Q.M 1=g4g-~K'[UOI> q/j(<u($"AxB.аmM5sl6뒫Ը{08CgрD_OrRv73ٵ̽05GUx'ݰݚ?[Iv3a*eJ O߰mE2X0IWGqeoٞQ8}>~~/y$7R%,b!.M5B=/sf;o}5~_P*|LB@l ti?1ڞ A%0-nmPk((ñ= -ha6,߇x[QU1;S}!pO}r})i<+<1B{V.o]F7{ 7Q[~W8<7]v& < Yjt!d,&|%6T-NI?6~6#Ԅ zkz5u^\ҰXXpma2en9CaJvJwi~+34ջV{`~Mַ G%FS֠@ASD3Ԯ^۝r6slʈVQGxTe2x 2Lǡf7DtJ2wɓ-S+}$'~@ .˻6z*kÃxQ`h51ob|Ƶn8pev]Rq]`m̝k9se"^凿?vE$)J:޵fK68Rlf׏o"o` }R fݪD}qd(7w_:h@sԩ+yjJ['7h'"G6% D]]ƲmLvjONi٬oi4,Y}s] >Lb&Į{5 8KrAaR6M:mI:]YV)8, ī/7yW1gS{@萂.9sw3sܩlf#Cşg~RUH>`F$b^MW Qذ㺬KqwmX?aps?ǥ-cS&KQmD }Ql{39v=. i%*Uͭ&~(Uk f뜦^Sf>EmU Ҽ;7TC?9zyjj{˩T,RAQ8{w]rdiܟlQaic?^!>GG?ͫ/7$EJ.R%gBXƍe3KcLܳ|~_>zV. LW#hމ6f$'_2 U"WQ7Ȗ]?uu]!>O~`TTˬ $]DKNMZ=ͬm=ABL:h3r3o^{)3N*ݦ OУurqVm2YovND u%RW^ <^WSP1*`GzO:MGUe;}v UsxezZ_4: ]esʝs~sjt}O3>2ٺL0>2Uftf2VחYM/Ҽ|!}#} l`~F DfggYXX8,aY̯?9Tǯ2'*iB uļ^#*EVШ Ml![jv׉Q pECSS7[-VbޢZvCߒf*&zU ?hb:n<\j4LGcNj`ݶ)6 궍lJU@Plՙ?6 ɦuq]Ronvha|4>s~?drtL"#]r,ms=掰"fofNk6K@OSy'T>~l3.ѡPU"S)S9]ץblj \e}@ZODܲ*EJ9VKDX lޜ?jQ #bUr!i7j3LQt}\wድ:/T,0ƷY;yޣ>"~s~cS)蒎ܺu'mfSIfS N'՗+`QOEjh(D@[ׄ vj CXl<[8TӴl3f@ՒE>kR63܏ D{z}b]ݿOms;9aza|_DÈh*F)tQoXl{쬫kx5D8@Lޣ(tVݣ"H,tmiB{̖߱Kj•8RXsmVojwnI?~GpKNƍGZ"f*cL9fvR~O]܈h<uPxRԜ ]Û fZ88ӮZM/vRkUh;9:K1gRܶ(Xg0?hx,ZXO{b}^{ }Bo6) %> 7twnRi6렫*]%7ZS[rv7T%$'Wbg|ݶwģARJ[nMN /~{~_d|dQι@ lf sij8}xס&">N& |_߁ ?( #pע0^.oFmLBCjlv(ld3 9~ntUD޶:%QKzA숽pPT]7ߧ1" !knk]޽d#>MfvqiffRFn'S~qrʶE5gQZ Z/ dklܬ

g+9ٴ}\>P(666Ng3Kĸ4b6H//j &R p#˿U'>f약ن*}a(Йc^|/ڬ(͂0Plܦ(mͤ)uSn֣wfm^jzFl֪=OE5gPC0 ,3GOӺ.])nm8Mz~^ET :zjӱjlj^K5J֢N3ZhEFhր0"F)FQY{e@H?])#vlfܗTj\{Y}yrWT2 Mc,Ƴ7|؎KP"WR5&z:\vv|Ź} b(PO IDAT{ٽZVZN=pAT\R?;?,ͭC.E4)ryN˲#EoVG7Lx%փ} _w8T4{$y[ vM3|N^k󸻮K`VlIqmUV]>*tK{C hv3l3sqVfvii3uF+wx_^ߗ+|y0Hf B5b;-halתfU$ۨDDZY+:_9a^(-{~ x\5ME4TUivaSDع{@T4]ã躆ǣk*B:[4j`ǷIwt/x|4 "R VohOCIoυu4E!ua1X|xU CU{t^<5_,*F{HnYlB{(+0 tlLؗ^*R3>j.c3ݻM%45D|0vRVy2ɯ}5sm `("fE |H;atpPhK^؛}zylBȵӢT] <^*xlkXjk1e($jOkfVeV*a3_/ ,..277w63R ͬo ԕ' 62iYb>쭗ʹ.F~_u٪UTx6[jNf*M1kroJ̅\ؠr{q(Bv;?-x [pDTr߇?>0}^g}eˋ4zy !'I'uYfq5Rzŵ4i==)Nߤē~|?OqY\ftT^A^%+Mh6jCdVP?:_'C~YbY{|S| !~}'/?}Gvے:dyQSC'_E׾b>j`pmu\juu~|^ղ͍l}HV(5U?H8ӂ삦x<:xc;HA bvv9|َ!`f:X]??'_bcPPK0VxP}=b^  `b*c*ŮeUVޫilE);= K4Ljt8>ryn-,r"ܸÝ%rFNf($3 hV7̗7"quE%k죢* U^ëjDN.1[5D~,fR&_o~?aV;UUO^~#gv| ) Ba+~_6I35?q2:lZb:X8oZjBs` 뢸:FlI JNeNF g#Inx*>K"ǥ1`6Lyܽ1wP $%=_^a۲Ȭ. |3h@+4?A`e[{7BnŽRpcE;vlE]sA[zULdWU^j2e$].Q5[;-@Q#SVw⫫ 􅙙N13:3ك\?d3ޥTy/%JOb :lT*خ@Wglu\/pKQ-TJ74Z7'Gؠ92o]'359xؙqxkQ-]I !14῿w7Y[l{v3sbLJG͖eso!ÛXNߢSny1mκ,eaZe~Qg vJPeY9!1?O-#|Mw+-..f u3;keb>G|hI}˿o|n?7H8<9BD[My4y2Yհn(T22w37Y_<~Eto/f(g0;u-G0%޾yuV60ޠwE_}p$MQfBsTG]HngYVu_xgſ/S|qC 9;77A>_ 8&163)gk3+s›UAArd$Gzt"3Ip1`eES[3Å5Jfm6u4]oYvqҬJE+8GbݙV5R<ei! eR*9i!}f0YwVY[ZRsqP$>Hʕ蓮ȵ7__b[2_3lB4 Hv۵r{_ͳFo/})uM!{ڵMJ̶m6VH//Hb11<ʫ~MuOps~K>~6ݛB "҅"xk~WFc;>\nSVj$֕wGゆSzt&Ƈxݩ#'t@8dQg+a;f#zCD(TMc0">4J|hh_?]Ӟ2`m)~QGNK˗}wo}w^zY{(@<xy JvX(Upͬ rۘfF{]4OxbYGsOOׯG0!} /=?sOOZ.8Ozy v!W}ȩ7_/ݮz/$ޠ"Sl|g}Xw9-{ZuD𛝭rrՒE]Q;V08:H.jU a_!B' * ~O]"}-$KRdGmYlWɬ.Y]!Ig^G|h|3:o--tk$HsD^XZ[1{{G>y1#, !Q~ÇG;xAWUDPY632fƷyZ{%WGko}7r~5a~/^OِG`|4 [-<gf ͡ںov{pP@x F;èX&K"NKo78}A se jb@rND';z=:>>SBgcY; 2ˬ.B[{! g05@2E_<{lnn GN<ۯK7Y{ ]%9:Frd wIi㰺e~)RboްJ,oY_cwX?pa6*6''8:޳N[>H~_b(9r;HA?G|_tWWn.0;5jF  3< !Hģ21LzFzyV/( no(b^XM>PJyPro; Va1s,GfxXiQu#N~|$\NO8B>~9#s4["Hxd^@{>o~Dw~VJ&Abw.d|,}]as=uWȮ~y, ht^/@v]䲻ִ|rKf{B6W/-N;FSU&;Y祿?gξ!8ledVX[^$ygDFZjV4 PFc$̱ \|0^F(ss.{^ÂZY-f1DݶbWU49ImS/ڼsiFɗI3b{,e3S5084Bbhб8mbbPz._Pvz! 2<VE~&8g&1WèB)=^ ޛ zFyЬK6qo7?z>ȣJ%xA^y~qq+eJ\VV=40-Ks^@h~|4N_`(̥Sb&+K-/^Ze fȋ!4o@nXgt\+mZ*#4UaPi-!p|-Nsmþ W&NTMT~rNJ/Qv^RUhse:-( (-e_66zYVsy W* *kaq)TvvsJf=yR5O^U%Jфp[*]eTlC$#+SIWh۶f\B{b?LbQ>=pY[ee.Mj 1 *JR@ >| H`$J0!{1uK-c6O2sU#~;[daq |PzB6|(=9`fرKfCyba n<#K:Z]* J۪` r_O걵IAI%Yaм뺔evګ}^޽@X s}]`b@@' JoNDsy"_^VڱG~TE!B15nf*888 *JmoN3~j<5<@dB'?o][^R*:|$GF< T+e63l̤qV-K.=OOODrH0Թ9PF3+~CA/éPIP:dwwn'k@)J[Xc777fhF26|:K77W ~3x/)7MiηBQN7~M0B2t­E!j{nVT[ojzm _߹VF?AEvaa7 <3:{Q}Go~Z9 k|?o,mILfmFO9 BR![]u)'8wS7 /fX^씅^tt> ˽<5rRU-kw'Y:(Jfm۬033{c ($B#'W/ ?#"y8U)|ʻ7]%i>L04-jj} m+q5Ym縖KŞ=Ksכ& W&Nb7v΢XkL|6n$GL uW)J<5L<5Se6\_c}e՞+YIzy"oA&D +=JE mͳ _czPI(>LC\`uȥ +}ڧkj%k;5h-uOnw & J"?o9wO!R'>RfTxF f-м0[YC*^_}AF7vP`䥽+;smlU*e*j=z5^'"}\~O}xϏLqk-[! $ZW?s]:e2+Klod]W~SNv d6:5JX]BW#9df*vXMm?2Ex IDAT5{3v9[|v@ԺK4\oe =ܨؼ/muޅ~&g͗+q^ޝ]Zk)Z^Us d[ݽ ˝&rN>|"1}>A*yddR![.2loHs;BuCh#[6'L^2dVY_YbyNG8\KAmյf J.I%dž҇\yRcltśeiz*z,;P L䯃87s_e 8g}wZzjόwmCLl0!\_ $I !\$F xyLQꔺ<~?|_߹#lK,C}z.>zӓ,Nۣ#mYM-pC${2lwWT &xp^5}*Fáa2.-'h$ĻMV"g.Ǚ:V-0 IZP8BLv޾,=,j* ;2xS(>&]NJtU ΄uS(PR8gN9b<מr-rÉu+ϔHD۷-uP1E$4 !Ğ nCڲSvoc =:䞻$_~c?3?%N)9_n֖La 8zKKp=mӰ]G ο{էз~䖟np$M\(aO<s9;4b\$r} w8DžۃFq)pu(C*6[}$ݳ*D#:詳xRӝ\o[^$ȋO?I4h{|÷ZYжC*T7#3Ɗҏc?vU UVmjEz\mě'uk2~oh0:jA8zyX8 1$1Rh{yxx(^UϢD2'˫r{oz8[+~nW 'I%ΗF<=̅ 70^8j)~nԫ_˗ Nh<ݮ؎ZRzCܟ|V\z其_ Ki0Ő[ÎPޒD/ómoˢʵ_wRNsA8t  b8"1t0Dd"y qskEnD,p1~Y]o8eOR֩}ECS1RH9{-Op}pm0Һ7GC83S$= c}CHi^/S3+/!?{䯶upCqWvwe<(}JۙxVeS_G۟M <&wÙuzsudh0-cic.Q{V׍=Uqg=-dnP/TT*J:#Gxs;VdEi;({!iFaݫGQT"L5m6Dqk9VX2-N<͙ B VgVY] RwO_/C>|"%7ޠ/ ||NjyDU0,w"n$i|g?屇L$ޭ"C۴%Z]Z؞ݦ O1 2t7z²kh*ݺjj$ FG/Pdh`>ҽ}\x ,nBmc8c$Cc K̃Tk^u\pBP-8,Z]m[.򝆌\O5A^oH?3e[oyw>i9A8t^q,ң 4}m+f-BMqUݯ<0[{LgJ 'ma~~UfJ/jTvH'W=gT^~YN(VRzUƮ" 3 BO˛?Unh%/C=t/~O=57.wczϣ.qv/n(_[瞯`(jޠT11LaZdY!F56lYw?[7X?cUUl7ju9l~!m*R)JWdތ~lJ!~Ͷc͂ru<]JcAY[O+) 2E !_qU#>:4מ|Ӹg/ CöVJՎ,|OCȒh:ÁBvD' Œ+4cx]NǷЛ ӛB6˹xItsRDTBA4%PZk%VJHkCth0-k,Nou5qN=0 }6uݘF  T'wa/MZwu],jbǘ{ֺ[hi ThXx>\N||Jm)5ﴦ_jG,5_龀^N ןKX)ƽ6I^^S@"H ?/"<Bv-][ lcS#|mjm*B`nLEOljg&=*B45÷Wߠ#5?UCHWw}meOYG ?BqZ!3fɑmQjxw#ڼKhSYy݅\w,̮-< *|W/HџML:.J([n左ܭ.>К,#WGoB9}oC]E 9Gݲ;i95FoV| NRGG$SW? &N~J'?%2' 3F sSU"m龠wVֶ~Y^>ێZUVՙ)gȲDn`uطK/緧m75(T- UQw#է;bmiSԽ-~0wtZwQ=*}a޵|&:}[zNwW/=_87vleruьB$QNBpb/:bѸxwNnA˘85p Ao4mX[C$ҽ1V˘v}]ZL!( uTUZV%{{n7Eo(%$}9_[yg?}k-\("NʾgHB4jN$MwŅ#=CwAH$#'ɭv7/2=cy!:wJC]镼AL Zn 'oV B^HByǷۯ5젗 SǕD$YB$ʨ/Eeu\^ze߅L84AT2 SiRl_tRmZ{lXQ$ uIhZsO HªQE DsD|ʩLj簺d!6dYNow#ݰ ʢIeqK牀pB&G}bP;=zMi_^3yJb4?~U=|A Ig{b >.븮2noF td(D@d(DA?b("s$/=ܰ@7A:'R Ų]"M>ʇ?QnL__z|?.kۿSIkFB$G4ɛ5`:3_-3S?7~ё!]A{j;_c)??|a-7)”R#!ZLu%.F!?SA[Wf 4/zO!͞M]Pj4{ 1q搕nz.Q+3?nmuT(DQt]FLUY_'3*Jq"۳m*zD:[QVrcݩ*L^}L ŵ"i 0FdFG筏[o-L`f~)秘bnq3uGaZ0}h$*~oc͇niOz L w1BO p<yi̕E AtWj5yyuAГi2_l4d{O^>erJNGlgg6UUnZDXڨSSJ6c-@v(!?ϕOvOj ʺx:I6v\ :}p]W'%>px7Coc~JLM>==ͼ卯 5wȏxҧ>O1wsH9HdC3/t=}L]_vvxfˑ,:#VK"z㪦Iö(* ۦn[D5UdȒHwWō~Igx›y}&'z"7E},غAa@ ZZo0E< b~drkM 8~On;bQ#{ $Jzʥ:u7Rʚz#rl_7O_oqS$S4'].Pn/]_ۃRUgf@7u"3wFv\j } U~yiZtӗ!;Ec$ONͲZW1U%$>6(Uu\#'))!H3Kj,P:#e˳|hra"=EjJv׾d3d(W,US'~]@kw=00 =pHAeZ.eaBb5}8󘫔9ASUۘ<j lá7`踞"IGTLa yAS[8# xds_Ɍ=d$ #0y\S9s)ra6|d:I2 ^oֈ =TOLBو.+:aEn%Í L0B!?2/,d'o73Z!/TLh[,%u,(hrc8Bz^,z܆D{¦,bIoX%+[zD=QY|+u32/O=ܒ$~YoW 7-x3}p`o3WA8&NKУJ0, MЅ\)}3JަgHE m\uF*<tT YX&3 }1)^!Yf fTMg=Q"n۔MݶQ(Tnyú9AFO%֧2wdSZҜAp4-ksK\[@UdF89pс>Cpgmu] < 荛pqӂ~*{K 7>w3tW0݋LMHzJ[a8K&ɔ(@wlmǡMo81۾(KP5FͲ&!  M 6q=<ôm#ܫ)~p)g 3޲n/scޯWd>Ng99el@{X $B~u 3Grg My Z4QTx\ec3EYXIB$4 !JFo$raQ$"A2ڳߺ$QU%UHnYD{UF՜EihY \_7$abwwB}]AvxD:A4u7fofrU 9+G7Oo oɼ4^fVw|\b}m. 4eb cn_3qªe9 qD"آ5X{da>0:2$c.璉GIh~1{Բ$QE%ll!V ?/LJsfm |W_|/~FRqzR c"F 4.K^̃9܎ܴKdO'ɞnr|b%DvᓟKOfO:؟Z3%%{H 7y,/+ )T붍YPSI'5DdT8hCKr\$T$񦯼8莍no>+DJ x^SkBwi~MQ)f nlePfP&($ɬ}D:I~e$ 8*?YEO<N<}߲G4{X4sKi uV4E7,9bq6Dwggx$ZFw,kd.L*qU# PVpD4!,2ɖN;um)m7{,IM#i@ w]Q|ݲU#= <(/'u:Emsu5y{? vXo'MNg8}xuȃ#}; z<BQ6 -6\vqR[&Y.ˋuc,\5Т2[Y$c7]rue> #y*#j[\\aSax_.}ڪ$j 6Ur] FwHαqܽ%!Hh~<5ˤ~@7HHoIaTgtkﯳZ(Sg|(˃&xݩ1¡ݽn- UܼLXk 'z'~ wkX5o~͏51zzv~ϩ,/\YZzYkz"lP\>6X0PUxBƳ5N=VY"ۓ`9_[v=r!$p\YۜP'?haӨyk߅q!Y&$d¾IMq?JHIORY2Y\g:V}oUX)i_s纝KHE#2 #Ԍ*+NrIK5QbqE w2!_S6FWdTJ hq$!KC}i$!(QEA;ƴuUV5͚YSHUHDB$"fARhyRI|3a2acZ+u z a;lyu΍ .pjd`z.3צ)g;gp'pNq ^#c"qs}XM̸??.>~+rNoLmn6LN$Z(řrS$h,u"$S3 MuY!H|NC7M/ժCaJHSM Er}_Vc/*uQ,GE8.bX6yTLX?߇_쇯nYMyJK][<^Y`7ם;Y+W(۽kan?nʌ$&>_mmP54lӱeU9!sҿ zTWIWƁ?ٔ?*,CڋmD@h,Dfgxr{LN~R .,uk8.=0 RYڶ#;}Ny`6icIkBHUTX8@[/x]>łxG(4*.CgB(5!uf-qu*-=a:zRO'4~xx 8{QJD~ _\]?g'ž?UDqOy_nl=>h%eO4+[6UˤfMw=N=bQYZ'wNiAg/:LL d~uC9z϶DP,Ha+xӵ R6q]l2RxxoxUZ$ aneaE!j$B!CwuyqvikhHB4 j ޞ j,84={ξD([V==Bq.Cr4I )$SKaþo+/ K#l-)&)rHp3ޫ *@=O&[u-DTt4HIdFߧy2O?gKotg<uKi߫c?q,s6>$I0᜸"zI ;Ѽ8Exaԗؽ"3@ IEda$K.Z.Z<2^'b6oi,~D%.^w>nz2G1gQno:-r"S~Av5LHyt : ljx. %LZY]9ٲz]wz7LǂGs5PБH)$&$ 06]^8KDI#ԘiYLZfˣy8#LK-pyVNcBw>瞼a|gZ5)_XVfU!NcƜ+dK̏MvD}KU1s#-_nv-!a:3e AHh )i">]ĩ,a6'^d`dKϞ[?'7lQRy2jN>l qB̜ɠ(ZD0yN'7y2/N EaҲ)Ɓ)j/N]QrMߎG5ZCK2*V8>O w+Ēyx .4UJ$?!aí4q%O?W?G6ެ5X_wnסYkPP_DEȲsAE!P5l~m̘--09Ew;n]Eh+xpTj\D=eI&dH$[.W%iQ6-"=GB5.+Z#ig]n^ HRFR9^([kY'P㘰~S6n».IP/1ѷ&di{Ǵ}eIRU,U,Iq̭k=e Ρ׏ͨjKRaF|S-H]r&c:[ bF[7V3€QWL[DGwԳOSvNE:ǭ- דeG\>f NUk<0+ o\`]ĕ&겤$Q$yeg$b5nUT$aQ2,bbZ^@{S ?j4]I̭ؗ9_-ulfb)$'x&Ae>بordWoS~eQ$X^R,Kzk#S(Ψ*׷xO=5jH[QN#]uGDTpq޼Cumcv]?@g#Reb,cͱvyo;J t?u2 0 #AgD=bus.VuÊy$o@'R!?Sܽ~)< o&&(VBqgP$m|N*fLM٨Z"q$-4VCS*vHRsM'6ƍBnWۻeIP4Yԁs" lp~TYH}>-ߣ-jI,ig2l8WJ}1Ϳlи;`/|^z걑R۴e36e f]?ObbQQ]& gO:<:[07SRAuv6O6%LFS^Y ߨ9]jNO!9PT=:Fa"%֚RXdwH@V$l*AHHCǢ4 S ŻqhV$izB޳r6:5kQ@]BύX\PXd;CAm)”Jq.+y]'iZ:~-氳%lD6, _Y ${3U8y^m[X:{J,IͤV}6ޞn2~BC[nrkq_O!K={0ƛxvskq}̘p"0t"2+hCrԩR7nsS~b?ae0 "FFmc~N;`n̖#vYkN,o*{Pte/^=˷~p Èv%kKZ4 .s}FYÐ"#I1P&=FyqƑ\ O'4tJ|f\el5EۚXrGx1Nz(ێq"QO]DȒD^7}tt`;!NlZ;o4sz9֪O} i\~2us,S9l̘4Nؚ ϡ0Bs[_(m z {YLYF0 W6BZH֜.WgR`gaTWFSe==kf'yC2Vw\b֓FxH{nDe0Fq$h4V̬L~R%[R9M0Vc%n -.a1;}KCQxE}x#yM/ l)[!KvLH{%U\~']- ?Wxag3XY0 @4z.nס2SAp3ܷ-aaLv?/|nEF 17oxhn'f|~ҕ2ʅ2o!Lju*@HF/tM,/v(Mev|6d'ꉍ cwc:Rkx!(oYni"kk>A?Oت㘺tjU+ueM53 ^+ski_'OYD'&'XO] EU$g*zc-fp~('1lDKuEBHt]5m@wU2RSǼN]dQ-\(z,hԻ`i*/Lr}JoWYDLwjURPt#Ak=b2I܄r* 8c:JD=M!H1%A]I:]3T,R-f2Rsz>p?%o|pŵ*S;g7R }uz 9:|1*0S:]f#yRb@3D'dn ^/ƻS]lI%7beO7jWC,I{RI}CG,%wRզRQ3DBܳmr$Q,ʖE:.^"IfLn'qcGK5͍Wv+ag|J?aȊBR։ustt,cZMռ$mw"ɘa#*"=@U$Tzڠ`=BM9l7vpFÌ=1,qujdWFD2q-XPph|ʓ D jHJx:tjDŽ)i.WdiSU͈B$;aH;^ִdጮ#&Χ[c" e۟L~qN ."LI²MH# "TU7fQ9wi>(϶NzaѫO;Yx2㶦hLZK=DZ (&.]>UES. mJy/vERz<8mK]V{hiMfrb=k~Q5=^vn ¾5EП66˽ $aV9h\)( HIK@0y411^U0 lo`,dˆ^G2u;fIp|֛cdz'zR޲L[T#5"'ݪ,I4.ӏPLL֤}˜ejYޛg46zm`)m% su;nHdP(#nS_ PuLQ!ST0ϱL\RÈ FbQDE7;Yn ̵W^~L&յ~E$ EdMM+ĹlSQYvj~sN|QǏ">~~iQ0lX3=%6EUB%4#ڳ8qI$TLM4i>,a)*g0I\6#c*MZܣ#RO'J΃ț:/p}YNZDXɦzͥ)M#/h4BdU"SH+SdPDJ3<^58okn2f*\бz % XJI0O2f( ryZڀNITZ}izՐy,_6) z ?n0- +;|1GsQ(z1zHrcJ@WDsG2́۬D,NN yE6M2\,0y8s9o禋LLLc3*\0w.xR bVWz,w PЮ{Mv5$G{B(荆=u9Vn<::tp ^aƺ;sXlYx4pY$E=˭;MxBeӶHKc1sJ+ w ][Sn8sF?!gۣ^]]@rBo?Iќ W<3+S%/F n0'rz!wԪ.YA>so:,pi`Mk@.k()p*nǺ)κf΍Z5`mT:KNZX6 ɪݾAQR,M^FHY/Wo!fݴ; p̘r"LxQ(v1< P(]ݖɗUesªdNlM/5. Of*9MR5zɻp=_fp pXpdc_ȏ߼3B$uscg^] A[2QV:pzBh6<-lN#_0дcŖMLuqn޺d1j#$UJ;ЙD+}],fJ8:T]g{LH bW0 &-H3a( :>wE/oguj~x/~e+c ɋk79юx,QDsc禬 , ٸ5ABn7ɒ#B&KlaS *|_pgs(_v{dQ5kg*ܪ5Y%u Qqmq,h5}ZM;Q(ž V>LAAT*,kf͘&gZA'@-|yx~`c_tIMhy%31?vE8˳Qw6ITT;>'LE_m^{Ͼ|klXq%TU3氜JkapԵ0 s=~w{;8ܳs:nu,Ed}#n+.V}sQsw~=EyFuYx\`ܬ54IabQN-z݀^7@ ELvo滍ueBΰh{`'IZ ]]B֤ AhzR,{@uhxnbciPVS܋(5}nTjZxW?򵥍8=g,cNO"Ek*/{GL/AjK*C!@oX! b\Bƣ4s&[;޽SYTEpdQdz`AkZKȫQV"#~C Ƒ>ixh3=@ !IPLޤ! 6+r dҲS#X{=ھl&;t}ڮGnNd 3 l}AhBMVU. ̵}n^F5^a\{/c ćHOBE槁D,N!碨˨7XIb*G ;~#Y\rbg:|{JMUIV]n7Z{z/"*<⶛(4͆G&i'ߍ.tOr$3#cf~1Y̜؞0Y:ۦlZuv7 k6>C3_mPR_#-cys,KLdl.U؛ prP`hry}QURykX"g+W5EU½;82fQ9v|EW]< LoF磤C4 ޞjҝ6\qс´;ݾ)Caj9{4C;ֶ?qa24;ń_RdM[:Ngy˝-6^8݃P$fp[bv5$ 8wk}TH#!k}jKzD"iaMAcG9G eI/Q[~>]QZJ =;=1ٜ]?tX -DW{t;AaI@^QB,J`safB$ ★Nu:AC7xZs ZG!U޺3]Bl6G6Eԋl6}%?ϗS_ONujaqnj9&_tIS7Ŷ[&+*IizJ] ^1Ct[e䌇HQ]LY]m_]X_8Ͽ{6AJ$4mQ0 n[{;7݉2qVF0N@$lLFhG.Hؚ? ㈸  r%|Y4U,CCGUb/3̞4|AŔ shAԻ?[ 3d6fsm,c˦(yfn-'|c#_W:cycNc5mQWݾhWZJWPSWZ\QN;p{aۉķ_<\2sO31>o*x hv2hԷ5[Uͩ#yE(BE\_\NKdf3ԏ{%GF.͏y <ô8>d9K L4^b~Z9$i41 %46}!Q#h#b+O4'>$?EyySڙ OM0Քã⵵9^Td7{u|GuЩE,K}Y^?lXm94}U(O#?F\p 3g!՝oysV}W,I\9-=*UBj=|?ܤϥ>V2ˡ}7֏%m?W;uNr)o|LWw$I泼|v(N9}9BiHsH4K̷Oc>o9{0Y8K޿L;ݾiQI޿g+{K]LO,րڦ d2*gU tEŏʾ&obgX 67a7@R{ "j17(i|-YβcZ!;PR x۩ YvX6 mn͵- }PDdYMIOcrfn3ClƀLf˰vs?ҙ)ǷZ$R $+S;6!H@ag/KnjITdrRSڿe!zFMic)E*0 &=”yX1F#=,]^;cԳǻIħ^y+*៽M㎴[= CUx\B!rJ;$_ B-L|F6c0kEZ-S>cN#ٷ( cnGpzLg9Y=us}0mgw+l %ӢG̏Dg!8^me7HЅx=s_ߋjKx)-:P⽑q/Xwz}ѵ I~}(\~zR(<ĹM5d.>}C7&BW|9%Nҝ^ ZP93HƜ,cA?,l,t4TdI& u"ڍzHVȊ y8攊4,!pΐϾx|"ch'6Pdsӓrv6Dާ'J~g07 CU8WTg%d_HLlڻ+;ZI~Z{-Hum/@(OZx{QHg?{fCwT72esxϦ~oajt` -:>0}n]C[Ay}`HzTkBeyba?{hduqǏ"GM#~?)L[I(k.͆G.S((#f*:2󸁙몊,I}=La߬w0UUJڡ@%,mxcҶ4vOZU Qb?ZuͮzkHFm :q3ܘq94 E1Ga*eKR02kZ=Fv=M.o'Ϋ:=, uEnA"GG[ѹY{Mq76lLP[Xl$lVP2F}B P\o/7鹘kQAul)e 2duή0,u`˟/ƿ\FkO3ܞPhȨKØ}$b ŒW>2F_nsX5UU=ŋϝO-O|N\>K,-קz4^vyMGqmv'(̓]-`Vtf}HzXUL&ݏ"ھn9}3YcG9T J w>Qgv2eh7Z˗ &^>HYYN.Ͷ[][ 3qFZscAjLLdS뭍II$J9r6G&<?Gq&٧-& uq$W7S}nfjs"7>*ŨؗpTzHHU(>)&n+%8k{D'x%~9UO! v%<ӀN g@iEI|&1k=̦dBd͵Y2.fh&N{3<-h-&p,Xdrvu#K<p#I"tߛ(X>(<tR̅Y$\ǥ<5I<1''?7<ˍa_+I/%/gUQOvv\zX뷇|U ?Ek{sOcSwopw*eCldDL;piϰP7J! mJ&bzY}9d2C˅ ]#K pue E$IdZت:O%r%}A cL3xSm#/s\]V^KNc<)pչW2_ӯ:'=snpx"@1цOx%!q]njj<…{xEk?sJ9e)qQ$ns>S42% " ~oErZX b7Y^ڼG9DH3<#A(9&iK\(lDVQe qsrx`)1cNGV?_:'3$C0"@"5CxG#=a}-$ vZ &s?WSC"9d.da2FMAˎ@[ ,,tR9b禋XL1]nqYS穳SǰiܾQ7U l2xz W7r V =7o7s}?1_~?;C-zv]D+3AVKFv Yu+NT)gsR$cIG3TV LroH4RGRվcbtᑯg̘0Ca;=;ᅯzjG@ DWQx@o\^0j ϿxXK+y~k/gX]]5WU/~)*,]Vx= #^Ar_nz|3ZH6= O+_{箞%1Z,I + Sf/b"P76͆Jo{V] WrF5drW'L2(}̽`,?oL(RцOpG?pSuk'k׏b⋟_ꋼrEXޏHDr%?GI>JV Ѷ0FЮ3n f(l, !x^QS<$C_cA'sw6X[#2v(O>o_$_U. 5:vUI&{Fk\ߏXZBKg³G-iwk_PԾd+%/Nߝq>yS_y77~뻋CY44z|qֻX^ml;躆FۥIPUґ$ Y$ EQ0tаLLF"N,c c\קq 0B "?(1dU{}dOEey{[%rَƹMt{m?#,}VUڻIڽ>d+QXW^>5scA{ {Ǽ\M~tlk\$5t~dU0DeE!1_x& 0h4:ͯRo  Lb0 .Bb y?|p&B} aʹ>ӑV3 vYER$ľ^7);=6-&C_Ә11M߿H=x7vxDut}'(ʉB!oS\0$ ] Yj;C^kujL;׷zeGyfQhA$~(PdI"uh:t$YžP绻^Ȼ7<{k3 ƅΏOIG})Y[ipgc% {}Vs2W:Oe>L2{`{qA7bޙ.8[i;*71G^Wki̘a>oJ(Q/ywk׉ug]][{mW\}ݧU}.}T\Tݤ5B;*i4GcGLUyŏԗ_'lKoMUBo H}Di2#vk)r3 Q|(vCpknH4f0Ƃ>f sШ jslK.S[($KD.Q_[p+L;Ov?@K IDAT Pgz]ZƌXǤRupuc>h[IeTHjȦq>3wؓ 5@*Ƽ>go,"{{ҋ¾fQrƸ08fQ  m!CS3E]Wuojð@U>ʓۿünWa%a|='vRFJ=Hi鏎Ш7f0֯ ۿ~dƻt;]!j]G4]Ex5B87;T X&t ^-8u^e/d A4ggv ZVE (%BB>>89;C`q1ϷoK UXرG"r5g>ym"=S}{aL&:}kZ_U~p.+䵰1FI߉AEB!ͧcq:>vMŬ6Y_m"I/fLLdzS?7T!}S[hkpga^hUo\|J'1BD*2V8^pmQG{ λ}yW-Y~-K,ꅰ! 9TB4ZߘPC.e{PBX%cdY;nyvuzf~=9}Oz) pɝ3,{|m| )Fnh@&U@&U[A8̼-ȱ u$tr(2}ݸY$4`:~tI>T;,Jpv&$@ :ozӴP*SbpΛȤ7$X]|+o2~m;[iXx'pcg+ݰ-Hߤ87&Q<л[v,}t;*vKJx8wWJܞAZ;D7~JIon'u; Ib\|d`'[ sӮs:3#]dvAFJ@!0!3!5-&28;8-}W{!CnHO!nkU#4s8Bl"8f☈mso^P ;뙌E P(fL `NFk8b+ HM0-yE-}?[\n؇,K\3sP~KJ%W}i%1+gf\&PhLshV4n0\(hMcMS zS `uzmܛ HO9X`]l US0=T6nbN ކ^i$w{{ډ_1`@"K0s6,m\GX:T2!A6jev?xӲ4^!j~x-(3h :q {Hb*p$-"Pi*,Q) Ee}y(*'ڹSI8D(+gD㱙>T5! :͂^)ZՈiFީQ At )a#nbw;>s+]D K^aDBc[㺜%@e10Ȩ m4PJc4S^N N tȨ/wLJ(˽;p5Hfs-`%0b'iXy6 R&-TM9SU;sw}yF3a,͇ q(# 3f:vɌh8`/s-#nIMR/ nqzk9aalF=`YrظM$$ce .=fr:#=$ +AA7󆒵Hrv]t)&W {Vo@0PrIK08G8b~jµNu\EЛ.W) G3iDDhnz8At/B8`i>3 a!,av.ٹ8ރĩviB!;8ޗ$ x:"3 j*%0Jue" x4D{CMsQsқ{"6nfq3b_ N$cc ƀXV,,,M[Y(q$v{{zE$I[ܡscXO1`7 =P )$ᾥYd%ˇL+DK\= _Za~&t]"c~i=ĝV } b^OT[_e;=z\A&t8Ib4{orV{ }z14iM0bqa {El`V .Gy<.^HSN$c|Al W, w 7Io+^-CIdsp.0==`{1?K.!쾱q2!A?F0ưE|$׼ Z@ҹi,5ą>q37 08xmv@l"KƢz;G~.2%ѡg썊 X^^f?0 OHЏ1Cuτ\uחĝ6Ri~?3sgǵ̻-nl yMc~owiu\k9*PP^ r@~Bhv랮yM\;EqY8D} 8Fbz^~u z=] YF}ܥ}fz@{\D@cXXX‚kI 6yf-ڍ&1#,ۉu'5+:8$1s+?N( ?w^+q^r!~ԠzBtbu[y_N~$0cqrS5Ұz.!~T*GXj>}q4V(wb\"n^%5h>.3\P!A?eqk/9` wiӸzmLFX&Bar#T;8aR 5 }Phqr|-1rÄ5_}/||D$oMc:C8L5?,+qP$rCA,wwrE_vv-,#42zV/A'[J-tCx=]M@NRuOD'$1nq5 B7ͭ;X38v;5"цa+s׽wC{}oCsa4EF@Sʖ|sfG!:X (&n!A'Z;=_xHn`+I P45M{۵;EcOLb!1D$xpR揎w׾j303?}%j/Z%COjkF7Mde*: AMD$pS>bgVkSK$ A'Z'~g&,n2b)I "!h,rYzsAU|")5DlݸJd ,[D06 JwSΗ)Ͳ %h8Duy3FYxmX us_!~ A'Zx#b;o() L z@>]c *!  '7Xӱ>'hHLO"1=BEU.mq =һunZJۦus1:Mu;bYJM&A0M%O1|H W4^U.>XZ?B3&)d,I * ɵBgfiڡ k~DDbڛBxsôp{/0w[L~1D0.ZOuZ`oJ]BN"2~_*o&Fм-rL,C4PJЋ;`*E IMF(@ 5>Kl@b ӱ [''1Bg5,0L/~w|'Kn*@UDJGW~yu%h>gOD'7,[ঀ0X@vD "!~&,]1QJ(gLpK870Q.ԀP4@Hs l{ ^p ph/$8=GOאt8@l7oK:_DEwi30h$"`BJ2*r ֶ^:>{:y]J7^0{?k_/$'\z??îίJIQ6;J)9֕hB1DfU{,*y RڄQ n@r\ZPE(T3 l6$03? IqS51m{/cz B2Y1@{UD( RkR@XF,l' 7)j~whHЉ /HmgQ.\- jf!ebfb21^J(M2&%^e&J2$) iFml}7n&7#Z5nv"?",/~AL2!j\LHgQRvP Pp.Ѯo|ԔOj(!A'| $x7Dvb 9Iʼ VuC$PLrݗ1DT{(9 dzU%rld9P)㝝- 3v97EW_n*Ӳ] HMiBXnm 6<ż5CB,K ё]{]qI@N" o2BVep7hZ.-X@$(-0 8!#8Xz/{5^pF@!];*6#jЉO9_k~Hlſ}8.*jȼx`ȩ 9vAtH4_ CpW$~ A'|sv|EAR;9XY z@h^W?Gޗ`!ơ H& RlH(y5Kď4s"0Gr+rƄ2#t,܁p\R4% H\)PP"W2K2HXANN;^ aarKSy[35s_/#IewjnIj% /  ̞ , KM`;8h"1t|+$D|~xl&Ԁ ɠ7[\]SH0 @% 8Pəf2f IDATa\W&7ZXC\[[%u_~}qCJk 2>o#8iF7[-tYI(1ǢyڑꍳHemz=A :51|=MƛBV$̜M`7]4w7caVq֟I@0 W0 ,f뽡D) (![$RI\6bBb 8%׼C//}uVD.uoo8 T>stܲ]*6z'﻾#^!A'z"Aw#$av>| m,v"B1uk/(A  z/gػYi<95ĠTd+ƛ[+_\:e@L :6-\҄uFR(A E Ez8{zA4"LBXwnOk'pnv ]pͿ~}_:6v`5)=h"'p^[K˭#?zZ>'N$@Xgϰ_9 h1@-l^0JZXB0ڛ .al1}RAhҎ%'2QYp;c7":n'@<$_^޳aCkfW7[(ݐdpC ԣ) dj2u)MA:-f-(j.Q(vʢŻcx!ap<4= ln.{&4/0D/PQ. ;E.؆i,%I ,AeY$! eޫyKwԗ%Rʎ˪bd&nxw/&5/x{#ko]<%1̽7& 7  "+l/(ۈ ͳM(]_*ɞ.w8oM)ɶ!aCN )wtco;z @b8úɹ B *4'F8{ɜocWKf_(LZ߳Źwx-ucVMc;]4\n#_l0S#d!`&,dr;sMagOڻvAwؓդa,x۾;xU1XHЉ/ꗰþ!@p䙎g|3rVoIJsPW T õyIp.P((&A ܘ1}h/{.zw/{Ч9l&MT,$&qy|x!14A~oN*Ym73'z+1%b'^@("d9Jje_%洔۷\]Ha*k-[m/<] c hAcwJbC#N_ |ͯ~Ȭ) aȦ+t˄n?GV- CS`L`LpfxƋc z;Mm Tݍd0&603@XjrG)WA1WiXd`]3s*f㽐,K X&a ;E];7DI'~{1HЉR,7_:bgE=mNP1 .5hV 2cPT{涏 ̀!e]}ް)PخrzIjcb#ΘX.4,r'1OPT3gڄaĢw1s1wQRMrȤtV9b0Bw?jgPþ3S?]1*HЉc&>k?/G 0=(guWf2`.A$TB $Ak a3m- j׺.w-jι]BWȘ> b&F;9ulb{f0 [ YJ 4YAP:tm/ >$yC_'}?!!A'F_D]T0F~w-.xsHb`P4@ _V;`U'4GqpL)m}%Y*WO5|2sL4D#GG@arW!RvmO".?sbl A'FUQY0)a~!_G%g4XeSxf$ծI:mW3O@D{+`BAlA;?Iw ?n7`Z`ΟEd_y]A :1r~_Բ߃ ִb9njֺJvȊ-Z' j*BIcgLI3EYcB2zd*WJY w T ru [ cPjYS?3c?t8@(9-!8bf.ap~yzV52\FQ@HA,OVp_;!~ A'7?_nˎ#*ar* dY׭,a ޵38=ݙɐ% vY qͰDwy?*Eml~,;0=*=~]B841(HЉ#X*HÇЊ2Xq aa).BNZaF3cN+n\#A I-"neU\pXB@p! 11(U<$Uo_Dhb<a9i,$1 MQEv%Lc{m _Cavp{zl+$0S2LM1Py=he_mq649* 4}M"I *dU»><{1a"+P;ր HDCk!PZ(d,RVv n$8x?ž[kGBN .֤dLOd2켞Qj?TjA zg˛Gò<6fՆȤŲtԶjGM@40]Q9(7[bYCQ?@tb~cxղC VBHTT*,cʹ-> E{};pݰP,W;!a[>Ȣܡ]nKz6^fTy%vd*nba~泐|'~!A'4k ~dž9kB]јdEB~70*v;/  ۱Q'™ǝ4 Y0t,<(4D yo㎘=mvzo b A'Ɩ/_W~{;57 X <Ci[Y%!+~wd@$AVI12a;91z 3Q<=giiqKpX: Q[$dpf([ɱ]l;ԚEb췞,sA tbr~g7|Py΄m&&Mh$SCnKF/XmK$R,KY0Y Uڊ{b2GnC7̭Eiezx+I<Yhjy;߀tr &szؖLxb`T {%O}b>b˟Żȱ( A'׿KO>R 8n阊xQL1YFzѶڇyB`fU"ܛH>o @0"EU|^l<[56*cmg e94guL|Cʻ>V½ v}^z(b>?>gqr_A86dYe ZVTaB!ۅk[9dǘzaX*Vxx*r5 }Ew 0,p[՟#S)QD1<ެ钲l.3 a:eetrJ-gg81QAN;^g~cFa`EءΨx t$oP#[wrO Zuzar%+) ;&)nn'&e*U reLh[L+\(pu/g0=91.ǒbsJXITј@ՑG1]7xK':?k< Y_]b?sL_A)ײ=l!`MS*"nlB^@&SA:Y]*(g9l{7>; 8ln>o2s=*A%OH,zDN׹;61t i]bnYp3 nH[=b'?g2[-cZ.H+ PRS#'>Mىc :q4W3XhtuefBvNnܹG7,w uvS*beomTN$`@Vi*yd*A )<N薁QuaX0tA 5H(Z9qz!A't-ɮZM&uB\na+=Ǡ@. @* yVt蒮I1( ^dYrn &ĻMarz{Y8e[RAu$Q :A( Y ;%$Ib$@rحSp)`F.}#" k y=$ :A bU, f.uV*egUH t"][@v+c;"NH H b$UN +uILf?2ZA"NANGL_" k#DY`nfl}W!'@Nc@E UKsv9ۻ  :A#*Cě F :A?pkpCNA'qʉ% GH @NA't 8A   N$Aq A'  :AA>?c [¹]<*4Btk,X.Xx @_!> 2' \B\buzvA!$_+c+>W? :1 cG} .W!? cs{@Svom5+nsQ& :1 cD-O\1 HGT(g`[ !6|InbkFN c"d`{B}a].۲DR$-}2.xwIЉapLen A 1u3VWFYdHЉ|a_|6_z,lq̈́κn]%8 Hǐ6B b~TѵA' :qT8!xsB-+IM؞AAtˌ'kpV) H;'OmkB<ۃԉ!B !.8v\S!B#}pNn/8'$=;Ā w]B狋ϑ}3o&7TOßDGņ =1]^D$Ӱu-Gdow"A%G}U}'B>^&8Wnh/A J'8A r#oT\@]N򜏧>4l"@>>[ J'|8吠Nl-8 8:F'cκ=EtbXz|lؐl4n8@> Ab"8^_QD^8 ) :1H)OÂA?R  $׃v gFM'H'N($ǃ#̌%  K  xUGC)_ztߋQw~$N $ch>;d 3~]}" 8#U/M' tb(~jDԅBBQKزϧf@ !6B5*ڷRUkG0i%˨Hv~EKW^-Unh\ ^F%&^+i=OYbX>yqʸdcmpn$йNcfi4=Ѧr>/[ vLHIDAT؟vuq;j !ֻ\2!<^?5I>qan_BuZV~z}mWWHLj.*q1 hc%z~tzO:!/vT=q68`tJ 8W5gu[U󌱗qwψ_;6QsϯUE%fezqYM7. ǵZM.JO7#+Me#Ջ lk\\a~w;ɝA_“E)>*g{Fk"ۘ`Cö.u=V$F{\㒏5V랿v4=~a M6Ъ~;}O!v|{(%tk:[?hDzk]oޠNF{)zak]6=/]Z{;<ώq<+94^ {aQ ա)v]h>_K㡣h1 xC\bk^:1s !Vz]96_`Uvf`]?IlO"Xhn3ս >]uټ "1b`t>:a0ŽyN6^oLDSӘq6s=K cph*DePPtѸ_|{;-7AAekLj 2b^m3;,Q~Yw}4}Tp_~N ~ /򬣲T[W0J5,cc_ D64K1V BZ[2:[̓91K]}{|cuOfaސ2 ~ʼnUŽVTVzqø b<~/s A?T[uWϓOIRl3a'lx=bѴkݤ*k$DUP/_8Ӹ+6x=c`Aƈ#Fw';56z1"H ]FkɵvC¨fg04:Ev]ANR'Y+ߋp 8xYГodq U> Ac!N#zKS=AdЮz mSou!.s !A At/zQCĩjhq Xx)KkA>\B-GP:V-e J^ ,i+u)Tu AW]JW :ծtځ娎̌r ANWG"w1b㱡\x8SW{ N<=F |P*X8YĩiO&cHǏa{H}&C@0SȚcOKP}L^xl}ky<:\8uxJ 1dH5]s3:uH~ !AOt 8oa17!f㱖 M}(TuL!AOVơkFaux|z&CD&R xτAB6fSOB͓q4@ z>c^:$ Yw~V8/c{v6:l u&Ca&/kIPcHǛգҔ$SQ :tNHQuP@><D rN<W FG ^ haC>#:choXCסW]m u|p sؤPxclmwgZ0X!~aL8>&R@>lQ.x1ve','S| <;&^õQD;Sevu`^<ݡ_kuxCPP3qPrd3^NLqpqbֿG`z=d}ZvsB~?(@qf#ȜchKcOp6a]i[E /խgLzq.zYkm7<)m.nS}7z clnG|-̢|s. F.$&׭i|1f;.c-%g cy)ks cb7mm@>pmGB peU%Z]q.72ƺYO?!6c/q25naet6&ܧ%E 4 xaKp'FP e'N$A%!gB~N7˰+` z5k\yB^(sk$u~⠤1_m\=$8LUk]~~ \W7Gv0q`mvq } B3>ɽ^d,Zݧ$SRq U}B:qlp)[T?Kh*[,w8 E]xbIb~2!A'|kGuq )\ıkB 'JmP ,tPoaP2AC :1TcN g%nXF-GScɦNCV q\`]{=K4/q&|=Y .AXcWޖ9`ٯ픪}/N8dC ׮Ή~a7jGFh݌nG>>q!A'?*]q q_碹zQ1o(E\P`u`ut׵t5qcn!z߻4aVYİp-u:'N Ө9YtbXwx|A'_R !V>"8wx9T4f'tbXx yk^B<"s mNźMO :1^n|TJDv|?ѴyaWv>4'a JJ@*$6r`& }^z&k:kɘammig{F͏No~Pfi<' where is one of" @echo " changes to make an overview over all changed/added/deprecated items" @echo " cheatsheet to make the Cheatsheet" @echo " clean to remove generated files" @echo " html to make standalone HTML files" @echo " html-errors to make the standalone HTML files, stopping on any errors or warnings" @echo " htmlapi to make HTML API docs" @echo " htmlhelp to make HTML files and a HTML help project" @echo " info to make Texinfo files and run them through makeinfo" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " linkcheck to check all external links for integrity" @echo " livehtml to use livereload to view the built html" @echo " texinfo to make Texinfo files" @echo " web to make files usable by Sphinx.web" clean: -rm -rf _build -rm -rf sphinx -rm -f $(PDFFILES) html: mkdir -p src/.static mkdir -p _build/html mkdir -p _build/doctrees mkdir -p src/modules $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html cp -r src/pics _build/html/ @echo @echo "Build finished. The HTML pages are in _build/html." html-errors: SPHINXOPTS += -W html-errors: html htmlapi: mkdir -p api/.static mkdir -p api/modules mkdir -p _build/api _build/doctreesapi rm -f api/modules/sympy*.rst $(SPHINXBUILD) -b html $(ALLSPHINXOPTSapi) _build/api @echo @echo "Build finished. The API docs pages are in _build/api." web: mkdir -p _build/web _build/doctrees $(SPHINXBUILD) -b web $(ALLSPHINXOPTS) _build/web @echo @echo "Build finished; now you can run" @echo " python -m sphinx.web _build/web" @echo "to start the server." htmlhelp: mkdir -p _build/htmlhelp _build/doctrees $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in _build/htmlhelp." latex: $(PDFFILES) mkdir -p _build/latex _build/doctrees $(SPHINXBUILD) -b latex $(ALLSPHINXOPTSlatex) _build/latex sed -i "s/pdflatex/xelatex/g" _build/latex/Makefile @echo @echo "Build finished; the LaTeX files are in _build/latex." @echo "Run \`make all' in that directory to run these through xelatex." .svg.pdf: inkscape --file=$< --export-area-drawing --without-gui --export-pdf=$@ changes: mkdir -p _build/changes _build/doctrees $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes @echo @echo "The overview file is in _build/changes." linkcheck: mkdir -p _build/linkcheck _build/doctrees $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in _build/linkcheck/output.txt." livehtml: livereload -b _build/html cheatsheet: _build/cheatsheet/cheatsheet.pdf _build/cheatsheet/cheatsheet.pdf: cheatsheet/cheatsheet.tex mkdir -p _build/cheatsheet pdflatex -output-directory=_build/cheatsheet cheatsheet/cheatsheet.tex pdflatex -output-directory=_build/cheatsheet cheatsheet/cheatsheet.tex texinfo: mkdir -p _build/texinfo $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) _build/texinfo @echo @echo "Build finished. The Texinfo files are in _build/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: mkdir -p _build/texinfo $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) _build/texinfo @echo "Running Texinfo files through makeinfo..." make -C _build/texinfo info @echo "makeinfo finished; the Info files are in _build/texinfo." sympy-0.7.4.1/doc/src/0000755000175000017500000000000012253362407014600 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/aboutus.rst0000644000175000017500000004405512253362407017024 0ustar georgeskgeorgeskAbout ===== 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 #. Sergey B Kirpichev: Bug fixes #. Stefen Yin: Fixes to the mechanics module #. Thomas Hisch: Improvements to the printing module #. Matthew Hoff: Addition to quantum module #. Madeleine Ball: Bug fix #. Case Van Horsen: Fixes to gmpy support #. Mary Clark: Improvements to the group theory module #. Rishabh Dixit: Bug fixes #. Acebulf: Typos #. Manoj Kumar: Bug fix #. Akshit Agarwal: improvements to range handling in symbols #. CJ Carey: Fix for limits of factorials #. Patrick Lacasse: Fix for Piecewise.subs #. Ananya H: Bug fix #. Tarang Patel: added test for issue 1640 #. Christopher Dembia: improvements to mecahanics documentation #. Benjamin Fishbein: added rank method to Matrix #. Sean Ge: made KroneckerDelta arguments canonically ordered #. Ankit Agrawal: Statistical moments #. Amit Jamadagni: qapply Rotation to spin states #. Björn Dahlgren: Documentation fix #. Christophe Saint-Jean: fixed and added metrics to galgebra #. Demian Wassermann: fix to ccode printer for Piecewise #. Khagesh Patel: Addition to matrix expressions #. Stephen Loo: Update minimum gmpy2 version #. hm: Fixes to printing #. Katja Sophie Hotz: use expansion in minpoly #. Varun Joshi: Addition to functions #. Chetna Gupta: Improvements to the Risch integration algorithm #. Thilina Rathnayake: Fix to the matrices #. Shravas K Rao: Implement prev_lexicographic and next_lexicographic #. Max Hutchinson: Fix to HadamardProduct #. Matthew Tadd: fix definition in units module #. Alexander Hirzel: Updates to ODE docs #. Randy Heydon: improve collinear point detection #. Ramana Venkata: improvements to special functions #. Oliver Lee: improvements to mechanics #. Seshagiri Prabhu: hardcoded 3x3 determinant #. Pradyumna: Fix to printing #. Erik Welch: Fix a warning #. Eric Nelson: Fixes to printing #. Roland Puntaier: Improve App Engine support #. Chris Conley: Use warnings instead of prints #. Tim Swast: Help with pull requests and IPython #. Dmitry Batkovich: Fix to series #. Francesco Bonazzi: Improvements to matrices and tensors #. Yuriy Demidov: Add examples from "Review of CAS mathematical capabilities" #. Rick Muller: Implementation of quantum circuit plotting #. Manish Gill: Fix infinite loop in Matrix constructor #. Markus Müller: Add Jordan form for matrices #. Amit Saha: Fixes to documentation #. Jeremy: Bug fix #. QuaBoo: Optimizations in ntheory #. Stefan van der Walt: Fixes to mechanics module #. David Joyner: Cryptography module #. Lars Buitinck: Bug fix #. Alkiviadis G. Akritas: Add a reference #. Vinit Ravishankar: fix iterables documentation #. Mike Boyle: Additions to the printing system #. Heiner Kirchhoffer: PythonRational int conversion fix #. Pablo Puente: Test cases from Wester paper #. James Fiedler: Bug fix #. Harsh Gupta: Bug fix #. Tuomas Airaksinen: is_real for besselx #. rathmann: fix some Python 2/3 issues 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 `_) * in 2013 for 7 students (`GSoC 2013 `_) * `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 sympy-0.7.4.1/doc/src/modules/0000755000175000017500000000000012253362407016250 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/modules/categories.rst0000644000175000017500000000265212253362407021134 0ustar georgeskgeorgeskCategory 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 sympy-0.7.4.1/doc/src/modules/matrices/0000755000175000017500000000000012253362407020057 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/modules/matrices/immutablematrices.rst0000644000175000017500000000255012253362407024322 0ustar georgeskgeorgeskImmutable 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 Matrix([ [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: sympy-0.7.4.1/doc/src/modules/matrices/sparse.rst0000644000175000017500000000055712253362407022115 0ustar georgeskgeorgeskSparse Matrices =============== .. module:: sympy.matrices.sparse SparseMatrix Class Reference ---------------------------- .. autoclass:: SparseMatrix :members: .. autoclass:: MutableSparseMatrix :members: ImmutableSparseMatrix Class Reference ------------------------------------- .. autoclass:: sympy.matrices.immutable.ImmutableSparseMatrix :members: sympy-0.7.4.1/doc/src/modules/matrices/expressions.rst0000644000175000017500000000263412253362407023200 0ustar georgeskgeorgeskMatrix 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) Matrix([ [X[0, 0], X[0, 1], X[0, 2]], [X[1, 0], X[1, 1], X[1, 2]], [X[2, 0], X[2, 1], X[2, 2]]]) >>> (X*Y)[1, 2] X[1, 0]*Y[0, 2] + X[1, 1]*Y[1, 2] + X[1, 2]*Y[2, 2] 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 sympy-0.7.4.1/doc/src/modules/matrices/index.rst0000644000175000017500000000032012253362407021713 0ustar georgeskgeorgesk.. _matrices-docs: ======== Matrices ======== .. automodule:: sympy.matrices Contents: .. toctree:: :maxdepth: 2 matrices.rst dense.rst sparse.rst immutablematrices.rst expressions.rst sympy-0.7.4.1/doc/src/modules/matrices/dense.rst0000644000175000017500000000044612253362407021713 0ustar georgeskgeorgeskDense Matrices ============== Matrix Class Reference ---------------------- .. autoclass:: sympy.matrices.dense.MutableDenseMatrix :members: ImmutableMatrix Class Reference ------------------------------- .. autoclass:: sympy.matrices.immutable.ImmutableMatrix :members: :noindex: sympy-0.7.4.1/doc/src/modules/matrices/matrices.rst0000644000175000017500000003135612253362407022430 0ustar georgeskgeorgeskMatrices (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 second 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 inverse: 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) ... Matrix([[2], [3], [5]]) Matrix([[23/19], [63/19], [-47/19]]) Matrix([[1692/353], [-1551/706], [-423/706]]) >>> for i in out2: ... print(i) ... Matrix([[sqrt(38)/19], [3*sqrt(38)/38], [5*sqrt(38)/38]]) Matrix([[23*sqrt(6707)/6707], [63*sqrt(6707)/6707], [-47*sqrt(6707)/6707]]) Matrix([[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 sympy-0.7.4.1/doc/src/modules/stats.rst0000644000175000017500000001220012253362407020133 0ustar georgeskgeorgeskStats =========== .. 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. ``RandomDomain``\s 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 sympy-0.7.4.1/doc/src/modules/printing.rst0000644000175000017500000002742212253362407020643 0ustar georgeskgeorgeskPrinting System =============== See the :ref:`tutorial-printing` 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 ------------------- The pretty printing subsystem is implemented in ``sympy.printing.pretty.pretty`` by the ``PrettyPrinter`` class deriving from ``Printer``. It relies on the 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.0d0/65536.0d0*x**18 - 429.0d0/32768.0d0*x**16 - 33.0d0/ @ 2048.0d0*x**14 - 21.0d0/1024.0d0*x**12 - 7.0d0/256.0d0*x**10 - @ 5.0d0/128.0d0*x**8 - 1.0d0/16.0d0*x**6 - 1.0d0/8.0d0*x**4 - 1.0d0 @ /2.0d0*x**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.0d0/65536.0d0*x**18 - 429.0d0/32768.0d0*x**16 - 33.0d0/ @ 2048.0d0*x**14 - 21.0d0/1024.0d0*x**12 - 7.0d0/256.0d0*x**10 - @ 5.0d0/128.0d0*x**8 - 1.0d0/16.0d0*x**6 - 1.0d0/8.0d0*x**4 - 1.0d0 @ /2.0d0*x**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 = 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``. .. 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:: 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: dotprint -------- .. autofunction:: sympy.printing.dot.dotprint sympy-0.7.4.1/doc/src/modules/combinatorics/0000755000175000017500000000000012253362407021104 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/modules/combinatorics/group_constructs.rst0000644000175000017500000000023612253362407025262 0ustar georgeskgeorgesk.. _combinatorics-group_constructs: Group constructors ================== .. module:: sympy.combinatorics.group_constructs .. autofunction:: DirectProduct sympy-0.7.4.1/doc/src/modules/combinatorics/util.rst0000644000175000017500000000062012253362407022611 0ustar georgeskgeorgesk.. _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 sympy-0.7.4.1/doc/src/modules/combinatorics/partitions.rst0000644000175000017500000000053412253362407024034 0ustar georgeskgeorgesk.. _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 sympy-0.7.4.1/doc/src/modules/combinatorics/tensor_can.rst0000644000175000017500000000042512253362407023772 0ustar georgeskgeorgesk.. _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 sympy-0.7.4.1/doc/src/modules/combinatorics/permutations.rst0000644000175000017500000000077512253362407024401 0ustar georgeskgeorgesk.. _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 sympy-0.7.4.1/doc/src/modules/combinatorics/named_groups.rst0000644000175000017500000000041712253362407024323 0ustar georgeskgeorgesk.. _combinatorics-named_groups: Named Groups ============ .. module:: sympy.combinatorics.named_groups .. autofunction:: SymmetricGroup .. autofunction:: CyclicGroup .. autofunction:: DihedralGroup .. autofunction:: AlternatingGroup .. autofunction:: AbelianGroup sympy-0.7.4.1/doc/src/modules/combinatorics/subsets.rst0000644000175000017500000000025712253362407023332 0ustar georgeskgeorgesk.. _combinatorics-subsets: Subsets ======= .. module:: sympy.combinatorics.subsets .. autoclass:: Subset :members: .. automethod:: sympy.combinatorics.subsets.ksubsets sympy-0.7.4.1/doc/src/modules/combinatorics/testutil.rst0000644000175000017500000000044412253362407023515 0ustar georgeskgeorgesk.. _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 sympy-0.7.4.1/doc/src/modules/combinatorics/polyhedron.rst0000644000175000017500000000021112253362407024013 0ustar georgeskgeorgesk.. _combinatorics-polyhedron: Polyhedron ========== .. module:: sympy.combinatorics.polyhedron .. autoclass:: Polyhedron :members: sympy-0.7.4.1/doc/src/modules/combinatorics/index.rst0000644000175000017500000000053512253362407022750 0ustar georgeskgeorgesk.. _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 sympy-0.7.4.1/doc/src/modules/combinatorics/perm_groups.rst0000644000175000017500000000024112253362407024175 0ustar georgeskgeorgesk.. _combinatorics-perm_groups: Permutation Groups ================== .. module:: sympy.combinatorics.perm_groups .. autoclass:: PermutationGroup :members: sympy-0.7.4.1/doc/src/modules/combinatorics/prufer.rst0000644000175000017500000000021112253362407023133 0ustar georgeskgeorgesk.. _combinatorics-prufer: Prufer Sequences ================ .. module:: sympy.combinatorics.prufer .. autoclass:: Prufer :members: sympy-0.7.4.1/doc/src/modules/combinatorics/graycode.rst0000644000175000017500000000067312253362407023441 0ustar georgeskgeorgesk.. _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 sympy-0.7.4.1/doc/src/modules/mpmath/0000755000175000017500000000000012253362407017536 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/modules/mpmath/identification.rst0000644000175000017500000000333012253362407023260 0ustar georgeskgeorgeskNumber 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 sympy-0.7.4.1/doc/src/modules/mpmath/calculus/0000755000175000017500000000000012253362407021351 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/modules/mpmath/calculus/integration.rst0000644000175000017500000000111312253362407024422 0ustar georgeskgeorgeskNumerical 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: sympy-0.7.4.1/doc/src/modules/mpmath/calculus/sums_limits.rst0000644000175000017500000000257612253362407024465 0ustar georgeskgeorgeskSums, 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 sympy-0.7.4.1/doc/src/modules/mpmath/calculus/odes.rst0000644000175000017500000000031012253362407023027 0ustar georgeskgeorgeskOrdinary differential equations ------------------------------- Solving the ODE initial value problem (``odefun``) .................................................. .. autofunction:: mpmath.odefun sympy-0.7.4.1/doc/src/modules/mpmath/calculus/approximation.rst0000644000175000017500000000076512253362407025005 0ustar georgeskgeorgeskFunction 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 sympy-0.7.4.1/doc/src/modules/mpmath/calculus/index.rst0000644000175000017500000000031212253362407023206 0ustar georgeskgeorgeskNumerical calculus ================== .. toctree:: :maxdepth: 2 polynomials.rst optimization.rst sums_limits.rst differentiation.rst integration.rst odes.rst approximation.rst sympy-0.7.4.1/doc/src/modules/mpmath/calculus/differentiation.rst0000644000175000017500000000102312253362407025251 0ustar georgeskgeorgeskDifferentiation --------------- 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 sympy-0.7.4.1/doc/src/modules/mpmath/calculus/optimization.rst0000644000175000017500000000175512253362407024641 0ustar georgeskgeorgeskRoot-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.) sympy-0.7.4.1/doc/src/modules/mpmath/calculus/polynomials.rst0000644000175000017500000000050712253362407024453 0ustar georgeskgeorgeskPolynomials ----------- 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 sympy-0.7.4.1/doc/src/modules/mpmath/contexts.rst0000644000175000017500000002670712253362407022153 0ustar georgeskgeorgeskContexts ======== 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. sympy-0.7.4.1/doc/src/modules/mpmath/technical.rst0000644000175000017500000003570312253362407022232 0ustar georgeskgeorgeskPrecision 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 `_ sympy-0.7.4.1/doc/src/modules/mpmath/conf.py0000644000175000017500000001023712253362407021040 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # # mpmath documentation build configuration file, created by # sphinx-quickstart on Sun Apr 13 00:14:30 2008. # # This file is execfile()d with the current directory set to its containing dir. # # The contents of this file are pickled, so don't put values in the namespace # that aren't pickleable (module imports are okay, they're removed automatically). # # All configuration values have a default value; values that are commented out # serve to show the default value. import sys # If your extensions are in another directory, add it here. sys.path.insert(0, '../..') # General configuration # --------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.pngmath'] # Add any paths that contain templates here, relative to this directory. templates_path = [] # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General substitutions. project = 'mpmath' copyright = '2010, Fredrik Johansson' # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. # # The short X.Y version. import mpmath version = mpmath.__version__ # The full version, including alpha/beta/rc tags. release = mpmath.__version__ # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # Options for HTML output # ----------------------- # The style sheet to use for HTML and HTML Help pages. A file of that name # must exist either in Sphinx' static/ path, or in one of the custom paths # given in html_static_path. html_style = 'default.css' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Content template for the index page. #html_index = '' # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If true, the reST sources are included in the HTML build as _sources/. #html_copy_source = True # Output file base name for HTML help builder. htmlhelp_basename = 'mpmathdoc' # Options for LaTeX output # ------------------------ # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual]). latex_documents = [master_doc, 'main.tex', 'mpmath documentation', 'Fredrik Johansson \and mpmath contributors', 'manual'] # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True default_role = 'math' pngmath_dvipng_args = ['-gamma 1.5', '-D 110'] sympy-0.7.4.1/doc/src/modules/mpmath/cplot.png0000644000175000017500000012305712253362407021375 0ustar georgeskgeorgeskPNG  IHDRh7( IDATx^A@ aV3[m$I:X)o"~SQ$p.dе0``  00``  00``  00``u;cV[B T Zp 8\Ef} 2Hv/`P,vH:1(qL ^8w@wP3Uoy*x>ܙszVok|Ȱx=mcyeջY+=gw'T֦M]tN= f 2pekj 0mX'>w_aNmZ|0 dh5Zu[50\Za>\ۚ/k?z31Q7hoOAz}kZ@W`l+V6G 5``/ߥu5mjϗGß &k;b09œf4U͝<=C?>mfOk5Fޟp'=͵aݷ e-h2am-b._ Vfy4iA!d՛V[ @wBHz=(h4ρՁOMw}opY×k^r̃I3*xvxW1&;k*_WmಲX2 aأLP%P*n5)_êꊶuה/㎶,,jFCN"Y ^ ys5t 6f 6M|=yC~V.+1v\SrvU\ \Tv, (\| ɱ<c sz[vY|)K5zosoj84`lߟ#pŌg:\V/@8Ge۱ ]Za2fe'><^+aBPWu><+d16LoNƜli@7ߡ uE/T} !ܡ@I/h[,_ 4ZsOk¯ eeJ@Ј$Gan fT91/m*{ZG- [.Fer~pwjJXlq{ _ o6IٯP j؅N< 6XLAߢ _;Ϋ`uA$-le+zQ:֨h<o_P9~)ƕ8'٠ P5!c"]!٫ۘkL_ &z.7רT!hNC2_;WaiegKabHTb/ %%fJji}ef[m_֎OmF;X SR7Uxp e|CW ~[+Ʈ;/s!dY(&fqKvg|0٢$g/a4ޡuG [s,Z|71vZ_l"fZ,ؒ^圏q+5cl& L$hYhA2#h6H23$)ihfN?n@9tS{WI!}̘γ9IVz'8JKH?9*XhC(Có]V.+^7cHTqn>|f;[X]DE:F aP`E3,ڦ_+uG%X; ~~Q݋c>ױn?bUJykcElK@@o=#jsO//ņϨӡ2i^wʗh3|1Kw47E|b*T&q_ئd5ت%_1 .k  ׯzsoE+ݜ3Uܣ;Uv/J\[䵭ncrУZ>{[Kabބ;s)=oXٹeL$`*_=x 6n\wۮ~aT@mk9뭷P.Q[nCI]ШCuG0 ѝ32! &ꗦBe{?Z[S_+!~9ڼ\&|H|MկbpN+_K3bj0Z%,J˄+dGbJϛj8a–BEmV9Wp[}xPnV4pp?XY }_ς\AgʗAB{s5|#xa q I b!̌;y$Rüb]zmOw_bO0uŠȩ\~:a7p;;SbӧR6`q"2(%^AyyMOP퀪P7ǽ|Uuf2p?A + [Y ՝C9/4siPהp)6!y\dj^A<n*Zd.$]:wM{$NP' L 6C Cb_Nkhe2$7}4Β+¹5KZ/@7V: ^"ą H' 8V4{ fCavW#Z aQE~,λ뙉XL1pwws >vi++9펔n6fj6{?=oqz f,x  ,*XoPRPj8˪^qxkNhW>JUđUn9=Wۖ[.}OGQ>$ OO^C:QNJˤa@4x ->e".,J\޳tЁsHKn(i;Y.馑ۙ 9Ͷ.3/},)Im7i6GC T [oಟܾ+a}ەh6{9^}̠K1 e9=m.,AH la+&C#BQH9H|xϸξaoSI\zJbSToU:cR36_SDo~߿m_3a񛫍˱`\%F?'k6^Lg58GlIR&g J1#&,ug LOTg#> sgpL^g&gv;g<Ş)nӶ@ \Omj}J}@m 7'0[ĺ[i<'xssoE|2JFfs g™Pη3 cs؏5+> fg!S~P.{VG# k13s~ 0 !LH_%g|[ʄ 8ѰTȆ֏cxݳgxqPnx RUg*Nul*GS*@~Z!T7 eg}_zk_~e_~%a:Y21Wl,0b2:07~~DžYaĚH[;кOїbvyƺîzą9,ҰkVxqKn\#͙g]{>mmL+}ן>?}7 xCưyxCT9s E7"a+>Y*aB*5_x& E70Lhg2~zʆ7@3Ը&2;τ#ew>fdƎ>j|oZY, ALQ:y~*X}EWt]/n3S3xp{iIYU,޷Ԉ l}6 3۸뭬\v.9s5rK@_/~rcuG\eĥq:3r3^ă5 kvM _5Y} @ZEؑSz^|wYL.ޱN)_04z9|ucU==`J`iK$`1v vD.2nj_|c7+;ƒ׽>Njo"( Elϸ9AbyNP?-#r9XUܼX0"| +ЇuNVtv>Jf0FКb%g`r\﹓ߩh>ت(۞׫rsGGx k`e2y W:cZJaJҢY &x!^ҌeYf>v|6XC 3ws r,~$*_n~/Reߍ Tnd>t#l/pu!ĪYTo޼Jlc ykljv;ۚB_\λSgV5yGr~^W4lY$`eG (a{ ^z&)*s=7$gAÃY(P2J TrNIpgʹ1CZ9-vqŇ*5+R ǐ/Zj|*63PV.cl{/lOuLPKK7wE]YKo݉_qGk yq{QI rff3.$P3%h u񱯲M[o T.XYLb5CX5}w:<`WTϴSsX/5 pY1_7Ibs/,$:6s<5I"0_W(L1?yVVOS2+7jᛯH&%QLX^+Jb2͟ X^ntWƂc|1Q<,a&8)~üC'z=ztnh_] %I `i[OpZ ">ܨp} OVd-f eP!YLzhюy2w4X8>Qm;!MK/85z^';︯5![lZ `i~vS~4*=%i8a qîaLZ82Ab\t=5$x7sbse5};0ysq`P>ꘘ44w3 nxƃFQep;@Xj+Icf4AݴI++mCC ¡ :0ccʾ5xˆ~pxxpg hjRAc[eܞ0qď@nfnS#㳧1]6S ojpSR&ё(.+,MB¢9zK63xnؙ~ơ?.Ud,czu~.ҍ]ΌU.e,U3273A<[%\!g6[ӠP.+A8g|9wG^4mЇ5us`P~G$z#5/=8|:anV5k9(A1GUhEd݁ ll?p◦^* 2ulcG"t1:7E35M^?*Q[Y,vsR9|U/>oԴk @?k1Gl؁{)&f#|s!/^= %b|HrqPjW}@ù78b4ik++]ה H8!;}3n9B^HfuǜgTS@ϛe–:Zf]^G%*&; XGasvĀp-e2'\OI'iRV:{&HzC;т"݌*hgnpLaeh%!\q o7}my'l]ۚv3+;z/n~c7pe0Y;.^q-Hݛ`M\6cwWs^MQٶԨeT cnH>A@\u%o2OpAZ؟`ApVyW:IMBY:^T~+BZ':x{s'̣nO^6^aSɢ 0uBfkv@ F;~zIww}? }_[ 冦KUpcrF!b܂S~C( qɈ:cI mvC7t &pŃf,xvl&qu;,j%SǦ8I|خ smJ\fT~@.+Wv4㜩:Jv\nCBXۚ*^Bf&e۔/ЭqnfF4޹T:VVL,خ^\k YЌ fTbΣ Z EǬ]Lsor>A'Lp1YPs;6'`#%nR<N%µx?\Gwc3ٮ蒄,|,x?~,3053Q-kiG\J w\\cw\骬\g:rqF{5-A= oGbN$n=2pSR8tJW8/\ѲRъǨ0w_c4b{:hs=ܐ۞]~VnȾJc4k5G8cw1!-6**0S իGX{6,HPPѳV>#x銶gX/`l1>dO <;X$}) KYBw?XPqD,yJWv4Hʜxo;hdu&S^m۰ō R7F+2ձ]o+qb|w.Lh{tI뒦kVU"h-Tx@v epFFhDZ4q\{6NkBdnf?UMئp0'kjh%^Q{%`U)_bh/VQ#ݍg1XjsI3ε'u((B|F2oYL9 Q.eڵ: F2VdV{<|YJk:SIފ(]vmufenCZ[wγV4xG@"*hMงg88kG- js/UrYz}uc̊^1@@0&|#"l$~ GME߻8K w`fX<#c=?Cngt3+z ?@e{&D&GqƤ a`w*akB/x7k@ vtx ]vg B.X~A lϔhTyޣ/PtI/QtܵhЧ874-ٮeDVV.>h.x1+"4fJiPp=Ye1\OchxrSbٶЏ>8CŋmkEtI/s9n.w5qx&4ՇubM9++sfMW mކp3뻡TokԆPƧQ! g@-'qvfhecb,֛ly! 3?8&^ ^ Ͷpw*W28qpL( X'7~h1)0X+@ 콿:pu{kfiv~7<ի{/|;37nKxm?zb{)" x͎r|T4_S bbqׂvT`_8>]~jTr^:,;͔گX^!'˻w~&NPW¬ .9a.TvwH75 kyJTl\+Q0.]"\C\a-ۚ[{bj7 LIG!xB3x s}/=ߕz o#&v,mHF< ]^L KPcob_{h%ow wnuԠZ4ic8V:S'/ԍ*,G A\Ԓ[ +^e_w P0DUA\6vn,^cqUt{#v9pAҷ.E|jLmL?aNIPF)xC4h_7\w븼ΥysەRϕU v(lԯLb>M?*7x~M~OU nMEoMLNf ;3L 29= B7BWftyWV)KTTQ)0d $4ZY K<̻R ӨT?Wz?7>O?,~^7:W<ճr]y繁%}ZF *:az0uʐibqT/CsS'tXYwn+٩}-woWd\<.#BY*n*w/δ-[-hY^v#2μ5⫿|U>צ/^w\;hhq]0{pN["׌BQn"@-x9Fyj7x݇理Q抩 }QI*q =ʣj'* =1ǂ~Lq܆B cڹ6>)m,UX@8PMfF,;+]0frN|35mdF j(I)>NMP\X^AXn#U"A1mל.RSYcJ [O^&.Z 'y:m5H[Xm k\#tnhTOՉ "k: Eo?Y99HG%_2v\vDbxf*_A8*[ ᫱D:ɠjkF(I5CNIgնL{b:f4 mˁB``w vk iq< A&C ~^'5&A&Aљ +b T.+`K9뗰 oc F.P;%.thp=l|=jHiZ.R&H%55iu/((A:1bxEF RQx ku:U:c` F4T[7DdMX9t-˵X۶h~Vmj?̟v)PefG!c7|;o5Hxq&DV?&\{h Q m e*8`h^dS@a}bm %j݉U6XLCSa(CeY-xtۗu:#WkǠLl%X kZJ}8Ӡjk0xoXXku[bU"KI %ßnj;ҬQ)Γ{s~Ճ*ⳅ^eIMjo*ĝ)7kc !8`Lø5HS֝@i S(D`]NemC]GM!2YI8*XEjU 2kf+o8L`=ϩBzLZKT9s\J9+~ H=dj3u`]}^7nrQU~.%醞; t Z>Vފs6 ˨AW,**ad5vo' dҞP7oR5u&%j=T'蠆1$Fu a𡚟F_nWM•cG (I>~)^^Azzm L)5Iw?!m.QupсB;WH t\rkU0;e+Vĕf,Y'h,I2uDP&t ymր<>c5(OY9ScIl aU.*ظ[ \Hi)ƃR6oq(̰ =-֜ 5eY d,:EBVV5EYpR[ k&Dv0QFw3 !ŽVi0F ݐ.vqlsL(1x\KblcKp;>|uZ0^ Y0O]_;;EL '5X{.A W/r[\Su3rPJKr*W6NPE9B-\ P܆rİ] g` Ȳ]'fa]{UO::f:*e];{n9+ |IbUہ1$d)lP6 Nnh݉Y*q?.CsO0+Rdr %FѢ˳6 69YmW@7pw*tw4:l1F< gtS 2gJЅS"-V#k3 .@[h^ngN߸qWqy*:{%,$0JP.P/>a ;T0c  IMNF :T&4nh\ť+ ׿*u^*e>[}`HG  kj~÷tk%$+:]tc9pd}#:>]GPW #ƌ#f0i"b7g21^HHc| )%-+]CmxK'!m]nZpwϞN=}|KςBo[R*xxZaPY0cN!,A{N**`Eƚ1Ec {qP-&Xgm!߂&Es[zcDmRyڀgqie-n_oƷqr~׾I姧O@ϢT-ҁhI6jUfC~ |`!D6ytqF؇퐔{Vp/.*sLX*Y.h &:0ULf06)OJvGr1_~_ܚH  ҝnawmi [pt֛oџɟ/_1IȰ4O*hjT/MT*SxŽ^l\KR 5U!Bx7fdCVeMHvXߞşǶ* zYޜƾ_yc72I@wc]Ax$`+ +d-Qj1`'HʩdZ3cݾ/Co<~~:@x3a"[G79ba{b'$Y 16teQaB9vψ|r{{4dJ*-E9!ngo$I&1["WSr<<ݨ.gD^. ݪ n^2_ V=痧{NooǑ5Mz%PS#&.Bc>Ѓ-U~ZMafc{vPKSNt#!S1@H! TARu9a_rpo"vsa;^YgB,}9ǹ4tV&x*ڵv<=s*y2>uwHī1}> x矿Cws*)ly$ ؆m:Sꍙn È 4fGUUT0dC\(rSSǐDاu+ K|61d7f/pBQ$KrMvPFA%fB܎ωS"T1f!H}u& Q 8+&F<-7wE W^1O]j>[_ 6,N_~Mn7ѣGŸ~>z_ lTϿ{ɷ?0EDQA)m3=%5y"k'[I“a96c4#%"5&5.yKpI/}JCNg5/IJ߰}iZa] 67z.:us~B|(E}w ծ~~E_C6c|Ec9':{HjitU8^Ct;{jvAɟ]~rIOvi%b]ş**ߡ]Q, b“_:&lK׏hߤ\o<5lu7>[9[r^r(=mжָ;z8:qj/{?"z./ޔ!v2/ÐQJ'IߦԀ"0W$XK~]CD_O`~ />gb;tMׯ_om>G8 p3uJpA쒮cF$_lۉM2{tEo#'z"Q'ݳ7ىQxs{Zw+Q~O/^s꯯곫ۏvKg?˛D. @ zBYH ^#+R\N1S'%QQ"&\s?|-τ).}@̺G*lPJ!OG77҆y[x I!LLr.!yܳFg9KOQ ktkp^6=3@؂X0|%߁-̝+[[/cӏW~W;V:8ZacsO僇D2}nA#XMp ktC燺LD_!;|JU>+Wmt#<햮E|H0|p\ƸWQPBYױWHo(hR*o @ͩ% ";@$DJܟH>C WmS(BV|qtb\> I|)\#Z2Vv=pWn3Puwy5$혀k(HҘr>IŘl LWz "*DRV8&d|PhGL>]oD'N?>)@Q i/Iiq*kT=o.y6~z-51%b:Bzۇr^Eh)wo*;!tϾǐ^r>=;B~o<&̓wTOzu{wD%ŋ9z&͈syMfN⊋>6ؒ% cx5y$賤x9_u|f^s]j6 a|[ɊJg{[DqN;@G8SS.]R !Ef!~ j(wdm% 2 :~xz^ y@N SGhTM_BoW1FtR sے94!DopңcL;(ag/y}&mގL(6w$)3rKH( sZ-mB&w!K&f9B|nF&oPB=D(7J:O~D?>As"I1K(S (+d´o-;*A9M5.—ERQT u=3xTA4 .0{.*m쨴1`k3v$1Z'c>Dq!s c끻?~Lݎӫ7i$t4QPSYVI50m!QC*.,/p={ :vU̓mpQӛ(5⠀/!eb:+VR^(A lB zB'tOzMpImjb ~cn5$7?#91HlǜIvg@BsEo9 :wnι?,.Q9 eLOT *`DU D'l89@r R,ÁzܛSǞ@jx\>Ƹ5a\:|D#W:>Qe0u*vyQ;L@,?㪫A[C Vmor 2`{O'5@8X7ݞ7DLBZK8u؆ݼ~<1;xɩJA M<0 T{]QBkdH| $Ico$pY{+o6<)^ 5lA, ⵅ#bILw ܲQҷyCUDS)ñr~D*_9Ym'V f:;UF"ދ=WuZF'+%ʠg OCq@'m9.}fJ—k+@Qn^ mE ԰=gi1uNK(* +ǓgX_s"ZVJnh#!_: X#KR^5Ty)^yg0k: ͅo֫2lZ^^B^߂[p!7`yX4(X݋ծY#/Ϊ8>B :V' 1 б_bOU` 1K][7W`IZ_d{RxjIVJCx h;B|+R^Ejaz&^UQA:̧ ɋFN%aQZh@!ϪƖ~) ^_H{ė.bM*=!,N j^z n+Y u*y[[|=pv}gn6p>6> b 0@%_N-_w}aV:,w&IzIkHxAP;)aW~<TH#ŁݬbR_(d a{x|lJAߡzU6 @> uVWU9:<VP`\锂-,I *Y!^p=[+,0u;-Qw|q9ᶚn^uw [VYPh:ՠ~r-$"`IThyuVMNƓcVE3CBX70 pq=Ly(j Fiz/9SfĈ[i[-\mR2+YymКڀ{:ޭ[l# Vp撐* q_OTj(Qj\AUل,kc*, /H@],IwŘ H կ./~u܆}mEA tr!im gyu 48+ nG@2CL c[ Ǐ$jxcd"ͭʪ_+U~-"-agl)/xʆqm+<+eolmH FLU,H%cc<eB]'܍T b wqaa IWB[> ۄ&WTg_jA,o.o]W_ㆶg}hJK4 }uּya.1Zc-=rTW5 ê1ar[[t t[ kr'*/0y[p7)L V&"[us$^M[p4_ 3Tm u sW] u~e9/V{~p++s >opAK[֒tcЀr$>^x6qWRBܸ3/nѐ:̻uw1J2 [VT+tbiO'k kدWeJtfW '=JjtrFrbQ'.6\ RJa٪WFq1`7 ]g5U-yTM&{SVh(PdEo=6G+@Z YX9ܪ*fiHV NĺRV8K@$c#f\OTNJuwùWVE&^5 mh[ R@% PLn ࣋d]v@€*F*J77d C0Q`^1ebpMAmQegsQ2=}eUj[p71_f@8pVBԈ5d:F; j "9AK{f! 8{&JԚޯ(c]s 6w(5 8;I57p-Ƅ뀅 vn;Y+wE0]rX0:Af>!CUH* suOOl=I66fXpP7edҊЀ"ad;k4W;ԍ'lHZA+ ~xǘۓ-h_ #~vL8 XS>=z}lʭ xU1 7mpnDZ{|1 n+^ewMrj^.ԩU`+u.W2q߭_uچzI_BҨv6aR s:=qoPWFBiH"F`ugP%lR ]X EȖ ֠9\l!l}'W) cpqCg+ rn `Ub@lbpȨ'(b.7 k`Ɣ:ט31NrHN~iujKQzRX[̺n aa0<@g>7U1+jG3ۊv-tłں+dCXH X5hm׵V vLfs;is ?MƂ5|[ 9N=P(?hSW}Y۶zU1_}dMmV׳Sv|U" |t#Ka|պQ|6fj GnC7nhXf\6U#g^n,ʥaI\Zb?\ q|EbLi{0ݟr(=ƅ{y@~#JIngI+% π8pY;RT~ѽ޸֐|uwE WXm9n!Y׳UWq_2=Y+omEڠ406'68+ֹv{0^ursci,9s.YAH{'oYDE$!/KB2;)<AQ !L{!h0*W>` .GrФV뫓LE^X0!L0w8[#WFxE]e&}ޛ D(ROA †UV4~"6n = i԰ٍaK:1β:YzRh;!W[% @AY w|K*R1\Y NuJu\g&Ŋx 䖵ZKxt帞~P1E±Xӟzf!M:>钮\-wUDL,J<ܾ=EěE]ʌxp\taT'eMg|T_Lq& KK  ZPҚ be6J"b\@L4(r qB\q ]uЀ ^u Px+z2@+ lMBV,vtU,7vz I 8q!ZDk# Vͳ׆ #R%Y4yۃlkbpTuj](ChÒn ,c_gY We>s{}Jj\pN7vC\ ]6<7QdD|]AnKin":&6*[jO'] ry@b|U1;k,_JhM**F+?KK ܫRMShe&P7ITq }r9s o.\R x5G}rX+O}ngĂAlu= ,@lXvSOvZ&D݅mϳY-WWg7 JWͨ^G2eg}$tdŮh. |kzN4ЙPƊWPBݏՏo?~/+D;*_ nÍx@lFVat*LtJR~sbAldY纶W 4 gzf(`'˵Uk45WFl=j]fd4°qH7rU7+ oTdb= m6<o?dI)*S[Wqb7h`+ZֵA'L3YgO]YQϨKh #IkgIبa~VttY$XZpnVRm?F |=p^t _}vK.MO?%|߽JgA ZTDFլT0ZfWt0y#8j7FͱTmPS kwܠ^ Ob m@MtxK $[+br6Y/Zv#\_X7q9cq; *U5LPr MO|z TBk_h@@]xKf3WWZR.IЫ-5jld+{{YJ:l -aiIFwv.yQjlr} i WQ*þg|0qr{[S *I`mO,fr7>H1`k@V/B*a3߀b݅"A-%=[k- 8vZ0e6`FڪM%^lhߦMO̮ATsV LP2v{ݑq=4{fTı5֘%{xt_iwt?˹48t9wsk8ԍ+ K?~8 _1VN𠏄-&ڴ;kWcE&8y3Lt|E+@ Rĺfqyxm~wPt}yN|utFwtQ+$+l1DRł3F‡nxE iA4 _&- X/ALupLb7>L5=:u=(<7Z8 Ąp \ʹqM/ut7w:ˏecw4 oV<Ǚ Y 6 t/zzPpMO'Nj r@+bߌJkReUIh6/"-ƸF_k$Cgl+XZJ N7ډeڠYn$YʝX,txN@)Ot[ p; fƺ@@8.i/ ^ v*b~`ă v끥K0ŇMkO6jjtul2Lkyfh'qjT)^jБRjo./{VfBvE YgiV)Ge53E+L&xbֳpzcơ KAn_ϑl~g6Lo4j'hx}NY\uV7q:BZ$uTFT\̀ZE$@9o&'Q: pg'QliUw\uU%<jXT¸9/fsM_|IpSL}kb[W5g}sGs/vGIVu`fF~gb**gO;*i|B/d@1–"k,1M^bu۟HH Л\XL+N[ pagQ&Py JT|W%5a{0GJ /k]k&i(ZS^B$DƇ$5,߀8)Vl!5lp`k98s~RN qsjN umZ Y^Ѭ #, iZƳJx x_K1'C p9 ZMGoNrq QmقA/X!4f%|qVk {6t?δqѾ&Sw; *Ov{?/|^cЄ!,18:jUcTq/a;`\a6cYjY`zȈky[PmJa iA46Z_L |eC;ކr$5H γ$f=KJ{>\V/EIZWkmyLL&;Yk\ ; @5._[UHi` v&*gA ,Y:Tc.CMa`׋mqt<k>r՝ ?8^p#RLgfHh7z\!^/v o@)5ixiB7iJw,BD,p ƃ3eҩg!˒$ b}.Y ʾK:߷eב .Ŋ9fK']%cl&4:Xy!6hk&WdiWҨW ]o 6%bw5TCiEI|Rì]PKQ C*y,*zn 5K,R}V3^^fHgU xF6U61|yutVg.Sݬ`%U59>#^-"|WˑWվ>6AaT-Amc-0T2PI_xʸyIn}Zk~.xXΰ$)i!biLȻ˒(?檗 Z3oi Jœn*D/[nej;A vTT1a:~7V`yt>x= j1]FGիdm igz> x}p)kXMPGwACH 5Anj(#|qubfTF3 oBLX3 sR.xU,7Kn|ՋuVtǮMO+N VTö9GcSv>ە. Řob#5U7 }ىpIC26cc2cˍ.q‘  k RpOpu˧Rn蚇i݃gY6<= XkPh&| pKёm| /hbR+_{Zi [LBVNM/teHWjڐ <׋ME8/bWt 2׃}^M*X0͚MO{T+dH#|?NE+=sn0$ײT̮CM7l$, B^pxF ^5̬gYFm9g>gt݄&9ppƝ\9PeL7 rQR+ Λs,VWr^\T8tؿM]~ q­?6Δ`oLY@U=4뤷fTlD餔mi *xE7[f2R‚c7TgY۴[Q%uG@1ߪ7`٩-]BS: (6ܨnX;*hN}Av3˞M8 EUn>p>mh-rR)2 Vx4oL_HiqNW&* TUcl`k*dN܋ATɀoRd3y<_=sJY)8F4 heo=fz{ 01+>mxv,gND7p_A\6{ kn3/mY\bՒ+|7'Jjgl aZAh33yjTRߍ "Vѭ큶iΑ@ae|&\& =TÏWT`>|W=V)=Қ j8_v!R1PGp3yN*`Zkc)wvs*A|PZMR3 kڟ?_{˗m?ns^k 7yMp,<+]0' ū3mlԯ:1`jxҼ#3eGʢ7m*i,x#5ti[M=u1K]I a<[^_>5#:Si#yHTUrJRQ\J>Wɭ-g Ҟ]O<췟-__yC&io0G\{U1PfM⍻^ A\vU\7(5b}4B8ª F gSK%L^{IVI^b}xMx ˺]jHGZVJ5}7ynN7O׾nk?w&Ο~,QqY('%]NYJ ;:c5$4T`3^sL 0@ !;%e ݌$w)``/AˆMC\ U0\\[τ̨w |@T^/#Y4~I҅\|5p=${W/坷 i?-74g"M^01}K}d Ӝ*IBg܄j #FRձFMn1]PTܻJܨ]L UI6ːoKGl9 ]Za~џ> m;hy@f߁9(p+sO͂d < a5B ٹh?_&ncZFR<|+d֓]^km!\N7q|sY!܀GQEpQ[O:m l@t —zƅpYk/m=9!K5 |Y[g!|Ī]~&baM}:[QN;PtfQǃx]z6d2×YĽ}작+N32MAtj|3=ptik09hncq~,X>tbŠniSOB=;ܻ _TҰނIG>7Ozi7ޛuf)]uYS6puکUE,LsEZZ}TP^tN [%ToUOyrKJ:aM xBz)V\Lˍb!\F) 39KJva ߂f=RF2F}c&lv{eEJF %u.-7*VU𫿓-o iToY:ٰ=ЕjxC\ˡ ŀQe4uK#̄C x3+ld07Wj*")TH6}J@kJxu6<?|q ePwXK'C8n/9vGv,W ¥BPA<*sogX98:J=/G⠅ mA[ol%7*ĕjͮJ(UeAP䬧N268Y}8|H%oѩ|\5qϬY90%dei7頛n Pp-?uhAI/㹺j_r`Ӧ zT|^K|o3]ҫ7bJ}lqˇa%^Y5p7&e)_/*Mpo=_C3lf]l1 g *(5hCJTU,j=H~zoզ6˝UI SF.īzD {Nγ/` %:Yϛ;Z VSYdJ+(z,KH(^f>?xnf'\ۥf?]O,};sS{>\cӦǨKwo~Ϡh}}UIOFUcArU IQUB\Y6C/~rV}.TeqѦ6{=Nހq n+S E,1*ծ ^&f,׬n/UC: W3wzYd\4'Ι* kRTi2㵂!J Zg ?'u,g 7;:*V\ 5`G,\}$P7w}#tE^V\j%PVd,V B<Jݥ,ʷMd$Z V$l2ۙ|򽐟l_j0R,+%U*d X`ʀF s {<,:`T1EꕠݏmVwvz̴[9co\m|dcO;/@ WSle+{l8.>#KUUYќddE+Vevd_knVa{O xۂo~`s:P_^|cw×xwG_7ςlFV:U勧}nA[ac-(eI DGТsa;=vb774n6m*੆7ո-'S| :G_Lg6rAs"QrpsU-출sZ325:!{\F/Tʄ.f;*X!Zkf%k9GE' E[S6mxB~}sJo1TS KLYWZjû@C! pE;9Wc| >h&h`PCk٠V%ʆ\5h!oRńnմi&=Z7?vƅ|5,(â×İN+ ӭJpRn k@xk p,gzlskEP`  5!+*!6<-Rl9bUL}"X VL%'.!DŽfs:*6^,?r5d5hoVw,JŃ@9v9kT ī|=>%7lo{wVpT*0! f8ݠ` }0e kك RcZՇv0Up$>-{PaB+LvPHA| dkt?4ip)ɹ|;_ 5`h244$nW\4wjN~Z\ީXKzmR(.jnب^C:] 2+/ 6Z&{$kRϥL1.Vfb͚g6ZqMY}D:I=.*|^0SoOܛ9ce`U~5SșXl,^48:a*Պ7ïe0PZkE_oJ2ytR\nb3`%$5RQXV\3sjFl,{#1b,^i ,Yo[w2ylRJ_9w3ܟp 5k!b梋]4#^g%EM*?>YL$<3Δqkk^vs7K&,H眧7VcX0'͠$2:2*:/9T,qG"=%L4~nȥ W$b3}ٿ PtA$, @gsg>v;/KRk3 ۺgG{ÁnGL(V n P"១gd+Yc=ު#묻vsٴ(d$I{0@}i^n2 y(}'? ǚ`Ft[S!Ǻwimܭ4tPGQXٔ0Z.c%W ;[_߆ߛՊN̊eP(AۀͲLZ|զ#Є@;d\|Eڄg&TB܈qkSgG 2`q2`OKBY@t̝2`Ȁ`4 @`  {КkIENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plot.png0000644000175000017500000005020612253362407021225 0ustar georgeskgeorgeskPNG  IHDRh7( PMIDATx^A@ aV3[m$I:Fu(mN .$t[;‘cK3ѓYɿ|G8@@@``@@@`}``@@MtwaRJ1pq˲4Mq\/1sZk^.亮Gp?b۶۶}~g6 @} ȷh/^χꦻDLƻmf1>b,ϲlTUR(B4MRk-RJBycZӶ6yw3P3xH=9 L1qe)8#U8}^ɽG8G~\+KN'u幉%.0 {x׉s=zRk1k㺟wQU? $"EH轈 EiR{*~"`EX~N$@P@EԐ~.8+ˌ)3ޛ[+KĘ9}rwxO?e.YHa5y4ә6}D:uWRg;Ld3 N-R͚1K2ϚG̣G3h*RbS0'%a[ŋqXvڕ۷o5k:M81@F1sOC_|񢧺 3rG2kZr~i>cn H߷m8xۀzK(!!!);/4nSOqժUN:C،Ȧlݺ5F1'σ L7>>uBy? s<"w0ϙ#΢gR68 y(S ە]#cc3_wlYkFsnjI-.pvW}-"=,sb"ƹER ,h;v… R7jcM\B,/f.XP^Gf\5!RZƃdk>|9qqCq?g>vuk(2;뽲 owkHx~ \И12TʻRFX 5`N1TF sΜz{*/3y,ŖITk3 3`$aYn>|8ѱc0f+[*UdƮji/kkD'N D5kZO?%gv(1d+F̙dD}4)m` uTKoLAfDÆg/q}"Q p:sٲr)JZ!jwEeŋi`A{l`:z ~[.DɒF#=e!:l62PUS?&&z94|a{dPWCj׾W"_&r^}վce vo ܣ/ us 9kWDdssHIq $x"RNM>HFGgHmiRۓnɯV,~:ٕ+Wn/y ǏfD=&$Pƈ)")cE /jqF@>Md9ѼdIkuVVv3V  ; p&>y%p8v+2z4EZq3WEʗ}T}Sw }b>ga&'kҏ^N-w۷31 Һɛre20AcLj""ࡇ*T `Ct xn]!F{9DH;N}2oRL 4a45W^)w=^tӒ%=) W2ϯQfls% ~ W$ kW=gg>y,h'"MLG"? Z Ӧ],3=`j!PӤx?9 2"$&J `A_~ #ʗOۀ+W /'%I~4'[7g+IIKnF'/$sxxdqW IX4/29R;As'ҭۏ M 's=b=K4o @7St*sgм61_f"W24kт]zowhcNNf5`G/ǎJ eˈAGs'Ѻudd4wa~%Rn_V`My 2ؾ6^n_>\֫&MBG5a|ihnGIIi5 s)!!d駎/и}6 ȪEH.zYYQ1a쩴Sj-Mˑ'?/F&0q"QBۣGo94 0rIRN ˅=%EZ H7B֍{ $iN_}EU5mJ֬aC釢48wْ%)ԲXl# -ɷ3"҄ l٢l5KD6 ,h AAi΂kRfInd4I "3wW&=) ٌL ˔e:v$Ѡ%$JiKi2<(4pj,= "Wv?L?D&"Νdy3VL H&O{ 2u2G ܔCՃX\9ݣkT^wTV @@QHqRwʗ"U.41@߾Z0 ` IXK>}Fҍx1х |e4_T)ܡK`dv$a gAF!ٽ;4w*gN>e3ƹ OT<Lb`A{d`md|8.A9 2r$NTG8=!xCDھ ,hSuT LL:<I+DL3h r傂&@91vdʷt#-qo%8U]HJX3M[>M1](]m[9"yㆭQ 3,ϺuxEdLfE/0ex1fdg ڂI||z&w *wn~(}HfG޼ sg"EfC$a$c͛GtZljJTHR"GٽbrY1M$$/ߣIX7?(C{H7Bꤗ ODL5֚كqL,XL|}(+OkN$ceǚI|Q;ʕ'NpBBׯ_ws~鬷,ӶOٲ:tޱӧgח{6\̜#o_*$N_f`A[-[PjըTR={vӧE;0f6G!G@$&Iol,HsЉ8Ɔm,蔙/V0;TZ[-\xPwܙ=jnzHrNt#, *{[hQ6m(ЉcJ`.4iBTޭ5k?|Æ cA˼hӊɓ's&MvJ,ɯ:O8W^QQQ^&[n… u}əgM<|۹f,~߉AA  LDY}dzn&cm@^|Es|lgl^~=wЁo2~x~뭷yɺ5H?/6/"mQyflYkv ɓ2˥yQvH :wdٲeTbE$ %|nC# w=f"Mʖ̜I WIa\$sswk9e}טž۬xxҥ=z4ScJ  2ER6Oіw@kȥ4 k^V87ebilޣ# +=+W)10/ !&_d.P@ԩ,h{$ S*QÆr^%)I;[ n=#*_ޫ(@ԩ)sˆz..Xvf 3'N$HAݑxt&]vIUȖ.8O#>L/X`1d´ӯЭy2İ;;rt)Y2@ uJC`әCGUڻxuʁnd˦Kk`AKV_>-m֮]|\Vh^ه KsbE$`Agܙ Юsi:h c߇$%(D 9Yt|kX@=d 70֭['qͻ p{Cřزd. YDIV$#. ; lXn&M},%%H>ui0Me`[vp9(KhтX123gquˇyDu@sѓ5n}ݺR3ƹ DK~'kX<Çu`5{xLU)"*>R~~("m݊s˂$%J $aYu+Y\$Tj2 6O,`exe1\:w]`Yٓ*v݈U+0](UJ-HII^@ѣ [Z.g`O L.%-_&TmٹbCk#̖Md]%ņ"6eH#GUڅ#b.^<``-RW`M@]n׮h_{Q-ZH*VRpњ5dнi}mEvrR+V (H\͛"+}i둄e,\zӍ\/_$ jKle0=6ꏈW99{vm k/GѢiGH̗/`{6tZ `A;܂ \SY{c"3HHjG}lhSs 68۶=J wє/ʧ?&:|:5VE$nIX5]$ay^G# ODD1Ѽ94w+RHU-[@IX&ԬItrY;OSþ\4 1Qb y]~XXॗ$q|$aΆvM;H9ڒwB~R1a"mj7-CGzBڵ( G@nEʖM-بvsz|$aRU;3zЖO|̱+rʑClHJR`2)*J$w$;XsD1y !nUi;@$ 刏$Y$, eĆFyZnE \G'caCҥv ڧtmBtN/t\sZ~N- %6E foXAʡ"q. $aC''K _S:u  ˇsܒ ]Dn0^NB$IX&{&c׵$a0ժKJBOǎD9s@sfy7mJT#KH.'/# ˅`q~ysZRh֭c,#" u$V6}:֮&Of5`$a&:8Xl$ay~ΕKgE$"Qņ?IXwQb2|Y;@)ZE 2e!qqr}{F[qcR2^Ć=S6Jl"Rt*wX~aD}6oDFs`飏X(DGH| 4лCbX9r0pc?9!6tnwCE$_~$aIIbF*S˱ X$,f0*HeXL^%$a={*fOB4, پ%ΜfݻEɬZ%oo٢ঁ8g54TuDrzDв4̓K  ['װ xf2eܥM$a^=:^aʆFWߥ=2U6IXȆ>pߐʔit n'vHw%"9~H„4[b@ H'ػGYlB*aXtL@etxiF_/ k*T ][HII*D>KԒ$,V->^aUlh+aaorc%:$,>ڂ޷IX`g:Hڿhn)@Ƌ- 1VĆ6*ѽ< lh{eӑրMXϞv݇}1VTyH4\jUA %)B@Ujz9󍾴b61+}˗ݸA :6D! +lLtgIXzl!"x:.d/Ɗ;5 ,|` Zvyo߾d+n7y˖lr`>ns ԨAtEu{S7~TI[Ёl%Ӎ@hw@Kj$aY l!:qIX`6%[gIXG*D/bC+0VLj'K$aΆF$,/~[K1VLko^}@zוd˖H6ee+WΣl{H*h~s3_fnݴH 0ݐ.]D+V/K˗/# +-GIo$aE69~FO7i2V+RdtG 'ܹ35k֌̙Cǎ#G[6mj 6 +-Y:# ˳,@֢EHN{AܹņXq] gsZ۷O},IJb.YR>-['`?kGH;^ϠO-K<7nS'-R րSm^!˷˽{6qƜ2>\l٘Ϲ`N%C6:Ezy2~,a3ߊHW57Q6*5ѓO>i)޽"""hΝ$}gTD ڳg 4b%ag$ayE63nhflo۷1VLjׇCBhCct+˗IbŊtR,֭:M}~7Y`Ny/M"Eprr-DEӮ p-SHKϠgO-O|5cW"ҟK26lMm""D%K8-8XK9r$꫰nA]8ZQz܉'$JdP_bnCח#аe~FB*̞9Fbn~F֍D˖IbNKe }і-HrkCGw/9Xа,N 8zDy?f D 9ӧ>fe܍ 4%lxٰ!d,#g3M7r$NLb$a҆^lDG'=Kn$rv9s:1ɯ6cD۶! ˍ -?te;z&E˗֯wh%'5FN~]Kezqj.7 5`q4[nSU+6K`NJb3Cia4i-_995jhjyAl֭4.udD?(sp%j${cлv8@ nhȪ9wNIldt2~Fmhh.c/"XжEG>w0'&߀ 52}w64ZUT.,h[Р`A 1Ct@e#!3 c!te?f)C$6Rv.=ʔIX^xBs톆 6tǎm)œ~3h@nbC%H :,h 8ƆVǛW"nt`?wC (HDR%G'VӻY);/*R2o. i5::$D>!-Z )He"wETT,h^ʇ"۞`z sX~;C+@έD>4ٻGHǏ-H²sZ>bC:e'2(.N,?2y':"#: i5myPG9~-ʔL>bE岺uXyzDe;,hk594- g'{N |$,hZԆ#3 #fE IXN -uyfOHeJT&R2QnVhް!QҞElYߟ- gbC8fQ@ 4HʱvYx~<^)9١64,h$a 吺ܹڶE*SԮ He5ժUVKTsmh<dTZZXKl=T&No':|Lԣ)#(FVf;t<>,ԟصK.`:y5`$aƆ6[Cr"jL鴟'j Mh҄;kCÂF ݦ mպcb/&CL鴟e{[J=h.PlDݻ5D/  =gx1 `4}f5kDSaA;ڂjQ8kL%W][)t{f͈q IXnFX!~aaHeJ$,Вy3щ܅ņ^Z$X~6W_I?3ضM.' :ZDR7`A`l#RKe/`?W[hAT̜I4mhh'Z zHmD* 11g$aYp7ƍDNAs¸#0l -uϝ(E~He2e~Ӈ\@R47ݗkCB㏳SGUsr2:t",V1\\-ZpZ@*UHժX/H2eEkTAڹSfAAHer9%t>r"$,Ⱎ9ME0 %ڽVӷ/:e}ERHerLJCK@ŻED@s䆗]vMe ꮻt9 rZFHe0'%2-L/_"*2aA; ;d.]Jt2-[r;Eϲq%%}F8@Iu i5ba)oѓ)88͍HekN>$aYՂ^%aA;DE3!X1]F õHk147ΝH1߸,hg]C?@t[lNXu H ce&@FF@j!xqBBVRnH2Abd$,ˣ/Nv[I3f@sS:v$ʛW<@jj"VƧLl@#!:ge#j֨܈a.X=! k;HG;̛ؗ'/p!D)SؾY22! kIXX}덉fHrч@@͚}Pn5GkIҥ :ˤPSL L6#GߩxH²rU]*C))SY}_K*,/f֬KlرCDRKd )hX>KЮp`A4͛}tno5P6Qժr0ÂvHٹ3) 5&'OxLVΡHB;di`fH6 … ܪU+QiӆS+،`]եKX7YTl3؛|ԶmlD9={ҎfÇE^h Ç X9b6#OR\{9":As=p\-[l9:$a ܹsTpaR)RΪabcc~Tn]7o^ &aybMNK^`Y<LfHV7єr;$ܺukJYuZti:6_~,X@G}\vƎK9EGGFm۶ xOjH}C˗bcNպjI} {,I޿$tP?eb^ӥPBYxњ_?U!͘P/?[K]tm]}IA]w2Sf\BСCyʇK6)^]~kQ|s|=)"M^[3R/Y=ժ?c تoߞf2Oo˔@]Ӵn:VZ&ay:+Z)LL,IjM @skݭ$ӡy  &cH)VcH)v3?cظq#W^k֬ɕ*U'" ݠHβ%Q p4sZ!CA a>sO?-VC88Q@T ^yE~=lx]i&v>`zfݻE?XIXxc -mjUT&~V1|T[d ר!ôiD 1v8>+Ͼ}/]+"}`kְ`Ti"̀x1A|;wh*) 4HzLD/6cѾXӦ0xg-[F}͋T&b)B^[Btb"" !h;ҬX>x# ˜C3ϒ%O>T&tOl8^[nBsS Vs,X11 uV8}pf")BfM-RxàJiА0k9!kN$^V!Q}YAΜzJ2)=l@~o߮H^hͭ_wPN[֮n'`A;$a>܌%.QH"2I~iRH뫯 :.wJƂ 7ֶw0%9֣^U H;2xL?E f>qsbȖfn7nWWn9 x܌^IN4rϟoLҥUI2D M4Qݒ%34w~}ڵJJ"G  kּ~MDzeNX`Di X^ ĉG iDְG* Y^|͝Q$cE}P!&1_fΝv+_db7z4~]D>Hg^yEDڴ 3`gqy<,}EolDO=E&m FtNHLp*3lIWHmԬQz}$VEOtүdz>o|VŊH6BGkWi 4$~`GeKd ,hQx[l[ϒ_k6do`:m5-w^ v(@T|$;IQ)RyclL7&HgA:|_@ƒV|Lr@ZK&S>ls6 3`QL3F&,kײmb1~- > u;A9sd0M^;n9cM摑Dz>#m$GSf !{bswo2=zTUyhܺu#*_^4>HW&HF O$nHC?dJ|L#D$)5`fW9'2-G0?Dr uZq $īu(>λ}ɑbY8 Aղ/9cJq$^{UIv/-k2 %vuyʩt X6.c8`ؽ;˚:C8`ػ9(H4`{~,\17jv_Fx9?!Hv{\֔YpbNeY%>" uk'*gNteUyΚ" mkIa$>ի-@'˚uxŷߪ;L۷͙C=cԼ PESdODlؐ];r&}fG\~y0:8W&Z}Wp,sz5lKlY ݻKFc~[od>YRR_7=bD0$,s)Iݧ11<)I~PGg si=(}V߼y1^yEDKv4e &Nn;1eYcL!a߯G!EϞ^ķ.SF|<9#G$^=CO0l\I +UJRfeʔ˴i2z;QQQtSPt).MݶB#( b+DsvH{V9޺ʱwG4s& ;9 34ܵI;(ҥrHM2E׮Q9ylȲokA,Ck_҅Q#i0` sJTlzwII>vT~/s˱L4ɮ_91"լEʕK:?`"E,U5ݺe 28 B ^^78᭷}Y4iKݶ|NiQԩ2D{ξ/^l'`4<3fPΟ_ݒ"Y*ѳR!$C%<}+ʕS?,W;P˪Y~;i~ HtFd̵kڳeӛ9‚"C)GKlUW`=T~>}ȭ6}:3`н5:U62HկK/^@gDw( R*%.|ѦMdD-[UV-ذhx :j\2҅h TDI'c4-QkaN"Ҕ):5JR~ў=t5<\02YvԭJk?[3g| &4Il3ܹb4onm~}^DjH{V%.V-U~;vr/]bOt옡AXx1DcZOϞR믳o1BDRVePWn0a >}bXSu=0\~2<\Y7_O>aʕhj-۴yy^[ye+VKϻhnq\74$#GJs:ej_ɠbE X9%nX3>/_Ú5M[y{0T!yT~Hŋ3~1bTMzm&;['ꁷw/WV9"XH,/,K#WҎ~;^xAjؐu\KRP;;e˪~sVRK/0(oڤ8$'2iGeZ6e*j@~C֭Zx9.;ԣ"1~ѣD:]tvBt4k'u$ae)H@-Et%]-B~)];΄/]RogO(!2Ǘ@mեtÂXիˠkFxs紫+$oFRlf̗/ۣݾ *U|+66&M<@*U/ 0=YƬY\-l]B[ [vel)Eׯ/"y?`VMD]"عnhөo{ {?g2xÂ65 T{6nf9u*3:wKd^-˓GD s6| 2ϙr@N׆ aA8PcP)5 f"IO|}42IFr$qIʕk’B%Yg;r^}=t ̛7VM+zEywٌ2e(T?d`VnIÆEq Jau:_DRϗezE8Qk?22kuGDHFΟG̬gf._^~^X71haɐPjQMB ѭ\$ұbf(%,! oЩS Bo+_wy!RWo!x}rhDt! !h OrvƳ`_wvX,ak&C%[ 0 d0po,U!e؃00KwCٳDhm͘iiyulyuUiZXqg$|\^Oݗݑj j4aqŴe]c=-JLlulIlnO{'Ig灏?h'aa|wg~sVWEn:&x9a6fSj>2Q'T .dnwo<C5(J,eWW,-m*h89(l۞-I*۬&qqMciU'EN؃00H *#l&4q`}TX IH?쒡 !E%'fJ@"q4-  =}7^^pΡu!{T/{R fjcM)kNCDҦ7ob\kў{stc`Zɼ?MHmU]H4 * ,)#ɯ 䊂#' MAf 6tǴAA+T-+Z-܇`.8=s!Ih$ܫQӯoAq&ӡ"# cКhXҤ\hJ[XX,kMԵQѴG{zzdwwW<谿^(ӵDo#p(---ݓCuvx@u -^UUEƖ<`_K܇ښ,|k(F7ntO#px,//Z`[il}}]v۽:.DD{Ɲ쑾[0fnnN.x[2q\\\z#%%% gggaKS#MCL$P]]p+xہtlnuu5:]ӸAK{8h%xeAKCtA%H` 7c`C855e666;ax~S5c~c-i<- l~~>h@͵҄jCSvHzz7)^LOOKAA5~(R ^OAH,#sERD_P$uww[Sm'Tijj쌻&,e+I;r֒(5lAAkz [5kF8??7cY7]'LO\SSccH:bbW'''AFF&$\9ECCu% }ONN+++6Θ|^]eEy tA ؊0 M &\^^(hGq 9`)uNo:-@ n`g1 hmm X,D"FC @OȺ=l.--} P  0`@ 0hJNPIENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plotting.rst0000644000175000017500000000127412253362407022134 0ustar georgeskgeorgeskPlotting ======== 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 sympy-0.7.4.1/doc/src/modules/mpmath/splot.png0000644000175000017500000005630612253362407021417 0ustar georgeskgeorgeskPNG  IHDRhPLTE             "$&(*,.02468:<>   % $) ### %""%$$$&&&!!)##,%%*$$-(((***,,,...&&0((2--3..5//800022233444455666611866:66=88899::::99=<<<>>?@BEHJMPRUY]`bdfhjlnprtvy|~<>E@@@@@BCCCAAEDDDDDGFFFEEJGGNHHHJJJJJNLLLMMONNNMMQPPPPPSRRRRRTTTTVVVXXXXX[ZZZ]]]]]^^^^^^`__d```aacbbbdddfffhhhjjjkkllllnnnppprrrtttvvvxxxyy{zzz|||~~~)CYIDATx^[ @EA~$QH>3Bѷϥ`,,,x ``\Έ1F-'(Gd-t̀8Ok/{_1GM=cc<8=&]qkΑRĨ륈ҤMn.EK ̋lux%$'KyG_ l3 Q\]jRdJ`bW.5b.{pcrI0o,.E F `ٳlj1:PNlN8n/51jޥs:kV +W ]0j4M: Y65NjcP:.\l2`t)d0SC[g U|_aP;[g{՟2`[j)co6dC]SsQaf.\rUD E+;A,DD $A$!\/`!V"ABkM! `J@vv6fVo';l6sTI\>N7Y@oeDWF`+kN'D[#_;_le-ttt(0rJt=& lP󘥜Ngm[FZ-`* r:ˋ>JU*Sgӭ4Xv:2Bꀑ Q#uʜs6pkN{GSt=0(%8V6D!~eYjyYUSj"o`%!KZ`%f ^ *JyN)V/2ϼ"#[,J,[y<`K#-vdaFoq *0Us]`(cS"!+ > oR ,D͇팩*SHƴz xo%B6`({XT+F8aBoe.-$$c( V[4xLR_MRqZ/Ux`ZK=L`r^]6tMqlm4~^AbCJrR\bO/֒Fox. uws& -M`(y&`$z+X g!׭ 0ȩ.qK eO.0a)0ojc7j]='g3?Vcnu'f#G*nwRqܺ .~lDYaF R` awt~\ignʆG7&Xm~>ȶL޵˗ο^Io%YX?ު7&ıMZi`ZaˬJΙ84 Eׇ¿ׯ|XR`"|fȕ,}ioGZ`k􀘂J ġ8#ih=FIM!epkCb866^뵉{>tK:]嫞VʿNP"djj K޴; q)=rǝj\ B7>[O\s}+үz}y+l\ע[ד*w1^ $rZU-/Na\IiA;x ~;]V?߱E]5O|yu&>b wj (=Ml2"%ZLaˊV?P;rAˇ==[` *gWk6_y+J]@+өO+w*ܸOn ԰Zn=DžJ<.ʁp+R.o)j06A*5R ^ -˗Z t]06߁89bߘz5`v&EP  ÿ+5ďl;-|J%߸!:Q_:-V&?MX0 m#:9Jc}S,?vc8p$Lg^u :v~`rK{*L] ?Z(/10)%LjAaA5>AϏ~ Uݬ KNq*Oqoؾsؗ-Iߵ!cl l]Fpl7|Q)I^ PͲ$EꎕMy<]sLMgWko&V:--cb kLdMϹ0ZX6"~ff'٩ʎlA߽ o_E?&+9SJ8̑Fupܑf U#MKY`FEMiK-=,ht|y+@co6˶ F&ԎQ5hԺK .(L%OfG4Bp'@~Ƞ4ďR)z5ӴMĀ}sy5)Ās=Ο@!쨥F43p/@KjcڇܻBztģ8*⇣<kW݋۷VӕϸoLq{H5bK-+Ľ TU?%PeIZe$BhO#HqOk;9^tK9̓@ +d:YRicW'+Vu:@!V?Z8$%/VkBB@ay vP~m&x}-wUb 6`8Yjq7\5 뮎)!iU8L+[Ȏ9v\>%U)F04}ӆ̀?-fp'5؉8G7xg<+;=uãl1$3 "\Q&&q~$s%o`"{-~x;x7% v`֝r&$ߴ wФ4aru-ZPzlf70./}ǷX4;_muJ2!`._)ͬ;QiI+&LUN3aދ:m%I 鞬HcO sP1`9[}XW?}O"_=|ǰyM=ďvg'X :>'*\}ܖ,ϻqCoI6'BOD܊aڍL~>G`%1gx!|tͶ}LJd?+{_VsbeȮxh$=IePG5eB\HS:c?x RB=bĢ [u81 ?/ܟ| o_pū>{/PW s6OݓGbKT-|KaBkɜcVF,ZPY"_ y Po3Ū+C욀R~ǿg/|y: 97?` bz^sŀO>sO"T ]ˌro$M+w=HjNSC}NT1-7r&,w7s+wV>nL s.|{Bu"fhA>6Ҽ/fo 4\ jr]=;'sB%FtbtAz3*_=,pLt1MkdLƲI.E%}]XanXm3S΄^ٲ1j?I D0u=eб+aV5coߜj;EZWa hr܌#_NW3bCa} Pe]h}`"?"d ’mbOMi*ysbnt'K!v U49wzh0ԑet,(KqϝDcw 0jDp#ɫF0B U#Q-X: 4 a}BxN=9a5杙UQN8 b[hzqs S̹{L#F08rZr$:US,#iYL|Dna@7f$]F |nUc|~ykܟvƀo)K ܑd vǸ )ɶCX<j|i$L#O>B*H[-+Pe{i_.\l\7gBkgL}L*`ؠ~p)c \8ct1|{#-ZYz\Ƭ sD uY% huF`GDj=<ޙ DݽDIh! eRNJ$82=4Ʒh1]{4l%{u-Zm>Y"` M0{0gؐfS:Sd$kZ'o\WKel9Nj!-`Jm%_>g[:$ȱX;mk_Hi],Bc טu603FN,8_B-^>Vv]~M.TEʝ-ߑaGI6,c5"~b+]Xݽ;|`ce IwTьvp2%I\dd7U8&BN/v#;\&?,TOt~ `R[?`ct}>?o0q&`u=إgv2:G!N0A{:4Wɢ+Ɍ{UYVQa8bylrLrȹsLhuRG. pO M =%aJۄY}^m'|\KuPdɻa! ;טt4- qym):߹SE.]1"4r4ʳ#6SIJ& %긥 #R}p<Y$eHrS(QKe,7M`6|_LKOx]#YF/&L mp~; Zr2YԨ(Vh(H%:ES41-7::R>)lď 7 ؏s>!bsYmkzUдO sb.=5/A7pj1hTvrƴNhC/cur$8pX-';߉={ OppԐO<WTG% {M:J'*zJZ["JldgIYbVR;x=(^| <撽;yht.Lײ]_XD"TA3[i` Vu+T5r,95rq2FXwz7Pj[r~1F'SaKRkb;b (UV#Ld"$UG=A%6rRt7%u i~=uJ*SO0_+TUD$Y(X4s{`®Br9b% rWRBP3 P$#h98@}X[mL֊S[GW`ZeM x& 3AdQ̮ol[$ W6PIy(ʘ} dNdG9!65Z htl1QEG܃Ʌn+J34Q ;R 3UMn9ߐ%ӥuT%k::\M˻/ܴ!|z-OS 3VD8U_Mbܟ[`)9RMKt$KLjYF`&UIKn#[Ptu&=)Xq&m#bjkL:=wO[ Wv3;9uWSpJ%ؠ9zXB}5:VMMٹ k'vLII fDpsu & ;\#rC}TPYPW+š5:v|&0 <]~Iu!I4V*RrVI"/3%=\wzy5+䳖v С<{iC':CKt<ǚ?%H8ً4ǡ 1$RL*o (1h_6midK])H-64igV>b|5ud\ןD9l n~t2BP$2x0pǥmY:f`!piUEHvʹaFɂ>d]0qa]( Iu+=3Z}b{o)Aia)|+NR#"l+@lFaI +wrWAPTV{IB=:Fm&G;CC-=@`|`}vnn"OQ6):R?Xhcd!R^dp6٤ۣA]0sB Sr!`kz) D Z!^ x-e†՛&mQEO~sӍ/< j`~}nqRWYsXmt A-^sM7*݌%";E|IehAkGؓjR l;=;\Pr~T;sQ^8)+ '6ڄ C Z$R@+Ir% YBf::|bCDAHS?  v5.~kͯ1,^|,#Vѽ|!-$;Xij>]wC|Պhw?Eu4oeL0-z~&6X$4sșC }l3x u.tR9Z!;]16{M{Y%?-{u +~~E>tbXbDyWE(n{q,h)G+6oG CdٌY7WgOLRbw l ;_ΧJ:hWldR~y)XNՑUOiPRZi]' {C)wXAc)&szk^B7> t(I&x݇9Pt.>e&Hg] 7DԈd~]q+c5 Oy-]AB#A;`k":KK6mQ%u'\#v͎SI#׀ٗo @{p0EX0,z5 {ĂwvBJҸqS2b.hR"pd]CGp44EvA#\p`]DE{[w:#>;u 0M$qU *f)F63NIlReT%dtb01=r+dT1C"=*BwMW,J{׶ up/̣S'ilZ!J&`^8`1w2+KP%U5@F&2IFfqn-P/O^$>v17nο8]*I#m BZqw35h/o,ήV?OF` 󉧛=DCSCKp_etR4Ζ ,Bg)V@Oo}&\9PzX9Fvg/@j^=/e< 7B2"`8UK((ڢ߼md0ρLbWC= yD),7Y0\)Jz/-o P[_3zE:pBFa&҅f0`G;9ebŀ_ LL庼3t`WF]77Y2qdMOhEO8 L@lCJ W,eDrB><&}<|.JPI7~{1CeNka"~,1\,]\.۩ ^x1a5DQkW-w uṣeě&mx |w$/m+54pFTI@4x+ 120v@ Ji2k{KTx5TY2k~w` 0}OEMN 5r1#/!;+ d.4息iAߦZE_*,*)OW?w!T8d)pXW4[٤? ?# ~.D sPk@٩G\.lѢ-`Nw!4ioza;7\̉]ޔfKAKTt\L\FRTHŁo(#&rJ[ ///w_Fs,4i[g%Ma ^;zgqmy nkU5sngF$<YRuTCQ2}*@n>-q//.kgN}*dcvyT*=k;Q#ʟWCGPF7#gIN{bK|LAIf)j98ʷ >ԓ4SL(Μ_ƽXKL ӧ8FУcK X[ZQhsȓي^ aH$ `ɵ0o=! ݁F7B'XoKy5= LPѕF ˄mJq/ⵡ)l(_8`+w0J gqdԂ'cFz$\xHec2F ╶E/%G{/c/yӤM9ig\0C)I,cu.tŦ\ 9w=h./o*})x;Dz?5</~HsYĹd9dZ3L\`orH|XB@qWͬ Æx06c4er,_ wJN!vC f%Gﱕ{JhEsMp-5#W 6ǟT//p6,"(<|!two'De}mq&#h{rAр0XYR\Ws]-hU3ձ󕊎3OB%pi=AV#OVA1`c1Uj?n{ܒy2IPW#^]=Gt^RK*^xWBPN4,ҨGL[p" ~@\aON3H&ML |RŌ4/9:V+o(/5rO Q$1G. x8{kq3T[.VxYe=7G7"1`qӈ8plKOs.ihCjӓv1@`Nʄbrx:ߕo_,^) Qd҆JF><&=Xa].U)TMⳡIK_ F -Vy*(E#^+i&(t\o@4fV)$u ŇvrUuu_&=^cAvW`n◗):!{|[L3gZ2 yv@[YY9Ǫ :k6:ulTt2F4 DZcՓ`11X<jz>BʀI;@R*b+lK.,%|ӤFTZpF\oh`u.65^hH>Yů% $V]*g aNb]V0,Ye[zT9#Z2XпiҶ˅XD\n|gΕ "51XқfC {Uh%z*qP M&m_7ppUE iC4xeRab^NiZLWǫ&wq1xP5|*`KjD;:m~KQ ˖U):PgҮ}xkKT+A5܍u>d ȭj(X\|/4iW&Y/Wh yDžH;NPfBNSmg'4<5@ )p)e)CUP5=M'j-Ra dwJ~m מ4vͮXk x/`$<@#Kވ z֙Xnlhmg@ȑrtFvN'*ltL+s9*CCψo~뀟 &Wk=)e3/R{ _(+^*jr]D'@T* AY[3T-XA]DQ_Da;v|k~v=+r̝+np>O_J$[@o cNLюHƫRZeRJS㡴ŲdkT `a{(=zp[97v%x>';RR;$n#!iiV{OGPAZr6wEQ0߳E6ϻ o#Q.19x2|{j_q{gL;{( U=<QSWi#MIPwݗdqlK7)-="CyCǩ> F~DOo dI`";Z(})8axu7]$Se]fVnJYRg|mGyz6~[uIHxʥܧm$.=K *)aJ(D Tu?+P[GЧ6{; /NdJF]I~}v;$mPq`sGfdTXe[H{/!Ctƈ!%&N@u+',M^"w^;E7D@aٔ ?6 c^ -+0CT#mwH#U؎4C^o%7nWw̹y /u!wZƿi Tq|g\-fk^V*ܣVjRV_tc/L ˁBN&JxZGJJyǎ+3!`!_knM^6=;J/9 X~O3t3TNV#PVFcʞֻ\ծhh|Y Qe%[1-8o^Fw0/L`2ra\?a`C)dǻOhOc"]R1^q~i¤`|QU5"d$/C!nҔB]on?ӻhMY^r~Q`yk؁kWf0`\e{,et9W}v1v 4,#6e˩-)/t 0JsO تnnFzM{yK~.6dIg1`&5ㅀ(m佨l">Bʱ?`iv,amy1cĀ=wdӤ̝+`R\>6B|Tԡ/)16dC``, ^弄Z|&m ނ0O 4c})-o[=S %v;-kjNsNlg@ Dh! ( ~ItTSl,dÎw֓ۉw/k7|z{t):f25XYbׅ(acP+μhJ2mjv g ]b6ّo]!x*_ `byD&-`'u9zxSe,CzGlPU_ 96 4_ l4juʉ qEtT8r:'#N|88agaRƵP=H~t%dRMQܬE7 T@ehJPu"a_b|D[v<5t#WĎ#QɉW#0+UۀVC^ 680P ېe%6^;km٤zAާMRѰ45=奛m1^&%ߢu&\#o8i1>F"'. zjKJ2RZ]n }5dzWȰo_L/R֖_v2UH=}k+8N3mpYN>`&~vFMuUIt2Hs3,">'1C@_2j#mf`c gK>ײ6z}hHpz Doj,?ZSr;e[ &3kuY'^3y~ᆳPO=1Y N2=ÏX9@+}|^մeTYJ7V5Xb v1Q'g7r=X޷XQ_F%;r:d O5˔͢[ ˄XX*F)SC߅xͷ G. 7%!;[fE_oIO om#uii2& 9N0lp65 :PUr2SKeQsg("ۂНC|!ۀ[N#'6 &cx`@, [G(C߉[a f dK1 isHVE'^ r H#Nb{HG#B ۱.`X 4,{n5 qիK|صRyʢλ{}}My>Sxd]1W gu*iHk. `JƈZkNjPnųfwwG3cOk78U])SM0j:ޞ#b .0il4f=ۆӄ9CVN6JfNst u>H(7/-9+[/BC}`W@`DLg8 Xs+߱ $b#HHae6Lbhj&+||G=Z%ID>5|)~6 Gq띷akGU8kBZ\JU݃y> cȻ9iqx3~=j0_24ai򎅗%W=`6 mCkSriu O2J9\Z!=Nho60ꇓ|\LXFNABVor5ԡp8Qk-Vv\ MpDz`vQ~ U#=#J a!4@hIi`]6;'Ɉg:լr+Vj+_XZ[F'oGybhw6y)T6 Ma\8c+1 $+ߚ FftPqK> K1,{]3T3wD2kIx. ;] m!5:⹻^~`W ÿs7?䊾"<'$ck~`_t YʂljbN1jsYH&v<_q&؏SSpxɜ O{ӄg|b܊?%.֧8w_ڱZKyVb`P^ -{o(᏾A3~r|&{é³7y~X l w] Yb<5CS%_N7YAq%F:EdK)rA×%TV r&8J ^& p@OQ?w؟W^V!_6m'P(" Hא:'ePN4>>&u5xLE&=ٳN`*`ҝр Տ[(QC\*\bQٮV O&k>Z3;.l7gOHnH%{*g|ow's-`ŗ Emcd$`VXJ7h]Ӝ"Fc99odM% {2f^zGo-.҆QN-|Rj,þT}XH_VR݈oVƆjNA|Չo@`V J77 `j,irUA`\)&p9v9XjxplT~Du?md JzGx:±(~̔6#kP^:-25E%2ZZK*3A/$C~lu ٔz#s?Y4{1X(y+jrI#:Hҫ6B6ULY۰MFJ}w`W f=޺xx?1c#FIPn]Lb^:i3N#D8ZAr0oR8h[8}i Jèë&ȭPaA|-w&iFV D@e}҅JU\ p(g8]ηt ( |8YH}ٍ0@rS4k:- !ʄəժޏ(SH4YЊ3=Q) 580y.xG*5* UI;o$xf)jjU$>TWtĬF-@R& 7؈-];J,4<[_-2Y3fjJhiiDP(6( qqbN?Pǐ`}r1QՊ6(888| ~8e[gȅP QUvD |( bpݍpƉY7֟“;'ip˺3LyCJp@/FS8ڑdr2eg{74?y:o`6'GS ُ< LU+^{g5_œ7pw5uB=ڳWiO&azw 3^+T 'Z$@H>r&X2nH#S+6˰Ġi') $b%|yb'] \6P{I_BZ,[o:̝k9_ƉS}abWWDƈ&`핲Aj8 Rg6bM аƚ-!%!7w@G FPUS+%/ ſGg-ȟ,x@4PBOw`16ټ*/`? &^F]b ͇Mgeui=Ic$`|z) ,\ f<H稆|%uT3xeeGL4DbV<&gzS%!@Ah*gk:`{Sȵ@wH r˫ˏkz9 {ѻ P4tj}z)рׯ&Χ0掚P"+yoWPRڿ 3Mdp A+г N@vPN26Q@F[-ƺBzn"w1D(  +y#f]9prh).$` 59I*)rW\,kSU LJ_\X/.,,1I+ԛ<_q0x JcP n%:^e B QQ@D#`;q8d8 a<34:b>H7眹r^Zr'N=IYYmN@ }mɁDD ʀ:ĠCdh<@ '0V0{ĕ2Z}2lǂſ]tzQ j.(Qh|-\jitբe^ ȥ#,[[~u_d`#C-\R:²y[n"Fvz0}0?[?9Þʦ0m&42jfڂ.w* Cq}_C_CŚP2, "*"TAAq6 v1mKr>]Ky^LO (0v9QfGK)0(z S@y2q (L=a+W(Ad& e;g0 Vo<z> eORbu;Sś]7+H92sTʓ{7K߬,Y ݁%Fp9r|l`m#;Xeo ,.+?".IENDB`sympy-0.7.4.1/doc/src/modules/mpmath/index.rst0000644000175000017500000000260412253362407021401 0ustar georgeskgeorgesk.. 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 sympy-0.7.4.1/doc/src/modules/mpmath/setup.rst0000644000175000017500000001571212253362407021436 0ustar georgeskgeorgeskSetting 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. sympy-0.7.4.1/doc/src/modules/mpmath/functions/0000755000175000017500000000000012253362407021546 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/modules/mpmath/functions/numtheory.rst0000644000175000017500000000302112253362407024326 0ustar georgeskgeorgeskNumber-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) sympy-0.7.4.1/doc/src/modules/mpmath/functions/bessel.rst0000644000175000017500000001046012253362407023556 0ustar georgeskgeorgeskBessel 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) sympy-0.7.4.1/doc/src/modules/mpmath/functions/expintegrals.rst0000644000175000017500000000441312253362407025007 0ustar georgeskgeorgeskExponential 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) sympy-0.7.4.1/doc/src/modules/mpmath/functions/hypergeometric.rst0000644000175000017500000000655112253362407025335 0ustar georgeskgeorgeskHypergeometric 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) sympy-0.7.4.1/doc/src/modules/mpmath/functions/zeta.rst0000644000175000017500000000400512253362407023242 0ustar georgeskgeorgeskZeta 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) sympy-0.7.4.1/doc/src/modules/mpmath/functions/powers.rst0000644000175000017500000000256012253362407023622 0ustar georgeskgeorgeskPowers 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) sympy-0.7.4.1/doc/src/modules/mpmath/functions/gamma.rst0000644000175000017500000000570712253362407023373 0ustar georgeskgeorgeskFactorials 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) sympy-0.7.4.1/doc/src/modules/mpmath/functions/index.rst0000644000175000017500000000137212253362407023412 0ustar georgeskgeorgeskMathematical 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 sympy-0.7.4.1/doc/src/modules/mpmath/functions/trigonometric.rst0000644000175000017500000000505712253362407025174 0ustar georgeskgeorgeskTrigonometric 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) sympy-0.7.4.1/doc/src/modules/mpmath/functions/qfunctions.rst0000644000175000017500000000115312253362407024471 0ustar georgeskgeorgeskq-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) sympy-0.7.4.1/doc/src/modules/mpmath/functions/constants.rst0000644000175000017500000000440312253362407024315 0ustar georgeskgeorgeskMathematical 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 sympy-0.7.4.1/doc/src/modules/mpmath/functions/hyperbolic.rst0000644000175000017500000000173012253362407024441 0ustar georgeskgeorgeskHyperbolic 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) sympy-0.7.4.1/doc/src/modules/mpmath/functions/orthogonal.rst0000644000175000017500000000511312253362407024454 0ustar georgeskgeorgeskOrthogonal 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) sympy-0.7.4.1/doc/src/modules/mpmath/functions/elliptic.rst0000644000175000017500000000345112253362407024110 0ustar georgeskgeorgeskElliptic 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) sympy-0.7.4.1/doc/src/modules/mpmath/references.rst0000644000175000017500000000557012253362407022420 0ustar georgeskgeorgeskReferences =================== 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/ sympy-0.7.4.1/doc/src/modules/mpmath/plots/0000755000175000017500000000000012253362407020677 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/modules/mpmath/plots/bessely_c.py0000644000175000017500000000016512253362407023223 0ustar georgeskgeorgesk# Bessel function of 2nd kind Y_n(z) in the complex plane cplot(lambda z: bessely(1,z), [-8,8], [-8,8], points=50000)sympy-0.7.4.1/doc/src/modules/mpmath/plots/coulombf_c.py0000644000175000017500000000014312253362407023357 0ustar georgeskgeorgesk# Regular Coulomb wave function in the complex plane cplot(lambda z: coulombf(1,1,z), points=50000)sympy-0.7.4.1/doc/src/modules/mpmath/plots/coulombg.png0000644000175000017500000010262512253362407023222 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxwx6~Ϫ^,UnmlS !!>/8āj␀).؆`^U%ٖeujѬJ;.]k9̙{B 'p ' =т8N8aD N87N8pN8ᄃIN8 'A;N8(N8pN8ᄃIN8 'A;N8(N8pN8ᄃIN8 'A;N8(N8pN8ᄃIN8 'A;N8(N8pN8ᄃIN8 'A;N8(N8pȓ:t_~%j5i&Vƍ F>h"t 'Ll2|?Gڏ?HHH'NH'p‰ C8o111xOhN8 5qx1m@HH~BCȢ߶>bՔUpt1ٯ %жZDGG;,|mgiHtwwchh!69=/}}}.^8h"2AOIrr2ySO=E>Cw^jɣ>J~ic7{l75m'ɭǻ;"poly Un!Mj"Gp=Tk|K,h 0>|s{#"0 lSRGÝiw~lt0vG[ {]aBn>YvG„8! dg/Ӧ`h&ZNbvlm}=6Ŏ&r[\dx(-[[i3"\JꓩQpPՖwʢ HT*e_?mr[\dZHwtP4Y1Yu~!(( 2H0 DBB r@@\]eIm&U..GЎqa]:\~;wmr=H4OZ>mg\Ȗ|@DEE "V?(1%AwttX:FrLJ%,8AX/z /mǫ_ՙ9]ns>>惾>$n&A "v KP $$  Ehh(BBBt6nkll… MYtHH(AKW(ptH NBnd. QV /9Iva`8J^*8D.ʌ0 Km4dk. #Ȓ9 J_~IBrpXK_B(ۡ#h9GhiiAsscg$W. ]Q\%A r(j]:𯱽l;~!7)ࠎXȖa"@~d,@Lv 8I7((!br~^ wg@p)!SR#-,MrA###hkk677[PHMMEdd$"""ﰰ0_ͨi"#4k.{B!ő K$ 0xhhTC.QZ;)ll悾}}}hhh@}}/Ԅ٪|}}I(C EJ|BS'9?/Y`'!P-qUryhץ+G_شp,#__Kbccy\۵bޝe+:@}v;= E%A v&-IZXQRW=rqjbSNɓe [/[YZ.ϹC-IP{#y.}^=*vawMnV&TUU ;22 " aWz- wb}nS;2dIТ d,9=S.߁lS_֑pUU]*TWWchhX|WWn+zU4ZdD$rz$hNBfFD.99O2205d*MJ|sܾv}Z-uk{lhh($+1ׇRDIh[#Z>kAu $hNB: .o'e'n100`ռuܐD$$$$߮!1G^^!LD T +kA-IcNrpܸq(//G:MUxBBB#^}"{9a̹@j_E&~A-IcǤF$yBAKKtnjj2ÈtM811 Q\,XYnڊMqu $hdMj$⮮.]]]4#-- {O[>7K+ToC^oKfrsG, Z jؽ8sfB 'yۋ"(J(J\x<==c@iiiHNNSO'~;eIruX`z/ek,Zchpnhh+/ܹsY .DZZy6QNBQ>vlB Z :2.((իWMl3g !..N]xKC~}>{/h@+} GshIo&qul3\0-[`ժU }}}2&I8Ikk+ QPP#j¤IlY2 n_]mo!W9]u +'X:;&|hZ\Vh h'!@krP!`yӃ|={VG &M2EGӧOGNNYSv-WF@Z8vBd[NPTx~Z f̘,**Jliނ>#<6I0ⒻlCZhE]@Icӟ|DÄ5zh̙4jtN8Ǐĉ~>SNł tz{)V&O8Dw(! ̉GG#hAUNBHKE/\::kA,?;۷---m Xh,Xn ᎫCRaAzP!t t!+h:ZD+w wrsG۠`u9VDB[oaɒ% '7t~_c/뮻dCu*\6u-Z5D$uuu-U[@ jm 0u ԠmJ+^?ת.pQ|wػwA[XXVZ5k`Ŋ 48ޅ#wY<kxCch%A[$db=p@tuDnn.z-\~˖-Ö-[҂۷cڵ! Q}W$/^ Ո㿈?17o }ʾqJ:Yڠm=<{6M>v N~mۦsssCtN:OtĚ5W_w5YƏa̹@zMw,_\ K &'!Bԩ78 ?^x\t J?  rcsc(/[AԎe;dA[$oDixCK`\'rJYOWO0˘=˃}U;dAu^t /0ydhhh%KpIqqzXn_w_J^&^9t_8V4\ K$$~1m4|P(xqe|w0O%r7NKn9~Lcn;B3hvAmIxI,^+V?hhhヒ)Sh~cK[ 7\ރQӘ˃Rʔ;dI|NBRիWc8~8Ұm6TVV3I6m/y%wg'-G@'BcN7cUH[: 9 +uxhrsG, Iڊ{ӧOǾ}(--[psFc!_ r0smqώ{𻫿(o/?dCоDrsG, ZIXVVٳgc4i{=TTTࡇ(/pM8}8A4?ߌ7 o1$EnFϙ rsG, uTVV'8 :O%hoũS,0i>6mGB`>_9%.Coka  + u͈̅* b!K@QQV^nlڴ [n.TnrurvU;~s7pwq{vbn\kSy/{9mkЎPc"Ƽ>Y7f(^RBގo^{b9^dg@ru{mZ=AƐ˘?ҏ_Lr'rEEw(v\յ{x?_lc 0cõ0o<=wpʢ^ dIP*7o{9:S(8rD'@gI9#}#13j&jkQ^nO鬂Ï96{]xx/}Z'0H9=wǽ/b8^{?qEjȒA`` [9cY>e,yj}}ThFwVxf3RLRD&r9B?Byɱc ? M@He};:n^ʕ 2>><.txfg=#NU'{ݏF{u՛!o /IN/6\@̝KȦM 4q444 &&z--ӟ_2C x`Tj~[ ) ض ;_|ϿƢ FU)֪OR_ͣrX^/~&Id8{X_|UDO?<7ПbN <61hGůt{6@|V~Lm"`v\^ A=zn :,l$ݷرBHyy9'RPP H…geہ$$T+ !央hHԩM#H Ξߗ_lyd#ep<(!@9Ʈy쫪H+!ޤon:.|',Fz3frd9Y{iy} 6VK>2P;_M )/h?A@HS-(('K>dZA=o$xA' )*"D ]Wz]8&ƍI\\yɇ~HKZ-yGO?M{1nKD M/ CÅB22lsFB]#;SAϚ7/]΅DI4Z}~z$B L͍//B\banɽRA6o6l $>n;wswvLyUB** y껧6w=qr:OD 5]5+!o#cB㩧hϊNBhW!YY7ߤhh(!3{//Q ,ڱ6x3y c7q3q8-k׮%۶1MJ"DիiF(//׿/]Jm&$1of<@;޺={&9w.lY@z>P(Q*_{h@w++Ǿ}}?nAzE#54GmNmmK_" ֩tmg!ʅJqu%$0FVj De%!TilΒyI] §şlɮڿA H؋ jk6S*8 Z t)/_u(n) }}E&'j5!T ݾq {wW_ÄmO ~^y{ !QQtWEw__O'46n{]B`3꭫By]IdG3umZG#2[EdUS`3K iAZ$h)p뭷BPdgK4Θ!Ag`mdcz;0ݦՎש.$7 IDATS uNk#2=7lw}a]]_؏>bwes̵ZBRSi }^C_n ޟly6V%$l+O_!Gp4~de!$$ړ T8A/Vw7Ua2_ؕ ߹7Ɉf==o36,r]2r˖yy@q1PP ASveX}9}ym 3; u+}}7?kfx&bcp Ucx0^ $$@p0 Ij1/.**dnݴZyFxame$k` #d1 {2+ۮkg9LV-ͅhVS[+v-їGi-:l/ ތ{_}}e; CeY4[nam|c`nGVYʰMD0>qO{ahkaacI:0u*T 4fk2 )A@}i"sw}7 1T)IKά6>@|spOs|(-22|}0o-~9"BI6][@P79ZЊa:+&|5wcmk--Ҽ{!s> DZf_G, ZMo{H9m\V}G ip fyBlFOx` aP`-zuhwazFMOym1P(;d̵Z04fk{s~=);@ܢ<ȊBJp C?+;B@F:Z-P 2X߱laWO%xQ{O!!0g[>ϧsbe}_o YSjI PS#AKIS_Ypad;4쭷oQ1Q'zY2)ܰrA]ޓ'd hy_`ya2NQmxBYK"yUDjpw.gВ `e2%CcI&GOěEbcYkר9vQw&>ٗ-lK0ckjK_a,p nP(pspa\rCV^4, xMBV gׯ/JС!T*Q25khn/jBNP0uŢEƳv,ͮ]]4t.ygSĚkRׄxZJ cyv V9!TsqNT h,.O\\:>uy嶀`8XuPx hi4=BZ`hkBx8U-~-Jpaxx`dg>_>0+G25392dIktJ;ܸ! 5k }gXۭK7K:LRXءʏ^@thj B|^-Mc~2V͘}B5oU@.'O4B\]sM]תVMĀ?/K\&#NFI+֪q A$hc'!@MLԾ%IRfžAyr kk=[`gKHf=eyt _9R0: S`ԍJų VhDtn'v]91s捣5G1 ^Xdf3gҏɓ_kר(71=Ovk#8Q{>iz{2$:n吉Cm$C2c˼rE̛GO1;OخS v-VܬY. ^;rrlz_N*ʂ§,`IA;e-Dp/_?kIkLv-ooJpmmn(owO˩a<[| i4a)\.8r\ImrjCj dINBTKNMKo9D`'Z-%OOJDb0g=sFAy%ÈC3gCA{cfLt4a?D CMZ-M:٭5o>NN✦ݸA#3ccëU#*?``N5j2֬e;WƍxhTĆ4aatkk)R,KhЏV@oq2gIG, I*⃋ wT{:O._/I(&Qt xPUMɡ.9}ڢ-@B%MXٳ3wu:F8{~'0ƲeP0^NX??֬gVO"U+œ8'ߵ}2,P}ӐY$`cC4HA, IHAۥ1 %E̡dp}X^#̱$C^)Z'cT-g֭Yj66ơG?W-YY Ĺs Ӈ)0t K~^s[>Ш .]#Z謨g_@_4L|0 yw7^שz, IP{t)cv hyr- .Ӈe ;pxSQNaک5P*tN][KY1>؏K\bP=A7Ӭ&g %h)aa4nUwYPB٤wY&ct~x Kr;ȶ,u`YPBf'RhЀsΩS6iNVQp n(%jyyy7X\ZM?"~~܁-}-(i)ARPCY5KNBscCuC8~30T7%+by٣fZ…4qF aZRiai , IHl $[Z[R rw|:.i34v=uu 0r$%8jz.Ze]a [*j,@_Ue`/. ϙ4 /O/4{8 `dJgxtI+{0sv95au9؈ Z(07f.Ճ(j.8C$h}~q`?,,طJ 0r0 ^6tJ l%躺19/-`mzZ%њG!$$ ʢ":CIgcAc|%hkУl9~ꑒ¿%ءY :o}Pؓ]vhY4@R$fT,ۡ% hI:!3OaP͟&[ :51 CaЖ` C9ed5NBH KC͍4וњ&79Ճ.K$%` pkmY]sU]UH NAge9;;C, {?=Ra]]]i0@__84sNB@b; +99y8{e[_S6qLN*SVsǦ֘9Xrxb Nōl~$$x@F[>*8<X6GrX$z2 Ruvhk㗋BUd &J5ǒ)LFgȎFS_jkc1Ѐє8ԏ|_‡~hbs6$$Vz%NX).Vj+PXDj,,4S`Nl /3qxxdɁQz=BUIE C+_7kޘmgٳ; =Ga`A7qeйD+RcY$h! 1s\hO>p;GCO(AO<U[[#k 1Z _}vލu,+**t7Okc\ nmvѣuɱRFF(,o /Ϧ1?Z`ɓi۷r[X 04Djh*Ta8Ws݃ݢeٳt rsm{ۦMm_=JHO>L3tZ%W+peE`dZn?W\Ϲl/5Ci9PZ.+H;Ws 0͌1 |-/ {=t[S՞  B?ٰay'ɋ/H>Cw^r1cGyk&=fj  ۛZA% l׿]}rB6o x}+ljk22A {*>UG/%E !O}^^_T'>HiZf8q{Ս z6ȧR`3ު#GpTR%Ke# )<A8|zƽ`3H҅ux*plQr46νG**,rxCdi+i퍭FXh&^՜3Ь,ZUe>H$4j?gEj&vEd5_ |CՇpE?8ƍWVxcy/n k/*Bh!,,9 1Ʃd6a5V0ܫmX-qdfRAЀvL"sďB6L xx ".,AȂd..Q  ^LDoA/(Cˋr|K w-]$>tbC:0dIЖC.Y!: VZEh4RєŨ(6D@EГ&#%8.Y ?F\(,ٟ%0mNLrKbM/*abn<]o-gFѹLi(B+}:8CSC@m.4 Ih}$}~ B &'j%y3x| l9LkbeEa!zQ #8DtHVV֙[<8'F5ZZ ^aM2wa,Hr}t/\ّ "=]2, \&!iӨMKr J,Y͛^b0UMk-Z&(,D}^Z`.TޡH(qi(/ 9 86`Q tQ[P=:EEfSufA}t^GS_23mN*dJ H K$ !9jDHBEiU]ds Р+*hu._ UBïKK76mmP&&_AF|]TV&S*S0jjh{f&,B.^.\uݧ- EcZ+eծ.w@x$ANPa;`Ȓ8 15% +t/] ^sD >XAKANdjkQ%~+U;:&ʔ'v1D YЂA.0u4iBH1(qYңF^ oa˱lIGL!K$Z-P'< r/'N` .f+0OLBafiQVI2cġ=$gu H KCG7Zk2J,;P'!{:2bF)dIB^sinrr@c]q$UI,A N\js%-Zf9J uݶb3mL0`03Z4]BQgPRa|W<=W؇fʂA 8ĔJ56sBp)ܐα,=t2, ZmC 3Ǻe#,ѳC<i?,]7Wm> vqI1Bazi {_$HKKEs3-* ,q5c!F9ע{zhWRh1L7CbJt{ѦjCVD<\=,wuw?"Ȓ8 :6 +"P/!i&q%0#hPr>>3vSuv5SRԟ;K`>k-h1阋Ң{ztMǦ&nZS}tʩ/0e _RtbJ7\A5OV4d$hNB6[P]rwgQ(mGBgCl.\u=z8duL@\TҐX)A uvCYLm}}T[ghm\B 2azt,`]=^^8^{$}tڵ́&Y;(V /iiV#(Nw.!Yѳ!6%3Y -vAꉌ07N ڱ IAʢU\Dwʲ~9Kpqew7qPZ @QJQB A{z`~| p9 #h{iiZ/.X[ ,I?}~4k0VD`B1g鑢*\ѢV s Ȓ: o4hwwBvtXNBo:s~KO%ȦBDEQ%,_ @bJEM@X]`ov&y ̤S%$GcXMީD^ ]J1ED&sOT4otwɤ$꼴^M"@ZȒ: zϒ$LD9 ^+p)-"i\`Gj%=B6lgT&{%Hd+PEz7%<.G!0A 5HP1FniXv1`ݲ-Ĩ)Y IDATǺQ8WO-%AqKZ+!'k(i@d+`Qx겄i"3f'N`Xc>QB޴.[z AVK﩯/-D-)4=;6VTLtfe2׺Ce:3sCsևҬSTs+>^ސ%AqvC-`S'-d, oOcX[@}p6Їyb]XfgfF-WU(Q,VUV $%"'ܦՉӠ-D3f6JBK1p1Yg/v]\t-4rFj@O :`pxA-I؁OKk߸Am..s4fE5o X1VY(ďۉC%."QR9z?`r=b9 ]\pSFG[Ыk/-4`3m͘]jˇj/CCԼ1(zY ڀG3}ѠY:+eefvnoSSyȉ́s6N}vr.AI@f+kcZG6oMz+V AR裏AAAx7Z7n+4 >#: e yw3&hJe0fĶ+1 7vmI2a\ba9$}(\8~1vWӺKHHh/ҏ;z95 xC2Mfʝ ?3 {z"+" &%{`39׃8d^fw=|bH(A͞tTIƧ~ᆱkm۶aڵեs,?~)))x`b:$`f=hlgOJKҮ bI`L@vh1f{{7rU*K八Iwǟ[P-\]aC tХ;YsV`Qu!Bzl?Bh a? 6议.~FDVXR|m@9:J\.9njv"V6&s$$ ; gΟAp`+:T*BPqTnnOv)8ӏARSM;NK$N+Vb( ;9fEh$~--rB]()*08hF,zbQϋq۷FD8[ߴo͟|B7Wn4ٻw|N;~fW >QzGiCNsS|7 6NϛMıqhހ0BZ5jQkԜ(*!)!s.1 -`gn#|26mڄ3X krO]IԂ|9 De 9MvY / MnʢavՠnF>tF٠eaЗ=IЖ>>tGz(`AbR% i0#j||$'F9@qKIDG_F[ZZ`I3,$'h/2 :,VF`^niZ\`K@K8QwBqCTR9N`+͉UNDx/P;+5hoދ` sp17^AE6ر捖5o6W_}/o< 6x4cLBEb"W^.M.)t֜iTY(1sB?uƚUs>3!g@(0=c9mQQԤSVF jfPP7cHmPEke(>~luѕČrV/&VQj~~~xX$hOOփ-e zT{c'7g{"s{ZSב߼o 4h}blj➪0= #~Hˁ$Zˢ:sejЬŢdg[M=gz&-$艂NB}v%I0me }ivq޸ASumR4ܜZT+-XO A GQsgb.LJGrM.|1sijOOH)d,7{iϹe̊wpÍ( _Fhoc>DiK)}O K}vͮ"`}}k?IZ:]RȒuv0sѲ7nvq޴R䓛:V{Lnnx[{->@1g~!!Ծ<7f.J <rq Qk(d?D.s)o1Y١yiK)F#5h`B-n6)l$hּVn@m #30&FDF5$d#TIE|rk0ZHU(Cՠ$W&34Ԙՠu$ok,7g3sb&0N;jʢq`嬁AB@mGay9ո8 tڟFIZZl?ܼEJ07!sѠz&%BCLvc˝NtNHm2' ~$3[OvCc3meQ:SEEc%*@n.jAȔuͅcIhBʏf[AGGcJOؗeN5~YN#)ƜlRbD;91stSt4hc=<>͠u]sE?+z tm xz1^ &:d!ˋzNcjӣ~:;i)4C$0T7L]N ZbX$P$e0q6KВ; LL/NR455dSHLDxAͭEJ1|MP3wF Kn/添 Cķ,'2.]B_ ׬+?/@rv, '!@oF#]0j@Izɝ%%TRJI\r}YgYWhgXoٜBI1楥6&u Pm61֧\rL?r`Bvz-d̋%Z32 ,&z dW22e , '!`;tI 'gTr'!%E J[D8 GBp.iBR]EOC(ty:nsmAj%AMʃQpj=u uL? AUfl(duIEߟFMoB63:-ԗPȒmq%cgBmjH$,rQV&*\r[\J;to22PYzA(p8;9KY]Fdgݨ<vh. bmԠ}}z46vœ,9{!cKP^NjQhQQ L '!`'GXtI8Zk:r G6Wܰ!Aj@Cb^|h捹DrpFC .XNlU~Z-D`7/&Mi_{Ud )P D$&4i EXX@V^YuzuW]+ª""% J@i$b }f̙iL&\ׄS3s? ؑV[a[6̹# -!0fn7'N +IB5|}l8_.BmnM66SºY|aa6NڏN)qhEVkDk2UYM ;) םBq:+`H^9bvsTOE:hcghֈ" (Xs&4 ޢv5I>+})[e o+@w]Xrv= u]78Tr~rJ JNBP1gjOTyf B:}Fⱥ;1d` C͐B@pP;6Aw; 9] YMv~w1AX/ f=tNz\hW@׎('lnM 4Y o@w-`@$zHIBy}9JJl$%"!5*JKq̅i'NkӴ!*5Vo Ct[p-,pS8Yy"Vsj?=b͹s]1&-O5BwJJX-++u!\hW@$ ;3:hǸ5I(ğxCJ>ggN -`C sUp+*xa:kt8-݉3~J? Z=U[t# KiC.?pG}V=HCg?hW@ác'lvqk 橍dX!CXw%WuK]C*ݮ,id@RϢ qh[ݢC"ш`-)B0Ȇ n 3QG [')&)>l])s{*9Aԭ0[aIIl>t!֢Dz!phNmظq#fϞ/lHH4 ,#I(,i ((plݖ$o',pt궶Z]X 0?Lw4>8%~JK%J ?fSPP izi]Tw!M nAP91! E]Fj;86[" yyp?)PPbI.))̙3}g/o/ĦMގsGponr ے ;T#fV*#gu[1f+8…8Mz$x> 6ZpӰ6S*~O8tm:eYr;1#ZB)+yy~STP|vɓ͎G-K%5]w̷x!TK.p[} ^b" |i3_'BNB*hŮvmJHc=\TRIb=$ o}DD*ZC 1Giux1+={tRJ|rx7}`s˖-6sG`€SX,Fb9ƒ;@a {PnIJoYYM&t5nja!VQa~Wb7?C%|e^Ə7;V߇v?>MMӆlDD`p^VWzU&$%5Ln.0y8y@%{HkK``47b Y?jg›tK윂",FFP;!lR> CNa^ܑ$;j@wZa$ 4ЀѮIE--vq8qsS6 qhy9gCeٳlVSR bŬRWhWsP1%* >w낁Ƕ4bMD8z(|}|m`g I~_?b#L̼>ޢb2LGB|L`AZW6?}|piݕ$p5qqXN7rЅvZgR 999·7Dct̲LJmhUj5"Y*"L8XLMe4>>̒אnܒ@;`:Ð!(-Fyɱ8 ^֞3&`@R7̿19 UUlzo\Wқ@3I=_g +'xme1ҳg͆$:>޼[F|PÌA3PPq62:rDzNfSR$w&mkVC>3tc,"_ HR9VFaȿ  ^`Vp91Ϫkס>% O S$!mhͺz.% 1dSk XjK9v hcάfg%̭7j|=‘ĊNh3?̂#G+,)a΂ZZX0JzsjO8s2%-'{"<  SR6lEd"#u7åvg";9\J sL60ɓ(-e^)޽Y?ijg[vgߍ3UHGH/zdő#h׶cJ(}OwѣY~L`-{8d=B@ڠ]7=tQ/RץD}lio piݙ$H  6S! 2I$O7.AXJZ1%i>q"cJh T(سvtTZ AAsIB7}~s}ΙcY0I X0r$ <:IϞ̾WW[['G 8;+:+ sXBjW )Be{7^-7}%}CZ_ V,%\*x qql{n.P`mbJ%$P)0Π=>6 EZ9?%7lӥƾF@0Ɵy ohw'  ʖ8*߲3k3AkwWN!,p}sNqgd0Ӧ6Ӈi;zWF#Z>>cikZtc@ϜiܖQ!%Nĉwfn ЬrR(XF`^ix sD*,>hZo!]lY2vƳF 4y}fޏΑ5Zؑ"| aH/K$/¬Lʬmm'  mi宇7c?%ʄXjN<Ãml,7=.^d^w^ϝ aZL0+_}>;> qRiE$ɨmENP=2&-ZДdF('~j=ބYǭ;R!{ )b)S\t\hw' YBiLeCpsIB=zXL }^ x?fx_lA%%c:i̦l=eT[^[.BP||!vpš"x B,>ͺԛbj++oR<¥$0lj/ aa".κ@-&ڙmg綷 ^3g 5[3%iR;#* 3xaM]ٳo{7kOf(aߗ!2̡dፈQ]+'Mb|0hPfپG;t:q(jm6#Xq2 ;OMh>݌!͛ X \J̎0>} o RgI\Dn‘}4k.NڙɓٿJ#!3f紁ӠRѡHL,06 >.5nk׶ ATfs V_^,.s} !̡]}-4htkVY޻zATjP^^.u)q)PF0wƾ}̀Ϛe7!1+:l{-\Jl#&{Yv ؼ%X,pyfWj^!4}>40tR4Ϗ5a;:}[4 ƴPZkhFbxV[hKQZ#?D9&2yv D7ԖsJJ!}YʆJǪTe!>>7`Q"v[O YEVJFF~`3N"i] qUog={v^矉||z"jj2UyƉ OHQV;:bb+Wcffϳq+W!!DDӧ>H ݛl׶|mivC4J6o_k!Ky*q|!CY_^NپDm }g/+mϲ lRAoeتUt*ZQD$~ҡ_ٻ(0GJ :&b5pk6{9s-Z ̤=SGFbo޶w觞zLW^طazGk׮D!.\Ⱦ3n;s( ח̉…߻ױsS-%at:~.53<٪OMإnWS!?նڽNG4iԦMmDuu|# g> {:LK/IԄVD}fʹCHhc;j;(CA?H!U UXDiki!c]ґN?Ml۝wi.e@6НXlniiQVV+f-Z.]" c_{>ͼ/לhS5GD0E ֭NPm&L`/z57}vWQAD£;vsB*hg3~4ln>>A~חCCjW<&N4kyMd~+QT?=4#~aB*xqjjXϏ"FnJ4a96>)4Z '+J28~QBAt}=`3S>=*+f̦#f8Gi{t/%P̛1ӱP@۾oӛ{޴~\a!5?0~9/wJя)))oVV999sNz7=[ iȐ!l2:}4кu눈護ޢ,X`a:Iؕdggv)DQQ}@tIДbeңȑƿ9B*h}zj( ~2UyNcQD՝4\|2P1/чμa%.Fn|3?a KpI!#>r|Sd x[!JC[?lccuTP?H=ӖRAOnt|P(;7oLך"Wˇ/wpqk[Ew} . tW$j Ӎ6h`j6kDzz . ;+'q|5: ?>`ݮQ*VQAQ?C`8u-l; CSV*V]GKIKށ{n/ӝ_eț@wtl>}K6KF>6pz-EP(>ӊPPf`pE&=7ŌA3pt%6~>~/3yxo+tlޏǥ)s$:<ҋXll2dm=.3%זp0_=}3hմb%8F~6g\PJ 8e*AtN= y5Fzf[vOr~xf3ov1OT132_,gWŻ37ΚgZ ]474iHō7>Fhrی7¥U$ 9ݏ|CcǹypCہ'{ ;~^C;!sH {?V|"y *}H7Q"$!g;HMs6V^ږZ7݀y"0) ^P') 7:X,Q$MM=Xq=k^/\[@jiaY2\m?⍋HLJ¦9{* n^3bu/zP(9]*E$6J$Zm 6 &8[%th;طUӊuaH!ViSs"3͸5~׶^1p* ZNztA>?mlS~ 2J3pS|7) oenvB(K -rD ŠvB'{77r3\h9Iyx=gl:-}>??wV{n׿.x,sCƆ (U΍.?R U ^Q] U$<^kObϻ݆}Me^=8ԤՠRD΍D # qi$å-' = ! P^"޼M{NZ ]ִ Z B,>QA%ſy7!$#u7@oqJhmD]yЩu3ww>o-\zrW;> >﫨g46`ZZ xy@goK-' =~]mCS]DѣEV{v\lA|ttŀuĠ)\h9Iyx W݀cɡHJO*V ::w-GlW#X 0 s@IBën_iQ9vWrpziuG񟋡mҢ߳0I pNIyx W݀x$j/h-mWЉ}4VRW_#\hޓ-C2Ϋn@vяD#N9tkc2d]?|p\W[.=$' =~[7ѭw FATT>9Iyx Wڽ 蒒̜9'ĦMގ퓓Wy ݛÞ={-\аJ 111hll4҂۷#-- ;v: /7n1bhMH5ZlPok~_rssz &u[bܸqӟ"—_~5kOԄM6A26xb|g!exMW;~{Pnűn:[l[BB-$ޓc]7n4;WPxZP*-)x Wڽ@z&%%%XjٶŋcѢEECC fWSS(pesu:]׋"ں[SW;{ "7OOO_|wyƺ ,]pQ߿/aapM7`hb\U7v^u|i7x8sL7+2mҥK3f ,Xx܌/ׯdž pywAtt%t+AKEVcŊ Axx8^$ >|8hѢd 8s N8B9svDt֞DGGFrAl۶ Mw֮h(<|hkkC߾}SOaҤIݬ'l޼oNDDO<u"qdffҴih…rD3c ""Z|9]vOޝD#hZh-]˻Y8RRR,s"^̧NJsΥjkksv$""H]TTT`L߿U9fҤI4iZ[[1}tdffv$ITUUbe_|E /O?fEٲe ̙{rW7oܹsO̿klڴ ƍɓ' 0o@~@_~ݬH=zWEAUU@v爊6l@ss3֬Y]oذj`>Ǟ={p)h4tttݬBw*D`|Sbx)cccq/}ahooǐ!C0o<,]vnV*I(###[o@x)Ur(--C="nI22 ̯SBT={='ؒ"h_- W@*裏0vXlٲk׮22Rx)-###ZFFFK "h/E62222^leddd@x)Rd-###ZFFFK "h/NfFIENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/spherharm40.py0000644000175000017500000000105412253362407023406 0ustar georgeskgeorgesk# Real part of spherical harmonic Y_(4,0)(theta,phi) def Y(l,m): def g(theta,phi): R = abs(fp.re(fp.spherharm(l,m,theta,phi))) x = R*fp.cos(phi)*fp.sin(theta) y = R*fp.sin(phi)*fp.sin(theta) z = R*fp.cos(theta) return [x,y,z] return g fp.splot(Y(4,0), [0,fp.pi], [0,2*fp.pi], points=300) # fp.splot(Y(4,0), [0,fp.pi], [0,2*fp.pi], points=300) # fp.splot(Y(4,1), [0,fp.pi], [0,2*fp.pi], points=300) # fp.splot(Y(4,2), [0,fp.pi], [0,2*fp.pi], points=300) # fp.splot(Y(4,3), [0,fp.pi], [0,2*fp.pi], points=300)sympy-0.7.4.1/doc/src/modules/mpmath/plots/coulombg.py0000644000175000017500000000041312253362407023056 0ustar georgeskgeorgesk# Irregular Coulomb wave functions -- equivalent to figure 14.5 in A&S F1 = lambda x: coulombg(0,0,x) F2 = lambda x: coulombg(0,1,x) F3 = lambda x: coulombg(0,5,x) F4 = lambda x: coulombg(0,10,x) F5 = lambda x: coulombg(0,x/2,x) plot([F1,F2,F3,F4,F5], [0,30], [-2,2])sympy-0.7.4.1/doc/src/modules/mpmath/plots/ai_c.png0000644000175000017500000017100112253362407022300 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxM,v/yΩk@rw[^Al!w -̰@bČ_H׍dK!h&u`y23Zk;2NٴKTRTDfFFyr/kXc5Vŀ"kk,kk+Xc5z5X[+Xc5z5X[+Xc5z5X[+Xc5z5X[+Xc5z5X[+Xc5z5X[+Xc5z5X[+Xc5z5X[+Xc5z5X[+Xc5z5X[+Xc51qok_oOWZc5c*Ww~wK/?t:Ç?k|bUЯ//_qwwXcX~%?ݿw_U~7~=_a+mghvC{e]c.=^~@{do4,X?2=0ӷq%|yx,>j 1Y‡t0At4/vD9byeD٠lM"mlx\ l3z2_eDlzzC`Bh,Fz4AR8hG_8[fV^ol޵,=XR j#cL$++)CN|Qp4씜:l0AX,<((𐍇 K~C1 _ kcƘnSL|~ď9pʉ\3o8֎|f'_d_b?6߇?0N/0}0N 'dW3m2ن ƻldd ق@vFރ|8 aC؅Fߠt H]hޔ' qq81 I>uYN@_, f0$_ڪKC}])6Y .8j /inYu1_߫!>s?q8Wdlރm @'kAL}a4:9SWx˧P/&Hf={I?'~70!EI@~2goHN̆l2ܓ+f{> ?gM<@a6? hޡlQ!lۺb#@g; Sڃ-=`LfLSsigpk{JgKր(~ 2 @Q_PT=ú9nſ3Nsy%a e@. ß~ԐHtm3 0"(~gȸjuQ-Q_Z%m:x:fL2˖$=/>?C׾GȏF~wȻ G2c>ȣ.=$Ew?3lfWWc-<3t@ ! CZIֳ]hB]_=F6#!P. DfYlUAXeFƧAz<`3\yoezcov8RkoKNjst ]wĹy0sA“V5EN8xwYծJZ0Q'MzHg'{<҉%e=L 9771^lI;kPhcYx+p'F ̸3ZR7t U<=:!]PY@LCY_.bwf=j{hTQ7?$ *^>/M~N;o2#}i:[ks%_)nrFIeТͪa G p.!HGgi;# X'9klH4A8?M@ސjKdP݃0ٓ5?rO][1WWbtfh[6E1>6.KёԉKPK,3T}ڹM}Pi(Ќ6V@ǫEEIUA_@\N\cgo62u _9ptԙu '=÷5y]Q6c gE1L+Pf% H'쐚ij|Ngӣ1@=17yO/'88M<toȇ??cvCw!oiyɞ {܈qkk Ey_xպ)izGtBY2bVt!Ü`3z߫/7to_"/8`_np~A_\ 0vp*еtF=ùНY@z:;uPjԦ2h'-W]҄\TH!R XrZnȉ.K(j1Gm(wiy` &[;u??K溁NV`xbpPs6a] =)a:EP7!AB㕢Q4J9}x}~/[*+r!yZ砮oԓOr͘.(3yE.(eW݇QNbC뮤 ? !dS6m/;KZu(;b}d'7cj!e覐q؅[_ao7;j{X$9rb/*Z[H3ƽڹuTs44aSt´v i.P=Gob)szT F1u/cF\VԫB"=bv .5,K m;da5 v"MOΠ3ɨc,ֳݡC,nDC-Of/ţf+e LfXuϘ< zȷkảA 'H{//Nwe#dW|(Oj HNa0*c6V=kd,js\.~NH8ݰ}Sk5g-`$z30[ij=_6`q4{%ZQS4=XfmqY꠩rH[-39yf#" }a1:Naz13&-^vy`Ƙ tO8|~&Ye.Û&F<&;oܣ)d$LlUQc:6VOڨ](bLMe4wۣ]mG]#BZ\uyZ< o/ ѐ/|/< "۷z-: c_Pæd\X[iKWOHM(f IV d k«:&S'̫clQ jAcRR*N0=]{J.]tK KnFUQJ^I2!&m[1"ZɲZ&1wH{ȷollzk2W9>co2e6 }-ؕ`?ut(,LĤN jj3t=!eL69'] KMJc7>X e%~3vŰ^QlBzyD|+v˜\Fψd\EϷq2Zb>-5 -] .)琎^zLCi1CZ8,WԆny͵9$(‰9杉S^B(m@~Ğ?B & C~`w }ްc΄y)+~|Op(Eg&Ul1Ovn>3i *ZR(AVbͱ][K6MmzVQhk+Y|e~i?|>E]旛WN ^1^]meamvbj ٖj.]c@do 颤]lP,Mt.0#6qd∲C8M8"L:j'l;dg+6L2*-䏘|=A޲;j[ɮ-Axm2t6$P Ϧ#bĎL9Ї1u:$6ΡȢvO#q %_;O{Ƌwi9?k{yZm:ڥ8 ksH:CZËRBCyVu3[KXp.| pJFw딱VWb`]@ZX2F&%Yx8 GAcDJNFMqygbM(#fGTOdL&tfUI^7*|w.5 (%KhXCڗ,/G'AK{5^#-t-ŢFrUJ r!铑3+ŻZϣQ*K U%=ZJή#+հAzd!ɕ5A,G&Ll&b]}3W7mMp,:qUɽGvX~r;, s 7I)la cgY1sz, ZcdJ$ vuSsn}ZPdR.1> Ke;jFj{8fQ@`Srb¶{̷ 6*\u"W{ Užxyu޲xnJ kXHTΘ6-; ]pzluԺvVj͐|)Iib^<_*k6w6:x ΢~@o]IXv췞> ȍďvbp]&Y e𜄻p̸&& =f36 d;!&Gߐ+zml&fM9r< 2*VAP]8h3T1mMjݱpКF7ܥ/w7V@a@B.s% 2+)׻8Z/e=7ы#S,io7-ڿu]XvFEw]T,kUtC.vFWF- MY{~oݡFVBI7UD@A}`S():3$dA6 IA޾(G;pmsPy;od Ͱlэ1|1G:EF cGo;@s*(Pʓ)ϪU9ԣ5u#FUKP[MxǸdLu{?kUX W{_ԟC.vYk_OQm\ߣc*4h@]r>vLi,dN-rW|%+tJ 27 v:!Ǫ! j:7VG(j(ajlep#>seJՈnCHdf]hWz2d! v›?w ;A72dGÉ1:+a=f[z\1HH(j20Qr CJQ/R|,~QթK9A70Y=kJrv.}c+/ſ?OOݻhl/ >waqw&{1[>gL7px ݢwu%n  9RT-bu\.0[UPwF+i#1S EҰӁֲY` 8kL3+Kb쯐!AݪiPLȽen&&Y/ o#HucQxBPB#z۰gRTkGʨʔܕNCug)u Eo5Ky[HPG)mf*_Я[3??ŽUӯsq))O&rm:/3)N[9r\Eg3B ?l^3R@F>4/gB_;v:4œ,Tg2UaݑMXБͭzBȑen|G2M]ʼ2(cک:vj 3pD$T.8nQ66MQo#2aIt\.0RMxVJt&WUUGx8kkx=>|G#~?? Dxف0]ia ؟uZ[WjVG)Q՞ЖϺ]tɥ˘e]YGH_" IDATlpA5z1v::G< ]i^SǷ[d{G gXgb?ޘ2ȼ'>o nAz>vu&g¬ն` t éBY(ud)6~_;/Jm|V4nmEs*gm%tЯ7&?r?_/7άҠPv>^&۪rVHΊ7 mջh%9%܇~==G[*S&悟ŏwEM;ȱLH :*˦rCK; 'S rU'ȷ#r{G"!օG~6?̭eLx+{7onq!TurQ٢ҡ6mDHGu Pi„q2W" 8^Xk0u ꦱVŗYM~%=V"erTrh&jn$pϗg$EQK';=JEE)cotΣ 5ITG,ѩq%۪Mb4b>ҡO  dkz|΁7dޘNwbQYoԻv> y=݄fS^zFʈPIL;`aY QC[|i+?xiM{E`/T5|wc'RSsX9,nkcШi|\IX2tY+p39^+-C8Wpaln1AojqTҢi|`3HZv~#gPξdc:< rk9oȾGT ū3q'Qwވ& ]qp16[u(1Oz:qH7*ڪ\3Ĭ'] so%=vUUjS3.8S̰.mښ"JEa׀(^-ݪpnA`\}U3 ya{(rTJhۯSyB7~6R>gVr-9w+㠤MQҳf0)6=Q'^YGJ~c ے!SLZ`}0r(f$~ 2# };3cl܉q'&7@sZc4dQς>? V7lPqtݜQfmnʰ[aQKsbxp&چ"V@pPLmV `*q٢I]xփea, 5_UўGhnm-Y\S[)̳:FM5W@IX7VP]U[OZz 0Ms:s$1[r*[Һ#IW Msd hS('焼EmȶbC]{|ΣϮob lO*ZF H3Z>vt:zJ':FRiu2jH)1'ۋIյX̬ o/]𫺌Ǎx>wʖQ(p{W->vYb|p9s\\Mn.~cݤ;SB=wfPѵBhv(P|yrYE{:eCoE^9Tu(N2; ӗjwiO-x\n d"^rs۴dCGȃ(b܈E}< Po#Mz޼ݹtBwmRrURu_Vl3%@]!ߍ5_]Kgs=i+XR%s&x[p[f8-KEҬ[̅dme[a1I]/sSkW?&#?x]!o6X!X9k3Ȟ|`{U4EEkSڹ~UJ`[xiГaSER(yF wRͣCum/l/NW OuB* -3i[%b *U<3i:ĺ]IB;9مա;(-\|jwXɛ^*X2 eH 8TN&Nы\Չaȃqྴ)6FxdZla[.i>mEg} GƘ1y(/smmHrȽ`hႋ(nw!&lbWIΠ;T83'=zgjs40|ҙuF癡\!mcoS ^Y.m 0+XqF^o(϶9mLl/dqksKkjsU8tTDv;0m?ϝ+cizaց&zသg&\q]:4 hWl×3:G9QCpRuC@9oڣ؃VeÐX(X (yCZ+{hlȠH1`z(&=ҫЫ2IRfi$VH<{%A 9ۣ6ֺg8]$Ҙ[E]^ ;9^,7RNȊ=m*}џ㞇 Fq,ddo 4R|7-|fCfDVTWlPUj"o2\+^fxSڋnGz9r- gQ,& ƠhNp| 8*ܨC:T|Ƞ6#6_]2l t#IO,ӫzu:-5 boV u%x p/u~%^}3mJ5c E;o`٥#gb&BEl%l";+*K,V:_:$HEK-?iScuՑ,yJuYqqj(F4m%NF pT_tt"Nu~|/^@>_3FA|]̲)$J~g#]}`3B& ف'4ә~ngZv9)'EGp rab.ǟXl;bޛӈ9Uu˱ØwqlWb_5w4Pnt9F-{ϮUqC*(5J}Y3l>.M] Pѽ$ :Gcur?Z&: }&/TωC:(ZnhQJ]u3vz 5eM|\ w[6g#wڙY>ț-ޔ$VJPW[ H+ j4J蠶uŎNd&}sm3MSKxfvm@X%U&as,-,V@(݊2nvs=7ֻo""ruY"lFx@3j"g>tSs_Xik:.>9ilM-p3jSSPS 1f>&:ka\ENXp5Iy D'\\1j`>f4a/^A|R|U|\ 0`.Qӻ׎BN'EL.g/mX۰us[t4KjEYWH7B#.:/T+?=lc[hpr6W0oo.R@ϖH|wkf"jFOvH}f}2Li_JAy]JK~U#|. nЮ0v('N&Ucg+Rt$VHG ;7 cQ︵kc'1 cΔYI8>𡗣Q"=-76hB :>|P"YLH&VEsk j{a)6Եˑ}X1gsCVEifO:76-\Ec9`ksZ ݦܵ֨HW;~j'\9/Z*/|κP֨)Y VǠPeXG7J@z~uMLLҹHFMuQ1+K~u1/j-[ncCG4 -&aN'azT/Wl+as16 myΜi@h lU46a3- eas=*XeGXnigzyD0 fVꔧy_{ =VA;]ZzN>T$FE/5 S⠨bu4puN“h*8SMKvHj{5]`}I|WӼ!60/=w}DE {;D-Jbn vA7PiX(V qArP+E(Vvz"mn ]>~A /tZrt+?5 jjF-)Eb?"VE۬63g2i+m:h94-SdsV[LuYU5mꌫ]鎤dMdIA$1kG;)I2(is5-8!p..Zd7Pm$Oͣ =G[LQn" ܠ\3-! /퉂u֔,<ץe4;,nIT:N}yaufQp%ih=eN.V*+_Z mB8+mq8:M'X'3BqFaz#j= Ah ++F@GPiND>VP?r4)Sl#X{%ytVGVRiS+ z.ntwZuY>Պ \@5^_QX%sտ//wߙ_"cޯiݑTvl. 5J.*'-I3XCzkt34 U h/ky"R;2jW<6z9f}4,lu'Ħ:Na.لn¶R-X$:e 9{,&2t@r5bWvHlچ[X6r@9/S6bC; eikԉHÈfPQjZ jºn3镸4_E5#7S?S|_xnu]W8LK8U- XP}o?_bWQCϲ]DT"0=arbs:Ѱ9Ϋ !ҭf?g+6ջN0 :6)2pҗ>qhHܱn7 VC?"'c몠](-ʖ n-m>/# ec )%^;m0KpuswžzrsێN1(]:BXJo6OOOo{_ÇW ?Y2Qbo E] ]xyPfN}椺ȉ0bƍڏtrWfT. =tF?^=7LEEPڱdax %Q8A\#KG" vs*ݹ8̠e}J( [/ ZEGkz''Gmi;m06$hq `o8TXGII|\!Oi%na';TڶDgmVцΈǵE͝~l~%?+?s?-o0Ph K6GY;wa]ZCy;gy]X>Qы3@F7iQdt!zU(p.s3uڱuylFfrY5U I#Գ+SZPPG8+e}(7{e)]6(:@ͣU[E<< ψ>袠#.;ldD_^2q =aL"B~z[*v9_n-ݲ̵t? 诌__~闚gN:KH[M] f^CEU.IVrA4.۬+2a˳< ŋ'‹eb ;W}؆т̻w06v5sǡlIڨhȋ8 el嘄7[0nĕ`Qj}'vP?(3h]m\h['.p 듩Cc2_g3Dh eSBƓֳsXV Q IDAT3X:^G9%#WbhŬ+Yq^hF1`V-ϽT}.#ERĞюӨLMGt# ftF^.䦸:2itڳѮQ^~F` eΝ-Л 2^SK$-E_G\־h} ʘ&ٳ}*sHoc3tk }m9J7mU'ѳ d+'KBY6Gia;/@]K“^*b!}e830pn|}Gd*LE PgNaq5u@:k.Qy seP9XJ1t(( AuiV&qs'cUQ[ Z>3'Mxad˵ZyTݝgIoH̞#YtZޑOG*ƦYB/\hĎn<Jvc}"gPe⹲6v\!b歭 KyVÕ EvDi_SzVf2՞$=&qyss)v hX;r'VU_Jhnwd9rSXMyPAkd\}̞PayEMoܛG5._D \1J|m,Ӳ]4/͟kot|{/cuП <>k]))6Ek"=sf‹k3`XTtC ]ESZAm--CϽ8aس-PEEPo8(0@]U@ngX5zV?<Rc:2aX]iaq%;MՋޅj>P+N~' !M5 Ll၏ܨpƓOZZ,fl-tur}v4xNGL?%F*ƌ|T7>cs-!ѹ.-MB dD2dGü:iɸ}//0++![ckUUfcc>"2֪m9bEdddfdVs1 #VBtڞJ(2X7-6#d4eH E%E?q͐y˓DIGgT=_xvji4փE^vhv"mH03cBK D KА)yGXԳ/:+yZѭ}N73!m*zv=7q'EGԢm#&؟rT1i`7.>P^c_Y 6ΔFJxmJ;J<@&Pud[(.L*XI!k_gJz|he,w'{:>zs3_FZYwGRє]lb\߁Huϼe-mLhoceN4:ľX:6@ZI*Ó']ȬO6οiaqh1q%1a*:)4;IEldqTGn-V9sQSU|Cܳ" n&^ WiʡRΝ4G`oGtcN5+HQ]-va)U =W wjYZ⬮u^]JWw;McR6ʃơh*zV+;'6݀QYhQ`:d-:fGFʀ^j^tW Qc1$v3Wq0E޳6ny$jJ5y~RUa @іi-L"L*噖br=c>ݰCV7ymдyڍFT֫uz1PÈ#:J妚/.[J%mpf:r!0撀SK,8=S;-bu!]M :u☇ۥ]lqLx-Z(S@_OMEۙn1%w$I[e(HyF,T/K5[ԋ%C{@hMlXO+6Qfh-C8appM:=2b¦^[;<+ g#\LC zF kfZ+E:j i^+bemD:5#W/V+/:%XcK-e2kX'c?Ev$@8c:y YILfsd/5иf}YOKZC h#R%f-6S;Ťch8T7j#8:죣å.N8 M![o&1z9rnl3@{ *0db z=p9#x[&z`MФPXx ZuͲ ME+0E%+As> z]6/UUDx]b\bW$ԏ1@ځqw6+huRӓeekڳcKga*i(zNIM],E ǫ-RY}Nm3K;M5Gu#ETEqhfhkJ$U4֬'@%TG/3H FzIsjp rgs@6ҩ(k6TtG*zڏіc Vo䐶XJEWJrco })+Y, ^z dn\EsPW';Ƹ={TYieuh:\%8NHg3ߺ,);}XIicn ۬zbݹY;MQ̙zTkH D:N5.H3/:BG;ƃ(i4鬟1_0{; i9 LX=ܮ'KdiJsTv92a%fC,~:~<\;{u5튺/zPGt7MD/6+hBajz q.ְN۞lJ\]8&f᧴` yC͋ HGdaGDW%Q&j\<z^: WiDj7Wu: -|iq3X/M9˃2ȇ-)DCDcJFqUm4zAT~tKg/EO3WK9gKIȬ.E7,g%]2"A55O~3*ClJ$2jW15&sP LbʮCב"F,TuW KEYhѦs2ꗹn^y]ǘGN^kd2x3]UuջLׅ4).#GuL1 185{M]5=zxڟ 1G'SsT)2Gq*Jz[M )ys`f]zf{ʕtA-=PbNSqQ%퀜t֋SȬ2f)sZI)&*Wnig@}c[V~7~hxnIRDc!$Ցv; ?>@SL*2hr Qa/pQV?t.cEU٣=#M=ujCvBGĔny4a7U%\v4N1r}釈CDG+h.36Mr^ب:uO]Isgo[(u0e~T 蝲#֤cn9Uqs*%ع}K{;;Nwe@TH:6!%6Gr N>8h)=Z?@NZlDt5q_VƋUԊͱcm h$*ZkX7 +ږÆ6q~tsTj4@g*^')2e#yҚV5ttqQhPBܗFRu8 |. .kfW}PG.pV׋/ E`Pmw3Q>Bxw;)#??G5:+1og֌nW :'Iɑg#0pm]ϲ1rAt}3=9Vum56Ѕe  zӿ!bs6^֊<5#)2Ĥ7ZYZ;55omu"c"lW*!Vw8 c 2yD` 0W&||ur7Sϵھm'^ ;^_G׽?odPƲ%+ G^PQG9Nq]#A̭kW ;f[+Fے]Yʎw }96;eM : 8s0/t*Qbf ᯒQlqetF1"A#Qi8/~ yyNY!wK8/ɝZmO}kG?ʟɟw__xU)|Zu ChjE4%*20gi(T07סvIM>-vmwm&,V١\5yi9 u+U*S& ^G 6^c(~QͿOe<N#!MMR=hͼ6qHy-є} g;TvKQ"t{g]B<3om}{y{{^3iu``eq.dhb'U1r` '5@٤: ]0D'BBuZ"ǑvuC.tdW)C5Qz)vs0W*vWѕQ\? M.DMJHRd>fZ Ϋ"9D@Ev%ؤ3R:sTGk\T^TlCc*Ke\]x[jwާ* 3"#=6U$T-z')c1$J"9B=lA +9>&EVWiU$ %;cP ȹ킴Lt]?:\ƻ]+hu@hJ#dDQ95c_DpC뭙&!jx>aj[u&o%# Iu9+915}޾gLݗ΀gZ{Ũj}Ufp4KAiVcxU֙ gA'ci-qu=ӕul-o*P]l`6;OJ U҉Yƪ4jwN "HOhm[痍<ԏ/>Hi=י)aMEEu@ZT4L#g{#Q'UlYe:{cEiSDF/jJzOdM!zPTWSH9StJȤ [g)0 U(6%O@yTr߹΀g:1hqȐ;^AM?&WuBhgfѧ'*v#=NRd =+/!ST, MhWlGr$Hm%fD9:U*Zcb4ذv%̃vHz.ϧה;ӝ`=FeKw˝< IDATqPQe: 1؛". T8Ry~ O:oOalgp {6K>PQ@GuZb(?S'lX#MvDꝲє mC%|%8rϘ;scs`nVUa'o"#Q`͚R4)QH:j0c. #ɾ,Ev榋o_\NXe$7AyyH2}Sp}NWI;f/̲ qi%~.WMW Z]byG 9XAڞ%Խc".}=#`J(v e(ϔ\cwjYUdshS2YNyIG"*hW֥͑XPV깂szɇRdV4Y b𬶵Z>Ct4e}b%@Yb)̋8hxaLvZ -&Uaio~Uk5\ <.aQh!# +mh?SQV)TP d=.El,N. ;#ѩ>8~٩ w{9_ {+c؉֠̚A ML zVY;9zYIPL@ߒdMuqQP_,JEpiIT)*:IE{">&stYuZb&|GGr ֑V$@%4{Q *OT.Bs%].qjf`uxנXQ$姳ëDΟ'} Oc>n}rGT20t&4mКNB;0 Hm8x,tBR_\}(RqPd.8ۉ@Ӓg&Vjo1UzwCo뤺ΰMYKiVFvȤk]Eh xuN`Y엾|;(IOF. )]t ZeEʳۿJlQk*?A?)4AMaVIAx *:7?@.H?(sXd9Eȵ$*L?m=Bg4 #ԩ%#[owFЦ.+b;u_ڥ XHM35->*:Х @*&Gѫ*~:*%y jD_6#eĒ_FlZ9[usC"w{uK>ͷyv __\]]}-oy7Qa줚 HDLodD7$HJu!A XbA3>oaX8lXͼ ge0 NLMұmDkՎ^{w)F . {ATAZܗZE ^ k5)HڨQ˧Zc@-UZ,9-)Fg`. zٱG%R=7]Q|ztNSbC͆O|~dRA@Nء\säE/= [@*P6 Qrhv ==:g嚑YI' ^'C S[4JΩ)`+55{=pfu떊xF;)̑Ցw uPl;XJ.2s*_`>YuvtmN9Oc$ iJx i>kҽBZ>ʝ^OX^[ goX@ݎv ;>|'߫cSmpVYڹLAV 8,'jlAW 1htZ v ҩԩƞ WD0hi +.@+HlhIf#x{k }!jsDWAo@w+Pe/5I*}hh* ']ѓQ,yy>Ԫ.mKPx<8xrz\:Vo uT`-}uiX@|w}gƏȏ[[o7qqg> <[dPnj\ #GvmNFj/i fM0+ktVXD/o%0u6X ; bo κ0]0L+n⁃*A1%h;Ho ҹ%M=6;EC-B3vuTׄv<ӚtSPkS݀'>[˽m~w;]?W/S=>M{qRˆ ݲ\3/2`&AƼ(8x %7{h  !|n Լ\L'9IWL0ifv@-z6 j`EtَW7 O=hȨ _櫂|_j4 }웢k%헡y+h ^{"WLĆAL4D]q -\PQ)ԠC>;z ä4_yIp<$FL+*u˜x~ޖʮ$rhB`M]-N> heݳ,G!߶}^>>^xя~O~o~3a|~_?u%܀ytRt_'Ԫ'm-VZq'nѼun}iݶOϲ{wF=^>O}#?𖷼~oFN#hsUD"*{4\aj@ x[5>& :ЯaE׿Mo3 ??g>7?mExk绚 k=fD֊/y.zr/yExy@ـ0طN+Rk7įI_ Dv< 2Q J 6.HY l.`Kh7:9( ( r?XyB="=„էN=)7p&BtuX'ؽǿ?Vg39zh<>ڧ;-ןzSħtn`@ w?ϛfև?Ş | =fe(9_zW`{E'>^{qqȊ6<[Z&A7<N7voJy@dۼMEf  [P 6E/ |گ5h"thh$xˆH  mx < 4_f.khB@B$-;A!K@ip^:@? Q-.JoiL nh@k_F:@6 H `Hc z zGS&Z&BأX0%!&]<*vp\0EiT _ B!@c'@#Z Bx^y`zv !\Yq +EJM;'A'jAEYטFH~=bze%(iY|priiAA]Sj8>|oϚ\,~o\ϗ/)/.~fgCK+[Z`MK!%b#LMoRwXL)Y&EZэ>zF' bny!@\ j}rbYݯh#)jZ.z|nO~=?o=4d㠝h6!F 6V?8AȔ+իQYƎIMI.*+Q$] ..\ӯOn/@\ ] L \ QQ!!{}e-G, 4VddsV-Rנ[0n XQsT$kR6$jL90H/jkVD0J g ZpW:AZE3Az lGG)j- 莓0FeSRqG,UCr-nuH6q׮SgNFgP^-4ByХ0ӟ-4Yy#\WL3*OTV rmWҚK+K o:TD崔P.:%?AխYE$o~6}Y;WvqK~- pfmA&T*U{E{HZI:'tI&XVetorzΆON - b9)#/`{h~y ]i[+B*Ӛ]a ⋢>J56,DMh6CtV)/YI,%~w.B]2 SИO߬m>qwEegZ (Cz 0v}k(s cSVr@xY2 h|Z'W,΀g A8Z*AͪTMD^piJP=6+XO$L1LQ+ ;Sk`HڟV(s*p#hFjщCZ⨿ YSϚ=ee,2e qcvY)IW4Z ][( ڀ{@\M5GXMbR33^FߕJTɰ]Z',܀3B:݈;=)hqjj 'Z-F12RUsVҝ rrygqhy?:NdJlWSnqq\WKMhy\ZYq10[fL^6bSpHY!NC!Fhj} 5OjnYY@ǨR< fy6_9W$gț n}-gWG;i$Yʄqf.k IDATGCW1hd]j%7Cvz_mv0 kAzQ&Eh>˅aEԽZ8*۴xt*ff*45CPQ]U/YmlZ2Z:'-IKZn1~2N% |Ɇ*iXL Y1W4f {̃>??qNNH0(kDY_( ֊&{VH?<m0_{ڷ1>I1'TCĒ>+tltbi$j! y zY*v.KKS]n!nJaR"f=4&I=,r\)hm< Lo?uN|nMj[ K% wjFV;wA){lfo1$k`NJy G˹+:K&J_{c %|SjB)Qm_C{26OnZwnn!xGt8<zºάR0h![BH~%;K- nѦUV-R<5H5STXN sVj @ZКtl&֡".A/./⠶(M h iaa98 TQ ,s,(feh@8yfU1qT$5Ϛr4q]yܫ}܃nQDEԺpmYRQTGbc ֦,*Y%<w$4k)Is=ȩ]<.:]4:Tp Y$VojC,*lb'ݭ KC'66nxQYЋ\zKBa+̸pܨڗw=AкCF& ,I@rEeca'(M5Qُ\=ɗ'WOYodsGB[qealMdR%bޱ<%JJU!NN9Yu٬X1%4ZgUt9ZZ:ZZ<9){iaSgQt̠it`W`nA2…XD…¥+S7(7 pb*zf{쓊v> }THPnB Ic`>8uoUUs7Nу3 Kՙ=zertig@߳8GNcΓT(az' (Iz2\ӺKxȚz\)в^4OݲFd.,e@m{'@7Υ>^ ahq`kpEhj VR+g.y [኉\S`0,Bq0W*E$xR=;ER͊sRS?2 z\-^[]̼s|T.)o@fx*w@WP;D?ϯ^0]ve=;]bsJ,tlGlMKNNL1/ufsb IkBVeߔ8,6s*Z棄!Y:T#.!fo\DWe*(Wno||av+3dy `-z="y t7NqeMmn\pz SۜsKU8:[}x|BG׿i3 RԔ[ieox㰰8(sYP K1TZkgH!vYAw{15 8=)xMB%$]AKpѠj[ 68pF~"nD#zѕTi,y FQ|蘿Rqom~rf=_Lb)y&itLsiwr('[V򕯜|,l$ZJJeq4؛IJ!viKrݺ6ti DЅAVN9Am.󊨝Ep9Eq·'rRQc{{)eC17%麫~1 ]+:z0@p@[MŃPϴA h62F!v Fga⟥/HR1QުgWOms4[v:z]Xd5spautPa-ك^R4!QY[J įHXQ.X^"y5fd6lQx3Èݟ!{6#=&pyJ<񗄼ȇ0&X,hȐ,z޺zmƞ yس_SbʒT$f92,J259d;`s(۟)g9Eel>oOfiLnmboׁ{|>.14=zfM;:PtY 5NgQ]Qyf0]:\@(ItYԼa8=pw9{~:C4X;!{6QleNP:לovCS70^S ?0gΰ܃"SU¾HސxRzU;[-˾L) 9SX7/.oRZRj veЧZY LEYd0 %ԕAHu-&HtCʢKʹ.6 3s,'csJݬ.Jͩzѧ;PGDV xՠ\!틁 أ |gpv'>![#='ڌ+K\Y6O)Fg[f&` ᄲg䅓`J 8'v$(C'kxlt{ _@R.v,@.0M_ވbnQIo5/.o*ܷxLKN񆅜OdzֱU=W{]zפC?.1uOtb9;,~snRs#@bpY{w^Ϛz>vVhX %Yh !ilw` 6UΞrS!msx7=p/؝ns8^AkBAXJba(8IQ{&k@zʹѓNE]Ρ,ꚘҹuF>>r~N%=m.|X:@ti섘;"aY.P]iӒ~H],:Y H2 ];io񵽥xb\' Zme/0yZ,VRڛiAZGgoHgoMt( еk7V3H5\K ocXH=4$X:s\s@qs,Y`kW,9Z;q۞ = 1 n2ܘpcFf̨LvG;crږ,hggҰ'eC=~3s0 p^O+4|`>{ d,6&g&v7p{=Q2Ya]=xmB{-n\3ƟɟOO}{R&Nl&Ed6,JX68 onv*xy_:^`}$9%&u@ lR:hpvuªE_0$dI3 )OJ2 |"')Wp#0yӋ d䪞*o=ɶ`w$#damLE_+EHk5BЅΚ!^v'd$wvj>фY8U"qPBI{ͽ\;'Ax/i*{֐~6.G~z3BEAPJvou~^-dQlUE{# H7d 1m*ԤvBr)+s)Ylj޽gycm{#,23O@hp^tgYby.\v7s /5sPi$zFt ;`RE;{amF:8 Hy;Jzm@ >EW''5ܫgtc:LK2"T($^[Jt,A7b4C~-8b6/& qS6/fwԿ wG39;S4T(pB4o;uk!x6#oRwC *rUL+śaC R>WQ%DaB,Bv ^@^ ^a| 0b {X%a5nk\‘{0Gzv0;}vH=yî ݀m2rQ+!/ ]؋x4O6YxRʹA2yme͡,BJ)B>`%Hz v酢M#+c@wwoT{9[^9?6Kg]3C7.ʊb7zk7W{ js>0OgֆtMk5BLeX]'8rçO[!,]=KDpX(CB^!`d'á&,/H*\ىb^&̾^nŖ:?oA}p]COpJ ֆ$R攒II3sk.W͒|¨4:0]әj 0s|j.w磟+eq"!Rr`\rmI2!5Iri&4*ү(&˧iFhz.:F:y PNjEOjVl.:\9FŮX"pyD@v+Up4b5V2Ó{^)-}gnA]9 _|^+C3771t#@4 G^llUfQdBJ 4>OYnq=K=jXGZ=;42WKٚz/e4uBd )Գ[!w,C5{hK\S3]ɞ0O%s-k'S,$X \5[yG=7X'󭁺B+ @(sNqZ8wr&8E9zSo5u*%z &&PC tP+X@Zs9TɦF桚US6G QA> 1VV*l9G'̫%p@xd3`5C}}`6sWȟV.wkc0 9Y 4<ݦ2Rj}~]oW=ݭ2N[ys}]]:܏U\t;{Ø7V8yВaɐ8xr)UL%%bsgŚLZqgDYf. (k-w&w5#I ԲDt=^g7?zfv *'!: 2fp[ڰGVYG_TU΄{uy_sf?ΰ]#y%6-sײYHaab5/t_A$=3a "#H?m0# lkib.sW+_9 ȐX 5=u*uv:]ًY--޹y~{CJ3)r_$ǥezx3@ʹU=cnbEވe %b 8檖e#٨i-r? sv=Q p#,w5`C6ֆY>(kcRŌϣ:η&ܩqf:g :9/@79O{H3L4C9(Fi47Ƕ瑢cKjcIy|': I'qzحê]B/KĒb_UkI S#ON[bs \r QYf ټ)#ˆ'9l1z94s:3JFyMa`gq./8 L+$rMα4w0 ^N Be0'~k0r~O2C(iSk#\xu+㳋dsΏ|\ͣA|R5=R<+B3AB+;]S=0RU5I B=gX:m 2 [ &dނש j9@dYX :Ce9r T3,;0;yxBxxBamX~وv58~ilqk-/b|/oxu5 gO5,i+5'x<" {JK^W-퐃O`މV03C\Q'm[WPqL1K 2os+-sr.+U~bwI\,tQ=9-u* bQZÀ4`ɢ`|\4 ΓM5K(hEQ\X9ORXi#s{{F3쐶'Da [{~^gֆ1^/sA;3z,gS>)ro.n@o</y%+W8ǎ<)lQ1t4C'fc"kb?C$,(T[īHO56RT)O˔.7٦/+}>.1Q p$l" bgwK̶9WϜ:QU(QOTs]>GRA]s  4s 0[s1%1B=9"6,ao@\/R$kp8`^! mq,{[e(K'img @Q+jnJ_{ (V{E>A6y"MEDZ+ʙ\g6ċY48*PbH0ql6G|-ʹn ļnk,5^0/́TH ^u[ nxLL6>c|FqKY6i|pȰqCJJ ?!=$dDŽ<-s <䵹I׷nʑQ`{TM=]& AD<#(r. s(AJC9s2|\WT;na\"tʹnr6OR^Т$.rj"qV' :@f{!d{# sTF'vXk۹r{CJRQtpL-2JK(:b1/S6..o-tVYL[?<ډq]b;0<+wkۚ)R̙R|,j4MhgmHs';~k>؜Ҽ!'AOF@YN G'yxRJW +i ү;l1"!KW]cI 1ƵYPnE&Wd(5݈}[b{؎ȓGk,Be48y`rb1v$vhk&69"FRH:b%|11(O4 UIi N..8:֥Nm L޹( )zc\7QNdڄ+luԳDζZz.α͠rDw`>G N'Hy2x܇4{#sS }sڎ;,̰Z^:zbtKck:p8c% b0(?A 7 U|ȭbwϙQY5%( Kg91B1[;eO=#fQEF$+ K#e' Gf+; ߝzno&DϏs\X&݄+'6ǔEIw9C IDATMO~3j9Eoy&US!Vi-&:ڢXd!ڠ(s!vf3L3 ku/_ 3n 1BgWΙ#pVsfk [ep6XDž(F$@,x%&+mҰkLnPA#}D'7ؓ|:" 2 _ηE%yT滁*kS(KK,$3GV0(9 sĎ$;$ޔsS;d.YrGlV ZnX߽I2e  TmMZNOgHnozb/v\ճk#~g=7ԳpfbV[\3R.3D50\%B5PsyQ^Z]9 xO\!Ү̴9m {7c+HrE *7pV~^b l +Yϊ^(W:Će Dd60p\|e eIfƑr{L#jd!/d{E)infP:d9t*ܬ];݈cql X a _OL d66pU PqoܓHg27i`^:!KlѾPH;L8'm 4^ KÚUEAGftW$k/.!?xV "ՇMgufoqL|J,Z۷n:J˗ ]\n0HmX[ 酤Rt;ϒGn4 7|Thʍ(768%33(3ܡv =eGOCc4btaCV°.Dh 5dZP35 Ťr]ZMJQ؛iVf% ___[[9;\*=djuzt)}bjOL 8Դwv\z"JDeU%cՒZRC8ySy(pޙ׶{2;Qd[b,! dm\Eּaw/ ga~k:y>a~O >DG/|OQE lD^[hcKйx#!!K>ϧBJ.wRu48pL UTL9W]uR;,W8e~sɿ˿ۿ[o ~76ޝf 3߷RbBM-s,F4樂ߧC b!p-]=KEMLUUݢ@9+fb*I ^f A4qaKl dY)\e ݬ!,bm8K^ µ7&|0 ͸}|B*vrK{4m{tcKq%y9(<'^xIk2eC g6dDr@0 i0.!)ՒK!\L++*ȗ/_>?:ȝcqNp%7Wͭs^i޽Sϒjpl5.m_|akKikT15$G,"ϫacIQ(yO+r 8X,0 Y`k,m\b/l{0^{; dlPF>@LO *ߡ,3-3zm5 Fkc\ yeif"=O5_р%qH0Z&i)ǜZȜҠ|.ھWеU+e?N 2|\?7U,JzRh#5<[ 8wLW;OR.%qgp`Ҝ<9F}C=Δ(C9W,P(CH^9Af$c, +u9,- ȳGՑڈey#WF^i&xQa Bt90_ɸ~\h܌-ILطv XuvOM|\|hl+Fsgm/$B="L=R g␬g8(- 3eҤa1'"srڲb YpgB= W8}<ےI>~A7ưtc+8<`> jl?)<+jb1Ͱ 0'Wϑ`.#A@(GJ48}\B+-zA 4WG=ճ;8oXd}v%)RfL;(iKZwYL}Ml^kfKa5/i26hY0f8xҧhTkhAa, {{j<UksQq8qe#3{,2|w}naLh p> zc9ww ppކ1q 0."e`WΒ2r>݈ 9gt{ @w XT2ʸIP}x˿6dhwMc+ s\0eW}yS_IrW9:eciF]_R %Գ+KvHLҡ5G뛀s4c$՚,E<pH3%`yS mڎnA0;_dG~2?o1X> :|p9! [&#O#T!m[Ƨ|5pm^!Q]aBtsygN <0VL'Wz(klhy * [UMq7>?OJ7 NhLc%H%/6{I^yn/ae%W+=`[WQږ 73a8/)?x |w4 |VV}3kjqi9#Eί%-ƒ^Zݽ{f8/ML"1 8#&\5(f%$xA`Qr9L{0۞Y'z{wRjh `踅th+B:`%tUfzŒSr^R9KUmS-WIŇA1#m1xݠ7 eaC·<"nW%8b(y44N_&SJ$WW0Requ1, `g38rO݊ZN.{c#̬f>A=sH{gt.El26'j^vg?VvvX>x5$5U앮!%䙺1KznЄnAWSr-@4u@-Nr`ڱtUs̺똵0 NYt²ڮB6Q;2X`Ha^}ʆC:4H`bM*8/4<\獷/bqigpm,aBO@i= $ׄϤ6xʸho6'ȡ=@ᤴ0fy?XuCd,k}JzYqr^M{#$G9L9ZF7 Fo*HZ6}Ӛj3)4j$g\Dh4-:!i d GaY$b^1Hu0EkX[-M n8^9GuG>ny#FV"]7H۠mO5D}5TDW0raOI/ S%XS2OiC.?]tV£$WiH *ZGbc@Ρ oviRJΩ@)tJĜekIy'a?+Ȃ1{3BH $Β ' JahK]$[v&g"!SܫgA ' H D!r#tFR =)k iw j(5U*3rV]Q]Ct 5mGX'"dRZ2"q̣̪cV,5bF`V؊&dbP; j=9K[XwR]rVЍ0؞Wu5T10TB3HW0jP# b#2 fXJg\˓2$( Uaɐ .UAFFZΎʽ98#gk g!YTkOg#9\PA/{#B=$4U1L͑ qؾr^jo8xb/}P uk:^Wbmϓm#t:.T]vd1x&i)ue=r,ly"1KČ*DKشA=)Zd I"tCa1F{蒃eDCmZ*}G9# Ə (Nz$I&l!HjلЈVɑsJ.W[n%Ȩк!"),gNsdt)YL93lf0`QpjF\L &XCͥP < Fa KH1ڈQ6tz ҰhC}Ƙ"cN(q]و+(˪TY"bSA'd$=83F{P7 q 9`ޡ+a,$&: u,59W([8&F{B>b] DNĩHnPhr65 ^H ( ߇!=u1lo pe[?b 1dԡC 3ʲŨarN-E/OIDAT P,?.VK[`1лD0] 4$>ݼb@@ %"ABhb+-ɥfQT;)[Ue]+D9/JĂ* ̩!MXmI S8q2M^h,+:Ӈ2s,,Z$XMG3e& uQGE*[vKX7ph#nbH:$jͩ #3eJ*97Ç@IлrnAZ'O9x;|WrpMWZhC)(,,zkC+j$cDp"ISu/Q<[qEft6yYrڎ1ƱKDSM쀜q&1Ĭ)gmb8ԺFWN}k9}͔FO1vZ#huQW;\8k7RƖI } +ůs@0^[rFL^(mr&+f0#i3Xb-MO)1kRIAk&4jJ yU+XǢ: TX'b'-pL.&4qK7:qlsOYzVbia b\cbia 'b]/dmI0亃Hr!3tQ>Dq*&Ĕ( T<ɨtr9==ol)e}W Qف$yݿ*I@:;! eK-,APs@^lZ!HZjA=7@^A*Je eM,^CM,D2ILzk®X*U,p n"v&L&)uRDŽ$9RP:IJJOWPY6t1j(Q Y~-@JB *SiYo0Wd(6sJ{#_!NZdʯcƨk &%jZbJٕs?yN;k <V̨Ƈ^ͫ9R%iﴄ4I iͲCCɪsD(s hG0bYp*Z˲WZS65 ZYQGG*5,cD2;rsv lqWY(qH^2p<Ӛ0cI,bӈ15飺I2}YBu 5ְ ` ⪂*"Hݰ_';#T 5ݰ$#摁љJN_R }$Wӻ'}+<)썓P#:䚤vd%߄@Ι(R lsabE Z֠ ހ(=oq$m␌;(ذ4ʆ*e$ci Ա4q2F8 'p+[܂}ゝa:K@-qc.cd9 :N7pWso@OdT%fJh1WBXC<Ĭ,NhC%J~ NW(s=겙;(3 rN/YqHy3"tJ]'|b(t5Қ^y>HZzwoIM/bx]QjilXuUbPbզoFc8 ڭVhO 9@sUi%¤{e&1,9`De++8]ŲqW&WUUyTLn)l9٥ sHyTovALW*[h>jx饗w=_p:8Dpimy H}4J֬)ԔUU^bM/4yHQ&pk[hٓs ^=yr 0e=0 {4bj4gٷҗYz:c+DDVtW6vLIm(e5#ϑ먯LiW7z"X2Zl!NЉjd WIzKKKt]n.bG<)lcer'N¥~OHaUL=KS  G~YFD64["2Jy&@ x"~{cTL80}1AEl  4 4mx-[K?'a 4Df>"7I`hќB4Yazx{$7+ ˹c4eH X^$?KD&-} طঠS-a6 ~*m1Ħ @W`T7Ӣ*H,7=ʌl>﮼J1|gᗿ嫻x&9ޓ9iyIG>ғ SIsFz\`'<䁄 5⯜`,vn'f0V+T*M-)m>[< (Mc|!R1a1+BxEQKe*u4g'cAEnE앳ﹶ(썕 HtT{"aS9p=<j8fHd9.V$C]llMH3ч igT=+b0Z;# J9␠iD|QV^$g 9<-~_SO/~DQDdrr],>F9V9=*}z#$t:Orr 7872n۵^z%ַu]덄rO%fffVchZ6o[=]`>|Co>;vxԧ>šCX^^?cG?~>#e]c<۷w,..Xǎ7x#_xg9we /O|- 걞yx ˓O>igqrQz!>Xy9tkc8s=LJ?a{98+㤸뮻jd.=N7ps9X`ssoeeeW馛K]҃~w/ |ͬ].Sd0 ?Ї>g>:1:W_}5SSSLLLo~^?|k_/Z˽޻kcxGӟ_kct]jC|ߵpwp?CƮFAI%J(q\$,QD%A(QiKa#|k/v_|;c_җYīFI%ް.2;@^gccK8Y NƸkW n#Gp!ynVx'y/8xᇙ+m9묳^OVˣT%qsy|K/-ɹiRA(QiRA(QiK(Q4EI%J(q$%J8MQt%J( D%NS]D)J.QD%A(Qi XNtoIENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/buildplots.py0000644000175000017500000000150512253362407023433 0ustar georgeskgeorgeskfrom __future__ import with_statement import os.path import glob try: import psyco psyco.full() except ImportError: pass for f in glob.glob("*.py"): if "buildplots" in f or os.path.exists(f[:-3]+".png"): continue print("Processing", f) with open(f) as file: code = file.readlines() code = ["from mpmath import *; mp.dps=5"] + code for i in range(len(code)): l = code[i].rstrip() if "cplot(" in l: l = l[:-1] + (", dpi=45, file='%s.png', verbose=True)" % f[:-3]) code[i] = l elif "splot(" in l: l = l[:-1] + (", dpi=45, file='%s.png')" % f[:-3]) code[i] = l elif "plot(" in l: l = l[:-1] + (", dpi=45, file='%s.png')" % f[:-3]) code[i] = l code = "\n".join(code) exec code sympy-0.7.4.1/doc/src/modules/mpmath/plots/spherharm42.py0000644000175000017500000000052012253362407023405 0ustar georgeskgeorgesk# Real part of spherical harmonic Y_(4,2)(theta,phi) def Y(l,m): def g(theta,phi): R = abs(fp.re(fp.spherharm(l,m,theta,phi))) x = R*fp.cos(phi)*fp.sin(theta) y = R*fp.sin(phi)*fp.sin(theta) z = R*fp.cos(theta) return [x,y,z] return g fp.splot(Y(4,2), [0,fp.pi], [0,2*fp.pi], points=300)sympy-0.7.4.1/doc/src/modules/mpmath/plots/laguerre.py0000644000175000017500000000037712253362407023066 0ustar georgeskgeorgesk# Hermite polynomials L_n(x) on the real line for n=0,1,2,3,4 f0 = lambda x: laguerre(0,0,x) f1 = lambda x: laguerre(1,0,x) f2 = lambda x: laguerre(2,0,x) f3 = lambda x: laguerre(3,0,x) f4 = lambda x: laguerre(4,0,x) plot([f0,f1,f2,f3,f4],[0,10],[-10,10])sympy-0.7.4.1/doc/src/modules/mpmath/plots/besseli.png0000644000175000017500000004523512253362407023044 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxw|T wIQ@QA *^^ AA\@ "RD@K$!''1&${vd>dsvf6/μ(:D"1N@$Dr=z H$ Z"H LDbPdH$"D"%Ġ-H$E&hD"1(2AK$A Z"H LDbPdH$"D"%Ġ-H$E&hD"1(2AK$A Z"H LDbPdH$"D"%Ġ:Ao߾.]wD"C'hooo*UDzz:u;D"q+^Hi0`vءw(D6|oܸ1uEQ_>iii/____RiRzuCĢűtE1D,jMg{<7*۷[&I:iΓ;{;вvK=<5r9X/,-4+ kfZ$##l=cEQ*eʕѣG+_}U;SǨ?;Md}4u|fq]Kk.JTjvG>lck*S^zu=|p~ qaaaz)qmPN':`|&MWv+ //*Yy26 KBQCժw.\H+-Z@v{@ݺоl@:_ˉ!/.|=5T!\)tAST}k w(0x0xyiU~8h]iTXdVCŢw~!nxxDK"]ºP޿Kr g1e&}'N@Djn6?mWT ϋ *GY ZS]kѸdwY:w6QkoS'SS|U Ș2AKx>E}[j۸V4:O5m KXla-MBs Ο]n{bN(jg.Bz:?,b-MBs ޸R¦M}h5l: MYL)BoxxQكvS&hi+(*#^ڶoL5MgMδ儅<*toES. S&hiۺUW{jsƥKJ8D&4m5iRܧ*1%LIhn̮pxcԨW6E x@?+-ǟŔ Z,XZPt5nҦ˻ő T\ǔ ZloDb;^f7(˹_Wuz]!b%mOo&eS&hi[^l}}bҸNM {Z4}B=\=8m(y {] Zl>{^Æ7˻ ZL"#_vmуE ļ<>\EuvE t S&hi3[P̔;|Ym%V4B.g]@4yÞ+H+ tk=eU=S&hi+( Kް"m: oNɽO+ĞKݙ 6{*)4 ͍Ym$<~AiѢ剒w0d1^S M\ޭL$47fѷ`x,$<<l8Bܓϔ+OF}5=mWT I?)4 ͍]?$vLqBhtza+YY l:sˍ%p ]eC'肂o:ɘDs3[H33HaIJa ⻑OܪN t`q g̘b(I]vui38vnwAFHW Vuʹ[]ƿ}2Aa;Z*M+MBsct}[@TXhvO8;f7W,Wՠ!ڎyv|)[R&4 _~رc̙3 6pܹ8q˗fVZɓ'*42z?0L,Z 7L,`89x˄[GxP 趘Wt?Z/OfZ&!C8t!ҞcY3gPb`o߮L>cO>>H >T|N\`":v h[wj99 n>j:m1e&1M)nDG wh[vTlhG5׍\ϖc#ek >|tgBza-MBsc}S=$&C͚E֖g&40~izV_Tl6*)S1OU[aCxA'.p9XVXM-Z=*k%$~ lOHOLIhnp(C\7 oiM O)\Y} }S&hiCŊ7ucR0>]ߋR[|f<"Q+ԻGF^̥ݕ^,a-MBs?=,TɊrux㩧nw#[n:껲 ՆU%)4 ͍22Dvrap4tRlMR-p}H-PUTZUXL$47z HJQ~}'.0ox-alD+mCʍZ{Fñ}6 +P_S&hiefŒN\ >^ިT {طhm<^:t q߽a5L$47z;W}5sC~>< /_[f 0e&C_V|XT9/Odx//?J|N$P!nvmjQpzi2e2ד0e&C߼yb#S_J q 6--;&>?Z3@bxp{vL$47gG[o9y>'6)R8 z璝vL$47p2-[`^1wRߪN'SN4 m%R.u!A@ L$47ԗB EeEKBmmoi\ 9PuXU]g%LIhnܩo|1tCpmN\ >^lXX<-߮|k>K/e9{Í2AKܸK_NL.>w\8KZw-jjO\κ&]ju+HI&TZQXL$47ҷ`ƙiyyb]눯xU\YŪpW^rx]2AKܸC_NL&>m'/jZw-jiqg6R|uh*t)=9)4 ͍;}(KhQUg!jAula4 ͍ļgcԺkQCzϹqlM?!B<4L$47Z{11a)uhjݵ--'mKR. ?Z//6)4 ͍V`h(N^$#C o:d⪶eG]Fro]gh2AKh^o *;RR`h\qU>[NZ E koxi4LIhnзe l;yla N%\v$]U[%Q"sptl &3vX޾a4 ͍VxU t !!AhܩK޳ňEQtӟMaiv۷gܹIܨo8|uaÜH~9r rYzx)޾<α#Z' 9T n Ϧ0l` 8nݺ]w\FM}YYW9ӡ) i絭=d5D՝ZhϦ0t6l6mbϟgYUVpɢ0;uabXttjכ6¥KЯ_8]8yi8 X&OEC6L$qE"g|Pj׽=>EDDffΜIZZFKQ@;͛7qF`֬YE3f |~I A|<4i"F'Nta0r$ ۶]DEԪPIQz;xe>JUhVMjݍraǠ ^Y0 =yO-}o%8^yŅ(W w8=y*h[1螜.wKoӟMa!&QCߞ=WPy!q9.G2<2O ӟMa-MBs㪾|?^|.,JQ:U|0^Qm+ >3[BqhO{{ @Wqz^>Ϧ0e&qVkĥ~-Su*aH֜\Cy#Yly6⿉NjO,}l S&hig_9w.M']ثmξ9wzrT%9*T\gh2AK876V=}psjS`أ-#7¿ǟ:=KWyi4LIhn_0gj.r%8:u*cŇƣfp "|6)[R(WBK}?FÔ Z}bγR5eWWLʼ6bcxΗT3cϦ0e&qDߜ9!J>7k8{Se`qJ39|{p{5l6ƃ7[xi4LIhnwꔘ 8U9+ {O|v߷V>+><_ra6+=4L$47fgûBW͞-ߍ Kv$[/lAH7Y 7_ oWۮ{i4LIhn?t*DRŋNi Ǟ'1\ $Lr[9X?FC… عsKJߞ=b\p0,]:?}Vrѐ%f%o fc5^⿎j=] /_ SO6ĉ;w.UTEOr1!MBsSLxq1]OJlNcPܰmyZs{X*-F8cϦp{uiӦTu̇`^34J7y2;>ch.u5s\ f흅y 6_8} W9/\(K( +M̍22g&^׷R?IߝNP *plO6.&aϞ=9wIhnnԧ(¿KL_Z% "h/FmsKRvyU-҈ @a9gp(rPq3ǏW,rE塇r'|R$14i(*\03SQݲE :FnRj׻^ʉq-ّvʮK]z#00ڵkl2/ &'sQFʗW|11KZm_DK"~U14p(V.v[Ϧp{~Xd O&&&۷3ydo5Ihn %'ða}wީϜ3Ĵ:K9N6K/xn8 2 ^^GϦpIظqc|}}?>YYYiӆ7|PV24 N-Za(*\XQDw)yʳiLIh|EѽvE;CB\]]^w޺RHlM7ԗ:/{x6̈́)4 "zΉiu3g^Re}&'T6t%P|u=0m55 ŪPwr]|+g5E_Y ZjcAnb.[^ f`hCQ^?T,Wm}>% Vz`gӌ2AKИ( L 57i}))mTvl7Ywj;vЪZ+4` Ūr՝e}e S&hi?svU?,,Mqqȷ0 |E"Իr"s_o/a][]z+kz֭[Yl4nܘ7Ih<>XlWX7ҰauJd!6kԏ{\wE}R6) Jz뭯at>}X`-bEǥIh,f΄_57nNJ 6{; /fqݾ~z]NTKK|Ϧ1t.dѢE ^|QT[uԧ(sbZqIIe¦ |3Rps41~JDLI~Eß8jr߹kRߢExE*(S~B|f>w]7dˢRJTMOƔ Z$t/D[_]tppw-/|Iՠ?V.u3_7O+lz:Lr%ȀA0ص ǵkb]wv#-ezQBtրYT Zv13cȋϣcթع[ig,`h$IĴukشI$iW)QK/b] Lc:'_~j3wWQ4^^4-~vu]"TZZh(8I^Ih@mĜ?ÇʕеvkѢ{^@QY 9Ls"wԹál܋\x^>^4 /o&{1eZOбH={BxF?{Z2w\6@ЦL=lbͰ69ݧ4I^LI:V+55Uj۲jtp}*Tb=]>+/`ǓVm1ĕW4 MBb!`iv$5JΨPA>\]X //t)ՏSô>TmgX9iOy ;ٔ\){$t_Drn ˍ9>&:;Mwϭpaz5kw_hȉ̡ЪTTJF&{1e&ddqAt4<'4o'5UIPm~>3P ]j]ֱ,bfCMT3Hн2AK1~ ڴ/U'w.bÇ_?_x.g]f̚1|5+*\RͶ8Ii@@], iSAK>237O|Cؾ[tWm3(<)xó m9kiEE_TZ l($t/AUomErR/=g&¹Λ6 3 80Ѿ 6H*͙5`*TK[4FC˥-5_Fi)4 K&=]S .\㢊[QVAJ]Fӑ#˫y)_^몡 O |քޢFh)8Ix3V+,\o /\0rNý#ի/@:EhJNf!Zstޟjh;Yr.PuXUjBTwە5Lك&l&΍'OIyD<{X)X97mzݷ4 l\9)yCU襁Չ/ǿ?Ϳl]RAŔ=hi Ξ֬_u6^&M%6lozF[^Ҧz=H芶Kz-1_4 ݋){e$LMW_ M֬ :'kaXu6JK/iɡ%;P>ܷ֩YmMS')H.΄:/&{1e.&"F63|'NP׿WVWV a4q +7>jER~I!eo +G&{1GY3 ҄6k$&DySi42l0.e\O1 Qmiv [mH+h:Hн&aQ6@cDP}duM1ؕ}pLnFGÏ?ªUkQ7h1+;K^Xb(#1;il{zs6%o%=7ń;NWi;9RK%]0Ϳ6^ {0ԳY0e6I ׋} S л; DGGco|r2 6,jˈ~$d%\x{]QJUcG5u lL6 ss~~۷{7\~F"!.fgx;8o_Ec/Mf.v^.^`m#3p=%JSxyjE+u,C2A$Ȁpo{Oȕ*X{}N&pl6lqtBfxUoĬD۟ רK.[ߞڒIA߶r/#Nϱ]2)V&a^:%fS9r52@гHکֹt4b,xqhՁq5ewi_'wެskNУ( 7w7 29C'X~m=ʾ};k*(ŖPG|㥽qc+]$6mM7+ @?g]M%}8|ʇWgm eGYl41uB&{1t c… x\,"" ;[|\kET~6_$_5kۺV:ٍۍMD9)I/ziV#\w.&:-a- s.$::z>80W=SذiC'غ M@bvEk˖bUՏ /61# R9mkV8D ,C\fyCmGapC" /!MjSwZ/drv/\IX :v\wgU<<s^0iR8s'd $%'Y_С<Ԩq SDJ'Oc=}z$R%NΞe4(WNs5tߝ8T’!K8{nmTܱ4߳QXu/XO6#ħƱ0u,""5k0sL0T JVV2~x^z?*(O>%4TQ@Q:wVTvERB(2ajM(S6kbc}xAAA̛7F_I*1b ݌It::ɳ]oa rɉ! Zh)8%$t/|Pm4)% 35kZnN`>bmocY/wsFDwGCǪQrYnԽ]z$ՍuD%QdrNJ_v~6c֎aűT,W`@n~RwrdVX&+HнmrZIT+sJO=Yql C=LI8<0t+ i@DrrhmrΠj D-KD毾{@)I<ĤЭn7֌\CݭI6cNXnBī<}3Ai`h4H$'K% x}+அw趣V%gEQ͉Ori3v& S&hiʕbw߉8?:Z}| ֏cʑdeaɐ%59?<ށm]y)8Ix "A+ˇi}]e(L΃u_KS4:O7<]0e. &aaT$?_Tk컸GV>… ԯZ@׺]& 2 85+Ti_-gO_i-vR&MBMlCլ>| Y<9[k]\H@*9AC&Sn?+ h@=N&3A9pnx%ؿ_|bqq?g|[m˵q ̈Ju9u7\>B=9azw291SKZp۪N&3A{Iز%l^O P"\~<#/nzK eFi?lI*|Å/+Ţs~Qi3Lk* \WPAnIlz,/n|0(> _?k)D9Eti<1u^r##hOg4+3 ֯acb'N#9ߚ콳iEK֞ZKÐlo}{Sr}ξ|m; *1#aTD&3A{I(y368ֹ3->76;)6V_ɿ7gw=&ȯ^FbU[Dž_ ?1 >4xu&QhhmzPQ_7pHη.zIxzGxK~w~u'#WlY4@pVjrMiqN;M~b>53wR|&3A$ܾ]]|ݶ-l08dʯS|n3kudzid]rcs99.]*&T\Q{ S&hS!o[yذffш9rGIhCrxS8"GcO鍨1; S&hS{;/5w߅#뢣 3w<83d顥Qxg:={O1dZWo}^}WqH!@p`jEѫl 'IDAT5fًhh]L‚XJ$Uj 0~ `Yr~ -?kT1OQBmUYҗ-{ySslMjM WOO^h2A$LO;̞ QQX6b|GfZF̉,>r) fc< ƴCװ$qeW%-Ūo%?ny>a'bW2E/Dbe{p@Q'fݩu9h&^oܟ10 a D吸:+G_u? Fͱ5 [.OO^h2Akffdep8O=%ڏςcFL5;XwzN+мJsƴ趣 Uv( ٧^Mֆ,2g}\X9JըtW[njTaI? z˅att4nHMgwnvFdgNŪX  `ov?5k2PrB.;sPmx5ʷ)IZ<=yy>aIraOuk1ch Q'Hmodf3z';"wM}z j6ӷQ[ [ 3HQ|Bб!=CB+Kxx8 6%ft S&hMt[?b՟N=9EQMeOvFdG^>z{jWM=^;we"r2:EL2eb9aA)P|#  S&hLBE[ᇫ6mDR5l%P!_>,^ro@Ϧ=^O$F-!+ S$\nz ʷ+Op`B)9!߈'M <_0e.$LO_~ cAA0v[{ˊp1"q(A$C 8t :DZ^;Ի,B^\ɉA.Njɸ]pчM^x S&LBE {8޶޲ʫ-RLB"S#>&W 2\h[mk*M U*'%|rcsoJ99(JOP % Oh6A[,y*VHʕU/-MϿ<~kFNK[[. Uh܆k}*_ZAWJE5ފ kTK*YX-VYVluV[ Ɍ(;țzU}"OP񻸸v$66޽{P~}bbbtbx.?南 x{BrÏпiE>'ky+uIϫW^rsso& &uEn݈nݺESZ)|\c@G~ ,IKK|b09iiilڴI04gKKK+9_|YhKQ@;³>KHH*T`ڴiz$H$nŰ 8J3=X~m=ʾ}GUnʲe(((qƼz*/$7700h zow8}vLwͨQ -{)4͛GJJ ѷ>DpB}a,Z۷oߞspB٣w80c MJHOON:zStll,aalfq(1-bz WftMPTgǎTZM&tޝ͛7_;NJЅ!pq(1>| '& 6M6~}Ԁ_~cǎ1g6lsI6NZ4,xygٵk!!!E<ɓ9~8?Own9o;w.}_dΜ9z*7ofƍѬY3ʗ/wH2uT@O9Xj[l!++ѣGN2 %,aD"HC&hD"1(2AK<_~HFܢDb@dx$}LJ/> dx,/^RJX K$&C&hG2|t7|ɓ&M"1rD"كH$"D"%Ġ-H$E&hD"1(2AK$A Z"H LDbPdH$"D"%Ġ-H$MIIJIENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/ellipk.png0000644000175000017500000002762712253362407022703 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATx{xTՇH0EAT `Q Vx)VlR, ( M*)ZIBq)ƄH:I !s^asId^ᗝ;vaN nAa4Z0 3Іabm(Іabm(Іabm(Іabm(Іabm(Іabm(Іabm(Іabm(Іabm(Іabm(Іa0_f ?8ŤpIJJyL<9HM0@~ZZ=z4G}yڛjVow/- A920&Gm&hZ!xނ~K[ TPڛ zݵzCq hz^w{8 4jwz]mWVouYض ?_v֌ʀ$ԈVos/+in DNQ$ޠ]7됝 }B^()*ښkzHn5 G7u SSap_>& hkVoqao 8*ښk7m͚W1Ee@[z^w}ΝΊʀ&hZ_/3!) ӷmMB zݵz%%r{/ hkVosxy}yI_Q$ޠ]7y 4mmMB zݵz?t^ mMB zݵzCݷo[o|P每ϲ&hZ!6Ee@'7h (hmhZ!o){ ~80!YɕprDغVi<#㎋㏗r zݵzCݸ̢MHT? h߮$l~+9"Cth|O=%iN8!:vF6~z^wp~ FIc\oCe@h_o%W4iݻӹwoBZks_V..(I`:SʀvnѶms]Cx]2}W9O:IskXVo_g79E$\l=%%%tؑI&PVV5\CÆ )--'zk/e}ރwߕ k*Ӱ-W9;vT36az0ƍR;-`_X{<[oK/Wdzޛoz{׺IWGygySjyG~+->˃yjp1gΉ|RRRh߾=7nzFs͚ɐadѧʣ,xe9@W19&  5s͕S|\SNJ8f„ B SXXȂ `dgg/YIs;wqqqdIs`42{ ~ȑdj|BY =d<'OwDn~js.59׮];g\byyLN96lfĈ0sG뜕EFFSL=àAN  1dƍG&M(..R>y7-#ޓΝя9昊v2ͯa`:VA=#C\ӧGNpmWv풆c,{qLb5 ޽]8x<6 nY~Aڅ^kD hߓίkHt\ۼYFّ~}ي7+ݺ!ZݵzC%%bM@zl]oeK8,9JKʣke1M@^Я}¡k=o |<_|QF @$<@/k<+~QF֕C{J9@Z'ѹ_?fLaYDki˗eE]%{'Y+ kSv3r]2ٺUj3fn4d^p\~1AO_>1D7TwI8#c ÚFx_űn]cڶ('sm5JZ!IIre%mw\%kD\ u_pxoo]YY` SOOJh3'̍7&PX(um\Tu7 5Rݪ=$^{M0@c_:ۧ#: ޛ7K+(*ʕεEښSF]\,ˬFFo%HG_2z;HY̞ mۙ/rmMBoƍezޭ![fӧEIqjRΝef3ȴ:n<<`>X~ώ ۶uu5 < ʍ/~QGUO;MepEa!O2;GiYq-[T8I?u:qrksp䑲r۾}0>㪷E0q,  \{T]wm,qXbݺ;MǶmdtpUmA%܃t!y{{j7ȴl$VaOaLX-veva S:;+.2_k٢rmMB Ca02E7…0~<}4|<( .;%^a{,yp^xA.>[8^a ]/[&{GhH$u^_8lY`"C)iT.e7\kޡ,H$/ \Iʕpݲ.v~%nݜMsϓ5NQrY,r]p5E*K$5qqЩ?.M{) &/Y"gl) 3dU=/)Osb8}Əj>/c%b.D=p\J~ЦMW9x)㎓s\"˃l-*G$ް{&}۶IPqi̛'ӧH [e']C6ln7K8"=Տ?+8pyq AFe bKR*C C%PR"SYkY\eU׮AkbMB< 5tON*e3%Kd rZ$KI!;́gモsZ֕n*͟PY&htA쥗d_"m.2zUh =&]P?..] 7WffrA͟aԖY/Y"u좢:w!п;nee23^r2; ײEښb޾ 22dtl2";[65yٞWV(ml.$Lжm&#G´i|AOWY6̬Y_dҢE^wI')d.ZL 7 ֭#O۴!I>2YY)GVl6m*¸o_ɲF> ڶ;T$LK ALLF> Gul߾?/?N~k w)) ̄5k*5rEܾCXy'^dh]ok٢r$kOP[ڵѷ7V}GFw2Ŏך;W$_~|J|~QKFp½жܨhmC8MUߠtݺUq=8ݤGtF@pp}R;9N{=@e@ەSeeWu4Dw]V}tͶ;:%)I67o`=[O-[UWmMBwIlŴ{ggZӴ+Eu1`x}'-MFw fI oOM7ɔ=WPYvNt0}%^6r.O\u4ܥ0}\&ˆ}O? O>YYbR\z@wo?p-[TmOyRuaʿۺ\A\y4ܥԈW=umm-~5}V6R|19zG>Q$~Yj•GGos /~!:- 5 ⿦z\zkVo4 /-4y ]7 ^XQ-VT-AD˓ݞHJ݃8%EB0ϓ5@|R.1?48QvqZgfMBa#?_nzbJJp.G8ݰZݵzd3SNSg`47ej~'@4ub+B6rS͚epǎrGMFSZ*3?NPAF1e&!$wՏ~7n{qcBuהSװVwްpr|!<׿ƒz7'菴 ho-IDAT2ݰ!͛;W[o_))U#a^wP OfRyYcDYɁ:Xc?<+ヒ>rl\} զG[tCÈ̑:_˹]{bJu@]5 wRžvc} lvՏ|>^5~UJ Zܛ5V\j\xg]쮇#2ٕ~oa^2=Ƚ͛0bҥ?Vlճ,4xpKx=MU 2a_۪U.䮵k~%+ u|ԥ>z7Vo߮z>>^ՅmQpPWimhZ!qqrQ˯-K}> d~uBek13Jha%8(+'Y66=mL!8TY[/㏗h5sfff B7u >7h z| b0/$+t}\nV-ZPkvz^w{|,kRcS Qگ$ԈVotoDfz] ÇCN_~2]p_P+z^w{#BNr{q0yMrm{Vor2Dӓ'K;dm_ hˍjD7u 7n,;^ '˖ɖ>Q$ޠ]78ޥ ߲-R86mMB zݵzMjy/ k3<z%@:??+^zE/++cر\{7I?ZAVoP>|8|\uz5A[U!ЀNIIaUJ+WSNM P(Dnn.999Bw `dgg׽"KM%$$8RsQ .Gd1e, rXR8fĉ,YCҽ{wBC aܸq4i҄bx≨= KHjZAk%hBB3g}fͪyڛ*sk.dc_Xz^wޠ%T5 G7u ]Be@ەk.2JB zݵznwPڛ zݵznwP$ޠ]7vw mMB zݵznwP$ޠ]7vw mMB zݵznwP$ޠ]7vw mMB zݵznwP$ޠ]7vw mMB zݵznwP$ޠ]7vw mMB zݵznwP$ޠ]7vw mMB zݵznwP$ޠ]7vw mMB zݵznwP$ޠ]7vw mMB zݵznwP$ޠ]7vw mMB zݵznwP$ޠ]7vw mMB zݵznwP$ޠ]7vw mMB zݵznwP$ޠ]7vw mMB zݵznwP$ޠ]7vwAx8ꫯ&))͛3ydrrr6lK.L0!y$ޠ]7vw@G .d̜9m۶֍#99"ڶm[y$ޠ]7vw@:??RSSٸqc׫V駟RҰ&hZAKСP\rss:B HNNf׮]QsssY`,^D䷷3.59j*g\jz.++۲e3.󒕕EFFSLkcƌaܹAkh0!!yW3gm۶?V\INxHKKcʕQϋ BNZVhZAt%III 557V9߾}CVZAVo |P(Dnn.}%77[]srr(((`̙uY,Z(~6]:8)**m۶[V%%%ٳgW*ٵ<ذa]w]ԹѣGs饗O?qW~ߖ-[Un4/2Fy[owɲe8p`Ewޮ秞z*P&ˣw=L~~~yO%~knr=55UVQVV:t(s:--mͅ+Vp*-[2?:3sy^yBk<'''O|+Sf TA$''k׮ kk﹊9S9r$ؾ};-{eԩ[m۶裏V}Ǽyӧ7|3h"nFnf)**b֬Yr!AS[of͚W_}{Y^{bb"锖ƤIV"3qD,YСC޽﹊6 8Q$4 86 p h^op]wE]aQ/4h\ve̘1C[QooHNN= X@'x޽{3w\&Nz4Ŧa8 00 Q, 00 Q, 00 Q, 00 Q, 00 Q, 00 QN AIENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/laguerre.png0000644000175000017500000004562112253362407023223 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxwxeMOHOnH X `@PDDE"5K$#&H230|jժAՖz>>>P* "!33@jj*ܘݖOOOQǶ'ODX{_$$Ox(c}v3htxc8;hZԬYSPʄINMM:^r!mww]IOx;g]|;Ti޼!P[XnaׅgZdH6Rg_.;wv>}>9 tpc,i4Njv 4AEB>}C#Fr)jnAEBʴi-R4 %BJ3AEBʴ-:11JtbCJ '"!HY_E$`yTZbaׅf;;٠yB.UmPiOQǵ)N6hl#e}i "vsM0Qqr'4OEBegae=ΒGI)w+l AEB ' h0z!@T!܉t1L\$d){^ۥKXza)c;Dh4QOo\$d){VF=#{;Q7䎅gQl#e}j[z~)>>1xN6hl#e}203t&,Xk &Tr'4EB냈?B]`>H%wA\$d)r vFĞ=R3ߜ)tH"ܱblujH6RWQ7`VE*ca 4\$d)T$f%bXaUp8G ct1L\$dV],XpxA ce 4AEB5p=<=Xub4hH6RԷ\O fc7;V \$%R)ĔEFbfLX*-i&ɬy. )ؠ+hl#%}7g}MC)#& Z.pK8V[7{C4hH6Rwu|}kX`}PZHF[yHA߽{`ii C& Z.k1rH moQ߽ȸ"Xח$Ԯ]*Jp*Il#}!CpMU-Ln3dU1 0jrmXw& ?=XUw֔Z`]K- نe}5wBG:G9^Ų6}`]lf@. {iЫ^/,km"!۰C/puBxaV XZ 0jrmXԗѻoydQ!޽{P*L 0jrmX 5 e}G/4hH6nl@*s+ܗ5m²hLo\$d=V?F >bR,i3V 5hH6#"{,s"tV A\$dV-:{#k],@cXf,,듯̈́\$dK8GZemAXf ,+6hy#;mǦMJEB4 >K/^}ص vG?6hR ''' 577[l]c@DDDXmW)bm͛7M,nrl X1pv6#X V}>o963 BP 6aUkF8G.-9솿]ElڸU},.5hH6bwL=4V*+l ֎&'&m|>V ZS!I6bїPW_zf՚aQ_BB 5藡H(eĠX1x`u&;? X{ʳ0iЬL1V 1"_/e솽=gc oXbb4hH6B;sO N/N|lrPX1d`4X{%^}sȹl#l6al󱼜GΝs^zGb8L\$dsӑvxXu!os'.4 ޽ OOO 0irm̭vF섿?  %3{rETT ѠAC1 & Z.9mێgU=o{%N\DDD`& Z.]yx =~N9w"<<lfE.9Oz 0;Gzx?' Nl_ASfD. ucW"~s-N6Lɹ+8X-\$d?5?_v80U N]WV_l>.|a_Ox#|ݞ˸\򚥧%;?fPZeϝRDݺuh4hH6eiH'ҡ֖z]a}{@B6BU64֍Q\XW\A?H' ׳K^W)UW}{7m][(-~wE mioC}<6GYgFYs {Saa'On#oyc(lϣV£^=*i..Ƚ\xbԸR]yO*\$7yy>V=._(n6z<߻&5 F m2x9Ճ"Dy*W]ϧ>5 Zz*}-+藡H"9ws16GD(,mڢMJ#w OYtFT*/ǥq"C1X͝1W_1i/C%Է /GP97mc{,I_}k{ރJ–[0 >/B@vk rq5}"xrg(,苉AAAB)H(e#fN R*Zgln OQcR X8ѪLazx*,:`+5iam{#ic"'F"(d]B5/ʃ 20z- %A>"FGRKH+VլmcK XF|;49P(5 6M-$oKƕvWϝO*B+h*CZ>,-Q뚨66ח/ CiCUV\o4Mb Ct\nuM4Sɉ5w\>s,L^AEBB:£upyĄĀ4>hs j|X s(:}w{}{N\%o |IFHMa?kX')&_"?\jGJJ rss <:y)yp3j6&-e >F KCM=~sNغp-d /%Efϛh1\ʝv"v}D0WpLI#GԩS CXXbbb; O^{5t:t@˖-aiiY E-aMauհ6\ >}nѶFp4*B㑙VZ1gaҠ_"!s}D;wq P(Pvm4lz׭w*V:+8;=?d$&&=x7oāp=ڷoAaȐ!ppp(W1f F05Ǭx5P(oᶆT;Nc7o6m*p$AE!">}7o?\]]ѽ{wt*^yX qY"bf@A߯|ww8~8?'OСC8t>S >AAA(9i@S ƼR[0x5CiDD`‡5yqnܸ@: "F1l0CZmxbjР(kѢ͜9Ξ=KdNGɻ\sP URĄo 4rH.9Vh͚5K:!8ߑoV(BJ`Ȑ!BCC:>00xLEAD*wX8a;Q3ObCHJ@ժN~};46ѶX4j^/gʽ[>ȫⴄ1itiΞ]ʺCjEm*N%(D.uo#((7c ;,?._x `<o#@4|G 7H8cB*V*F(.fk@IAZ_mڎE+@*feۻ\? G~_[k7`mm_q|-B _)5nc2bp+ME]a8umA j &_09';;,X@^^^%s]xtZ=\8CcV{TQkG/Bɲ&ADE˗/'@δe^c1yyti E(̍:u:ucmI6lhOh mN=BQdQǎD6A<_̤y摻; BAׯW:VtbQtk-ʉ(PHh)g+ !q,֗I|IɗM`` =yFVS,:aBJRS\$mܸ1d怾}Rll,9Wq&޽zB@oI`"_ߧFL4iBDDF!!!BHTەs7n{Ę/D2.=DM7%쾱ֿObAtDVVO: hR4aET@JJ # =z4EFFVzlAZ.gѦGx9 Zr7NOIIw}JRY$bl"nA: QժU‚BAGGGӪUhɒ%tq.,^z|@:6lo/ LUOu QJ ђ%DM<5jkkC!jyW_RRR/$[[[@Dѕ-ROt$"N8y1E?%̫B/.'r9dt:Z ݛ զйE, `TQ^&))P&MLG=yd:u*ڵ8@/QFѭ[8E}5 8P-U-څ]O/ďr΍V^^Yd:х DAADOͺV-9sy2RRR諯"@VVVP\\\4:zg7Bf^&M;BGq\+j֬=xe^Ȥcq:ra@Ç7iItyS jJAAA4}R=|޽; i:_ Ej]{x[GOZ$уhv"~&Kjj*S*UJ?wVzPG64ر# ooov̋PW/6O֌EEEգΤq$iDD?-O&f͚WAAA߾}__V믿^t[{+It3|EsBH5[EhY"#M#zjD~JCQ$55f̘Qʘ? BPG?,PUJ?y/[wR_\5k=~l8) IDAT00۷ϘJYt]pBJ>'@T!"{Gh~ZH֠,YB&MѣGaxs̡ӧOѼyԩS%&fddP͋Hץ @P/8߱dtV,J} v&z"Ȩg&:~hJ"̙3KVVV4i$2 upC:WctҏoDD/[k*1k:NGs-YŲ|rqtҹ>zPZK@ƑA_~uFz<ڵkK+֭[͛iΝ}v"*fWՔJڵ#dgmG!v!PZ[}-s:,fXB@K~AO 9Dqqt7 %QDOKJԱm &\1[XXOG-/˗/Saf!y*Z+Zkt~JŶa4"c5mt:|.X,-- }5u[Eڒ&:PPҨ5ِmkK7o%]or ܹ,XPs!̠(''=<<諯t*CG:\;NG:Le~J!!$Rh:xM2m1t8:FHq*BE-[裏LKD֭;wP\\>|LRic1gHdd$uС,WoNKEnܢPVрhW&ܼI'Dnn .ّUɹڶmK6lrh)PE~I.@Cco9x zN{{B7FdNEI222:uݻ&'I67#Fƍ9/>>nBll,E;HOHe%T*XkQ~3U nJT8Z݃r@_`XcG(-D>GÈ� )Sņ 0j(888ɓPkn і /@WC`W/Ν=z;wyfA3Zx>>>)5r"s}%׳IӠ0HIIAVzt22n (\wSoPݶ+WVQW0f {7 ..Wܷ}9r$bbb0k,{in fm 55 w? :wrb/gWWbӑ8pg\9%j:DM皨~lnEFXkn6}ޅuǷAHMGz^:sӑVyHx0DGAGRQ*hU:ε:w;Zھ "7WFӦMqI8::V~ tp 99hW#x 0p@l߾ǎCLOL0j EF^'لt11Ɉ)5P!禭 =G#4hXj1W={ÇѥK۷"іonFhy%*^EHX\ D*ULOlS7'6VVj{F^FYdQ#kG~a⢐,5pqKVpmz۸ 룊H!%oߎ7xĉrJNͥ ^wBL<UU!2n9'''#66 4Ĝh(mX9QQ۾h:2(0xpQa^Kg C۶mzjԮ],ѦP(Pkn-\x 1_c1 /]hժ8?uPlS@``эW4j,X<~,tF>>>طo0c lݺ=ڜ;8å r"i]Qq߹3bj`ԠWJ ڵÇ@@x8@Ew*0uV( 3἟0M[1Sr#  Zd \rō ͛'NNΝ@^@͚@T52@=0{ljhslwܑ+?@̕;}t,,,Lq0 b/=yyD[uFP<[D7hZѣ9Q, U骧IS$!!P@@[fH/999hm /~~@h(0bP:zqRƍ-[`ҥTmM9Ixs"~J1L䋄b|}.8xhGNlТЬ/@z: Mؾ};gٳ m5gׄB@wqdbD,E—aA4EBcP*.]͛D`iS5jՀÁG0(! M-[ҥKh0p@ 9B+VZjXf va|=PR<KϦlЌ  > q-[ 2K20a<0l:y\.q0O\)4#EBFH]v4;֠;]6U`WuGx=P͌ ܸq$!`Ҡ"!\rE> 2|4h(sP(Pm|5zyKu 0jrmJ3s}իP(0e$&7`UQJ%6%AmbP͗qz`Ԡ"!۔ }@ m۶2e 2331qD:̑;KKx 6[[^#gS6hlS>ʜ9s={`͕oU_yB|6pEAf?0irm qCêU'Owr#!Be圀0 .: /$L\$dq:tI&!55'Op_sba滊Y<믛BäAEB1Y }@o/n̙݊;Q^PX)1lw |YF Z. b,UT)i?i$9sgf >"yyeϦVř3g`gg' `Ҡ"!O> 8z> ]vŘ1c)Ssgb]~YYYh۶]L\$dU&Yp!UחDpsΩl'PN85hH6fWFI}@믿Y9ΝB@ ]EXhn}t٬ L\$dAǏO>6l0uR j`U(,HZ]m\ͩ///ǎ#ڶmk & Z.8dxxx~Ñ#GJ nݠ u?.ͩɓoR?\$d3;~㑝]tPڪ x[ͩtlL\$dQ3ȀJ @8mn=ݠrT!uo*44>٠5hH6g@V_~...Xt)N:%6=!eg o1x~39ň >R"!0O> ^?% ";Vf>I8,sݻ|bE\TZvH6LꫠȈ?GO77DFF"x4Bt +/+IG#~f<ĠѣGR6m͛7vZ9s+_. ÊT8qR\0> P)9Ё>ȝFC`cc:~>1# U_o%WZJYYYزe vU刈oy2V?sM ~Ťmһ7+Mf Cv@~h9fCb7rSdž -Z<$իصk.\GCDHј1ch„ 4yRK˗WOQ {z!&6"=G%u͑3gZhz@"Dk!7QFh'JE7l 8YתE4gQ|<1DS(B)zv4o֭[}-6oEB>OOO|j(X> z0z=G]JJ .^VeAEB8L8;vč70|N]=;TiY9wr}aÇЭ[VeAwկ_J+WΝ7n<݁> ךhsWe_^W -I6RW̓FѣQXXXzG`@<ncݰ ?4hNBgM<\~Rլ8aRCɓ'ٳ'TyXIl#e}jS*Xz5lll0{lܾ} = xX'w7𙻭[9XIl#e}kW̙3F =`ƌ喃{_w(T NlOoؠW^E4hH6RW)SM6p-Zd?Щi[0KK8wtFB<9XI+w=z7Il#e}eiSTXf 1k,DDD7x}@#Fի|P{?w@d|N(& Z.A A~~>ƌVkډ|}.8x<-Z5~3VȝL\$d)H-[ٳ駟9R tl\4d д)pᇰn*n5VF90irm"mXf ,--\.'}Td/'HH50wnѺk##w۶m 4YIl#e}ikܸ1fΜ\;:  eK`rC[4m> \Nި& Z.m'l2~ݴao5 wlp鍞={ӱ- Ffii5kӧOGtt4qH;ڤ> \xzC^Q6L\$d)W[@@Kj7Sb;SLe233cAEB>C͘17ѣGoPeX{[#t& w6۸q#rrr0|pyz4hH6Rg6+++Y* 9bbbxKP;@@)eW2˗/ qiܾ}ZB-G0irmm!!!hӦ Ξ=pz .H?=\> 믿&Nh & Z.'S 4ǏǣG].]]U"T JZ5`pQ?-:dzC.V- FL?aÆ8p.\QTOQ٩,-2(rt ԩ̝#7x7ѨQ#H& Z.[l +?ȞCs}@n@t40s&~>0ys0irm m7Ə?FC"33Ȟ֣B! gg?DF^Pvp?e+6 IDATIl#e}\i0a hL0[a9wrm8խj5>9FyaҠ"!HYW ~wa֭Xj'i?lQ}@^F4hH6Rǥ6gggPT裏pmvn ,AEB>>)Jlܸ͚5Chh(&MIѰx:uSz*/ԨQ&L0> ,AEB>FjհrJ,^1)Spp0lll :ML\$d)S7o?Ǟ={Lv <1DDZgbرƆY-I6RǷ-[b " :ׯ_7i c 0irmO(m* k׮ȑ#v!44T(K_\\AVcڵa Bv-BGL\$d)RJš5k"99]t -\߻ z 1112d L\$d)ZJҥKn:XZZ/UIMg(i7gǣSN Aa(LZhaݔ$d)QFٳUmۆ͛c߾}^M+mp| e;nyw7‚-/3L\$d)\t {ƽ{ЫW/e~>9s&w|?aiiɋ& Z.M+vލuŁФIL4 .]*uEq4q4 ֬YFaܹppp֭[tRٜyIl#e}bԦP(лwoܺu ,=/_VZӧODž @*2OfBEvv6>)Snݺ3f ѫW/\|Z!ܨPzѣGDDViСDӧO/oǎll߾]xEXcZp!mۖsp x 4|畾} B)Ds?}%Un݊}_Ezzzfk"*JxEXO?gϞEll,.\ݻaÆ HA l-lѠA;Xbq!QBLe^vƤIJm9r$ Vj[BB:uC||<|}}:C~~!~)>S@,\l~]ctZr_>>>E }RRRJIrsҙG#e}K%WΏ?8(WdΜ9شiڵk> k*Uo:D"և7pqqS9͛7C3f:$Nh4ݻ7:viӦ jժa֬YB)OPzudeeaժUBd2 5knݺ . ,, f͂-zA "[**JΝ;ǚ5kp1 B ;vݻwR"t8sEt ?# DWL3ooo^ `ٲeذaV^-ptE0e p(E֬Y>}?~[С;wТE ϸy&S:v숍7"00D$<|ij)..xp(-Z|GB)۷?c޽+fusssCFƋwڵkW_aݺuƍ7sCۻc(H4Bzl*e:`j!)MK>Qb~ s~'xG ^/|{3/6(n=׫nVQF5 Pz>:GU۶JT}t:)ccPY\.Z,Z.nJDY9{ 4|:OB( 5Mz' \y~[F&-'_hx<4G0uuuYm-=l-CBbade.(ڶV%i"Еfa~cXJHc6g-=C\33,RMtMM';;1lZ$!-/3ÍTtgvМ!zb1BPԶxI7k.Ͻ!ffd+++ϯKz+F`ӔWBz%j,Sm3UK#=Aoh+17U`|~YYY_Dh^Ꟈ΀G[R P\\ @]]]iaH$` i[R,mG[ Zh |>z7EV97>VFK}3:^u#.d(((hwEUvɩ'mۧþ$Bk"gնAwQ uuu~1z|Va3mM#77׫t~xmJUmVmQW F' m==M!S[[Knn.|>@KSx νhl)p_g"H^:̹}\fv{׵t"; p߿SȤ悑>H`d[e.< XⰹpM!sf?lJ:#S{3m =Gsx//t#6s;єE\02^5e#?p[!y:Ma=*NV?']`dc_55"R phMC\02ю{FlDR"mߚ&HEiv4_'c)E:P8k¢v[&ͥ5^;Kk;vFnݒ!ߚTǿ@ 5~Awl>^4VWW'ü5s ,Qosڍ- FvF8Ց[CGƙ%+,HCМ_&.?f\IB"*vt"^Yg=MON"T Ex_?;ͭf9[Lp{h}?KbFdUG{G)D{f M<h22S;AՑS.\{Ì STdfxR*#J?hOtWE*zfl-LZ\02;8^wEEkϝUzH'}t v`"hHcX}53 ^vH梵TٴwPh ),[悑Z~=M7t+ ^UUiQ#m-uk\Қ"t## D"t-%X**K$٢B@Ozu-MZ]=i2_,M 9fQr뭷QRқ2JJdwJJSRҋ{ҷ6L4J~?yyy|>"P%e]]H˩!QR~~~}QVnn.>?7h͟h4YR)&kjj Z+ S`=󚚚ԿJ_@,p7/Ϩǃ wyg&//oRel LT?&8en (,~iƎ˔)Sظq#˖-Nx `bhʍF(,,^D{vO02D}g p'pCNCDޛ RZڏ{ッq#m;y>y5&8H$»YggwŊ 4!C|r-Zʕ+9~=[őhivk]XPPFmE{A]G8欳.^5 B/lT`-0˼zD?fÁ\~u|V_o 2k[76lönˁ>l)nҥKoXti=YϘ1իWs 7GG{&glX$gF0kEc63*8Q[[Knnn[=8/%@wDQȋFv-?'À@-P,`o֭[k/kTܤm[ @Z޻|;`j-^xsOvap9PZZJqq1#F`s1,]o}7*nk [Amp@}Dm:6mD~C߀+X< |sȇ^܈=~@ (1%ƌӆ;8v$iRg+`0X" N`)Y۰X|qW4k&R[[k:~ӟ#u|@.R9瓁F^*"0"` l5:ӟao H %lMS;rut3!HZl޼ݻ'yDC'df7ӭhSH,5~{-@"Ӑ͑| a{ p|D;#ב1f@1s/4L~ )4ICQ[[KaaagғYwfF2۲S[h˚ӕ7UUUP\\"9W *O|vEg`"ODA`{d{x e|;"q^zXn=EH(#]m"J1]NAi~"TUUeT x'pޛn-sUנR dc erlByP O>Y"7ȃ^ lDH2~s7w9c[[vGZE]S!T i3'[fQd+? K.E r="I(Kc;-D((=!ˤ GޟkOhc[kH*p-#].n/Dr~'Vϼz3J;] TR .8KX6 7S.@1GV#SRv-T3x+lt'D :#hMiэnkI}h2)<:D_"}~ڐ"b<|Hq .DdX` Y%H9oBj:R]SSCIIOb ΄q&qM@O Ss D3hI?ԻaESm?[nlNvͭGYF*;R܄H+*QHVIk-j=+DLk7!bJnZTs>RQ0-ڃŋӯ_?D£9{#k\T/;vxz:Ҽ~ zQ.xz|DvGv[?ZZ0ivX,Fuu5@JZ;f%sR),,&]`oa(Ka2RSetn2<A*Fs"(*%"JO d E$ TcX}8ꨣo1\LGe #\|˃AP"Wu4!,ilHۚ݋{$=t>}2d" F"SYml<\s"DžHQ>ȩPЃPP#1)@z W\u?7ˏfs,Aj>f͚v]9s8cQ_QEG2 dAӝBXԌSdDiд=bƺ#6=!tUKЍ,'''Tvg~`dDU_`]6v-AݏP!% p )ϞHM?p-75?oB 9tǏksps޼y{":4@|26 Eجy !kQno)4E<%0Nwׁ%휜!DQYYAŒ8U%,ݺuKGY *@)a" -ȋ-@DF"(vnEi{ňEw>d5טTsXOV- vZ< s>Ȇ_"_b3RʻX9x ;!gæSNOI"f̠YtYrssӚ-?LjBHMg1OG)g#C򧇢5 /G~H>-|at%3Hg#B@*ϑGӪkD"lȊm9GHl (SY$S$\i^(@}cȣLl$mQ-TIEy-2m`0l8#D2]n*]x'Po6(BbD0g^ ?z"] } zBzo(d"!WDTfڰ">V]_YY)2H@㑧>|c\W_ZWj'OФ3żnǏibqi@zFK^r, wk)!-;:~{rSoڶ0׿Hd$R# )У Yeo F6/Hm* E*aA)jpR=E,XQZ΍gy_JsQLysγ-S,+?&Є/5gIO,\ǽmfa#QD^Sa񔔔PVVɓϹsw$^A'l#t[oBydm\ZyWQAv(!|yvC&DVUQOPsm~f~㨣NhQMMx>@ H9#O57#M>s/J!1AjAJlwĜgt_sVҵE ?+W' iOЭ Z6QAƥ;"H9.C5~:Ay͠n""et|i~'" @(Cď{Lf@KuTVVǴ%K:ծ"O5|, u&OnF?1-,eոɵO M(;4IעE ܣ?6{ERJJJXp!93۷x+4H2˗7x}ƌlov9GDm-'t"H$Buu5h'R#"(Cu(X)! QO8|D!@3^~E21 ̙3c}]vGcH94_"oB(c4y؜E(kDehrZi~5{n5pDz^{裏c1~-s*JS kgB! ׿fܸ b{b<ϢߟB5w3 2sBJD?A䝍"?A²z|s~䣐~$_BЗ2D&&Bpsgm/p}tʋbV oFh6!}q&"sC]ǐxb̶l^ն(ƭO0 r"e;'f"ռuKSE͸B6^H[uȌB!d جtD0I vYYҚ~Ӧ=SD2!7/B(a,"!?CDdsy潋QߎOA!i%EXmˤZ\|c>∣4m=M&lBD0cڀRwfȏy0cf^CUj:}-wW||=n;zd0&//vϷ =C$w R+jو4@~sȏ";BD"r|XD3Q6GNaJ g̱ ]jO?<`>l( x"ըȫҏ·h"y dק°ZPˣfE@8nwZ  =c1tDFt: uM} mj+V+&M7(-Guy:dmtG=Cgt ȏ>3Jh7 ;eR6CXLڧDھ5r  V>>T0&r0eʿFQ\)"jDx^2_ ;NAWP lԹ諸|+D6of36pXlfٲeby8w,#?ڇ슙O_% ;ȪEVD\Xl3[̵ӌ1l0YYY &L󿟮F 4&xgۚf lt[*~.:pQ~.HAaDCwߠkrk,BOUVC vGvBL< U-hҖcmKf*b5jGyٻ"1*YdQ9RŐb+Jɻ eX #Ѽ1w8 , ?*Jٌ<:B9G"BP2DW8:_uf͚ŋ)..hv`ⰽ'U+3QXXbLUU'|:fC,GD="ld7||\ oS}k2Z)DV )PƓHY繨4((x Rև#y ~f޻ɼ'/*0ҷm0*8z,R|(m0Ѡ 6ևB#^~WFUYȾ3_?BI0 ꅈ%lzCPUUjZq= &pmFtG¦Ev/7F\\\\ss̟y~D >D  z j9UA Jo=8ݘrE8i8ܘN4yw# ";j)JYK>a"0j{^ `V !rބkPԕc>m2ۗI.^{OSỲlC Kة$7oxZ:9)**jp{y_C Pl} Ji4RCZTfOy RPEHmFu"+QO/CEr6 F b[!/{y4,E+-z:A3_Jmy5Hs;2De~vMd@^s/EHUi3RڑHӶ5v3sڻJG7ܪ3Sv@"T>޵؉rzyGNAceXEV~:*ĸP+G#;Pqw.g\> !"?Mh"{9"BͰ! ! j5Ho45wf 2/[e~s͠#K?}܇?|o;Ń"8lޟ/ѕQ;odA'DZ3R5mBnm{NZcՎ>&BkDE^l/T"+q=݌?A"7!Y UWE~ < {]r.@YE]ODHs" [ڪĜD dODEfhz ,}ÌqDA|>?W'7(p[vLs#}d #:QI.>~*Cf팶1 e2LB=2fF5H(yFV폼⫐`^[kⶥ}Y aFW/+jF*?Hދ@Gp`3ln{3FvCcF(x,Np2(0ǘB8MxU2cZpgUj,i3GI+ c:jNB! m&=ǶWŘT"U;9q] .mZTHZb.B>Zo_ܫB(W`}۶u숅(hxʁwXj+ }oMFqDdYcP} sz2M6Pr9#H5&ѓE͛74Ctitd_2N :X՜|Tc"HVe{7Rȶ8Y#aLJԹ P1KCygA\F5"4[Uw;*B4G anC}twC##{ŏ&AĞw2Ǜ=lg0RC܃PՑsSgI~B:#CͩWۭ-_uڋp8L]]]}Ԡ#"Kt{_!T "0 28AA#D̫"1"B`9RÐR_wg >׵7>yRߙmcWP>)O6[=~cnGd4yzإ>7`Ӧu[<8hLmIw> hkg|M'v\~oE*[Dj"l_ >Ve!*smKbO漗!%lH _x1监BcGODD|0٨'"d 1g"+eqWJ[CCb/ė _.A?m-s~|JJltb4igIe|mb_ȼ'H==3PA^yGsO[rRKVwHK9\ DvX,dj+X&Uco\KJKK݈T(%g,  r ~"8=G"kd%"u(-y_. ]z@E.Yog޻a6"etU9?GOt"3*RAO Q@/1jB ,Zrl{m~.5֚qI;6cvY%=) d/ׯ_ǡkOCmօm޼Y SRRBYY'+.F0a999D"ヤ\W8uuuQTTd#t[x-Ci)E~^|hg=#7(Sa [o5MEA#C 8ϜoNzgҾ/#44qCDysylC_}pCE*uflm" #^CNtN&G5h2Fjvy..>0tپχf RƎСT%-YN*{9q-d~iƎ˔)Sظq#˖-`֬Ylvs= 6Yfmqd c'_:Yn;%RsagMFu1 H D}qVQcۙB`^E3R΅H4,Qm^_ڞ*q<~saiDKpV~ȌsR'RQJ *dY(R˧I8ɠ8 hXh-\HLP]}XokIEruZzֹ(˿o!Y 6J-seƍ3m4~_vbŊ!C|rfVXA:t(˗/OEA :Sd$h4 Q~WudCߑRR9۴|ReߋHj"ėQ]q$ M"86@Pp}:Ƚ6YQeBנ/>j$*n9V J;ף #MZޱ}@83?W"1]"h3\_9nhAa޳Bmv;T ݚ$Aoj*G}43YTҥKIٽ}ɒ%)[~@)a[BMŨ%QPPpަAZ޹hg1if# lAQ|&KpHg3"~ cG?pBDS8SH zDZ(o6`9OQ~yQF S@_仙|iw9vk`9qݛ4a^ߒrN`sپf?E=A\mG孯D|WmI=9+ܧ֠-D"D6>^q_sΡbFQO{.~`02:cqt)tAn(j WWW'}#;oGBL !xDf+Q.4Fr ";q&UJ%ihDF2kHPf<)(z "ԉiGYHG6k1W/2s m9f֒yY2͵X6O{" /(=sϊZF*aΜLX.//;!O<`dGЭDVM{$jjkk),,lyNBt:vFg ь~ovq5GG"ܓyA+IA}1N󾈐i?\VFp|:j}blj7M~w9T|/ñ# qY98+?"N U϶aSg}vsoQ@uCO*>DsZRlP:qܓ\I5H1ufDCF[ ks$̈#6r=S@+}m1ycx!"UE9)QumPZیCQز6lItN$j=K@* 0hRKfٌ۾Ԗjg֒&dA Ɩ[.?sm@DODTy,οzgs_f;wg՛>ht $Vhݻ½hAIII-c被P]zd@B6(j›l=Sl"#·@9޷8VG" a=_k9t@ӌQڊ8F bg#M}yBo[Ns{dEϝf.X0&&0"<5(^Rbě':eԽK$i?kYY)>_X*oB%Yd##xDR`{P[iyfw4,pX*1כ7MK}[9EB?ƌuW1Yd;31W1H~1*6lm:ڶܟ(`x&O1Ȕɶ :$"ϝ`;red `M7n<8e%h@$^C=B"z.JFzՈtJPjgjM1G2Hnb89Y8awZr IDAT/ӷZwj={5g^A?"Ϗz~ӌ* چP̸FMlUPsN9GKS?9?Aǁ{xm:[g H$G˜9 bk)&Yb7 P準[H4<峑_&[50}YqtXyCQJHSr[ 60pH\]_ҿl"U#Rf6\r IZ⤵mrMEq84A NЦ G/"PC=wsmD4aG%"kVD?\YP@I`?|'tMVqmiitTmE&\K$4oΈwTt"tk wq_~=" m`ZHTʮZUZ-G v*{1N|󳭾a ׶&C8v 8VMdzV6Q&~`ȶ(E)z}HrNFI>Ro#{&Q<ۥs~ufMU`*R`Р,\ "n쥥< EЉ|@%x\ٻ%48܉KzEG 5޻Ү[n-؉ت:^ Z2*s Z~C 8q ?F74 "b/@>kO<ߗ(@}Pz5 2=xxmEix_)̎ 3-Dv{R0a7l`Ze4pz0tMl_#bNzR25 $~lKy~^LdZ,njImd5RYYI[ޱ1 kg{R@dFSfb',rJFżr$N("(x \:DjbKPO񞎼S]9HNE$t2Dx("Pڏ:"7RVZ[#DWwFK֏C ~Ǿ8x.Z(w]Ie3N;[n$p~%r'C}H}s}Ef͛8˃QPc:|d*DNDJK -+"_ JC»l{pοH"%y""qHg(Qbը }) APs5tGŖOE^UhY[ߎ3Bolz/#g9j4mk*46y~O">eļedD_R[[LiPϟ͐!Ch v$WnR F;tɩhs9ͩ h;haMP5[)R>됂.B$tRu"9Q!D8'4E# 6 ҳ1C]&:1c6c϶0FFCYHNB>\+(Hw)"W?Jo}5"㕈ܯDD2gyR=EP@"!<"J#)DmD<7H.@N "BjC]7Dțcmp*Bd>d0Y ABDy(Z>D r83W{w**̙bRBdWdWl_ky먴9@Ĵ"~"y{""f#%p}Ο]^ވTXgG mܾH~l"(3s>E (q'zI~OG=n jz=e,C B:\>8S=Dr4f"vxo3}x?sчҿRp8VkpFzv&''^Ioڴ/ygQ^^NqqqĺdfΜɔ)SB̟?~2bرwq^*F8mAF c7kgRwGKi@65TW_}{#+C`?b("a[RH"̷PC%[B 4#;}\dWgpJqzI۪µ8"r}.u ǽ1\ PI[hYC$PlzkHm @|Y&?A3(Hy.<CJp+8#~)s?Dj{ XYAG [B]]]Zz\x̝;rN?t{?deeqUW{orW7gҥL4G}M˦>Sa9ўН]b>ͩo{;PSN87x.i*4 R_""z§mu#C8v=,sYm~qx1?iYʒ:8HGȚ(E9҇< D{AJr4A'HHs>"e<ΜwBE8z˜h^ Њ)1&̶g5˿ekA{Wٳ'sLaÆ1}-Yɓ'￟;ڼ?fR߈!6" RVAGۡTQH5]6?~er R9m 5('2ʑmr 7zY>45BRۧ m-pg25dD/1(3hr+jnR~*0YH7 iSmƵ^о&M ~MƤI6l("r "0xR! "~ etCJ s(xR"$3{QfFZ߈/CO ȞMݑ23'wyRt#`0[o뮻;]S@DW M%ɦ2ghf$"Z;jW">TRZT}\"g3YF^Jhང,u>eoB%f&,~}u"by 1T.|Ñ3?}oF#WGr?*iFE#{qt>!4>T)hF~}ῲ^_!rlև,Ct zm$vܦYLNڳnC]]]9#Hnii^MsssX40k5Qs]uo:"BYd2YEw#y $wT!"*GY74Z4\Eو>G~( ;v =׃HA$/F*lp"m:kKck?F:/su4"srb {99~ǿP[RQ8w9f>d}mH'9/bqq"cJG6+*ǎ?zs."e"xڴ5QmTɻ+~tdphk a ^u.pFHUD@ﱈVjf4ۈx")V"텈8\~&[?7T2}%8v 9g!ib%BߍG#[ $7 E~%5~HM(r:ƛMC$yGԗ.m5'''y~KK MMMAk!#"q+"4'AB4Q Kn7ZQᣏ>Wҁ#b1Jzt^JD~;pw!L!Yt!yp 1"m!M{3䒇&Y124Y Q?"yd{u˰"ss^gV6Ht4L$e۬F;Y` vNNNxE"I#4D}@:MbNNNDM"\477A^^W^yH r.C~!_l!l@춡(XH/J@fkBBA#k9f=n?JA},~"Qt>%/f=sh8lEY,rPd|30_$4)@' 7^2zˈZg "oZ}>B=F 3s;v|a03g$緮.B 466O8N 2&oi-FHLބ.P]"D된(!WtH4X3(f9aR6"jӺ_3*|DhY\d{Eyȯ="!ko֙NAbxBW wz X똀"fгg۶m)ؽe@4]xv;^t6Mh+DiDz{qD2 IDATfi72DHgyki^O T>mRnZD'u"ȵl,B-MІt,җ[/gƃ҇,y3g`| m\mCޫD1҈;eײMjwˀ\ $;R.vk@\Xr6j]\} ڏBh)(gݶ y@ΈsѥPI0UG䗇HԐy &Խμ'e(DYF?(1w'":ង$_{[߁$A Đ9%:o 38JzvcU" =6᤽(]eA;"'ȶoB{D>u),@PW|oc6{pyZ?C: d~Bu-J|@z!ueF SPjBU׼b5ҧO Qd8B}HOhH 7F}?@ s%2 %h.QɿREJP45=zzlڐDN]6̙w$Jڛ h'ms /;2R#A7 ^kDF1>^cHlhv6ͩkZSSCYGv">HC5$OT!]x="gM$ M!s{Zgؽ{?#_?Xwt"sk3nڀ|H13֠sEZ?BW!mmHk_=cO@ l '@,y{m|fmhi❦ 8 ަOSŢ٣Hm nLQdv fc@N( I"ވlc"2܃h*"5D䘍"$kT PBXt8KGHn[y7 :ځb-@I儦k8hM(ދH6%z~3)毄zcEnn99~;n$wy;W]uUcG ?DV27 ]#qR#bq" B#m$yTX$"T=" ׄڤk<EO:j]ebrsrrUˎml+я~Dqq1*7x#eeel޼'ۑ1۷xxPII륾COsD1YD[jnICs!a5ـB=t߯Py"? \k};"7Q3fYg(vu Ǣu;;SQ#HX]J5|,xEX6B@&ߎJݧ y&Ʌ"Y.>Y$ Y-D%cٵձ;|ٱ"++O?on߷l?; =úu?>^{kMIӜ hhhh^v,xPY/jt-D͐zT FWu*V#2 5dŊN׫ךi- my*wG1B7feUCG 6s M0Bik,he޼[;}M#Z{rn*gOd'/;Nv#FgϞy2o<*++Y`<g}6ׯO9d(I=ݧ„>,ڻι\>v"u*j6UN(,4c_#5NH腼;Q~lwC:F>6 .~8&pi{/QA?P fʶ#)TTr>Ϸ'zZOQH!;j ᘬymǟB Rʯ^ǧ_E=|ӂ C(D&8Q s&iڰ>F2W 43yNT)M8FvNJq$җ]__!E :Y; i677vˋ8gpbgzc$"&vGu?@{"ߢD("׭P(*w3s]Dɮ W WKbBg": j)jٱ9]Id :7 F2$nD.59'9 se/Pjo3Q!D04ʎ՗* E :`>, X~YaMk QsU>PϺ}$!T:*ԸT!ºPB(yhP@zRvAff&J9(}I)[I(yJb>C5i?PSKB^nPts%Ws3>McRp^}$yQ d#=$Q4Eg[+$tɿRT{#s]e܌)D)..6JCe2P䜏uyQZ@$ m(OEd&4P$B}:zA}r,E&d{ {XsᤥDG٦lhٍ1L)I)q|>̌߇' +++8<\S_!C )t ;k^d?E@%"OQ5tED$hsiy-7{TG)..@K(ZO62EZF>@}7T@Y?O#tU1 |u<4|3ǰO)C)Z54^7c1#8Ypbܸ" J⬰J "Bn@>M#)7rlH] ]ݻ3 N:Z֡=q'ҍ7L ""~YV ٢Fa &=;"QE@#nJ6 Kr&F^Yr%MMM\p)Cn9x߄sQs_5;qyC$"KOA2hwMֿQi(qGՈH[r_DŽ3ʕQ^HgPnFHO68Pӣ"4[H~UK66FYňܝI G[0U(6l[Ukhh࣏>b\x)CΐI mnY:;ci -:}W\q^x!a=hQ15ӈAɸBN?nw-_|+k6R$< $\|^DFCzR"#= 9? Y. DaƗmjV۹s'| w7:!e :B>u.ܱpbʴ}>_LcAg?eMD(.EgDp&Ѷ9&ꐵ3}|^믿FFF6J݆"_77f*އHw(%G"^$QOȆtt#?:o'Zjg4/.Z{Tqiph pH+@ Uظq#ӦMMZ ڂ8)zp85{Ⱦ}9ꨣ{ٓ믿 e~yA% t=] ܅; AKCNFP%hEэc|\._|1ƍ/ ofHRT u&222~`u.DMf`@AA# εuvvv0,C,(.LrSGr[]1ңG[ J.@Q2$]|fx6_Ed|\@nB:nC^cFFF#777f&@׳c.첤G)[%⍠M׹x67J<_2i#U=vݛԑȸ %^FM>CA/@ßsԭ%߂H!3lL-:s:‹DtK$rx^/^Lss3SL9lR#A{ms---qu]go FeffFyDcM JIU,p_Gn~>hi.A+Hz >%S#JJVG~o գNI983Za!h^.+Wsg 5x6qzΞ ȳioB>:A[P#{Ay?ȱap?Wuԩ))KБ|! bRM/)}9^oDHuD4 e {5B6F=Dl_8~3Pᙈtmn,yvOn J.C<ġp@ܛsd4t 4r:k?J,^c2rHVHYGgs M@ ssni.5F͆heˑLQ m]m&r뮟Cd 3qV(QPI$BJElT^Ν䓛v4hSj$D.9qپ};_|'N* ohh[oG+8[o%''9s9B!mHsQQnuX`%=62L4+j%tdggG~>쓔䓿C柣&GW!("QwQ "hK"WP_FHDZv9^{uh&yUM g)ISQQA}}=ӦMs?SrW3}tmưax9sYpa'GVf뜩' DZHatUf;S:c{?7,_"1уȥ"%rdW+A?EPQD}P!VO*x}McL ǒĵv4Oի\rIL;vPZZ ÃUׯs9'5&)AaL%`,o ~b6u& ;aΏ=r  /\dxsNzzd(I859 9:V K].z H0:rC; kΒ&ȉץ{nVXE]׈CuVƏ֭[d{k.x(++NkN#e5h  yް̈́;lbz5;DTtYUW<}=JT=Ns"1GHz8XP< P@57_^oJVBH o웟:C `͚5ٳI&}.ٳ'EEE5C2qDnʬY={v\ϓ,A<vÑaGdЩ9^ĚxL2sNnm~~'ңZ|"\1*ށYe<"w-A2D@Kү_?N9.Y2#e BjGhpbؚ_R>գt… 2:$O4!Tx2PGLT|xoNg3?k"~.[ZZXp! `\p7N4AG:NNFͩu/%f"r*@ @MM z6{xQ~"l<<i\zΦrqp2{{pXz5]3rH~8DJt$%nǬs&jve8gGFw;%[qᩧO)% flIDAT#얖nu{2z=A744"#1j#;xt<=ha |F 'ٵ,]SO=C: )MН6~LGvvv\QaFm"k׮eڵL6+ҡզi~M6n:@CT{Miii3#}^3$piQ4p#&w'#m۶f&Mԥ9#i~-[f͚`!CPRRȗ89ًgRQh+3s;1SQQACC^xaJTC@ `ǎTVVB~~>%%%ҳgϨ>mhב 3`,999pUFcc#K,aԨQwq0ΐ& k.*++x<\.z!Cݻw/ӗAة2kh*3ٯZ zٵk\|}4"G@}QQQeq <8D6ى'ObLdXतaf߿'&]H@@`ycذaׯ˾5ى$Sr@w67 Y'އ&-[F9Za%3gRSSC]]/!se޼yu ᮻb„ \{RQQAuu5.̀TǍx&Ys"̮xtRؿ?eee|[ߢO>?.\:Nvv6>^x!5͘1nѣGs5K/}8~_ ͇?le>y䤓N ׳gn[%\رcMjF6ѻwoK*++ٹs'{ !eg`s*i~<999 `ڞӌӌ#_'7@ ƍYnSL{ƌÌ3x7;v,%%%|'x<~aV^M `Q{̘1&MwU:#l۶<߿Ǐ>6dj6;;_ٰ͛a]vY{t^z)?ϧ)xҥ̚5:*u[>_|͛9#%xsR<ӑ9U`` '~?+VqvFHǺuظq#ӯ_? j \Wv<ے m0iq,Y?QF%|iĎ4A};eddaN<Ą>6?Դw^gÆ L8Ո4iNzͼ3nܸ=zth ;gRpr֡?O?e۶msu׵r |HyG* \LKK z+.0wܘ& ;L/_NMM 3g$++m۶uJ{Ǿh@Ea`,hiԔL\c=eee :zZ븏Fb-[0k,̙oo<~O?4Gu'OU F**++inn6*--W^]*!32I*7jrz$؞={Xb\p={t`it%h`֭,Zӧw:á#RȠ4m ٳUVl5dm,d񌓒F `ݺuܹiӦđ4A;hf@e>(/bJ 80X48p\$}& |T+9f~#eҥՋ/܁ե]HK]pE33f`ȑ\~r-pwrA TTTPSS墰ӿGH%sӒFuu5˗/g„ ߁ѝHt]r8Ъc_mN%M6W_1ym`Fk :n{OAAAP6 N;HNT6|>K#Y&4TTTsC:cI{h,]O>aÆ|THtI&VZŪUOdС 4K{j85yi֭[Yv-&M!V*YA4#xk.***xGa|rՎo $ ')//0mڴV# +BAAW_}5ӧOÆ ?fٲew}sm76V9sc B"'^$ Zvv6cƌa̘1HrÆ |gTAAAB mmiԵܹUVq;J#FgϞyA+heee zpsVVT@ $zEw#++ѣG3zh@$i&,Y@~~>{I0!@ ի9pSNMHϬYZn<4iB"'^~?[la͚5qQ={dȐ!8Bئ}`DR<ҥK8p ƍ{}i@ ; PZZSǾx\vk>cTUUqEѧO4A@ ]; 2޽{wHNN |fπx':n4AFGEEnEqq16zn qil2 9묳iYHtiD@ @uu5SOeԨQq'jjjXlgqvhi2F"\̪Uxꩧ$??gyWwdTWWj5`,u_}7nd /eO#u&4gkTEVb̘1uuuTTTo>\..+Hu|\ǹOH.}it+{{IEcǎmb> SYY$l%Kp 'pQGuH! :nEz쑊(((`=Vb͚5TWW~7K#,G݊p3EEE\s5L:@ #ػ/d<L4wy[֗sHtiAG3/2999,\:#$ai}Œ)X~=UUUs9ݹ4 :4g{=v=C2eiA7?g x?ۇ4eʔ+&ӂnț&|=O`f<裟C2e ~~o}[?˾_7o=y=(}Y(_QYaxgÏ2{NJ*ۻӸU=Wg1y.wpE}N7\*]>\m# nC Ag7^):L̗t ^D] RcڇN7X| 覼-onگ>O'^s]^3gx88RY ̭<P:O-b̌8;гF߾mE#ǫ4ΖGU=Y'>*GU,~itCK{%~WCd;`pȑ{ؖOJP ݲD9#h}UoU]? d5lsuc:w⾨W(el+H<]w3ehY}%%d=6fݳ۠U IZ[QּY7^7BP]JX+rW NmׂZc nx>S ꐦcN]dOԥsOE>.XNuEӈ*,6ύm݀#9w!^a I0  > w|/,Μ~+wWtI5'>$<9s9pX:,fTWY(yޏòF5)lw nE="綷áYu,D#z2WH*wZț. ]oRq0KJoYápEq BV`}TzoYҹ|lkQ@MBC*#P  `;ھڔ߻m6U [rmI*or7UVsG6XK0S NW`Y 8 hj|˙@KU.;ۂel 9@]~2W$ ¸a-W(w@ܳ!NhX봕v tiř<1CLD&/os `kRaI}c] 0. V;6߆yv4=ezh@7!;YXqm /µQXzbVZS}lK|}98W[ť~{MŶS-8o* nm;Z*2W,@Z.7,X7š,͌ЩAb^tΧo^ .PJ~tl `sN[9u<ڗR@VLPO 9nY^t`rPt.g}A͜HxE6ihV46b18<`RJgG٨ dn VK/ vx~v_˂xy 幟2S(q#eP1rVfSUzn(oJ9CK6aԅjgv]6 r=Pݾֹ՗uJ ηkP[稭׺V<ezUN7vzo:6'X~ T/XYKY$vTmPuV&, pHVwǽ#Jh\6ez-{o bʭ[nyx;V{}L:6ʠM8{uG\ )g5P:;v{*kjp.ezXqh!*|~TC^3hcۉu9qgVr-j_>_/u?WP=ԃ(2f{wHVL@mJomc V ,nMdve#]g(t kim]2r0{~4!@p!)sNʺVw} -h`[#Tu]mð_,VЕDžZ$+۩~ {4!(Smе.cbK>&bXB &1I[gO򄳔 蝢&nTfwطU^i[ #YI$,Kk<_5TKJ+ycz#5D5 s# .@օp(Q-]ч)pziK7!ezݏK,@2VlZS:Q0JP "IQQӶ=RӢn dmzX#`n %);}\`kg*_."=0kVhy'VZЖL@ 2mBo^]f]'{zk[EEn|IwCLYіͧ,/>dVrYzPISJo?VFH !G ZyWW&wJ :oUM acu|%:&Y@y*QNhRe 1f :4)R\e= VɁS*5CҏxУ^)L@-&S^ZEқji U UM8U7|ZlDr`r J=p.X|2][ʔSemk2 ȧG-xM(ۊ讶L@ h` {ns p-?Q_Ã;i-噂Wd[op`UX0 ʔ,f鎐M}ߧYυQꥥ\Z0wuSn:NрN{FxzT9]K&Yd_5Nl-e :.{mfRr¨p&DrOߚ鶅ܳa곻Q0[SuW[&ӟ4^WG~G؀NmzQrWdQBeUVXwF]sR[aنm )ׄY߮▫Pv*m 4̊G@ۆzhhz 0ݔ__?<㛄QTr7zFo :n/c"@Rn\[A\TnYmHK@ JnAW2 <5=2iOߔ2ݐ}c  ;׽Qk]OSm#͸a/PknnސY+0fEhE:[@,e[G`y ҷ\:{VnUF]+Ֆ 膼5ݻwx+|s0cO ZY)7PuFVyʝB׀1r i0Ϛ]Q/7ˠnjnm&8}E[V:<̜Rq:WQ&SO;;я~5=bOabުgݐ"tAT$!?mXMw֜am[L sa?,K<:!/oY3:]uYk?^s:X-kᶸz tCnݺF #8vB9 E y̪j i?#GwĤ)fXZƤDD-k08ނ>79_h> s|%]Yǔps bzO\5zY[0*V~d¥/Vrϗ!^&m-34!_1dQkrcŨyc%)8_9*CbCo qc[t 2 6Gݳl/,do-,8YіOyŠn=b/E|H˙⃌ͼ/AeA>ū(clX{)CVƦ7 {n:n7= ܽy-0 "_%\#wلed[k§7YK%\@ͲI+H깰n|$%(@dy/ q3K8;j{[ԝ`a=cjyJ/Ԫo -kXBZo$RX^Ŧˎ9_r8|[ 0Kꅡ { VFCTY>7ʺ<-$лeno0,h.,S-d' 鶨\^i `>6ezPХy/r9r$ D"V򖪽Jzh.H[FFҖn:nax@d)F(3+d ˤ{"5Q\8X[Ο:nY)(eۧŁyE F4 hbsvڈn*mr@7,}Pt @Ū P.ZY z6[ rLcGO:F"m dXRhj VNQ)\:ꪴ9 ds&fFӾ8c}.}.TZoz@2W[w#L+@UK'a*bi2V4y]#`>-T9D9dQ7AZں\YA=.g:&6!~ZM_ouݮmz/owܭ6>X#t-՜n4Sn y)Egj5,  s+?\F*@aKh۠;b;(BziA'+IV6z @nwj[ֶKY?鷺 JS9) mY@?1&H ݘ/:j7@IJ"H0 ݨB0EǠGuf)A& 8SP/Xqf/݀1zNEQmsܲm ̪趆l./h+x$kJ<5E2*mx qY;[8#Y6&Ds܍'LzK]VFzJ B(1`GC; [ 2?s)G%#T>R'c64\7z 5Y1VuuaC1) ?l4C2륹FzT0O@g+DقazQրެ?Qo:T x˅2$ePǨxF7wU1< +D^u ֭Pӷm1gֲq Fd>E+e۰z{"4:Phi,cc/y2me-ަ e⻝ 04ls\Z{Pm"R-U 轲 Vz||wE duU-k;V2U:+Kt|= Ǖqp+2)(]{\@6Al1 9\&D%mĔ nvz~!ԀL+C:m1ֲ~ 蚽 βzNXkfo<K#CH|r(iPll5qK.HܵKCX *3'zZֲuoZ)]9oFMo+[~{6H+DPvU8- A{(,!>hPTYBT[5N>Ӡ X. ˷l؂qe5#_.;yTU'L@w'~'__detF<Xjrk{iqeU,d4Eڅd8u]eJ/LʕŬ= kuz. #QFn@_0i{e!?GO<QF{[q'7߷yk <"!H܄r֯t4*,ai=W*-HW~e*#d ʷߊ|Y3/FWQFzO]Sx8Y&}o//x'|-euѬb X PMUv -0˯FPA)-R n e%KEa9@dPe}zYoR)y[W- >0pw xKmK۝3 <ɹ bw wAl[P|c@nC:Y(!F׆W eIcg.eoT1O1@`zXV n:ꗧ^C 66Ai96vrdzC7o|cGhtOWX|KgC׀3!&J}˚< ] P pv`ASs[;}֔ʷ,fQ2w_+ ]A#n=#If呭qfQ}?wdz0l@mx @.@ކD  V14Q1BR2eGp;R oN_(YհYY@ ۬/a% 6rQXeaK}QeT' )[֝mk-li/rv xl9艰riJFٵqy=NEf`: m9kX_Jrt%jlz)U ^ ,m|_~Τt 2 SmGھ\ޟl9di-[>fidWNckb=g1Dg| }Vrth'u%x~ #k|D 8mXl %K$h o8Ft\$Wr$W^KUz | DzL@l<-- n @iW؆ :,}ХK#[ƥ+#|qq6+H#؟E@wDw u\FyS%bK Mڈ/I{}L :Nѐ2 IDATU;*!\@\Ztǀ\6dm1ԕQ"_ * 4|=C\^ʼPR.@hrBX@5 q񛚺 (;!,.۪U67j#`^7Z΃|ku   ݨ1I02i(B 6F> u9TvWwJUoJ " [>ro-9`.]΋9 %_R@:B+c\X,j0IRK]iPf~iAg)#me eN0pТIAÉ-(Ǽr/چrt8nRI:Wѥ[.);d/mi@F*2}dF7+-ADWz<{h!2jݍݥԵL$ǔJ8kP@8ΰ peeqp7Q>xmH]3OE \5)Lei ؐP}}eKңnJ B(?2#5wE{Y:j]J25֖(p1J'/p{05|*ΰ9m .U̽?z}NK-0LH2[m^ha^aY=8^(W~_5˳K2 -ga=x,,"`,A2p\=`XOŨi/r~FepրN̆/ `EL@m@|_75gTTm݁9 n++(f"`%)Gp`:PqH|w޷0*({+WEd[]ċeS&O譞ׁUg].P_TeStE>[ڷ_ ɵނVnְ#8! -PFL% M#[Y.; `x D8F`~D+57÷u;nW,YR):Cܛ fÚ|@b g:a70Laxr> Ć+FSL@(G˾+uozj%-GC.+F#юws2 dE'+9 ƒ`@Ɓ21. e9fHd@9[roѨg{2)O۩ ;.٦ۺӬ|KG*-LJzPF ,@ !ZB>0KdK4|sHഴrh%o\vv5nӇl1 K^l۞2ݒ~?8~8D`H/iH]v+wE\&1j,\޽A] G^EL%U,L߹z&.-hM/jK=s a.4eN0ݔ7x x[2>{Hކ[K6FyۧBӿH7l,fVsQ3#`!Ӂ\ [ɗbeP+Yddٚ f0ruHƬڴo&L@omo{D@/{;'ó|0noOy5km_g9[΄?+j8Zȸ$%ermµ #Zm/C nZs8[ q+}?lIoWX&;?q{7}Y|'cO!=1XKk _#o}H{UGY^DYmG"ՇEf:D@3.9AŽK#B:_֐`Fе pYؗ_׉g{A.k|ߙML@7}/~oy[~Q|۾WjxF65jׁ׆r1vL=1|.O#(~mV VK@^K0'7E0]DYڃ.Ռj`O. iZ@WPVtaU4DZYևXL0Z/lƦ+ݸ\%vy:]p H7Z]n;0JÒѿMlߕ֥P*b`"x.lKX^{.b #L÷rs`!]Ksڍ|Tru9bX;/N7~HRo9îL@G(SYI,5}R!o/Oಞtsi ՘ِhكYKn.23C|"LfP rًaLBT8EQo&.w#A[m3cך^a(]*yu7-`YYr=%TuS220 g ߙ! J׆3]=/x/\K.LW)b$q~,{Dm!#C_vZ熫KouSV2_"@+K4T:%480ipP5b@(^=Z7d4ċ\Nv3VriC%u:Z Z;{r1ͨ_8w gõ7,fq½qAM tA} ,G9ΘpίėɅLµQ`lu-zFoO+2WH%:lFʶz~j_@o¹Pq񦝴<K_np79XˁpC^cP9 +ZZS9fYeuzrνG׺0{uXL@ވ[u돂x 0vEqZg^Y? Z:&#r\]N60w[՗:טq Y4yk\#`ӗ~{f ؽ::JtxZY&N@io+}|.1x'M+ҀaEs{t;>, xn` f:׉p-Z3#6, WFi'{d &gI| [~듾=XS_4eKulܞn:^uwV*߾A:}J3O8M+JsHtG !P77p A!gވ>bxL|_ d8 V˽'sAD=V ,{Vft#Vmo[O3c U.D`˩vŁC| ,\w ~DAD nq[*zF oY~ӼqyHקӖ5Slalz 轢{3_n[(-PGx0`G}z]77@ɌLI n?u3{>a8߹rb(OE\:oC}9=H3˴}){{[_:*/{gqmɃtM;jjX@Z.&:WR>t vX.w\;7Mx`n?N8e3[#4,fdJ&x#=t jQK"̺p4 8zmkݙ|34Y`zh@e {[ty$7[gGyAx@?[̸n$+څB9~%akHŘpL5瀇 V,@~Oρzle q޲e|'5-U)GR!Yܟ³1/_{ ?[k}0A!ƣ ~\wsx,+8G\ AtE?ȄM(ۮ t:mw4`@c@VՖ,o}[S?Sy&~y{ ]bb\(Kmbyc`ybŴX\ұ|YsmlӺ5T3Ȱxu~;7x+:NF9Z: p~@!%ˉr03ƃ̸8w8zG~G܁p)Xٻ1H'` do!X0t#0JU֪?x _;w֭[//ƻ{.!2v0bYeֺ{ g92/BBUI~l:,jc(gw|Jx/cAv\<A wAt K܆2UVr dR.hkVϰZҝ xt,=~~oM(?e{,묷-i3F3a- a10!}g_CiM8˙\bg,=s p^ \@|0@b=:A_^{^{ K7aK˿O~m @V=@ );kpe:Z9p4 V1.p+h9]²g{pmfG-Çۀ i x (,g2fddˢXd2g~oc#-ЎVjKggg O?łLo==h!S{nvM0n`0vAfoH.3Bx$a;۾ʨ1z^Pu@#@t/?!% w][]mo߾oo#<1hh-{p߲m@99!3pK؈cxp׃} 8{ɵ'!|.>3 xCil_[GOgZWhi{M; )޲^-t,w[[W7}{O++7/雾 |w}~}kmy(7뜰vG,X~h{Gom_sű tK?|=C:p7!W? >. X>%epe,=WueuJoŧtz+xWS^o6Ope/{[uO~_5_xxk_ ܼT|ړ~%7.ue=` sn!"CKm6 %S+8Womb.qX8_g ^c 89sv8c@p7<zA= 7Ag ΝX)|X;r\ `9|a94k=ѮNMoYuDaҏ8>͑=S,yկ~5~ykO|o |k^3_/|1g(Nr֫=tNԫ8Cuu|h[e~0ɼt_q\#8οH>} uVvΰa/ ,x%z/у]ÑO|5g?}x]%p\<z|%p Ɵpxl-~9 H} QM:O=|0ҖnޖU`_qyI'?oZno/z=[(ezvRxCuѽbF+8,pnNs=p?a9f|+pnي88 sp{Mt3.ْƥɚ0] 'io]萮|ܣ;ʰ#QXO@ou߷= 8xDN+i)}2.r% #dR Ua _'ୈBW BI0. >xV\]M9\p c>2`U7 ߃Y еi}'dU_U z!t^c V8yp^|=KW񮴐^LvC,E%v IDAT/{@y'Q)/kp8.k#Q&n-v~3Zx1%Uz@8KNfQl5hYqV)Sn[m8VO@'yMoz ee+Â#wV/_k8nf҉ѫ7>| Ѷa>M| f -x .nיq.`Y|ywXo [-HwY#nC.F}]I^~$ܵ`t{G݃]> VV}{mҊ8Ê4$8_{@`4sOsٳ*Y8 sPE(!塡-H@{8jitp>S -*vt { GگV|ݿ~ q v9s@M"rdz ~]?"}j]uƭKhG˱Qo/dzX^Z_ڭkxnvжIAgr,aw\×p ̸z5YVb:D۰sF̝ GY_ԡz[LNHϘu9>@OsLsNUhP8Tz:nvrhk -8F:j}VvUHHcP-6. phǸ梛\J{’ JSj!.jv umHAX}_rsV,rr}@o ݢ-7t|y{lkyހ+Wo>=HYC>.].?bssf\.3n8o=l=pBu<N\º+_=/gm{}ioӝZ= b?ԇ)^&wuUfuVz۩>> g0jsCq@A-?JAg h9Px雴&KX˫ Vg>djf`ȏ\S{UX[ݔ d/Gch:=Em$kVwK(G0~R]u9o*2_]wOc?#9\ݑ\\UՖ@U `ܬ;0WMںVlƫ*лEσnK#ݳU֪U/ `EkY-s%`q  Ǵ|.p9saA{+ 75hXe.1uSss5R64;){(C'L@[8F=s|X5[CBZ8rrGaσم8.sdWo9/ACPCXԬ`]v.U^[ZFt{⭲)^&w;Z6Е{;oW)P. XV.h)CV4p||-|p_ k^|.#%?ZΗYRZ=+l9{]u-큳ΟΞt~RR>C,V']'0-lu2u7Sq|Gl']=KFJ'`~0hzxL@ ]Jk4woȏXljZi @-iEDž/0']ux+9zdAǕa}LyG;ڼ\1 P/@^ռܩSwi+U*^tمVTm*_duo;>(lڧN綾r f9Pk>x .N֟RDZtH߱ p]\rvH_C,v]_e=]O?ez0G ^ڐDv,oܕ]8P& ih3+5C:A]~$KY)\× iY78~5X[ip,,Ń9i Ƿ;i5>˳lX]uY& ԧ>7pm=k|a ЧSHʏCOUVUsÿ:rx $@!GtiX99ZT.D qe~JeZFh^>Դ;8/:&u.3ev'L@W ?? ~|c×~B8j{:Ti B>-ji}d]OaEb-`ⴎ/f$k鋖c\%]ZYҒb=QaAW k|5@k*ozRfݱk >q+x;Ywo;~~ `V "V7l:AYN0^DDPpy @=8-a mQjpJ'+|VPtk p 3j!?8. OJ9duQyew(9o/`-f&5ahI 93ggʦ@s!΁ήC WGtL鏖9N":-ʺ3>j0+JfƅgKγRM8K+{n6z6@G@BC)D}#A:}:-ii9XZ֜Ϡ1tlSR>!ӧ pgȖw7w^-`7}=H[vƨ: 2 dj|%lC2 iܿY@}'82. iIAc֖tswiߜoلr p^|3["t1mU+t]K!^nY'L@ @p.v^nP-hukݯ {B޼1rPUyR P=,@,hRvv$YۅNAB|q`S >MǓPk%lѠ.sezD7'[h Nt!/伎DZUTdژ̴҇t!۠@ kZ8AZr_E OOI\[9#[B]WcJ:E ߀F>])-?>c΀G; 2}:VPaủH/kR$M7 t5#9&P V3=➒<K8USx`[FпIQYO擯j 0[ֽed#qsp*es0y# rpk'륿W YVh- ؅ Ҝl #W+T 77t-Vb`=n/?Vmn!랺׋l@mX6zX58XW򄦥{$ڽQ.&5!pnU78j}$\bn[N p~mtj[齺){E:ޠַ \cV4A[C.|2EeG>FP"}p!3}",,]oZr,gx5^SɤH$qbc6{uM0[,@k+=Vvp܀3oZ+KHwG5 /ٵ}s,yxde Z3>ȦU͵J0־"V@zI=#P)&wfWu)̵nq-p[0n)H-k:pnh=v[1?|OZצUr. NSr+6flmc^NcZeߗ-x -kp ?-@aҬ@Թ^Y.XvV$(衚1װ22,\x."ф@,r}L@ 2}6X^y]-XJ/QX+_JĘ s5)57b0qQ;~]X 89$m-*Z׃(4)cdzҵ w \hof5WQʪ,A9%KQ\Zص]Z0[Ż_;,lS^e(~_iEdsպt#= 蝲@ gtv4J/,u!M88$̹p}Xt(-hnVb`V;uK]$M AG+(;h+utW>#zZ/lU QXJ5A)u\]7׭hzF)|{Q<^ȏ#>&~[Jkʂq+"16\;eM`B^a3q g ؕ j.y vqy ԥO|AGhϺ[qj4 K˘A.kvjWfL@7W~W׿GT @pMym=rؐi+? gJE!"]X2- -Gˆ.k5V=Ccj8s7Au iTrQT}FԓpmN-uon˕?q,toɟIT FГ^UUvO]-oRS)nB$l#ڲ\+D,֖g(CAh@hgu%u?6uVYf|rِ+׽u ~< ;}CگZ>| <5@lٖ-ъFoN@Mo~ `PnYٕo%̭p޵7yҖ&nR=j0Gup 65u^b \wC?_җ/WЖ( f1i'(ާB^SKL/cy' \,NE Pu`@-,aըSCjhiܽ79߭Ѓ6i kۢ .t:?bU bzJێq$ j[ThU$;YZLHY*p>@;$Kvi{[`,\03) zhm/14t+^ﻪ2[ `FBă6tUU=..^i%{ak?GvyQZ]jKt dk`i7#,z懆Sy~E^e$tϖܪw`( J?m+!'4 ~'00щ%#<7 <UC9lkLJװy۪YlZuc:22WQz=0KHVE@hӷ斾Z5ˍU1b>o,#]##%ĵR鞟ڲo ,a=ƆNGvOezgpU=h#zu5b/[Mb[ BF|Z}, ,N).̣ĥ K[GmV~CѲ5-kZZ bh_ 1t tu 0N@F;N>q<.YG!r};K<дͪFnylo pV8$x_z=\Hs#3@lX[P֖nOU2[F۞^[-ȶڛp@_~>jʆclPBYf[@޶1LkTzJ >^l-!p_m1ظX쿺-X€NQDZLx+sVc$Cڅ8bP7'bnWV{ciYCWp]쩔Etu+6tZp*mm/vy8cvJMsպS&JJ~IQH$Y8 yqJ w'|iRz=\l:!97ѹN)q44.u8Z+yO;^oY2W @srُ+VA÷Y^ ٳ=yH1t )9X=@옥r8𷪺Ndk^x b n.91R]ÚbL{ Gm3ezX4T~+,!ޛ$܊hcoʐ&rCuzu[E>}U3| k4,uB  Co 2gjXY[[czY2W*@$C&i`QM~I˲MP|9kr.^rWdppPMq,|ДxX@;r{=ܚwPڹQgՋgU {m \ Xl 0_ FDPpF͝x3}].>mpvkQ.?0", ~df]T뮶L@$ ZtIDAT؏ȷ@#xԲ *_>"sfp -Z.b^& c 8 c\腘lP+ -KmUo/ƤT5d8v~zo ({%8܏7 8c7>,۩hrD4`lj?6C |s&,qyX..u;-{^ϰbKo;hM8e~:^{ICW+oob*>r(7-"B ML "1 #&1Fc` Q5b'€!W Rxy+{̚5.}fϬY3:蠵ɣg^ 58,iѦYTZvxf[t$hZ[[~1k׮Emmm jژSJ2'ON% "ռ;Ln5O,&!sՏ1kGnD*-&ƅ2W;/VHS}#X>#9 %:쯘<<uuu ӧOGXDMMMRh4Kǚ Ȯ2 jŒ ꙠNdvQM9xH 0:k[2MqjcH~LPcl""}0Dzvfz(K}q,^X͘1hnnF{{;X.T XXgY^i~BByfXGpU(Gy-M \$zQr3[C" Zڲ<xWV._>lHI>]<цɓsNىjkkqС?aMd64cyD+Ɓ4vۉM+?$c9%hvY"l6(/ΰ odHɒl3NO0y4]wܽ'uВq-`ɒ%ƶmⲛFĜy3J%Ȍ+;|$ױh2Pq/*:)QԢVHPH y->{tz Qbs:L[?(;mii?%rzTw!!~FGK JvgdA{! &WyAM`O"B[K[`ͣli$-6]$H]`NqԹd#KyZ}X9-SvߖC!|HЉtHluňa[ԛ<.c:tNWW>/gk[쭦K?8BJQG*ũT:GZIw4T¢jEi$P VNf6Żj gZtNmfq `$ײ]J)g'LB!N=G *#tYRA}Тgi:aDi(MIi9]#Y'/Ny@ >(*{hؤ mHpGT}V`#GyZ:tNEd]>Y:L;9|ܟR۳7/&z #HGs.a2E=u!GV#xR}6(TGP7pVʭ ol!by@T  +eAzyJз%+`j>S<4dt"q 'NurmGunHdb/u` T}f\uLlm}4[ ev.(}Fi*y@At@<|˃yfOR՗2mHNpmɒ||&AkL$a`/w2m)%ڕ: ]Ni&++rAtV@d]u D_2ڗ9j+# )Ff`_[2fm˶^R^llcuvB@<ӁQ%`/)eKSOM'+(Xڻ:ÍʴIa.%>6Qd)hm:k*KVhBc?JovyrOgMй % ?mHߔpWQsJ@^*ωJ!K!#'R ȧPo(,'7!=}IB-e|OLI ȖIo4ԶԹpʵ׻ŖLI>P҅0_R"h`&;}A)|\B[zy~5(F O¡ztnYoJrPllR>UTʰ6WH`/OEc ܼш6?<|)qMx<+IyVߴ|O*#ͺ|Љ,_tqʝC=Z,1IRfuA<0n/h|Ix1~_äHވ2lD:| &~D 0?ӿtb촉Ov;M?=GP=V,#bԝQ1 wxZضEk~b'b>污YOxJ(,j?@2/ܞXIZMkUЂ~7wo`޼yai _>Fd9Qui6гYtӦ0ΒcTO푴iq٤rk9 |MX'i>P橓z~I-':\i&yamwPՆ97% X#{5H< i&fD6`&hж"j3ln9]r+K+Q]WzA:::ގW^y~)9gyp=`Ȑ!1bFQFUtYΟ?_>|_+qy~b}]MӃ&L8'OĆ p_o Z[[~1k׮Emmm^x_}vY>Μ9իWcذa1t9Ӄz 5553f ^zm__|}}}2e ֮][<fΜ+WV"š5kpY?ׯX_OƳ> &+դb-[0{l8pb}uuu 7p"jjj*|*?hiiqpcǎxG҂gϢs୷Bkk+S~"x*G}G cƌh_Ye\t)u$t) ākƌFss3ގƊ`|ؼy3}… `|wp/ҩS0k,IpIx?Z[[}*{bܸq:u*+ÇqwcժUxqqL<"}͜9?0"<8quuu'͛1lذ9\ڵkzzzg ǔ)S*W]]/ܹsڰzj̙3VӧWE~ :k֬sݍm۶U| ̙3`Hq,_zӃEaѸ馛/h{=<={6u֊i޽믱bŊۋEaرoQ~Glܸuuu8s ~m >b]-7 < ]^W1o>lݺƍȑ#qQ8x Q[[M6ҥKhkk1c ޽JU9hVBsʕ+Ԅ_~MMMc H]k;>/WVЦMp7bٲeXb{9ѣۋرcXn.^Cᮻ^W9hkZwFWW.\h_MMM4iX2/tٳ ͝;kPG^^^^T>J9e7%~IENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/hermite.png0000644000175000017500000006236612253362407023057 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxwxTUߙIoB RAQPqA@((*uUׂҤ+?ABQzh! i:s~ Ld̝pg=ɗ3wJpoB}PPPPP0dddddddddddddddddddddddd̡wp &_QQUgĈ/i梸G..rRS\O?D %7WWf1%q!Dp0׬㫫J̟?_6Eq1\\!DuEHШ4B5nM2)pGwrrϏ<]%o\E.r] wrKV;dxC >'00VVEIڞ%(.Ƒ\<\uj 2VZI"۶ms$E3F,Aq1\\-ӥ2lCjC PSSs QR-Aq1\\-K\ϻX&i{G..rR[a]6-72@5ZsΒg q"hy.B *=h9s$mE.\OS^IyX9hsjXnn.{=KP\#x@s)6J; Ճn)P\"hy.ԷP]QQ\#x@r5KqP)P]QQ\#x@r)9XBMQ "7ߐTFqsҊ-R4D.в\j~ERf!(KqmNgggHh#ZKVCKNNh4)Chs*@Zˣ1ZZE*ZLGў"!*++[L4@S$CZQTT$KK*Hx@q)]%/ߐT1wAk[REJZgP1Hђ-R4D.r\2dնmXr%555DGG3zh̙;#F;֜"!5''GghY)Q\"h.U9U.=vhy=hC2tP Frr29 >^6Hкuk-Vm/Bq1\\-vH~:^-)@.w1b233뾪\=d"aHHYYYI^AK)Hx@p)Z7oeee<3 4UV~z֭[@RRRpڜկ/\qy 0\_S\ҹsg݃"`{AYYhz9:tK:KLv^tСx'̙3< &O,V\Y{׬k۷Ob̘1R( !8xdmYbCw);]&}^ 9bɓ>&=i$&MTիW="5R-b QJqE'uP.(.Ƒ\<] vChsnnn*EB4D..j=]BfsssI6qm!!!䯦Ȓť!rv)q7*++Cնܙ X2#Kj%?޿R-q8h$$uы-Bqi\<]7=hanA;zZ(. 83x-­u%@KRbMXzZ;pmi Ў\l&KCRh,@+=h G..rtTC"]=%@Hh8rq8KNpO*{-q%;p G..rti,h%-,)>@Hh]E7rQw_kW Ϟ8dHL`` YYYk4D.x.{)w/N rgffԢ Ж 樨bG,ť!rs`GXzCӑCHHjCFq%,:b(. 8Kc-.-)BG,ť!rr)O.G{Bg7O۹7xA+IكvbP\"p,=e%EB6@;ZV(. 8KS@ вC"!HpbP\"p.>|h G..rq)Q-[*1J kR$KC㒻>5)S8uƢkSJz(. 8\GO=M Á,;QJЊ(. 8K/tST9$f%J2Xj2@ ˃_uF!((l[lxc䬾_IAVVAAAfe^Y[ зwg37Ngޟj#$$j -rqbQ\"LG|47rNo|~ 7Ѭ6CPP6߈;b/3 ImHՃvb=P\"TGh5xzB3͝oÖI[;nmC+gƶe甝l_0ej'-U-@qi\<@.W ڭ՜]f? vLඃI=;&'G2n8tUM!UZ{4D. oU| +j*z ˎ,#U$?;B{il;bg$ a [:u'1rH=gmHY$0~ [@FFwmо}{  (ϧ1t)`SN+r$;Rv)[JDi䭌Co[vMEnl>-%wMQŖ.Bl!Cg,X 6a6mĂ ?~< 2_s-tߐRO@ԮO y<.ʎm rޡ윲ԝ [:7YR$.rٻw/3gάck׎ӵkWj5z'N{nlΝ;ٹs'}a&1+]j'ހtzq:w,$ni{ oxE({\*$-$o8w:N899P\rҤ㋋ tRDa`.bº dhAܨ;Xs#<Κk AYgd k1x`C߾}ILLdڴiT nER1m4۷/{aV^j]rW_`ӓSj1rv LNY3`Kq8Kll;2@7gOBJw~̧?JWŃk=#P(b˩S4hgΜaԨQڵ\bbbصk#G 4SNImKu^5&wu^x`!|%U%~|vgUꐍ}zz:<7t'N`رj.\Ȑ!C8tSLA7mm)=l\:-ë޽M>tD :tbߴ} d-M RF{/55UV~z֭[@RRR]vLlc=DyE0y$&tկ:tZOn:Yx1Vuf͚5iӆk3Hv_Zݞ]Lis2oߙmmˡCX~=1EEE !GYY>}O>8|xɓʕ+;j(ItE+v6N4mH-hi.WǏ˱cDŽĚ5k̾B..5kCT6nA{ ?*R A!ѦK\[.⫃_IҶe$%% wwwRƍaRHJJCK'kY#Nz񞏅jJ0:DQQd>r вMq4%㖯uh4T*w =mxSYSiRr^ўXxG(//7{˽ܹs)//G8Rd.4 y4u<<:TUၷykC\=h!jZTWWݩEG!_E]Gt @&SnZ~jWV'Dg;a5ZK' %uRz`LBSGדŠA| 7}/}M/Yr\d^~eoީY ggg-Z/2ϟ%;V[| sSMus,Qpݜ"<<nbB6옲'b ,ۗΧ>mtiJȒ !xj+x4,[z_z/LYY=˔:uzhu/ Ҋ}<{CdHb $l^ƅ,Ag~2ꆽȒ |l߾ݻꫯj^}Uuƶmo!v) ơkL^?'~!G/ū%@Ż е욺7l0m[ 2ƒWc}%}x>>!xa)rzAgPP2K2:(_7_6[Fi%@_㠫Z&s84`."`_$شiDǎEU;_ؓ*#e{8?8D- bgκy% Mnvu\Iq eٙ.^`QƸ | L'lo<*j*$2{~NNj/|uv8;; /qc ?1|S߹}df1(Et8---E @|Szq7 @|qJ8Ě5B=G-<5VkYB(=hIH硍qgyX1befOfG*BiZGYu kJŇ~/HGjBW ]N~~0S{MmKH=Ch)`YNKKwLGw|^虵y|٥uhT?>/^Clb. `̘1ddd0|y-]23;,^[(q+߬~욺hh\jWLr&!XkgdUj.{I-cc]G]n/C9)2ͤ2M,ZƎKǎl.,uؑcǒ÷~k7bmB&uЖhy0A@ '4z Ж/fIp`= =Gz泛߽v[+)M,%sQ& =+qtƫ89ڐgE5j8_Ar*R+HKd~m˟V[q4R]]]={kcYsgf͚5|<㍮w$-7BFI>,.[DM`"\%@9 Z-Fy$7g ]2=!/\NSKu{pû7NL(+TVMR_ );^F2祣)nħw}ʙgТ%pI\"CjOZ*++JQQQ>הzXulhak\DпG|hٕ.z^\\tQ#N'v+.炨ȨE2GN"8@" Q]lXt_Ӊnݺ @ @t޽*}e㩍'ms~UV !ۘg2(A2; @Zl sm"NZ ⱟE01|pq)>X,?X% yBXjYcwyg?,Ab7Zzѿ 6[f\( F]j(k>ADGY#zpssw5r J2TWW۬Ss_sN:tbtG9=4cR^77n{P7ָ'_'fVM?{25kS_^竱EqNR{=[RF_. lɆX:x` qkF@I\򨨨>)k5jā$obN`.z^џEbOG[d,hr7[RS)QD^;DdQ}ɾ JN]vصkusNo:(J+?, ?C1 F)YT IDATB!JKtIAKEB0Ph,7'7b`@2髛xa U>GAƂ r"'t?<Ѧ}l%ȅ%EKyL7Sn/i李+3m- )\Z׋)RTQS.s#䑦wn#8JJ1ظ> Ox >ym R$3wc|t=N,9hzbLI'9PtY@,:/l޼(?wBH~9]!&.fܸqmۖM65X/Qg !Xyl%tfՈ=u7=Z0z|D͍BtiK?Sx9==`xicX cG4rE;#B˗ @Km*E5\zqhJu+꽮=+&}TTK;̟?_bܸqV?P|臇Dvivoqĉ}]If(("!A^k#!zJ#Co`; 2{z ѵ3f j m&h Mڳ*j**,@P;ߓL5FCDDvJr+5#voCHr}Ƞm۶ϕ.j-}JJqA0,ܝݛnLF$'~|)ei{OHIThb!)ATO۶eOEEj%)SwIޮ8Tok  "==!tҬ2/ z>9v1܊/^?:{[.!--CoRp^{ze&ɳIN\,-f1j(֭[ڵk8qOΝu,9^#$'[^#e23 6%寠{++vx .]4 e}$++vY%88իDN4*.w^q{r$|#j䷟-bѾ}+*"ĹW ]S:Wm6[պj2uA׺CęE [B<C ѡ..M Mb"8Q8k6qEϞ=k4RكLB:Dzz:AAo~% V]L_بc)'pd ݾFFv]o5y+(oz 3nNV15kְk.6lYmuuWrpziMB|S~FqmѩS'Cbb"z>+@FPy KR7foGJ w;WDd$xyM&N7izZŮ R/7Z˕cM]i͚?pB/HDEx.uO̯-~9 ^z%/14ܜ.h+vOM,wzwkyx;}c|=^Yb%%%%@;RIX9?~TuMW<6ek8swy{ۼ=FuS7=ŭmojrB.\},Y8<0Gv]S=rɩS/Yd oJM?Y1^g7!\ Y*]Nj'oML@L]R!gfAlkyS]-8蔔oNii)={[n@8ݻ`ڴ [P[o]5wIOO <\NDȾ=&j̥Pƞ/+ʄau7) R,f` r3ډH(kK_R* iU!J8X?>'S 7Ze*>a@gvym<9\,8.ׅ${77ou-5Z=k,8p 8qC/ҭ[7TB|'yPAE+m"GH>>[16v&I;w@^9> kWUiM:W m /$Zt[Z*K,~jq{~XʰEjjj{RIx%wqM:^ݪş kl\Ym4%4G=Idd׷\ŘUcĺDyuyڛ7oēO>iцI=C/N n 1kgX Uk֥wmX XҰZffЮc=&1gΜf*+%v|dzYuQ3Ok{,z>; E*N-*+n9. #FzMb'2sL˹;,n/==o7dڶm˅ *ooD'''ӵkk/U3T pt !6T.AA4ԝ,?u'ֱᏏcaB uu_Ņ|8hœ_WpII Tv ;% |#xzBn'aDEA``:+_h"̙FU6V_ŏ'guǴ^Ӹf}>5E5|$,GIr=A ¹snRSSHMM%""'Oj*֯_ϺuHJJۙv3s^s<&tϞ=&n: kגv-ڴ4()!aظB;s&<  t4 ׯ58N cwߡM2hGʌVY%?T*. fʴJJ5Kb9nnn=:)*2" BӅVbرWVV&&L f̘`}k~ YvĬYv+̮}w8U(+5MR%ݡĝK7u_[J]=V|}kqB1c@,]y cXD>]2,t W=5JgOG!@hϕ ;%2guGK[wbE$q$ap=v~YܳRQT!rԥm?O+VX:-6Qݝ+WZܞ˗/7fB)X:K+穹TC0|ZT.Wʭ{Nfrdf~9 ;RvZ֞X @gΜ^?.+56 #-0S1 +55]~is'r>!T&'QwĩmhZ;/V]šއ8Qr}Jðmww4RySO8K).kCOO<߿?>} &+%am,d.9ЙjC*k~OS^]ԝl>Mg6s蠸S1m> bhyޜS/Ԡ ぷޖwp0?NN:I9)]h5*k*9});INc%$'Ýwrg kJCs>+B/8)j j0z В  #!!2zqNڋjj)++ںB/H@:~ّ6F-tTp:N8 eWԍz^A?]7y/4D7`oY]XO6FE("_dj]5s1ϑ#TugUOvAlXyZ)G%,,l*++,ZJ\?..ܺѣG1cMZˍ́ʺ6.e8%xzHTU pcAAͥ[V$Ua (g]2ڔ/3# W$CUӱ|rT*'Nޕ%'}.HK"!33g8[p3g8#G8}ܜp #==PBRV^U^j!]URY¥KVRj1L9>8;d 0~߼\ DEC>Ķo&\yO~a̙ŋy7l3*c#in;Gٳvaor5;k m-̕+#3oB@mL4{QQޱ8222:thJu.MZCnt Ndd4O4h*B_,/2y+g_{i׾fb'~l۶8w1JǩH x\0[Й3gСU=Chk$CLќ,,Qs k<6{J 6ޠ3DGW6mzxҥ^M'HxJmVzjwbKqq]B.ŵ;e/;Ɇiʍ11 BC dS]$uѣo-J~~t7Ge?К2H~!sϝpK!KS~\D $~ýa^|`XHIwv􅆕j5JCpаPNoٺ!Ol"{'md4Fs\T*O{/'Ɵ`Kz hZ?ڢ<,Z5k0m4۶E _7n6|Zϧ6]z!(Ξ=KYZyy ^Fе+DE[=v\߷Avݒ%h˽j?]9.^7x@νp_$ir_gxv7o41)Sh";+]^pSd/5•{< D?l:&EWS =zez>2M 99~IM50B헩m~S~~t66 -vh [^τ & ؊؃d,krVI4AcLN{PTL2nȝDwܙJOi@x]K2Z ߡ="ѣu>TtqmJh(gfBt?@،0"^h֘G۶mپ}D !Yx\PY^IȔ:~nB8k23  n|;mMjT^yu{rkmb*pGX.||ԷSIO:^$P"_5ancʔ)l߾ݪc+3*9i7SA]ݕnK/{l~[2jP!ʬz"Ν;7y\S.p~pA,o}EHHPŦMמưKev89s#Np!N=qJӱ)R%ťK$VEqbobߒ}^/QQQZ&.r[~k,Uxa IDAT!00ϣ= RLl{nE*mFVVÆ #4ZRZOOWۢvUs및SH5=jDfIn/$DNM;EMI G{8IkHAc4"""!v ង3 ki߾=d֦TPG2|7:Lu Srirqtڟףxj:Ϧn*ɔ)S,^XA :ᡇ)]g7OzMGh<4|!v3 k1e$c{s~G<==5j]]]EDgwOJKq؝?G^MUV`=h D7s}^9r͓+{8ʉ|j]#8AgbJPkduo)ç~wئܘT3vXxiÈ-S]]͊+h4?ޮ. x >@6oLNN]]2q2@۪HеkW]; tmĚp_.ZnmWKKs<?~<555\Ү.Ƙ 8dLBvwޔ'1Pzx[#uU]mb rqiGm9ŚsQpm"!@vpss!jֵyEC,EOQQׯLJ#GR\>}йsg ŚsbW;z!-Wnd}YEmle ) ?k׮cǚ܋P*jhhRQQ .^Ch[ B~?jj{0.;Y RbĉQT,[eomb-v9wB>mY$ WEOJJ ;w$22E bGdd$C !==;vZ\rI:udYږEBhPXzSxv&  ?˗/`ҤI}D^zX#!{ ];@!pm"!4^M4!ٓSv 1fYvdhRwnYږEB0>CeʡR~8ӧ۷EyJ9셹~zX]= (4@ۺHhl$GTeW󮞸6HQ8(To3RM=.={p|}}h%dz):uTZ-&L'_wаPذxR-O5K ?UUUX'''ƍgW)%Æ #$$[6G-\J$z{lg}rWfԨQ|{yo0ZHuUj*Z-XD:[u"✭hVl]*i[ZB (7q8,QBH~ s]'|zއv? AJ?4prwso>'Kg$%%A ,, m6r%x899!** FGEhں7x@؊aaaY+++opu1CB}Px0迨?tܥ3 ~L?cbbdwV\6ڻ=耀$%%dB%%%3?~ӧOԩSZhO&!8^ AHKK%33gΜA~0dY]Xj!u>Zcy%\jZ*<>}>jjjDt`4j(Z|9]zhɒ%fڰaCHBj<=<))IFQr'zjU8]k.@ׯ[E|}}ITVrԖ`@Bxx,7nS8EQNN,.eҤI222dwV\}6)J4hzY]RUUEhȑV mA]b njb B`` &M$"1gܽ{ϟE(L.RY >K lB۱B͈)!DXJ\8dz&7[>@Y`4zҥ+.ByDFFEL.|GG@K=ZZ (:#Z.\R̜9Æ E,Xq ,@ccy\.B`r$u! 3 5g44p,k1Lwj.bJ.DWBVcr+1Ch9BCFFee%nn$ŰZ_O>X`.bŠ3g΄z]J.eeeĘ1c$38d:$4PT x ͰZQ__^z nnn +.Bz(JDEEwb/Zy8q6lZPyޘ8 [Eȩ +.B{Fs|g74JKK͟ &lY qox7>a5ܾ}~-|||"ذ"ǘ1cOɑL;a@K mt^ѣGR_RV`E,ygΜAuu.=~-ZܽrvG s ?III@XX  T"G޽pBt:k@K&x/@_pAtk.9vh"[V)aELRi>|.`*< ChCB2*+pR{m/,, ~+VEjXqôn~&.];! !a@_Ypwƌ ; ~:NhɆBHŋ˰61}nF! 8d3$McYYYhjjZ,?F"HiX(w466"++ td@jj=\nT*:YYY9Y'551m4IwKf!2T< &Nk׮!==]VBss3O+W2Y Uz^EAd]Wa)ٷoi;s V\Xv-ֻh],a1c.,Z* ҺTgZ(d!(|8sN> ///7ENXqc…Fbbyc\,a1c.,ZPs{E=<< t:QO>`+D9؝"G޽rJ s!t:dffO>$38d#$$#2 'gx ,T*38d#$˩-.vrws ~+ܺu s̑e!T[Xqý{p)Y]Ҷ{Cnq-FHh^g荶?޼+-[A۰A]9<: l$Ydwa,bu֌nVEPP /c޼y+.rx`ԨQx"duEPP.ChCQwj<61^[ZZ BΝ;'.k.֭['.,?P(Weus΁a^E@ V&W:zE[LGDD̙3r|gpss{ WWW9r>ϋ.ChCB[w?)xꊯZi߹܌իWKng.\nnnFSS9å?ѷo_Bcr8 Y Pu%j%3Op 8z~b0k. $nW‚͛;v@ccd5}M.BDWRRR(66z=:t=JDDVsCBB㔘H_|]~(''Ǫc_5"m~mgݻG*GMMMvϚc[l!4i$Ѯt͚5͛%F!OOOrrr{1mRbb"{I,D.,,$///Xy&544В%Kh͚5aÆvϏ_TRŹe?nUѠA~=k.r +yyy BMMM_رc^~e.DD111r+m+B|FnwFZ}sǒ͞=E0=/^آ\pA.:>>^k%''[|vZhzJE*/---G͛DYYӇj5vOR(E=J---]G(X+L!Bª*@Y֯l KJŠ+./7oFcc#|M 6t*4MaBb_bT* XkGTJ_ŤP(hSHHP7UWW ~~O &BA_ד)v!?;hw&!iPVm]l?BCCq# "_Dm۶Dž4#t ;wa͚50 ^(//Ghh(ڥ 2YIXWOu}M,(Әӭ[ __~=vV,.ax|rL2慔@a֭~*NG@;|k,GzfA#""%%%7 Xjֵ7lҌ,V<.J_T*i&ܻwO'$$ǔ)Srt! !9 / 8}]( رuuuv]_:y\tKPP֮][Dd׹c( 6pР3Ѕ>(o:F᤺ ::жm:O||<ѣG35TWWٳǮs l2줃`OHXY c35Ka w+@Gwww|PՈCzzzΛ?pvvƶmzyCh{Bjtoغu+Z-^z%tׯcܹhllΝ;OE*KGX,L0`+n2k/~ hZlݺÆ G#\JE*N8!+0 Ahd0z]II Z~=tp]V^M( nܸakzuq8dZ[(9a iU,k@@qqqN`̤@111g[\Ćt]t:-\P]KS\\#Gئ!LhZ]g<FTT2220n8̟?ƍd|+WB>дEL @.J{i&lܸ>VXQ__'ȑ# 呆ˍfOȦTR ᆨtfڼy.?RIgϦI\Āt"\nܸA˖-#RoURoM͒ kw "`l=`8tMWPQcrdH@ //W\AYY1g']>#HOOGmm-jjjp }1?&EhZ\nnnׯ?h[j`0~QoKqq1^|EL>?ϰn:Q=ʰeҥKrIg.R ?ǏGKK n\Cv oookjjBzz:># +DgݻsssCKK /GW.RKqq1jjjp^^^&H&HKK3];袢"YݱK"** yyym\QQaۼ.Xd ˣI.\شiΟ?Y\nS0l0{\viKYYf͚СCQVV___ma?.^ш^xaaapqqǒmrao`@ ))T8qÝ`q]@IIHbybРAvbR\\g|g]燒AŋnSNָP*pww^ݥ3?ۤ;nݻwCVRa]"$y&&MW^y 8y$oߎݻwUUU; ;##G`ʔ)ݵO<7xׯ qK9eÆ GMM 8BIEW_\]]1|p!44z N'Y%>|O1=␜0̎pAs8p8 40 /(@s8p8 40 /(@s8p8 40 /(+N^0IENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/gi_c.png0000644000175000017500000016060112253362407022312 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxM%IV3sEd&ŋDuMU ,hMWkv@bĎ_H=j5 !4Zb ݣ*̈szaf~>Y )<ݯ]5혙3?~9c9ީp2s10c9明s1h̀c9xGcs1;3c9ј=s 9cw4f@1s1z9s1h̀c9xGcs1;3c9ј=s 9cw4f@1s1z9s1h̀c9xGcs1;3c9ј=sFw5oȏcX L?s?p<~_sYA~/"7qss933[[/7{~~ٟk6 777O0Sb钯no?㤸*~mYƟ(c58ۺϲT%]\ -yۥkrusEBLPXWj%u`vfuZcZՈ:Zj~1Cjм,@6KBwD٣ݡ^. dQb, KH\vqh0! YC)SN#GZu }g}vby~w6_ɟI}}A&_KZHNqb7׾޽sًa`WZ1E׿"]дh*??~ sE9vhj^,LZ`;VGCXAXc .hÊqxx‹ָpabfs;;vxAszx|8;@6q\` , 0Ymc,NEy0{@ sE" T@\AR' WL} {=&{u\RGK\\0`[dn <7 ?ص/؅ܵ;* :å(}W ˞?=6 O~!U<_S/xdO¹+X>2*vEKJ8trn[H!f 3ٛLRE=րV5E< J XP#ªVfO~AXX%aRÂZ<3՚s?,!\\@ lw FJTX]%u??:J0r_<,|g/f@ V.ɇH[I~]H݄dx;vҞ~/Xmp>>JusY=+igU,foFey5_FۤkF+ %n ( ²+BL߃;- d"ÎepI#:@ P'`6`peȍ&P]8|aKWD <^/eJa l%H3P[}z6MS5:6>zHW5'u36 VnC=Mz8Oir9Թn˸-O$VOg)Ac8['μUC,& `GB}3&SCh-phdy^X+ R(YMd"[|:\S˂e4j1uh=^]v#<`m %4a,v 1 ?1;/מs]P u H*\{5}ZYy =%/OnvI=3<9v9= w]FCeٚȻbeHk:Op6G.zp13UT4AX\%8kUAUҒLnz(-A;{9})0M۱r|rp"Y'w΅ȥ4?\."7`۹hðA*ndo`5VN B6J@B4TSfk*jPW:kiq@X$h+FX\'8#@H`B [x:xpį|Ǹ^k{⍕dTn]W82$td񥖠?Xa^X_tǚnsY>D5]XX ;8amTrrA-w4=3a&ԬEYXY`CT ;P)1RNu6Xk< UpCHw\Oᛨ;6G?Ǹ|sfOkrophP<0JţՑJem]UiݪQ9{ Qr1c*2-AwΞhdARihttس6@`oar0ap0.:iBuZwpq5mھÇ U,_#lL@D7v7bFSgIƌ XPcGùHKP{9r̀^ǴM*铜b#8j='=[sNt~+naQYQK^gUC٠k̐ ^Ag_+KmS@Bzd@*8V{Zv+ WU"_'%)j\Zxm%&Q}N(Ϩ.p4, ckH9*s4xXZt<1SJ b>=NS:'c0w'M_+ё`},UB b$s km~p~'-֐&Y§Y5¡L u!xq#pZVu^(V'`W tEsn Vl hP(([=Ƒx /ZxN jrs }I بM6so9aS^N/yq x>+ҏREgH VZ3w;`;WJHA?KvªdwXꨖCk#Nkr֭c_#-%sZTBEFU-wC[#vOn\#vpcҖ\QQ4FKtX=3jê*^!sǛ Oy8Jϵ@ou-5١l,[cE g^tу[3S◹| pŇD ]kg4*W$8[g}XV']tZ hA=; $TjG˃5%vovr!-#< "@`QI%cCŒ% ੍mtF'hTLjWcZr`rJ8@CiiY"+xO0 kQ&NJn ܚJg5\ 8Fk%r/V! CmQY5WP%Z܉.!=ni:3˘ \xpqa<訕bq{}pÁ7ñO i$PPoT8pYEI]uoKWpm`ж'ވ+Y@ʕ4~>q8>;19xrl*Y U2Okm/Mx`<x\ӪiZi@bI)OܥEW8chPzNAimOmi p Ӻ==(g;f@86>=0|i.E$]?u%sͧOAtCۆ/I}J?b69 aUaua5kh5a,8Igu[RzFVq1 +Y" PJ:@݃l#n kG86zX Gh1/x%ƫσ<n*&׊JKh zˆEf%=t t.02;;gkLAz4csujszIXcH"EB+nLyڋQ~6`@Lzm,?51,EtQjԪw- JzRw&Az8FCz\_lRO}҃ \̀>fooK~<5zq!FsGR0|H[N -!C=Z\}lYAgS9pƑB}R䜞L8vfvQ|PˢRaeJTAxLͱ`O;i $#+i Q=Aֆ1]Cɢs5jz'D #·Gn. KĭVCVXXBXw ;.Kpk6*gN} X8bž1ed4{@*:ץ765>gH}&??(Q9U669RitS`&Scq-nApl'6Ng97]is ZM~n26<+iUB`?+Bt>mlQ uv廀ml$$Ȇ YħFG X!@L2bey6+An'k1]0YCؠa wA p5[x6WFhcqn ELh׵!DT5}S)T-pgGGz [g~|']As$T }ir RfG otW0V ks\p$ݹ.Iy`:AC!h#0Nݚ BI *Zq$?:rsi Bj$PIKXu `X;8>"Ay!(_Ɓ!\r1|w䖻]kܷp4vG}METJa`cCg^Џ:7)vǴZv/䞏c_U~~ OOpss׿K8h8 vRdذ6Ȯ3 M|b'*-vW9m[ty$np,٫p I ajbo;oy(qG}hhؠ-bZ(TnY+J{O:wX )#F=bEWRQWrE xjG-p!-W \}k+Gx\_úd5D3{>}{]{5v^kpqʝRmS xXW)`3xKx?}?.y ߞf̀>??~M˩  .U/ۃ:_Tn K_G'͕o@}vv樨qS⻘ <k<qVI n0 FUP'I$E-=(nB\eA: R M7'X.!\W])\9<BIo}+L1G< e IDAToy8ǽ!G*AuXQ[]ؠuWa&^Sq` Rˌ7>Ye3?UL-FWyb<6̽4(\|{m]]GyRVKTξk4*<6Pҩ2V{7X-6*=0s>tΛWa_w;bWAC,gaE%/3E$;i4ȍ`kA*Npwxs ۆEXY!=86`77$S7GП(DXyOذ=nWؾD?\>7_yK?qmR-U\\ԛ vxiwTX5;Z*Zcd=@θ;+(QlQ,,1UcZ͸wUMтr ´ty_\Sc7_GOo9f@ffPծdzVh\459պ^U֍{VG"k4Qam5jȤ8]ts FeK%ES@J }Hbj/;.hijH^JZsA ݝx/JѬ:@lpppT5qJ@4T >!qs9%YF~8t27LW~ ' `ET=;uLDȑ6:6)Q<u9];GLcDh [*yuxF#+YР,-є-.(`Lno< <+*6sLiCQ򃻨s]xwZvxקM# > #8etL)@OC , IWڬPS4\}EWR8ITrhxg8ORυ讫 5fC\h5Kid;òvrw1G;rLBQYq溾܃S5mڰ8JR+yFTP9sBؠ"ȵr< YVsjb-!>ʊfS.=Pۯ:1D5!Ft1 2󙴨qhcaw2,=b磊XED*5p^caRҖ}i:uH-fBFT1JZ"A:g] @}Hkҁ:t /\|"hi +3>>>DH=& IQ?`vŹkE<֦<`lMäQdĦ OC+ H%#MlbrFEVN)i9I36 XB:tҹi'7Pnr|Үd}Ɣ9/grLIg/ sauE^tn8luTr"C h&ΞP⭳9z8'%mG9 M8"W +apn8 17XFHz}@ᒚ M-,eءI6+D]DƳp5Irw\3-S೓Mt'q"Ř1g9U‚]9"*+\剋u:ϐNxKM8 $#5]Qag/z?gݡCMeuCZgsT 8v5[iIIk ()/僵yEH?WJ`YMgڕ:= mrbwEHmX81X;t@4c̹ p;b{u uyTwmOTl'1csb;7nX< ݲ]k_3d@gěι,˭ɱNkg}e ~ f:Yb`^ۭQ[E.B:In#%%rF۰l8d{reoao0?,p| n;'@ ^nѰE(K.P6pXᩀzHQ =4/8u}2i7aioku1-#j.U)6L`欠DiA:d{C]9S@A3VYzo<чc8Gyecc_[\Tҵ.Yhw\I`^jrf'sU$ި%%m5$%m }l{i9p.@IQ{j-l*V T!Ыi'o-dO`a")u46lgqQKGH%ӏ%!ݨ4)ʽ>9H3?QtX|Yge1DG@XE=Е3KH' xKM':͑h8fx&4UJ:GXHY -pGءPۥ4٫IM}Dm]_+#~9u{^{0.l<Ի ̀ K7FܴnD"w)]5r#u1.GdK Ѭdy6Ց9{cFfָ .}VX:] hNssTؖ=G3?AԊ``s􌮙,=t}򙳷|;"mHlY=x g؎MSls=BM(輤2>|ѩh@5zST蒥WG>HRm: V+^Myb%9o>f@?!~~~o&uuLޔ8W ?H~(~U6D{뭎 {@o<DMU(ty.'ڪ ͐},2v]sxƠMpnҿBz0T6p+5mRP 5 u?rr^U┴[vބ{b B7X9|$ް#@E39r)h"aȩΪX44ѐAFGLxˍֈg#8)zúRO BW #hȱWԡ 5cjZ ju<ry2} yBG# 3{{@Tn_SmdhtNsqOܢ-|$jD7*٧0~tal`stk et${^VN3K%4`m}^Gt쎶un>I6BvmidsBPVGP[ս61YWzcJz`Mm,/A y$D2yXsC޻9! I1詭&P>OO28$j b_YЯ|+|+_^Gρ۵l4h*iK>Wt7VRtš*G7cяa(Ƀ֖_Qᬦjq%ZւlGs͑]5k$G Go1QG-.|g)@]{#>9%l;P!T"K-{y`+GV" @j3tƻ@jY,z^`*:0YQٰ  5qLUvkwyJ_Uެ;\OTS~ N3:2~ "$@W7)tBAg/޻C[}{Hg%]ە^"[QiC?Z-Ll6/u۪IhUt*>fO_^B`QFao ڧI~ű T|7G79/xwq"uЭGGRѺdAY ؅;4G|\o5 *VASM+ܣ^d5O|08Us1-#:Y Id"RJDo0;a< |<6GF :]K  s^ 6GJШ7nEw֞.4q46.--WKCΏΏ<"N+OVWZֈ~̎dc:ׯ^E k6fY.fXɹ-.<¤E &.h+> /ٶ[7qAsxT,1k.:u_J[#5MۅM27Nq\0}0#k${I%ˣY͠POmu)majDˠY ]&=)t~htFZEdvJzDZPnu1ZP+ĿA 4X O.+ (Wwp%8_&8?` & IDATuZ6X\bzzkۏy8~?QT,ihg.%r~3}zl ٱ.8s:U[ 跌8b7h}hR\npoˢvi>l8s]L}&#SkqPY|hI9JIomtk07N9u  {OBTiEq.rҩH(Vřv> FVX=̈6Gb4f T)koM9ދ֘m! ;.:l`>}y1xqg?cS[w)1-#s͑0lRrMG]|I-F @2qDP1D:̗ls,a'^lsX1W(YEُq8͇NJqJ]+"Cb"ްht*zWrIF e*e~k^=*^˾c7b;{by xn%6#Əާ :=>?٧U>!c[Fl 2ߣ5abZwEŠТqQz#٠0=,B.mfSVw6 bH=`XSc. ]h?Pѕ 3:iP\tj>8FE9E\O$&ã 뤞w%;=ߊG>Te 6-fҀĠ!$3%  !@Ȉ bdyM,30e`tK\s-5צخSu;_+"3㸐Zr"22"3N_=VšFiBrjf<@+ kt͍`J=joR7q|  Y<6Ѽgw+}9&,lj$4XoS#?ƤnAgUgs${ӂuq'h[]S/Zm(t%ŞUI>~=c~GQl/2rN!B&*j٭"@Z]Ŕ;沦z9L`Z;s׹T%da/ݜǕ l*dY5s+`t°r@>h`>9ǎ;r}a`w՚2;,NJ*D!vjj(l'ìyCO5,fCkq> Ts6Gr]$&k5%7~X[I+k݌}pYVR׃VR.oV?79Ŝ|&rrT:esxJY"ng7h* 鎞 t :MZ@7O;D erh>p^Cl 88TB'\tk ERa hzX{MwsCV@!x6C6O=#2CYhe+[}O6 T"h%ЪM,(3"-)Y ?KdA|*fL;8r}a/vݔU! FssfX n˚HCR=R-ۤ0?GMOIq&Ҧ@ўJAADZCү1?[Ws! i 668,6M,yyZ`S:8+'Wu_JM=o.{(3Uf-J|Q~ܠ`>o]B]aؖUmitfU=]c9"6h8CA-]tLrC;)T- seTcrvG8Y7[9itRѱw;ܑ-6 }> ؇6z`o'p/Jų1u}O9 Bߕ*sjb1+%)h\A1쀮-J ֊Q$w4x8oJ?&rqi}1v2cd1m8b_^Ͳfki抨 :ʆ.$ jɁt;>t7hOVw%3@krISľBi]8X1tw*2+yf8@`Gp.#UY<-C|qU96(.~;e, C]EPi05.KH#z;=N鑻+cXE#P>'WAH{U蔬b8:S_辇HmCt_MEw C'X"iTUCGTt&6ǐHt#)ho[ЍG|CԦu \`Ϊ6X0<8pXXGzc:.y:t.CuWКT{o=c7v )X֣UbŮ>%Vj?_[?5(CDG : V`r}l`E ~그ݪ qE".tR+TtjEUʪ ȋ jUonW4̴& It " -R4"=)-WA=| dv^HFtWۇ N# F#v/3(,o>y`*:'}vlq/ښ(g7Qt<,E*T Ҍ29bn9QBV7*)w':cˣSm1#4J\,MEx[򣳭1i []g JMK45.e5'U"n&T'gI68 x8q+wW~i}ԧ>ŏQ?s0J &HA$a;|Bb:SNWN@N甲9:O(t&ZletD{ꝵGFS:)iT{.RtJURz5:0pZ5 qz؜f7Bմb]Ё첊VSH$zH='?lg-ʹ\?ﮔN#o{l6~OwF.s{ET hUg1Egr0Pr.(LS20+,#ڧblst;]|,\R氌I2e#KHnH[E@2>xo{fh"kЫ޸$dc; Y ^t6x΃Ol=(5;6EUNZ&j? E GMRG:_{x BEk'VRYIP šCE_[GsE"A]޳U[5͈UfY3W J$%!-WzW=9|_ -ϑ?;JOx!yGG{tܤﮚo۷'@sD`Kg;30TDAwk\hWYtvC]G:b_uQ}h|+1-XC|tFЅAy޸\`a@agݵzytP\a9ąd]]Ps%{ѴGey=)Mz|XI>vqsQ|_..;tM'@{*lU(]͊ }U,eKM*WКdҭ"H(/q&EɷͰRLߢWT}"PHBuv~L2YhM+X)tdV)̳9V:Wj\5A; iR ]u2xx̀g.3NP&6W$鮅(]A|>}}ls}+lrU%IٌP=Ra D L,gW#@KC$=MQXZ.Yϐ 284<9 qKK h8iMeXP驵:̍B*ʱltv*n4:?dsymċa=hr*z8^';d\my}<' '@sD a+֐RIߝNa' ]oxbY+p"ݷ8.}IABLʅ&G}5.'9U33wXb 'nh*\ӸbŤBM㶵xMq)"PV: -T*{8Wû`[By!i߻w;Vh1e|B ON{MQŗ I.ta5-K7uo;!҃AC$- mvULuurX!xӫ$au .˞H ж$nԳNZ he ~b@[Oʺu_|.iv?zCpܱi3۔ڽY%o3l_oq=G@AKLHENa A#2ӓq y],lY'䰠yЙ=mq4Rv8y#?E ss:]qw¦5C9UR95޺:}i~c8^h{aL,|H><'&Zm'@{ k& Zwƒ΁Br2{om>?[nzϳ-ଝ!`h%ef#Zы\iHwb{ ӼM)kT/s44.@lcU,VtfN:,-tSb'bT=-X)] }̠q4žnZYYC>Ƿ}q(Y7--*Es097B)U  )zE :ha|蚜{w$T;C  :ž'dkVst?ڭyZRJK49u4>l7tE dtf%ge C`>9kt4N7v8?>Ƕ]{9yKٰb &i nq'f% 6z\r. ;zVnݫbeJ;iq?8?] }V> N οTOԼݾt!#@K )Cm(deǦ7Ed-=A@N@@\ѣ2 % mp^@-q^׺+,olJn.T jRЍ=TR o8҉L@= {C뇖m;]v8xM{UWZ$ MAGTvߗ=,g@jUH1Z(lv1 iʌ6(kzV[N$yh-1ϐ- NHST/%/ΞAи~.hp^FXScEʥdű?hiɎ@z[9dÏ*~v'hz |w7Gx׻M=oTekvl8 w*l4z?fn O[נVW،wQt-&E 8פDC̾AA[gJυ% @ kIt+mTmǀ|S p8;O0'>O#?#|~?qэwW74&jemr@X$I=Լ} FYek`L z F Q^/Ov=O'̺eE _&Mzr(lXur[?ܦ6u4Jc7{84'˿_|5®t~K?cӷ\kI]B| 'ŬunzM =&:L^!@<ἂeu@Bp_9{e߱IBtW1T lAzo%Ɯ^yf<&^Tpz0.&ut?3Ϣ@ +Wtz"z'+.2P P#e8T cQw>~8t_/?tUUg>EA.gh힧^݊UT֯KP*TW ^t\Ы5}F6E,*kzЦП b{̫#V! _X@ (9]SkEGE-T൪A_P͠σ<J` ʉeZ*_7k00bo7N*vys٪H.V4q *Ajb}Orh,4'u<Ӏ_e~W~|:k<7; 1?:!us-=_} /Ax PV\W+\+n^}s' oђ@U:(bj̉&ܗiB^nkWl^pT[! xʷSGlMa‹8V>zx:+_o<~xf?| _1F{9~~NpxAw//~|m \ sOߠ{uncUVG5WXN__~,s=gKzxG_'傆/SשxD%!tPRJмEw;mE"ZYX]T!\RG}ȗ%,y sy@#/R$5kGj5wa p!-~Wytm@xdEDsOw ƻt Z+-K޶8 3ゖR#7B"b%Ȅٷ't6>_O .˂,sPW@_캧|go<ӀG>=ձ??@ :+W@&T_ <\՜I$pBZ֒+CA?C./ .KPChLAfX25k/+5Ss= Z !T]Et <ޤ(ۅz& U6# I87PRo'^6]" vy- jx)]yRx*(-5-BMK%s6/J*ށ1$DQE7-Irl㾠=<Ӏ|1 :v|L}{̓o; j BmA*'5Cfj^ OnB!Յ0x(pI_?!RC8pa.A J@^ .+; k*TlfbKK zp}a0DG h*1K+9 (67*X{kj)'yB_7.EiT/AV^_OcHKΩ?R{zظ:>~lߛwq3 +f3yܩ+؈\̶sQ@C %Brv.x9 rBz6 .:N"*N:=$sQf 4%>ՙU/ Gau*r94fmpLL-6& ^ \R]*B_Sx.DZ\-T&8G$uhZ'/]N3sGys}ڥm;vN{`,; }`<Ӏ~~u}N ӫ)؍6f9g@mZ9Xp)[KmHҫ0( ȹD(s" Ph pi&_ z1ax,1\!dŒCgZZ-* |1ȫ %\.wR$2_* *6nitb1Z/Zp.!]dF1x@u۴è{}֏)T 1i@CP8 PXMSge9B%t hfL Hg.q8. kՀXEgMH) ,si?IUWb3p,=f{ 93,^ 4nPa )"5IT)V(nTZkk\=<} < ;!αLJCK`;Ʊ."5:L:So~HOΊܶ>T.<̇N&Pnua@}08|_՚4̂ϗG20:ye3bL">s9i*Zv:\TB[Y@YyyZX`΅uB;fR5PUjf I L"}Hg|ZZ4_'@cvH{;] Um3/Q@G,鋜|].V`=.Zah4r)hu:U6Ln)4󷌞V@t?иl);$l#prN :zv@@gT=@O-ֲvJds`/oc}X?b-i!-.< ːU|f&VMqDS4 f3{5;tC^V~d8a,90SIj^-<&,.n3 J8hLo|h 5׈+LA:Z*"+tD8#z,=[fAF /~ʊF*INZ.:rp:QKU|ƚLPu<&P%@KhHm{ jDq4F(\ mvP_ m:u@d"JB5vHJ>u ~fiIL6w@N GlFWk ,EqF`Z`[a22g, nsD6#gyh,'UzD9/孑= 1Y)C.%@e(˰ʾd&ۧK~%=w{ a8ܕFX(.X!a jS@pm5rFM ܤ-U]AB(D/tzӀA9@̃+|ϫC'P407%n K[۝e/|>lsV!vQ ; LT V`r{ -%ݷ:LQT!H3ٖ>7jȧqt}Qawcħ,fnq$:'TVXfUʧb2m]\%_,M68[9hY 9D])_@j:~*zr\Ut+ĵ@E"pr%3WɇneknuIHr%'ķ:dsvš3yNrpKVGjO}&i]SϔV1}8#H`Ey"ԋ' gÚӬyL;t*|aТ-0ζfV<@bBRFBy"ѡy3O AK蠢H>:q0kq@ %zg,ûɖE7!gX~ 8h]5$ZPO~1PtKr^͊ZG=RG }P)c= cˉ a R E\ꐾ`CG= 9pR ʼ!f' *[_H +tx0yI³}KHE M:Yj%N [>yl-y^2qFAșźRczK#lŗJ, -9M\Фݡ2-ys8dMjOSLbZ<6RԇTYcdm 7Aۦ۟F]<=AAp6VhfmX_0(meFQQ[ ҥ.C0lFfsbtBp^)g ̂Kz_;%%CzoPDǡlE٢ACĭ)z>L,a +,\JF456(UF>[bH=JMOW\gA28p$@AJVb0@G%fF99,HcvT9wrN*U8˨jVC"ca *g٠:t&ę殢FV դaRѹÝS8'_CG G^3L5:7n1_1qq=GPQ_ZrP.ge@kYsYSz=ye H) Kh~јCsV-F IGoԚT,CZ% aw:[r@ 67BEYtc@f&:bۣRmEf8VfuKHqZ&ܓUF @N-|-Ȃ IDAT}G|p!]ku;M'v'@s,!\)v39Yc]ZaGHݥ%)W#Y-@%iM T(1@S1yPе)-ÞrCY gQ9 KPt-t Wi}m묉Y PK!<В-RE mINd9s^CJ&M#}C=P:s >73A36N,AR@1 &8Mt6!HX3tPDtTx]WI`o$ktDL(z[œvRНd圁lN+gW^Fhcg tRbm`aEvdDt\BXs<\AG?Q/]]U)zR&i4ojt gϳMB Zk9deiv8c!Ǐg84E =dp$iFjFUYNmABtM`2Is#}2{Ca.R7AʻѸ7tVђjNMpg!gH)V,n(MeőF z4kh:, y$\0f0ҞLiEz#b^T@pTR(% 8?3Ve-oz Y18ߤj6peX m>wg?YOd}1_w,=-&XƄiv -RTE*SڦT/1hšA[99۪5 B9ǎ|&2X&GA qkAhRnq+wȹ >wH29t=l{FX +A TdձNS2g_G92nܧ7R{g0Ҧ祭}6IE| ȯ]<A_^y4(J8! SЃ@Jpu k@_{aefŅ)*S1 Ee5skp6C1}ye^yl޳T ާT2 +,"qgvIE,@ X+$lys AΔp8HfGzٸֽMΗ)WԤd&7$` PSuPGrKRSjK%h&ppeD0z~T 'S³7N>0wK/|,m@ Q"d{ݬnq0rj-X]pzް?}e^BX.k*TU&IIv(=JJtP'uU(TfPGڈHTվt(/mIOM;6¹Yz.pav^E8 ^;8tnOl׽r0f&8@' څJdĒxބ*H9ATҢ{6G].>eXG}>{qljF҆AN< 6,E]YJ4)XS08Wfuad@EJB2{{2(C&GF;)nw6ی1؄l]=i;s[?K3A=H3ja'u+ZDQC: U|Cێ) Xj!OZb;P҇?Y*#@msG9y YAK5ڊԶ4R=XzRI)KŚE0+hE*K驓j,*AeJ썞{t^[6WQR+Uo[`'{^/\dkCJehtRUH%?h ?vT!Sޑ#'Gt'@sMK zV%ch"yPW*|]tLmF>*)T[|(![eڇ"alZgsGYEux#-\9`itm[VR""Zl[+=SB=5 &QrA1 /`U)OTy \jW AXGu@ [Q8PZYmT],XK9.ů4xx@}79P'f{#K;V(i^6RMSGK : ^W^,L=XAK aH$d :%dihg~k bz{)b#Aic\`Hݣ`F'CҊnו 'rR*ͱ`a vX0l<3=@-nS8:}$`17mc G B  Xq`ep 0|i9҄'n;f+i @joU}dCRp0Ϣr@A M=z?Esԙg̈ژNḆ=nmF L^ H) a.dP%/.`•j~\ }Y) W*\r`uP6͐12hDO^ٙ O_,Zz@^kr㦋J~Nv0:u hT\ǩnlkIaqh EqNj8UVe4vaIHs"äRk썐m E3j6GA",;=r60u|g/ze`x"b Z?Ge;ȬgW?>#4"H@l9В#Ӥ\9lI-9; qТ4#޳wU""#;`NAʌ̪_-qACp~RA&yg)Q% ZA.+8/MyO0N]Fa!@:@U}-v|Y98_M/ؼ>Ah؇%k8:zC&ʼnGb1Z_~IhULŞM#8@ lVlDoBZUeҝ]]w|7JFs[kq0_E=ZtggϯΦgۘr8c Hĝ3q'9H?qDat%hDk^r%S1S*¶O7KJa)%&UW_`>(Cff˞Ȳ'>?_jfxAk84!!ifωzEސORUȠ>J_~̹h9PDSD 3K- @ǮQl$FtoUڀ> {NR#CŞ)c`}-Yf4Zn ~  -}L7f%BꐼDy_lf: %P+=m%b?*14Wh͠;njڌ$c͠VBIH jH Kf-V9^blE9ɉ)ES:31bϪs؛=$9aAWq}$Ξ`P/bHqϖy |MK8[0Iq_y`.o܌֝Ef#vaN K=;P[ I) rZW}wݖg~v<~47ۯf~? wE|`_y t *y##h>;ZPz,qTUiyqВ4ڑk]gʝ3cJ[79`,PϬkZ8s>qsbT@2{6-67iVMY7x -Q+|!,=o[tknnI|#lT˳^\6 cwP2//+j#y{y`жY!.020Tl9蝭*wQ)n4Dp/iHybhRV=3(+r2O8Dm5 g'cax&&MnPe{3grvݹ1M&=-a : %Ƌ4[*uA[ѿZiR|lyf#>9c})㫙tp?Uwhti)65@GWe۞`?B pY~:?nO3ڐN5Tu!8@ÂPiҬm~̞6J  Ax+YE28;NI/3g<8ns;klE5 kHOi'1b6l̢ b-;E/ ^f3\TݷW~"᥼;~ fX3HYjP&-s@H IgY3.LچRx]pHlլb CFaLs. kUnGmX/̞-M&ci{ࠌmIk~M[o*G-vԻ0aa]IEĢfiy O[x|x=-3>oH x28,K۳`6y-,_M~)nh?5ZbP*A* ݅E^l*9#yib1gP.A5~ctQߛ/ow13hϖ.HC,֪\O&ݔƱmY~y(6A֘S4$3gPl)'3*^xJ(/>/1GilSlͣ{ؑ9(/:tpp~Jp`~P֡NbgK&9 H,ښ%KP=v] ΁nqp|-m3~9.Ͷ#t-=/;ߗ-͚Tax?vol3ažл EڨAQ$P籼CY)aL_&!Y(~ aĚKx cF bOwb*qE]Z*$`aJd P'`Wh{lt#+#[<x@^0M#d66D;Ftn7n}ۀnm|&Uib7h9fC# 4ObfI2rv9JT_e@4;an>~;76f˨:S̢(z#y 5N^vS~a cn51Yv z`-X9B@5-rkŒAl/:bxv֠=#6;+>V*AZ@`҆QhZB @ω`&Jya0c!Xmҝkaֻ-k*ZhGȾlq$xh_֟I iRgΜ9[n ]*uDMjn"6xi9#rș0`GTD{=7j ?wQtk9P~kU،Z 4y\ibn5q1.@#R>J3OecoUA 9$owpzsHR"Z/WyYP3Q Ca \30Zxx6x O/jhX!='9NPA t#t] j:b-vi".r ę`fwD=ێHn3 3 9'o bIw>l!n}@HavVM*t56?i ,kT3 0K zRjֿG]s0g aj;I0Rs2darƬ]bλز5 IDAT [MC0^,gx.lh7&Cܠ ,W2t!/vݰx.gpf-Xuz GաXK857W+z=Tö/9 i<)`\{öf=]{{mso ?ѾE$]TZDB*p>~yeO MlIHV٣!? '7Pfk`E6:gil~\r1b(w1mr)87 `̹+̙aXĞ-؆@xwS}9`XTs5?m4 Es3Хh.gq[PZ"'Di)bCdƖaz쑞'L/D^fͅf 󒓚P[AO^lL;hg۷c~# ?_+??-?_O;db@_IK1/*Pz[͞qoCsg {f4)Ѧ2VjiBa9.&ۏ c޸޼9s&5̹sa8g 4/LZ8`l=cHG1#2Cs6t&.=s;Xpgj8+YGzCi1{"B\C\- ,d2ңyވ[*\9*RFͮcΥOO=Onvtn?y~w 8E4,(r'k֟Ee#ْpjFsOHIxJV5{5hN$o ,o2NK+Q>fɯCVIX]cPBKgv2M ՞]@'oYsI tep)1{FDT$9eazL`{Lza+V* q!HV0&5!c7H{?7obp ~v{l?/Ew.Ou̠ʅTXtgQ@3gcM7׸Uӆn7nH9yCJyr0`-`n,AT:,vĠvxƲA, ˈ-A @'j{q1{  3@7=_ avl'g2}гGvǎK%,܂^K![ V’FL7T˓Sic[2,g҆ki"O9M痩0&i}?vol;`T񻰟jf6$q}D ٳFn!I3oaZf٠='RK4LH1 XB:wiÖ5s/$W,i 4$ppR4ţ_G;Н9{59|kFl'h||PF3{%Kd+5 -̞謺-eR03b#PjN9 c32Tf'қ>{Դ?evWi$ΰ_fϲxTǤ 8kc͞|Cng4En0ҟLl)9%(b"sP*`Sbi>!k8 hW39$k&|%<%(eVtg%9XƱĉ% gi#Q9p\9 *E8CV//CIZ֌9}3g1|Ty۔)`rXײ)?@k3ҫ;ذdl*h-̀Grf陵 M0*b_k d2$XI-~U_Đnk!au΢su݊^Y|f"Ę9EjXb巖5Ű sNXB6,0[;{Vw.kpo>1xGҡ;}ށHpq 5#9} /1](4AW=ZPbZZ-@U-Z5xQ*UixauJϵv=, r2/f3) yvolΜٗ2hMP*Ϸ?/ke:E4-*FHL䠃e1$Ř_H%(L=(R+ib0bs58PĞa , 5`K\'Xk-[هT6Zz9uH3=KL z6::sn 8;{474j!HNE/ArOM>-վ70ewޱ}?lfI#̣~!7 A~s>CIA)9&Z0$*.'ʓ9hPaSUЫ[$9)Ι-*&onXkK;+|t霠4kxB=б7‘ѬFHsy1x}a)?4zTE%Ǯ90f4tљAw.F"^=*9.'ۈ}Z%0Ge*!dr"J͞A[dǍsz F Kvhhu6YFG,u%5 d|S!yyZsãs$p5i!rQtfϳ 9ށZ plWܔ?wDv8{%Pl+䬖V@:$Uvz=4̱ž#lqm+qּΤ \ l bl\O̹ut.5j&u0CH@%o`fAMpbjCf{۔SI"S&[ji֣vӦ oh`g7cM<Ř7ZHbo{ff쁷U,썬!%b9c=<¤JP)ugY-t&v :2PG68(BrS0b4[<]5*f54 lIz X$mDtYg;4&qכFD.`rt6 Her5&7sS`rۦ8'Y+zg=E/Rb#@^gSX:lK69I_6%= ֍#TC}bw)rVF,'rĚ#WDg\h{uwYcİ %ۻZH#u(Ot]BaΟw>7KrǫA9&;,0g&q`Уvl&I&24<tY{Ng&=t̞*f9jctvѣbωqSaѱL"sjxB2{ndPH)y]i:єZwN7VjUcDs>!LCKd6C:Pg ;-ְ3X20K,q@~DzĚ-XEXk\%iH8OyK T%gl^*ULGBkY.klq+qcΜ=.O } kmC1NhuBSbJlPDWt qpj.gn:~l?3BwJiz& 95X`}=orK2ǡvwڿ??c^|wo; [6zO VXj>ڹVarqlJob{^iYE?L S͚% )LsszsEvĬ&@K=c =33؋g5.X*U)ZWtYĞ)n!˥;|R6n_&mhf픶?%>-h/!E"|n]4p{ڻZ>Q&zdg/D:bKtۡ?h//˾[m~pA}@WW>DyX7qxw=-jrTyb4 Ӑ>7,$FSE)V%Z= , {90/rYCYkWb7@[:2 ]gTT3㜽߬Dk]QKHf?1ٺbg^er%[%vv9qPc'OD"s<7LKxhEζA`<&>i ^xgs[92,>ჵ|L3,NwtvOG=BlRP?{?k]ٳU,A{#>hAgL>/84o~!kV ^8HFE"ȗV}sW3g1伌ª9Zٞ@UіFӷKpd ܥMb-ϙ hm%P6p(ۂ:l8(gk%= &ՙ5A!;kZӘ;cuKF,O~o˖5XS:c`잠G=qią 483Bs-7Z?O7Ky$}J;/ oݳט=O<,HA1TQqUߡ,AW{Fl9fvowP\G YtL@;?{qspRYDN;bthHIKQ&( xiFƕs ݀D2 㔆h #92hyf LW`טn o`+Ö"E6a10تs2tKd$mo^fǥt{zTL*i^7^g =G\!x7+EH}1?'$z G 978cځrNPt3}cՇVl]l`kL(]v6"6I#]er)+]6`/i {km FwO(j8@y7ppf8S<75Uf5dg T MC ι!ͭ=UjuA8<;u~Y(嵊<-D?W:/2z-hV*Ƒ/ =Ѝе"&p^\Di(N#|hQ|@z`wl}}b(\m:t +ej~ybF76qCʻ=2&^7rTsmǵ}Vx;%e}wÕ&zĠ5ZMKW\R.U9m N@ǠYfV&{]8N+*?cȿ]7+wTLp~c%;Vhn7$Ԝ| kwHbu7Þ,1LRNJFx5H[Lߥl-"C,^ؕZZ={^R*ubՠKFM͑4,H+0nKc q;nupIc#q$-EqFV(~'B~AD;]ܣ-̈'rqmh%4L>R/=vəՆ?o"*Z(ń5 }}8tq{.O7N ;@f$OӬth&q_(%3zb0;el?5(/!&&q4A98k$tskE}آ\°1x'tkؽXzźMFxr|"8b^@=/kڣ'!sϕ|}g-OM[WF\5M/i5Dkʓ ׮#v&:~۝#yCGUI9/+m?BM^SYFof[PC+\\rYWIE_=BSWo ՞,n 9S4mīd:).to` δ +9Huq%-)PϠgىcĢU),kR=^ >^!]A{l3C'Nb6Z̸ٝm#"CS9Q\FwMख़5KbD@azpԡB?wUzC09[>:ZJe|8FпDwFeJeJx` o?sL[u/LV֦otŖk\G y)C& *=rl)3bBL-FL k[R ^ 鶇c<su=gy3:jMD;IiDgin" \4mc<7K  mu'!@O]pi1o{),٪eߘn;zpwHv/[b8o՘^3B,kk6llkZ{O# 79sVյki/9 zdF[fDZLs١z zŶ875r`0=%p {Nvzlкqp޴؉Љ4Gqi Ed-/B5]sBlOP !{+={vEL)(Vٿcνת_A%oo{j_qʑV]Xz=E3{"6_0e jV24)0gKske߀s)g8HG"k8ns 霽A03g^e`ϋFHw Z[5(ka$m<[nCȅ>rnz3mP*EBF/41Q.D /40%F0%4QRL$Qw Q5 -)ZnߠvwbfwgۥPJINf:sfi;gss.iv:!M*!a!YRH*FT{-:q?AЛ@%T;s77Edֽ}ZOdoT:gaz\Ϸym#1uc!Ba,o'sI+œ_űmZ􆕋Ү,$S"T0v$ zqI{޳vCDG(0x޳;"t!q.+spJl,M/wkn:ᒶCڝ!\$r]0JѪQh+z=#H$=ݓ@ztلz̈]^]= V1H$(HϋvU6@Ml{zJ|kY"#蛭ŭV7=h5KiZG'УE[(/Ixa̬X^q'UBb6ID!jNb" ! ۂX sjG;`њn I.F(Ö0Iq v,C'KB@.J@vK؄7bB UXnX`hyQK*đ& q߉nk@FߧSoB'W3}a~rx^,$5r{8n)(dүyݪS #$mW[B OduRH$IBX_@UMm¦fJ3 P'AA'(IIDP"n#2%գ#+Σczcx8s Jֶe˖aY/"mmm|7tQǩ8z\oXEr PVwd_CAߑfrN.9;Jq#W4 u8:)qE:39\epCENlKѩ!,΅(gab=q^{Ał+7~He_4Z#ڡGluKu1R$ ,ǾRNMCn!b#IcI*$UTCvMLR b$uqJ%^F\[tb}oӑ3·>^>3/;Dss3woɓk<#ضMQQpp8kϻ cغٶ?3on%@ i8<۶mٗe00@+V/SSS7[MMMyff 7䧟~oɛv6nH0$w^ &F4套^ zI=~>D"Yؼysl$ y͛fGkͦM[ŋYz5w}7/_f޽yu+a 'ȵ'G ,f {ͫ hllT$r}Yܹsn#OBsСC70me-Zihhӧ8^y'(,,,IDATʕ+u ׋ ;v져5kְn:^uˉb477H[[!s z05L]L?hm͡C`ٲecmݺn• xІۚ 0gΜ-Zd01`0LQm0 S#0E1m0 S#0E1m0 S#0E1m0 S#0E1m0 SFZl-IENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/lommels1.png0000644000175000017500000006403612253362407023147 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxw|S?I:IB)P6_A_Ad*T*,TdBڲW)Ph]4i{~<$$m3nƅ~xEoxrzs9<" DDDDD\ 701EDDD\A(qQD-"""⢈ZDDDE":hEt"""".EDDD\A(qQD-"""⢈ZDDDE":hEt"""".EDDD\A(qQD-"""⢈ZDDDEqsۇ-[@"** aĈ0`ڷoYf9s""""Nzѵ6,~y/*TBr$EA&@~ - o ?0ǎ{7! bƈ6tHhIxN }9{9- bƈ6t ZIq IU(;YVc1?rxB2Tݬx! bƈ6tv ׯ^4~P9Po SG"b fha Hm79 `@`(6K`P1і7녟]- bƈ6tv 9 !P(]cbێTVP <#<~ Zqo:hQ3F0HfR)kп{@׮@|<_w>m~x^l y;9Pg:+# bƈ6tмDL | w__`6Xp1܁>’`8,:ޛE1M 4",ѢpajDG|c Pr2Z E fhat'w5cǎxװlٲ*TY׮|Ii{5iS &LRpBf~xM(#D8A7m?ӕH$@yy9k"«Hm <40xp{yUϞqܦhi0"x4OGT^:ފC1MtmDFFz "ʕu 0; pЏXm~t;Q3F0pYG*" ư [nEll,bbbY>V͛#GڢC,fW^ayֲM[c AQn4C9\mvkemOFll,,Y'T ׅtqJ%fϞݻwc֭"""ネ7BӡE7o'x:X8pк5ˑ|ͨmpW!΍8/6D,>>11Ѧآ@C }bx~ PZČm" y ׭cf95c7nkp4'ë[z[u>n7NDA&@J´4 ) hĉuzNwΞ~{k-V#DA\Is'{}I3z4ܺeilGu{;|BBJ Usƈ6t6^G}0K/UUVhz~_(GJy91cDA:hD¢"֑.4S*u6WgQyrxyX= ex91cDA:hD¿fO<d woqc/ڊalCxOzP7fТ fha HmHgxCT>eᶔw _w+o<DČm" 頭 xQ~3!"+ս0/G"85s_A]Čm" 頭 ′2r^DŽGaJhq˕PPïm+S}ġEA&@j?+ =^^a@E[LHçs/šEA&@jP`C7;"̡8ضޙA1M U"a~> DE ez P(C~ȧfPm}W17 fha HmH^u,5zrDWP ۞Ƿ T9u4 fha HmH_Ğo]﮶v{%(AL[EL':gqsMLUEBa HmHw?X2W| ϽAh)Nv9o^ETݷ26I\nGQ$t?QQ CIB d>2| ?~=gJ iPTi*>'{yogCR$MDU~]a H( - #h8xʎAWCH=3-|euךZ=1e%΍<Nš-a[[ BPgR HGv%"0X$tTY쵎0_7>]vBa{ bDK.A[EĜ4{$I}R-|:h_>LX,E "zyQRwc^lg/A,oSçZ|ܢdr:t_OQy@ E"#zrX&:)[|vY{?zPqh{ESWmVԽj%pjfv"0H$:4y4~12O y2OCFκd H( - e}g0QGࡼN!HݥmWvH@lg--:B I$JWQ$t޽3;hB![^A(ho@@;pvX߮}6UU^,A "N<ڀooAMx8fj*paXSel 6޼^^!}Lqh>) \|dhLa/ǫ܃QTk.ÍPYMl:WkAxU K8дiS@͑Y}EB}W/>g>}XFot'~K`Q:ePP#xd0ml[ǧR%JJx%"0pIt@ZZ"_NII֭[@jjg^Izfj?{mkHݳ׊DzD:"xnLL>[Fqyqs1_i%/+Z8A%11ѥFvibɒ%()qò.$D3.T*1{l޽ÇGnaÆaʔ)Zƚ5kj7alذՀ屙3xρUpo4RƦ |Z8ڬlXIBq^n?:Xpu88mҋ@H*u⌐ YSЕ o5boq Rj#rV2ڵkk=,Yְwo9g šg 7mjLJ]6iț+h\#n|MhyKj輫3RS!J%aH 2_:e9zJq[G=ei9nvB^)w}Qn@c!Lm]J@F@x69؂g'Cv B$)u<2Zk^m׵_̯2[G^UϷQ@ !n_G8{ ?AjKЖG(.y[L( ?1a!_dBF!y"<yѸF6ϳ'Oj ]Y_g0Bbs :&N )z Ϝa.&2=6)(G(OPh]*+?d[A(qU0,&3@OCwA.A7Y 'A8W^E~~> ǘNY8d,u-ѢH)çš*Cl.a׎E}1qhW 5ρT.Ep aAW3, lv}v؁իWc̙|^j ʀׁ-M# D*a!ۍ쁣С  ,Zj력R4\%=ÕDB v@&Az:UڀCa !w}ݮ'69[⥗^»kLDlNx/yf'ٳLG ?V8s>{<bb=`<,B2oBng8a^.w%@144$09 BfϞH?G`R$nb%vҳCL} H$lqG)u$Πwuk`6//_@֭X .%K@B@y$zq%P}6ג#@ ,WFL.$)\v#A^uUyfw&0>3o40|а!n3x4{_9.Ɔ ?*ecg=^ͽP?^*"!@" dc O=^bsVbR$ǟ]@ 5硇+_O?(/(Qv Ν f]XwG&MoOƦWzaS&$%zH}bT> PIuXn\[{o+y=%KP[vZnB(-@U^K.IЅfе z:w||'񄣅CoV9zX͂, y԰^21o*ܷ[7^hǬLc! 36ꁈ WGIG%Tkׂ1fWdIl8kNŚ5X3r l V=(8/'p` $HG+ b DƓ#,E[.6SgJcƌi0w\4t-[ftH.0{Naǂ!hǷ/ Aֈ|嗭NyU9^f힅r5WN2<--e%%@UKJ{Q+|:/)JTR՞8A=z4VZ"p!Hr7M1 /]bMv:wfYNZϗL.OGoQkB($-bp!Cb!Wo]Eu}6lSNb2y,t:ϑ6mK`S!1RS p@uJ%x10p ШAr\޸:yr}spBӦ>22oH>|?#V^m䐍DB oA"? C$g?MC $&=z>j\ك^k{\9 :idž74|9蛛olNStDDgG*" M~)))غu+bcc;T6mT rvCʭBaBTW}?R[23e,111 d H~ m/H$]cөMx|(^=58JXǏmnaӀM{/d <jt(;VȑĬ,goP))H$nRoNt}oBFvlx{:^^3Xd JJJRQ9 3f M>̙CW]vQ||{&˞-!tvٚo$$֮Ng9w_M I-~{u~g?PmѠ5֐ ߙoyMR@Ay߹h f6m{gaGF]˘ʜ+2tj#,R@A^DD&|qcТʜ3$ N"Trļ~y~EE/RKqnqt(P7qH?@{AkOu4e \I}>>*h wS.)ɩuXTTlv%y]kD!3׬ի<4ёGHUWZSS,6R@x8}_Z㭺RXm@%Ǐ|")7 3v"d2ߵ[oiWX?L"]#@D~heC,5+"Hm Mڤ?o]>gELsg&n9bsY-$Rt&O/{2l=FImh L+kU]^&ug6oc;O~L1L;7sq88 NO='"JZq 6'oF5޾\*JADB?UN!`W0&$4g48h7q&<+_=DXlq;ʿkRCp to)#݇E|6R#b`zaBz̺W4``!*zfD]9y3|ðrJ΍:i Ea1] O'>=):hJADBpzEs6Fw6%̞tӇExxLfX=:OvHH[Ŝ={bN9(*Ǥ?&9=ԔwkMy ms怔J|zSz"nB^YiwΞF"7MiMû jv?AA?=CBج $?lַl窇?$ f $Zؼcss%AADBE6>ȏ < jЀ͍(;." ) :~Jv`MNLjFc#N^WQN3^'D?D?$)suO ѠWլ93h(,,._fNҤ_[EB|e/k ?^m ~جwH=5ckc2>K0FXի, -laӻ{CWTU_AYUmFX,A.&Nd? ֮5ibcu=vv6[m.\݃IpKv_Z#c 5-["}:-ܪ}avamg9o٫Xӧ? o7oM|t{A:芊 ee(;Y>kY@0%9Ľ<ϚH?/ޛ/BRT8yΆ۟O1%NXfu%mliܼ *ͲjXm!!@P{tW3ϰs0̞{w7ʓʡ1-և9"aFI^yqXt  ӔV=2T(=V (oth}#b!K,$Tys"n\z ϫASƌt'O2 젥TLe *,$"4G/QMѠ'W|a3jsp9gVV*8|pE,矉:vIԹ3d xyU* nF 5]q;"Tj*ך^h]3kyp ,*U*Pw_৤ 'rIKq5Pzaիh8M|(+nFq1TJԤUԩS>v( };O6!4DŽhC_Np_ŋ@4kaSʄKHŘ UJj-!:j9mV;܎f&ڽ%h.^#k4@nΗG)璭 w+s!o]?Rk;=)!dIQzu:3gm#S?x0㏳3>hLhN"Nt|0^SO97 t l ݻfp혅"MI-i0+MI~kC^qtaRH6=`e"I0 С1DoΜ!Hڄ?2K;-wxO[{j-!ee2C?smذ_,)#Gl~HsGw'DzIUO6#:rRWx=mD> cZ}/ǟeQʾJ%YvI 0g ]% fbmbbQ^9mnoyׁ/!4ʧJK͛Qy,5zyoD_ 1i5:sٟPWyO`>OgRe8 X6x}pasدtz|creoԅD"'`@>wl(d`Zd"_99ֳf˖fRRXW|e͖ TA]ʎ㩃ݺQַY6DTTUPeѠg6Qrru?O? 6KmԈHY]E>w j7myy4 ZvԌ+WX1P@Af(1se3ȯȧoѠ9{ޣbxl_?u>ɘ RMb 7jr#ެ*?Ϸ:>{vuizf;v'HB4hs[Hn%""ۛp1%A񤀂 ;kHHK I,n@_!OO9 Juرd_|aj UxMA#?>Bkd46wީY&DA&NdB^ jCt@ -v0d.:hdna,]f]8{֣jos7((MlJj*_۶DG8 zq# /Ͼl8d^28k<8uʕAB<88Vw59"G_|íyCBLqiyPP6{7196np?sѠ.+ʍ]czc%j.r[͛>wQ`^zݐs6D=z8zѩO٤[ٴɬfh>;)ћ{$D:Lr*5SvbH6lp8老J]HgQZT*ǖf$Dmω_yxѢ:w:K;E %ڼKm? \HW x6MIH0ɋI.պ5;fA> |xD&8zZ%8?Le yNwL.ݨtD͚ ѦѠς-yi9JM#{; [J$,][k?5)ay9oa!bhP!tm'vAzcUz]$dj,`)mr!.%Iu-(`w77+WH8Yj|KYqSGoAV"NVgKsD}K/ԩSw5lt4i$6mM<ђ%JG' ts_\A;5:++ M6DFF"33h{ $#:1gc Ƽ/DtH=h}[HlkO=pۋ[퀇$T*mې $_-] k,3z`~_<GTt@zz)߹=-- 5sWƻE8}]4 icIn&_؎vfM+55Ր_׶ )HN͡lRmf˶F"oΝ{B.WbN 3+;;qBUq2ǺQpEc ej=/ BQ駁oMr9wZaC KfxD<,RϔX 8s~CZSO!"`٘z5#>kP̡c$~bb% E_ҧ2FH@"Xt %VtIĉ@׮8zTW_N|i'<<0{lڽxo__$^h755OFll,,Y{lrr z̘14}t3g^vEѤIhƌ&cЃZw? &?4|aNwkE#귮1>6L'nSTH6Nt=A*{V|BNּyJsJ[CY*F 00*58JKxyI^kޗo>TKPUk=caѠ(¼J+o]a-17/xe5)K7.VGINN3m[Cټ !'z]ʕ1y:-eWl8yh ` 9? 4iR{>bϡgN{:w7hoѠ>@b`u :h9r) [Ui~rK(8짭>>'G_o4ێ&^}q O'^6s(_'CXm-k!i Jza hP3u> *bIJź;&9;gɘh5Q/ŕzYkB4j1/͜S Z󈫵;C]ޖ+_O53Tęag{BcYsd$ѿ~;QiTo]?B4Nwf#:hψ'Kg h룔Y:)(թuTu8F ((;rΥ2uH?@K/11c _;yM7|E_VN(PۙPYHopΗhm]rᦇ-͇/n` Q"ُw:N (2페y-n:fL=|yC!H.g(Qt;"g6*/T&VjW0#|YYl \l(9;% N)iH{Kz4W"wm->$-ᰛ,iBľBm6; P7Gгnсu˧Hx5CCaύd`ñ68Z bE72<^]df!3gK[_Ji@ ID,ܨ}]d~ͺ]yX1ӨHpk`Æ>m.fGL7?Z`B{>Tr5ƌaדo4߹,myFm[EAzujecB4#_{q-g?oYw8}w߼I/ie=!Cc֬Z?ˁD O2ȃ];MHt<` b.SFqKyNSb(3[俿&o5g@n.t)Ҳ2Ҩ}4N=o죏ZuN}c׿]p\u}+k3IL(%lL"V%("ŠUO@c??fX˰RSIt  BgΜ_5kRX[L~;U% %F%Hcq E>/6-dո?H r[F+4f o[4[qt"}szGkhwqQ ("@Hxmn.EJMˆv-]&?6RH-wӴ 2]W5(VE+2r. s9_/^ٜ9<{|;5PlE$>][/H^hT$Pr.ޔ5s`QsiT0;:L+|L]kIVԚTW_D*P5zTQa͢\YNC60_%eRb-粈eK^Fi@q[J]2OFu+QR3;L/b9K -r~8E_}Df{vrYbxh 9XHT\QJJwjU5|fm0CGoѴbi5T(+h/ 'w>IyylcU3{{pfjf-:Ҫ + }n2g.YqݨY\v:Cn/biMOm-[";sOG_G^| MB3TW~UNb21˝):c0HAŊ5׹ vׯg{0>ȜbNKgR|r< 2v0}> ed5q-qkw,t]JyMwoncv蛛msi4C1le"ܥdkYX´c\B7!C8U[ۙX[]nCYb~]&5l_JH`-]3f0ϙcQYRiyn4"qmb!'NnԍY_2d&)S(ҐQ%nc(}}s@s5j:v?<>b…ϛU݄1!$[SB̷Ɉy٨:<{(y0+ 9X'Y&cfN7`DcPgw {Sf87*(1=\W,|faeb9y)+3>>٨Umy7Bh~!;G4k1|ɉy33;~~֠Oph&k)X}u+:]x GVCUJ/bdVVbOZ8+ 13v)¡B .xU,W_Ν;-ڧغ?ٴi@|<~rww "/Ԡ*W:p&W˯jU* ѨkDZ``x +Ax:iuwkRuu@~.[Ǚ~. QA!`^.CC>Sn.0x0;׮^ZC؇'6aq: ? OРm@mC-2K3q80~3jjݩ;Cxq3geȚ܇>,q4='[۩ɠؼx 'RW H|CY[: LB!JV9_?zT벧l޸`sw78ԅ%uuuiê,b;3g6]̺gE ]Nzdݻk59EO:lg\7jiŽ9wY>-Ѫtҋɠz5봡R{03J-5uvr"GA ]DO?m]һp$B׳FE=y8#7س_? |pz^Tfʍ~'%EHSn)&~͒Tj^g\*+,nb6n c[CQt/蕃){ЂkI~|_MD5aRڽʾ~tq H`Q!1X_"?VQQDP X%͢ej5]-b}Nݧ0J^}ߜqZE?q ~( =])*^{ 0{1߂A\\3mx~͢$Ld=0w. S̖]\%-X./DFWW>y@$BBj(4od˵Rr ~nf4Zcb .ysFjׁge  b};xy82t[RLAA &YdAM_,;jJZ:֍dm|DžШKՆZ6R]c'ٶ>\gϲ7α[x};?T_a. ze*>[VВTж8 E&ڲX]g{jW!7?͍=C3Ⱦ l^mV5ORǒkQ\oZ-]T)+3Mh뵔FNɤ5ڭIZ^6O`u֬a^ɓ7 E@ 'լddV;Y[l,k`At`֛M ҁ!-W^aKZw?ŪU6E_tgPN@(kA^B%#rhV&YϳꀅS}J%ن .*6YRB{XhYkQTiEq(Z #jݨnzȧ"V$J`qqܼiT26uWeWFq2SDֱѣ1ELŽBfII}uqq'UӗۼaZQR: KCv?AZ*Z_dR$!kzϔ:uo5a;,.TcLt: R+hp4!څ_eJ` ۟\)Ys$q1u ׌eLaO۷Y$3!6RkϯԞ#9u)7ɅhEavS?O99,.R|X{':MPvL1U*Tk+so*GV+`wo67}::4 8~ ; pŀS됳0uuufKZBB &n%u$-4[XXh@Fc"NuL eߗtfĽ}w~3 km\Zׁ۷1r7ōo\E!w󁞨H@Wfa>jcz_ΐӒn8TUUٳa[XXFggglܸ~=UdxࡇӧciwϔɦMTўJBjx>n|g8 yfpTv0N=0h dV/7w6)?\pvwFPc :?? 7oĉEf4 |*I(i7@ƩSct?@ǪP=ޏ6.Jeg|}q"2̈́usa}^=̺ 6d rSt^^Əo >ضmpf1|ϳv> Jj\qkr4V64Vb@yWxÆ iFWS8j s\&4զJZUB֤,h*5z'D.1NQ!!!HJJj7sL`in?jjj}VPP&mpWLg22<=q~:(SR_|ywGfN&O̷z 8{{:#0&Hȉ1{[[o&~dv1L$4$YرđǺUh r}U佟8 ̩c5e" $y&0RTpAXFhm~(4\mRKGe" $P);L>Q}SpU뫻+ F=<ج,DHRAs'0Rw |z:`eJ)5^s_<n^)˥#2TI(?{dN2\u7޻ hlDb\xZ ?z,ut\&@ ; i89"w|N">`ј]A.be" ZnZPg' V@qJR5)ؒ"12TI(LFFF-_e$24qp'0.i I͝pǏ0\.pHI*h$;~r1DHRAs'0# )\&@ ; ape" $P.i I͝pǏ0\.pHI*h$;~r1DHRAs'0# )\&@ ; a\L2TI( wb 4NBaG.SL$4w ?pe" $P.i`W`<(-5mon:̝;ѐͶt%Ł=.a *7"""sJ'N 11ؼys2()=.aMAwJ<[>5k^z% ܀PTTlNJZm)8$\.pHNQ!!!HJJju;5___ܾ}3g5^VV~Gs4CP){.SL( ʹ9ȈG+V={0rH,^DcXv-^Jlذ}9nUA[RܹsoooZSrRRR05 /d7,미|2ѳgOL4 ӧOBK$$$ 33}Ŝ9sn)v:Ǐ޽{h0p@L6I5ݷoLӧ7Daa!~{O899tJ`` v؁֬YM6ƍs P__;>3cƌDFF"77I)bY< =z4F Jc"55SrJJJG.͒%K׮]Ò%K_yFcΝ4i9p׊U 1B{WWW89Iv8())hZ;3s׮]:8"ttt4^{5ˋ8p=:̜9ӱ+J|2z-,Z111pssٳ==˕+W0o<刋ݻ_"22 ,p׊s/߇9A p9]cǎ!>>1cI*#tI"##Yf/8+hN͛C8+hNd֭1bv܉X&#Ix8(|p8 Wpp8 Wpp8 Wpp8 Wpp8 Wpp8 W?ɖbIENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/besselk_c.py0000644000175000017500000000017712253362407023210 0ustar georgeskgeorgesk# Modified Bessel function of 2nd kind K_n(z) in the complex plane cplot(lambda z: besselk(1,z), [-8,8], [-8,8], points=50000) sympy-0.7.4.1/doc/src/modules/mpmath/plots/lambertw.png0000644000175000017500000003676412253362407023242 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxy\_( *""I澤ebfV^EV?n[Z)i%yr'rsD D003oΜ9c4m-P F x-  #Z)A"Z)A"Z)A"Z)A"Z)A"Z)A"Z)A"Z)A"Z)A"Z)A"Z)^F T͛|r#""ӧO7ZIe4M"Uq-`ӦMFk %8.]{h Az̙ooo)_}־}{mĉ35 8y[oQ@z:i@|5k Ņ .dԉGAA@a!0|8=j;G֭22/W_j[F॥@B}..IJ^w 4j FϽ l ̘AmO<4lHCaCIi֬MKڦNK<$%Qx%8E..\<qQ%=IB-noMӮa4~I p-ԖGd K/Q㫯lNq)@\TA4={5<</\H׈Ozo.Ug\I`nz%ܜ-R.ڈ4 [;Ǵi֠5uyz5.κQSp-R.N.]{qXݺ}Ҫj۶ RKxޚp@\UP2@cGJHL֥#F^K Amv'/(,/ǎUKyx J躰0,&P-Z<S֭@p0pMmKII - 3pE ЎLG&8̣MM,գ5Km6\w|uwJKyx Jhw*7j><&* ߟ_t=s&zf lB;'?\\x JhIBWF׻Sˆ xxJ8%~pE Ъ% A.fpbPq:. @\Tuƒ%K%%I#]K-M2/ .:@W$ q˂ iz;Ku*(;+V &&W$&&Zn#+ڶmfuDD$b߾\KuRSS q->>111={6233 it5ZD\L:ׯǰaG~7n-[f@~@GY+[X}}},X IŠpwZ 5`[ԕ.Ņ .z]ʹ+O;Fi);+(՛[\`\b >l q~mR:tಏ0 d涓B{\F?IsFŅg'@QQӸ1Xڴ^?D-; 1孷F$ -#tbץ~X^WﱟJvܒ\ҨMzҤ8/=\JK {!;;NRS :wڶҹ!!U+:볮dppi;fFF¡_>\JJ(޲_tU6G͚]GWX}5jm(%I ش)QQgOpjRRBDףG)W6T>l}J+JhI%8\~ 38.EӀ dxX4DՁ84T71OpC-IB}soi xVA89ΰ#0&WDQ2@KP.sRbh`(81Chف{pLw:3 P}pd$hGpD-IB}̛Gv#.F lA`~ WpwxxШk6m %$ vc8ңrev5>>@:k)sg_KҸ(%Igbcwu}/5HO/,_ ijK ]ߗK\@s-99޽tvђ/]9b#hIÃ}0s&ybW{[،Щm ҅>xpC-IB}_|).ҦMw]k ƻwS˲n m97 ˅JhI%$#UF T~_( z7h]"JhIꣂ֭T˅}on  7XIXܒ\P%?xQ`]KNa⋴9(xA:ads~eq 7d T… ڵ5ƍEx!Chܲqc[8x5-oBȠv2*.;w۶H:=&NTo'Mp@ޤlXP-MI`+9#ZIL4ј ])Kv6{PZ g_Ko`wwWɅl8|%eJzf'a iTw X<s~ǎà _8;ڍ;WC )\R,_N{6lHEP+ ܺ9ҵPN.`CBBHIIAHH幬,X111Xz5 11W 紪e4z/Y1l[<۠ea[|2/d"?]o i8ZԤ !.و>X֡5+;GCЯ뗎;v?ĥx`̤Ol4mƐmȑڤI_~湻 +Mx^z0P4Mۻw{5&h1grm޽.=JM[Hía`MۼYJK M;v 6MVL$ߌ_v?i7r~`y~ohvR4xb'# ڶK0y2oO ~p-lh@h~{^+P0avNc,ϟwE9.ɖJ]JJǁS)SU1ի.. .v2 eql¿l /|mq^8~|)[tNt+ԥY3`Xt*KNiK?*]\@\TAb?c@3Z6GOW#.\~yb)vp+ebhIE58tvNBTʷ]s..|:CzC%<0G|:c+H*uyI/H_oJoп?}Aq..I42=ߺӦ-sl>;}_~&?px]A.4mGbॗQ;tj]\ @\TA-I,}-8:ܥh.MnŽ ˜R]*tuJ>Ф 3zcp.Z$1 [Z?]oc .mC8'Po %7({ҟ?}ۗسC]\ @\TAbѴɟYiZ ab.PZN3A.9HO_VSФUw OZԯ$*(% 삺%ss Z5{J K1=(%1Ӱ+ܐ[uii]aϼ:tIL D*(9IB{_ۺOy0A %%q_hQN1i1@\TA-IB}q1yiTw ;Ay'v2f v5=Rǹ lcpcbH#.ZTųͱ^ z7:#zL k%6&_}U7.p,0~<5u@\TA-IB}j ?"ج(\P3__`8ϟ9DGӷ9vҡpG\<qQ%4$!RϷ}f~ϱy.M}Hn?}Ki)уix{Y|IgrG\<qQ%$ qK=_ke ,7}ĠcWRR^M7tn;|)%_|ѡ]UP2@KPg4Gy8(*FO?=kЬǸqT"YpG\<qQ%$ q7FgRk-/ƶo̞SV?u'N 6gqELB \}G#oqcs/r@*G4 |[AKP\Ƕ>pv]hڴڠBSwqE В$(&M1P8"B~]Ν+?Э#.ؠi\[ В$0˗_xn ەx]z\],YQwoʗ0C-IB} ss*I6d\B^ō_=G#hWh..GƍLL3^^SO&JhI]֮D /xؽ{w5F-Hh ]/G^@D֩p#.@wIOfiOm/sҩmڔΘ= y1u^ -Mq.{P%#Fȋ۶_L#PTvL +aQ4iMۘ1c0rHa$ qcЛM+34&Lm9%"֩`q\RT]ʹh>cL su%$nt:e6o!A^\ѣ=qq\<'oՊV^ll}.$#ܹ9iG вP ];֯M_}EmSд'pmw1qrμ`"66]; SpC-ILVTht)Ø5BC-뢇 4{pvӦQR9#w}7M\:ZEE@n.0ʡ7:t LJmeF0Ns s>riv.\O*psǎj}ϏVؑ\/P2@K G|=G.4ף}sT/:Ѵ޽N%9Kf&mwA,>HK\F85@8qK,ܹsu$af&cxmq#޼}slpj23)bˀtu4~1#9#"K2 Sttt44h֭[c{' Ҭu)̇=j\rڬ=sgImN׈;q^5!vd2p ]j0q4mo>5\ 8-@O8_~%qIl۶ SNŞ={j咄%%K_)^HiiTw^=h8;VT4nLmIm=zG@I7sJp(b2Yw_oqGRm(x]km_'/n8-@oݺ~e{4u6ǎ//`-sf) ϷnM\a Q8%8u1hsRH'tg$￧7`K5 itu geea1b-AG>bbSWҽpNpjƔ)4<ٚfܸqXl5jS",,(ˀgN e]||[os>('PBT2im/Xep'/nj; .N/ع MMB;Rk\ %4띄yiu/?ʥ_o:Z*48ɅJhwSCi֭i zr.Na3f/XXɅJ:_n(i.R#'ieR%~~L'p 73gĉc̱ʍrAy'h.R-R`0prݪU+,]h֬ʜ IB}wE ajGv6ˣʆrF8N.pZpqLjѣ1j(ˈ<'IB}wdҥ?]F8jySq.OO:H'Ʌ,FX~רQuV,ZWHHH+Wm$&&Z>*h[z!κuම/+ c]ꗘ܎]]©_\̞=ഝѣݻ7|A/vGp YY0 '1(BtN~mz Snpsܒ\S.QMN/9ʘ68 SEI3nǥ_t=6IT"ks1N.`;Q>&1ԉJmѴZiآZvSg\?{XGF* \ %$ԧθL?d r m~%ڌb >xpC-Iȅ:b?h}D׻7ar ˅JhIS\n MfH:N޲hX&}rᆒZvS\|}i :sf >xpC-IB}꜋9 S$+su8N.P2@KP:b^]mH9C6zbG(%IOsڷFT鴓dxTZI(z]Q#u.˩\q .jd$>ue A:̩:TE~c(%IOtiЀD_Luڥ_?:@v:HML9huea4rZC!\d$>uޥڡ.M<bG-5Su8{hْ%&"'qI%GВ$ԧλhA%HuAppr~'NĨQPPP`$ q 4ǯe:jnqj'n >>>OM"%%yI.ҵеv|8z/pr:t7|b̘18|0ڷoo3$-\#_~qG""j2nqj'vhOc̬YѦmȐ!˵5kh}ihi{VZmDӼ{{r4[ۻn>6ik֬>m'X8v>c"==G&M,Ϗ=_Vrss[m\FزjE;e#<#8]v1o<|76$aEK5k_".j2@W$ qjnX/\<qQ%$ qhڡ9.tP@ЁNB\@-Fq//kW +ˮI5r +j @'!.jdK+y?tgbp"JhIV._O}@Aгu\!gN r.Z TɁTN.Pr-IB}&#xxPv nu'n(%I[Re'tc]섋 .d$>nbbZ.#GӧWA{qc'\d$>nb<.ǎo||j)W  .jd$>nbJ]̛RFvi)nw섓 7 В$\ArQj)VK .j]Up8%1X.QQkg8|b#hIv.&*>D63II; %$ qK^8}řN.P2@KPt:2?'L5Nz ظ~epc\d$>nI#G_qÆQ廊'`bW?vɅJhI[(@?0[4jTk3]oͱnW8pC-IB}%(hؐ>0};U-prᆒZD,kRεc蝈[; %$ q[ʦ9L&t\]TT{}7?UɅJhI.UC~qY 8pCɂ$m]ݻw>II@|<#4-z5%[vޟ* %GВ$m]Ӻ ;Msޟ* %$ q[*XQ{.tS\:@;:thvI.Z GRXtDɂxqF7BAG_UΥ~}xYsg~sޟ* 1tPׯ#<3$ǭ]uFtG]̡Q8E?Ʌp_/_Ɣ)S[q+V &&W$&&Z Ȯh7UmWr'Vϥ~}j{a$ w`Fjۺ s5~Km񈉉ٳ VhOcСC˵ >}V.њhڌoi˚VXi?Lmqq\kE?eرF+`*~rm$ǭ]ڶɓ<Ц m.K>TX)/?mB 0Nmj+([ߟJ ZI.P+3*s4g>w =Gz@Ӧm@&6ap6؁[ߟJ %$ qkNӧv9}o*oN Vuu)kGcb4z஻tΦ`Xw6nl]8q"]7nvwFn 8pC-IB}ޥKz:k^ԅb:e1`=wڮp84SVS 8A!,f\ -7 В$]*X mb.I" HAysG(oEm3gҴLQr47ysaM=K R֐{xЖ{t֑K/Q>}Kj;j'N@-IB}ú;vBknyjpivj;u'it5Z:7˖-3ZCH^4J2Oy9Fe]^n8tΟΞŸ>`[AKPq<ż΀6 pK\; XTk<Z @^r]͛<\}8pC-IB}֑ѥK]BBhN.P2@KPqē.{7@LOEprᆒZv#. \ .d$>ͥ_x JhI#.НLHۗ S\d$>%&{{ &"8pC-IB}' KJh3B˖ֺ.F>\d$>k.3ѱcG Υw?prx\d )r Xz 92#իW9'X/N.}e@e //-Zi7UOOO, ,MҗAчh?I&ٴ=sԫW~C!33|et,&O7"66xnЧOxzzbN+ҥKg} XsEb„ G&MޕcJKKO %%%XhS~=.'Nap7SNxꑖ7|Į],FIE.ؼy3/_bDDD``LVtRDGGYfHJJ$bF-["ˁgUr >GKYBBB~!%%-$$;w7VD*r1၀9ݥ"?WIUO̙ooo)nѣGѻwo<_|999XjfΜ9s )) 7o^XGooA߾}1m4hUVǴil,^7ň~y嗑L,^Gqi'DƍѰaCDDD $$wqz)esz.~~~QRRpܧ3=N 6`С֭}>3f-OOOtŰ~ % ;DP- BdӦMx7q <Õ&t+:93f|' l H,NB@@!(hNh"˖-ԩSY&"A"#hAHA`hAHA`hAHA`hAHA`hAHA`hAHA` k~݉aHIENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/ai.py0000644000175000017500000000030712253362407021642 0ustar georgeskgeorgesk# Airy function Ai(x), Ai'(x) and int_0^x Ai(t) dt on the real line f = airyai f_diff = lambda z: airyai(z, derivative=1) f_int = lambda z: airyai(z, derivative=-1) plot([f, f_diff, f_int], [-10,5]) sympy-0.7.4.1/doc/src/modules/mpmath/plots/bi_c.py0000644000175000017500000000012612253362407022144 0ustar georgeskgeorgesk# Airy function Bi(z) in the complex plane cplot(airybi, [-8,8], [-8,8], points=50000)sympy-0.7.4.1/doc/src/modules/mpmath/plots/bessely_c.png0000644000175000017500000011705312253362407023364 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxYnIV[{9)+2k̢tA FXB (, 2@凲-$l ,t%d0Ve5PY9޼avND*^Wq׊Xb Q3L34ۊXp?lAfifJ`fi:=L36giަ4L34۔fifz 34LoSzfmJ3@4L3Mifi)=L36giަ4L34۔fifz 34LoSzfmJ3@4L3Mifi)=L36giަ4L34۔fifz 34LoSZa v~ȏ{x!??-L3ѬA7??q?l[߿-L3ѬA7軾^~W~UΝ;"4Lݠ'?I~~__㛿{N7qvgQE:@o(`\x7oUf7u,7[8 gщHΧ_G}n:SzRY>83{w=_S Ig|ZSn_SD[iJ#v?OڗMrUvt2MML*two}91*cNjx|ikAڳ90gVh̚]hB#MAC{Mն#9 8ERi5 ܦ c![iwׄɒ2ZvS[KԉM,4L &_?:.x "8,FsG13('WLhbUft r.bJ1#CSfZY?ogdP+2O& -޼JTNg:Nꚝ\Ml$n?C5r? uoc.A4gkW2/ʱz,/Qk#|Xe]c__2R)eD8LyL_5 _JOކ&Gҩboȟ뙈iɶԠH̭Š)TL39b`LA#fJy<5YX)R2 $okUh+Z6VXV^%rZ%ʱpd[ڔqZx6\j&7IɷխYIsQZ3L{`^,fJϡwo8\O#dޜzrU™cƟb6ljesMmʷ::N&f&%KchVq _UvShj>tQ||hU `iPޫ"LQ.~ϻLj!9ı%x5A{r&p{8͛WDL_A{UT@;*hi$;g-qpc(j.v~;6کC)dcu)MRa5T7ԯ<0ҡ7Rl,MZk-VunQ}eMG ݠ{+|Iz-/e<̋<6"yeFQ@HQ؍]թcu[H[sXo- 3ę"ҤtRo*blkGe9js;N"b]|n2oWX.yyi??=1NOnGۊ}LQnMF4bBpRǫT-qLrŲHae(KRomv\Ngf2=;Uhp#0q=ɕ9Bx/>&qtzzg>mEenU\wi¢Z≒hjL:PY-6)mrĎVJiJǰMRUelEXٻseIf7}M`q MnծGY}F`Ĥ ͂h=]]A=g2׉( !Rǧ =TrU PK3@_Ԃ@9⟪ͧIijW v R$)Cmb@Qa$y14B[pMDH>=v,jL< G,8O߈12b^5dnj}EĈ90kqM S s 6(OTs GM9[Mh?袜+VvɱU<^Q; sYw^J Cm4cv3Y$F1eDQrJ*m!a{ &"6q,(4aj68S!ׯ&HkD b!e$R6TF$Jѱ7CcJ.מ ўf&fE;ze b۩pi#LgLh#aA7RuR-IX`NOԹ/2:U,H&K5DٞZ5;-ikRLuۊZF ETTO v_SS4sc]Trm5k]CYfW2@)}JZE?H`.@ هtgikR*szOCeˀu:Sp'%1oB6kE\+%6Y+k2J9p_>jB)ǔ9[Ss볩ҊG*{h*@5|'(7+byټ= Ri>mTQ Zr u,%XW3[ѴcM(:AW ~NUb|^-?aXn4 $Z3q؇$i`Ma% =* 0O)7c(" *lHe `wSJe̡Z>+Ɨ~rN(%g' 2ZlTf45)1qtvLFyUڬ耒i*!e;ucRe| ud%PTy=x X5Cdlm^ՠ ]ECL\oxsZγcI(mXJx%TItK:2b~-,sV*> S&O@z[~\9p>%T5/C{Ld(8r:v_(]`:d*z f.h 㹴2zPҦA-B}h"I>fu7A>)!A* qj2%A\tXk.DQ;h$ I|8A +rIMyi> =tIzچ {%^~lkiHuX r舃;>Mkh"x`ÝG#d.š4tnh( c;ZXWv U4|F~Yrށt֠GhvJGuUy֤G-ZtF6]vlPGzE_7Ewqt\ks:+ D=W#*ͱjaZ\SL>m'?;H3@_T0 rܪ-C@Pӝ6b8 Ң?DǑV]ET#Ң=3d !S1j#- %)ZTiÔ'iCҜ V#3}MRA5h&8]Ӊ@!?V flqtb_%l$}ty KBZǤ1 ΉP20ӆ*Z}ӡ%-!dHhqy?Yik3!h^$[ǮE@P--;CFkZt5C(r\Y]U"uj f\M/S((Q5u#f^eAxʹ2 8 @\]F2ws>'h|w}?? ŨAStGj-NL N>p6LN+ُ0q-ƹ>G@76YBI4t9k1v!hP+_֠\fRh+WHP։:H*9=A??ΟS7xc1SCeQbWCHpԞTk&JXբ5l H-~S)X´QՠshQZ :c%ZN6h:Ҡs䬕s+M= dj,=/u~6q4t~z^x!hU cۨ'ZtD_aטՃZ+G**y @DH9`"$Bq׾ 44D0Mʼ*BTtʯGaF|RL-㗮=CJ*'˵5*oV@Uo}|mfi}}s|k_O?͇?a6/OJmQU gDA ="%\Y49FNlбKҊJk gmjœ"J7Zop',oR3k ÍXd|JKq<ۖTMkеK=kPl^X V_U>Z} =e:F1C*ZYc4sĮ k=q)Ft-5նҦJD;xD%Ɂrny=_~/A.٦"B 8KA⠺Caz%4-~A2ZW^+4p/, 3Y}w~'CƜYsS m'y똪858EsLfl3 ]<@c }M + ;Jӿ.VW<1Xd?BmC|[R&"Ajc6aB|2uhdjg`Pcz\2nrǯV2dʋ/gHL8}Cb/LsOyZi9) $e\jbdJ8;xٖ>){C4;Ӥk-QJN0NVՊQ^ZA0{Ό "h d&i5sG}Y0s݉?z %s1o εI؁*+ cK\BO+UJ*֠g45)ؠ=@ҞJ{ҜCVrN,n^˻PǎMJ“Ǭ%ɞ#88=􃮱OOu:@f\%|PأFhri`O:#i $š.3« !]%vNX_9i#^-)?hyV IAװ).0j~LW1Kʋ.N}_ФQ0l R 8`bT\eN9[A#5![m}Y b@(Y>5:z0vS3kTy.\H|tM}=AW^VKjjZAs]?ibW5p.knC/T9ѦcX3i֠SL {\2[6tnԠyJs6Lf&*I\ D˟S0' k :L V* jTܥdaR|'O$--WhMNJv~NDYE|e~%{~<J1є */E~8I5o$RT7Ic()vW\ŮlwCUc01Ȱ_8_XV6;LҷS(lj\ g(W&o=8*T;^kp:hlHik;6IBio{AR5Γ/Vio0r4C@ Ox &)fb^s+TwK'f :":sQeM/_b,?ZtVJC<5~&:Jn:fikfQcpYS{=t!>R$!!Zzm0uQpjzmYͰ5Vsm׉fUxM U[]Rr jȱV(HnSmЦr}^ҏRgGL3@_7In*`rc,8dE, Zg6V㤷[[ׅ,wCYZ-<֛8*{U& %XIb5ancQS=_].ZWګ&5iRЮ˹]F3Y 749ici8_ūU"Ký1sR؞L4@%*DM庸{$(T,jI*Bܳ#G#148 &e׆Zse63 צWRE Ft~RpA:Q|t)w cbaJo(%>Ф->Ӣ%ת&A:$ChNhon- k٧Hb9#,VTx6[ɵͣ: Y1aؓu؞O *Fo]g45L׿d>РS{J xqH A>Fena 57;#q8xՠ>P!`3$^ݽ#EBˍZ c͹inBx)sFTڱ@bpN٨>!wUMݵQcSZS_=CH3@_4A74&kmƜ̱#DEюbS*BgLMJGJ+KUiu^.S~nZWڠ pXQJIiZ:ʽ)Nr?~m|&K3@_|,0jAŃ=, Bav\&u~Sj rE*-d?m2j Ɏ9iA  p])UB#_bD1SrY3\bhmI_ oh״2O>=-]-a #}MJlqk͑8Qdf d5y<%rM @R(CbQF4X[@ZdnE7=̰Nv[Xڏ%wmLH{5Y%Hb g{{4y,r΁9c^How9W'>Zfg9(.ohO3@7~i=o|Vit`]kԞ@s>OkqҪZq50kgM@u2ɝ& -Ӡ h7xj8YftxKDKcʨ}֑a)\A5Up$&`1E&,LSCA'i;h:2hỉi1rԤ[Hz'z C/:Ѕ{q3k|30]2ux]0PKU @[Ʃ)#eM >, a$+]zS\2O+#߰qRyZx>B%(Nޮe,(o5jW;W C Fk8oѷ~[yY0o=.>4t>?/e?'MNWKеƔs ;uG؁Pv;Zǎ! 1( V33GXK2F\#[ɨIg(VV}ktbIh0-j<1ɵ庙#H='t=-NIe?V#Ic>H3@7:D6f&*vX&=hF cxIsp}] m0&q;P' {64TAE۷ " 2zx`.RMN$ӈSNw90A#bٷGLwשC2w)#,< ՍI *p⊤%8=h> dAADN'0U9X amE%֤ vcYy@G Ket)7eNΟk݃qeD>䣹90k+8ޓq҆y9{Q4:Iıg}ldk^]F:ӐT0+Gkϡ2`vH3@_중bjd ؁l#6F@aE"mۋԂ^ДZFpK * `_RɚLiu Eۊ)gue.~71]Nj\RXH)qA (/JFihKݣ\nCۭjY%󯝽ӕp3@ GC0Erόvf E"M:hV{6NNFBҊFMVaoMȽPEѥk ;`k]P;R= jijfjbuX1NJ;,i={Mt)S:=W }^.čؑycdyiCA5Xc`L &QSC ;:sL;IdȆXFv,0j8h#}DzH뎏 H!a95K{O 6,؋lDP+eʨ9wqB)3: *WhAغCOyu$tSekh*L}SF wOotBn Hm2:kYNhk V5h_Qu@LG3"(XwEf8E`(*zFC;FЦ]<(;XoXb ;XyB% e3(Ly^P. 24Q1Nk[hPvy{v|f?øO-ۑ5ᦈї `p9_&/t%j6Q5yEaR0tv3kЁf&Y60d6|> 0l# d#r&ʖ ,AGZ(c<"(Ӌ6§t1+DVt.;V@3 40.噚5zFyưSn1춆•c3ݺ`}v  WlPqfg# { 1|}0:I*B$ dW7ڳ[ V*ewxJHGS.,0.CF5F 1}M`:*֌(akƐ+. 6";,@/D@.-gw(W=O1H{5Oj`eM'+`miK-Va2ţB41Ȩ9/Q7pW_v0 Wspel݋EЅ{dxs+_yA;+d҉&c[<^#)3hH{V-[ȏqG0BxDTӨ45CY@YbmN\m,`艦CpEwmPD:{D/@KD&ê j4h?+0{0ԫbX/xptΌ5k:NAOP9cM ԃlŐT-:ţheÙA1ӟu`{e0 z:GzAV PBxCY.B ܹѸ[Ry#ӞnPkAlSKtDfCE@4JG<Ӛv3@{K k43. ,H zbF-НCAG>Cq`8AY#֝נD!~;th5xGE5F9```jZ%'trJ)=7P=7,,vtVα=ZW_v=rkG5,Dݞo?]wU5 hu͕vH(Vl@K"٪aEKPoIg-Q?{ 0b zY=8 4L< bxїYI{ iVCPԳH3@_:GzoΠ'_9G?@#sD;aš:D'@!4̊Fn8Q̉b:W+ zF-MDoaAt?fƀ ?G:Yv`oJ +GϷږᙽ|_F tx[$$¸>Mz6jXc`(cYpGZP*W#}ׅYC=Ho8_nוw18[ I#0-VN?yl9L}mUоlc0 rS7(l/{o,\bϩXuRee$E{^wr= v.Z9e}z 0@3r m; [d/iB"ؐJ|IYk0,T9usJ4p;~K^[W`,%v/ɬsF#1f`;i vfy "~-REWwA}̋Jiֆz 7-hh'Դ|g3;t5g;jYDihO3@_zC~np\ E[7a:xx! pb fVjRhfH#2qF@n;riFS[pk0͂^o ]OaZ7q؝C`.xuYE|,!yW96~+o|^u e#nLkA"kl}Hh;6;ƖMS/{YGw {dze`2aڳz"toy2t@r90sIÊ)q%'c' CF wl45C9P7 ;^4;T޽[oedU!ksX`^xpVXҫU42th:"SCv U.̚>ZyIvrAm`]q`%ɠ bIPq $a{<elxx6݊;=82fj 'T\h k/Q7{FY)` [v0u v0}i;(èwNXбvJEH8KZI{ :ty&q×o5g3AG45N5*'a g:pю ቇpK͒q]r*b0cx_`hOyOxMqXIlnrQ76- No)Doqϲ$S Fζ折&&c-u"JF(g"\X,~_A^Y -Ŏ7zQzx)m'6_6OhK%Rf`vʌl' GźC*mA cM8d$ʩQ#&g1w#H;Vc]1#zfg~g7#[=pJ-s<垷~_FϬ], gF81Ke D]~W Fi<5=ayvO]& IDAT繵y^ΜDߧRywqAtհC {t2r), wN¢r r,X۴&}.}6EӁqڭ1솁 sN(aBh}Rw&jۡÆ0O_F.gcYcDcDtJjٹ4Lg>h{#QsV]Fs8MXO~''>_{;,@C N ~iDG))V;b7W9eͽ}Sv=z!=婫&o`oA܂yvZp^=-Dߢ,y.tFX? BHpqZ`kx [轜7jAٕ`xd6-]JX$q@;۾!^g&ԚRy]3yHq#4!'Mv"Rn-Zʪv{u8/N 3@GO)Z8G?ʧ?{]j!j26|"rŮ{WԳz]a+® iH=߉;#}t-37Cf.xummzn"N`@M&18ߎ.C{7Ex',XD;N%;` D/䂞+ R*ȕx  Ͼtٱ;y(=z%)S:=ˋ+9;fQB4aɠ4b@=Rs .Qd V`>d v$܄ =`= NY8 `2}[?9B5W,զ{$y>uf1f G){wo;=@>⼱-WXa5auKN,`n{uV a!w'Î[5s . BÃ`nU>/ngd`Ϡ6גB/{8CN{8=d<5L'tTH#߻Ш3)qw6@?g>W?w btg=F7yao8kv,PoPCvFOˀk2y ^$pZ'$*Eayy킡[˞qpF b)X@ B0ql$#Z}Ň盧͚|KwO{%Z̝_c e;(g EaCQu.TҘa}n j"-{ރpF0.1c][pփc\UhbtAٱg`1 (Wҡg=r[ &v] K8MI`ڰ_W93F';EOOi‰ӖMvt# ҙ;j}9^#VCN [MWȻgnxbAg3]ܢ(+xE{Rk7qvS+0`Na /`=yzUv r8WJl;½:ZbQo/6A{TsSvgBK.`%;W7`@:Yzwn'jѫh;%LFE쾏ǑVt+ g#?#'>q/۷y'{~PX s3;W/^7v"G߀~- X,PojV&>ovo/˸`bc,0uǞ[(A rnDn w;X|1z8{`#0{܉|\aUV_v$iJ9 Ȱ{pBN)OwM1z l%M}d? qS K0g)_a6½/p{l^Qkrnl&}`{]M9ʮ_wxoxZ6,W\Ybta͠󀍞ԧk[]{ O!7 0~稟?">`hn.jLOO>$/"gx饗o/O?ɟ3_җxmZ '* D!C t v )gk.PZM[j)~$ތGdT hlqf6{Vv*7Н!zmk}VtCG3'>~#j-rb7"CIJ1KԪVV8m:܅-/ 7%[5WʸzpQio$V(GÛ=cZ0T늭d<|)! ΧyԘ9Kr*.t@ { #8u8W;BX?:޽˟s(pW?q>/oQel6Wpu6]\ Gr'ȫ7t_;q n}tl=~%0H4p72wnQ,r 7 ny9>g/ 6dwlEXuZ"¸a &`n`l̷d yuo# m| {; ƨR6xF1v6mV]<Š˗c廍$1$n!_ޚHsp g '-[:w&24|Y| tpw0@?>1/spo+=y }06 żЗ]AA^{ ȻO`Mv9nǞ.qWZ"]j <40 RmAe)O+iFw_'ݫHo}{X[V섥[tGay-XsPdTX {1I*Tr< =Auzo_.Q15-E׌NMd]i&p7Qx$}W:[n`!_%vC`g(,Q>rPXuF>Tҟ)}t[Wȍ SX  vUf77Tڿ8@vq m0@/>^c};?G|s?sG?/w`}߱T09Ԝ*F]:Sbz``NDb]itZ};ʠ:chT=Va{Шzߧ+vab2=X뉥Oy@ ,pB5Ȳ0zyY<> G6G`4;qE$@f1[AnBQգ/V~錧KS˂< ,Mb_dQThUFܹ$kŻٹ^d XVOiV*Xl?]ko=ԴHD1z݊s7[?ǣ;/|ƍ/c{UƆ)ݰKz.tD&Mrʎq[Eڱu.6rvġ: F1\^v>tZF+sąF&蛨c9YSaKOTX ܊C [|&5ow);#AB]yϡA·wF1seJέy9_aBP ev?;Ip)py{=ڂknBEXˊKr8q>~Z{{qR ,"=W IBBR Н*vkX5^4Fu.U3vzi? b nŹ2r xG?Qܾ}}-4Ņ[5B-Hߴ Tɉ.[,l9a4gtɰ .k09B8U_"Gk0tr`FШԀx "J.9378-:P^RY`veXӇSmGۢ Xv;;X߃5n%:fgpjV\a;XxˁDdi//fqx/EDF9^X ܤ z=<ܟRy >3)ʾ hXraG<쐷蛰~wLmNĎfa]zƒd@ٻuŮ&!R) _a@z5n z"艢gpԷWoT`ӟC%v"2$Mlr=ˆdn{_[y""ћ7޴| \i_ )1A j_Ԑ`Ro^h!k[#Q8(ߞm͚5ϩ{Ϭ5g~f͚5h"-lx/riMs MY:~`#p;b)bHaT 2c\Ah ?c:V;`u麷щ:%h6ƓYNOb]lv:/#iL(A;FEM@NtI`uL^2X3ǽ: 09nYsK.K&h:3e,f+wdfniu^:m{ Glmw7~r'QU6Ɗ:wߜŽۑ}Ǹmg0 [uӝ4_&jNUlBg_p`",gbn9p#R??$Y<mX|K{þsE-O6Q~8ۄ5ܴ|6\#c194V0<<Qy;)B83aKpRZ3&6 A pI`%a 3v?Z4~BȍZLdp '4V$Z 9GvVS LO[qۗmUf@h qwl\ɓ7vlrWl-oy84C x[N`B`Mq(aeôqS "wZ;ܡ|W-:mEΥ:;O?G-;8e`{`>colA<BĪmmvdmVg.p V1*,{Wq{cQwn2a%C4)`55 %Zs.Lb+r EK-!F zo@)R108'm]y<{K<s:0]aDCw:]jD)`kKY#4+I 跑pLp3Lmy`id~FN3+@ЮX`Hdnhu08ĝ6FO[ ,)vv4]Gb# 9lnopnѦKh8?-g+]̼fmz)v5%]SF)%7eAUۙ8lE{MڮJYuI5,h6a ]$0d `w*݃[n,vБ^QfNk1O†%L=<9ׅ{ `G&' Nm'1?uNJo1?\CJ7,JB6E~.U2X.%)'aۙ.&tнVu%^gLaP)͈xLÑmn^n-6{vU³7L?Uq#rw3 c8aʻqML4Zb0/bZ?Iv)!2_6b08&m LG)| HD^V:#\+HҤ!k_Ep4aǠAGzAQv'!7Uh\`IX 8t>܉^^c"w犭0\3`u bch 3l6h ^A; _<:5l伺NXM^ m8 MsVVcsp똙W.>{;qa?1:Lf*; xy0pNk^V9@qe=Ǒ4h= @+ֻ9S m:  GH˹5PN)$9limzH@/H3mtĄΗi\|u"!byZu[![&51 k(#8cY*5ΆWm aY,Cc=,{&`6\%낙D\wu&\cF'ABLgIkq S:/hx9h X*-݆\}%?;`<ݱf6qarioIq87]5_497-m:S.KNzKFNZsr`6h+l͆5)& qMHNnV!Vׁ;gr^6O,010S=X@#N:FL#q:iE@wبE' X/yypA]w1hNJ >44c13 l ('}ӵbe< ȩ'4 EҎak)7k äar@,(.%<(O`)EH.>3;N;2yFlDvi3X,ٙ]iuɖApڇpeNXIDf.Z>(DPRjQ88cAbZZ4vhF@/HN-%Kܷi9s=S7$v5nn0١̫#E N4mJ59@X"A/L Gs-@wnA@;:?tm!,^vƈEy1YŶu2FSufd`g4,d2L'43D Ar?̌#v8KvɇqN @$'l4H(&W5m^ af7Xy)hbv ٠΃prB 'X񾸫^^j>1a:%Nc 8f?נ]MV˰Q*:d6DN{e# g/"s a/ۃ6  asfǨ̦Ъ<"嶱ز-iJфDfŗsܼ:L`} :݃07R 1f,Y^:^MAGNBn^{ysf+q˦ (<(q0G{נAszAL;7[wj;m.24+Y}09@vyH:ZWAN}d%.˃R ξs1K䶽}[hƛ,v,0 >R fA#:G+4rh>em֕VG}2l aE.̽F)&UۢG$a´!Ѳ1O0bFYt1Б^,fua;A/bN+^f%tBAvrzXTlb;ZHFPYф0Z rO~JAfZÀzFl\? EMzeF~0Zry 3p|֏Hy¸yβ? #:R.=gZMU†Yr%T˶!f{nvx.x4d Nl(MkvFqG%D0phaHblS ;5!E5jA{jRY2q9$II2ff1'`#N (4r5p͇"^H"c$Be+do{VCf.YĵPݙU048T^jPC(Kqת4BO=nV`ccwu45iAw#|klWظRQ s vPդ5 M*Xn[!&@ N~B5F"[J ]p8t?>7P/Z%Uk% r`3]bR xPjZ3r30S>[~SjX(:~g\N=eZzˍ6kk :5]q V4hUmʉ?;,L]i4۝9H;J^M{U`F(z6}.El`eC#4hv=@9r7xcvõ^|;8~xgA%e kEGЄ TLXE[if;"j=0E`It0$ ^לlA D#(L`}#F&ω{}⋘s{{g!l3do^a>Ki5OR9gܬe`0%vdgN/w}~{qwfg',61fXl j7J`\x{)#'eWtm`\ٷr6$0GLZSnh:Șd\A4/fL EINy#@dIOvz\ diQѵh9 22"`C=zG5\k7tnut *Z5:Ć`CSx>iCל[@]}xiұK:KsWzޠC٢@X5h\4h^Br7sVAm.sMՀ̈́p %TZ{^GP5 92rL.:`|Kpv- Х^g}V ;| }Mȇl PK-&H۾%N{7XK(@ rMTpu&ٽ8fj)"G,pMh`6VB`l:+MttD4^e e*AGf'ٸ<~, [[b3˪ˮPNzAr :5 d ;'_|4hJ CsYw9n:5Vz xP&:7{r9m#,}J3D&Q.J1>pro,,6- 8Jp 9RnwqVax} B ([t|N4D |孍5xM280drn萇DȚVjjcJ0>nhc6?'mε _ {-U$HߍwPG˽.Zi&rsHV3mE$RT!@6"jyXQA`=t&B#[Ԁ9FKu,7o(;pֵRɔ7YΘ q&SbIȇڍ!uV9HgvH~|vV5L(URVG2V{L|Z0g]-YVKe3@GzAr8;LES,!f=cFB„+qGѢ Sמ3p&K2-Bkuk3_j$m rU  j#T(W&`6*lALX<đC wW;L(P # 6>-FYr0^3$9d}[Gĵ}T 5h쑢(@Pt~X멅?hiX)Uҍ8'Ԝ9s6Վr>2=e7 AG]` 6jJ6Xse5qэdsP6%1{\נ!,k90Yc5:2+Qmρ$]E.y-m,z LTbCZX y=8KW>sРszAr~ kUc4C%r(V1#TpPR aOjt +@X4t#+~f`iMjڴ1pf~ϙ92;tzgY]G@3zAJ&TQ1.K$f da(Q2 \&0sZ pLj\j% 5fcY_q/$#]aBHȤf VC qf{ͮ@<8P9 Ro " +!09>|+ˉ/^s[2uw!+#/7 e^$J=Ӡ)ʾ`tGjj8b3C2y~šwO6mi(8d`QDBAMՀ!+̬ L_nˣYu|YfjU+[ 3Qʣ桃r`|!XT jĵ}W g]0Ȗx84D0q6saR9UW`D&k!t<5XNO°d>' qQ2_ivMPT[JkE.Efw+TEPs DYK&r/bqh巋i)٠]U'5tW9fnmܣ5Ǟtx-(ݹUw[p60bwDB5rUdK990$@*849 ]/˸dJBWR(ڰБ3%>*8?!jfe*Tsse8!eXX+i瑦jlgxh18O yT :y Y%;nCV|cS]kVY3E2qP`P_z7n޺ju߽Fj\9zB&i*BOm 3CL"u͵"Ҵ!">V7:]З\rIf8~8nf|ߏmc<.~/*2Ja_J@˯ @@rohm$ ZOKS#CsJibz|LЧ/yW4DŽ!{'].ΏʤDyj\+ksiWϺ"?|vev-zk5\8|pgXٿKZlbsZmtp䅂DshД,d6Tyi,|M ney kF%(RRB Bc+8V*/~1,Ap8u@ K Zc+SYTcS9S ﹲaP~]9za$%.b.gW-u7%_>JẊ9H[+m0W@iyafW,Ork@^žUYQ'Sico|~2:J-:OV.TJJ,ヴ %?h(ˏ&J#Z'P"E n\.Bjh3[XBBӃt絅|Y+8SθE}.7s SZg'wZT|}re>L2 t&#Ru;!IYetv&D1A$H7`2̖-%Ec+ZtM"xi3ׄyUvVtg&Ipgew*yYw컙^B,MAjy h,1^ 4ad<#Xk%9B\[A.i)KtgpHXySɒ& G?SmYO_Ս5,˹Zi(_cd-ӓ lR&@J=ﯹU>cZl[+GБ^\}]6QjPGZIծl\ƹŮa пylo9rN[(8+mn%7,(封qO 9RX+ɰ{Q) +YӲz G@/Jmzsڱx}OR-Osbj8H&+U _j KRk P Ff\>:[p4/Z 5"97yy3!ir.e :: AkZ Ϳ*H3IYڱOcmwy9ˤ4.vEցqU0ϸW6)]F6ZA~MbՌ<6B{n1wX\[v ۠4q >^ '~x?WDj r2ɣWZVl4ڒ,벥 ǟ+b5{Hp1]f1'l{)C9y5Eԇ͸Pޮa$.z*bVI>RC\Mm&kBTVSLd@J*/1S -ej>ID 5di2 (a1r׀/F|Su$yT2IYtbUm斥P+{2RԳ6Rk$YЫ|k>_4D 1I+E#`9, Qj/RG+eqʜ ؿB%̵֨rqVu!AI{ᅌ95ՙ8wϘW |Beg@GzArXtgQ~ڪK0`? Zۗ=kzp#d T+}Ƃ<+(ua@ 5h+KXr>rꞂֳ ]C&qm|^3`74hQqk[|.+]ARq^(5e;BQOYMҳn~Ry\&O,ďw hh~i}xӛc ˗z״9Rj|LY̢L\lA8zdHAd|G9Z|;ǍU&ļn1% -|듂j1Xfj2?ddj9Бv=@9r7xcv+*s={p)wU|?AQTi,i0O6t3̉$ֈe3QP˲+fjaQZ{TͲE]ChLM4\}@v=@_r%{?8z!6o;b ׿x1X S VYc߁ʼv,/#`NsXxs^^z,~A̮+GGo 0G-F܋V*=Zm}6ѷcdk_۠k1/c YS_,Tţ SBr J=}_r^IB+KoH+獰sbK1q7[hp&x7lku j 5^Ro)0 >=  q՟e $ 漸Q 3$  0IoE&(+/[h h/-/*~1q9'Tj~Ayry8KhPfW `Ĺhl5ܩ`<~ °ݫVʜ|sRQd\T9GK bs-:Pڼ2v!*za=Em4\7@/١m͓k79K"|H 4a{rd\Ϙ3jW^\Z,+o{Рٍ֜6(J!-x'M!QG; 6Νtnk|jrN̫Uk+ ^rG0yd]਷UGfNδO.Ysq-F>Q/PH@ '9Du.N}Xǵ o=PSyFyX[vhA#d=0AAWwhŃNI9B>$%Woݾgt"(L^ DdfyT4h9 & ۪ dZ~jIM JQNir7y$IBܻC*ϺG$̸kv/#sL>/Q!kGєžT@~W&vWkBW\Ϲôrޟ+];1ؓ{>qeHN⳿`*Gmu%ӲK/B%˚zhI.N]߸f\6IX;HkJHIjןW] gsM\$jgjCTCMRMF~]ʞP˾ 6тeImo IXI&ۛ`@H@/HVZzU Z=UlְR|a%r)hm TJQM:ch_r&|rVɻ_Rɭ)?ɓﱜfzQ"(yq8 5TN&nAOCWl3.O>k4*Z+9-VZh5Ta.@$(yDGk ~R&qꥷiə8jym+ U@/p~bUJy!J gGwa!]vQb&?k6rR4_lш쩫 K!jE$i;TRX(jJ̇ Lkλ޹¼Skvm`XG4Y\kffyR J1){npaayVyGLv "(27uqy-4 j3L[p3;Y$M5k2j)Is0^Zg(Akr_aUT҅ŧǪ4/[q.zat2BѨ5̬)W5%4J50j?ip)Vz08Y!i@U΄y1Ěq-Jݹ^a>e^3Gn5v/[ D"vA*EP.# OFkx>?'V˪LJ0 ,5g'*<}L, GTʺUY ^MvXa".%zA5qSX @m0KژBEhcqR)9GM&4#&D7ħ~T*-RԶ4 eO@2y,yVq*($zgFoQfGyP\.vehh9^g"]_w$sz.X(Y pһn}੧b?|@>]H!*K;;Hg0Bh1xLaT9xiB@ᇇ`eOWfÇkY~Q'?ypXwPoT_ ַwLJ",^mTd5%U0G-,06QA1BIZIk\?8gzˮGn+;QUyjcOW4OQ/`(Uò{ԱҎ_MG7ޘݻ`EmY!DQ2YhXafZ۠QZI͕#ퟭ3m Hn%1g9˟*l=%̔D9C嘟9{웠oK+G2"+˒Zol|s_җ_eF#8pرcgku1;4gy~xhh666p 7 /O<}c_X 4._Op1^7Mn/'?xwSO[o 666p]waiia8s''5}NOd2^ndW^~g .G?3'>U‰'p]w1^/%& 馧~w}7a/򗿌. .wމ?><^o&N)Dsn]?Iآ'O/+;p={_Fx]{o~j-^]wZ|;7 tQo5O|0N :t(N n^?8z!|j>?^կ~{/ΟF]t~W~W]toi -O?w]K.[[[\{q뭷ꫯƇ>!|ś3B zA@?^x=>Ϝq4\}էmdѣ>-kr ^םVx{ރs9ß韞9-W]uç?3+׿u˿ >1x{ރ;g}{?.O=okkkgKh:Ki$h:Kih%K_馛w N*9z(>񏫿{ߋ3-@hz,w;+n&r->;pG>z뭸 3@i^d\7O8w^<#x衇p'>}X[[W\|+?l 4P?^DDx;ށ~7p^WgfY<˿K/"~'O=@ҠAO|سgnf|~9眃M<;c#o󡽭p(L&MpH4C[7ذڶEs0'uנ-6*t:\v3]F_~ SBAE=B⟏i/W_vؘIIa`3X{=Mrqh~) 듽{֭б#8#Gy9q\\_]VShwww@ { 6DT}$( h~) XX/'&¸qg)-*%6DĎ,Zi+)tÆ 8}4n.nQv>ߋʕ/Y{z%u5bnUpm4e 0Djf呕4r ?DKin@^z50e teޞ7G݇'=僗9vfЪu<0D L,޿ٍZ/!|~=tǏ+KzBS!Ow{1R$ӟՕM֐h4ҲeKx"|N|c-7c%_J>1`(|^6m xb\t.ڞٍNQ /qZ?C…pMF7J~}kRRy4kJL?h???Zl (Ba13{+KWn(GE4)}]^An&QY}f!f!kWCItjjj4@>i҇(~^ٍFQ/q*(3x)ع-◘_8vA5+Q3\j| ԫWd}uJ6/'qC?6@Ki'YY0zsvsSʅ.[Ux#۬>ܑ @_2hU1%:p4Ӝ-gp,=̳N-/q DFBj-`nx*w7i/;v]H;dJиqc<<x;YY8Sh~)Oc!?χTVlTb}zjN(8[@~\>zΝ?Gqssb:55LÇg{=ld5np*RIb" ~ 0g>!f134<]uqz#Itze-+ٞR˭#s^8Sh~)dVe޽л+pLKcyr\cJ(o PϻSzLTPF);4y.]RaaФ]#rrx{_eX>LU ' 999.+fVYxxdRsSk>{RIz:{/xy)儭!3?|ލTrxHl܉OS)m2~~a8nĮ)᣽՘}a;4vɡCJa m[طƌQ/<~W ]{N:"aΠ^"zW> YY5e]pHR/O/Сjgfh"\t.k^+No% ";M6%))2mה:=FF~ _AKi⓼<8&LP*-Zk*KIKћeݖ׼V,ww'))f͚8e. JY˷^gE98bFq04}r ˗C@ 0cFK^Qz:^5 d¥ /' "!\ɥEGG{zm~4))|!8 _JS>ٰA7:*KP_D|ArN2;&<1ljS'h׮MP "!\qpE(6ϮPh*qv45 ΅!#fV;7jd %^jL7 -@ۀb,Нv޶#+mjİR$% y`Ԓ|MRVN ;z= h[R,|=fc%w;V6_JcSݫ4nBG#Gnuų;clV6_JOrrg^VZ LfwgpUyMQPP@HH:WSEBZc` KTJ?& M +/Q'11Jի!(v)ST_BWà״_矝I ' Wp'NN+EpYM +/Q'Cp8~gOu>y1H,jh[sHUA.4Yu&XQ9'p(|^6m۪O'~xq淕yOFxN+iF\-BM +/'+.TN:ٰ|\\3Dsyuh3h[sHЬY3<==8e@hߎ 5&T'JaV8 tz.{`wlyg]AEDa! ׋...mۖt.^hQzdwo\&T'"ʌy`pz v;UzZʝ=^,$h^-lIrr2mGioa\/Bc:;zfj M +/O`h%榔 ] <=mg`%ןI nfDޗyz].=H~W~ae4$2zPVkhwOָJ  Pv u凱ׁkEB^n$q~<* M +/'k(cb`evζ76n&<>vx݃[3HA׋`YѲt3XY10⎌&TBֱc!?χTVlyOE;zg;_9OީٺUY:w/ 2+k+qu ,Ξ=K˖-𰭱*,z7]LIO{^//EcpT&yNQTO7\(8@vй@w ʨ8wK,VDFKi3uSjm 16L_LRV#ۍG_naS Udڵ,]NϏlK+K$4[^gddU{ae4!vA>_JC{U&YY>NϼA,jIѩnjD\͚5#11ڵ>Ԍ,joc#5/|X~&旫˃0AHh],ܽԼTqկ< ̤oMǥ ~Lm9@_QTt<;~G֯_(>}BN8Qk+Zr퍁oaZ085/_;~#brp⫯ȝ4 t:ǰk),ڽdWBZ6#,8<ywAAAO& __k:t C!"Nȣ>*SN_|Q>3ٸqSO=%O>ۥڍ;> "DFFZe SP-/ZOMco/""uꈀko,ߞB6=gqSϝ9Źkǎ@ZaWT`3-ۛիWڀmWHJ~? **V^+| v_5EBBS|)~1 4='i# ,޿ZnJ*OZB%Z%ע?.+O$[8rUc0tyNS*뗔6L >>J )|-BS!3̤A=K8 N :]VbeeeYݟZ PVD~b>'r|2G)J+r}U)ݫ,ۺ:vT Y$:%|M]3viwϵيAg(T.$Сz^]ϻmof؛o}ڊ<rS\>pC٘eޫ^Π:tAוI(KCQKBZ>kY̹u~bԍ7{ÇbU8e.o'!!!!DEEBkF|VY }^CyF."V\ koRDݵ+!޸rA_K B3I$G^Li)3. ᘆ?菋W͞/gKrss''&MիPF GY>y33e+|K_xsѡCR"aL8~growd}cxyY.tP;.:+~q9DBFX+3TZmﱿ&UE85n>@dtn:&egzɝ_I1 jU闈K-|{Ŧtԉ~费 i)Ĺ%8 o;Ln[ZE5нo]BWApɟ3[i]5Ot-ū7}m T 5LE"!+}f#5I|?rgqB[|_~~lPnz^߶!dep!wȊ8*.UQ9'p(|^6mJ:Obf=m\6+-@pe)Z`x m1'kR_ywٱM4֘^{4+Z~dzzD K@α\+7/A*uh>*?w~Xex8qV/N+ A9c28~jf4%imL싱D1HЋAbUw[{ilzM󷚣83 "ȉvGnn.Ge|Ze˖/}vǫPDp< UJ>duG&>muǏd29eztGe"!(zӦM9rnݺ6[ŚckX1SOVU0嘈?q%<~\j|nsn ko$x^0Mf4F9).^?O?ÇIJJM֭޽;={dȑ4mڴ,,xIE?'&]#i|ݼyo{Un_VDLܩݹ59@N+ A}ya >>RV-cU޽{ 6}k6e׃P䓽T"i0ȡAxXV .(aab >.-%\777y駫~ZsɓEiҤYF/HHK "m# IDAT[> ȭ_ZmR:>}ZӧiZ,uɓ~&vpDoJjnrd̘ؑk}@7\w_ŋe̘1%1cHBB֖ɓ'eԨQ%c?C5زE һHbb͌[ä祋х'fz0%gK~zdʔ)h)Y"b:5ĮIK㭰Ӥw{o:`u"GVSmFAu>˨R ڵoqϬYjTUiӆua0hڴ)k׮SNlݺZ[ w.~VR%_t *+1 )S@^!ܸ"Ȟ={{f$g'|q}US,٘I0Ȏ;$s6Th*ls&g\lN@&O,5`mdddc=V25k*IKe-f;1b-7=|dPR*#F {OGA;eD$Q^/M6= v,B{VcRŠ76mS[~No/rssKR޲~zlP/k׮-Y=2bV2 Vs۶"Vi)|VF!"nת~N==iR^ߪhT$i۶6!KZo~ӹ. raUp,ҶIx-o}9 $ݻw@6m*H3gΔ37_\d&zl4&ܔcj#D-pJ{ zbVx۷=zpnV߯zQ̮]߿?O>DGGW<8&LP*-ZkׂomUJxOϿ2IGGם}a1N- t![!3 RX{Qϋ'ko>=}hVsmS~}[r`o8p ϟg„ 4h`UAMԭ[͛7#G߾} 3g_?X`3<ĖYY{Q)Q j>BS?HşwgNϐ-[ ? hq]yə3׼#C z S7(h)919.YB{ j~L&y饗wwwr*R8P+^mw9rªfn,O*{C=$lr-š={سg -Raέs31uԒӳYFEZ} VSdYx&kA^\ֳ1yM_z=K,GyM9mdsapȀٳӶ5R.KgEDa*y<^.o__<NoٳNGJNqt2@[Rn___:t@||<ϟU /2mӿ.j"?._P:Z ɲضm<R}n;)d1mɓ'STTȑ#K;RR`07||jt }䳲Z~6/[_qzaΝ#!!;[y}[*"!@޽ؽ{-̹OWO޳~{$Gc|Ʀ3EG {!//Ӈ̝DYr/&L?$??Çw^兽{[7eܱ#8ԳQ" .g)ex-EDHYh.[9ӧUc9N"•7&-FH~e,ܹロ\,XYpJ:Qڤ1k/=#;;aÆqWHLq`hvXJM=+ /0 duه+޾x s^<1sj ED[o62yd1`;jl\GɓrM7 ^ do^1`ёb.r.,/Ϛ%9*x j+aBuR$%'E>cĊ$_~]~ PޚHU ڶmK:u8p6Zj'fth \ x7j$pG 1b Kqo-[o')R81D:i5)NbMLRurj| %'E"&: AxT,{y]aa!N:q_,)tUDBP^zo +W;3}wʹ|h1M$,**bԨQp}{g؃tƣ+95T*k_ݻ⣡CyIxx@>`x2s4y)#GPPP@޽2]SU J&M\=yGpӻJ\FMu$PD:u*Ν;j*\\*ܓ[d3˙Ϩm^xF˗˾}o>~iվl–>ˈ#t[(nz7_0^2{U7@N*Bͭ09iZ}؊M/[3ՑD>/F/Pvme-u%Dߌy2D9i`xMpqÃ~&M7߰pBPMlᙍϐgcN9E f!yU2PA!Bg"yvoUĿ/ rdؑqyEy%g;Φ;?t:}UYX_P~<&Ҩ+kW3gʼ%z^6nTJGfmZ!iqk!]OiJiсe-y]V U"!@:uh׮6 {;V*iɒ{gϴ 9r1c "\=zTn>t 3/Wm~Qf̃Å Ss'y{׮]Ybf1cpIl[<+<۳,ԪbHS7z^.\ ..W+ 8eHXбc1 wk^b(:|yfme-^p#Füy=zUuDS'IۜV~TKV/a28=üdffrQfl-Buk2} .],?0 4@WіVfsqp٫ʼ;?a,? 1T"d&11q+o;Q8qgWU =z(5Z]',n[oqØ1c0l~V~9 +QF|x+R֦`3St}\j/4o4@WG$ o+bVӗR۟/L$5zGyS$\`t҅?\-7"`L&}-[/k@^#F([tRzo:o&QY{~y /F|A=z 7[sDBDxyyIaajdhE's?ӓB(кT߿_\]]f'uf9 1`Jaz_Lz;LVuy钣lu|Mb6eQB(2 s2G dO=-,,///&@uDBa%//cǎfOg/5df~X{|-jvC$aرF.\h4N6Խ.ѹDoyqj%1 O?wxrC˖-YjYnֳm|=βUѣѫWbJ1NTW$9Q9\\{<`Q__Vܷr9UlLJ}̙0|p&Olӱt::|>dg=>ڢ-U֭еRwo8tե뮻9s&7kPY9ug6>W=$$L4|nD4@[8?k.Ul'f]치Axs3牎c ~'-[F X|MKRۅ`OR֦p3/f3q\ӧCX4ibեyw֭۷og޼y_>+"_&*D{?moSǰHߚNnrM+^)Sy9ÇWmFFHF>f)DQ+=_Ona,;*[DM5_ٰaC[LQo0H⇉k_DG7{{Y#k^/aa5[g Hȧ![h#܎?|\  g6aÆ"Vh9h :O>HddUDzޤԞ_9d;SygfG  X""DܶEUXrҤItRk iRZKN(L+00 cNœcǎ }z\G N6g7|-[GnL.kqwg?lӿ0K הH駟Ӯ];Șe׏vk=6̝eЬ/_~ }_)lan'fRKF],iM⋸fls>?ṉ=Q:闼<8&LP*-Zkׂΰ[x1Z⧟~bɒ%52fu|tiԅUWBYD{s}C!GAAԪUKjժUйse6^weYeK1ǒx\>&??_:u$,X\٧ŀAv7- *96VK% ^3FVM<<<ȑ#6L"#mq]%8 [NTY]*{nG\\_~%1*|eTNuwɩֶ43WuNgؠ#ozS|cՋz'+ѣG4hf٦Hui^ <Ҁ|s+ikarC0p DD(gڙݻRPP#N ੀJWٳmVg}%KP^=BBB`?~\-ʤ^=뷒V7a2r =N ڎg5#Gxܾk׮s5[neѢEԩS6:B :dGdP"%ߵkW0`\>22`les$3f;$::矯_<I> 惘gKE$Km&`]#''J˺o̘12i$yKL&8qLJm-{Ut }MP"~o{d6%'.NdP%#~, .HÆ kl<+Bi(/M""qĉL~VdsC8oJO>)n k׮gҥnݚ%K\*ubHСC5j޽{ʲpӳ>치<:iN\:aq[["O?͹s?~<=ckW:m{;痟'~NzVf;*FҰaCV\ IHJJ8>+'.`ү|4m_BA镯d߾}о}{eO3<ÓO>NMJJ߻5kFbbb͛7/^Ÿu:Cd2m6dA\|jcp`Bo %(F[dY>; WX?@pp0Ϧtp3.&s{|w0c ?~41{Sߓȋ[6΢ׯWՖӧ;w.6{UBS0LGe%iPcgӡCOfΜ'p<8}=|mFGGszrg=]f ۷ʖC?pB23msQ+M4Irssٳ2jԨ*ɑG}TN*/|gqF12qD6mZ9hDIIIHv*7<1 N1XWrR%!W52f1ҫW/_ѱI|sPS $'\.loıcC$""F^qh. d#C d_}` @TrЪ6^:!Ӯ];$1z/ŊyfcŅMxt"믿.KYms^AERRDD$UeKA5G}T2Pck"q5F%ڵyQV-VZ:kmFQrNQp2:lڤq lDBu cF͗.ӧOgذaDGG3{l-Y.f7x~x~|>)?Vߍc*.+ZLq{X[l+˖-c}:qqq<_?߾]AREDDw~]{jwM!NXa"kiԨ˗+O>Irr}|&7Df$SG箳hd͸2p@gĊtVV,\PJW';PݙxqⅭؙS\r=dO@.}i6o~)QɮkP[#"%$$H:u-[XkyA%0{z|eMKvdlۮK}liӦתI\z4O!hG֚i1Bn[ $+"vSL@>sm wEҨQkV^*mdjb fLutZI<=Qd2ɠA3ge;&Ҧ[P [@Y~I76mb/k:8 )QQQ'YYr⛅PdkW/Ήfy>XrzJ߫tD *` '"""e6Nm ?Rs:/|u|SߞP5jLs,DARELIƍd2I1`=],ө޿ g>rX ,^Y rfYNN=mHᥚEZC||N?_*"]ONc7ɮ E:`yd2I``zpd6Vk$, 2`Ξ=޽{I^̞k谅XZmލ_~ٜ8qaÆ1m4YYMnU {+浢:޽ȋ#HLy[:M6eҥ?8iiizf+Sލ{hY --/SX@{c{ܹs 0 -8eVs'l Ў̀f|Ęc,.Oa/^?+Vp/!YIcq\ӧCX]H:TR碣CLN QHHH '?&ML8 !//Gy|z-uf_cϜ~s@ 0c@NԧՇ09v1r"m̩WVZ޽a3Y~h9>l}|+>JzWVęg0irS<<6<<8[tq .6mZ|&'/,9/ԩS /"u(3Ehk8i1` vHNt:`yחPxn6& .1T{G/-b-6v=|p[0O>&q: G #ԯ_ߦ x"f=[Nk/U,;wJ\-S,#G@+ϕI)2˾$WRRR]j$-h)S /|sO4e„ {g䕾; ; (U2#*jVuiVhuێKk~ڪ?Num]"(.hE!!B[!yz>ŻyhD||Sra]w>:)k+z=cXWWcNqtΜ^s4VڂH,B"|BsTWܠs*w&|1s|.C~~ujb z3Wob("2L.z< 0 W H6m`ԨQ(**Ž;xo_tZ/oMOOsUb4iܸqbbbHq<%s'ۃ]ߟ醛yZ/vmR@A9sl~޼y&MKb26=x穫M"q5^kyŋD\pnۖ(wqui͂ H!RB;uk;̆_4:ǂjee%ݛИ1ch VTQtTzJS3ѫ={v{!b2 Ϗ8`XIX=_{ ]Fz͟?~?@"h#S^vEj{viBt mWִF :ynpYFYQ$ā6tU՚=zH"Ў;lWәhn@L2V:a|r@}q.F,@"VO]F~~~e|c>ٲe bJs'`5 %'SJ`ݟ~-(P?3I$VxHWD"@N9:yd*?\mZd2ѭooQ d>|V[2CU~ XLb6l`@/r+'>kwjʨ}VXгhp$,YL (( Gȋn k׮%TJ"</fXI ȑ\ߟhfWvSo7 MKv=6Jtqs̭zf38PpR*klٲ$ D"JHHqNw_pY{tҕ8V d2ф v{jyQ/sۂgK÷yb;0 4sL@R֯_vY.8GFYI+Gy7To.O;|-V=Dc%b4J4c"pB2T]N'ڞsT:7| $Wۨ yШ3ұct(/;;r]Nx}T*ӟDQFP(!3lZ".8-FDoEAM ܅;M8w9~J (@T麴Q]۷ F{r8d9s&z8=T&ӧ~X9 TJ(..kԥK@:tkoP' _fh2 i膡8tK*a%$RL2JJ ((odи ԮI;@ќvLa\G1Ll2@tqz,@#&OsW[J2{GZ}K^KKKi$̰aT*mfoEEq94(a2hПjm Z< &d dIB(xq0}w;d2J LhsC*iX&*JNK ؽDR ґ#GSa1 Ow;m_;wk׎vehh4OM65W[jPSAf؞=D\p0_pSy< ^ız&_$t:*RCT˱ct;vƵk(66D"PZ`[D(Jڼy3EGGׯb <`IxgWu+Cۊ#9?49b1uԉO#G={R@@|u0OKKzͰ{ LF}mՅdM6Qeq >(4)ՇM+W轝tj)XOٙ2u2e˦;{,gӕPo2׆HAoe׹3̥K5jD4x`1bEDD8߭[7JLLtuiZDD⥮w|_(N*Fhx#;zGΝ;k.8/ЩS'5 &L@֭יS1cKnH@`iR,N] ^HbvөmL,:.mM̘oo>(P}ˍ\!!?'9d=eʥu>GwWBå(=\ m{V-޶._;wHMMNq]vի&OAA$]Y2@"11A4& JA'qRjieeeP*P*hܸ1ZlWXnI?@l,f [qVƪU6} g6FȹFXՅ'oǢ㋰~@g& F+-`'EO+HCK! B%"tE:+˵Тyls຃{]t: L&s䥺 yW_Ŏ;t*UhR#t;I=s/W\AL6m+S7f4z 6߀e'Z5@&1<a0k (M6!b"r'}?߅=Y5jA}Z m!R@O!.> O lSr\p5PNk@v6$%{s1TL1{r۴oe?ot~w|-dmwWp~yKIHThLn:&$^U@ #x` F*{û7ľ54C3 Ih#=zpj`P`fI{.XW:W:3w`Klފcحcpz?;̺4pE$LơpMz@ې]#ss" {5aO?Jjk׮ïQQQD"ʕ+k׸qm|ܯG׮fĵ+z’Kpi|37!U bZƠOx @@۰5Ldf f o&PjnȯunӪ`/k, RfHJJom>/a0jq`hP[rT7HTuRX͘1OKBB#+\nÖD loGfQ&v_ݍWwּ"G"L܏%00r5||`47q6yUr?ܿyVj?aX4LqbjohQ_j6QZ LkoYmluu.0M Gжʣ\ 6zzsd$pM`Om0],aAh[LB /snз/~tl0d$$"n-oxhSY L e֭aq0:LK&@9L땨]Y/AoF˙@h(ޮG1:LK&@#Lǧ7͙ܿvg %La ]Ih?h (s#h`Əu.0MN]&auY$!柋dށ<0],aA2 +s*+!g r9u[xХ W舧 06.0M t]&G柉իrAWIn30:LK&@.:1 T**U; @c%La ]IhYޯasBNWy& <8v 'UcƏu.0M tm&:Ȣeܜ޾ p&c%La ]Ig=[|Vs8;fXb Dx4ۨ$tB`8.lc%La 5М SR)tǎq= 06.0M 5PsY}dJ\"`R` +x DDÌ0],aA8n)/ZVL@|<`Əu.0M GLBܶ- 06.0M &!~?{ ϛ6qSrrQ%QQoc%La IrHӹZ-]܌ ?aX4 Џʔj6Zi`\@92:LK&sKx$,K+;CԹS!ic%La IJS1@r2Ъ3~ti" 9Q \-:@)-ko3~ti" 9~$,;7xoWi3~jb D2@?j|-^ ;l'Ì0],aAGMBste%L{$`-nPWFc%La ]m&Ϫ ~@~94P(3VXY0:LK&@$TgA:"{޽\93眙(@c%La ]m:6bGJ%Z0:LK&@$;\\ | q,uh?aX4HAAA "A$AKfͧNq3 .]κfXb Drhs!0*жID\~+xtf0q ?WTSpeB@=ufi3~ti"  ׀l <HJj9?h40d¶@^\p:[g?ti" r_,O|ZX^gsfZMۿÌ0],aA7ZFυN!!B wv0d:PJ9%7(* oDD?aX4Akj<2&F̃qy$ 5 [VLK&@ b?1cr9g//QBnfXb D2o⫗_ҵm˥4=0],aO?{Ź˗cҤI;v,ݻW]܅<(29Tgؾ}0],aXGR 5pk^r%kq\"%%k׮̙3f͚E!hu.nS)bLK&Gnn.MV؄ [oE-iӦ֭[#??y#7 0d{.,b D$@GDD`'7mEEE7o"<}`֬Y "$%%aXl^R|wh޼;`0ní!L>DJJE^|9Rj*4kMt=~)J%j5֮] ?{3FEE!&&+Wtc/]FI EL&LT шx7uԦI^^FcǎSRPP/YYY8}W3^CI&//* k֬QT\.`0tn݊W^yk֬AiiyoJJ ڷo߈@JJ{:jD$!(( ss/]OV׼i5^y|O tׯ9?ߦM9rdypa <5u4hgyhժN'=b4iݺ5`2K/aĈpsoݏW<.@m(>)X%//W^{hh(jnqM7o4#-- S~MR&ՈbA׻Wfu̙rT*$$$HJJG}Yf! HHHT4 &O`]vUUUn+4 @bb"F#"""矻.E`̙8pF(|&@3 ӆ`LBx``0<O$_|<7\f0 Ќ'!C@"`„ XjLCxb}6! 4$>>111X~=fΜ .1 Mc0  Ca`0< Ca`0< Ca`0< Ca`0< Ca`0< Ca`0<db[IENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/bessely.png0000644000175000017500000004631212253362407023061 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxw|S?Iwi)-P[QzыWUAQ+(PL+KYڂ^3MҴxHBWwޯW^瓦<~"DAAAAAARxw7DAAAA>jw7@AAAAaVPPP(A+(((HŠ$b E1hDQ ZAAAA( QVPPP(A+(((HŠ$b E1hDQ ZAAAA( QVPPP(A+(((HŠ$b E1h"iO?Ç) sXf VGw7bbb Z "Btt4*** ,,LnjF#"""$WI-6[nhXf3ja0pyw[ ":KE)**^x2DD4b7̵|nKYڈ7n8w7AaŊ Ν; .g}{nKYI Y _ԐA+!YI Y _ԐA+!YI Y _ԐA+!YI Y _ԐA+!YI Y _Ԑ!$4|`jkڴ:uJ9hY>!K-$HICMM?R;QQ֭0f 0z4кmw^8޷v>}bCajt(ջQ+d|=|a=oTC4I !",;yXxח2A/gm`kб*C;ہqlgF#> 98$&ԉĉ2ÿJgtiN5GR#:8=zZoLjN#6n" & J3Z3LZj 4Wo^J)Hy,{R 7nfd7b]J\\'zaΰo+ԂTD㶏&p/ވh0xL۱4`Nv;v%_~NA8gtȪȪw*J^a."#/o_O_Ta5{Z & uH)IAJI 23Qi{#182; g*3z ЧOÐbsŀLDQ#>|=|=ؿ=x6V5ޛ 60g!RRy:~dbnj""P?Oc>XǬNݚ?؞;N[z10 h7N_S_GZiRJR\y'q$ʫ˯^[%@;mSœgn\j5{lO܎ KP~rXL77@"KDBxyWgVeeߞS vBM~ 땧7K~.A웣[a̰#d\4SGk{(-e}E lhZ"C=;v W>2!ι bڌywQ}ѣ*>T,III޳;v%—[~y `Y"6 grয়0>$IړZX-0)AzOjSc*5w=z?¬BC4 -*UX*UTf뤨hsWO Gؽa.-}oquöڕbbXvܡH"*,dQn.0VTZ!wQg#F?,qNBb~̘oEO7ِ7dgd=!G(븯ՙSC =šVV._ |Q"?" %mh1NĦfkP!qg;V+HAGű /CRXEQ!`O G嚂}جskc06}p0{O @u5YYY/nbK&_5hXu5 Ϟe'{viŊA;ߏ]v-XM_4A?gӭX s|K\6&L{zίYxf; Qc13ǃG#O w0 f:M7aŭ((:=K_´d6;ʎ+Z vCW}K Y`04W/ߢ/QTT]MJb#.\`_ ެg}]뫮;8zec_%8uͻucm#1mZI4,ɌSEED}DQm[4vEGF}h =wh(vjh Ҧs=# - wLWEiUW}QTk@t]DǏ1VEk<A-vT,|CչN7h2ҏ[bA)8$Pr%:U!CP] BOO'z=~l/'@At-?d/{kS^NoтDwMJWIo]YQX-\Hy3D%%^]4ɓ'5ʈ b{%2]w-Ngrr~Ɯ4(7WD_MԦ LDѷ ͧҔSH5_EI5jF#D]^;]G(kQt\?4Ĝ?OԷ/kTǎDzjL5c% At6X7ROO~XݏI0 tye:ѳ1~ʏ:UU}1eGD͂3!Y0RG׏uȇ!)9DoY:n`rȥSl ~(yz2U9a:ڵD#F>(SrKvlŠ[{Gǎ#">Zɵ/ BIZ0}guO[} Az_oQE[o>4bI_OZ"~|<!dDmٗ^bnF ;AzZ_)ۧP\~u?@SFC塂V'NM>uzP|2i[-E_fI]ݮg5?k)R֭[G=]߃>|8mڴvA[n%"s|;7pD|9ra{ N"C=}Uےv9&oLzt#XwL,}-Qhh": ߎ% f/O'XOӡU[ZJ3fߟX//9s(1+#") 0sA֍O}F{kI8)gimL }5%L;bs[hjʉ{C{\e @?^0Mf=CxC'3g믿^1W}edRZ͂dR]M4e eݪ7MٟQ;ҦMN *-eF0Qlɾsܘ8*; hpE7V7psXEN.1^E:wӒ%./C*.>yM>%1<>"*,l6@~e>}t#곬~ϛ>҃Wf3la ܉.;ͫ"#v1TD7}}a>H@CsΡj¡Ctְ^!36t)tf̣[P@4knf\U3JJ-"jD轃m:yK]ՔPΧ9t-8]YUwgs.F^J~wu惆J_JX1hpאKm+q7KJ %Z:UkfLK/NOjuYDee-p^OtZrrXÃ5M?ltB3RS^|6RbKK>Lt"$=1xEO4/_A?iO/E:woAX!aUmoa֭ E_,#5!"/(8>2_+5l%=D;PJ ɶu#^{m3ڛ&3!tQz !Dqql9Ϻh~q @rO콳VS/PiTTȘr2pOLtKx\8UAopS! .]ҠrԧQʞWhQ2 ArG$\?xQ`fef3 l0]TDݓr*r`~hTg0yطۆ=VP_Ўbii ]6"~xUo0_ET*7cDDm~=B `|xo_4O 'rO`ra ~UD߂c16lF^϶=9(mHBoY]]%(Ue>TW9.tñc0zzĤ>0$ 0_$NZdibyy@Ϟ'.]DiZ˨Mceu2BDX{v-^ *۶/=: s5V!4+ xt]>mUUŶTv&Nd;BKl7߰7R<U܇>0@iӀɓَKY0X1lN؄ QTSj}ܔ zc{{٧5"Ro;~/Sq'ɶYm9">]|hox:Qf1$:[ٶvzT?6pw7Zޓxns5WUj:U;>l(z-Yx; |{ۢn8*a}eW_ &Mbf=v9EqЄ~]`܅(X}$&v?4x[Jm-p pp0pS: IDATJkđj^+p+:u{/I`F6%H)IAji*RKٿ32P+xathe .@Զ06rMw7|}joVAM\iS"7vg?Μ c;qop^<أ3H]񯍘gʫ3'=##Gze y_A0 l.s9M&mlvK Qhl" 9سfwᅬϱ b=[n1q|=\#''Q] cըΪo-:caBŠeʔ)ظqC%bǣG+qQYv_?Mr ^[_O?A^ [x^ jy_!,LPHD;b賠۶oC<ˋuNǍcC}8ٯ3+ 0w0ו0Xi+. >m>>,g`"$( a/T3TVd$& Cd\l5sýZ jOyha=ѣM끞a=ѽMww?'"JMΪ(@0 #Y-*x{@G޳'}YŠřpG= hloӧSf(5"&4k\[oq_K }_0fҨ~'Y*~8xuiiǣX`]F׿"!rAAsTW;dHL#X1>+VZ -gO-"G)d\c5Jc%TPU2-μξoC{/E֑뢮͎h-uBY( ~ѣ] r{{GjT588fݺRa))0cGg;vNC]B`k08G[(b;mqdVNOgؔ #Ht:V BCYE $CCY'_?W5LZ inn5~w 6diaMbbv?{v+6 X3-!ݼC0x7O~K&,AwCnFf؃}|hFWhݼx'5di&de1lӆ Av)%%ǜ<0i⧷4DK,Nǹs |Y>!=!aM +oT,t &[PaؽC bj5wp>>1⋻-)A[(X]=>[CD94 _ԐAnT< %:<,+gtΦ98B &$Olg ;+[-y_!s^&L&x@ׅ]Pyn<-AI Y8 Ϝ邆f#;wfSw;tp ";V™39 f͜~/Ùgpi%*Mb'HN/tr9|MEI_MYgF^߻Msij6}RC=B“'J*R†x#s'S|m>Ƭc90Py՜ b:3.>v3S!T 'Kʜ&A7x@ \iNk LmH/KƶGۊʑ :'G*t [n@q &A7S%$/FlX+If4HG;'Po|c͆(bQu !B0ɚ3`>96}RCTHXR:&906>`; ;0fi0m40j'_sCmq-: g}˛6<MFh>!B >σg[O <0P6 4 _Ԑe՟5WO?v-[A.W]ƽߋ c>fTc1U¯&4 _Ԑe0!-4b["!JKJup;z<Ȫo bH L9|MOcth}pmwn(!b朿2^kAI Ytc!A0/Okؙ=v5u &.;hY>!i~饗ЫW/;PHX[ ;>~ة"OI?aɟK_'0?vX6)S7ҡU-_sxOjHڠ-[Q =n($x W:Y̠}|ŋ8XWy ަ.pFiMv&őIO'j ~ 1gswtqD"]}ؓnu(TUt$籐e.A:sjJj>B4-D=xkע_~׆vOz"۷1,t"3?? c挚[eH#* } 6^[^xxOjHڠڐ0#d[[:ϝ'|qC޸:G]Ҍ]QxxOjҠ ϞeyX;p-Fo| ս_PK r;]wq\Rhu\R砉gm$&Kݐ`ׯKg۶l:O>>X0vCKAfBӝ>9hY>!tݐUض<X  ~:;t '*Q>|qzG _ԐA -gFplLqZ,?*tu\  g}ZZ .>hv1!Kl@Ftt ۸;Ygx 2j gm, fexMؽgQm);S Eנݔv=YI Y%$dnc':t])Pm9,\pA@x{ó>.5LY99"y-!".+cC>"#oC/l. ǎ4c=׆())N\NSۊmi|f A xOj|rرeee?-[rް0GF g[_`4t_)џ=YI WX___t,DDd2Gx[±cY0P!x8U/%Bf^ ﳵxdz6}Re=c |HIIANN:ٳg̙3N[Y :يJ}:}ϢXQRgmJ111Up7⭷B6m>wppp av`@+\ ݬ3tw)4׀ g}x >S됸Q`!ai)wLH`Æ9t<ݥ  ڧ闝 g}}NlA,jpg̝N #dw >joYI Yf`X׼hX-̕f䔺gm, :88e=d@RêXe-yރ _ԐAm)o8P68} AAԡ;6LrhrrJ]xbxdz6}RCm U* )s O坂d1P;^*V@ipiц=YI Y%$ 4Mt-@X17'+!G֡}xOjҠZt:;90!ܺ`卖֟ _ԐAF F%S'`18D+g g}!tvZXHNN>V^LHXSC+/$¿.!]l=Oì3#`P]g g}!t׮]{zɓ'XjVXQ5,YA/81!Kkn$AGFu++wxoz󵠍? bxdz6}RCm2{;,+UU?[g}4&CBπ5#` :1!Kn2$tb5qC>A xOjҠ 1 x=9Z7hރ _ԐA7:8IPk@AU[}Zʵgm, ɐwh933868jރ _ԐA7ZGDuNkىPP^ ^r{ó>4&CBAuN1FpTULd{ó>4&CB Z1Ж{ó>4&Cbo_FC^ן _ԐA7txݳŚ8߃=YI Yt!aE!<^u1$8oм1!Kn4$t\j(E]CBegϻ.d=T^*tqA xOjҠ -ݶ]cwmi-j¯TM?YI Yt!#8l@gm, ѐЍcIA xOjҠ ٽ@1!K;$cXC _ԐA7:X6Y=YI Y!@ʳ/ǖ%p϶ tu=YI Yt!J jZ:TK|y?YI Yt!ŠZ= TIDAT -OZ}sOq g}!Kn0$8Z][\ ({bxdz6}RC`Hg g}!x9Ԗl6C!vA xOjҠ (qke wx=hރ _ԐAѢ%ރ _pw/2T*Z-֬Yoo6:`RE Ou=xOjHフ+WbŊhӦ =`H@:II*b"bxdz6}R=茌 ̜9ޱ)S`裏P\\ݻ{\PI*jc!cCgm{]vݻ&O~aÆ 7n[9ؼy3v܉m۶@kdбxP,4s#!5/ԎTܦ;vX\\dڢhw}'u,>>;wŋQQQ)"!uIOOҥK!JKKl2Z6m֭[WIw $&-w6>=)m ΩƉ)TӠ8[nXti:[v g}4& hy5h,(1!Kntр@rIE"x=c.,\Ճ}xOjҠIH ڎ@uňJr2hgkgm, 0욤Rj(̒E#xOjҠ 4I:*O<#<>GCgm, -T -wgoԎH g}!Kn4$nٴJ#A fuf7 _ԐA;:kЮgm, ِiv g}!Kv6$z g}!Kv{HA xOjҠ k= g}4CB;W+qj g}!K:?4jv g}*": `R-[`޽ Ν;rJL0x~գ[NTTTp.F֭ڵիWH [-{hӦ :-oӡС0Eii)6m "<<\}eee`FuO[\k%|_MM Twǫ /33Z}zTTT =n0 sq*++zefϞ,_ٱcc43g2>,3k, g ȉaKtNӦM\R#@,[+W?FN:yiA,TA; ZP__n 6P#'@4NqR:aNn"B0]÷mAOo4Lt]h(丛 u &Ovt7 )pB4NqR)Еͱc%bjd<l ++8F^'~8) 萈&=ݽDXlڄyؽ݌ 58IYx@2 }OKv_}*9z)pB4NqR)-[7jM> qE~ 58IYx@5iBߨc^;0` /8uʽD4NqR)Е@&A;xy!+(~ڪZ58IYx@m;?=ˁl > =JɻwmZx5@yiA,@Ϟ4FPSƉ4N#?O.]&M8X^>np̞ 7qHTL- pAyNf;G;v~=^^nƉ4N#8pjj˗֭`ˠ}øه O5k'6K[f@18IYx@CA{HF#Ͱ *3<9{wE[p/7C'e]WWҴBvNh{¤,=Jہu@_'O^nƉ4N#9,0 *??/ OX0r?;yIAƉ4N#:$$ 3i6- |'3<'CVhA,cl4ZAƉI*x@Biaqp11ulB N|GЬ@_")(r^ÆkҪ멩si@j$/p@[pzKr|['@+ {Qi\I+$'~P#'m FH9#hΠmT]M6P$XH|9]t|,"C$'~P#'m B#-L8 7 SNmlG#hCSWÔl\ʠIj$kGxh RsʠIj$eee uwrѢ l'&j 4N#3$Tڃn謬,0B@M,d u 6'5_'em Fv¶ g2в:@!ЄoTC18) Bk1*%ʤ=NazZZPGr*)w18) p+qDDsI/^\ NRa!cP(KsMR64a `0=&O<u5u5oTcqR)u)C@hC ŎYȐ S07N= `f`*O{zIbo~i[`0`ʔ)={6,X`ڟnݺ駟ҥKm ]#,!ՆBg`/j0:.@ر)v -7ѣ}:E O8 'eV޸q#Əe˖tu:BBBPQQv(3XrŴh!ڥRCP,́nshI,ӧ))a4~1k-``N*IYUѺA8bccqa%X:t}/_n3b f=nC!NgS!!@@,#hCI?[p6\Z+Z,<8y:p$ԣGM~N*IYU۴ĭaEffIYxyy!$$7† uVlٲG*Ch)I:#cj{:{/-4#:iiHjh;s&5~pZJ,^ t ,\K9Ih!\T( 7HfqW) :\URP[ \n )@𤋮ɒD9t j I*x@sӠysExj.w}C 4 __Z{x,z$"]}EΟy^ bciӧ,** >]9WȩTpkQpe!8xyYYEUE(*Db[y_5u5vMM0 i[{>-Hʬzsevb($,R%B{K%΃&U끓'ELN~!c@k/*?Ov-$ԑXYN[Ǜ= *&~~ƍ Ӈu%{JKVԂT! &.)S?q:t:[6eG %x&[K//ы ya4T(¸Iĉ4 ;xֶ{9xqz.ׯJm&y=`iC4".y]+^Xz=q{wG*wK *JP^SBRF<'rrp͚4C\hp6 M'FpS)ӧ$KrV)7K0e ~< ݊ B¼<`wJ8?}xyN/} $C~!52z 鷩QMKwlkNG$ԑX VhIMeVILLDϞm_5~M_X}n$e$X13 FABD:w6m;GtF|X<질TFx@svbN%X&̞M#Ӄ%?!a`` ck yM*L{ѣq9!$ ه#58qtIT t^ v-[ήk `?' ȑtQ?`ӷR;cz%X OFvYiˠn$F>}нuwE$y?YamΜIoÇI] CZHf͢_K }Ocm'OtNBwB65qg~1 p2tOga>xv@uWGP[_Mg7Dxmdecth|q87r_ź0,VJm&`p>; w_/ OΜ4d,AҙMA$0J$l)A;~r:v9$,.H$.\H@}=0n\G>u5 n*\s#?=/mɛ'c] a qeoDvq֞9Ihp.С.Ȳ'In(A-ș !6:(  $AC^L55`SM.Uz<\%W'N{"",=Ȑu:tt,Y@~ϕ=賢ml4ۧlGL5-ąOz8kOG uHjCiDA-ZНi^ih12#UWNSR6zԨ@n FVa0G&"86u>n"Z5}f4N^;ÿ➎ۋ1'5@G yHC}]>ᶓU&Oo%%?۹7q&!0$@7naϖwE[g.3$2`'r;:=(g~UzWćcӤM842'wCG yH~ux /X[K,;!TчG\!ysfUҭ>*čO6sX4.: /@. FٟWF`:}%I/^{7 w6<}t}[v;FNR#<$dGkC zo'!Tܷo֚ӑKղaХC w۶5H-Qty3 }xy%;<94~ɜͰu 6o&i\< Y50\ u$T[G'^`<`ʽ00%xݟK'95r )ЅxvfpխV!"$d-!q @h(la9s6)ЀӥPGBfuHrY^4R㗹q萔sRj$۞cf0;p :}DMDpRj$tH  9 8AԻahQ`͋d4mJ> i%"] u$hח>q7TI uÒ |8-k~<#bCbӃ?^n"; JrRj$bʟDS6oDTrx{SCUH$'OEqهt:,`ոUOB>駁9sȩTHϧe^>YOhYgg  Fh'٠";BfBvTl!3ǡo_'.,ТCIfښ0xz(*3v#xk |j I*x@WTЭǃNBn(NZA]{AAZt#@Ի78H}NϬϩ?#.4~Wgb"YݺX i@N t!)OC|tBvQj@y|T!P'A˾BC ڌ޼IhC?W sN5yoƩamc!%5r )@8wͣĎK'm#-sN{ڂAGDOA7mJk!0P 8qr!C(s8 lFsQyx#F:89EhaTѣ0Nj I*x@סG;9hַmqPPvlB>HDt+^ j I*x@WA7kF}/_K8Nzޫ 樮逑&NΐBNGπ*-*ԑ Թsg* p5XN}0W,^} ܹA95r *!h9xMiq$$$87^ RB3y-"b%ТB x[ˉŸ"۶?pYJYW$=9?&~%>i7ȩTpY322j*,]%Y9 ѡ4/Z@H贂w tT3Mh=Y'*ԑ@6=s4ˁo*pBY45h> Q?30@X؜Ν HHH^ǴipV&mLZyzЉ+8#h:wNjV#h> Q? Hp'Q4IPT#@,fHwwҰRlaodH\ZN+2HNNĬY۲%<,^L֑t?9$ti=w}fwio00sL4k aaaXx1h4⩧b zR@$N, NDӦdI4@~XJ T^աϟFDGu~ {a8!@ Xxy\ lLmΎupؑҖ>2Wpor$ЁXf mxC~gxx_7b) eaRNUU49zY:XGFRXeX@OY9y엗<r%3Ϡ ~c1a8Wm۶F֭ڵիW- qд@ds\JA<޴NŎz &T!"(ڕ._r29#8t48|&}7)\n>> &"@"e1Nktٸ1}f&p}q5D>" A)FX|+xjz Fӗ7OgӸg8yP1/_F~~>Fr{ЦMdff233Ml?##mFjϟdž uVlٲJJh(PRҸAl+=z8||aa}}:97AA@a}:~iŋ0VzkG~N8s<}'O1hP]Ç &n|@z:Rbco.ס]+22`v3ߗuik4eg(tVC,p`sB/vk_jka 8a%Z#"h(,PFcZ3XhfӾ7lprӣ$Ϊ!CAL 'Rc0`<֭#}{TsdK# 6`ʕ8qT|x!ڃTͳ.Z@-tt4wg4eV[Am +랟ˋFLyy$@|VvCN_Tբ@PVFk˖'I6m[I'},=-)òyII,rsi4AT VnN2¥tpp0^xƚÐ~⨩m똖  4sB+?'{?M v99d o/XiQv1V>d()S-='!?7ɂ@N k]JڍBB@kGO=u*1lcߤ]f0}h6Y~0bAa˖?ſe3 q吏?oROUKΖTUBzR^~Z-ZDE9Ih!hY]M6A9bB@HhC "Bi IN@K4SպuN~"RGGQ4ڳŋƚ?yb}5rj$4"MIqZEm>}04; a7nI6`1S#';$1ޡ@;A *FU`xg}E] P лwӖ5$tQ_}ƳQ^[I]'!,r [UQ<0u*of͢OI~1S#'-UHЌƵ,ă>u*T?N}hzÅгgyr&ް)vіO,Au" E8;G:׭޽TFdz!95r )RE._zЉa *FN}h YȍpA;S~8}-X2M!!Rχ<|> i;D~NkЛycNI@NG !!pbqA(8hޜj^Ow:̎+%ʁ-@@GEmT<{?h۰ȏS4͛Gh+VP 3白gr|SUcm"j,95r )RJ'#hsTA!!TfgL9QT׮₏ԯ]rԩ%vx |Z)"=OL몱zⱞ%]Z I'"5rj$L[ 4W" C &~؅H:1M7 rn 1D6-gӖ->yJYRR Pc FNR#iHȳ'4]Goz. *l"iUkN,\e.D : 6dCL 5+.s%k5.$Gzpj$Hjodśoҭ֊܇8_(SF}2-YB;hHIfTHvj=N@h-ТBB@ڦÎ@s7:uF.Bza-ͨЀ$+""Lu}9ęi6]Lv'Ow%$9Ih!!zPF-Z4P@`_Ú^Ow*Q^FЉhЁ||[hTE t!x!܃mJ0ti8ݼ "u+)>II@"RB9Ih!!@1 >(dA:D1|x)l!_`9'I %fyhI"ػܩѣ*(4t8m d9]Joď>O_>vzsj I*x@; #ɦ3 6q!!x8;YsR@ Ʀ&C'Ъ-zQy΀t7vČprI*x@ @}"rsQ (42IHXWGw~~.t!!Ǖ"y.dbIN#6 /Y&4~ӛ~<: f1S#'-($)ЀmgaUe0TB ߻gOn$$adhl*V)׊ =m|h};m֜ko]o|`lj^@v6t CL )gΤ)-[T/O=%Ym1S#'-GHPҼ9Mmq-8"12{4ϧV+YYYHKԉ2T]6Vv+W趝WQVI{brMG < }Zӈh`NȺs)Dʢ%#7ȩTq71#$2i8`j | #ks *i]UE)%OŲkp,|:U]YBB><(_}a8Iy7nP9Q⯿U3J(6Br81S#'#hBBd+#kԩ!*[9UY?N6{]a~H hrdol@ɓQ$5퍉]'XhG \!!@ep0Po~=mM=NTP!pXs6mqA ̤HV&v*' #V n$'e-WH!LCӧgOD-PE IDATwDӻ!RՃv/TYs l7xBIYx@ 6ǂ@QT3׿T`qtO/|Cdm}ڶ03 FwE\X{CNN@,u8r !! H[H,%%' Q\/ u~?A]\Laixk%rMG",Cc 7 4N#WH mJx3 ũS@˗۷  @օT{/$c!!I3uFftk :/H{B00g>^BA鐐}IW NoFয়D7@鐐E:eDۀ_f̠_/MDȃ^?$#i+#hb!m :8Z\IYx@ mQ#(0ͻH\DUV](9] >doԶq, .}~i\koL&.TcqRܙmg=B.#$`GdZ{ؙ 'ݾ=C]P[c{P^5GI,,7Drp'e#hA!! " Zqh4NCW$<1vk|Ao<G7J+E*9'Wˮ7$4N#ZpH!*h֌J,\Ar0(iC[rW*> I+,~iG !CTP(HpOedshNBѮm32xWCݍx}6GAAc-{>tR^+a&QS$4N#Z :hޜڰUU ?Nh9 v5FjwInK7zvI ղv0b."'qRZHBB2(b]$". ڡx'ߟ?OcÇSmZ70 4N#Gp-(t2V$< %/==Mcŋ:gЯדs4;(H"8 0{oIJhG ?&$\MD Ot:EWTvd:/q]KYUk, )9I HDŽk Y|XDa+;ZkWzm$RToPcqR)вq)$d_S(./ZѓS̡K,sx$|f5'P f<(.6ؗ ॖUAڣJkeno\سgcCB@xP26mJ"4 ay瘱mt^|?{L:MƆ6`ɠ%yaiؖaC)z`K,踸8ܹGAff&Mbm-[b_yy96l؀[b˖-T')I0A}YKHH<} v0QQv +ڶ**o⹝[>`s1sF7P/s; N,ѵb\&v(m*z cjb=rlݺ}J$79ɸ g̙L:1 ӧ 2̴iӘ9s0=SSScqرc3 0w{,0̉'=a||eKa]B< ZԜ`~K)/`bIsR99Arv27ͭ2RtNӦM\R!{lStv-CB<7" (@jA*пUyMkq;xP4t\s^B`iB* ׊aSyT,~iG !Hv)iِ{o Ō30n0Hy O*k+͟K"qs^-Za"bKxqdf6u֪$'4Nnhap 2PZS>}0!a|*!D KD6_PS_CᯣOč5u+^E|mq`eȾYIg<3ּf{Ƿ9Vwx21ω/7uΐ8) AIXka}; }Sn_vÞ+ |"ڥL1155xwE&q-cԝǧG>ŗ{ +M?>t$me|_t`Q \+aL-sIYx@ *0l0 B靟b̈́5}H/Iǘucp0 t@.11xnBu4|Zs-N84#RsǗF F}oZ5I9Au]5;Hu}v6԰iZq*Rk/yw%̛>L i;mBg*I hG oAn7uz%7.A9e9Xzt)|!f xz;זcq⋕'V""LD ܳ'mOrx6LDؓsKM'̏oyX:~iG 4ߐp_>?u}=ͦŕܝsQ[_˛}oCLVk6@fH|{[=! '>ƻiуNF}^x֗4BWBUn[6Y('qR)|B:c,C}*'#`yvGZa]ʛР" RV!?6r<7thL<6`aB9w'Cny.&$L@=UlNiv`J`ZihѴۂ$'F Ȧܭ^4N#OH/q KįAX|*||7WC0!,;/ġ\u:}+C_|ˋ| X˴@*!ZƔS@IYx@; oTkI[ ІչMnBdl9Ƴi/V}}0h=# 9;ur~ }1>a*8) Hv.س5x3`a6pᰅǪU8{SN| anTxcSY_G1Tt ?DRz`N|0{~yOCxz;ۑG-c:ANy"3_V5¯ =_NJC,>A3qI l=ֿkqϘj/դW0ɑ#؛;9^iFˀRe,LX8Xꗴ_P]W ]̺8) h ,:|~ҙjBY3qn/75f炳I3޽]ht4m40щ8{^UI|x{y;~2C9pBBHMڴ*9]q$fŝ\+% @IYx@s} <g y(-i7y5uӱˏ/y`xO/gQN3_Kz OH3|ylM݊<8z=Y c`E; .DFමd!;qR)!>O/}PP%a>v#0X_q(()߆:,m}ZQvE8YI垎``8uGغ`ѣ.Tr$&&7X<4hM6!G8 6ی:c&u_o_Y8hG yHXQ[< #c;@dKxQRm{GAG?B!|oNZ5.ܵWK? ?OyANF8p~QKm@@J:cHW,hZwf @IYx@x /`lxH ݢaF(*`{AE^y>>1-a`l,ĊƲFwAQgQ"B %H?*i ~`MmTkbBMbMXD$5uHc jcuAX$iE@!x~c=ݽ=YvX<<>O#۬WMXfDE˫9ܹOt옼%4ԪK:99h䮣B;`ވy`p !!T&T˧~iɦ 3욾 /7/J͜ze~o^g3-[7l褎Zug x2LsXrI,Loiڣk9ӌ nޔ)1'鵌 kRD/HZ^lY_O@]v[do8d>'ќmMu| u0;(+!'bORo?ǖ7/wwy]{9Vs<آ˅Ǿ[oqӶON= * H,Ls7dmƻ䷿= ;[N\ !0gK?" Lnhd^7wܮwO#8df&JTW˩NfPAƕxlX8z!^=GD냛=,CG x8&)f>;V׷W_۷!&F9߾͉pߌ ]±օ~Squqe}hdHtJr0f͚eX!I IYYm}$9;Kj7ʞmϽ|ݪ2$R/m R%R$+oKm$ IW/ݿHA(@->g]6(-6!-It{)Wz4+T<||nߧެ;g{ܓVRFU}1a1KQK4O8}֭$?19K]z$|$I"X6~qЌ_I_2@[2 t{)sYE֬PQwO^biQ!=008~ᜪ9f3qDRR-&auy΅4psz걧0 z͝U/> }Z8/]2 ]$lrh Kd[@s߻?Ga}ޡL{r TkK #(,"hOCorͿ//H^CY"8Xވ:@Gs#"NNNNLf]t ΝyYA d|zSMU"\>A^A-F,~ '}qLqw'G_v ZL<KҚ>,#"w\]i=Z$I~H}6W.~ʶNZQ<͛rh5-T[p:˻xsy aﹽ 7_ ӯhgNFC8ChEEB8a.2XI=4-TL(8_ 'Oc!Zzփ$MPN -2M׮uaO8B(*BJj9݉дP1a|,sSZ*oSj{Q'Z{M JTPgkϒU:@ٵ2F=>Ƀˆp Њд~yM9/$T~QPˍ ׭Hoh!Z{999 \\H)JRC;ϰ`q~̟duFs '}qH0t|l>m!CT9i^09!3S~77F+>'bIDAT; 4i=oԕ;wsw%4Ӛ)ňp Њh<+慊3PO Vm?2X_9:9Ҝ/Zc}7m#~~I_2@+.C^{̓Uh aa/%KruqeK; ϶Va'cѾEI͓e_fuaO8ChEB'')M_ &H7Sa ' 7ܻ|0Zv4}c"zG4+͋wR '}qH-$?A)OP }t9#ZG}(65SA^kN!E 7lXf̘ x@uF̛L0fս-:Ν;]K+N!EH~~~[4 hU9iwFtcz 'x멯ux;$I)@:3,_[2j(x $IbǎNyy9uuudffcE@ v 2| )..D\\xyyb ͞=\BFFvͥ={du|4$--Çiի,^ݻc2ذaݺukm6 f0ɓ'ϲh"{hC-39۷3uTrrrkӔ/[nf8v\԰qF\]]qvvCA?[PXXȀ___rssɡo߾Hdz~zrss9xuj*Mfo aȍ*]\tA.\Lbl5kϟ'//]9EDDFff&[ne֭vu9s&'N ==M6ҕWLL 3gɓ4440Լ뾶\XٗVѷo_?1dOee%Gn_YYeee\>\^{5]|:r̙3ܿdcx9{,Lh/}6:^uWӽ{w](((d2w^jjjx 8TuQ1((W^yOOOzom77Ĝ9s󣺺Kb=HJJ/ƍdgg& 0i$舊 É`…pd̖-[XlQQQl-(**ѣ$&&[Es*@ U$'D"d߾}$''sEOnYh-x(1bccV`K "@ Z._L޽1LV!weȑlܸ & N  x"@ -Eh@ 0("@ AZ  @`PD"@ -C]IIENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/hi_c.png0000644000175000017500000013437612253362407022325 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATx˯JVY+=wվ++|?wfmf=awOO{{G޾}>f=YAO?y~g~9dzgϾۇ4lg6z__җ?|KEU(P([G$ـn$/YѱaC{q${w,r/+K;BpNՉaG~}չ;t)N,]_sW K f+U3Ys! KRZ JrP +~*@$ۛFAĢ(ņe`P H+ā80w7oA>&_l3'_"[__//|3sg~*|%q>Fyɲ:|+~}-OхW*`Y˧o_}7\LYLo\q5h%L&I5Р@1K`\k8>GY? ߿k?R,wek ٲܰ7, %S-,= `,W x^"KeV돱d/)8RRIAMA# TVX.Ʋ%VJ `6` ټ̀OOTW~?u:LƶK,Ã"/'_47&dX:g';Js%9]>قhW b@-\-vúDKeT`1VޠcoA"7wHA~ 53U> ;U(GJiebjTjJD T ZOOmvu'pN-H=$fp~"CF`= +k7Wq`@e1w?74F,Xj,5|Y o|i{yU $;[~[ze8 JD41 4Zh3` 0$Apl椌YKp9:8^>ӲM_~#~H;fWAxam"#DRj ҀiДHk1򭂝-MA]TtX*Jה1o# bn<i:BvO!RҰ0,Jejp{@Wnhi吀4EQ8 h3hg|'{Hc-lNNW;dD&<ر)>;iP2RWᯅG% hRI+#G b,O) ?.G XIJ|ʮ%b߀}`olf 7m6P2pT ŬV Ce";5S?QMn bf@?F8?H589Lr nJMM@!@D||gճqAn ;w$VG#N |G XAQ K VzeǭeZvb[#YY+ C:h[filAa R+eQ*5J-Q+hp49Owytq.̀~ # KYE^P<,Sw$zA_$GoodskL=$D-qZ"%bW\! deu\Z6Z귖Ѳu[ [+Y悗^Q bn!#684jE*>R)TZܻ;d-zP f@?>)\c1}0E|p"l )up7vI⹉VnF@DzQ0%)^"K*"B:~hCag8p>|ZacKs3sAif@قف9xaJHaPt!RiЊXhp8 ̀~ gU5gTsoDi\[d$t?tU<{f)]J3ۑl^^X[n85۬UX"u\gyfṅpXCg[wVxa.2k s1Һ6Gvi+84@ BPkPx`/S*PQZUZtD7<"O9siʢMƘ98)p kBVցqH PynEKye0 6o[;+YsaVXs5݃%m*D*p5Rհq :ZBRP@W=En3bc*>8g0>Y//;8!ezac.0J4މ=RYS\q:zcѨ54ҔB۶@#BP87 {RiKk[b2cHyFho*(fZf5G04~@jp *Ҩ4e N3Cb sǹ~%S-1Bn{OuOʙճM럭p|SnϪ;+ GԵgH #~)k}ҍxzT+cg쵡1 l AlOyn%o Vh6pi 飑NU rKV 31FL Z4дP;&F+T},Nv]l6l ]ȾbTn# ӵP*J {ċ(NG;ckj3{ǼFwX6g ̍ !wB ԨOC\,1h堰6J1MiIjZɄԂz6)Bd 48?,)+ Ŝ$ofry}gc?|j,J#7\sݺ.A!B ,Χ܎J: ܲG*QvFv|]^b0Cz-;0{9HTAg!,UXa!BKLlصl|㓮p~:XK׽y#\f@?2HK2? nR~hFʲrI%cUzS.{mb;SUsSS98S%!5UUǢ+.!ݷ,q7skjfG}-u[.ga7;   BcRX !,TXňC44WIBKsЄ-騬M2?0i){zIqv_ЩiO\yZ֙8u>))T9|ZG qкNO:M]ϋ!'*gE*B`р- [v+(/} V ~k 7rǎJ }#ԺŔϰ\bN!oyl}}v*(V *Ԩ# 7<==O#8qypQE͈M_'ufz rr}5ϟ<9}=pSHcjZ9uX܀`#4į %tzJ(8&#܈˭niٲ(===mQS\`&i$ٟF{X7"}B`fQE)ƞ̀~ G;1Hui ٺ1}GKM}r_']>bjAZ|OaMՁiuEhZhP} λ8,@ւ\^:f J-܇m ܨV)7np'ʆl*KxFQ%Z\,уW]4]t yn[[ay=~99. ؈*~#3'H^sw ZO1OAnl#ϔnf SnByl!v*F[J@[ǡqpgcu`Wt z mƻDʽPTp ^vV o1bp'-–ȃZAdÀƮ1v%^Ǡ+~k}8υmSZ}J@ڈ~%{k3`S>Q]e?6*4/7qv?Q)G<3NɩH)P~4SGnXDqbp#NZ쿽e ڶ5GKA\GHn@t zHHv;iu5렪jV>,ZJc @ w.NÝ[.ي'4h7ȬSH9G}duzPJ;/iބΪ 7,lZazvG!e}L/1}A*S<[HKb-II+ |^ZR1u{١sans&ihFЍ"Ұxfm!m؋= 4c2=,QDm, 1Q8ǔ<|!ڤ'l;P{X;ܬ̀0}4G']0N1hl'OP#yj~hDA vWUrrOi9u,9wyt\W@[ ZXKѾDkpUKY>F&L `[c[[E|(rkЗP^׆ *:@:-Qsƻ>P0^eDuG.Jti% y`>e6#ɿu=a׾5>Oŋaa'M{8< ,{sϲt~x%:ps\mCKp %%3pxS*T/X+ b1Ru K l0b[-f{ׁZ|w~޾gŵr2ăz+^"# N% *Ch,P 3HH!]˞ݬ*C5=|ɟK_K???}퐻92d Tn.w Ilbj've$od;2W?FKӔ 8 .|s+D.\k߶eCZ+/ F7n0[[tA6ؙl ;vO׆ŕje؊Hhe/Q~Wiiц!_ =%]XGtx3X]=̀d+޼yr|+/Tq)n5rh竌ѺwL]&˙~*:Ͻ} ;GAL].%V]/ya|XPPb)9 o1-i^:V=P\.WJl*1adF@# ZT"XXNN颢u}_onNf@O//뚟`iH3FpJ'TdJXwL^fsQmfb#/.,# ݍ/N ߺj>,\)0oZ W8 xP*f ބomA ۠wŅPjb68 RRAqd4y`Q}q >>ˏ"{}Z?//)uU$8A~Gv+#c ͏c3**:e`݉C9 tQ ?i<mw,k)43Jp'Jicِ[T߻6*:F ]m#%pc|(*`@c-h(Ʒvbib1T|\S MH^E]lVҕ b,WJ19*QRgtQ6AnL(SPEDir?m{n3bρ{l]Жt1)hy91cHVX*Bim( f{VCX<}' _a>@:YtPNNO S}s(.#y'ЖrWNv^<3 gcpv^n8<|9niԄ:@02ؽfCkQ1+ b`-b ߶Gۥbة1+ssfa{m2VGp'2H_pdTR1 ea CoCO=_[ٱy,%=g G0>6I9F )i^cv(9s_(A)'?Sq ݀ 3 |C+]VIp~H*߲cg dii w ^她ʬ).(0݅нߧc7w񴢪·MY(TBh:4FP. V[Gf@?\2=Cg$5}(wN(*)l:%>4gu|#a%`pZz,}pj7`Vr\Zdaaaa!֖Go-UÈBcc1<7W\\٢}r QW] "3gI] %ZR` Yu֭$@ȎsȕQAܕ}?mS,ua~?[cՑ=$y'˩ JUrQ80![+uh"O64r5bJ,{v /"+ . \Xo[޼1z4 IDAT53C}ό\6k s@tO1(3T+hk5UA-ԕYBBFpAa'MiЧ6)0Ľzi~nc*{Jy1f]1o ̱ezIByJ |Sw쒆Wp:hM{`)%-,[²DVr!-W \xf8oo*AFXV˥u#j@E?B4 plߙs7dT)RPׂXE&?'g<8H2OIك|])Uvv,<IƉ_{;N@'yݿ6;yLESuvq$6"M^4&c` Xl-rg6ȵE(F[aw%G0BskKc4kVj1*:*Hu/`ڒF+vCqrIFny0̏5Dyr9#9AtL.>౓n{ϧqsߴ jDJh#uhP(&Zy AR䲄gZE+MnˆF9VaSPB=-P)&(iA[jBv܈v?dJ8 @t(~HM!K182+f@?\?ad$\Tn&=Y4kN0xpvF0OaΏ8U)?_{'Ǖl츀n8 * J:01q ]0.\ZxV s,/--lhTzu[{{hUXiZX1Z|/"]!q?8 m7CˈcOqd+ycNO؏t\#H4\78JnRR ilMP ELOmY}^Uy&=t+{aRR m1ڠ5C;wvPڇ}'OONCTz΁5|C6c1㔺΁SC|r sKIs|ӟ3ع sPg; KZ WQHh'fӖnLCkj05bajP=b sU$T°Wڃ=!š#(^a( ~h燥/jiſDu]|U/g6o3k>),;sXXHZN:c?9W'KQh;WeHʲD^j0W7%BZUii`c촡6TTDw tyX`wq]o6)1H(8TSVB=Qa%M@B-Z q#ckt;2p yys'lymS)^"Ɣul&?sY c}~>g IN7s}ϟOd&v`atdIB:Z4醣76ş#,{*9z\k# ffU9xkh}kZ%5UmFBB)GCkrקpBb{x%Q#c3f@?>%͋~Ycrs\)eX΀ .J.dtqXQ:pAY[JǡFap>)_N8̖VTF#z%a]\c+b^U>t>`Q$\Ei]! )Z:'Cyw7 fK.DÌ=gmc-є#9EO%0Ur$\Vgyk._1Pw*zD5kDLIKk7uB,d 1С_.SwwlՏzPܱƏ`b0r c@Cc-^bKbY{PMTGJ|s㧴B!jx"2!eP_,GAH/;A{J=OϹH޽pS0S8|w@E@9^wSʰYKͦø_`C# P 6>Kt]py:ˣ4u7mQHx0o J>l ovj3`~ᑌnHLs\N9{NԴvdY$O @˜{#¹O:ZΈqE (jB]ptiƈ-V`/6 kGA;6;9 n Tݐ~k@ S,e6=ws7: Π:V.o4Dtzx%=Y|f@?2e9C'Oʓzx ,F9xs%g8W;r!a\s^.Nppuا)U,@ +ߘ\ E+jz56ܡlPN=5GyPK~ט֨PB-KhmnkZJOk:?U%v(cɮ;=}0h-.|H7Џ 1/(,c |t9[c/3at*:똋dYgN!eyrats##~l[p-ֱj͍ w7{e*YK k(j+B.;QDWwdx,h8jTzP:X%jhQmm$:o4F 8 0gK/ŷ쮭Bl"}7U%@ztgk~gj_0ǬSr^es|`| h8O=@Ψ%T3#2 ǔs ecFp1I}q^)[4hn r(%h)^=@.@/Cᶂ Fx~+;c;G ƀ W#*+A>ݝScpQ8g1p7yjAKp>.jZ|:nH[jqypς6tTx8[AJ{c ]{wn)3;(v {R.Vʳ«M&C~mGEMGaf#1i*ѯDY,-JX3HWCc Ʒ6=e.-HowoHr-g86zׯ_|f?WS yQe<RcpN7ր'uq$+&p"1(ѳezԢ"99 ?ةW5ԵK#Դۺew785E}s;"Q;WQA_ށ^dȝA̵^+u]*^hQ*JHM[`iJDB"" swdj: Nvtn8kDCxf@O__n?s___ӟ4oU z^z&0GꟵTiJz*/onJ{[Ns0ȏO|VQOdfhQ !~ 3818#n;ƲoK}ɏ+ Uh[6{9#zJR<4"w>\+J).J.C#~2QZ*¯5D!@~D()1mcnj̭U$reG+CqiZKzcKiT֢amGCWBE*S҅44>!jS'-]z_=@z 3?Y~G?Fi3}s|s;Hf&Tdgd AY~b}KA=8)0Ozw'?aмZ ̱[@5W `7K?mJIJ`}ʾӼFA ݢ-Cyg0 lh_)r\(pQz(hIqP>yPKh8<}et^Mc@ #X{IoH^mqyan3kbp>`>a4=1d:S%''%dX4O&ax%[xL_aY'try"G+jM#2 IiɈ% 8 ,C6ǵ^A{3WʲCH=c/t FB@0>1:56zkʠk,Ri WҩJ}_`n :g^ ՙ5O1(܈'e2'1(j93Ѻ bj.l?t+H%N.=_+[GeA ֐G\MpulݬAdt=Ne/VJTJ}~^ "Xvaߖ 74/^:XC:UwT{zNL.˟ogR%-zT-P6|qBAw0NTr禠W;ZG;'Cs. Cn!_q%O-`iZ6]e`)(Q͉aq+Lr1.\!= wj!k`'kEv +A,EHOwyht\x/K k-*I4_- sal̀~9ɦsb\~^Ƙ%%;Q!\"oh -B3FyD(דTtDW'퓏mU@34KQh'wа5B/tHJLΜvGh0>tC!Q~W?]W B0,hF%- %B 4|wz%-͑J%WG])ɓ:Lf@?Şh}=ɵwUaN$=9MP9]:ok:j}@ ^hˠXHۇZ C5W%&] _E}O;sFTȱ 9 rP8*{( B00xG8GXU={IT`'i~0ln2OT WvMg-нu; yLE9=Xx X;$n1oOP"m{J@k9:˛;i n+1X*g\kTWwGRE*hk6>$L0B ^MYW W VB0 Ym8K.4=o.dשgI@1H;~;3;XK}c9m>@M?J='#xl$*p>4JVwF0G3YYfχ 0yp`]•En zQ,o,m%BWGp\Kl}RiDEk5qpnj u ?vU4cXaOѴp8I[xx"!Az03 f@? %IgGYNaXZjky{c+۩.#A 4KdkT'*h@ kU{5[<]\ x8WChu_:^B/E׈dl3fSM 8'yµ1o4"j#;p-HR\$ӱ`-iWU ˹FqS0w01-]D8X) mi}kc]GGG r~4(j5+]csuǠ+pla8nH'vW}4=dgO|w ~z?b= [z΀=Y[fRm8rXyCuqݑ9ߥ<-1~SF\չ~&ЏW'VGCC9MAz{ T9( J "KRc+#^Itڪ2zZtөg](IdP}Axuti#FEt"8_h6srmSmJ='y FuMӥW]>Js8K衽ˋJ7`O|L#Mjy+`$=R.{8@Ki`aߥR(wwxC .0·#|_tXVj)aF=u>GK7D9Gl{2T׃`S 6짩C?L5ICY֊^nlAvp؁1U*!9q{pJ$rb)R-F}Gf%-PIl,ON7ؠ$]oE}0جs%ne|O{y#rT簞CKR6u|(d{>QcgTiEu pnA䷤Xt_4zw=h Z) eIs'Zt rմRּOB) IDATR,t`_2蕮#pyqk>jK7f@?GN g'tڤ;}L}σ'N~H9Pع=mMA:%PFOR`k灒N;h'6*jA81~'dlX0YN=Vd-`bH pQpoWejz#@ZN›)ĄF*-*Z \vy/''b~@{JurZmc-{ސ=͔#<0.nwqq:O";q$o+3tcºI@"_$NUkЖJ"&98>C|@.]Yau aD**,.w p (PbEBwuT#ip/WQPŇ|c!sTu6)v.N(XM2?J )GKTkN>&PsM~x˃1HK)r l<~<{Uxww:ԺUPETe+}JdrYY?PF:WQEo愶c#>:NGHn^B_H!AO!nkHN]|Xn 1̦:GbRE6M48p<}U=!QrSL]4;\9>s [ \=GBc~]WӨcG.WC @>0CAhBwsz 66F8۝ Djzi])]u>- Vq+#ҵԠejS#ē.ˏRπ Z|}aLzxT&GGTIQ2ET r YFMFV:F9MGO}n`=V7ZڥԇĩmL$q2\w;4cC;n tH* }N*C8KD=P+8u>q>-6VQM)T@-K@ =tE?M46l9hb$1t|-ȹd\>[ e0}L6? /z O{#IAF%PagΠ6B1|9p߯@*i Ud-wރ{2z^IWp&9#gCV #*(*{X )K >A!}%w뤃u 4N9i̮R@>i0.$T2=ެ'vNqPs?s}Ft}8?ScPyewp~7e8,MrٱFfk6e`6[VѦE:@sD8x8GeF %jK-ZcPnPP6'I8G.vgHΤ־O0Wc$%cx`'5JO 3̀>cbC.,gEfUuކ#s#@ba ĖA ! $.b,%.sXd$0Α-l$S ؛#d,19{^ZUȸddUtW32R2z/"SYTJ}P2g n)7njm$ &-wLxA*|  #x"{/@eI X:ӡ`MjX{c:X"P0igdMܔY7ÁN@32M%ِ7M%4 0ɿm"}oo[2ᅶ8@.!ׇA![(@i,e4sɫIk`I$u#Bx#9[]91.4ܲ2ס+Ϋ砠orH00G #GH{aN@2);\4K cTg%iǁK{ehi@: }jtvo|70+I>6]Ex[l)k~j`ƿھ5zft4 @7U}-o}$ZW$ /TqXwy$0kTXWfíX::t=7Wa-,0rpS"3eiLMB+Ũ!z_51!+/n'n$>J#sG>Fi!t}梱znZ&+OTEBz`͑*hͶ9f0([dg.!@:L'L9>2eEdž838c70y8%S 2AaLD[:Wޛ!=$/\ܵċl%9 `[@:}|?H_t=hB=QpM~[`":Fu o2~4a) L|Q\<*Y %8G!$F O$D;H8 %v {ȸ"kV(6t`=5 gY"T!US_xHKr@-gv}KPg_pQv 0v6< bFuTA@ZreyH.}T(;xȻ[EsMIW` s{Pϒ:~Ia-$qŇ(Ħg*>&=(nmaaM}>'I]*@n2v)>DOX9Yϖ%xNx1K[ӷ*ΈжN%oHƣ8jia UWFX!IPP5-4GX怖i_&4DHCMj?8> %kK(Cr4 n+R?& IW3XD,b= )dTEpH}z#$N6{NQ6^QЎj:[20E"E0lpֺlQXd|+CKB:&u(_N@qד'}ryC:ΩW}aܾyʊ{;>}`Mr{m߲ɾWD(s\/ƃT& irc+n:hg`8pP96̒C9UYR@,,d ? rKh;-RQJ:ڗ%렬"%Ľo:>)}-}C68;_v- )ؖY!dr J)̻d}b:3AuU}t0Wk0;}iFa-1-~E*@ZZ\(i3Lh+dBr7n["Mάa:K!T1{M^ߘMNewIH΁]zLRԥ,=+{*[Gp_oiZ?ku5Uɭ6ɶ9'Y`\o;6~8i^S`ESr+)AcF gFTtjw/t9ݽkKLz7 3m55إ?'_t}| ŶP7g,A8rrRXop C :@!/ÀLp%Om+WLCVIDhZa'LngD{LE6DI0oRlX+]6I5/+?>tI}j {z׃{H@7=ͱ.1(kfkԠ[1 2kJ[R8 }9,BZ6}]@&DHO\L}y 'dZiu`d"h}D2(j*C/:tΈ_:r.Yzpl{K.S&ie#>wMw8JVϵىv߶,wulV'Ca,dW. {&t$Q־[QiN% ؓXG!M!A 48kH -)&s$u5"E}vn9$۾ʥ7zcW} :-WDMxlrleIA:QEdKTzH!i<~'Oʒ8lLIg$#_"IbM&AEǰnfȭݧ<^ܿ+ˇK [8/W)RM߻$}?Aa=>gǕ9#Qk2I}Oˆ ֮N2 RdbnʲAY3+B0?__}1'>H{=ekeݮXR[몑~YSwmdu b2w+yRjJ=&SϹ謗^Rs QUge"0F\USBKHI/-۾??'?ɯگy@~A:l^v/t{mRř!&G$w U\֥MKo˲A$ŲQkݏF#Î}P%BChz?׿NOǾ& ۶J`u|_@'m;qZEcӟR;YSPj9/P%9=m}Jzؾ>XuDah8RoByBķjz4 ooggw]U._yw;g۶m۲{{ʶ 0)jyK*eol֩k:k,z}2:FcKy~~|#w:q} me߇~{ޱ($VT=r]@ZjV>9)}qghKЙY!wv&R:o7BEG|7໿w}ooSG`e|=h}9w\1|Dʓ1r=;vcF1Ȼ/ "% [suٙEK ɟӟ4O ~'~b/8|_~>;@K !{c*z1g_c|ma͚ecbk]N)̖o;nį{>+K})Lk{W]K ooSԽ׾5}k`[ 3R~$ts:󦻤che]EfW<Ћ=ƺAnr:ɾޗ5 yY@v+ $.YJ"ŇD6dhr¶Zõ'n˷ɓ'K/-~w~/~SH>7;...z|$M{E)g:Szcnd=' c>eS UM-ẏOd)dz :jBe@zn+˼Co|}+ɥ{X,?ӑ/~/P۞nKa>֫10l˃lMEuSH:9JO.Le5ZrPc_nccknz/}K!";K ~/wQ.eM% 9@~^%bV”PwPڠ&XK_ ࢬ%pZl (rz-w} ۮ^j@_wa6s@}K^=n o~:9}LF՟ ZBRkPy, j9|SV=xRVIGz^뵨^Rۿ_3@h":>0pNXWMS"7="*99wp `nJ\&?:T/9@]{BިWd>l)*ce>я¢3j͋~\ 1 #ݖ},cUH Pq=/'^1+޳~rY.@!juԠGQ$ NJ47c*x c+)>JCK O}S؏Ӈ^0Tnrޭ6lWc0W)zPND5Bv}&~ăOBFoX0cAՅj_TZVޮԥݵ.׫s?xzyoO/-/ c/ZC?m%B vf(ogD86C fmq8ۤ<2A4JuuI:( :\2.,I}M\rhU88&'&uA]J.]~1`.䱎 ǿHBQ)9̪ ")S0u[&(B /=cL9FerSK:{'> Ix±c Zݶe^?>\!Wc`F3A!9s3SK)T'^-;ܩ:NVa[bN1 ]ْfI&U.!=3᥅lt?Tr=}n8cݾE>cI&e՜ZN) V-,[uA)NQ p:NjT:rXjكڪzu:24.MȖje _Y_G2`$%6!: "$ZΩ̶=~cuxAZd IDAT:Ω|juun}>)3M-Q80f9UT~=@9*瘧35  Ɓy@Z{H;Eds0gU ` jPpK7|6ނ&0Ġ2&ԠǂkRe,mCJw.I=@KW[lx<^΁[5hor}MRW:>퍇ƩV5ġ2ZA H1! `)"b*SV4XWQ,&Y %F!Mnk D ㇎B3u@ifLB4` x Ȼ>n$ʺH0LBX;&)L$ S=mا_ q{8#DnHBʥלy ;sۗ {8bdjyeCGXXpV։ͱNsPϝuC+wrsDiB;s@6SЩSAI&Nm%.:9j#Q=K3g?5;EWлOo/Gکd\ 79ֶA\7r\ ;o떭^I2ÇB1 'DyBT^5[N-Kx%Beu jAZYyV;}zڹ 푇L#9yL%$5?@wVIA=BGQPO_i XudC:^6\>F:vF9Xm-$j/WhoUYS+%:(7Lf. )gĕ{=#G*gƷ.b5q f-(EkkF |ُ=$G?!q=P)?ґ$-oS{n V8U@wUGKc;#q.te 1F5`Ԛ(ݠ98TIh6hZ) yPհD-(epDUgwlp4sfs,W {+h;|4Z#4Zl@@ Yk#9*nK\ֿK̺w5n7,y| wm=GnJyVMslqz~fl$j2@5q鶇eI✅`cu.ez(=oq]p^VaR[ƒDc= Y1 *tP9SeEǖQz՜BYQ'Q5W}y O縍5߀5#C:?__a6q}}g>&Iݠ<}g9V=cbUsgr9P:Dip:BW*hG-qsQ5i5ղŰ²nnEXbnTX˪ |"  /`mGo$N,U *d}vXZT=f*__.a@rpF"8ws=^Jb:??yN|أ`m1:|a+ٲg`:~sas*YrŬ$ ЫްG?QO&piҶ(=}czN#yƶLS+pEp9kuP2>J2$Di- X3np#R,-ؙWǂ{H)W6i,s vj1m6XY9G\W^98'jlUr7ǭ&ʓƯ?wGr?o=6מC;h}+~| _-?ìV-o/xq? tZ.C̈4fm*z0眀o0LgH >H ʷ+go,QjY XйccSz8lOp^t u6ǴN,Vn]f*A˯JoYo76LaJÄu9Zw4|_(Abwަx nJ`u%Vš+vK=ZjKÑrS*SgVl), v~\Tg!%+P.a,žT ݧj*vnkև]JX pSS0iC`s Ƅ6;Dy!Mak+I4g6ξvrÙs |m!Mr^{yςUa&9- Z1N=Џkg;ú0Xk,|{t@0V:'pVr0:; KJ=9ش G s c)pzr\vpƚk:D Xƽz<蠞U7 iXr̄9dyXfj2UTLhPu9Xҏ}JՙXbͧNS9%W<}?Ln8@Эת)QCS/J=TiJ'#*!ōF'%p +5JPn# G6k(C$i %נXrI׈\{*W&sZi9ư@92Gr$q4n ʶdtJT IK=S΃%Yt8˫s*m鱺]P.a>kò)IGSEQ&@CԳ&R$ ]*8 ֩h&1h '_GC'3<x`XhDžr&g^==WKuPpkae`hgk3K=xb mSg?EjTrBU^Msi<3竑WkKw F_ WEK2i]9s+N;l2S"4ljmё9DFx}mZL L,L jP}PCxwiDzBp*<ùvL -ygin-"fujR8o0wj yΐf ӶT'99(e߹00+CaoKRp:^溊uOE:j!vM1ڝCz~e,A5s-9@858XIs/%Y:E- 50!." ..9c,PFݲ\x{条1ȍEn,ᬷN=CVn|fqJ9B#ӵEuN+wPAx)WˆG"<4EydGFyh,\;A-"n:1p㕳5X{(^1A#?]'(gM^{&ò29P̊IhILQFA޹v)FGn:ei puJHTEګ]j7UuΫ)F(%@&B{g ھ;)\`y < <xhుG6\eW z ʢއsg)woń~Zb΃i;u˂}Z Hk]g#{FJGK]({wX6`Vz|қ_x/6#-Ȟη2&RPE]!L#CSC|CW`!)4y{׀G"<8R%K\vA,9νz٩e'@SOբ uU)g;Ak.r] ey5J !}¤Sao%d8!#v|9QTV֘8G `2DCZf s8in#kX jnkDP 'HsX1kΏcj\wpiY;diХWѷs^Y5S=E'9Ӵ1jQ;Gj+D9wڸPi5d ͑8*ݓ0t0mY+PN-to}Tt]5c[3&(-z䵆8%J)^g)ጿnrTFa1zPxas ܸn)ҾGcrky(ft k\ ͕WU\ ܹYn{/LΡ=HkOP{jpنhg}emk#*VY@W/\V;6s!sa[F΅ rHtʱ~\[/upn=[!Cj;fmX\_i_^ip6uBn-nʕ%5gнǼ](QίX>Qp6WJw-Khqm&29A'爜bN;';az#P;ec k+1"ϺJgk}pw2l&}^З9t( ۝pN?;t]T>Sq.>`:jwm]mR+Z6Jj㨠o#04pw,sF@<7BAci ;18TxAF0KnFnqP6W̕^)t-LH59Cbzkc͌m*+MqBgk{MLiLA'It߱Ur,nH?7z:iz:G;n6NǪ.2mhc =[N=[aCk-Z1 PTTg fűY,|LVpnB^-f5C >9@NYY|0zY*o}4B{m&=!ƣ\hl0mTJ JZO%!5}+_ _KoܵTw?_Vƶy5ʸsP.sCܢ0͟ycaա7f+2︘,yB[8VЭ[)ݭsTR,+tpNcϡ@Ĝ;ܜ Q-ؘ)Nib4&=ѫfQکA\M{OHCh#`Y! +R=#~NvH!=/mm|+_l-quM=kJAX)ݶ.Png cN@[uvuzNk(xehs IDATZNsB;}ЁvKB`fA FN0z3:xYWA 5l̂r=VYvҨ6`m6V d0Sep`V&z;#Xo6z([Hs'$@V'`؇>!_*'~=ۿ͟ɟTa)J=yBBwQg3Hac6;#nbi9[C57X\n(δ|Kſqǩlg ṈFyra9j{FgOvN{,m Y2v{p7ҏ'ܔ~FA]`$QNAwcxȚ3Ԟ.`xeN68{a yc0,`r r ? ](v.\3q :bea~֗sGH)/ J\;nXucuρ;o k䮾z|v\ AyF=uQbAܕ(Su}cb!iy?[49oL`眉- Kl_ nm7a$*h=k=c-ltεmp 4z­uSuua9&g ԩf{cI:ڣ3whXX`\3nYC;#, -&h.L*'M7ͬAƣc]w$s2#`&B_HhO$Va9Vhs5^= zz ¸_ 2kDG%ׂsl/`cD\GE=TZ)~eW'>?Wa t[-C ]c]ᦚ8 Ά +ɥ7^=_bp WF6Ruá(ݙ`0:0O:vM#+bu5Vt+oaJì2`2%'" /uqtҘB.ҏEpCaH! czPMK_Cy7+ 2eąLX3At7(~'Z-bYF,KÕZ.R ϬºG ](B0pnN{"yl +KfVkasl~KVI}0Pye|2CTwNc+E|C`\ ~hG IΨp֨7 [9Ŝ,kOAjq)v!a="CB|CY0@eЛ ܪe)K5܈ %0\bn;֋L)H0]8m.q歍Lט#74,XbtE'+z޸FAҁB+LGXffjqVFqPJk p? "g,GZKzHp=Ӹj*Z]O!c,S^WS%VL5Q9(voK~JyEղY7VzZpIn¥p);5>4ǂyt$;b0͒k,7gzNA[ 00aNa|LrLϹD-SXt]U|X S{{WϵmTt]5L_vdr_xxX~A5 n5JLu6Gx[ж/ck&ͣZ:ܸ0mTYWj Ϥc55lP<#6NE_XfrE'0 #p-+bDp" L8E X>Otc-faCkP1W~^,GG6@\0a:^)jZ\~HA5r 8rOe]-/YWyv|zη{#O1.Tv)IOPE7>/e3p >䁠 C_3cA_e3˜t<% tzFNne.3ߑ3JDZŲYu[:`:0&[j-34t *Y*scI$˨5<.`j*r 磌Ӹ~ y.)~5J.hԤ /< kR0^!UBTҩ"{?҉`6J[,nTXOn g\< 6k&#+NޣWȵk%F:1rspSi8 K4l,RLŢE:(D13Ț)0A#GHzL1[)kVǸ5q[%ǎa}1o ivbO}xk<.ɮϞrVa)f*؅ z!9N=?x$ Lg={l F`F/1rI5†c Z .0фq8!g\ڵY[d+Utc46UEԀ1"~ ?chaU>WX.H20MϻۣǕ?kbR!;ork)W۠=Oתտ?uVlú;Q ,S3Ls!#%j<y{NE>bz- iy$@:.0kùtjÉ8yn>Lzca@7qbL 7i tԫ tsj<ĸ$w/w:ީڽm QScJ:}}"7|=GB[-aL5*Z|\k}Զ/r Jyn A1ҧ9@^"'M,sPj>uhSd󞇳`ȻL5u&.#bЎs1ѱ4Ln;MXd[E̮o$̏&.tIq j]U=uC)eJ8뭎|y\Iڱd1Bs-|)dt~o@;]:d.Y4=QTA+fy@lޡ' zcykk#1)N]^ӡa+DKX;le nmJe]֡Zk$^̇uho MT5Һy]஥]P`"]9<."m-k։S 3u):WdplQD6S*cH.yd6^5:I# 8:7A.;CCyϺHӢ129A #aB7GVg-4lT\WN9GLPxaС>zWʡ<'y! \;ݾ"4&ݖW:=g~Odd},7>fHggktNI+h L :QZdj`f# E6˙^36>n:1pt!O;䲃g\CzfqyӺHə#lD8.3iam¦ҩ#@+ei adӸ:17?ﯡ)}TPMWC,o;}˺1 %;K^fs_޳Œ1H? ) KX1H\d H(JY"h<) p1`=سg׵硺^{=۳a7^k5@ IdG,|NLuӽ6Țx_QI ʺfhMD7QzB-Vs) yrppx:̎CvbbspЃoHۀRXj?yĵPD WǙ C/ 1[ 1B/Lm V.!FB6ʆNXzv`7&(s<\hP[Q$J؅,A=ah`Wq/<5G` UEVҖFX+"yxRO},df&b[d0awLZ-챓8JotDI{idU*8|rNes=g$tB1iG{AU\A,W@@8-i#l@ΧAvz=@kiAVe.[@bP7֦ }/qie+QB0l [[FƘ-2VPM)Pt!.^7 Zwv;/CZ'|BOʘewC *E y1i50{d`rD#3o0"TFI+'>$ޟ|+%9?D>jvn!~7N8c$5/)ў`w@л X胚9ֆju[!L`."~sohJ 框D炽]}cRlH]*H-(LOFO1mVe( :/_䕯|%SE;^ɴtٓ~avG^EJ8}|=cdx,O+&S;l)鲳O WߨW}4aq]=Mּ Z8+pFS+}=%#݋} =GkHm l 9/n qK`P73MV:ou MLq/<ʰ*lq+j$FgܙMOO<o3i~;F￟K.q%vr]<|LyHNc"2<&1&rBF"g R97xۅy:]Zr_y%Q, 1+!(]>vjS^8pF^Z ^7.p;a:5ǭ/L$&IgS [o_b&'n#z:çmhۅ\\=O[>E1Yas-kROq|Ndۓ?RGj==Ц=(<'@z0Zuhkwب)NwDW^_ F*[9h -Y$rM:p]B+}' 20qf@ T+~\Lо*jt(G%4Fo39*?ѣ?Bvwvf< 7=A… /x|S5S}ݞVcjwLȸ<bC+8$'0}x8)xKjA#+73Ap=uˁ :`'+q5_C:|жJ]tsJTJ2SxK-7ߔk4b01G&Ӓ/v ]*X8x b0||H̀ 98K㦕4f˙IO?I bsf9L?YaF _wRVWt~«ܾNFSsKszP nHXU\Wp=UVqq>(hqQл%n}Wa {N90SN9V"5!b́ \S|G^"#<~f~>ݣƤ&\K}V^tz;/PR>'uIѰt23ךU53H%CgES41ـ%$p@iwV8U[@W*#y xYıeht :0t ;Nu){V8H+ԖE/ ~^sUsS@j,ၤ&5R5TPRAGs9j1+=7]GBŜ˳7M;E>g5Yz\9(,y{8ǒ­ 5,SEfq>?s#*+x᥁^2#g*5k h3+t]m'ƮqB_!24 Ԗ!Ԋ[>}NΣ<^+)3dy|ĞleE$D#ThacMX]5c::"T7, ŝ)pk[\=cq`N`uںEy*!4q4Аkx`cJ~0 bPcIh6)QqGi{#=l)lNdpfíR*EED^\f> $yF$'"1};!jJ"~8D'.>&=kY C!S`&6D L1{0,]=2TʂVYCK si!z3an#_$<,c?!hI ۥ$cᨖF<-o?)D/,$*\9 ɐ2֌+1K&*[Zt"Hz6'UvQO?_IQS쒧\d= x%\'qROk=O"DOϓ(gg ^lotѫ e`A6"Aꀨu[X6 yUJxEY,<`#aƋ:x0L5ԀZq UO6+x b^vc}8Ҹ> 'yENxYk&L͖ϼgc1juL{cŜ}m$Ggʈ?Be/W8 d kKYV*^aʐyVY8VԱ,%u,cK;KeCԾG8t|gVl@%gNUⅸ 1@%AQUG'Yyyd1Ozևx:oB%1%ϲTH(-&k:GU_U=y 0&}\<@Я8|S`R+k9%A9 A.PM"FCzkZak"*QǓL70!䃵 ~uDJE &^3+ :1$/{LQaRi"֘8C, gI/>0%1}|~wid?oi;c?Ց"M:r3FVm ;XY]s;VkuPnPsU\-`9qa9uN cYS*+bYƱ\<CyPgTHVZH%ؤ5Tcb3afbNtb瀞%A $/:/73$<5Ouh'9(&,ZTp:D=YLz;0z z$(QţM`dX]Y]Yt!>H&M0y8K3ZcClLkbYűqHM&:o[øJչ8>iTA5:jjxo"DbF=q+yݢ6Ųp"<<'K/VټPόX)e/Ly("yN׆k;>{tŗ$,[xN@ 2̃.Jz>B*=o#--l|e9eR,b9E)aKR=p^r!ZY@ жD} yCdHb}3',FTF( 1*6NWn:?_.RD%cͤI#7Uт#M#8Ĝ-sqR9C 33uE ME-EmE"[,rL5ذgDlu4;~Xص较(uanC v*hm1 _@|<'V_}DyɌYFԲ* w%AQ10ZMj7]nMM'ǥ_Jy=+ -}d%I=j$o`Пk]$Dk#t$j5uEmzh89D6A"[,> 6pY#٪cC"^ٶca'Bm!n=xyG,"nq<튙:Co!`ΙX9{Bll+qG$t'"]"$cbV Y$=>7_MOQ,5sû Ycgvr/cz5Լx`kHF=; @NXA*Ҫz&PhYT <,uviː 3FbQY89f"W1c]Ǟs!΂]Dk+Hm2n!Co;C/aPWQIrlIYx%A =Ӻ$w%rWueIMG9;"<>|d|t{(.I:4 h&B2D8Θ1paq*{ [" ̎C#8蠎TFkmĴ9e-[Dc {G>s=q+1+Wi) -0͸Kt#xΚXևi$!dlR>J;>W^O|;o}[|oep6mȕ Mߚ8&,Mk0l_?<t١U=`yp(+=0C/=VܶmaFN= Į^i++JvYWT* E`̂bR|CpM0u!Oİ7OU~r#ᡇҥK_5oCwT*ZKKK,--xe]e]vvv؉3<st{8w??^_e(Q&CI덋/o|V}G>}k|_<:~i^:N~TZG-oy ׵4?$"^s}X]Q5}{߉գ|`kk3g}zꩧ?SΞ=gOFka /_Vaaqq^򒗜hgΜŋ<裏^:><lmmO^:.gx"_WN7x=/c=[}k'>`07;nFY[NҥK\tDzӛw>ϹYuu]Mo.n%k_Zn~q…^O/^ oxÉկ~5^җyu=裼կ?o{x'[O׼5>_Q_~$[~?C$?0vGy9ST??˻n~/}׽u'Rx9בӟ?)ϟ?z|^_,|K_\|7/>Ο?Oo{'?{y睼y+_yb=_Pz׈_v>Sԉ{wyueo {^]::o}[Y^^faa}|#qXk'?ybu%W7MXnEO|<=f{$%JAQ6(Q K(QEI%Ww7L~e(ql]y  ox?vy;{>psw?A^Ν;G;~wQD1J.񼅪}|_瓟$kkkc1yᇹpv}c |A&~;_շQD!~%^{'s=/xvۿ/9{@`~%J.k}cwٟt]z!xq>yGxի%n\KZ_2/_殻Ї>=-+%GKZ;n{__s.QD.QD%A(Q K(QEI%J(q$%JAQt%Jܠ( D%nP]D7(J.QDKS2EIENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/chebyu.png0000644000175000017500000005373712253362407022703 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxyxSW--RƏA 2""pAD28ଠTA+ (EB)cy$#攒tH\9'YMV@{VPsc"""""Rэh""""&h""""&h""""&h""""&h""""&h""""&h""""&h""""&h""""&h""""&h""""&h""""&h""""&h""""&h""""&h""""&h""""&h""""&h""""&h""""&h""""&ItPPW^y;w4v8""""͊IT* mpDDDD ;(//ѣlPDDDD +cP Vw%  kkk/ +++%%%$b1c.$ d2IbcJ(**BYY._lDI޽{qQ`ƌ5mܸ~~~F4Y~=.\h0LQmDMtc;AO4 &My.66V4;h׮C0ID]51L:IXqqqP*$uFD7...fkбiuFD7A Ѡ)--5v&6&YL&C\\H;͛$uF<0Knٲ%jPLݻ;DEQ, 8q'QQQ$uFԤX8xC, 8NgLQmDM9s [;-Ҡ=;?uFԤ@=6fiD1QmDM)˛A{yyA*Cw`0S@EQj4=hS, :??;v͛7QRRbpL1QmDMTUUڵk􄫫, ݺu9ALFEQ5 P(&9 A+ qDFLFEQ5< AjďnD]5Qc3835haC4jL`lD]5QѠHNN8ġ1QmDMԘ L puuE\\T*C2 ďnD]5H"66&YBD"AnPVVdcd݈h#jݻwD"1v8:1iV(;v,5sa݈h#jbπUt@QSG36.ڈ3`֭[k׮Zrrr@qbG7.ڈ=&qDGGc믿 +++ݻq-!Xa\M᳴c&)u>֯_?X4=hؿ?V^1v ;±3f`ǎ<<|x sԩRSSQXXhL 1Qm,]BK.9"&oк}k={\bpLSNvQm,]hgeGr0fiК$!.^hpL1Qm,]_yy8"^4finnnk ҥK dWFEKD~~~Sk51C҉YBwDa!ꢍk"t/?dȆD& Z_ܾJ.]`gg/2> Qm,Yx"な*<kWkcYIBL={"//FXz6D]dMRRR'6rT1K=I,=S.X&Ɵ&a}{ Qm,YAꁒ8r}'{#G4ۓ؃`鉟uƒ5t||n0S=I?u!ꢍ%k Lw0S=IpwwǕ+WPUUe%'~BEKդ 111pUlH$6hKM Qm,UK.A | 箘Aߙ$uIII&mMsa!ꢍ%jRPP7o[ NhXKctWҠu% q˗/7g8&&~6:zxd#<<^y̙3}Qs8m!ꢍ%jNf1Aشilقr ruwWu5&ko>;C q<27UJ^R?wCEK\9Q 5i0(y:t5e%eaS~ݻ6mϟI rȽ|l޽&)u>e241tla~/qݹsn:^&HH؁?vvvXfpΦ g?yd 8HJJB;lF t ݥ] ]uhQ1\_uVz&ۃ=z4lڴ9^ x5YBKLQm,MSNzvYÜӋ1#`b cW+&kuڪ5 U9g+31H+NsRaKKQm,EEa~(UJL;iiwfi, rǭƣ,XO}uR4ޛ,d8??|;[#GwwҠ,7`ߧ'', ^*O*3g#D%%~6IrC48cĖ [Эu7cW/Ҡk+7x UBnn.78#bI ꢍ%h3iHCPx0)ޘAVnV\!>CPZ=NmIЖi(.뚐D axĽv}/` ͽI,(8{ LfYt]I8p D9>!2:z⧱hs/k*pzhSYt]IѬ(a;nnX?_ِ{=XD]W5a(+A.]T9b}$&RSx} Jѿ"66 -ċ^DZq4Jq{9D]4)*S~7`n(--ŀ ՉIG>bO? x}@͙3g Ha m?ǜ{Vǽi .+Ĝ?lY|e@+yx0qO?a„ o Ro k#C;z7/oF^M4QmM> wS~ӥK`VVVM'R.婛19{C0ID]1wMNu4QQ9ApR0]wAT!cP) QgΑhkoN?=a$4$ H0l0(J M >>yi'><>BmVaBEsKK{}8-zl)O[JÞ|DҠ뻒v @0^8<0ZڵĊxfcc40}"ꢍ9iz|ZlqR;C C%Zdͷ[}7wҠ$=Ǵ?`#ƒ /f_b414.ژ&$p<}0.c[z?#R'mx} ggo1Knhz gggk}; w:BBgsc@E$6z@}1]#~[uQT=5 ss'pvv߄0Knhd2 zM9ƮC~jbnBE /z>0a V.}…@6J69gϞEyy9z!ANҠ$Pa -Ēw덺osanBEk | @N.={70 wX+%X0ziRAn^Ԡ$neҋ1rH5́9%~QmIv6 K. < òGeBr> A[;И$!leccPTzՉH$8n#rr1j( F+Va.4&]ѣR> L L H$-Ũq=:6Br*?R=~qJ'O֬ A7&Ivvv4hpʕzN&ai$.g^ƸPTQԨ &~6դؽ8fZBB7իEEs.g^ƨΣ}vȤQU Nh9p<::yyy4hlmm ޚ4$ 54fll05C| "%Oz eUeTDŽuFTT L1mlo_] #5ذ6R벪2L5)3VMս{`$!P i9GG=omD9&dRu;1QD]ы& xQSO>7}ڰ32͛#+ E зM_=o88j]EQE ̃]';OrqN} "ʍNaa!FBѨ623sCOk"+GDĬP*`WHw򝶶ĉdIITT]O` =YYS)6ܪq\P͍,**j[#rzIBhѢF"| ꊀ1 _f$D]i&$p4x1о=0|8i lۦ)?@= 3f`~tmѽʐ[&ݭfvΞ= 6 NNN_fYtcƌ8|phۢ-p\;aO(U&TdnD]&$pz[.k֨Ǒ}T=].- 8x9h@^HR`fLރNphۢmI:P^m}{^KA7%IǠs;y./F7i1Qmj$>X [%|ܺ?̛.@*%fퟅ_/-;hܮTfV"}K:d2x:/Iσu>#\|O7%I~~~h۶-ÑWWFxd#yI]c[a%m~i"JKQ_2nUTU_ vv󵃭-:ZmvZQ͉uQSC7vn~lS|OJ oK߻.Uhz;Xq.77B^aݮ];lٲcǎq+ 5H$3?1eʔ&ѵ#^£K;'novB΁ MdN2ȜdH%TP%(=|d.C]2-#$21lќ|ڔO>ѭjS㓇Bc]uEЬ thᮯS+!+ ڽ J˜1c̺Q&mеqk6 r za4͠tLU6HŮ˻p% ;+a dՙt\Ά$-vT9Y *<Pn Ch{62l*`[rf1L)FoY- pB~wDI'@j/Idfʺ0児l `ee[['u{WnЮ]XYwQb[.[.zNPwz?鸨؋V:[;Tx);: 1Mf0n̘15? ]L%z! !EJ_[1$b!gSMkc>8@=r9y)AkCYw0w7N?A@V1տuFhM_ qlcsM|w> 0|3AS S?Z{\ne lPEhKVbKV駟6FB<>Fm+Xx1:'xk׮  ӷ6o0Exy~l*KF SzsV{}CqLȇ籛p)gkt= .z6N P}=Nu7@q6xTjsoxٹ-ll@*Je } YYW})))gm?2el\*1otڅ1êP膫}tXJO*X%O<1\IhsIBΠ6H~V{%5":t^;ϟ[-@3AҎ?@ IDAT𫯾K{QޛzCWפ/3jXS9O?[ޢzٳgv)UΛoL3Bl_҄&RӧF4K.e`` N]ȑ#|7ؽ{wVVV>}:O>ݤlTT=G:9U/ qv&_xhu#Ч[n%.X@omBTps e n'L_xnHlJJt2&*{2=B0+_a2ޢzƅnjr *+۵kGlا˗%K.]MY"!~ܸhظ! D΄?8<*_.\"ghPVTz ۶mkl:(q7oozkW_IBLKK#vA/PT\z|),˻k/,Is&GjI†R1eS C=C?Ȯ c_~%KKXԅ>u)..ҥKikkKl߾=rkg~~զ _M&'6dݴYnC?jӗ(kj*FKcЋ-;5k8sL^|m[|-cFJ?R/᪓R%$bPYazI@}QWη}VSh=g>=cڸq'NHH$|XQ{$'W4޽?'PTW?(DMg65 %STYb G?}<+Ia͚5zKԞ\;g-99LjtTuq52D=L`k%Q9/9s~OCc:;;W݌ rrذܵ+t)mɦkR#&RxsC-u^o׮]ۤ{b $g׭[v'99Yx\50AK(Gl0iwR3$T*ttt$0@|Ϗ<_=>ܕZкܸqC%sӦM?GyyȑTZm>>ogϪ@S4)(/Ձ_y0Vӧe)L#UUծR% oݺUuŢ o9{l/zk"j`aaazo[*;D611g4EEE4iP&?fe.4YYV'5Y0Bg}&$;'MXTD 9aim]mʞEɓsfBb^"{mE=hдڨ*)SCL:u8lذ&We.\ѣ9~x3Iaݺu7xCm߉JbX!go338B͓jݻ7sw]TP_Ÿq!y,ִasrivԉاeK&V+9w.yzi&It_N#ձ.J9~::o˽Ģ ^bii)SRR8ydkSRR(HM{4*޼F9<0RQ/fxllP 5sr?2=#E:I_&v|8044 e7GKګ6DeV!ҚRpʹҸۨsgl4. y89oVSc3p1@kkknٲ014jRƒ jUgj܎J!g~{'Nyp'c/mƸ8޼y|7x̙&m6l i?yMtA%z0$aUUx w}6KbJ EIU*W]T*2,|5mjS&Ǐ'w D;a\z&>qؖa?ҝ[@?xE7nܨ8nb zʕܹs'|MܰaCqP"S*m۶z"nN>v~#A^Ac&Nd3 8zd7 t;SL+ێm۶MXвvZclu;>XıfyoWۻoBYFEEŅj$W9>,/\{_L}2A ;$a6oL>}Ԇ8L֠뢹D Q1p]ʫJemNn#we=6{eo\r0Nԙ-MF"#"7P'4leEKnۦNAn`.: _Mj\CHHE|UT{eWwƹ+s*`'N :݈ $ԠRثWzm>t+.4m990S1=ST*.Y#G;B^H~zƔ%GWO." KO]byjۮ/QQQBYf5jbR~':A ɜUN(++<7\C)cxiaesSč7gϞ]59r99 f(3G ]YxJ*EoVe Fn_@7ߨ48Au4 4qyz ~S,Oiٴc-XXMA4h=ܙօ  s~$vK4nYZIx% Gl!EBͿ /V64طo_fff$sKs?o گiߢ3L| R96.QNdSػ!h$hп$_0_8fէmMVryr:~H_#r谡BP,,$o'xB=BcmےNĔK|Ft6UH!dzӅ|G ݺw W!LO6Ѡ1K:8-ђhaF}?.\'矯uZdJ$'L>s0.h/[O)dQ-{@GBLY3Jݛz8%FlV,@Xd"pɌLyJ⧟~J_U{5E\u9]y}&K(H"#%bu^ n$""W^y^VBn pwj0rFҀ;-Ex3C ƽפU*/·?h=_egׯuVVVܵkWci?1VmI_6}oRɡC UhzIBeR(O0//hXjn#Gɉ;ܽsg 0\1yF0\r|E/W)+L2I]aK7hZ5.9ޫ-}+]9[nT*D"?p\ɸuLMJw;6lwW6c6DH&HR/ yʕ+M3)~i朜L^MXsU_矓 RV1 &cv?2?D=Vmd j2޾&(CB&rO%!2LL/c*er; VچhRXȁ.5MJJe2YѠ@sXV! 1j*`~L_UUU\`P.־(#ܰ6)wJ.]JF׾=1/?fӱ^f'y?٠ e7 ;.P1,?OP`c&%5Ç 5.]ڤaJ̽0ІJO@v2/ $O>x!qD*9R)' u+kב?;wGHHZha$::|B7{ė7oBV\ xi) 4{))!!GrariIDATv۶/xA/^[wGKgll,w#6;Wre\ȸsiWW:gMBil)e R9N‹/zm_@fϞ/A7W]E=:}ީk+++1fr5 u!HǬɓzll_ƏWq}Tp2$"R"q+2.\Qu]k Zv[xSx8z vpu#:{$QBIe + Wdd"8)E)H.LFb~"rSՆx!ҷ^UQ@O鸵ʯ%jCϴm'wƜ9sPZZcՂU:fݙH<w@G[)'1h NAƍ?~c?Yц+ȉ.+\bq 5V{[gS?4/fr\*2 g"4{S)U7|z`$aE )08q0l6Fz 'N@}v :iAvv ._X@7l߶ =ޒaL(,B<EPxP~B ظeJJTP** j iJ‚ p⣏>ŋ:iӦ!==ÇѺuko07R9eee:u*P"pѢE /s:b٧ ["ښ/6f9RV?x댙 .0$O;%n,7m00FtQykL%TU՜JX@OOO~M.˷~[X:w\lC1ivfiІ" fEzERT矅Dm۶g}Ƭ:vNI!׮%&*Օ@T9s01QD JEEɥO8eJ$`-oŋ ZUV wnݺI57AC$ֵ&os'iii:uIn:?9TlHL']\a(8uT꼏)-1%D]ѥI||<͛G3ٳgs׮]pGB\: RseJJY ZJĊ ;3.I%$k#>> .=;Y8Æ ƍk(."b(RSSl2>J:?jՊs5:޽VU{>)AB&Dg yYJ!̠ƑQ4p%FEsyHdp⌅@F-miӖscZGZhIk/nolޮ2׭SӃ^aC=ꤔcrwWuV+#"'N_>222`UisZ&زe lقf7n܀n~DDD`ɘnkYYc09"b&jP@e ' ?eO>3b`}&\DD Jlu[CX#9B_8rfEy*`!˽s15(YIns[om-PP z>ydgl7wii)%A=TM›߄م6߇zQ @?;?~X_g6113Qz(ݎmW p-p~ 9Y?K}E76~䘋AI?XpA.#*ذkر>] 13QgЃj޾ ||q$T !Əs15(y}&IIνÇX  BC1v(w\DD JAK=P}7_VtѣQF9#?()6~䘋*t:{oemj~}#&J}joF#gP{$~OÍ9"b&j0mNNNzm55-zavM3G/˗ ]-_96XoPc."fS貲2[f qEܺu[V\/Vpck듇0 13QfG^k7nԺ?>۽[;~iS4MyZMC>Ծjs}I/&LXn:4׬ΫƿolLQ2\L!3qD444HNBoy`"b&jPIZ %"ߎY䘋A}7MƏs15(Y{QwuCPXiV09"b&jP@v '಻0 m2&\DD J6 9t=Sb."f% `?Gxbc."f% `7? HZKP`6#\DD J6 5MOnl13QzMB%lǘߍADF0xl13QzMºO{B'<+]aG,ij uG jdh6~䘋A=& Yى9/2?rELԠdH952Ξ6~|a."f% KC}A=,įӬƏs15(Y$l.nFW}b dln9"b&jP@ߩI8ҮƏs15(YIzac8+#\DD J_k6iqKe 6~䘋6mbAkk+8K~Izc-ol\DD <Ǟ={XTTT9IvE#c]돩 ?rEL`tYYrrrYSNozddd9Ipv&a9׹(\DD A}>1e|G;w.Ξ=kZ[[qQĉ~3u)tLcE{M3.^B0iL5z"عs'<K/5.  1f&B w\[`."f"n::tix!3aܹqYt. qKFdq񅹈d&aq>Əs15(Y7 ==h<Ո ,U䘋A'ao ȑwF7v嘋A'aI#yy0_AݻIi>o,@S{VabG,нtVv"jfBC c."f% t&!Əs15(Y{7 ܃9"b&jP@w7 QG<+#\DD J&a<{9"b&jP@w7 ןWotcG,mmmjꂽĎPDM2zJƏs15(Ym6N5As7GY)#\DD JF^^'Əs15(Yccbt A=/?rELԠdvV9v(vD>6~䘋AR#\DD J萛!@Əs15@kk`{̆!ʼK}133CcM[ϝ; 1 ,\ސZ/\DDlƖ!''Xvv6JKK}v|׸f4z \DD M.jDz+((ٳg1fbϞ=Xp)S@ߴX#ngELzvs]]._lz@xb:ui t^xΝCII ۱c\ݎ]vyϮGW^y---hmm=6m4̘1Vyyy?N'֯_(୷x<lذpػw3_cɒ%3gz!ԿuV\|}wl6 ìY`E~~3Fyy9v;SO(\.4C:v-[|477{-))I{nL, l6RRR }o bx?YpڵXz5[CC7r={6ǏGqqqϟkꫯ0oc3 .6Ompbذa6lIg g\empݕ(JsK^apY>s# k[.̞=1 1f[4:sK^apY>sh999!#"x'hܼyϜt7o"::x'm6Qv q|&i5'2^|EbŴ{nsw&X1cd2s\-|@bb%tHG^yDZcPVVe˖ѸqYq.3ׯ#::2 ˖-CYY;???lݺ/R=i.}}C|oˑ#Gy{{ӽ{Z[r%^zQ}}zٳ'+W8v=  ?I!m{~A*̟?n:t֭ŋcԨQHJJ† \>Sjۺu+nݺQFa-u ;wz-w>cu}hp?РI߿/ǤI +V\.R}\LvZҥK!8o6t.g,`wq8< www*..gyHP=##a=‚޽ۅ l ߃ -_3|ظqi~\Tھ{lY {}]( ?2}Ƃ ߊd@ PPP.Pcnʊ󫪪Ãݼy r o::-+ԋݻBۦM 1gNwppobbbLϘp]4,Xuy޳gIq.36R~/k]n…[rg(in ҷn:d2̝;...Z0`|}}q9LW>ckvrrr0tPk]gφR͛Mp]4$&&B&aڵ mƴvP(ɓADؿIq.3Ǐz)˪oܸQƓmp_@TT%tHTT9b@PPuwh?blm'N=Zaaa9r$Նmp_4a׮]ӧUǐ!C D2c[W1z;wz.UlcFg Px RSSq!b?^:D"}Y(J:tȨ~\gLmϟGCCF HW&M;<|N{k x PSS &Nzƍ8s挱~qY1O>uX[[cΜ9P*زe 5 ?#A <gpϘڌav'عs'5]ڵk???|'& 2 ϟ  ŝ;wPZZ wwwhrg,meeeHLLD@@BCC Gٳ'nܸa{k 8ۃ~ᇱn:l޼.\0鵸h8z( voP K>ciH$ "<ڶm ˔ph}Z/mbA&#Zqbccg߿? H̬%h#ŋQPP'xZСCK.QeK mܹsFxԜׯ_礛+Ϟ 47I7nMoz_S庞7o\Wܹs1cƴدҧ*ۥ:ж3{`q@s3 8pڽF_޽@sqU8;;#,,1xyy!11CNotNNz쉇zׯ_GZZI/\xL o@ gLͅuѿ-k>@!C@Ppmhۙ^4=Shp+Р))[ns we>Xs}C 17]]]n߾ ___xyyQU*}=W \xL o@ʽammvo׻wo(JTOe Xs}\3Іݍ70@>9@;'N{0>ƍÅS,4۷/b1+WCΔTT4קz۽{ɮ+\xL o@C{EMX~hs_]]__6Ǜ보D~PSS̮Cڮ+ ts}SN-mg4r߿xgZ3>chsߵkנT*5zHJJ20n߾ >zp$&&"##ds?{7F㨬SO=½NC=Hd6w@[}*o!`dUM4'80gjx n:Z9Tlll{A*]ۯ@[}\Av@DDDaƤ>9@\ڽV1n?߿qƀm } 2%|2Lzmm0gjx N8 ;mR1 9O*"-- puuxNk}Fjjc E߶*Z@ PU]͉70aMڜw7zBa m]WhM0w 4FoXYYitog mvsQhǕ@mUZ(!>>&~gAsNmWYYhOW|ЌUzN}s,3P KKK{&}=z`~Ou{h_ߣ>?( $4#F† O?4ɺ 'OT*н^\WYY"¢4鳷Gpp0222PSScJOunO_s79僄য়~갇i( 4hLPmS;<=}*7jʴ9Ч@w n>Hhfk7PΝ;~u"%%ER4mb껯w%&NhV}*hZJBW}*e[ҹlsmN}{Μ9MwU߿+WDee%8]#uV Eѫ?k,^֨i_E駟\S+--%Իwoʛ>#@6loz-S }.$$нSw)ij%JU(mR}o&kbgضP8݃~ua͚5&9 ں7sss 0ў>ÜSum;LLx[{#ud*r>A\q!IGCqfHvɓ @WgϞNGo }G~~efоp&Եaqa\?9>}@rByTޙAOFqq>HՁÇO?4;=+(4G GGGxzzvxn{\Х.VhQ@dh$_|\G"b}fDX҈#xaF3}BSL0طoA>Hճ~7 /]m\C]]""" :<#}!!! "dgghv9n>s5Ao Ц ?#luFF<3IЕ*++q!899aZ }hs t/88p]m.444"f:JL?///H$w}g>tea߾}hhh)S`ccUЧ2:5GF@wOeei%(U6(*n6vۨRm}"'ORu!A 4l۶ ugHU!б>6{DWM΄@ z+2L=O߄,GfT}*Tn褮>tUNuWWWܽ{Wv֧Vh[2?DB(*mё>s8:k2X҈n nrsskkk-y/&g-:8y$ʴP @Wv a0}tD"u0d2T2>)).4)3Sɖk&\H,WŸ ad/"ʽ@oCaeʳB{>l9 @Wqo]O_7I%Rxr>?3 7trdffsqq 222·mi[R|_󅅓 uVBB[!Ukʹi?>tE!55W\ADDο]7Ph }56 Bc[Dl)ɳQQ 0 Y}GaaCWXڈ  /&?=hH1Bed(kzİagv>Hc;ۚ ۃ6T!uQ* W|[ダ@YDd;B[Cՙ>s9Ӗ:Ly>`gff@B=z.V(bܹM6Q  èӧO׹|WB5І+ғR{OaUqU$3HM [} W7šB  bcPѺٳgC(b˖-hll[ASN:t 2D5*Ҧ6D)K؞ap4?BK!?w 3}ɡI[/EPHMA"~bCw܁D֭&Lb>H`@xW*UgggݻP*;O0D_RȲdp':K;z!;ܽ{w3C-/ 뺍s44"lܸQo} LhÁɓ'UGWB ˑuC~^mNB"оݙ>sjoעJ5ARAAA:0#tA%(ޥ9>vƑ#Gtz>2а~z0 y49]g$Pu buѪs(AwF KKK[k+={nah"6W /jZXX``[l1TfASr96l@ ]OWB /w[Z erʕ.wugD"P\\jUBsm$Z[1 ԄHƲF.Lm3\{K/6oެ" Lhؿ? Ok/DWB1eP||QXpFFr4&=!<_']`m1y0lmPzZ:}0|pdeeԩSƐ>H`@A]@/||_Vi謁݃ҽFun{5pqً%iX.ːgO,\nۃ>"p->}3fAuue P:lo&6=@ԏQk*?>r@D:#"Jsb$o|m+P8|xC·F1c0 x7v5֙šy3B;]<{SLۧΝbl׃LhP-Zа 8::iwj5mιZx <\0& "v=KՐelˉBՃcVJamnmگ Qb{v|  '0q N8U7p`pr&7eA(;\(R9>={666x뭷 +V0B>HhFK/a& _`mm9s\_WB*dxț(Q0Kk:vLفxEW s/ߣW$$`301(.ENU= x> 0hw mOeTšBu f웁 /*ޯ@D옲{M\!GUQ`,92ՄH|!C+3{䄭[%Q׃ kqz?k,ђ@s5P2Jru y~IYFYǾ1 C2NQ{b@^_zɌQ2Gdd:k0v, AA@XXz  + P*stt+Lvto^u j8"z wѐtlS,M?qD;Ofc(5lnM}CbHKo _ !Ν;~ٳYm]UUU9s& K)M1dI1a85~Opfg]|Y.oF}7g0}tl:Ic_YrB5WTT ??8pӧ)u|1qc Ý;@aa"X$%!>ز%2Fȟ#&LÅ/gooy桮W6Z&&&"''^cʕ4Ho.ZZp!/L DĽ%%%dooOVVVcn9r4(9-w"Ā&L5 5cqqqNs?^/S\N\cI"PCQu8N bD`ۻ]~ /[2J%QCQu5Qv6ё#D_M4oQ߾ Bo/՜9OebB hm_:I G6nܨW+HYjM?]x @BWG\%FiXdffH$"6B5inĄ}DD>^{(0>"ojg2 O/}+έ Ā\p\~im>#'^Mԯp@0N%ڶ(/g)*5 5'+F$!l=6m"&ˉJK_VЗlSre:m{8!\O_$tԩoUW|R_B! $:f̘AhʕFPbob1Y[[ӽ{-G#r^eukۍmcQ _'Ā4Jp}.e_]&#Zť뻉;|ܤH QV_|r5>DǏOI$D""__RxǍz}L,;VFH7<0 Iivt) |p @߾PXev 8P~/>p ̄48 п[9F >sg"D辤;:)sRtZ5={Č3PTT_=h"JNN6{ƆlmmxK)I1RvEve|RJRHLL L@B/+HΨ:q"MgΜIĉ-re$.F\I_kT=1cƨm5T&p% ?֭#{Z;fĒBԖ{Q-']@vϬרPHHH=/b@Z}HNN&\I]& $artt$HDׯ_7>cµ4o?O@X1rܙcU7hy뭷ܹӠoNLM5$Pʂ6$%Md}(6m]O<6.,͆N <p "ڄ94.[ wYB %Ġe.#/u#Hw@B~xn"Ojb@}u+eQMt4U]2oА!C͘pͶ 4LWKkN|Ć5k 0 =SnwI ^>v;B(d sPю*޽; h椽FH]ݝIT)D} W*=LӮٞs]Nuf$m@3GHx6 h~;.%Āwy! $G|'hll>}ںug xm҈@СC斣FӫJY}jE\E:>Lh޼yI.N;$dPUkbȹ}M#&I$eG`[Ҕ$ߟP~Rј1@={ZU}yyd̎,?;w:֭n͠MMND-_\reG-5,0 %J& $t+;3=+t4u:KLc^.7uT@qެȞ=RںeXl`Bc}0 vx#nd w}D2-^ WSne.!](%mvJ'5A. UL7Ի}5͏~) ccc7~ T5G~pye 5w@U~eGW$_Qls3g )Q{ lEpz J.W餯9AB`tE "BB!0m |5`9APKP[u(W."{ :HIaGyz!3>ypvYL~h21p@8um'׾^Fb,?+իW 5u{:SPP@~Ew)ؕK=[miT+7gGB |%.h @B}wb@Fk˖-[}힓#O~boߦ ܾ3\ z}Jƍ+5dBÈ;wƍ#7@*ڢdY}7tƎ9_z~W@a&c`n R*JDGGnj3L#} 1H nAՎ 0̺KKN @Z+A~RyXJKCm9Z{0r$kY={:1q#n\ AHM y~G[/=ʎ/  t 28p2U״ϭ BF./~H(+_\{mg=vB;s`S^^x,^; N>%' F@+ܵa|76ׇ\^z{=Z 1JoUp"СMѣ]#jhXQ,>ߡ>>Mn9蚺Zr6,p'ĀnKJPe,;Gfr>|8^xC4W{͚5X|9X4ϋǣšCw ǥy0>lQ:}uuu(.z  6lˁ=Eױ.((֕beתӦ-Y Pqv&LsxÕPZWшiZti`>S|8ۛ^$qA}}}8OO։~cǴ؜!Cpi%艣wbϋxng ^X 0Y%vl۶M?c&֭[ Y[[W\_}c={]WH=$bl۶л9R)ѢEdg4aɒW\! $Ts)[^JI +$-uDpp0h{;!f^ȋv/=+|OOOdBwB<$Ar9њ5DDo#$ItJeo~?;M;/\ znرDzȫUӿ=M +G-'h;SR:m}|W+W5D"_*$ ]݈;D"DtgB\vilJd .2eJc55H0U2#++ŋΑI"d'6' oQ(diiIޝPN)V|` QyzuiՅU6H v@'!!'4'ѷ߲P[l2@k֬iyaq١~襗ؙ?:0 mLHHύ׹"v,*pxc LٱcD"@VK!tLBa/$@@@_ucsyz{^حc)>ЇVw?!XfgHS$.My~鈓\@=V/"{;ϝ;uu7!TvLx\+4s&@3ѻRv;w.Çk͕ F[r*rhֱ-zu:gܪɠ TC:}BG~&!FpB@w^_5FF=3ڐ˂( Ð=9::ҙ3 =}&!o)PYYI @Æ 3ZOmM>{m-޽D/HDLmL)F)PS^GHrӕ>Ъ Uصkj+[ckXշCDʾ:u@mQʔtZ|؟!elvD?H)'@H/MBk2?'k2=ĦF-+Zob~" jY 6~NWaWI +ejeh?<5''G=O 믿NUUMn>HALуްarqqMLSdKsQZ =-DB4iRΗ5QDo߶Tgy$k9IEI$WT']`?C' dEjfd?QZZe 5JE(?Y&ܴ<=C=(I4hPS"w߱4jA|n<:Fl:|@/xb3cʐ0 ,88P dOkc6mڤvyƍM22Йi&o鴆TbF̤w}ƃPP*ʽ+꾪]jK{\v!~WU~eO6m"Zv-;yٲvӤls? 1o.|رc ݼPQ-x:wꕀh~ Cu!6K~wo?Nh̙T_J>o=hr:tŋ "/ DE}) 0gidFv_՝ZBW]tFAd9s(\۹-**ӧ4svӇ@{ѣj*9s&%%HFd_N+VQFP($daaAӦM82uF.]VЄmCT۰ѦMT)4^%ZuFF~Dl^?lS0 u:KmN֍W>mEFFޜtue<0a\Tۛ,--Ijƞ\!o1 r_vU΍ Ĵ_WG{7;|ݽ}!zU͛ΟװBxݦazg.0[)MhOZ N4Ɂzk[0<z/ m-KL{??3gjM!r,p WSWWv&cD7< < %a~ 1h53Ga/v<8+fF-w o\b,T=%(?RMB8Rb І5k`(j' ޾mJ:RD,c֭dBW믿q]vaҤIxh" PXX{eeeee(++Caa!2f@TS$ߣ`!xlHħD FnʦJFjy *Q E 娔KQP*҆RT5Vh(pce8 CãXa@~jk vJ}ͷ*ڡPدkd4mҎ 22Rww8lk>z#Iރ/,5h'g{mp|*5;vhe 00Rh?NzT ںܟu۩PwGiFvqH`|U}s캴w)]ҀxvkU w+RmR\A.iUj'( 7|!C p Vx )lwkZ99 8[/BsW]P:d"/D"D 0)+\ʹKpa>|EQ77"{ t+\\ ggdR)^}Uc:ȑ#F"77Wm=ӃpqNb'8;8Ch-DJOځrr7muVu3r|+]2'x9Ve(3nC)R *أн;C f qOk{粶rss!aeeV]+#=h.mm}11b"9-׶`a.գ!zE_@lCv$@zy: ~R<۪ {Wd\ `SK7a\XvVڲl \B/XBAr\ i4_}7unM۝XU H#7 49`r4pG5 O_#-(dJ M@m7m?S{ÇðĹ\ IDAT#t*l <-^3]0v-z#pJQ>>ȩɁg'l`T"A|D&C؈jR;Y!B>Rx[!]P +V4e-U tYg}#һ PUla,%{`8B#.BtbY1`ZEdw GQc1jL%TVVgL|h"Z Ro&~wapvv>sٳg㧍?3 9+S)A J0 JL Q?S@QyXF=^BG܇[ M> MR*RvS+w{YY?Ed$0x00lЫG1w\l޼Gرc;<`)ևtָmp$6O܌9`ժUxW_wѮ&M,Y`Hocǎaر3g6oތI;0}t > cuL Hax8Nϯ"##ӵx A?Ŏ;t*+m۶v ,Y.40r"9Qs5W٭6 E 4"K>=>v.u60- u׮A|CXС6 ۷(,hǽќGҏsaF88tp8{ޑ"T]P-ֺOsC >=)a&Fk39H{h7\g(&3Ћ-1p@"55'ѿS]V/jVB.#Pz%jkPz{KQXd.ɄokN-!yD׳ӧu`7pqa/yE`;3ۯ}c~z` S3iI`@, g\=r'+t2ЪxGVV&xqU4 yti/PLfCBB`aaף{ƒ%K}dlEpz N;!`ݪCX_yw<ԣ9doU5*}% 0x`ƌv;Ļwj©SXvvlmhicze`zW/835@ Z u\|"{ (%n;K]׍VBo޼I 辮@ ]/;~#B&!M "賵e~1c,,~u{de˖>5k^NEg=h>:}*u!C\4ãB20e PR #23P_e:ZB7cH$T1wMϞ4>!$+WRVVz3\F5݊E;3ȫZ3*b>~+0 Caei=%|N鍈L ѬYg4tH#銎fφ=yI _ .3n8@׮m1Yo6w޸5mz *. v=Pq{_F\B޴N33LBC 0YY bmY*5z|KV\ZZ׫BQ-R*" $% YH =$m̙w|>g9O37$j9l+-QU˗k-b!)):-+XnKnb\8{({a?A`ʀ4I'.94j;^'@4>ӚbNgV{)jXBN kHuYxd\Y,puk~X].6{BOst.((6fn/bᎸ;(v.ڳG] m`ԌfU~LBUW\?zsq:4qC:NtGTT._h~6Fv?OQV~2p@*T>-ܮwq_rV4Ɂ%K"ڪU_ B+vNMMmir:9z}A&̞@@jkh*oյw4^K> O IqSDCE4MtRZࡇu37nqe]]3pŧ ,ݭ]׳~M\\EEE47V?jZj[+LcA"~4}[h[ǘ c@|gk{o Ȁ7Ж[Z ~Wåd۱Z m=@dpC >wπ۷Cxϳ  $85:ߎY?0y/:~;mwor3fܹ$ 5P]Gf~&!׆h(**BI`Xr%zyClAr8J9BkcmU￀S8YsI:2i(h{Gw<-+:y[S 9= #4 ًpy И[hX,|m$BKNv8BȀ$:9 䓴Lr.K6d_ ;xƏg 3?gޭ1޶%dێtt 3>f|V[:zZ/D*??̟z M/R  {= k\EJ)SI4 Yۨ(BXWe۟wx͛1o/Zـ՟oP,ھӕ{}J7-Sy Sh1Wq68oZ׍/22Prssq:  |o Vm]ԩ>҆]Is6F|_?|8pyg+yiKL_ǻn~~J&[pC 8و8Nz#aza[7,߽STFBðG 8O_vP@u!4ϝ!rsᗿVKx6mŰ0m_\KH\u̟vAL*8W~s6?՛zuW8q;kxBEnF[7sв|Bv?OQ ѫG8<-Km>@TZeolj sڪyVɅ OLXX#8w=!>,tH_/}w:m !syײ uz;NOofƕn--->}bldu~"u^x1)));AB?)'xb "Q齻YDٱ,5 |PKahiZhlԖ-/{᷿4m&%\(vV,vAH@+>[kj߫Xc1_x[S2s9L ٸ_󕜮|0E-b|x~=[GO4a)Of@n,^#22???N8q6fTsKuuudgg ߥ!2|- mƫJUkix])))bȑ#nVV3n%}A}yb-Es][ 222 9_W~{+v]X,bp!h,kaEKc[nwu]0[,_l֭[Ǹqv ޜq.ūMu]M 1\hh㴍n?=hW/ڞK{~B99Zѣ 9No, cg ~~Xg۽?0} lH~٘=h_6|ГŪ:B>5=/B ^,2`ĀvA~'Oj{0݃v/()H o!l;F(ͯ5P=/2h_6| ^NB4_YQ"AVK~||<~~~ ];?Ÿ-Ă\(,7 78ó˨-#|f8^NyMV3y8?Oఎ l6#F̙3 VvF̊`驌1ؗ1Lf*##8{,Ƭ(XZ^_ _Eh/4b& xM1E[P&s8-F]ouĉ?ԟ!!DЗk{$''ÖV5l**@#8AVr:5d?EM?m+mJ.\@rrrOFa߽{ETFB~ׅ2uoez9"Ⱥ'~uƄo^o7r1ƎE{6| _hFHN9™ܧ]SjU=Na{Й.]7A/Kw?:bY9$'\cMNrʡB#xûNmHz7/+Ob% NmbGS\ۙ"\}— cٻ1z>z~:$oh#-@g蹛d%%%<RWWǻヒfʵd/4x6-k_KѪ".dm %kKpR֥ SRR£u)ѳK@@Zc4h]1lȎ=X֭[ǚ5k^#I~% 'yU2Ce3Lb2?RpINNsι&ɓykZ/ZTTDmm-Gnض| "@呖gÆ dddP]]͸qv} FxR""IDATץrHz!)nIRVVFee%#G4t;i=ao6=2"ENLLdǎ~͛޽{ywXfM㋊xٶm^_S=/?weڝ@}-@{>=@_^KMM[~l۶W_}*BqHHȩSDTTX`X`i}7Z4|F#{K} @ݑEgϞTΜys.^g8x?_u577_no_"_㦛n}u?~} tO>233cʔ)R:roeEv?OQ כ4IOO'!!#Gь5 Nzz:ϟ v3g.*@#~k׮.{ϳg Boba!En܌@ _h%8Əω'ݻwZk?ou%ܹ֑.})\TFBiz---ڵ ̙3M3YfaZٹs'---߾};9sاnEp8O~‚ 7o ^_.k.JJJ9sfl3fΜIIII[pw(?Bxbqv(zAAAޛ;wowشiϋ`,.^h[LA呖gÆ =z'Oz/{ygժUGCܹs?ٳl'(?_z%}v͚5KlڴI|Bk&w^aĠAĞ={7|?n7^[dٳE@@رc~_KOO|xWܹsLHsrrĒ%Kŋ=#۽o!~/;vO*aEee~FSQQ!vOS1vXŋ.6?RR0 =_mmHKKؿm߿_iiiV 7w-BC2,n1|֯_o455vZz+)))f+yǏ׿cVRbEB~6 cRRRxXpaٗ )*@#l$2lTFH3d ٨g'(?Q,T~!nFh/4(?ϐOf7P~f4g'(?Q 3d ٨2lTFB dvg6*@#Ay~23РM6Lrr2˖-3[̙3ӧSO!< Ʋb E¾ٳ_VkkD`*[5kV`̝;5kPQQ!xYfvZ֭[g}fN+qfkt֭[9y$~~~DDD5L/5%0`x 6l ?n}]0^͛GFFՌ7$1sLv;1d[nl۷hFEYY:8~8\s /xG#11l-i;@TTYYYfueNvv6wnW05`޽l޼5k֘`93m4󉏏7[V"00%KҁݻwSWWǮ]8{,wy'fkgd#rЗxgzK4Y0aʔ)G?' ))d+-NHH/J>}x7V}_OҎz~a"##innff+y BRTPP($EhB?+Vpinf+)._>z-,J ˨跜9s~놢_;0uT֯_c=FssJ ˨av B!)P(BPH B!)*@+ P(BPH B!)*@+ P(BPH B!)*@+  p31{IENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/ellippi.py0000644000175000017500000000040612253362407022707 0ustar georgeskgeorgesk# Elliptic integral Pi(n,z,m) for some different n, m f1 = lambda z: ellippi(0.9,z,0.9) f2 = lambda z: ellippi(0.5,z,0.5) f3 = lambda z: ellippi(-2,z,-0.9) f4 = lambda z: ellippi(-0.5,z,0.5) f5 = lambda z: ellippi(-1,z,0.5) plot([f1,f2,f3,f4,f5], [0,pi], [0,4]) sympy-0.7.4.1/doc/src/modules/mpmath/plots/spherharm44.png0000644000175000017500000012077412253362407023561 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxw\U߳33[HBBBJ  TE`o_ "AK* Hh-m|ޝLl25Mf{{9ϧ@2|JUTQE@2\ TQEUT5݀**2JUTQE*AWQEUT(]EUTQtUTQEJUTQE*AWQEUT(]EUTQtUTQEJUTQE*AWQEUT(]EUTQtUTQEJUTQE*AWQEUT(B݀*v$Iz{{顮`0HMM @`VECUH&$ z{{I&q:;;W("  ]EJU xX,F2DnWSS3}*vfT ෘӑ|8b>]HؙQ%*hH$/[~b1z{{9$*iWJUd2I,#~$Yfi)[U U"'!tmӶcuuu)gwʨt0TY{ >~vI$U]!*AW 2Ef$?v.$=omWQE%JUL&)bjrS RP%*RE8.$]]]q"P(~<&`05$o{!3WEEJ;1@w1ēL&H$B}}}%ɔRNd2=X(%/ V#T z'DȌB-Z#v0MMM})QYuuum0K{IU X.$e!sejkz{v[# K$I*TQ  _dF>ˆtuuhhh fN27% MO{bY{YEi N$@s2d2I$! D3 L]oH2]΋*AȊD"QXOdFofQFz`gmfGdQWE;-r5p8LccJT1dҨM"4x{zzS^յw|T zAȌRM:::BDf&ӓ _%'lvzICUH$L JPD"3 A%G:i[_(ʙd3ɱM67Q%!Ȍ%ʆD" Q 1dfJI?v eBSţIeJCDfd,9-0m`gпFX OpiPҵal _ U6=wvttT9z{{ꢶv@vdDU3)[_bWۣ gHVuA+$ioo' Z9 pOd_#ȈLI1vb>muuuV7F(]0b.5}j3q˾,;kjj)8J% r{$TCJG+ "3_9;`E2ՀΆldWW@E 62%$[J!BBv%P% A9jfrj3r6iD"%,OH|p6VT<*}'|v|@KiJ$UtXS<J8昫Fg.viFCPF4L5*5\o'lRRfVBKzLU z`ёr c.\ف@ 3GѲPdZL )?F:xU.-&lάB#&kl2vtm6` v S~>U'Ef޼<@kKi{~kBh`PJZRٜR}Jy+tȤIB ibs>xdC@(7(T Ur] C,lS)GU]$Ef'Tjk!(恳;hRL; tlEYI;_] #J#˥kW@ D1S&KW( ias"Bvϵ-5oGBc*%@K;[UB,w:϶Բ]3Y.hٙ9puƒ=6Xjkk: ;+ije]s i3?J'm؂`A;'r4e޼y0b|޼yrח|t (µP^3L.˜IB[I45YufD!wd vծT;>駟Χ?i/_ΤI[={6O'dΖRX@1Bf җV-J"[mVLD΀d2ɡ)'l2f͚ŲeRd}f.2.\… 9CҦ*A;2P ,88/`+hT ^NlyRQhgdgc]]]\S^t6tvi|gذaL8,[+l ;9AG6MS#555E9抁Yd⦋ϻ:Q,i'Ɋ HX$VƏ~d|w+St(S-B$0PqӱX,ܑSsJ]ɺXmה`O 6lPېIB3,xkYdJ6ߠ4 Y;7ؒ> CV~g I?)8vr`!h;::ttz@sIXb6-󆆆Thϻf𓶟vfVLNimmZb@ rQ*il6yجƝŐٖ#yRA A)ڦ&Awvv0hτ(innP˨ЂFPZX!Wr8V?tkGF6Ҷ~G2-dW#ۋwf!`ڟ1SNloJ}Lg.* ]76wgl r/wj=b+w.42#1fm D{Z@qE1*7-;7 3#[g# {!w-C|-ӏIߜT\!H@MO6p8w}$ԳOKYH !m+7*.wrVbhLǐ&X,:k92?XQrŸTrbG. [hHF@0B##6\J!\φ-D^DH9^ ӿoN9Ȟ6`I&m[-ZVvRfigٳaҟ*v,q~N/њ-V;\Vntʕ\qҦ;mȌv&L}GYDH$RrBY,|]#[dDIЕL L$,X[oe˖AĬY}3[ &p 7p'F7$Ld2*H$R^LXEdgi@OƜ\jxݓmll;h.D"1(i0SkZ J,9PUz&4+=׸w4EY?uQ|͜tI|_%KllF}QN;4 VX&h?1[Rv{{{ioohaq Z:AW,KSSSS?vwttPWW)~0|:i׍ʤI( s֋It 79spqm8~G}}=/r OCKGr~kuuubr[h4vXJ@3#E2 0PavE׿tTۛMr O=ܳO ls9KbBN0T[Z\P mElG ;iJ E{{{BIʍΜ9s U ŕDW'n Ԓ]A똴Q̎l`H匼(!JG%薖lMi ZS)3JR8 !S[WPC߭ @RB2F>7QW>kkkt)f]&mT*y]HH19;ޮO_̹ f #~Ur۞FLmYHb a%vƌӯk*ӑn]FѢ ib'\zva@\lwm=J'1PHߜ5W"r J]iكxՂ' .= -ȓd*PH+3G*d(#P(TQ 4G2vGGCMFLߟ/S1v> A! eLKKKtl?HCdEI;?;`E )y-@t(P )hTc1[[L HngkOzr"=&&tw:(wtCߙv1UpR$?QSSC$k-U/8PV9 )h߸ft6HdQ a5(yq:::RG~z.ೌ9񴴌et(STg&y[Kb(oe[mp82hl#~\ Pd{^벬X 6ښ|Vn`,_8tMZ$BP%>dQyY _Ԩ3umȿ{Xd ʹi_?L, hdɒsΗhiرy7|a]]`H$-6wնXP[[,<#is1lڴ />K.ddp{}뺊8 q<c.Hd,Ykl#B@bA rU3RYg]w={q$:;rm,]*#GxL1ɅDJ ״A éJq̦'l/r^{q饗ڒd7k֬m*^HNaAiY[[[P耳Ҧ@ oRC厙6AuFWr` p=`6p 3yG?*zpl2V<27iiً'--rgO?] goyiqws*w}_26m3 [n-k*ނ$l5:u,r\zx^GGGY}Wp^d2oa{kx뭵1@ XVDڀK'("1n@;]v%w,gImK0!X*Ū/FOp X@N +?AsfԈRڹ*٭^z  KHG]K2aSHr[yvmx 803__>/p6+nMeժ6#mhfWH_ww7t8+VlDr(X4 ādm4t+Zo}\zYbA^H;}S=r00$:GEO@,fQf͈""> x8 H?L{>+mm5L0%K^,{{ dgՁ$a@eơI'"Ž?JL bJa/?8JOf ,"P1$:dEdh@r[6`7ЋYo!Ux^ `)"ǀw"0p;"Z[y;ep5k֑,^ʵ#LCV~M(׻o^t"$&#PlEKK3s{+␳e媭QbT; se;/J1c֪PۄAdKlrsOOs7gd=7I$Wa&ER;8ҫy˖$}YZZưx>ڀ=s{Ioh}Cv}>3===edu,pR{{{FEEkA;2fOʍ0o4J9?qh(d9nE "iv 7g j]t)ׯ/`ܓ={rVo }hIDtgpy4"F;lA.6O+٨"p81k^3+namAG_쵭.G"HYAN!7D5Y 4 "ee"{P( ԩ{ں{HLH$1dYk\{C<&׎&]Fk@ltQ{$Fo|O hk!_.y؍v>xn3,dnb[_ nBVkEXܹ'qyqMdlٛQkB2>({p3_r<?QG/"H2 /aލ} IDAT7D^=tױix8餓܂޳\@ο{֫DSPRʋH={צݽ~È0wgT{_s=yGQ?IB#QP#'!B hGȩxM^Dχ'yH<6K.!p׳rg<>L&עibn%*R4D"$|:::hhh(7+ ˼/ZSO$˗[h_VDhh^$0u%xDIm4&D' uD[>|WϿw˜b^%CKh$SEVsIO#b;YKрoݱM훑n}~}1r4 m7?~&W^yeXY3]_Y' d?:﷈[<IrǜBa_BM袵;gD[T]]]`򡧧'u`yD<39Sr!0w>% ~|ӟ /dҤIOBC{:#_HK!"r- exNuyFI4պ~IS(7\EVѤs>sP5׿?G{-6 BU7 Pt"J_&Gy$3Lې@bHt>,&@`Qq1V@</\s} T,"R LU:,7Pc5޻ -~F :D*-Rv)" i\~70jnwHxDoDwhTd>ͬv$q#B B݀hhA 0ZZڪɈCy"½F1ĵ]t{?_ $1MvO#kc(lŝ^$Y_4#BI{GF2$ 1qDΝXR6FCB08h%ڢe,rYtt}oٲv;x|?T`gl(Jd^HO2|p:u*'NO_"ӟ3g_|1ƍ+-Ct. @fb Α)8S؜P.=?j"u] Agas~Gi>t@WWF 7(j!id=֢+{:K|,ҧFV\"f I ӑ`1GWĚi?p]sݝ|B$ن,7sZ]nv/绺 *4IeHKYe9nO|׷kԺhR:\ڝ$9>+44sPվsQxϖދV G>|˽VvݹmӃN/k^]hmKoreaMW~0cYTgHC1~Fpt鯝+jX&"6G)}Yfzơe0@K"uޭB*c h>iVT g"›%{h7" q 꿆,o#Ͷ?q}YVFiЛy,n :UXV w!9nBdy$"]];@o"RED1 @vۀ__um{jYwlniIwQ[':i{[H%z#4ον&מl[7<Mvr1 $݃&IKHIؘEٙct}|;ѣG =h |k/.˵ˍl@YL-g)޹krbH~8 Yt#r./j$H?BuDcѠFDw݃8ލ4uhpN@b YQ$›5ؓg=ș(ҡOAƑ}?ED{ "^w4uc6 v;JGi co;[M4sJo(Rc=p7{!i`_~ʍ[ sc+ó=EϢ hD!dV=؈Ҽ5";jj oq}k-n<$LO6˺KW_}uƝ+CM2Ê Y}1Y$@jWhh4Joo/ |r hF+h/`CId ݃bhOs}}5v"s+;3r,@Zܹد" FDixHx]WND (=ha " ҫG' Ay ft*j#GEV܊d5If{ߜ6"xѝ]k0DP=;W_|!d25\S%ڿG ;4/StH&DѬ5___*z L/CdŽ͈@߇;"~D߈HxnoDP4|D~39fsէ$],ҿ"m?&TMJ kkp_a4!xSr)".qƲxYY\_/u߈,JKGˈîMO@w"s3avm;9r!d}?)(:&&0ZV"gNFDPDbq'/H8 Yum x=3gΤȶ"`0X8?墋.*!Cݴt/pdk7]5G#2umE"4C Dw!s29,u7-{ R4p>$4G!=uDҧ ݈|z ЯB4Hs"=q vW6"}\X,〻L}]ߢyֹvP7s6Ɠ/CȲopLs gw7Ixdm[[&0^B$$M-wx$<u/A>s/tdžnI?b2*Dk5MrH3>_}cI h` -vd%/AY9ޏg aGqDFG#x>,?6&N$Y_s1t"G9'Iawdq@d=}vZ4ԻAP+UW9[[[b&Dr#[]EwS2azDV$jc@d4ގ&h%3j=^h"uヮT &{; -S.&gQg!\s1'(BV\O??V.Hpy xn{& b}Qj\?l.ܧ/E & x}}}Oӧ7*1d)hg4sF.=YыySڴ-ӛ{d=m['4`ybo;g--,@:"RhA۱H>须!kn fvi5h? O~vy뭷3f,"^smva5z9JMzjo8Y >莝gvՁmg{/קS/,5ȹk\酈|{d`r/^MoN [䬗\|}Ms|@`7/-lIb 938Ck#[%Gy }{quQn: A%Jks6W5twwr/<#pǐD@f+"@=UnF|T"k3^A{@2k"Mlj S"DfF=ȧl,4l=e"fUkA펍Z1ACV_d)6ǠU?/ "펙k^q9޽ڕDRS="Xb"mDĺ_;" BnZ9dv!?cl;n zl :F'6pu'9 Իm3~ Y˅-X͛73<Ç>!sW\q{,f{GuGqϧ~$ :~?C`ma 6i65gٳ;GQ.TQD2È,C@Z ?$d+Da?DD*QtG%lEKzq?-W"d5O܌g>lοۈM>=6\]?Y;Lit/ף p!"yvqm&[)MQv3)V"\~'d0MX }gf_v}r6Œ]~S|]98+H #YcMNsd7k֬>&NȓO> ҥK9#n9SYmH$EKz 3D۹kgq6Sxn˲;H4gR䂇Qw@$0"g $CXhpߊ>hзAkd-ZJA6k<$M.SPݟ ioDdhqjpm~uD~Evml­x?<pbiwaZz}fdmw; TחAg cwkld>i*|V#w;]^A]PdY}4~c+$"B gśeBS ^xݾxG~ ԰|=.seŸgfΜ>lN8>ODjUas\#FJ"w"e2՟8 ؊,sZR%/A'd-w]9AX4x_FVWbodGŋHqm{+"{۽zD\{B@FA> όPL3+kj;' D=Z+H~̝ O/Lnf{I$}b_%!CЅjqɤ{A_̝;Q.b_}7! r40dz2H܌SG k̉&x^l+r])w$^mtZkP4ɫ ~;*C# edmF`{%Ⱥ0DIw|o'Lΰʱ&}C8D[.P7q&PUaHn +ɵM=4Yx{\ߍBנ#x\?'6齮`7i-4]TKНEOlo Q2I P*AsRqa7'}T k$gBߟ?YT!kFHˮ#d zDID"Dx' AkY#lkK1(kkXhwxŔro+*{ϬX?LN cm2AH;`4=)Ǣ g_D"Bs>< AY8"{ŢT!| +oЅ|wy;nGE&{Ӎ{_wy1+9~P7&ۤkѠ * Bv )6lH$@㋩Ώ~t5-hY~+6n IDATPQ I݄Dݍʝ{x/tM+e49]fmED\xwfY ٫Z0O$ N$R[[[5j*8X::lM(HA#hEBVf"ڿFK MFdq(Fw 7M/ny<&^jD{nC]qm+ RĵFĖb-1hI.Z\\{~0"ch kE+#]9kt DvmEn<D*D }?ۑu:W9awAxVt6/"ⶨ7>+D&0P0Se;ˎTAׄPWWWPh؜e&I 7{I<(siS~ f46AHU#U< D&`-CGtd  \ r⮳ t<ʮ I-rZbC2@.+f=vҌg)wr/5NEQڲO7vxi<FNͮeTF4wogBHזixq͆uh} oK5z=oR]y~ hB=!ןaQKj" } Lu /sV)?rjBjFW z'螞>asI E>No3/F 9w]y{O6!Kx#w0Xn$쎷M+}[$m;˳]#|}-ְk]?V*h\dBϙk7DY]_w&M(}*"Ĝ/"k:x7%E47dTMuҟVn~|`˖-q7/Rn:;~66,\;{~ng %J?e# {mjw!$ dO@Gh~ OL ,,@u-Ơ|?E?\,m (bĸeʜ#$o + <FDO^hpX:w("y$[ӭ׻sֽ,z۝MTpN ߜm؀W9w׻\w6C$L ^ ~t JCfOI kȩV>a$r>NpmtÛg>dǐsy wsN<,3Zp @>=ۤ~;7r2sLM[t)W^y%_=~;|s̷mN8ᄲkjjٗPjI<@ 7 ؙFI&D";D^|1Vd5ـ"6pVwjOӽoZ.pö`2 ׬ޛMBxdw߫[[ r=EhY8G .DIdov6DW Bwvmy9ֹ` "!MDw"K{FOY[lD&7ݿ_B1V#WT,SK8wYoʱxVd~W+ AAM4 F~x{6x!HFGnBQR&ֽ|1µc!JDiCr=u-"LRax}qK.M_tiAB1irFMMMQ)ځ@ #p8V\^!T|o/?[q!BGY{QoE Ճ43`mkI:Ĵ+H|u(kiuOE{cQZ{z~mSeLMN<ÇsuVn&-Z~;.2:n-Z>9>貵qHiйvU)dΨ!UTTF>;} |#_,a4ށgeRd3L^0,&4j}|468d쯽L'mx!rxݵ~{_R"Cvn+Jdف"R2s5!h%`t+U/&D{sQݐُVw VGRhd]>!+9w;ooY{C &{ !ӄo POZ+_$sh32׺6M$6<|k_Jc(~.g/]|# CK Oh6͆X,52=.ێw"z߇WD֙d}E6DѠߌoQa$#[b)FfMUE=o s6ϿYFě3еgګ(mK;n+8h&mp!zH{>}oJc,ĐDP׺ADh0&>tV4t)6 xd/6>7RQnFgVgvA-VYQSo|o΅b gƍ ;EL@i+4ҴW鮱Yc9K-fw/c1r| YܽrYr^4Eh22=:cO|D$ )dG& .pg#`2 g뇕xUwZb߁@n0UkmM񶤐1h50k -|K3#U(n?[] ?Y xx߿Dds6gmZo6SqdER h)ɫI [Py!cDk(]EԺkt]6!gO Ckiϖn+$2JJ 9c}ŁQbdk݋+'MvWM>B\Mw?mfn=+od{#Y1~/lyۘ1cFs#P( m?)bUn*0:[盜L&inn.G*Vy0,K2gY hIlVmAX,F97!^j|-5/G#(DJEk,PHG99V#5D=t!5M4V PB#(BbX;dZEv?ңoru,qM 㐳,ӳT1刘qmrP?λZPRNB ɴK7W :2}揙s KCCӦսhDni -׿Y_L52r9DhOEh s6Z^%=ۅWbvD7ew97I{2"n}#ϚЦwߣb2kÐsu 4 ZN;#OPqDaJ7_Ƹk.v(Ӵ]yQ'<,eM[îf yk# m%;dIgr&.CE4kM^=y?ymQ(`~;. ꪫK*z˫!eAtP,|JAMM Gyw!Ahh0-F$כMg'CF =,k9kc9i=%Z8ϳC| |>Ep22i}|TYuӌЍlIwzN FcoQTZx8c?}#T;vEklٲ]VV/O?4+V`ƌSJh [)m3@M*Ce~V^;%\ݞ={Ϙ3gcN4ؗ`ǏvNb~FrssCU]BUHu7b"O-ӛϷ7Q }zbˏΪPiQhо /ЃT *DF=FQgh4滐L2!g#e޻C=vkB/9dNs߶/:; Q |r_/@&mňQܧWl청  gGx5*WF s:Q_?gWh cΝKSSSHnǎ,[s2tPfΜINN7tEv0J VFQ xhjj22pO"zu/GBb?(J̝%WWU/= IDAT#>s {Y#CY">1~ q~} eF^a78RvjgĻLRiWMW ev aRHl$I]$%юdpT;0t=&-~!YڸC g v2c,Zz]ge$21u&3Z&_$ܵ}VUus6@ņZ5V!;~*g^ٲE!hMl;PԼ)gbh0ʧ !5c[Qf̘AKK xI LNG Uxc8obL1zC݈nـ^*a2Sw.$3J8~IMA^7X_nTh0Jx*B;|q(zգ>]w0j}m/jǻQ;׵q(A 8ߴirɧ)zy {q{bG" &:^uvJyX]/L(~]j:'Rت&F n;8~cK̘_c6+SlA \#h吝-Tx)oFsZxWq윜K@ u5￿]x)oubGj*]2dYyTyq%@ۆ&ʣw\9ˉ3#A7WIBVmڸi?ňR$I:]ѽ02zPZeR(((MU5j+˜؎[zk_iR^d4K.)v$wij.BPI߾q"u{>;,#, =z7 ᄇ>PznM3C G׸j8~|5[nqGI?Oy 3H9MZCbʑPE2j)ge^/F˹{صk AGZE:Q~4v#b@tmd[&ē=}]^Uk?*5󐥿1v 1,A&!/Ggz^#1A.@_KH nly= HHփΤyIV TjkEkR܁ ,Q]Eo3bM;'!-5*GBN_C{lEj]%Cjq *gHE?$LʊK?N Y|9?{g!BѠz677VZmd*mDZ 2M4M>୷֭;S@ !lEeV;g KCmB3U*.y2ޤm{zmS̙]#71Xg ;"c-Q?x#F\yJyN5wQ@3]\o?°^A :s?]6psHQߴ㪕OE8_j9yF;Hg{ 2Đfi=FXU Kћ@̏O5FZ148 Xz45~zxA֭cF%t7@T{%"~v)N$ z5J ?GB+J*nC we {+ FLV޽((z'G߆#Z< $1ЗhyLZ3m f2crYgq_r2>݇pax?zϻ؍*"ҿ $K1#Ʀ ](:'RAT6z]ݻƟH"$Q;msc7o֮hģVlAh+ 6dr|<}ߍnjIj02qھ!HctFHO~lGbB v⵫FCYȾV;&}v>mWqJdά |GՋ/#uHimi/5J:rF}[@Z*QK|CRqw R ) q5bpr 5KBtĈTTzml=!cz-1 vf gU9~rĤ'$W_r饽ٰauuہ^I" ēʵ]})AbkIn8~>FN;،c6vzrwihPGV-4ѣG3f̘Ld :R:5/6G_."m!6 Xi3ꎔU߂5a4"0c} I}x;#Ʃ'#qVrϿ{݋9HAm7!dQMlAV}{ڛ)z.<xz-av˃}W_(((W^\x?4}wvͣ>… M8x F͕W^rt~?^m<>n@- b2JE@$ކ!i;,TItεR;I7*mC刑1̼yhii Cuuc5Z6oޜV=~kP8wa1nsdN*v<@ jm+x999IȒ׈~dD$|&p^;$<=N/HhsH/-Ǩ&Ncg}E~O]sLSR5Tէ>m =t9Z%Qۦ!C׿CcbN8YAFZ+ȾvSfS__ϭ|Qt"7h]byF:q!j,j|&\d|AoeZ^@ߢWōG ]rpf$71Q^oēS68r.6*&%]ԿI`^n>[p_A ?s$;Ad"xIm~_"7n7?d۶|mttE&|dq@O6JAA!'~5G:&Sc|acnI.;9dzRbٲeyw}A:#<sNr4DׄkVbW(g`kiЍIPJ#xb2JaTgiTU}I0YzCcoG#Z$g(-$i'˻IByP444ZE#FGlbĺ#v2Q\x DkcG $sU^;\P~Mn:H3f^6m4$yae'XP.h+t_vF)ABYA#2[1KzfbLVhcQ/O~,{PJc~d1bh# ok@8Շ4Jc|O#AAnNNG6)£<9/RQL׬gϞ Tƾ}iw px=rbQHhdC32(+7"3Ǝ-4!}ȸ&.cG_2ae0'*d_ 64xIE"Rx.u]Ș]whB<^URĚ#"p?ATz~1jiG~7jBy3fחӬלZ(45]-[4FG Gk !tDT]s ‹UPG:?L'kkk#++˖kf#l햴f4CmL[=r{6.RAjR@G3zo0ޛegITa\4~nF \}7}Cx$N|)?xFJ~InAH,PUm$?>Zז6R[×^Z̍7ޢ;1^$QRx/2< $[Ff oÇ+p@ kewtt1w;6PaxT1N1h;mm ;N766ck`QזPYysssƚAטb>1=D?ģJ hG@zѠ<dq3m|فJ\* S/z'2>x_~ 14Jg#gj$܂0~P Ϲb|oL>y+;F]eQX[;jMAbw!?"pD5n9!:>`,W\qbl*kVXI8h,MOոڳ## HImusBWoFѤLER笴#Q?F sկ~ƾ}xᅭB>A=hSa.C ׭H|}$6 ?EX>!j'LÙlˌ1Sx[Xyi#GH2 "O;Hb ]5CۮXòe{Žhv9%"﹓;e~qhZdHT1ΐ J.''Ș`ˣ)F6`a)!2iG1xG#:J?!Hǐ;Z$:GDnhwlZIfffOkg)k<*+L>ٌ2ܕ!]_@ 04=#!*y`?gu~Og!;q#1HO+bّ yvr2HImFNsaa =$`|֭^W#xf>|8k~ĸq_Gbɪ%ծICVi!+툗]P XoNSn¨*\n&?DZ3ld CFFyFQQK¤Ij) Uk$7_@ aXSƍc&R]C;ːDbs# fă擗u+,fz"n^2Nc٠r9,luH=ﯾ*v";;zzeݻws1-ZkƲeʼz/i甒t7Fh9,vq'fPh!}7شi? _O8WX؏Λ-x>xߒԱ# ĢFjVR /w /ߍqz"?!xb#Lb`/g}oxBATeHhG^|q!3gΌzjvqcS!U8x͌XOIIsJڱc˖-cL6~ֱ6*`WSYA1/Ikii5GT|F=JHmDz͑zn0id88+5^E3T3?}曯r=E]3X!;㴂=ڻfJ+/[ov[oŸq8c!7NES]H3VYKh!#u.R*`2 $ݰoKmlfh8qKjrIdEmTg!XSNL.sXhUW#% t"0KG,,\dP}#,ܐ 85[3[$Y^CqyESYQR"uvZ+D1by"cŲ)..v20 G{(͕s˶Sh #Q4/[AR@ClQ=6+u쎎[jvƱ&Zpb7`2nxͱ`_Z'ahHVv'Gf&L]_W4VI9cL *Ca;Ej[LV1EXsql`h(2&nN.c$j8&UžpƓ|p "K0d׮]ر3f$mHYm$G"27E XvʴB cHvC&%VrWܚ\ rsrw [ꚪw 'I_߾};Dh몫J9f n4꜕N:Y`fRe5N>Y v˿ Y"Wnt.T!++˕H$766rJ<ƍ9z(v_|q^/7|3EEE3Jo&;; :>d"eYN`W4)ny |"b Re~Kmx }\,M& a F%+K7J#B;)kT٥Ω;9A kEf:#0o0^935U=*#hlj"'N61Dq." ={v\נ. o>8 n3 ijjʗ6By>*$Jf{(>]M7U̠28kZqw+HHUV1tP&Nt 4={pgxweS^^׾6R6mdE}Bm%QXd,L0nr\UϋxRcM6 ų 6z*L$nTEqjUq>x ֭cʔ) z?G2|p ԩSسg>( ,H8n#e 42 F\{{-^)7<=YMjnSOjydc"dH8Uk4$[?R`5 i&:ĴiӒvS)mԹfJ D>Toa%)eM洂1ҠɠE^@ 槱.Ϲ3+l ##իWӷo_կ&e|A[Qbeĕ0{tqdK=xxF9Y VXql'dN7K6[})_y/B;cmhh`ڵ۷;w2~xˠAbӊBWSSC=Zl:;Hi!|^oA!E5V{`2J#dN0zNbÝ^v ¤x$7;+`d)|ĩ پ};wfƌ477j*rrrKcnK/͛7/DS={6ӟb?lHi:whu)aƇ1^~Nf'0fUb5;;;T'gnIC|1$c(f>;c$3`ŘqْHQ^^NNNf+>(t}>,)g! r3@'5h#fQ+#Jіvvv-*̐E!^*4f,PcUdzv"&n;! +;vիWs eXB0i$&Mĭ͛5jT tCQ`MfrXdIQsvHfcqNyDz1QxQ]]֭[>}z¡+ ]>}xWB/NJ%Y8% 2Jqq vLM܊%$wՙfp2nByJ8VI/v.SZ rZ=f@V/jv].*LiݠU6 w N ,uFIx1L^+W2j(FjHifs,̠&dz񼨝6K5kYf yG#=YqJh#:::x8p'O\ ]dcvvv͗4BVbI-:'FT3&ha; ;ؼ:ɳ[__ڵk袋0`@Rx:7Л6mbL2%YGG6l`䐗G߾})))q9zyzIٍZAM2*a %ܤY222BIi,q(Zeyv ;wdΝ̘1#͛ٹs'YYYѧOʢxbN;fugϩ MtG-xH:ĉ~٭f˖-!wtU`6&l۶m۶2{|z+a`+FfZ<΢ϙ2&l#Ѫ*d@bTFVZgl޼ٳgo;VVn6/_ή]TD@@ `Νl޼x3f CՐ Thvx)l" FD`"6GNnȌH4qw^6mĴib7O:wy~OE4ͮgO<رcя~Ğ={Xv-4 (.. * ZB3n ϔQe&cQc0GN&TTTz={vy,~s:~߅{w&|TF@w uuuTTT*ѣUUU\uU"_PIfD8ڱD8p[UJL*yFm' ٰafԩI 0)enhll^8a)xb,YByyyPXhL2B (--o߾`Ћhsf1jN sqxLD *Xf cرIcqĠwͱcXh˖-?AUUUx^ׅlܸݻpЋ@EEׯ'//|JJJ߿\/24F٪mhOLutt OT-DIQŋ7J#Z{ʕ;v#G0f̘ Xr YYY~y4w\nFFŵ^SZ$I!% tuu5v[g'N 52d|Ao{[nү_T0'dR̖6-Vx'aDuu5۶mc IsW]u?Oͥ-.x<<iݻw裏pB^{5:::’ O<G+Qػw/ 8|lܸjO0N sNtҠmv f:V U+ۗP_$R"cxvیdƴ͌Q)DGCd5rQ*v'fVZ9c+F!AG@%/^Ճ~ש[oQ&@ RS`b=zk!A%xNeaM2Gyv*4kjjbԩa̘4NN ΝKSSS(ٸc-[Ɲw}G0СC<# :4꾬>o<9ŋÚkv6??Ġu>'nڗU]dO4N@jUp>򨯯{EEEq!Z~o ,pUiT؍g? 63ft(u|$##{a~b](3X%{A:*@ 4dp!ckk+V[n԰rJ =h?v|*m]ivijg}vRij3x`@ =$UVVFϞ=mh`'I6Q(DP:tuqӣG_%v>iv Ng@{1^xᅤ ED`YYY۪iV]fuƅ,Qj6["̖`0Ȗ-[8pgv<>!$*=w\̙3ᦛnbܸq]=ܸ 9rׯ< ֭cʔ).#c'777 G3";pHHիWӳgOן)y,X_|ӟ4L 6)TJtttCz袋ϧ窇ke]8E2*͊QNGz֮]ĉO a4C:đ"Tz+Wdĉ\qώ?2*/.gF9sC#J8FU:::BJv&Eܵk3gLjd]< q*$hʊ>onnfTUUK~~>  5X$ʈJ8*h}VcɬY:4:i}tI 紴PQQƍOPSXş:jSvẊ~Hqq1uuu\tE 2$iLkANM[[۷\:::8~8SL"TOŅqL#$~|԰qF<O?tNgRLu:"mO#$E2oK[[!''P[*wX~=\~ŞRtC:q!/`0Ȕ)SBފ:ؼy Peee YA*b81؉NZZZXj#F`ȑa5StC@FHfNjd";;;d kٶm~i(F @l+Ԑh0xۄ8pJ⊸Z> T4B;^t52335jFĀܹUVK^(++@ ߿\RV۬UXGGG) 6lȑ#̚5U_NDs/g̙u])_LHǠO#*mٵkWOwfӦMx^9r$#GuzՋPp4 *=V[[Wf/iF\x饗ų͛<`ԩ',O`ɒ%~>}8>Tj~9!4U<{ԩqL>{wvp]A0 ;*--o߾(--+5k )))k2b h_8 s t]cee%O=T3<#= _uѽ{w:,JKK߿Į]ؾ};3fpܞˊߜyXr%|AXcGLs6it9b%j?M6͛ǙgC<()))~/ɓ]c.͏Oucit9b%}]FyRgx Tѽ{RS[ @j*=\ ꘢͚qr"AxgΜ9ۿ[ҥKٰa=PRYYM# #:.U±kYf yG(..ᦑF!mӰ?nstR׫܀Uұz@ @ff& .⑦Ft<=8o~Ln{8ڷh"=޽{(++cѢE<QO%>#R\\r_c_3flٲWUUzbO!^ObIDAT t3a^z%~_zHPU U&MbٲeybLTWW3u԰W^y%L8񴶶s+|>Z[[YxqJumOYx1}᭷(:4*p7i h})Mo)I#H4҈hU/"٬X"I#)HǠH# [ne\r%]94Nq tiDUwߥgQ^^ޕLE:đFQMVh?,XGƩN#48IqFiH4H#iFiq"mH#4NR\,/IENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/coulombf.py0000644000175000017500000000041512253362407023057 0ustar georgeskgeorgesk# Regular Coulomb wave functions -- equivalent to figure 14.3 in A&S F1 = lambda x: coulombf(0,0,x) F2 = lambda x: coulombf(0,1,x) F3 = lambda x: coulombf(0,5,x) F4 = lambda x: coulombf(0,10,x) F5 = lambda x: coulombf(0,x/2,x) plot([F1,F2,F3,F4,F5], [0,25], [-1.2,1.6])sympy-0.7.4.1/doc/src/modules/mpmath/plots/hankel2.png0000644000175000017500000006473412253362407022747 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxwxTEl:$R}`o JQA@Qب"EPi~A"U J@BB iw;ggdDt@GHHHHH |Oc 0G$$$$$jG .%$$$\)@KHHH(RpQ-!!!HZBBBE"h E .%$$$\)@KHHH(RpQ-!!!HZBBBE"h E .%$$$\)@KHHH(RpQ-!!!('Obǎ֭fϞ SNB` SDШ='x7n͛qi3gΠGXv-t3g4H F%86oތ^z{FF;wFzzzcIh4uVXOOOcAAA G1+ӭ[7T֭ }vBii)|}}P(PXX-Z~-fgCѳ' ~Ǯ\A9c`Дk^WVV"rHg2닀D$tUt8:ki4n% QQQNBDWߨ{4m4zwiL ɓiƌ4eʔ DDDjj4D `k֬iWDjU7ڇh1~Zmܸr>>>hjmhT. 8І D]cԣ)9Ѝ#Pū1Ga-Eۑ y ݕp1hk!";pˁ{h^o7W`pXf ~(zpW[2" b 'Nį2DgA!W@&9MA'@yM&5qj\-pك 8xІ ,&L**DfdљEThPTk\)HOhݎWJ4,[P*>h̆ acdٵOKKC^nyRu;^[+eMmgKJP )ItmFyXjq㎾i++V,rZ/6^[+e.,,Db"AaC@B1ZLĈ#̎GFF:M?D7ڊZu;^[+!&_&wsZ|-.v%$xb =h^7{*(,xXDtmY|v1[0qyRtZU;ܚ^^I@6TТuGqIۉo|>jU7vW?܀ @(`.@U[u22ϱ&1gܴ, E#cuhuaU;\B'|cc%7oM:(njf{4syF-U;I`tnDV}vj,`4iJH.{zm0s&eC%/3p.qp<846,DA{(lW] nMBx)SOwg'£lW] .{ƌS[!dƊBB~HܼI)I䃓_8?ljU7vWm4 ai5m2D4]ѮI;xD5O} ݾ~#gӇWJH&!`0 ǍKp6,BkkNt:d΀Gktxڕ$I|16Vt3 CCW AS[TD-Gmvųëv^u|kw% FK/_'~ x 辣ݯG97*V@i߂g^[+e6>>P\lcED-vv A!n(@bahM^[+e699Y_XLtL"Zpws[8jU7vW]$jU7vWtFF&MxxJJ ӧcf窛ekv `:;+if%0{ltr` r ֵͳëv^u|kw%=@bӦMfd2ۛgcڮ]Y~E;.WYQ 8kbޱW;l(E³ëv^u|kw%\vwNA3瞃/_I}yS vT0* LLwh;uQPiT*|ޑW]FA7\.?tIIIعs'ߏ={`lbMα*6֊cw""Zq,-- Qע6;FDqV=rS9ӧ]F5{\O˕+W~,_p%}GJ>Gs= ((ضm tٳgܹAy95mohv}v~e, Jq`]cxu23_3~%KA@"##1p@?zAlkΡmމ92 fXa#jU7vW-i('Rŕ#NHci1'd^5^Jd@MExW] ;Mˆ`D;&4VZ-vVT71.ͺ`ۈmx37_7d5/[zajU7vW]}%!leg͛xz%%vVV;WX]κ)MŌi>7ZE x^ƫv^u|kw% W["Ç3`WXuh+^tH[ݖuWg/*0^[+e$mۀ$*9_jR}U+6dg9PF)J4.5ZWW]I ̛'bvGѧi݇&MʧX]賻d\U;\4 7bߟu\;͓bx(VC AC.6[Y(<\Xky6}xΫnoN>غhLzd&ɓ"TfNmɊ +0|Th+Do!@h7Z<עlW] .84 #. Dkwf| 38 /Ch@kjU;\4 6n݁+jNLQ;ס3ضg=/=ͳëv^u|kw%=-8e|\sGFpl[nD&<Ü* ٔTB߀]ILv.]Eu'}[ENEֆuz"(>QIq³ëv^u|kw% еAA-bK!!"UZyV (C>$U;\LB D< "R'_<ĢDڳ t5b'ƢP1~M^[+e$ӧٲot>@n(Ug:oҖ(}7_x6}xΫnod6Į]06 h~DLetr-橖hl$Pj3 q1=3hByCL& +1祉b* P+Ͷfy6}xΫno6z 7s޼!ͷnK̓G`66YF&S?ux6}xΫno68u w|6ѺUYbt k90#x e,A XAU;lW] .t]&LMڽTR$8Frt_^]Pzr̳ëv^u|kw% w5"*8Hn.0kma{vt3^*+>WJp2 8QyE6#8خoyZxyh;-Z(k^ +^u|kw% u0~<3 !ܹ@Yûԇ5+}_AJI ZHLyH41No_,x5x ݕ2+N]&!(;~ /^/)lZd]I 6i-^h֯A{Ϋaūno6miтe uKhmiA5OF/?4mijyvE{"VJp2 xY6\lhxjC`35OnH)I+_k6i/nM0l0/Nhۆ'p[RXx]bm÷N:}B-^zjs&2Ve8}{հU7vW]I l$ʺiW][owox*<",=b0C,A^ +^u|kw% |=s{/Ѱj\_T^z퍖ZBEKVx5x ݕ2@7dzyÇ;PDp0c7m'2 Ws9kԭWnP3WÊW]Fedd`ܹ¥KLAԩSP(`0ar<orDΘ1KV:XT^fM,鉔= D_)Iy5x ݕht`` 6mT#9s=zڵkѥK9s|C&!,PY)ds h`ȥ:'C&Xzn).f\.k[P vRzJVJGFF;wFzzyR9s؆'#GMcF ݤ,\(l0OпM<uYJunnh3 Zľ A%aūnoK蠠 RRRdv;g޽@BB[Hu]UXjL,6u;v ѣ,vm{/"25K-uVn?. >1aqbͱ;1WWؕ+W~,_,{@DWP*4uTԩM6֯_O$M>D_߰hf|#"l"4J$}}k*,OwLۛ 5w6&W [= j6Dܻ7pF$rag透>ta;`ʋ+oO#З葷# HH'Xblgl]D/=`۶e3<TJAgY6OE=ҭPj=: G^ +^u|kw% $42ilʝXü̧soDDX!*08rxzEm 5 11w]t+趲ܚr ͫaūnoFJ.LfXZ.gK#jŠNLouiHwMH_^k x ݕ2@7Ј)ϙcY2z5Pϙ0rPQQO>,:tq +LC棩gS,87rNr^in'je󺪍W] .tC+ d,$$X^䓪cDl`׮,f;WiG{޶=Xٓ͠/#z\44ux ݕ2@[jl1NJ6e˫sC e;pyro{10wE2O߻}QR CeIjX[+e$CW^1&M^QQɓyNrtL9& $,= gs r'V4UO`_^{jX[+e$4l6 Š>}],# ~;kTgjaL0 ~~z>[:mtPSt_Z@jX[+e$4wv.F~c bcצy۷/va^{ދ{ Ҁ$G5WÊW] .5&!|1 ?-vZ _(AU-C,}΢}h.Cdv͉5}d2> ܀[ނysy5x ݕgߢjXklc,G;'mR^fYgW>޽{;;o,<cҀIV! pf ŧ!ֿaūno=hkMB-[iS6灏 ¼!rI牸`n\<)@OkehrOTl΁߃~48WÊW] .& jrC` ƍȶi$־AAteveq9M77 p߉*qcVJp5 R6m*–73c!~"ԗAZdt(הվPUBUO$0}d2Ez }QV ]8 këaūnoZH," 4?Gǟ3Ɔv掙΄RĔLS`G>-P* Zq8հU7vWmI@cz1u}xuw!rLO.Ef]'zyuѦgO\S/\h[LB>mV,Bcch+ǭrwlWLiUqhG;t:49<{hiQVJpm5 cӧc &ukx\~;oXOޜ`+1h0nZ;h֯PM >XϫaūnoV`@Mk j'*`S.dccI.M1sLxylY46}赩 n͸u=i^ +^u|kw% жQ W' IDAT|p,y2iaJ11,H\mC:|y7[ݺL& eݼQxh{^ +^u|kw% ж{Ec%'C <@;P^Elo˟^^GfQ+rxK |BnyWÊW] .&>}Ge 0Z5 0 {2֭?0:uv܅L:_H5|{[odH" 'E+y5x ݕ2@cٸHO7 :z͓$rW/gz͓>}sh( Է7T*嗁Gmsj( ,Bh|TC&+Uh=5Zlͩ \h{L,Xr_]\'']ׁo:$Ç7Xv1cXÇ1(prrJ< :} 2 iߤa#j&ZհU7vgL0Az4Qq  є)De:t(Q@џ6P6<5#-2? T;tŦS =(uǶ$=HᏵ@DD)}U:;54$ N+=蔔lڴ Wƙ3gh{MB#}lu}ؖy6c~Ftd2~XUeϞe֭ntu'[Ì?5tWÊW] {ѬYhtQZr%%wHMo"R5W^!*/7?>vyO٪sZ-QvyXL(7Χ'"t2״kcoTqH$ zvYdK.~Jɓf͚aɒ%X^ѻwo>RbF^x(-.\zHjDR]z!btuzCdzw& _0r5swg[&Z'# /`W.3gXoG*P*Yzp`0ysQUZ*W@@+7-֮Xu֭CqqXdGEEڷo_X&! x=-$|9y3ahoZ~<=S2=\ƕJKX'\/f{ jU٠ :55MrCw<ܹǞ={AaX֗XddL`*Dv <$bcbʎ9瞃CgzHDD ۷oou3̈́W._{6;bZ FdVUe ٥ f b/_X:kagM}uw Aa+=0^* ?_[%}fMSs눸kб7Jκ_\ؕ+W~,_p)ivSN%JE4j(*J;v,;4k,Z~=Shh(M2&MD .Q*UZƃVj>>o;\ƪbFr`@a3*7>mT6em>߲J.]d2wϟ_rݻ 瑎\y=<F kӯgtm6~u(o+WdBg,ػǢ]vhפ[zpASY8\yߦBX `ztdv^xsH*NBrI2<>Lxw/{nk]`8tB&Ώ3 ~~ߦ?n}7jy震~8s83X_ֽ?= R=2Vf+Aᯀ\!\&G!x͗݇//N~M"xl0`84h5P+jd+ei(^]ss0( :~:Bk'r~Eۑ{p?4ڌ%Bשpov&X$I(v萐ƍxwDVb$ O5> ?bg qU,QǧN+W71"22 e h Z4f_~)%)A`#x3dk8^{eܰkְFvi`h튦죠WΐdH8] V 5029z6وDjE\Z? ty}ˁp#z7 f ꍔyrxK@FU(F޲j܀)ߛ?W Gyx9|/|/@v q)%%81` JKRI4>Q^^"!_P@tL/;t裏l%hpy\IsN1;n:rR%ȇDDmN4`сM* =ͦu;< ,=y귩ӿyVPβc47rw䚕C(J=eoΦ)^z|抨8JOR7jC\&K7n.+#9h+ bԱ#6֗)jÉk=ṳitW i%i4f]g]YeY*ISӨ]l՗HivkHDA uF@ 'ε>gv$bߊUT\@< %%ҳK BSBvCETeoΦO ɠ6/ /3:]q-@ ":ƸcU[X5xPvffZ%v$uUĐA04,k]t,=N&$^kQ/ЙʯfS,BX2#ݻ7̞kP=>a=6eӥ~昛Mqqd[zu+E\Y#PRQ;qt% Is(T*PJ7l$j&au=dS,{w`R{e5Od !00`t5aaĉܹUݫW/l7 3ݟ1?{|e>ZlʚLƒ<]s9ujqi"vӏO)<31_+AlvT#))FDD+/JAA[od=z-""t/-f/\I!S+QTQfQ3){n{'N,kCsCRVYm8EƯ]"mv-/'>Ӊt:fvl$vl\v&;v-QQ*u4[^9b%am̞6oNzN|mWСL,A;l}Yip <0O8MwNx(!de"4 |[!(<ie``&ND8<0ohcdsq؃vIX$ "bU?̖ڀWX]ƺ*G Zzv)/{2yy:{ƣu @NR{z RѪ1113!BBcPړIYIR%(s]J,k\L_ڪ+VE[ndA ڸnѶmLXHqY6kGe>In)y~2imlTyر7.ez}v*Kz=h.. Wҵo;=G2y2MH@n6|-?϶@ti[)הDD(tE՜bDէRg( "4;ѐ^gQU: aV3$_"~!^֚;c?th>1b#lDl!r~ϡ#<ԙ YIyy҄f7JOD)@3LBJO'z)a W4pIxA(,-cq}zSʭ{{VgN#"y7):>rw- "" 8KQ({s846 R(c?Әwѩ"Ƨ-4rH͗惂ロliiDǎ|:1ƙ/[*(᢮BJq O]J:z ̜IӑAw]t4˄Zl'S|~iEf++-=JzT-bơ:KM1c:KmD,bchf_%%f_lafUVMȎ8QTQ~jec\-@K&amd,'߂w̨PY&ᑄ#:IIx$$yҫV䓧uGEOcxh25ޔ }y{nG[>~fe:{A9VO?:Μ9727d2:/ "Bj'P=RZ¡ / [N:#OgQ=FI_} v`石cO<K{]\}6tN f9J=hE>wr6AOӤHcZ\YLsN͡[jJJb LeY'z]Ѻ5QdsL¼yt%S)yTe)~TnK8Vo]G=a;8tMi+Ҩlg?# o|뜺hV`?1ol0[:T֗ژE_+gWAsEeej&TBIIi;vRЊ ߭ <ԾAA@$(> 5_V{4Tޠ2;fMDV{aJ벐䯓H yx&3 QsY*A z=v̪T@8yO()!h$ .Ёꡬe۶&1۪O"":0i-ֻl```s [J>.hpir J}Bc3k1b5OMejgE§ Ր(s}UۘӠt`,ͦ b,SZ9žKsWm2-^ď?&,OtAAOo||5% :q\X5sk-8!m9 `~J0JYB<˗KZJ7©SD|b[֦asjio9g7ÔJ%u8}4A(x[yu~v3*,&AGmn3Dm۲?ro;]AJKJ**Eq@3rJ c"/y2F_jql޸AԧOC mO'BzuYzuzi58D租،~F99DR~*nI|^|eJ$ֆܷt( ۝Y 4ȁW24Rg)::FF^k9sh^y9 w^NJk3WؓP[3oWSU9f-6]Mه7P^&17\9kmyJϫfBEoʋ7\s_knT-;Ot^4%*YZM`ŋ V$t%:*<SRIK B-ODttϖxcbkzuYG=&{r6a˾dbZr{TE,Cҥ,ᑝ< X:22z|+3O`| 2v,0|8 0.!`$p__`(;^}^Cw£Gm;N7n`HYd{,AU /`{hLs6WZ*)2@Dæu?K4}#OK< ݯ`ӕMVk! WPqU7p9xTomjAGDԻ_]JեvsY2Q@3MBMcyw((cɉ eIr\W6XNʞ:vt\;H8EZ2h 0.+csu:6__e}<8Zs4zlTz*]:ſOaalլ ^mI{7CHd$߳yט&t%CYHKE'(d egO|1GyxX3O܈R4m-KSN!"2n&Ff\.\INGTp#kH1ad?&!U7d?S 4bDզ{V "PIR|@UCTV%OcG> IDAT-Iӗ'4{؃4a9F)kTl7wqd z DL:?O~C nXS=s'_U6]qNc9Iv$ڱߎ7&ovܹF7 "u(D,YƬYyU*;ЮFNgMS_+NR#r=E ]ٔnv>s]&|&H7=!z%6lSȊ 46dIUNN4YChH0tdPȠ6Ԙ,*_F͖6[n_HD駦qR[߾=T,,)@@+ ?'Y@qJ&YgA^e:^ӗfIN:M+ԙjݙK) S(jtSmO=AΚ#ǿ_OMuqKJd 9Q))RVYie]<: Dչ!jmse1mִ (iNóWeA%aii6!? *:UDRqm  ^~j>OD!oVŕJDD/w#u%Pq*Q^ff(%¼J%LGzRulwiӀ1q\cj5QrbaU|PD#G=n3gR1ܐЅ'sӰ?WѰ13u 2dVmM,̼y/ t56oL;o8uTJECHH}{1zjVnD]A*ede2}y7>fE_4O9j(j7uxªػ`ܮObZy5+_mZۻxuBAB軻/t:* ew;թ~ͷ؎eqO{,矁GΟ_~5J% ܼ '|Qz!55j Տ ((Ȭ\ee%};CuLj2LMׯEmPC@DD.銸8}ҀHh25_f녫9WQVhY}ӧCUXL?`džg%z`̘H$%ir ߏ˗.5رcwޡYf)88Aɓ'ӌ3j+ݨcw +X]UCPQ &9HAkJʭ%`UVR'rb%Kj£ll8RcCJ Zy5x!~ߞ0bUV)tt)&Lp> fZliTVN7ڈs+`g{$W/h^ffZ$/3EC7e4X~ڷ}Ka?թ-̹nnە>zԞYP4o,Oû][? ]W=p]UCR(l3o7dqX # /(Zdad@QQF-ZGd ZVR}tMڦ&!--M&<OInm'Jb-r*!;ۑ˙ ɹɌ8A\xp! |Gd28t aWgo4ޝoRfb4-e6^7lHA#z=]{5!O> ~^Zj4i^y-,E+EdN:$xG󥤤/rHHl#c';wB.M'>e9\Zs^RL(n! 08xP}Rܢ3zUQPޮrŶ˓-9r?uȥKu֜θ{/OG37<]H|<:Jq])+-& O&:ʯ?Nٿ>X^ԩNRkSg’B\ғga{%ͶHil7s8a,nopyTIIȿlْ\f/֗W{L !6\9^ O`Io$O:%Fq[y齟yoܸAi}jJ_o|P5ћqwz[Bѣv"#mۼz=`qVTTa"!+ 2x Ls^z^LL}b*4j1 V[e@n.97>f߇ #2?=~"zGۅצ6 : o%`#ÐWvM I@[,?HL& O?mEգ| S8ss-mV [>XRg!~&2}:>}HI_7?=3QQs"ws(=T7*$ d6J{!{D4[g!jy12G<ЗBn2ug_OID FAIA_PB Qª@$ZSUm۶9RlWB- &qJUomx$S[U(f琤9\V́]N Cg%`>,SyZ݀Yfu;Ot\Rhڰy3ylL`NwC<ʵbټ9wϛ^ؑ-fM i^S_Hۚ&HbA}{_LݛZ4MQxWN]5U}қm@$[z@ +[,:ȟ.J[S? 9ĵfBB團4sLIȷ&4 D?%Kh6aIEE/ϱ][&ȣN HdQJ54ιIl8!ʈ-W $[-*)q}ܲ04}ѣ_BYvh/5J49.OgRN-r8٘=[\i\db"4 ;u]db7i xL!6CBŪ!) 6j47}<(xMja—Pn)[VY9Omۚ9}cm]4Mlbaa!L W/p 7C"8mZՏ?^ xoٮ#]nܸs_ bjOV;MrFkO5CS$~R}h)8d$QUU̜)vj ~;0z}(1k3/33gI ˖L;  "a–TѢEML0 hܠ1RSSb|`yr<}ZQ;=Wz۷͵$X_̽3IvNcfaPp#KEmfx=kf9,R93<EșP4dtMm J 앜‡RL=mCz-R1HXMFUOV[g\:&|92,Y{e+z\4ږ;i7 pe$ @tjfE7vZ ;?/"+VZV:}i:"22%&X ,︰,()<SW~YYբv z;uEz:Y#2א$tyyСv_Sr=+kϞ-ۤ0ض6o-EE f72fR"P!ffV~ӧ$ȕ+ź~LG "k$g[47k2.\Ǜ(r9RL&Z9&[;)s^kkQWyRN7@|# F08.4ED\t)32ȣ) ќF7jѢJ>/E㶄m$=Sp,INB4O!I ?˼^紅bMoEJ}+ "-M]#I h)]- s߱c@ںFAPa42(.mF1|. CFNVL\(+۸|x9zӚP9fČ̷?w^tAݐp8MsڷS_4}1@fƢ_p4i ,] e2 /B@g@wa#M>YԼT\8y +O cc*Щl C>fAаСw(0nk iF`H|?tX)7^zP@\B wзZ2Nv߷m,] Cnoi KHjw4f0Ad4^s2MY_o3Nbaq1- o60i3ɋnq5+a];WsYu–v<={云$'͞$Fi;$̣ja^qM$mv{ꢷfj7Ӆj,SF݄P{bO]D9wIrTd4'sNQcx-mڈ&O>Y"lVS #ZT$<ɴ?Ԃɏ>LJ42Bde_dmz+brYl@ (-^}hXش}Qk =z4pX?]ޭc  ?p>!N8&gAp*4χȌxY6>ߴD8G!^o6^|4wpo_>ۢᝬ9 I:F)AߔbWy3-EE-۴;&5ML E@, ­ (Z/Wt[}.QݽGTi&r ZIDAT9\]Ȫ[:)) yyy /5k8߬Y3X,jժ±*H:0|!!bm.aF`ˆ.u Ѿ=n=i*d ȭ]Ox@'&&bNBCCѱcG=܃Tcvލ`f(7ZV$-6̙bz 0tX '%bsf3#nHX4`,ֻ7<7vcTyMU7 v=ݹsgDEE9{ב @mƺ SL"[k&I7Zlk=z#^{ 8r0 6{6~3wbmJq@TX۸QEG"־RǕhXk ظ4||Lڸq@q8~uq]]ҍWFk񈌌ٳW,)Sp6lM&=>YYY9r$Ǐ#F\^U=VL+E=ߜ+V8FB1*ڵk-w?-4{圗GVݤa =1j(X2niNvYM-[;wO>) EZrs~ȪB.f[LSABS^w׮g88}reBfzBJGV݀e ȭ]OHi-%[B6d ȫ]V݀ZUzYujU7 v=!VGV݀e ȭ]OHiUȪW ) zYujU7 v=!VAB#n@^֮'4*Hyd ȫ]V݀Z =y˪[@ U7 vYurkRh$<.n@nzBJGV݀e ȭ]OHiUȪW ) zYujU7 v=!VAB#n@^֮'4*Hyd ȫ]V݀Z =y˪[@ U7 vYurkRh$<.n@nzBJGV݀e ȭ]OHiUȪW ) zYujU7 v=!VAB#n@^֮'4*Hyd ȫ]V݀Z =y˪[@ U7 vYurkRh$<.n@nzBJGV݀e ȭ]OHiUȪW  &DFFFΝ1cW^Aff}ɞvý-FȪWyy[^5Ёx+#::!!!}IY3gΜ9,X[DB^5Юb20f4k wqkoKiiiqAoq;v`ժUX,駟z[RHHH%KPRR: ے\b`߿?>#o˩v{ O< mIՆ$>m۶$O4h#Mel2)+ ~icصkTݻcXlbbb-ef͚_~2\^zh޼9Ѿ}{oqpowm9v2iiiСu/z[KDDD`3wF˖-x[K[liӦy[K9s={D`` ;DoK ر=zCE0gL&L8R\⥗^BTTbcc>ݺu+N8@lܸϟ$hԨՓʴcǎ zzY@:رcѢE 4m׿-ZL&L<QQQx1o|8.\(u-ŭ2Њt4o\MPH2ЊKwXb&O,4ŭJS( v BSV( BPe B(P(:EhB)@+ NQZP(t2 BSV( BPe B?umfIENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/besselj.py0000644000175000017500000000030312253362407022674 0ustar georgeskgeorgesk# Bessel function J_n(x) on the real line for n=0,1,2,3 j0 = lambda x: besselj(0,x) j1 = lambda x: besselj(1,x) j2 = lambda x: besselj(2,x) j3 = lambda x: besselj(3,x) plot([j0,j1,j2,j3],[0,14]) sympy-0.7.4.1/doc/src/modules/mpmath/plots/besselj.png0000644000175000017500000005577512253362407023057 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxw|S?ʦlZ%R*"!CpARʒ-#ҖQ:hP(m8$iiM{y^ɽ|r<9yIh\x \*5оظqc- oVB֭Ty$O@Q\\kז;wFam8rX(-wv:tWRZ oo殺aOYNi$tsVF~wH#gmj86Ilp:Q>YNiMezx7U.rϖ#gmj86IX&mO W=[\R&aϞ̙Z  h$>rwp}⑳6s5@[Sn>{زDAOYNim)7:o,\ ue wG'9k>W) -F[>mq̚@Qe#\֞}ԫYRR$ rwp}⑳6s5@ړ~}`@a!\x \ 4bz~1 Y9@T#gmj8ӓysw`F*!\x \ 4b{N.]ʶ;!\x \ 4ж: ut "+GpJm,_|:2qEO8}ZQe#\'aYƎe"wG'9k>W) NBÆժ J2$;B>YNiu_xu 7WB;B>YNipp6!\x \ 4R8 uЬp*͘rwp}⑳6s5@K$XQA.&ZOYNit:ݵm;B>YNithْms\\hXrwp}⑳6s5@K$쯽rwp}⑳6s5@K$ԡ3лv7!\x \ 4Ўp@vll,-~;B>YNi$!*ZOW) :5rwp}⑳6s5@;Iݺ[7o~!\x \ 4ЎtBaXEmrwp}⑳6s5@;IÞm;B>YNi$=-S4#ܺ,ZL  (@X"Οw>{F \"@NƘ1c*eO>!V*`Hѐ!DIc6e'iHCekq:mshlܧk^>@p0p4[Q ;)˗+E-Q#g}rp}ShG; u4lĨҠx{))…A^  K@B>9k>W) te8 x=Ӿ LTfo =V 0leKh;jOpJ]NB@^~G ~3gxZDDk>}:c XxV wGYNiI(* ³x/^խ:\԰a=l'l=r撳>9k>W) t8 8y {G ?`1#GU5r'gmjTV1b&OoVxbb":w)S`ٲes`J! cEA)gvXR j4WGސ@hI@~>&39uǧzcwP;oe)CRk-},?!`LzbO6m )1-"Y+_A̽Edgd#ei δ91ꝩ"twGoTM[*^BhP\r E^~9k>W) $Np@bC>vɓ,:BfAе_}Ԯ YOCu=l76^;{L3HŨB=Y\sx3{㹢;7:n#Cշp᳈tG#I= cWԷf8`TJP_Z6 6k!Zwk *:vvAyl$&RHRAE!MB{uq8I(v; oU;-msuu/Wmy6=ԩ@zM,FSqyeMP `xwqQ}lVߵBh6JllGOّ$gmj8I/,r㏙sF fH~+c ֟ԨchZ`l6\s< qZ_ g4ӣ谶x8<ّ$gmr-{j JO=LϞl7"*roQ%-JG԰!BAt@W?J*H妢/H[-w?A j=[!ٖGʏ?#G$\yƍc["1z.CvAj^*viY?pZilifh54 ³'vGۅmV:xWO~/Q wuFQV _@^D!gG\ZtO¢"`ru F 4aeJK=޾P( >izٴ 4zHIZ .3=@>nXZԯ^.CaCs 7گli((~sZr['gmj8$ܺUMm[4t—CB,.=G5oЀ9/j` O wu=;j1޴VS:WcR;BۢVhpK=#] /9; \ 4ТVv4= "4] fjaTQ~n$?_60#zvkךm B"!gG\ZT&aD t<$: bJg4ѽYwa!- 7,^&[ߴ#lB5נN:XaailON ^@kr66s56Љظq#-[ )4YDpY+00;p"nGO>x _31<2Zm淁[}wUI͡P(nY;{/#`_%9; \ ~UV;vDDDƌ+ⴀNBؾC5J2uXSPӳ& ق'xR  E7qoJ3J;-miװsFAu7 nnnwgh[vC2ѮH6s5<9~*_Ik n4HGCKZ/hSt} NKiP|h2 .nk{Tb貪 cD6,'("zE iNj>Vn,j,96s5ZA{{{c֭_|qGbP1aZ}ģN+Ѓ/#DťF+ q Ԩ&>>E٘4G@Gj\Eqjh}rEհ{:++ }Əv$~؂MN«W@ ߵ=~܊|m=Ҙ" |mNev e5BV@|F$c7S} v|,X.Đ^ š+m ={}n"w'Zmk׮!==/HlMapFwx'YYo<`@ bދAֱ,Rzý[k\1-4_quޝ_n%Z-DF/̺Lׯ@Xl0bв%z#dg-I&ZTz뭷$?%%DMݼ)FQGѣ.o? 79TP6V{b6ORuZҒ&J6nnx.xP`@*+lIK#1]ukӉ'%*-%k׈m#3ˋE4g;!_-I%i42ȑ#;p^M|IKYYTX/ժETTDTP ]y]_Y6<^K˖Dv^m= ZgNDRAEOP*gӧ7gIUo%RSz.ym@tђ%DΊ~X':z:qvbg2>2Г&M-[P\\%''iԩ&Ċ 2ĺfݻ%;57Tv\k'5_o * DyF l'XA vq]cE((pRAEsE3ƪUDrˊJl*a͚D'B[z:kÆe>>D'ED.J~* s)pBھ};}4ydZbeddH(8V]ĔѸd+Ah%ß{n:UiHV,Mb KԖO H+ɘs `D3g<Ģ"ŋ5' G& e-c/ @4jQRx>7nфr4~;mΧ'rh] 2'x&OW ؽiS'h*mQ̚EsUz{n݈n0zXAI}~s߆1GyeDMbׯXEߍJP*MY+Sb㯅; m) E'Ւ77h]1¦}iˎҜu̾u/Y^zMmǜzuA~Gم٢7ŵﮑ *zD0 OI%asS*)!z}"bvn;we/A"7nP JPyi~|>Sٔ JP9ПMn{Zuv87:MW?JwKaTRDE(beΠ_=ϓJHc8v(']۷m Z^RnWA"cUŕF ;tRAEǒ`ښx7 #鸚| *pF .]O,-แNc;vϣ.QSJ˭:gH" 1|iN p6)lH,9je^#wwS})ʱ]u0,1)SXLL4 6oAX'עED ujӢA zd*j˦FߥKuUW},h'S{=SLц4~tUo/zR$~Du7&zQPDۛ%+]n|\n%E+Z~Qj/l *>-RleBd65KfwA 1GI"k.4QYOP6/g-¬hZ_\cNuIR)1{[P`} $[ffS^j32.Hw#Dbmw,GgDw?h 0$,.&_1d%׎CRri R( ym.zTPQX0*ͱ3u5k\ek5  N 4"iu_ɧ dHe@ߍDkx[ǡCD$xz҈' ZN.5 ޹)EVu3iSMN4 e' %|@*0&.D1q׭?%B^C]6եJp-&Hȑ$ߟ} J(Bݻ@_Np֤f^~}l:ZGn%u\4QxRAEѣ+GGРA}S[&2D[,zӓhn9^w}:$CLv@C,!%j겪 A qMŇ{[tBmFjh}Mg6vIz L@o>_+&"bu `z:k "Fϖ [JPMvm'=auluQ:/?u׮mUNL pjsMO՜[bt ~tB@/j ~h)($ܴIԆ zs3[9tXsT[w\Ƥ=m#9}q6, welq3ɜG̿ ζ:Qb+O6rvD!N| 4rH4p@{|&ׯBxCY}7 ηXb Jc+΃VgE[4:e,hDÆɻv%Z 9  y7)/ɿ#JGqZѹ.(_uX+?=̞o|8Be%:}#i PJj a[[^7$X z H+*bC} ucih5-#(A^?7t%z O]O^9CDn߃ELOg.Flvݩڜj*mnX.n}Z_4_|>V67D;ۑz#S/e]6/]bEJJ]ݺՐkG 9/\h{F%jk֨a^GX)HZ^5YԄqɾL] ڕ -RD3.1JMްa@KIz5~hw5v\b܂V%/>|YGɓ-\С&:^UZYI}zm}Đosv11m{Wٳk[]'<HI +|}Fuqwu]U".բrsc SΡO4>%Qw ؗhf&70rHgP#+nQcc%h6;h2Of곰v8KyL=?%6HkTvcҐ%3l@P6Enu//((ĹԪW~IYmPڴai2Ǘb7 +WXwp//.]sIVh5ws_4,ݭ/e6 4 xDixLGq-C 1İMw/-nwrbKJY|^k1bHo>AGƍLGY=}6p-)…&k6XCnPT\=i mcIr5|]K]Ç~~z]_ s>ђ%Dz` n}JsOѬsY6 TPQxpl2nzd70fΜ1{Ŵ|Is4 VgTUt!] Di߾mpWJJ&ӡA(7bp>Z-#tRSYg u[&MwyS@eEY |{}{K=7o2 ӿ>WQJ~͟]gE1:Lj쯼<};- YdR)ZG<A;ZI{I-qMt{n n39=Hnu M={:veD3=YI #u5ۧ=i*WBRm)=NRC->?_VPxV~/ U t d?FoG~{Db;PKG3'W)n0?Awwgh̑*С9rrXu+w+hY LѤoԦI !#0wH1aF52֮,NPSP ps9TЯUU");T3^ Gн;sͥ7Y%,D4^-p]zwiҤIMհV &ɓiĉbE܊ &(YGA*QH)+SK7=*r_1r$>+n%ل{X 'A%hwn.^ n~&R`@ H"V#F~mٹT>5O1ӧRIU{c;<׮RZX=AʻͅL&aG2lڴvdƓ&M*܏7o>A{>SO!굨5֐}֌zݛmFGA#|A)umM{b;}^JOňbm JPMu糄O5 3ovuVW`֞_[1vI^-)@z#ɡOJ# JS 4}7wU~~F㘂2̙3BBļy3[n۷A۶Iݎ3 Ha(azecS8C݆JCWCL<A'>AP7z̢E/bie١6$-%U4wRsSFgSRvagEdz=QXUG(գT]NP*% +0 cWp@_C:}Y.ѹs6+7*III$Vx<11~~~+FT?uXcx2Pjh0xl1~C]Q`5<[,jfPu Z 1oooc@TT⤟cC@ +VX'8|DT,\(>]OGYM>8ȲuAl&P$G&hG)p٢'nߎłq"PR"+ jh(/.%K&Ma?kCꫯh͚5t!&L@=v4 9J;ĔLBNJ'An%Hr"X4)}V󏳶Mc5ҧݿS^=/"U /{~Cy9DT؃-lWJjVEs^/glܣHUH35 SFqTI(39BtݻW ݯR: ˲+zA zzmWN`g@A5HPNI}'>CT{y+|R@zW'j]f n%@nP#L9i>h-=Qt "2Tu.(QoEȅ7V{b%iKYەW(tjzIJLW@.@@vo̸uGIͬ҇ڵgO>Mn7;v~Mj؝^ aaIgj :MJph,H>GA|]DFVJTʤ:,g@4z$|Wy9!yスRەEzv%hqbLJj^H/+(T  "n߿/+WX$׊s+JP_PvDB$+twG1ZoSa"ϔ(S"sy5 kI9t+dU.WCiUۮmS&eUbrf^z-aMNa6ҶwvEE6-(0~W Ɵoa#Auj6oC ]3hgv-h~_#7:M(۲mO֑&U_3ECVA^=YC B@auNƶT~쵻p~^]\ t~ȗ^8TgR_ZuuSt*ߝ"}~bU]D1;>[1w.J)g-T[2e9 Jn}84,<<JPD5<شܲh F剏gQ :~Yu6_;9]#_6zHLR(F ]77 (]We*qR] vV$'uw'{xA 곱㺉ѥY$ wx-bM1u^љm(ZMԾ: !IN"^Bk n) ſfδ|VK.PP@ԭ/ Raur-NiI* ]j͒㏆ [A 񕫇nIDATQ}6: @8FAGc^>6 _u)MٝAӤɳ nFޛ0çӟ}UiyidkE]=D1ۃ];fIn7lޖj)_>\4Ю$ԑ<͚Yh/D'NjJ: WLaM%T{^moyc?Z&*,U[?u:KBi1tuF|NZuQ̴djOLAVt uF Ef?in2/ T PZ͒2)fC#G̟d]BKf# ,^a4#Ύ;lzb&PzACJ @1cHI 3L7}:{#;u*&M*gT_lo<{JDmڰ?Ht# =R Rn%@nQ &Ofŕ++y] )"YYC$Śbj=A :xHĺZ^F'>Njz-;iBQjzJ&" vBt.|s5j8rk~q>ڟƟ1:F:J2K(/sЬճ(%'Ev) +9 u9Sy1iJ76 ;^"}x&mfι٢ 0AIF]w^A |`uD}*<(3@kqaln}D/HݣԪƝvcJ_FNg[߾YxZ:7m+v>3n@nQ ̘h"2TxENs4xyY_O-@q_Y<md3g1qƱЪ+3EBF՜[1R9 Z Rc'VؓC{R;{iB֖[A?&?NT6 -ړ5TF-މ)4Z <9S?Ǘ}Iw%TZ \I#5E:;MGaTeU\tb"[9׬iuRW96FlouKʗ5M6' W:N6"0n2*EƯV˚> `wEѷӊHxu~IMi4,"!t!j> pa.cwEZvwtJXa%(a]ﺱc+V63oiOxm67<:AM|XU5{ KPXEm%umhQ=֒^c/m[VQ:* wSs6Hza =ΤQGW&y;^EX}6{Ͷ8vE']ҙ 15.} K/&6%'ίK ]^*;|p/n?8+09<([ GߍjXd {pwC*7xPVPUZ0L'F_i) tw7p1g pX%/_6yΞ=xQcq;m`liNQ~w}) ۞=&<7 (5}W.tfBt]Hаy˅-zg\Nk!yU LqܱTw~]ǥ?wwߥR_ iZ'_S"ۛ嫘$-HW `!!ևg-_nobhHK/ΧRU]s'XӃ>89*%R͢QGQ5=k&Њs+,Ͷ8vU'ݻ IrT^fg JPB4h h5}`ڐsk2d3ߝb{Qg  ]z )Z}Z- 7 Zu-Qv,q _\̎d׳z@w΋/ Wޥ   4{VoWq-$ԡXԧ '4S.lzGz}o/ VXaPʹ/$I+_yo3fP`@RAE6+C77-ζh6C軛#ܻYxߢ? 㞞ꫬ@7snҿҖ [hCZ];F]DG+}[%/;f^BF5X@\YBʽIP*P8Z}zd#(HV#26+.&WֽFgz_ٍFCV;wT mSTi#KVvTVLb\mqJNByy,ӓrX0-ZL1ۃ'Sk]d_퐹Z-k=C^(zu)־bưN7>DJe p:v!3ԙR ":b_￯"&Aj(|guᬏa@Q}:h-M>08QJ1h J[)QHa (ٞ1 0̙l'Yn"*ځ]4Ю$q&+SU ͮ1 fʎ;yյ#%J4zhtqS|2tk=Ohӡ|殝XK7IX#8ͱ=QPBfT$''K2/@v60{$CA7s'=]L,~=={J!!رrd(2KroK?~\եj g8^ :~G?c ON: Cأa0vj-lNaHM~Xx5DHsr'7Dt@תb cǎ͛ZFp.ЮPX\? ߝnP>7}#nG`{'Ъ^rǐ4?%^)uׯ:P+PPuYdz+<-h-jviT r-NȨj flƍ4૯Z}%%)g߃ۭT`pH!:22A?GDP(o t <9$d&4tAIE{/ @  }^vֲrCs蟈 _ggN?x4%hs4Qjiړwڏ'-*!"16rfG,qMnkD.LFIԆI* '+?s9|^s\Cp<=M=Ԡ*==&rw[:wFнO;ʆu;Ot >=ySRmktx7u}zkk-Pڽ9Ք( ^\O}j S[tfu::IUѸ"UD\uR|TzzimGLL ny^BBBIP\ t\Jѣ0jٳ1X> n|Hy9̛gޘ1 `v6]&Qc%*<)Oop_4]\9Wك4&n ?2zh$}-=kS_]ݵf))׽ړO7Bn` {햣5ԩ-E[ԝ[to؆0җ/{2܈?@>th]8 nϡÿ _zu5<: ۷w\kmƯ-MLEԖ7/72sLKPPP_Sne#ް#-7[nvg;yc o4n(QTMRii]WymJI:.׃.]ɓ 3^y%p0s1q {7\iO1"#ᮻ8}8ƥ(`L߼n-pBn_V]S7r!d@c )y$.\'4.۲`|c/ =~ **`N7d;oƍv.tns[^[vvdTHKx _w<̝k>qZ[4ex=c%9gs{}:ѹ iM)e \9ש1si)̙cS9nŖGнi'aG࣏`BpL>15]z gw4C :S=yO>1ɓ0q"|qtݙ#IO7ʌ+5= @Ɩ=eHXa..θ޽ƂANLe*c!0z]:9-to_$^>^テѣ=d\rNO0ÇOn_ڿm7 I:9-/ :ScƌΘb޵8.(x9x1<ܤg~]sHhOsHر~ ! '; 4^Hҹ ily-]&]:sm }NqTd?gm }Nc->tns[hY$sHr@"<@ƖZ #}4вH3O6>t@YD/^L}}&OҥKY|yEBHy:9eԩSx<}],X@^^Hcc# Y$sHȀrpDDDpwp{eŕ+Wطod?gm }N#zĈٳGLL q42˗}V__Onn.J'Nh=h"}}[dd6-9r|x<'xɓrJ^{5 @]]YYY۷bbbpV$w]>tnnOT^iii>0a&LdEBϵIy:9-/EBHy:9-,Gӹ il9e'<@ƖZvGӹ il9H33O6>倖EBHy:9-t]lѕsHtۨbduBNsHd@@ss jhh:Sgm }NcVoNJKK૯$ <0rE|䐜̎;vQ]] @SS5m544∊:Gqq1۷owyf^ -BjjI>n7֭cܹauRzjS|דilld֭V'(--eDDDP]]|@Ğ={?>"::Z\. 3n8Ǝk_M6ѧO֭[97m[ h!MluC!zB)‘ڵk9u(Nd@ GjY%33Sl ±Ξ=KhhQQQ䐔c҄h\f'#h!Д h!Д h!Д h!Д h!Д h!Д h!Д h!Д h!Д h!Д h!Д h!('` IENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/hi.png0000644000175000017500000002477412253362407022023 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATx{tU坆 K@IDCQ*^zeTjm+`Kki;رNK-րBeFQ**4c@AH@Anp%ə?@߳YdY'/_w{'EaaT\8Z0 hJraFX@a h0bmR, 0BaFH6 )Іa!0 #X@a h0bmR, 0BaFH6 )Іa!0 #X@a h0bmR, 0BaFHI _y.\HMM L::~RSSeAjaB#믿9sPTTի3tPf̘A^^IaD(8;vl0d˃R3 @8x vɓfʸ+<\F >lsΉN4)Dg͚}uuu &D|ĉOye]m|>}z B;uWFC_E>Xg?kJA7q5{ߜ9sZ|^FFFNԍޠ !q߻]uw鴇PAh4B Z]z7}>wLS n㎠څ7身zCH1555A+H$BP]wUo{;FaC2>Bپ}{ BtU!$6 a3-.TA]Botڃd@Qڅ7身zCHtؽ 5mC2Vh7#G Z]z7΍8|7@4$7身zCt!-< m%TA]B р?ޠ !p{ph@[IUouW:8$7身zCmVGtU!6+ F#]M hOXIUouWקr|QM9$JBz7Q@nN{ h+ X@GeSQ}y>$JBz7=Zs m%TA]B{7Ŏ \A2$7身zCGcqOEd#hXIUouW;IAm%TA]vos}@G$7身zC6+ {9?ޠ 73)XIUouW돎 ]ᬳm%TA]Qӟ={FRRp:A2$7身zC8J]uu VGtU!%aqw:ՂDJBz7$֍zޱ?ޠ ?Φҿ?;ijMqxJBz7,Rр?ޠ wU VGtU!@F_EhS?ޠ 4NJO}*x VGtU!@X@egNwش`Td@[IUouWc}0?55 VGtU!pT@4$7身zC% + |IX?Q]wUo$LK,JBz7^[Vfg-2ĿF< h+ ޽PWSV6Y@Q]wUoȽ"[׈'m%TA]rtMV6vA_#HQ]wUo}Nd}+ɀ?ޠ ǎ+3Q]wUo=v|mun:Aǁ .&8׿/~&YIUouWc##.mrrrxOHJJ"33*TZIUouWciA'f͛ǬY$7身zC@;v@Zvld2339~xöj-ZIJeXt) ^_0n9rdh\ڲ0u[FFFh\ڲ~_](ۗےҥ^Zuֱl2MFee%a")F%D2e +Vo/&77ݻ3|jkkcԩ 7nJCD"222h3ޠ A^$8<(-mˌ?sۮݤ-̙3ꫯnvzI8 {lC; 0VGtU!XA7m%TA]pΤγ$7身zC)-l8?ޠ FPUouWcaMqQ]wUo}HJb펳Ȁ}} h+ ٽvSF^$%{D"VGtUPSÑ:aF h+ =ǁk Q]wUo^t ։F2$7身zgX@o=8=['ɀ?ޠ cSTtXIUouWtvug,HQ]wUo wdPHLXIUouW) T!Q]wUoBEGE5jDJBz7xt߷ٟр?ޠ Ht}A4$7身zG-[xX>`#P`%TA]CnKHQ]wUoۃc+y  ɀ?ޠ c#1|x.$JBz7xrĿ]HQ]wUoe I)k#0a%TA]<9;w#9T/;-m%TA];޽6@2$7身zCbk7K ) qum%TA]~ {wFGHjD~_$7身zC{pϗ\ ɓ'3c (,,ロM6$7身zCc{"DOS222~=z4pjqPQQ>ƍ_ڰ￟Tjkk={v穗?TA]~hm)=C =/:2gmy O?}ʈC2c N"?ޠ s?A_;?2ؿ?<xmznEE999 2&[IUouWĹ~.g_ٴ\s 7o?kmssss;MYY'weѢE,[KPZZ0R_Pq[IIIh\ڲmqi붥Kƥ-?%ߗٵEuXlӦM0F#“&M'd&##q'3gΜ_JBz7$ե Wp# 4i< >^)SvڸQ]wUoHbWFd?CF2{l>Ù:u*guV\^JBz7$Wf^WZœO>0}`+ @tU!1@N~F$dh"̙wܑ ɋldOR_3 ={2eʔD4`%az7*r^ܹOsɣYIUouWx~=/ɀ?ޠ w}t СJBz7:gt豒?ޠ u_\]ϱ$;/nd@z7}sd}Cj?ɀ?ޠ uHOQ]wUog]g-Q]wUo{I |xl=Z+ ?U"\@sqyM%$JBz7}|r$3.d@[IUouWqH:"m%TA]b iDO$d@[IUouW/_gåv h+ qxEѤ$8i!VGtU`0I+NfZHQ]wUo{+&m%TA]:pk_#M$JBz7t}vظKd@[IUouW/_]9ބ}shd@[IUouW?<Ժnz#s“ h+ ~H^}Kg h+ ~W_u5+6lQ]wUohsD^RRKꥆd@[IUouW= дmdT~#F@ A2$7身zCܟ.\6\uU h+ >gqewhd@[IUouW1͊2e7|5B2$7身zC.tGIڽFDAd$JBz7ݽ~zo&m%TA];v- 9sc$JBz7g(IAd$JBz7޽w۸Zk VGtUWCE\wde'#VGtU} X`am%TA]Zk[ݳ'=0`Oɀ?ޠ s>ǫA]z''#VGtUï~C/'d@[IUouW3?4_^LZOHQ]wUo8{M <+V}t#(d@[IUouWӻwuYclz4HQ]wUoh=~a1w t&"HQ]wUohٽwcĈzW_aOSC2$7身zC|'7Mo <0azE>}x(++c̘1\} 6ɓ'7yQ]wUoharɁ;Mo,]V:^x1v3gd VIIIdffRUUŀNyQ]wUo8=)S(v,a^3]$Ѐ ''S^^p{͚5̛7YfR ZIUouWSݗ,?=ks}Ih@m6m$''Ǜl?t-bٲe,]҆¸04.mVnkK[ 4(4.GJ6 >֭|9%{-n:-[ƴiӨ$L$E <0qDzM=((( 77ݻ3|jkkcԩMw뭷saJJJ$UA]]ƌq{nʔ)oXƏ}4K>D0g8v"uso VenvVGtU~uÆ\ޅE*Èd@[IUouWq#̘/_][9xFJBz7@~~!F#c(* 7.PG$Gж?ޠ %[dSOAu5L`>ɀQ]wU7ހtvuNIӨ m%TA]ѻ-D0m7s\r_!C2B2$7+zO pU۹Fwi⦈d@[IUouW~ewoX_;زsTC2$7+yFݮuv5rFm= $7身xWVر$?N W7t Sn!(MI$GVGtkk_vc@'ܣQs]{gB2$7+x?[ߞإ^y=DE h+ a3Oýz9Μb ɀ?ޠfsݙQ,qGkLII <l HQ]z? sXnjΆW%T{VC2$7躇{wX\d{ʔSF h+ a~Y;9.ۗ,ِ h+ as9~[|< Ѵ!VGt}~ە93/w+[G`J.o^$7]VV6  =w'<+ae(%H$7]\ \/9C8WT8ܹD h+ Ax??;dƞ=C n1J]wc=O c~aCrJBzo ֭tկܙȏ#G6N3#h+ sggh8[o2\@ÆJBzk{] gd#+W=5`<$PÆJBz{"{o\ V_V?3g} ɀ?ޠ݈ӟvK>frGmrӱir >=qb3 )+ ޵enܚpSahطlayؐA[IUou-4T]ަp~5rع[\ Q]zֺ%3fn[׮r<E-G+U?!%aF-@tG_P^ 覈'Ntk3g~>|qw7GrJBz{kwп6ͅgΣG3u~7ݪpnqf$GVGt[CZVZ+ǝ97޵  ro7awmH$77޼-"*7mu"}}E?Yùҭ;օ'Grm%TA=><;EAo+t +W@./O}MhӮRÊd@[IUo{] ?]@ ) .ȭ vwLjyZ: ӧCBaU hPq~N^[Fo.6zRR{N IDATs\y%M\߻-Ϫ3WF2$7w MȑA᳟ukAܩi[Vߺ]Ps-/aB2KB9UoHnvVw;;v>}`( .n}[l믻m&@K h+ sF|֭nyt}_**ܴɤ]x!q"lq?hԅ¹~jsݮ&%ߗ0!VGZv?rvv{_B>N')-s|w> ]y]+^|хn n׏U&$JB(xG/MSWWxJycƸ|~Æd@[I3yG.#~>tDִ$7-(85p)KG6ln %oȬ,oy"h\fwzO~Æd@[Ixf꠺%9u[/#Oz-Ӷ^ ']gf>}\ -~CnTyl7l4Q^?R(,Lr[pdI];}WUEHNh;ruzXnU[cHGVn:]={\x1v/~x"xf"iid\rɉ0gܔEUN$ɀ~̚ձls;v!-ͅd>?p;_ ܚ%UMݺKUU˯& N\.ۓ)><~0>HuLŅPZ%n|i.MSIKs6?wMrWU5iwډ޽~qnn#bOJxh@G"&L@^ӧ=uuuR[[ٳ FɷD!r)[:z~ߎ>6a7ݟ'c]]ߢӐ ={G7ݣǩ7Ko:)q;T-QQQu])//gРATTT!C(?Zނ}λo<8@;9 . R?]#DFG-; kG~[D˶m۸+ضm[C(fʸ++pBjjj(((`ԩA+b'/W\AR/gΜC}Q]]r 7oj>#ΝK.]HNNWh#+W?g}6EEE̜9}{tZ~s3gXzu: r?[o|ɶ~|+_i1 4o7n}lٲ^x! ӜUW]Ezz:ӧOg,X#yu]O0o޼LKߗuqA.ZG[KQQcǎ ZPt^^i \2R<#6l xYbEEEL<9 Öi};;ʻy/Yo}R^z%Fa˴_j/f̙X0>'x]wRj?1 ,/6 %cϞ=OiD"{ 8r~O:hVw^z)9h[n%4͛?L~~~V-ӸW}l 7@JJ /V0 㓄TIhI0 #X@_~G}2Άb06:%eWUzꩆ} C hӲc233D"AF6:%gfԨQ̝;)Sz4h/aFHaFH6 )Іa!0 #X@a h0bmR, 0BaFH6 )Іa!0 #?7xQ*gIENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/hankel2_c.py0000644000175000017500000000015212253362407023075 0ustar georgeskgeorgesk# Hankel function H2_n(z) in the complex plane cplot(lambda z: hankel2(1,z), [-8,8], [-8,8], points=50000)sympy-0.7.4.1/doc/src/modules/mpmath/plots/hermite.py0000644000175000017500000000036012253362407022705 0ustar georgeskgeorgesk# Hermite polynomials H_n(x) on the real line for n=0,1,2,3,4 f0 = lambda x: hermite(0,x) f1 = lambda x: hermite(1,x) f2 = lambda x: hermite(2,x) f3 = lambda x: hermite(3,x) f4 = lambda x: hermite(4,x) plot([f0,f1,f2,f3,f4],[-2,2],[-25,25])sympy-0.7.4.1/doc/src/modules/mpmath/plots/gi.png0000644000175000017500000004341212253362407022010 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxyxU' CBH$ $AAFqTZU+jQEuzu`uhEX:UT@2yHB1L' ʛ3y/gYg]E?}0 8H@aH=aFha0 SXa| 40Oaf), 0>aƧ@3 ha0 SXa| 40Oaf), 0>aƧ@3 ha0 SXa| 40Oaf))yyy9ylڴ ~mvmHIIAss3͛Q2 $fйxאq~Ŋ4h^|E`Ŋ !0Le|ݻ7#b? -q著 PRRs9' @^^( 뇪*t )))@VV)w}Xt>싱$\ss3jkkQSSzlڴ)IB: bΜ9زe ~_`ԨQÔ)So;DCCz衈rrrlٲ';|& 7x>^ٳ= qQ1fĉB 贴4̝;Ww/u9}6k@vn SDY㢆c"A(J#$@͉/Ḩh!D @/DI eK\Dp\pLavBL)*`0C%59R"Oяuk\=_qQ1)Z1HN&MAoLdtV =_qQ1)"CC@^з/աnD஻ (**J| E D舩Z~>@m-oihq믽GN E DcP:ƣ@f&4m8.j8&r @G ϧc^~ NǙ36~ḨȁaF tv6~ Bo ض-~o ц㢆c"R tI]∗@?| o0`8pƏ659R5M¾}/.+Á$2'` E DPd99GZQ-jCq6ok?p\pL@J0 ++iJs2r:U|A E Dڕz 2h6~Ḩȁf?ד@ :u:t$1hI$0.**/;6r+3LJ:ve @Y4y$ɣ :-/-%0^x)X0c#lȁtIx(3h8v̻A4M}Їyw_=|ycGK3†H)m&Ƞ]^NbAǃ~Z ̟q6pL@Jn3 J R{_nnȜ+K&OnνFCS1)$+qx -V /o@ϞtLlO?E@=t(M}߼V;aCL DhC>6ʠkjif#I;†H)ЖLB;-v $0!Ե2rdw8lȁm$اODe;vbj8&r @'$5pqAzv@ s,m!c"R I(j^ tz@JtQKK\߾a/6M$lȁmj ƛ tz:SC`ߚa!c"R I(V@E= 8ͽii! FS1):$ؑph:tYGYAj:6pL@J̤_h4:bE hߞV΋&Faky:͆H)4CI[ʢȠ2謬iނx 159Rf66fVZ1G39 159R#LBEo=EvG<2+ƏaCL D6SQY.G ZcŒsנS1 t0q㡇j;_RRO?/sϩk3 R߹)"C Zc\]/q!c" Eaڴi;w.*++^4@Ep2iW#LJ ۋ ڬƎ2'If _j%6pL ]^^\@~w޶+x饗BxR!7džn&b :LMMBנN`8ǁ;>(eCL D*yyy(-- )) hll8__[44wlݺ&^|[;t@ hhzs[ ;?WY}mLڵ\ZZlu^֮]| [QTY \~9.Xq9ύ=7cIK,'Ty=.|$:eʬY_y饗?PY|2sLoV{1u]v2u F:΀_}q7.ԝjt_l;UWWFI$Ŋҡk(օϛGKQpԆq9Eh3cƌD!D9᭷矯{]0OVq2H|^(++3CoNbh?䓴͜ | Cf r1)2;toԠBwqsz^{ly7v,Dj8&r @Wd]b/58 ?B?,M5 HKS>#կ1gCL DDeVKe|ǎAFpv_[횛 hM;ܝwlb S1)BS6&%SOWnyC^0 ĆH)mStpS/ݹXlȁ$ևNM5~m5h Z98f!bj8&r @55тDbŠ:)) 2(65~wǏu9+!c"R tfsyyjb۫Xaj57;EE4d7rJ$ lȁ]aUO\W{G&?7fMެq%s87D7}bj8&r @gua8n];S'˗vtJOk]0ꀫ*}Q[pS=eV#S1) .Эoei5bnҥ cxq^sO GajdQKr=DU$|]. W70vlϕp}曚aCL D:@+zC"m.HF FEVmaSYIXԔs3v@70̡s<@[VtΑ#ERX{@fO4bj8&r @g( t5ص txS%f`1C/ __BU̍ A'ZafΤ.b7Ns9 >zZk6pL@J, rԹ`s$f/Í$FJ|3F߰DZcz![Ooa%)M4B6pL@Jjl?@z5>%Kfc"JR].]뮣0#t:8c㧡4*SZ[_? 2zx%W_M6a!c"R Up"$Iu5:Gs3puU[ɟ~ z5P_tkG1 tt[u.>D{iW_^zMTMB9RGHQSQaJjƍy˝{.5|E }g^V=  :99z9D\~7ۗ>r pQԾ>u*}jjr~MB9HI`$ll֮8ɴh˨-a!|@2L``iAYycrOBteʴ͖9qDe%-TXH2?ﻰד ,}ZW/?D_?H$)3h+&aq1eL9#LvFCtP Z 2UeSUEӫsxkU(ol#kNw_<^_ |=A>,# ?˖P/YB{8<80`e6 @Jb]K>CZ7mi~-S]M Xp.bچI|9V'M&Y+02 /<3IIKkG^ޓ_z 8x!6j'lʁm$\ZuJA۬Pydq `v97$B׭qp JzƏ?O9)z`P`Z,f}7о}KwMVx1`nУ(~G@ YaP:tA":9JoB[(o|-Ǐ枈dP?AC=i 5V @mY{x2{ڕyNS]M=p rlʁmf?N=zG 4~:-Omɂ@7q@j zaMiO rsѶ@ЄZӺ͔8~uhA4oƜ9b}Q<ߩ&}rI(R IX\LY͘1@ZEgY mtplFaàtܴ1Ak?ޖAHk8|ܿG-"!?yrpWuu({)ȄːP!gA+?0,&H)f&a@E tJ -Fb!Xf*Ёo͞چZmIҥ MRi Z N}gclf:' (d2- ~ R[eq1pd2sҚI(R Is'}Lk=!:?28 ;Aš ʢ :8gO?=} B%G5r=vkpXLCR3DƍWrN @}+WRF}2zY0x5k¸r @gk9݂ʔ@75NҭjX0ހC4Əfy0͠]hEJJh6v3qΜ7@կ0UȨ/OL>Vu>z}o}W6 @JnJ_I$j#I' ڠP?NZdܮ T-(p #ٹj={R[W\`~A{m݁_Z/Gc#0>UggZ!>z{r @׉O N%;mKMJƏ@8:u"#$bmg)I=[M#2gP;޵p!mA{oa!M k2:r @gZNjTvHYO V{6ix&%hGwm| 0Q妦ҊsZ> i̤N7ޠ.Uhu3ΠOd \2MB+}?)lʁ]QQ;tv?t9 P()2wAf$ǎXҤn47G]&4rC~ujl1#4=mۀYg][$'ye&'ԩϘ\z)e>KA lʁ gOuH 7m,Z̠BCfu$}_`R7~졲ر eeI3w>vi2zh @iܙ5J뷅hn &h8@FekϞ]ڵN]Xob蒒|稭ȑ#qaLB3`*Ц=55^ܾD_d}R[e6)qա- 4@Sz>K| IDATr\{ɱtu+iRq;1f郄8q *_fC߮rė$wuRSS1a梨*~ez&]ڕn.ZBw;zR9pG :9̛M(1> !e)f'՟.Y D͚ڕ$n.} ~;}{SN<%\A*M}>67_^@wk3-R []⩧nCJJ 1o޼LB-$O(RЭ?f&ʢ~TVRAP5&]Ǹqc:NZQBk1|y Z>LÙ3],bڷْCQ{Ɋ8AOqX'bNKK ~~[aѢE6mΝʶM+VA/+VN$t"ЦuzJ :8t[ Qni!)oa^W/q#}B5YYs/\jUu&a @nݨ,7p Ǎ.> ^q0}:pmwI&zjbAWVV;@}}=.b[זcĉ~a޽۷/ˑۚco/=pNRՁM2h@:8t BRTWH7X,e={Rd @j 4g͍O<\u faǜWg* @8sS5y{1q EQWb`ʥ^L:UٿkϟWEQn6DQEYlO*(O>|ƍ{ァ,^u[@NSu)([lQe:e(Ӧ)آw_\ĵ۷ӵ':PsUe7( lGe /Ը6|,Ŋ(F}ŋ:kX|}!=eh|OgSJJ_qEQz^tkE\TQ.]{esRSEپ]ٲxR'ʺR=VW]uʖɓ߭PQW(uҷ.;[Q-J]jkG-ҺT=@y iӦ)~"(F8oQYYk fΜ.] == @^^L[o;vDCC}7b~Th6k"ϣn};'^P_mEEOq晔t;SX1JO;deuHv?a@`~𠁩S\LS}\w <rW_mx[mz"q*)Ț5quxZ3f**Ⱦ/+rM'"{^ 6))צվB}{$Ksm"iiix뭷4AI[bA8Tj;kKѝ;BWL:8zk> [c[(quh]_OBѿ+ DNN!>-s4fQN$ذ j8;JOOp1ؾ=Ҳ4tzAw)))~: IovL0ǏGjj*oߎ>ӧOرc -Щ@A]tf̙LRe̤즾02H*)Z/I*?@!N7Xdϯ’:Xc:Nfq-F,bIMݜ(+Vϟ>/>n݀s!-,i9bCodd#.Æ.!,r@wsqk,6 ^ ^q^pM%Kt,J`ؒ@Ç&J[Z(NI?Ѭ)SOh˖?2Hx ~|c%b&!c)W6 Hw5!թkϠUX,qXʠufOcI Jwa۶Bhjm;9lo@o@<<z$'S?Mq1 dNzHp$1hrs vc):$4? ;97̠ Jbž=MD$n׎DzvzsY(qXc2\XwY7֟ N{ܸ؟8ˁu6 EkYͥabB L2h T %ߧ}l I-V[1wc},ȁmZtr2} ?|X#5(qX*o4`}(}l6h'Dz@[X0 Pj8xsX kWg7!c"R tIh@E"0(qXzcJR(v 8C(+V ɓMDhp۶Є] ArkqJ# Bذ[,:Ky8y֌H)&a0H]&[ޙB5A[0BtncAt&!%<h6pL@J7 Ŏܦ"i1V ZChA2ZaPWzNzyC<_ ?Pwې!7gCL DpZ ZġQި!((؇0 t @" EB4-U:ڞ@''S}i{Fn.kh@;bj8&r @ &O[D@ ҶR8 НmŎ:hOJt" ҴoCzvU%39RMB}{ V^NeŊڬ 0C[(q@9h]A bٳݮ]lű!c"R tIh0h T΍:8D-֒@emQh_-B6.7֯  r2 159RIVSXhUڠZdBL m@d'NUVVQ r'Kk 159RIx ifȠ%M裷9)Ƞw`/qASGp`8Ƞ-бԟ6Ĵȁ-LB MuJWaB!,ڥR+YYYX0-q|=O;́c#ڕ>a  +W{RZ†H)$lZ<֭'tJ vb `ZVss?6,9,FZ _~IIb߿ 159RI@DqGxGLmjY6)q|Nu`(qBlҥtzklZZZJ.q0n\ &[]^N5TW7܉2lȁԄ't6vNO7Ƞ2Nl a$4ؤ|v)T//#pAEٍ9R333j}Y6.LO'aD&ЎaATjݽĤC1?ݻz5pF -':~S1)+WkB Fa߾6F#lTOZ-tp$'x?6BN!ǥԇ>|8УPH)YYYm 6. Ё0r$MP8؅?] iӡCa±(̠Rw3h77lȁ]Sӄ={H$MvĂ@yaucz:h&!KȠ@ ƏUN={Z@^4bj8&r @K3 m7}tT) צMe㷍 zH֮a#Z?6KYqc P{B7p6pL@J>rQI WCܹKt\z24u>Tn1 :9WUKI 7l3nTaCL D={* LfBݹ30zti!iu54*sf^92 2GpS1)½UcQ`A$5Pd_t {KPàA<^&!@Yӂc"R tJJfrp EʀSu.֖i8Iډ-vMMe>6~ aCL D#꜉  `V~k:?} 22h&csCu8vI(DŐAYCd;Osɑ`CL DamCbW LN[ouvk&SWK喋. ]q=i4 @LU`CL H)bQh!;N6vP¦Qx%@7EK I5*IJaa=@:/*]{78;^#6pL@JˍƁ@O6U۶aq=$]Ohb2~w'js\Tt z5%#MS1)ZIh;*T v)z(q@ PC-#:̛GIq` ^ 17V.BES1)`glCd Gxi:x1iO~^:&ǁQ8v,|V];8?li1):3{Po1@@;`2෿x~5{1?zSSIqA1?li1):&-z5WנU,Fr2ISo9{CCNJz`xa [Z 159RbGLkl|5xxm.&wo:F,aC7pVXW\a2[!c")lʠK"cCSHKKs6}h6)ít~{UALq9Iȁc0q,oZd 1?BKKm] ܌ލhnkC`8L[bj8&r @;6 @MCCv>lBLƏ .шc7Ý6͛:bj8&r @;6 &j5g%+d},&\T@卒${?{Yӂc" A?8vjjj+ 55wF㑜^x!:&!iUG @lG:dk˘]I* `uTS.P$,.))AUUΝSwމ}FFޱIX䨩Yn.B"mê@<;aGup>[ZiVԩ1ob5c"q={G?Qכo<@~~>q/ĉLL5kűIXj?\W”,ZrGzYl84 Ӽoe_OjV`CL D"裏"z AIIIXGӳgOD-SSS bɒ%xw[nm D}M\K P[flCU l@EZZPt0۶Y׫+{#b|?Y޽A\q9 f,>waɒ%ӟ*|ㅢ(+ Wf͚\wuJ0T6lؠ<ʑ#G3f(fR~+ M:MoIQE3TVc8}rzM~1[.-^L.{(yg7*|gϦ[?uzxb'a djXA~v} \ 팮űct6`CL Dl$C`ځ@'$\}5 5*puH)Оh2蘍Jm현b؂dg&GhOKon؆j6pL@Jy&!Y ʢ+*(͵Xu믧xf @I -$v76ĴȁIwr44PEnCvac$ʠS1):&0haŚQ5㧠ԉdE~D͆H)Ю@w0)J553 v ǰ!c"R k&aگ&!`\޺z%?li1)5Pkh/kf%A;]5~Э~C 159R=5 QI`>:ci]5~F7ߨt)'Mr~†H)О(q-(FF>lWҀ/|9r9CS1)ZzP<m-sUC i,_:nM9<[ 9%6pL@J$4/v:~Iخݽ!c"R t&&!A~<@n׿V$ 159Rc6 tqxAqϞ1Oa!-f 9,Fp^†H)1b,qOGu'zmzx bj8&r @lT横TTK {wg `޴ 72xbj8&r @l$ЊYP,8 IJx9;>_96pL@]c6 i!X[K}^qm)=3~ bj8&r .IDATeI0"^g&Ai<{S&lhqQ1):fH@Thl M./Ə59R]1 Z|߫Wo2w*qlhqQ1)PKc0m]K3ttlhqQ1)-2;f ǎutlhqQ1)30-2/ki"Mц㢆c"R +&aϞϛH6 ͥ5zGtE DhWLvIƨ1 ;"~c9g=8.j8&r @b$č^'ze񎟊m8.j8&r @bu~1'\\ubGH9*>,X@喘ֹHKKsqp'592v$":a8W_MŸ?p\pL@ -yshzw"%ц㢆c"RfЮ\@O?vyƏ6592v$,(eGKJ{㬳yƏ6592v$BSWW_Nђ³ôḨȁIDN}=o`GHYp$.RSiڋm8.j8&r z2d<ݟgr-ꪫpСߕ7L/? Bxw=_qQ1ѦJ̇ ^x&LP?~8VXW^ysܹs#~p{1c}!6~踤{Y"p >}:9r999~a޽oiipА!E G^ɵrF^To#G0qDTv&-1UUUq>UḨᘄj˜˩&(F$=z-}ٸ( y3 Fs=]w]0#`ӦMoyf<#HMM_:CZfh"L6 sEee%vJ>'~mhLTWWO>NBk63駟_W,XZGGTբWŎ믿7nlK.1s*68qK|D7߿[gŗmTCii)&Li&̘ŨGũڰBǎ$hٳ'ߏ^z9SիUW]3gbʕҥ)!mٲO?4RRRСC92CJwŧ~:\NB 3g6oތYf{wߍ4tMLBaS 0 SXa| 4sRtR<#())5\02͜L<ɸ&@3'-Cff&H 4sR2o<?ǜ9s&a1 Πa| 40Oaf), 0>aƧ@3 ha0 SXa| 40Oaf), 0>JIENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/coulombg_c.py0000644000175000017500000000014512253362407023362 0ustar georgeskgeorgesk# Irregular Coulomb wave function in the complex plane cplot(lambda z: coulombg(1,1,z), points=50000)sympy-0.7.4.1/doc/src/modules/mpmath/plots/spherharm43.png0000644000175000017500000013601512253362407023553 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxwEƿq6ɂ z3"p(33PT,N98E*&dX6NTm30 ~>鮮z}-_$gyg)eH$谽+gyYMK3<,ygygyYОyg)j@{gygygyYОyg)j@{gygygyYОyg)j@{gygygyYZg;E"deeNZZ>o{W3ϚyY-" (//UeddAzzڞyyY,  D"@M@v[8HOOp{c@{ s3fXh 0@`c=ij<,!:s8hxs(//QeV"@۳<,.D"AB8`, J),H{g;yYf~TUU]QDRYY3ڞ5gڳHU^;cyffk{֬h϶"3 ~*++mLouNf@`H$BEEP^2FV@<bM f[RRoҜ|p[{䌌t(Ϛy-ZmuoN_hہigsFBݺI^_jb9}>_Ғ,v@d3rkK$϶tt [] Jg䶨x3y#$MV~zz:іh(ΰJSLz{@sOא[@Gɨv=%%%)3o2#p<h7uFZ,ؾ Z 1e- R^^Nfff:oC376;+ GbꬍM\>n]ruh&>:N/2.'~VW>/;Rt57xTc^`у kȆ ɚեz,Ȍ>p8LYYY+k_cnJmKg Sm^2l[K noض+#pI1@bDf4tJoTUU0.tIE***X`˗/';;}^{}~mαP(T9f&K538#Uzk@ǰffth\;k((A*++ҙ 6nȱǎd@i@& cƜν%adYոS1'Fsx}߆b Rd.6llth P#K"֐NrSX0OX -=n?k`=O{l:ЏwWmANݳefZ,ж%> ,..e˖Ighjj6MaͲ+ϗxU=fff6ism9ݿ۸ᄅ l h~ | ¼y ((ʕ_ӪUթ>j\ٕ,..S(++cкuk.VZwA8&==Gy$iShE"Y^T3}mi^z>;O"I@`(p5b ?|#PB.0w[ iԉZ}GA{G?=8#}>ټy3{weN>N:#G2n8V\I.]mxG8)++KڂcEf$mu9xfGgH | |\t::nN>s>ᇯ1`ש!4u[nTݜߍDl׮1|=~;3:Tj* @׮]駟ҥ f⡇e˖I]mSt]ɔ-\wlϤQS@{ƲDt1Fޭf ϯvM2:H3,N(E+11ըH=Δ9R{өSofΜ,"3g#|aa89) _\Z9p欋yiNa{<_~)bC'!0z9R>7ݫ>^CR9Pމ/J_teĈ{ ֟7|5-Xm>AWBh>}`0N)``;NC[sf ]e֖]ז(,pҥ v,7V|3k@cd{ أHwZĺݖjE.ࢋFr}w7>17ӶV_fJJJhѢgЉX %%%dffnll$F&|ɲI`7S9Ho;ԭH z6C so4<4Y>pږY[RX؁ghƐi84BZ4ѳ> ihQ K eQUvS_fƍqZ2,ӶmU툴,ew'JJ%KyNz())!-- Z諯@:=1H@t9o>/wx2Jfxs#n$Fˣ${WwMvbڵ Ec-0m+T#fh7"G(/Hi$\ϙt1e"jH&je:SL]h;u)(ȨQfLעyo-Р<͸bE-3duh5@;?޶-f\?e˖ۏR\Hbd")@ ,Tug (g%UHKh>E/PS81 N<4ȡ}x'Kp[E4m@d f (~4 1g<+ hF ϯ#K\~5^i, / 7gUcP}v24p[_Ǣ܀"c~Bo׸`jZZ}Fx3͛7M6mmwpBxzC=Xp^YBVkՇ$,E`Bz@cGs1ȉB:H g!pDB1mbҕ =~pB=I:~sT[({$"g9~#vr"Yʑ& ]+_k֬YScڬt"cm5kW'C\s5kJmޛ'x#FPRRR羆Zt"f JZ_ɐ8)hZfmD!1ߙNA5HG[1G‚oQt:`<>_b:ti=z=@=$NX%ÈՇH@ Vo#g>N @ P"F|$r0B@u(kQ2fqwZA BYY{8k`vNY}pR~2z3=6okL#>wߝիWo%wԵ;{TpHvmh2dtm:s<:ŕHưL)CM S]:PL5݇wE{:bsih|>~;fϞ]#)XR6X~ktl"z|??w[$F~BI Ǧuz"9a h6R19n68i+3> +H.=\w?svj#> 25 be$W; ;vXƚ j_|ٳgWɴxE#1;s7Yp?أ?"U:(|̂Ep'z {z UbyFL׹ gcPg+8ڵomb`g,WfpBC8:lץlrq,^0Eq_L94yѢEt7GL `1 Gz"H9͎rͽq.u3(8u[; z;sX2d ٝvi_b]͘1ٳgsUWqF.bO̚an[c4Auf_C,0|[Is6#An: ރ>sԡ;uއe#@j7 )e85:'Ռ2#Y0Λ sn-XQ#>S/b6Ư>ljrqww k /CR]:bӈaoewK?|~ h+T/hڊ5~6vHyˢW$%HXItA􏈭Mtb^v#4׹ BI:#H8-oO"z1XyGзP^pd%3V{hAT|;o{M!hfq)3wO>D>qh"ZjDzev/.& IDATh;WH8;Ei)G$WH: K~:l3uxRN7?Dc~3~} hw[ZVVV-ye꼼F(Ua b"@Gs Htk# -= 寰y:'=G?9H@̻ bw#}*I۶]BZ` # _TYdR36ZzOA0ݐcf҂et]@C2x/9vB7sKA_s"BZ܅u+Yw%z09-ѳ{gh͠ҿzZsTg4$w7>BṮvUi31"AwE`'ކ CFnbߙcGpkcm)gO=92T `9n$>L73[p}AN>>H6hM}n0u '(zaiV9ʔiV{yf ڲxݪ |P{v6@rԷ.5k^EU?y s9G6m>A o3Sܕ2 kmǻkuc^X9%Xs '߉tN#=HC(b)O!v=Bh1]ĮEy p vAQ#w/&4t^?7nc<^Shkv-64ǵ46g4;#}y9]sFs}'NK=+.B{r-@`8g(B|40wlj:4睃mgAIaгG[1bߟ'ʜOs?DJAwD2{GuvUv4G^^^Q\lbu沲y3i:' !6=$+oiR& v8oD{ +ض]q-I {!gbT4mm7$!pngd42tĆ,F~șj5h-x[^ ջ^~]vAtzh u|>k0; cxSM9=p8C hfY#L]i4 ~_3BAF75NV )А^$:s]r7n\:d+ĀA~nVuBQȱhH~]k|bPxk@\ Ӟ@d2SHs뭓0 L9"8ȝn~KGO,k?4w/h6APZ1A3 c#&=J Zzub,:T7|=ǓP;$1o1Ҋ7 "-:”2]L[h<+4AQhp=$H4> @.HJMg=FXD"M3u@ @YY?Ek\B: N9?GklP@x[T!#cڕ/".@ pZc#흎y>`F ~!p*Kc)d)# : 1fVז gBM\ix9'|_UM_~%uɼ'L[YW)o6[liscLA8=+Yi9on#Z #I/$S]Xkh>ՙCPY$p8L~~>/5 y;u+x;g!f80.CS=i2OAHx u{q#p6b#6owo@y? F!菗<@ZĆ il%h@( TKР[#69 `PF9i8@̨.^'NСGJTQH }lW2 &ЌfG؈(< gFs91| bߡ4>Eaa6G}t=+>KMP99Rњ@'b2h9##Q:s:z`sP ћR,Ķ^G,rʻ(9ҐH6:Fɿ MWk܃ALA+bݙxDנ2h0@~TF:qI}7:9ce[p"[Y0 tX]}k2퓏::D@!4HG`2dѾ}!{Q'"@dUS L2y1(A`{" hӶ9H?XSh)澖Lo@t?4WWl"hVu1K,95`0ؤʑ ;5@7Ej*f1)x!Z> ͠ޑOOر=3fju;3<{Giii&Gٴi+Wj'd=^7tFb, [KDgNB{.QoѢ}7AA{1gw5^D nAg~g4$W܊t!F<rA;N6%8s{ҟ7#M{8^E3û̜9ÕSN~C81ʙV"VYa # W6gVU/B+JД._@sN>jX%3/B~ҐvCm%4O@x)94`EfhCHD5("@չA hxh.G3Ќ 4PM6!]LV|%܋# 3g=\>l/_^UVUjGy$&L 0iR @'b``xud j\19uڕK#lH/C@> 9G9u~:\B̯DSsH3k5r'b}ҕ>O>-U 0A me΋‘ a~_aln0ew6ДGjS4 ,GV!mi(7t?0?f!QDь S UdbhWZմ$f? 毢c̴ D9\ ֘SNt֍+{{]8dZYYcǎ塇KE"8oUJZ(",SDClPDGeeeu"999q_$eeevE;&sE9:X 6]6wC`Qhl~iq(b: "8bcQS#Xw!̘lF.\H"]n? шF3+<)ٷ";Vdz"̳yI2UA4ryFGŔ2׶YwL ;|g^J42=`evYWX8& ̇ ^x>z+`VVVE]DVϧgϞtܙc=+W_}?n]v%f֮j-[F~ׯgqF7 e-"HvD:ԈgNRrssEq/娣tNH ܷe")DSZK8:#ٌ:Z8,@?gG:)xrBE$(n=飽kZzܕ%K~A` K6- E!C`p49"  LyvQb1=B!+t3${w$5Ft M=z!p>ui4ULih)0YO{ndE<5ԭN]~2+Bh7ѳ9h ɜhͬYӫ׉K/i­T^dĝLk ڎf¿}:Liiij){nwgwclnCq4g=M$߀@;xn dYGSuE-C` 1<>qdzhErMz"}SHOEκgPDǿ7"}}ȳ '?t\w`d3ٕ w7L K7e嘲-s.3e}1u2ߗ 69f r.EI!-%y pfV3UH.F>(\7k͟'p bbi3^]}磏>Hx$Kͬf?7|sJ&͒A[`|MJ}|l^("''FdF2w̛ܹp)#sQ'{M[?Wim|'݉Gf{=fGr"f}EQ S"؈:Po9v4r}}8݀]92sn+S_ˤ<6&s|OSߍn6S60V+ĄWv2$͜{ήͽe[Sv\s~dAcLs] _#P#`iZ@{.aHhe"H2fGhP)28ŋc}siii hͅACfkV .`t[cͨWP(D hƱdee1m4Ǝ3EO .DqnFR:ۈ^'&zF ُ^=@iK<9ؽ-TYB"E #098P.(Bd5ڵ2u{ISnaίBcǩf`Z@s9g7yٴJ*qXpháh{ԷĴ^(.;]`#iy#͹?yh&d5 :G+=8>Ԕ~EC {ub4d!~iPX(/gѢa^ZZ4ڬڍ￟[n%IJ5.))1 cP믨/̽>m(|+f]t !83q{ʨD?יz~L"Fq2 "of!G!CGp@*= 눘 +FRݺKpl4Ls d){|r6d-DrMNJs~7 !So:f# pvE$A hjwb.JUNf6|4C f6K|NvD"a0!G'==A7%@ہ1t#-T3I$d-ڢYxuo&Npx5(Qg )CSӼ->D:E:s9~ r>җ{k,DDgRĆpSqrzF_mߕ-?ag> !W[l0 OBxERqC ]V"O  !m0X/F|[[[!2e!kcs$Ŵ7^LA {@# Tl?  RC be_&j7SS;{4@9lc.;$o,E\,lOYo1ЏPDNK]CL#)-+KA~BUERM,-E>o z`Ĉy釷:;Yqm8j xW˛-ێFi: cRm;L"v3L@L'|S4wPgm0bף>"(%v]1{+}Byg!Ç4uאsEnz5RX"SfZl VGhc9v!Z؎;s_4;kZ?yh[t,sٷ[^j| \pA2=z4nݚ ; B=Xro ѫ K6@UbRt V C,?gbP-[:~E# 7L9ƥ0֛k_K(̮ݥ9QS;zj%b#{,3F4 @7~qVڹ- C~#/{Eq7M0}qb3sǦ!S!Ԍؿϴy/+[Gir @^WlD'ԑ8`@iLc.J6kHn B~~ݻF|eȑ7믿.]0|?q]w1d9:0k68q6, 1sPPI)bfDt V#@n <#@BD,z4eMFL/b{ϔD y&ZlEl2_  ؏"l')PC !&|o֘>miBĀBtL3>Č-pe<Z#@d~\#Vk4ȴET7'(fۈf; }҄ c%/@t@/h@:ަ|r0hf1/n+D~)# dzA>3>t@4;OM!\ve̟?M6/̨QիWVbذaJ.]jҭ[FR5N,@[I!==m-guW^y=ڃ_ b/$fw;4%?45폘qM?@Z]1iؽX>D[cB imu7g![t#s *Bzs6bCq2M=nC`iC~`ʌ5)%51u*D(^yN@Lksː1 ۗ#VlC{A}9# ϩ8x>zn;'/=V(F53}@6j3>Kcٖ-[ܹ3?sVT<tܙs|r $k6G}XcWOQoa}GS11.@6䐚5 jNsfu A`~"POEw9n `oMOCab됎1_p br[*;KBLo&bEO%O:|?҉{6th%ln@}4X@$ |LSo4B4+q/=~NV^gў@ؔ5s~DjԾY Tflٲ,%h{4h}{ms13OeeAǻҐ6őh}lYbekc9'5 e#k$.-!p!bq$C,4>`u 3MB hESlj֔0 'L!gUaN4yg%ndoF^W#\S`aw.DDc"m j@Q.6Zf9Q9|m+zv &A6  g#j_,މX݁f1"V>:^c4@;m\h6⢋zq}w([֔ZMƉ'ȮƊztt\C\[Ktvfffu|<c>Pg0qsq$D}7朏s4.xphqhĚ!4o l !P9 j&g!ICÖ(O) qtÑTWW}݈?lm? 6ο@ޭ0YzЀy}^jqHJh܍h)DUX" s`Ǚ5YS:}w'rYf*рƴCKMVzocLSvGfAf"y^3k&bw ~߅BN@C1"Ė:!f+@馤2Ϡ~b]; bjHj8@ ])bC }e>1pOF枎F*4^ {`CbY]8auֲP۶A?b"G__:Ex&b}ALvsР7 y)ZU1Fm fP, Qȇ^j_LDޕ4U֗o9vr = kOoWPKf J0m kL\};{ۺشO>Ox4XةH]0@Ծ !g"6\=PEE$|:bFzb\L=vGĺ1,F`s!vm7T=o`p]V pSy{7QsNo]9ϾcLHim1g{40k gBĖoqE5VX8IW,*N^l0=X+LCh}(EE[Њf\;֛ب)tJ1h5u\]jѐMgv3s~ Ѳ; E;[[:!u_dB{?kaS4-/BSp"1DrbiB'ab:\#S#LĨF}5ӫ k ʴ (y%^H$ѠvB z=ZlwȔѴkoj[1sM>蝈N MF\Y~:?oZr6;˭mK쮒Su7hf ://4kYJ)8lp8L۶mYn)'t˨B D^q<" u1[P>CQ#?Κ:2:0j.+"+4F .Md?l*rovo-I/"b}8|UP|AQ-˼Ls}v۷z !fL7"@Ŝ&Ʒ6;jL=W2JpMla)FXabDzsɎ@s^6Gu׮̝ɴ7=KZ˭mdVD"5N,P6t۫zWvxӢƺnIIV}єodw|Te酒лT `UTDUWsW]ۊm^{{e]ZpW׮k(齷&sN&3L2C̜̹bNk1l"бhFX7K BxMhјB6_Z 1SMƳ:IJ ##  w}#bw3b3ckv}B?-7 Nma6iHZ$mBƱhFlvSA^^3&L\%9YYmz e eVǶ72p8Lii)Hf͚^ɵ^7Dvr}hj>w!mV2t@wB)B rbU+Emh~,xm"GzRWnܱjVEDs !ּ/ZL(r[&kنnk2Žo:bܱ 蛢vn,PU 1Bqh{q}TGo7XrMV]97#rWFACrEKKKI&#C P(TWfopW qwj$уD ir.VbGx7U6L"ݸ|>" ~M9 ow@O 6 R_^4<Vc9/-w-"*"U@ծ1C3tkkuT,-sXCLGĆ# VoB xJXXvtԔ֭c񨛥* o`R DxGwنt2LIttNC׿ dXg͛;=xhr$_Dlz1l+ݗΛ}"jnD ĽfMUG<@F~],o0J!߀a<AgO1^87x ocͧE2 F:r^6vXoӣë#9xKڢd4B1 )ނЮ]WÔsC_ Õ__={WF)9m茼*L;xf Bzzz5};[x1{_!C̰xB,.nM_["W4eI/pg$4a<xUA7 u6"Q?aOН>[lQΨkmyx6 cc ܜk|7ixEU>-OiR֡WnR-|֮}=<3nܻ)kO8ڎlyyyV_ٲe EEE3 xyꩧ8ȡYd/Ɠà/ނl"_'2d0Ęx`;nfRD. Ѓ۾9xav":fQ=ضtu$}YdBn\- }~o'WnEl.ELrbj׺uY_VX&dsZL{^ZeF]Q}Xuh'kkšֻQM%Y8] ?k5-6ZgΪLwp!cxDR==M~7mx9)^xG$Cm>bwYxЎ -kGM&Ot(}0yZj ELA2QkToң-wǮ@N{6C=Sߋ[̾&KU"ICZQQ͛Gnݺu/[s1sLƎwSO=̙3+8S6FfH~`0Hii)&ݪkxEEEEE%Y/dذQyc{D#xC($n{m brȪs\e̳|GTΛ@j ZnNj^BM~_di 3Pa\!ikпt7c m&}صuӷ["An^ơ=ݼ7՝?Z`~vc]iUܘV(V5ƵzL/0/J Q4f^s(fPIÐҥ IDATk:ݤV`MJE?={2p*haud߹;שKtɓ'sG,)m{"f#?"Xtݓ0 ߌA~SZňMފXZ t߂3g w0H #n|2\K+_;=:c:x-1 5h'/@gː.@ywNPC}n:DGo@=c'; j7vPxwǫ RqbHGQB;XϡzWdw1`ji2v(xVTTT羥;U~>%GkP絚#s/[ 6!l]/nȞp #fh@v|ӑӪ;="} e2nwcl0ὄY(-.1˦nGoݝi:c"Ud-2X/xsEDB E&^ٕ-tc ݳBx`:z'c-|{@մXeD 999 ҩ8vn*_V*wQ!>11c F:,ITO(CS7T!:"&n"<4Ĥ-9/b!} m1YHy !`lvTy1^=PW @1wvc$K P=Bw֧5^oscjo|w$ٞvO=۽~G:JJ7ލ2BA=b{v55 vQP5 't{~_n.Ouv r&.Xw%kT朗*YsVq!9z)xXv/X0VHNEmҊC#vb9(x<ĎD,/Can+y;Ҁ_BN}k[KUPVr3D먪_ۢUښ,B~< |uGt,rb;ZNCt$s{ rLOwۓK-8"{+jV}c/$v"fc4G'd>䴣㼾ZrJ:u]iiiHpJ]FV~ IޝRU^AElS Ѷ=V>&i#@ 8f"1 :A6{tE =sԝ{lI7HŨQoxjs`vu й 9Ck~ -sТ/9yhGEs&)G~fh49x_w+| ->yyneZu:=i͏U3rsw߻2i:t QgӮ2̌'ZbJԆ;--RKKlbII It(3ϼ/ l[]0}KPCϯU -0@?G,Zd9"* AٌVzy1\x)[K׭n9g+ZxܼM~Cs-Vq#4 *<i$7:Nƒa+w][gc5ЦMA`biyxpE=[C|ӕ]Qt:BZT6ǽO2؊ꂤm|iҶOwۊ4Nl ił p?xq1h׿b˛jbc[Z$9mqHǫy .E Хܟc'!9a;9k!mh<"LGٿ!cЯt8-tx fN^H0.,C^Q&wk20<͛2Ԉ+**B I#Ѷ _@ƠUOWyu[}xIEe]RJs/Cmb@%@2A@ EZxAwmGW㕷lJ hBL.!yZDCCbscINk4Ľ`m :lr&G!VGXu:ݹGh#mx OF{4bݷ#m:(7A;R@y}fwMV.rF#<҂ng:Yi5L"[bP.<6nȳ>K-j9Zr蜄PJ`|Mf +"BU TTT)>V#;눕UO<4^{z#@<6 ڶx@`h.A=PW>^6abJmt(\DzA0flFL=Aw>x-"*,ϜV!wHNGlk-gs܊\ODKĴ sDZat~}c@1OB uoa@'9, tsxЂB QL";7hk[<.E>/sXlf6mڐ"$YSDSΣj+VZŋyk<Β%KxyU=cуÇ8JW`9S]$S~t?q2h0JK7!"<Ė !J3i>>cAxm܋]l5r-F MCՍx= wJ{e,4}XX?wqs/iZPsŦ<{E Dܯ#t_ljwQxo]U!EL9boFI*q-q~6<1 K.jj8&Mħ~0W^y%mڴSK2i$.Ҕt}l&Mj\9ӗ&tޝ+Ӳe/WC @Z  0 9ƠxQ? p<9m.BH#'Dn<kRWsCάQ;66$#z/V*Ewf Z77 =|0w`X꺙}kȠGQzz&Z.3x򊵚Exm^Mh1 1R$qE=flzp< Z>DH9Io!=ĒGU7V%MnN ݵ% 8m۶qW"I9s&?cƌIisF'qS YYY*fp@ @~~~u^ӯkAnoڴϿ>1HclXm''ȋhYCirF>b]sB@s҆@`lڢz 엹1DLq(1?+o7qotٌ':WG"SB |~ i1׍'YyC@:߽f9/>:wG7sy7 17gZr1W/!Icw7_}(DlwUV!ǚ6 n,Ɠ#ɹ[gGZCJeeeIZsY5up W_`cǎn{p dgg?߿%chufffR["u'*dWڒkB< xݼЖ|K>E:|ZVw0r MH۝/^}B}.!@|}E/b ^K,kZ#kE2<'|1rd71q1`Qp  XrVVVFNNMnӧSRRqQ55:NlEEM6M D"JJJT?Q[f??kYf bI~ތ/Bp6W| ~v8 Y2Vcju;ލgҘqAD>qy"/NdE||F2KJB,g<-9Q$CmB &233lFhalu16 Ǐe˖r!y'ZӠ1seeeչV_H$ܚ.4h&{Ơ4mYo^?A,2$ӞLĈ~O$MGZ'0FW `X&(CK>k[5L{/4 tixջD6̚-_ ]5F,B^,iƾe]Qso7HK~ S[_vˑTYv/(:f!Z!OsMGPGs5'[ю@:iiǯܹj1.9Ջѵk߸х0eee0WK4 0YNn9aL,w]Rg䈲[൑2 HbYW'"&rj&t!gG ~(ʈᅓY2eLmu kigrb[4@"&eވ>I;7Z O [#6sczݓ ҷOq};gsW|s >وn7u_"8]s˅D"{ӧϑtWK܈M]5 M!@T@?\}"1j395y3۵ζm+5<ϥzUșw %4:#;'ݹ_ERe_l{" -2zs~h4-H[=h/;&w,#1aĉS5aX@z&7)M߉b欳BYvnW|"g/w}9 "=pǸ %tz+y{Yȑj3vtQQ:ڬ~NNN5ͷY5Λ/hth @fA~h=bF/! xa69oao"l"@L"sx 9>@˖#}-d,ٳgзo_$zˑEa%x,/z>nf `N:i(x,,.ڲKP?b6w#~ -G!-5ĺ/vtY"'T]lM1Ku/Z# `D,^&%y׻igYhMRK e8o-͚%!fA (#jz motW䎱 mz!F雏 ؝7mCyQd9z (Ҵii۶;wbcOGLK{;n:mu5nl |xE-ܮ-XE%nnurr{nwCs4bWm@a/"@@A(|4U|^(jNnnZnyGO&X/]V5k-[@ t|q}ho]!hj7%!-JF}7^Gb" F:磈MM`QxF(9E8@6mZѭx !ZԦL@>}| [1+j*zЮz bFP], "p9fs1,;A] NFi( YD l#i̙ңGe:x#Yn^$XvES#Dbeڴi SN9O?wtn>Oݼ$h_>$GPZǁxMFp& -NwNl/ԑ䩽; ݫ;1R֬嗧6.+,a~1خ_o/j 6ܾ}; TTTPRRP {HqU# Ex0JwBu9>Gw(9Ǣ6wcNJ `Jp`X"fTXWrZW<[1c9^{;ͅ%ؔţZڢEiWFskJi?L3PV5h3/'{-嘥I=' mZ7>Br+r"v?hÍ7O>G,;5Mnnng'95: )..,ߐ:EB-z\ia\xK%zq11h #F;ߣ@v{QtMۖ_^o!^? 1у T3w.n͚qe!Ƹ,w f IDAT\kџh+DZvGuZ˫|.\DVVV%A@9^ANܜ=vK.b-E-/P{^:z!~'軸k|)kpYnAR=B0 ;;;n_i(:fN`0X tmsg#c {y=z^E[{kZ ab< =ү/6S"`NLwv(wK5 }ӟCeP\\5q$ QVVĉfm4荦h0^N'@{lc$ iC<;S1c?/p_S$ I*1H~:-6nE,9JZO=C wIYȉ2[ X?EzFzʕsc62^~sXa~ꪫ(//g͍.++EΡA̘p4xA~W ե;}Ug ))x} [!f^@I^x=h.6}ҥEB[Waw)u]b}4Zf D`{$}lw]v?tfg#&/"0qD i~EWcpoBz{  h's9Eٝ%H*pVcT:̖#T܂(Y3I>e/w[SĖ/:#i]P݅h @~qUoDGcnڦΗh+</jb;nIAy->ڎ(SQQTw7!fE.W-{z <HI߀Hf#Gr}Uh:b QP$U FwhXB~b 1n>G5`9"q-dC7o'F85lڴSXkC/@vO>deVgoMӟT1ųF)qԵzM BI< G!]_h+b1p'K8tE vq, Ę!94Ѷ.@܏c$by(yHJLqoذRX5+*̡;H.(kjx@ ZZ& gώ'{qY#9["@R$z/^GB!$܎Sc/B,4GnAM_Pˆ/VVDɬ>͖kH$R飯b5kVuFaa!_~ye3g2vXnV"k׮eS4GCl m6222R͛Gǡd:׻k@ /lEP K1>C֣;];g}ĖOD2,h!n3gfuc~kҤ-(" iAT䀛QC4ȿv $H>D  hĉb7A ۡkCݛ-Nv7ѢtVc~9?_vZk-//'--M?"e׵G*BK%;h :*L0H֌ $Y\^^@]۹65Vx8]$Xft{mE >@h%HCC9s!"@9* ʕ +f5ihPb=buB2T7E(l^ū<]< U,[V{*o!CCs_tqimFхhw> F"Ę#F}3нjx-ChFȹxKcQ#jfٲooʪl,QmHO:p8\/A[G0X|qO?'{ ⼻ѯ[h׏hl)-^lrC(Sy"&b\_*@ FF訐'E2!g"6YG:L Rr-F߀"UB dam-6Y nNNNtAmSl۶WFu$ByHhW|!@jt^E BG!=Z_@r 7ظQ #1,a٩Kضm;vqv:VwP(TZ@>gѣ@,+^b/"9z Wof)+<9JDi@Ę{SUnG[cXd^esFFF僜xղt7bf"]KG;(> +SfA}f%ДHjkv=V-^/tV^믿΅D ^Q\{n 9Gb$>Niov#(Gx !Y;"z;9n#mrd>ܹsѣGm[6Mp8\o$4B14p;Tm]2!zz;9E9xMUKr(EY}^ >ץ["W%d P|$ A ֺbO@rw#G^}+rD j7i6wML |XV!ds*εB e"]E ku .*qٜW؉7kX6nNh<_ M\н<ݛ49FbRSТ|5r~α /'ls4 k"Їnk__5`g.Z/'(Xtgz $V4;;VcmҠg!Fs"U@bb Eň@"R ^[ TBr`9 6[|.]V M#ޖ7UOA ^&bkǹ\(^7j ]ܱVY37_G }ӬYEZT|Y|~0mF;x=]Q?(wwhguҜGL@9{@~bYr,>n4iUͿg[ MnLNF Љv7@i,='T6dl,K,_b5J_z=سìCP$h /Dr6|},bZ"0|tĞ?@dw"W_}ƠAJdq-v.C!HGnȖ˚"-ko6Zh{EE_] ģ;ddd~QR?*vIv׸ jnF@-Z#9$Wu9ԽlEz$x2Ю[ߜd}7*דA猌*kOT޾}{v;-@[ w"Eu,$yx!z~/D~kQܩ؎ _k6x5'vG;"Ԭ-AD|lErltҧй+Cl~jbɚtoBx7L$q<@>mVo:wg"'daQdcv=YC=C=@tFt;I<3H#'2}DN\t]%4]v#cѽmC}Ђ ᨒ@c7[+K5LM,D6iٱXv0l4%@ѬY>7nlM}kWc7EГa5)'-" ; zG Kv|!=1TRM7]ǨQ*TW֜G!='ڪC\,+ GqUED[1ha ?28(++#--ގ1?>}2@ڗumB}bȑIAߎ@#m F9qǟ[wz RډkVɲl[k[Sb˗/UVZfхj3B hP_?oC!nl! w;+(D̳bJ{ra9 ^6n*gL5k=v5fB/ P9[cmұ8}qHEeEc9蠃vHp80Pmۈ bCQF7t/"v}"A+}<,^|/q]8ټyqR΀,۞=eee 2td iѡz^̴o?kׯ{/*rLu:CHXN:bE1U(6v5^A(dr|M@:{'W\1ʗ!Yߎ8 ^$ElR{Wږ߅Xh -THcT&>1Naa]iٲ%Ϥ8˗^(noÑ]Fw{Dļg#{?$}KoG,{2zCY6P̔fХL<=ܓ=Ze&KACzɲo3d ڶBIKG d]C솊c~Vn \+]@Aazl23| 4*M@'pEVTT"Mot" r{*Eۻx-@zC1lԛ1"jƌ' {ph7i @hZ7Ga,Bψy*wnv槟*ς P*sy]lժU̘1C9MPlm﾿EΟ11z0O@E=nF31ć!=Or9ꨃx9k,$:I#lǬ-#^\3x%L[QqX 蒣QC}@- ,$a ggg=ٺu+:EV#A,$0t=> nO$A!{g染 XAΜ9͛73tI:~F^\\pйעis.:֭[)-2Nw9^wh:G,+<}ű3M,_RY2QDh]VVƔ)SСOXw5Z .$z*Q`:},(|Fdh;bo>Uڦrאhx6d/ED т=^ }gNF;>f*xw褠[]4bEfXշ~˺uX~=zgϞ5qXetU))) JYYfƌ}@?==zQbm@"!ե@}'Y["45o '_LD:(&w/ `YcPb],EAX_=}݆EZ5j&i4t4̴iӸ:u.XLDJCEv˝wR%A'VNE,0o<-[F^2e SN[oSN);ώF /--%''fB2Efͩ4fdd Yh6lSNtҥ^m26#2[`^qe>z!pyI-K VHH{?} A1jpŨ&JJVļd!9ڂ ׯg̛74w_{otLA4uf`iӦѴiS:(N_@NeHNc=uD\< BME:@_%xb"QsBIlPtXs@ڴYƒ%?%|M!c+^I҆. %}m޼SrAѡÎ #jЉϰsuY\b_A4rCO4(~bH5Ž^3-%fџ-iժ˖LzP?7%㨳"~XQ^S8fƌs1$*--墋.yhт{eŊu]a222\?]5@z o{UйD{Gųde.Bf %⊫xP(P%ލסĞBc6GҳDEn6x,ڶmKfpNN~d";;( 5M>RJJJѣx`Rz3K/eԨQnUs)ꫯlװF$D"P&5G"]U+++t,g/NښBJ6 '?*AL$D-sooP5# ` 48edd488CA8&++*feeez9ԣGW^seɒ% kŊt^]ve啯?w XNOOOF朗ΘF@C[*юP8=sַ},{9KQE?^V9j!|3*2tz K=;+k=XkēH$O?ڵk9HOOgĈIK.,]C9KV5Ο'̙C޽S6TZ8 ƶ &5T PTfRѺ[*J*mgH)H7!G1S"|ꗆzo m *=ʑՃ *&@̹-0_N8!!ggf z[ડO˙2e mڴa:Vii)_|14mڔ={ҥKZn+B("pI(QU4Thא '$jT:vgTر#`&*Y4FbuITtw"QO#8眣x'c7:XutDC]f7ndڴivan:nLӶ}}ɂt@& C U.@yymB2I1}{'4(TT9--$%K0|] klM輼s55TiVogD+ĒzH$ٳ9"Y T+T؊%݈*Eq9r2~TD q_gkwMAkTΪa\QQQyRG2z)/~m%F!1fwh̩տvM x?L*јXF(b=`ݺA5C|!Gb?w/Xȑ#wխ.k,ͷ.;*t/EKlݺ)S0h tPo֨бNR64kɢdmG&D+oP/Pg7!86LJRcGH#HMjGkk"sXx1Oxщx?裏VdzǓ]~L\< kg{њ,ԹycE l߾+_(AU$Sx2>8s7Pӽ 9,_v1|9#:v"'z*/Rl~qj|Nz]5d!m)3C Ś?ԃqbKJJ?~=J8/iњzRQXɿ6Kάc՟=zcvn4v-2̹&[j+W3`Ĉ?$u_jJ|c4Etm߾3fzjrrrv<ؙ@òT0 =&b. `gjW+###:v0dԩ4o`Bsa…dffGܹ֭s.TVۙzols}lG.Jyv"D濮ŋ3w\D"~I߳2 G}T17vkԉ*zLgg@0aܪU+:wL~~~NX3LؒSJ/Jx&:TwDFفv=YYY1o,Ku:u˩`Μ9wyr)viYmgb222ݻwep8… oͥe˖tܙM&T3@ X;ʒ  kZ䬏5dͲe駟8})3L8cp%KO?U2:uDAAA}ngk%4T !Dt;3#.)өlaH1c;ۘ(?I{EڢHn-cSp Xdj2ȒeY1%$n&ʆY&}K2" KB,cB ZaQv Rmo/1n9yξ}n\Mfdd //P(8uR)Gw h 4 f3ݻle8-7s%6f uv>\ yflݺkC44˗/*   \pqV(ԓ/q @O6` bJIadd* 96aiH cF#D":t:j5AQ;)֙&+vK0p<x7{̉U 4Mccc8|0 r111Z@(B$A.C"8FD3-6!;NppLIcݕ1[ZZ;vx"|'`rrSSSx⼎z^^L&t:!Hxsrrj ( 2 6lXDf=L}/(7ڎ4\ՙHMMubojwee.\pf(J[ضmrrr?*c~{|] V?ϟdFSO=>B9yU  ..۶mCbb"Faܽ{@QCzyƼ*g|7Nn=3 paهyh4"88rr, U1 :DVVD"SqUTT )) J oX 9siN];)) _}6n܈!8p_t?;6ﱔv[p\\=?22|ttt 77k:{nz* 혚b3< fEQcި{=9cфI|4A-e[v=b`xxiiilv{'9..wqڌ3ɓ']:TVM?>>?o̝vJtP^^GCQv܉Zt:dggh4hnnVe5b`ffAAA(7pp0d`hlv`20;; @ՆPPPP,ĩ_~iiihllĹs眾3x`㋽%''kʌiנJ%>|~@OOpqi|,ccc.=eJ8ofkNs!=&KBp[ho$==FK'f={_5܊s%Zl6͛gO}8yJ>hsω@<"`Zhmmb={x$h718z(B!QZZłNtww[% r#aoKN-asZ*E<9x2==&<ؼy3&p I.jqYʕ+śo}wHHH@ff&Qj ]]]Gpp0u|I^$dL#000Mnw9;% ƒ ##c^'qE!5J^s `.!lݺUZGee%z=~i@&!44k'XF)XVM QpQ_Ŕ"fݰìred1hf9BJ+QآjY!biFsŋ8?]|G۷:c_XXopzbض ۝ B1w`07nĮ]}ŨF|TOœZJKKWUɓ8z( LT*ܽ{Eek®݁wׯ_G@@^|ENM}{ reƮS j5!  !JVK/ gx477cǎشi' Ih4 waFGGC*:}iiiVe (Is1RZlg0ڰ~$FIk fŮ]xqUs >t?iZESSሉAHH I˭Z.aJhF{{;q!QVV`΁?9Ƶ)q!aeANY!** `bb~!oߎX466B$A.C"}DKK y| `O!R?ZJP(EQdذaS>tww#33ojoBJN@^^P(p)HRSSSPt  l^hXp X,ٳLA4aE` ֆowyt>z* (Ʉ&<HHH;TB4aE\9r ahhxAQa|R&kBoزeO'g`n199k--//& :ŐH$&$OS[[hZ䰣ހiBx YYYFDUUռ^@kk+OQ܅8NQXXZTWW#22kk :t4MbkqyWƶGFFB$y;l) Eb熇qeT*ťK?%p9A…  DVVrssQYYežRdpp_|k_RRss>~8 @4FGGQTTx%I89|0***H@Rb1IA4K)ERSSQUUPB$1H@{JHO00 (--]UwB4@ (A > I࣐M > I࣐M > I _VIENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/ellipe.png0000644000175000017500000006002512253362407022662 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxwxT@Oz " WAPRĆJQ:`A,tUG))!$tJڦH$fo{0;b#P] 5-b````Wg X::::::::::::::::::::::--֭[?_3LݛC:y8=@O4;f/Ю];Νݻwtpj޻w/Egpʕ+PL._ =߱c&۷s z-ʗ/ORQFDDDPT +Bʔ)CLL >>>sm .k...|Eʕ+ sll,Bgacsw}6 &..{SO=f3!ѣNVB'(Ə/{qI }ѷo_1dȐGߠA'X̙3`39+zΎXXD>`PPA#P}d}l"88 6j-AArԼ|90vcWidjk %t…`uqͨ欚/_Y&7;‰д~MNqUm@ev*& U[jsfW;7̘ݻk?8ԾЧAh(xz[VQrm$ G5g|A='u\;/8C&{|4k&+%&:Cm$ G5g|A=gkB в%DEA~rR%'>X`|)l(!3nۖsKP2@IBQY5_PaX9>rJgP4@IBQY5_P…H ރD3UK#KlF\wߕ~vYF)#I=9 j9_}q. +Wʅ#%EG/1QοNe3G$՜Uu/;p z1ƻ}vml&gO8uJYCm$ G5g|ABirгg2emQQЭ> -Z@h(uooge %$՜U};'&ʢ;,ZS,XdҧvuݾYEm$ G5g|A7nV3r+PYO]Ξ{YӬ+_;19Jɠ欚/It.2RW(ZT ߄4ܹrC7(96ڣj?͛eP5 :ݸQi8QVc6 A:$$>}н{w ~=00>}0Jh#I=9 8o*7ݹ#sp%Ŏf߀9b,)X'Fm$ G5g|˗˂Gۄ f76>X۵K9 *Q2@IBQY5_pԩᇲ?&KC͚嚾y *($欚/8Yk|8W.ذAnFт'&'Ð!\px-9($欚/d9-M.G(Xv+6Ȿh 7/,[&8y bx%$՜U9'&Bǎr&dI9@^MxlLՓge5m*hB#+'FuFP{TsVw{W&J`dkGL"O8qϘO>0iM0ڣj`krx9{Ng~zukքGa ]gPOykBÆPpuKnEܾ]nM1CpqSO倀־Ǯ>v\\?F7MN0%I܎欚/Z\!Zlŋ !zޫ  Bxz B̚Cz͚bٲ`9uh`nĭ[OGoIӺuk=PHʕ+(S /_p$՜Uk0>۷CZg,hdWħί9&"QvEo.O(;t֭[9z(*U"""(UT{' u0~G˵:u|=/_ eP-9 y͚t&X,[&w jC僅 ,굍9ϧ[?emSF8 +]d Ddڵkl8s挳lF5g|!݅X6>pF! ='ą 6ݮ6NLM ^#gV;/B竷9h%; G5g|g͂Ϛח"]:U} zkoӭrݽl4_4/w/@9h{0ڣJ'çg06h/^_`Ay&֔)vK_L_:׸pW|}OmoӃ37;(96vjjΪ ~ sƍuhB[VǭZnTs*Mㇽ?@ezoT^پ@; UC5g Ç'ܐҰFII]N28*w f3xo^jͫW"Ŝ7tӏ Π> NBQYÆȑrĎ&/BÆ0g-* ~3fM>#ܔQi^9'dL1x$՜;v,  ΝUy6YK]Y@z xi=>'l13\-11SN坪ŪQzYE$՜;uji,.k28N 9Ƈڟ86HX0ޭn3O؂#h#I=9w\|c-Gq\r3Zk1}a>AIBQY۶o#~9D6;߹#+m(ʁ ic! ~zh*>>h5#=848>DvQrm$ G5ggY eӱ AsRE&+Vnߖ~캸 ;3剔[z>A$T ՜{&%УG˲ʕrŋrX~PgM&=Tg]cGYafP2@IBQYǎAaߛsj<׹3$'Ï?ʃ\͞ƿ?UeѸ3xN>A40̈j}JNqIBQ+r)a0dm?My^(TV~r–6mpv1i)PF+GP[C$՜s7, Zy/:[yP εka3dS)?ʳ+(oƯPrm$ G5pZ[?6@NǪ))0o܊2kݗvoK?¢t&3špj}JhhR9|/_5e 3<䜜,'ϗ;\fΔއkzu>9N0,-lN+f@>l %8$ׯC!͟%WsCb{7ﯫ q%釦SiV%VZEɼ%_yӃ3ׇHjjZFE9r7=vk%#`lA܇7O ?gK&P[é#=zЭ[7F~=<l ' Ldb-Xϟ /^?[˵HݸWmӮF:&6og|.acZJ`?@h?kcඁ[CQճ*?7͈NٳgǏɓAO!B˗?вeKܨQJLJ˗c6)[l}vʒ%Kl?@Jn 5kE e˺AZi:!Xuj7q-ѳnO\9-BlرcV՜囚*DǎB+ qw ɇm+D\Bm|ilI3#wCO'ao.]/ t1m+FP{Tsv,[N k&D7v,/ZӡΎ݋]Ֆ퉌*o?/}#wk %8ڣB_E\B6Dn܀7ހ#G\97\١%9- L`쁱$%QPfū^}}+}BO(96vjj) ;Kɓ#G?|;. @hL3 =HjjN 8۷%uvy3. ,/'ޒm|5*b5КgRP,?O %GFP{Tsw,yJܰe <,<7dp5 .48lTMokNTR{=lSp~#Jh#I=9`|6 /iifg<\SC6>y:IJKb C8 +~O%8$U+ WY.bbaXzY.j[ kKBROT)Z%[ =HjjY]tEivq4j$sj2hcpǷbfΑ9TU%!K(S헳˞lgo HjjΙn 'XABvV2gףG>vWWϨf(]a_*HjjOݵ :v,.Vs"QQHM6xRsQꗬϑGzC3*($8dr{w;.\׹3g_N mF>JNqIBQٚ#a$fdT_ uFmSNws_G{o1>E8 }B5AIBQaߐ9E/w ~uKNiZ%s< q|J<_Zsk?r?5@,|c*Hjj2ܻ'lsҢE ŋ~}߾}<̙3e>2Eb$ G2w4i&Em֮^gOغ5G܌3ghQ'dL1 Π>0ZC$ݻwׯO|ŋyr <3\|9$91QJ%ʿ&YB7N"O4ȎastXJ)WvշќOX߸qT>4K6mڔ .Ef)S`20`@RJdcR2s `ݺuOOZppn\TwNN- ʕXD޼bŊ̟Lo+&1wгnO?Kquq}NQ7-&ѺG=KyTJ *QIOF$aqwwg$$$Pzu; *IBыzSPJq.\{] 9lb;ɱ ?gbfj͜6sh k:FU_!VH{~wʎ)ӽM߉YMz={:ujH744a9,\(\};Ԯ6ld=öY'^8`ӹMkϴV(R_ל`&bT\F rWM)T k(96 iU>>4F < }{M&{"MsrZ2c*l:r˱ͬg]zBpk-&H\=])?gZUfɳuUzM¥o/q}uf*L@ЃovQrHjON9X!+úuOsy{jk %GFP{v޸_yrY&,] -[;w((^ć5>Լj"|S12"* jSȦJh#I=Z9HI˕ty›?y<_yO\ps}l4s9ZW\wHN- {Ji50ڣr'vR̘!|,Wׯl:l、{|S{{fks,8zBK{8Vz#v*%>.APRvgP}HjL0q"||옌׮3ʊ+|LIk1uzfW}TZ&'q D 0yf_#h#I=t>v Z'q_|7[ܽ,ƑCfN|is>n&ܤK.z;%8z‘iqi\+&jm^^T^YZk9$8zkG^9KcxNP!yw=c71aMŐCHwD}\0'W\vQ;X<{[lQrHj#Owe_ȑycr2˖B+W)l ! `ඁ\L,$pb쨲xvDSFP{|ܲ%'L+E }}þ^U\L*8 7\7 !6Gk%0“(zaԣʚgP}+W6lNiӦ M4r 0 5#I=q>qB_?>1pY. J7)- L`$P_+3?TpqEb˂Fr9vjk }}}Yh#O\\\ȟ??<)FP{uep391yVYS#<~7̦  #$On3/p:SNqqbSՇjQ@=rJIUVVfѲeK~MoÇ(P@19>EӅpuo><[1[%` w.<IHg{\=K_'{s 3Օ󓚚~-""XnaaaL&zf2t㢕s`-޽`nHL|}z vs+ {]L6SjWTRRvQ~}W&՞DqNo\;xn\rd2Y}݇9yTXCp< z`l{ɶ:Ztttwqzk/5rړ]rrbau…л+n7۶Uz[rQ\]\j6ZX/$_K$K& %z*sD}Jhc'|lp~ѫ鬉aYOͽ+.M]%0۾I=֜7l'bsmIXQFg~[軹/#qrFF7gDžW=&2>ɝOU g8LJsen%@W /ZvjP2@$Tm>,22ÇMە+eMt޽P 4ksSw`?#)*fӢ\l9lIk4Wg^%ǍKR_Ire-ƪZCm$ g?իc^ U*)R>:^#U1%xjm5$aRB= ֮}(8/[/(srg`&92&i ]Jopi6 j}pi[ns;%SBZgY+_-QJ$6X,rߴi7o6lx`"- R*9s S2rHL&) 3Z͠mwWQBssMnI-Y-%d䪐jPrbP臘B׮2)[ …a|kr… 6߫;Qn{hXV"2W"W. 礚SCEq#%&CW K6NK1DKSN=sbAqa'bccB;Vܾ}[$''nݺ !?8sHII۶mB_$,.^(iii3':w,"##ӯ-(_ug+ıc,&jsBDEBj8WW!&N|TAD#n?A"Am$lXDxc~18=gwvϝ7nfY{B!~7/"000G||Eܹ QE.]B[|R;v< Ⓧo8qD0ŝ?[*(= a?DX0)Z%5k! SNB1qD*fSK&>}Z 7߈~2[lQrIB=h/xv :tP'dpLU{F(W-koS$w&@Z]puqNZComBCTlÝmwH_穓"mPmaC\\;voqqUFӗ~a cQBORr̛7^xJ*9S1S *& h!x5{WV7WC2cDr#t%\Uc[PB9<"H8@bpo=_;Oi2ޮnSm SxwӧJ*^;2`y+mڴɱAm0<3L43gΰbŊ׺wD3 ۷ ;҄KyE7$ň[ \#./BLiށ{"b\8 ? {\#u-DZ|Z &f B!f3%ٴi([pB-(9Vq' |\W;~r>;s#3z덛mWiX 'O©BH8@Dە/ o|TS!=III,\~QLi޼9}m<oƥKx72e Dw\!@Mg|+W6lN- z3׵kW,Yö2o#̟''Oz/BjF  ߖ~쾴j|ė'R> ҥKON޼yqqqUɭ N!>>D8/g-8uˢEhݺug嫯bܸq۷&Mn$ Æ1 VA.!)IV_7Ù>}#{kY6mPlYzIÆ ={v '$X,,[!C0c ¦Gt9q|}}xg|r׍$a$%Anr"~X7H}aR0As~ l<[q!%r3h>{3<"M|-d"HNk&-. ss4},is0ݼy3mڴ!>> 0`N @Q =& _N8tGMz]TT)"""TR^ߟ֭[@XXXzmUr/G?7:>իd0~D ^.%쩧0m_~I?[\wזƛ$",2tIDAT;˿ѿv<<5s>s Olf;oF **s 7pqyz W/;RSSIMMݝw}kMZZ7nP*馇$ajj*gϞZj׮^Jqs˘WosN F }&wGK%`,:j|3NoϹrbCݽbN4{(?bӃ+|O|pOP.]ĉ |U;w2h nܸs(Tk[t9FQ-3RSqڼ\Q6m ث|sUVl= ]a|/{(_2!O<鿼yX/^^z+bX^z93;tPƏ@ӦM4@־V nTi׮l̙3=bbpMyW 'ؼ$dŹ)} sbbSNB }{9\w:ApLX/LTBCv aqbiODէf\vmkѿFc: )R 4|8d^ct@@>xt}cbb0aӦMd2aX;wnnQ}"r+ʁrݺB^^ƍzU-f1\Qp|ASKCf%"+ 8'>a|aqi%?7Bb;v"...}m`/^K|9آ9Ip6tlªCx|qP9cNwa{_= .{wϴ$Y.QFq{mRSI" oo5GE)ҾgQɄP||<'N~`>}:uP^=ZѾfwRHʖ-ҥKy`5 ; W.] %F~woO(T-^F|3EXxfuȳZy{򛞋 _)HLjW l{9kҥK\z_|dnܸQiii3vXJ*Ŷm_־P2@ϟ 6Nb6wr ^nj+bJJEAk1O|HŹn.ɍE7H8%ѹxP]v,J6(-_gĈ ,Hɒ%4h 4`ܸqٮZp^x!8Z"Edjk %w$;w|܃毵sh !nѭV7?Kz҃%BhN?EA\|S 䩕 +Z#ZbKqvZ3OOO͛Cwqq7ޠUVYJ .l7|\q],7+F9:ĔHQ'^,b 7ݔِ/}PJcwƌ5q#/tsZEӺ[6QGi&wFDM9͕2 @Vfƌՙ_|Azl{HIdªSTTi޼9 +ڵ3gPXXHBB.Ãx^lք23[OICxmޟeZηo,~>"VtZSؿbOҪ$z~;_o^oګ`IoXX.A}'S4Qwǃyֳo 4F5);;hhi8-pªoHݻw}6͛7cm۶]v.\޽{>vX9v]"##嘛;v㡡ܽ{W{zzʱ Ξ=+:wL.]YO!ǧO.9ysww/8qt9#8vٳgFVV7o߾_֭[,ӧ}:t9xb9X0`߷or|rʱ^zݻILL֭cnb׮]rW^aȐ!r|֭$$$ȟ?g֭raÆ1l09>c N|8C [CMG5feeqY&NHFFׯ_4V"j1Ì7ɓ'3k,qvvF`ggGnn.$?y}SF&Mx# ^F!*Ў!T>4i'No߾A ӰaC81cXzj,5a^c528::BJJqTTر҈Y; % m5ݿbWK$͟ g?3`>__333z-Ξ=KJJ |  0Yqe’͛Z MFII F9).PrB5>&-NNN$%%˗ʊuZ;;rk/tsQ97J/5--,In;f#:}ypW67i֢$e%ѽ{wv;w3g&>>?GeJiUmk׮%44 ڷo`e˖X[[+B_mPڴu]1ZjV6\ѣGٸq#( :ooo7nLÆ iݺ5NNNDII ,[\?%.q_2]Y+ċ{q`kkә5kV%'^I( `ĉXXXrJy(SoMMӠWi&*A7d $i )b%ou68oHrY^{ڦMO?-w◒駟L-A&22R$IxbŊ*FIzk4? z::@)&a_n'o08Thͩʯh5ZN5PT0XܹСC%++6?BQ; e]7>dKPAd3e~?_equ{h,/^$++ 777oߎ``ʕ|q#Uhc%%gQ {_ERECPvd2vXxV[PYQߚSSS 8s"k|TYa&\ӱ[̑A*xt ::G˝PYQ_ٷoaaatڕSE@קIh0gA|u*|x)8icѣ#}O5ۛ<|}}(w"r\GL[),~a |L`chޠ9{?˵_~dȈ!5*W ԥ撒g///BCCfu=@5>,umJt̻x{g[esg4ϰl2>ZǬF.4'''sq(((ӇQFՁRj,I[r;-ܥff a&M:QvΞ%I%%%bԲQVoU$̼+g_%\tv/pegѬx}$ѴiS6l؀VeϞ=ѩOKMk|TYI($L_^s 8iX@r:TZ͊G|m ]v̜9ƍ׋<9VBQe~\WK}ygx̹ɌU3VCѬI`Wa,]|||hԨ`5Obk|TY$.^=a$'SO>CXX֓RhVT9//7o/&-- +++c$XGQv,N.l_鸙wtY\]QoqGSEeH)d< 9V:BQeIx s}3Mhs˻Ƅ2PYQ0YD*_Aュe-jαZz* 2 %s`@ワJFaM_jU>3K_Ī6BӮ];LfI={6ׯ_G2gSKr ,ULl$4u63NU1j1+bcc1 j*Z-'O&##6m0bSKaԦY5>,M{roMiœ^ˤ&RJ6+$IDDD|'?)% %:ԦY5>,/ݵ+$SH!CC4/n3tFU(Ѭvh4:DQQgqF{tPf@t:^uf͚n0ǧR$]7Djq۱kvNս19rI뉍+8;;zf͚1p@izI Ç7n۷o'++K~KA۶mٶmDDDwb=p)*vV7Ο?6ŏQ#͹ I 88֭W\QF^N:UX izI tjj*杋 )))[l)#DG'63 2 'Nѣlذ{{{Ν;Ve̘1L2AТEj14%j ,$I1@WS<<00KKK^{5yŅpߟ~!ԩ=P:m+lk4M/O7;;lrrr|r}K5&-:ooo7nLÆ iݺ5NNN 6 (,,dΝ'&Ť&t:^^^4j{{{Rq̙SRR^DFnnn̝;JKIMMeŊ\vK.Jqugr!iݺ5˖-*5111رBeףZP25MIu5 vvv`bȞ={*TrӬڵv:J/۶mcϞ=|wr{uG&)N QQQcǎ:QrC9ZP~+Uz~>|8}۪{=Eh'''ҵe~=11'''iHuj)%&M6M (9U-C0a~-/_&77zTRkf"VƆ JJJpuuLNcT@ENxkRARE|ҿ~.U <}85ࡐ8p Bd/)))(:蚚^xΞ=ŋ0`111L2o{:^~Nxy"Buªwa /|>T &6@/L>81wZ.=_F*AS<:1=IGz^6lx wKBtzGN Tj\NxgRSq> r3zo 81G?e8!~;_C%02k#bB{:{c2Or}DC  :Co޼t:cg5? $  9&IJCC/ *Jɓ' jrAa T*W^lg?RA.AAkA; k"sЖp!g:{Pp  qo1pGcFMϪ> 8g0ՙRfP ! 7w}Vٵk#Gl2j(v waܸq8b 0h{; _)mAAA^]so8{)PWP'HpQDdsW~GJJ6#(//,K.5ŋׯPPP J(U[J.,,X+lF`cFgBr85_uL aЉAhi-[SS5AZ' ӣϫ\y ػt٩fԩ8qѣG?ӭ[vǵeݻo>&Mę3g;ϗrيĒo ..A廴?ɋuDZJZQ"ٵs5Fo9YYY]A/.2;/]V̘1Ç3l0v؁Ec۳[nwiii? )X,&|FU 2aPȪhQtc@^M9y%P):gE=I>xcGhB5dO6_@0*I1k,0`vB,7ofʔ)={Gydϲe1ߜ cBR9z?W,U.렃lLs4Qo$j3kA†^h8$ɋ]g15(1ڵ}go]Yn=z`˖-{,%Rk0mË/޽ЩS/ª¸RuNZl.*5J迢_R R$8H|G`jpN[ͅH!D!ev`2( o۶m 9993g???AV wv=HHa L( Z leYYA!D!tH(ܛp/ tU'<"_Jcc=o_n;p=|)Tĥ?^y.G/$**ʦyK߾}Yj&y摙i}oV$^vgí;o3̓䐳,Ǯd2S\\3z:仳NNZ2|}}$?:ⳤ:{YVa4:'Dw<ƺjKH!־\)9m>`޽ۗve]{=ƌCRR]kP)Pg^V6|r*s8}8|}ZK:hFl!RXXȽ#1)42>QyRy-ɓ+}8ӓ/~m.\`:oF$LOa`abh"cS&2y=j?5QގXU6X27)^d=dt{wLz)Sϯl0< }==v֭uuu<.k)?+Nc&<RSa$nPfc0]f' ^^b=iϑR6aj0q1뱴!uZC4GR_PgL;/$ O?-ɜ#ݻuI&g"#̙UUs'[4<2CهԅUѣ*qI!+,,pj܏rѥL{;H:JDzQ=8W^ye˖VK.^^^|,Y2)T\V$˃  $D܈aMg7P^KI6%KȵjqImިPPP2uWs&*|Qx%c|MxGjᣪ%HaѣGcQXXȟ' V\\R$ܿɓ0uӬOYRsRJt&%`0#9%9SxƙKkKHh-#a r ?辴;A m^J8w}Zwߕ ʕ+9z$s:  AǏ|X]*,#9GҍQC9;JrqIݡ0;h@ee+;ʛbeO:~O8kI*]'AXd ^{MHF* 1㪸HXY s9gOO X||l=;JESVZ"evj$pt_V~T*?w7 )AcvE=x%׌vY`{槟~⧟~l^G"aJ "VktG.EyDZ}w-.cM#n!9t~-YKkMERt \i-xz6ԐL:5%7K|x{K_*]4 oKQHvXߜӧ[oͮ)S S8Sx~HH6 P%uG޸ݻ)JLW޺@cemi' ĹG]zi&Ξ=ѣ뮻$Zesܹs[9r;wtnGX{χZxزEذs@ s:""{HK:R:t@QQQJ(ZW h-%ľv#̳y[o믿./V_ Q+VH cCg2HPn)Έ?`|lkˉK:谰0"""0 jRtep\(N\ d5卞qCE@ t< }9s#F0aWy92}tJrr27o|~QHwX:w( . 3q4(I\p Fzt?:hhЛ (jN ~~D>ChxAx7yg.*Yjd+F$4;XLoђަ8h&]Z5nt73}M/t}+])W%;J}vN:Ő!C{=t09Ylc ǎy폳3@Wn :bCztc:PyrA38G FRK3-k.N8dhv3f C ̙3ݻWHSE/# #Clxt8$$vgOUX%,B>e=& {ձ**UG=uؒg ?=#ЃP\ ^{!䴋J 2*8峢…bGKaFvk3Ա)muib\$qGb0ZMq2h239u#wy.G;Ǭt{Q tcǎȑ#3sLOn̞=XvMJ[ނ>+/èQjDFBb",Ync}{yq'u.|_Ir/"u:>guغJ(PփCw D_z ?/Y;.pge1ߜ cNJ%tG;-7z]jRn';pQT$lAύS7Rgp܁_c3NxXޣV )+F#LbaU][oE实͵]TPtGRRRbG04kqaC%~z΋;;MqaDA}^=KonS__ϣ>J@@h"Odn"2zK1oy翧P=܃s- UKA"U6opQ-5RDB3{bE $ u:aaÆɓ'INNvj1h:]ʳ@iZR#.s; Hhi|ʃug`ll-FCVHף^yFŕwP"-vQT Ie%̝+==v+W11_˽;ERL1"^oo]^e?E`I%eeelݺ`~"Xilq 8.> ^^^YFq6II!Cjnp'NrC:Gh1G~JjlD\AE4E4\ Jsm(Dy5f6l@}}=w>>>ľKE\GβKXXgϦ)kh lvXߜӧ[oMקݛ܈˨7ǻ7ƁA%Y$TTDDDPTT~c}fs[k|{#w%7@Y"իxE;.J ۴I}xxP[ [ P3kT{2֖^:E7EY$1a4)-m׆oޠgvB UǫK ufk 9r=z0b])Q32vXw9wq#$;ƌ?`xepPn[z~+RN_˵-埕,:hHpuqQgRDom7hgEVpB@Yba6ٻW,;zwNpu _m[pG2` :wV_zբwAgmg}\l̍@k}dPNY%&1C3hgexxx_S__&&6y'} :y UWuq;2G^;H%A{=x;c.jzCi9PHo>;v,qqq7(vdm6LM` xUe@[`L̑}]ζ ~;پ}"(9|F 㐐eYܣ+>ǐCHh}55`BA[BNN\dgg7ÇϚE\$A6w_SV?]}@-n޼jf͚VmZs,G(v{y aB\Ht)l(|u+mq׎sGÕ+WrJ6V /^gÆ lٲӧOpҥƼܹs@ Otig,I{GԵyZ`Acӊ҈\IFU޿𛶋yk֬qZRSa(Vb`d$'][^;|0SYɌn3KՑEbU˗"99-[PQQP p ԰h" {Ϸ~hk׮=|0 IDATxBhˋHrr{\z?eĿ7㻎h鉧)w hdPHMM ~~-srrҥ ;wʕ+5ͮh ggclvi` 22rU#Ύ0v,5_|_n =7cssR)TĀDLL W^gG᫯y=~~~č3qMStԉ|FU'v1>D.|C׷L4E0w\VXw}ǓO>)MF_୷į_|yqE4٤rX&[P@DDSN,k*6sQ^[{J aw)ƍ8 И0W+XJ\t>ު>1>D>*Vt\]aơPHhfԨQDGGo>JCQn^Wlt4kVJ->?(XA`BWu7l6p Il8,(PC)*O!4Y;U g϶i|Ar1նZ)jAظq7XFj< 2%ZAhLo]Y g֭=/pX cѧ)Z,e 6 &]mslb)lL݈Ɨ{hkMt^Ač"=9hϡԜ?>q:)~lO5єEgX3a0ΙUUs'D(&)ms~Qg&.xuf?q8%5`k={nNs4VoLSnz/?ȑ#/w'SK%WDA[nbyyb>O:ٱx,UM,ް@n~yC[ɸQ$ .=6v,=B{p(g6{d8Zٹs'iӦIRRsYK#U fXXɓ0uUS(&mq2#.8qc-S}n0n U*;w*nUו{)~hyxGyS<^*w}(#b<~<âEpt~RmCa_Zejt"hA!<<+͸Q$i` n[ʃOM=G ?ܹ&M$ټj/5K`4W Lj#|2ǎkJ;W9{z_ʕ`QMZ$um)է;R\" .o A% bUs!bFnp0֊n"VlHmr#lJDWo p-\A(f4j x _ʏqORR9999yeM:a4v]/͝;^SNv6,4 j~]n>Aϼ|8YYYN(.LD61s g2,j"˽NPyK~;vrfzz_zft 39B̙3Ӈ^$f/]^\kdm2.+`F# ˖lTMaU˭÷/7Qs%Pբj%k?Cw-ݺ;9QMQP]Mp_}V՝u t```+t- FѕTWW}}'cO6+G?v]^h[+V 1225gͯ'Nˉbm҄/O}IGn{_O_ƶTQYYIUUˤ7EtK"!HTgP* Ěh ɅOii)cǎ 6L{5%lF|_AQ()NaBxq#ҥq#\;CFg0|v3d-Up@.[ AZʣ&Bdž2~{wځZFOS۲+t:x!xajg9ͱyf9V(W$Gwߞksrr%D%tk"7aaa\zզeccUxFx콙}fK9_|ަuˍ\MiaYh bb5k &ŋAeIGbf 6QwC[)))!** ?g5v w5Dš:jO*8Etk"!79/yQU_ӫ_n~ѳa(ȅΧ4lX^L "nDy -kQTlٲI!R6P^[D~o\A%Tm݉І U'uXןCGi 3!tzy{EAl~ߓ'aTɦ`ԨQrI54ӓfOU #hњH @ffUsV{-aPAs콼ת{ȍOQQ&**AI:D=-q.?EĈyxχE!jIp=9$&%q,etZPn6x4q;hҖH+Wn|[=79@1LG2) [ J]V[[N2TVܹbSlr%ȴ"VH2] ۼEt["9AWXn}n3g)o#,êȉRM 󿭗Ib2D G6޽;#--tIVHW3k kp|s3~w@ QTV8Lu& ķGOJDoooZE6Elz~l+6\yh%=Gc*V ;膆󉊊rhs)p^-4dj|i_Ŀ-^C6s)-c홵)WHo鴠 /}쒝 c5ͻw+o6t‘#G>))J [k?f˪?M`Nyyyr pQݖH 8h.~t#EI%FoߎFacrAHz܇j+= Ç 'Nh֡jr֭[%" ?X2|Uo=!nK$Š#Q ?ܶ=AWdjH%9r"ƎKppU-J (PW15a]L&1qP\,7'G^r䡝-4FD`xp:*Co\U um`]-t:(GPT<;T9]~\)a'·SqQ٥ f̀W___Q\ +3~;!!!OTUIb"_DD ֲ@n0kyiM|J 3pp6LNe9oB%44r9RN%9Ylc ǎyr.*<==:u*ٳG9)bb{O0 TBƿm]D$"h[f|4>,`dqI!;w .0`tOq4Ac@uR5*_o._|#F@F1HHpjR<3EBsϡQ[ z ٥iO$4;"#4CFְ ;=~\5z1ƕ…bGKaFs ''Oӓ۷XD"]ߜ\(ɜFఖv@ܧǕpIݞHKǎhlpQSUxzǯ#:0=@ym9'}n<"㼎xvxs1Y@ v|FU 2a;@JKK9|9K$ħx|k.dddҕAgff_l2/Śڥ=4GCCC3:Lz?#K,()a𓗗ѣGʱ}t~3Q Qjf;|sr2+6m{G2c @43D:C*dujsM*`~YbaaaӇ$,X@jjTkKųD(7ܔ~1T+ِl^g۶mHyqgT*VaEk0mË/Ν:9{cvu ץ@W}[4?LT3VedrmؕwY*@԰pB wS|I4 F+W6gIP8jԨ"ܔ_p?>wiWOo& PMkoB!kW0ki5111 0dΝ;-b\~VE>C^dVFhնveŜd-vE~~~|7_G[ƍO)++k.OϞ=Yb]v)uҞHVFЭ䮬et ƙ3쾴[9᧺3f W.pep9+6:rAlF4ED~-3*d[]=;)SVV3E!)|IF̙cXN'<O?-Kg}&d.\(<¢En`v箬w-/ $ +rZ%|v3 h=ECC*xyy ^}?/ 1:N8H;s$駟aժU^E,޾X/;7I/I~K PVVfќGbn`j6?Eׅ >>>dSN $ UUR/O7腈G %$5t:ۜPbbSLxEAIHa';gҝ:صk3gδy[?+֒W'#/ԔH:w!D!7LbJsv8z)V^Mzz:ϼ 8qBؾu, A_RXؼy`|ְUqꍆ9s ^vp"ER]DW߁ƍVeϞ=zpHRj ,PPIn/\PP^wYAwFʕ+yw8<*j}-bH 4*#CQi)[WpF6l`޼yOjXZntmPi%×Qk'id0Lo1h:"w!BͬdeܕԩS`۶mL܀; +*YvtTJh(l _' IDATj|jymXͫG# ~PV[ƪU lk!i>ete%̝+==v+W|>X epqntזЭ}`KÌoRfgHRPn&8:ZFc_od𓓓ɓ'֭ |))0dlݺ"tj(=|)k +%M{sX"aMC K,ERynO ߤ.- [JqT9z6cnUԍZǜ9s#YÆAz:L.n0}AVͽ]{r` ~[@vv6x{{˲GRPRGuu5eeeT%t91c=3gbSQQAbb"aaavs:0f | +`e__]4Z uF]+NJm۬'Hjrr{&:*1՚j,L&KE5G(#hFR?Y(g׮]4440m44υ{ҹGapĉ6M.QEQi;yh9DB ߒob/#xlpR F5xx؎mOnrkŦ7L&1qP\ =~6Ot5mr Jǎ9pC$\e "f![uЖp](̤j0Rxsܛ YhK~|||YPee0c*ڵlxWms]b?̶dgV>}:F;wZ6:hKEBs%72DFŌ"0Ҿ|~K}QQQĉM$%g{ñc0oDS7K@GRVCٞ2ILlIsH-n>g팉uFF٦ᷰI\A[*֠===kNA FF9PKť7V#!#ClxtHXݒ])7jL0___vE]]Ec AoW%%鍶5"a=PT?k,]3hҊ$D17GRTL>][^  t)l֨dt'և=eR,4&M}Y4FJpŝ$'3(rwv7efILJN.t(pKF$!66,Jϔw"FoS('99lNǎ%\ Fsd$$&’% ÆҨzƕӐV AobY OQ`j_5)+++)))!..%J%WoH9 9 p;q+>kb99ƎϣGvx$Z -е7u]T*nj)H9s #gT5J#A#P{~+gpQmHлwovZFF4-~ꠍFx5qvy9X,chkvi|<S+ʺر#Ç';;SN{"I0ʿ݂o{J^wb?m;@E5"!\Ďa|񤗤:IlO:STS[oV+6kFzػyabYJ,=ALR;C.4mHrr*s= j<ИnA .]DV$N&r,.Q[߲+nKqXzc&<RSa$nc~[AxxSYݸ2rHBCC駟l:[EŠ =.jƿe2w1LdIH"؋RXCUjhR*vD%{&3{~\3Md}_{s<{99D3Ǣx㊍  \SNUy"OQ?!$یG O8A} r8 co4otڈD˜B ,AҠo¿AOuT%;wEEE߿?D"#Ϟ}؂Gh$oH3,4&E$(U!Wi&UpF$,}Z yM6`w;o3LB1~ވ`S"#^ d聦M-x}FFݸ2`Um".\by1uZ1T%F[t`_,1⻠ aU*<׾Ze08r|>BBB a& b Ҁɓ Tε|f< u]*un\qvvFPPrssq…JV$LKkd=k2.]k8mh#*vEڵs΁T!W/z^s5<{ ݺu;1g[[`V`&0m1QC0FB[ALrdv\Qx6sr7.O1l٨ ?͛77{A4CaZ(Nm[-9,tƒ7X_B+QcΝl~s\nnNqK78wpFb3U۷GBB޽[6sB0_EhlZA^RH(ϑC,' *>l!Zx@xR8U#,/ ? sZlFnPi)0}:U[*eah+xrۿItJ4?__L{cA Rr/VoA[ E7 ,ʒ`eygiyE 'O=ٜS +*ˮ9ϑ}<>bJsК ai6Mo`ocZj rMA[WP1@m`M]𣷃>{M:wf keߖ3w\Yc6mHKK+wL9u{.=^m1D#YY59'F큵5:s"aY?5{eȗxMY'++ .\7:t cde3f>>~ @]sޓw#m{ַqEUwmY-dHRg(xĶ%² 5W ++;v `Сe.D"`.`ZN`ZZz)a7ܸRU9Yva>ŨxȪ4 !¡Cgff";;1ASaZ3<'^w3w|Tm|z`ZPr]!8(ׯwwwB.)_B~^Tuf<Εt0W* s0vHvpm8sBB䞶/9^L| <> \cZ:BaÆЂ]b&2K ¦VW$mB&YUk#O9T8S}x#38;ٙ-xKT*x<gX"e'kȓajxk&O"OKc ߿A$JHbYE۶m7o4m7E=zy{'>:[X6mpa&:x,Ъ[訦K" ph|EZW UVwʹt}ݰ3Z 傔4K^^R]H_LSaE5>9 6܈bw#6l`))l˗~<^9P=zT='JF} 7v MoƟA[ 5U *A#0 ėaceqްOQWǗ}E²] s2 6)(Vɺ+p% :<}$<VeA$VbiӦ y)]?h=D6",yq8vΝ/3pڴ.N !7]E'o]: .\'չ`÷![Ro,!ϒí[:QH^AAڢUVFJuV*kP Jz|*bbb.]^TСpЯS'sl6 !ku0 w5oan|> a~zL;> b66y< kL&CBBׯhs :/SNرcQRRRXv0m4L>uՉb%R79`vhU“±xbp[[vva!Nw[P" =_[/X*}<8N<<@@,Vf<-Ϳw0hժ3fsIIIƍ;BOO V'JI:epж[v:9v X0HXz׃-2vgQIXAAApppWyf⯌kvu5z;j߾L3+&qЉ.c Thzyl޼ +w:A@W߮^Pf0pu:p:0xM )8 HIxzV"9l=m QqAV*m$K$: 'N(#9YEPXX^-/,,޽{qa8p.HP[8w#UkӦ AWwE'~w8 2쳐0=}Z  Hۖ[oYwg] ,>"µ= o{hu*P[nܸÇ㧟~B~~͍1L $31o?~HY=r!,o¿Ab^"n62eŋ1{6 [!P ]JRtլCUU%* =AHfq*;w)sqq@HMe7 >?^f6r0H®'mR~ܔkOap`ݠu8p ;~Uzp."V頫 a *Aӧ[R?ɓXMփ@h< df620HX~KE6%x`}@=zUt*C 84,PI.1ZC qO5ׯgsO,@zf&.\t͚5cNw.cȝ!{7J<^nysNd-7ozs^=kׯ_V꠫IXS W<<<,6ٳlhsg~߾ؚ DÇ#&&<Nvs;d`~ 2GTRdFypm'ݪĨks=zle[xaܸqNNNhܸ1LAW%jBUTb`6ѿ?̘?OyU[ rl ҊLleaW`>|H$-I24!( [S]_]\a} B(Ҹx]vҍiU~DblxjK"С…H] !0xxx{ʋa'o|,I&dE 1E²M-mM$4 L<<Y,|'X~D"A.]X,ZL.&xi*ğ+uЕl9JJ! oDz8 <~| 0ztBQFA|;b}@{ &11H~_EI&gUNGs ?~ @w} BcCJB_;n-DBm38TXwkW 1-xt*틨j?߽(99⯷'>ÜƳ݂1HX3{3Pczѷocޙyb׈]pG}2r!K- SHmYEӦMk׮[қ`$GV((4jʔL X#\\qAP1ԈB$T!pkwaW& B1@aYehնTsҹsg4l^K IDAT.]£GLfgvq گJ%]5V+ )vZmllЩS'"66 iţG@n ?Ν;V_ w˘vaL Q#rN i{~yKfĢO@|*S SU+c(P `&N7 66ܹ3l^V+ )vZE .g;ƛo`o`{V_Gǎ[XUbFCmQm3VDB|[>,ixG&hOğ5pwaUxhǎ&5L0% ̻z kT"aYW9 Ǫdr0[Pߥ~痝@tqqqvQ?ׄ9h EP!<[fh_4sqЙw?U&)7E#@̺9<> oH\RoeZTZa{!Oyax5^✘J,,I,A<õgE+ >Ν;B*"*\\\k!%%ŸhvWٳ@Vk&r{@ Q*D [s!5Y_hSe=6\83r"GG>BlF,5D{nM88.x|S?Fjj*ڴigggCgqX~Q$ei-+R9 rJ [e6Y Μ9L 8V`a|;ށZX}yV6X#Jxh*D(K %J컳 j55ԩS @ff&Ξ=kp;:1 ohU:EBծ-mah7S6 8jگ.h.pǼ3^Ҥ oy@$œOj@ $a_Bd#QPulN9tZ\x-EBtnAűw|}H`ڴjS誢͋hiJRbߣ6Y  xc5MM{C ~;x䭷ނ:T͜!PSÙUz>UˎU:E’%:U4h3@@;@~2JСC())#P"mŰm'bJ{;߁La9U 737?02g鿛S"o#$sKDesÇβ-F\-:Ell,P~Y*/ Vˊ%^ӭ[7tD.ݪ]XȦӝ8qփnbځkF7pEL8< 1:hK$,\?},[;"䣓o u9Q}/mԵiQQQ W&X.+ė_xCqgπ>}~؍(KSof" m82jw; -^ߞkغ3u ]X]w{G _tUIPPO4sH3 G>M^`HXDMuoٳ' ,,L#"Hׯ|ڵ `̘1u䅓cO6V^Zku5"1HXpiO~^0_ NvN84<t+jNƌa{n/˳0;.lluG\WteEBC*7o___\rED9(HKc ]mvشi`՞ԣ)> {{|v3{@, seU0<|0>>1l84yˎd„ ~ _ȋ3H]dž7333qU]S0%Vˊt< ":u wacζl͛{'VEXXѽ{wk՞oW 0X\xl$F’hQ~#e3È{l;зa_nN^{5t 8w^:Y k:u D`[3Vˊ*M8qcc7`56.]> 6eÆ O>s !m ^ R~}47 o#h͂[{(dJ~ | 64'+)k} ԩCR(BAiSU X!Q,{(y">IJI|>?~yy$G|* rww'[[[*((yMPK*W*P)"_@W3wJK٭R)، #i&0 ɓ'C(x!Ű}ctHOF`dK_XXHXp#)(@V)`qMs" 1yr6}6Oĵkא=z7bZ%2\JO5ׯgsO,Q7oǔ)S4Ɛbo>}؜?YiM%ei OFѭ" ds<ña]5)Sc(--j3ȏȇC }ה XV% bW Y6u.:ܙ-W:< 2~~~]ch1L#ݷa_J{#(͠0$E Be A{c2)Aݠ7vߩq$mdNt:tH㱉 v_' 砭 H^AvݲeKv YY~&h#0`뀣"q0fEmZjK Rf}qCql1nϻ^zJt6*sXu( AJE-JIhCpP'-D"`.`ZΠ۷o#22M4A_-V1gocCaH<~^zqe *vVCmw#\Fb@8)+ol4~qƈ۷k<ąWyUTXV]JSAut\Fk_FW֯_6mZĸ) mݿ1$&׶^HK2 %eIOڡp5E4>iӦ{Y;!'A޵W|Ϟ=J{"V頋$6.@׮蟞;;ɐYƯTprrĉb{F{CR^zsfl,U$E?z@Hlg$L6}'۶mCjj-A7I333qIxzz_~zeXvuu4Q R%%6IV)SP(o`/_1cܴL3f÷_oqÓ'GDD`"aԓ(LL8W=Hߕn{k3'kƌ3 ɰbŊ*KYi!p}ٻw/J%{=^ê!+ܨBG2I 1ݟr_ڵc7xyED˗ uY{h@JJ BrrrL~RASM% i&r^4胣ԑ Z@#WKb"]#$hvN233ёB!V8^WLS]8-2:u"m4ۨbgcǀԹ=ر#4i˗/#>>^_kdŊd>}:˱4h:Pw-~ EoBYdvN<<<0}JW&ǁ2hּ,qqqF`` x dzVA+ 38J!@^0w.xW ܹSoӧشi1g00tzݰJA4ߤvԄB|-8^v E=u9s6m³gH{5G5}%7TX...Agf}8;~(6;ф yƌ:a}W³N>}:Rz-K!a^mm.T+ Ya z„ tE 'Fe˗||xsVDxI.]hqP($GGGbϖB4BvBAע'mEp2$Z^ Wʖdkt]q\1EEbz2 Vedd#Sjj* EbἇX,&ԵkW)\ dg4F"6@``vS-_[=09 :uys fܼ!s͋D.31p@I0<ߘCmQmwhV[gC8s fsO?R"X6DEh`3U+ Y z1$nu[ ƎeWͶD6iQP.S 3߿OvvvHۜ쿳jMHQOmI5Yۄ 6_籞n}Jb)-J G#–SnxƾvWShX$). عe{Nh!<`|VYlN6Waԩ(--ŢEx"#;- F\vma SLn)EaC?#>'!!l1R1?<_(r=6Oz۩Ϝ8K1^0r[SgkDz +\Aoڟ^C\￉ٕs~DzwKJJˋx<ݻwOqʲm6@[R3]a=wrޙ jWӦ'QzCkB({gUBea ŎeG$^xΉPAW\38C mnw#ooo*)1^xuX *[obʋ̫R9sX }5B}G@'N{Lrww'tEdztsބPBAG9mAxm$GY;cbunfC0JnuӍy>eee= \n%aswCL YŃO:f77c v߂rss#@@W\yahLb0,q\Yl%ބPݷv/(p10ּHr_ݷvPP֘dhfCgcҵ#)JNh;'q$.7Lɓ 3FhF:/6)-yv6@D B5k֌$^3W^MLJrВ)A-ZM2=P(+Q"RGZdY2 CxHb))w~R% ʜ!fƗH$ԴiS@6m2:9h}z2+!ZH `IDFa1 C>3 #@@B^jPۊͯkӂ?/G.s٧fM:2,MuI$I 1ݛx4En fs/^իWI(@ silYf 6X^W8m;ݛ\'1uDn ;JX)%p+;8lت߰=H(6ϼ0 CD1 3P[wpJms6.z_T kZ9)Z@M՝KWQ Ν#Vi&S9h0(iiDsnؐ(&d6ڵPڵ)22󣢢N:f͚e g7T:j-gNribJ9'g'?Զ.CY3ء)tk O,QRD '1oTUs̙3 խ[._\ݻuאp ~)RZZZcV>FIclmі2fM:"ry o߾*ݽ{7 B@ӦM3ZmqMT.7S͝ 4g?%&ii^$g??4^[Vtҙ'IRC|<0 BN {ޣC͉XAOV?Q;_S5g\NӦM#dooO{ܽ{k!MamB9̾8qb]RRK.ŋovTD|>ҥDJ5/'4j(:pI$H$}vu>Ok׮5jСCF\І+h_!O嵨h扙5f+t JMܒ\u6Ł($sƳ$N֘4LG/u Տ[pe䧘y4-zDbbzPDʋ#iJ:x sTtbߍ:Zd֬Y~&tB۷o'DB _ĺu0 oۄrdNbbbƍØ1cB9,:'00?dߥglǴ7aP +ɇ'qY/n8|ky7[UCd#BѦnm4jVcbn㋧"em #nG77,߱/:_Vtx9s&4i> QQQ„  TsN :T*#ژˀ>˗/GݺuտdxwpDEE̙3XhxVPy@WWWn5E%pRnN#??l ƚ٢0osNtsaXd VZ 77K9*`L>gΜADDDgzjܹsX~zu*_"//زe D Hk;w@ uhiH$4i\\\.0 >c@TbӦMftT5'III|c֬Y.EEEؼy3mQH$Ν;c…qa)ŋ!KGFFڴiС=ȋ/^܏G]Ѷm[RRRлwor @S%h,,6_ 7FNhҤM4AII v_R@RZ,)) ;v.KMMM7$ -YѼysYh__f3j5JKKQVV yBp̘1l߾=ǎŋsΝ$Fo__Ϟ=)Idi)Lm}L ٧Y> 'd bҷW!wIRuծTݤr?裞`GGAAAXhQ.]z||O 8|$E^=:+ 3yطOv׍Pn@ڕPv9!KtUth;__[m ;0v,в%goSS"' .lv]RuծT݀r7l!y=Rx \!B|?=z4^xQβ-B#hgR}}ˁZʊtJ (WRu.'i5K̛\ ,XL _յ;GZZZ!Pv]N2ͮ*\'j@7C GZg;owH7T݀r+U7lrB#肂S>X?jBdds ]ek4WիWQjvݻ;WPn@ڕPv9H aMN?#mz` ԩWjDjWn@" 5ej $SGRuծT݀ Eh%d Шp!pH*5xT݀r+U7lrBA 6̝ oRuծT݀ EhO Y3MLl+xQjDjWn@" 'աT|=н;RuծT݀ Eh +hc,OPv]Nx+ ]L6Ҳe@~@Ӧ (9TJ ([PZnAª-Mڂ'Ή<;z%}]ek4r :B!Ig`vJ(URu.'i$V __`];Q#b9䠏R+U7lrBZ)AŠO~8{2 MUSQv]Nxn|Q#`Z$rw*PrGڕPv9!q[(-HX'|?&A䠏R+U7lrBZAʐ$/.N[( Qv]N(@+=HX_*哆䠏R+U7lrB!HXW_vVj9䠏R+U7lr$t3Oy1햧Qv]N(r$X@AjWn@" j2FFׁᆱ<~+J^|~ݹsرR^wqjB+]N(@ +C`N;]}7,X`~^``6ɩ|$2=] ŋ3gu"I{^jwԀRu.'VzEV?<)nzbúuEz,WW̝ },<():wݰ!(~~}s~ q,J$ks=Ru.'i? +8Xζ""x!ۄoѢ4n*?Ap0p],o1rr7ڳX^bW_u썹 ]Nxtxj1d;'O.OII]w݅wyͻf7ABsfend6 0G޶u@˖Nѳヸ@׮HNLv0|h^V#o7ԀRu.+HXbCBBHcƌajj*I2%%zaøqk1b;ezI"{ &Ȣ"ML&:uݻwzY$WW"+? ~ ZߋϟgLmz"f[<:@6m۷GzzuXX,^dn >>O _/.a9~n[oeN{:{MT̒}@śѡЩx}Ņl/PHecboκlR+]Nx@mW>񩩩Vcm 6h[lݺ7o[o /AdO >.ζ/#gmѢs$""鋌.֯NC'Oe ޽<t" . Ų^IJ)SIJ@`[0٬xF$iy82p>l? Vܪڈ0w5n;ӼNhA{nƣ׷}τ%k`͑"֪VTj5\&d^ypX?{6RTBHbeC7%y9Һ!qѷ3I4wfdޤOtۢnvOϙGf2*+ I*REUsj5i6]ruf[| ?}H͚!(.NT`௿D3ui>"jT#=%,>P!6p4l[܍.ͻඦYտ!=>4\s e0kv<@k$"GXmX#ܻ^ߩ_"[ E]]q"$RNE@iUnw;^s/EhU0S=8nwãߌ"#)IdfhQSS6a0иp⇰R__tj $.~CU|{C^:ӧ|PUZjwvۛ&E#V#qy}OiVmo槷\a(}JJÞAh\}_JHߌ@ɖ胦>1QS=Hz NB@:s)S7~1esmCRໂ%uK$t8FCNN^ w 'L9ΦKN/kg7z`ntHktO7r6]JEj"7) ޽dvp#͚E;G| ;b%ֿSg2֟܇3ȽfҗI<^$.%D)6,,tқ4&fbʌ&Ȍv;oYcsǙ&Ϲƥl" /i "IMdv;vlEDD8YP ^:%lIyCד:i" YuHwygZq4RR= ۝䯿{kVRL6L,aIJ{NYfGHN$3~d<3g>ETx-AL~RfK矋S ,ee+_xQQUuY+xqEF?MvNx>w^vI.j?0R._&9+,)\bNBA?Y2X1N7Ϳ/ؗ.~{OOPwQ$Qqyļ<fW25#O?E 'O>GnjYqxkή˶7 $I|3tQ\*L&|dl(*9S?}CǬQǨK34.b[B,Fxf{_ 7Wk=E\K3q'Imo$I#`fh:OJĸ88<$9|O)Ji$9d6DW“'I|phƍצCL9r$cs9IE<:,׼wºyL"Zc̫1TEx(I:OOL[؂_~˪n"nV]9r:N;$9L-Nd{>&ILɢ"JduSԔޑuxp ,S%FE<+~#MHgJROjIkY'\LX ZڣAO?;l ?N_/^QAa||ylsaw˒X|] Iד2,-$9xh1h(_ flmˮ< x"fKr)h3$w-Kfݺ{gV3oS Dӆ-,U lyQQCu'b3|9y슻y|e>M.J?=j ZqmoRusKO9fPlf{{aUs9TT˨KQ4gI^ik@~\t'Ňm[H63ܺU>q±,$aiav7dB~[Ԋ¨x_v5sBrޛ(/JJ^'x!b [V4cdr2FZՒ/²2TG_;wtis+IodeKrcDM-Vg-`X̧di+U@s|Nj̚Xp$q,xfkV=+CbB5NQOr* I"uRO)J8#1;"ǿL,!m$~h' J%>O$IH8vM a_ZG9IMO?&Kj`?ۚPm'y ,=K.[&Vn([WL9dpf&iCOhSWt$I֠Zf i2TčN%`y5穒qč$*e套KQ2J"*V0&E~(5 5GU::/ļ 8-HV&>7dB* +l^_H;c5iL^Ҋ/Τ$p29V*5U<:\dn)?|6t6rrQ2?!=~;SRr [UU9S$It򫂪Eth13\l)GN۽?x<^z?|ŚH8{E՘&6 bw+R|uiD#yIX9.Ny]y]֕I|o-ֳA[E$qjjrdbX~{.AGpiJbY`wA`:-vWZRI0;q&̲2Jfɮs^uEچ1eF g`* IDATy U;EKGZ] f6 ɮTțo}T&IMEBvu:2*`>mk`tSrj*+#[o'ɋ"дpH}$6иҩ1h5w"~[FSP 4T} "?eF U"ycq]%tu[ǡ6^j|ϝ#- GQ#*pՙUv7mtv4ZFugMwW#F.Q^,NwIS~ܷnkGtKٓ$A>~V䒝u4)-~ ;FzI5ehN`]T$FWrMjY( dK9B.0XW5NFAzćlprK$/ 44(9m?yc*|G&'1s<(Cʸ7}sj;rPT$&uN pt|i ?_dhK/U]ywcvivzzҏrcr%I<䯿Rv%ĂL@ےDzr~yd[-qFZ1!|=bk9@ZZf6`~ *; oBP@7 6Ppb tĤ j_!L&4n.:3Z~hXo)-^|%гmY~>4Aԩ@aӏ#S7v0Kf|sµk@ӠxǑXW7Q+B#о= E6 lhگ)궯7ph"ʒʠ>Fܰ8zF̚ie@k-!BDWµkr ڊgF٥ kv#epV_MZ Ȼ"-.m1IW"cƈ'vAf:~t%IK|wғ2]ֽp]ךTވ@̬vV fh"#D $(G3DYIZڡ ƍzCrЦA"7]}N]iI$E u2?g_&|SgϻPWuNa>4g{9T˂m׮.>~gJ6Gc׵kWx>4%FՓ:JMukw4,Y7Vũ|^L |3vAB` V&8I-^%IbXJfeie8DQYcs¬լjXƟ~Hŋ}XkEi085֍ZG=۶ф@bs˒˘3sҭDXZeHپh4Y,ƍ73C{8(d^V]$I[0`6ij~n/OC_'}EeE{b.u** -NXQKan-:dJp?_ X dnN-oU2ފcےp$&_a!/_t睵Ɇ?ajdn49;v'2rxW(u܍4iL4M`OUa^PTxz#;6Om8,VMĶ2[˥/͇/.$1ij/|xqo&i6Gao(綏V/dX..JQ繗1q|"Sfp}!Op?m4aggfqVmQ,V25^U.k،;EELA|e/=(=-U}+WVYnܩf=ZI6mڷokw܂FcON(59IrDW<ʳPOR?x`k_W7vnP߀N:O&";jmH&s2<|uo_3 VxE?-W@jT+{uZ*괬.h?=n|ƚ\RQeUۺuŋ?l׀m%%.Y  $­/`~|>~/Ā@߾@T22dTJF x-"hb4hH#"},kϭEDVG{lw>P j` &7, PϜbck-Wx@mTQ.<%%m۶ۯQ81;et ZPڶ 7%S)nDFF.hH#d'7>u#F_F͠~^Av6͸g=HR%YɎæs\3ltn %%}{>Hi.HLJ[r\f}v*9S,իZ  thoViq1"'l*Ev:t6<cD6cۀ#a0#FģevN."5L*D!irbt|SCdd$o tAsNngϠ  - \Ȗ-ŲiP'@ <8II@@@4GEEa֭QΑ#>!Cpܸq4i/^̝;wR$5Ǐ9 kYNLL39V9Bn4Yբ *yu*5$qƁh62(ST$NL*Ska6Eox /|$bGm@]AE\$ɲ77191WT䏳"z 4M\2E\ER P5V%Y{#3$ZU$VsRf,ㇻ?$I1ⲛP-@~gLIǖTxFu\ɂל$ӥTW?MLos9j1qK&̙CVQPCfd8Ml" S$th7Qr.VfCRԌ3g GyRDnމyɩ5)IIOzXuL"Mq*uK^QeY.-\sIb 1l2Կ.ňIQx%E:$a-h'9 Ⱥu"PVI/͛hNz,֫i|%k׻I"׮P^e/YY@X$(@Ê3 sӑ9oeD~rv]ah_Z  "Z>(G:e&yRP`s.\ؤ<%Oܗ3R‘F$JO;i2YNoj(4X~L(e܈Ү_]!0*ˁΏ!FGEEQ5#7ۢH- $ Z;+6Hw/޾ohkq9:TӚBDDx|{}+&O1d=dx94LUѐ:: d$'ɬ&֢q;9v&O] :~I}h^v5a\'),7R4:P_'CmIJ"}F7"1?|&42ɓn4$Kk%]?&L+L+ag+h zW>H&"#Zl M,A՘:UۦMJK2k߱C|Vf.\mWǁi+}o{諒ȊG9w-Wy$1Z z~RյkS^p|ouŹs?0}:p#RFQ֛ )ĄExq6G|xe^!ԙ-%h2(.>7tFgSkFT&yB;>,1z tTc?Ό9]DW7_G HՕi@½Qb&E ?ܲnu# Y2"($y><Ϋ^CpIu)ߥM`%1Q4VJD|x}kT$I0>m_ļ8wFI]MRa{.%T$s)+CCqgɃ 7pPŝ`6E::;LۖY>>KO]$1&kv\}C.[&d:UKK]c8c?Y3? tS)ʄ-WHd[Nپf-lJt6M䠻Q#qy<{ źbL5 5)67@ABӜN o2&J⑾}{|-MAp#^~Yl{k ֮>h__/zM7{^:(*+rC]i$;* *vYV ?u%49>]d0#C4ߙrvr^8kEs!I-k~ДrX2 @4M.˳Mb@ vY5NAv)M _:WxGNSABIV֬1d$LIt:KIJJFi}iӀ=~0ݸ]x䨽)N%kdwrq2|oj@=$,.j6;N$J,-A51Se"tꔭ/Nʪ0sɵ\ MR6&sĦWϽJnm{O#k1h)v_ʊY/" '.bX(VӖ%T i)D̒ .ڭ!6$;v2*#f/i Z݆$^yE{6/yfJ٣hb(S[kfcw Iùq-VSZ-Еk'm.rMY,5u8rwxw5n;x!Q 1r$p˖yV! !m rVÍ6_=&~22D9D\_х'q˅K*p/H+ W&M4 0vh%Ngm2[U&Ʉww] &>Xvm?.*=^;ߍEF,IRvo=_\73׼M[Ǐg&@mӁA3{t~zp8DA矢 Kp7<}\tمjN F͍e|^sE?i^ͬ?DmQhf3`j9-t ڮyEL#Α#jE//듓[˖AJ>IšH ;ǂ'#Gq ݥRFgG$=-,~ulw6'].CG,5:+yNKz!Wi>tb4"ʸst`ᯎ!-SR\d"eh9ETb]@ݲ#MۑFh΋ѐU=Sl+*j,I&=m]SG"yNr9i2~iɔ$KZf.rpn*7c6-Zز1nQo'$yni3/wCc~fFa jk)z1Tm6/,8%+ 6$#BC6/%vj7 v{ѭ[?aܩ;$$ybPա2W=s'٠H;uqaҘQ̇[ufk^Z*c۶~)bɫw?mEILCѓ(?=&J!Il(e~$tSc0?Gh=oqJ!> e黓hXk~_gz pW%L* -hɵ>;t0,-$9lk? (9s@g;w^{}Kb;$%~myYcsNT9&HܾG?n[fgz )fv/( ߵ[b{nC/wXRy}}/F2V[ϯ@;w\K\~ עR &0,3Qg/ytr|O,1ǂ(|_\=Kyr9}~m)^]̩1t: el|[[+g@;冷ӝ\$&`I ~ao<PLĘu%m6;5k&JSqy-)I%ɩ!uWΎ懻&WLgh_D|\9@ԊS2Ks~;ͫut爈 7 0'N?=sYr$ hE4iĥf̨}QJEtӉNDeB0I&i:͂?s3o2?Ml@ÇD_]Qw}srhL4O Nj剿۵^زENrVb}˖@a!л7kMZTڕABJTn,ئӉZt'+™/.ScPgqs՞E%D]e\l_?maauee^|^ĉu[ۻ1$I,EͽYWq jK*.]òC,L L90Xz\AMK: 9r:C" MtͶb8џ (--zsݺu<???vݨ$@D0qo)m33E;Áu∬ŧK.ݮ7]j=}V:iLѠgwʫ%Tv9QtPPV^m{U,Z/6n~A0Lhڴ+W % ?޵pRuk1GщnUGcЙtx7N@]]%J*Ҟ174-jMyAQ8t.] ۭsvPA z U1i{t*pD]kui|wUz,I_1sVu.uA{R^(9ЦdrGKG}mnfڵgذaHMM Fc}5-[Diiݲ8`֭ؼy3 >>Z(]Kx[e5W||MbYYUlڵUn7\ع3 q.ۼq3ХPеB_eˎ]i"/-_,** [n? ֲPe̘1jI8n8:Zќ:u*9b7ÇSL&WEcǜvH0kgjɾK4=!IM&Ƽ#ZfϪz\/?[3q7$kNbذaXfӎWR?/ \M'"Yn]`Z`PQf*mÉ g]PGQM7E24M<-ahS7x+W +rq;VBbb"qAL0Ov qfpQ` x!Æy8)'6^{@UMS )h\Eg@ف6%k2зr d̘1 2e >gg I7N9C8<1E_׮B N:>!Ndi 8Xe)^WPnNfD=M*yw'3RAhE;N7lJ ([ntBa]P5~]<98yR\~g &Ʉ6@@L|ťU@(9*rUn B\iV-_oY>J X)U7lrmѣE; wO:v Vܷͬ>4FW=_MYsS4}) URVJ ([PA灬,੧nSaD~K=8S ـw DHlH8أ3Łf̎-@}~PjJek4е ? 9$Aՠ/*yصR~@@}f.|||<15~~hz hӼ궭1Rn@"] bZ*zjrG?`AzCd_FV_?oGp`4yf_JjSn@"Gе : DA@H^mzaɹ0 Ӿ8 M+;nGWԀRu.'ik$~`zǜ(X'g?lYL;u`kQǯ5۸+c6 wC.[ïCPjJek45  89X'͛<!F- ѭU7m|T|K:Ca,4"E BYO7Fv59[PjJek45 /$$8YP5޽;O#?1-$ٿ!( W^ꠏc uloPPv9H] aAХ NT Z-0n{HFsOEP@ 5tUe1߈ۮ.|"|8TjURu.'ik$/` ZGF{O Lun$pC  A&hT+>$yAA"Rn@S 5ծNu?ӏg BT՘f$Imo$“Zxqr-A$H{7G@b 9·O#Ag^4ooҿP-쬠̈́~A~hfK,oKRVJ ([5[yV+ 8#N/B.I$LMn ]y΋OA; 5Yh9>wLrek1ު~umЇ t^޹GU{ @\@"pP(`+x*G*QZl xဥJ#bȽ@)rJ^@!!qraLf2L$3>O3 /;ַoݷJ7v7" !uuj=G-?(&X;ʝY9t%Qw΂'8b j ݍ;h_CW?}ꙂK O7kΗ_6=n]e.Bdlm~J7v7ߛnSm!SF`$1Q18[xW-l(^9$W̐P| ͛EA#<;3[UѨ8*sQyq$H@qLjJ7v7" /!aMlxϡC깂O=uz'Gq: =H{(Mӱ}V5v~GXThͤVRAY ZTQ|R5 6-۷o߀=XNN̼{~Zj}\sd6N6Zha_܅E#վva222Xp!%%% EQ(!b޼yJݕ+ǎS=UEQ.\PRSS:=ztc<颮(g+JiOv)~r @cyKmoWK_Zފ"}ܸqVCH >ܫ*˥O:t\*lnri%ҪKA@CToEn-rtS!aQ9m긂cFc5ܚx+sxب\E$&hߑXIFBdn*$t8!#CGЧ(>wtҠw<[ fd*TP"5 ݍݨ9Ū p]װX,YAZhGm&EB^[! „ Pova>O gf3}ҋ⊳֗RAYI8czڔ.ۻ<}Wm#찺bO?g?a9mN?G';trڤzlw#!@7jSWu ,ɓ~kxRslgnw5gT_ƺJqfqHTon$DBBM=IE^xSSd멭L1bZGk@VMTJVz@Ton$Dvkht4ڤ-f}G{_0ޚJߤDZCEWoNXȟb[_JFBBaX8zT/Osi_“f:v&:݁( %8?`PVRAY nj?M=6_“_W88CSN=K|"E{1Ton$DBBE?I=U`۫=$d: {Exhߒv ͻ7d>Rݥzlw#!@7* t9׮g-mnan\\sW_pP Ƿ5ɡTw Hn($4Hx<. 6hN! O, [d._qz.nl*9.d wОBBEn/͈u렴TM5dG xO|AVJ(}K}KFB07ƍK5J=eL͊34}. I8XNR~K؍H}KFBdtI婧4y=nh@“'hײOɐq辶;<Hu ݍ)$?a2hnԥuÆi4JCɱcL1 ⏊CJBXO$>Rݥzlw#!@{ o x Ӧ=LEQkg­1 chy{K*V4ɡTw H,ОB˜.HI`p`,d1Oߓ:Ju1*s+AUHu ݍ)$|Ux Y|}i0X]<'Lgh|MaZ2E;?Ar#]7v7" p,x`ccʚ~O6ȶSۈ yn֕6C?Qr#]7v7" tဗ^t&9R]Ҫ_~x2ʝ\-\MQq'}CRAY뇄㍮]5|Nu:nxr.ѽGӡU]k^ғ>kPr#]7v7" t:w;8zHKN4Fj'.1c >D~NֳYTfWbmCRAY뇄G7[87_>3 NV ]A=@ɡTw H,C~Ԗ~iP[j“l[6fW;t!TLTFFr#]7v7!m4uTviW[\>}߿?V: 7l<9tRRB͛È 45I.ij~8/ؿqjG>"9.dA1p@nE۶mox~H8l< tj P?+׮*("S;z]Av#]7v7A) 2ƍ9Pff&+W?kvٳaz *}EQHnu!E3d$>Rݥzlw#ݥKvYgNvmf)wGæM ==_QN_&j~z>õ?Kb>\+//y&<]X,$LKqPvaør0.^ra222Xp!%%% (GC1ٰa `ƌ(–-[x駙1c\v+WQ ҈#ضm2UUjHz=Ϝ>}e{d鐥$F~/.̥Ebӧ>W.亏?uօZ:!-R/9С jiO;j~kvgWHS61>`-r]퐰Kx nzxL pY}93sBЋsEVv\%>Rݥzlw#!L²257^y%Cɉ'pߘqA9tzͻ6鳒CRAy]{'C3L?22vxJW%[7:'b>g3LTon$D; ߆S{_ҽ6҃Zl==6N=E3LTon$D>⨽נX]eNնx:ÒKt!#f` ~}^b"_Tw H> 7.m;#Ytf<5( 7N9 GTon$DA KJ !MwLT̜Xr+hܿvkK+"([w-9.d wеC%K|oa$+9Qp{qWu#n CRAYk KfHx-ciqdٲuO\xƞBnxK}c}KFB#e7~#0;՚IL;I9KCr#]7v7"kBջaHQXZ;98=\v ]q8CRAy]_~аX.:n (Jзw'<}KFBtMH`oZk6_kUׂTWSuѷG12$>Rݥzlw#!@ׄz8FsޫAZEU]`c] x,ɡTw H|Q 'nv&]npm{{5 Av#]7v7"kB‘#&tݻ9PVjב.?fݑu@pÓgz. hd>Rݥzl/ IDATw#!a+jgr#UUD$F4FaԄwܹzh3uzkU׀'%PQ14Hu ݍ]nAy{L[zL0>HU]E$2%1;SCRANaap.n@bz}}s0EQL:j6GTon$DAׄ ‹/9H^` uQ²ϖ]!8I*N9E<ƔHu ݍؐpuB6bbb= 5rQY ֩]LQZaG 43l*ܿSRݥzlw#!6$^~~;M}<=/vpMn~ԎI$>Rݥzlw#!:&&]Ԑp?l-v ʵ+l?++77m*NZk-Iu ݍD ˗0aSL'ty}L4T 꼖p>Nܺ5V̑"qrues< VzzTw ׽$ u#${9s zm68˙3gZ, င'VWp؟46*[S=fj>-]u[Oo.@:;;!Cڰazq(,,p;uDnnnn7[o>dfwo FY2nKuG`k7Ws2z#]7v7!]fw^6ơoIf;RSSپ};`u{Ebb">n'Q5HRA{II;炂?bo Y⮻"5U}saٲe cѢE9sƛoIv킭hbbbRDmT)//gҤInݚ8~s#cΜ9?~CZ'>C}]\.]vE#Gop8HNNf޼yV СC4h/Bub߾}̜9>}pw3zP+y(̞=FRRRCev7of,_&Ԇd֬YC6mB3?+Wdڵ۷/:^sw?5kpPoȑ#CaaaPZZJǎ 9{,VP\GT#99ڵk6lX5|b֭ 2Z'233IHH[n Os=^a39s~Ʊcֱ/*)))Z蔔}?Xh9صk'N --;vj%"LXhӦ !6R zҪU+^}P+yEyy9ӧOgΝ<,YAԚ3w\j%---J^*Xxq|&33χZ+ٽ{7vA1qP+yMEE'O&>>ŲeB+&&&&'dbbbb=,&&&&,&I>̙yQt<=D/m&;v,oWMLYM\xt;Ɋ+߿?֭c\P+Ġw&&&&,&&&&,&&&&,&&&&,&&&&,&&&&,&&&&,&&&&,&&&&,&&&&,&&&&,&&&&LIV1IENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/ai_c.py0000644000175000017500000000012712253362407022144 0ustar georgeskgeorgesk# Airy function Ai(z) in the complex plane cplot(airyai, [-8,8], [-8,8], points=50000) sympy-0.7.4.1/doc/src/modules/mpmath/plots/spherharm40.png0000644000175000017500000005612612253362407023554 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxyy}hEb#Ď fpl`;ز%K&8$8’䋏c̱!`6m@F 횙޷xif饪kzzzꮻY|a<q^ctȌjѬLB==Ea,a2y&`2,OBL&a_=vryx+Kb1dŨPK]ciJtG<X,mUe֌7ia!vgQeFH:1#`$bV`QhO\.G4%};iۗ9iWVrE?j"3VP(N tww7j}k#{pQHkH SdͼhƂGM@"3 Hb Y|Ī~#mb4~GxRzhvs$\,D"u6xJ8NicNt;]Dbt:M p51c$ʞGړN9A׈fhAat0tѩvTz i{YߪGUYs+1^^?J"L<̨>X>s0,Oy^*{mpN$tww{hdF-h_[*ʕgN$MYOVKew'i0#h4\f.;R(F&x0xg+fuitPKX/j!UUv&HS6s`pB>-Gh"Dkш,K]aiC[j:{sy D"ekD|>?,V[nEN<\2ٮM Zۅ[o+:#kb!ZP0C? dG|]v2N>>S]A͎hdYPP(noF? *g!Zxh|ȹϫw5stG21^WV;"hnU\;3Bkjy_5Vr c=fB0_KcY >,"Q+%Gvp+j=&F̮N+&h=GB+ zxt:ݒu6[+ĂPB- J9P"/G.bYd1}eNc旛ujAypS<8ʨ7Dj,]o*q1,kCccXavaC?WDɿ(BE}yM|@{5{~tшTD"1fT*M7DOOSNn+VtR/4t֪ f"ϗKuww;b-8̳#@8 WU-GiX^ؕXwki2+4y+džrZR}Q~ߌYrk;ѣ޽^DSjI;ZA >)*X,dcNsmXE`|!SÈ CRXI*YCXj:h.#b.#+aŊ9|X'!=L-}޼ydݺu<Anf,YR޽{KXh{a…,Zg}RW\ʕ+jؘ=̆aFJn|uEQ!!NCvayfMBtx|>-[~⋇*,X] صk立w8[eq4̱XT>iVX|KAZ?!Ea{Y Gu!0x 8Du`V|Uk !^cEmt#:h>i>jXqҪ'e!zIф]~ X z0v BZP(FMqjgJ\F&O !^Ҙd I-qk9zZ5y;yI?~8FrÂéʳS k<3G"Z bUlh4:jxp$àS'Xw׬݇&İԶvVws=s%m=ۋNv2]AЭPa mLFNѨ& #jzXZ^TWR~rԮBIW"<uC#hB Ezzz&\Oz3!cX^\x *߱ D&SFeh؍VZER^&#a}hX>fl''IBPѡm(Z @#œ<~ 8x;3l>40*gh9F[S,h n$_Hnc[ P)JҳUÉRI,:Vln++Q*דR8|#{MpT](d2N6v{i"o%Q%O?`-NZ_ ( ;i+7T;3 x| M~9i$ t{A +bc+,ύvtB9Lai5|6Lu`ppБu8%]/Fj:Q>/P fDI@bEfD*i}+ 2"C;VEnS٥Zʒvkw7 zRDGte{Љ;q2ukb0<AT*9\ +V4vGՆ]ODN+ 7sU^ j;^㬳J#y)BsӐg{98 )7y~W#W%$#lC GHZ'o@/uo=~N> WX+{nMAkf3bXZ F7 :kRq  D.G|,2ے4b!$CՕ͚ʭ0Q%m>kE$/A,c~  1m_~"ϑP: Dh5daǴ[=YPI|~XdkOB#vFLlDs!Bq$PzQē"V ,/#B: "BBasLS/~Ѡ\I;IS,ˤ](F$mc5W_EBN?<21g V@HoQav'!Dx!:"BY,֢Gj[f7T+{Pf ȅ sY 2wZ$;wsu2k;igY߰xVYdty/+U[TDBhUISX^t F;%$UZNT G+" U`iMbc]x(O~իhUN}A lR*t΃Su"oQ8CX32!j;zXawA8VJQ $-`qUS iw 2pC.,>v381Z DJ%GO[5I.U1^sUD+$$b#GHZիs\%l#cc˩R.bM@ڻhItJȚR՘8 O%]b`Ъ6a/F^o'=j𒬃cORtM0uTn6@ժUE~5ezWh"P( NZlR(BŖ{ЅB|6 10 nz$M:5V,VzϕDK沎a)Q[[Z'rbvVQլl_߶n4h>F~l㿚h6ثtAb~~ӟyfJpiӦ ;7>/_7?iFoo/O>$Ln_~0XhQ÷5S8b$j"(w6Dmnz&u*"`7)SkBU1vVEef1 ";X !3=5QVŒ{Gm&Nh%1ڟ?ύ7Ȍ3x衇رcǰݻh"/^\~p A "+W*$uXa)-^@|hR› IDAT#m׍!]TTA>9f8ǰTfU-S B6"LJE5u!C.mGVDŽTJ/m_`v`׮]eRߴ,A52 xh4ZjJ6۷k dFlAPYISm C&lUƺ)2e1ۋ$ג%w#Njh7\CN$,JR),x^ZZKOprr9ӊrbEe ;|\ԺAijf"AnzGj>u!+n;xkͱ [~!Nߛƍ/׷};<Hӎ\CЭ@d drNAP H˓!3[WBG>R\ ]T!.D^d |u9f`Y{u"~\>`VH72F֎E @/,[vƸd*u`,/\fb58Hcݻu4ϤG @,Ѡk3ֶcmC1ܕdb\DpxQ 81h]m[$b>t{gS(\ymM'7\EF%A;c$6/FXW#d8!F}öG +VyBzyGZ3FH=aAsW y6rQEEHQD'bqh-[ZD|鶱k"+ c5 g5S کRx]-҉vHON᭷ގLk#Wݏ9 !9D)bOav3Y 2_t~C>(_#|] 4Jl~KkS7cXDN~gzp$lEbXVN(h"OB|Xň@ZQr<҅&Z ~C$ɶhe]x<" D>@H+e>O!=Ҵ d(BtCu`o֪)ZC7 ))rUh%XYs} 2q)qRu]:Xv+ȡLH#kIPpX']Ëb!vW!n;FyiA!UG .,Xxڗ׈,ժxQȑ,sk0V^Ң{*1_E8g/'NONUй\kŤ8ΘXL]'FM7݈s#GrzQ#d׋!CR s:H{̿X/QjŠw"b\7q\kȅCK+1`)B :! 1< m#Fˉ?Bl6K&) vAk4xI$L2fB2iv h;{iFw7Q g9}aF; ` dn8{Q KX9T ,A2ODb!DTuk D B DNGV}V w! B/cuiQomK9 rpٳgmQ!ks!Sf@m%ٸq#7pC2*\TE~ZFw7r$1l֬yQ ܃!3R\?cJMeޅ-ͰPՈD%b(kS <\ *rHo\c/"#* Ǚ,uc;oI+F+فtXP(AƪY<]]]m94Ph 1IbF#4?i@,G%|Rn;FXې ͈R}/C&GJ~ގTo,?KN dD;i( ݪV#!Z8|,!vƏͱ ӻ%HbHwö́vV]i;Z g!r{(c`fqh4&nSK3>|")ݯ#V_ E*Į DE&Bo#jxxӷ!M'loȅ+HVĢ{sxW[uȝT$DA_i=H8x5G0$Fj[2 L\.ׄАj#h\-d<4wIR ',ȑױjsGRGHJWq? Q W'~QBT&ŤMU= QǙ |"!EDun\LAC]|?}S~rG͊U q~Yb'xb2*\f7rl3׃vu׮qN֭[y;.EtQ5Fb?"#]!W"2KD5CH[2.׀o"^B*㽊}L|!D>nDE]d~w~`Mck6얇B{FړnPЮ"\5l"Y; &j,YDQv;nClH[НH͌!f~x񏿅L݉j*DނU ?X2\(Du!kDoE.*!_y~<QأFr\SvcHr|qvFT*'^4jB5 R(rj $n*uڸ5X )U>HHa.o>Dw "} |?_$Op '4f6 js5b'& 7u%AA>/rnPH$|9|H\8Bi0F0&w/%1!B !]w 5׵IfY\>S} "D|4sy)P)ϱ'eV8|Vh5eػOVkp>|>bd2YTvGqFК Q՗> /1ߍb #L95g})TA"Q"Ma%e!yHT(Us!AR5!ěl5S2.oJ=LRz Pk$F[FF~aqY> `%V{rZpv1`O4/U>(8BrYp/@H $DmBr)jhҌg $3&B͘Ȏ7̿S>`G *4|τQP] 5zv i; "h[v[ -G+ *rbcǐI ŕ $#jۈbBZY6[@9\ ݱ+i6{: oDT #ax;{$4'E jhl~TKRc B ۩"V Zmy7Q3r\dIJفxD?UC9X{d" "QsH9HTIHƉHHr$Do1B vD5i J:"!>TaU+ԜZ 7hPӬVu݊IP(g:Ֆ=xY}3ղ˯I~cw$;XO#{aB? 1N'f: S.G8BH!J102N)mGeއJ3 u-ֈj7kaߺv)|>O**wtq$ X7tBZ+uu.?@5g B_B {~LfQm|MGX*߃uܳE`ro5oo;1Ih5sLVIviժf; $M HRZD"O!bKh% !˿COyN\qoGHBK Q v>VnD) o- OElm1ׯ Vɋz;DDMq:\G&V)hiO:Ĭk ,Yxʳɺid1 2爅q xBнqJ?ϵ} B{=XzD#gܯσ*; UxXpK˱3  4pY}zǹn3LҋF|/#f"VƷ7#7 D ;}CTtv85,Q/~3e6BS݊W=q g">q$,pYrWֵ:$‘j422m=NǫZWU;w}:?gy&>@;['pdN&񠵗,]]]T&~? !40#Iފլ[<\ÈR BSތ&5*_rb.?(dXE| "D dDh.O86ս<ԇѬ{x{T*5n~;kx衇CP`̙ٞ,[P5Ճ9sp96"4L݂xb܂ěH/"J6DRo#H 2)”C[l\e_e\ՙ}}H:$/m~$wu\{,&T*JRiAUgWW䛼]⋯ ^6( D8`!g~Q e?,@b (K# wIǷ!=H\}iegԸOd\uU C#<2vmJ֕={6CCC کW m. u5h%|>߰xf㧟~dYr!DC?g-H_cBV7Kῑr0?9r2`w HD|/eOć."ik;4|ry玾3jx 1m41#/^L__g஻bӦM 諰QYj:ht1:d rMAJBGDU3!DA".r3-D"C̿}XEJtl6K G"xÉ u ͯ1퓀N3ajUc--5$^+xX\@ HTȉϼ!3 (r봈 BZt% /~cFE4NUN?w$fc$n$`d9}*yTMʶJPӟ!LU:SD}DgŚ!vm6!m>GTN@6>a4hu2xnT٭G."tQvutG0/(e?BPt,RU^(I HbHJ;7 vKZl'vʾZARt:]zլ&wjv ; C_}u;B߰"qQ՚蒷}s,jFFʟ.@[xꩧXwh`Ǚn8\MDa{b1|>_ӳGC5 shaG #-! R+S^[s+q"݆~mӵk׮l7Phim6&v 9K iXnDI;QdXk⮀HlYH_cH.$p)j(BW Yu>!G ˳XӧOx5ܰ2viR$p)A7T*LJ` G5ۡJQ0NexbIUym+4/U4Xٰ}5kV}mmwFSvM*;H0eʔv*wۓ[IDAT'|>_.B3ڬy; s$`!do!PUv%DVUP yևF8S lQIڣl}I*{pp#h'B'f|>ߘ1N!A'%ԋ(x0.v`]HG 7$#hD?EʪEIۉs$qW/eUv8dܹ _n3бG6d2qWLhtF  ˑd%MT*d2e8|!OppB"eC!7"ϙ;)4}9c.`y41F |>ly5 [#P,d2"T5.[53.;HxG31މ_$`*k.UhFck2H|r#!>؂E`M(IZ&%p8\ZܠcQYʹ*-=th5s5Ǫo<5L f%kn//5oxMOrzY|٨#Tms'6TvK+{M^7IbiPQoHTYZ,,"c.r~ 7CZJkTZCg?{˸>7Rs&CÉt:;•m? 9 ،lf$IjMoջn,xfe$B-_4R`)|? +DUKJ*ִ8Y^Ԣ/ W"N 4lUHQ'~3My{?Qo"*{PkAj 1R|!oe%|Zs챚lڝ΃={0}tT*E(j$`#:(ڝpRˉ?ډJ\ {a֞|6v߆L >Rh!D=!8nDo2ƪ0@_P:|nVL] <ՃRċ/H6_bS5\K]]]mKX+p2VWjSW^y)S"D:hqVF u|2_.D1L!4OnxD0Q&;ҨT{O>NMp-ADﶪs#E RTY+F!ff:#XaVhhI!Xu< d2BcoȎFӵ$.j}ظq#W\qNp-A7!U7ʪYO;oĪ^7y#jXӴjeӑDQv-]TjCƺ\[- 0شiGsgƛHB6vWPKbUjŢ$WbX v܋U"T"B-4lKmwc뉂~_r7|[}ra%nC6e͚5̙3O?Ù\KЪRM;9bz)zkȚ ]]}fm5ޅDyNFbwcFq gڴ8{4wG|~Y;-o<f<;ȡN#X%DC#@={567CB3V7 W_e޽\{3U:s,=0ҭu'5k䒕F/#eҞ-Z^TYJ1NkWZZN|[<z7RhU'L:?^'p @6*(V<{u:6׍ՇQ^@n͈;${վV9z(k׮gΜ9-Yg+r 'vҏdݻO^no"kadrٛ(2R *0 ~kq5׸vBsNR͵N؅BaX(7~*ЁJ%8իb``|##,sй|P'a2PY}}nC'\4W`׮]<\x|hZyNƫr\5e;tB:7|^x.5- *h_mhTI=h&>̺uXb3fhpIGNI8)[jO5?ZѮV\al߾~W_d Wt-5P[jO5[i5Jh-%aP(~z0z׻=aRNQaH[pkFɊny(+}&)k֬fU/JqM7ԩSꫯfŊ,]/| z#Fwݘ ]0adY2 l&6GΣAU~4% wABfr޹s'=+W|AZN=ݻ^smư[c2tmlHF5IFÍpa tm76J%^|Er]w]]޽{KXh{a…,Zg}RW\ʕ+ ܊#Npu90ȄƶEvyx *kJPժ9JsϱtRN:餺`v\]?~?M*z^S:@"4&)Nt:Qy @`aN_gƍ QTO~͉'Ȃ {X,rs-4hCG[&C+"4ܐ|9` tnz4 M6qQ Ǎp5Ap{RrRLh'a;5|ըsoux_d2Yyqimn gرctww3w\f͚a5+[sD쭑.JڭK.aԩM__H&lذF̞=ٳg;ZK5ׂVqr1VwjVQ%alݺ_kfR_l Ji6noA$FN ZN q ;ϳvZzzz89I$JdY6mݻ gfΜ9-OujFh(+5NJמHS#Gn:w2{V wR`Rt%g߾}zꩮ?'zܔ)W :%3T*qFRy{\-nG aڵ'L2e˖tr\SSb4WOdɒ%Τx ଳ}/aoi&2 X͛Ǵi:cmd4hߜlG'xlذ/)S{8 n lܸd2I</Ϙ1*{BI5E:0 _~Crʎ8N08r7ndppX,FWWc̙;5E ˕;lk2k,H4:ӡCX~=]tӧO{ ,JZ`0HXG?Qc?.]ʇ>!~]r܆F,^mbڴiklڴ}q#@+Uf=I0}ifpoS/ `lݺ5ֿX|97|3?0v,\xu-Cw}*y\.WDF̚5\˟Ta Tܹm۶qWO>Z+O}SDQl] x͚5~|{N8WU;w}7XޙgO ;h  ;w$ z"2]]gR /@X/vx'+&W;硇cX Bo ~:~: [n婧" F9s&#x:1|] t}Ht@#EnéL&Yz5r 'xba x=v { /dLx<'׾c=ƥ^ښ@ ʩ ڶm>,~H$RyWYl'\.0iN{i&V\I<ozYdIBTbΝ<$ 8 ^{5ΝKOO`z͵"aĨߝtXx1}}}|e``M6C|_D"U=ܪYnG޽S+MPHnWML&Ú5kX`H ·7I MG{_gӦMrr3uԎ"vVH"^K/dx.y饗HRPc鮌 vbj-auVW_ÃG08|06l Hʄ톊}nXH>g͚5L:s=#0x)0 GqF;V7w\f͚ꦂO#N>,Dcǎqgr:ba|xedÆ >|xXžYftT3 ;v}vz{{yپ};_9=<u#hmC2dÆ 8ph4J<g-)M}ݎb X@x1Hlܸ}b_"*:6]ɚ5k83Ypaڃcfy饗سghh4ٳ3gN]ʷ*ڵ-[pUWy 'b̜9Pn FTbÆ d2.Ro"$O~8?͟pBx ֮]˟rmq, lٲ;v +52a'rAN44W椓N&&zxA/_0mO?M  ͒N;طo/_~oyxK }}PNG `ҥ\uU0뺸&&&,'-hv h(]ס:Ad`&<|>ۡz@e$ hX,ɋ& [D_.lۆixzzi0 ]]]Uv6E"@$Hr\eMlllx2 >c@U!4MضaTwwZ777xCNգ`@3LD&8S8/&I. h:* H&xxxWapp}}}^Jb@SL˲066a `~~J?+8;;C:mۘ,A gwwx2SPBr\ՂD"^A O˲055sy)X[[C>>Za@ӗ333XYYBo3E޵@REyD5}K:B&''1;;!P(MӰq<xIH_6b1aggGX&HRAIMD$)4DDb@IMD$)4DDb@IMD$_dLIENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/bi.png0000644000175000017500000006117312253362407022007 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxwxUM/@B FHE* MtT*E^ !$Nd6m7;[.yx&yw39{紫! PsC{ QQQQQ)G5****jUTTT@8(VQQQqPT⠨ZEEEAQ hE5****jUTTT@8(VQQQqPT⠨ZEEEAQ hE5****jUTTT@8(VQQQqPT⠸vʕ+ի?hZ9...… )SEEE.uݩS',ZK.Ş={tۇ5kb޼yZ*g?****v!\K.EϞ=uOHH@`` 44W^4aW̘17nn_PP<E۶m Szu D}[YWNW7QJ:?2!puv5xj ''&--BT݀E 8@"b7A tjj%M||%qp wsp)I<3 v9[YWОzg޵qY@uAHho feo f!n@\C{n<r tu)I6Bh{K0&M[YWq]m}'jm$=.nډ) ȅUDhr+-! $=.no]AEp#&RRP\h4ꒋZ QujU7` "7HX3 VGT݀E _  j U7 vQu~8hjN:dBh5Hh{D ]T݀ fph5Hh{D ]T݀}gd7b`2vd.Bh5Hh{D ]T݀}J^= 4Z QujU7`_QQ@uph bA $=.nڣiQW㏳j j U7 vQuqȀZ5Z QujU7`?홙@v 7\TW =PGT݀E O@p.5@8zvc)Bh5Hh{D ]T݀GFB*V-U2WZ QujU7`? يlDuoh5Hh{D ]T݀GF.h/UƨAB#n@\=+ 8u rẃٜ5jWh?spJF9 MժͨZ QujU7`7qB-Z6עBh5Hh{D ]T݀}o~a \MeZBH mqv"`~g1^:?h5Hh{D ]T݀ @ &I?h5Hh{D ]T݀/oy>Pj-"!!Æ C- 폍O<ѣGcΜ9^SGT݀E ^ bX@J0ЁXdIF/RRRPrBAB#n@\ldnr%D>OO.Wۦ:!!!8x -[ ;AB#n@\l=1s &7u~}4 h'k%lݾ˗/cժUXn֮] #w-rm۶aݷg"g߹sF#/WHCڗ f).PL=;~8֭[ӧ]; ":ömϢq 7VX\TZW_}?$8~ |y6ZzMn){" o2\Xf[>֮];E[U7 vQu.ÈGy%8 mq41\9̵3 Qqpur埰k"VGT݀E N}@v6t;&:7ʹvF"Uwwh&BhWi߾CTSN}Z$=.nvEХJf)@6@$QujU7`qq@L P.Pnyv3h; mqvɽѵ+hAw 'Tl`! $=.n6%ݭh \ݼ~}[ VGT݀E X_{v6s'<$gx u<* @P mqÇd}{3MTmW U7 vQu׮ h\5jWkߺݺ[*ͮFzm["VGT݀E XW+@d$P:P6h$eG]i U7 vQuվz5o_~hDGD{Z QujU7`]+Wш¦L5$=.nzϟNpp6"kP C mqUxsoDFUUnf ! $=.nz hc \r}:*׵Bh5Hh{D ]T݀u>u :c8? j5퍐Z QujU7`f`mP[ůio4jW17o׮@-O@`@;jW嵟8\4oT32.@&yCM^@AB#n@\nȽY h& 4t4jWetoRÁiZ+Z v-_y' Z-K1-rHw@Rxk>~8֭[ӧp(ىT9r$ШQhej4|pz7iĈ{K"oo֩Cjw!H{K0 Qu]TDk?wɉϏ}=̝K;꽪e"0tPǴ 0|-Zy% o>ؾ=3~D ]T݀'L`/Gziμvv6_ 8% w7fR;,;#jGT݀E +7sgvUcsjvHJ YwmݺɓKgaÀ;N<+{"n<&Ooo=DG#)."+J_ D `%aNw/ko_퉬 gK}''6{ n@Gu븝ȑڻCQl_! J£G8`96JF!@Rgc6{!n@/nࠝ;Ϗ{@ N o\8v;'kӁfUkuEq XQuqy ￱Ϛ;UdG ! dhhՊgǎYWL o'M\\=|6:Jq X9Lמ_-8iRٵ q.XPB}T,UQZo0'ʖsQÇ+&OmʕvߧӍ˗'R[U7`wv+p7A K@f&Рm96)xf R) п?iuf;wR`Ӵ_|̍ Z`Fx7A at4oRfl#"'L HGnܨ(z*5xmn[Ф p"ClU7`>;p#޺\v!J8BH7Hh@{{vBh$#s˖Z.zVػHNH=X5`E -\\8Zt8:eD ?\SVr~A֯ l lw:8BhA³gyk@@T^ >̮vjJ2;}6ª9kZ5vU]Ӫw͓eD =+ ;91foׯʻ "$46.ڹsʋ2{yӆyY٣&SZ*18|5._ZLD (Yik@.gbWR! .$tٲnڵykGm4x" #<UBIX-`5cE9 h]LD 0 uzxpbk D)RhQZ! t a\n-7IFpPLacUPߟV,|7ٱ_ ^{ ʕf_F`32A;7ujAK7HdcՎpu6(h".$4Žp] tS.nӆ}͆hx,5PJj䭠S_%f_F`?@/p<6<͋SaXT Ԩ$3B)cA_MQ7筍V X-_[n:\jfF6Quŵew1/PB! t :^YQ&RbG ސV<` *O>i8e6Qu߿ AƧSϊ6Qu˥/l+Wiib@ڏ{@ 1` Pom}m $8`fٗ5&n _ʕo2X18УV% PU6P28$l ~nuKhU͹O  XrJrہ|7BQmXke89$NF5p$9eBQBI.BBAB9 `G ސCрhL-D ))ixU?.M櫯ݣVb}i  h??].6`GNP†Zр%geqwlU7|uyĉ)Vh3/d} foHiuA\5R%3h xQi(\"RU+XsAY&1Qmf뎌>PΔiڔKsO9 | حrN^о1mkvWThiuA›7H˙=v5z?%$NLL@dXe e9D 0r$&M/ zucy0o߳'?YC`3hdhg S3z O? ?O?t.Hx*ojո2ZoǜD ZՕ+m9oϿ;ڄ'Qmtoƍ_ʗ>rppxz5/q#_/()Jw2л7/W|v*~w޲u,X@[l{҈#hذa4yb :(32ŋj޼bx/QrfXָj2\xp' ѣD]-){nvFD|<>۷ L_zP)(-+z惶8zOOO򲗗(n)ED7EJmHKK+죏%.ҥyK'|b"epH ,U+FD=bvB>`WѤI}Ұ!} ؉o#4N= tu;[sn݂\bq#gO?-(s둑 Ӷˁ@5 mڴAVc0`YIMM pbEG5/I>hK ]"9Ln9dgse-ܷI]H){>6vG\OZYh;ㅹo%#"_v:+3Oh t₅ "55??gZn#uU8# qMNscm22B Ϣ׭YZ ϊh"8pp]^–|)^ݺ:=*) HLD7߇ur޻Yݴ(U٣Ţiݻ1sL]ũSnJB 4? (Ta셄X>TmQ5iiSeHx$* J4#GF2\R{ti駜ot) s%g#hsЯn?:hz` zժUXh+k-,f*$V΅OibI˖@ƂWfeᄈ;N"'x irN@uo"{O)-&εaPG)3`\hm֚A7k*"Dybh{Ӧi5˫$/CWB׮11ٳ䓦Øz\}p#tP飇FAۈBVJΠ+U[Gj<)@gY>ĵkdpƎeQ~< ~922;rmHKUg?8]]ɻKy+pi.Hazc}@Ppi4HIlmȰ|h@[L mDa2X _Ѥ=Cn.dz^q V#,,ُ~ڦ'ƎŞM_+NRse>io2@` ~tq܀.NN6mO^=X#phFyNN@QOP(g]/ܼrبCCtP鉠fMc ʇNj DF>kPZ-Qݺ<\TRR${I3!pP[)WKDDٹt!jW}^+{F 0~.)7f } uoAúGA i*2ܳgZm~F ?TÇy_4{JXkre"ggNj-#4Pꖃþ}V =i Z[&rxfK"***%Kxkpu O'ܞk+OԘ[M6>c=-l޻YH@K+?sCMd`]4`53b&^zza8~血,"??ӛv;^~qkF< tSOSJ(2/>h̘Ǻ~lCdGMCTiNIMI]R@rNp ;N7$Iu^\&D'烈(՝8PL\_U.݁ᠡ ΅F5 |7)lrpC*za+U]]7tBe)KHntӫ f=s͍|yYʗgׯp`z:FEjy3!n]]oJ""8Aoo}[>?sҥd.p/PYU UEDGf祝v:qO|K/$t?4Ν 9oZ-}B8oE]/}>;*Қ(;7>.p\&O!>P'N;QjV*PH'%ɩ9 */'zXs1^LATr>W^r*#J{ԭS0-zqt'YJnw,uL3LN=niZzo{:|q §,^l|Yi?6)ZN\),~\*Srr^pPZSZVښ-HN An{WAr3__9SJ B8ֽ¨Zhq* IݻbI8B~S76QzWWZ-} B8}.(|yLz}ǧky̓~Kl{_lF_bO_T_*~ %SrEٸhdo>6Z/XSHJL;DXݽ괞Yš5fJ 33{WܩذzRu0@8ktZZ QFO>j1bF7xy&y3ѣm༼=q{HJO‡m>ɹС@μ HqJA$7e?ya㹍8{,SOYb:dҥKiUaȑGDDw囹yydSL{  -'e o@j<&pv×{,'òek4%fѫ=1~\elxeܝ1~hh"rgDcV@t'+7!C'/%UU-[oL}fϠ^ˡ0./8Zh슼5^nzKgxs8x#6j z$2HJϴ^=E4ȟgGQ+n݋=@!..3"HYwDl ֭ڵk111evaOG^8v2^>4Z 0aWEk9Nq]=}9Xo({bkDEE!- X"nnix啒! oNX՜U i[Eb-]dl N30#GOFAI}Nwdddbn7n Ki_߾10}:)܂N?彖Jf> #"j!Ǵj\\mٳO|yN9|x;h4_?uax`CHMMИ1c裏> Ж-[H7ߤzsla3b J\lmR5܈ʖ5Y |=M}wu+/ l)~TùrND7I&d1YȔSa^zVo t+[-;x1edf(ɛ'|Qs,\^{n< dBѢ _5+WUcՎ2p \Ώ/9pTiCOC6om} .C1@L,?vV[FxVtÅĒ5ckQsm츼 *6ד@ohKmqY p?Ed#Xvb,ѽ~=?MN4sx\0$mXA8VK3$pPoG7k&UP읳ַu҅%_+< LO&k9SJd@>{+oB8ߞT/.)-.J TIz` ђj|',YBDys˛pPwDD..F@ WK$jΩI7^抴Z?W ]p'O0rD^_y)L(33abq0`B8h ֿQ] Ai:V;yh/^ƻ; IDAT%Ou0lḫ3m6T\x`#K_ 06wl, 4T]H;;_M7X".W^0\r4/mGѹZό4Px]4C/f~fw#~,;mZn @J/%-n_O``x#xHӳҷ\|}yL.?<~&4KVL`ʻv۩P*@\*E g/YlUҪӫoLOgrҿ7)1&KD^=#VLgև?|HC[ڄ Eg)-+sč4>D&}i[kedRjvUi,ބpP* D8&p2{/]ͦA~11Ck+Psmӈ&Mzv:՚[D΅jP4І> J[/l1itIVU&o`F[zz= Fjb_0{N#vq[:ı(?y$̲eFLOg Wg42 Œ*{/k1*}e{ #U"!y-$>!EA5 `ݨԇ TSZO#MΝ#(҄zx>Iu곫T?긬#}Syi,f)]|z&FZ_Ug jiʾ)/ ɤg׾pPen Vʉ]&rͺu[qM>v&\C>S|trۍ^kAx{77N$[RVJJV}b`]f2AAf۶>jI&F?ߢo\Xϕ+KZM6Q`6ԟ"KG}\ON':/rAZnbO0RO[8Y=Zv}F5YЄ27 dfڨ:8:HX={8סC|YRӂv8p Jlj"#ya֭eIЏvA9M+M"’xsV9 d4O-oYhS+B*W"f!F$ZƘc AHݨd<^}(1':@8ܹ7Xh\s?|.cBh 8|0C xś5dڎ'*рys/ȓ;Sd~='O9<K,1i؂E)_~iݢ /솟zTRkpq$'ssuoo.̘1~SlY=%/okilY׋T =Fѥu#>`@dݏ33-inS`L3~v哌F&]n>nQ& Zwc}B8hҞIֻQ] X˖qϾ}R,\fwA݁{7JI1x@Nzyɾ~x}'I\h灊ciqA@Q7f͸z1e׹(pݾ?Pz~+k_AvnciL” aQ8u:UOJB gii B-FjGfi޸q8crb7KKh99YG2KNT7.w`XҤ'͖#i@&믲ܙ9bQ B8hW~%۽#?A3vm^̎3h($7q54H8̘Az`*~?ULy]xx?X:~wyABe@KA”o)~DpGΏU }?[hIyǨQ@ -P ckPvۂxV^@!HZF/-:ayp8aȺ!|~5"`H.=0WwRߚ~fcvhҤ 2i}}}pÝ; NR7س-[rf׺uQ6SA>8jւtXX-0k䗐%{K ࣏xn~Syk"aaaQ\m鋽{pҞ=i$t] `0F53}_ݨB>ngg#P2lAH\K_(w7:]^^\bnܐBҝʾr̙6o4<}=fVI|խ2aS|ǨlYDDX ƄpP;Pj"P4ЃUsc lɃ'$=VKԦ sw+ӭW6"T)ze15wH=8J;1h7xQE'%)>[8U{+,{&r_ 魷, EsJcq @ ز%Zӧ[Hh4D{|G?۶ت_ _CDAAݣG pآϮ#n]5DIJF̒`6iii@ǯP\`Th݅Iݩ,A@/TxY. o&CV)0IIKgyB;`+&E *:11-[r`\Kt,䐨_W%6[VE\\ݢ1.a*W.>OH&\C՚[fA  33hwu_]osk|p A_QEQp]0T XNKKW!Z-/Ā||ە1s|y!!?r=iؑlZ.Z|3O›o..5'>>Xcj>츼}k!Lܝq#>q{ T&'́EE6Bh}FB^l !Ez'([h\~)^RSi&k{ -^=.ywq;LCa@D߾wE1hUaڴ)#G!+K۴1|l }vD݈3+Jv.v^ {zO>rUސ>p?LWϢ/_22,K)bz֬ 9\X]ĻlYGKE*N{H?7S\ o9N(SÇsƏ14 Vnbj穸r !~,rP٧2*Zc6jA avVArs$$X6Nm=9ۤNޱ#o5Z ={è)dD\ݵ+a&OZ N'TU;bXalzU[8jP)4& &xgy-4ibx`ZnA t!풛ws瀨(N,>\ݺgpaܓDl_{ܦOo?LkbBHՃ2*<Zps`vzu b i֠6p!}nʗbbMQv66j彶?jPA[=H@^9\ ޹EUq ^JH eRLdtJ+ȼ c8e-F[Qgl(&aQ`J*=>~f]ٳݗw}y#@;nh~Jh_f⠽G`@.&}+ 9#ϧrY|9<X ޅ4yޫ8k SGK.ŵkP[[ tܹ1zhk׵I4U5Vg@Еv},;!F}JCQ@RXH_F9i~I4Zr~""Si!OX6E´OVyy9l61uT8 ŭ[i7$ .$IXM!,-=kt35:iW¶HsW_u#8֦Mx}1^h]$ A!11رcѫш۷/ʴe_b?'63$ٓ8@n "3Zp(@;i?nm޾M3gt?]$ AGFFСC?Zh4}:@ta}AwCmg(x\yRCFF^h>}j8Z8h@ff 1 ԄjB}=`Ҋ+M7Ot΅vm|FqSLśE:vM @_EBl @Qg&Gy9MEkn4?t3?n| 4zZL+~YH І39\ITXNq,@l@N3g"2@o.m^mwMKiFt3 nۼq! {I0g"2@o- +5~Kt+ hߞj2mKooU᧟R9s.o脳v` 3 )VWӏ/ B'S?9 vG ˗)8Nm3fЗNRRS)mh~.脳v` 3 i;<1n,xis//!N8k 0g޺Eqwo 0O 4z@nc?߿HI!N8k 0Ϟ 9nI`kj4&ꨛcs}{0扏#BqEeAhj$Zguu5 =͖ WtYH Іkj(4ЁFW<֍y}#hMm}t)M57h#+:]$XhCMBH*+iС.˜'AA4Np+4ӡ[7 84yRݻi@W;wR͛'p.,&!мbc=75͡!]'L~hn.];~||r$Kn/p"] T_E'CMB9ت B@0D5 >tCLL j|jiӀeZ6\ 7~压k;W|v` 5 `zD /#h̓q( s@_fg@Go|!M+\JnDnB @#bI}TSDjgK >lB']|GR~bW1;˙6 y2jt6"] T_Ep0(F7nx6 m60GEѬg<ݺ3 /vEs,hktU@f&bg"2@niOyhDt4OL[5lTF( 8µFxk ph){C͓H :vXq7l,F?kWksp .,&!@U3ɂt|nMND+*"dknv` 7 G>-׏Q;ٴ]޽%O>X$q#dknv` 7 2Əh}|%ѧՁvIh(@6[||6*ҩ\5U7[H Цhok[ts2n6I;YC5ERSխڹxk M1 U+5Ozص xEUrkBP՚ͽU7[HAbXgOպ͛S'Jx@h$6?2 \s .,)&!@yYϚ;F5&LF< y2cڹxk P7vNZr9s^<૝nv`M3 ZTT m{TP@)^z9WnvEBzXoټQr:\ڹxk SG .q%s[n1}t\|j5Jk&NL3ftX\U7W;W_6l SSS1f/󑑑%K ==|^~()iQS;OJnvE І8ʐp,!!fpӧΟ?p @~>_RaV૝nv0$@GFFСCn+{xx8.^zr8_UU-Dnv^m6[ȹu:lE)4[YYYx'oCQ`ڵHIIAII jjj}vDDD!Q"HL+ .Dnn.ݎ#44{/6nhD̝;G} +b? +W4o֯_6:p/_vڵ YCrq۷nBrJ%`6Z5՜uvv6bcc^VWWƍضmbbbpI%iŋDǎP7[zHnn.̖3ݻwbAzz:~-#>-8q"v łol9M9ڛ_5U3wކ`̞=MRWǍN:aBVV^us8"%%w)IDAT6Iw8:tI!Q+ sZثW/XV3ƥh&Cqq1yf>|III&)t+?P-ӧO!#tXx1Μ9GbҤI&)t>l""Q ))) 3*Tׯ,X]K.شi=b1o<ϟի1deifŊv^4V9hxgΝ?E}]1(v;cK>sY111h׮RSS͖YH$LBD"/!D" В;cǎaժU(//̙3VJ$ZrG= HKKD#2@KXo=$lZrGc=XdۤI$:$DPZ"HEhD"%DPdH$AZ"HEhD"%DPdH$AZ"HEhD"%DPdH$A?OۚBݣIENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/bi.py0000644000175000017500000000031612253362407021643 0ustar georgeskgeorgesk# Airy function Bi(x), Bi'(x) and int_0^x Bi(t) dt on the real line f = airybi f_diff = lambda z: airybi(z, derivative=1) f_int = lambda z: airybi(z, derivative=-1) plot([f, f_diff, f_int], [-10,2], [-1,2])sympy-0.7.4.1/doc/src/modules/mpmath/plots/pcfd.py0000644000175000017500000000034112253362407022163 0ustar georgeskgeorgesk# Parabolic cylinder function D_n(x) on the real line for n=0,1,2,3,4 d0 = lambda x: pcfd(0,x) d1 = lambda x: pcfd(1,x) d2 = lambda x: pcfd(2,x) d3 = lambda x: pcfd(3,x) d4 = lambda x: pcfd(4,x) plot([d0,d1,d2,d3,d4],[-7,7]) sympy-0.7.4.1/doc/src/modules/mpmath/plots/spherharm41.py0000644000175000017500000000052012253362407023404 0ustar georgeskgeorgesk# Real part of spherical harmonic Y_(4,1)(theta,phi) def Y(l,m): def g(theta,phi): R = abs(fp.re(fp.spherharm(l,m,theta,phi))) x = R*fp.cos(phi)*fp.sin(theta) y = R*fp.sin(phi)*fp.sin(theta) z = R*fp.cos(theta) return [x,y,z] return g fp.splot(Y(4,1), [0,fp.pi], [0,2*fp.pi], points=300)sympy-0.7.4.1/doc/src/modules/mpmath/plots/ellippi.png0000644000175000017500000005263512253362407023056 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxw\U,QT\Ad%jnEaejlh95G#qiZe * ˁ e{Gq{{ޯKs}yb Eጔ[DCCCC(0544454445444544454445444544454445444544454445444544454445444544454445444544454445444^o߾|駦k:^{#G2yd;ihhh'yf _~ɍ7HLLk޿?'$%%COCCCî8wލN_~ʕ+ EBBݻw'!!//"+!dddPzuIMM^z"\i3UL*|N&͍Lp)ϵ<4hP},n޼G)#Aq\ؙ}siӦ(-^{5bҤIwߵkW;XV%K[brNJ sNNB aRP[+[oY_ص]H=ѣGk7n,~777Jp96VzsUx%0` ~b%v/V!,`0[br^}}\UXW_˗axb6_s2A{{{["RQr9ՃVuO{];X̾RLz `1r9˕U~9s~UV-j5*tvv,Br+A2o)Ǻuo_2P[kU&Zj["`1r9Bժиu8iĉ6*XmPeNMMEDDD[bpNO4@h喧.У|mʅb6_s2A׫W ѱcG{+X΅֞*7Utl6mgEv-UC Z+ʏr?bnZhFF51|͡ G9r$hUX_l:fmdݪZP~pϞ AP|..6UkU",gV|?77ذ\\\ 2AkEBH͛[ʎqv4a0…pk{]E;A=LZP~휗II V}4OSOˊv6k{T"X9>gx;/~}XJu.2AkEBϠ_o-}j4lXۊt.2AkEBS@1BHICgEqeU&hH(?vYҲɅ ޢ82|m*V$k;˝83FpdYmr.Qe֊cmgbbo@VLrD"scu( Nt^ɓ\kq)ϵf.HY`( !q~vA~~>/r9~WyGywLy駟l1s׮neGޅbrbm[8z]/ 2AkEBs- C׮CS'AP[|m*V$:?۪mK_/]j]hG Z+Oem݃jg΄Wa`z-څk{T"Tֹݲdʁb|kvwsP[|m*V$8h638J1B:%`Nnذ, 2AkEBstl5 Vþ}W څk{T"T`gg{I_/YbEmB=vMǏgԨQL>t]k1rH&O|"T38 1=ᥗǬ#Ujkkn߾=_|W6]߼y3 /ƍwZ+OeуTcb`|i'Ujk֭[۷/ݻw7]KNNoooG+Oem=*c!?_wzRev'sN=JVV^^^$$$Wg˖-DGGƜ arcǎq.\&Mqts!矉ؾ~)ƹvֱcGŸZa"##V,X@FFJAqhg_رc΋/Æ vԬY棾lذB&OV~`vJBp ? #Wjko4o㷌o_8{M޽~oƍ%OEBU9*Jze*`yr{7/9q1\?)oԮ hEBckV)&&'@*hqBڞB[HO>Q|rV[!@D%"O~Am5*8"Y p7<~Z1c+T.+ږknyOpprb ꋯ9Tك֊S^נ eA`fЧKg * kf&&$ [GfU+-kgذa 2?t=>>֭[3j(/^|"Yؿ_xAHBر“OW]TWSOX=ʍnF_ֲ$g(oBB/G TvAٓ={O0uTUYYY413m7*?qNH$hj׶TIq:0ą (bjkF;1K8p4 icye2|fϞMNNTW^f <鿽9x k׮eŊw9H6k2˔./O2GmRߌ3x 3!R> ^k@ {ɞt;vp3m4F#ӧOgݺu;Y… t=GGGjժEAA}] #<<-[m)F)bM[kvt޺mcW0abWkvRKy%&&܄\~#9u.\hG{rŵ͜Ϟ={׵ӧOҿbccyGС˗/'##%ay6l`֬Y<899Ѷm[^:6l`0c(DisW>8^ ~};CL lO?m B7$M$yA2<#UTw/ h}'g1w\rss!44!q2z){+Xıc`1e9_$Ѳ1{$ѯ8vE*EIFQ\_b/{jE8ذ(?Z9stf[oаlT9N+OY΅Ϗ]OX:TJMW'g!iժ~!~!NRerS&R)O^n[b4^PsOM8Dj8lV3d…x[EZ*8"䜙 д)t_uаԋ.ƽc{a70C"N5[GS]ARR]?~<?v" ]ve˖3(/A=hH(?%9tP#p~A:`xoAA+V`̙RzufΜɸqLPJ|+*{ZP~JrW_Ҟn{)ƶ$xNC>uՙW$9}ϴiӆw}45!QH}O<NWe8Hݙjo-OzVz࣏J^\!vÁ0<#< :DMqp.TNl>CXz5cٲeDEEѯ_?*& mw^(9sn齻{%ry8Đy0~[eS5lذ)SpETĉ2eJ&V$/ҩR=fU! ~[<μqd̤Fӎ ݷo]t᭷ŋKDGG駟Z6QUS!#_ '#XNe/'bl- ?K$i^#UV nSǦ{y&NHxx8]ve…t*/Dm8Lj,mESqgoH77NA{!ƕE)RG|]i6osW7%%ٳg|rz=̝;_~Bce6QUqhEB)lmtY~۽ʐq D-r**ɓM2WVs+"Kru⨲ ND8qBZ+޼ GK_/_5ʿ)c\QrrǵP3&SF,$44瓙+ƍcҤI4h~Qc(l=xV^ŋpЊs-v}S!9^y⮺cl) =c90~+-7eIP~_Net:FEll, ,Iru҃ ZjtOOO"""믙8q"["lC-Dk׆E,~c\^^pye.L@{^8ɻ7sYyyyZO>˗/>}]Tc(, zܹE|I=;6mBԩSin#pvv`0W_yV$B<7iᡇd6L<c9%mW1Š;h4>ڤ=)EIzu1k,Ӹ/̙3:̠6QY8X~鿗,Yb~'={dʕY}_sV$BYx؛,X r?h6Qي7n`ЫWR]f <鿓u]ڇV+Osl11l WWis+jqI_'~FcvjsPc(lEG}X]c,=[p!:5///U=gΜ!,,pl@tti8kq̙hmyzBrsaT"*7**~q63~sKĺ?5ĭvq>v˖-#(({'Ob ~Wjժey*}`222PBFG!t:xx޳~z/F)F-VX!v!F:tÆ }J^QikѠ >`ZZ"/OQFQlN-Ws'΅?xgNΝ;hQZn~zު4hU'7Vl1~?.^^=ҏSk 8!"{gz:JdGŦMD۶mMub˖-HiJKв q9uq9طoƍѣVyV$D6o_zI7RSa( jqbb"Iy]( Nf[ZgBt@^y bҥDEE1p@[Dqd)W_}Evv6mڴaԩ-c '==DhMݻahDJ -Ɔ,]9<0\mhTYr%ŋ|4X1֝;!mo~_sgUOBBgժURJĉ+}J9kmQbEX@"bŇ;H_ԯoŇ+3iČH#nKj kEpnsh ?K.ёqNJ+JMΠ|͡m7jT((cvq `餔ނg%ccK/?+ =N518V'sDDsa˖-!S.!!!m<C=hH(&uvuN+=tf;P+=(Jۮs#ČAɈ&t=ߕfI ݻwӧO:u?@Æ 7o ̚5]!cKP9TكֶM#|4`(cu7okq!#ْ3߉|͡V$>å **viO` NٝkЏ׮sIhh(}8880`ƌC=lGLZкt~=T"MSU)\ ['9bГ0;EɈ|kSW|pPC+Ks6 %44t䛻;G&88__J(GmPV$.aa?ҵ 9ggK{GA6-kXġC$}g5'\hd Y`<xbYp]3(GmPeZ+ZCG}B OC0~uʉbKŒ}*[:9{p#|>;Ϟ=ŋYvҫW/ƌC߾}qt_J8j5+V$QQ?(\K Q46lP*ӷNEljM\#ǎ_~_~|rF#Çɓ޽+"9۱9kU"(^,"hi۪[8tr.# B:9{ljh<== fСmJn(k ¬Ggld9LZ:dgK›o^srqlin 4<#ɋI8CxO8VǚҥKYr%Ғݻ3f{9-( (fMfEPF=nTۿ2~wHh֬Vﴕy8qxBa'+ږknuq4})]c/'g!_LJy摝͠Aؼyi%ғ3(DIB=--` \O.`smlX N.$1dѥK"/\ Zj%F)BCC|Oi!77C6nШWXO2fG89>#ٹbݺucǎ=<<ĴiĥKdL9rBXC0LĠ-ĥJ-vA{zzzUYYYwҊgfi5OBwLsiq#4l(gy)7bу9dPݖ6?-к0k,jڵkիIJJ?qe:+5ff0vXٞ iӠ g 4؎e ܃.O>f O,5_NmBk'u~ɹ3g5zqam^qqqEa,0Z"""[o%T"   +FF0bἆ9bߋDJ-vA#jբt-!!0ٲe T5NU_;CZp%sg6Megese7_Ox ?>]wJ+88;Xsz=Gcǎ]UkÜ9s *r"vI_yt:ŸS{yxü[dlg|;1u*u~{Jt޴IzsB(h,7tqQ8ɗN ]Ϊ&͛'Mbɒ%"33bg7/? v_JA=AWQM_-|sdnnB88{,~E'_8iJGҭgΜFnnnܫW/? `Ұ,1sLQꂙsk%8aU΃VcP)?Tz0J.Wu:i^-'Y}k ghd݄k.Uƈ# UV~EyPިgMΕ+Tq{Gy,1{Ug:Iur'CI]A*1/. .}'++K,_\z˞bܹ"55*Q9F~nhiܫ?**LUc csK,Х \YtMH}kC74jACW~w;v,=QJ(/J_'zx`^ytit|+PaZ+ZRqc!X"&ŋo19Y^2:%i9yL*U&FQ..._G](]X|[_7mNezЪLJ 1Cʳ+WZ_IWW!KLs>}b/{߾[Vz~qnnXvm~V)]cG/|iQ<(-2A?SVcǎYJ-[JKñp!ԑ޸~%`3S$OܻgLX+W3g svĚ5kDER{1QGU3n{ܲ3VWKV@iAT2z[Kyv۶r)=] ĉh4k?^#Ws'Yw5R=vx7Ms='ۧS箟C‡35g,RRd\HhYXV:&ᩧ Bw nٲ;țĎ%}T۷.~ުz+)ztߟ H'DmE,[z&TFaN::!ԩfYXm5*;:C+>I%oK,mE.q \ pkB[7Ydiz-jԨ!.*#1GcTQWj9T3 Gh(\(ܵk9J:up%r $/H&qn"l..49M5 cm:ۯp'|1cЧO;3ھ£YvdФf&vȰNps\Wm5*l_9;s`hx$"39\*qǑC&x=ږ5Kپ}WɉRW敄|/߼WǾ⫈t~u}g7^5^j kaP_|͡ fzؽZ֚`7߄v <}G-u.H+ ~V<_BU}{k^'<,g!?;>s9MF6TiPoرݻB$g!'dl?I1Ύ<1G~hؤ|'JoEQeVf6 //3 œ}_sKs7b v.{O<޽;]vᆪz? .0~xv*k _o.s>ֳJ^U,BxlcL;݇vӧ'7~s7h {/;ǛX_Yך5kV"## gddd$ǁvpNǸqصk}CxyyѧONժU˻k 駟槟~rf"9:tݲŴDs9 wK8|{jПڏ޽э7Xr%K.5ӲeKƎ믿n_/X*/#"9siE!PL=da$Ÿ"oG5AWVcp.,^|7 w+HΆ,sIZ1HFUC7Thn\߾};v,zR,!KR#ǧuoиCth:Zܭr"ǠH8c;u2 /;w4\, +kp _DZ#Sx׃ nO )iܨQ X B3Ny9WKAP7@J ӡZM1C Het24ٴZuVK"}_:1!x 5gUU%::%N fذa_y79zѦќO=O!ϴ2E ߑ5lGMs'c\j5*JCץց߭oK[;>(Z)Gԝ71pqz{C5k2eʔ"uرc8pէU{ !HL6%h^ś;̓ :t[oEW`%;C i%a|<< …'/WH7lJF %pqEDwU|?KM㗅6oތ`ɉW^y1cЭ[ lΧo9z삻srp"nzҢ^ գn;E7{8V'IDAT6_s2A$LOF/^;72uf{rt023)H-64ϱ҆Fz[h"oԩÈ#=ziNSsX"!==5 q@SWjKQe_(Huӧ? ӧ̙q7Bv;>]|CUirN dر#,, Pnrji|bb8+~ gMI9-'{lJ E%=i D-1.D=Lrp"2য়Yxcl(mmܱqT!<#ɡ$|:4ٜ#s7]vPn]BBB=ztd%X dž I8FFaǺ:r}-QR˃k{T"e(ժI=΍Qinw6(׉Kn\.xk;|ʡCh֬իw%֤pDi=[Т^ z-hVN;9^-yhG ^,~K#۶c@HIV4(ț'{=zx+ٳg4i/r/U`)7|g-:1#L.Ш)&G*5d9kG ^+^-Xtyu0|8,Y"})\z{꭫|=K.ӷo_gTbFnʼn~g9z] gJ .iͦe 6g2AKEu \\`˦/ͩsv7eט{ki٥ 'Tg,ܴU>ˤIxdq.N.WO%%'8yDN&]JuRBk*9kG ^)~- T/} isq歷ƙ/H̆![XG^^... 2 &T|yD_+_y׃mؖ6 Ъ~+ZxԼaK}ڜ5_ۣ}/ 75&?=A… Ю4Ѽ9 3nj!wH WhF3vXBP߯>m/ϦEo,:DhӠ)mؖftK5 Y=Lj/.ZIyߘyx ]W'J>qqen~?РAƌèQSNs9r.$Z5(VOlTQ$ܦAZoIU窕|kƂڜ5_ۣ"&Hp-X?>c iNgtdcD\xe v9"gW6thAbKPڜ5_ۣ"}ّ̜axpT:MܼALNlȴHmڴa̙ 0Gҗ! !8z~gO&J֕"rC2%䠦AfOwV[Em>g2AH9*OKoO^ .J7Ljs[qqH֭[3sLXjbNLNNthԁ]ִ]=r_ʵ߄|A}ΚQeVK0* 8>kPUH:ݚ֑>:uDDZjŌ3x&T]*{saRϙHP zOOz»6_Pk{욠u:Cݝ:u0g߿?<-[$$$P$ܳ{e~$d?µA+7D xٌ3xqrި@qs"ˡ[o>ӧ'=vVW[Em>g5Ao޼K/1rHiL?[V-hҤ]SrPieq:0U7~F̙+ D-Z0}t^~eSb)׸_1G~:9ܽkyW!?8j46_Pk{욠y&))f͚1ݛ}}ME¬,iS29E^@*=0 ӧꫯčl?dW.2iGߓX s|mv'7 $$$ܵёZjQPPPzNNaaaeMDDDڙ3Ц1}F:L#&dܿ&`rf͚ӧ b_鵾`[q(p/1lM>:^JŋkD\o ؆c/ `ZZҸ#Nfhj`bhKc`mfۂ&MHBxyO/s~=oY~r&j"̐eL[ւ\:sTTe\de}WVV͛v_*x\rOt:IKK#((v inn&<gy i ZMIISqnݺK]%҂UJFlu峗YЍ{}ptML>~Grq cTQ$IdP K]`^Z"9< %c0-H`7 xQx&Ӝƿ?==WOL!521^P5HŖ xD%ᮝ.6|Gi$^irINC܃q46S{R#SI VHyk<"ڬo67sžhP:ƑpamԀIVSۊ_li Y}Gd@M/qxD< տЯۙq}ƑJ\x>> !款civmȒS|dFa }|-h/T|{0q,zK#G^Hs xD* [} 4҈]FyK$O z=>_H,+9Kyk<"wFE|$~rKoGo]t/ss?9$Ҝ1'Tԗo睏Jj8̲B4_笾#rݚ?+Ӄ 3&d|XVHs xD$=7؃7$ K gYVHs xD_IxJ89A&{c6C3J$Ҝ?F>N/9xlV#Y/sV_15  44G^^owS@7sCo f+i|Av*++vR***wv[uƞ|:_HrsscϞ=uOG)X:Z"'PJKKYf Vjv7V'>|gS_ϷC^~D.*[+**9O-x{{c8{zWg|98e˖t:/8,`_ʷ+`ȑlݺS__\~ҌV"zrftm:t( 1K9[uƅ,X!CCDDgתؾ};[lN:ѥKp8c`` /6[UX:EQe,}CQZFZQŢh@+b233`̘1\.h@+k„ Xk+J[ЀV:,ǎfu;k(&Jd 8222<&MQf(bQt(bQ4EQ,(EрVE(Њ(EZQŢh@+X hEQ(bQ4EQ,(EрVE('BC9IENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/lambertw.py0000644000175000017500000000016512253362407023070 0ustar georgeskgeorgesk# Branches 0 and -1 of the Lambert W function plot([lambertw, lambda x: lambertw(x,-1)], [-2,2], [-5,2], points=2000)sympy-0.7.4.1/doc/src/modules/mpmath/plots/spherharm42.png0000644000175000017500000014416212253362407023554 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxwTE{3dE0 5}"E uװa+`^*bv `V0 P3Lj'tO>O?3}Cݺ}O:oH$2gygIe)Hd5]s73<̳ 3<,ygygyYОygIj@{g%ygygyYОygIj@{g%ygygyYОygIj@{g%ygygyYZzs7m"@Off&iiiM̳i@{VoD"aHP(DYY)))tIKK@3`@{V/ BA"Pr;ˮ322HOOgh27c*վ@;PQQQɪSRRHKKdڞD<,.D"E}rjj*: J$h{G1=E" P@ @ZZiiirXlJ),Hٶ'xОhg@:jH h{֒hb;2ZmɻC6PL222<]۳e@{Ŋ̈꜁ms<2 Ŝb۞yLgD5sMd`0㯹?6YҘОUpHF" BXBkbi5n ?rƳ|>|>~ihϕJ$I*Hg#Z:<6?ِהJ]^^Nff&yyy f,SL2oPg%ۗDYC s3Y;Ȉ8VȨx-ZNOOl6XhI$%j@AX8p8Lff&>/vȢܺmthi$:ˎ,zuuιٕ,):жH,];ڲ#cY]RRsP(DiiVl[iӦ?(Z9jԭ"  >fziitҥF p8$ڮ%Uzz$ٸC$ivrM5?BMlёkP(DNNN:k臺`0#==@ SOv ÆE \ X,86j/H,vz#g1li̞.={m%ٸu3j* + f[ $&XE*HŢ$_tN&B ،YH8v.bԩH~8<4 `6-"T+ bsKI⨏*1&Xl?h`nnnV6SH$RAVi.´N:ib[ǀ@w'@ 0 :Zp4p&?`g MN]ѮއP(T$?;;iad.uI6q;5Μ`;ScUM^\4`#03HO\r qd)WXƻ(kiI65YuIEEEe蟽s>4G3cڶ7n}Mk rJKKlrn"##,|>YYYS^^Nyy9~U0mʕCWx#V Cv! 3`"p~D 75Ų`-\^^^oWmO6, üݴ*1xmnrΜ9W^y_+#;#>qÇUs)d#1<  O~?wCHs%LC{0 xEq# (q g~%2#b9#|Ndkx vXfu+VpǰbE ."U`p>EE_RT4WӀhZ]ȑG2i#U$Ua wD ڷ", W<Ğ\';#}dQ|W]1;$.@&~ mjKɴM'ql襇jA~IMM%77KϞGbq(v p1(^C;7Kyi3==plJC!=-*giM]n(p!!I=4~A=XOf_wB>׹9nYx`BCtKLklV}>VR}콰RUSJ$[:As;~tfUUIͮZz E{Ƚ/C!]8]i׮ c\s[|[i:t !$Qڐ"q XG^ղ (k{$LBl[&O~a;:BMhcɦ{Gh͌hW\h8[__hJ=}(؇8%I|ʙ2[L;cΜبKdJ2{ݻnQ;x!4R #)(L}. ۠p:[ӤCH c}E_ިj{fI3T:[u53#0''Rވ:tؙ#A@&;yд8I"k^L~dѦMg:tho(uf_W֬YӁ{?h輂-0LFWއ4@ .3/EWjV&];3#${G-fP(QWP5>Ô^o(-ʩ tCASW [WN6rq'׹-Ѿ;~QOw~`O:H `X::yTg5$Jwx^=Жom ݐ h%*eՅ`zp;)cX}#3{+җg!M 62/r^" GΪJxCs:u.>_WxqWۓ0| Q(q' c2 {*}bi)Wo ҩKKq8,AC:W\q-}a\sS[SH_wjMI6X?|ڴiw˹*~GZNG37HXB7[#BRSb52;#gYBNDH 5KFǎ[=쳬[E h07#;ԗ'O g2:cm:RRZcTn>Sׯ˗/gذa|[wݻwgٲet O? &PPPРq%4Y`ugvpR!;;N?ڊ+C;s r[;͹OAaM@zyƵ@Li"=O!f+ݺ =km Ìwb(nw)F%D7!4XضH ku0fق(tӁMܐT :G֭9ѣGvʒ%KXdI: LBvv6ͫ㍵ܡs(W6s<Yg]4Ii%ҎGk-M@YW4^EqAL$<ס߀WsCXzIҦVgmg#.vG 4uC/Q#i(i g`%җdF)((`ԩtڕs ݻ>MbHv̰I&oS;m t:ҏ PY:bbkҠQ46SہP!h߁͛?$?x|wTT#!DKѠ%0*6!y_ho;!&MGn#T^t rئ.ars*[׶rJVklKdu >''>w \u?a٦S%^ Zf>5g#(b zY8Ѓv(iW"PMSc.(a8bvhO^{0cƌ3š#7|Hm$EGlv1>f-֞4B!~\uHIN_!~ v*SXYUk~OUQQZjfT? .2#(X@w(^ZҙEQa8iQem 5(t4NE "p8sqi5HR|b8ӸK馿VLgA}1"#=XQeg,&l2.H~ED$EX yyUдL⋯Ar4 BR Q42.wDaѠWb41p+55G]UŝO)Tu.C>08r11xp1[% Uf_Q)t)++KJ,@#3j+_ӏНd6uW$#FkCgmдs'!9A8&( ߁e~"C!rfOmV8}w#@.CצO2hq@u#@}-7>'IT[P@gRpس,SQF,裏NͦOgffV%lS:lhwjv]V3 +**(..ܔ+gG[ sFszGSlx?@,}2vB:v+@KC~4HEx}{\כ(hn>NЬJSi4kB:*VY3ʈסAqԿ;3ȱg4ȂpTڶmKc[.}:mT]t vDW4q;l,V (..& rEcyz_A=\ĉn&!fߨ\Rƻ `Zk/MwEI 'v\B@ù/b311 ״գ>f( 4جC@x1l[DI<;H93ݻP}i@~_j|R豺[m@;--nYdrͯ yy e+7Jzԩ @>QaƣMt/AMk`@q~$d?ңi5ؙ 3h,hGNOO0F$/A1୑V?9[ 8/; -gm&"n#m,w][z1Y6];a;t!n~sr*J6j-բ.j9dH$BII  9qZݚ}bve {Ϸ7 )7#Ym}=$@ʛp~$~ W @1L~u r(in̬d̞=^'|s=T48ŋ/E׽-ADZ3L {&\ %!0Jz]5l~dd-ݑ 999 ;[Nk[{nDUmQ[iJjTzsia'bm{?l^"` #`y"ș(b@f©9J,hzQH߂2&O@.=P:`ԇkDG+G+؈ @@slj.C0`.?~+Ea}ۡd6p+-ܠvDYN2,ueۙl֢:VdFq;!|VZi٫O?=KPDW>d舜PgPoc.E A෡)km(ځHʰzG1(Z"!c(e(>zO_Al7=o=ofk*~cXFl:7Zq0JXľF3ixqAh˝wG}TyQ~0PK̶ĝyX?زU ~8ݛ}}FC3}H5MA@6[d7r!"cYRt]"3 `bBP܋ֵl֙mZn]CxJJIX#|x(nٶu Z+ ;]1s%iuBZ(N{bEig۷Zň1@p$C25h )ϙ+dt63+ނNhQ9z\dRz0т,hZ!MFvkFÕGcmY@VVV wܙز9^EF'hHBZ#f%Ҍm=!Vh݌$qG4Y7eQOCClB$)hvnG"}l kPQ.rZED=rP$M:oiضrJ-[ƪU(**" Evܹ3}I%5܋Zg; @ PJ2YRܚ1h dgg7_oClK"n9#vM0(H$Hn@X!>} ZTNsHƸE<܋4"?s|<i p"6m:QX6>=.\fr1N8Fyb۩HO䝱8:ҝX}k ' I-sl'JygнJΟ?ɓ_wd¥lDL{m!I;s896u٬H[%+++X(~G|ARSS|V9mS(by 1]g8 I#wb)|V!&>9nAuЊmD[pfk Er$a\{~x1bs=#یр'9`K /?ȗϐu-A^oK~~; 8I~i %%~"[.`4Rs (-{QGM~~wbƍ$5w*{uY&Lo嫯tXsĉٴiK._~<= خWT 9UYE%8g!}؆@|"!🍦C! ,#mvb9(~ }1or{6[`6"+ Ѻu7~:k2[ϴg}aܸq< 05-w/W i)Hd5ΰtf8W]II )))2WVVVY+6~222*u溦S&X|9z FҖ3\t:t,NR}8Ge݋@ć ́/AE$+~w#Fw b@AB߀pёH] YhPI[FrEsPh`Jy쿘S$iYa1O'@0QsBp3/CL(|5E m'4(ڔ48|bZo}55zY^YycuB;uTvi'{ߗ2zhZnM^^={k׮q)pD;tR; 3+NWu"HB"))) O-t A} hz+LD݈=@W$U#]ĄiFi,= GslZr3bsfs.- P9py/lιC=?|/>qlպ s\TqPs]3G*rdG`nbM>0Eܔ"V~E` JG=WvhVi\ KQL@Կ‰ıӯ ~ >t?C{&p=O,[6 GCY2mEEE˴ŲZreZ@ @`zz:`+$ ! HKKkb*:E(bpbr;fzzg"GzC)mzb?8٦ *FZm ?j?nK7o}W@AE,G`CM;"f?4p[{cĔC(8dk`֧>PH^;p68 9#ɡ OUS W4sHK#Xclrz1Uf\X**3`̙ i)RTTt@ Р-x: Q(cDWP(ć{=po >shO#¼?#HHAh N ̫?˴dHEExqb+4>FRMT?fb gc1KtQxT$M9V! G qff>yE܁f}QrYfHB|m~ݏ9 @-[ՠ̠k k.Kz:suPVtCGԶ 222HOOgȐ!t ĬGh?Bpː?݁!bGX(w9&)G+T&#]csN Vӯ^uCT8u3v5MB:f;T!Ґt bi b6خ Nnw~}[n@y~*B!QXDۨ8GO0Dl{4h`g| 7^}&`ɺU htm<Kzbе(~V2 6nxh*MFSm.A.GlYĎ7G}ZF <6"|? G@ =Gl?xt6rZ.]ZmqR{m@fРN0G|1LU(*d +3O3PHnC<4}:_nvжӳyx;<)|̀֘h %/87ؒWMɠKz ύ-q>Q2r LDY99@>G~ bkbdGi!m݌p"Y\)(o wC:OH㞈Jr^\73pP אVY/s>H0Eek: X}!tD i(k2 鸋q\f\sӑ141H(@hpeJXhp|2/4dr[r[lc%%|>_̾3j3o,tSX,tp~sI.d^j!~2@G wEHvGbĬG%(tn.܂ڙOFT4BnE2ռ@H<HG Ĉ[A {oN b`"6rD]Ee[显Z5E`@@?ܴd!B~twocqdJ3Pk`2wUg?bkpYЯh ;Dj:e%fT )Fhmxc:m.TW/B@y7rbuFt;[Z4e~r9S[!,Fצ0fB@x٧/4|H_~ ^F !$11Hh1c`P PXhW.}a>ۈHx9\st4? #-3דSY0(nY$v鋓v{ >C)H~h8i(w4f1/hvaih1 Nb #TlAM\jbɾ`l2Ǡq25 ݎDX:( h=Jjr ދJvELr[A>!@3tPy}РǮbq>lHY۶Ub#A@68k@ՕT2L!w!$gzihb}le\tאNoy PCu3]S4 GE%=H_OEL~>h96mƴmѰmhKV@Oz _,e@F+IZ[nv;jxw"~hF-bY#?I86S@!`d"%Hĩ9+H} RHC]gŒ32g !jV92Pj$6.E8 4}4œ@zP6o< ϯYÙTM]׎~d!A+X>hp51} % E:v~30`.1YNOO'55MGWki0Ь*E5gggU jerrr:!Ě.G,mş+Ѓ?Yc ^]"@ 2(cwN*fv9rR Xށ^Fjӧ#0N5E`ݼan l1Pj[p4-U:(@w&Ni-dx/'(¢'7hAIJDz5肢s^7I5mAQ4I,))?LܿQ>@;Et]tS[RRRf&d`8PcݩZ :z(b磩wgĞDH `[$XNDxQ@, ћ:nF} 5­ʍ7Knmwub4ˆMg Xi.?RL?@hI3m->fvAzvғ޻ <~զݿhBl #7@#|qnBED"?2tjz᭹@VLFk]sBeee58 3HD٢OXmʴiӆw?9 c9B(IŲg"`m[@gҎ:{rfCE{QrDE vJYw?X+@7HvE e%}9b9_ eQN2AZigg4C ѸR4t'L?T =I9Ϙ>rEt/CN~_3S4P|9ϻqPH;ѐ.^жIcl:.Pn6;{Ջ3^eȐEisye2 )m׿yuGaqmDF8xxQϓ>s@p*z߀0ά^='@Ieo9&PFpB514'#1ft4+(Eسub ,: `_E({;1G#ґ/snjRRLVc4vorWiiiZZyyyѩZ@ǻbXj \]瞼Sq6b7 |%N KC[\_T':_A-H=ᛈaBJ%H#=8!#}bF̓W~]YbN 5SaB_|g͵#@gڼ\1u͵ICm i8E@z% 3P&㱈}ym1 N'u23F?k[dm%8kwBVenÕ5nжEvaaaB]3vX>.\8וA:]<6k.^ӏ^!V#GUj]V-dgg3o<{8b[>%tGLxW4I")UuyfώoE$Do ,O-@U~ִߦ 턘ye]94(dw<E\H*JCaw M}&]p HUHb1]am%<1*dm#59 ل"o#&,y,[ m 'Wy=,X<ƍG޽]?0rH.BЭ~8b}FkhĦH4mɪ[87o߾,X={d[N ))?Z=j>i_nKEnO OGzJT;'Vyr}\ c(,/(SXvMC jAXT,rT;%K4m E{L1x.gI#g5 #v[XfmM՟+D՘sbmĪAh kv j/R>cfϞK/km*Y|9Æ |g̟?<o]L6""@=H~?Dm6:ĦGoӹsg֮Oǎ;); $Hմb쉤!99F~Te}e22n6|ב0 ]hPXEtdZ MEk|!a6$©\ e܅3rhͶЌGsL 6#p?yl {h(FR_ ֡碁)ퟐ s8kte>iii~1][-w~^3￿[@܀!XMxi4qEYv1;BLX?#f;9B`/)Xg^hL)ldX43 EZ. {;zBΝ f1G_9֞(^[s(:l~P$N|4HB೦-$xg }6m h0{ ͊zއt OطX"rtc'|2Gf0uTvʈ#?~|8ݻw}zUW]ŢE۷/}SOq[`vvv s,{*$zDͮ7ȧ0`P>wEaG4ˎwD: mhC9H97#ToP]+VbW4]!#bp}8؃qcA9 NC1-ƚk8 [x4tDޮ}֚~ָgWL.{*sĨG~֖9-s?\M׏9dQɖ{je-[ ĻI  KOOOxW1W6(JCFn[敋Xpb0GVx6 go3C:Lމ~@,䭷AFl8@ C}Pd҂7"TLB3oA$E LGiMiٹg| , i=PT]eNߟ`w"MvBO8[e`23/Y7jDOWcZh{ IDATnKhUUŊ jC$ БHYjZ@aMhѾT^cՈ|zm"6m*Ƒ0ClS2GP4Y0vF潝?.8 FR{9^LvC4 ցH ~1-GrjYZ# )Zf^ 47#rݎ$HWoo;Ā #];9cssL$M βWL;'kBe\[¦Sߦkl--SSSkHhejFl쒨Vΰ}k0"4 wF`= 鏦ͰԼ(U֮ӷ:Olj McY6Nh40p?G1TM%OH(CC 2Q:=߁H:6Adn!vj6l#@y8+Byrb}: { 83&rVݩm-qdphr-]=Z'k6n\Vu,P2PVpe t\W#k67cW_d+\u(//F@RS# =?!@E=]7 hPkľlm=ȟĭ3qjSر bY*ނ G`2s.k{ .Fw#[ӑqJn^4ŷ16;1l/!zsL42}5_DlH!mqV KmSij94 Ϋu7[^dSĪsoy _kŦVҷo3VP 5UgSY27@F3ֺi38%4"-4OJap,+<iZBQ?MAe02ZT$=Yaՙv5y(g>ˆI@<!8 n@6m u!M%lƉՏv9),CZ,Q2JGF651\ t?>*UsMX? nLmVD"Zj,^`0H  ''cŌ?"nQv#bp3k;4%:4=" *C1hz=1]QlS8+X PRS~ :Ct9~x6/ٿ#`1t'p3\x$z15]9Gkې~|DZx%qOGqc?J0ʴhɦ:E\W-Eh);?>#GRkbu -=ܯ!Ezؚb}Iv'deeQ^^^tDޭa#Gʹi#rN{!2':ar!%4[!VT\BH]V @XHs}/)i}ejcDl:JJ�Xdv-oB@b[~ /4x *9i_~9 s}S6H*}w|֊mpB֡p$ m0mHA22*X:mncJ$ZM'D"<tMq0,D6Y;C(bʕL6 I61:N h韻xĠ!_ o@@D GQLv-mlܱT]c/GOE x6N"5G%vDe8c>BƢ/̾%'s"Ӯ.M9ko@4`.nA,'ĺCӯgۈfF~v܏ 69 {zcLcYЎų\&@ 躮RvՅ5e9wep.="v;NFҏ8@Nbo>鿘@2Cf"8's-dH H#OPHIދ(m2ߋx]b+BZmPqʡN3?f "f<I P_wBJ*ҖKрu.}qkpV7s'wA2+8~h@*" ҟwߵq[c dN$qԶJm|rjۿUB:4=P#)z7"`+lF`SSbeh*@*vByS.24wK3#K$M8Xk`>;9C":,̵y v|$~S?y$k6tg:x#Ç$B7 V#h:"6_QK bv }@%5J1-q[^|9_5sNUU8f̘1 xGc__feRBvvvNh*յ! QPЃ`r(z՘ǡ5P-C e/ Vx+aҼ֛Ḛ7b!03g{=Eʼn}fVEI"q(>zGwNE}kG?`[>uWzl?BΦ q=l+7$CJ~ьӞ}C?h@ hpIg1bD5}Uwk ns@'M̙3 Br) 8w޹YrW| 3gk;`Ȑ!x ~=ۜaͽ[JhJsGWTAm[m\FՐ݆"bߡ)ib:;!łihJ~3E9f{䔻QD^3Ps/ϜolLV 6z9,WYBiO"8: (or nqdބ |:  s[|  6{9JKG?r#v= u!gѯ5UrG2$c/fΜɦMcʔ)5޽{Wn_rW˗/\Yewdٲe1W_k1]֊hUMܖȺ={o-F@ueH*8' `үg m:Ewl4C{6sv<ỠxE(m(>w+g!}/|_%C-gk_# \M{vab,D3[e݊3XUPWb q \yosh$hB0}PS'#1 h==Yb.>m۶"FMa^]Mey[}_rW]v//^!CԿ1HխbWN]RRRZ!$ʊwI*nD" 8 0+rcH.7#;b]m8!6rHX[3v7"ӽpEUdM81!ST>0^,l>Nq cOLDu[L3 ,W-F\^G 'c@8Ĥlʹe9N9])9Hм2ūlc|^dߔ[k#Ozzz799+0[믿ΠA]ѣGӺukٳ']v# .QQQiXD(--% UD"BH=y0@ 0 %H#T6l/; injw= h=S \74=wRdpn@ ! k>JPtDsuCS8,M_nv'i#Έei̧AkvgF,16o}lxlE?;@g&M\kXgãIt"HY]ײuu u-XEPwE]u`+JNL}o&df uqLr33R+++lQ5aQ4:u*֭[,977VC|l_@T"yyyMZ rss)(؁/&_@H Ch1cж<0T2#]H-=CciX(-F -R3 ڝ#~]w ^=#lk#M:o5et+j9( Wx㯑-&v:&~rϳ)"w&7#Ѣ5\rϽ("55ѧOj?hG"#öU?~<ÇfCZ @,:l.PYY$.++#777 wadq0!!_ynG_<0^օHˬ«f B؂0~CLCxbD xm ZHvw,u72u腚@Nщx%4G;_[<L3k\`/V5AG?>½$ -&O#7}}[BJxYE ?I'0kQYYY-go c>Fͦ:/dҶFgo_h,1K,t}qOFU,wFL;s1r#{OĊK9]Àx-bK`폤V Afܶҍ+boSq Sl %ha8-$];ȋfi9,/w\vt w;f# ĺmȷ9B5]x*A A.^Fe ` &L`ذa@r0Q;l(dRǎ˨QI*"WѠhfnS*V̬e/;^ :*t2+n7*8d-nAyh+nLs!ε0@x@TwO-jzk"u36Dz=(m3f{.D(;37G=?HxI; 5pw\?s$Ah'>(V !ބq&=[;7Wj^٧5omeޣ9,Y=ztBjP_ZK%Vj(l9É233)+[ù^SOX>1PvZI 17D31K6>-ǫ~w>"Ē= 1w8墐X(#H>J]=O1ڸG߸k %LD;ʄ^sw7jwMNEܳEc hCKcK%dٝ,i&%%пϟOii){~ivW_'JAuu6+U$kA IJUTT #43-/٤X7= >r m}PG@1I/_ [0X#FrO"b)N3"\#y#AFXltB I\w.; 6Z*@ -hwPHFhXט49T|?v Cǎ8c3f :\5 gBj'bPf׶u?Yee%)))5Ɇ$>=0d۟hj!^W[: $ey #:kCd{7H~ _rD,wDq_Ng߄Hޝo$ ݱ'UZ}3ֶ6OBȏn|h!‹Eb=V;o+@:'@?HW!;^3Ns1_Z3m91b]ˤak)!1j2L߿͖t4,3rrr ? ֩vk\zeLM!` #/93bGx#"lv=ݱ] tuǷCga1U{-e#`XHb8-{@imoɘ[&rf!iŅW"p3:c[؍#Ge6G L)9 + kvuag7O2JK2}{L>ǓO>GXַdj[VŠ骒,B5]IVI"U'bVqm&(++c˖-O駋ЗǓ/r龙2]vE_1-hmHߡ_V*5BH!Muu6PD1b*КTÀoxi߭15'o B׬6/Ezs-ܜ9\/[:CNM@))?aÆmU5gL7qqMUOU1x,Q?:#77P(j L:"޻K.|{Ga̘)+ی!m:l#A@ςM""Lx-x%Q|0 *wrb2<0.w}%BxaoPu xGZwbFjQ:^e.hQ(q#F%TE`7Qn|%wcm^:!ki]i0eSsw;v;('^UI=I!{wsQ`?w?(}sxTd5l]͛K۶mi%Se.tz#73|. ^KQQ%%%LӯJhLbXN:"ECl.~;3eʔscΜ hs| hۊg -`l]ks(D'Wfk51(oTwl<4|bҰBdL7nA3ƒs{+)1w"bQAL瞡3 ln_B7mPzl=͆ у<7ޘNkZ ~=_?{̙ŕ|r8q"ӦM3Ϭ7bKD{j0:9DbobS7233:t(CM6qA`ɒ h@`b㵸my(Cq/#>E$<샀$nD,cQfd B‹PIAmb7Ls4wD(EiܹQGR19Ѝh1]*^X%ljW! ved4'c /!C/Vm~U]^^N0$77Y c6m4':P(D  ###Xի%% g0Eǡ/6w$S]ĴS{Y1{9*t>i1+Pu"0*uܽD@gVo5MqwD D _1 :ALs ҙ#0܄$+oYZ!!c!>I El>,~z6Z$Nv3s➷w7si|݂eIѷo_3#Mɖǒ2e )(nX*f&gCmhP(D6m?)%Zlb9Pg9Fƍ6^d7{͎3 ͷ}ۢ"J 2NGQv+l8 Oz"ދI/DHifSe7~~7 &xId,&:n%UA  DօwHoAYvG@V/b-ch-6<-lDjݻIK Z|dXIA/)]!~1ߺC=RTAG(k)KFn rBtlos15{!'rX[ єq\t(nh 6JIGv+NGjG#/afiwD wo?8ж=D&5~Zcg5B,-]=W/#=A`hrl^ |_$yq"ZHr4"+2FSHoakZ8Bџ(ܮ=S5z_&Ƴ_Ex0'/rq5Xh[mu\f A(hɎ&w~"xcY@.P2U~1+ї U^VkҐ%\֨9W"vbVc$چ/vcy'"0-+Z!!wx"y>[E ۅHg.B:i(eoQJЍ"EnE x҇;S7.:п1+@ |̡r;tұG-ZlB箽#OQ$iQ l2LKZhsdMX,_=MDXjzS;xA,\Bi@#Y!JT?nVvaZbn@v6bх&:7 0-x/l.r\C*(#pnԍ+q}k:^Y| ;1ZCh9ڬƕ(ʒYHBH?؅]ݹ%HzR&x~KCh@={x*Ync>BF&?D mn@Mz[@o iߚʠ*عy]7|,d/42ۣhwPX[Ϩ`_F.ίF,n$!{XH,@AsĄtRSЮ`Ԑ/R\ƾh~5(xiih1) Ld]ݼC@HxkfPH#~k'xyhyhm>hcI,FG \@ :v:g WWW8着jhs?XKwUIIIN ޻̄ *%rߵk2u?>I1Qk}xYvwPqE6j3$,ӮVs/f-G UOE[QH^G 6>ίƫl"wCα!k_ ZQTG&1=wҩӑ̳v " 9;/+0`"_BZy~kSdqeWQc5[twV-mH`0HvvvR]@w6m}#}G{3J^x ig!6k@dgվ>F#P;Bh2 上\6b9"j1A6Bu1(6BCTjж? t6r}vW m-wg"En>6{~{3Fv5&,I;ylpwucArw.=?DNλ'1Ob, I#2hcۺ:[J"[#H$Ҥ7.DB*++wC:u7^(,5ތȋQd Zjo\@% Hе;N~a4M(m!BбipM]4t~5h5 tB HTL7ýH/ iS.?sQU(-wܜ@iߪҸME |\tA ȑE( 8x|'y%ǒFRUUUs\% J;};#iV о/TΟ7" Ffo|ZcBpO^-(|n"P,sRDXas6fu$>k^Rf$M܆gw{E\Z s7ļW*7rcZ3"f} ;"Y*-jTܜ32$A`[ouGӕr&%5҈3?Z~+//*KStE:UoH`%xgv:mΈyޏQBZȹԍc/9d$kET#0Al'W֝׶ :nuF3, b]YYTca3"${!4b[Tc;Jwh' [.tu9341ݸnF ν~bӦG`BBlHZ[8b}(-Ks'"L |>:lk4fpkw9aƍL4bš'oƲ,E<'''fb,ЩS_S([n N {0㩿N]H~Vg#کD5$ hACښ/r4-t945;QF(Þ)$ }FNݵuk[ޞn.b-un\!NCe-6䷘c-eh8b֐@oǶgH$¢E>}:W/`椓N JOO'Txis1 tC)ߔEm5}CVK8C1} nCڪ9ۄv%bz=PpOpGqi7 z#֢!I#vRfhK^BJP 3sJBeŐEKXH،}]oE@+3'T-&ĴEHrdzG7K#gE9܀_«ý;SW]u #G`Ʋ8 (++cȐ!|TUUlٲ]X?.:Ѿs7t~2 آМVQQA^;-9Jj#8:"pU|^jr FAQKquBZqhK{ED[1"t#]F4ḃK_p)H.Xv臤9஽+v{!W.BjX+zTq3hUhA0'B$e?!Bl؀v߸ _wӣHr{Js?ȝw1;~\~v[ta͜f^J(k/QzڴiqmٶJeEgcsrrZcuu5@P(TSykܳ xb*㫈teO=;r~{޽of^B̊T-x?{:\bG#]Ruq{z[AA&Yn `96GYTobIvBqH29Lcwi$CXrwEha% :r60fYYYdggUS^ *++kBM[C"GVRRBv?VAg&g ͢#C-~_|SXXW[gEERǹx P0gv/c,{a58g*HXe/$XJH~.,UHy-qסQZ?+v{?,/q,[;f d-£-x5]BB,ydq}$ZFa J\v؃d322j$@K 5M)hBGSḓi-E;%%65,VdHKF?oJJ }X^&_ ڂ?1PZ.ހX$;;Hn@Q }ގb;5p&t ^QBX:wBң3suF;{,HlFvE]o95dB=ňW#F=-n $Xַ3CR$iC whlذΝB]y IDAT ڃ %[fkXh0ԗVب]b]'V[oa61 JGوY/C@w1Oγ"=t= j#H]XRj$\#?ឭ"A`^ =H]c;³_!C/>b)hNGN[jT?"dDZH8$\Ge"Z3kq/r7ZCw!Ю]wbZ=XY{,{k0d+ K<꜄$@ @fff|( hQOgn:u-\tCP@y=bFݡ险8NCg޷CM#",4lr2FhKĉ v|w~w߳H8 {Ov37 ׮jee%u+ΣIߊ Yrn|׸9"p:S|j^Eڼ1Bu4Q砜g˖u1kek몪k6=zt1h|ZUŒZ fB`}@Nܿ^Zr*b9yC|w@XҦ0R[zTΘλv@sMg#7eAnWPw]э=S(+DrG=GZ(  =2 "TҔGzr>J"Fl&PߍB { Pn*v:Qom lטVаu@mT%ixNFbàg#5Bbtk  BV?+cE!@g Z?j;$ qcLӵG9 wG 1EF׮C2&z(df =cUNgn\͋%gX$3QWV bL1B丝lĢDTc=RW=߄bB w9HSoOyy={ȺukIՄgnݧM-Z6k5jw-iPP(TڐKh >كHCČc f(K`g"} !PA<^}y("O!| շ،$Eu;b6sQLwP7O 8c8&…ߑэӪmt|8dkkǢ Q#B Pdyt=^$77 >%=@ڒ_:PRRL߾Yz` g"b 5tSt3n'3Q"6יYV#iFm"J$qlBL^=tX!@@,vF12^JC@=1ՠ^\ƫO -F#Ie0J栤Vc͖mjvчuZvP&\GrĭnW{"KhWo@# F2*]bǢxϣAG$w܈=|#˾enW~F܏o  &rC,`Se3Yv2ZXwXF%fH2BP  )++#++7ᩮfe˻(?tӣжy(zEaZO"7j~D!o %lގ'pᎹ]3_@꣬w?DBTdz>#Pjdt˪yO0dff֩s5 t@;!c4͎g<Z-+^U{ĀwB̺Fs*zoFv.SNA$9|{SX4cQ@WNJOO2]?y%^k*ڂQC,rG+_kgh&*++#;;;nlYH$B~~~䀆D())+X;K6 phWK3 a󟑌  {;/Fzb${QY}}2p#b=еG1Y2/@MHV}pX֡{C;erZJ~i V.vs~w4<Zr{~Hk{?Z.G m՟e̞+ka 322j~к %dffMZZ5劊 ***BꎘE"V)9UUUQVV?٥kiENNNbNP6ȹ4X ŷn Cyx1~H{"gT/3vܟËto]X?\9v^`ϠnT[azK5fYYYdddk`f^N8)xiiYj?8Q"CHvÍhE| ^}f+:Ν;>tƝΤIo/RUUUÚ1egdd4ʲ[˶|[Ȓ%KXjmڴ^{-]tÆ iaK.SN9–x֙b@ _#:''qXYYYCl̬P?2e<Ì&bP ݇3e?~!l["m$_d!&1wERHJ6;x{qoPH.Ƣœ! =9~Ķ{t7#c޼/eH[o.]zww@>|E;Oh-1xm6"^|Sk?!@~wOǢ R<Ea9o߾ӭ,:bĀ9ޢa~$ؘ>#Oŋe]k8Sԩn9˗s]w1aMF(vۍ#8ٞɬU2Ƭ! (--%==d\^^^lϚC9 y#!0 .DLo)b`#@4pݱwu} <\{]Ǭar"8QcrٽR^~\עٗmJƴ1}QUhb(ǻ{'.6 3Hws }ukW"#hz;"~WGݣFډhg>ekT :.+zwߝj-[QGUԩSkuSYrek]s9^xzn VίZDjjj5MJ6^MY5G͗_.B(2 ,:Zk|ߺv 7 9-:%T}/CI/ix5A/'t~tqBGy!v ^+(䶠yyМeqfVonxvW#@ϩ}jw[4\ 3 -~vbB21br!b3dȾZ0`(dXiX64-b$tO?H$aW\fT&M ~gٴi_P]WϠbG<5#wGX?k=}]u~A@MA{rBxKp#f~9@ Yx @B7V/|qb#`nBLK+(uAnrgs9AQ#sHR4H%]{?Q.G (o{`w( r"J:EYw?dl²Mʪ -9m˖-quݻ7۷gr)|75bF_΋/ȑG"cU2hӐ(//@$<ɘEg&gLᬯJٻO3XT\'I[Sނt e9uz}*mFvxA U{]'1Z/w6=-"тQ5M: m}E@e6PHjj9%%?ǚlA,]jq9߹q㞹-^ѣߠd 3^-G +$K:Cz .Bu;Q7W(1k(+7^hbu5껉W,;Q=c ;J)LMMk"5Ag苿c}ϙ3grI $y&VNk,R%0EN~u?IbauEE 4/kxI%l}Q|Qtd^xc֯Aca1sb>ho@z/o@÷!@bFĢd*r\ވ~zo7#%\! jKzoy]-o'㏧[n:Ζ_@B!IKK#///1\?}~t9c}"H8莔JKK8p?JJ(Q}V[&E_X\F" ރӻ H{[˪F[GQ9"g~xzv鯻" %< ٸq}B.]"bLnhA;]9kg Ek#9FQw] }?P<'fgUw/v`_ /=j}@4/~ (n.ϾɁSG?~<^zi5Ww4ūA[\VVVMչxXsC毳۾}{/yqױre5pwIBR]+ Vi9ҡ֪ qvk#x<tB J/!]m6K~߾}9CywѮ` Z2d#fe@;P-=jycގ\|jgKFH25a ǡ/hA{zaР(+K^$%%*>+F2f9YvC˓/J'ath'`S3 5N)XYYIUUUMh*_ǚ5=իHIi\$K,&,EJQ';CNsh@|ȦB~gK~˸Pt] "GZBDWֿr<@/!=ئOz_ ,F&o#?%yH3$L~RowU AٌwC LlϥȐeg٤H<o1ʪIHJKK WrcLnrwX '9/¥==xL ք@ҵkWvq:? hKmP@nH;}kTPo닑fz;rr^@Q\] _׏Ÿ((胸$nBlf$Irhp b"͸` ۡEqo4>7>u{9&/p"<671eʕ{-q2!|?l:bl+MO.A4sfʕtD"1MZ ⿿l\i0iVСf5ZYUUUKk!]1H;u ^xx9BY ٢}kAw gxc[m"f0b***jj"̚x 1NC7,Αy"h!s#Zu*ԗ G)& /_!  ̛7篯_S>9 ڮ3G"ϟOee%W\qE'l#*%Fm UGt?Ad*vY`sYZZZ[y/7Hz0}9حqzĝ7$#Y%z>[" |9uknd!x0ZDCNAXĈ=_xvg{'~rGP$1܇o"7ZLp㟂*t}Q(Tctǣ(}Շϕ-H~)6jƊ,5Egv k|6BHX ޣK.h5Z1@orp\}fAtbuuuRn/ʼn'[o!C,ҧD]]w\\͡v$oC }9y+DI!gpA @:w9X -~-\2`JKK&ѩS^|XGĮOC1ͫ:Ưj=Y vBN;ynnwDAygD{c_/hk֬>Cavo0; gM PRRRS,Vֆ̪-QJbE"ڴ遶E(f bW9#W>bw uPB.ھq-Fai@oWtdK8߁q+=:Gcܭ Wh GcwG>np׹9.-`%%+k>okoM3]:|72w\)**svoI@ǰp8LII yyy5i?t~Dž6ֺ' k2~ ӭ z!М}B sˡndIwA:*k埓KVs91~xY.C E*dJ&~XZt\otVXUt$kF 8#gWbZ=hc;gru2& }c4c@$`-W\qcȩk_ 4uqdܹj*+YgmakmY2ms67$VQx+5E"bs./*19d_إŝJ9J6邀#Wiy(puHc_#KG2%X34Z ' [@iUVVF}bw7mbBag}z.ӎQ wHy8p Cuߊ~Z=\MT 9C1V\}V:g3cиs2VO8 1GBЩ_ALx/Ѷ?˒F,x^_zy gHǽNWvF)!pG4!P_\f͚Ɂ /E"tZP#s APr.bmv?%EHƍl?k]w#Fޅ͛7J/Ѥ]`S>D*UWWh"VZj:4fI\:X^C5irssͭa uhNAss: gR;y5;{!Z_O݈{}oF@$+[Bh[n5@삜e UUUQQQT233s=>iJю[Qr J.Asy=ʼ<-dnhp!w.C1h1eÉq^tj޲qٵ}r&jēdP(Ĝ9swqpV ЍYcX\CFg?:w9RíFLe( IDATcg]oPI8A[h[{H7킘gzMEuD[+u-:|vG$h96_D`'p+b񺒃BA&rmBh">fEEּ~v.K\r9Y!UHTg;F͛7of̙~ ۯBb>`c0>kַ8X6s׻S^&6K/ĚS?Q[؀E;bm=ig}wq-z\$\aT\jƉnGa]pwA@͒;!E 1a nv.L3:blRёd.N,jܚmݸY Yd ~)s o}'Bkt2EU{wC?z 2uZIKmF&8" "`q2j59[f!yJ/'к!t Z4C`#ECTx]wZHh>ƣ99-~Eg҈OFslNh3ōbʃWһͲODw8' gsG"ӓZ <[UIj1hss~U̚ݘEx[\ucH$1r[ C&cA:k?겯 bi>G 羨|6Y܈@ 䈌 m T:Ы;gЭLU.2:u\SQQ*'?bſE`z+CU|-pu( ;PyD4ːf@Ț5k/##F{kLaVZZʜ9s}ӧOcwymۖ:pwj*nz&Lh6%\YYY\cGT%jMq@%k:CzzzNXF(@C`1U{ĸ@1fP޳ٻv#6$R/G;1tFcg#9b"z t)o1fK폓tXmZ҆ =+ Q P9;" ^^̏GQw ?Q,vh*thP(TSľ~Y|Јʕ+5k~xB sq3qD6oO?@=8q">(֭iXxp8zv2@;Q/utI_*JB;U7~'5WXNEGPDw}*3`܇kP6s:ߵE2"=EZ@J:6۳ (++# 6mv`1p#qE/Fh# rMA!v7 Q4;6 e.4PwZe~\d} /"/< :uJ}\j=zȁݫWZ?N9ڵkpV mpB [Nз[Q"֘#2^Bϻ!ﶱ ;TBq!=x,*Dն/ "Cj%b~ok*!2VsWssF"rrr%---&M1cUh>Ds"Ҝ>ĨC:(|j佑Č#'a,wZ@s;Hjz sϽ,BKcǎ 4O>ŋsw'<={b VXQC'''K"j3 c& $_0X:I&TvSG}4￟1Z("fx QB㋶(4g=ҧ^_@XRX1"Y_VgchYd,Xs'6DSO=o`s hS;+cgٜ,ښ+| ==i?HqYXBÙKGxtxhj:/Fs.fcǎ5S TӋգ/w'|GG4@("#P$͵(Jf21~_l}As9^APܿK2el1xp"MY"d, |'p1$=_jWYYYu #5Mp ocg֩SGv|5lx)u3 ArD}r om'sn'$Z)A70\5r;Tʭ#q̞=T*ɹs|Աtt fIX*A%UÜ"ZĪ™(Dz "|o)H'yw|Aƍ|7<$%r="zbإs0'|IfѢE~,dW`VV#G0~ᛄplkC6pgy.s!u_N"f:rQXG&xbwu][FaB: E36!R6RRxWvu >7lGnܸOCNl#KzDTC~#=eF-g **+K;fO{D:$<-f8q[N>,7swlX"..Iz؂ؠZWo" ɫݮ3D4D!CaHD@KKcwu܌.))anC^]Й%M!f)zDJB(B@뛬[ Ɲ 6l_!ǚ5kXr%$uրfP`m2Ci, D U~9 ؓ'OuL~1}DCx]:bcw~켳6G%^$!7S'nOOy _|?36m ]Hݵ".|."g *U}vt`7Fn޼ɒ%K]F* N\Ysgw;lyrEfX؜ <+#gL@̠7xfD3_u߶!tmCC9=!BKu=IΝ;γ'-/*vձ_a2UxV3K'II \3} 0r+7Dtimm4 :4\r@ dMíqWu/ '1DnYzr G䟯"6@7Bl=i;ܯ!w!t#j!|Pɧoϟj38Bnŕ$Д穭%''GIiȠδc0wNKK{5kV7}| ai2D+%l7ܼk?z?ӧW3VWPW'ttVr1/]]]~Y2c ֞@PuF#IUUUTUUt޽t:+e:~eP: Yzu۱L>ݾX,s1bbbj$%%pvkv.]ecgq_ E$&E8p!rCq8111L2)SDxJEEǏ'::FCbb"t:D9J| 4FCY1NH]B̜ZsPBlwenD.g),{ Ce~Ii*s"Iɵé5f¤|2Vr֮]˪UO8dWٳp* Gl68^ih45L\ TqfQ^~ (Wۣݷo#TyD=;vκuBߕ@b%yo3T0r555;wGy$;W`vv66m,]^z+Ql6L&Νf'--ӧO3k,|*VII s>,Qv4(!$w r-M@̺j-_~9!5_pJβ`wa0 hѢ~ U$IСC|,Yx$!!!$<** ՊJ \ks[xN :1gڕȆ\K\mP 'N0nܸF A۷/h$zF#fVˈ#5*dvqL0OpWkns-,+11hjG]N^^^ߓ\ t+Q*)zJKKYxqW }~@$1 <;RսAnG_gPtgUa rq8Z*Д$I={ׯ hnn޽bӦMtvvH~~>a$I455a0hiiA2l0z=^JBU3Йb0p3' tz/wmw7ɓ'IIIGkU6Ll۶z>.;_g,Y$ tMss3&Z-: RSS]Pwf_#Agt:"77 U9u#1U׸sl6~je׮]}0 //u1qD֬Yû;(:x"bulлi9s'njѣG{=^__ /@yy9ׯﷱzCBBͳ߾}6jdddV`ƌ/ᣢzIܙg\l_]›.Lܐܕ]$QYYɥKXb++w+>}Y^}U+jQ;SOe֯_رcDpsPdjL;:gOctUz(ىhހ4%%^*3tG3111a j***Xt)jk׮a4;WO~~_hk\\\̶mx;v' #T"_WhZnܸΝ;IbR]]Mll|.?}hRWl6JJJZ̟?_qPުDoرc (]cZ9<ĠhHNNFףj]`ァX }u/<@4֭8qRa 1rK(**p*h&MĤIP***(**"**^ɓ' ,@)f$IvcTnn҈U".\<-[Z旿%?=\dg%::Ξ=jW!+=5Ew'pN+ 03zh&O%@+qlZF#P92(U7w \C@lJ;k׮QRR… IHHp%@@'/s!9Y֓f/ILL9GM:B]yŞ>$I?:.]w:`L&nݺoG}DAAA/QULRf7 Hč7(--̙3hZ{b6ioݎnj%rjm TIDAT[.9r$˖-{r,fFU DT*IIIv$Iܼy`Æ ###|2IIIjlzbh=ERXV=JZZuuu,X{0 %@"JbԨQ٫^$PWWdž cԩЀFA&*_hĄ'UXVN:Ettt*q  (Z_X,1bĈ;koo`0PWWFA+S& IG U+ZZZ(..fdeey F ʸ|2FCZZ~ i‚QҥK#}cS!4(zʎAWWgϞd2Vj$''{U*x[uvv*lb6Yp (zw^t:?OVVwrz;::`jiF{{;'N{e~B䠨8xbbb2e SLLǏZ4i&?E2/^ uBxB?TL8+V0qDv܉$IܺuG_S^^۷Ö111l6f3f6{[, F***XrJcʎB&OLRR>͆dܹsHF!!!^O|||sD}-=Aqq1iiiL6-DJ 0d$^Y.Offf|o;\~SN1o< DvV*"f 6PXXŋ|8. @+s z+WPSSCee%| ӧOg{;H$RHIIDnjjt 6 ^lƍL6oCKss3'O'`˜1cHHH`ƍv۶h7Xp!˖-}D6(Z!ȊٳgR T* ͛7پ};SRRRBFF>&.^Hee%999w! q۶mn;J E$T*"lڴtR^z%=܀ٳgÆ c͚5ܾ}@cc#ZVkخV3g`Z? TBH[[\zP8qI&1vpUahĝZ7|(4 ;vH{c6)++&{1t:]0@QBD⮮#N!bQ ^石yfL&k֬ ]WjG9(*Yh_5k׮aK-o>Ξ=kO{ &경i&:;;ill$??0T!(3h%>>c:t7ϑ#G8|0ǎwMMM<ܺu+ &]srr(((_9s&%%%aB0Qf ^k.fΜY~=}t|. +vAI!v;wYhBd̠ʕ++l6y뭷B>O?47oF$]֭[C>У̠9ߪU=BQ6 /9ߗ_~իWBHPBk0o< jk %š?%%%`X0ܹ3l(JCAAAah(JVPPPd^~ ;ёIENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/ellipf.png0000644000175000017500000005345112253362407022670 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxwXWTEJ. *bǖ{MX5v#*$K4]{71hT5((RT"JDڲ=@d]ey'O0sf8^ιEDDh^a.8ַ@QPxxxxxxxxxxxxxxxxxxxxx\.aÆk2 cǎ+/^G;qF|E8qÆ ޽{h= hoooԨQM6-r=66666[[[CO@@@@ï\L///z #GD&MРADEEsΈB <X,iii033!amm͛k"p:?7ٳg1c 337~9Ek "^s-''j*>BT |}}U@DgnܸAׯ~=zD4vX5k-\:u҃eٱc45|_{{{@z6*V;qDTѣzQѣGKjժV*`9755UTʆ>f|U"ay "}+hBзưP(@DHIIuԂ>f |U!"- &M¡C!3R)쌠 }0b #h\odV֜e2RSS4}o`q7kHIINfY5_U0---NNNV֜ F|mԃ>f |UdNNNַF[AcXsc.bk`2@[[[[A#\\\190`Y5_U0"a"!+)%XUZ(֜Kq,* BP\>f |Ud5g???4}- uk...HLLԪUK6bk`2@ EBÚL&CBBvzQ%%9Η}c#a2@ EBÚstt4  >f u|a`LhH{XsvttDbb"j֬ }k}_KCP),X%ޯ/ BP| 1/) q>è:6֪,Kk +b5:u`' Ǭfe"5 i>i7mFz2,&4EBNa9** [>fW@Ԫ(l T^Xtзb05`lhEx7DCl*{{Ў 0:5gCCUf)@,fdC<&ĆgU #hH{Xs VfY%9!fs 4{;g-ϷD?3o>+WU-Zmۢk׮7n\3BP,sc 4k}w{s 4P }R[Tecڥ*۴i={[p],_~g"a˗ Ь1_}Rb `{ j$b:8}DΝ u^^^_|" EBÚsll,4k}7_"B tq`T9m`܌wzĈt|}}!J|Դ|222OOO:u R>?WƗkNNNq:'%%A$!99Y.^7\ԹY ;/""1V1#ZA$!$$ 6T=xzzbӦMHKKQŋ XXX`ԨQ իW={bԩ :Νӗ>.,9LMMaaa$} K} WDԺ(D̚a,UI&СCZ8zo(_RRRPzul=ҷxs g#+" *b-76k/آ"ay%瘘@4Xc@99&~CtGSڙk ^5E(]sRK ]/q>=abc槛_- { &GJBÒ3>*7a\Ð6,WL%gVS,1P9t9aLJ̀E' mMgU#ha%aə4K} ֗g}\Zƨ;.DbQdUd%gV4K} 7+" nxs h kW]WL8"a9?|>S"jmx7ߠC5iV ׿`r- u+ W{2*X|ᗁ)J!6n.hƌ*A EBÊs||< Woaц"K狞ïRXvDGaV3^P$=875kgaoTN EVx &eeZ4EB`9?Xn]=h+}Oy}r C@dg\,8* BP V~E+}Oy|/rg퉃m[´A++ k &SBP3@׮]l9q>ó!hg`5+>;C]X_U09p@NNM4>G_"BG";G3F-|̹R3^P$=8?{ 4hU4>Χ,ߜ9俸TxCcZ'ֿ̰`r- u 2 /_-sFMID_p>JF&Uzk8u[p4_RCdϟ6mʄﻰ7Y{"0( 4}âۿ>>h-yHsLqEBÂs~ޞ wam_b/R¬uE =ML‚k p(xJ &GBP_ g]Xs>¯"GvhێW(JNG 8Zf۰plX6L"a4 šsmb D Zq#̚[M%aC̼0@fwսVJedf ⶇ,8Y}R"HqU1lln!2Jvfb`7:`=hW-KG)̜9SLe d2;Xxq"a9$$hԨ‚3)Cz^Vl:8\NGz VV;x/N yݦMٳзo߂'Na0zh"::fBP955qqqprr }Uw笈,<w!6^ܨYghEuzQ7LƆ>PӬGEӧOcܹsXlmm v,G(;7o3!8Et3G;vHDpJ^7ne`*8<#FK T ;3** U0Ь,xxxN?1=5޸Wކ/~\[p}CHVۡ*يwҮ~'4 ?Lbsa?4RĦM>!"^^^x"rssabb 5 M4aeesss_sǏrR1(;ϝ;۷oF{_U9$!/9UّfX8uoo3̹4]i>}{ڙ4i:tѿ_;zhϱX${] pvv_UY*G[xs0jP>M<}cz(r`]vO> ۰b09N(;а w_U9Z B& '6&Mx>>os)\bG1|oy{<EBg甔ǣiӦ06涰oIY!S p DNljۗqLZ >=)1)0 Ƿ"09V>;?~@ ߾%/"P ajUsš^kJ Ļ09ŷoIT3!nW"2[ YoPžmUf+ ݋%ח -' u[0D-7:WP$=|uFpp0֭ڵk\oi9M!|N8HAhl[ T^yw%7df`nZ|vpVL"ǏP(7.n!`J*ppy; >~=~~3F:m^/sϞ&&\LY,_շ4tL B(@N 7}_ g@w}$%NG{իT3ŧ(7* 18 ΃AAwߕtZ(:mWx^,yf0/ʺG `wLk ^^ctbTE O|sr5kf̀3g;;YyI }`2! u_@ǎ\oih͕7xS%O8ìvO:ѦoFNXlhS ރm>g\:c2`"J#hH{蜙G~G߲І2O狟QGKCrEЎ/D 8r–{[`flZ py@` gP$=|tRćAoYT9;*#n: , F"T7,9 /ƵZO}B]+JN\U,غ@ bM|tw |-8'IBPS0`ΨX#dX?\8pAQt`  pT56./{?FSBPѹG߲(2[pp~ΙW"i&GBPУG_盯:㬐*W_bKm!2aY_\싳:Xu1 |-ܟv:Rwps֮վfEa2@ EB74|ա,gY O?,XzpV=*|31<{r ЯI?Mj_"7زXKg4h>X:MI~ۂC~02ҾrE`2! uߜo޼ H]:|ա4! ~}@{g\)Ƕ{ǞC}81.|XOr˶UK_8ti.ؽ7+W" Ƙ8q*H@[ַQʕ$$%I)W[wP= 4kgbbF&RD})Ν :(1|-zAߘ>}:&OիW\D1sLl߾sBP9?ѽ{ᓯ&>FԪ(řh^ͪkkLk:@tm3FT|C"?89'N66\ˋOj*һ];m[5kjW[5ݻwoЧO,] `ii Tz*6O׮q+zU=|UR<ϳQYU8USW~Cߝ}& jV 'hG7W Ëq N_8rXHLf u)жmۊ]W(ԧOJE7ԴBff4/J֭KZ}|Մ|W"*$|Byyz6+_}C]D3Ϥ77X1c s};qn&uRD ?#8a͐dꫯ}M,yyyE?ĩS!!!j×kѼqaɓG˖-ao]}/_Us⹋x3x,[?6Flݛ{y.ڭm{/qstFBTv?c֬ dV<=k /o~sX ժشi+H##G=ٳi߾}tEӧӔ)ShժUŞrc-[oC~H XЛk:RIG:A,i$WuW5k s .$иaZv$kͷ‹ 2D C}+h _H֭[_uHJݦ}G|)+2KJ'G>"pfxED:0C s{]3^WZo(233Ԕ,--)77W:FTRXat$OIзIs"2ZiDp5՜#ua))DqΎ?!}X5զ W[lVVUnב? \|- E+Dp DD8zs/EtZ4̰̌Z4FE}Pp~HJUV~5`j71 %w8+`h%_"ayV>8?0dȐ2oIdGeÿ?^zzh{-ͨz=OydžbpDEcH`~ł3P>H.~ L q4 Ώݻ&py$ 4g֐ڐ!JlllH$QRRuMd! $ݏr^[i单dڔnO/""/LwB[RSkuk"-09V}; 66~!jԨQ}"Bh<yyqm{|% Zie7 <F8 -n9(FFMr"Um\ t%_dZXI{|aԺ_߾o)2)I' "~G[}2%'OĎ;ФzPWv"Z?T+!ׯ|å5D".~=Pxwϗ0"ѷӧÇW~}'J2`.;Dg̻2l,Og?f˵F\C=nN sj*0>мB IDAT9쀓'oo3#x'* #hLBݣO#~F}|>C!UpI]9'f&bᵅ8pöv jT6nrr6l>\x_Wh^ɾ Z(}:{xx>Sї/!vk, V5oNeg@ {NG 8f!(ppU*Ç֬b`rn繱c5WrΜ $'Sr)rS2$L7oo2Q*(5$7%xUy=$^!&n}CisN]BCуO·dC(}9?~k\eS)0mg[Fmh9%+K/^߽ :=g0@6ofCn[Κ;VqSrQ#.KX ݻ0GDcǸGnjѳ< ~r5f-3{| ݩw33ww'Our{4r9w_Ӧ܌*Uu) VABC'ܽ{PǎR"Rɧ20pP US vrv %J~P#[?-YRm@D"/$zJڀoP$=p>|0`ĉ?[ ' w^f Zm Cg 5uJfo܊V[g~W4zDʿ-s>BB7b7BBB!C`࣏xؤ"sW F< ++ؘSBIϗr'm0Ac+J:toOpU[[By -}|yݻw͈Ff4iBYE+HX Xl OQzuו"e'``a'zTsxr8\/g->æ~PϼaS`e{[s~$}{tv ;;uDm)?wdC(v_SL)}t" vp㢵 7Ş~q>=LΥ3Ξ647ϏIS+73XhGŋϹ6ABC@DDDjР59@Nd<ʠ; z!$V6.Pm զ}=Q*\6n$譜ܿڵ 9(.N{ bFБ8poߎ7ojmH{*yS\mh7b2;#':BqjȻi1m0}ab!hN7wInT nse,{WE!!Tu+[QX+`=gZ`yzzmٲ&L@O@ :zQyn'kRItQ˖C[ -H~"~ohҤ .] P*1c P(?yN(r>u1|p4hРTԗNPZnOd2'Ǭ S}ﱤT5R_,\``n-4nUV۷?w&ڭvT ?w ޾}{ݻ% mذ֮]KEO6l,+O[Ac*k׮UQ`@@B>|(nZ\!>#lRؤ4I}Oԯ_0cG7T@ѠADS2WwA@JJ fϞ,۷{#9~9z5]w(0jBBCnsful1?4k?iin)>xzzbӦMHKK 8f̘A2^|I#GTyϑ#Gޞ\]]iٴo>x")J6mӋ=Ƿ_C4':: ښ233+_}E&$H_g/}I"w;h:"KK.afF|9QzMzEw,,֬!J|-2Zs[Ǎt>PEѵy-]T+T*$$b ݲmRAJ ߯P*4rVINQ\450& kT\7fSS.P~]>zƌta hv}7/ u.ZjdbbB(PWσH 4IɗtG@|u3$rї群יE#^87_lm #GrkHt4Qo-y kP$,&M?3233ѲeK,]\{,B(]:ڵ R3gD:ufY x2 -ϷY 3|ۤc(Hub=dIc"#N/^p jA#Hn݁@^`f=oPǬB'EBD-[cg@XIXY*b۶m000w}vK| N~H-փ3p鄭 ;T粜 ˹ 򝜀)Sܿ?a M>T6268*-8#XU őN6mȂ ~ P 6?~||^di~$PARh3BBsǟOLDG5mZۗBpO++"ww7o*_o͒"хT*5kX,7vGl_I=4"lFWr9,ѱ>>=y*qX[s2ʹP$d`yqFu.w܉$|gpttjo>'s!6fsB9zλZaC p @OrR ?l ={r.]n705[}P&hd$'e|UB @٤|@Z꒗G]]ӇRoj)/hC ÏԨ7P8AԼyሹ{w"D|}>:ul!blW[A EBݣm͛7#%%&MV8lwǛKoPչ*\qegɑ`psaƸ0N9 5oPO?mGAA(5 n&q{ oocm׀ b=fW%$tL||<1EEU`Y iwȧֿmTw5*9p W22טRIt QօCvR*_/-޽DZ}oE8`qCm:\7o6,h '"`bsQv}oȰ渌8̻2ǟo;@S릚7F;[ڷGիW99\^yV. p=j{̚JPhӧd``@VVVZ?[Tҋܲâ=F+)h-d֜> :YY ;ܷkW8mߞzv3 D+V-pM;Q. JB&4:Qd D觟~j.~}ujO+;`Ϳ2ҳ5xJ:t(..\ ?~L4ujrlh[3-0 ox4FΗ/_&ԤIbb4U=!I w)QF}2h)3FkސRIt2QNMgEԒ )lܜ믉?/wX{+h-N(NNN9:::}-rRNBNV(wUPcM:pXtRIt*Q΅QU+ӧffCT؄_\"|-B`XQQm۶!44ðaô|!C!UZp bSq}0, ¬7BH$倏wE 8|xG;| ]o>(AZ5_#hH{*Mfffddd%Jb*WߔrFb;rl{$L(ɧ4v,!!رDZڕW EB=N( -Zm)r:#$7%x$-RINMpY=\7o}Qa`vr":vG rSv-|zuŋbuwv-0")ٳg Uxs7;$ڷ)ퟒe%QC= LLJ۸(?:8pҸ\ KҞ=,f=zo(F't… IzeEei^]H+ [ț5rn%{DϹ慏P3"P-EJbEy.\/_bر4hP?;F*xy[3 ^){ח≹"&=Ռ; \nw>W|sens%o[Fu&&fsr֩r; GBPh $FJzSxA*Hg:1b4XeCF~;rr}>Z6PU^|A=@ɓCEx񂜝ՕmVk|t#tr+g$nޠ%\Z YyY 2YeBpo'g^7!СEw2DkիWt EآFmllpX[[.`ii Tzݨy޼yĈ#0zh?K!UɈ'h}5NQB=-rD"g>F&~8 6 h׎ШAnRrX0mZ;CׯsMNĥ6JBēǀT^W(ԧOJ̹sծ]5~>+:~@H= lJCLZ <1 1 xÏq[{mlv.uثTr;XXժDgi.C[>. X KKK\<==q)@HHd2¿9rM&EW^Ŕ)SpYլYSH3.g:5zYAOMl" -fG'j&ę1gF1S BU+iը܉  801)lv6lZ K2_}X-kwd2޸sjOOOlڴ iiitff&͘1lmmՕG/^$ooo>}:M2VZUH{rV(4p@@@ަ$B !EsnFޤ滚A+ iѵEs^ƌ)Zmz"hT`lƥ>%ח`^wž{жnے}_V~ P(kkٳjՊ=Ý~/@j*wGn!Ct'kP$=W\IёZ*JC7 aHSz1PT!CTǚwP gߟI,/駟Dk֔8{pqn?2%{>md['?D"UV8T.S.mMq^81u;Э8TJL*H ѪU*j4FDKihQ? b Z(ꞷCCC‚ɓ'|6+*|o~^Szv:G+ {Z%?M }=1(xn:%觟6,/:R`x|։eRRRŋ˾_B>5|H uW%T*ēTS};|9m%lC4kV7 e˸(DnnDffy"//4l[l Lccc3aaa:t(V^]D_"b^HA?>ljŪp+WmD=zo֯́ j^UT7s\X65kY6a|+&tff4"::NNNЈ(ܹW\A-pQK8U@،0$ID _Qgbfea:l\8Z;bנ]ݸw_* ـ0o[k۷G ܵu77.8רQ  !!͛7UEJ#+26;cs!y{_fx|։%n޼I&&&$*Q*9p~Wa*7;ʑ[ꚪwV;+ &'sZWLMIBAt"Q߾jՈ%PswQ[la2- uÓ'O#''6mËݣ)e&@l*GԙP<,y!싳S|{T5zRSޖ-@Fܹ…\L9lmW_qgYZaY| BPDEEHMM… U'#@/I8̋W4w±cMM\62f/llp7vtMgšHHqǓ=ɓ'co?$7)h۽md΂.|h[R)Dիs9 CCn%[EH__n5(No_@&P$ԪU+@Ç}P;geEgI ݲEIgNs{5`vF_;S૷̟'L6۷5n\cKT@@-0'##ѵkWxzz#cN jzo0`ggTiT$%k Uk>?bbj5kXQww/_;{n٫@ aJ}\9 UEB>!Y̔IDATJ1x`ܹs~!.\3332O狞C7r|m>m st=MÛ7nBB1d _`L.8'N &q 33 ￁:,Rԭ[7@۷6*-Fs/% 1vo[sb^1$!C>$^ deEhQlle÷:9#o2220`ܺu ۷իWaeeH 6Hư "{| ;m076΁;`tys`D "w'a ZFׯs'k{Kkk΂ =cbbhԡC" M6\]]iŞr,͛`N FΊ\=[p4Τ5޽5o)X111E. '&&ݻwG@@z ///XXX BєF;v1 λFaC =e@a\ѯS'HVĐ=ѬwrDpЩS|,Xs|+^  **  zFF<<)9d'wҼ9NW[ BP ??-[~͗Eg'''޸saÆjOOOlڴ iiiGff&͘1lmmՕG/^$RIӦM#7779ha%aܿjԨA/\)lZLWAM5a޽\"?ѪeI)~/=J[bX[FĞ˷/rК·N.]"333@s%BAϽ[oK2 9p LVĝ]WN9" Jt[/ bMƴi ˱n:wg!nok.1`?;̓s& KG(~\ѱ$1֜ʇ9!"Z &M:tzCFhv΍>f4Gl,lp~\}ɓu4wzYqjV/ÿ YbY|A[ޓ6778x U?O G_GO)փ" ãG0v_a]cT ,-4t\mܨ;`8ngX5gar-$,$%% Q~}\;v 5WDHLD.LۂnQ#toljڍ Ash"m ĝZ j5garmmmopqqI2dжm[e"+3 0Þ CJ` jFO0D1> `j;\1v,Y Dqc]š[0"!cԨQHIICH. ܃"̮1?U/g-> ׏ ݺ"u k{΂od}.v܉#%%sF1xݬ^=thZaEX ʶZF'Nrн{apֶse/[09~_999pssall5=֠ӟRri >0gvv sp*j D?XۄNΕk{΂o},ᣏ>Qۺ6كN;Ad-91Pc09k1#VTðI9`̘!BXpHY[Ε k{΂o}+޾}#GīWжA[,[dk$vL[7$%vϐaboK+c& 9qRZ5_=ga2@/E|~ \ᵇ5}TUe?D &j` W71ӣx 쒎c`/)Z0S+gʚtl8.WC\x8ý_>|:Ixxz>~=nҒe[z9n,7|P; ־I(܆8 i Y#RjڵkH"2()#idFK8e練mD; >p1g"9+_n%aUU6\qg*Sݮ7oǿ)yn4}=gprsCc͌4_|GZzIO?Z>nq7b_זÞoCX\pّ 򜕯AK- 뙟648~+^a'OkMԾ%qFN;KA/sV#2%$ᣇ‹irx `ބm,` 򜕯zp8HHH ))s6Y|mI* 5M#Dtt+skR̔2~˨#i9,|AC:''x֬YCUUU@- VnNJIxiS__D<=i*#x?E p#Y{'"hۛVHyW Jlذvf/ 5Mc˦-`3&e\Y^GS'#yYmE/sVch@a a}777V+7l۶0.///6o|?uyJc{AhsN'r}<0ɓ*߸ra qg/_ĄH-9+_QO'gϜeXPLCU]yIiʹT^Eg'OYeh,9+_F=Ih/0p< Տ^?wĕq. @i s 򜕯<л$t:,0 NogthFD>G'ypO4Is 򜕯AY)&qT"G/ 7f@p^N"b=IJB4_|GZ[)ԛHbX^̢C;9AfY!Y/sV#rK¼xcLΓxAlx4~d i|AJ³'j_n44:z!]:2kf#}|+ i|AGM]jW5ǽGX0vم7 ~WȾ ,9+_nʒ"I!Yf\!V^M5$Ҝ,o ,9+_yc%i=twovd`@+"CuIJB4_|GUI7k7zhgՉUݧm*i|A}kIxE^zb#'StmZhϕ2%= H,+9KyWDƤѭ}WW|IuĶ$?k>;C˟yhye4gi Yꏡp8HHH ))s6_zә:umv5яD"N^s'h8c6Jnn F4_|Ѐ!>>5kPUU#I~~>;wfՄu!QXSb%ۋ\8]qwY?9KyW J݋LEEm;vlXsKĴ/{̦whF#Y/sVcmvAAAv DAAt]xx8= W.ºIٱcB4_\}ŋZQX4M+1;l6JP2dSNӓZ֮]kBP}7SLח6mڐ\+MF-p: pW >XtBJJר$--<ذnr6֭[r ̛70]udddP[[K`` .\`D4WJMM [ 00۞4]9uqqq[׳uؕY ի̤aQ -ĕspp0lذ&خ3vg~zF{V_w :5WG):m%r sWqssjR__o=c 3xٲe8<;^njxٱcEEE4{gJDWdggt: i83Ajj*;wdСCČlggg`y޽g׬صk_}uuuj ___FMhhhGaɒ%F6 SBP<̘CP(fT@+ IQhݻ4;v,P4ЊfW^aժUX,FZl9uVY|DYvZCVV.?&M036;B0)jP(&EBP BaRT@+ IQP(&EBP BaRT@+ IQP(&EBP BaRT@+ IW|_a IENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/legendre.py0000644000175000017500000000034612253362407023041 0ustar georgeskgeorgesk# Legendre polynomials P_n(x) on [-1,1] for n=0,1,2,3,4 f0 = lambda x: legendre(0,x) f1 = lambda x: legendre(1,x) f2 = lambda x: legendre(2,x) f3 = lambda x: legendre(3,x) f4 = lambda x: legendre(4,x) plot([f0,f1,f2,f3,f4],[-1,1])sympy-0.7.4.1/doc/src/modules/mpmath/plots/gi.py0000644000175000017500000000014012253362407021643 0ustar georgeskgeorgesk# Scorer function Gi(x) and Gi'(x) on the real line plot([scorergi, diffun(scorergi)], [-10,10])sympy-0.7.4.1/doc/src/modules/mpmath/plots/ber.png0000644000175000017500000004577312253362407022175 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxwxTU?3:@H()"E:XA,(DE֮R* *UЋHK$dRg IHrgs5j믿ԩSo5nD"tuILLbhD" ;b1@Ks|c(_GwGЁENEN^,B=bH& \9KZr K~v<וA0 *$46+Uy8 ?3O:}Q(G!MBcb3Bz<@Ai`u볼΅输#h"MBcuޝE|k;+6\SI(xHs|v?5H|z$TeC;Ihl9LOt5 nEtFb7w&a`1@Ks|Zc[f#41-:nz40 ]rTC&[RBL}^3H0RIhl9f?{"$ ,hi`YØfݮ^WOiC 4 MƗ8lm%RVǛf&UZ;Ihl5;A*p@k6Ai`Oq*8Mit` edbMh@GFߨbH& S]BϋzusӥF!}g2Y<BqxIhl1>䔨+swMx3֢BCpڝ8KEQ{r/3䷷`,ڷ[ʹFh&!@Hlv9Hr:F/!S| Vd =;ܢ\N33ar0A@70>=73{L4DlQLB8c:pvf*A0@ &jպ~C9Y?~FA;#B"ݼ7Î FqwZv3K/qsyy$E'iMde'[]Ъt}t`Xt)=ݺucza!-zOQoΓd4n]bTQtlܑPK('d{RoF\:{Nήb?O5*Z)(\hI*]yTjDl&..x&r~3F08J4fGoJ@A1/_~>gx\F+8عci/Vgk,c3(B-Uϟu2[k\1:o:y7#)*O?YS\Vs5škvҲ̘1s2gvUQDﺉkݻkږͧÿ:IJf$%f7i6Y5=ݔ (k~`̑^ZBINz.n]bq=6nܹsy7٪Vh(2{le„ mݦ|zhU|r)-4ۨg2' O0vXYYb^!Gr.$ f߮i~ ^kم_J^sbgXw[=RTǏ{ M)BƟ-ڷeo^pݣFe\`ӻk+0K_>Y -$oݽ隨R̕ $46qsxYݺU1.e 99<-Vpˍ#his8<˃Li"fF|K_8\ sp{ wbe!.<)kf) gC 4 skx{4lĢ1e=g wbKNA/T<) jV9̌)^Lqh4 Ze2y~''珻ࢴ|-[֣[j\M놭UhzOi.܍-߿۳z/ź޴Zp9Q)Q)m&5fpɗS閟WmŇ9,{۸hӜ/[rx^Z{5eC[IhlOQ_<7͹Rg) {WN=kiP_z; T= |? Ob  =~/OʂPhܸskC{ل`6C#q C 4 7qo٥Ï78Q0p3x74ǓĬu|WŐ͛ -M+9 Y{C+ )$46w(w[#5.w6WޮS8ɸ&5 Ƀ}DAxpXm͊ꛬRT 0@Kxߎc;ioV\EY{Z:'uVMw )Eva0Rgt+š(b`P&q7_Jrnt#K.nG8BRoH$/oqTcFuƌm3~Pa)%%BZ;Ihl܉up++鋟滑?:gyЩt-onpÇ>myW{+7 4 M]9&/=?߃l_}]f(5ڧkywRбqGZ뗉+c]G&a9 hi/gČ;ӶGw׶(B+"ogL&S( ܎/-[vMgƺI*`P&) bvϫ= url+ Ok>ׅ/}7h"C"eKNܖ' 3 eHToݡu[r0`UmGѳ5վ]|D<#;綸p:E#!bb|L:gHTo\%9}/Wmv}$64k&oFsQ]R 0P8GƦ{9xlc\?zʊxc|xՇNۮ š-1ՙuk i= XgN&WU+߯IX>QdC{Ihlc׃yuąC}Ruv}|ͧK/wP_.uQ]sUiZ!h'"^ѫzY$\@\?u%5ژqq9փIx.UqRPyhC.EĈ6 D@L DGcY; tSmXKY_?ߛUWÕWBw yBR2^d1ePB!\z?n@kyof0[U )$#{‚B,SxOǎ[Z'VLmq:?K?-$b<iw;~ׅZ}wcn*ӷMYK>G{d2mdCߜ &ժxɓ0s&|5^}p<.LwŭK:\&O.~Rg)73hڱ>|3|w<HBKϴP.HJbyrlE6"K2TXެ%BbE;2& SR?FŤIB;{L5rsǠgOyDZ,|+2iDu.* #C焊n oUsJ Ea( tZ}g6)sxb!wS  lKpyrA0@KK֯˚]s ̙V+LV*_#fG!e2^Z>Ck܇M61*KLg={f>}c>mrvs}wZ`^_C3ho`[j'Gs.r )R Ng>}&hKzڛ'?d!b :4pֹuפHsl"ݛ6/)=^vw۫wCZ{(d] jNRgV,Td)#MB78}Z/ aN}W$&?9ҧ%_ H扛yX5D܊S'^yIɓEjC<4،`Mgd2ޯPE\h]]ToLqhϹ`z!DmHJcE5Ff"};['L8_v.'=;^ebaemԶ׹_H@lyG^nc\r!I_ꪣLa畧9xP;š,W0$B`3FvF+wJbl%iuV}[ {Wƍbb\~Q׃ h^B_C3߫׫97 Mdm2;iG 1ްAqܿ =&xFSlDCNNBi;mbRD 10J%,XkssO˖^{gIZ\Iqr}NN';|p}OH3ƌH @NOEvn6^"qOKh+H/YQgFEE]jlOxr!0ц3YgWשMB0Ao`ݳMi,DX;V|WcjX 8J~ a8b=>FsW.;0Pv޲2LՁ0Lϻ(@a&!Q!Z;~C8{}{snDX">^F|̄#ha2pBH~q>oּšCM=UmR-M{.3:_aN.j?J1=bh6Jlhӌ .`Kv`j@zZ.=ǎoZf ZZ̭Z bA1{Ha,_@{.]ছ 9ٯm>)k0uTl"2p_6nUiGdkmk9aa=ZpJqA!:%m )">]}ջ.-hst%  |m:=3i?@9bu߾е:K%;'K,_3gNItt}#9ֿuPh*t$[b)˷ {z}OxOOTh/QB#hMBRA2z{ IDAT MB !%XwNyX=)7[ 0 wmddXQQxH+ʜ r_EaL:i[q0 !\0bܭT_+Ro%Zy.+iL0K]=td!t" f9^L:삨ːۙG:y1?s^(ꗯZ|-7ί6(Y.SڳffXhn*;b#k cE7~+Yy ĄA-1ь0wԆZFQ94iu> o 7{Z??gez:;[,[fz7 +jp@iiTpAZ3 Sy~=|dX-꫅6k{C;_-~Qhߋp8NVRFz&Ll֓k_˭SZ8Or%(1DžO;$@M~)XysM:SA6TxbN>M׮]պYh2D+t^*Dy@?ɫ1f3k'~n[̺m|d#od} odB(oN3k j?!0 YWYdm;96t3!qwEॗ_ROW^XLV\U" MBńj9dT@%_~$''g}#:r̙1n'IjBdKKEOBvqV{,e+f'cka#?2+R_N֝(%8:X)\ | p[\Fʃ+k=GQ`.̨ړPg`R}'0štK/{r1RRR ##3}f9`_y{؛[~$VYх}1i軽/C 弍1j(/ğ'x>#^}Uf2E/,ͬg2e,K08 1pE`4&\n=aSx13t2'g=GO2٤OH罞e%Oщ.ƞmgvܵkWENJvKv"]t`glذtS &c[{b/iD]زw{[RcDzq/ؼ},!4d6oL~q~X Nvim6m_y*=۰vC Iz7ndܹ"EtB'N䭷"77xYft=㉏'&&W^yo/EQPgSKC"##>}׳Zq& S BU( Bg.9\BQFEEgS![ (RSX;XUV,R4buu #ڵE:Y$,s8v{$t pΚM(8juքSPP@Νyꩧhذ? =$4L6 %a(]k.(+þˎ})Y}E*&wanCC*`"/YT]~V8X󡇄毁H?p /gp_Om{Ҡ<$bR?ԅȐHVgƩ8r̟#hM(8jfɒ%;vbuVǽ%({Ć+^UN ri'ouW-gf"FVMtbmkZ%OzdQbCHOO[n\p̗3Lxj8^lE⭉!dܹbQ~Խ~9ij W^,XԹv6~LB)1c| #GTrb= f:E)ƕBQFwv>k]c "K\"^oZ5L&Q6dM3#*nM[GX\b>J-CoDZ0v3ӎYUWUӧ/zyYDkq:%)ᡇRRnFd62%Iѕg>EXiTI{ )Sȉ~kvA*K6o.{u=ZޭI]WF|=9)>TLED"8$\[oF…[7"P&Z3 'M1qe"taOT5-GڢFUc2K #,1KIX_5pO!Oqj*ąT#F$$qФP0? 7$f$n RM|p-p_!O!| w9cF{4+aO ?ԠD"ܕU'3 0 eC$'Ą3؞%gUFݻGy_3.Pc[3[MZ[NQrң8N׼EDb?(5& [¸q"1cX#-MJF6CBvIVp>LB9F1 E #f&cQVB(NEp,hoQ䔈sJ)9ZRQ"P5jP“ÉM̅1Dw&55V1׿୷`2hADGBJQJN=/ yZᦻC mZëgA[bl0ּC0aȭo$t2jaAdHmXY9+(n>7EՅ5n w!gU/غ6 #(+ݻŲ~}qfJY^5!\0 ׋(x 1EY%UE|=$}F >.HEꛎlХ~(;Rhijː!xJk6t_pup(|j dlᱴnК}n>nj)r.zW/cKrз# `C7=8XDY[{KA?}g]f'S@CwnvbjW-M|={O]D *غ&<#*ڟ#h]HlNQU84 Cj*,_.6.YnYUuƏO?UrQJqss-/()} MB}ѸXcMu鍊&3g©S_%Ю*z;s.ҍ&S@#)I?Լ9|-Hw|@, Ωڵ)B/}Z#Pf~-MB}&DQ#1A|]|*9d[n8K a_5)f  )$/:ᢣBK]|={7nB4WwY# ?AZG}VxXz@wLgFџ|ӥ@oZ/}A; sЊ":`P&8P,g5s[w߉UD*-z黳RBZ{Ih >\x#l&e| Q/-bkۨ-E@Mw:njC/}W!rh0@K8#V<}Z:O48G.S-W/}WQfJqaTIh,zJL߷O/@]t(^Zƍ3D/YevA8I *$4fXx@Lb*$+^EUhңB/Y9h$4baF0uj&_|ujqt^Eߥ^ͳdC?HИl gҞE&Awt([\KOvƄ{2;$4.^ G:%%b9CnQ F3fx@]nmMFmɰe[˽)8t4 /wgx8ruF4D6u;S9gO/f9hiB;S@ns-Fb_Q@7 t߹ݻC.z7*) MBcIX̙͚ iӴnU54 wSb'v˽)8t4 +$[Q6iݫq*s a#}[7h {~{?ܛgA~&bM7AI LJ f/ dݺDb5u{2;$46{Y֯u͛!4jт,<˽yVLqi񅄈EY`(1 rLp8?k0ۭۙ:$5˽i3c 1]f'S#MBcSS|iig643FiN&p;;Vr%<1toZ,2šGIhljoH0kH׵l1Bѭw5 tFz7QfNb ZHߛoBNlTm .n tHDo6=Gz7-QR&+>UTEDOڵlXMh;r 7 Tm&6BvAެRj'SAƦ?zK7C^^VwzMJl OHӽYN4 ;MpfIШp ?{j q*N:﫧{GT-RC 4 ;LbTQЫ$&b:TTbCcެ2%V-RC 4 5h RNT'f3\}^SweIK;7[Ofv}#h&${f&yzDm(<^7s{ʊv.#h&4W^VScCZD-_.]T\4IOLqiO㋊IӮz1x0lbE褨$#8ug<+!QA!&&.݄ :HsE|>=jd2>=edT=ݛfk2;=`P&6^7*7ZOݮ]"߳gs2h3+ڕ t7@ʤIxg<.MBcm|VTklrkۻW(e Koהo=ݛ)[#Gaa!IIIU&%ᡇD*xx R45>s <-{@h~*?ӦMc֭|嗬Z=A&5^vF~Jr!CqѢw-_.A_yAެ(o#Se˖̟?ϭZ|RROw~~>3f`ܹ̙3]vU|dF},55U7mc۷z;w:v1yѻ7VKBIҥ8pu6)<0K[7oU-nQQ^]oƍ̝;7x͆PeCvءqw߭w}U5jU`ذaM+j裊2d8\=Rg=Ͼ{&w~;)_sر7LEt1:gGVyNF{:v~v.Dž zʟ}A3 to垔Ai⋈U <08eg`q!Ͼ[L3]cF*U@4 =OÝwhۭY#޸껬,Jvk\j wA9بOMO/?T5c6àAbGεam9YN0{B k-ZșFE#$Oȿߩ%sr@j }ެA/'G@uO>)fTK[Y0B#"K]ȆGij/wZto&̑fT@iе:_ iiкX9Nm1sK=UVKہMKGI>&W|aaWbu p/xypѺAkܫJ[voZ,8K-(Z/H3 .;#~r;@7,\uZofEp9 $46}a:xe?ba +VmR;Gahڶ5 voVTr!GzAY,"a ~zȨ(Q;u5?XjvoVh9$46Mxu= FԎ͵y7 PNofŊvrIhlĉps'<ޤZZNy :*B9$MBrIhl}[gֲPDtۼyb?bu.,f -[SCAݛg:\4 M k_xxyy*ATp$O͛UmtqF߮*S#͡{b_B$46jw=7V+6MCv-Q@ެ[4 M3`ThD<\ƛjTVlsBI \wȠNJݛ)KX%0@Kh_B|1/ޣdUnj1CokYj{Bb5n0@KhUW q>z&MRqGi_TN#5n,vz7+fh`H&27%c IDAT߄-`;/\Ik3fQh5JvoZŧ#$ZC 4 ĈY&pJ>]FQD w(AtX4Q8uӵvoZBJpX  4 ]|fnf  +|ʝ,^ ;v@>"YJV^OѺcs *$46zYR]TtJ#>^Z{UhS%C*c6 vH$46z/, fFôi*\\%IIn[MԪCUƢTx9RRIhl_rg6uߟtZ?@l5aQk6^΅$fJ-hi=7hHwI!6ߟ@N;$DTTr8SwJ$46zGk`^_SR`68uʣ.<)Lfͼ|:Pk8D{NrC 4 3Dy)֯2/@(nqKH<]Ms-Zpj`H&c|qqbJd|L+/9oĉb`2hՠEz}eTIhlj n ~k7l(~qS 9O{~FThL!RM hi=wGPZ*L?ݯbc KK='i> ^6M(]`Gh MBcK +ŖYb/.3` aӦ:ϝ5KR5ɓ}lav,8L8KZFu )$46FG Ó'łtfffIu9&|e`֚WNw}WP#.ZH!> ^{ ƍ,huҾ}jU^<zkK%| (m!(=bH&1J|f<b˰au&==b$V<_?xy?P42lޯ+(7Ihl,f;`;jтݻW,Dvv~(vxAbT"YyY8J,)] MBccƍ_~4ZFbarHM P+JGzn,GBƨ $&kBϞb;ΜS[@;W/];q\JƉOLw}+(RnJ $46F}{XJ<+: ֵg"-/W2nعeF.|hݎ%G@igiٻw+Qڵb&1\v09HM69ʕ$57]T8ΐإIhl%ƍE#鐕՝Cʅxu̫2՘]usHKhݎT Jp )Ih>|Lb`>bV}i*RkCw}WP% )ІLqHs|Ub[׭Ӧ15t&ݎ%\Ԛ2; MBcUsgw>&QY‚$,+rIhl9*A.pO;}d&%6cczz]]7iK~Ru4 M0wVl={YyYVW}W>ib2Us$ f9bs oL-Rɡ+AZu4 M0wVl=zG5hRu4 M0wVl:œ6lK_&\9h){]v-ĉyǫrF`Μ9Z7s|gf@/ꀊtscĎjl6&)SЯ_?L&3gdĈ|Vj:'XXs5_,KCW}Adžꌠy޿?W\qE󲲲HNN --VusTEQ|Irssiڴ)}M򙬬,ymڵkٱc< >nA&$t``|'|,]T믿ȑ#n_3g hYn ߦDwf7$''WL~י:u*| ׷;p0@e_|ߴn,[ڴiuSŸɅ^Ȕ)Sغu+׺Ir饗2vXE <4@vvvjC tJJJE tFF)))H}|Mv;MQ_۷3e͛޽{nаaC@s)[._~%O<_}lٲE&N&MQ۶p9`6[qeaX2eMRe˖#hU),,d4lؐ2NuTe<󤤤ͧ~Jdd C=ꪫ8q"Z\qxZ7X-H$JqH$ɹhD")R%A… yg8p7x#NH}/y衇~4Ip"$DD")DS@K$N-H$:E D")DS@K$N-H$:E D")DS@K$N-H$:oyIENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/hankel1_c.py0000644000175000017500000000015212253362407023074 0ustar georgeskgeorgesk# Hankel function H1_n(z) in the complex plane cplot(lambda z: hankel1(1,z), [-8,8], [-8,8], points=50000)sympy-0.7.4.1/doc/src/modules/mpmath/plots/spherharm43.py0000644000175000017500000000052012253362407023406 0ustar georgeskgeorgesk# Real part of spherical harmonic Y_(4,3)(theta,phi) def Y(l,m): def g(theta,phi): R = abs(fp.re(fp.spherharm(l,m,theta,phi))) x = R*fp.cos(phi)*fp.sin(theta) y = R*fp.sin(phi)*fp.sin(theta) z = R*fp.cos(theta) return [x,y,z] return g fp.splot(Y(4,3), [0,fp.pi], [0,2*fp.pi], points=300)sympy-0.7.4.1/doc/src/modules/mpmath/plots/hankel1.py0000644000175000017500000000031112253362407022567 0ustar georgeskgeorgesk# Hankel function H1_n(x) on the real line for n=0,1,2,3 h0 = lambda x: hankel1(0,x) h1 = lambda x: hankel1(1,x) h2 = lambda x: hankel1(2,x) h3 = lambda x: hankel1(3,x) plot([h0,h1,h2,h3],[0,6],[-2,1])sympy-0.7.4.1/doc/src/modules/mpmath/plots/kleinj2.png0000644000175000017500000017043312253362407022753 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxM,[v[;"?Ϲg KHݖ'b% jĄ!r [ʞx`O`a -d*zs#>b֎Rܟ%dܽ*m^֟|'½)oږ'2pXX7^Ps\NI:I%vi@xzkw}/O~ߒ5OZC|= Jϓ?9hg_{ iڀ7?v)?}8c4)K.aE/}<Z#{1_\N1_zjtuգ^ _?S| z,,>B?^M-}Sԫyz]g**^nSYDR#5C즚Mg}S%˛!M ~α΋\8 S&x?ǫ}qEa>ݭW~J!6dPq?jQooâljڽ8d?o6F:#o>Z^{z/`x 3}A ۶K stq@y,qXozӯC70#9:$sb1I 7770WpubD޸@gdq<_i0⽄0'Œ@1 IWY`^e?}|Uu3R|i,c>)Rs17#Ͻ95#< w?x }ޗ$|x٠%Ǘțnz5gj"?ejq.:G]urG$\>>|Io.kJFmR% ?|?s^0'E EZ"AXa~Cf@:*2ECma:ׁ&̧hMcp* 7*%&L^fm_V|*K,yE@fQ2$yh9>/jӒ@"m;I1M_vrMT%@W6usIj+]kBb^[¼nc`;y>h*)lṠθL.1?m+7=תmZ5 ܪ8@ؓo7$@ _}N5~`ET^[(5urR~WJL7!W1OTY*l׍c>7IUVN |gHCH #UALYnmP&bIщgC[J|4 HL"=_VoIC~j_'Ej,57xnz$d_N|o>{ǿ7fϱl_ 䭣tHVF=]nMX"3@Y TJik׊4@_z1BBDu:!8InM\NeWXs V Q(?|FHkXg[kwr٤[+V- [t*9~__տ:{~+տZ~rΎش;.!pKE5;l/tJytaf1]nmo2U\ !{| ]tDԃT_V_/7kԇ^Pi2@1F$%֋|74褕HIOg6'b~5ygq-w/ )?vǿXASTa֙D77i7nbLdOEЃ)FZ:rQGպ7K&ö]uOg7 ԃUfs)cy,-Ƃ~R:3}dRpI-5 ۨ#5tu*_qw{C")}|欿%=ѺEuy=rGuNH(05,{T'eB}K^K8<&8qJj/p+O4ISk4󥯝sz-ĘJd:Ɯꙩ`Wpc{Ųa&Ff.l*ޜm712ǻڇbA~1WgH UBIBw?~.h=ŗ*ЧӉ|[ ~G~w?G?G8mggYSv-Q9ӀB@TKrdaՃN9b? D=@87sQ[7_5PdnQ~}ț{XKЩ8C-Y2AG s/IqB9^H~3~]Nu_z,1m/%bq/?lxSϗg=ӷG>#)T>/GOqgV/Go8:5zVn -Ţ"2Vג`\m:jdzLL^OJ3"7yI+Tݬ8b+[zsbEys雴F׬H_yRNXJ3XfLWN0lo K֩JNEBIYW"m+-'Ky: nZ y#8u"@s'ԞiY8*L5YJp mjjZ)^(eM*c%vMZESW0E[n i4?0+0zP<`ivR⹧Hx2sf))k+ctY{߇PO^#pǽC |K,ixvu.n9-?igA&:`gVŵ$M^sHMHCyc]Tbm15 ~H/:$ZSe̗݄vqZ"_e(#Zii;bI+5g㸅BN(^5\yx-bZ13Yl8J}e@t |i .;-&iA,a&%zWjs%2izq+ , JG .vyxۣwQiaHbͺjq^{QSUKˎ$E5DBLբ7p^ X:ye%?iJgZL1#fMqo%E5gSÒŦjs$tyR\D0WmV+5 9Ц:&Ȫlhz[{ߟ('eYTnnΪҘ9gՠYI,fv-}€I"sG=uwK6" ȒKbv3&.+(Iz-:KN]qҕfU aI #:a>hdᵂ1KW3x{vk)㈣)˄U+ĦJ+ۯ|5!N퀬MCqdD*N3%)yL*dgPZSbDz(XhBA)Evё[X䝔~ezĹ'7ҒԦ9Ar/5? 4V,DdΐF'1_wpu aK #6aPdqw-`LDz:+̒IB/[v:urB%1b()-B;%je< ׷y~ e9 f\ls`]\MTʔ_Wo9銸 ,WrA,:Fh$Tb1jpo H1M< Sh#5ӄ(gH,P<҇r%ÌTskf?ti\ ]U+q-`l-r]C7q5V%2Eyt4i;m/9ϵ|ɐ.b x(O;<-PXx-%w ҁaYw ;ibW9O3e_6*r[āBp+QCҁ Ԧm!n>]LaăkQ(sDG: σ#uԈXCR/`|//)P.l(>Hٙ7DcYF,-FqS5$n&mux6G,#Y4$wZ S G4Q8k0]H!f!!63. + 7#ynXT~gAG^6GfV7JB1c_#8>; % 6&VJb!? 5]]/ coLǬx:k+}1cvs zLi L[IM|$AVhz+xPiv5nx>Mw@ :ƤuqШ /l^n}d20;( :9M'W&B*QvXH(b!aۣ{N G.Fb`z SJ,Ȃ>Z NNjS9a.6)oWW/V8$ESb%[n.σ63bק8'3b^dDZ-b]j1-q 'Ķ7zs:D}W*T6ZqxmII`=5*vMK[wU?9~~ Țά$ F]qh{C(f`b%JmNSHHMh bbºj~Q/F+X5zhi@$hDFc))&=!i1`q;%c{#hy.%.`{}J Ub}=XO0c:9FP>Kӊfh5U*J?-U2|'ے;%I-}^x=yC[іV$ƈM-&9̗HJ91OUR HX)DO*MKg oo_( ` o:&n_3䰉.zkzpb1' Zq KئFV bk DnDpc" ۋY4 (DPv|C˙lb;4읎l|86I~Hb.r䘄pZI"-!oy+:-Iˆuw_N"a:U1R <_u0ӻ -SB =džV[6M&b~R24O+D|%&pe#ߑە@W)ǹ7}o ~~o|Ǒ3ۏtnmZz^8M(muBZ`CbK,*2&艗g:8愕J &VWdQ0*QM*l9,+KmCW}~u猭m6wP*lF=tVQC0@ttsb6JmhzE{AzWD78F(2T.Zr/4 czjݫ}t9ت&dOyJvbbaPN!S&=$i ̇ cWgA[)tuWG?<C>#:O?v>H$m)BѰ,AaP7lt \:!o#3bJ>h8p4瞆1tSr|! ʝ%,8j`%ira(ejA/9 [y;eM%I Ӿ FŲ8Ir̘GHxO1k Ɗ%$C2i:ޥr)g8q3qT"\F[62-Ҡ1]$9)Zn'|ň nϹ޸RJEé@Nw+_m[¿}K^Oړ[N{s!q[[Ta.8lH@@ԉXdƸi[s1)d,+H i\P,ifݚƃM%`4n#mֈEbkUœp`ŢW:`Kr$&^ 55~lLMt_sGHdx^9w%}yFZZIe1fbAqOn{9LR rc2t?\Pm<0Nٽ uխB!8:ߙrk8ƞ\"ʁY0LIE$tq}(cUEG/17ZWp@^$SG=uSKna QSLoPXG}mFGf}n/m&bH`TS-s;uϛ$ľ$d\I#;nůђSbxf[2!J-ք+jx IDATv%an&fcé1]z/m+o60\Y2EܾWTB߸0PgBRs7B'Aq,Ίw@3&8Xd&>:|DZ_ky~Һ*$7Ko(3 `8Pd̃!D &:捓H "Sj:eVv[8 b.%SbY4\,p0eKE7l`V&&Mxܧb1X4"}F`"ԛ7ŃS"%9%xozK q)1*>Fg"N|oƱx0At;[).%aqWzd[ƖpsB-WQg$Ua>ZO| &,# A$$76$Q} +i]-gvt V2Xhdd+u[,:lUc pQ|"p} L֬/ iDac0yx.fQ$Q>p^C,!j%aErB+@z(XWQKr1`.݅yJ8Qr¦NS]%ńmq:1 %c8x3Sc19O&$bOJ1.Nm5 uF f>u⹜AsH8.s0NX2xY|G A( *HxB^$pFwlmE q iN:;rdjni$p2ƒ/D1VFqHD޸Ф3hiI%wt4%xo:΍κc:-pۅϠ<"mѱy+ m렻(  9fd1b!(q^%$ p1wW IJCQȳ|b{I3ӄpwuay&=By9IoP1`Sܓ ғN*F#iHJvh9Z_% Fkk]'b1ü˂!ұq`8J&EE?)B?489袳XKA>Ri%qV x+Zn6loU`-[Y)Iا7xT-Fkg08R߹NHVv&D@l`O%hfҤA'r.h&Y@"Ƶ%jsӺ́:#QtJ:\*qvnB,]t;/QCrC" E&5<.6Z )>HL4mۗ,5iHXoǵ=;]$nr;Kd\\]*{s{#֍FvgSimp0@h›hvѹXT}Q 8f7 'xˢ%( w|UGw(,sQRR @|yR"b|;yטĨ9ZJsX񦼨1!&lv5^x1[ݳñNz2%٤DmyvJF|=R 4V+Dk*z/Qa?xu"ҩ'8[J))ѕR"R7ЉQ+q&zpqVZ<9XoM t1:=^:K)qc8sV+<F٫oϥ4`F8O\%ccO*.c|ڨEJ,>\\zrL$3FQaYȞ65s n"q {kw\q6Q]pm{@ 8 4>"\2K'{HU-7 ^lFcy.FxM:8b:v3XxFȅ+BE^]4, P>|2E\eM_<԰؄8;p8`;Vg`L=A=>'ȍzW_b;kMIX!& ĞϛsU\5p\$8=sV^jt۸ MTB!s Nc3eIly2!n*+HĽa(';a܌J" N%'AtT^q5Mc8@(TXo2q70 0q.%>e{= Kspis'=[ T8(PAs>+9ͩ"]EcBĮN0f Wb^󼈳R !QTJk½촉gCzWE#ulggyʾ 1ழ]pDSuY2T*F=Th  t\x•SSR 0QbIDt\98^R hĹSvQ$)8G:84A#ppB;J$٩|U$DVsd,yDEZ'n.vg9UQ!aNfEǘb[hcF>Dx=u!1v7'иJGF9hv$0ut[aNtVȷM"1H(,[q9l5Hœ6&>'.f{3ϯE]Lmϫ!)ԄFngCc6=BL9G,x.11H& X jZcu/&n.9Ju$\Hq \oc^- AnsOwl3r⢯=k!uvA,BAG`)z v)t?(n &+Ilu6=\tеPA((M7^mG=]i:O+pEt湶BɷLՉM1I58htE~ks 8991]oOvl8r )걻hL wJR\a|Zpl|YIr1sJRM|rB$'QNޡ{/Yo{[mǑ__^߉b9VU)C$qO^rI)QA "=g˞:x:psb{Ng\rkjsl*θŽ\#ʦ"1~<+ GY{ĸ6+Y4Z7aސ}0$azrrϕ+^ìmLэcrLqq bslotᄃM&` 񤌩b.N "K](ʩ_ZF%!VPOR6x3S+Cм/V&crcMl\a؀!< #GM4&ƈs<{v{s2dƀyJWg9Ӻk1Awy|8gIC|Cc@$'Èr}8q#?#?_qW7-~HGBu,eJuj93ras G.!>tț0 9ľ9Q(ZQeq"q|UDGZy:5z} ,V7F[)Yfr]bwJs>dOQG]GS\4 ĊED2^xT8<"Xē}4)405`~a(Tk9ss`Dy;FMip'x"}ԑsI[`>FkgPCxCϿz@y~_@ߺOO^9~ϕ H Dcn)PN>ગЗVg\t&>i)PTAyQF^tv=zK ș}ypI<<K)}8rsn}cөz5]UKRD1?1y#'F#s9 hNn'\sfVc+sܟ3%4bZGsu< o_m_m7wG k_sisqNJJDŽ-EJ{T!GtѡCGMO]_ 1KjwۮƱ YrpS 0O (3rtq/Dn 줵dFF= O*/^{+h/A}i 8RqK ;[GH@8E̍jIhO#Uvl8FiOX )m0߻[x@_$gA^/{Jp0r3'`u"߈dqZTu20^xs h2t1'᠏Q呀󈾌F_%`> o<zgy fo.74m6: %̗~,D mkɈsk C&`ۊfxQs6n[ >`&V.F}" < L{^#\ Nۄyax* 1n[TBdE`^I5E8Kxć)P\%o>q? hv}Q#}g!N#W4 o`|<0>pa̶x(o<;F(UFw({Tv&tcSluq]@ g$P8KmZnN`t4pN3MR#Cۚ_=JgbR|g7<#=#vlAOJ>{ q< YLpI И 071f8xA )#$h;x.SCRmR* ٌ@apU/_u> &'w]y' F9j KƆ.㮲}qddp'1 E<^p? 4"IKpz.x*4[t*Gޠ}yv';oތ ҿyܳ<ص4(Nxo4_ E-;K!6X$($9cptt;9\q[ce1|20Q? oy=q4I=qlG޶8 {{'{p<+e瀗I;T7 E3l089]#}>bnlv[ MyE30N2.%D~a1cFF=2_*W+eEz 6o`$3~4K `?'^s7ϡ_N/'A{cS>{m.xtazQA, ^/G#=蜷I Ig_.m@dm\jbғPom(I42%bzKGjgN8]c|>1Ό{}O{F7tvl܅ǭfCC,ݖQJh JB"~Q%!9 *&qւe&oHZDb*<6 UkP|2#W"M<pGc7:s}aO(QNh΁**r2퐒 S#%D')&~}7h |$+pWdHƟ IDAT|M%H<}SD\˱Mf:uV,|y1>'Sٵ0sLhe(oa/GLt՗Sd+@vVO2rÔYpWSpY0݉A>{ 1| o呖GqrD#xGDac(Zi"J _D9/sq&6V VrsAJҤe  SPb191j8!. K [_cexR>vƝlH?fH#88Gìl"޵PEBqJa:l ( s cB9d61f}ic3}\{ x~=) g>VJgq{MB3 #FF74k̿eOǜxˁ#{ٙek 6`Dڴd KٖM'e*o5T{E*en񆖁AQI Wps.]]4,.^ځ=-swt 0x3ر0}aOA[="I8:$q63bć)m{"y>w~'6PxWN}^;K Yx\8;5ϡ ca6/lg?j峮[8!z1?6h8Hˑ=.I%7K!mgGD8hNl `Q0bۂ4A.pӐ0N%<^" )x Cam77.~G54j,{7Q\ĺȆ8$NK ESB \3ғJ.̠,[`9xtG3b0wM(#>Z|]o{#Zb~aD$q*(֜, IW|-S491ѣGp "O`({t(//"rE4"prKvG<ޙ?{33T>f87gv. 1Sx0y=v-4<Т8ID6uI((B@oHה-F,btpqtpw  U/1q/ߗ(' a9;ap{|7{|s@zl]x?лO" KļwJ#>u ?bqQ4cj2)!:ab,!FN3gJ<~;4|>D3r9;C7 2_+/RpNؾĄW?onowOGe?OSKwvh1<7 ;b_8V%9aN<'?ooۿ 'Hl4 ʦx GBb<:xCYtMpkǘ5`Y"q?IXuAGts.4Ÿq?Wj"x:96 0a\$%<[CO[رB\δ(p)!i̝ +I1M{ |t1oø(SRFHuL9G1#64\H✙x^LQ<}k/o}[|g~gɟwӉ۷|'|兿7&_W?Cor<jBb?Tco@}yq Sޏ%ɅOP?g2>7-csHT#>@9#|G+ 3 g?d9 Ĺɤ}> ݉%p}iepgb 颃v?繸Zf } P1/__}۷dYbOs:U sU'Ty\)< CLHa 4Akd&_Q{HG;y<^kLij!ll"l6.8{}T$=U1F\\ȃ00 q;Č:5 X2wgpUcnϾ@6C!ѹ(gH a_>"_:uy Q`^C7SfWHmm96rm`D{|;\0ǼˍZl 8 (oS02,@@<7q~V..\&îb k it h6ȗȗ٦DPB ՞MedRN qFaٵYd|ȉY @vsIH,{<y):~> {O/K_]7|7|c[[;~'~ӟ b|,J6' 3Eznp ]yĹcpi+xj'2; 9| )+93^6 nu䴍.GX8~93FXΎ 1G:Mߏ&???y/G?E;/~5W'S?S|sϭsy\16|ny@\Lй> +0 و <)<N ؅6N!_AAҕ4wOW@Փp>"{9ƚl8ՙw uc^E:+Hvk˪ȑgN"vTlFiB6^:#Hm-bTz!&`a*\й !uyT x>V9E ypFr@|^iI#Ԩ 4!5pǔy~c^Mffa]Dc~iyyA08oZ:!ב@YZ̵;E]@ycwac kyǀ>,|D"wb¤ S@ ue<x^-͐8qXyH&ν@ŹFv|.c:#kƯou?C?|哟$????/~~nup ,<~lb&+,s6]<a'Kt梡WanA=ûBxOw$-W@ԏ b1ґUuUmխM4XGd$EudNh#1H :iCc$MZ$hz^Q_BM)Eǘ\8TbcRxǠ1'5yo/Lg㻿{f{@ C]#hth ƹ" x3#ʣ£ MtzkT].Ay v=WD^InjuzGh"X8_P1 󣙑qX}8|b8nqL~l}'5^r?)D817Q[{rp".Ї3nBA ;mTרXGapS‰+6=i28"f,$"+BHYDll'M Soqs#1CeA=D!pa~ cw:zcc]ЭGav%r{i#1aK(,Ml!t`ƂSj`Zt*\\ŕW~xM= BjbCKI^SE}&Hi@'V sب <׎gLTHu#r5s(, trfd=B6v Ј$usa.S7#K i"T ӓ|,EPki:$z8綺04oo} HJ{.m_n17wN';;ߡ_HوpAnk|nmjAQoP7[N;:4p'%G5vWL!Cc%8%K{Țaj#6y7)*!V @&?O TNAhdb;d A{A"!mN3;2;:R2Qs+Lr:&Bqw b{)Ώ BaNȍڠJ-mTOCui˱zc>i`ژ+NIHeJp|يwLXXSL@;P.[b֌:?s =fd[=׎ A\; c"Bxf'ܨ֎,(X/b.0?-`plFbLj"DIP;a* 듋q,oy0L% /ɘgg)ęT5:p s GKf-g† IDATK"bW0΄bGB!H-HuG˝nHo~OopLp)tiE[95ój\0/HHBJb?Mk0bO 9~ZY:4")Q[qF7RNb"\TغHo6*$JK-&97#[np';-3ZOs?5\n:#:RZ=40$!MBjsrR5`~z*3Y ,;fZ.pqEq1M9vnFz̽0!5p--wC1#ҜV² %zQxA^^m#BuZqTҚTUP lqA,݄w% l__\.-_K85. ,v٪H$1+{hŽU( .sgAZLΉKaps! `=FN95Hv;Ճ`L'&s%( tX [unb LC4- :.}q(X).*.,\i(a.b߁jݧ+=Z|'J/ηt4zOGpNgGBiL&./1lZQ:c,Dh -=^gg|๛QAJw2`N|e"o >vxw$,N½wu,Ld:]˨S_P "}JB:|ɹ]" MgqqoC"zQo{=>0kh}h$` YW%י š:d4 ‚B.z".cF);Y9wjB:S6n+Zz'NKv\qԓ LU_Na5 }A҅9M+I7tHVrs2{q} B`A[:E-޶\?t0V|qGa~,c (/0/v' <(Rjys.=iXse`"7Ua^"JN!PKmKM"⨼kM3Q6¸t߾+:y3YՎ %4|]xs=F ~O̡cF?OKK#֋VZZbvgPYE}6 ;B`ww"NDPm{g8 h1=9`m#i1V.SE|:a\)@A#&/wQ}X:&l.sǽ`́ʔč9g1{\uC[rOŹ``O8X2Q0φ*Ԏy,Qi!>0 FlH&& a89ἁ)@Dټsg> A{ut䈻 j  kDHH[ZӅdX<eL83 ??A|i{I@o$-V՟9q`B!X{Iڈ۾N=igᦅWwjaFfQ9qun -gߦA(UHw1:!VXCC{ۢT&s I'~Xyk82Z po=qF/r7osF I99W^#mn^0pq67S-\ߊx~[[sv0?RBD\(4K$v^ u 2R:cj䤽ssי6 !sԲ(nVΚ\^ w!)o' s#p {KAT#*';qQ>!|2Gkx{.<'ZǜTiHR!0KUYOp9\GK㓟$! _+E+#s'ɜ@DEz%czzˆyymD|&C|zC-^JK͙g2gTZvOo8bDV'o!'!ǎUz@cE "J""~zI^C뢇@N݋K],qǢUH8*'8 wҾ!0("dJ{7s"Уn{Q8':]}!GK'VE("Ww#u=c`\*$T#Ӏy&mԞg7 '07"8#I<-3q-#%5kd\}J0[x^|0YA,x]:JOX]`/8OTj[qf vb&ThC蠿;K uuK!jni[ TEmUZ[8/G7HG̎ Z+Zw͕" i;+`wh#~ԉ<eu8nB.mΈ.9p]@&TS{vgW /IxɎLjNlRE<}+''!<H,"y2Srj )XyunXvz\DRnH#/0y6NQ8XtݛIUoB~1T+]Bx#tB89k< %TīDʵ۾E]<6!> EC ?$`knʼnlڅ:#n &IĹ9 IyE,w*\ $d=Tҡt0ʠQ:4wrяe'fnA!TFB H3 2q֤ 2vFڠR[q,NΟwoq9hn:ӊrNjTrC]B{<G\0F?) Ny嘗N(tjv̹||p8 5cAh NyaL/W~W10x},,8#$ޜZHV3F'> w wWٶVظ0#wGד$U'vggW-kȩQHehUפ.Y;i'oΝ h} e}q(gcq$T\3_۹~ (ۤIoyhnj.b+- :%G|i)w)Y\{G9qɻQAtN"^83` 89rƉ%.EhU" йP 7HD6a~ޫcd&> 7AK/0W/1zcbxөfJǍ;g9#̓$9qyhW7.YAta.Ezx.7/kוW__O|Kq.Z>*%'K(57\htKd[!g)}YNlSN+!{g1 g7Nx4IbgKD$[HEIT9vD^_Mo[kpv)У : t_Ө(j#gH,[/z uUȳ œ^0f'd,dp$hCEVpD br̟#ьTě%4<{4JF;f'f.Ht@)׎u/ԢȮ1')rJu߆yxYx- 򷰈C\݈R#9l c.Vk"ruHctqUM\.]$u. nzgF$ǝ{]y|糟,__]gЗ΢rv@EHFN(K9N q~VY`Mecr]SDvdݚ@j7Q~q;BvPυ؈_oA*5j+9Ltjo5zBl# |*qn3PCQtsTZ(mzDA}8< 7zJl @/'$X~Pls:5PMw4EV&,~c8ORXx. ܡ7"N~*va2!:œEJ=wv$ϓ+9\裦Q8NtOt|M{]y};y||sϧ,=me[Hr*(͝Zrj < ӣ 6vJ zѸ*mȲc9:3Uھ(Xa-:" hD"҉z\jUMlPV-"B"݉ egʋi;LǾa^#" 5h'& UF؂Z"~%Ԍ;99X}y{!Ή1_q,4Y"}`Bgcwmiĺ/P.fm:?.%e42|8? " ^`X J#{ -M2yye8FwB@MviyĹ q̏"kQiiCx6CZ1/⿾{`>*W*u~ OOOoşM_0w)qu(tDe 9 &.8A*qouPQ 3!o :jʫ&8b' H#vbLBv bM.Εx- gd'o%'qӓ8-oM(..]';7ؙᔯWj'{2v.,! F ++Z{ $Ej7nEѝоTP |wr0FC_$B#Ij$ 9r^ Ǝ:j\]gxr66>agshܧg%Jt, 2,{"]oiuk-Ō\:y7U }a,Cw,@mo!r=}aHb"N}n3Y󂹍%@hϾc,"T|O|t ˟P]y}`HU۸.284e,霰,] Y!nrTIʱ\r&7W!QEG'$>%G$NjPssU9`ھ 0h"]ܜo0Ċ#YVıpg.l:L=[Cb<+}s< Oԑi5["K bX5JEV \'/+Cq\ؠ#M̻=8J&.НrI1ڢD™*R5:M FF$ṿc~;;޹t.Bq/#Eo`,W'] !Zcܸ8a<}]x%>;C>Q98/c~Q0yJԓw+H))"mX LH[wζ9$=֭&TŦ W򈣟p7׷|d B!rE8Tlr;%\B;]fXDb j9C"Уr' v:k6i~#/#$Ѥ"&ğ(SG!.f3\E'PItt.70hs. g\pl裥,݊* WP\Wk1ߠ1 OgǕ0skpσPFy=X;ֵ ρhf}El m$XtLfAǕ+V6Js(JZ8/=݊9f] [آs 2/i1GF928邷a4`; 椓uL@ožٳ ̿q~E,T=q.pjz0qfm\9gzV1l >#.K58"zB 4AKdOw? :R-u/FlIl\0\8&5&l!%+94sR Մ""WvJغ0ob#Ծ k`9 S=13g[,HXa5[S9{A^{/x!t,ΉIIDSۺ@ILdpYf$pL"'g-a.1eQTf1" yJ]/n> *l>9 1)ԧgՃt\:s}e*m>7ׁ4Hj{źy [ȝM*?Nt-_8އWN\rNNMzq^tk"[$_8#-,sѽ X(2ºX6L+QG' :r-;ۊ@aN{S@s9%sdLLcq<#-R1#2#ʌ&YE<s~YjpIDu'&Tj]KAU3T7ל ?!qeм6[6jeѳ8fr/Z IDAT] X%H:Gfy6 u/fgb⢺tѭú`nף'8$ęLLbb&/a ltX…yܵ@ f33ĕn!o=ot"g =旜S"WH=hO^:VNd\&YMf4dϵu\Ho{G!Dvh۠0-\ [<<vXq0#7ב&&DvlỈ#3W7s_D9Iv'eo.o!Qɜs vBةa-3Q<3ZRfzv'4?8w.>n#adGSIHh0WW U c õ;PFߏ1 ';j7D]Ŭ=AvD|baJ@JaGf$:5mKr~<c:rTN\f:HkW\gy~ C=lKx^"GcnM$yEq$yy'i1C6%k; ߏWqoa899Sw \: 'n{ȏ! l$$m !gDA.:rJj &mQ~G"s8iOsbqH**5}2Iv$43(^|]q"at-$&]eN`Nsq{+ bR3=XȀP gp+T7Ĺi-ЮߙI7\ᬨc.#(}Q }Bo 4zaL\$*´+#{;|F{lfHsC߂EuKqPn苡N:՜1kaz?,x_ee{,G4H.zeٚ0C~%u\xNasy@DobH}vq똻H쉲X/͈ B F5mJ%s2m0,IڅvT}4&w־2ŁcBpȠ*[ŜȶUZEv mE 7 ]bv[&F5YN=aRBk[@[Q}QT2sA {%ZRB?¸ S1@vH,?絺!q?b"{zXOI4=c_)՞%;aKg1SUT7[rcO@L؋A|  W%b*$$l\X;8рxq#-~ʿtmrWdN^{ܛ8[X8 gС$r D":!ntNZH; h~0q ဦF,~ȐM3FZ` PDgΐ<0meUmZSNz6nBrɉH+o#')a6\@Q 3jXEmz9YgdS xn%t"y,YM 4|ĖV5xaW͉N@gS{̋vy![V[5/1HzCG=1LY.rhGB1c>3eF8E^]NG_&ejLɌve?ܧ^\xT8dtuqΖ\h:;TNrS<#4#/uu}C>IQ8%NOr0 W 栣Š)ʌՄc ŵ0`)ڻ'+ #ȯAatB*JL(bʙ^aUΊVؔC^r1A+ V|v$h 3lZD ,:4Hjā(GB9. nn#hоkH4ns̢Ӊu+3=YDf|Q3Z`H(ˡEV>JE/Y"v -=RDZuzM,,/#,s 1<˔l0aZ&saj-|T[3ϐ 4*E`bFl pT0?FFTdDO9h\Ḟy@ (;"A(δ̽Hi|xW<J i<:L+i Dzt<9ybϑfU4C9qOB HoM8P!6ǐ='1h1_>|B1^Evb_eBa>/'y<?_F?gLq+0#'5ujq >%8.w8! LM@uTX\*" PAB)1䘘o`ɇXR(eY~=2, ^q"+D Y2s~T!utҡtA'%]mc4hy|`H>.v-Qx0z@"yw{EG/Ev7t<1v %S ~)Zy;;TUşٟ=. Wmd&!;C:f8(9p1{ppadI@&$Xq~ͶuL>ˤ Rie{Ɍ!)"/M~   OqMaf.zeh +rP(yLB! bFRߕ%Y듀ZC_<J2FG#ĄN+huKIT2!>zaqqc*=Zx.cLsy=q) m1[W6TC2c k[퇢`&O<Zvs [$8ϑz ՊqD % ӡa LZO$?'Ͽ@Woo77DUqAz= JM6'~v^\1ĸ(KDiĦ&l`\jd5ڊBF=L3qfaacKP6ں{ w!MZqyT"EF3A;[4n!]4 %0 +T#ѯJd+= ЪF&9Q4c{-1`.^iC$Qfi( K7:WTii7`LҊ557a0wC!s9N1cHyŋUCLn1c"D*&*L"lv/ XmUDkvy`/7+^11/D^ 4Dl +7*X 􌄰oP]@vipg$bTZUYh]3K!M<<7-<ڭŻL0m4!yŽCP B-#~VS of\d$D?b~-S2 mQmd7P8Y+~p}~ 1H._B!ԃ F"dFhYF& KS66Z`ubs#ׅP9Q1El2:E DLsM"q}q5d<5*4Ff"DO ɷ4$’sJWhc"cFBYc,݀Gl@600K.ʓF+SVŢE>yCfFfҙF0?scv۬)WRs<ڕκbS s23!ƨ.!j i*@P1;.E0*=#3B n*#J&joELA֘(lX0V)+)`)V03G7!xޔ SԜAnbkN5y2pH -`.уc([0(HOԞ%|<4h}~UY+oo|.KX_{MBsV+Ąi9L|o XBBmH!3yJ+S,pDY&{/q-q3ٸ137 RS"hS $;mS8e e~6 9ЊyS!l 7`kdӹo؝HFPIhmH.Ds! xM{,LGr3 5>m-gs$O]XkpMsq^l hjF3b2'n͌MHRc|AfCq8::oluK@1z0#dBkyLWVK\)KUQ02!QYd0n6H0Lv@+*H#8ρMrt )B!E =0̘ u{#`j[dT=b16߯"䓻`Dj#:* Pu70,VypED2)~o"0LMbfT0剱85r( Kp_:yVI`V_ ?_,¡QC 8 sd ]kCHs1'#2 К̶9_Ŵ+ ~_JNBNPc^076G#Ѧά4qia(VpgGbFEfF(7{wk8(" 9'@<&sM0ka[uA8*G!pfbC>>qV10&}4E+bQaV" L ({)y̅.$pnʹ*1b޳扐{̯94II+sȹ4|(-$RCYt L6V*²` º`~#PS) k1f$T {.[-@YO0XV'pgfda/s]|IŊ xsP0?xx6f0Lm1壑<&4WXPBsә|Zx>r}GaTÔyEZ7Ȱ-c ȴ Z7*d_Kdlq[l}u0@-"urqFc) ˜|i5+*,^QؒmM;gmOǜy_\nn0^Yy Y31pG2)ȉ2g7f,pǮ) q:V9Il5R}(DBB!UR W;U`^cD4+[$B-ƶrdc[e\Zډąլbe 6x}DSvkrڞ0<I7p]k0l I }+>( <ad-A.M~ &B+ B.a$hr:P$*ҋb:"P,{i:'\4Z* "$yY3bn [Wl(F105ӘW +:M19z~A[|<^&A$#7VZ|sHXK\ɔHJ(_{&\τHKhOhYp]Ѯ}pj1;xnweͻJz%+r/_ȧ0  fI[Ð`=N%HQHB,BF R&1.!5A^4ZnrU>7b^KFZ"Pk5B"&l$Z0z.RE:%v99=?~y1 ۵q:ۋEe{'%ߎҢ;Pnм3rKf8A85#i GuuPt  SZ6xDg#Q*K~^6Q[H֟c:W|n+6LsQڨt|Kcx~bd1L`yr)]ּ <+Vˉe-缀8'ls%8J˂y+'FG/65sWX1MRVTh/zWo?IsHa ˦1?^9zHt?%`oUk^ۙ|QcvժX6R6FcO7XŒ*atU#:W48qqpBwC# uX|db ̸$si4t6G@Q7 ff̶d[9Cl ОY; Լ˚Gx|*~BHnM>6l>LFb IDATKj"ͺr _;R׶ X,b$ON-AM=x؈.w€c>RXT9Sc5ecoǺm\]k\r0| >#?YW[,*[][xpv@Xo4J"1X:Ͻ7ݼr}/\b6! AkԵbt< ~AZ{'T]A$-$dF0ȁ3? W*͈t,9`u̥ABLT|]sq7ɽc^ *N ނJq==/b9qK{BeHy@` l:NRѡ"JXaie=Komȸ$Hl`:(ola)]}ϯ6 cs/Fƾ!qiBRіT eP<Ü0s`M5޷tM k*-4 1/cm;h% QBn !PD "ʰ2d@d\Zy E c!aA!`՗0a) ii!&KUïeJ{XJHNPx`E$a.P,8'EC1[Ɗ`*rsUf$ J1*W"1 1|@g[C0Th0ײFSJɎ-~0n{V̐s< Ԃ1V\E}#<49=}ǯ'WcGOfsb;bPS0'a6CsC}dNq,TE}o.SelB[rB X h)Ƅ 59U.`!\u *_wG^y܏*ymI;CS1xHXeL{ߴ>lמ wZ, HHV&-HMm 4L2-N1eΔ 楝|)NjNC^w )Y#ec$Kv aY Vi<Сd&TT=4`׎yP07Ȉ{H*G>ZulއǯD3½Z$%!fH@'44Lg)s&,)挛CXqNsntH7t s0hx>e+ @T|$aVl"y^%ĥc e<@mY{`1`]*ycnxѯ?{ ;d([[s@IAEOʫaAżɄ0T.`sBpH|WxU)7_<1/ -(U!1vV 2ʩV@ aai _F6 ְZ \.aC2r$Dx}xνO٭Ɔ6%09d΂ >t`kG&2e ҡlQ= 43OaH^W|ꣶY\,1͡%1T2Z,/̧-!Wmʨ|fj%LW7ư~vX`9b} Mg X DR'`ۧ4:R.Uc&sej529`΂ef"L$ 撈DZۊ[HC*ed.vRŃ-T 1ρ= HmHWsZ"Ek5/׭RlL&*~Do4q x[ ф)5|2As/mfxlZzN&h*ex0Z<G_R>h F JZRS|:wkLMKԂ"'],\˘x#=!@Cd“cܴ5G_eu3#s30v9S֤+p⽶2)k$)yp% t<wyhّw@qS! BZ~lNkjl#Y9n踨cMt9)7!I͊2?~̓; [&";KD*צ5"S滰`X.Ӂƶ3B=%T52(yLv l󡎹^8x^\4uZ0ݚc <0Y7էʿ_S4昧g57%M`qkO̍)tz/nc$=p^JB%"X<C9B-1KUʣݟ6^K{PLb:M^a/LF(9k hҰP:4 )"ŝ`XYF̆K|RhW7\mo0W[^ZW8do-dg6. S&gBT) \ex(S+I#ȴ%lhhɣJSӬuLs|Z,F;T(;YVl M%RbEcLR$ V[`$2[.W.g4˷׬w䣁[pcN>9Y.xt2̈[# 'zK8F}+e]sn᳘fH Nv2b.`A{\ZrڂyknhE^Sv9r d6vF>f)rV}8{kM׼L5|gq:̜.;<4bL~a(sRHsݪ@.9D"K A k$ A2P ~ ձ푽`(-V)?(pXfGViA6l2-b=b 5òsrMkf"ٔLf`ˆgx@% .>o>ĿXNwjf{eg9DE }F{KєQ"ҀL:d)bF{/& =';:q?]"3F(-ţx4t%tɷ̶K6Zp׶0){p&ӳe#Y|ʻ$ 1p~RioW9ذns2!3qτ]az a6#!ɱQ1/VdT&7 C&)v6v(W TElon(K.x [L6dq/_㹚*\35xˆ|.>Oũwwk<<_=;f  s߅)Ă^ǼHwר+F\Iqg0\DY_\zGZT50WeÊf-蚬+l1v K bctdC9P{/^"~Iz=N**6gb҈ x iW=L1`f dLa& FFu(`kG.? hl\5 ̉bbEZBlU0%KR n<'»7VmgH zg?8)ɷ1.ӂ2d m @ Wb92⊨p(qjjé1sFwc;;v sߔ@NBa-ZHkLWX\;k VXJŘ_a1-&"gT</-cYb n?bc -1.!݅;mk~Mh~v O^>#ی`@@`qoblba>t/O@F){-M0+^{P~=/PģI+<ͬv6%|3xan M%ȅ\?X#t_3ݗ8'<j&sѼU@s${fO q^x~nA:;1O'-ռp![R[E':jٖO 练> \nTwL&Og|y/dܣ8Oj0:\>|卾#B\_-xoZ@8: ,zl XK;@'W!tO=b'~B'͒YL [M 61"UpO}R$ uPe9y옗Q+ױ~Y$??k|[~QgۈmjeZK,0TQGe`C˒5W̹fЙ7-踽hyurc4-X]=ƌ̔8;[Y87<c8:A@d„j;—CMn*[XUEUGܯ#'2Nؘng;mQXP,EFw!JTtvTCW\ق+j6&֒8E+MK-kvT07d&ą D[Gpt JY)=FD1_Ax WYy\q=qx$9^*K̳Lk9qw̅d1'e:Kl9\:M#sB:^ܴ>hi&-b]t1 qcA˯ؔ󜹯Զ 3r1=BW@鯲 _Yujq{1cav18v3L!EKkF5Ds|.7_%w?H₿wwk1IFۉ34l!)2wY3.K鬣avKo-[{!Mf:#CP@8`UYCuVbt-naM 3=uK+F}ґ&N7xFzO6c?ݍs{̽4bny92Xs.s{uW7|Kf-ɀ=nQ"n*J\ m>S^ ɷV|c*# W>ɓ\ Xq2h#)i / ] v;3[}USx~&qOz{ΫW6|kVxè|^xYDܠň)kcC#v_`rAct _3(Zx>-~}|IL?%kZ4;ϙ?bsfyE7 &C$P}Ӟ,.Awv/[5wako6c2lO&l^ '\Oș'3MeJܳCn0b| *w|}׽G{Zlyol#`6KlhH!ߐi$t%@BHRRBH #`f ۀmY%Kgs68~AǗ޺}_Y@pMy߲3_Oi+@ X)T0p2ThL3ejE +vgwQ1m1o "<2E ( "69"@e“DLbPbg-X&LO0 LWK2&zMPŔ8u:_Ƚda.@T-}-:5wa6HWdIzEbS/x"A.fiA<^A|0jj3D}FџB`edLW_ JN, r8ⅷ{%b( D 31M \ W,r, 0kq"sdB߼M.mQ>2'kCa!g~steO$1 %ɬtZÜran =F|MadfaS|w憅pfDp18D 4Z<0֊JQ@zҩ1> :B:9W$8rޔ%U"+7"a E"*b:+W|'zb5qBe*,뎱\iH#dՅ5t,c2n5L1zHy?{\NN2=Ч!iilCSheo'NQ<+ز~<@ىc0SE,Sæ1*F((V ssz^f Mu E#B7CÇ X?X᝙g֠f()yxM]XTS5;sa!(#xʇJ X*¼6"ʩ3[*qYDQHGB]%"FT ZUD;0@yo =ȸ *),4]zSALJKxa,jᯏ-#*%  $cIR{߁[k.廳1b"(c@ݙ$ay4UIX]ȹ2 GZ6N%5 3'}:qrrGw%W?w7+89LY\U0PhJA&&r$"A32`N>ƻObQEm9ʇL.mu||;:d'v;ı* wj)#ԋ1c^|E(Viשygj.Ch|AdxH8  M`B&G9Z_B_(과m" Q6x?~u\HٌL l,G\ӲS&[D+Zp95=J@u %q-s(DA&:ޑjb g*hbEfJbvi2oA-wE `h / %^ dz޽5|CI$-7 MG6GePc%{:+_&#g!9U:K-:;o# x&;DJ&G21" %F}LAPi7uxFsha^&˒}ʵHLC=t"mm'Ȋ -c ! ;OPL ᝮ.djG)[0gjbG+hsVJje[G@tum Ck^M^AhOޒ)dF'u(]qe'I AI) Ir>&XdZxt&JbHD_} О,lPq(9@;&1>&=0 qxesA|g޵m d!D)c$cH/ Nm=¦1:\? "L`X=͍@ CxS8]KyN>f(9?Xwu8$ˍЌ)a89 W0pz.t,HΌ0/KDG0˛ ^*"."&Ş> -RdLU_]3ײ[MB2h)ʤc03xekO4`evVQИqqNPv #Rԟ؍jAf"x$qc)ga$S xuW\` H!N5VD,HS"jHh)?TSg޾UЧV~@GFij]ذlGg+3zCG}|R^5H~ً _ \wRia)"!)u,aN #E|[60;CgCC 5B ܊%a(r7qFi865R!e8#m1";L*`,QLА 8k:}G7o犡kJVK3FEN1*"'BJızX#K.GmB‡jd)`}iL݁&NAuAʒY@/zܚNr6vOLk,Lx@5YbQG܌:M;~c~+ez`+S /d:G8D=Ж%`` F"tq1ZwUqߖ‹b!qōz(ȳ\[\LtjXָ i$ɧs>mK,* PuRM504SHVAR$Oe6NPyēOCPQ~=aV7mC |Q|Wq /?)fgɽ(3ޱFF-ju:(KxTx 3c(XIv46&ev=_2[)PݤScLPS#-1) #]Z򲰒gnovpхZ9,$ED =%K sM*+L 0^E>%/AM,M5H "Kܿ+S=a_;6fjEB*!MТg9#_J@3Ϙ|s͉c9+ |S}Oy!O3-0s b^"3nO9e|I"pQS#.mC~kx^V?Hv 'p,dL4ȩ ʩ1ߙiar̚9^Rș{}$cmtX 8YI R}_'r) qhDq*K4g枕 f#pu?ў#UQ"n` f.OfGg?Z2+Iy".BJ!߃zfU!_n) &?K xv/fzl0o7k$v}ijft A{K~릅n%)cf'02#>ފNcƉ5{ +ZE2i1?ZPj"1bZG0"yv7{e#baA!ҩN%R"a` bqʳY@+:ο1_(9ɵO1r~pe{)xt8IBrJu Mp=bm5u%Dqduh;5xvn{p?}*;^FQ.O!WC~+,HDFscܕMM!5m*O;ë.NkK#{#~e4-KlDY)V?GXZ{p qaD #r dӃrzo t?:P,ԓ[)rGu^#hfOγ:.c<_ I~-E4\L]YQ~ Bq+.$F6? jn~.RT@ H&5='ҟf Ļ vr~7?{I7G-%\[D4TۣJ^lvi_7u{+6,pL[/1u5,,.$- y҆ m-v`5ȥޯ t \!EP~5Df2qF0`iIDATgy,8A蹇Xy!3G zV\XB0%/ΔI2uTq]^Bŋa\0 .zi8> gG?AG.b$Ɨi"JYq#l:/L};w/uVLofǎ<#\yg}OSnZt<d gJ+OPKS\g֍,i0蜾PYXM[@8剑BtHiT6Px$jS2UWg+IPGF^b7r%d:vHS05MgRL~3lXdo(#RݔլGȂ I;,ҭ{)jPyY5K7*2+C %摺Z8ƽ}5ʸYdVȢu@B2󧟿1]234'2 ?)鼛/i.Ʃ,|멿aFN ,@"0؇~kpo*@r#)$NnXg>zX8 '%,2p6xQיcEI8_iمlr]̎ #sk/5)6.eIVJ"}5  WVݤp}CGҢ!&-YnѤq3ôd(Ee;+2߲XXQs/U߲39ؾ,Zq?2ښj u UE1=CϠ.kDD$ez%[.u_~a|/NRxx}>v>'H>Q7o#S,j D3 +q.YCRETAbD180{_*nakԄI;t>ϕngpFlc[NuTf(]# (sgn=2[Αst˫ロ)z{{سg ,{ٳC-lCYr?-|3Y@톥:Kmx&W/t ~{% "#/ec-w?DIFv5l_%> JVLwg)ɯo20_Ὃ##YY~%Cײl6gG'#_c2@O}Ҋs߿ Q}}1M[W0v;+,a;y~`HQ0QvŇUB@ -ee` . :z2$O^ȷ 4|z1z݆uMNݰI~/üYr+;T\W r"H'!oD_l/O+3XL6<!`EX28y)hŔzi-KG}ՏL'_+X!k e/o~͛7~zoi&6lދ(xw}ɇ>3WSS7 =yM]@e22%5EdL҈lZRݑ)^7kY=Qri$AyUN6K.# :SCELBO'Tb:) Aϼkii$DC@t+$$!`Sd'2ʙ/rd.Y&[O#(CYr r2x+"a`Z:bV%Ibu!915 US[ q[gh S7S9,$j8R DsCȠ)f #`zRG%r(4D|{S[\BT4LnVDD m%PU 3c"l.5q aZ  +ɡ^T g0g9(1 Yv!e+qs=Z~1-+ 'sdA4DD.Ys,,AJdM7ti(YQ0DYԼɐSɊgI8dID{ϰsNCaaY?---p]wQPPpFib?-l2֮]$Iypyd?? 45MO}SȲas[/M6Q__g?s<ҳկ~G/P\O?/~ t]^ϛ-wy'ׯiCnVs0 w}7W\qt]'v.x 'SRRrGL8>Cjs[l?s=ܙϵϹ'OӟYG>73VYYI__߻37fc߰aTUU3ϜݻO?͖-[ޝ~6o ^ϊ <#:u~+o5J^z%LK.as<|[9_j~=pUWV9ر-_'hazzz΄fc>[X,> j짉'˝Ϝ5}rͿiY_Zϛ{з~;?Yn_,xo||;ߡ)߹G}}K_"333~:;;y>n7xzNz>?}n^ àƹ"Lr뭷O}v-[v^>k~["IMMMsM@l6kΛ6;m6mvzذar _=Dw~o)Lf%W]uwq8p[n/~_5wqǙ+_ Iii)T.6[v޲,7|g޽yD"<Ǐ'JEEpol6O~N'\pv:߆:hK /qM7M7tg46l۶ A<mwWжo?yn6nVTÇsws 򕯐dhkkc{mAv0絯~tMTVVl}m{Oۺu+oW\amNm6mm6mf(;m6mf(;m6mf(;m6mf(;m6mf(;m6mf(;m6mf(;m6mf(;m6mf(;m6mf(;m6mf(;m6mf(;m6mf(;m6mf(;m6mf(;m6mf(;m6mf(;m6mf(;m6mf(;m6mf(;m6mf(;m6mf(;m6mf(;m6mnZIENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/lambertw_c.png0000644000175000017500000007001312253362407023525 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATx{oUϻ^ћ['UT\iAA%AQT#! ҉H hMwaр B#kRu/QL%9u{Zs1cާT9^s>ǜk^nݺuvW&8Hnݺu6֭[nu@w֭]jݺuvZtnݺݥݭ[nwu@w֭]jݺuvZtnݺݥݭ[nwu@w֭]jݺuvZtnݺݥݭ[nwu@w֭]jݺuvZtnݺݥݭ[nwu@w֭]jݺuvZtnݺݥݭ[nwu@w֭]j/v^L{g[[׾N8}{O~EenΪi"^:|#<Eanβi@{3??voիW񶷽 }{qEܾ}[?^"~m&h-,{` T[xDv:?7Sa-Ka/] NA Aj-#_ыPV8s%Too~},*xyփʫY_F3/_{ԧ>E-^ 5ud #2*) uA{Zk@[y2 LJT-`:`uq@o1G6_G.d- 0/C%?ڍu@}1i;!'ێzc)(k"K(t*)P1O{YYw[}OFK*l@lY>Yl="f]?^ڦ-l^=yq ޶@ EeT~>w!ZJceN;cUVmmox<zR8/^7uei9e(m 0u=P|Ӡft^ΰu@o8[_?/=&|A'4UPonWCh'SMWT7 @j#LR/Yr7SE{TXCx|:M]﯆e.;*yKkPokv˪tv}|t=}>ckR"QX0`oӀ"_Sr0{Ūsu:W\%;kqgPW0'N떭z?O}a*׫ݔN>uAd S3G?iSx ƞ:]6-nd|{6P/=b?|N{ݲu@xy;RLK@o>؎V6yږ`6[,yq굏[woG]9h)ctyXe%`[q'TA9M ;/OYI/y{M[G7} xe\EPE9 (_Z׭zmaȉvjYڵyun3K݄~XTRi V?W`}}e @&{AtP! m݂sQހ}"-)0ѿO,bA;n+K+nkajVskN/\fv2T0M (m4C3oM@^S!K/ޫ,?kR(ɶg6d=ZvM NKzHW]Z[g>ݗ9U9ځo܎NT2iDY`^9\  AJ{ *(~io?WsMv^{W 2:xyqϽ>:pli=Z;$0%(7(urN KE #?&`V906T[%^iy-itQf#,z`aN7Ѥ*gxLW邻 1- t<8~t׭:'s@wC1)gmks|:mdd\I֏4vk&HTYؚ㥚& j_Ң_zᡁ=2j,hx}֜I@,<2:iryT@8s#^G:WZE(%ϮPTa^TSA,{qG7wq)y^w4X!\;0pM 0:𭖁ӎJ/ kg ӾwH'`޽y eL9LrnXAT1YҊ5`XSK6I*ʘ?jO)6bH3ha20@i z7 v@okTyBb55]Ŵ<ڀrC$Uiͱ +l Rq9ޮƦc!i)f6a:WZZwk}Z*ogMxb%NsDb^R.5H>'oO[ fݟk֦-2 @➂`aV<fd`zRE[`,zPUm&lUfYրkoj׷ iZ*tKkhO5prih, vb[bèױ186֝:Wp [f -9y7W|p5L1g2$A| o& l:bjrkX~/:^50mQ f>ΛY( ^&P+ upJځt}쬼Zn8oܸwݸWO>$~~ oy[׿*mi@\[ڬ|@9W,"\*g惂 pV=f+@\kzUAgo*N>Yy^5sƭEdw=o ׯ_ǫ^}?ƴ|iA36eq e?/SU $ ^#>R/*tb '\]f;V=RT5 8`m{>jc-ۙV< ַz!ך3!c* xiYÞ#;I{J.;+"T̩Tˏ-ϫ&vKv}U?Rڭkӛ~s .*l:lŽ4ϴ#AZAP>drm$rq~oZ j$TeEs74KRYntmfXeWEYӛgo:׏ĵcNhgz׻W_qU\rg?xR|. q=@Oi[µ4Sj=Cl9;WbguH??S &vuzR 7iBy+x G_mtLg?k-o15+r Ext>c\ U.OWko@])l[`&gzɇ+VW Vkoy0qQށrvai.TY]i^w k n4Ob><-pg uB2 ~j/%d-JZE3'̄s7yV̓LX8X۾^bk_6+UwQH ;T8r萮YJ5Q[o)$ cA9zf7kaX~2!zFG[z,-z,aũ-PFfՅ&6S:?cm p͞pT:l[:P Pj ™u"&>PNR}i[EJbyUۖ*R܋R8Q~CZjMX[Ұ)h\/yq+ͺad&Lgjv3ANvԔQ,d9a?hX*X5ѽkWVN\Y@g~l"θu@zU׮_ޚp-mʠؔ0˽זz*4e!w*MM c}6dX 1ݭ7h%]F= ([Y^Wѥu@o6gp4^j!x9bf nPi9OXHD,,e5Z18_+RV(Fi?w ҈G.ks'ּt@6OY.g-!^F{6SÿtzmS<ז:wDs(&ku|g;/Ä04ªy ]\-Fyl790C/(4/:`wI5!b𤩍]D8.b#ş1_c8J=k'VӕUH;'bT(4\T̔W!O&zepN@׶T˕W0Z39F9_KJn3U g2:"c_DcmLQ$g V|d04ޙ؁tj Ўe՚֭z a(:puTs_dx}BCK͇9B31+3 Os];S@29(q 1OV`JXrs LM^\WW.V U0a[4{&ȴ 5ݮV>t=(]Zd s`ƴاJAƁ?O[7 sӃw Ϡ7\/M(ދ( |V?P-[eCZ?k+fF(Qy5X[`x{ 1-B34$dp&LkmY#|)ۮ,@S|ъ9U+h=Phh\i.r +y| GL뚠f^m%/z~XJ\!k6Hvt3J": _q|hઘù-SV$˙.lj|x|$ԨN**sp赦!*nTͮ⫸:euZMbaS !]ɵ*sjsø;+T5; VY, 7ua4`9:u@o0ڟ'kR5hTp ̭~܊V *Lqn-:Ir[4)ϕt P4i)Z89\|dVGNEVϸu@o|M;|aWѣ[f{0NYPcPmr00(Å4ٵGr5_A"{65Wqp6'zP$J^Nq>/Ȗ9݄(L{ ߴ`+z8KVB4CYwK{%2\M5[VS9%LAz&F^x%մ iƎ?άuja *}S\klː+j+fC=GH{`XcmBLn=SX[{==nq\*g7XiVSQ;`.NΕVTʺ p^n:7є&됶ѣ*C[J:7AG Ho uo (N`l}B9XyB[蕖oF i50W# 4=:DUܹӔ8L%>TȀsl+™9_9#;C똖ֆSj%E1;SȁvL(7;lkθu@o4^kcJ9t,0KE0S&Si2DмG3m#̍=0  U)IP#8$kD˅uPpvŬM vz6%>Le,gn4h9Zk `07]&ZKJ Zbzf6@'ك0 TE_F1@!&p&k2RžiА&ֳCruD}oF;eS%E},pt^kAtweZ!]݅2Vd>"=+VJBzn4_&L=iR{dWnrttsYMҏ-zFzNsz֠j:A!iםXEn\@յVn:O4aՓe\셑<VTȳM qaӬ % c%"W 2?\rµ 8НYrgX X\u]ZX[ ex7a̻o(HJ{?dyE8 %"܉ :̆jWD!3ɈOc k`QN4䭠{ ̾e>Tsz6RVMW51퀹١g@yX2+tQ[*uK-Hÿ1AK+#Ke4=?Heڞ1 FB\ pyܫ 1>t 8pHi6.A:t}׌ϋ=@ǔqT^#6! +P( b HcTY3j'2{i BwwhH2_QQ'90Yh ŲDžsBEGG0:0`c1FŌ @K/5Rss0o289-qdxitRД@,UOQC[xu%̈́Vqчq^iӨ+ˁC BLIG< )c:9v!=wjG9#L81Q7[_4?t`utoMXr[nKے~lm!T63H{~z{ѭIwFYEg.Y(l HOjvh(9E\As7Էh?:BzG1"x9aA<8w5gKR#?Ù sm@P]$%5x:Ճ 79\qW9MfSS4 $.aȕt|O01Cr}D-^6G)>h>%z$qp#)͟>Ego!@G@8 cCx۰ܡh( GMW֮k%o-kuΘu@o-l5urІtri@aN#?%A܅.67:i[:\ ΔF ҳ×0I㉠4dP4!03v# ̇fstmpù Щ[r\q0OPah^pm<K+Ȱ7ú_VK&^i1EeS>of|z$|ܯr``jϘ!D$AtX ҄- <?0-äٳA989Z]gaA]  eY -`0O>O$>ŋw~VK !a]}e`X=KOMD'jTP&pŻˀ.fHZ0Gs?tđjjIH#v#HH#);tqHx {JOBQKKqc8_?-)cͪu{{y&.]x_}Cw|5{jZqt< $q0IĄs^ܤf vqꙿR9y&aRqvwh8qs#p& 07t  ߜpοMm/ÔXjUVW}BɆ'G}_җۿo}ͪٺWyNA8:Ռa#Î-"Cp.E \B..piZF[2[8+l}hn7 G&xzݤIYߚ&6@y0GsC,=$z)CSLW,ٸT[U_]oY`A7%/~< ]K3/]`etV9}؉2 5}f%=CzfЋrDw8'U4:?nc8~7s88Cra|n#^Rޔ 7WMh# ^|G]n/yxgGG/6kjYCK,T!LsQ=O`J58WЁ[訤% YAϪ9\Ap+H*//TL5.]ѕA~`8~GWp6-xn%? M 5#h$&&I9o$ZsgczX*ʙw2Pп?_Uƍxߍx+0#'u_`)T2ͩ)n^R&y@: Y~sȸ{E9n4⤤ y9>: qxk7^[!a<Ӡ s| 4{V /l(3UuKn/k{I+}CxS|7Tsw÷m|1=q<__6gַC駟ƫ_j<3x?0~\e_cej3y%rI:9ma)L7ͩ=xyŽ-5p6DL) +9⫠ˡ$9r gӝKOn?'7ZǐU܈)K~.̼qaM;pn;?#k6Bn˗QT{fmқ}xp:"[ox;>яLd'\ƍx{ރxs^իxކx"n߾-|ЯďIl'2OJ_F@#kY|R#~b" }3iL/bt#pnkwS:DcJ@ z?>ރI9AG1p{u<=![Az-q÷pq>qp=va20y !*Ϋ!pKpQf^DU啭նZ羃 #KZA׾5| _Tg?k}S5."U.73o+Pv`z(&1"GLox ` V4}b'9>AI1%=7> ҍ{}s8A70[p# lGB1h˝k )_7EMMnK^~_/}KBxg`MF%Tb2X5[̿[2&2c^saG:[37kwl2sk: pc=? \ہ .a&@pnb-"8-RQ@ZR.Žwaʜ[}K_W… P "A)P*T"CEChu۳jx48+#&qE5ZEf[pw5Bpi f`y€v8s!SIMYM"m5aT*lw铕j$0SX?ͻq{u{~w~ٸZ M$c'FfT<+F8G4MB'pG:`; _񬚏g(G@-nq0b7Γ  n#ܤCtwgG9΢a!2]TSŕzkL2Wlrqߦ'{s,/)hHްqLN1T+0sBz{#"9p!͡Ll d|fsn- C.a z|˜!vC%WGRˣ2.bkk=m8e?8EmZˍn/i@#OO5?lua=Qkgay6g\4ME[ qWpoj9 e) p{dr i&aTT^p\xҥÞC _[jo=۲?[r/yw]H' GW/f<ۙF49@8|{NpWpLEf戃f}@p͏!LSǷ)%Bz >C"fPgx``uzܓV+3R~Ph)d]AG{IGG_.LF%%.oO9,ah0s[ l ;7ÿEs∐>5Ǹaw`kw ْs8|z80y4\48x@\Aӷ4t53WY-[;b K.iʽdC\JZ+g/xL[Yx7P1Yy,Mgo4xp&Ս:$o褜[# IFZ-[ i3!)@az B> d?q9΅iąsZBYHt[0P Cq>3 };I80}<;8`7F8 sa3q탖oUvOS ipac81ϙΊ"/;o'"S`kM%z:ObεHo8 HQJh[\ _cNf5Ca^pNs ~~/8ʳK 3wuH=~Qr7Dg0.3;7<"#ZgO92`q8e`=`RqK#w\Zg83IE+礎fw8Rrf3s  4 ls|kazp~p~KCzR0&Ftk̠%wp(z:EzΏaP^Q96l8iaw׃:Zs/c6#Ncss;|pGU.#(Ij,NO(߭7Rf<c@\ Lg09{#1]v9L߾YVkw'8v$˴#RγT3Wyk각\Sָ)Kcu@ED4rS>)U@ jZ9Goss<9IES^5oAwAu dP4t()8(H_슾gް}мCz;prI^DzzJ\>sX+vpnaԷTi[)OϳCf+/|pVugat`7j P kyJrpLBAZ짱V0NIFltwpa׸ }ԧ㱊 h+t1C`ѮO=kKE_NQ&[WgQi`Z٫ϡ.9nqb*:&T8w7~@Bw 8(KK*tN4\RжvA~k#]3 8OPY a]B=]VmnU6C%,gβu@oO*pNuU_^&z$zʌkJ:n>BY #7=USzBlHBɵ}ғ#!m9crOղ =ǣrLU*Sɽ _[ں[:WZv丰8:ɶ3) kP A+gRa3%EN*Z#m ulGvꃞZm:jz"@zu,r_egw3`^\S̞"@m|]宠u@o0=(j'iz\-ljqR.u 1Qutgvη]iPaT4΁Cڐ7PYz/\AO`qJy;ZAPJ96Y~ұGt=KhBoDZok J:%1Tz˪)p쪀TZoqZJ *g8gdӬ`+hQON^,Uo>9xCZ? q@yZ>se6I[_UMwiZ3$V* $k$ѵAep7 vc%JO*Z;Y? :!M K pwOk3@$/,}UuҺ|:Wu#Zb1*Gf&@=W(ӤnG9n E! Co~ 0+IIˁCkJ\?hi=%HM;hQ]VYSI9g4KEgkxi 2r.ڰgG@t~v(Q[%ew6qoԆz)e j4{WY9sG@Tq`0e0׷'E|C2CCRTvQȷ8r+ } ` [[i{swuI^iw)lkCfYNDj^e͘y0רK_4'??߀`]><89#05 ;cMe˰s[3%XC$_™m5]=-e gnx "T,}6p%+߾Zakb[#CЀ-?zgk蟵dmPVk`9ӔҤb.:Sξqv2ۈۮ1J,nCYX 3ɥ ̺NYحdCX[[_@K'PϮڈְޱ쬜C \'&@>b Ƚ(Y+s-DA nc6˿45uj+_FiaΛ(ԨK[ipmjU*g/<$2wypjA.\gϽaTVȳMہ9?Rk?>󡭰5_Y[O܎k:7gqSnˆn9'VdalL,iw݇JFLpSޤ9W;K !`ŒP,kyfBV'ss/pUm /ju7F`">*[АM_ 6>CʶV9؂r,mPiY- ZK5 qi_"FĠ^݀x7 %94ߌ/]2'nhM9x[8-QztP:kV6չ͑ \Esw.MrrwxԲ5R8XVVSs39 #^{sF]ްu-ܲj*[c}DS| R'\zq@8¨\I%{fK+Tku~]˼rˣ+imʐT2T͵R2~iq58xVC.}CB 5WK=r9̊r?RI8w2jsLSyi+,zij j6K(B j8q+-JM,gnYV5WGRJ|;$-_-lu])yl1v/:oKAxN4K.V+ù x!-R%Iס,]O2 -Y5Y Ԡ|FGtmAC(00,V|憥#{S=h(,g@qu4;Ϗ;~lsmA4~vTG`k.j/SI e g+l[#by_ rf p.w{m^R%$ZUCbSGB3sz鏞r[mR6z[mMro+7 zKjM1hŗzXZ0n3%sWd}HB:7)JZ#<䣧 (_G+ean;uJKomn Z^B 32B6h# #EB_NJA:[_/35476vh CV6tc~PeZMVoN܎L7~7|׮]ç?i\t)oďȏ`xJOIԵ \uaUAm~ 7ϋx+_YuoKݲƪy99OXτ(,ױ_˖-/cv39`0~g<B,?tLe@{miBW,u& OH{ߌW~a|_ M_~~,, )tf,OK_[-, oͬVNߪŷ0 Csw:(ZȈn% m߳> mbJa>`uCddz*i6,Va[Y/_^iKiml:L蜾nSJ!3HA@bj=Ph][< 2T\VVxSK@^h =pz'Eu[\F2,!>e/Y>\vT_z]g~e~֬z{Fm8^s'=(gtkgrhwTkQMY=ج?|m͡/Y $r.kE=wmEUlFEMH3To(<0)A`NV {j,y_Beg&H׎E[^Mu{yhlӿY޲[ 8le=K3sp#}CUA]SJS6_jY)P^Pѐe5-^i:I@y25oVX+d[~i )gwH`S[ض*fkh.y==5 |KsE߬ۖWm`?NZ-Z{Q!:k>sÃKem;J1kY7\sX_>箚=`'j4<J\f9-ʰQ͑kZ@.y_1pj  ZRguX*`zY76V7tO- 'vuhpմ mG= K/ ˩zZլߓdrҖ\Ow :9P#Y ܪyRBrǂ.\r ]&g7jZ>8>7z\o+h{NUY+/S}?t]9xY9c);.VRBM5[iֺJ~e; yqGwyY5b1]ǃezs]_YsU<`q -f ,U͍[ mR4[{+rq^ }0:bV->&>.74*5`ucK-X+XB-riPO] `+ ,hta4H IDAT?.X[2nFiY+hm4/ ^J0-ʙ}s ;_.!Eӱj\VZNիR-r{6Ah+M?1>ߊ}+, 2Rɺ?sre2^Ttq"Xmؓ>g /Zې}_z݉K5]P.U=ChlkiF v ]XwizF%.sKYV2f6wz.jwr[6%pʳmlAZ) sjyki =| ڵ6}hr_[mʺ m=DZO@?S n5V۪-p-gvԀƨ:c#+Sd@Zn%iˏljkՇR,O1ϵ1lObu8{ W9ְfhhw_?`nwP\ej~:i4ѐ:yQ 2~!,Z{/!yGW2liHk;<ռȃ CRvVmlSP",Sr{z.յ.mHt8<;]PhjZ ZB5S2TׯσW*cԎJn ɽ)뢶[$\x _MnV]SѷÇs-h{i1>f \]Go3?|L?>~%X$^k)yۦ!ls4 /eUۖZ^ oS)υt-77V?ti|9^ Z^۵6dJaqbDY[J7>V5f? fb%hy9[=k{V5ޮaMy56*qX2f. 19VzaoJߦm{@j{ s-^?W۔m=!]jKs˫̹l]YKL[F;e])Z޶bZTsr{i6@W^^˱H/{[sTLױVI۷8lu+y~t:7Zt\!H6 rr!=rր?# =1֦Y)qY[h?R~{jr8Tr=|AY? uiZ $~oOAekW<:z%=lDt y\3mbXʛA55_ /=-g{V?2kQ9]ɵ`Qpz1xy[J`m/j5%e} yZ c3A\QӺR5_~ W֒JeA\:+h:7؝B 1՚p5(ka, ׳+/WѨ|6J}'V>QUyv]MjJwK ̾zoT KIWЭ}ʶT_S-hη V^ܨ^ rm:@:TKyb%KM<ޖFt}J`so_9="\ڒ羐yV;ZOyH+>V)2nŝ0Im|,V~_9!sڻ˿|,hYP\oa#`={**חuUt 򙯿ֺ55qgtKj9.\k5NiaRͦviS}rk{jg@ Y9SE^i|El̀WS;{pm]N۶s pMBbK)PI[=ױ,'Wrumz{yن"/__|?G?wx׻ޅy^h\ZeB󶖗BnoZ=4#2Vi j׏/o>z1pev 3r9xٳ3 xӛTߺu ?8>O{1_qmu+/dLӜrk׮ebbgQ~1o/gxi'c,\[la0==P*'A/bPlooǶm,ˢo8ummmx^lۦ+V8?%I=磴7ٻw/ǎT*Upgz 033Cuu5/^t:jN$ܻwF~ ZDĥtI("R*hRAKz!@:7ߓH$盛IĽTR< PSSӧOiii̙3\t H$BSS?~ϳ~zطoovx TR2 wޥzzzXjyi^|Iee%a>͛7)))aǎ :=HVzZ ! N(4MBМח/_f˖-ٳblEJOв(a^/OٳٳgDQFGG J+jkރEmppso:;; BTUU9L ZݻwSSS3Z0T9 ZDĥ-"R*hRA ZDĥT"".q)KED\J-"RU-IENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/ellipk.py0000644000175000017500000000013512253362407022530 0ustar georgeskgeorgesk# Complete elliptic integrals K(m) and E(m) plot([ellipk, ellipe], [-2,1], [0,3], points=600)sympy-0.7.4.1/doc/src/modules/mpmath/plots/besseli_c.png0000644000175000017500000010442712253362407023345 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxmmy{j9vnc;qW&(xgĘK,A`)H틕|p$&F@"AH q2Clw{{g>TժU^9]OwݪzV>YjV[mV{] ozjjjVЫjS[j:ЫjS[j:ЫjS[j:ЫjS[j:ЫjS[j:ЫjS[j:ЫjS[j:ЫjS[j:ЫjS[j:ЫjS[j:ЫjS|wj_AkKcfؿ~W_zj٪+?[TzKcf++?ȏ{a~W~oo"i8sݧp]u[R޴uзr{h'ܿh7ڃ`֥}gw8VZyPD5+/J.r-7@О+gw}OlٸO"`D}գkʓO"$IK9%}~w$m7v2'ԇ~_{OMl(>G9_gU{{ˇ>!~/~ 󷽓?=ߛߎnrZڅ+u77}}Z-[e~6^6?~۷^zm:ک+wNmѮCA*Nt`I(*ao{~q8883©;;# l q@7lyc+ Nb1r[w> Rk ?\2n_Ȓ?>翏`[),J%N,mk++MM@ \"oXkmj:åDɸuڤͤ0N Y7AZe 'svz#mhQ-õZq0#M MN\ O p*/%SLQc"`\6o$-j|i$k1: ~YZPW:K =x oytl#%^FT+_h[;OؼwmY=-t]G6iwj܊͉)ʉ #H *ډ}Ya"ą t x Y=5X p΃O|jc`H|pJsȷ7ѡh=SOݙЋMx;"(k \ έmo}"cܗChG\HؼI+{!=^O >oA-m:]m&>vJkV  ]rm_JT@]OmAԖ'Ko^|SVNyqh/>Vg )8P)bS> ^F4z%tk!큳ЍX8osE9!;\9쎖 Є|=x 7雨xHW%2K6֪{[J  a!KcEN+YYFǝVҧVVq:X%nhhʣgǎڣa'](LJ {&)4s-mSj2O)\Y0Sq\| vD-D?𫭀^lihͭπ@VgփRU6xPRs)d,oᖌگ,;mpG^;ek_{g. s)t{"8;!GQJ` C87\Ж.&{"ŝ80aOsHt ?V@/XASk9~uZ&^)o[{/>P>Vc :u>C@/;= vp s!4cׁɔs5+NVECt"q[ }}cc%^I~lsmb3ؙ 3KNw|!įUP 5.jV"tmǡkJ {RG Bsasy;Pq*a@'I}I. >;"CmΏd V@/6k>JJa[1z'# s %-bo8O!tp&S/}8٫Eޫc22p| [8)lcpЮƴkЎ5I#*B>V^c8z[ ᰍ:Nq)5Apɧ}KK{+ ^<xhe )ooA7s*V~ WG kmܟ0]2P 4>oLXAi#V&SR(KsM^J T\TeUAz [rB>>ˮ+#f_NuE4ԅ;hB؉ZG-8 نa/㽽D. Р ;v_7ɖh va#/cx {=[Tz a\R7C͊uo:1!7.oi񲲡+}kʺ&~UHK} wGFUAz :i@ j%0_: Y5UhiقVaN0r~Z \up%pi#UEWɣᎥ'Gᔍ<%9@z_& {U@]hjl2ܹr!&|EDY.V Ŗ)PI@cܖ<X;3C5{cv%(KҔy[,^1*A*X=?2k^mW`w%G4s$: uxPsqADd$cw=#Eƍo"__wIb\um :ЋM@s`v{S`I_쁀+933/~Ze8A)f:ħ#p^BégPBQh΅gұ.c"1eKÆFNhp+ y`H|p]$>_3'W KdctfAep`F]?YY\MkB^j tK0K>/?"ʸ43.HU-3,1X({^BJʅ9. r#8m3 'tlPaa|ZY#{ݙAE?Ii 䀭@Ztp5nPQ99)D 妸GudJ.Sh iK*f(Ū ېt+QY !0ǥK1sG;™g('6m-;6.-$tM=GlS a, ='xddTv5X7 j/zߤ"kqYvs 'wjGp1$cЗ.@Ρg"P6hbq{p8Ɇ-AG Z :?l$sXG1hIUHfګ +)  `\SyCF@]Rzş8PKPǕ W.Ŋp[h}t -wp Y#(hHBYbhԠL&{0%&njz `bEW8 8w'Eqޖϰs`\PZZVpF_:>-T;CaNNAkD,j}caаa#ۨ] "pFb`ˠX>|p|vPÌgY}/=1*ho++Wxy_k5PWC#0A<2.Vj~Q k!~pAWgtaش΁F]-`GVҧ ZrfD}% x |Oz<Ϭ򪠽?_ o}[yӛr=뤎TQ@^ǜ8ޥ2˭ehK:c eɧ􍗫>6M=z pm#kn3FOa:r_ t|kb$+%KJE2a|]Y}g@/{%u='[W;E%]|z F䫌kTPUI9GP5O8&u>6 JX҉s 'iW 4YŶ$q ^t8 腖( c%ZB\ ˕rn11ch/YpiQ1ָ}P7Y#dh3d+˥\C.QHD(F yIAvƦw/ `+ZIAyA%PVԃrgn{үLVp=l𥒴<+)\[8rTA9D/(i8ݱnTQѽ {ʖD"d֒'A.Sv q[r@Cn%Nx۩w1d6A/ԳѢ41kPJ8Ҋ&>FއHLj~xo?WE$XsC!SJ{#it M7٦p'o|Uf}(uo\* o(I7}>lrD+\WұǰwIf'o?9d< _HG 0( ijXcTQ+ӱJ=Nk# VRժ$Sj7붰 0Q ./(!>fMB2~[ o/:U6::UZ:BH¥ic85pO J3Q m T,U&m:h0c\v}IEρw+WlB)1-ttArcx9T)*%uϹѼNΟC>~gXvT8ұE1t+ Fiml$G|r02xEyܯ1@o<]{H6;8緞_ Y *`+ZP^9?+^b2/7hnsd ҉/je&+$خ n] '⎽Ј?  W(b8 t[D`gm88AvH\%Gz8dh;ʻuv^G'u~|Htdtl+_R#OYE@g1I1D\@p![򜶒պ=f4nvrpEG#B@9.NSAN5D IDATt}\&(w] 0c_QיPHh =v~=zη)U\"}y6PP}^m-v>]nX:)C6Tt)|Ɣ䖒R#T6[:'l9a \6 M;8APEzQLJZ(zgm| }<|Q#>sXՀ0,oJ-/Uƾ=iH @Q%0 د~A^ymAC{WjH鎖3vzƉ׊qpQ rfSEvNE(hCC|o1IaL@4J y `qғ Q 1;||y{/;R:NToN}Ÿ7Jy.%0}?1K~_sς.p4)W-'zz~#<ӊ<)pWଃS>X))?g~TqNO-QsзxlXtd+G~#|%z 7U Mrq</<ݶtvбNrarķ֞V@W_[^x ^AuUq7Xwxw s9+ B^< d2T7O N7t8rA_c!tK[6rCO\xOUϒB%A:w9X}^?@#9>&k+m*V@W|K_QyVK Acj}QW^gV>ܧ_B=|jV@WoͿ X%ݿ'x 1\jAVs<nD9 [T(KT/̟C9,Xw>T݂I {.;~?7"w]$siE1S4}f =RS.E%=6F~WfUڧg_;^_(Y[=aηG()\RM sCc@rǔ+6T!673,cVSy( O< wތރ]؞̂1^-qOAUt\Yz Cȋu)o;SW[ܔ0c:L!+nЎT4mX%pM#)d!B9sxASqH禸Q; nD1QO+go77/']!4{<4wAs E{[<%#@}s{@dp.J7Wkh{޶Ơ{[ؼ^78/w1t=28rȉ.@Z<c{_X74F1;0'`N掲'ڷB&7ߤݝ"+\sD@NS€[-4)I%XKTmW{&X{ķ*V@/1] m 8Vs%R+j.A6uW;9Vlqe gnz tOۧy{xZ:2l6H@l2 g 'npΈIv)QWWB'$ܗis^MU## :Ћs?ɧ<1c {D!eGEV:hxCB^D=zIFl)gKN0j]EO*lO' +@#sك9u>Az8ǐ`N BqM[xbqz1Nꓑ4fjms3NE?ƶzt c)8pƑ1ڵ]Nwr\mKB"eHOk--1^9{;>3஠O(*B8xB50[cn60}rp n dz ?#\RI|BDi6{p:;,|V@/qPҕEAs6o{x@|_Wa4Σ];W=d}ݕ$KLR!lAsbOة='aQz4摍#7Ż jmԳW<χwHP$1qu{⼝?Y{G|2}-6mR`K"s##\~d=<Ǣ,W*9KD}>sTM.!:kx@. 6ULҝ^hnK> 7tMcW3i {Db5CQCU"\{ȽB@--!j|ڿ?⏭^jBxhR|0]`qˤk%v+ezvBNqõ:N΀3 Q?\V57ʅ@Mߘ"]cO@^9xsHoEL9Cu HVSkV@/5CKAcu>s _/uL'S8uZD)veU's"p)8pTްFl,{t9j1хyƂ q8] 8%G:T5U-E A>y~"]چ|K}:Vz nPJT]7^Ƚʾzpve$$р4nGS NA''B(ڹϝ{vW`+v]S>PE?=-Ez37UaPө9_+s~1rrSD>he5k+Z.GoUƙjZk~/72QJ/F fp;ucAmx){_h~l;cѭHΥ9pQZs08ثgXMG>;uԒs17=Ⱦn mƼKVmR:>اrź~ mUpbnT镴C'ut((ڇ=*Zz@QavJ:R] q8ǤKqXMbRh2q첊%_c*ho+ZMA9ȓ6Lc-MO-b&Wp`qTI0isl> v9@wF71.E= Uˑ(*a6!rP".!AAK2'+RLQ uH/Q sH;^jv&}zB?s"bg 49JoaT3Ijr3#]Wq|x-肟DSҥ,I9ARx~,` zV@/R#KZZkcwpr/d8Z{Nij }M ,v(6Nbs J\x n 1?FldުE9тalU$rs0[+=0%dv y4zYZKGa9@.m?*u]nŋ~5+ <&>"*C90+"QXH;qggpǠ ş塔VZZiĠrt!SE+uPgu-"s@^We_ܫz Qd\:Xw*٬xrKc>\](lA,vVBg_НH},'n-:ZK~pCpt!;еy5@B c9)\Že8Ztj+Z(gvmr >`Ɖo_$/MB<:Al,U5%q={tN%c7%W|$Z:`l}Q] d&}Aqj(d4:u^OQpVmRC0Ӂ@5nxE>J۹T_IRQO*Y=Y{ly_X4}@8^IkPv hc@7hK5=. Ip>u\ [/'82B{UV@/5m2_%zsۣJI ъд$@ =b_yլؿxNiFh]c=jUe0G"`F%M  ӏ%=5" ~i8Dc% =mp mRh' }w~' {~~s,d\XP|qۜJ%r9}wH:891ԃ?^ۛ)[_'ymSYSSI= u@7dP+X*TYJ$A/֕TsI=w)vca_1ϬP)zFH-g!CKZ硘>M><6mVz_}>x']]o V:Ғ<%\,ck{,=cR)O8ͣ%y7I_[56^up9_}fmt>~pWO%*8.`ZMw)1$`usc|[ eQkRa=si7 9Uu7qkmsDj+[MOi>i` Ǎ=x㶩mI怙(/Y%R1^ h)r_=XZ^z)u@\[W3/58LQ0nޯgCD+] &p)|?sw~65j+ۘ^xsöz"O5ڞr!e]AD h9PU v#5y x:}Hk$dR 㻚 îK\I{N/sVg+0sţ#%ޤo M-m-aGT;EHGzю:u*vKXj&8lU8NvlZ0hE'Y N5feM, ,V3ֶT^ 棡o 훂;pXJ3%*;k A1J:(lhuQ;ڮ ڽVh7X8#q#Jbss|@H|׿|c5g+`/9 %&I:ZP:lӱOGt{ ԽtPm|zZTwb'4ZNN.VѠ(V'yNgPږnV?o)k{[4 AM/}x ܑAMsK{=]]þm*jɭ/o6!‰8@wP tGn:1Q2pf-7g쥀maF[ ЋMzWkPMT(%wXs hfb|Bu mP5\ui:tvFQoVq cN9 zMЃGkݣ[hF1`Q?ktlJp_o,|w{ꊟÇo{#)91sS}f*sNS}J6jX`4z;@m3hW9;iz>0GQJw)[Ktn~ҵ?jUKwm?_*ް!?㜞|滿]w`d-s7xwIRХ͓PGO>ıGuzz AZ/l펜0tتОWFMGE[C^A`@wOk`}ݵUs{gggC-4A\ٿ9kMݤosi>-(4)h38|nvnx>R-AK}\r <Rs8 SO7j-_%Mxsf[tooX@]''.oaT eyϗE"DkO2qI:H1&Hxe<[oi3<Ï^{g}|3g>ó> W(̋yY `a21dؖ?PnKy֘DoW_6K(?_7ɦ+B |gS lg12"#vQgJ5PSFK tUČs-HѨ)Iݚ܎Dڸ|ݽ$zRAV]3k$}ك7F,g?K#V4{O|???{|_k~i{||}-q}Iai%cNQ eƱtaA;8ss<0W3zЙ™7l `=8kwӹ`6Smܒ*QOwr-yᣟd?~>)>]k;wS?Spo.l[dmN@9Gj>|/A9_ ras<4 |I9{MxZvvhy'l4<9+Hp/\o k@oI:x:Vn~~{!"7ۙb9}s)vlKEo{̏ нWذƅ#5J_Ҽ,< gTWᄆ;4HH{J+A/Ć6)A^iWQxBNN3\`BjI^?Q*$́{7w lK%`ΏYW/oT)&x xFX(%}V5r!/h|(nS}¹# 쮄DjGQgeˆ ^E{%mwH??6ԠD^KbV9)[ _eqn>hMA,Xv)@o;,f'0'P., ^&z/pi E/,50pq ic8-  ; c*vEp_@6L)ֹwXXFO)cߪa??o`%\$,AqKRҵ.U9'$^E;H$R4pr'GB=!VJZ<X1l`F(k Do >7UAxe5x=yf`e3༮j5 x!aH9K"vǢ(A`߬^A_ p&p)ebPġ0DزaaUNA;?n'y<:)wlܥW_KM?m97gJ)O)bh6$w'*4 ')Υ o_ B 6FjhoZ}}=JKyDK:&V@/56o :,NAx ʝ0J֠WRS٭M 5lQvbi q]W{-m A$z 15p AsK!;yyUV@/5% q}k91.US lUu9 yC?HE.(iiFaE;v 'XhFIAwЫAllE0}U4= m>6!5Ʈ긤WmRa TzO)]Ǖm;6[ )g@@5@. l0l"vjq Yw9ChG [ ,/ǹcBkߦJ4뤰O t*9A7XV@/4=hcъ/{ΧQ:4*wԾuQK::7s@,҆93~{R/To`+oP6H]=q kd0a>s668ϻ/E׃7ӰO9|2l;MAtbcE7[ЄypLƘ %u^2`_\xt dQqp~`Diz ye}@l`4 PN>:l8%O`B%*]򚯇+^h%Rtt>׉`PԿq&@&Wֱ%SO_͠Y\x9$\$<Ŀ}eF3\|IRVt j~<ؿլ^hBxdl s-h!~K!ġ2)SŠ_X@NbߌyLh_uAsl-W@[4Sm5(Sh+QD FzޯP7v amX]3)b8K24nņk<RK幫dpjY1IUNuCd/o[Є:rRyj@y ĩCu s =Dhz :=~ʜggvXȼ5xsm̿~%uc} `+ژSAc'C%1>Isըi>>I]'sy2F4OmGl+ /~s>nG%(_|lJ0k?[{)/ɋV{MBj&@|}o2V-.6~'~⥗^ C&> 2b 4}Y+^^ g}uP[KC9ó`5ߜǵyheTe^FڦiU{@ӟ?>}}xAg@s]jkYD !<6=Kv!5)ͨeF4yKM+=(+DVU3!=1OcD\S1dn=w}l_%~gOI>ɟC^x>/>sc sϩܪ@C2\m/WQ 3Kz~2:k>K׽)>=aIK|/!=`5k=Kkk|y_K|7~#O߽mO,B]V۾$Ny yۜ z.0*rpV% >?Mc?>k{Vkaq٪fOc`9F_vԿ_+ګ8*nKhbXh\H!z8jگ)8'N /_od%u;n"*5*z &ɀI2Vor,%u7Vr$+.?: amLfu-}k_qOpaoi|\KqJ ]RV=ȖӶ5I7ge+Q)u@Vy+1j1V"_K!ߺX z J%u@w67'ӫQPHD@$M&&%FFطiZ+s/Jhω~;޿VtHsc m==>T]d=J-7.H0,@7jaO ca i@P=(F*t~(wqJeqya\}8a }d$Q >u(^to+[YB}&udL?f?n[R&hM41oVbc"MlG ƚ#@h)F-M?j G_mķFZ"+s93sf}}`랫5s~|k̬:Oϙ*TԮl.o P/ߨ0QQ0uB3΅, | s`. :tDYX/1KjhfsDLpQ70W:Q΄-4x[}ï.U7=9@s ] ]F:M> :%% M> O5>+;'[)GN7c~j&dg`s 4RX` oزvٵF 肴1nEouS>d%3ͅ8-sLYU`\QҺTs*;)Sox_?7$1cK*J[ASVAmQ0S@cA'L6l_9rƘg80ڰze Y? /ǽ ܃mڶ =CZ7u㸾[pְ QSo}8d&1 T[ ]Tu/?s.3 T,G:Q*L1Pv`aM]>N0Ra\,@{2$S~`׷TzD}:ڧo\{O)?^[dI4yJ@U}XP556uĘ)#;YE6Шh] fRQ PL tjU`Ve30đ8)Y]jxUr@_nV]*d\@yS7朩pFuu) Zr!msTt2`”S8mws hf"6+S guJ:ޠgN951D=;{bKt@u)1|>]cV'͆_|8U1ΆzC U(S 76 ]vA&!F9#,'YJ igm>ˆTgiZ 9Q-Qy qlڦ4>rC>v<@m-SViOE~x=AP1bꂂM&؞oE]xège1.-m 6 upt&ԡxz_nŚZP^5G?/*5kl+U}iW6Q\_hfwtܿ[t"8{e[)h3 c &)S*HS2fUj@ρ]-إ`>Al lU !քIKAUn)e:r=z6>e 2AA6zmSB@*hp)_`XHkPt=O1`t*1 >I}˃m&6)qA BC+}ggs3(Ŷ}Gj(1i7s ZvSS *6tAbc(I%0v)) -KF خLcK`jTtJAS5YGE5tlUj8i:XjUj8ʊN.D m&z'YamPn畅T&ivb׸JZ} ~ăvFCV YT:dNɂ1Ba! #W ^">mA٬`CB]4[Y#3~؋*g _Pn8:`6ѣ[5׷UGe׵SBr8 aPxi6Ufj UeZ!`}xueTkxmژ?8DxiB3H8d$ݥdΒ AxI c2A. کg1S frs, h_ᬱrBꕻ[QR=[?/"Z6WZS|ݿtKMA?//S>wlea_+v6w )-NpPԀZ7)a.KD( 80\\!җBiv:fp ׾=(+0 lr}nioV*O[%4 ?r|O@k9.{l/Re¿ni_ qWe~%^  ܌5J䰠fêXp1zeR+SV; rƋ?zaEjw *OA?LɇP|Z}}}\sB@k&|~XKi/s˯a=_ VY-LY+57 @udCc*K6Y63=Bwq9/Wn"ބ?BU , UoK7Bp^l 8 9~93L@lWA+32uG]{ec6窪ts!&ڗM]ZQղjC&Z2B1%le}@1%%lVl0-( bHC Rǖ72PoƅO7/O7 AA;aGxGx衇SаЮO*Kmg]QX;u}J,# ບPkUN{?Ve_ȫ^/%9G60d z0S>`BKi(.J: IDATk\q>(hg3}׾<#aSА*߹@:nKBV:+W%!W7W־s `yr:v=9t>φ,pK@\wS`]pG@i'ѫ`auB'0-eW S̋ xqtj$v-MDgq*:.U>s؁0< k5\DvU@͟K ^yVg뫬>ͲNʛ&Zz+Hz}Że[X_Uے)qW[]ʙuؙ22~΁Y%E)# 7 (;TR46qhĠP=` k}.|EmcKWP Tj <kZ]n:JS%_tL=U/e}@]}xvΗR98W҈6i-z Yy!R9;Q!beMQT̋9,dN=H(zԬ+yYN57T>k,, ~kץoiMnZ(Rػj8t~T)ᖱq[qq9"^ PAOBO%R Pf@&xB2`Vn% B U-40omOĨhg!X,/xg&o뛂kBm4od17 a9Voj!Bq_7dk[#Yn Fr43Z}Ž-A_] elHls7جѤzjJUxQ0y^ B4pYLaFRRcbAۚ$rԅZu,L}"Co }T0kZJA}}q\M#?׷#hG~%("0X #z/^Y[ib,0/`Y}aQ0Є9~naR+0m_(xxM^xmȎ,dq;8j VAAm+:݉sz_m>s@ﺂɅ$\gi)MZtcfi,Մ@݀ۨcKR1.yw;] \a]{@9újCy0Ê8'o0c4Ũ7W^ Pi}5S-R@j*t&n}X/Ք^[ X 7 =r+9<`MRIݧx`׼v [tN36zM)U]}95CRȗ|WЮ\+(K-@c ;bJ~$~ITN)xSql<.?^[vnwAq< ^B }@m]~ 뺇w5>Uy4 py@!v1QirUpF @ƠkǁS퓤/Λisy 2·-﫟O!`׶nq=y<$Gr $qsR i\ܤ)ǟa.nk& aΩ#wO}ñkq>8/P`6_A s*h˱@}n кT;mG61[V%WQT Z<_l)xbTة _w s^ NlO{l!5M >HW8}`YHwզyߕVbxqYPȉ4Ҕ??fZaRvDWunbw=Iō6m$5W(uZui[hU=>ok8HX;ӀESk9KRt9r0CCRȱ"N7Z*, /mBawksAA76zM0SqڟZ ©vwA?g}jC)7A=6_NZ ¶Հr0V\+a j2__[wĎ䃲c{3f֨\|/>WЍ ^̯Db&0UFvPTmw6PM_46+m恴50K/=f³jaQ[Vlo*umXa5/ a\!᧳V}@h~,a{}MhTQM=ye!rVQQ*^[{C L@ DL^M gE8T6o6*cԵy-܇s;H7˦>|%NY%,O}2P/Z0jwmxM_6P9^nztm׶9l0ZϚܼۦ5Bʒ,u*kȗu)-kk}E($kuL+ձY!Vڄڀ)b7UwvY~+5'ҔǪUlr veѭ WqXo>kK[}l=*Hm+5y*ZaSrO m2Q^MB 8u +!9 u__Z]wF"FS Oըjwq E';l(G4x#$٨.|vg']E觞z;3p lnnr}r?W+^}>Vm_;S~H$U=1nK%hhHƆԛZnJXEnP FAW0-`Sa`Ɯw. JkAAQ3%>%ϱuPО]ky衇O??x''_⑿"\}Z>U<:|]p^5B gC!1}h.% dY-iŤ98Wv QQm>`_(Q?2) 7-R,*'m-tܱiț\B8\غnv}3}Y8y4C?|7׆˿ !,T@u~vrbQpG3pPF 9KSeU[3R5% T+Fb]Flb sJOԻ\|qŠܭމr1Nȃ1yGW15@ܗnEcdʩzΗuJPaN(6`b00Vg5 T s:CZyaNj,6,&!mZ,b%+\XIĬm0G8&Ą<]T̖s^Z0c" a)ANCTs;XMWGT:Yv"ՠe> BֹdF P}_|ch |`tc`2 ^ߔФ^/dڒ]kJ |3zOQքeѨkЉABM^ρcjզIREɩ1,UX|_A^RdG̋)svs'^X8rA+P|X[9ڼ{M" <>붌0V j|0O> ^|CӇpЫa}uB ܵOc붔*/VjzPnCV[Pm yavB|2R-dPIZ1{`P s9+SqSM^6򑃲]oOFT}[43ozϗ)r'39X7_A) າqnw&~s>r rlm5ri~^.ͨ.T ]\n_߯cϳK0 7YU4&䠡Bc"#TGH\˅&,#rpNeB8[_ZJArMiTyf9Kɫo]JR`,NR,lrISـSn3ڄ؏QsiõAWsu43'޵Wmgr XE' UqhbU+ ʹ`0z}s :>]տ^p;bX֕T9ȋ=f>> ME";́uo"RUUj9Fn +U ]pA `0z}0@m-űo =!kylsJlQ9?CRJ8 [ ;z#{n#՘eLe?/F  wac#10rp Qd`jV߆q+N ``G oP@k]`[OVbW@>Iў~cYta ,6f; 85B^\+\R6 FD.œ%Z nC͇o*T S$`܂pJ'4Z/2ahONX; @o.|gӧ_[u1{<\R[Sx}[oΥx\ЙN }A8#\*s`1dۨh %"N7,}%jׂrXW-@Zj9"%M99bЄK=''4s]G׽N31ߑ6ps RrvW3AiW Lܥdʘ)#6)dZ#aQk̨1ᶊNeߟF9YVqj8G}ҡT9 6z]MXA:OW\ۊc,W>V\^;So QKrĶ(ҢxAS R0*څ9P{4:~v UXcbRUJCЎ.~>8 ^ה ܺ5Ǧ-鷰vE>~ùĪh%@Zf:SQ6K؇IŔֵfl0QgL,h-XQ'jN9u7Ą)mHnJUm ^,U*s_@kS`^P u(V]3{߹Z1hibgp-fʴ,BaVJ1HQ0A3:bGaۘA4S OAKꫀLS Xo)_T9(hg5)Ih_ehur{Dg`?& >4+ ],sʤ*P6)X̬dwziS0CQɄM?,\l)g &ZώT=RПqM KM/kRmsq|m4GpR/W/p ;RՔ+ /T)֚1hDŽp֖EUԝL%(P ƕ0`C6WoPlcr?~LzPH3b"}E|l8v=R`\#&/U})pzZM{\kb_5s=sUWsOՇ/.+P]w8 ^T׫8sb^4v; |vEQ|[ⳟl&_EU ιz]т10⑿ ~2 lGe\֦H'צ>E<+G~=P#4FS0b&U56p.p$~f#zPԹisS;:CT3LxI X=kxm+ϥ+(LT?þ {1|A 0..{9qĞ_cpN8F9{Z/'3gpwWgy~a` v賰^ؑ#GW6sسk|#Gᡇڳk<}lllp>O0L5Μ9ï~ ~~>߷X.\wus={v-r//7={vU}{/W]uZ=[ū^*N:'>=Ɇk@G?ʍ7ȣ>g:v<锢(ؿ] s}kW]uG+ѣԧ_e~^x~~ g??ϟٟq/u!+g>ÓO>h4+k=裼o(w\vvٺZ7pw}<<~{r[o~{|qAy=@6`]6 6`]6zЃl_"o|y;6N>}(y;f^` ek"7}Çgww~w}~?~}ݼկfggn!^~;_>ƕW^%\“O>bСC8psx? a`/[ַ;oq\}uj?#~'~__`ss^z~߃ = ?̾}뮻xoop嗳c=Ϸ-3x'fv0zcǎqm>pwp5lV۠om7x#No<ml@mPЃ 6` l@m` 6jl.P=` vl @6`]6zЃ 6`^i,/IENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/bessely.py0000644000175000017500000000032512253362407022717 0ustar georgeskgeorgesk# Bessel function of 2nd kind Y_n(x) on the real line for n=0,1,2,3 y0 = lambda x: bessely(0,x) y1 = lambda x: bessely(1,x) y2 = lambda x: bessely(2,x) y3 = lambda x: bessely(3,x) plot([y0,y1,y2,y3],[0,10],[-4,1])sympy-0.7.4.1/doc/src/modules/mpmath/plots/besselj_c.py0000644000175000017500000000015212253362407023200 0ustar georgeskgeorgesk# Bessel function J_n(z) in the complex plane cplot(lambda z: besselj(1,z), [-8,8], [-8,8], points=50000) sympy-0.7.4.1/doc/src/modules/mpmath/plots/besselk.py0000644000175000017500000000033512253362407022702 0ustar georgeskgeorgesk# Modified Bessel function of 2nd kind K_n(x) on the real line for n=0,1,2,3 k0 = lambda x: besselk(0,x) k1 = lambda x: besselk(1,x) k2 = lambda x: besselk(2,x) k3 = lambda x: besselk(3,x) plot([k0,k1,k2,k3],[0,8],[0,5]) sympy-0.7.4.1/doc/src/modules/mpmath/plots/bi_c.png0000644000175000017500000020341612253362407022307 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxM-Y{""ssN ClmRK /mZz cfX 1P3`pn nwsM暲ꜳ^ "2#縪+ʧk+3ٹ~_x'DU c9x‘Od9ci 1sx̀c9xCcs13c9И=s 9c74f@1s1z9 s1oh̀c9xCcs13c9И=s 9c74f@1s1z9 s1oh̀c9xCcs13c9И=s 9c74oo?GG?Sc9>e1+W!]q{{>9S~E|+_WWkT)1?c~w___:?8X\__܊";?<Ǐ`s}cj3X`o;?hAT'oy;e\l)iQR@뷔ǒVt՘(EX e^! h^^Ctht>BuTNNNDGZV-iY@hQ:GTOh<_"+E =cGϳŋ&8XGĈNJG+K5Bcb1x1yW+"i `p\!X ` QDAL^;|ŅЯ__+_/| O4_W׾7 =!;z7/W6sW`X8lQMK}]#:@[jPkL432IT ?ڣDaQ==#i4["lCC}@wiѣ/_57~}/W^ʇ<{W8mJ鮠V'JwSD6'B=/=_G{w;>_{~y J,;LEVx!iwZ@ȳ6\Z KpwIM{; e{JPBo. bdk[ N =2 *A+oܧu%Cߞҵ0io<[G pqk9~@wApkģ |IJ=+ g6BGWj:_>1#[bVjl;&'-A[l2t,4kX΄aR3r@{ "A_G=6\^h҄QhO<|ˬh 4X Ƃs8;[BN[9xb7ܸ =bw'Ĝ 1ho7HtY,Hu64d gZSπ d|O[;W“) V,7: .2֠ʼqӉ·USϑ֦4,J9C#`LR *a!Y ʼn?2['6^Z@hyDOwh+B?a7!nwq²!R >hώ;Cy.=K6%; 2]C%q-"曘G|k>4o[` 3ļ5oaoO f]fH?.-{Iu)'kkX44f 5Ӎ3 @k%GW?1cǹWsRK)K>{yxg><1Σ#bU*@zir~\xy}π}11-VngzYbM(i3Op >Kw34XbiL:e-! ݏ %5Zџ$X)П(<;i6ţp>7PV(}*2~526-Jߗݑ~7>:4o4Ir'#E\2\h*W%8`}ht']zw;\T~Jwl9&ųųV={<׍ڳn< ҭGT{E=x0V8S.E-7'}1/qc+=n-E!`w LR}iyKҞ_ANE:;UdG8LR(4CGgP>!U]hISTJ:hrIBēNy酰^` AsM<)oG5Ӟ;s{Az'GrHf5{l{k/1s3 )Y*$P))RTXjW-/4 O+\GOSX`wh;bP5H9t #-V>望6ʀaz\)qIggcV ם)6(ؠA&t:^꓎4՝r[zM%)=N/9rK<%zl>ØZPMchM%&Ah,1|(b#eG'Sjuz|HKL.EV2;?.`FXIutJp"i-!M}@EhKvП$>K{uCvb腏NՖՐ.=5$OY ^S6]M|d[CXi;4 * BkQ{Ρ:%;kCԶ#˾C;|4 ˒u^/N҂3rOϋ ܩϽzv1V\jOi%RZجSX`yVb8Ȟ^>@豜rfek.0.1-攔M!]Rc>_;Fbim#6:\s]П8^#-^rZ֛Bok\|<HlmwH}u;)% g*ZǷmƬs% UY.VZ&uM]1ܒ&b"Zl2`s%J7a2s1P;@XhOp2am bXɊZ-z9'x^.^{Q/!ަ cKL7 99XYŵ:ő7òÁQx#xt:(X9a8a֘fY.>O˘\ ]oE'C}J mE GL-֏|>1C_\N^>oX^W^vÖo`C]~2Iy%}t !B;7[9kJQ VVGK)EC먩ivܚFrzFQik1`c`f|P6A4`Q asEh W7@>x+Ǻ%)p.wy.NMoY>q\-/MRw)XvXtX01 ؆ƮeTtQ!%5 fEg'Šl=kmVù"5pf?*^ bOcB0iI26-z̋.N I9RأAUh Π]Q9U>>:dX_]6-A.So ]5}}Ñf<}\0 zEψn/ IDATLDڈـ4Y!-Ofo;W IIrpn0xDvd@KUY`#4 j3tE/u)&4L 1f@"_:[ŷ->W[pւ><9{ˤ2{ J:9euumsLF5XNjHPX5E-ZK̍m:,Q}5tJ" 仂جG k6ad uueV m^v˷UyDpOmCv“7ſn1/ߦ( "Nٞ`gCVVtl'H`M΄6pIu<ȁ-#%o-"-"MqIA?PWH R=~zQ Wďs<x/_o»_~xg`:9KYJ&j!N!SyPQ8iEv' %KRu4n@y4uk70VJ:OXe#vQ!ѹGX~hšMX!NIZ?p^GX+75Q4|t=1vV P.^c!D&V`N m5bMCƹ-g0D3:j4lx<=Ac3v q8zҥ`ŗ JGEcgט1M)oAe^ R J†{wBxq.OI]*q,paeIb!>'cc碮 ݅rcXƖJEgHf@rD,a!$Pc-ӆHӥ\^z/Ի¿?g3_Պ??[ (~|,MQ黊<NE5"5&F*㤮6YQm$2E8j21DӦYQ(Mqg%&#.ɓ CH}+Oh816HYKLh1AϠ \kh._¾:=G"(dAÒ5+S }CAmSvGg'N9y 7f|I'(ı~R|uSA&P`Qԃ-!f@_8뫫RˏU@FϦǪ`8GBG #pܵ&7φ6$EY~zI 6 by4DI'%3DӠR4`G4&y%oNBs+Zp*QCE/`|%o4M}^KE/"ڤ`;G01,EdO55}[WN3q`اKS$X~ a2eCmf$H˰(>_H;DbuD0` "䖣L@- !f@xL#zRޢPScڢ3Ҽ{ؾj_{|eUyɍ~8kںNl9MiLxVѪyPe]?:6oY@*ݑ"UK'82 C .\r6^ȍ8{ORwUmyހ:B#ÿҰCCGgZ޶lS|VERg0{Ap<KSZi, ] |ꢙ^I RPA;e@ɐ3zn:!f@qe2 vU#ˡ-q% 3rP5 8ͥ\ٶ#R[j%Ӵ_X{Gjk q̏U]I) 8"mХpϮt6ZIkH<9@8y O\+\hq\`u@>g=I 5|76,p88zc[-ƮYwYIHg%m`KOja.u k94и24إ.  {$ʀ@{,RWܙ6$XXslqLc'Rdjy|33Y"tinCT!jyy)袠 GnE:B$RK?>kp8!ppsK.ݚ^'8 Ҧ="3upSd8*|ڔ,FRkU 8vpsWTUxƩaMuj=y;ABbTYYߠ+g6aN͐joB҅_oKF\ޟ `-!)35cYIy@ZIpVQ=9-lU!FIFd6 R͞]3XEm.X'(e,L.ޣaanI]?kn%.i֧rm G<^"w ki6 18xoXNN8Č븱l^czmRԒ9 KR 5k&XG@-2[X"=1̀>Bf9ueL/ s4?ssȨ>"Kse͙6fk򤩬LRI7r*fJ 4ٷZzTk(ho' >P!: 91CM7`pp{ﳢ.\k_z[&m"X/Z>++븰YlGW %7y'z>/RII3&ߦ fX##jz:=~>0cLjG :Mw+[ԀJ9ЍH-(˦10X=K%-Yُ  TiPg\/>oՑQŔz粵lTLΊ (jzۆ`GDa@O֞>:>+&^MV5z}6:U\*ńElcnп{ԃIt%)i2 .,ri0W` -hz,]I9dU}ȊZ.̚YLboj\S6A0tr>eJR.}0YUk'v˕c3?nYj*ExyTZjHuvr~(,R84W`1dUԜZf7I5mNoє1e3e\T4EPяNhf,.-J:Tt_)0*5:$Q[E=GK>Cp[z`=:^hϷ>- \ZEI̙ vfN{ i.Jʖp2YQ`7ư1Ԧ6+ij-4w+HI$ɐ.]VC'W,П(&Ӌo`U+9 J>Š_Kd:gEzt-cV^FH贿6hVYZ|^@ 1YMBpRלBiDI ͐|ah@GOGsm}xج< u}-!Qa} %"rF/xG(pV{ A;-\X턣M ALٞiy#.폣Vem8ƃ4h(="xbrǣ0eJ5)S :~|sC8o90Mc'A=_IcѳZґ@\eoyN'Ez\(T깼|TQٓ.^- seu2paE} h&.t`spJZ{Ntt j`~@$Y0Ө '}x a|! nqlxnc?g>k- h`Hw嬞NRAGu$:+EY8IvǤwhYW_azXKj1.VƮ1@Z>2ºX Ne3&ŔC%"( \`sԍ FᴂTo>F1yf0%`|F/YI/z?W1]2;(h^u9l|m{Pё4`yXBJ:!iϑ@>W L.&^yƣq- Q7DVD]PfRhy|'Ƽs, H @`B\a/cAmv Ҧ@(1G{C]O SVņ&fsYM7× ,#KR\D)b\:"$ 6CTk h5=[,Q(6 < _GG~y30`s bnrY\c{`Rm>嚔NtEQ:+5)G#!\Cc乂p} "6?w!"M1f@(6#ԝc~w TТ3+2+xaZb-*R\ -? VК5>TdFnab1: & N-M&jtRݙV GhI >^{N5r$psP6M`iz!-EY]ƃۢޡ2q=ncX~sw^y 0O 'Xrm1f^6!QxJ5)h 1Muݎ#עf6Ե1Bz` )_CQ[ ]4~)s1cFԃNs3ZA&ˇiJ"iAg(Fw%U J{jmlSj8pZUp h• 6@Si35hvf=*iMTWl#ɗ.I'Zz&`]@S 4Ym&Hܝ4`/ۂag9Y=aǰMbkv\4^hu4 7cvAI%Ԥ,@#'+)[ SBMנ.փ6\:Cz:Ƙ bteO.Ҹ(h3[כy#:F<#a}2ts߾`·mEcP s٧pY5J:ҽ:-m 4luhtE-C}U&+i+^itQ<G<'JEP|cnj8y~CG"a$ SWʡ\k^hBZS]ʏ6ٖ'qXSέу.>8FHbtLktKqT҃N& Ml 賒sEUʩO]rB4v劚>HǞA`{z./K1n@t '݀Xe!l XKsAR刕Vi]@>]$EXb-В%CZRO3pH:d 6"42I SXDAW&)@HW3?vި.D=)3,wV%c ٶkz"`*EA`oh R50YQ-!YOk-z|a_QhbC[ 3lwt^ >Uc'@gU]GGXlUdw{j9O6U`c6Ym$":qL78A0Na f4Woз`,a_Nv(J!} >`daq,u`uHn!}RYt!/}Wd,,(C!Rb%55"?)^㑘1#Y%#n`_-Em~Z(LǐaԨ @HzFcR*Q&y:O)(hUSV8qaجK`<`HHE-Q6M JS-G ɏ> J:kV)U/5`w$8'P{mO@Ϟ }Vчi /6a(C w,J$`MRѹMDEQ!+>Yg#; j%HXr2ZK"^^{vvXӹեbncR*ۓOb#KX:br/y6RSxr`IkYci0*ZQ+鴖 z7 nLM[F`$ )dj )g¢ cnjկ̻(Jzi E  ӇA^\Q ~EL MJQϠV+]kg+kb4uψln!A;B x 4y~VR y59,\p⺵ ݎ@:*]6~N1,ҹܥUEc >ī2p h:Nuf0dFTY,hVMHo Ei펵n@]&EQغ엥WW~ӧo4}o[\?{-Q21wܞ jjoСx^WW1ɹџ<996G؄ܕB5FoGtV]tͫGKm]ޒ,Wԃ/r[ihukBސ0HҮB9垬=Yhs4t8* Ay</ yVMn(]G"p lu+IJo5NŬ0kJ7Ht8 UMkJُL>~W/ןn<}/˦ *T%+0'V3͑5``0H/IK e+"QRtաF;oUtn!'à*, "}F7m =yѰK>o<μ1-E= p..PuG#ӑlHllɺtINWt͓ûN;Bl |!#ABBD \ Ƨ*V WeG.!JzjfhPϧk]ݬ !"})cTy\!]IXfxK H3.:Y3D˲ miq8015!k%9!n?`bsDE;EMwN.\m9&ڰ׉oW@R f3Z@3NEEkES-> XvC$]]v= Ig8|#48pzD?W,a! t]s1"'k.(je ^}` qhWѣ*$r,ߚLqWjt[hOv{"^~53<7j}zgp*P2 qXa76wb IQBFIKzFs~֔Q3e@)­gxK*O\EкXÔ/uK4'ʙxEtwlt@c9Gb gع(AGD{Furh-* l.6eP-6z!;@}9[Za#'N+ oA{wngLE8+Z d=YDQ]APAFIL'~\1~s|A:pmt% #<BUA,{`[@mo@^~X)0$EU+w9p=*a8\tksd_$PL|"Hl $kùѹ GBNׄcRUg&:̳ %Ƞ<dG+N]9 $ Op\MluT%-V,d/w& nF:Q2AU[(WvBS.nf0߲<|[޷{@]=^<@X3bq H?/R  vWl've4h4SZΌ7Q|gAf Lg#ݏm,H95[@73j-ie]A̽tH:ZɯS6mC{戥hn0 $W{cL*rhE+,yexfqy+hW˥*H#Y$kY!M LȻlH \^ԩ9 9 rk*띚)0gn,hۖ2amED%Љٟa]4Y}>hUY bgmk00xKR :W8.㡃x6a RY^pLLEOXin}zK6 ,Jˇ~]誃WMw+k,*z[$iQyٶ2/Vc!۔s*s>W8k> { .tkt$i'G^7y%&#E@3Abi2'snl [C[w猎FZ3 vNݿ yWS,/hϙ_Om7\ 3~vv] qlwzdvc,NJXsM̠Y+tu[ft/yGɍEJ''BЄ6k[_Az=]ù??4M[տWWht#9Fɡ uz4:T/wX+Z큶PHiڞR,nu íPR7YKYJ޵Х;w̦4t4awOs- ]Tюpfb [,)R@3#q?tOu{]lYdrarpA6[~cPŇ T])gTIj"aݭͱt5 b>nZ:>o}ߐ[;n.6\ao0EĒ$(YfsJ:iB:Eݰ~/g7 IDATOz9RA(b/L>畂oLġ3a%4 +֥쀮6R~tѣ)n'Jm:7?]V]V{JK,%27L+-ʱUђv)Y;;+TM9I3HFsB~Tyݻ5W@ApYD0P@ی{$HQ6"{{IR:1Puj 0<)5zEQG eUK`7킬[i׌%?1l[_ó?5o~f6olh*kYz@&k }{0 0GP tn7@VE.uVХTrPV8+[loI**h?KG^49%,L u3 Dž7+{#>y8)>u`3p*`؛ ;dK7~tޖ!F"0" HpY^ޭ,,h_˗W gO_!g#/_c>wP͙ FQU ٨[}yp VrKU.*Y6n~Pjoa4y$G ~ܲ7jK9օ(=*-s)iw6ЦWXht ֲ9NV􏕴 2O/9 "5"W90%m*hpvZ!aܭT ԫ0we햜WM6H-HW ~.kH/_g?g?y?{OO?Eq&d,Hb-E6[Х&ZiDr@ϦΌ\=/ fp)WKc8t9a ^̈́pdjԲV]([U.JוZ!]QZ]˒-٭rT"Ha++%>yM|! cr]!Lީ;py$pt _(OWؔ`]+F$YXR嶪j 0mަ;H-ԗk&1[kdv/ZQ 꺇a/9VbWDb:ɚ1i](\*&ma]I3Ҩ`^BZzƒC2PϭޔhvH+6TΗj`f7Kw yP4e=  ax 5腇uxcR鄾mQy&rd2쐶y|66e'#DSDWey SdQ5y?7+`gۡ@* !Z&-ߢ7HעIF6ňP;ңІeN g]"9BɁjE[2Wj lnEq+۵]gD7z.tmUp$%sEّ MF:-UkswU0 )0f^-A𨷚)л(l:883ٹ"Pmp.mAlz ZCD Qu{ 9q -u_Ϛ]mU$.NjC[ 7$yJA%_zf2{X4H]9ĎYW@y4M' hN$NK弚*SPk3twUlnA-80E!`'=і4H`BjDXgD vxh UE4 ~VXv|ϐ0+{^~~ّ֧(D`rj,2v=Ŧh 6(A=nB)t(s}yl}]:+hbq(?rB0M^b7RPK);V"It+^&Ժͧe cW}2n=ZMl!V؊_]=#( ϔrvHi)*_(iJuhfzb)%bZicZEu27#)m'yɑp s"9عA8Ekm~E65xSلA7^gQo[0mHY9B!$dQ,RO$o ~[]JEAGs37N]6ǍK%1'2 kHbN5QJ<jg`9EC.P^Z-ez`oUv lp :D>/9z8FtiJm,[d,.zBRi,We8m-j7g68uQZbVAŧl ;ur$-lTHQ].p766gpL:R:w]0@oҳz6=0zeu Ee/`k'Ek%MUқQBIX5=ú(usk7_5ר~ :#e͏OfOM,t$vOSZX/{ײj'4@=f(tm^ hr@nvR0+!/nvEBV!w! MɍF5^w({!,I!^HBA%ptH$0VZjVI*ZEI>?3MCBY\zwzV|WyW}KhXz֍w]k8c(a +VUA[acqx'JT{#a{hjsUtM lteS_1ڔ7Qy.k8H $hPֵznV4zRCrIn)r;.x7kp,ov@q3.JgV{#4pf 骤Cfx6^m^v6^G}܎޿w[˝Gs<%G!jʦ[xоB2[2uloLXsڢ38 sZ(f, 2(.Kx03j䲨Iks"w=hgY$BzZV>?@@W[H 0f d -WW9HjC.m'2yXLA]-1G@8#c'h/9Z[ %}3dWrDSb[ Z2Kr8zoSZ:,?iAohC){+UtD9`ͨBICU :!vqyvSӠ ތNOj8Z4k[^ +gR0徙M?Y^ia"dYxK%]^Zs]\y^?-{}u1-3sx{/?S?ŧ>y*yZ 8A(+K(ɣ7ΨpdiAOqs!lLU;FkaD)'T́ Nk+-aN!-5wM㜥p|t9p9 xfz4Y}A(W)م-[ܶ10R.4-K*` r;aVwK`A=C|w]g/Cٟekkޟ8gzUʢWȨB'YP'<`J+& VA٠sǠ~"GWFE+Z&%RϯI!`΅-i'DX/Ywwwh1 3}.6 859ᩐ.(ҵwlTrǼm,bRUw5 4FU6㵾S?Y]^qwШZ?e|h #?#O_?i0/>87s A^@8X0#H0 ep32OID 6X~)z ռ,dĝ3!{LJO)~~뿍Ϛ)'IӞv =]υpTKθ0OH<Bt]m.\E{v6| %q#cp~GO >Bh0UNt$KL5n |C.{}>.Z)AJ:MXN ӑS`.`zx"ħrT[(leV [X)}kkPu۹.(Wg KG k# $`^ms Y|^]Wuyy ~TLJ1F~g~w??ۣU^Ј捝Omi$pN߂B|W/e'U_xĆI+%g&}uD&4_@1:Jl.ҧ0|3SPS0XXF_T&_bDxN`=ckK=|П_vl-jʹipR J9- @䆞UO~s SxMG{S;jvABiD = qJ'( mgbAO*d2g&NzQ?HӾ#}lE}]W^SrR`@ 螹Thk!usx>|_υᛂ Ṛ>WE~sgEjsT7T,|{ܱJaߧUHOOS?S|3~g?}}/ſ?Ÿ?_#[8f +ēF5O%¢xe k_apo7WH}|2y2.š6 r5yatw967㷡X6#~D>@x@ۅu-0`I u};7w[Q׷K=.? C ?g ??˯ꯒsW^O? S_f@+; =wK\}7-0SBsﲕ #*79Fkǿ8(ᓟǏR{ulA"  ps\3}'qosq8iבN|nqd4XC`+!byxkLyvD0D@碚SqgyK8\Gwq/^o0>{F:E5̩緆;~^^zx{?yw}y9{ŰOo+ܽ-&K݂sy t;>'>{~'?Iկ_*'ЛAo|DJG3=zubg1FMFvpJ᷹7H8l_ {n:8 <xGk;]*""r@8!2]IfvdNdȅ+O<Nʁ.? `.A۔Υ^svYw껿>|(FRr=Ljk H߾̠>KRM]l˱w,WXWxѧM|_@%buq5[9Ϡ 茶\nB-j`ø|V"ڼS|//ӧOZ@_eD__ܟW~WxWL=r} e:GCxl\N IDAT#,9\ގ7z߸*A/sG\X2⩚mϒy7sZ6.#*;W>cZ׷TJ<hDW^Oc5yvs>C _n]/H0)zi |+нXvn]^%ICHFAw yxS ).s-D  {@!8 B, |+hم줧 -wR7xՏ-p!p:I8g8)rD١pf#k@xu b,ؘ/@c!>Y lD <"G,-JI~Rao,C4GfHB.(5 "X D"k'p&@ k%Ja+n#ыlԾsnW@oV=/KYE᳔ֿS{hrQ[CܪS[c5/'䟬?~N7) $:J?{#˖i~ko3gDsn>ȢP& [bVIHwJĄ1& FlԌ'-A[՝Y]P =xl<5ʮy7g"FXBxlrF K*'O$t )>-$CbE`WfzD:&$l`""N/N~OΕƘM|٧{:qH+qO } |h|9ЩVmO5~{F:VН?p,]Y;ڄv%gV{f{LNH7T䥠ەP2KH+Av&^r8j>3њJ!~š򟏊Xy@E t 5u\ue;42J;!tQ_hS0l>}J?g<_uhO'y)vКpe@;Ub0+aMۇl 'fEZQo/=Щ6eyc &zɩf~ʅwJEx}P8l8 Xˌ9 փοAs gag+]6w1iW>|;OYf9PGAVf.πqA=QS߉+ o/LLAqCvRjs6y},Ԝ%wP39 5u<(WV G製+쑧N.<_ZĕهW8J́VrZj?~JPydyr姨XXt^ڙJmHvV=F( g8kUyLPS@]Nۼ fB}^Z6!ɟ>/ ^qPH4+?l:bN=5PBeԌ*-*N 2Pfu7}IV~DԽnu&CZT`Ω*mŒlrO~*w1! t~"C9Y`V +J罴d ;>{ ʤƉr.ۗBX@90~~bs!6[!U7xڒ|3}uWtZ6ʕ iS>``9{YelX28n-V{mWҹi3(ګ6}?5N}|[&E''ɶ)(槿={;|_diFipE , BXF2eŘ"Uط*㳐`V݂Qέ\`^0}[Fuu 5l1slZ~V76gnWYg@ cqtYhP ( ~-\ѵ`eo箠 ] Zf݃t tjY*THg׻57Q|>Fd?gr @YtO'hI *Kc'Sa>xR;6˜1HKW3˺z8#歳䬮&;M g~#D}ޮ9}g"}m3?pE ЮCkqhэ+hCj%h4G(zBܜwOE3o RPWА]N ]Qc<ǩNԉ'UMO{;\hU=yJ:Z_5ً0 tJKvquE}e'I9p$o8iSV=<S`,XhϪzΛ g)اP>-^llx<We}4?ӯ? \ucOjBWEOCgwm$ebP/,)$sZCQ''Vvc)ד : :.!yM#"22fe z$yZr CSɹEG"}Ρ&ީN6[.)"ܞG֗1$f="g g~~~bO@8rTg\3Zfz\0xP{D+hL*JN~6 ]e38TDZC:4BJ9(;X'Xqm^yCڡx@UՕ;++I$v̊P~( N6JistaN>J:I2;n)M.8ٶ{vBϷgȬ 88j Jrȓ4÷>̭Ϝ Ϭ|+w]~~0šz=P@%w^RECsVf!a8&q+ő˼CIjQ)i._d'̃'?$tճYOsA0v6X/VI=r݁d(%jv@*:"o`W5)uL1=rԑ=#W=C J.9ٚz֍%F[Rd]r(;98`r>0ώl!TlfH@'awT 0ʹK积y:>7}7 gsRmI.R9U)]mhNbibIfݒᬳi!EAS=|d~Az]X)!10gaUu YI/EXX3ryh w# G'S &x Ƒ^ t" /r`I4ieRI HD)Q9XsZ0O<sc8\=cqGָC~Z*?-@pbYWUo=//?#~~ov_[ɢVp(B8 5\c06VQ39ʐ~S[y7U/;D*z .B:6>-TIUh"sVЖ-h#* "qo0LdP~բɼdx/7U\MV d9۫+7Z,Scs?GTX HIE5z͍Ѿm lQF DzkvkiD9Z8vFqy!mYT;|ʃ?_s=>c>c~~k_+Co@cQʼ]A/)*BΫ, L2s)rl9r4YQ46/o]38$'к {>BVGzE5R퍵T?z)J'5x(fmh|nD.Pl ub:bS4%5o?w[~be .Ծ^*]A:ޕ4-4n"K 'TlOA24N^" " :PWPJSi:NZ\sl;8 l+D=;gρӏDu3O7KA^nZ382x'^BAN)ꄆ.̬diVKm|1S1<=:Ee̶O<$7Ҩc;B $e):39M$d: ҏUM##"90xq ] Ttr@d[Az7eG}`JHװ~hM }܍a40;;炆 IDAT(Alk4{c*pVk" YAٶ cI eΣ*A &-[h)6X ΀U;5x `葦!`oi 3Ϳ}R(,겈oʍ15Gq?Ք?z74I j+d=FRVbzH#!ѩz~9uH kEWl]d=אVB\+ qAi"'ۺqlNdre\ؓNԳm`@UXJ`E盰*,n@k@O qe u ɫiO+lOsf9uBi'y29VN΀%~R~W{C, 4WТtDp.;>ɔS)V= h\s9z`ޝx颢1EXh`-5zV-B M Nֈ(!ٖ iY=~>ۭ"׊T<]Ê3)π \ 7O0:6I儢АGIqbokmNhҬTU h{pǖbok#샮fFʁA4G1iĆ \Ax`yԴ4AAwooA7 [E."F哣|u M[i9Iv˱'Ɏn͋{j xP Q6ĸR{xhs'rRc6CڀUJbh ?*ȧ63?pTQ|rI7 .vYE _Zդ؁"R`iT5Hm$#겹v@;zгA4VyVJ;V ـn+Tk^k8EpHCZƒ)f=ɲz {KMEfo%H*l$՞ hb# %Ve%z>MfF\%'PΓU4`ΪHgf]rrvj gOy_g@rP -rra<Ţ^Rf1dMT0/PRCrz? Z胥I, cQaֽ-NA36@- mR]=Kn'jMr;ERM9O;k%Z@4H&\\l phTq :* [6$gm{e_b[w\2* Rԉ+ \ZsD8$d$ڃGE欜N I$Ur4OZ;{٦na9Nz}[?|v \rc-A!gpfѹY>;wmA)KSCfodyrZN]#A%0Z SMcU/@i37j^R uSkq!^My,;{@ /Qu \slƠ ʭ{[DP.6% ex9r>zK`t0GxK&.EAD.!d~ߎe[cl\25<@"-:Ey~5`JŅM{[/m).NЏ@Y9yulxn;k" ڇ#!j9!;wYɘ+ b]@;֚Q |Od@梞*]m*B~j6|\ 2?8-sCN25rQF"ת`#n\=߈rzlQ. ؓ)[ 䴺DMM#;z*!|I/ |!$^K \ĥ.db9Ypֽ)hukCT_'!B\|", ,д@ʸNdY.ճx ]Жn\Q%c/:59.{dHk{Ynowj=7-N o$" BU{ΓWs#7߷Y6}!.58A9L2zΊy#v` \aw p#6o_+3HG%z [D7(y6UOMR?%SՊZ,‘Rʗ_ Tx!Dx }@<x4̮Q'YDŽLngH@VhBL5<4h}+ʹ^@y߀s&C#8[>p/= ,DNd8{ չ0KR"w4%-}|p(KULfl,%l&22JOg)ՂK;,>Vnk4- }v 9<,^ ktvnlqEeAQ$tH ~U4` bR' I\EU2 1q1N@p!z޻z>N;7sVϞL\@vH@IhxFZ^m&+Ijb*gxH-._-.U~kP4Ȭ07Y?gHu.A"tb@P$nVb z#MkU3yw;ڜ2AE(EqޜyZi`LcJt1LIH>sxa %og]DWR.(,>3`U¹qNh>Yg@#V7JQ!1BڇUcjngH ;:W 5Ŭ\ zZ67*^r'O//[~JqP+¾4R@s P8y \ABӥ-_AҪT:琄 GueyZlBԴih/vs;TTH?k1(-| =B@[y>5`"o/:SXvkQMP{2Q|i.YF>{B9f7t28gٲ69-E߫U;M{)v:刈0A':J(Ɣ<:mx8hGds\ӆУנ׊^  6"o#&xÂ炟 /C>y TFO4 IVİΔ2+/`D%.tayڠqEJ ) ۄCRʘT<0X[xn itOm;ҴhjX[aCn8 zmEp/΀ۿ|[VO4;i~UТsuvkٛژUsa3zƸjs~Y FzfLE輕hl(NGPa5S*gaizF9j5kC~n}||-pouI0{ >%Ȏ]9^5K@ =@S3[_$Yi[]$i|`@F!bD}]Rg6kX!¶%( if5K"+u:vEe EyYo ҼS..;Jx:NT 94Q, ?˿AOX3?ck;˱vkV1?PCv8+ʭY?DnskWKnAbn4`]L U:Q몷PW }Rz #6{D=0{NH,#jM:`/c)LhW{9j@X6FVkI:L^˞x ^XN6QTᅪZd"#$GoJlo,uIW%;f 0&x cA9Da v.TBHӶ԰u3y+I208Ix["輽W$vҨ;>MB3?p,goRJ p 㡇 IDATLrQJ8 ;rRHEeBxb싄NlѡRԇjBѶր`R0Ɣ$kڐlo8e)Lp^`UIF N50.;)mFxgYI_ ˝VFPTR\A+4jK wLrrP侀f@YXR]NVwbnr? LXBwi5-RIFIk!l % %R ̌FN&:5jyB'b=!׮9unkT{z>8#y]g@#VܠMMkk[rl6[RczYng27N.cDl)޳Usi+iQsΡ o=!A%Yt(Jct8UWD:eXEKcql=-=ظQF,­ۨ\pTyB%q%Yp r@brO)ĂĆQ|"#^#w,InJ^K%\IN \@*a+fmd{ca40;a$~p8D:=JFPl[sVij^p(eE25[BzCZ$Gt0Y L$½&~ )P*M=(LkH>yL/x%+6 [W)x`bO==b$< t Xx`lOTA9XMx gi"ϕ_GL9ڑ@uhjlZXAus¹!yy{.D4Yc֬EfH e v>28K6.y >rˡ܋%jYdB44!!}>d Y0szpj$YްuՃ/wz')4z'ȝ8Xa&U*W [&Q5ojƒ0&F(Ko5um#/ ^)0T Wfmĭ2&B$=A 38IG D :Y9$A*GQ`HR^Bȍ.xi}dꏄ#@7S}XTs+"+:V+RzucC9Gmr;%גnTG{urwAZU!%hd[PjҾ)T {NBNzkC^\=k溉NAQKYƐ8H3 Gv%"V,0-Y=Cmȑsxhu8a5)`M6zPeX;*Kvq(uk HKz FV0߿!|K6vW"*K6D\ML@⁉G"$ݓdG҃{#;`I`Cǚ5J960t) I \|y/o{Xs+Y`pup"kvz|Yag$/{ֱT(ļ4?%"R37P}6Hg܇lmhtyxLjuPO ȀMnJ, m}&"Az.ZMG3v[hZ VaF':}@:&$i}5$8H@;°''d aCQtdׯ"р*!W "/#LJi}$;xO=>x$#[=#]kd+وJ+&޻v1kuݻ-Zs&(/9A䘈 FLk(LbkbŘM(bpSRzk`zt@v:7G;.(f}#.R]=P ])֌prSp1 zd cQÚXťK<{LaעrF21eYr2eu4hpґh%!H3 hiG8:0XoΖ!|V\κNaOnA7E1agzD;[JLBf9aR В`Ս&yC諧 #TcJ58{Cg:võ zЇ* Q Dxd "ri=2haFsEec4b#Ls)V} 뚳!9* 2~9d)˜=Mh*Yti SpW+<jzycU[J.x#_Zl3F)ۚJ([} |M0es QPbxy6bna eaGwEױB7s6ݹ #dert&hWk^@с"C[X (##\.`dru`N CMsO2).3I`)XðIƦ:u,5j&E#(iQc5Ɍ-Ķa9 Ac9Χ.WTtW X{~^OyUѥĉD.W~Q\u RrB=ׁzG#qj)`EJGItdЉ=ǣz$oś,0Zz iVr6m|j "I9\h=.LTA #SapzA'HkYG&p!Y׳g-G%QY;m' Q&A}jF FNJݒ9k*aXRaS2@-kssdd`!jB‘ mVd9w<R 9jz44)JY.qxl[c,QX7.U)Vv8%T/Tcmꌧ*r:8ۺUjH*;HhJIv[0 mZNtgObmD,|*H5dN:Hx ccУP1JР#4#] X,탴a㇞#WmONQtųA±adžbYsln1SL=:3c< fb]ڨFi`9E5Ӗg_zE^ChiGdZ䍳eR{%9Ğ(kme-sרy sj64 >nj : 9Tۇe$Y(SX#8"#SI*8qPǜRސ9ihvHzJT8˞۶NT=AðccmςGbB#kI҈bY˚3c(O 2b;\ƲNPNs}Tf&\.`VM1lc] C‘ fb -ɊeFYC¯@HJƅa`3WQrf$ M\C%(Wp]lgC6$҂* CM+r ZWLjWHX䍚\מkh^Τ:ǩƜ F9wtYwFc&<ʮ*xQT9{AQ>qYQDzG2xG( jX6p K>uaqԢ m! P%l .d}z`z"6blt9d#0fY'>:s<$2EYiL4t#FZsI ;QKƷk$uSL t'I#17lNIF%8=<}f{Z sԜ1.2iGpz2L(ZjڈplT!AShQONQ=EIk=A8Ai+3H/s_CG̻ʉଁ=\ U.r ql"ll.7=°OSG^Sp1lGycP8rjCtܖZxf#ypC}Iڮ13b˃ RTdʁ9W9H)RhԞV?'m)$iuY+6tY8CԵRxەakhNjtNrεJ2 )(4yxptqphCw@8!hMŜ;@GbA&)ix `=l]j༦Z| 혶C<xsE+\z.IJ5Cn:] s*'dܠBE!s\G ghO r\ NlȞGVdυ 54Iw+M✦rGt*ZdR a,r\<_:yGnn*\Laj4igglLbɢsQ$}e8/0ecbz1(7R Ւ\Gl7-dxr@-$‚ YmC9.] Y:Bl1Ю} iP=e+쫲/ʞ*Ue RL1:o7:DVz 8P)C㸨n&7q&s#;HZlǰ޳ynv- 71=q]!:^U [+r)% kJ:#009/ܐƩDP0dѠ !%x-`(S)*LY {?Or`huDBr0-GXc5/C|LS5vh9xX,~t|al]8b H# 1\Q!hmf q6PH̼azW0$y#DXڂmiKh|ۂfKߚn:EĥK{x_1pԟ(5]]e9IRSb)9HxA^] {^Ԉ.ebRNyOտ'݆熠9jAs%2LE< , Qv\;" KL0oYxSDfvcE;JQ) cX#Qs H =2]^ףı!Vǂ/98a>Qt+CiMp &`O3vt0 uZ;Qn3]&V:aj3ͅ.VR͔#dVՐ ׮Ε zj{~y߼+^ :wy'{/{/~ٍt(]\ݕ ֯Z\7ΰjG^Ğkwh/%,{mJ=ʲ`VK,K !fA[HjN0 0~x*i#.ۡl%66hh5319E[8(i@HaO}Td:@%$uT6n= { o}A-aM~xy r>߁ ~ffA~mAvu]-LLj+%{Wŋ!\T8 mqJNI#e˻5Lu,h[nᓟk?0_җ_rDymi37StuJ+X+ĕL^XwyRW^(j\ڮRbλRV}+R,E\VW N0rÁ6CԁU >y ݆Ȝg5P-,mK5LYS|WYB! p,={">RɤjaL Ov(}  +ٵEvsW\?[YwK_^E /k!o&s`H\fmfHzLeQ穅WN;enb}3A+c;iKɠP IDAT}&x!|=t9^,:`KWί5#o{{x[7ٟXQV;<,W^(.Sia8ccj:n5鰕ƹK칾AMwƄ:vddT?U-(xin)8`yc=4t9Ygdk44 *kY-H 0C@OI7Q?~W,',zْY7j[Dy ɹ9!N;T J'  m!&RlvR#cL0usq)y_&{*+4+x Tե.e+L V+E?~ߞy>FW|Qi2%8U9zM]EYJu:s:ΖU}"dPs,ZDI{8z \`ւd X݊ mq sD#/Z -kYh2gVSUNNU9E9AaXHh+aܿjx?@=H+V9*e\"܈a XpȈO'Ly ߎƛM2r- (N"@kН]ͱ"-!*@00ZJ/t{uhNfpxu8_14 aYv)^ @;qդ`uY^̞W7:ge廩. ?LT%s}rYA=Xɫeؽ>3fƢE3;??s J;(pP-ClJ ̹%HvcGH5IT,-4>*kAZq =Yd/ bv{*lqu\r EiY#82] .N]Ca6p)'5pGp{0tֺ_S|_Țo`VS5ҩb\5jŐck)꒜ $pڲzG$Q.nTzxxK|V>+k񡥼׭k)9IE+Tb;A Z*]mӔ \k=X" ϩ#sfe)Gz_`ip-ejlGgq.=Tz9CE=Ag dW/ rEi V"*֙Egn : nıxȼXHrp`#F88Fڄ|>TP0k!h9C8s E3a$Bd [JpV%1gETwi>Z4L%[򤄠J񱎤lPrveb4|ZQ+.6`PVImi<ˑ1 >l2f|Xgh!0 x4Ju ҆"C2Ɩfc {9CM?yW]+ )U .c[L=s<>݆.*`t'LgJj2P07 ]%Z[Pl)v]pJl,Ԑ8Laˋ xuxI9|:BK,m,m$5LzeC-[xaG }@B NG{ 76JB48va {N8,( VО@kG0[!C7Ƕ <Ӹ㒤0IE @#m,(ĴTh'1kHOlHGàWg%-kge|HyB5̙%X[מ5&VCԪYw5eQO=Xs^4H/% oh,>0Bl0^l4F4/gz*=Pt_PišʎQel_AsgRⴏgA+oGS GF,vGi_rQ0(n f`fE{11V'8`#sv1CT(WKFVO[|E7ok5P[C>C\aU-a,IqFI.,R]J2KuxEiJ}hn-8[k]@Z qP,].:4CGAc ơA0J>&5U  ^e ~bu9AtB1􃀣0!Ui rn8-о^(.nG)f]9=ÙScNp20#s.B;=#g>^#“{Uӕ%pN#YsZ j,"84:#M@,guk_*?zY{vڵX] qR&B؛ztHTXT% (E(Z(F8RtdYdY*95Qj08rDrD[ .ة T'DL Xi;g޲{~A+;CeT/#2b'H6Eά8a._̔ݞai r_0-vm nK)slgFaN0tc,SSth@ =5SOGBL[=z3۰0E8,kgX0YXhZ@:`sMn0W6*s9B]rwhЊDH= ,8T "~j/#\d* kPfSf{\ٱi!lgܶןS91NOprc#΅LPCY =G`I8.2}b sfH{ŪEk#qԣg TO .X$^}\~O~Z=@z])1T*ҭgL uPV҅!ϐdHS9u'S RGdXHf]˖ #j csVmcL)rzBmD c^Li>J^ӂAv>(mhY/Ci0"t H;L;7ϓ!|1fc0-Eg'89qLX9 A{2%S#KΆXzzxG f9 #t%>k< @Ns=Km ֵ+yyUmd&]$!J&.gG_>6E/WFv,1A2s -Z<_A{1sX roV'cEO#EONl-/t8U)a G+h6{th'*nk`]8\ G ߄ tڛ!9w|# 1z''bu0iMaKZlbC-jg!la4ts\1`2Զg̪^CnhY?9h_UXeW!zIX_ωвr(swIzz]TJN#խzjO,0:g`gHWhP,[07t$ر=daHBWҏ˚BON# We"ZXϐ b{|c.<4q1E7shPd  hg{dEНp3kSiI*<XC U@ @WǗRwA^X򝈷gR1j#}Kmل Y8RpJ;W[sNRb,ǣAڙ4- YiG6ZZ7T[A 5{+!@ ]U̞;~1#4;DC+! u{C=+'w7CvmjkANvDx 9qXC9h#m[%8 5lfJ,l_ m 1F."]TH9?zai|KRHU4}jԀz)?XWkK?zv@<\s42=k$Z 룗"T K5'L ] gAm`yXV0@[FdJGk8T詧3DOAQM+C# X62tÖC73}ϕHxh&hUW8|-*6jޒT{exI(3%&,C +&DMg2T$zjЉ`(m Yha)tԑJp>B00=Ȱ@Yѡ"C>fF1]0jUk2zQ^h-7" 0\TNȒ iL¢tl ɇWpC|78޷⼡0BTq){ut>HHzitӧRF8K"'YquW3+*Q5Ϟ>o4Ls(u>4F@6aS5Y̒{K'C8&"ؒ k56A{(v8 Rgz /}9CR 2ge)Ayy/cuXYVK H s( Ӯb 8vq^B:.H)k層?;SI9LbrZV!mŞVtP ,ã?F)Vk^]0caƸeϮzP⹤cx.pon#:h?3NP_1&nEssVp 'ֱE 8pdS:;tR4CEo@k#x !m[ްXs&2g1BŴ$xDY2 SJ@ l˫qq!c5D!P%)YTl1/\aGtybiH-z\ Yp -a^.J'ٳec`D i/6HϢ=0[Srg+%s7H!ceыp NpG&10 np"UW{p;)00s\Y o8 [uĭ}3M(XB8/IL Gpj=+~Ooe={ʹϳ1\U Ckg%J=9XgкDxTw$iCDum!0:H,>Rm _DYrŊK0a|[ th%HKk:6d{ =2L[Ƴ3v%0It A @:jK hX:G Ǻ)/VCNpX5iD|5_^UV  {ZHŚeyʮ!z:@ekUKT,b72dxBH+RFJpk)'j`- r$b{7q}^g7Ns0&9jкы Q%8bxk\mLmp`zV!ٗ'HL7DwIڰ>fI;>:|ghg2''!_x^W@XbOa7ŗk|X@Wk=вzevW3F\"8뒼W_ kYZ[H'}fR)ڷ ~ɔ[_8߿EЛQ yqGz t0E0Ae ,Vr./ٸaIژZH=(hdρ9並 uk༅_ pwty}#dYfss?"%+ǂںi_:b/B-:˩F^*?|%%8ϭ2wC K=+q)Pq$Y9**Mx>ț7b~׿>}yvOseR/Q,mK+hm'K ֶ d9]^-eJ`Ρ;T#v %vK[X\[BNB]IXD<6P펅 *UƢ9VHCU$ϒ16Vd3 ^#8o:e]V%O?u]?H?w}7gqR.?s?w͏WW|_Z5=^W||^x' ,exN;3{w{wo oxoƇ?aO>Ɠo|#oxwy^/ţ>ʫ^*^#p7?ǩw߽Ts-ⳟ,;;;|}{{{X<??̻.~|[n땯|%??>9TM[GOr˙[L }e>?̗%mj˿Kyϧ>)~~ '>]wm㼸馛x衇1z!nkriʹdO}xs%^s뭷^ctMLS ׮>{~7ykx׻% iw=y}kyk;Q~|~zx̛&666 ozٟΫ_j|fJ~x5;d2Moz[[[E\c'馛x?c5;wK4DM4&IDM4h&tߵ|o}+FgyG?xoy[fc63 u{wy'v|;3o=wodk^/;hGM|׆77~|s(;;;կ~b%nfЇ>|>{WOFM\5:&kCDxk_;Nopw\ʝ~A~'Nn nD|C󶷽wگL&>.lƗe~臚 &A7?:>Os%7y?wq/zы&u4 Wn^6s:DM4A7DMZq?*^eF7ʩQE hov=z@ǎu+?o Rإh 55N@DO>GRP^xzN]DP. e@{$BI?'Mi"-]*6kv3xd#Sވ0NTeE hov0a{xʖ. }3lg $0E#,H( #hhի D*[8EA8o"Rpz{=w|~;O^3w]v1Q<@ȂqvnsOLM 8yHeśS5MoepG']D/xr)[%a׮sd"A`28yHeKjj*;[+ "G|יq:;߲͝J"(Y2U@Np0Fg_0!RْMJZK"Lis~+&<(w-GL\D@P. l@k3< ҏۛ^B%--ks7NϹEn(\dA؀`߾}8a7Uր<hlZ^rt .z{=]." (Y6Å^ȴiؾ};hx[%!>ÇX;RD%??;>Pr>J5_3v(\dA؀NNN&$qqq6VQQ $##Ď1-WCn:^{A2ubB e{v_ؾcLh&*~IMM5\Z^e233y7)++C(4M۪ jƌ=C<#Fxs>T@Ӟx}o޼Y eg{ͭ.5#K"KyN׭.2F~:R{"~CӔKKЄO"50Ν5o1NVi(8ƌ͛a\ȵEzsgƝ;˒.Q`ҢQ<@ȂC͒1膂;lO?5I"-?ŸsQMxpɃ,ڳs;D/xr)ڛ%a.=* kQ_\ F4ߏ)DĀ{y˼]:Gtۻ%ߔD/xr)[3 A?91i߁oYPcyG9uED\Aŵ[glֳDG%t5Q(\dAʀLBIIz8vMfA h(3֖D]ҥr{ 4/ ѻcQ<@Ȃ͒<nRpt#GBE|wb ek8v@o:?fÛ?ڞzG"(Y2YgݬӟGl)[S)Nuy[ˍHU]^*c(EP. e@{$Y1lee:U6?QQ?|6ɗ3yODi  E haKB0q~ѩS=8z^4?}IOeѝ " RKDRI;|4Г/1lYmTl cZYA$F$o Ł(\dAʀvIѣݡłcGHOO{u%=q*4GQpO|׼Lth4K,[t7y_E hoshjoýQ4VcF4MJP. e@{$8|ys8: /ݗ󦋏h<`p;Z޹cSBE h_{ݾݽ\ 5Ke8q">~QO]|DT“}Nr'7P]ͫ׼ʄ&@JP. e@$ sυ"(,t}= ҧ蓡M#wJi)esfߦڀ(%(\dA耮gkM$Džsw\7jk[ߛ.^a1B?E7xQF5[Nqsݮo o[.PY[ @=]x4E xz5_))'q53Up΅vri'O1rHvE=Bdr!.m!(\dA؀عs'ӦMo!77j,X@ff&4e9.3{l`JֵX,ܤ$rf 7O<ߣ.n.+(U?⦊'B qi,--Ͱ}\Z^e233y7)+MӶjzj^kl̘1>?Y@23]{UUg㏚'x)]\fic2hvsmoӲҎ|p."(\Z"==h&{+'ޯW%!ǠA`I?ݛub<mGJT .FDq,UIj畂㦛 1ŗe]3qD#:b :wrk%C ;QD*~Dq,Hо* #h×_Ţ_Ti=We]3i$.#:EޔfӻnHŏ(.xr)W3 sgسGs[xuȑ{FiYpifC8G<%+K&/.EQ<@ȂM?M-> azP~2VtiUohƃKd榙t $JVW7hf"(Y2}Y\}jUIkwx6 |{{E4&-ČM3s͡Vc9Bp`x(.xr)ڗ%!С+#* >.8>+|i.}!W܄s oxQ\D" R/KB>} !Aih`SG\Z:81#Wq^y0B@T"(Y2}Y~JСIk׶^:gx|9BV~7–-^s4>YYNY4JWl~7\(.xr)%!>dQfφaHKnիz06i07N׏9?r[M"/$0:{D*~Dq,Hо. 8tVV{8ܳy!#%sW6i`I`ոUا5b?P^uR ֭pi[F=ٳ}_lQ8yR9TW75 ~Q_f6 71puꚦȷ0uT, dg;oɉ׶mkPYkYxSQgSXlq翟 KBG|9eaa, ~>Fp%p㢋 e6֏4FIV `Q|6~t9N޳õ`u:tOaҿv<J t+̞gߣ[oG]>>M" ]v ki$}\p°HʖEDq񙇦烺֒,f)<\??2RcyH( u`V=$??_흺L}/AcOWn?'^kWL}G\>ZAآA⒟Oj`rȵۦM)mj-hFs%'GHоIؘcp3y@-MxRxV[EE_r˖ӏ{G EyCOU?$:#5:<,*J QEp?uun=VWo٬b׮nj꙯3폒w_\~[cd)~욝G}iq+שb|~aNPW_$/ Vlˆ؁m\&AAȠ f6뿆zV+if{:F Nvd$iж]vv6ihhHоIؘD}vee0"jɥ^Ͻ_>&q+NsKCݺ5,*AXt&Aq1BiiGe|m55CӇA mC83(\ AA=A I_%q3y@R@s|1$uHb߾#&TL&jᇥ[al|d),;{H.!e@$tp-zz'y5Q (hbr[Y1=Y9n%ݢU(_WNP|vnIЇ?L~A#H.!To:jed4}MX]NqYfv34b`2=)~1Q<@ȂrJ>ƏϿ, ?vtDKk{ kpqYD]]q(\dAÆ co7b37mEqȑ#\;ZvŐnCίmj)?SsA* $G}͍.a׿~G"]*q]\:὆tRjG5j0,~EP. |@O<?ܰ_edff۠pNNN7+XΆ֗^ʲx ~-ϻ~K+Q%Xe䒟o>P.-/۲e 曔!meΜ9Z^ &h&MjXnӢEvᅚfф#Z/꽻+,^ݮB!Ff3>ٷo3f`ڴi ˍ( df41G~<"kw7@ E]I?9.g~i(.xr%9LL&xeIGfA-]us LɻҒJl7ĺtzf"(Y2( = =dLɈOG`yqTWW{sN.["?E h$tƔ)u:gb~msmmyLz_Mk/-h"//z&D" Rg:#&} ?N4q_f:/5oȪXA]QQWD4;LQ<@ȂmdIؘn#9rDyF "ctwW׽]\} E h#K3Y0K/>n7p Mxvճ<),Aߵ#eKb}zw ߹[GQ<@ȂmtIMhu|9/M) %BX>v9,[jRXRݿHŏ(.xr)ZA~~> t~շނtZRoO SI$ JԪ8vܵCGQ<@Ȃ-BIQp}6deAr~akOkmܙq'&C? WC~ IDAT ox(.xr)Zpᅰf \z)6uVF>gLO֌_Cj|UW9LUچHŏ(.xr)Ẑ#1~{ؿsz}̛ۢGzX[;Tz_5iվxoxQ\D" R %ak,ڳSUWWq…<:t+B1-M0ɇ¶mgeˉ;zpx.DQ<@Ȃ{אJ3m4oċ'z~7e AX }BG*] EP.Ӏ>x V0dlW1w6ÃKl23sN_?@R^ԍF6@]NLԇN!.N4Rծ* rG:wEP.`4m+~ cРAk.l“O>I>}<=ٳ#'^]*[7YxB uM\̐?\{|XY~+ss`wkH}a:,}G ->;~W4:D ʽKW^ixML4Mo+k?FxP8_לuǟk2A^ÙKI>x H{4_X5!(7g?0 Q<@Ȃb0gƎ ԩSy衇\F,\[nѣG3aIII+ [Ckv&-M3 a阥 Lo6deߥU#'[xe%TUnl1 yhC>Kjel3&垼x,ToL&8|8Ԇh}ukӭ[׶^?mc%%%L4jZ=tW_}5ݺu!e( lu=|:w⻱ѯS?C\Z# W}\oiglC[Nm1kkG}kkQSKMڏHer9h4*,YBPPCukd4hyyy$''7{n,X@HH6QFCJJ tDz dٶxz,[Jάyh X#~wqe]vɧύz7^H yru qc6}{w]j_eйll=7s) ;wfFn,8®]ٜ}v䐐/''^8x0x}{fil!.. gӽ{9 ~54!:Z_v`6i9/ M׮i;CT 4!2R_vH6:QX/3-?Zw'O{x6;""tl()!<\߉ED|YQQ6qqi`vXgFYYKs շWRMTT 25\\(oJB &[oQRR# Hql[P4^0ĘDҳP'wʖeTVaP\{_#(\dA]nh=ӉeU*z6 +[Gω])\|(.xruv?S~B%HplU803 { w](.xr), ? ϯ~H]N!ow\-[NdVi#|3ZGQ<@ȂGF?c6YxBt=չU\-[EP. %anq.,Z[-SO^bOՔfBhC](.xr)%aIu #>AG>p*ps;wdu7d/@P. e@$qgSÍolHG[.]ǿ o۱,HGTdW&-Īߩ?s@E*8r)SNuEa= u'E Tlr~Μ_~$F$_="m4>>zvş"(Y2M1iA2qDL!RњƉO`gW](.xr)6L?|olu]TV!gH!-W5 UH-Hߟ])~'xr):c BR_S^6IܐZ}HGk.>nw\(.xr)ɓD^ TnW\+k^!28y#( TpR]A2z5?gȰ_(\dAʀs*/FCcƍ38+6hHO}vDq,HtW8]Ř~c\ZGÙKZ ?-$0*'}v(Dq,HUUUD^)DIV Mx9E9L0H&_7D*8~0ZF}D8Y.F!(\dA؋%=zgyV+f"(H" k龥,ٷ1=yxn+RqOSWTG¨Bb$E0|&ye۷o.Ju:[7hiV>dш"(Y"{ҥK<ƌCVV3gl<,X+1G~*c e'rNNN˦oΞ={u(7}[=e]{WqI"~1jK˶lBff&o&eeei[5ٿM0A0a-q,cs'NhѯFk ж.ݻwk=_eM2mEMS.-nB- {IQQ_n9~ZH1{MT)/Hߎ}^ ">+'9Ip`nu7ZsQ\D" B qK{ !(.eԝsyUU4@s ~."oM?sl̷Xb4xr)= AfF'kۘu6%JtSD*8Ҩ/G0.(.xr){v#n㭟KEYPV3cil'Z]DAQ<@Ȃ}= ."$)J9T,"$݇rA4 םRL&RL1E"(\dAʀ>&I?֠pAGѓ֧r?~gG V4z:w"ooi"Q\D" RtЁchZ/_9qpC޴{1l23Q^u18 JW'. Q<@ȂRIؘ^: ssNVe`ׁno(8lU6r^̆ri(\dAʀn$t=wnfC Gy!at1Cc ui Q<@ȂZIؘnt#06E8ރ+R𺋿 Qb= ui Q<@ȂVI 0:ul7s<.,84Mc߃j5[I)"-ʥ9xr)1=ֶ|ςȌ#/+ƒj!dC]B4GP. e@R:X{l-{rؽ⯂Úc%/M;\̡Ϳu"-ʥ9xr)ڕ9w 5kYůUZ;ލNv'2-0WQ.,׿>|x宖k׀ 94%+J菂#<*6Wuy)O|'Q<@Ȃ|rz5WKbvGtם=ݺxk(W uN*a.\#(Y0<8Û<ΝҥK;jIc\M?t׉#ow^q_{joz~+Q<@ȂݣG.]Byy9=999|wM֩`dff@NNNObǘVL+RhXf{D sy뺺 h-Ulg;E DiHEfTriyٖ-[7ߤ 4m&0ÇolĈ.;/>ҵZ9K!|cG~7oh}gmvmZYK7kz\ڋri(\Z"==h&~K,iՒ0(zn<(z,lU6 ߆uo|QpN,{g۩!Q\bNdvHKT(Ehma1d:w"yyCo[D$J4Fma-*PA҆DĮ`v R٢\#(Y2* N0'{ZoRBQfE_Q!ו.t!<&jb3\EE JBǙ𔐮!tԕRW\GbNd" 2 "DyI$}1g$)??_<"(Y2* i (6Nc:iL&+EJz{=Źt Hth]D*8sDq,HЭyy6"9E J}9UD\#(\dAʀn$UA"͂R.EP. e@V/@Xw6"9E J£GH Q\D" Rtk%ct Q\D" Rtk%#;d Q\D" G}c=lHX1B " V\#(\dAȀ>vg&88LRIXY[Iem߆7@C8GQ<@Ȃ}1k,BCC:u*iii̛7:-+ Q\D" t=XtiرcGIIIuY`ddd) iXr#2P..iii-[ɛoIYY"a4m+h3Nii)'OdMܸ;wnu2vepgGrxtDQ\Dsg6ZÏ[⥗^b̟?iu->*8sDq,ЭRIx-R\#(\dAʀn$4ZC8GQ<@ȂLB#ZYP9E f"͂R.EP. e@VMf, ~sP.EP. e@;+ 5M8~sP.EP. e@;+ 뫩W Q\D" RT)_n Q\D" RШP.EP. e@;+ h Q\D" R1a1~uP.EP. e@;+ :P.EP. e@ZPKsD" R* \#(\dAʀV%ssDq,HJ’S]W"9E hU:G8GQ<@ȂZID*8sDq,HЪ$trq(.xr)0*|Hrq(.xr)aaEC8GQ<@Ȃ?G}ѣGs&5i*@FF?%sDqKK!jL°aظqcM&S?[ -sVɩIDATDqKKЁF 8p'6Yvwc6IOO_eɒ%M^MlTAPSSl Q\D" &MӶi/¼yxoٻw/?p}cǎ>lL_Q.EP.g~ȹ;vr&BjO]v;h-B+BtKXV^:t@LL a.cǎlذ0+W2|ٳ'>![nwߥ$^|EC<sM7qUWSOzj~i.̘1c s4s=gO?Ĝ9sꫯػw/9rG}.]PQQY 8!K–Xp!r 3gΤ'j7\s >}W6c̘1?u7`ԨQFk`6]꒑} &ƿndРA̘1{#F7nꫯfʔ) SJЇ")) nݺQPP`X|G|͆:| >AO޽ 2d˖-c֬Y<ٳ /iӦ}v8`ӛ(o*ΝKzz: 3>/U@'''7GrrF0ydVk2nV.]ʦM};wdڴi|7 44r5mJKK d߾} 0Pٳg3Ķm q`ivps}vZIII1j㏳k.&NȔ)S6eܹ̘1aÆ2m4C<-[ƒ%K&<<^z Џׯ_OϞ= s`TUUqwرz.C}̙øq u6l/"VѣB BR q( OV( AQdŊLTTcBZT@+'yK={6?ۤ)NS( AQG B!(* BPT@+ V( AQP(ZP(EBP hB B!(* BPT@+ V( A wIENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/ker.py0000644000175000017500000000030412253362407022027 0ustar georgeskgeorgesk# Kelvin functions ker_n(x) and kei_n(x) on the real line for n=0,2 f0 = lambda x: ker(0,x) f1 = lambda x: kei(0,x) f2 = lambda x: ker(2,x) f3 = lambda x: kei(2,x) plot([f0,f1,f2,f3],[0,5],[-1,4])sympy-0.7.4.1/doc/src/modules/mpmath/plots/ellipe.py0000644000175000017500000000033712253362407022526 0ustar georgeskgeorgesk# Elliptic integral E(z,m) for some different m f1 = lambda z: ellipe(z,-2) f2 = lambda z: ellipe(z,-1) f3 = lambda z: ellipe(z,0) f4 = lambda z: ellipe(z,1) f5 = lambda z: ellipe(z,2) plot([f1,f2,f3,f4,f5], [0,pi], [0,4]) sympy-0.7.4.1/doc/src/modules/mpmath/plots/hankel2_c.png0000644000175000017500000010600012253362407023230 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxknKU&^Z{B$mcԴI}N6*D4ba 4[Hiv<9ibhi`6 {une8?6Fը+QUgUE!1(2=%SxpotDFeQF}#0(2-#@2(ܣ2(2=*#@2(ܣ2(2=*#@2(ܣ2(2=*#@2(ܣ2(2=*#@2(ܣ2(2=*#@2(ܣ2(2=*#@2(ܣ2(2=*#@2(ܣ2(2=*#@2(ܣ2(2=*#@2(ܣ2(2=*#@2(ܣ2FG^/}Kx[ނs|4(\0tCyk{{X.u7:J2A7կ~5~ fիWQeQ.ݐ}xߎ׽uxކw|w۾_a2`GWp Td#n+Wym7{|^{ެ~(xܺ}[wsOe<x;ށO?{ x97 ` {y&=0 <{RÓ~'kS|c_@Y i=G>W|c`3ADУ7/>1W9 +mNx|el5ŵc!'7Zu[&R}'M,7SY;AӿQݐW?ؼ&Ϭ76qH2lw[o\OgoPՌ֘zS:q*%@!epfyݭepVPձaK~zzAk4V5fZUɎM+x3J`-ވ曆Z6Q؄}]~`ȀYݕ,29.D[;3YT[¯Z03@,ժSi-+V>Sj^c>^Aoڝ!ˀ 7!PYuyJBeb;J}E`TQ.KΆ`BJa偖8ԬduHzۄQ^ 6{:6cRƠV)faČ#)ʯ2? ԍnQ%p#@ߕԕY{e&fdUU3`X̨Bv:bZ"yZa0,*S|DdžLpԊJ°2&׊Td-/2:y{/T@6LnTM pڱ9K a.݌ T+~]mWM1VgjeJ;J%#@(l3h<`k1.m6fiF`hZ+_視jˆ!jk;lQ5AhxUؠzXT=9iʖP~qcp٭^V9 h[[`D(#@(~!6 :f-lsb,u2Umךj׈f,C?*޴Ao>,ƛJ_.)#qsV {z+96u.XFőd%{6ۮa!0C9sA#\|s-'/Fhs `Z(g;vZ݂&{-?;Z0tf zb2'Z9T ~[׈JFQbQEa!­- UV9f>X6[|ĢE6$ӹDmT[(2syakؚJ$no;(#@ߍĹ8,:ɠQԳkr$ Ţ,7; |yAΚحv>0~4EG|CC a;3]BoB% ĻbaOb AK;J,Xflѡ(p:%j֠kPMsm R5N\kwnX͐[jf@uDc@--dز7+(ބQ6RQ, ms̀qLp;#:YU76F=j*[=h+[ !#@7OO|/x p5uu A ):-]Rlcj7'e0Lh.8(223hlc` BlLb|nFEc3Mt+c[ef!#@7ӟ4o{?>0_qŵO{)VX1zGbe@zBTOoV_3~k8am߾i]Mm<Z+'8ͦ=eHYytXT?Q: yGp~~xqM_X_x!dє^τ!^Z [11]cdZ~a瑄۬:^jFּ1 [h0[t606F5VoUPߴV N8} O+ɩ )#@7 ox7㓟$5뻾 pp2uquCCزiɫ-U 6hj}@Oo cajce۵TPܣM sLe*< T#F|^1VQM(~㭄MoٳQh2r/fx%>_g.ݐﯮ鋠 틱Šsl(LZ*uNnj2 ^YUN ֱҊJ يUⓍ7FqրfAo5 Xi{6v؅RidI( LF iOAm`HPp1 *SªVh&S<ϝ{nAe%V܆ʷp*;%ס'%qo^KKXPnףzga0\b3 L=Y*Tfnpw -7K8/FVOѰ*U3l!~/M-&p}2``8nn1eSڳyo7GF+,p,֤Eh, RY.'u [tJ3TOA:~mx s;PGWH S*he+QNL5̤s42P6F@V퇛P2譹{3CV[PWaEbĪ9^rԦehr/ }R5|Qt`޶RE.2Rm[CYMB PQU6@̪`t!y ж]Q#K3Vc)ޕ6yQTkaUIzb;JxjVQ^0K4&XP}gme@B@0P+v-ҀrM &4u~oU(`v.{tf{t.] IX44 Tr!U"HF 8tCX܀*USfM=vzbS rUct*7TKEZ̵j )ȭ]h Q,MJq۪ΎJ1QQaiPmۛ$m5r}]lzG^6s"g Dz"yW~B{'ea]^K!RpneqUiuk̵0 !}ؔZ3V_P)qM /Q 챣3H9$o<10)TFIR/Z.,QbVzXýRt͗3;nT֊Ҽ-lBy"wD왫ZJ[MS$8)5-,CߺZU7lQler)uP*Axg(?AE`"@X0RVá2ni+[FN2DveWcR`7GQXK{"sa*{`lhHf56m MD9?ĦPRu\1k}ސCԹxbJ/(NJܯ+7eH58˗#n[,2<ţݐ~8==G>|_Ə؏Gc|? = P8W`7IcPrf}GG8#;zhDGМGJXfWuj?hnZmAMSXFQι}Y|柞s(#@77~7x&O|>(`~^38As7i"®.ձAyo8UT m@]7Bp+E@BCVlt0KpVjhܚ ;3#(;*f՛fpq5{a3iJ=A y?p|l/UF zիW*3Z7Pbp(_7!u}[ՂZla <(V!Hܰv̢_nk0}pKH|ـ-ViD/i<-n"m.m_hz Ͳe&o.d?h_3Pk[tͭA+ĥE^TT ,n ͒M?N/Znlv0*KJ-S%vn I(jj)rؗ]6=u,90twHt: s?k):ҪZ) -9? tU&0n5ur!CZ,./6V;Ti]kNm [0ʐ#޿ax*auz2@sazx7z2"tt\J Miɢ>iD֦|5Zyް$\22-[( DM,ֻfnΚK|+*Et;X:(IFQ|Ak Gs 2ݪf %Xj2`9;.ˋCw IDAT"N.u\FKellV v6@nI-X|ČBT0q -`X --SB5 =4"uwy-X?ekԱ EU,Sw}PQ(yW\Dt-tx4o< S @L P M ZٚD4ڊa Ͳ Υ*22twгQ A}MawtL+ ((u@:lM1ouP[2kvoP hfFŠBAqxTU+;:m08eŜmGX ʌāAAa;0šd%j)EUrXy \Эj"dȻ]}S}X#zښ$I-."Ju*3GE]-ɇ[3;YDA"8do;pCަB˲g&x(#@߅ht"lie{W f$ʕQXTʭ8jfdtV JV(h863¸s;4sVofJ:l9?e|(_!Fj%i fA'0fLHVN-tK4wAtVx ظ֣9X2 8N%J+(,AvG2RYbE2+d .@ ۳$4}DoxKeGcS`7AYB RTm4[9(~PBhc=A MFU@KzmM`.x; #cOU^AŋA_ ;+p&$Uh 烞_TS};[lvh{qljz̈a[ ]B3Z鶢Q00H Zk@9[]4I :; s=:jXX`HFȾWTITU%aO :23n;(f]qnŦKMM*zm54fꭅUC(=nzԾ57tܨpc7"; ыhJؒüs`rk*H(=̜y/ߪ W.vIA\AE6 dͨMV!{M՚`6n`Z3PG}r=d&#@GzGEsAOSԁD@ˢFIiJQªN1hoڡ]XNe-fCR|H emh`?Qc ";:'˹fЭy9j&,¯^QFYN’A@f#_)Q%mwNYbZRQ(s8m&KBz%ۂey(bԠTk sʜwB?`:Ivj~ֈ /#@( 6<r$OIڽnoSdMk ʤ@9wrڜRfe, } lRkj13g FZ 6N68v]yq4'W)hW a)#@7//|>(~W~@̂4UQ VG{qi&SYY2* i.T]ԣhXhXp3۴Q5aMX1CӍT+Ŧh:tCF#tcoຝ(nj$sedʳsj,b)}k׾O%5Ѯ0q@D FfCTOh5o煶%=0}[y (,E?X@CQ F~$ZZ}I l0lu6: 5p:x'>D!?4"t˃bѱ!y(#@ox'ӹ/%/E^06{ɬY7Z9 \NB"mDlҔ=Fh)6{Czm=P%4Yc"aۜUM&bhBTnv:s3ÿe(F('㰿!Z~CQF~~!]o=3_vő %W r'DHR^;qO.<^]NXcYSDɬ"`98uLW 7R6,qTcj?: 8X`ΗzZ訯%@.Bs>/8m=XeI̛mTbVaU%?{U .ݐ?#<y>};;p0ĉ @B [4u(G %hp&DI1 -etv\#G+=gJ J%UYh/mҲ7pb)lˑ.k la.L!熝eKXM(;ؕp?M#&SFnFo4ZN S3H@˙&$ q8W,%5BiG32́}4l _x51hRC[V/msZY pܞ_hBdz($cfl)`;0!Ɣس鈆l PCNZ~ 5WGp%Bl,'؃@SȬ'#(ۣ.#Hm ӼqdP\f$ଷI`ϜD`=tj;CQrv6sT\@5ԴWzʮ_*ٓCǁyC@VzeA@x`L칱ת@z873fw9!"/PK5k=xK|$,MW~ Ȼ9gD8%Y`3(0@ pǢӰ@9̅Y#30PO 79@q#@wL Dh Ζ4BI%P/dвG-|%Λ>!ɥ|9VfбaTjDZ@ig9YFY~s0M&7S;#@ :gТK`Y8*`K30/s^OAX1 3bv)f@7ht34 }/hޘ850is8=PsbJ9:1aNf!ZCh"8vh񺛠\$xQ2#s BhAǨHۆ~ӧ`ͱW5jB=mDPV͊[ YFU.1x77 V`Q!g N݉j:sԐ@" TGdBAhك 8czu "pNz=FG@{9@sF7#$ ̝g}lf/q>wK8~i0aL3@8Db{ Uh$+!aU:C:Z\:XDPFYv X*=F36--! /+zWa9f|!y>ڢ+CQn_ I]Wg@,kBQ +[ƜRぢ|^F@N6p<. {y#N; qٶkp.-ueC£g:Ġa\O"[UҏG6b~ ;;7 /yKƐ;0{&p6X1gXsx@^ t!PvKœF^( Nj%ysKxpn379=t8d`߁&#:c@H;4YB%kr~&r,[W=K#Wv.w8PSC94bȓpXtpK`qFԼB/p >r"s6KHMfԝf<Y5r4Tlַܷ{wG={{'#POnπ~ `Soyu,@wn%$הL#u /wS[{S#]p5 |nj:r0L^^ԁ9D ,(,AzM\h1Fx1x~-:XK81hltU`5dŞjݵ(c?x;p'[+x>wr ggsOVLgǒ0YƙUC@*]#b`L'ϱ!,&[5x' :ﹱ3v:ӈAhSKP Ce87;+px3*=Wpp1= 0Rl%88F Xq6sV\c—KdCWPa:$J\SڒNI %i~qDٖV}D hr4e/>~kyf_땷e*p*_9ŗOw֜yȞUj#̟8Q\kG;8 ?s8п[ѽ=0.M1xΠ8%c- XqܳxRhbZ#.O|=3)=;%|7;Ƽ)+Ǡ&* <;sޚqFIiD)<@OYy IDAT Pf \0}Xϭ FHɨ5B*;Q,̑"+ŠIshN`(5@O$>੧ !=iyLS+3gׇ@_s s @@87m%/a2~Y= M=ҡgy_=p1#`vz`:&ΏżS`$kqH/O~ ͩ7qp0'O@8# ͡7D;oL ?Q`+N.i+AYhiF` :m0qNAq\ڠ+{G2y8AeY7ʉ|ϲ?ʌ=Lw ۿ|_8%So@_O1e{")L9! 4_By ;P!P'I"(Yhtca&_t@M] :xS`L.4=I&y2& ? Mw XY݄p__s)0 9Fzp8'SMbdw9Mi;BYuu^5k]/; )IO8f ً%:J{'8lYmEdIIMv~O3@q" tI} 23K ݭɃQ )pu2^2.@{_'EK8 O쒫1Ɯ’*V50'@VϮ>YPG RltADy¼ -K6-sgFl #@gox7]ܝc1 l[{ -n @ DjRO~2uƒb- Xtd݊h#o3=yxѷy9wɛ/qcr؟9LB[+yA9d.;+!Ej. eafѲe{mb6ovhȠMowԧ< ੧k^K^|_qgz bu1&أG`<A=XAk, 4_f+^nރ=hutq0Oɜ+ae;tĈɣf/"Q68{Wb4ᐉCq9HT83֕x;y׿GߏxG~Kx>| > Cai# ?8 qǸ]up ¦FGz0%|k>P^~~#z\&ʫ҉VhA8y./q`1)p82Θp̸2н pSpCx 1z?Sk3d 7؟KW8`e o TT0͑+0PuMOCk|9p:} G? "|ox77x7߷w%BqtTǤ lʏk`)1\_')J炿PhyCuJ4\8~Pq-h֫ɶ0 NX4?11n5 ay1{_w%??9yUp~|R^3ٲEOX<]f}7<9:NKYPK`NrϬL" -\5}_)^x8NKO~?!ؼT!3EN2P)z>񸏠+i0D6yDN>bo4֜9 +3tx5'7; pp <3 _eLOq~qo q5ι }gm:t P<:9]X59kinŘV3˂WDEbF;>%/|/`<+/9W\}CY.r :tdgj^o*irc VrrLZug5ymG]X\}]s0Nϲ6[̸ ๾3`|CFw X_c)nq =_ncs„Q1gHFrk_"{3 x~C ړ#cIýqa}jmhP5e$5@dz>=:4.! Ŝ5aYHa#W,ɞ#ǚA8{9^ѡg0b9;=f b=N8f1؛5pdƳ)Bwޕp++9.dW@?Y?;üQٟc]znHGi.}cPhQs1(ýA0Xt9a jQQc77aW`m[t.څ!ѶGs ̔ca5Eb,2s(8ORWvz9#i [n̄38p q̌; &mL< p݁g&a8>ng9 3큉c?Ն %9LMiM񊴉YtFG54H4$C6,}8K!`D(-@ďȏ|&`4i/2TfB⿸i3FuHͤ@s SGG2;'wB(0uhP2{h3wg=@ 'ܡs=xNI XP&{w8q 8`9f̞Y\ȗ0hb)W=fMed˃A*|*1#386A^#7s/|~}%?g `-syA0( P;Od69C~bȎM P^(2l QyqHFh鵁D>g8_] spXm:&ׅUS:2o{ k . =gwa6UfTMˉ1Yu6P'P'N͎%ptsAK;4GJ0KUAsK`nYX́* +-@կ,:.$Unvr,%ePPgMAKsd0o=f1(A(9'Gl[!q=ΘpḮstg9@g5bρGo560o02@CHSZf %Qmd+M Ig彡;%Kb;ĵezP`-ҰtB;WF0wm }`jЖG_ٟ#1AHKYBɪ ս9밐35$-P^E#@g.$^* 6ۅаA[T@s2o ˤju\g18vMf Q>lyL~8CEfLe"])3^ڠcĝ4sFq̨㺽ɜS{ qNhQh"b3jze/{cMX9[~.p{$#@(y.\<'8QL$)UjULx !HǪt֤-t8M# lHz@dϊ]@V8*U=02",}5j^gFd0Iu*SGd ֬L\3+ꅟmވˉA؟SAol% `IDPS :G +HE0h_diYH>ٽjܹ eP20Іi݉(; c(nvMZ %y*PW!\rg*~8s yַ$@K-n> 0˽avLϥջA(2{'F͜*]IG K]DA#"5D*T'I޷UU9J PML"1>J0b%\#`4F `(iIl U 5EKBssqk)9NkϽs5o5捏 >cy){]CM7`㠵'CKOzQh8߻لBwESYhTZВݻwc"mÆ X~=y߿g ,7w!h iW% á^9@-80Nd5gB9 c0#ucsYpp*ʌAkxHpG2; -t0Gpn =&7J_LtJxXpclBTNzڵعsIGp}a֭"s X=U6uP^%8UICIj7^һuٸbi$$F M:OɀqQ^>#\_Ʌ Ɍtu+#uRcaa,S+\ JRkj~ܹ2NΌ]w*@'dK/uJo˛e8{ |hdy+z{D6H4>;BQڎFAhR›j1$ju@G #(wǢܩgޠ;H|R-OZF|4>.^;椎.CҊmjXX.,1V Ms6nԤ@#ׂ3ҒTMg+vcO#kזDisɳ8e8{2,_f,-yڣ3<򞄣sT''pR sgI!~RNtJjs9' Ix9&K l} QmO IDAToHpt(/v,Ht̷E+; GBۡv|D}-&c*a ;~3Ij#S )U+"=QEs>W1hs c@N=(+TXiSoPw8"9;i W1a÷ ˱gޠqˡ \ѝ9,Ǵplh3g I0ΠZ"qRisA=;uP}=1BI k Xt|sa'D#1KI:;|$cW6mf1gesEzrh=NdZ]є̬ˑĆD[%Oŭ|;CNB ξĔDŭzrV6b"vzQT,cq<PD)$Z-@u[=Y AgqzMɬltRӖ| qHBEܡa$) h3]cmkk: %쨵'ߤVHRjI)>?)側U2ɒ׏gV3UZ7[xؘ\f2ԡFwkpxVx݅5سm֨4D8+8Gj95t)"\t2 >=Owp R)Xw#98TpPgWQMH3!V;뵕7P:S )wS В&4gq혽 c;6j-p@iB=sY?N9:)yζ\Ҳ<cIN9sh oHš06]9!O6Y騌*5\)vf=(-򠸱]Ƿ31`QlgPp: ^y<^8|-aY d\BiaXt [UlB#!B,P gkB]!g,$!4=eRJR8Dr( ully/cSSnxu Y?/Yy&{;!@;x3b鑊;'#|M㠵hS均n' #Րu@c@`V_Y>qn &+QSH@Ad2Qf=0%O'ynM0-/ oI`<]P&E'Rʤq}{: -!6gK"C[Q+]<ε.t>6d!&ГVy sCBKj%2#/FtNp*N D $c;NqT!S&-4N!hs ylZϦĜ(\gɴ !,nB3jvT91F˸ =A l*)`tPHa8+6 =!k?% y le+>K ġZ;.!^2v2 ;;H"@ ca9 t4;C0#@I=]>WҼD(h 49ʼBTK̙8T;SzL*]o?8v)Sհ5,!`wck\B9fXbQǠ%URR{ Ä1ç'vE#oRJ8h stϘ rm%0*].&_5]eG8Ù %Oz衇pYgGqIڃV~4%Xr֦ 0fD"0+ˍM}o =\)"lҹAkB]C,W]}jWtCNh%"V5tV$5L|Ғݻwc"mxqw{Uw׋A{keNhcjRSSOt53YXD4[H˘{QÐĠ5YI&W~%[穾GwR}Q9%%bwPtFnҒkךߏ㪫?|]t`IX>AT}=쉜Œ2%hg75P-.*58"l!I:<ʏ!KH훯?$9i![eZ k#`hG֭úu/B07 ac08Zu)RC-Lڬi[S#HhOdVoy0V4<|+msjz ALYvxzg!.1f`%ɵ_,ۨCJˎ#:1Y{>0.hxGR =)y:SR dMyP%=&/CIs<#3q'FETC\[\<|_b)$ooB%>_4 )gl+:H6?C,%=QCc2AJ#Kqx͖&uTB#2;H~K@+(6aD+O0Rz蒎5{ 8;+nR -ب2iDVp6TzB#o|UxU_, E1gϹrQ00+IW+IJ_V5]DW> lȈ2a-h(ڢ<1ǘZQי΃_[ D$d!@ b :ET)3@MI0qj,9w!&VR^_=eンhrO>踏9tuQSUIZqY\H klXJߜgjhdו_%"6V$gX*w:H 78Su#F錼FGQJyVE{32VREi ZPV^:(㔂V>"$GBC5T h2p%]7WU_{Ah|zkԯP9yNy#hƬJiJ/tyew)8V gxyC20, R0TfEq%y6'p'dlxҡ R&@٫`ʺ6GNd="p =LX^41(*cwS\0VUu/ [cYz\%JQ$.9YeLH?8sJ,+UxhжI' OM7(pd Qeǽ;Ht5#DvʙwNBɑ;!VW@pREDyW 滔D19찊x5yW-GGVK Dy[LjRU񥀏UÎ^y:JҡMl̰LZQ2xMU4ƛkԩīݙRƙ5d#^UC5۪ONq /䵞9nCxEQ=IY%۝#DjewQG(9 T *Tz 7ƮZdsՐuYjNI8R:x ?PCE|e6,!eهǐ,K_QOgi% RqҐw7ݠ`+ UPz*i9NfX?G3 i_lUdD*`=C?@BhT3u%R֤ЩJ=c/>s\`N廚iBH1:ETΛJ*@OH#.0 vr@T79 ")qO:܏PckoYz*LXh{C9Y0Gi"{TcJV @(Cm9b'ܛqc ì۫x5ّ1x7ͫ<Ņ#ا7m"Je ߮ 8D@}Jfr#MWTzBJU$pcNP'*21S`=hx#ZVj[sKJkNGgy}I|j(=*>^6%kMz+7]I&S1lwݵJ*@OLmvkE[Mqg_kyM7+ O_ݴhn7>ҫaG.k`sgWP u#8:"pth4(2T"aYTl۸O׮d BɸɃaa}t =|)+FǨ(C@y`WS#8όkgʰRG's˯fd;\J3H% J섀qN0zjW~JtI7nc[-X eQVfdc(Tg3*LT @)T̷!LD%xh1 (UX2]3RtoiCP??OTC)jFz^0>FݠZf. "pp<-@ġ8UJt&kI3)'}R'3)kE|)%]ER]tP^ m|8W);Ƃ2y%U826 La'DI;kMEi?Dhf(>/8ci/>F\d*S.0"$> Ђ*@m6<8p7bʕ88KPޅ4~Q_zBdM{6#&C5((ѷh,`ʕx} @hpL4˳5|Ț5ԉ脞9"Ǿ{%x d](WRb ɻP[*iaW81ESw &_MK=Zf 8 \qػw/YęAh` y&/[95iX]qȓǰ_R$3XjSa .t&uIef V4r EOșI{W;۠!`Љ*@~M"b-qr[D"0{sS%~fDL,^ N9:1LqAAɷ3ys!b͈+Ur5)׻^vm&`jd\ijn3eRA[HNYםvCtab]WS58G?Hϫ0k4 ,==1y3| # kq ֊$OZ%019o(ŧB `@ إӜqd9~: Cn7K#!Ɛ@ld=(44B=bʭ!5$WР_#ߋ"4lbvNS3>&* c x2ҧ^>ĒFwHs5iB 2_Tz !/T؍E1*9륋NLd@EHn&fZʛ3/5I&sSIJ\*@OE }*VnHØ*RTCiF砏A=L  jJy \%J>ɟvgԉbV:m'[TJ}UD5?_T[)xtD:wX|M(Y)>&ͺ RǸ=)~B<|uy%pq*=R@1Ci0hQ)(2z}^'gJPZi!NC"Z^Q_GITvM\|uy;"˧--=]0G6N@ $>\W0 /&EaCvty'*bZfƹ mQ@cr Bc%UR|4٦r?Rղ@zSwt b=I•$RwbD;Ie**߁d(o;QCuXTz ćs$/ 4~.f+xX""tqMA+8z4;5PY*5 J*@OAD)1 :u J;8oQ=>=+ojq5ɜL%VΩR艉{6ªϋlA)V~ia5w:e+pid_7@Oa%h,]&E W݆=kuao??n[@ K*@OA <(t΂#FBRj4~%8L'>4ИXå{qvXMus!ݐ y9Mz𗚧q}eXb鞴9uDǖ9 ЧЃ"đ.yesd6)>U2-yzؼyH۰afffկ~/2x;{v Pҥ5'RiԻ,_՛_p"9:m2 |tT0bFZ\{08 <^#q25D|DvŽ;o7q1NI'N:iQo߾EQeUYﵬ}a߾}oӟhOTڡ`ӦM8k[n>:[*UZbTz :mۆ'|'pnF\rdߎ;w\4o&z,_=܃e˖88p_ױb r)wDs~_Ǚgoqd?)mۆG}t$|+.W^|ESNYTYO=.y8tPX; K4M⑔uyÖ-[cر7n\YׯO?;{a(ڰa֯_gy'>#&/믿 .p^Ú5kN۶må^2{1Z g}6ݻ^x|Su]o|ؽ{7֮](?||_O<"֏v]kךW]va׮]]mۺ#-W_ų> Xr%{Öɺ~\uUx⢋.ZY#֭[[GW+~xWzE;ˏ-ā{e]3}s+qe_ǭWw]lV45Uip饗b˖-8s?W^y%jr--[`ƍػw/n&\8pA\|/kQRL+o_7nO< V?A⋘ó>5k`ʕp!l߾<;<<*TAWzR4KpbӦM_M6#Hwhqя~8?JR+Wm݆|ꪫp56Ok.lݺ/2n&xO^+W?={`Æ 淛o6m駟R~tj q9縿]r%+T=J*U:JzЕ*UtRJ*U:JtJ*TRJR]RJG)UTR*@WTQJ+UT( Е*UtG.}IENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/ker.png0000644000175000017500000003303012253362407022165 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxyx[?G%[nDZ/ &!P7)Nh Srp̴e-0mK f@( k$`HbK;eɖ|'%ǻ%=?HGkG߼~b' F( ˜'FHvFmF(ڠ5FQAk4hh4Eh4 ZhEF(6hFQmF(ڠ5FQAk4hh4Eh4 ZhEF(6hFQmF(ڠ5FQAk4hh4Eh4AB!.]ʽޛR4fQڠ>-[24&)(k7n$??ٳg'FI d0:~^{} ***(--p`&崵p\4777vٍ͋g܌ / RPP4ގiJԢs1JkpN:::f֭Iv(Lb*̆ {7駟jF>6׳l{mRoD>LS . %Ġl :yys&I-O P ߧ$^%)d}|}lH$bQH'YחbA'!{=\O6OtB.aH8&G9"Ydm _jҠ]ˠkkkQH'Yק65,~cض-ٕu9'=w8U&I- Ϛ/3h0{M2Ud}|}aK3Ʌ A<9.\rD>@>հA;VTAKb$듬 S [t_lГ釖H'Yק4 k҃$kTÖ iH'Yק4\$kTÖrH.rcꃖH'Yק4uCBa)s@߄!=O6O5liйVHnډusHb$듬 S [t{!!LZz#Ydm _jҠ ac1I- 4C8dBKb$듬 S [tWWtuqHb$듬 S [tVVVAU^d}|}aKnnn&= 0ԃ2{B/=O6O5liyyy[Up8H=&dSz#Ydm _jҠC k F>@>հAGBBPנ91I+LgU ;H 81I-[Б5ɴ1I- Z&ׂH'Yק4h;b74ݟtcs F>@>հAGhg8 fj j{1I- :2mAĻ91I- !!L<(H'Yק4h;0 F>@>հA!$H= F>@>հAa&!D9[z#Ydm _jҠ\r\tP;Ad}|}aKnnn&#z֖ZF0 suW҃$kTÖG-!ɭe4&2Cz#Ydm _j(k[ln`q1B!CT!=O6O55 b V\ɦMb^0 ?*B$(9 F>@>P֠{9.]ʢEbgee0mBޞƆ][>']z#Ydm _j(mЗ_~9 7o+jэ;vj*Ls CϯHµ)۪{׿*#*++Ekߵ~ZZu5kpӦب4-d2^{_~RSS5\ÓO>7 > 6g$Qxt}ŢpOw'F3 oQewT袋袋|-VϷNG]t}5&x),9H'YקJwq G#bMMI,f N;?۬Ad}|}aK }①҃$kTÖ|dvm8N楏-}d}|}aK;2.-hwW v?fhAgkI'Yק4ha~R:}>?}ŠZI- :ڥ cH'Yק4HHY0}؃BAd}|}aKddXh[ Zz#Ydm _jҠ#!!XmmۛĂƀo[҃$kTÖ >UNRI%'@#4Jb$듬 S [tI3fX}T8me;Ad}|}aKfY={T8]5r? F>@>հA7Gs5su޻7)3hg1I- ::$S z-=O6O5li!Z)y)N{W7;H'Yק40bvhAd9z+Zz#Ydm _jҠCtk]M %1y-=O6O5li!!X ߟX Zz#Ydm _jҠCBW?t8m  F>@>հAG`~heyj&0C.҃$kTÖP;;anAd}|}aK ^-hݠ1I- zׂn6҃$kTÖ=8$,/viA ݤLſO1I- zpHP[ p'!=O6O5liЃCBC!z&H҃$kTÖ=8$CG|o:ڠ1I- zpHɑ A;fAd}|}aKf8j ѽ;5Ad}|}aKօ0\7 F>@>հA}ZYgZ}m-=O6O5liCee`{w d_;-=O6O5liCTTX{$ Bi(=O6O5liCb˖),f\X҃$kTÖ=TH]U5L܋s8AKb$듬 S [P!!'[g;yj&lmo[ԥ1I- ziІ K9h b$듬 S zݺu\wu|;ᮻ>TH0mΝ0L#[Ir/9Z^MAd}|}A/^{'x 6_.$>h CKb$듬 S :O@>Pޠ_[޽UVfV^ @uu5~H?t%`O3U5.h;wTx^Um|^}Uej׵*֬YO[л% 4-d2O=?Xd Nxk'|r恵R%SX$iY‡_Kd|.[-+]vbŊ~sCB3㏡g*YggHsвmNv9 Er$YקJp5ܮo¢&#Aل;86$Adm _jҠG a (Y/d߮ "9h S [p3 #D&i$ mgI- zN=:GOgڧ3$ ɳ$kTÖ=RHV T9 (>Lv) Cr$Yק4BB/~L֯D >sd 9h S [h!!yݺgRSI9=v$ 9h S [h!!X-h?9$Зׁ$W$M|}aK-$(*㏷V٨3o>ph!̰nAdm _jҠG #صckVrs.'H$kTÖ=ZH!a7^p!ӯ4I- z,!!aCi@'|M %"9h S [XBBl8k]\Tŕ"˹B1J@r$Yק4豄"k& b (7iEr$Yק 5{rJ?7|3nkHpy͚}‰1y_Ù&z{\U4IF [naŊ1w\*++ٶmۤ=֐atio=%Dg¿//G\U4I+Q7{z/B >!XCB.V^~wop~n3)e$O|}C'XC_u'SBt%Ad?FAr$Yק niiᦛnb =vXnpSrK ٔa4IFB >СC\pqxBBO[ cp44Z7׉4IFB G~;091àb+Zr$Yק 5~4ygv?7U- RSS=l!wgIF =| cÆ zl޼yoH]f+PA+N_w{ȝI6O5f\.}Q~_sNnv/L #\wu~!.q l ),`$M|}0^~=[1qxCj[q)%! sa*w&@>HDUVclٲ{"!!X{t,_>HA̟̤t/Wpo9Adm _j$ӝJyyy/&Fַ 7VFErG be\{_ &@>հ_󋉅Ҭt(]#1;OEIqEr$Yק4艆x˨Uc &caӖM!9h S [DC2J1 ffwW+Zr$Yק4艆tu~I*%c{MAUCr$Yק4Ʉ.f͂a8GL GUa4I- z2!anz|s]\kvl {\U4I- z!a}Z8l7̺kӱ#UAdm _jҠ'F̄Ț}qe\O.ps̽ǀ ;3pAdm _jҠF(-6}ݸvR7)ngfYIC/G*9h S [tM@UCr$YקJt]]˗/N0we=I'132OCPڠKJJXrQ} r`fk!d2 ƙd^^MQͮAdm _j(mϐ0aw[o :8bm2AL%PL^SS}&@>հA԰j*֬Y##x-Y_R%p D555M~W}ka^4]ԢoL-VUUŚ5kikkC)Lb*JWW},++3o3 i^zis~1M0ͷJیȎ;&}mƴ8T?OU$k3Md-h?LMM =nHXAB x1}٘aWPjAdm _j(mÑ0 /j/VC L45v\Cf%M|}aKNDHMd1>(ow b G;K40=Adm _jҠ9p8f΄_qꫧ#\Y.Nz$4<{El4@>հA{&ptwl%Z3S9Ns9z"y6dm _jҠFt3@aʕS b2NgpT_SMX4I- :!a4쳖Yt՚N4 b1{M]S`$M|}aKNtH8sαv] `2hmM%2) hGtN{ I6O5liS[:vﶆv?IՅTUE׶mIK$kTÖ=U!a4O<sK/Ye%jZ08(έg4&@>հAOUH8LxU(*ĘT1`Σs(?p񇴬Pr$Yק4 3k}%fG b à*8cwqC _Adm _jҠ:$ '/[3 Cx)cΊ9莯mw }?Adm _jҠoKNjd1s0yNv~o'}I6O5li bb˘  oo0|4>HY '9h S [tB¡2ka(d {3ALyٜRu YdY|@4I- :!P\}5Y^7Mv)0|Jw)=q[4dm _jҠŷކW]== A#AſWppf83*騚f*K|}Jv`5K~x\םG_8=dx2N&;5Sdt8'\y_ KOCc#<Gz'\G`8H{@kBkw8=RQDaz!"6Xħ[qݷ(rEAvݲ笝 ]>w;ɕi4k'Lv):[;96؄?(?ֵ@ HcG#5m5Դ ܨ:O৿1"ҋ mOsFay4ibͻ89aTN8w{TVV˔ S [U_.Bo] {~;n$ڏ+#s?}#'gSUFyV9YGg[}n)P{=?CzwQ8Rm?k>-5ƀ 3 "řŒȜy|9,_RLna]`7t4Ps@k~7#>; 0וb)ppA>_Qr pmԒ&@>հe .Ov4۬!xsX3fܹ{k>iP"kj 6͕FYVYGgd ř7}u'{iZcOL_˙wqx:O"|}miЪΝ&oa=7d|1MCC]ZCm{m><; 1Gǭܱ/[2-E}Yi㾟F3U-4K._|1eL ӴPWgwVAIDAT[/vĴㆎEIoqeQY*/L/a N;VK1&4 0~IC4A~ jAZ_S RQ?d+ڶZC/H$$ƴM, A 5X#=V8ZJ?Q"=-4+z*eĕ;aZ?'Kil#^@ׁ 3gfrڎӨxiUӬSƧ-F%m[#=DOtkH8 /?Q5۶YAÏlS@(0f2f6AQ%/-ٝ @q~#_s%gSU7 =DOtPoLZx;==ڽ+MD$̌6Hx}S=Yd1j>g<,pIxFޚ鎁n##Q_}XQ[liB‘1 oiQ[]0mR[vdõԽQG:I[ei5TͬjV[fn 4߰3) L2YmOtP8A/B8l];t˨񯘧:fؤ6V7qwn?OFwf'oÎL|oS5E5oAK )ٿxq+XHK2sϝa"AS!HVZ׷Ҳ>mONǷG`NYM46?h߹?h ~5S22=36hiRCc bLZtJX :;կWbOLA$AS&o֭[ Ƽ8~r: 89I>^'ݽ:pqe=w]HscgK^Zico^̵{N!bKV퇘l:;|pᅖYPXivx::줷7 DiH;6!70M@˘|,+ѤR4}פR'ҌjbK^~^zɚJZf}c_Û4MuA:+;ꤣ2`]pȯw->f[/7ˁlܴc hnpaw}x|0a3<.]i! =ӓI; w 2#ܾ x!bKV퇨*lh/BMRN't,Zgif=YzYՉO5}R RHHS]]S]#1g8=,D|i#~;tw!yk$''ᄏsH8QLoW_߶c`O?ݚ$3mZM[Š`m2]1ݽw A&32ħqf9qepepf8m}c =NGO=t;8y#;z:s^`S]xR]5|\M>'.Kfʎ#zgK7_Omm-eeetE.DfָN;5M0M`x눐k7c#gδZA})*. .} [% ==9@/5 5UviלNP^#߁6&dPPpto3n=l3I]M0Zj)TR]+׵e KF^P뼤Ru(N`}$QQ7FC>뮻l222{_SJS޵ ~G傌 HO{\ǃ^nVo.ql|Gam-8[pނø:[H8L z( =./AK#px> /~| /].G饳Kg^/]4x%^!'BAя:344q:ΆZp8p洞N\xxs8 ..5.-⾿ܦ(ۂz0" vDMհA777'"}֘Vvp#&9{c%%[Xh0NOliyyy.!H `۶cV+yfk1XǬYwI84hڐ>%u+*vYf\] ![D7(.,#wt}aK!YS#-HxvY#ͳR0)l- Z`ǖ-KEζrps͸@qV6 "]jҠuHL xfh;Iz:q@8rdm _jҠuH847ǚ[SIM;ԁclk(/AH- Zqj G=_t‚f| ֈq"9h S [ 'Io/[g6Yע9X3^ nc%M|}aK!8FT[k›o2 ֈsυNS:X\6O5li:${Xfn֪E|>X/%KYwAdm _jҠuH8ZF鶈Cvs,C^j)+xHK- ZXks!Q  9XAr$Yק4eH3m{:** ^Er$Yק4EH~8`i`_03Vx S [Ȑ02mz:*Ye5Y$BF\rɀ!pmHK$kTÖ-&$ll֮}XVw|V'p@>հ'޶!a[l0m>aX/Yx,aI6O5liж xC~}o9œ9^n.!4I- Zِ0C~ؽ񊊬!S.z;$kTÖLHhs@ ofeܹc 1I- :!a]E54 $듬 S [55q:޸Q:aMihF$H'Yק4脅 { AG3o5m8,|d,2`ϟo[kZLf?I- zB!adO-[wޱE0 8XC>2m*H'Yק4QC^ktE: V)Xf|ypِNJd}|}aK[#) G][4n5b?:gf&GHb$듬 S [tׇZ#'#?N:i, O`d =O6O5liYVpQ[Gqaa\%$kTÖݜO>2 F>@>՘B㈘FA@>@>հAv19E I- 6ˍNdP$듬 S [ˍ Ad}|}A|wq8p z1zdP$듬 kkkKv 1(m<-:aRNJLv E>@> Zav{ovUWo8ڐH0Lv E>@>0LOv!|{ӧ_7o-OMh$듬 dkkko9(~-LAkR__OVVDc[AkD裏rgOr뭷&M#=NhE5FQAk4hh4Eh4 ZhEF(6hFQmF(ڠ5FQAk4hh4Eh4] 4IENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/coulombg_c.png0000644000175000017500000013201412253362407023517 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATx;m[Z\Ͻ9np[dGTe<%D@BB!C$ $,5vhɒqd'M4vTcu{>{Ƙc}%Nsw4||7UK,oUp Yb%XK,KcK,[ Xb%XK,%Xb4@/Kz%X-K,oi,^b%xKcK,[ Xb%XK,%Xb4@/Kz%X-K,oi,^b%xKcK,[ Xb%XK,%Xb4@/Kz%X-K,oi,^b%xKcx=.8ӂgjpͼ/ϼxn>#yZ|`t kʁm㗧/6/&S)x3RtnK6@3z^y|~&LKiR#A/b[-Tڟr4uL*,iygwvSڟo+K,~r~/G [`lj?~len gSt|nYo·ڶ YƖM|(Cs((eTfeݔ͓myfS{{|pXK8}lsgݹݤa7Lo2տs~ׇnLNfR,A;sy. HUܪ\4k'jY08 \J?{1f!@?9Ex@4{^*Oo|ޅ/ tV !̿i]z֕i;mD>8SٹS|?Z|, /Th_F5.["'FS~Y3K hZ[ }i>A[&ܼ$ łxѕqBؔ_M.KU8iG#YӕѬ,5=:.XĶ3Hz̽:[1|eb+X餤ΧL0*&{ Tu{V5qBXT 8 l(haRgfC~#EIK a| n*Bw؄?6EylXnôptfqpvDfkd N_LE{:ځdU%Zoϳd`|%Hyު5OV9dmEӣʵ.kaD#1_鬠X!Eu`BJ1)iVPo׊Yo5Tm{'rdl6EmAʿd*w礠Ѫ0Ў*Yl:ʹdao8UͲ&YǬᜋ>mO\8yyr& (jvR s,~bTbfgl,v)9^*H0ɠnvmZ~Td9.RnJM^oGpW6WYHP s|[@f˚Ϛ(i<aeq9.9ϹB3=bkzsN@6G9p;P(Khk_E*X7[:1~_} ZI']m>:Ů*Z]#E=Y"+q\Q~~{ODeė{>?󌯨2<{pn͎`Vԧ^-l R hl<]JЎ?W\o-.yـ V޺(5+Kc$V2N0rS]ϟB1.{UbN':VaS%eilWq.> b [Y$GNZ`2֚rFyi{t4to9fLg]t`0;A9Y(hr+`'>$lt]6coh' O؎ͭZ.acuY&cGFQWyqFq;l+R \!=﹆m,??s^h :MpRj^s'[V:&mwuz 7nHtP&N-IM[8 <^xb|uݾF[eiaiNF!Qb,kWFqבtl{b %*VI˴:w[Uw}X==_k~'~w}G>?Nl{{1"ګj +;fg.FM&YTy wҪh" *Zhlnlӱt'Om[Xms-M XaUsKkf-O}'vy[rR׿ZHk\/|?c^_D3KK@P'» {i}V,0 <QRPKV:omL!ި^hٟ'P.yA0TZVʑovNXMkqktXfrKA1|GXR+_sͲ SI@c7?-4onl?T;Uԗ8{ dG_2_e%5P]]HC S>l *9,SL, g~;hΛ6Oz *Tq)Je(VM[k#-S۞s_5lH1SP2#5T ejq9 o հ=Zim?y51j IZ]>6~DOP'-׹8ojVQЗzFyO9`TEtv,f[pXJ`@8me@[-Azuj \`]9 PwH?Na\ gg2mkNaM޸iaݪ6s&&8kSE^O<4Wjсvp!@ 감.:Bx.z9*s i&MHRxk[|Xi`];}}gk#\Dҋ@Vozbo4ڥQl 2h3& @Rlx,TSPz@n9d$u3cmcuALl - %@?1k@2PLjrsR:5 a  @}'0#[Pg@uI(t8&*qDnxɼS,3ew.{{H8>)).ʾM+D Jha=X{U<ۡ^f$'CX0o5< b$ Y]b᳝@c] с gnV"e IУ 4nA}&Z"艵ѠT g!rkPkno~s/.YyZƱ7`X ;4?v, >C)bV?)ѾckgT8KdaU6i)7'kWR<Vf:mVպT>3S=csO7T7('5&kXҒ 8>(rAЃ Gp4*g9S^vkST%ǵF=SsxAPفA)Sxq5- 'ЎM ^b/DUjYJDa*f*➪n ܅n;sh֩YgGDOsPW!OC٢z݂oP늓`,Ü@D{p{ rP  pjUKTOcn58 0"&\t,uިhkqX{Dd'LT+3E,36HLuwmYp|>:VTBZ66"\y^xUТzjr (eߜl>;i»͇ %ggYVI/e%@?9$^}3\_-z^ޣG(9)s=\c zоX :P@~R.B;S8Kx0 v<F8-t;`*RI. 5̍* D嬡p0&gan- R-/Wogm!3Y&>X*_U> 3#?p3EpN;pw½=ȃ1BH4rBtVOI}[L򅜫:8QE@רnuGNṒKHxb9+VqNt74g~:&`y~JM6%dҵ`& F0ihqosvVu>sQSC^-LvKtA儰%PpQ8!U8"`jyҭAz=+eUau%)TmB]s`CӴr QVkCc\2d[5l'R ښQ]낼GҵCsMammF/c P@oQYc1zE/#gȃ9{@``˷A?TVwVAr=*z)uIG44!1:Ҷp9l~+F-Ud#YQ+ly`EV(j{LS(4uh`39VĊHUW55NKΓTgͅ\A: صjra;hq-e&c4I_tt#w;N0m@   OA)9XJ3lR 3ۀt'VS;p@88 ]:d#I^k7$xҹX5+QTsӵX)鄡BNRϒ+ PspsTT\= j%1nٛ)]} m.4W<{ccvA5 ua\øs>=pw>0p0~K *Z{w P. 58e V鉃JdtA]4x vP0][g5. -gd8Q4AtUƀ *I-xjsam$٨f c,9pgPU[kˮ3-+vA=̏\goە IDATШY,α1IC+7't70AÑ.~J ÁgT0-jZ>,i)#C:{FT}FVvgU cBp8 Uv[dA݁߁u=XkCW=> Z+Jy`[ߘ)9߹-\E#XnJbNA-V Nl`Ǧp؄ykmԏ;BZg!m=%B,~rDȩil w)ݚӇ'o =!+/'5zlᢖJjXb`]Q3IAi b]΁>YF' #^/ւ+#'< Q }UJit5e Hy5cT3@ߔ^V=H ΪYuvRalRBϔ*01hig*va} Y&ꨁ%m(]\bSCp%q e% o^?AowWDCAR<n!ĨgI۪hK~ᜲ5BF>DhD591r zFǀӁx8"/@_*1~#>[yE-IEJMR FMWp\{*waqֈǡ-dG[vqԐ^\bScyH%IȍL"2oXDjQCݿM"Jءʖ=g'}^}f _أ%gxใ; PfHUMA8ֆ͞PݓwG>jլ5F! i'۞iRH-[um/pn\3W>WreO$]cS#)Tn49)gr@?P[ ߂ݝV윏ٳbŚAv q:BpąJQM۪:̾ާ(%3]f$߬Z֐ =ᬷ<_r_}WSɍ, Ip 깾{j+Y#%=f]/ kilk4:8 #t6.P.Włd_Z.k;D• ԼH`,F9BR]kK,~jxpu'K?hPw >>PVw8p+mU006 $Hă0ô@[ba Z.'/x Sm<;""BK? ^~?}@>!cc``еC.db4JVr#v"Ԡ~9>ʩHle:T*x|*-Wj, e]1%£Ek1Xxԓ;!|,`fyU9_FV>]VXY\bCFA44/ ;x  e-5݄%Q\Ȁ`\hypBd}pڶ/*PJ4o$mbs{Cb+n^\|wl_wSyX` ða7sa~mŔ55 >m8Ѳ-6I\|DRǯ=e-ap&<>(@?dy46Ǥfzd! I]ρmAd>( ~w #ȫlls_@?=cqti{ knG鴬׈ c`$c } ƛQتg+ _fiu'F 3=Ʌ팠+ Qi*|ܿ_\9E7඄Z;p\ ʃmUrȰ ƠsV}R.҂EOڝio9떗6J T.R~3%XPuh[E_ S!\4mN}t?s$}LR(0"b12C]RԼYg`Ξ"S8,eHouhYnj π|JA$4-p;?|bo8vG qR+`*G>߁?ƯtWWuW{ÑP}焽wZᵄ}lEϙhmx!}VdWmG gJLz~lx"Xz٦:۬蜒^'h 5v;Y#\ף.A5oE٨ HRJ֘!9g`Ǎ" IGj?4.DA-VyLYP)~'x՜;`zX! 9}. e*6*T7?]7Bil(JBNnR5t]z9߃uͨ We8 5kƒBǿz9:8)5ʡ`rQQI6{TUu]QG-kmͥtƬ<ܓJ+αa Fbz<=\$0';B-sTiNgA·IQG HVp; DŦB%=x=*x ]?e+ʃ{C:dmdHDGF[}5\9r3NrSFdHtF@Q:Z`<ң3j=P G{I((irHʊ9i2r.arS#K$ 6DNSԽc:i>b5DO Fi7e u)*p/4f}%">).6$Z|I USǬ[CW؃ rs /5檣Oπ]{qFJu‹ciDwh3aaZ/Y![؇8 _q !n؞1"({ .@N5RFh(@z`NRhqoaV<2[qeuؒr״޽:'(E.r:q8Q=k;R!$t"x`8R1B4Od y؁lb+s73Vg ʸ֨:TR`@ ĥ] $OQ0_VڄB>uYh{q|q ka|]ڐ 'ş!Y/Z:MP=g\wA팭XUZLGۃ~T=[qH74܋ݏO$#X@ڸw ہ*b3_ Mi1u*\"a/SS ήJ`c;k܎= ^n L`T I4@?䈤>r RS֔91;ú/5{ b8SFI'A7f!yt@p3{^ ㇊{z G4AsTZo8B;ب907wxHkfY6tv}Cfdc>K'G.2m(Wڙ&&zҷiG.fU)1 ASusQC0y6sch.Z {VcE?!}#K[#Rmɰ,5Ad[z, jc1w G[=t|q;8B6w}Ž ByDVPV!9KKֹdk|hoTmo׀Q嘵6Qes@tsHam,nO L;Z27lTs2D71&I56D I{ + m/o eHjP/GϾul%8 3FhU%L 4&rEH{-߲?`p[NX)=fEDO ArV[Q;\X]BB{+{X(Ijھ ^ug $ZJ!|RZ?Vm68gZ|l-5p2JXmQʰLn=Bڻi)֘m3i$m ݀?[ŗ-{ 8'mC$O4.Y*{1>Yf5O.@W$H=i D3GG˅w}+\ܐ^_o5VؽLN_k:XUeTI݄@J06Gz$PKq5kAFƲpvڔkoQf))+87`1cnřpEIcp$]i |4 ּ77|i3ɞ7#9MF* CCNbߠzN[˴ xҭbmۙaˣp{] MBX=_WWW t,t߭f0'wg8G +EW=%$[`[HgE54]FU=h_ԄdyƅUƶoO/&/R>ˆKzù!]+rNW&P26 7;G>"AV!=-(\tWW~WnL^6_џp3PRwxB֮ɲbrQAfDSӢ9!P7M [m$ܜ}8!45@ 橮 bBQr~:hemDoYbdrP@hup䥢/g'8^F|fÅAX#SgDJVMk;TQU]9&XQOק0kj%Bz޿3uyzyېOd,7~7WOOkk/} `Y"}s;H[|RZ̽ѳ8 |P)wq>:)w{,Fs RutOl9&Rq3kha.7 AAvk,(T%)^hwrGQ88Y#IQ"zQՕX"6δyU);f2uJ,c{7|2bL|k_k_ZwMϽz75jqjoI/ͨlq Ry^̼w-m/}uMS Qr{bU4=6=.x = 8٩ƛFdsgRL7euPќ)]ZG-ugQ:s|$ΪYC_Q[Pgu]O8%@?1l,qS'*Pk[&3-:ypn:$)h3j'ZK*:TܞngfղZ7 !1W'gj>P xp ].GVgy{<)t;3NRep t:p"J̎s WZsۼg1Cek?f6s&cG'y 'c.zR'FcUӼ: sMe{ ZM:t:W$RuuEV0頄:pN)Mv<䕇_n|Vڐ2 >*iM6GCvЇn ]mtYa=3cs_I3zD2o+Lv[{2yRhӲVM*Yz2%B,̴m&[';9/L|y֗m!ay^ux{۪0}+Rc*h9r'OdPc!A\77T! Ҧ˲EYS1$ғ!X+87YSkgYѵ99Zak/Yu,~b["=!䞞+hF;j;epL<-™* f:ekn,VVs4Zؔgeu%P^5jln['zSdžKaN9cå7/دdu|Xb':jz5"To~A d;1Vߺ'FP/$ h׃QR[E-Z1_ȵyN )Y%Z /x-3ҧ3wMA-Ҝ3z#$ S"<[7B}[Bkb3*Q% #& UAL^|{L8--'鉝!SH/ mEF82\C 3\kx][i_yyeR+kelUlvު IDAT&6BtͲt]ڑyK{XjItTa@u^cuAr== 5#)]D|p69;, jDB *54߽*Y zSb0Z8ԲRq" j <;FY[`y p'ĪީןcE,(\6S5ۦA;*s"hY! yp֙:WhZ9УJiz!tO eCu_2`[l/+Zj:^\jд,)2nnf3nS.Β;Lr?tYJ6?J0g 퉂D[-,L:v*vx;l=EAcVɍ9J@RWd%-JTsa'"{7*dj썴P?g4{[){IO;sW[)vj7t,lDgKW<ѼC[=[L;3[GzKXd;e%D`VM_QX le~sNQlAƞ_2{=:팯\rZ~QANѺтtݲhtJKӋQbSCzܜu.5+}NM?,Z_svނتtzW< kE4뙎ut;(h]8XH p8aE*'JZCwkٯ_ mVw@<}NیIevK6x̍O[ްc kZUZAzAP;7ξr M"mQ6Nm@ m筗`AhNQ(g|_V_l߷\iD N :YIA4"q ZMY8WXPa,b#eMmE`X&旧ʯŭܐ}rTu\>kRS 5 {QX깁񩷮y'24h@ =%ZZS2oN``Q۝wְf؞LQy5z'cJ6 ${ABB =Z1 ͔sf/}qZ&l}bt4LRQf,jƱ_O7BڇI Unv:]퍵 jheC E :y6VA܃4^1˪v}P%9 Rz&$8W6,\hwHvIO$P ;RdsNAksY]jgکr}f,P.)纡]rs 1.6EWc]fB/\R&s#;fjΊ  6_se򰠞v䞏V{,}dΜVX*Vq* yhZ& X ʣ1rM|b#O0Ўj½=1^s+žx]QWUnL0QtR:7ZE+켄4/FU#&B}?̹iѨ2%v-a0f:-Lth(T#XS̸|ɉ5Rp[T|m/:=HbEGXӄ e` ®4PMsf@g;P- Ozꭳ5mF{ӲHl l@-AAZF6j:Y|jfmZa`n!%X;ۂ 05gC(pO53t9iMڄ+>*:VPxJ'4@?5IyW 1?t q+$fUzvy]餯 V=Ȑ6zv{cZԲvUQ:.Dب =(3j%"(5PԁsiTt)y2l26.h~Rcya  ]zn!<2Ζ=q `}"\i6="=6R8x# FAAY h1(eivS=#2z 4<$cp3vR l('@kt<#^g5j1 чNT=NЭ!)qQ9@?1KJM.w KO[߁smǞ1|a76Цa<1i*vZ DEn$(p@5ZzI9*bjAjNV=7eq4 ˳R đۀŸ=1@1:*d|B8"%ؕi$?Z@G~w??~\è6",|= S> 6Mi|mBFIq^ rLM7JYe*L'lg@VVLC dvP8xp+7#hW^s񛶿a)ܦu M3eo02Ux*mOHB< < v[[[ zn>y񏺼_"//^FѝwmAt3n93m0iv|ƙqdWk80 [ n-5 8laVXoZo^+Yq /D+pwr:A]#܃Bh@Tyl TH?6Jѝk뚊2UvxPpB<8V(WQ^A_y%Gx>!~ȅW^Oă }~A_|}ᙆK4OOo~3+=\_u]].mgs11&F`KDpĆ/ر4#" IÂFuO,s99=e.s<g9{s/5Zxƍ&?t)~~>;s|{K_s>TLiGAj16}Pv~Kpŭި!b v9{0nvykpxhmuC=Y1D׳1ZE*YLW*ٛuڱRJ^[zB s#=u>G>Npo kEΫfK;?3?xy??}+8|+_Ї>Ŀ~yoLxӄx*nX>+{pc/ #Cc1Ms-ær !vڕe-#j+g|ib|xk A*B|ssΝVż$NͼjHbQA )ml3<`.'LAʇQZ:دw}^PCcw!nS  |||^{^U_O}W>(tϊx*֏ Kl$0"ȓM>K+Z$qT A6 ԅfʄ{<;\s)_٣K·ןvp{`}M;CUts泴|3v6k{fwܢV3 +%-][(BY}2GB< 97`ou6Yޱ}> "|ӟ߾ͣGl^8c#0(R-znnY2+zoUly@oXW^+][%RmMοQi̐_O "!Ri9(E%Py̤bϖ(6 }G,r="Ӹ5֐rGt Ե9q,!VT enj xO/ CADy1j3s59!cn~}""&޶1!o|Opx1KwMiS־u^AsVLurn7")^r[&ވt~Zz{䘃9'kvb_:̂C#c{Ur_A5GjWg;\nn a8Pޱ'?ɏoMCRTIea,ݤy%U8smCjchVp%r%_+䖅m-W(褢svqnA a;DX΃5>?D0pd4aԳJDwi],ݓ7YnBAus̱]sMa}8;NCkB)gvr5Vbhs ?!AIRg )wљ,xǍªk Җm* [_sE`%@qUH}@iyI>hIQΚ|{N9؀S צ1YztmiCFW )BcMJ`n~!147qXgX -';?C?_s%:CS޻ %ΦTMԳt ˖ui0N;- -%@9jV=9w61UQn~%Z!jß L L"<FB { q14dqei|TeKK˒MJ/KQK`A{FĚY1Z{hepB^3gk QaC4Z3rM6ըh3w9FմĨCLUm4H= 5|Y91օNρU:^gEExRЦ} cQ:c P 7pD7mѭj6.3;bKKv~/CLzQ+|4H%9=٨geTmV );a[٣ԳP8VS힇9 2eH5Q@:Pڻ5p&M37%-/髓^ E>\V qON~E=Uг{K6Ѩ̣&Lˍv<t\㏂[ i%a \-=k!\lQPNs9 |=8q1Tob2dϤ;:$,?YX+jHFQיIArK%PHs>Gtgٱ?~!f[ 6aɇ*盞_i"laf%"ftإ3zh!Aۀ7cL[$glU ?]b#jlodHo@}Lt5 U.(gOH$tb w.jrq8A{iIE3[m{ÆvP*?a-jv_ /ЯlyP)V۷aUҒ7$(qc&h6Β7G1z Ήmm1 sVA !$;x΁ ͹Mݤy LV!y{ER4G~E9Z olU-ui7|Qvv+u>yM0H A}!y@:9Tځs֜V mPpN:{{E`1ӝ=m`[kQN!VF081#UͲ6%7Cţ٣> Cr w,0;G/lgK+ފ_lI1vk,PT/Aj%f HpN6G4 M`lntOI/lUUSoLQCxvXfɫMj;)FSAv| ?ڨO(QqQ9(EP5הdjXCoCsң2-d^F45PEYcq8rKgةylΊ<4fA"9[[7%{=869c^k8Um-;gkUZ+lWFr~S[--hl2qYt=79c+:aKA>,r~UE='P[@*jWMg\=~BYڴ4Bt EA+NӧV5&#>ݠ1W6YVgKM-9!%]މK)1p&6X9Y}!5Grti[ ?W\fOWm =ŌV%I%v7zX xkoVD۸$L =΃DZ0kYX)gC`&8k[z%j- ìlQYpW~52+ߟtޅsdA`ܪK9Nj=h``M3ٕ3 Z2 ަ,i>Zz~rgsUj6=[= c^4t}%V( eiVSevצNAh}S(49sJQ X!F0sg:N}SqVo#|+i#ZTTR,z !.No˽oկ,ي6{ pQC:U}j}ApVv%+h/U;-͹&*JoCE.689-n6t/V2Qfl :T\ odj9oN %{m-8"0sCVAXu`ts]WnM!}bq.i?1q=Գ}oy>9򁖺q:IPIϥr=}8KU7Ft8J9䃆~уf޽G)70I]P3 /hWAz'@ߤgI ([dGB5{r>Ilա{[AmRU@ ը\XEo v&=-lld 夡M9m?a=ki- o!f:_4?JAK5}W\99y&zggh%Yg [Ws "{ 4I)/2QU/kqO=W f{.|ݲFq '|tt G{gnziEr*RNcI(Х!|Z^22n|h]LMZ%δ`8[x>N!FE$Kku3<;&<=m|PvZa2p-ȵ"VZšJ=ۋYⴕ֎:n@?;PRK9m V=g8V39* 2* jԱJ9*eq8DRvgz[^4_ҵ55NIZ>F4{x ceꎝ?p=l|PmӵAA_)Zhy ظr*GUz#:V.nUxRu9%Ӆ)m> L뒒e9ZZN U l-zqKGMM[C.[ 7ixd@v'=1{wEGj0J@NU(ͥ'&sjl<\xS͜%spπg .]^F{Ch}g[ 圞ٓiBbmV,wq9S__cpyyZeIt2 nVg_gJg mA݁sphs8j59Vկƃ\t TF<#9!N`:r'y2]+q'jR)g ڐNCǁЀ%7، Dy)67sؤ ECfYZ_z@ v>c^Qx Н??} SzT̅YDۊYR7 JqDA8+Zԑu4fSΤ8|Դ BQ/0L f'!T}P5<ʵ) },OJw 9r#li;`e=pў#.UZe~*W=0w8!:w=/O|Z?|ooyI36KKqXPƇy*뚋2WͶa,08k;]srioM8F\(,VfZ8!%"tKƎ͑K2@]L/٤M+yȃ&ag{-&1ݵcE \DkCP): ⛝Y2uO'Wrt:KcJR7$u*,zhwSNTBy0gfO}SQ|zʎ\Aiի{[2pM7p@l_ShR{tIA䞒(^R1!nhko*GJ<_0=|} 1W贂 ->\7aUX o(Dp\ ~.OW`UY=ۣ!ސho(gކk{~ V5?+W${ww={K_** ]]g<+prr^Rm T1Uϱd_VZX#&ZQA`ez.=K'F (N4GxR&`t"&/p6D,T"݀^?ayn_VC9#8q'PgϥU)LK`^lOWV]w,~VY _?vTB9S_}S X4JRК`S7\(P\%'mTf gj +{ҲPp ɛc!LtS{UZOkqغO: S>BD@=0<'ͯ^ c|,+CYŊ0 dxF*Bt~HOUlHV]N.4p2I 'z^Nc Q 6A܇VR:Yjn??ۗg3. fc1V +[z)>GTD8Q w.ХZRҩpyb갈HߗjNi:dC3gensVjFA%M|ꓒ^uŘB4-!=:: +QGA^Lf%P\NP72P90.Sy \#ߜ++=1u`\ IDATV|'#xa!:<02d"S78d"VImkTtzX/Tϐp,D՜ dh/EQ{F#C!%+Y AWnE(hTӾ?ҝn:5ѩa?:8LU \ ܡo ț M *XCF31)IR gV# Sc  \$ޏҘ ׹I9/͂JAr PJڋm–p1o]L.jtAQ]7?PCQT7wfʇT$J#Qi$$sơgYlk Jz RdƠ޴F gYd9$y+p%\੃oAJOx\0A1p*B::::%ixB 0B\NTa>a{NCㅩ4xOHr/s!qRqZ=IpfG e;PGGXbM;TFzq4+k° MS??{,~.B;gr35@ F90ȆIKc5.%k`Ku/V O%IDŽp:6Gk*J=7R w,t2L{(>V}#Kare4v Ie ۨ9pD@WIu|r\XU|d@QKG9wpp $W&&%)ĔqO1'xiR.A*x Yl<(OڧK[8gA6868plC !)j[3մs X)ޛ7$ VI[0h(iUbT*:kJ('@߹xW&|JicK]1NwDE$mUBV-;r]9R"4{ѳ.D(0-G8G'ab҉ 6}u\ pO 8 zEIoȊ 9UrecCJs/A+[0>ىpLV8FV8V8Y!i'd6O0Ԫ:ڹhq"a&oZRTt^Q7*#Q9k֭=g'xW.ԸZ`됽22 z:q5'#"#qѣĸg-)I-JZ*Zf*ڀo7v "9lqZI w.EA׽tB. =C}xNĔ {rw Jq4eG vPטQiQ )6P YS+-a?=}*>!s{#G>yi\`}l݀VB콊N94w-#5ɵ}$+i`۪mBO6<$[BpJQ:)u.8%1a*q]IVJ,hJ^1;iʼ4s ھYLv'z}2lJG#o+:])^\gZk_H 0 l2J̱]fN8ro#UL<%;fL\"<xyB_Y!$ wpsA6 k!Κ|g{cƩTiNyZpһ`/iwhA=wvl%zɆqN2rz6/~ZV̶ǬR1.>튶'o[îwRu9%ЭMsm zR)@z\FE'`)>}gРܵHլѲڦV1ŭ a%oz"lzT1@}Asз~OؽF S!b!ldEHzk+Y Tpxթ3e:Jh21~έrGS9%ܐv8TMț4an_qaշA0O 9%~7Ca1:iNAͪƄxhҪPB\/Ki>F; F>yrs^Oz+~(2-ӎӁmT{#>ocu6Ir%GeZS݌jnZ%_Nݞ9(" [*3~K<|߷'Cp sd؀lhcRө2MCś6936; վmLRǍp/ z#T' :CZ=c `u"|gzjs&6v]8K:=TR9e H!qД_8&+taЃ~z[\p. *be`6)K*RH<1qW*ԓ&TQc\z~yX=}0 h9 Cn:FX+A"1A]L}iS(Ju\2 +I`V.@z-Q6br7^7X@W)qߤ~Yo{<=Oq;_]F .'@߹ (rBbO#W 2L~`xr2C %UNߎ˽ Z$&tK3}f#!9/A _=px2 _CV 0\{} D#^!F`^k2h{Ϟ 5Q&k#Awl}WbS謦o f?Hi*],YfZ[C9u Щ}tlt nA9pxn/x=ׯؽX}g0\ U˱E`Vs.iSw&]XʐsZr3'an-Zqg0\z_^R ~ *w Cn}IM˄"VFS[BExq3D>+&#cavAUGX'Dr ylSI3ӌbxGDD)gn0V,$z9S8sEρ A._(rzS6gn#[7hdļ!=#:A85ˢzNO1`Kcv!4t>iɎXL۵UiRvX9~{Y6uT>5Mmc,Kxs)'@ߵL֐78S }='>K;e`*.aݱƱ*Z[lI .W>!]6*-p m7s "p, 9k18$*Z"EBeys0cd815ҭ 䍽B^7Xªidfu),RgoC3He:u*ެ=r-Ou9Eݐ9gX)<' }F qgC*e/zp 1!ᰁ;}۹8^t2 !3ZAԴl4lد6AYB(RMȦRq"2ІEHgC?DHCAU^8(` z2a夢:;rz0az)>mEa ' w-#C}-!C.}%ۤ,3)= 7D^l͛C$?8nc]T^%AsͱҪ)uJI{0ؽv RxDX֡eHG;_Y}.Em]E򭎉= 0$߼9& t'rK!VUןe(cm+˥ePaq☗Xd \t[4W굃kkEB؈)5w-$1-P 9g@9 ԀN`FI}4#b9"SO'̡bgHȂV_2jEc"/]H@R) W(m޹{`A9sOnmhPz,,UюAJ=i g ^ ƏVYm Ӂ0ug@_j}7`K9ENЭ6BzylpDsNsA81GP1>y2M~ Arxfnl k؊ESYF=/z̗YO w-:n"[Н%t{veM(*|Q!CPѣm,#.ž6*بRAdj>t bt39ɾZ6kB0ԕ}5 %+8CDsNև8 f\,F95źdVY-װ.f, zݙVG V-z^nrBt)'@߱#w;y4N`IYKeKPc`3\v.stC!tWj]cARo'q\<~` !-AI /:8U`NPe ^ I=7L$۰RgV Ү>ú 4j?gMkK]]y9%Z=[R=ɏ&IU(i赺p!Z)"JFfì?U YY|hj"ZsGY&i}5UJV&/zES6Oŭ/'JOUxSTmNF 垂E;*f۫VY6`UG* ;٘6KTVw"]qc{p5@b3f㤰du(՝hBU{ϭ:rTT"ُ>d#]h 6tl5qkQScN/YfYtmřstDI08IY9C0}ҫ$e ~Ea{SN~[% П䮓4L(Fq(Fe@j+4*:=HpM򠙫f 3,S1ܫ5ßu lًN YBit)rBma p˪9/zY3m^B=89qjY+pa/ |ftma/Ʈ}aYg{~4Ϳ;6TB9%(h첵.TxӬU\S'UI9CD\r=84j,ɷwE4Ftn$8GՓO[#9昴tsW,3<<족09D=9pk{;!m,zҭRVGGKR )\HPfh ^zue ѩ}o&_g>S-siLFWd̵Xt3:8CBІ>m?P7ҁsڃ X i:͡ AeE"3* -C9>/|Z.dVfʵ>l/Fs@ ̷FZ UtKiYXX^U %xI-gyaL m|UV`fu$X'5* oR̕!և}9Iq{.1T̖U̘$v#itS1X޵^VBp;ӋRCcw0|㛼Oo2OvJ(z@ʫʫ  g!/~)E YH7ipn!JS nnZYٗx TQmYn`mŸty\̭1/]r ztzZlk;I>g-pu4GޚʲJi*; km2R:K_'a}%9ϧ̩r ӟl3:B7e啷}Ks#!ͽoߤ[nVi3Uح4VGg#@n?GwT ZF9w+/ھ/.eKinLɑ@ksސc* m;) ai=RX vk&g<;tAQT`K֧joҡHIϦg'@2;| +iCё^ B1Ѝѵ>^uӉ\k\Ee9=F9zqlfwMaK[Y=Pw=zY!u ө}2L㼸[5ȸ捱zU&-IZsOIdy巓5KH w>PVՆ<[fw!ȇSa}s8$,KK]i8d:.JT}a2n\evs~S)Q\Rd0Ky66俷oxgү6.ʟUV_1X ,9;}q 4=r̕үw5"d%S4?*'@߱װ˴]f Q7o'AB*6wEիq 鲿mb:@ݠX׾R>颸,}z ɇnO-Ŗ?6WΕ7]MG5=°QՓZU||CEa//A\'Dr(:8& Ω3=Q-qQ%MJ[slڨF[Ch瓊͓FuF4@z+hЋg36EW6Eww†-omlcҰmo&̟J('@߱}@D%}+|״_~L/9--?#`r9}01?: B <&tWM7=W87Yo:[SOEw<j+ZM 9KE.聸,f;-軖4];_svt{[(w* 4V)VY;iJ{p|m̔2-[X Ist(r0}T9Kpx>t{ILcwy3֟p>PNcIK]#r$p4Ѵ!ܪ0ʹ6Zq[eԒamzёB^lQutbܙjf ?w[ C`1zn6xݩr=Ꙛ,|7hPho֞6˜nAG篱Cҵl+ K$G#LVAi54ډ,[ȳF+畒o mpA?ݏy6GQZj7;z0W\;.ũ o"78^Tm>q~ڏloZw%ݨن|yްp*[+rxlea !}q#K ~5-{ z3S}8Xg'sWn}Az^Nc6XVO\ 'BSbAZ6y,Jƪjt+ 'BrxHjЍ ]feDNw6!Utas &J,\ *"ZIt!*HePr E6HۦjiBn7XҼ;g|ͽ[d3g;3ys=y 9x+ ܪHׄW. 2d:>;PYVoT}cxflضBXsj}KQֽКְc[DmDZ+LKYGm]YJcfl7,9ͨ_Y:$)6=j~7ϓdL-l-,v-)C>6왘3$2ØZ'1.hע쓀- GK9AݐOTr^2SNǒ8tPɡEnX)0UA iOm,)G"fȞ:9d0j=ay;0g(vzktt-z/ú˚anubk{.:C=CbLsj "*3ZTrK ek5l& j (cX+P՜7s,fn!"OU&h[-sHձ҈Gs-#lb̖{v:zIí'ӂ\zu'OR<i}3b`k"mE LAq_a˙A5kƍ7066z سgOH }lZB2 AU~088-[`ppEEEaKI{{; pѰ䤻LwQZZ9 }IA***p֭eߏ͛7ҳ忐dp!bɒ%8w\ؒz*N:rb1tvv-)/}}}x1–2!&(%!ADD!&(dĬ[nE}}=1>>nl3<<ӧO[LNNnӐAс2 6}t+3}09I&1zIY`OizD]?*I_RaJC$evŨ*ne](H v.S]4P,-R/<&JS,S}E,;uTղS4g, ]0h[o %͙ flp7V|u3ŋ  zs V:zy鵯D10{ON|˫"g]+~>qu-ijYk|ٝUXn=[P@@IV.Zn|>z{CA?;+ =$@nQnmHEg/E%d*6{Cab:Q][|a hYA_8I!S} UinrCް:k9^i-^>Fh  xŮ{ë頤۽{o$7}禤Vouٶ 5r<-m6Dٰ;" `m^,7(WCp7BͪZg/N]1S<7w=)-%;f{:{#@9yeSe{_9Va"ƛ-$6@?0(h]z!Z-fmƨMJKDVO$;a#Ϥ+@6/Yeacr6 Ĩ 3K΃☝A5Rm^(rqpl=Z4&`(m}c`'~'4!aPiE Qʪo{Ir?~:%٭o-d#wQ3*` m&[ܧ灐O8@8+i*bJ|œ'O|Ļn\=zmxŧmw백>Qh -_O ;tRBBDn׊,_v3WSapPuZ+R&h)i5:'zSzz3+G[plq< U7I9Sk3J-/==Pf~{-jl^P~7`޽WxQNP@9Vިwf=F K3ֆ34#*m҆P.tGp'21n\YIg8࿟p5KEvAHt`5Dc<3Z,l쌱u_૰ n V|N}I99E]! =PĺBOY#v2ml~x0kzZUx7![S`er ) ?{3jlvH^⬩(aYLz8i]1yιczlC1;Oybݻc5/n:~j/ :.{rZ]QCJAkv:Λ_- ~- )jdym!<|cC!%n[׋Ec'r"v`-<Dž?ډbEX; xdJr+ $]um*j)ZNR4Vȝ=Ag[{0+u@ 4슕rf:9ՌW$dXJUM! 육ʹ~[tyi*/$0X ̡qp qʝWd%`kkcE >s]&A4PW7X_WUtqZ^ܺŭ]N)f}#(sCByAho!PE/^sCJVј1V+ =OMEdco yE {:C7{cP ;E|Fmx]}cS)[HT핆%m| Ymngk$ J'iē+Е@Ѵ [3禞3sGShQ-upi,ʚ窚y*u7 壃sXgmxnsA:4"j=bS @oC%eOEF8D9QWՠػ0&K#M>lT0Zhgd h1~sY#P 6L%=guGN>4;Lޗnz$ 47Z buY Li|6FQJFG+΄#<"b#µ S+K)klqhФb^M{y+'`wn~ABS A=ZP5=qq=ʒ#sU ,;TY UA;N)fqPIxAP\! p3A ,h!#nZ44*τ IDATa Wʤ=cSEP[3bdgVQ1j{t^4u'Zca8~M2PXZRljOA5 8\|=;s~s5VcP!.kC8bIRT

0(E[~t6M@a uBALa`hy+ i&` gyg0Qsz 7SA=gj}d]DU< mFokm I|#am>&A$@+d3쌬{8HuwRV`"T6@?  jG0, ^2 3usYga=Č6Q%fj:<ߍ!2UV Wට .7UmT21sI@2gCmK+osxvhln3XPcn9nI0i*]/[B 8;CM8F2@=gZ^Ax^-Fk"(d{g|Jo`α"{Tt;znjy';K4bu9ح/Igoȏ%a j`*#[Gg̪`5ߊqp+',Q1 06}t΅V#Xn?C@+Ύ"M1cCW9|yhAˮYT#b\ ^q=w+d^U26p(ߣq Ѻ?; 4u<6[ɗphۺL [c2[qT idy &vCtPdv8uP005$zx[ǔlgAVnd_Ӈf6@?E8=rFPŬJ6=Υ˖ڻ[gTQ_KȺ_9ƾ{]yCX*&,ց`cUڸz'geԓNS ;"`KOHצZ³m>S7Bp);x5@g^4-)Ŷ!Tgq\F5@ (=tMokfQPP6X+c{G<@4[MOz?7t4듯 =pu4ovb$MߥT\ӇoJ~:&znqpF;Cv $Dm]ϟjf@{F"t0U@`VLEMmb`GMIf`;0g@MoO}S8Nxߍɟ-7lu"cV@(i q= wn%b^WZIEkP1VqP:x|)*9}nÊV0am^@MU=XLZSll`@tž=jlVFueߙ;Li]``!=ֻGvǝw?~}CЀ{\ s"-rju02 f9( kDmA# YXr8b*0 W- 85-NJOaRR"V.\@ 2wjzOievT lmtugp뢯v:ZXS9[hl#~((% R9WܼsC e)N4N90$~I.n<)m_V+2A0DaN \6K㶪h::F7C=\H^NMBi~5Sͧ)dҠXC}6~~[˛8|._+2ctULZ~ +U#~9Kݶ/Т4ْ]t1}1uك]oB2VX 8 |od&l 'SUA3 S 骚#o++ky* ys$~~&^_"'?__Ň>!0~~ @c\k+Ia{8ew 8zv¤PgsWX7\ i?ۓRzGNi31PxʷMM,ؐs. NסAyϲ cTL\P}{5Pvzsx+xw5;/bl^?ܭg8XįOY c:>n(8G-~bmtD63ˇFq /]L-n?7rb-L- g ݱP(g>42BLQ= ګ0zdoBv8S^4 kTN h[rrK.u%_|ΜkLsChoFrH}iuP΋Bhy[p9jzIԲ(7j[pA=- 4>Ri}i89PJ7F3Pm4(NZ/BX~D0w8sZcQȱ"-K#d/{[@1kN5:[-֘*/ mW_:Z0ۃ}G<S|**dOy쌛2DuàW硢&&3v+52o|v @ AhG`Nfr֋Q{uR9)w@8}?F2(%8Cs']zo4|)* n􎢚V&> I@=]Oԍ-46@?8+ۆj@A9|w%{x}We guc֬C֜W>y(P`H h9?9Ŕ2ݺ'@;nsخts1(Wl¸/u_z,A e Vzҵ`s`yzbr~:ybS6d2VHV+r[YMNQ0y;R){ Cp7`otzj#59Ϣlۺ,U),-5Ãy|iY 6y+CS0YLzflՙ[~E a\%aFԥՍk];|_G'xe177/)z7է5{CyBSzևA(6ȐMMWuMFA6|Y6F P8p8HIO E!`,M=>G}V` OX: " ن/Pȴb-=p=˖ ^ vT@3|aCaߩg^\C_t .Akd\hM9H=R|aqҸ˗*yPGv觊} 2#BGZ>WYǕ.k ~ԭkttQ7sJ^ݞl%!=iXyj䠦At~*`%9 +2>Az. : #}G-gk _ $P;8 L56SPR*bJ?$jSe. mq@Br[oD?M9g+ ]U##er屪˝pLϗ5ռ)Ql~Y!yxtl?&ܝJY)A(}>w]JוsTqӷXoNKs& |S ۂĸ$j|ݴࢰ`3+qaŶq11Y 檤PeF?G[}j[o'P8Պ㽰Q51bG ¦8 Jeu 2q_DF,5PPӫ=C`;s☚6OqP&ׁ/Pլs ?7PtU_녪K(C crL`cCK]&iruIʙ, /Cwge4ͮ%Xwbqe\w `?U}K<'`m@KO4Zs?ŲUmgo(zVKFU)j&Q?L7 35c%/&"hpdv ;_[t/ 6^={d G S pifMNlT1RZJZؑ-a@VAl=W zB?tI}gc.4cb  ,sT3 Kܘ\U| TM߸mUIj(TA|@ޓ6G}Dm.[b d'F5:5]/8$8gy0N-6@?8@Ph/"tj煊?ܥC تUiA>A^I{+¥49V ]sffvX( `WGFM^S(t} OZ[] YycLmp4.?H6]}W`IlWfk,3^)} ^MSHs9"몦 Y>`W37 Ϧ髉dwz_Y@̚l PikS5Hwʹyѭ!zXGQxR8y8|l~ppSVU&ZS6`צk0A|y:b)ɩfhj㊣tΖ|$ žw:m*,W¢Qm&u>tJqYdoyoRԪ emRi_Ime,)Yש(Ql;pԴWqU9]\Q6,](_C;r:0WS6GH{ c ^ ճmid8au,N8)ҍeYne4k/fͭF΍R5 IDATdj`x2(O%r1Xe`VP_VB)뼪?W?ګf夔+l+=)AՏ0nBbu6ZC?y=p`"8RϛA\31n44X Rտr^͞W̓[nh**%ktk|hn= =^I{8OI5yBޛ ^AO`La$iQ4Z@} LO[QcLGH!(WG.Ua&(`߱(L^-gci8vaߏA0VϛőcC`깩hۮY*(΀30W^U]M a褠W{}W(n6N -d$cfƄ2+0,gk\7ĂG3 b\r ._Ak^,$W !$aONzEU6zUMAkl~`ZҦ;rmC[c ~ Y&{( |29O3+Kh#Č 4oFiRq Z5&G5AxԔZHr=Cg8%lFyiں^Y.Sa~7o*:S)UI{g'sv*UHA g T0[5zY;ٺ}:uWV su:pFAƭp>lOSaA)Az'ʚ ׆1RPH@?iyz4? ̏ xL+poe}60H&H;Uk闪Zm ;VH&90A]GB σ `fJګhW3gx =g`u#N^AFE,G:Pҳ|&)l\IǪaWyv Q0׉b4I, ӛ vcP~ xL3KAvK5;X.X`bsr^-`ޠ:հ]?˫谏Tۧy]F;ɂփi- 8f𢦉$aTQPUZ^z"p^*ˌNuHN!HԓcD8YMsas5x.c ͋M i;.ԧ##~ jOO([۽Tawa2Z-~~ 7x__|#x׻O *Xt8[6 j):̚48:_!E(Z>aa4v9(iwGXn4Ht\0Sѝ j6Z ùtSڹq;L3c {.9G5ML|Gs`%"K7P)K!tfpxٽ酏?c?oo'> \]]kkof|~X$tH9HNE4 ;kCMsjtreRfjw赠>≫z!lR8'R3#)*W~g  (:cXpzZ C9U9& .xL/"vsP'P@"=1]MV W~&phAsm&p.(q}}G?Y6H>7Z_<y1,@SYpd')BNr L cUɓ*N3q  霓5G!R׷"g ;|Bjh1,ĂKCE? <7@(Dx/QϏ@:1fwihWT0S=X^b51V gY A![6Sȟ}]:ŝkG5 I΁ps hx{ރ7~7S?S &Z! 4Sjq10QR >#E : 0p`Hlީ\"v ~ b[n?URީhw4ھkwt}TeܙsDŽ;'`7/l<׀^|3900{AWV<<92qS!HJμPN8rǨ 7>Z6CD/$ O34& d)RŁǞg<n' 7|[[%Lx ^UbJJ`b}(0 M?n6|jkoOypy+ zd;Ba[h<׀?ԧ>ϣapf Y&GiP }tm䮯~]謢ixl ],G,8 pzBu[jcaNUz ZMܟ_Sz hm]n#qkwcG PpG\ M%<e |/[ROAroӶ48}yC_Y5G?Ї>>y??W_}o}[s?su`/֣Z׫gDASPftT;򢧺۱X`!Lo0[tAV vP#^xI aW#Cs ~G>m'?|Ʒ}۷ỿC?C˿KlǰA1^=;򅓢=aթ9'wc*ש\ħN7Onq0Su/xBOjIk )?ꤔZ%/$..>d00g_Z@Aa!90M~PMƇgdؽ;*ǽ݈ }F/v<~|韢-{/z׻WW {doi{ur#yuSXve/PawBF9t ɩLkqh@Y0_= {;S|zG}aWZgvy \}Ǐ`•)M<wmϾ/P< |c{?y|=>xoq0mvԇ:Ref0yejݘOyy\akL%݂pIө!V3zm=S@`;i0?q&vca=ŷK4׿N+qtDyř3l\&>'zVh?m~<x4/">𖷼gtw ?v GhcAq`m$cLPNTCup$-o'zޘ@.0+n1XA-9tdj<^1{#humޯ#Dh;X~iLsm#p:^?1A8m-;`=XwL=V秏Z\=~c?\OOOǏ77[ \5ړfJK5sh2gpWHT3n ! \&\Jq.0 Lt0HɃڤcϑaMC^aQvLs ~vLs0 7`GʹǴ{ (q挿c {&"š;`Uk'7^3Piջ\獎^85߳)Ygžֿ\}{~~*[uLNFғ@`A - ==e hdcgQ S?p ̷hΝt;,n@8TPE`Ue?50ꕓ .1 '@IƏyGXv#7'›'"G]^2cc]L >Qzh+ܲAN*f`羟/~{}sÝ9ms }s;\^^?Q RH ] "kS' g:eI2p3Y\v ;D1c®Щ^tJ`Mu?e;J&r&=1T4 43{82za<pMۉ"ٞW0K_a;$ico>?\F*I|1p{}Ndjc}ˉn_Hsaj]Я~gSKUQk0 nHnXuvvֆSjiHs|$8tۄi!\ЄHD&L4c,,zj{LAU+c;[A=:q+|1Q,J^w`8̈́ca7ĸ/@]of3t3cD;f0y {jh^YGx7Ep>t}1:9p0wA@3Gs@s|++5*-c}4y m 窢E vL2=ꡧTQtBz$m(}'(6d~%~?K8 YD/x5.ۅqKD8T8{vCQ.N4 LG"h#1B*LiTúHޫϓzE[h瞟ϐ EM$=nݲh흻̨%;PqprNAaM-;H Mi8C˜ž;fOӺ2d+QXBoAYEC 0OŲʴ#t7ռ'ȣT 48XWL*_8 7oΘ&sPfQUG/%w9~xU}]H9+b$e?m~h(M НOTR+hjֆ#RLFp$Ϧgó.zSI Ծ6 PveH# @'о*c!lD@X>fѾf'4,gLhҖH5MЧA i@ k>6@5cuͯC§).C;u$YVŌe>XflثJPE=:)#E$ՎA"ÄNQPбB)1-7qr +wbpfm3yG(; Qe0QaK.pSc`YCDApӄSfgpfjU՗~)zէ*l73)byo"n fn&HeLŎr-r2k4fn~ՌguOWjhNw`'4 rXRUa\Ω^y=CBxx({`]8r.\N'Te"0S5&`O=N *vq :U-鹜jjWz5#f47R cϟTSw ̺-ۄfq m Z 40n So~NWG"p@ j{0j DꞲ!( x=+(2_<7;{ۘO,?$ pUqt,Dܶ=ܩgXv5w\Rk='GbQ%-&B|SAL*y }Aj"I*.m={~3RRQBp=K-6@?0,Uν@kvptW9z`UsjAtyj8^=gD-y40Sxo_L>-o HY+h_Xx+NeΛVR0#Kz֓zyUOURs!ȩiO lpޅI j~MDMese$詪h{"̈́3<Փdv>.1S#ePqQUwj:oxNW?S{PjUt^X4ɽ! r>'rǎ4MG\+Q*R]f,hphwp&I3P;Ԭ k 6Ŭ_s4cF+f4jwq{3c**aIC(ºPuU Y]C@y<-m:ݚɼuV_Q\WS =[0c;W8SA{JP5;zCb`"om =uN=Rp mŇv*>I$pYAT}}}WzQɍ#Aq5K#!Uѳ)i 77[ڝ3=~KB@m3I5 ~ `eK9 ةg4`o O XK*$olKƇ0N˅!<ހ4 BX^UF =,U8ھA9w^G.vY!jgp&]@37*(ƨ*fڵ{>nqãsm`u8ԗEYI,2PC$~swh|+@?tIAt^fkM[ ˓Y"R=]žT/2ŗEIMIEOFB OvQIزϕ+ ݙӛdY |I&;^Eg*l~' nfkMA?eZCa}-A l\(ئu™&Yx1h߾mo,r03f^i ƧҠBYo7Ԟ6p^;TWE9zn˩n7>[l~hhb%tNn$H4$G$P``סD`υvu|8{#O-GuսϽA'[M)(E A "(R 9L9 `x+EA 1&&~ IDAT~1D@$+/clc8r{ٻߠjU}kժg$\Ժ껻{]w_UKbs!IzzXA -gU=KAeM3esZq itEq|UjüU7@(3eވ~ `pG:CZ>=H e/3AppF]/iWaqT(2<:p κ.@WcrXB%_~qFnRd(ےa/ zŇS?:AZޓEp+i8dY8?t"mC K8ۑnu:I Hyֺna:9-]933ޕE?dW a} NBL2W3n;jSmSP+z |'ԃf.TzZsOEwsUw`fVx(ʊXXoÏ:]S*,#e}2Caځ, lVQ#l)xO |B ꔇjmTO3 t:۠m"(c@liqB㎱7]b,(lS4?$}ejs$7(BGŞ3+h +K-2̓m< nŬwܻgW -TVǒs zҋzz}"LS~:Zy"\7wcG1 V8siTs0]CUL=Lb&>4d!]Se'o{Ft{H<5jqY!ja,'t?=\qj' ``S0#5$5#/OEKR SҘpr]p}[ g;v";~m#i'5} H]OP0^ "!R=8H GsV[2HLx>kx 2ݣ-UA{}1e>ðHu1׆ R;㕒{JQ:Z&EHz&p6Cٱ,o +*A{y,ӽ#2V 3RNU1NPO.2<*gOp)a<{X%Fh!6!揺F[r Kvt2@^l;F_DL*p Ärlg8OrgȌ4]plu&ܸ=i&9 }p.iO zES(q-M[lr$K}\Lr66G𡁷6% Gv؃6S lf17Nu/O1flԬ _y7_,<}svZst'$6Weh~Q%}F53К[4¶F `k{Z ~'z?TiXs+9?%ISpVR]kWYB#(` p"SU @ër{(hҶ+MUԻ=M=0 /|&̲&yR "ql!=pFkL}քeW$0#y - hs!s <1}h 0彖 /R2ȴqS9'7p oLD zZZ߹8; PtX2CJ:t"Ʊ`Z&L.uQYU1ulEb3f G ܪV=O/;7 .A;`hP{oDg)u@uƺК|.oŠV-iLOuOHO931}(YiwD~[49RF]y[0S %E,Hoxp/ׁ4-u@lr'kNjGLR׍ّ~hKW0tD_'ܙ2RVJRnyqjN8 %/3GjD΀ԅ~HhŮzLFۮ6X5[^eu+i7(<tJ;PxzG2Ep!mmѪ=ֈ9=a>2X+7`aXy0`qƌ(9aꧨquHx.n @%mZz[$ E[J;uC=_k-έr^=#]s, MVGfB:evYk`c:p.ɌfبpT\# R$P~) 7nh _ϖκq"69Ͳ so~ss=u.CS¾ 071}h@!>q4o?9[JEU XS ӱLIEw4id`cZ?jGHcU4 8QgT1Ú| Rz8ssUfl'V+c8UȬ}iT45߾Kc>KLMr-ˈeMa}{͙쾷Y+zFC*h16Ŏ:I5K-$JBS3=۷ nlV(7cB[ULP.jZ3flhfXx#hOHև)T445S4A|{C=˗/ڵk>#gPZR3AB&?_cbþNV=hB!dHo1sG7= v`fyXpCҜ)JPԼGʑf̠ũ .k7KS&y8Bs2+ֆ*x, Bk @񖷼<0z)]L|tg^g"az} YҺ݃u𖇯aտ~3hEDeU |.t0ne@ ީ7h jD"wcz}@ۼqQZ(̺@p" 8%8+rZ z]N"ن DiRn&ZM{iWb7~;n/]xXb N|ߏwf޵5LӍx\$i{JT"B\S hTsmFzUIIy:J|褖eh5D.lul`{,FiF_ j}VլiC2w r.g:=72٭g ?(Xt[?-|Iz/1[z8K"A(n_35zP AJmO x[Hw,+H\%zw.`FKr4P1gq/}M* }զpvElu 7XCvG mAf3f+1}h(~ h*}P{v+b_I iW45Tbs 5ԍA44P ZH*zbVpB+DJfHoUA[=sږԳ ,OA=eN-4];zLk@iЇtD`Eݱ:[=VI^1Go{oH}c6lX5oD y@i`!; V;t38,`X V8GiT4fV*u14ėzykq g ?$PiV>#r ؾfW<6ֆzGJ;CzY2:=wϓ4CE'_ve7uhϽ`fDxY_ l`*`Z\̯ՙM0%U7 6F-7VG'@:N$h^>4Ma}'G 4FAż? f1A05GyɾC]f@op7;^,h#N1lm`.n'7RToa-E&[XkZdA/qCPoG0tA tpaקjǙ+TcA>J5= }z-![H7cTn* G/݂du k*KXcb6. f>lWDNA*|9+Y\׉vYdE#;[x gK6 U( ڏ, R ,9 pi'.`ny`hAbFP,?9 c~nǠsЇe6;A-~Jnx{"چnsR%\ZOũL5j/# AWהN@N1勞ȩk'!^᭏ n LDǫ D!9+i?݇fM;0u=Ҩ?dé|nd] \謎[]˅Fb!ȲL7NhJntgD/rgjUTW+>5>4)J+5䪲Fk}lFIoː^t~w̴$)҂ i۪]: ۩nRB:!FApf$~b7G>K4\SnCPdV}?׊tGcA=l gCu NSؤyw^Id0ȀV'THɇyQ3ދ@Eglh1XsۻY0RМO]yPZcЩ-K*ޒi"" !=~qk6 )G=ho |C^AYi~@57+[3r> ޓ6wgUe%޹\f,EdW%nK9N,pjqEGwpCC+GZđvP8 _/",<}4a#@ۊ +hMR)vJM(Yx@. 9I<ɀe YմZ*"G gscs`H[1o*0UZS"~^4A5@'xI]eju.Ͷ]mǺQ2˶hI, o[:a5 ^I:Xe#V]oA)sE)+szޑf#yfI(I5mbT֋XQ}lp/;vsMm3t @ږuwxhfF*cqڇ4E4UQFiq+󢹳}Ӑ'Mbk4t?Q`e(;lslfFy+IGr> ef }xՋ֬8s @%&_C(ۛ,+уzJYmkbMu iҠЇ3'Gu@7jvU=p6G,UEJ-ߺ}Gh4gt,A:4TPne?ֶᅲWᶃ4yʾc ߦ騟P]~"%=ͱ{6@Ў:{TW>պ2O{4Zv$#G`1*:)ios(I3%%POH6wbPl%m:LT/z9@5G Ys fo |HFGV#kgh`RMԞlh8xՐf KQZ m_(MY* F5o %/C9k[,5CAEׇ֬T6TOhQʄkrS.(-xo6G`k @u)A_Pn`-A:%qԬ۬ IQg@tnMg`4(l tdZi>t-1лزS 媢̠6)ضhVpxEa/|6JšiuV;rw`CChQ.a.qYST->CYXYʖ/hru-R7IUH뀕vÂ%BxӲ#H6 /M`NLgcw>@5Kkq;/jJ`ʞ}qv9>[4y91u4saY*q*{UdTt;_4B]Jt QT4}XdsChH dkwiPl:fHIה;}G*=_7kX;rcn(} @EA rL9usQyuVY*Ҩ(o[RY"K}vQbt(LЃZuj4eBlo9N` n E6 mv;Rͩ>t^z%Mv=0OW"VЇnw|@ڴWO)iSxKIDAT̓ރu &[#@W0CceJ@sN3TzfP]@Zm@- %h=ytQшF浪]{z UE3XAYjwx@,dG PE{; DYUϾĈC9c>@skvW༳l]ټ:SWakUMij_>CHQ͑Asy-k9+ iרV͕:O۽/CmOPhAl*Ӫh(Ch`6R!б(upK:pgh(5cƋfЫ^RA8z@A܃SPiw:#Ck#gqO5m-J `E+dL"(\A>KueotmlGf OI1mPv z): FSfʑB6`1?ÈG-ҪҎXI7"%4&춹_r@ZsbdZ=0W$d&z5^ICYe87t (.})9gC̪?Fpϓ*-K5Ay'-ֿ\iVTILVUF$atY8QYArPV~N(C]B+isaQұ5#wʜO8\)["](EBύz7bH͏)jq/M(]PK{1; MY^'S?kJ)~S]gj+Iʺ[do0)uda2\@zJ^40=ei_@@y:uv^IceYƈ# y{xAkCۺQ7Z?R\/ bZ!@+*F2 QX(N0/e;6H{(Sgr,zE~m8;>t~=F\wƍ-m-eAM{TYsk?KrvTpq'A:^Iso ۥFksDt@Aekf{KMhըZ~-MD{W5iOsDW: -AXjgV< Xʻ@Zҁܨj{zLA9M>,⨧M ansy_ܸ~p{_g-Z 4-  PocmR9V[}FOE8Jܛ3EKκ6'N´@v׳="/znivZ!\y?=8StgnnXؽ`Ņwa, W7 <x{ \-wny@ŶիgV΁bE+CZHFW_N)yDF|MHm0>˦,vֲ @YO3tqWH~^VMgN)# SS*µP=G :~}`:w}7੧*pGxӯ/@ݣӀ:R$M}rĠlq$|gfr@j$[踃4H4T"<Zam3;ʺ!8١vƌǎ.K9G!}Ы6EhOQS?7 DVR\99z`B'Apq?Bvf*g=SCՋ3)q߶¹o/7wo:O-1}P6\G~eHTQ&G&-˔E@^ZUPf T&Tn{: Ia:n$[s8B8ˊ6n?U yطWjJ  ?"1s_= ow/1}`&S n]. `Y44?ֆ*D2K(&ne_%MMGv@Y2 t4 z5p1w F묢S']^a/DI`{ۤٴU2>8 7s;,B\67/ )DwoUAOWN1:6C8*Qnt 2޴!L1ZWLeteqWQn: U"/K(:M xvnKKԆH>>C΃:P?*0m\+ˣlHɊשa=4K ^On ip^ Dx_ղRmN's*گ2#Ǧ1Ah#x0Gu-Ҁ PˤCBH҅5Uy൝ h kjr,dx= T;/r/p: #fl{K4z %wB*;FB6 e`abwS i`fabf{xWAK^ֽ%}MC/i3H]09|hU^i3}_mpY p*}pi=Fv wЏCN{b {Ъ[ýѶWuZu@t͌vMWW9E뺏5LT[Dn>1_2: :b2 lLb;`=5ω?ԝBoYIxel:Ĩi̓{13:^/C壦]TL J[)\!ReA]9l]~jvFp^(f Z$4v; /@Tvӥ?Xpe u`L'<5pVլpr coo2frG=N&Ga9MTl/WMS> i @th Ru8 M8tu?SF'{6 tS?w6lG!lcu 1Ey|>0# S|Rm{X}썶" WSEBnm4MDn{=66"5sPU4܊BOr.u6"eӿ"TpO) 9Yh# @ 9P<٭d{Lw\+S޽zIgPvV+h)o.} n.ipGkY,m:_K:mֆpizf +tC0 |¨Vh6A%&|\UѪ7闋Ls[mMmsEV!7n{i{K!QG>t:޳ħ T1T8{i|59~}oIűZ!RH(M.V8&ҭ\Hw\BXvZ퇄+V+9:m3;Wn|kO_Nz˜kMZo7zk<ԼZq;|7/R4ݷs_ P= kyzJ澮IyG SYY޳IYtaa*hu "1աܞ-vqa5ZߪcڎqXm=˶z#胣W^9 T/Jb'Ls] w%-7,,ݏoJKylZw+i{,yS]_&iumoLy?Ʋszf%mI"S7gKޕ}3m 5.(8(?7_klNrж,)C$yf~C_=1}`v^@ED,ʣoY1?zo-l=#X(;Be2i_ O^@ Q'G•tk**mEqMÄ~y0Of5+qn)`kɧ9){l/3B9n}zg-niH=?jİ8 ^ZkjKMK$LH?>uofw72(,:v=J7WM:{.\˧KumXINօtʭIdğ)A`2;B\k%ؿÎY*mkEGG,yWKɟ/x_J+ʪ+oo_#Fqư8n'?'|?yWC˸v>輫eڵkmǛ&|S:*uoo899wމ;* ~7~˿}c]WuN›(/⮻:j썷-xGpzxꩧλJM o'{sa/>(\rooyWi5~a\|{UY'x׮]___>y]WmN›0;jtg|#olĵk}o|c?c?}UƟw<㳟yWio|_yWU#Fq$1bĈ4G1&W__<|^xG>w~wpjq ~|3;N?x|#> ^z%|C=xyňeYWW?g?Yzx^~'vmxߌO|q ykҗtƈy#^!"xއ}cowcv]Y?O4~~ p-G?zq zī">O൯}-|A|Ň?aFG\r>,>gό 7o<җ_|{oq}o??wx;{7<⦎G1&G1&#FIczĈ#n1bĈ4G1&#FIczĈ#n1bĈ4G1&NCIENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/gi_c.py0000644000175000017500000000013212253362407022146 0ustar georgeskgeorgesk# Scorer function Gi(z) in the complex plane cplot(scorergi, [-8,8], [-8,8], points=50000)sympy-0.7.4.1/doc/src/modules/mpmath/plots/coulombf.png0000644000175000017500000010377212253362407023225 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxwxǿ=$!H"UDE,\kA,TrUDJzM6@z eS'2;|'̙s~}9B|ӫ0A=]{<<<<< 4O/h^ /<<<<^yxxxz)@RxK 4O/h^ /<<<<^yxxxz)@RxK 4O/h^ /<<<<^yxxxz)@RxK1 BLL 0UUUpqqj}ws㵯!`ns'sț5Z B]u.3!1bDOW[`Pm#$H,bI,bh^UoS~I-:KbKRD'N\c.[[۞A*}wsöO(n#|;#H >?#ocu!KOKT[snA(3XI 4!`PdSܾm'"qB"*T&1Wcl+1 EJ$? "M#t:P@t c=U0(wsiIP_NTlC{KƐC`;*:_@aa!^|E >Ŀo]6Tj*XU0(wsܷH RNAZ8 u@hXXT"VsP}Ӻ=.زe \]]|/䄆ׯ˶FcV7mo_ƒ Ah;Xfer dpTӞZ~~~toߎ7v5aaa=]r7nnmbXzXbAkI=~ܑ ,N)z@3B899A"{?_"Ȥxb!D.|۴n׮]W\Ґt Gns.unW+r)ǹϩ=6!!5kP[[ބ`POU@,㭷±cbȐ!񁝝v L|<ضm[OUG9%j|E9O/H}.Cq 0Ӝ۴GTmJws!'Ip$JJ"@{AΧw(uW8+92<{xC1ԡdK -^F . A»ٗiH[-qV_\ܚnx;@WS}_ !0U  xm0DEy?OѴ$ϴj$4mn_Fef잁̪L;֨oǞ[{p:48lЙ3|ߩFZ+Es)466L9vb.59 QYYU0(Ln]/[F#*S8:(>>cxXʦJ3 __+jr?EkI+< 7Ե|c}/ fm_MsM8%.Iuɨ'ƈ7PTEY(]w~)CZG{&q#wFSF*THt$6ONxvȨ4WS1+ u2늴F|!C 0g 5V5I惄>W8~1gF2,,ѝ4חFYw~IﺒQ$x =kXvv^Ć [LR ik XYad{F|rQP\mт 't{i׆&)|д5,m{cCD|r8Z9bS&dTf]N`n2<{@`&@ٮ2%?$6o~8``xw̻ʥX~z9'e}G) u`?!֖p Vtu4b Mmڀ70cͭ9#@-7$&]wEr!߇Șx>&)|дѶ}+ά@a]!|SpZ[ [goл<JВٮ=TC0w1GJH*$ALR iMʒo`MA`ZbW.4Uֽw2 yOQ%Z lOYh Mց&BȈ N tvKqc^e}W}ZK[;Vc*nh>Hhڰm8{nxu+^@>^;i HhڰiTp ј1jEʽwJ @`!Gƭw9;{oNI 4$4m4b: օsjP!mB{ *^E^. ҐXyYϴ>KP_pP@ X \:^481s1Rksk̋a~ʸk !o_Jw ND9&)н2H,] @x8pEˁ'?a=(W̋ؗOž<&9VzV:s*TyMR{]wϞ df>l٢Uqr@$&`Xax(!#֊2w<} WB|u+D[._nspx ٲ$ ؽptO=˪{1}wF#31™3ZO/]\2;뗧.I0m4\->q}u#G~m>f홥sf&,=,}F2)LT{EP*~hj>Uf B\EߋD9<HԪ@#q]F._ֻMpF~Cs'[g?(OS$W nƌ-SkձA {-L'BZ:r)̄fƮR7Elߒ{r>j||c Y`o1@ `<c9w&)=$ˁ?3 x1ulLspꊰ8[;!/P2glp!Zp/]}|=*U+NV^u~**0f!z k;z\bA¿nߦY&;_?4vRhuA*/wz@)S Kp& NIHVU^] ; ;|:SIV "hPSSˑ={­[ Dag1=$$X{r%K={kiVcb1lm{d3й} B >YWMÞ[{p<8fduw%'KPu ց !Ki#ܺu ׮]k^.c077L&æMcAt:(Gu+c_VL @.9ƙDijT$'៬}/iܿG0oӕ*1UVWW=cii gg?''.fB|4# mn[QPKLH@>azTe<*8wBBBl2|8wƏ߾ǂ۶^X,m!k.ۣoSq~ybfFz,]RQӂa58u@!CeerT8A@Mj*jϝCutt7a/n}T t2urriG32`=0'D :!3ojihLW#6QXXoo. t_F5Ğd,dJ=JR8i|w!B!Iۗ464tO$- y%*  sޔ2+@V~cƐF_^^e%%ҒBȫ<9`<//BNN! ,^䡇"'O&C%#V͍DEEaÆٳgE|WdժU?'{>M@ٴ LRuH({[*XZv/}r򒓓IW+@>uUV/]f'NF BHo'"췲"_.3gM@766ŋ???/7#G\N^{,Zq ,|J@?!62`/8x<=:c)O?ZwwB֯'d83 ˹?_Feϯ=IBBBB#D*U^ .]Yr9!iiiٳd߾}dXN̼F#@$17$Z# $Æ #ӧO''K,!+W$=ٳgH||<%DΦ&McfF2|8vnE&'עXĒʣBiG<<=!˼\p`Ȩͣt!,ؿ`gГOҊ\??b(z-3g$zSC5B**ۗͥKj$ҒggBt"_~IO61O|cP\L5v~@HH?sy#XϺo\x2$D&S\.'uuu$##?'~!Yh={6>|8#Z9 dܸqdΜ9d…dٲedɒ%ddܹsۤrpp^"t~>xNO۲_H,bIx"իh6}rB^x~k\rt k]=I{V,k!D"ZA//zwF"!d By].i[Ws[GwzϫZfL weoj?]|~ڐՅ b ̆T4Vt(FO|2Hȑ"@\lذ|ddΜ9dԨQ$ ڲ\@F4i?>?-9r-u#rZ. Ņ:0ʸir9!C}'0KR9v"T"k-jԨ;wqtVuzrCׅ)+Ҫ>h •BMvo/D+vxkff(22!1cWVaer lb|gZvv_M7?`9+?gϒ4 %[n%| yWȣ>JƌC=kKŅDDD'z,]^lݺ=zD"RTTͺmm%ۻsϷR/X@}E!γ!V~kײ7o;Q"$ܟݔ)|h?fbeTU]1rH 7Ko߱BR+/#q>PޖJK !?디nϣMǍ\---$5=B0! 5kofns!=tۗtF>t111k@V 8# 졟ӱ72 DFF23372dZ# Ss# tp$foI|%'# HGwwf$&bE̝_6|mǠ|+>{6E&O:45u+e23W ɓ(X*p%nƤI P Ԡ؝sNᰇt18ky{0J IDAT&)ڌڻ~>x?֏;oՍGv6HBT L}}nsã"3=kJKig҅ Ǩ@<@|1c .@@9!FnT< S"RG05X5&Ə}@@3S1 ͻEUu:_||荛e7QR{l,tsE /BB7y2wT9L,C_}J 5O7 /m_Z銻;Zr۹wl,.nݢ3!j3QH/$tM?57N>z6|+;;k77 < JKOnSVBP\\E֮a@` 8шBhh(aoOz#L6x] FWoiC)v G+G,Y@ɔ@[-'+gc1v2]|}4䞁T.Ŕ) ]iohVrn'觉Zπ 4 aCuKGl6ϟ$agq@_̝ ???>|/_ƕ+W P]]077v!; mݽ^^4)ii6F*k_8 "ȑt֤$``hYifn0}"MzQ;8ku",7I2+Ƽ˾}&Q)Pp0_$ - [pbW>~~m\ _ &or B! L&-__\pqe+++thxxxh=U;TǂTƉCTa/bo KF,s é cPpAgJ٩hCS՚8 3T $$ZA/A&O =y@+Bg=`ֵb(<z󁫅W#Gj_g 0}rRǗr9N={ &7 ᥶=ZNC<+o_j :t˗/<''' Tddd>,6l؀444 >>۶mo)SSkqtw2V97vG>_A~~~Ndo(CD@c#ru ;=W @*fti!HK ږ..?y2 JQufebXd$=MH!'O׋nYpqA\qZmk%&R_р}X)^L҂f$|~]Se.{C"8z(;.0qDèʂ[@U#$Ll-CRUv^9WLHBG.00gu 0cH9e71x` vF:E?٦)2|8ȳgi· .Z ^ [g}}{u[KĘ0I% 1{gDQ>:@&LԂ$˩@[Yiy ̳]9VI^% O?G+&LիW#11x? 115558}4V͜-="Hi[74P#N#2|ah]ui>gGGYXHs|u@]B!um{ΐ}KKF >--(XS~ut`y@k>Xw7FxЭ,JifF$ZS0=Op++- OOGHk2n"*X"pi;/^z%߿R3fڵkll߾/2 sf6\\MT\iO[ ׯkW:(޾B ;BܴPgh` jGɵʧk>4c!HG?U:Fh/+{y6Mrx kk* 6c\F0ڇ2>A\p0x'1I gt/A\G"$&&bΝؽ{7JKK۷c>}:Əvv)!`&}ҍnYFQgr %tu(Ǝ^i>;nݢ bO>{$a03l__:ZWĽ,|1Ȅb҈i߈T#T iA? Bs\*RVA~>fgDZSP/n=xs}sAAvڅ;wPSGŴiy`W׷cƺdtṴ7G4絽}ӭBaCISG+GDGF #*..:++CD4yR}:MO> cw)z ǎȄ޴j ?ǀH8Cw ^ظ` fvK͛t0ڸ+w  k b ! D,46^ APihi8C#-H 0IV$oAA:w @ADEEXv;Yfƻヒ[nA$᭷ނ.]e)Cr*ж9 5JZ[*cݵuʥxy˰T>`FX4h[3Wypicp) 2@zZrYJ 0WL ZR}ryۃA] ro4Lo7۷1j(8p/ 4ih:Aͺȸ9 [1n#,,ڰTC-**hlx`X ꨯSiIآ8`j  Z7>9ggeT;݁U+XF야&2t!]mQ QL'3&)ЪMImۆǣ=?|n# GL w3SW^WB I*?duL¥{A NO zv FVuN-^KKpF%r;ylxTuut~PM a 7n .йϪLDB |Bc*HFׯ_^xR}mÇuy5|S )i"DJ +irjr0-h롼̘sCj!7:90{f :v`᷅hNx7xRT6H(*A*b GDXȁ(PPW`hUABR%лvk+++=zK, .:ǎ6ATH //SMK 'ErAɩR|v3>f}|f!,@je޸MW@kw)qFj*Դrq. 8nnԷTUg 2 f8ҕ4Ğfp$!*i`LR r*ׇ&Zь@k9̚W2 o=n+}@H9dU샄R߰4`< `OJy ss\5붴4<䓐dضmf1X'`~# _yu5tݟa2`-+z;:VPbUk*d hA›e A aa:sXjW_`,H구χX,ʕ+3tlLN22軿I79Dq`n0n}De$q~Z2L:@##{N8Z9bR\4/.#iodB ZO]gD ZZ ;nCْº Ah;; S( ?X3f /_uo,%3sr ̛>V"2 @B-gHNAPCeBpo00nx8; gbu5uh_,QZ 3~hpv6 M`$LHT.2`) > ;*-~`gn_J՗z;=*bO?4^~e{"22vn) *ϝ;իW g b!=aAMLgq.@A؝M)@8TXNp6ZБtF(5j.0¸8}5t˵(Zê@̓Wޠ\2j-E{t$QY+*0n}'Gz߾}3g~GTWW?Ѐ~u;NYP1^*⥗^!ׯGByyB Vk11-hQBu45[P 4U)Film{*$ {"9"mcCŀQ:u9N$!,NɚVT"GKӃMx{ӀjsTU6A›崐h\ 6He=*Ѕߗ.]۱qn. XL-_ߎ)#w܉4<] mSGԎRfgAobbb:r^ts0Ε@EE1A,rdXXC<Tb @\1]O2St4jx葑͂Md1@{7>>>kˉkkP'''H$]a޽8p$'"20^+W>C'jj* tO-GE 8~\~\}WQQ B뗘sy OO1_Q]8,drpw./C\^qHDmAC\?X 뗜tTXZy- @~E>ޞwHM xlX)@C|U~+;6"Zݸ @p6ʓ ( -~t m.QAZ;@Bs5`͚55{BI$=Dcc#y+B-[F6nH9BΞ=K-ZD^|E駟v;nΜ9]u+!!Ga̜9S  "D&XϥKiwkBHII!$!u.'-1a7Z e'BkZW_,YB2}:7RǞ=rKvTQA7EEqwg|`Ȧcѣ^PZXMZﴒ^I,bIپ2αy3! C)))jW >۶DڔiӴ+ۿ7F,XA.kkk]v)6~x) ;Qg4O?U^ᇕ9a!nzp /׶/0umǰ_H4~y l0ml]7ܯ^!HWZKZ'rqֶ\wES0-@ ;iL) $C<7nb9`ivABi&`U9T,7:cT;HDo@q7I?)¼c 5j`̖hɋ'K/5Lf& %JPWlm6D(dr{pi 2T ncC54?* 扫p?)gr@+ \HM{3C}=pH{P N{ 1Qu|2pe)Oݏ1 ۿAT`QP@¤TNA 2  @,,j\BY nLiD曙 +f:,8 LXE)H΂:~Kz&'uEu29LR;$ɨѷ/#Brr2 AǎIfd=pw"#>ZK0\mRl?'X3u ̅^1lIN!V:iTSpvvB6k^n -o#ț_Ꮊ+"B HW{hI,3y мmd:QW]h(c T{!"mYZWWkGk)Җ4:L1\][lASmg横BB_Z{A‚_IeYhHlx''B{GF YKHB9#< wWAZC}|`;&;7I t a{Cݻw O=[[Ç4uӧkuNc1z c#ʬ y5yXyv%fzJa(d.\dV酒N蛐+ +BɁ.\Ep@/@g:2Jp[.& rP\ZiA D@CkkI=@w2!PQQ|*اOӦvvZӘC1aa:gr&`Ehhm1W0mj];PRzLcoOD; "~@0)RR IDATd?u}.@@fwv~H"CNg8LR;  :=}; Z7Z=29?3Nd@[8>zB$nrF$#"ZN/ f1fWJYq1 nuڔՄۏ Μ=Ӳ!PUwQOO7$SP@SRCd6.htRK qq쌙2b`>xaiL K F$l}o6!>-4??ј\H鍓!j_PgAs8JJP%)nO)SBzKkHIozk7RBqu@]1:#R v-h9HC"iży`mBLV91%q%%1P:5- W[KWWtqBDDɁL9L_hJ8E .۹LҀdDL.mVPw NF5 40>Lwٝ3l.sY橩IɁtK Fz]hǍ8yOXVV .4W)OPu PPt*kYڌtwWc tprDdȉ-+` 郞@*P(T=<N`"c X!sDa\T`@-Ƃ݈? njN9}=RG7p@h;r?CCz:pT# h@ȾKȫ$ Rk(EQ7vg:b99b]TD-}&Pņfi3655xg;;4ZНu8S1SVV9ݼ`&0CBImf~7fvf+ 6g֌ͶlA;MT:t3e7I 4$}(ߙ ƲbtŽ!*ax|>s-jb[׎3:u2rDG}|S^z]PT>/#{y>}4NcTOw$D@;f-h//N:)(Hr`a i%0qD;_Fa^Xt@srh+[`h3<=cvP b[׎3#RҐB3mc$͍^V.KKCGv\m> qܤ)7WÃ~"NUicA3S01$a)gr@3T ;v9H@Bm:$yf3웻^ڧnSny95jn];`ԠWsւ CuJD~o W b:i"NU%'\h07v}Px}/8rsse]ΝN!@JJ2r >wln~wO>yaQn-lS*\4K)8qZִFRVWGt9H_DiH$jRЄ7Y{@f |'VECݝ;t(0X@! Hbrz %K?aaaDX`nsr\]]QV9gϦ/pJ!n-sin8fjzbicA(K@H]#k! ܞ{&9W"0٘rEɣBz(]Kdrn߂:^Ǝ t39 6`ظq#^u.WWWW tazKjWC:b<;[_z42CYGms-voGo|$8-E lj@24?  Qgaؙ1 JD]?էq=:QE|;b=$]\bj}]&i=cp",\l\Z \Eoacc~_NHO1c~썍 7 rP&UYu 1n8>qeֹ$0H6n*2m.6.wGze:Ba )*OÍ7М G0_,s&ǜ9Zg˂7|>7FRRs /3,X dZU4ɑ9%&SLh-\%E뺱@4Rq4jD"qsM |}  Bw5# k"T3Б0`ashl<&w4)˚HhVSb&^޽{O?>D.11:7[j}|AohVD9 I*bSª=cףW !88TDڍ$LOZo:gr$$$J@2pIQ#xRE+*zrHU[-9ϯ*^zSm *Hr$ܒ@n3!㛕L沯3s}q{/~[0#a[)+ĉCWh -[оm?)GI.rJB&L %8R,p H0$HCAphXAxMO87oFnnȿ@b(-u굟|֝ԝ*Zg6*yÇub޶y>4 |񫀒3@S)p{1[  yGڗm6t w 6%&lff&.A啕 % Y'Hg ȬuK*+$7D; :mB&MFMG Oh2 LZڿ"ed{M&hN1|i׼~ VUUw5W⭂(r~0=D#Z$9Gl _tfV"sL.u\܋P$Y-EךCIfВVuEd2̎/_5kx:\lZ wλGbsN ߗc ڽ}j 'h\.v!KC$ޒ9q3romM0+jj!T~]tn_,h+:GrVF əE3wI۶ _OiAb#4S[Ć{y V5rΩF A/?w$93fQs<p0?Klh4EtF4RnHAHZBpoO3}#i.Z1^yyc8~8#13EK7}:.]"2'GhYkA%hՊ7"!!IIIغu+nGdd$l6^}1^0 |ֽ!Jm(f ըAe{%>Mc"(-]s­U!]HYP y'@t`(DOC&ހ x$.5 'ǜ=%H*7&#~~m8ɱɨ9D K#7g dm9V7rhPH6;T:'10Xj`f~h60; [ ^mvl{aa Zpvc{LCD~~t1i1X<# ҖvoNCIF v7FּjMs!Ww^G\93QP@]W{HBHڎZ ن2?@JЭ#梥SNEkk+)3V^^ZZZƜ{,V4f'cl`ȏ FC3`vϏ9sȖ"غttn1:tЍ :tL ̀!€DC"`H@Btc]T7L+Fςa_:~}}kD=[rXk٘ -U<r 0~C0kFڎVap:xDYUϘ̠P-!;sg &eaBPUf[3d#>9՘oL͝IPUtx^Uq0=;\VjtZRȸ᫦SNE\\}5qW^ "Mĉ;'ӡP.ՕAnT^8w/^qob0DܺP]**|ԅ+LXbOJiglS~477ԩS薔Z=cԛV{/&MxL>999Xj>````zzt[aw@;^(nAA h94Iá* v~qSӆj - fG TQɀT$hom: 11pLСniZq ۔@ zdr ZH#  jjQWG 2#YKDB="A{k[>s%E`R( tYvNV ӓcBy!z-Ĉ}>LЊ#TDB@P T-R!p|<phOM6Fi蠔o:zW{p]vHTS v^^IƆnVB4IС" A=+]t8>R<l:h`m%=f7\FpГ;y)F=D @dRIIȈϠae%IPhH{eK=arƧ!$zgG DBxNrm$FpұLC켮C :bЕLKI |5DF~o(L+Pשu @J"af&cb[e@P'4A7:R0|K a*}(H.%E8Y!r[nŌB1ЂCJ"NG/{o/p2$M  !vnd?8@,h@qնbp?Cu pHUWxqFbz8$h:0AP EBDԆAẃl-joJmBamSz"88,C_b W^ 8Ak CJ"!Ď#'6FinhӤIȚNҰБCJ"!BE(T11`;R IDAT.EBAz =ms̅: Ä?wvRsFL $j@Nb@E!(vfGm ?+Mt(wKQ*hKaZB/(y{IEBk*9->: IfL\2$q0q,@z.K"AzNMt'54PG*Uъ,L8 EB߳ -mk=TKQ3Ș2 R| ~Oi$ E @|B۶[cׄ%ð)x;XQ" (VCTU b*eȁ& :EBX-Dh|_D)! !:.z# %Bnm+k̯Ja6RqA9\D·n& :Eˆrip`9"4vlPQ3Ê3gR MBVRO\sFg@pDp;w 0/6%*:Å+VmQ*eUWh]Ŷǰj*UʐMt(rB:.H ~XH(RTnGrX2jBr"8ut|XZЕ$iA_ ߛ+,\2@"!POhb70?h~hZ"!m>a%$ ZPfL9oE5 HFEm"e\ 30nZZhm.)ʦȚ]hCQ$ M"h?oB- 0\<;8AmstzL'd JM򳔤0FDˣZLjPݡ[64IС(?G0\<drTE-Po4Tfu6sZXXGݙK!ށ^x 򑐐 Sf&  KHf^TD^$AH(G&N#kr2ՍJ f3lˣ8 ed@P}+&%w3?qy%h㼠(UyZPuo%P SR-X_/bTϞIj@ЂL()Mhj]WȉShut׋Vɂu3QU ROhSv]rM!P}r  Cn{`KNF!|.BlՖ3 AhCU$%4Y,7w[%)X'NBZ"aMhwQPEEUDb_zM)DkT gg\D&tLG# 4IС*} MCƼT8F0o "a_EdM&{@ hm/ڜ.kSG(䡇qq68 MlU-hFU:"8.?x'`w-..ƃ>M69/TEB@%4y14DML|9":dvӺP2m~եz:},;-t#'/uȊySAK-#QZDoHlkK* ѻF Uݍm۶ᦛn{{BB:vP eڗQ $Ҩ@ErPּy~":DCwms#s!hY 73hH;j.]O"U9u4Pvo"F\.x 8\yyyhiiq9g޽x088={"d} M>fEED*G нPߑHVKs_b-sW$t 3N`ӫH)l6+'8S% U)F @]v;4BHOOG[P|oo/yܹ>fz~`3ZaHTǪؾpnn.J0ޏ?}ii~逜7qQ|S>u /L_CFs[FwΜ93g`^ e]4?_ބ: 7畕/T,#GT` SV${' I$˪O̸Y%0u_.|wԌNk'z0k,F&**))O߬N1Z4f"! EMhX?c0q"s ho7<:Z)ǏSV$#ѷK@m9w&>EB;sOY {z(mCO!hߴiԙUD'\^5r3q$B$P y M^#88zfd["\KkeEB^baE +=O]`(cL` ogp/*.*3 }[|d\lUf zI噄(6MаlڪQ3f踮Ψ躣 m3 t\L$zqQr͐kh$tMi Z=pZEEŘ9hCy&!@æTzϤ,m6CK h3т sb-Â:ppTSxL˚A#/ROiZud11iDi hC]$“g>-$h%}׌FG+t]WL/9Ky_6*7qR qHOwm}js 0#"FHѧ, Mt"IhlL#= E'&;[tV#8} UzGJ %%?x 78͢[&RKLZbP^!楏Nim(MnĪao9k24IС.ړ_PՈxz{ ]QA/zeVVL ?rN"U$bDZzq!`2EZGKJ a3Һ%&R4ǩSC)udRVFE.,OivkC"!0JRȓ{ \@mP{."¨@!U$={5ŀEŴ㴤;)סCY>U-1* ;J,jP^Ng@"!@Di!ɞ V[(11@~ gΐ1U Q$d`dNmo_Xy5Z=]ڧ["NW&h"!0*rw MCC&Q2&P[(aA%>%h sʿ١vEB8WLsg'ѹ{kTw+1OiZiL k0Ak Z ~hw;.\(Пh4T1/Bjﺧ\ӦhE%(ϢE>]:^,hml۲hagv3>NJL&W\ᲛhA$$ʽh1@^2Y#"$]"#'ׂW7`m_$<A߸p.`ף.?_#%K<Ҿt-]v+%Z,MZ& Z "! ݅&}+S?to>m*Q$=)v.1ӂBgC/Px옶B@U$ M"88C+@Ѝ& 999xOxSLj6= GDW=Ot٣uX1CBqgv>%Ї*:kͽh !==< B SSE.V$etIIH-4 zR~SLjZ@a[WtR vObcڧAoh(( ZCЊHH48 1ǏSǢE"'uJ iEGKrjIFW{N ɷzhH(P XtsQr0F<*E8B#o C/ 0AkZ Qn !pbD Ύ;bQ[K^/\3\f3&K_]} )#u:mQ1"@cͲlttEW!l|>Aś@xh_n.nf!hBJZfVvB@8u1D,H?s5S.0X퓶^mm1"@cd 9?\#T!}:=]r'rPwDh"(+z.a8 1 ~.c2 \u祄۩NWX'" 3P|@'Q?"駴]LyC(?[ iț83%C+osbGO~ & Z+"!@{ IK99fT?;Wʺ oT7GOY32į'SL|q}0kӍ9sO?%-RlZv a,^<&3<ΔK d˿ֱ(s5MDBv<b>>+%N]uuSNOBj|%q*gHKӏED8ֲ\b:#NBgΐg+!AT  qeѻVVFK, t5X>D\تBLJ 1*C%O\#믗QVeICvOڮX!ةS W: >O{pA] ̔~h 8p@||B[?ĉ鴷Kw/EBXAEU% tAM3bfG ^"hB`Th2["0ILmAj{_ wpj=NT9v`.\w!b\>I֒H?dBy9%@JB,4q7b!4-M%ab2DX#m۷SXAHXHo#6F#mm6>T>z.]*E$IW4JZ 9ĸ9JJJkĵSSiJ{)dfeQܫL8 MbrN*9(UplH۸{CkӕcǀC< +LPY}5 ?Ant@RJFș!MDBH#hoZ?sp7hCD[TVtyTwoppkNAM&AϞC9C¿/L~n_T8Q94$.H cg``TݻiE(AkM$(hiT`E0.#(/3Yh2zv젿Of/76ubu:) q˧w^{]s5y#j^7woh h&rp1|lF E<M A3M7)PB}^e% `""/n4GYAr|$eDxML@Kw j:j`lDvB㽊f go)l8u ^tv;YБq) MEBV񏾏{ bd9՚ ( M6}DG#doxڮ[H5AA&iԂs 2gBǽ ׭,b 31ud˗SE{#/''NP7K!yKRI֢HGΙCOK`0(<,[󷿭XU܅jkGQ]p-}=̝KcoatDDG됯˷9x]/ӄ9i6}gQit$ ooW_=|@E c۷{>OvRmivbU$4~;m_y9}DŴa@q?pCviFk Y6c;yGöݰQ(\wsOV?ݥtHsoу;::ȶHLi$h-דR3~k0c& ˣ/I~$Mٚ=[>IhZyYs}1Ū!z=}*_~=L{>ǟ쳴ݸQ^Q~,Y7çHP[n:F>I1AՔƓvk;'X96l*cc?wKT/S{i**[]Ggdlx8 U^vƢKJb_g?w tiUcz=c))/a س{U1>7""Wbec6)Z _70,ba+Iח1l+x{v.ˣ֎'Q[ϸ6Կ;cv;uJ6o0ACl͚518~ӟj7Q 1OVأ\ 7PAv>lM7?@س˜2 jx7oyo]]dUV*Z Wt:{/1U5-,Ilئ° \ફ2#KZ&f2v֮e z$bjnhh`Vro2ƈ-䖖v2kkkc7nt}jW9 e6lbWoRaw'Pq2}YT^q_YO>,wݥJE/1lK?9r_fϦA|X!w|Rc c쪫h߯~%q2/3f0V_/Z' aDG3K1fXLS=;{Fֵޭxa غ։>WRΞeH~j8h$ "!hvr8>P}j#6okj?=@uz>vؙ˷˺[4;E}MBy\JK% MVg ZJ)R}&ܡCq'U+: fne@5۶d#5k39R7Q$q lͳ4IZI(~gk)uY/|;" j8وR4\l5;0`Yka4>'蜟9xt#]c.Ixn_u/"&2}/6ø{] g1 vE`…(** v5TCww}8rMtah*jƍ$lݺ5UR9x cҥX~}$xgPQQÇ < bcczj[.UmٲǏǔ)SooXhQ({oaL>k׮Wν}!4E.֬YulԩS]-Š둘dee: ;;q9/4ZJ{~v|r,_b 444g޾˗ܳAbٲe\+.--Eii).]+Wb޽8s ψz PWWz ӟ\#ؾ};V^>h\>?7(PzvnnnFNNNkbbb@Ң` ==gΜl F=/", yq^|EXrBiʂ[p/Iƕ $O>wqGV+6oތ*a"IENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/ber.py0000644000175000017500000000031012253362407022013 0ustar georgeskgeorgesk# Kelvin functions ber_n(x) and bei_n(x) on the real line for n=0,2 f0 = lambda x: ber(0,x) f1 = lambda x: bei(0,x) f2 = lambda x: ber(2,x) f3 = lambda x: bei(2,x) plot([f0,f1,f2,f3],[0,10],[-10,10]) sympy-0.7.4.1/doc/src/modules/mpmath/plots/kleinj.png0000644000175000017500000022707112253362407022672 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxg}u:yA"01(0(ek?Z{lk^9AZK-lk-EJA<9LtJlɏ-H眞>p u;uKh D" j8o? D"H$D" TБH$r:D.PQ@G"* H$@ED"(#Ht$\D" TБH$r:D.PQ@G"* H$@ED"(#Ht$\D" TБH$r:D.PQ@G"* H$@ʡCسg?~i!gv[D^^#~N }n:>O222?|ZD^^LMM0EH%]axꫯz{{RbYX qW3u@k|!KLӾ,x,zIW2Jbbuc@\&͢5^@crIN!.B}9G'$9Ξ={qAxIԧ~wּA2't]fqi)Ϟ.yaN,;CmJL.=tEoԧ&[_]&v"ů%s0c]qVw Q ֯w|csW c.k2Ѹߠ%X+ǨݷĆ<iJGȏ%?݄s_R 8EbprebMߨ_ۍOV>Ft?Xy+p 2{>]|n ס/GB /~y/ogO3_"pʗ$}jC~o캙ҭ|~]m1Ȝ K~qDgtb퇱X|7zb@\r6Kڦ91y;o˾ &ǿDO;]x(.,9|0g{W9tD04n|}{&^wD58hX(>{k?|՟ Q@G5Rd XD5;fMC{}hV) 4gQmGL}FNcEkj6i;|c;`RN8qSbIPRŵE̫O𷩝|6.I?c.;*7xYM_Á뎳O=R'D}bdUogGye$0 lbuԮB͝w~, מCy:c\X ck޴Leڊk4N3_ekyuwKƌ!-74<3] L4-VQ~܃}b}š,}_g$nó8B 4Fb[r{g<|3 FyÒ;g$J^;L=unY.r.^O1eq_$%poFHxϻ?;h=lLMO?'tGeIl {[~}67 T|Y dݟ,ײ2"I%)I-ISnefg 7H׵92~Ah ?Ѡ5*C#pۈOa(i]+1bcxpi'&7,-ј I9Jh&ѿTG"A<˧Q >ֹO|W^Qf$(wpQ1{xSͬiMU5t1?=!r>D =ؽw_L)UuގO9}$V?<4J_G[>aIm\vgx4t>:jJRslKQYIbLv9bc]]|#Y@ڨr$* V RSk | p+60+icVw3F|ff;һgMͿg tW$GEdIۡ;Nd-400x~@19ѷu4SGiHƆO3n".mvi|w( (uGMlPkHT;Nfnu=iٹ{# fi=p[vZ_Mykt49!K߭f&ΟyD`ppAFG188} 4ta rmM$6Fzk|p7o1c$"D(%HWG._&1kR7S'4p'?LM))S鬓IW akZRڀnB@hIZL'R-˚CobajsBr_f,7Ya}>zk}t7zȹȰJ2MBSmѬ%Oh?@yRZ97r_ޓ|sE4J7+s}>;x&;dbbs/ 藸j5F9SI|;M!Y /&ȭf ]֌L]6^c=v#lO7)5/-}'XVţvbؾ6.F&.V$f^&ڇ;jĜq\ҍb1ReSB7WWkz*I=AX"j:Aʋi)_h &qVښEB|RvK<$fɹ.nS jcTI{Ȱ=tIZ8_U.eG}?ɋ*ù^+ rֳßK&fg {yEǵ.aso 21I&<Y&k*ɭ`Y3һ[O)QB.:#9?^.c-eb0uTz5scAאKGgxF#HЦ5lnhXFZ#\ʢfظm9EBF7V~p,e e_GLICHAZ:mjT0pR+tyԲF-Qة9@^K̙W򴿍s ԗW:`8]%g`SvxxƍWL-,vFHDCtX7-#$h-"H(Pa:E*m6g}A/]t0A4A lĠNz4*K̏ͳx <<+cnԘk+Sm7ѠeвJ!&>`1Ihd)PMjnbHq44TQӷA.[ `7uD˅hfa C݌v1N=N:"L ¡֗bXbg2g6=Hb6&:fj:<'k&wѩ824ˍt_}H!0zƠv>%rDSj+n1~ Hz˲ƿM hSD ѠfÂI+6F@賶mAm47Ǚnw9&}2L ;Y_#?p &mqH mN٦2 $&&кa&ԉ*>;]-sPPNY`"VgxIwE8QGQz^@+ &4S72Y0hH:7F\k Usi EF-s{ k0eU8<0x*s<)I&4G* M8en3Ok.Lsf"m~<~h+O#kY1,'V# L AU7m q30-;%k%)e|.=qw&piͤgvϱ)M^gyFud5a* vbA#*(QK(,b8AjN ]⌜c$dMEOEE%|P h0fk'+r <{ !=3r?ƒK@EBgAAr2j!\Rzd٤spHIbmx#I%L!B}h>]iP?\chnelLK@[%Ζ똍]ʦ!r)K[ PB6 A[Y bqf o0בpcTLCTGPbuO%yxm̯.aCl*.&ƘN <26nHx@\k^m^6ST5eN 65`doA៑LCz V;$^r D\Q 9h1TȢT;(5e.iݪ-<6va b ۇutS0IȖ2LиU*2 ge9D,1^ryE"/Hm/N0/)@x^9@B$Z71 "UDeIza29lD_kuH*ɰp-O\ QMB  9 r 2޹2K+&+i樍lW4ǘ^aL/d\ԮHhxRS9Zܯod{1M.9 UШ   &v0nѻ'<yNތ7Po 2[ϳlڳN+qvnɵ]גM͗jlus, T*oy'o'gopAA*U"ۙ2M60%L| .4ђ+(q&!~Ja+2 Ѓ`}&% BTMdAI #yئ!-^ x%YG7'(ŦfY'Z׏j{uw1Ŏ1}5VoYBǑ''%vX1M(' f`KƾR'uq:\7 A~4Vvq4ng`Cq|l$q`WyT-[I7_+GwKvb;IOOr]wQןy.E"a7lVn#OG'<տ{aZ"t-u<%@!IҨ@#k~~g4s=UC IDAT?b]F Ĕ&h:CHZ C)X!BAZ t*0CQF :UVfeΡ!g k AĐA%a0ihN[׈bSMgjRN Z-? z!R~v3}b+`RдM5HPtW.i5%qIWZ34.VbGuf$1mVױZ5Nr4YS}|NSPal4uH¼Fj3P<I6* q $«pYG_Kuc8g<4bw< ۻ7qSyDw{VY.o{^n~?驳;q xGazd H L4s3\)BFK8 2#=t!)|pѢ5 J.QDX \Fj@DJQ W4a#\4#E\fÀiePIe,`IL1Ab Ó: 1Zbtj,:%UdQ`Am}cHQ&pxՙ"*(VA(( a6 !!>2$;;]CLB#>zg(HUF==Mӌ_ɡ .[8aʱz+t .M!;)+ ׫7V~_-sQ#?(_~ߏ {qz>ܻocͿ[z%.?`7uO@ PM{U° MJX7mMT ' &H&q tsf5Հ}T"ǦB9 ^ Eeh2Ot9H`SKX,F} !\8uDш W.9 beBCaT!Z( [aY 3b*M--j4sVFШ&(j oVДb%~R3&QITp6NK &*)W pAP4B1(a$K[4崁7R;ȋpXӘaE$QY̆`vk!M|T%'Y6dbdЅSzced-BPMBiiت ʒ)V@mK>Hr8AK-ǺCu CJ?'?.odslX|R;G~(_`0K&4'O؍6nd$VBMBFMkI0B e 5!!W_q+LeBAAh(O!B "!F\Zkf5Ͻs7A( BjIh8ZӪnϝ# T1698pNt;v+@蒄DJh2*S vP=k!UPid=NW+AVv}b)L17dgT!6[ 'l؈LEu3eb^6)L=`mP<#T"ln95P=M)6.;E4A3=F10(d#݇x>ʓ3_?xV a|>D*"aN1rǑw,şMr#N/|7bƦ :::ZxĕȄA` q.-()VGPP%V=R!b+&$MeZs'ZZZ1-M<+qM Miq<=ACԱb*e"jGQ MZg 1J Y.􄪁Hla j#F҈M9g1E98"4 0CM됖[s6r4Sq !+bh,m!j`VV |n0`ޖ<9yn{a^ȔQSmNN|-{]6Nr1_瞷,2tϟCE1&>?$#Q@@hn~ˈ%Ve/xS|~)lGcd5V@k@4BRqD5Gxffs'dj!2$ihW.%P2xCu %岠fFN}6Ʀg?,xZ ɜw9Iؓ#14rCK5VG4Z̝85߉[b I|f+7^̽j*b+dw{]uH/dʭx6I0o>-GW,1.9W}=Y$pҸ$0}bt QcAV@(QHB)І@X! pBy(kBCbiBe|J(Q%j,QǤCh-1 ><4YK'iZ2C7XBZ_f9JV4fC =P iyn~)@iD!` X`ybe1e ؐ4ۉ`F8$!V_dEڊC )4 !s R@ '}ܭ9G&&KQiH2"Fj\:V}ucPQǸT59L\Qnb&,I|͂K&Hz[dUo܈ .yk^Cj(`uz\J u{O@"C?cb~*k,$Jn\/UMkOL6J!<[R ET BS{M^eqE7W{hnf0\b1V;.熋y~U| /IQ@_<7ONuwsIez˹8Ww (IaL44m~\6 I$cvBXA^D+\d_`[ऻL[EW%W9FZc*YWA$M(b ,V*jXgB DHi$XB!WsX`"X6a,цyYs&&R"+vmu>F"AG ѭhJf|MR88@FwesJ}7K!w<۾MI -nxf#˧wp')~{q&n_<"JϷ(/PB7 />>{~+\9;SWe:r7oQnYcC4[̻e%cDw@a/-(/gHZ(L!K}cu$(-Ph|13 y9o`x@Sp5\Q10mxSuWKizE&[sgERE˺a5 |&)B@Z:hG!Z aѫMzV)DidY{,%RҵtzӮ.FЈaAFDA8iŞ`̌ K`0$!!Pk%uVon'?Yխ!V8Q'o̬/> ch&F^AJAn9D' qDVСӁ_^PL mycOj̇{#|`FE=N cddyүɔϬ1xbÇWq1{M]?.7f\sܳp>Et)5 c+Y@ S@%(WT.18m"RiVv!CY0WΡS* Na@mSp!AZ{HAva} }am d(rjUMj$#yAGP2Ed\W1#(`+1a!SaVS0 Chn8E~̑"?7%666^:[a5v?1>[=c;og?7>cpWyrϦ=W۞+l`͎:54C͊kqMCp 5 $P+PRP?T)1EtES!0@5rH\TʐVMk〟Dzb#BlqSפ W=6tRVJNx>u>4$4HJj)!=0;`,2AR+4yMfUa3 [0jn+h=@CP$JZ#HnC֎TtV6'|XvM\Yn=F,&]NFx/?D[^&g!+p G*)//|~g2?9U /[ X ǁbؿ9VlCaP& va-:LbZ", j \juJ謢1_BQBՇr@Z.ҫHFhVN9*2_+U+5P 2T&3z^!&#SvhƝkqcpG! L*-09blT*d SLLִE1/B&FD fYЛqsobMC[LDh0$Z*@.&z_!3@K< h@C}:"i١Y[bA[SC IܓTAH ]0'Z]g]yfyJ;Tl>`x%cg-Xv0&0Im4qnClDPA $aD)(%Ղba O܄\j>ڋ)㝄2X Se)M>')b-;C$@1FFKBaA(Ќ0b݇zS-L&mDu=EyA;ED#Ƿ7 ) hx@u >~n=I_&?x1~ktvx`B}kw!݌ްO2uw Օ9~nW5ꡄA(g[/%N(\y7)N zEg/qYT`d~2!Ԋ0Ġyu,uSQBW,.LLI.!T;H@^ hfbYO&86E&7HWb~!0g#O7H;G:9/f7Rƣ$F,k8`iYX4>BFjҩa>İ20Fg%D/ f/.Cݘn'>zꕢiM6ftTi򀲱IU&<6d!bSqpr'`+p28:wv>t7+7?ғ]dX1+ֶSšI;T{yf౫To2dz(#-Q{Bh FK) G)Y ^9X;†- }$P (r@ubZLEdn0F%a Z TH=O^w\)R3b\g%н){Н5E̠Q4S]NpCX+(+(9,дu`jP` aj )Q24LtTCP D)fYڞNT4bBI!!فF 'I fxH|@9uP&56dtAN=VfA8Qm:,~o Zm~*_K||7!,-9zK.VK>5cǎ|9WwXLHosd |vxe1Xu~bB֨*i 0N 潔87C]z Iš=_%h)U('+!|` U\'6Mji0 (&JtR)a<<m硞#7HCB&੘PMlAuTsۆER[! 4R$Oaع"\.$T Xٟ̓7t LQ a!1 ЌG9M @)B n" zw@kE*@"I"hQm# ) ʅXƠoQeR̃_*R#rG<3Lx6-Yt&\CѼ`ͨ|ia9yXs_\~M{k!>oA0Δ5ǹ?:[dOzN?xkx ј'D`K IDAT3#ؔ8(#4 <t`ԄHC,hM1FcDB !u4MݒtehPmG%_w >㬂<)mb .&taQ; K֐̭"&`[6D,e. NaL`(bFNðh4c1m%FOЭ=o0#hu;qPciP4#Tܛ*$ۜLBItXFARnK`NM(&%t[ᕧ"lQ^3,5@'\)8rxgM&DQdBf&`^(|P@(Ldg.[x 0ڃQ%ABw]yo \h@ {c*F.p) ,Є"rĕ}{RvN[iQ5M!`4mԴ*¨QE(jVPD{jDJt70ژx?ReRXNB4HW у)f),4a5mӰ3# JC1F eT* q& tD5z\+TMP8f!Eeq 3N@{Ddְ"踓h!b yT !Eh&KՠӌuEeh|GqvM=ċ .EaΑg SSR2x_?_:ݯ/35#Wy5ʒ\D#TUmf4[G5إc;]p;87 $Х toYY8%u 1lB`ƚ/0lB ` v@آVmjZq PSѐ۩Ik #jPY!;3w<*f@)TJC[a9͚l#r9HҌtu?dXi"L(aV5]Rb62ah0#C+3JS! TRJD j5JJP `"B#~¸J_E GF᭐>LhH6| E+D;21mVHոE8`TNv<#9KR_LVnR]-kS3?w09N:E G:v=E1_JTA6 ˔d9(ZM_ӬkGgR3?Q/9 Lz$L=2tK16.uڡ+4HD@*Ą &% 3@4әAA jZ8ZIB {5aAEP.pu ]MԈ6)Bt@H)X',*ͪ%$ZHA%$+$*JڂJnhms*Z d IбJ 2E3Eg9% *j1*6Y_qLVбXA 3ACܴExFb'АظI& )h5I+ PwI J;HGU!繙40nxbI kmv_}vje9K<:N@z _k@c>яtWWty͙3g_:uv.>&'ɛVx9ӴGc1ҌiE2eknHMZiɪ-9(9YO8|__ʕ+>|2ٿwkok-}ͩ#/ec 81 \Moggꅜ vng+t'S*R3>`y,ep4k#;1gHbI3&@ǽbS((t,&-RP*߈elHD`WItR]*:6BTFA7  #<)ZMQY42DU&&( /!|'mA"xgYhP` .2i`g&ÁEihb+E .!wU6y9+ 3s8Bs%t.4/g|M')?~z׏r~9^׀v8rW^9r/}Kxa}QN>#xEƛt?Ū o^<%K_fysFfv]VS;o`vA1>a|$őUv#7rqx[UEY}=ƜW[\kc{(} WhPٝo 5 V $Z w|g#V: r[\UQ`!bJHS@h;ëlHX}{k,$ؘ͹ ֞8 naVsn/PO9t&aig_Sr/>۾,o`~yfWR C+qWrq\r_ub90O tjÁg%je[=[aXȎ',~Q\^?"-ZNrЪnn8Z kB7S~HAZY@ 5=DBwXH%)w,A\ϒ4-Xrr,-1Vʶc\5ƂI7No{J#n̔(QfLPQE;Vl$p6F^]E]a Hxh=sVBLvx 0{|x&b懋_HJ;\PiKZ7H'NcJ.Xothn-:X98{oڷoo~S??z?.q9N>_y Խ |p%y kGI}֩>v%0qd]%mWqr~Mף]Ejݨ +j =YFp-"ψ-=&}{ ]C(H"kuq(Ѥ6[0H0 [ZkĒ)qb.$X F4*l5cf.Cjk@%F~V(MP DQc6(!k DaUc~tIJf|6|;܍!92C܆NT2D@IciXaHeXiKL3j-.6;m"WO_yژv߰xb|9vwu}?]@c<d,3S[<g= s]B 6)y+ƀ\aBAA`"a MB`PdiN)癞b@s =~%^JzGlT ;O #NJ[e20"~w9B7nTKroD6m0`5aAzHM *ST+TlRAǃ:!yC&XXZ"L1i`lvajAVQ (RXnjVx3H JP"xf@-3@veu~ūoW)k==(4![iE%ƵxXthR=r^8O޷i~f'ٗ}7>Qn, btL#G;~/'_{_5'' ^} j:P|؊ieT(!d}Zgp5Q&{s4ˤ;/cvY)֤B(LĻ0 ]<!a1KԬPqjI݊^S[ P̣imsW|hFOU.5aw4FH;- <5QIHds[IkZc0VcpSa26?* hEuLGE=&zj- HH}sٍF)J?h^}{뙪;vBB8D!Hu,rIBR򲁩< Eӳ9Z/t8NCg Ɵ/c_~^Sw)N4Zmb.qѠFBB+*=SqX(DoϬ(cNm"Zϳf+ߎbۯB _;w,$(TcA'hi8ExS3i-°ɽM?vOl꟰_8C96\NVnĸ BHB(< х 8Ńsrizpʙcg コƛEe5H< $ԧa{^qtVzL{7NIYD:nD=Euz:nf&x੩)Ô^ Sv anJ>UB FW 9rه% K@J 6c鳚i==@6K -@m1acȌ! QvΚe1FJ"E4DR+m(<ǁy1Ccf=Ϝ/m곷O ~o8\|a9}lOЊ`A+jz$׆IFb2Wo'sǹ[Ƞ|ކr'ш vMT~cz?O=gVʹǂ'Y90St'@8!pvõ)ymY]7-Y IDAT9w9/1E@E),-YXMzfR^EUԢdiWNP& If|x1s܈Kr[;Eĉ}Y)Cmg) ˒pmŒő`II!:dlPOggHɀ.#]γMAWl ~t ѺQ@y\pF`8aS $PÒ`+`"c0>z1 ʘMWt<,͎#9jR2(daꬁ)ցudPF0ƒjДqpk;{*͢\el'᫻ٽyC˸㫣ząKnfU5K:M: w`!; KMn?xo}ˍUN7R>pXQo#7~y.fM7F5eecl=j9٤uM9O:u)Mҋt1lYykp=\HB&)<& ЙƷ-k8ĐIKJb~죔 l<#óMF*#VsXg[l]:CBCJD}0\p )H(AqDT hIi)R[`[e^MAHw8`+ÒvIӑYG PC0$`R"Ib*g@v7(n:N\ٳwI%%ޫyr@#(j7 VǷj?\>㻾m`9o0b /Ǟm/wq~9Јi12$7} 91#pG|u'11$9g}RXCќgT< 6;)hA %-l#tAzD~R ؒ! ?>4(}R;t\gy )]ٯl3jocǩ!ז@nBz%)4bcCI:Ph4262)m2'@=v:8Hinf CPJFJ# Dg6 -`ncmcm$mm;t%9l64-nI% ]xa0ӀIU^LؘquhcB_6gL@z ߉TZeU xRjc /ϻ$/{ou?w<ߚoꃜyO=s/6'fSe~nq>!sp6[Xy%q+Z<2=48,)5u.C:B;xX,PIjuJ4d~WD*Hˢm"uݙdR /}|G}b[hspa}spzms=#ah-@i5P2a 4hYh t t0e`DHOꔶͨXWY9(2jZݐ$T,ش'v0:I ґ qy]LC#31Ȕ jbCo3U` :Ir4"7oE{}og`ޏ;I1!ۏ>`Z%WQ;iҿY今?~#AF}֖6 ?tA7@@<ÅcЙ٦d3S0<<nH3!;$ku2@!A QB:*8>iL!bxoOB@z:*@HδZcQ&R 4 "F;fZB!B.'&^[)l鎁iSFdmh[G6qlsمQ%h`fq[9kRlZb+?d#.j-v tiU<ㅖ0Xcc@"kqOrSp?{utZQΐ rXE*OrcF=AfӉ֔hld[\*a)w4t.AAg#͔bZul#EYcŞS.+a9Xj KHc~cc5 nm2^Oo!SQ#piL}ܔ%N)[ٕ^GLY~5xoh gJ-VD| y `3$'RA:qeAxQU=To5ƐIYyܹ+u+gQRh$9[O?ၗ EQ (g YpDYI֋tP;Gi!Mƌΐ_d M&>t-# b$H}`FZH!e]*g؅dצRqԌÙkhijɵeة\@HU!U`Մ2.E2ۨ|ƾTnYN@-lr`umFJklN}76}/xMiT} ^0q i0eI1 kC3j_uY M#>y̠S RI}E}M<2` APZC9C p+a=i2(gA(!OcNsΒd4LH7<Dž0SMWYKz:v9Df0L#t0q-4⢎ .fS|?1<-x+j&vV&F1#CFä1cb*9c*/v Kb\֚%Xłjߵ=-=캅S}OQ~>KU/_E>Wҙ d3n[qo!Jf7"86`{pH,}Y! T-ٵA7S SR,3xmrд0WYZ"ud{<A9I 4ϲL!p6:pfqز[풮C{AԄT8Xql.["2YLaHqu$`-5fPeM:Bx/f,Lj2cHØjHDCꨏԌ c6 BمN\Fp~y'C'4os&u[7' 7 V g*/? xdk_6L!rHm&'[}>0^E>jq~Yvh,(.Ȕ@"BXG* :;0V|բL u?Bm(Pv#i5􀡘V3rB(m p G p]o#GpbYJX&=5p]=+Lel+rmM!\2e)tjah`h"@h`L5$!Rh lGbhD7@j X̸d<8[B6اY3.W슨 &8II659y9 ^_9Fžbdj5^wrg9!`_agŵ8'_c%/Yh`GWi֡!Hz1%/0ej $)[X,TmJ;@i,^)+@ ueC,qe- `  0FM8/pV3PΨa]:$Q=aG`9=BO[䬡M8  @&v֕dU )NDXMu[dR7q4L@:N^i"Ҭ.+pSǘ&a$h cCK+ۑ|(JD'6Vqƚq9eqt{띟pGloW8j΁-|6>0*'y_.M^qxkcu-yX~(r %l{#Ы3خ"6ـb@CG`SI `v x @Yvr;0[*Ͳ@-좾 Ζr^j",O $$#1n9UIdrЂ9)iGEiba7G5HVvZ1^wdHi$h55ǀ.qFJKԦ.iT25d$a]|Ub#d0 L@rB'A!-߸לfGS}O7RVo?~;M?/ȯq|1w7E_swiԮ|9^^ ᷸=7~q~5]1;~fL,犀2T ;&G-+!!ELJtNNle9;ff'8:Y-SǙ;xHVebLXpkoԣ?qu^]O>3 l7e0. @2p(EL %1DL>uB.eSiI4^۔CKKe:fҶ[vF]I=:". )a +[W$%<) K(|]i cڊpH 1ˢZ%b-f B p-!Ui[fk21WlWLg1'=%-Od1 ZeERғM(կagdasTڼR5 '8/ms7ᄒBCgRS|M8y!8esbzIMR8W0_!6}뮹'^|>T2#{/>%bq.OGYxlo8u.Pds`ԟf0d$ y*F  ")AR!0ZX6SF73i㵅-~ZT Fi.. E2dPN [ª@m%0*Kh.;4IEO+ g9V eAK2  2]fVA - J|rV۝]O-Q,R=& bcr`T8m4wG9k86Ko 7sx0AIQ"3u XRh$C$ڐ"2*CFJblĐҎ87/L&tǙj=n&Y3XCXVQHAuy`n,z6Yπe 8rD GJǡ`hV Be0Sa`URoce)~%"I/3&owG8t hI3Ǚ$!yӹjHxnc?^XB?v vgIjн}e\ݫ,|QF0Ҷ %:kX91UTI"*9fAOB1{ J;:e 5#*zfO LX'z]a BDQArݮ=Yg= ٺB,)@8$A1L#L'eҊE <ҭ׼@n]sɲ_F; QJ5pu_C; RVRn7 K5f^|d `6z˕7k1t#F⨗R"BP,jc,j:DNB#SA4n&896u+Qj1J@[R|l lh\C0"a Ө΢EV@+z.9SBGa6QUXTXDYB:෱ JTCFWxzRBd*/О%ePk@ڈcjx[AABU蜔Xp6_gn6v+];N/^L:)tI?O0f(qׅ{m;៞[K)fYҝ:0uL OAFAy IDAT7 -x(:+)hGhJT "mX0Hw%ˆF$AFu%( zye ( ̪0 38VQV5l#Ti*P2dR`({eUB CF @Z```!!˧\\Z}` *E\ʪκWbf0 9qXއ7(\wm7X D$g~O*̯֓| xGWiIH=jI5=[bIVi/N q؈ĢKf4Ԑ mmΙAZ ôlc( mT;q K?# VpA  :V8a:lPfBtKs,.#l"ҍ֦!GuDef/j$Ԩ'Mׄ IAMm6fuhXޣJ{bgwoDC%X՘rOsic~|0B;kgxϿO=bO r??V~w_Gi\[Ktt@>U0HC$`$N\56l(. 8q$K XIbEDBf 8 Q;k#!OvUA-6hסJX%J+@/4QZ:Ae:tfh "EDP6@(CDGd\P\2QTlf` S0)$LAT6QBB A+015Vu%JeL|2#}y&!'Տ4xu?R~?O~y3HHIP*ZML)ORLܐh#ޘj{ %dVlnQjI9B rk 4PZQFºpŒQNhyh` ucuԭU^puD7P l#C*+(;HR -40i΀A gOyp a" AqָURxYd\ԱoS=U_t}B?0"̍Kz yZ>@ G¿6G?p[7\ٜ2D9gg2VǞjCV0$P #hR#fMԢF]`$i: Yš:4B]R  v ]ĮC،K!Fę~Նg4`MQm!2~dg[)o}{YCL&b7quw\z^I (9H hšX"BVRbIU25/ԽRQKVf1}Hc"6Ty.Pȕ0nZ4@|FsKseEY{s5 A*azyq8qړW_|zk߾g=gp*_/?NZf4|L_(Uz&D}FdaRUX;Sv>H"N IhUlgvʚ#H5U,t9(9(!hSrM9c VE~5QHA|v)'SBeցTO|bUz܎g{U1!{SHըtHUg/`U c4b4`:++iK %!(5G F-V^b!(i#$FIlp;@m/jvꪍ?EZ9j}Ys6ǞfwS=9@Ѯ_@B>BwڧB #-6zGS6` ^ =np딲.%4і: jHQ9!EO7f}{ϥs"J. eA-t_ > bЏ3*|UM~ܶ®k ;Ľ\ϫ\"5Rw s%)4xEi1#0QӋ&x{sG78;.{C5=Oj) 6L)B }% C j8G 2!q\sj7Uwҋ8YU $0R2MРC5,TUeԌ$2AgK2_ Mٳ&DbPr$z`>i12w2<qN \Y)2bGL&G+8c{뢡 ԇ)65.^\u}sBO OD) }ݒP PF)M<rsQOHo4-`-8w9,jAfBIG=5< > ;I<OzgcI?mC#Wxqc_ߘ}@?(8ٷswXdu}7;JUٙ5Ř5 9~#r0!N6v"Sy]gB0eya.f(tуrw{P N L38nKJY})ðhrX9>mHDs_Mٿ$doQt zw0~e/BL q<Ɛ]Lg4<?v~MoOW7#'K\fc8qtԨ}1˅ح(L$vbrpC`>$T&y'3`lPaBcnnUL.S҆6!d(+źRnAe'"1M-n I baj6WqMr隷:6hD6:ИF-f&q#&eb֎ĵng)Xy7v^WwZ{^Ivfdir_ILws;w 'E\W 2Bj#30n/FjՐ0E#a$cHNÖj#.%J}.Rk9`ی ]ΉeP-k=9EQ܏3Sp5nC[u-S6Zhقh44 d; {inD{/==KC=<kk :7|3|;{mKco9 zj#ųwHRmA*yZ,r8rGcFAq!4PY ΁sJm(nQU8I%z\4u]{^^W/“5)G٣KB0c8DvEME!YbXK " Ly@uaz-%xZNUt VJң5.Rc(fmd0vmIεa`½9فL )EkBVZB[IZݱ蝝=l Ƴ{{3??G>N>Tʷ$dW%Ʉg^5o/%G DIdT%| C,C0@`ܟ0WRF8_fLl,27pz2m;tт]At'8jnu~N묁p"$m΃@oZh]3 :PS4ңTCmf{FZ}M}#ƕހ|3Z~__}{߷$/ӷvc̻uYZJ.S-%GIH;r2rJrJRk^hA!rD23*Y 8dG|y޹N6_a{]-p1)VK8xG08‡42Xz&pLjJ#;݄8TzKg3XZz6~~q}l-oy NwB~p77|^nԕKWC9:ީNTpvUІO)BYJ<OW֠G>W QU<aXCl?xF|'ozӛn;yOOsI;?qzww/"ddm Q]SEtB{wq{'.ak1Fhxr^q"gM !ါqQjK%d! -1}?7*{5AI$QS# q8 s'iޏE<7ww}'~'Np8}4x+8~8Nرc>}#GpUWqޛۚ}woϝv[=˒, MhBG,0B'Rv*Eq8E*1P`9 2jIHVwߡ|ϼ[Û?ַ>ϭU>{8{8>z=M{Owxzy͆מ ,xh1]6!d 0 ;| ظ8>.їTNy GUP舭T  9?PaW1 ^鴡]4*s oN1s'">7/j>~8ZN<[N<9#k 'NַuvdG>n6ng d=c,#^6-"%eQgK??.ӡY99, Yصc`e!,tw'3[lzqng>Ltb<"c"%1P`M(8d]g.~/;^ve9%J5M m*N=Ӎig!Wn(<< EM '> Ǿ۾'~';Y\\8^{-y{~~mZ}{W}ӾeБNϭبҕRTͺZv^dP7 d!2+ȎmK{欌KBF;-9JiӖ%+ ,;Xs!Yմ_+ Gwsqn#-ê1,#B *f8gbggGiݶ} /_'~n~c6)ZCf5aJ&q4 jȔd/vs׫[bЪ,*c֒uKݻX͹c<0>#}vOw٬> L8Rv" 2x+gxӟ5Ȥ$^ZEpعԦ+z> =A~q,,, "0c9]CN9!k4ٶ^C6 7 EXќCR\8MMq̰|HigLNm.T'؊#Jc5EJ D}|/GXZǣHX "u \ʔ$|< ^O}MʲxN W*8Mƚ\>Ca5t2ˢH"lMsufMf0Jk2Q T |aLZj訥/d`*.S;Cf{TE-aGZ .u%#[R]nlYi1l9q R^VpJ bc4ɐ+l#̗OO-b׃+'o{NS֝ʺf7wmt,Z"F*vn!aα! FSQUCVL̈k tPkP``i`@ ~ łևCo2,O kJfs`-! B=2O#o#-YZЎAKMaf'4iJlK?:| 9ŋ?ﹽSo𓱩}ݴtN`sӥhM.LG|O N `bE%Et%% zNq=&3#iJa+nPs'g.`2Y9$,p|@U& ud}|-\R ZX[r#-hIrA e~7sfDȷnԧ>0xQ>\E ' 乬gzqiZĢbs rYc'\lc3]$ZP Q9V3r(բjjQL#L?Cj]A E^@2F  چ9\GEWrRv;E5FǶ\DW Ѣd8vSbU!;8EZi R $u#)r4U o]~n>@{~b |pJ&#n=H{}hg!~0 ID=mNT˶bHELRSHs;Dޥ ;4D mClFڪih'JfB ,[D6ȸ.h;%Vr[0(&*O.)ih-:EacGað9KyLi!I?pmzv:A4y;K_ |xovzr@xQŃ!)|qn*b;w@ҍU]Q C (&D2ڑvLaͭB"벆¡5*h!F,TYAhL=naUh["\b-pc>ಜDM)kS,_Cyfpq,L0Ḛ̏l[6mZFL̟#idK-rVA?EDB?bp{W2Q4M04$j5BU̠ CPG(ɈdM("yND;H;@ˣ- ѻ|!qZyZTG IDAT9ž^!f Cn#K1W8hGAsJ!X2fKNbRH0E68v|}|ːmBöK= K&uD(H, vwNW_294O&i\Z>) F]`UVCDsBβg$=dةdj(h3/3!f17DD58 Qk$'RH PV(:##=5SsMVH璶E*DFCԘBf]5X3RГ 4RP3"{0UK؁!  >Ch`bl}B2ҲlѤ~J,>W{p|7֬5 >~?_L ou}4Og?tiIR4|AgbrfdjRH!fXQ;"Qk29@IZ@nnUݐfVu|"햃vjv9KUtCLk$O'!:FOcULf(CEiRЧ` 6y\d2 2Y'U%>Vتi[O 32L4g4 No'~"yPM%= Ov>j/>1)BU_VG:7ؐkszQGLm{tN c4gd#29GsVN_rV$gSrv&gHn Oe,`Lv1H:^kWub/ W7Z\Łc4_S0_SCpyA? {~hB~O]l߂^_!tR1Q [ӬԒ=ťrN?$V X4D3$4y5Rf hC@ZK$]?Q|z|i5\i`OW==g<ǴAk PDCZB(pisRb-S6k- mcl[:%v-L474KNCjdX mV!ͭu+fΐͿ|?i`$_ş/o+ƌGǨ0楌clXyU8[w_ٯir}>[o-uξ|bbqk{!d4ql&0&t.5Ć9<(`DJ!95*v2B jKD j:F-n+Vi0xԳcϞzxV^Srf"x #C &*!@6s#-TAZ`"C {v m`Yfu ti cAJArAhO Wmیq%j=ch-sOje+|?i9ǯ?gqtOKwveqmT{,CfkJ %`DK HfM >#h!HJ6UD<7`3ɛDݖ<I\JhaSQu Z7 T [Գ]<puO&M^ Dёfi--i1M ‡$2 ϐ = ==IǮ@ h 毑u_r/LCiݴ{7W|?qhw>^8 觉X'i./}=|7[ys5\Z3Ywk3+<8p:S36ڜ Ah@ib!6 )w[P1$@<{Ush+qN2vhnR- 8,N3g18v11VGok,x=:Pv7DsDZiO;-dԆE oج Bn:t,Πt 3MWJIG3=mf`3XNSjfycr ގ/ݞG{m>0A?](AEu7?wQ/W7yu\<"^ˌqF#Βy!@.A| і9't]Ŷ"s*M@`Q٥fK5߅Ky_~Љg;_kxy׾^˭Eq8{`c= وVC&0nS[ZQj&e;Ɯ-{SJ0k`$uhHёAD] uH-25ke4T|5=%\lIki±#pp8LdY߇Qb*r$]q=DۨܤZ2b2i2M~6qB%k3_)OIyZ7WuAqt߿3o1|I0A?MD8?6ke~h{ƿ19{͜=f%T#peR*2A@QEqB.Gnڒ6ŸQ-\ bcz;ڡ.`wrPu8l4z,yDqz>ԙiSGM@GQScbrm !P8 ђH+ X]2 =J USc֏ͼoy:*3N|dS5HL:wTfiD~%9iig|Ⱦ:}@_j_5'0 !?yO|RW7aany6oxq^|-'q⚊V"K,ŎA7ڌԲgRGVP :)P6"Z Ea#TQ I0-Qx2<8tga-ڥ*:Uv#pc>-.ztVGc p#^ɜ{(HI&P 6 ]e H&jN+*W繣̎MH3Μn0MV:mLBcUQbSXx'DTE0ԏ`\O~ |raז}~wgi'}\:!ٹ%ܦal#ѱqJ6LTTc&,FC?TB[6^ &DD+ryS=R5]Ep’[vZ4rp~P]>ڢ^[nZ=NqȹzBj pVϒf` 뚝Gd6QqLOf|Rts_b6""kqN "QFդ8Q)QDGqL3lqPBB:m<&EUx: zM̪hUק-LIS=p3ΨT#[8SoʒW,+h3L,.V,e2cm'a5Ew8M/AKE%FV$ZJ6m` d&`l$ZE%u=UAp"yrV~Nޙ/T(+BwؿZ#0…p@RV]}z8 {`<sgxobe{V-gۑJނ ~ d-fTxLz 5Z%2gYrkxWj!JpJUn4EeYtqF v˜w{sʡ4+3̰qerY%W% X62f|S ?ea '<{0}͗E*w p<YbV?en{YM|ov3vy$U v{N%#weȡ}6l喳qX nr]"Jt8$ B==%&`̪V@׌jTTP8Xu++2EѝxU5[ÚF^x.GMq{`E\AdDN1,2Y+YZX %ĶE@m f?T˪XXXhD1A`jxu]f6 [$bys=֝{@O~0qWeyuv_cوrtݻGG'6y͠K~9…\2N LlcE{^ qL1m^w M QҌ 2#5Kd I(M5M. u^ɝg/e m6g1z9.[[5 =?A͉'g83⺑k"VDy12p9ïgQa=ZՈ(q15V ~3OwRbi# }<dmk9E.-¨'r<-hcѡ=A:!k6"0!nAqE$#&GJ"1>K5>!~]r"*CD9qiXqܘKrST%s]am,ְ Yw JC:K)'r -2z( zIKM$wl":(S)jJMJ lƖMf3͗&B3eL'/z8ŋL(DQ$ĉaȠw=±\_-c3x ,akk7W|EM.S'Dԫ»gZNYs`Q'hHn]S6rV>Nz٠M2t'Cw Vdg<1 65(69?إfD b\5^_}jA]{wƫ*O IDAT@A991imn6Qv:aVCȈu)|5GARW0ހ 2hnYj8ҬWtې5.%8Aum"ڌ_UU GAM11]-l-QQgMХ͠lm5Gc}K&l2zjiS@΂,J"¢F$'FIZ-cj?%Q M %N ^R0TyC9M5ؼCEf S!9t"D/N,ry'9~?Ix\r݃_,b 8q];sM>'-GX^5=:U (xjFN0ܻ^LZ=߽k:19[/dȆQǠXD$KCpL[X*h!h "vi0AC ; [=Gge}.}lLm-Km,x1MH'U])-QUX"1UY@[> 0nV:IAl .AC/9N u'I*&&VZ=Is6.ÆI,ӳhk'7^HG#d33H fł6Kw7ybA?pYdr ׼;7k/~=gW^o[/?O6|xtH5'P3o?,˨ 7n>3OQ $܃G7Xxh)JMΘ %xO$e&4v"cfq@bH!@ b5bsc8Ɨc ޲>C jdD*cIdZ:@%^IdܗD} I`@Y$`{tlS< h`KPQGBhTbP55IJErEo#8D2)9 ">󴙸oÛ1}p]SuLtT&d -.,.C8 gԈz7'41/io^Wz+oz{ 7txlpK/pd疋{dFBg+N)$p6–izq ,!/xR*RuEn"f$2QPiNjVVt"|IWbM#xx/#f`.+a@KʅlQd){6ZBBIDzs2F18 t〺XDd }^ &0|T"B-AZ@vx2L9KH"~1mdB1E:g<ի13ݾ } 6˨,!~5 2ӐsO6VH>I3[9G &sj*ot W0G3ô̮.gL[nHgwYxQaN#=Go$7/f)~E˝w>s֍o,# o}_,[, ;!+%*6{'>w}_ ,YzI,Re,7Y*07ܺ&$~[`n[x[?{ nFX)_ cH7#Uh+8ztC]>ȸL;љHPi4퀷*$J\6º ;x'6FrC ҘpMT3gAGZsY@$JRh(i 2`y0 fgLsH鹝 SQr_"ů RS)%=ρd(eOmc8&X$l* !` Lw|&?v%u1'2- @30tMr(\E_WhKpK 1!%PK(@REE25,G&%K]̲ ŀP X{.t=osp{[tr?;pVR*̍"AIU=Gdi)l#fb,^O~.S|K1nX+'oS;w}c|C機T8P#%}JFk4Ew ϫ_[O7yb\a&XA-!*5఻kQ̊`o7(\:}IAb` we)6s0p!1$;H^`+}<)nRX24ݢS\9ҹINr$CE,Y@bPc"!}SCQ"LB M@]ŋ1G%M f ꓩ)y2 `A̭Yfo' 9&R~!⛿Pڄ7ي"  UKMix9v M^]ҡW9?{mq$ު'?*~E[XASR 3 ) }Y%I`܅ц?߼@sgK;[5FqrB!s(5IE1Cz`m!PUv0W0 c:=(E$;aH!if2hGH#,b'9޴|LZvRptЅCS$C5@yQj,,Jz DJ"mB@10FS"-GU!RXa ۙi@j9: 4H~3>|v+3/eO{ʛAvIJcW~8%vhVʿq\dB+)t&,w.2w$g='n6ƣ|'G9ާy_bŔIowN^_XB6M^8 o<^B6Ǹ#5CSO>XCg4P ư;,x}9޽dr 0 QƦX6XA)U ucl})ق})R4&uD(5>&B #- l1Ŏsؼmr-ĕmXBe 1˨,#f ' Lƞ(]m)li+R@ DDRg+XGUT S Z*_LF@4R"+_/d34t CjJWr,<՟Ǜ|[#?~w2Dg  :{ycXxO>?adi|ߝ񃵜ɧƱS,XVr&Ǡ{rPp9IeLA7м1^{yxz5dnhqT=NH sBD_G|\g bƥͣ*Sc4s߰RǗUro Cvq&qTQI,aiPGh`dcx\2)_6+%^!bPETYYE E Ԅ`^dN=>#"ɨQSPё<j@j SLTʄp:%;,ZlgrX̚zą u:)AY9 s+tLNo@+;u+Q A\@ I EɡƐʥK'y:ٵo'Αo~J'\f,|(&Qѳi~z C~7~Jr?O<__֞/s'"~+? WO>MCe=9j^@ur>}IW |*IedƼe+;ɟyl9+++s]9^vxtP?_<߽W߷Fq:sCl3/ XXe} z9a Ϲu3O՝"N`<:bewz26:ȉCpgљ uCR bB"5?oxC0=rz Jv D r*L"ER(\V)HՠZ0)[ F*G Kv fCdPTGSZؑбWbg9eԟِ`%)9}"8<1Ha 1hAĔzi gKӠ  aOnz̶-R8(R,g@pb $1^8es'8?X8Wj{_K/s657ȏ}I6ϐOV'oܮ @8qO}So/ .Ik_S|#[<^g?7VZn1cp,&h,U9[!vJК0=lpEcT/7>ӆC41MGw)ZZn=V^dq۪ݗggtwwݛgv迬?Ngg|套^8?s?SF|#}arws|w0yҗ84dËlrnD6B6uЪx{sk !*EjsgvFo +p9*uTz©6\ eKx!J EúW/y΀2qD@iH>| n m̂P \ H%D{)[`6G5B\1uԵNpuk -K ˠ+\mMm]BPSO)} sƞa`AV)c ]&1C'S଱<@L$ܳqfXOs +7 fq>t\᪥GnF!UU:Ues9 FGDL2cN 2AGjĔb!RPBfJ26u99f|: 5d^SSt &DXgrz^P5*U߷Ǝw:G]uz>/|>絹pBk!^/ ow跢^uٌ.S]N<\Y$-RJ@ŀRGjlɔЛzS7}z8N.cEL,DpߢaZ / 5]k' BB4mc -rxo8 򁐏Jz&VQ3a1WZ6Cl5HGXSJyf&g8RLbs-7 6`QfkB7uMT1h kX`Rm5{yac$(ШQD@MmB3\4-~dhXz[5cv_)R4D**7@1IJG}:tTi?p¶X1SNQ]|Aŋ9VX 2jzgRSڟ dUXrcez9h!Ő22%LWg + yOd Iik dz4>X/mrltǖk).vk8#_DOeCVX}uyÿw}+ۺ oA])87xdrϓ7Yٔʔ3wvp+&~o=ԃkي  20ȶ\ hC9\"< rLxN01`b« &D㲋Q*e9|iS9CUPTUvd; ;;7Дk%S(g딒uHe~GWN)E籇A%)=;4#.7 $*HWfݖA J̒d4uBlcL3`'-*<0"3017E^8~~~d>SCorgcw*Y^ӛgX[?uߢ]iq_r]\ Lפg-CSw$ߓIbLRUL->kEArTAW*BcONؚ'<2?,{  H:YA}TF| %fK1젲vA8E-Z)=H72S/dy>C &(1'fXP@vG Z=r *)җEwDaGKpr@RAWzy0(^0=Xr| ~aщi黁26Blh1kl1x('\埜:C' _y=){-k_x-6@uqq_v$NP6MlfU7jl=2rey::qa:͛02Q̶Bš_,U^E6A%o 1M L: L4JD{{6cB** Ld dgVRhiѺE*  AQSrB O8(1}&tlcn5@pԑպΡ\FΜLYQ`eIVMnЯ"R/XԂ\5WrخE@8'{qBK`ð{ >p@kHrsCS&ʪ |AW- l8tBs j <˞Uva#Ŧ{hw#G-JGI1GwPelCc'it7Mop )_޹̕mQ-/<7AZg &аm#ҹsty+sp>3Oq%!Gb>N0&.TCMd}fQCa`ҕ&ⷚK!V)LH5|܌Q"m=K@ˇ5UV 4̸윙utѹrsE90!5Rn6;((JRLN%I]|G>i-Z.ėAA.38NQpe)ѾY[ LdtV)5="X [Xt779HV%LIi0 Mq8SܡA1Z(~^%yi!Uȃ9<b2g<<'_{B<*cԹ]{>s[9bx=1MmP.]yG}Ts$$%^:8w=?X-:OOb\yM˽My 0 惄V \6V E-JA,|7_U*RMM!Ue%V6o4]NkđķLae,k^|HS ę`,$g {Js}ȯ!ysTd3N1EXuBet]a]aCa@v=UKp^yʎ}q^Xo.{D(,25Bd%fCa*پG+ RyrGubH}*Z蔑[pQVx̏x[o.iO;#_묣ܮn:wOo_pv'z]tfQ.l/?Cӯqro3{} tC8w߽^.ۣl΄,;}AwDV*ԃ d| 1Y=Q{5jx [zB!BTZAESa.ySIf ) ,0pI-gy싥>I)HiEP] Uұ2ݛO7ȲM|!%:fzs!pNsa*R+Cvt!yuU-j*xAz{e  7-L7 q:.;a&e4é#a4 N}Ķ 7d´28^!!'إ4/|n1>_6Z <~ ?}0Gy{/l#_z->56@R }{~ƕO-q_{ ~|G) FO~^aF#EX` i.pgA'Cא Jxi339[&ҠZi K,Y%>*"¼#m=9;jU>aP]E-N Zǔw6kt j1[0y ^e991zp:泜{,}m56`*` 7=P!Y?k 8F36%n1.3#4C<M &}~N3, 68sit´HRxM<0[Ƞ1Et1 lr9j,COs&LoO/]VEZ^5R9ؿ_e? χ<='֨%b@,sN}ay3k#0. M8UU$YOhj@_S20%@bPxyD A^$.[s# t`liΦzlbcK!#*LHQ͑"S4f.WV^yic1>|A[GZW8;m6 5Qf#.0z CWfV(}W9?64'$G YgJzc\+7☊abeHq1Cb7;h=č -1a}^AP) &dGq!~dnݎeƏ6~%x wGXq\Lw?䷇?ŏ\(E>䁷Hm6@Vw{?>}.O{2|}}ثre-&F-?M 9rWDv0]Lؖ}sQ2*I/~P/M< l@>LnPQuYedžl @ VD*X`r?˘zi 8ꎜBT|Df|ZG(SoB3rjId)EDhU i `A}7( erX!A/a!*Z/n(3M5zJeUiyߍ)=1uʳX$A.hIv {~3] Ϳ>'zG͝k@]w=O~s~/{O0tQ|~|;kZVcb k WY56,I.V!DՁ)&ESe@K|a(a`)!b*tYQ@DMܰ"̪9!DBW&bC:zN2t\R%yw Io8A=L~m#&K v,6մ`yto_@ZY((*2*E|lK"dLb,ЄK:ZNG9#{ HA5@AjdQ 5?-^k r8 {#hHGQ Pyt@-#F('N_G&WM\9 9g悜}XOwS'9FZ}^4B!w~2t0tg99%f,3z^ GNǨ|nN_(W39 U#aPT \ D+Uՠ>)ү/}Xz0THj@*'HseAK+ETK-4DsɃ^\TI )f(C+zG~ā'!fSb]̠ydF_B*S (,,8@)) #(cHJh05hPСV*1m95PPZ[,E* UMB5ǮL!Q&ך[*1X^ BV@F|.9›I~?xgH^ * 6afT^wӷKRm!Я &a K u u Ʊy|m|xnw7GluV4+fR/K pMFkh$()(( R&Ʀew;K) (*$Д^5g@[R mAa! HU=(k |$a IHS0Frk#)е28Qbυz!b"тE 1 FB:z*_IV@c.^T 9Ho>c#{ׇ`|1OVQ5RF'EU0Wk-Csj!5YAD!sL4/HֽMIUhltW[1iVEESsT& C-atlYqACEUvc.m&u8~!شuom))* ` P^ Rq5%*BXH'1 sv'π'XკQ'd ^&/;KsBBiҌsEd@K7|ž7!$4j~@U-`lQ J<Qo,wuHxB+ VeY8B˵ JQB *(f壄5VFYt1&Pbiajx킠UN@T&Zb!| 1i1y`&0X$zB΀xY.HxKzX:5Li1MAЂ*aVGKބ6 ڂ@.)lS_3r*3Y9t:Cw|:$ ~`L 2`ZBк=.A0sNcdT>ɻU\vs{;6㽹]_ZU(4$nHTR!ZW! #N,,y!CBDA8rkJHGm@BFh *6P:1Oq%VURIWͲ1:a-*/mBK=w SjjGl 9rx"0EP;L K@hs'b?C?*kfrT>WxlhGv;%V\[pygC_EVq7+9c q%!Я`6kWur!coWO0pK'voaU~cM1 Խ6(qBh LH_ 9Hte*.br9Py1QC!PJקU.BUaѢ*"ꁇ$#Т4ѷc@يe @ =z@veV"y<&I<6ɪm֠2l|(a@`S[U0*(wpK{P uri63 [FX!4Y@X#:He7LmH)Δw>0- yGי|ᕔJD\$ac9d #WS= r߬y MC_<8yt|j6]m33Tt}yqnID21. P2q}sl0$=X;d5 wZވrۈ;Ko&|Kmy BC_(4|`9m|. !*~םZ׎֮aD.?Qk>JWH<$ҨQQҨ(4o)0̰O'bf=ĬP.*ƫ0]%<|SD r=5+12it#BPD)Q&?:P])N(*Cǐ! fu# |7V.ɣ,Xoe!Z !*,q>Z U\> *1`פgbp%}ּA_,?e[cl'%_cYW)xNjZ+¨QQJS%\Ǣ֔jl N8S%JNum.fR ]"E^- \DTC$,-EF5V Ak< ss x6WL4ve8\(˴!4&G@10N4KANՋԏCl*Gu*Ko%ةf2R's\n\LiO+'X%hbeFYnpWb뤿wXsJy[­4>ʢW_b[@ )4v]5C޷9r񥴞vT7Xxs#..c):$ՑYIPHPPji&g7E1-PQY UhfЍ+H"Kb*+DM{aUENQDF"+`*+풋U`VIPx⨇"T\$G#K }LJ&|h/Zgx3%Cպ i}eV]E5.,Vy q\wL2s1jSyR]b++a{;.g{9"$nIR>VLx0|(/ ~3|øgZyQKk 3oԗ8}4E EDLHitUC*MD6IAS I3)$ Ts;,WzF* b#i " o13mB6.('@kyFqSʜ Pt 62++B߂v,a&J(ď||= bgʼnvkgF'" 29'/Iu!BOb"QW F%y5^k,^j~'@w"<(X}Dg,Mwk껃ˁ59t*/# ~S^\pIfʀǽ]/~H<+d)5ߪ78V[@BcUH1Ex!8H?961- i8awEZF [*+KG$b%jo5ep4D  QXP.6 2}6ڮ[ltkG@i\ DO:WRst1Ϩxa?R`00+Q}"Ez:γs7 C*+{"0҉ܳ`/ɍ 9j,$XES!ҼYc̠\Knd%: ==T!ίT`೗ydwaYlRN_xBKnOZQxe5uN -HR1JșWĈ%^пaut,t̋6A"A i!jN" EfTH*hM6N\`*UDQ)%RF'! "2#!o?BB0EmF483JxFK3#jk%zK~ jp _ia=\IE->i{PY_LW3/7R bm ADe}Ca$0=@y}PE28Q@FX / ݌TIx = t}Ut +Z4,N2I,=VyijbpaJIVZ(JK8pX_ٶϜ̓u2f0R ,A-3æP=! 4a.qE-&c`*QNp.ycDhcᓊD!`WcFWhT #ұIg eeNn%a?hF$<6ؒoz=-UHvJн"ɺDNƖRe*mxd6=4/ 2=C">A[}HFX˝R ZQ3O= Z4BJSNC_LN,ƻ'YUgWJh h 8M2x ?>ͦ070NCX2!53v#).zsp5ydCӇݺPgbi"\ c=Xa7 fiki Љ3&,=K-R5h9S{*XB7!OE+\ Xt aQL<YM 5E\ZDiN.QJH&V1n4@B8t5+S/etN3}.* A 6̐Iz~mœD)=cwQ1;e! ccj<39Й|s4Y, dX9BrswhG6zWpY8Φs{v ip:m&NeHV<#gMhJ m=؝ZWmqE_ ! !J vQCgQkfka,RȔhu/' p'Vf%G^k.Qλ6u+ϸ>Hm $Nu4xhtT+)jD,qNI*Go&߶s|A!2=J mM$Ӓ: r:.(`5&_h]|O!@ dz(G٣'9ggӘ9s==Z]ාpIFN); N- n315; y&isy>69@>>Y7a u˸"7,3IKbV5^2C9GXPz oIDAT@\- `ᾰs h9g9"f)ز [ #^ղ5R7@NVd76 93wܣO{.?Nmew ^h3m3w R9@j/dݿ乒C{;u|A?c2~Kr4υb" %t~=88Kpe=wiXM8OKXݿѴs&Uvbj։0%L2Nu} o`_ kl.S绉gW ̨D şDף5S"’- ՘Arm'hh;ch<8,*{u+s~ ؘ_!S*շK͖G{c֪$'h,d4x@7i(B/ Cwоֵ Q x[o?Ըr.bqYK LiZ 8}JDn : ڛ}X5 \.;M8rOv) O)TS>'rfx OLW< nQ*MC JdǎLminʾ[oi@X5Y'v\6=i;غe=]s-ec}V;/IT*()r$OE]\wR(enxogg~n<6%OAMk6C.={m ;/yC&z,aMvnkdL=coėIxO}M`L\qaC74"z7qjtzd3ɥabUhjW^7tG>֬YO>vձk/+/}l>KѭRuMBX÷4PO._sJw1咈8<{ϲC`_wi/=ΑŃ9*!8zȏ@J]M[?ggCK\E9ѷg>^񰳒v?M"E0\J%GѮEZjTᗆ n}{O<^Tعs'w}7O<;v hiiy9r.My>dZ#-pSwǹo!^K>fy\4xtly_ru 2C=ʚkQ(ʕ%&7}2?n ?ʥTN̾}~$J({|xgP!(PsXMnr~zOIRﲏ1]annx nv9~wwy' 7ѣGI|saޠA״@<|#`Ν?!7x#$wwQ\d2@,#HO~ Ї>7|G^W]u^x!֭)9pO? ׋kᦛnsǟx뭷};vbfٱcW_}5wy:<_v򖷰c4ccuS;w288?yٹs)O{]B8BWRkAR_8mC!   leHSc DF 97 RC - W1p8ӹy\y~7|Ub#@񽾼^zgg'oOOOX^^?Sof rfq8>:GTvJ(ˌb竚?恢W:~wR<>>5[NL&C__L!w:R)K^e~XjjjRT 竚@1xxx Hpss>cXr$ k]?LMMaٰZtwwt:ezzLX=J}Y,vwwy~~Z!|p8!>ކjR@ ED J-"bP h['''x^BfG M-ud21>>N<p~~N(baa58@{(KKKtttP(coo]T@3677ikkjr}}MT˅n'---L&k݆HE-ud2D"A4`=rycc0r?^ȯ ZB,b077"pFP₭-nooFEI7LŸtZZ2ɿ q5L紂644yw+д1(ED J-"bP hR@ZDĠ""1(A)ED 3,ੌIENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/besselj_c.png0000644000175000017500000011105612253362407023342 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATx[lU&y/U266UvWjsg"/V f`I&dJfpwB&dI&|0$L2.@O2$ܥ2$L2]*@O2$ܥ2$L2]*@O2$ܥ2$L2]*@O2$ܥ2$L2]*@O2$ܥ2$L2]*@O2$ܥ2$L2]*@O2$ܥ2$L2]*@O2$ܥ2$L2]*@O2$ܥ2$L2]*@O2$ܥ2$L2]*@O2$ܥ2z'nxߎŸ}6~4$\3tC}kX׸u;IL25A75y 뻾  f}N$Lrd膼} oxwoo--xt1 W `W >w7c[{;kLgqrFNv{eSwwɥ''qkisdo-Wۊ5ڙSܸyW1;e,wvz7Nklksܺ}0姟I&nʓO>w?3| _c=W |ӓz=H حk|C?M+Q?JUӦCxfn㳿S?R\Uo|S1@ U:vvVm+lMzV[O96ڏѣG5Z9^qT63MS'T/`'@7䛿~y .M1fR%Zْ ^FY&vU븆;V2&)9ڮTPE*V-ֽJ鞖 @zQ0WJlrE}xⱇ~K|xFS <艇?ߖfc򅐬^^}[vAgw2Sܓ6pvy⡗؝Ψ)x%OiK'jtJ9^k!@) ' 30B+ҚGx=|\eY~oz<6 |jFk򫶤L#o Ww=ffg'Wm Zj-1abRڵN{C? S'պ[s}7_][zoqlUV_O.opuY:gljklQIWRWÄ2~OQsi2€[^G5 4lܟln/b׹uZS}:+e;1F?IiVއ[ )-v^/mEe&JJݻ e f'_#gлxgEU/gO55DKF*ŨLV0fIL$h ,F7<ʅ Ʋ>!ىfSƺQ+uJS>/S. lԾ2i%ҏ}ɭ vK,ͽ"l{TnІ@6K&dQ-V1\h~O+ ȵ5!+J< ь4ӄA&S^E_w%R %vSMmtINE=V=xE k7>kd/6U52ZFs.9խQKEEE=g/w޲hчdw]ji9A,zmu '?xq_Hk@ ЭE*\<4r`ŠV[ui K(B98U>T(Bݻ8`:jݖo=05UM Wם\Z&`l6fILݔ}۷x~~ ԧ#8,bRG:Z梅P(-򮐣"uY_270 TIU~4mԞUZl=p?~Ź=ةK@GRT]ER@ww>2tC~a\]]|AK"d4d"D͢V߰q! 5شXs4hKW>%Xs/Mj!#% Nl1T ]$['kORY'>fI4NdE®F_JB9ڬ"3IpZw{AӮ xė}["k װ6i:Uf%}wjEuы褷.dDzpj8*Y&re=U`HJ W=جbupZ9Mn~*CkJBN*F(LŠe 2)9D(~Иvغ8!ܺyOs]#e&Kg..\O.&2[bn!)V72I9y"i8Y@.n3!s Ի4 =kjF*¥Sun -eRi.$>y3=O&SANJL4 TW&>QKK)/nwIJp.3;*Rۀ>DʖLjTf:B(Lb! Pk|ős.&0Ȣ:f{K.OΑN4YANYc6{uȾn eCrC=Av{\T2=YK}3+j|)1EAdP,~.:i t@h8JԵ @6 /ET& P϶beC㠤b/ڪUhnʴZg[d4uƃqoR;m#+Q ZéY'k6@dA-Oncckg%ȞTltٽ骷jV~lϭ?4@.G#i -5HkRQIG' {F.㠷ԛ=1y K2ӠG(a-D Rx7w.ב~aԾaXKf.jfi;bWGI(dw?n4tIxtT3t pKFqPȉc -Nn] ;Nj,:k{aU)^[⽚}.HcULĮ5ut&TS<9Vgr<~JQ ֬q}eo|׼?3?av} ]O_WƘo!|iŨ&VRYtT# ]kI^G1\Rq KudVn%uQ=aK(8tp*c3ʳf ߆Z&w><.Y6pЖ@HB>֔_?RHqQ AZ(Y>L>U ٺ&<Y$xdUF Ed e7]' u79ǰ) Gx+䇾iV[-cdYH;hU2" VIFT+!29J٫L%%%NOO3/PNB<5؛% GTk cEN_,eUA6!.VAԒթeDˤPfuoy/pf|}EE} G? |#E|?}QNgV8ykvI:0)xǾ=p \j[կ~5^WϺ:M \.ۊPS6K >5HKjOLu# NI$}P v6ͼz%]aQǨFrdqځ`iFr5ae?ьKu>xݞWv;4VGL0>)(8T<@`6{ %ŎE* *EU@5AS_PZ}]cH4Jt.{U;sZS#͈SUn[ Qۏ^eL Uޮ+Z/Nl%~@\4( dMaFPHAѤ!$j(:sؤMͼRυ( IDATe*YYBܼIP$Mj[fnvVXx~+Z&XRnTb-T4hxU)R/AkK wHJጜA PkgV!zrmk;\׎лYf'2ޒ3G"1ց T"wH H" ZOj]筲B,Ubv-`ZUtc9`y89/]J_#]Df1?t!eDzOa,[(OD͸+.(' mXC/ aKE CD1?8kᅷ?l+@_Jƀ9|2QmI ZmG36MHU>mJzOq.RtQu(sF«6} \:?C~k̭e#c8 H)V .;/!iUDT{,iF]ufo4ǧP-C8CK]r4+s=A ,l浓 rDN 9,IYPkK9ě} ;5PWؤAĕj:jGTUA62cIhEZC"Z*e'H蠹,X_nKe,t:˾A\"-oK&S>N:akeM@PȀVҲ)&T8qzηs(8u2J#F eYHpN K?dyh1dEC]M77%u ȠE#)2Z]9)!9z"R>!hA+ P5Vv, ɛ; [إ*t}Ҷ2BUZX8Y 8\UP&SҙeԭkeТv-&k g#5~8|Kִ *05{[~-%`CEveyiYV'$K ^c,VEېP 0h1Q«VRW&S\˫c͑'.YÁ2#* $:R6xcZEc+̋f=cweHc zآ"FYR`dwOl2N+[WއExIBc77 FUz% A7e轅aOZ]u6臆\/*{ԈZ9sFǣ7:jj+ߘj;5gQrO_wi0zlPP!IʢK@͢rJ+zc,]@C&S,NeKH %HqNOk)pb$_,P9mSK r Qj~E&!,=|IBajj7rjWU:-fZ[47Bk)@),nm1DݥƷW%Ԯy؏tq;i@ "͠s`1N:\|RvEqoJ2,T$BJq[1IHրhNԵw!qEre៫szw$n:]ݫrJBGCE Bh-Qtdس n*:oZ@uXfrcIiLN@}Ր2=4+Ĺ%!~4gVsm7LJIHۏChW z{e-0z5Q&[Gi{c` r FnonI k1Ҽ A:ű }UX U}EBFTI!=)h[dx܃exe,cwl ?:H N{rJ#SHP>1]_$ ˲q7oٽ/:dU ºOK17Kn ..sAe] |Nd-{{xӛ?;^FpG$oZޠ  C@ :=IJ ˀZ =1z j&@S]60+, !.`̨ʥ*HcIeg5)( 8\ds y`Ϥ1DRBVLd_u,KOdgo_%f/c2ZȪWЂ}T`Yౕ.*ڂ q.0KU{GޔdSZQ]zZp9yFzG)M5xi@7 -FSΐT% M r~rm9m&޻XXϢ%Æ_?c\,&q2tCwwO'~'[[s?)x [\OT| pf3tPlKs`y <,΄<6gO5y8ܲ0!_9DAVtT#-V‚A VaMtfw#z{qE9OJff oQ$N&n[-o|׸KĻ2mOnAg %jR5v:uZX9UsOA؄ .= wsnCi˽5?ze! $̶+9.N(D-k[,چ6 In>6$f,[2: HL_rCPiph $+:*QrH%] U3<@;2R;V's"Š98Ԋz|ɠǀrs8”!°ɢ9ZQlRv#Ի{܅Grdg@٥2.s*k$@+[ܬ=9)+gPOge&qՈ@62{=zH~p84U`oa7 R235iK@T9c!4Ժ)Vpj;#>4{ 2e d? ej w&d=xeWɒF2@gɦ+:Z֚A[*Ln8*Wph;% ʂҀ\~b:7 k2Hh%HG00(t ,?ieCg@ٱ\cE5g -/ꖟ<2 LD ି"MKӴ0 QP:0Ҁe=gC@^ِo|M ˰rh< ٿgٍߤ2~ypv8ve$Z`!0уЉу@".F}鯖UY788b3縟1jr؉AKz_a! (9,:{/A>hQZ2Zg*jA9.6@[x%H(8afm$F.Y8ΑR*`Мy򈂒<K@ʽ@<8~ȷفr`k0`- Zo )#بƐ m<6l^t,`x龟:h\h2J/d^jxY\5Q8žC}=܅f%`rr'"+]Y1f {tXQ0 }5rpր:Hzh~6y|+@ZTjs`s30=P`a[nYuKV0B*m a`Bό^2,!l IBD: nOȒ%hAg#~h!wspQ)D)} Q,@-{wpƜ3DWeb0g3t.fb˔&{3"`w1Pa %Jsm_Uk3g`\st|AgQ98>"V.(--m/G/{c.=fqƂLd=aJcCuڒwoZh偺ll\uW\i@¹ d*a ;w\] -Y$HG,`mH grC Z{S6=̵Re|W9'fщk&^g00ZW3^ 䉣4"W`AE2̩$(ϣ.^ zŨ?6y2$98&:0c@ +(s&Âz `2aAK,/*w&vvF)ORyރਭx^vΰk9㖣KW"wL:`АHQ7$H˓ /p5F 2{S1q+ ӝ7B}_ żtcd{X++X4rb1Q +x6УNS?Sxހ=888}/qfʇz1 ,ns l}j3KU3p%`Ed*,0Nf]ԀspI0 AGpdC:h$~iz<.$SaoE`~;/6@bl7nծWZ^j9Н|9Üp:t|EGi-@HijOs:f `y`1 epol:7'. SGitC(__^ܗlz`X Vn&W>Ca.!.{8<</;ίI E6wx8`/>9gD̳6&9s. ݕw{fM=60_t "h+LsQ&ƃ?C\p`8|̠8p$@ú 9@swĘdNy ZN⤽뀽1X <` ^Yp mBw3fv@l/'jQ ^c!w;rB]laf G>ֆc80u #gҲeGAy*p:[`npiW9ss0@^2}_z/~(ξr Y`X +آ30ppq׀]3̆`7IɈAh|dr86 \H3ccs97فn|@8$` ,& ~h:Ӂdxd&!XcKrc%n~Wzv.9pFn[ؓް@G:ƯگאA/08`k 6WXD@7u n3GmA!^,>\A@[!, + €6A Xd<=@tl#dKc޹I9AqqPbrS\na;+op cs"fWStI~ bYnGV V(GCwvW|㜀%  ́g@7ɕM({w,m e):g Q>!.X[57k.Ԕџiˣ&跿ƓO>o/..?899<__1n;|qp}lv=jyk t0&"[v` [=8 eft $*0'1 =α ƀ܂odЋ<c :w0h3 AvL9vLQT1&%=;e%[7D ]h0,}^W~9@W.890pa gK|re_.?)Pە[c\t9 zC.*)&ﻼ B,zZ* `~gZmp~ρ͗ ,݈ŬŃ`Cp( = |;ַ> ~<o?||~~??|3xK^Wapu7^ B>lX`u\[gX}`v ]]l݄eeo͹ rqHkR4G7A8À6|5?#{Gn<ϟ'8fḴe̝H9s&(wH:{K??>^lo `s K3\~Ơ{0p}W3&Rs"˅o BFgװ0*#lA8#7a^v|朋rYN$΅CnG_׀=#3a4޹E$NY=Z??8SO=׾g?glfև!py } χ51\bpRw)X>0y1> /} # ,pG|瞑yl<1 eƻ-2p.!8°$quml> fl } N>9>ـ".(SZٛѾ!;$ߺ?7p xnXq[ \|| ^̓Kz`;a 6WWGc 37p?9g17`'|"!;I]~衇wӟ4^Wӟ4~a]`'` _/Nap+ S a@t D ,8$coGB.)GRJ9;Z0#= 3# <| t `93:p6ST8䘝S֤:kxԸ M|E.*O:pf30 .~f;HڜǕN6.C>/"p$ʉwEYc\LwĘ?hq/pRA`y_.lbRP')1;edd3g+8n 388~%ї`=yS g,V°qnwͮܳ ~L?7o{{c,^ouyES~)0'XW0X!e%;3E '4AIFOnDf',^,onvS;̃t СwBk$ ~Wd$@9ɸp`k͞fJ|II‹<|9txapWQm.gchcuY7Zq1 *?F >Ηe̿<& vn0 @G:h83H m"uG{pʓs&9Sxy#sxr= a+p!^ڹ}j޹rO'> _z*@W4:90_ n`Cf E,<*t`4l"HPhsY莁}$\=@wg=sw[aa,tM$hq)Z :sDwLn<y@i,Sgs2A <¸Y6wiJH|\ @Z]mj_pe :g2gg3=K8ȁ Z`f8@Gtmo"K$ SH߄h@Ҩ%u'gľ|h9$tG I aÓ80Spwt3P1!N&S0eÔlc4neNuDaϊ%:Ðiwa"Ik.4@c3 |ep3%grW0*k~ot a D<[aW9ʰC>$7w < 7<:&AY݆> 5~2>PXM=(݄ȃCg9NT m_ 8Q @S8-bmp,@{{a dso:_ a;ӈ ,<>a9c^2`Ls\80, sĸ6ݙ́[z:],s򢃗).##&9n|)N 1[h 9Sw̳50t{{1ʯӸ%I _bKr=(Б f04UaC&Btn%3]8$%a3pA'=q ~cXKS?d<p%kHPrWF, 5M{8[ "==՞p qgz8D "D0'hK6Y5,hRB p̙{K/Pf-{ȱg$ csv7:KYI[C1&MbС Ƴg뷿5qπ!GjZt0v)tL Z= ?;;_C'Ҹ"&tE6n#3]tgʚ@``=0n~wcЀA7\6i.1+ 9(4~GB?(00+z&6_b-Aᄓl |08cs$JwF_|;Ft8$0CYE9`q$3hnTpnb[*+:&M\446`ࣥ]+0EE.;ɍ8weW.-py&OyoqHKv;Ο8| &#aw]ޯޤn*sl :XPX*is (tFDŽ; }|2^& 8b`1'N6;4z25x3FpIp-D!R]XM֓߬aHݍZ\iqK2bI3 a=@H| V4EfN27H{l:Dv 7Њbzq0~fB!u'ɑE[ҁMwd[,4? oC5JȤznN= k-;A3hØqkI>*cBBTA ax&䠟 ҏ95|GX7+ tؙ8t6'v_+:hB: 2iD92ɫ',`@Gr11w: !啟Y9z8ޘ%s7X:΅ zc1 ;.3ڴ|90Bz4VQ# ~ ȏb8߸nr|0iHWX A0 &_S9 >>zrqg5yͿ̃@CvO Qdﲙv\5E 7A s/2t+NnÜ9s:'ICYB>>datxe-cnj,U(P[0u`YD0"a6@DCafA0H.};#u1OѐnBM:t~  NLtס˃]e RHbBQ y!4P4j4Z,ss] #KQK@g_Nȯr00hH"Ȟ@&Ap+1fi$I8m(k{| G, nmë <+ SpA=-p4zœ8tFz߇uQ@"C5<(?c3EUI!` t{r;Ann>;"F4{A~U Ďqިۿac*I&WRX"Z{,FOfj;7F_4xAsΞӷYi3mw` rY3_3j1\apSM@-شw1߱HR1& \wO3;f=b6n&Qnan>`tX\|BUJL92$3^nr!,42 X1jF73;HJ P , y;W4w")PF<+9R >+& ±M;Bdw E V@Π%`=,q8ybנ!(``qlD69j;G[z­~ G!CE.K?6j Ln#@#Z'e-VRwn$"& K`‚ Cgl^V0EݎE=Xgwg5tNCV@ub3>t@zO!(eMPp G; ,(}k8wAEmE8 tAE6e$p̵!1KA?$P8dХ]G&,@;("!W8L C4wA3G\ :Bgw}tpoozN7K-/-f3$xVE+? |m3,/ A0`\ZURcص <#晳|oebMvd.}HpfJ o| KX ߿+:tv \s- Y`2sWvh=SΞY_K@&jeH=\=NhAD"P'-ˡ@zo ,Hg0UQH5`D U*AB B?jArp|,\o6rrD0w k4'Bغ|-JN\ʆ`4y6ilWX"rV+E-Q(:G*FaNgN~gY -M,m<t.@-@g9}Š&0Ga 9neW٠>G[E$,zF(> Y ^eNQ`"6 at/w0w~RZʧĢgpy`<.U,_-\Y&Ȧ 0~( T eZ @y:{ F ޓr_!m;k':q@]ix|6*ZjKA06" H2pLοCا= \u᪟~.F98G6&' >'(u/7嘮fgId={-u\t n5$I& F7ki4-ֆفɕ@Q8.毢X@-`I[h2Q' DbM$^`%QL%M1G=D=UBcm)Ϝgf5k>R|f5ggY{7kY3}n{$Δ:FAhWlZ.:@ Iɰ7?CϦ#ۢ=8e `٭7Vze#n]EOˀ7zM)FV~=#k>rI4 w(?Ғ:@9ҩD6dXو4h"9`H.fz m6wst[D Fׯ3k){McPedBnՑRT?k">Ayk!ŤEm1h"j,́zP. f`0ŜGqh\fAgj=/ ] _U*kJ3~PUU`,hVt-[jNQoa-eEM^?"JXIE ^& 3gWj;ҭ?؁(YYL>,-C`nUκZS9@B o OeLV@lB85cJN!Aٶq/"68m ph(p OeZe>s( fA}Z."kAk6^K;f=DB #}99ˊvg2vZK hA[mP6Yh )~ 92f]X϶|.꙽w"8s.QGr_^^O@<Ҍ[ ;ou% i@!uЭc=`/9z~ѯ9L4ǜlKFxh4^ 듺UBomΊ}0D IDATo%E,aoi{]Ay0LF$  CǙL 1CߺߤuQH Ŏ#h';`qs`̺K^b-ɞۈGU twdXlK ۷[nիqa~8~:٦!]f`RE1 rӵ_/V?QK yW\6 5ۑ1(Y} ,l^L{CS1% elfM!{)` g]0Ԇ*u7I20 $ޅO}к;r5xδzزeyflڴ opС '0F,_n@2RTdq[X$QhŹ/O/|yXIb@J0d 9(@ߋ+G'л0iP'٠ԫ!*+_H `MD3uJ T ͅ%V^zD+7l؀ᄏ~};m8Cp 9avU1RXzT4q$X %p3ޭ֫΍]u&iQ O]>/,v9Y4b㥑IL >;$iqvNE8h@:,q`j處\̯d[W.Vu();`I=0G'V*xƍqF\uUkqYgf'+?k E Pډj^Ů17G졒i$Kȕ40V4sƂ A23&5C?l&jи(J繣%,ueXL>_t"T>@ a%hUD*;;T'oկƹ_~RLjmg/=B?qgyj0 :&W+ @/6M #:q`, e Ϻ 3xtňP硰c u|KPV0뙀.d&P[ ǚfŦ_H[>\Y-:Q9@0/Xmhq,7w!]5$!V ϘI5sK:y#a7JNv*NC f=ɾ* G~M {;vy(4DY7s HW'8/iR9tpX!Aj~j`VH' 7SnEK.As)Bu$ ZRuOLfpW9Na0 ]>:hA"D n4d@蹉/I Mn`ZC `ŔsQĜSЃਗm ]bLWuJVј3+kq&š6F"H"^l}cmXϠ!9D6~ڠU3&HڴL 5<a*c7zRŤXB3fC[^~Y$]Tېw*^R򃾀#?ca^ l)E JMHwD;FR=@q4tuPfՑF4Nz 4cmS1#iS&2*A_]%-htn!j׃uYD*$qT4Ոe֣tӄP\Z}mDCV H8fCWƶ5$8GP3@fYJz:o4:"tqrVb[Hhٺ{a-c{ H. Xҍ%q7ukl*8IMԁ*1_pwNL!v 胶̋*=gg9h=7z@pi ?z ̤]:AGnhTznu_t5:Yj.v#}2%)0m^}s1/-^$6Z1@i?G B^}j`EJrm(AsY5|fzN"?EPƬ[м =僎[ I&*)YSE)FRVVVgU>82ػ {E!oё&r,Z崡ΥS9ֶ5;hS41wtsRP9 Q8`TP[  ˤh 8<XByqОYMbX%D=. !~-Z Y<.<,b܉;xlDfFtja$0aq9 +aE2H(|ܲkl$+\%z +]K b !˟gǂcJˉ* C!DLUUXZ[ ~ZGsRuM, Ywb68}+/=FxYCaMēre 6JƀZ<4ӎ%R9` 4R[{z}L^H] ^^NE%xEvq 6 CYTgάs?9眃_\'wqpC2eWF^Y};6lzb\L[F!hblʐݱڈYx 9^B!,X$lƏ5@m…HzÆ …wy':?0\|q\ry%Z͐f]W¬Ve 5SF­$bZdЀw 5@ 8-;2=DDjkA[t4էU5@荞Ql< Z@Er E?rsϔYڢ7bƍkCPJ :ZVWκ;so)>?ׁA0xj@YnV`ad-y !B>W%7fA{NXἶ5b}W98V #t]w:|NOƹgщhnA;Ma$X 33s%J엪;tma ˾j 8)tpGPtN:gf)WbXI^v>{sA_ũ+@I:J[Onuhx2"X*eo?mckȫ,(hAsKzqSr8|FpV)s[[ב=pgb6%f86F'j='y\"%^ڙG9XPɑhŏ_0ê_`͡ :>Iwlp$sO>$AK`t^H])H}J[ނj 3l@IB7;p2(zh8:i|a8/i6&G梜|.bwela? #5Ձ:ڔP)hq>/D+ $[2哊6`7YXKu?W#ưsKThj Z%AUphUa9Z`P3vҞe4B e.F'`Mv]_%1:'˔sˊ'bN}uVLF@)AѪ_r4CɊ|(!D^Qqtr} E/*,z'j='jHmoYH3p'HXH/4q X6IPp a5HP\*3@cboa9MA-pRԭEl6YFNAu#AjsȜh>D 4HPs#$̺hZug?Wd0j*4"Mg:H$~"h _8%J{BjJMx-z-hݕHG֫SBh=7_t720VF2tG]]G2O} WCaGYLb{a%ͨI2/J#*"@C08|Z+D7h|s&(Ɠ͂Nz^|VYJٍ4<あb(o9Tjт.{𻧢 z@aK(CHH̹5M&F:c5׆b=Ctܢzc4,L# '5pzNJavF:*Y否 5#'uH*KX(#vkPiX=f9Tu`4uH[s{tt( {a*/0;1C.)FjtsA;8]^\ԱҳRB:.?{-Q\b%wQL}k/C -u>?r8[GYHMJrCm-Hv*RPq[z#q-Lx#D@vq8 $8mҪvQ͒{G&Uɍ]O R ᎖N&)a|:O9w0Z] ̀-H+AGG/QzkAS ck6|NzNq֗%PPaٓ6.)TL#e) l4{a"Xԑ)݄PwAcg{k&G*XdhFv60wVB bHDzzL"44zN"00RlH0A Tq=nE;S<`ړ)fdB2&*a琤TwW3&1'7ie0XvvS!09Qy0(?YTfđYASA(IZ*C$-J҂N^ٓOSS;]/ri吏ISF `o1C Y CJCgN $R[̧k, v͵4t/͍ dC9/K9 `"ԷE \- n*7kPi!bZЎ +9*pTJ4QcM%8hY:\ՄVm8IHK7 <:^dOz9 q)\Wpg fA'jgFdjJbOfix=CG1R1/KGxdi3[*kix:ԡQh tyJHV:˝FJcbې90܂q7.WEd{@;}v'믿k׮0T4VaOo9Ujjv0"OMiQATnzat]*u&5vqyZO.e~0P3*SPw8HzزevO-܂;wbΝiHޘuіVun!B'*K%GlX4+䫥_1Gg~ʮ4AW+EkY$0rv($V{8u9'D+7l؀[\{'֮]z({C8{F *ɞ2iE}~ۜ5?yˍAmÛLc4!O윇1Q-gއ5ƓX?%WSܺ6vx9)hъh18pv[w+ORY(Rյv{IDATlMhĿJAj )@cqKsܽ'τ ڼ^Ay+YW C(W(Vsc)Z7`lPId݈0muy^ (~=I@ `^'8i4&kଘiN&%+NmKw4 ˵\8f? 0*6#]Y֍~)ݼIZn 7ׂWJsR&Bk_Wfb%u66,KRKaN [5,Y+PJcEX2Tqh.ǽrnA뒇 aٸAUC?ӨDY+J{QhVu /f!BQn A'0_ʂ@'0ug炣J=9YHb!ZmīB`j"M w,Ɖ!,5ґ9>B DH%TfSZEeTs Th“ώxx)ޖx|*?4/T)AMspqmప 3zC* .S Z6A[L:m*jAKl<>4CqMg^ /8X0n8c A3S GW5òyձr( xhų,$8WLƕ)𘮩҄[Wt͝ĕ AdxsLd]=8hG(Rɶ܎Pǰ]EaD @f=l&j9Fä:FM٪P꾅#,u>e-OnŎ5Q<4 Z6q$$L^U[,VnA>,MX0,.¥@,bm蹸m d0ZBHz .cjf8n7ײy,蘨NOOʀ"=V2Oc5*`L &*w|uA}Y{pJ^ M]Lyc+@IA-;LPVG|X 1% Ɛ3y ~G;BU/_oxLQZ1/uw1 ЋRq^=bEӿQ,.*(BOT{j='Ax%0/T6>Sy1W]VyJ-Zе+sIsuj=&g/<J.vQ:!54so["]6o^dnl ,SS-_sq$jҕX ƠJ:1-\S $#y"81-׏UtpCǬ4Aq"vTyoaĔ*/VwNAEj='Sd8jypEY+eűۚ\S敁QFz"%/rLF4eE,#\r6[tڗ/9:RP7kXlDP`%89NhH-)c&Y)\OzмIg]Erg=h|I)RbQ9 GgLult8k,h/g])j7;Hu#_S&U䞂8/~n%ZNqyl][WB"#+@It ,L+nn1VisQҹʷڥ-lb8,J14(8Σ4zlT׳yU1ծɨEV TI[GKΩWV[,h/KдV%,nSlYN= 旊ST_i`.w%S@F#d461׃ <54j='EkM(%w8(&V3mQlq֜Gj.- g* qfߐ-IšO)Mł̶`fMA:mD*kua*Osl\.'8Zjrk.w,Ƶ1jŧv2vg3]&G츘>9<-&rEQ90;>fWiñژk?#yM[fMA[ddcF܊V,i|9*hV)lu:3mpdծt6RfV%#dl+@/5$T*2?K>{ v:<1 .]uXyBQb>A5w{1giz}2x,\LΜGV&]q'>~طo_Na!MBI+Nz=ÓdG ,F]a:"#{q l.YI؞G+ #}|¨dл{a|)HI3NYxrFyEݫthӽYI3>t\7ő#G} ]txqc >Nis`eJ {UƑji +5K``]ݻ𘏇-OJ.,d{'ܬ~--]c\X']= j@2^?@GZ.ݻwc˖-͛u>]wݕ%Ȭtp./"H&65}&إ#Z*3~[?/S%[B,1IP %36R8&(,<6Px-DvΝ;/?#8o}+VZO>r N9eΣj^l^nxAƕW^38{M7݄7 /X5Zaz tW`͚5ӟXnݲھ};~_O_k./?w}طon݊իWøq 'SƇ?aYv/9'?w]<8q//x|;O}jOO=֭[oqxO'> կO?o}x Icǒߏ;ve/{Ú5k?9眳<`ݺuؾ};mۆO? ǜw܁}ضmz)<ǜGw]7۷?|}<裘f8Ӗ< /_&wJ?HXy5 .'x"nܹ;w_,6mڄ_׸oQyflڴ op!nj_{ /Ş={pgs>oߎ.lYyg?駟s=XV^ x[ނ뮻G{nlذaYx{?G_[?ީt6lP|ڵ vJ۷oOǚדO>|vZSO=|+/};>ࢋ.l6í޺l"g?/K|\6GG>U?o|=>bطooᤓNZ6~/jݨQF)AF5:NtFK~ӟx*|?5݋~?ѣ-fFKЍ^DDp-=~_᪫µ^n p-/ǁp 7`֭83p\r%"Q#@7zR?p?9nV~x+^G}=|AyXv->gŎ;pI' .bQ#Zt,.R|S•W^?+׼&=կo|#wNO~\s SOő#Gk.l۶ ?8n=z=aߚ^߿7o.x㍸+׾EQqjtk袋׿޼w饗6pnt\S5j8fA7jԨqJ 5j8Ѝ5jtRF5:NtFQFSjݨQF)5nԨQ@7jԨqJDǏ%IENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/besseli_c.py0000644000175000017500000000016312253362407023201 0ustar georgeskgeorgesk# Modified Bessel function I_n(z) in the complex plane cplot(lambda z: besseli(1,z), [-8,8], [-8,8], points=50000) sympy-0.7.4.1/doc/src/modules/mpmath/plots/chebyt.py0000644000175000017500000000033512253362407022530 0ustar georgeskgeorgesk# Chebyshev polynomials T_n(x) on [-1,1] for n=0,1,2,3,4 f0 = lambda x: chebyt(0,x) f1 = lambda x: chebyt(1,x) f2 = lambda x: chebyt(2,x) f3 = lambda x: chebyt(3,x) f4 = lambda x: chebyt(4,x) plot([f0,f1,f2,f3,f4],[-1,1])sympy-0.7.4.1/doc/src/modules/mpmath/plots/kleinj2.py0000644000175000017500000000017312253362407022610 0ustar georgeskgeorgesk# Klein J-function as function of the half-period ratio fp.cplot(lambda t: fp.kleinj(tau=t), [-1,2], [0,1.5], points=50000)sympy-0.7.4.1/doc/src/modules/mpmath/plots/besselk.png0000644000175000017500000003562412253362407023047 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATxyTՙ[[{7;2 J\81F4IL8c[`Ɉ#H~]iz꾿?.tVUWy|Sݷnzhۇ{ιi |ZEA޸-  F HAP)Ђ "ZAQ@ (hAE-(RAE HAP)Ђ "ZAQ@ (hAE-(RAE HAP)Ђ "ZAQ@ (zʕ}z/I BJQ@\. 8|0Æ K BJ1L\ nHq%;[E!ex- \GGf\.Ldȑ4TUr%#8t꩔@NNC=l2vұns;zoUUÆ mm>tIKl vg .uvvDcc#lذ!ծi5_6osƌ>}|ҤIׯ7M0.s97&0~{q4M\ :FQ4%~zzL>v<;;~g=;\_.f-++pst6v:FQE a`F;^/&vv-<@K=]ҍQ%V腖zȑGFG=GfuAm qK8@'@ `?tKKKמ"c zq.hYCt#x *))=]ҍQE,!A'7n\AtI7bG]в@:t~sdBJ[GxDQ)l;x肖:lH6GsG3CBfIWKW\R".vTq- tؐ06GtP)l;x肖:lHV#WCrTE\ Z!!A .VqRCAU<@\tA6$39_78v0dH*-bG]P@.R~َ%ŋUB R".vTq >mÆb8Z!C4U [Ŏ* .l~w(--㎳5$2$LӆI*-bG]P@lܸ'x%K}6ô Xx1 .Rv6Gq4t0Z=X߻pB۱;h@} MֱP.\qXyyy~ؚ5kXx1= nyT+Wz9sf7\wii~zu&aZ2{{R\ s ]GeGA;<^b S$$TU\Tq t(+$f`äH+ S=NE\ Z!anpxKL;$! ⢊.hY#$Ϸv|xZVA>~cDPiEQE,WYύ%0 |}tLݖ*;x肖:bHAQ à(rpxz :jSׇV)l;x肖:bHAm/po+*-bG]в@G hq ށVP>JaQE,dž 8|R?V)l;x肖BBp>#\AwH]V)l;x肖:04^t*[*-bG]в@9.Vu5a{-!aQE]в@Ǵ0E-pG:F*-bG]в@G nIY#\JaQE,CB 86ivaC# u#hq.hY#` nܹU [Ŏ* .eBBA)r*-bG]в@G !v*$˽S4ZE\ Z!!t)Hroq.hYc !]a]I_R".vTq- tL!!r !˽U [Ŏ* .eϐ6G}[=]fp/R**-bG]в@gH\.)Hroq.hY3$3}M)HZE\ ZC8GЃr9zpzJaQE,~5 ܗ`PΑ:0U˽U [Ŏ* .ef7sB-:JԎU [Ŏ* .e͵B¦0'$:iqD 8\^bOz*-bG]в@VH@6/Qύpj颳3.'U<@\tA]P`a timGG1ێ`R".vTq- e$ [㺾t#&U<@\tAi$ [=(*@;-Hbq.hY󣄄`9# 88RXEE\ Z薖(!!$އDKG MH=HZE\ ZJ˜ t}hGzBU<@\tAmQBBH@F X}#:EE\ Zh?JH)AG 822hR".vTq- tII!!:jH8Fj JaQE-k׮[o{{o(#h7؇ӌ%VT [Ŏ* .I@8Əٳ袋zVRRBNNZQFбe14#.XP)l;x育#hEq饗2y^[$d OMZE\ J諮_իWaee%1 ,/JKZ;z,ya5n鼕+WF^FYTS..X⒪cb?V]]?f/^C=DCC*a8-"}Yt)~LynF֮ǺuP[kQ[ %%pivo ucǰm I1_ Ǎ7ȼyҭэ#K.?̞=Wq랄ardpH9<JaQE-Б())^].k} ͞3F#5$/*-bG]в@CB!(lo6MXfr@U<@\tA{~ **@"K\ Z肂跾e7Gv̮[Ҋ,q.hYc !%˽c 8220fn;R".vTq- tL!!kCj%T [Ŏ* .evB.5HjBq.hYSF(бɡR".vTq- tCH-XThq.hYSmC`کU<@\tA0ǗCqV1vaY4$JaQE,Щ F5ʁ!_9$*!aPE]в@ #^obp$UU!_5peYRUT [Ŏ* .e.((С'5N`äc `gΐ; 8220;L:bq.@ܹsc:~C(.НBg'ĹȂ###h'GU [Ŏ* .}w0{lJJJ7n̜97|n(.{`:ZvpLq.o/@M4''= 1}[ZZzy"9ڸmg,v߈ .tvv6>+`}#h'G֘,ZOjR".vTqхHkk+]tk1DŽ99]UL7'I,Э_CK"U<@\t!-!)Sؾ};wCGn8AGMCcCC{mםGhq.@?S+<3~_rd]Lz`yoшԇvpd ˠmg]}¡R".vTqх觟~,ʋ/0HRE NWAe]VPYbG]Hy5k?<[ne׮]\;3իW| G!a2 tӀ-mq.<$=z4?477s)p=P\\5!aLz@Ȁ;vnqA; 83(T)l;xBG+VG_Qq{Hr֚#H-Ghq.|`̙F0$yȑm8Dǟi?GqWU [Ŏ* .t^^wy'#G~8JB@C}A}!{ЎC׃V)l;x肖#*8 p1`Z?&;ǍoϚjבܩv*-bG]в@CBuui$nS<G֘,̀I{ur7MR)l;x肖PMmB'@YO_AJaQE,`0y׮oDڅO&R".vTq- t0$5zaNhn&G<GU [Ŏ* .en1c;"㱊޽]Y7A<Ghq.hY{q V4B3 2*V>OH8 JaQE,=C`#j7 {Yl9ή|ށ^Z+[1;woq.hY{@'80 N(=@[>tGq٘fRwS)l;x肖gHem\Ueݼ;,Ijw€|psx[|5ooPU<@\tAgӣQ oJTK}<68Z 8rNry JaQErOXY|9/"@ѣGs=CB?aHzM 8rO}R".vTqG^x!scʕY6\#D tGtGU [Ŏ* .tsqWt3$Bgf A4 /N77cxo ZO_T [Ŏ* .|~iii;>VUUł Xx1 .d9w&G"GY^uMݡc"U<@\tAs%afr ٰ!]<۽0UP.s8AǾH VdU<@\tAw%!8q`~%c߼C-^݇^xZYbG]в@ !4$cODd.XQ)l;x肖oH  9}7&p$sɷJaQE,БB¨'a&DŽlg G2F*-bG]в@ mv KZg ; O|p;KfںJaQE,СBBÀOVؼ9ě yyq#tWcH[ϖ[F0ȟaXޘеT [Ŏ* .eqzu75nnNho賆EIka̟'tq.hYCp@i $q3XNÇmԢR".vTq- tbACR`zo޸$< 'PE\ ZP!!XXHT3Z >qqsZ~ګ㾎JaQE,СBBgi爵7 -"+>ʝԶ} hsU<@\tA.$Х֭lGg;Jah=䟓xPR".vTq- tO?ݺgqг`/|;Z*dP)l;x肖:\H tpދagXgUyL&A>6= U [Ŏ* .e ju/6l_?n 'pjs6 s1&[R".vTq- t3Eg@F|A+ Y˦/9\5zhCE\ ZH!!Ȁ΂:kw[n=frU<@\tA)$kİ0H=tَeq_#Hޙy\ԯ^E\ ZH!!IVcrNB߄ ]. Ꮭ9T [Ŏ* .e`(݋ɓFq 33ûV-kb9~JaQE,БB ^j=zӠrfꨩ,L,еKNT)l;x肖:ZHYo|8ƍKj:>4~HAgwV)l;x肖:ZHL{v L>LO&oxή1 EPYCE\ Zh!! _ N +r8q"L:47͡R".vTq- t0H1x0z*lj= 8(mPfH*IDAT!*-bG]в@5+(XMos 8Y=&yCwV)l;x肖:` kӺ; M/v 8OIV [z9ވ͡R".vTq- t,!alW_p҄ 0|8|%p 9N?wGEVQE,бAz_"dpVΊkπM/9F(r5&U*-bG]в@L'Qnls8C 8Jaf!KXp0 ^;/6V)l;x肖:֐ 7X_ϟ΃x({3}L79\;/wnŎ* .e5$ rV~.W!zˬ{.]ls䜜C4mekQJaQE,NBB2Bس'ʢt_}js_}R".vTq- 0HN2kW^ئ 8C/އV)l;x}{uY; \uE99Xm 룄 8m8K䞖K樋VT [Ŏ* .t.++cܹ0HN|[e}ƴGtcꨩ k^Kʽ ~E}vct!.vTq t8A_n*dž 'Q*}4&:Nω˵'y0y_~<q.hY7ô Xx1 .JС ` *+[X(y|3@p{ 7``ߞ#&p*UwUܱK:Xyyy~ؚ5kXx1= ݰ0Ms(-b1¼[v4Ms̙q_LLsD sRCifg[?}/o|9k\ ]aAHDjK:;;~*z)|>_H 7#Lχ뮋a9zvG'w|.Mkh$tʩR".vTq t8 uo?zH|#Yc9[n(a0~jcT#.vTq- t!ao} =|3TƏ/]{HoU'`l1;pٿ`?{q.hY$wDn=?uD[u7eNv=wbM﷏:TZ%.vTq- t<+ rHO93K`ǎF[U]gݎ?5k<7{Ƕ J+Ŏ* .eN$$ bcrYVR{߄jpcNv5J%^l8fIT9vIbG]в@'S[~0'MlMض fgcpdpǤ;6.IR/_MG*-bG]в@'׿keaNzAp*w9րs$?#g˟*"0F@wcT .vTq- t!aOiZ3뺺B4~<'.KG5(*g~ {u(28q.hY䦛7>ڧ#$#G…[$?QEX\E%r3ޑgcF\ Zd=qa\̄qRN<}[,'G7/QVL,jRFRaQE, {ri- q׿W_m-^կE/⻧~M{{ 9.F?8*ؾ~{BK&*? Zd=yGxQX*K4g>{KȹtZ);? ]+⢊.hY'womm jӧS曎?4G.y,m'qde_gKQ)QE]в@';$IVu|2A?uÚ&wu,\\2*Vø1luk}:RJ*.x肖:!a_;0߆%K`30i-}ft{ޗ0xOSY=̼r.ZD-jlL>FGU<@\tA!a_M9s=OvEf& ¼y?8c k^p˒[ݷEk<AGU<@\tA_!a_nIVmM:d^}*?)ϸ xwrINNbOs`a FU\Tq- t}6kJk+|;_/wI]]p+?cY?@+hl}ǔhf>'6_k/*? $ G:ֽ ^Ӝ;7 s昦am;~Gg9&aN}~ޔo5 Vg6mLZ'a0wmmfe͗Oytnth<./}%Nx *q ;^i3lau>NU\Tq- t*BP̚eEEVaǷmz}L"V޸I&2o B-i M߰ˠhΚsв5uGP)QE]в@*$ Up puX 5//;ۺWyqV1nXƅ^Ⱥ}s_y{Ҿa28O3[hlc54|K"⢊.hYSb#+, GN"kk㏷fvʋ\_.Ki㦱vQ*lq\D~RFǡ]vJ*.xh)!atu/fi k8aw=M#wtv2Ӹ0fGgˮGw+ cY*+k ?"Ԗ Z0[ش jj fδ6\"/^x n.kߎٗ/gʼ)T7E [^> Wʻ*Yw:v9cDŽJ*.x肖:]!a(0`b2Ffz?`-X:<ظѺWCȵ'_˚[p3`:T!~K4\5O$߮g)¾/ W)QEm0őTjkM_4Fviui8`Z= ^X'fSl}0\iUi~_X-V\olkq|AGBڢ:!aOEEn;vX1겾?X&\p=֋aݐvވsoK\\U\ٻQp1rixi~yL~cIO"^;u<4̪ 3W|'sM?? hjE*!!D8FHw qQ ϬMͳ0uG]]k_2ZfS]_Νw.[q3L𻆃 տQPJZ+[HU\Tq- j!a, ju5/nnm+=fb##FZ't d`_dqyu,~\g ۷~3e7ƪݫ2h{LbC~0Nџui?]VF⢊.iNKnF͛n(Ysx^VO}o{^M q|UW[>G5yym 7}iOcEkƗsdҾK{xA^QJ|\^- BHT-Z/#쩮7ް˖AG=tp`WBFsȑpsϥa&~z{Y\ĚJWQec/㊱W_aXnfIZq/촮)Ptq%_/k-$U\Tq $1R_ uq/~ox25]4E˃[_Ĝt5߼%[b1onΣ358.;sa dz2Ci+9!߮h#<.."|N7̇a B?ZmѲ@Ϙ1^x!pdgggcϬGy5.]2|zuIV>$8DɛVtR> 7Q>& egs[2%c3Wl~e=rC<*6{z.ywFUhH'pHNW^y%q"quuYac`vrCLCNf'lCC1ǝwpZF |]ǝU|Tmyb1x |ڶQYzlB7G,2$X9N~Nߑ .T!@Wl6Yϕ~<;r|`#'q1/BnR㽱ElPYF&B/bd18wpFʁnŽM[bx 2G-Yf[[SpIDH>O})**~*S… >}z5pC<ݻO.+jk;md!n7m5qY | [Kz?(/s[ɛYw1et ?<0nŵ,¤(]):Z%^_{JzUos=F[+ʹD  F ~_ӚAk^'3hAc΀F2pj'˟Onۑ"ޒOfG&dtdPy3$#_Zs2spփWs tvf3Mrma EXyMh4LLL1:jĶcJe ébTUU1|<] z ^}~Or1k #^x#x#ڏJG|P.>̰a7 .d۶mn2M'Of'?Fڊ3~\>W&,Ԫ@PVV >MMMl۶ǧc޼y3eee|i ,P\}|ͼvOK---ylڴnG}/ /ٳ:u*?яx'tR^{5~?cǎ%'''-?`?F6 [ǒ3G?a&LH0uT~_ow^% Ъ! )Ђ "Zdٲe{ܹkӌm9 hSvw˓O>[+RXvMAA---V-C?>yqwҭ$iv "#hAE-(RAE HAP)Ђ "ZAQ@ (hAE-(RAE gk>hIENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/kleinj.py0000644000175000017500000000017712253362407022532 0ustar georgeskgeorgesk# Klein J-function as function of the number-theoretic nome fp.cplot(lambda q: fp.kleinj(qbar=q), [-1,1], [-1,1], points=50000)sympy-0.7.4.1/doc/src/modules/mpmath/plots/hankel1_c.png0000644000175000017500000010644212253362407023241 0ustar georgeskgeorgeskPNG  IHDRhHsBIT|d pHYsL1J IDATx[-Q.EZ{ݘnsi yeK%dY,x0# f4$d4 F3#ۆn/_Z󐷈Zkmwů벪222*22feYfyIYfeYvfeYfeYfe=,geY^2,2KTfeYfy г2,/QzYf%*3@2,DeYfe=,geY^2,2KTfeYfy г2,/QzYf%*3@2,DeYfe=,geY^2,2KTfeYfyۿwkkqm|jGiYfydfۿ[Mooo /W;J2&3F|~/̌zYf;x׻ޅ-_E[%D3rS:"9 8?&0o(8Ke=>5yK[CNz[ E^ Tfc5X-霸 1S b o0 ]yꩧwgyO>$` G',i#=q3^D7שPgIXcSh"C[ oW`9a_$6K3p9߼;שsf~/_co;̕Y}\r逌-t~ӵRr>YAj.W<j{"9Y_>vY,v O[T>W ܺ?-OD3\ ˄NM z:AԹ$n֘|OKއW'U kM,Bf>P(b,2Q Q]9rHFmTAz}{0#6AޡQiNiы{d>ٕJWR{R{qE;avU8r۷0I=^ZWMgԏV-(Ae^,%Wd=d<ۮCief#)Sv_9#^=tnűĹ j_+n\@@I.ГkOx־68g/ oP# N"wxwॼ Y ]Oi Yf̽NJFJwJ&jɹ{zҁfv noza&I kA*DY}k$t?J%\նo$c:E=ہf8j{0ݝ/WT+['^C/tZ9ݣ$[w'8{M*}}%o3띒4nݻ N_!:{];LA5G\vHut:(/޾sm"pCM"^o~{+3@(4`*9og;E.#g T{IYDTtua&ņetO{.D~eϮϞ{y@D^h转(mިL82tʞrBd?fe/+QSԥwWd,lLfx2G ȹ&ɢx %!)$ToW5D7ǒi#`0缰[@D;DwI#\踥X|9@mYm^g`ne ĶXk{#je)`&OOgs"ISz I䛏E~plu\ wyj9lt5Nůs,*qj  2kh2Q9б)/}L$kyxyy+#iE.pvv^JM#6DM?O!0~Ăsz{ߩ'^څ"HoM/?vX?3AG3ny/c'~䤰SWh%?.y6 K)H;H\[!=ĵ!^U&>/<ꡅ#>ܿzq(сJ{65T^Ózn6i&|[>yDPwOa7:7#C{˝h8Iq %z;d#B3w®bL\I'?ZO:!;ڛ,I=n'kZ=vUG;ڜn:tP)=0SI {.|mz$^J4tG)&Ú]A9OLfrl6ncɼ+xUۃɩstFntzI{AC8AUBS%~R 'U^!'OA^v ڻ5K^uCUp&(kL;}&)m;_%O;;O7"7H֛:M&=/ޑh sͫI7QC΁`#t#hw{ϗM_f23<㸸<#xuk<",k-{dGD~W'^U( @NA{bؽ'^0{ ȟS;/`0-STlN&/K#;S y[ߊw'>foo pqY1a J"߅NhNھQH^v y0Q'h/= 컔A߼dyl:~j&; c⢧7^kN[/3ef'dRQӻqI愵8ɳĄ^ɚ  ;-{iPO8>ECM 3 U0 T #z6}eBQT=9^6j>U;Gݙ6$mT)1 ЇJLΗaOTIsΛTK} {DXʨk {"9`7x]үU{pL'{uq>P+:;1O[ve|/\Vm%S$vɀIQLz9= G75-6I&TcVh>E?0aueZn{Oym̫gs!e:nw`&Yu%nNK* %6YNƷ߼ wK;i/q/)v+_[3&z}{I"""v˼S۲Lފ,I5TVf>P(Y~Ld&@L@_ )dJB֚~R R䠫 F.\P*|2T~6N~@Fh/^32} rpYMHSOԭL\CO&JV3=RSY`' [qW63\:q$,۵ANj܆mH&sI8KClJN@r%*(4JIl9yO%Fvj.Ӻg{ > "`~K ^h#>&GJU&=$~*0C$m5π,et6(j,7wk]]̈8ƒW5z¼4M8ZywTж56Lbk>v! AS)3{$nAѲ[:0j; x9kPϛ6Wk&goӤu*^k6D4RJ'!9eEvz@bg+.ukFYY6bc>C$|re2Xjz C]?n;%9Ge>%9qEl~JzуD4gUfxl,˩r~W~!*!d$#GbZf!QzMNHgAd1KO/hb5FLKSyf*жWmajc:V*dרL!U` : #8(чfڎ^i 9//d@Q༇Yp<3Vz&+o@(VC;G/:yjbM-}ȹ8rrU'֕28*NE$=Ir)=׫}=N/+EtC(șle`prH\j2NN@pR6hy{3(qk_Eo!e O:V(`!tpurq@ ] =ҕ$ ΚT_ AAO=̰҄Q֡sjXIp\0orqKf`wt p( [訥IN-0K5;F; %8vj'iF[6qSk DsăZEMMA/LXg(!sjTrp2YLI48p3N3YrAvZ;twS+isBr 9/u, %ATei }"NhG js: [ͬ0(-x"Y"<`K^y|@nPn΃n)wF蜛ڻ IDATL$R؉Lst}¤[:#eL2pYT_ed@!vAj `Se`]- hu>GTPڠ=?[TI*Y'LPMNXM=A; mUSM7Jg%8 Hj6gݡlt0+\b6@ɜ% $ O7@ WaBJY t J+A\ȃ!eU"ɽ&8ۡ&@ "t`h,JX8%&"']wm'Tde3\٬}m^]ff/ ¶tuE<܇s: !Ӷ.HjģHI7bn0m37޶q4jk‡)u̲S-79١n0Pksi9Ot ̍\zϵf?,Uf>PT8$AhL@ d~AF2jfHʥb^blu+IתY`U,S[9DFDׁ]9Jځ-+Lvhtmĥ8gi[ }a2wX%!3] I˃ɻ,WFkLhj1 Ї ']¦r7DZQàxe u-F¦aHLA+n'Cp=Zmm^[ԭ:I%Fʢ[*)8_; 0j mC)r_@Չ^9)iS @7{r.5rP,#\zB3P{[}k+ohAk g6[_ 0q8{& :[Bp-5s?N1v$4\Ь_5ߟ PՁ]wʞY} :)M@6epjVL9O[mA _(\{Azll54׶^r[h<G=/6&4\tɛMA63!E_9d78LVH~{QZsխ%}%d\2M輴V ^j,|Y YKVm=_rzU 2mkTji7~ԠZbZ,ɩ";^YS`I4CT6Ϣ"[c]Ro ݳCr'a)GSh!S4]ʺaѤ:2h{t$5'$/S7,X/I>N=Gk.vvQVh Ȗ1MZ`f z|iĦ۸t]E+ ;;yɝԌ}Cz,Yf>P2 F-p H:Us$~3]jR$|/; sqM ]*~9nT0H^?2 wAj-jMcDh;O4@ @y8BAyݶ }Mf9cT'K Uf>P\Wf4 Bk;tzB+@m Ќk )F Fp.8!hca2Z8M8݈47q@dy=gSxG=smɊQoUvMG%.^B,Od@Ism S]pY5YYa M.[V'6@V)iسq"ǢȦWf\Ca 7e#fA:f8ssf˞9}@yfbnp`>Y+)M^&z&;S2DN 'n3YcK]7};@}QH1Ҫr2@;!ŠV"P,@;WE4l 5aRn-G>?%82y,qT  Pt8hJl' Lmm6 lP;yiR;Tm\@!@]@ w![wS0=6PMi<Tf*3@wo~37z Ѐ2q*xLB~94@7zq#t20 5 d Lm'sUYt& &z$<- HF:fɠ4U&@='-7sU :UHPv\븰h|`_,m ᶑkس8 gx2}&M -k,6􆃉\t@1q^5KY:aXD'Rsw[gݱ j`";+6]p zҋ#5[,l@ycS+tԥycoEXM`vn=&i P)fЪUhГג| vVfd(TXzB~7GGGٟrr x~_԰g$,djjoSmP,ˠBTpP=94@wmv4O"yv*gTzM€f$k>ܗ2T,BMF֊EadU$Xsb&'z2Ȧ3h DK,8CiG 3qdP%IVp n_znxbT|l:#gDN ̑@`6E3xKҝG 8vz 8XrgǴAsQРYdž9g@f*Ɓ(B]ǥ(V2O`ʭ__^#4HuE=sa ;e ^p>UV6}=9$mo"Ԃs|, V),:"Ǧc)8$;4Aؤ:jrSd'ai!c\3` լ @g9Y1dl@aξ Z&Fe3R[ktw'R,(-EtÐjcXg2BVN%-;U>487=hYtPJ|dН-9$ zQ*0͹HzK$&*&`u ^ %n#.& d.ra1U3W|i,F")`nP) )"j(TAk^mCM&ф{"'gq@T(@N}\As'aJg{SFp"fd5@:Ul2)8q h 4 8KMմ!ŅE tyWtě*YHBݐi-VA*DI[q,3@(T7 0J x4%V4NB'b Rp֙E@NgBAr@z֤;kGd90 ZDĠsǠd̫rXX!6CraNla*Cjv|)<6"Z3x!Z!/tiœC  GaԷC[2B)UxSG-b ̢$k@@DI:meȍyC`낢z%y%@:%Fli,H-82rd2Ȃ'LAϚ5*`'-Q1X|ɫb]-TSX"D'2-ԢVKٛ z*3@(qHA\~} mCiYuVXp҂3f6o ͂E ӆǢ&h3\p0E`^!ɰi "livhQt20k1Mr;̨7 (Ah3Dsaϔpia{ Tť^M9B=N۶cvA5HWe(jsJ`}"(X) Z T,{J"9D-M?C[S\,8ǦFF)hILp+TGhc[85u4#1{fXs4hʚYug9(4>{c{AHglp|A4a^keәZ!ڎc)]3neCC:IXKCCLȳQ(],vDIW:5D?#yuX@ߤ:iɦWL6S3HbވL+@oa[m͊9 >A68%rmcVk.ءGCjSʈ 0 @HRxe0L=sd zF@ɠ3(eC: 5A5 jJ aHiweâ9hkP @5ws㊀#xNX`-hQg!o58'1 >8mGȿ3؂X2e ̎i#@ (7 1re:&1/ fdBDk]6 -y|A>sAS$&akiPŐyȽD Z.A}FvvdrJk`84)OWTGD8&"D&a-X6%%tA3q1dcfNYѼQLykL= h0|̋ZߖvKZcp9[\$&p%T7rYl6 y$z Uf>PvfˌLZ]*(V*4Us؏AY3sXv3n}pޒjĢ З\ 8&\t5Z0l0p5kưq15!_gq̱spP9 ǜ-mAݰŠAsG؟e ;STkj7Tqu&Hh3G05a̝R^}yy?3ܹsm߆xk_eKXYcqA-l* ,Ra; X[۳ey<&@d-["CY5U}p7l @iaLiX@3b8E cfpcVU[5k߳)$a9Xy@"A؟;.`K;.tqZe5:Z-!I^tW2hSgxV[}1>O]u*lc@]F]-%I%Hޤk)=Se[O Z ]d[#t6 `_Ȧ/ppm8%(\ce/Gbp1FS84vG<3lG`lNOҐ@mʼ!t$}h@]3ͧj΅{Zp]v2 `ȅc z5”Y^;;8?? =wᖹ8,vl#~kkX/R ;lV4+}&`v$D4n}Ref N`CMl H Wt.a JH[_ `-a5q [1 SPd>a #HeLA|*5/ P^RIݙW .HK&^ڜ=_#mYz,Z-|ݷW -B>oCBې4--.+0 P6, hb0 "yKXPoS:`h@Cڧs,0`!@HFc=a[2$N 1-7p%ŊaE8ud  ꜰK8x7up\ÀSP `#l1Ј@#龸 CaEګsBXPaס(x. ]e#RN -dEvR[?<7P\̡l48>sW,Le :h{ ͤ T%U99deVsYI{EgmΚ;^ڟl'PrX!(#.tNL[\ `Ȝw-xp#퀛 -p [\#58Ȟ7`yܶeQ!K8̹7.uyMtVىc-݂t':_y]'*˚AÀx}+fDVE!/a(@i_JadK<3|i*= 9ǥ_pf4U%cj)[ʌ)˭\ cN y yHL9lP'c8 kp-ׇȜoQ x% x ⛾;?# ~G??~Oe<:w\PQU&uqE(gfq IDAT.94o]q6NF]Npʌ1 )SicLk0֎ycSTM̱S8m<80(.q%pȤq J@:bS!v!zy(/[ɟI}??`Gܼy??8>9Mog?Y<x'XnҶWָz<x#oq'`9as l^ fm/@۠ 4H:Cڇy?"nhh .p b8pypEi@\H֤%=‘r-r)F0[cl\džobC_gpO2>/^[`|M^=f^ cB8V@XV*-HGZDPmAH(u:@ϯL9q0^yρ81qqx ['lx4 wǛxˤ5%4l<'#p  u2qh0o,aԣ; K1z0.}{9f@,[ZqQ"?p˓+>6fexzy<4K^|e [[G? ~ɟϾo;_n޼'xk`[v7ڦm`=b}u; ob~@_`Ћpct x ┘[subzQ|qgwĈ lq-cslQ0/q.z5 \> Q>X^ k!N90up#2D_t<%pB+xkx׽@8?~ukx)A:+V<"5h*6uSlY5 k6cЃTAXU# +' !׷'-oס_x _.O`(/k'>??gOOOJJx<cXxY |W ؼ+Y\n_xЫCcto8 ZqKUtߚR\G$ObO**T1"To0%OfdlqW#Vـ?>>=0x|Fj > ͨA}jJfsԇKt@f+q8#`-(>ה( /9^(SW9G#Fq*=oK'WX}>5w4,MӗB#@ P@k@ڑck&N^7vhwg-Vh H/-:t*7}ufyoN¤2H=.'| t$rgE[oW.-7`O1^G y }͊ 2j¨ nmҧjZ%q׿W< 7ǀa \D'@Z {r'ןQqCd镦h{9-:.fjx7!# G/a%abЩBFڈҞA ,=si 66zZ%HJ{c&à%P|_PJy–Ag> 3{G[`q^ :Qe.zH\YG@Y#rhO؞+/ǭxa,lc t0'78})' JAc빱`uC+QɷY*Wf̺֛*,@ﱒV4P:I/]fe]8fhָ[ za~ 1ph%XNp'4A@UM:}e;vf(L94g38 |3`A z^ `x80n/nb@pxSN@X%NldcՆpd٦Jd po Z_< _?3az ,MRaUqm)قCW5V97;p\☔amqD[;Am:/& *jlL:eE17\(dunlpo g K26yl/KlqL6 7l4d@ qi-I>E*LR)Bϴ!4/W2v4!rdvxs G ihOf>TFPA<tNBZ::!cPr!MLD"+NgIL@ +t1@Fׁ:~Su.%.;D26u.C5J*fv7s_-l^v`\2[xn?sb As8Et3Z0ζ} MZT ò;We%ׯb,h>DT|cӍ5-HKh Q싴vhnAˢڡ1qT7;̒dCe'! N_kƊrA"SiB (_[5w@MG :fx )#c;D$1{ ؆(DEyHHfꀉNƀܱYy;-urgDs^dwހa ܦ[pDa!X0zv|2d Lٔ;%#1K.Y7HxHv92{ȧ wBbcrJJ:X,\:]R1#tAz*W# BzR鑖A! BuQm$ʍ-M;68G;q{O| Sߗ'隀4Y>pA\9c@uW_6ssTAfn%pϣ "91{y]"\n2MOZ>aE5"DV2 pse~ @ F%ݜCP B7.dgP66 U*U0jcߒ#Ү8[q9U% Xtgs' P)ـp4ջd@<%oi9I4L}m1MW1^Lc"b# Le. 9 8Kxd3N@?g kHjGZn" Y6$P'r6#ScGMX*v`@Lj=akrKCZB\Oh'V\lTl\뭰/#qdyndJjXEE3RuFEX@s/1Pu4tbqMږce$eQ!+F3$QmСɼ1 Vi}gqK&eH`,d'R$!FC.X{Rd[:&p$N6 0}IPcJ.EHM@4*pJ5Jœ@1sXhs|R(}2C֔-Mo*E9D\K_kN,*PƵl#xKCl i9l~6*@3uk;H൩/NC g亡y-Fc&I˚ܘXXpQ?q@!& Lb/kA"b e;8# y[F=YTT2AFԮ`!?C2J1yE~q~ʒ3c=^ncA~Khj5xb%Уf1{0T(F5a& n(; 9:قNi6Kٸ,"$d!}.80K*.9U皬$؂4%(/׋qSV\l0֭+&+|ڸDHbӑϏ3 rl9S)feֺA,B CkB HT>v 3|$1_:4sI;΀кBnuMuh-:9I=s5 N$P@"dlN+9O%`DKCa+\ ܒ(m(?^|g:ZÓbҺΐ@]\#㔚32Q h9Im@8.餓׃~$J2"U {mY۳jɠs##` ͪb;}km@:RYfz0?8kGM!sRl@ OIrHu8GLs SUþ.PSQـޏKpY]()kq-*G-ΒiŶY @OHMŤVhkuCbE^ `SzMZ"er+>ܪ@1UPz&ȸ#QFl0…Dw) ʥm;;#6\PMQ7ik@TUvtLVzq9)fh@ɋA+.rIpx"Б @OHXQC aTLX3l4͒s -^AˆR0+LAPS9Lwit!T45AIޓy7d :#Tk#v[= qn){p3ģ)$)6'-haIF-řa+:9 s.R0k\uEy`"GJdI!yC% yb5:X?fEF>wB YГShT lG̉qbW2ծk1c*[0'AiI;`ƖH{@̀Y^OYܠiw8#@ [_ab HoQ /;j9*&>ioe{O gГRk}elT{ein@ڶ@. 6X؃mە;ųg9>R<@fƜQ ߆w4"P$4;$PR7 f2 dWEΙ rQ )f.Կ~ܱ҆c|@`{J6#\  \}Q>pSDt')#y]%)*=! %*(opb(0Gfn-s>MvgYL닮Eg?6>@Hf]C=vF5Ψd6`HD$CZ[{{uܾP hA'r6'^WSM3A#; cɜo!Z^k3cJAYs;8I=D$+fyg8"KcNR;K [m"-N܁B>Z tRPܨmkZITt`m\'1߻;DYr0{K H9sN="nR{#~Лr X?[ 55 X l*-Ԝ(Ygw @OH@w)‚j NI1pflW϶=hOxO0N}7.EZAYdTA°V%AD2N0?5=)n/D[ r#MbAwTzBj @iy'(LCuZ`yU kTbJy{ed,~ 7*&8Ћ3TĔL7 zȊm(u%т dt]v-) U.T5XSSP[HQ )P4\πs7}$87.v>@Kw! PO\ tz feD[u 1-H|ϱւ}@$u[vx:YTzB|ЦF !Ϳ\; ܶoZJ4,OPR86Q)(U<8;h9h@5^'AOSKf4 ECh,Yhפ\E[hTzB ]Ih7AӂK'Dwe͜ߕoG!eK!Z$82-)yVa#bA}Ƃ@8غ6r=͵V- [}w{:GRĚ/RGG'd˫P=#! Vzeujat<CHkL%`>=w{sT:NlAyloCG Hn řLTl"BPZrvHpHĒ8;B bcvN1v0A9VdVH`*<=\NSSa/+GWXR`kg2 ҂4+asx!նng˖eLZ95'Q*I_wS -g!ڛVAVVts s]҄Q @gnc=͛7wi$l:ff@؂Zϖ1d֔+wsh| mg }`m)Dž*L^O } ^^"j87T'5d{ 5:絬SQߖ3=%-C> N9T|bi3t%uѭcC9|{˽SIB rTuqVFK1D]ڷr)a^'Mt/ٜ2o="SLBϿ"}ؽo/{hA҂hF+Wʕ+3338G-ig}xa?qJlfcM[f9w9 ˬZSƤ\7,u8{zHe9 A t Ȫ€^oO8(pf+ɭntvh0ֳ=y}iKOiK}hRuv%@0hɉjf{tqxНs@H/n+'!#*r%` cWy{n~AA40'$.AKvm ń>dbA7c~<mIH @OHs>,Nf! /M2".Gr*p5D Q,2Bu\r=ag!nZ)0;TFltqu JFA_[2ڃqJk!RQ\J;v d9@V}?`hKm%VU[ع 8%EϠ=Hh-pwTQ\i~#*\V;p rea)Ø)Y6̲JUGUߤɭ7c=G9Zɡ~*fDtj&vm{iv]yбi!L^t+H?i}}ҒREdž8r:EN݌_,?G>NTGN+!7'=ad1Astkul]E[hTzThڬiueº!Bۂ':d_]n5a@hٸ8W ̆IUM LglЩ3S/0\ Zvw@YUXrl 9Z[,s| L-XTTt!sQ */+_)Wj?t+Ŵ;yB*31]5Q \Шb5d毮`ZKtk:Jy$!6Ns+b9O7H)ÀرA@u4LWLj%%ig)",:%vpXBR^͔f_b8!Rtؘ۾~\ap֥Ͳ/1}h%\MiȬldm|tmXAvD6.`8cp B]TzTZH4 2HisZm9J1ɟ1G19,OpBGI8h^gjd- F !XڅA]sMNs׏bu^(R )͎x$pxOiDr-*+쓛4M͜զP"k%dʑ"ӎD3 ɉ]#2Ҙ;QAM5s%bYp K4 @ρ.HlQݿZ(82uHZ\~lAya:W` n%ѼL^PksӰgqب}N޲&M6:#+OYTzBұ8@Qd<&p~c[gƼYn"+"-(0߹ 1,{dH^_x_1l;qTL|^~X ο&GP\*9֒ @OHӏHBL5DWjjT?vrgRг4)92jr~q Z0\] %TOmwU37:P'-=a6!12z!NG@m 4Tn~B ,1ݝ92inCd(S c$=4\|xJgK[(..*}dOܞ֌Q,NgqjTR.X*=!54(̧p  T~@C4cGn1^: Qwl㗊<+12a,l} Ri%-],/gޕf 4 T68QW͂s. 2A*l@؂3(m6{==~-1c.ߊA/ˊpuRغzc_3sNg4.n+  N%ڿUV9Sil*sݓSPhO`ѢEdm!W4bAV18l؞4|ym8%N])3Ou{n#؍-6#〒n|h˫,X'+vcn.Єv؁73DUUXxqw/4aM,\#i棓Uf$M^G38/oYf 817 Wr6'gd\L1s[='˾D|ct"I)D Rضm֭[:,qX~=6mڄM6a͚5Ͷrz54H4rO<$zcLYp?$]28]qk,XSv,#@۩#)jUC:j{wܛZhzŊؼyJ3<Xd }7w?o`?ppq5=3t)jZ *XfT $𧂷}49XaF2xs&|܅C; =X@s{e ~$n-Pɔgw敿W^ŕi4N8ԧp饗bΝ뮻{>~4W.EsCW+-nZ-y"{ kEc)䁎F%-AK-LX,yI1捉K3Pքl4l/f$qXVL˵-weH+>O5BG @;  Vcf kG'ǎb1Le~De1yS.қU23ɔWjJ2sCDyYZh@- yyvܦs}0Sݤ;gZ@PzNT[;A3ѶWd[c9yaHvMz$m'GA+ufy c, ƀtԷTFߜ(e֝ϱaNgXhTzB q2 AP`$󔛹1 Ze?pσLFXg3`@eT{rK sޥmUr`Ys Mdٹ9jɴPzr ,I|zh;<\8\6P,SH1mu=-0̲0d_OȣM'!1dL@A8HRΒ @OH!c,j;,S"#8fsrQsQRXFd5͎b_YD߹D_Vcs?y`:L,NK}%`O'H\ ' ױLcKHKY6n%DXKq0B!#g"x޶S.WdO>SEҥGNgyn b.*=r) a4iAx hNwA3 =-fx]B,·{LRwh1N4̲ bV^e\(='M?NƏȨ70i ̂vl8g @9?'`<9L)ԾsLsC~[8)ZJY!X B]Rcڰ4e=G}`>U5Y`k8! ur3VAu$A\r8ʒv!wmLi Bh $ZK]7-Zp.гwiBjWE9̷ZjL%Goi4dz!; IDAT(;C @Msf5-E25skq jZI t<>R䯦bU'[9gVݽ&]-Ƃշ0B0U|Ϧ^1DnݠPɢT=t3IUS|aH'p YGiS֊dI 2?4`poCޣk9&|xh:Cb{%N;ND}:]|4y, @OH*v,)=$UUܖZB0ʜσ`;Clv>7 ̏ -x tH03+tUј{Ls 4R]{fx""JJP4E?M&{!0Ff=E7Q<4<28rE6ΨY_g9yE76Gm!d3&:*`܆aQI4{ VHydD2*{瑁π: -hTNGa+ 81r $JٖAPy @OJ*5m@]w\i8b0<Fd3P9TsEez@!t`gɤrc*s2ݘe9U75s ,c*rڄa{EBɿ+ֱ=\a?<` Tܤ"g nu*|:m^}Ngrl3|aL$Ccfve;3y ij3wtў;E%FjVâEpG裏WYv5< k.ڵ _ׇ @ڳg֮]?/nf|ӟ*Ts//+Xlټ񚞞OcϞ=ŋc~?կ~O 70o`׾>\sͼ_=^u,[ 7tӼzWpÛo{x} NHCv؁7(ŋ<8S,[ ذa.]{׿ulذ:fgg:H_xGO71|~`֭±;|Is9;o߾ݱihă묳GcӦMشi֬Y3/VZz ~;#kXj~i޽gqA_98K/N8|$MOO /W>(.]SO=;wW^333>kwm6Xb^x}Wu]u*+V$[l-[n`󚝝3<Xd }u}aݸ+sy7/ƽދ 6o/_۷_"oߎ˗ H~;?Rr`Ϟ=;vࢋ.' {o;w⮻w4ˀ //| Xr%ફI'tPyٳ\r 9|Ã_s=SSS;WG}qW{K.'> ߿?_Wr-X|9^}UG9o>,TPBQ* *tRB :Dt-=#җ. ]tz䙗_~r ^wyg,ThTЇB ~z~ӟ. W]unffܹ7x#:ػw/?|׿KQO }hkŚ5k;ҥKqQGa֭xw3N%Kp뭷b߾}ظq#b*R]CK!\pkvZڵkO~{>O~|3WGÅa*t [} W\qj\yc9{Ŗ-[a⋸;gg?[f:t̃.oM=v؁իW'n&]'x YBT,BtO.΅i*tB T,B :DtB TPBQ*]PB(.TPC @*T!J *TЅ *tRB :D2¥!IENDB`sympy-0.7.4.1/doc/src/modules/mpmath/plots/lommels1.py0000644000175000017500000000034412253362407023003 0ustar georgeskgeorgesk# Lommel function s_(u,v)(x) on the real line for a few different u,v f1 = lambda x: lommels1(-1,2.5,x) f2 = lambda x: lommels1(0,0.5,x) f3 = lambda x: lommels1(0,6,x) f4 = lambda x: lommels1(0.5,3,x) plot([f1,f2,f3,f4], [0,20])sympy-0.7.4.1/doc/src/modules/mpmath/plots/spherharm44.py0000644000175000017500000000052012253362407023407 0ustar georgeskgeorgesk# Real part of spherical harmonic Y_(4,4)(theta,phi) def Y(l,m): def g(theta,phi): R = abs(fp.re(fp.spherharm(l,m,theta,phi))) x = R*fp.cos(phi)*fp.sin(theta) y = R*fp.sin(phi)*fp.sin(theta) z = R*fp.cos(theta) return [x,y,z] return g fp.splot(Y(4,4), [0,fp.pi], [0,2*fp.pi], points=300)sympy-0.7.4.1/doc/src/modules/mpmath/plots/hi_c.py0000644000175000017500000000013212253362407022147 0ustar georgeskgeorgesk# Scorer function Hi(z) in the complex plane cplot(scorerhi, [-8,8], [-8,8], points=50000)sympy-0.7.4.1/doc/src/modules/mpmath/plots/chebyu.py0000644000175000017500000000033512253362407022531 0ustar georgeskgeorgesk# Chebyshev polynomials U_n(x) on [-1,1] for n=0,1,2,3,4 f0 = lambda x: chebyu(0,x) f1 = lambda x: chebyu(1,x) f2 = lambda x: chebyu(2,x) f3 = lambda x: chebyu(3,x) f4 = lambda x: chebyu(4,x) plot([f0,f1,f2,f3,f4],[-1,1])sympy-0.7.4.1/doc/src/modules/mpmath/plots/lommels2.py0000644000175000017500000000036012253362407023002 0ustar georgeskgeorgesk# Lommel function S_(u,v)(x) on the real line for a few different u,v f1 = lambda x: lommels2(-1,2.5,x) f2 = lambda x: lommels2(1.5,2,x) f3 = lambda x: lommels2(2.5,1,x) f4 = lambda x: lommels2(3.5,-0.5,x) plot([f1,f2,f3,f4], [0,8], [-8,8])sympy-0.7.4.1/doc/src/modules/mpmath/plots/hi.py0000644000175000017500000000014612253362407021652 0ustar georgeskgeorgesk# Scorer function Hi(x) and Hi'(x) on the real line plot([scorerhi, diffun(scorerhi)], [-10,2], [0,2])sympy-0.7.4.1/doc/src/modules/mpmath/plots/lambertw_c.py0000644000175000017500000000013712253362407023371 0ustar georgeskgeorgesk# Principal branch of the Lambert W function W(z) cplot(lambertw, [-1,1], [-1,1], points=50000)sympy-0.7.4.1/doc/src/modules/mpmath/matrices.rst0000644000175000017500000002223612253362407022104 0ustar georgeskgeorgeskMatrices ======== 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 sympy-0.7.4.1/doc/src/modules/mpmath/basics.rst0000644000175000017500000002116712253362407021543 0ustar georgeskgeorgeskBasic 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' sympy-0.7.4.1/doc/src/modules/mpmath/general.rst0000644000175000017500000001033512253362407021707 0ustar georgeskgeorgeskUtility 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 sympy-0.7.4.1/doc/src/modules/evalf.rst0000644000175000017500000003256512253362407020112 0ustar georgeskgeorgesk.. _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) 3.1415926535... >>> complex(pi+E*I) (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) 4.34665576869e+208 >>> float(b) 4.34665576869e+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 sympy-0.7.4.1/doc/src/modules/crypto.rst0000644000175000017500000000303612253362407020324 0ustar georgeskgeorgeskBasic Cryptography Module ========================= Included in this module are both block ciphers and stream ciphers. * Shift cipher * Affine cipher * Bifid ciphers * Vigenere's cipher * substitution ciphers * Hill's cipher * RSA * Kid RSA * linear feedback shift registers (a stream cipher) * ElGamal encryption .. module:: sympy.crypto.crypto .. autofunction:: alphabet_of_cipher .. autofunction:: cycle_list .. autofunction:: encipher_shift .. autofunction:: encipher_affine .. autofunction:: encipher_substitution .. autofunction:: encipher_vigenere .. autofunction:: decipher_vigenere .. autofunction:: encipher_hill .. autofunction:: decipher_hill .. autofunction:: encipher_bifid5 .. autofunction:: decipher_bifid5 .. autofunction:: bifid5_square .. autofunction:: encipher_bifid6 .. autofunction:: decipher_bifid6 .. autofunction:: bifid6_square .. autofunction:: encipher_bifid7 .. autofunction:: bifid7_square .. autofunction:: rsa_public_key .. autofunction:: rsa_private_key .. autofunction:: encipher_rsa .. autofunction:: decipher_rsa .. autofunction:: kid_rsa_public_key .. autofunction:: kid_rsa_private_key .. autofunction:: encipher_kid_rsa .. autofunction:: decipher_kid_rsa .. autofunction:: encode_morse .. autofunction:: decode_morse .. autofunction:: lfsr_sequence .. autofunction:: lfsr_autocorrelation .. autofunction:: lfsr_connection_polynomial .. autofunction:: elgamal_public_key .. autofunction:: elgamal_private_key .. autofunction:: encipher_elgamal .. autofunction:: decipher_elgamal sympy-0.7.4.1/doc/src/modules/logic.rst0000644000175000017500000000605212253362407020102 0ustar georgeskgeorgeskLogic 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]) The logic module also includes the following functions to derive boolean expressions from their truth tables- .. autofunction:: sympy.logic.boolalg.SOPform .. autofunction:: sympy.logic.boolalg.POSform Boolean functions ----------------------- .. autoclass:: sympy.logic.boolalg.BooleanTrue .. autoclass:: sympy.logic.boolalg.BooleanFalse .. 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 The following functions can be used to handle Conjunctive and Disjunctive Normal forms- .. autofunction:: sympy.logic.boolalg.to_cnf .. autofunction:: sympy.logic.boolalg.to_dnf .. autofunction:: sympy.logic.boolalg.is_cnf .. autofunction:: sympy.logic.boolalg.is_dnf Simplification and equivalence-testing -------------------------------------- .. autofunction:: sympy.logic.boolalg.simplify_logic SymPy's simplify() function can also be used to simplify logic expressions to their simplest forms. .. autofunction:: sympy.logic.boolalg.bool_map 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 sympy-0.7.4.1/doc/src/modules/parsing.rst0000644000175000017500000000420612253362407020447 0ustar georgeskgeorgeskParsing input ============= Parsing Functions Reference --------------------------- .. autofunction:: sympy.parsing.sympy_parser.parse_expr .. autofunction:: sympy.parsing.sympy_parser.stringify_expr .. autofunction:: sympy.parsing.sympy_parser.eval_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 Parsing Transformations Reference --------------------------------- A transformation is a function that accepts the arguments ``tokens, local_dict, global_dict`` and returns a list of transformed tokens. They can be used by passing a list of functions to :py:func:`parse_expr` and are applied in the order given. .. autodata:: sympy.parsing.sympy_parser.standard_transformations .. autofunction:: sympy.parsing.sympy_parser.split_symbols .. autofunction:: sympy.parsing.sympy_parser.split_symbols_custom .. autofunction:: sympy.parsing.sympy_parser.implicit_multiplication .. autofunction:: sympy.parsing.sympy_parser.implicit_application .. autofunction:: sympy.parsing.sympy_parser.function_exponentiation .. autofunction:: sympy.parsing.sympy_parser.implicit_multiplication_application .. autofunction:: sympy.parsing.sympy_parser.rationalize .. autofunction:: sympy.parsing.sympy_parser.convert_xor These are included in :data:``sympy.parsing.sympy_parser.standard_transformations`` and generally don't need to be manually added by the user. .. autofunction:: sympy.parsing.sympy_parser.factorial_notation .. autofunction:: sympy.parsing.sympy_parser.auto_symbol .. autofunction:: sympy.parsing.sympy_parser.auto_number sympy-0.7.4.1/doc/src/modules/statistics.rst0000644000175000017500000000723112253362407021177 0ustar georgeskgeorgeskStatistics ========== .. module:: sympy.statistics .. note :: This module has been deprecated. See the :mod:`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: sympy-0.7.4.1/doc/src/modules/geometry.rst0000644000175000017500000001623512253362407020644 0ustar georgeskgeorgeskGeometry 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 for 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: * ``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' Hexagon Theorem -------------------------------------------- From Wikipedia ([WikiPappus]_): Given one set of collinear points `A`, `B`, `C`, and another set of collinear points `a`, `b`, `c`, then the intersection points `X`, `Y`, `Z` of line pairs `Ab` and `aB`, `Ac` and `aC`, `Bc` and `bC` are collinear. :: >>> 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 References ~~~~~~~~~~ .. [WikiPappus] "Pappus's Hexagon Theorem" Wikipedia, the Free Encyclopedia. Web. 26 Apr. 2013. 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 = 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: sympy-0.7.4.1/doc/src/modules/polys/0000755000175000017500000000000012253362407017416 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/modules/polys/literature.rst0000644000175000017500000000774712253362407022347 0ustar georgeskgeorgesk.. _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 .. [Monagan00] M. Monagan and A. Wittkopf, On the Design and Implementation of Brown’s Algorithm over the Integers and Number Fields, Proceedings of ISSAC 2000, pp. 225-233, ACM, 2000. .. [Brown71] W.S. Brown, On Euclid's Algorithm and the Computation of Polynomial Greatest Common Divisors, J. ACM 18, 4, pp. 478-504, 1971. .. [Hoeij04] M. van Hoeij and M. Monagan, Algorithms for polynomial GCD computation over algebraic function fields, Proceedings of ISSAC 2004, pp. 297-304, ACM, 2004. .. [Wang81] P.S. Wang, A p-adic algorithm for univariate partial fractions, Proceedings of SYMSAC 1981, pp. 212-217, ACM, 1981. .. [Hoeij02] M. van Hoeij and M. Monagan, A modular GCD algorithm over number fields presented with multiple extensions, Proceedings of ISSAC 2002, pp. 109-116, ACM, 2002 sympy-0.7.4.1/doc/src/modules/polys/internals.rst0000644000175000017500000003776512253362407022171 0ustar georgeskgeorgesk.. _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:: sympy.polys.domains.domain.Domain :members: .. autoclass:: sympy.polys.domains.field.Field :members: .. autoclass:: sympy.polys.domains.ring.Ring :members: .. autoclass:: sympy.polys.domains.simpledomain.SimpleDomain :members: .. autoclass:: sympy.polys.domains.compositedomain.CompositeDomain :members: Concrete Domains **************** .. autoclass:: FiniteField :members: .. autoclass:: IntegerRing :members: .. autoclass:: PolynomialRing :members: .. autoclass:: RationalField :members: .. autoclass:: AlgebraicField :members: .. autoclass:: FractionField :members: .. autoclass:: RealField :members: .. autoclass:: ExpressionDomain :members: Implementation Domains ********************** .. autoclass:: PythonFiniteField .. autoclass:: GMPYFiniteField .. autoclass:: PythonIntegerRing .. autoclass:: GMPYIntegerRing .. autoclass:: PythonRationalField .. autoclass:: GMPYRationalField 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_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. .. TODO: write documentation for new sparse polynomials 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:: groebner .. autofunction:: spoly .. autofunction:: red_groebner .. autofunction:: is_groebner .. autofunction:: is_minimal .. autofunction:: is_reduced .. currentmodule:: sympy.polys.fglmtools .. 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 Reference ========= Modular GCD *********** .. currentmodule:: sympy.polys.modulargcd .. autoclass:: modgcd_univariate .. autoclass:: modgcd_bivariate .. autoclass:: modgcd_multivariate .. autoclass:: func_field_modgcd Undocumented ============ Many parts of the polys module are still undocumented, and even where there is documentation it is scarce. Please contribute! sympy-0.7.4.1/doc/src/modules/polys/index.rst0000644000175000017500000000141412253362407021257 0ustar georgeskgeorgesk.. _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 sympy-0.7.4.1/doc/src/modules/polys/wester.rst0000644000175000017500000003663312253362407021474 0ustar georgeskgeorgesk.. _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, ``_ sympy-0.7.4.1/doc/src/modules/polys/reference.rst0000644000175000017500000001002712253362407022106 0ustar georgeskgeorgesk.. _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.monomials .. autoclass:: Monomial .. autofunction:: itermonomials .. autofunction:: monomial_count Orderings of monomials ====================== .. currentmodule:: sympy.polys.orderings .. 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 .. autofunction:: apart_list .. autofunction:: assemble_partfrac_list sympy-0.7.4.1/doc/src/modules/polys/agca.rst0000644000175000017500000003253712253362407021055 0ustar georgeskgeorgesk.. _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.old_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.old_poly_ring(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. .. autoclass:: sympy.polys.domains.ring.Ring :members: free_module, ideal, quotient_ring :noindex: .. autofunction:: sympy.polys.domains.polynomialring.PolynomialRing :noindex: .. autoclass:: sympy.polys.domains.quotientring.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: sympy-0.7.4.1/doc/src/modules/polys/basics.rst0000644000175000017500000001323612253362407021421 0ustar georgeskgeorgesk.. _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') >>> 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 sympy-0.7.4.1/doc/src/modules/series.rst0000644000175000017500000001242712253362407020302 0ustar georgeskgeorgeskSeries 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.residuesympy-0.7.4.1/doc/src/modules/ntheory.rst0000644000175000017500000000336212253362407020476 0ustar georgeskgeorgeskNumber 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:: primitive_root .. autofunction:: sqrt_mod .. autofunction:: quadratic_residues .. autofunction:: nthroot_mod .. autofunction:: is_nthpow_residue .. autofunction:: is_quad_residue .. autofunction:: legendre_symbol .. autofunction:: jacobi_symbol sympy-0.7.4.1/doc/src/modules/tensor/0000755000175000017500000000000012253362407017562 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/modules/tensor/indexed.rst0000644000175000017500000000014312253362407021732 0ustar georgeskgeorgesk=============== Indexed Objects =============== .. automodule:: sympy.tensor.indexed :members: sympy-0.7.4.1/doc/src/modules/tensor/tensor.rst0000644000175000017500000000123712253362407021631 0ustar georgeskgeorgesk.. _tensor-tensor: Tensor ====== .. module:: sympy.tensor.tensor .. autoclass:: TIDS :members: .. autoclass:: VTIDS :members: .. autoclass:: _TensorManager :members: .. autoclass:: TensorIndexType :members: .. autoclass:: TensorIndex :members: .. autofunction:: tensor_indices .. autoclass:: TensorSymmetry :members: .. autoclass:: TensorType :members: .. autoclass:: TensorHead :members: .. autoclass:: TensExpr :members: .. autoclass:: TensAdd :members: .. autoclass:: TensMul :members: .. autofunction:: canon_bp .. autofunction:: tensor_mul .. autofunction:: riemann_cyclic_replace .. autofunction:: riemann_cyclic sympy-0.7.4.1/doc/src/modules/tensor/index.rst0000644000175000017500000000030412253362407021420 0ustar georgeskgeorgesk.. _tensor_module: ============= Tensor Module ============= .. automodule:: sympy.tensor Contents ======== .. toctree:: :maxdepth: 3 indexed.rst index_methods.rst tensor.rst sympy-0.7.4.1/doc/src/modules/tensor/index_methods.rst0000644000175000017500000000012112253362407023140 0ustar georgeskgeorgesk======= Methods ======= .. automodule:: sympy.tensor.index_methods :members: sympy-0.7.4.1/doc/src/modules/assumptions/0000755000175000017500000000000012253362407020635 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/modules/assumptions/ask.rst0000644000175000017500000000010112253362407022135 0ustar georgeskgeorgesk=== Ask === .. automodule:: sympy.assumptions.ask :members: sympy-0.7.4.1/doc/src/modules/assumptions/refine.rst0000644000175000017500000000012012253362407022630 0ustar georgeskgeorgesk====== Refine ====== .. automodule:: sympy.assumptions.refine :members: sympy-0.7.4.1/doc/src/modules/assumptions/handlers/0000755000175000017500000000000012253362407022435 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/modules/assumptions/handlers/order.rst0000644000175000017500000000012312253362407024276 0ustar georgeskgeorgesk===== Order ===== .. automodule:: sympy.assumptions.handlers.order :members: sympy-0.7.4.1/doc/src/modules/assumptions/handlers/ntheory.rst0000644000175000017500000000013312253362407024654 0ustar georgeskgeorgesk======= nTheory ======= .. automodule:: sympy.assumptions.handlers.ntheory :members: sympy-0.7.4.1/doc/src/modules/assumptions/handlers/calculus.rst0000644000175000017500000000013712253362407025003 0ustar georgeskgeorgesk======== Calculus ======== .. automodule:: sympy.assumptions.handlers.calculus :members: sympy-0.7.4.1/doc/src/modules/assumptions/handlers/index.rst0000644000175000017500000000026612253362407024302 0ustar georgeskgeorgesk======== Handlers ======== .. automodule:: sympy.assumptions.handlers Contents ======== .. toctree:: :maxdepth: 3 calculus.rst ntheory.rst order.rst sets.rst sympy-0.7.4.1/doc/src/modules/assumptions/handlers/sets.rst0000644000175000017500000000011712253362407024144 0ustar georgeskgeorgesk==== Sets ==== .. automodule:: sympy.assumptions.handlers.sets :members: sympy-0.7.4.1/doc/src/modules/assumptions/index.rst0000644000175000017500000002065612253362407022507 0ustar georgeskgeorgesk================== 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 a context so you don't have to pass that argument each time to function ask(). This is done by using the assuming context manager from module sympy.assumptions. :: >>> from sympy import * >>> x = Symbol('x') >>> y = Symbol('y') >>> facts = Q.positive(x), Q.positive(y) >>> with assuming(*facts): ... print(ask(Q.positive(2*x + y))) True 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. sympy-0.7.4.1/doc/src/modules/assumptions/assume.rst0000644000175000017500000000011612253362407022662 0ustar georgeskgeorgesk====== Assume ====== .. automodule:: sympy.assumptions.assume :members: sympy-0.7.4.1/doc/src/modules/solvers/0000755000175000017500000000000012253362407017745 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/modules/solvers/pde.rst0000644000175000017500000000267212253362407021256 0ustar georgeskgeorgesk.. _pde-docs: PDE === .. module::sympy.solvers.pde User Functions -------------- These are functions that are imported into the global namespace with ``from sympy import *``. They are intended for user use. :func:`pde_separate` ^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.pde.pde_separate :func:`pde_separate_add` ^^^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.pde.pde_separate_add :func:`pde_separate_mul` ^^^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.pde.pde_separate_mul :func:`pdsolve` ^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.pde.pdsolve :func:`classify_pde` ^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.pde.classify_pde :func:`checkpdesol` ^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.pde.checkpdesol Hint Methods ------------ These funcions are meant for internal use. However they contain useful information on the various solving methods. :obj:`pde_1st_linear_constant_coeff_homogeneous` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.pde.pde_1st_linear_constant_coeff_homogeneous :obj:`pde_1st_linear_constant_coeff` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.pde.pde_1st_linear_constant_coeff :obj:`pde_1st_linear_variable_coeff` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.pde.pde_1st_linear_variable_coeff Information on the pde module ----------------------------- .. automodule:: sympy.solvers.pde sympy-0.7.4.1/doc/src/modules/solvers/ode.rst0000644000175000017500000001244712253362407021256 0ustar georgeskgeorgesk.. _ode-docs: ODE === .. module::sympy.solvers.ode User Functions -------------- These are functions that are imported into the global namespace with ``from sympy import *``. These functions (unlike `Hint Functions`_, below) are intended for use by ordinary users of SymPy. :func:`dsolve` ^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.ode.dsolve :func:`classify_ode` ^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.ode.classify_ode :func:`checkodesol` ^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.ode.checkodesol :func:`homogeneous_order` ^^^^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.ode.homogeneous_order :func:`infinitesimals` ^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.ode.infinitesimals :func:`checkinfsol` ^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.ode.checkinfsol Hint Functions -------------- These functions are intended for internal use by :py:meth:`~sympy.solvers.ode.dsolve` and others. Unlike `User Functions`_, above, these are not intended for every-day use by ordinary SymPy users. Instead, functions such as :py:meth:`~sympy.solvers.ode.dsolve` should be used. Nonetheless, these functions contain useful information in their docstrings on the various ODE solving methods. For this reason, they are documented here. :obj:`allhints` ^^^^^^^^^^^^^^^ .. autodata:: sympy.solvers.ode.allhints :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 :obj:`almost_linear` ^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.ode.ode_almost_linear :obj:`linear_coefficients` ^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.ode.ode_linear_coefficients :obj:`separable_reduced` ^^^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.ode.ode_separable_reduced :obj:`lie_group` ^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.ode.ode_lie_group :obj:`1st_power_series` ^^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.ode.ode_1st_power_series :obj:`2nd_power_series_ordinary` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.ode.ode_2nd_power_series_ordinary :obj:`2nd_power_series_regular` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.ode.ode_2nd_power_series_regular Lie heuristics -------------- These functions are intended for internal use of the Lie Group Solver. Nonetheless, they contain useful information in their docstrings on the algorithms implemented for the various heuristics. :obj:`abaco1_simple` ^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.ode.lie_heuristic_abaco1_simple :obj:`abaco1_product` ^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.ode.lie_heuristic_abaco1_product :obj:`bivariate` ^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.ode.lie_heuristic_bivariate :obj:`chi` ^^^^^^^^^^ .. autofunction:: sympy.solvers.ode.lie_heuristic_chi :obj:`abaco2_similar` ^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.ode.lie_heuristic_abaco2_similar :obj:`function_sum` ^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.ode.lie_heuristic_function_sum :obj:`abaco2_unique_unknown` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.ode.lie_heuristic_abaco2_unique_unknown :obj:`abaco2_unique_general` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.ode.lie_heuristic_abaco2_unique_general :obj:`linear` ^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.ode.lie_heuristic_linear Information on the ode module ----------------------------- .. automodule:: sympy.solvers.ode sympy-0.7.4.1/doc/src/modules/solvers/diophantine.rst0000644000175000017500000003711512253362407023010 0ustar georgeskgeorgesk.. _diophantine-docs: Diophantine =========== Diophantine equations --------------------- The word "Diophantine" comes with the name Diophantus, a mathematician lived in the great city of Alexandria sometime around 250 AD. Often referred to as the "father of Algebra", Diophantus in his famous work "Arithmetica" presented 150 problems that marked the early beginnings of number theory, the field of study about integers and their properties. Diophantine equations play a central and an important part in number theory. We call a "Diophantine equation" to an equation of the form, `f(x_1, x_2, \ldots x_n) = 0` where `n \geq 2` and `x_1, x_2, \ldots x_n` are integer variables. If we can find `n` integers `a_1, a_2, \ldots a_n` such that `x_1 = a_1, x_2 = a_2, \ldots x_n = a_n` satisfies the above equation, we say that the equation is solvable. You can read more about Diophantine equations in [1]_ and [2]_. Currently, following five types of Diophantine equations can be solved using :py:meth:`~sympy.solvers.diophantine.diophantine` and other helper functions of the Diophantine module. - Linear Diophantine equations: `a_1x_1 + a_2x_2 + \ldots + a_nx_n = b`. - General binary quadratic equation: `ax^2 + bxy + cy^2 + dx + ey + f = 0` - Homogeneous ternary quadratic equation: `ax^2 + by^2 + cz^2 + dxy + eyz + fzx = 0` - Extended Pythagorean equation: `a_{1}x_{1}^2 + a_{2}x_{2}^2 + \ldots + a_{n}x_{n}^2 = a_{n+1}x_{n+1}^2` - General sum of squares: `x_{1}^2 + x_{2}^2 + \ldots + x_{n}^2 = k` Module structure ---------------- This module contains :py:meth:`~sympy.solvers.diophantine.diophantine` and helper functions that are needed to solve certain Diophantine equations. It's structured in the following manner. - :py:meth:`~sympy.solvers.diophantine.diophantine` - :py:meth:`~sympy.solvers.diophantine.diop_solve` - :py:meth:`~sympy.solvers.diophantine.classify_diop` - :py:meth:`~sympy.solvers.diophantine.diop_linear` - :py:meth:`~sympy.solvers.diophantine.diop_quadratic` - :py:meth:`~sympy.solvers.diophantine.diop_ternary_quadratic` - :py:meth:`~sympy.solvers.diophantine.diop_general_pythagorean` - :py:meth:`~sympy.solvers.diophantine.diop_general_sum_of_squares` - :py:meth:`~sympy.solvers.diophantine.merge_solution` When an equation is given to :py:meth:`~sympy.solvers.diophantine.diophantine`, it factors the equation(if possible) and solves the equation given by each factor by calling :py:meth:`~sympy.solvers.diophantine.diop_solve` separately. Then all the results are combined using :py:meth:`~sympy.solvers.diophantine.merge_solution`. :py:meth:`~sympy.solvers.diophantine.diop_solve` internally uses :py:meth:`~sympy.solvers.diophantine.classify_diop` to find the type of the equation(and some other details) given to it and then calls the appropriate solver function based on the type returned. For example, if :py:meth:`~sympy.solvers.diophantine.classify_diop` returned "linear" as the type of the equation, then :py:meth:`~sympy.solvers.diophantine.diop_solve` calls :py:meth:`~sympy.solvers.diophantine.diop_linear` to solve the equation. Each of the functions, :py:meth:`~sympy.solvers.diophantine.diop_linear`, :py:meth:`~sympy.solvers.diophantine.diop_quadratic`, :py:meth:`~sympy.solvers.diophantine.diop_ternary_quadratic`, :py:meth:`~sympy.solvers.diophantine.diop_general_pythagorean` and :py:meth:`~sympy.solvers.diophantine.diop_general_sum_of_squares` solves a specific type of equations and the type can be easily guessed by it's name. Apart from these functions, there are a considerable number of other functions in the "Diophantine Module" and all of them are listed under User functions and Internal functions. Tutorial -------- First, let's import the highest API of the Diophantine module. >>> from sympy.solvers.diophantine import diophantine Before we start solving the equations, we need to define the variables. >>> from sympy import symbols >>> x, y, z = symbols("x, y, z", Integer=True) Let's start by solving the easiest type of Diophantine equations, i.e. linear Diophantine equations. Let's solve `2x + 3y = 5`. Note that although we write the equation in the above form, when we input the equation to any of the functions in Diophantine module, it needs to be in the form `eq = 0`. >>> diophantine(2*x + 3*y - 5) set([(3*t - 5, -2*t + 5)]) Note that stepping one more level below the highest API, we can solve the very same equation by calling :py:meth:`~sympy.solvers.diophantine.diop_solve`. >>> from sympy.solvers.diophantine import diop_solve >>> diop_solve(2*x + 3*y - 5) (3*t - 5, -2*t + 5) Note that it returns a tuple rather than a set. :py:meth:`~sympy.solvers.diophantine.diophantine` always return a set of tuples. But :py:meth:`~sympy.solvers.diophantine.diop_solve` may return a single tuple or a set of tuples depending on the type of the equation given. We can also solve this equation by calling :py:meth:`~sympy.solvers.diophantine.diop_linear`, which is what :py:meth:`~sympy.solvers.diophantine.diop_solve` calls internally. >>> from sympy.solvers.diophantine import diop_linear >>> diop_linear(2*x + 3*y - 5) (3*t - 5, -2*t + 5) If the given equation has no solutions then the outputs will look like below. >>> diophantine(2*x + 4*y - 3) set() >>> diop_solve(2*x + 4*y - 3) (None, None) >>> diop_linear(2*x + 4*y - 3) (None, None) Note that except for the highest level API, in case of no solutions, a tuple of `None` are returned. Size of the tuple is the same as the number of variables. Also, one can specifically set the parameter to be used in the solutions by passing a customized parameter. Consider the following example. >>> m = symbols("m", Integer=True) >>> diop_solve(2*x + 3*y - 5, m) (3*m - 5, -2*m + 5) Please note that for the moment, user can set the parameter only for linear Diophantine equations and binary quadratic equations. Let's try solving a binary quadratic equation which is an equation with two variables and has a degree of two. Before trying to solve these equations, an idea about various cases associated with the equation would help a lot. Please refer [3]_ and [4]_ for detailed analysis of different cases and the nature of the solutions. Let us define `\Delta = b^2 - 4ac` w.r.t. the binary quadratic `ax^2 + bxy + cy^2 + dx + ey + f = 0`. When `\Delta < 0`, there are either no solutions or only a finite number of solutions. >>> diophantine(x**2 - 4*x*y + 8*y**2 - 3*x + 7*y - 5) set([(2, 1), (5, 1)]) In the above equation `\Delta = (-4)^2 - 4*1*8 = -16` and hence only a finite number of solutions exist. When `\Delta = 0` we might have either no solutions or parameterized solutions. >>> diophantine(3*x**2 - 6*x*y + 3*y**2 - 3*x + 7*y - 5) set() >>> diophantine(x**2 - 4*x*y + 4*y**2 - 3*x + 7*y - 5) set([(-2*t**2 - 7*t + 10, -t**2 - 3*t + 5)]) >>> diophantine(x**2 + 2*x*y + y**2 - 3*x - 3*y) set([(t, -t), (t, -t + 3)]) The most interesting case is when `\Delta > 0` and it is not a perfect square. In this case, the equation has either no solutions or an infinte number of solutions. Consider the below cases where `\Delta = 8`. >>> diophantine(x**2 - 4*x*y + 2*y**2 - 3*x + 7*y - 5) set() >>> from sympy import expand >>> n = symbols("n", Integer=True) >>> s = diophantine(x**2 - 2*y**2 - 2*x - 4*y, n) >>> x_n, y_n = s.pop() >>> expand(x_n) -(-2*sqrt(2) + 3)**n/2 + sqrt(2)*(-2*sqrt(2) + 3)**n/2 - sqrt(2)*(2*sqrt(2) + 3)**n/2 - (2*sqrt(2) + 3)**n/2 + 1 >>> expand(y_n) -sqrt(2)*(-2*sqrt(2) + 3)**n/4 + (-2*sqrt(2) + 3)**n/2 + sqrt(2)*(2*sqrt(2) + 3)**n/4 + (2*sqrt(2) + 3)**n/2 - 1 Here `n` is an integer. Although x_n and y_n may not look like integers, substituting in specific values for n (and simplifying) shows that they are. For example consider the following example where we set n equal to 9. >>> from sympy import simplify >>> simplify(x_n.subs({n: 9})) -9369318 Any binary quadratic of the form `ax^2 + bxy + cy^2 + dx + ey + f = 0` can be transformed to an equivalent form `X^2 - DY^2 = N`. >>> from sympy.solvers.diophantine import find_DN, diop_DN, transformation_to_DN >>> find_DN(x**2 - 3*x*y + y**2 - 7*x + 5*y - 3) (5, 920) So, the above equation is equivalent to the equation `X^2 - 5Y^2 = 920` after a linear transformation. If we want to find the linear transformation, we can use :py:meth:`~sympy.solvers.diophantine.transformation_to_DN` >>> A, B = transformation_to_DN(x**2 - 3*x*y + y**2 - 7*x + 5*y - 3) Here A is a 2 X 2 matrix and B is a 2 X 1 matrix such that the transformation .. math:: \begin{bmatrix} X\\Y \end{bmatrix} = A \begin{bmatrix} x\\y \end{bmatrix} + B gives the equation `X^2 -5Y^2 = 920`. Values of `A` and `B` are as belows. >>> A Matrix([ [1/10, 3/10], [ 0, 1/5]]) >>> B Matrix([ [ 1/5], [-11/5]]) We can solve an equation of the form `X^2 - DY^2 = N` by passing `D` and `N` to :py:meth:`~sympy.solvers.diophantine.diop_DN` >>> diop_DN(5, 920) [] Unfortunately, our equation does not have solutions. Now let's turn to homogeneous ternary quadratic equations. These equations are of the form `ax^2 + by^2 + cz^2 + dxy + eyz + fzx = 0`. These type of equations either have infinitely many solutions or no solutions (except the obvious solution (0, 0, 0)) >>> diophantine(3*x**2 + 4*y**2 - 5*z**2 + 4*x*y + 6*y*z + 7*z*x) set() >>> diophantine(3*x**2 + 4*y**2 - 5*z**2 + 4*x*y - 7*y*z + 7*z*x) set([(-16*p**2 + 28*p*q + 20*q**2, 3*p**2 + 38*p*q - 25*q**2, 4*p**2 - 24*p*q + 68*q**2)]) If you are only interested about a base solution rather than the parameterized general solution (to be more precise, one of the general solutions), you can use :py:meth:`~sympy.solvers.diophantine.diop_ternary_quadratic`. >>> from sympy.solvers.diophantine import diop_ternary_quadratic >>> diop_ternary_quadratic(3*x**2 + 4*y**2 - 5*z**2 + 4*x*y - 7*y*z + 7*z*x) (-4, 5, 1) :py:meth:`~sympy.solvers.diophantine.diop_ternary_quadratic` first converts the given equation to an equivalent equation of the form `w^2 = AX^2 + BY^2` and then it uses :py:meth:`~sympy.solvers.diophantine.descent` to solve the latter equation. You can refer to the docs of :py:meth:`~sympy.solvers.diophantine.transformation_to_normal` to find more on this. The equation `w^2 = AX^2 + BY^2` can be solved more easily by using the Aforementioned :py:meth:`~sympy.solvers.diophantine.descent`. >>> from sympy.solvers.diophantine import descent >>> descent(3, 1) # solves the equation w**2 = 3*Y**2 + Z**2 (1, 0, 1) Here the solution tuple is in the order (w, Y, Z) The extended Pythagorean equation, `a_{1}x_{1}^2 + a_{2}x_{2}^2 + \ldots + a_{n}x_{n}^2 = a_{n+1}x_{n+1}^2` and the general sum of squares equation, `x_{1}^2 + x_{2}^2 + \ldots + x_{n}^2 = k` can also be solved using the Diophantine module. >>> from sympy.abc import a, b, c, d, e, f >>> diophantine(9*a**2 + 16*b**2 + c**2 + 49*d**2 + 4*e**2 - 25*f**2) set([(70*t1**2 + 70*t2**2 + 70*t3**2 + 70*t4**2 - 70*t5**2, 105*t1*t5, 420*t2*t5, 60*t3*t5, 210*t4*t5, 42*t1**2 + 42*t2**2 + 42*t3**2 + 42*t4**2 + 42*t5**2)]) function :py:meth:`~sympy.solvers.diophantine.diop_general_pythagorean` can also be called directly to solve the same equation. This is true about the general sum of squares too. Either you can call :py:meth:`~sympy.solvers.diophantine.diop_general_pythagorean` or use the high level API. >>> diophantine(a**2 + b**2 + c**2 + d**2 + e**2 + f**2 - 112) set([(8, 4, 4, 4, 0, 0)]) If you want to get a more thorough idea about the the Diophantine module please refer to the following blog. http://thilinaatsympy.wordpress.com/ References ---------- .. [1] Andreescu, Titu. Andrica, Dorin. Cucurezeanu, Ion. An Introduction to Diophantine Equations .. [2] Diophantine Equation, Wolfram Mathworld, [online]. Available: http://mathworld.wolfram.com/DiophantineEquation.html .. [3] Methods to solve Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0,[online], Available: http://www.alpertron.com.ar/METHODS.HTM .. [4] Solving the equation ax^2+ bxy + cy^2 + dx + ey + f= 0, [online], Available: http://www.jpr2718.org/ax2p.pdf User Functions -------------- These are functions that are imported into the global namespace with ``from sympy import *``. These functions are intended for use by ordinary users of SymPy. :func:`diophantine` ^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.diophantine :func:`diop_solve` ^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.diop_solve :func:`classify_diop` ^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.classify_diop :func:`diop_linear` ^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.diop_linear :func:`base_solution_linear` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.base_solution_linear :func:`diop_quadratic` ^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.diop_quadratic :func:`diop_DN` ^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.diop_DN :func:`cornacchia` ^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.cornacchia :func:`diop_bf_DN` ^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.diop_bf_DN :func:`transformation_to_DN` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.transformation_to_DN :func:`find_DN` ^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.find_DN :func:`diop_ternary_quadratic` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.diop_ternary_quadratic :func:`square_factor` ^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.square_factor :func:`descent` ^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.descent :func:`diop_general_pythagorean` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.diop_general_pythagorean :func:`diop_general_sum_of_squares` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.diop_general_sum_of_squares :func:`partition` ^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.partition :func:`sum_of_three_squares` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.sum_of_three_squares :func:`sum_of_four_squares` ^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.sum_of_four_squares Internal Functions ------------------ These functions are intended for the internal use in Diophantine module. :obj:`merge_solution` ^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.merge_solution :obj:`divisible` ^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.divisible :obj:`extended_euclid` ^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.extended_euclid :obj:`PQa` ^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.PQa :obj:`equivalent` ^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.equivalent :obj:`simplified` ^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.simplified :obj:`parametrize_ternary_quadratic` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.parametrize_ternary_quadratic :obj:`diop_ternary_quadratic_normal` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.diop_ternary_quadratic_normal :obj:`ldescent` ^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.ldescent :obj:`gaussian_reduce` ^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.gaussian_reduce :obj:`holzer` ^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.holzer :obj:`prime_as_sum_of_two_squares` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.prime_as_sum_of_two_squares :obj:`pairwise_prime` ^^^^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.pairwise_prime :obj:`make_prime` ^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.make_prime :obj:`reconstruct` ^^^^^^^^^^^^^^^^^^ .. autofunction:: sympy.solvers.diophantine.reconstruct sympy-0.7.4.1/doc/src/modules/solvers/solvers.rst0000644000175000017500000000357012253362407022201 0ustar georgeskgeorgeskSolvers ========== .. 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) ------------------------------------- See :ref:`pde-docs`. Deutils (Utilities for solving ODE's and PDE's) ----------------------------------------------- .. autofunction:: sympy.solvers.deutils.ode_order Recurrence Equations -------------------- .. module:: sympy.solvers.recurr .. autofunction:: rsolve .. autofunction:: rsolve_poly .. autofunction:: rsolve_ratio .. autofunction:: rsolve_hyper Systems of Polynomial Equations ------------------------------- .. autofunction:: sympy.solvers.polysys.solve_poly_system .. autofunction:: sympy.solvers.polysys.solve_triangulated Diophantine Equations (DEs) --------------------------- See :ref:`diophantine-docs` sympy-0.7.4.1/doc/src/modules/plotting.rst0000644000175000017500000002110712253362407020643 0ustar georgeskgeorgeskPlotting Module =============== .. module:: sympy.plotting.plot Introduction ------------ The plotting module allows you to make 2-dimensional and 3-dimensional plots. Presently the plots are rendered using ``matplotlib`` as a 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] sympy-0.7.4.1/doc/src/modules/rewriting.rst0000644000175000017500000001051712253362407021020 0ustar georgeskgeorgeskTerm 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₀, -y), (x₁, (x + x₀)⋅(x₀ + z))], ⎣╲╱ x₁ + x₁⎦⎠ Optimizations to be performed before and after common subexpressions elimination can be passed in the``optimizations`` optional argument. A set of predefined basic optimizations can be applied by passing ``optimizations='basic'``:: >>> pprint(cse((x-y)*(z-y) + sqrt((x-y)*(z-y)), optimizations='basic'), ... use_unicode=True) ⎛ ⎡ ____ ⎤⎞ ⎝[(x₀, -(x - y)⋅(y - z))], ⎣╲╱ x₀ + x₀⎦⎠ However, these optimizations can be very slow for large expressions. Moreover, if speed is a concern, one can pass the option ``order='none'``. Order of terms will then be dependent on hashing algorithm implementation, but speed will be greatly improved. More information: .. autofunction:noindex: cse sympy-0.7.4.1/doc/src/modules/index.rst0000644000175000017500000000303012253362407020105 0ustar georgeskgeorgesk.. _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 crypto.rst concrete.rst evalf.rst numeric-computation.rst functions/index.rst galgebra/index.rst geometry.rst integrals/integrals.rst logic.rst matrices/index.rst Mpmath 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/pde.rst solvers/solvers.rst solvers/diophantine.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). sympy-0.7.4.1/doc/src/modules/sets.rst0000644000175000017500000000150412253362407017760 0ustar georgeskgeorgeskSets =========== .. 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: ImageSet ^^^^^^^^ .. autoclass:: ImageSet :members: sympy-0.7.4.1/doc/src/modules/functions/0000755000175000017500000000000012253362407020260 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/modules/functions/index.rst0000644000175000017500000000060512253362407022122 0ustar georgeskgeorgesk.. _functions: 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: .. _functions-contents: Contents ======== .. toctree:: :maxdepth: 2 elementary.rst combinatorial.rst special.rst sympy-0.7.4.1/doc/src/modules/functions/combinatorial.rst0000644000175000017500000000573312253362407023645 0ustar georgeskgeorgeskCombinatorial ============= 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: stirling -------- .. autofunction:: sympy.functions.combinatorial.numbers.stirling Enumeration =========== Three functions are available. Each of them attempts to efficiently compute a given combinatorial quantity for a given set or multiset which can be entered as an integer, sequence or multiset (dictionary with elements as keys and multiplicities as values). The ``k`` parameter indicates the number of elements to pick (or the number of partitions to make). When ``k`` is None, the sum of the enumeration for all ``k`` (from 0 through the number of items represented by ``n``) is returned. A ``replacement`` parameter is recognized for combinations and permutations; this indicates that any item may appear with multiplicity as high as the number of items in the original set. >>> from sympy.functions.combinatorial.numbers import nC, nP, nT >>> items = 'baby' nC -- Calculate the number of combinations of length ``k``. >>> [nC(items, k) for k in range(len(items) + 1)], nC(items) ([1, 3, 4, 3, 1], 12) >>> nC('aaa', 2) 1 >>> nC('abc', 2) 3 >>> nC(3, 2) 3 nP -- Calculate the number of permutations of length ``k``. >>> [nP(items, k) for k in range(len(items) + 1)], nP(items) ([1, 3, 7, 12, 12], 35) >>> nC('aaa', 2) 1 >>> nC('abc', 2) 3 >>> nC(3, 2) 3 nT -- Calculate the number of partitions that have ``k`` parts. >>> [nT(items, k) for k in range(len(items) + 1)], nT(items) ([0, 1, 5, 4, 1], 11) >>> nT('aaa', 2) 1 >>> nT('abc', 2) 3 >>> nT(3, 2) 1 Note that the integer for ``n`` indicates *identical* items for ``nT`` but indicates ``n`` *different* items for ``nC`` and ``nP``. sympy-0.7.4.1/doc/src/modules/functions/special.rst0000644000175000017500000001161612253362407022437 0ustar georgeskgeorgeskSpecial ======= 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 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:: Ei .. autoclass:: expint .. autofunction:: E1 .. autoclass:: li .. autoclass:: Li .. autoclass:: Si .. autoclass:: Ci .. autoclass:: Shi .. autoclass:: Chi .. autoclass:: sympy.functions.special.error_functions.FresnelIntegral :members: .. autoclass:: fresnels .. autoclass:: fresnelc Error Functions --------------- .. autoclass:: sympy.functions.special.error_functions.erf .. autoclass:: sympy.functions.special.error_functions.erfc .. autoclass:: sympy.functions.special.error_functions.erfi .. autoclass:: sympy.functions.special.error_functions.erf2 .. autoclass:: sympy.functions.special.error_functions.erfinv .. autoclass:: sympy.functions.special.error_functions.erfcinv .. autoclass:: sympy.functions.special.error_functions.erf2inv Bessel Type Functions --------------------- .. autoclass:: sympy.functions.special.bessel.BesselBase :members: .. autoclass:: sympy.functions.special.bessel.besselj .. autoclass:: sympy.functions.special.bessel.bessely .. _besseli: .. 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: Elliptic integrals ------------------ .. module:: sympy.functions.special.elliptic_integrals .. autoclass:: elliptic_k .. autoclass:: elliptic_f .. autoclass:: elliptic_e .. autoclass:: elliptic_pi Orthogonal Polynomials ---------------------- .. automodule:: sympy.functions.special.polynomials Jacobi Polynomials ++++++++++++++++++ .. autoclass:: sympy.functions.special.polynomials.jacobi :members: .. autofunction:: sympy.functions.special.polynomials.jacobi_normalized 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 ------------------- .. autoclass:: sympy.functions.special.spherical_harmonics.Ynm .. autofunction:: sympy.functions.special.spherical_harmonics.Ynm_c .. autoclass:: sympy.functions.special.spherical_harmonics.Znm 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: sympy-0.7.4.1/doc/src/modules/functions/elementary.rst0000644000175000017500000001303712253362407023163 0ustar georgeskgeorgeskElementary ========== 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 `\operatorname{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: sympy-0.7.4.1/doc/src/modules/core.rst0000644000175000017500000001237412253362407017741 0ustar georgeskgeorgeskSymPy 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: 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 as_int ^^^^^^ .. autofunction:: as_int exprtools --------- .. module:: sympy.core.exprtools gcd_terms ^^^^^^^^^ .. autofunction:: gcd_terms factor_terms ^^^^^^^^^^^^ .. autofunction:: factor_terms sympy-0.7.4.1/doc/src/modules/diffgeom.rst0000644000175000017500000000076412253362407020571 0ustar georgeskgeorgeskDifferential 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: sympy-0.7.4.1/doc/src/modules/integrals/0000755000175000017500000000000012253362407020240 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/modules/integrals/g-functions.rst0000644000175000017500000007446412253362407023245 0ustar georgeskgeorgeskComputing 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 `\operatorname{Exp}: \mathbb{C} \rightarrow \mathcal{S}, (x + i y) \mapsto (\exp(x), y)` and `\operatorname{Log}: \mathcal{S} \rightarrow \mathbb{C}, (e^x, y) \mapsto x + iy`. These can both be shown to be holomorphic, and are indeed mutual inverses. We also sometimes formally attach a point "zero" (`0`) 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 `\{\operatorname{Exp}(z) : \Re(z) < k\}`, which at least defines a topology. Elements of `\mathcal{S}_0` shall be called polar numbers. We further define functions `\operatorname{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 `\operatorname{Exp}(a) \operatorname{Exp}(b) = \operatorname{Exp}(a + b)` for `a, b \in \mathbb{C}`, also for `a \in \mathcal{S}` and `b \in \mathbb{C}` we define `a^b = \operatorname{Exp}(b \operatorname{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 < \operatorname{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 \operatorname{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(\operatorname{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 sympy-0.7.4.1/doc/src/modules/integrals/integrals.rst0000644000175000017500000001105612253362407022765 0ustar georgeskgeorgeskSymbolic 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 :func:`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. These 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.heurisch_wrapper .. automethod:: sympy.integrals.trigintegrate .. automethod:: sympy.integrals.manualintegrate .. automethod:: sympy.integrals.manualintegrate.integral_steps The 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 Numeric Integrals ================= SymPy has functions to calculate points and weights for Gaussian quadrature of any order and any precision: .. autofunction:: sympy.integrals.quadrature.gauss_legendre .. autofunction:: sympy.integrals.quadrature.gauss_laguerre .. autofunction:: sympy.integrals.quadrature.gauss_hermite .. autofunction:: sympy.integrals.quadrature.gauss_gen_laguerre .. autofunction:: sympy.integrals.quadrature.gauss_chebyshev_t .. autofunction:: sympy.integrals.quadrature.gauss_chebyshev_u .. autofunction:: sympy.integrals.quadrature.gauss_jacobi sympy-0.7.4.1/doc/src/modules/concrete.rst0000644000175000017500000000562712253362407020616 0ustar georgeskgeorgeskConcrete 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) 2*(n + 1)*(2*n + 1) >>> 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 sympy-0.7.4.1/doc/src/modules/galgebra/0000755000175000017500000000000012253362407020014 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/modules/galgebra/printing.rst0000644000175000017500000000073712253362407022407 0ustar georgeskgeorgeskPrinting for Geometric Algebra ============================== .. module:: sympy.galgebra.printing Class Reference --------------- .. autoclass:: enhance_print :members: .. autoclass:: GA_Printer :members: .. autoclass:: GA_LatexPrinter :members: Function Reference ------------------ .. autofunction:: find_executable .. autofunction:: Get_Program .. autofunction:: latex .. autofunction:: Print_Function .. autofunction:: print_latex .. autofunction:: xdvi sympy-0.7.4.1/doc/src/modules/galgebra/multable.dat0000644000175000017500000000455012253362407022317 0ustar georgeskgeorgesk(1)(1) = 1 (1)(a0) = a0 (1)(a1) = a1 (1)(a2) = a2 (1)(a0a1) = a0a1 (1)(a0a2) = a0a2 (1)(a1a2) = a1a2 (1)(a0a1a2) = a0a1a2 (a0)(1) = a0 (a0)(a0) = {a0**2}1 (a0)(a1) = a0a1 (a0)(a2) = a0a2 (a0)(a0a1) = {a0**2}a1 (a0)(a0a2) = {a0**2}a2 (a0)(a1a2) = a0a1a2 (a0)(a0a1a2) = {a0**2}a1a2 (a1)(1) = a1 (a1)(a0) = {2*(a0.a1)}1-a0a1 (a1)(a1) = {a1**2}1 (a1)(a2) = a1a2 (a1)(a0a1) = {-a1**2}a0+{2*(a0.a1)}a1 (a1)(a0a2) = {2*(a0.a1)}a2-a0a1a2 (a1)(a1a2) = {a1**2}a2 (a1)(a0a1a2) = {-a1**2}a0a2+{2*(a0.a1)}a1a2 (a2)(1) = a2 (a2)(a0) = {2*(a0.a2)}1-a0a2 (a2)(a1) = {2*(a1.a2)}1-a1a2 (a2)(a2) = {a2**2}1 (a2)(a0a1) = {-2*(a1.a2)}a0+{2*(a0.a2)}a1+a0a1a2 (a2)(a0a2) = {-a2**2}a0+{2*(a0.a2)}a2 (a2)(a1a2) = {-a2**2}a1+{2*(a1.a2)}a2 (a2)(a0a1a2) = {a2**2}a0a1+{-2*(a1.a2)}a0a2+{2*(a0.a2)}a1a2 (a0a1)(1) = a0a1 (a0a1)(a0) = {2*(a0.a1)}a0+{-a0**2}a1 (a0a1)(a1) = {a1**2}a0 (a0a1)(a2) = a0a1a2 (a0a1)(a0a1) = {-a0**2*a1**2}1+{2*(a0.a1)}a0a1 (a0a1)(a0a2) = {2*(a0.a1)}a0a2+{-a0**2}a1a2 (a0a1)(a1a2) = {a1**2}a0a2 (a0a1)(a0a1a2) = {-a0**2*a1**2}a2+{2*(a0.a1)}a0a1a2 (a0a2)(1) = a0a2 (a0a2)(a0) = {2*(a0.a2)}a0+{-a0**2}a2 (a0a2)(a1) = {2*(a1.a2)}a0-a0a1a2 (a0a2)(a2) = {a2**2}a0 (a0a2)(a0a1) = {-2*a0**2*(a1.a2)}1+{2*(a0.a2)}a0a1+{a0**2}a1a2 (a0a2)(a0a2) = {-a0**2*a2**2}1+{2*(a0.a2)}a0a2 (a0a2)(a1a2) = {-a2**2}a0a1+{2*(a1.a2)}a0a2 (a0a2)(a0a1a2) = {a0**2*a2**2}a1+{-2*a0**2*(a1.a2)}a2+{2*(a0.a2)}a0a1a2 (a1a2)(1) = a1a2 (a1a2)(a0) = {2*(a0.a2)}a1+{-2*(a0.a1)}a2+a0a1a2 (a1a2)(a1) = {2*(a1.a2)}a1+{-a1**2}a2 (a1a2)(a2) = {a2**2}a1 (a1a2)(a0a1) = {2*a1**2*(a0.a2)-4*(a0.a1)*(a1.a2)}1+{2*(a1.a2)}a0a1+{-a1**2}a0a2 +{2*(a0.a1)}a1a2 (a1a2)(a0a2) = {-2*a2**2*(a0.a1)}1+{a2**2}a0a1+{2*(a0.a2)}a1a2 (a1a2)(a1a2) = {-a1**2*a2**2}1+{2*(a1.a2)}a1a2 (a1a2)(a0a1a2) = {-a1**2*a2**2}a0+{2*a2**2*(a0.a1)}a1+{2*a1**2*(a0.a2) -4*(a0.a1)*(a1.a2)}a2+{2*(a1.a2)}a0a1a2 (a0a1a2)(1) = a0a1a2 (a0a1a2)(a0) = {2*(a0.a2)}a0a1+{-2*(a0.a1)}a0a2+{a0**2}a1a2 (a0a1a2)(a1) = {2*(a1.a2)}a0a1+{-a1**2}a0a2 (a0a1a2)(a2) = {a2**2}a0a1 (a0a1a2)(a0a1) = {2*a1**2*(a0.a2)-4*(a0.a1)*(a1.a2)}a0+{2*a0**2*(a1.a2)}a1 +{-a0**2*a1**2}a2+{2*(a0.a1)}a0a1a2 (a0a1a2)(a0a2) = {-2*a2**2*(a0.a1)}a0+{a0**2*a2**2}a1+{2*(a0.a2)}a0a1a2 (a0a1a2)(a1a2) = {-a1**2*a2**2}a0+{2*(a1.a2)}a0a1a2 (a0a1a2)(a0a1a2) = {-a0**2*a1**2*a2**2}1+{2*a2**2*(a0.a1)}a0a1+{2*a1**2*(a0.a2) -4*(a0.a1)*(a1.a2)}a0a2+{2*a0**2*(a1.a2)}a1a2sympy-0.7.4.1/doc/src/modules/galgebra/manifold.rst0000644000175000017500000000025212253362407022336 0ustar georgeskgeorgeskManifold for Geometric Algebra ============================== .. module:: sympy.galgebra.manifold Class Reference --------------- .. autoclass:: Manifold :members: sympy-0.7.4.1/doc/src/modules/galgebra/ga.rst0000644000175000017500000032310712253362407021143 0ustar georgeskgeorgesk.. raw:: html .. role:: red :class: color:red ***************** Geometric Algebra ***************** :Author: Alan Bromborsky .. |release| replace:: 0.10 .. % Complete documentation on the extended LaTeX markup used for Python .. % documentation is available in ``Documenting Python'', which is part .. % of the standard documentation for Python. It may be found online .. % at: .. % .. % http://www.python.org/doc/current/doc/doc.html .. % \lstset{language=Python} .. % \input{macros} .. % This is a template for short or medium-size Python-related documents, .. % mostly notably the series of HOWTOs, but it can be used for any .. % document you like. .. % The title should be descriptive enough for people to be able to find .. % the relevant document. .. % Increment the release number whenever significant changes are made. .. % The author and/or editor can define 'significant' however they like. .. % At minimum, give your name and an email address. You can include a .. % snail-mail address if you like. .. % This makes the Abstract go on a separate page in the HTML version; .. % if a copyright notice is used, it should go immediately after this. .. % .. % \ifhtml .. % \chapter*{Front Matter\label{front}} .. % \fi .. % Copyright statement should go here, if needed. .. % ... .. % The abstract should be a paragraph or two long, and describe the .. % scope of the document. .. topic:: Abstract This document describes the implementation, installation and use of a geometric algebra module written in python that utilizes the :mod:`sympy` symbolic algebra library. The python module *galgebra* has been developed for coordinate free calculations using the operations (geometric, outer, and inner products etc.) of geometric algebra. The operations can be defined using a completely arbitrary metric defined by the inner products of a set of arbitrary vectors or the metric can be restricted to enforce orthogonality and signature constraints on the set of vectors. In addition the module includes the geometric, outer (curl) and inner (div) derivatives and the ability to define a curvilinear coordinate system. The module requires the sympy module and the numpy module for numerical linear algebra calculations. For latex output a latex distribution must be installed. .. module:: sympy.galgebra.ga What is Geometric Algebra? ========================== Geometric algebra is the Clifford algebra of a real finite dimensional vector space or the algebra that results when a real finite dimensional vector space is extended with a product of vectors (geometric product) that is associative, left and right distributive, and yields a real number for the square (geometric product) of any vector [Hestenes]_, [Doran]_. The elements of the geometric algebra are called multivectors and consist of the linear combination of scalars, vectors, and the geometric product of two or more vectors. The additional axioms for the geometric algebra are that for any vectors :math:`a`, :math:`b`, and :math:`c` in the base vector space ([Doran]_,p85): .. math:: :nowrap: \begin{equation*} \begin{array}{c} a\left ( bc \right ) = \left ( ab \right ) c \\ a\left ( b+c \right ) = ab+ac \\ \left ( a + b \right ) c = ac+bc \\ aa = a^{2} \in \Re \end{array} \end{equation*} By induction the first three axioms also apply to any multivectors. The dot product of two vectors is defined by ([Doran]_,p86) .. math:: :nowrap: \begin{equation*} a\cdot b \equiv (ab+ba)/2 \end{equation*} Then consider .. math:: :nowrap: \begin{align*} c &= a+b \\ c^{2} &= (a+b)^{2} \\ c^{2} &= a^{2}+ab+ba+b^{2} \\ a\cdot b &= (c^{2}-a^{2}-b^{2})/2 \in \Re \end{align*} Thus :math:`a\cdot b` is real. The objects generated from linear combinations of the geometric products of vectors are called multivectors. If a basis for the underlying vector space is the set of vectors formed from :math:`e_{1},\dots,e_{n}` a complete basis for the geometric algebra is given by the scalar :math:`1`, the vectors :math:`e_{1},\dots,e_{n}` and all geometric products of vectors .. math:: :nowrap: \begin{equation*} e_{i_{1}} e_{i_{2}} \dots e_{i_{r}} \mbox{ where } 0 \le r \le n \mbox{, } 0 \le i_{j} \le n \mbox{ and } 0 < i_{1} < i_{2} < \dots < i_{r} \le n \end{equation*} Each base of the complete basis is represented by a noncommutative symbol (except for the scalar 1) with name :math:`e_{i_{1}}\dots e_{i_{r}}` so that the general multivector :math:`\boldsymbol{A}` is represented by (:math:`A` is the scalar part of the multivector and the :math:`A^{i_{1},\dots,i_{r}}` are scalars) .. math:: :nowrap: \begin{equation*} \boldsymbol{A} = A + \sum_{r=1}^{n}\sum_{i_{1},\dots,i_{r},\;\forall\; 0\le i_{j} \le n} A^{i_{1},\dots,i_{r}}e_{i_{1}}e_{i_{2}}\dots e_{r} \end{equation*} The critical operation in setting up the geometric algebra is reducing the geometric product of any two bases to a linear combination of bases so that we can calculate a multiplication table for the bases. Since the geometric product is associative we can use the operation (by definition for two vectors :math:`a\cdot b \equiv (ab+ba)/2` which is a scalar) .. _eq1: .. math:: :nowrap: :label: 5.1 \begin{equation} e_{i_{j+1}}e_{i_{j}} = 2e_{i_{j+1}}\cdot e_{i_{j}} - e_{i_{j}}e_{i_{j+1}} \end{equation} These processes are repeated untill every basis list in :math:`\boldsymbol{A}` is in normal (ascending) order with no repeated elements. As an example consider the following .. math:: :nowrap: \begin{align*} e_{3}e_{2}e_{1} &= (2(e_{2}\cdot e_{3}) - e_{2}e_{3})e_{1} \\ &= 2(e_{2}\cdot e_{3})e_{1} - e_{2}e_{3}e_{1} \\ &= 2(e_{2}\cdot e_{3})e_{1} - e_{2}(2(e_{1}\cdot e_{3})-e_{1}e_{3}) \\ &= 2((e_{2}\cdot e_{3})e_{1}-(e_{1}\cdot e_{3})e_{2})+e_{2}e_{1}e_{3} \\ &= 2((e_{2}\cdot e_{3})e_{1}-(e_{1}\cdot e_{3})e_{2}+(e_{1}\cdot e_{2})e_{3})-e_{1}e_{2}e_{3} \end{align*} which results from repeated application of equation :ref:`5.1 `. If the product of basis vectors contains repeated factors equation :ref:`5.1 ` can be used to bring the repeated factors next to one another so that if :math:`e_{i_{j}} = e_{i_{j+1}}` then :math:`e_{i_{j}}e_{i_{j+1}} = e_{i_{j}}\cdot e_{i_{j+1}}` which is a scalar that commutes with all the terms in the product and can be brought to the front of the product. Since every repeated pair of vectors in a geometric product of :math:`r` factors reduces the number of noncommutative factors in the product by :math:`r-2`. The number of bases in the multivector algebra is :math:`2^{n}` and the number containing :math:`r` factors is :math:`{n\choose r}` which is the number of combinations or :math:`n` things taken :math:`r` at a time (binominal coefficient). The other construction required for formulating the geometric algebra is the outer or wedge product (symbol :math:`\wedge`) of :math:`r` vectors denoted by :math:`a_{1}\wedge\dots\wedge a_{r}`. The wedge product of :math:`r` vectors is called an :math:`r`-blade and is defined by ([Doran]_,p86) .. math:: :nowrap: \begin{equation*} a_{1}\wedge\dots\wedge a_{r} \equiv \sum_{i_{j_{1}}\dots i_{j_{r}}} \epsilon^{i_{j_{1}}\dots i_{j_{r}}}a_{i_{j_{1}}}\dots a_{i_{j_{1}}} \end{equation*} where :math:`\epsilon^{i_{j_{1}}\dots i_{j_{r}}}` is the contravariant permutation symbol which is :math:`+1` for an even permutation of the superscripts, :math:`0` if any superscripts are repeated, and :math:`-1` for an odd permutation of the superscripts. From the definition :math:`a_{1}\wedge\dots\wedge a_{r}` is antisymmetric in all its arguments and the following relation for the wedge product of a vector :math:`a` and an :math:`r`-blade :math:`B_{r}` can be derived .. _eq2: .. math:: :label: 5.2 :nowrap: \begin{equation} a\wedge B_{r} = (aB_{r}+(-1)^{r}B_{r}a)/2 \end{equation} Using equation :ref:`5.2 ` one can represent the wedge product of all the basis vectors in terms of the geometric product of all the basis vectors so that one can solve (the system of equations is lower diagonal) for the geometric product of all the basis vectors in terms of the wedge product of all the basis vectors. Thus a general multivector :math:`\boldsymbol{B}` can be represented as a linear combination of a scalar and the basis blades. .. math:: :nowrap: \begin{equation*} \boldsymbol{B} = B + \sum_{r=1}^{n}\sum_{i_{1},\dots,i_{r},\;\forall\; 0\le i_{j} \le n} B^{i_{1},\dots,i_{r}}e_{i_{1}}\wedge e_{i_{2}}\wedge\dots\wedge e_{r} \end{equation*} Using the blades :math:`e_{i_{1}}\wedge e_{i_{2}}\wedge\dots\wedge e_{r}` creates a graded algebra where :math:`r` is the grade of the basis blades. The grade-:math:`r` part of :math:`\boldsymbol{B}` is the linear combination of all terms with grade :math:`r` basis blades. The scalar part of :math:`\boldsymbol{B}` is defined to be grade-:math:`0`. Now that the blade expansion of :math:`\boldsymbol{B}` is defined we can also define the grade projection operator :math:`\left < {\boldsymbol{B}} \right >_{r}` by .. math:: :nowrap: \begin{equation*} \left < {\boldsymbol{B}} \right >_{r} = \sum_{i_{1},\dots,i_{r},\;\forall\; 0\le i_{j} \le n} B^{i_{1},\dots,i_{r}}e_{i_{1}}\wedge e_{i_{2}}\wedge\dots\wedge e_{r} \end{equation*} and .. math:: :nowrap: \begin{equation*} \left < {\boldsymbol{B}} \right >_{} \equiv \left < {\boldsymbol{B}} \right >_{0} = B \end{equation*} Then if :math:`\boldsymbol{A}_{r}` is an :math:`r`-grade multivector and :math:`\boldsymbol{B}_{s}` is an :math:`s`-grade multivector we have .. math:: :nowrap: \begin{equation*} \boldsymbol{A}_{r}\boldsymbol{B}_{s} = \left < {\boldsymbol{A}_{r}\boldsymbol{B}_{s}} \right >_{\left |{{r-s}}\right |}+\left < {\boldsymbol{A}_{r}\boldsymbol{B}_{s}} \right >_{\left |{{r-s}}\right |+2}+\cdots \left < {\boldsymbol{A}_{r}\boldsymbol{B}_{s}} \right >_{r+s} \end{equation*} and define ([Hestenes]_,p6) .. math:: :nowrap: \begin{align*} \boldsymbol{A}_{r}\wedge\boldsymbol{B}_{s} &\equiv \left < {\boldsymbol{A}_{r}\boldsymbol{B}_{s}} \right >_{r+s} \\ \boldsymbol{A}_{r}\cdot\boldsymbol{B}_{s} &\equiv \left \{ \begin{array}{cc} r\mbox{ or }s \ne 0: & \left < {\boldsymbol{A}_{r}\boldsymbol{B}_{s}} \right >_{\left |{{r-s}}\right |} \\ r\mbox{ or }s = 0: & 0 \end{array} \right \} \end{align*} where :math:`\boldsymbol{A}_{r}\cdot\boldsymbol{B}_{s}` is called the dot or inner product of two pure grade multivectors. For the case of two non-pure grade multivectors .. math:: :nowrap: \begin{align*} \boldsymbol{A}\wedge\boldsymbol{B} &= \sum_{r,s}\left < {\boldsymbol{A}} \right >_{r}\wedge\left < {\boldsymbol{B}} \right >_{s} \\ \boldsymbol{A}\cdot\boldsymbol{B} &= \sum_{r,s\ne 0}\left < {\boldsymbol{A}} \right >_{r}\cdot\left < {\boldsymbol{B}} \right >_{s} \end{align*} Two other products, the right (:math:`\rfloor`) and left (:math:`\lfloor`) contractions, are defined by .. math:: :nowrap: \begin{align*} \boldsymbol{A}\lfloor\boldsymbol{B} &\equiv \sum_{r,s}\left \{ \begin{array}{cc} \left < {\boldsymbol{A}_r\boldsymbol{B}_{s}} \right >_{r-s} & r \ge s \\ 0 & r < s \end{array}\right \} \\ \boldsymbol{A}\rfloor\boldsymbol{B} &\equiv \sum_{r,s}\left \{ \begin{array}{cc} \left < {\boldsymbol{A}_{r}\boldsymbol{B}_{s}} \right >_{s-r} & s \ge r \\ 0 & s < r\end{array}\right \} \end{align*} A final operation for multivectors is the reverse. If a multivector :math:`\boldsymbol{A}` is the geometric product of :math:`r` vectors (versor) so that :math:`\boldsymbol{A} = a_{1}\dots a_{r}` the reverse is defined by .. math:: :nowrap: \begin{align*} \boldsymbol{A}^{\dagger} \equiv a_{r}\dots a_{1} \end{align*} where for a general multivector we have (the the sum of the reverse of versors) .. math:: :nowrap: \begin{equation*} \boldsymbol{A}^{\dagger} = A + \sum_{r=1}^{n}(-1)^{r(r-1)/2}\sum_{i_{1},\dots,i_{r},\;\forall\; 0\le i_{j} \le n} A^{i_{1},\dots,i_{r}}e_{i_{1}}\wedge e_{i_{2}}\wedge\dots\wedge e_{r} \end{equation*} note that if :math:`\boldsymbol{A}` is a versor then :math:`\boldsymbol{A}\boldsymbol{A}^{\dagger}\in\Re` and (:math:`AA^{\dagger} \ne 0`) .. math:: :nowrap: \begin{equation*} \boldsymbol{A}^{-1} = {\displaystyle\frac{\boldsymbol{A}^{\dagger}}{\boldsymbol{AA}^{\dagger}}} \end{equation*} Representation of Multivectors in Sympy ======================================= The sympy python module offers a simple way of representing multivectors using linear combinations of commutative expressions (expressions consisting only of commuting sympy objects) and noncommutative symbols. We start by defining :math:`n` noncommutative sympy symbols .. code-block:: python (e_1,...,e_n) = symbols('e_1,...,e_n',commutative=False) Several software packages for numerical geometric algebra calculations are available from Doran-Lasenby group and the Dorst group. Symbolic packages for Clifford algebra using orthongonal bases such as :math:`e_{i}e_{j}+e_{j}e_{i} = 2\eta_{ij}`, where :math:`\eta_{ij}` is a numeric array are available in Maple and Mathematica. The symbolic algebra module, *galgebra*, developed for python does not depend on an orthogonal basis representation, but rather is generated from a set of :math:`n` arbitrary symbolic vectors, :math:`e_{1},e_{2},\dots,e_{n}` and a symbolic metric tensor :math:`g_{ij} = e_{i}\cdot e_{j}`. In order not to reinvent the wheel all scalar symbolic algebra is handled by the python module :mod:`sympy` and the abstract basis vectors are encoded as noncommuting sympy symbols. The basic geometic algebra operations will be implemented in python by defining a multivector class, MV, and overloading the python operators in Table :ref:`5.1 ` where *A* and *B* are any two multivectors (In the case of *+*, *-*, *\**, *^*, and *|* the operation is also defined if *A* or *B* is a sympy symbol or a sympy real number). .. _table1: .. csv-table:: :header: Operation,Result :widths: 10, 40 ''A+B'', sum of multivectors ''A-B'', difference of multivectors ''A*B'', geometric product ''A^B'', outer product of multivectors ''A|B'', inner product of multivectors ''AB'', right contraction of multivectors Table :ref:`5.1 `. Multivector operations for *galgebra* Since *<* and *>* have no r-forms (in python for the *<* and *>* operators there are no *__rlt__()* and *__rlt__()* member functions to overload) we can only have mixed modes (scalars and multivectors) if the first operand is a multivector. .. note:: Except for *<* and *>* all the multivector operators have r-forms so that as long as one of the operands, left or right, is a multivector the other can be a multivector or a scalar (sympy symbol or integer). .. warning:: Note that the operator order precedence is determined by python and is not necessarily that used by geometric algebra. It is **absolutely essential** to use parenthesis in multivector expressions containing *^*, *|*, *<*, and/or *>*. As an example let *A* and *B* be any two multivectors. Then *A + A*B = A +(A*B)*, but *A+A^B = (2*A)^B* since in python the *^* operator has a lower precedence than the '+' operator. In geometric algebra the outer and inner products and the left and right contractions have a higher precedence than the geometric product and the geometric product has a higher precedence than addition and subtraction. In python the *^*, *|*, *<*, and *>* all have a lower precedence than *+* and *-* while *\** has a higher precedence than *+* and *-*. For those users who wish to define a default operator precedence the functions *define_precedence()* and *GAeval()* are available in the module *galgebra/precedence*. .. function:: sympy.galgebra.precedence.define_precedence(gd, op_ord='<>|,^,\*') Define the precedence of the multivector operations. The function *define_precedence()* must be called from the main program and the first argument *gd* must be set to *globals()*. The second argument *op_ord* determines the operator precedence for expressions input to the function *GAeval()*. The default value of *op_ord* is *'<>|,^,\*'*. For the default value the *<*, *>*, and *|* operations have equal precedence followed by *^*, and *^* is followed by *\**. .. function:: sympy.galgebra.precedence.GAeval(s, pstr=False) The function *GAeval()* returns a multivector expression defined by the string *s* where the operations in the string are parsed according to the precedences defined by *define_precedence()*. *pstr* is a flag to print the input and output of *GAeval()* for debugging purposes. *GAeval()* works by adding parenthesis to the input string *s* with the precedence defined by *op_ord='<>|,^,\*'*. Then the parsed string is converted to a sympy expression using the python *eval()* function. For example consider where *X*, *Y*, *Z*, and *W* are multivectors .. code-block:: python define_precedence(globals()) V = GAeval('X|Y^Z*W') The sympy variable *V* would evaluate to *((X|Y)^Z)\*W*. .. _vbm: Vector Basis and Metric ======================= The two structures that define the :class:`MV` (multivector) class are the symbolic basis vectors and the symbolic metric. The symbolic basis vectors are input as a string with the symbol name separated by spaces. For example if we are calculating the geometric algebra of a system with three vectors that we wish to denote as *a0*, *a1*, and *a2* we would define the string variable: .. code-block:: python basis = 'a0 a1 a2' that would be input into the multivector setup function. The next step would be to define the symbolic metric for the geometric algebra of the basis we have defined. The default metric is the most general and is the matrix of the following symbols .. _eq3: .. math:: :nowrap: :label: 3 \begin{equation} g = \left [ \begin{array}{ccc} (a0.a0) & (a0.a1) & (a0.a2) \\ (a0.a1) & (a1.a1) & (a1.a2) \\ (a0.a2) & (a1.a2) & (a2.a2) \\ \end{array} \right ] \end{equation} where each of the :math:`g_{ij}` is a symbol representing all of the dot products of the basis vectors. Note that the symbols are named so that :math:`g_{ij} = g_{ji}` since for the symbol function :math:`(a0.a1) \ne (a1.a0)`. Note that the strings shown in equation :ref:`5.3 ` are only used when the values of :math:`g_{ij}` are output (printed). In the *galgebra* module (library) the :math:`g_{ij}` symbols are stored in a static member of the multivector class :class:`MV` as the sympy matrix *MV.metric* (:math:`g_{ij}` = *MV.metric[i,j]*). The default definition of :math:`g` can be overwritten by specifying a string that will define :math:`g`. As an example consider a symbolic representation for conformal geometry. Define for a basis .. code-block:: python basis = 'a0 a1 a2 n nbar' and for a metric .. code-block:: python metric = '# # # 0 0, # # # 0 0, # # # 0 0, 0 0 0 0 2, 0 0 0 2 0' then calling *MV.setup(basis,metric)* would initialize the metric tensor .. math:: :nowrap: \begin{equation*} g = \left [ \begin{array}{ccccc} (a0.a0) & (a0.a1) & (a0.a2) & 0 & 0\\ (a0.a1) & (a1.a1) & (a1.a2) & 0 & 0\\ (a0.a2) & (a1.a2) & (a2.a2) & 0 & 0 \\ 0 & 0 & 0 & 0 & 2 \\ 0 & 0 & 0 & 2 & 0 \end{array} \right ] \end{equation*} Here we have specified that *n* and *nbar* are orthonal to all the *a*'s, *(n.n) = (nbar.nbar) = 0*, and *(n.nbar) = 2*. Using *#* in the metric definition string just tells the program to use the default symbol for that value. When *MV.setup* is called multivector representations of the basis local to the program are instantiated. For our first example that means that the symbolic vectors named *a0*, *a1*, and *a2* are created and returned from *MV.setup* via a tuple as in - .. code-block:: python (a_1,a_2,a3) = MV.setup('a_1 a_2 a_3',metric=metric) Note that the python variable name for a basis vector does not have to correspond to the name give in *MV.setup()*, one may wish to use a shorted python variable name to reduce programming (typing) errors, for example one could use - .. code-block:: python (a1,a2,a3) = MV.setup('a_1 a_2 a_3',metric=metric) or .. code-block:: python (g1,g2,g3) = MV.setup('gamma_1 gamma_2 gamma_3',metric=metric) so that if the latex printer is used *e1* would print as :math:`\boldsymbol{e_{1}}` and *g1* as :math:`\boldsymbol{\gamma_{1}}`. .. note:: Additionally *MV.setup* has simpified options for naming a set of basis vectors and for inputing an othogonal basis. If one wishes to name the basis vectors :math:`\boldsymbol{e}_{x}`, :math:`\boldsymbol{e}_{y}`, and :math:`\boldsymbol{e}_{z}` then set *basis='e*x|y|z'* or to name :math:`\boldsymbol{\gamma}_{t}`, :math:`\boldsymbol{\gamma}_{x}`, :math:`\boldsymbol{\gamma}_{y}`, and :math:`\boldsymbol{\gamma}_{z}` then set *basis='gamma*t|x|y|z'*. For the case of an othogonal basis if the signature of the vector space is :math:`(1,1,1)` (Euclidian 3-space) set *metric='[1,1,1]'* or if it is :math:`(1,-1,-1,-1)` (Minkowsi 4-space) set *metric='[1,-1,-1,-1]'*. Representation and Reduction of Multivector Bases ================================================= In our symbolic geometric algebra all multivectors can be obtained from the symbolic basis vectors we have input, via the different operations available to geometric algebra. The first problem we have is representing the general multivector in terms terms of the basis vectors. To do this we form the ordered geometric products of the basis vectors and develop an internal representation of these products in terms of python classes. The ordered geometric products are all multivectors of the form :math:`a_{i_{1}}a_{i_{2}}\dots a_{i_{r}}` where :math:`i_{1}`. .. _table3: :: 1 = 1 a0 = a0 a1 = a1 a2 = a2 a0^a1 = {-(a0.a1)}1+a0a1 a0^a2 = {-(a0.a2)}1+a0a2 a1^a2 = {-(a1.a2)}1+a1a2 a0^a1^a2 = {-(a1.a2)}a0+{(a0.a2)}a1+{-(a0.a1)}a2+a0a1a2 Table :ref:`5.3 `. Bases blades in terms of bases. The important thing to notice about Table :ref:`5.3 ` is that it is a triagonal (lower triangular) system of equations so that using a simple back substitution algorithm we can solve for the pseudo bases in terms of the blades giving Table :ref:`5.4 `. .. _table4: :: 1 = 1 a0 = a0 a1 = a1 a2 = a2 a0a1 = {(a0.a1)}1+a0^a1 a0a2 = {(a0.a2)}1+a0^a2 a1a2 = {(a1.a2)}1+a1^a2 a0a1a2 = {(a1.a2)}a0+{-(a0.a2)}a1+{(a0.a1)}a2+a0^a1^a2 Table :ref:`5.4 `. Bases in terms of basis blades. Using Table :ref:`5.4 ` and simple substitution we can convert from a base multivector representation to a blade representation. Likewise, using Table :ref:`5.3 ` we can convert from blades to bases. Using the blade representation it becomes simple to program functions that will calculate the grade projection, reverse, even, and odd multivector functions. Note that in the multivector class *MV* there is a class variable for each instantiation, *self.bladeflg*, that is set to *False* for a base representation and *True* for a blade representation. One needs to keep track of which representation is in use since various multivector operations require conversion from one representation to the other. .. warning:: When the geometric product of two multivectors is calculated the module looks to see if either multivector is in blade representation. If either is the result of the geometric product is converted to a blade representation. One result of this is that if either of the multivectors is a simple vector (which is automatically a blade) the result will be in a blade representation. If *a* and *b* are vectors then the result *a*b* will be *(a.b)+a^b* or simply *a^b* if *(a.b) = 0*. Outer and Inner Products, Left and Right Contractions ===================================================== In geometric algebra any general multivector :math:`A` can be decomposed into pure grade multivectors (a linear combination of blades of all the same order) so that in a :math:`n`-dimensional vector space .. math:: :nowrap: \begin{equation*} A = \sum_{r = 0}^{n}A_{r} \end{equation*} The geometric product of two pure grade multivectors :math:`A_{r}` and :math:`B_{s}` has the form .. math:: :nowrap: \begin{equation*} A_{r}B_{s} = \left < {A_{r}B_{s}} \right >_{\left |{{r-s}}\right |}+\left < {A_{r}B_{s}} \right >_{\left |{{r-s}}\right |+2}+\cdots+\left < {A_{r}B_{s}} \right >_{r+s} \end{equation*} where :math:`\left < { } \right >_{t}` projects the :math:`t` grade components of the multivector argument. The inner and outer products of :math:`A_{r}` and :math:`B_{s}` are then defined to be .. math:: :nowrap: \begin{equation*} A_{r}\cdot B_{s} = \left < {A_{r}B_{s}} \right >_{\left |{{r-s}}\right |} \end{equation*} .. math:: :nowrap: \begin{equation*} A_{r}\wedge B_{s} = \left < {A_{r}B_{s}} \right >_{r+s} \end{equation*} and .. math:: :nowrap: \begin{equation*} A\cdot B = \sum_{r,s}A_{r}\cdot B_{s} \end{equation*} .. math:: :nowrap: \begin{equation*} A\wedge B = \sum_{r,s}A_{r}\wedge B_{s} \end{equation*} Likewise the right (:math:`\lfloor`) and left (:math:`\rfloor`) contractions are defined as .. math:: :nowrap: \begin{equation*} A_{r}\lfloor B_{s} = \left \{ \begin{array}{cc} \left < {A_{r}B_{s}} \right >_{r-s} & r \ge s \\ 0 & r < s \end{array} \right \} \end{equation*} .. math:: :nowrap: \begin{equation*} A_{r}\rfloor B_{s} = \left \{ \begin{array}{cc} \left < {A_{r}B_{s}} \right >_{s-r} & s \ge r \\ 0 & s < r \end{array} \right \} \end{equation*} and .. math:: :nowrap: \begin{equation*} A\lfloor B = \sum_{r,s}A_{r}\lfloor B_{s} \end{equation*} .. math:: :nowrap: \begin{equation*} A\rfloor B = \sum_{r,s}A_{r}\rfloor B_{s} \end{equation*} .. warning:: In the *MV* class we have overloaded the *^* operator to represent the outer product so that instead of calling the outer product function we can write *mv1^ mv2*. Due to the precedence rules for python it is **absolutely essential** to enclose outer products in parenthesis. .. warning:: In the *MV* class we have overloaded the *|* operator for the inner product, *>* operator for the right contraction, and *<* operator for the left contraction. Instead of calling the inner product function we can write *mv1|mv2*, *mv1>mv2*, or *mv1_{i}}^{\dagger} \end{equation*} but .. _eq4: .. math:: :label: 5.4 :nowrap: \begin{equation} {\left < {A} \right >_{i}}^{\dagger} = \left ( -1\right )^{\frac{i\left ( i-1\right )}{2}}\left < {A} \right >_{i} \end{equation} which is proved by expanding the blade bases in terms of orthogonal vectors and showing that equation :ref:`5.4 ` holds for the geometric product of orthogonal vectors. The reverse is important in the theory of rotations in :math:`n`-dimensions. If :math:`R` is the product of an even number of vectors and :math:`RR^{\dagger} = 1` then :math:`RaR^{\dagger}` is a composition of rotations of the vector :math:`a`. If :math:`R` is the product of two vectors then the plane that :math:`R` defines is the plane of the rotation. That is to say that :math:`RaR^{\dagger}` rotates the component of :math:`a` that is projected into the plane defined by :math:`a` and :math:`b` where :math:`R=ab`. :math:`R` may be written :math:`R = e^{\frac{\theta}{2}U}`, where :math:`\theta` is the angle of rotation and :math:`u` is a unit blade :math:`\left ( u^{2} = \pm 1\right )` that defines the plane of rotation. .. _recframe: Reciprocal Frames ================= If we have :math:`M` linearly independent vectors (a frame), :math:`a_{1},\dots,a_{M}`, then the reciprocal frame is :math:`a^{1},\dots,a^{M}` where :math:`a_{i}\cdot a^{j} = \delta_{i}^{j}`, :math:`\delta_{i}^{j}` is the Kronecker delta (zero if :math:`i \ne j` and one if :math:`i = j`). The reciprocal frame is constructed as follows: .. math:: :nowrap: \begin{equation*} E_{M} = a_{1}\wedge\dots\wedge a_{M} \end{equation*} .. math:: :nowrap: \begin{equation*} E_{M}^{-1} = {\displaystyle\frac{E_{M}}{E_{M}^{2}}} \end{equation*} Then .. math:: :nowrap: \begin{equation*} a^{i} = \left ( -1\right )^{i-1}\left ( a_{1}\wedge\dots\wedge \breve{a}_{i} \wedge\dots\wedge a_{M}\right ) E_{M}^{-1} \end{equation*} where :math:`\breve{a}_{i}` indicates that :math:`a_{i}` is to be deleted from the product. In the standard notation if a vector is denoted with a subscript the reciprocal vector is denoted with a superscript. The multivector setup function *MV.setup(basis,metric,rframe)* has the argument *rframe* with a default value of *False*. If it is set to *True* the reciprocal frame of the basis vectors is calculated. Additionally there is the function *reciprocal_frame(vlst,names='')* external to the *MV* class that will calculate the reciprocal frame of a list, *vlst*, of vectors. If the argument *names* is set to a space delimited string of names for the vectors the reciprocal vectors will be given these names. .. _deriv: Geometric Derivative ==================== If :math:`F` is a multivector field that is a function of a vector :math:`x = x^{i}\boldsymbol{e}_{i}` (we are using the summation convention that pairs of subscripts and superscripts are summed over the dimension of the vector space) then the geometric derivative :math:`\nabla F` is given by (in this section the summation convention is used): .. math:: :nowrap: \begin{equation*} \nabla F = \boldsymbol{e}^{i}{\displaystyle\frac{\partial F}{\partial x^{i}}} \end{equation*} If :math:`F_{R}` is a grade-:math:`R` multivector and :math:`F_{R} = F_{R}^{i_{1}\dots i_{R}}\boldsymbol{e}_{i_{1}}\wedge\dots\wedge \boldsymbol{e}_{i_{R}}` then .. math:: :nowrap: \begin{equation*} \nabla F_{R} = {\displaystyle\frac{\partial F_{R}^{i_{1}\dots i_{R}}}{\partial x^{j}}}\boldsymbol{e}^{j}\left (\boldsymbol{e}_{i_{1}}\wedge \dots\wedge \boldsymbol{e}_{i_{R}} \right ) \end{equation*} Note that :math:`\boldsymbol{e}^{j}\left (\boldsymbol{e}_{i_{1}}\wedge\dots\wedge \boldsymbol{e}_{i_{R}} \right )` can only contain grades :math:`R-1` and :math:`R+1` so that :math:`\nabla F_{R}` also can only contain those grades. For a grade-:math:`R` multivector :math:`F_{R}` the inner (div) and outer (curl) derivatives are defined as .. math:: :nowrap: \begin{equation*} \nabla\cdot F_{R} = \left < \nabla F_{R}\right >_{R-1} \end{equation*} and .. math:: :nowrap: \begin{equation*} \nabla\wedge F_{R} = \left < \nabla F_{R}\right >_{R+1} \end{equation*} For a general multivector function :math:`F` the inner and outer derivatives are just the sum of the inner and outer dervatives of each grade of the multivector function. Curvilinear coordinates are derived from a vector function :math:`x(\boldsymbol{\theta})` where :math:`\boldsymbol{\theta} = \left (\theta_{1},\dots,\theta_{N}\right )` where the number of coordinates is equal to the dimension of the vector space. In the case of 3-dimensional spherical coordinates :math:`\boldsymbol{\theta} = \left ( r,\theta,\phi \right )` and the coordinate generating function :math:`x(\boldsymbol{\theta})` is .. math:: :nowrap: \begin{equation*} x = r \cos\left({\phi}\right) \sin\left({\theta}\right){\boldsymbol{{e}_{x}}}+ r \sin\left({\phi}\right) \sin\left({\theta}\right){\boldsymbol{{e}_{y}}}+ r \cos\left({\theta}\right){\boldsymbol{{e}_{z}}} \end{equation*} A coordinate frame is derived from :math:`x` by :math:`\boldsymbol{e}_{i} = {\displaystyle\frac{\partial {x}}{\partial {\theta^{i}}}}`. The following show the frame for spherical coordinates. .. math:: :nowrap: \begin{equation*} \boldsymbol{e}_{r} = \cos\left({\phi}\right) \sin\left({\theta}\right){\boldsymbol{{e}_{x}}}+\sin\left({\phi}\right) \sin\left({\theta}\right){\boldsymbol{{e}_{y}}}+\cos\left({\theta}\right){\boldsymbol{{e}_{z}}} \end{equation*} .. math:: :nowrap: \begin{equation*} \boldsymbol{e}_{{\theta}} = \cos\left({\phi}\right) \cos\left({\theta}\right){\boldsymbol{{e}_{x}}}+r \cos\left({\theta}\right) \sin\left({\phi}\right){\boldsymbol{{e}_{y}}} - r \sin\left({\theta}\right){\boldsymbol{{e}_{z}}} \end{equation*} .. math:: :nowrap: \begin{equation*} \boldsymbol{e}_{{\phi}} = - r \sin\left({\phi}\right) \sin\left({\theta}\right){\boldsymbol{{e}_{x}}}+r \cos\left({\phi}\right) \sin\left({\theta}\right){\boldsymbol{{e}_{y}}} \end{equation*} The coordinate frame generated in this manner is not necessarily normalized so define a normalized frame by .. math:: :nowrap: \begin{equation*} \boldsymbol{\hat{e}}_{i} = {\displaystyle\frac{\boldsymbol{e}_{i}}{\sqrt{\left |{{\boldsymbol{e}_{i}^{2}}}\right |}}} = {\displaystyle\frac{\boldsymbol{e}_{i}}{\left |{{\boldsymbol{e}_{i}}}\right |}} \end{equation*} This works for all :math:`\boldsymbol{e}_{i}^{2} \neq 0` since we have defined :math:`\left |\boldsymbol{e}_{i}\right | = \sqrt{\left |\boldsymbol{e}_{i}^{2}\right |}`. For spherical coordinates the normalized frame vectors are .. math:: :nowrap: \begin{equation*} \boldsymbol{\hat{e}}_{r} = \cos\left({\phi}\right) \sin\left({\theta}\right){\boldsymbol{{e}_{x}}}+\sin\left({\phi}\right) \sin\left({\theta}\right){\boldsymbol{{e}_{y}}}+\cos\left({\theta}\right){\boldsymbol{{e}_{z}}} \end{equation*} .. math:: :nowrap: \begin{equation*} \boldsymbol{\hat{e}}_{{\theta}} = \cos\left({\phi}\right) \cos\left({\theta}\right){\boldsymbol{{e}_{x}}}+\cos\left({\theta}\right) \sin\left({\phi}\right){\boldsymbol{{e}_{y}}}- \sin\left({\theta}\right){\boldsymbol{{e}_{z}}} \end{equation*} .. math:: :nowrap: \begin{equation*} \boldsymbol{\hat{e}}_{{\phi}} = - \sin\left({\phi}\right){\boldsymbol{{e}_{x}}}+\cos\left({\phi}\right){\boldsymbol{{e}_{y}}} \end{equation*} The geometric derivative in curvilinear coordinates is given by .. math:: :nowrap: \begin{align*} \nabla F_{R} & = \boldsymbol{e}^{i}{\displaystyle\frac{\partial {}}{\partial {x^{i}}}}\left ( F_{R}^{i_{1}\dots i_{R}} \boldsymbol{\hat{e}}_{i_{1}}\wedge\dots\wedge\boldsymbol{\hat{e}}_{i_{R}}\right ) \\ & = \boldsymbol{e^{j}}{\displaystyle\frac{\partial {}}{\partial {\theta^{j}}}}\left ( F_{R}^{i_{1}\dots i_{R}} \boldsymbol{\hat{e}}_{i_{1}}\wedge\dots\wedge\boldsymbol{\hat{e}}_{i_{R}}\right ) \\ & = \left ({\displaystyle\frac{\partial {}}{\partial {\theta^{j}}}} F_{R}^{i_{1}\dots i_{R}}\right ) \boldsymbol{e^{j}}\left (\boldsymbol{\hat{e}}_{i_{1}}\wedge\dots\wedge\boldsymbol{\hat{e}}_{i_{R}}\right )+ F_{R}^{i_{1}\dots i_{R}}\boldsymbol{e^{j}} {\displaystyle\frac{\partial {}}{\partial {\theta^{j}}}}\left (\boldsymbol{\hat{e}}_{i_{1}}\wedge\dots\wedge\boldsymbol{\hat{e}}_{i_{R}}\right ) \\ & = \left ({\displaystyle\frac{\partial {}}{\partial {\theta^{j}}}} F_{R}^{i_{1}\dots i_{R}}\right ) \boldsymbol{e^{j}}\left (\boldsymbol{\hat{e}}_{i_{1}}\wedge\dots\wedge\boldsymbol{\hat{e}}_{i_{R}}\right )+ F_{R}^{i_{1}\dots i_{R}}C\left \{ \boldsymbol{\hat{e}}_{i_{1}}\wedge\dots\wedge\boldsymbol{\hat{e}}_{i_{R}}\right \} \end{align*} where .. math:: :nowrap: \begin{equation*} C\left \{ \boldsymbol{\hat{e}}_{i_{1}}\wedge\dots\wedge\boldsymbol{\hat{e}}_{i_{R}}\right \} = \boldsymbol{e^{j}}{\displaystyle\frac{\partial {}}{\partial {\theta^{j}}}} \left (\boldsymbol{\hat{e}}_{i_{1}}\wedge\dots\wedge\boldsymbol{\hat{e}}_{i_{R}}\right ) \end{equation*} are the connection multivectors for the curvilinear coordinate system. For a spherical coordinate system they are .. math:: :nowrap: \begin{equation*} C\left \{\boldsymbol{\hat{e}}_{r}\right \} = \frac{2}{r} \end{equation*} .. math:: :nowrap: \begin{equation*} C\left \{\boldsymbol{\hat{e}}_{\theta}\right \} = \frac{\cos\left({\theta}\right)}{r \sin\left({\theta}\right)} +\frac{1}{r}\boldsymbol{\hat{e}}_{r}\wedge\boldsymbol{\hat{e}}_{\theta} \end{equation*} .. math:: :nowrap: \begin{equation*} C\left \{\boldsymbol{\hat{e}}_{\phi}\right \} = \frac{1}{r}\boldsymbol{{\hat{e}}_{r}}\wedge\boldsymbol{\hat{e}}_{{\phi}}+ \frac{\cos\left({\theta}\right)}{r \sin\left({\theta}\right)}\boldsymbol{\hat{e}}_{{\theta}}\wedge\boldsymbol{\hat{e}}_{{\phi}} \end{equation*} .. math:: :nowrap: \begin{equation*} C\left \{\hat{e}_{r}\wedge\hat{e}_{\theta}\right \} = - \frac{\cos\left({\theta}\right)}{r \sin\left({\theta}\right)} \boldsymbol{\hat{e}}_{r}+\frac{1}{r}\boldsymbol{\hat{e}}_{{\theta}} \end{equation*} .. math:: :nowrap: \begin{equation*} C\left \{\boldsymbol{\hat{e}}_{r}\wedge\boldsymbol{\hat{e}}_{\phi}\right \} = \frac{1}{r}\boldsymbol{\hat{e}}_{{\phi}} - \frac{\cos\left({\theta}\right)}{r \sin\left({\theta}\right)}\boldsymbol{\hat{e}}_{r}\wedge\boldsymbol{\hat{e}}_{{\theta}}\wedge\boldsymbol{\hat{e}}_{{\phi}} \end{equation*} .. math:: :nowrap: \begin{equation*} C\left \{\boldsymbol{\hat{e}}_{\theta}\wedge\boldsymbol{\hat{e}}_{\phi}\right \} = \frac{2}{r}\boldsymbol{\hat{e}}_{r}\wedge \boldsymbol{\hat{e}}_{\theta}\wedge\boldsymbol{\hat{e}}_{\phi} \end{equation*} .. math:: :nowrap: \begin{equation*} C\left \{\boldsymbol{\hat{e}}_r\wedge\boldsymbol{\hat{e}}_{\theta}\wedge\boldsymbol{\hat{e}}_{\phi}\right \} = 0 \end{equation*} Numpy, LaTeX, and Ansicon Installation ====================================== To install the geometric algebra module on windows,linux, or OSX perform the following operations #. Install sympy. *galgebra* is included in sympy. #. To install texlive in linux or windows #. Go to and click on "install-tl.zip" o download #. Unzip "install-tl.zip" anywhere on your machine #. Open the file "readme.en.html" in the "readme-html.dir" directory. This file contains the information needed to install texlive. #. Open a terminal (console) in the "install-tl-XXXXXX" directory #. Follow the instructions in "readme.en.html" file to run the install-tl.bat file in windows or the install-tl script file in linux. #. For OSX install mactex from . #. Install python-nympy if you want to calculate numerical matrix functons (determinant, inverse, eigenvalues, etc.). For windows go to and install the distribution of numpy appropriate for your system. For OSX go to . #. It is strongly suggested that you go to and install the version of the "geany" editor appropriate for your system. #. If you wish to use "enhance_print" on windows - #. Go to and download "ansicon" #. In the Edit -> Preferences -> Tools menu of "geany" enter into the Terminal input the full path of "ansicon.exe" After installation if you are doing you code development in the *galgebra* directory you need only include .. code-block:: python from sympy.galgebra.printing import xdvi,enhance_print from sympy.galgebra.ga import * to use the *galgebra* module. In addition to the code shown in the examples section of this document there are more examples in the Examples directory under the *galgebra* directory. Module Components ================= Initializing Multivector Class ------------------------------ The multivector class is initialized with: .. function:: sympy.galgebra.ga.MV.setup(basis, metric=None, coords=None, rframe=False, debug=False, curv=(None, None)) The *basis* and *metric* parameters were described in section :ref:`vbm`. If *rframe=True* the reciprocal frame of the symbolic bases vectors is calculated. If *debug=True* the data structure required to initialize the :class:`MV` class are printer out. *coords* is a tuple of :class:`sympy` symbols equal in length to the number of basis vectors. These symbols are used as the arguments of a multivector field as a function of position and for calculating the derivatives of a multivector field (if *coords* is defined then *rframe* is automatically set equal to *True*). Additionally, :func:`MV.setup` calculates the pseudo scalar, :math:`I` and makes them available to the programmer as *MV.I* and *MV.Iinv*. :func:`MV.setup` always returns a tuple containing the basis vectors (as multivectors) so that if we have the code .. code-block:: python (e1,e2,e3) = MV.setup('e_1 e_2 e_3') then we can define a multivector by the expression .. code-block:: python (a1,a2,a3) = symbols('a__1 a__2 a__3') A = a1*e1+a2*e2+a3*e3 Another option is .. code-block:: python (e1,e2,e3) = MV.setup('e*1|2|3') which produce the same results as the previous method. Note that if we had used .. code-block:: python (e1,e2,e3) = MV.setup('e*x|y|z') then the basis vectors would have been labeled *e_x*, *e_y*, and *e_z*. If *coords* is defined then :func:`MV.setup` returns the tuple .. code-block:: python X = (x,y.z) = symbols('x y z') (ex,ey,ez,grad) = MV.setup('e',coords=X) the basis vectros are again labeled *e_x*, *e_y*, and *e_z* and the additional vector *grad* is returned. *grad* acts as the gradient operator (geometric derivative) so that if :func:`F` is a multivector function of *(x,y,z)* then .. code-block:: python DFl = grad*F DFr = F*grad are the left and right geometric derivatives of :func:`F`. The final parameter in :func:`MV.setup` is *curv* which defines a curvilinear coordinate system. If 3-dimensional spherical coordinates are required we would define - .. code-block:: python X = (r,th,phi) = symbols('r theta phi') curv = [[r*cos(phi)*sin(th),r*sin(phi)*sin(th),r*cos(th)],[1,r,r*sin(th)]] (er,eth,ephi,grad) = MV.setup('e_r e_theta e_phi',metric='[1,1,1]',coords=X,curv=curv) The first component of *curv* is .. code-block:: python [r*cos(phi)*sin(th),r*sin(phi)*sin(th),r*cos(th)] This is the position vector for the spherical coordinate system expressed in terms of the rectangular coordinate components given in terms of the spherical coordinates *r*, *th*, and *phi*. The second component of *curv* is .. code-block:: python [1,r,r*sin(th)] The components of *curv[1]* are the normalizing factors for the basis vectors of the spherical coordinate system that are calculated from the derivatives of *curv[0]* with respect to the coordinates *r*, *th*, and *phi*. In theory the normalizing factors can be calculated from the derivatives of *curv[0]*. In practice one cannot currently specify in sympy that the square of a function is always positive which leads to problems when the normalizing factor is the square root of a squared function. To avoid these problems the normalizing factors are explicitly defined in *curv[1]*. .. note:: In the case of curvlinear coordinates *debug* also prints the connection multivectors. Instantiating a Multivector --------------------------- Now that grades and bases have been described we can show all the ways that a multivector can be instantiated. As an example assume that the multivector space is initialized with .. code-block:: python (e1,e2,e3) = MV.setup('e_1 e_2 e_3') then multivectors could be instantiated with .. code-block:: python (a1,a2,a3) = symbols('a__1 a__2 a__3') x = a1*e1+a2*e2+a3*e3 y = x*e1*e2 z = x|y w = x^y or with the multivector class constructor: .. class:: sympy.galgebra.ga.MV(base=None,mvtype=None,fct=False,blade_rep=True) *base* is a string that defines the name of the multivector for output purposes. *base* and *mvtype* are defined by the following table and *fct* is a switch that will convert the symbolic coefficients of a multivector to functions if coordinate variables have been defined when :func:`MV.setup` is called: .. list-table:: :widths: 20, 30, 65 :header-rows: 1 * - mvtype - base - result * - default - default - Zero multivector * - scalar - string s - symbolic scalar of value Symbol(s) * - vector - string s - symbolic vector * - grade2 or bivector - string s - symbolic bivector * - grade - string s,n - symbolic n-grade multivector * - pseudo - string s - symbolic pseudoscalar * - spinor - string s - symbolic even multivector * - mv - string s - symbolic general multivector * - default - sympy scalar c - zero grade multivector with coefficient c * - default - multivector - copy constructor for multivector If the *base* argument is a string s then the coefficients of the resulting multivector are named as follows: The grade r coefficients consist of the base string, s, followed by a double underscore, __, and an index string of r symbols. If *coords* is defined the index string will consist of coordinate names in a normal order defined by the *coords* tuple. If *coords* is not defined the index string will be integers in normal (ascending) order (for an n dimensional vector space the indices will be 1 to n). The double underscore is used because the latex printer interprets it as a superscript and superscripts in the coefficients will balance subscripts in the bases. For example if If *coords=(x,y,z)* and the base is *A*, the list of all possible coefficients for the most general multivector would be *A*, *A__x*, *A__y*, *A__z*, *A__xy*, *A__xz*, *A__yz*, and *A_xyz*. If the latex printer is used and *e* is the base for the basis vectors then the pseudo scalar would print as :math:`A^{xyz}\boldsymbol{e_{x}\wedge e_{y}\wedge e_{z}}`. If coordinates are not defined it would print as :math:`A^{123}\boldsymbol{e_{1}\wedge e_{2}\wedge e_{3}}`. For printed output all multivectors are represented in terms of products of the basis vectors, either as geometric products or wedge products. This is also true for the output of expressions containing reciprocal basis vectors. If the *fct* argument of :func:`MV` is set to *True* and the *coords* argument in :func:`MV.setup` is defined the symbolic coefficients of the multivector are functions of the coordinates. Basic Multivector Class Functions --------------------------------- .. function:: sympy.galgebra.ga.MV.convert_to_blades(self) Convert multivector from the base representation to the blade representation. If multivector is already in blade representation nothing is done. .. function:: sympy.galgebra.ga.MV.convert_from_blades(self) Convert multivector from the blade representation to the base representation. If multivector is already in base representation nothing is done. .. function:: sympy.galgebra.ga.MV.dd(self, v) For a mutivector function *F* and a vector *v* then *F.dd(v)* is the directional derivate of *F* in the direction *v*, :math:`( v\cdot\nabla ) F`. .. function:: sympy.galgebra.ga.MV.diff(self, var) Calculate derivative of each multivector coefficient with resepect to variable *var* and form new multivector from coefficients. .. function:: sympy.galgebra.ga.MV.dual(self) Return dual of multivector which is multivector left multiplied by pseudoscalar *MV.I* ([Hestenes]_,p22). .. function:: sympy.galgebra.ga.MV.even(self) Return the even grade components of the multivector. .. function:: sympy.galgebra.ga.MV.exp(self, alpha=1, norm=0, mode='T') Return exponential of a blade (if self is not a blade error message is generated). If :math:`A` is the blade then :math:`e^{\alpha A}` is returned where the default *mode*, *'T'*, assumes :math:`AA < 0` so that .. math:: :nowrap: \begin{equation*} e^{\alpha A} = {\cos}\left ( {\alpha\sqrt{-A^{2}}} \right )+{\sin}\left ( {\alpha\sqrt{-A^{2}}} \right ){\displaystyle\frac{A}{\sqrt{-A^{2}}}}. \end{equation*} If the mode is not *'T'* then :math:`AA > 0` is assumed so that .. math:: :nowrap: \begin{equation*} e^{\alpha A} = {\cosh}\left ( {\alpha\sqrt{A^{2}}} \right )+{\sinh}\left ( {\alpha\sqrt{A^{2}}} \right ){\displaystyle\frac{A}{\sqrt{A^{2}}}}. \end{equation*} If :math:`norm = N > 0` then .. math:: :nowrap: \begin{equation*} e^{\alpha A} = {\cos}\left ( {\alpha N} \right )+{\sin}\left ( {\alpha N} \right ){\displaystyle\frac{A}{N}} \end{equation*} or .. math:: :nowrap: \begin{equation*} e^{\alpha A} = {\cosh}\left ( {\alpha N} \right )+{\sinh}\left ( {\alpha N} \right ){\displaystyle\frac{A}{N}} \end{equation*} depending on the value of *mode*. .. function:: sympy.galgebra.ga.MV.expand(self) Return multivector in which each coefficient has been expanded using sympy *expand()* function. .. function:: sympy.galgebra.ga.MV.factor(self) Apply the *sympy* *factor* function to each coefficient of the multivector. .. function:: sympy.galgebra.ga.MV.func(self, fct) Apply the *sympy* scalar function *fct* to each coefficient of the multivector. .. function:: sympy.galgebra.ga.MV.grade(self, igrade=0) Return a multivector that consists of the part of the multivector of grade equal to *igrade*. If the multivector has no *igrade* part return a zero multivector. .. function:: sympy.galgebra.ga.MV.inv(self) Return the inverse of the multivector if *self*sefl.rev()* is a nonzero ctor. .. function:: sympy.galgebra.ga.MV.norm(self) Return the norm of the multvector :math:`M` (*M.norm()*) defined by :math:`\sqrt{MM^{\dagger}}`. If :math:`MM^{\dagger}` is a scalar (a sympy scalar is returned). If :math:`MM^{\dagger}` in not a scalar the program exits with an error message. .. function:: sympy.galgebra.ga.MV.norm(self) Return the square of norm of the multvector :math:`M` (*M.norm2()*) defined by :math:`MM^{\dagger}`. If :math:`MM^{\dagger}` is a scalar (a sympy scalar is returned). If :math:`MM^{\dagger}` in not a scalar the program exits with an error message. .. function:: sympy.galgebra.ga.MV.scalar(self) Return the coefficient (sympy scalar) of the scalar part of a multivector. .. function:: sympy.galgebra.ga.MV.simplify(self) Return multivector where sympy simplify function has been applied to each coefficient of the multivector. .. function:: sympy.galgebra.ga.MV.subs(self, x) Return multivector where sympy subs function has been applied to each coefficient of multivector for argument dictionary/list x. .. function:: sympy.galgebra.ga.MV.rev(self) Return the reverse of the multivector. See section :ref:`reverse`. .. function:: sympy.galgebra.ga.MV.set_coef(self, grade, base, value) Set the multivector coefficient of index *(grade,base)* to *value*. .. function:: sympy.galgebra.ga.MV.trigsimp(self, **kwargs) Apply the *sympy* trignometric simplification fuction *trigsimp* to each coefficient of the multivector. *\*\*kwargs* are the arguments of trigsimp. See *sympy* documentation on *trigsimp* for more information. Basic Multivector Functions --------------------------- .. autofunction:: sympy.galgebra.ga.diagpq .. autofunction:: sympy.galgebra.ga.arbitrary_metric .. autofunction:: sympy.galgebra.ga.arbitrary_metric_conformal .. function:: sympy.galgebra.ga.Com(A, B) Calulate commutator of multivectors *A* and *B*. Returns :math:`(AB-BA)/2`. .. function:: sympy.galgebra.ga.DD(v, f) Calculate directional derivative of multivector function *f* in direction of vector *v*. Returns *f.dd(v)*. .. function:: sympy.galgebra.ga.Format(Fmode=True, Dmode=True, ipy=False) See latex printing. .. function:: sympy.galgebra.precedence.GAeval(s, pstr=False) Returns multivector expression for string *s* with operator precedence for string *s* defined by inputs to function *define_precedence()*. if *pstr=True* *s* and *s* with parenthesis added to enforce operator precedence are printed. .. function:: sympy.galgebra.ga.Nga(x, prec=5) If *x* is a multivector with coefficients that contain floating point numbers, *Nga()* rounds all these numbers to a precision of *prec* and returns the rounded multivector. .. function:: sympy.galgebra.ga.ReciprocalFrame(basis, mode='norm') If *basis* is a list/tuple of vectors, *ReciprocalFrame()* returns a tuple of reciprocal vectors. If *mode=norm* the vectors are normalized. If *mode* is anything other than *norm* the vectors are unnormalized and the normalization coefficient is added to the end of the tuple. One must divide by the coefficient to normalize the vectors. .. function:: sympy.galgebra.ga.ScalarFunction(TheFunction) If *TheFuction* is a real *sympy* fuction a scalar multivector function is returned. .. function:: sympy.galgebra.ga.cross(M1, M2) If *M1* and *M2* are 3-dimensional euclidian vectors the vector cross product is returned, :math:`v_{1}\times v_{2} = -I\left ( {{v_{1}\wedge v_{2}}} \right )`. .. function:: sympy.galgebra.precedence.define_precedence(gd, op_ord='<>|,^,*') This is used with the *GAeval()* fuction to evaluate a string representing a multivector expression with a revised operator precedence. *define_precedence()* redefines the operator precedence for multivectors. *define_precedence()* must be called in the main program an the argument *gd* must be *globals()*. The argument *op_ord* defines the order of operator precedence from high to low with groups of equal precedence separated by commas. the default precedence *op_ord='<>|,^,\*'* is that used by Hestenes ([Hestenes]_,p7,[Doran]_,p38). .. function:: sympy.galgebra.ga.dual(M) Return the dual of the multivector *M*, :math:`MI^{-1}`. .. function:: sympy.galgebra.ga.inv(B) If for the multivector :math:`B`, :math:`BB^{\dagger}` is a nonzero scalar, return :math:`B^{-1} = B^{\dagger}/(BB^{\dagger})`. .. function:: sympy.galgebra.ga.proj(B, A) Project blade A on blade B returning :math:`\left ( {{A\lfloor B}} \right )B^{-1}`. .. function:: sympy.galgebra.ga.refl(B, A) Reflect blade *A* in blade *B*. If *r* is grade of *A* and *s* is grade of *B* returns :math:`(-1)^{s(r+1)}BAB^{-1}`. .. function:: sympy.galgebra.ga.rot(itheta, A) Rotate blade *A* by 2-blade *itheta*. Is is assumed that *itheta\*itheta > 0* so that the rotation is Euclidian and not hyperbolic so that the angle of rotation is *theta = itheta.norm()*. Ther in 3-dimensional Euclidian space. *theta* is the angle of rotation (scalar in radians) and *n* is the vector axis of rotation. Returned is the rotor *cos(theta)+sin(theta)*N* where *N* is the normalized dual of *n*. Multivector Derivatives ----------------------- The various derivatives of a multivector function is accomplished by multiplying the gradient operator vector with the function. The gradiant operation vector is returned by the *MV.setup()* function if coordinates are defined. For example if we have for a 3-D vector space .. code-block:: python X = (x,y,z) = symbols('x y z') (ex,ey,ez,grad) = MV.setup('e*x|y|z',metric='[1,1,1]',coords=X) Then the gradient operator vector is *grad* (actually the user can give it any name he wants to). Then the derivatives of the multivector function *F* are given by .. code-block:: python F = MV('F','mv',fct=True) .. math:: :nowrap: \begin{align*} \nabla F &= grad*F \\ F \nabla &= F*grad \\ \nabla \wedge F &= grad \wedge F \\ F \wedge \nabla &= F \wedge grad \\ \nabla \cdot F &= grad|F \\ F \cdot \nabla F &= F|grad \\ \nabla \lfloor F &= grad < F \\ F \lfloor \nabla &= F < grad \\ \nabla \rfloor F &= grad > F \\ F \rfloor \nabla &= F > grad \end{align*} The preceding code block gives examples of all possible multivector derivatives of the multivector function *F* where \* give the left and right geometric derivatives, ^ gives the left and right exterior (curl) derivatives, | gives the left and right interior (div) derivatives, < give the left and right derivatives for the left contraction, and > give the left and right derivatives for the right contraction. To understand the left and right derivatives see a reference on geometric calculus ([Doran]_,chapter6). If one is taking the derivative of a complex expression that expression should be in parenthesis. Additionally, whether or not one is taking the derivative of a complex expression the *grad* vector and the expression it is operating on should always be in parenthesis unless the grad operator and the expression it is operating on are the only objects in the expression. Vector Manifolds ---------------- In addtition to the *galgebra* module there is a *manifold* module that allows for the definition of a geometric algebra and calculus on a vector manifold. The vector mainfold is defined by a vector function of some coordinates in an embedding vector space ([Doran]_,p202,[Hestenes]_,p139). For example the unit 2-sphere would be the collection of vectors on the unit shpere in 3-dimensions with possible coordinates of :math:`\theta` and :math:`\phi` the angles of elevation and azimuth. A vector function :math:`{X}\left ( {\theta,\phi} \right )` that defines the manifold would be given by .. math:: :nowrap: \begin{equation*} {X}\left ( {\theta,\phi} \right ) = {\cos}\left ( {\theta} \right )\boldsymbol{e_{z}}+{\cos}\left ( {\theta} \right )\left ( {{{\cos}\left ( {\phi} \right )\boldsymbol{e_{x}} +{\sin}\left ( {\phi} \right )\boldsymbol{e_{y}}}} \right ) \end{equation*} The module *manifold.py* is transitionary in that all calculation are performed in the embedding vector space (geometric algebra). Thus due to the limitations on *sympy*'s *simplify()* and *trigsimp()*, simple expressions may appear to be very complicated since they are expressed in terms of the basis vectors (bases/blades) of the embedding space and not in terms of the vector space (geometric algebra) formed from the manifold's basis vectors. A future implementation of *Manifold.py* will correct this difficiency. The member functions of the vector manifold follow. .. function:: Manifold(x, coords, debug=False, I=None) Initializer for vector manifold where *x* is the vector function of the *coords* that defines the manifold and *coords* is the list/tuple of sympy symbols that are the coordinates. The basis vectors of the manifold as a fuction of the coordinates are returned as a tuple. *I* is the pseudo scalar for the manifold. The default is for the initializer to calculate *I*, however for complicated *x* functions (especially where trigonometric functions of the coordinates are involved) it is sometimes a good idea to calculate *I* separately and input it to *Manifold()*. .. function:: Basis(self) Return the basis vectors of the manifold as a tuple. .. function:: DD(self, v, F, opstr=False) Return the manifold directional derivative of a multivector function *F* defined on the manifold in the vector direction *v*. .. function:: Grad(self, F) Return the manifold multivector derivative of the multivector function *F* defined on the manifold. .. function:: Proj(self, F) Return the projection of the multivector *F* onto the manifold tangent space. An example of a simple vector manifold is shown below which demonstrates the instanciation of a manifold, the defining of vector and scalar functions on the manifold and the calculation of the geometric derivative of those functions. .. image:: manifold_testlatex.png Standard Printing ----------------- Printing of multivectors is handled by the module *galgebra/printing* which contains a string printer class, *GA_Printer* derived from the sympy string printer class and a latex printer class, *GA_LatexPrinter*, derived from the sympy latex printer class. Additionally, there is an *enhanced_print* class that enhances the console output of sympy to make the printed output multivectors, functions, and derivatives more readable. *enhanced_print* requires an ansi console such as is supplied in linux or the program *ansicon* (github.com/adoxa/ansicon) for windows which replaces *cmd.exe*. For a windows user the simplest way to implement ansicon is to use the *geany* editor and in the Edit->Preferences->Tools menu replace *cmd.exe* with *ansicon.exe* (be sure to supply the path to *ansicon*). If *enhanced_print* is called in a program (linux) when multivectors are printed the basis blades or bases are printed in bold text, functions are printed in red, and derivative operators in green. For formatting the multivector output there is the member function .. function:: sympy.galgebra.ga.Fmt(self, fmt=1, title=None) *Fmt* is used to control how the multivector is printed with the argument *fmt*. If *fmt=1* the entire multivector is printed on one line. If *fmt=2* each grade of the multivector is printed on one line. If *fmt=3* each component (base) of the multivector is printed on one line. If a *title* is given then *title = multivector* is printed. If the usual print command is used the entire multivector is printed on one line. .. function:: sympy.galgebra.ga.ga_print_on() Redirects printer output from standard *sympy* print handler. Needed if one wishes to use compact forms of *function* and *derivative* output strings for interactive use. .. function:: sympy.galgebra.ga.ga_print_off() Restores standard *sympy* print handler. .. function:: sympy.galgebra.printing.GA_Printer() Context handler for use inside Sympy code. The code indented under the *with GA_Printer():* statement will run with the compact forms of *function* and *derivative* output strings, and have the printing restored to standard *sympy* printing after it has finished, even if it is aborted with an exception. Latex Printing -------------- For latex printing one uses one functions from the *galgebra* module and one function from the *galgebra/printing* module. The functions are .. function:: sympy.galgebra.printing.Format(Fmode=True, Dmode=True, ipy=False) This function from the *galgebra* module turns on latex printing with the following options .. list-table:: :widths: 15, 15, 55 :header-rows: 1 * - argument - value - result * - *Fmode* - *True* - Print functions without argument list, :math:`f` * - - *False* - Print functions with standard sympy latex formatting, :math:`f(x,y,z)` * - *Dmode* - *True* - Print partial derivatives with condensed notatation, :math:`\partial_{x}f` * - - *False* - Print partial derivatives with standard sympy latex formatting :math:`\frac{\partial f}{\partial x}` * - *ipy* - *False* - Redirect print output to file for post-processing by latex * - - *True* - Do not redirect print output (This is used for Ipython with MathJax) .. function:: sympy.galgebra.printing.xdvi(filename=None, pdf='', debug=False, paper=(14, 11)) This function from the *galgebra/printing* module post-processes the output captured from print statements. Write the resulting latex strings to the file *filename*, processes the file with pdflatex, and displays the resulting pdf file. *pdf* is the name of the pdf viewer on your computer. If you are running *ubuntu* the *evince* viewer is automatically used. On other operating systems if *pdf = ''* the name of the pdf file is executed. If the pdf file type is associated with a viewer this will launch the viewer with the associated file. All latex files except the pdf file are deleted. If *debug = True* the file *filename* is printed to standard output for debugging purposes and *filename* (the tex file) is saved. If *filename* is not entered the default filename is the root name of the python program being executed with *.tex* appended. The format for the *paper* is .. list-table:: :widths: 25, 65 :header-rows: 0 * - *paper=(w,h)* - *w* is paper width in inches and,*h* is paper height in inches * - *paper='letter'* - paper is standard leter size :math:`8.5\mbox{ in}\times 11\mbox{ in}` The default of *paper=(14,11)* was chosen so that long multivector expressions would not be truncated on the display. The **xdvi** function requires that latex and a pdf viewer be installed on the computer. As an example of using the latex printing options when the following code is executed .. code-block:: python from sympy.galgebra.printing import xdvi from sympy.galgebra.ga import * Format() (ex,ey,ez) = MV.setup('e*x|y|z') A = MV('A','mv') print r'\bm{A} =',A A.Fmt(2,r'\bm{A}') A.Fmt(3,r'\bm{A}') xdvi() The following is displayed .. math:: :nowrap: \begin{align*} \boldsymbol{A} = & A+A^{x}\boldsymbol{e_{x}}+A^{y}\boldsymbol{e_{y}}+A^{z}\boldsymbol{e_{z}}+A^{xy}\boldsymbol{e_{x}\wedge e_{y}}+A^{xz}\boldsymbol{e_{x}\wedge e_{z}}+A^{yz}\boldsymbol{e_{y}\wedge e_{z}}+A^{xyz}\boldsymbol{e_{x}\wedge e_{y}\wedge e_{z}} \\ \boldsymbol{A} = & A \\ & +A^{x}\boldsymbol{e_{x}}+A^{y}\boldsymbol{e_{y}}+A^{z}\boldsymbol{e_{z}} \\ & +A^{xy}\boldsymbol{e_{x}\wedge e_{y}}+A^{xz}\boldsymbol{e_{x}\wedge e_{z}}+A^{yz}\boldsymbol{e_{y}\wedge e_{z}} \\ & +A^{xyz}\boldsymbol{e_{x}\wedge e_{y}\wedge e_{z}} \\ \boldsymbol{A} = & A \\ & +A^{x}\boldsymbol{e_{x}} \\ & +A^{y}\boldsymbol{e_{y}} \\ & +A^{z}\boldsymbol{e_{z}} \\ & +A^{xy}\boldsymbol{e_{x}\wedge e_{y}} \\ & +A^{xz}\boldsymbol{e_{x}\wedge e_{z}} \\ & +A^{yz}\boldsymbol{e_{y}\wedge e_{z}} \\ & +A^{xyz}\boldsymbol{e_{x}\wedge e_{y}\wedge e_{z}} \end{align*} For the cases of derivatives the code is .. code-block:: python from sympy.galgebra.printing import xdvi from sympy.galgebra.ga import * Format() X = (x,y,z) = symbols('x y z') (ex,ey,ez,grad) = MV.setup('e_x e_y e_z',metric='[1,1,1]',coords=X) f = MV('f','scalar',fct=True) A = MV('A','vector',fct=True) B = MV('B','grade2',fct=True) print r'\bm{A} =',A print r'\bm{B} =',B print 'grad*f =',grad*f print r'grad|\bm{A} =',grad|A print r'grad*\bm{A} =',grad*A print r'-I*(grad^\bm{A}) =',-MV.I*(grad^A) print r'grad*\bm{B} =',grad*B print r'grad^\bm{B} =',grad^B print r'grad|\bm{B} =',grad|B xdvi() and the latex displayed output is (:math:`f` is a scalar function) .. math:: :nowrap: \begin{align*} \boldsymbol{A} =& A^{x}\boldsymbol{e_{x}}+A^{y}\boldsymbol{e_{y}}+A^{z}\boldsymbol{e_{z}} \\ \boldsymbol{B} =& B^{xy}\boldsymbol{e_{x}\wedge e_{y}}+B^{xz}\boldsymbol{e_{x}\wedge e_{z}}+B^{yz}\boldsymbol{e_{y}\wedge e_{z}} \\ \boldsymbol{\nabla} f =& \partial_{x} f\boldsymbol{e_{x}}+\partial_{y} f\boldsymbol{e_{y}}+\partial_{z} f\boldsymbol{e_{z}} \\ \boldsymbol{\nabla} \cdot \boldsymbol{A} = &\partial_{x} A^{x} + \partial_{y} A^{y} + \partial_{z} A^{z} \\ \boldsymbol{\nabla} \boldsymbol{A} = &\partial_{x} A^{x} + \partial_{y} A^{y} + \partial_{z} A^{z} +\left ( - \partial_{y} A^{x} + \partial_{x} A^{y}\right ) \boldsymbol{e_{x}\wedge e_{y}} +\left ( - \partial_{z} A^{x} + \partial_{x} A^{z}\right ) \boldsymbol{e_{x}\wedge e_{z}} \\ &+\left ( - \partial_{z} A^{y} + \partial_{y} A^{z}\right ) \boldsymbol{e_{y}\wedge e_{z}} \\ -I (\boldsymbol{\nabla} \wedge \boldsymbol{A}) = &\left ( - \partial_{z} A^{y} + \partial_{y} A^{z}\right ) \boldsymbol{e_{x}} +\left ( \partial_{z} A^{x} - \partial_{x} A^{z}\right ) \boldsymbol{e_{y}} +\left ( - \partial_{y} A^{x} + \partial_{x} A^{y}\right ) \boldsymbol{e_{z}} \\ \boldsymbol{\nabla} \boldsymbol{B} = &\left ( - \partial_{y} B^{xy} - \partial_{z} B^{xz}\right ) \boldsymbol{e_{x}} +\left ( \partial_{x} B^{xy} - \partial_{z} B^{yz}\right ) \boldsymbol{e_{y}} +\left ( \partial_{x} B^{xz} + \partial_{y} B^{yz}\right ) \boldsymbol{e_{z}} \\ &+\left ( \partial_{z} B^{xy} - \partial_{y} B^{xz} + \partial_{x} B^{yz}\right ) \boldsymbol{e_{x}\wedge e_{y}\wedge e_{z}} \\ \boldsymbol{\nabla} \wedge \boldsymbol{B} = &\left ( \partial_{z} B^{xy} - \partial_{y} B^{xz} + \partial_{x} B^{yz}\right ) \boldsymbol{e_{x}\wedge e_{y}\wedge e_{z}} \\ \boldsymbol{\nabla} \cdot \boldsymbol{B} = &\left ( - \partial_{y} B^{xy} - \partial_{z} B^{xz}\right ) \boldsymbol{e_{x}}+\left ( \partial_{x} B^{xy} - \partial_{z} B^{yz}\right ) \boldsymbol{e_{y}}+\left ( \partial_{x} B^{xz} + \partial_{y} B^{yz}\right ) \boldsymbol{e_{z}} \end{align*} This example also demonstrates several other features of the latex printer. In the case that strings are input into the latex printer such as ``r'grad*\boldsymbol{A}'``, ``r'grad^\boldsymbol{A}'``, or ``r'grad*\boldsymbol{A}'``. The text symbols *grad*, *^*, *|*, and *\ ** are mapped by the *xdvi()* post-processor as follows if the string contains an *=*. .. csv-table:: :header: original , replacement , displayed latex :widths: 15, 15, 15 ``grad*A`` , ``\boldsymbol{\nabla}A`` , :math:`\boldsymbol{\nabla}A` ``A^B`` , ``A\wedge B`` , :math:`A\wedge B` ``A|B`` , ``A\cdot B`` , :math:`A\cdot B` ``A*B`` , ``AB`` , :math:`AB` ``AB`` , ``A\rfloor B`` , :math:`A\rfloor B` If the string to be printed contains a *\%* none of the above substitutions are made before the latex processor is applied. In general for the latex printer strings are assumed to be in a math environment (*equation\ ** or *align\ **) unless the string contains a *\#*. .. note:: Except where noted the conventions for latex printing follow those of the latex printing module of sympy. This includes translating sympy variables with Greek name (such as ``alpha``) to the equivalent Greek symbol (:math:`\alpha`) for the purpose of latex printing. Also a single underscore in the variable name (such as ``X_j``) indicates a subscript (:math:`X_{j}`), and a double underscore (such as ``X__k``) a superscript (:math:`X^{k}`). The only other change with regard to the sympy latex printer is that matrices are printed full size (equation displaystyle). Printer Redirection ------------------- In order to print transparently, that is to simply use the *print* statement with both text and LaTeX printing the printer output is redirected. In the case of text printing the reason for redirecting the printer output is because the *sympy* printing functions *_print_Derivative* and *_print_Function* are redefined to make the output more compact. If one does not wish to use the compact notation redirection is not required for the text printer. If one wishes to use the redefined *_print_Derivative* and *_print_Function* the printer should be redirected with the function *ga_print_on()* and restored with the function *ga_print_off()*. Both functions can be imported from *sympy.galgebra.ga* (see *examples/galgebra/terminal_check.py* for usage). SymPy provides a context handler that will allow you to rewrite any *ga_print_on(); "do something"; ga_print_off();* sequence like this: .. code-block:: python with GA_printer: "do something" This has the advantage that even in the case of an exception inside "do something", the *ga_print_off();* call will be made. For LaTeX printing the *Format()* (import from *sympy.galgebra.ga*) redirects the printer output to a string. After all printing requests one must call the function *xdvi()* (import from *sympy.galgebra.printing*) tp process the string to a LaTeX format, compile with pdflatex, and displayed the resulting pdf file. The function *xdvi()* also restores the printer output to normal for standard *sympy* printing. If *Format(ipy=True)* is used there is no printer redirection and the LaTeX output is simply sent to *sys.stdout* for use in *Ipython* (*Ipython* LaTeX interface for *galgebra* not yet implemented). Other Printing Functions ------------------------ These functions are used together if one wishes to print both code and output in a single file. They work for text printing and for latex printing. For these functions to work properly the last function defined must not contain a *Print_Function()* call (the last function defined is usually a *dummy()* function that does nothing). Additionally, to work properly none of the functions containing *Print_Function()* can contain function definintions (local functions). .. function:: sympy.galgebra.printing.Get_Program(off=False) Tells program to print both code and output from functions that have been properly tagged with *Print_Function()*. *Get_Program()* must be in main program before the functions that you wish code printing from are executed. the *off* argument in *Get_Program()* allows one to turn off the printing of the code by changing one line in the entire program (*off=True*). .. function:: sympy.galgebra.printing.Print_Function() *Print_Function()* is included in those functions where one wishes to print the code block along with (before) the usual printed output. The *Print_Function()* statement should be included immediately after the function def statement. For proper usage of both *Print_Function()* and *Get_Program()* see the following example. As an example consider the following code .. code-block:: python from sympy.galgebra.printing import xdvi,Get_Program,Print_Function from sympy.galgebra.ga import * Format() def basic_multivector_operations_3D(): Print_Function() (ex,ey,ez) = MV.setup('e*x|y|z') A = MV('A','mv') A.Fmt(1,'A') A.Fmt(2,'A') A.Fmt(3,'A') A.even().Fmt(1,'%A_{+}') A.odd().Fmt(1,'%A_{-}') X = MV('X','vector') Y = MV('Y','vector') print 'g_{ij} =',MV.metric X.Fmt(1,'X') Y.Fmt(1,'Y') (X*Y).Fmt(2,'X*Y') (X^Y).Fmt(2,'X^Y') (X|Y).Fmt(2,'X|Y') return def basic_multivector_operations_2D(): Print_Function() (ex,ey) = MV.setup('e*x|y') print 'g_{ij} =',MV.metric X = MV('X','vector') A = MV('A','spinor') X.Fmt(1,'X') A.Fmt(1,'A') (X|A).Fmt(2,'X|A') (XX).Fmt(2,'A>X') return def dummy(): return Get_Program() basic_multivector_operations_3D() basic_multivector_operations_2D() xdvi() The latex output of the code is .. image:: simple_test_latex_1.png | .. image:: simple_test_latex_2.png Examples ======== Algebra ------- BAC-CAB Formulas ++++++++++++++++ This example demonstrates the most general metric tensor .. math:: :nowrap: \begin{equation*} g_{ij} = \left [ \begin{array}{cccc} \left ( a\cdot a\right ) & \left ( a\cdot b\right ) & \left ( a\cdot c\right ) & \left ( a\cdot d\right ) \\ \left ( a\cdot b\right ) & \left ( b\cdot b\right ) & \left ( b\cdot c\right ) & \left ( b\cdot d\right ) \\ \left ( a\cdot c\right ) & \left ( b\cdot c\right ) & \left ( c\cdot c\right ) & \left ( c\cdot d\right ) \\ \left ( a\cdot d\right ) & \left ( b\cdot d\right ) & \left ( c\cdot d\right ) & \left ( d\cdot d\right ) \end{array}\right ] \end{equation*} and how the *galgebra* module can be used to verify and expand geometric algebra identities consisting of relations between the abstract vectors :math:`a`, :math:`b`, :math:`c`, and :math:`d`. .. code-block:: python from sympy.galgebra.printing import xdvi from sympy.galgebra.ga import * Format() (a,b,c,d) = MV.setup('a b c d') print '\\bm{a|(b*c)} =',a|(b*c) print '\\bm{a|(b^c)} =',a|(b^c) print '\\bm{a|(b^c^d)} =',a|(b^c^d) print '\\bm{a|(b^c)+c|(a^b)+b|(c^a)} =',(a|(b^c))+(c|(a^b))+(b|(c^a)) print '\\bm{a*(b^c)-b*(a^c)+c*(a^b)} =',a*(b^c)-b*(a^c)+c*(a^b) print '\\bm{a*(b^c^d)-b*(a^c^d)+c*(a^b^d)-d*(a^b^c)} =',a*(b^c^d)-b*(a^c^d)+c*(a^b^d)-d*(a^b^c) print '\\bm{(a^b)|(c^d)} =',(a^b)|(c^d) print '\\bm{((a^b)|c)|d} =',((a^b)|c)|d print '\\bm{(a^b)\\times (c^d)} =',Com(a^b,c^d) xdvi() The preceeding code block also demonstrates the mapping of *\ **, *^*, and *|* to appropriate latex symbols. .. note:: The :math:`\times` symbol is the commutator product of two multivectors, :math:`A\times B = (AB-BA)/2`. .. math:: :nowrap: \begin{align*} \boldsymbol{a\cdot (b c)} =& - \left ( a\cdot c\right ) \boldsymbol{b}+\left ( a\cdot b\right ) \boldsymbol{c} \\ \boldsymbol{a\cdot (b\wedge c)} =& - \left ( a\cdot c\right ) \boldsymbol{b}+\left ( a\cdot b\right ) \boldsymbol{c} \\ \boldsymbol{a\cdot (b\wedge c\wedge d)} =& \left ( a\cdot d\right ) \boldsymbol{b\wedge c}- \left ( a\cdot c\right ) \boldsymbol{b\wedge d}+\left ( a\cdot b\right ) \boldsymbol{c\wedge d} \\ \boldsymbol{a\cdot (b\wedge c)+c\cdot (a\wedge b)+b\cdot (c\wedge a)} =& 0 \\ \boldsymbol{a (b\wedge c)-b (a\wedge c)+c (a\wedge b)} =& 3\boldsymbol{a\wedge b\wedge c} \\ \boldsymbol{a (b\wedge c\wedge d)-b (a\wedge c\wedge d)+c (a\wedge b\wedge d)-d (a\wedge b\wedge c)} =& 4\boldsymbol{a\wedge b\wedge c\wedge d} \\ \boldsymbol{(a\wedge b)\cdot (c\wedge d)} =& - \left ( a\cdot c\right ) \left ( b\cdot d\right ) + \left ( a\cdot d\right ) \left ( b\cdot c\right ) \\ \boldsymbol{((a\wedge b)\cdot c)\cdot d} =& - \left ( a\cdot c\right ) \left ( b\cdot d\right ) + \left ( a\cdot d\right ) \left ( b\cdot c\right ) \\ \boldsymbol{(a\wedge b)\times (c\wedge d)} =& - \left ( b\cdot d\right ) \boldsymbol{a\wedge c}+\left ( b\cdot c\right ) \boldsymbol{a\wedge d}+\left ( a\cdot d\right ) \boldsymbol{b\wedge c}- \left ( a\cdot c\right ) \boldsymbol{b\wedge d} \end{align*} Reciprocal Frame ++++++++++++++++ The reciprocal frame of vectors with respect to the basis vectors is required for the evaluation of the geometric dervative. The following example demonstrates that for the case of an arbitrary 3-dimensional Euclidian basis the reciprocal basis vectors are correctly calculated. .. code-block:: python from sympy.galgebra.printing import xdvi from sympy.galgebra.ga import * Format() metric = '1 # #,'+ \ '# 1 #,'+ \ '# # 1,' (e1,e2,e3) = MV.setup('e1 e2 e3',metric) E = e1^e2^e3 Esq = (E*E).scalar() print 'E =',E print '%E^{2} =',Esq Esq_inv = 1/Esq E1 = (e2^e3)*E E2 = (-1)*(e1^e3)*E E3 = (e1^e2)*E print 'E1 = (e2^e3)*E =',E1 print 'E2 =-(e1^e3)*E =',E2 print 'E3 = (e1^e2)*E =',E3 print 'E1|e2 =',(E1|e2).expand() print 'E1|e3 =',(E1|e3).expand() print 'E2|e1 =',(E2|e1).expand() print 'E2|e3 =',(E2|e3).expand() print 'E3|e1 =',(E3|e1).expand() print 'E3|e2 =',(E3|e2).expand() w = ((E1|e1).expand()).scalar() Esq = expand(Esq) print '%(E1\\cdot e1)/E^{2} =',simplify(w/Esq) w = ((E2|e2).expand()).scalar() print '%(E2\\cdot e2)/E^{2} =',simplify(w/Esq) w = ((E3|e3).expand()).scalar() print '%(E3\\cdot e3)/E^{2} =',simplify(w/Esq) xdvi() The preceeding code also demonstrated the use of the *\%* directive in printing a string so that *^* is treated literally and not translated to *\\wedge*. Note that ``'%E^{2} ='`` is printed as :math:`E^{2} =` and not as :math:`E\wedge {2} =`. .. math:: :nowrap: \begin{align*} E =& \boldsymbol{e_{1}\wedge e_{2}\wedge e_{3}} \\ E^{2} =& \left ( e_{1}\cdot e_{2}\right ) ^{2} - 2 \left ( e_{1}\cdot e_{2}\right ) \left ( e_{1}\cdot e_{3}\right ) \left ( e_{2}\cdot e_{3}\right ) + \left ( e_{1}\cdot e_{3}\right ) ^{2} + \left ( e_{2}\cdot e_{3}\right ) ^{2} -1 \\ E1 =& (e2\wedge e3) E = \left ( \left ( e_{2}\cdot e_{3}\right ) ^{2} -1\right ) \boldsymbol{e_{1}}+\left ( \left ( e_{1}\cdot e_{2}\right ) - \left ( e_{1}\cdot e_{3}\right ) \left ( e_{2}\cdot e_{3}\right ) \right ) \boldsymbol{e_{2}}+\left ( - \left ( e_{1}\cdot e_{2}\right ) \left ( e_{2}\cdot e_{3}\right ) + \left ( e_{1}\cdot e_{3}\right ) \right ) \boldsymbol{e_{3}} \\ E2 =& -(e1\wedge e3) E = \left ( \left ( e_{1}\cdot e_{2}\right ) - \left ( e_{1}\cdot e_{3}\right ) \left ( e_{2}\cdot e_{3}\right ) \right ) \boldsymbol{e_{1}}+\left ( \left ( e_{1}\cdot e_{3}\right ) ^{2} -1\right ) \boldsymbol{e_{2}}+\left ( - \left ( e_{1}\cdot e_{2}\right ) \left ( e_{1}\cdot e_{3}\right ) + \left ( e_{2}\cdot e_{3}\right ) \right ) \boldsymbol{e_{3}} \\ E3 =& (e1\wedge e2) E = \left ( - \left ( e_{1}\cdot e_{2}\right ) \left ( e_{2}\cdot e_{3}\right ) + \left ( e_{1}\cdot e_{3}\right ) \right ) \boldsymbol{e_{1}}+\left ( - \left ( e_{1}\cdot e_{2}\right ) \left ( e_{1}\cdot e_{3}\right ) + \left ( e_{2}\cdot e_{3}\right ) \right ) \boldsymbol{e_{2}}+\left ( \left ( e_{1}\cdot e_{2}\right ) ^{2} -1\right ) \boldsymbol{e_{3}} \\ E1\cdot e2 =& 0 \\ E1\cdot e3 =& 0 \\ E2\cdot e1 =& 0 \\ E2\cdot e3 =& 0 \\ E3\cdot e1 =& 0 \\ E3\cdot e2 =& 0 \\ (E1\cdot e1)/E^{2} =& 1 \\ (E2\cdot e2)/E^{2} =& 1 \\ (E3\cdot e3)/E^{2} =& 1 \end{align*} The formulas derived for :math:`E1`, :math:`E2`, :math:`E3`, and :math:`E^{2}` could also be applied to the numerical calculations of crystal properties. Lorentz-Transformation ++++++++++++++++++++++ A simple physics demonstation of geometric algebra is the derivation of the Lorentz-Transformation. In this demonstration a 2-dimensional Minkowski space is defined and the Lorentz-Transformation is generated from a rotation of a vector in the Minkowski space using the rotor :math:`R`. .. code-block:: python from sympy import symbols,sinh,cosh from sympy.galgebra.printing import xdvi from sympy.galgebra.ga import * Format() (alpha,beta,gamma) = symbols('alpha beta gamma') (x,t,xp,tp) = symbols("x t x' t'") (g0,g1) = MV.setup('gamma*t|x',metric='[1,-1]') R = cosh(alpha/2)+sinh(alpha/2)*(g0^g1) X = t*g0+x*g1 Xp = tp*g0+xp*g1 print 'R =',R print r"#%t\bm{\gamma_{t}}+x\bm{\gamma_{x}} = t'\bm{\gamma'_{t}}+x'\bm{\gamma'_{x}} "+ r"= R\left ( t'\bm{\gamma_{t}}+x'\bm{\gamma_{x}}\right ) R^{\dagger}" Xpp = R*Xp*R.rev() Xpp = Xpp.collect([xp,tp]) Xpp = Xpp.subs({2*sinh(alpha/2)*cosh(alpha/2):sinh(alpha),\ sinh(alpha/2)**2+cosh(alpha/2)**2:cosh(alpha)}) print r"%t\bm{\gamma_{t}}+x\bm{\gamma_{x}} =",Xpp Xpp = Xpp.subs({sinh(alpha):gamma*beta,cosh(alpha):gamma}) print r'%\f{\sinh}{\alpha} = \gamma\beta' print r'%\f{\cosh}{\alpha} = \gamma' print r"%t\bm{\gamma_{t}}+x\bm{\gamma_{x}} =",Xpp.collect(gamma) xdvi() The preceeding code also demonstrates how to use the sympy *subs* functions to perform the hyperbolic half angle transformation. The code also shows the use of both the *#* and *\%* directives in the text string ``r"#%t\bm{\gamma_{t}}+x\bm{\gamma_{x}} = t'\bm{\gamma'_{t}}+x'\bm{\gamma'_{x}} = R\left ( t'\bm{\gamma_{t}}+x'\bm{\gamma_{x}}\right ) R^{\dagger}"``. Both the *#* and *\%* are needed in this text string for two reasons. First, the text string contains an *=* sign. The latex preprocessor uses this a key to combine the text string with a sympy expression to be printed after the text string. The *#* is required to inform the preprocessor that there is no sympy expression to follow. Second, the *\%* is requires to inform the preprocessor that the text string is to be displayed in latex math mode and not in text mode (if *#* is present the default latex mode is text mode unless overridden by the *\%* directive). .. math:: :nowrap: \begin{align*} R =& \cosh{\left (\frac{1}{2} \alpha \right )}+\sinh{\left (\frac{1}{2} \alpha \right )}\boldsymbol{\gamma_{t}\wedge \gamma_{x}} \\ t\boldsymbol{\gamma_{t}}+x\boldsymbol{\gamma_{x}} =& t'\boldsymbol{\gamma'_{t}}+x'\boldsymbol{\gamma'_{x}} = R\left ( t'\boldsymbol{\gamma_{t}}+x'\boldsymbol{\gamma_{x}}\right ) R^{\dagger} \\ t\boldsymbol{\gamma_{t}}+x\boldsymbol{\gamma_{x}} =& \left ( t' \cosh{\left (\alpha \right )} - x' \sinh{\left (\alpha \right )}\right ) \boldsymbol{\gamma_{t}}+\left ( - t' \sinh{\left (\alpha \right )} + x' \cosh{\left (\alpha \right )}\right ) \boldsymbol{\gamma_{x}} \\ {\sinh}\left ( {\alpha} \right ) =& \gamma\beta \\ {\cosh}\left ( {\alpha} \right ) =& \gamma \\ t\boldsymbol{\gamma_{t}}+x\boldsymbol{\gamma_{x}} =& \left ( \gamma \left(- \beta x' + t'\right)\right ) \boldsymbol{\gamma_{t}}+\left ( \gamma \left(- \beta t' + x'\right)\right ) \boldsymbol{\gamma_{x}} \end{align*} Calculus -------- Derivatives in Spherical Coordinates ++++++++++++++++++++++++++++++++++++ The following code shows how to use *galgebra* to use spherical coordinates. The gradient of a scalar function, :math:`f`, the divergence and curl of a vector function, :math:`A`, and the exterior derivative (curl) of a bivector function, :math:`B` are calculated. Note that to get the standard curl of a 3-dimension function the result is multiplied by :math:`-I` the negative of the pseudoscalar. .. note:: In geometric calculus the operator :math:`\nabla^{2}` is well defined on its own as the geometic derivative of the geometric derivative. However, if needed we have for the vector function :math:`A` the relations (since :math:`\nabla\cdot A` is a scalar it's curl is equal to it's geometric derivative and it's divergence is zero) - .. math:: :nowrap: \begin{align*} \nabla A =& \nabla\wedge A + \nabla\cdot A \\ \nabla^{2} A =& \nabla\left ( {{\nabla\wedge A}} \right ) + \nabla\left ( {{\nabla\cdot A}} \right ) \\ \nabla^{2} A =& \nabla\wedge\left ( {{\nabla\wedge A}} \right ) + \nabla\cdot\left ( {{\nabla\wedge A}} \right ) +\nabla\wedge\left ( {{\nabla\cdot A}} \right ) + \nabla\cdot\left ( {{\nabla\cdot A}} \right ) \\ \nabla^{2} A =& \nabla\wedge\left ( {{\nabla\wedge A}} \right ) + \left ( {{\nabla\cdot\nabla}} \right ) A - \nabla\left ( {{\nabla\cdot A}} \right ) + \nabla\left ( {{\nabla\cdot A}} \right ) \\ \nabla^{2} A =& \nabla\wedge\nabla\wedge A + \left ( {{\nabla\cdot\nabla}} \right )A \end{align*} In the derivation we have used that :math:`\nabla\cdot\left ( {{\nabla\wedge A}} \right ) = \left ( {{\nabla\cdot\nabla}} \right )A - \nabla\left ( {{\nabla\cdot A}} \right )` which is implicit in the second *BAC-CAB* formula. No parenthesis is needed for the geometric curl of the curl (exterior derivative of exterior derivative) since the :math:`\wedge` operation is associative unlike the vector curl operator and :math:`\nabla\cdot\nabla` is the usual Laplacian operator. .. code-block:: python from sympy import sin,cos from sympy.galgebra.printing import xdvi from sympy.galgebra.ga import * Format() X = (r,th,phi) = symbols('r theta phi') curv = [[r*cos(phi)*sin(th),r*sin(phi)*sin(th),r*cos(th)],[1,r,r*sin(th)]] (er,eth,ephi,grad) = MV.setup('e_r e_theta e_phi',metric='[1,1,1]',coords=X,curv=curv) f = MV('f','scalar',fct=True) A = MV('A','vector',fct=True) B = MV('B','grade2',fct=True) print 'A =',A print 'B =',B print 'grad*f =',grad*f print 'grad|A =',grad|A print '-I*(grad^A) =',-MV.I*(grad^A) print 'grad^B =',grad^B xdvi() Results of code .. math:: :nowrap: \begin{align*} A =& A^{r}\boldsymbol{e_{r}}+A^{\theta}\boldsymbol{e_{\theta}}+A^{\phi}\boldsymbol{e_{\phi}} \\ B =& B^{r\theta}\boldsymbol{e_{r}\wedge e_{\theta}}+B^{r\phi}\boldsymbol{e_{r}\wedge e_{\phi}}+B^{\theta\phi}\boldsymbol{e_{\theta}\wedge e_{\phi}} \\ \boldsymbol{\nabla} f =& \partial_{r} f\boldsymbol{e_{r}}+\frac{\partial_{\theta} f}{r}\boldsymbol{e_{\theta}}+\frac{\partial_{\phi} f}{r \sin{\left (\theta \right )}}\boldsymbol{e_{\phi}} \\ \boldsymbol{\nabla} \cdot A =& \partial_{r} A^{r} + \frac{A^{\theta}}{r \tan{\left (\theta \right )}} + 2 \frac{A^{r}}{r} + \frac{\partial_{\theta} A^{\theta}}{r} + \frac{\partial_{\phi} A^{\phi}}{r \sin{\left (\theta \right )}} \\ -I (\boldsymbol{\nabla} \wedge A) =& \left ( \frac{A^{\phi} \cos{\left (\theta \right )} + \sin{\left (\theta \right )} \partial_{\theta} A^{\phi} - \partial_{\phi} A^{\theta}}{r \sin{\left (\theta \right )}}\right ) \boldsymbol{e_{r}}+\left ( - \partial_{r} A^{\phi} - \frac{A^{\phi}}{r} + \frac{\partial_{\phi} A^{r}}{r \sin{\left (\theta \right )}}\right ) \boldsymbol{e_{\theta}}+\left ( \frac{r \partial_{r} A^{\theta} + A^{\theta} - \partial_{\theta} A^{r}}{r}\right ) \boldsymbol{e_{\phi}} \\ \boldsymbol{\nabla} \wedge B =& \left ( \partial_{r} B^{\theta\phi} + 2 \frac{B^{\theta\phi}}{r} - \frac{B^{r\phi}}{r \tan{\left (\theta \right )}} - \frac{\partial_{\theta} B^{r\phi}}{r} + \frac{\partial_{\phi} B^{r\theta}}{r \sin{\left (\theta \right )}}\right ) \boldsymbol{e_{r}\wedge e_{\theta}\wedge e_{\phi}} \end{align*} Maxwell's Equations +++++++++++++++++++ The geometric algebra formulation of Maxwell's equations is deomonstrated with the formalism developed in "Geometric Algebra for Physicists" [Doran]_. In this formalism the signature of the metric is :math:`(1,-1,-1,-1)` and the basis vectors are :math:`\gamma_{t}`, :math:`\gamma_{x}`, :math:`\gamma_{y}`, and :math:`\gamma_{z}`. The if :math:`\boldsymbol{E}` and :math:`\boldsymbol{B}` are the normal electric and magnetic field vectors the electric and magnetic bivectors are given by :math:`E = \boldsymbol{E}\gamma_{t}` and :math:`B = \boldsymbol{B}\gamma_{t}`. The electromagnetic bivector is then :math:`F = E+IB` where :math:`I = \gamma_{t}\gamma_{x}\gamma_{y}\gamma_{z}` is the pesudo-scalar for the Minkowski space. Note that the electromagnetic bivector is isomorphic to the electromagnetic tensor. Then if :math:`J` is the 4-current all of Maxwell's equations are given by :math:`\boldsymbol{\nabla}F = J`. For more details see [Doran]_ chapter 7. .. code-block:: python from sympy import symbols,sin,cos from sympy.galgebra.printing import xdvi from sympy.galgebra.ga import * Format() vars = symbols('t x y z') (g0,g1,g2,g3,grad) = MV.setup('gamma*t|x|y|z',metric='[1,-1,-1,-1]',coords=vars) I = MV.I B = MV('B','vector',fct=True) E = MV('E','vector',fct=True) B.set_coef(1,0,0) E.set_coef(1,0,0) B *= g0 E *= g0 J = MV('J','vector',fct=True) F = E+I*B print 'B = \\bm{B\\gamma_{t}} =',B print 'E = \\bm{E\\gamma_{t}} =',E print 'F = E+IB =',F print 'J =',J gradF = grad*F gradF.Fmt(3,'grad*F') print 'grad*F = J' (gradF.grade(1)-J).Fmt(3,'%\\grade{\\nabla F}_{1} -J = 0') (gradF.grade(3)).Fmt(3,'%\\grade{\\nabla F}_{3} = 0') xdvi() .. math:: :nowrap: \begin{align*} B =& \boldsymbol{B\gamma_{t}} = - B^{x}\boldsymbol{\gamma_{t}\wedge \gamma_{x}}- B^{y}\boldsymbol{\gamma_{t}\wedge \gamma_{y}}- B^{z}\boldsymbol{\gamma_{t}\wedge \gamma_{z}} \\ E =& \boldsymbol{E\gamma_{t}} = - E^{x}\boldsymbol{\gamma_{t}\wedge \gamma_{x}}- E^{y}\boldsymbol{\gamma_{t}\wedge \gamma_{y}}- E^{z}\boldsymbol{\gamma_{t}\wedge \gamma_{z}} \\ F =& E+IB = - E^{x}\boldsymbol{\gamma_{t}\wedge \gamma_{x}}- E^{y}\boldsymbol{\gamma_{t}\wedge \gamma_{y}}- E^{z}\boldsymbol{\gamma_{t}\wedge \gamma_{z}}- B^{z}\boldsymbol{\gamma_{x}\wedge \gamma_{y}}+B^{y}\boldsymbol{\gamma_{x}\wedge \gamma_{z}}- B^{x}\boldsymbol{\gamma_{y}\wedge \gamma_{z}} \\ J =& J^{t}\boldsymbol{\gamma_{t}}+J^{x}\boldsymbol{\gamma_{x}}+J^{y}\boldsymbol{\gamma_{y}}+J^{z}\boldsymbol{\gamma_{z}} \\ \boldsymbol{\nabla} F =& \left ( \partial_{x} E^{x} + \partial_{y} E^{y} + \partial_{z} E^{z}\right ) \boldsymbol{\gamma_{t}} \\ & +\left ( - \partial_{z} B^{y} + \partial_{y} B^{z} - \partial_{t} E^{x}\right ) \boldsymbol{\gamma_{x}} \\ & +\left ( \partial_{z} B^{x} - \partial_{x} B^{z} - \partial_{t} E^{y}\right ) \boldsymbol{\gamma_{y}} \\ & +\left ( - \partial_{y} B^{x} + \partial_{x} B^{y} - \partial_{t} E^{z}\right ) \boldsymbol{\gamma_{z}} \\ & +\left ( - \partial_{t} B^{z} + \partial_{y} E^{x} - \partial_{x} E^{y}\right ) \boldsymbol{\gamma_{t}\wedge \gamma_{x}\wedge \gamma_{y}} \\ & +\left ( \partial_{t} B^{y} + \partial_{z} E^{x} - \partial_{x} E^{z}\right ) \boldsymbol{\gamma_{t}\wedge \gamma_{x}\wedge \gamma_{z}} \\ & +\left ( - \partial_{t} B^{x} + \partial_{z} E^{y} - \partial_{y} E^{z}\right ) \boldsymbol{\gamma_{t}\wedge \gamma_{y}\wedge \gamma_{z}} \\ & +\left ( \partial_{x} B^{x} + \partial_{y} B^{y} + \partial_{z} B^{z}\right ) \boldsymbol{\gamma_{x}\wedge \gamma_{y}\wedge \gamma_{z}} \\ \boldsymbol{\nabla} F =& J \\ \left < {{\nabla F}} \right >_{1} -J = 0 = & \left ( - J^{t} + \partial_{x} E^{x} + \partial_{y} E^{y} + \partial_{z} E^{z}\right ) \boldsymbol{\gamma_{t}} \\ & +\left ( - J^{x} - \partial_{z} B^{y} + \partial_{y} B^{z} - \partial_{t} E^{x}\right ) \boldsymbol{\gamma_{x}} \\ & +\left ( - J^{y} + \partial_{z} B^{x} - \partial_{x} B^{z} - \partial_{t} E^{y}\right ) \boldsymbol{\gamma_{y}} \\ & +\left ( - J^{z} - \partial_{y} B^{x} + \partial_{x} B^{y} - \partial_{t} E^{z}\right ) \boldsymbol{\gamma_{z}} \\ \left < {{\nabla F}} \right >_{3} = 0 = & \left ( - \partial_{t} B^{z} + \partial_{y} E^{x} - \partial_{x} E^{y}\right ) \boldsymbol{\gamma_{t}\wedge \gamma_{x}\wedge \gamma_{y}} \\ & +\left ( \partial_{t} B^{y} + \partial_{z} E^{x} - \partial_{x} E^{z}\right ) \boldsymbol{\gamma_{t}\wedge \gamma_{x}\wedge \gamma_{z}} \\ & +\left ( - \partial_{t} B^{x} + \partial_{z} E^{y} - \partial_{y} E^{z}\right ) \boldsymbol{\gamma_{t}\wedge \gamma_{y}\wedge \gamma_{z}} \\ & +\left ( \partial_{x} B^{x} + \partial_{y} B^{y} + \partial_{z} B^{z}\right ) \boldsymbol{\gamma_{x}\wedge \gamma_{y}\wedge \gamma_{z}} \end{align*} Dirac Equation ++++++++++++++ In [Doran]_ equation 8.89 (page 283) is the geometric algebra formulation of the Dirac equation. In this equation :math:`\psi` is an 8-component real spinor which is to say that it is a multivector with sacalar, bivector, and pseudo-vector components in the space-time geometric algebra (it consists only of even grade components). .. code-block:: python from sympy import symbols,sin,cos from sympy.galgebra.printing import xdvi from sympy.galgebra.ga import * Format() vars = symbols('t x y z') (g0,g1,g2,g3,grad) = MV.setup('gamma*t|x|y|z',metric='[1,-1,-1,-1]',coords=vars) I = MV.I (m,e) = symbols('m e') psi = MV('psi','spinor',fct=True) A = MV('A','vector',fct=True) sig_z = g3*g0 print '\\bm{A} =',A print '\\bm{\\psi} =',psi dirac_eq = (grad*psi)*I*sig_z-e*A*psi-m*psi*g0 dirac_eq.simplify() dirac_eq.Fmt(3,r'\nabla \bm{\psi} I \sigma_{z}-e\bm{A}\bm{\psi}-m\bm{\psi}\gamma_{t} = 0') xdvi() The equations displayed are the partial differential equations for each component of the Dirac equation in rectangular coordinates we the driver for the equations is the 4-potential :math:`A`. One utility of these equations is to setup a numerical solver for the Dirac equation. .. math:: :nowrap: \begin{align*} \boldsymbol{A} =& A^{t}\boldsymbol{\gamma_{t}}+A^{x}\boldsymbol{\gamma_{x}}+A^{y}\boldsymbol{\gamma_{y}}+A^{z}\boldsymbol{\gamma_{z}} \\ \boldsymbol{\psi} =& \psi+\psi^{tx}\boldsymbol{\gamma_{t}\wedge \gamma_{x}} +\psi^{ty}\boldsymbol{\gamma_{t}\wedge \gamma_{y}} +\psi^{tz}\boldsymbol{\gamma_{t}\wedge \gamma_{z}} +\psi^{xy}\boldsymbol{\gamma_{x}\wedge \gamma_{y}} +\psi^{xz}\boldsymbol{\gamma_{x}\wedge \gamma_{z}} +\psi^{yz}\boldsymbol{\gamma_{y}\wedge \gamma_{z}} \\ &+\psi^{txyz}\boldsymbol{\gamma_{t}\wedge \gamma_{x}\wedge \gamma_{y}\wedge \gamma_{z}} \\ \nabla \boldsymbol{\psi} I \sigma_{z}-e\boldsymbol{A}\boldsymbol{\psi}-m\boldsymbol{\psi}\gamma_{t} = 0 = & \left ( - e A^{t} \psi - e A^{x} \psi^{tx} - e A^{y} \psi^{ty} - e A^{z} \psi^{tz} - m \psi - \partial_{y} \psi^{tx} - \partial_{z} \psi^{txyz} + \partial_{x} \psi^{ty} + \partial_{t} \psi^{xy}\right ) \boldsymbol{\gamma_{t}} \\ \hspace{-0.5in}& +\left ( - e A^{t} \psi^{tx} - e A^{x} \psi - e A^{y} \psi^{xy} - e A^{z} \psi^{xz} + m \psi^{tx} + \partial_{y} \psi - \partial_{t} \psi^{ty} - \partial_{x} \psi^{xy} + \partial_{z} \psi^{yz}\right ) \boldsymbol{\gamma_{x}} \\ \hspace{-0.5in}& +\left ( - e A^{t} \psi^{ty} + e A^{x} \psi^{xy} - e A^{y} \psi - e A^{z} \psi^{yz} + m \psi^{ty} - \partial_{x} \psi + \partial_{t} \psi^{tx} - \partial_{y} \psi^{xy} - \partial_{z} \psi^{xz}\right ) \boldsymbol{\gamma_{y}} \\ \hspace{-0.5in}& +\left ( - e A^{t} \psi^{tz} + e A^{x} \psi^{xz} + e A^{y} \psi^{yz} - e A^{z} \psi + m \psi^{tz} + \partial_{t} \psi^{txyz} - \partial_{z} \psi^{xy} + \partial_{y} \psi^{xz} - \partial_{x} \psi^{yz}\right ) \boldsymbol{\gamma_{z}} \\ \hspace{-0.5in}& +\left ( - e A^{t} \psi^{xy} + e A^{x} \psi^{ty} - e A^{y} \psi^{tx} - e A^{z} \psi^{txyz} - m \psi^{xy} - \partial_{t} \psi + \partial_{x} \psi^{tx} + \partial_{y} \psi^{ty} + \partial_{z} \psi^{tz}\right ) \boldsymbol{\gamma_{t}\wedge \gamma_{x}\wedge \gamma_{y}} \\ \hspace{-0.5in}& +\left ( - e A^{t} \psi^{xz} + e A^{x} \psi^{tz} + e A^{y} \psi^{txyz} - e A^{z} \psi^{tx} - m \psi^{xz} + \partial_{x} \psi^{txyz} + \partial_{z} \psi^{ty} - \partial_{y} \psi^{tz} - \partial_{t} \psi^{yz}\right ) \boldsymbol{\gamma_{t}\wedge \gamma_{x}\wedge \gamma_{z}} \\ \hspace{-0.5in}& +\left ( - e A^{t} \psi^{yz} - e A^{x} \psi^{txyz} + e A^{y} \psi^{tz} - e A^{z} \psi^{ty} - m \psi^{yz} - \partial_{z} \psi^{tx} + \partial_{y} \psi^{txyz} + \partial_{x} \psi^{tz} + \partial_{t} \psi^{xz}\right ) \boldsymbol{\gamma_{t}\wedge \gamma_{y}\wedge \gamma_{z}} \\ \hspace{-0.5in}& +\left ( - e A^{t} \psi^{txyz} - e A^{x} \psi^{yz} + e A^{y} \psi^{xz} - e A^{z} \psi^{xy} + m \psi^{txyz} + \partial_{z} \psi - \partial_{t} \psi^{tz} - \partial_{x} \psi^{xz} - \partial_{y} \psi^{yz}\right ) \boldsymbol{\gamma_{x}\wedge \gamma_{y}\wedge \gamma_{z}} \end{align*} .. [Doran] ``_ ``Geometric Algebra for Physicists`` by C. Doran and A. Lasenby, Cambridge University Press, 2003. .. [Hestenes] ``_ ``Clifford Algebra to Geometric Calculus`` by D.Hestenes and G. Sobczyk, Kluwer Academic Publishers, 1984. .. [Macdonald] ''_ ``Linear and Geometric Algebra`` by Alan Macdonald, ``_ sympy-0.7.4.1/doc/src/modules/galgebra/vector.rst0000644000175000017500000000040012253362407022042 0ustar georgeskgeorgeskVector for Geometric Algebra ============================ .. module:: sympy.galgebra.vector Class Reference --------------- .. autoclass:: Vector :members: Function Reference ------------------ .. autofunction:: flatten .. autofunction:: TrigSimp sympy-0.7.4.1/doc/src/modules/galgebra/debug.rst0000644000175000017500000000040712253362407021635 0ustar georgeskgeorgeskDebug code for Geometric Algebra ================================ .. module:: sympy.galgebra.debug Function Reference ------------------ .. autofunction:: ostr .. autofunction:: oprint .. autofunction:: print_product_table .. autofunction:: print_sub_table sympy-0.7.4.1/doc/src/modules/galgebra/index.rst0000644000175000017500000000047312253362407021661 0ustar georgeskgeorgesk======================== Geometric Algebra Module ======================== .. automodule:: sympy.galgebra Documentation for Geometric Algebra module. Contents: .. toctree:: :maxdepth: 2 ga.rst manifold.rst vector.rst precedence.rst printing.rst ncutil.rst stringarrays.rst debug.rst sympy-0.7.4.1/doc/src/modules/galgebra/simple_test_latex_2.png0000644000175000017500000012544512253362407024503 0ustar georgeskgeorgeskPNG  IHDRYsRGBbKGD pHYsaa?itIME %C IDATx{T?\@gN Hf=̀hъ0[KcSJt)J^!%r11!` "^3ų _k콟ϳgOfΜ,u}>q2:DDDDDDDDD+J% 6&I! """""""""|#/:.]pLV666 QT*!5r}}=BVc4iR:߰uV477cʔ)x:/-- YYY3g&O|OryB&O?f͚_y_MM ~w`ze?3= ŰN1qDXZZƍ1gcr\.D!""""""~ƣ%<==_K\t >>>:u*,--rJhZ}qu={#00III=ǯڡLѠO^Q߿l߾}Ν;8p@|wbr9,,,p)xxxp"=`fZZZp>}:hz---\|pEt:L2[t1Y˨4:F>}* jZm\&577ܹsflkB,ڊ[n=mtPTB;h4m5CV8痕͡ǯRЀ._Ggm+s晱83FSs'.GLqv!?j(fO@*b۶mFTVVZa߾}e'""""""L&ߏI&ǻkަM0c ?1c.](++[lAVVVza8pǏǟgTVVv'ҥK8y$^~bǎ_W,77f2*99֭ZFtt4rrrsFFz)"-- 3g Gb@x'_1qDwgjj*NDDDDDDDѭ֕Xt)o`ȑػw^'N`ժUꫯ0o<@~~>"##'"((Ǜo GGn t̘1xddffB$k ”)Sd,YDhcpss \\\V͘1oYY"""p HRٳŘ1cFcV[8w|x"p1K/L#G]M]/۷o  /FxxP'77)))B~)"##͎!99Y_񁧧=zgΜի1vXs=\`l~Ygq05Fshw9gl.`f}G:19h4ظq#bbb+++au!.\6$Idj bС'""""""O&'466bر1CI҇*wqJ tRc.];+'N x0g|ᇘ={6V^WnRSS1f!wibgBѣG ngmbsix Yp!n: q;pg;F8۷nnnz\\\07"췜w癷7ϟ7:13Gsr}bwQQQx7(0׵k:\ݪ[_|""""""hQ"Acxg+Vo[֦K.Exx8^z%\x9 EEE(,,w}Kvx!]y K&?zvfOPRRwwN 4< v X,kciiÇg1=6V!ZCR@WuΘcc4gvu:{ٽ{7{1<Ӹut:YEEEjLCf4uDDDDDDD`]FOCTD8ֶ?B<7n࣏>Ձޝ_aÆ ӡ )NdΗ_~ׯcԩzP%%%hll>|qt:v }6D&Y\VVfE&( !As3uؼy3t:,,,ʂwۿ͍PhPRR???4mgG Ygq05Fshw_wcgξ#dffB& UUUعsg 0ԧNCdd$y0ppp\Z- `,77'""""""O$ֶ=on ӧOGBBΜ99r%%%X[[Evv6?~|8RRRq}R#<""''񨨨q`kk 2dBlcȐ!;;; #P\\D\rnnnHMMw}Z OOO 4H8;wĈ#PPP/"77{9mܸqO<H:NKKKh4444`$hmmV=pss3jkkMnݼqlll Jڪt ҥ\ׯ_u_^xH~]jqU͛Ű1y---PT]ޖdtX[]{{{I"cq2wlF>ײh:kރ#l2vSs''3;YP$$$  &R*%Yӟ`QJ5kVFwS*3 DwήVQQQǏ3 }/p֭ex7$"""""">+_h48<\]]cj* QDDDDDDDDDJ%!!!~Q{!!!\HNåK$Shii DDDDDDDDO)JH g=z aii Hc'H$22쾍_5Z|cqF̙3G\.Gtt4!-%"""""""ɓ'saٲeX`~Wԭqh4v霉'bȑXr%&M%K 88ӦM޽{QQQ@bjZzða: D$!<<QQQvcvvvBoW"66v233hѢn]:G"`ĈCB*B&| .^@iZzų>kL.N7o娩>t:T*444MMMzer\XZaduu5t:^F^3gpm5JάPWWgm]G՜挵Mss3Ν;VkQ[{eeehii1xJ8::|o(Q?oyyy}6^{5XJJ fΜ|7o5k6oެNFFz)"-- IMMɓ'q!={P[[ H'|'|_| 11'NDzzz Xnj5cֵю;i&455>56iiixMYYYS4]vaСub1?kkk< Gř3gzj;VXU7c ̟?C(((@ii)z-@\\rrr0{l#GK,طmXdرc^z kYY"""p HRٳŘ1cFb(FEEEHNN޽{Ifn.\xI$K""""""""Ug:bƍH$ɓ;;UP(V{u!!!ptt twǚ1c@*sk.}탛1#,,[1UciME :%ژzFaIII6g888tZTҭv%aٵhZk4nڬՌ#"""""""_$Xw5V S(B+c8pL&Css𹬬`±Ç}- DaaYѠ~~~]a\rdܴZ- o(Q?%wƍQRR:`Ca֭Bѣǣƍ- ;; #P\\DTUUa̘1x֭[!H GGGs!223g;M6Ө͛71~xcΝ;1bŋͅg:d8::".. pwwǐ!C]988@,cjŋûȀ~YTWWۛR"""""""~&22"ZF ---PTmVW7oބX,=hpuA$Z40P$$$lذ>>>0a'Q?T*;xXXXܳ#p-Dx衇0dȐn'$ &z-tlЛjIG"""""""~Gz |}}QTT[nu(śo cjMF*SPTg)R#.R qhh(#ADDDDDDDDD&44xttUj5ZZZ`ccY9%/_ƠAT*97wo޸q#Ri \.G||eeeٳ=nu>э]ǃDDDDDDD߯$ccc@rXXXԩS_;;;,Zwرc2e lll ꫯ555W^=kii)Ԝ@PPP57[H2urn~xLKK?n,** ۷oǭ[+WO駟Fss3n݊'O?r={`ҤIXhpU<_Vv<<-'|_|po'Nt΁ObŊHNN.ϩT*̝;Ks7op'ۦ7fW杩$"""""""LxS˅ xnn.RRR/>SDFF̝;ׯ_Gkk+GG.m7op ( ( x;%H9jjj`-[i[Xj|~ӫ7}t#998~8Lb777E/1j*8rݱd6p1K/L:=rssacc _~DfͩF9hl2̝;G^^>sD^]wƮ's3"X]] ++SSSamm wVi41w\|]N:⋈@mm-1bue VZ+10`.\-[`ڵ!b!!!ضmq1X~^x̙3~!fϞիWw)d44vR)|q%8995zcNH$lٲ u!&&Fޘ]w渗* """"""="hmmmp5%gy蹶7n//.mNrsNXYYᥗ^2p!WTo֑d ,^C@@&L`^pp0"""p d2 Jc PXXK.Ł̎e-i؜9h? =pY/G}˞ͮ;~ R"Qxtpp = Bnn^b-o_U.W_ŦM JDbf_\.ԩS1m4mnll/ {{{۷<ñ}b C@@@6sssQZZj/ׯ_ԩSX* !XRR׮L&{9JYYc hȑ#0a f2Gg!H"<<Xj%27M1gޙs3ky>=###>>>z`ee;wxGaccx?~6tPcʕ9{,mub :_q-//aeeHRxzzHJJBYYZ[[1zha"/_Fxxxv?ܸqOmۆoذ>>>&922t b9bJCv8Oի͛7!acc#{`477l9eޚw$&&b&ټ3GDDDDDDD)BBB nqlmuppQGVܪj7 IDATillZ7T#p"NNNF_b~YXXMbἇzC ѻghnnR4ٜ2Go̻2wޙM"""""""MEEEu=i ;+W0Z/66o&g=rAjTTT}bNK;spnѽoZw?*j*[+{NUTT$""""""~CTBh"ݦM """""""""ZZH;y$]~WMKKȑ#ݪS""""""""JN1==Nʕ+@ڵ 6m=cժUpqqAxx8,--;?dq 8;;l|dgg?bdffpss\.Gkk+x NRRjjjڊP(~3MS"""""""_պ{={pB_fM:5 L:@`` mܸFW(7{fn&Nر#116lX,ƻヒK՛={6v th4(,,y)у?1//gΜApppW<gΜ镱 ' WWWTVVSUUXXXt(knnJC=ԣ>zABB>#={r\(AQQ{8pj...غu+|}}z;p6OO<|,YK発DDDDDDDD[_X]]RCӡgΜAkkkz%%%8u4 N:F ZZZ2|inݺTWW}m۸r Ν;sAv]f pƍ^N\.GSSSŪ=gggxyya޽zkkkamm-|ܹs}vz=̎cmm-I|t:wn2zZ684 RЀg677xuGN< 88fkÇ㫯¸qg[h^{5|Gرcl2~ |||Da̘1((('HLLQGRaܹˢyf"88ӥ5 b577f͛;]v +WĔ)SL!$9t:n޼i򯦦K2&44[l>WUU\~A: z+%MSO=XsAAARRRpQ̟?C}vܺu aaarP'OСCӧ̜9111o%Cc499֭ZFtt4rrrGDDDDDDD/n֬Y DTT֯_~pi_P/)Skkk 8K ;\h~ܼyojkk~z|x ,Ǒwy4j(ǫ/_X,Ɨ_~&_?rfϞm|ƌ?2[[[|g=z>$ \]]'v:mЦXYYaٲefʔ@Z o՛>}:표p?~ӄ|}}QPPR%;qqqHMM|/ /EJJ Ӆ6>SDFFpwjG̙3Xz5Ǝ+$ Ӳ2DDDĉJسg1c }!l{plҤI8r7D"455aҤI4hOnC&LVJO2  z(.gȑ#ضmzVDJ$ڻw/-Z`,Zd]ZqXp!lقkb!!!ضmq1XK}b  ^ jZ kkkdddh߸q 0yd455cƌTz+׎+itsm Au=co6Ӑd>|x^B+>  ֥6?˗/_}OowyǬt* ~2 ,蕱-^C@@T6$88XX(ɺ!Bb绯Ç3hyw̬I&vh{矣k{{{w^k.?QQQ Ő!C-BRR|i_cԨQ8t;XFAAT*F\p6m[oGGG? xb?<֯_[[[C'O&ȑ#5k_ą  K"""""""{ذa^ X KKKC1`H4fϞ=y& ؈uaر>CnpuuEeeeơj1f={qkKwGkk+$hp5K---PT]Vmׯ[""""""""CJezcg:om[V/MH 8FVuԋcLپ};Ξ=_~-MMM,{7Zm#Jk*hE$GDDDDDDD=+l/LyZ-vH\:d2 ]^k'ŋN8tZ-0rHt:qUx1+..رc%"""""""{KTu:GAcc#z{SN]""""""""2G"""""""""uJŋŋsp%8991}ZFKK lll """""""noG^^$ ZZZ닑#GbPtĉY?>|x:(..Fii) ڊ7xC"44 7nĜ9sk.8::bڴiݎE67 ]\.Gtt4!KADDDDDDD 8'" (** `ضmNc|5 㑘 6@,wҥK͞=w? 0l0=E67 F{ޏDGTT!IW䄘޽gΜddd`Ν?~<{X-ݻQ__Wv1L2666dxW@X%٦ ,ų>k߈(h'JKKQ\\|1vr8u%Aܗ퍐χ[޽{Zleesbz{!UYY GGGŦC~5477(hCVܹsj˛ k4řleeehii;RЀ455u:o>Jupݺu~:"##1w\tyɿN Ŗ-[UUUx衇: ?۷o 744LKK?i*u SmXvɓ:\z3g*ԋq-ʕ+z$''cݺuPՈFNNP'OСCӧݱc6mڄ&|gؿP3g"&&| &>;+ADDDDDDD Ҿ>@B#66gϞѣ: &XYYaٲe&bժU(//+~Maood2eP~xzzv:QF806^|Eܾ}P(P(Xx1Å:HIIAzz:~)"##eee' Jgcƌ9rXd^EEEHNNVzxxpvvF@@=3g`;vUb%H_ """""""n'N`ҤIˑDy666XjU0`.\-[`ڵ|=Xl۶ 8vVX!WWWĢ-\^^^XnjkkW kkkdddhnj#<7}M &܋qA$a=VS%ȸ>xlhh@FF~mx{{c/ovzJ·~ NgL& :mkŘ7o0a|2L/Aimmݣs=Eo4hy޽bC ---1|p<3FJByVKlСC{ "W3-J9>C;wsr9NiӦ{+t{hllxzz탭-;plsvv7W9+䠼GW_͛`aaW\dff GII_8|^ BHԖQXX(|h4())pнVW$־;}jP)))裏cbر,VLpuue0(Z Jet*T*!]hQx T#G2}BB` iѢE\xt:\t NNN FVbDDDDDDDDdZyLII ݷhTVVh눊ƍW'))I8^WWظq#RѾwڅG(ahØϣO-C)zJuSH<ܹ7nF/M8رc>HLLĆ  bҥzfϞݻw yyy6lg">>G sF"))O-C)(BuSOk߈(0WoaLff&-Z'旱尰S+ADDDDDDD }>񘗗gy~~~صk}^^^ػwZX[[ 0w\l߾]/$*++tȯ]T2Fcc#N> JZ-tE}}=t:jjj: x۹P]] Ngt|(++CKKKvT*С\_ """""""nǶՅǷ~!dN͛7MJ-[Cu~oohh\.?Js(06 KKK;v _'Ċ+"$$B\̚5 7ok7##O=bcc_~AAAclڴ MMMϰ~,%%3gDLL xyyAv+Q7H4 b1b1܌LvznMM Lֱ²eL ĪUP^^WWW^ӧ1e… tܣFq,ؿ?s=\`_Fff&D"f̘wh(--[oCNNfϞ-+**Brr >>>􄳳3pQ9sWرcM25+Dg^CDDDDDDDcVV~w|W#Gb׮]f%mlljժaXp!lقkb1BBBm6رcXbP^]] ++ /`Μ91{l^CJǥK$1D,UP(r}w 7nD"&Oܣ5ZӉǢ"_^H 44 d&UToM>'P& +LYx1͛L0h`DDDĉdz JkkI,zB@QQ waҥ8p@MHlZc=t{2""""""""2O}cmm-$^|\SbڴiF ^cc#Ǿ}`kk NB} C@@^\@b^NN˻5/ׯ_ԩSipgmm#G`„ 6lpX2}L DaaYѠ~~~F.VW$־;}jPªUp >>=]YYYx|o~'C*xGDYYd2ˑh!999GEEƍ'$r 1HLLDUUƌ~X,jŋ [:[ G<<{,_ """"""".j3}P$$$|Fg̛7577.]!C:x|`477ږ4 ^ {{{/ 6z""""""""HTݭT*5tdff>1n',--!dpuss3T*YiGG{tlllZfґx|}}QTT[n=qwxeo8x j5***p~}bccoruZgϟ+GjT*+W4R#.R iHH$ ADDDDDDDDD"$$+{NåKƮVH""""""""6R i_X~~> H___9۷oZN&Nh`d^ƍ1g7n!Cz6]vӦMV>nr\.7Ozĉ@tt4K! IDATu눌ܹs{ޞ={P[[ H&қojnݺUW6 +mٲefXiJ㕍ǎ;ɓ뚫kpXl4eM0AC ڵkXVk}Z窶BCC75m4h 7N_}5ߚ5kT\\,˗/kȑQ||4Ըq 6hϞ=JLLTttg7>>^s IQQysUJ}URRpXnܾo۶m㏵o>ׯ-[l2k׮ڽ{>s>>r8qL&pɤ={dZRV("eĉģfӱcԹs=~Zj%mXTVV6m4\Gs@s#7W xѢE ׎;"ɤ2GZr,l6 @ؤ$SsOsgddӋ/XqOOO͛7Ow?~ZO0Afj:q℺u&_j5dȐRcpF<.LJJ:u:(00Paaa7ovڥnݺI+Vhׯ_揍բEdZk4eM0AC ڵk\XV+WWZrkegg+,,LׯwOxqu Ψe111z۝;wVttV^H~ͭsVTT]JNN}v!???7x ժU<744T|MW@@fl6MBl6IRyycV/^\;wN}p>=>}Z75Z1.x[oΜ9YfGqhm۶iРA7n+zn߾}ԨYfe0t*!!A#GTLL㕞p)..N?ƍ'ͦ 6hϞ=JLLTttW\$m߾];}#Q[ ά#GT^a%ooo7N7oVX__~e!!!zǕPuڰa\=zTaaa ԨQ4c ի޹|jjѢEvx񕀀 ZbpF8.uAAI*((H&LO?7d2{$8qB .$CVRHHHc***K.ՎFuݾ={ڷdXJsUTTFZ݌zZq\غu\+))QBB^y1BԂ Uwڥ~\n4ydl6ל4ƥ-<<9wvw>}\3WV^S^^l6ڵk:ݬzws&pW;v쨓'OV?w\EFFJ|||G7Tvvv-..dnppݕP㘊 yxxxl̬֭aXo˖-kl*^XiQ\IҦMd65`={V5fԣ.u חK4CBBsNv\\Mm޼پرc2 \811Q+W[^G:tHqΥKV[Ey۶m3gf}]-_ڸ-[(11Qڲe[/О={駟ȑ#uq;V?֮][sU)''GǏuIݺuٳնmj.YD3gάw}nf=RWp}$ي|'Oֲe\L.zY*88Xw}MGYYj|M~YyGn;P+[k<(0K]M.]bi,wwZ\Y,Fq7Z܊\]v)//ӠR\5WYYY s rW%j޽{M6uEEEEСCՍz4Z47>>>xmҤI2LdSL4ɵxl:v:w|ïmXTVV6mڐ&j͕^h%I;vPJJL&=zhʕX,l0`5wFF;5Լy)OOzO? d6kTXX'N[npxUV_C qz-ScpF<.uJJ:ud_0$00Paaa7ovڥnݺI+Vhׯ_珍բEdZk4eM0AC ڵk\XV;WV\Z Gzz>RQ+84cbbV׹sgEGGk:p$)!!Aׯjȑ#ڱc$BWrrrc<=={;nɤ]Jڵk'777fGsѣGL޽AQjj&M0:u.^P=fΜzhH=D177W2;bEDDhڱc_\z饗g)))UVKhhFρfl6ۛlrZV-^:w>35t>|ء{8}JKK^ԣ]+8%{j=[o̙35ky駟sk_~Ym۶$m۶M ҸqW_kWIII5k֨XA{4rH(>>^ WZZ?jܸqlڰa٣DEGGk߾}UPP?.///I;á{(**1׍ECQ[ ά#GT^ᕼ5n8m޼YlJHH3VX__~e!!!zǕPuڰa\=zTaaa ԨQ4c ի޹|jjѢEvx񕀀 ֢-g Z؟ԫIjj4a=ڸqL&ykܹ2L5MJJ҉'pBIR=j*z #""BҥKcFQݻwo{{{gϞm///Y,FJΝ(=jL-G]%[ɵ%$$W^ш#4p@-X@QQQzzjEEEԘ1c}p׮]zOuM'OVIIfsIsk\k}WoW~JӧOr%IWVu})//O6MڵsJ[ GQ͍r%^رNHo+::Zʕ+ d2Uy]788XJHHz֭[Ymeðߖ-[TXv%ooo{s1##޹ڴif g*66W:5֣.u חK<+WӚ5k;S>$رc2 Ox@[n / 777ݻW.\РA$IZr{̙36mZX:ҥK޽{m۶M2zw5p@M4ʸ-[(11Q4l0(--MEEE PZZ9?X/Ǝ~[~{׶kʕ$hU%}1cFXd~꣱ӧt竰AK]ed+..nNZ\Fl6=zSmXTPP@\ 9rs`-ZeddӋ/XvءL&)$$D=zʕ+eXd4`J<==5oaB8qBݺuZJ2dHkH=Zy\ǔuԩCbcch"Yz0͛7OvRn$IԊ+4x`~`PddΝ۠գG= Ҕ)S4a 2Dk׮UNNNsaZ\\R_;\t}'N#+GkqcLL|UTT]JNN_ٹsgEGGk:p$)!!Aׯܪ>)wwwݻqL&uUԮ];l6O>3gCCgffj UNNRSS5i$_ԩSuE1sL=CN#-Gkqcnne4nJJ~jժUկ4sL͚5KgϞq#ӧkǎ󓿿oi8p@ lfSqq XXX(&I*//W^^}jŋs>_# @.\ÇӧOԩhh=jc\{۶mӠA4n8}W*++$S"""4|:rHoΜ9YfG3}*))Qf`0{UBBF+==]JKKS\\~G7N6M6lО={h۷^ %Iھ}|}}uw8tEEE5檱hH=jc\xȑZhZe4e4Rmڴ9;wwOg}^zI7n6oެdRIICb / ?L*88X[ֆ 0ѣ S``F3fW^UVU-RttË`08 GM18VhXպՒt -\Pԣ IDATGZJ!!!ν/+??_?Oq)55UAAA0a~imܸQ&x***K.ՎFuݾ={ڷdXJsUTTF:9\8Z+4K4[n]kvom[n>|hذa2 ;w"##%I>>>裏o*;;X***Q㱭[V{mX [lYcSbMەŌzJ6m$٬ٳUdeee5FΨף.u חK<+WKLL|B 4HAAA+W[<(Ţ˗/kڴiڵ}|\\֬YxyzG%Iǎ`PDDx =SjѢ}ܡC4x\tԻwom۶)66VfYI&Ue%&&J HJKKӑ#G^رcoovM?޾$o^3f̨vK,O}4G]/$[qqqtZlY\8>Cᄅ(++SQQQI;YsQ W5^~YvjҥKX,M^Օ,F4] We!!!ڵknZ 111/rJRXXK]V͕˼j-IVUVo-ԡCrĹZ"""l-""22ҵx2\Goϝ;WÆ Stttqqq;SiiipKkҍQFi̘1:p (=jٲRSS_ԣ>?իf͚%I@IҫW_}Unnn@M8ʵfϞݠ_9??ŋn:~Y5 ._ jJC$$ٳ~y5k]1w}$l6KLV AzxxUVUyyyIBBBJ?:Ǿ}d6եKW]_E+cǎΝ;w}׾-I*))o._,I%݀P?&IoM2uiٲe*,,u=؟>>>NGxtnSLqpĔ)SX\s #s5j|||o$'5Jf#5W8GNGxt48GNGxt48۵NHMM-Zf顇ӧﯮ]B|~g͘1Cݺus8DEX,1bN* 4!fw8p@}ڴi#ͦO?Tkj˗5|p͟?_np6M/ڷo^zIAEO?Fkժ;w*00iIsW׬YX-ZHmڴ$ M2EwuW/o@cccm6{Q4{l=sʪ<{UzzSx=\Yǿ/zꩧYO*++vfS^^j{… :t*** {1{ӱR5x`Ⱦ]XX/jj5XTT$"ͦ*jORsϟWVV~_8hc~bbbte͞=[_+JKKu*{ZlΟ?8qBsZxw*%%E~z$iŊݻjJHHܹs!IZn4}t*>>^?ORsN:U0ܒj\\Ӓ$\ www+==]}6l Io~_~gyF{d*O&%%iݺuJJJ$=Z|̙SzN3٬'OINN4i$IcǎզM_ZӦM-[M60a$UVz'}ZkΉ'n[Rǀyxx̙35***Ryy֬Y>}T9֣G_^eeeׯ(MJ}ڴicoZjxݕQFU;~әW7(F5a4ժU+Q4f=zԡ>~]eAswwرcAF***}BVUվx-Z;<x@O>͛W!!!駟PjjzZU#PsI͛7Ok׮Տ?XejٳgզM=ڱcGc{ѣޘ\di#())J㲶F$͟?_WVNNN_|yzzԾ}Au}||TRR"zrIڴi Ν;9_ms&%%i޽pK2ot[ƍ[o#G[wƍ5~xlR2UQQ  nMvbcce6qF%''k R>}O?T-Z͛տK/K׿͛7+66kܷ~/_ݻ+%%EGQRR޽{L&tN:Iyf:tH+VЂ [|~~~j۶m9׿ٳ1b2r ߕsw^CՎ[VSӤI$ҬVqq|||d0}vҥKJII`ëS8IJKKuɓ'շo_kh\uvkZdke6lжm4d%%%SN>}zl:|IqsS6m_~JOOשS'I2 jժA>}._-[8kٴzjEEE;6G-6____;wV۶mh<? ѨO?Twu$)))IAAAUdiiiڵ|}}UPPŋy=ooF5Ku!Iu626//OSL{bkL\zѣG5gΜ%&MdUwǎV~~~o) ƹ|}},zrt73F%۶mٳ%I'Nh߮,xܿZYXXUVfz=OOOM0Q1_7 -ԤbkH\h>\bVZC%K(88X))):r~w}2M6ֹ4l0 > Ҩx׭[-[jɒ%ZM*%x3g,Y=z(..N=:wQ -ZvsS^[:yz#9R˖-ԩSLl ͏K<8vX=3fΝ;{W~~~Zl&N,It޴X.\{ӧO+55Ueee !uh\ {4;CoFqFu]kӉU04rțovvvUfϞduEK.\3:uTbs4.G&xR~~VX;wjc˖-ӤIԦM:uJJOOw}[_|!///%%%iђ$ͦrY* ԩSZm̙3׌WUXG@3Y^z5fJJJJTPP8ݻWkIe///V'IYYYW˖-ciz嗫\ofl5Uq61qqk.7r&Guy+#GT\\VZiСz'*^MF_WTϞ=eݻ վ}k+ ͏)pzJqqq4i_v*.. o߮4]tI)))2 >|xyj'Ipႊxb-Y>ɓ۷F5csdkh\h~h<:Y~SN1 jժA>}._-[8*ܹڶm+oF^ZQQQ!'6G@c] %44A qnntr]cl6բE \sbi׏?kΝӴiq44n:Y,I\d#&11ѾuPPwNR >8GNGxt48GNGxt48GNGxt48G'X,JKKSyyvܩ .dee)??_dZu% k2l6+zE-YD&I8p;w?L&:wvO}/)))IKO/_և~5k(::Z۷{rP'i^z%?ffu'MmڴiNf]xQfk֗/_֬YqF=s:trrrhѢ&rjJcƌ7M8v?h:\1]ٳՖRPPŋyrm,Iv^Њ+6mhzl./_L]|Y}սM˽jn:j]ڧNRԳgO=c/[n #//Ofҕl_W4?.xLLLTyyBBBk:vڷo_BZJuݦ&Lk_)>>^<.]?5sI%''O>2}WEs}?vҙ3gNAj߾}]yrU㕗IҨQԭ[7}uҰa4|Z2duv*+eeeZ`***o>}ׯ>PttV^drYILL5uTm޼Y6M#F֭[E4o'|T~oiq ԢE*3227(''GVUڸqƏͧ%iذa:FݏjuE͞=[aaaWcRZZ*wwwl6]t]klRo~Qd[)h:*4 .t-_\իi:bxIgh<p:#_{UȏFh$ߩQSȦȸev@Ni2_QJڔaX+^qzG/6ES$`Ȩ]nrQ/aQ]spdBvga~GyOƓ$I, a2~yyyX`BBB|H .!Un9s.\^K:f?>3lݺ|c_>6n8?Պ|TVV";;MMMhmm= mۆ/> z'_ODDDDDDDDOUvOFvv6ǡC\[*2dVYV\rV˖-ڵkG3gŋqfٳgjwFaa+=>6puK6;i!??BѶq&[TTmۦxEDD`DQQ,Y={ ,, HMMETT`͈GNNθDDDDDDDDx4Zصkh[CCӍ7nc64Eƛ5k;-''Gw ;u:77с3g`ҥ µkאKc``Bmmh[hh(z-ڵ ntwwgFAKK fϞӧ:TL?.#NItc."" HLLtzjƛ#66؏fl6|z*ۇx$%%Aӡ'Np؟NCzz:f̘1__Q]]6?u***j!SOٳL"""""""";w^__~-N<~ Ŀۿ=`Mx}~r,Ydt3 vRSSp|FOO܌5k֌khhG/`l۶ {sD?>}]<#hll`O<$"""""""R9pYdʿcϞ=Snŗ_~իW###===hmmh?>!1|tuud?CDDDDDDDrvܹiLy ЀrB$ 5͸XMʼnIPQQٌ_=ǻ0BBB Иc yyyشi<裨A||<'H備5ͤo:@kk+9F8qDDDDDDDDw<ₘ"""""""""R7Hqx$"""""""""q㑈ǍG"""""""""R7Hqx$"""""""""q㑈ǍG"""""""""R7ǙL&add477h4$IАc{YSGuȩv6n^ݩc"""""fl6c8pt:~'@}}=t:Q^^8$!55S\\`0L(--0(,,Dmm- 6o .s1J̓x}EDD&idTQzz@WwkSM5KDDDDD>$Tl6{L IBl6OjaټDgq9W_GOOlZdgg !C;wN !xWD{{c+5U\f2Ď;|}O|OEN:ΌYk zu6fZ0<< !>Saڵ^%ۋ@gg>Æ ܎Cbk֬fCKK Ν $''Cnx4,^_k Kt}O|OEN:婺Y|2rsHnmNUPw<@Ѡ z)))VRdee!44>GPPq>~tuuØ3g`…0 Xtc+9F/_Nv}Wr]Nm6._?CV}mOVemy;V9SǾY"""""j 8,, xqi<{y,_v(@ooc̙b ȀbVlVFf̘_|>6nJσx544?sŋݍM6)lpߏC322[nuصkqZە+W`Zl2]}5WVVV^-[j⣏>r;>WsiӦMb|{̩vWѣGڊ3gŋə뒿׶/Umkk2k9TK^ݩcY"""""=m<~7K/"Z)++QTT֯:xBnnFDFFzǟEDD`u!99yp'HNNƱc`XF7ll_$ݼz:JusQ[gg'dٳaaahoo^w!hۏ?8f3$** ۶mڞzUfɭ7sSJ,7UmMgsf̚5 QQQ$ 6mܝB!!!$W9iN,]pqwtt̙3Xt)p5$%%AYfa޽crrr'xT_SYۓ&_֫-ozzt' .>,4 ZZZ0{lL>܈TD5?.sI<3XhJJJ\7o qxؓ5=eMMR! шBY0 ؼy3[w0ynGXX߬o%*=) wSGΞ `Gm6>s444`߾}GRRt:q Ek{2֕eWK^=SlUU***j!SOٳ|FDDDD2~X]]Lˣ?NBpp08l6CZZ;o'O˗~}pp0\~C?:ĝc<8kŊ*9rX,d2!>>O?4Y ,@HH~afJLLslcc#~}W9iNLؿ:jw%Kn(t:x7p#22|Νdeea˖-HJJRZO=^t2WOD5`hoo#N6X,9s$aÆ 7nTZsͩQk4q1444$ 믿;,!!Av6֝eNԷ'\j^=Srj40(((G?NDDDDD*½ש{nqnl%2B!L&wxl6qq144$yQ]]=|pkme^Q7Zn Idbm@Nw֫?^ݩco.^*E}}=_8F?L>#IZZZCUcs\& âE s n޼[kۗyuSGjRz z;uM6a…hllDff&֭[DDDDD*čG"""""""""R\S@DDDDDDDDDJ#)DDDDDDDDD8n<HDDDDDDDDD#)DDDDDDDDD8n<HDDDDDDDDD _obB~s8Ʉ:fF$IR|lO8wrh~η9uoz55KDDDDDG5fǁO?P\\1ǔLد$IHMMyv+ɄR! шB`0`^?[;qZDDJJJ`2|=O|OEN:\rV ի?lk ,M""fY|s,\zK.jl*^qn__~唯goۗyU*f?9ဨ֖okݚ;֬sȚ%""""@~O(cǎ)[̷/tNʿjE~~>*++&r-ڞhmySfC,`5An 7q!}FFFb֭گ]xcjn}oޮ\Պe˖aڵhl_QrիeXV|Gnw {O6 eJ׳7m^'3]?''GEkk+fΜ/"''gҮCj֖kj9dQ Q7|8K(**ªU ׯ_GQQ[0j1** k4X,_ݾ}Gcz4559=fݺuHNNIޝ ̄8v,f;1/IvOsn^=o%:9uEEEXd ك0C׻u~}}}χbm۶}mfkٚfɫ7sȚ%""""@Ǫ*!11V/ 1{lD[Vڵk7p1oXq{/fboU\uuuXr%^{57L_|r/xn644*lܸQ5k^n^':worog9u.'f\ (//Gkk+$I*++;?KHHug}]nͺ3ׁשYTTTl6ׯm'u``@ttt׿oڽ{~]7qf!&I;l68~󎨮x~~S] D:˩vooZn$ zӿk5}vqU&O.ODDDDD@#jV/^,[̣|A$ࡇL&ۇEA1.Aܼy1119yuuʫ:jWSk5i&,\ĺu/DDDDD 6h1DDDDDDDDD4n<HDDDDDDDDD#)DDDDDDDDD8n<HDDDDDDDDD#)DDDDDDDDDxr_%oq& uuuA}}=0I044|ippp4g`2p*_^ȵMDDDDDG$!55u\8|lqt:Ozt:g#88حs2L(--0(,,Dmm- 6o .cslwr,osDDD&'Wo*_SjRmS}zmB}$*URR"2ocfxDjj$I!fO>6'v iIDAT;wN|WnSOOlZdgg !C;wN !xWD{{[cͅ9ϩd2Ď;:fddDsU9m,r]rad/9yj뛈*xSqZ0<< !>Saڵ>>Æ >'V łY6 ---;w.*** !rs,Os#$$.]}L^^kL9m,r]rad/9yj뛈*7KKKPtttulJJ 4 ʠ둒hڊ#((sc?pa̙3pB z,]ԣU 5222Q4w^~=Kv)*N6 /_9əGWwM=o|OMDDDDD)Xm?˗/Ghh(x Cff&~m>}<!:9s+V222`Xja6jh0223f_ᅬ7:wY r9s`ƦMY 裏";;;vWos,rnZGee%ԄVUm}#ƢG/pСC.[kv=>'V Gʕ+ZXl֮]Ɩ G1+++ WƖ-[`ZG5W7n@XX6m,ODyfͩzӷm-''GEkk+fΜ/"''gҮ1^J@moo[ML7M>Um<(**pWdxo^z EEEXjՄDEE!77ףF#"##=l߾=Ʌ;c(--Err2;2a#WZZPPPhߒ$9lC~~>m?7QQQضmWytȩ*ѷmDQQ,Y={ ,, nu!99gvUߞҝz3jfNE}zfh4jb׮]m nm1y]UUnۅ HOOwCGGΜ9K"((׮]CRRҸt^|MRr[mY}{˻%^3 .>,4 ZZZ0{lL>ԈTL?.#N#Mɓ'ׇ{3`ѢE())؈$$$ 11ի;| DM.+::zܛƚL&BdF:w޼yo`͛ܺ˩|yvaaaȫ7=Q^Q^> دfݎWb߾}GRRt:q uס?նR߁pTPQQ TWWCz gϞ4"""""Ǻ:رchhhmoqI\|WWW#33/S6iswM՝y)zNr9;XQUU#G`޼yX,HOOdB||<~i5` ? ]J666"!!A5:y}SgyK,LtxwoGPP`ܹAVVlق$ҟ_֡?նR߁pT,XW_}xG'x҈No~SJJ?7o?P|bxxX455y4}{SN .<{n?㭷y2*$IrCmm_nͲR<-ږӿͼz~Tʕ+B߿_<よoΝ;wF"##ֆŋƊ@hh=z555EHH.]UVy; gΜ$Iذa`hhUUUظq1~T͡˜:˫ܶ;i41444$ 믿.k~RjWϮJ׷$IlFAA֯_?v"""""R@I_*:::DGGv-_cl6 !0LeАxwDuucOuZN^]/s,raZn$ zø|YV5S޾}zhkkӧOa""""O5PMM Μ9V9 IZZZCSyL&۷-bAܼy111[r}Sgyۦ|r ).5/_3}Qߛ6m… ؈L[2LDDDD<)@B^IENDB`sympy-0.7.4.1/doc/src/modules/galgebra/precedence.rst0000644000175000017500000000061412253362407022644 0ustar georgeskgeorgeskPrecedence for Geometric Algebra ================================ .. module:: sympy.galgebra.precedence Function Reference ------------------ .. autofunction:: add_paren .. autofunction:: contains_interval .. autofunction:: define_precedence .. autofunction:: GAeval .. autofunction:: parse_line .. autofunction:: parse_paren .. autofunction:: sub_paren .. autofunction:: unparse_paren sympy-0.7.4.1/doc/src/modules/galgebra/ncutil.rst0000644000175000017500000000115012253362407022041 0ustar georgeskgeorgeskNoncommutative utilities for Geometric Algebra ============================================== .. module:: sympy.galgebra.ncutil Function Reference ------------------ .. autofunction:: bilinear_function .. autofunction:: bilinear_product .. autofunction:: coef_function .. autofunction:: linear_derivation .. autofunction:: linear_expand .. autofunction:: linear_function .. autofunction:: linear_projection .. autofunction:: multilinear_derivation .. autofunction:: multilinear_function .. autofunction:: multilinear_product .. autofunction:: non_scalar_projection .. autofunction:: product_derivation sympy-0.7.4.1/doc/src/modules/galgebra/simple_test_latex_1.png0000644000175000017500000024366012253362407024502 0ustar georgeskgeorgeskPNG  IHDRMhWsRGBbKGD pHYsaa?itIME $- IDATx{XTu\PgZ$"Jx$@E1-tD+ĵuWmeϪlmTb@- \DQT /y28< 3*_|g|EÇץQFBrr2x>qUTT0:DDDDDDDDD,J% 57&I! """""""""j#.:׮]C׮]cjgZR 'WVV?Z̙31h ߰e`Ȑ!x̺.>>h@._ d_~7Ǝ_9Fj}ׯ_G=`kkWv)8q ѦMt:KB6mj#** zr9"""\ΉBDDDDDD6m} -jڵkСCѦM,^Z֬kǍ۷oŋO<SL]Zō9r$*++qeYYY-rF-csALL^퍿꿽RzLh4ذazMjdLT@o=J*TUUY<ꮿW3cq0hS2Hb8p ֭fȑJغuslž}0x|'O_W78端µkp9 u999D6m0vXlܸA111Xbj5"""jV0fDFF">>YYYGff&:'N Aȑ#xWn:Ν;#G4f\\Ν;D]166Fpp0߿7obԨQX`֬Ym۶qEq27ؾ};֯_o:QFaڵBDpcu>| -Z=z(**jP845Ϝ=>WL:UUUf?#=z4r9/ ""kE"݅c&۷ѿ1 ,W_}ſDDDDDDDOe1w\xw={޽{;}4,Y3f9sXv-0uT}ʾ9uO<iii̙3<| 7ԛgT͙?sgl.bf=#8wΜ9777!f沵VGrxzzC"4H֛5k:uĿDDDDDDDOTWWO>1CIGxÝ;w<|TJR/2rrrp5tSHNNӧq;;;$$$x:Fi8۷z\\\p_~DfrnIݏγѣG# @o5S}4gZs,s3OOOxzz".._agggֵnj0G5)c`)2xH$ |gИDâEx_Mu}̝;aaax7p( dgg#++ ?3ΝÇ7ږ%u4doSljrssy۷DŽ {nba_6mڠ{0aBѾiqj z,^fIݍ1'hY2枵HQQ;C,O>{.N>q5z}UU՘]hjDDDDDDDl]F7n8(J W4>`6m ޽>Y;h_'t";rt:|||<\=#w:t(28q"rssQ]]-;vtzmL&ᷠF&( !A3u ظq#t:lllʦN4}8۔)S%h4ͅxܺgǏGYcq0Gs5gĘ sƍH$pssk:6mO8995Z-ڶmk,-- yyyODDDDDDH, h\p8~8rssoaprrF-[ %%'Oā:/q=^ELKKpE޽9slmm}vcsΝ;ݻ7?4Z FN+Vw1csN:o+D".^44V gggd)hS_Sbg3X/^7no߾Od#DGGի/<3T*K</Xt)h9Tc6(_| 3T*!fήVQTT'O2 -QRRҠ,22 .dZx$VEpuujT*8::2@DDDDDDD-_&""""""""fT*! z# """""""""s#=;:׮]C׮]cjgZ)R iKSp X,F6m ЧO9"._ dg- |j}i11\A.)%"""""""jZ;փ7cɘ?>fΜgbĉ4)S`׮]VC ++ˢkгgO,^ œ9saÆa޽(**z&155ҥK@D"°f>DDDDDDDDXcΝ `oowy7oDdd$%%!((Ȫ>!''Ǣk$ zԩR)d2<<_ppss^gt>꿵Y?Wsz555tZc1 P[[k:spvv6/'jnj b޼y±CaԨQXv-~G 8PHaرظq^= 3f "##_SNEUUpN\\Ν;D]V{Ϟ=H$+8r^y[sNxyyȑ#bbbb jDDD 55լ۱~z~BB/ GSJDDDDDDDʄC@WQQh*ʪW͡jqM8::޽{Ű7GS4 n߾Ν;C$5y,F5y\HH V^ oooߟQ*x|RlllXxAD"{9t$D"ĞcH$pvvn#pC;w@uu5j5DDDDDDDDL ;;%%% "##pBh4\|1Z JGGGR*L<QR*0DDDDDDDDDlBBBZ׊GNk׮k׮{Qը=ykׯ}P*ϴm""""""nW먨(HR9r96mڄʧ3g<')) ӟ5k ** zڵK8~{ؼʼn'Yqg^8><6___۷ߣnݺaػw mmm1}tl۶M[|F~~>T*jYqh4ذat:߿t:T*DNCEE*++P^^ޤy[֭[z>W12g9:",[f mۆƍ;wbܸq7njjjexyy믿}cmٳ BPP߿7obȑfk'>>?o߾8x:dtw%ꅄ`;wkp^pp08RUUUfHi?s!11k׮E~~p͑#G+`ݺu‘#Gs>W^y-BLL =`YN> BB~oc4NII ֭[R?#Gz$2Gyy9Mckk7Zה)SdgΜ/~7GGG ,, 'OĐ!C%FMǏ̙W/ӅoΙ>>FIIM=z4nܸRaH,\ 9z(j5p1o$KĹMDDDDDDW^|sQPTV y[TT{{{tMDDDDDDCTB[~=ADDDDDDDDDb޼yk#ܹsuƍ׬ǣgϞ\uJDDDDDDDDF)#Gpy,^lX8;;cذaO%KaaaF,|2 UVVݻ֭߿pt^ՙ3gxWѽ{$%%!''yyypww\.G]]y]vuuu Bh5=%"""""""zڔJe\>c|ǸrU/]YM6=qj4ڵ ӟ &`ʔ)صk(_P?DTTg 'N_ɦMsN^b}Νw{nL8%5 ,=%"""""""zgV Z lٲ>>>y;1k֬g6Oϝ;3g`Μ9]g=m-~cYYpt:… kp^nn.Ο?FϣZ(DZ_Ǚ3g%%%DYY^ݥp \t .]VzLnBMM*{nTA.-~6u ޽{WTTNmkkӧc۶mz߿SN5;0}sNܹIIJӧ,ƍ@TVVEk4okZZƎ76(u/^!ClC$ I6st:ܻwrbeLHH6o,s՛8p0t:VJ1c 22ʂ?233q!8q fl۶ %%% ō78;wXv-CaԨQXv-~G 8PiLL VXZ/=5Җܹcbʔ)Xf VZ?|ٳ[n ooo١]v̝;WX<.`PP~Wܻw***j*߿ ̜9'Oę3glٲ&;w'_XAXXN>>D^^Ά '̓O?W^6IKKáCp????~ ^É'pӧ`6tO rJ>}R{ANNFzDDDDDDDTH[zhkk+4h?acH`РAh߾=nCŽ+%&La\ee%JEu|78~8nj| S%m_}^۶mѾ}{3!d޽{IP୷7|cpww/ppp ?<{;`ٲefST駟L-0sfٳ1c IeCU2M=cs6mн{wL0ssswSNfu$""""""g$ݻ[[[hZ$%%}}*m+ 7~_/7{xZhU zӦMҥKm)4 RRR}vΗ:tsڶmkq?RSSѹsgZPطoVX!qw GFhh(8Р4t}1+xԩS`t:'%%a̘1zs @ģ1kkĉXnO;v GDDDDDDDOEI<~gٳ'N:]_lll<܍8!!X|9t"\{Ilٲڵ|~)))عs'ڵk?=.]vرcسgf̘ʸx,Z:Odn!`ccSbҥkV|ANJ$簾ѣH$3f ^|Eh̬I&6Hko`|(..D"A@@͛GGG7Y˗fcǎ ڵKof0񘒒DXVVLT*!33W\{/׿?ĉq+FMUVA/d;w2e{ڳgO\K.믿+WX K"""""""UTT~Xz5n޼ X6mژV]]m6iE1{{ HjX}i+VE޽q'Fsǭ>Al:ܹsGHR> n݂T[[ Jekզں}6:wD-!Ju}o:>m> W^j׮܄U2q)۶mŋo6 YQj֓JO5<\dƦYm=-|w^'aʳj}vD"rܿ2 +͛#Gbf'Ξ=DhZL:={4xNCDDn޼4f999ӧY""""""""zJe՚,pqTWW׷Y޻w/:wC]""""""""2G"""""""""jvJٳgٳgsӦp5tڕ0@V Q+jv?Μ9ݻ78')) 999˃;r9;ڵ 娫CHH oX8;;cذaVǢ:uXS\.GDD R;v ** 橴光'b/k4lڴ ;wի!Gaܹz?wĉP(.]hYYYشiSbXh:_$!,, k֬IDDDDDDDԊģV JKKܹ3{nTVVꕥcȐ!L&o *z<[o@dd$&Mdݕ+WbԩM:uX[\. Ο?'jnj L0}mw ޽{WTTNmkkӧc۶mz߿_Hbߺu 555M:h:B:PWW&o/'jՅ駟P[[ku:3zBBByf;ws58/88@ii~UUx 0T*OޤX\֑1c 22ʂ?233q!8qt͛5j,X:طo_$'' %"""""""jZ2bbARR|||&ϱM3e,Ypuuř3g~MÇ111 ɓ'1dʕ+lnnnM:,h>>>D^^{= e˖>S\z:JKKBBٳg#,,̢>J$TUU %"""""""jZt199;{@Ϟ=kVK,irڶmYfaX|9>DDDDDDDDdxLOOǐ!C~ MoϞ=H$ƒ0fDFF">>YYYGff&:'N @HFܹsHLLڵk/X^߾}̧֢bbARRUumݺ׿#믿<vvv?P\\,///;.uv|$VD"AUU""""""""Z:99;{@Ϟ=  3ww\,UP(ЫW/ᷭ-ju"VEYE'j*au BBBPUULfUJW 3]%ܱz<DDDDDDDDDhZWTT@" $$$48?-- yyy ' ۮ];NK,(B!$sssNNNqYc3T>X5Fբm۶|:j˗-[֢:%Kx زe 222 V˧~wbȑ±t_(++ý{K/ )))FQQz聜ܹ7n܀;?CV۷GeH$tM[oooc֭[rTTT%|r$''cڴiO,Vx"0zh>!DDDDDDDDdp*** BRSNzCBBmrsٳ3fxf[z5ѿl""""""""RlٻZT666 luZXnKuu5j5DDDDDDDD$L<6#dgg:  fQUfhpejT*8::FՔJ%DDDDDDDDDԼJ%H$5`xln:׮]C׮][]j5jkkaooIDDDDDDDDVS*x-ԩS8{@dfkTT  ڶmkupvvưaìr9"""\'*vޱch, ={ŋ1h ̙36l݋"he,##]t19˶m?ҥKÇGVV6mdu]/5k """""""&iGV "55բk% zԩR)d2<<._lnݺcezطo""""""""ZO1{l<%%#F PTpssCff&\ރ3MUVAml۶ j5<܍7 yRjLAAʧ#% IDAT&`$,P[[ J5鐐DGGܸ{3V^ oooߟ7,T*[-o3[v=Z ݖjj&IxlF>>>FIIu!22 .$"""""""&Lpuumu}WPTptt$"""""""")J&y)JHgϞP͞=+NõkеkVwZZFՔJ%8x };uΞ=k<002Ye~:z[[[ۈ3 fU]/0r>!DDDDDDDDdV;Fcu^^^ٳ'/^AaΜ9 İaðw^YF,eҥYv؁T?{ "!-s@TkaiŶ&Q,38U괮;sjOAWV-toVtE[TT%1r /"@%A g-߽&}O?Cƪs aZt:H$%!!!prrBvvv PVVxGҰ%ՌFYb{rSt7nܰؗs!??ߴˍ7 H|TVVXv$6_ޡ^{ OƯ~+(J={PXXgϢl3wyn¯kӱSNaǎ(//GCC~'?T$TUUa8w>3ܸqƍCff&8^@aСسg$ FijСC.^ 0/Yfu9wÆ [zr444`̙Qmڴ "FNh`VH$%%Yݸ… q\{t,Xo֭ 8DDDDDDDDg ±wvTNNN'|ۤ^XzIG"""""""" HRuuu6QYY Fkr0.\jA˗;ڮjŁ$"""""""") H.] DhDcȶ͍HDDDDDDDDvYt)g<>,FW^ň#Lz=  2`pcǎaĈ]UUU%[RRZ<}6++ cƌy@{: eJKKyUoTjvL&!11wS]}555=>tu?55'O_=***m6t:F`Ũg^ñcOt3k.g<DDDDDDDD`@$}vzaa!]"QQQS}AAA3f ֭[I&aǴiӐ>QHHHI`Lڢ"$&&⧟~Byy9FQF'`59h#>sK4hPY#[oagg>DDDDDDDDprrB}}=Adz>kL& ϟu}Gxxx@*زe \6ܹs}Ô)SbŊ~ywOD"Zy!//ׯgh4B*Zw|РAhiiU?y """""""rx,,,ٳVjs=z.]yF#t:)m=P*^Vvŭhjg[[̮;wnnnD2hmmţ>S{;oCdd$d2}}vc0y """""""z>x)L2-—_~iS'66C]]VZ7nnɓ'#""o?'xW6KpY>;;fB|||'OĢELL ''qqq(//sVEXX y|70k,\pn˳#BD=&;y?"""""""͡X ZV=zOuÈBpp0T*>C… \OOO,[ |imw׫T*,\eeeP !C][SS彈I$7ٻw/6oތ1KtwwGff&BCChP]]mZhz k֬q܇g=ꫯbܸqX`#]$9qy H3g<=,RGn\nn._O?0fBRL#;;l!Ȓ%Kht:1O׋b>rzihhkuIxDDJ%FevR;ǎkmmC>|8? /L+V !!bo~>'N޽{͎>0zyȐ!hv]FF.]p,]ԦۻC8уЉblܸ4hjjK4hFٳg[,wssٳXŋz]6 ni{fIOBCCgk.--p?Cbʔ)hjjBMM) zx{?6lV^]v:f[鶾_~ׯnj3lJ"""""""aZt:H$%!!!prr2>WAAʺ7o ̒jܮ\ 0prrFѬy{{iM{{;-Yg};=mmmo}>:gǒرcv?ǂ //ϮcwriT999x؈)S`ҤI}A.+~GZtͮ]ܬW^[o9sʕ+]@'NH$“O>٥-]3ЀgΝ;C@@@T*ۇj/^SZ,SN!99...x0yd,[TnZ~~~8{,jjjcP*?>6n܈aÆuY]pIFDGGcڴiJHH0>cM9o߆X,T*ٱD"-h{""""""""G%`t? ZxyyYLt;"22IIIVٺu+BBB0a„3V >7ӱ`^_bb"V\ivƍ̄+y 4߸q#6md? ׯpwv/sm=q灈 ſNё9rRO=Tf<`z|1zw܁L&붟tM(ѽ LRuuu]v݇ިFqbT*$%%utwIGш;vtٰ/@DDDDDDDg<>$ 5j}918qV|}FF<==1uT灈ZQS(.[  """"""""~l2xF#^#Fz  2d~ǎÈ#0vX2.CII jkkOڬ,3Ʀy """""""Lii)6o|o>_/_!J͎d2$&&Ν;}jw}MMM%֭[n8yc۶mNhDMM /^~.^{5;v Or[ؾ}{j@DDDDDDD4P8|ⱽNNNG~~}3rdz>kL& ϟu$ F T* e\r,++ùs܇)S`Ŋ2K/&&\~nnn&AZƼy󐗗sa4!JA}y """""""(>XXXٳgCV#55զ:Z[[qE F06wRXl=j+&\\\`4L FS[PWWgVܹӦ>A$˸֢裏 Lv_޾ޏ灈9|ԩS2e -Z/jКcǎhiiG}ڬ<''o6{$''ڵk}*WVV&Nm{kWLӡ O";;fB||}}y.A6A["DŽ}#:r:Cbj8z(T*U/..FJJ 222M򄄄 00#GDMM x B"l;A```mH$6ow^b <}:~zlٲ׮]7|\yaРAv?p <3vC"gΜAPPP_#scnn._O?0f:tA7옏9UV믿F``)TYCCCl:""JFR&k,1vXv>Ç|x ɰb $$$@,СCo~csGĉػwo\t굳f꒰ҥKKNDDDDDDD:X\\7+H455ťۻ,moo7; {*ݽW$44buliIpii)܇ZSLASSjjjLЎ?\aðzjڵˬlʕ}n/cƌ} RώþQA"%BBB.ܹsQTTd,JKKVsM Md:fTޙ7nܸmlR:{ᅢT7hs\nJ.$??}6ƚD"D/υ5DZc~NA@^^I"""""""!999Xd QXXh:o>r]vu8((شi?oG}w??o{nb۶m|r坩T*|ŋ:u2K}8ul{سgO򐓓᫯ٳg_wFMM v؁k׮n]mwׇ,lذFHKKτi?cM9oƝ;wOƍP(L7n4I$W{Fvy """""""D:gIA?NƂ ͛6ljkk777SB{EFF"))j2j֭ jL Z-<<<܇JOOǂ z=F]:߸qpuu?AKZ<DDDDDDDDIP8R$HT*-&;===! www[OѣG-577C;Dbұ>^okc`^^^xx~K:SۺbP_8@DDDDDDD ART(..F]]]x]v@*++hRdF;vŋm~<DDDDDDDDXj ˗/tLCkh4ĉhnn6m8ԩS,}=DDDDDDDD}P( ZtO?e4_⋜HDDDDDDDDD_fsGb4qz4440DDDDDDDDv:bΜ9BH$ T*3^hDpp0?>>ɓ'c޽:u*Ə_?g߿|ArA> [nX,_w3;gAZZ̙\B > EhW,:a R?E"_""""""""9R#F ..iiidgg#99ǏTz'kzzzbҥHKKÝ;wN:)S`Ȑ!pqqʕ+4KCKK VXaJg1117o]TG_G(++ùs}S& ϟ 3gDDD^z%9sP*#GDpp0222̎t:>",, 3;СCVuu5J%C^[[Vba:z=.]v孭AΝ;/^`0;n4jԄ&OZWLtt4~Glڴ aaah4⧟~z"##{n7oE"""W_t&;0qj讎޲VGjj*&Oܾ}?x ^mmmbcco>aժUqY=)))^Ƕmېo*DII rrrrkߏ;v}kSÇO .._|&>{U@@rss+ADDDDDDDd7P.cѢEDž 0v^]؈nquuŋ/9skJ̙3P?iv兔DEEoŔ)SL555 ~~~vǢ:zZ .D}}= B IDAT !˱l2DEE)((ÇqJ‡~M6.^>}R8wf̘h48q|r#%%4 5 !!! ȑ#hpITTT_?,b%H_ """"""""8|Ә4iñzj9r ^{;;;cɒ%ؽ{76l#bDDD`޽©SꫯPbߖ,Y`DGGCaĈf噙pwwGvv6 ?cwS۽1n8c>>>8rVZe_Dڵk{V/hz /^c]˖- h0a煇f򹸸%(=gO,fϞ4.14hFٳg[/ RctRd2L:ӦMwRΚ  XtJfʕHHHhYټyPPP`G?g ǎ3C.sEQQ (--Z646hoo3%l aըÇg}?8 77| =;w`„ Vbxxxt7d"&&ꊀHR)))qEaرpqqpwkyCEvv6BBBx,:۰arss1| IIIXn WWW$'' yyyկ~e#<R;wB.#//xGgH$9Ҵ7b1kcΝXliIuNNك*^Ԓ .3g/Qlڴ "FNh6ܼy^^^]"##dZzz:,X`W---gA?b`VlZv{lݺ!!!.'""""""" c/RŤ#DDD-9~Xz}$zZ,Hm5*H&%% J}K:677C3HDDDDDDDd#& Jb=vTVVvy%(\p&._ ___AzhZ3W{ ҈^oJBH$vmxB""""""""EDDpf4qU1pPzDDDDDDDD=B#6,99P5k֘8Z0xzzϜ9<?QFu9ѣ8w0n8d2_6矣mmm\.jީP*6mͱ\-R?e2mۆ(d2R!7Yx1\]]cƌfeӧOǁaÆݗaΜ9ؿ?>. 11}n X/~gv3<4̙3r>|x,**Bbb]\-{~KDBll,!l䐉GDpjDGGh4233c(JDOOO,]iiisY٩S0e 2...Xr%fIvhii+N>lyE:luأ ΝOL'''?DDDDDDDD6;jD"}?~\~@0rH###N鳫+°o>:dJjUWWCTB,>䵵hmm+CҥKhooXj\ܹۺ[[[qE FZMMMhjjBKKT8x %l v=x7oHNNo^]k4O?uc=ؽ{͛7_yꫯP__oSSYYY8qbj + kubɈ۷?'իf:/66C]]VZ7nՓhzl۶ L ''qqq(//7vرcZZZG믿6>|O<_ 88j⳧X 77DDDDDDDD6:z_y$''cɒ%xw ɍHJJWWWݞ3w\k/Μ9ZfM>^^^HIIATT[L2T^SSgw,ձpBף rr˖-CTT霂>|GT*|شiŋӧ!JsΙahp cf/..FJJij@@BBB#GBɓ믿YJ"DDDDDDDD6pģ3V\T!C^/YwƆ %b1"""w^DEEԩSxWM puu}(oK,App00bL#;; A{1S{̞% gFZZbq4Fٳї^ii)Muv&xxx۳GDDDDDDDD}'4&-d:u*MfޥН577///XtJXr%`4dV6o<ѣϙ3cDmiiܹsQTTd,JKKV5F݄3%l a#55hooo,bxxxt7d"&&ꊀHR)))qEaرpqqpwkyCEvv6BBBx,:۰arss1|兤$[lo􄫫+䄼hmm9VZ """"""""i&]_zz:t:D"ZZZ0k,#++ EEE ٳgqa; e* >eeeaÆ 0FZZs_cՓ/bԩvD:V 㑑HJJvHOOǂ Zn݊L0MDDDDDDDD}P(WkG%ѣvխ!?ainn^gґc?RT(..F]]uTVVB<>cڵL"""""""" Z3ApezhZxyyq f ҥK>ԥ?7b~~~P(D""""""""ҥK9㱿F\z#Fpm0 2dlP( uĆ%''#j0fSفP[[ AOOO|w?a<<<...۷#44j[#5455JӦM)V^/Lm۶!** 2""""""""C^x1\]]cƌfeӧOǁaÆu[OPPƌuaҤIX|91m4dddmEEE 1|pHFBB&M{g!11X{D"oaZF,ZHسg~ix{{JtUUUP}jWII Μ9˗w){`q6VB'***nW|+̝;X{}g7os=~C vWHǏǡCׯ_^'-@}}=0x`t:ܹshkkC]]ٵ `Ν뭮R͔Wmm-Z[[mU;S8x !DDDDDDDDd3Gśo7|oFrr2~Ugzz:t:D"ZZZ0k,#++ EEE ٳgqa>k׮`]Ժ ˗/w]CˋIDDDDDDDD6S(L<QR(FDD@"0DDDDDDDDD/"""֌GшWbĈzC a@+"""""""B@j3gΠ* cƌ}a4 u%''#j0fSفP[[ AOOOd2l۶ QQQdnw}VrbYcc#_8;;\jj*J%MPƣseXQ0ZbBPP4 mۆb&O{bԩ?~|-^x1c ӧ Űa"QQQS0f[&MiӦ!##UUU} (**Uo>8p.]F<EEEHLL4AG6ֱ""""""""3`xgMG8d?Ri9% áV h*Ď;T*!Le2p^["`HR[l+W2;wױ*>}˖-FcŊsMy=܆{2VDDDDDDDDDⱺJbysgΜK8s <==T*SPP`Y$q!P8x]@}}=NgJ666ommm3]+vXݺu 7o˗mCmm-Z[[mcaxXjCf@$0qDei&Y< hnn6=x7oHNNo[m @nn]IOONH$“O>l̚5 BQQBCCqY>|'OĢE`4 ..彊դIp5[ :>M}jxt7}kmϱ""""""""" cMMMvXh? .t)/"222DfKy_yd2,Y3gvYD"1%j޽ؼy3RRR̎T*,\eeeP ;233 Fj\rAAAxꩧf7ױ ؾ};l|lsoƣ7cїֆ+""""""""πH<644fuviL4 Xz5A0+H$ؽ{7Z-3KX9;;cʕs{lO{{MGWׁk,1vXgWWWzb!66k׮~D{t7 [~g@$-\kjjBvv6Νh\~|9 _FKK VZ .72N*ʽ#44; ;s6@iiib!-- {uuu駟mzގE_~f@deqƍ.cccP({>>>β{wၗ_~uVYvFbv8;;[,+((СCwܞ{/MR+8z(\\\7o/=? IDAT|xzz썞ƣcїu3U*{ÇcժUMǯ^ H|G=<<=YǡCpe͛VrEL:bٮ]|)lٲ...xgϞ.!''999W_}g/@II vލر׮]q1ddd`ԨQƪCUU?K,>̞}O5fΝ;o>ߏ1Bfru,uu}5k{qZHz\jVp1&&F͛7W&MԵkW 4HӧOמ={ԺukIw߭%KG -++ɓᡙ3g0 ^~eիX={ʕ+ƍKL&ƌiӦ(]m۶zս{w=1b}JNNq.,veW._kʕJJJҠAvZǮ]pB]BR.V۵3x -X@nnW+ݺum۶cǎ={ f͚Uh{1f5nX-"0K[w/VմiJqL4ITΝĉܹ:T!#G(99Y5\%''kƌ:uL&~g=#JLLU?\jpk+2x<~5kV&I!!!9r}Y)((RQZjeRnL&&O[jՒI%IZjȑ#pڵk',0|I]~%`ii222m-ϟ_\?^1ڵk\;vVs8s挊k֢6QU ^+ԎKׯ_>={V'Nԃ>X[j^{MrssÇWwyUDjԨQjŊz衇\zs;Z\o\Gl޼PXX<(dyzzVYttDқۙ z2եhϞ=ȸj1̞=[ƍsAvV̭֒dXt1iӦSvv4iB& @#G#b̘1uţa:uZhQ㶧OV``j5v^^JJJԠAk>WJZZ+kM@@<\)y),,LqqqJJJ4`mV_du릮]ZFGG}뮻$I*/hرsW\3gbTPP|}}5}t3FvǼsN۷#Fl\]*++K)))jݺkw}f͚{*qi Zq\2111j޼4i]jРA>}٣֭[Kn-YD=zPppp^~eիw={ʕ+ƍKL&ƌiӦ(]m۶zս{w=1b}JNNq.,veW._kʕJJJҠAvZǮ]pB{=.b]+8gϞ-ZЬY|r9rDqFEFF*88X_#F(44T| ð~n:}j֬L&<==`w$I5fnvM:U'Oq.<{ڝ+IJNNVllFAѣ_u_4f8.Qx<~5k&7ᆄhȑzg 5k~M&&O[jՒIRӦMlUV]|9 .]v22 C999"`VVVZZ k[Ţ8WϟWDDuv)77WǎΜ9⫺U@Dqg^>ٳg5qD=5UVzk)77W>|;w+ϊ+#ɤ>}hƍׯfϞk׮] S||/aZn߯M6i֬Y:t]޽N>-???Io UVCvvv# 'NTz^a9=ںu~Z /Wխ[˞_VVVy9R^ 6mX?}~~~˻\6mƍgWjG]Ko*\+((ƍ5x`}JIIќ9sj5e_rW",,g^寧/~Jؕr˗/W.]4p@edd(==at֣. ǦM*55iӦi̘1͜9SjWs=SZn$999Nc=EU2,]T۷vl{}'۷ΫeggQFϕǛz:Q kp p+?^̲yCaa򜢐Ye˓KongZ+T ={(##0{l7Ubb U Z2ZKbѱcԦMMNNV ԰aZl5iU} ZZG/ @FF˥j u\pVgtᱤD+V`zԅL8p@ X,JHHPaaasz… z7$%%%LC)..N$ݻW'Nk +33]S裏駟?P:u ?sKǎӆ ԫW/:uJqFJ.]KZ O?}Zϟ6mzYf5}DEEiҥZ~:txvkN]x{5x`9rD_4n8w}Qll^z%z衇[;vĉ%I?յkWIқo7|S]G0֔)Sj4}׸q㔟իWp]pM&Խ{wgϞQsIӧOou/7mNIl$V```c~~~$>:$٬-[V}m1///vp}!C۷[?^$@?yWTT$Iڷo .7|)[z-Z,>|XwunA!!!Zxmۦ~M?ƌ J6m۷k׮]JKKSO=e}cfͬN:SN)&&FGU&MԹsj߶m>Sedd())I4~x=zTYYYjԨ:uugĉ2I2rrr\z"*++ysaa|c~~,yyy[nnn# .p8} 8}38xP'(<p8 Σ p˺xdAj8Zp8 #(<p8 #(<p8˝~I^^^2 CΜ9f͚n{ j߾};vZn]7mڤǏKyyy QXI 0l}{ȑ#9s4h 0W_?{uԻwo}ѣG5 Cnf2Ls=jذavcX{nuaI>WfV+V(22RS $I&I?ƍ:H֢$iʔ)?~'!!AvrhO,H_^?Xml9zh}G0\|̙3g$IfFԮ]uI?Vpp$_.I ի-I8p&OSV/--xfRSSl߾ݨQqFIҐ!CyfOSO=e=',,L۶mS 4bIR``~f裏ꦛnbTe]vٳglRXB~{ڶmkתD֢3˭YF 4- dXd+OOO%%%{Wg^Ztssl"h|j:yd߿? ׭*+h2d~*OrssSYYYg7b/VZ}ݧ~ZӧOysJߕ)66Vͱl=?rJZuϣm{ӧO_~qŢsΩAzWk4hg-Ldh*::BVS>S-_\7ԋ/h=b:n@@ $U.@F6oެnݺEgh%$$p]rwީ =t k޽ڰa &5kLnnnZr4g={tM7馛nRddf6lؠ۷޽nv뫯nݪ.]aÆUڰaC_l2mݺUnnܸ/^6m(&&F'NPtttN:/Zl͛Kyf=zTK,ќ9s԰aj ҍ7XϷzKΝSHH qU8Զm[5k֬EjڴiZ,={VAAAJKKl 7` b(--MM6raԩSJNNwܡ Ty^YY5M 6JJJU/j޼{1Wz1LuIOzeWz0~xhB/)֯_<%&&jǎ$B\Qxp8GGQxp8GGQxp8GGQxC6VVV,9(<š5k+XeffV:gZDт *9klԩS;yd6=@ QxhuYÆ Ӱa[Ǐ+55Um۶կ_?-Z56g+44Ժ=(\cƌQXX<>^*,,TLLL&z]o6ۥsrssul$)77W999?,X`=rerss5i$+66VqvLƥ eP=lpBw} yNQQTTT$ǯa5ky˾r6.IM~f̘Q*gEnnn*--io\}Ֆ-[5@)6gGᱎ^^h>>>Z`]4g͙sq£aq8?Tm۶V~h"͙ssujǎs֪0tjeff^Q}Zؙ3ggoviǎ.3 5]vo+44Tɺ%I*,,p~||.\Pe_?~,YrE&%%Uxqɔ)S}vlR_|Ei[٬4͙ssoQFp"Ip<<*;##ұr 6TxxxƍwE^Ço5p@waTEaXWiii6ۖnٳg(6[Uޜ].ږ繬F9@yBL"IJII7nDPd<|.TWVV KjĈ7;;w ]jӦrJ]v)--MAAA2L p_uE>Se_Umذt-ZX_c/_~S[Uuleol5zwTZZ*k!ޜ2>HV$mذAwq$quۦM* y˖-ׯUTTTT92L6tQ'OԩS222c͞&6wwwL&yzzZ5jdT'駟*00PJIIѸq+pᡴ4r-{?|#P=Aׁ}j˖-$V_/gϞzXުHYYYK97j|;v;E|X|J Pii,.~x߾}f2뱛nIgϞ+6[-RqUV\ޜߞ&.Ԏ)p w @ PxpnQxp8GGQxp8GGQxp8GGQxt!yyyWiivޭ\Gbb.\ IX,*,,t9}b2 O8군wo9p9.[xX,>|u3^^^"""dBCCuM:hΝJIIѓO>)I;wVZrXb"##{iٲe2L.lߪ+gmuo9pY IFAAӎWZZΝ;g[n5Əoak;rj/FxxaCOv9T'O4""" rOWc}JKKݻw_ߓ[s5^n^Oyuosm|p5.yczzrrr$IiiiN;ބ 6lVAAճgO())^]yYPP֮]PgϪiӦN9o>8p@Ç׾}\.~[jէOedd\ߔ[sV?:6 [ΝaW㒅Lj;V^^^JMMu񊋋6޲e4w\eff̙3ZpnJmKJJSN9lGu9kժRRR4w\~N;[8u~GmٲEC rO8zr>קPӧO׸q4yd9s~m}nUӞ:rno {Z\sxZqqq;&M?a(=={xA7zhl6+??_f󴊊4qDmذAǏѣGy9*00PֻᆱG})P<4~xby{{:O>z![ҥ^|* ꮔV{$[[\u:72k8*<%B`ffϟ_m7nCƫfIo,Yd5h@{+tkTTTH;wV>}n6&RU~ٳcf裏}ij}rkkVWGUy/0aJڿ4i"I7o\2224qD]-[o߾ 6[oI&j߾*ooo>}Zjݺu|Py8jWs/W]~o? gpi /p3F]?濯aU_]lݺu$/yNJJ u%%%G]aƆ oNLL4f-CiifСCFiiT{XvvѡC#++ ?|bpnMM]浾~ g[sոHqT3fЈ#*oڴ[ԫW/{a.vmIx{\II̙2:tH|򉂃տ͘1Cf]f7oڵk5zhmݺUa($$D;vp$K/ѣGK.*--uT_{lƌz'Xc=[رc5aӦMN?9ޭq-++sټ^/Sqh\\1l0CƮ]wƭj|gN9=W8j3f_ӧo1bcՆaǍB#;;pww7o\8wCQsߍ#G&HKK3:dN} V :UaJ"[1p^{&.> g\sՈԝW_}^dzX,ƙ3g wi+W45jdFll 0f͚e}݆aƧ~jt)ע9ܹx׍Gqqqގx5R]2ɓՎV\e8s~ǽkᄅ}[ۼ:Sn g\sո;pgի<==m<$???yyyU8~ZJɲX,ц 4l0yؚ$Y,]V2e 8Zؚöm4k,]pAaQFNrLJ-^߿1p^{}SJLV+qWRoFSa³\ń 4rH2񗖖CTTT$///L&{-ݫo~+Xs\ʯ0L.Ytd-^XG]T6%ۛ=p^{jk7 GF 8GGQxp8GGQxp8GGQxp8GGQxp8GGQxp8GGQxp.[xX,>|ˏ\\_jҥ*,,t1kKӕ#IJKKs1kK#""4vXyyy)55eUp\\yyyI&6a(=={xA@\XVVYǏj?~kܸq@\l2?^3gΔ$, 6lPu:L… ***҇~h=vE,}w2 f1bDP5(<3f7ިpiӦڱcGmԫWj1Tj#F(22R_f֯_͛7R;uرvj&{HG(<p8 #(<p8 #X,^x^^UZZݻw+77%瑘 .X),,t9}b2 `Xj/ҥK+!0 .hԩN;[عsRRROJΝUVZHҊ+{O˖-db8G;L0mӕ#IJKK/((O<<_~>P'grH&Jxeee:|}}/Iъ+t=h:wuu%PnZh~7͜9S .TllӭEusO+++K{y[6£+mܸQӧO׍7ިCGщ'4qDbs9_ /GԩSIgǶm4w\Y,\R͛7?Qxt!a*,,tBbMM0A#G?+::Z.ii>EEEdb\g<///Idr٢$%''k:r䈖/_REGI"W<p87R(<p8 #(<^yyyWiivޭ\IRbb.\ IX,*,,$Y̯aڷo,Ο?/^^s(X,^X^^"""dBCCuM:U;wTJJ|IIܹsj*JҊ+{O˖-d"5mu{KQxbU/RK.pbAAx )88Xӄ t뭷S:t$mڴI{yٳ%s[JСCϫcǎzenW3mu{>s \G;j"==]9994q٬EGGgϞ*))QRRvZ0 ={VM6y͟?_}QFFK6}>|Wsutnۣ[gط(<ڡm"""4vXyyy)55z|ٲe >" IDAT;w233u-\Pr$UVJIIܹsuWoIItԩ+SaaOqivsWԩSe 2B3ѹG3βoyNjw!///5iҤB!f*((lV~~fL&cc:= hx[jQMnFج,ioj.ͽ;5Xo XFÏx1[S/ZQr*6FcTdp)?fs0|y9sΑ̙3{Ok׮ :VFF:?^CƏ>H/M6С]!ǣQm?ۛ]N{ ~=IH7nٳgvkŊAsl6#áYf^6۫|UWW+,,L6m/aǣS޸-KP06mV5:V-"amNeddmO?;""BiiiC/--շ~HIRaa{9&өHEGGk͚5 UkkRSShѢ/;;[zw%ݸ%766V_|Őϕ4v4kvoǫBo-iڵ+h70_Cy[[w^̙3ٳg/f,^ظ~z'7߼aO6:d~0 fZoGm_k8p k֚zݎgo{݌u{e peە456Fb…oΝ;ب>@ZjvTQQ1v4s̠W_}UGŋGT/Teeu+W~w}7ݺu~m%''+..N^wB\f^7WI[ej= 6gߠ[[[,:uJeee$8p@۷oWssҴaÆ^Ç~{_ԥKKjܹx_\oн[{IUTTh~ꔔ4{;f4-lٲ6_ uX,M>]V50بp󩻻[AkX,nU3ϧJ~7̈́v\.ۿ:T?ÄpѺn路`;'>ǣgڍt+6m,Ycǎ)66!׫SJnV,ˤv`M;,˸p8{n;wN&tIRhh褻v`f#x`:G#x`:G#x===ӧKr$I>O$0 ϧ"rGSBBBXOOJJJdV^-˥8qBmmmZn$@$۷OںueXh.jnkޮ]TVVع(I}}}JJJROObcc3(==] ,PHH/^,I:|z)IҚ5koo1&n\=p!==}s]nI Oǎ500 .h޼yիe\(IRCCQEEEZb:;;ǥ]wq<ψ甔hƍZjoo@]]]|5gI?6(&&FҢ?\UUUz烎9sF---w[c[<0`|555ZtV"##d),,L b̙3{Ok׮$͛7O)))2 CGcneddСCJIIp8TXXxk裏 /(--MqqqڴiS k0~=I+Ţ .衇?&}>Op^.c0dە45*… %߮=00;w|XZJv]yyyxv]III9sf#Gŋ#ZÇUYYd9rDahʕ~_[nodrn[xUVVN:2I8۷Yiiiڰaw^r:tf͝;WGSSS/_|08SMMO>Nii?>ÛaZxեuvvn믿xS8F0l޼Yw~:::3fO<}'Trr^*!˥e˖0 j7se͝;wDk'|Z*..7|3hh?y***~z]~]uuu7-ðe˖vmSLќ9swuuAZhrrrܬAߵX,?Iisk/bرC999z饗??|>-YDg} 0F8y<0 = q,W^ѴiӴd;vLcR^veZeX(;'1*Ǝ±%ph:w***,tP.1ƎGB &l0kф wڥ2Od4!k׮[t:'l `cII6n(ժ [NhҥZi(h]?~T͚5kڄ ~=IPKEEE?{3f護޺6rutt(77WsP0""BZ&Lrvm۶XccMCׯeMۮ`h"x4 Cv]4!L>]wTZ*11Qjll 8p@ԙ3gTXX8hԩSc쿅 Q C?w/2܆)AOOjkkzui}4 (|JHHQII իrC0X 0h|FhfӞ={p84k,fXL(?٩ ;UUUA_DD҆_^^J7t:heff*44T*((`B!xٳg++++hf);;s].nmkllvXWW'IJMMURRҠW_)&&FSLѥKj*Mi݅˘0 v%&&GEE ;x\p[sN566>PllVZ%ݮڵKeeeA'֦uI ~ IqG<ڵk$9 ŋK>z&`R!x39%%%ڸqVSeeV^-0tEEE ?003gΨ qhҥZ %V[[ ֖-[rJ566jڰaMBh2߯Gjʕ4(xz9s֬Yڵkl6رC?^zItE e*-٩ f !痗CvٳrzǵbŊgNSVffBCCڪN & Clξ\.>Sk7|Sn[ӟn9o?XIII>ӕ+Wb… z衇tspWVk!ݮĠ񨨨Ax Jcڹs>J%''ȑ#2 C+WTuu5]JLLTiiu8Ѳ˓nk/~ h͚5jmm??ܹsjkk??qpVaؼy233ǬWGG5cƌx~~>}UqqN:x-[I2Es %闿 Ў;~0ڽ{Y.K/]i*-8vޭsΩB4w%tj tLGtLGt0 544󩣣CXTZ0z|>^y۷O555 ќ9sqFIb|>+**קiӦ}Ңcǎ_֔)l<B5 #s5uwwKNgg jllTBBh0&ax<#SRR7j=0Ң?\UUUz>Μ9 g<-]TVUAy󔒒"0xv+##CRJJΟ?/áB d~_GUJJ$ Nr:JfӞ={p84k,fTL8?٩ ;UUUA`DD҆_^^JzzzNrssLUL8?1{leeel6eggrֶmc $IJJJ;>O#>S͟?_}9AO4 (|JHHQII իr'NM֭$h40|ۚk.\SRRzzzgyFZ`BBBxbIÇSO磌tÐ>9׮]Sww$tקcǎiЅ 4ouU9\.-[,hajmmoZ˚;w ??_|k檸X| =-[l ?׿BBB}EӧOj 566jr8|֡C~К-ˠ[꧁󩲲Rwo~pb$x"0,ƱW^yEӦMӒ%Kt1rrQzabQvޭsΩǎGB qV^WO/I2 C |/DE8|>zzzTRR"0zj\.HۧRmݺUX,4`[-|>w}#WTT׫OӦM$utth֬Y֟g0@Ңcǎ_֔)SFw}$TxϹv횺%IN30>;vL˗/.\ IjhhPcc0꿫H+VPgg''"x39%%%ڸqV*((PWW._b͙3G---UUU>hΏ ̙3jii߯;v護RvvUTZ`-]TVUA!brr^b(,,L)))2 CGAtСCpG^PZZi&͙37>&7G~=zT)))4(xnn-I?So 2(t$ͦ={ph֬Yf5*??_ ӦM}Knbf}L~\':;;JO?t 9T~"##%IzN"##5k(44TJMMբEF|lwߕtVX}gVVVVИfs].nmkllNA뫯RLLLK.iժU]?ݭ"8q"0fZ;h۶mq}ϧGѣ>z=\L0^.c0dە4u… %ߚ=00;wQ|bccj*v婢bcv%%%i̙A㯾9/֭[o+99Yqqqz\8n[xUVVN:2I8۷Yiiiڰam?޽{/tҥKJHHҥK5w\y<EDDNMMMZ||c+..N555>}:~oV?>>^ҍ]zEp#x͛7+33sѡp͘1CtI۷OO>uU9\.-[,hajmmoZ˚;w _^ׯ_W]]ݐdA8 AM׿jǎ={;jnnVFFƘ*((ӧ~>Stt4=qx< aӐ^bveZeX8)8GLGtLGtLGt)!!axSS\.W;$0 ϧA0!<续yvRYYY X'NM֭$h}T[nUyy,˨:N0LG8 #s5uwwKNg`| ŋ%ISO=%IZf|M=cz7FwiŊ$TxF&7QPSSKj*222(x~Xmmm*((PLL$EqڲeV\F޽[6l5~GzЕ+WnѨm*-0ѣG"IG׫3g*>>^֮]+NȔ!ǣͦ={ph֬Yf5*??_ ӦM}v }`#xNeddQ*(hPZZڐѡ\IROOϠ݋gϞ?+V8So ::N*::Z Ukk n7+!!Au)66V6-phg1~aH6mXAt\Ok|MnOo[}JJJ;uuur办}YY,]pA=A֯~+8qB3g ǪQ^^ވ|>}UqqA0 C?M\|YsQ'OBׯUWWw۲po xޠ[6ڵk^ziXϤ4UPP ϧO?Tg}h.{$W^yEӦMӒ%Kt1ƎImx#jb҂ph:w***,t%)44Ix0 f#x`:G#x`:G#x===ӧKr$I>O$0 ϧ"rGSBBBXOOJJJdV^-˥8qBmmmZn$@$۷OںueXh.jnkޮ]TVVع(I}}}JJJROObcc3(==] ,PHH/^,I:|z)IҚ5koo1&n\=p!==}s]nI Oǎ500 .h޼yիe\(IRCCQEEEZb:;;ǥ]wq<ψ甔hƍZjoo@]]]|5gI?6(&&FҢ?\UUUz烎9sF---w[c[<0`|555ZtV"##d),,L b̙3{Ok׮$͛7O)))2 CGcneddСCJIIp8TXXxk裏 /(--MqqqڴiS k0~=I+Ţ .衇?&}>Op^.c0dە45*… %߮=00;w|XZJv]yyyxv]III9sf#Gŋ#ZÇUYYd9rDahʕ~_[nodrn[xUVVN:2I8۷Yiiiڰaw^r:tf͝;WGSSS/_|08SMMO>Nii?>ÛaZxեuvvn믿xS8F0l޼Yw~:::3fO<}'Trr^*!˥e˖0 j7se͝;wDk'|Z*..7|3hh?y***~z]~]uuu7-ðe˖vmSLќ9swuuAZhrrrܬAߵX,?Iisk/bرC999z饗??|>-YDg} 0F8y<0 = q,W^ѴiӴd;vLcR^veZeX(;'1*Ǝ±%ph:w***,tP.1ƎGB &lpOkZ===ӧ>kjj I5 C |a4pIo&lk.uHLӣիWr)''Gt iݺu߿T{TWW7co>j֭*//b#]bo &dx5uwwKN^Wzzig  ŋ%ISO=5(**Ҋ+9ndf?{w]U}uB3ȂR0pi XQ^XՅDiUh2 BI qhdYp#8 2M!p3|~#טOrNx> $짞zJדO>qm*?/ǹ=ƍ5m4ԩSAݯZڊTuurrr4|p9?~\Խ{wmڴIcǎ1FgΜQϞ=p*((买ZjO_~٧cl7//OԤIdn1VmSmnc]qϞ=p 6S^^ji[JMMUyynݺ׻wo)55U ^SS j:x֯_ǵk{ռy󔑑3gxh۶m҄ 5gnm~NϦ於 Sgn>S͘1C.W^^u5OLLOުji[ VddY}xNS;vԯ~+-ZH'O;gbb6lؠ|uIJLLlqfڵKz駵tҺC1oԌ3dQmm"""6m oSs)?wqn[[PUZZիWKe |:wYfZΝ;zoʪWܹΝ"##%IQQQѡCTSSjԨQիW_~Zl"""TXXǚ5k4i$uYc)..N ﶲ/#֠h6gnls)?=m{[ګ)>՚mGR;^}Un>|X+WT\\ƌd(##%''kʔ)رc<򈲳uĉfcmڴI Ζ1FG֮]~~<׹e~%izgtZ7Vmk+.xܻw/_0a,I㏵e}zt=7߬;.++^h8P߯el_~ub>}Z'OԤI( UIIMcǎi e۵~zٳѹ;w/_^1oK3gn9rDEEE6lX׹e~%iѢEz''xދun䭿``3M=gĖ.]j7s=e˖y?nJKKwVff㕐竬L7tSscTXXX몫}{7_jJKK_Zsm~wޭ M:Uϟ=>>hskE+o[:LAZpa@#$$Dݺuk\yyy*,,T߾}b }JJJjfkx#caS4diժUZb&N.淵涩ϔ*˥A)66V}Q̭y㏼yhw}:jhr8:~xѽ{wmڴIcǎ1FgΜQϞ=j jҤI {ʧc^nFsεueqƍ6muԩWzzRSSU^^b[n u8:p ,Ommۻwo)55U hXa m6eeei„ N [>5nתU4}t>rl޳g~+<<\=z£1FgϞmzBCթS+!!AՊTUU"##}^MMuV͘1CGU~~^{_ө;W-Zɓ';.i ݻw׌3dQmm"""M哧׮]{WsO7Zh= £֧~3fH%˵nݺ&/_&&&jÆ WNĀߚ:tH5558pFhj ߧfW 7OӚcҚ5kk.EFF駟ҥK=Lf1ٷzK?ԣGIkkɒ%AٯӧOGׯ>EDDPfR߾}Շs)))I? gVVnwYs q-ae]/J ,cqqqO|h[>4wNΝ;5`ɓ3fLݝl'?mKLL{9̙3;etquUW^ye@8\.mٲE^{:o 4'I}֭[n[xxfΜ_|Q)))n׼iIς2%''7(21bF'>>oӧ:r:|V\83FJIIQFFF@oSؾ}6mڤeggѣGk׮]3oqŋgQBB,9Z)Scǎ?#։';7mߗ>;w޽{|rׄ 4x`I-[okuo#Fnk8P_~ub>}Z'OԤI( UIIMcǎim2-5\^ziΜ9tiذa3oqE'PRRx on4g۵~zٳklܹZ|y߫W/7-iߗ>3\;+ۭREEE)&&nݻx%$$D*++M7=-[Xfx }Zz_t_EFFNknEcTXXX몫j֜7Ҿ9hI:,\p!жl6^o(??_.K}nݪ|Paaa^;b8\.6mڤ*+7nnٚc>L)))*++mݺUfR.].m{6#?㢢7Ҿ>\Jjkk&cv{ ?+B RNN₦N*555 f#q\1JLA`xf ʢ$k:r222([#""Ƚ o.ט`%x`(<GrX#Qx`9 ,G(|BCC۬ruIv￯+V#q\С)--Չ'MIII?Y={ \̋PH7KPU^"##5c jkkeџ'kԨQ~+:vddÇp27Ϸ|<ݻwצM4vXct5I5i$qQ5qDl6+55U'NT=U\\4u֭y8V[[뗹ݻ4z㏱h۶m҄ tԩV *4:S駟={I UN.턄UWW+22RUUUuMMuV͘1CGU~~^{Kө;W-Zɓ'*)){rS˵nݺ&-i;22RDmذAԩrsssYSSCFԨQzѿ@]vx{響aFEE顇ڲe~_/L>NΝ;5`ɓ3fO/ILL/׹lx߯3g;fյkW]yA7˥-[ku]\x㍬MlٲEΝw-I;Էo_]㢣5b9O||_>}HЫ*ۭÇkʕӘ1cedd\:۷kӦMJHHPvv1=zvtcqZxy%$$hr:^w}Z'OԤI( UIIMcǎi3kQ^4gp :r䈊4lذKHH-Z'xBIIIz'<'1عn***J111uwޭL+!!A%%%WYYn&}lٲϚ5ko/իڧsXvޭ M:Uϟ=>.2+$$Dݺuk\yyy*,,T߾}b }JJJ . 2DiiiZjVX'|@g}T\. 4H裏Hd/1*,,Lz9lϟ~XW\q 8Ng}jjj.Fx$<<\dق(IZ~9,:JFGr!LQx`9 ,G(<GrX#Qx@ee+ө}"hr1I\.v{P<\.C" Lnn*өɓ'K.oF6mRHH[n0`6nܨ!CӼy󔓓 cTSS+W*33S)))ڵ˃ ٳGaaa֭MЊB?U]]aÆ)++$I}֭[5gtMms4eڵKqqq/~oUrC̙3:q℮zI?9!Iw=\,(:tnf}׿j i6###U]] >\CǏoy?c޽6mڤc3gΨgϞA7I5i$qG}T6l$ۺ뮻Եk6m3==]*//Wqqԭ[r8:p ,_mm׻wo)55U qh۶m҄ tԩVOEgϞU=*$$D;vСC<g6Ohh:utImVWW+22RUUUfFIIIںuf̘G*??_%Ibb^~eto~_iѢE/QF8BCCeQmm"""Z%?.]hZx͛(IZn]hfdd$)**gڰaթS'*111 篦FRMMXWt q~6^1СCzwӧOGׯ>EDDPfR߾}us)))I? WVVVtYsε|,L4i$?{=5U[C=dY?~IҬY4eʔF9uvܩ($$D'OԘ1cڻ'?/_^o[sQ2̙3;etquUW^yePri˖-kuu)77W7x#+@3evvU_o5b9O||mP>}$pW_ÇrJi̘1JNNVJJ222b|ǎ;i&%$$(;;[=Zv qHŋ3(!!Adhq˖-zekԩܹׯX\\ӧOɓ4iBBB^xA*))Ѵit1 >< qK3gn9rDEEE6lXPC-Z'xBIIIz'}7 x Vn[RLLLݻw+33SJHHPIIUVVny{9-[!Ik֬o/RWVZZkHؽ{2224uT?^X6< e /$$Dݺuk\yyy*,,T߾}b }JJJ . qHҐ!CUViŊ8q q|gJMMҠA>hxlc 1FvW\A)''GqqqA5YRf-m,<<\dق(IZ~9+:J&p#˅0F(<GrX#Qx`9 ,G(<GrX#Qx`9 ,G(<GrX#QxlEڻwNۧV91F~m@IEE];V7.%N|(>rTbsmֵkZ &Ҝ9j]iش5=^7gtXp`hUURSSg}2(66V;CZOVMM%Ioojݲl[_ڸqzG}Tz5zhN:y<')))8pʀkXX?k oUYYiI U^^ٳgЫ 8@|jeffj֭t zbW_5.syZvrlӜo6֋M{Y).F3*&TUU!CI&e1pndgga1/0LVVVN7nٹsgկp8Lvv1c1]w_hi_,Ybrss}w~bd1 ,0;v0nی9X|rss͖-[1۷X6mmoZp_;iş6Vk\7u=k*6gp x8ql6ӕ'G {Ga\.IoK]vm~]^^^b[n/(??_ ifөoqjǍLKX#IݻOAAmۦ,M0ANjVl`gmkx[Zk]ϵ˚LMs2Vki@[͟qiIl|YϼŦ=g u3gꪫ?O0yfuV_x=6%%%M_UUUƘooQ_<^ڤV]]m.\h֬Yclb֯_oilZz~inv}g7Vp{v3{lM|,>wsl`gmk{]˚LMsۜZ~loֈKKczTly=kw<>}ɷuEǏŋt:5tP-//׺uٰaC)Ifkq碓'OgϞM6MW_}|I=Z|MwAm7VĨFRFFQFOhh$f)""Xo9ve=ƵkǗ5-ֈ2Vki[gbYAhu?T5~x^ɂ{ǧswYf{>7L111u׿7jرz뭷T]]3f4=W^yK=G/~ܹsJJJ1n[VVmܹ_byOK^cKγM||]qεLMcq 9cl@[5bUl.k`=O>m۶&_swk|rn:vZ?o%)(vYߌ5fϞ=pktsܹۛ:/玏!VR|=Zw4՗u|~+򻥱Qnnq:zk_Glw?cc՚vƟkp\u 476!.u ګyz˖-:wnIwܡ}jڵo۵k.Nx#Fȑ#=_RҒ>7%66VEEEuѣzH*++qFc4vXiŊꫯVΝlt~_v|5c(ꫯTTT{L>ಎϏcs\zK/Vzzz!6umk/׎?׵K?b6sZKl;pXs@ŗ@ cvvN_uߓ#I;vPhh֬YW^yE/'|R6M~C߿?}iqboIr=h޽u[ԧOm޼Y۷ouWK5eUVV*..N/4fȑ#1bDMk(Ps5(,,L_$i9reR󻥱SO=' ش絭=\;^.uMglZ{Mkobu놿_:XA>۷'6忸c9… Z IDATfɒ%w5ѣ-jK.5_O9s&0>`cvȑ#s9Ƨش4/%6[Ӿ}vvŵnִ16^Ӛ M\7[Ħ9qׁh:,\p!+&&F'Nе^[-::Z۰arrrtY)//OCm?>svm߾]'O#FPXX(>t UWWkΝr\.4Ʀ@}zכڸn|rSTTTo sdZsz|f̙3g۶`cvȑ#6?˗2._gɒ%&77#55tќ={mf-[c_ hS!?#?5[p9&~R[[~UHH㡘8ql6ӕ'GYyynݺGՆ r$Io.uڵ~{VQQRSS5`͏/?˗7n233=p*((vZJӧO/qV!Xs@۶mSVV&LSNizOk姕q9槷&rӗq+~/\ dz>~W6M3gsUW2|ӼԄ͛7[/>ü{nٳglΑq_8Ns7n?o;c?S^ҸܹsMUUׯ)..nr!sԘE~}ѦG~\m 6._c17;ɓ'ճg&ӧJJJ|hdd$)**J6]txb9N :>555:t2224p@5ʯc6._˸|:DZJ?'jܸq:qD\UU5kgUdd~i-]cC0hhòlؽ[V嚟֚@Mo?V^ 74Z[ܹsJJJ1n[VVݹsg͝;eeex?Pzj _ 8P!!!{<::ZzT=zwuƎ9|6._]|ӧOkׯ-[*55mYF&MRΝ%I=┘hĪ>Ukhk\ӗƪ8[~2797\"n_|gΝ;O>{1mLxxx/o;ì]&P_ߛ7o6_w}8p:tl޼/pႹmSO=1o>uֺs;f.\@P+?}ɏOc?66Nݻ7(3Pr3Ւlh w}H׋^}Un>|X+WT\\ƌd(##65euرGyDٍ>To߮M6)!!A2hڵk9 9ltݒŋgQBB,t(jInnر+XsDᱍs=ڻwoݿ5uT׿7IڱcBCCf+r8'|R6M~/>#GhĈׯX9s7zꥐEEE^PURRiӦט1ck > ܹs|5ՇkFw u 7ȑ#***ҰaQ秷iEiǎ馛[n%3r3Ւ Q^|Z97H6c~Zs=e˖߯F_*K/;M73ۭREEE.ݻw+33SJHHPIIUVV<*66cuUW5>YFoK^Ziii(?=G ~dddhԩ:߯)Sqn6g  .\4XoĈ iߘ8qB^{-vo߮ɓ'7fSttm?x>r֭[6fk`cy>\.mڴIUUUzW4nܸG0#?=G g}nݪYfK.eY17;K/iK.~6Gmmdn}bazuWhРAQ\\9J~D~:NJjjj^27rtqg?c2ZQEE.\={e~a[GԩSu]w'k("7[s6(<o`9 ,G(<GrX#Qx`9 ,G(<GrX#Qx`9 ,G(<GrX#Qx`9 ,G(<GrX#Qx`9 ,G(<c_YY{tj߾}hy;2Inoy?槢5(//O.K2ƴ\j϶O֚IhtJ:t ө[oUÆ Ӿ}駟CիƏI*++Kr:_I&++viƍgvY׾>sx%%%pl3c c1n:uͬY1{ヲb?TVVUst}ut:.G/׿e6n%kNc mjimn.@l^~Oj6˗/7EEE^5 0G񩝛o̜9޶-zT_]QQa.\`,Yb}]S[[k=ڢq-Xرøn3r62._gɒ%&77955tќ={ͮٳge-[c_9M'X3r5Ӫ=/ss9t8qDl6+55U'NT=_Ժu|jGՆ r$Io.u/WllBBBZ<Xiii֭[ջwo)55U hz/e~ƍ&p8t\ҸvVZӧ_8ڿ6r@۶mSVV&LSNi~zO0gKs4Ӫ=/s-?[nA0VK̙c*ӧnokn9s9wcJKKMXXټyٺu/QRROYYW^m/yUUUƘ{c0f&;;McK|q:ooXnyoy?L:cNNN6s5UUU_~ng}֧-G/^wvo lnjix bi>}TRR[4?S5J6M?֭[3g6yL.]4~x-^XСCܿݔ111>}zիW_~Zl"""TXXfM4I;w$=cSbbzϝ;$ceeeΝ;kܹjl1F~67GK~ZL8?Knh`=O>rm۶p/{?i.]66VEEE۪qbL9.裈}TXáW_}Uʕ+1c(99Y)))hdM2E;vGQvvN8ѬnvءM6)!!A2hڵkerÇڗŋgQBB,iiO׮`MѬ,㏛HGջW_m^~eS[[سgz[&--4͛o~i^z%^Fs>lfϞmɸ=iqYSСCfܹ~ln6j*3{lC?М}>|؄r(\r1Ur/***2i8O֮@Mٌi⶿ W]]uԩg.KgϞUne٨^zIwyn&w***vޭL+!!A%%%WYYYcTXXX}(..UW]sYFoK^Ziii믽ӲevMݻSڿLB~^&ih  .\^+BBBPщ'tגmnkF6#AsڿriӦM+q=ٔ#Fx}9ٚ9>L)))*++mݺUfR.]$?X-7x׮x F/q ,]Tޔ 1FvW\A)''Gqqq(9t:*IQxxJ$?/?[kj.G.2:#įl] CMP$Q 1Q.d $Mpɸ0VpY F`[chS71ڵf|%h{9il2199rǎW_ŋq%lݺ֭dBe!B!#!B!B!ppB!B!B' !B!B!B!B!GB!B!B#!B!B!pxB!B!b8x^|xGPZZj|D"8z*&&&P___|}PUUE?C{{;?Xb{'9{l9L&]mfƍYfmXrN >Ei]A76ݘi73B1 G_~Yɤqy'$ J<U̙3@zzzf=H$dƍۛ~322"x\l۶MDDR1չ>|D"h>H2ߌrt5;v?/W^ W\6\4ݚmzݘG-kwSLfv]fڧz5f>?3.kyF!vN{---r=H4T*%~ \۷zl_b,]ۍءw[M3Q1S{7F䚛F}gSeqf<3oܞgKMwޑ_~kcǎ̙3JdddD6>>nxD俏gF̰o> F Niii1g><䓚}뭷LoF8rQbR__/`~l?3R)bzǮ~Fm7qIDAT51ˏճԒkNd|wZ.k|yF!ģ TVVbddDӯ~ݸqCnlڴ vB"ʕ+UkNLLVsw͛Ǔ ̄|co&|>f\|Y3%Ԟ;w.ѨG~'|Wի Gɑᦤxpwܨyf[!ܨђGN5'Qn5fZLSۧz֜Zr)}cL2i!8o~6m¾}TO+ػwom0glذA{XhvaxX`A+WֆÈFضm<Q{ҋ366ݻwCDҏߞw--ϟ{?ŏQsnyf[&75kNܰoqz,?VgZ1Պu-`:͉yF!I0;LZyF!v1Zwvvbll O?4`ڵXd aѢEYb8}tK%555YoV2e]v!v)z,7N|'W?̵¸ɥorʬc3-aXgZRNB!Va`[ns=FII 8{"tPAGG߯x,xr6l qTVVرcBKK |>(^{5D"ҥK] MN.ٓn2V59t<3ҍۼgb{xdcϞ=2::ԔD"wV 1O<GJ,z Í~X;Qw=̵u-7=Ѝ3{n3Bv<""3?<ϑY_p|\T*""""""""""/bDKDDDDDDDDDdX%"""""""""2Q,(xL DDDDDDDDDD&^""""""""""/bDKDDDDDDDDDdX%"""""""""2Q,(xL DDDDDDDDDD&^""""""""""/bDKDDDDDDDDDdX%"""""""""2Q,(xL DDDDDDDDDD&^""""""""""/bDKDDDDDDDDDdX%"""""""""2Q,(xL DDDDDDDDDD&^""""""""""/bDKDDDDDDDDDdX%"""""""""2Q,(xL DDDDDDDDDD&^""""""""""/bDKDDDDDDDDDdX%"""""""""2Q,(xL DDDDDDDDDD&^""""""""""/bDKDDDDDDDDDdX%"""""""""2Q,(xL DDDDDDDDDD&^""""""""""/bGteGFjj*G z_iii&JR\E"3 EX[[KTKDDDDDDDDDdLr9s@$q􈈈E̙3$9xژJBaa!u^&QWW;;;p6l'?|GoYYv؁pHHHh]~~>,&?#>JBiiib'qmw+7ϻw Ջ.))ѹXA">B!""""""A^oBB 'Nŋ㏵n{n9HJJȑ#QXXUOw^޽;N1c`֬Yضm߿nt;ѝ\xǏ###SNťKpQ9s3g(b;v cƌU'N ((yyy۽eDEEK.ה6ꫯPXXWbѢE h/ l4WX!-- ؾ};jkk5ٿ?vލ|ZHɓX~=._h? ooo,_/^ԩS1eҘ VW$&&ի8y$oߎׯk=߃pa\rEkt9t TTT=z4VXzr'667n\.֭[fT? ѣG1zhl߾#IJJ˜1cc@LL  &gsbkŘD}c䠡kISb,\;?ꍷ;+VW_}?dÆ Lџ~)֭[hyAA&M˗cx7₽{bԩ\psAHHf͚Ç#55?3&O ޽{1b8i$t ؿ?v܉cj̹ؽ{wB"`zBqq1~|ٳ'=R,]/[[[@޽QXX777߿?d2֮] @$bscزe \\\PUUx?`_^]s>-ZÇܹsxz-vppXvUViܥP믿#F 88cǎhXݹs/FTT<<[n:c.qh GGG K/?#ѯ_?(ĉf֭غu+JJJti,BDy!&&yyy~>L_uW^T\.o/222w&듘[[[$''#99P( DDaa!QTT'O:Km5 uMmQ02*Gf͚X@qq1 QZZ/bۋ>芝.ߣ>Z2;ӧgS~_+VhH$СC=hw77mL\|B!HW|k1r~c%%%@󃏏EDDDDDDj/K  fkVjv\|D}9aԨQ TsKΧjnn=zwiz2ފ ,\={6޽+Rlt R)]__qGtSxyy077ǻヒ#G@"`ܹM:fNNÖ y|Ř5kVrǘ9_1cj}}Ġ9ܜB!Ǝ4ZbLk)2^3"""""""]^;xǍkkk䨗,7L&Ә?"P*XVRR -*Jc<_駟0`Fk700RRR)MJJJ?ÇPT;[1˧L gBL0Vkx1<ٝV/f̘hE]}dbyolYd J333s' 999V/;u~3Vy؋,,,4m:Bb47B]5Dkz-16BoJ:uҺ.==׮]^HW&׿5"""p \t ?rrr/`pqq-|}}ӧOܹsΝ;… ?'O֭[ٳ'\\\/RSS sss}˃;o߾HLLđ#G uQ!77u8[j=۷~ VVVN>7xCc_]^+++߿]vō7УG=z}UϗEEEӧ^u>>/Ƹ915B9 4 偾1җBrPȵ)1r~c;wFrr2t͛())?6""""""2TZ9jHdpjCP :uyGT*annbX,T*͛7QVV7|ZO>+̙&=P(C8;;7Zh7ǏaggT6}4YPpvv6z[B"8::Ç:nBU[[νwSWW&OO Bڦ/w?zNNNZGSǪ9yT*| b1<'rsc9lL X[[K/oU`uPIIIJ;vlu/G1PDDDDDDmT bĉùs"(..n.44+WdH0KDD ѫW/2\28;;3@DDDDDDS4eY%""""""""d^);((/""""""""""zQPPIwARݺuS/娫DDDDDDDDdx/8s @,"èQ +??h_rE9sY[:bvڅSj,֭[ KKK~pV0tPᣏ>¤Il2̚5 W\A@@*++kʔ)8p@ڡP(a> ¯~+^C  0g1F^^^T__#c?nnn^$-[0MJ2 `gg*8)));wnp5deeD";R߿?+ܽ{]⩯/͉1BCC1qD,--affl&?xۘ%JKK՟U*PUU*hXְ\}'pIIIA Z7nӧOѻwo4* 깐Bڠo߆R]1j8^nn.']PPPWWW/0aDDDDDDDDfXmCϟӧO𧻓=zGqA bbΜ9&LgϪ?Ϟ=)))Mjknn.6mڄ .@*"..YYY9rhQff&bccqa ~~~Ayw܁xI$b""""""""o+8q"<<}_tH$޽;\]]9&MF 8{,vލׯO>>F L&ӧѿ۫ϥ/bX(Ja޼y_ONNqy&JJJ+  J!R Ŵ5Z$Byy9N:5i !J%#M>Ə?O>'Ol9tΜ9cՏW11x_3f@ZZ:dΟ?777}IH$Bpp0lԸq#ܼy5j*++qʕVɛ |uU;"""""""jW@Nеk۾PL8Ѥclii 333dgg9%JԬcH$Zlڴ &k}x򎈈cP*:梮* eeeBUUjjjnSsTWW(++\.ob noxT*/--UL}}= _ZZ{/ϛ!yC֚|^G{/x[Ill,6n\["--Mc{n믿FBBQ=ѣGc8x QH>y$֯_˗/#::?++ 077ٳg?YPT*} IDAT> 5ǣo߾<<﷿-N> Ǐǚ5k|AŪQ۞/ӦMÎ;*^0i&FqرFzz::Ꚏcih< A,W^2 mkkk[Baa!u&8FTK19%1yGDDDDDDD/x[RlRByC$mL&Cff&222p,\ǎ3د2|w: ̚5KPlmm }oooaF%}]DGG g6z,Q\\+V ** VVVpֶ4hXO!qx/~1yll^ ɛʩ+crJЅ\K9ѫs𶂀䠺ZԩS2e 222ԟ rrr0aAT*Edgg5lΝ;# >!!!1b?Æ +ܿ_OĉoPPP~EiiiuuK,ݻ!JuK* ˖-êUJdff6NTSNޒ2b.iMyy/_  +4/ >Bs!yМؼ#""""""dÆ Lџ~znЎ L&ӧѿxVHHHRDXX͛fzK"##qz888 66pYܼy ~Gܺu RNBϞ= x(u UΝ ??Fñ|r>|8޽ 6>>>xnذo~8HJJիaaaŋz ݼy%%%ӧO#""yyypwwGVVbbbp}8rr9|||`ccHOO#JJJpM|/m6iii۷nݺ K}y19e!9%yGDDDDDDDڅKlll P[[ 333޾󙶄#""aZ۷AAAM_͛7 h O!qhʹ˵Nbl-74#""""""zX[[K5I$8;;]O 9Э[vSPPRRR0v&C.7*_~~~1XҶ1!ۭȦoƭ[0yde>>} ///7Fhh(V\!kL^1xv#"""""")CP(G^Lreeez& 󎈈c mhܹH$̺D"xQ)Qx7;"""""""4w\l76RPXX!K.vvvֆrԠK.""""""W9x[Gns磪d׮]JN?z&** ֭C~~rKKKڦT*cx[q#ܼy5e8pCpss\6c HcH$Bpp0lnmEyy9 :<x[T*S+%%'21HSNڵu033Cvvv?#:<x[JBiimݻs|TWW(++\.8%jjj Xwrܾ}J6UUU())|+++CUU条\VTxl&Lv;#d^c:u ǎùs?ꫯPXXWbѢEBhh(qYVKOOرcgR? ;~8s-[b,]`ܸq7njkkAa۶m->N!C`ܹ0j(|ᇨo640((đ#G兄V٭[bϞ=HLLĜ9sPYYGbؾ};<k ɓ'~z\|Ѹwvxyy!55R]&"""""""ojE}I]vł HYQjϞ= İapibc$$$o߾Fjȑ9sfǐRDDD ˖-t;wGt=zIIIl۶ !!!xiӦѣGG||<\]][ta騨  ɰd,Zرc_`ԨQ Ǐǚ5k4k>>hJDڪxKL6 ;vh⮐6̘16mByy9JJJm;v,C!%%Ufff*++5r!߼!0tPrCuqμh`T-0dİKDDDDDDD mXp!1{lܽ{>2 #GpB;vLyO2|wzHYV睘ѣy;::qqq!!!zV2>>|8Fgذa1чN})N8o8|󤥥֭[F6%K{nHR/k6T*,[ V7*++٢mx\VŋNER朡\@vvz߆KWTSN햷 fΜ}$"""""""hxo+ԩ:;v {ոqضmRHb1bcc1zhܸquiii8q$ ~m1R)<==[,»hݛo?_ܽ{#GcXd D" իWƍX|9lmm5oƨkCOOOwfqu̚5 {E|||ggg^\\Oޤ} a߾} j6v,]"ظq#뇹s :\ncQQCXXXƦQ7o ??? 0ݿaaa>|8%fmm ,rfnn ]tQ^,C$[n M٧5}1D"Kr\ hk둒/ *4F2wEлwo >\n裓z~[[[F\.]>}:x1#""""""^juDqqq֭[bʕ&QZZ B/3u8ڄB@~~>z`NreeepvvPy&`aaA"""""""zI ,+T RS y HuDDDDDDDDD"͛gDmLRݺuc0pm 2>8qɓѥK+N.vvv KTK֨\x[n֭[QPPulݺ˗/ǖ-[k.jls򊊊.NvTC̙3Le|TUU[~gMǏCT6 -q]ǏqΝW"gn:ᨬ䅒 b ߿?W(GLL 6o X? .noL֪mV(h?nnn:_"p&Sƣ:e8p]cGGGeسg Ҭ2Z8ѽ{w\4Ռ3;~D"c˖-PQcZΝoѝMgϞŰa`gg ,Ypijjjxbxxxz{]6Ohh(&NsMD :x45_SRR0waÆa; %RmyYÝ:uB׮]_xsiii 333dggbIDDDDD^jqݻwqa尵U´i $ո~: }vǮ ´SRB}'JBYYFGRPT(--΂B,{!jkku՗V04<PZZçFP UUUXgiiy-Ǧ"i45gGmm-rssQWWڶї'oSsN޾, & >>]KbػwsQQӧO鮪dee!448{,?k߲e P\\Kuz*N<۷uIII3f vAرc3f VZX8qAAAǏczSVViӦi]/`ܸq7njkkAa۶mFkS!C`ܹ0j(|ᇨov?~AAA8x 9///$$$4inB^pEL0vXܹs6l$$$o߾F!==GUem6&O~ ǂ 4;a={VyHIIfҤIHOOf͚-ZFw޹s>>>zӻwo E{~~~6m=zz O0}tTTT… ddXd -Zdtn5رc_`ԨQrH̜9Qsssi&\pRqqqʂ""" ˖-kXl]1rH@ff&bccOFxyy>>>2ٳXjԩSgaXfz]bb"lmm B-,,U[՟}}}1sL[nۖʪIln__bڴiرcݖhÌ3i&mc8tP}KիL&c͍X,7D"j0wѧOH~ _^>EG߾}5yxx )) K."""˜a߾} jv8L>/_zGɩ7n1bbb驵R74 B}MumCQQCXXX"H=)w l޼~~~z!""""""amm ,r^ (68ssshBI$\offX&vҥZjhM `رMa󃍍MB:B,ryh|msGŻ5Ƭ%oh<$$ F߾NNN1ۖʻоVWWC.KDDDDDs5щ' ˑs (..~bӧziu-FScO}  :^jj*Fhy^^4z-}@ uuu@t׮]CNNK}dܼy<<==k.|7s;1Ґ`ܹׯ#;;ǎCXX`ؾ};7??JtmۦϏƍ1tP䠶6lh_mm-q'pKŁ:u  Xpλ};ʝ  Ԧ.>3̚5 }ŭ[ .xJ7;;:uRɓ'1gaڴijh…=z4&NKKK[U{pwwԩSV.o+IKKCvv6z-D:oи+W`gg'?`HOO+1vXY555066FHHD"kc555ػw/ܹO>D~6mBuu5@* gjӦMk׮!<< 8p׮]Ê+pq۷OaJR+;\|AAA}6b1"""0}t9R~Mvv6NK.)-3DDDDDDDD>:ܢAݻ$={0i$`ǎ ELL ѳgO.|XtJԬ,#$$D.\+311]a˷066Frr2.]  hhhD"VmjC&aΝ8qV^ gggL2EѣGh"oˊcU6}T <b 55QQQ OMMСCUN_y """"""""h#[[[cǎJJJ0{l 2j_,e~wJXxΝ;+l[˗/+\Ӹ PUU;;;ɏKR\|Çcĉؽ{7nScƌA~~>1i$*͕}Iؾ}5R.],e}z 8pYHMM_cdd]짾@DDDDDDDD+#333o_~ CCCDEEa:\~-v؁^{ ~-ˑDEE5?aaaH$PO$aػw/|MWUU9jjj`cc#_٪MmM:>ĵk~~~ qiii߿54U}'>CRRݻGGGŋ###oO}偈HW܃Wӑy nA@~~>v}dk||:,٢E_Vwk[v6oތN:aܸqxa/r߉H7|cƌ ?~)Skq:u;;;8;;pDDDDDDDDb ^"""""""""zubQ!h Sx!QKuȸE A''6m c9CDDDDDDD-aرO׷7a۶mzaQ||<>#o~ƩSĉ2ٳg=Wx u\,>'233q17m޶H$BDD^͛~СCQUUK.ʘ?۶mk\)E3DDDDDDDqHKK+z쉍7qƵ[fff044Dvv 1 Э[=C"{6VBpppJU/ڜ!""""""^=whhh@QQwm? (++Cuu5Q[[.{. WQQ!K1Y|`` :n󢦦׮]CYY*++dN5[ZZ|]]rrrP__~=xuuu:'T""""""""mg9r$c„ 8w9gbʔ) ٔ 11ׯG]]ooo6l"##ꫯ˗/cΜ9 #G`ذaX~=///d2 B ϊG >|86lسg<==Q>pEDGG?)?A!44TއCbҥ>4gNɓ'qQaŸ{6mBmm->|f̟VVVI&PGs Q{0` + ΝիW6oތ_~~!7߿=z  HR,Z0ydիIr˖-qib֬Y ={7nᅬW_}UFQ{9,XU>ݻ77&*<}t$%%kM9zD"Auug>W^y{F޽ !!!@FFsŜ9sZ4_~x7]vŬYH,3 m͘?o߾>'T9CDDDDDDDXmb}V?ӟ6mrJI~H \///] 1bv'˗/ǏnB(k.L8ɹ`,[ χ#n݊}*e֭[*GOɁ_ =A,#66Æ Í70v&׸cƌ˜266F.]z=z۷o̙3zjHHH'|={xRT?vb1U?4& M:gڃHuH:ϮVJǏhhhx.R>sL(lH&(,,X,MUqqq iV믿!йsg+_w܉0]^^rT*Ͽn퇦1D9ђ6sQ۳D,r@]V^}^ CRR ({DEΝ*++5娦V322H$S'|$dffJ|FFFjaH$pppЪx-hsH[,R @VVunn.~tt4y27#F@~~>1`sy3DDDDDDDDO &R)ܹ>}i(++=s"""""""jE DDDDDDDDD^fH7WHɉQee%accds+x_pHOOD"A}}=/c׮] 򂧧'mܸ&LhJKKw0669R֬Yq2)OOOa͚5B޽عs'|||BF*ibMOOck׮]q-ѣ},(ADDDDDDD,ꑓ֯_ƍD޽00x1LXOktt4ƍ&ܾ}GPP>c[znMfff044Dvv6 """""""j!xlĈ … nݺMc}A@YY3g>n݂L&kv<-S[ĩ)ֻwbUrǏᅲ?o߾;wZ-͍UטqСf1'"""""""Xm+WÇzjL4IA@aaڿRIHHña={鉄5-[jjj<'** vBQQ͛|:رXv}ݿ? `ذaDCC^⌋àA*ocСXtBڊsAN:gϞ:Ǯ|k:m @rrr8ouc0۷?# Ç%"""""""KZLhܼy...ZWZZטcj Djjӑ$,J`L4 nnnضmӱn:D"OVP9rD^ ڵkzjäIC444СC:Tx*fD"/2ޚƼ6l%bС ?JDDDDDDDo+A0c DFF"!!D}666Xd^b055U۷oGLL ;\ׯW_~5]K˗/DŽ 0x`Z>8p666GQQA@.]t]&FǏ4ڌyQQF]vDDDDDDD%:`+x-**JwKKK[3fo޽{ >>>j166*SSSzsrr+&% VX.]Ftt4?~PD Fdd$AOJJȑ#_ݘ  ;\]]QUU,xO^=9r~!$$H0L> ,00z^Ml2dff"??غu+ 777jƟ*ŋc;v,߿!Cmۆs΅H$ A{ƍXp!"ÇWz%q6rqq3ƌӢ`׮]8q^s۷oJ~m׮]hѢft^ۘsrr Mfn^v SNq!L<@џhd(((= !acc*VT޾JJm}GXr󵵵ZLnn;wDXXXc9s&bbb\燁ژ颣n/*y| `ee 0ű&$$Fb_|>#~9Q b*:1(..֪MBQQLZt4 ^"IRܹs}a2tTYY23DDDDDDD\+x;KBCC!H8]H$ Ecee+++&;2nf)//Gmm-lmmu9.__/kⶹm.1%km RMHH@vv6{=DvcpppXii)֮]_JtYqqqx,,,%KлwoDDDҥKHOOD"A}}=/c׮] 򂧧'WFN64ZNffN&"""""""5һ?1ݻ׬O/q?۶mS8fmmrHRh:L;v ƍCrrrR{… addOOOa͚5B޽عs'|||.˗/ƍUlmMExko=zz%UV!88#FԩS*tQZZ\]]ѳgONA```e֯_ƍD޽ jkkUƠ)F}Q5o""""""""j,YII ^Lwƍhhhhrݕ+W TlWUU <kא (**¹sPRRb,[ unݺL>=xuuuM3*tQ}}}^7ba…Dn-AA@YY:U5CH7,ٹs0c 5 ɓ'OV.44UVW_ܹ ׯcÑ#Ggǣ_~8wىٳ{iQ &Mjr*@JJ RRR+-C 2D6ivVVV3f 8XS*eeen`jj=]o""""""""j}܃xRLJJB1`6iǑjmM2>@m+N={˗/GQQ~mɓ?c 0qxyyZ?{۷o7ٳgݻW5RTȑ#7oq)}pfǧd2Űio\\BBBTQ[[ ccV>i џ%:`+x[iĤՋXj'ӴV8hjC,{DEΝSYY9ؖ ?Vp짟~B^^&Ow}wΝQZZrMNRi AMԾhm@nn.u61􅈈h y&ajjڬ_555 ރ[nQ%m1Q[4KDDDDDDDDD/Z5n*%""""""""/<JKK444H}reeeFuuu7 ġCe&"""""""Xm3g=z[[[š"ÇWy (,,TWZZ~W˗1gΜ&1rHDGG#>>.\@pp0ל9sF–-[RRRD_uuu6mBmm->|X~8q| .\ݻwWym||<<<|իWcҤI:|?&Lsp={SLQ(*퍰0TTTÆ Cdd$4͑#G0l0_탗d2Yx &[BQ%k`Xd rssѧOdff"00ׯ_WnΝňŌ34>111j177ǂ Z%^Iy˖-qib֬Ykp9\z|`HIIѣC )S=dddd@*bѢE,W0~~~pssC=T޽{/IkAnnn*[XX`ʔ)͛7u~U_~~,'OFqq1^ """ ={7nᅬW_}UeDP'""""""" ӦM駟xgZ=K,ixgee!99xҽmb1#laa&hr!+ z̛7O}*/))Q{>## Œ3OPM̴i兕+WNNN:X,+D"Ule/=Xm% APP &h2j555ԩS[-ފ ̞=>}ڗ='M*#ɚ' JРHUHLLIJe0bx{{cÆ xwt= cƌ Z۱ҥα{QMM jjjnnnǡC`ggI3hUP333|}}U -!! X랽WؔyqygT+W 00PƎl\\ڽ{w+=%֭Ê+p-RRROCS~T;w.lA`hh(gd2 MDDDDDDDH>mM~[w"==Vannnnnݻ7bccw^\rNBQQJKKQXXwwwҥ?ƛѵkWܸq8r^yN>ܾ}zŋg̞=[o=NjhhE7n\ nuP8pUUU RSS1x`zt055EW_i=Fn@P2uoIII-ڴ[ռj""""""""o+{. +Oqee%nݺL&?&***/eeeM RֆqСva=元*///nܸb۷E d2HRtҥɹwUvMM ]2TVV*jkkGy՞EDDDDDDDDmV碢k.a޼y$$$`ذa`Ϟ=DBBjooo6l"##Р 99YvA@aaڿR7sLl߾]ѣGU{O\\!0|pVVVR⣏>ȑ#o+\W_!///_Ɯ9s/^h!55?Μ9QFa˖-ZߣjxQ=x[ɽ{3gpy6 k׮իTӧOGRRmk&Ob\z@xx8"""X̚5KÇW^A޽ѻwo!C0eʔ&Gy""""""""VRRR"i R[ ):iӦ +WDyy9T>O-llldӦM駟xB1eggf7h Æ oٳGvVVGk׮ &`Ŋ=z4}cMEDDDDDDDD ZJJ###8;;c̘1Z=9:mڰ˜1cpbL:U$1~~P7-𤈭g#$$AAA8p'LS긻? Dٳӧ+}py㏘={6=mQ5;^DDDDDDDDԱqVҽ{w}o̙3 Чa055E]]sNN"oJJ rssunܹseCCCmd2ko333W5>555777СCd=&&&MgΜիWU -;ѣGW '$$@|^Ɋ[emiGӼjxQƥ~$ vĉcX|9ƎcȐ!Xl233GGGlݺ}mj㋺nݪS<<<4,''>>>O((wXn޽ D)S`bs,Y*?7Û<1jsعs'`͚5ppp@mm-ѭ[7\R~֮]ÇC1;v쀃} 7n`رs)))8v$ F/f^5wc ~K-s̙QgT*Ń`ooL&CAAQXXX {!$$DihT[[v_ ???Fx<~666000@CCŠẺ:"//;wָgW/"""""""汴D,rtkH$޽"X,EΝw+++վM]wkjjPYYbAA$I⮮W/""""""" ( YYY(**j"((⏎;Á EDDDDDDD L*Ν;ӧOqee%=8^DDDDDDDD-Qh/Y D"J$t=XYYʊ28^DDDDDDDD-! ^"""""""""zq/tyKppp@rr2ݵ^m?ݻM*gϞ5jLMMuE&Cmm-z-ȏd&/ kkkIwŋxb[a۶m޽;z7ϴgGn.|Zشilmm!ɰsNXZZj}ĉqAt .DDD$ ƏJc9x pI899ɏ/]/U$ƜZ0lذzNEEE)7oԺ@DDDDDDDDOv؁p#G ((HѫW/K ǝj*=wy 0@XMMED$ԅŨMiiiiv ykN.)++C]]]ϡC555>V2L^555Jzziii OP}悈p^=Buu5ҥKٳ'***p VVV044yo߆±r|xPTT#F_~[Cpp0z聫Wʕ+HHH~+Vbb"n޼8::7n֭[VkS2Fm?}=urJ 2iiix饗Uuuuѿܽ{9sNطov 6`Μ9 '''HHHԩS\xnnn{|ekאGWO>ׯGΝ̯;w8w֮]v.47\:|JǢoz At7DDDDDDDDWɓ'_X,ƶm۰n:"&&ׯ_y2 gA` 2'OmllÇcѢEɓ'1zh?^w܁/80Pa/TMmBU|bԦ}Ao5)5Zp!F b͊ރ+wŊ++Lɥ6ѵkW[T( ӧOcΜ9=z4wﮐ/}|g5k[n)?.\*&7ɓ3g0m4sq&NKKKbcS"'':"""""""^={7d 2s%KЯ_?,]K,%KճѤ ;;cǎr Q]] 5j}.\±9s~hu̙3ٳg R߇NmBY|bԦCJ˗/ԩSn8q&LIQ2L\jKUƍӧ8w򗿴X5aw؁e˖ɏϞ=ƸqmtcNBCC!HгgO 2D\xv.]d} IDATq)?*|OZ#DDDDDDDD[4ѽ{سg&Mر#z^zz:z ;;;_~%.]1++  Q .` LLLk.lذ#Kx|___444"H6u,>u1jӧ1l04)򦦦bRhC6l$ ))  &(--EUUĘ,/|… _ϱdعs'N8իWSLѣGh"y\Æ k9f.h3^:tҕ|W: #[[[cǎJJJ0{l 2*_w;HR,^XxΝ~˗/+\Ӹg Eø8qT˗/WQ>|'NݻРuPi?55:&ϭx`kk#55UеkWO[ng}S.oood/ӧ1tP\~ΝX3 ĤI`kkp>77W>'O믿۷+\#Jq%+UͅٳJPOmsQYY{6xDDDDDDDDm^=233 RSS} \\\Э[7ϩ7|;v ??~- 022BTTTCZZqDׯGN //IIIS\jO>066ݻlWq `kkTx{{}N"!!+ȑ#صk쐖+\lN O͛7WHH󑑑ɓXf R)q#""""""HD"˟ӑy nA@~~>vڤpxIcM|K ˪*XZZʟ]VVkkkԅTŨmMnTSS#/VUUA,+ݯWL7rA\jM VVV kFFF033SzHOii):w$'JW7/L?vvv*\џe%/ zh"|066Fdd$ڮ]͛7SN7n<<<|=:j/Yӣ3&&&x1LҮ]ԩ\̞syDDDDDDDDD+x5"""""""""CnEVDDDDDDDDDD! /A''viS# ڸq# fǏ!a۶myN'N""""""""`OnӦMxre˖,ٳ'Ms-,--a5e2ZhuL<|됕-[B*50?yDg <Zv TQQLu;Jf% Bl}#"""""""" Lցu!""Fhh( on8AǏE#..=zؾo>b„ (..ѧO̚5 u/]tAbbڶR@DDLvލ-[6lM]}l޼w^xyy!>>^TrssgݻJ/Q^^?౏?Ǝ;cgg3g۷驱}ȑǕ+W`oo{{{L8aaau72L;sL >ٳg_ |DGG#66j`` <==ѺukAW_ <NR}7n=*}DDDDDDDDDu ^3尴 BF: 7[] `ggl̘1Fxx8ЪU+|UxbI\TUUL;wbǎؾ};ñqFQ9p:uꤶ_F||GGG3J0h ߿RT/ն-^˗/]YS*3gwӧ裏j*3x4 JҤqWڮ-}DDDDDDDDDu٨: }IgO>֢עE ܻwOg;#c鰴4kRL&òeФI̞=6mÇ5ME>+J\||I㠯rǙZ϶1[rrr DKKK`…z<+AAA[oGH$pIpcĈjdffj$4ix7j!J &M+W^C||<.]mۚ4*$$1RRRp=a۶mpwwW[W[Dз@CDYaH$(++Cxx8<<<0~zϤIcXE:?l1֬Y@fkRꪖ5eUUU:yyyJprrRf]GFJ!Йם;wкukUf]&LѣGu@%UVV'?e2Zl1+הqWR-ZD"37nvͺj>kfUUUؽ{7$ lmmQ\\ \#F&Y'OFƍ[^ӦMSSGDDDDDDDDe/@T֭[h߾s~II }DDDDDDDD݋}!_6~x/,G*5Ջ>""""""""y?8LRTTr8;;TP]t 9990`L:s .^<44rnBT +WK/ 4g{^ \[d ,Y۷ot… R#22Rm#T*c p:kRo.A{gcbʔ):u*qiףwP(zu :tyɓ'#44͛7NJϟZ6tP| ޵ŋ_m8m6l؀/[6'O &&,X|9BBB4 eaa:kA`aa InBAA^z%L&ҥK[[2 -Zx.&Ю];@&M`aa\Ν;cոsNʕ+HOOZv6_g{>&xͬW\AJJ A@VV]J._ (JdddLUP(N:ɓ'b\z)))G!55j{*۔  BBB "( ǏUkTVVѣG:{ֲ dff\d2N86YfxuC_߽{YYYF8APXXR-x5] (..V]}6:۫J%nݪ3lmmSWSTugffÇur]xC#i"44nj3p1|Wڵ+?~|v+++XXX%Ė'$$_~شipy 6 8tNظq#;x{{j̫Ġ ͛7 :7-- !!!(--_ƥKplܸW^U8qGDDtŁ1u6k,^侓H$J [8|0BCCP(}v <G]x]Ց̇/Y3cСXv-VXf͚^I&ŋSyfptt sΘ2ej.t#-- yyy={6b ѣ`8}4RRRhѢZ]gYHH8cUT\rE[ê:_;wTKTtoiӦ΄| R)V6x`:uJyܸq8zMNNV ѹsg>իF… :u֢TӘ1cФIP&}HIIQ>ݻw!J_~c?~;v3guE|$J5?~7K$x״G۶W_}T_;|||w^ ^}R۷W}+lggF{HOOGvv68N:_F|||86oެݰa?oFkJ%d2&N'}-[J&OgvQ6ҥK5… HLLĹs琛M'1IRt &e˖hӦ긫ym1[s< !^PSM?bcc1~x^\.jBP}0:kSG"""""""2&xI&Ƭ`D{UrɺڐH$pqq{jt/*{W'4RT}n޼9v튘x{{;g4of¶m4LݻO?E޽5շ,Bqq1L07NKˌ963N6lj4f˗/sz`(ښ={6-Z,=M 6fH^RXX^or9F]զDDDDDDDd>\xd:z(:w.]˵<}/:֨Qmt jI(mk~4 777 :ϟW)J\|s?3mۆ,Y|M;vLk@5ڐ!Cpe[VZ:̙3hժ? !!2 ӧO'|777ձOƮ]`cc?۶mCRR ,^_}.\;v 111xAT_Q ڵ͛7*OJJ‘#G<]O(,,;RSSqmlٲ ,54iGɓ'駟sNH$xyyaҤIXr%^{5cҥh۶-"##f;H$yp5ptttggzTPP?JĿ/}:t˗c…9r$n߾HqA}b׮]jKF$''T*/Ew$&&ҥKprrСCSNaϞ=Xj|}}1qD?FJJ ݻ777l۶ 9 ҥ&WX͛@'L~ƁYa}!eVɁ7$ ,-- \[Bms~ݳ3M#D'U%ٜ ͒%KfܿRVVVdϥ.eeeUI[]v5\ i]@Tpuu5:TYY#++KLP@*ׯ_Gaa!uz!zoI-Cی3лwoL8_1Z֭g}|UUv D[[[C.cĈ6JOOZՑ^L\~nnn&?o (++S{1bcc???&;^Lg%"""""""""3zQ/bgO0~͟^<&Lx!DDDDDDDDDUTT222m>׺ )) [oM6=zFV`gg\ j-݆DDDDDDDDDE.[#GDrr2rss{]0d޽5ʕJ%"##w^|'֭͛L<Gll,n޼~&""""""""zΘchڴi ƏCP:u ={Ю];@&M`aa\Ν;cոs9c|v[Zn oooƪm/**]|< B޽{YYY:P?t HDDDDDDDDԐ1[9K"-- {ݻw׭[>|P(ؾ} <?~,&MΝ;UsssA" @uСCӧ6n܈ިRzjdggҥK:uƌcC\(++3x=o|ɚݾ} ,ٳg![nU>ݻw!J_~s?~;vлfΜi\Cq o)))ҧ """={")) јٳg_@"j<7&xT%Ijr% 5j$NNN?Yimm1c`ΝOD&L@˖-ѦM!R]vD"jD" s!77Wm]C\M&aΝرcoߎplܸ,ADDDDDDDD1kf:`8`{ IDATooiӦaÆ pppYЩS'Q.,,wY\ѣG:ĉo#88  !#M4V\\)S ,, ƍx~T*1gtӧOGaժUH$"""""""" ^32dv A HT/ xE%(k}֠􄫫+8pUݪF9O8^z eZz5CrrrТE DGGcԨQx:wٲehҤ fϞM6Çj1u<7&x .ħ~!Cիub m999D"%BBBpB:u^{6++ 2 FŒ3 ƾ}p-̟? rV/&N۶m9rt0tPqX~=<Aзo_ڵ -[ժg29SǃyP+-AQT"77͛7GNNr9eeeƏ ZÇprr*++5bg㚣DDDDDDDDkԨ^T)gLPܹN:666pww埮֚m볎DDDDDDDDD gֳ*޽(..\.Lj#tkm+eDC= J"""""""""2ޜKDDDDDDDDDy/ ^)wܩkÇ&7T.]/ "zƍ|5%%%(((`G,u+**  C۶mk}3gŋ:CCC!ގÆ ؞w]v3C˖-VUU͛7c #..6l|}},ϟ_~aaae\.Gee%`]_/@߿u7os{ }?֭CXXlmm """"" :6rH$''#77,B0o<` ?bccqMϩT*qygϞ\]]նٳIII(++ɓmrΟ?Hm(**Rzrc 0<,J%[9A{aر2e Np>}ڬuݻ7 $k]5ǸhCo}n___x ̜9ǏaÆ753ϻŰŋ_}l}>{ }?4aڵKshڴ'Ю];@&M`aa\Ν;c&-p,ߴi^umUUU7oÇcҥ&k ޷o_:3T.\\\4A,,4'ߺu x饗TtR6~ZhG:&<] hĈXx1F ^.۬9Z +++tl}={ C?x---h_uD\T`fff_v\APTTBxxXR[|P(ؾ}׋AQQ$ ~aӦM1l0СC8y$FApa\t GƍqUsšGZ۷o_ի_n:PXXÇklZbOl1Pرc駟pi̝;YYYݻwc˖-(//dž pA޽{1` 0صk~z(JDy֭[cذaѣ~zܻw>>>FGG#<<%%%XnW^l\t SNw2tܡCЧOlܸQ7GBx`ͪ>B||MKKCHHJKKE?qcT[:uXcڵ£G0}tܻwOik} &G>}0k,TVV}]}-\ӬYzZ<ˌ-1}:SbB.] 11 """"<&xX`.]~i4*77}fϞc?@yy9>C7?ڷAAA9r$\#008|0 `deeΝ;oԩF{]{K.ԩSpwwD"1wrcZǎo ((SNɓ!.\hoC.]0|?۷1vXDEEAPā0w\?~َ ׯT*ŪUУG퓙˗cɒ%DaaA@DD7n`ZYq k0i$DDDLX&~3|Mqơ_~I-kt^0j(zu2;;;'NCooo##Gbƌh֬₉'bͪCƎ献smCmi̘14hP\&}HII1xSeĖgg]mb3Lf{T*E׮]!H[qʳ35P}8j8S'sQÇ舄$$$L]O[nj 999hժIcl_6W g=66K,ATTT>Ɩ3s Р """""/LYeeG6m6l؀m۶a֬Y #c 6L q}A׳ֺ|gGGG3n޼_=<SܸqèFqq1Ǝ'O`ܸqjl}9I&f_]+݆Әг3Eii).\zK-fڴiAaРA2e z0h ߿?,tSbP?6ٳh"^ Wg2`EDDDDDD/"&xlȐ!P]I&Acڴi:us8qW\^`cck\$-ZhM6#99Y{A&]cdJc5JׁuT/&]===憡C2R˗/c|ضmQ7|ǎZ*DFFm+((_/_FYYj[sCx:Sіls3L/7K_kk8S'Sm+Wěo^zĉj=zԨU; "" ҤW'}fCSc(J$%%{}=Ɩunb~RUUkkkŃ8ݱpB|2d^ [+V@۶moooH$XZZ"$$ .mնSNaϞ=Xj|}}1qD㒒pO׷,((@jj* Tܾ} obŊh޼9ڴiv DEE_7oӧѺukYѺuk>~yҶmLv͛FhҤ ='Oয়~Ν;!HI&aʕxKm۶Ě5k;@"õk"u}}xTH_RۧCX|9.\#Gٳׯ!]vHNN? T ___:xD\t NNN:t(&~?FJJ ݻ777=s| ѥK@kWL&C~??7\:uڵ %>Zjrm۶6mݺuܹsxb 2wQKD?]m=z&1 ]е X jqSeĖ;bhHff&"""""˓p}TZ"Q})JyɁ\.bccӧC" 0+jIcׯ޽{ҥޯ~߮Un ???tդr***PTT=J߇ɈJo^ R),,,pu[n_?E_4DDDDDDv|]||<֬Yk߾}8yڶ*l޼Y1ӯ%Kȑ#z?>;>}4vΜ9S֥^za۶m kuÇ&e\v O<(Edd$ EWWh"ܺuA=Ĵ1ŋX3Ĥ9k1k(ab[d ,Y۷ot… R#22Rm#tPy}ݻ7 .^sRo.d; jk]ŋ .ϫ={nnnl&$$ "">>>&?|0 TUUaŊED0]V3mFd6gKWڪmLb&&x 6/D֭>ɓ'1K=/_}ů8CE&EzZg5vZXXťmVVV(//7`ӦMx5ӦM3iaEE,YsSNX`Wc-,--arEg kkk4mڴ=ĴXELVmbƵ5DDDDDD0kfr RRR p5TVVjweddd@T"##eeerBtIub\z)))G!55j{^ 䠢BcA8C5ԺwEVVٗm;A@QQ*SPPb!?~\Tsx,lR#Ĥ>}d;;;mA@^^?&9u9rK.EZZكw5<߿?kbŊh֬ի4i.^;;;~7oFϞ=GGG:w)SfƏ4a(**Š+?GӧEպM:BBBp;֤'NСCdJPP֯_+Wxvh~~>\{{{cĉ SCDDD $$={DRR1ydm<~8X' Bjj*\ n݊dkmGǎo6mɓ'ѣ:g 77}޽ T/5dh߾=RRRet߾}f?KJJ`iiM.k]7U&iM‹m6?Ǝ;cgg3gt}6,XgϪfunݺԩ^~ejԩSƍѣGG_L뻆xks̘1hҤRsN_kC/aT'jΨǑ~񁃃^{5ѶW_U๭ヽ{L|*?|0UKil̘1Fxx8ЪU+>pq9 pq|7uNT۫>k|\l;5jݻ#==b&aiiP(X??~Q}^PP1#lmm5bj_w} ϯT%w}2ڌ1Æ òe0p@|x>F_Lxsg```眾:bb6 """""o=^CD5>1oz*r9ڴicvI$޽{Z^o mڴAt^ !J1zhbL2aaa7nܹcT̙͛c֬Yضm[ 0gm=M 6fBNt?{l,Zz2da6mP\\H罡\ڧKaa!{_1nJqax:Dž pyL~Ib5.Emj=sΜq-&vtDDDDDDd~\婒GEΝѥKz=/P_]g5jM* '^ھ; "" {%5 IDATh|9>> ((ӗR vMCkN8vXS:hvPuZZ<==b+ x1m4L:U~JIIIzHEjk1뫏>ɸqs޽cUVVVZ_UUkkkۧ-OϞ=:m!C ##C?/[{6q)v}Fصklllw^`xfϞ ;v 111CCCѥKHKKw]vyIݺuܹsxb?~QQQh׮~=zD"A@@F7pA۷/N:Z!99?3qIdKCIII8r9$HMM!M4ѣG;wbΝ*bNJ+&A08T_!--MkY\\>p߿_5"""|r裏~zܿVի~%?33~~~Z+}XXXW^ؙ϶xn… 駟عs'***n:ܺuܶm8D*Ett4Ξ=( 2D<$$HII?777l۶ /^ӆa(^bꩯjbs`L\]}ODDDDDD D0Uϻ֗4K,5kp}HRXYYY`mmmR2[nvjRRDNN\]]uֵ\גּ 4+ƕh߾=괝biGEEzSmbbbӧC" XSLgҤIرcGc7&&oF_\v ]vչDʚ5kZڔx/X}_yɁ\.$QKEE,--ƍk][UUyyyJprrH>i10/bKl=r< ZL_}"""""?FLro]- 666u~[m2 -Z[WCIgggU$Nm)vXYYܭm;wкuk]W ƽ>0a:QRR:xt&PRR3%}EW+ю3fnӦMxm_}1bE/^ yAL}""""""jD\nR&?2 HLLԛ-.. 8}4L2&f7Vݻޱ6W\OPжw͘P芅gx s5L\p JKK/^D۶mQ\\#GpppJݼyvvvjۊj*t=B߾}ѱcGOm_}f͚iďJlo GGG$&&">>G}%Frr2鉬,gcƍ7qƢbG_,3___\|O7W?Qfv1ر;v?OHRDFF/@^^v؁~Mg+>+##=zP}3gD^0rH4o٪r'''# Tcǎa8oݺ߿&L@`` 5tMclHII٨̛7ݺuѣ<iӦ6mf͚07ov#F`Μ9Xlڌ8vN@3Ƥ5Bl3gΨ%8p 쌀꯯ČJ$%%aԩ8p Zh_KO1yd׈ٴ4Ogfddh /vB1{뭷ШQ#HR+DDDDDDDp1kfoϟ^zaԩ?>:v숏>ѵkWQR(8wF?@FF ve$&&wFee%v\ZZu릶mԩ54i1eBTΝ;pss3bL2Em DݻNFF~W :h"-Y#G`ذa&?<>kk&I" AB"mT"ZJlՈr[JmjQ^EhlMHJR"$bM"Ϙ3 QG,|3S\\ L@cǎ0`cD]?h y&:t2[655X 3Bit_$''ޱ> k.,^Xi9r: !!WY_[4˗/qo8Q%?DLL ,,  ]8;;cذaw9tJۿ|J3.^aÆaܸqϗٳg+mk֬BCCyf*SxUD)Dfoa޼y$+ۖXpppPjgBB @LL ]],J]?h AJ)ɛ~A,C.#!!֭S.b%dguu5RRRЫW/5.e2vލ'N`ժUpqq ᆱנATkh BL׸7Vi ^#jժSSSEEE9s& k}ԭ?B*bJ[l,*S裲ɴ(KR*f9rcǎŞ={PWW'BݹsG[no߾fnn֭[kGbb" \d2l߾}ұR/_V;A[_uBB^yJkkk3^Ϟ=VZݻHHHЫ޺b%d'0p@\~zJÇ#++ PZ6~cll,^yܹSi,BXTUU)*k -VBnjF.EEE077e˖ǎ t{cޟDDDDDDDw͛73*e}]|7ܹsi&k6k׮a֭hѢ^uՋw+81j/Y3* >͚5C^^<ТE 888Ņ$q, gs3x>3>zs/=8 d2ZBΝVm-.._Ν;sƁs`d_~%6oތ {?>Ν;3g"??oP/}6F~E*2DDDDDDDD ^#}pIXZZxD`` ~嗿U[322pyY"͛7ǤIp-E۟?˗/@DDDDDDDD5Kb֬Yj{Ǐo׻?[(((@PPQQQ>>X~=VZ___$$$(;yd'B ?J:… ѣ/^ {{{簳… RI&wXbJKKѾ}{rܹ!!!b lܸQp֭[{#FH"::Ù,dXpyaa!rE٩SdXxg)BO"Yg"b]զdhٲ%֭[mڴAtt44_RRCA[H$L8QP}}]xyyA,cĉJ3R)ϟ^z ըEvv6+Um;;; 6 尷t+PTTZm>C@pp0FcǪd\Rs< qҟDDDDDDDD0͛73* ^cZz5 I&yyyRk,,,…  411{{{̛76mB^^"騫bbb HлwoɓsB.+W_07od,AH ^#rpp@ll,.] >yyyHLLĊ+t&LMMѵkWէW^Db1T]t)VXBє,'ѳY] ^[%ADDDDDDDDyVbv0KDDDDDDDD#&xQL=LJO:&&&="""""""""2S>K FV *oRSSaÆ`ǎ /WWWd͔]tEEEʕ+x!Z=ۇvڡm2 7o'|]8vN:ѣGo߾΅ sVl@]]͛(ۻw/QWWӧogϞ˗5O2Ν;ppphRu7`Z -ZE :GRRbccaaaL???UVܹsMDDDDDDDm–-[e˖?4>(pvءJjUaÆqFZ*b޽xw`nnT*|'9sRkh]T .<^x>3f`ʔ)߿?<'^'m5j$]kkk,Y[l1/7oFPP{=̟?Ν̙3oǛZH=&x 6G>0J=V\ C 8]BѱQc-ajj SSI"o6 ..Nf2lūWҥKO|SN{{{B" k֬ݻwx"&&M577GuuEDD`߾}X~=lmmc000RWMxS#իHJJ\.Gff&QWW_ZZ\T+WJQ^QQt2\v III$''H܅Xx1pmܾ}26䠦Fe8mr+8CAA㊋Ֆ͛x_՘+//eR[nXMm())Aee%*++ J,BtE\.GYYbk-**Ҹ!尴TNm1Ziҥ5k,--UƏ(t7:zLYrr2LCbو?+LNNF`` fϞ +WD=ɧ;w`Μ9Faa!={6qQرc֭玎aaa kP*ajjZW_}6m±cp=8z(Μ9 &(%bcco!11 ,@ff짟~?jlذGQذa6ljjjk.xzzbJJSNÇ!QYY6ɵn:AAARcŊ(//Ƿ~ӧO+"##'N`ƍv6ѣG1h lܸ?3zݠ>wB$a =x`l޼YcOOODEEiߔR">>CŶm۔Χ-FBToܹXfDz' =z-srkyI4UG"""""""2dȆQFaݺu/ЦMk0}t\|VVV6oތ~h֬ 3gTc޼y(--_|_zL8HJJ6uEc?"""oUd\zU["22RQ?{bnOO IDATݺaĈ֭[cƌKz oooxxx7߄7!""ڵSɓ'ki+'''#88ׯ~8z"cժUy&V\K0`ȑ#qIaƌs^xQc;t耑#G̙3HOOǿ/CgZݻwC,#<<SLQ*EBBɓ'#&&FP>}ZgCc<|ل 銑:=jҤI78&&&puuERR<==8333i=Gqq1BBBcee9sDDDDDDDd}pI\tIzHj666xWԞGݶ_~OhE郰0 ^mb߭ѵkWVVV(//W޼ysKtݻt]ܝ;wFTT舯ؼyJrwÆ 8y$vޭbSNݻT/22)/Dnk~駂⬫swwH$B߾}ԯSNEv⢶䞺d}? k :=ۻ)SZ/U~!?.\@=xbg;;;,\ycԑ '~}c|v$d]CD"8::"++ NNNz?RdL&SO&)www8p{Vwpppܹs}v2eb֩D"Q777 ,777Am`ѣվxNht!V!1g5Լy+f%bff#FJk;;hӦ >\RRCimD"ĉ j1HDDDDDDD5xQ~~>IgϞOJ?~kkM0zr$UR/HII1j(\pAQ&J____:cT|QcƌAllζvC APPFv+տ~ ???)PZ[[+օMKKmT?C_f͚MJ$rݼyS%Y? #m񊏏իW T*E\\nիW8s<~^^^߿uKhsiܺuhu$"""""""1Y|glcABBʐ 6yвeKիLD]{.ƌ8lذ}6^{5|y&aooxQ۶mqi\p 4-Z@XX!$$ԩ.]0dee{/rxxx 55(**7~ڶm X#G@&a֭6m;vw////ܽ{˗/GMM <<<k_lH$VVVسgC˖-Ѯ];lݺֈb\k׮]011AЮ];m'N`׮]PYBv_|Qسg:udܽ{EnЦMciyyx6:=5_1-[Dqq ?A,BBBz:r?/2kjnݺqi"""""""zD͍7 DbP&qITUU)H_#8.L&xe:"""""""""2`>s ^"""""""""z%kDDDDDDDDDDD2'44W\App0:v؞XXXX@&EEEhժ:whIJJB\\`رpqqQ'&&.]½{о}{XYY=ה)SZb-4.O? F4~xlٲ_~7oo-r9~'\!!!P􄵵5O[naJR;v@zz:~w?FӧZddd?D֤c-"""""""" [V}ap=@?:9::"00GEERYBB;;;XYYSN{{{B" k֬ݻwth ADDDDDDDDL6\BE.]YfR?u{*ꤧ]tkyJn޼bD۷bH$ŋq)?JkjLLLsNĊ+qF6?6&xNc #F>vXM6O&KJJp!J$L8QP}Mqaȑxu?zh 2֘5k6l̝;ݻwWO]0sLc*/uGIR̟?/p |ꫯDDDDDDDDDOF燝;wB.C$)^Vի1`L4 WWұGgcii ///XXXh-BUUNNN+<\>5khѢlr9jL>zGEEEA.m"<<&Lz&&&aooyaӦM˃H$jp=mLQ.]G>]|/бcGbҥHHHÑDXB皯ڵ;w;dff&LٳǾ}p,\H$ꫯзo_L6Myoߎ.]MwD033?>#b8qNBjj*0j(q{qr 2vBvǺ^,#Fer """"""""M$}ZiM~Rppp@NN$ lllf -- pqqGPPD"b Α;;;Ne!y""""""""ҬyxSLLL>e˖8p:wEf͚K.z͌m @Bb-DDDDDDDDhd2~'D"XZZ ogQe{\ GwF55Krܻw۷b,--Euu:7Ua8ǎ /WWW>˞rΎ """"" ^R+W 88;vTlOJJBll,,,, 燢"j ;wzγg˧LDblٲGV^XXSN2Cvп6L͛7O>Q{r};v NѣѷoF .DΝ="":̛7OQw^ӧO3{O̜9S^g䮮nL1~+F޽{o߾ҥK1b̙3믿0zhܾ}l6kwO;BX[[cɒ%زe>g1uDEE1>> ƺu """"""0ۨ,,,кukطo֯_[[[_T.&&&ԩH$pssÚ5kp]yU\tIcM+mdxacc޽{# ~Zr%U2t ejj GGFr055UxqqqJe՘5k㷱ީ͛7%Koƨk6kwM!BڠcԳ:m3Pp\?ls F QXXfҥK1k,_)?~KOOGaa!tD\RTTTuCAAXT[j|85\8<~\qqڲܼyF7VVV@hh_Umo߆L&Xu6r'tk)ڮv}_ߢ"䨋xrXZZjl SC 8ؽ{qtY#33Sm{^WȽ4YfBu 9&~j\니{Lى'駟"%%{@=yB$aƫM6رcpFd=zgΜ  T8q7nĵkה}1Ku KKK\v ǏǷ~kpJJJJ=ġ^ll,~7$&&bTOP]] 6ȑ#0 6 Æ CMM vOOO_^>RT%QS(,,T$`*++U"֭Chh( ,pXoqiߚGŠAqFEr /DEEaؼy"ƞؿ)))GeeXhG||<m۶W[֩D"wDBWk}зo_L:eeeƠA0w\)[f ݻT[*q)B^7w\Yq74aߐgcKWzi"会s^Ϟ=q){LџEO?ū¨ƗYfz]oXzڷ>>>?~<^ ___x{{=z4FLܽ{#G'w}ݻwWik>*55 ҥ D"vYZCnݺaĈ[o3f@.ŋǻヒ={b…Xz5O EEEyd:N)-1`L0A^b$NbeeK<~x̞=mڴ51m4l޼YԈ\.ǶmвeK9RiꊳX 6?jҤI>|xbnbbWWW$%%t2}ƖDy513P犉񄈈Y9r011QJ+,,,^ҥK1rH,ZHЋM:+U񻵵5vTr(**RA̜9BgϞ5\84o\/.]{!""B%ݹsgŌRGGG|׊YhڵSwÆ زe ;7XS*z///}"##akkhDGGJL֭" 駟bܹ:㬫swwH$B߾}ԯ/RLV׿t=1V'cQq!ԵyҤI8|0***+D"\xÞ={sLu\[Hy{{-Gz_wj_W7[+-- nnnmmmΐUVA,G1l0U$YYYjg*7$>~2L)www8p@6qs۵wʔ)Xr%Ο?D2u6Фǣ- _m՟$ $F1CTVVŋXh^ñ~bL8Q 3gDpp0&O,Ey,BlL7o}q mPNCbԐx;V.cժU3fRQ׸~ml۶ rfffJeQQQpf\.W=gI۵YTRqqq%w,:gs =6~"`aa?|sd˟J?eoo{{{C"HHH7 ggg7WFFF7o4DFF;wR;0p@Ŷv_|Qqqq AFF:uK.!,, YYY޽;"##/СvtAiy-Z ::Jܹ3!J!˱b ̛7OeTX|9N:Zh0 .7ppp@QQnܸ/жm[b9r2 [nŴiзo_ر^|ExyyݻX|9jjjᡴulH$R+++ٳfff4[i IDAT?Zlva֭F\\вeKE6_ k.ddd,u![{ANw">>pppؿ֭ڴi3qiرpww PgϞw}D"ܸq8r:v숏>Hi֥q4JYYY᧟~B֭=zݻw8뺮kj?O%ɰ~zA1e-!Yhܾ};wF6z}r aȐ!ŪU J!O"죦F*"77ɁD"R2iiipvvK^Pf ())Qӧ#$$Dmoܸ,SWk8q5[ndrjjjPZZ=Rprrcuuupuu:j}T*ENNT*ŃRFcdC,Nzixh:_SPZgKS;+[8 vc!~ mϺWc|]xP"""""2͛L2KEEEC5rDGGcر GAA;,[̠M&SN NQ?)_K2DDDDDd4jkN>>>x" :֭[jגW˖-Q\\B+Ofバ#?y|56m{@3xI T;wƍpvvD"1i8y$^EDM///,?˞rh]†\IV ^"""""""""2g5k,{ڴiMFDDDDDDDDD6m3Yo%Z2JMMENN ngc:v^xy<Grܻw۷b///Gmm->zrtmcODDDDDwǗEEEaڵO}`۷gΜQ&ɰyf*,[ 'Nк… A5v;v K.ٳgVC%%%ow}#==gϞ۷ 0۷o_|b-[T ~Y]mm-Q[[Rfii;vB?}?Ɲ;w˦0mkА1i ƵkQol2,[ AGFDž cm(--w]OQQQ˗/kG*b޽xw`nn$9l0\)i4!c:&A׸2vu}""""""jm6l؀:tZ8p(Xr%U2t?)&&&h۶}r9LMMk*455cj۷ׯ-ɠ@onnjc6m믿_~5kA kjjl2,XݻwǢE̒\|pDGGkZ2H&x,99SLСC1{l_`` fϞ +WD=I;w`Μ9Faa!={6qQرc֭玎aaa kPⴤ*ajj1޺u!++KQo>SNEYY1h ̝;W)ifܻwx뭷I'J5&hشi;Re_!툍oD,X:א ##=zP[m"Hg"Y[̏;^zy&Y[[#==]eߞ={ԩSj#}rky<ɩĉO={6.]¦M`nn,** VQOOODEE5]Cx6ܹsfK9Mug\ >5= q :Fºu_M6k׮a|2m޼7lmmѬY3fΜ  DJJ 1o>>X~=VZB\zְƴi8\.Ƕm~!..1c6tM֭Fhݺ5f̘3$r333)m311+wOBU"]7Dm^h)..FHH}0gA-ZΝSܺu}͑#Gн{wt;wVJN"!!Aɓ<ƴk/ƚ>4i>.,2/5y70zh|xKDYƴk/Bon~V}ǵېo}ѓR }>}5H$]"Rz]pqq5^Ç!1qD2̜9D%!8o<|0`1m...(++SVZZR.mӤyD2QWWqLڗX[[ŋp~̜9& kh/BoCiLsBnck6|E &&nnnٳ5Ǐǽ{Z&L@xxAWJxzom۶A.|9** r>>>J.+]SךcƌAlllS.+A]}׮SRRgggz^mV^Ç̙3J@i(pӸ8 $m1o۶Z̚ӧO֭[bEj/`aaBڧ%п?뜏W\QĦekmַ/K\zUe VRE+H^wMSmcZ5]zjCCƤ眶:3]mc&xW_}k"00۷?Kh ((H%]vYfXx1 ..aaah֬,Y̛7͚5Cll,88~ʔ)ٳ'"%%{nvv X`,Y'O"44:uRٯW^D};TZLLLT0V;p 8qqqq8|0~\rEp;3(ܹ;wTxTV!Ak"55駟b d\.9X[}||رcX|9r9VXkƶm۰rJ}X~=W_a͚5jMxyy=cjj]jwmҥ >#|gHLLΝ;QSSowѻ/`툈Ps!44~~~Jr pa8;;c|1Ƌ[H=š!cRsN[BƮODDDDDDMHnȫvE"/i*-[k";;bjg= UUU0([ KRIc]*˃LMMQWWWFmBQSSR^Ԑ<iiipvvO]L>!!! qTbwwwK]ފ /O6JR999H$QԦfffwZlvvL&Cvv6X ;;;䣦1-ƋZ/9]q-$>cl?f9a1BHÈ2 iJ"NID!T9D69qfXkpYffLuy_zk׼lٲR)+xKVܕ2eʔxqWwnFBCCQR%kѲKw87;Ow3__Bwo6?׫Pڴi5k:-ƻz]o߾v(LTTIZrXQff;+-h4uw[.W\νtW^^^^«`~+U *؍3w>YR9Wqgϵ;Ϯgp[VX3g+r̻B JMMukЮA?͛K>`X4g/r\˗յkB_7k, >WZ:ۍ~yv=[[4_ ,l٢u鶝gIuwe˖.Wʹdɓ'UvR?33SiiiZMNlzv[4xdo߾3gOb1d-\f/;/Y* ѣG.Qiҥ$KxYJJ+ɤXشݻu!Y'''k̘1:w/,IÇk.eeeI~%$$5}H˗{npD޽[}ѐ!C4yd e̙֭[ɓZnZn3gH֯_?S￷x9!CGOպuk͜9ӭcժUvZիWOwo1{Oѣz衇4|p=*SO1B׿Խ{wkJz쩦MJz-[$5mT}kڴi>.]>|b naIARnn7o`jJ믿JvܩDI׶Tv~v5<$I09(??? $=ܹ$^sÇ5j=}_חx8h(EQQQ ֭[ǒ4ydAAAlov?+I:p$^/ lŊZ~FlS{|rرCCոq4n8ڦwjР^{5ݻW͚5{'ԬY3M0AӦMejҥZdw}*SƎ˗/ʔ)M6iҥ@xY,tNիWe6n#???feeh4O @Q-[VX* Ze'x( (Ġ{!P,zq5w<^(UxCy 7jӦ~w(=q|ڤI4j(Bbʔ)G/x, ( /x( ( vԟ~MX,֭.\jժVZn3w\8p@/"dza:uJTvT^=>7bXn :z>s/_^E׸q?Yfn6mhƌjٲecX,9r*W7|S^^^԰aԥK˭~L&ݫM[JOڢaҥZh /_^(t~~~RJYhon-JR``M_]ǏwXٳXsU}(]UwԨQ:t q+WtQhkXte9Zܜcǎl6;gرzg|+WV˖-{YKOOWZZTeeed2)::niii̔bQJJ9GIr'rx/]Ǐ+""^zY͟?_fRnnMe˖Yۭ_^cƌݻ`>}ڦ)Sh޼ytg:ɓڭWv)IZz7o>LpBkժU_~_^~$I+VPC-ڵksNAiҤI|*qۼd… $}||={Ӛ5k$I 6T-ԨQ#I+XFIY+V(&&FԥK}G wyܹs~nj=ׯ_?_^7*""Byꩧ→|ݻ$)88X/:Y}QJT)֭[W~~~xiiiz.]ۜSV^<5jZܕ홛oʕ*_8-0+p7 ׋ ??cz'u"׾}{>mhQTT~W7 {fL&]z~U͚5O'А!C4}tmqر9٬?Sݺus8}}~g6M&U|y=ڵk͹K.ڵ8`-l-83(&&Ʀ@쨠,I3fO?'N_d|||cr:nٲe-`16EjqF5kLիWwڟ>cbb˧ &L]&=zkݺuիʔ)jժ`0hٲe2͊J*RJZhn:mݺUGQU~}i͛ոqc؍'$$DO=|MO/͛h"T vڹsƍ+<<\|FjԨ0I׊G; *::Z!!!NUB};Vj׮ xYnܯ6XթSGժU+pd2ܹs -?dŋuwWr7L&?^NtgXt8qB<ʗ/olƔ$ )55%hGVXX+WxA$m w_]իW׈#Hp2ϴvZeffڶm @UT IDAT ^PxCQE<^PxCQE<^PxCQE<^PxCQ|ݱ]>sw}2eOg.8/_և~h¦Ml!!!;v۹)n&>[yyy7,tq8^P-a6lڴi\uE*Wl}r +Vcǎ2X,ѬYtU/j˿n֭zᇭm4dv>>>vOMMՏ?X`_wvv}$IG}9mݺUח`ЩSԾ}{VPASNnȑ裏gYnۧ/CR||*Weڴ+j|*V4ԳP1<9+ͥ|L&]VkV:u~=|;{𖠵kz%I=^͙35aaa:{l'NlСC V\)Iڲe޽[ᒮlԩ#///y,???ujӦ?Vo:VZd3//O2:|>5jH۷g}3g꧟~*(7b۸qV^AiͲX,j׮mVPDD ,$Wϓ#&IvaU%wՈ#4h 5nXW^ @q„ HCڼy};V{}'Iڸq֭[Y,5o\FڐYF۷95k֨B ڿΟ?/ɤ;* @o6oެݻ\W\9-\PUVնmeS;rRRRc`PJ qg1W^]+Vܹse˖i޼yjݺի,uM?~kժUKڵk[nTXil&I4i$ӤI4c )S']?P73W;]*ߒ裏g5yd5nܘ/I^gEϞ=xs͝;W[c&4uI۶my\嵰+gL%9YXx\\/_'Nd2)==]֭S^u.۸ʍؤkEիW+++KӦMS׮]^[t+,,Vq٬'/bݝ|9skiݺu=z|4EMiʕ+:vf͵gΜщ'dX\cbQjj\>|._z_ -Ҕ)IJNN.9L&EGG۴X, PNNN=<- Ţ{N806 c^_|q{W\q٦\rrxk׮Ztswegg[=z]^n6F;PF u]͚5SO=iӦٳjٲ$iѢE?~2335uTmٲE4i$%$$hzEL{]|5kO>D~ׯ_Ȓ%K&///y$I .TxxVZ%6-[ӵrJ޽[:tPvv9/ڿ֯_O?TqqqQddXc?f͚\M6M˖-skAiҤInw͚58qmۦqiX,%%%9pUVy֟7xCqqq޽{kܹ|6lP6mr ISNٳrJu]YYY/_2335sL>}Z?|b2o1cƨAVOo`ТEԿIR.]uVk~i֟;w;w*66Vo$)::Z[lQZαm۶ƍa3^x={Ӛ5k$]+hB5r:nNԧOUTٳg>mڴIA-[VCմiT\9柟+Vcǎ2X,ѬYtUk^܉N%l6kΜ96RRRԴiSuUyoիWbsΒbhѢE0miL~-SM4 ^Mqhʖ-k駟֮]M&<.]8WbbbrZ$髯#]ԩ0EGGlٲڲe"""TF ͟?_wu>5kjŊ_uǏvڪ_ö.\_Ǐ+((HnݪYf)..N)))JLLԃ>hӦJ*Z`j׮;w*!!A111jܸ9/R{w^-\P/^T6mԤIs_U\B}72Q}5jHM6`вed6*22R7ov8nxxODk=Y6lؠ$}zԠA *UOHHqBBBf#ʕ+ jժڶm.[N?6+Wܹs}v*==f#G{-[|X#G,J^6Wnk֬ٳՠAٜɓn#x>#o^u-~']tرC.]қo5FQժU+L>]ݺus}@@|||tڿ%WoooG;v?O+L\u rH :TYYYvϗ/_^ƍӻ F(e?~\+V/\,:tHիWW@@q(&IwVBBƎիW;.''GeW\ѣGW\vvuܣG*11@'N(,,d I]vҥKՕ+W=5sLIR֭?=q6&W8bʕsRHoRVt1m۶M/~'u޽_~EW߾}+g׮]0I׊O{]޽/ZWپK:s٣Nk֬ݢE 5jHz"3f(''Gcǎ-ϩSԨQ#h4*;;X:Oxx~%$$h*W{]򼔔 {ю;Ce/[W_}Cٳu1;vN:رc\BO?8p@-[$mڴEEEh4СC֕AAAtxҥKU~}cuիշo_XWUfff*88n? *콰mVtnڴg!!!*?^^^Q%E͕cz_P^EiNpj߾}soQ׮]ժU+XB;vhbQLLf͚W:,0i֭zᇭm4dqo6e2TlY :TӦMSrꫯ(+W^~~~nǧ@*TԩSm9R}M, IDAT)nŘ ܹ ?WPJ^ӧO+33Sm~~w/nݺ,C)88X~~~Zr{nKRSSUN/X,֢O?7xzd2m|QVXXΞ=ks̝9f}rWwSnŘ 9Gʖ-zH}Yt'L04^Z.I(ӧOkڲe233uwZj К5kTB߿_ϟdRǎిri…Zmۦ(ݖ-[_]"##e0l2fEGGk_o5f͚5K_|.\m(h͚5j߾;s8rRRRcպuT^](܍Vcʕ+5w\m߾]JOOw$Gљ3gԡCtyYxvHMMռyﯫW***J;wtБ 6hݺuj߾:vX,_ag/|ǫjժ>[J |}}eXEb.龯%KlWjUӧԞW^yEUTѸqJm̼n={ŋ QFFF?OڴiSjwyG]tQÆ KhԽ.  7^lS]z֭CBBʔ)c7AAA:yj׮rrra [_oa%?%SQ.{aϿo5Զ?-JNNF}͘1HFUVXc4{%I*U3gŶbccgR"UZUҵ='%)88X?T]EZ[1EIz5j+M%ܹsU1b͛7OS@@pΝ;kjҤ*VO?T8ۚt+U1TjRjjz&)(~ފJ*z'?_rEǎl̙3:q9^[ŢTegg+;;[n}a]|YՓӕfoVV紴4effJ lo&I6,SG*//Ϧqծ];y{{o9a:0^.Ùc.aÆҥKvj||||cLOO/Prrr| 8p =zhРAիӕ#G˗z>_*h m3gΐ8P-5jPլY3=S6mΞ=-[J-Z+33SSNՖ-[$I&MRBB߯^xZĴQ˗YfOߪ~n,Y4͛7g}&IZpõj*IҚ5kԲeKM>]+WݻաCegg;/kOEFF?5k֬YմiӴl2- ///կ__۶m2e͛K.o߾:{ݜn޼&^z甞sΩiӦ4h^j~˅pշ%iРA4i<}we2l-\P}9fM8Q۶mӸq4{}Z,%%%9㨨,ehTݺu] * 奠 \M0AڵS\\,X^z--JȼyԡC-_\&N 6(88X%Kh޽z5{l=3jժ6oެEiX,FEEi֭:|ƌ X_7|#E.]X_N&I?wY;wTllz-IRttl٢Zjٝc۶m7*""fn/{w}5kH6l-ZQFNԩSS``uEZLLVXk>#}ris^teƪlٲ*[ _~mۺݷJ*aÆiʔ)Zduw٣>@6m`/%%E_~14|Ǔ%ݺ[e:-9R .ԉ'T|y߿_#G$qp [L&F Aʕ+z4n8uIzݱcǴi&رC/^t3///m 5j$///EFFw +i˚Ɵ_= \K~6ԩիWkذa-[Zrʗ/o-,ggg[Wӧׯ)S(==]s[U>,ۻUz)Ѩ _i7~x߮]/$$DG.=&Lĉxw]Fgu}iʔ)? %`׮]V&M$I4i6mN:2!==]zկ_?:ua- swn-c/ ^f6 f-lٳǺW5kOGvsi֭_ N:;@b`65gc)))jڴvڼ@~իeXԹsgIVNY,-ZHҵf~0mqd2el< @W\_no'是lٲ=sL3gO?ćJqKݙ3gi&=zT:OwyGuQXXUlYmٲEQϟKV͚5b կ_ߺUvmկ_a .믿n[nլY%&&iSJ-X@kΝ;5nXGї_~x{ڻw.\/M6jҤI9毀PFըQC־5jM`0hٲe2͊͛7;7<,_(vυ U3բE S~l٢?S#FP||w﮽{1n-܍i͚5:yƏGZDuʔ)wTf͔;l6+))I ߹s.]H 4H:qℒմiS>PJآZ;hРZjU`uh[\`[E~@ UVM^^^\*Vvk\4FJ?n^r'٘ײeKI&i咤u(t[%$ӧOWn 駟t%i۶Fazrڷ~1EFFjС*p. @>>>:p,ǫy.c .tmԽޫ?XӧOWxx8T(E[ ٳZ`Aռys[ɭbQvvU'NR \N;vХK/t{s;n\999nL/sk׮Zt6lx?wjݺuرڵk'Y,5 X - }jԨQ%Zi֬v}G533S3go8Iӧhj*5od wJHHرc??>8elH ƤNE~),P,ԙvmNbjМ,0f3ǎ:6;R|=_ryxҦMu\UPPE5d-_VLN{S<{۶m$r-2 2w WQQ\s֭oRR.???YZUQ\C Qff$IGl*c2t@pE:d2W^W_}I&9XB%%%) @[ ޜ}PSSSմi2N:)##5sL]>XZ?{yy),,̮e2tQkў%Rlvfqqq {o@E@4rH 4H'NUnnnڰad2dX ҥK6/y9X\\w^IRff-[}ꡇߊx{{kΜ96BCCQ5~CrrFewή%puuu V)/:UV۵uV9X/6l(777EGG+88GǏ~1bM?SNծ]*[*+11Q~~~o6l^d ,e-a:>w uyyyyH6^+**Vń 4{l6j֬Y:bYfO>ҥS>gwֻᆱ/ҩn&xifY ҂ scӦMrqqV\ Saaf̘ӧ;SXXgyFn6\IId2iyg5ruuURRRZrW^ndL2eJme5jo>}:_ d(pJ҆ 4vr8p@ZqŢrKLLԑ#GM6ŋR.\`?5%%EYYY6#X8%o߾Z~}b\sj:Z o;wN/4edWfرCݻw9HJNN$eddh$+<<\~~~ZfM\R .TQQ}}ssŋ@~uV8ԩbcc5宠@-iȐ!Z|5学c)uy4vX B 8T%$$hʕYnXƍ5l0 :Ժ2&&F>>>.222eϞ=ϵyf<:w,wwwWnnFPaaa6}>|X;wRWL&]pZ[Q Ç[W=ZGrj#Pcǎ;sN=|Y | K_qjڴ<==K߿ IRlllZ~:t`sW6mСCj]M #''Ggy/ [xRSSˍ=((HO<ݹ dҁ+i=<<hwnTLٚ9sXZ?{yy*:|_1x`}xIPt֬Yoqqq޽{%IZlzH6lPPPL&,pB]tܢqyJJJJ:|IIf4hoQ8(I^uss;NWW2c{תr~zd5oTLޚ3gͱPEDD8|_SOi̘1;wnEqc ddd(??_1bSjj׮/r԰aC)::Z`XE߶3_4c kۿ2 Ǐ:nO%%%rssVDI[؜!&G ֭[5h 4Igi&]p-2224o<߮-[]7oۧ'Nl6+((H?jɒ%:tڶm@F}*))ѢE4b}G .'OG)zyyiݻͽ_Unn{jsyիռys%$$(88 5Ӗ-[;|:z×88WifL&Ǎf͚ouݷXϟT\\\j_Ԫ#33S-Zh$]VO֘1cd0TXX)S_/?J&+s֬YӧtRsvi5k֬N=GUi„ ={SwTT>mݺ/ppFR.2eJرCmڴKO+I&I>>>b$9rDwuw뭷.s#FY(8[d IDATBjqXRqZc_pݱcG޽[%%%|c/:/_|aD+VhΝ;'www <-f͚W_}U7{O!!!ւ +ӧOWqqRRR)& 괪xׯjY[טfY_VٳjѢ]bRRRԶmR狊4sLmٲEoRSS?:ƅpEEE*((Pr|&ɮb$yxx\p6 PnnnCCCzjQFڷoBCC(35sL]6%///y}NN$ݽ{kڴi$Nmԩ!ܠA2 'NЂ Ծ}{͞=[nnn:v/^LbbuZUhT- <wy>SxLL|I-]T#F(ڽ{ԩSӧ ԴiS5hЀ@-3SHT@ޥnZJb-ZH%%%ڶm6mڤ#G~bQϞ=@R`N^zruuչsHFiѣ>*@L7yyy:޾}{͟?_:yN8Ç_hTVɓ'O))):~H*PM 5j/oϷmjjmۦCj׮]4i bRIIM痻WƍX 3=3$___}w֭5kNu=vQPU߻;t6_0`233jqh4SLL]יcڼy/^;ٳ7s.77 5QY,͛7OsQ^/:}@ѵkW߿׺ut?#?~$i˖-znʔ)5WMܴIZZuVW֪UԿ襗^ nTu-[ܔc?pZjU(nXt… ;wԙ3g4ydujPnnnvk*&߯ݻ_ZjRRRn:uܙ/AX tU7ݸfvQhHJNN$eddh6:r´iӦZ@-kȐ!Z|yi۶mU]z}]]VAAA$n tj֭K߸q cǎ)''G#IRRR.???Y2d233%I=zfst+**R||I8hTL&^y}W4iM1T5mڴ̭B%%%) @k]۩S'eddTvvfΜ)b=Pg///WppL&8`]I@uرb5i҄d 9R ĉ:8i޽L-[L}C= 6(((H&IEqqqZp.]$֜9sl*""⊏?l!99YFk ڝg@GiժnvmݺtĈ6NÇ]v._5lPnnnVpp +11Q~~~==]>>>2R}jvŋ5{xzz`0T.00PvC @(pJSLoQ`Z-ZHo%IC +nʕZp믿3g/^h 8Pmoխ[7c%##CiשS'V[55%,,Lcǎ ͆/ 0nܸQC Qffuѣ+[vϞ=E:uҤICN:z75tP;vL åݸq cǎYWǦd҅ -גԱcGq .O"d2޽$C^~zu是6mڤCպ4??_ 6,GNN<<>^zծ~I۷oח_~^zٜM6i֭^yM6M&NjӦ$ĉС:tΝ;+<<\%%%6}>SJLL,K>}ܹS[֚5kԣGkV ե\hMU\괲^V;vXWΖeŊZnRSSciÆ e3LU*l^f#Gt]wYz21bDKWWWyyyI?BUܕ*9JUձcG޽gnfPM4S ̙3u16E:///yyfz뭷ԯ_?pM6ANZKL&S{ -X@۷ٳcǎi$؃@V=xlpر .q9sFEM4!iӦ111z'tR1kfbbbԶm[j߾}I*/Q^=ܹsٺuիx@NҪUd0j%4\RʿŘ*RPP //R[n-v{b-ZH%%%ӧk9r>]tã i 5jԨC`yyyYf9s7n|MߔoWTm۶MCծ]4iҤ˃qVLYYY"WAAKk߾ϟH͟?_'OժU+6mmۦ.](**Jݺu(_Rƪ}Mq:|pڶm|Y,럓'O^S\0`233jqh4SLL]יcڼy/^;ٳ,M 4(q^xA'Og}gyz~׮]7o|Mjٲe^uZe/]j5׺ut?#?~$i˖-znʔ)5WMTKԭ[ FQv<<>>$+<<\~~~ZfM5deff*++K4zh:)>>^<\-8ɤW^yE_}&MT}iӦen=п*))IX N:)##5sLY,XZ?{yy),,e2tWcǎ5SuS@@4iCU(p:#GԠA4qjU8i޽L-[L}C= 6(((H&IEqqqZp.]dE@y5gc[cHNN֨QT.\hwL(p:Z߮[*((1bSjj׮$)==].aÆrssSttklŕ(???IR^^|}}kuZbӕAp; )EDDZdddh*..VDDv%Iӧ;wuZfzQ+c./ Oھ}KիV≎ɓeX;(**5m4`;KMT?twb?^^^^***Rqq<<< &hlӧլYbݻ/U8 l֠A`8 ~STXXgyFn6h d҂ 4yd;Zqb*))ĉ5w\T^FF٣~ sNFuڕdP آ^pPxAQE^pPxAQE^pPxAQE^pPxAQE^pPxAQE^pPxAQE^pPxAQE^pPxAQE^pPxAQERIII377WWQQ:/j͛xbuQgϞӱXB֭Sjj{1mذ @eX,Ҁ A:z5jT':7+6f͞=[k׮Ջ/(޻eݳgf̘XF\R{ ?77WK,^OhhV^t5jHShhhSG`X,fEDDhҥJJJd?O >TgU~4j(I=ܣ~A͚5իu=k׮6dgg˓G,<<\ӦM+ԩS'Nyj߾ $777;vL&MR6mtl͜9SW?ֱիnx[ պuk͟?_O<i̘15mTj߾?>}(99Y,nذA~~~$޽[-[TˌɓJHH߮@iŊzW-*zL5dȐkOLL|I-]ܗeff*>>^:thÇջwoT=44T2j6mWj[4Ԃ=yFYf Z~ͪ;|233Jkʕ?~||飏>RBB>#GՊ+ʍ~޽{sssnZBZdBBBKjԨQzG4`=zT]QQQjܸΞ=KC\ًŋfbܹOoVoV}Gz?jǎ嶏V֭e2Խ{w;V!!!j׮f͚UnEsuԩS,N>MZϷo^WddN<'Nze4x񢲲4vX:tH=P:m4k9s^{5uЁ5o-_' IDAT _lzK-wwwyxxXϝ>>ڸq#>j :~իuqZgϞߢ"np%iƦf͚իgy暯 RϞ=*ŢB=p+Sk@@za-" C7n\eܶm[9sƺMW_}DAҥy7|{oՌ3}چ:믿bQJJƌS>[$IJڌ~s= B޽{o>KÇյkWСCjڴ5j$IԿ _vl٢^z[nѷ~ŋoyuX lz'+;wz'*b(22R!!!ruu=VZ)33cH UTTTa'|}`/hqw߾}/ty[Ƒb$uxwy5_{eޚt-瞓d$>>zGe0eb/Pj۶m:tvڥI&;EC?<F=s!jdXtȑ澿wy}}b! k̔$]tIog}\]tI#GT nh?ݻ/իWk񤥥yzϫj߾ƌzգG}:tP婬GW9+))ܹs5c 7N'NԛoGW^rwwxvܩ3ghĉr?쐱W%;vUV6E+VhݺuJMMc= 6T{eF͓`믿9su}xk`PHH ~sEEE oݿSL&iFԤI^zWdd>\cy*k<;vk}?+--M+V?B=Kbb90mڴV9e*qϛ7O>CO?iȑ ֻᆱ?O*((=OruuURR]@Fxxx/_ns/Tϟ?Tشxq٬E{/*%%EŒ.u^^.\ .pႵ?{@}G9R^c-))Ѹq?mKX,:}trss쯠@-om!Chԩ222/Zj"gϞJNN֪U4zZ{iwFF,Iu:oVݺu9\yxxHf͚U8Ί毲V`O|:uRlllk;~υԜ~X-[Ԛ5k4vX%$$衇$iÆ $=ӊkS*99Y...Zv7жmW_}z={?͛%].<Zv ԱcGUeYlF֬YaÆY㊏y饗wY??Sڵk߯ɓ'K-ZQOΝ;th6%K￯pUzrXy,>d!GF >\˖-رch5jZpfg-<ׯWljӦM2ܹ +wĈQ֭m׫WԸ~]v:uU}W41cTx_Iݻwc>y ڶmks/Pll<`6罼4iҤkz.e2tu{Est -X@۷ٳcǎiUvvfΜi+6;///\׿*))I*[}Z]_k-<-ٳ[o_~***pU߷*9_y/l|@[Æ S꧟~u[nEwu|I|][)/WIII?KJJEƍW9Z e)+߷:hlX,x>=233o߾R]Iӿ !C:x=5jԨrڻw$iҤI1bDm233:h4ݻwy{{kΜ96BCCQaܴad2b(..N .ԥK}f}޽:u#45mT 4%ӳ}oԥK=:s,bccgUJgյܞ3͊Q۶m}/j{ְK=^z%[0@qqq6­[o߾_l^4 X}_~tyE?lmk6/駟by͕z6&www;)>SUsu_JؿcoӦ}QmذTNcy]ս{wѣ?MLLb__R/xڕUW\\\ED|Ν;wޚ?"##nݺj}ӭaÆrssStt5Ǿm6mڴI#G? Ţ={*!!q}[^zzt)ZJq;eZrXUWRR"77Rוuӧk9r>]t/j)7HZFeddhرc͛7V\[nE?>G-R ?_7-[&ɤ;S>>>jٲFkhѢE1bΟ?KСC=SflZpR+5kUVm۶ڵk98w}_dM6իWԩSSOZZΝ;K.e-00P?Cm֬”={;7A}ܹsu)k޽ꫯԡCCwygsڤI xyy{_OOO^Z͛7WBB+,6nX7u9}Zbw﮻[zgU^=;ZtZn={mٲE?xݵyfy{{k߾}:qfʽOefiƌzt1{?iz͛m8tzUVi֬YО={;T:N{篬^K:_[0:^Iң>AСC5k>%KUUv%:uJ-Z(ul6ĉjѢMl6ɓj޼bzylVffZhQԮEIIӧOh4{Uu<96Ç+==]۷/55l6j֬Yrwwu׮]Z~5rHeee)==]999ҥKN0Ag϶+b?^^^^***RqqqV$-\P+VO? hڽ{w+**câ.8(BZX iږN&v¨YV[ɚI*dY!YdifJ(brF$`2q]^ߝ=Z8~[e{5r*FMk~wVLLz))99@ }/^,///Sԩ]Ѩ_~E7|sj4c}կ_?ŋg&6ӻwo4~g֡CԷo_9sF7o֒%K'z4v͝KW^۷oJמ۷f͚eVQQ{O/^,OOOIҚ5k4|EEEoop|dzK6o?x>ѣG7IϩS$k(""B|/''*}Wu>m49;;>T4LсԮ][3g\m 7oܖ`G(--՛oΰoqwwף>+VTھ~z3X骨t+m7СC*//tVTTsܹs*++:@_\]]uY9sRdd2̙3*))tqgu3f<&Innn*--8k;CZ|y6崦-9v233+[|XRdgyF.PPP6mT*?і-[`*k:88(((QiiiѣG޽{c\^z%! M(88X_} $],  ,g}|M0h͝;W%%%Zp~GmܸQw}{O~V\%KLk?-ܢw}W׿gu/Й3gR 4H$i7|#Ioŋ_T5rH;wܞ㉋Ӑ!CGYZA~~~7oT;vTG}TgϞUNNnfM2E.\[o,۷OO<@mM>jkiʔ)z뭷jC5sL͟?_FҾիWk„ zX d2)//BFQ -rmِwr*..֤IcK֚mݦZњ>}5tPIR\\6lؠ8I߯M6MSjj__~ /~AY[IR5x`}zuA꫺h|r9::*::r2I8q;Owց4{lIRTT~Gs=:tPO:o]o^ǎxm-Z]vU!C*㏫@PԡCM2EӧOdҥKkСڱcO*00|XjRǏWΝ3< /0}*wۧ,sGZlsIbbb"NNN۷eݪ~@D 9::jZ|O]v$iӦMԩxy9FmڴIrvxi/Wi6o,GGG 0@58RV^*mwuu2?~b82K۶4gybuuIs_5]cNO ,ٳgu5H8S8==]JJJҩStWZKm[:?/z;> IDATjM,m)游88p{ҵxW\Yi[wu6lP%k֬ш#jl{ذa>|x~_|O&Ol^椡222rJm߾/k -5.2919::*;;[ӧO7o޽ݵrJmV;v#oooEEECկ_?]q䤞={[W_}_BQQQ Vqq>S>|XӟTmlvҒ%KBꦛntL׮]j*GwVVV4p@Zl222Իwo/ZzN:%___Y= s[ 7\؆ ;vXk׮O?w޽{C)..N_zꥹsgWtmV?X)nݺI*}'9s:u$I6J***QVU4,[ݵVYY\\\l͕u…Jɇt7ͭcL&=L]wuo}kZv|||,?^~eedd(((HSژ[9rDsաC /VfΜ۷ZHҥKUXXhfϞ7|EemLEEEPt1  楥GUfL& KJJR~~^yt=ǘJKK[׸cǎ,@+CM3JHHаa*m/))Qddf̘TI;v$i޽Rhh6o,q EEEUiܸqZb .xشUپqFM4IǏӧ%I[lRRR0*::YڸqƍJN*(Ih]I[.]cǎU3FJII$)66VcǎU]k BllBBB̟===jU\rrrRZZyf "IVUߘ@D͊SQQ%I999Z|FCjÆ L&d]pAε?pJBBBQL&n6szꩧs}c^6+88yi۷$)33StgrqqѦMxY+I***Od2UyMNaaaa-ŋ?DݺuW_-777}Ҿ}tIFͭ^}mݺUFw\Zzwx6kmL6mҊ+"={\4.Xyy驲2ݽ͜9S ,hPLFQyyyڵk1b/ԁ`_{ 4 `I& `(r&rdddhΝ]w%j ^v@}]'==]?&Lݻwk֬Y68KǗ pP`Ξ=_~Y7tSsXu'TTT$GGG=cڲeeO]ⶕ15~)"""/q0L&TTT(((H .T߾}r IIKKS=Vid`{*++KڼysKKKe͏0Ο?ߤq154!Ct%%%%4+ ʜ9s /ѱFQ 6lX%%%Ԍ3*Iرc%I))) TXX|}}m>`0(**ҹƍӊ+.kܵeiL U[>C]ӧ͉/x=MOLLl߸q&MǏӒ-[[ԿeggkJKKӪU*;n8(77W4uTwmqYSCՖڼp k hVΤOOٹ~K*==ӧt颎;V?f*%%EXfIiii2dH ^iillBBB̟===Zz=j(Ⲥ15\{^yرcSz+_8@ nDGG?Oϟ6mZ,_\EEEJNN$h=z 6( @NNN2LӒ%KtgڵKvT=S… +m QDDŸk˒R|Zsw}zghѢj46 BvvJJJԯ_&'88yi۷$)33CZZ<<>>rppqgee5Y\iH>UwAzmۦGy/9͛uܹF}%Zx~G[noV^^^ڷoN<)ѨYlcǎZzwxZUHݺuFdqw֭^q5F ɧ_Uǎȑ#}?v^{c uqťb)//Wqq<==UVVr[uhT^^vju3gԂ 4X17$֪֬Y?X۶mhrx$('''4h^z%7}.\Ν;k5k|V+hYvUPP GGf^߿_ !v‘Es̩999իW>DM2Ez74~x V+[,jqWzꥢ">}^lnv;AM:|?}t?ʕ+URR"IuYeddhԙUXXX:p;~KKKxb=spyPꕕ`0}Waayxyyyw$u www޲2;PjjVZS6ߏ?XAAAzSN5kÙh~;;_|FEFFjٲe6L.:X,hTNo>Ի?%K(>>^z[oiѢE?%kZUllNgOOOV{~NN*b_C_Bl޼y qϞ='hȑUSٳGu?99YNȑ#ࠌ uE:t@ Z=WWWiFgϞm۶U֩Sj*9888322rJm߾]-ow-1`0Y^zIdHyyTQQwՀtwWddbbbmWppp&MҎ;tȑ:?h͚2ev!ɤ;C]V>N}TRRҢW^^~Xk׮Oǟ8qBEEErttc= FQNNNMW}c6oVGܹsu!uԩE\LX\ӕ'777oҐ!CԩS'UGRuqqѶmj,?1bDђ%K4h uUk׮M7DqZ(h`,:r8`3㋉Q~~~z%I[n?nysiq 2DO?t-AFF T!uWV*JRaaRRRn:[-ŋ[ y{{[k׮u_?d2iZpaZfIjAi֭614ѣM&Ν;gqIIIٳWii\\\k5&kj3J8aÆqjӦL&JKK|7ԦMj*kNsĉy@ ^v[n޽{[|FQ U %%%Ԍ3*IرcݻWYYY ͛%.Jq7N+VhZ~A^Ij۶]2V?h}ZeSRR;jΜ9[t7K\ƍɓ+I:u;1Deeeڵk>sիyh .899o+͚5EƘ.]T1cTZZKbcc3xl(<<\&ɼ-66V!!!Ϟ :@999)--lK"%''Krrr|r=ZCՆ  '''L&iɒ%pႜk{yyi…(""AqڵKv9T=SVqDK,h( F=ԭ[7m۶M-.J͛ɓ'o߾.گ_?I_x!mڴI-{WTTT$f-LJ/LGh6d ]@K竼\ڽ{$iȑJMMU||ԫW/EGGk5%&&jΝZ~fgӦM={L&Ν5k\{_W6m%W9 klreyy驲2ݽ͜9S ,hp\FQyyyڵkj.oh-r@E1zGۮsa0F\ PZZx@J`f 쎓>C͞=[%%%vZDnEE^y-Z.`/Y=@$%%Qr \xFD( `( 6/v$hDh([yyy3g|}}IЊ꥗^ҙ3gHV/T^^z7Ԯ]&d2)99Yv JLLTEEEyFFV\۷d2=c^ffN:U~͙3GO>dN@c .}6lpuԩƕW_}Uwu믷|~Є {n͚5˦6[zp3toVG+VU ^|&g˖-ܹz٠v9O?-I*,,T}\JIIQqq8qBEEErttc=-[45O̗{<:p.9cQ3f-7sLEDDٳ<x؝^{M/ܚɤwyG'NlP;zᇕcm۶KTHHHe4giVw_$mݺU?xc1MsSǚ?3(==]#FІ YM}[GկZ10az-Z ʩS_j„ MObbz!WW:+ @>ld2i֭8qӧkӦMuj#))IzWϜ9s%Omq>|X2eoh2 ͒MuY+88X+Wx̳>>fsF]YlF:\TVZZZ˵; 6AGUf1K[NaÆ5lUTT?מ={̟۶mkU\w^eee)44T7oW^/u&Rl5qjʕez={VVМ='MyY{ >\_}v:uÇkݺu< x ɤ?X&MAQQQ1cRSS%IƍM ׯ_c5JHHR$.))Qddd85vXI 7 ggg***Q_t_|Q={"""4sLJIIQ``z9c[Mi *,,$)>>^^^^ݻw17?K9i{CAΝO<˗P*P`78<1nܸQƍSNNrss%ISNqMӜyyy;֘UǤItq>}Zŗy{{K|}}5uTo^fҴi5ӦMӤIb Z<˴l߿_iiiZjUڜ1[4gggI ZljXjIS{uOe|P Y`˜I{7hY]IiiiY2dHǟ>}Z;vmҥJOOs=JOOW.]/IcƌQiiRRR/I4A={8kcTIIM{Of̘[Uq5$ J/Thhh\k^ 襗^C=dohߖrbͽW.]gϞؿ ۷w<4 w}Wܥ\\\k.vmqjjz*//qqq***Rrr/_ѣGkСڰa$ɤ8-YD.\x]ٕ6mҰatWH>M8Q {y… +m n IDATQDDD\k^-Ik֬7߬QF)??_&I;w:9Wkq*W_;wԓO>Mcvd2iϞ=.J_+I***=+Й3gOpp^|E?IJKKRvFԵkWǭ\RǏ׫$)//Wqq<==UVVr7K.333)???u֭Q69s,X1'Ɗc9i~:km+??_;wVRR C͢ .,]Taaa:y uq4|s=o#`Zm9ok֗ImqssӪU裏`XM2u:ĉѣc>3(==]#FІ D.VB医5w+**w)((ȪԣG=zٟ]^6iΜ9u:ĉqÇ)S(00PoƏ/PΝիWJMoժU U6m>S'Nh8-[o]\4/t:k׮ZrJJJ$I_Ξ=۠uAߴn:.Є233ꪻN繺ZGr8p@ǎkPZx{9EDDX}^cu .Y,xyyyw nb}O]{_RڵksY|n;PjjVZS;Ώ?XAAAzSNjNΤ=(++SN,?/>F"##lٲFYM1WWWސ^Zԩۧze0dU/z--ZYЊ"Ch ^i-XyϞ QFꫯk/<<\7pz!󶰰0\ F̛7Oaaayd4_VwIu]]wy5kVfGDD`0hܹ..33`}ꪫ^6)$$NkX>Crwwn5kSFR~~L&:wLVGυ-[{է~*srrk.QGwm%Ξ=A)))I~٣:/]-[ԧOh߾}馛a ^vUٶm\]]5x`:uJVby,##C+WRcBť}zJ˴+**Jzw5`}zjz\+I&MҎ;tȑ:_QQ!I?^~eM2Eԅ b/T'O֩S7Túe0ۮJo$9rD]tQk=6==];vPpp.\ۗ5)77WW\q;lS>|ﯨjZv}Q|XC2㿴TԞ={sjg<`>&''G6m%klR] _^^^Ȩq>}TRRBbPyy~a]V>>> Q=*F9995I\Mӷ~#Ghܹ:tP/DÔ[n55}tMݻw+**JC QN'WWW=zJE۶mO\TVVm۲f8`r ={TYYrssIF33JHH2D1cRSS%I;v{*++Kڼyse0U)qiŊMϱc$I_}]Irv^vOڴiLbbbTeƍ5i$?~\O$mٲs*,,LnƍN*19rDkI 삫?k'!$==]]tv1cƨT)))kkۿegg[l@2Lm 1ThhqIiiiٽ2dn& +))Id6/1|p%&&'$..NEEEJNN$h=z 6( @NNN2LӒ%Kt…J?A.\Xi[HH"""׮]tmcHMMSO=e՘W||>^IIIի5|W@@sN_^wygiji&͞=[&Is՚5ky@rrL&nV9"<ѣ^{5BX*++Syy̙3`e4]6Ɗ #$$DZt)`(+?{4 777bB.\kF[n5/%(!!dR-Jqضm:Z 슃-Zs ΘL&z7IV/s]wCڱcȆ ԧO :dh5X]Tppl"rA{֮](//i&m߾]Z^_tm K4[GV׮]O? ۶m @q@ ^QEl^QxFQ`8D6(11QHFӜ9sK2뫗^zIgΜ!^v\_o]vMڗdRrrr9q~%&&2##C+We2lhm+33SNq̙'|E}'9Q`>C 6L~~~MWxx:uԠ6ꫯꮻRrr^t0avޭYf}cWϞ=nqު>}hŊ<% NNN_ll٢Ν;gϞU9rD%%%Vᡧ~ZTXX7zJIIQqq8qBEEErttc=-[4뵬O-y<{9B8p@eeeGf̘[Lׯo]/$i֭zǛ:7:V{g}kĈڰa{{{ѣ_k\֭AP`7L&>cM4疔(22R3fPjj$);;[cǎ񜄄ׯ6Ѩ*ڿTEEE5K/_gVDDfΜiU\))) TXX|}}-I2 t޸qEX Rl511Kn_rww$K{>`йs,Oh< Ɓ#F܍7jҤI:~N>- Լk<'''G^^^(((jԩj߾f͚iӦ5jnMI&iŊZjyemq_ڿҴj*yƍSNNrss%ISNzMঌRl51KXMnYsdd-[&77:'^^^ʲσ>`Τo4x`s.ƌRwaQ_" hG򘁚-!Q7[,+;hinv@F\{I)Y҅ڕiז:hfFVw<䂈lq8<U3g=vzg>wP$i۶mξxZha&R9ɤN:Ux\ĔymJ0K^wU=szwoW^}ݧF[Çgzxx(88إK*--}jԨQuV#U&5;˚k]Uk륗^رc>|r+ @[nU׮]x8o֡f6nܨPfiǎZx 5j$bc+%%E+VИ1c4h [Z`A"""ڤ{GZ$}zꩧk.5Fuf>>״P޽[;wg}Cnj0yyyW111:5YV]rE>>>UwJOOׅ L_//RӟTXX(\X,]rE^^^***bGcWZ,_:f͚WklFF222Իwom۶NZp|}}֦α};rմGj̙/WNNZn={h|p K.UTTΞ=[鸼|6nX===ݻwߴ>XII[7ZM&I7nn !͙3ǡOwӵ{nM4Iaaa'xBsܹZzP3 Tƍ///>}A\l^r4(^t5_tIM6pj*]zUԣG]|ܛ9yFkŊU/&&F}رcEEE7d7믿Jnj?^VU~imgϞUvԽ{w=C2ҫZ*((ܹs%]_>& @}nz?>0ֺ;v<<<qk֬Fl6nݚӳܿIII=z/_2SRR#Fd2رcQˌ|={˵o>;||ժ$uMJMMU~8* +eVvvXZ;vLV֭[{XVXX(\.]HRW,,Y[j͚4io.ͦ!C(99})<}zm5Rdd,X]\\o׀f 8 Rd !8;VnݺU;D,|A[NU?}kܸqz뭷|jkU5}:~ΝGVy@TիWնm Ϙ1C?.\ wwwyxxط-^X֭[~Eea6e5k֬cL0AÇw{Ubb)??_+V(w) ,VNti@S{^~eI7|{Z3gNU5kԩ*((cYYYj߾}6mڔ W%lZh,XPff2ԡCJwܩE)33S}c={ R`H^::uSNٿpÇWl6/={(''GgϮֱ]VuXUSyBQ{Ο?_e[M6)!!AM6՜9sSO5XAAA $I&JBNTTTӌzdZk׮2^x+:t$ɓzGc߯'N(22R7on dɒRu=裏&ԏSNIzQgdd(!!A3gTbbFdJ_p={TƍQϦQ;7n\K=z'uEIRRR'PXXZh9s讻 u=8q=2eN:U5~?~\ܹs^zjPx8777_{UPP iii)w?*,,ԁ߶m}o߾}uJUFIIDATl6s۶mSDD"##]WXX\]]ua^O>VϞ={R,/ݻwkƌ4رCJII$9sF+VИ1c4h mܸQruufӎ;xbizE`REDD(66VuLCmz6mڤٳgfiܹZf O-f4L6 p"SN^{5qX,]rE^^^***bG7k,͟?uYV]pNnWW5qr劖.]J38 ^Nɓu>GAAݩ *..Vǎ7ؗg JPPfvE3n pزewN p*&I .ܹsipl7o͛G38^Ngذaj޼oN3[ƍխ[7 4fp: )edd(<<\III240=Z֭pZ6m֭[o f̘GyDK4pZcƌ~GЖ-[@ 1 `P`P`P`P`P`P`P`P`P`P`P`P`P`P`P`P`P`P`P`P`P`P`P`P`P`P`P`P`P`P`P`P`P`P`P)tAьjt<;^4+W*11Qiii>|6nHS*`X͛7PӧO)kD Аӵ{nd2Saaa:{ioDGGcǎzGua)3l6m@CٵkFgCOVuq4W.^:ܹs!(hPAAASF/dy{{k׮47RSS*((O?ɓ'Ϗ@/nE]ժx-[ʑo^ Ԙ1c~{ܸn륗^҃>H3%W﯌ qSYF ȑ##֭ͦ[Ә_ر7o.}|IMeVvvd2ho]/_$?Os I2l6m0SNS;==]wy ϵiFΝ㤖#))I}NwyG֣>Jc ޜ8qBk׮r\vOyȑ#Zvf6mKݻw׾}4zh^Z={]wEoq:|2(5xIV4|~ .wŋU\\\깒EK5kL%KZ駟pSkpuuu5mڴQ6mW3xQll>COzqqq W=sjԨ{Rl6jڴ)3xk׬Y!ooRٳ5o7NݠǯO yo=6mϟkj„ ٻ:q լY~lٲJxxxh:NDDV^ lRѾ xb%''M/|M-\A_b ˗ruu{ァ' x?QaXnmۦ#G(,,L/$UonnnLϗ7sDTTxr늊ٳgծ];u]=fꫯk׮5AAΝ+2 tm_WULL~Wl۶m:t"##kԧf 5C^.](..NF'|iӦ.ӳԶ8[N;v?W^qFK_j߾ڵkWn]ΝSrrڶm`iʕ>>Ƽ{Zn~a={V0ax8qBv횼t 4H[Pڷo_ڹsg3S+;~ZZl6z3g(//O{VnnZhQ{wtomzճgjY_϶s=޽{aPZi֬6lؠ;S1۷땙 G2LUرcݪ=zԾVtYm۶^z1{lZJݻw$}eB3fׅ .siɒ% V˖-ջwo)33Tk6e˖ oN7a„ gFWv|-^X֭[~UuCuWݫDoS~~VXQpo rdRΝչsgmZ;b_v[Vvvvݺuӧ~*YiӦyyy:pԵkW-\P'NPLLL^vСZ|}}+V%裏h"-\PǏ{qsUu묏^ܹSK.jU߾}աC}|![ĥKSTT$,I Ԯ]$Iڵk}UՖ5!CqƲl*,,,lCشiԴiS͙3GO=T^hUM AAA _{M4ltTA[nɱ/kX,j޼ P-ZHȑ#a諯Ҽy/ @]*,,_|!ͦGjڴij޼ChҤ3O:ܕ %$$ѣJLLT@@W Ug.r-nvHIIQjjj34ϟ?LJJJ.lRb}'z饗oFCU&MW_iҥڰa'^A>HGpܯZFg6M:u7n 5:uJ~~~:sL{W-Zp8q*>zJ@_z wSSSգGMἽ%]_ a [LNj֧&Mhruu$9rD:u?'^AhP={vء-Z7'|b1AP}ѣ:'wwwBP`Pd                                                                    E5"5v@9IENDB`sympy-0.7.4.1/doc/src/modules/physics/0000755000175000017500000000000012253362407017732 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/modules/physics/hydrogen.rst0000644000175000017500000000017212253362407022303 0ustar georgeskgeorgesk====================== Hydrogen Wavefunctions ====================== .. automodule:: sympy.physics.hydrogen :members: sympy-0.7.4.1/doc/src/modules/physics/secondquant.rst0000644000175000017500000000016412253362407023011 0ustar georgeskgeorgesk=================== Second Quantization =================== .. automodule:: sympy.physics.secondquant :members: sympy-0.7.4.1/doc/src/modules/physics/hep/0000755000175000017500000000000012253362407020506 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/modules/physics/hep/gamma_matrices.rst0000644000175000017500000000036612253362407024216 0ustar georgeskgeorgeskHigh energy physics =================== .. module:: sympy.physics.hep.gamma_matrices .. autoclass:: sympy.physics.hep.gamma_matrices._LorentzContainer :members: .. autoclass:: sympy.physics.hep.gamma_matrices.GammaMatrixHead :members: sympy-0.7.4.1/doc/src/modules/physics/hep/index.rst0000644000175000017500000000034312253362407022347 0ustar georgeskgeorgesk=================== High energy physics =================== .. topic:: Abstract Contains docstrings for methods in high energy physics. Gamma matrices ============== .. toctree:: :maxdepth: 3 gamma_matrices.rst sympy-0.7.4.1/doc/src/modules/physics/quantum/0000755000175000017500000000000012253362407021424 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/modules/physics/quantum/shor.rst0000644000175000017500000000015412253362407023131 0ustar georgeskgeorgesk================ Shor's Algorithm ================ .. automodule:: sympy.physics.quantum.shor :members: sympy-0.7.4.1/doc/src/modules/physics/quantum/cg.rst0000644000175000017500000000021312253362407022543 0ustar georgeskgeorgesk=========================== Clebsch-Gordan Coefficients =========================== .. automodule:: sympy.physics.quantum.cg :members: sympy-0.7.4.1/doc/src/modules/physics/quantum/hilbert.rst0000644000175000017500000000014612253362407023610 0ustar georgeskgeorgesk============= Hilbert Space ============= .. automodule:: sympy.physics.quantum.hilbert :members: sympy-0.7.4.1/doc/src/modules/physics/quantum/qapply.rst0000644000175000017500000000012012253362407023455 0ustar georgeskgeorgesk====== Qapply ====== .. automodule:: sympy.physics.quantum.qapply :members: sympy-0.7.4.1/doc/src/modules/physics/quantum/represent.rst0000644000175000017500000000013412253362407024163 0ustar georgeskgeorgesk========= Represent ========= .. automodule:: sympy.physics.quantum.represent :members: sympy-0.7.4.1/doc/src/modules/physics/quantum/dagger.rst0000644000175000017500000000012012253362407023400 0ustar georgeskgeorgesk====== Dagger ====== .. automodule:: sympy.physics.quantum.dagger :members: sympy-0.7.4.1/doc/src/modules/physics/quantum/cartesian.rst0000644000175000017500000000023312253362407024125 0ustar georgeskgeorgesk============================== Cartesian Operators and States ============================== .. automodule:: sympy.physics.quantum.cartesian :members: sympy-0.7.4.1/doc/src/modules/physics/quantum/state.rst0000644000175000017500000000011412253362407023272 0ustar georgeskgeorgesk===== State ===== .. automodule:: sympy.physics.quantum.state :members: sympy-0.7.4.1/doc/src/modules/physics/quantum/grover.rst0000644000175000017500000000016412253362407023463 0ustar georgeskgeorgesk================== Grover's Algorithm ================== .. automodule:: sympy.physics.quantum.grover :members: sympy-0.7.4.1/doc/src/modules/physics/quantum/innerproduct.rst0000644000175000017500000000015312253362407024671 0ustar georgeskgeorgesk============= Inner Product ============= .. automodule:: sympy.physics.quantum.innerproduct :members: sympy-0.7.4.1/doc/src/modules/physics/quantum/piab.rst0000644000175000017500000000015712253362407023074 0ustar georgeskgeorgesk================= Particle in a Box ================= .. automodule:: sympy.physics.quantum.piab :members: sympy-0.7.4.1/doc/src/modules/physics/quantum/operator.rst0000644000175000017500000000013012253362407024003 0ustar georgeskgeorgesk======== Operator ======== .. automodule:: sympy.physics.quantum.operator :members: sympy-0.7.4.1/doc/src/modules/physics/quantum/index.rst0000644000175000017500000000140612253362407023266 0ustar georgeskgeorgesk================= 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 sympy-0.7.4.1/doc/src/modules/physics/quantum/circuitplot.rst0000644000175000017500000000014712253362407024521 0ustar georgeskgeorgesk============ Circuit Plot ============ .. automodule:: sympy.physics.quantum.circuitplot :members: sympy-0.7.4.1/doc/src/modules/physics/quantum/qft.rst0000644000175000017500000000010412253362407022743 0ustar georgeskgeorgesk=== QFT === .. automodule:: sympy.physics.quantum.qft :members: sympy-0.7.4.1/doc/src/modules/physics/quantum/tensorproduct.rst0000644000175000017500000000015712253362407025074 0ustar georgeskgeorgesk============== Tensor Product ============== .. automodule:: sympy.physics.quantum.tensorproduct :members: sympy-0.7.4.1/doc/src/modules/physics/quantum/commutator.rst0000644000175000017500000000014012253362407024343 0ustar georgeskgeorgesk========== Commutator ========== .. automodule:: sympy.physics.quantum.commutator :members: sympy-0.7.4.1/doc/src/modules/physics/quantum/constants.rst0000644000175000017500000000013412253362407024170 0ustar georgeskgeorgesk========= Constants ========= .. automodule:: sympy.physics.quantum.constants :members: sympy-0.7.4.1/doc/src/modules/physics/quantum/gate.rst0000644000175000017500000000011312253362407023071 0ustar georgeskgeorgesk===== Gates ===== .. automodule:: sympy.physics.quantum.gate :members: sympy-0.7.4.1/doc/src/modules/physics/quantum/operatorset.rst0000644000175000017500000000024012253362407024521 0ustar georgeskgeorgesk=============================== Operator/State Helper Functions =============================== .. automodule:: sympy.physics.quantum.operatorset :members: sympy-0.7.4.1/doc/src/modules/physics/quantum/spin.rst0000644000175000017500000000011012253362407023117 0ustar georgeskgeorgesk==== Spin ==== .. automodule:: sympy.physics.quantum.spin :members: sympy-0.7.4.1/doc/src/modules/physics/quantum/qubit.rst0000644000175000017500000000011412253362407023276 0ustar georgeskgeorgesk===== Qubit ===== .. automodule:: sympy.physics.quantum.qubit :members: sympy-0.7.4.1/doc/src/modules/physics/quantum/anticommutator.rst0000644000175000017500000000016012253362407025221 0ustar georgeskgeorgesk============== Anticommutator ============== .. automodule:: sympy.physics.quantum.anticommutator :members: sympy-0.7.4.1/doc/src/modules/physics/units.rst0000644000175000017500000000272612253362407021635 0ustar georgeskgeorgesk.. _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: sympy-0.7.4.1/doc/src/modules/physics/mechanics/0000755000175000017500000000000012253362407021664 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/modules/physics/mechanics/vec_simp_der.svg0000644000175000017500000003427512253362407025057 0ustar georgeskgeorgesk image/svg+xml A B l x ax ay bx by c θ sympy-0.7.4.1/doc/src/modules/physics/mechanics/kin_rolling.svg0000644000175000017500000002535712253362407024730 0ustar georgeskgeorgesk 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 sympy-0.7.4.1/doc/src/modules/physics/mechanics/masses.rst0000644000175000017500000003325712253362407023723 0ustar georgeskgeorgesk======================================================= 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. sympy-0.7.4.1/doc/src/modules/physics/mechanics/kin_angvel1.svg0000644000175000017500000003102112253362407024600 0ustar georgeskgeorgesk image/svg+xml B N ny nx nz nx NwB=q nx sympy-0.7.4.1/doc/src/modules/physics/mechanics/vec_mul.svg0000644000175000017500000001135312253362407024042 0ustar georgeskgeorgesk image/svg+xml a 2a -a sympy-0.7.4.1/doc/src/modules/physics/mechanics/kin_angvel2.svg0000644000175000017500000005547312253362407024622 0ustar georgeskgeorgesk image/svg+xml nx ny nz q1 q2 q3 ax bz cy A B C D N sympy-0.7.4.1/doc/src/modules/physics/mechanics/kane.rst0000644000175000017500000002663212253362407023345 0ustar georgeskgeorgesk================================== 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 Matrix([[5]]) >>> KM.forcing Matrix([[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 Matrix([ [1, 0], [0, 5]]) >>> KM.forcing_full Matrix([ [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] Matrix([ [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() Matrix([ [2*q1''], [2*q2'']]) It is possible to obtain the mass matrix and the forcing vector. :: >>> LM.mass_matrix Matrix([ [2, 0], [0, 2]]) >>> LM.forcing Matrix([ [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() Matrix([ [ 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 Matrix([ [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 Matrix([ [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() Matrix([ [2*q1'' - 7], [ 2*q2'']]) Exploration of the provided examples is encouraged in order to gain more understanding of the ``LagrangesMethod`` object. sympy-0.7.4.1/doc/src/modules/physics/mechanics/rollingdisc.svg0000644000175000017500000002242212253362407024720 0ustar georgeskgeorgesk image/svg+xml R N nz nx ny rx ry rz sympy-0.7.4.1/doc/src/modules/physics/mechanics/simp_rot.svg0000644000175000017500000002436612253362407024254 0ustar georgeskgeorgesk image/svg+xml A B θ θ azbz ax bx ay by sympy-0.7.4.1/doc/src/modules/physics/mechanics/examples.rst0000644000175000017500000000066612253362407024244 0ustar georgeskgeorgesk============================== Examples for Physics/Mechanics ============================== Here are some examples that illustrate how one typically uses this module. We have ordered the examples roughly according to increasing difficulty. If you have used this module to do something others might find useful or interesting, consider adding it here! .. toctree:: :maxdepth: 1 rollingdisc_example.rst bicycle_example.rst sympy-0.7.4.1/doc/src/modules/physics/mechanics/kin_3.svg0000644000175000017500000002775112253362407023424 0ustar georgeskgeorgesk image/svg+xml nx ny bx by cx cy P Q O N B C sympy-0.7.4.1/doc/src/modules/physics/mechanics/kin_4.svg0000644000175000017500000004312412253362407023415 0ustar georgeskgeorgesk image/svg+xml O P Q nx nz ny q1 q2 cx cy cz C N sympy-0.7.4.1/doc/src/modules/physics/mechanics/bicycle_example.rst0000644000175000017500000004340012253362407025544 0ustar georgeskgeorgesk========= A 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 set Vector.simp to False (in case it has been set True elsewhere), since it slows down the computations:: >>> Vector.simp = False >>> mechanics_printing() Declaration of Coordinates & Speeds: A simple definition 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, q3, q4, q5 = dynamicsymbols('q1 q2 q3 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. sympy-0.7.4.1/doc/src/modules/physics/mechanics/vectors.rst0000644000175000017500000007176512253362407024123 0ustar georgeskgeorgesk================================== 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) Matrix([ [ 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) Matrix([ [ 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). sympy-0.7.4.1/doc/src/modules/physics/mechanics/kin_2pt.svg0000644000175000017500000003735612253362407023771 0ustar georgeskgeorgesk image/svg+xml N B Ofixed in N Pfixed in B Sfixed in B sympy-0.7.4.1/doc/src/modules/physics/mechanics/rollingdisc_example.rst0000644000175000017500000000101412253362407026436 0ustar georgeskgeorgesk============== 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:: rollingdisc.* :height: 350 :width: 350 :align: center We model the rolling disc in three different ways, to show more of the functionality of this module. .. toctree:: :maxdepth: 1 rollingdisc_example_kane.rst rollingdisc_example_kane_constraints.rst rollingdisc_example_lagrange.rst sympy-0.7.4.1/doc/src/modules/physics/mechanics/vec_cross.svg0000644000175000017500000002342712253362407024403 0ustar georgeskgeorgesk image/svg+xml a x a = 0 a a b d c c / sqrt(2) 45° sympy-0.7.4.1/doc/src/modules/physics/mechanics/vec_fix_notfix.svg0000644000175000017500000003642712253362407025433 0ustar georgeskgeorgesk image/svg+xml A B f e d c c xd xe f Fixed in: xx A B sympy-0.7.4.1/doc/src/modules/physics/mechanics/rollingdisc_example_lagrange.rst0000644000175000017500000000576112253362407030313 0ustar georgeskgeorgesk====================================== A rolling disc using Lagrange's 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) >>> le = l.form_lagranges_equations() >>> le.simplify(); le Matrix([ [m*r**2*(12*sin(q2)*q3'' + 10*sin(2*q2)*q1'*q2' + 12*cos(q2)*q2'*q3' - 5*cos(2*q2)*q1'' + 7*q1'')/8], [ m*r*(8*g*sin(q2) - 5*r*sin(2*q2)*q1'**2 - 12*r*cos(q2)*q1'*q3' + 10*r*q2'')/8], [ 3*m*r**2*(sin(q2)*q1'' + cos(q2)*q1'*q2' + q3'')/2]]) >>> lrhs = l.rhs(); lrhs.simplify(); lrhs Matrix([ [ q1'], [ q2'], [ q3'], [ -4*(tan(q2)*q1' + 3*q3'/(2*cos(q2)))*q2'], [-4*g*sin(q2)/(5*r) + sin(2*q2)*q1'**2/2 + 6*cos(q2)*q1'*q3'/5], [ (-5*cos(q2)*q1' + 6*tan(q2)*q3' + 4*q1'/cos(q2))*q2']]) sympy-0.7.4.1/doc/src/modules/physics/mechanics/index.rst0000644000175000017500000000703512253362407023532 0ustar georgeskgeorgesk=================== 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 (statics) or motion (dynamics). There are a few common steps to all mechanics problems. First, an idealized representation of a system is described. Next, we use physical laws to generate equations that define the system's behavior. Then, we solve these equations, sometimes analytically but usually numerically. Finally, we extract information from these equations and solutions. The current scope of the module is multi-body dynamics: the motion of systems of multiple particles and/or rigid bodies. For example, this module could be used to understand the motion of a double pendulum, planets, robotic manipulators, bicycles, and any other system of rigid bodies that may fascinate us. Often, the objective in multi-body dynamics is to obtain the trajectory of a system of rigid bodies through time. The challenge for this task is to first formulate the equations of motion of the system. Once they are formulated, they must be solved, that is, integrated forward in time. When digital computers came around, solving became the easy part of the problem. Now, we can tackle more complicated problems, which leaves the challenge of formulating the equations. The term "equations of motion" is used to describe the application of Newton's second law to multi-body systems. The form of the equations of motion depends on the method used to generate them. This package implements two of these methods: Kane's method and Lagrange's method. This module facilitates the formulation of equations of motion, which can then be solved (integrated) using generic ordinary differential equation (ODE) solvers. The approach to a particular class of dynamics problems, that of forward dynamics, has the following steps: 1. describing the system's geometry and configuration, 2. specifying the way the system can move, including constraints on its motion 3. describing the external forces and moments on the system, 4. combining the above information according to Newton's second law (:math:`\mathbf{F}=m\mathbf{a}`), and 5. organizing the resulting equations so that they can be integrated to obtain the system's trajectory through time. Together with the rest of :mod:`SymPy`, this module performs steps 4 and 5, provided that the user can perform 1 through 3 for the module. That is to say, the user must provide a complete representation of the free body diagrams that themselves represent the system, with which this code can provide equations of motion in a form amenable to numerical integration. Step 5 above amounts to arduous algebra for even fairly simple multi-body systems. Thus, it is desirable to use a symbolic math package, such as Sympy, to perform this step. It is for this reason that this module is a part of Sympy. Step 4 amounts to this specific module, sympy.physics.mechanics. 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 sympy-0.7.4.1/doc/src/modules/physics/mechanics/vec_dot.svg0000644000175000017500000002461312253362407024036 0ustar georgeskgeorgesk image/svg+xml a b a a a c 45° a ⋅ b = 0 a ⋅ a = 1 a ⋅ c = 1/sqrt(2) sympy-0.7.4.1/doc/src/modules/physics/mechanics/vec_add.svg0000644000175000017500000001705012253362407023775 0ustar georgeskgeorgesk image/svg+xml a b b a a+b sympy-0.7.4.1/doc/src/modules/physics/mechanics/rollingdisc_example_kane_constraints.rst0000644000175000017500000000640312253362407032072 0ustar georgeskgeorgesk======================================================== A rolling disc, with Kane's method and constraint forces ======================================================== 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 import symbols, sin, cos, tan >>> from sympy.physics.mechanics import * >>> mechanics_printing() >>> 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]) >>> w_R_N_qd = R.ang_vel_in(N) >>> R.set_ang_vel(N, u1 * L.x + u2 * L.y + u3 * L.z) 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) >>> I = inertia(L, m / 4 * r**2, m / 2 * r**2, m / 4 * r**2) >>> kd = [dot(R.ang_vel_in(N) - w_R_N_qd, uv) for uv in L] 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) Matrix([ [(4*g*sin(q2) + 6*r*u2*u3 - r*u3**2*tan(q2))/(5*r)], [ -2*u1*u3/3], [ (-2*u2 + u3*tan(q2))*u1]]) >>> from sympy import trigsimp, signsimp, collect, factor_terms >>> def simplify_auxiliary_eqs(w): ... return signsimp(trigsimp(collect(collect(factor_terms(w), f2), m*r))) >>> mprint(KM.auxiliary_eqs.applyfunc(simplify_auxiliary_eqs)) Matrix([ [ -m*r*(u1*u3 + u2') + f1], [-m*r*u1**2*sin(q2) - m*r*u2*u3/cos(q2) + m*r*cos(q2)*u1' + f2], [ -g*m + m*r*(u1**2*cos(q2) + sin(q2)*u1') + f3]]) sympy-0.7.4.1/doc/src/modules/physics/mechanics/advanced.rst0000644000175000017500000002077412253362407024175 0ustar georgeskgeorgesk============================================================================ 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. sympy-0.7.4.1/doc/src/modules/physics/mechanics/rollingdisc_example_kane.rst0000644000175000017500000000701412253362407027442 0ustar georgeskgeorgesk================================== A rolling disc, with Kane's method ================================== 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]) >>> w_R_N_qd = R.ang_vel_in(N) >>> R.set_ang_vel(N, u1 * L.x + u2 * L.y + u3 * L.z) 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 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. :: >>> kd = [dot(R.ang_vel_in(N) - w_R_N_qd, uv) for uv in L] 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) Matrix([ [(4*g*sin(q2) + 6*r*u2*u3 - r*u3**2*tan(q2))/(5*r)], [ -2*u1*u3/3], [ (-2*u2 + u3*tan(q2))*u1]]) sympy-0.7.4.1/doc/src/modules/physics/mechanics/kinematics.rst0000644000175000017500000005244512253362407024557 0ustar georgeskgeorgesk===================== 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 sympy-0.7.4.1/doc/src/modules/physics/mechanics/kin_2.svg0000644000175000017500000003262612253362407023420 0ustar georgeskgeorgesk image/svg+xml N nx ny nz B O P bz by bx rOP sympy-0.7.4.1/doc/src/modules/physics/mechanics/kin_angvel3.svg0000644000175000017500000003342712253362407024616 0ustar georgeskgeorgesk image/svg+xml p1 w1 w2 p2 p3 w3 sympy-0.7.4.1/doc/src/modules/physics/mechanics/api/0000755000175000017500000000000012253362407022435 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/modules/physics/mechanics/api/printing.rst0000644000175000017500000000063012253362407025020 0ustar georgeskgeorgesk===================== 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 sympy-0.7.4.1/doc/src/modules/physics/mechanics/api/essential.rst0000644000175000017500000000073512253362407025163 0ustar georgeskgeorgesk================================= Essential Components (Docstrings) ================================= CoordinateSym ============= .. autoclass:: sympy.physics.mechanics.essential.CoordinateSym ReferenceFrame ============== .. autoclass:: sympy.physics.mechanics.essential.ReferenceFrame :members: Vector ====== .. autoclass:: sympy.physics.mechanics.essential.Vector :members: Dyadic ====== .. autoclass:: sympy.physics.mechanics.essential.Dyadic :members: sympy-0.7.4.1/doc/src/modules/physics/mechanics/api/kane.rst0000644000175000017500000000075212253362407024111 0ustar georgeskgeorgesk==================================================================== 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: sympy-0.7.4.1/doc/src/modules/physics/mechanics/api/part_bod.rst0000644000175000017500000000200612253362407024757 0ustar georgeskgeorgesk===================================================== 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 sympy-0.7.4.1/doc/src/modules/physics/mechanics/api/kinematics.rst0000644000175000017500000000041712253362407025320 0ustar georgeskgeorgesk======================= Kinematics (Docstrings) ======================= Point ===== .. automodule:: sympy.physics.mechanics.point :members: kinematic_equations =================== .. automodule:: sympy.physics.mechanics.functions :members: kinematic_equations sympy-0.7.4.1/doc/src/modules/physics/mechanics/api/functions.rst0000644000175000017500000000132612253362407025201 0ustar georgeskgeorgesk======================================== 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 time_derivative --------------- .. autofunction:: sympy.physics.mechanics.functions.time_derivative get_motion_params ----------------- .. autofunction:: sympy.physics.mechanics.functions.get_motion_params sympy-0.7.4.1/doc/src/modules/physics/mechanics/kin_1.svg0000644000175000017500000002102112253362407023402 0ustar georgeskgeorgesk image/svg+xml P O N rOP sympy-0.7.4.1/doc/src/modules/physics/mechanics/kin_1pt.svg0000644000175000017500000006224112253362407023757 0ustar georgeskgeorgesk image/svg+xml B N Ofixed in N Pnot fixed in B Sfixed in B sympy-0.7.4.1/doc/src/modules/physics/mechanics/reference.rst0000644000175000017500000000207212253362407024355 0ustar georgeskgeorgesk================================ 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. sympy-0.7.4.1/doc/src/modules/physics/mechanics/vec_rep.svg0000644000175000017500000001227512253362407024037 0ustar georgeskgeorgesk image/svg+xml Vector on page Vector outof page Vectorinto page sympy-0.7.4.1/doc/src/modules/physics/index.rst0000644000175000017500000000054612253362407021600 0ustar georgeskgeorgesk.. _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 hep/index.rst mechanics/index.rst quantum/index.rst sympy-0.7.4.1/doc/src/modules/physics/wigner.rst0000644000175000017500000000014012253362407021752 0ustar georgeskgeorgesk============== Wigner Symbols ============== .. automodule:: sympy.physics.wigner :members: sympy-0.7.4.1/doc/src/modules/physics/gaussopt.rst0000644000175000017500000000014512253362407022331 0ustar georgeskgeorgesk=============== Gaussian Optics =============== .. automodule:: sympy.physics.gaussopt :members: sympy-0.7.4.1/doc/src/modules/physics/qho_1d.rst0000644000175000017500000000023412253362407021636 0ustar georgeskgeorgesk================================== Quantum Harmonic Oscillator in 1-D ================================== .. automodule:: sympy.physics.qho_1d :members: sympy-0.7.4.1/doc/src/modules/physics/sho.rst0000644000175000017500000000023112253362407021251 0ustar georgeskgeorgesk================================== Quantum Harmonic Oscillator in 3-D ================================== .. automodule:: sympy.physics.sho :members: sympy-0.7.4.1/doc/src/modules/physics/paulialgebra.rst0000644000175000017500000000014312253362407023112 0ustar georgeskgeorgesk============= Pauli Algebra ============= .. automodule:: sympy.physics.paulialgebra :members: sympy-0.7.4.1/doc/src/modules/physics/matrices.rst0000644000175000017500000000012012253362407022264 0ustar georgeskgeorgesk======== Matrices ======== .. automodule:: sympy.physics.matrices :members: sympy-0.7.4.1/doc/src/modules/numeric-computation.rst0000644000175000017500000001514512253362407023012 0ustar georgeskgeorgeskNumeric Computation =================== Symbolic computer algebra systems like SymPy facilitate the construction and manipulation of mathematical expressions. Unfortunately when it comes time to evaluate these expressions on numerical data, symbolic systems often have poor performance. Fortunately SymPy offers a number of easy-to-use hooks into other numeric systems, allowing you to create mathematical expressions in SymPy and then ship them off to the numeric system of your choice. This page documents many of the options available including the ``math`` library, the popular array computing package ``numpy``, code generation in ``Fortran`` or ``C``, and the use of the array compiler ``Theano``. Subs/evalf ---------- Subs is the slowest but simplest option. It runs at SymPy speeds. The ``.subs(...).evalf()`` method can substitute a numeric value for a symbolic one and then evaluate the result within SymPy. >>> from sympy import * >>> from sympy.abc import x >>> expr = sin(x)/x >>> expr.evalf(subs={x: 3.14}) 0.000507214304613640 This method is slow. You should use this method production only if performance is not an issue. You can expect ``.subs`` to take tens of microseconds. It can be useful while prototyping or if you just want to see a value once. Lambdify -------- The ``lambdify`` function translates SymPy expressions into Python functions, leveraging a variety of numerical libraries. It is used as follows: >>> from sympy import * >>> from sympy.abc import x >>> expr = sin(x)/x >>> f = lambdify(x, expr) >>> f(3.14) 0.000507214304614 Here lambdify makes a function that computes ``f(x) = sin(x)/x``. By default lambdify relies on implementations in the ``math`` standard library. This numerical evaluation takes on the order of hundreds of nanoseconds, roughly two orders of magnitude faster than the ``.subs`` method. This is the speed difference between SymPy and raw Python. Lambdify can leverage a variety of numerical backends. By default it uses the ``math`` library. However it also supports ``mpmath`` and most notably, ``numpy``. Using the ``numpy`` library gives the generated function access to powerful vectorized ufuncs that are backed by compiled C code. >>> from sympy import * >>> from sympy.abc import x >>> expr = sin(x)/x >>> f = lambdify(x, expr, "numpy") >>> import numpy >>> data = numpy.linspace(1, 10, 10000) >>> f(data) [ 0.84147098 0.84119981 0.84092844 ..., -0.05426074 -0.05433146 -0.05440211] If you have array-based data this can confer a considerable speedup, on the order of 10 nano-seconds per element. Unfortunately numpy incurs some start-up time and introduces an overhead of a few microseconds. uFuncify -------- While NumPy operations are very efficient for vectorized data they sometimes incur unnecessary costs when chained together. Consider the following operation >>> x = get_numpy_array(...) # doctest: +SKIP >>> y = sin(x) / x The operators ``sin`` and ``/`` call routines that execute tight for loops in ``C``. The resulting computation looks something like this .. code:: c for(int i = 0; i < n; i++) { temp[i] = sin(x[i]); } for(int i = i; i < n; i++) { y[i] = temp[i] / x[i]; } This is slightly sub-optimal because 1. We allocate an extra ``temp`` array 2. We walk over ``x`` memory twice when once would have been sufficient A better solution would fuse both element-wise operations into a single for loop .. code:: c for(int i = i; i < n; i++) { y[i] = sin(x[i]) / x[i]; } Statically compiled projects like NumPy are unable to take advantage of such optimizations. Fortunately, SymPy is able to generate efficient low-level C or Fortran code. It can then depend on projects like ``Cython`` or ``f2py`` to compile and reconnect that code back up to Python. Fortunately this process is well automated and a SymPy user wishing to make use of this code generation should call the ``ufuncify`` function >>> from sympy import * >>> from sympy.abc import x >>> expr = sin(x)/x >>> from sympy.utilities.autowrap import ufuncify >>> f = ufuncify([x], expr) This function ``f`` consumes and returns a NumPy array. Generally ``ufuncify`` performs at least as well as ``lambdify``. If the expression is complicated then ``ufuncify`` often significantly outperforms the NumPy backed solution. Jensen has a good blogpost on this topic http://ojensen.wordpress.com/2010/08/10/fast-ufunc-ish-hydrogen-solutions/ Theano ------ SymPy has a strong connection with [Theano](http://deeplearning.net/software/theano/), a mathematical array compiler. SymPy expressions can be easily translated to Theano graphs and then compiled using the Theano compiler chain. >>> from sympy import * >>> from sympy.abc import x >>> expr = sin(x)/x >>> from sympy.printing.theanocode import theano_function >>> f = theano_function([x], [expr]) If array broadcasting or types are desired then Theano requires this extra information >>> f = theano_function([x], [expr], dims={x: 1}, dtypes={x: 'float64'}) Theano has a more sophisticated code generation system than SymPy's C/Fortran code printers. Among other things it handles common sub-expressions and compilation onto the GPU. Theano also supports SymPy Matrix and Matrix Expression objects. So Which Should I Use? ---------------------- The options here were listed in order from slowest and least dependencies to fastest and most dependencies. For example, if you have Theano installed then that will often be the best choice. If you don't have Theano but do have ``f2py`` then you should use ``ufuncify``. +-----------------+-------+-----------------------------+---------------+ | Tool | Speed | Qualities | Dependencies | +=================+=======+=============================+===============+ | subs/evalf | 50us | Simple | None | +-----------------+-------+-----------------------------+---------------+ | lambdify | 1us | Scalar functions | math | +-----------------+-------+-----------------------------+---------------+ | lambdify-numpy | 10ns | Vector functions | numpy | +-----------------+-------+-----------------------------+---------------+ | ufuncify | 10ns | Complex vector expressions | f2py, Cython | +-----------------+-------+-----------------------------+---------------+ | Theano | 10ns | Many outputs, CSE, GPUs | Theano | +-----------------+-------+-----------------------------+---------------+ sympy-0.7.4.1/doc/src/modules/simplify/0000755000175000017500000000000012253362407020104 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/modules/simplify/hyperexpand.rst0000644000175000017500000006574212253362407023203 0ustar georgeskgeorgeskDetails 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. sympy-0.7.4.1/doc/src/modules/simplify/simplify.rst0000644000175000017500000000364612253362407022503 0ustar georgeskgeorgeskSimplify ******** .. currentmodule:: sympy.simplify.simplify simplify -------- .. autofunction:: simplify collect ------- .. autofunction:: collect .. autofunction:: rcollect separate -------- .. autofunction:: separate separatevars ------------ .. autofunction:: separatevars nthroot ------- .. autofunction:: nthroot rad_rationalize --------------- .. autofunction:: rad_rationalize 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 opt_cse ^^^^^^^ .. autofunction:: sympy.simplify.cse_main.opt_cse tree_cse ^^^^^^^^ .. autofunction:: sympy.simplify.cse_main.tree_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 sympy-0.7.4.1/doc/src/modules/utilities/0000755000175000017500000000000012253362407020263 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/modules/utilities/source.rst0000644000175000017500000000017312253362407022316 0ustar georgeskgeorgesk====================== Source Code Inspection ====================== .. automodule:: sympy.utilities.source :members: sympy-0.7.4.1/doc/src/modules/utilities/pkgdata.rst0000644000175000017500000000011712253362407022427 0ustar georgeskgeorgesk======= PKGDATA ======= .. automodule:: sympy.utilities.pkgdata :members: sympy-0.7.4.1/doc/src/modules/utilities/codegen.rst0000644000175000017500000000364312253362407022427 0ustar georgeskgeorgesk======= 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: sympy-0.7.4.1/doc/src/modules/utilities/timeutils.rst0000644000175000017500000000015412253362407023034 0ustar georgeskgeorgesk================ Timing Utilities ================ .. automodule:: sympy.utilities.timeutils :members: sympy-0.7.4.1/doc/src/modules/utilities/randtest.rst0000644000175000017500000000016112253362407022637 0ustar georgeskgeorgesk================== Randomised Testing ================== .. automodule:: sympy.utilities.randtest :members: sympy-0.7.4.1/doc/src/modules/utilities/lambdify.rst0000644000175000017500000000012312253362407022600 0ustar georgeskgeorgesk======== Lambdify ======== .. automodule:: sympy.utilities.lambdify :members: sympy-0.7.4.1/doc/src/modules/utilities/misc.rst0000644000175000017500000000013612253362407021750 0ustar georgeskgeorgesk============= Miscellaneous ============= .. automodule:: sympy.utilities.misc :members: sympy-0.7.4.1/doc/src/modules/utilities/index.rst0000644000175000017500000000061512253362407022126 0ustar georgeskgeorgesk.. _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 decorator.rst iterables.rst lambdify.rst memoization.rst misc.rst pkgdata.rst pytest.rst randtest.rst runtests.rst source.rst timeutils.rst sympy-0.7.4.1/doc/src/modules/utilities/decorator.rst0000644000175000017500000000012712253362407022777 0ustar georgeskgeorgesk========= Decorator ========= .. automodule:: sympy.utilities.decorator :members: sympy-0.7.4.1/doc/src/modules/utilities/iterables.rst0000644000175000017500000000520012253362407022764 0ustar georgeskgeorgesk========= 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=0)) # same as multiset_partitions ['A,BC', 'AB,C', 'AC,B'] >>> show(kbins("ABC", 2, ordered=1)) ['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: sympy-0.7.4.1/doc/src/modules/utilities/pytest.rst0000644000175000017500000000011312253362407022340 0ustar georgeskgeorgesk====== pytest ====== .. automodule:: sympy.utilities.pytest :members: sympy-0.7.4.1/doc/src/modules/utilities/autowrap.rst0000644000175000017500000000373712253362407022671 0ustar georgeskgeorgesk=============== 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 = 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: sympy-0.7.4.1/doc/src/modules/utilities/memoization.rst0000644000175000017500000000013712253362407023351 0ustar georgeskgeorgesk=========== Memoization =========== .. automodule:: sympy.utilities.memoization :members: sympy-0.7.4.1/doc/src/modules/utilities/runtests.rst0000644000175000017500000000012612253362407022703 0ustar georgeskgeorgesk========= Run Tests ========= .. automodule:: sympy.utilities.runtests :members: sympy-0.7.4.1/doc/src/guide.rst0000644000175000017500000004074512253362407016441 0ustar georgeskgeorgesk.. _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, which is what this guide is about. Once you grasp the ideas 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_mul() 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_. 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. .. _issues: http://code.google.com/p/sympy/issues/list .. _mailinglist: http://groups.google.com/group/sympy Improving the docs ------------------ Please see :ref:`the documentation ` how to fix and improve SymPy's documentation. All contribution is very welcome. sympy-0.7.4.1/doc/src/tutorial/0000755000175000017500000000000012253362407016443 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/tutorial/printing.rst0000644000175000017500000001476012253362407021037 0ustar georgeskgeorgesk.. _tutorial-printing: ========== Printing ========== As we have already seen, SymPy can pretty print its output using Unicode characters. This is a short introduction to the most common printing options available in SymPy. Printers ======== There are several printers available in SymPy. The most common ones are - str - repr - ASCII pretty printer - Unicode pretty printer - LaTeX - MathML - Dot In addition to these, there are also "printers" that can output SymPy objects to code, such as C, Fortran, Javascript, Theano, and Python. These are not discussed in this tutorial. Setting up Pretty Printing ========================== If all you want is the best pretty printing, use the ``init_printing`` function. This will automatically enable the best printer available in your environment. >>> from sympy import init_printing >>> init_printing() # doctest: +SKIP .. sidebar:: Quick Tip You an also change the printer used in SymPy Live. Just change the "Output Format" in the settings. If you plan to work in an interactive calculator-type session, the ``init_session`` function will automatically import everything in SymPy, create some common Symbols, setup plotting, and run ``init_printing``. >>> from sympy import init_session >>> init_session() # doctest: +SKIP :: Python console for SymPy 0.7.3 (Python 2.7.5-64-bit) (ground types: gmpy) These commands were executed: >>> from __future__ import division >>> 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 any case, this is what will happen: - In the IPython QTConsole, if `\LaTeX` is installed, it will enable a printer that uses `\LaTeX`. .. image:: ../pics/ipythonqtconsole.png :height: 500 If `\LaTeX` is not installed, but Matplotlib is installed, it will use the Matplotlib rendering engine. If Matplotlib is not installed, it uses the Unicode pretty printer. - In the IPython notebook, it will use MathJax to render `\LaTeX`. .. image:: ../pics/ipythonnotebook.png :height: 250 - In an IPython console session, or a regular Python session, it will use the Unicode pretty printer if the terminal supports Unicode. .. image:: ../pics/consoleunicode.png :width: 700 - In a terminal that does not support Unicode, the ASCII pretty printer is used. .. image:: ../pics/consoleascii.png :width: 700 To explicitly not use `\LaTeX`, pass ``use_latex=False`` to ``init_printing`` or ``init_session``. To explicitly not use Unicode, pass ``use_unicode=False``. Printing Functions ================== In addition to automatic printing, you can explicitly use any one of the printers by calling the appropriate function. str --- To get a string form of an expression, use ``str(expr)``. This is also the form that is produced by ``print expr``. String forms are designed to be easy to read, but in a form that is correct Python syntax so that it can be copied and pasted. The ``str`` form of an expression will usually look exactly the same as the expression as you would enter it. >>> from sympy import * >>> x, y, z = symbols('x y z') >>> str(Integral(sqrt(1/x), x)) 'Integral(sqrt(1/x), x)' >>> print(Integral(sqrt(1/x), x)) Integral(sqrt(1/x), x) repr ---- The repr form of an expression is designed to show the exact form of an expression. It will be discussed more in the :ref:`tutorial-manipulation` section. To get it, use ``srepr`` [#srepr-fn]_. >>> srepr(Integral(sqrt(1/x), x)) "Integral(Pow(Pow(Symbol('x'), Integer(-1)), Rational(1, 2)), Tuple(Symbol('x')))" The repr form is mostly useful for understanding how an expression is built internally. ASCII Pretty Printer -------------------- The ASCII pretty printer is accessed from ``pprint``. If the terminal does not support Unicode, the ASCII printer is used by default. Otherwise, you must pass ``use_unicode=False``. >>> pprint(Integral(sqrt(1/x), x), use_unicode=False) / | | ___ | / 1 | / - dx | \/ x | / ``pprint`` prints the output to the screen. If you want the string form, use ``pretty``. >>> pretty(Integral(sqrt(1/x), x), use_unicode=False) ' / \n | \n | ___ \n | / 1 \n | / - dx\n | \\/ x \n | \n/ ' >>> print(pretty(Integral(sqrt(1/x), x), use_unicode=False)) / | | ___ | / 1 | / - dx | \/ x | / Unicode Pretty Printer ---------------------- The Unicode pretty printer is also accessed from ``print``. It the terminal supports Unicode, it is used automatically. It ``pprint`` is not able to detect that the terminal supports unicode, you can pass ``use_unicode=True``. >>> pprint(Integral(sqrt(1/x), x), use_unicode=True) ⌠ ⎮ ___ ⎮ ╱ 1 ⎮ ╱ ─ dx ⎮ ╲╱ x ⌡ .. _LaTeX: `\LaTeX` -------- To get the `\LaTeX` form of an expression, use ``latex``. >>> print(latex(Integral(sqrt(1/x), x))) \int \sqrt{\frac{1}{x}}\, dx The ``latex`` function has many options to change the formatting of different things. See :py:meth:`its documentation ` for more details. MathML ------ There is also a printer to MathML, called ``print_mathml``. It must be imported from ``sympy.printing.mathml``. >>> from sympy.printing.mathml import print_mathml >>> print_mathml(Integral(sqrt(1/x), x)) x x -1 ``print_mathml`` prints the output. If you want the string, use the function ``mathml``. Dot --- The ``dotprint`` function in ``sympy.printing.dot`` prints output to dot format, which can be rendered with Graphviz. See the :ref:`tutorial-manipulation` section for some examples of the output of this printer. .. rubric:: Footnotes .. [#srepr-fn] SymPy does not use the Python builtin ``repr`` function for repr printing, because in Python ``str(list)`` calls ``repr`` on the elements of the list, and some SymPy functions return lists (such as ``solve``). Since ``srepr`` is so verbose, it is unlikely that anyone would want it called by default on the output of ``solve``. sympy-0.7.4.1/doc/src/tutorial/simplification.rst0000644000175000017500000007104512253362407022216 0ustar georgeskgeorgesk.. _tutorial-simplify: ================ Simplification ================ To make this document easier to read, we are going to enable pretty printing. >>> from sympy import * >>> x, y, z = symbols('x y z') >>> init_printing(use_unicode=True) ``simplify`` ============ Now let's jump in and do some interesting mathematics. One of the most useful features of a symbolic manipulation system is the ability to simplify mathematical expressions. SymPy has dozens of functions to perform various kinds of simplification. There is also one general function called ``simplify`` that attempts to apply all of these functions in an intelligent way to arrive at the simplest form of an expression. Here are some examples >>> simplify(sin(x)**2 + cos(x)**2) 1 >>> simplify((x**3 + x**2 - x - 1)/(x**2 + 2*x + 1)) x - 1 >>> simplify(gamma(x)/gamma(x - 2)) (x - 2)⋅(x - 1) Here, ``gamma(x)`` is `\Gamma(x)`, the `gamma function `_. We see that ``simplify`` is capable of handling a large class of expressions. But ``simplify`` has a pitfall. It just applies all the major simplification operations in SymPy, and uses heuristics to determine the simplest result. But "simplest" is not a well-defined term. For example, say we wanted to "simplify" `x^2 + 2x + 1` into `(x + 1)^2`: >>> simplify(x**2 + 2*x + 1) 2 x + 2⋅x + 1 We did not get what we want. There is a function to perform this simplification, called ``factor``, which will be discussed below. Another pitfall to ``simplify``, which is that it can be unnecessarily slow, since it tries many kinds of simplifications before picking the best one. If you already know exactly what kind of simplification you are after, it is better to apply the specific simplification function(s) that apply those simplifications. Applying specific simplification functions instead of ``simplify`` also has the advantage that specific functions have certain guarantees about the form of their output. These will be discussed with each function below. For example, ``factor``, when called on a polynomial with rational coefficients, is guaranteed to factor the polynomial into irreducible factors. ``simplify`` has no guarantees. It is entirely heuristical, and, as we saw above, it may even miss a possible type of simplification that SymPy is capable of doing. ``simplify`` is best when used interactively, when you just want to whittle down an expression to a simpler form. You may then choose to apply specific functions once you see what ``simplify`` returns, to get a more precise result. It is also useful when you have no idea what form an expression will take, and you need a catchall function to simplify it. Polynomial/Rational Function Simplification =========================================== ``expand`` ---------- ``expand`` is one of the most common simplification functions in SymPy. Although it has a lot of scopes, for now, we will consider its function in expanding polynomial expressions. For example: >>> expand((x + 1)**2) 2 x + 2⋅x + 1 >>> expand((x + 2)*(x - 3)) 2 x - x - 6 Given a polynomial, ``expand`` will put it into a canonical form of a sum of monomials. ``expand`` may not sound like a simplification function. After all, by its very name, it makes expressions bigger, not smaller. Usually this is the case, but often an expression will become smaller upon calling ``expand`` on it due to cancellation >>> expand((x + 1)*(x - 2) - (x - 1)*x) -2 ``factor`` ---------- ``factor`` takes a polynomial and factors it into irreducible factors over the rational numbers. For example: >>> factor(x**3 - x**2 + x - 1) ⎛ 2 ⎞ (x - 1)⋅⎝x + 1⎠ >>> factor(x**2*z + 4*x*y*z + 4*y**2*z) 2 z⋅(x + 2⋅y) For polynomials, ``factor`` is the opposite of ``expand``. ``factor`` uses a complete multivariate factorization algorithm over the rational numbers, which means that each of the factors returned by ``factor`` is guaranteed to be irreducible. If you are interested in the factors themselves, ``factor_list`` returns a more structured output. >>> factor_list(x**2*z + 4*x*y*z + 4*y**2*z) (1, [(z, 1), (x + 2⋅y, 2)]) Note that the input to ``factor`` and ``expand`` need not be polynomials in the strict sense. They will intelligently factor or expand any kind of expression (though note that the factors may not be irreducible if the input is no longer a polynomial over the rationals). >>> expand((cos(x) + sin(x))**2) 2 2 sin (x) + 2⋅sin(x)⋅cos(x) + cos (x) >>> factor(cos(x)**2 + 2*cos(x)*sin(x) + sin(x)**2) 2 (sin(x) + cos(x)) ``collect`` ----------- ``collect`` collects common powers of a term in an expression. For example >>> expr = x*y + x - 3 + 2*x**2 - z*x**2 + x**3 >>> expr 3 2 2 x - x ⋅z + 2⋅x + x⋅y + x - 3 >>> collected_expr = collect(expr, x) >>> collected_expr 3 2 x + x ⋅(-z + 2) + x⋅(y + 1) - 3 ``collect`` is particularly useful in conjunction with the ``.coeff`` method. ``expr.coeff(x, n)`` gives the coefficient of ``x**n`` in ``expr``: >>> collected_expr.coeff(x, 2) -z + 2 .. TODO: Discuss coeff method in more detail in some other section (maybe basic expression manipulation tools) ``cancel`` ---------- ``cancel`` will take any rational function and put it into the standard canonical form, `\frac{p}{q}`, where `p` and `q` are expanded polynomials with no common factors, and the leading coefficients of `p` and `q` do not have denominators (i.e., are integers). >>> cancel((x**2 + 2*x + 1)/(x**2 + x)) x + 1 ───── x >>> expr = 1/x + (3*x/2 - 2)/(x - 4) >>> expr 3⋅x ─── - 2 2 1 ─────── + ─ x - 4 x >>> cancel(expr) 2 3⋅x - 2⋅x - 8 ────────────── 2 2⋅x - 8⋅x >>> expr = (x*y**2 - 2*x*y*z + x*z**2 + y**2 - 2*y*z + z**2)/(x**2 - 1) >>> expr 2 2 2 2 x⋅y - 2⋅x⋅y⋅z + x⋅z + y - 2⋅y⋅z + z ─────────────────────────────────────── 2 x - 1 >>> cancel(expr) 2 2 y - 2⋅y⋅z + z ─────────────── x - 1 Note that since ``factor`` will completely factorize both the numerator and the denominator of an expression, it can also be used to do the same thing: >>> factor(expr) 2 (y - z) ──────── x - 1 However, if you are only interested in making sure that the expression is in canceled form, ``cancel`` is more efficient than ``factor``. ``apart`` --------- ``apart`` performs a `partial fraction decomposition `_ on a rational function. >>> expr = (4*x**3 + 21*x**2 + 10*x + 12)/(x**4 + 5*x**3 + 5*x**2 + 4*x) >>> expr 3 2 4⋅x + 21⋅x + 10⋅x + 12 ──────────────────────── 4 3 2 x + 5⋅x + 5⋅x + 4⋅x >>> apart(expr) 2⋅x - 1 1 3 ────────── - ───── + ─ 2 x + 4 x x + x + 1 Trigonometric Simplification ============================ .. note:: SymPy follows Python's naming conventions for inverse trigonometric functions, which is to append an ``a`` to the front of the function's name. For example, the inverse cosine, or arc cosine, is called ``acos``. >>> acos(x) acos(x) >>> cos(acos(x)) x >>> asin(1) π ─ 2 .. TODO: Can we actually do anything with inverse trig functions, simplification wise? ``trigsimp`` ------------ To simplify expressions using trigonometric identities, use ``trigsimp``. >>> trigsimp(sin(x)**2 + cos(x)**2) 1 >>> trigsimp(sin(x)**4 - 2*cos(x)**2*sin(x)**2 + cos(x)**4) cos(4⋅x) 1 ──────── + ─ 2 2 >>> trigsimp(sin(x)*tan(x)/sec(x)) 2 sin (x) ``trigsimp`` also works with hyperbolic trig functions. >>> trigsimp(cosh(x)**2 + sinh(x)**2) cosh(2⋅x) >>> trigsimp(sinh(x)/tanh(x)) cosh(x) Much like ``simplify``, ``trigsimp`` applies various trigonometric identities to the input expression, and then uses a heuristic to return the "best" one. ``expand_trig`` --------------- To expand trigonometric functions, that is, apply the sum or double angle identities, use ``expand_trig``. >>> expand_trig(sin(x + y)) sin(x)⋅cos(y) + sin(y)⋅cos(x) >>> expand_trig(tan(2*x)) 2⋅tan(x) ───────────── 2 - tan (x) + 1 Because ``expand_trig`` tends to make trigonometric expressions larger, and ``trigsimp`` tends to make them smaller, these identities can be applied in reverse using ``trigsimp`` >>> trigsimp(sin(x)*cos(y) + sin(y)*cos(x)) sin(x + y) .. TODO: It would be much better to teach individual trig rewriting functions here, but they don't exist yet. See https://code.google.com/p/sympy/issues/detail?id=357. Powers ====== Before we introduce the power simplification functions, a mathematical discussion on the identities held by powers is in order. There are three kinds of identities satisfied by exponents 1. `x^ax^b = x^{a + b}` 2. `x^ay^a = (xy)^a` 3. `(x^a)^b = x^{ab}` Identity 1 is always true. Identity 2 is not always true. For example, if `x = y = -1` and `a = \frac{1}{2}`, then `x^ay^a = \sqrt{-1}\sqrt{-1} = i\cdot i = -1`, whereas `(xy)^a = \sqrt{-1\cdot-1} = \sqrt{1} = 1`. However, identity 2 is true at least if `x` and `y` are nonnegative and `a` is real (it may also be true under other conditions as well). A common consequence of the failure of identity 2 is that `\sqrt{x}\sqrt{y} \neq \sqrt{xy}`. Identity 3 is not always true. For example, if `x = -1`, `a = 2`, and `b = \frac{1}{2}`, then `(x^a)^b = {\left ((-1)^2\right )}^{1/2} = \sqrt{1} = 1` and `x^{ab} = (-1)^{2\cdot1/2} = (-1)^1 = -1`. However, identity 3 is true at least if `x` is nonnegative or `b` is an integer (again, it may also hold in other cases as well). Two common consequences of the failure of identity 3 are that `\sqrt{x^2}\neq x` and that `\sqrt{\frac{1}{x}} \neq \frac{1}{\sqrt{x}}`. To summarize +-----------------------+------------------------------------+----------------------------------------------------+-----------------------------------------------------------------------------+ |Identity |Sufficient conditions to hold |Counterexample when conditions are not met |Important consequences | +=======================+====================================+====================================================+=============================================================================+ |1. `x^ax^b = x^{a + b}`|Always true |None |None | +-----------------------+------------------------------------+----------------------------------------------------+-----------------------------------------------------------------------------+ |2. `x^ay^a = (xy)^a` |`x, y \geq 0` and `a \in \mathbb{R}`|`(-1)^{1/2}(-1)^{1/2} \neq (-1\cdot-1)^{1/2}` |`\sqrt{x}\sqrt{y} \neq \sqrt{xy}` in general | +-----------------------+------------------------------------+----------------------------------------------------+-----------------------------------------------------------------------------+ |3. `(x^a)^b = x^{ab}` |`x \geq 0` or `b \in \mathbb{Z}` |`{\left((-1)^2\right )}^{1/2} \neq (-1)^{2\cdot1/2}`|`\sqrt{x^2}\neq x` and `\sqrt{\frac{1}{x}}\neq\frac{1}{\sqrt{x}}` in general | +-----------------------+------------------------------------+----------------------------------------------------+-----------------------------------------------------------------------------+ This is important to remember, because by default, SymPy will not perform simplifications if they are not true in general. In order to make SymPy perform simplifications involving identities that are only true under certain assumptions, we need to put assumptions on our Symbols. We will undertake a full discussion of the assumptions system later, but for now, all we need to know are the following. - By default, SymPy Symbols are assumed to be complex (elements of `\mathbb{C}`). That is, a simplification will not be applied to an expression with a given Symbol unless it holds for all complex numbers. - Symbols can be given different assumptions by passing the assumption to ``symbols``. For the rest of this section, we will be assuming that ``x`` and ``y`` are positive, and that ``a`` and ``b`` are real. We will leave ``z``, ``t``, and ``c`` as arbitrary complex Symbols to demonstrate what happens in that case. >>> x, y = symbols('x y', positive=True) >>> a, b = symbols('a b', real=True) >>> z, t, c = symbols('z t c') .. TODO: Rewrite this using the new assumptions .. note:: In SymPy, ``sqrt(x)`` is just a shortcut to ``x**Rational(1, 2)``. They are exactly the same object. >>> sqrt(x) == x**Rational(1, 2) True ``powsimp`` ----------- ``powsimp`` applies identities 1 and 2 from above, from left to right. >>> powsimp(x**a*x**b) a + b x >>> powsimp(x**a*y**a) a (x⋅y) Notice that ``powsimp`` refuses to do the simplification if it is not valid. >>> powsimp(t**c*z**c) c c t ⋅z If you know that you want to apply this simplification, but you don't want to mess with assumptions, you can pass the ``force=True`` flag. This will force the simplification to take place, regardless of assumptions. >>> powsimp(t**c*z**c, force=True) c (t⋅z) Note that in some instances, in particular, when the exponents are integers or rational numbers, and identity 2 holds, it will be applied automatically >>> (z*t)**2 2 2 t ⋅z >>> sqrt(x*y) ___ ___ ╲╱ x ⋅╲╱ y This means that it will be impossible to undo this identity with ``powsimp``, because even if ``powsimp`` were to put the bases together, they would be automatically split apart again. >>> powsimp(z**2*t**2) 2 2 t ⋅z >>> powsimp(sqrt(x)*sqrt(y)) ___ ___ ╲╱ x ⋅╲╱ y ``expand_power_exp`` / ``expand_power_base`` -------------------------------------------- ``expand_power_exp`` and ``expand_power_base`` apply identities 1 and 2 from right to left, respectively. >>> expand_power_exp(x**(a + b)) a b x ⋅x >>> expand_power_base((x*y)**a) a a x ⋅y As with ``powsimp``, identity 2 is not applied if it is not valid. >>> expand_power_base((z*t)**c) c (t⋅z) And as with ``powsimp``, you can force the expansion to happen without fiddling with assumptions by using ``force=True``. >>> expand_power_base((z*t)**c, force=True) c c t ⋅z As with identity 2, identity 1 is applied automatically if the power is a number, and hence cannot be undone with ``expand_power_exp``. >>> x**2*x**3 5 x >>> expand_power_exp(x**5) 5 x ``powdenest`` ------------- ``powdenest`` applies identity 3, from left to right. >>> powdenest((x**a)**b) a⋅b x As before, the identity is not applied if it is not true under the given assumptions. >>> powdenest((z**a)**b) b ⎛ a⎞ ⎝z ⎠ And as before, this can be manually overridden with ``force=True``. >>> powdenest((z**a)**b, force=True) a⋅b z Exponentials and logarithms =========================== .. note:: In SymPy, as in Python and most programming languages, ``log`` is the natural logarithm, also known as ``ln``. SymPy automatically provides an alias ``ln = log`` in case you forget this. >>> ln(x) log(x) Logarithms have similar issues as powers. There are two main identities 1. `\log{(xy)} = \log{(x)} + \log{(y)}` 2. `\log{(x^n)} = n\log{(x)}` Neither identity is true for arbitrary complex `x` and `y`, due to the branch cut in the complex plane for the complex logarithm. However, sufficient conditions for the identities to hold are if `x` and `y` are positive and `n` is real. >>> x, y = symbols('x y', positive=True) >>> n = symbols('n', real=True) As before, ``z`` and ``t`` will be Symbols with no additional assumptions. Note that the identity `\log{\left (\frac{x}{y}\right )} = \log(x) - \log(y)` is a special case of identities 1 and 2 by `\log{\left (\frac{x}{y}\right )} =` `\log{\left (x\cdot\frac{1}{y}\right )} =` `\log(x) + \log{\left( y^{-1}\right )} =` `\log(x) - \log(y)`, and thus it also holds if `x` and `y` are positive, but may not hold in general. We also see that `\log{\left( e^x \right)} = x` comes from `\log{\left ( e^x \right)} = x\log(e) = x`, and thus holds when `x` is real (and it can be verified that it does not hold in general for arbitrary complex `x`, for example, `\log{\left (e^{x + 2\pi i}\right)} = \log{\left (e^x\right )} = x \neq x + 2\pi i`). ``expand_log`` -------------- To apply identities 1 and 2 from left to right, use ``expand_log``. As always, the identities will not be applied unless they are valid. >>> expand_log(log(x*y)) log(x) + log(y) >>> expand_log(log(x/y)) log(x) - log(y) >>> expand_log(log(x**2)) 2⋅log(x) >>> expand_log(log(x**n)) n⋅log(x) >>> expand_log(log(z*t)) log(t⋅z) As with ``powsimp`` and ``powdenest``, ``expand_log`` has a ``force`` option that can be used to ignore assumptions. >>> expand_log(log(z**2)) ⎛ 2⎞ log⎝z ⎠ >>> expand_log(log(z**2), force=True) 2⋅log(z) ``logcombine`` -------------- To apply identities 1 and 2 from right to left, use ``logcombine``. >>> logcombine(log(x) + log(y)) log(x⋅y) >>> logcombine(n*log(x)) ⎛ n⎞ log⎝x ⎠ >>> logcombine(n*log(z)) n⋅log(z) ``logcombine`` also has a ``force`` option that can be used to ignore assumptions. >>> logcombine(n*log(z), force=True) ⎛ n⎞ log⎝z ⎠ Special Functions ================= SymPy implements dozens of special functions, ranging from functions in combinatorics to mathematical physics. An extensive list of the special functions included with SymPy and their documentation is at the :ref:`Functions Module ` page. For the purposes of this tutorial, let's introduce a few special functions in SymPy. Let's define ``x``, ``y``, and ``z`` as regular, complex Symbols, removing any assumptions we put on them in the previous section. We will also define ``k``, ``m``, and ``n``. >>> x, y, z = symbols('x y z') >>> k, m, n = symbols('k m n') The `factorial `_ function is ``factorial``. ``factorial(n)`` represents `n!= 1\cdot2\cdots(n - 1)\cdot n`. `n!` represents the number of permutations of `n` distinct items. >>> factorial(n) n! The `binomial coefficient `_ function is ``binomial``. ``binomial(n, k)`` represents `\binom{n}{k}`, the number of ways to choose `k` items from a set of `n` distinct items. It is also often written as `nCk`, and is pronounced "`n` choose `k`". >>> binomial(n, k) ⎛n⎞ ⎜ ⎟ ⎝k⎠ The factorial function is closely related to the `gamma function `_, ``gamma``. ``gamma(z)`` represents `\Gamma(z) = \int_0^\infty t^{z - 1}e^{-t}\,dt`, which for positive integer `z` is the same as `(z - 1)!`. >>> gamma(z) Γ(z) The `generalized hypergeometric function `_ is ``hyper``. ``hyper([a_1, ..., a_p], [b_1, ..., b_q], z)`` represents `{}_pF_q\left(\begin{matrix} a_1, \dots, a_p \\ b_1, \dots, b_q \end{matrix} \middle| z \right)`. The most common case is `{}_2F_1`, which is often referred to as the `ordinary hypergeometric function `_. >>> hyper([1, 2], [3], z) ┌─ ⎛1, 2 │ ⎞ ├─ ⎜ │ z⎟ 2╵ 1 ⎝ 3 │ ⎠ ``rewrite`` ----------- A common way to deal with special functions is to rewrite them in terms of one another. This works for any function in SymPy, not just special functions. To rewrite an expression in terms of a function, use ``expr.rewrite(function)``. For example, >>> tan(x).rewrite(sin) 2 2⋅sin (x) ───────── sin(2⋅x) >>> factorial(x).rewrite(gamma) Γ(x + 1) For some tips on applying more targeted rewriting, see the :ref:`tutorial-manipulation` section. ``expand_func`` --------------- To expand special functions in terms of some identities, use ``expand_func``. For example >>> expand_func(gamma(x + 3)) x⋅(x + 1)⋅(x + 2)⋅Γ(x) ``hyperexpand`` --------------- To rewrite ``hyper`` in terms of more standard functions, use ``hyperexpand``. >>> hyperexpand(hyper([1, 1], [2], z)) -log(-z + 1) ───────────── z ``hyperexpand`` also works on the more general Meijer G-function (see :py:meth:`its documentation ` for more information). >>> expr = meijerg([[1],[1]], [[1],[]], -z) >>> expr ╭─╮1, 1 ⎛1 1 │ ⎞ │╶┐ ⎜ │ -z⎟ ╰─╯2, 1 ⎝1 │ ⎠ >>> hyperexpand(expr) 1 ─ z ℯ ``combsimp`` ------------ To simplify combinatorial expressions, use ``combsimp``. >>> combsimp(factorial(n)/factorial(n - 3)) n⋅(n - 2)⋅(n - 1) >>> combsimp(binomial(n+1, k+1)/binomial(n, k)) n + 1 ───── k + 1 ``combsimp`` also simplifies expressions with ``gamma``. >>> combsimp(gamma(x)*gamma(1 - x)) π ──────── sin(π⋅x) Example: Continued Fractions ============================ Let's use SymPy to explore continued fractions. A `continued fraction `_ is an expression of the form .. math:: a_0 + \cfrac{1}{a_1 + \cfrac{1}{a_2 + \cfrac{1}{ \ddots + \cfrac{1}{a_n} }}} where `a_0, \ldots, a_n` are integers, and `a_1, \ldots, a_n` are positive. A continued fraction can also be infinite, but infinite objects are more difficult to represent in computers, so we will only examine the finite case here. A continued fraction of the above form is often represented as a list `[a_0; a_1, \ldots, a_n]`. Let's write a simple function that converts such a list to its continued fraction form. The easiest way to construct a continued fraction from a list is to work backwards. Note that despite the apparent symmetry of the definition, the first element, `a_0`, must usually be handled differently from the rest. >>> def list_to_frac(l): ... expr = Integer(0) ... for i in reversed(l[1:]): ... expr += i ... expr = 1/expr ... return l[0] + expr >>> list_to_frac([x, y, z]) 1 x + ───── 1 y + ─ z We use ``Integer(0)`` in ``list_to_frac`` so that the result will always be a SymPy object, even if we only pass in Python ints. >>> list_to_frac([1, 2, 3, 4]) 43 ── 30 Every finite continued fraction is a rational number, but we are interested in symbolics here, so let's create a symbolic continued fraction. The ``symbols`` function that we have been using has a shortcut to create numbered symbols. ``symbols('a0:5')`` will create the symbols ``a0``, ``a1``, ..., ``a5``. >>> syms = symbols('a0:5') >>> syms (a₀, a₁, a₂, a₃, a₄) >>> a0, a1, a2, a3, a4 = syms >>> frac = list_to_frac(syms) >>> frac 1 a₀ + ───────────────── 1 a₁ + ──────────── 1 a₂ + ─────── 1 a₃ + ── a₄ This form is useful for understanding continued fractions, but lets put it into standard rational function form using ``cancel``. >>> frac = cancel(frac) >>> frac a₀⋅a₁⋅a₂⋅a₃⋅a₄ + a₀⋅a₁⋅a₂ + a₀⋅a₁⋅a₄ + a₀⋅a₃⋅a₄ + a₀ + a₂⋅a₃⋅a₄ + a₂ + a₄ ───────────────────────────────────────────────────────────────────────── a₁⋅a₂⋅a₃⋅a₄ + a₁⋅a₂ + a₁⋅a₄ + a₃⋅a₄ + 1 Now suppose we were given ``frac`` in the above canceled form. In fact, we might be given the fraction in any form, but we can always put it into the above canonical form with ``cancel``. Suppose that we knew that it could be rewritten as a continued fraction. How could we do this with SymPy? A continued fraction is recursively `c + \frac{1}{f}`, where `c` is an integer and `f` is a (smaller) continued fraction. If we could write the expression in this form, we could pull out each `c` recursively and add it to a list. We could then get a continued fraction with our ``list_to_frac`` function. The key observation here is that we can convert an expression to the form `c + \frac{1}{f}` by doing a partial fraction decomposition with respect to `c`. This is because `f` does not contain `c`. This means we need to use the ``apart`` function. We use ``apart`` to pull the term out, then subtract it from the expression, and take the reciprocal to get the `f` part. >>> l = [] >>> frac = apart(frac, a0) >>> frac a₂⋅a₃⋅a₄ + a₂ + a₄ a₀ + ─────────────────────────────────────── a₁⋅a₂⋅a₃⋅a₄ + a₁⋅a₂ + a₁⋅a₄ + a₃⋅a₄ + 1 >>> l.append(a0) >>> frac = 1/(frac - a0) >>> frac a₁⋅a₂⋅a₃⋅a₄ + a₁⋅a₂ + a₁⋅a₄ + a₃⋅a₄ + 1 ─────────────────────────────────────── a₂⋅a₃⋅a₄ + a₂ + a₄ Now we repeat this process >>> frac = apart(frac, a1) >>> frac a₃⋅a₄ + 1 a₁ + ────────────────── a₂⋅a₃⋅a₄ + a₂ + a₄ >>> l.append(a1) >>> frac = 1/(frac - a1) >>> frac = apart(frac, a2) >>> frac a₄ a₂ + ───────── a₃⋅a₄ + 1 >>> l.append(a2) >>> frac = 1/(frac - a2) >>> frac = apart(frac, a3) >>> frac 1 a₃ + ── a₄ >>> l.append(a3) >>> frac = 1/(frac - a3) >>> frac = apart(frac, a4) >>> frac a₄ >>> l.append(a4) >>> list_to_frac(l) 1 a₀ + ───────────────── 1 a₁ + ──────────── 1 a₂ + ─────── 1 a₃ + ── a₄ .. sidebar:: Quick Tip You can execute multiple lines at once in SymPy Live. Typing ``Shift-Enter`` instead of ``Enter`` will enter a newline instead of executing. Of course, this exercise seems pointless, because we already know that our ``frac`` is ``list_to_frac([a0, a1, a2, a3, a4])``. So try the following exercise. Take a list of symbols and randomize them, and create the canceled continued fraction, and see if you can reproduce the original list. For example >>> import random >>> l = list(symbols('a0:5')) >>> random.shuffle(l) >>> orig_frac = frac = cancel(list_to_frac(l)) >>> del l Click on "Run code block in SymPy Live" on the definition of ``list_to_frac`` above, and then on the above example, and try to reproduce ``l`` from ``frac``. I have deleted ``l`` at the end to remove the temptation for peeking (you can check your answer at the end by calling ``cancel(list_to_frac(l))`` on the list that you generate at the end, and comparing it to ``orig_frac``. See if you can think of a way to figure out what symbol to pass to ``apart`` at each stage (hint: think of what happens to `a_0` in the formula `a_0 + \frac{1}{a_1 + \cdots}` when it is canceled). .. Answer: a0 is the only symbol that does not appear in the denominator sympy-0.7.4.1/doc/src/tutorial/basic_operations.rst0000644000175000017500000001546612253362407022535 0ustar georgeskgeorgesk.. _tutorial-basic: ================== Basic Operations ================== Here we discuss some of the most basic operations needed for expression manipulation in SymPy. Some more advanced operations will be discussed later in the :ref:`advanced expression manipulation ` section. >>> from sympy import * >>> x, y, z = symbols("x y z") Substitution ============ One of the most common things you might want to do with a mathematical expression is substitution. Substitution replaces all instances of something in an expression with something else. It is done using the ``subs`` method. For example >>> expr = cos(x) + 1 >>> expr.subs(x, y) cos(y) + 1 Substitution is usually done for one of two reasons: 1. Evaluating an expression at a point. For example, if our expression is ``cos(x) + 1`` and we want to evaluate it at the point ``x = 0``, so that we get ``cos(0) + 1``, which is 2. >>> expr.subs(x, 0) 2 2. Replacing a subexpression with another subexpression. There are two reasons we might want to do this. The first is if we are trying to build an expression that has some symmetry, such as `x^{x^{x^x}}`. To build this, we might start with ``x**y``, and replace ``y`` with ``x**y``. We would then get ``x**(x**y)``. If we replaced ``y`` in this new expression with ``x**x``, we would get ``x**(x**(x**x))``, the desired expression. >>> expr = x**y >>> expr x**y >>> expr = expr.subs(y, x**y) >>> expr x**(x**y) >>> expr = expr.subs(y, x**x) >>> expr x**(x**(x**x)) The second is if we want to perform a very controlled simplification, or perhaps a simplification that SymPy is otherwise unable to do. For example, say we have `\sin(2x) + \cos(2x)`, and we want to replace `\sin(2x)` with `2\sin(x)\cos(x)`. As we will learn later, the function ``expand_trig`` does this. However, this function will also expand `\cos(2x)`, which we may not want. While there are ways to perform such precise simplification, and we will learn some of them in the :ref:`advanced expression manipulation ` section, an easy way is to just replace `\sin(2x)` with `2\sin(x)\cos(x)`. >>> expr = sin(2*x) + cos(2*x) >>> expand_trig(expr) 2*sin(x)*cos(x) + 2*cos(x)**2 - 1 >>> expr.subs(sin(2*x), 2*sin(x)*cos(x)) 2*sin(x)*cos(x) + cos(2*x) There are two important things to note about ``subs``. First, it returns a new expression. SymPy objects are immutable. That means that ``subs`` does not modify it in-place. For example >>> expr = cos(x) >>> expr.subs(x, 0) 1 >>> expr cos(x) >>> x x .. sidebar:: Quick Tip SymPy expressions are immutable. No function will change them in-place. Here, we see that performing ``expr.subs(x, 0)`` leaves ``expr`` unchanged. In fact, since SymPy expressions are immutable, no function will change them in-place. All functions will return new expressions. To perform multiple substitutions at once, pass a list of ``(old, new)`` pairs to ``subs``. >>> expr = x**3 + 4*x*y - z >>> expr.subs([(x, 2), (y, 4), (z, 0)]) 40 It is often useful to combine this with a list comprehension to do a large set of similar replacements all at once. For example, say we had `x^4 - 4x^3 + 4x^2 - 2x + 3` and we wanted to replace all instances of `x` that have an even power with `y`, to get `y^4 - 4x^3 + 4y^2 - 2x + 3`. >>> expr = x**4 - 4*x**3 + 4*x**2 - 2*x + 3 >>> replacements = [(x**i, y**i) for i in range(5) if i % 2 == 0] >>> expr.subs(replacements) -4*x**3 - 2*x + y**4 + 4*y**2 + 3 Converting Strings to SymPy Expressions ======================================= The ``sympify`` function (that's ``sympify``, not to be confused with ``simplify``) can be used to convert strings into SymPy expressions. For example >>> str_expr = "x**2 + 3*x - 1/2" >>> expr = sympify(str_expr) >>> expr x**2 + 3*x - 1/2 >>> expr.subs(x, 2) 19/2 .. warning:: ``sympify`` uses ``eval``. Don't use it on unsanitized input. ``evalf`` ========= To evaluate a numerical expression into a floating point number, use ``evalf``. >>> expr = sqrt(8) >>> expr.evalf() 2.82842712474619 SymPy can evaluate floating point expressions to arbitrary precision. By default, 15 digits of precision are used, but you can pass any number as the argument to ``evalf``. Let's compute the first 100 digits of `\pi`. >>> pi.evalf(100) 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068 To numerically evaluate an expression with a Symbol at a point, we might use ``subs`` followed by ``evalf``, but it is more efficient and numerically stable to pass the substitution to ``evalf`` using the ``subs`` flag, which takes a dictionary of ``Symbol: point`` pairs. >>> expr = cos(2*x) >>> expr.evalf(subs={x: 2.4}) 0.0874989834394464 Sometimes there are roundoff errors smaller than the desired precision that remain after an expression is evaluated. Such numbers can be removed at the user's discretion by setting the ``chop`` flag to True. >>> one = cos(1)**2 + sin(1)**2 >>> (one - 1).evalf() -0.e-124 >>> (one - 1).evalf(chop=True) 0 ``lambdify`` ============ ``subs`` and ``evalf`` are good if you want to do simple evaluation, but if you intend to evaluate an expression at many points, there are more efficient ways. For example, if you wanted to evaluate an expression at a thousand points, using SymPy would be far slower than it needs to be, especially if you only care about machine precision. Instead, you should use libraries like `NumPy `_ and `SciPy `_. The easiest way to convert a SymPy expression to an expression that can be numerically evaluated is to use the ``lambdify`` function. ``lambdify`` acts like a ``lambda`` function, except it converts the SymPy names to the names of the given numerical library, usually NumPy. For example >>> import numpy # doctest:+SKIP >>> a = numpy.arange(10) # doctest:+SKIP >>> expr = sin(x) >>> f = lambdify(x, expr, "numpy") # doctest:+SKIP >>> f(a) # doctest:+SKIP [ 0. 0.84147098 0.90929743 0.14112001 -0.7568025 -0.95892427 -0.2794155 0.6569866 0.98935825 0.41211849] You can use other libraries than NumPy. For example, to use the standard library math module, use ``"math"``. >>> f = lambdify(x, expr, "math") >>> f(0.1) 0.0998334166468 To use lambdify with numerical libraries that it does not know about, pass a dictionary of ``sympy_name:numerical_function`` pairs. For example >>> def mysin(x): ... """ ... My sine. Not only accurate for small x. ... """ ... return x >>> f = lambdify(x, expr, {"sin":mysin}) >>> f(0.1) 0.1 .. TODO: Write an advanced numerics section sympy-0.7.4.1/doc/src/tutorial/gotchas.rst0000644000175000017500000002472512253362407020637 0ustar georgeskgeorgesk========= Gotchas ========= To begin, we should make something about SymPy clear. SymPy is nothing more than a Python library, like ``NumPy``, ``Django``, or even modules in the Python standard library ``sys`` or ``re``. What this means is that SymPy does not add anything to the Python language. Limitations that are inherent in the Python language are also inherent in SymPy. It also means that SymPy tries to use Python idioms whenever possible, making programming with SymPy easy for those already familiar with programming with Python. As a simple example, SymPy uses Python syntax to build expressions. Implicit multiplication (like ``3x`` or ``3 x``) is not allowed in Python, and thus not allowed in SymPy. To multiply ``3`` and ``x``, you must type ``3*x`` with the ``*``. .. _tutorial-gotchas-symbols: Symbols ======= One consequence of this fact is that SymPy can be used in any environment where Python is available. We just import it, like we would any other library: >>> from sympy import * This imports all the functions and classes from SymPy into our interactive Python session. Now, suppose we start to do a computation. >>> x + 1 Traceback (most recent call last): ... NameError: name 'x' is not defined Oops! What happened here? We tried to use the variable ``x``, but it tells us that ``x`` is not defined. In Python, variables have no meaning until they are defined. SymPy is no different. Unlike many symbolic manipulation systems you may have used, in SymPy, variables are not defined automatically. To define variables, we must use ``symbols``. >>> x = symbols('x') >>> x + 1 x + 1 ``symbols`` takes a string of variable names separated by spaces or commas, and creates Symbols out of them. We can then assign these to variable names. Later, we will investigate some convenient ways we can work around this issue. For now, let us just define the most common variable names, ``x``, ``y``, and ``z``, for use through the rest of this section >>> x, y, z = symbols('x y z') As a final note, we note that the name of a Symbol and the name of the variable it is assigned to need not have anything to do with one another. >>> a, b = symbols('b a') >>> a b >>> b a Here we have done the very confusing thing of assigning a Symbol with the name ``a`` to the variable ``b``, and a Symbol of the name ``b`` to the variable ``a``. Now the Python variable named ``a`` points to the SymPy Symbol named ``b``, and visa versa. How confusing. We could have also done something like >>> crazy = symbols('unrelated') >>> crazy + 1 unrelated + 1 This also shows that Symbols can have names longer than one character if we want. Usually, the best practice is to assign Symbols to Python variables of the same name, although there are exceptions: Symbol names can contain characters that are not allowed in Python variable names, or may just want to avoid typing long names by assigning Symbols with long names to single letter Python variables. To avoid confusion, throughout this tutorial, Symbol names and Python variable names will always coincide. Furthermore, the word "Symbol" will refer to a SymPy Symbol and the word "variable" will refer to a Python variable. Finally, let us be sure we understand the difference between SymPy Symbols and Python variables. Consider the following:: x = symbols('x') expr = x + 1 x = 2 print(expr) What do you think the output of this code will be? If you thought ``3``, you're wrong. Let's see what really happens >>> x = symbols('x') >>> expr = x + 1 >>> x = 2 >>> print(expr) x + 1 Changing ``x`` to ``2`` had no effect on ``expr``. This is because ``x = 2`` changes the Python variable ``x`` to ``2``, but has no effect on the SymPy Symbol ``x``, which was what we used in creating ``expr``. When we created ``expr``, the Python variable ``x`` was a Symbol. After we created, it, we changed the Python variable ``x`` to 2. But ``expr`` remains the same. This behavior is not unique to SymPy. All Python programs work this way: if a variable is changed, expressions that were already created with that variable do not change automatically. For example >>> x = 'abc' >>> expr = x + 'def' >>> expr 'abcdef' >>> x = 'ABC' >>> expr 'abcdef' .. sidebar:: Quick Tip To change the value of a Symbol in an expression, use ``subs`` >>> x = symbols('x') >>> expr = x + 1 >>> expr.subs(x, 2) 3 In this example, if we want to know what ``expr`` is with the new value of ``x``, we need to reevaluate the code that created ``expr``, namely, ``expr = x + 1``. This can be complicated if several lines created ``expr``. One advantage of using a symbolic computation system like SymPy is that we can build a symbolic representation for ``expr``, and then substitute ``x`` with values. The correct way to do this in SymPy is to use ``subs``, which will be discussed in more detail later. >>> x = symbols('x') >>> expr = x + 1 >>> expr.subs(x, 2) 3 .. TODO: Add link to basic operations section .. _tutorial_gotchas_equals: Equals signs ============ Another very important consequence of the fact that SymPy does not extend Python syntax is that ``=`` does not represent equality in SymPy. Rather it is Python variable assignment. This is hard-coded into the Python language, and SymPy makes no attempts to change that. You may think, however, that ``==``, which is used for equality testing in Python, is used for SymPy as equality. This is not quite correct either. Let us see what happens when we use ``==``. >>> x + 1 == 4 False Instead of treating ``x + 1 == 4`` symbolically, we just got ``False``. In SymPy, ``==`` represents exact structural equality testing. This means that ``a == b`` means that we are *asking* if `a = b`. We always get a ``bool`` as the result of ``==``. There is a separate object, called ``Eq``, which can be used to create symbolic equalities >>> Eq(x + 1, 4) x + 1 == 4 There is one additional caveat about ``==`` as well. Suppose we want to know if `(x + 1)^2 = x^2 + 2x + 1`. We might try something like this: >>> (x + 1)**2 == x**2 + 2*x + 1 False We got ``False`` again. However, `(x + 1)^2` *does* equal `x^2 + 2x + 1`. What is going on here? Did we find a bug in SymPy, or is it just not powerful enough to recognize this basic algebraic fact? Recall from above that ``==`` represents *exact* structural equality testing. "Exact" here means that two expressions will compare equal with ``==`` only if they are exactly equal structurally. Here, `(x + 1)^2` and `x^2 + 2x + 1` are not the same symbolically. One is the power of an addition of two terms, and the other is the addition of three terms. It turns out that when using SymPy as a library, having ``==`` test for exact symbolic equality is far more useful than having it represent symbolic equality, or having it test for mathematical equality. However, as a new user, you will probably care more about the latter two. We have already seen an alternative to representing equalities symbolically, ``Eq``. To test if two things are equal, it is best to recall the basic fact that if `a = b`, then `a - b = 0`. Thus, the best way to check if `a = b` is to take `a - b` and simplify it, and see if it goes to 0. We will learn :ref:`later ` that the function to do this is called ``simplify``. This method is not infallible---in fact, it can be `theoretically proven `_ that it is impossible to determine if two symbolic expressions are identically equal in general---but for most common expressions, it works quite well. >>> a = (x + 1)**2 >>> b = x**2 + 2*x + 1 >>> simplify(a - b) 0 >>> c = x**2 - 2*x + 1 >>> simplify(a - c) 4*x There is also a method called ``equals`` that tests if two expressions are equal by evaluating them numerically at random points. >>> a = cos(x)**2 - sin(x)**2 >>> b = cos(2*x) >>> a.equals(b) True Two Final Notes: ``^`` and ``/`` ================================ You may have noticed that we have been using ``**`` for exponentiation instead of the standard ``^``. That's because SymPy follows Python's conventions. In Python, ``^`` represents logical exclusive or. SymPy follows this convention: >>> True ^ False True >>> True ^ True False >>> x^y Or(And(Not(x), y), And(Not(y), x)) Finally, a small technical discussion on how SymPy works is in order. When you type something like ``x + 1``, the SymPy Symbol ``x`` is added to the Python int ``1``. Python's operator rules then allow SymPy to tell Python that SymPy objects know how to be added to Python ints, and so ``1`` is automatically converted to the SymPy Integer object. This sort of operator magic happens automatically behind the scenes, and you rarely need to even know that it is happening. However, there is one exception. Whenever you combine a SymPy object and a SymPy object, or a SymPy object and a Python object, you get a SymPy object, but whenever you combine two Python objects, SymPy never comes into play, and so you get a Python object. >>> type(Integer(1) + 1) >>> type(1 + 1) <... 'int'> This is usually not a big deal. Python ints work much the same as SymPy Integers, but there is one important exception: division. In SymPy, the division of two Integers gives a Rational: >>> Integer(1)/Integer(3) 1/3 >>> type(Integer(1)/Integer(3)) But in Python ``/`` represents either integer division or floating point division, depending on whether you are in Python 2 or Python 3, and depending on whether or not you have run ``from __future__ import division``: >>> from __future__ import division >>> 1/2 #doctest: +SKIP 0.5 To avoid this, we can construct the rational object explicitly >>> Rational(1, 2) 1/2 This problem also comes up whenever we have a larger symbolic expression with ``int/int`` in it. For example: >>> x + 1/2 #doctest: +SKIP x + 0.5 This happens because Python first evaluates ``1/2`` into ``0.5``, and then that is cast into a SymPy type when it is added to ``x``. Again, we can get around this by explicitly creating a Rational: >>> x + Rational(1, 2) x + 1/2 There are several tips on avoiding this situation in the :ref:`gotchas` document. Further Reading =============== For more discussion on the topics covered in this section, see :ref:`gotchas`. sympy-0.7.4.1/doc/src/tutorial/calculus.rst0000644000175000017500000002350312253362407021013 0ustar georgeskgeorgesk========== Calculus ========== This section covers how to do basic calculus tasks such as derivatives, integrals, limits, and series expansions in SymPy. If you are not familiar with the math of any part of this section, you may safely skip it. >>> from sympy import * >>> x, y, z = symbols('x y z') >>> init_printing(use_unicode=True) .. _tutorial-derivatives: Derivatives =========== To take derivatives, use the ``diff`` function. >>> diff(cos(x), x) -sin(x) >>> diff(exp(x**2), x) ⎛ 2⎞ ⎝x ⎠ 2⋅x⋅ℯ ``diff`` can take multiple derivatives at once. To take multiple derivatives, pass the variable as many times as you wish to differentiate, or pass a number after the variable. For example, both of the following find the third derivative of `x^4`. >>> diff(x**4, x, x, x) 24⋅x >>> diff(x**4, x, 3) 24⋅x You can also take derivatives with respect to many variables at once. Just pass each derivative in order, using the same syntax as for single variable derivatives. For example, each of the following will compute `\frac{\partial^7}{\partial x\partial y^2\partial z^4} e^{x y z}`. >>> expr = exp(x*y*z) >>> diff(expr, x, y, y, z, z, z, z) 3 2 ⎛ 3 3 3 2 2 2 ⎞ x⋅y⋅z x ⋅y ⋅⎝x ⋅y ⋅z + 14⋅x ⋅y ⋅z + 52⋅x⋅y⋅z + 48⎠⋅ℯ >>> diff(expr, x, y, 2, z, 4) 3 2 ⎛ 3 3 3 2 2 2 ⎞ x⋅y⋅z x ⋅y ⋅⎝x ⋅y ⋅z + 14⋅x ⋅y ⋅z + 52⋅x⋅y⋅z + 48⎠⋅ℯ >>> diff(expr, x, y, y, z, 4) 3 2 ⎛ 3 3 3 2 2 2 ⎞ x⋅y⋅z x ⋅y ⋅⎝x ⋅y ⋅z + 14⋅x ⋅y ⋅z + 52⋅x⋅y⋅z + 48⎠⋅ℯ ``diff`` can also be called as a method. The two ways of calling ``diff`` are exactly the same, and are provided only for convenience. >>> expr.diff(x, y, y, z, 4) 3 2 ⎛ 3 3 3 2 2 2 ⎞ x⋅y⋅z x ⋅y ⋅⎝x ⋅y ⋅z + 14⋅x ⋅y ⋅z + 52⋅x⋅y⋅z + 48⎠⋅ℯ To create an unevaluated derivative, use the ``Derivative`` class. It has the same syntax as ``diff``. >>> deriv = Derivative(expr, x, y, y, z, 4) >>> deriv 7 ∂ ⎛ x⋅y⋅z⎞ ──────────⎝ℯ ⎠ 4 2 ∂z ∂y ∂x To evaluate an unevaluated derivative, use the ``doit`` method. >>> deriv.doit() 3 2 ⎛ 3 3 3 2 2 2 ⎞ x⋅y⋅z x ⋅y ⋅⎝x ⋅y ⋅z + 14⋅x ⋅y ⋅z + 52⋅x⋅y⋅z + 48⎠⋅ℯ These unevaluated objects are useful for delaying the evaluation of the derivative, or for printing purposes. They are also used when SymPy does not know how to compute the derivative of an expression (for example, if it contains an undefined function, which are described in the :ref:`Solving Differential Equations ` section). Integrals ========= To compute an integral, use the ``integrate`` function. There are two kinds of integrals, definite and indefinite. To compute an indefinite integral, that is, an antiderivative, or primitive, just pass the variable after the expression. >>> integrate(cos(x), x) sin(x) Note that SymPy does not include the constant of integration. If you want it, you can add one yourself, or rephrase your problem as a differential equation and use ``dsolve`` to solve it, which does add the constant (see :ref:`tutorial-dsolve`). .. sidebar:: Quick Tip `\infty` in SymPy is ``oo`` (that's the lowercase letter "oh" twice). This is because ``oo`` looks like `\infty`, and is easy to type. To compute a definite integral, pass the argument ``(integration_variable, lower_limit, upper_limit)``. For example, to compute .. math:: \int_0^\infty e^{-x}\,dx, we would do >>> integrate(exp(-x), (x, 0, oo)) 1 As with indefinite integrals, you can pass multiple limit tuples to perform a multiple integral. For example, to compute .. math:: \int_{-\infty}^{\infty}\int_{-\infty}^{\infty} e^{- x^{2} - y^{2}}\, dx\, dy, do >>> integrate(exp(-x**2 - y**2), (x, -oo, oo), (y, -oo, oo)) π If ``integrate`` is unable to compute an integral, it returns an unevaluated ``Integral`` object. >>> expr = integrate(x**x, x) >>> print(expr) Integral(x**x, x) >>> expr ⌠ ⎮ x ⎮ x dx ⌡ As with ``Derivative``, you can create an unevaluated integral using ``Integral``. To later evaluate this integral, call ``doit``. >>> expr = Integral(log(x)**2, x) >>> expr ⌠ ⎮ 2 ⎮ log (x) dx ⌡ >>> expr.doit() 2 x⋅log (x) - 2⋅x⋅log(x) + 2⋅x ``integrate`` uses powerful algorithms that are always improving to compute both definite and indefinite integrals, including heuristic pattern matching type algorithms, a partial implementation of the `Risch algorithm `_, and an algorithm using `Meijer G-functions `_ that is useful for computing integrals in terms of special functions, especially definite integrals. Here is a sampling of some of the power of ``integrate``. >>> integ = Integral((x**4 + x**2*exp(x) - x**2 - 2*x*exp(x) - 2*x - ... exp(x))*exp(x)/((x - 1)**2*(x + 1)**2*(exp(x) + 1)), x) >>> integ ⌠ ⎮ ⎛ 4 2 x 2 x x⎞ x ⎮ ⎝x + x ⋅ℯ - x - 2⋅x⋅ℯ - 2⋅x - ℯ ⎠⋅ℯ ⎮ ──────────────────────────────────────── dx ⎮ 2 2 ⎛ x ⎞ ⎮ (x - 1) ⋅(x + 1) ⋅⎝ℯ + 1⎠ ⌡ >>> integ.doit() x ⎛ x ⎞ ℯ log⎝ℯ + 1⎠ + ────── 2 x - 1 >>> integ = Integral(sin(x**2), x) >>> integ ⌠ ⎮ ⎛ 2⎞ ⎮ sin⎝x ⎠ dx ⌡ >>> integ.doit() ⎛ ___ ⎞ ___ ___ ⎜╲╱ 2 ⋅x⎟ 3⋅╲╱ 2 ⋅╲╱ π ⋅fresnels⎜───────⎟⋅Γ(3/4) ⎜ ___ ⎟ ⎝ ╲╱ π ⎠ ────────────────────────────────────── 8⋅Γ(7/4) >>> integ = Integral(x**y*exp(-x), (x, 0, oo)) >>> integ ∞ ⌠ ⎮ y -x ⎮ x ⋅ℯ dx ⌡ 0 >>> integ.doit() ⎧ Γ(y + 1) for -re(y) < 1 ⎪ ⎪∞ ⎪⌠ ⎨⎮ y -x ⎪⎮ x ⋅ℯ dx otherwise ⎪⌡ ⎪0 ⎩ This last example returned a ``Piecewise`` expression because the integral does not converge unless `\Re(y) > 1.` Limits ====== SymPy can compute symbolic limits with the ``limit`` function. The syntax to compute .. math:: \lim_{x\to x_0} f(x) is ``limit(f(x), x, x0)``. >>> limit(sin(x)/x, x, 0) 1 ``limit`` should be used instead of ``subs`` whenever the point of evaluation is a singularity. Even though SymPy has objects to represent `\infty`, using them for evaluation is not reliable because they do not keep track of things like rate of growth. Also, things like `\infty - \infty` and `\frac{\infty}{\infty}` return `\mathrm{nan}` (not-a-number). For example >>> expr = x**2/exp(x) >>> expr.subs(x, oo) nan >>> limit(expr, x, oo) 0 Like ``Derivative`` and ``Integral``, ``limit`` has an unevaluated counterpart, ``Limit``. To evaluate it, use ``doit``. >>> expr = Limit((cos(x) - 1)/x, x, 0) >>> expr cos(x) - 1 lim ────────── x->0 x >>> expr.doit() 0 To evaluate a limit at one side only, pass ``'+'`` or ``'-'`` as a third argument to ``limit``. For example, to compute .. math:: \lim_{x\to 0^+}\frac{1}{x}, do >>> limit(1/x, x, 0, '+') ∞ As opposed to >>> limit(1/x, x, 0, '-') -∞ Series Expansion ================ SymPy can compute asymptotic series expansions of functions around a point. To compute the expansion of `f(x)` around the point `x = x_0` terms of order `x^n`, use ``f(x).series(x, x0, n)``. ``x0`` and ``n`` can be omitted, in which case the defaults ``x0=0`` and ``n=6`` will be used. >>> expr = exp(sin(x)) >>> expr.series(x, 0, 4) 2 x ⎛ 4⎞ 1 + x + ── + O⎝x ⎠ 2 The `O\left (x^4\right )` term at the end represents the Landau order term at `x=0` (not to be confused with big O notation used in computer science, which generally represents the Landau order term at `x=\infty`). It means that all x terms with power greater than or equal to `x^4` are omitted. Order terms can be created and manipulated outside of ``series``. They automatically absorb higher order terms. >>> x + x**3 + x**6 + O(x**4) 3 ⎛ 4⎞ x + x + O⎝x ⎠ >>> x*O(1) O(x) If you do not want the order term, use the ``removeO`` method. >>> expr.series(x, 0, 4).removeO() 2 x ── + x + 1 2 Currently, ``O`` only supports orders at 0, so series expansions at points other than 0 are computed by first shifting to 0 and then shifting back. >>> exp(x - 6).series(x, 6) 2 3 4 5 x x x x ⎛ 6⎞ 1 + x + ── + ── + ── + ─── + O⎝x ⎠ 2 6 24 120 This means that if you compute the series expansion at a point other than 0, the result will be shifted to 0. You can easily shift it back with ``subs``. >>> exp(x - 6).series(x, 6).removeO().subs(x, x - 6) 5 4 3 2 (x - 6) (x - 6) (x - 6) (x - 6) x + ──────── + ──────── + ──────── + ──────── - 5 120 24 6 2 sympy-0.7.4.1/doc/src/tutorial/index.rst0000644000175000017500000000042212253362407020302 0ustar georgeskgeorgesk.. _tutorial: ================ SymPy Tutorial ================ .. toctree:: :maxdepth: 2 preliminaries.rst intro.rst printing.rst gotchas.rst basic_operations.rst simplification.rst calculus.rst solvers.rst matrices.rst manipulation.rst sympy-0.7.4.1/doc/src/tutorial/preliminaries.rst0000644000175000017500000001150412253362407022041 0ustar georgeskgeorgesk=============== Preliminaries =============== This tutorial assumes that the reader already knows the basics of the Python programming language. If you do not, the `official Python tutorial `_ is excellent. This tutorial assumes a decent mathematical background. Most examples require knowledge up to calculus level, and some require knowledge at a calculus level. Some of the advanced features require more than this. If you come across a section that uses some mathematical function you are not familiar with, you can probably skip over it, or replace it with a similar one that you are more familiar with. Or look up the function on Wikipedia and learn something new. Some important mathematical concepts that are not common knowledge will be introduced as necessary. Installation ============ .. sidebar:: Quick Tip You do not need to install SymPy to try it. You can use the online shell at http://live.sympy.org, or the shell at the bottom right of this documentation page. You will need to install SymPy first. See the :ref:`installation guide `. Alternately, you can just use the SymPy Live Sphinx extension to run the code blocks in the browser. For example, click on the green "Run code block in SymPy Live" button below >>> from sympy import * >>> x = symbols('x') >>> a = Integral(cos(x)*exp(x), x) >>> Eq(a, a.doit()) Integral(exp(x)*cos(x), x) == exp(x)*sin(x)/2 + exp(x)*cos(x)/2 The SymPy Live shell in the bottom corner will pop up and evaluate the code block. You can also click any individual line to evaluate it one at a time. The SymPy Live shell is a fully interactive Python shell. You can type any expression in the input box to evaluate it. Feel free to use it throughout the tutorial to experiment. To show or hide the SymPy Live shell at any time, just click the green button on the bottom right of the screen. By default, the SymPy Live shell uses `\LaTeX` for output. If you want the output to look more like the output in the documentation, change the output format to ``Str`` or ``Unicode``. If you wish to modify an example before evaluating it, change the evaluation mode to "copy" in the SymPy Live settings. This will cause clicking on an example to copy the example to the SymPy Live shell, but not evaluate it, allowing you to change it before execution. You can also use the up/down arrow keys on your keyboard in the input box to move through the shell history. The SymPy Live shell is also available at http://live.sympy.org, with extra features, like a mobile phone enhanced version and saved history. Exercises ========= This tutorial was the basis for a tutorial given at the 2013 SciPy conference in Austin, TX. The website for that tutorial is `here `_. It has links to videos, materials, and IPython notebook exercises. The IPython notebook exercises in particular are recommended to anyone going through this tutorial. About This Tutorial =================== This tutorial aims to give an introduction to SymPy for someone who has not used the library before. Many features of SymPy will be introduced in this tutorial, but they will not be exhaustive. In fact, virtually every functionality shown in this tutorial will have more options or capabilities than what will be shown. The rest of the SymPy documentation serves as API documentation, which extensively lists every feature and option of each function. These are the goals of this tutorial: .. NB: This is mainly here for you, the person who is editing and adding to this tutorial. Try to keep these principles in mind. - To give a guide, suitable for someone who has never used SymPy (but who has used Python and knows the necessary mathematics). - To be written in a narrative format, which is both easy and fun to follow. It should read like a book. - To give insightful examples and exercises, to help the reader learn and to make it entertaining to work through. - To introduce concepts in a logical order. .. In other words, don't try to get ahead of yourself. - To use good practices and idioms, and avoid antipatterns. Functions or methodologies that tend to lead to antipatterns are avoided. Features that are only useful to advanced users are not shown. - To be consistent. If there are multiple ways to do it, only the best way is shown. .. For example, there are at least five different ways to create Symbols. ``symbols`` is the only one that is general and doesn't lead to antipatterns, so it is the only one used. - To avoid unnecessary duplication, it is assumed that previous sections of the tutorial have already been read. Feedback on this tutorial, or on SymPy in general is always welcome. Just write to our `mailing list `_. sympy-0.7.4.1/doc/src/tutorial/solvers.rst0000644000175000017500000001220512253362407020672 0ustar georgeskgeorgesk========= Solvers ========= >>> from sympy import * >>> x, y, z = symbols('x y z') >>> init_printing(use_unicode=True) A Note about Equations ====================== Recall from the :ref:`gotchas ` section of this tutorial that symbolic equations in SymPy are not represented by ``=`` or ``==``, but by ``Eq``. >>> Eq(x, y) x = y However, there is an even easier way. In SymPy, any expression is not in an ``Eq`` is automatically assumed to equal 0 by the solving functions. Since `a = b` if and only if `a - b = 0`, this means that instead of using ``x == y``, you can just use ``x - y``. For example >>> solve(Eq(x**2, 1), x) [-1, 1] >>> solve(Eq(x**2 - 1, 0), x) [-1, 1] >>> solve(x**2 - 1, x) [-1, 1] This is particularly useful if the equation you wish to solve is already equal to 0. Instead of typing ``solve(Eq(expr, 0), x)``, you can just use ``solve(expr, x)``. Solving Equations Algebraically =============================== The main function for solving algebraic equations, as we saw above, is ``solve``. The syntax is ``solve(equations, variables)``, where, as we saw above, ``equations`` may be in the form of ``Eq`` instances or expressions that are assumed to be equal to zero. .. TODO: This is a mess, because solve() has such a complicated interface. When solving a single equation, the output of ``solve`` is a list of the solutions. >>> solve(x**2 - x, x) [0, 1] If no solutions are found, an empty list is returned, or ``NotImplementedError`` is raised. >>> solve(exp(x), x) [] .. note:: If ``solve`` returns ``[]`` or raises ``NotImplementedError``, it doesn't mean that the equation has no solutions. It just means that it couldn't find any. Often this means that the solutions cannot be represented symbolically. For example, the equation `x = \cos(x)` has a solution, but it cannot be represented symbolically using standard functions. >>> solve(x - cos(x), x) Traceback (most recent call last): ... NotImplementedError: multiple generators [x, exp(I*x)] No algorithms are implemented to solve equation exp(I*x) In fact, ``solve`` makes *no guarantees whatsoever* about the completeness of the solutions it finds. Much of ``solve`` is heuristics, which may find some solutions to an equation or system of equations, but not all of them. ``solve`` can also solve systems of equations. Pass a list of equations and a list of variables to solve for. >>> solve([x - y + 2, x + y - 3], [x, y]) {x: 1/2, y: 5/2} >>> solve([x*y - 7, x + y - 6], [x, y]) ⎡⎛ ___ ___ ⎞ ⎛ ___ ___ ⎞⎤ ⎣⎝- ╲╱ 2 + 3, ╲╱ 2 + 3⎠, ⎝╲╱ 2 + 3, - ╲╱ 2 + 3⎠⎦ .. note:: The type of the output of ``solve`` when solving systems of equations varies depending on the type of the input. If you want a consistent interface, pass ``dict=True``. >>> solve([x - y + 2, x + y - 3], [x, y], dict=True) [{x: 1/2, y: 5/2}] >>> solve([x*y - 7, x + y - 6], [x, y], dict=True) ⎡⎧ ___ ___ ⎫ ⎧ ___ ___ ⎫⎤ ⎢⎨x: - ╲╱ 2 + 3, y: ╲╱ 2 + 3⎬, ⎨x: ╲╱ 2 + 3, y: - ╲╱ 2 + 3⎬⎥ ⎣⎩ ⎭ ⎩ ⎭⎦ .. _tutorial-roots: ``solve`` reports each solution only once. To get the solutions of a polynomial including multiplicity use ``roots``. >>> solve(x**3 - 6*x**2 + 9*x, x) [0, 3] >>> roots(x**3 - 6*x**2 + 9*x, x) {0: 1, 3: 2} The output ``{0: 1, 3: 2}`` of ``roots`` means that ``0`` is a root of multiplicity 1 and ``3`` is a root of multiplicity 2. .. _tutorial-dsolve: Solving Differential Equations ============================== To solve differential equations, use ``dsolve``. First, create an undefined function by passing ``cls=Function`` to the ``symbols`` function. >>> f, g = symbols('f g', cls=Function) ``f`` and ``g`` are now undefined functions. We can call ``f(x)``, and it will represent an unknown function. >>> f(x) f(x) Derivatives of ``f(x)`` are unevaluated. >>> f(x).diff(x) d ──(f(x)) dx (see the :ref:`Derivatives ` section for more on derivatives). To represent the differential equation `f''(x) - 2f'(x) + f(x) = \sin(x)`, we would thus use >>> diffeq = Eq(f(x).diff(x, x) - 2*f(x).diff(x) + f(x), sin(x)) >>> diffeq 2 d d f(x) - 2⋅──(f(x)) + ───(f(x)) = sin(x) dx 2 dx To solve the ODE, pass it and the function to solve for to ``dsolve``. >>> dsolve(diffeq, f(x)) x cos(x) f(x) = (C₁ + C₂⋅x)⋅ℯ + ────── 2 ``dsolve`` returns an instance of ``Eq``. This is because in general, solutions to differential equations cannot be solved explicitly for the function. >>> dsolve(f(x).diff(x)*(1 - sin(f(x))), f(x)) f(x) + cos(f(x)) = C₁ The arbitrary constants in the solutions from dsolve are symbols of the form ``C1``, ``C2``, ``C3``, and so on. sympy-0.7.4.1/doc/src/tutorial/manipulation.rst0000644000175000017500000004453012253362407021703 0ustar georgeskgeorgesk.. _tutorial-manipulation: ================================== Advanced Expression Manipulation ================================== In this section, we discuss some ways that we can perform advanced manipulation of expressions. Understanding Expression Trees ============================== .. sidebar :: Quick Tip To play with the ``srepr`` form of expressions in the SymPy Live shell, change the output format to ``Repr`` in the settings. Before we can do this, we need to understand how expressions are represented in SymPy. A mathematical expression is represented as a tree. Let us take the expression `x^2 + xy`, i.e., ``x**2 + x*y``. We can see what this expression looks like internally by using ``srepr`` >>> from sympy import * >>> x, y, z = symbols('x y z') >>> expr = x**2 + x*y >>> srepr(expr) "Add(Pow(Symbol('x'), Integer(2)), Mul(Symbol('x'), Symbol('y')))" The easiest way to tear this apart is to look at a diagram of the expression tree: .. This comes from dotprint(x**2 + x*y, labelfunc=srepr) .. graphviz:: digraph{ # Graph style "rankdir"="TD" ######### # Nodes # ######### "Symbol(x)_(0, 0)" ["color"="black", "label"="Symbol('x')", "shape"="ellipse"]; "Integer(2)_(1, 1)" ["color"="black", "label"="Integer(2)", "shape"="ellipse"]; "Symbol(y)_(0, 1)" ["color"="black", "label"="Symbol('y')", "shape"="ellipse"]; "Symbol(x)_(1, 0)" ["color"="black", "label"="Symbol('x')", "shape"="ellipse"]; "Mul(Symbol(x), Symbol(y))_(0,)" ["color"="black", "label"="Mul", "shape"="ellipse"]; "Pow(Symbol(x), Integer(2))_(1,)" ["color"="black", "label"="Pow", "shape"="ellipse"]; "Add(Mul(Symbol(x), Symbol(y)), Pow(Symbol(x), Integer(2)))_()" ["color"="black", "label"="Add", "shape"="ellipse"]; ######### # Edges # ######### "Mul(Symbol(x), Symbol(y))_(0,)" -> "Symbol(x)_(0, 0)"; "Mul(Symbol(x), Symbol(y))_(0,)" -> "Symbol(y)_(0, 1)"; "Pow(Symbol(x), Integer(2))_(1,)" -> "Symbol(x)_(1, 0)"; "Pow(Symbol(x), Integer(2))_(1,)" -> "Integer(2)_(1, 1)"; "Add(Mul(Symbol(x), Symbol(y)), Pow(Symbol(x), Integer(2)))_()" -> "Mul(Symbol(x), Symbol(y))_(0,)"; "Add(Mul(Symbol(x), Symbol(y)), Pow(Symbol(x), Integer(2)))_()" -> "Pow(Symbol(x), Integer(2))_(1,)"; } .. note:: The above diagram was made using `Graphviz `_ and the :py:meth:`dotprint ` function. First, let's look at the leaves of this tree. Symbols are instances of the class Symbol. While we have been doing >>> x = symbols('x') we could have also done >>> x = Symbol('x') Either way, we get a Symbol with the name "x" [#symbols-fn]_. For the number in the expression, 2, we got ``Integer(2)``. ``Integer`` is the SymPy class for integers. It is similar to the Python built-in type ``int``, except that ``Integer`` plays nicely with other SymPy types. When we write ``x**2``, this creates a ``Pow`` object. ``Pow`` is short for "power". >>> srepr(x**2) "Pow(Symbol('x'), Integer(2))" We could have created the same object by calling ``Pow(x, 2)`` >>> Pow(x, 2) x**2 Note that in the ``srepr`` output, we see ``Integer(2)``, the SymPy version of integers, even though technically, we input ``2``, a Python int. In general, whenever you combine a SymPy object with a non-SymPy object via some function or operation, the non-SymPy object will be converted into a SymPy object. The function that does this is ``sympify`` [#sympify-fn]_. >>> type(2) <... 'int'> >>> type(sympify(2)) We have seen that ``x**2`` is represented as ``Pow(x, 2)``. What about ``x*y``? As we might expect, this is the multiplication of ``x`` and ``y``. The SymPy class for multiplication is ``Mul``. >>> srepr(x*y) "Mul(Symbol('x'), Symbol('y'))" Thus, we could have created the same object by writing ``Mul(x, y)``. >>> Mul(x, y) x*y Now we get to our final expression, ``x**2 + x*y``. This is the addition of our last two objects, ``Pow(x, 2)``, and ``Mul(x, y)``. The SymPy class for addition is ``Add``, so, as you might expect, to create this object, we use ``Add(Pow(x, 2), Mul(x, y)``. >>> Add(Pow(x, 2), Mul(x, y)) x**2 + x*y SymPy expression trees can have many branches, and can be quite deep or quite broad. Here is a more complicated example >>> expr = sin(x*y)/2 - x**2 + 1/y >>> srepr(expr) "Add(Mul(Integer(-1), Pow(Symbol('x'), Integer(2))), Mul(Rational(1, 2), sin(Mul(Symbol('x'), Symbol('y')))), Pow(Symbol('y'), Integer(-1)))" Here is a diagram .. dotprint(sin(x*y)/2 - x**2 + 1/y, labelfunc=srepr) .. graphviz:: digraph{ # Graph style "rankdir"="TD" ######### # Nodes # ######### "Half()_(0, 0)" ["color"="black", "label"="Rational(1, 2)", "shape"="ellipse"]; "Symbol(y)_(2, 0)" ["color"="black", "label"="Symbol('y')", "shape"="ellipse"]; "Symbol(x)_(1, 1, 0)" ["color"="black", "label"="Symbol('x')", "shape"="ellipse"]; "Integer(2)_(1, 1, 1)" ["color"="black", "label"="Integer(2)", "shape"="ellipse"]; "NegativeOne()_(2, 1)" ["color"="black", "label"="Integer(-1)", "shape"="ellipse"]; "NegativeOne()_(1, 0)" ["color"="black", "label"="Integer(-1)", "shape"="ellipse"]; "Symbol(y)_(0, 1, 0, 1)" ["color"="black", "label"="Symbol('y')", "shape"="ellipse"]; "Symbol(x)_(0, 1, 0, 0)" ["color"="black", "label"="Symbol('x')", "shape"="ellipse"]; "Pow(Symbol(x), Integer(2))_(1, 1)" ["color"="black", "label"="Pow", "shape"="ellipse"]; "Pow(Symbol(y), NegativeOne())_(2,)" ["color"="black", "label"="Pow", "shape"="ellipse"]; "Mul(Symbol(x), Symbol(y))_(0, 1, 0)" ["color"="black", "label"="Mul", "shape"="ellipse"]; "sin(Mul(Symbol(x), Symbol(y)))_(0, 1)" ["color"="black", "label"="sin", "shape"="ellipse"]; "Mul(Half(), sin(Mul(Symbol(x), Symbol(y))))_(0,)" ["color"="black", "label"="Mul", "shape"="ellipse"]; "Mul(NegativeOne(), Pow(Symbol(x), Integer(2)))_(1,)" ["color"="black", "label"="Mul", "shape"="ellipse"]; "Add(Mul(Half(), sin(Mul(Symbol(x), Symbol(y)))), Mul(NegativeOne(), Pow(Symbol(x), Integer(2))), Pow(Symbol(y), NegativeOne()))_()" ["color"="black", "label"="Add", "shape"="ellipse"]; ######### # Edges # ######### "Pow(Symbol(y), NegativeOne())_(2,)" -> "Symbol(y)_(2, 0)"; "Pow(Symbol(x), Integer(2))_(1, 1)" -> "Symbol(x)_(1, 1, 0)"; "Pow(Symbol(x), Integer(2))_(1, 1)" -> "Integer(2)_(1, 1, 1)"; "Pow(Symbol(y), NegativeOne())_(2,)" -> "NegativeOne()_(2, 1)"; "Mul(Symbol(x), Symbol(y))_(0, 1, 0)" -> "Symbol(x)_(0, 1, 0, 0)"; "Mul(Symbol(x), Symbol(y))_(0, 1, 0)" -> "Symbol(y)_(0, 1, 0, 1)"; "Mul(Half(), sin(Mul(Symbol(x), Symbol(y))))_(0,)" -> "Half()_(0, 0)"; "Mul(NegativeOne(), Pow(Symbol(x), Integer(2)))_(1,)" -> "NegativeOne()_(1, 0)"; "sin(Mul(Symbol(x), Symbol(y)))_(0, 1)" -> "Mul(Symbol(x), Symbol(y))_(0, 1, 0)"; "Mul(NegativeOne(), Pow(Symbol(x), Integer(2)))_(1,)" -> "Pow(Symbol(x), Integer(2))_(1, 1)"; "Mul(Half(), sin(Mul(Symbol(x), Symbol(y))))_(0,)" -> "sin(Mul(Symbol(x), Symbol(y)))_(0, 1)"; "Add(Mul(Half(), sin(Mul(Symbol(x), Symbol(y)))), Mul(NegativeOne(), Pow(Symbol(x), Integer(2))), Pow(Symbol(y), NegativeOne()))_()" -> "Pow(Symbol(y), NegativeOne())_(2,)"; "Add(Mul(Half(), sin(Mul(Symbol(x), Symbol(y)))), Mul(NegativeOne(), Pow(Symbol(x), Integer(2))), Pow(Symbol(y), NegativeOne()))_()" -> "Mul(Half(), sin(Mul(Symbol(x), Symbol(y))))_(0,)"; "Add(Mul(Half(), sin(Mul(Symbol(x), Symbol(y)))), Mul(NegativeOne(), Pow(Symbol(x), Integer(2))), Pow(Symbol(y), NegativeOne()))_()" -> "Mul(NegativeOne(), Pow(Symbol(x), Integer(2)))_(1,)"; } This expression reveals some interesting things about SymPy expression trees. Let's go through them one by one. Let's first look at the term ``x**2``. As we expected, we see ``Pow(x, 2)``. One level up, we see we have ``Mul(-1, Pow(x, 2))``. There is no subtraction class in SymPy. ``x - y`` is represented as ``x + -y``, or, more completely, ``x + -1*y``, i.e., ``Add(x, Mul(-1, y))``. >>> expr = x - y >>> srepr(x - y) "Add(Symbol('x'), Mul(Integer(-1), Symbol('y')))" .. dotprint(x - y, labelfunc=srepr) .. graphviz:: digraph{ # Graph style "rankdir"="TD" ######### # Nodes # ######### "Symbol(x)_(1,)" ["color"="black", "label"="Symbol('x')", "shape"="ellipse"]; "Symbol(y)_(0, 1)" ["color"="black", "label"="Symbol('y')", "shape"="ellipse"]; "NegativeOne()_(0, 0)" ["color"="black", "label"="Integer(-1)", "shape"="ellipse"]; "Mul(NegativeOne(), Symbol(y))_(0,)" ["color"="black", "label"="Mul", "shape"="ellipse"]; "Add(Mul(NegativeOne(), Symbol(y)), Symbol(x))_()" ["color"="black", "label"="Add", "shape"="ellipse"]; ######### # Edges # ######### "Mul(NegativeOne(), Symbol(y))_(0,)" -> "Symbol(y)_(0, 1)"; "Mul(NegativeOne(), Symbol(y))_(0,)" -> "NegativeOne()_(0, 0)"; "Add(Mul(NegativeOne(), Symbol(y)), Symbol(x))_()" -> "Symbol(x)_(1,)"; "Add(Mul(NegativeOne(), Symbol(y)), Symbol(x))_()" -> "Mul(NegativeOne(), Symbol(y))_(0,)"; } Next, look at ``1/y``. We might expect to see something like ``Div(1, y)``, but similar to subtraction, there is no class in SymPy for division. Rather, division is represented by a power of -1. Hence, we have ``Pow(y, -1)``. What if we had divided something other than 1 by ``y``, like ``x/y``? Let's see. >>> expr = x/y >>> srepr(expr) "Mul(Symbol('x'), Pow(Symbol('y'), Integer(-1)))" .. dotprint(x/y, labelfunc=srepr) .. graphviz:: digraph{ # Graph style "rankdir"="TD" ######### # Nodes # ######### "Symbol(x)_(0,)" ["color"="black", "label"="Symbol('x')", "shape"="ellipse"]; "Symbol(y)_(1, 0)" ["color"="black", "label"="Symbol('y')", "shape"="ellipse"]; "NegativeOne()_(1, 1)" ["color"="black", "label"="Integer(-1)", "shape"="ellipse"]; "Pow(Symbol(y), NegativeOne())_(1,)" ["color"="black", "label"="Pow", "shape"="ellipse"]; "Mul(Symbol(x), Pow(Symbol(y), NegativeOne()))_()" ["color"="black", "label"="Mul", "shape"="ellipse"]; ######### # Edges # ######### "Pow(Symbol(y), NegativeOne())_(1,)" -> "Symbol(y)_(1, 0)"; "Pow(Symbol(y), NegativeOne())_(1,)" -> "NegativeOne()_(1, 1)"; "Mul(Symbol(x), Pow(Symbol(y), NegativeOne()))_()" -> "Symbol(x)_(0,)"; "Mul(Symbol(x), Pow(Symbol(y), NegativeOne()))_()" -> "Pow(Symbol(y), NegativeOne())_(1,)"; } We see that ``x/y`` is represented as ``x*y**-1``, i.e., ``Mul(x, Pow(y, -1))``. Finally, let's look at the ``sin(x*y)/2`` term. Following the pattern of the previous example, we might expect to see ``Mul(sin(x*y), Pow(Integer(2), -1))``. But instead, we have ``Mul(Rational(1, 2), sin(x*y))``. Rational numbers are always combined into a single term in a multiplication, so that when we divide by 2, it is represented as multiplying by 1/2. Finally, one last note. You may have noticed that the order we entered our expression and the order that it came out from ``srepr`` or in the graph were different. You may have also noticed this phenonemon earlier in the tutorial. For example >>> 1 + x x + 1 This because in SymPy, the arguments of the commutative operations ``Add`` and ``Mul`` are stored in an arbitrary (but consistent!) order, which is independent of the order inputted (if you're worried about noncommutative multiplication, don't be. In SymPy, you can create noncommutative Symbols using ``Symbol('A', commutative=False)``, and the order of multiplication for noncommutative Symbols is kept the same as the input). Furthermore, as we shall see in the next section, the printing order and the order in which things are stored internally need not be the same either. .. sidebar:: Quick Tip The way an expression is represented internally and the way it is printed are often not the same. In general, an important thing to keep in mind when working with SymPy expression trees is this: the internal representation of an expression and the way it is printed need not be the same. The same is true for the input form. If some expression manipulation algorithm is not working in the way you expected it to, chances are, the internal representation of the object is different from what you thought it was. Recursing through an Expression Tree ==================================== Now that you know how expression trees work in SymPy, let's look at how to dig our way through an expression tree. Every object in SymPy has two very important attributes, ``func``, and ``args``. func ---- ``func`` is the head of the object. For example, ``(x*y).func`` is ``Mul``. Usually it is the same as the class of the object (though there are exceptions to this rule). Two notes about ``func``. First, the class of an object need not be the same as the one used to create it. For example >>> expr = Add(x, x) >>> expr.func We created ``Add(x, x)``, so we might expect ``expr.func`` to be ``Add``, but instead we got ``Mul``. Why is that? Let's take a closer look at ``expr``. >>> expr 2*x ``Add(x, x)``, i.e., ``x + x``, was automatically converted into ``Mul(2, x)``, i.e., ``2*x``, which is a ``Mul``. SymPy classes make heavy use of the ``__new__`` class constructor, which, unlike ``__init__``, allows a different class to be returned from the constructor. Second, some classes are special-cased, usually for efficiency reasons [#singleton-fn]_. >>> Integer(2).func >>> Integer(0).func >>> Integer(-1).func For the most part, these issues will not bother us. The special classes ``Zero``, ``One``, ``NegativeOne``, and so on are subclasses of ``Integer``, so as long as you use ``isinstance``, it will not be an issue. args ---- ``args`` are the top-level arguments of the object. ``(x*y).args`` would be ``(x, y)``. Let's look at some examples >>> expr = 3*y**2*x >>> expr.func >>> expr.args (3, x, y**2) From this, we can see that ``expr == Mul(3, y**2, x)``. In fact, we can see that we can completely reconstruct ``expr`` from its ``func`` and its ``args``. >>> expr.func(*expr.args) 3*x*y**2 >>> expr == expr.func(*expr.args) True Note that although we entered ``3*y**2*x``, the ``args`` are ``(3, x, y**2)``. In a ``Mul``, the Rational coefficient will come first in the ``args``, but other than that, the order of everything else follows no special pattern. To be sure, though, there is an order. >>> expr = y**2*3*x >>> expr.args (3, x, y**2) Mul's ``args`` are sorted, so that the same ``Mul`` will have the same ``args``. But the sorting is based on some criteria designed to make the sorting unique and efficient that has no mathematical significance. The ``srepr`` form of our ``expr`` is ``Mul(3, x, Pow(y, 2))``. What if we want to get at the ``args`` of ``Pow(y, 2)``. Notice that the ``y**2`` is in the third slot of ``expr.args``, i.e., ``expr.args[2]``. >>> expr.args[2] y**2 So to get the ``args`` of this, we call ``expr.args[2].args``. >>> expr.args[2].args (y, 2) Now what if we try to go deeper. What are the args of ``y``. Or ``2``. Let's see. >>> y.args () >>> Integer(2).args () They both have empty ``args``. In SymPy, empty ``args`` signal that we have hit a leaf of the expression tree. So there are two possibilities for a SymPy expression. Either it has empty ``args``, in which case it is a leaf node in any expression tree, or it has ``args``, in which case, it is a branch node of any expression tree. When it has ``args``, it can be completely rebuilt from its ``func`` and its ``args``. This is expressed in the key invariant. .. topic:: Key Invariant Every well-formed SymPy expression must either have empty ``args`` or satisfy ``expr == expr.func(*expr.args)``. (Recall that in Python if ``a`` is a tuple, then ``f(*a)`` means to call ``f`` with arguments from the elements of ``a``, e.g., ``f(*(1, 2, 3))`` is the same as ``f(1, 2, 3)``.) This key invariant allows us to write simple algorithms that walk expression trees, change them, and rebuild them into new expressions. Walking the Tree ---------------- With this knowledge, let's look at how we can recurse through an expression tree. The nested nature of ``args`` is a perfect fit for recursive functions. The base case will be empty ``args``. Let's write a simple function that goes through an expression and prints all the ``args`` at each level. >>> def pre(expr): ... print(expr) ... for arg in expr.args: ... pre(arg) See how nice it is that ``()`` signals leaves in the expression tree. We don't even have to write a base case for our recursion; it is handled automatically by the for loop. Let's test our function. >>> expr = x*y + 1 >>> pre(expr) x*y + 1 1 x*y x y Can you guess why we called our function ``pre``? We just wrote a pre-order traversal function for our expression tree. See if you can write a post-order traversal function. Such traversals are so common in SymPy that the generator functions ``preorder_traversal`` and ``postorder_traversal`` are provided to make such traversals easy. We could have also written our algorithm as >>> for arg in preorder_traversal(expr): ... print(arg) x*y + 1 1 x*y x y .. rubric:: Footnotes .. [#symbols-fn] We have been using ``symbols`` instead of ``Symbol`` because it automatically splits apart strings into multiple ``Symbol``\ s. ``symbols('x y z')`` returns a tuple of three ``Symbol``\ s. ``Symbol('x y z')`` returns a single ``Symbol`` called ``x y z``. .. [#sympify-fn] Technically, it is an internal function called ``_sympify``, which differs from ``sympify`` in that it does not convert strings. ``x + '2'`` is not allowed. .. [#singleton-fn] Classes like ``One`` and ``Zero`` are singletonized, meaning that only one object is ever created, no matter how many times the class is called. This is done for space efficiency, as these classes are very common. For example, ``Zero`` might occur very often in a sparse matrix represented densely. As we have seen, ``NegativeOne`` occurs any time we have ``-x`` or ``1/x``. It is also done for speed efficiency because singletonized objects can be compared by ``is``. The unique objects for each singletonized class can be accessed from the ``S`` object. sympy-0.7.4.1/doc/src/tutorial/intro.rst0000644000175000017500000002106512253362407020334 0ustar georgeskgeorgesk============== Introduction ============== What is Symbolic Computation? ============================= Symbolic computation deals with the computation of mathematical objects symbolically. This means that the mathematical objects are represented exactly, not approximately, and mathematical expressions with unevaluated variables are left in symbolic form. Let's take an example. Say we wanted to use the built-in Python functions to compute square roots. We might do something like this >>> import math >>> math.sqrt(9) 3.0 9 is a perfect square, so we got the exact answer, 3. But suppose we computed the square root of a number that isn't a perfect square >>> math.sqrt(8) 2.82842712475 Here we got an approximate result. 2.82842712475 is not the exact square root of 8 (indeed, the actual square root of 8 cannot be represented by a finite decimal, since it is an irrational number). If all we cared about was the decimal form of the square root of 8, we would be done. But suppose we want to go further. Recall that `\sqrt{8} = \sqrt{4\cdot 2} = 2\sqrt{2}`. We would have a hard time deducing this from the above result. This is where symbolic computation comes in. With a symbolic computation system like SymPy, square roots of numbers that are not perfect squares are left unevaluated by default >>> import sympy >>> sympy.sqrt(3) sqrt(3) Furthermore---and this is where we start to see the real power of symbolic computation---symbolic results can be symbolically simplified. >>> sympy.sqrt(8) 2*sqrt(2) A More Interesting Example ========================== The above example starts to show how we can manipulate irrational numbers exactly using SymPy. But it is much more powerful than that. Symbolic computation systems (which by the way, are also often called computer algebra systems, or just CASs) such as SymPy are capable of computing symbolic expressions with variables. As we will see later, in SymPy, variables are defined using ``symbols``. Unlike many symbolic manipulation systems, variables in SymPy must be defined before they are used (the reason for this will be discussed in the :ref:`next section `). Let us define a symbolic expression, representing the mathematical expression `x + 2y`. >>> from sympy import symbols >>> x, y = symbols('x y') >>> expr = x + 2*y >>> expr x + 2*y Note that we wrote ``x + 2*y`` just as we would if ``x`` and ``y`` were ordinary Python variables. But in this case, instead of evaluating to something, the expression remains as just ``x + 2*y``. Now let us play around with it: >>> expr + 1 x + 2*y + 1 >>> expr - x 2*y Notice something in the above example. When we typed ``expr - x``, we did not get ``x + 2*y - x``, but rather just ``2*y``. The ``x`` and the ``-x`` automatically canceled one another. This is similar to how ``sqrt(8)`` automatically turned into ``2*sqrt(2)`` above. This isn't always the case in SymPy, however: >>> x*expr x*(x + 2*y) Here, we might have expected `x(x + 2y)` to transform into `x^2 + 2xy`, but instead we see that the expression was left alone. This is a common theme in SymPy. Aside from obvious simplifications like `x - x = 0` and `\sqrt{8} = 2\sqrt{2}`, most simplifications are not performed automatically. This is because we might prefer the factored form `x(x + 2y)`, or we might prefer the expanded form `x^2 + 2xy`. Both forms are useful in different circumstances. In SymPy, there are functions to go from one form to the other >>> from sympy import expand, factor >>> expanded_expr = expand(x*expr) >>> expanded_expr x**2 + 2*x*y >>> factor(expanded_expr) x*(x + 2*y) The Power of Symbolic Computation ================================= The real power of a symbolic computation system such as SymPy is the ability to do all sorts of computations symbolically. SymPy can compute derivatives, integrals, and limits, solve equations, work with matrices, and much, much more, and do it all symbolically. It includes modules for plotting, printing (like 2D pretty printed output of math formulas, or `\LaTeX`), code generation, physics, statistics, combinatorics, number theory, geometry, logic, and more. Here is a small sampling of the sort of symbolic power SymPy is capable of, to whet your appetite. >>> from sympy import * >>> x, t, z, nu = symbols('x t z nu') - This will make all further example pretty print with unicode characters. >>> init_printing(use_unicode=True) - Take the derivative of `\sin{(x)}e^x`. >>> diff(sin(x)*exp(x), x) x x ℯ ⋅sin(x) + ℯ ⋅cos(x) - Compute `\int(e^x\sin{(x)} + e^x\cos{(x)})\,dx`. >>> integrate(exp(x)*sin(x) + exp(x)*cos(x), x) x ℯ ⋅sin(x) - Compute `\int_{-\infty}^\infty \sin{(x^2)}\,dx`. >>> integrate(sin(x**2), (x, -oo, oo)) ___ ___ ╲╱ 2 ⋅╲╱ π ─────────── 2 - Find :math:`\lim_{x\to 0}\frac{\sin{(x)}}{x}`. >>> limit(sin(x)/x, x, 0) 1 - Solve `x^2 - 2 = 0`. >>> solve(x**2 - 2, x) ⎡ ___ ___⎤ ⎣-╲╱ 2 , ╲╱ 2 ⎦ - Solve the differential equation `y'' - y = e^t`. >>> y = Function('y') >>> dsolve(Eq(y(t).diff(t, t) - y(t), exp(t)), y(t)) -t ⎛ t⎞ t y(t) = C₂⋅ℯ + ⎜C₁ + ─⎟⋅ℯ ⎝ 2⎠ - Find the eigenvalues of `\left[\begin{smallmatrix}1 & 2\\2 & 2\end{smallmatrix}\right]`. >>> Matrix([[1, 2], [2, 2]]).eigenvals() ⎧ ____ ____ ⎫ ⎪3 ╲╱ 17 ╲╱ 17 3 ⎪ ⎨─ + ──────: 1, - ────── + ─: 1⎬ ⎪2 2 2 2 ⎪ ⎩ ⎭ - Rewrite the Bessel function `J_{\nu}\left(z\right)` in terms of the spherical Bessel function `j_\nu(z)`. >>> besselj(nu, z).rewrite(jn) ___ ___ ╲╱ 2 ⋅╲╱ z ⋅jn(ν - 1/2, z) ────────────────────────── ___ ╲╱ π - Print `\int_{0}^{\pi} \cos^{2}{\left (x \right )}\, dx` using `\LaTeX`. >>> latex(Integral(cos(x)**2, (x, 0, pi))) \int_{0}^{\pi} \cos^{2}{\left (x \right )}\, dx Why SymPy? ========== There are many computer algebra systems out there. `This `_ Wikipedia article lists many of them. What makes SymPy a better choice than the alternatives? First off, SymPy is completely free. It is open source, and licensed under the liberal BSD license, so you can even modify the source code and sell if you want to. This contrasts with popular commercial systems like Maple or Mathematica that cost hundreds of dollars in licenses. Second, SymPy uses Python. Most computer algebra systems invent their own language. Not SymPy. SymPy is written entirely in Python, and is executed entirely in Python. This means that if you already know Python, it is much easier to get started with SymPy, because you already know the syntax (and if you don't know Python, it is really easy to learn). Plus, we already know that Python is a well-designed, battle-tested language. The SymPy developers are confident in their abilities in writing mathematical software, but inventing a whole new programming language is a completely different thing. By reusing an existing language, we are able to focus on those things that matter, the mathematics. Another computer algebra system, Sage also uses Python as its language. But Sage is large, with a download of over a gigabyte. An advantage of SymPy is that it is lightweight. In addition to being relatively small, it has no dependencies other than Python, so it can be used almost anywhere easily. Furthermore, the goals of Sage and the goals of SymPy are different. Sage aims to be a full featured system for mathematics, and aims to do so by compiling all the major open source mathematical systems together into one. When you call some function in Sage, such as ``integrate``, it calls out to one of the open source packages that it includes. In fact, SymPy is included in Sage. SymPy on the other hand aims to be an independent system, with all the features implemented in SymPy itself. A final important feature of SymPy is that it can be used as a library. Many computer algebra systems focus on being usable in interactive environments, but if you wish to automate or extend them, it is difficult to do. With SymPy, you can just as easily use it in an interactive Python environment or import it in your own Python application. SymPy also provides APIs to make it easy to extend it with your own custom functions. sympy-0.7.4.1/doc/src/tutorial/matrices.rst0000644000175000017500000002437412253362407021016 0ustar georgeskgeorgesk========== Matrices ========== >>> from sympy import * >>> init_printing(use_unicode=True) To make a matrix in SymPy, use the ``Matrix`` object. A matrix is constructed by providing a list of row vectors that make up the matrix. For example, to construct the matrix .. math:: \left[\begin{array}{cc}1 & -1\\3 & 4\\0 & 2\end{array}\right] use >>> Matrix([[1, -1], [3, 4], [0, 2]]) ⎡1 -1⎤ ⎢ ⎥ ⎢3 4 ⎥ ⎢ ⎥ ⎣0 2 ⎦ To make it easy to make column vectors, a list of elements is considered to be a column vector. >>> Matrix([1, 2, 3]) ⎡1⎤ ⎢ ⎥ ⎢2⎥ ⎢ ⎥ ⎣3⎦ Matrices are manipulated just like any other object in SymPy or Python. >>> M = Matrix([[1, 2, 3], [3, 2, 1]]) >>> N = Matrix([0, 1, 1]) >>> M*N ⎡5⎤ ⎢ ⎥ ⎣3⎦ One important thing to note about SymPy matrices is that, unlike every other object in SymPy, they are mutable. This means that they can be modified in place, as we will see below. The downside to this is that ``Matrix`` cannot be used in places that require immutability, such as inside other SymPy expressions or as keys to dictionaries. If you need an immutable version of ``Matrix``, use ``ImmutableMatrix``. Basic Operations ================ Shape ----- Here are some basic operations on ``Matrix``. To get the shape of a matrix use ``shape`` >>> M = Matrix([[1, 2, 3], [-2, 0, 4]]) >>> M ⎡1 2 3⎤ ⎢ ⎥ ⎣-2 0 4⎦ >>> M.shape (2, 3) Accessing Rows and Columns -------------------------- To get an individual row or column of a matrix, use ``row`` or ``col``. For example, ``M.row(0)`` will get the first row. ``M.col(-1)`` will get the last column. >>> M.row(0) [1 2 3] >>> M.col(-1) ⎡3⎤ ⎢ ⎥ ⎣4⎦ Deleting and Inserting Rows and Columns --------------------------------------- To delete a row or column, use ``row_del`` or ``col_del``. These operations will modify the Matrix **in place**. >>> M.col_del(0) >>> M ⎡2 3⎤ ⎢ ⎥ ⎣0 4⎦ >>> M.row_del(1) >>> M [2 3] .. TODO: This is a mess. See issue 3893. To insert rows or columns, use ``row_insert`` or ``col_insert``. These operations **do not** operate in place. >>> M [2 3] >>> M = M.row_insert(1, Matrix([[0, 4]])) >>> M ⎡2 3⎤ ⎢ ⎥ ⎣0 4⎦ >>> M = M.col_insert(0, Matrix([1, -2])) >>> M ⎡1 2 3⎤ ⎢ ⎥ ⎣-2 0 4⎦ Unless explicitly stated, the methods mentioned below do not operate in place. In general, a method that does not operate in place will return a new ``Matrix`` and a method that does operate in place will return ``None``. Basic Methods ============= As noted above, simple operations like addition and multiplication are done just by using ``+``, ``*``, and ``**``. To find the inverse of a matrix, just raise it to the ``-1`` power. >>> M = Matrix([[1, 3], [-2, 3]]) >>> N = Matrix([[0, 3], [0, 7]]) >>> M + N ⎡1 6 ⎤ ⎢ ⎥ ⎣-2 10⎦ >>> M*N ⎡0 24⎤ ⎢ ⎥ ⎣0 15⎦ >>> 3*M ⎡3 9⎤ ⎢ ⎥ ⎣-6 9⎦ >>> M**2 ⎡-5 12⎤ ⎢ ⎥ ⎣-8 3 ⎦ >>> M**-1 ⎡1/3 -1/3⎤ ⎢ ⎥ ⎣2/9 1/9 ⎦ >>> N**-1 Traceback (most recent call last): ... ValueError: Matrix det == 0; not invertible. To take the transpose of a Matrix, use ``T``. >>> M = Matrix([[1, 2, 3], [4, 5, 6]]) >>> M ⎡1 2 3⎤ ⎢ ⎥ ⎣4 5 6⎦ >>> M.T ⎡1 4⎤ ⎢ ⎥ ⎢2 5⎥ ⎢ ⎥ ⎣3 6⎦ Matrix Constructors =================== Several constructors exist for creating common matrices. To create an identity matrix, use ``eye``. ``eye(n)`` will create an `n\times n` identity matrix. >>> eye(3) ⎡1 0 0⎤ ⎢ ⎥ ⎢0 1 0⎥ ⎢ ⎥ ⎣0 0 1⎦ >>> eye(4) ⎡1 0 0 0⎤ ⎢ ⎥ ⎢0 1 0 0⎥ ⎢ ⎥ ⎢0 0 1 0⎥ ⎢ ⎥ ⎣0 0 0 1⎦ To create a matrix of all zeros, use ``zeros``. ``zeros(n, m)`` creates an `n\times m` matrix of `0`\ s. >>> zeros(2, 3) ⎡0 0 0⎤ ⎢ ⎥ ⎣0 0 0⎦ Similarly, ``ones`` creates a matrix of ones. >>> ones(3, 2) ⎡1 1⎤ ⎢ ⎥ ⎢1 1⎥ ⎢ ⎥ ⎣1 1⎦ To create diagonal matrices, use ``diag``. The arguments to ``diag`` can be either numbers or matrices. A number is interpreted as a `1\times 1` matrix. The matrices are stacked diagonally. The remaining elements are filled with `0`\ s. >>> diag(1, 2, 3) ⎡1 0 0⎤ ⎢ ⎥ ⎢0 2 0⎥ ⎢ ⎥ ⎣0 0 3⎦ >>> diag(-1, ones(2, 2), Matrix([5, 7, 5])) ⎡-1 0 0 0⎤ ⎢ ⎥ ⎢0 1 1 0⎥ ⎢ ⎥ ⎢0 1 1 0⎥ ⎢ ⎥ ⎢0 0 0 5⎥ ⎢ ⎥ ⎢0 0 0 7⎥ ⎢ ⎥ ⎣0 0 0 5⎦ Advanced Methods ================ Determinant ----------- To compute the determinant of a matrix, use ``det``. >>> M = Matrix([[1, 0, 1], [2, -1, 3], [4, 3, 2]]) >>> M ⎡1 0 1⎤ ⎢ ⎥ ⎢2 -1 3⎥ ⎢ ⎥ ⎣4 3 2⎦ >>> M.det() -1 RREF ---- To put a matrix into reduced row echelon form, use ``rref``. ``rref`` returns a tuple of two elements. The first is the reduced row echelon form, and the second is a list of indices of the pivot columns. >>> M = Matrix([[1, 0, 1, 3], [2, 3, 4, 7], [-1, -3, -3, -4]]) >>> M ⎡1 0 1 3 ⎤ ⎢ ⎥ ⎢2 3 4 7 ⎥ ⎢ ⎥ ⎣-1 -3 -3 -4⎦ >>> M.rref() ⎛⎡1 0 1 3 ⎤, [0, 1]⎞ ⎜⎢ ⎥ ⎟ ⎜⎢0 1 2/3 1/3⎥ ⎟ ⎜⎢ ⎥ ⎟ ⎝⎣0 0 0 0 ⎦ ⎠ .. Note:: The first element of the tuple returned by ``rref`` is of type ``Matrix``. The second is of type ``list``. Nullspace --------- To find the nullspace of a matrix, use ``nullspace``. ``nullspace`` returns a ``list`` of column vectors that span the nullspace of the matrix. >>> M = Matrix([[1, 2, 3, 0, 0], [4, 10, 0, 0, 1]]) >>> M ⎡1 2 3 0 0⎤ ⎢ ⎥ ⎣4 10 0 0 1⎦ >>> M.nullspace() ⎡⎡-15⎤, ⎡0⎤, ⎡ 1 ⎤⎤ ⎢⎢ ⎥ ⎢ ⎥ ⎢ ⎥⎥ ⎢⎢ 6 ⎥ ⎢0⎥ ⎢-1/2⎥⎥ ⎢⎢ ⎥ ⎢ ⎥ ⎢ ⎥⎥ ⎢⎢ 1 ⎥ ⎢0⎥ ⎢ 0 ⎥⎥ ⎢⎢ ⎥ ⎢ ⎥ ⎢ ⎥⎥ ⎢⎢ 0 ⎥ ⎢1⎥ ⎢ 0 ⎥⎥ ⎢⎢ ⎥ ⎢ ⎥ ⎢ ⎥⎥ ⎣⎣ 0 ⎦ ⎣0⎦ ⎣ 1 ⎦⎦ Eigenvalues, Eigenvectors, and Diagonalization ---------------------------------------------- To find the eigenvalues of a matrix, use ``eigenvals``. ``eigenvals`` returns a dictionary of ``eigenvalue:algebraic multiplicity`` pairs (similar to the output of :ref:`roots `). >>> M = Matrix([[3, -2, 4, -2], [5, 3, -3, -2], [5, -2, 2, -2], [5, -2, -3, 3]]) >>> M ⎡3 -2 4 -2⎤ ⎢ ⎥ ⎢5 3 -3 -2⎥ ⎢ ⎥ ⎢5 -2 2 -2⎥ ⎢ ⎥ ⎣5 -2 -3 3 ⎦ >>> M.eigenvals() {-2: 1, 3: 1, 5: 2} This means that ``M`` has eigenvalues -2, 3, and 5, and that the eigenvalues -2 and 3 have algebraic multiplicity 1 and that the eigenvalue 5 has algebraic multiplicity 2. To find the eigenvectors of a matrix, use ``eigenvects``. ``eigenvects`` returns a list of tuples of the form ``(eigenvalue:algebraic multiplicity, [eigenvectors])``. >>> M.eigenvects() ⎡⎛-2, 1, ⎡⎡0⎤⎤⎞, ⎛3, 1, ⎡⎡1⎤⎤⎞, ⎛5, 2, ⎡⎡1⎤, ⎡0 ⎤⎤⎞⎤ ⎢⎜ ⎢⎢ ⎥⎥⎟ ⎜ ⎢⎢ ⎥⎥⎟ ⎜ ⎢⎢ ⎥ ⎢ ⎥⎥⎟⎥ ⎢⎜ ⎢⎢1⎥⎥⎟ ⎜ ⎢⎢1⎥⎥⎟ ⎜ ⎢⎢1⎥ ⎢-1⎥⎥⎟⎥ ⎢⎜ ⎢⎢ ⎥⎥⎟ ⎜ ⎢⎢ ⎥⎥⎟ ⎜ ⎢⎢ ⎥ ⎢ ⎥⎥⎟⎥ ⎢⎜ ⎢⎢1⎥⎥⎟ ⎜ ⎢⎢1⎥⎥⎟ ⎜ ⎢⎢1⎥ ⎢0 ⎥⎥⎟⎥ ⎢⎜ ⎢⎢ ⎥⎥⎟ ⎜ ⎢⎢ ⎥⎥⎟ ⎜ ⎢⎢ ⎥ ⎢ ⎥⎥⎟⎥ ⎣⎝ ⎣⎣1⎦⎦⎠ ⎝ ⎣⎣1⎦⎦⎠ ⎝ ⎣⎣0⎦ ⎣1 ⎦⎦⎠⎦ This shows us that, for example, the eigenvalue 5 also has geometric multiplicity 2, because it has two eigenvectors. Because the algebraic and geometric multiplicities are the same for all the eigenvalues, ``M`` is diagonalizable. To diagonalize a matrix, use ``diagonalize``. ``diagonalize`` returns a tuple `(P, D)`, where `D` is diagonal and `M = PDP^{-1}`. >>> P, D = M.diagonalize() >>> P ⎡0 1 1 0 ⎤ ⎢ ⎥ ⎢1 1 1 -1⎥ ⎢ ⎥ ⎢1 1 1 0 ⎥ ⎢ ⎥ ⎣1 1 0 1 ⎦ >>> D ⎡-2 0 0 0⎤ ⎢ ⎥ ⎢0 3 0 0⎥ ⎢ ⎥ ⎢0 0 5 0⎥ ⎢ ⎥ ⎣0 0 0 5⎦ >>> P*D*P**-1 ⎡3 -2 4 -2⎤ ⎢ ⎥ ⎢5 3 -3 -2⎥ ⎢ ⎥ ⎢5 -2 2 -2⎥ ⎢ ⎥ ⎣5 -2 -3 3 ⎦ >>> P*D*P**-1 == M True .. sidebar:: Quick Tip ``lambda`` is a reserved keyword in Python, so to create a Symbol called `\lambda`, while using the same names for SymPy Symbols and Python variables, use ``lamda`` (without the ``b``). It will still pretty print as `\lambda`. Note that since ``eigenvects`` also includes the eigenvalues, you should use it instead of ``eigenvals`` if you also want the eigenvectors. However, as computing the eigenvectors may often be costly, ``eigenvals`` should be preferred if you only wish to find the eigenvalues. If all you want is the characteristic polynomial, use ``charpoly``. This is more efficient than ``eigenvals``, because sometimes symbolic roots can be expensive to calculate. >>> lamda = symbols('lamda') >>> p = M.charpoly(lamda) >>> factor(p) 2 (λ - 5) ⋅(λ - 3)⋅(λ + 2) .. TODO: Add an example for ``jordan_form``, once it is fully implemented. sympy-0.7.4.1/doc/src/gotchas.rst0000644000175000017500000006341212253362407016770 0ustar georgeskgeorgesk.. _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 at 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 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 >>> eq = sin(2*x) - 2*sin(x)*cos(x) >>> simplify(eq) 0 >>> expand(eq, 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 >>> print(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 __future__ import division >>> 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. 6.2000000000000002 >>> type(6.2) # in Python 2.x, in Py3k <... '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 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 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 `_. >>> from __future__ import division >>> 1/2 # With division imported it evaluates to a python float 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) 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 ``Float`` objects. The default precision is 15 digits. When an expression involving a ``Float`` is 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) sympy-0.7.4.1/doc/src/conf.py0000644000175000017500000001520212253362407016077 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # # SymPy documentation build configuration file, created by # sphinx-quickstart.py on Sat Mar 22 19:34:32 2008. # # This file is execfile()d with the current directory set to its containing dir. # # The contents of this file are pickled, so don't put values in the namespace # that aren't pickleable (module imports are okay, they're removed automatically). # # All configuration values have a default value; values that are commented out # serve to show the default value. import sys import sympy # If your extensions are in another directory, add it here. sys.path = ['../sympy', 'ext'] + sys.path # General configuration # --------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.addons.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.mathjax', 'numpydoc', 'sympylive', 'sphinx.ext.graphviz', ] # Use this to use pngmath instead #extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.pngmath', ] # MathJax file, which is free to use. See http://www.mathjax.org/docs/2.0/start.html mathjax_path = 'https://c328740.ssl.cf1.rackcdn.com/mathjax/latest/MathJax.js?config=TeX-AMS_HTML-full' # Add any paths that contain templates here, relative to this directory. templates_path = ['.templates'] # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General substitutions. project = 'SymPy' copyright = '2013 SymPy Development Team' # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. # # The short X.Y version. version = sympy.__version__ # The full version, including alpha/beta/rc tags. release = version # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # Options for HTML output # ----------------------- # The style sheet to use for HTML and HTML Help pages. A file of that name # must exist either in Sphinx' static/ path, or in one of the custom paths # given in html_static_path. html_style = 'default.css' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%b %d, %Y' html_logo = '_static/sympylogo.png' html_favicon = '../logo/SymPy-Favicon.ico' # See http://sphinx-doc.org/theming.html#builtin-themes. html_theme_options = { 'collapsiblesidebar': True, 'relbarbgcolor': '#2f441e', 'sidebarbgcolor': '#3b5526', 'sidebarbtncolor': '#4F663C', 'sidebarlinkcolor': '#81B953', 'linkcolor': '#29A329', 'visitedlinkcolor': '#307748', 'headtextcolor': '#2f441e', 'footerbgcolor': '#293b1b', 'headlinkcolor': '#AAAAAA', 'sidebartextcolor': '#DDDDDD', 'footertextcolor': '#DDDDDD', 'relbartextcolor': '#DDDDDD', 'relbarlinkcolor': '#81B953', } # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Content template for the index page. #html_index = '' # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If true, the reST sources are included in the HTML build as _sources/. #html_copy_source = True # Output file base name for HTML help builder. htmlhelp_basename = 'SymPydoc' # Options for LaTeX output # ------------------------ # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual], toctree_only). # toctree_only is set to True so that the start file document itself is not included in the # output, only the documents referenced by it via TOC trees. The extra stuff in the master # document is intended to show up in the HTML, but doesn't really belong in the LaTeX output. latex_documents = [('index', 'sympy-%s.tex' % release, 'SymPy Documentation', 'SymPy Development Team', 'manual', True)] # Additional stuff for the LaTeX preamble. # Tweaked to work with XeTeX. latex_elements = { 'babel': '', 'fontenc': r''' \usepackage{bm} \usepackage{amssymb} \usepackage{fontspec} \defaultfontfeatures{Mapping=tex-text} \setmainfont{DejaVu Serif} \setsansfont{DejaVu Sans} \setmonofont{DejaVu Sans Mono} ''', 'fontpkg': '', 'inputenc': '', 'utf8extra': '', 'preamble': r''' % redefine \LaTeX to be usable in math mode \expandafter\def\expandafter\LaTeX\expandafter{\expandafter\text\expandafter{\LaTeX}} ''' } # SymPy logo on title page html_logo = '_static/sympylogo.png' latex_logo = '_static/sympylogo_big.png' # Documents to append as an appendix to all manuals. #latex_appendices = [] # Show page numbers next to internal references latex_show_pagerefs = True # We use False otherwise the module index gets generated twice. latex_use_modindex = False default_role = 'math' pngmath_divpng_args = ['-gamma 1.5', '-D 110'] # Note, this is ignored by the mathjax extension # Any \newcommand should be defined in the file pngmath_latex_preamble = '\\usepackage{amsmath}\n' \ '\\usepackage{bm}\n' \ '\\usepackage{amsfonts}\n' \ '\\usepackage{amssymb}\n' \ '\\setlength{\\parindent}{0pt}\n' texinfo_documents = [ (master_doc, 'sympy', 'SymPy Documentation', 'SymPy Development Team', 'SymPy', 'Computer algebra system (CAS) in Python', 'Programming', 1), ] # Use svg for graphviz graphviz_output_format = 'svg' sympy-0.7.4.1/doc/src/outreach.rst0000644000175000017500000000225412253362407017147 0ustar georgeskgeorgeskSymPy 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 aggregator 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 `_. sympy-0.7.4.1/doc/src/wiki.rst0000644000175000017500000000044412253362407016277 0ustar georgeskgeorgeskWiki ==== SymPy has a public wiki located at http://wiki.sympy.org. Users should feel free to contribute to this wiki anything interesting/useful. FAQ --- `FAQ `_ is one of the most useful wiki pages. It has answers to frequently-asked questions. sympy-0.7.4.1/doc/src/index.rst0000644000175000017500000000123312253362407016440 0ustar georgeskgeorgesk.. 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/index.rst gotchas.rst guide.rst modules/index.rst python-comparisons.rst wiki.rst outreach.rst aboutus.rst sympy-0.7.4.1/doc/src/_static/0000755000175000017500000000000012253362407016226 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/_static/default.css_t0000644000175000017500000001302312253362407020706 0ustar georgeskgeorgesk/* * default.css_t * ~~~~~~~~~~~~~ * * Sphinx stylesheet -- default theme. * * :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ @import url("basic.css"); /* -- page layout ----------------------------------------------------------- */ body { font-family: {{ theme_bodyfont }}; font-size: 100%; background-color: {{ theme_footerbgcolor }}; color: #000; margin: 0; padding: 0; } div.document { background-color: {{ theme_sidebarbgcolor }}; } div.documentwrapper { float: left; width: 100%; } div.bodywrapper { margin: 0 200px 0 {{ theme_sidebarwidth|toint }}px; } div.body { background-color: {{ theme_bgcolor }}; color: {{ theme_textcolor }}; padding: 0 20px 30px 20px; } {%- if theme_rightsidebar|tobool %} div.bodywrapper { margin: 0 {{ theme_sidebarwidth|toint }}px 0 0; } {%- endif %} div.footer { color: {{ theme_footertextcolor }}; width: 100%; padding: 9px 0 9px 0; text-align: center; font-size: 75%; } div.footer a { color: {{ theme_footertextcolor }}; text-decoration: underline; } div.related { background-color: {{ theme_relbarbgcolor }}; line-height: 30px; color: {{ theme_relbartextcolor }}; } div.related a { color: {{ theme_relbarlinkcolor }}; } div.sphinxsidebar { {%- if theme_stickysidebar|tobool %} top: 30px; bottom: 0; margin: 0; position: fixed; overflow: auto; height: auto; {%- endif %} {%- if theme_rightsidebar|tobool %} float: right; {%- if theme_stickysidebar|tobool %} right: 0; {%- endif %} {%- endif %} } {%- if theme_stickysidebar|tobool %} /* this is nice, but it it leads to hidden headings when jumping to an anchor */ /* div.related { position: fixed; } div.documentwrapper { margin-top: 30px; } */ {%- endif %} div.sphinxsidebar h3 { font-family: {{ theme_headfont }}; color: {{ theme_sidebartextcolor }}; font-size: 1.4em; font-weight: normal; margin: 0; padding: 0; } div.sphinxsidebar h3 a { color: {{ theme_sidebartextcolor }}; } div.sphinxsidebar h4 { font-family: {{ theme_headfont }}; color: {{ theme_sidebartextcolor }}; font-size: 1.3em; font-weight: normal; margin: 5px 0 0 0; padding: 0; } div.sphinxsidebar p { color: {{ theme_sidebartextcolor }}; } div.sphinxsidebar p.topless { margin: 5px 10px 10px 10px; } div.sphinxsidebar ul { margin: 10px; padding: 0; color: {{ theme_sidebartextcolor }}; } div.sphinxsidebar a { color: {{ theme_sidebarlinkcolor }}; } div.sphinxsidebar input { border: 1px solid {{ theme_sidebarlinkcolor }}; font-family: sans-serif; font-size: 1em; } {% if theme_collapsiblesidebar|tobool %} /* for collapsible sidebar */ div#sidebarbutton { background-color: {{ theme_sidebarbtncolor }}; } {% endif %} /* -- hyperlink styles ------------------------------------------------------ */ a { color: {{ theme_linkcolor }}; text-decoration: none; } a:visited { color: {{ theme_visitedlinkcolor }}; text-decoration: none; } a:hover { text-decoration: underline; } {% if theme_externalrefs|tobool %} a.external { text-decoration: none; border-bottom: 1px dashed {{ theme_linkcolor }}; } a.external:hover { text-decoration: none; border-bottom: none; } a.external:visited { text-decoration: none; border-bottom: 1px dashed {{ theme_visitedlinkcolor }}; } {% endif %} /* -- body styles ----------------------------------------------------------- */ div.body h1, div.body h2, div.body h3, div.body h4, div.body h5, div.body h6 { font-family: {{ theme_headfont }}; background-color: {{ theme_headbgcolor }}; font-weight: normal; color: {{ theme_headtextcolor }}; border-bottom: 1px solid #ccc; margin: 20px -20px 10px -20px; padding: 3px 0 3px 10px; } div.body h1 { margin-top: 0; font-size: 200%; } div.body h2 { font-size: 160%; } div.body h3 { font-size: 140%; } div.body h4 { font-size: 120%; } div.body h5 { font-size: 110%; } div.body h6 { font-size: 100%; } a.headerlink { color: {{ theme_headlinkcolor }}; font-size: 0.8em; padding: 0 4px 0 4px; text-decoration: none; } a.headerlink:hover { background-color: {{ theme_headlinkcolor }}; color: white; } div.body p, div.body dd, div.body li { text-align: left; line-height: 130%; } div.admonition p.admonition-title + p { display: inline; } div.admonition p { margin-bottom: 5px; } div.admonition pre { margin-bottom: 5px; } div.admonition ul, div.admonition ol { margin-bottom: 5px; } div.note { background-color: #eee; border: 1px solid #ccc; } div.seealso { background-color: #ffc; border: 1px solid #ff6; } div.topic { background-color: #eee; } div.warning { background-color: #ffe4e4; border: 1px solid #f66; } p.admonition-title { display: inline; } p.admonition-title:after { content: ":"; } pre { padding: 5px; background-color: {{ theme_codebgcolor }}; color: {{ theme_codetextcolor }}; line-height: 120%; border: 1px solid #ac9; border-left: none; border-right: none; } tt { background-color: #ecf0f3; padding: 0 1px 0 1px; font-size: 0.95em; } th { background-color: #ede; } .warning tt { background: #efc2c2; } .note tt { background: #d6d6d6; } .viewcode-back { font-family: {{ theme_bodyfont }}; } div.viewcode-block:target { background-color: #f4debf; border-top: 1px solid #ac9; border-bottom: 1px solid #ac9; } sympy-0.7.4.1/doc/src/_static/sympylogo.png0000644000175000017500000004637712253362407021017 0ustar georgeskgeorgeskPNG  IHDRofo sRGBbKGDRhj" pHYs XtIME [Zۇ IDATxg]Yz>n.B7: lNaMMeqʢ*?,rlIU,%"KT`g ':ZϽ@dUup{w/,xw;wǻe<3ϝ;;F|sΕw6ĻSsO}js"IԈScXj o !Y@?w']`*QJױƎ.Li~?6wފa/Yo<ֻ]`PX%MՌm\ ^yz:?6Z屇t4UlyUnb֬mlnwzDE_i': !:೺s"}u_ѿ;٧Oq+/S>#eLMVh'Ņ A۷v{k+o1IC(FIS5~h@"ZGBXk]!:px^k͋痖x3>KD?I ){}ywɉ#3#ӎc26^$!/6RHXi l@i,GbӖCS 4VThIbE)T$J'iѩFDv !.-K4R,NNTO^=sۻ|Y٥nŒ`|gha60Lc{ QZ#H!!@?K `Rķ,(!NJiD vZZۢ @JR B4{߹x~[#$lxt|78PZy9ǵNWc c łoy~Vԛml/Y&9X`~LβJ`K# 6 ީcJ$*aq|j9Cbzz QBD%eX)4!N;.ke0Q-&)kv{mZ1A;!4RXx;*lx(<39G$}R)q={ı),=MK_fKA#g<0c>KXù\|d*M/pk~G(hZwpmɃG_7Rť-xjTHEC3㌗RYi՛تHLh8:CRiJ;h!^LRC:)ZZ-!o³Wҷ+w*3|cI`_}ł7<350>V/̍sd _?r903"&4KE$Z*H)p^Ӊ"iҊt!ʓjEH03R;:z!`sIH$cqbj H)qM/ZgHϠ=>pr4B &BUA1)q4RHC  |RRNc@?CeNL-uc9_}%Ro f(r2c%ʾ˔5M:juDXDͤp%hYh֬6xJ5I1 i S"d(NG a`[bp[NRFg<0=J5H)eQ.lыb,#8v~^;݉q_ܶ; J>/.<{4oʌBC0;w'!s9<{l||XOc)Q񻿏X /z0cC9Uף8ӄnx*~;OBlM GrBEM6^;%bTC*љ`tw4 )0 ob3p}3p=1Xt IL!L 4Y6wQd0߶x9FH*^K_N3R Bך^WkkBXZI!)Q!ė/_ZQ۬~$p\;wRzdZx c/X'O.ih!֚_\t5xLM)(uilv4V^Ð;ZLIH !;)BH5JyHCU",`yEhZGm윑_iZABƖPt !E1_L'IS( _7_ 9`kArVZ\x8{h%BrsPa|vfd~t,N,Nqt~\"MA~U4_[TxG/]t~@;v2!4cnj&R CI& 4FOF Rb&a`4- c蠆(QZ"c0IBH B(!)ؾAa&?jSVNbx?g7mrmH\fneH$ǎMU1n}zԶ=ucbULܢ֫]n:'BHB|+?,@`7;wT W \ת\{x^J9ד=or+hw_Y~~Cerڝ>;۽.0*(\D~ք@4FK!ЩBig;+ .B PXA>351L\,J<ա"`YRi*_{ݭ ibZ6Չ)ȗnv{nd40 L["-[0?ct˖-˜zGq,'͒L|Ӣlt:t^Ne] C *aҵ}^vbCԵ? 8ۑ>яZt]?=}Ţ_92=Vg8u|Z-$DCZ)΢#in6>e6O'b\B G4:( 5qJo`v<^=XE u)x9F %Me6Ng8%nˢ *T)T866g-*X9xϵ1-4qAϗ>)i`;.1p')WGIuڽפ$7sW8z4'N^-OW^FaYq~q bRdv!%Ef.c@)%vcWօNx~~)?wzk5<<,,"P[X ,Nw=8MFG823ƃ%MSTL##֗o3{$wnji7GX+'v_ )36][s_34F&&9L2 \oџrqUZnǫǜ%h)-9Ugۡh1dT"՚翱ɕ?Gܾ;zS+.YvW)ismGĩbfjr9Gki"MR6WY}mbxM0F K(7z\P'M48L,\Te'A*G$0- >r1Ro ΃40L;\v!R>{b kF._,1˗; OY``&9"Hb W#0T*83[x}%6^ӿwSgܼo; !%'xDEljA?1^R«_兗oա~񵫦01Ƿ,|ӢvIk]LK)e :_B4Saܼr;7rsecZ6C#c-~C$I­+/013= #o5;mhvg?X%Ű5 sx΍RM#  [`;I*pG?X{4m{@{3k`{-.ڢ2WLrv}X#S-cZ݀ݍ݉(LdR6m&fAEOÅ|駙]`f(QJ!,ɼAxQw56hw8Qfʇ=oR=/P~l*JD+4!<*Ei138Uoz][ϵrg>g*H-WC`~SKD!]ddH'?0qҔfK/s_DJm;LLpA&g1 8h)W-Zr`/&IʥV|uN'`{>^XGefefZM?:bG(J=E$PZ뒳,V vP]X4RDA tPK>|Sx+Ϙ;H)ȍX I8UBKL 4 ̊i770`qqQrpPI'ɟye6oWce|fr䑹C&3 [춸qkkiw:R(L0L>o&Y[ ?O ^'ZЧJюD{8~yEJ|_5yfyUK+6u%-R.]pMS] `c;[זᡒs191$==Ǒ&0>5JL[0DY]^|:Vz18}rƱM . FQc[C C} s&Qʄ&q-ֶElB*^pxݑ3>痾x^,||JkV^94ϲ*Rܦ\! H>(Ya LX8OM\V7כ,/[7S*ΌPJAkEf.WVثi/ fr@fu?J{٧ORO?dӃsDZmn*nAD!QTb.RY*k_\baq6[ӏlr%*J8VGZ @)1Ṯ^#h%Tl$.CYmwN-wk׸ugC U TJum 5$I±!>#Tʙ\uE?o_]/.ֶ~ܰw 4!5޴V*)3Xʿ'sA1K[\z#EFJ9ʕkuT㔌Ck\:-ZCeĦJDTTdZu,>AH` +hے?ᆴ y[| n+~ӧ~Ǵ՗6VBG-  `A6.M l7oي|`aVvRDq&w҄HxcIYƻx'\l^\ZĔzӷb;a-2 3dZiV/g>| ^>ZT4Q \F)<_1unIܨEyɊBi>z4B3V~A >CyCMU4D0!LSZaİg0˱2R03йpa8ct$+7`jvɱ9x~^³W>}SZ[)=5OiC.B mR,zAk#%bCj5G>NTxY˯cə*S3U6vÔR2D(du)X6~3)}[ [;]8aT`kܑoKQRJ{;nu<ӧ ^ aX)8~; B,_M}vsBB$:d)+w~]fdmztz z\V8AB|rgYV?}_؉)%aꭕ7\}y٠obu+_+~UqxMk}ɘvZK֚&m҉R7C#Xm.9Dq Ҕ;A;b2K!+jWYigPJEx\ɺc(˲& +MzQ2O:`ՋoYY3E IDAT~Νo]7B:36Yq-~ϔD_V"Niz-%S#nA :zaDo'8{&Yͭ6…|[H)*Y  ;*qmw9"4ט82aoYzqÈm x[Ǽk|ryߘWX~ry SOq;o\+xyջdGZ`Bӯ(timL b/2PY{!ż&ٻ֧ڈmϾoEGthv9?O]<8( QJ34R%M A-}#-0cJ=翡'30Wގ˹Pdz%V>n隂Ш-,j60Pꔊv t"FhN&qEw}vpe!s,sCA^0LyX/B<~?~_^Jg,!9,=z|btyRhY4u [(H#K&Xi6 IU@C(C˸r-f,'iu a<NDs5ٍmwӯ̟|J#Mrd|*kwQ?GLoht>mDiw7rҾ2PBO̖bYk,?sZoR*Ҳfd洋@$ aآƸ3eH%l,'9 9!(E+hF!(db1>V_s\]2$Ӯːkg]޴K H<'5Rh:l%WKTPU~#QVBJD?{M&z@H>!$c!n7˧ \faEN$Kdgm)z7?Y+KFm|amA\%U'˸$}8=>Jޱ)4!JS4 U]IB~OȢi[ZFK˼,5Ea%I9qdSXA2@_j4UT4(MuR%q:+@A#ܿd^k}A5duɹoK/fLLqa?fv¥kiz"֫,;Oi4}ZCS0jH#B2<,uZa(FM;xP^SZ5~$7l1X^-a-v+K|ի8ɇx=|T 4I\`|jR ɦJ+4ZJD)+$QA~%ɼO)uQS htBގ~3B3yɹ 4#)͈@B0>Ym3L͗);Z6 >_{HPp,VJgf0S,25TbnBZiex>~T+Z;uA(MRp/(7`^UMNJԭw#v̀ooO^U$IƠҐ>a*A,Br: L0:=n Jb^R2`Dҽ4:R蓚vV?c0Hӵ-yuԓBr֔Qݝ?A9;?-)=~9^k}i_t(<ʅіH"C0[_v֚v+;ElZ~43#eJ.I+8!c=ƚ;_mB/\_9{eQ};۫XRJ6W74)?o/0wJsckZ-ChK8Z vu|bDz.]Үe-T)"k0daSlujvt!ߖ<ݗ)w827!90s/0sGw톤FHr]E~ȼvZkVZ-vz=\m&)[a/"!%NֶAܤ DˬХ߉xpf^٣a (Ec0TWϿʫ7ydfA~27޳6W7Is% #{u8s)%Rk74c8_X^M;~Q "'5or=vǔ, 0 0oYh;bŘ9æR±2ͯvV8p55jYN)G4Z C1Q#AQiƈĠLK&RAYHzIL3 too IH,$&vq ʈ&Li0Q,l*KuY`ی>4 tBafI_eVgGX8@ThtّAA#OOGԏ[cza۱1̬ΩrmÐ_XZZ}m+MM۝([Q6}'n wOxZv^iԊReZ 0孝Sk}El?P4^x( 绌3M׈$BjF& T3s ՚Vю#IB4zI$A RE:h  UR!f,pa7-R)-:Tz7Lsln uh7 B l'k fQJQ,y-F)h:%tx5V8>qzSy@476 maap|brq7^Yefvj&钌(Gӣۍ>FfwUM֠B j58ѧlT(4 )K˒miT5e7-ryBC3 Ymv֗֟|m<ʍokYs+MZf2Owb[G~”V-=58an0{40I0ec=cL k#`v,ŭ&iQ%nrʐCPۈo%% /.9s MM&8Rba uL?H{)˭&e,r1z |~L㐫Z\l/pf{禨lZkŴL^Hy7oF2%TibX`24IBe]{&KYQH1ǛbܞPZ!`R~ }:' RuB)0lSM˖t!4!4q|Zz؏$Wkm ߭+?5ŗ~=s'fƸ`-AZIr;'o+c[[p|_=P bVyЙWuRR{H[r=ˎ>ץ:(d{TaYNl'ǹ{{Ԗrk/|^3 B̂fɳ4XˢB˃9B[:E҃  B>AKp} <`JCg,SF?EhPڥ+x6}.L&Ő?>jFֶx\ 57!v >BnOIڦIvE'ӴoUa(-dKDFKH QrrsZ#0 R!zq8kڎ!DcTOVszaPP1;7B!r#֌} Jv Ҽ/k,&\D0 b#v%a1w?ee&!l_{0|yMF¶llg[rݎutS'nnLJN;:u$vO(Rv(S(ah0bb#j"eGP(+,mn$to$c{8oq-9 2x I^?,pLR߶HRMX0zlR3pTkY2r*lEj z7o Z|;s՚=v;]j#HƳLL>ݴK5{SJ}ְb}Ɠ.$[) S߫1<>rn)01!,+F-!D6lB׋ql,9|3y0鉟"am 8NE"Yq*/H}$RV3DK`!ġfo53uQVr#Ї!QefZG%1/pYyzdڭ>76y2iיtDaFRn"eɴKՈj'B$RVZ2"~}Uy"nIj&aK@?]~qڨQϴG|MK\"=ɉcJ=oY1ˌ|L+LI޹ZT>1t;6&'W87Y\-]p5X-Hְ7ML`%Ľ E d8,\ >"r)V4Y_)z [C3b1ep"C&0SFhX ;7囵 7?n6tV/haEd$(!ܕ_1g+L_C84xYuϾ&&ͣ{PJ26Ttn~/O>塐JfaL\'z.Y/+:d3);8( nj 2ÊjC#XhF|n`/E~^~ڿ~ׄX&zY on,aH)(rc>3pO?_9F6Z&slp<293̠SXR f-}dRbr(7@|ޣG4h[)i(gCi,9x,m?BuPȉ'2 Sa 6e\*x&_} Rm} GģT,ԉxF6rqGu@vLަRkDœ!.G:a>olA9QCz8T Y^7_s{50+S]_?s;lԪ {w%IJ łG60<|b!c_9B-c~;g+R\hLIoG(bݦ7YgNk-ǡxd DhXY]TJ`G[7ט_ʦ3jryHz- ַ V(`?܀*a3PS1đ9 h8rm%s;AssMb/TحKu `qn8601­ı«;u1$³^gt/ !fo\;YN^rm8jܕ­Bo$jr}=c[^PYmܱİͧ[B dK OjHgFA'\_reizs1Rf$qge])`a#QJϯ0}:O*li1u*˝㘍{pb7(7ojlHƲ1>+ drI"WX# ]KU+#s+[)%Q?y\k\!5Iflbel_FӪ!bޙIz||?^kSYi0_^k%yR2Tڅ;5ݾBJnIpY3ݶ(LN4 ~ዸodvjRd:[CJ 1ڭ+K\%5ZOeҝ<~Ǒwl&e[nL'm *M$uf m ~`tGvmK ۅ5Kq}|FZBŅqQJ:L\q-%BmzY uJXŒeEBc)2,_Pw'_/0TBrY[.n Gal?X0V"zevKI7.)8Wmyr;04FP4YN]&\Tz;`DXHmq:Qr c1ƲRiu%S0o^^9? d{r#9z=k,Vu2axLD4>: sU&R ,!:.^["WeQLg 6mX)g!%z(CMPʐRIzjIb٬Rz|V[lb8kmO?dg#ґvz9ʽy%bf>\` QB\%ߣYo$dRI qזϯT1RJ}"ȇz R)qzUOLΓQ gIDAT8V`WgKnr+%7ݳg^>}4gϞN̙3]p7Zȓ+kp@VY ;w:U(͆Onk~Yزhn6(8tBF7Ko._h(>;ڛWk>R I = "N7۶^k BJagoXk/9sn"=4pv~pƜ>}Ƌ/S{O>ƻ#٣lKkGG8Gr4l#>oeۭdrYҖ.iVnӿ/Oc-+_oƄ-TI=bb?={Gw{g^.>}:f‹Wvjx,G9qx7P!oaIR\;d:J'/O*#>"">8b,DIEZ |V2n׻>U=\n~g8/,_Z.*՛ScxiP,7؇}[WM {([OfX>'j j1:pѴ웋N|wp~1> Z5Muΰxӗ) 8:ϣ(\"z߂)`&Cg_(߻B}8[&Z )$i]JC *X0_"Mr#ZVo¹)'~7Y5PǓGﶵQaEޙᥟE.GL>uڥ;[Z?yuҎolLi>JHFTsz߰Gkڟ^#|_8qۗOIG\6,"%8&߫]td|CдVT^[~6%CJmI߿>&ķ f #n.PƣJzCJXX{%D> 5gH 1`{/1h[pnO>=q,WV_ DjKtF A@'кEpX_Xe ?yyqٯM# fɤY~0a,QLE/U޼|Ȇ8Rx}KR hH]'QVb!d5ٛ ªU-a>'=ayYR_U!U҇(GNpS.Xb RD'ф(rDea8y^PbNXzvR0pҾ4^QaͧW4[ňʻT)KD,6ĺKFX<7um_Bִ2y~OOsnIP^'4(QC tF\UZ$`%%mF\L4 :2f'(eh?^ޡ8p<+1Ү>sҤTrZB !uOǟ`r{w%cvhodN3X%KuBoTʐJNoFR/С%j ]r'8f5QhO mNTSKXr' 7! kƪi&ONSw+0G:z+x)xViJJT8B.ʸ-l'|#Pm?c-)O;sJ# тtb֚>k̀S( ,|1: !h"Z ipLdsSwݵef1+q!Ǧ bR(HAO*#$יKq:N<7i,vڟA0bqNҏ7-C샴HCE//T+HPMcժ_p~' 9.Dl_4J<؟A vPQʑVefP5ײPZ o|} ifQ~!m dAdž6Fdٷ βVߛ<7_6P;iN>3?+^eݲ#2)iEf4VugGDZEYS 1""p*<xQnZւM>#|(:ʪO&uBԌukߺp~ݭs`ξWg?XW4̃ &r/ERi0hg玴NvaOְ<$5GwPGldC*Oy`vlf#ؠXj&B0y~ǟ`r|C`1.V(i%H9feFE r૟=Xb) mG& |MNO%Vm'A΅SssOs3A,906Q"ߓF됵&j\(a{dL)̓@l4u kѱ% Qېz@]`&O{eni`̢<|J5 % 6()2^q$J 6kvy!4ABbk$i*'E٤Zl|w+{}`nǟx?S[RBJ'N:#P]&zXT"|`"HsSѽ=`>@3bk&p ѷ(XGX:ҕƎ btW@|`7&OߓgI7&SGB#rP`H&PhPfI+xM ˊw{qsd,63pn"߳7{co|ު7WIENDB`sympy-0.7.4.1/doc/src/_static/sympylogo_big.png0000644000175000017500000050670112253362407021630 0ustar georgeskgeorgeskPNG  IHDRߊsBIT|d pHYs ` `% tEXtSoftwareMacromedia Fireworks 8hx prVWx[M$e^kzZo>i6(?BbDO^ !!] c$xPzpЃ'1L QIC,tDoOWwUP3lwٜ?')_l0fʏ))lͧ<93tMS9΂)_ 44|Ͽxۿ8'?ps~~GG'??|,Hn}?^NՏh}o*{sOpwC_q_sp2޺! 3S $?wj{OkW>S}d *!<}{]ëd%+YJVd%8U g0CH_޴ SLØ1Zn0)0QD&#y]UWZ[TeSwJ) xM=xHS(R*TH.$WVZ#La|#%4̣^DĠNyN:o" $Y! D6a7GLp] $_93L+o@}U ]HH&Qڼs><RpZM-*l\q3ݕ:0WTfx?U On vBo IH-0`iӎo0s Ad*GпX cmRӰr"fJ(h˼Ẽ&}epAݒW tWЦߩMJ/Q,0 Zd/)ֲxŀ.YZ ᔸy:=~m֋T}!j  ڙ)R /2ӧ#ϻcmHcN΃`0;H`ǡ3M5Dlc~ݯ<z`bp)7ky`) FbȠ7{\DS` a9볧hN|~bD*+Npu>>mP[K#]:'ABA/q+q(-[Gy9WywCrĺoeJ?ۅ y liSv8x P),1EDh+XbƇw pd-ˎPHBcdeYmqpف(d D#NbYHPf ǶҘ?m+ߡ %{Mrw4*35_BV0nd{6tX5h@.pQ? P6Tpv7ً&` *tl5`Ƙ,+  s4\WLzU#٧aB0 b̶CޔVٍOu^߿|a7QHaALVzcX-HY\WA2gZBqC AӸC wdhЮ xi@Zw̳y@n` m##m6K_?ZƸۏZs!R**r2t_4z*I Hp~ -N/Qmɰ٤*&-@]a,%:ކ"y~rf%DZx-Wۯ(Yٱ$}bO}<8ɇ=؂Y"^w^Tl.+GL?-qdak7|_22ape#H0L< &]®\z32;c=yLI{6X'4[,oF8ӀvN;qFcޏx #;G ܹٽ;d4&&|9wE/1/ "Q&eq|o$sO*X :CjbYn_d\[!q{/鷣}찰ۡ$ʻmg<~#~B^wb [mknzەd%+YJVd%+YJV>JwMǽg~O}%+_Mm\*$HmkBF)3>mkTSx][sFdƱ%dka_TONQEI]8$yqjkcKVsNwE D Nsst͸7ؽ?N`h7Wl򰛎]؇05 }LJ&N~n#-"ڕvEÓc_Q-Z/d2螆 xg:JNblOv/ l`w0Acx@vG `Dv[o@s؇cΈ5GC&]m'+}=5;S/=hӃ&lLp3Q 6 ZŷE3\R6ccng-Q gphX=XM1Cǵ>>."B-md !!d3ɰ3>D!8e1Ҏ&>@ eHPacP;|{?+~j99|?hj42-7[l!I|HBj KˈcWoНAE dI^.N EUv@FXҭC3:CE[D9M'R;jV2C]@^֪/WϖTFϾ_p-ESRRQ|{ga?ܙ!^o)@c6DD/Ì5W)e3+}F M'`@id|VO(qb E4eq0h br^s R&<2`]6LYicH,'Eө #gc2j25fg62Ѓ|&_HnVL(EXbȦǣJJѲLZ.Ceh̔0-2AfJ2F1}"h9ۢh3IatY2>G+5ϵd{r`BL)G`頒/MXI+բgZ1`ȆagzvɘUeV֠\e,XF)J]*כd'_*Nqp u6n89 b=<*)VjfjrP#T_y99&.z cF+a > 5eD?9` Jrl(Dn%[]~kEqͰE[B(E8L>` f5Tp1ʕJ#&b #HUc5juf 4|N^C1 *)^KזѯU S3@E(/ӬzϝK*Γ/\}EE-"2d@ a5|>aIbܸi 7 N/L7,2 .F (SY߂' V Rfp7ZmV>9 Ȁ<̯6+)%E4 $,MhUoJZS 1y(dմl|:F aps:&%%UXmdz @KDXYbQXSZ9#*ocN3goz<7Hi|*ز'*hdlj ^?];|!%A\A;T }8ٕJ|7S,,q\<ױ vf4˜?P*HiOArMf`7$pr8[Th!>&لl.,Qf"ZzEX#ޚLs:aR*M]6%"P׈,^Z-q )3irSe^q I{3ʤ|9>k by79lcC=ޠK  56zzeS1W,UF_f_tńAo'ד]{N&&C4W`+locK&lܰ臤`\APմ-6r69&#ܐ-f#sB(ZeوmH?rVٲXL%U&JnD2spb# ]dD1_SQUQ1g EF;M)=&_iMS?%;l orx?idvVӶȥlBǞ8BKCJ.f/(i_1p5rfUfR2 23)-*/v75EB>EQlVVEFGbT*/6Z-&e_e3ܩv{&ݻ8ElvFaƞO/զ-ڴe1٘-„/il2ÄgVҎњS{68NCz9 ŸS|\Y|BLO&[mGNhF.+6vSXx:˗%hí[dj`Kqخo<)6zYJo$I0g~p_M>QzbKcIzXRLY)%tWB %sЊ}U0DAPuq F|f-Kt_P?QK%:Z 3 uQZM?T5([>׭;SsQY7ѢFZ@+iY75O)L݊Jn~P5; T t ))Zw9W 2UG&ǐWv[fw%ŅDh!v_Lf~Jdzμ#U[\@# ʽo5K3ҝ{I R5O׺g9Zڟ@dJ6vqVP/~38eAMXJul8 j&}oOOo%eΘ핚\gǽ^f˭HCZԌ7ʈ${>'Ю'{͸~H{TLȏS8YczV#Rz ?aNM?eN{$'y avpNJI i5 عҌY쑚NSg=.tS3z^7,#ptٟ^j2*垼OkpLi_DetrCˏmu 5S>c =@ CGv-mRC%='v2B9璿 .;9saKeqOBoh$_3O͗W`1UǗ%Lߚ)?z YYs[3w~@[i ,"wv!=4.άM::k? kW% 1N0Ϳ~9r'vWb:);opvg7-C 2t-C+3u 9Кrt-Gr2GwѢ1mݲs-;q [疡[ne薡UڽC3AċO)hO8rv-g g 2t-C ƭZ:nڣ[vnٹeb2ʻjvϕw-C 2t_>C~]5Cʻ[n9/on~c+Zvnٹe疝|vʻ5Xy2t-C 3WU3zk9얳[9+Ge薡[ngۯ6sHeoe =}Qbkíe| }͆-q5̯Aoή_S3G818bJ&2ݤ1o%)(4IA V_*XJ \~l8~qmhZr$Vt9i;k?ϊ4p^/h<:JMہޤWƃW#0N _gwzHwC]5)% ]*jrJ|ǴFD .?Ђ|8c9q>#?[9+ii|h;ʸjeM"sp7ZX}Lr,fC{JA,hLq -'cs9Ϡw(x}W)sxx֟A3UUҒYց}-Qϐ"KyF!vm|;O5wE5!ye&XޕGa$ Z]RU}L4$V.>KzށlekWyJ,1=5|k72˘Y[̾%e_`?h92a#-+UQA85_-Xz2Qu#/}O-!_bgc'1*n"]Ђ]@j2/sF*\ϭeuU6%z|[F6gg[ HeF]' 1E|r !e~A-Ϋׅof#̞ mc\u$@UzNxT+IYdQm%4bQ6׉hⱏlmW+J=ILn3&K!Ƚr-w4'{!`{?֣JgV3"U:T.yF" ٢x ay\s+~?XmU^Q^]3X?m3, , 7 ~hyw^x|:p,Gbazx4O^)GCv0:FW hK'=1h@3qsݦؾï;M6 l{x

钴+hƏxg_mwot65!ǻ2wvj?#x?q~ڣن4\ | 54Z!BIdr߆Iw 6'DDFaEU^3. TEDNjrv( "\GUHDUEBD("m/"XUU!B~a\DHp*iv~r*m$" Raq*H"F"Ұ MU~#Qa)ɰ2T,#0=AjDFp*lt 7UF2ϰrUò+dTy CPщTe8dxA*8UʬW+Ti$#APՋ[^ptᜌHՍ[~#alBFm}Ff}F3/d'F2>4TvOA/}v 9'v@عi:si"# 2UF2C6h4ɈHso&EhvQ|Mϰ U1H]?KX՟;f#efc5*>RUpFv Yϳ7eȹ$]eٹraN,Ut?nYY4P;ZQ_/B6 uS\d(U_gMݛ9+ )b[6%?8;3$l#-N4q:ܦY#f﷬,mkBSx]N0Æ WM- 2*5X|DZ=fLc(F? hҶ BLrGpodgd/e>f 9]WA\\Lf[xilϣTs1H QlHاEt(SAZ_Y XmmkBT~rxݡJa]q cb X ,C U󄧿+|u8;U=ߛ[=| Io@B_Oo@GGGzLo c@iz,џ/ ?NПP$ _>Yz??ǷG @њ[M}VA:Y@mkBT~xݱ `Qhhj $Z  W!1M~K7mQZiצmviz?2 ? ޛGGG7?^& ϏGGGGGAz????74k >QuimkBT~~xݡJQa/EAt-baip? 6AP֌+bv* N:vk5jjmצmm71Jo@'" _.7?Jo ,`G^_N?7?????Omצmצmצm֎n vПPvz? k[fz^z???ท@Jӿ6?hmkBT~x-JQab:&ʸ'jE,ÀlP q#p^pJUSߚߛp@zހ?|757x-fAk__[[' '\7 GGG?_}ހs7??????? OO(M&<#YXrmkBT~x!J`Q"*X(V4-0QA ܀b ds wz= _}tscA5Mncr@iz? Ouz?O _]7^7??>Oo@GGGGܸ*7MnwӿQzG'˭//6& C_M/c`l<4wmkBT~ܚx-NCQ% !iB,yiRQנ00$! 1(\#`&QnqX~';vڍG5jjmצm>`G џޥ7W]z?sׯG//џۓ' k~@b3Ko _7????3`mצ?.G \mkBT~bxݡmBaaF!) 5 !L +t&t::Lqyuǜ0jQSӿ4kӿ6kӿӧ7?ۤ7yY7Ko@GGJӿ6kkez????3P<`kz?????w wiz???? Qbp7mkBTx-JQa?h"lNqcm\Ab\;9{{?pi55Kӿ6kӿv:Io yހO8џ@GGGz;Io *`G/_?_7?7O lS_Az?zJo@GAz+ kn5`Zzvz???=;Ho ?_TqwZY)>mkBTx흇WTY9ٙA2%$79HPD%*ID 9a<;{6ͻ{55 9sfsoݪUu@m[ֶmOӆ Çɓl.^OrFm~Zmbb̥:QvlmX~,֒8Ǐ@iqoϻW‚Z}}=2y!02gkmMԮ_˼X+Φ8gsңpyo%av'x1+!2G5l |r~ 7mKX_p .1PU6sJMk333ZkTvm+:,#kbvI\m y3PqӃTGn.im:.&kbof낤9B aǡ0<_7 l70^[ZZRknn`^D1Z LKp szJ3GSjq}pdN 0Gn t9=(X2~?ALvkw&ч9 VJ37t nkp;40G~Itv*.)Gg?,|k===Db⵺B1p2,<*YjBcHNp74+/c`iT^.3No0^{@V"U00syU"=Y( +q0204NL;fl m7J?^|6:: Z>V#jxM^ϟ,smuvй&H.B\} ^2g4OSyLC d [A9$ 6˻k_4TWW-^;Nk=`OtjFY}wѾ\%==7qoDi0*ÝѹQkȼh pFB?={VNU8^i? 5>Pq/o}4sT;m~ΎՈ&V~Ѹ)VwJduBnR$dw^W7^c2pV?LkMMMk 񚐦&]k.KZCu7~G~Y;F~j[AOuhZ9Q&Pj NҢpnuk-%plHxT=xS)c3B^ޯ_D)dB_s9>ӋOhmIݻPR$7^o^A|=o";d_ű#Du NI3GavZoVVrz;xxMYǜ真z֞G=$LЧ_G!]OsyEK+SClm~Iڵkk׎azn.ïDk|Yx~zF)1߾dY{8 xs|?x!6w87SOÞga߫-//=^k(KkBzrOCC'4ps2\,,Az2=c3^yQ3}P~') z?sZI>9uww;^'`0]^AG=a bu76DZg:QvT]q+w/tdO@eGŔ`|:J3F` %;%Quɧc#J'_'woǏ5VϢ@6=Z=^åh+$Oqjp?3ràⲋL^s)r: {iC#H=fKuA#j w%2L622xmT9s/`=u*wbcz:N&{t2g6Or`6Gu`OĚ?ko޼Ygv{+ 5-seW w.W]=~x=߿KS!k,2=eN:Aj|d1x(6-H+#z^Uq_^˛2^#^:R~ e_b9xv޾v}ꠅ̞#ԕy\mU2J〺RZccl"s:ZUf9seq=ISNc~v'^Zk6pq?x6꽠=;A[x֭[.*T-9"4(9\oQsX MFV/Ne܃Aˍ㵻wuuu9^k-+SM*ޅ.πs'$\`!{&q2;B@B`(ܛivHt*F_~rgyB}w<0@c|zoQY4sq6SǚkE쯼Jr^;YO9:>^;;+բ!..233+w㵱Zxv_WO.Sp9K)b}ɭ뭼q>}W?G`{E{vX5*f_y:(^{a?**J8EGe;~ rtV=mm}e[kCBdc|u  4ʟm'af"q L {W"!-535ŹQbpR%K|y2l 5%sZ󉟍3ޜ﮾j>\[Wk0Qhځz?ko"\f.4sp6pdC?. Pƻ0/9:0־ԟBDgt<vRk.z}}vn~ \`>~aK!'hp|g(^#Ȓ? g xOa= "ڍÝ"#%#`HO޵̘;_O|O qY/v-,H_ Gv1p4{Ww {T~޼w%dvdIz~6˝?++#8kQ$R"!'+ k13B1YȤ^ +5%Ѿ#yv'|䈷>jrc/nҔ%}.qoCZa'Ԟc|g (W+NA dAQN8dPAx(y-?'.uX='~;n燊J9Us9FeKRXcWvW=s_OX"}bC^?j-^V  )^Q_^۠9<φi*X <ۙ ԥ5l>m\v}=~C"};DP bGa e➗%$^l}{38cTvN.5Lo 1$Doi8ZtzAcL=Q5}8/ggAia,6ҟZȆqΉxssbO%t~l}E+se1{c}nk8H Ȝs5Yj=ǻҺu0WDBgK̝Z9N,[T9@B3t[cO<#t_G?AlVP m/(yu\ ka;+U|$|FNω_}1J}~XD5[1AM87s CPY߼aU9dW8G^Pyz|Hx3?{2+X)v{} lzk)s9'r 1xN;'W[Q8F̔/;`!X>2J]D|| CI{_?K 1D1L[8Ixa~-ȳZNL]Mκ[{KjĪ(/>!>ns"X5@ =_!hTְfo,tF5@k5)_OۑsZR@?X}>nl=ωzD/iJIk)^=LO1uтdr:Ggl6s" ;'ek{pi1a2!QYZ%')WH=[ۓ=]Yq>)t^B9N8pgiCOg~s)=k|z\gB|!>MT}_{oelɟN?s5?IW8CqgO ]pX.p7HK?D s;멅z+=˙_rU˺!6&3a5e+fP'}KU$y;@x~Hw+nWwƇ;k '_y >b =F6-֣-w4|v;i/tMG7_}?5LVw 8'`VZˉzB~]{(#EFS6ޏfD~>Δb WkpnEK ,V7/r'`siE4TϹ=8}~™JX+Z+t(z2&[lH W1[Msn:TGbğ{~G?U{ ^m7L%>Ss0{~~r7HX> 3]=X{j=}?gY=/|dv8SS,{MnzZs=sb /TWWZj쟻1Yow ȶ]Ocf]нΜā>^zֻC@36Z5$Չ{l=-˟ٿh/ COMRC+vwVp>=1u^8%Nt[Hm iNmr7HMT?^o){FZS gs9Ľo'b }ϔoIOi CoT`Ϧٯg ,-rv'H`g\!~46å7S9m-EOV4܎hh)oB g٪hiʮFz`l fDq< zWmo?+ya Q3s{PvvZe b&E\ M ԘzBrSY!O(lay6KߌN}p$ 1V_B< Eg~s ,~{_w@T{ nLBCU<#TxoMdӥ#r~"tQ%ptݯ7P}+F4?@CQ<='5wzroܟQ~3"N5f9;/9I{糳q^;g}H:>>'Seg3}f}a G~k-v/O-A"w9/Sã\s?'^YgĖ8zNlZJ}`)FMsN^3IE$wT7zz~ f̌o!Ftg؋`K=N *ezxۢO $x{Q_Ž9|l_z̝>{Z>es@XuzT"z5,?w1QYlu>3]qpaO{{Ǘ{Il1=XV]_PD50e#_/Fċ"ovTe f)Dϯk{~D+4˟-7P q!&q?{7I\gR ~؀n9 Dǖ#t_NrOݠK}~$g}-fαz?,7Sȿ(y|6KP~:p;_Š};Y7Q\+f(7 WU6#T_ q-J 2{aHuj/bzչ/8#h=6ԥl@\aV<2z4}l=HX3|@vl?Iyڼ5rAYIp{R`4 n!>fVb޸oe ?}J8DYs$i c{$.&gnzHz`jc*V1~R{lade޻ߘ*J\J evxg7D),Fg@#<gTw/y߻ 2!*ܬw>Ӡ=4}ЧoVH~,AgֆpyΕ{K^#Ms?;7Mg ʌ5 V|3֓b3MpS< >bV珺җ3b>\ͽ ~&4!?0u`/(Yjm0_o''^8Vh :KstL9li»ߵhOw?7n/ܑ"?vyN|k£P ַJBM`;»n&;i#`z@wW}ΪY*zuE >W]Fv@MJ }q{}k@OΌ.1gDD гO )5q^|}[A{!!"?FY\ˌk@dσ `4̚9DR^gF^4k|;udH}_ 3k Z]"mRŒ! )Y7?H}Z9)V4WW%fF?<-&{bׁ+t IJSn4C2"j'iJ+ |_y^qP1eÀ]َ-.ls_j0_Ns_VbCXo>0fl gznCőD8{tD<ecRl,Lk6P}Rr\v7손 cοD5`W"|gbs.~oGH!ᛁ?[xsl]̨s"*y?TTE?x*kp&c"v5P,gQ/!tأpgȘ-$dzǂ;ݛɌ_Sp43,1Mw\ցYKLͅ\{aM(\ Ԉ]S}%2sCS ="xf`/g)cbzmf:xJgZEQ֪ 8FـQro[XǬ/_'(?3Q-ʡ9b8[ҵ7u/ |]0 7l6J!MuXoK\E؀rL·'GՌECPuJ*Mk p#k@jT"4cE?l nW;Rҕy`)o/v^i_nfa8V=}2kgJ,fe eR>5/)WIgT= ]Nك ?$µkQT0BlQJԇ򷱔uvgJu' grƬ9OMq3'uYx.0s&L91_jlmFl+?Yo#7M_9t(Ǹ пG_gE3+CyτSɴ=C!+%tA@.wO_QsIgIπ6kmez0)_/(0G|4YA8/׎*NRNF.Da+&R?s+]tb  3嘸{ ) 22H^$jҒ1kZS~R`X33/Z9%UK[lstM8kB7;lw(IAdckY-bf#{JV-]0v77mײuP|џ({d?=/?I&j53n1o*ZT>ew#,WK 8Il_cs+E׼NN@фmO5?n+X^ᚐ?;63d{(:@3.93"vM)#~F|8z sP>]`ώ$Q~,ң;t ^:` c.^tV!g |Pw&F./4Xw. 2? ѽƐo Qt\W-("GZ5 ؏5HU&r_GV'mkBT!x}Uw鵶e13%uleY`ٲZ3@&&$9׹;\dfKVVUNY[z7 ObL I{ocj?+y0>MNaMFx+nI[|B詏`uG>$IDy -<"jPZQLg پp;vV?:|<*9Řf0CXh{ u_Zl8 oip&ɂ[e,"8\IS.,.$9z^ WaVp5Y’\L8hS|rQ_v ˌ<̘)JM44|nqYȟ஽l]D\jSx*?3yH)daRlSZȆ@1k4ՎF nW:<|_ŰLzaP5[Pˢ͝@h: 0M"݆̆h}7- A6 !1^L-_|zH:áy/j | 7qlu>_E2`MY|'}>etf/3x<i:@^( M4c|c}&3cC%zx2Xu{"}? vVT2rp6`=ړw^GAfD:sz;AӵQ{)7w18;Miq(2B2c TǑ=*$?ꀹ2oH\s#E}-Ǽ-A&*rǣtb4YcR\1c #3KǣFǓ*D>WGA;\Y#m+UGAvT܌}=/lʠaN{+-Y11 wNx1U=<72dq_ aFّ~*T7)\,ФSe~"|9/wq;WXtvCv!<6s ?fNce/ =d<&5p+ʀ{Uɶ*Y?{}xmЇ`J.X*{+po֘,hC΀̿a+ L$9 %9R+Wh`eۄ-z"}9.V?y|b="R:Q&Va` 6kaL /c^PNr25 n#;g|RZ-":/g`D>1'\CO{Ko7gD߱/xhq\& lEb,cH:= tug ,d`E #ߒ 3:9KP Uk&/F"2g:1n$֪sx?(ڌEy0$t}[{ϛ5-Yۑw'-@̴ P8Spc& ,"дږ wïbυnSb]eF_L"3һ r9Lj&uuBcG3`q?ڏb3<$#2%2 FX*32Q9ui^rg5|z[~Η+N tn଑eeX-11 #7&b1D'#Ax:Oe$_HFh ^@@I't&*Ő s }+!+d=I6ZHgDd&T?́P('?!A/"b<%#sx}'?삊@+wdWWkQǫoKsM^ڀ2qb[0u7أ1|<ߟmP(k#>6A6eZ(3,NT[-cm| =v}#Rƣ>m,b/ecwO!^ڶBcL9k\"66b= Xsc?OVV)gB?؃I&p̜! &9A5;`m[\emm0OCM2*y31`;K`{ǝ\ uO>ag>)o)x ՎWQ0M?fk+裮R1y$xzQü oI+eyv"Ф3$Ϙ2 >T̹pS VQC֌X3S.|Ȗ7c끈q`B/lLAoȚr<x. hUw܋yçz0ƻ<֢leh2=r&Yo<#FOG=g 4v9U%/~ZW0X50CxrB1Eb ,?kÞߐ!欢a/\R0G#"ﺃouh }W͖/ưb;l ?uT9k+'_ Zk h1ل+6/Ę5Yp>|FGy?gF}+_ ?_nFx#Up~Z)aMoh-.#o(X `g4|l9r:7_tߒ #{e0{W~8gh~!`|ZV1 {X*Z#Y\{Aάo/csNek>Bs5OydX} _@/&ՙ=1>DKͫWac۴&|"|]ivetuC'OB/`]82LjV#eP<}CVT0JbX[d-ěfl tB0Y>\*{jt.B ;Xtx1CB>{\IaPO=r57(z&N?5 /~ |_ae!br1 kr!XֶX 6kde~ol;BX?Bc={ s?RALFGtMelGCZ>/8KcRӮlZ\j l2^D9h8׌ij'YnΓ,9=b\#` Zf 8F? 3?m))I}H KHVu.~| _ox+uW\˗/AAvCqJ#(nTh4CW.;ۙ =ݞ]\e)>^+?+2SG0{]dw 9(3|j{ax >XZef!h`rO Oth4aaa:V2GmT.r!d)< \Cgc r NV;R:'5 >G!0ESX-= 𰧀a' ?Q'Y)@qi'8Cl>Z 9 "HKTXmpw @?h[埫_j A֓/4]f\D@(Յ) Wݲi9ĆA%T|gde ׺v@K%%7[I&2-Gtߌ}#^_^R1D0N\}|>:`D@p̭//CNkr#I'Xod@ut0e1;_GnV wVZߤB6~XwBb#G"dfJ#L#0?#92?l=k } Y@- m9cG UJ_~ Cmy/γ 3P#qe -a>("1A}!z@({ eQ6l*}v{l<7s%#Og 3sFTĀH_as_;5R\Й?L?ng p?Oy5sZx~(ΈP(žz7 |Ú?μ2RٿV[VdX&`ߥbbOEq뿕RS]nc }3&'3@iƞs P|fs\h{7T͐<3/f=Q,m;~O\-񏻫h?39T' ^ :-g9YQx9hpGqYgh^xQ?m=Y=ڒg?㾸o=91B8ڦ_!OyKC@N竛?KgHfUy9S?)k9#l}cbu`?-&?AT4Zj;.3<%3{V+m@F}4|sNl˜M;w;?A?)įi{,e村{,ɳyq|W%6kV(Lw9)V5 C?}\/(R^/-5S;9䔅I@)WͲIsB&BaN8;4?^ Zg'ǒv O 8YRk@l>c܏8#U|op9?inn"P2;on4 ]<,0hg?YNOjlӝoI ]6nP{Ahflǒ| pd}]B/<ؘ†b ^ [hg4cCk*?nn>2!q8wMc##>e瀑? ;N]cd.5Ϥs~-_8n=VE륚i;#zE8PQ'~H cq `! uPϿ-F c}DaUxL 0s]ƽgc~Ol.IM}uJ* ܫe p?B#-B"u? |[#9_'c~/@Q$zZV ¸@B>Ԭ>О}X#p/xoklD\tnH#5׀.J ?`>t>x[p:ƁʿÙ]?5eTX,25`n^ ~Yǯ^ lݭ`k=wJA.@4'r6-{I_Tnv!+hܺ=s[B.Ac' -ş_z#?q@iAϷLc?r@n>~'H;9?aId|#Ooku?惍ƽ"y,?#g'Jo ֽP)s.-f>6Un)]|~68 ”!~?s^o̳d1XǽyRSD=9b8Z]l=-7kP^L<t>l@kC 02jan~s/Hr3}h轭#G$ݟjUqx`+^`?9F\mj+(?{\b4Yo=xP@?뉷UǾmj;ٽ\?Rߎ5`Q ԔD?}26Ca R;Lo!yJ3]=6rs?+\åK .ϛ2lO10#ݑb|=1C-5Y#>6$#b]0??>W>&*>:ノ6|$$ I<$O /ΕJ/ 8Ӝ=Cz?DS :Prxg>\׿o~3Z:$(p/7P%kcO8Buk1ƿ,w>0b= y1 =1WWcc=cJH(Qs5߆؏'r @[oAٻ=C䬹%|!}%u~wDݳ[$]G`?!b9֙F|qOc]Ze7jN_ UܳL><.Osu^ 9%}~<ӱـ'>磄;_WG&roΜT ` ~an; u0sahz?!:>|3t_S·$a_X>WKo}?7}GΦ'i?e< 2!RǢ!i"ӡGD=cuP;_??j+m&{mkBTACx=JAQ*6cF!ڙJ7l,l,\;w=/<006+5jjmצmmw@sހOgހOmz??Q߫N7?{;Jo@'<`Iz txq@oaצmצmpq@a+]/` ӿֻHo oހOG6kJի--mkBTRx1 !A`_ h8k~-Qncl%zk[vG~?3Dݠ mkBTWx흍8 FSHI!)$FRHnw HYx3ꇤsaaaaxIǏ'U{o_ھgW9 o'GW {>~Jlo߾)*/N\ϱov[iZ_ձaJΝ/:6O- 92b?Tlk%?_21B sY5>:>c=1Ow y^- ڶ,XzusM#גU]>H_yYv!ۉ_mi Rus]Xm_g)YY)m]y,m z1aaaxEߓGקo/Y\k6xjgH|yu.\aæM&wk#ϐ$?]Mo\Ⱦ,/ڥQ@~6s?)}, l gX #vQg Bٙ^uのuhm?}{].~}v_J;xogJY]޳@.)oqC?}>@Xߘ'-(W? źvƔOʙRv[K?[A}?-wmՑ}g\=c}M ggg DŽ-B^k_g?F? v0||؎=ǧHPgs/hؑI t~{n^}ZyD5XWvO)"c0vY Z|~_%/,p\ɹyΰZ/;/xs_9?Pܯ5ݻ\[y|č8gʱL{? 0 0 _k3>z_\S |<)b|7aaaxn.ta?l^Cvkؽ#~e)3<3^kdlc&jK+o"e<.ʞ`^(3zu l+6v<ï k7]/lc[`On}򚄫 G뎱zt^v2)?;Wmr5ocIz?Ozx{&!ez."ѯ 1Gg{+ҏlw<=}GݽFƨ^)zIpG K֜{{e G12ۭqiumf>.}~a? 0 0 [u+7Svq֭y΅ ?ނ}XwŶv?ߩDZۓ-q/?߳=<~#>Fk"qzrQo 9r,nY[;o:)@-`ק-7({߯S@µK9֠ɸ>:n3 _[_*mtcmC>qSL=<6;ǫsaaa{xˌ\ފpx?0׋#5zяc]x^l򼠕(f:~٣^lin59W~\;?vn6erUbS~v^U O7O(|;+SG4|?f*?rW~2oNٟS9~daևmH6mX[J~s.ym4ٶO|Bd/b5ɿyU? 0 0 0 0 0 0 0.P~*1@G\⟿KrKXs2(ߥ纎J8'>X@▼QQbqwx b)_K|v 1M6kee-2Ǜ59?K^E~9ϱQﱮYF8N?~;:=J<-tĒyNAgC \NXKs)'^Kg\~2}6}Գ)n]Or^j~"{p29w6/.z-v:+M{WJYZ굢`% Ҥl9ힶկ#OUz+U?;sd~vND7*.Y+v:ye;8}~|+ÑޅN9}{Bƞ#txխsXɿkSV/uJ=o G<ջL'L:D]6jfgLz/+ؽ[{rCMYq~[{yy czA;w9zszWHVax3 % mkBT]px}[TYS}/YL$Ifr9(A@ 9 "I2sFTLl}gw<ӳ~7`P@a={ww>U{a||5558{-<\?XpRQTT$  o߾~7Ǭ믿 7nhK;''!Kϟ޽{'<wsyvV28deXȖ\~'Ē!q8ODιtD#3-ٙ Buu099)/bS%@fF2T~",nN'={'E?+=Z>|?( NuiX !\=&_~?~Q/? / [?._{>'`R8 :S:0w>c]i^ҀvAX[YHp=M[{6`_0<"X K(Ÿ Aq^Bx,ĦG BJ~Q,T 5xpeɋG?YueOѽ D%9+zg 7z.8x 3#3iBeS>,_<%שbb_?246 A160sӅfX`s`/xD8Åasb=s'{2S} ?g4j~碢wSx{Mr^{̒Q}U~m8o>m8w l'v4n7*UpӄK^ւzJ gC.s͐4M'k6A0 0ى#;znBn 47cњ,u,v nBr^PV%\yQ~SXh݈^ۡ{ 2]D4D|>Rƌy9͑7i(iS7PrmP-*ء=2TsXеt rv3_q\DS0Bl>¿Tn,ly,cFt1a7G.Mshwos>u!N7A857w 'Ǎ:nK&(bk]Fe3@d>ڇvN.Bh<yWkhOΑ\0FdaxсUD{jDŽ.! C(/nݛwObz9#x5/dCH8G:CH6@:C0Ӂ# f+׿ BZ 4&_-{勑>|Tt,C;˼jM!pNxkR㥹Ҽi h~&ߴi%?n nc!>t/qBDzCkE{b`(BeSdOHQLyiF/Au,.5?&np!8.q,zӺ-g Oy)K)&Yx|G]' #堨2ɓ,=o1u`"rcd/au//Ƌ3o| ۠`9Ö f칏BxA3('& Fr9iE K_5#p/׆E.wP@<32 ӧyd!͝ܦ.7O۫?!l:4]f]gqb8+Nԩu%]{^[5r.y,|ז 92GO5HZLLe%kN1*{ XDРުq?i>`?CGV/C+:[ϓ]:D֜Bɇ_ $O1oZ[[6m? `S FkùF U{aW <5rHV%-ӄ}^8UkN_óqڮ?{Ym1a>Y\-ʍ-j\kv¡Y]{>:5FLkS~v߲ZLu.oDcTR=_^khPzV#'6FyFשǹ.ՀQ*' w6`O- {T/a'd]chR̀bbF'li!I~za)GxY߷Wޭa{pnhD5m ;Yov Y*N:VSHFmߋ79tpGXݯ IWrpیΛe3ܶ@w=νJzQ| d^ڼf׶Wj9TOU]O6 vGMPeވV  f SI0$6` %@~]|GUx;Liq˦1M汢pArR\ :O5h˘Uc}0se@ ?mqaۡM֛`>Nӱ6g<ƦclĕDLMt,yL ɸoUvM9i<ϽR ys5TQMJT3WPuCtN+ɔnM/3<=m0 jp- V)ܜ38o汾>OxX쁱`6.avp41<}Lg?KZ#8a!sDT3bGBl Xt ]C~F8h :7@h2ހ=@]/v܊#`L A݋L3H1Hk|pἭ q5az` m0d߿l}7BŊX5M~6Vj3#V),&Y}гIDh0 5o|}臓IH?@d#;-9 C~V8 #PXT%l Z՜Xz !Q^@l!!S!~bS8q/Lf1cS>_uLU`bu4s}S9p.FmE[rpi \{myy|O:pc$]!I Qp:ks-\Y}(IӒx^nIuЊ'/ml~Ͻ5Y词ĭ؎dG9A|:qgmK c0{ ϿnWx~~z nKY} Fpr)UG>@j(mcޥËGF^v,ϋrb|4רּ=gErk8ߖBt<1ޞbk<;k\gVʚy8G*6_ !w/c!:rI+Q/|y5TY>;l—T\eQe9@>=)yDP]Ή>w{xmUl *endj?wmR c1f4&"ʃk% _&s]1]rK?ƍz4!s?{c( Bc4-(LEPol6⟱ZO\-5wqi8-G/OX[YrNozW}\:*UrcͭՅSDz='ٛkjucK")!puq9>b./CǦ{^21H#xأ՝k|f`Fd,qǂ{Y|Kby+X*Oa~&w+e6zO{Yp% q2u}!mm䊒p 3Φ/]d^?'|ko_mOz.qJ׋3077-⢽Py =a kndE̍4&K|Et~V u/t7'\35PO輍!O /&\BWELށ^C>\lWH+uj]cQ\3 d-5\$򞝒wڂjjMꯊruV+tazt]_.4esSl_O@Sߛˏd3E!=O+6w,ggH=_T瑾ә=W)tz?y{:_5%W2>#JIy] IO.SWYB@{0H:#^i37cSXry %ǰz/ ]V zIUOBrU|NNߜoKP iSmf5yܾÛu%uݑ?wݢ]/u+V׃WOו^t]@~u=fm뺒ᴮQ.`-+_ot=~麒xJv]s_R}׸ˊϘ \B#uySf0s]$\(s|`1uzuuy L% =:4*u]:˘:ӛ5H VϰӟW+nHQYg?uO}T?h?/|g{d: { kkt=]q<=BYw939r}߈,x`J+JS." k:ã7Q]uWǔH kH׳Xt݊tJ]_xЅ~麝C(R4bu0tlx:|eu=+=Fs9"Fu6KWAK # t6'OH甐 Ŀ$<cAHI:{,J]_H9 ?oyq07WϚt=DM0t_9f-]דD?7GPq*"]ag+ƿ$:KլEȰ+\ˆ/Jl| 6\y({.#YJWdyV<:bc+l ʳE'}ω\ }PpOh}JypOh}JWrE'U#ؕursO+?[}BAcy^ A]lw~>f!79qL}bc_{_`)}E^+XwMWT.J}_];S>w&Rq& _xO\ue@kycZ_tLw@Pdx&Xy 4F Y"N˲g>qB^%j $آ,CWk):.)uXcд޼ [`祇DUYΥ<謏үe[uhc;v=8j(j(,ԍ6S1l|PA֖Yp"EϨ.{åd/Lbv+4 7]Q(- \[Ư]Vxi #sصNELRh `ᦅc?TcD1f5L9_Oυ4= 3B~ע' 6k{kVHjxGW9[0V{YgWH{VMD?3&g]\mkBTaPxݡm`ю,I!ӦTUT0.@w&lA0uLw73 g55Kӿ6kӿ7Lo@BUzgzoGGGsMހtHo 0Oo@GGGGC8`)џ'Ϗ RTqHALmkBTiKxݡQQJ  AfŶ@ GBH_AK'&b8&3m֨__a@uހoOUz2`6џ/??7 m~/GGGG8pzހtM*xHk͝\mkBTnxݱiaRE0bg!","hR V6"d7)FMMM>7=MПP,`2`"`GzGGG4kӿ97?????_7?ߍ`Oo@GGGG:?<7KiP Vr :FmkBTx?xݡJQa/*IYp0V\""Z &k ˪Q슈½{_s8pJoyFMMMyMo@Bfg ?79No c~ހ,8`?_[f _ џxտv27y]X;xLo@'a#`G { & k{M 7?????_TZ6 FmkBT3xݡqaSl P@TQu6a3]#k*>o8Ox)fAk__[;>V _]z&`g@Oo@GGJӿ6kk"џGGGGGz<n' ٿ7Giצ?!5mkBTxݡJ`]L*",-UD1Ȋ+4L0N7`VۂŴ+N~by?xG;5Vz??sހOrހO> ^]7{@,џ@篻g'~@*YS0`MM?7?xKo@G6ܿ77}af?Ys?Jӿ6kJ =7YdmkBT}xݱiQQGUbPFH@ZV"CBv MS-)N^RSѿjM_[7?}~Mo@Bez???w@, oGGGGGny7Ho ؿYПPfހOO ?ۯqz??wހܨ_7 F<mkBTx-Jaa"*Ij A0QA&܀ Ap }¹}kuPUToo@~z?ПPϟG?x;Ho@'"`4 | ?7w1{ӿ7{ӿ7{ӿ7{wk loF& Ok? '_U RmkBTx!nBaQA P  -tM ׁN~Vb8&3mZk/MMޥ7?ܤ7?ߏ o@OiNGGGG*`2џgiz????=t(MY/ծRmkBTxݡm`юmB0a0 , C|Eulp;|>55Kӿ6kӿ6^ Ow@o@m@@}ހmHo s7?????/?џ'- ϋQ@xjlgEmkBTx흍) q ĉ8D^>׻gI@XjjgiЃ`0 `0 ?ϟ|:seQ3|ӧO|:2|.};7eGFO6_Qv]T]^ˮg{>pjzkuo{yye?{-x/ D:3D&򈼹e^Hyi#/OGzϪ߯_~ :sMe#M3Y#=2 QЙ[\s=E8}E>GȩT ڲTg-}VfoSVwzV}./>~!?U1<#}=F[ ~QڋBN..+푹^edLo+[\-k dW(}6q$#?z6Bөi?L7!3O_Q}Пuo[=tkȋM!'}/Ƈdr2_Cﲨ: `0 :8o=+8-4}۞cĥXdq{bUq©ήm!ƶg*ΪU\z[GA=^+ru{LV U?)V>ғ)x|Yҁgi\yi^cUo*= !TY?rfgWsʽVn*VX#=Fϫ+[F~yH\L~[O҇h5ݵTow|Sfӟ+);F;:x )/OS yUo2e)Ve3'wgGg=J^`0  ľu kU,Ksؑ5nY,bXw{ w&3QהNQev ]ƷgcH˞i{A3I8hwduwUIWq8I>+@pQşGcZ\ƪUߝ]/:3d;ɫ:gB9R|GW~w2;fzt|+i5nΟgZY|<1NyŬ|E7k?z/k><=Α}N΅>uWydʬdz `0 *\?W8GY:Dgcg< 2+'W6qn؟{ru"wU쏘~c#T?+y{Q,,^qF/Xv8.֩g3}ȸOP ~n%hUG4(_sn|W}Tg&x^c,Fѭ+ <#+}/Uw8BRh_|33!mr\7U9m({ѝpvew[xG]߱?g;,nҽow8]וb?OV=Z_#ve?vN_WrYLo;1g9pV^G~>[_vNOS3 `0Q[ veO\k^8֔v<Zbz\Opbn$~}oz3ј mK vU]^iNWA#x딫jt q :E= z%օq)CcYEqyRG-+u (K\hP'*^ء^q=m=y|Kvūe\rȊ4={W1;=ݷxp;o@>ȘT\Ԏ+C=*ɫ|GJOCW]x1.ﵠ9_Eб Vq)v(ʑ}[GwǺ{-oSdו_˞׃2;iT&w*w:g׭SOsj%Z[~_˯d֮+w]7 `0]kIu+eL]ւoA^;=GR?v쯱;<y o$N1紈=:ߥPVu< <&3KyC/4r)i=*/|Ύ^]QNН1qGw>ù{ ?Kv:A}E:_n+{u=rq͓̳]>>d}+|L01`0 leg:׺񶊝`W,3O?]\9P~[kOWiGc~)-<w.3q}'vuw$Vnv(r52S;Wk_Kϔ8B/hEՠ'9w?K;x:x<|@cϽVyc@ۖSw8Bq]=2lBe6V}eR( VeZT4ade2ޒ+nYBTqSߔ<[&=f[|szP)G}{Zׅ3n7jpWwftEw[ǽ;`l? `0 `0 `{~i`oLy>uoi\qK|}7Svu9G쯿c¾#>,jow{ՆݲL=mW2u_8دjo?kD߱mw>#}E:OۡO;y`$jwmkBTxݯJPa/E f*&(Z&LYIE,lVAl 5½{_i ٙ55Kӿ6kӿvހOd7`'g@wހLqz{ ߽7?g _l72>Ho ؿxހ?7y____[[^Ko ؿ{ހOy'` ӿ9Oo eހL)`mUbJݶymkBTxݱ*pa"td1IVIl,eHLnբ\dsww oŅ17/v :Jo@B߿O9Oo@B>Ko@',`yz?Ǐ A _7MnwӿؾJo zހOd@d@l=џK5Qy]mkBTxݡmQaFATTt]-z\B7?ɛ\sb=147m>ަ7?o? N TӿSz????3Qe@Yb7Mnwӿq@Lo@GGGg7IRPmkBTx!aabTŠ)b(-!ݦys''@\ڠ[Ho i@Zހ/_Fzu(RDmkBTsxݱJP.%"$Z5¦@Ƃ hn ! x 3<8Ziצmn _M7??W ?_79Lo@Gvc@<`Oamr@t5No@GGGgG ^{U__[ua daz'GGGgQ_@#ԟ{ mkBTxoǿr8ر8s:;})MPh(`P H@0 b&&q FFg87N|Cry~?yn9'OZJ PR3*U" TCn KAM'Tj*5 nNp5{WǯgO}?Uds&ξY Y kK4`joH6W2=UdԃR F썆Z@]06;vÝG7^ >|1j n1Jca,V|TIptk3j ]`K ߋAp4փ?lH5\9 Vã6?O?~:PV4'_a $h 7̧\zZ`҂٘z\0<{rW ugk/4`KM a]Niry57z`Ce.DЦ"7t>8?.½ `i szMRcs{ͦ^7k{C;"O\ϸU3g APYQq/ Rys,ټ!dz0xc!bZt]Ր+Oj;t`ijjC鋡$ohVޓbf݃Ok<0vm <0| cD4u|6[*GW"o0oH󆹽f_lYͼyöݬ g08r\[T_sSBR=N3yCbwvBpupyBK.q;n(|/Sk` 5%BȘ_ܽaF(cWaзO r7 c/_8ϭ/hk/]k!?M"oy΅ U ސ Z|-|W\DXcV1 oK 쌕uP:M\΢EԞ#:w0of`Dsncof8\0B{&?uS-k`gg_QL]s֕ 1ݽ783Ϭ {ۦ֋4dn`g8eUu (1By ̍,!+߳H|?T`RdcX\x7$ij "U{А&6\Z5L &A :p1G?@ZE}g A8-&83;4jhHʛJ?ʺBbAf9\D:oǼAi*=FK:z`M6h?X[&MCQNHFV3ޡ3s]CUzcTs⾠DŽ}}{6^TzH9Z.ǠJǐ=.KLȱN!Y-fq50/:@4L ư<Ë)fN:7ڠic%,+@`_0-]Bmk]UwvIJ?rI{y,T@%! _4 5jKbfoE4qWM'8W"pneUlJc!3|juw{ BS/pĵ#KRrr@-`_yڀom -LW6pw9?}˂/9,zc^:0 <)y,L ڤ=;@O͏~CI!`]`:t|]zI;v58{*2ܗ^b!'DYNX 6њX}0_X )I\P"9%=T=414{%#d?wz7_lAPLX3x:C/+}LMf՞r.%Y/gμ'}^pl5A-x}pdX=TM{m@+̥Ra塢S#E3^|owܘxwq-Tp Fqt2M6Zcr} d?ߟ i.4Ztx<[д]ӹAYVS-QϤ)x#ǹ.g1/ A>}'Ĵ,HIK~./2}[8SL: 7!]?`ĒvL1_`-q "Gj>^eд G=6MCgRypGkIւ' wLDbu>:"8CFBsX?p/~۲w<}R{ +wڰk*ٟ?1 !=99|er߿or:q77'7-H׃ϓƗ_Sܾ|AE˗w{gT7 s!ιo{n`U.:{=M޺5 e|)'6ݸPW08rϞzZBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAem-ƆymkBT6x횉m0]HI!)$FR?6c>>~sm+vuՑνYu8uN?WP>1JsWiV_uKEϸ/rˆ_gKW]ױEYcl,[TYHT}xL#}A GV7^}>iҞ-i;}LJX&TP3T#ߨgJl e'=?͘ona|7>?ǐU%;/mN/IfQփz{G}?v✽3X~j{zTAO^ʰ>?sy|G)P7yހtNo Jo@'xހlџ3~7?]7??Z xҿ6ZZN頿MUf`jmkBTxݯJaa/e"|1V\,fQ 2X]ZZL˒MfN^ O?SvV):u+vzwg  >/No &џ~_7L}7??Y7?~>Lo@'ހO G^znz??RAzOgMzIz߻Oo@GGG?OR5?P?{ymkBTxݡJaa/ ƆI&+ (. V/`1-N>b{y_G5jjmצm{77?. 7Oo@G6`GG盛'Կ{Ko@GGGgV ӿ6kkqz~z??sAz/ ,`_Jӿ6*]SmkBTxi`Qqqqq ! COMlS$I$I$I$I$I$IqO9˵wOo]ss,Խu˲<^0 tc}av=7_{V9z}ٿ^s_y~?}ez۶@wE՟k$I$I$I@? R*f}mkBTx+JPQbIr 6-[lzA &'"8ds 8 a3' c : MnwG ? |7?sހOqz ?]7zހ=7?????ӿ7Mnw7{ O6ПPf,6Vg Ol+ӕSMnxD.MmkBTxwSםQvwyd˒%kwٖ[lc`pBRB503s=i̴MLN4i5~}nce!s~{~1vp8@U e^XÁo} 8^T@Eo]@::¤l/K5u_1AͼN2SjqfQ 7׸)twT4kKA'\W3]~Eݯq? v-YrOW U ?7?2t-KPw\T^{ ?N]d%>#,}t+dL-OkwMLgM %?yP:W l7s_ g!xAY'gOCP 9$K Jޥuߓ"LNg??G过7rͯ>8 mZwM' CrOhz~eh{vٽ */o@?V<'@S]4y\/ZtFcZP_{c4CQuORk W@Tt$wot ~9\; uF޽L`Uu_D!fyfa#_O=wwaّ }Kz,337,[ {B!g|~KwoO`$9'TfBQ5^+vI竤O5Bh g1WSto=j~G0c)8qlZ;hWݿa׼ʃ0p3^8rE?aJGjAw(_}N}5¹o=J}q.HJPBB}\#ʠp2^gWׁj >C8{ۂuQ/]CP^ z{~@sܫ[Q󾒾W}5E?ʡ|%@7'c{~B;PgN9Lեi8hGv?48& v2_8{_v* =iܟX(x1%ΞFmoOޜsCEuVYj7:5ˮJAB Ż3Z¾{{_.JN@w} +iG:+Mع{OCM`O.q3r:.> ZM¹>Ol~86@:T-16N<I9|E&X{}^>MkaUn>Wb>G5xo|˹dq!h{6A|!8/WUlߴA (+;RiX2!<$XCo t}}5wO;Vt_ZZ{/R1LE1)oYtdz|_gG w놘;|!b vW:g9"s )A{4>&}}+fg5nhe tR=Y` +wBB3|ߊsdV%Bz1L{nZ>ˡ\b}u&`+*(SG)X9ǭTU vo'澤Iǽ{?*bLH1[`wFk/_jE#+OkI9XlnR cP0z_^뽇{r/h θk mkakzh:. ^4n\wkv{rwp_fZE=CeqS]kZ 5^稸 ܇X_=!޸QOqٱ'!m${y\#Vm$%f:{0 v`ggÄ6,-|Mz93Z7}͔Q{_bs5jn:Hz%!}5yIZQ>X{3OyY<z$]px.z=a&GZm^};ڷddާX6SxO E+{>^ڟ*:Junuol~ڵd |=!24@1٠sB $⽨9݅!%!p(#ig+&տU4dz~fOo5o9w<JƢ &B54MԾ-7 =_)ȵ8L܇0n%(M`z+)v]k-57ycs ]IJ;SN1rzY׾u+NOs-u ߖ yʄ蜑J9?HhhdKGsVgd8ӂY\Nw 5rz\SҕQzݰr2;V$Ƨ9S@|=*YK81ߛ y}9uV*^1|]6đKA%hlSYL5؜O#S'}x-֪/r:=|5({3gJ IO<+xrF%L%dz$R whn_?[N1$C?۵+{ڿ ٿYx׾Nqc*,t/O'SE5 n(w žA9FC'=r/ 3Wɢkw/ܱ;~jVþ/ca i!YX$wq8~ɼwc!f+Oc+d_M:f˱ scc˿'{7.r?p}Y?<~{ܿw%nw-ȎY4`+w3W-o- 9ֿ+5+gqsC\ρV{|-3NOF^}?/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVB. mkBTxݱ*aqbD -GNI$-daTJ,&b QV;y]۟_SƛёR u*WM_7V'?~[Io@B ?~7٦7s@0nz7 _7Ύh:qn.3<T*NOO}}ҿ^<L&8:: Κ}M=#HL&\.7p\OAprrߍ$ ߘژS4JzCmkBT"xݡ QEQJ `6b5 i.~@'&<1GL55Kӿ6kӿ] _'6| oހ4kӿvznџwuz?????<_TMx% rmkBTx!JQabĤLPP)) P%Mdl7wr] O?nco3Ơпz;Io " ^7]7s@]z???3q?q7q@8Qzg ϟf _>6/ӿ7mџ?~mkBT xݡJaa/E9 YdA,a .xc lfz'W1Ox 8mwZiצm& 7?ݥ7?{ހ,`Ez'Կ{=Lo %.;,[pO"%dƾ(]mk``Yq ?Ҳ{oNQ<;?I9 Se0=d[ [_MN/_ }͢4OT0h~`Yzl!BJwsbJ.!N?SΛiN?ȅ#w3y}Ƌ1P'BKm+̑,(KzN!~}&[5uat} Fj?;+L$y2k7 =3ȟym/|azYy#N$)/,.kt&I䋯k=dck=)* ɪ!8HvN\h.6\B7c o¶kPs&v@aL(3 \e&^NNR"C2{n4I̋ 9a$6KN2LӂW .R.=}(4GnȯN} Lh-ePth&RSᰨM Qn[YI. NqcCU< TU2X}Gl-Wo:;!l L (;H"t_߭ ciG}#w3z¶cw#uG G rgo<E; DVHgGB0d,A`8볇Jqk!΋ ZŨ愜v! ;!~;LfggˋAl;Zg{Z뗓$4QוjG@#Z0Rvp~.)k#s<:%) ݾ 1 ? $s@r<'vB%_0E\ 2r̈́Uk.Fg6GY{tGh㯫bV t%ūHG?_r؟ݟ*8{ '\h  ^03mvi2 7l0l Eyusܟ`Ew0֛O|-ZBzѿz0)g&k'_0B'@'H\ st|aV+d| y}xmO Ϣ9@=y?ƇO07A>H-joUX(;*Etw3D ^ x7e2Dpܽ3 ޻bv:7PalIb8I7OφS9Mͷ 8mTGi^an%پgk6M+^zcDN 8bjE0ms~4lZS8I:LjvpF>fMbTV_}L>;X}>Ơ_Л)lЌG_-}]h}!Et3(O׉XX[9 khgXZ}b=e-%P{|6iwBfͣpNhiUa2ĤGk^1ilMXsٓZjA0^qMhWkի1UR~BS V%@pv}9k.aҖGp37|㋰g>h^zt NE)w#|AbK)sc `LbS7,|ӳƞ4g-D=+4wk敽PW$g*0v>st2ySՃa߱VK i:Z`j,bEs!4W 8&0YjYo G`g2u,}vAKμIygҾ* 4n_+4{}yn]]a޺ZUJN7)ZcdNZ藱QK ;ӹK\<{vU,ť*ɀxQh>&~aoS\ΙhRZ](8MʲP&GpWb rەX?7Hd5K VU!dX+ܝʹ ʟ˯$Add$'HJR }+`IX7%< ڌw`&fOs:!Z͝5R(b琸? APoz3JETA)sM!y$:dD]=yf\?x9أo W07I3oj_0/w-bB~OxFqJĹ^sSaM!;\? cD2?`nQk[T 8뙫{]JX Am׍g]}J>vowW1il's@۾mM6!m@RbfL \=rk%3#MO/> ?gAhW/629!:ˁ<@d lĿ@"bYVNzpuS]  (yS@=3 4Wh=vi?˂Wʛxgs> a!`(ePdWº;d"w t}z3It,YnX>Mcr>`9>Kg mQXzs炪Jkۉ}z(g0k40<Q8WG]y.֚Ί{٨^\RKi:as} bj04TϏ%E,?H~.u.|0i{:8Ƣp,9<ԂB6g:sNUN|uloKGg9G{p\e잷s[ҸY!m1 |-j3!Жq/f rvQ<Orq[sv [;VԶrŞ@+An>ڀ)g4vv3aIE|uעF]x(M!(T*m!}]O șLgxuw F91Z?Sr x c{1uqܳwgf8'5c7]v{ 8}SD޳FMQAa <gObV#={? ^Q︉9OLZs/k~%g(Ƈ:3f"(Fw=jik߾ϛ}?ACu>. .nw?7gymggl3.(de`{Z%'Y=F\Gqz/Õ(@9;͋ xCVل_ l0> hwQ2!L^|Gp/}v1{֨q g?8xѲ>Y.ɪ˿p߹6:`P7>#ۑ(փe5s9-ϻ;-4n5}s_ Q.9Wj-9~_ {w`k'ߞ#uLtϧ{½8i3[@a;q\;wm{R0j׿s!{x y:;DZאsi-![a1au:}'!*w}~jܧ j}ii+>>;0~\\;ګ>J; =1kpR]-~y3\%+U$.;<Es~tvm3^gsZ[ Io3~/]] \>D6o$$b)x]:_8 x=r"cIBfP?u١thh4T*ɵ::E~xDVУi,֡|÷bwM?ߒ3yy"qgO'ǏPP+/5iD.WUid A}a"q--ZOǑn65iWDH.\(GT^{"#y`^r -_Nɓ>((((((|K?%WRmkBTxݭJp]l2?Ғ,6eAa&T &o@EVLk^w* OW3֨__W' _7?Ko@'<`Yz?ϟ />f Oez?????t~@MMM=7?nzd3` vzmP/c>xmkBTxݱ*pa"tIdQG) &7jQbm*x7֧Kc : Mnw{g[ _7?uހ~Ho@B>;Lo@'<`Ez? AK_7MnwӿؽIo zހOd@\bc2[Ko {ހ?Z>amkBTx!JQa3("1(`hQ D\ǀEfd *'<-7[{͍ҿ5{ӿ7{>/ _ Oo@GGG&`k@yzQzoGG Oe? ?{>Io oMMMj8No t@j47?Mo p8Ho@GGG/ߛ@'XߞmkBTIxݭJaab!* /[+Vg<b,`1 <(;W Sۋ Tuӿn׭\o7Ko@B? L ?O џ9=Mo 0`'쭛ހOu;`&`~zIz???3?*Jj'`Jz??3zzݻ[õGGGGf@/Uӿn'|H`{CmkBTx][T89j#JҔޫ{W@iQ @"OԨ9ܜ]2ϓ'-3޳03{fMg}Y]@{SS'mK'shk4+l h?K6.h5Xf,e] k*mdEU﵁c.P5(4*#cp{/B`:B|bB:_fT8C" rt JߏÞ+G >IfMD^fip$>S0D#G39o*򅈍k4O׀UF w#^MX PߟMy.phMdCrNG8pw!NȀg)4 6LzF1 T@0t( :AUvK&} זQ(0'{A8 ~8W`'] =Sor7X ?}!R%Xb]9c-s4ÅSc̿ Aޠl@q Bd;ayNW: R6k>V4 F2Vkw#(̞ 'k8ˋ"ΌZ@sQفPA08Hj!1qJj/D{,/ dF;(偔hG9'sMmsT"&%Zh ւ~s̛<ql/bq-d{+i !Ʌ؅քPV n~S뵁j\~']DsMpl{ / '✔G$'E!7Lg׀}-P:EtpJ:@TORC(! V=Gd:R Z(i 5Ս k:|.H .ԾXLZy>CbǭgJ 2R'Hp%R#Q`n;HA)?ՆY2|!_iLsw%h`- l0}~DJGсvL8PqBFoi4 T=cNF<*;`|3e~M#Mi`NRA` 3EJ[!o$?`F <( "Y,©Fpȫfp4=S X%p gķGP|5 iS2BIL1R L'׶prrG|Qs kGw'j@y`Rx{Gޥ*rvl"jwLf 4 Z3i^?h3s G2a>"p0ԈL0 hd?=)f[,{D!l:x?i!h! `s탠?6?V8+AcPE$/Di]6H wt UDg93WH=UI Mf kp:#Xs )ni]+2 D k$?S>Oh~PK"}ICeT&(X7D*ރ+9o p]]}{SH,~]%?#.{Ɠy s$'j;4%ǎx&P[~ ݓ օ<´I|7{[>0W P`wGd=ysQ?y:ݥ_W? Z+-p%\E(3=\@S\ G= ']mBg F?י;ŬGv'Q;'<ΧOgբs`#zX+bPf9e#wӛ p; *uu1b3ҩH9 ?2>Wlo2x=^4nWD4oI ~QiKqTr??=Δ;-]ה1nOSwMmC&zN\t1qW$}0sXM*0z%δDxN@w>}+QROa=5|ݭ+(KL=r9pIg4ç*ϹՄs?9%/U)Ӭ߿?twuB{S}E̍WRsy=niſg*wLP&?7'9]6;Vٰ-ϦROp=?'^1'h{U7|da X]o)q4y&3SA8/PwEs?wu <'_?ި{5Ž*Pw=Z~q]EDЦ>X-cYnSjIrMЁ4yT'8<âƳR<.!w?Bo>׍9IQ@o!5 s N,oDb݀x IMywŔg755RDwH~TqS_Alt@k'q a,%DzKȗ_<&%ם!y.[N-X_P-5;%n .'z˫[N*S_[>#U;Z%03^̛Zb 8] ۡ%r:۩۽e15@]{aͰ@wPOM6fńzK<"υ{ K ٌZl1<$b677gǨ޽]fl͹jc&'Ho9aog_va3Q8x z=9"OoߚMOOñcyBOL`[|)_of6??#O, 5x5L V-+C_KybP-1_:剟L+O+y.ʿ6Dὼ"2 +?ʣykB(+'y =aj%8C&g`Z'>y"&#=6FCJ?lMXFȿ<AWG ħ@Ee,5fBbx'nskqXz;?n-[BTD@sCz9P[-&'D\Yzy+s/<`VE2X |nO%-Cc P+N"ZHl0` +`称C0w'ߪz N@];`PyBt{V9,=>ka/> V:?l>_ևYCLF2z'2EymkBTKxݯJqa/e*",-Uے  jjx Қ| o8Ox z^kѨހOW O?~nПP}z7uހts@fހO:W ?+LMMMd=No xހO`@@$џR@% 0JSmkBTx!aabj0"&Am YĀmcUpokQSӿ4kӿ6kkQzyz-o?{= Oiצmހtu77?????AvހtM*xSE͵mkBTHx-JQa`b h1| m `vN`L2Y|Oxpxm`sFMmkހO NLo@Bџ ~7? OyzWӋGGGG,`___[Ko ހp@Jڠ[Ko f=`nހ8q0_jӿ6dBףhi]mkBTѴx!na.M&#&:L YIX$Agog^{Zk/MM ~W 6 _?CMLo w7????3b0Mo kހ_?_]z????#O QTq~Cz}I*mkBTx}+(H,"H$"#X$,QԈZs>U{ ..T}6ڳ-F`p]k߅~b  О$wݓٱ|sCoA+q3lOx@(0a+? T,_7s\Ϙ^Bl1)C+k(FyN"8dPC_9>O0&l4Im+nwGrŰ)/tihf ѸX>E)<,6s45zb?J\<OM%O#(76:= ӋYAƒH Ls6MXBcX&ǘJte. 3.je(??Lj=%wZizFTx$kP8Em jAOހ>~؆B9 ֤8UKCvjbL Cy ;mj P. DkwUE€3ܨ8xUJs\ɟ+;}sFQ(KIXݛƨ 1 +KdX];Jģcx$D׷X`i @l̏rnm$^9΄zBGϞQ=nfkDe; <a>,⢞jk0B[p($Ǡp4 nq`XƓ vϵ.xHnorJ5Hu뇗 f a[Z:>36[g RL؍?( &w.7C#~B{] UW 71jk~ecGrD.=K@WDZM0倐0\xvqNZ ># BE )&yA}t?B Ym(WIpɱ |2+\2 )l8tl@Z.Be񅋍RSƃm>dIl'N adĢG3%#)?$s _5=YBR#-k"qGP-e"f%֩-ϓ378M9ϊ,_*n;HEBƱcl~ ˝[/sagIE2,z1t:kLș壋G){7ond{@rP>kwk׽ #kXfyEAB9uM4P=_lgW؇N#_nGpp ,ZUu6ȓVӰ0EK7*|]{75F\ԶzQz! uH>upT٣o3P)[^6` -d&*=%fY<^ط`_6|h3ء>2 Pq7ώ ,NsjF=B` 큳CiU)R鐏@LҮǧmb<2FHRqùFXi䎲OmGA}:*u f:@ʫRH.66jcGOpO- 6HKJU:Jǃv,3DZEƮqq7p?ȌK%ȧ$;?Qr6pP7`a^=R_)m>D3#£ _' Iɭu͋C-Rne㯄ssL<ȭ/R)|Lt_1Lk=rr 4/gEr~PnB[\g[{gYvRW' {Fem1{ wL;7&$xc0 n&u@5sCCձm8Heft x{q(aтa?Q%l4ςxmWI׆GC1kQ3iJh,KRO`ʲ4)%b6B8\pe;u)ko)#WSncRx{[sXv195_0Kՙ7>Tp5ٴl3S"؝LX睫[5m Q="u}pϘ*xbՉ#iM+@Z! Ϯ~jYݬ$?5mtu] %@݅:4h8ۃtu3; ΑO1A/r R*5i&j#Y2:$Z(ad@>'z L뇶6Z8|`6"X1_z' F-я?X^ A:?1;h/KVB' vOnFS ƤQ{=kh7MwXQp\v͓O/. N3HKRlK"q^Wh1wt h@3e6N|I;y?8t[[! $,ήLe"z%IކAkRl!3u8ځy?_W)AbCO!rza5Sn֗#<43y6"R߃CQ&>[# BHǽ{vekOTlq(UH͵h ݔ8,@tՂL{p/*L"d_y k,4 G̖bD>,.ok"D;|7[.DCA#ilϟI֬Dq]+eE _-- ڰc^Lq1~CCC9gNH8BkhJ#Z-`VoMa 9r$պZ-hkh ?C$ ^tď9d(8P݅]ڶw[wl;dn׆oKd Hބ(DInI M_(5)6H/Y1 QRk,nXHʉ?>df&6^EJmt{CCc`0ʅv5x<\9Yc}106"״!֏9dl:' 1H"z'7QqɌ#KR./CVgQȬ\ `?d1yuM6Ƶ8ZX]8^pwQE &1frRKi$GݜЕh3'{;;~FK37ku<pdʎ+C RMzƏ7)nҀ lEGyl:̑IoBS%|ЕsTulebA}Aʹ10A{KʘӺtjdLI=r PRg_LbR Şl?␔)![Fo wi&k^CV(t@pW2{hxHGRn͉eCbxԉ6GQd27\ثdS=\Ff*0ۣOP5(rZߙxQZ>~GAeN-jY7Ҿn;n?ӹ"Px}/NW:݊&׾:x" ꭥу;R펔 c䛅љElmG§a= h¨BG_uYnZ쫭FYs U"zM&:Gnu.DX5Xn;}ԫ%XO?~2&Frjj8 yA*W I9/ub)Zl: s 85J>~iI3Yԕ;:#hELם[ROd^GA˩f~Y!En0~/A Km>^WYq"<цF*c:xw|͞w%ehRgd9̕v3v Dgh>>?3hYDkgC(ʹƒԕSԜ| 2Q94(?OGQ34 fccPopTYaW(>@tX4`LGٞpɄaŰl\[9c26U M6f,'C4i?W~psϠ?kAKrŵk@I|>^xs?\`,D̒5W^w DMXf_8<%|8_왉pP1Wlm߃f?4:́_Ԕv M;k:p_sj؎qw]$F}y ,b'N=o0, ~M YR46+!}@~ujctCP.Y(x׎z?70WXFܣo3z0c8RGg0 TU򄽻w"/4֏CQ`[{Ocn]+{{ N!33+5]qpj' r9FDȬ)~: 9Gmx2-?sraG"yvUpa;Ră A\& ?#n 0eed~oq嶭!!DzP^H)>oȑ.ļԶ=Hy7S-M ?8ycߧq|#5"2Б lm#UeΤVbM͘jAc7Z ]> 4gb s 2WRsKg6 's8qzTT[R[w)I95xWj #!nN+zPڔ KgTE,?{^RDݥ=Ru^zîc&D'i74SJߔ&HUG[crͦ<׿~4}څh;lpAZ%XZ;tQ?yk1+Ƴu6[ Dc4Ɯ*dB#!}e>samhG3c^8u9󼵕⸈߂UyB;f "Yi=D =4&|C3g]~WgjhSIXU"1A5Fr4{AljwTt6</N \Rta| i>T.Wo>>xϯY{緷m,J{gg}v~)]s!?wXGFl!7U|Cnfﳅ:.@mq%臔Ru?.:aBֺE#Gg'yXDuSWNJD)21ѵVagWPqȒ s?¶@g")s\T{f3go^w:^"{d#!φt},nyWFKv„X4|VB~,˘_&fjp/WԍwaO H 3I`u1ͤ+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_W|+_Wݚw)UmkBTxݡmQюmB  @`$uMIA#H@PMKw7(PuӿneOo s7?4 y.?.џxПPu@8Ho@GGGG?џ7 OCTM@ ^4̔en]mkBTxݡmBa4h( I4XNрjgOGLv#%CZ5y- M_Ic=*G iL2gYh}=9q: 0Gykf(Ǐ);J%J~WJp t|=33Qw9(U6/*Vӑ2wт/r$a3jԀ̽ޠq\Fd% ?]nƥnaM!C''#-p>'OX&?`0{-悓Lq^1#Ygs"}rȿ_o,am* EIi#?*u~_z\f'y[]O,$ r2w4ga]16i3_#ҳd /q2;dt&>]*L)8_?/ęqm9ՀG91 ,?eu;:s>wXow%ng w~a˹j2kw/diP?ya[t:&4sm6/gWu3;h&t,ȿ챝k|v^C4tol5KLGTt~5;ţk|K[1x} ocv$ $/`B"4NnFZ;sԡmwLFɿ|Ay±XT~US%6#eh3ռ9h{­^Oh܉HT|Ud~!U#:V=s =RqΕ_v/:˩{A*vZŵ$ȝ#ο+_^ _Hh.*.hgCOru/%T,^3s \?ȿ#]i#9u:UH`gݏ9b[bKzYVXg]UXSՎ@ BdkS\gNZEi] #ʆ>ma޹Y5/$YT`Yh]|&8 Ćjݫy;}: _%V îptFϵW( B( #%~`/5>#C<J?_gھNe,s`VS}c8ԉ_jBZV>iKyNJDjy󠌣;4-$c Y6{<տԘr ?CjX_O/'>Fn117pY(_ܜP/kZzv+aerH~G;φ 6#TIPsR䖥΂Ώ)LP$@zBf2%QXil9AWO>5iT9kX̽;k~Ɣ֘?!Mfs@}vW C_/!ɯ $%NB2_#!ɯ%&                        )w"klmkBT^x1Jqa⪄jh"z OM~B ot^k__)џ@Kz=<7?7???wxMo w@gހOo9C(`sހ\uz?^ܦ7 ]7?????g4kzmkBT1xݡJ`a/e(C.FM&„i6ՠ4,,&W^y?xz55Kӿ6kӿvހO0`W? Ho@'s@azo _v72Jo ؿ?>Ho |ހ߽rS_ۭ{w ?_9]No @h#Yp?Jӿ6keP3YmkBT^x!nBaQ A Z[W%;Y9|n̴pZiצmֺ<`, ]ПPe@'џ'?~w)MJo ؿ;|7?????=_{@{ހOǟ(MS^ImkBTxݡaab F)$))|؜L^G+\~)O55Kӿ6kӿ6Y {n  އ_sz???qMIo@GGGG:꿿./4No@GGGG:?[4 .X|~mkBTpxݡJaa/E &ú$V&'Z F` V`_3xUl'Ox6mo֨___[7?:Io " _ݦ7?W /џ ܤ7>Mo@Bgo]z/ Ϛ.?7????kp@q/џ@t'`޵~@~7Lo@GGgmP?ƺ IDATxytdu'*VM6IqId-,k,3^fbg&>3q$s2'edĉeq2ؑWMırc٦$4EJn6 Q(Wz{xRU@ant~xBPu BÍ{@!N! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN! BHN!A!7kkkʊ8b|2&''!>(y<-B@{qniiəÝ;w\0:V3ހyE05d7®T4Y ߙfZ^p030<<.]Gy%<  G0;;[n^wEQX3 eq]Bqgaj]ǵq v%LMMQ!! 脜\.j3T**ԝ}Al{w !:!PZ#l6۱Wu 86p8@"TS)Tm;[;Xnbu3|rrRrZږDj$-DBяE#f"NvwE8!3 hۭWl3uH3SiLO =̗JX氺.gau'{coF±#(ILLabt#$~EÀ:!G0M@63ntTLM13@,60p󋸻F<'pC&5S$GyP@'qYܹs5^̦'RfX`vaw1[w={>d_n$(Ҵ9ܼymt̬\[Y3Si 63q0w1 $(D'0"/0p"^dDﺽ8Ab^˜uK2/6?c;L²ؖ,ہcmuvڀx75uqߏ[#ϏN5$G0|=y(\.תPVn3Jcl$PWÛW7Pm CqPaP $s71c`=kIs]K.lDži9tFnYh,hGBlB05m9Gd\~=cOK@,:Q@';BU~6LO19$ XZ{ o^wϽœ1@ DS bI`D/@8w>a a H">[|aaZmCU^q]؎qizBjV1Q-Ъ ͆Ys-WH8aO\S kr(j ⛛]̚AKYf(z޼zߺz;rO &D " B!$Bc-_g8$##Aw`t~ } .\7w\b-_BX8=iOxdˆճ ñar@i¦rN(Yo>Տ+dO^~O_y,$QߔP@'}h֭[j6[浙e03$ 7߹ߛ=B6%$ #5G,@Ȃp3cVjtV(p~㺸E~ԃL`;.lٻt]XDž:~kn ¶bV7Q/XlN_Y8Jun#{cK8w]mpX v`:6Lǁi;]iuEN;؎mC,J: [:v65T wLhy f|I/?2tsTEDSiTͷ^ǝSc@(#c`J#ē>D>Q˕[:1} M걝Tu];c% S80 s; ;wqR mahX5ۛ9zr1=rW}'=NJͬco7mfRlz*cxm|oy(!: 1b0b!3@w7߾z9sC]OVg+ե:by Wt`-_n`9ވ>G7G0uӄf(W l.QZonaK>OOi|ONN*mnZm6 FyR_}M_GjT)AD{8EEB^7,]g!:A0b h&,v߱xcѶO6KO ac7_Eԅv?Vٵ\OG1:iͯݲѰ- KuYꖉaak :*I__4zi'P@'^Ph . ޡ,34&ƓDoڭ;?KU=vd N{AA 8=\0l+ a n[Ntێbn50K. 8./{d{#rеch&e[r59FcxEP\Z-mҶ2SpK,޴><3vqq}mŚh:N+ao7uQ7MT 5,W-Ts #/~Ч/Px0+ qA ޥ tU^ߍ sqy(evRq]UF WՠO9Ʊ=2~3??Q缈y({*ͶjQ%~dH3i6̺)+o9 b 2OKUA@hu!"+)ʑX.^qJY\b BpdNΕ 0yrvj0|d8.BZoA]8B{Ͳmq+=wZec3_aX[zo]dTYtrA V @l<9Bħzr@0Q3MtZ &-`Zi?^~{^Ͼ E>ȃC|>ߪP/j3 `PQ֏N 2~dC {_g IQY90z91EEDM:Jo-a[%;5q;6tˆب ߾]4"Mkh{#9v=@@Q8}myin. <ϼKvEP5 fs9A*߽q*}Pޖ] hyf:0t߾#As- U@;]E0T_ {dIaz%|W ߋP|"EJ7o,bz*6㸮/7G~7ڨ xy!c, EnPӰyp5u!5F`"4V̂>XQEf^,hUZ͆VP0-͵sJw< HdpڼT׫`͔U^ee1W@Zd,8 sDX=0_$?$Nm e{p/ ,(b<'vٳO> ^Gtuu!$# 4M;Y\`z > bz2Ƀmfi_ qڑ %L"@b/D~uQ_sv*5,wX+ ^W^y-`p7`t8Qt]-bhӭ5**Ɍalx1q}?4ͭc-7W]ŚrCُͤA3 Dvfggt6P z_KJ> #Jc!#"FQYF?ةnY(6(z۩8(4xga j]va  @NccRUVxE+~L~4 AP"x P:,K,5=ouQL b3_FqrPXQ7_U(xfwoį(<|AjDx(!;ߔnZXϕ;%ȵUY#x79M?|UZ[K$q _3p;y,ϝxAyO?B=]P@']̬}AsX Si 7 0RW"H07'.0Ϗܽa[تQ=C|\VP*Mh@Wqg똷л| 88cnce>i\nNY\0ӻ%/(aj򂾨.x膅byu# `d0u$/qsݣ2HhՕur-Z~]R8\N@xhu wJN뷫Gmwa G؏}'0?:9 bvv7o<ͬ; <8'>줾/V`<E#P+T}]ӛmcKlik+R6vmoR4Q[( XFW!@ڇ~2c0 6E C}"\3s: h}f w)0x=,@iz_L/&B?&jôaD$Ȣ\# 7(mS7rxݶ[$"+٫u˸N:sJ+>>'06<BN+~631 `f232;r!P8 n_q<6gb*UI8PEalfq0r:J4[ ,A9o3P)8&A- XՅ:Q^'9l/2b"|^D(a 3ֺ>n| ޔnW:nW{ df:=iV//ZA=oA+Z ֙=p ͓5,@ (,6uKc>O ϶+ Srfu6d3iL(eͿVyēi3ø;58Fx]<a:pfӽlfk3v>yC״ۊ5񍥮oyTQ0 'خ0ہn|C;0*LCG( P{ bZ\ \33l__DD|e؂C<Ʊnl D(@Z&vrY4?'#ʀC0PTPV`j< LI=/#p(/JXP:?d^I"^~dk:nY(44TnY7B{QW"a<\p +5}{x~ÿ_Fn,Nf6dx5 |5-.tΣauN @&DTW;Cz\Jsu]CwV8[EvI;rn8Wst)ea___tox$6!xy,W  )|Pl,ace ;=%YFjx1ةT66v>7y^x!2 6 6@ r Gl"3a2*T(68o%/sZa[)hXw3+ϱǿzp]˘;C$f&ȜSY'wno~>C8601\X|$BHڛ/5su] mP.vځtƎuɌFR0qwƹrf\Wk/:þ #Oc ,y#lQ$ H&cHUT ]26V]]ț1D 1Guz+M,odFmzO*P|5zq\,o[ى;  !|s`H5n屲Sj=q]liZk=uuQ2tT~,"CٸN 矒.F/w@H,y<$Iߧ""@,h$Ф3]N.WP.{~?D,o4}3/#_*uiecpF횖7 dwm't'vn$*PE^ybCCQ۶Տ{' Hj׵Z9;_+}]%g#>?]`B;;;JR6 G1ALO11(^7? o~.ɏxF!H(_3=^=`^o _sa<Xt=.c=\WoF.h\e$1$CQgx"CD~LN ad8h4Y:"7v67'[FZi6`۽AD"3R#cP}S,2n]Ƶ_YACI c>d>i7tۥtc/'b!t7OQYvt 4?bciI Yz %:S뜆y}?3>h(?$rWLmfSiLCy1ŎsOHkGC!gs*{#u]r]pHPems6goRbm▁'2_zJ2BٷTU!TEF2(2qwko%!$̌ X¶e! Ffj~xv;n "t*ǐ~ҏ:hg,DZ q5UceP5M;O(2" 25DVEhƗwPW#84 i^S BJcz"}nmfBMw?v}*9㙟LBq=s֝%uUv]0pPcةYe.*]LGjaV F߉{c6p_s "D2<;GiECK#>=9kjG/l|Hf #;;o66^\X'+jk=14 Qٲl]ksXgUA%# ` 8|vwGR)M1À"v. r Z5 x~~jܶ|6pho7n3L5Hd@$6x/egx7`݂fxk~`[amDQaհ^cz|xB&@<=[ K"E@,'p>ޜ@/$2È " ٕe,bsmV=a0i@N]71{6XހiƖ<&'{w$p\(1Rԣ3pɅnX( ^\p' _w; ?ywnn<#CU^h1f6=Tm3J:c QN?hϣNn/-??q$.(j͑5`Zw7[}mC+~3M˷+Xծ&Br¥Y h|(?S[O38j{]e^LT+tbW4@"ua֗z{ap5>Lj,d1{kWo vʽcsw˜7~.0 w.h [KXyg~[?s󾢀~|wggg[OB,Wl*LlKXYFҾbrh/g?g?[m ΨxxshSQH0@l mc^jACε  Ev'YkkKqŇ4ةF ߯BDeĕi$vwuL\z 3$4trʮcg3 CF( J#A"3Hl]˞V7pzq|'%(jo{onv 1`Rci (I da;X* &Bmf]v1q0GyllO _fN]>Q@@~~ɽq= Rsx^ZˋȮ.#_<'A"3|寽W;ms;9c Hk4gyX E\]Zo{[u gvns׫Ъtоc .,b \D<9>U(~>U tڡcq$Num[ȭy%OPܢ> OL!3:T瓌|c F0jڷ G:4r .nliخkBFh bF(Kwvȑ?x1,A'#PЏģﻸ6ֱ*jSػpni~m,euqGC48s&+_bi%-l抭 ^Ѫ8fS a9$" t7  PtצZ3-h"k," W_u] 8Ʀ( ~?|>y4$F16H|¢R~ƳV-֥g?˿V2 ƀ,c$@ H0ꖉrN3|nfvOcP84WC9o@ \9p;af0&CN*K"TU0qӲvQeW5zoi]с Si)'>@(|ѾVݷmR#H BQV87 X\ai%JsߺnX(Xbtv;AsO΋qHvCHc dΘwۛYl,Zz[gG'W,1Ddi/Zi%]j }^?~np|_i<Fb'22"'_@7y9\l.}¯@"=lKa{-_biJ*v̄9RBR0Y􋸵1C S\ok1D-{{_5mU漀B9PRNۀGxWp:_7^T/+-\YeoavY~ĩ3W6{Z 0i{URd"a&Us`F;R)lwj(vHW7uuӶ)1iU]$"㕏dϏ{?U*X_lgl ˆg c096~?8㸸P"jrFF;;SianwRc# Ģ'O!<$fibsmKȮ,ev7gd@>+-7*yQoiXV~<|~,٥w.{H RfD _l}\(0t]i0t @QTHYQ#bDz^ fS3% E#14D b<^E]<bQ#\~d>Vg՚fCMxeۚkn.ϼY2g8Kka8,r/\CCq@~ j03殺_}+앏|T}+}.OqyKmlqf +lK[RRLV˭Լn,oB0b|$2[Q$ƽTε.loꆅ[t sH?;*uE  Cn3.r{bt ȉӓ#'_<Ý:PFCBQ|zCC^CR~ >TpppP8?ߋ4tdWW۶3/ Pk ,-oaq%V{*\Gt|*=cz"Sftaf9]0 a]/vR uϖ^ u,'66 t҇tvKͻZU[yU, T*XµKE#C=}j>:@t0Ǟ :6Vk𕎅jyoAszenuܣy?㐈n!8o+~sM^C ,~1=5r7WQV!"^اU^Ca{ 7ekVBUz@(DD $с ]WT|g+U51Kס*5v{4pQq8qm'g}{{B JAUNR@@?ֿSEmo7OOs(=5oXXl>5|w x-C>ADq(ۚMò|९ `Evf–ݶiH㉑i(b}ϒ(`p0o#_ǝ{ZsP|juUqce [=4T%T˥Z(b0Ffl yJdlTrk;0;Uc*W "a{R{<5rS-kNz0ulo{Emcmm ӭ1nߘ1`a\ۋ+a ߟU>Ֆi~Ssds|@h'L_} Fl"qm&a{+jsʝ*כӑ8.gObW'Թvk}eջwzN;`QәU#B"=X lbkc,DvueǑ 2ۃ/T'>38}5*?Bѓ4ܯiUP=j+Y8FkY\=t.蘂ظ6j_7 O 鄊tKCU޻Ye4͞éyYQ5X}8FGPHo]8` WvG¾V~h؛l4,\}%n<ĉJ~>ԇ15}O?kܭKU~*DID"30# JH !5<7֐[_ o碰Ca+2cyI =hF[[oJu(|^x:q[ %萚 vWX7z05ǍmS,Z,Ay6nvkދW˰vX` =2 IDATԉyⳟyG)!k(l4L-cmq 'AdYƧxsћ+\[En}qtOX[\q<":xڠ'a#>U0>噳OB 9 p"8QE|X°51}*́aBP(b||r<#>Xݎ po@D ?6=,¿_?/~O> [b~ kj`@Hcq|oWs3͓|7az oY4;x˛Ks6ءZ"WJ?{o&}~~g}}]3˒wx!`B$@H!$B qBcK%K#4W׾?NwuUWUo==9=󨧺=}}./qEv!؈GIu]Y]#$I-45K:U4>`aF I.''<{ma121yS?㭤^7#<rRܭBPHn_K*Rã$PrBuI\NXmzKh&4q.<_5쇸'$ 9䞍:/uHw_.Wpdj4@>pNum*z&a2ސm H8{Mv(/ 6UWɮxힱ0t)fZ`(Ltf޾ = jUAfd"}1&^8ԟYϕ&ulۡtJL`dHe'zul{7oDpht̰I*Xe$h$%ꢦ6ݐU'-}?ŭt1 3,^ZΩ׫!IObm鼺emTVMsϕ+s{KwFFTTeHl{l;U==;,76W9J:BJC7Lo<"E„B0P2Jh*X3$8vXXZoTJ?KHbܭ@ mw9 Y)rᱝtۦfDJdÄG%Ļ*핡7XFzKo{MQ M(<`?؞_-0sFUn &Qh/#:XC].R}JtX_[a}mI%)b$ʶ@__w}c:"FۜLCZ)SutM5LĵmiȲ$+ȲL $Dē}[*ju+}TzXc*=̉%RA HF ro\9Dw wBHpI^mYЛH^@xslOgkGhYwZW+,Iyz+M)\i@nzqlJ<D'=qE$1>NJt5$vuSJ(PRO9<[^J㌖tbG+IDbJبvPB=H }gćZk+O۽t#u`\&1BV^tnu 5m, Bx$D4ܺi<ܢ-: .EgZzh~QGQnA*n9>=J.O˯7~!r JJ[k/;ԁ^?|;!qUmxP5'ߞg/7K%q|A92ͭ^l?hQ 7t\dnLi\lۥZ7 "!T ub8.&E9@O<@9;W0$346mz3zTzUPR%K9.(Y_}F`Ou=73*u[$G n63yއݟĭt#K?bռg^7L^^X;Li&s+yR1.s.D8OqUʔK*"FZ(r遭mVXYㅧL"mȮ"=T=9ۘ|whrQ6fFne.WMݣt P̝ݏHѶCJO󓟺dzW|A9\4x(!J7ąvxqn.1.k 턤m'mD4$H¨W^)KQ7_}y # M08:Now0?Tp{T~(]|F\zuKMP6Vr9/:(itu]qq\.5ˢn.Y{_{Գ~~K|[>zˏk7|A9vIm0lx)WVb4f;K1]_* ѽhNMdS[Ca0'Ϝo|.ϳ47,un\c5f`hlbVә[*=_J 3yK@ ?tck$t vjzMK0nyYMiP1 ޗq 2Hgۇ:oc#pdHx\ЈsUX.ɖ[6lNFv8\Ħfl4t"DltfUOO8fNu]Wl|]%Dve}H,d談o%RmmTzTkD{Ky9'O?o+.D\/ZvtZIԊ:z k?ۈɷ$Mi[e4]oau}e,,n)~LJY>ĥW_lK.P/.:ni1l;-?J$YpNTg^!\ ^tY$f6ƶr$W!fSq,M p6.>k*K'εΓc¨69iF8DaRKq\'f>(dltU$5jMga)G@UH&#QX?lGVưz糫;fl]tEQw`x"0y۶)gY_]fea0=q;@vyϮ$Ν:_yϴ| '%7V/ BtNjyXUMg"n 3:ŅpޭtC]{M.SM,EunՄ+P( WL: oWbbZU*݆ZZ$ F2,,QdH`>R}t,Ks3,nY0ean8Cc z{%~*_:q@A,lEw^]YCLZkNCFp'ͺV'ձ%{>XZ)BF|ۿx _}/=QPBgum;f-B-qU6ܯ\m3\C`9eA$I"PQU3xJ?K%01O_|0g1q]Ybivi*Qo˱.;0('S:NTkV6:VZ/%ҏ1w/~ bAplҺAOfjŀfA 욄k261vuVLQ?!^R/7?]^ ϡR*)R]i* ) ޺7\oof+m/+[eJ4wd$B!Zj8hx®ĢzײEֲE5cLgN *$xTJEo" ٕ9p 8eK̭2=Jv}T녏}_yA% 4%7A!%ސ1|XeV%£6KݯVkAY!pN}Vzm}/_c:Z_}]O u{"Tvml7B3E޼cK˨t4ىLa5)WMlSw01 ǶY]FDBJ4^yuW^CQdFz01|,̽p0M9*"#e,,3=RbDJ猟Jy!+ϷckҨB~++d-> $`񴼹xS6ZŠJO(6AW_}^W|r0|?I\ja{⺒MAX}FH Fຐkte^u]Yρ,+bQ"0P:?˲7VM21xyU 0:y@D5&-,溦U.tEM{^;jyL803qli}R_v&j3 jD:LvADvL  RoDpYٳQ ϡaY6J,|Ȓh8ÁBE+b Ŕ啖ǂa!@q;'o!77ͅ,VubZgbbH$B4!4֋y%5PֲE7DS!~*Mgrq8L>k;p8F|7Ml6eYD"W`d{PQ7H!=^k7^NՇtCcnqd'p%4t)[3J QNmш™{yztթ+5 Ӌ]ץZRz`0{j^eb aYWgZwo*>{#N~Op6Hej" zϣoX&WUdY&*괪z& fmrݘy _}^/t'w?D_\FEkO:T奶1{U;[h(a"!7>qT˯YRn{Jͩ }KW'sWݩ'Oloz=ůmuX֨kNݜd}'O㠵ϻWDz;_u}oV ϡ1x1F{poL/`;.خ|=jͶ ^SŲ,JGH;ܩ$+Y^scL6Nj~Sbc?RgN91|G෉Ju!@ϮG2"59J!b&X%:P+FY|Q49|AX[)HFQ0] @H$P02+ B`h$I4\w# ` E{-l/@ef~j~~KoF*;f[j.6U.|ko箕='0<4. 5mJePp[jkf+[Ǧu K՚J#/q?|뽉œ Jz2$F;C\F2u3lz0^ E9Ϳ"&OfX[S[s W//HҟIHE.ؕr(;7>YFިtąrX;@]iQw ô xNz-kY7Rs=}p(/_/MpܴFnZC IO=g2<>紛%2[5鼠|VVW|AGdR,vQhT+W6}qdYR1;\ ziƣi2\N$Y_uw[_э] YTL_wo\LN(w{/~|;FzBpXHZ!V(]vn{lC@4~ouɓf bq~u$I"e}auߥeͰmBU#J4_Yq6Ao5Ll=\sNi{ů04ku8`(!{VU= cpKUE%G w+ ~OO]l>_af:R]r0 b9N+yJ' z7@p  \y}Vg܊ܥ\zDxy;~}Wi%dn}\ J O+Sޟ[#dz UM[\ϟWvS&F]!; IDATy<5"NjE"Z)R8ƞA5k\ƈTRavBmȲ̇|]w ˻Ga%ҶLNj7#`L2JΕ?̡۷# ]H*#?qTq\Dv,5k+⭛f$A:$Aas%x/muZM ge ?ƍė_yϸ ӡMQL뷢}*ɑ MȚӶ1lׅ٧J~|362q;d|AK<5 ^?m;IPg[V0YܛI''Nsr4;R$z33\!wdMլI5kb2#Lj3rv=7zS\'RG;>V ]J_&A,ޱ/}tKvLR֢whEDUl!d,6QU%(o3)kͭ{rH"%J | >-+ٖWDbN{Isۛ|ZI{-E1=wif\BKU/U 1pXpZbj:fS ,:/#nS\zagjTriSu[w(ۤGWdWMꭤETm_UCa^@$tx $|[I' rv]^\r>f;. N<^;J)CJצ<{.[>GG" qZw[,,3 _r.֛F~ZCRɑ J`MnZ7}ց@hK_rR<M ]PWk{;ٜfNcJ'5r-bΧ[sf= 0X @TU_'*JrZegzH[QV|:S.r & LDݧϝ$ɌL062gV,4,L30Ҝkvq,F~upD'?rxSrϋS:^s_bLʥ}u]y,aܕ%tFtWjֽyimA7ГaPL$TsO^>arrVClqc"`4k^텽&ZtD}}qu/]ȋ_k8}d'~/~ؘF8ęBir:j i,vwھ2HBbdp1MZʥ+//=gր]-X4.C?li>%nۺ?|xG~5G/w9$1>WڕqU0K(*Q+;ǭkDdD̕hiv%(GlNIәH%"XCP_w0}uj_b s p7>&Лw3SOa#O|3~xӣo%vg=<#?"_hcۼO/ӌ=YRO 9|Aat+]hwu]fn4ZdK ,ر8b-R,jIYCLUI Yo@35wgJ9P8^TTdܖؖK_^̮ 5O F;8uôl.و kzпW/W:~>pI@Pax,Lv'o r1t!kCܽ8Ԗmz;QUmDB Em;u41 ݴq6$IPeB0 ȒDb(ߝ ][×/NSN'' w-ir~$K:"|ӯ ̈+^{W["@<~d O,̮ii'BARF*#*ԛ D6U+&';Ǖt#be*IAIu)$D"XM0`"dX@m\eXTպ0;[hhhd@L_ex^ TvZ{97ޏ$=};Cݹy 11{ߋ^,z{>1#l? 00zaJ붥7\Ud]`i`ZŰ 6ZId2 V+żgPḫ&G<ywjx˥Z7W_?͟,;Os {34#8'uͬ@nO Y^5xTJuVz'^3qs\yXsTV6 :}0.2'Nt3"ub)W^IAve*尸V$ ћ! \J,^â\ss 3hHTз "uz{K`O0}|n)J$˾R{#}w;X䩁} z$RI(з%`gRY yM#Hخld"$ז1-WL CI E]lFUcrKkm+ v0= 'ߑ; ֯ɾQ[??L-ZO @4FX!RAw4G||F綠?A<\ڛ[*fe[f pk_Kv.KQ:BL@ + 0[.1H" AYɖ;WID(JNc:P״6D2}\nP7L{  0$:%:TU0Wlh1 CV)m-4 Ft^|A^z~zOM(e;Ț8M=ci=Jw\RYFm%I+ 8)v$oKI)^dR1E1T'FA+kucOL80HG⸎KyŤS).Xi?"$1>ɑAN 0m"8*jM *qIvyDOX9&~&[yt%3" R>4 6]1!QrkxKcn9ӥ"')TIfY,˔c(DC2$(4 u"BDU[%@=C5(l1r\ 1 1`Q.5). c;SL-'_0>ljN gL lR*F1_%t!8v"!Mh fHn{ fXD2,IMBҺP0m,3R *A+ aEA,JfY(NC*^7oN;S ?]+Z7Ľ8S3 e^_@Ud81p>Cpgnqkkhsqӂ~*ޭ{O  37>h/7-w řBp7R.5;uݦT4H${4bfcݲm&[Hc\EY &eq]$15@<q!Qv\lv]lŰ,GWJ* P)g iXXƂWE>N g81a|@{ʶXu Jnm>>3Grgt O8M7lg.$?ڕ];>JɆ,)[iDc*,(-J&s)k>w2ƅ'Nk_yz籽gfjY ƇS,iZS}\uq6iv>uSGًg$"Z !(;eVkUz"ITً]%!*"ooE5o|8V=@ $Mr -mNTex4Fa"H Y/V쎳ZtAQVXm Z!H;וUl>9MAbk|k@ 2N;q,!e5z,@D Dq6LP>ܣQ|CׅҢNnJ#7Q]_Yr\__'gNIRco}|n'uv ~S'Έ0>YQU"; oo }{W蕴E& s TK::i~Sԟg~3g݁v/N<\<|xeiFd7^UV\6Vg"Dc*S/7B YLRb1Z"{\d #J$13֗BQǘԿ8\rZJ0lQ T piڹ`6U:-ddtYх ˶yu|üESa cJ3;ƣ8v݄~kD9{`Lf8~MȗgG_8+OO9'N K IDAT7v|PA7'\[L*N]WWjaj IvY!(p\F]3ᗫR1U%PM Y&Dt<_Rc/*4Sռ,GE6mcwTth떗7ۇ臯&n Y yjK][\E^]d7?Y+3צ)Z*n|n?n*,K$$b>_,S1-ö eU>&}» .TVIW8ѐ[?"LEڋmD@H4H xrkT NzR,vc=K$3 }#;=N.eaalITPe7}V). шfVUҺeqc5ZmΠ\ò1O-0mWr% uI $+dPx#oQ1 ʦA}jDb#h?W!?5?s/7=}|bob/>;r䂾)J}q`"N)Xlq5RMT ez6WmLi"?>'oV#Z}ҧ>L0>:ɉc0yrW^{/NPmk8z2|dwH.#v8; yv+H<7建{zκYww p8gDIPȐB!=H/C HHB % {۽5;3ޖzU]f'bbcAs-nG!k m7}Jnr$Le2fΣ8 C+IT;~,Zm ,Ȃ unlޛ_RrbO1e L8yt|~)om,{]pTm?WMK|#>Xm}ih``NMw gi*z?-D'3?xJ "t|+zfb.|Ƽ]6nwi.9 ΄QȽ%>~~1| N>suwE1Ш[\,!k֮Z|F:nyDQLeij#ޱ; {nx5iwlk29E%i'(AJа]2M.MmP# ž&;' xMץ .WK\T7:,:v /Zt"K=gu $#uyU& w9 O_2}#mi-_/zn拟|k%c֗X]X! w2Qwfy}\!ϹOrG<J҂sqaR{km۽oP$EJ{*$k!~@7ܵz. dd0aqҨt] 6J O|)Zr~;t$I w?X8Ux)޼zo7ƲȕrEUS12EsC;L +"Qk *y2ћ,vڬv-NA0xmSmdQĐLYPk]W[#zXF>%PHUlyPq5XIĘd0z4Ib40M0kn"OlosMfFGSOͰ7k gc4k }jKDa(L_4QӃ UU`@'[.nP 84=1i_'] n*{85ppvz"rZ$lt)bE[۱1rabD7cE,; ? A}+H^KVrQDsi{M_ YƐʈ@ESU9c=,Fy,e@=,o4Еۂ% L( fu]MI3C76M?e0vEg'~"?eng?v>mE:ǭ- דeG\>bD?NUm;+ o\`]ku~SuQHDz3#͚xl %A4x"E9{a3gմ(2b"grdQ䃍+Gtb͚zʯ, c,%bI^omt!)BE>~퉾YE}k54Lmt\4iBο?$bVf{Fq\SS>,5<:w`s)+9L9v( HgMu([o~SL aյk-|Gg#Bǔ*c#1ql-ݶ{ܮOaU,xVU(20c*<#A;pL߯32TuVӐq{u XfBdFx5I".^`6"SS% 1Ai;3e$ڨo (!Dܧ׬Qw퀉 YI0 cj>ՀLad8gӉM#%jq€{.yQ$ E(GgH?KS~u\oY\(#q:Gsx~ZKکL Ӥ:lvtjJ<g_r4՚/ʧ_kO sڦ-1 8h֛-&!յjjpԮsñ/->fPh_ң Ԧ$(r+LmQ{M7xbɁju8.nXkL)c5F#YU!*'ifV B@8 gΎ.xZg嘩s}bwj;)}΅С\1fw1D 2ɓvHҷUh!k{΢04i H*J(EC/p G u~T{me8[WEA'awd-v( 6w/_*򙏠B ՋK={Ճ ֛ỽvskDQ乩}Ĉp" xEd6P8ZSN#_6x 6 5lIc&~0P%∘05Jo;.uV_p:"3]sv,;e9IaҕIW[x,l'Bm\~-iQxv͵f CAYE>2Q*Ǎ=gԗ}kqN#cZKkn."ԢMEik飍jS ;w\eQDm8E հ# 94 W5FǴ=ut`;ev]Y|}͜^*:qCb7E.2o\=(T?1"=fs(RFGTq^ow޴x4.}r'f]1a`v>885gDjs>dt(ȍy"Ьwz U=+ɖdNӁ7=g*Ʉc@@ lEQ{ExIB(^UDT"o!aˌg`ΐe򔍀N;u]νcɭ?QKFU^~~~ Fm<=Qn±l*S>#JvWxDtmcWv};Hw |,m}xڶˏgyr|.]J*c\(s{v vloFUx~jW;udmB~bҘN^vq:Ncc';&OlmցZ PD5IB'|˪vK%ADwD^EH! ~Ŕ̬Eԝ۽_#)\);]+FH< s?l1VB?*$- cEUg*z#/fp~0#1lDKUI #rܡ#cCuY"gmaP6 ZKvpS$A`:,Yčq/$Fnz?F??3>I~DMS%h_{ײWDaHy:8}NLO::Bc ZMռ$lw뒂$A-," ]@dx:WmPe0Nֱr|vAݸw4CaXI>1ƽz]Fc"Q-(anym3~j 24) O8%(l qXug'hk,QU䗾DpֻtCyP:XixEyWYqﶈS6a_MU>Y Sj D."r_#ʉK7G=v:׻6mn}D{A8*NkFK9Sy|:ţĥ+z,|0;`{R"®HRA^=/dybmK fd}wj>ur)jUI (qkk.KEqgͩ/ZrO6nԹjR0'~5+>)i(i:O>+9^ zu~̔,aҵ*c{^#FǎЛ ۋ`>FFy24~,P8[v+ Ah̡Jb-+@rXmu|&d:Vg-sʳgyoј̚{=%cD7i;XtbIPԆryo%#D(gc?L]!cC0.vmrR d6 ?. 0if(j:]+5. 9tIfsS*/i잯`||Ӫذ66?5bQ9Kn΋J (ZgKq Șh<QdM?n9ʠ8lMCe2ݵG.>17g5INDU^8SZ{W(k!Aqp<$5vۣ\13+^Ls- B"f^:$Bw_"ÑaӍ|V$me5S;`{\4*c$q!yt7 tYbI˚3_*F; NI[xaą'/uf&nTG8 '+ѩt+w]VyW|:">ʖ fG1]giSul{=M Vz&gΖ8 d񜎮 weS3nQ/ףV ~Je ?) bՀ;.3?rǥ] ^l:4|a?KW^N5'i.KV,n CV,vT;ʕbabz[w i1Gb>8AWÃWyo[Rn(sF8xVz=Csva˭6 Gǿ AGsš(\Т.K\-{F E+i|m݀6C4DqV#d̛6+>_L]SnlyX8p8r2걮*nwӨsVMDoۺETm;:KNZ& ɪݞAR,Wcu^jYKͭUcͷE=n8bq9VN&80wm6<a)/de {;xq!0h8^5K) uOޅA23W-3%E~rsʭڞyu!e-"̋Y8få E982.͂[w/fXXm8ROjbSC:|}7_ETco/)h~^ 1l;4q9$R["Ȣ* }(t}XPT!Ye 07lCF8,[㑂WsƙY;[ ^x,$Z!٭>hڷRtbՐ0+šM)@tt7VYzc t"=Qp 5mGW׮NN r^`-a2⦬Eq ܽ6v7zAHHQP38GՏ${b[-A |4= 'xNDPIՔM1>ނnf4&./}n\Ds^,|?'oOQ6 ƽzko'&!aF ʊG0=NǧM}B&#cf#^T4LE#c( BnD׊ȕde]14>-WVLe2{s{y (i[6?Y Sd6fs̷[=eNod9 G4nb~Z9$i41+ ׎ilz7B^4 6K u;98N-d=:Czaȝfbqhl{.u79=/Q&=A{49zf;zQ5yKd_K?vm8s1Ik#ű]4Qv~9L[>ܘU$x=_F1V+YYxϥwVe^4tSW&_~|6}&z3/OOp\bj"#!|r=CzemƊ?i0lZj=D?# AwȅZή6IHj$q.g\FNyz]));G,'RX"m[UQFNocamCЈ>9%=HU,e!OlRgR+EO=f߲.DJiFc\|Jn|WNp6 a:.Hpζ}feilD;/鐿Gb` .J̺mlMۍQ{pkecoK>i}!}*g;KRJ+eЗfiT̍џ }(s7N("(Gէ)L&{_YU޻' ;C6MX4CBc9-A>f\ґ_dWgxb*aEK70h<-Q0ͤ0cZf^0!cNqvS~ju& ln͵ _$4;OXG2kY^}pIψ:SeXyܿtf#VAT)Nkq 5^>Ʃna" 5:!B ?UJ}cGB@GGeXGb""%H7ߍ!k}+ڝQί类gz͌Ƶgw#W.U?{Njz,dȅBJ\#y1RbE 2 6Exdy㘇n+ L9#wzDA̤_P@w; ˘nqc3+|O&n-TQԜ-Puza]{?"\uS쵔=cܮ/P_O#ON5>mτaWw,R( M 0# Ui31_Z?(Ͽty0TsW?Ɵ}]jrۏ&K\*9_ȲԲ Z! !@V$<@8wY9XnD2CeHBjiȺ3i1UK0p{睔5$"k]a}'4 0dl"c zuJTHяg8`K엗,6i4% !w\AΘN#>N縮r띔xYr]!"gg&юLEE泼zv'E!F ä!'?\Ev;>o2wfc;rd7Vsl}5c9^j߁)+dͼ+Ș?kUDQ8]t;YVcیO1"oAPq @EQ UUq`R Z8nb=DQ[ ܶ6>¥ʩ]ÕKLoEfr?~A`"k25i..59ML;tH !*Sq rtokjHBr[%Kص< 6w]Ntٯ.EZOxF1c &bD鰙zvJת{o6VRe i,ȲL}u+ ~Ĉ- tt tX#]Fecͦanz8 @LYl|}kvW)9;1d|Ӌc Gyԑ Y^ƙ ĝ8.7eeGĝn}l:z=Rm] S"]ߧ"nϞ] Y&ͤȢHYzg]ßAjt-6>3yn]V~.%cZGqiݔ6SVn{B,1uJg+Yh; Oncjwdt{aHP g }0:@Q zFF}ncm:0f.CJ\T0v#qlAڌ~>}}qjk*lH҃qm'EWLy0]JE+|ozGv;, P44{_[ݦc2̲|]\^Tx}֖||7f3]Sh*JEs񣈆0 YҎpV$I2.笪"~ԿrFG~?jdsTA%su#FT#ow/};IuBV,rY}k{@:չ̃QS3sSMcwzHqk'qZlE@8Vӣ)4ynjjq"<0>,}s4o|= MDJIJquJI'O~0>_(oD.zSfbZ( MYV .m.z8emapYdrvu<" <p#$A tߛ.I?(M=t[p4PX^Ơ݈؍(^H^Y!#EQo8U(p]==͓OL߻bCICeʚLYmߡX;anqڔt fdt ,% {+&Ɩֱu8PpIv+)+{{T2.WwDA:@Iױ;1e* 6j:EI4{7_)|m&sk33'1NDsov؄u笄G+w[ ϼp>^顩2g/ޠRb'oJs\$Aϔx*?ɹLj [Q"W#>Vq ,/Jam%"r逑͘@ ;MҖaʁُ"˖Tw䳽1MaF8![AϷ|mFڌ gϜ4K q7$ZD=46B`b<7Q0):f \̖2(bZch;NB'u=q]w*pntiAU?MɢH}`%j_6t_f$)?.=k3¯Wb.ڋ<-;6Q͇Tb_bAwo |̥+Sʽ֏ K_Xa”U&.C ~X<lZēS>< ƯʗӓS?i4If\r%_|f$8+>vT)grR$cIGdV Lro0h!=Y3&<7 l~3ba !o_מ~!jN"^ߨ=~HY^0$+Ͽ`XO&+yW^ᙧΐh(}@$WƴLֱ-aQA5bV-R3P\4UftƋrHD/f8;QtrVJ71 澵$`MBA <{/?GG|mFG%LOMW0&Z߳WMta{nq~S*|o]'짯Q)g1 Ȩ̤$SF!.cpnv8uZu'JnA@W Y'K,115UehAOgE4ضߝy-5f.돢~ bډ>hǿ%Zarlz|3ZH6=Gڗ_gf. %J„؅4zᲺݞo0p(}c5d(Jq? )G*RV)F1ᆇK=tSo5sSA1oO_?ï~E^qIL,QDAgz$ &]wѵd-#hW?♆t7 ql5":@l{Q@ǸϏFQS<"{#QL%s sE_zx]?L&sǮ_8\1rY*V]DL ~@jD񼐥v7tz&<voDv75IQO^^rE#NGk0__—_'?֮.|̓ק>k]SvukW'hwuյ˫MmUUp!,a*  I*i 1qX1Q8펃aDqD)1 EQe-$(0iv='k{emK?,,u Hm$[Iǐ(ʑiĈ }wC(Q37-*_|]rY\VA(HD&MAui4:̮Ro {Lb wKpF (kz?qks8yvxiwM:|#f9@Ͳ zpt٨/}M#FH?$6a"v}UUQ՝Ԯ$I':(I!or4t-,n0;j9;A ^kujD ;׷:0 IDAT}٥<5}(4=wCv?JI :ddS:(`X{ή-xy>g}M#FĨ!ڌP)O|ؗҗ s}?/܋P!5~P?X^0֮c2ƋϜ/y3.vc]ruVmaJ CvpQRvv1ASF?bQ DeW^ȇ}qooun {Tp![F޷MbH}Di2#v+)r Q|'\iĈA}D_fnШ*sLCc7.SH X.Q_[pWߛvo1@hR;51H^0s{uc?x[HEDMKjȺp>3s߭ O~98B$JwܓG<ތ}D0֮ƙz'A֡kݐqkx蛖r}Nl~ow`aIKrW3Rq|}M#F b$#z,\~>^cqk}{wVhWW$IKOjIxN9cz^ M=cDP(}1fgF8,#A WS&֮QkF""nZ8y/!m|qC&sUg| nDĶxAOj?f{]yđm?gΒ/cww `PkSx1y)vq)E^tàYnG!1.z'E.qF>Hn.o~h@TsU_:F0ha+BP#Z>;J9C֌AyT ׊ƹ+Zt9(j8?rX\BIϖɠ:^ *A(])SBE#α=><8\+V`CtZs+*2e[P{~;rqh4\\:'v)E-G`GH/ kX[zsW.ʥuhL3YLf`{nEq~^Gݭ xuA x\.HXLP6_Qk6twBAENwNHb [X].au$fݙ_]@J"]YmsPLLaǛ6zzG%Ur#/i]\h3vxJ]Zn3%QT ~s15$@J=Ң8( p A UZF~rRkݴ[H5Biеq]/N @Jx"b2z`ImW?t g(I08 (P,԰λۍ?L[ݏ*^)@I5Mл]pkD]-[Vd#]k~JaQcJcr*_y.[)\ڽW MЍg!`N 3SDOuFRmw͏y[/dLJQW  %!s\s=FQ?cm\4z{q"RBHpe]55ks -!%}q@"2(L#S8td RH`K͟oke]S_+7bý(yH6`F*-bX\Ñ!rfyc_:e7CdW-q+eQ8[h86r@<1*с8G:aZo?k]BV 7/SunGRBA˂~\kmAlXFOv:[߾mλ-q}{%xr@w?`2DviJ0un !P7l6̝cm]rD'xŒVAFs"FKkd1sڎ `k{ѐ ː, q_JT69n{hGss(u%n4QsXb9tY&2 TDs:2w`44ŒXcu VVZ1h@'{lZݶO*$RP )QT.7⻳]3\ +6:xsQӮB&RX2'V5McʵV^Qrgvw!Pcg/UQK`q.5Ö8o/h)C'ө?P<69{T‚VFn#۸:HJaݶTna*թI^H|s^z!ĕ*.\.;ux(1!.7pu9kK\,W󯻅FI)۶%#,Cg7αVH4|v⼂L>^s`"sQ99 2,U7~s bgnAU ܚ˗\K.N."ӱ8fcƸ98ffݖ8z>r(,WKͷ9HeC_J>"S1,ny!ՈOQ}dS1LzW=ϥT[%ىDb{.\.9p(t# HC*a l N.V tcn&v`J{%y^]zcvq3.͏Oe#G9;93(7l Tij$S:px.׵] >[!q^o]e }c.\@P|Vдx f|R~WPj|fVY>-q@~5>–ZnB gQ.[| riKѨm no9_,NBIqqH>@@ SנW5Aߩ^]no+}w:g-gfڷmKTQ)=Ͳykpĉ8(~?0* pΟ?W*\ùnM°&jBE48t|Kanqs#n;#/o Vg_캜qp\T$=ީua DkJ9jzoWz0`e`a~k.^.•o&vlj'}d2qwvplS4x8>QYfDYJ˼9hmj婙G[:(ŰkovkkQ>i͋*Uk﨔n.vɓȍ}3MǏǡbVRo]5 |=%܅Xۙ;% tW\EW' G^Μ/Ru!b<Ͳ'Oɓ{ ؇' c XXXC=Mi~5Ac;%RÝ}˯CRfKđe<)^xĔ+"碥RT=R@og=xB]ב >eq1vI*(o`{Zcq>-ͷn;h݋1whn;wm%2F6K`c~ ]2]155]R:,ͯ}KJzW\5ph`~"@njb{˧O}wK3_Z_[#OO"mS)ba~~}K0z^)h ¥8&Rid…us3]gAD1pёʽ 籌ޯ]h tW\uwǮ 8߯/\Nv0li^%V+mL6),LL"Li~܈gfOT*\^YGaw]>uBes]*C̛o(.m_ow_cɞ@?`)/u9>JX)g1Jaab Tpt6#3g/E^~=ρrv ][f {^@?z?4XXŰ81+SJ8QCӨ@eXp :ٴQϤ2_) k}/`XFPwY3K`&&0m,b{ީ4?$3Vz}ͷMggCqw@Jz>S`tjvlzyѹ+V3=_}}C6!â@']^{s_L3R k VDrSMM^C7tXݛRⱧ|ߑGBbX 6px>f'҈o그; B(V@mw1ӠJ{$ҿwɧz TS3Kxb\$Q8g - mzJ^inl6毓d:ԙ (׺O:Gbj~ puv@zsi/`OVs{~ϝ2$ tI4ƿ 2Ksaji-_. ?Gp>9nj-X i{. 8cJg€\.8Qi3;BeGaI*,SwN+mK;^Ђ8rPfG>~ >1lG@ \gBe==lAx N9] H%?S€8 KGþgUqAI]!D$-fXi VZCh8zKR@ %/]uX* x KX-py$_mLbJ/!%>܆[biRjv]őoSLqð8Zuek;v5r=Qһߏβ𢪬Ѩ8R)4np3<4!5kr(0܋|; "W8<,ч[=u%[Y"t&{?49G&4}gZ,w]['8)mJ)40 `oYZ %Bp~'ڛqoxC]'!;AN[G:'ѨHfW-;5+$sa߅X9zwѻ] 7V+)!dw}K z 7H>kλOđY]HQi~,x[E󘘆&W UbR*\9@ =& Wy<{އNBvhF IDAT { elLGq Q,j]BrBf 6,ԜԜȎѻ5R̀bsmsgpvy _{4ޏඛ&8 l|!/=cxۛ~pGȨ(~G߁7SxyYLQiv= ihz#`"RuL4xF/^ifs823{o;ێM`~ff n kZwhz!DnjTn':au9jA<[·%G,5Z+ ٮX/c 5G:ܻo7ދ@3v=pNl2 ylޱs $7ƯH\R73׮tb5q̮M2ugjWajUbv@)aF?jUzw9f2'nqm!{x![~Q@x&3nJ~-H!05vBt2 ơk C887Y0D8Q^A9fhhUp k:9ܲ07ۏ;M=+u<}2^bN`< 4q,ceF>uT1Ҹ'^AՊe)B]G`t7$tXpSFZ1wC|E> t4M#!_${P/I.60{J+6 oPxdb0b|i5\VWo`ԑlܻ] >&v5V™'Oa"Ʊiw!|&F;|\]+ty1;X;Yx9c2*=HQBy=ې}7k#e{߶Kѫ:}wqBtIvu_ \zq&'5ܚn#b9yҖn%ca1j3SƑI2Ppbk>emkMS&ҩ&p xUGpHN/BBT ,.Q6P`aĩ!O>g3pO'j_)|}̩]y+&c鞫ۥPr󅧱*Xf}_ !B߉< g.DOD-0)/ee$H/N'be6ܑcbIӓY((Tv))$쪋FE paؐԬ[>n(U(TP5Gۈi&2V"rU@6D/mϲwĻdO5׾Z0>C:x7Ga440Ơd>|^  rXwb;pηM6m|®ͅy nh>1}Њq̂[c>q(F`H[ sU;,6OOΰOsz ^(ɞ ۿ}3m515ҩu8/ ~~:2)L``n2k?ULB‡d>$z. @uClaP]JetLdF;YcߔӇ kpZ×)gh:LMGL7^X]i|^m}g? _ !;GN >SmO[H@\/=Ģ.:tK 4jyL*'Q@m(c`g\\roY5q>w5`߬J +: :ӆrV={pWA_zu-`Fg}\W?G>ALp'1uUm!5vso"f2_ G+wV$;r9XNGz\?N%5=!,_o 揅s ]:>>Vl錉*[u^4= wh wLqk,~s^ \WknmC3̸ԱU_5n}wVm|>|xO!;AN^6/?}[[mHqLLƐɘX?WFZ AK@ )!:=4h!o6{ӸfXIa~7 U %µ5}v^zMofC"{[(˪aO~H1,G,cf& ,?_W( R&R9ya3λVʏJI/UI%! J)0Ys9c5CXo\@z|O!@'F[ b&9`0LNƐɚpVN5F0܇]N9i =fp>gUuv8~?c\*Dlk/x+zY^,;#!Ϟ?BFENP1!c LK]vrNlZ> ǃ;o,< <3ڶ_%'LmGvv;pM3 ) tGg_̉ HTĄM稭X;S a ֻNqΐ[%¹.KH #{ 9rA; I)UZ0o99&sgP}'痱_*qH1lDv"m.TA۪롵Mc:\*h`JCXȗPL4٦0=o9 +yBZ ~ċv%|7|[ \i,<g1]@beLMߺd#'D^@'Z^o#EU&•J G Ox R}0yuS; ܞលteF#°BെKN@1hW6}ӊX'@8RNdvj#fw;'s죿q]/\_zam˷@,6n5i/T5Q^<ޕRsLBWM_8¿  Q%x kws iߕJAZ\)`|;oy- BLe=?m<#Cvt?( R7/ɭ}㉄LB<p/QREei?pEwx AZ A[k&}s p;c p[e * j)6m}^n=p]őm4m+pBI S⡐wF)yٶ5Q{| #B^._"_E S"6s092Y m\.UV#[w^ SO,^ڣHdϢF,dc57kr 9>|_b}͆c(7ZN͏cjbz&d@'7]?b$LsflSiVKکx(]Qr!}ՉnF)STl2k3Arb/j`qGS (] [ViWv2ѫ( myu GV]e jY=zh`Ie-K we{Y T4x᜻*wo w ۲4G_5އPk%rH ;~O+/u]%̈ V+>%'_>Ke}ŃFN~ݏ|90e}D@"CoB䢲܀Svy{ `%`{8=^t~ƀS%`ݮ !!#_d>H>U&s]kTVP8n701݄9Ff~g[Ul Ia-ksQ3.u9V &b[/P]Tʃv=ϋ=|~-$/q<>GkeAƺWsOH$ z[i|;@uFuFÅ} 1 [,wif~+yҳߛLLvgG+܃9+ h}j>{·ιr]7[8rᮔ:9x~_}}J ]}ij4XqֵMIz  d !tai[1꾃g \YФ\4 %3%ϡ& mhy9?.*EN3Nq|*!٢c{m XLG,!7W;R:ܒp6r 559ÑfjwOHϗpeF'*Tvnf_UQiۿgp@jckA]+M oy>* Wm ca9 И6gA75{=im #~P} } Qա#\Z]AH77WI|!:9N=m;gQoP*ֻ40M J* 6*K 5RȰkXai$&^0 UώX-e[wNg柡ӛV^!O_nBn`4/~ 4xe2H+\9]34M&un̓SA858#i3 uꐧ= oo nKh sDw~7/ Qau}?(om;WFu\6 )\Gn.\χPW Ӯ(h5Lͱд=:!O?-<̹Cn>7?1:n7Lx|nB$|w|$|Y5Q0y],e.M?}hR'FDNȐ_Wx _֫-h:<[38g<|A֩JE|[*Bȡ*ry_y/{ۛ~o|JޱqBNH)O'W2N}~ 7%u(p7zنy~?@a@N.*x'}+TnSZ}77C_9bV}ބW?@%uB@'dH)SS_dzƳO!_\mw `Q/aa<ѱ hem9 OӧgаGk{Lfp` Ivկ>$䀠@'e$ąKSxŧ܋峰mVD mϱ5%6V\7k^>cu(!^Q(8,]: ΢RPtr8n>z+n9zzzūa5!lHtVֱ^Xza훮rܯ$譸q~S/!@@'B~ZK!QB!cBB@'B:!2( !1@N! tB!d PB!cBB@'B:!2( !1@N! tB!d PB!cBB@'B:!2( !1@N! tB!d PB!cBB@'B:!2( !1@N! tB!d PB!cBB@'B:!sRIDAT2( !1@N! tB!d PB!cBB@'B:!2( !1@N! tB!d PB!cBB@'B:!2( !1@N! tB!d PB!cBB@'B:!2( !1@N! tB!d PB!cBB@'B:!2( !1@N! tB!d PB!cBB@'B:!2( !1@N! tB!d PB!cBB@'B:!2( !1@N! tB!d PB!cBBT+}{IENDB`sympy-0.7.4.1/doc/src/install.rst0000644000175000017500000001030412253362407016776 0ustar georgeskgeorgesk.. _installation: Installation ------------ The SymPy CAS can be installed on virtually any computer with Python 2.6 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 __future__ import division >>> 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) >>> 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. Anaconda ======== Although SymPy does not have any hard dependencies, many nice features are only enabled when certain libraries are installed. For example, without Matplotlib, only simple text-based plotting is enabled. With the IPython notebook or qtconsole, you can get nicer `\LaTeX` printing by running ``init_printing()``. An easy way to get all these libraries in addition to SymPy is to install `Anaconda `_, which is a free Python distribution from Continuum Analytics that includes SymPy, Matplotlib, IPython, NumPy, and many more useful packages for scientific computing. Other Methods ============= An installation executable (.exe) is available for Windows users at the `downloads site`_. 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 sympy-0.7.4.1/doc/src/pics/0000755000175000017500000000000012253362407015536 5ustar georgeskgeorgesksympy-0.7.4.1/doc/src/pics/consoleunicode.png0000644000175000017500000035413412253362407021267 0ustar georgeskgeorgeskPNG  IHDR^3 iCCPICC ProfileH WgXSS@HhH ҫ^Jb/ !KZ+".l9uw̜}͜yf`D)<Lq+:&Ey6'Cyw Du3ֿw N*G9"q&$žނL7A$B\N1H16 .hl8YYDhG+B<bͅx SRS\Qvlv7lv7<6:dR؋F?Ujkф5-#9,0o 9l0U Lq ?b%E>Ir;HaPa`!dxPl~xԸ&"4Z:gdM|ϙ$v1߲PC4ZK%@(3.L9>I؇'^xp?Ø1Lq8c>0sowi KBy'L$(*ŔL )i5J/僔TPjT~SRפI KKK;JKsIo.n*+=LUR$JjzFFFFGAf@fL!K22i4'mMB@5t=I@?eN.-&RNZN_]n\\rle%o)0,R+*)R k)Ua` ]'X(gg* *+ *+*[+G*/T.Q>ĘLf s#0$IxOtm{*n*<\:*TYުɪUTj&jR;60Yidɇ'SGMCSoSi81tLܪyJ_%ڪuZ9KJaαյ%{۵u u"tV<ԥ&nmқDF>_EQk  U k э\ҍʌn퍓ww&6&|ti))eSnͲj̺2M]5aizbmvqWsr,,^[Xr,K,oXѭ|[5Z6Yﲾcða֦拭ضֶN.ή}}%rm3;dߩot=:l].,8=.]ڮl2nn\ gI_z{=yt\yzz{+zGx{?I]G w_ß_?`4\ -0,8qI8i:#`Ɩfl[6<+dVɬKB/1{1~Q$%R.rNdu(iKĨbc)Cgo;fNΜ[s .:Om^ʼ#EfK9\7Vn?ϙW{PЗ蜸%/<łWI~I''W&DԥJƥ* 4uLE9tm@qE171S r$F$Y.Y%YD.8Papa"E=e1eK/ݻ Ye5{WZI]U V]iƚkz~&G6Gs{uuX5{9<0s>'?˾>g?}!z1<Ÿ/^WW#ߨ|ke(dѻws?~h⧨Oφ||.bk###"=z`&$ދb١hT F A? *X@P3`ч1? VV d'#r 41<|y /⑑##_.cw2BM=rjm_A|m`X?h pHYs%%IR$iTXtXML:com.adobe.xmp 1418 862 ?6@IDATx tUy.l 1J uCrL9-%ԇajJR<8U.fආQ잸&!uj.Nإ I 6˶ @zn=c5k^{kK54k|$D@" H$D@" H$D@" H$D@" H$D@" H$D@" H )KY&,B.ιJo]ǝD@" 0m>Ծ}ZfT" H$^ArF< 1YHj2Apd\~qGPH'%9#q= 8u]" Ha<m.ZJJ$DFEJ|q.F- `61hYei(P|1ө7B,|I=> -e|qi&GUW.džY sClu镨WJj.@\Leݎ16,q\9T~##}JK<q<ہki^_/v@orηV X>[s&9ׇqHg+XA$@c۽( P+1pkb,·*SH%keF1FUXRD_GU'qoќ 2D@" Hfl8_|c/HBZ TϊKI^T]aT^w3>GqElsΨ$%2|=h$^]ժ*n|$ea=K𐓨h*eQBeSizH[&7҇n]rY઒x$PX+!kQ!^iD&ҳ5⑨2/%}۸SJ@Иzd Nv۲G*Q_j6[&`FLqHy ZZ}Ɲa FƔH$#20-z. Ё?mB0~ ͪ?I"Ľenl|!h~>b%N21I@5-00#l(룘M \ H2h}J;*o>cZڲ#ODq/3@_XT7c,-ofc9+ցTӲo)5ʲ"A^$>H{J Nݙ=&pLX(ImY?%}#S;Jt5\Wr'_0&0sBpt,g+75Fl`9x&4股1KU.M[JYU{ ?NU!7X^ &&5|g q_.=)P0a^C#Q0LᆴRc>޾tK$D`"xjЄyVZ˙wb!]>KgjP\Epmj^~'zJ"8Q bL`ESJ6OlIH̩62IdhL_ 0n[=}MN98K x_ g#uc(ؑ Oz=UҺgup̑N[+}Dp΃h'*FLkhN@&E:<%O|hXA܄gN [<MڠC?#Q0M~S+4BOБ/D@"p##wޝ%0-&uy.S_d?ި0f>hm-ǝs0R7%[鑮zb~#yN>|#ߴf#84Vo|IA.}aq e({}6eg4J4W5c]onbr5C~?XPk}!>UE@MtɄO1w"SliK'y2?Q;Ԏq|9+`⯮(&f[VUE[>tBž ԇ#,@JA{.L6pRbh4:ZZ m'~LxS$԰3d,͗qa0ڠS98x<*DA'bX*1y<ёD@" YO8XICӷ^U=q);z hq|Me`v*Xic|4[VXbǫPgo+7B:h b_ZOgل֛T<7/<1OQJ7;A% ǻ0$]tVkg-sWJjOPM\!HVKKkQP|T]q#;9HG?MT2/HHƘqAO*GOWwlNETaSvaaE*qΡf9 R}LVF"n CZGA['k!BA%9Lq<0⡛aֿN IapND)Ǚ cޡŕe_hYXHI>`wsG-TqgRwdQ9_4%9ȉ~SĻ*dћڡz|hok\I~Bo89xVN)S%c[NG.$'fz7_mя@jU#` i9"ݎaU5Z0tnz6׸x1H.VэUkAbM*tCfΔ&HQ|P)\j C%;tqiBw֘kH$[yVAgZ;UlQ'sB2;8sMtI8 tڦ.ӝ:xqsbLˏr4(A,EX\qi1!FU>ϼ5W&[|1Oe5Ŋ+)T ޭ {fw_F>߉͝zK#oyTP2'i vdwot0?_Fa\'pv.h^4ԑN=fa N:i(tp`dz,;wi^!I4+Pp6jjvGK=6~`ٰ/gd.ܖYNv2IRr9 ô] z*'K!WŇ?yػ[k z6k-ejۘ 4w>TƾPEP&ĉPS ^Դ\ @tA^teťr O6h괾t_!y$"K -%D@"0DrgW]k[nW??T7z7=@8HhZ{S#o/ٺy(4A 5bjgIБ9 5etB=/ʴ> g.anL)4'x;]O쫓[+/l,|yM|3Ig|GmR YyQr YtVJ} qD93r/t_M+"%IJW1}:0xIN   윿38bn1XX"LMV%/KXkf8ڑȒe*H$eG&_߼ˣؤN 3|Mx/jiʦcxׂa4s֝9iq;z|e޶IR%pk|׊}h-%h |_Ae-߭s|3컹 tpyѺ/R=[_oK͢FδJwp?+`|#?!cSiKtn]z+N"Yˡ6诽dR}13 us86T|1RzWF5>'+5މc/g;KvtKɖ*}Af˶{Lr8#;D@" HnXK`0&ps!"a>aWtd(k =Vby&0UΧ@Ѹ3bK{َ+w||Γ%$[odžq&I%#q{[QΛk@,H7hih"dG>q; xq'JXL/5΄, Ҏ\, 0+q-D;>wgܮmc$ݵ4KeL).)|@Awvigs:-~/P`oc OR {Wݎrٞ!lf] ucWple"@Mnf*YYj2d^] IA]d=I_æY@92Efhqq Dt^jz^IJLet&52,EITy[|5@²f2ҳ'U yH7m,DR{%$ؔBfO]NUǩ`[K:"x T RD>kËŁz\ޝ{vC])FJl`1Suuz*c:n:S{u!-x$/Eg0a[yƥ<#k ]5x .*qIHS]&`t|$D@" P`w8*ŃE69d5'47!F)҈3cٷxBYWv# 5F5:/p,0]vKʹt y"N˞~.&9ۑa|ժRƞ%v]t$ipM3>9qdriNX?띡.Ѝy+d0&E9M#nP#H$D0|lPWK!6#G<n<¾ᙚ+8!23븮B\92eO!ΓH`/~B6鲌''r)6)NJ I[ճU%+\= Q &KK{Vؘl8]~OK瓦.2@)hL5rԻ-ɖG؅S!F^#hr6lS_fT$$)n(Ϟ:ʌ-"d#[XHv5-ckPsqP~SыY\V) gtRqc27uB>0$s2~\7I6TD@" u`ZVdpkM~y/[gcIba6ޯAwŭC`r*υFpnpg/#cU_u'14z[fLɳpB1Mk SnQaٍH_'V :\Dq|)_wWv7q؉O\hQ.h.5yȪOsC=Ek62}V[X<%ML{[:qJ(bW M̰~;A@el茍(Miu:~ Sڈsa p1|M:սS\;OQ7 P2ǡCD&j د>D@" 1`s涥X5ƗK\(qOt%god8Xjc os!~HNbcaɬk.\ fXSfL $JC Zl3-F/܊ \2?$zoL')Cbqپ/ V`$/ŭqip1Kq^~X!~$; b w_zI8ԳMu LYHː﬊*Ḵ3a#/͹T3Qv G" H$3hHsaGgtf9>6*x^@%.9cLws7imzv](ey?Ѓ{:l,G=SW&ʪBe 񒢉~w ^⬍DM uxzElᤥgcIE$*6ǹb2"gvk.ܩ.+qvFQEl.Y6< pTxyyV3vbM=za]1%X͕y|tb_ժesx%TH.mo/owuF^d+H(>\xC1u#Hv[GIn+CT Wr{8eK\|{)0\%բ~㈍hB;qi[<7Ryg32/;d ֳzRa,;B)"}Csl$Kղ pY"WD@" tHZzdf?fUY_޷O6"]>54%/V哖a=t1[d,oҢVRx-EU$QESqhI]&M(q{tA}`ClBmsvqV:J-dqqiK>U4 l2k _fd!# F[Xbi1ڈ%F^,X('1+;#o_[M&ĩnC wm n}`9XShMPoD_R"sXfD+d&`N](6dO8;>^;(3!P̋(axF-7GhPtH$D`#`eǰ[td{|J4uB;x~XߛCY*^!$77D?ƂMmhsZtp9z,FoZ!GN(qι=7f6 ;v1ѫAnǓXnRvfUVxԮljx0vN w1-o:fblѽ_5ﴯx2~ucED:Ie2˗I8clEіYJ;t#6<~B"Cb+cI_? <'Kb݃F%Ye@J&x4~F%'~{^ԶP3K~ gFskqga4^!~DsxjС렏{D]niF)S ϑ,0-fD@" H$ DzjFk/Vgc<Vs1^q$R>dBxlTbvfLfp`:XN+&NKPzuG;"dJc41q_x~{`0: [ %z v M6៾!64fY \Q4MA!*7@Y4D7vj͝iQsTDPb5!ִǺw_{4JaʲRXˊ/dUH$D`Ȕxigf~gor%S@t gqgT"vC@µbe1*\{ naKM7WXN)HjD`z QNKD@" H$)C êwi1>eɄ%d-V" H$D@" H$D@" H$D@" H$D@" H$D@" H$D@" H$D@" H$D@" H$D@" d '+1+.X&HD@" H$Y%9Wrֳ+d rG>0>ڷZP&A +zID@" !@gI>JIam,ퟐ! ~6bBS3ݍ8#@g 8^EUFOqnqdpz#e5s(dc 5xI&Hɴ'VO(UMs 3a@Cn,I\K}w=bX|/71ja1 ] S%\ܜGv/z/ oC8.u(ԹB Mp:'3^8ČV__$$˴O5zfOq68Ft N93J|q.Fh&KhT}G["ɽ_DY)jh52ݶSnJ8HU3'] P٪8B{·Հ<(sj_.L5wcODIV'*6|Lo1\HK?jl-|](Lo}ux*4gӴ'(0J=*_fJԫJ[gV3e$=a6ͿM6 t - 4b[F]=zԺ"*"1v>V+UGXF\:;-W2/Z!$WҶpSU^6FãVBtrK8s8[ŘÞ+/agH r$ؤjk cSfTÍ*! tFl4h| @ oB"" _Ke % ՙU z=fu\-I :\j4\QQ?A.≏La?aP2,wDXQ.zc6)،,UeQ i1g}7X WtK֛l p.ތ޷S h d"Z]-CE#)+ghhwdb/]|;fHe6$iD< 9W 8NIªXW5.7QkME"|N`Br &!в sDh1^2u"JG5;,Ж9t}:&W[RΦrf"z(sJ86(W"*X!LhZ_I rG.pLM`& [?Yt<λF>h22=B|S؃"DQ0*B Mam oulrvźZV$*q)וJ瓏rUM˗ ξn$ e^%Xw2lsZN@C%GEP9g\2#pp1v΁LCX,T~[̌iLDqd@^&b=߶(?qf UD z #M]y%dkzq8gXe].἟&%o,tO0  ԰g&8_j|}4)V)8 UM3`FyRx4_bib?ױ.O"{J\7yt;2| K҂@[+P8! `@sC[X})0 ˽Y65پyu+p=cOo9B,/*ԙ&!Yj kJ3Rr|a=a ~*bdtK{x>yVp2ųXA%SMG,F9X]fZm AX Ony(ng]!;%cZ]aQߋܒj0YnZo+ÎnkwExЬu ǻ0TrUFg*}ԨLYZD#<7Ъ F ZzfMיFҲwۅkFDRƭ} ֎L:3RW/_"QTߌ+<3ϼiLzu"JWx]`S8#oɇ|LlX(h#97\X"_ה s 4%qNW[exv1ρh)Mhd]/ĝ6 TZeJ<<OWjČtلrYä*n6-ODWx;r\!\FΈ悶`KоZ[B׻8-#$geAc{'5̎~!q⷏2q#_$7F~Ѹ;WZ P $N=X4~_-lV*If>)L5 ={W9U({q<`NC7V; S8˷QTOwB+ڧQ@!C+[VϦ*Wm5?ߏC9x4K@,:l w`"O6ʙ%Ac AbZ|Y_lv/^a6rT $j61X]w7R!=XR֮1R7g>3O Z#x^ey fp?Ih%P1xTf؄R0N)[=B)@/ʋ1xNuu0[II@b8ދ3N=-O"EG5 /p=N/SoG"nKXXX49XenaqEcΔ&Q4pa`7q5w4K>)̈\ Aؾچe uX<ΣDH Czz6u]Apc;lkL}R 6 )SP7E2YzZ&BbHxz9^k%39')|< RH2=Q sbTa&kǡ*>߶~4GWnu)آ?Ky*>َFeآɆG a); 5^ ]-<_.=V%4 '>j9?vdj3DUaQAkj7Q-WBI-6kitz^x-e3`4\2+#!8oDbA ]xvL)j5Q2LVgp?oBՆYq}tN/0 /$ߋr4~]6=w+k iY8tD=/ ,CI7} HsXX4UU{1GKs8uՐMPd% :姥slBNn(n[pf&(Jc+ΰݡH-Xa3WsdH;*`  kl3KO̶{/eLX"DcpuW|oCZZZ ;QuUύ WWǂodZ\y;;\ xb/Jd!t4FES"z2NkСpm{( ^ M} QzTunMMÖN=(_vM4eVRdVAyuFu. ԟLCY;ȵ\GO.آn(eJ!X,ZcB4bVdyEzg_1cwp?=4dǍ6l%?ؿMiZngR~b6Z1Pnh)꟢DžF%wsUZ/_o!R/&Tb7%2R[zmU ܞBF}%^oޞ P5/Fr|%NuFD6ZJ&l_(5 ŞByGPT3c3k^agaH;򾸑%وm}xmV%F9awx`Qu޸k\^rC-w|W (,“ӘdM9[q Rf,{^ߑ'ܦެb.1_@IDAT2\,e5nM>qWA8H-K?oEJ<i2tID G4F&X,3j050Dk探YvC1yW*L]_ҋcYzqNk@waAeURavϳ,S&V-YC&CJNZAxDn"g|nUreE[[,d,띩ψ`Lp`=6D7Yըh~`G̢(b0V4tPWCx L#8r,VR OlG@iO'Pe=X_$ .>e^#oFc*)XX)չӝULNI=miѥj pz+2 Kl3neÐsĂ?-(4ѻM/6J-D*2,ˑ }B>jw .Q!ngD;t #nVIҞZ<,V~d̳ -ӗ tN*rgGѤ)$X6NTBeЎU#ѫ \zFj'wh4Mlmúv!a2y߮OKiE`g13"Ӱ|N+˨sfD2IpأS#>Š`NVTf}#+l6Se I2QH҅kxOt-16-wUQ,o )z%RS'Rgľ+9Up)SdLmꔐV^:qH#tskڇvcopQWcA1ƷT\ל':CX]'؄.5gfD2t3Ld9{ZA+y+VB1&['_%`m $L}zg2Hpf߳μ6RTU43sx-X,'J|&9 LPm%[ma΋kW)Ƚe93TTMz%nB򷜺ҩ2D`gRИbQH<)m\w5idp,e!( /dS?.RMSQtX}yt,Y7Sϻ10zm 1qxk1y 3P꽨tCnd;!SQA,.nTf4?5! $ӷ?A GdpHw)nj>ۙ`XbOdpdUy>,%y0 tu8KD@""k~< ܍Ft\\W +9][Q:|7y0_-90; Tz&U(dbD!Γ-+`/~}`8WTTlu|<gtӜselad(ÊdV^ _FɜFVT=G<0Tujtr sJn!d!X/ztСteX"rg 2z o5W:kA$ioGG!.rZ\XcW\yNPSEc{ˌ~4O : 7cA+YLpW1?!{ĺ-lp {Z:>0PCh+%7PË3Y>U3"3x ߸Bk~<7YHd Tq2Ym( :b/ U"   'ԅ3l/$6MU}?h{bYh&#oMI|¼9cxVQ=X=կ{qepΞr&MGJ%@y5&'~mill]`61]/`2ӚJQ}?3h>%lNzZ~j7Zi1Ϋ͟[K%Sx:q@;ԅG n)rXjنtS.cz,P^.){ᗬ֪ZE 4c֖8oZ\'Z^/h/jPH:p6،е=Ԩ,<[6\|qRZ VK_l>HV)6Y?_Pψ=fl~|lF9?Gmf}ѩ,LPZ=_&'- ڎf/9gʷ &D@"&'J8<|W~cXZky 1kV<-0̄`BlI(#8İ5U@"|_}(ɩD#x՛RN.HyѽhlDF/ 􋰧kqHi)V1dRrkO&V/66}{*L5_mnMB1YFFNswu} P9,iqn 8=]Z(gGXHd\ų~h> Ym2HrLZӚ*ϙk Q<β$_cD BK7¤Ћ:%׍_+Qa[ ˜b门F4`?U4LO/܊ LG47)*tmQ1&.A}슉 /x[`y)暨*M¨G Ϛ+"82ힺǜyVWbB~bU=PF{6@Gj}fAiρ(Q:}Lѯ2g2D 9yjuz ZCd(eSX {bYdr (U*sg`_yc1%Xvb嵹hMmz3qW!޸sgFA*BFRGp8KIV/-DF!-ő6|b0M+2MMI e<]FP#>~cnl10J}Y" P$Al-2:ރ.Vb8fO.Լ1h^lwDʩx'v CKq} K0n\bp4F׋DfԆ|OW k<3 pL{q~pOv8ܟ_5is&'eij  LJú>Ȃ;-spp|u ):0:ptwz5wg8,sjĐsCVD61|E[;=la-e-nXbكk"nSK K@'+O](Gkc¤4Ț~>}38_czQw]3]dmK؋zػ~iy<֎t<g}=6Ugl:%,wt)& ^+ߵ}<8\,EЋ61c}:?J'M+$4ԠLit>މVΑQ?`d5\LH4+&>cxQ HI5-%/Qd'Va,u `㣀°Ap$Kt0.g)̾ oͩ|L'.z2B'_ ,/ү 0PZp-&V:׆p̼`ҕYJ|z.f;T~֏#j<9jV^\E{ 7tIJZ͐תF-8876\q>)JR'(Mv'F G!B"X)‚OdlD`Wl?#Ss:A4b&/Dp/JqK;}*tm~p64:$z 3uQI{2rхb@S:3"e@*9w(h~)19s8L2t(/x"Zb#$x(HgDP1U:Eh~&=|0@F$xbFxHҏe e S@Rk 'xA e)3ݸKFߍS-Kgg+ WO]N/ ZveJΐW" $E@M'fa6S|tCWEs 1*H$!×>.^@;6ELAkN:.>>xYwF!0ճ #dQ0جuf23D@ Kt8/D@"  .ߍ4eLQn1[㸥\d(瓅,O^nLf|{ٯp*x\d%@&H?D `ƛ-N+`Be\HRB@1{XtWk< NY" H$f.D`fY\'+`5Py E}^tP f( 2[D@" R<{JKRzSYW>0 cIE" H[qn&a IR" H$D@" H$D@" H$D@" H$D@" H$D@" H$D@" H$D@" H$D@" H$D@" H$D@" H$D ț;CP5"x2 p[@S7e 1}eB=UfDCh=BpC#=~=g%g#!4X&[J׍0̲g\~qGޛzx{cFeʶR.вܜN23!V)kTI=啘B xbO5>: ~uz=G_suF+Q}lZ2Qc(h[ȷMGаF0OQu#9K`D2Y[ݘro FHW +:BjuޒIKHJ2Ad5*H d)˷ }elmOl|'&Ma) 9}Juah%i$F# LM_}Nr ^D0+G/9%xnd? c._u0ϑ+i8(_ƴ4-yڳ S'#)d;kFU!Q/cWi'2 ^& WQr M*l oY$0ūsK꺱T|4L<:Ӓ`fUelM-Wʫw(^lQ!+gjFڒ⨗b_C 烘1~i;떹(|8KdVN%ޮHIg8&XQt$rAم@H^3Ueݘ.!Wݘ.L>#(eȗ;qw̥!V)km)z*/ƭ0'e!ߖXbGZ)C7FNe.Yt7Y7o,jKKi23R֨Ff+,n%@2cVg1|&fi-(nVMQvb'(JbY{!A ,$wQxǒW"1=5VCtF&Oђ6c=͹&0$^ñw(L;*jr]B$^̭+e"1oX 櫥DNhݨNׂ׺"XQn2BkມX/\#8Ս BFk) QfwK Pjz6!4ͿӞ=u~k%>5#xGݑϝQs}8͌+ZC;+kTQ_O`,@qJTjRƢ5=$ӷ^) E-آ- 7 .A< <ց ;'.ņfRW=9Bz߆غ,7)meэuIg _)"qeh|t ֗xꘀL VUM/4hEEVP_֊? kƓAԾcx]bݓ]U8ޅjK+W=ue(4r6v!goEApƐpB^aucc%u=c䷷ Z*_u¹j757\J ,izhz2k|mX=(ePZ|gwF +cuouWKL(kjTųXQGjGy+Qoyi\|:sUz٫(=\oO^lss`XSY%PuU6 tHж&P?Fk!^XBNoi[aMh-ë5Ne*_wzJl&U֌ -Pm^{l }qvRlB T9Kxhաҙ2}K,P(`K"T}sRʛMȃH{H1tuZN}׍@}T.W=^k|'لL,ܫaˬ3@o]l`!o̹9F N)nm!j8чChci՚nr˃xy<\jGn +uC-aAR'eK (!t\r9[Bc)[by{(*Kꆞ-%Pגճ)QCE(ejt);{=6 FVSY_Y>pjڡpBd]1 d2_ogV 6-QSK:PӨFmK~e`9xڴv0AIgDZ.jw) ,tLVzf5k}reآ)bС*j3Bo;J;jenИ.CX>q<ډesk&M1?.GTՋ?/Y@#21 xD>4,)50OT錏c& r<$ .IiwX ",8T8'Cj_T-Oسv7_4i!g,Y#z t M< 3'>#9 0@uEҺj*ܩ'~p v7\F}LpZPq&\^l& RIZIq I44| -Jk{600'pӉB 4?j[?SDKKY72u=z+%> V7xphߖ2]jߺlq^6̵R8[Z+ܥ3@oyR{C3 栳TFa%2|%}~8G ~+I ғoX{9jͦ.`Q9(&hv>Q7/d!(tg! dʳeDyly E4Xi aJgmh5R*41M@GĪv?Nv5wSo_hF_H@%Nv&s Lx,"#X)J틥YeKJf[{j[= :"nLuC[L<;kG2{XR a2 8`uބ]s= C2|LjQ.]|MxD3nXnk|qk-tC0ͥLK@o#>ɠGax 3T*ŚJI>L&z 6y~GvnZu ϰo(X(44"t?􎀟f$M x2Mjڵr C31ih;.+x_T}\ңuɹ`q)A kc-j!}~SgP҆A%vr {fS!lvSc{Ƈ wqн{Y"Qzb34o-L:GCHxYiXZLi 9 /"Hۧ)oH~"S(5UU/q CjBw{(8L4Ŷ_ߔKj[w7ŒK\v gM;eoL/,ŻܫFsUm_"pUTV-LP(SoXʨJޫ dt-߁Ul¥Kk+qp-"^×6!/F9(͛3L1jh@k/NWW}ds{9|ru5/jmL'%H;D\Ga tB='5vïOӐ&=n.V__f'^S3"q+Yp`je@8Gkc2x(7ǍVh,|^ho٬-62KT]ặ旸GwBm((RlH _īoKS¦ySa44_5Ś6!8;%ud{}"m=0;zayKz`0jv|wvҮq|HL2 )J4YB^Z|7F$2_iCFq^ &2M2B6ĮbބL*g _~S'Rj6{ #~Iy4/R8hkUrF"R܆RN*O#(6"W|i,WJDrTJ0E Vh*3;ٓUʱ*LRolgT$瑥fѰhUCkLVib=j]h63밫+QU +{c1)dg ʾ?{8^+`Oi1~U2QF(\&] ^l15_ġ,QECِxz89XE}|Q\feoК2eRdzMEVkUe_)}C4鿴L+E@&`弨S0Oqi(cXi>x-Ҵac%\{Wc1tdN"WDF)>|2j1['6M&‘14w [[l]r/ eۥxF)mވ3ED$@$@$@$@$_ۃ%&Ĝ}&%ЄV7!"NnbּVXSk94VbNXCS1 [3m8ʅ3Eg$@$@$@$@$@!':jѮ ư˵`6EhٞӾPb&/6sk}PY@"2Ń$Q a<J So2\    BvOdv^G#F/@;CgXaEAoR4>Ԟ#c K϶Xp&ʨ0 @iBhQO&g|]d[b9=7w] @`EԌYTql§;^4 ݑ ,&B|N$@$@$@ '/J-*3Xӊw$@Fܙ28)+2@" _    ˽pOc55gBNƉZB6cd_?Cq'`A#Ő|14L    =P%EI^'ʁ2% J ibp[#뉘'$HVQh,d@ @`z 4Bz@ @O9fq@amd\| YE"rG$@$@$@$@fx{:0H@:=|HcY    |̞ӄ%eS'01=aaw3]1}x?ã*mkV],&+P7gul09Wp8t_Pe81 3 ~h./g' 2Eq*^5W``  DPQÈA:tT悕;JلZ20Z L0/̵:(u5>N/2e쥄I1 H ,Z3JbFks[ѩ@婶O˃m[o"1v=Co+ +KP90Ս&<mүU>44gЈKo|_о c֌hBW4݇ZWנ=;(ǓWTtӊq^ 7%e3}{{3 AUZa_eEiB⤡JpO rكZr< x[qe+%AD YE(/|rR[X|C<_ʑ,3_+ 0iC HHHH d2r\Ol@ϕ\yE|+;\/˳8Kx>oBjMKzG߄pҺ /^{,z߄p|*G߄i9jb gVyMPNLoqoLcqSr|d*bK/B4[j\}BV^*i"g*? k*2}4RQ u|]\2waWo+2& !@ѿKomм׈}i L/!6&i>DH$@$@$@$@& wfs?.ĶB$=FMgA, 9T>o=):&C0z"+%5Ԕk|o 3@ MÒ.c#;2A*sMA䵷2RU>J93jQi o6/dM#x^(fǬAl-p䢣=[2|[>ByTHHHHЛz^~IoU> [jh-Oø˵ N_^ZȲ'KFfEDD+r^ܕؚY(6+3 , Uvgb´k!@VbLTD^x FJO'   H S vt;iCXaMx}80;2NJ o.:&g&\TSG4 tB%_fIL}9Cllk/1{(çX\ș0Т4¦C-yӼvվiAY+Y-w^͸ j2mN,̗i~ B$@$@$@qp >:[)Ж|8}KH'WX+;Wee _t7|=pt$WZޫڬV1*ǟȃ]WclkS̬x0̺Fjx25Ȝa:̛#^,9KH54I=OY; 9IڨʵeI,of bK! N{b[#kFm[ϋW*Qy(G&&Cْr%y<7;NሚǡD7c*:9!N [U;ei*dSwݽVNHI_CӘO#t7͛Ą~#`SRd?0kL_f|r#L~ @-֠snA_Cl0yyIVWCql¥KРJ\Co{cx01m޾W76{☒ 4Z3VO ۮ,1kUp (@Y^,U$f&WNE22g= .S{[&o˝}8>z$v&S"?|M͈( r~>i,N{ k~z)Mv8pGTצgZG={1ܒ3FҡW7' yYv'Z8oK!Xi>V|J$@$@$@$Ee;slsGҁ.pׇkiӗYʿuڍO.6k̸nV&n'tݎaYGb !/knIwr<(+NQ늬YF ,w[̗J rYdbȝOcJ*o."[V|e -e_4[+>%   "n9]m[}86?=mMԷD#Nj0 j4}q%Gmr̗" n%   G@·n?V;i+rG[~szϢG LK'߹S RyIZ}~qk&„ YSO{wrM_{ f /Ѱ2í{i lE~;FtGHi3+pϏcDN78yKT&5#CeYځ,-B0ϣPhZ[ڮo8,3_bVR$˗,m8xHHHH m+?:C`g3~UZY"9;&z\e*B;ʾÖ؈5 |~ի ib jU) a$Hx%}rrcs,~5C9w1e+Gs?b͇OIHHHH`܈>皋Hb($4HHHHHH " Bd0S++ݰ?fQ,} 1[k5Cơ yՕHHHHHMn& B: RQh[i>7$-} /<WV"}F NIHHHH4{4բLư˹y^U» D#8I3Μh} @{HQl>]WцAV u:aY׉Eh~GtA$@$@$@$@I@pJS;jU:ivݻ8sK,$&pqj2E$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@y pk̼A G4g <3h}'3>!     &EG 0a糨 ;@ _:uj<' ̔Y8FW :r:C      As#ۭ o %?)ݳ.xl.HHHHHH NTir>-}B M0Yx qNlDX|Hq˒#wE_*?.HHHHHH{N8|] Yje)6-E[p!pL|?Y/}M Ҝw(lCG|D$@$@$@$@$@%J{O8_Lz1ywd k9&V q-K2_Wޏ˒{UTbOHw$@$@$@$@$@$@$@@;:zta7،=AɼzܮSjI$@$@$@$@$@'`lg8}i)qgnX k<~8ѫQ-6KQ!Zg[Tbwv K=f帬"qu,3_m [EO,&+P7hnӠicDz߉ItIHG&[5եHHHHHC@kz* _l(VY<dpcþ04Ӷn:؆mȁQ8 N ^݇m$6#v^o`oDǠnk_4ͫjh,Tg*71o}l`&W *Mxz%d]5a҆xZQs|eIEJ ϟŎCސ @i5f9_'6Jt8}V <\'VdMolīYP$.hwym Y7!6,SM 7!\%*qZ:Ãuk&̗|Jݗ u^&g+H"t߄0sf GjW?Q~tA$@$@$@$@$@$(l+1nwL$N_J) ̠OMcĩy bŗFI?[s Y>B#g oM{ j_5~WFpdo6m!k,byhWV22 j44GFYehWA: gU<~de%|gkZ#%ՔHHHHHJ~]߮>|~{[հ,ÖZk='WЎpZ?oĭ2b+VoB5t}֞3bKWc>¤ sԖG ,zA'0FmėzXl['֔Tc4 %}|ɍ+{%[} RΕ<;/f\t ]fL` K4% ;{ U!F-lIHHHHH`0螐nA{DkI8}9F- [o{:(9)[q\X{^Xk?R&{5*v'qQ ˲14m\!ږ*+PeWDȴQYOi@ ,*\5Zh="3\Yk+2;Nሚ7c*jN?`:Ed1۳B)LIǃP7<Ŭs.ff6ē-IHHHHJ@A^5\[jK8g`ӗ-&zp4/^b^˭3eK czRj==ux[!&e:*:է{u2;cPy " 3y_C@<ՔW(,hq N%Jgyߌix5vge遚Cm~M̈HuK:vm})uXK@}<=4GޤWձ^%JGݐ @힨slsGR1]tӗgwZ׏M)pTaYG%ǚRǔT`G=dRee WRuڍO.{j< hIJʭzQcBzlLY@$@$@$@$@$@ nؚͦ71c#Xs2Kl/2&v*E,΋ao6}¥j6Y. 3_ Hykr:Y9(]&3պ ꌕY O_] @uO'q L{>!mݴeKѢA^A:%n]Qc:Z0t5jhѣe8 jt=-8q5.w<0rN']*\-dD z|W%v{FCVobІHHHHH &o=Ś9r}swIh!KܘZseSDex `[?Jߐܘ|_*B     @iRAPSw>R2<ɧҐv`&K Nqd Li^&\×ɜ:zt[c ^QqOV4|@$@$@$@$@$@$Ξs,RD|]?                                                EJ.EmIH>ڀxncaFH@zVA73w {aeΎ>I`!Y,v4,$? Sg_L}c3iYB eO+TZBo|tck&qa %&|jrxIjC)~}@% ۢUL9jt/. V~9{Vr,~wO$P6fOlkVc*DItG'2}۹w&,3Xv)/&uؚK?_Թ;WK$YtLaho#vMQ6<֌ʁY슮N)ڄ\cKe}Vj\V"F4qW0tWߋg7%14w{)4_%ǜU폾v96UKYc>tx?c*?5YYU*$#˓_h=SFE'mf'U'p--PcWioKpKs9\q<EIQW X2jAfS_ĩd_pφb鿚:[2Jr4Tm)Nx"R2*xd:J.m x G1۳Nɢa[ol/\Ĉ몭 l#%""ȗ\'gsoB>8i>`ul1ǚ?#힨yV6U}szyl9pe :, Kjγ_k"kp4Ng_镖Dx:VQԐ$ UڤF!Wk6-xt#j~[ ,ŦhK}ֶڮ&4 Ozq_gϓd.wM^'Ƒx#iP~D:MwE)Fp祮 D9 {|t_(,T^}J+*ozeQA*JBre%`GO*\S+y3%ZD}J3͇˰z;zNY=! bMdC8J \ѿ}Qx;;v5)p ^j[]ڇaj<A>3bS ^$ ;,z 4BIrVA9K*tj;U QX!Bl UhKuKzoWGlcI6B{lń54GMwQ/ʭӻWdIQa{&SKT[dkjv[_-ǜuVM/ȶSY$֣M~"#0pR\W"D=+MS<ޏ˒sK+ ] è*B ;+Y)XJn-Vc~e8|4Ge2_?J?* ))G;3niHVeԽ+.1 t5=QunJskrRH:cyGmBH%]ZbCq ^P{npY/T|AA1|yF:vGW&YS>(;,o.2|^{SisXH"6D!'LʨxS @ QڨσϸYROɨ뢛\u$ZZv6ᆥ㇃8}(vi*dawnPYrg%>!a~t]9gbrK7wv :Xa4 0e -H]o@L*_2*^Uf\zx? %{դ_R*Lաt67pb! Qe<ɾ +zܓg٢H(yVx(N{we9"\"7l6g +ȃjDsx<'(H,;d$:=C1͇[< *ۦXi>C+E#1_gMw߄x8CX+72{RH L`KrڄNoIQn5rk?W:^~6xܬ!Tda3emO$*CYK17~˙d ^3SQ;J,tڦz`12O6 Ċ̾ ʍֹkxţoBl]$@krKkSqW߄y)[RڶϬj5,VhK7!'*qZwP9.Ӈ/jAnx>oBiibWmrѬHW*B&mmxas sMo]ʨlroKjC⢍@^"4wfODLH]8T )).}BBn[DNHΗxeCRTG?cP]:f¿K_MG\s?b]l.w9ۈma|z W{/ xYlq:{rM:q:oVKthԛ7!<[lqJ1]#1ފ ZFBuzڸTt_ˤ2H4R n ltfu6f+rJKoB(3STEA72\%CLF|Fo#Es;g S8kqVN9!7i9%ʡFGU ati>$ .gyyo9xq3F5ZW y\k0bJ\w+EC '(p7,2-a֕xȫ.Ni33ҵFi蝄LmQaJl[AuHQ&-!*v:_=KυYm^gK~"bT7nWjf-ɭp gLɛeÔ्2d}h=0h׏c+%mՊحgiagЩƸXc.ʋy|{*sWbجA\&~j(6N:j:lJTJcʒA}gSCфovt^qƫ.yv2Z/3v4&|j?!͓쎰e?5;S`%.,x*idtho޵a6vrPſk;>%z=܂eƚO^ے9zj)oY QY$лʨMӸ;U-,>\t+ߦ_Q98M^[ux+c{:$O&W r/Qsg $G~e锣`6yOqqeE/:,a zf\#.Zw6vYxXliv咱  ]lGXTQ+Vib"s<&KQaJ m"Gev+T أ}ǎtm=[MX{W:R^9ɍ-5J!}ā f5s-0i>d޲YF}$|3G ..wS[rX3c.?H'nסV绾.B}ѺjnHMn QLi}(}}Fҽ27 ~MG߄I7^{H^o}h kĭ򥋭B}cCb{Blc ی)LC4ĊSoBۆ7Ӕ)*i3] SgAJWq*lQd#nqXNXK;Ur.*- zoFط-Y%2j3$|}gw/Oa{rB2kU=jPePs5Cql/K 5&jamIUn\:|?yex =#iy6ye1ONݏ. t! ǗĥtǓ-ٱ!VkyY|Ui3tP%]Fc|[>HdgZ[mZߟW bݯqSJtgw%^\F1;b[;%%YYXXI&AfS@7pm =*o5Rplu m>mcB3۟+;D߫JOl2|B5a5eiLsچ9È;^ w0Wᅵ#,V# }Yب`C4"}CEe5]KTrR{[Ӵ! S}(lFN9ge?8ej<}]']ߩ9KӼ2|y_a4&|-9AA&qD7ge_u>m7s#LC]KNԃi\^Cp(By6 dRD=boy6_al9jKa%/yo4H]Syˁ XA>\Y9 lq1Evi^ ,1(0oYiS;NJ0 ze-Cecx0փ_ߔ_qXF P%=A)x *9$Eh (c%\KAΕ8޶/b7Z5^1Ftq.w:帡q6!'$˨^mIz ѻyZCR2JC]ӨeAy޻KrDT4:ge%,3<<28~ (^">?u~&#V>5`Uɍ189`8^;n Gd[7S!ډ\BR'3YM Y/4P |^h yYr]aS&.g~-?T:DKr׽V7焛_a{~u ;Z2]PcZx_of^a5GuGS)SMeT%qj(yR7jqDe{o̾ba*Sd,Dͼ3KC'ҹA~LLC@siޡo/÷ M,]}*V׻:YuߺvF:^o~"s". ZRon}TAhQWj/1tj `A2UȺaƢ̬_~:t@_ijR2,mݮM %X]nHl >e OØӼJ)A7A}kC8?ehs+QF2KplNZs ^C<>UVٞ9?εtMarkL/ͶVYkGr0`Q%fF<]?l[MK^ iIt<*{‰xeRȾ 㖔Aĸ wEWN3/ZoqyG<|}XJ5ܼ!TjOnA_=k[7kڶFp_5Eb k~n`'䎑a֖^"Mˬ?J⌗V|TAe^xL;?xI6KRZR|ͩeh~ ]*ʇHhcD\Bm6?WX=#v!Jw8AÎ>{4TqkAWjˊkbKXqr8 Get뽽]N1J<(MV΢ -2E7O%쯏(NekG.]%ӆ;{g Nx|]ƐVJ-ZVHe> tqcmf}mw\'3Ѐzg{ ğ~|dt#̤ohzL}Q%v@eʍaSDolmRV2.;!wN'yevk̵!)\" kOo95Ƴ22ij8s祐 f%N$s/"xjzRҙrكϟEOƺ |#, *K lD <=f/Cw\3XeT8^@k:9w\^ r$9YZ9܋pɰo_>J9,秡L _Y_"Gr -Xi> U=߾JfK̎,r4"~OOOJmJQo~ 3/~}~CS9wδLboSKQy|_PKCQJFK:uMFF볌~LJl)JN>u]o[kJ/ęYFl3uq^2ˇL(ٔSļx̾#qyI^5sGJӪH({Uy΃{S*-[c*mrV(75bhMz6G:H^;fv5D-,~|H Hcs=X4)X-*OPZc K41w ZH j ^37nqdա # -+[)dDG-ڝ{[ aW .)$=2 '/J_C'QڢٽYi54 K4_g:-Oȇ|qy G (E_^թ7 2xIOL4  ;s ǡPb[Ҽ߷7EB~28ܚ& ./J_}Elg>478*R<3{b% @.C6:喟Kk{n|1y3foYF$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$.Q\փHifIL=y N|CG9s}9^w,C2G<|̈́;1q3h3cgF-c dG]Ԓ]oWA$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$@$940PE к`pxHH IqX    &=_,Ƅk+KϰPжZ`* Z hIP2 n|Rfwz   R쁚X/E:{b< ˀA2iP9eUU8B8g.p8] O| ~@$@$Zʰa. /h 4Tͥ(D)>)3D8@:IH ){<nq:J듒ULJ$@$@$@$@!Pv/ԭؠ c aCpvw`(VuocsO쯬ƽzv5Ex ;pKߨ*'$݋ :tkwJ2:[XTTcuX%4ƭQ{^:aN`,.[ p*4&[.-s%^YMAA[*<,FWc鵍}h,>,zlg?6Ƶ+m#P1;l=߳׈=}Gԇد+ӈj]S0O܄r7{p貗{]Ž&scO{`%)}*6l^%pTXGwq\k7bS\CE2]MK?ŚTEnZu|IC$@$@$@$@pR'E"oB仳נ{oB$ ᑥx}C7!RWk3y=鲊H !'߄ZG٥;&Dƅx6약hNGP}"fboBܵd^o ܻ7!=DzH#Ce鵍W1oB`[]OhU6e9E^0Vg)—%RZ΅oȠ&F5Xs2B͊.3كkc*‹+ ASA< TJ+ܟχ= ^xS{SI"oL$@$@$@$@9$`"=B2ڟWoh'\k#c#EmPmMաUTzK@j":駦ǖY͵B}菉^rzp\:?NеZ™D[MRGKt}#81g>Oj8ۢ +>i<6ZgkWUY/egom&4TuqHm5=r,C w)U eQ\t.6}U1uٶ_Y.1p%^oD^Oe|ISƴ]Bh ?#V.oOA    @GO2.Ӊ+oBwE>y)-yꒇ{`+ʰE8YkEYv|nݾAcȓT=d&/,l 3 kY5U_Y_lM/!ڣeUWTd3е}-1rM wX{XϺ<<^.e+<1I1 Y8HHHHH 9Tu\s~_Vm漆/9N$! &k)5&ow$HnRn$˘c9;aDx,s0ح_M)*A _J]M 6"q6v(CڍR tv6⳦9!.S2<۠}K~-o.Cya8MZ=gQUUnTzxr\yTW?SiםkoI863(75 eZ. L$@$@$@$@1 'ƅQك[oi?n/ԕ)$z;?l.ayU>uY_8s [X!rLzXR%𔫩 G 6-@5l0ۆi4:zK1\-{ֲyhVGgQaaH΍rǗe@ti[,ITXLz%$F1KUQuyx*K2p鼂m|q    kg2GZ@T/øѠX7'b2E K"|o%֣g^_jd2u2< ASN=_E&}fYQh/CۘZFlIնaJ[nm>4d^nҊJЪWп8cJY*,ꊓm\ncyN~uSi/g.3-WOJ.B$@$@$@$@$@rA9?0e n Ozl=V~t^E cq8A-]]޴ÅX.48k8w %cӨn@L"vۤT15CbUюxRe%E&m"(Żb}}KtіJ&)b:P`pA: uY OqYۓ52 L"u~*uryxRr] '    dg)qs[sŝܚ?e&/犭 ph)_pI wq7𡻄n"s[~w'/~c4^1bSaikj asLٴzl6)I/2.}3+#*6 TSơg;Bx[_ LrvS=)9+ @r|nM.)+wk=IGïbBه>>*8s] bs%s/gNq>\YT, ғ rT +,s. \i>)„$@$@$@$@$Ey8olQ6Hsʞ6~ՕČ}PKR&T/"u̽Xo"  $mSo n cH~+'v9q􉚗mEH+4J>~$?4 ]b 4J}fmpHHH<{=WyŻ()6*cyȷaѮ_ =L!sYk.6aHHHrD lղt b` "?|+q(ޑ@wzϋYk^iHHHC ɱe!I9_bL>@/`B3\.HHHH J{mgs N`?n/֖q4-"|!70^ih(aJ!|85DQW 3y7WՑχ˶26|5&6/0VbM$'Oe47k;Tcu&">t=;c>J*жXq)K&mc{-XmJ?ꏽsc& l't܀7j zG𝫩{Q!~r4eUxXYo"QQ4tݬDO#.`p V}.KJv> ݣemݱt T+Y]Fu q*Լ7VƸysq#KJ&uc12e6DWVRSL4·>rK6<AE3yn]zvٞx{0L4LE:l4'3{&H*zr-x)VMi{7bB1߄7i.-]Q- xyoB$.Uf؃./mC6PYS U (RvޔEP val=N$a?s)I7x^,7 tWl'ncMZ"MAq8DlDC-еO'GprR) 24atZ>@c ʖ"+*;]ѢH)3xߟt!.$.ۆ߀؈AuK T[e"GBİR)KbcCg:5쩌E-w6!eN ZSi ץ"gw2m oK7x!5^Yщue6WXz -v&   ԟN@:zq8Z0 `C9- pjqmf]E |eF$I@<=KfKZV^exw9FDE8XiHu44Zh'v 4 Gn-Rh}d|:+'X$@wS#cpۄ ۚcGuhu$?ljT9z*8ۢ^ 1gTRPZ*׮`܇J<ĭ$jD_5R~Gx"$s=4F[艴t Mtbwkd->IHWW&mC4(O)Eނ-ZxdPeHHH('~lBYPl)%&K3nfϼmVfTCϨa   HA oę[j _Vˌ&$AgĶgNm2/d șR \SVgzOж+r\D6OeZ,ޝrb1'+ZcWj ?1twwyJ wUa!Kwg-Ylm`F   'PE q.b'pײ_L>w^b'_K^y7=g<T5E>zdu?EEFJBغ &+8(bw̎Q'/WU"߃ {n|FE8/d ]:FHHHrB{bo=D2O>fO59 #3WNdIi1""7*8JK|^v|̃HXiz.Nú{Lip|5z z*jf< M r\dA-ҕb'p&ۨ9bWrOc!s{"A:|!ĊdE5\Q޴3eDt%;ީVܤ:z*jɊ1UvFI<+4w?ã ž(|5C&9кO36bHHH)K=Q[*E,0ގaE4K̥vʼn47?U<1]]Fc/oܺՖ˸ `hܵ2epDR.o}=}-NL=iC{I\\15{ûNktmڹaocm36R$@$@$@$0 rO;qM+(=;cNcwbrsZ849V(qtL 7?M,u8$:]J3YChք\M`kh]goE-˜2㸿FJsg-UVc|*{*ݷCCb%,K!3W:ġmC/@b;nt5cVTa;^7\{tZ̵ [xI$@$@$@39A8ij߹]?s)j:cc6c`-^j""x'KܻlSI$.0sʋ,|~R疱.IB9ڟP;~[yzoM6#gč,(UT'XK GO~lMK;g=#HHHH`cOtX+QN҈7 y{Nn}݆"%M_~"vq#mT 9-XI(Tb%/X$i<׃rdb9}0Ƅ,g˨;tR%>^[f$O-np3C֔6/c8e* ӚSJ5uYF޴ڡ+ x&~Mǯ-Hӯ/IW5Fp4OǴ1a$lA ȓ.KېEe,4'yGٲHHHc|* u{W%+ :[3l_ޫoLDAb?uibo֋_q+SbuKx @l6 PR089&l"SY-X\ >/[p{ QxVz& JTsmŒ wtOxgǜ$@Bh [,c=&"D,7!$x)d1_hvP [Z@#Ztۡ }LԨҢa)Fc7 Ia< LHمPִvJcqjv&+|Ų@8zMA"LESyD5.*Z<k ONbP['ɸN9aW\a*o*Oa['I5< FkcLĝ5++S!6X96.i|QnMo ojH% 44X \yå@pM@ȅ2_ 3I#kwzb1O%*ˍ]`tiQ̄\ܘrjf#:ư ](D֒vgx/eĥc h0֊#"U'<̕ЈHA-|م"{"Rby7q*.]U[ʎhߒK1Vʜ IgzRˁ{[(mFa6Gm:8 hxc=^m% @^H=Q /7`Be/$]_;?s^iL\ZBD]hyS?; $=;]G8!~&8 O0; b6%AW"tt\ⳅY91a,6 ݶ\"!-5G]^ <vh2FI"iԄ 0pZ% pG%ƱRGݟZ\"Nc!/#Á2!jF84iQLyٱ6^g >Әv(zM GևsxDKƮ*VjOl9BIHHHH&سQT\j'%~v;3jm+ksOl6&MDL'TCxp񴜬1 :^{O]TV-,$;a_sɌ䯅-{3/ٹCƊ}r4s:%BnUۺqet}aw}L6$+`(dC'>뷊WKtlŸcRq /7͝ȴ2SiֺlF>T_kc6Zg6oyϧx໩f|dHHHHHA@` q~%Zu:&\IY*IJB PL}n`!k/eSbC&SnXĦ;ʱ% cOrDԖ['9AcWH?HPRJ=l +Bk-:y2HHHH`yT,* @ V<*2W,f7kX !6HHHHrJ ٧->~)! 3*`9R8RPr8Nd0RiQ}v<˰5&ņ-"V BTNGC8:TE71ܪjht 706i%+5C%S &t܀7jN;W hHc}D׳:,bnf3͙ФoEC܌mѮ~/'=ED_8h0*<";<gɇ-!|v/+ [XMVvB**,Y P=^uz;&Dƅx6"ny;eji.3swmdRNk)(( ĭ.LJ$@$@$@@Λ*:ǩؙg.uv {eJEPd,S[pgRU1,qZT57paq]'O>Us>5aOl?ΉH=3ț}M!t߆[]݌&o\A|Ө~@a[3meʼEFuρ0+=} ű!S$lۛ* ONb\'eL/*-}"e/ӜQl{G ʼm8؟$ʯF+#$Ɔz [   SjĎ^=*n)6r;ndvw~469"Tp-+Q/ {xU2nLS\OiL[w O$#xIF i)*8U\%`lQxVW1XY-pej4[i 6^(oȬEu_GG@]I4\[Dcj@Y\3}ⲵ ȯʡi$]CDLM( q={,K7y1tЗ~mWTEQ &r ྾lKƎX< =Dz!PZ[z@S._OgaLg.pHr\;&exm {jFUَĪ2z;z8ˮ{j&}3OVU9$ӣ2iQrΙ lGI|8sZls4T "8DbCtLP כ޶[J?Ãl}Q1Z03 ҶH2">}12i1<|o8r`$ WF=?pG)e^,ŷ?闺ܗ%RL.ó[,2{prߓZc_a<gK\>&у!kKX>:}\JmՏ^'ex$ƪSORώn 㩊Ƙ!S3 VH SlTGjpQ#qI2IȬ|"}odLHHHH`o/Y{?vޅ(?s9yhWpЩw=O rZIL-Tl15!m vLcՇz5˄)7:Jl#&[K[-j0 cxzf gv)+Q>/s) zehSSTszmDC$@$@$@$0KxuOz<[O0S"v\E.b3fjOqN-9wyOf/ĭ/࿑1!P3d(z T*N-yV90cMUq[WNE5Qao 8_ҰURQ sBrW晈HHH tb /7`Be/@,w6iF?sy*|,[;ZzoW bpwD r:= Ug.!>T7$]ٱ 疣.mj}(7TcqluxRZ+v^@¶0/.,\T^6q2uMĩr~su9Pܢ< @[L͚k3.Rm ?Ul1]%7=έ^e(\Go]+yJ(7Q67<+oGb6q91%#IHHHIT`gwp'^9\mwuGt+Z˫c]!c QS;n}G"u"18Z-OqXuR'MԾXs)vF"<&-鋅Qam6A|EJ\4;zҔhQ=w3E%<>J="xWhpM~C|y?e%o QߍT송XS1aB4!'{dJ]PVQVch1%#IHHHR'77[tS~JiLb:,zuIjŲ= 'sZP#߮sֹ[)\*j <N?veIs=*>zuO-3 |nQj6fN9 䒁}iQ> P L9gI;{ڪq*Gm)8gPi{ktyEQj4}`&Q>zo _[䔟,mu1HHHf?ZsG3MuASK9v̝WT~*‹И{#sq1]K[xg*Фy^6[+rc9u{IJbj-Op̛9ÎtU-UvO;*mcfQ>79MK$@$@$@yA@$8#:!S1c prcg?Z_DXW6ocsz EJ\WiLwc@i[v"X[8J i[衮B9ݓJ-ʓi$n|GjgQ+5RExw8ʪ9wyU&֣ώVj#   !@纨52﫞(!Hۦ;BGRD濅) 0o{iQ96=?G(s+62e<&8vY$(S}O$@$@$@s^뱵\'6ժQGؠ"y ,O˗fR̛IU}`Z b+YIA$@$@$@յXPi̤$\& }ؑBk0n俅nK2eޢfC)il^1 @XYKAY"pf=5$b%yY Ͷ  "UB.*_#;R)e`ul> @Npi̜`P l8K5;>ɦd" /jѽbsq4~E c_=X3,XYE- 0\ cM?+(zGڧ\(X,Ӻ8?Jz/H;ݽɲ {$/vRpG6B I U):ќ=#L$@$@$<=Q덱nϹί[ 5 B-SUWbM(B]3}EY'Jo$ m)hZm}X+FIK7Z ݚi<1 @'@^K,H qb2~p 1G*աoB=z4 ja \>x p*@q\abx"@Ji_35m񿕣^xB h1J 7= s@!Ÿ`>aý%T'{}\+ d]SK6 Y<cxX hAl. m'  < C"JIH2$pTpkm\gĽgx\L8ot]1ݢ$ )pR/<0$o.UDbQ*3BQ[Zt\S @H3 Dϼ2RE^Yd1Cv܊M1lt|_a^.P+ȷ =$@$@ Ж~e==`|pT$b(AEgp0^;@tcz*tdv(Eb<9ěQ-a_鿒&ņ-"׼BTNGC8k1ܪ ϵ|h5.A&ix8jqF=]=/?0u:bh񴤌8uk{xw%[Q?,b Jms7ɥ&;1^ lBKfzB =MiDߌ~[ Oa[-I}1Fb%ZVvt)li-5?Q feӋJKa|/ӜQldD27ZNic!S!CG+&1g Oy$&5ny9)'- F%43D4떷&UZݾ] ޱI/<鲋 `SPEhDl` {m&Ж<ˬ0%B21=KHHH@#vntqVwKar<[v 73ð$3ґfhH|zCj}u} 4)Gn6}JmⳐ\#(>9t[%:\ ܨ_Y-оe2,RrEys 't;ecɖ1 dᦐ,H#ҲPWc@ַnnSy\u^,첚 )[\Úԕhdz f*{_̄!fֳhUbtx(g&ȸۗ節GסETfzWa$;X^vNZH96&۽âo{5` HCa1٤5MbʞMHHH$އ:zq*`epHL{2e2Y/c$ᴕF8>mjBA͞3$x} L{Xa >byZ' j'`mo\b7I HHHwei(m}ԁsva(S*`Ԗ.]\%@/]V$Sn+˓jzǟɅ3_ssTS7$uKI93xCe U<|B eMaaUߝ/-|+_T1M/35ka>(aƙcd&TfVHMRY0D*9 G ZrµeDI5U5Tutf=eI`xZ-G=XCU!h}Lx?y-MbCi<7&cx7q=C`-іPE@}"p0llߺK/vrgB,> t"Y |A_)t9 Ga W^SSnGIHH`p$/p<K52pxt^L% +<;SĖ<(߯b$|{܏k}-vq?CKºb\]*=."yɅ=%[ߖO($tsL 68ةE;e# ` >o6! /26(L lۢߛ4iJK81[mo"<1UjLkc1e |% i˷VQ[k%|n(S-B&ķhBM(& րZܧUEIbkRkPL+RY%e+gC<1q['ܳ쏷e*>`(r88'Ҿ3gu%Eu0}J]*ϭʷ]Tt óz01S[V`\^E$@$@@ZD^1#b?s 'ā(Sؤn|9/{k 㡄^%W.1x}aBIz+[:9/ e5@l4my7ai>kyJV2зd᪶)ՌN*&"'(eT gR蕡PPo)6†%y#d6q^'Q͕hfM%3TCgҷ-e._!X^di{jJ%nPbTjS;[<@$@$@$N y:381=8?K˕>*?޿̘*cח% #;zCލ{Q&PM4¢ 扄1hMSy+E@~`Hvwj*c (,X+A1 %m&+qK Q-֦*8EXlK|kJ CY U<4yI2-ѯxb~߉ 1Z~/< 9)0J9eErcf̰ESOSgIHH`<Ζ0+?L~O*7Wyh_ylw:- Df[=OvBPo\6g[C$9Q`\m5F"9/D@IDATjSr%0U6R0P7rNډ/GgBI{p)0H]8+.'e=( J%=/#xQޕQ6_M8ك3xy%k`_Pyd/NȈ1e3g}Bkp t3M(|1؊+:+C;rt٨5.Ol d^`ejO4M, x }Qޚf;L'Z<"f@ŜKpM PtM@Æo0Xw6}bbU:SҏlR]b9efghٷblS2_/J4&f0xtU3fU@O5g!#&P^ ϋJ`z.Ok~o{ΈIHHy")'N}#}c來25!G?r&VDycDn\"%OMLXܞ9kF^@YLgx'IHH %͡B .vKNje-akϺ:l 1g4 HHx4OdVD[3Թa6K24:'L`[b~$ 61. /+}fN77cB   IήTNHH`8aƺ3sFy*J$0[^@ԫ+1}ԭbHHH+'efpIX}h ' %Z;bG阣j @ט>`1* ط ;Y1nhvt`$@$`Acq4gfHHH -@ TEE)E\'k!˱2(VhY /J5zDŽ;~e-@(weF =]"f퍮6'"҆bc`9S1LVhL9 p^}+eO]g P/,eh *HHH,4OP"ޞvzoNX|C<X(Ϻ0rwafVJ9 -@u#"\W{Ct˼Ǟk1K~K|'a%2+氾M]hͨ Q|{UU : k#n H6^ͱKYΔ-@Ϣ٩ <6_OH>t^SBxP$:Й1;cEȗBԠoHHH@'@Ѽ c>J%@K<Ô(-@"4t,;^/~YT24Oе*$@$@s epUI+j %g3"X.!(-5m3Q}[7~!j\q -|1ƧFMy85ny)+Έmp<Ԑl6/@4~4AztnњY 0+c5$^,ԓ`7=~u܄{ԐW áBML&-[GưZUVTnE1  /"5- ^L媶ǃV[FS2ox8m`I<~w ,>W:އ;ðZa<񐞤O(%'U]~sk[ы?CT͟/}ރE^tz8 {9û#CgRt.c1BP懳ޭV%7&adSE qW 7W8mGPO0qމkIM ?BAƦ 2Mf^#*y=ڏ.fplt'TMp5n1܎OVs֑cb@ '<<" %yY|5 %e쒟_?xm)6mBa1; m}dH,&swlޕ_f1Ozl;)anZ(1ɜx~\Z6?4<ň$@$@$@~ 14WcG%n[hO ι%Lshڮg3o|pՁIHH #.:W}.qe*WiyS[|qЋc׌C xe}*.2$Ky:9Ōk횸MB X';u3;/7Z_"2vg0(øl C<٢]*4T8Kjo\lye˼e HÿLA$@$@$@P:j5h[#z,Ls9V9qDNCU! ց?y YȻbCi<70b]hPrWgG~@5Q1dO8wQg^`e8RIɤ;EB/[8 W^]\W4 @{6u'ccxBn@[G:TR[|O%MVYZmSe_zu"TBX{y%G7{J-=2ByR)y/1ujQNYI`P3@zR>-p.e4Rj Ql+䫔X @35׹lde%-T*y]Z3V|7i^um qĪbkq"!J]?,T<{)D >P yÚNHĈY"UpB_#Le  PdupOz־*1l{9oz G76^-Ml D^RjZ6<X)-~@%  J@>S_E#q,)*.b)rƱ!*TIURɘszMؓby\s{o)~5Td;{>v^)rl<\r<,gvT~*²IɫwMJ(EH-wz P=\IHHL! 1&shƷ/pqLϳNqh]cuO>Ēe?SŃj 5 1C۱‰ /Gv_r.K пZERjKԭ)/ ԛbI'=K͜a{:,+OQ'\tBx/#kh^T k͚/GIEV{Sʩ~| =k1#Ÿ5[T(y !Ϧ,S{I1^hDFFCl=E ~y3P-WD.[7CUҲI>~A~ HHH֍q;P+;\FU*rT(Dr!,bcPSxy.K8d_CGviGq2 0/#M9AO+,>O^๳,cJ=.1v1e/Y oyb-`0"bP<l"vʐD2R'33:Ѵ6X&VF5]oiFX{uJ1eE3ǏW{%v{X5 rNy >Ubnj+KnV9_lqym ܶz(+ @I/H"[Zgr/KV((Z>>GdT{MEԝ䫩!~Ɠ]xU҉IȀTˣH+]g^qmF,H˖ c5~GV:oEb,   PW8`zܭ}T-ućUC3p 4X#{e ŜKpM v T cg*žpu>;Υw$?K8 IjjcMᖷҗˌ9]SYhmJ늍:<1hF>M j0TH#dd, 7ip:e{z5zEL+;LH$@$@$Amh/<홳[D]jkr[`w t!-Aʼn>5ߊ^2kD $Pk9t;3}/L)HHm6Tb b;ЀQt:8D)U.QTfsYncIۄCDFs z(:34˳x8$^231N|, 艠[V1x`YֆN_FN4ÉQ3JEq4 p; _k~6NKWw˾s<[ȑ3C   8z"k*\'o: fyE_3nP"P@SaK7qhЁ\ G&5$   &J尢Wj/^] Wp1h6ueJa; ^kO$@$@$@$й-5,6eKH?g/̇% rq4WWKaȤyF<, țl<&T ZqAdњkм1;E(J\SK9:-Kq}>?ŞjGs}, 0 R +0 3xsrhTm#1;KJ0$ٷQBgY:fk|7,|ܛpÖ$u{"X*HHHB -Uh6O>q5N`%p TZ,fY WSZfycI˙f:W٢]@d.5^eZi,d3ACCDT,e @Z=EX֖u8ԍshKIrPs)֋'2kS"í\J_MNn<ȣ @-/u`K8G޲\&/z,WG$@$@$@"ݰg WJtb[.3BPX@3}SeW:1tfUM J^.\E΁"fDP`KAqRdq<9ͽ27XtRyHHHMyBxHmk7`9 Y߇Vs؟JR9AyIJguBӜA%0(?Ryx$@$@$@$E>E.}o܈1 E!bSA6~eb?aiУ[b`pa]B{-|1ƧF?őۊQS؟bR)7mbI.[jpS!f>+_W`,rGR|~Vɽ P&uCzGUWYFW.N`WW)f$bWw񲣪4~6tʤF9%yw*$U673=Y1ʾ0X*j_kLcArx>IJ£K%sQ QoDOo&DR;)s/gݾH*E mwۄ(GU)S1$حfQJ!f&,R75*z * \*m"ZBRBƠi$ RcT|l_jD̖n ^ƫ#R5hT*PN]81#8:N>E+ ᤔӢ G,Wʉmrh}Yb-Ũ0􇶃Ru/38:?N쵸/VE[.x^&,/ /V+"+Qu  OVy=;/ax'*'բ8_|q c_8Ӷ6)Hw`K3]h?>Iuߙ=#ϩn Z?*-UA/1ޠ=\EsFq51wWlon9H 6&*^+_S~U}xZUHH ͚Hj KZ[_`&4ۄ8=^E>=f9a{vzjL26Jb J$ئV#ɫI7\Ԅƒmxl`&DMl+pMlҮ4.Zl OaSmB:EܖwPk_K&`CL7P=̐|J/&tXǓ뼒7`W<Ǩx9רx_:>A6!fmXC+Hi,E6!va!f)6ɰ[/ϥ]9i"K K2)#z۫&حJQDcA]5)eX OMK/kЪfNWRxCAei(ۉ y }6!Kzվer#ӘIH%IT:\f1۹ 5k#hN#21i=Z3nO=#N|gˌoyMj 0L%7ܮPVFg=H_oZ:clL؊asK3wG{7Fgr k@&\-!A 7W?ņ<=~dÙ^ 33i}|ੴkw\?6" (SQ=0"m`JjܺU , a 3GP.Pi7::GFU=2J~ר`^4|]PS-Uz&,[|0̖ i;%qaVg9BtjxpP>B0nRw R c|< kdl3>>'4(SѶqsdY*xuw0 Lm=l'|t)T_DC=t[@Z ?)C5᷇< @HxK̯&w|R,fFr0|B9r!8XDPUaNmrh(/]; jhe.:㘙׍hZY3!e\$ԇu̼g\eȼF W$z:I=AH0 )kb\%ӗ.wbcv(A2!R yҴl)h1oߔ&xkt@^ztcb|gFסL.Z$gU^ssT~kT4؊hSq1F! apeq_1~]8/Vn[qcD,zb1Ʃ`v 3~aױw<=RB87Qo{U\+x29T.$5_f=JUcCZTo|sU7[(S.h곛Ƒ)U7`4rH!wiyLLOvÊĹZP*,4R#/@=ܹU:wʻ޿+Z{ժ#NxOqgZk!2 WSRŐ j4[MJsKAiu"BXHryg3O zOi0X*ѧݞp9lonQtb:\R7?j 7A>7O?*/0bU1VX7{9P$+b>>TZYwaʷi xCd 5¤r<,gv5wy_6F;h&FaqOuy%J9 nUK\˷ŋvҶso$+9_LOnLJg/`Xf[ #*tzg4{'#>qr[ %5H^d\7܄eXzhi| *7OlV)?VW.~Y P":fTG`K%2lI0߭8*]UNn_X&WK5LK3dQs-!TZy'U'6vP8^rJȭ !V]z\L0ȩ@5*nE1ݑ]f  9Oy"BU,VK+5T%8^o!iܻ]RMpgU7nk|<>T/k-Hz&-Dj@K=N݈kUpjD&=L38 [ՠǙy'3ՀyWFu5,_C!Ҹm$vS:o+:FEne&OVhu%˳g%6>}4P(0 }$ aTv^=06_m/ RKq5==-s]C_z߈]&>$XjxSz{7:*|fsHQY0h+J~*{Ut< C%[ٶ.K:޿w&>.6-^o<‡v)VaBt(b9ر‰ /Gr::4Ne!TۄB=29L`D hrS̤d dO5 ?˵Ae;+QJV܄]cuO>Ēe!&kObeA1@fMwjT5 ֊(H>˸6b&tLCaОE3 %Cn~ AX[WI>@ fH1e]7:m"?}+82f (+ Rչ-^øwڋ/Gj׮Aᛕ_#B"Sh:k^B` >UbkWj8Qd,\72zH K!ՋΑP,yx% Q;[kpz_V0ꡏ +eH/4~SS^qbR{҉e#'1a7XGk4,/`4OM 2UBA^ !V˷C$cHH !Hĸ}B̹3IlfN|Nzp)u~a){K9kw Ư/"~V4"c۱I-8-XQ|< i7$RyHH`>8d([j Q,1q?)^tUJX\IuD=b,,# zv0(L/c88)iu ir9^uC o0+žpu>;yr+Qfe P"m7J G &]<0ZL= ZbsFQ+ ;k &|Tkς<ݏ?& X*otJ\k P1KVKQj/FOt `Q FeYCTZQ=ʜyzfl +`͟}-}e$@$@$@$@$@$0WhmvU~q:L,U2i<.{Byʶ3s!ku͙K  @>$@$@$07SY1sႥ,Xm/7B%s)\[)UsTNeHH l4OMHHH##:K &ނ ^s]#@$05xg וew8-LH$@$e4Od0œ ]N}s܅@SzF-SLQabNd3`}&.ĝnN}g$@$@st<Ԧ$@$@$=S3$1 e$OFʮuƙIHGhٻ( e F@>/CHA$@$@$0 a/2pQ{8\Q_,Is%EڳW,}R[j֚8bGQR=xHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH ( rUعP坏qGi1hԠs q47{@Cy+tPH$@$@$@$pH`6Xw'`Eщۣ"^1LV\hGS|\$@$@$@$@G=\?Y62O%PK YAO:5wi/"#j;Dh^d0A mY{hb³#c>ӸWh Z.EԉHHHH`.k$:t;uEWZ.a ?W Hd?4Z#k1b5rr?II{jpP~mC*uh$XxZ8PkQoJ>чۺ2#kr/    sWR_ N݂|օ??_YNj?Rͤnx\7 Qر %޲\&/4UNY_ډZv/ @13WoB & XI1l+84Z'p\fx }cF%r<^ƞxP戣1B^wCBԠ=l![|-oL@$@$@$@$@6`=;AUA+r;2P`r3Re],aZz?7uN+4r:16/Cj6B/~Y &1Xf'DAg!&!    hWep:!:piom @p8h M63B);د @pQ}ը5C$nENgpxX;'"(( .LKhOB& YG\DwB8ևtXhđ^LwF-57asQ/+)J£d8#cX_jH,+AKk % \)d_%Yy4 iL{#i>ȏ*o3okPzt۷tjĮA(/^ ㏏MP{Vr3xKp0K\LTUGb|*9 3됽 }ĭgf#_1KDX'pM UZj7{\$Սn؛pִ(Q3xYXʍC3s2auC\2Uull_F7n'x/Kٸ[;'zq[;5ۄ O]i6!mkPK,ޱ0m(ۄD3Rh"qcNŊ,0_@fD$@$@$@$@I$ڱ5?`m1vatX˧YL260e9JZ9 t; M8(:'SweZyZw,FrOh&)l:rcx~'$k1V>3rq7Ǟ)C/U;wpϩigv fC82G}ՅM R*G/ea!!Se[ r(і(ԸBR-r: G$ GqV^e5^Tk|HHHHH Hr8Z*; Q"G DNؙ3T4qANt nKcĊE>mgGY*bTC%-2]߱L=ʎOsj=U6ytj؟kRbĜmn7sIGECv|ƭfn>'mvyxxPIz9c ~V҉տ½]!gjx2UTgh"8L0Eנ.9=D.R_U` ]aۢV$$O/F\Iv0gJ]Z۸mM}1KEZs_$[y5<蝡L U 4kǸp֠*ٔ.'Tv9YTHHHH`ȠևM>i4F*]+c^, ITh?ζl'ӘUsqL46lo9GDOR?]JW1gǭ:9S1 @R':c36NzJt!/Jӯ?.;V.+޴E˽ط!~Ϝ;|oV'zd6@_Y$+P.n^7.5p{}GܨOf8ɕQUwpWwnW˛:1Vw|!oK$wƋe?қL%wmVWc)  @HY}y!dơKǧc_,5$Cvgsrڊ`Q]iY}J66E^:yU{i^.UskFͧ{Ig=NlN( d',hȯ㏧{v\) ;՞)O]{}mcWǻXy3\(9YZu\șz @%f Oou/RA2]clj)z*~#iwXIo㞮8X޷:ſ_f?̽s!?"+{+:ogq'6~yҒ]Ve.uu^dƤ[2 sO+(hc>I ڛUsUPɖgQކ`U3+, Û-F.Ɏ-osEǕg @@+ -k#`"@232{;g޶6,?7=0|o|4`cdc@#qGr`BgG{<%z'"igZ.'2c"stЪ#. @(W`Ŷ7*M$M׳T#-/vZb.q&\>s:v )K~YPǹ7ǣ?;-VGΕї&^Nb˲Wq<9EAKU>"ܽ&_&) 3~(~tnMJe)KGОNNB0- ɻVkD2E?ܿC'⩼xsBFaHJQpJ-7rȼ)Y8tfrt~JDhiL$ىsTA5|#m\tB @jŻ.gO_oRS&^Lʐ5w3s3Ln+|:z_/}{/0*#Ư>w,n=o%5TdDž2=v$?Wm(=r53uJEEgپ$Yz'{t3ΤLmr;1m5m. @h !Oy|Y B%0oG̦_+W k,+{c'BㅭTx_Ь3Dy3vzs=Į_T;jS @@^,#Ϣ{{c`{EX/]ieK4P|xxUGӅ$zhn4#ΎH[vb ؕ×+Mĺ܆It^Sݸʯ_N @*`D޹w7G_kxe0~:[*q@Ξ(sSA*ӳF:`n @\͚ضmc`&oH~'_DRL[dVYbHA[CY @, :H\eUW @ &<ݕaz|nk_ @ @%o9vuw#uWoK @4@{U&Լ;Vš8XUM  @ @ ^ۗE&zR2 @ @ T1{"bx]R  @ @UTE @ @%'JH&@ @% % gիT[3 @ @cn}ruUUB @ @TjxkE%փLP]kq*A @j'PUx#mIuOq߲i3op  @T'PExjknLx)):~3ę\ @ @ ^ZKF-x{ק{[^ݿ( @ Tq_6wL @XU'WCr};oDl @ @KN9E$@ @# <1rwOnSoA$@ @-%P֘-59Lg|3S푷cM[8y)8WUF @G@xz{њվmm^/U8Bx" @Y@xw#>n8:jQuq˒u NNqN @S@xm6^h|T{K<;&UMqJ @M+`k޺x(u7Tv52MH5| @ @@ON>zh—OԦeiI`#zZmjV  @h+jw+>2SW@ԉ {oSH/$@ @* {jթ @,8{xu.#jW @ @Bݝ~C0ubvJ @ @BHZ/vܟNN̖Sy @X6-3_<=%@ @ Z@xb]CC,*R @,hjo2ut7T[r @ @@OTNw;#<1Rmϕ#@ @ &`imS?]':2."e;CKKCMb @ @`@{riN sN8r>> @ @@5U=QM3ZfwOnSo6(;7{7cy숦  @ @';#oirTBE+Onk(a$ @螸:_9Z:+v2Z:+ @ 'J=t׉T6 @ @l'J&&l_ŷJdL @@@x(bG<:qb*I" @ P[ccy}NLb9MkE1<W845 @ PD@xJ|re& :QhRMWEpO<ָsx#&UOn+/MrN @c }7S'?岄)_)-Mei="iOϾ D\ψM8#@ @@+ OL$_'.N,P ܿsG:u")/8v Ɣ$PtQlUɖorb @Dzw}ԉoԉOvG_zr>7<ӵ^`W.ydg%ײ# @/ |8~.;ub0vVUI7e+IGFतj NM[T)9$ @ @@ =1~m[gnz9__6)+2G+GK;PwƋ?RUҒ  @ @%'o2wsh(vLwտٵ(6O OXeGڌe @ @#":q[:ugZe WH[޾r}1#^-ɖgJvw6$@ @؝:1r9vOT?7%<۫k禸si&CdJ/F#ׇ3 @ @@ ,Ğt׉xԉ}o}}%zc`ku @ @X;Ň^#y  @R`']##e l @,Xۗfn"a<J @ * OMuAn @ @#P֘)ϒ*^xGX'cȲL/3u">8&@ @"P쉫qZ H[ĕEkus:><2OTO @,PExbqt-d6s Le#3sZ|GqpZR3 @ P@ቈrk/7ߡW3oE@ @Ssѣej舯vE X3Qh/  @ @ 4Lxx%@sEg|,:q|?K} @ @fhDco 3h+ah(v!s\W{}Rw/6OJrJ @Qg [@L-:3x?ؒl9p&[wg|oC"R  @ @@4Ex"S10($t׉˱U;7ŝK3CwLΒ . @ @fhD\?xub}tß̮ɖюGƏÙ>ؖfI @PYӹ +6bOp<S':pޖ_?GB̖O @ |s -M/HNh/ltˉCg&$ H{ IDAT @ 4Qx"b( ?6q_K'*w. j`0n=0Ǣ? EM^F @ @\7ec %(t ɼ4WI'f}u< @ @Fh ؝Sԉ+qOޞ @ @@'L|r}P33> @ @`4]x"ʑ=ij"c$3ub;tL @ T3=}UܱpD/ճT^  @ @f)<yo<;N1z*; @ @M, FQRtKV @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @Ã~rfIENDB`sympy-0.7.4.1/doc/src/pics/winpdb2.png0000644000175000017500000026714312253362407017626 0ustar georgeskgeorgeskPNG  IHDRN~sRGBbKGD pHYs B(xtIME 0;k4iTXtCommentCreated with GIMPd.e IDATxw|nzOHB(!:DBU\ +U+WPPAPBH$@脄{GȒ3ˡڥyUgғbNnE;zcJǾh/&Tm ! cׁ#&h4\Nc~( |@_}AӉ3<8* /g΀w盏зKG ᗽDn{#Sqs݊ƽf?m @***HKIbSH*`\gʵAcPW3r03{B]|]07%+#/vqutehB>ibRiԗ{ 5Ɯ6)tݱW62vJWIW< TF^y x)oL`hadY{HRеsR(** NxutQ1MLܯ&&&W8SZVF0j!OM"vDH!X cC.ټD QG{;;y`iiaPYGOBR2%XY[ENn>R)-+ڊ ?l5V 'SoiY:7j:9s:# r#.1S»xj׽ǒPTtp t*ڳ>#Tpu |R2%X[Y9G;P]K$%_NeTl6bH:g_-eǿއZŝC;3eTw<Y`UyV/"?u/h@qjz7&c[uJWIרBS9h*. 5![+3כGhkO{' !tJAqiӪP^?3&]Cӕ΁QVyys4*)x*)*B2k[ȼӒd4J:!Q rpc(ehMYyəĞIG~v$\ʡNKdR6a=4u% L;*/~.%h*4HT nՠwڿP ܘYpt4*6ck-:BwW>!lճk|EG>z@?:yzx }jK,΀G:8sX2zx3E''}}9::PlEh;$Ppv/{ǭtqY9?_eC*==GkrpoRnbbBlr6(O&(3"*$LMԜKɦ\>*S+<Rf?q/DsX퇣Ԯs~u)#JEjVw`Γs8h\aQnM鏅 )|%/(i!D|Wc&f=B ;]W ej{4z,D?rt 6MB/7WΦ TTTވ٧W}zӟ:޸JJK135ͅ?_|;J~Q)f&lz%G{;E^a)}B}YF}G/8iuX}CB..θ8W.n>ѣ ʜ_Z]_Ra'xL'+&נ:dg2GǓA-W~VWyUW X%%^Ta _T7V΁:ˤR]gYkױ9kurrԙgόٗV_^ײoggd$У ΥF?<53OֽH7J:N{Ⱥ; IX%_bB N^[{CB4'`\O`x]y4ۿ? ov,2xƖbÊ݅7t.&!0LqYo̶M2G[')~oLqYҭ1 "復39)-Dq݋#G狏?IiYt̵Remj8!3SQˏ; k^ҍm_8!C!B 3F-[RVNfNAHWŁm7R=!B!D{ӭʴ]:?KZB!B.ӆ7QrI!BUi'ZB"B! B!BQrB!TqB!B=d$B!zȥzB!BqB!Bk9prshkuu#!BI$B!D[Ө{=Ͳ^cϞн[7fϙ͝wl벹Ҕu=077N#YZ}ٵ/Wk抿FB!D[3N/x]KUe= £=&k={&sM7=s^[O?ڷo_}vv6L͛74!B8O( ^}uݺ ??YVT0oFV޽Ѡ$$$C/?:=C޽Or%C{SOKJJ7A OSRR3|ۇQ7&.h=_WufwK_EQر͟ϑчC?V}ԤY_1Й;׭;˵#'''yŕ3p`Wg>Fy/z?UQ,m Ly##u2~払j'Gϲe !DjgWm.|j ryfN4y#KL}e ˿`:&:i6V鉣:CF'\cNqBq\*]zߝaۦtt醺TmN{!Y聓҄ס#>¡bLkWʫ55W' |6P f{;qg3nhQkqׅbM#NNNu~cIVDEEG>@…ƕU?ˏDؚ;Ϩ{vjX#?[%(IڝsRY/իzpEy6>O|FTÂX(\1]d{YrK*ӕÃj{=ui6m*,Y]'LM+7{ EͥK=ZFub |qP.S5)ػm jU>6V1/ξU/spt3-';ӿ򷃣vD.!o*`8qR:u޴ze׊cǬekifeA_sk a <GNwט<5wn˿Oó#;+j,_T~)a={Nٷo_5}Ɛ7qb{ѷg#L[祥< {_#s|/o}xr\QTTČ3i-_nP:wSps`]wfк^v_KZٙ &/?( cX&11.4m ._O}婾n*[CU_L;]%'87Uy+*xyɶ\=vQ~2{X6~#ۏX6f/Wm7dҰ"kjԿ;vE( gqsP9^Ӹ k#QWU>}k=|~w}w-~);+So?>rS'bɒ5=uIVfw'i?pݻwŝwQc>}l9]ٹkgy/$?w]wmظ'.$g„̝ /޽m!9v>u1Gs19Y˯¥KDGEu III꫍}CxW%6{aU{o'fS:y`.\ؤZ;)DFR\\A9 7D?̘1{{& } 4 -[}Ր4!} eHĤuճ;{;z f+. %?/e,X:O=1gNٛ~+qw6X.6l|s|붿xt t7uu_s}>''-[8..y<38:V>nQ̜9ﺋ3grqDʪImC߲}4$ͪul ׆bblV}ks6!QI|uq*0T;3Ri"eiL58V[IgۛN_~Ck3$ϡõˏ1E]KИT1U+5dR?kꝀBQ'LPAP*u36_rP4 *K 7lQWcݏ\IHQs7q-8wrIjvRz BD~5Rjbty˹瞻yYv-qqG/T/T:u}ɨ՞foo}offsZyyym-kkggWo/_W'HKKW'ʑ}ǎρH^KRXX[?WkmllX.|ӧyxTPW-cΜ7m[VW`H܊wl 45z&?\\;}dj 9Ŧ]k|::P㤏;1GHOKվR/7KL6'f1777:777.\}_LMM)**Ҿ̒IHHkx&$$ҨJRR}bbQ?c<أƐzsgz;h fٲWٷec܊/ F2ᇛm4v7W۫g=nvړsп?&N/OnMo>s}?s8:8гWo A[w67@ v;Cw^c]˧깸yZ ̰ AN󸺺7}#?ר+s= a={1hcfffpܗ/E /¨u^ ?2p`:vbmSPXȧ|jsQmoY]닧1iZ6]뵡}מG2ڜM^jx!?XGb|zWϰ5٣{>t" 5WU<Gf돫 s2D[%nO[¦pw5xK?nnem=]ϘDҾKL*a.^䭷<0i~>}5 FmFV׹6XԔB;ll).%0؛oۨ:9u\.\q7Y ;u4wQ#e7у= 4i4,hg<އC4QktiASS|~kȀ>D[F եi|oӃ!Z=-#3t`_BvN.~9L=yj IKǡ9dʕؑIHP(,|zɓ9USI_ {Dk[riz6"vAs/M?`%%%V}6'|ǨT*ikўNm'411ჷw,v-E.AZk yyxƳlC?r$^^B!DמMӹKV把 JΖ B8 !Dc)BaA !Bә3$*B!BvkІNgB!BN~D!B!d$B!2pB!BzV%! yB!c9$B!zI!B!d$B!2pB!B8 !h6X[H $.B%Dsm݆(!ڜK.1eʽckkGǎ0``#޽{KRN7jPS??X'hs|?3}l ~~~m~믌?^-:ujq.,,@v8} n-Zٳg[[;5k6yyyuXz5ݺuщ~lٲ%K# dEaŊΝCx8 k.+.7N_ 8::҅+W]?ԁ`V^mTyׇ;wЧO,,,ݻ7}my4 [t{{BBFc=G}D!ضx[ /",'sFշ%s]6L ޣK888_S'n +C_>ԩN^^vr|}ĉ}:iNk}}8m_W_WڞҜGIU>ҒѨ~PDtti,]իW`:EG|=zI&ɑ#Goaڴ/{A?ŋY=Xf5II<3|u_غu+gϞO><3Zʨz|̜9N:qD899яӧzjRRRj[od&O˗8q"K.Sc>JePG#::S~i>>>u.ٻw{/xx RrSѻwoΞ=֭[ ^Wj}Tppwy'|woNZZz+!!o}>couҷ-khqKm(;\۳~4Gmj3+?$3#] V5FUƍXlO=5gTTT`gg3jwB...pssC`kkJn]v#!!ӳ#~~~;V١SZZƎ֭ua8v2sѽ{(\nݺsyb !B4/}]_?fffƊ+x Bbb"Νݝ!>111!//WgZ}bTh`ʔ)5p):v퇫ַs]6L խ{PΝ;s_`Ⱥ2d :&BCC9p`pڵl޼Ç5:ކ ɿI߶hL;vZ;1 Mi֞\۳c85GmbR"|q㟪7n8~!^z%O|Ϟ=} bgg@fffy]\\'U*O+Xŋ# kk<=;^l/hyQ <W ʎۻ߉F#)) ___6BÈ9p`?'OVDYY/v3P5FY`{|ֱcp>טDƔU+C>*<Ğ={Xf Yd\9xyyi:[XX@~kg͚ERR";wҥKGVDԐzT'!!B\_xGʝn{VHKK7U33j>eÆ2pqO 4-՟*wsId1dQU;}'lܸB̙l[߾O_NŦhbҔ=JXXÆe222Hz̙3~Tx*p-?Jiir#GjyXbż ;h6oLAAeh>eƍqo eH}[?U*̙3CU~}QFѳgO֯UVwl\߾O_NŦhbҔCھ}YsεJ 6x!'ߟgyիWӥKcƌo߾5i׏ >CΝGhb<<<1 0?_S'oQF@TT3g43g? ))ΝCG.!FWq}eҥ+:83wSjGy.\n:/^O|hL.!ןq0}Jtt4L`# oK4k,-[FTT9o#+!QRRBdd$3fܼڹ}_C:[ISO 9>zqpp{ϩgCèC%f!5mՒ%KXc/ԸwD}ooJKK9u/vuۏhM~8Bƍ7F-wS˯( +L:_$m=W\L%B:tH}kn$%%ѿ/^n(Z_!kJN_ !^Uv͵DmNr~b՞ڏ\'B!2pB!B{^Qa!'Rqg< )Ym&&jxSxJK6] ,]H'yHKMmp^Z }k)>!5?<7Vj4RR3la*J -M!ZPtt4}@!h1)ݻѷ܊/&&&( P^V^ASQsسgxvش7[ճ+@y }zqI8 !D ABef4 h44 {w77زe3 SÞ6\Kk{Rrػk'99Aj 'O7W=$ BB~sFQfccg@nNJTNȤfb6D8WXXu;A1>-"==UV(J[RR¼yJwż+ZZT>F-߽{(zSybbdfW-#n<.9pquCnh4}0FC~Mݛ+na={YFe3{xq?BRqp}/ͭ' bu7,ZannNiiGRȁccmK[k̳/B{V3NU՗<>c4/&%?dMo8< 嶛ҒnC9ս9($ 0=3bgl<䌓~eeeh4[&N4 8Ҧ0Q.ѱ@jDre) 8tkvNHo_τ{v6k=h*))aŤ`mmMXf )))zO?eݔbjj;#K+z-Ν;Gbb"FbܹbccYr%V"--^z":u*+W`ƌe,X@PPPޙUUs7 TT*S+DSK%w\pACM\Rc0-R3Ӭ\\Q5v?#7{Ya>s{ϙ3sޙsμy'OOO6l3gd$&&2sL{:/e)O;aiӦ X[[~z)Ç$##sss ;;///RRRgСJ_'@>|'hjj[o1n8&OLPPbÆ XZZ#11Jd7iii燎R/Oiii,_'O 533 _{-U>W\a|,]%K}vv*ݾʓ_e_’77e2&͚QmJTww"""8G`aC"P;ף}uc{na j 5T(.%2l53Ȥ`\.'"ď<:0bd*jPmq1cVVV|g\~;;;?d26nȘ1c ܹė|zJd2FA`` 塮.U,L߿?7oޤcǎܿ_?GIdcii>XZZJSd2AAA̘1___;v@GGG?Ϝ9Í7prrJjsKQQD VbHZ2eɜqT+ ?{&%ammt:==]]]Q YYY1~xN>MΝK|wd2222J[݊SI_|)/_4=)))s=͹|c4+_GG+ԯtP:-k[)B,N_y/OZZZI222neQ|8q"6lˋKQhlRE~%ߪ|~Wqƹٷw/Fܾs!Cն}T7u5TϚП{۞~r?cSp1 ûp5zF30b(w~/Uʯu萛lLC*\~;Dٙ]v?}TOLV㲒_|===4U}'uuuvʩS~:TKUeq)*2Yۥ;Էo_/)&DEFҫ׻*c'wPC(Nb9}v3sJIiv)ms]]]NjȺu}\ݻU:?III$%%& []]]RSS+TF===/wU"W۸Q9˧*YOXUeO8777~WXnddDrr2jjj0vX~W8gggˆ#N_|mll8y±*k?Ņ-[i&FUSa?hSrr~j> 8Pa%X맲r!!ttt̨phUOڥt܃v={Aaw,x,f疯9{圗DŵV/zի cccqttTtԉtƍǼye˖DÆ iٲ%SL!((---"##9vAP2|plll9ɝ;2Dr{{{`ѢEX:-cǎ)?sذaիWRƏ/ cƌ!;;###cxxx₶6},00\GeÓ'OhӦ رKݻwٳgB*Z?PcٸH6' &zj"""dѣG_G[[͛K T}%?eڿ2/ 5MM \u~~~bii)QA}9>%\qR/DHyXWũIS;o\Ցe8ɰ&<'ssItaJ [J\VXAǎETyPj0+N2ˑ+Wƫrũ˪*N_hڬ97_w@PèɋߖLOOBd2lß{44xZ[$&&Ҹqcx m& Cv),YD4*/FqҐG((J,Ovɐ!K@rrr7>[\fi3˛'~"''ݻ!t=z|r54ho/\]]˿F%}j]sqn߾>ڷg٣]W;7kQzJ]beqZ@R-Ne\%OmQFRDL۶m*ĺA}ڶkJϞ9]"ɐ(#…HNNŋ\p.Z,/b).]S|IIR'C2unf|<-7{N7yqm9™_ϖ[neݑ߾}OԔ-#2 yS%Lpb[] (TeT@ PZqP\NER" s,-- '**J ߺu++n!_~\ ]] -tY<_]]]nNT7n-ȃVq0ܧNaC122ݞ=f;󙙙|6w;N3w> qCևfx]{|FVV?0~mwU;w~~lՆ=e{dd*N|Q;v* ዥx.t|6~#? NҦm{MǏf[*f <4kޒf[*QV͚dѽ维lՆ ҥKBq@ ժ8#)%%[Ɩi />} $--|lBtږR⤜p 4P%%cEJRᇣGanݺEU q={ct4w%hj)|D7\G/8}$M4ዥKs7ݹpwn̹s4o;w*c̟7\ΪkUŏ?,]:bwrIu/@)\R \r?s ?~>y I I ^q*TMҒ)߼_8G`~ܼ_b#G6qWţ|+Z<_HdvO@`[]`Uy۽{022R8ּe+i{z ox{yahh!}񚍡x{y~)|ߞݼ먫t%_Uϛ냩)9K@Pl `]<.;1ӓ$sHahhͿo2?y=:z4qqF.'77W)I09\Nlbc.G45Y[s7-M!pk놤ܹ:vƛļVSP|Џ<^Zt{njjʀ ݰ'O155RSSiTnwG~_mGr=SRUM^^8 u+森DŸNm.| 8[7NtƪAcN;PkQ,N-5ɓ\]؆,YҋjԹ,YR/JLIa ~jƥ7vNzjjH 9rou*M_v7ڱϓ&TE.30*2>|mׁ:P~\J;gglђ.]ߢͫhԨ689s|j@ҥBwpp,06lPn |b}J(('$&`ffhxRRffҾ,O;}cs9sֶW_mMU<#s}`J)Ļr valQuA %Ɯ?/SZ>%K5WL&Atև~`.Us97Z}pđZ+`q25eƌ.'&>$==>WicQdaFuv~@Yv..X[cC.ܻvf /iՄmйsյ z8ʼxy JJݑ?zo)!rtuYU0MM rySŰA! RxA|1C><ʺFe>NM< 0ԯ_,Ν?_ɱ}K?,*l~B/X~O BKKMM-XzMy Ҹ1|9yJ]Jgs[_ uuu ׯcggbLƍ3f >|/3^^- 2#FyyyZ*FRN\VyM==ey> U+e*+M/ʣ.o dV&]al҅O>傏^X(]ڱCUR!d|S)Y6n(_%G8#WJJ9o+C\k<^0|l!,#{waar%MsLwv\p`ccɓ޺ sW CkkԵIQ8N.{j_IJJ")) 777Ǝ}6+66wwwtuuIg4===ݻ'߽{" Djl,<C/Xm˧{0>m{215.)/vph4:U( jdyu &_?~/-akұs.;>(;[ "x9(wӾ{"EdHĝP9NE"8>.sh4߸YtʙRdiSE_UwD 5`y>b\lb@P9NUɫNN蚙q!"BNsY[M蚙ѦhuD 5 en΋ svETPbV|zzăL&c˖- >qh'E_ռE Qqʃv@ ^:N F!FF4os K!ȋ8 @ ]q204kG-A xdNҾEtpuBhP=@ xppg8-tݹHFMp?Vuq\<{{RХK#~eRb$'')aaa$''P7rI (("'!!޽{3}t),&&`BBBHMMeѢEddd`hh+Ii<==S(m"˙*733ooo TSё>|H222077' mmm)<88BBB7ŗd4kFҸz/6Hm,s%-I/2n_s/n؇1Ss+&:v'd %,uS8ƦrlG-Qqr%].ͷiSwQ3fʊ> ׯcggt_&qFƌ)pÇٹsgzhd1@,,,C]]] +<'M"?/׫$_www4h˖-۬ZJ;((3frR塬.EE5lXaC##i=th2Ysd #_ce7mM--*3?ppaI _/,N@Pݼmw{s+AREktnNj*<_6ũM ڴ`%liDA󇇇/7k֌D$wvvfԩ~\\ǏtܙRϳ~zңbv 7""B'N8=L<{{R'Uh=t( *H_ihz.+7 N'@ fd2&fS֯\:nYߚěx4rsbUs,B#ÇٵkW):>G!' Y] (4d  @7jT{)ի cccqttTtԉtƍǼye˖DÆ iٲ%SL!((---"##9v́@ 2|pll m^wqyy*800ٳg8';;3gJƌCvv6FFFXJWMSBTR1s N624cZ+reA8ESSCjx@,*.yPz-~ :wFLy?~D[!:CTP===}X /j9_&9[ZZ@\\@PbӋT !ټy35VvR㾾j%FSesLLL }?U/k׮ҭ[w\]jAYuz/Z(m{ ZZZR}֭uVN1m4 Q`\#GTJ' @J+MZ8Zuq_ƉgȐXZZabb&(Jm066ԩSDDl]ӥKWbcc4yyy| ff9G߇(c 4mڌϊAbnnYE& 9!3gP8'䭧Ϛ5kh޼aq鑝-ućh)Mћ{#wb-rw^rKqgܸq̚p<##y\2n8Lʍ7%66#G-!EPJ<{wݻxyy8LYIy뭷ܮʒv9#'z3jzЕ ߃M3{Ξ~|2XZԷ'Orpu݃cBCdI/zq猍{n!YM{Rz|±Ugs?1Og餥?bͯ7&Mb !gk_G =iҤbi֭o989 aÆ̘1;vT*OY_?i޼9sxVS \,쭷ɓ'|`ϟ/cƌf;vR*Kނځ:=_Ԥyk!Hqc/8M^M=zoqLMu1K቉IO߿uѠQsxHVS\̥y߳哙0r*w.LYnE- |}Cpp0!!!h"222044UIi<==S(m"˙*_ff&lSSSё>|H222077' mgLLL !!!JOWd2L5CMCCoy߽dX"h̕$r<%/_N~~~ᯜ\Ŏ(zO~~>|W={55Qey?T=BէDz' "ď>Ssbq&:v'dj_x{߿I6h3i<|EPi|ӚÇ]C2TΞMb%_Ʈ]񉖆5mjB6ud2O¹!7oޤcǎܿ3f`eeg}ױS/ظq#cƌ 44Çs;+ 1bXXX'yΒdI1`zݝ ,qmVZ%Č3҃XrePVvVp:To! e2\\$[8+-aԂ|Eд>%W= !޺Az$uMΜa/wn%8i˖$'?fiРŬYGCv巉6m,hƢԡ|-[1x=>>ѸvP9Pokk˃p-AѬY3U:ٙSuc>}Ν;+x/ׯ,=p &""B'N8=L<{{R'Uh=t( *LoihzA7+7 5uڑAݠ<놰~Ԭ.d2&fS#)!!{o*v=Mnnn1Op*!-ZH-,,:Cѣ55cvdݺl_|ыIޠ];+nzDXy{waar`>MNN>[ĔpLwQ~z T58::n:i_.{nORRIII),;^'POO{Iw%;;[!Ρin^}?$$#F( 舩"Ը8\\l8Pԯ?*(_{_*!,N@PMkgz!';?iKhشɣ.[}ז]Yؼ"r9ӭ+oƍ')pO?kVWѣ_b8%'8p/gQ6y{xtww'(( -*h+VÃիW兓hkk訒\]]ԩ7yIa˖-ɉ ҲeKLBPPZZZDFFr1:(8  e266.T@fϞ PߟlfΜ)3f bŊ*i?jʤ"ԥ-3-]0:/*JPSChjsAp .yPDBҨsgt̔ǏyHpV9KNu:C@\?t])*k yFFFžfr:رcZ]R&/zzzann.d\zUZڵkSZנORw}$T,ʒǥ^J-JGƍ\W"9V,WU;oU5ME|W_}Ezر#ɷnq)wY,O/|H222077' mgҐ߸QNfP(;INNFOOSSS”Vw^6l؀%hhho>nylڴSNzxyy>CUOKKc9ǿFff.[*w!Jc4hЀUVl2}8rʕ)S!!!JgQÆ642Q2J+++ ׯ|r0bhذ`btt4?Gȑ#Kn%yYF!|͚5x5 ͍Yf鉕 -67oޤcǎܿm۲n:BBBJXVK߲eK,YD4k֬Ҕ w.^rÃt4h.@PI hNŋ91uT 7~_iߙ?έLͭ/qkY˺lqڲ"ɏټc64^=-f:JLڵ*:СϿ3thT>_W"""'N\dɓWP*JCYPQ o֬"@N-kU:3,,˗9O6mtR.\FGGGԿ鿭- _UVq*LJRDj tڕSNaee-% T /;˼8⻾fs={O? 111Q8fllL~j|$'%_Ugkx>q;™94p`{˺lq;dۑuaG|E/&Mzvuaa ;qʕ4maݺPSamm@J(M㻩SUgggaZ!!!1B!NXX?44TU bj\S._.+Qrݻ nddDrr2jjj0vX~b?rQO?7J 666>EGGݓ޽KvR]\\زe 6mbԨQ%ummR %56 ]] TPhIT!Ccay5{';;܂|'z<[D;lR:vwuN;Jyeӻڲk#K7_D.[[cu{##¸q>>YP6]4b>'_Ǐ#@fϞ PߟlfΜ)3f bŊ*&f+*W '''ѱ^;vRܧ߆ &-naaApp,ܽ{={vmou%xxx₶6}Ի;L0իWQ8x^EϚ+V!)ÇFTJmmm7oNzz4|ws'֝;+]ڹn݄IPsת.+8~P^"I(Q'd,|ʲ/5ǧ"e-LuV ԔpdggK^8AWWW,--Ks!|޽{...TAe8 >6oތF-#==Ǐ4䄮""c7oF̌6ejUI <+Vӓnݺ… qrr"::'''y&L,B?Z' KEuw}>7o%Ю];i???%K_]K'Ncl7V|ƢV=zݝ={T<<<Ą@d2B__1cƐUd7裏000@GG?PkL&#((ƍ&}dR:īlذA!N]Guѽ{wmۦpaÆ?z(~!ip444ppp:3i$[s}7L4饔u|з/ `РAJo FX'<~A .t+KizaFhի'kcب*N?k֬)P/;wxP8q111s>s)l̘1ܹC˖-ɐO9s G\Kumȑ#Yp!=ĉ>}ZC]GU2w\-ZwѢEL>###ӵkV",,Lؼ}vYzABX@0@WC] 41X_ zi=-L 13VO?UhަMQW Ӯ\R7rJzŗ_,+eҥ*_bu꒜Ljj*dqT}R^=mùz*GsyfΜÇ%whiiqA֭[ȑ#ٶmtڕsα}v~4 j /ګ@ VHqLӦM+T&EVnҤ oߖO:7Νɓ'@/ 6 iΝ,^___Xr% wX1w\qttdxyy)m}5jQQQh@MMMqwwʊ#Fh"!qI Mo'mM--ՕNwrTh^eB4hЀ7nTH7o*t ԩSIJJ"??W 9+N:o>RSSYfl@P5|Ghi?{WedBBi6,K+ 1QDgHs 1pTΔ܉9RC dqe]|{<9̘13gPmۆv7,ҥKlܸݻw͛7EeTB̦KZ-fwTᵉƨcG_dydx W 7ʳ-DGGcS֮]˨QHMMRŤ߰ ;x`t>;J6i|P@ )44\[ϝ;:ХKZjE-~gϞt SSS7n, +:::|ᇼ%Fn.rCeᡰl]GeJ۶mU~ԩbrԩܹs;Jd2Mo„ 8;;$*Ҧk}mGbD*^ۨ޽_eخBq@P(^Tի>>>~wwB$O(|zӬY5kV Bxl޼y桦ؼ̙ TZi<}Ys54#ӿ[ --~㯿bK= C T;Ƀ:^bjjʶmPQQ|p ]zp^ݥQRu?&ep6~}uN=BXX;,]_ܹSS}F4w`vh@ ܉ΟRA2 xG@k Uq{)}l*vC SAOOxy®]Wر#g@ '@ ޥa#C5r׸Dݼq kUqׯ ˗/ҽ jj*%Νw/#GnIsy昙5`tZbb.OU*4 yNVD $UU5GFF:Ydfd:_FUz>ДݻGhlr9{-$8_~,?sfSР7[o5#8Rs;4X$p:~xw@CcF:9Qջ jСf:'~8R|ǏA8 @P#*x& ?BU{@ xΝ;+xADx@P)z;w5OA C&UC!ߛexROEVkI upƓ@P8lݺ .^(RF\.';G<'l9sgfz]={ZSU@PQ)ýzٙ>6lW8~VzXcU~$ od̓aРBdžj`lfJf??O6RP:;Ku "B4Z~Ȝ'8WaBo8/+q$=}n=im4My愄Ю]B_y.Zh;Æ YfBKIq{7qqqa̘1ܸq޽{s5d}BhQ v\vB=".k_?N1K|m'*</EANFFFUj8YW7 ol;*Tӈ' 1R * E?b_|ի4i߾FMLL=z*Uֹ̦ QUUU2hh᫥cW6SY&XZjU}vȑ#tڕ֭[/_RPF;w. FL^x-++K!W_}U{~㤤э8VZ7r_b1oLL&W^QV/8Μ9À2d}6mٳgӧO{Q~}d2d(Sem3MWjZڒjێ܉T'';##hμֿǩ7RTմB&}S}Mks2s*4Aǎt;ƶtbbS!ʳ!1{#9x Ǐ'66Vj)Iq2dsaƌ9sr%yn߾lz醑BkgΜĤ^oo>>>ɓ2dt=e~ԨQxyyѯ_?4h@RR”구mm2+303]ߏfϏ{Iq˗ݻwӧ'Oˆ 044DUU555%zQᑑ+y?Ώ?xyy憽=ј VՕ5kɓ'@MM ooo6lHXX8::JyvssM8}拤Abb"5bҥhjjJ۰aΝC&͔)S裏4ljYY67c]FZ\ ةiY8JJbt$FZ|c48u#nGxs5ebaÜ=\\=/a?+K|X<>}Rq{Fm74ZiE=zҦK=}WJ3?i7}W<|]y]RS9I2zk+%<,w2[V|wwПĉo|tdȚ5y}/ kn߾믿ΣG*dbzezմ;Ӈ\Qߨ>=g(z6+%#A罙$Ow;Q1a"%Lc2U:EkShӳX\1s2_auOg(]lаunGiS.l@{IN{z:$9_ H7nJ_5p IDAT++;vcԨBFnNǑ#}~pFIFZ89uys=Oddз_Od„ssC~Œd/2sf?4|j*|YGQ...1c nݺE6m>ZD])<666xyya``@vv4uRSJq*0w\MF.]geܺu 333l"RPTUUi۶-^Ϟ=ԩɌ9"e+N7&N(\9 ӧdý{033c٥*Gi122UaC}@@|gƶm۔ײ+w?y8{w%CA:^8{,=z(0ˋ'bjj!o’•TޤIKKK黕'No޼(ڴi#NiǏ+u/]br# [75zK/"?Jpi7ц%FcÄ n8q/Es|qEYk0Դ*mu9谏e;9tȚs wmOȑ[ܼ9 ǡChvqq06ϧgƎZ|AAA u֥׾Kz?MMMP(zハO/p ,,,TFJ͛7oj/ )P暻;J8" @T(Gf̘!Δ\Qz?l0 V`Vz-knRd4llǃ,< K;&1uZ)qbȑ9r$iyvvvp]ؿRݻw)wBwTF~Y;PŲsA⻭vqdGlؐr}dfim|q{>TIً8AmoZLi]~deeH4KJj%g4ET)rrr͛=֥'RKFLEݼq 2=Zr~ k%<:*l۶M:(6'"'?22$3#ׂS޾ɉ_pt6>T;iXZZҴiS'''QWW___#=_Rի]vdeeѳgZ+VHcggŋfʔ);ݻ—_~Y`z?cƌe˖e}}<==nzy GE޽ܩvӦMbٲe{x{{&M]btxq//WW_e M}MT5T4':Wv[o,wc3+߼ac/zy/*)o1`vZqdТg )]``vم~).x-;mK&ƳzEU]'*5o QP0E#]A~V rZjRږJz4~ަ¸q{4m4]Ltӏ~3\\\N}oRMpaoNANL\www,--GCCԃŵO۷oرc k׮ A~Mzs@ LjU8IT,./d2O=w|XTn ˥ަ!ˋ<{VFqKAT=֮u hߕ8  >^ d]VZAN8y$kuuu^u]t)FFF0n8H_+"Fl[ooo7oJHSSStRQҩS'155eÆ ^k֬aDDDݱKK==='--Ml֭T1chkkcaaARRRz駟ݹsMd2Yu]4+*cA@SK;|նYg!ƖV54kkVV5"Oߎ90q",amZQ<233dž͛uR_gkktMLj4x`\\\'..333\]]9z(ǏѣG3pBBBx1#G^'NFTTqqq|R؜9sHHH **K.qرR<9w7999R~}===|||M6<|PjONΓ$&&2c }U !&&0=Gwz&_|A߾}t ss 7Xծs23<2ʯ(+[e촴y@w''.dfPyW\% b6lPIMMESSN ·m+pݨKM/egd^SL5j'N$&&+W^QzaaayTgݻLBBV*0-"QWWgӦMXcdd H ؘh2uuuf̘3gpppP˚feRe\ؽ'ơ_vqdnB\/h؀Yu!ɮ(8)zm Y,0hRV<ϕ;K]&MEpptʊSv]9+++v˱ GVVV2M=-IMM@[[4ڵkٷo׮]}zNdEʯWmg͛463y|ef}PFj~b\}OW++m4U{Q'*J1,EMMM*(puu%!!L˜|gXZZJTSY`ԩe˖CLL ?^iVmmmx l_Y~/c{zʈ4 WWW>3LLLpssc[!.ҳgO:uDrr2#GdR؂ pppe˖Ɓ*$Oey6/?-Z@WWɓ's!)|С >[naffƖ-[*߿µo'JF… s,Y>}p=̘={RϪҶm[lmmKUIXP'?22$3#t]Ǯܳ42rKG]_ŴiӰr3XlL2E6n8222cŊJz4~N_m\۷ A^x14mTZ䄷7+uve;v,ݻw'%%/R᷶$޾}1Yv*} 3f4࠯_]$ѠAomߋ:QT'ADDD0`"##0XZZ**K/^iǶ aߡ5fSU#8>222";;[CPKAA4ɑh=teo_Z>͚U'5[~|תӟw|w8>sIMyƚ3:zC044_b+V &&HA|ޣ<ȴiӸ~:M6e|<$T:&LP>+WpBY$)T66Z.SW/U?,~ZhXL]*vShh(NѣG|Kasv8q0믿 affV`sɑ\.?y摔ĉ'8{TW"Tݰk0=?-m ͈j9N/y,Œ4yyIOQMVVV޽˗cii2UUU~mN>ͭ[055&eɟ/MMM$俼L͚5`׮+89/UwҺTR~}"##ˬQj*j8Ytttpss… Y6j*5k>ʏ`hh!>>>RXxx8{FKK ===-Zđ#G_bEt]EKKXhѢ4r(*ػdKGݼ&{CۼbҲʣ*efuk/rZtϹs1sn]^$0".ŕ׷cccH)lْ'O*\ 0 -[d X[[uV6mڄm ceeUaS&яnݟ01X}TOر(RSs=g +ׯ |+uO>̀m12ʝ֠A!7_}ͽC~&Nի+Tσ&q`D!TFIߵFbcciժU}W߿?4\pAZ(M|gڵ 鉞>>> 먰5zӦ,\/$'gЩ!&DW)̓mڴ]vJofx{{wuu-Q?\qP*tSqs_7oNHHHZbe2nܸA6my&|4կYfxyyѯ_?4h@RRzzz).bX㔇\.?:#ʒqiS8U5b"Yt)cǎ AuƬq*3dk3<~)S(+ $$$0eƌ#-$**Ja]UQsڵ:L\rLrW.E T>uaSu%s}%%%da4 :K3ΝKҥ ZE {ٳ':uԔƍKa\]]?w-1>777uV';CGGl""^ U}S]E/~J",, L ,`B@Ny)w^zÇKqrww'..gϞ+ϰaøuYYY8;;+[Xf͚ERRRJU ,,, 33˗/ӷo:%@PuS燖}.\("\y'#[K-q~8Nzz:NNNiQgf̙3Reg 5*^d8::9˗w@ T- W6i)7l~5g>|#VvnҧhֲMq* |}}Kܹs_j>r9K,a兑χcaaرce\|Y`AY|<5L㨨JׇʔMh/pB֭[W'ƌ!%!Ah&BjFoBW6ZtᳯH̝HC(ӻփ8U,/55kpvڅJ={eFŢE $((E1j(•t]AffeU#ɿ۱#&NԒ%ܙ;.~j51/??!jL8}ҫW/!=@P#x,/L ;1vZ[u㔖4jԈK*q!oNjj*M4ahhhHι~zdf(22[VrwۛHSz-JxzzҪU+Z֭믙1cIHH`ҠAݝxttt9rB|<{ ƍ.@'|trRKU_Ih~P.m @޽tqvJ:ɿ0ˆ 044DUU555ڵk9-r=@۶mcΝ۷777퉎ܜpLLLgWYF {fjy|Z&>(G'6C&ѤIvY)ƓL&Bu۹?0|p_d#'''_9iY \oٳY=u O>!4( o0gϒ3j}l^_yR5)8)Ypmo?jQw/U3̜k/^wI޸ǏS+;vcԨBFnN:Ej;ܿ (\1͚5#99Y!000 ;;UUUܨ_Yj&Mdɴi 55  |E8_FmE)58Ά=x|6j|u:R"hР4m\]]tR{|N4554hΝ#22KKK)ݨ( !!!CCCrrrȯz-)׮Cf&'SO[[tȦֹ K:,~ZhXL]f82bĈJ1JzJ \k srȖ~100 >>KU{Jzz:jjߐ())) ccSjjj&MpUI>y<&VXy}Hlذa,ZH4+^K?@vF 54t& [/jh8i;^zsJ3J5`:te˖qi߿?+Vа*pWSd2  f9{\x!j<]=o;G3 IXӎt18:v˗ڵk9|p]O>/Sp=8777iVIiWWOľ8OϤ714y/Uvvvp]ؿdؿۜ>}###LMMKmTʯ,9@̻7ĆAb HHHիIS,--F233Yz^.0Lҟ>-gU{|GݼqBRS:6Q8YYY)캵~zlll cUA68]Z~P7ogϖ:gX~~\~-DF9ʭ[?>K,Q*} 5UAAA -[ɓ BBBJ-&&jh|-ˬpVo%׎)tUTy>ըuQ -[d X[[uV6mڄm(+?e&&S <ٙ>IKK==='--MឥKbddƍ#=_ǣ[,SNcjj*}~'{ܹCӦMy 2kҪU+444ԩ'O$00vڡ믿.-nͫꬤ򓙙+4lPahIz77y樨uvv63fmmm,,,HJJ*Rf 2]]]455߿\ȑ#ߟchhȲe8uå8p #((^!7W_չPUꩪ'?/'hb΄u~K׺zyyy5ܻwe˖|ܸqX[[Ɋ+*Ǿ^=Ѥ}<KI >?ѣG舋 ޿x kOL>[[[ƍ_͎;h߾=۷o8::Q]r%X[[3~xLFF_888舝%ş_V-zʮ]/n:ij8<eɿ( ёqž={Рm۶hhhHk <.^%Kw1LwرRʮ]|=qƔiStǏzW^G.ckkk&)L&clܸ/N:I#Rb/u1h ?>6lL2˗/KƗ_~I۶m@&1|p|||hܸ1>>>|////™3gJϬY? ~̛7ooofѬZJ`~w6n܈&MBKK5k*3sssVZoMFFs-rQoiUh+ݻ~7222 dΝ|nj1cĨQضmk֬ѣSTgn#FƩ$*{SU#8>222";;[IV/KK2v٫;|ת|}lm-*%q*ǩ,F?rJ~G{ ~ZPJ%!!-ZH}R~}[r >͛7G[[Xvµ?_8:S&=I7mbժU4k }}}hRVoVX!MR||޽Hӻwo+vviʕg(u$.^Ȕ)SXyxM׀{3'f3Y EEi)?U1T$TTUEUUE CEKR IDDdGʕ&<ɹggs:{xHNNϑHJJܜM6G1eL¦MTPT=)tlذA?R#?rtԻ~ӪD]BӱcK}c=zT& 6`WkA M8T\8*U28{h*lٲ}Q~}TΝ;aӦMch4fΜɤIN1׵^{vdv:˩4ܹc6rn\|SfMlٲnx ^{5ʔ)B0 c+EZv:tݻqF?ٛcƍ=Zo 1fAʀ?>š'R8]vET2vX>3iT9С/LJSSF7O=*M<>ŋ~ӋqP9amP Jpp72|HRj;{CƍٱcQQQ,]Çúu놹9SL?`Ĉ]^v:3<777ùm7fN޽=z4h*⧟~_~lٲ&Mdy7߰uV/^KC|#7‹EF~ NǣGxQ veꙮ g$u1xϙ3݃ vʉ2K<.}]ƏOdd$7.R???b„ W4Kr )))t:"Bi/>}:u#;eW^!2fx&LUy#G2|p?j5/^̐&}iJXZZl`lfFA13-ZĤIؿ?bx O=Xb{ΰFi@ dIA(UӡCkߦ> WWWVJժUqwwee˖Ԯ]OOO={h*tޝ={bccɓٰaA ժU˓IgƔ7sLjժEz\2*TUy>-[Ҿ}{۷/=z25kƆmҢE;|n_6~a̘1j z&d^Ѡ1AJ'WhZċP>V}ɗ~T9 WG ҏȹQ`^ eJxNs\~w}ĕWR(<9Uо_X9߭JDEև>a|b߭s}SbPFP|mB15rzq>}:#Gu3f(u{_ARvjѢ=&&&xzzO?*IT9&%Nz,z ɉ6S`]:/g2u^&fЩ{_6l3'Xh?YV"u[ذxq,Sxm ^^.(иQyMN. =O?- Rl iՄpcX#Gr5 ???Aɰ({N b,&) jѡNQ?ՇƲal:Ï2=/h,4J^&Sϑ!]ScWV-ٳn<98X1~|,by8/H))$..2_=zDjXr%b4 N vZ-ZݓZNKRRR8zw:wyG/uJ m^ tuxptJZwQbuZF^/> իԬeR:ݫYrǐ|… &443n88q"Ç',, ///._Vҧ߳g6m"!!'''.\L4Hlllxw.y{HTTg&!!;;;eĉ$&&bffF:t(>>>,^ݻw_₉ ر|C!|q]RRzC+:zRywム;a&aaԮ]iѢ<11Lmۂju/嗝Q,X #F Jhb!% ɓ'gKLLdDGG?7,X}}ל:u BAXX&LCDGG3|'M3Jx%yQ b8 4 ۫ޱ>p+1>p~CEۮSbP#NC4n]W ڞilԩTRaĉ@2e >'2e\\\!** )((oYf1ydKNҥK.y{Yiذatؑ!CR 5k of~?okoQa.rׯ;v?\׷1˫ر ʍ2g3ʹiP*D7 IDAT[o ::m*rɕTB\\QQQ8;;}ᇸW_0o<&OW_}o_B ѷYf7OHHcȑTh8J)ʫ %y})h4F8iZ6lX1cW=Nǃ8;kէVlXN||=vL~T/^y)>tD"0GZt3#=1l0N 楗R˿>UVշek椤dYiO(ވW=AJ-۷oMڵ< {\BKμfV}t {UQVEc}ҾS~ڵ+lݚ7=7F,-- 67OLL4O—_&FSa~uV.\7[n&>hΎ[rز *[ۧ+ ʔ)Í7-SP59'-hg(rSIqUɂ޽{yS̃ӻ7nt5U/N(IIׯtcոtAz{V-,iz))Z"kמ_XsT8ڵXG[._̘1c"***Oy]VzjݧO7._#F`ȑ΢E U*wATRbE ӧM]|ʕT*͖7$$juGwb$e˖st츞E:v\O.puMfgj~QV:èQQb됞ѣGlٲ ʕ+0`yVXcǎ\;p@xǎerJ%I"ͲeCmyͮ+6kƂGay311eU$''QO;~y"shieaq aРmzS^eŽ̙s_@Nfz]v| ŋS|y}'))AaccCٲeCih4kVkkkC\\\yfݻ]6xzz_ЪU+6nh֭[C +VrXXXPvm;ڵk^:4hЀ/`\]]a$[됓wN,_e˖\z[r%L>b2yg'YPUTo_\M:=/m)b)}y)G~B̌[fx iܾ}k׮r-f̘aԩS={VeƌDEEsӘ;w.gΜٳDDD`eeaӷg̎{O>89ɓM6ٳg1{lƍJ:=q>|K.]صk; >ܠ#GpE?ΕrwN۳rJ hnq!?NLL =z0Oct,;;̂ʸ8P(HNNUFW'"#NBr؟a B':Dji׮ݨB <<\]СCTZUF[ݻW5""zq,̎ *0i$~mʕ+g֤IƏO߾}vڵB{ptt=5J<+ ]<\#49dW:i'M)sfL=qpp>sQf1F2_D(ɸ|ڴ@zܹ 2]f(?28OC8 BɠXqrߕ+W&"ЍݻwTAJSfMemٱeGR ;wԇh4fΜɤIևH[J :˘zÜ9sطo_7#C}I.e$EQŕΝlj\ogtDDFB]'S=$#^ o]]] ֏ܸqlӔ-[ MppARƍct:wfȐ!ܹsnݺ1c LaWF^Io!!!I^YnzԩSϭQNNv.(⊛;-Zcؾ-iJG''Zh[6Qr' {A\MKIIfH'9Y\E+ƋE* LMi׾==\33"7Uо_/:Ǐ_]y1QbتFFvzSQ(?+crPR=Ϙ19sYA۶X^^J?`PL-, O>{~.]^E+͛:t&MRn7KgY0./wMf֝{VUPسw/L3*nrr2/ɓm6ۑN9QPSbc.~T%Y~V|ލ sh/(MLhա[C8rў5kfz!u9|01QFQ^B1N##rePobO |^ũfjHEέ].__Ƈ`,Rj8jީS'ܥ *TR=YN#.A-LU͌ر]:(o&&&oʞcGv^šgie7*U{[7 \f,?) 8//hۛ@V\Yؕ+G~~޼Ɇ7ޠRvsn9C׀*&F^uܹYצMRre8EEEQΨгh:4ZYuNix+GTTTԥ(lNI-QpO3sscffNTDxê$Q8%&&2ydT0 ,RgϞ=lڴXp!d ͔,mJ 17ndP(xJy…J7nL8Ç/_ÃUVuL4Hlllxw.Y.3M|} 0ޝ};1܋~r[/@`` QQQ̞='66'͛7gСxbv_ &&&c\)}|8vN\M[t '}&OW: Z֍nSm/ZHvÉ'Lٹ#J۶mؾ};{eٲeYYh!#FsEm^Ęɷs獷w#+7eS(̘11uT͛ݻwꫯFŋ?~<:b_๘uwLٴ3>OIII)S`;BApp0...KN?qDʔ)Yt)cǎʹ>FJ|a&JS~ +2`8[zo3:(߫W35ɽ˗3f VVVy^Ӈ5kwy* "Ez|e5kw0V*y&t,U\SP1b#G$<AVVؖ}dVIJprvg%BqsrBaI_vV/c' ~m}G3UĔmWFBJr2#xK &.G~G&J'>C:/HNNf„ k&99JsWff8֨7nСC928wwwj<)חŋl2}g{ذa;w?[nxK,ϏcaaAǎԵIٖ~Zʖ-x4mBs}#"xW|m[dӦMi:ݻ7x+.ɊoڴSN%>>///lmmټy3 ޽{$''s=mfP+[Pdĩknj#!qx7_#6!u5Ȱk׫qi>1 &o0x9GBc۶m<޽U~dYqQdGrr2VVVDxcرq+G :QU+VHJJ |U2]ĸ&E9NnU'9995N{GNs(DB f^t}l~S6dS~oIC'ʕ{vr"ʕ˳ffcQ 'iڴ;+PN\"gd"%Xi4.]@[m۶@~t4XQu5a!ף!AJFl9NJRݝ-Zp~.]+,IPذ 3SSڵۓ۟NaʟL+1j(A%9N*{{EEbeeUUjj"9A$cQ=nA 6AN5kyqTY^z B)Rȡiߠ 4| iZ,_$#cO%G;@HdĩpPؼُ#G|3E||/P '^Nz\.?_.ARNNzؿR_'Ҿ;AvL|||St~BB.JӃq,Xs׋Ԃ>}8rdyu:S, 1]prvi FA4i!Cر;2c:tۀr4VpYE*n\WC"/>└ăqv:KA(v߹Kg8 y}4izawp+WI۽\%޾coEqVqb#qܹZ5f߾ch4ZJF^DFg˖܌ _Gױر3ï|r~ʖub$'0s/N,Zo|ϋ\m4…݅>}RJy/%%m& =z,Rʕ=9ʔqgυ>x{po_@'BdiIZm_tSOkqF9K Zvh0+4\`jZ#uĩ woOd}v>B ٳ#6<~Mi۶)uիF:ujн{{{pwwܜAzճƔ߸q"͚գo7qpPvV_>֕o@rc۶q boۻ+^^Uiȋ.]ZqܾJUkЬY}5O&u133Ezj顏oLy0zt|--Z4;;uj=Z=Ĥe:eI~V|8uj LosA(Ln\?3Rփ;a! `\<G["+knW xxVtjF5aO ke+ȨP( `jtڞ%ZZQApǏ &{#Nn+Z8z`}zi;>SoϪMG< {ŋrv$ IM_$q/66_?ks zZ$ h˛oELL 1N<{R[DB Ǐ WU}Tp=;4jUqtixX=jEBh4ɌkE@F!Ba(٧L2 F Bfǎ7s篼ڬ_|N$Z-6{ IDATgQUd=2UO݊[Q\u}҂Nԩ -[S!BGTl>,Y}VpA^Iș˻r.m4hDc<-[͛ӣ@HSnIAx!ȈS7 %xכ<MFNV"iA 1jTQEC$  dLr/F1!KK+  BI̔˗.*˗03y"N   :w'sڿo鈈$88Ν䘷qA(`Z-k?1\>Kd踙",AAxiѢǏcmٮgR*8:9ѢE s;}d őܮqINIU*""L= Bq#]* LMi׾== >SQxT6E!+ܾ͉'hզ-u<:N""֝{SBNL3*nrr2/㘛5rW6 DʩS'ܥ *TRN#.A-B,bZr0AN3m 4DüݣgN #$G>>\|g/>Brm|3fdRt'p݋b֬Y|L8Q|r&O p۷;wf(qi֮P&M+ Bi}@gh4;{P#C8Ftԩ?cA@A^nٲ%;(ju 1f̘A26t=888CYfRuSV`X[[3zh M!5kҒkn}x@r͛#̛77CaaaJ-[uiPl=k4?wѴjM&m1=ϋ롓铂 %꽬Rر};Ǐ7S㩠)}ܟx0338::Noݺ??~8f}… y(^qBCC(_Z4>c>x&-ZĄz1{lڵk+-UJ:ӇnxX(anRh4,I)su8bi& 2.]`ehޜ}_hʕ#$$`\\\23`{ܽ{-[6ӡCj5NY &44'ӌ2x`._G1c iP QIJJvrvg%BqsO{:JSI_΋ W'#N % }ң "I7uy!qqطoݺuIslggGbb3gʐϨQ#2e*L|>\|uҺu+,-- B))ZUȹb(}:/' ի͚6%c^ '_X2=YW7n>N# ݙ8LQ5,_J*S''~ >ӧ( 5kܹI+RY !7nEt/a]]_΋>ū Bz/;v:`zE')2~d3R$`eĄ8~6Y}FZ-!78t 5r"{g֙E޽%<~,Bp*a> p  eU98%%%ٳu*zV)JUbjfƱÇpq-^Yu`!nAxJz8 P*9Ej4ܿM "111$'E^g 9~8 P1jVETjo!ojًvAXXk=vY⃕AAJ99mezjZd;)))fhԨ!!!L/\@۶mBR1g Ί+(_<֌;8-[fp̙3~-_}ʕ֖>m۶k9TP#FB*U#&&&tYJAAJF;HIIA|d#))1ki6mTڷ@yW N:3p< ˔61QfzMVePg,n򴲲g3t\v%Ks,۱1}6MIPx  ٯZfk~G۶md=CěUn6`{̝:V/Rf-TԨQ @6X[۰~֭FqQ}GҦM[qtt[n߿?Wd8cɒ%Ԯ]ʞkHF6A90zVL^113\O-&~6yK}:|2YfP(ÉqO8Ή'8p`?}ҥ ܹæM?Ѿ}{Μ9âEСCI/bڴiߏ/<ŋ1c7V, y ^dPMS"y /WWWˆ#=wк}vi`1<-ZbbbBz={6ڵu9iPN]n B' ^h_E&"OqzatM~2raf͚ٳg4hsS\9BBy8^}|2u֥uVXZZ>W9>Oe:>$.[nABYf̝s닕+WR'4k*ƍ& yD1gnI G4 7oSڢN(Dn}SA{`ii)yj٩Ty/cZY[AU!<,իV|Ν;3[T*/xZ4A3Ȥ0eY}+ }!|rF@BB舟_>sʗ/?'NdĈt:':sssi9abbbޞ1>#uKVp?^r|̚5SNejx{{ӥKi[nѠA\+Nᖝ|_XPSx~rSwa^(uxqss 11;;<h"|*-f5w2y44yVEv 'gOiZ6lXψ  eUsZٳgywN_reΝ;GBBΝ^zzØU*nnnԯ_?WzҥKFTp '///._LVʔ)+BٲeQ(DDDp (Zs9}4^^^XYYCN8ALLQu^ziϙ3gU~OPP~}Taaas֬YÛoɢE0a{ez=Oo_ &&&c}aݺu8q@y(>_{=?m۶ӽ%%%駟)}iݺ5~F|t:.^8GحTD*U ƽ{oooo ::J6l{6}xx8/2AAA( j֬IxxṭvܺuLTTXXXjm۶=zRIzhԨQ_u]]]ٻw/ԪUf͚{nSZ'''vڥ76j׮v"%%W^y pi^^=,--ٶmÐܹ <ښƍJJJ ...ܾ};WrL3rNV\_9=쳫pqqOCxW[T tRu۷oXqեƆuMLmJJJ.UoСCgƍ駟UVٳG_j:?hժ:NpvvƆ _|ñaxyya֭ضmڮ0**ҥ VZSSSvaÆSNҥ h͟6h*B~NNNC6";wo|W_}M6#P;cضmmܹ>|8fϞ عs'M6McUe˖!&&:uQ Gyy9Mn[|9lllM6 wa&&&*=_Oӧ+ɴ:e?Z~ k׮UΕ;v`ܹd{`=۷o|tpqEO ѥk7dge thԘNB!RR;;;uիWI&aĉԩ\9]s g IDATQթzkS/G} Hp YDlK-djt}eBhK<|pss ;v:󃭭-B*V*qG۱. 1j(Ӗw333V+kB^^z:NwGXY3(.rJ딖̬^kh.\b0_{ ;ٳgcܹ`0wrǏlj'0aXXX(s֭[xUVGGGyB8A]|xW'''٢JM"-nJaooLKmK.H$<{ ffflWQ^^BH)0J}7uT̝;rؾ};6l؀~Os>|8s 8LNڵCHHܢ*A}EƩ3|zL*\.χb1MLPQYc]puuEoMaggǮꓯ>|8kr#RSS!!t#jK]ds;wB0`ܽ{Gž}pfc8v옒-U'my///WzV3^ZZ WWתh߾=JKKycʱB!\[MYkyxh_cQDq"!sAhHޯ1~xDDDޫSWL+V ,,L>999lk! .(mKWuNz۶mJ-4 ӧzAA;[_rW96CSQ~}ʾo8(SSS<~])((Fvv6;Vk>Zr3w\lݺwﮥ puuٳuFX\{ʕ+:M5mHSNl7\.:tOIxZ*k/*m] ɔs@u4]_/R'!-- >>>>>>Jq_[$8m?{ JQ-][͛7qM_5B,,,l޽{ݻ7^RXYY ;.]ٳNɁ?+Ǵ4KY]9bdR\ז7)Km]ZO=mʚD|!Wz ?o<]ve#7)Lx?P饬x*o`lEb̖bFcq >}f[Yp!֭[WEGGc޼y޽{&8Y3f 4_!߸8͍7ׯҥKP㘭-֬Y8Ƨ~ z+*P`bbΝ;C$b.\q4-! rJVsC}+P'򱵵ŬY aeeT1ݴi&O^z RW_DD KKK9rDitm=tΞ=TN___1Z6l PGc9ʟ6o:lм<Ϋ?qD <~:c ?VRr ZÇ9_H$X,D"Mj9?%~GSŁ/WOwWG5yq" Ko]^dڵkQǃ1)hsG Fa 3`r3Tng\|/ sV  xx^.r)Jʥ(IL$AqEUR1>Dsq"4[  UЈHTf %tTYYe˖a˖-ZD077K>ob\rJJJ|n `ݺu\zrĨ۷g}>)]! U7llc_~ ''&?V.A jq"4B] EEsܸq7obbb^+Vil޼ӾzeXXۥKֹ;aB=zwƈ#&ݰaxbƍlݺ[nijM0&L]k.?.'zxGpp0x<^I@u??~ӧ"##m}= GNhBဈDe Gyf$''cx?зoŚ˗z}&:u OFll,|,X#F6lhݺ5b1[nؿ?;ak0}trJ|:(,,d'---9ҥKrJvBn]H$Caa!"""  h9ČV7 $T˙Zm-MpR/u#^$֯_SN]4|EEQXXEtt4x<nΝpb1B!֯_ܽ{GƸqU&uժU8x 9.] %%7oƶmې+W[FLL 㑔m۶ $$022B߾}1g̟?ׯ$$$̙3JADD^sxݻ.m\ cƌA\\\ӧ+4h~}xzz랞{.t8|0YoX|9>s0 >I ,͛d gϞȑ#QTTsaȐ!/'OUVZ zjU#KKKرc!  qIk$A8£hqxJjr/ZΈ|clx<ܹf8z(ȑ#055;T0|2>|)S(?yUeM[:DGGch߾=>b+oeeUk[Ca̙3| `֬Yرc7k+22K,':&ā0yd9s|$$$O7nŋ}?S-rسguָs\\\Xl/zH-N8Aӿ@ @ee%BՍHKK ӧӧO H$@ 666 u*;vUz<055X)ߪU+}9N|>RT)6*ddll LVw}~=BBBpiۍPVV΅RYY H$N5),,DǎٳfPap!::Z'ɓ'ŪU8i_u "`bb}6GGGv_ccZ455e2ѣG 2 h9֮zfzDј[҅nݺ!55+rLKݸ^}U$%% nnnj]+c7e#kkku:cZ>H9sF)ܺu͛7YYqII]ߦMώOꫯqR <|z8qpwwPe̙35j+**[ aɪbԩ;w.r9233}vlذ?=P @Q X"22R@@ꑹNFm۶YFpp0mNA`` ߏxj ((kFCス9ۂl]ݻRP.TݻN߻w/V\D).sb1w\“'Or7F|}}qvjb…شiS7od&ɓ'+۳gR|x >WWW̞=ɵ3j(?a@;|'!u: qA*bҤIppp@ Ϧ 44CYF3gD^ 0gΜZaMLLйsg]u:g F^^;vzĉ1x`@DD KKK9r۶mP-PQ!^nBhC@@\]]TU.W?o߾ TE f̘cժUJέK,Add$]vXx1"** "bD'碡wczNySofTobWPQQ2Y|9~gcƌZe˖!00&&&>͢Ey!66{ǃL&s:88`ǂ8qV^5v7%ߝhD]0Dt骧T_̙3ؠ2ic:t(~vLcFFFdANP8r H$Biii;MR֭cildߟ1Ɉ$ +mu>A9N4)APZRRRͮr.["++ ۷ٓ(JqEAz:9NyyyX};+Dhݺ5:tK sss\v횤kAAttr?C:t(nݺ/&)AA,WW=‘o=z~~~8plI:P5DŽy+XXXݽ#,Xϟ+gOD/^İaV,`ifvEuDD|ag+i"BoXXX`֭l߭[ҲYMiin4t{P "H ^? )om%ᅱ.:#o׮fϞ^oqJ6m:p :RvvJ_r۵ktdddcǎju?gρFIND)))ٳgwb7nT}A?x ߏm۶a˖-عs'.\X P׳ݜ zjUucCXXNΙ;0ʗSBBΜ9TDDDo߾~ee%-[|\W_41rssann[[[޽R^QQplmm |t)))ؼy3mۆ|\hݺ5bbb$%%% AEEзo_̙35WqmJ]CBB, mѦ_Mر/_CVV.]Cݱc!  qI.9ۿ6V>X0LMM9O 7o鉛7om۶ؾ}^_&qR...Zk׮UVfa U."Qߟä1E̷g0#F`iݺ5y3UX?1?f.\`Ĭe0odgg1/fӫ)̌_t-Zhu4U˞ۙw}Ǯ_toٹs'}vfܸq+Rv]$1_5 O>ed2#ɘZygoݺŸ|}UeR>mοd&**]RY.\|zTTxbӦ6maF*rĉ;v;vPW~59kեkӯ64ښ]d_~i>o̊+XU\/˃[uҘl~~>v}}_=5? -_mSgvf'W ̮_sY3g1_}YˣwU0[N1?ٯk̢ o`&vxZXұ237g7oǏ!agg7/vvvX|j+"@ٳ"$d9Kٳ[|wa̙juxwQ^^?KO 0>k6W뫛L&CPPv CTTB!{H$ٿ*lllPYY 066Uz)o߆ ??Zmk67i{>p\W^__E %_msΝČV7 $DU'Ul4KIXXY۾]1Nǎ۷i!JK###۷og&Xx1rssjE.׮]{&M408~ҋzm0m4ʧM!((׭[9͕Ŭ!}[YY!77|>={6z40ydܹ]߳g^S.χ|~4|o޼E5x9  XN8ӧOPttܙFU3gW^D3g;0͛Xݻ<2 K .ĺu؀h?{,:ĞO1wĉ/66˖-C`` LLL0|pVlMMMe߾}c_=0or4|/)S0aLiAP1N̙3/ c8qcS}SYY SSFCK O7q :ƉZ t@$+-gϞ]il,KR[~~~ ꀥ29F.9S#>)r 8ooov].cժUleZɊahQ΢>ױcG;v ˧I$++ ۷h494WHxLTYQX Lf0B 066IXCl0s'///]uoh|<Y>M'ڵkU?-/A4C\#cc_|I?SYi)7;oBnpswG+ -:M-DdKI$  VI\Y'Od>d:oQr9D|tYCl0)zJ,AA8UVVk2l\:L ΟSdY %  B$i!wp3EQQ$J|.Rt@DAAfI.7Ir9wC.Rt@DAѼCvn^9NAAA@a_̀T"wM^R}Odv   5rпU䨬Ύ#k RPTt,Ѵ:0r46YnAAF[ڇj%MSY|CASGKA?5T*UZ Tl  PTTNjXǩ P nnn0220x!ƌkkkj o6?~t֭[sssx{{… سg^~e)))1Rpvv%PRRR+?-][(b۶mԩ7nM)Xmrmhu)X,ѦMcݺuq@US)c޼y-P^^چAA9s:OExwD=k:Ndbѣxb ;;/BCCILLٳg)S`ȑO?&L@pp0իq5$''#;;fffE \}y?3fR[<Mr5th*ʕ+q-\rGVV@"5O۷o֭[Ċ+8_  ٴiAcƛ_|.ޢL@J:x ,U:addTk_EpqqQ{aHΎM:u /g+ l܀F̼NeQ_۵cڪ{Sʣ)u:$׆l.eswwO?Ν;kmHLL6ld@ɳb~pgͥ AA0!;7~LFΓJ`݋Ĭ]JۋT7;+q۷s!H *WJK+WVVl/_ȏLTxJXXbmR]Ά'X(\ݪڼ7'y4t)\2-Z\8-s?~XXg<~  >M7_߾;v =]c9TM6AAp=#80T^^#==-ҩҨj۬Yhb<|71sLEkk{Sʣ9TbA}ѽ{w|D"dg`ɒ%:- ` /AVV6fee{=vܸq Ǔ'Oa8AѢxld2x<9N`*MrM6#Ц3Fx_魴cm[ڧyg$`YxUrmmyoJy4E[[Z4)dP[=ڶmaÆT0t //okזM[h^{m +~q$ _8i#? i! H:sf*`bN{+ RQoe1TZȖ Lyߚ!{=|jo:AhܹsF`gg~}5D"HDFF۶ms6x&m5o*ee%-[-[4Jziӿ>죩"Wo2t(:A FJv].!c$]AYM`;Ep`TVfq]5*[ClpХ$0 h3z,2?QXP1χ={M~:GctiIe!P9=d&#D$ 2ĵ{#fh8l9}^4;B.C&2W\/_?A.ww7>} 5Gxn;!Cb>/8 }PGIzxuBtM4DDF"³{wmϞ$Hb\ (94ݑu|>2ڗML V%AzuuÑx7: 4xrhMccc .ڻ! |YI ܟN>HQ M2zGԹKRAa!qz  TpV*zmv+a^u$ڻwps^:4!EAjB##8u7n@}oܸ  \rKׯk׮ @rr2N:YYYt$ {]-)ٳ'ڴiǏ#))  d\򜜜 OOOٳgHJJBQQ=z@֭Q^^TÇ޽{HOOgoժ|M}?˗/#;;脨+jj*ܹ=z_:-ԡI_l,77(..9zLH$8::"'eTn_6MD,FVvΟ;!C@uFALL$r6vvpr:VZ'I(B*RFFF),777OSsU :T zRaXDׯwQrrP֍70f999U۷tM333 .ik999񁫫+aii >>033|||?9!==={)LMMѳgZ]զ߻w{%x<1`Q|9@>}b1Zn]V}/ ݺuÒŋ1j/k;{cahۮ%AYĠD͐Lm!ЩS'XZZP4>Ĩc¢RRRqq" B3Z2& yʟW| (CLJx.6)7 1xP5j>2K/Ƭݻa+++8qj.c`/fM46mڄ_DcͰgϹvl<ܥ 6nT\|9NH,_KZ '5 )) >^>066ָbBQ7 0A8xySqW,#pE٥B`7\_|ypJ2 O<w'O[näI1d`8~ݶt29c /W;w@,c_"2cl5{΋/S? 7oҥ?@aA!n\a`BI B Z88~  F:xmjtKI ǿ 㻽qJ\I{~?1c5Z>8#;+Skݽt?`HM@U+{`kk*|jmssyu>&ݻws>66N&ݝ-+' DocѶm:E ixQ/<8 iۮ yX=k|*5՜gKc+Na>y< :tT  4qUCPa}8U^ ^d0plڴIL>џ_{ '1o\888ѣGlKMFF;15{6>l%  KKKͽ##Q B TXd2n\ϞKZ cՉY4B!7JM%<< _m؀=(**X,Ƶk0?}ƎPPP|q6z^+**`jbSSSdܧƎwj&˭[ͳ;6o٢5;aJ:۷`n4y2nݺF(&͵9NAޘ=g)N8,$_ cژfG;;; ?xVTTӧOSv?^;;;\.Ǯ]Uؽk7rj/J@X`Ν󜛛7| H8q2p%9g5.ߺ>O|ty 3gG>)~ V+U*gr*ƍ+k016eٴ4;onfj z-7iiijEoAmBѡC{o Çi<C!!{}ZnE{:k~ϝ;3f`֯1axN綷GT_m_~ c%fΘ #:zV6nrPZo'lj hq+Xt\Z5[7XG&`*pܹsX1_=wnJN6ln%_NƠApr㔛7obذa1| Ι=kZ.f-ߛDڙ%'O*߅X~=z2ѽ{w%a:;}Jxw„Z;aΝ?UoAY3B1:rGXXXjݏAz!~nnK"-- x .^`Hi־q!V| 6kDrjbdd1cFȑ˗b tC;v T]v{vmLSW?l ,4d@ 7ߢVG &FqCg4eD uE)F[gneL0Ydf/2Y"}SD3 R R1+l1IӞssOsr} TTTfW*o x-1BYNLxz#!v雗P|h475نkƫͅ^w sd d߭o@&%uSw#O˽LJ<+WTO/BBBPVV~[0!ӌYKH[rT-Wyrmī9S^~e /j5F#{Xpܹ Z,[[S޾>DFE#,\Ȩho}c nIRl6Ɇz뺤?;ܻ^nps֭FoWkA^w,+^[~Bf0T(qLCppaF&8V[e  ^|\"Z)))0W{;44qe]o?m49r[KY]]T:5k֢ _8^[#x”zɭNɄ$d;}@횞k'NDD˖-ǩS_fѣGZܵ Jgttt=xAAͮ. T';+ EE]\Aoy񙙙ؽ{\%&$`߾*3G6(J(rAݩ Λ >}:uv۶@ףcqlh["..VLdd$Tx7xSSSq\8())Zuu;qj-pPXy+95=zDD4l@h1^B[K0446nDo &5?e+|y:_NAfUH_6 qśvs p8,@#''cl5~\ zz|&q n,Yh9ΛNSv"͜5[ƬvZ՝؆NwJS v?| ٌӧOcY1s`n@LL 5Q='NyI7|P{}CZo_Ұp ȘBubUQk:}ݙ&\{OacGrW="""""\c+<_S8q"""""I6;k:p']%=/zҰp6:A1AҠqDtW="""""vc8y\d """""@acvo,䧨IENDB`sympy-0.7.4.1/doc/src/pics/ipythonqtconsole.png0000644000175000017500000073540712253362407021706 0ustar georgeskgeorgeskPNG  IHDR.nD iCCPICC ProfileH WgXSS@HhH ҫ^Jb/ !KZ+".l9uw̜}͜yf`D)<Lq+:&Ey6'Cyw Du3ֿw N*G9"q&$žނL7A$B\N1H16 .hl8YYDhG+B<bͅx SRS\Qvlv7lv7<6:dR؋F?Ujkф5-#9,0o 9l0U Lq ?b%E>Ir;HaPa`!dxPl~xԸ&"4Z:gdM|ϙ$v1߲PC4ZK%@(3.L9>I؇'^xp?Ø1Lq8c>0sowi KBy'L$(*ŔL )i5J/僔TPjT~SRפI KKK;JKsIo.n*+=LUR$JjzFFFFGAf@fL!K22i4'mMB@5t=I@?eN.-&RNZN_]n\\rle%o)0,R+*)R k)Ua` ]'X(gg* *+ *+*[+G*/T.Q>ĘLf s#0$IxOtm{*n*<\:*TYުɪUTj&jR;60Yidɇ'SGMCSoSi81tLܪyJ_%ڪuZ9KJaαյ%{۵u u"tV<ԥ&nmқDF>_EQk  U k э\ҍʌn퍓ww&6&|ti))eSnͲj̺2M]5aizbmvqWsr,,^[Xr,K,oXѭ|[5Z6Yﲾcða֦拭ضֶN.ή}}%rm3;dߩot=:l].,8=.]ڮl2nn\ gI_z{=yt\yzz{+zGx{?I]G w_ß_?`4\ -0,8qI8i:#`Ɩfl[6<+dVɬKB/1{1~Q$%R.rNdu(iKĨbc)Cgo;fNΜ[s .:Om^ʼ#EfK9\7Vn?ϙW{PЗ蜸%/<łWI~I''W&DԥJƥ* 4uLE9tm@qE171S r$F$Y.Y%YD.8Papa"E=e1eK/ݻ Ye5{WZI]U V]iƚkz~&G6Gs{uuX5{9<0s>'?˾>g?}!z1<Ÿ/^WW#ߨ|ke(dѻws?~h⧨Oφ||.bk###"=z`&$ދb١hT F A? *X@P3`ч1? VV d'#r 41<|y /⑑##_.cw2BM=rjm_A|m`X?h pHYs%%IR$iTXtXML:com.adobe.xmp 1582 1134 .`@IDATxieUy"#2#2#NvzHmc(\]* n u#>PS Ňbm)袱B@ c0 vgzv3cDd~y";uZ{}{wCFa 0@a 0@a 0&Ca 0@a 0@a 0` .@a 0@a 0@a }@\KF@a 0@a 0@a <a 0@a 0@a 0 ypo.E a 0@a 0@a 0"k 0@a 0@a 0@7 ži$ 0@a 0@a 0@ȃ0@a 0@a 0@a`0R0@a 0@a 0@a .@a 0@a 0@a }@\KF@a 0@a 0@a <a 0@a 0@a 0 ypo.E a 0@a 0@a 0"k 0@a 0@a 0@7 ži$ 0@a 0@a 0@8 ~bP@a 0@a 0.Nq!g`0p(`\se" S3 0@a 0@a \ aɅqsypPL>V@a 0@a 0@X~P{p2 0@a 0@a |Бb8'+`bg҄0@a 0@a 0p86cwL\sBz@q!VcB@a 0@a 0Z /vgsrykrwV/Bk^@a 0@a 0@!a=c++uV쇇J a 0@a 0@a G  8;+=!Ƃ+ʃg!ٮݸ@a 0@a 0@k1pV,^`;5.:B~x'۱ N0@a 0@a 0O/=˟^yp}X=<{e@a 0@a 0d`Ͼ_;!^.n~YkY/0@a 0@a 0pa0g&Zk bX֗gsw^e@a 0@a 0@6{pt[w+fk <؍csAo({1o~.k)0@a 0@a 0v]~n܍4\/.n}[y67Ä52ĩ@a 0@a 0v^ni{>`\.~I3{$NbK Q@a 0@a 0@8;]DN$~': ybZl ƹ(w__z[[oqg>>8\Y2zn[d a 0@a 0@a ʗeKMuݘun<`7.vv_'n oQ"nXVC٬e0@a 0@a 0v|b QkQEy-nfybZluγo.Nbݍc^0@a 0@a 0;ۍ};9/v|v\q<^:_v.[:y@a 0@a 0@8 :El'ϼy{:ٔŁ~pqZyy)k*`ja 0@a 0@a U_un<8p.v Η軍7e*Wg*rMըyr_@a 0@a 0/ܗ3esY"}e~'.|2LxY}]Ty&Ţ|0e54 0@a 0@a \P ,ʽ7Ub cyŢ/~N.rC݌0| v2yI1SUm3S2-'0@a 0@a 03/[ɢE>b S}L^c|͓;#6.Πc6b/W-~^Ot//ѾH.Y柗{q@a 0@a 0r{#岸e̳/\G~^S޿*fff_/\6W_ՍUesSE0@a 0@a 0p=^u ^WT|_6MWl}5f^cŭ"w3* /U9i 齯טyj׉ت˹܎>vr%& 0@a 0@a _j^u}>W|[67⪮9>[[kLs܏?uqe3sqz[5F\K{ͻSq5Ůyp!ms9Ie_~-/ONyS9Vcv}*:ة@a 0@a 0@={v}QS1>f_sqEE>㑫6c7.V{UE*Jznlos\)LTyyv"@a 0@a 0@؟ w{T[fsU|_UЯϹ~52y̻\5vU:?X(.Т_}Uٗ%FYW/yky)Z4~Y@a 0@a 0@9}yi__XsTLW̢]#~rcgX|_2TFU7b&EU}j(|++n^(uz,fw< 0@a 0@a O ,{\gT̪6-OyJ⪾nU|U̳(^̦/. Ū_X/-m^ʼjRrUgn*{_?_(_ceubb a 0@a 0@a`=nSqƬə_9{gULj)T}"W1}o<.V!_xĀqUS޶hO9{ʷyE1Sl< 0@a 0@a 3}~[37^8r;_TG 񌊭̻kz>G_毈.ufګ^k-Or*CyzY7͋7Ϫr^U a 0@a 0@a`}/ϰ~;{Uz|W]ƢQUvyqg__uΑOno&6~V;<)+N 6+`s|ګ4{܁^E䮂NX9\] F]iWtE;zk_W_~Ç/Wx )ڀd3]Rx_m^:cX3eͣhΕHj*k~袋61͋n=$zZG7N1_4kS}?W㭹H=Ҧmj~u :_q}e>ֹpyՕbӹi^m[e/|{͋n=X xV]8]|Ye=Ҧaj~u :_q}e>ֹpyՕbӹi^m[e/|3Ry綟ϲMk$B[jڪ\֗}֪5<8Ҧ[sЫgj3\6cxW])>+1f]U/kܼy~cNȼlGϭ*XJ1ڍG*76em 8WV{#Smos.Moc3j4j]U;Ey>>wo缗朲kWcaxu~Ǿ[ovcԩSgq&\}T yy Ziij-scz?>daXZV[^k} A2V_<5CE rO֕ÞMbk}j\ՉOyɓkU,xC^s8Xuc3W6y47vD7\ycϾuiCz~ڈ!}AoZbU{su)c{MCԯCW]u/hKƸČ'^ۻ\{}Skd-{ s1qؓXlU^3koJ_zQvִn,~^w_vk߇}wùx8bI,s}}}n}`7%{}` uΨ{A;k:?5:L{g^(^0חkT}<ȥ]ƀqcݹ175S!Fͼ->*7^:WG2,:ʷ<8⭃]NvG,-ԧ~׽# vۆ_+g٫^k7_sΕPjWγ_[u^>vUm!NiOkv}v#Vy/XbzK~ooKXoz`3ЍW獨pMfd,~^qE.V?8t8pdl7Ĩ9rdäm>0zۏ=N\sbe^m=TխM^A'a s LUsz̓ڔ*8{QشǨ5Վ:@wT6}27/R]^O }5p^q08lb[T9ץ9G1r˼ڰ3ȃQU6y]W\skщsz̓Xm1q{^ZӞVrbjyQڔܼHuqHrs>ןxs+N7߯ k5#,pTBf :kC2W߫9c3G'ю}0)|˩&)^\k#ޜS'\aCy^X' L?gkR} ?ݵxϿCqgZ+y/O٦^c\ֵ/ӿ)Wc~~p8}{L?v<Fv唭b?ohZMI\oVxt3)g֙]I*=7lbLocΰq2ElI9Fk1Էocӻ!S¦r1xrkCg}U{O{C{;GkC*V~racxϬk^97s7ƀ.^ F~>u:\3g0g2W'G?7^sCՑsæ.6 's^7z3UT֬e1]ސ 5O}>Nsn476b799 |!5O}SkOׯگZ| ?,˥ ἗|cp˙~Jo 9/fĵuΤ;ʃ'[UVU7S>?1MjUW~Wl/+&kSYm#pź}N|3`q8GNňzHϡ|5'*<攳^M>9iCrxx^M<6up赆:11>: \[|ΧZc27fyU'N.;6R9k>0hGZK Y9F ڏϵlM%81xź}NeG9r /F\x.جmnPcjSn as>W,6u#է9_IvjMzAfq̵!W&j=3L፫ukcƉKέf>31ڑRjC|αs-[S N} xn%FYp‹W1 6k9ԘlBgǜrza]HG}s҆煝cS^k#~бcsa|*ڬe=&i/SxjlƚXqRsk#iovڐ5slx\TS7^yQxt8#bUFz591᳞zbsXWsI޸Z1Vi8Hm@گ8i-6d=0k?^?ײ59z^b5,>)q`CjNf.ty)g^tT\='mHzpx^M<6up赆:11>: \[|ΧZc27fyU'N.;6R9k>0hGZK Y9F ڏϵlM%81xź}NeG9r /F\ŔsyWF16ؖM9C7F9/c*v7esM嘊VTvV=+pbZOj;Su*j}{,f o")Ao{~[n_FK{QDlnmyǍܜ#/f=lDg0gƘgt/zu361Αڐ9vV5~S7A^>g]eӷq17vk:*=8qΫ=1^Y#gjC^e)?c*{&Տ?'Yu댏8B<[~C.%`Б_976a 8c]9nbS6tsiڱU8l}Sq쑜ĉSw^9ތCfցx}9N AkCcטyk |=:ȗ~\Գ.0G~œ=4^Y+\Fe΋9uKi]e^Xz>@7ZHcotت]8l.ĉSwNMm=1؛q-\np;5 )^s[c+J|=:5ĦS׼j3\bsIկo mQ>cSyh_7hu=hoZ[c9hSڝ^΋qΧէ_[/G>=z^֏T+!90uNXp\QsQ:W\&c,8㑜s0 ׼q8jƼd\9k7'!88;GH2u{NY`I,Xl/k>y b +~mhW|rb=|rf-9#pԦ_ިUNvu÷8d^KŘcNp61\Tz]f^|}>pt-cזł19>k! }Ե^ߘ>oG Ҝ֨˙+UGRKi>GkϺ`?9/_8t>^_ڐK:zcpǚ͉ds3N)?8Ç#[|˓[9'H'9yz;xw^ElVNÕέ%[cۓXsV9 |q))?Ѻ˫ʱ|"s^Zu?z.oz/x ~>6f lhR}f;>byXmMa-/ݜwi|Vu2j}{9/9T< ~#ݛkΧnW's 9M-{I*61N>eU7fʶ̷(Q25'G5|۹*MU[ϫ9墘y>rg5Ɯx7/7K7:h̽Ƀ?ͧDZ=`#ͼf~o⑾91 oC=b?8lƊ!}=r)Vi=և4Nsb~l`_`XHcnNtuc#kk.0ă!?ż83shqNk `G:<#'=XW 1Oc׹Qv<sx3ƢCU)rQLOrVS:1\fs~BX'^Ys6J{M9e9s7܄p ;70nLK<تM̸_!I/}?ڽ O\=7uxo[{=?kt3?Wm^j5~|bsfN2g3X|AkFbh/5c(ȹ8rkCkOCm;j 9Y!1.*qJ7:C\xUs㘃U9!G;}3zni̍;~lY}۟ǨjR{#3s39Þ|a`jmṡ#ƥnYϱc ]in_m7zX༖}qX_u6rZ6;w=7ϑ1^s+_N\_@?gu l/Ï:z̉19,>FgΨ)̇5Sc=[a`sX*^R#6tѽģ3fm㪿 6^j 90\EW^acG׆N}s3+幊[up==9>_C^~7>fxmΉǦj|l/?>r193?c3,>]6F6gͯ|8`Xlh/5yWbr\cwN&6kWmLR6Aκ.ʜ96tk,vU6k!9z.zsp}j#8$ho|W͞푹ڜ_yǏM{=?kt3?Wm^j5~|bsfN2g3X|>l\mΚ_qeS36^9kc(ȹ8rkC=E<:?>m6{ۘ`m uZI]0Jc9s_m7zX잫xmBr\8|FqHzN'=#[ɳ1\hUKͳG2zirWmSU|#]Eٱo?q3#lg"=ce6SNyi!fCnRnl6kn`o37Oçx |#o旾l啯n}+\䛪o~zeCxGsǰw$'}?g?϶ˆڛsp/nk[~hE!MZkڇh1GG?|>y~plẫwo{p/^xK7c \okcrް:14l`wC-mc0̅Ͼŧ ?=1AL7{R|S駟\Է_V;W?5تUه8UQ _7y qd[ojY˹ysL79g'/qן+9g15NXsuMʕ8'zkssvn=:H9>~󏆍s]9Ǐnu|þެ9cߺUn:gp:c5Y5Qmx n>l`9g;71ՏuTkc` ݾŧ ެ9cg vw{~d^yӆCmݳ:EykVU^gץbc/xDžR^\wгg{7N9~=uBqzɅ8sLaSګ$[uV¿s;e['9ڪ.1U|+R"z{{v8uщ#ߑ!dTSoPFr༾樱lx31bT 8F^HRWdnH{7{='~P8tzcI,\`_EZWyeM87bX8Ċaΰ:)c*TΈ`Xɋؿ6>X֠^]E*s]S7ƈgn 6pԫ0{doʍ|^s77* ^Ѯ` CL/|:G:U?~ۖ3p3b,Ev~zp1IMٵ)k\)6%>ueoۮsy5-o~/I*~m?j?]qLJۮrU/.R L~屯_x<2|櫏 Oa{w>}G&?"K9xM7-?<؃7\1zU M^uU=+_ylxGO>ax7~nEz1?suC|+?yxǟ _yõ7]9\kozˇcg==:}H~sN81ƀxKrj 61ΰcubUsØx!x9[;6^Ansهy۳xs!>v0`sq^]R}4sb{Çdmu0gO>jQ닷>EeϮ\É> ~9؈s`c,y[Z` oZԕĺ;'F \|?f棆8͓rG+ l^׏57l=ףAƢkZw먮!0X_˜zG\^{9c'j[\#`SljW.K=/vprSq]S ׺k_G;{cp89^{9z{ 0FXmK]g֝80#Hp 8x1i={j?E/эs*ǰw>ϯ]WQjg7"lU"2ץϽ+=UQmˮ=&;ebsަY}JqΑ:MauCvүd$us^~ۭcs8rE^;?/ '?v| ~p_~G\Ǐi@IDAT8\/Ǡ!18n2׽қ.wɯ 'DQ_LJ}s~56֡.C=/Wu=/sU/i~Ǟ7;|S >_y`Hj _}?}O r˅퍄[8%>$7?8Rudo`Gړ>zs%UjǦ^?1^7[1n- kӷ>k bz^[lUX\rBs0g1?ҚU듃gn|ݜՏ+\WShX/oljoYWpߵ7/;>;o^WEw6\|ps Gn ñ|bqeÿm?yፙ޴ӓ=#w5}kx%6БC]J뗞hÌc=gÍᢋ/&`=rϳǠ_?y=\uK^Kzp[/ynu[W ~_7\{W꫆>?l?҇8p [YmM~\9#F78vk2w ;a^O}䬺8$>+C}؋axy\; , 9Ա37Ɯ\En]$>щ'CgNb^Hl s0"ٯ΃?#s`[c.Α39'kxN^bGC{7%s1+ySYm|+'uOQ5s͐s&6א~$~&?cHUѹ6pdT^3\t0x{1F;̳g7O k  Jy׃ C8`#1̉s _~B_tIY0/~G?Q'fj}7|w}xqw=Z^G`ıGˍE; F^4Ɯ+^>ݺH}_k뷇Ϝ 6H{6-t)}]kĻn#dO]Gb?M&sF C 0ձ37Ɯ\Em%ͺē[13x{1F;̳Y[ևk݁?v/|N&j4l6f@ϖ!8_WZoس.!Ab{s:9nNm){sbU^[f1=f_nX6:Ilzlnr l􏶿ipcx{}/O>5◆SoZۓ{'ڗWF=w''ߔ'wÍw=WG? h.9෿}᮫ wm7>tõ=>h'Mo\O1o{pON<5~'\7<X>[AgOЈސhYIW9Ewps zb8_8gjٓ.=|^pÿMGH Ey.]s5zc_s\6#i}#?ibI y+oO3>8urٯmj14vq`^01|V9sLZ3~uxp5S>XJk<:x\Ka?'kʵƺag\ONwpRsN[v4+> S'$oTzʵ>/O}d_]g`7nv7͛8#=^;CoǏ9qSc^)쁰B~fiSc[^qtj>ƞď~nl rsrpƍ~8'χ_nXtO'>׶s{3>4Z>=}?2}=8`"'.|cߘ:{c'lxM7p'Zϵ>l?z=Z;=!7yxr8bn><ڨ㛆;_4{B oqnoJb77p nnzuå03Wb`%X*e,\/pnm|}Nla >jrȑ}!<̩<R'p$Xbc84,@޹5k`{8'3ؚ9qPq'76`󖷼e{79'Ϻ׽n fjyZ4':3֮u*amߺr ޼.S׀ᵨz ~kw=f>9gluþ'"]ՙ4yB7YC3xquEa ۥ2ȵlCslüup|O٫ ~}#`W91UU5vbH73<ᶣ O <ؾg7-Jf7ك>h'Xr|׿í/{Xް7|6h>>^yݱoK$ũ*?˛HP4Ʀ[߳]mwUqt8yc?Ҟn<ݢxCϽjxy9?u؟/Ü^se73=O b#`e:Em/Բ?𭭴Gf߯}ǯ8$_>`_9}UA.8 qipڕO,~yZģ@o*F5RuN.qgogΨdn^^#Za|HC{9g}-pa^_ϫsM A,~{l?kŵbmpN8\O3\bs%N_WgΚ ,s󘓹7$Hu\^o>ox>?a3q-fN㘫#9F}.ƛZ<b8p}YstB2ѯ8tSsiC:Eg<^K0E<:8zNo*F5RuɅN.qȇΰ>stF%5's>j.|2s0C'rX>kssjz^gkX b 6`֊k`pXsu-ag>2p/KϜ5X1'sփk\z#Esb]H6stCs3>ѵ+O,~yM˽ |R>kgX \ެQs)6F95M7 zW]<;ϫوFۦN̪w.N_`lSěC.V|o6ۚcOlz`npp9? _tLJAő+hS OVG9na鍟8tp'7=2|{3{Ǜހ$7-oVb>}{+?=\{%H'O=|W'[?-Q=zB%ǵ_2\WܷұqHz~~o8tǎm8~PMCmz@?~C'K_2zs |Ɉ'orm=Àw!;Syrgn9`NX+xSt8zyȉcN~j3XC>8Ÿz3荃9Rs029/qrf 9qz?><_bnOWOks`^@>$.aj~ S|$'cSc8O >xvmcO\sfOp''kH}~b;78r˻Z^ScgyߋOty {MX"13U5=i{= 9R{29/qNYg=zSƁ}ƚf`=u Ƶ 9=q^oXk=gE/13{E37!)?s29/qrf.s'"_ǰK: ;j|qMskd`#y0 {@ Bs[' 7g7st5b;G㪯 a݊UڃEyA̛W2;ڨ^'|n(lxn4Fc{& }np}{ Hhܺ帨(͟hvi?enccCO O?B}hu'[7*yc ˛~艣>wpdž˯:6=mp=uív?john ~m,7˦hc%n~go>vzsսas|_>\qŜE~Q=첵qS횴 #?>|Q^8; 8YWA8rWYrmĀ6OyO=#I^m#h;9n Y>g͚OjP\5\=1y^3P|bN.27806| K m5F?>ZO>硍< j+'x|Z=o$?,`G*k+ e?o<9s[8V΍U0Y[qݺǨEDLZ;6N`dp{8>xk^3F|S\y^aWǿД~ ??=>\O<[ց$ٮQRӟ?0X~yo냣?3?3mlIu=Z+k ^u!<9D[k[m`c0nMk7V8j>kW^o>o}_گk5Vb|W:tb[d%cp{1]n~3?o2v"̣^{8rj1Sslrl}s Ӧ"_myceFǨқ&7Ln~yg>3\~x{Hq}~QKwڧ#v{n}`rA7>ܰ[gO[THZ]+ԳҋvoO=ƣ ]zpl[=Li/+*ÁT)ηK9 K̩vr ץG_lD9́/>+[.=h=~CXp\klzsΨ~r{52@gx>޹ z'5q {bSh75?zq`s N,vAIn9`=W%|N?׼N=򙛾n|_ k=_kb=bC];ٓ8u%.{=k??õ`k͵;b]jsqooC=4~yx4- // oۇkW_Ѓk|k6]^W __?EJR~//Xo',~7sȏp׏/~ïꯎ$O`S!u׾v[n7ppd}spm+랖s9 6rV,ܗ˼%޽pnkP;9|)UVμy6#Fg;nխg]y305N,v99AInz^npܞι~^l $:56A= 9+K|5?G<;zc#N:zS{={C,>{/d=e@". "טuuqWr;|_n^ڽІbg]SxG ZKuA̷ޜ `zp__d9go؜gȼ l}eBڢ˼?[d}ծTש<{dT93V-տnCpxBr~o^7Ⱦ#ڃ)_.j?=1 pYml5}ގka8d\|79Go þƧ.;vmÑPp{@0MБE}>;TnZϜx8ޗEwִ*=nB7rGln/Q/hFG3qL e&2fpLPSSk"TNS2!Ш\<ۿ}ݽiӵY빯g}}gXs1Nm- .j&jW+D<"?3fţaF/n7_[śhذ`'Fㆆ9~] O;ۅ`C}h| 1HA;ǢoBN!O.K[ƈ|Pt7O2:ƃ,th6?<;xc|+d+f]z%担5J<+_|;ʥ^/M??44nωk#|񐂇&Ʊp²x'k7#9i3GT֮][xxF㟇}=O,Y/lTʸFNm'of|(W\qE+Onsuv|x>mrixȩuԭ뿮s,;0\sae4/r] AAk^woz?zM; K?.|ͦ=GoC?^i{A:Lm舎'_u>&JSmn dgŞ|9-n]ܖ-'mtez'VO;#r;/9 (_H͎Á3M `9 .N(VIh_Lxc`ƌxA (&FMЍM7Ι?0; uQVŒ(VuV{0Ê}x17.#ށ=qx0gNwb.?njx|%e6mr:lc$-,ˇV({}x>ЉA1;J<I,t 7H0! l_A>ɲА52o@{Ե |Ќ^+m:tʁ_o.@8ty9Ī2?Ca?&< l9ߵhn9t!tt 6OɃSYl?tg]m9)k=(:6m'>/]es"cdƆ]GZ]ݜ1F:Or8w=\C VqA1 OKlpzmhbzZĖkmFs Y6R}*M=>-=e~Sdžm<}2е,6qd,rذ@ : [7F_M}`峟loܼۛB`O6v/&vx;<>C/$G>Ǝs?_ tO|oݺA'xy׾>?7R?~چ}D6*-r yx64}t B=D}}뿛S @.\n~S/tsMs"cdC@XGZ]g .XFm7_SԆtۖmYWs[6ݶ8fZkUWӿ Dч׋/Mi]ye?efx(zxq8NC `|Kq<̉7xx y( ku)\:{w7> ďK<t M9C 86Ɗ,1.xMvo0>Gl~{q-?m62ӯ>S|m=;^pF@>S֮8t4b&Ŷ)&rc @N:mڄG/-@G`1 ǂ2c,:0~(|O䴫nBa= `O[rk>SGNb(Ɩu :eٲex}a<7nXx9xt Sh} uPl+O m"G-016a Z]C5usI1&wrmr͜0 #cS#/1rc˺ Q_@4t}tQ GA@1|l)chA: s=tEƇn=L^Ͼ¾@/ឪLnk}{Cc:6)M6v/'v9鴳Mx؂t9lj = 9bp/6VڀyEBu@\_?|#O:{P:Ӧ'ӏzC3,^Xُ>ԕg{Z;dmk[ܦ<66|edܕdz'faA7 ,o܈Xxg^voya%~;I E*n +<5 |UZyCɏolįC;pF|Q̏mF,]pJ0TuĀ*xh1w2oּHc|Mp|a).;ˮf'wBpG<؈/Sb2fdԑ+S)O[0mtQ[a+PMxٞv!UYbB,4a.FNz/hxМ#ʁ1>eC?:|8@?p#/Ay˹Ï}яte̴)Ƕ?6 &7ƣ=3k:u}SWV;| 4vӈE>]c@v]ut׿ҵC;UJn&7ե|n?~wgG?j?4|P ؏Uq&3,l'&m7<C#rqmyڑ\{i8:v3X7,.F] 4^;/ hyMpQ<5m!G!8?Wyu߹¼3wD@QI &0 '>2Xl# oGox@]evFOブ6IL}ɮ qmLԉ đ_; ص =>Mi6~}lذh{#-}D  <0GHw^Cۺk6zPh;/lR.5 (Mx#S/֍m]@Y6@CL\C~+?iC?EV=:.'2}mq&E 36a ]sP>Qx^?t@CAdzѻ+mqy|ȴA-3mvd#Q&k*=mʴ\o{@E׆<22n>`>sXPXYnaQ# 9 } `K4C<[~d̏).KYtWLJSp4z;;RoD,7݀ؐ˳ ߈ О+j[ʪxH?fYkp1^ltwE^\|ML|B8{ DfIv_͊}Eu?P g302Јln`C}>"oh+OlАi1.}+Hg;Ƃ,PM`~؀&`@MNe?4btc]͜ k+:Q#}@Ogg/O=œ[ {RȺ/;6oY lmokxCQQ䁭k b"NIߧPd}rB?Ǘc/r7i) s;wM?s Rȥy$9rcرE^Б]z`NX+;sz]{,a_ysjiFӼ~!Mz'O<5?Ur;u׮sp:>%[%tY:X:۴iYP>{ҝo\ ulX~J|q)צg~/63ݿN^@scp-,=rEy_4'~ix"Ž \J/k(ax(Й-޲2o1lkwE6Aʇhm,) Sm幗cW ~>9qvf:+^[ 3+H -dtv>l";K#FuS|6Ɔ1ХP&},2ؓ]y[>ɮhAd y%40ڂqs-M 8g~0ߘɻMA8hԍGɃMdŧ9+K{Rla_҆,m6t v%6ĈMhĊ1>OGoIgkrlK_]o>Q]G~w0yeƍ:k:z|ԅ*?OʏbŊ x|;_/ʀ?R枪\=s͛ REcklW8uOLgUwPk=?sy`oYLo`ۛ[^..*'_=xh#vmS?q%e@xÎK8[1ViN8nΞSy~Km9=8xaσ6fYX^ذ ohf|%{嬈a)^E<#DȢ3om~s~7'āo u  Y ~g>PїNV=dlƟeqKs|E6ڃ}2M>!>C|0n~m hc{uMЕ]Z#h!so#c.w?ɾ7:`N:`цgnmkƂvC=e}9E#_䔁OmKyxв-h㗛4dP@ Y4r%`Ƨ-qd>m|e~4Еsܤ12qrթ{3G( 0( 8gF:nڼq%46w@IDATQo6u~SOmt^qO?ݸG˚q' |c=k^󚦿YwY 96Ֆc-xʁ[[]s˶ swR~.ndz?2@qEy.š$Dhmbzn;ѕ'rX~販qȲٹq͎:0,sgw*}w`kB9!޼$fCY~{#6:v*/_xiKYyK9˒sm6Z|՛C%Fmg~e6oH̝]y\oW V"ٿڻl޲1|^qoS.+_R`<_ՙqa#m_f,f܄w-;^*=byW/wrkApLOLے6 LJ:|0SyƒkO_C}RԑDP?vAl \G|XCkG8I" l?o ٦<+>[_? ?0֟m}і0Wct\AKs;!G6(Xbꉱ ৮IVwp&`iLgRl9oy{12!ty׍.ZlݺL|_K}Gi~xu6&2b+|_m|{+^ziGw_>67xJ_c]wM9ZB,fxBاtQg /מ8"Otml(__ؗ1ֿu}'6u|sy_ݿ;Cc!\N]y-=@FӾvlHWWfӓ>D\f:=Α6d@&ۡnv:w?YqQ @A΋EhO{ʌ k1XKo0Ccg7twƦy ϕ_nz+-#AN?nֹ >q".vU="9|/1 ^Yn|>˞C/\}fcys|]I)ƁOKo}3 c.`@G;4͹yQ9>SWN"h?s@[7m/! dc-בU:ĉA}qOY0FSk[]hAcnlkWҦtc; c]5"䧮Zvxi ]uq6sm2J#q՟y~#2棞Kȩ"?u]|3>ԑ=YW1ף{ m9{m@z]^~gxyT_5`c+Lu AoDV Wá0ϴWu}:?`zA?z%3Z[v/2O|q[vhw֢E1\y!'F`3|0~zQY˶_\Ny;sl ~M\l3F|0Aq ̞_woyi۳3'twEl`Ξ͈ݻ[ ϕM/>6m;or47o &oėcD:2R~q+[.۷ߩ_v.] o]jʦϗ^x3W7d iQO;Oؗq 퓶A~6/2MB_u`/m6aG/4cQ:um?=!v?Q;>CPGdEk<9M`:d@C@P?v[M8'AӜ7+:֕]mQow;/U]̮n>sy\u>9hG:QWû5k?sMg/a<7([1?o~sC">O~O!=å{tiC+CYic{3M;4mA3uhg{wTp@L\6?Qz[ζXm]upymib8u'vd[ʝ{m666  !h[l_[,822'~0voj۞XTN8r?>]8"eLl`d>m痝/+_=,Z F||UdiЎseKˎCeIo*2oѲC2a/#q(G#K|o]_~Ceݯ-OX|rYơ}2mٱeu'__rӛOR 1Is\!k]|G u Cc|?;o!g] Z`smBsb~39(^psk |3V馛Z?R-\饗 /N8fbۘ6560y62`_<~-i`mc|~m۶ 3>dЁV< ` txQ?аO;zxڳߴPgB'vQ.<@eLl`dݿѧ6еo`bw\͡&Wq+rIq+C#+ƖG:m3|;D:+z_׿sq2WCسkwsh#C۽ s9 @{clVd ϛ Q M~6|iYVXdZ dh]2Y-g[ uԓ>F S"x'f;c%Muq'-e^N_qg(Kww,rl|;'oZ=|-!9[_!uמ^snYe >8`Ӄ`@SrAam7>lC_߲xً˼'Y|~9nY͇>? 7tQ]e>jeWl,/E|;hΞ_ .-KR8)"a4.chE㡭r=>ӖF]sБmxh_ЬK1>7vݦeo_ݬsrvm)MLƣ>yɹ 6ŖR~LbK{`M1ƠЁ8ѡ㑯q8?U&cX/ZرFiK#&uNJ8䋶9fkrڣ$_:{O䇼oHs]/m`^8s]/X\#ypAsnGG*A|3#__yuуu}.Јuk7e p-6yp} 4E3b;́>!oĨhJmp=Gs~|9&,D dC\4C0 ^m,mWG8ӵ'n'^F(g;v=^S.?"PEil3zt/z{6:m}iy`!EΆƈdsduE:+>rcMJCmA‡JXO,j64|sG:|)a#V<\z?byt|Ѧ6g|;?lQȝ]?611!.rO lFmeYǔ6v&mt/b5>2t3}n̗6c,ҨS?t̅ş@[i1V۱ӯzfl2:OO֡^ēc>ceā }ԉE#cMӶRdžkОivG[ԑ|j^~ Wc#s|G %73u$3c:cM9NrR_Z^C)tnsƚem]?\_|͍G>r6s{#U{k8q&8 S"xM":~r(< 6 }16h|1G{`4fbEhnhmzĠAB@]@7 XG> `?clNzmr/@B2u|`1w/]I]s0 u0/70m0ИG#E5k4skwG>5 >u@:>#Oabп Ot:s<|SE Y1u]Y@GPw!ӯ>6(z˱+ th}c:<,!6Qұ>4uK&u}UOYm+Ny՞'tD?6^#G^\AY]xįhb~ؗ]8' G\,YrF 27`Mڵ_bGBߤ>:|@1(Omlk k=66싱IGta>@B/ vԑE]lPW7M"PG;}ҦDУ`A dxc43?nj ڃ@6U  _m/ta(}uv}q!G{|t3{;Ā:&ԡEyG؀FW>maxslda:vsa[ƌmlj>g2O#̕{slv_aFyE" O=8c6]d??k~ ?ҝ}G"K?v!䉼\`VXko:z_ٻ( O.4-J\a^Y:!|r98Shru>by M`=)Ak[Ӯ&.^Le{)#d5ږ.r++VGʾj z4ڽhړ'uY.eY^9#ne::kںȨw=56I7F`iۛ46[7vhnl5q#r6PЃ.>`5>⠮/u67yJ]0svbcC>Z< _]0|>!40| &MLMi:=EO#o>< `Eѱ?a`a;؃Nsb ز>|hñÖi=yp>{: cp?zhla=Q-S'sLc K/WGF;Zzɲ6c{lԟzmw}_IAHA1 ͆-d)݌hg]ِ,F1G^ԉIl|/_bW؄K9|m#&|ni >m_#]CҨ3?=1 F]yP;ԱmlA:u}C^>cR`_@Ou~!vhGutoGdti&6C_ꢇ~.nސzPy\h!K>V]s0o?0)iAמwA:u}|02zo53͜aCyļ\=!Gi3܃A{䳘6d)[n.ya1gyl*y/خ^3_\'/R~g\]byuy5,c=ce3z?re3~vvt`l^~c7)<\t]u(M_[zmiG^qg>rҴ~klԃ~g.nldlh6\1z\$Qhc_yᩪ- m8qw-?Cg?|/vӶNămNcM6ԓoa!1ԑh=5е Ģ-xs4`N'mƧy2Ͻ>[{[Uz3/<יG #3^y >mG\?A˶S`툱 Цh 7^y@k<PGu33us-gsy܂<h3]Oz_y/qPw8?|:k4M-Bi嬋 :36 :|@䋳\/ׯ ]hBϴ^r2ldIջWCR9cvՖiV۹4mdhnKgu??4 `b¹ 4t|\ֵa{#_/aXrgАf/!f̶'+/#K0Fm )+eab0l~7z`leP,}oAC0_tdrM贉:zevl3e#UЩ qL6 vIʃ'}ay |8_tcܶcߝ'ٱq͝4Ш3ԑd{PA M뿛 sIN\+`r1#eAu =4bhBe>hCOs lbhBe C3ܑ7sN G{ceG6CkmOWu;PLyPG9ئ -ۧ_m_?ϼܻ9h̙ܶ 幋.F,HB.,/s= M.ҵۥvԡ+&BӞ:]QlOX۶L?_{aKm_|6g6X>Ffa .ōE /o<+-F]_m:6j/:9D;ذFO;\:k}9}G`Gبv!K=BMB:6?َmld,\> _F;e}1,y"]`3 {d6(؇&}UmҞ{f\w> F@G{sզc}0Fk@ur6=?ͱ%w10w(F߼c~c 4ӭ;h+iQxc[6hS}Y.Jz2::e\|t~rlv5z6~ ~=j.e-~4udi^tu~-S/_kjjjjjjjjjjjjjjjjjj~3=WrC z֕4xB?:|k+cvIe3-p6Zo1A&WG\?0< ~xKO]xjjjjjjjjjjjjjjjjjjj~]3TGIdym t]x2UT$=06>4^2be^m,ׯ?u7W3P3P3P3P3P3P3P3P3P3P3P3P3P3P3P3P3P3P3p3O6_hѢ櫤_)WCS&?Ӥ*v᫦qx<{ , Ym>r{a3h/,c]l Mk}U# LzL6bx6=LDV}:m;{nS~q2wh E8U3P3P3p344/G =X{ʃm8:T@[*Q3P3P3P3P3P3P3pe}Զ=mbdƫ#k}:r_@vӆ팵qަlf?דjg&@/ׯ]O{rmҕ>A[ꩲew^N[2oޭϗ6ni,K.-Kʏ#_++>D5kr9zzrXَrɥ字|c/xdm놧ƝLYeqwc=`y?-?{e˖⋥ޏ~%8|-X[ۃk?y_c_ |[{>ra(OݡZT/;q6ZESwf˝- S|X%?i:浱qJ:4yN+Gfm;ÒG|}kkxl.q4+eW5555555?/m"[7ޮ+m@%צi_K^++1ۭ)LclG84K\ns`um3Z;d#ßhDzmWkXuegr/:L7i|Ӗׯe(Xm(\sy[׾wukŻ/_G5 k6Ͻ=rѱv}h.|3+q]֯З˚Mq1ǜ?~*l\tl;Rl0ckL됼L<up~sSK\^V&2׮Y.ر@\?~\{0kjjjjjjj&S2@~Хt/O41tbe}jŪ/7|=V]nzySn~,1W*W^qr]mx"w}hju:k˪1ٹ̍q*ll-Ӈ_jz Dr>Yu`]]=~17ÿ-{:w~]=r*UfCu䭕c_/|8kpf-4c|V4O/n-A<9uzs}vA;؍ak9,{ĸ:{}սurkSҡ ?2;I4::~[+M|gtӧn&c/kks׍)IRu@{_Ϝi{ɷf^uj1M~/6mmKLom׮k=8 u+"nmrUהk|k0'r5!:~ a|*[f̓q-eU_n{A}` =_5W\VVeeVИ2;}daC.2{,llp eC'N{qto֧_.r?n 8\Wb'ʷore+:su\su}=:"J El{?/Wz\|q1ok91}PkIjػV毿]ڗ":׻-{K*EugB\7.]Smܪᚁc3{mv/ށB[vmu{wNLeN pW\ m\"a>SM9.ʂŸ>/Jeqԏm[('Dyp91IQNrp950)gDymE9sQG9;ʹQ.0oDycxe7EysF(orpyGKs+𻣼'ʥQVEyp(wӡlܶe{~վsMY{o\[vΕG&wlY{^tqݬ\?s=M~sͷunm7^l6xdW޶ cu;Gtl\s['r୾+Ttn^ޕ5unN9I~kuP8շ?:wbRkb4[׏G_ʉ9~uݪ/P-s֤5v흵w1u=z%鬞:2uwwV} շUZyvӁz[0bV݂nj^cLh];GsKC9S"=9b˦owv>;yx7wz`_\娝ќ]ylY3NVYyunѻw|;NSGsDkvmsnt:wReE|19139kǵ9טnx`\Nzs-ts}*֍ۯ]quR:7xoS f@@@@@@ORbbNs)7ݩ\ "a}pP\0\0q(>h?`QLs09G~p1Yo‚8 u(\Q~;?e=9!՝t)?T[1Ys~h\$7P#W:+sMa8Ӆ 8^ p°+6#g'kkH' ȵ>5H:"AR֋^{cI 8D:^ectmdu%[ɖf{ggoﭷz}K'LIQ:JClHauJn]/O)RJ G{ }5aY645*Zʉb-m(nb*YbދvSi )*CFAb+x*LژOWhlq3S)L M^SϨ R\^Ьt(UzŕPMvjkb4*F?T1uM5[cx.6YuuTB=4Ra֯͑1;?z>3zqRÌJ1.tO' %|ef{q|UJs;[cOT>hSz@ЦXbs.@%h,bcVT* -uL 1߼G(\xT +/45:ؘkHؔEv4fo[]C~S2bѬIaH:`yXtkވ_OtgXioiPy˪?m'(I!@F`.ΔNRNV^ޕ`BejΖnr  :a:b+y?cqp!B̍멹B./.‰"Na4Ҫg7\d0@-Iт/dD0Z@6ZI' 89ˆ >rcO<>W0'#|r'+dObn9Ovn ~A|yh!mge2/4~<61{Fq1~Iz[('=Gu u5,bjo釰)Yb*#?$ٛ ^߶SꤧP;BS*E]1ʛ[ cJ^A[Un#}KέW~=-;23p1lkLa௻ G2pviX,*oM1njr| R)+Wt>vHO oFy]-]{a[қp:xoHZb%^ͅT:Ow*f6,Ji^v+۸n0稣UO's}D>n{>w'`~(:D9/m#7rE. Wļ7(J|+[FIqQzS޻ n]#Q>C3ʛM(x^y3z3d'5\$[ߺ$ ox7ym5^Uw:QBm\[ !@!0יr)סr]*שrݪг~Ź`er]0\p.rr]/r/s] s1s1srs27Z=s0\ a a0/7.Np7Pt>MB~KŔ.xC3N'rʭʌ4oϘgLۡ<-_#H z#?un.M` ݿ" T>\ؑI1Be9>U`K. ?5%GVɊt> 9|وll;P#87x##簷@ꕆ;>LySb8/Ff\NZo:=S~Tc0066Kmmg"ehH;?@l]mxc$'E#7bիc_[CPXZ= [Mv{kpl}hEnuBEK/oYP!Lc^i/}K]8V=ߛIFd˗Gܻ9\\V׍PLa|dzld ǔb? $kp٥lB7EXK&Tyw@<+kpfHM !IZ 7 H٘8m;P6]U*M7(0\OEP\{;:&.?dfۣQg&aןZq5^_W*r~AީN؉@M=wY˔S%4m1H?.B}L\7 aهqMh=*,!25)i)ʄE{}ʿ4O3MY$:nݼK'de~,|1&P@@X&Ԥx! "*Lꘆ{gdh6>&&۩VHNLmy3]@w(}pŧ ƹ23a\ךp|5v ?Ֆ<(m*?Fukq4EY!@!@zSDHqViA#x}崨#B#:Yhόnp1Ysf5_>ߪJ↓JC'oVg _mm| NÉ>*rUƓms؅QtX(*P>; / s$ mZF&2cfffp ŵqϮfpDI-U8s-.2E~s1mn1T q~zvw4#&c.gg5Ə~ރ_NSф蔪V"/]{wL*&5b͚voؒ9mU i/u K\b<2뜷⢻#oSZ[A=`X꧍kPz3zvcڙ2[;5zoi]>BWv";>ng8Sm;cf[hB~P~ϲW7c=a"cF LLZwp}ׯm-݄p7b&6@Y!@!@uUqy"_qN&8 ӢX,m'e^rUe'ȳ+ޥ'TRy/.$f/[^F>fոdN߰ںի .-'({Tka<Z;VPm4~$r *o53q$%ͽ3]l܉* vШZl|$-v0 YMض_]m릨 B BB*trZqNrYZ;AME2}.5xYs3M4*decXne7i#/QטL ͶA}/a|V_.{+w4HwEn*^Cfra`I(ZyoH+oAe4k&vpo-gOF=pVo"Gߴ2oZ3qyvz=T6(M($MCacV_yNU"?XKJGJÒ\3'xMozD\\W mȅ;.K>1r7=lYHkMϔ\OE'$KϾY7e$m/;,U:cD|3rt/ŁؾR&|idsԗzqg+^h!\x8V] x?B##׫xo5g0[gBfv B B BgC|gE:2ގ\njF.ܙEK7+NV.ӚS!_Gy3pHPIsZ3z|+:ѾY^&qrc]܆OmZْǫq(l=HH؋r{GjD24]^/:Ǎ2;W ӛn<k|{]7lݶ,֋4_Z"5p}@$C/M6+}pԐض=OWjUâi33q}LWoi6-*x˞cG{n & &B B`"u EZf"v4>Ίt*ߊ֊^'x6dg5p<_xc:VtLkF,ϬgAzgOA( YGIu?&bAg?̲MZLALz֩CF/r>uZ2SP}DndE='R{&͟\lƺqdZBּ9اQ-8.dmC`1I֍;}ǵ'v5_*QIRu~Wm)R =UaOyIQDZ!{^zhH~z=cuؙu"4&NJte|]fӘnCLgV ^sCjZ@WkNx;BO4.cxdVg 6/zbRFÞ4Gdvdԅb%'od s6=w{!vd+TOTOuPBHϩ#&ZK/6 ?B B f/\w*Osg̷'GHI$KXLFÅ\;fX확Y".B!;O[".EZ$Z}ؓCz~5ʏԅ(+}੸Oĝh cPz=}}f98Hs9M1vJ\Oun.5EYߧ./_sBj(vkDlNIv;[հ/lggTWۛb5Ո@{2Q?]S,^8X{1*cpN-0_$xZөڧ6]wc_Yʬ|I h\1}ANMŃjBƝ~答f*LŰ~dG׍s'ql3֝?|]&p%V_M8Ȍ+ FTgR*Z[t]83h=sMBE|]q{z3|ԣCvbXR^Mlf"][mnbl[c߮ 2j(vx'D&Ҿ>' }$*Zԧr#INfRD] ۥ #j!UтV/&>BûK{Y60_x8ဤG%y3鍭\9+7.*{z-J$wJ +x Z5YZ/bSnn4||F"0[-'*r%&<|ML_S3欒pOG.{2?+; Sٵ``d\}CU#N$v>f< B*D)ݴfZN=8>(+ l7ٽ(y &B [r` 6zY-P/3T]AC_.dX[y[r6J e"kǰr K:&'2|‚\=~H+Ƃ[rr|?K Z񳧍k^i}Jead!rVQWM6"8P; E y/*H5ZIJ1wMtGu{˶Dz ַԎDX?Qr 7HArS.Ak2) B B ta) m􂏜'fefy{qvۥ"ˤ՝(Fc0,QZPi8/"ߘgLz`#DZiOӢXsϝL'/ACAi/ȡ饗"ʜیv#89 ;cքQ&ܢ/#}['/XE cͳK8/H{V!wR4q.;b+BwX =\pV qvB%06ԇ_e6ƃހ7 5Xv ,dS-6g#x|u~=غ曱4gA95n5W!L`o#cAXt12m\pCO׫x܌Ȝi &FؑW;l²9`",Y!\w#}՝7/[1` IX41EX"mq>"!2 cIj10cp|v'Κ`ݏ\|M_a5Zc]%%?dw`bSA7S2q#B BoΝ6W ϙG>H(rΘzl> H\XrHTzϝ(DZFEEl!ƼA@U O% B B f!g(vW)8}J!@!@\)Q/rhF# HDe9mQ&B;4vօp!)v륂HG$*}ix>aW|l\=%B B  Ìɻ/m@;6oDlK!@!0:T2:3De&2f"nNЋm=Q'<4$gU&ˡLS^z8<5K!@!0dx/hJۤB B Йr0̌r9sgEgE=&*缸K'JKKp}[ [y}<LT.x(F>̪\thE B B B B BpWi0,6Q\7AnW9 6E@;HNVrhx'q3r?zG!@!@!@!@F0e k"͆ OF7#OD#8u($B B B B B"DjVq3?2UvpJ:isROCΟkpe"_]3v:7m!@!@!@!@!p"D71urDtrԜnFFz(ʽ}víXr}zz0<1[9DÐ5}Ki(zFy+΅Iحj%߲cO$B@7f|K$5_6X/cz;{"vJ:8t[݃Pf (yxɧPe$=|?0RT˜=E7k{RT,0[~ N\O݈:ok8!@C@L9v L+t|ӎevѸ׬ɛI߸9URզ%yv#hE}9-M3>rU!P\*0vSS}kR1E B` +y+rj,:oZ`\dF[GU,?FF  zFcF ؘ/#h|Z6ZqE gqR'BuxYԟ˒6n0)bMZ)u.̻] k*|Sq!@!00ӿyvVy ']n{t9l@6'q7҉rhqhv -=v:P܀Ñèi cӳx9!".}1}ڼyx췰nh'"&R?*jgk[*3'")2݉b˳/Etchu#պH[gLJQӏ%ɤ+ֈzA@Kq.ivWf6rW-'݋t2om!])^ok8C)]i2Q}CgUIe3s=}[*q$^!@@:UDn߄kĸ/~zSDi?4Lb<-A}h2 rbS8E=M5Bye&ãXd7/M=I^4e0Sf xGemfs{MⷛM#B f2)ڵkQX;JJPƼqR*fO6ٙ}<.f.:)c#-U:9-=tQ][$ Ǻp%̗9hy"򾶾}ܼ%j|VG:#|}%^k~*7j/wFDcʑȵϯ6&kk=eXXsw@-6B#=ޥI K |g_xs i*-@nuUf:Q({{^v7 7Z+ Bbd i6a45sTHQ<bPEȍ"(K˞?o!{s3Xkڼy,Gg>;p7;$b~ kcS  en7292y/0"2sf~ [߿K ^f=-~+oVƽ2+j9JڀFN~Z ԅv--/-)+ڕ6xESbǢ2ԅ^*Ugu@[TiiTE#JCOoKzZNa\RrÝ՝396ܩ0K/SRO޶=yPiib܋V+Wٵu?'5"ڵaFǂJg&[<6Ӊ]k=čTTukDL.4-@nupW9= F}Փ>8!@!ݭ7QVNy#իؘ?TǕ Ȟs:Ns>А288эr(y7:TK:U[:Vk:W{:X:Y:Z:[\u\u\u\u\,\Gu\gu\u\u\u\|.\-\Gu7+3Y=י|>5xY'd=:}aigϻ!%L 5vA#$@(2t0\!,>hb`NOHb'_<̓e]S?MeZѫoG0dFRÅl䐯+KRj8 ;ZLc7W@Ȋ"][OqyRU2QD$(;J{KR,)9jI (fņ>+fp(W~("-cliԔ[f 3ZZ VNnVj`}p1n"W^(R\\|)kCP^:ai%nWdV4իȸ2xf]W0 J3TW1\9mTvV`c#ƪhҚP49/։ SmحjhV:[b]WZ#<>fG:׊da|Å~Kח|=J>SMX \[# sVQok|_p(%|__|=(nbmuʣ8lcbKnOs&wHs&ٽC#n**v[i9;KZd{hP B`v"0 l/:Sn,r/ˆ #2\px2Åx!ptj;WIz#0OT<}>} #FG*;|Jd5j|z#[CR01nٓMw %<}xb8IF٫<~N09^J7+v}\iֽ)`m+ly?/u*bO^Tjkalc!5a.z*2(LN͚i8fbؙoB/avoDX5EKORﲻo^E:*YKfo7;vd3Z*5WH:wn\iЌ>ݮ]宅=Å~30(* H[-U|:!fN֋uQ`5*NPU5mVMkzk\3n/|;okDWڠÒlVum >%,$3eOQZd^纫5B힖&$V4 ZB۸̽)MXzQmgLJA!0 \uF e~ \΍ fo]H+b9I"$?eB6m<e±-+Q5A.7-# 4ٲGRWY7cP@IDATȇys6Md9?tm.pȇHy"۳Ο?eA<3zwܖEEBo~Ɏ^ܣW?01?KWƞMj:aQ`Gz<'nzjõ( 8:42" cm ]/1gc1g?S 2Hx ڍJϵL;CʏB{>*CC];$M26Њݪh-k"O'm٧ky{}R'\Y3CjR]aw[,ɮ@4Gع^]fi(,-W׏GTqkYv]0B>e"nώh_IaV'aHĉ!mBcccbCQ3̀;k a[:6XxɪǾdy/Ί9"9$QTK_aқKl̍]OF[&.m mV,>}GcPڗԨ2IA畟j\ ()L{: J.RXIȘE ś&$OE ԡP3ΩL'ZF>}똥~u/ *s;ռo<9RdhrW2M<2oS mL>P^EU*Ƶef!ZG C?"fp~-3j|ڷJV}SSF[ ڍ}}pP~{P)jy}k(TVvWa;Ú1LRK!I΢lvx#U/y*c ذpx 'g\wFDLٞqy-Vɺ7ޤ<wÜ24utaB ЯʡQDeFJ ig͆ ?.>4\O!UnF񓲭ؾ}{bs;Y 3k XF MuK攆Qb/>VykGY_eϋ mK:Ej*yF3$?84빉IO-d _0q-L|X&Y!@7_ m jY9}1D)M[{薕;}?ARl3N J(K9K76$j5"Rٛfs Q ޤo&_F-+yzKL0Y.~ˑ?>uk ^V 6Tѓ+FA H4qñ_X]*}*"N'y7uFhrp۲OyAuJ,˚}mFpSMZNuEZ^~s^ѽ;a2|}3ihxQ B&49=NӬUFk|bC}a6h7cS);~_/>.Ж,AZFH|m(|k"dsEbHokb[oQ\ }SM.Oj"ؕۇM,KS9&Zt ,Uj5ȋ盩<63ݏY/ŁQk7§=vmS_G uGx4,ɵR /:0ܢD(Zֹ l㰷@nKq급\!PSAf/d~ڥ~ rJqaRR>_#l[iY\Ϗ"tv]81*o^ هC<_#=Mi= g=17q9#U Kj‰Kزb&$ E B iF@rpZ;t6X.kiɒK)˼l{nם=s$I.D#} O&郕,ٵT> V ŕ/,_;H0yM7n9m2CC'}H9z<* 1DhECc_">\X~=֬Ȟ䶈=!n8swlf3%נт5o B/>+c P.0}x;LsKsDb T-6;CV‘s:jn֮b犴,>a}4FHE& 9ywݭrl;P S;4>Nu _L<Zq&L#VR>Bt Aә%V@*0,Tc'}Ys,|b +fm,ەt f'q'Iԗ'g,==.\=*:Dh,[uH,:Y+B/wBnnإ72 ~-s/B;61sE˒;U&NQ0gKocNP7ӎD| MH3FNt>u۫Y 0y|Kބ%E*l}pK׳/%lr-9(S׆zlx63'ţYyd䞉yO-/ '~3V|6Q[dj0ҹ\JMYG1ɗx?M!bʢݧ~rۗf)-qC:U{(10IJަ=Rv}r 7?OH6 !++ ps(ɚrB B@ r(ʌSNO%dp E51y-#قĕi'j״Ilغ{{Vͦ)$& 4a1V4kDcc#|TIzgyM76NR30o:@mMD =1S U. Eyq9 ` 67a8(c5J: |N6ar.DF 1Oi?jw⫬3pyVUD]b߼y^pc펾{aY wñՃ! %ERX(1|=-*Qt!@F[ez#/9-<{A@Qx}10rȍ"(K˞=c ,.tudJPNWD&P9W0cSazTY<_ѬeYau`rN-3(NP~%ޢQz{Zo%{D~˱QN ?s17)c}xu6(Wm񔥲9*ҵj6v~&oe~4h^e6lu0!132q_k$2I_i1g8!V( y^9"xEn"U\`vNi]SLyX[]fd3e?Msz) #tT>f]@~2>>LLL{Eqz^nGos)םr*ץr*׭r+׵r+׽r,r,r-r-r:]:^:_:` :a:b+:c;:dK:e[:fk:g{c>yioy:m^\-g 8ו,]8\>W7\Gfei:clVz9pf/pՋ=96C@ s 7rЭeuYϗgdK1ߊ,{=zO<=>;KJ{]^`&SGM%DxZ&t\iAq썙>VaKU/XV<>7Fj#ziv߃X96usWP]ו[+ʷ{ .-fٰļ݂jG8KkpqElK|ڌe~4ɛOԆW/^e6T1\G܎59+?(rD&Mn 1j8}Wכh5Bŭ삅\{a-s<nXnLTuzX[63ɹNwG5߾ 2リ+ޏ{O8;~u}"_#ҡݦ[#{M*0޹ۨ(?~;$,D':Jyn˞^q˸u( ȘޗPcX|e"+(nbU(N!038S8CSʘMsëclvec\0QX&N^y"-B9_PyrFN[W3k2(**7֗pcDJS@3sXh rZ{kob|_} nƪ\h}55W{L֛I;U n.[e?'Fs-s*Ӟc>K`qM&Fر\W;|,XK8ꋱo(~gcaGBK 6f18ݼ|rԴb|j?,{zKnKsivP~_[gǸL#89 ;Cj(Ycs=t#C-n>^|mcUޑ5"{{ Q}Cl~5͘Uyɴ9c&pşmVcbSS.RB ID7ܹs#+jcy<|D{3#gރc/WLE=BWȫ0XhBc%8/rhiUZfiLE34qa#ڲ"j8 :c,_Per(O29B B B`#w  0~!@!@\K1\p; ~mpxa丙=uM7gdn$g$Eu9dp쫐BB B B Zhno_%F!@@J nx뭷gᆉ>[`ҥ\Nk2\\cK=#B B f)c8:>K} GCa/Nl[aEB B Vӂ-:Nя~f52\\L}$B B fBmTJ $B B-oZ-:^'333͋|BiD?bB B B Ć |޲X6|*S9!@!@ D@m1<<SQXD? / B BCg:*u B  n|pxݜթ,B#HV B B B B B`#pUҋ ׌G 3~H@B B B B B f7|ru&՛p1=S!@!@!@!@!@!`.L@,B B B B B Bp1=S!@!@!@!@!@!`.L@,B B B B B Bp1=S!@!@!@!@!@!`@Ie.|X3rOakƫ]=zCpaeθVz9Sc6"4#:zzv܈/^EbYN5LDB B B Bi%mt-L *bqT-Om]dmB3fnXC}lj(Y9y5v[C0M^?b':Pæm:q~Oi9s? M g_}Z wE<_1L\gP]َ:vm=6:`~>܊y1U'B B B B XhS I\hJH'xf]O'p ɯ\7s\˜6^fȏ:YTd7/&I;t[5X4%[vjw,.a!S߆PQT.aC+ԒGaC2 7/Oqذ;j>^!@!@!@!0b1N-@[}ҋqKc]pިWތ'/Vl3#{ihĉx3LM`2Οyn4f14@GVbA{?kd:J:ը3ZNoC ry*pۼԶKB B B B}\{śEΡu8 =.쌵D݁tD{ <;>Ҟ~rFxUGdFB!0p[zTŰ{z`%b= ?Z856S{P[W=tFF&@R(B!@!@!@! xNgcol >s2 Aą͑h\^VT`2Ѧ{OJd$삌H9(P1u54A# Ij h߈F[䚠Z/F#OC u-%džB!g KDE,j&#3+=gfea.ڜ}߾3s ٳXiEյ;E^oʙW{ZVZ Y866.]nᶮ:Zΐ8&k(L 0&`L 0&P+.cc>.sqmU/E*._vT,%,vjl ʇ [P[ a=58Q/[?{`eRYڼyQn *$!dxIWbS[p mDnV[pEx^yJ2or~Cs! gًp{#f8_^wPU$W>s#ta@/qȏ")ɕ$5ǩoƀ>2:.Jo'%&ҘS?Cp7}yR"]osj=H:na،46W9Va,s6%qƥ-jw\hl [`L 0&`L 0MXj4)͡0eot^]~M)ZTu_OS[K ]X`%#yTaAr_õiW}|EfTcB/+~|:q;#u3`#ÏpY粍tޤ\gnmO!78#xHGvfWx?e::%C΁Jj׵G<U:{tfa6uw^5, .硻/ |"{|zڬ8^ -=GWپZ˂|(]Nƞ? %Ĩ)o@)Sx2?<$ p،3xT#:mĈgLgAcE!/`GpL 0&`L 0&A@s{K'S0,**1ZP2BN"0բ ֕w j_a)ȍT}{˝qD[$Z"#LЗ*v\DZ0 {P(LSIQ9\m\r}Ge5[#-tCih2RB=z|!g(vQ)E JPQQ̄Iq쉠 jpRzT("h,ĩ}3(TE4Fm6`L 0&`L 0%E@ /lsf#&TlؑN)cL]H%e8}{WLwػ+Mu,ly\a|&gPZOGe%,عی/amߨD4&[jydۻjaI 5Q@i6Ǜ!ۭ. ?QET~aQ!'e VxKcX;:<nAstݚ P"2650Y#o#z+ )~N_SԄm:afmY@kI:$]'?}3j)L5[P3SpOV\x< BRC3A+vyU=K/t)':Zؙp_̎^ᑴpI?1BLJyW@On97(EP\D:.7ea`L 0&`L ,)Œ3WϴĬA3h)(kGd\ ΞƤc]ϡN2ZnCi!ܚnYc6$fǫ5Znj=K{:J Cp'%վCNM?%U|ˇ%+W֐huXOb3p88ށ&4t=G51Av!5>4GPĸ"(@E45HH_Cm뤶?F4 {*-9NANݧijh.TZ4pJ !B%ҬAi!3}_9 EuOనpJ+@,x/)!OBhA}Og-ɶQ+"짞Q(r.y1 *-}E/HvAIت27E`L 0&`L ,QIJKDy m}155CRDoRJbzrÉ`wN2٧3惞kKDǢ*~J쫽iw8;r\?nBf!號@—vgJm]^S,Tu .htJKJap_0&7CMbqemzyRZlCTnSn=@Rx|FrIؔa-K3&ʋr&+&S_rʳΒTH-6cie8{plUW 0&`L 0&X,bq'da;J%]K س+Fk+OQ=@KĸPM-ϔ)!R>Qv7ZCSTl~J9^/nus^7uUMϞ~ iQU`6['ˡ3 ަ ϰǦ O*Zkдk *x+5 )Yca&J/!\NW~1B/ Z$ت6j}1&`L 0&`/k' y:/0(do$uil=M%0VS `L 0&`L 0&K!J'bUN[uQhE`O0/ot& Z Y=C탊\WȊX7hz\o~j ӕ #P'EkP~b.݄k.㹂 ˳RjlzZ&g}!2(\ήǵwpFO;~=A|ZЩj0@lsav_yCP?mѻ,w? 6=s4cB\ 8tYw_8A>I}\D])ЂjtYtB<*RWf2g؅5JvgL 0&`L 0&h;,}+-7{nkBjZ\-|yNiZBBbqxO "/+ҍ9M[+9AsH،BB5i0R̒u5Bxx́f"rGw0~rb&(,$.JՙxΤB@n;*E8=;~"W}NZB9avܺ9 ]~\4q+1A#fBꐤlpT3~˴غ y`Z jĘ`L 0&`L 0 "ddCq)5NknaR_9\6`3>YȲԶ*L*|!y7܉`)|I!蠝BWH~֩^,bIOD+GŶkTآlbe|C޶읿xW|UR抭hrc]Gle?eڴTDcymF=?M `L 0&`L 0""nSgѽ,T֗pߏO~C=Q}$ ݄]g耹bCwg3VaO 1s4sxž=ۋ|d&0p{с^DWW':;t)OssI[L0J㐔 zdC֫!ˈa|gU d9:ʑ_QLLLp_7-Tm QQ?%xaEg{((,ܯ뵿%Ýx|ٷ_͑u[-ⴷ?cצ$vbpd#Ãđ%&f*E(ok3+RWeϺlG/ߚn dc>vE[شc__<Dz2m UͼP,CGTc 0&`L 0&:Iq} [+J3VCj> mغʓ(s efmIyqsO \Bik^>C.sÒ<-mF@cIg`$`gC.CKpgḨq%\(JǫN"w.73^UtaNA9h2vrO-2 öƠzzRb^܇Pw 1gϟĦ]nΤ0ZGFP}y 8zN{d&?, r۪znWݚc'8뇤U抇a˂~„,U=kFv/ݕXa@#.h?p^?r%cr+Y#=M/dL 0&`L 0&;qkHQvc4bv'5W`17!O@3i(&Ф`iI~LNZL/PtM97czvf`$)~N[YW)#4?5!*A]o|wyġ7]T @"FwK%C֛Wp҃Jw/zkp>6.ZZ)L|yG["o,n3 衴ȫ.lO֩oߏCh Qa'xeӡu(q=T["M+o 8ȹT48t:z>ψg;KrH=eq;HilVXwjFK݁[]#&^oh{= 0&`L 0&XGęG$n\+xk9ZPs-Q1&^g&Ep_+ݕn~^}QXX(|qt}3._Ikju*t&HJ޻1&}PrNKLQc_)v3]18 +Xmf.X'1~~#4˗իζ)*%|4Y*XQ짱L['Q8{?'7>4[`L 0&`w=(E @a(Z ?y=s-?Od@F|}g:{Rvx_yvų{w-VY/v'k>Èi}fŅK1!*pZ4?k_pʸJ{p tQ:Q\D)+ P͹ƞW'/xiptL 0&`L 0&XXqx _0B+É}]+v,oZ* AP`L M i]ojّ1LNYa#CɲG&`L 0&`L 0&p`R\L&T$@N^G[֯ê$$L#mE_|fL 0&`L 0&`L  9%mҦ7.؟Z*7yօ-L 0&`L 0&`L DC ޙ<t iQ Lx@IDAT`iH޸_/_]Ƈ2ܷ6ziV 0&`L 0&`L` `%U]vg-,sf@x4H4PL 0&`L 0&`L ( RQJlgL 0&`L 0&`L 0&bJ1ω3&`L 0&`L 0&`JP`;`L 0&`L 0&`L 0&S)~N 0&`L 0&`L 0&P`Ņۙ`L 0&`L 0&`L 0`ELsL 0&`L 0&`L 0&+.4`L 0&`L 0&`L Ĕ+.bgL 0&`L 0&`L 0&4 96T\MBل 5ة{xýi8@jd%kS%{\sWޓ\^Lb{g 0{s;W]0`L 0&`KD5.LFqid~ -B,] X߁ TLC Oenp~W\/5kJ,cӉ\8m'Cdn{iܹ/R 8~>8F&`L 0&E_|̑+#).:d1EJ@_y/|t7HE,b~w[,8LYR4!0Ph4$v>34)sH ~HܦP&,e-邃`L 0&`L E(دf٨;G#vc62RӐ\g(ЃIR}۝n%e?WY-rl[%`L 0&`L 0&<qPجCINBv gp¡ILFAU*Vv:lDQC I3r?Zާ݀SRFS:5qMxA1H4S%(.S[ ̽h'ܨqQ[0) oV4Hq=ڇ\hifQJazQ_StPؐ#N%{gvz1=:49&`L 0&ϸ61fSDl)BT"N w`I>pΰq0„( C(7'p &S 3g;"?NKyN'!1.ͽD~)q`qTHK9QNR&QRj ϛ(1 =UrxgC6-6 w5hh{}($Z䛎bp =sa*gyJLs-ƪCCbQ BqӪJM-IZmɧ)ErpUԿmGLUYIq$6Ma^UsOJhs;|z$%Æmck\'6b'IO]~%% ʪQ-ΈYDsϒ!G:=_1&ҾUlO-#xY#wDmzRw$js 5 0&`L 0&I ZELVt$lF9,F`<޳/l=4 K+{oޅJ M7 Gep͵ֽaGBM6ЏM82\ёK 3Qa1 {I̤eR"v>l_8^4T15ڇ+foO2$ڼ- }M?>)\P0~6w{,4awaڻul?Oa{˱B\%qv@Ȃ*Wp2Og(oîm(;aAl坈Ou:F?|cݵXg ^G[6PwI4篂{okT]_zvBF"cĄ+S4W4 {k(Km=@#rE#O`gn{GR;6l?*uvs؏]\MՊkm^`L 0&`ϸXtU u=zc%k+@?'OPZ30'!/G߈{;3#Qk%ȥSRi2;ʷk;^bQ#~A3 4#'n{-YX!h& / Mmj ILZvkϢ{PZѦkFV*ܓMyi#!)|mЧ(zT֣Rj4G_˴Ciai! N@Q]̓;{'U]S/P/000Y}Ri7 tNPٍyN ZquTH{$;І7.ȳ17亗LAԸq]Xp)Ci=8zEkۣDZQfhO=y<,S[UncPZ*a>Mm} z'P_SI:PE:8 Xų{\:+uܔSiy9ߐ۵3m{s+jRZŚKi[|~׋[1&`L 0&`77=8ԀC-2WpA)'셱T֜| -EG=^S{(Ahj1 fp3r-mKU(>Z,$˫7b~rDCueΊz_MY%+4R(=1%=%'pu<@éhQe#.Ӭ8 siSr0b[ ['PO# "7gzڮ_{S8Ngm46̕`4BK>p+pXwlyu/`s&.X"L%@JFR͸} ,}898JJZ3Ubf aԺTNm8㩧uzS Wy"N5|jQs7<׀> W=G68 hW5B(cr[1K_XNH\\v80$93zC \)`L 0&`L ,V\,%K萇CxFCrnPCTK<q\x=ʪI+@S 8[ҿQ^+׸%Ɩ!:SZ>#!k'ڍRX`᜔[T'Av!5 TO}Il:I|l<[u.Qe?II*ڇ{egWYע8ǵ p0pLHBm ¸8O!$KpW{C !݇euZvǿ*1gh) ). mJo{aKu\ob^ }ib=nщ$ 2#b9kz=\EH_|. ~s垾*eQj\sn$5L#9,/Iad FWePjNaFW_ ٤hSF| >7d $lDO1ʁ!y4 p[<;hF#\TatZ/HlCyhxD' 0&`L 0&V\,R=_eX)p(l'ʋ/}[{BnC2侁[@Tr"d$^0̉+sCmfpRm6*Bggg`sg|RsSDm,ljèS[ڽ־OƓuM$sE-J]Y5)$ V+i03&|(nXc .?O߁)ml쫗435]=Ĵ}SBgBfZ'%6P#Dޝ@N;^'Fn[NmA?d>w{@(B少I{k%j-))d^ICtO'^D$!iYH)QuoE\~Ga"D6ƨWm͟g#É95Bϻ\`L 0&`L ,>X|u+/ i{h,D\\$\ ][ ŀ;&,/5}O`Uu6wþ?{ i1V`pidf1gK( UȲ}3j;۴ Q hמPnp-{՞8^goaHh|vbR, ٨j鈴b6J–*|+SzDaḊ!{o.$Z/\L}KNᚁth7!! 9Cbߌ7ޛ 0>UO(+Cɹh3yXV]mWt~l}_,+_ Z_3_/~vgL 0&`L 0GׯŗKu'ZCJEEz,4I!+;F'ޘr ZZX2jFO;~5-\Z:F%;za+dycaq)n~KoBhש98~~fT4[\نWΙ1ذ3<4:7OwVwv f(U>'q5!dPu)e,axqQ4",*Ï:"ܚT(P'n/b&$g ~2{1166qa: vbngOu¼#-{`L 0&`L 0(ॢJBQ#ҏ/F C(cWަE?L1%BC=:sϙPin Lh6P'X+5b,1~ᜰ;ʨ>S3^V̗"t]Ge4Ioj;E->LQm,SoAN:o(o%A#!pC+-Ն?Kr6͝ WŦAڇ˧Z!LuD4xEj_YRw|\. V%`'&`L 0&V\D필_jT$sV[kNt{xR xВhߴGrWA'x),$KCa!e0+KV|8MiѬFIne_ r|:QҥsG 1@-{,4fGRR&8+`L 0&`L DN3beIx# ʷ0E-6؅Q.tvv:MWߨٍͧ>cGJi H-POT ؓ,qϰ"^j )x|UlWfB\8ZF0N}mb| }]((a9mi娗x? 3s=ILN`K\B\>C5_GoP`5)&ƆyԄBiVpWǃKRd?$IqdJq_” #s,Y$&i-&Z+)Uu>S&&0MKA J}S~tȔGKm;a^[R`j"ER{v }ؖ8݇qLNMajj S<; W\,1F{yq:qpZiHLzc&BG(?88Z +du|6&`L 0&yEf1'sHi}šv/S⺱€Fog|4gn -҄7YFnj=s ٷLOQ;J+w(a!&PgMDFL_ *ߨz:QC*Hlφ0.j{д[Z4d+[ݾQWM&@}iHoEm8!g6NJâ ]SBDzn؉=,toZ6ӕ4l]"OML1$ _x&giڠZ;h @5]$fcZ}%y;b$e{j,~GMȽ4ψg~Y^Iq⌉$ r|b7&`L 0&B #>>޹ 6 nbhY<{ׂ[0̭[N??A{ٟ]}OK.څ{}]bx ߵNZyOgxxەׂP#9Jȃ{\HL4vt N#$+f&8-&X"l>4^ي?Յ6EʻeH Aǘ;L 0&`L 0;+.Ok_3-,ocB$0_a'U@Ciz/|%=U`}ccҨ'%`L 0&`L DB%یKD>4->GY9mO'=˹z~1G&fp2}Jd>{bn^dcHL 0&`L 0&p`[at)(~VmU|8۪P\&p[HA1us`L 0&`͹áa`L 0&`L 0&`L 0!ʑ2&`L 0&`L 0&``E88 `L 0&`L 0&`L 0& Xq X9R&`L 0&`L 0&`L aL 0&`L 0&`L 0&+.+G`L 0&`L 0&`L CP0L 0&`L 0&`L 0&,HMnNE$ A456#}S.3oo\:&d0:.S?l9s҈N#c\&oS]X Vg) um:rd"qn-2/\Dq7@CwI4 a;'eԧwt$7r(ƪ/t*>ލuwlz~*Ehblξs}]|hbzzHJǖ˻oB/f:9 LS=)%ijPs]ǾMDcHT;h鸎vistD4C9V ޜ̍>l_]ՙ9@_{mWբ/uh 3:w[Kdg󟿍w/}+wқ'oCe< T%~p'~}r#,[.@!?;2H FZ/wFch);(]x~_tUbL 0" nŇb$Ѣy(~gtSXIUٵcr|Y q5҂̣m>\Jy7wp9ƽh:YT&] wz#CIq.l f{ gh7G O0H~ s>fI'Fγ֛ /ƾ\#7$,[pX:,).n yG( A uTa9͠m7Oi{/CB6J3HJJ2u Y ['1~>E3Yԉw)Ѻny\VXs}ժ#bL 0 *¼#(2|2xtfSn&+<)EZgoJ350?݇|/#zsT'htb6Ĩ _#j^f,?-lm.i===dND|S1CmX-%sgqAzxZWd xa˼EPGugZg~?S9)g8 ݂ ;Hគ{Bs (!~ˆg.0sd._ {7 y3m:d^8/;2}fL 00 ,7a憃R7scWo"%=cQݓ.-?Li Cҡ966fGZ,l߉qfve*gQj(MeLԌ3-&',$[ef{=mK f*Ko:[RIfo:wG%x?qM0I=, iSY*yzܞsfVrl\T񮚃{ UڿPZGsC`L 0F`%l@SSDDt 4 ;ٴݮA"mZ3j kz&1>mj4H)Cr}xWH/|E3-˺h^}DI;dsoon^۬SpwqhhD}җ!S 3o$; t^ k Hq!J>(TV)]1"&ϴ,,V$0kC|RBaτ1 tц!,×i {B~ ޒ)#fCl=b<\p opB]&uiYplu;łaL 0q!mvdKRR6IZ䛎bpIQ.\mbU%&C Z޿"ax"+MGv2>QA('lSEw JaMI\ ]>CȁUho1tAIQ۲[䣨AS crO܄{>j"ͽrf`W39q5F%%%AC~Q tJ Wb`7xH▟_ UdS.u,q*)I_1aO06c0ji\=5slu;ѻ.={9cUhoiuCCw93{YzRQ=`dpgZu&?jgzVQ(/g۟s!~*CZb: y\SD|N!9Yg31(ٻCN-#М>pN Jow}[U}c{'{"Ta$@W?~|8Fu?Lg#]>|O>T{k ƣm}0S] Μ\}C3(Tn]|`b$r8z?V/Dŀ#ED266,o +J96ދ3 6چ]UmoG^JLbq~N'̵_Y"h{ 2:O9* _zv窿uGK6ZꓮbZT7Ǻba8VMm0p8r=Nã[K7)j P?:,Wu9~kLY\){R ;y 苕ɯzVvZGxrw׻@sf>B47 }0f8n ktB gqZ*8ebn?;agrNތ_eSGڠ{9n/4R-VL%qO;ޙ=|&N/LuSvǿIvdL 0F (b4'R6tVaB.ii?e3 !a:Mҏ=\h}(C׺kTXNA&.jNȱ _{ˮ]n_#}3w1r}Gh"e粒iIB/,}JQͨ(n:By~ ^G: Opt#~P:3 vRI6g30߳"WU6 EOYKUvw=z|!gltUMUZkІQ҈~-)q}4qsC{nA]r ~u~CiaמVL }tz(;N yh+QCXqc% 8uCGXomt!w0ۺS*m |P7\ #=/aO{2Wc ޷/ļ|GnK Ա+}7EVé3!`4QgSOJUL`s9rt3lwfޭbYų{`^|{^sr 5`F;CY- pn]y}YH :Jvg2`L Ĝ6 P!|a*_t4¯9TE#HyqIҀOn# ?EIv: ϻF&m>Ct2wkL2kKFr2%&2L>2z2[OsdyCf|Cd&%2ydvycdL!9'ȐEdL+d'ml!L좹#?u5발oW58Zz,MU@ӎvXp4 -;N*ڋahӵ=n 8hG^"6hw.p^hnj@+!E*O^+^8Eb[&NVKcG*)"߮*78Ķ3͓XcqgvzeCGqo] UڛX#Y.WcLxϊ=2ls{ūe;7"s1ܜcyhC{Q1qFY>5m[S^dE\v׻qA2| ~fk;;|HLj91iGvY3B&3/s8H1(pSunsqV8}㥝ޅaיUv'">`s|}A{9~:~9fff?㭷ޒot(p_7;;O6qMԔƍN٨ 1Y2T 2TA*T٪ cdU 2XA+d٬ dV 2/dlW ^A+~ d‚lX bAf,Ȏ KdʂlY1 fA,Ȟ3ddҢ|:zlA-ʷW]y?$ 2l\0n#Eyz"#fE#5^ѵ(e=PKX DErĤTl!WHmxbZywbvϊ9(6Ɯǫ~L~k?1qCMD 1aƩ`HA΍{p$M|y>'o0<˜uT|gQjWyMc+Lr6[ztK5n0q{k;*?c8c]gMtZUT6>~v`HzH绸;wV)R~Sz`N0CGpo6ƈ %tcP,I[xDF4 wL@NZ 7` y b9IhVgx_$uw51R] 0&V\,ZR369½C}Buhze')Gvs2 HAX.SӋ}( +|ѩt ݐ/-O E8޴[RHXZetA;^+kC䮅 x}s#a %2D2Evu*5/ Jmnb˷)ǖonL7WNɥ2Tnl9%V{{ADR"n-  RA hZ,%ݝ33ݝv3g|̙doPkb"*} ^U /PolrRH:xoert_G}SvGZCsJwScgt?}NWHzꩲԂ,\a={Ca߰B;[!#9O+Q) <o8d"ZfNwGҘ, fi|[0?H=6+R @IDATLZsoɘ;i;d̢$>G묧<+ޮ4/ʹb<86f4'Apibb< [G酧ʦS>|&g91e1fI&S.7'-B$.E>YN\=tbfq]⍩ǪMP_m|c!ڕrmL {B:XHs̡LViۥz1*4>IcYyG9s2Q&8˻Ҷm"[}gƻ 3}Μ"ZoZ"~naѦê+sMh*`G'u675@G_T!V ]I=g_ݧ-v~)D[oN6-2EN+ӬwٺeқO"OJ+ͻNYp\Iv&1t6wiszB|d9ڝkՓmz'i)\ӄ9%tWr02~PF-VDSNxwlo'"W ˫If)Qlə&#|$._3+?ڠW|͑): =y6Juc9eӵO GK_ѷûBDt%uNo+|YYr{LuΟ [?-*ө51MP)~?o#\ޣ bћ筥CCT#yd[l~: %Ix"g}WڰQ)1ݓ…s- ? 6{9츏H12eshN޴x8.sct3xvF;]n掾Iyzvӽz-P*ErxS`?R$?}DΪߡmڃA]/AyТ[签ߐP"Tf` p-w%eE%ܢ1$#ʄ  )G` r.#ч㜝(]\ ýɻ{䚮{qJzE8On$Y1G?A rkỼ>/ՃNMMN`Řs# D)RU[u|'tמZG7kik?SOk:ج({-ߊ0ӂ%ߒ69B>N`.jjGNR7.Qݝ O"kHB,H4Oxibn.J5ͿSnN,>RiGh1${L@rCEhdme\TO#iS 7Q瓛>X72h.8mgc%h8AXs^H9j4pt'< =O2?DyTb2Vn{+Ԣ.|)O~M|!MYFW꽒؇Ԣ_N.oUZ>?Uv5G}jݝr72 =}^{;Eg^kz[Vt 񳨠vyz^F󭻴1)]=z=e>@~yڙPbsV~Xg7"W>g>b ۥM$ytCuO#j$ezIr2KOl4'̂2=4h|?1"$fvJ$u#ݩmF/؈5H?}T$?-|B=?!SVE[+xY2n߫rh?>[tY+icdWkc\oƱ+QN~1]*ʳ! ?H@,Xsuh!,i` =Q1?KEzȭ麋{mE􂏣e]׶}R!}w򍛞z 2(vyZju|$^s~oQ:g^ jG$E?9ldO?ybTV ߦ67>[n_:yݾe'NQ6"gqG/NB}N;#q"s6w̡1\KNlD>bvHߵhUZ54#y ;j}r d[3/'~jW2 E+drWI?|扐qL',\L'ݔ;YIvtR}&[_)hܹ[ўFg6UYC$J3An-]kiIJN6ϢߪS.-'c1kJ. Ƿr%C6AHwiB1 s3-y&iv?Mͻ*oZ=9J>dP6M5~uSeJߧ!G?8oE:yY4#[}$Ϧk}?tKwW7B<,i#?^l)q"aYȆ0bs!Ґ4/csm\BXf9 lnbs31[l>Fܡfy6f /-6جgs+llds.la6jlkl:|l*/"LtǐR"ÅKm*CCC@P呮RЭG;UxrQjmW ʕ1=69nwbtBitҩmR|WiQ.MǕ2c+]uz詬UZ/+M wY}1|6dJU^xo}sw{%~-j1^iooVj*(C-,W2;9Zu;qG BI`C&EQDjMxzn 5 O`7sy>^i226 * 5x{(euVI;SL探ܱADiЪsdHssԈ:aT'IQO( UL]=i7z|_^ۢ-qs3ykl!-C55Ԛ]Id{b~rS>6 UyellL7>H㏕yWSNi^S0NE6711\tIŋQtB?sН Х Э е н        % - 5 =)`#tҪ~zۅzi]il:oU}5 +*d]էg]k++خ0Old1hp,9r 3p>UM1nmbᘢXR)QC`QX"_˧/fb&% ABMzܯJ~J8D\nᮬ15ԞZҦG,v-oΠcQfN鼏H@Fo Èmgm |''=*0Ril)+|+߼p~p^VN[L{0wqҷ:;7Mh\W8&A)dm>G03z~tYxO%>bwiByD<|i>EoM?)p^R,uW4.Vʪj%rj) BFiit<j A،rO$Y;{2uj¹a}7@@f9b18nќ~v\d7_łM4lpzWje7ξ_S6n(b:&Gib|ʼ!M_J7U|q !oQ^{_'m ꮥ֝,lQ@Ọ̉BZl -ʉvфU0OoK ʜ7.KQQw"7>-\E>K/,[[$Zq-i қo$.ru]t)-_d\";1׷Q@nE178{1M\Dt"Bʏ)a~):ZFϋNǹT;Ǭ<1*@#+_N"/Ng [D\.}~uTE;Ybov͘{ܜ1n50T"8#3sϙzx vXkEx R <r4az]PO<- םq0wugo/ w`!;eI&`+&pSRU]=W[O׿&a>9o%z=%[Cvvz55.]ku쮞jxnowS[T/. ?0Qӊ  u@3/sըVWrܲz}9lwp!2 KѲĆAR!C@@ M cѾ.}7/b7RFт;Ml'7a`O WB0wDU$ ƒ !˜Pevj8չ|M¿zyB.(ߨ3Vz$h!lk FD8H5D6Ƀ-n*Ƣ%7Hv-g1X]1Jf4m_\n*[V]M⪡oԙ+  ?N)ukVhtq?5޴FYM†x'BOM VEn>6\, `xY+2    Sq7ߩu"ptQ6G9ul<`  b&> tڤwU̲z*{EvXk nLst.}@@@@ `" ?)YϹj(9P\7۾z2)EDA@b#pmeTY.&]xxD]nqӗv5%Мe۔ !)C1 Yl:0і=OЖO @b ,*BOˆjV>ݎR!cx1r    qp&pH,\$ <'p&pH,\$ <'p&pH,\$ <'p&pH$dA !=tGWhmq~BLF$^:O_" ʫi5 iɲef2Iz~OY*৞SZDgGoRqKr%]h-\RG$Υx~%̠w)N*MNr̝ssgcHhxv3 3=M-E)1EKb@Lpo.r&)wq-ʱ ؖm}]WCOlH,=EGCR5FrfU!MvGi.K]5^:@?J#3r`u"Eψu4ZAɬ=;B--)|>n)nkɄ4=5GL ڞcb[Z, @ʕoɥMB0yǞ'`OB2 2KxhʞmϦm$} HΞw@@@ ^6xC@l^rmox=eM_Ҿ{5h!$ʘG׹?|; YgIڲ ?l^H6W 'tCHQ+ ӧ, 檖g^uk\xϤ7hݮH-E򲠏3C5I隣I벗P95sG|i" ZDY1/WCQ>#zdշnc G3m玳 1D$x.//Z\z4_˴0HpUL˵cK7e c i!|tlhQF2^ t+tu}$Df>:+môtE0o4 3Zz]E 1âEYqӲ/R,Ѕ395/ ˤ H]t_ 4\4rf'Eiϕ]5Y@ e06y ߹$v WHآ$HN}뮿\c9!w|@@R.R8[A:E9oqpJ4m = voCA};BQ"JŔQDONQeevWv]Su^P99yTPTJCQUziJh˓46TIC3 F]NGܮgePޢ*ѝkނ>b!2esgo&3m5]9J*6IS, mktIosޑf mPG$L7ߘ KxBM{2з  ZOzVPI" ٵh}opEo-%T#0/?>̂S/yoR9@P4ËRW}-.xz_QqLB ˡ(&jnǓLhDV&UrSLbVAU[ <.hm%V~Qח}('G2R{u< 9Ѿ8ɬ}L-JF7\rbSpBUu댌)\j1u5?ZfZq[NrvsnC%qE{|3]tg֧cRd(Y19L56qܒ_ 8#7.]3%ΡLC;q6C١\pΤ97dMq~.yqo5>իWSiq<4TzOҁ }lͤR{"u=thvgY&/;1XOXCYܹ<#i:(:}j}%QE8Z߈{Z *zVx$ULBF29K'=FKiasGs 爃quQ1?%`<$nMqOqpM֑>cn?ekm?RQd]d>v@ା}݁1CoO҇a28'q8w2b抽t'7 8ˊ(09F,S|.q-q+ӌ2F_F<+x@ %%ļ_lym\l ,gs=H lnbs3b6+|؈cVE̿:,s儻0oU4w!t4umyꄛ,  Jz{{wyGSx壏>R>ce||<`^}UԩSya::: S.MLL(.]R~rEettT F~n:S;:TK:U[:Vk:W{:X:Y:Z:[:/:]:^:_:` :a:b+:c;:dK:e[:fk:g{.dSFU2 ҐlV .tުjgDž:tUva}d`j>d>粑u>l>:_sHH$± șv3,\Lk m k&Zl:B a7jV0+UҮ]Q,\J[VrL(RV^"&M`"mVָ˕LSU/+RIq(^xQShF(0/WZduLt AqlWDnd+ф}C =)UǻX͒• 6="Zo#h7"ma\. JŤ4'Jj ~CjSmrSU&MMR}-opz.QpNڊzh n; cZN,6#8ݖjiqUt /*Id}2'I?#GR >đ^{tٝT5Cz+J佚]W_,/{?<[O/zcIQk#1k2UmIs 7E䰡ǟMDظ*4>[D6l|Űƫs ΄evL0>(#StK #rL7޺GCO&-2G_"ΝMs.CǨK8OpZ]r4gv.K[}i񛽕j09)y_gDNr6 [N@6r{jZ` ԄUBpr8E ,\`Ⴋ V4"v' qҲ*rx)xI;4W{S9yQiX;ҏ >} $Ȼ825-U┳qu$ڷez@kf袿5[@=sF!~Z7t2|]QbVZjˣtf|Z=~G=wL^:kz"{^kvxY,:??: 'iY߳)H"hR_e?GiUZи1@)~UtH nMu $箣n#gvWmi8$㽸iW *Tut֥b/ L;-K+X{+ґ'4aIUwGp 'PEz3EjѤk]WQf(Qi'))4)]tQ5gdLq1,,C{MO.~_*'C)SB#rgbcIz{jRoDfQ%'c/\VpqYgtɿjUƉ~qn&]̓w un:58*;U/SA{JujWubZNq) ?nrۭ $i}ݴTx߂gMFM+';>fܬET1sFͻ /^Jt5k|]zJيk֊?lk;W\۩/t`ST%׈q$dfJ08b#xe]1GU侁5rvʹJ >[5a}D"R_5e{kf*D\^MH٧L9O=UƢg9gm>EW%ft)(:tmAsJfw6Sws66}J |L.Ga ^49OWCa,"9Ku] ^f9-vɓ"kҢQZ`zb $Dh11j}i{m]M$stsmNct'2W.:b,хVM|OsK?ߡ3]tUR#̌c?s?%['9?A:C'ڵ'Jߠk)ߢ?J|;m.drg'{,}-(vl"I#* -*ɝ;F @@@ e)2zGbC;}o<,USBjX㵔9Ђh'c臯υE+x{;\/CS.j.'vVb#^㑸gUK"iV_v-쐼Wt}hS[(ԉ>&G i=0{95jC }2sQl9ـ=cyD3uG1 7Io&$GoPS8{5GOͶȆߘGJw\fqYO 䤏XTO#iS :z&|rTb $h%ȁ=R[w*>Lyӧ,Zh;Z'H1$@^ݯ;䃡D|fd%Q."@5)J5oxHlGVN}R#j-i7}"*m7-4,Nl(WI 5? } UG'[8x!6j7\dڎ-<e~ez}fߤ-rvArys0ٜ:$!7feP<8=^O1/j-ұan қG p YTPRJ;<\=HS#]]Oy*`v6.:~yh'fʒq^XG3D@,Xsuh!,i` =QE ҏ4jj”C} IOm\ս"QQ;'ۊ}PgX m隶`r7hvёJZ)gA=!` << a'ΕhEhݡpm{۾z:k0uPC h,ϞOUIĜFKBv?\5z3 n*}a6GYW@{,j;C+a_ףcz|:韦>YFN>ݾe'NQG8&ݸ$vۼZLUvf+w,k%m,:BBۤ|>ߌݓ蹇 @@O g: bBrsU>I}5Z7O/Q;Lvd{:9C_I385mƕU;T+- Z5j1#PFdrѤz%z_:@ m栭ްܰpN콇i/.!9jM}v}ph'ٝy *RDXk3]gՁY,NK2NZ}ˢϭ zNJSh i+Nv23W׽zeH$l^j uCDL9:͜pf/ .(n &HI-Q.XB ~sF=M߶CeGDgOӭ'x͔G)]h%FmuE.U8Fg{tRc4';tPz7XKN吧\RttM'醍EP :zN=M'OigKM|zzm6Қtdza9y6/Xm'IiJXC+׫N]m=t,:KxnMgNK(k2NV}#;l'NimAi*2')S#/>FsWbTI^ק1ьNDr3sX2 s}ć"wN)=pD0LQ=MTF`kg1b{t-(m=\tt?xsq{=,oN=b~宮crV:v1^:r|DrCwڶrV? @@f#EQȩ|ϱ0bQD6sulg&!3U!#[ /6  J9`ė Y2el M!lgS67{_lXбϳM6k|oYf=[6_d#wl~F6 {xlfU6_clg#~_T8\aAS٤Ltz?_y\hVڟp/WuOWJe\9^!,:eD m)uJǘwlܫ0(+&~JU˛RSw6Us*u)%2VFL+K&^ꄫ9],TO59GiUW夞ui뼫:{h+2}q:#8Ѿj[^F~6o$m%޴e/n@{mT݆1)Fn0=4m߭K}\GJT7kYFo}y̬#Rk]͗ۮ1g(Ӟ ɩoJK;byut+ ͣxMI1GD9[n}iJSNWVu=1e #Y?9ʬG3\j ;#̞Q;K\Rco^i)s'5cbـۗEc0NiJw<]  L^wQN裏?XW_}U9uf^{5ETd# ʥK߯\xQUFFFQ~q6Bg*tB*tB*tB*tB*tB+tB'+tBG+tBg+tBE6B+tB+tB+tB,tB',tBG,:c;:dK:e[:fk:g{.dSFU2 ҐlV .tުjgDž:tUva}d`j>d>粑u>lv9^sHE [C-#olג[4O׷[foξ*ynbLϢ-究,,0j;JZS;o#Zݑ#q#ͳ|45Sq[F˯?zfk+-/ GjvRkLz:,qSt}"X[ӧe_b=2跿*uՓxz O?O Xc}dRuPόѫ1-ő)q/ N'}ZQjJ.*hMo$m%޴33u9w/oI^NEn nJtyT5XTwy-u;˨]iQ;jNOJ8D^ݕu=f$p,[KT (?K9L úKriUS2<(TQ1X[H/LրIWRAC\»E76wˏRFX۩s[Ģu=1e)ێ!)"j-+|+DTsK\wg\JXbo%RG}eD pI11,v>?N^8 )[OXLup(S ! ki@@ÇEfhe?fz.Gb&sG6j8M=WewMvq]ُ|ng_qƍşƿ}竟WiSXsr͛ o(*Cb@-hi~XBmʅI7 ?s5z\_=v\@_ ׮     pVӜR:LPM~_믜3 e.ݸn= wo_R7ӌу@Ȣ=Ghksgh`,(nimC]Z~;r}:Kt|}*-$    !,\\:Yʠt0ӟRpF #'n?`4 Jh      ENFnA@@@@@@@@@@ `"@zEz7r           )M )]<@@@@@@@@@@ҋ.ҫ[HiXHp           ^p^܂@JEJ"*o@@@@@@@@@@R.Rx ,\Wy#           p@@@@@@@@@@@ `"&.          E U-4,\t@8H/XHFnA@@@@@@@@@@ `"@zEz7r           )M )]<@@@@@@@@@@ҋ.ҫ[HiXHp           ^p^܂@JEJ"*o@@@@@@@@@@R.Rx ,\Wy#           p@@@@@@@@@@@ `"&.          E U-4,\t@8H/XHFnA@@@@@@@@@@ `"@zEz7r           )M )]<@@@@@@@@@@ҋ.ҫ[HiXHp           ^p^܂@JEJ"*o@@@@@@@@@@R.Rx ,\Wy#           2RZ:vQߡKv%+VRIAWyՔȻXfS}Kљw#bV ׮>ʻb6f"YGO==?oҕo/R.i&0Y)Jrʚ{蔷,tMEe6˚4ueW4M]7g/RKS;:ݹkUs%Slsu{?}ͥ¥To^D/7Ѯ.(dg9ʓR>~'еKg6^n=-ҊWƾ]A9 l)F0َPZgg.RkL]UBmI*bef3,\cE] -ʼj2D{ˌ.?: GtfaBO$$m)/|m!MLA`z df]gH`s.PEN&LJ 44ghE˴d^k W< -4 k#I02~S(M9(4@^vh. g^^ys|^ܳoSFYc+i'|f ͗,PYkXy^:|Sib[Gvzs"n)&;I?,˼ "'-˫/M7B{g=0̺|y-l\ۮ fv&&YA6A@@@@@ - `"=k9=`hOQvo`<bQy|}g}VQ|feQB)ӝ݊hqeض,Zf]J::ռ'K桖]S!${F'i쳕tC47g-ɟo5%'a쑲lh P% ]̤."zrb*(sqĐ:lzJ:A:)h@@@@@@R@ w'<$h+ihOB߼þy5E iYEMu@-A`\]@.68fE{3&# -,)lV iXdrO(r+.dedL?;~˃%t.zraCSǑ`q]Urg)O EO&7>J>bS6]}ՔsEQgtɉ:~Nktpz:>${~"`GG>ଊKW*9 qڜnUļ8y]nq\|@Ļ.ռ . $L^ [QN?N^~jܫa.9NU~Mh^43G3ιr_-g&ZAF#_2JXO ʓ; +ۤfu/^S63a w.,Llvẃ,s fzJ*;1_3գ @@@@@@ܖ9—E'63.>ڼBEtTڻi5:gӖ'iqMT߾OZ!8N7xw-'^7`bùɯѽ( *w/mO<1^ cEBkcnZb*kK,$s)V,L|Nk;m~޷ rb0Xam +œXy?m&Ye9tB?6Kݫ/aZ$&W~ôo c0%&y8(@ P@teo n qʹ6:juv*i7JGͧSo|u+q|<4iOS|JHKKS|ZnXl*r4tJkEyʳȱvAo`#k[ҥFh_蹳E)m4YӧebK$Cw\$':շGh:)<>h 4}H_mlɞk^&܆_<~WتLMZۆECVFbڤ¢)ݘl41`9ui2u\VҫvZI봲=\uyu^DŽW@tچSH-A(AW:,G'ڑǟsAhlcո-i\} w~%xP}s Z$!:VNAf~+o°[>+ r` S QR Z(-'GgqV3e{ɦҔS%gۿgnD-95珢cP8:.~8how N#7ơP6-"nF]K#ϼr܄/?,vE ***%hO!E2u)s;?ր.mxBH/w1.14¶RiK-Q~9ܷ_Z [$"ӌ;Z(.\]{R7M0dw0s\ 4`m(@C;\4v?reqV\kXƝ^,FAcr%91oJ WX~ܒV_ &a$>Q欖c(@ P(D be,z gakL؞$1qq' \7~ HJMq41YJsUvʴkfx2O$MAP,v cD^ټ@_̰Y1DƘ! GW?>$;nm\wEVϗھUil"}]N1rT?<_=\?.j ʣ4iLz-yJosيW%P"t:^g:c sKdJ]R'#4^.V]ݓ4422eKSy:CK/#A_;7>6"mFRlÿƂOM*Uc,,y7F5xr% } 9O`g=4#\&q>.\h%}} N5۬q*VfڭXdᑯaDb-#rMh3rWa6qR4t,r uh7j2k1S8/RM*c:iN˟)VTegļ ;ATۮB[(hU;=p&ZEnKI(~"v9qI6z-{8s4PU1⑴竸#{ۣy1f_/a<"aFh~ / P(@ P='pUUŚ*%~Pf0ɱc#dUغ5;~.'v:'+<'[[OBmҺNSIu E%qǩ߻3_ՠInh*qBlQ:ǬŊ[5}0Oݎ[ з;SgzӠ,6u&Yx~gӭR3O:O3j"JeV̵2t]$﬘m])87$h.Ҟ9rvMyI- 87$haUCjg5ցW;LE^q__ٱ_KpOIiUc\9m˩0 D [U-9Q F=uTc7ǡht.wpqlx97egU,S ҺOϞwHeV\{v'au3ڲvc#haKPתt=4nmМpwn.A 7a>l38{y%1ZqJq&Xc̺!hQJQFN(@ P(@`^U)WڹSs < ckdBҼ$&!%~" r}"YkV:P\wLcL?苿>\L}E^>}}n_ HZČ*W{+:=4JС᛺g@;'9ݿ5,2TQ&YJ0$s$8͡bS2 .d.![ahWALѻnPc4dt9رdc5]9Ҡ+b:Mx0(Mhhd=wh29)!RcvJr 1VHuv_K ah4:QD ̚h=tL, Sq{K;"frNv7VSÂk5Cs< mwc_bu6߄7װk_jގp-3[?OkScc")i9I9 ߛÎݯV;ͥkmCM)L"i~vO8^3»Pj>ux9jGi9EbeXZsR?m4ܷvqG?ƔBo9λ;Qm 05(@ P(@`ǨbEQq\V^kz\%Q#=d ?LV^q%rհ+}F0cr=6sjsr䮋6THO_] B|'tDhƥ}]"w'n nlUu/B>ÇtN oX.ufy&= K4J\V1}8 cԝH"Ux@Er~ۄ,n6J8} uwSȔmT3$h<1DwǘY~øL.(@ P(@``M?g: U29%-sZSxYFI`Gi ut]Qe;2 1}\"6r=hnZǏ]|Q xyO߶,DřӑN6jn+i9\)4wǘ]"Φ(@ P(s hnuB]Ջ]w!D\(Ԋ}Ɠ륯K5/2>[(^iͲN<\W)w]J*]'7/'|ц߽@hB&(2ukDp[>csjiꃟ9^`H[4 At W2gc[`o@9u 2 1EcFF.ۏLsFsK7jO.ǙMrSq8c}8(@ P)E 5mYC}{vmxY[}#n{]^Zgҷ&܇"%ܪm@OȣPC٘8X;wm ~x]B7[y^ ߾eF'#ۯkSscԩ^m;P lMxmאώG?ͱS]3N#wKn3::NW],lkm~t 1yM0jLBʢ_rnli跧2q=+p{vNt'.,stWbߕ!upF{8m5Wx=$7MPC#.v✓m39rYGhm\T9 1o P(@ Ptq,޼'g!2 WөW.^Tz8:0"> Kh~oG!oȕ]kJ?!/Dt|a\;)q) ԡ~w[Ww4r+Axg_|D_n.;*#KYJ4]`N? <[b0Xo@Ucܵp {cн[)9]. Xˣn+Ivm#ejXzJVãaH*0k(@0])uI_n1oS-l-D#0.֘{~oFw ݣ.{eu2];^{*O6yŸʰQI>tڞņREA&lmKJ]#mcxexwz[CZ-MȈPlۛW#b? Q]Z<$gA:Rwv5޷uԭx|i=G"4{;~c|\L)lf-J5x>) uJw-_51Զ|mvvG4'S,QFrln5av L*f{a5P>dlr>\Cb"ĦROh^pԎtUh4F8{GqրDi '1Pij?rSgQ(@ P@ Hw+N@5YVZӗ%yqJ|0 wChs) .a S/ \tnyv;/74'de~E&5TB#wu 41ڊ<^M'.ݘWdu;5X`64W_cr\momC(}a>Х1ɩ@'h<>:')=I0FK]\ɤI"r$a&j _{wph#2]mk>C]Rt_7)ڥ%ISʟ8_uӱ&ٷvreN$!ks8yuS5ǒd>qG;Zphs6< bǿ=J^g:ג&wOI-nadq?YEbf= ;wܺ1ħoA$)]Tm]"'/)O.ABsuLJn䥡m1fdxK-ݝ"S3/7KQb2J6=܌=fڮ6],(!U ӚDv1&'u:~kou\Ykk繦N{oPFZZ5m4Y3,[r':[GN%':Uׁa-{pJru-HM1YЁ'>D}#nE&`iH CX$s/_CLF: X=$[}oI{%_ IX}hS̚oOyf(DExa9~ p]q\Eؕ@IDAT}C8{2:ČÍa+1e08;E 7GqbMX2yn]7'E]xgNhoiGha0~r,Mޜ]lYNc1tx(Z#ǎŘQݞ,MgpIWnoWQcoDlLyi1>zLI P(@+F?FXX5J* ؽ{u v-߮i[vm/\%̯&;wOdd|'遲~{WwL-K;_W˵߮i[/u=Mq[Ii%pͣ^`B 5^}0X51i=!ߐOLhc'HJ#hqrɕy1WEdD577xUe)~Y>^R&DˣBmb|<;&-|rYmv >Opbkj5&ipB:)j!@m7Td F|6Z!Q(@ P@sJ(0WNW[,NyT:5._KR 8~ye{֊ $rs8x gfs82z-= ||.L P(@ P`O@f%vmETVؐw6מBU*4 :/R&3o_- }M/'b(@ P(@ PA`"(,4cQ=o^^DpX<2;_X`J)qm>sccך(@ P(@ P@+V 0@搐 (_v_c0U>*P(@ P(@ P.f(@ P(@ P(@ PA`",(@ P(@ P(@c \sc. P(@ P(@ P EPY$(@ P(@ P(@ P0\(@ P(@ P(@ A H P(@ P(@ P 0pa̍(@ P(@ P(@ PA@e(@ P(@ P(@ `˜sQ(@ P(@ P(.")@ P(@ P(@ P0&17(@ P(@ P(@ P  \ER(@ P(@ P(`L cnE P(@ P(@ P@*(@ P(@ P(@ Pܘ(@ P(@ P(@ 0pTI P(@ P(@ P1.1(@ P(@ P(@ PA`",(@ P(@ P(@c \sc. P(@ P(@ P EPY$(@ P(@ P(@ P0\(@ P(@ P(@ A H P(@ P(@ P 0pa̍(@ P(@ P(@ PA@e(@ P(@ P(@ `˜sQ(@ P(@ P(.")@ P(@ P(@ P0&17(@ P(@ P(@ P  \ER(@ P(@ P(`L cnE P(@ P(@ P@*(@ P(@ P(@ Pܘ(@ P(@ P(@ 0pTI P(@ P(@ P1.1(@ P(@ P(@ PA`",(@ P(@ P(@c \sc. P(@ P(@ P EPY$(@ P(@ P(@ P0\(@ P(@ P(@ A H P(@ P(@ P 0pa̍(@ P(@ P(@ PA@e(@ P(@ P(@ `˜sQ(@ P(@ P(.")@ P(@ P(@ P0&17(@ P(@ P(@ P  \ER(@ P(@ P(`L X6@/ B;=e*b"H)%ۋM<%pu=I~`iG0,֌hG{b7u}|w&!:e '\Dܝ3XYo;5ј8!Qv(@ P(@__~MK8s;1ZP]Jeٷ 꽪\2 {9vX-o~?}#':px \Yo݁&{xcIEx1'ܞ{9E/iۋGFQioɚfdǫfA^\|G0e}I^lP֣_CRW8-=I}B :#!=&k)-~2/AA2ܟ0,(@ P(@+T+q[pvu`.y@uZ;axQgxuF~zrefdm&M 1~>BbYm?. g`GB [;1`gsJqWw4<. m2՟gvDV9V9Sc~+Ig f!ۿF*ɔӤsh@|`*B#x_oNQ(@ P{+ejGAƛ˧mn[ t Z M0صGbz<%IV_ޡ"ZcWmf+Z5wψ DLlwj+s^0~4nS28 *=s]0fT6|;9ٟټG|.G>sC)fO*EَȝѳYF P(@ P,g$|6y9Ծ2;Mhmu+Cdxix4qckba"Q(@ P*/Wk0FI(Gۣ0-]&Dq_h^f(oH'mmr&&;a={rMqbz1$ؑiO_GV?pxKP{I__@+egQIHtSA?IxFڴ1c/NqoȜRg0,6-R(@ P)׷PO Fq K ::Bap#m%C ⧏ ngm[+)[1F3\< msCLޜШ>i^Ԣ~txE h&x(@ P(@ PE@K;P֊DKxozTEX~~\ z7ף~(0e_Canb?[zݴ[F`Օ~k0)}my6W^gqСƴӖsřD5_-3Fnj NԡGȫߦ#%?-Djv%[XԴ=2 =:-}y&Y'ȚO~I:WN`ۯ_9Ś;$wlzId$tQuF-~ j9Ůn(T܌Nю ot4tw@Jj'glĖ7jQٌ#'`1r,z<%Ǐz5|ۡאҾTMt?tli&fly NJ[og6Wy5Co<1!8ڸoM?Lֲڏq^oy×dm.ll67ۥ(>刘꛶ Oޗnf(-* /n/BB묪"=IS#1G(@ P(@ PSʁ\+.bߖè˴cӧP8\Di(= J Z翿k߸wX[aXUڼ¯Ԡ0~w['^T9,b}'O%pŅ'?Die5[Ȩ[qTCU)ҪzM'u7IwڂuCv\ܗ5͑:RSX dߺnsF%߾r:}f:I\)#Q LGAz }mݾo NXezZ#ga,w@7HBt8OG}Z~ܼ짒M$Vj*u5o}8So'ըbz@w.Kաj2o ի̛h/gVWᷞFy?2ٗ%EM!ݏʕOht,fN7؁ڹM;5us:mۋ<]B4Xsxxe(] ^i X} u?b-Bh9PjVBEm6c-C:T=;' ޭ/!V*8]|_Rc__z۫-؜=ȇ4vW H_wnνƿcZ׏3Py:&V~/\"(8={ٓxs4ҕ釽tA %(k}.+HhYЗ圊7T"묊r,*=t(@ P(@ L̵Z]? [gو0>><.,bzQrQ:IЉ_m-z~ҸV,,3rWacF8Urwc#f݊EI|=#sly edz4)*l5,#j220@OG3 ^{LI(~тkӖ,h q]hL]UV:;E~NSk8 S"7#ykqdIHsfwcaն 9l3vM@U׌o̊Q'> 4Ax17Eq7qD[g81"4,Y!\:s϶!ksY1o/5qf|4M:JobHIsn,Yl۝ਜX'N3q (@ P(@ P`x\@ (P:ZyU&IZ| ̸&Еyw)Z2 U> {>4 qhb)FG![T;bMz-XUS-r^:p.:X0?58WT X%Q5R- ]]`BGVBRW ]J袡t=R`wn)6qNdl ZeF%,ƵlX #[ۑG3s.Ҝwkdˋfۿ/vYIߎ7K=&ZS43si7n h^]9 Ѷ{gtz:utLxi~KUݬgdܝ. %>I&S(@ Pc;Z\ 'b.haoXGmxGSsUwi, I^7,T_c.h֧-ͯM;4Թ]yE=q %ϕ>R^: 'Ĺ8VĮ4ZITt8R:nDJ2Pe}EBJRloc Kq+ҳ,XW}#o-(U ý|,-CS[PzNA XwsY۩=NVtՈsagBiO:߀cҾr^Yjt87Z o˺u\Cp${dڣzM5E"U:="(@ P(Ofle}M^6̇Ȉ}^%3W{3Wx(9Z5erʑ<=nJOuhm1鶶>'՜Z-)G^{sj/Bmpx{pxz wn^~jf\Rӆ'/c|g%z߫CA}Zs##6I|\(@ P(@ PU'.Z6lwQq\xcX6U@ia齝sBѽݸ ՟_/@q UH$+עha&T hBRQ0FV7ZoΎ #0s6Oncdk=&PuۂjGN}glWܲ@oFV*lBӌd Z#V`vyڻ{wyHnSǻy(@ P(@)ŀܬ=Rr yG>/iàO/9>xTB^ӊwW#48s:UU4V8X"j!iC0:P[-/I7vg~9+ٽo2iS&w t%޹B rZ~;3iƺzfwTSﭸs>=[HFD=9.hEz5I<Ӡu&h} %XiQ` CТ {wV: }D(@ P(@ +xlHw^?<o;-k#SyЧ+=p$ W>O.s4d7cQ펃1ot;VKf7|3`#0׏L{QQ֢k⣦l$um6?b3wAv|~L*!FqDw~,lmVa  QND5 728v9ʟlF(@ Pq?D@j0lz?<avqn*D u5v‛w"bƌ\TA @sZ뉳!5sD]k7uwU*[Y{ݎ$5kv:pNp77\8 etΨ[2w(FNP5nƤwwJaFAʑ\kMrGo QYm6x1Ygjznk:y4w@=(u..(@ P(@ ^70k1C4%iΨ_P]/cVt」VokQdԿh[qBas+};d4 R4iCBUm:2ڶKw(\ Mn/$waw8r6]Ԉ-UG03+ge6_]Nag1uZ S}(2JR2T?T'f:2gĀ5؎le$LS:m۲*g˘a`gvoL]NhEze;vz5bg}INJc]|<Ά5{4 ė (:%6;" mgfU]mu2bT'<'=-Ꙙ7i+ށwW9ҙҼz#G(@ P(@ P\~lSͻqR^Q!G2 ʹto1&\\=ށH/u\ !6q3Y*b~BPj9Ɩl=nxWik$d{KC#-cޯۄwʥUci9e.,ː7h6V/AsY=sGQd(6Lci Axrd:n0!J鶶T`FTg2'%Xߙ=B_݀=Vm-6m}f4i;jB )QSOʰ <.i ~:CҲ,aJrwOJVUJ3X[͵/!Xs*[wXx&YcgQP3D0loS7gu>ga#0y@tfmIfu>~&dj*r֏@cbo w$xJi^.isK0M P(@ P0&Xc` q<ߵ:2s8]y}ܮ]cc] _.^nǚY%{Ojk*mSIjP\ .2cD!1pdkYcӽ{ֵ\sI-%Rk!qqu~CEyH#e3HuQ3P,/C.L yZd.TW@-04Uӗ0N=ؗU.hɨ}oLp1}Qʔv9V4q[ծ:/(6^'ҫ6ӝe= lmyXJ`BJcwY w?5w,لo[/)(@ P(@ P[XQP}i}t`89< r!4Թ{c6N&ǯ]fi ҧIM)NLltnW9ө mZg:\esݮ)w`Ez瀅5mru΍ю3{֭#_- S3ڢ|${Ζ*%Q2ŞK"r5Y+ʰcF]ߺͯmeh>z6cuzyNڏCcɈl_$>^+թS%mgb'.׎h5gw^/ "M)MԖTo5i8ӦMgem"Zf׸ٰݭ&=ug;"a#11й-GN#jUG̼9(@ P@_H(窫.P`yjծeu:c+n_@\(kkkIΝ/ quyW߮Բqe\:_V]ӴO]nZLW:V:l P(@ P(@ P.EIY (@ P(@ P(@ PF0*|(@ P(@ P(@ \@ P(@ P(@ P 0paT(@ P(@ P(@ P'e(@ P(@ P(@ `¨Q(@ P(@ P(p.N)@ P(@ P(@ P0*Q9(@ P(@ P(@ P  \R(@ P(@ P(`T rG P(@ P(@ P@8) (@ P(@ P(@ PF嘏(@ P(@ P(@ 0ppRH P(@ P(@ PQ.1(@ P(@ P(@ P`",(@ P(@ P(@ \c> P(@ P(@ P.EIY (@ P(@ P(@ PF0*|(@ P(@ P(@ \@ P(@ P(@ P 0paT(@ P(@ P(@ P'e(@ P(@ P(@ `¨Q(@ P(@ P(p.N)@ P(@ P(@ P0*Q9(@ P(@ P(@ P  \R(@ P(@ P(`T rG P(@ P(@ P@8) (@ P(@ P(@ PF嘏(@ P(@ P(@ 0ppRH P(@ P(@ PQ.1(@ P(@ P(@ P x,] tMswtY(w{+Bݵ C'EcV(|n l4#kFR\tmr77$P!ᲅ8C DWpŁ8=4↌]) 0))o}}y8t55$ORL@ \ Wϕ5h9sp6:?# J]6=ty9n"F9k z @f eMvs)Ѓm1eנ:D)۶fT(.Z}B?[v9vmgfMC3=]iܐ)# =77m7h[s5UlɎ&w;,gpt!1bL~q_s-XUJAodg Գcm}K͋A^7JFms5BoǶȈH// loX CFCnּg@b}njz)477#rd$u,kP^}[Ad$"M&Li !R)5ٿ"=/cmjKf{w\pnO.z\R{ \mYaN,r??r-2VuI9,Olۇœ,Z 3&ѾV|p׶QvPl/1͟Cls[?]/t?;`sqJ-,u;C#b'j^X9n{P7 oni,ٿAYfrtS><YxѥXCRó}?V ')ưtoޔM 7Chߎr?ko.jځ97 g!@AUe ڇX9SlxdwVF33PzzoCX1ڙ.}~lY4a#Ռ|Oq1(pWζi#:hzC;N]vPf|h|c#QXUU[uVl߾EJb.m x@~lM4^Fbl^UeWnݎ닜mvNzL[ tYU`tN"ǚ|~'GZ9ށo{߯3bGR@'6N7ny #bf48 iOɡ Կ4k[ڊVHPn`Օ}[X"="] m؜=N0磤(+P> ܭ\-HF~QV#+S'E ߮7ae&*vw)gp+PHƄt8jMo P ^Y iYKdPw Iʝb0"޶sXK":F D'`^3{߲P(zNe+9FFʼy4Yxs(F@93VжLĪf=Pm;Y*/={^G (dA(x+XKv%=qQ,Vs+uA~l H\-(Nd Z6uD1=sΜgið3sfy7}vPaM@Sօ߈aŁG9\.v:L~e#QD`?pHv?V_`|3X=CIs }klU O&0̌BaxϖnQ ^Tyb(؃zA͛廟W. N|wk3TSCE'~sZW" H w\\g`"bIW7yAu%oQ|mdmZ*==i'{+`kĞUő6D@" k#઻ Ԍ7_Ac8Kf\NLLLxsUPS󵸜޴'&pf|q*<3ޝ!cǞktҎn$ yWL8C\_|2@3g7  r FͰ00C"y! k:.ǿ']0i`h/W)6jy䫺ŔT}gtȻ%N$pi sfG(2}t%Q̌j2k6yt R=~C~Woۊ)y0&䢲ᴺsVdkg,vyՐg~Nھ܄r} w޷yq3̍#?Lq%N+Ho;*iΩοSh.@6\(e9JŒsNP/I hW:ӡGԱ&)]>o/ΙgF<96؅v)6nYh?;yQ2o*ay^c8Hԗ__Mb~C}-xCǽF/NjLӍGKN<,O6D@" ۍH gbRD4LnѰtѰO>090$|'Æ, FܤWf2(SAVF,2N&d$i29d֐a0i.ϐ32M濑@f#{K>2#c%d$d l%2)"%2_&d!(dݗ^z̬Y7%8nKOm^7eϻFrw<ݧvF}߸{jƯ>]Rc qZaQ?FZƾSOOq"otp3 tnnnu7חy_3{fʴd8isS#%wE+_chju X<X_<m򒻖#h,iZCwN%GP-7QlޙVo;j Z+Olg͍nG-7uf o,pjݗuZXVm> 6wOcPIeGu=j#U "3ê^_.pG;%~1buIem}~iB/ݩGq3srSKHuZ{`|ح̰9Pw,W}WgK7Ox-|źB}2_v^=YܝyDdG^w6 ϘCy_}doAK=Z;Ju69d֯ٚWz4PVaFLگm~OyhGk8pRTyV-o2Ϟh5QˎaI<_֬AsX;bry+*R՗[ !ӸQ[rҼΊ|P튻P`kIhH.HSL+|s2߾W*-յFwmu*bl'/oc[ ֧fM%(y}qgs7:κzHYkhgz.|vhOW%%є4U#eq+%F`$AzE'0+q~,CXzc_ c]9-ф!N5_ L]j?m%5G4k6eg}*![tQL%nξ^A ,X U+EVqwE ) H0k8C.'a׷x2SOP=c^☧*"xRbh|jMo+)sq6]\Aw(} 5G#n"c'#"S, >shuV!/Ή8)uu>]L,'}X. ǁӷs幚XT,xۧ8^JS =C/b e@*E|\[H'c@Ta8e'AP \Pe @l '480q RTT>]?,־m5;)5&j0~5NAgo&d_:혰jI2W:a _uLAk5[ݡ|ܨPh0=]&..oN/Kb(7V'57 }gva3K8Ji]_Oz@9iLO )Wgj1B+_q"3®LgǁFMsmürҫh+XNvMN_fe 9]~<,(e06bhl,b} H#iڜ?@Biמ?8_"-՝~2WT?}'Wj?%ӗ4XcUtav{+߰.5|^/΃"3i+wq\Qq;Xog1dHXZz~ژ'Ӷ w]>F8m 97-/֊F%]!jX %."K" j튐u@nc0pF :Zܥ{ ow7_ݫU{s%[qp:ħozӊ=x/yQ?3r u*fdMOjvَ)ϔyط%Su{-t!i<|(wsX\:Ssn,5g7/7ZEn9;z: .-=pw_2nn.N*D7N*_|mS/:KކfԺa1_~~~iv*è4B{n{DΚ8\)xydњ3{Vtt5Լ %ۼGͯ%yDc zi*eB>ħ"oS SC]0v~hܠ}VOsJ9Jcp}ft6ٛpbogUFŚ?[|v_IG'3^_vdnu,ڣk6"Gp{<4M^{ʰ&-O 8rے,ě͕e#nOFN+ݿ@6< c+v㙐@55&K5m#LloEIBfe9'~a{9[NFW@ jAmϳ4HH&)wf(fF:d?~IYpYOnEhkk lNڑ Qނ iqts;L /kITzIyّ|0F:e~6 W8 rCBstc + 8XA@)B eO zIOo"k+DZ'ACT:B$?71y hk#8($ gv~5\Q-O>ݸSo]Lς(s›*Z[dZR$ Xwޜ g_6 B׭{ <"13:-;^7RUA{#ګe,ZfT㻏qGؿؾm m+a%⇸q:]Z??*m@&~4닁5Sl#9N1|UX}wx}H)j's-/ gK#ID@" C*X-(R[=Zd]m_Hr>SW8$@\5NP΃Dߨlw瘯QC!+I"hvf#A.)$<%p?St?1M wLrKh _wM<ֵ@Ր@E9P֯%IP."`F! /!'.Z%v<_Tf!V;f̧߬B B9wvkNӕXQ\˕;k׮f.'>ӎB}M\Ja 㤮|֬LAnQ%wbw{2qY qd[BB&T@ `vak͌K :/tqD*S(?SRht5N N>fhEu,1 (#ɰ@~Ck8ziHACd G=6E;.iڙM`yBuI(Ι-@tl,7#0L鿵,Ya.`vcf['Jy#R+EǢ‚umW?}M+X ]_]xF[7ܴ'ܓis#kwߝ3SG}-jYy34I$@HE-֨ j,!\ niyu(KKB:`NX޹y@ Le$dRcgO,{Up;p8Ć5HGR_ 4je>ޘ^Z\_spdREµ~;1tNaB .*Q_kվ0ndQ \ فykC9̏$-Y~$Cӏ{v%5hluU/4az\ojvVŬ@hS}%7rT|,=#HTӎB~#o=5;~8g=yܽ-GÏͥ7gQUtybY=#'Ogϖ?oU9+ØGD‰.m~pƬ\.W4gG~ Ʊ/eS29.~%oV;d)1i c0}Z@.935 ?Mt4(9{)D@" zPȰu6O3yIIw'跹27iħdGo-Yޟ)č_IQme&; =GX7T\ 237aݟdnwql<7|(͋7R荽2jYi,H32g8~Ղ[h# ??#9pӎb}]'.L8р:MruxuznLތl[۔+3MNVr^C%2ضf ~l?Z1Qv?՗Yg(l;1PΛg^ٯ:呤ퟫoT 6,ұ@sV;3Uʪd:iϗ3@}±,s>K9Dsˇě30~YnTVLj)I'g)A4jT_<ô;rh\7o_ӿ>-gKqgiء4vNEH$# ౺fBNL=uEx⭸-;̘뤥YOnF<W8^|t߁(D2߱~_QZ5Tr(~ pfʇs'U=EM6Vry^K سъgd.,K' ~f&=sӻ L  KSoT)N^5[DjazcQܗϯ-mpkI֩ZLoejظ1vk"2 5x_;՝lu6tx4Y ܙs$qt":3NyF \Q8) gN/X_٬W뗁sn]cGT MiaOwyޤ.>DwfHxB3{p|~QSсWޘ@񒠠sv' p٫k3#t LwCO] Lƃet3=#qV^?4LKJˋh(]g*@>+'1*pUX{Na8IHg LfyI̾tſRE" HBG@:f>Fj:A e;k!P֢htb5[(k?WD'zGOh~lVxn4d; 8lws2%Ƹ'-#q8^[Qk8~]?N` 5ӷz4yA+O7~ާ+}G>LTZg.7o- FGF0Bf"4AoLWasy> xQQT*s(etpe\YUvlE?B:PObv\[e.&:)J Tš{#c9Pѭ"ش}/QZǑ}=1Yl/ar-96u[_ Ze*9Ovl@Ae~ msGh E089#o:B8G0 O~ U` ]ePT*N_ |(GDaLI\Bdy Lhl4'P?;B}+qtl*] t'vܡytg> ZD1|qxA}I3~n'LӕZ~[<;Aܯ1z('IVW=>Bm?C#y,o2D@" _\u6mrEPO6ueC cs>Jw4&iom BR39+A|8g:FHA)ʀz-L,m*3OkwjUiT\Ko5yC-(mO*0ʞM>#ʷƲjw5%A +jLߴ&бG~?UBaXJ;:tG nǓL)dtJsׯh+DSČO '~^*CUF 5}㴆)ډFh_.D4lkD0T@ B+{+wrh?*s/93;i&yDc Z\Vc_wqNL5^[|6zȴg7-<0~ŬH]A>ېFXeÿ5\$ %3uhIǩiL+x4\Kr.Oqc,r a@|mbd~'@t\Md9 [w]%F~[@W8P]wNuK,K3# J$sk7y U5euuhruߢ=n*[ʆ bttC8~E Xy~6G{{;صf+.bm&p]M(KXJ6ָ8NX[S9CHTlMJո%s!9&Xc\x>voE6dJøJw}QhPZ(čw=3-_cPaҾF"I||[CMY"T"jˌKu2P[+ЈB7I)[WaEEf[QV_b}iOg6mZß|ۮ)f/("!>;&T|+1_:1ttC~84k=BNv.T)q8zX0 Fv&d謆2TwSGGՁ#EHH[)Dr ~G:Vbf@l9)dÓ _{hEn+`5}A}3g8 8BM=g I\ c1ͤ+yDc Hj^7baRrKՋA[{u ␒e+$uNv۲wXŏܗVhyKA/GeLJf_bx+&Phï/;?U(nӻBbF6XQ;̉@FJՄAM8SbaÂ}5VWcNÜ8u'lY>|1%g::1/Ɇ"H#vL S-&* %d熉~O*S~o$4]Nv#dnQ̧7̭a&f"dd$i2ltZCOEdL2Ff=72l$s{ɰ#äǛ<@A2AV2_ 6*"%2_&d!(drKnf{&ݍ6)&ܔo\5- ek5 4t|Z&){b_9[?ސx9gktO| xY4ΔvQMe-x> =pӓEXƫܝZJ -ΫB X_k{u\Ό [>Zg9Yhag%=%:,x_ozB֜q_Ԭ֞ +}$="vAj2~I%bz8}k_s,2gs!px@s^tcZ$ӎ[(oo}P5{;k~J&Nڛݗ>B7ƚ1߀.BwTj^,XZ[M; ''aQHg8[ty+3#'+Yy%7e.b$믚?Nsf-}ڀXz~ %vj{ݬ~}:`Ќx٘quM2[O4܅'b}&xsZ11[cSۃ:oSK'%:0 Rk0VϷcߒKWϺIڳ>Ľa߰%'قFNg.ʑLyjM"0 _'''NJcw0)2*2*2+2+2,2,2-2-20.2/2/2030 313132%32-33533=g$dҷ*&~0Y6isr37Hed|?&C$3dKsٹa2xcrF3{PXC )`ڈ$߆+wpm :B謧u?jGɳE&`}@aVTӪ+S%?+ogc" -EkR֧{*Z+Ƥc`e99w؍7qz̒3Cw|Ϊ$kRT-y*䭨 F=G~1FPf& >jhc{q.V>y5 _PшKHbz zz쫱T/`Zj NaxDvduV?͒'KmrW:a)NzK8Joӷw J|Ҷ,&O<;9я7߄d6_>Q;z}Vhn>U<>u_cy F^}.뾤;Wg=Lr(D) ƹ7e.ts#]CtOsiBZY0khy>#Sc)9yNYqnL־[,VbvQ#@7!YG&iOXcR}D< 3%:^b˓('ԇuBk)Zֿ&Awyl:NI7:p Iv6ad,%83]m |h|-v\Abg-n{3xOuqGgq~L5|՗1, I$0cڏH8zLhqavٍD7SfaDq?濢?cs/Èn5M6Faab13$"UWHs[iGZ_c> l)+nY+n^ާ$l8^~-9}. -18v1wGq$Yv{ٚ1yρ",[.苺N =h@T^#;F:hѱ;#xMhr V޼s>567~4׻,d11؅qK[, Emf}q00{݀Ӳ漍X>woaw#4ô9ɻg?@;7E ;m՗(&-l~m,Y)tosGD@Ǐ炋џ*~G}qӎ OЭ[-Y.a?(}_;NKvy|fn_?FfaBu0i+^W0憇3v+U\0# Da">ƒ^N0E>D 6{W U80}tN+1(Y",r>_ŇX"  ⢏v煲%dhD`! 0s  uɃDRqSTlB,o ]a#?06etM%?Gi 5캾r),Zef-- (Q\ r6hU2.X#LfH$!O 2D@" nZmˌK۲HĶNY yӱ }I@u=eLD xԅmG!%D@" H$D "(2D@" H$D@" H$D@" H$(" QSH$D@" H$D@" H$D 2"2dlD@" H$D@" H$D@" HT\DLIJ" H$D@" H$D@" H$%D@" H$D@" H$D@" "RqE0%)D@" H$D@" H$D@" H"C@*."OƖH$D@" H$D@" H$D G$%\0{|HO]cCxcp| p>9#{^,2^$ \x$j3 )|f\.i'/+Fsi]z yђ)n3./Ke46؋Q +62su]P$D@" H$5qq L`dhC1sF`7ffnހMp 38Tw`}Ka!mX5wdpu&EẄ㻑$qn8=6a;Cr GOc7rTAG~%aeO?y~aT#ν%.4A)ȉ<3#I (=҆1WXVJBFH$D@" H$pѢw@ s`Ed .]$|65%`y=>8T.|-1yЊL&I7#;=5d$b^!s31\0M;bb`-Z97|$dl>[׬)u~m3o?;o!Sܦked9'~pj jJ BCb.H$D@" Hu_KXr >Sjԁ IHF D^Qiaɳd _}ν-55G֣eR {"e8KR#{PGm8凎y\eݩ'b-h9qƑ::A1D{QqVQߊ*v%4FDJaQ2if} v$Յ+یz\9j]dP" H$D@" \H5PfZ}{̔IݴJ; ~G1yW?L@&=mp nH%"3;O6UK*9}.QQG|JN&&fX`#:Iܾ~Qm IqvQڛy;b)-,qMnN|b23acd}O?(GFi:#MV /H$D@" H$1d~s@YVH^ZL:}K]jNi1l,ޤZan|-9Xo!Ņ%;hEdDZm}U>{cNLj!n!r0Cæ9՟۲}}k]7C%D@" H$ /Pɏ"8111gyr»2hg\]ƍ&J~|o`ƥtbִ=hXML㍴Fvs/a4f TmBREZr^D(ma@ :.0 9#6 f/EE[9ޠYep9"?ͳA|M?>uo7SD@" H$DA@*.L i1R$H{*KQZZʆ.9#ȍK@JVJR*q1 iTx?M΋m(HHPAa5?I E1I<|S t5J GzQY$ǔ$%ġmмL]8ݴVii)+(E{y|gF<ؕVVR0h8=2 kDq)a_ڀ!O]tylryYR]8 *o.Zg` ŹqHQjRPPzf ̢Z߈!t4BqA.8"7ET\JNu UĥJN'e 9(2גCmEM[_^4!Ni#))I-648[xgxk>'6> zg˖kGd}hʋ@IDATku9t}KJ P;,w`)I'ihhi~u BO5Ze6vUJrx[G~8\z2^;qhic 47֏E|x?P୛AI2Po/Կ1ǂ"O4h3 .bsϢFU9PT+D@" H$D@"p- v#}(ƙ1-߉n ˆ~E~v_3k^zomY&XXc,0\@;GԂlo.p,Z18qk$:qKxN vGX. .?aj! ٷ!]9<02iۚndupe.S'fZ+uy)K8^Jkc~FOGF"V̀ط\o>MGhl=nv􌟀}.>( yH}GalJZh1+$RJ00wcO?<ɨ~'qamdWi#t}Ke6q1LGqa_\+=%+Ύwa/KjBčGxkgUbâ? :A /ҎXg_ S" H$D@"xI&'B[S1GLJ~-_?w3963g=G{zzֈ|og53xDoD9,Y<oIu-qU ()-m݇o_D1/`vYySx,gi.u[BݎK[uhOM}D Ԃ+[TйfW:B3킶W\IP8\n3BNKWbR%Yt[u#}#nEP l*=W7_W[+(7hN;7q'u64wM1cs8K@}/ByDmEتMNG1 :XK:|~7]Mu(T|P3 ߽}Swa~y&hwhsG'-e-Z[nu8ق snZ~QJQTawc9w"RpҮeXu>h=&4v>ƆS.*bԷF\CPFL=$\8)r/}>_?:>P-;?| :߳DRZk9׍ǺM1H'n"D@" H$DC%sR64oBSihhrBщ~sFZS(ݶ c&5Hyq@ް5t4o_c*N`~fXϙ#tOQyQ̷1KZ%<᯿Zd|C#Y1@܂kÙhM5YħJ8ga,2uGShXۣDbъDMd/xb4p^>v8T)6C~N4]pFjKeF Q̉^V}OųOصmMחR0Sq!YE[ .8}RӨeYϡ^ԩ*jUJMPWW˴c}Yh$=ѫ-NzS؁QSv6ǎǪ\12]JKJ>sd?N:_XZAӭBGAE5$f)Hx1֜AѝHW7bAMv*LX{k=^،~;a᪀%ξD7|F9ʬ%f8n4dO86e~͟w_Qy>ghդeXݎW6˃D FGF0Bflo,]"M0G:c elKBuGOW._(~-5n[}k2Seսڅݭ/kG (,a]cJ,}K)7u`Ǫ~!S[Ur>dTyIuUq 4`ÚÅW,j=&aθvgHy ;-Q" H$D@"%nHOOxl;a-X\?HSQih%0'ՂO~,OI\E'Nh/ ڏX(Kp$5*w-_}w#xP=_%j![KぃxC;: A)@UlWz~EJugþ$2Sf)N& #+ YdJEX;N>oyQPR&ג|ؿuT< {RpGS?f0Վ~?;T*2W}k;^f^|SZR0&N c]2,ؚo DìVV[$^ !t;W=!a:}Q,&io}2532jU=9݀8j߄l}Y>D@" H$#L)zjr0;{wwz~"E6 HżCLG3j&b}iGF166sm(JU>F"o5qYbd-eH+DGw(w-YjF(6=kn0=^~9UݭYSKhٽ:)Jw őq0 B<`h]IO7$ʂ،"ϢFY?us}i^{YOuR-v>vԖS8=K<߮bjNJGɩδ1#F '$<"pxӢ(G}YCt_JHx+ q_o斃SWp:$2g`٘;/Y1. ӵ%Pz>1b(@F>}dfHMGow= /_laq$vk۱q6!軗2.|_f HHHHH#ĸ @yK_(=;}h8&5z4|q|2heݱW|W<+Z m/W~[&u #Mk# eAn -S6 nFrOUYpbe3C$@$@$@$@$pH 8M k?%w!vӡ$_p\'-lrs{y K/v's_o'oc#hӇ$ G`Tָf-ƷNߋ3SƂ^}:>U6籯`E*!.A ?!JSC$@$@$@$@$@H`_&qVxU;VvF[K;ifڽkfW^5WͬvHE=7XfE$@$@$@$@$@$@$@$@$@N^ӑ-ƺЇZĬvTsM/۪}\~otJ'BמG>R5|=WڽM#kg^˪W2?ns^$@$@$@$@$@$@$@$@$@$@$@$pH H\`9           H\H`DHHHHHHHHHHHp2@$@$@$@$@$@$@$@$@$@$@$2p2Y peHHHHHHHHHHH eLL^/FE8i`<$#- w㕮^ {&|#Ügy3Q4  /#](*ȍY󢿷gn|+L1+D4;-5!}QQȘƋGົcۏ/v+ #7g,K$SmMxa?֓MՆ>`~mBNj9d+$1'YϘ#NFńRǕ9ki<:ԏ+]eb֬YJ6x-xkq0|hJKE-e y}mط{u/?1u@w8ue[ldgeamK %KLm0 ?6mj ^njwZ/(X ށ>\:lF4Cc#Quw:/_ƯxCE/|k/DcEO]BO. :Dْ&J'{>譜W_ގ lli q} 'aś;^_0(ڼ[Aa|H̋s,CH˜\2gtWnb>@%ńl>G'~>5[qYY%XٻQbI|"!m2g9c׉2,tM 'YQ|[1 ,_w.xdlO=X8X^y2'CnS /c`Ꝩ UyX@pv*b<lt[SWufcَ](BsQ}5RN}#?i#/E:Nq3#I\(8){X(0ۉp_ !gomE8ƣа6HѯTgpSF1S[}8l9Mov 2-ZRKM۲ ڜleaY0ݹH\<lx (VxbݰG_8xv7:f|4~UsmzRώX0΋J׶[>-XIЬ`%ڷ . =7=b\;ux̓#cN.3\xR4d)*?َ{6ˣ"]8>o_m 7K;p%H\ĄcCS@F.J֮55:M{T@n4_99FrUq׍šS{O0 z/~x\DRهڠ݈|I>C2cvF:!\MWf73ß2nԜ֯t'] 6sRإ { }89ZfAλk2ij1yBs3$Х|l=Z/U -\ϧ^"K&N,,K_܁=^iL&[{7n܊2U-{-SU7]=Vwmf)_WqZ:vzs4dbIZzLl۶l쎕ps06:b)-XOށ8]PysrsŇa "^ Jh߈FanwC`TMkW`ib&)b{Ƴғ!]F VRPmk\.4`VT,P-(E{ئ[D^DzQ<+;XtK+-Vȣ~oP}vOiD۫(JҲ3\ CXSۊeіœL,VZ%8:TѐUJqnΞ?^dz&a>iyxt/ֽ$.-W7-t2l\ܢo`GNcxx$89E~U2gdw'#u+c Ր4ێs56mE3z̋aEhgƳSa2}7ve.8(Uʅ;oMJsFS˃o;WyXp3|#{m3zbaGLJ+pq$̑}ǂOkcbl  u4|OUل!㘚G1>',h㏡qp'ixOO_nׁu b]cGgڷ$2lQ `RjymjS_dgj]KD!j=L8%I$lZc@xNo3bp124e5ȳ8Za2mƮ#ݧcs/ܜ3 ߳#ѳ>}m8T21k,dggb 0Pk3Hwfe#}Z 7E(gs!tK^1CXT#3})*ovpOp!n0tL[|w􏢫l;L+)ۏp>Υ{WUW.ßD݋pqHܭĽgvdJpaO|%=yؚĹj}OAo[*-EzFf48mJn٦l= Vm޼ ]<`YL/ACPP}fvdCKz'!T3mkhM>u f].hގD%6hGxQکw=bG{ar̟KH6ZphWJ.ŴiӰT;l}A͢M}lEEU6RP&s̏}b?ÿJqlZ*3Np,,E=o Mva1OӰbķ6 q|G$Ү;חvR7};t]\Y}\pܱtO5Rf41W;/F7\kj5q9 CE},rzh_?/3b>Fqh#O7ZkQ j[I$?vL}cMDljy#9i`^j7򢔏, ϭȲ$r%V@N=Uw4vm6w`~*c2e0ػ.0?mKEr5v ss Lt}K$ qj,?*# 8I!Y%3-"WIJNnRkJnaZѠ[jP9A5K栒'"ԭAq+eįjjP %N-N>)|O{Prk龍R>-gZ&8UB-=B+J>'TP_ P_AB!,z"B="Fl[=OĮA_>_մ_MGGiow 'k;juw"sQU]#;z_}q_}MW#3C73Rw.(~pa n5͕z(3wRA_mkUk&u-;|z<[-a]/wEO">s&WE+#խ Uq$e(ŵ>Q㺆{|"GeWz#.X/Ǐ]F43Օ;Prqkqi5Wz9r؎Ip`G Ga?jH4no'C]l5 J>Qmkx"[lWZ봴}窊Mx'ÝǕE_Zc{ *u\>p'jJZ_쪯Fi|ّk!<}5^]BO.+c{%z:V*'QTi=,7uVka-Fmםˍx8WDŽž+c2 AR8iMߕ+W8#=xUK_ ^*F.VKJ]42^~a=8Ҽ'xs- *s Jwiml"}RurcG714LPΕk~ϭ=I)atI>ʳzHа*"0)E@ڣ6F-avrĹ%gɶoj6޲ih]5_}hȓä_788~u5]~]W7nFGGMJ>}^#o\^ꗍJh%J٩JYJ٪JYJ٫JYJ٬JYJ٭~N()ӕ])㕲^)_)`)a)#b)3c)Cd)Se)cf)sB %eҚ|zKA%eRɷg yk L44 ]g TR>]Q7 >͢>,U~U#5j ShbشH I7|&WEғ>#1&GR(LK,E\r_uU~×NsVju,-KiWmWnY8%%_hQYA Νky態aIebpW;km>+U&YM*&v #xܾj(;w'P6jBF݂9 RMf_uy!*o^}1Qړ@LUveҘh8+0OaJ}.j}־XoW&*HLƢm[Tt[#; \վv-&[>%`O=nBhNp!uU.PTkk|nC(̍&rw)-!A7셉`OڍšV„h %@a|J J"/!eqٟ'FڑHL2za;= :^eiljZ;+cq_8Lm{*}Ǜ;Ha'tu1^×;x..K*k:.+mK  ~,R_g֭{ԶM=xTm%Sxr\ Tr{v:ԫ (]Rxtq=Mm hp杹5vt}26QfZ0y}c8/a2r5ޘpüQ";ϫ}헌FVn!B+z^ݙ@/7)B2#$mX[%wICXhRʪסGmK[6TeaI֗@RJiԁu:W\%v riDj(V`]q(.u9'[J: 1ʁKo2IVe2Fmk8;'窻lkecz٨&fǞ #cC8waKT_f1)@IDAT8' v2i1-HkODtWa,Uۑ` ;7r+z&VL;{Ou47Ѩ]6jr-"ިm|UG6|#D z_ƽw\&N-/*E`_iz GXSY=yqQ.mu3T3^Iv3XT-dVdyQ-S2nߢMԸisSfk؁2e~f3Yn2/$uj?Uy&TO4'N ˺QX&{$0"ɶo늃aN͵D4S.p!8.DGDž<*$]truGy7t :z% bj&!Q߶JQ6U U)K*3D8T-ԴH[C8kcC@RwܼbQbfb ˷'P ڍ㍢-\hBj} uoļ"Z63v5͕be6dn[5/#mד ۜnQF7JGhם QDzS׼QEV7Y)WV*+O1d`JWSSs[.Mov}Rx7KD EaƎ#m‘Ō W9W^~Ym$׶B.2&Q㦶j;7k\O/O?Ty&RO}B@P2֓´6!^;kllV6%$rK&LK}Kee1ɄmJwyF%Sؘ \; y/H@صw*䇇.\}g jQ$O[ӯ|e_~'?.waHk.Aҝk~KOfs\"sbu^Kyv})n._5#"F9VS\n;%m'"<$4ZaNƨ%%(-4eJd𯇍6c\iKxGRMM&yrԮcX>ky,6^]K?An 񁣣⿡u;JtlC;I._Jns. nYU'NsR~a۴!NO:ȒOU'U(ڎqẌ́acpTy^ix xP_:֍QgިMZBʱX)Q}kNh`#'pD pY%h~F츷վ8 )GѬ]xŷp31P/h7ߡ6?X_?^v#>m]6 l6{gnhMWI m4ƅŇQ0(oDyœeƁu8x;z A\joBGƵgն6'\;c]W .N؍w˃hKkߚ ܴ>یGb>)=bVvҽܬq>>ɩP*6icU!K6Ewcp\7ϲ_F_kWc&>,{xcܜ n]qRc7ͦY ~2uޑ $hfд>5ž=QIM5V sw&&|'.TYah"n||Q+|1࣑tK?D(SxX46wYl…kSWpW#נs#2Xy7 ;c *Kql.&QG"Z9;0ɉa/0?}r1VX #o-ZX_y+6A<~ oc׸܌faSk:nw>v~L] 1(,(+0b!mqϕM( MoO;xNNfֆYN*?/`ca21Yl"W1Z[Pxz[SqZуPQd4҄bdomqv?paQww`iG:Z ۽wϏតH[P%Apb֯6iSͰN⁷ sW ChnK?Y֌y_P[,k6)*ځ70/KՋ:3dNm i>ƛی%yg]Ti:6_M~ٜKU$Z ޙrJq~\{;ţݸ/pHÞ^{b;v=1w׏C,262}?w-Y^=LGmca{_="ulwig=ߙu ,m##`/.`g7BL,(mFُhQI}ˎ2d-2r)jcg|9꩘GQz =QXy~iLHn~21L>t$QW$vz{:ʧoGOs {pam[ $Jݷc[?ӚklcS,Qzi]dYK!/?Qw7>iR`]U=/{`At[h2WXNad2Ν1)"8ԡ}F-Z(3yU'n5N([[vP{j[F9c65IyD\@&qDA@8&%nR34^ğܡ\x(rU &Iy67d.S*q/gb1jOO=ǥlU2>ܴMvȟ㹂ii ڏz izژIݒ;t,nҲrbvF#0֛߂;Q{-Xs1z@nRsQMȧ{?T@0G4X5wYOMW|y(B[j|Nt'Eaw;zlnY:f/kz] X 1ү7G -=w1/pްko¨+ޮ#lB]Tͅ!hc7Т->b:7Yɱf?;.sfA%m!rGVbQ?;= #kGjWJJ D׬ja&ʏ捋Nj=A7aX3y1*>#u'9ܾ yGz_oyr<>)mz*'8 H42x(L'gcоgImY?WkW{5K$.\I0]C;>WF1&isQ:v{z겖7_{oyCzLSFB_\{axDc[OZipzؤ(wc8>WO z\ֱbE=m͎C 㑹F'9IijP!EWKN_0.5v4%b2y7sܬw, bxy.R<8qB44<^5ް.C&()5I|6-zu-ׇ>!liúipvjF.~ZO=k.]O`ݬZT19 JgaQ6Gtu6>1-3ܒGHb":}.ص˶3@zo`~E-m2104 8 kYŽ^ԑH.XWQ*ֱURm]崯ZNc:Z [8!/DD#!]uhЅmG({/Pٽ7O7`o>x=-KFm؂mQoNRGkmF]OcF޷I|? NwW.%tݰSށ]pNt]XVYqime"V|c3'8 8Nh8qU}7#o/N'3g:XO6؍TQ'Kn|؋m[c׮LԿ:Zd-5;;݇ű8:;-{)_6 ^s3b?Z-?p{8$qp~NWM^cnwm'bl_I{q<.\`Yp_k6B%9'9ґhPoxdx3$ֱb'--dẻ(ɜ/#ODrS|xH=Z~Y8t]bn.YbX4Vm_Sd&čݱ}p3x͍H^z85pwuL LN>_|>fO|Z%ETaq*)V"7kJ4%5fGGJncB,;[J6%X[WJ8>T_L:_B..N>)H/P\*ԧP˄*rjPJV uP B$ԃBZ'CB}YB}EzDBm[=OV'ť\խ&o;j=;wMY"7N +qr牑b%z8=uk\ܾAs\Ku?"sz},GͶj?||+Z(jS|k㊳ U %[l 1.խW-=MUݘPqHy an=5mv"0C"a@yd _Θ>uWMannOt>ŵu\(gۨQ-wA2ڈs1CQ9.WMyAxM텭o񕻐5|445WEok[;}K}Za !ZZbMʸZR,LcxRc'oO,]m2L?!-yNmNv$v݁#0XMSeYކC87\ەhI^[nM徸3bΉMzqu/tZy"WCƬj|YH!>؜ .]JEVQ}>n|K'6ܚ򾸪׬ո#ryV벵:?7kcٳ'Fu8Yz:Rk_WYfRI~hZyXNR.(Mhfݾ_׾A8յk|^wu]ݸq7225{ϧ*\ړ{o``wUlTGE-TN URLUVUZU^ VbLVfVjVn sBIJJJK KKK%K-K5K=*O()^ʬo *)˖2mM=[[Lg ǥA%e<=S襒銺I5%eiaq*U/ӬJy$Ǽ>hgiv͔e۸ҳlY.n*ݍd5)Bst˶ U/ۀ.l,-aI^u~HAעsV^0f3=]+Z5;vsWlvԔv9;e[\9Ub(UKhٽrN^ۨ*Gqd;v.[Ikn$ލ+M(/Oe u>"EyzH 9InӍvBy=>_Ή˃㭗QQi{"X}RͦiKcǴ+eV-Bv~.ͺPju^Z{=jrr\ndbF#w9WZaţ3 P94הFA]sQV472]Z}T)nCc'xZhMgK|=nH]w a-F) eVc3smeȆ`yM</%QZj4^j8Nv剒[w<>FMճ.T2NYm26X>2^Z9W:{qf;;hykMmdƎ oBj=5FaDn1Ug&d[״HYk„g:>^h-z* 4Ո.Ԝ<l$=T|;vT:j2yf}Kb[&𪝺qq9ϷʀԐ $A`\H&0~XWXڽMo}xUi43^U53sMfV;}$"M7ֈMkGecKc ֵBв$*$C}yM\R3gbȝcߟ#c:̙wF߁1 3bdz0:ԏޞ7kb$3f[bNNtn}ӯ⎂qՐ8[ob7Dz;ͱ,G2q1C;Cb}뭸%7ON47*^}U̺u~/!. Dyo)ř~p1qqGYWEe͊L@W .b=FZ"1aGRLco/^yM 7:v| s^X4 C[ٯٳ1w^.ld}4<@ݺYǫ;PvIu<6 ӵ%]SǝpO`_nGqԿ-1h}X]||L$lnQ}hS䌋dNm~়Ÿm؉7Bh^!A5HQ\E_!l=ZO2Jm>)mtݝшߊ &K[> >Nxn3\[#Vu2*?)BD=Gf }DAfƭn dK;:NE+E oA1N`paZx$ _*\fj/ҽN{^}_i_z^ڱ{…" !Vg,Z_ߥ8!1g$@Sdn&sܧzr.}C_3\HlFufh!Vm[ǔ dz^Ӱ9П$Č^ LR.!L3g*ˢEv4עgme4I3N?u&8_t}`DyUE*sT "e  ֋ӻnipX]vl䙀 +>M渏k&OO9O[K~wg7H~m^~eopXI|Go^n#5b٦%q7VvB{)6zjHƃȵ>s-TLn^m+(!;`١e+zYX{ OIMjID ܞд7h<+%f,ޒ df:w=X={pK5 |wA:dW^zԵ׽]h{ۊys>4v Ń\XH.Eue6ԝ( K?m+$y=;\#oT@ܜȳb8memը\> W.XLŬfRďR #,t||3~籯`E,DF$0&&s4>&4%OȟU?ν˃gϻwGX$;FWR9p\jk9ֹ=ؼnE8zE$0 )Ol*#R6 bNDn'o-ͦD9gqKC'z)cH`ǹ~ZU}<[3_\3^>jfGOڏs HHHHHHHHH~qmǸRis쇻WizW.^vY L r?/           H \Hl`$HHHHHHHHHHH$.\ .\LV0"$@$@$@$@$@$@$@$@$@$@$@\`           H\H`DHHHHHHHHHHHp2@$@$@$@$@$@$@$@$@$@$@$2p2Y peHHHHHHHHHHH ep"e!           @Ed#B$@$@$@$@$@$@$@$@$@$@$            ! FHHHHHHHHHHH ,$@$@$@$@$@$@$@$@$@$@$@)C ) .XHHHHHHHHHHHR.R&+           .\ .\LV0"$@$@$@$@$@$@$@$@$@$@$@\`           H\H`DHHHHHHHHHHHp2@$@$@$@$@$@$@$@$@$@$@$2p2Y peHHHHHHHHHHH ep"e!           @Ed#B$@$@$@$@$@$@$@$@$@$@$            ! FHHHHHHHHHHH ,$@$@$@$@$@$@$@$@$@$@$@)C ) .XHHHHHHHHHHHR.R&+           .\ .\LV0"$@$@$@$@$@$@$@$@$@$@$@\`           H\H`DHHHHHHHHHHHp2@$@$@$@$@$@$@$@$@$@$@$2p2Y peHHHHHHHHHHH ep"e!          H#o姿ƍ8"?wbea3vɂn~S'bHI׋pn2P5@+.@ xۃ7>VKr͏']Wy XKy~xQe.Gn+A#H$@$@$@$@$@$ j %=!x{ a̜u`M^y ִWG~ށoͿ@W1>4d}_loKC1u-՗H'|T{ zZ&Ie|ik x sі>b j o1iӨ;p-U^EVrMfOa}ЛWcw7Ҧ~$\{j: 9_+m ;Q!̙-+?U; WVޏOޣ $(pAZJmQ07Nj:؂d$dɨX0 f#@-ϥ<,&a8r-"Sv$WG/`חaOJTyŅ}Xagf㩚GD=Q-~񻀧wWE n6wn (mFUYIR eM$\hqJo'.q HHHHHH&(Qj SL@Y\e<"("B^n 2$ ǗoML u⁹X"oV`jblYS\;?dGs|ܸI]k6 w&mǷo=K9Sl!ɑ6{{`<}q@^F|_;5&#<}!q/fARg[={ܺ jpvLC̸ azq;X0;bƒ9wWLŔ9HHHHHHR.R-G#> l׃GE}iCxJq0vM0<XGE!",i.f^yn߳q޷q:lyX8'PZBqRJZMBEyk˳ŀ;-3oAqW%O?MQy<*j6LO7SR$DYEڵFtF#.&t6-ZPcgN6-Zk2Dۄgڂ u]7h/=fwi2k6l^i܁ye[?OJCVN.*mͼ+alܼHHHHHHHƏ@DQE!?c=,u1d>f{Z[_x;_E3WoN{O@tӴ9Bj-\j8X;Dy #UXCQMF@5CT+E:!a'@!imѲIDKXP^̽/^E w=o'/of;EPݧpi^,t9_kbWnzޢE iQFD>GL  >䴇o20t}$Hq$= Mԋ8B3+^\7:a?FI_;ewB]?V8v`7hSxretϛ -Zxy!wɏːw2O-ߡ*oD}E ixcXOc/tЈr zp {%$4nA76TyQ?m}J      p1))">6=#?/!>^OX[̚PqqycE铘+}q!Eabs6oơ3~޾6*['-?K{&(Șa@t$򩕾 ks ҃Μ CՂCPt)Mⷤd(e(eڈjo!l_7Ho[*-5⑝-ʼlj.áI7[Y9-4l~3!'x3څ'!p6>i̞@~g#]df=&|yr֕``>mR۲ S=!eWšCh_!H-lqٴin"}b?)ű}k<#(y?EsBƻ"{F~ՠ;mKh7 V~w(ʠ* s~??*]#*vgg؎^lL՝^fertsUhҼ!      p #ғN^9:OPAŷBk-8pu`8~ftu7GX#Qq̉:~8x+'^3Oٍxk[Ďv]8>  ~bhg1\pMDEnHř`vH T90W8Q064^ttȳ`ҍxM0x-8v,UD9Z.z4;ԅ$$Bˤu}^UP\mPgwEa?R^Cp yh9x8pzg~*L@h Ġ)deM *YIZT6&ed]ҕBD٦B66v4 nH50TTceA;Μ{Ͻ̟; >xs{?s9ㆍ1+I?ޤumo|Ƣڭ)g]Px<+ߖ/0&XxFW[t?T}tˡ{ gV2QUw/ܴpec._5ڹgEYtR!5#jF|&ܣ俛> <#-Ϟ(HR1A@̾;fNlnhl}|`_4:%1xy̾kC|ˋcN!j%;lybqtm.6'c_::#~.\>d$ -wM[$2, {\u %z2i{bӝd;'6<|L!5&N(>PT.K _cwuhܻH/3KE۳zj]; g-HlqqKp1.{1486.Ha\<9µie)^^+>:5{U|qn˿v?+0Oouؓ.j\q P @8^Ǜ8R@sŴ 4hxhpB+?ɯ ( 0DŽ 2'W3WaCcS㪫GX/pn]}=je R~ 3to|z{:zh~6կRgQ&;'M^z7'z7 ߵY ˳W&=9J&`xejfz8btrǷP)FD_Ht w= JƛߞϋӲM[;ۑ$$m Y6⁓\.dAIMGƎUώ$2\A9I[Ioygo k`lܯngG4)Vf^öj5ISC㝗f; |Wgz%@ @}M@ࢯѓ:AK3*~OIZoo>{OqߋO]0cGen+b=)*$lz.|ƹq)t'bK޼14fѹ%.-nmf@znձ1U(,//*m {x; +ΌiuNF=ٴvbQ|3eֹYl @ @@ \lhvx}jjݨ ס'D>oz~&Źm$- L0&*W^PtAif w{3fXV8{QVi9'+s)PioZo?k  @H@"&Z8ܙZoƎxa,*oJkNC֧dA♧ynEf!Bg]+ gLx6^F_/]ܫԤxEf$ش.šu7o?>P5'K#f?'Icޣ̢Y8[(V\q,}F|W q}7]\zɞ_>+ݙ=Rqm/]7o- |G_;O$zwR[İZFm @ @nF%;(^_׃:z8Y 5|oCrzd%=E-Wmv[Bہ1ڈu?|k\*.}\>04J76Ń[_beqCv`bF!cHGXv@iܲbc=2U6}S+|ƺPYk;+4~X=u{Zj^qt裱ko"f;*L2kֵ8lcf1?w>Sgb t< @mxŠLV5bzlOsܓԖ7Ž ;ix[Sy[q%1lزxbIzސ=68NJRɀb|C#v>CTrx@oK/cEPGcƵm/ԯ Q \4λ3k1u¬B^|o,+޻X({ϦR´nK^U8Z!ɈemDnpѿ cͅkMYK~2绱xB߷oV\|6h2 K zJKC% rǻ.J|C+n0?-=K[h}{twci!`Z|\߿8btLG~ZkG˲Lf/0>:٘y$k;;ފ; ψg^@ @tQ;;/oks ?_ۍ1_ù'Gcq3zT3nzGq#~kKܨb?>qWiNJcɷX*ii΍"4hP)xoG+,`|΅ј<zҙϟO\We#"V5Ί|%/| uWah4Ytر$Xs{5WྡྷR:<(&6\Ϙbe䓈_=|m,٦ݙ|96._iueL~۸8MÒ1?^b1$mnB̺5z7ο8yyU||܁ʍ[QzmnOucDeyWqQZ2;_ly_x*5[aGDF}^.=lA|C9my=[cĖـq -le|I:̜$;뒁T]yman]:W9;;Oi\88vX; qԦFpQ;"@ @Sg"tMƟ^VZߴdsɹKu҇o4S_GqSgњ 㳹(CMX%^ʖq~u+-5ƼYه'L@+w(;޼$f/%)]Q̈Hvq_|3o<7noy޿|~C,^Q}1?2`5at㦆%1+ƕ%3қ㞆lZgZ{fuqc~H7b~E,}d_LJm$yqCI\}pEW%{rt%i3-6dkuQ|F!xM/-bl8\X5V=P,j8>`ovdg_ƺ?oS\{ךLF.~'6U|eRzy}âx1wqh>FPIJV6*l0<˸B;}73*_~6cOg':>HpZ<⫏ 7 ֽ=Z*n}سJ]*6?!wcѪl+j<({tiu?[}L~EYS:#U?- rg6S %w{W?jب%\Rg?*y}Zz<͸>Ji>|ig~ GZ)F0gL&IX!23?t~(X(MbßR<|x>y?_WMYY}V {㙟?$~zyV$ӒWL\<2$C>žxlsqdiƳύqZsmo^~q<1Eߟ]vs1bro4,zN^x܁Hr$ן\pn<=;^>Ж(Dӣ+y׃_p:n]>[n5#2ɻ]=`x2;ƑAc[3~JyxE\}ټBL$dw\=$Dw @w'c'7\u{]~0=f??&o1GI'[mmϥmeܹ|v[~'tL1]xq{zOc|i1Q83 浻AKդUMtӉߒKR_~ܞd >hcZBCvA|#Ɋrm4p(>ai \\q"xݙi"6m5j6Çrv`MD҇-j @3~vt]|$Y7y :|%XDuH>߲fCb 4Ūcպۢ4h| 1~ҏ_k`b'47O}^\{ƒBIqߎ ,X|B֋8aVɺNi3o(]+$=gK @@DXNr#UϷy>ko<<;ӣ9oJ䞱:sb$MK>L4Z[dw-@Cdمw/^mn\qǢY2G~G~ߏ홉 ꓀ߝ-f_>JևcFHVxlp+{KH>{Ѫo͎ ִq @8!bsQsn,u_Wx%'O n8Y#xRw[:eɫjN6^yC"_vmcb;-㑟'vɵ]:#Ź>,tR1SgƊ[?3fwshSKcQo.S/qgv2`x0X5Z$.m=!.!7.Zqbw't+U  @ @@8wع^vn?Iv<=ۦl~8wFDh?=%}l{<{a/wwzBݕ4N4oDw+d!£#IƝ2  @ŹKN>h~ˏnJ^[urO?~Ng-Ν @-CGԫnU* WϬނUq̉bD  @ @rC @ @  A @ @.| @ @ @[a  @ @ @ @ @ P35s+  @ @ @ @ @jF@fn @ @ @Ͼx`&'ƎZAͶ6??V'Ąc @ @茀Egbݦ=l=GbX {ǻkw]:>>̘_.}$/{xص;7/w88vPkcu7ƻi`sEؑ9V8w-;N @ @ \ӷOyE|yMM|6Rw\n9Z B,}pxCg81vium5brƕ(^Xh\hM3 SGqQ˶(7O @ @N\t/U=kmYТ>Y|c xxzuKj>xzܼ8&Uǚ-Oxd_|hj6ˡO[P|r%Zqԩu-#ǷSb#ꏶDZKfߜH|\xwƥ6 5(qLK @ @ h{cݵ25wg s7.364?>a|~7.44p`'Ju|1D/_޲ .zbǡO2|B_cǩ꒟©#ӑ\K @ @x]窫~mm9[(qNaoI;-=Q0'iO}[qG @ @T'Sڞ/\E!뢝:btکөSuc ͯߺ/>0ix\fh i{jLu1už7kN쩾Z @ @I-=7/kEAAV_'U @ @ @ ^Ǡ d_?ht[E}b1ocm\q˳śi_K @ @=' ps5VhdNl.Zɾ3hԥ1sǪ҉5+e(=o @ @U@[9kA׭ȏoےC#Ynw>2qZW2qh; @ @ s=g[-׍VS;eC nz|" |G4yBj1qZ <0L @ @=) pѓ5x' #񕇣W@[WcEcҝ׾>zqEkc Ymس%Vmgx|Scp]a,~{2 @ @E?iʆxRS^G}"~kLh=X?2rqhO|gO*IH8Ƨ +;%MOԴ7{xnO[Y&Έ#fěft_9lXygbw[.D @ @[~Yݍڡ4ď K>o(Tߥ#O~mY|qȹcub!oy{RY׷{v}{ö3>Ov>6VsG] H?p4^޹>ld:QaO<4c~% @ @ O?Ӯw'Ws-;8?vq҇Swç"nOߧ[(M8mqc\vηせ2яZv_W3;a^s7Ś-Aܱ֚GϹ‘ y<3qЋ#r\%@ @ @@quL&}[r瓽y(>73`]qq^o|CKqcc~)V-jn߸1rqC8FnW{3j{{gؕkbԱ'Ғk @ @ Ч~ܧi2  wm6CFĀ_'ygO 39F mq#:^9N?;:L iwnK#^</*:xZ6rd>zTkNl1Wǧ}~O<]qGSc. g\cƔft[ɢnde kX=sҙ^ @ @@OM\qbrπqr1b䧕s||Gۗ%}Ln L^fސ9H @ @^y׷:#Mƨy_]u"C37Ƕǒd̮" @ @ʼn۹G^#~ś$Хőxry㋯kt @ @*Jtλ[oC:Xx%tqM,s.h`1 @ @YYkZ3/;/=Ic o#. 6 @ @W@Ezj @ @肀.๔s΅A @ @ 5A @ @tqJ?x3_bE?0m @ @t @ @ ]sigqFE-/T;/R{v7l# ! @ @ @D@E  @ @ @2.^pa~o.]ߞwyݻ5k䷽?W^ye?Hٶm[s1 @ @z~ @ @!}fT5 -[t-#F/ZvcٲeK+&W1u5kVc淩K~ @ @ Џd\o @ @ @d\#YreLׂصkW޶qhѢN =ͬH3Pҋ̔kצl  @ @ /d\n @ @ @6d\}ZbE~3gsioiE{u#@ @ @d\Tn& @ @ 2.z5{ܹsAŔ)SԼL @ @" Vq @ @ @!—[֮][ޕW^Ywmۖ{T @ @5) &oA @ @ @) Kڞ4iR~oIf͚>'@ @ @@M ȸbP @ @ @ ȸfk)ݻ38#=dK @ @( 15E @ @tM@E\"m۶)St~}5'@ @ @ȸ;L @ @ @@qoamL ]"ɓbئ>}zI @ @ @Vd\1. @ @ d\ÛS^~}I}m-[tҒy!@ @ @qѽZ#@ @ @ x.- k@G&Mv۶my;O$ @ @ P2.j @ @2.M)i}-#auΝow߾}myɓ̕ӧ̙ @ @zg͋ @ @BҐ '(9؋wL4㡻bŊjꤴ:,]4_z߯~oIA  @ @'$ \D @ @􄀌PGm_d]tQ~oٺuk~ }>':+2i=Sݝrs @ @}G@E߹fB @ @z^ ;tt}u k84f`ߖ @ @]qUA @ @ @& (WC/R~6)Sbަ:3z<{&cǎ7ͧG @ @  @ @ @fd\̭])_ tۻfshYáԆK= @ @_@Ej @ @8A'/۲eK A׶ؽ{w~<&M*_gwM>… Oװ9rd~?<ٶm[~ѢE3+W,.]4<7m_O]`Av @ @2.=6C @ @ kd\[U[M:HG Κ5+_vu<:,8u͌H3-̈i&K[.iE[*vSk[̙3&GOI; @ @ 6$ @ @2.z}Q)_>i}CI35W~z)S*zrJzرc'… ץ]w].3gl^iVFFNFFyos'  @ @2.m4  @ @ 7d\xf%QaP~ӿOHغuk+Wi&A_w4L>?ьBo||HNL\:N?Zi? @ @Hd\ @ @ @@d\Tlٲd'{m&Ooӌz%ZI3ҵZҫ鶣:N34J3-IJM @ @ wd\{i& @ @ @ ȸN HH{2eJZ/̐f\k9tAPvc]tQX"<3cڵ%Sxz?XlY]%@ @ @ȸ?L  @ @ @@ ȸ[T[\2ӧWkgر%]VvL2ZWJ3 y&YrePz>+hIg͚f͚6xY~}zi۴t| @ @ Чd\kr @ @ @w ȸ]j}饗}t Z~if@AQxҌZ t'ZoÆ Ko6][__m)p’zNZ'iEAf`i{ƍ˷- @ @+ {3'@ @ @5' nIm|mt t[+N3@Ҍtfb5t͆KUOO[ :Wi{ ,+  @ @2.  @ @ @@- ȸQcI3! ~l/袒"e\;vlv @ @-7?@IDAT @ ȸ8z#@ @ @qSEt bMm3A֯_ߜ9sZݻeZ  @ @NNC @ @ @-m8^"2eJZgO-w`|Z )} @ @u} @ @E2.zͪPӵ 1L4)-6]bڵKmzܹ֭sC @ @ PEU5 @ @ P* ^@i&CY۽袋cJ3.~ǟqbŊ24cʕ%mٲ%t6pI_|#G ,o @ @}M@E_C @ @z^|NӌZ_"gyfEO3׼Hmߒ%KMi?i57̊q叧M @ @ @ȸ+w< @ @ @@qnbON!]{!#Hku[~PL4$y-j]v叧k[̙3^Q>Jv @ @ d\h  @ @ @ȸ+w摮 62M3~4f`̚5xOgכ9sf%  @ @ 'd\jR @ @ @w ȸ:]s!\8iwSGSLɷfI30VXM=LyEyt|Z @ @ @ȸ/w<  @ @ @@/q nR5f}_iWm'O\2t-U)wh ^z)?ڎ288%] @ @ @K2.b @ @ @;d\tfj+]"RGiZۖ;]CxO{ڵ.~ӌ YlY9sPO @ @ ȸ*  @ @ @rA`رc۶2 h!:ݻwӧOO6ͨ5kV5k3goS7Hmw @ @]@EoO @ @>t3s* ,fVDL"ͰH3(5+ +W-\07n\ЬmQa @ @>, \S#@ @ @M@Eoc=<4#!fܹiWo5:ҌVDOYb_lik~ @ @ @RJG @ @'55,vU2+V5 Ǝ[rL>ݸ  @ @ Яd\m @ @ @d\m۶6[nm^o=f:&@ @ @@_q @ @ @^$ ݬjE]lY-[6loa  @ @8y2.N @ @ @.:r @ @8y^ukPK. @ @ ȸj( @ @ @U*  @ @ @EVC @ @EUuN @ @d. @ @ @@U.ʯs @ @ @ + pP&@ @ @* pQU~ @ @ @Y2 @ @ PU @ @ \d5  @ @ @ \T_ @ @ @@V@"L @ @TU@ࢪ:'@ @ @Y e @ @ @U9 @ @ j( @ @ @U*  @ @ @EVC @ @EUuN @ @d. @ @ @@U.ʯs @ @ @ + pP&@ @ @* pQU~ @ @ @Y2 @ @ PU @ @ \d5  @ @ @ \T_ @ @ @@V@"L @ @TU@ࢪ:'@ @ @Y e @ @ @U9 @ @ j( @ @ @U*  @ @ @EVC @ @EUuN @ @d. @ @ @@U.ʯs @ @ @ + pP&@ @ @* pQU~ @ @ @Y2 @ @ PU @ @ \d5  @ @ @ \T_ @ @ @@V@"L @ @TU@ࢪ:'@ @ @Y e @ @ @U9 @ @ j( @ @ @U*  @ @ @EVC @ @EUuN @ @d. @ @ @@U.ʯs @ @ @ + pP&@ @ @* pQU~ @ @ @Y2 @ @ PU @ @ \d5  @ @ @ \T_ @ @ @@V@"L @ @TU@ࢪ:'@ @ @Y e @ @ @U9 @ @ j( @ @ @U*  @ @ @EVC @ @EUuN @ @d. @ @ @@U.ʯs @ @ @ + pP&@ @ @* pQU~ @ @ @Y2 @ @ PU @ @ \d5  @ @ @ \T_ @ @ @@V@"L @ @TU`@U{98|l/pן51i̐ jUصxrkmU(4-q7_ @ @:%jJ=ǁgȈ7ƨ*|j5=%ꗾ>/~C/Ůǿ>(>2=ɜOmp|{Nc~c߉TU @ @8ޤiz:?TL^ j+w=~䮸X|΃Gϵ[:olv$@ @ @@?mg{4؎:mwL8Ե-k횒E겿C]jJx|oxj @ @ @@٧mro ԝ_WEC4_$[fy=4l^U˟auuQI.}S'Fwk\;/0`P h.Fw؂  @ @ P@b+X*pj9fTYxGC5:FbօkR=[Q5>#@ @ @@x]tAKiHZ455Љ6QA/U{.WGwuZΡy^U>r @ @aˍ7ѹ_N~OǶߊkg1lbĈe1dF)A|ftu1&w2&N,5jd` @ @- 8t^,ɳFc;ߊm&x1\`ɼ~Srw/7AK3enx7+י>Iu:Y/ʜx+ChrnPs_&U2; @ @/N<ѽӛw &.Xq{ w ]Xܿgǂ;qH @ @Ź{4*Hok+G{D'.Ϫ_&i+$Ɖȉc BlF1 +d"(L[B`b2!!d+n)?G}{ꜺT=[Z~k>췫甀$  H@$  H@$p>ys|m?O'wgOǟ_w?w'[_Iwp$  H@$  H@$pꮔ=&ЯpVMe?Zҳ0KПַ~qޖ__Ŝo}_wb, H@$  H@$: p]OEmUK?v;D'ow?#9>$  H@$  H@$p3꾙;C\5cED@ln?q؋H|( H@$  H@$  H`y=j@7ܭm57ͯyN g=/ g~tX_K~߸G)Ao}iS$  H@$  H@;t|NpD2Y5Z>+78cߟ<|+||obo~+w?s_}w//?w}.O6֘$  H@$  H@MHgdnݱ.z]KWSڛ\أ |> 綜l}sdΕg_˿}[w?gv»߿lG{%  H@$  H@$ s;9[;ZlfZέU=O^_]F?Vʹ~WW_͟;e|ݿ3sw?ݏ?nn# H@$  H@$  #o}uGz:랗l~iuUݥϻ;3]\ij??j/oگ|Γ$  H@$  H@$pnH;_q'o~iW4>[)ؗm}y־So?/|;~~$  H@$  H@$@įʯu7zW;}|y_l%~N;:s>䓧?Gkp[4U:19: ~o)V,2OKן#_җOOȏ~Fp$  H@$  H@$ԟߴi?￾qŋB_qSy{[mc9O"j+}ѻy3VC%xP/j=WZ%k6_=sg޷b}Oԟ?}}/~?㪹$  H@$  H@$  ,o~_/RaBF>=u~{dm\s[qfǣZї(G^E@]bre?¿fW\eaaKϜ3_]}̊ _?٨UVN~Ŧ:kof֜$  H@$  H@^~ܟ벝5U[3/2&Wyj=G--3? U___{}S5fX4}.U+sg'ʒg>qZxT\rc],/.^ʿqSm}@"|[^ΫY?دK}ݧzxlֺ_1ᒀ$  H@$  H@W]SO'e}b.+΀.㑟sxt+rQ(wI@$  H@$  H@E{Ñ\_|~tlf?eeܩ~?۩snﵽΡ{䇥zja?YrFSgfene|C7$  H@$  H@k&wKdS;:+wқ=皓3/.} ~Nڇ^>qN諿9rYhzapo]Ԭ{WK$  H@$  H@#ܲYK3>$;9_k{Wu}©}b،j} )|(\:7};CrֽWq-_ʹ[#~#j%  H@$  H@$ m{oqۻVUS2^ݿ^65?ҒK]gzfSf*_\woC5=qWΦgݛCOxo]}_+ H@$  H@$  #}azЦ-{vmegsmGboǩ~$  H@$  H@w[6kVz4[φ~dǜnӃ:bīvohNh.Ob9_\! T^xo?@s9ﳫzt5c뼩c?rG3uW߇S/ H@$  H@$  \޽\g읹=Ԟw^/.>~>?C;ڟZ3-:>Q+Զf=wQs~k63K@$  H@$  H@k{Џ-=>SKꣅۑ6s]_ht+,r9Ȳvܪ.{n޿!ye{asN[uÎV`gcW<|j*5AS?Ō{V_$  H@$  H@8#,hNVJ՛?گFr%zʟZP/ݪn]4OQޥ8Su9gkY3QLS쨯[=U+3']$  H@$  H@8=\g<,<>|U :>3R?RvMţ5˗F4Gu[s^L^\/W>վs!3VLM>F][=F՜h&liI@$  H@$  H@8FxUgL䋋.f}fyzM4yZ9\RDKoZo^p>>Z H@$  H@$  H8{:;tQ/;E|2O=mFڬg~{fz4,O{f|_R^\SحiK]#NOhg]ٮZj=rS'V~k/,7iE=rT~԰+7Wok?vjS#N-]h/0rS~{$  H@$  H@$ z#=j&˪e][1zԶzG33~>gj<^J{\Կ]n憎r=_qљfE7^*?[9tO}#З$  H@$  H@ p7}^F3fG[g:4eY|շjG342{r,gUs1UݗPJ@$  H@$  H<M.5xd{.Z||#^6ˏfksV.kؚ1[YL4G{C$oҎt|5<>Uq}#-<`.q/1$  H@$  H@N#wgyG7Ve"O۪ךjكl^{̾M_3Z7նz-˞⋋KAptl]WgNmSVV~?|/sue-Mնty }1f^$  H@$  H@s{t.'e//9RK[ϕ5h/?zg9UƷ>м}h[Qm[srRSvMє?VzfЋq^9[#O}P_h@H+ H@$  H@$ Oݫ}Z{O[U>k#Ғf-F̕Zɣ-h&슦wig5[{qs%{gQgzR+M߃ZɍE_o欭ygAw?i%  H@$  H@$ O}\#_vԧ|YQ>~V4虓=xFbH4icv5zl߫1;cu^&}P#z>(_\﹪Q9ˬ^K.Ζ$  H@$  H@ػG~ qV9CGEvV+W?fyh]C=+hc{hf9\_=X$  H@$  H@{}q< =[Ҥ?kh4ڭއ1?c|,*NӃzGv4o5(\ngqs=^dO+Uϖ~RK\64RFq<{dFiY9k%  H@$  H@$ ݹzٕ-}jF>y쨖?gL+ߊϩH=_~@mfdkGs4'Y~cOyK7R/|t#Mjq(W4X$  H@$  H@x^yG8ڹ{L=[Ԥ?U"r=?rzKҐ'~{Y93<}Wi?ċ lf>;ӵ=K3W~-kUfg_{ck~=.(WZ=F9+}Y[ڿ H@$  H@$  H`^g6uI4W<>9e{{ Befg_hs,٫>)!^\<DžyJF;VtB`W6}VVH-85+ggm/V-gK@$  H@$  H@޽`Sԥģv_TLWZs>Wn/ifߔaՅ{hw6'.[#_{\˃ۋkK$  H@$  H@xys=Y>3W|bSў~BI[=kƵO~obB@V<,VV`˯>07kU˕TM-~(~dgT3' H@$  H@$  |x;EN=:ݦ.k&394^=gK]?$٬Yf䣧6o@ҝcO}f댼@ͭ\-4壣+VSzȳ>. H@$  H@$  H|m43N^rf=k&Ut]8ςgVs}VXy֚sl3Es/?kyswt`Z-ɕ:[纵g$  H@$  H@PZs>rf=k&U*9fj3VϬyޏ訥-=<̮?^\P ]_ڮˍ5ﻢKȯ\}!=3HK9-{L~Β$  H@$  H@C kb=ˬ-]ֶ^qUzj˯EzV\9Aߺ> gcWu^S|=ekm큶tʱ*W_Kbԥ?1cO\{swj3$  H@$  H@k 0W={g~RY4z峖~t(fFܣZY^Z3 z-g&̧{M/އP??lV<wM٣rӗ$  H@$  H@nwO_,YYK?{3Cۜ_HKAFCiGs4O}[}1s,V4yت_ei_A[YK[d ~qH3ғӣJ@$  H@$  H@p/.U~L<~'Zſ_3ڃ7tY岎me.]Ξq =r+}r~Y?/.rɐ=#Ǝ4+*W3YtVy_U.gѕz ~5'S# H@$  H@$  \8}XÎY/_=FL?vϟs.nGM >Y5lj#,{݄[냙]o՘gzSK}d575,zGs˙{1:~ H@$  H@$  F~-aC۬j#_Zbr#fϞcG=ԺenY(OvKU&|qK\l>w-ϓڑO.{5V mYԈs&5,]WOW{>c H@$  H@$  {ٳ~W/5,*&EcK=>6ʒR1I >FG}ٓ>K]:Ӈ|qt7uQcy,z,y쬧,I:qUh^f4\9y{4O~>V$  H@$  H@l]U>9'?g匾}جVVŵ}̠̎gOL,y,y(IWOж.♾2Nrjul[Зezd\֙9ڇQˬ#}|+{fϊ^$  H@$  H@$@_1JY>3Mck|lsa{[Z¦sLj䰣lGg]ۋ`uYtUoe<37ڋ:65b߭<ǎR2hr9t<=FOG3WOe~'u$  H@$  H@xfj3O=9V4,{vz-jٟS}1=[7}ȍ,3'6793_#` QO𱜧bVIj<d_>,{7g}r33:sУ$  H@$  H@k':zүXV9-;ܞ,zje1RKϞ0ԛ.s{>{鮾^\$N4Q_>[uW-ck|,ZK k%  H@$  H@$ w}V=kXc+%7+?ﹾV}Vct+Q=geH=ޛoE}Hyq|K;Ԡ-4Znj;Zfϥߚ=[:k$  H@$  H@ AG-m8khzn++{cASZ>V=<>M~s=Fczҋxs`-}=Oʗ>W쓚|~Y񊟺>ogO,H_WkhҟzX$  H@$  H@^m!'ʗ&uxI_gA%%v̦4*Wzzojf*/{+ФeѹRW8nk1kyjjMU̙S[yuUz$  H@$  H@$OeG/ 13e1Oղ=GliklՏu1s@Wz~Y]ӟΎhF=3n5_~ʹwSb$  H@$  H@$^}/9blfqWjhR?miz8-gf j1.OȨuԦO}f5 g:53FmV:o+'4,GIDAT|oFO,}}J[_g/{\R1:,ub#J@$  H@$  H@=[تg-13_jnd3s7E⴩z`у.-Y+gGm{⳿~S~tiL<=7yE]V}ˌ]1Q_Uk* |뚌9EǏR=9 H@$  H@$  H`N߯N؞ jGL˼&}4QQjH79cwc5G~so~>`̫KH?i2eN:5,*UgHķ<cT3;З6LWkhʦ.[=ӗ$  H@$  H@k$0WH}a)9ȒC|fK˜JM3?sK }WiEA]NwTw%5d-s+sjRV~63zL~ds'Pӟ>X+ H@$  H@$  ;1zu9ܮ,j]CKF\Ҧz3ɣŕY{Ѽ`58Jݙ߳}Ug Wo*RwXfe~+UskKC5$  H@$  H@k#0WqH}a{bjQi{xG4ٳ3mZ7 rMz=]u 16JSk\鶴C[?}Q~E^23,9,gfO*/k>˳HrZ H@$  H@$  H`@_1Sz;U-#kr\^ȥ9Xj9j,؞'.ˬ̍¢ߋȯا_\ӋC8.」9l!|T<s/V$  H@$  H@$TjeU,gWs,ԕ_ =+?V1k%  H@$  H@$ wp,'Ƣqg|blGv=#M*}Q>*eff~kjf=ׯ:@fߝ=h}[5t#M߿49[ղ'VsgN*ggY%  H@$  H@$ V}ƌx͕lMFQ-u#޲Rcj=?+wd1\֯¿G`&,|,{<ˣ+[HɕO>ϔsfe|f1+Zs~j*59W評e,ⴳZ^_$  H@$  H@xw/T~n+ڞO[,Ӧ_Ԯ8Cߣ{ٓ}z߬O3Y>_;gQ~+k8e븣xi3\r]3|ŵǎV,i^ų\k3~$  H@$  H@J`bxT빭8k:7~Y̗_nS7٧jzeܚ7V=k[v(?Ϝ+gVH_Zm1cK<Ғ+mQ\f1$  H@$  H@$0'R9rՓy|liuzY/s>z9>=u-;Ҏr[3>h_\ȼ^{žQslh3|zʧHK.uc{5} H@$  H@$  v{^˽-~V.K_,_=C|ƙ|1}Xtgߵ=FeOٚw5EH_Y`v{ѢxWUz>FP~*Zr\YǯZ-i+?ꝝ9̥Z~}2z$  H@$  H@*QzZ{ҥa+xd3_cϘ\kuc;5Fnն=޳\S@%lha^i==ϼa&O=~*WC"l嘝٣C:,zUcOT42OQNK2WZc H@$  H@$  H`@ˮZOma's[QzǽLy9ײ4,t؞Z#-7|je,'b.5Y+|++kӃ&F6uc}gbVbFfbVE-ח$  H@$  H@>}x*\oY[9+n^QZ#mүZ<~U\zeer=gy3~tvl>䰹mcVL[5|lϝ}6gE1z|z2WZc H@$  H@$  H`Zj=OEm{x v6gV'^}e ʢj˞߫ݿO35E=r{60笪8zl>5C6@>,j]Q1+fg}2_7ӑJ@$  H@$  H@{=j2N?yblicϴ[弜O]%q~szճUs._ӋS@.kO> zbfbSʳc+_~_9]>K {.c-mKMУ%ν~T#W벦/ H@$  H@$  lغ_;RiG9NC Sf뻆;Sæ\Ycz}]̾{/.{P9|j=W1tKڪ\]|{Ӄ%W|S>{,snU:Z H@$  H@$  H;yG7zG=CEGG[\Z|tig3-v3ʥ>^}ƩsVz6->4X>n>ы3E̳d vKh'gQgVƣ̮VZ-{k%  H@$  H@$ })Oe__3?υˌQ}+G >ؕ<ڴ3[ZfyjhEss.g2~%9[Y-YUig=}#WyW=Gz_=P/ H@$  H@$ JܗԳy|/CG>9,ctenwdWs}|L[^fǹoE]X仦9׈tH5亭:j+E7fg?>Z,c/7fhjZ H@$  H@$  H4KѤnʍj_G>97kg\Z-)avy䳗3Fܖ~3^܋ a4Բ':B3klЌljџ( ~mT\~h=[vkV5 H@$  H@$  y}yzgq!k闆[=fY?Z+5L;3!ճW{\/,8.F>ۭ^#frs H@$  H@$  D>ln\ٻ;Ғâ-K[4=O<]%=־Xޮxg.gxkFF>?gtu$  H@$  H@:K7fQ~(u6jؽޑ.s'ғ^#2K;W}P/..ت3kdV˼ʳj猙OުO^jeGu=/T]c, H@$  H@$ L`v6c_ձOgLOlNߨZ9?mͦ69kT_~q~MU2>3ˎ#=5,bVkJK9fa`{~wQ;tz H@$  H@$  Hs]^|$ܻ_Zblh>1vf?;&k[J?nWc H@$  H@$  {[Vz4[5iz.yb,nݫQO-[~ow6ڃܧzaA߸e~e?YӃES6{2_~Aԗvaƪ=2z^3sgu$  H@$  H@i޶jF=+=6іi27:1q?ʭu Gf<^\)[z=Gժ={}Ti_K?uOK)=ٜݵ[֌>X$  H@$  H@8;ul(,]qf~N[ejYϞgƖQr߸蔞OFֳ̧{n+S~ޗh(2s֗K@$  H@$  H@E`v)gQ~cNڮ8)?k3?{`j]3jO>cy_o\|!rA·W3hvvM֩a39şP߳9s$  H@$  H@58޼Y}FqZ1}以^Zg+}hlT_\A]\n٪|3d~9ty^FGmUlQOgR' H@$  H@$  pO7WUGzVsӏw!A˜ ګW󧢊ȁ?=V=kñ3=6Jn+ڮgwԏn;ʡ_O_C$  H@$  H@$4+[;G1ԤןGvT\9/-]9m'm]oճ~~t+爱9fUVf/ H@$  H@$  6e,_OUK#]x%eF+Y~ֻϬ^4+sR{5ETT¬h|UJ>Y+?Ұz-~rY;c3$  H@$  H@$ 8r8['=麦ǽg=FbW{Wu+{>~ェП{-ݬyR~u-h(7ό-;59$  H@$  H@Gȥ-'s]cte~-M7~5(ॼYm{p:Ou=>қg3s)lSf# H@$  H@$  8tٽ6A5{4߷=N}.}egQ6u=мg}quQ]|?ê{ڑ?ثJ@$  H@$  H@K`|Vg$ǽOm)VA׏Uo\rYcUZseϞq1Zlgft>j#=Kح\b?gJ@$  H@$  H@/^=Fug=<Ӈ]we{ǩ{HבYT{* "'E^i>vVG,4쩽Ǻ$  H@$  H@$0'p}uޭO1Ҭ1:hH3z}6zG̮{D|/.َ\~h4cx8gzF>+-V-g_r)G$  H@$  H@PfoF[{z#@Wm/.ޢ8͹߼]zVmמ#(7ڃ[v6::$  H@$  H@$r]G$f=(2sok=ѯJD0_XCǍ]HH;}V4>^X7_^#G[dN3G_J^GKzq:r?+xJl9fc|%  H@$  H@$ ctifs~Y,={݇{/-!^ċyˋ1ull߳瘱܋=$  H@$  H@<2kkVmv#=Gߩ}og0/Üèv:z)T_G٫}s;z H@$  H@$  Hx%}C7o=G{>{ /s]:甾Sz>2>ed{Ko3 H@$  H@$  H \}o^} )N{/,^䋋z+xyp}?Nj?,3> $  H@$  H@xrD6gXmYY^KzO/0x1}S9)sNa?$  H@$  H@$ S.O99sڗ{?/_\ԃH`"#?eSzg1/ H@$  H@$  &\?wS>)zҢU9s8DK@$  H@$  H8E{wlu=:<σ} /,xWC߿?=Ǽss}Y!ɼ$  H@$  H@$p^g?xK}kzaC틇}?m$  H@$  H@$Y_9sy{Ңվ q/s3q$  H@$  H@$9:笷G~/,_\q?貗zpy=ΰwF$  H@$  H@ȅG.5$^0jK__z~:{g>;H$  H@$  H@WFb|Kb}Y _\|̅_`>}ae# H@$  H@$  H.`r /.lV{]Ëk8<$  H@$  H@ pї 4|q=3Ĩ}o%-@$  H@$  H@8gy ħx3'勋cS?K5,xMϚ$  H@$  H@.I./?ȳb|q1rJzɟ4%  H@$  H@$ DHH8ݴJ^b狍s$  H@$  H@-/&F|Y1/.o_d{K@$  H@$  H@{㋊=BO M3ľ$  H@$  H@x!né}Yq*|qqv—g0 H@$  H@$  H@"Ks<}/.NgwN_h\H@$  H@$  H@/(~6'."$  H@$  H@+%}qq}I$  H@$  H@$pC|)qC G ^K/=^'sJ@$  H@$  H ʎOꕜӗ%1%  H@$  H@/xū$  H@$  H@$  Hz|zI$  H@$  H@$  H@x|q|~ H@$  H@$  H@}E$  H@$  H@$ $  H@$  H@$ +"+0<$  H@$  H@$  Hk %  H@$  H@$  H@WDWax H@$  H@$  H@k'K@$  H@$  H@/.($  H@$  H@$ N'痀$  H@$  H@$  \_\\чQ$  H@$  H@$  H@/.^O/ H@$  H@$  H@"ãH@$  H@$  H@^;_\_$  H@$  H@$pE|qqEG$  H@$  H@$  v?gz&IENDB`sympy-0.7.4.1/doc/src/pics/winpdb1.png0000644000175000017500000020115112253362407017610 0ustar georgeskgeorgeskPNG  IHDRʹsRGBbKGD pHYs B(xtIME +iTXtCommentCreated with GIMPd.e IDATxgxTրߙ酄PBHQX@?DT`r ((z`DTB $@z~ )Ls2m}GJMejAAAi՛UV;ĸb-AAA۪V?;I3 Z  -Azve56RhgyKdk_8Tc9-_j  pK99٠Ç֪#~+]mK cs jKjYf_XHisSKܜ]Z%  89e#`PLv!%ӭ+=2_ijMDU^BVer ((.DYP=Boy!wh7 \~Jsߡcӫ{gl+J矣 s3 'fIyymԗ?v'h*{%ͥWΥ\Ԅ6vx`ffw^)BӣT\ RR7:JX[[v+QCnմkƭ|t:*-sɯ r>-Sgذs+]W!;6\}Ln_n"gp?C{?.%&SXX hJAa"PѯgWj:FIi)B.%-`7l,MK&Hjlze38;!3+s/@ ss3d=@P/I`ai9y\II+K |nR[9r: wzcΖ)Kh 9 ӳS;N&fP( @]5W܄it bbEEXZ{;PWHLJqII:'M&1LWy3{#{gN۳}ک|+*Z>(-N7>ݚ I٣dkKU}Q)+8Ϙ(=N>.Jͱ-Kf;|ze!*N "/Su`z/]{hMfnı/: c?{ť2O{Q^~%Oet`@/ΥcffJg'9Dn?sjyYᰞzfV/&PXTn. .1_(g0/)$yzi$ml-X^J w515G#G^XNN̟<xvotsfs_}yt pG܄p:*j5v{r :?ځ҈gv3NJ[7ڂ޹Cƾ&F[{߉u_D fx~%c8´ P={^tUK :ܗמubv GRҰ03LEZvjLj/A:ew}`GN^?=s8$65J#R*ւReOQ{WqJ KU$b;L075`eU˟ w.`cNbJ'Щ8\XSXTf.50;5}|@ wgehز%}|M^O}L%2.㈍O„Fwa6zqВT/*itZm_WU!ꓞt:ڦM,sF'BV!? '3YXZGx2P Fǔ&5kQĉs)jClzujDž,N'f%5xN>/ާB\ ZVJ&77Y?:{{LMMv J|R&_o;AQYNY_)U>WD{3lئak!U<\l pҌrA/,.WvU u| 66p֫S6ءVu3]={tek43aT@+Ya!>ЏeLHEE|s$}C<͛n>t:Ђ]dz\U_ED+nxXmhT*(iK*ռfHǵ[Rֺs4Ϥk`|OfjƘi[v pj'Q%2SS<ڹS0ON_Ĉ{h}Y 9EޒLqlv{4yEtrgd-iZ{{yWLui%DƱ<]mwё̺O?_ze=R6qX[<úzq3>+|rU9'T|yi\]DL"2I[Gk2s }ΝuK ]>=vs%' ?3SS<<ܱgһsoLZ͹H~ןU8[dnVWX[Td&d`eiYg5I[8k++ϞAVR?bpEPp0& DjcVÖ}gxn<>++GT*6.-#QZ޺s@/:AtLݍ҃}h7NhߊΖC'ɀ.^16z{iaooWKB]mq@t"vv؆_]ijOc(|}uʪ􇍵UZZ ӤeggۨȈ2IYs\pl)+WalBre*f`OEǹ_ _Õ|~$}Xsˮڈ<U뽄/ٱ bҐzAPϦ=ql}?_EԊOb7 3#z[_"_=Ahaڏm9Z%ٖKKhgM]B;<-:{=RҖ<nK13ҾVh.Jf0D4~ݿmw tR}dKJ016 ?oM8{;;DǐWX߿vnogg-({vFa~KbX7 J9q>+fmmo][¡XYZV땵{אZvܑc'VEEY=ȝ܂"N%Ծe@B ;h>y?V0CpVՑǛCgr`eeZeoyz{y0룝uiM'[[RsrXA P;[}P*Z넍59g QԘ??ԨA=AhQ>Sszŭn]m;ݪi׌[ju}Y)٨;\|a6?<&2eɎ7w*}}Ahqrr5;еKz{pG;4V(ʺ[;{6!L 7[/rNĩ$PWƺ]jkĤd6ʠoU&RANZecSrpךgkMUkM~lll Wg^n3W2fJF&_U?kв ^c&WlDZԹ_Ӭ=g{KbYg,^:۹3cRYs-6x7wbia*kKNj77\]oRzAn$E 5[{;kge{a&PTZnty~`4'eH ېkCC:.64%XZZךEsm՜vAh ,M116bo񔖕SPc?[-]X{ݹ?w9]j  p 3-.-##;Ў Y#  7Yy}m=R+AA&&b~{HR  ­A>/bAAAhs/  ZʲAAA5PT-̽   ĹAAV,AAV  8qqukkͺIو  s/PKAҠ5n]jͽ,vvvۇ7^//fͻtiC]Wھ9{,y}Oyns{=MVMKcʦSTsn㏳fjbuc *KMeCFACg^IvH*e}{ jēO=%ۄ?q'pwρ?wwY/tѣ~];++NFF֭[ Q*  s_fyto_~4oBfqdZ3u뎛{[䉏G?O/oxAҴ{<*ݺkx :u׏^?JJJ4ዋl؞gDqqV_#O/9j\ܫٹkw a=om/YŒcc)ݻwAzӕOc$֭Q;""JEDDkrI@II 3gCBYreӭW>]k׭G}Z0-j  4sVuzK-xqرQo镟!t2ª ā*dHJbyVٿ?nҶT5]m=k r!r!G}j3}F|'dWJ#AJsV={ 55!CDFݝhΎM!((Hرj*Oaa!={}B;l2֬^M׮]t`~|}}8&LHvƆ L0Gha쩓klDb¥Za+փOg7]/ظ0e.5'SLaq̟?7xM6|Go\^ݺugÆ qy믉_n ?̪O?Wɧ2Wy3L0Ç5i[u- 8LLL}e93WЯޖ.u.si,q:u /Tq'aƌnmU9xyy^ӓT4Ej=t,,,(++__ z'zaG/CuVvگ!L4uQZZJVv6=zEII ֭cҵ%9WJjj]t߿` ,[!?Fd$kҶTW6-AAhZi-Ņ-nͭA+-Mջk.yLʢE (Z:?oXτ iӪ>>\JHIɩxWޥK\Zͮ]29>hl<6mSO>YgCj:ujޭjݻ}:vlPy9;;W.U^nx{{q&:t(C~۶ o,,,uCrտk  4--}/ŋ񔕕qT,p\>S-hIkpƍԍ4A <1axƏܹp|^=4x={8;9ӧo?퇛+rt6mw{vsw'| ?zmV|QmˏֱϿݻ;ocnji2颍_}?\ @>)))f N9sGxOI[_DZZcnj!55Aqڂ  MKjrKەYnLA쬏Mo}u$ 77;7.YAARL H[AAAABfAAA{AAAn&dY   d^AAZ f/^8'VAA[;w +  -@Qaa߲,GAAZ bA| )//o<037 B!  s_#BC"11Ѥ\m1щ!xxzbkg'! s/ IMI!p:"* 8q{ڴiѧ ps;2s/B(,(HBC`fnJe˼ccbMxxOÃR^V&" s  TB:`ll J#ԌԔ c 8 MjP(Q(+_AĹ,1ǎл m_j5ը7Hi^Rp[" B3o[\w[AYsߤMӽa^><2#fͳFy+PȗܴYA38qbomSm%}ǂ V}kY3`Z|2&OȚ)*)#b:Y[瓗h*d J7b;JY ʑ54\͎?2ٱ= ++1a #CP}gb3vHJfoOY^nOZROUbnjL.9/KgFT%9+vR[M,5䷶t^k.^J(b3ZULz;^k煽|Y OM9åKE\+.9vl9M[_ne9v_m:)g;FG~.*ֹ8:U-WKտk;|V KQI9:Z䚇oxP̙񔕵J2x/5L&X;w1?6u$sOߨvjkg_-?u+;CכMFy[u&]S ]yY&ʏT=҄M5F-߄ydDV.N%R#:0aP m׻ǿpW?4uQmS…شظAhNNJ\)'& iǢE-QWkɱؿcJEVm<%ߌrjʼmg Ӝ;?P(pݛӍNthrZ7R56kmrrrX;lٲ$׷/O?=_Z#Ϫᚲ\jk76܇=m8-@UV.ˎC(2K;37ILlqph66JϷD&.ΤP16ؑɈm Ֆ…(޶.)-JUs%~#FhmٺKK+SB1'͛ 33={n6ng7f>0G]~sYLy~>!4|B1ygd՛gY)..>א zk|#[|C}mr61}|VMQj5wdbggI롇3aBfHc˖L8;lm(8poa=prv;)ڋr_|APQQQ%%%:w.~xyGW O/9۾>9 yi.]VS.\`ж.nL8TʾK]{EBTرcͨjF֯'!!`U7zjtS+W]6UjwZӹox⻛JrzjG5gY<,Z~w?e<7V[o.^TU[c߫vRΝгAEU2!+]ǦA|(-"T~x/VÇ˖ >Y+?c/Ξ9 .pavz7zq!KFf&{Op{b҈:~{{F᯿f_xk"uw`_c0s Tw^Q|7QǏq9)IsͷJDFy0 }}rdp"8??Ӟ39G`@ kTץW^̜""(*_G?<36n芫_ǞWU4CA}֎bƳϨ4wxgɻu&+qz@^nqq3 Nf)ծs\Ò^[^mFێy򹹼h6>0N;a*Auxq+59;w%!]ذ~=MIIĉ=u777d`͚/}v[&L{GcVCW:vb˯s[ϟ;K6+,,ĽmZYdiH ;A}Yr ƍ{ZlXy?lpf-__TbTW;לvh~U;5j,,<|j D]+T唗ZF2a]peQ9Ku6,ߓCppDZ%USԓh4 99=϶(999,oYY/y]֭[Gtt _&u埒Bv4=<_s&&&Zϕ5Y;nrM_zvZHMMvã }}rݶm[s("7^'(((ZOu}eoeeżys7on̳gY> )܆#L'?3G>''^$&&j~'$$'⩧$D)Wp\֐ץ4x8p}Iӈwajb›oL~& -㦲gcdkM AWkξVro6 Aa*uij22uگ<eeZ2tٚ_}KRR9#GZ<5*6m*ЄfP1:g>#18ۅ^(MnΎgV;7yc̚"/^N1eTi99̙s%9矟ǏNZZͯ3N:g&ʫDvv6/\7v,s'--4Νqd{]7FFF_Au33s͹tfhPkь3_6n$55r._oIXXX5֬W0k,Ө֍ДW6m&]UڷjqoR\˿9|`տ=5C}ii:YYZ+O}yXYZX7gbCS!m~]ٹ/\gzyߞݛVMǩuWmOY9 'g͹g̠W7O/o4w}|_u]+ѥk7֩߇[ϟE kC_g~@DXpڵk6w8;;#'={ƫ W^Ҋ.]ۯ?=011K~ fرnPke?yۏ<y( ?NTǤ3nW7tv]= IS_ٴk}6ѷ} -:ov˩Bm7 Trt T.!Z쮥?Vs웓ؓ;-* q?&(зzg.0|K[nZȖsp珸:Tz dZ׮9ؤ9ИUbfNQVVFNe,qLPyyRYk6R (@OF=A:_8, h*; 77RsY"GH7G}qc1~8Xb;Sm:_m ڜt܎}cq'j9GOh´?<-Б(FwCG4΃ Isۡ1xzd;oHfvvvնTT{4xwq;<-Lݻ'={ve),`)))浅 7o… ;F "6ܫ[ɠjinKǶ> ɫw~pv~-5-OC߻{ѣ[Çc'_2OtOòOd~rPS篯MMG;SVf׮vL|]qLoː!^)Zqu_${ߟ{G|4rp[ \ӓ^{S\\̈9sZ}[7W7w<==P(R&7sZc[BN###V~yo~ȸt:kԷ‡>g^f 3/ӣkg\tğ7IKءi꜂a6y^UW9pD4K~};NM\AS0u1DrAlr9r՘Esvt೥oTKcnXXYz񻕭cf-d[ںLN:Űao='}ǂ Vν B=+WpuXAӹ!>@Z CBUJ{repHrA=5Fv%Z1i^){A={AT*A!~Q=AA{q  CPDqq1fff-ޛLyyc 8׽{A0###vξ ܜF|ڐ|ʈ9#(*,~LA^Q1 C~|BAqAZMA~>b AAsΈUAAC;5  pk rA111uT #p _|AAqAAAй,R$pSXPwX3Anl,3  J^AAĹAAA{AAAĹAKK+,-bAuzwV ur&MWWWmh۶{iU:>tMT's+믿z B<3l޼/4lV_II cƌE~IÇ3Ο?_o?KK+mc\3]z5;vށl۶ ={xj˗ӹsvvoӄ={0h`qttb ށ`VXa_~{ ^ yAZw{ѭ[7~jaT*;`kkGPP0oT*U*5UOi>++f/rrrj9d_ ]#,,ݻ4HmLG;;{j٭g)]3gŕaڴik׎XAhm888ޓSzjY.\Ȅ z ƍcѢE|S(zs#G/l˫~+Ng5XmLrJ^~uؾ};7oֻj_xK^^ksNRSS뮻 j`{# ɿNU_w}4B@1gW|BFzU6XyXPϦMXx1/05mccc# wR>'';;wpdEJII)vcǎպ8y2F… t???OGǎx"'GyAnV~ ĤRcƟ/Lttt˗/ ((.\8+El)MmddDnnK8::j5WRcҤI{d8{ m۶ՌiU/}mQ܆Tn:u… ZU}JV p;wСuֱuV `{G}uIǾn>*[ѣyGy7u}߾} 6WWW ##VX''Z JeU+^|P,-pwo{a^҄yP*''g]ӳ 鑘vc! BkcA:tӧ888PZZk S9\OJJjP5Ojj*cƌum۶ƴ!6بMnCdOʱS۸OY3j>,, ::|~WBCCk9[?BKX9CΔ)Sj]{Gٷok֬!++kOb掺UAA>yyM>vEqZiUuuLQ)O|||AhxxxOW5ԮV҉\ ǿ7L>56jd6AVo]3j_x_?gӦM0cƌ&ߺ]7.58X[[رc`kkKQQ1 .j2OX7|c} &&PssZi\`yW3ॗ搒Bff&A 6~JJJ4KC  C|rXl9@5[[o ?oڴ{G3/ۜc6JiӞьU}g&~Сt҅ ~fժUMlzˏЕ6lm+Z\pZ^`X9q5Ջ5 IDAT9s^u~1bHzA``@:8v8>r'̜9S'3ܹݛ/\S+QF1tP|}waڴi1m4>d%oDxxO Wamm͂܁6m9~!VTf޼y,֮] ׯgRM xٳz2lȑ#$$$0vosuT2M>ŋC aԨQz5%>/΢yLMM#˿.};;;:u~Ch]BmkUAngBmke…Z C///ӋΜ9ɩo2i 9[OJ_AۑM6q#o9Vj~7CXqK<MAN.(N 133cŊ\rq)pr.z}Nb,ν|vT6}/}9y9MӱSgbOv7bOзbAIsh9G2hmic /9ofs|9W IЊΩZ#%Zj#ego֖R-7Gnvf֬ b UB:}OsS(ӾG,]nnKprzkc,b_[wxdg xy}Ș1?0pLMdz˷qF^|INʳ>qqq3w\MzP(<ӌ59s0j(NJqq1ӧOGP_O3yd233J__,Y=z4˖-u}۶m<<̘1b͵b̙#qT֝>]pyRݻ_ܼ2=&˸z=6Bp-V3zU706l`ceeų>K=02ʺuff&ƍcƍh"VJi)))Ԩ])))ԭ[W9{^ s%Ǝɓ'y9v^R87oέ[[.ԩSGY&::vMD^1J5_~O_.pt\IlTZ]0ǐ}P{oaԪUĎQrH6_oȘ{T̽QU>{-gtԮm+1;,A#F`رhZ"##Y~=~)K=##ڷoDZ޾ȉWdd$u-22RIKb#F`С|8vvv8 &+ 9cƌ!==333+WϏÇSNLLLpss3oÓ=zV9O^54hjSrrWߐή tgٲܻ"6[n$''ЦMݻG^x2_~l,B[C_B'8 >q?<{Thpt#q12*cKS1%K5jVVVEZ.XOp}DYW/wL'{35jy l}.3M Z;!SJJ +ī_Ve Kں&uV/w۠ E)_ܫ@ա:_'Qܢq~˗5M*GB!$$֭[i,X-4*m۶Qb/_4lu_7l>mGUw,ŸNer{Q `󥦦T~²O*Wk4f̜7|KFFӦ3? Fɠslܰ^z<z*LcԨQo n||V&((XLMMiȷڵKΌ(~(9=ꕧ;6ՆW*ݝqjժ'uj5饶NFr-=KЗf0B0+E͝;7ory"##;oR`".^ǏŋDGE+e e \p99?-z~g,3f 6&p~/ٷ,C07g֯rÇlKeӢ4z9i?-qqB!(w}*:3^؋|˾{,KzXYY쓥|J7|}Bv?RN;]tFԮ]9sg5j && 4l؀5?+ٷ,|ŧ  Է333uML8SSS0׶9> Ijj޼ /u+ӦGDD0v7\Ңe+F yk}^ǹEK:wƎ+>f}NfΚNCѰpb%>׎6+effe{=-[ޤ)ܻwO)oo|:eA3M14Mͷt -y.^(ɽB!*fru+?s}c{s)cbbh՛~_@XbR>v7WC Ɯrϭ[033ӛ/ןy˗_|AYLo;e24r??Fy)7ؽiTI>yg/6d711fL qqqX[[3w,ːy[䚖d)=䯿b_Y~J} )eS&ǀAC0ݠFe=s[|z1B Rعu ަz 5: 9Z%8BJŠ3&3x #>>2dR>c8OJtt4w4T7Ę\g'Uѣto4tt:QnC׭[WoHNBy.jAgiРA$$&[gHOk"ϸ_ {[s 6l+e4ȱ9_F?qi3.ԥn28>y]ĄL9s/D9?31b8!ھkJ`J>MMSY,KS1kl,-hպ ZƖ3g)N6fμԾ-]ZӠAClL!Vֶ:/^M9߿ x+k[fΜƍ mw6;|E= ϑQXX͛<::zg1ܝ?r䱇e\\aO9z7> YQ|2?OW%Qy.giiorNyYwDQU #@]u%0BIUg:jT7aϸy#7Y3jT7QV*\'2&NyIx㼔fכ~A9IS'yGw ׄYfQQdddr~ G#5⏗x[,^ޯ7K>֭$nJ%KӧR ժ9k4^ C@ՑXI:_Jޝ3}RS,\11ܽG/Uu6sEFF&WǧVZ_ϷĿ81Bs/Z(![!*ܫ+[aƆ[byk(oݢVvek֑=,\_ ={3q޼m۶՞LR>ol,Sn`mm(OO~@uuڕI}=/4h fҺMi!2BF wgۢҹ3D0"ϝûX 2 NX1cFys-L㱷g;gH{BŊ^/Co(.;-Sf`!*xok ۾h46_lIVsgiݦjY!Dwo}#Jb/"-l#**#UQַ]! sSC{wn!$zoU3FU*EeyϝC.`Bj˚E} ~RvnIfs_y{ɞۺ;U#> E~Bm9 #A!2$<>6ʙ{!BQ{IpeۅB!$,mBQk%BwK(OKKtBQ*?{wKDTJj̙zV!(G Jk6# cdOBZ-179~m}:uJP"%]E"QfMZ<ӒK_EDDtZR1XZIg !$wj$bBQ dh46͝۷E{SPn=:KaÊ|xtf4j=?HBEV-m^CMvM!:"Y9GusX1T}%2VdXB %CĹeBՈxZ4 O2OHO={QzԐF0l U Ӽ}xN>}fѱ6QzmƎY`t,"Y?m E߿ӧw0vN>!$B!ʯKkiKJUk48yK:u.tٴ󻸳qXef|ɳC ]:J*F4hІO$MFB8TTe]뷀U+Z艥eS{CB{!xt}#~&F$K_]/՛VZu{gb0 oozT12ho%կg% \1Apu7ߌU78{,~I_B{!PTԵ c}ZE--啩'J;}g~I!Gʕ jִ$>> 5hoobY7/6TbC{ŽS5TRD/ QB!ʽ؛)ï^ڶQ7GsQi/nX̾sLZ7h)l4V`Ȑ:'^xm:/Y. 'Dy%gB{FFUٵ}=idfhФ3w^ױAA+9ukT*Zm&NN] ll5riVFjj2 !-Vb֬EZO3[Tk/j)ʵE_%}BHr/BClS6 N9J[IOm{N}xq[|ڑ/S~˧>!$/&ڔ=_a׮]yZmJJ}VDt\fMڵ{e˖Ѽys ȵxE fQnYYvZ$B!%6n]s>sSr_yY|^ SΌ5Z"nax'o|v!A"&~uq| gȐXYYS9 >>^)WMٰa<Ӓ:ussqz+[bfV^jυ e2335k5¢#Gz|Oz={7-22ܽ{ڔg9S%ޤ8M✿ڵk3ed.]dp ZmʪUprjiM ̰kŚ5:կ eH733cUaUk!Clh4̟6oެsol5gWٷeڄ  %KrYN8NfL>3fOWHJt/f„ Ԯu1ѣGY`Ĺwe5nZfH 7ǩWB!+1B]*޺r+1!1$߽C<vW@ IDAT@ժU;wnGмy ""Q捊\)W24[޽@-ؽ{7͚9 /ᕾs}N8q"nnøz*{sjjSBBiӦJyhs>Ɔ~;Lƍ WaVMv*[J< w!(LčFQ BkX'9OU~9{tT$֯/Ϟ=C9~x'O+=B6^=KNu2227nܠM6զզ7#ŋdNQb `oo>66V\ܿ ɉ)^^!EVɽ16mdĉd[x{ڵܿt{ W]8z믿Nj̚5SNo_~]y?`ee%q.J͛71ide7$^[Q.yTEz-ZĀk4tq)ҺZtA(8l 6|Ž-eV.%R͕_\BUܹ!Csr3DGRJ|^!Ӆ>+VOtu:dff^##r<ʔ{kd^|qQQ@Xgt$eN~:%'rLN:T?e"eG~k0yKZ699d d QX|9mڴ`Ş={8p`hN#sy,WCTXo^5 #zb$߽]*uԩ*\ƍKzKP2l{-נRͥYUXqOWo ;v\*\T\Rg?h8v, ib/sYZu>qmHLL>`?3g,_}vC׮]Iy_d ֘2fҔVVPJw^Jźu밷ؘmr9|`ll=7n,RXv-ѣGٲe ͚5S%$$DY&55cbff^^^*iii=SSSlllXti.h@ff&ӧO Z{ÒΝ;۷/Μ={e˖Ic4omsgIsg@ mo^kw_*z>/?| ?WX|Ge+{hQBCo1yY.}kkS~Y1|+ֽAFfVufѢ|E?[eK+aժU̙35kְf>Cm&Y=fժUVKG!$$pbcc5kcYп탂8vIII 8///lȑ̝;d9ɓ'L:t`O>۷@n߾СCeƌܸqP\BDD3gTgΜI||k׮eȐ!DFFW_ѣGrzLlmmy w5 /qU?hy`mc͟J=nn-ؼ?~~/{//#8xqqѣ 6ֶ&>,XНѣе=-[Z2jT6,k&ucgWOmMlQm5 oٚ5阯YEPV3eɊT>u+WVbB3߻cH{Z͔E*Pejhh(ݻw'22R)YioDHII F@Fc4hРtJEBBJMMMsM˹-'(('''\B݉A)}p8pgggbccquu%&&<*5oC. 5jݻzO}}ۿx|Vft6v@$-- FCzz:5k$Z-[~D{AF? y=6gv]&)ʯzuzKvShР6u7s7WKhb5:6k׎ݻwϪUJZ/aaaXY;F0ѣfÆ/CD{o?Ӧή tgٲLSx_Amӌ7oooƎ͛7eG(}}}}ٴiSLC)suuO>aԩ$%%1yaԩ˗E5|pfΜ#:&777&O͛4izۚ:.WxyyrJ7n… پ}{ CeСƲm6|}}9G9|rkfuD\;;w˳,::7kfAz|Ɠ<=״Nt*VU*&M,H gڃ:v숋 XXX0o|֭Kƍiժ]v[(}0m4:vH=Pxxx[>kkI+Kc4 Dvp!SU%DBQO뼔ޔA}{id@Ցs8ytX"#eMo8f2m*~^Qdj.#5=.,N7e";BHr/$2hB!ʻ*!B!*s/H|f-7tk$(BIBA}|!ڮDQ)ɰ!;i˒ !$/k+B! %BIB.F q745> &!E!ɽT*Z{RQT$BIO#k|ڴן/!$9W~jt:g$ձ{!Pң^S"IKKKcјbccҥKSSS;v,fffEjjRhҒul2Շ֬Yg}ƥK򝧰T*֭[=ƴmۖs)噙L>+++j5$''خΝ;}viqT*K,SSSƌCZZcYb,De\ns-Xu%(BI3gOxx8ϟz3fƍr"""9sR>g.\ٳg '**Jz:un:z-4M_ ;FRRK)[hO̙3RF lW@@C*͛ǤI033ȑ#Nll,fz:+[ {ߏϗM8C"tT>u+WVbB3߻cH{ZJ 4 ((GGG\2t~䤔w]I6lH`` ͚5ٱ*C???VʢEr_JEbb"`ff$8pgggbccquu%&&& L<BCC޽;/_FVR U"Yb,ģ#5=Vky-N33ٳgZVsM4Kd[geB!*|ZoZXXVVVg[[[¤' `llW_}Wڅŷ0DEE],sM~뇱1ӧOĉ7N{lZh+4j$ϏOqڅŷ0ӦMcǎZ4hY###UֱcG\\\‚yH-B!\߿? >\|[!D%O.BqD#"$Ē]yxӫG^JxE ґӦB!ʽ=_gO|!$B!Dy׹S<^{!$B!Dr?mf&v !$B!Dyv}B!LFMqx#VY]9fEY׹<߾B!g)wH@!Dqt:ǼC!B޽~1%_J'.\iӦe'yU;&Zz^~A !*<g.DyW_}UX X]6ǶBQi=3gT*dff2}tPո,=₱1lܸ1ߘg^|9 6>Fr׳gOu밷ؘmr9/cǎ 333HMM돵k ...=z-[ЬY3e!!!E:Frѐ6 iii=SSSlllXti}*,,~QV-WN޽˷ǝ;wfzEDD`ggǝ;w?JŒ%KԔ1cƐfP}9۝1֧hҒul29B<>{N+t8΢E8}4gΜ!665j/=#G2w\9r'O,4N̙3hZ `sQwAAA;v$R6c nܸAhh(W\!""3gСC}a߾}rmŽGhH چ3gOxx8ϟù>շo_&OL\\8;;oۏ7o޾:o<&MYpBBB'66YfT_vZq3g.\ٳ%BG>u+WVbB3߻cH{ZJ*s8;;+111ҋyhԨ~~~ 0 i}#88%TT$&&bnn@JJ fffh4ׯOPPNNN\rݻ+IJ"!! eySS\rc6҆A,wO`oo/'OÃPw˗QŊJ"44TٞuFFFZ_^_Xb85lؐ@5kV`aP^MѐաjԑV5ݼ 'O_;u52;;ǺװN OT嗳GGEash7o&׿ſ~; ^>i9Y1h iCAC&M/l:~8:uf͚T*%ίg333={6~~~J?@o{4iwC7oo!$DEE)t2|ڵcdzjժ\D'&<<\U#q`cc|6l'N$::V˝;wp?Ƃ~all9qƍ{~̹ ׯ_ڠ>[[[Š?~]?=?p B!퍗׮]###ݥ1|p.^FA靡S/_ $._L ^M{ǤI{.ɫۆÇK||nϟOݺuiܸ1Zk׮.i&|}}155[nƱ'''<==K/;v숋 XXX0o޼B˫݆cũ`ٴhWWWhԨB!2pA坷7-Z`ҤI'U_qc[" jm/H.B!DŽǗB!$/qAAA;v$Rַo_&OL\\8;;뫷C&)) þ}>QO IDAT :To-Ӝ9sXjԨؑ#G2w\9r'Or 8ugΜ{u޽CP!zOYgjrՓswװNc !rH6__6g'j ԩ5kDR)ONIPyM˹h7o MP[[[MsUm%** Nr*Jd_" B 7>o۶mc]aÆ6|p.^FAiWTtRf̘cK||#e$رCySEOӪ+Ռ1222xOpdHBHr/†ϔ:K>!ӓ='簜g^!xTBɰ!(̽Bɰ!(ۊ|>88X&O@j~#CpV)O{[w}r!(>]tLJ]R;wfz"""Ν;%h`ll=7n4T*k׮\\\8z([lYfӶm[BBBU*K,SSSƌCZZZ\닥%ueٲeJYjj*cǎ 333HMMkiذ$LOjwww3ٗEyj$3# 4 tRSA )))ܿ{ ] B j*PՅyjʴy1i$̤7Jȑ#;w.9r'O"88$<<<ӧ#0;󰨪7gewD@ 3wR q%& j*jK* &Yά?hn303yps==/' EEE0{l미y&=zϟcŊjUpmx<}<'O`2秤 --ڵkqu022BXX2#]&  fj_xQmÇԔ|N>yQOh### 77...(*}VZ .DII kc+_}[<>>[l3,--i&&5ʜqF1wŔgffرcjˌt A4C>ɈP(ʕ+Ҩ?Gř3gЭ[7m?}`ccK<&d׺ukgϞ999hӦ<\&M6xL'''ر#X,X,Zh/^KfDs 112NJpI  fh7dQ```K~C@@ٳ'N8\lٲEƍF}#f?##j_rd{{{urttDVVdBi7udFL4N>wGd ¢"|8we[AD34baٲeذa"""```@&N;wC,ˌk/^\bѢE4iJcXԩS1oPTfsseB[9o߂~ݏCA=A 1ϟ\\QI,x(#Fՙr ȸ' ^NNׯ\׳lڢ_~p 4Cь<Err2|k zz)]G~-ƌNn4zz B }e*xןr r!hF}JU󃋋+ "QR! 7ČHVh% U Ypͻ;,,,qEׅr q-Վ BBEiHUp{Ah޸Z A4#^$@(}u# -±`D34<_ BƽYbb"N>[]gfk~Kn9AMWߑ^\ BH/©etoۑC7ۍ>_奄 |2gA{6RΝ=ZAD@P5?b 0hp Eb[toY] HLA0'([li- C,!'ѡSg:GBTZյ? i׸Ɩ-[ѯo_L4QBn9A̸nkgʄS:GGhYb'mR EE駓4iZڂA43^^Ԇ6m7~GW/oQS^h9k @Kww^rÿ3r q/[HOǃa V=Zᣙյl CZ6e/_F.]EA4+fC$i̹<9%rE!m01oQ˵X,[r!hFƽ?y[:TVT8r^[à nܫ3:;;;ԅr q߫Wo?n.SI(r4ngHfana^"A4'^ΞEAAM!  2  i^__6 nftnaaa˗.hƸ6i Gnn.ZtVUH HԓPU^BKgg*ͧ 퇧-_$b`ll۷}011e.]w@TF擭[fhݺ 梤D&ϖ-[бc'XXX|[.][o lll1j(={4 D"p8EZH(čאU+ @uxy!iK=> P {* hŸONdwaΜ9ܾ}iii(++|f|R99O1uGX|9ȜW^^ŪU{n3۶mCXاFzC9s?CO1RSSgndee"44۶}MFcX(Fu|YN-kP}C#*o&~&BJ___q>3@*hŸ_|9LMMxQ.}M] BSʂ7{BԗP-ɬP###l WW7pݻ,X䩞<#F '''/[@ Ķmкun$&&iXTX,k~NF[HOǃaxx)bL9u7-  +/_]"2 ư2o/xcbO>ӧO3}РHKK96k,, .… dK&J>|8NEAl6DBaƯ yY-Gh9A4ܸ+1oQX,FDKAh ;;;<~5&HSYQ!A'C{uFaggG=Axϟ tTkW1k3Cx(SW@JU' q_x$ h<Я_?K:[oUdkﵔAԋbA_Oɸ'  W77|;+;┏WUUᏴ4 :nmHZfM[w wh}}}=ɓ 4ֆ$Bg6uq ,H(DAA>Z'(,,WE'y tsg0#ẸOJ>"l6[xDp8DI$OД2oND3 .M"m.DcB  ȽX,O=ɓ  BƽIzu&/ IDAT@ b [ñh"QSÆ O?U+M"{B5^|8q`ffcܹ4hPJ}A txby-G 0|dffb׮]222ꉵ5mF'N/2ܹs'NĚ5kH8AAjwMqcƍcʕr|7e-[ &&ݫSx`ii ;x9۷˗w^tFFF͛7s333L4 :- .`ڵpss͆-ƍ$FN@HJ]m~BAqqO$zbiim۶aԩu=z4ϟO";;۷GHHL$;w8q"FgΜA^^ƍ@&둖TdggK.iY| ^Z|Y+o]4GAA=O= z _8p `aa5k_~_UV066QRR[~:ؼy3aff?Ǐi99r...=ڶmŋPyȷ&Q~AA4TP!!TxL^zff&llle/۲e}Ȥ "b9OUO`X22755a~s8l;;;2uYȈ+cŊx1~xÉ:X<9ȏ 3&2A۸/+:&/}Ϟ8- % )pLڔ)a1tPNN-c-Z@RY899)=G)Mv gV eU6TTT`˖8vTUUq AQ-G,nr C||<-[FTWS5eޡC̘1KˤUTTyUM>Gzz:|>nݺiӦ<~G!WgϞL 2)stQ~фR\\Acݺu9s>ϟ?7|}}zAJbĢ[uRhӦ-?.zod/OgeҶn݂KѢ#A7zusӻ7}w$Z`xwtZa!8=zAyY3<[,_yhkfFd͚bŊ9s&,,,`llѣ L>H Dǎ`nn:"2rDR766 OO/[zs%@FٳgպN1ѥKWXXX}H_h#"97‚Zǥ((n<@BO&ϬٳdFQ=baᢅXheMy%xb+ ?Pd?T/j*,ZVcժU`X ɛ|HJ:'aΜ9u&`ڴÇa~:6n܄!C} _}˖-d|U 6mڄ˗COO ̧N&ɡF>LHѦ{n9 ڵ 0|1F4˗/ b#&hٲ%l6|}}qz]GB\\ ,, Ƙ;w. 66, $*܋WudO$]YYYxEId^>}Z+}}}YŠ+bJ ///^ T:]z u0AM5|i앍LHѦ#G>|DKUB dee_/bժUHKKܠ8;;22JKKȂ Bw{6 =$eD`${'mi\eҥ V^o%%%˗1nLɓ'QYY͛@0W &Āo\n4߶D˗())ř3g0j( h(uas8DvVIbZ6`${'AhL_p<`X]|˖-P(!:z |:'OCDrbwX]'((FFFؾ=nr ѻw,X & kޜ@qt(ϓ \KIA~`mc M!巫 Gr${'AhD-,]^Ey9/uϗZؘ: BHߓĎ8#prjxYT0&0Q?l6666̃dO$M?AQ)Z)w舖-h-> "ٓ< BOAq/3odl #'Wm#s ɞI 56ml~A3b0\KMōڵG7xw  AsxÝX,F^ XXXBXnn.RRƍ?н { pus# "  c߿Ǐ⊌t@ Z zDh,8AD9O"99C +85U 1|5U$"#\rpTz79RߟAG}CHGJYO0xuAo__de=A$ :uE6mb  PYYrh NTZ6dZ#ə)͆P G{Ņ&6*jK7HL:.А~Es<<(ͧh:NNNܹ3 S\~*h׮?* ;X777GϞ=aggŵkP\\R#/_~6rHXXXH|#$+Z2y2W׹H^D_( ¶mCTTlllxZ|>_|Zj(# bEEEx뭷'Bϟ?ǒ%K0`F{KB,#&&۶mӚq'O~:)\I,--}ѐjTA~+)ԟ+W\X WIep8шq/y(3;u;wŋӃ'|}}qYo׮vpww?r兿ݺuS~Gff&.]ɓ'UJWVupvvFVVsUVZQZe+A=^Fؾ};s|+WM1n8`ɒ%fE"&NcccL:UO<7ccc}v|ZqW| >h֏WY?ML aժU/S,#;+v.CjdӴ4|*ߦMܸq7nˋ1U)k777۷7[ns*+:ܹs2ƽn߾7|SӣGh, ϟ?Grr25BPGjj*<<<`dd/_"99*թ}uׯSNnݺm۶GVV^܄՝7ހ+ܹG/tfgHLLTYR_i٪*g^zwޅw/TVW}N:AOO?Fjj*?i[oΝ;w^tu֏gϞpuu<~׮]*WG^LGCZZZ}_%7f#""&Lk !!{nHK߉8}4U8<<}c(++ 44fff%#dzgFVViٲ% c^ .7o"&&\388Zi~ܿGƘ1cvmר5kСCHHHSZ?eܹ)))`X¢E0d&OUߩSpaTTT_~%X,/^fŋ8x eI>틙3gbΜ9شi*++(DGbccիW戊j_|]vwƍ-ӧ1uTRDܹp8É'+>>22FYi<[ki48::"//O2#1Pݻ?TG󳳳ѹsgܻw, ;vDvv_OyѡCY\.7tPm:tS?/# x{{<0LU߽{70#aaaꫯuV0`bcccf°a0c ]n0e'#/6hrA> L2Ek4R)S޽{[RR壏>G}$#SiO7;_7@aA>:tL@,[6)+++YMOOOD @___rQpwwm۶v_d^zԾ[?gϞA(ƍpttT3#*Iٹ̨;]r|pqqQnnnLՖӧOhݺ5B!3zɱ!ruuiՔe֭s޽qy7 HPÆÇxxoee(+-mX,8zqaӦMl6pּ'D"O>5K̋dSհWiSN]`X`X066ƢE4&_Uwss1"/,ٷox@@SEOSK?}f0=WQ,?&Oϙ3*_+11&Mb~O8Qtu7..p4 3gj~QQQ;w./^C(*Xu# %%/1xkoFq0W6U٨e߾}q%3B @OO/J>>>̨b}קO3oԝ:uB߾}we_U 777XYYŋSUUU2Vgs%H1jԨ:SVw###J%%%jٳgٳ'coҥ ~"+###!6GN"}Ri$#>T7ZQ\n-]YMysdܹ(,*o書E̘1f͂Dqq72rtQhkk(mWKX, M.8d888M#iM_]j*rr՚gkkLod}UIW) ˗/addČ#***.B ɓ1k,D"dff"..7oO?5qh=  f<=СCMVQQcǎ蒴>){EEq cii)\]]! 6VZA6+++ennڥՔeGCΜ9WWW)_eeL\~n#001sLkl6cǎExx8>z} Xr%BCCS .˗/P6ɓ'ˌʌtK^ yyyLtMOـ/w.iB~"={0;vrx9[Cz$900f|UMDNqif͚۷cϞ=@NNl6\]]1c ?-  e^j9 Rw ;vٳge>z^^^̹^^^x|[/_ÃשS'|ҕտ>ܾ}oV8!wiySSSx{{7^zGii),,,еkW&ڐgddW^ѣ^x)|||9>z={%GsssJ)jj}jRZC=} :l6nݪW222УGK*V FB×.o?ѳF&TOf={6wN:aĉc^$K.1%$=77ÇG}|^KHǏԩS 6 $$#mܹظq#3Qٳg#::^F(T)gה{g;v(,_";v0L7nlڴ -BTTBBB0kkklذA.;vIdh}C!ڵkr#uOQ~_yi&aՌl%eՅ"PcmmӧBߺu+&LC W~(++̐ ?,]Y>|Νignd $%%aС߿wkׂb1M111 a^^3(9s>C 4GwޛXcFAuk[#BX* o }3B|IJf׌J{e[XX7ހ-Oҕ_ E ުU+szYge/@=&/bڵqM<~XKʸΝ;޽_燣G\.cǎɓ' QȱK.ҥ %g===&Zǽ{Ygh=euWy:#ɓ'HIImFS']  ܼySI"'I|7 ?Qys] LMh "##1m4rjTUU ,> 8~xFj*9i83 &H$X,BUUܴܓĎ8=AAqO45˱~zfԜ6l#G(K>Ă hhdkָ#& BWy&<=="k֬!IVVo=wߩڴicǎ}Uɸ' BgڵC^gQYzC`X000xcFX,P$X$P EbTr\lӦ .AQR!@I񲜏2 KR J_z6h ]' C4 tz WR /ƶm۔W^^άP/x<^v \C,cƍ̺-"FZg}Ƭ,]"a-7ods1 b|J^/_k֬!w(FfDBb 3gFTT #MrJ,{KKK244:tPY:t@HHHqAOOGŞ={'P͛QXXx#?R$ IDAT?[lQ9]۱}v ƨ߸q0n8޽wرc,bbZ J]Sݻ7"""}tiBP'YCGb$>A ŒAjj*~ }m6#xjjTɓ8uc7((~~~xwԾf||< GGG+B3+v`KKKG},J,v\.űJKKaggto-ZWfmkQz>;v ??p8ZyBEf`(.C(C$WJ"qk3C\M-ޡ0%"CMM6ɓ[C޽eVDXXTɬm6ڵ /_ǃ6m+++ܿGƘ1cvmu(5kСCHHH@pM 66Xz5***`nn(#99r b۷/fΜ9s`ӦMD>}O>Exx8\̊̂g}EU`Vq/Z*|8p `~{xxȬ,]?9k ’%KC,O?Ŝ9sԒaPPbbbmk(?e Νȑ#QXX .`*_!44/^IX~=ʪc666 sNۃ@OO'N`ܠ->"VA:rb -7oXn={ƸgX,ڵ ӧOرNѣG rx)))HOOĉer,L(A(ԩcذa1c`׮]9s&b!>>SLܽ{C AvvҲnb%e+/PbzndTΜ9 ")))6cҁeGVZO?E~~>8z"-,,jVb1fΜo0}tܹyRj\[VDD,X;;;d"E&L`^MVg=k SSSZ e̟?ɻj*0_6***w^e݃ )) /ƟI7Qh䞌{ B=AUU ģG`ee(((; FFF'ZYY:P(D@@v  ڵkk]b\.WV.B뛘ejʸg2QW"#z_Dpp0N:777=(++cbWUUԔe5G6m&Qb0 22R-ŋƚ5km+ߺ(--˙gϞPݻ̗\3y j%eܖv؁nnnG1xq!# ' t uܹ3nݺnݺ)4\E"Lzff&ڷoKlp8ppp[ĸt'Dii)ՒoII T9>}Zpwwwܹs۷Y^h~[nEnn./W_Ae^=LMM^~?~nB֭TGO:}4F2׭R̸cɘ5kD"233͛7㧟~j#@r G#nL<& 6 ;; d\d$ \fU066fD@^^3Sz%3U[ЫWz߾}Xz5YZfB\\;..fR9xСC*QnݺK.)|RܹsuF}6c.]&Lñw^tWWWծ7,,,6 WWW̘13j(;nrw8~Npg~:W9 8cƌ@ agg 2QQQ ? 6l)cڴiٳ'1sZ! Ѯ];eFÇܹsڭ[7ٳg/t~AQVV!!!]$FƍI4;0i$L4 ?|(;&MWM:cǎŚ5kd^$\`"""Θ?>WYvZek~  w}Ǽ<%$$T7Ʉ@fWUO*#22?*++K,/[[[L:UxbÆƯ7QQQ={6o>X,B 0)111 m^^?^ ]^娋R{r!xvQ羡iӦ^2}%y< g2>⪜3l0/š M.&AA4QZZu^ `ƍLLHMMJ礦""" {!ֱG]NAD7%+X,8pk͛7D2"++ ZуqxH"TE՗ rQ˸,X ΣpqqիNA4cccNv(1δ"'A:Z}@@ ~9rC ;w$E  hrQ+毿 ޽; ;ȤKF%ҿ긱&055Cm4%%%2yG}`ee wvؽ{wILLeBAA.1 xox9o_r2%afϞݻw#''Iv)//S^^ŪU{n3_5>lCJUܺu[n] 5S)<AAի#00"JUUU Y`>DDD 00U?m_PWo^u_ I~׮]ҥ EǎwoJ燆cǎr 8yg&}˖6D666V9;vY .`Ȑ!"hbFW`` c4...pss\\\p`hhf͚}Q~nrJӿMT/u?I`RSSeK4b ^rCLLLafV}% ~Ex2b|jtؑAM SSSl߾1@o33&UGɃ]55]~UϢ`"~6CtW(Lggg̘1nݺǔ)ٳg8z4C @ L$gggdddhӦMe%$$`ر1c&1rHzA4!k{%=z4ƌk}O>Exx8Jl}_UUŋ+m*k(ʯˑcccX[[cϞ=+nee%zHf^ES Сn޼"77WFEEx$''#66VnDb۷/fΜ9s0տkq6G䣬wTTsNb!++ -b<)sNۃ@OO'N`qUiL?5qjjP;lp̞= o&,--]z2pТ#q-nݚ9 3g},5-믱dI0~m|6/"~:a6/\cǂa֭:#zAhP+oلp\cĉ9vTc:u GP="BEEݫBXXX0ƠB|| ֭ ::֭gFh$s'''/ e޽{m׸>lmm ӦMeKPQQ?-0ABعs' 44k׮sNyy9 UUUWm6rQQQ/9u>£Gd ll@?G/ pwwg\mOYK˩\.W-墰P&&&2ƽUpn ׸Wֿ[v؁nnnG1x`Le}6?v(޽| 1=̟?99OkE4h PRRa֬Yr#L:))WQTTyfy(//#Þ Bp8ӧC2233e2[cG^^_Z65WT>U_(R[[[KAm=yd;v _~%&N/bԨQyH?_ xԩSkoذF`` bT^|LF&};;;i}Ǐ"o鱱S ! 7nܨv2A?򔮞Xm~[XX ''l61cRSS5*1 AA?SN1^HHL!ڵkǸLdڴiٳ'1sLDDD0igFtt4Phޡsbƍ$QIZdd;wfʓ~4hJS$ :xEGG#44'N% 1~x&=** !!!P=vÆ *?|0Ν;'#nݺ!00Ϟ=#O(++̐Xv-4iR3e|" P=bӦMի7nŋagg(//ǏE*sOA~4^6meN:ܠo'Ǐw:۾_TUU6N5ut]{ P^^Rv͑tC @7ۛafH H!kWՔ{ Qy&<=="k֬a >I <,wf6A}Q\XE T@˅ T̲M-34siK{o*(" J* "2820+9ϙ{y~'t4mQ?'''Y?Eߖrrr`kk sAqqqWB!aidݩ.ڵ㙖,_B!ɓ'QVV&5gΜiq?{n888B"Ç zɓ'PjpwwիWߣ_~Ӄ7RSSuB!6nKKK"$$ںZNپ7ߠwLSRR!jyZhzn׿`aaڵYxq[ֶdK.Euun:455Ů]pyiyq)u}BL:IڬYxR&HLLDbb"`Xr%򐛛}bڵRŋ(..ܹs1uT={ΝCII ^}U,[Y>Crr2 ===lذCA}_|/_Fqq1]⡨>][ݻHJJBFFrrruuu߆M}GZZ޽Ǐc˖-*ﻫ`XRӊ u%BSdd$17c„p;bd}xRZ"sAm-1*LJ].lن/m.,XYYqUUǬSPP>xͦ|TWWqPXXooo&juW:m[TYϕ墰2!/1mEuT<-Vڒ9::ܹsӧɚfooX8;;222;mE㢸>/|LˊKgeЮV-FuUJ+qp1B/ qB!V~GwH:QQ?b2]bssT-BgRkkk|넄o_(X,2=[4Pȼ4f[EsUݣܲwx(ZG:<-՚vUn[E.,,Z*ﻣȊgCbghhcb/:eա+ԕBll6>A yME9{7mۧ!={++kdmO4 ^hL[^u-m[RZZZɓ'jѣGwu prr\#^UU}}n.())7|0թ !>tƍ;{plYDtD((,ĵxR΀5j_P+WwxPR@?tS?? ~~~S'O(_f71ľ1{Dm۝Bvxd`defb +O7t9SL*/bqawp`%wBjW'tyyvF񇛻mÌ(Kt =(Dnr~&HOC[[[3ܳl5^"fwP<])5cb` ԏNy+pK0ԫOJ~UeJOAp y=ÁrsrHj2MLf5.jW'iivbX[۴h|fX~P/ IDATXp8 侁 ۵LJ{{z"+c XlOJKux 򂮞օAJ$m׎"p  e~ s KXX6t- #:}cɓAdd+ tjr++kxZ^l6:|>=y<ȺP<])-ڵ#L}b O1h02$3-Ì(|N'CnĈlv,_{$M>= ץmPW'И~l6::uxPRzeH|n^2G rZq='-)!~ޕY/>Ϗ쎽n{By DGptrX,HX=F,C"kffptt@t,XH/;٣88FÇi"3q iؑ|]znnرc{$BKNj3p+RSSZfC,MSh9+rjɽP&NĤ@숛 RrOQټy^kpC% պc,Q߱)a]Zviҕ3t,?[9;_RZ SSSJ !Snc|||q%8]{JPޠTVTH$Կ {yyO> ܺuK#e=)5P(DAAn޼NF5CCCTTT )) `dd1cĉTnVƆs`Xk+YZYc/;j~Hp@䊊QX,ZZ;nڃkrߧOѣG70`PXXD9111U;;;\zLj#/Pݬ w-{R HԻ۶wpћGAm-bWB^RofɽRRRPOɔxxxh\r?o<ϛ7nB,$&&B$W^,ߣGL8NB]]FRqo+W0ϋMTcii0틁BOOO>ŵkPVV!C`ooBRR{޼yHLLĀ $&&ѣ>}x3g;<==ѻwop\ReT 0tPC(ݻ>n޼C__QQQ000aaaB\v 5552heeAgϞFjjjʧ0"VHİ[4=QMuUZf 4sss@ ٳgɓ'ÇT;XXXܹsׯ3|}} puu1Μ9: <^^^HLLYFUʠe >Y\LLLp&yGRR~wlxxxۛ)S2!!!Յ['4f ::r/z%-W' '7.b UBG$!c^A/>VCV6:p#gJz׮^ń;BQ=,,-pq]ٶɽP(sfٳgR׷o߆;wHDQQQrׯ sid=x<%mRBX,浮{R=#k}eo\+*cKxzzzoYmSSSxyyZZZJxeuuuqrrrb1 aaiE y*Vr_^^>txin`` cxxx &&&v{PWWwUt)m7o|MM{P wKTWW#::ZMM2TWW7{+3rHܼyyyyٳ喱qqqG^1ID6}JԺE֣G===CB;M"гgf4.tuu1x` h 55 ۷5zÇbAOO())4mG>mmmS 33VQCCCX,aȑr2dff6{߫+߂ 8bx< L31M_eI71 B:Zg߿L:@8: ݻw---gy1ehii!;;|DJ 77#Fjkk!/1 5%%CHIIiU9ܹbCEERSS喱e?`X ׹~:1j(TUUݻh=ʘѣGА#l؄ͱBW,ٻOJe3曼]TQg̘1lIB<}TnPlO}"" KP[WamX˕0ۑ5k222ɧϟcXr%M;|S7^YQ=uBZ [v~ 9]ssԿC-W޽{YYYXZZv餙ʨ;b ]ܵ =Ƚ/ErO!M!h_?"5-8q_0OeT]?-~kXr%.Xc^ֆ7;rYG'ňobG`7,7b$'$c2V3w例j̬,<;fϖ>ob#}OAo3 f>DgP%Fc!h6A"x!Nt13QQ?W`֏.PPPIH$X;ض_l>u ؿ̞=ƍʼn2֬AL:o:tmߎؿx=?x<5lo"V2$ y]:!BKq#߰ "bFŐHĨ}Ѽtrf;7Dncc7ӧN L ԿԟθƽQjckl [NS½{6θ^dӧNёP_sOisn9BT^PZZpbfD!GqtBܐ`nDtZNbb&O[;{MLack'OGEEE!t&ИʄѣFSXdeLMM͜ʂIeo㏷bر044ijgfffR;cB:'ɓ'QVV&5gΜiׯ__}eeeHNN eq&6nČ@WWY[FgΘ(--EII 6nlB=!v)S2 ~Yy9^5 /^jpC`unj{M053!<شqcaW6v>=>C}Z ~#FϏ.BHr!%+V`?Lӧ0__رM߷o_wrbW_aW_ɜ/TeZN`` /^HuttH|MF̑ Bi/t澕X,tqd ???aÆ/>M_ =!tκFdDGpp@,SBS bD=7n1o<u$C^۞9sG.|>̙\6m_UO>ի. ccc!66Vm_ܹs{2S;l6!!s}p8DBZD"8N$ w.$۵k{=#-- <s2SԶ9V^dff Zuv’%Kmg˖-X|WٳQWW/CHH>cW^2e$Cy BZ2JjX,K2gX,vsss .Dmm-Nmm-,X=z;wl]E;w&M̰sN+,5 ǎZ>;;VVVx*eqEXf nݺ%sY@^ZjzZZbbb֪+r%l߾p8055ŬYĴo3>Ĵi`hh]]]L4I*MyQ_6l1gTVVłi! !!05}1+Wb}{7oFqq1=zD7ۦe%MÆ SXFy۴i0k,&غu+VZ%;L[z9"""0n83R{=lݺھ"~~~Xd BCCܡD"yԩ? @-[`͚5{ڎmpM$''aʕX~=kZ|9838 ZS@ իWO?1###|Xd CFF$ z-l޼Y*Ap._ >ݻw#00'NDll,3mɒ%?TW2REuP}4ʎYq _Υ[[;NNDaa%8-8y zsBW,ٻOJKd.`̗}μ-X,JjZFF;v,?~ A\\3pqqalRRR0c DGGo߾r˨h{Cի̻w[ymp Lb'N@~ȄnYP}e˱}v>}<eUUUppp`ξ;^nG;88 &&...Bxxx@1a~=MkMekZFUʠϕų#+Ce/qfbGT|˥!*ŨzȒ8<'B('!B!tB!MM!BHFg !BB!ҕC!,wsбK^ Bچ1ߤ0K%ߖn9B!tB!MPB!(i*:bJ !B~̘83%]/n9B!ؗ3 }CO=!TTT`M s Kvyo+W0˴䋥-JKKү?7|Kumm-\\f$-)|SuᛘY Hֹ֭ oXXZs̚=/\PዾySVy?z;7BVveC ٳ̴2DGGfo  >8r>|w;b?9KuSzСC ˜ ''O,$%&Ɓ+ꖳ2%EHL?en~~Xtڟq1s&nX܄{B颮^ǖ-|>AAqĺYYxyw5f͞D"|'pqk[,ZϞ=N圱 ;?3:1) bI̴G!dXxFXxz#6Ν;CȐ̿? o֭[ ;;[p\„W^? fX011ýtjm[ͷ7ՙ3G R)'СCII7PSSl~ӳL:-[ґgggoVΗ_Fʟҥ]]]|V?d~@$&$ }~~>ܹfÇcE.Tz]U<˖@]]B,̙ݢWKKK/1x`+**gO\]]ۼOVcG1zhz$0#]9 Ch9Ewس'#b@[FFFr׻yR~?l5;whc).eK-[`ˇ[?­[3f?DVv6Z7???1m߁d&Ҧ_čճ4022~;-wsssD=p˯jy-^C;io"QG:RXYۀob +k#m6ҥKl1%uw-]7llLIqnHsHMK6A [kѢ%t,%Y%=BW >}۶%I·ƢEpv*Jd377ԿĒ"OwG{{=x:Z;v |GEmmmKž",lMkƎc~Vk{]n###ZShؿ[Y}Z[_AdƛVG !/>|9]ݸ+r('.jڴ8q$!n`oof[j@WWY[Fju̟5k PwӰhbe3g>S;0w΋zްZ^8~x 7HM:Ir;HaPa`!dxPl~xԸ&"4Z:gdM|ϙ$v1߲PC4ZK%@(3.L9>I؇'^xp?Ø1Lq8c>0sowi KBy'L$(*ŔL )i5J/僔TPjT~SRפI KKK;JKsIo.n*+=LUR$JjzFFFFGAf@fL!K22i4'mMB@5t=I@?eN.-&RNZN_]n\\rle%o)0,R+*)R k)Ua` ]'X(gg* *+ *+*[+G*/T.Q>ĘLf s#0$IxOtm{*n*<\:*TYުɪUTj&jR;60Yidɇ'SGMCSoSi81tLܪyJ_%ڪuZ9KJaαյ%{۵u u"tV<ԥ&nmқDF>_EQk  U k э\ҍʌn퍓ww&6&|ti))eSnͲj̺2M]5aizbmvqWsr,,^[Xr,K,oXѭ|[5Z6Yﲾcða֦拭ضֶN.ή}}%rm3;dߩot=:l].,8=.]ڮl2nn\ gI_z{=yt\yzz{+zGx{?I]G w_ß_?`4\ -0,8qI8i:#`Ɩfl[6<+dVɬKB/1{1~Q$%R.rNdu(iKĨbc)Cgo;fNΜ[s .:Om^ʼ#EfK9\7Vn?ϙW{PЗ蜸%/<łWI~I''W&DԥJƥ* 4uLE9tm@qE171S r$F$Y.Y%YD.8Papa"E=e1eK/ݻ Ye5{WZI]U V]iƚkz~&G6Gs{uuX5{9<0s>'?˾>g?}!z1<Ÿ/^WW#ߨ|ke(dѻws?~h⧨Oφ||.bk###"=z`&$ދb١hT F A? *X@P3`ч1? VV d'#r 41<|y /⑑##_.cw2BM=rjm_A|m`X?h pHYs%%IR$iTXtXML:com.adobe.xmp 722 424 t@IDATxxƓ@ -{ޛ M" vP4 oEґ.H5 e{7O9sK33!!   |lHHH2 {@$@$@$`6f3$@$@$@4; DfM $@$@$@6ah6C$@$@$@ Mhv͐ ;@$@$@$`6f3$@$@$@4; DfM @N"/III 9%  =z1u%   ܹsp,X0:::W\/#  5j"y ?l2egDHH%ly9 m? j@$@$n*bHV 9! jP[@sX+HHHf4;lJs"Wر /KO @@Џ#  _"@×u!  &@#/;G$@$@Df/= B$@$@MfG@?^vHH|_zԅHH͎~ KO8##$-V+\-<,cQ0 8&@1@~z{oN-2K&HHevΊMN vG\4 rٲCbbrKQ;jYMlJ$&;g;`TA\?Ct$Oximr=,!'4-)$<_H>$ !p_"#ө47?SN12{ӷ)%?餤'v>cbs+yFh#QRyK9rq_5T騜QIc\_HK SK0=ad^ Y72RHrwN 43i@R)99Mcϥ4oQh%jϯޤOHHNIIWeKXQ#7l}z9J, ̵.?/TkٽGD|_5 "g/,zAΝnպ}e3gO].%_2ʵf7B`b;uA[|s͈|㿏?g* ׯ˖-+ٳg_tiĈw}H:tcǎ+W.T?|.\hҤ 2cƌyOmڴi5jT\>xE>~-Z~_1OΜxO0" <fW.<+)W|&Lqk̻TkQ}_,:#6~v/S[3wΔ:υ-'ZIg%aJFWͨ3:`}pJ LL!3߹sš3vk|CoU̙;wYeݺ'5*~tdkVm+U+Y0` wN>qġM 㗝G[oFwW#LO_av ]ZVZ}j;,,m۶Fi¼+V3ψʕ[x1Ǐo׮vqϐ!Cpw߾}׮K$zqҥ3}6 ,S7o>c iG%s{qX=8i6R +6ږ'Hc)̎l_~ u'4hsB0'@mӓLd4 8$ү,l+͛!D(2>C=Sݶ\Mj-8?DpwhsF(|-eB C^~يDFjȡ96Q|>qr(6 Je1.7;8ǎm3wnoX=Ts*wfbLLrw֫f/8)Zk_vM&&M5ԨD#f&19{k[GմdSes(e$3TY Q ^wi4B.@IXp3<0`p|rbk Aa"sH֩]6Ç7 W^y%,,ˬGD}fΜ.(> #ywws{kYH-K2pXk^m!2M?wCV S( 6!9AZ!MH!$ N-sꊞW#nlIl?zK4y;~axM͕-5~4)5iRo˕=݄١l(%=Eyܝ6JbbVK|K-i*Vxy UGW)/۔oH9w Ɔw"##EC?|FVڪU+Bdž dxM% *ɛ&wѮ]nm4J T>"nRЩ[M6BFK-!o~jFSHWxǕ i1KK/!<7$=9ӫt&4"H)&L^+]K9$ d\O$Ԟ?DCOEUzp(Bע7du ̆ T’Du s˗rVyK&0#B4̙0>޽{ YTB"ZB_{ѶmG27і~f# ];H&(}fG  68`][bP&>Zꫯὸ&N/X 6@&  lׁ<9ҠAUy.o <B0f>v-Q9JTej^#Wȝ&޵84gK'\\tnbEo,Zq6S#X(R7gƱc0]2l0)|ر¥!s{W_} KU*MoٲEUN^aʻ6lCi&%XN^{ts22&$W 0*Kbt`၅8mN^rfe2]l& 6ݱcIK H`ef7m/i[J|O#XFP 8lۧKK4%-eRPT  oAk6lg0` {%ϋl}re;rKM~RޭS}/dm vtY4\ +rgN_BK`*Qw_O,UT~V^`Te6Ts*q-=uqEݯ7\k}g;sQdYt`j#:Z$@P.qYx]1~|HG'VͰX7NyGV`C{5čs=b Q-Mnv b(֧O͆rdqA7|SH===`6|&"sFnECUa6(،õZ‰*J9F#Q :П2cPmŰD\]%sHl fʗiiu]H͛EDGG YU%w(Í>U qR3!APn75_mQ&+0k3.ܕ cZU8",#ѱJ Uyg(~k ؾ}CkIrbbbp'O"6eƤ Ҩ^y=Z) _ۊ2eȒƑ8k^yo G-VWfzD-U/Oj-K#X?VL@ug3A+N@~.U=hZjZ}0]È1_N̝{E%sŋxa%)SF?R Tz%[`/@%DB%%r3 }U؝X1[ZܟE/Τ'NxGGD.ZY?p`] S~Sѭbbdܸիv_U.}}i >l2°жn\+{jhyي'[YH*ev\Sp~h(XNs*+?u2i ĴiӠ+&hڷou}M$@J ̎+rKeONNkxum-T Lhܺ'%˗9rdڬ`C ZkxL$@@@ݻsi=z&2'3pv2Ņ&XH x=`3ptKbgRk:0eӵkWO*V+o> yG5+ {u n)OuS2 0ZW*jHHLQm(HHH ˮ o[σڐ @]#  "@÷!  &@#.F%_uܹu;v찼D,rA6ʟ5jlR 1~{޽;Q3!GѣN`^tX"};|qP'8nʕ+띖v˗/2-<}O?})lRX1iWn9BwQOBs}Ĵnp8B ߿N/_|?uz;<ǖI ȃ<ܹs?/Mhe~9oDEEi IOHNeٳ72 m;v8)I`" NMSN:aʣvڞ\\*U4k,B0çUV{/^t&Al9$0bC̙#\jΜyVp,=c5̖AAm@n!A`#rRRRpNC0N<)+Y >jϟ?}7o[2| Z:eB0A]vg}>({b?Q/^T)Un1yqi~ _~,[7?+#I&/3'r6m(iD`Gc駟NUǏ;H 6lСx Q ߺuwީLyƎ /T(D&+K]g8PbSyHĉAN/ҩՃ}Ȓ% *9 һws窌S{uཽۗ-[zݻwcɒY ,/hv˓^Oޣ… bŊ/Nll̙3bW<.WX0~x|h ` 2D{Z/AKom۶QFZ5``sg]'׮K$zqҥK+3[lDo>*4o\#ƍ`TVM{xq>*K,93+J,pw>7ed썍arʖ-mɍ7Rʗ{7}iC5B#ҥ]`hJOOmÒFxC["B N(xt(:Ar&r2f9SO=n:_j@x@`  PҪ 0M+V30׃ ab S0`~ 0 7F\0իo$gA44l3M;VV7 !l`AdѢv)V{}8-5=;9kMޔmےq+@|>}ڻ)7p:?#c/9+ѣk#|T>?ސUV%$$Ve`Jx7z`#\_/Fz m|{FQϧt"o^l~ÇgEٺ2!9/XA?AiΊat6&5ZU #wÆ .X߆KLl\"Xxae+HJ!2tSi3EDbET裏!.kժ 3DBI6,),0cPϡ [kẀ#G'&nnٳ8JDNΘܱ-#_e}օ5MF7qVe]D+ K/=3mz;lCmSC|;b#Sao,T Vc6l*j`sh#~IH裏fU~}̕`Fd׮]T5L29mp!Q#x萩ꚜ;_fHዞybЪظAQJ#n^DުUsW|e^_\X)Ҙ%)|]jn\_Xn[o p4OAixg0-КU}zh!Pyw~s,%m _`=`hXbЧߐkwO8!JO"ո&/liruQxR1xwij֬ժLԎG]V窶lZXS V q?3=5MJ{hA%W*b-ʤxWcXQ.*ӧ<KP6I "uf<kLD-q񡚷?% *͋Pnu*n91Q؅f y-NbZ"ozm.]6,s\O N-ٳ뛋ÿ\2#+~qK?T[T|ʔ)c" N2*b_p4[bɥQDB$'ʀ,F;( KH:)kiX-Ўg sG yh1Wl7E^Ha0) # &b*_\B &~bc:yz"x#Pe}V9pYb5ri"b/0ReG(6Oh^_ 7"dl]!>C:`W4{*%cI]0䈅ʻ > XWF 4:}>h=uwYJUUiұWi#]c\lbO>gԨ-}^aFz6D25Pb:HO3XW)%$#"؁dU: c;Ly0(}'Qb{+JLc)f'XǂE c e]# ؓ&M%ᜐrrD_dy(Ɖk[J'cGȗu `JHFr-0B)40q^Cb)շotD4,TˊX: WD-61SO+HtX/ڦazL㠭?"@o=UŔ 6"hߗ|9 PE3\#ag.մiSڞ?|4/^ڕS$U_7oE8a1 灇 z. ρ݀Y}1\Vk-&rXr7DWlSg#>G< |Nեف[pk!2 g-* >a†NFŽyD.\s]B_rDbyQ=b"G>c=6e *2IIo-_Lg&g(q4;|Po޼y(LTvRuppkcxژYݒvȐf#<]JۄF;!|R9"\*寲PKs7dn!YAyv%bv򔅑0 UXWx:|~D>ivvUMe0>֜4 [vJ`%T9.@+e&BC4F8-g7pZ1Xbv`8! B_:0ǿpſSm1}OLq Lr^oeW@TU1u ?=9{ +Żx4ojJKn1^g1=*h>Dn Bp` U`"U}UyQ"l.%Uax/0ݽTC8 dGD@,qFb6d&֜R[exW^t&TsW(s2ǵDiѿأg}WSz:j>l̏ Udٺuk@Kձm' 1``vqPZ6mԏŕW/P(Su܂Jqqqg~!5l<iK5<@ x:tL4+`]a|=lt @~MFN'YA ywS:FG9)mT>Z +n#)eES SZT) D ᡁ偭s@%*k^(SҹBsID/kyx)N8ET7C]9\azӮm 7xkr7i}[f=B8?!B/vbƌs&I3~3~ uZk "(?\Saݩפ lsp=׈Y!},_r_J"x{.XD+=ќ[2322Q,Ǐ';&D#n5aKepF<bkڵkGW-Ƚc;4/>=aD e_ɓ'ˊVe@< m]TjJUKL' E G$'ΤFuܨϔ{=~>_;4@Ӧi/cӰܹ v[.)SA a=S%rH$@$RO 7 O 7 O 7E?xhEOwk5}|ܻwݻ;;Cѣؕ.U9G$@Cvγ^%]9N۲^zHq8BǬ`+M= 2׬2>fO>OP{lRX1L*ThV4Z T7$@V {VTry:x?`|9qqTTg;wnŭcǎzc& @@5SJI+˗džq8](l(z4_Ms_>HL/ #UVx3QÑ:%yH zSwp&` znaх[eb|Ϝ9sW_C>x ܂9,L $؎|wٳW^E&.#99dɑ#Gɒ%x3gΤ`2M5&Ɓ$&&~'X"-%7*|gw̳mHHgxѦM/q~ʕ_~š㯿z׎?>w˗/:tղ"0z%/E¸|>|xQq;JgϞ]zuU+/QFH[ xQ|eE ~[eL`%< qȑ /D &@l\/ #Z O :XʻpBe;S85iD)i͛71cw߭1?p 2/Px뭷|AL !xǣ0G2Gz)dH,YD$@Inav,_f$hv#ƴ>F[l! ڮiasmvРA.\6l8nܸ={)RD5%qǏD={|ҦM| c:CĴ>̑ءFlV2v GKժUb>Gy%8KYTQKc |<Gk(`{}p]Hk[nQI׭[W]r%[#Ɗ4" 4;1Pk?00qno kC BFn`H!c>+*^&eZ  SN2eʔXm;v0 CM\Gxx4_Ut駟^`=+"!#;w%ŋ|6@ycց_wI 2x+?샐=\++ސ&*TQ~.cC8 t6i @<^z%7i4#`QԩS4^ DB@p5lиx#_bՑ[$@CfG*E`˖-*v_DRN T[RFjo!Gb,uXaa=%rX$@CfǴM~]u%φmTAz:屪~d]E$hvs&/>#~UG⯼ f;E>flX.tñRI!f8?R28KAUZgǝZ~8DuPlܰ:&VXyWbz؝ . @$Keʔm۪26_`4V8K&H͎z>Y{\ai]v~$8:R]&+( ?xl>:%0B+YPr_ɂ I1>|Ž^~BlHH8yT^IH#,fK$@$@$hv }hvǚ- @/O$@$@akD$@$@ANfG> Gf} 9~M@WH$@$@ pg.\3Q  pW\H"hDža3y6G$@$`-4L.\>L#)HH<V4cǎ$H.TPTTR)HH#@>tӧO/HHH+hvxuAI$@$@a;r6H$@$@J!o  ۑA  V4;ɳ$@$@$`;#g$@$@$hvgIHHv4;lGIHH X '~ hv؎ @O&   9$  `%@#XyHHl'@vlHH͎`}7 Nf +o  ۑA  V4;ɳ$@$@$`;#g$@$@$hv$@IDATgIHHv4;lGIHH X '~ hv؎ @O&   9$  `%@#XyHHl'@vlHH͎`}7 Nf +o  ۑA  V4;ɳ$@$@$`;#g$@$@$hvgIHHv9mo E )))>>>!!!11-AL$@$@a;r7}RKO]ᄏrs[<&Lػwp$/\_YK9r݁ROmzڵkIKK;ydFFFkFϜ9T:O~O:u뭷+:rfĸq[Oo]thC ^<5GH&Ƈ,r)J)s_ZfʥB ߿7mdʦtA+8'`Y;k'''@+x4obc[(qӈڲ<@.%?جj>9s檶EXQGy냮 .H%{VTr_}ZU2w|?/sMo6QQQ W!m͇xh́%&%%r #,"ptSuUtyرiӲRKR; v,@Kh:uYY۶ngTmapC޼|Ѣy jRFoT"=pS(\vQ'Oެjw.K(ӬY3yf1! Főؾ};HpΜYo f{ھ}{Q+%%e„ 6%1TRE<SսXصkyrqدP@B~} -W 2gj F|1ֳgh~x92LZx%ѓ{VWCFGGBk3#!!_lQfFƘ1K[GhQ^ IMuJ+ڜo]U-ZtѢE~Z2nJä ̙#ǫ*rK-D*{׹sswqssWGh }Ly9k O<1x`8<,XЭ[7g%}3MCLHK3@ΫW1?K6?**ʕ/~ @0ȑ-n].7l8Vۤ5^{<( ck6Dǥpaǡ(111flTGX\Ϊ&"ptl>;D~'(n˶|RRtFe W9wˠ< JgGiw74$ێ怱pgٝEg] 31˟ZmZFm׮(gw}f ᮿo"6#L86]xѢML*Sݻ`R11.=|w[\r-!D"i֬H'%YQ0._NG+_gӵnݺTR[)^z5 s%bF97رC3LbŊd)Ss۶+kZ o;R|%KΘ1G0<sΕ%E5jԀcǎŰ?-2Y/- ch_d >jaQ J>֭[Ť̝wީLzq`!ߘ,L%W^ޡHL<СCݻwRmW4g@ W|'@}D޽e90&\11dȐ/Bz,X;v쨺+. H0 FTԾZ53(bE͕9(,#Qd|\{]K윐Wpn:V2n/ؙ3g">b<#%˖-C˗ÐUV!ۀMr9ڷ4dȭXssE8>})bA;>ŋ pG+.s|-PIئ[`aذa"(aܸq7n<~HԂw<٦MzBKg9O̻[Fb(o0FÆ Eux\́}x)`+|JH,իW1bN Bd@^׮]- !n:ٰaHOO;J] nݺp̀؏?nղ#0bdy? 0dǝ5l|es+˻8:ᬗקk2QŠWңGq֢E .^W@s%U_\'1U-w-@!@fرmFh)F֭+4k?qq#ʒLټ}{ aT)d^o7߄!ulx`|`T9Z/cw4]܊9`Cr;P+D1?駟 !P!K W^i9rr!aFDDd-Y#3b P2O0 nI&df|\xR%0'󬨎<2 Ul7y(^zIH'֊ρmT2]Dp0Zb-[Āj-0G (GasK<,35*#y0Bs[\5^L uф75p-8ۄi; 䬤]ϑ!` U-_pȊl VDt:]4GキUj4iRjҤ.~{~+Y[+?@EԧH+pyHc*%(h7TUGb3l*.WYX%A{ 2A[@FaqW&ͦ:;Ҙ 駟ĬA0a4mT#gs%M񑵌$ ׈(e2BvO24" m؎mX¢UL 0a:~z!S/'Pfp.bJ0"G*`%-e^X)6 sGL|dgZU~~Y ۢ BO.9RQ<9Vʻ"Pe.@&"'T%&#rU2ݢQ^/С4Y&[eNxAQAKrT9K#|Lה(U BOL:c!ʰV[W~}g/r]a؀!6<1|;nՂUgc}#(ϖ.Wl_S襡`V2&Yd,im+W4h GYlԡ-cd9'N_Q+b3e>#20A"\U.KJ'dA̖,sSX )CU$1f!#|Jhl_gvG{9l!2ad]ˢ< 9|+Gm[r0lTEt*6"*{a;/2U9r@Q "#G"RX+/J<_e.WY]?=IN@/5~9փ\}\DY&vzo,F~Ow=2@0~)Ο߻e˲zk6lg0` {k.3ߵkW `ٖ-[>ȎX*:in`ik6UJ+LB;+K4*²pHf"`xQE3 4IJxGPX*pQ_s'gWGٸ:`Itf'Nb'إ^#~o22zw®2̈́m',3;,\rgT:uܢcU2R˵205Ə_=v `cVׄXR _Җ s"('v@_R\ 6r(bfx܏T wE}U~p]|p~^8L3]- ,kl`u@=,J-5 ";(̛7CfUv`Up2'g _q[efs=f}Q!"Xo߾L@@6-؟MvK ̈A*^`g d{i*$!4d{y2(3>Q;v7%Jdm[<\5j[ז֧O'Yd2…*}L2?D`o֬Ts]w%IN,sOR[p*v'6뇥(jժa5se)_m$ ,.;lJ\H hBo>p_ue3A7#* cc=ܹKVf-?xqc >@I͛7z"92aaV\HB<, xndK-#EfW5}vX˲"C=Wٜш)a>o,f@>0~A4*".VN3icai#.flr+WNw}$L޼Yۘ\Q # y 3b9,g\|bMUfEovE 8RU ,iύx[t ;L`(X W*Pv5:ʣšZ!8K {cng_ ?Z!wGAvrSm]9-i*+oԌ-7MA2edy˩ +lyy!ym:ya]dD|fX]{笤|7LOB/:A gB*T(hfsg=5͡E iݧlBa E@WHאdfO` (=!V')[ڝdF }8<plؔ'"ߙ >f͂ H(z^~GLE8T)nljv>SrRF |d$ >_YHQ8d6hМ#ʨθWf(.A0BpR 4qT Qqn驼B~]PUH~,iBp_{ v(30E\"kr)pʕ&oN|VZUUp \c#ia1.`l,k\%H':=HuSeJbP:2$l:5,oy%aV(FI@0=1*[p?V̝ת#pr@W_mڿh5S2 a}4ID W117 A1f[HsTgR륔:!״gQGx[}2+{ {T '}h߾-04Fum-TPLh95YYt83`a b૯6ZMx/X%<8G]@٥Ò oZv팸:(,H`Gw9%7 Tl,|z`vۻw3f8,4;$@$@$%4;͒ @|Ϝ=&  /%lHH͎{1 x/g$@$@$|hv3gIHHKhvx <%  #@#9{L$@$@^"@K, c  xܕSҒs)^<@w_w;ڴ)4,˖xHIbWw('=/_ҥr:qtٲձbbBի75*$4T0A$@$@$O@_޽KG3cXk>zttrLiiO Ȉ̟?~|￯} +of„{+E̡ tHO͑C4 W̎-[&׭ws  Vk6ܞ=__sT 3R]]hZbb%Sl28DK=\׮Uz5"u괌>aöɓdա4 dGّ|={J&M*Ѵ鑿k`dbCGn͙+,ZBNyt毽;v#;N`4pɗ.z a;M*-!H 0;6L   'L8}k͚!!9F۶E#s<=HNH#_|p͚R?Ol1cQ^=yWHMU^2M$@$@$`,f﫯V C\毿.һg@I9z\o64gȑ7b&ŧԦ2$@$@$y;nE:*tBP{W]<-Zt̙E_~9,"_# ?{Fh!Cn\!Q !5)0bWbOo*Qn qXIfN[p8,c6]C4',O$@$@Fفe <Xqb+be.:$Scyʕ+򘰈[WO`ʒ982'ϦwϜ99 {[WQ#U ̙gv:nR95ܣdӦm>8Gd$8{ރrmV l&$lYU+_\|xb!h S}E !  _&`!:AZ+DHD,7Sr|/3.ĵۦLGpX55XHHR}0;J5o._FpwLɶܹ!*\Mz"FDzk,L!$@$@$y;aF[\9>s'v`QZZ&M vVmjnC$@$@ [ù{˷oo̶&3t+{Faf*c5J L 9+'YSSOZaC' ?XMMK{,]|5$   3;Dp by-ȭ]32i΁bvIu`$@$@$L̙Xi"]B%U;d4Kǎbv'22V}&d gC~lHH 3; G&ũpa0q&,*+w5"s&é|w27z._^Aw. eZrqޛϒX1N9܊4VF?TIpxyB22r-?_2tmR-[&'Z׮]2l_4`{-$@$@$̙aa5Dpxʉ%#K.%qYkWEbÇ^qV͞mtSϞ]]; Ǐ_3v,fsϟ\> 0}反)S7J4mztr, =2TX^R%yP˷juJe#GF8G`.2en;6򨂘J;Ko]]k2s>D^|)wG[f/妮f+< $fGT]~qj&ǜ>}T\=[y\KDtta0ˀb0 ~nVUeXxmo-KW Z5{rD+Z,ڠszt 7p 3IHH dtxtv!'I_6B~@aQ77G?yr[GFL~xKҧ6ntV1 rY#9zbR#UXHH@hњոr$6.؟T(^8p. @ə+X?ܦ?Ğ=X.хI+"m7KOI$@$@$LO(HI(ѷKtꚺYݺ)V3ƍ1 ǭZ:6V% An۰…[G gLqQdVB$@$@E ̎ +P0D KG:uZ*W+S1  !VlG`bOIHH'J[   $@#:L$@$@!@;* ! ) ]2dȴiӮ^>zp% pHfC,2?))).]*/_d% pFf32&M6=C|ș/R, 8%N l۶-));,T`$@$@`H<DZzz5k?ogQQQ͚5lHHhvxDߎ!E 6^3!!a˖-UV-\&ϑ#G$@$@ajhhĉ:tXr%̎|pwuמ={[nQqcڵ?x^̙Nm q0*K:uW^A?7ocرJ:|q`v8q \uZLMMcǎ9rѪUt  #jdddٳPeBMcbZu:oڵK^;t /$/<abzu QR-D^z4 RJ i(w܃oG0o z;|Ax_ɓ' %:vѴi=zOpryGEvl߾f͚Ei>쳲7(  _&@o/?tC8̙3E{!E"44~>;wM{ѠA5ʆHH!@ξ fXծHu/.$֯_pמ hvIwZbPv%K(A(kbŠt`$@$@#@slF֭['_.>} R`sx-' \X;Zmڴ~0; "XY6M$@AHfG>tu͛'ԩc|cP+/^ 1׷Be >HlV(I5*6psǶ-Fl~RlH͎xnu+Y,*9s;׭[dm ]4;|أիh ӨV`v 2HH/A%1!GEEaoP}  %@;`:ꋑ.1RB(k4uQ `1CBrɸ #4DUfD"%Wvg?9qf^s'ʸY`Ԏ9.3̝;INێBy$ Pvo7NhOF X(K: 1㓀$P[x1/! ,[ +M-J8 $ Pvo7c3#6nxi;2' H@Pvߺe$Xi s̡Ν;5 \b_5{,H@@9hXp %!aǺuB'mڴiIA۷o/im۶mc@s1;v p#1H?=z4lذ~ƍ;蠃lj$Plʎb.?~!C~I`;C9$O^jޫ^!Ϗ5*y!t%ߤI˗aÆ & t3$P:J9VlT3o֬Yӷo_>yAO?4 5bU 'mo{n|Ok׮=w+ĭHÂ$ $GDuΝ;׈sҥK,g_5k|5-V>Cx\x aۺuv H@C@Q9O3Ű}x}q%KZl}I]״8`С܅;Cv5%  ,%I};v,8 p,Ԗ pݖ Rq_ H@6>5BC4k,FqŊ+c-a&QGծ]? H@6?GŻ۴i;f\>}:=6m7X{^+K@pʎJ|aIvARXp!<]v-j/-7kܸqMo$ J&Ii%>}dW_}g^B[N"v| pq.( bkuVC/sNpb SquH",,2 $  Q Zy;K18餓J5 d+t@a:ڽ{wR̜9-G}4SUe!cݺu~H;p@ ]pUP@PvTγL;l޼y≮ '|o뮻nҥ]vC=t 7^k (JL՗_~A4[og8ݻ1cgy[MFzI@&&K~=5wZ*^X• 0Vxʤ0`T!>XA_z*%/ {駟>y54'NAy\r%xp R&hA@Epn޼yʕqUj; |-bǝw9vX5wqG*{(! F=XLsxC aw j*ye H@C@Q9?3駟(_uLNHeeeOJё#G)u]ɹ`.m)gؙJf{㭷޺+O=X?a AXBq'x"V  HRQ{dFn۶-NSN\xaʔ)/IYi R(,{ e˖娐%VV&&_G|9؁`nKVO!/M6hтuɚ%  TeG<4:˷3Ť.w}l.$7#r3gW/>o޼>G/šZ!j?J3(d|@X~}8IG+ H@C@Q9zLɻVŞ4}'ѣGx lZi{}S3GA\4iRРA.(Y-U1HV er& 4zu>*= ͂Iq & 8b()maӰH<fZE9.,RiѢEaPoݺu# H@@Z1J*U2"LϡhMu֑s&MVKX`_c "$PpNq;JHbIlY@PvTγ;S:i-!kƌx]v&;{l*`$ZWg,`\( 6Aௐss&U&O!mlւ$ "쨨]/iO>B*;,x>#N%|vɒ%XZwuJr>.N.9bԍP'tMAA}&EX^bf`z5לp -K@ʎy{gwcm3l0}{<qA0"&~m9JR4#MF+k` R!"t)W_A|_=~CW-H@4ʎ z⸏+NSf͚}+E~ ܲ>}zN@,v+rT{"!,)o~Y`}ŏ>} 8H߇`bl8`\%)XƏPyp9묳PHǶ#y٣$ r!cTgc%y;q4 o1G|ΩF`uJx qХK0}ٳg8I/DIrq Rf,AX$PŜ_F u~rfaXիi,hSNkITRl vsBh,n ce*a+JӦMO裁iҎ$֦8Z6yղ$  aʎ:p45 ":tиq?H 5DjYn H@H#ue !D8ϤR3Z6 ͆[xV H@uov,u#tIc7$X2^z͛7c^LXb$  HPvԁ"iR~*6lxdž/Z]vpف4)eI^ $ H@#'Kg3bE f;vR@sPbɓ A;vH+uӦM+ H uvLL$w>\\Tu%2GIʙ\_s=z+~ ҵkWWͶs5ke H@(eGؖQ|zS#^|駬CĚ ѷ !ʤI=\bG3`kر+K8* $eGfK 1|pFH6mBׯi8C06Y0hѢE!x졼o_{`gk֬7Z B^ H@H#-eqYfCĜ+pc*hI CSN9QF & =ʃ 1CyȐ!Ȭ~;Q2$PQ,Y3ۜ1cF,Îݻ3P Y8p`-[rخ];VXq R-裏>kAjD@Q#\2qXYr D^.\zdٲeXm۶~!"+&FI#ܹsIÆ5IMIJ$  m8uڵkd0 N:NIeBiӦUy6KD$  W;Aq|=)_ >$,֭ VԃHMw`%zGht5 H@H#Ee ў4fhW(Ϝ9߄HBX2XI}6( Hr(;5kV$. Eyr [ Y9M#T֪U>z'-H@@ (;J?R1_vyBBK.ݰaʕ+Iv_a +.}{%Zv99|p„ H2, H_C ) P͛UׯI 1 ~9#]8L<[nH'-H@@i (;J˿UQzY::u Gir* Fy6=ѣGUoҤ_|ѷoO[$ Ёijw'lUh^t۶mEb:3l0$AB<={61Bj\%d/9j4`+K@@ ?(v rAaȉ*PHXI腽K/t)y5e^Da~{sR;xҺjۑ$"W;RY⦰ ΢8 |3tJsJ&E@l+~'-K@@YФGAiٲe`IDATdye3,uRd9O]ۗ$  !eG>}֔!HǍO:0ر>6Zm1֬,~֘wK@@'0Nsn xy`0dȐo=ɸSb^\xv' H@eB@Q&"abѢE{_z5qđ#G>JHsYP}7I@@FdH@$j$  H@Pvdn$  H@PvH@$eGFF$ e$  H@Pvdn$  H@PvH@$eGFF$ e$  H@Pvdn$  H@PvH@$eGFF$ e$  H@Pvdn$  H@PvH@$eGFF$ e$  H@Pvdn$  H@PvH@$eGFF$ e$  H@Pvdn$  H@PvH@$eGFF$ e$  H@Pvdn$  H@PvH@$eGFF$ e$  H@Pvdn$  H@PvH@$eGFF$ e$  H@Pvdn$  H@PvH@$eGFF$ e$  H@Pvdn$  H@PvH@$eGFF$ e$  H@Pvdn$  H@PvH@$eGFF$ e$  H@?S):_IENDB`sympy-0.7.4.1/doc/src/pics/consoleascii.png0000644000175000017500000042364012253362407020730 0ustar georgeskgeorgeskPNG  IHDR|1P iCCPICC ProfileH WgXSS@HhH ҫ^Jb/ !KZ+".l9uw̜}͜yf`D)<Lq+:&Ey6'Cyw Du3ֿw N*G9"q&$žނL7A$B\N1H16 .hl8YYDhG+B<bͅx SRS\Qvlv7lv7<6:dR؋F?Ujkф5-#9,0o 9l0U Lq ?b%E>Ir;HaPa`!dxPl~xԸ&"4Z:gdM|ϙ$v1߲PC4ZK%@(3.L9>I؇'^xp?Ø1Lq8c>0sowi KBy'L$(*ŔL )i5J/僔TPjT~SRפI KKK;JKsIo.n*+=LUR$JjzFFFFGAf@fL!K22i4'mMB@5t=I@?eN.-&RNZN_]n\\rle%o)0,R+*)R k)Ua` ]'X(gg* *+ *+*[+G*/T.Q>ĘLf s#0$IxOtm{*n*<\:*TYުɪUTj&jR;60Yidɇ'SGMCSoSi81tLܪyJ_%ڪuZ9KJaαյ%{۵u u"tV<ԥ&nmқDF>_EQk  U k э\ҍʌn퍓ww&6&|ti))eSnͲj̺2M]5aizbmvqWsr,,^[Xr,K,oXѭ|[5Z6Yﲾcða֦拭ضֶN.ή}}%rm3;dߩot=:l].,8=.]ڮl2nn\ gI_z{=yt\yzz{+zGx{?I]G w_ß_?`4\ -0,8qI8i:#`Ɩfl[6<+dVɬKB/1{1~Q$%R.rNdu(iKĨbc)Cgo;fNΜ[s .:Om^ʼ#EfK9\7Vn?ϙW{PЗ蜸%/<łWI~I''W&DԥJƥ* 4uLE9tm@qE171S r$F$Y.Y%YD.8Papa"E=e1eK/ݻ Ye5{WZI]U V]iƚkz~&G6Gs{uuX5{9<0s>'?˾>g?}!z1<Ÿ/^WW#ߨ|ke(dѻws?~h⧨Oφ||.bk###"=z`&$ދb١hT F A? *X@P3`ч1? VV d'#r 41<|y /⑑##_.cw2BM=rjm_A|m`X?h pHYs%%IR$iTXtXML:com.adobe.xmp 1810 956 Vt@IDATx u[Ii@1F; FI 1N x% I,d= h{ ,lK!-$ć4yo׹ܪ{=uouK[u|?>NREF!`!`!`!`!`@S~S{h!`!`!`!`!`Cٺ!`!`!`!`!`@.fe C` fa]CȀUS[A|}(ovyQUqϏFӋ~,C0 C0 k9ӎVIvq r,c@?&6ATƝ^wb](:琉8moi{岨HL@h[9FIQno^VйK;77xQH<%Nwn?}[t(äpPދC9^j-ԁ]@!mǙ]RSLAAk3fGd'ageVlVu;`' Srގw8"0F_DQ:}G'ԎL %9V`38dF|N_OMӆq"Q́mɪϻv̘ڶG^$f䈓LQ.da q5 pҹ^&JI߃8Aa<޳s?ۃʮZ0Zg8XB-Aڽ"cs~: 0G8vm!`!KR,lYÑu:x ]&+p(ot%)əPlřh/t>/Qx;>!|ݛ.͇q(?ȫtd!D+4v` ɸHD6: _Sq7iKIOA M5yGˑ*K&kՋpƎ:8{4:";Ek Gޏ?R0lށ?󼓲ѡWvRkq>ǩS(wgQѭ8Ξ`Lit rba݇vDsm_Fo-^L恻y.BASzjwDc{jC̉##C0 C0 J 6AɗW zW̉Cy Ɇ1 6 (+1Ficlq"ANBߍ{-NFsoWqq+eg2u^}qh-,2 eS(o6[zBmg>uw!Ng:q<_[x!`!`@8F.GFv@ @EΛO)471/n};>wa(h ^e^ڟ~h# ?`ȼos9˭vV,J1/ٗe[F먛;;n GbTuOZEš:~+U!qy?|*D~$}(.1,ĸ>~Α3y9~C9WWq]r=4qE S0(^8#]]} Nsk3# y'EyI˺]yW/vx厽fc18}<C_Lu~%"u&ѳe)!p$ߡu7ȼÜ>%A;fʉK5:&ײſz_>藚Dȣ wI<$_W4Cyš_,㙷ʠjA=#ȶGwis(ge!`!`t OJ&p<#~L3lmfX*'2LvH6on6NcxduwؽМtg=2cG}WtFG07ߕAyƥӷ寗4@w#[pq_ 3TtҦ2nFf83) OCR RYdWvXYԘk?^'ZVʯ跣EOoplbv5i#g'v1ߧ@e( 梇w2J]sJAm.|uz\ )=G,$n}C׮ c݊>]"pɡ7[{1씴h. C0 C0@m(Oh駨<R(h"9AmQ}pkcgkO37ʡ_$h26>olW4'~c6[L\{ljiȱhby{y%{9ӟ>T1F}[vL}4C^c`dٯGivY5]:=bD~L)<5C\9Vŀ]8aAg}0բ%[s֍Qx^-KrR{hFڑ̽!;?-r00iC_uxGٺ)d5Y\h6ouJf|׉9/2"iQ'TۻӂЕHγ5EG\|Bag˹i'gBVzjQ1,D;LHk!`! -pqkpibqܯ 'UzNa:Z_7?/~n)~p7=+ʈ$v2Wšn)v4 /yZ`>$lY 0INI˳07TJI-3]@ps_:i)ϠϤ6˼~S&.fe{U_]źudu}J8sGU8ǁkc\ y^cǓy*oqhob|8I>식3c>XJĊ5ۑZ }>r&2gm晭Jcp.}@ 1&)^uo"7ahgv>a-u83`&ɒNە̧vh=Ƌi̪1 qn(/wN@1*Cl5Eq8l;Pv填hC99xI^ԌB{W33@<8jg5#Ĺz>a]3".,lBߕzR]>|\ywϾ3ȩiEsYo,a˙WTlNerF"7x-4F`L ޽[CuB-yم!`!`@'xآYNv)FWc/=`iZQo=/š0q:NX1m'"<ˢABFH9i{`hAw&?iY/>Re|t,d>l |e|Ʃ<=[XgEO㰤ctށ}9B9+Rf~FPf$&_drf*ɋʐ=F9+rn&p'cKd5PԪ+= s$ΠBmg&6 0YyuZq`,Ǿq:`6掉RkԽeizA_ -ޚpb{p򏸛6~Rɢ%1 C0 C(m2vsCSzh 栬SU;.Tiyu ^5KY?i..V#q.?q8C釱vhE6 io_l=kq{MJn#8/oY V3z噷ВEРƫ.M;W0G2,aXIN(=IE\s˗8ųqki}| ߥjBQ.k{/U͎/ocge/G#,.C;6ob/B?oc|1 -`,3%g%M}#G^ȹֻ͋񽍰ϖ@g>Is)d:Dڒ(}p+ #}C#v6ȸj,zKh-!`!`@pUoz]qFpczmPOߓיDphUzJqOp #:N\D\ F_[)Fzv#(~k1*A8 .WT[<Π3 SXޝ\)yGpMNq^΁=Wi>>/ڙ,y!#uıoB{U/IC~;ы\w&/*O {*Z wɣQ_H~T=;&)^wn@TM=.9 \N{:rąlHp$oy}c ;4Gp'KRZbqE\Z$C0 C0 n#U޿= |k8Éʯ8?(ƝЕPu™uK;Byt"lHMDy";mS#\eI%]ƹ!/iU8L p/ۡlJ +%<~~q $ @R،uqOuʨTSRƱ핐>GdPg/Qn|NigyPzijˑˎT<֋ߍ{h#c&v#D QuV1̘֕'p,?r8E!66yEezok:}ϝ^z0ޢy[.t6 C0 C:;ΑLGS-ݵVjCy ebNrs~6 PkȧK:*MU'JB/CUU\9|QƑ⸴Gd'k,ʖvO6[Ӹ$~$'-wN-{؍)FNQӼgv;vFWyvJדMj'~uAnygW[w$yQjGΝե;Ģ{ RdЅ:AhPd{GǟR{+;z^6>9~]z50$:BoW~O0 C0 C(-reSq'c x^0B: _ lb3Q? 6fX4?W2v؎b,4kg\VsYXk58RHGwH~9Ǥt.$5 ?/$hH.~q7>A7LH8H= SM#J,yYt'bvw5 9<{ pqykd>yo™?Ρ,iGСŽ`-@8@ iZܒ7 C0 C0*@'r Payd^/VN6^Ġ\WM:\-Ovd~1*1:&̎a99-8N~36ʟh~UBZQt26^㨍'xoיִ̓jA(ޏ+NZNˇ {Eikx"hSD|SO'q|q3.2Έ/䡌Qt4c w&}g5фͮ= uǡz={>wՔK-sp|1-ڹ{<Qtɢ^~P˪VMqXd9vZ_ܤp3~Nw>X?Df9L97)]ze&AF!`!`@@yzW' rgүb$E<%?=Ua]]a5Iq&m EMPNGX>1X͘S1w1&xY/G%|@ *U Z^[fmY\18۹0 C0 C (Z oFs$|Eڇ|>bA"(uN 0PԎV]A&?3}Nq(Yyfn[p`v~w^Əcޓ؅bSN M[9dEKo8C3jǢlGcgR) ǹuK xJ8 2?8`W:q(1|_YQ<,lxvՐη~I f҇sN__ܸt3}HWB;-nJ_WESu*dj>z o+"-M:75Yj?Hq Δy$x7JӴ Tzt Ԫm;Gekᆀ!`!`# gQ^ :rK؆"b1<6jāM 1ވ:j(ZV,t]CQʃsp8VFlݼ]iö>!NfDߎf1 &1Emcwr*NѼs_C3e/I=ۑ,ki ݭ0>} Fg尳tkg˂]^i(1aRi^`zH8GWUf%y2>c'3&Cbd G:@{\ m(?_>wxjF>V&ΠUF="ݳvY-]™+Au_%8P>dOU+gܜ3O-o'UY?;9_eQblK!;<-M9Tu7V82JxoE=cSˎEP͒Ok4E2r2PB-A]|d[g \\5 C0 C bj_}Yqj)Fd}Q1^g=.=0 C0 CBTǡRMhe7!`D16'r YoYM9-8SW*׭'6M֚8:-kF -vFem,NK҆!`!`t+II>Y:9f v`g8 v% #CC@!9GIYaSNK&UsMMfmNvܢ{xK-ts&ca!`!P1zP^7R<Ҟ^eѹ~k2v )R˟! !г^;(VLU_hc:v1`7rۑ<Z`!`S*nMpTˉX9d!`Ljb_݈eݠ8`K!`!`Dwg(?ĹCyg&/d#C0 CUՖ1 C0 C0 C0@_v%tV!`! 6u5 C0 C0 CΡ<Ɂ3 C0 C0 C0 C0 Cnt0 C0 C0 C0 C0 C=̡n0 C0 C0 C0 C0 i9][ C0 C0 C0 C0 Ch%RE > h~(:m4j7!`!`@!,1W %Ȇ!0xwEbx3=FEGё~Rڗ%(bidN .#13%ٌ =-%@`(:@Վ~"ә)+!`tۊ<07qkݡ?̙̊!6,m^qOw iwLw,!z g9YbGiM(n2[Eg)ͤ +.T>9h+U b]Xߎ)0v|'Ɓ(,*j/п]D]v7)-4[1/WCX1-qOF5 F8$F)E=[o6TE):uMqP_Վ gf{j6vuȮM\YƼ켿_dw,:tݘxH RN)10+)TWdM 4 sq6Y+p`:K/1R>Uq8BxAgϼyb;a/0ޅa 1uoXvЎ#.Q, \bA*R:j>cyLQ֧y~*~?۫oeM.#Mq[{^7|_XEM\ƒp$hGGA6x6qymȕW`+m|kkؙ6tr5]fUa1(Li`[ DEMrD f;[0LP$s zw7Jum,4EqZ+4⡝5ϰr*kn\sanGa\)f(v__Ƕ o+?H=g׍à[(?r2cMu8;ÒyI>xQ3oA qw&E,6\סNάH9!8״R(SV*F1\JZMjsB_+ a v^})h)2cnQq@üϕ&ulcAgEګZI:̜_w(3m,f?H_rhMm e\yc/p2ϭ(q7.m~orϛQYy.Op2帰v矉ڻ 5W/DXӏ}y7x5[|cN.2a`[ケ_\XH}vXNR͡wW+gr-_dl$rܪ3Ⱥ3ʐZNv{U􅇱[c:^_W{ o!pycN9*nk[9pmTibEo|36uS2-&1L"ZSgr\OpuϜqݳɝfzCxrB~ ;hE)Kx%-aLTrd^ѳUc q E7}= %sv3AT.jyc/lwL>jK/W`. i"^L)Żx> ɟO7f~.;|XI N7{%xBdW8ٝ'v%WӷJ[_ ,jق1Npʧ *^eT`_߸5s__90/ &iv [U ?;bkRCuqp?g{;NUsؓQt33?>V|̃RwD8w&^< ̩h|vxT" ;挚z¹Sjjhq ^wC)SDp,,xMW>Φ4\#wЋRXw13ZDQ?9+#SvSb0GvAr#ǹȇ}3\2F;Efy;g(X_?>ަ#6z C):/<0 6\XokogH?UcP4\8 G;dk0.8F>V R%8>U{ ()KVUGւkqR~yG]tۥ}=68?'n#QG7#-1piZ5 |.S_} \۴Yfs1;Э0s 6'~pF3'C)_#3mÔ|C哒uˠJWK:0/<a NdLG}d8s(c@IDAT2%ʳnO|ߙ*L%qGfU`*w<ʿ.Od8ՉfM̫ȩחz$ (oE2E<,Z3M ~[*2D p=||۞r:Sm,<+MwV󯟶8D]Fr1:ib2y?oOYy0x"EhXhS !OC?Xl5 yfiE f-v&^EB Г-zT]mB)Uiw~von֛~enOL"^ͺ O;e7I#-f(缉IҮ?MTGYno}3LpI8G:G< CF$9Ƹ(؎Vp&CaF&uc\Wr ۼ $xؾR7|H& gusGR Y9|xN%;b]`oʍPj?8d3Yʑ}Uvr/_4:%Z>aV?v\|$jJg\l >;a!0uPR[av2 D$ͮS;%{X{B|ztty*Gq7]0yv8BJ4*V(T#vhHJ(S'3pFU{H);(es^1V*?TzQ)xp;u5 od!%6B&4n pO׍>kYi ]%"_NoB^K CӒUmtn{[ 1o]EwjO_nr2g"Je*vγ!H6\ :ìU _r# '~ЧCQ! d#-%v 'p񿯪1F˞z 4 @;2)}!lulUBӪ"Gp;?TDZ@; 'TI8@2?U4K䅉1 s5J?X7Ƹ VҮ >Y__R|=tu;ۡ}) +S$S;Z8l0 mT+t/-1ы*Be~(ن'\AUm\w10rׇإ7Hi"tv1|;5⥪q6ך32vife F{bv7<<-gnLH0C'Q;rqQT_f M)%+gFс~^{x8~qfc8~ӃEcSZ3HNdG7 s"K Dr#nH~M0 ZPSKHA%~xUYǍ9Xw FlO'=oN!PU&EO$8$SvGOz3N8YP(*W8ܜtɈ9z:Ѧ6iԟ]{~Ӷ8OYT3:Y!}oNFt\7'BQ^:𝙂`.`MP)AUx y>T>.UluFaz䀲,l@ܺg WU6 v㴠+Bσ)Smj.өZzjx~ ){\T?Mi<Үݚ{v,gㇲ.+j;TU^Y&Cßqqye7-, 򧉹rUgdny_f[3[_WAוj C-8*S|AiR*ʩM^ߪ!p6(^G-[ydx}|P5ޑ}a'+vo@PW"XI{>q>|9='ȱ~F:?8T>~mݣhL&o1^1w<m|ҏ|fK˙$م?^n@Em걗̅wW%|cM30^2-$;il-+4MEp/+T>~mW Xd_A'zvF8$^PjHt*Lg ' aQθ4ؼg h޲U|xN3#Ґbehe.z~"%;oYngٶ8ίuJ&vOͼ{2u7qU : B~SWG&WC/jgĿ1E9"t?˵U`Qܥ_C0*eȍSRW(ډ5Y'S)ny|QQ'l8ӉM:\]3c"GRyt:y;#E)?E -!~qQ]Rx_{Q2 ˳6wʛL{}WX7}=Y-.]5~B8%㭓>,22E@}5(ljW{E1wsEA 8,%td*$*sKc/Pz3%^-y BԣjvAUHJrMDl\݀_s5yW;4/rNCpEZ#C0&!uQUޯ89)Y&h wEQ~5T> kșl$QXxM70 ^[(}Inٙs"gێSOe-ݭ \vOJdd(j$m %y&e%xl#{ R iڸ| xS wˇ=zIsr֧01a7 dXK{xufGGt18_"ʣմ~$ m&jW'c{գ2aO1).鳢%Wvtlz [GO)^ТGUH<㽬y vkI䨊:̿ͅh?Az^8Td1|\?3GQ7p1.?:9:πڇL\,. Nov2vV.hW}uݳ<DwnX5*E=Eg'cPš,ǻ}JUl/2pR0F C0 {AcQE)ߋ} cCB\_P4\015[ijy彭0ғz=0IpۂxoŀR ꈟe1rd+J3Ok("#Jڙp(G+M(@-E9YD7c,rI>;gk|s0vZ>;T^Il@vq>R\,>MݘȞs^FO/cG&臒y|bF'{{6gFl ybdixEfM=`#%!Ksf>Bt6sg" hS~DDnA`cQ/X)B, ΏNHj} ur+utWT.h[C>tPtL8}Z~77wqʎ{0 I@ʠ(ݹ:dze~?sbd~^3I})gp;gBXrB]>]+Gȸw+| )\g$ ~S-dFgݢ2v64=JA>MEpg#YA,hr N_Wj1/;dmC'.'B#9N$q5Ffwdk|DԈ1:9R?CBDE&ٍŲ:!WVw(BlKة6א~ xO<fs 0v|H,/{anתPqAۆWG@7f0{ H" D}<'xq)~ >5I @^n؈5?o,nrf1ʧ|4[Qf^|긘?g|;$'30+[`{Nܖ mԢCC擹R!b:YU~Tw"Ytc.h/C?$ k4W#_Ia$=CE{i`f3CGO6l@nEUb],Z豊LJSʲC0G@gvʕO9Oyg’s5df{ Fc3$<1vD#yi=B|xU2Tȵ>GM?R<)C_@ѸL PvC{AߨGY?6lpނW_cD}F1ڊ83u$MҔRNӸ@BB/8UQq*,Xx#~E} Boܗ,{=y|=HyqN*P,kOJԣ7iwA[-VRyUV'3yr%Y@t(>喇Es_K?2|Hig nCOޥjBgՊ79i1v2+oMѓg~wə|+Q2?p^x+:b I4Z|xy\U. Q,Z+E\>,r]&5-*yRP"f\.!ܘZDC0!vPnNU(Σ[S!p3 %xH(i=i<gAG9 (Ux(T>Y ]uM 3J.KQvJ4j~Dž-1VC(YŸsݏ/RD}U'WYJmd~y?v-|.2C|te]էEv#CZ~pg3b?mROAȻy[RvSo¹mfO;'[ґQo_;Z#țbW &oujXQ2Sj+Y]lx@LJi=xc}6Fޭ, &B)kU=7 4 ɊxTkrwtӨի:2VXz YxWf{V\afB`|@gcőQ'chPHAr;Cэ~$= ~o4:KdWsW8gUեBܿJz6І\ )ꇡIa1Li0`X.@5ԛ.8c2L|}'0ݢ*΃X~-ؼ#n帐.j倉S9Otl {~#l}m!tDXt,BR7tm؂k_8.ϕl" $:] Y0,s_4W/J_.*߫(W+k!!`L)boku^țsOCHiRχz|p}dh6*,2;{s:+nC!8M㣭b?E'2cdBWyӫ<_3HRVAd[nXnN& \/U^bT+>&<_$g> 춁Uڟ۩Hi=EEO( QmDjf G#҄4&-aEЩ|/v\;p{b}?6ad{rWc7Ψ{l"|(iw]9㮁0fƑ=8frӐo`86 g~Mغ*swf kn1n^E"o>%N6~E*Kx%eȃ/C{HS(5q\dLJJ=zΰ"9"A6DhQ| A5IGeQrc">"]9ϧF93?O 8g(|Ѫ?ܖ:_eA[TW{I4&:!a=)tINcAus}5^iX2L7BfT#`gx/f+]Rd~A2z'2*Sܩit\wb9s|#_zdOeQcTN~ IM<ULy9tD\q!n37J8/nǀ$ѥ8L+^fw&zMĀͤ 3+l0ogl\k6\_Ѫ9d};"('B{Hy8EFf 8AroN_ꇓWH|VNq}(u)Z{1NsYw+7ɯ_b|}qp?/糳2o'~n,e:cg?RU!伮VPg6Ů; (Bȍ=>tN`+D`6,cwޏ#Lva#9轫SSܱSm։ܘ"dĮ8MޤjFbް 1f:?d~ %`+*cELpY$ ]uZ w&|Wh+̚.Yx]B>wNBX+a=iʢZ4MbYxGrh&Vw{;NgLˤbj)N<ρsJ= JAyz$ c#TxW@&p?iO}G's{Pҿ$+uHy;wV& #x1?ˈ+]~24!TDn8M+7GI2N.vW`<MDl1ъ.NOqi?t_C`! ݾ7$:Ӹ]8gR ˜bq;+iLٌE;J&N,^ϋrWjg7<7iî(:^o8ć}Խ|( 0s'lGWXʨ >~jw2)g^mn1F*Qyq 9-XH~I8%i/?$Qf3j˒?{~+eq:&8DQ3>=).P1"me7 Ñy)X[`Wqڢιs?t& <ͼ$թ=.2: ^#F_zYdP$D9u+s!FWy' X,CXHct(OOXT#v]sp ɹjGTX?[X)F:*Tga;kF+tbcz~c2.bdQ4NCìzZ!P!< ky8}ZM^ =i8dDUy JGP !yVR vܘp?ʳ,<>m{V}-|AVؐ#SYiݸzpty("&v/E(c9U+YpZ:nSn,\Bfϡ拆|H[a8pWՙl_ir^]dv@j7jis|VN΀0wClp'7Q69HkaUkwr'YEl3G܃^=ؘ>hwmk*g2Ars5,#+BbH\9|eg?0M ;[A(=ҥ#.`;euA,_Wĩ:>{V-|uijW[?i21H {G^NZ!F2x}惁9lWnޢ@ 8mFlٟY NJUܬz6"1>o8S3f>\ꤜH?:r#xXd 1ʪWI{;-{ƞ+3qc!tϝ ۹Z'A#)y%v ^XH.Vzʶco$1kN0YrCO'ԛO:תMUǧvo =lD'e;ߧW`=ǽWO.Q߫^N=mp8^VO*]x˦(mfϥԩhz5Ee\FN^c 9kWFwE_MӸ*K벊Cήs Ц(enGNUVrĤby3U ҟ;͛4Ӫ{. D=QzylGɒ@Y݈ZV{v;+P*I˭v*١LRPtoI;imxw0v?{D{:$Sr:ݨjj֎p"o;ES4cJzP׮;mG3xHo"Yߪ׼e4hs6Cbd1H7Jc'V.;@oP o_QJzXr^W~ɍ=AWUUxolܼ}Zvo^xTo@Xs/{ó$IU/@I4uɔTf2ۿƖ|n5ס:wgWQ&vy(,gt>e!=3Q >A}zfZKQ=5YXMfs?Iz{wsRpW)@D3PUa0u->3N+ja5BJCή,9M. r#xbhxUzš6ޛZi|?7oV;5(eF^R>>r aL;cWMmI v6:X^rrocyOU'R*J{|#k:BkpBvVʓDlی9gɛ~xC{HQ')m 3m 䧬kmPp%!O C1(Py+:gODܟ;Ew> ؾTromʍR{@`_UW@{sتi=T>L%͙,幝yG)+-Nkq} l0 F@J5aL(uq:n_D1VeGO։U8&&>l,-L{&~j]ʫ@?{eGq{VB" @B Xcp9 $bLx:(,[Ha1bx$z@΁Ŗ`BA(~έ;ݷT?UTկ~U]s@XAuf5fӺJY jWdcNݡ+m9՘ s+1+ 1)~rKyQe﬽o+,/p2\`@Cqsʄ>s08W"˘iI)Z;S{a.E㦷Rs/c&JԔ4¾T0E(ߝ8YK.zʶH{Ą]b r ?ZL Be=m(mWfYX _l 9\az~"A kO8#.wWTo5QU?xҾay/Ww}wW l8>,Xi8l\l@@+x*Hl\\ cF=G%" U\|q}:6d\0{f){ I `Oi]|OdR]nYEXcg;X0y#B=,?o' Gxs.O#yޗ_>fȺW?ez\BRVWaYz*ڋQ~E_v!];_Pi~ΐp'68 əgz@=t1uyv7wNꗇrY(}3sj=->Na ߰6bq"γ"SOW^'j\P [M"s)5oXx Z]X %30`wjka:VA͂,ObȰLGsa[?/TZH>v#180 O8/exFƷ|,zEEpBz01;n,DXTgA>@Ea  !kuqm<:wgȕ)5 \s{8];8k>T$&hҌHSPVx9DZNAG`Mպ}{GN74Y\S+̧  -ٍ6٣hסC$5&;_Fo_Ar0tzzҹAC ʀRoLمpU]嬫[\q{Ey]C0eR46tB?m*XYˡ L|o'daNoi0i\|0P̞?uD*¸j7̕ d hث+nb,64( 6xB.^`OPIơi0-?~0.މCnK=1re kMqazQ2 _X@<7[ #gz?QPݪ,Iyg=K0yGŦ!~m!&MDYR9<~qf.B0garE:;^ Ez޿ Q}wTߝuU|K3@D,x{W}qO0"V̯$P ʽ qw*Ʃ0 K|߽yuժChh(*߳ƹJ]kK;=6*NxH@Zj'}Q[YtG"^߅ `8>EswY'ie=5$K$@$@$@$@$@$@ HWs=FS8ܪz5P|Wo!>ؒ6} @+@|/BV>ݪ?y_X(Z^f؝st5SJ$@$@$@$@$@$@ aIĞɋwr*'q0yySL<G=| SWd6L]5:NXO'Uv3$@$@$@$@$@$@n h Mp] 0pgsR7;x{KexFY$|wJJvhVb|V*OP`oOm.2AC$@$@$@$@$@$@$@6mIxN$@$@$@$@$@$@$@$@$@$5,[#          hPn̈>HHHHHHHHHH@e          DhPNHHHHHHHHHHA)dRG}<⷏(sd!ЅlRG0J,)-}grR|/5c"    h)fKͯĬW+f?}~KiH  -ڱq 3ʀid HHHHH`3oNdx.-=@ȕG86{{R'"וڄ+oK9>ː+}Ne#     b Р\,_՗Y[tۃWHz k,t$0 _i\ovݘZr.Š[gϣ0y=,<^#     IB3F6a({m?: bv+c0$pZ}e9-3 @4ٸӍ-b+ؓWݤ@> Æ|A H|M Rd6 M$@$@$@$@$| =*n~} 'bB/)o~ ~k| eoJR3Ӑs2ԕxnųU#~ @w# *;>NP]-,Rax9l]vRg-RF]#cǓ9_Qyϔ/pWB]U.@裲#ggP=h+{ ol4ΕI=zRWb.A&t!Gw%pLWgw'+ኼoGx%̘.v8G>y/>~C4Lz ę\*-`|7 tRg#IHHHHH$),! ۱ 0(sk=O5{A& EJfWQGOr.)Bm6犨ԯNXlu#u>z)rԄps*ߑU]6lu &ݺ>'y}X!WĤ0xꗘvAl^I^\18[Xj܅eRZ+qX!_W0 \\3s |_x[C0*t Y(5{ߘX~-&wˆQ EzwaF3qfI51X]s|Y>@km^_q'u6 - ]"\+eu/8#"Q%X݃0{ [7ùwOM7[<wQ"iy|Df!'¶PZ CzKJWu}wUMK$@$@$@$@$@%0Vdè#]HW-f%Il$b${zcTEQL `11d `yQ'XVqx5ZSlr6晴5C?w An$~ck&uƓϊKu|`v%] e1 y"q}ܛYK؄ӬEUq&Mï5-j%1<>pv̎`oڋ0h4Lny' WYT^P}/<9 8& Ŋ(u~${k@'VՁWhaәN\=Ɋ-e7V+f?F_^WuS`x6'0 턁ڸW,vHX/Uaqcpߕ!5aOV'C|gq{,è-WΐK.W|\s+M}wpBzȶ3S\tzO+aJ_ `(v<#@J@qEwg HHHHH&Π}R-[tV##1XvNͺ~|, 8i_M=EW_.ثVZ a~8 9yM*]3cܟGyܔ+/XoVeԯ49\C#(^N7-F"\*e9     (@),Z[ \YNm$oĞQ!N5ȗȺثwxR7`g1nRjVE} 'Y'r1N:תWȗPްE(wem$^(aܿ]0Tb>'9;BZ1YG8\> Ge{1M7Y;d,^4rcG*;ܤ?     p@ ΔbQc]Wa uz5o[81N[3m,>E;rTL2 v> `Eu뙫]bˊGVWa5LᦐH/?QsC+$9MkZlT-ýX\sx;c&?\4U81}FN @ PQw,̘ .,ccַpS:W\u+QK`8T[UEEӸ^a0c~, 3_Wr=9` Ab ?ޥoFξׯL F_`"1Q87҇;jrD6'ⱺˮ© غ{}) I AYp,Vl>R'V `%H| 'b+)cx<+-X[?1NZO7 2^C판 iAj[ Z峲׾fD$_^4l_sZ"0 }҃ucGc/7 P;(W 6Ǘ|7b敧ٜwGЮ7ȃ `@>yply|ݤ$@$@$@$@$@$P09 J{L|G-I Vcr+^l;H't!/^mbk$w& |wix-{9 o={ 3E0t-+640(4anqm¶(yV&Ұo tZ[\ӅYHP`ø {㵃?&B ?Uρjy֏`eW&uԍ<[!]GPOLT,'A4h!*%JWha]뻁_     (epvu7=es㈰%[^+tjvy(,8_Cx-Nu0 #1hqIl~_s0?mCLb lz3\E(q} ̳k9{^U?. vqaη|/Hsv rLÙhMNan'oZ2gWw @{2>(?U]\/a@CJ}-l ekvcur^22 űm bN="ҤOė_N:8 *0aqq>WFo_C>܏-:A9ːD&H$++K&Wtf2+8!Y@ם:Bv:L>>A9ԇ@[SOD:}#D?U8R?.fl/ K@/#r#glM ]ĊPA`)Đ2EٍbXY7EZ/T>csԟ`@;{|E _(dm0wbu*~k* MkǂR2b[V_HF>^uVh3>Bw ڌAmnL\*$2' [,Cz*u(ݹ_D"~ vf+=Xe$@$@$@$@$@$@$@$ k3M_Ģ$c%0&eJr(^ $ӡװzV \^wy0A@$@$@$@$@$@$0Р<2@`\.L,>hd$| #ptbpHHHHHHHl4(ME VNFAU!zA1~;҅eQW)[f8$@$@$@$@$@$@$C85I b;)Nc45].2 5gb2#fXNt.|$A0 HHHHHHRV|&)4^?8T*,:Ǎ d oj"Ш]q IHHHHHHHu Рܺy˔ S @Ae #         hPv˓ @Ҳ)cH&4s.y#AOUj &HHHHHHHH @ ej= d%Ȭl]/vKFZ֤δ D.l{ \ӟ&tlh;jXԏƹU8idۍLoqo` F3 kSXUlwVcc+vӑ o,$O$@$@$@$@$@$@$@-E ڪTvxd3"NQJ-Moqp@> ~WN6.PꦪlF?12uA.g=dYǜ;tJ}\̍~ԷZ@G 0J#o VbU8y]rOgah>Ń{LF:` 2zpQz e;FY:|#<"       PXΙᑋ<)-L~$).H]۱jZ~b;sy䙙Dސȳ>Tn㔺XBd4og0v͕'yً8Ai q{=7LaE[0t-dzuV"? 0e0.Rr=0[mRV\~/sqNU<^ +8Ϡ >F=N8[+uGNcb C4i,`H z7b{0[8a2b_0FF)0[.<`ԯ8__y}Fj4= êq `+u¨;Kprڮ 7",ʛYKG`$-l0 Z #S[tq\*#[|a<v !W0\\3sS|#5CYyx^kPFϡMA udt$@$@$@$@$@$@$@$@%я=JWֶ[[a1ҵy=z0-dAh#61YuI_#OUlf6a%|c `l;R׍S0_%鏺uXdԦJӰxUȃ(\3.m(U# s Ec Gϝ>+^ _#lQw4P#]FysN].tz¨b/)`~EPd@_@( @ A& V( ]?UT܆-GѮūF&©%2?Օ Od/}0 ZUS>L\3DglF+A(Zs'+ ytx‘e=>hPGS'->,-ۊl >cP.+j6mvYD18#>^)1[q-e4[~ԅȠ[n̅h3QC z}Ifbœ+T+1 Lz"ɷ~0tZ{ւqi{)x| &?b r(* okWj 0cGM%C'       / y'X!A1UW̪߰`?>58v0Cc~JCaQ;MVyN. > {/:qcR[ lf|vmJW(;Bcs\uQE>*6xoDWmA1&OqNQ7G_H={$@$@$@$@$@$@$@$03RΤX{6SQB06El-oԏP=m?C89s%pȜv~+j2>`eS!9i97[եzk{s IHHHHHHHu Рܺy˔$" va%~\Jݘpa@$@$@$@$@$@$@$@Gy@Cao۸)X GzIG+ȿm9n<}9l x+Q'       Kɛחy>K$@$@$@$@$@$@$@$@$^y$@$@$@$@$@$@$@$@$@$@T @n4(FHHHHHHHHHH`rAyr3SI$@$@$@$@$@$@$@$@$@ Р!          A`H&SI$@$@$@$Jx#/\KH8= pRCg(Gǎ*@݆-OF$0L=#4((]?^)#J{/ K`}"< 7aNSjJ4T MU^z: C?p[3-LH 膾.pt=11:1?Ka2G^3: UHǛ5}|H '|x~ 'Sj7Z/!(pz0Rðn{_>@(wR71=c1'R Tꃎw`VKQ+OWE988*_(5oJAC0XANתE= n]\c I靤W|ZU`O2vǔ@꧒?9 E;t9a]o|{DU.RJ!P>8vVߨ|tݓ>L#Cb㧕h !rumQᅢv?eQ22 ua/e|]+(wٝV^bPFk;tu\c&_$T߇7 q4W9H}K 0fi}>MwڋL$ُ˲8&8o|4Wd(ڇ}G6=|:!,T!*usE[Ng@G!sx+:az\2eu?`%p[?Pj3^40NB LAA={s{ʳ"2`4aޓ-A9?*jT5nR686(_]գ0M'I~K,mЍ͍iefRߋ҇^$Şڤ9ip1Y [JWD=Bcߋ`Bk,M?K{T\^$B+or`v1:Jw#[&-J J)3mb ^&{ : MV lb< ڻ*LInL#ڋ\0yg 9h oG=yu3Y`kQ\1F> w76y7z.}qxy'a|5M(}0X%ߡ#X[[yoA])&v`/{9 T,n2ʟ~yhDL76Ộe/@zb2(c"VUZנ!dTP׃1B?MsڋψFr3b xy!]л`k+o-BOzh(H$0t3\HK@tlc!T1Ƶު}-Vt' Y]Z$j/nAI{(  y{}&6bеLfڵ θ=r8kN6a F! _:e=sabn@f9VMGﱰxbE}㦀O:ĖDϡW&D'[Sȣ'ǵГ 2Ax=6'胘Uce'텷NHH < la(X VB=+GߙG:\r0_*:4[RW鴣yLfbm,..K;xۭ֟Y87cE[2R{lx8{\_yr0٩=,eC;za{+6c$=mj(AZ[11wĄ&{g+tM(z[Lzz,\;i/ n]\ù<8ń9f_.+up&Гgv#: nWvxl|jw_|TGT |Q&30! ȔWqA׆Av;?/V+^5y\0:)q~^- _)&ӥeg0F0]q >N<0 O>1oDEqi ƿ<򈠝އ]1tB  0u#X=)~u[ʛ.9eANGQgGUԧo2"žrMga{զwBGR_kVA-[/q;P*1:Ւ>uWkz06K= z_Fʒt쪸^Hr#u_X9}lQ=|\1g~GL!G("*s욥ԓй ]Ajf];c_1z`TY)ԑPAF^w^g.-2\gЉ#Љfr|񩾻,a֌%iPv0J`ρ.oŅ_}}9i=YqquzU ˃nݝ<2>@V^Y]Q>B=z1hRjgdbtrЈmVIKK6xGs捍aG?0C"$/?`.ӨJgm+ x i"iOYQ9뇻w0/z>TŪU7p *+hCwd7 sƵu4sǯ z:3k"y/̘^ (,GsO"aݸv# XuX!S\#n0n#u:P6쐄9IWH/!:ih]nM uuF39hdPÌɚO;|Ǡ=ݍ8Wz8{lcZۘ_z!+$WCP|RR鴱"i2h|ӛ|އmP/S2-ieP-'E`Es5P@IDAT?s!_B. {V^cyi(=C߃w!e.LcZsvXiش<]dڌ~ >zAy't= 5&kAۯ18絞^_i3\eӟ?b|!?ިr'{_kHkpШ}~xGgzZoB'Ԙ=br&2N_q+o>t^aT]O]rPقcFLAPJ/kCp_5U} *`ӯvUij{1qzabE0:jvأ!$ ҿ> E:ma/'8y#0  !8q6:u[+\Pzy܊6oH|ʏJܾA/YdNӳH0f%,:egg/ [? [O:m -e҈utCU.3X]PC^ZhYje9LgΤ_ `TvW@]ˋ3 ݸIʶBڋ4gD9@듨C:ts$wKBGxem~e0{}D%U(a uz6K%I\*9AB>VB}/smd0iR+U!O.Rtw.K+x7ߍ{2$~+2uďODz[-R\ƛNs~EsW`m*ӱQL쵞ǘu16]VbyOl/?~/iU*@# }c>7C/^[Ջ1JฌoGo5}tBϜބA븖ItE +N?ŪWT˾5X][.~O{8ʨEpm1\B:ChC06ڊ!.}6c㜿> WdBF!lfW W>WS- +Gv9FBC|Oei/\aMӟ/c|&cky۝o8 +a|*cqY\xG&~76/8o(q+͇ci|/|A'(?g{:*C0<{?guFs49Y>+::{R.첒33]G '*\âkjXўct_E%5ޤ, $1w2aaT/~F'nX !|w~4,]/)u݈h J >/*Q6G^V@PoиiHK.PW8?ؕYEgp (}%MlY FJihM9z'0fWk0.7Nu!ˆ0׉^HmX#}C%\[!@yk{ FM etqjUG{W\ӆЅx8k%m n[|8g~eʫ"Oˬ>ɌP [.QկpKcV.た*R^z2mXG'va,"Zw'*:y{-ʺSn_(]ֱ&SJV=_4&ki6ѧ&aˮ;&;`d}}>Ho[eпw;KHplPW]O3;UMz`_r84`b!UU{`7t@W̚UW c tkR¿38t63BgfmY1B6~,ugbs5qS`HCUFyqgQO{#?ڌlxLga$;!jЫi~+țczux$;apj|CMnhB }w1Ø Hf7EM'ʖozI%~:L&j/mY~jZ!=/iu+(O] } fʀƿ]XPh3p%Wcnp+}hzl[1fBA_ 1} +=NPfs=U@D-b~oŁÅUm/W@v9M0c#|r~?ˤ#T.TR#t *vNNo&ۙ)'5ܯ+9M?4ck΅C;?E+ 2(⯽2E)~)srrux CYQɤJg:Q)(eC3O[a&e'$̗@2BE#> QDkJv)B0=ϧa+*]aq%v2H C]0C? !hqXV± 4LDnԯCGӦCg>INJ^[g֫2#$R˵왇a2&76fL7(_t+1irkAЖ6"twuSK\GNp?"S^IJ?745r+*Osr;q}w[@oG[}{xV01Xfk=vCx}}thK=|?fxW tif]q/gw"=FFoVA,TE r|Q9X|B}AyB\wZolȗ3Or.a2,l_\rn\VO{%ʡpD?A:E*aXS sbȃ|M?7w5F1ƷOW0P.g5)iի;*S(QR;/2/60,?0[ ,|*q#]7+PjPOHu>'% XUS}سQنEǥ-0<9_jDj/n2OB[5 әQ`TL.$f.pzZs7&z+ ޠz%$0~xHNWXQWVyt>O[]_g0 7 >* WRUm! Ҙ\ 㗭0&.=sƒpxk>'lHTu[wHхnjjxА@v9M(ZBN<8"xx'NoT )NBL y5Mˡ wڐEjt< 鏸׹̾9jGWq`Pnj.0C6FWː'mS6/YwlbE|-yȧ6T09 K(kLFcD6;]"*}^T}ϓʴ<DZ#1`>V!G*(5oЙZCGo_!tjPFu}2ISLZ]`%_@J(}sxe5'ჂX|VΰN/90( &t)ղc(x}5ȭ{UJ&ӉAev{W[8CtUHAy9y~rQWni0"<}FQ*"XMGt!>w"~cD2%E{\ܛ4WdF(a}C~Y!xEMC[loD_gCZ~tXñm ~bh'<+Gb\. ɠ NIui3(Nl5>=9%f"]C)z^qӓx¾q` qΨPEߨ/ü=!kcO@5{SZ!a|PWhwKJ͆1!lӓko  Ps3Ŭ&ԫ2ܼ1*1QоK ,VmRLNI':y]= [S17glq][F ܤu"!d{9]mWdD-%I#Oi#dy~En`W"GzDTs}Y:nmb ‰m[0g1)oD s]` 7sfϮ_6Hْ@^ 4+.sȘ8Q^]o5e?!Nua/-@yOWv&fih>w[Ȭ.qYkzkD(Sw`|FuxJ~M4yP4ˀf 8iu`>3c֬͠@R b̤Oh|P A͔K3yndcg{1;Zsu_mW& ˡ^~#A+޴.#pXmvqFA= eoLdhCT[Z[tH.'wW $uF͡an%f9OךoKڻaaHT3-9[>B//dq^ì&p͝jgk7pp.Ÿl:z!!d.Pt{Ԥ+¿ˮmڕp^.rȢ E`D hHݨWsR/.XEIIv:O_co~8i̚pC7z+jC tn.FKoŽF `P/W =^=(KjWi܇UKҦij@|ztԊNWEpENm]4a@=ytn|HC!:qo6ҫMqӅtwIP-s!ӂsqA =,_hڃNZKqI1ٶ2\FZ)_ <3PG8f[ECcuʻા7s˶_(PsY<}ЇRG^H#竲LhEoLD)Ihv]"{!7˵a;W3I!=+N[!}>Cn5g4G.;FVtsoF "_cܱ&UpOP+ sD+{.a`>=~s`Dq}[}KP|qV{֟aPVj8W:V7륎";dWQ #=(z {Y܏R]=;V@ofdBMhs!*(d}Q'Aer+8]=LD%V7A7Y[N waTsR A3n _Bَҿ&̿h~$ (Z|=zRKrfy zG5V5E:~ಾ7s_S9j>–?ܞhQ8j^ KQQi@0LrB_/@gK -w=9F/^M^"|˄2˖Vp#S{QB*)^ЫX~t]{<ڀz^xԇڃcW֠B\. K76Zzp Ҹ:p.a@rygw+P퓞jjl+o69GHj:rU}] VeM Tg?1  61KAOw[J6:fVU&g!Vsӑ\,¯vK~i:рaȼaU]#Ko!ĈWd6|܎AlT7 0eZ6pD/rtG3OԩHΫ ,8FVOR]#/F'S}7PW#ժ&35'z5<^mv+{Ѕ2)2lkPk:b IzS uhQ%8} vbg\瓘hCkC:Mq]3kPOA΂ӫt )QpZIC3 9g ~^3k~CvǽP27b'a\ZBqU}o:Vl8Wr~BR ?WnKofK*C\-л^M~|0-&8-=N_׀tP_SwЉI G06+80B? 6"X;Qn5:9 ;ziڲ,-0wa(~}Pr XлO(b2~{,~I,:$E:}cxPtzFԽ¡_yF4!\yVC1slGa#zCuUE9nv #~Ŕy,o.wI|7aU~sJ@:'z'4Vfg7r36 YDw+۱?*V,OՕՏбz* Y@uȧcE~tG{!t6SQ5,u>~U))]E^)EWQtmB];@.<=J9į6$?:%@XWgꚉ묪A7wP{z|yNUV#G:W?ַ [ ^lqUXB껫bM˅_emG =c>eOᬪN}ߟWZMxt?im]~Iz|[ PGAm/|k].+}C^Ƙk4͡>gct_u3Mi]CO|28crh vtokb ɡIq0vi] Pfz^)1(ϸ;C&4JgK$@$0NUjk~Z( N/ k^&(IHU^mtJHXyR9~S z_̗'RfDIH ]nL-8I 31avh[X>wWKЛgP.Ժ& #& GL|ϕ. Hd%gt$@$@$@$@$@$К~ DIeR=N4Q _׻PAyƾgxH Ju gT ;$@$Ez# |5RDظAcǁxB$J_'n}ʈI'+]qw70I꣣1W( L8~졜ܬ=S`  &J-3o|M 'I#     r=Z-`zu'(5\^U}\>;<]ed/ !pzC@HHHHHHZ@rr+/P+u`_!         㒇okerFƒ*u ^>^_OԖ4+J]5ph_{I㺰/2nǮ zg@8ir$O(9]OMj%dݍ|Z_7+dOVb!Q<3_3Pv# sD^]P@D:rUwmR ~YFG[FIsg9A\z)~F9eHD}a.PL\[\z,S!+%s_FtA      V&G°Tt۱ByQw'kc,a5CɂSxD@1KY^"&IpB% )TCg(1(Zc?X[ZH$,d$a~s;۷{t?==>p"vX9uz[u׈s)DZi^O3zQ>W>D/a2Z8WG`3 {k|ED Dž1Dklcz`1zqG(P`{\<\;#ilySmùK)J'\9粁:8#6.>r8/h8w3#%Ր溅9Nw>.HhiUno..y.^ĦH;||/;p νM}'/~;NOŞi,% 1[OrIOVW^{la~l=N)n2.*&sI/t|}as91ܹйp &o]H}[daA?]#S.Rf 3 +k)^;+&ܾvVGIljސ.EmNٞh5o7h*D‹ Է{c'&7hOѥroo@$>[m3o&g #N\# i)" " " " " "0d"'J6ģQ!$و= <g>+Ñ xqr M1a}0bIzxՆ`[eOQ n u[F$/Zmmp:a:wGxvdZ~n1k;8)Z>qVҴakRh$ȓ;Qwŗb<Ӱ'\$~,rub{#z~|M#%> #P?ْ8opf!HJ9xa 0;lxVsƽvsX\iyJ! #\p lM.{Ivx%Nఴ왼bk"vd^cNKaTx>d#G 3"<0F pusYyG(/|#_8uED֐TP6.PH,sZ`j?"?UatsմsQnM Hgim#ZѾ|>FMgy3WL˞M(x7zͲa)I=>4`W# 30P½?VVQ:FD@D@D@D@D@D`Ԃ6D"8.'j FZMdsbXt3D6,F ^ Co!4=qb]8lsb?Xts X so"1r>my}yhNxGCe$TF"'ϗ'ޖ5/"{93enAbƭh+]O>\u%8/z~_/4?KS]\e۳F+NmPc+Q]MWXk=U { Qn_j>mbpsKӞ4x+.6->7ї^8," " " " " " -w,:XIֽkD=}NZ? >.W^jCKp\qOF@ZҴ$:#^\ۃE>_>0f#Ϙu/XO+UZo=p#]Ք*xm'=ic fNYtj|l4oXxwyۃ." " " " " "0 (gS!.Ef/u_m(5䟍?iAXsdw\=ӦR=Ip>i"eDY0DNI-<я9rdHmÑh;Y>%Ө'` Ȟz7~$W۪1:֟fz}0VSr2Ͳg4o4E .5Wg?ʲ)|7Gvd@W0i8.A^ ȑjlWXp tk9Gts*\Q{¥S[".#"O:tD|ѡ갠y aQa=uc̛iIߘr2@D@D@D@D@D@D ruS+هȶׯ+_" C4-k#(%S m#^g,S'&igDBFR-= 5}G c^:0ZfQzXt7g[j9.*˨B# o#k2|#'Yk_ٞ.ij4*^&CE@D@D@D@D@D@@xSanyB k yĭcq>U_Wk=!u1PdwX7c7)D؃iVçwQqb?x*,/p^)]mbZ˜MA4b~gm6!fJae4S&l2i|Ǽ%LClMbQxxlBks==och6̍Pi} ZļZ\>\qx+7ea|+({/;ŹZl)LPg` ox ׃!oԥ+W^e^0`dl2vLnMbIbɄ>%M>icdRVphBPMHOh[)= $/u;׹j2cD@D@D@D@D@D@3x墜P m= fEYm5݋+ V&AmMӑg,&4$."(G4%#X\'pCN#Ti\#|C 䳱M#|䭀nO>TʊrAy͈sʕlO-}[HOzfGFN9ĕ~ۦ;';}iiV||~2[[F[Pvo"XoL,wq0?׷ w죥E˾q=IJtw.q+X#X= &sOg#|k q )J0֙0!Bdڽ0l嗤v6s^X\!~UXٸȹkl {_Fmu_綅|Ft-;J}L/jm#>e͋1^;Yk_sL#_7M1ΊwF?{G!p xn6OZrm|J|7y "^3@IDAT " " " " " qcp]7ޔwsO0b.Ddq.hBW (2Bx]Xi_f߆LbiJcyk;^]^w$>j8gt6paaΑ'c>08oxuM&*FDP/ NSzYfƦ.*dnZ9דik}Khct6C1V״a5侃Gp_iwP2s4я ;ES_,f}~O=B\0}}>&yA=y`pw$/L=p{/MHOZ1M" " " " " " "DJId</"A`كq["q/6 rҲ'!<{y&Kg9QE4bgxіJvd,+aI_-w3m+M#έ*7cL95]4a4 s!<ƵFP>ʠ`iYk_ОGPMcut9wRH>IbX|Hc+}`6X5^&W%w1uEnqA)I093]ժ/l"҅¶]kY 0߽ Q ~bpD Ǎp?}q4 i$ϋaq 1|6!&5+κ֓c`򶯝7ǤxT~Qcqy}\gx,n 6YC -^K4ps2 3K}qebY?Ri4@ oZ YљLts'S'0~t<P9G5nQ/:oA(C/" " " " " SD@rgn^9=NSX-& ӌ@ʽe|IP4fWmYon.gBZ=ͭJ]D@D@D@D@D@*\P:u.0 dE<Ms^)>͇6Ers.ܐb^b^kZ[8~QkRM=9Ν<j3*ۻ/0?MWXҵjk_u:Q cpRg`|/^|s]01k~ch x]>}njmS=]RD@D@D@D@D`JOU=q ""@YK'ؕrb箏̙\)qP7(L} ν}s_s#B?d~A[NsnG8:΋{:׹ cѝ4Fuc% ۩#jczZ從sCNOR&dQ.C G+Խ[ߎ;rv꧸( b6lSO(}_?үCBy5ҏezhDQ6WSj}nfdD3C1xK4Rv/~M#'KWʅwL1P7^˹/q/\\hqRE#K˞F볽xv`_?;V){EByc* <:w\<) YKLtRHT8" 8ӹ;5]lMQXvK݈8yyxx_c/i \2\o{s>{onǛi;q~Iu,m\N Q$i Z5,%R'SvmdG- gY:#,m}^AΙi%sr?p£n!t7нF|6]qZq% X5,][u q}u! +ԟh@BfRh&uP ؓK:}Oq/J=|8jba{Rd(n?i+:R" " " " " "Pc~r"Cz e?$ÃktX_G!d|^O+k[Y,{ZNGafݗ^eMgsij!\m!s> c $Q1fu%"x@ߋGܼPxā๾ED+ʽZjem!{_f*AVB-< .7ZA~Vbs44w8"'~rH;L+N}bz{{/c)|tnqi(w# ½al@z7nJEQ{ҮϾzQy=O(4C7fi#" " " " " 5*bu P9];w!\@2Ƀ5ky'a2NļT7{-b4{TL/w=b엇I#8aigN ?Ɠ+8e} aYz}8g-Q2*D7tϫ>#\~䵏2ʭm>GVc&,%7b1۲{%%$4cPu=<~ VL2j5))+ 0v?]àOM`" a\>cq{jIgqy/'>49G#" " " " " e X%;X`sA>S;9{Wԗ8q!k٘<aoeC# ox|'ݣF(؉@~^a}z Z@ Z4XI :i" ޽j ûq&SXFϝ5Rp\6R[{-Ǿ h3k7 _}-Nmr_5 MϼuWCOt E@D@D@D@D@D_P.O8w%`?r$2V)d0[+߫ ?qRx :

fw(zѨwMDi[D@D@D@D@D@$TyvA^|sɲITn*l'O4_oc/"IP6IjU%PK}>C~.Y191͢8-?Cf.+v1S'm^*cA/tnC֤q0=&:xs٤dUM1W+]Z)ňLG ǔx3̗srg#&T󠛵t*缵GT@G'`Sby%*B"PK}^sCYEwAy̗̗{"s289C7xt[r)?MVvx7|e9<14(!)C3A咈WI(weP1tUޔQ3GIFWh*'M#<@%(pr$SfxVE2{+~X7sA9F7}}0׿;u>M0Թz}3[:MD@D@D@D@D`f0O2ǃJ<;gxWO97w1NlA)Bu؀0=P&`ul\pm'i'E 38`Y9!k]0'_.ʵ":#ބ+^ l3"cFWm'(dOsP牀$'2Y^P] YKT:0OgOGĢDR Q!+iG?[qy[b2PX4kyt= d_4l~P~lL/=_M6=HcD!-{#l'װRi_ W=eAk4,Jʄ.yWx-s1܅w2b4ikt Sz}4.*'˦ #{-IyD^ˇ_T0F>ÓpEeg=>q2,:8M] #_[/o1F+ɿi׶|ꄩ7w%S \Q.GPN ctm>멇6FڗM5w%Sv[~42 NQgs5]d {5uGSdPJR&?mv4ز~=_" " " " " "!vId6]{ĕ8柷⪺@ HPn"\%-" " " " " " " " " ӉPN@ HPn"\%-" " " " " " " " " ӉTʋ4&U" " " " " " " " " "0̚N~^:CΝ>j׏:mL!K{XysGz]͔+" " " " " " " "Ж~ ٖyh/tf;'GPB{CV@vZZ|P;E@D@D@D@D@D@D@D  ۦݴȹ>Цlh"Tc[.N&`{61x " " " " " " "  N?yEDŽݼ{Ν觰IMD`&8ɿ$f=iY?>4y$(OК@]$(ׅq:Pu. "# I4WZ" " " " " " " 3f|)bs^9o6rF60 Æ6W@`.'S6׆RHRI@mrxq㲖N9*Nvt{[㮵̏z qn'GJο:w}ocSlWqϞ8Ers.ܐb^{/vᒹ VaSƘ.ֱ9Ѿʴ>=$jfHDK7Q.-."" " " " " " "8DދNHwsOND_{62$t]i-7.F,+0&xi3xOutnvD0򇌑p$-9]yC1;u8Btg$-GXmvȥژDs_un]Dqz"m2țǜ[T9h ^iyy=F_W({Ӎ1b\ZJ{]LS\9N~md5eq =7Օ n3B=ңҋQH|s1uxb Ver_ܧౌHYWZ:ue@:9ř݉7nBhڏ8s^Fz&aľb_}z#|ߗFca]NLg^qBsp#l, &&\qX1_QzCѐV馱iVGA0 ΁G+~tYyt=}ao41md@ D#s\|d)S,tnm5BI(!TEp˔ ,?z^e [ ޝKGװ {ޜ;JݎWp_Rn? ] FL0N޷ +|hJ×#:MOaV-ŽQxC0qrk~VXuWeܿs#'5'_~Qg k/{iBxf]/Q(QPGk N5眩Mh_5'ַݶaG5u>qtQ3T?۹oS$ll_xُwفQ/n~(uݫE+YKȸ6 t@TS7?t: {x5vx/ײ17Gj|XD]iFajµ0<.E$ĉffY>=expo*\=p6| :F&&ˤ| ;MC&8Wb4ɪ{پ4}i>𤿖˸o|n/Sp|.P/׊@ !jb]j1"]'B5tky"0hZ:Q@pxR1'Ay}`͓hLpnBcQu{\ona.ȁMmľ1F$Fh>1 L=\9N_lNRD@D@D@D@D@D@D` X6[X`sLJHA.%"?|Tn g-x+{eU8oe;M. x~f!fAm?x3pqUxn7<}VB,_W:.(yq%XΝv옲 pnJGNWw9wYeǙ[| o(d@*SL/p^ex7]"k锳vMf ˈ?2?$('_{t+/r๼bfe\h_`zU|#9i-i3C*Ps u>6*z9}33֌_}s_7 g)ZD@D@D@D@D@D@D`j'(ܣyy)DD]R~NSR Ev3V,߈~٢ЬDֳF~:Y3%ra775s_Ӎ^ш'b'3Ƌ:Df5_,܊UK{ ;h{pox'ip=}%g*BD@D@D@D@D@D@D {jP'gjs#d0?ֶo ~!3tچq#vNdIvxÖ_nJ0* fnLWj 6/@S/^M__i^H\b32[r Yal7H@Gī4Ϲ+hao $qKZ:6 a}w3*2cQf#2\vn z-]]avHVDḰoU9ĭ rL! _Wʵ^le&.d1_qvNe\޾}^=iCvcc @Žه87/^K}W#(_ι_yju-D@D@D@D@D@D@D@@9)%bģkAΛq{#9fCH YK'1׊9d^IWBEFrLY17i'p5ts9n73_ OkۋkI,|^6`V:rzuO b$OX1y/_;F&91;/ߒ:X.)^#Ӧ@$P!?ELYK4'fNI.91qVN,俖`gBDbrny>>j 8)d._INU|޾?ÍBx{K8Ҽx=Eqy)28(s›YAD@D@D@D@D@D@D s cxl>s߅/ʄ9Sb4@2S!:FN^O/#NFΞ^l?ϹX$X8,[04|s$;y.&?TG4Mw@rt>nV-+W/?V:K|"M*d_>+?A8[I6kD̛vCWLB7/qq_~r.{u d=:o Wec7Og: ")+6T}[/qm* 3eg24ۯ+)#OynjqSa6ܟXbɱ'PW݊P'L/32FܑxO3qhue t~;ŠD L9|+1kr*fH%jQ4t0\RLw級@!_l _$r`.7DI)Eyz/&<_ ~k4/~{ Ef sxs.J{X톱TI7_:6.:_E.20%1僵%rބW;9f`{c6 wͺY 5=vF7Gy}鼞Mo˅f/6P1=ԑ w9Էrn}E|mzޝ{ztksPQ6ZU73xh=JWD@D@D@D@D@D@D&3Ae).rwX> u'x0m%O}x[ YLq"U;*Dl$OCv BIν^K115LUr*u"j䉤|3lؾRfCD@D@D@D@D@D@D ">g&LX LAڃ)]wN)%@27k$d1_y:fV" " " " " " " mKwG6j^׌tW1r" " " " " " " " şy6.p"4"{4tW9%MXwrTNlsLR" " " " " " " "U"%#^;E7vYOÚ)WH3 6miD@D@D@D@D@D@D@D c$sDuz?'"h ?+yu/R\>{DGbs8g-_%∴8Oq6tyh5 ʭ&뉀@ДmZp2[D@D@D@D@D@D@D@D@D@ZM@rz" " " " " " " " " "Ц$(ilh5Y'" " #~s׽¬EdD$>s+" " " " " )H%#" SNa$ДG[֔61scZ ? 篻HWisKy=/GkOCg\ q^ǠuWoV&\D@D@D@D@O׸į]AD@DvڎGdB/Ys{{R 1%]v„uF-ss=+w978E"FO WD"t " " " " "P/͡\/9'mCC=1 qm^plehfp2ifF:MZW/ !# h%})֟ל ?j~YV" " " " " "')/TD`UeL§7Ὢ0 dw?:wjsupO`@,櫸–VD @U`O0yos" " " " " "P7,^Õ{޵~e*,SBcNާ87'?TU6>u&>Ĵ7Uuf>~mĪc3۾7pHKW*UG)Mrle_J^Fa6m/" " " " "4EއOH{^FI}/rCS\j]mI;?\Oޙ}]-ZEKk(WBWǺٕhYy/n,){Fk݇H%eWG[iis0qμoc[+O1K#pGCaVI?tg&?{]laB:#\:ZDwkQ||1VyHyi \C {O,Xi2Y}WIYsZO" #O}vɳ&" " " " " *a쾄HNoDGtDIx nh#9XLvVbN]B'bMa+fx2>7d-bSLM*%^:MlBM[}פl4=>/doD'BgV8"WccTLO8N,f8AX1ٟLfs~Ĺ$&d 8ݔ`?&hyEf[ 2d~V}NaVzzϫ#x̜|lH@ S#s__ qd)f/tnm5BI(~7mIAx0 WCuƧt⭬?v~ >aʇ.Ěm.}}"G87ϰ6\ [[̩q^0I|jkO6i"@}$Ӂ v|ldU^rX`Ї ޗ|",Bh+8yOUznKߎJ܄v1o5{`#ן„ufj|XdEщX㵜tj|u|^9XܾchO>?k{)9Y\܏W gk79Yxך 'Flvk5ͼAp1DΡf@IDATTYrr-˩1Sdn a'By|.^ť_6'hϹ@'.ߞl?|'\t9~}f!l{Yf~v}jjQSCD@D@D@D@D@DVVx}x^0q"l#ZsDI|hZ:u(&)#\ _v3Vp5ݎuEsq^Q^(v g&_QخOB N8żUq;ݴۅ31U/Xbs9·w# 'WN.-`-ZV^ WHhH&i&S$JVD@D@D@D@D@#^TwrQoɲDS%'cLiu2mHĆB|9SC9h<5IX> [ZN x aMLs\??vq\7y!,5f ?m3&V~d?a҉fg,Fv0.+P -E@D@D@D@D@D`'({G3S;܍7r0QF(Z:\ljI m-}71s}cfV/o j V3Dyyȹ1\ҡMv'-5kZ=;66 "bϒϭqOZ.{Nh2 ~U…8b$VUj:HD@D@D@D@D@J4((3~ܹ3 t؇t?& ~ZmYKS<|p sD#d16/Q~}dFnXk+m̚j$O?T%|kr/7]w5t ҡBlੜvH LG"'taɶXM|pvl2-\"g?WWH{.%" " " " " e isWc=! @H(㖜tl^Gh' i9׽6+(LAda->V8 +#ÒS>pjAl@k5BႷP0sZ2X.f\b3} 3Gf8ccsB?cI'26m;0=K̠X6WL/c,*֙0l߂6dE@D@D@D@D@-eREd "",-νUYZ:|ywMId>Ʊ8wEeفhKS;ӝ,?p.S]nyħGc>7Zbquʗ7x_C- |To5'0' SOܤ4@"\ei?i Ĥӏ`l{cN5ٟI>q܈൷i_M9kXZBeVc<]E \SzDHz,؃)x崛 sx~$w9\b< 0eba } ac2%~NqjBnp" _ƞG&$ЌxA,h$kϫ~"ZC /P?OeTV2H(Ci ք<8T0Eko0&zBҩ' FTG݆ wAtK"T+*&L="-Ɖ+2aiy#ɖxs$N ?^høbw$?8v6,i:U@pQv{d.H'DŽ7TYlמ:y<,cU 8ajgӾ ,?pMw+=:яY%_}6]jkVy ?Mcer/>g`^Mm=|נ@v D2z!<9쮬Sw>anQ?;s>z-ׅ$+ S\IyOckb:vv¶*= 'd _"V&V`> [6d _QM羋@h˫+n3}AimDy/)L]Z{nQуGbAнQN:W ܁0;T#!a?SB/TAc)S ;i}>&i=y6,kWg>S)MSbZoc-Ih o 3& [D}cF̼7xZ!v[IgK~̵,+N7\B #vs1M3Q[={ᘋ 6y'N]]Ee`l{׬;HglV;78A.}5vo.r`ĹK^cLI cW;9f,?ȵ>c3oN(ʯ?YE(-8ryˤ١Tm>yfiڗ~~ x?NۗQ'}~3gŶ߇\|Txx?)tExXze2_e!隯NH?؜x"]ojCULsQ vQ^xݮsaD~o#$t]gddQF sd\[Gx%e6!SE@b JSD@D@D@D@D@ ӮH!hӜdt# Ay#" " " " " " " " " M" AI`L7[&Xȹ?qE4bJZD@D=|عӝOǏ9b{-+E@D@D@D@" A.lNmsÓ8.~^h)>|h<׏:^>btnbwwX&=GjMKy9=/GKCju>Jێf!VQ۴C͸B{qR.CU3)" " " ӉtestO .ǝ;_|S_.@ c"8s^0;b5oDMȄ.;aºp]{V޹ԹϕJ?Gj2U^iѤ24)J>~0Sw:8~T:NnS8w眼 ^*6ͫN@ͨSnv>_Hob) 8fQP |zL؈Y}V Dd f]!ZVN ì2}L!wBoNHP4l; S!]GHx{Ħ> SLʉK`s<+L@̈^;Ɍe2D&05;`뾰9PK뮐 9^9ͳ1GpxO3#'cz4{~eCoF jSD@D@D@D HPn" "l# N`6{&dONz<.Os O>96߃xcްY?~$(y|(!P R0k`QDYW1gys>9/"~^9xv WQ/#3J4G{4]p%Y]xG5\vկ^y9Ν>Ĵ7Fz)+\ȷitVyʿp^wIz=LPV)pj5/nI <ع!Cل֟B=BiSHxe-o=7̃ 39?7zعGec{\I?1VUw/ynBNn(0'~'b=H@Zl86BZ#\:="XZR/;_!WUJKxI5?'|)Bt6_MJ3ʫ4']LS\q}E%ip^=3@Sr{FG.i6ޯ%_^|kyo[ t[Ɯ;7z24q} so?X11ߣ̅mύ!nr=_;rN M>U!iC ԒN1 ilZ:ƴf>XL֟0W}rnά.ldoňO32By~2.'&{& (|0$]fN:&Q+x jF=R'NK?u<-ϭs;9&ILts'ojA{h6b-g0Zկ66SXU?䁕(-=R<ؑxbOQ3{Q"H'&{5}r?hgc?h=!85 p'oĉ./‰_6/1IFXR8Eǧ\^O6;QVjxh:!#<<йCҩhpx+:oGMZ9q{ 5"6B4;cMXe<5!C=x 8<n]=,a`=Ez%=zF;Qn 91J`u^_칑ҨQOg +o{57vvd&SJ  X!cF#{1G#iNeF=l$Huic(/ks+_bj HGgu8ҿWkK#~F^l`r~|\=x秧Ɠ'?hLl;Q܂G3q׍&S'$W|z~{h(2Ӣ 7jW 50/( YE@D@D@D@C *$[6īr{?a֗v7u<| AWZ:V6'־*_s,?R{_|4/0xfEԉ[P=j1!?1vˈK!sCSTq8qnBIg5fJL<מj?;EJo6M>catepg3+&{1V]km@L+C#Txclt=J#zhSuiúXDNJK"6feNPMiq6@y\Ñ7dde=KL$olx g!%{^~.ިpA&z?-jUG{ zn]!Z+" " " " mK75aesx | gx@aߝ qZZ:5bx0BL^/ƊdQZCyLQmo+.ØfvP_Ds9chӗ#ĴJ̙tjm雜R $y nF~Zo#0y3qEޣw޾}o'|2{}BbHztĄ@( =??*c $\@^蕠k揪kZy%\}V"a3ӯXb7XZz:S'g1B_e>܊Eh?*3Bs!.2Dv"s)XJO4NWKҨ5UM:^D@D@D@D$< י`˿dYDėrZ:lyRY:.[0wt#:im_41s{++;@^׮ M R|2/v63&V~d"|¤9+ΐY4S~P (2՜9_߶+ҭ}{*6ŞUwa9M7"_(6--" " " "Pj*?a%w6KpS0,ŕkη7fX}bA\E'rL8ڟwnSCf0%|7tv̥Ís&5zj^ї µ$1ЀA(?wnyn{F?N>w>y @~ҕG~-6oSo$u 67 @Ȋ@])Mߐ^_ߪ͕cm;$oz˱cŦ۳>KNX*AY8xHIbk'#vCc+o8ɹ=CÃ{5'[Δ\FkZ~gmT}ks)tN'PX(?,f_>OyZy'Vj>\ H]t>V$Q;/#Q8uXF ?OHJ{_hyٞq@  ^wٰ%*!dTїPhT} oYY#qyD% DU1Ҳ?) onT[7I^$ޤ % GͺF)Y,SsVK(TTxY{Nm\z? 8H= / '-"|j;MZfr踰`Lj*fjSpGm\cU8IJDnxnrM]=9RAZl&ᙗ@ 'Pox UÇ v5W#E '"YzX&^(.N TiĵI1"T%,rT Jw½.PЍ隮xKD/UU6o7KV̋h1<%_VnUy1YШ>3oPm%rg폽ODq\fUifYw/7rzFI[}mژz>#Ҋӄwm5~s 6Idڋ֨-XGV fͤ.C.>p[Ǫ 7Xw=q*Y)JHe)-~@T SǫrίFk\9v @ԏ-^D/BX#GXp/jxxXZyߜ!l WzB#Dz>M %>7VdJZ!lg^;SI.a}qch b2 }n[>{cIw[@%@,W;E9MxyYbL H*yЮz H_EMWŝ\c]$VzڨV7Vwu@Н8}ELؑr]/ ߰9ҥf|5jKw6h;[O۩>Hzny`׵ St*V́oV ~5!}zFTzۣ(so\^'6{ͽ^A L;!ŤNF5p_hᴚTkP,ǹ :0+rʮU5]] "k V_q#(z=Hh[X "Iv ..v -Zփ5~v~ cʉr?U~٭e6^pбBY5ۮD'Pv^ gۛ|pRuy<*(+OKS6~=ݫ8҄?YPۧE~u/o! e4&;K+:2Ol$I|9*D1_yxM>&G@ z ]ǹzdF^r. 4ڠ:qCQY BЦJ.[Ol^,Ųv*; ENdžUμQ)*}c*kIKk S_Jd2-{{f\B6[m$o۸U_Иs~YK}u݄Wufܰ=W".x-pn7KtIӘD/M v5mls"{S$W"`TpmQ~k7xe.YWqnp@b_ҿj۔Ӡ©T{;C{Ui[Br3։kz-Y,%%kkjUieyiWbŅO7'L#Ģoc}ޠvzoj~yؤοDwv@YNN-Q|&cQ߿'5ɐe_=e9Lp @@ (?K}|*go7ļ~k꧚ii,Ka\ (*uwbY Gv>Lw8Ck;kݫ @83&T7ob"c# @ HϬ8kTqz]k>cO@JXkd| br3P_/ Fƴ>&hcYS[~""f%l턻 +8(:~KdJ @z!Y'k|hNi@>P|rs6D;78uO~iR0w~H߰z9-Jo>+hW]XUyݷ_x玪]|Mm]C}cJC&Gھw) @f_P9Rܩ$nZ,!fΔcqL#l]'A;aLp>Ϲ7zۇ"9rXGKGwz\۹ɽ]Dgy2]%4 !MhUФn?@0g]Z ~ MV6U  @ 0L5/l7J(%=d{+.(ʈ(OѮv8f ]O3ʖ2۲5}pܷR_U῿Ъ;&o>> ޠ ʥԳlV)q= @@ (!TKjd% 6 |c(ACmqmhWcpZ$E-kwxaCP# @ 9eoe0Ah$§ڡߞBd}˧M҄,nHQYXWQozt;5z}GT~GL6c!- 8/*U4J22׹ʃ׵ M~IKVp)nPsoU~X*:]Ԯ= JmDeemA=˾ڲ6it*?Z2в"Jϳmf^.Ms' 3ջ9j#dt @ gTׯk|M"@đ/N$)Z8 ޔ-K֎Kz!poRߝw"nV3bķdx?e/u6/ |svɳJD˴Lti?+.Kk2~!1_UF |s}~p$es&?=)VE`;,NWc*a8Njwi}bĩ8a\W $q<נ?ࢲV.ZbwByTOY?GjnZ}jQC[VnWuo%}T]WФlcu-Ȕ$#d {N; vO==&OV ǣ_qٺKBQ]բcuJ^߳\uZe^3ԣ6FyiqMC$νÜ|r >/e?u8+'Iա߬(,%`΍&܄'.չuu.=UЖJ|YP5q咵ZO6X^-a~s8aޡٌCcەʻ]mSRWJv9Rߤoi#Z}[~bWNEU;pY=t$4 ~?hķwiɧF]-j{y;&gG95v |_I />MoMmyާ8ܮ0O6MFQ}<7 гQr^P?4yĹڞceo??w2/S.C -_!@@y #z%I%ٶRMFu-Z8ѱLtEYmOk:W= >OTRx $IVkBiP]?ѫMs%jYrhsïNbk[~]Nup.zߧWyT\.T~ķO5::d}y &b!cMuM>5|[bEprZ2k%*߼/8;{lݘˬOn, Vm¹\LpW(b'lU- wpv TMQvaXΝGoX1ڗ~uӟsז{!Viϋe_=7eQ[m"zPDcgTpe5~d9Hfӯ# @Lw#-ϻA Ħ| 8J `ڋy5i7+^8na>+&x_cΜzEzY!tHi+&T_tm?J"f I8]$JoԶK ,:΁+-Q.^#ޥvʊ>νqu:5ڿKMy?ɴ*1S/8P]~~O$(g_Ak~3(gc& jU@'բYcYWl)-SILۗ[ɠ\ϋr CsZND#-ӫUfd[f\`n^qˍ0>܅6}+&7oJ| '䵵 +*"TINcliF:rM\wϬ<!3@s'3 @@vN~x tTk .u S%ފe4^M?q!DU %HRpD򺚊?{%hP_5 *078_7j%ͺiZkTVWV Ĭad; "S҉"@{8Ţkdeͺ IŎBAUHE}Dkys8 @@])q>)Z?YU'+GvHIޢcȢcf59 ]T3'?/.NpckMt7V2 QjNRys$&lϷoo\nLKGs)tN'PX(?,f_>Oy`@y'V?}7h0o5 N[O(h(߫#^;,2Ce/ @]G]-QZ%J8^#<--)O~KcUL["pCVMq/MyTZ7IҬ}EM 9f=ͺF):jq} zZ.ϓ8H= 'e_>NeU Bp[|xYPm4ϋ7/8,Ш v<}_8aЎ4JzU]22A L+V*>+ duB1V6䢅cVm N I"~O'e@ڍe_T74`و#NWD:vhd\V?s㱑=,D{ix{yZXI)f __?F{mu˒ju1<V$V[V<5he %ps4u5ZVK+ (,_X2hU9 bҧ.a`:ʭ=NlZ [%#6~y$CNӢ>/LU\\NpyC ܠcԎy?wJj+Nˍ gg8 @LP)%A\-&?#K<Ω]R..s4gsMxVhkKh_U]MkhY."/4Rҵ_d#EJÐDA3ݲTb)~X~GNZgim=U/Ov~*;UkksPzCT2kv[A1PkSe˜ Je3{&>FE|^-$.r̳r~Իu3VuMk> @m#`o_;W~(Q勯hQ!-8r'5`ک`ze}r=5/!m~,D![dU5G?Vㅚ\y8d@4+&kstJ5۱yݙLv) +$z 6vU/HT\=$Q>KUO,_I6&*7Oˁ/#Mxyu &9eaPwpic9;$./ Q}=[3?%qEI⛧C}ZmgRmO=='\晑g||ؙޢ`}1cGmoE= d9@#NVv)MWdb0sr6fWA L;StJ1EK"-HfxPzPNSv=oR?oW$X)z@Z ;K5k@+Q}5aU_ʏ9&ON(_i#Y> o ՙvK{TEi%.T+ z[v]⏤}1~H<5 r9mKpjDLj{ ޯ?{]Zだ|DEc>̞|l!@@7HuSe]qe~u2 @ʚݯo`qYz.l)Ro7Ho!eXV(]Qw1=j1>urz/0UR"s=g-&SL86JUy|eEMtMe"ϖ9a1[C'$\[7qú\ջ]6&xn7kRbh&?=D͛YF9:Pv|WmRT#c/?0|wYE6^l&g&F[m]j3#uy8G%#z>o[ N v3ϖL;Ysi:ٹ4bp$ @8ocz"px<̽Chm[q.)/O(YkS6c}_l|?^M/$6<|NM*QtsghRx-!b Fkz-Y,%akjUN]B>KaW&WE?Z⾃΍$9Z_}Z4}M<4!PH"@ @@PΏ-!C @ @Vd^ @ @ 4&ܘ> @ @ @@) @ @ r"Lx @ @ e @ @ $" @ @ @A2@ @ @ ('„'@ @ @@P @ @ @@"ʉ0  @ @) @ @ r"Lx @ @ e @ @ $" @ @ @A2@ @ @ ('„'@ @ @@P @ @ @@"ʉ0  @ @) @ @ r"Lx @ @ e @ @ $" @ @ @A2@ @ @ ('„'@ @ @@P @ @ @@"ʉ0  @ @) @ @ r"Lx @ @ e @ @ $" @ @ @A2@ @ @ ('„'@ @ @@P @ @ @@"ʉ0  @ @) @ @ r"Lx @ @ e @ @ $" @ @ @A2@ @ @ ('„'@ @ @@ @;[ysG5ϝz=fk6JX~oy|m7uUG%dWKuE7pnBCÛJm/XoҸY esg\u,iXVvo( ~›g,_EK)MPHȇ6OUZɺ<_-&%VWVn#V(>AgRᏵRkojϾ͊G"T~ܺEx7 =k+;5U*bdn+U 'Hu~evBͶݸ$^lʷ ĆMz+OtGezuIFL(~{EZy>i|qԾ_ո8JϯM'g|Uv'-_ @m=n>Nkkox^}0} eLL"f7\X_YyT}K˅f\=g+?pq7<߭_?zU;0g᧓lxv?KmŦk(uݩr\rƻǹ7v\v{¹m݀Y4@Sw: /(6b;3eu^B9g'~o"t`%.'T_4Yr=kϜWXRz Gk7_:RLWVmçR_=:䦏ۀI" =)JL7}Q^bv^X/if9_:JΨJ%ҷ \=Mr~B%6rKTg+_i[q7g:i\pYq%[QI }HV;ޘQ#v}R5 qHlRLD0^C N(޻tCoԆE0:?:CC,VS1 >jWk+=fD%! ]鐍O_qx<Ͳ<>Fk&.b'Eۤ"3,?gO(+OQBIݲJ]S%$Y뷭{Ug"Uθ^8l֔׆jV,WMfNꯕ*]YSJsmSn72ٺ+nYMNBJ=?sn8]3]W$'u%wTmSQi{~%~.q9}qqS"bn׺^͆_bkC[fP/JW(T7*m~&W:|͙ϯqkWTai. @`0#L ieݘ#]Zcuf .?h7JS.Я{~Zracoh9:z{eɶ@Sgms++@hIHO'_nsM _|cFޏpRTB2q]#_߱u\P6^~{-mөCK-Jo7uoe]߫Co~nPq3F%zu4Z-? G\ZmM͑4MrMn+MI܄V+teN\U1hڍs0U:26gv(]m :/$roIYv43„#.kק JR?uድwʾUDjv i64o^qHmhk 9+Kv<}Al8Soe,?eDue6,RSr:@rO2tW}t𥁀dQR%&{/- *GA?<]+&S#/-'+>KN I_.pռŕ+G5G ˿[& ׁ-6u1^c"s侮C&qƨkc#b1[&^ )fdžcמβ 4[n _gDѪhhXLХnNUg381i:f5 ܓ!Fqof(ƹ!۩}Z=8ZwD ,GFW|Zh`hE:\uWWS^>U2ݾkmNn&1{nܿՋ Y{ rچ9C5U:L_u,I/mei7WS_/_sI~=1DN?^̈́|o /2i '>ԉ~y*URi?2F(Z8SaKϓ+ꔋzwGZ-:fdwP{CAdK[/NWMܚ8ЎQ՛Q}x䗍0^Ĺk@2xJ~cU*L|1f`~nczc8ȽNߗX/@=84H^$x~+qUYo ey*B~e]"$2m=7$q6cAe:n߉EViI]~L$؍(viE*uɲ~չMGNί6M}5{iN%zӨG8YUXL9m{&}Δn~ź~_/m] AV@ .Z8Ad3V}yrbUڝF , ~@x^ezrcIK _R:]5ߎ}IL=k~W{_I?@ C AK%/gfO }XzfSM hVF Jly5[W-`aGm7{sys4x4}뎄yؓΫ<%*[_Y{WNU15ί˫Wn֡EVɤNy|eDd]ݴ2ɯ67%Q47)sMM(DȶWӄ% 5^0bE4nb&+E 'h9_L/-7.K`>ϹGjn %}wz>Yp&VEQՎ4=> ڗVr@/z̪,4x+Ef;vC2EO7W'seq^Be{̤O /\Pc֣vvP/`Glmv{*PfVVؽXj7pUocpop2Bim'Lo|xwXYWNjN)s|qTG$,j|(/9Gb~U΁̸/?Ng5Z{3lT/v<OH9o}ҝ< 4 P۠%Y/Zg;xi[&vOK<*M+dhzkR5@>bv6#k9 r+EK|5]==fNmbP/f9קlj7Sꗯ5nm:>_lXYk_"W׫ r75䬦|鯾]^gͿ6Ps}Z5:'>4l`&ʣn/ՇTup#(_& ,s㻵GV fQ6$~VVI3%Oi fY/>g?Aw1ϯ?0LRϰ~u]M͠Y+pdžL_i M40F8K|W!x//2+ql)/.wO_05[>^Ohᄨm\ -݌2,qH)~Ϝk&̖fT/JW9e?*fPSU1a@ 0 hт >ѯ-85zBV"5Yk=CS5s#XA_] 3C3%,]T_?[`+̞qò -Ĺwi;(NwWkSp,Gr>sF؇/lz<&>W}m=\/R8A3;=/R7j[TwzMjtA}Ma(R<5R=TnYfgʺ]6C*]Ysڍ-vcH6.pN[<^&/p\ pwcI0S{|Sj-pwH}ȣ*SjL{=72~i׼K\9 ic;~|GnXn8@ Ѐ7qڅJ}gWSs䴩sj>$!Q?(P.zoOo@>ʭٹѫDl~lQxgK\VNq ii ƁqRmO(> ޕWOmd1u̩,*UiZx@ ePP~WqIl+LؑD}T7s o"E~ʰ:~]4ؔ)J8/P7>JSS`W^wc\Y} j- 'T~Ue{ #}z6lMOE魬EgoU~ڲ]gnz!Mbnv5S߬ҕU8)P_+¼gj,[s[;#N8~i2 WR׳f~Ni'hegdߪy;ұ P{2Eӕs0+ܟkd׫v=PP/;jB C t3nxx:HTQ fWtJ!H qi PZ [ ;ZhSmc(>2>?>EyŻOQ.2]Q38WD@ASoXTQ'M}JU9,ST xl:RYAZSږ@bʟȲ~.eۦo۸syt0|Kg^A@3Qs)&֯z^Z&7 [gpkOf+tj(Ի旍CVj5nx ЇTUqFngku^7kgt4.pRauykqS6WpL"ۃm߀9j+cz}dɥ7{/;TU3TieXڬJCpRiIRxxh] UgTpɸ56eIB 0cx3ӻ`8[89ԉyKⷤmM[qy bÇz_#\ 믑׵זtlQgiz]~m _w{9Ƚ4@.Md*ϴ Mw2ϴ_ 9ٛCFQ7%sn:ۍzߐ@Hn9{h 4۴*opRޞZ$w,䍘-)&˧kbYSu5N˟{ũ}1 @*+(؀ @3@lU8ĆPزȹ0@ke>V7]ӕe~ϝ\f/F%qY HL1s qɝZ)4 @@P)B "*@uw!7Ym1%R/A =^;&eᲗp   @Cxv{n] O`L>3.d]/:\ >T^Wz;1򎕅t*@KkA Nos:n6uph7~>-wQr |_V#펄_Vh2I,w9;EE? kE2AGAxyB @ @ K^2[ @ @ @P.^#@ @ @$\l!R @ @GAxyB @ @ r!HA @ @( 1 @ @ PHʅ"@ @ @x' @ @@! (2[ @ @ @P.^#@ @ @$\l!R @ @GAxyB @ @ r!HA @ @( 1 @ @ PHʅ"@ @ @x' @ @@! (2[ @ @ @P.^#@ @ @$\l!R @ @GAxyB @ @ r!HA @ @( 1 @ @ PHʅ"@ @ @x' @ @@! (2[ @ @ @P.^#@ @ @$\l!R @ @GAxyB @ @ r!HA @ @( 1 @ @ PHʅ"@‰R@IDAT @ @x' @ @@! (2[ @ @ @P.^#@ @ @$\l!R @ @GAxyB @ @ r!HA @ @( 1 @ @ PHʅ"@ @ @xf/J$isO<Ϝ'8"8rn4؛>oα|( @ 0 (ϼ<'yrs1:;78}.Q WKž̑DWYp&vTB^dG51j/3z>3:I< @ Ji'R^@[ ?_W0b{wJ ws\ܪ&8ݳ7LN }t?c__oek]udέm;ȹcj%ZL %9 @ Јr#B͞:۹f7wնAFS;KzB׺<V|Hr\EqOgG@V^7"EΗP ڞZv7IP>qfP}s S-r ;vW" @ *>*n~@K&صLK%| ND%;GnŨc3Y-> 02Paq7.i9HD~bs_[X+&{zG3z dT I7KI[n%7Odф @ "seiSz xZSw^Q8K*l-&+C–ːvYnZwƖĻUܓo[fZSres7-r%Vzű~ _ƣ7=or9 @ tnJsq*+խ8۹9c~ YbSe/w:9@Z'O;:z X"œJ9 >Bc.p&VS )lA @t'М҆2NluYT3_d>&]Yn s߃7uHB;XIPxxp>#QҧT~45|5bڃ&ʲ<\Dy!3/M~`s| ʦnKws7d|s~6R[:A-L3@ @@,{~D,&I{R~g=qnꭢS>X(:?ٶUO s}/etmRA2|>n1nКqwkQĮKTSnY%HLiH|@i̽ԟTzWYLaF:= ^r=k8i ps"XMhIkDP]_$q̋qnBf1#H"N(sJUa؈|&3 *7Kk@Oy<$a mNݦJԥIG/\%!OHX]z2JSʫ<89ݹuB07Ud}N품+\6Gw8oZ,{uXp#8#/k nq閜" @ Ppu뒋$}Iع$4rE 'U"rH\z$|IR\" a7/E0b}ƖNj.zS/PQTdI ὖDte%&{?=xUt+n sD;ſ&ׯ|&"-wI oerZ}1ه7_7 dLRԈޟkUOn{z/.m~Ŗ[45q br4}z-sL]O:'19 ~y*&| =D @@&Db]HDNBO,h4pg=ʍI1!+|:I,daϙ1m?kj,]/r>%!FCT 5| Q)/[WtUG^o9&tO#JHs(rqJsv"ߏz!Y"?Th% [vWy*%&\6eXԈѫHV=8]mIeorCUVէ_0jw^4efaef oB @ @AGdas.SBlkpS%H~USICA7|yt~R-=, 2; a&zQnOo^G,(OVWY:Bd?UEeOٵ`u1YU^nʆE:^~6e6TFw;Rurr>YT_N"߰5Ռ&CU|e&>pJ߅!>ʯ[Un~(g lߚtN eym 'mS9R*]eY{nxT~rCxs*'R%(Qutf Q @ n#Є6SVg0'r4B/Zjy VE u ZТPvϓ IT„?k!.xc*ٲdOֲ_:?&VLg6 ZMJڮa1هYb[~D+bdZy=&1/p4܂x] M/Kr.?ǢraeP@wn5_>˳ $$]L)`/oMp=^ n]&1u~\6?XqjOL.|C @ N 06! qϹ{_󲠬rf$H.2;lehT"V f~G'W, W_KsCNUq2!arwQ:|cGWvY%s^N)eM.-⾧zDOʯ4q״9Mclݽu]8 d_lRߺ\VdG99yMJ!@ @ sJTYVcZ~F|GyWgN%8,Бy{Ky9X5 ;Kk Kz#Y*nkb$FQYtߎ ѥ}\\*+ӵTfQYCw K"%hb\5F_WoۑNo+<PX{0m~Ն͑f9gs׈P$Q'T'Y)Onu=ehmg!̘& @ 9la>^~+qA;NN šJsUk%N5gEU-L]ɭL]S9ZEkz,r%Fˆrlke{ @3H0!r4_Z2$́󴾮ζO(CI :ݦ9gz6iNlS~6붎cX;|f 3g@ @) X(eN媬lOeuBqKUUv}Rq~}PJ}.U="k}P^`yf+8شydA~&7wX$gp >q恇:1˰ԹG;O @ L- >dIJ bC4vH ٢5cĢ3Mr7dHh]tLx9Z_~G*tVNjESYn{Ź*R)^AƁ{AnRXZ\ ZyzẪf`,>y%>=m% =kW?:s5s.G˔uxyd,ʒEa'W@ @ӖQHZ/YV_`VӨ,!dVpɘnʲT. :-U#HLW2KxA1K؜ﭲLR[V0yX9V$Y7ibǦXIJNyүbmk(}mc_0cZbɻ؋'OG\Ĺ} b3_k|68 @ t@Ą%BF$.Z8u:N ["7{ZRv,^GR\$L]uʟ`*tPٵbj[N ?n % .(Z":G51 .rSTy!x]W<#4֜@\8'uz  q*&n!kʓ(=. աk٥3tSct,Ů_pncDN( Of# @ @@@V K>i &ڢNNŌ"5*Bl܄ Cl7FM6pV͏Nؠ9}pTU?&{$֔]/1`O&V~}պ}IKg 8WbeH'a4c-5daͭʾ&9VxO2+$(]rʤUzwvdnvRI2'w視Fh|a0 @ FI"%ZuDw;w~¬h$M%_O*_궳܋OA JDNZ~=߲KA-[kO;+ku͇gIqnE! N랟9#ؙݢ]R- _91ky1yke+)oP걿okdaNuVh>^|]/yu8u޻r*u" ʿkQ~d뵞rؽfɕqñ> |>@ @]B1_/?% ~(8= hᄢ%i%QfgHwLD-%Hw}Lؑ/ P~LgN@iru~wKl0)e:'%*^G#Z֘u| jNw"Em><Ebv—[r1;\f=KiUwQc8p0nc5VXU!EFai]Z"Ej5Bj7V[G}nOwr6+h.(/DZ+4 fcm;s#fs99y{?pħmkij._!-g\l24nLM ޛݰnngفrܚCs[_kifEޛe[9Fd1kZ8?a\-/e0bsy rn3z/yYk"_^;˿ΙD~D|PzTbS=4ƃgc}\{ e$@ @jԦ]ې36gRӜ=ۻ.|%08?+3gCk-ѫ%Ad(٧ay>/mwG<3Iog6ɸ'yY7 o _IF"|4־n2m1 \ Փ=%F]?GuK kٮƇz]ܲNta*$@ @(wUh<fpܞ>t&nM=@ +߿Z[Qya Sh{{m18Rf [|i]k-F/N6_q:}ti#@ @]XڥzUKZ9xeb}<+dYO-gXu C!gv.D4#}pL2l~w MI;qAt~&G>|_zw|A  @ @`PEw3w6qx2\Ija鿘3thF\F`hf(_-\`ӝxC9;A @%3Gi@\3v8C'a\2~/S\U$Sζ @ @) PaFL_rU?2tz!T ޯ@s[6Wž" @ 0';>luUI˷dǟ>1ч6 A`珍 @ @`C7:D @ @N;7P+ @ @ @y8Q/ @ @ ur׉݀ @ @! Pq  @ @t]@ub7 @ @ @p,nыk">,t{LģSm\ @ @h \s͝~\1nc@ E  @ @ @-XbNJ09]{-' @ @ @@K`4f(+ @ @ @}fvۋ09A @ @ @(oH}6Cž" @ @+0܁KZEm* @ @ @@(o0ɩb_ @ @#0[q֖d|־ @ @K`xg(ZU@!L.@  @ @ 0o!|S#_Tqs?8?3gl_\34 E @ @ g۝wڼ[;N)Q`SL.rM?x@Ľ?wˣywo_Zulnn-g/7rӚz%@ @ rWXUM^֪|]> g໶yzHW"^ȥ)Id~Y>ҷNc˹?Yi^ @ @@P*;+#\`z{@6]wVr93Og@9|e35vq3uy&g3.C @ @n w_(ù@-uӏ ksOʛro26h'2P?ݝzʹG^Z.11%@ @ -NV{[%햙A38*5 +nUعz^e.&s @ @: `r_ET}mNکԲUmwKDʰuiδ=[My͡6 n.t6SUuA]{3/ߺff}{ @ @z$ Pn%ʖMo~4݅-,?ݜQ,v>=Ac|#q=Ss  @ @O`_/ v+~9{ͿM{ @ @*ʕ459XX.ܞYZr f5pC6c.qƺ=:uu~5}% @ @=(yQtCqy .wQta>1W"PWXμdަ;#\zp 1U[W( @ @芀@+tkY/PŪDE{En_ n㶍u9;3x"S[ @ @tK@-NԻ .mq|f>wk|wğ?Xvֺu'gk{7[LF @ @ />%Kh*L`\8gUpw.uQ=eozBD|?`hn>߻ @ @@]]dsلÈ'j*?DѬKb< |5@(8q2ޜ\}*./?/W} @ @h,[Rt"ί!?س\Gd.&s{6^  xo?7׷s 3k?rH @ py\V;7Mʼ0bq\x @ @(Srr5r =o @ @ @%$5x4B @ @ 0SCfz#@ @ @ r  @ @ @`@y= @ @ȧŹcy{|yK"|1q?mpW @ @D~%M @ @Aum @ @ @`4 @ @u(a @ @ ^mZ*v>}&b|0_Z5 @ @U=oe1_ӃvG]KFou38 Vѩ @ @ PΞʟeܫ#6vmķ3P߿9byO{  @ @5.(3m.7~MTI @ @ 9r1#UU]:2@ @ @ #G'|͵wTwţ\U @ @@lv&Q7"YʟR#و @ @@yzliXKZ5;;k#N_YrYvWqn^kZ[)IDAT󽴳 @ @ Wr-\@RVF<9U}3]\~ޟx#יž" @ @(P7N>zgu`uh,m]6vGvx&g"rf_\ː|Ո-E @ @" P;9C|d܉mת Iw,G@wouxu;#nۧ57J @ @@ʗ ggr좶\:Z5zgkwJ$b2C cc^|7N @ @ 9[wͷ#2~\=ޓk"OOES9<\Oo#@ @ @?vwO_!P޼5-y-rY&?Zg"nF.3ž" @ @(%3ge FwsKZGhtiw.uQ+=Jo*רW"A|s[}]|[s+ @ @}( =gΖ1|b2h0ɩv05svdĽ9;jT;\ /]: @ @B '0<ٲmE6\!dA:\8}Oqm屗>ۉ @ @t_ o}3DqoZdqw9k9g88= @ @ @y^fGR>A.ǐ?yuNPoXc3#@ @ @ 4b~weecD?dD  @ @ pQ e @ @hK@ @ @ @@3@ @ @m l l򈛗DMjN"@ @ @ /P~݁ @ @ `ɋ @ @( PQg @ @,@@4 @ @ @`֫@]k"VΧDl=W  @ @*P8>q>>K{Fuh @ @ߒ@( @ @ @6 kC! @ @ P K e @ @(W8@ @ @@P&@ @ @Jr% @ @ P K e @ @(W8@ @ @@P&@ @ @Jr% @ @ P K e @ @(W8@ @ @@P&@ @ @Jr% @ @ P K e @ @(W8@ @ @@P&@ @ @Jr% @ @ P K e @ @(W8@ @ @@P&@ @ @Jr% @ @ P K e @ @(W8@ @ @@P&@ @ @Jr% @ @ P K e @ @(W8@ @ @@P&@ @ @Jȹʣ80<%G>8Տ' @ @!P@yFz @ @/`ɋ @ @ @` 1LI @ @ ?Z@ @ @(0i$ @ @/kMظ&"NNxtkR1 @ @t_઼Źfr]XƖ~ @ @ @@ tgɋ+týh @ @h tg?1̪Nnq+ @ @ @`poz"Lj_<ڹD @ @}|#EgFsU @ @ "g(_=NOGG @ @ @%[2Inɍ:5;ED @ @X u^rξqO.&@ @ @X A|0[CC @ @ X k/N3NjU"@ @ @X warxt`t @ @f ,,P޼lĖCž" @ @ 5뗴,h* @ @ @p ?P~c!aēSž" @ @ <ʷ:-\;}% @ @ZP}Sq.b0Q$@ @ @P ,Wrr͇͉GN&@ @ @SM'@ @ @E oɋE @ @ 0A=m'@ @ @@=v+ @ @ @yGO  @ @ C=pxgwDgQ  @ @:,`rAUG @ @a( @ @:, P0 @ @ 0aY"@ @ @@U @ @U@<#_ @ @谀@à#@ @ @ ud @ @(wTu @ @VʛnCLB @ @h?P?-'"ԭU @ @9id #+ԭ @ @5y.Y,(\_xYuny @ @.*ߚ#SĦ#~ڈK؎w"}ߪ[=-mϽܙN\c @ @岙r=2bݢ9{z$Ȁj[=U+ 9N @ @ ,%[FnWVϕ8 @ @h?P vF>TFnd @ @ @SWG럱@nk?,ZY]?ZM=:E%U>?ٽi 枸kKUpYxu589=-x @QTUVyj22+~:/H꧌0>^#>?R[xODՍjNwceHw<2FVQ&?855(}CpTF&JŌ)QT?qG:q'32JBP{ XYT39*#{[_G惖jC,[@R+@vV0V|מFP?$Bn1L~^!SR̳GeNJض4/%ƢUSP\Ň<H̄IzsF%ϔuYw&M#yA 7$iڷ De.HVߓqR/=> -oI牿|Ƨ&1f,ƛ|n.<čRʍD ۃBWZ@X`+|M4v%is*oN#t_G@OsKgjf=0K_efwqd~ hBm{677vwDd";'|d@⧍Ry&n kWFZ۾>ǨQ8sBf[jn;g{k8k_hZY8 aCkOōfc2)RX/ 55QXV!} BkȻnd\Xm5lSQ](wW]i}*1s:n 䉑Yhկ-_w:=:T. =K-Ut+izb7.a\`NjOeľcч "~~twWf{mZяukN!`k@O[\$ۖo6 3Q:@d\%[=r9z2a~^3ӲdB%4o.Ka93:LAyMN+߷S0`:cn۾ɛ[H}֙͜e0:x$?cWjҀV YβUtաjv0:FFݖC`쥘L%*W>o*H5ZAడxyQ?__ϝf.*<0Eax= l0^ose-w}Yv03{?^m`H.puR2uoݻ,&[ipS{!ɲ##:\6uNj⢲*;wס ]̺^c'Yy6 ]Pƺfb>GOױ c5W҉MHrMvS_ht( xzz,5VƬI=&jI>ˊYOc=:rZXҊ~k·.5%|9FkJZ"(+- @;@̤j>XK,KH,!KH,!K,KH,!KH,+77C۫ҥKԩS455 :Y3gΰvZZ-: ȑ#qwwg۶mh4dYN +33!C₯`ee%"|QPODVUQQd4Cqg8w'O&11///NY3|j5DEE1v.WHnn.cƌia5I"??ݻwS] AAA\x.1&&I&ԅ]3 Ι33fhD{-^^O\\"ü^j:#FV,N_UyZ;w.3gDVU_Ǐ3e8%عsehZfΜIQQgϞIII!!!%Kh"O!Nlj' dӦM(̝;/// = .dذa >]RRܹM6Pb`yرc׮]cݺu`$z U8p={Xt:\]]OBzvő#Gz4piiiKwO|||((( ''Gk+W8twھ}; ,\ 6PZZ#Z&܉i-' ::kע>|xKnʲeزe *w}HZZuuuDEEindt!/_?^{b4J׳fә8q"o&/FӱcFc%'=(+%ʯ˗/:$$DY~RYY$?DP?,!KHZM",{XkXXOb!',Nϗ>`<<==Ӡ3:~8ʼyh4Fy'::+VqFL&6lh!'ʕ+u%$` X%$` XB%` XB%$` X%$` XB%` XBvCe%bTDd ݱ*/F%%'=a?݇*IENDB`sympy-0.7.4.1/doc/src/python-comparisons.rst0000644000175000017500000002560212253362407021213 0ustar georgeskgeorgesk======================================= 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 its 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``. sympy-0.7.4.1/doc/man/0000755000175000017500000000000012253362407014564 5ustar georgeskgeorgesksympy-0.7.4.1/doc/man/isympy.xml0000644000175000017500000004267312253362407016654 0ustar georgeskgeorgesk ]> &dhtitle; &dhpackage; &dhrelease; &dhdate; Ondrej Certik Design, programming.

ondrej@certik.cz
&dhfirstname; &dhsurname; Programming.
&dhemail;
2013 SymPy Development Team Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. &dhucpackage; &dhsection; &dhpackage; interactive shell for SymPy &dhpackage; ENCODING ENCODING TYPE TYPE ORDER ORDER -- PYTHONOPTIONS &dhpackage; DESCRIPTION isympy is a Python shell for SymPy. It is just a normal python shell (ipython shell if you have the ipython package installed) that executes the following commands so that you don't have to: >>> from __future__ import division >>> from sympy import * >>> x, y, z = symbols("x,y,z") >>> k, m, n = symbols("k,m,n", integer=True) So starting isympy is equivalent to starting python (or ipython) and executing the above commands by hand. It is intended for easy and quick experimentation with SymPy. For more complicated programs, it is recommended to write a script and import things explicitly (using the "from sympy import sin, log, Symbol, ..." idiom). OPTIONS Use the specified shell (python or ipython) as console backend instead of the default one (ipython if present or python otherwise). Example: isympy -c python SHELL could be either 'ipython' or 'python' Setup pretty printing in SymPy. By default, the most pretty, unicode printing is enabled (if the terminal supports it). You can use less pretty ASCII printing instead or no pretty printing at all. Example: isympy -p no ENCODING must be one of 'unicode', 'ascii' or 'no'. Setup the ground types for the polys. By default, gmpy ground types are used if gmpy2 or gmpy is installed, otherwise it falls back to python ground types, which are a little bit slower. You can manually choose python ground types even if gmpy is installed (e.g., for testing purposes). Note that sympy ground types are not supported, and should be used only for experimental purposes. Note that the gmpy1 ground type is primarily intended for testing; it the use of gmpy even if gmpy2 is available. This is the same as setting the environment variable SYMPY_GROUND_TYPES to the given ground type (e.g., SYMPY_GROUND_TYPES='gmpy') The ground types can be determined interactively from the variable sympy.polys.domains.GROUND_TYPES inside the isympy shell itself. Example: isympy -t python TYPE must be one of 'gmpy', 'gmpy1' or 'python'. Setup the ordering of terms for printing. The default is lex, which orders terms lexicographically (e.g., x**2 + x + 1). You can choose other orderings, such as rev-lex, which will use reverse lexicographic ordering (e.g., 1 + x + x**2). Note that for very large expressions, ORDER='none' may speed up printing considerably, with the tradeoff that the order of the terms in the printed expression will have no canonical order Example: isympy -o rev-lax ORDER must be one of 'lex', 'rev-lex', 'grlex', 'rev-grlex', 'grevlex', 'rev-grevlex', 'old', or 'none'. Print only Python's and SymPy's versions to stdout at startup, and nothing else. Use the same format that should be used for doctests. This is equivalent to 'isympy -c python -p no'. Disable the caching mechanism. Disabling the cache may slow certain operations down considerably. This is useful for testing the cache, or for benchmarking, as the cache can result in deceptive benchmark timings. This is the same as setting the environment variable SYMPY_USE_CACHE to 'no'. Automatically create missing symbols. Normally, typing a name of a Symbol that has not been instantiated first would raise NameError, but with this option enabled, any undefined name will be automatically created as a Symbol. This only works in IPython 0.11. Note that this is intended only for interactive, calculator style usage. In a script that uses SymPy, Symbols should be instantiated at the top, so that it's clear what they are. This will not override any names that are already defined, which includes the single character letters represented by the mnemonic QCOSINE (see the "Gotchas and Pitfalls" document in the documentation). You can delete existing names by executing "del name" in the shell itself. You can see if a name is defined by typing "'name' in globals()". The Symbols that are created using this have default assumptions. If you want to place assumptions on symbols, you should create them using symbols() or var(). Finally, this only works in the top level namespace. So, for example, if you define a function in isympy with an undefined Symbol, it will not work. Enable debugging output. This is the same as setting the environment variable SYMPY_DEBUG to 'True'. The debug status is set in the variable SYMPY_DEBUG within isympy. -- PYTHONOPTIONS These options will be passed on to ipython (1) shell. Only supported when ipython is being used (standard python shell not supported). Two dashes (--) are required to separate PYTHONOPTIONS from the other isympy options. For example, to run iSymPy without startup banner and colors: isympy -q -c ipython -- --colors=NoColor Print help output and exit. Print isympy version information and exit. FILES ${HOME}/.sympy-history Saves the history of commands when using the python shell as backend. BUGS The upstreams BTS can be found at http://code.google.com/p/sympy/issues/list Please report all bugs that you find in there, this will help improve the overall quality of SymPy. SEE ALSO ipython 1 , python 1 sympy-0.7.4.1/doc/man/isympy.10000644000175000017500000001463212253362407016206 0ustar georgeskgeorgesk'\" -*- coding: us-ascii -*- .if \n(.g .ds T< \\FC .if \n(.g .ds T> \\F[\n[.fam]] .de URL \\$2 \(la\\$1\(ra\\$3 .. .if \n(.g .mso www.tmac .TH isympy 1 2007-10-8 "" "" .SH NAME isympy \- interactive shell for SymPy .SH SYNOPSIS 'nh .fi .ad l \fBisympy\fR \kx .if (\nx>(\n(.l/2)) .nr x (\n(.l/5) 'in \n(.iu+\nxu [\fB-c\fR | \fB--console\fR] [\fB-p\fR ENCODING | \fB--pretty\fR ENCODING] [\fB-t\fR TYPE | \fB--types\fR TYPE] [\fB-o\fR ORDER | \fB--order\fR ORDER] [\fB-q\fR | \fB--quiet\fR] [\fB-d\fR | \fB--doctest\fR] [\fB-C\fR | \fB--no-cache\fR] [\fB-a\fR | \fB--auto\fR] [\fB-D\fR | \fB--debug\fR] [ -- | PYTHONOPTIONS] 'in \n(.iu-\nxu .ad b 'hy 'nh .fi .ad l \fBisympy\fR \kx .if (\nx>(\n(.l/2)) .nr x (\n(.l/5) 'in \n(.iu+\nxu [ {\fB-h\fR | \fB--help\fR} | {\fB-v\fR | \fB--version\fR} ] 'in \n(.iu-\nxu .ad b 'hy .SH DESCRIPTION isympy is a Python shell for SymPy. It is just a normal python shell (ipython shell if you have the ipython package installed) that executes the following commands so that you don't have to: .PP .nf \*(T< >>> from __future__ import division >>> from sympy import * >>> x, y, z = symbols("x,y,z") >>> k, m, n = symbols("k,m,n", integer=True) \*(T> .fi .PP So starting isympy is equivalent to starting python (or ipython) and executing the above commands by hand. It is intended for easy and quick experimentation with SymPy. For more complicated programs, it is recommended to write a script and import things explicitly (using the "from sympy import sin, log, Symbol, ..." idiom). .SH OPTIONS .TP \*(T<\fB\-c \fR\*(T>\fISHELL\fR, \*(T<\fB\-\-console=\fR\*(T>\fISHELL\fR Use the specified shell (python or ipython) as console backend instead of the default one (ipython if present or python otherwise). Example: isympy -c python \fISHELL\fR could be either \&'ipython' or 'python' .TP \*(T<\fB\-p \fR\*(T>\fIENCODING\fR, \*(T<\fB\-\-pretty=\fR\*(T>\fIENCODING\fR Setup pretty printing in SymPy. By default, the most pretty, unicode printing is enabled (if the terminal supports it). You can use less pretty ASCII printing instead or no pretty printing at all. Example: isympy -p no \fIENCODING\fR must be one of 'unicode', \&'ascii' or 'no'. .TP \*(T<\fB\-t \fR\*(T>\fITYPE\fR, \*(T<\fB\-\-types=\fR\*(T>\fITYPE\fR Setup the ground types for the polys. By default, gmpy ground types are used if gmpy is installed, otherwise it falls back to python ground types, which are a little bit slower. You can manually choose python ground types even if gmpy is installed (e.g., for testing purposes). Note that sympy ground types are not supported, and should be used only for experimental purposes. This is the same as setting the environment variable SYMPY_GROUND_TYPES to the given ground type (e.g., SYMPY_GROUND_TYPES='gmpy') The ground types can be determined interactively from the variable sympy.polys.domains.GROUND_TYPES inside the isympy shell itself. Example: isympy -t python \fITYPE\fR must be one of 'gmpy', \&'python' or 'sympy'. .TP \*(T<\fB\-o \fR\*(T>\fIORDER\fR, \*(T<\fB\-\-order=\fR\*(T>\fIORDER\fR Setup the ordering of terms for printing. The default is lex, which orders terms lexicographically (e.g., x**2 + x + 1). You can choose other orderings, such as rev-lex, which will use reverse lexicographic ordering (e.g., 1 + x + x**2). Note that for very large expressions, ORDER='none' may speed up printing considerably, with the tradeoff that the order of the terms in the printed expression will have no canonical order Example: isympy -o rev-lax \fIORDER\fR must be one of 'lex', 'rev-lex', 'grlex', \&'rev-grlex', 'grevlex', 'rev-grevlex', 'old', or 'none'. .TP \*(T<\fB\-q\fR\*(T>, \*(T<\fB\-\-quiet\fR\*(T> Print only Python's and SymPy's versions to stdout at startup, and nothing else. .TP \*(T<\fB\-d\fR\*(T>, \*(T<\fB\-\-doctest\fR\*(T> Use the same format that should be used for doctests. This is equivalent to '\fIisympy -c python -p no\fR'. .TP \*(T<\fB\-C\fR\*(T>, \*(T<\fB\-\-no\-cache\fR\*(T> Disable the caching mechanism. Disabling the cache may slow certain operations down considerably. This is useful for testing the cache, or for benchmarking, as the cache can result in deceptive benchmark timings. This is the same as setting the environment variable SYMPY_USE_CACHE to 'no'. .TP \*(T<\fB\-a\fR\*(T>, \*(T<\fB\-\-auto\fR\*(T> Automatically create missing symbols. Normally, typing a name of a Symbol that has not been instantiated first would raise NameError, but with this option enabled, any undefined name will be automatically created as a Symbol. This only works in IPython 0.11. Note that this is intended only for interactive, calculator style usage. In a script that uses SymPy, Symbols should be instantiated at the top, so that it's clear what they are. This will not override any names that are already defined, which includes the single character letters represented by the mnemonic QCOSINE (see the "Gotchas and Pitfalls" document in the documentation). You can delete existing names by executing "del name" in the shell itself. You can see if a name is defined by typing "'name' in globals()". The Symbols that are created using this have default assumptions. If you want to place assumptions on symbols, you should create them using symbols() or var(). Finally, this only works in the top level namespace. So, for example, if you define a function in isympy with an undefined Symbol, it will not work. .TP \*(T<\fB\-D\fR\*(T>, \*(T<\fB\-\-debug\fR\*(T> Enable debugging output. This is the same as setting the environment variable SYMPY_DEBUG to 'True'. The debug status is set in the variable SYMPY_DEBUG within isympy. .TP -- \fIPYTHONOPTIONS\fR These options will be passed on to \fIipython (1)\fR shell. Only supported when ipython is being used (standard python shell not supported). Two dashes (--) are required to separate \fIPYTHONOPTIONS\fR from the other isympy options. For example, to run iSymPy without startup banner and colors: isympy -q -c ipython -- --colors=NoColor .TP \*(T<\fB\-h\fR\*(T>, \*(T<\fB\-\-help\fR\*(T> Print help output and exit. .TP \*(T<\fB\-v\fR\*(T>, \*(T<\fB\-\-version\fR\*(T> Print isympy version information and exit. .SH FILES .TP \*(T<\fI${HOME}/.sympy\-history\fR\*(T> Saves the history of commands when using the python shell as backend. .SH BUGS The upstreams BTS can be found at \(lahttp://code.google.com/p/sympy/issues/list\(ra Please report all bugs that you find in there, this will help improve the overall quality of SymPy. .SH "SEE ALSO" \fBipython\fR(1), \fBpython\fR(1) sympy-0.7.4.1/doc/README.rst0000644000175000017500000000050612253362407015501 0ustar georgeskgeorgeskHow to Build Documentation ========================== To make the html documentation, install the prerequisites, e.g. on Debian/Ubuntu (similarly for other distributions):: apt-get install python-sphinx texlive-latex-recommended dvipng and do:: make html and to view it, do:: epiphany _build/html/index.html sympy-0.7.4.1/doc/apidoc.conf0000644000175000017500000000035112253362407016116 0ustar georgeskgeorgesk[epydoc] name : SymPy url: http://code.google.com/p/sympy modules : sympy, sympy.core, sympy.modules output : html target : ../api/ dotpath : /usr/bin/dot parse: yes introspect: yes private : no frames: no docformat : epytext sympy-0.7.4.1/doc/ext/0000755000175000017500000000000012253362407014611 5ustar georgeskgeorgesksympy-0.7.4.1/doc/ext/docscrape_sphinx.py0000644000175000017500000001746312253362407020532 0ustar georgeskgeorgeskimport re import inspect import textwrap import pydoc import sphinx from docscrape import NumpyDocString, FunctionDoc, ClassDoc class SphinxDocString(NumpyDocString): def __init__(self, docstring, config={}): self.use_plots = config.get('use_plots', False) NumpyDocString.__init__(self, docstring, config=config) # string conversion routines def _str_header(self, name, symbol='`'): return ['.. rubric:: ' + name, ''] def _str_field_list(self, name): return [':' + name + ':'] def _str_indent(self, doc, indent=4): out = [] for line in doc: out += [' '*indent + line] return out def _str_signature(self): return [''] if self['Signature']: return ['``%s``' % self['Signature']] + [''] else: return [''] def _str_summary(self): return self['Summary'] + [''] def _str_extended_summary(self): return self['Extended Summary'] + [''] def _str_param_list(self, name): out = [] if self[name]: out += self._str_field_list(name) out += [''] for param, param_type, desc in self[name]: out += self._str_indent(['**%s** : %s' % (param.strip(), param_type)]) out += [''] out += self._str_indent(desc, 8) out += [''] return out @property def _obj(self): if hasattr(self, '_cls'): return self._cls elif hasattr(self, '_f'): return self._f return None def _str_member_list(self, name): """ Generate a member listing, autosummary:: table where possible, and a table where not. """ out = [] if self[name]: out += ['.. rubric:: %s' % name, ''] prefix = getattr(self, '_name', '') if prefix: prefix = '~%s.' % prefix ## Lines that are commented out are used to make the ## autosummary:: table. Since SymPy does not use the ## autosummary:: functionality, it is easiest to just comment it ## out. #autosum = [] others = [] for param, param_type, desc in self[name]: param = param.strip() #if not self._obj or hasattr(self._obj, param): # autosum += [" %s%s" % (prefix, param)] #else: others.append((param, param_type, desc)) #if autosum: # out += ['.. autosummary::', ' :toctree:', ''] # out += autosum if others: maxlen_0 = max([len(x[0]) for x in others]) maxlen_1 = max([len(x[1]) for x in others]) hdr = "="*maxlen_0 + " " + "="*maxlen_1 + " " + "="*10 fmt = '%%%ds %%%ds ' % (maxlen_0, maxlen_1) n_indent = maxlen_0 + maxlen_1 + 4 out += [hdr] for param, param_type, desc in others: out += [fmt % (param.strip(), param_type)] out += self._str_indent(desc, n_indent) out += [hdr] out += [''] return out def _str_section(self, name): out = [] if self[name]: out += self._str_header(name) out += [''] content = textwrap.dedent("\n".join(self[name])).split("\n") out += content out += [''] return out def _str_see_also(self, func_role): out = [] if self['See Also']: see_also = super(SphinxDocString, self)._str_see_also(func_role) out = ['.. seealso::', ''] out += self._str_indent(see_also[2:]) return out def _str_warnings(self): out = [] if self['Warnings']: out = ['.. warning::', ''] out += self._str_indent(self['Warnings']) return out def _str_index(self): idx = self['index'] out = [] if len(idx) == 0: return out out += ['.. index:: %s' % idx.get('default', '')] for section, references in idx.iteritems(): if section == 'default': continue elif section == 'refguide': out += [' single: %s' % (', '.join(references))] else: out += [' %s: %s' % (section, ','.join(references))] return out def _str_references(self): out = [] if self['References']: out += self._str_header('References') if isinstance(self['References'], str): self['References'] = [self['References']] out.extend(self['References']) out += [''] # Latex collects all references to a separate bibliography, # so we need to insert links to it if sphinx.__version__ >= "0.6": out += ['.. only:: latex', ''] else: out += ['.. latexonly::', ''] items = [] for line in self['References']: m = re.match(r'.. \[([a-z0-9._-]+)\]', line, re.I) if m: items.append(m.group(1)) out += [' ' + ", ".join(["[%s]_" % item for item in items]), ''] return out def _str_examples(self): examples_str = "\n".join(self['Examples']) if (self.use_plots and 'import matplotlib' in examples_str and 'plot::' not in examples_str): out = [] out += self._str_header('Examples') out += ['.. plot::', ''] out += self._str_indent(self['Examples']) out += [''] return out else: return self._str_section('Examples') def __str__(self, indent=0, func_role="obj"): out = [] out += self._str_signature() out += self._str_index() + [''] out += self._str_summary() out += self._str_extended_summary() for param_list in ('Parameters', 'Returns', 'Other Parameters', 'Raises', 'Warns'): out += self._str_param_list(param_list) out += self._str_warnings() out += self._str_see_also(func_role) out += self._str_section('Notes') out += self._str_references() out += self._str_examples() for s in self._other_keys: out += self._str_section(s) out += self._str_member_list('Attributes') out = self._str_indent(out, indent) return '\n'.join(out) class SphinxFunctionDoc(SphinxDocString, FunctionDoc): def __init__(self, obj, doc=None, config={}): self.use_plots = config.get('use_plots', False) FunctionDoc.__init__(self, obj, doc=doc, config=config) class SphinxClassDoc(SphinxDocString, ClassDoc): def __init__(self, obj, doc=None, func_doc=None, config={}): self.use_plots = config.get('use_plots', False) ClassDoc.__init__(self, obj, doc=doc, func_doc=None, config=config) class SphinxObjDoc(SphinxDocString): def __init__(self, obj, doc=None, config={}): self._f = obj SphinxDocString.__init__(self, doc, config=config) def get_doc_object(obj, what=None, doc=None, config={}): if inspect.isclass(obj): what = 'class' elif inspect.ismodule(obj): what = 'module' elif callable(obj): what = 'function' else: what = 'object' if what == 'class': return SphinxClassDoc(obj, func_doc=SphinxFunctionDoc, doc=doc, config=config) elif what in ('function', 'method'): return SphinxFunctionDoc(obj, doc=doc, config=config) else: if doc is None: doc = pydoc.getdoc(obj) return SphinxObjDoc(obj, doc, config=config) sympy-0.7.4.1/doc/ext/sympylive.py0000644000175000017500000000241112253362407017222 0ustar georgeskgeorgesk""" sympylive ~~~~~~~~~ Allow `SymPy Live `_ to be used for interactive evaluation of SymPy's code examples. :copyright: Copyright 2011 by the SymPy Development Team, see AUTHORS. :license: BSD, see LICENSE for details. """ def builder_inited(app): if not app.config.sympylive_url: raise ExtensionError('sympylive_url config value must be set' ' for the sympylive extension to work') app.add_javascript(app.config.sympylive_url + '/static/utilities.js') app.add_javascript(app.config.sympylive_url + '/static/external/classy.js') app.add_stylesheet(app.config.sympylive_url + '/static/live-core.css') app.add_stylesheet(app.config.sympylive_url + '/static/live-autocomplete.css') app.add_stylesheet(app.config.sympylive_url + '/static/live-sphinx.css') app.add_javascript(app.config.sympylive_url + '/static/live-core.js') app.add_javascript(app.config.sympylive_url + '/static/live-autocomplete.js') app.add_javascript(app.config.sympylive_url + '/static/live-sphinx.js') def setup(app): app.add_config_value('sympylive_url', 'http://live.sympy.org', False) app.connect('builder-inited', builder_inited) sympy-0.7.4.1/doc/ext/numpydoc.py0000644000175000017500000001313012253362407017017 0ustar georgeskgeorgesk""" ======== numpydoc ======== Sphinx extension that handles docstrings in the Numpy standard format. [1] It will: - Convert Parameters etc. sections to field lists. - Convert See Also section to a See also entry. - Renumber references. - Extract the signature from the docstring, if it can't be determined otherwise. .. [1] https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt """ import sphinx if sphinx.__version__ < '1.0.1': raise RuntimeError("Sphinx 1.0.1 or newer is required") import os import re import pydoc from docscrape_sphinx import get_doc_object, SphinxDocString from sphinx.util.compat import Directive import inspect def mangle_docstrings(app, what, name, obj, options, lines, reference_offset=[0]): cfg = dict(use_plots=app.config.numpydoc_use_plots, show_class_members=app.config.numpydoc_show_class_members) if what == 'module': # Strip top title title_re = re.compile(ur'^\s*[#*=]{4,}\n[a-z0-9 -]+\n[#*=]{4,}\s*', re.I | re.S) lines[:] = title_re.sub(u'', u"\n".join(lines)).split(u"\n") else: doc = get_doc_object(obj, what, u"\n".join(lines), config=cfg) lines[:] = unicode(doc).split(u"\n") if app.config.numpydoc_edit_link and hasattr(obj, '__name__') and \ obj.__name__: if hasattr(obj, '__module__'): v = dict(full_name=u"%s.%s" % (obj.__module__, obj.__name__)) else: v = dict(full_name=obj.__name__) lines += [u'', u'.. htmlonly::', ''] lines += [u' %s' % x for x in (app.config.numpydoc_edit_link % v).split("\n")] # replace reference numbers so that there are no duplicates references = [] for line in lines: line = line.strip() m = re.match(ur'^.. \[([a-z0-9_.-])\]', line, re.I) if m: references.append(m.group(1)) # start renaming from the longest string, to avoid overwriting parts references.sort(key=lambda x: -len(x)) if references: for i, line in enumerate(lines): for r in references: if re.match(ur'^\d+$', r): new_r = u"R%d" % (reference_offset[0] + int(r)) else: new_r = u"%s%d" % (r, reference_offset[0]) lines[i] = lines[i].replace(u'[%s]_' % r, u'[%s]_' % new_r) lines[i] = lines[i].replace(u'.. [%s]' % r, u'.. [%s]' % new_r) reference_offset[0] += len(references) def mangle_signature(app, what, name, obj, options, sig, retann): # Do not try to inspect classes that don't define `__init__` if (inspect.isclass(obj) and (not hasattr(obj, '__init__') or 'initializes x; see ' in pydoc.getdoc(obj.__init__))): return '', '' if not (callable(obj) or hasattr(obj, '__argspec_is_invalid_')): return if not hasattr(obj, '__doc__'): return doc = SphinxDocString(pydoc.getdoc(obj)) if doc['Signature']: sig = re.sub(u"^[^(]*", u"", doc['Signature']) return sig, u'' def setup(app, get_doc_object_=get_doc_object): global get_doc_object get_doc_object = get_doc_object_ app.connect('autodoc-process-docstring', mangle_docstrings) app.connect('autodoc-process-signature', mangle_signature) app.add_config_value('numpydoc_edit_link', None, False) app.add_config_value('numpydoc_use_plots', None, False) app.add_config_value('numpydoc_show_class_members', True, True) # Extra mangling domains app.add_domain(NumpyPythonDomain) app.add_domain(NumpyCDomain) #------------------------------------------------------------------------------ # Docstring-mangling domains #------------------------------------------------------------------------------ from docutils.statemachine import ViewList from sphinx.domains.c import CDomain from sphinx.domains.python import PythonDomain class ManglingDomainBase(object): directive_mangling_map = {} def __init__(self, *a, **kw): super(ManglingDomainBase, self).__init__(*a, **kw) self.wrap_mangling_directives() def wrap_mangling_directives(self): for name, objtype in self.directive_mangling_map.items(): self.directives[name] = wrap_mangling_directive( self.directives[name], objtype) class NumpyPythonDomain(ManglingDomainBase, PythonDomain): name = 'np' directive_mangling_map = { 'function': 'function', 'class': 'class', 'exception': 'class', 'method': 'function', 'classmethod': 'function', 'staticmethod': 'function', 'attribute': 'attribute', } class NumpyCDomain(ManglingDomainBase, CDomain): name = 'np-c' directive_mangling_map = { 'function': 'function', 'member': 'attribute', 'macro': 'function', 'type': 'class', 'var': 'object', } def wrap_mangling_directive(base_directive, objtype): class directive(base_directive): def run(self): env = self.state.document.settings.env name = None if self.arguments: m = re.match(r'^(.*\s+)?(.*?)(\(.*)?', self.arguments[0]) name = m.group(2).strip() if not name: name = self.arguments[0] lines = list(self.content) mangle_docstrings(env.app, objtype, name, None, None, lines) self.content = ViewList(lines, self.content.parent) return base_directive.run(self) return directive sympy-0.7.4.1/doc/ext/docscrape.py0000644000175000017500000003756612253362407017147 0ustar georgeskgeorgesk""" Extract reference documentation from the NumPy source tree. """ import inspect import textwrap import re import pydoc from StringIO import StringIO class Reader(object): """ A line-based string reader. """ def __init__(self, data): """ Parameters ---------- data : str String with lines separated by '\n'. """ if isinstance(data, list): self._str = data else: self._str = data.split('\n') # store string as list of lines self.reset() def __getitem__(self, n): return self._str[n] def reset(self): self._l = 0 # current line nr def read(self): if not self.eof(): out = self[self._l] self._l += 1 return out else: return '' def seek_next_non_empty_line(self): for l in self[self._l:]: if l.strip(): break else: self._l += 1 def eof(self): return self._l >= len(self._str) def read_to_condition(self, condition_func): start = self._l for line in self[start:]: if condition_func(line): return self[start:self._l] self._l += 1 if self.eof(): return self[start:self._l + 1] return [] def read_to_next_empty_line(self): self.seek_next_non_empty_line() def is_empty(line): return not line.strip() return self.read_to_condition(is_empty) def read_to_next_unindented_line(self): def is_unindented(line): return (line.strip() and (len(line.lstrip()) == len(line))) return self.read_to_condition(is_unindented) def peek(self, n=0): if self._l + n < len(self._str): return self[self._l + n] else: return '' def is_empty(self): return not ''.join(self._str).strip() class NumpyDocString(object): def __init__(self, docstring, config={}): docstring = textwrap.dedent(docstring).split('\n') self._doc = Reader(docstring) self._parsed_data = { 'Signature': '', 'Summary': [''], 'Extended Summary': [], 'Parameters': [], 'Returns': [], 'Raises': [], 'Warns': [], 'Other Parameters': [], 'Attributes': [], 'Methods': [], 'See Also': [], 'Notes': [], 'Warnings': [], 'References': '', 'Examples': '', 'index': {} } self._other_keys = [] self._parse() def __getitem__(self, key): return self._parsed_data[key] def __setitem__(self, key, val): if key not in self._parsed_data: self._other_keys.append(key) self._parsed_data[key] = val def _is_at_section(self): self._doc.seek_next_non_empty_line() if self._doc.eof(): return False l1 = self._doc.peek().strip() # e.g. Parameters if l1.startswith('.. index::'): return True l2 = self._doc.peek(1).strip() # ---------- or ========== return l2.startswith('-'*len(l1)) or l2.startswith('='*len(l1)) def _strip(self, doc): i = 0 j = 0 for i, line in enumerate(doc): if line.strip(): break for j, line in enumerate(doc[::-1]): if line.strip(): break return doc[i:len(doc) - j] def _read_to_next_section(self): section = self._doc.read_to_next_empty_line() while not self._is_at_section() and not self._doc.eof(): if not self._doc.peek(-1).strip(): # previous line was empty section += [''] section += self._doc.read_to_next_empty_line() return section def _read_sections(self): while not self._doc.eof(): data = self._read_to_next_section() name = data[0].strip() if name.startswith('..'): # index section yield name, data[1:] elif len(data) < 2: yield StopIteration else: yield name, self._strip(data[2:]) def _parse_param_list(self, content): r = Reader(content) params = [] while not r.eof(): header = r.read().strip() if ' : ' in header: arg_name, arg_type = header.split(' : ')[:2] else: arg_name, arg_type = header, '' desc = r.read_to_next_unindented_line() desc = dedent_lines(desc) params.append((arg_name, arg_type, desc)) return params _name_rgx = re.compile(r"^\s*(:(?P\w+):`(?P[a-zA-Z0-9_.-]+)`|" r" (?P[a-zA-Z0-9_.-]+))\s*", re.X) def _parse_see_also(self, content): """ func_name : Descriptive text continued text another_func_name : Descriptive text func_name1, func_name2, :meth:`func_name`, func_name3 """ items = [] def parse_item_name(text): """Match ':role:`name`' or 'name'""" m = self._name_rgx.match(text) if m: g = m.groups() if g[1] is None: return g[3], None else: return g[2], g[1] raise ValueError("%s is not a item name" % text) def push_item(name, rest): if not name: return name, role = parse_item_name(name) items.append((name, list(rest), role)) del rest[:] current_func = None rest = [] for line in content: if not line.strip(): continue m = self._name_rgx.match(line) if m and line[m.end():].strip().startswith(':'): push_item(current_func, rest) current_func, line = line[:m.end()], line[m.end():] rest = [line.split(':', 1)[1].strip()] if not rest[0]: rest = [] elif not line.startswith(' '): push_item(current_func, rest) current_func = None if ',' in line: for func in line.split(','): if func.strip(): push_item(func, []) elif line.strip(): current_func = line elif current_func is not None: rest.append(line.strip()) push_item(current_func, rest) return items def _parse_index(self, section, content): """ .. index: default :refguide: something, else, and more """ def strip_each_in(lst): return [s.strip() for s in lst] out = {} section = section.split('::') if len(section) > 1: out['default'] = strip_each_in(section[1].split(','))[0] for line in content: line = line.split(':') if len(line) > 2: out[line[1]] = strip_each_in(line[2].split(',')) return out def _parse_summary(self): """Grab signature (if given) and summary""" if self._is_at_section(): return summary = self._doc.read_to_next_empty_line() summary_str = " ".join([s.strip() for s in summary]).strip() if re.compile('^([\w., ]+=)?\s*[\w\.]+\(.*\)$').match(summary_str): self['Signature'] = summary_str if not self._is_at_section(): self['Summary'] = self._doc.read_to_next_empty_line() else: self['Summary'] = summary if not self._is_at_section(): self['Extended Summary'] = self._read_to_next_section() def _parse(self): self._doc.reset() self._parse_summary() for (section, content) in self._read_sections(): if not section.startswith('..'): section = ' '.join([s.capitalize() for s in section.split(' ')]) if section in ('Parameters', 'Returns', 'Raises', 'Warns', 'Other Parameters', 'Attributes', 'Methods'): self[section] = self._parse_param_list(content) elif section.startswith('.. index::'): self['index'] = self._parse_index(section, content) elif section == 'See Also': self['See Also'] = self._parse_see_also(content) else: self[section] = content # string conversion routines def _str_header(self, name, symbol='-'): return [name, len(name)*symbol] def _str_indent(self, doc, indent=4): out = [] for line in doc: out += [' '*indent + line] return out def _str_signature(self): if self['Signature']: return [self['Signature'].replace('*', '\*')] + [''] else: return [''] def _str_summary(self): if self['Summary']: return self['Summary'] + [''] else: return [] def _str_extended_summary(self): if self['Extended Summary']: return self['Extended Summary'] + [''] else: return [] def _str_param_list(self, name): out = [] if self[name]: out += self._str_header(name) for param, param_type, desc in self[name]: out += ['%s : %s' % (param, param_type)] out += self._str_indent(desc) out += [''] return out def _str_section(self, name): out = [] if self[name]: out += self._str_header(name) out += self[name] out += [''] return out def _str_see_also(self, func_role): if not self['See Also']: return [] out = [] out += self._str_header("See Also") last_had_desc = True for func, desc, role in self['See Also']: if role: link = ':%s:`%s`' % (role, func) elif func_role: link = ':%s:`%s`' % (func_role, func) else: link = "`%s`_" % func if desc or last_had_desc: out += [''] out += [link] else: out[-1] += ", %s" % link if desc: out += self._str_indent([' '.join(desc)]) last_had_desc = True else: last_had_desc = False out += [''] return out def _str_index(self): idx = self['index'] out = [] out += ['.. index:: %s' % idx.get('default', '')] for section, references in idx.iteritems(): if section == 'default': continue out += [' :%s: %s' % (section, ', '.join(references))] return out def __str__(self, func_role=''): out = [] out += self._str_signature() out += self._str_summary() out += self._str_extended_summary() for param_list in ('Parameters', 'Returns', 'Other Parameters', 'Raises', 'Warns'): out += self._str_param_list(param_list) out += self._str_section('Warnings') out += self._str_see_also(func_role) for s in ('Notes', 'References', 'Examples'): out += self._str_section(s) for param_list in ('Attributes', 'Methods'): out += self._str_param_list(param_list) out += self._str_index() return '\n'.join(out) def indent(str, indent=4): indent_str = ' '*indent if str is None: return indent_str lines = str.split('\n') return '\n'.join(indent_str + l for l in lines) def dedent_lines(lines): """Deindent a list of lines maximally""" return textwrap.dedent("\n".join(lines)).split("\n") def header(text, style='-'): return text + '\n' + style*len(text) + '\n' class FunctionDoc(NumpyDocString): def __init__(self, func, role='func', doc=None, config={}): self._f = func self._role = role # e.g. "func" or "meth" if doc is None: if func is None: raise ValueError("No function or docstring given") doc = inspect.getdoc(func) or '' NumpyDocString.__init__(self, doc) if not self['Signature'] and func is not None: func, func_name = self.get_func() try: # try to read signature argspec = inspect.getargspec(func) argspec = inspect.formatargspec(*argspec) argspec = argspec.replace('*', '\*') signature = '%s%s' % (func_name, argspec) except TypeError as e: signature = '%s()' % func_name self['Signature'] = signature def get_func(self): func_name = getattr(self._f, '__name__', self.__class__.__name__) if inspect.isclass(self._f): func = getattr(self._f, '__call__', self._f.__init__) else: func = self._f return func, func_name def __str__(self): out = '' func, func_name = self.get_func() signature = self['Signature'].replace('*', '\*') roles = {'func': 'function', 'meth': 'method'} if self._role: if self._role not in roles: print("Warning: invalid role %s" % self._role) out += '.. %s:: %s\n \n\n' % (roles.get(self._role, ''), func_name) out += super(FunctionDoc, self).__str__(func_role=self._role) return out class ClassDoc(NumpyDocString): extra_public_methods = ['__call__'] def __init__(self, cls, doc=None, modulename='', func_doc=FunctionDoc, config={}): if not inspect.isclass(cls) and cls is not None: raise ValueError("Expected a class or None, but got %r" % cls) self._cls = cls if modulename and not modulename.endswith('.'): modulename += '.' self._mod = modulename if doc is None: if cls is None: raise ValueError("No class or documentation string given") doc = pydoc.getdoc(cls) NumpyDocString.__init__(self, doc) if config.get('show_class_members', True): if not self['Methods']: self['Methods'] = [(name, '', '') for name in sorted(self.methods)] if not self['Attributes']: self['Attributes'] = [(name, '', '') for name in sorted(self.properties)] @property def methods(self): if self._cls is None: return [] return [name for name, func in inspect_getmembers(self._cls) if ((not name.startswith('_') or name in self.extra_public_methods) and callable(func))] @property def properties(self): if self._cls is None: return [] return [name for name, func in inspect_getmembers(self._cls) if not name.startswith('_') and func is None] # This function was taken verbatim from Python 2.7 inspect.getmembers() from # the standard library. The difference from Python < 2.7 is that there is the # try/except AttributeError clause added, which catches exceptions like this # one: https://gist.github.com/1471949 def inspect_getmembers(object, predicate=None): """Return all members of an object as (name, value) pairs sorted by name. Optionally, only return members that satisfy a given predicate. """ results = [] for key in dir(object): try: value = getattr(object, key) except AttributeError: continue if not predicate or predicate(value): results.append((key, value)) results.sort() return results sympy-0.7.4.1/doc/api/0000755000175000017500000000000012253362407014562 5ustar georgeskgeorgesksympy-0.7.4.1/doc/api/conf.py0000644000175000017500000001010612253362407016057 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- # # SymPy documentation build configuration file, created by # sphinx-quickstart.py on Sat Mar 22 19:34:32 2008. # # This file is execfile()d with the current directory set to its containing dir. # # The contents of this file are pickled, so don't put values in the namespace # that aren't pickleable (module imports are okay, they're removed automatically). # # All configuration values have a default value; values that are commented out # serve to show the default value. import sys import sympy # If your extensions are in another directory, add it here. #sys.path.append('some/directory') # General configuration # --------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.addons.*') or your custom ones. extensions = ['sphinx.ext.autodoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['.templates'] # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General substitutions. project = 'SymPy' copyright = '2013, SymPy Development Team' # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. # # The short X.Y version. version = sympy.__version__ # The full version, including alpha/beta/rc tags. release = version # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # Options for HTML output # ----------------------- # The style sheet to use for HTML and HTML Help pages. A file of that name # must exist either in Sphinx' static/ path, or in one of the custom paths # given in html_static_path. html_style = 'default.css' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['.static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Content template for the index page. #html_index = '' # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If true, the reST sources are included in the HTML build as _sources/. #html_copy_source = True # Output file base name for HTML help builder. htmlhelp_basename = 'SymPydoc' # Options for LaTeX output # ------------------------ # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual]). latex_documents = [('index', 'sympy.tex', 'SymPy Documentation', 'SymPy Development Team', 'manual')] # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True sympy-0.7.4.1/doc/api/index.rst0000644000175000017500000000046612253362407016431 0ustar georgeskgeorgeskWelcome to SymPy API reference ============================== This is an automaticaly generated API documentation from SymPy sources. Click the "modules" (:ref:`modindex`) link in the top right corner to browse the modules. Or click the "index" to see an index of all SymPy functions, methods and classes. sympy-0.7.4.1/.mailmap0000644000175000017500000002301412253362407014665 0ustar georgeskgeorgesk# Prevent git from showing duplicate names with commands like "git shortlog" # See the manpage of git-shortlog for details. # The syntax is: # Name that should be used Bad name # # You can skip Bad name if it is the same as the one that should be used, and is unique. # # This file is up-to-date if the command git log --format="%aN <%aE>" | sort -u # gives no duplicates. Ondřej Čertík Ondrej Certik Ondřej Čertík ondrej.certik Ondřej Čertík Ondrej Certik Ondřej Čertík Ondřej Čertík Fredrik Johansson fredrik.johansson Kirill Smelkov kirill.smelkov Kirill Smelkov convert-repo Mateusz Paprocki mattpap Fabian Pedregosa Fabian Seoane Fabian Pedregosa fabian Jason Gedge inferno1386 Robert Schwarz lethargo Sebastian Krämer basti.kr David Roberts David Roberts (dvdr18 [at] gmail [dot] com) Saroj Adhikari Saroj Rizgar Mella RizgarMella rizgar.mella@gmail.com Pearu Peterson pearu.peterson Brian Jorgensen brian.jorgensen Chris Wu Chris.Wu Jezreel Ng James Pearson Cristóvão Sousa Addison Cugini Addison Cugini Addison Cugini Addison CUgini Alan Bromborsky Alan Bromborsky Alexey U. Gudchenko Alexey U. Goodchenko Vinzent Steinberg Vinzent Steinberg Chris Smith Christopher Smith Chris Smith smichr Chris Smith unknown Fredrik Johansson Fredrik Fredrik Johansson Fredrik Johansson Brian E. Granger Brian Granger Jurjen N.E. Bos Jurjen N.E. Bos Benjamin McDonald benjaminmcdonald Prafullkumar P. Tale hector1618 Prafullkumar P. Tale hector Prafullkumar P. Tale Hector Øyvind Jensen jegerjensen Jeremias Yehdegho jerryy Oleksandr Gituliar oleksandr gituliar Renato Coutinho renato Thomas Wiecki whyking Thomas Wiecki Felix Kaiser Felix Kaiser Felix Kaiser Felix Kaiser Andy R. Terrel Andy R. Terrel Andy R. Terrel Andy R. Terrel Nicolas Pourcelot wxgeo Min Ragan-Kelley MinRK Bradley Froehle Bradley M. Froehle Matthew Rocklin Bilal Akhtar David Li David Li David Marek Gilbert Gede Gilbert Gede Ronan Lamy Toon Verstraelen Toon Verstraelen Andrej Tokarčík Andrej "qwp0" Tokarčík Goutham Lakshminarayan Goutham Tomáš Bambas Tomas Bambas Prafullkumar P. Tale hector Roberto Colistete, Jr. Roberto Colistete-Jr Robert Cimrman Raymond Wong rayman Raymond Wong rayman Raymond Wong rayman Luke Peterson Dale Lukas Peterson Davy Mao icedtrees Kendhia ALGHeArT Nathan Alison Nathan Raoul Bourquin Raoul Bourquin raoulb Sachin Irukula sachin irukula Siddhanathan Shanmugam Siddhu Siddhanathan Shanmugam Siddhu Jim Zhang Neynt Jim Zhang Angus Griffith <16sn6uv@gmail.com> sn6uv <16sn6uv@gmail.com> Timothy Reluga zanzibar7 Saurabh Jha Saurabh Joachim Durchholz Joachim Durchholz Arpit Goyal arpit goyal Tom Bachmann Tom Bachmann Rom le Clair 101man Manoj Babu K. Manoj Babu Ljubiša Moćić <3rdslasher@gmail.com> Slasher9 <3rdslasher@gmail.com> Oscar Benjamin Jens H. Nielsen Jens Hedegaard Nielsen Alexander Eberspächer aeberspaecher Niklas Thörne nthorne Marek Šuppa mr.Shu Prasoon Shukla prasoon2211 Sachin Joglekar srjoglekar246 Sachin Joglekar root Stefen Yin StefenYin Manoj Kumar Manoj-Kumar-S Benjamin Fishbein fishbeinb Chetna Gupta chetna Rishabh Dixit rishabh-git Prasoon Shukla Prasoon Saurabh Jha Saurab Jha Mary Clark Mary Clark Mary Clark Mary Clark Mary Clark meclark256 Akshit Agarwal Akshit Colleen Lee Colleen Lee Amit Jamadagni amitjamadagni Seshagiri Prabhu seshagiriprabhu Shravas K Rao shravas Ananya H Ananya Nichita Utiu Nichita Utiu Matthew Hoff Matthew Hoff Tarang Patel Tarang Thilina Rathnayake Thilina Bandara Rathnayake Pradyumna root Francesco Bonazzi Fra Dmitry Batkovich batya239 Yuriy Demidov Demidov Yuriy Amit Saha Amit Saha Heiner Kirchhoffer Heiner Kirchhoffer David Joyner david joyner David Joyner david joyner Jason Moore Jason K. Moore Alkiviadis G. Akritas akritas Pablo Puente sympy-0.7.4.1/setupegg.py0000644000175000017500000000006112253362407015436 0ustar georgeskgeorgeskimport setuptools exec(open('setup.py').read())